summaryrefslogtreecommitdiffstats
path: root/src/widgets/platforms
diff options
context:
space:
mode:
authorGunnar Sletta <gunnar.sletta@nokia.com>2011-09-13 08:54:45 +0200
committerGunnar Sletta <gunnar.sletta@nokia.com>2011-09-13 08:54:45 +0200
commitb62bd0584a7872b6917917009b707785b3abd077 (patch)
tree9981f274712c098cabbff0c4667672a3934e5393 /src/widgets/platforms
parent5e10745dca1d10025404a9f268f03ae697fb10cc (diff)
parent97baad65f65783d2b5ff938f6217aec9434f2e5f (diff)
Merge branch 'refactor'
Conflicts: mkspecs/qws/linux-lsb-g++/qmake.conf src/gui/image/qpixmap_mac.cpp src/gui/painting/qpaintengine_x11.cpp src/gui/painting/qtessellator.cpp src/gui/text/qfontengine_qws.cpp src/gui/text/qfontengine_x11.cpp src/gui/widgets/qlinecontrol.cpp src/opengl/qgl.h src/opengl/qgl_x11egl.cpp src/plugins/plugins.pro Change-Id: If52dcd55cd55f2983a756c2f843967702b60a310
Diffstat (limited to 'src/widgets/platforms')
-rw-r--r--src/widgets/platforms/mac/qapplication_mac.mm3131
-rw-r--r--src/widgets/platforms/mac/qclipboard_mac.cpp634
-rw-r--r--src/widgets/platforms/mac/qcocoaintrospection_mac.mm125
-rw-r--r--src/widgets/platforms/mac/qcocoaintrospection_p.h84
-rw-r--r--src/widgets/platforms/mac/qcocoapanel_mac.mm70
-rw-r--r--src/widgets/platforms/mac/qcocoapanel_mac_p.h83
-rw-r--r--src/widgets/platforms/mac/qcocoasharedwindowmethods_mac_p.h610
-rw-r--r--src/widgets/platforms/mac/qcocoaview_mac.mm1388
-rw-r--r--src/widgets/platforms/mac/qcocoaview_mac_p.h87
-rw-r--r--src/widgets/platforms/mac/qcocoawindow_mac.mm90
-rw-r--r--src/widgets/platforms/mac/qcocoawindow_mac_p.h97
-rw-r--r--src/widgets/platforms/mac/qcocoawindowcustomthemeframe_mac.mm62
-rw-r--r--src/widgets/platforms/mac/qcocoawindowcustomthemeframe_mac_p.h61
-rw-r--r--src/widgets/platforms/mac/qcocoawindowdelegate_mac.mm439
-rw-r--r--src/widgets/platforms/mac/qcocoawindowdelegate_mac_p.h110
-rw-r--r--src/widgets/platforms/mac/qcolormap_mac.cpp111
-rw-r--r--src/widgets/platforms/mac/qcursor_mac.mm689
-rw-r--r--src/widgets/platforms/mac/qdesktopwidget_mac.mm257
-rw-r--r--src/widgets/platforms/mac/qdesktopwidget_mac_p.h75
-rw-r--r--src/widgets/platforms/mac/qdnd_mac.mm753
-rw-r--r--src/widgets/platforms/mac/qeventdispatcher_mac.mm1200
-rw-r--r--src/widgets/platforms/mac/qeventdispatcher_mac_p.h224
-rw-r--r--src/widgets/platforms/mac/qfont_mac.cpp166
-rw-r--r--src/widgets/platforms/mac/qfontdatabase_mac.cpp494
-rw-r--r--src/widgets/platforms/mac/qfontengine_coretext.mm870
-rw-r--r--src/widgets/platforms/mac/qfontengine_coretext_p.h153
-rw-r--r--src/widgets/platforms/mac/qfontengine_mac.mm1229
-rw-r--r--src/widgets/platforms/mac/qfontengine_mac_p.h165
-rw-r--r--src/widgets/platforms/mac/qkeymapper_mac.cpp1023
-rw-r--r--src/widgets/platforms/mac/qmacdefines_mac.h158
-rw-r--r--src/widgets/platforms/mac/qmacgesturerecognizer_mac.mm272
-rw-r--r--src/widgets/platforms/mac/qmacgesturerecognizer_mac_p.h106
-rw-r--r--src/widgets/platforms/mac/qmacinputcontext_mac.cpp378
-rw-r--r--src/widgets/platforms/mac/qmacinputcontext_p.h97
-rw-r--r--src/widgets/platforms/mac/qmime_mac.cpp1310
-rw-r--r--src/widgets/platforms/mac/qmultitouch_mac.mm218
-rw-r--r--src/widgets/platforms/mac/qmultitouch_mac_p.h102
-rw-r--r--src/widgets/platforms/mac/qnsframeview_mac_p.h154
-rw-r--r--src/widgets/platforms/mac/qnsthemeframe_mac_p.h246
-rw-r--r--src/widgets/platforms/mac/qnstitledframe_mac_p.h205
-rw-r--r--src/widgets/platforms/mac/qpaintdevice_mac.cpp152
-rw-r--r--src/widgets/platforms/mac/qpaintengine_mac.cpp1647
-rw-r--r--src/widgets/platforms/mac/qpaintengine_mac_p.h256
-rw-r--r--src/widgets/platforms/mac/qpixmap_mac.cpp1174
-rw-r--r--src/widgets/platforms/mac/qpixmap_mac_p.h134
-rw-r--r--src/widgets/platforms/mac/qprintengine_mac.mm911
-rw-r--r--src/widgets/platforms/mac/qprintengine_mac_p.h166
-rw-r--r--src/widgets/platforms/mac/qprinterinfo_mac.cpp120
-rw-r--r--src/widgets/platforms/mac/qrawfont_mac.cpp83
-rw-r--r--src/widgets/platforms/mac/qregion_mac.cpp286
-rw-r--r--src/widgets/platforms/mac/qsound_mac.mm190
-rw-r--r--src/widgets/platforms/mac/qt_cocoa_helpers_mac.mm1563
-rw-r--r--src/widgets/platforms/mac/qt_cocoa_helpers_mac_p.h310
-rw-r--r--src/widgets/platforms/mac/qt_mac.cpp174
-rw-r--r--src/widgets/platforms/mac/qt_mac_p.h286
-rw-r--r--src/widgets/platforms/mac/qtextengine_mac.cpp656
-rw-r--r--src/widgets/platforms/mac/qwidget_mac.mm5418
-rw-r--r--src/widgets/platforms/s60/qapplication_s60.cpp2715
-rw-r--r--src/widgets/platforms/s60/qclipboard_s60.cpp331
-rw-r--r--src/widgets/platforms/s60/qcoefepinputcontext_p.h176
-rw-r--r--src/widgets/platforms/s60/qcoefepinputcontext_s60.cpp1200
-rw-r--r--src/widgets/platforms/s60/qcolormap_s60.cpp107
-rw-r--r--src/widgets/platforms/s60/qcursor_s60.cpp533
-rw-r--r--src/widgets/platforms/s60/qdesktopwidget_s60.cpp316
-rw-r--r--src/widgets/platforms/s60/qdnd_s60.cpp359
-rw-r--r--src/widgets/platforms/s60/qeventdispatcher_s60.cpp196
-rw-r--r--src/widgets/platforms/s60/qeventdispatcher_s60_p.h127
-rw-r--r--src/widgets/platforms/s60/qfont_s60.cpp136
-rw-r--r--src/widgets/platforms/s60/qfontdatabase_s60.cpp1099
-rw-r--r--src/widgets/platforms/s60/qfontengine_s60.cpp566
-rw-r--r--src/widgets/platforms/s60/qfontengine_s60_p.h167
-rw-r--r--src/widgets/platforms/s60/qkeymapper_s60.cpp258
-rw-r--r--src/widgets/platforms/s60/qpaintengine_s60.cpp145
-rw-r--r--src/widgets/platforms/s60/qpaintengine_s60_p.h84
-rw-r--r--src/widgets/platforms/s60/qpixmap_s60.cpp1040
-rw-r--r--src/widgets/platforms/s60/qpixmap_s60_p.h141
-rw-r--r--src/widgets/platforms/s60/qregion_s60.cpp52
-rw-r--r--src/widgets/platforms/s60/qsoftkeymanager_s60.cpp440
-rw-r--r--src/widgets/platforms/s60/qsoftkeymanager_s60_p.h113
-rw-r--r--src/widgets/platforms/s60/qsound_s60.cpp224
-rw-r--r--src/widgets/platforms/s60/qt_s60_p.h651
-rw-r--r--src/widgets/platforms/s60/qwidget_s60.cpp1464
-rw-r--r--src/widgets/platforms/win/qapplication_win.cpp4248
-rw-r--r--src/widgets/platforms/win/qclipboard_win.cpp398
-rw-r--r--src/widgets/platforms/win/qcolormap_win.cpp201
-rw-r--r--src/widgets/platforms/win/qcursor_win.cpp493
-rw-r--r--src/widgets/platforms/win/qdesktopwidget_win.cpp387
-rw-r--r--src/widgets/platforms/win/qdnd_win.cpp1027
-rw-r--r--src/widgets/platforms/win/qfont_win.cpp166
-rw-r--r--src/widgets/platforms/win/qfontdatabase_win.cpp1348
-rw-r--r--src/widgets/platforms/win/qfontengine_win.cpp1336
-rw-r--r--src/widgets/platforms/win/qfontengine_win_p.h164
-rw-r--r--src/widgets/platforms/win/qguifunctions_wince.cpp408
-rw-r--r--src/widgets/platforms/win/qguifunctions_wince.h151
-rw-r--r--src/widgets/platforms/win/qkeymapper_win.cpp1207
-rw-r--r--src/widgets/platforms/win/qmime_win.cpp1556
-rw-r--r--src/widgets/platforms/win/qole_win.cpp255
-rw-r--r--src/widgets/platforms/win/qpaintdevice_win.cpp62
-rw-r--r--src/widgets/platforms/win/qpixmap_win.cpp477
-rw-r--r--src/widgets/platforms/win/qprintengine_win.cpp1775
-rw-r--r--src/widgets/platforms/win/qprintengine_win_p.h261
-rw-r--r--src/widgets/platforms/win/qprinterinfo_win.cpp122
-rw-r--r--src/widgets/platforms/win/qrawfont_win.cpp709
-rw-r--r--src/widgets/platforms/win/qregion_win.cpp149
-rw-r--r--src/widgets/platforms/win/qsound_win.cpp205
-rw-r--r--src/widgets/platforms/win/qwidget_win.cpp2139
-rw-r--r--src/widgets/platforms/win/qwidget_wince.cpp675
-rw-r--r--src/widgets/platforms/win/qwininputcontext_p.h111
-rw-r--r--src/widgets/platforms/win/qwininputcontext_win.cpp847
-rw-r--r--src/widgets/platforms/win/qwinnativepangesturerecognizer_win.cpp133
-rw-r--r--src/widgets/platforms/win/qwinnativepangesturerecognizer_win_p.h80
-rw-r--r--src/widgets/platforms/x11/qapplication_x11.cpp6239
-rw-r--r--src/widgets/platforms/x11/qclipboard_x11.cpp1539
-rw-r--r--src/widgets/platforms/x11/qcolormap_x11.cpp670
-rw-r--r--src/widgets/platforms/x11/qcursor_x11.cpp640
-rw-r--r--src/widgets/platforms/x11/qdesktopwidget_x11.cpp406
-rw-r--r--src/widgets/platforms/x11/qdnd_x11.cpp2072
-rw-r--r--src/widgets/platforms/x11/qeventdispatcher_x11.cpp191
-rw-r--r--src/widgets/platforms/x11/qeventdispatcher_x11_p.h86
-rw-r--r--src/widgets/platforms/x11/qfont_x11.cpp368
-rw-r--r--src/widgets/platforms/x11/qfontdatabase_x11.cpp2154
-rw-r--r--src/widgets/platforms/x11/qfontengine_x11.cpp1205
-rw-r--r--src/widgets/platforms/x11/qfontengine_x11_p.h180
-rw-r--r--src/widgets/platforms/x11/qkde.cpp176
-rw-r--r--src/widgets/platforms/x11/qkde_p.h81
-rw-r--r--src/widgets/platforms/x11/qkeymapper_x11.cpp1869
-rw-r--r--src/widgets/platforms/x11/qkeymapper_x11_p.cpp489
-rw-r--r--src/widgets/platforms/x11/qmotifdnd_x11.cpp1031
-rw-r--r--src/widgets/platforms/x11/qpaintdevice_x11.cpp84
-rw-r--r--src/widgets/platforms/x11/qpaintengine_x11.cpp2499
-rw-r--r--src/widgets/platforms/x11/qpaintengine_x11_p.h246
-rw-r--r--src/widgets/platforms/x11/qpixmap_x11.cpp2419
-rw-r--r--src/widgets/platforms/x11/qpixmap_x11_p.h156
-rw-r--r--src/widgets/platforms/x11/qregion_x11.cpp92
-rw-r--r--src/widgets/platforms/x11/qsound_x11.cpp296
-rw-r--r--src/widgets/platforms/x11/qt_x11_p.h757
-rw-r--r--src/widgets/platforms/x11/qwidget_x11.cpp3149
-rw-r--r--src/widgets/platforms/x11/qwidgetcreate_x11.cpp79
-rw-r--r--src/widgets/platforms/x11/qx11embed_x11.cpp1808
-rw-r--r--src/widgets/platforms/x11/qx11embed_x11.h132
-rw-r--r--src/widgets/platforms/x11/qx11info_x11.cpp543
-rw-r--r--src/widgets/platforms/x11/qx11info_x11.h123
-rw-r--r--src/widgets/platforms/x11/qximinputcontext_p.h142
-rw-r--r--src/widgets/platforms/x11/qximinputcontext_x11.cpp885
144 files changed, 97788 insertions, 0 deletions
diff --git a/src/widgets/platforms/mac/qapplication_mac.mm b/src/widgets/platforms/mac/qapplication_mac.mm
new file mode 100644
index 0000000000..5868849836
--- /dev/null
+++ b/src/widgets/platforms/mac/qapplication_mac.mm
@@ -0,0 +1,3131 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** Copyright (c) 2007-2008, Apple, Inc.
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**
+** * Redistributions of source code must retain the above copyright notice,
+** this list of conditions and the following disclaimer.
+**
+** * Redistributions in binary form must reproduce the above copyright notice,
+** this list of conditions and the following disclaimer in the documentation
+** and/or other materials provided with the distribution.
+**
+** * Neither the name of Apple, Inc. nor the names of its contributors
+** may be used to endorse or promote products derived from this software
+** without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**
+****************************************************************************/
+
+#include <Cocoa/Cocoa.h>
+
+#include "qapplication.h"
+#include "qbitarray.h"
+#include "qclipboard.h"
+#include "qcursor.h"
+#include "qdatastream.h"
+#include "qdatetime.h"
+#include "qdesktopwidget.h"
+#include "qdockwidget.h"
+#include "qevent.h"
+#include "qhash.h"
+#include "qlayout.h"
+#include "qmenubar.h"
+#include "qmessagebox.h"
+#include "qmime.h"
+#include "qpixmapcache.h"
+#include "qpointer.h"
+#include "qsessionmanager.h"
+#include "qsettings.h"
+#include "qsocketnotifier.h"
+#include "qstyle.h"
+#include "qstylefactory.h"
+#include "qtextcodec.h"
+#include "qtoolbar.h"
+#include "qvariant.h"
+#include "qwidget.h"
+#include "qcolormap.h"
+#include "qdir.h"
+#include "qdebug.h"
+#include "qtimer.h"
+#include "qurl.h"
+#include "private/qmacinputcontext_p.h"
+#include "private/qpaintengine_mac_p.h"
+#include "private/qcursor_p.h"
+#include "private/qapplication_p.h"
+#include "private/qcolor_p.h"
+#include "private/qwidget_p.h"
+#include "private/qkeymapper_p.h"
+#include "private/qeventdispatcher_mac_p.h"
+#include "private/qeventdispatcher_unix_p.h"
+#include <private/qcocoamenuloader_mac_p.h>
+#include <private/qcocoaapplication_mac_p.h>
+#include <private/qcocoaapplicationdelegate_mac_p.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+#include <private/qcocoawindow_mac_p.h>
+#include <private/qpixmap_mac_p.h>
+#include <private/qdesktopwidget_mac_p.h>
+#include <private/qeventdispatcher_mac_p.h>
+#include <qvarlengtharray.h>
+
+#ifndef QT_NO_ACCESSIBILITY
+# include "qaccessible.h"
+#endif
+
+#ifndef QT_NO_THREAD
+# include "qmutex.h"
+#endif
+
+#include <unistd.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/select.h>
+
+/*****************************************************************************
+ QApplication debug facilities
+ *****************************************************************************/
+//#define DEBUG_EVENTS //like EventDebug but more specific to Qt
+//#define DEBUG_DROPPED_EVENTS
+//#define DEBUG_MOUSE_MAPS
+//#define DEBUG_MODAL_EVENTS
+//#define DEBUG_PLATFORM_SETTINGS
+
+#define QMAC_SPEAK_TO_ME
+#ifdef QMAC_SPEAK_TO_ME
+#include "qregexp.h"
+#endif
+
+#ifndef kThemeBrushAlternatePrimaryHighlightColor
+#define kThemeBrushAlternatePrimaryHighlightColor -5
+#endif
+
+#define kCMDeviceUnregisteredNotification CFSTR("CMDeviceUnregisteredNotification")
+#define kCMDefaultDeviceNotification CFSTR("CMDefaultDeviceNotification")
+#define kCMDeviceProfilesNotification CFSTR("CMDeviceProfilesNotification")
+#define kCMDefaultDeviceProfileNotification CFSTR("CMDefaultDeviceProfileNotification")
+
+QT_BEGIN_NAMESPACE
+
+//for qt_mac.h
+QPaintDevice *qt_mac_safe_pdev = 0;
+QList<QMacWindowChangeEvent*> *QMacWindowChangeEvent::change_events = 0;
+QPointer<QWidget> topLevelAt_cache = 0;
+
+/*****************************************************************************
+ Internal variables and functions
+ *****************************************************************************/
+static struct {
+ bool use_qt_time_limit;
+ QPointer<QWidget> last_widget;
+ int last_x, last_y;
+ int last_modifiers, last_button;
+ EventTime last_time;
+} qt_mac_dblclick = { false, 0, -1, -1, 0, 0, -2 };
+
+static bool app_do_modal = false; // modal mode
+extern QWidgetList *qt_modal_stack; // stack of modal widgets
+extern bool qt_tab_all_widgets; // from qapplication.cpp
+bool qt_scrollbar_jump_to_pos = false;
+static bool qt_mac_collapse_on_dblclick = true;
+extern int qt_antialiasing_threshold; // from qapplication.cpp
+QWidget * qt_button_down; // widget got last button-down
+QPointer<QWidget> qt_last_mouse_receiver;
+#ifndef QT_MAC_USE_COCOA
+static bool qt_button_down_in_content; // whether the button_down was in the content area.
+static bool qt_mac_previous_press_in_popup_mode = false;
+static bool qt_mac_no_click_through_mode = false;
+static int tablet_button_state = 0;
+#endif
+#if defined(QT_DEBUG)
+static bool appNoGrab = false; // mouse/keyboard grabbing
+#endif
+#ifndef QT_MAC_USE_COCOA
+static EventHandlerRef app_proc_handler = 0;
+static EventHandlerUPP app_proc_handlerUPP = 0;
+#endif
+static AEEventHandlerUPP app_proc_ae_handlerUPP = NULL;
+static EventHandlerRef tablet_proximity_handler = 0;
+static EventHandlerUPP tablet_proximity_UPP = 0;
+bool QApplicationPrivate::native_modal_dialog_active;
+
+Q_GUI_EXPORT bool qt_applefontsmoothing_enabled;
+
+/*****************************************************************************
+ External functions
+ *****************************************************************************/
+extern void qt_mac_beep(); //qsound_mac.mm
+extern Qt::KeyboardModifiers qt_mac_get_modifiers(int keys); //qkeymapper_mac.cpp
+extern bool qt_mac_can_clickThrough(const QWidget *); //qwidget_mac.cpp
+extern bool qt_mac_is_macdrawer(const QWidget *); //qwidget_mac.cpp
+extern OSWindowRef qt_mac_window_for(const QWidget*); //qwidget_mac.cpp
+extern QWidget *qt_mac_find_window(OSWindowRef); //qwidget_mac.cpp
+extern void qt_mac_set_cursor(const QCursor *); //qcursor_mac.cpp
+extern bool qt_mac_is_macsheet(const QWidget *); //qwidget_mac.cpp
+extern QString qt_mac_from_pascal_string(const Str255); //qglobal.cpp
+extern void qt_mac_command_set_enabled(MenuRef, UInt32, bool); //qmenu_mac.cpp
+extern bool qt_sendSpontaneousEvent(QObject *obj, QEvent *event); // qapplication.cpp
+extern void qt_mac_update_cursor(); // qcursor_mac.mm
+
+// Forward Decls
+void onApplicationWindowChangedActivation( QWidget*widget, bool activated );
+void onApplicationChangedActivation( bool activated );
+
+static void qt_mac_read_fontsmoothing_settings()
+{
+ qt_applefontsmoothing_enabled = true;
+ int w = 10, h = 10;
+ QImage image(w, h, QImage::Format_RGB32);
+ image.fill(0xffffffff);
+ QPainter p(&image);
+ p.drawText(0, h, "X\\");
+ p.end();
+
+ const int *bits = (const int *) ((const QImage &) image).bits();
+ int bpl = image.bytesPerLine() / 4;
+ for (int y=0; y<w; ++y) {
+ for (int x=0; x<h; ++x) {
+ int r = qRed(bits[x]);
+ int g = qGreen(bits[x]);
+ int b = qBlue(bits[x]);
+ if (r != g || r != b) {
+ qt_applefontsmoothing_enabled = true;
+ return;
+ }
+ }
+ bits += bpl;
+ }
+ qt_applefontsmoothing_enabled = false;
+}
+
+Q_GUI_EXPORT bool qt_mac_execute_apple_script(const char *script, long script_len, AEDesc *ret) {
+ OSStatus err;
+ AEDesc scriptTextDesc;
+ ComponentInstance theComponent = 0;
+ OSAID scriptID = kOSANullScript, resultID = kOSANullScript;
+
+ // set up locals to a known state
+ AECreateDesc(typeNull, 0, 0, &scriptTextDesc);
+ scriptID = kOSANullScript;
+ resultID = kOSANullScript;
+
+ // open the scripting component
+ theComponent = OpenDefaultComponent(kOSAComponentType, typeAppleScript);
+ if (!theComponent) {
+ err = paramErr;
+ goto bail;
+ }
+
+ // put the script text into an aedesc
+ err = AECreateDesc(typeUTF8Text, script, script_len, &scriptTextDesc);
+ if (err != noErr)
+ goto bail;
+
+ // compile the script
+ err = OSACompile(theComponent, &scriptTextDesc, kOSAModeNull, &scriptID);
+ if (err != noErr)
+ goto bail;
+
+ // run the script
+ err = OSAExecute(theComponent, scriptID, kOSANullScript, kOSAModeNull, &resultID);
+
+ // collect the results - if any
+ if (ret) {
+ AECreateDesc(typeNull, 0, 0, ret);
+ if (err == errOSAScriptError)
+ OSAScriptError(theComponent, kOSAErrorMessage, typeChar, ret);
+ else if (err == noErr && resultID != kOSANullScript)
+ OSADisplay(theComponent, resultID, typeChar, kOSAModeNull, ret);
+ }
+bail:
+ AEDisposeDesc(&scriptTextDesc);
+ if (scriptID != kOSANullScript)
+ OSADispose(theComponent, scriptID);
+ if (resultID != kOSANullScript)
+ OSADispose(theComponent, resultID);
+ if (theComponent)
+ CloseComponent(theComponent);
+ return err == noErr;
+}
+
+Q_GUI_EXPORT bool qt_mac_execute_apple_script(const char *script, AEDesc *ret)
+{
+ return qt_mac_execute_apple_script(script, qstrlen(script), ret);
+}
+
+Q_GUI_EXPORT bool qt_mac_execute_apple_script(const QString &script, AEDesc *ret)
+{
+ const QByteArray l = script.toUtf8(); return qt_mac_execute_apple_script(l.constData(), l.size(), ret);
+}
+
+/* Resolution change magic */
+void qt_mac_display_change_callbk(CGDirectDisplayID, CGDisplayChangeSummaryFlags flags, void *)
+{
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+ const bool resized = flags & kCGDisplayDesktopShapeChangedFlag;
+#else
+ Q_UNUSED(flags);
+ const bool resized = true;
+#endif
+ if (resized && qApp) {
+ if (QDesktopWidget *dw = qApp->desktop()) {
+ QResizeEvent *re = new QResizeEvent(dw->size(), dw->size());
+ QApplication::postEvent(dw, re);
+ QCoreGraphicsPaintEngine::cleanUpMacColorSpaces();
+ }
+ }
+}
+
+#ifdef DEBUG_PLATFORM_SETTINGS
+static void qt_mac_debug_palette(const QPalette &pal, const QPalette &pal2, const QString &where)
+{
+ const char *const groups[] = {"Active", "Disabled", "Inactive" };
+ const char *const roles[] = { "WindowText", "Button", "Light", "Midlight", "Dark", "Mid",
+ "Text", "BrightText", "ButtonText", "Base", "Window", "Shadow",
+ "Highlight", "HighlightedText", "Link", "LinkVisited" };
+ if (!where.isNull())
+ qDebug("qt-internal: %s", where.toLatin1().constData());
+ for(int grp = 0; grp < QPalette::NColorGroups; grp++) {
+ for(int role = 0; role < QPalette::NColorRoles; role++) {
+ QBrush b = pal.brush((QPalette::ColorGroup)grp, (QPalette::ColorRole)role);
+ QPixmap pm = b.texture();
+ qDebug(" %s::%s %d::%d::%d [%p]%s", groups[grp], roles[role], b.color().red(),
+ b.color().green(), b.color().blue(), pm.isNull() ? 0 : &pm,
+ pal2.brush((QPalette::ColorGroup)grp, (QPalette::ColorRole)role) != b ? " (*)" : "");
+ }
+ }
+
+}
+#else
+#define qt_mac_debug_palette(x, y, z)
+#endif
+
+//raise a notification
+#ifndef QT_MAC_USE_COCOA
+static NMRec qt_mac_notification = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
+void qt_mac_send_notification()
+{
+#ifndef QT_MAC_USE_COCOA
+ //send it
+ qt_mac_notification.nmMark = 1; //non-zero magic number
+ qt_mac_notification.qType = nmType;
+ NMInstall(&qt_mac_notification);
+#else
+ QMacCocoaAutoReleasePool pool;
+ [[NSApplication sharedApplication] requestUserAttention:NSInformationalRequest];
+#endif
+}
+
+void qt_mac_cancel_notification()
+{
+#ifndef QT_MAC_USE_COCOA
+ NMRemove(&qt_mac_notification);
+#else
+ QMacCocoaAutoReleasePool pool;
+ [[NSApplication sharedApplication] cancelUserAttentionRequest:NSInformationalRequest];
+#endif
+}
+
+#ifndef QT_MAC_USE_COCOA
+//find widget (and part) at a given point
+static short qt_mac_window_at(int x, int y, QWidget **w=0)
+{
+ Point p;
+ p.h = x;
+ p.v = y;
+ OSWindowRef wp;
+ WindowPartCode wpc;
+ OSStatus err = FindWindowOfClass(&p, kAllWindowClasses, &wp, &wpc);
+ if(err != noErr) {
+ if(w)
+ (*w) = 0;
+ return wpc;
+ }
+ if(w) {
+ if(wp) {
+ *w = qt_mac_find_window(wp);
+#if 0
+ if(!*w)
+ qWarning("QApplication: qt_mac_window_at: Couldn't find %d",(int)wp);
+#endif
+ } else {
+ *w = 0;
+ }
+ }
+ return wpc;
+}
+
+#endif
+
+void qt_mac_set_app_icon(const QPixmap &pixmap)
+{
+#ifndef QT_MAC_USE_COCOA
+ if(pixmap.isNull()) {
+ RestoreApplicationDockTileImage();
+ } else {
+ CGImageRef img = (CGImageRef)pixmap.macCGHandle();
+ SetApplicationDockTileImage(img);
+ CGImageRelease(img);
+ }
+#else
+ QMacCocoaAutoReleasePool pool;
+ NSImage *image = NULL;
+ if (pixmap.isNull()) {
+ // Get Application icon from bundle
+ image = [[NSImage imageNamed:@"NSApplicationIcon"] retain]; // released below
+ } else {
+ image = static_cast<NSImage *>(qt_mac_create_nsimage(pixmap));
+ }
+
+ [NSApp setApplicationIconImage:image];
+ [image release];
+#endif
+}
+
+Q_GUI_EXPORT void qt_mac_set_press_and_hold_context(bool b)
+{
+ Q_UNUSED(b);
+ qWarning("qt_mac_set_press_and_hold_context: This functionality is no longer available");
+}
+
+bool qt_nograb() // application no-grab option
+{
+#if defined(QT_DEBUG)
+ return appNoGrab;
+#else
+ return false;
+#endif
+}
+
+void qt_mac_update_os_settings()
+{
+ if (!qApp)
+ return;
+ if (!QApplication::startingUp()) {
+ static bool needToPolish = true;
+ if (needToPolish) {
+ QApplication::style()->polish(qApp);
+ needToPolish = false;
+ }
+ }
+ //focus mode
+ /* First worked as of 10.2.3 */
+ QSettings appleSettings(QLatin1String("apple.com"));
+ QVariant appleValue = appleSettings.value(QLatin1String("AppleKeyboardUIMode"), 0);
+ qt_tab_all_widgets = (appleValue.toInt() & 0x2);
+ //paging mode
+ /* First worked as of 10.2.3 */
+ appleValue = appleSettings.value(QLatin1String("AppleScrollerPagingBehavior"), false);
+ qt_scrollbar_jump_to_pos = appleValue.toBool();
+ //collapse
+ /* First worked as of 10.3.3 */
+ appleValue = appleSettings.value(QLatin1String("AppleMiniaturizeOnDoubleClick"), true);
+ qt_mac_collapse_on_dblclick = appleValue.toBool();
+
+ // Anti-aliasing threshold
+ appleValue = appleSettings.value(QLatin1String("AppleAntiAliasingThreshold"));
+ if (appleValue.isValid())
+ qt_antialiasing_threshold = appleValue.toInt();
+
+#ifdef DEBUG_PLATFORM_SETTINGS
+ qDebug("qt_mac_update_os_settings *********************************************************************");
+#endif
+ { // setup the global palette
+ QColor qc;
+ (void) QApplication::style(); // trigger creation of application style and system palettes
+ QPalette pal = *QApplicationPrivate::sys_pal;
+
+ pal.setBrush( QPalette::Active, QPalette::Highlight, qcolorForTheme(kThemeBrushPrimaryHighlightColor) );
+ pal.setBrush( QPalette::Inactive, QPalette::Highlight, qcolorForTheme(kThemeBrushSecondaryHighlightColor) );
+
+ pal.setBrush( QPalette::Disabled, QPalette::Highlight, qcolorForTheme(kThemeBrushSecondaryHighlightColor) );
+ pal.setBrush( QPalette::Active, QPalette::Shadow, qcolorForTheme(kThemeBrushButtonActiveDarkShadow) );
+
+ pal.setBrush( QPalette::Inactive, QPalette::Shadow, qcolorForTheme(kThemeBrushButtonInactiveDarkShadow) );
+ pal.setBrush( QPalette::Disabled, QPalette::Shadow, qcolorForTheme(kThemeBrushButtonInactiveDarkShadow) );
+
+ qc = qcolorForThemeTextColor(kThemeTextColorDialogActive);
+ pal.setColor(QPalette::Active, QPalette::Text, qc);
+ pal.setColor(QPalette::Active, QPalette::WindowText, qc);
+ pal.setColor(QPalette::Active, QPalette::HighlightedText, qc);
+
+ qc = qcolorForThemeTextColor(kThemeTextColorDialogInactive);
+ pal.setColor(QPalette::Inactive, QPalette::Text, qc);
+ pal.setColor(QPalette::Inactive, QPalette::WindowText, qc);
+ pal.setColor(QPalette::Inactive, QPalette::HighlightedText, qc);
+ pal.setColor(QPalette::Disabled, QPalette::Text, qc);
+ pal.setColor(QPalette::Disabled, QPalette::WindowText, qc);
+ pal.setColor(QPalette::Disabled, QPalette::HighlightedText, qc);
+ pal.setBrush(QPalette::ToolTipBase, QColor(255, 255, 199));
+
+ if (!QApplicationPrivate::sys_pal || *QApplicationPrivate::sys_pal != pal) {
+ QApplicationPrivate::setSystemPalette(pal);
+ QApplication::setPalette(pal);
+ }
+#ifdef DEBUG_PLATFORM_SETTINGS
+ qt_mac_debug_palette(pal, QApplication::palette(), "Global Palette");
+#endif
+ }
+
+ QFont fnt = qfontForThemeFont(kThemeApplicationFont);
+#ifdef DEBUG_PLATFORM_SETTINGS
+ qDebug("qt-internal: Font for Application [%s::%d::%d::%d]",
+ fnt.family().toLatin1().constData(), fnt.pointSize(), fnt.bold(), fnt.italic());
+#endif
+ if (!QApplicationPrivate::sys_font || *QApplicationPrivate::sys_font != fnt)
+ QApplicationPrivate::setSystemFont(fnt);
+
+ { //setup the fonts
+ struct FontMap {
+ FontMap(const char *qc, short fk) : qt_class(qc), font_key(fk) { }
+ const char *const qt_class;
+ short font_key;
+ } mac_widget_fonts[] = {
+ FontMap("QPushButton", kThemePushButtonFont),
+ FontMap("QListView", kThemeViewsFont),
+ FontMap("QListBox", kThemeViewsFont),
+ FontMap("QTitleBar", kThemeWindowTitleFont),
+ FontMap("QMenuBar", kThemeMenuTitleFont),
+ FontMap("QMenu", kThemeMenuItemFont),
+ FontMap("QComboMenuItem", kThemeSystemFont),
+ FontMap("QHeaderView", kThemeSmallSystemFont),
+ FontMap("Q3Header", kThemeSmallSystemFont),
+ FontMap("QTipLabel", kThemeSmallSystemFont),
+ FontMap("QLabel", kThemeSystemFont),
+ FontMap("QToolButton", kThemeSmallSystemFont),
+ FontMap("QMenuItem", kThemeMenuItemFont), // It doesn't exist, but its unique.
+ FontMap("QComboLineEdit", kThemeViewsFont), // It doesn't exist, but its unique.
+ FontMap("QSmallFont", kThemeSmallSystemFont), // It doesn't exist, but its unique.
+ FontMap("QMiniFont", kThemeMiniSystemFont), // It doesn't exist, but its unique.
+ FontMap(0, 0) };
+ for(int i = 0; mac_widget_fonts[i].qt_class; i++) {
+ QFont fnt = qfontForThemeFont(mac_widget_fonts[i].font_key);
+ bool set_font = true;
+ FontHash *hash = qt_app_fonts_hash();
+ if (!hash->isEmpty()) {
+ FontHash::const_iterator it
+ = hash->constFind(mac_widget_fonts[i].qt_class);
+ if (it != hash->constEnd())
+ set_font = (fnt != *it);
+ }
+ if (set_font) {
+ QApplication::setFont(fnt, mac_widget_fonts[i].qt_class);
+#ifdef DEBUG_PLATFORM_SETTINGS
+ qDebug("qt-internal: Font for %s [%s::%d::%d::%d]", mac_widget_fonts[i].qt_class,
+ fnt.family().toLatin1().constData(), fnt.pointSize(), fnt.bold(), fnt.italic());
+#endif
+ }
+ }
+ }
+ QApplicationPrivate::initializeWidgetPaletteHash();
+#ifdef DEBUG_PLATFORM_SETTINGS
+ qDebug("qt_mac_update_os_settings END !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+#endif
+}
+
+void QApplicationPrivate::initializeWidgetPaletteHash()
+{
+ { //setup the palette
+ struct PaletteMap {
+ inline PaletteMap(const char *qc, ThemeBrush a, ThemeBrush i) :
+ qt_class(qc), active(a), inactive(i) { }
+ const char *const qt_class;
+ ThemeBrush active, inactive;
+ } mac_widget_colors[] = {
+ PaletteMap("QToolButton", kThemeTextColorBevelButtonActive, kThemeTextColorBevelButtonInactive),
+ PaletteMap("QAbstractButton", kThemeTextColorPushButtonActive, kThemeTextColorPushButtonInactive),
+ PaletteMap("QHeaderView", kThemeTextColorPushButtonActive, kThemeTextColorPushButtonInactive),
+ PaletteMap("Q3Header", kThemeTextColorPushButtonActive, kThemeTextColorPushButtonInactive),
+ PaletteMap("QComboBox", kThemeTextColorPopupButtonActive, kThemeTextColorPopupButtonInactive),
+ PaletteMap("QAbstractItemView", kThemeTextColorListView, kThemeTextColorDialogInactive),
+ PaletteMap("QMessageBoxLabel", kThemeTextColorAlertActive, kThemeTextColorAlertInactive),
+ PaletteMap("QTabBar", kThemeTextColorTabFrontActive, kThemeTextColorTabFrontInactive),
+ PaletteMap("QLabel", kThemeTextColorPlacardActive, kThemeTextColorPlacardInactive),
+ PaletteMap("QGroupBox", kThemeTextColorPlacardActive, kThemeTextColorPlacardInactive),
+ PaletteMap("QMenu", kThemeTextColorPopupLabelActive, kThemeTextColorPopupLabelInactive),
+ PaletteMap("QTextEdit", 0, 0),
+ PaletteMap("QTextControl", 0, 0),
+ PaletteMap("QLineEdit", 0, 0),
+ PaletteMap(0, 0, 0) };
+ QColor qc;
+ for(int i = 0; mac_widget_colors[i].qt_class; i++) {
+ QPalette pal;
+ if (mac_widget_colors[i].active != 0) {
+ qc = qcolorForThemeTextColor(mac_widget_colors[i].active);
+ pal.setColor(QPalette::Active, QPalette::Text, qc);
+ pal.setColor(QPalette::Active, QPalette::WindowText, qc);
+ pal.setColor(QPalette::Active, QPalette::HighlightedText, qc);
+ qc = qcolorForThemeTextColor(mac_widget_colors[i].inactive);
+ pal.setColor(QPalette::Inactive, QPalette::Text, qc);
+ pal.setColor(QPalette::Disabled, QPalette::Text, qc);
+ pal.setColor(QPalette::Inactive, QPalette::WindowText, qc);
+ pal.setColor(QPalette::Disabled, QPalette::WindowText, qc);
+ pal.setColor(QPalette::Inactive, QPalette::HighlightedText, qc);
+ pal.setColor(QPalette::Disabled, QPalette::HighlightedText, qc);
+ }
+ if (!strcmp(mac_widget_colors[i].qt_class, "QMenu")) {
+ qc = qcolorForThemeTextColor(kThemeTextColorMenuItemActive);
+ pal.setBrush(QPalette::ButtonText, qc);
+ qc = qcolorForThemeTextColor(kThemeTextColorMenuItemSelected);
+ pal.setBrush(QPalette::HighlightedText, qc);
+ qc = qcolorForThemeTextColor(kThemeTextColorMenuItemDisabled);
+ pal.setBrush(QPalette::Disabled, QPalette::Text, qc);
+ } else if (!strcmp(mac_widget_colors[i].qt_class, "QAbstractButton")
+ || !strcmp(mac_widget_colors[i].qt_class, "QHeaderView")
+ || !strcmp(mac_widget_colors[i].qt_class, "Q3Header")) { //special
+ pal.setColor(QPalette::Disabled, QPalette::ButtonText,
+ pal.color(QPalette::Disabled, QPalette::Text));
+ pal.setColor(QPalette::Inactive, QPalette::ButtonText,
+ pal.color(QPalette::Inactive, QPalette::Text));
+ pal.setColor(QPalette::Active, QPalette::ButtonText,
+ pal.color(QPalette::Active, QPalette::Text));
+ } else if (!strcmp(mac_widget_colors[i].qt_class, "QAbstractItemView")) {
+ pal.setBrush(QPalette::Active, QPalette::Highlight,
+ qcolorForTheme(kThemeBrushAlternatePrimaryHighlightColor));
+ qc = qcolorForThemeTextColor(kThemeTextColorMenuItemSelected);
+ pal.setBrush(QPalette::Active, QPalette::HighlightedText, qc);
+#if 1
+ pal.setBrush(QPalette::Inactive, QPalette::Text,
+ pal.brush(QPalette::Active, QPalette::Text));
+ pal.setBrush(QPalette::Inactive, QPalette::HighlightedText,
+ pal.brush(QPalette::Active, QPalette::Text));
+#endif
+ } else if (!strcmp(mac_widget_colors[i].qt_class, "QTextEdit")
+ || !strcmp(mac_widget_colors[i].qt_class, "QTextControl")) {
+ pal.setBrush(QPalette::Inactive, QPalette::Text,
+ pal.brush(QPalette::Active, QPalette::Text));
+ pal.setBrush(QPalette::Inactive, QPalette::HighlightedText,
+ pal.brush(QPalette::Active, QPalette::Text));
+ } else if (!strcmp(mac_widget_colors[i].qt_class, "QLineEdit")) {
+ pal.setBrush(QPalette::Disabled, QPalette::Base,
+ pal.brush(QPalette::Active, QPalette::Base));
+ }
+
+ bool set_palette = true;
+ PaletteHash *phash = qt_app_palettes_hash();
+ if (!phash->isEmpty()) {
+ PaletteHash::const_iterator it
+ = phash->constFind(mac_widget_colors[i].qt_class);
+ if (it != phash->constEnd())
+ set_palette = (pal != *it);
+ }
+ if (set_palette) {
+ QApplication::setPalette(pal, mac_widget_colors[i].qt_class);
+#ifdef DEBUG_PLATFORM_SETTINGS
+ qt_mac_debug_palette(pal, QApplication::palette(), QLatin1String("Palette for ") + QString::fromLatin1(mac_widget_colors[i].qt_class));
+#endif
+ }
+ }
+ }
+}
+
+static void qt_mac_event_release(EventRef &event)
+{
+ ReleaseEvent(event);
+ event = 0;
+}
+#ifndef QT_MAC_USE_COCOA
+static void qt_mac_event_release(QWidget *w, EventRef &event)
+{
+ if (event) {
+ QWidget *widget = 0;
+ if (GetEventParameter(event, kEventParamQWidget, typeQWidget, 0, sizeof(widget), 0, &widget) == noErr
+ && w == widget) {
+ if (IsEventInQueue(GetMainEventQueue(), event))
+ RemoveEventFromQueue(GetMainEventQueue(), event);
+ qt_mac_event_release(event);
+ }
+ }
+}
+
+static bool qt_mac_event_remove(EventRef &event)
+{
+ if (event) {
+ if (IsEventInQueue(GetMainEventQueue(), event))
+ RemoveEventFromQueue(GetMainEventQueue(), event);
+ qt_mac_event_release(event);
+ return true;
+ }
+ return false;
+}
+#endif
+
+/* sheets */
+#ifndef QT_MAC_USE_COCOA
+static EventRef request_showsheet_pending = 0;
+#endif
+void qt_event_request_showsheet(QWidget *w)
+{
+ Q_ASSERT(qt_mac_is_macsheet(w));
+#ifdef QT_MAC_USE_COCOA
+ [NSApp beginSheet:qt_mac_window_for(w) modalForWindow:qt_mac_window_for(w->parentWidget())
+ modalDelegate:nil didEndSelector:nil contextInfo:0];
+#else
+ qt_mac_event_remove(request_showsheet_pending);
+ CreateEvent(0, kEventClassQt, kEventQtRequestShowSheet, GetCurrentEventTime(),
+ kEventAttributeUserEvent, &request_showsheet_pending);
+ SetEventParameter(request_showsheet_pending, kEventParamQWidget, typeQWidget, sizeof(w), &w);
+ PostEventToQueue(GetMainEventQueue(), request_showsheet_pending, kEventPriorityStandard);
+#endif
+}
+
+static void qt_post_window_change_event(QWidget *widget)
+{
+ qt_widget_private(widget)->needWindowChange = true;
+ QEvent *glWindowChangeEvent = new QEvent(QEvent::MacGLWindowChange);
+ QApplication::postEvent(widget, glWindowChangeEvent);
+}
+
+/*
+ Posts updates to all child and grandchild OpenGL widgets for the given widget.
+*/
+static void qt_mac_update_child_gl_widgets(QWidget *widget)
+{
+ // Update all OpenGL child widgets for the given widget.
+ QList<QWidgetPrivate::GlWidgetInfo> &glWidgets = qt_widget_private(widget)->glWidgets;
+ QList<QWidgetPrivate::GlWidgetInfo>::iterator end = glWidgets.end();
+ QList<QWidgetPrivate::GlWidgetInfo>::iterator it = glWidgets.begin();
+
+ for (;it != end; ++it) {
+ qt_post_window_change_event(it->widget);
+ }
+}
+
+/*
+ Sends updates to all child and grandchild gl widgets that have updates pending.
+*/
+void qt_mac_send_posted_gl_updates(QWidget *widget)
+{
+ QList<QWidgetPrivate::GlWidgetInfo> &glWidgets = qt_widget_private(widget)->glWidgets;
+ QList<QWidgetPrivate::GlWidgetInfo>::iterator end = glWidgets.end();
+ QList<QWidgetPrivate::GlWidgetInfo>::iterator it = glWidgets.begin();
+
+ for (;it != end; ++it) {
+ QWidget *glWidget = it->widget;
+ if (qt_widget_private(glWidget)->needWindowChange) {
+ QEvent glChangeEvent(QEvent::MacGLWindowChange);
+ QApplication::sendEvent(glWidget, &glChangeEvent);
+ }
+ }
+}
+
+/*
+ Posts updates to all OpenGL widgets within the window that the given widget intersects.
+*/
+static void qt_mac_update_intersected_gl_widgets(QWidget *widget)
+{
+#ifndef QT_MAC_USE_COCOA
+ QList<QWidgetPrivate::GlWidgetInfo> &glWidgets = qt_widget_private(widget->window())->glWidgets;
+ if (glWidgets.isEmpty())
+ return;
+
+ // Exit if the window has not been created yet (mapToGlobal/size will force create it)
+ if (widget->testAttribute(Qt::WA_WState_Created) == false || HIViewGetWindow(qt_mac_nativeview_for(widget)) == 0)
+ return;
+
+ const QRect globalWidgetRect = QRect(widget->mapToGlobal(QPoint(0, 0)), widget->size());
+
+ QList<QWidgetPrivate::GlWidgetInfo>::iterator end = glWidgets.end();
+ QList<QWidgetPrivate::GlWidgetInfo>::iterator it = glWidgets.begin();
+
+ for (;it != end; ++it){
+ QWidget *glWidget = it->widget;
+ const QRect globalGlWidgetRect = QRect(glWidget->mapToGlobal(QPoint(0, 0)), glWidget->size());
+ if (globalWidgetRect.intersects(globalGlWidgetRect)) {
+ qt_post_window_change_event(glWidget);
+ it->lastUpdateWidget = widget;
+ } else if (it->lastUpdateWidget == widget) {
+ // Update the gl wigets that the widget intersected the last time around,
+ // and that we are not intersecting now. This prevents paint errors when the
+ // intersecting widget leaves a gl widget.
+ qt_post_window_change_event(glWidget);
+ it->lastUpdateWidget = 0;
+ }
+ }
+#else
+ Q_UNUSED(widget);
+#endif
+}
+
+/*
+ Posts a kEventQtRequestWindowChange event to the main Carbon event queue.
+*/
+static EventRef request_window_change_pending = 0;
+Q_GUI_EXPORT void qt_event_request_window_change()
+{
+ if(request_window_change_pending)
+ return;
+
+ CreateEvent(0, kEventClassQt, kEventQtRequestWindowChange, GetCurrentEventTime(),
+ kEventAttributeUserEvent, &request_window_change_pending);
+ PostEventToQueue(GetMainEventQueue(), request_window_change_pending, kEventPriorityHigh);
+}
+
+/* window changing. This is a hack around Apple's missing functionality, pending the toolbox
+ team fix. --Sam */
+Q_GUI_EXPORT void qt_event_request_window_change(QWidget *widget)
+{
+ if (!widget)
+ return;
+
+ // Post a kEventQtRequestWindowChange event. This event is semi-public,
+ // don't remove this line!
+ qt_event_request_window_change();
+
+ // Post update request on gl widgets unconditionally.
+ if (qt_widget_private(widget)->isGLWidget == true) {
+ qt_post_window_change_event(widget);
+ return;
+ }
+
+ qt_mac_update_child_gl_widgets(widget);
+ qt_mac_update_intersected_gl_widgets(widget);
+}
+
+/* activation */
+static struct {
+ QPointer<QWidget> widget;
+ EventRef event;
+ EventLoopTimerRef timer;
+ EventLoopTimerUPP timerUPP;
+} request_activate_pending = { 0, 0, 0, 0 };
+bool qt_event_remove_activate()
+{
+ if (request_activate_pending.timer) {
+ RemoveEventLoopTimer(request_activate_pending.timer);
+ request_activate_pending.timer = 0;
+ }
+ if (request_activate_pending.event)
+ qt_mac_event_release(request_activate_pending.event);
+ return true;
+}
+
+void qt_event_activate_timer_callbk(EventLoopTimerRef r, void *)
+{
+ EventLoopTimerRef otc = request_activate_pending.timer;
+ qt_event_remove_activate();
+ if (r == otc && !request_activate_pending.widget.isNull()) {
+ const QWidget *tlw = request_activate_pending.widget->window();
+ Qt::WindowType wt = tlw->windowType();
+ if (tlw->isVisible()
+ && ((wt != Qt::Desktop && wt != Qt::Popup && wt != Qt::Tool) || tlw->isModal())) {
+ CreateEvent(0, kEventClassQt, kEventQtRequestActivate, GetCurrentEventTime(),
+ kEventAttributeUserEvent, &request_activate_pending.event);
+ PostEventToQueue(GetMainEventQueue(), request_activate_pending.event, kEventPriorityHigh);
+ }
+ }
+}
+
+void qt_event_request_activate(QWidget *w)
+{
+ if (w == request_activate_pending.widget)
+ return;
+
+ /* We put these into a timer because due to order of events being sent we need to be sure this
+ comes from inside of the event loop */
+ qt_event_remove_activate();
+ if (!request_activate_pending.timerUPP)
+ request_activate_pending.timerUPP = NewEventLoopTimerUPP(qt_event_activate_timer_callbk);
+ request_activate_pending.widget = w;
+ InstallEventLoopTimer(GetMainEventLoop(), 0, 0, request_activate_pending.timerUPP, 0, &request_activate_pending.timer);
+}
+
+
+/* menubars */
+#ifndef QT_MAC_USE_COCOA
+static EventRef request_menubarupdate_pending = 0;
+#endif
+void qt_event_request_menubarupdate()
+{
+#ifndef QT_MAC_USE_COCOA
+ if (request_menubarupdate_pending) {
+ if (IsEventInQueue(GetMainEventQueue(), request_menubarupdate_pending))
+ return;
+#ifdef DEBUG_DROPPED_EVENTS
+ qDebug("%s:%d Whoa, we dropped an event on the floor!", __FILE__, __LINE__);
+#endif
+ }
+
+ CreateEvent(0, kEventClassQt, kEventQtRequestMenubarUpdate, GetCurrentEventTime(),
+ kEventAttributeUserEvent, &request_menubarupdate_pending);
+ PostEventToQueue(GetMainEventQueue(), request_menubarupdate_pending, kEventPriorityHigh);
+#else
+ // Just call this. The request has the benefit that we don't call this multiple times, but
+ // we can optimize this.
+ QMenuBar::macUpdateMenuBar();
+#endif
+}
+
+#ifndef QT_MAC_USE_COCOA
+//context menu
+static EventRef request_context_pending = 0;
+static void qt_event_request_context(QWidget *w=0, EventRef *where=0)
+{
+ if (!where)
+ where = &request_context_pending;
+ if (*where)
+ return;
+ CreateEvent(0, kEventClassQt, kEventQtRequestContext, GetCurrentEventTime(),
+ kEventAttributeUserEvent, where);
+ if (w)
+ SetEventParameter(*where, kEventParamQWidget, typeQWidget, sizeof(w), &w);
+ PostEventToQueue(GetMainEventQueue(), *where, kEventPriorityStandard);
+}
+#endif
+
+void QApplicationPrivate::createEventDispatcher()
+{
+ Q_Q(QApplication);
+ if (q->type() != QApplication::Tty)
+ eventDispatcher = new QEventDispatcherMac(q);
+ else
+ eventDispatcher = new QEventDispatcherUNIX(q);
+}
+
+/* clipboard */
+void qt_event_send_clipboard_changed()
+{
+#ifndef QT_MAC_USE_COCOA
+ AppleEvent ae;
+ if (AECreateAppleEvent(kEventClassQt, typeAEClipboardChanged, 0, kAutoGenerateReturnID, kAnyTransactionID, &ae) != noErr)
+ qDebug("Can't happen!!");
+ AppleEvent reply;
+ AESend(&ae, &reply, kAENoReply, kAENormalPriority, kAEDefaultTimeout, 0, 0);
+#endif
+}
+
+/* app menu */
+static QMenu *qt_mac_dock_menu = 0;
+Q_GUI_EXPORT void qt_mac_set_dock_menu(QMenu *menu)
+{
+ qt_mac_dock_menu = menu;
+#ifdef QT_MAC_USE_COCOA
+ [NSApp setDockMenu:menu->macMenu()];
+#else
+ SetApplicationDockTileMenu(menu->macMenu());
+#endif
+}
+
+/* events that hold pointers to widgets, must be cleaned up like this */
+void qt_mac_event_release(QWidget *w)
+{
+ if (w) {
+#ifndef QT_MAC_USE_COCOA
+ qt_mac_event_release(w, request_showsheet_pending);
+ qt_mac_event_release(w, request_context_pending);
+#endif
+ if (w == qt_mac_dock_menu) {
+ qt_mac_dock_menu = 0;
+#ifndef QT_MAC_USE_COCOA
+ SetApplicationDockTileMenu(0);
+#else
+ [NSApp setDockMenu:0];
+#endif
+ }
+ }
+}
+
+struct QMacAppleEventTypeSpec {
+ AEEventClass mac_class;
+ AEEventID mac_id;
+} app_apple_events[] = {
+ { kCoreEventClass, kAEQuitApplication },
+ { kCoreEventClass, kAEOpenDocuments },
+ { kInternetEventClass, kAEGetURL },
+};
+
+#ifndef QT_MAC_USE_COCOA
+
+#if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5)
+enum
+{
+ kEventMouseScroll = 11,
+ kEventParamMouseWheelSmoothVerticalDelta = 'saxy',
+ kEventParamMouseWheelSmoothHorizontalDelta = 'saxx',
+};
+#endif
+
+/* watched events */
+static EventTypeSpec app_events[] = {
+ { kEventClassQt, kEventQtRequestWindowChange },
+ { kEventClassQt, kEventQtRequestShowSheet },
+ { kEventClassQt, kEventQtRequestContext },
+ { kEventClassQt, kEventQtRequestActivate },
+ { kEventClassQt, kEventQtRequestMenubarUpdate },
+
+ { kEventClassWindow, kEventWindowActivated },
+ { kEventClassWindow, kEventWindowDeactivated },
+
+ { kEventClassMouse, kEventMouseScroll },
+ { kEventClassMouse, kEventMouseWheelMoved },
+ { kEventClassMouse, kEventMouseDown },
+ { kEventClassMouse, kEventMouseUp },
+ { kEventClassMouse, kEventMouseDragged },
+ { kEventClassMouse, kEventMouseMoved },
+
+ { kEventClassTablet, kEventTabletProximity },
+
+ { kEventClassApplication, kEventAppActivated },
+ { kEventClassApplication, kEventAppDeactivated },
+ { kEventClassApplication, kEventAppAvailableWindowBoundsChanged },
+
+ // { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent },
+ { kEventClassKeyboard, kEventRawKeyModifiersChanged },
+ { kEventClassKeyboard, kEventRawKeyRepeat },
+ { kEventClassKeyboard, kEventRawKeyUp },
+ { kEventClassKeyboard, kEventRawKeyDown },
+
+ { kEventClassCommand, kEventCommandProcess },
+
+ { kEventClassAppleEvent, kEventAppleEvent },
+
+ { kAppearanceEventClass, kAEAppearanceChanged }
+};
+
+void qt_init_app_proc_handler()
+{
+ InstallEventHandler(GetApplicationEventTarget(), app_proc_handlerUPP,
+ GetEventTypeCount(app_events), app_events, (void *)qApp,
+ &app_proc_handler);
+}
+#endif // QT_MAC_USE_COCOA
+
+static void qt_init_tablet_proximity_handler()
+{
+ EventTypeSpec tabletProximityEvent = { kEventClassTablet, kEventTabletProximity };
+ InstallEventHandler(GetEventMonitorTarget(), tablet_proximity_UPP,
+ 1, &tabletProximityEvent, qApp, &tablet_proximity_handler);
+}
+
+static void qt_release_tablet_proximity_handler()
+{
+ RemoveEventHandler(tablet_proximity_handler);
+}
+
+QString QApplicationPrivate::appName() const
+{
+ static QString applName;
+ if (applName.isEmpty()) {
+ applName = QCoreApplicationPrivate::macMenuBarName();
+ ProcessSerialNumber psn;
+ if (applName.isEmpty() && qt_is_gui_used && GetCurrentProcess(&psn) == noErr) {
+ QCFString cfstr;
+ CopyProcessName(&psn, &cfstr);
+ applName = cfstr;
+ }
+ }
+ return applName;
+}
+
+void qt_release_app_proc_handler()
+{
+#ifndef QT_MAC_USE_COCOA
+ if (app_proc_handler) {
+ RemoveEventHandler(app_proc_handler);
+ app_proc_handler = 0;
+ }
+#endif
+}
+
+void qt_color_profile_changed(CFNotificationCenterRef, void *, CFStringRef, const void *,
+ CFDictionaryRef)
+{
+ QCoreGraphicsPaintEngine::cleanUpMacColorSpaces();
+}
+/* platform specific implementations */
+void qt_init(QApplicationPrivate *priv, int)
+{
+ if (qt_is_gui_used) {
+ CGDisplayRegisterReconfigurationCallback(qt_mac_display_change_callbk, 0);
+ CFNotificationCenterRef center = CFNotificationCenterGetDistributedCenter();
+ CFNotificationCenterAddObserver(center, qApp, qt_color_profile_changed,
+ kCMDeviceUnregisteredNotification, 0,
+ CFNotificationSuspensionBehaviorDeliverImmediately);
+ CFNotificationCenterAddObserver(center, qApp, qt_color_profile_changed,
+ kCMDefaultDeviceNotification, 0,
+ CFNotificationSuspensionBehaviorDeliverImmediately);
+ CFNotificationCenterAddObserver(center, qApp, qt_color_profile_changed,
+ kCMDeviceProfilesNotification, 0,
+ CFNotificationSuspensionBehaviorDeliverImmediately);
+ CFNotificationCenterAddObserver(center, qApp, qt_color_profile_changed,
+ kCMDefaultDeviceProfileNotification, 0,
+ CFNotificationSuspensionBehaviorDeliverImmediately);
+ ProcessSerialNumber psn;
+ if (GetCurrentProcess(&psn) == noErr) {
+ // Jambi needs to transform itself since most people aren't "used"
+ // to putting things in bundles, but other people may actually not
+ // want to tranform the process (running as a helper or something)
+ // so don't do that for them. This means checking both LSUIElement
+ // and LSBackgroundOnly. If you set them both... well, you
+ // shouldn't do that.
+
+ bool forceTransform = true;
+ CFTypeRef value = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(),
+ CFSTR("LSUIElement"));
+ if (value) {
+ CFTypeID valueType = CFGetTypeID(value);
+ // Officially it's supposed to be a string, a boolean makes sense, so we'll check.
+ // A number less so, but OK.
+ if (valueType == CFStringGetTypeID())
+ forceTransform = !(QCFString::toQString(static_cast<CFStringRef>(value)).toInt());
+ else if (valueType == CFBooleanGetTypeID())
+ forceTransform = !CFBooleanGetValue(static_cast<CFBooleanRef>(value));
+ else if (valueType == CFNumberGetTypeID()) {
+ int valueAsInt;
+ CFNumberGetValue(static_cast<CFNumberRef>(value), kCFNumberIntType, &valueAsInt);
+ forceTransform = !valueAsInt;
+ }
+ }
+
+ if (forceTransform) {
+ value = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(),
+ CFSTR("LSBackgroundOnly"));
+ if (value) {
+ CFTypeID valueType = CFGetTypeID(value);
+ if (valueType == CFBooleanGetTypeID())
+ forceTransform = !CFBooleanGetValue(static_cast<CFBooleanRef>(value));
+ else if (valueType == CFStringGetTypeID())
+ forceTransform = !(QCFString::toQString(static_cast<CFStringRef>(value)).toInt());
+ else if (valueType == CFNumberGetTypeID()) {
+ int valueAsInt;
+ CFNumberGetValue(static_cast<CFNumberRef>(value), kCFNumberIntType, &valueAsInt);
+ forceTransform = !valueAsInt;
+ }
+ }
+ }
+
+
+ if (forceTransform) {
+ TransformProcessType(&psn, kProcessTransformToForegroundApplication);
+ }
+ }
+ }
+
+ char **argv = priv->argv;
+
+ // Get command line params
+ if (int argc = priv->argc) {
+ int i, j = 1;
+ QString passed_psn;
+ for(i=1; i < argc; i++) {
+ if (argv[i] && *argv[i] != '-') {
+ argv[j++] = argv[i];
+ continue;
+ }
+ QByteArray arg(argv[i]);
+#if defined(QT_DEBUG)
+ if (arg == "-nograb")
+ appNoGrab = !appNoGrab;
+ else
+#endif // QT_DEBUG
+ if (arg.left(5) == "-psn_") {
+ passed_psn = QString::fromLatin1(arg.mid(6));
+ } else {
+ argv[j++] = argv[i];
+ }
+ }
+ if (j < priv->argc) {
+ priv->argv[j] = 0;
+ priv->argc = j;
+ }
+
+ //special hack to change working directory (for an app bundle) when running from finder
+ if (!passed_psn.isNull() && QDir::currentPath() == QLatin1String("/")) {
+ QCFType<CFURLRef> bundleURL(CFBundleCopyBundleURL(CFBundleGetMainBundle()));
+ QString qbundlePath = QCFString(CFURLCopyFileSystemPath(bundleURL,
+ kCFURLPOSIXPathStyle));
+ if (qbundlePath.endsWith(QLatin1String(".app")))
+ QDir::setCurrent(qbundlePath.section(QLatin1Char('/'), 0, -2));
+ }
+ }
+
+ QMacPasteboardMime::initialize();
+
+ qApp->setObjectName(priv->appName());
+ if (qt_is_gui_used) {
+ QColormap::initialize();
+ QFont::initialize();
+ QCursorData::initialize();
+ QCoreGraphicsPaintEngine::initialize();
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::initialize();
+#endif
+ QMacInputContext::initialize();
+ QApplicationPrivate::inputContext = new QMacInputContext;
+
+ if (QApplication::desktopSettingsAware())
+ qt_mac_update_os_settings();
+#ifndef QT_MAC_USE_COCOA
+ if (!app_proc_handler) {
+ app_proc_handlerUPP = NewEventHandlerUPP(QApplicationPrivate::globalEventProcessor);
+ qt_init_app_proc_handler();
+ }
+
+#endif
+ if (!app_proc_ae_handlerUPP && !QApplication::testAttribute(Qt::AA_MacPluginApplication)) {
+ app_proc_ae_handlerUPP = AEEventHandlerUPP(QApplicationPrivate::globalAppleEventProcessor);
+ for(uint i = 0; i < sizeof(app_apple_events) / sizeof(QMacAppleEventTypeSpec); ++i) {
+ // Install apple event handler, but avoid overwriting an already
+ // existing handler (it means a 3rd party application has installed one):
+ SRefCon refCon = 0;
+ AEEventHandlerUPP current_handler = NULL;
+ AEGetEventHandler(app_apple_events[i].mac_class, app_apple_events[i].mac_id, &current_handler, &refCon, false);
+ if (!current_handler)
+ AEInstallEventHandler(app_apple_events[i].mac_class, app_apple_events[i].mac_id,
+ app_proc_ae_handlerUPP, SRefCon(qApp), false);
+ }
+ }
+
+ if (QApplicationPrivate::app_style) {
+ QEvent ev(QEvent::Style);
+ qt_sendSpontaneousEvent(QApplicationPrivate::app_style, &ev);
+ }
+ }
+ if (QApplication::desktopSettingsAware())
+ QApplicationPrivate::qt_mac_apply_settings();
+
+ // Cocoa application delegate
+#ifdef QT_MAC_USE_COCOA
+ NSApplication *cocoaApp = [QNSApplication sharedApplication];
+ qt_redirectNSApplicationSendEvent();
+
+ QMacCocoaAutoReleasePool pool;
+ id oldDelegate = [cocoaApp delegate];
+ QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) *newDelegate = [QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) sharedDelegate];
+ Q_ASSERT(newDelegate);
+ [newDelegate setQtPrivate:priv];
+ // Only do things that make sense to do once, otherwise we crash.
+ if (oldDelegate != newDelegate && !QApplication::testAttribute(Qt::AA_MacPluginApplication)) {
+ [newDelegate setReflectionDelegate:oldDelegate];
+ [cocoaApp setDelegate:newDelegate];
+
+ QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *qtMenuLoader = [[QT_MANGLE_NAMESPACE(QCocoaMenuLoader) alloc] init];
+ if ([NSBundle loadNibNamed:@"qt_menu" owner:qtMenuLoader] == false) {
+ qFatal("Qt internal error: qt_menu.nib could not be loaded. The .nib file"
+ " should be placed in QtGui.framework/Versions/Current/Resources/ "
+ " or in the resources directory of your application bundle.");
+ }
+
+ [cocoaApp setMenu:[qtMenuLoader menu]];
+ [newDelegate setMenuLoader:qtMenuLoader];
+ [qtMenuLoader release];
+ }
+#endif
+ // Register for Carbon tablet proximity events on the event monitor target.
+ // This means that we should receive proximity events even when we aren't the active application.
+ if (!tablet_proximity_handler) {
+ tablet_proximity_UPP = NewEventHandlerUPP(QApplicationPrivate::tabletProximityCallback);
+ qt_init_tablet_proximity_handler();
+ }
+ priv->native_modal_dialog_active = false;
+
+ qt_mac_read_fontsmoothing_settings();
+}
+
+void qt_release_apple_event_handler()
+{
+ if(app_proc_ae_handlerUPP) {
+ for(uint i = 0; i < sizeof(app_apple_events) / sizeof(QMacAppleEventTypeSpec); ++i)
+ AERemoveEventHandler(app_apple_events[i].mac_class, app_apple_events[i].mac_id,
+ app_proc_ae_handlerUPP, true);
+ DisposeAEEventHandlerUPP(app_proc_ae_handlerUPP);
+ app_proc_ae_handlerUPP = 0;
+ }
+}
+
+/*****************************************************************************
+ qt_cleanup() - cleans up when the application is finished
+ *****************************************************************************/
+
+void qt_cleanup()
+{
+ CGDisplayRemoveReconfigurationCallback(qt_mac_display_change_callbk, 0);
+ CFNotificationCenterRef center = CFNotificationCenterGetDistributedCenter();
+ CFNotificationCenterRemoveObserver(center, qApp, kCMDeviceUnregisteredNotification, 0);
+ CFNotificationCenterRemoveObserver(center, qApp, kCMDefaultDeviceNotification, 0);
+ CFNotificationCenterRemoveObserver(center, qApp, kCMDeviceProfilesNotification, 0);
+ CFNotificationCenterRemoveObserver(center, qApp, kCMDefaultDeviceProfileNotification, 0);
+
+#ifndef QT_MAC_USE_COCOA
+ qt_release_app_proc_handler();
+ if (app_proc_handlerUPP) {
+ DisposeEventHandlerUPP(app_proc_handlerUPP);
+ app_proc_handlerUPP = 0;
+ }
+#endif
+ qt_release_apple_event_handler();
+ qt_release_tablet_proximity_handler();
+ if (tablet_proximity_UPP)
+ DisposeEventHandlerUPP(tablet_proximity_UPP);
+
+ QPixmapCache::clear();
+ if (qt_is_gui_used) {
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::cleanup();
+#endif
+ QMacInputContext::cleanup();
+ QCursorData::cleanup();
+ QFont::cleanup();
+ QColormap::cleanup();
+ if (qt_mac_safe_pdev) {
+ delete qt_mac_safe_pdev;
+ qt_mac_safe_pdev = 0;
+ }
+ extern void qt_mac_unregister_widget(); // qapplication_mac.cpp
+ qt_mac_unregister_widget();
+ }
+}
+
+/*****************************************************************************
+ Platform specific global and internal functions
+ *****************************************************************************/
+void qt_updated_rootinfo()
+{
+}
+
+bool qt_wstate_iconified(WId)
+{
+ return false;
+}
+
+/*****************************************************************************
+ Platform specific QApplication members
+ *****************************************************************************/
+extern QWidget * mac_mouse_grabber;
+extern QWidget * mac_keyboard_grabber;
+
+#ifdef QT3_SUPPORT
+void QApplication::setMainWidget(QWidget *mainWidget)
+{
+ QApplicationPrivate::main_widget = mainWidget;
+ if (QApplicationPrivate::main_widget && windowIcon().isNull()
+ && QApplicationPrivate::main_widget->testAttribute(Qt::WA_SetWindowIcon))
+ setWindowIcon(QApplicationPrivate::main_widget->windowIcon());
+}
+#endif
+#ifndef QT_NO_CURSOR
+
+/*****************************************************************************
+ QApplication cursor stack
+ *****************************************************************************/
+
+void QApplication::setOverrideCursor(const QCursor &cursor)
+{
+ qApp->d_func()->cursor_list.prepend(cursor);
+
+#ifdef QT_MAC_USE_COCOA
+ qt_mac_update_cursor();
+#else
+ if (qApp && qApp->activeWindow())
+ qt_mac_set_cursor(&qApp->d_func()->cursor_list.first());
+#endif
+}
+
+void QApplication::restoreOverrideCursor()
+{
+ if (qApp->d_func()->cursor_list.isEmpty())
+ return;
+ qApp->d_func()->cursor_list.removeFirst();
+
+#ifdef QT_MAC_USE_COCOA
+ qt_mac_update_cursor();
+#else
+ if (qApp && qApp->activeWindow()) {
+ const QCursor def(Qt::ArrowCursor);
+ qt_mac_set_cursor(qApp->d_func()->cursor_list.isEmpty() ? &def : &qApp->d_func()->cursor_list.first());
+ }
+#endif
+}
+#endif // QT_NO_CURSOR
+
+QWidget *QApplication::topLevelAt(const QPoint &p)
+{
+#ifndef QT_MAC_USE_COCOA
+ QWidget *widget;
+ qt_mac_window_at(p.x(), p.y(), &widget);
+ return widget;
+#else
+ // Use a cache to avoid iterate through the whole list of windows for all
+ // calls to to topLevelAt. We e.g. do this for each and every mouse
+ // move since we need to find the widget under mouse:
+ if (topLevelAt_cache && topLevelAt_cache->frameGeometry().contains(p))
+ return topLevelAt_cache;
+
+ // INVARIANT: Cache miss. Go through the list if windows instead:
+ QMacCocoaAutoReleasePool pool;
+ NSPoint cocoaPoint = flipPoint(p);
+ NSInteger windowCount;
+ NSCountWindows(&windowCount);
+ if (windowCount <= 0)
+ return 0; // There's no window to find!
+
+ QVarLengthArray<NSInteger> windowList(windowCount);
+ NSWindowList(windowCount, windowList.data());
+ int firstQtWindowFound = -1;
+ for (int i = 0; i < windowCount; ++i) {
+ NSWindow *window = [NSApp windowWithWindowNumber:windowList[i]];
+ if (window) {
+ QWidget *candidateWindow = [window QT_MANGLE_NAMESPACE(qt_qwidget)];
+ if (candidateWindow && firstQtWindowFound == -1)
+ firstQtWindowFound = i;
+
+ if (NSPointInRect(cocoaPoint, [window frame])) {
+ // Check to see if there's a hole in the window where the mask is.
+ // If there is, we should just continue to see if there is a window below.
+ if (candidateWindow && !candidateWindow->mask().isEmpty()) {
+ QPoint localPoint = candidateWindow->mapFromGlobal(p);
+ if (!candidateWindow->mask().contains(localPoint))
+ continue;
+ else
+ return candidateWindow;
+ } else {
+ if (i == firstQtWindowFound) {
+ // The cache will only work when the window under mouse is
+ // top most (that is, not partially obscured by other windows.
+ // And we only set it if no mask is present to optimize for the common case:
+ topLevelAt_cache = candidateWindow;
+ }
+ return candidateWindow;
+ }
+ }
+ }
+ }
+
+ topLevelAt_cache = 0;
+ return 0;
+#endif
+}
+
+/*****************************************************************************
+ Main event loop
+ *****************************************************************************/
+
+bool QApplicationPrivate::modalState()
+{
+ return app_do_modal;
+}
+
+#ifdef QT_MAC_USE_COCOA
+#endif
+
+void QApplicationPrivate::enterModal_sys(QWidget *widget)
+{
+#ifdef DEBUG_MODAL_EVENTS
+ Q_ASSERT(widget);
+ qDebug("Entering modal state with %s::%s::%p (%d)", widget->metaObject()->className(), widget->objectName().toLocal8Bit().constData(),
+ widget, qt_modal_stack ? (int)qt_modal_stack->count() : -1);
+#endif
+ if (!qt_modal_stack)
+ qt_modal_stack = new QWidgetList;
+
+ dispatchEnterLeave(0, qt_last_mouse_receiver);
+ qt_last_mouse_receiver = 0;
+
+ qt_modal_stack->insert(0, widget);
+ if (!app_do_modal)
+ qt_event_request_menubarupdate();
+ app_do_modal = true;
+ qt_button_down = 0;
+
+#ifdef QT_MAC_USE_COCOA
+ if (!qt_mac_is_macsheet(widget))
+ QEventDispatcherMacPrivate::beginModalSession(widget);
+#endif
+}
+
+void QApplicationPrivate::leaveModal_sys(QWidget *widget)
+{
+ if (qt_modal_stack && qt_modal_stack->removeAll(widget)) {
+#ifdef DEBUG_MODAL_EVENTS
+ qDebug("Leaving modal state with %s::%s::%p (%d)", widget->metaObject()->className(), widget->objectName().toLocal8Bit().constData(),
+ widget, qt_modal_stack->count());
+#endif
+ if (qt_modal_stack->isEmpty()) {
+ delete qt_modal_stack;
+ qt_modal_stack = 0;
+ QPoint p(QCursor::pos());
+ app_do_modal = false;
+ QWidget* w = 0;
+ if (QWidget *grabber = QWidget::mouseGrabber())
+ w = grabber;
+ else
+ w = QApplication::widgetAt(p.x(), p.y());
+ dispatchEnterLeave(w, qt_last_mouse_receiver); // send synthetic enter event
+ qt_last_mouse_receiver = w;
+ }
+#ifdef QT_MAC_USE_COCOA
+ if (!qt_mac_is_macsheet(widget))
+ QEventDispatcherMacPrivate::endModalSession(widget);
+#endif
+ }
+#ifdef DEBUG_MODAL_EVENTS
+ else qDebug("Failure to remove %s::%s::%p -- %p", widget->metaObject()->className(), widget->objectName().toLocal8Bit().constData(), widget, qt_modal_stack);
+#endif
+ app_do_modal = (qt_modal_stack != 0);
+ if (!app_do_modal)
+ qt_event_request_menubarupdate();
+}
+
+QWidget *QApplicationPrivate::tryModalHelper_sys(QWidget *top)
+{
+#ifndef QT_MAC_USE_COCOA
+ if(top && qt_mac_is_macsheet(top) && !IsWindowVisible(qt_mac_window_for(top))) {
+ if(OSWindowRef wp = GetFrontWindowOfClass(kSheetWindowClass, true)) {
+ if(QWidget *sheet = qt_mac_find_window(wp))
+ top = sheet;
+ }
+ }
+#endif
+ return top;
+}
+
+#ifndef QT_MAC_USE_COCOA
+static bool qt_try_modal(QWidget *widget, EventRef event)
+{
+ QWidget * top = 0;
+
+ if (QApplicationPrivate::tryModalHelper(widget, &top))
+ return true;
+
+ // INVARIANT: widget is modally shaddowed within its
+ // window, and should therefore not handle the event.
+ // However, if the window is not active, the event
+ // might suggest that we should bring it to front:
+
+ bool block_event = false;
+
+ if (event) {
+ switch (GetEventClass(event)) {
+ case kEventClassMouse:
+ case kEventClassKeyboard:
+ block_event = true;
+ break;
+ }
+ }
+
+ QWidget *activeWidget = QApplication::activeWindow();
+ if ((!activeWidget || QApplicationPrivate::isBlockedByModal(activeWidget)) &&
+ top->isWindow() && block_event && !QApplicationPrivate::native_modal_dialog_active)
+ top->raise();
+
+#ifdef DEBUG_MODAL_EVENTS
+ qDebug("%s:%d -- final decision! (%s)", __FILE__, __LINE__, block_event ? "false" : "true");
+#endif
+ return !block_event;
+}
+#endif
+
+OSStatus QApplicationPrivate::tabletProximityCallback(EventHandlerCallRef, EventRef carbonEvent,
+ void *)
+{
+ OSType eventClass = GetEventClass(carbonEvent);
+ UInt32 eventKind = GetEventKind(carbonEvent);
+ if (eventClass != kEventClassTablet || eventKind != kEventTabletProximity)
+ return eventNotHandledErr;
+
+ // Get the current point of the device and its unique ID.
+ ::TabletProximityRec proxRec;
+ GetEventParameter(carbonEvent, kEventParamTabletProximityRec, typeTabletProximityRec, 0,
+ sizeof(proxRec), 0, &proxRec);
+ qt_dispatchTabletProximityEvent(proxRec);
+ return noErr;
+}
+
+OSStatus
+QApplicationPrivate::globalEventProcessor(EventHandlerCallRef er, EventRef event, void *data)
+{
+#ifndef QT_MAC_USE_COCOA
+ QApplication *app = (QApplication *)data;
+ QScopedLoopLevelCounter loopLevelCounter(app->d_func()->threadData);
+ long result;
+ if (app->filterEvent(&event, &result))
+ return result;
+ if(app->macEventFilter(er, event)) //someone else ate it
+ return noErr;
+ QPointer<QWidget> widget;
+
+ /*We assume all events are handled and in
+ the code below we set it to false when we know we didn't handle it, this
+ will let rogue events through (shouldn't really happen, but better safe
+ than sorry) */
+ bool handled_event=true;
+ UInt32 ekind = GetEventKind(event), eclass = GetEventClass(event);
+ switch(eclass)
+ {
+ case kEventClassQt:
+ if(ekind == kEventQtRequestShowSheet) {
+ request_showsheet_pending = 0;
+ QWidget *widget = 0;
+ GetEventParameter(event, kEventParamQWidget, typeQWidget, 0,
+ sizeof(widget), 0, &widget);
+ if(widget) {
+ if (widget->macEvent(er, event))
+ return noErr;
+ WindowPtr window = qt_mac_window_for(widget);
+ bool just_show = !qt_mac_is_macsheet(widget);
+ if(!just_show) {
+ OSStatus err = ShowSheetWindow(window, qt_mac_window_for(widget->parentWidget()));
+ if(err != noErr)
+ qWarning("Qt: QWidget: Unable to show as sheet %s::%s [%ld]", widget->metaObject()->className(),
+ widget->objectName().toLocal8Bit().constData(), long(err));
+ just_show = true;
+ }
+ if(just_show) //at least the window will be visible, but the sheet flag doesn't work sadly (probalby too many sheets)
+ ShowHide(window, true);
+ }
+ } else if(ekind == kEventQtRequestWindowChange) {
+ qt_mac_event_release(request_window_change_pending);
+ } else if(ekind == kEventQtRequestMenubarUpdate) {
+ qt_mac_event_release(request_menubarupdate_pending);
+ QMenuBar::macUpdateMenuBar();
+ } else if(ekind == kEventQtRequestActivate) {
+ qt_mac_event_release(request_activate_pending.event);
+ if(request_activate_pending.widget) {
+ QWidget *tlw = request_activate_pending.widget->window();
+ if (tlw->macEvent(er, event))
+ return noErr;
+ request_activate_pending.widget = 0;
+ tlw->activateWindow();
+ SelectWindow(qt_mac_window_for(tlw));
+ }
+ } else if(ekind == kEventQtRequestContext) {
+ bool send = false;
+ if ((send = (event == request_context_pending)))
+ qt_mac_event_release(request_context_pending);
+ if(send) {
+ //figure out which widget to send it to
+ QPoint where = QCursor::pos();
+ QWidget *widget = 0;
+ GetEventParameter(event, kEventParamQWidget, typeQWidget, 0,
+ sizeof(widget), 0, &widget);
+ if(!widget) {
+ if(qt_button_down)
+ widget = qt_button_down;
+ else
+ widget = QApplication::widgetAt(where.x(), where.y());
+ }
+ if(widget && !isBlockedByModal(widget)) {
+ if (widget->macEvent(er, event))
+ return noErr;
+ QPoint plocal(widget->mapFromGlobal(where));
+ const Qt::KeyboardModifiers keyboardModifiers = qt_mac_get_modifiers(GetCurrentEventKeyModifiers());
+ QContextMenuEvent qme(QContextMenuEvent::Mouse, plocal, where, keyboardModifiers);
+ QApplication::sendEvent(widget, &qme);
+ if(qme.isAccepted()) { //once this happens the events before are pitched
+ qt_button_down = 0;
+ qt_mac_dblclick.last_widget = 0;
+ }
+ } else {
+ handled_event = false;
+ }
+ }
+ } else {
+ handled_event = false;
+ }
+ break;
+ case kEventClassTablet:
+ switch (ekind) {
+ case kEventTabletProximity:
+ // Get the current point of the device and its unique ID.
+ ::TabletProximityRec proxRec;
+ GetEventParameter(event, kEventParamTabletProximityRec, typeTabletProximityRec, 0,
+ sizeof(proxRec), 0, &proxRec);
+ qt_dispatchTabletProximityEvent(proxRec);
+ }
+ break;
+ case kEventClassMouse:
+ {
+ static const int kEventParamQAppSeenMouseEvent = 'QASM';
+ // Check if we've seen the event, if we have we shouldn't process
+ // it again as it may lead to spurious "double events"
+ bool seenEvent;
+ if (GetEventParameter(event, kEventParamQAppSeenMouseEvent,
+ typeBoolean, 0, sizeof(bool), 0, &seenEvent) == noErr) {
+ if (seenEvent)
+ return eventNotHandledErr;
+ }
+ seenEvent = true;
+ SetEventParameter(event, kEventParamQAppSeenMouseEvent, typeBoolean,
+ sizeof(bool), &seenEvent);
+
+ Point where;
+ bool inNonClientArea = false;
+ GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, 0,
+ sizeof(where), 0, &where);
+#if defined(DEBUG_MOUSE_MAPS)
+ const char *edesc = 0;
+ switch(ekind) {
+ case kEventMouseDown: edesc = "MouseButtonPress"; break;
+ case kEventMouseUp: edesc = "MouseButtonRelease"; break;
+ case kEventMouseDragged: case kEventMouseMoved: edesc = "MouseMove"; break;
+ case kEventMouseScroll: edesc = "MouseWheelScroll"; break;
+ case kEventMouseWheelMoved: edesc = "MouseWheelMove"; break;
+ }
+ if(ekind == kEventMouseDown || ekind == kEventMouseUp)
+ qDebug("Handling mouse: %s", edesc);
+#endif
+ QEvent::Type etype = QEvent::None;
+ Qt::KeyboardModifiers modifiers;
+ {
+ UInt32 mac_modifiers = 0;
+ GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, 0,
+ sizeof(mac_modifiers), 0, &mac_modifiers);
+ modifiers = qt_mac_get_modifiers(mac_modifiers);
+ }
+ Qt::MouseButtons buttons;
+ {
+ UInt32 mac_buttons = 0;
+ GetEventParameter(event, kEventParamMouseChord, typeUInt32, 0,
+ sizeof(mac_buttons), 0, &mac_buttons);
+ if (ekind != kEventMouseWheelMoved)
+ buttons = qt_mac_get_buttons(mac_buttons);
+ else
+ buttons = QApplication::mouseButtons();
+ }
+
+ int wheel_deltaX = 0;
+ int wheel_deltaY = 0;
+ static EventRef compatibilityEvent = 0;
+
+ if (ekind == kEventMouseScroll) {
+ // kEventMouseScroll is the new way of dealing with mouse wheel
+ // events (kEventMouseWheelMoved was the old). kEventMouseScroll results
+ // in much smoother scrolling when using Mighty Mouse or TrackPad. For
+ // compatibility with older applications, carbon will also send us
+ // kEventMouseWheelMoved events if we dont eat this event
+ // (actually two events; one for horizontal and one for vertical).
+ // As a results of this, and to make sure we dont't receive duplicate events,
+ // we try to detect when this happend by checking the 'compatibilityEvent'.
+ // Since delta is delivered as pixels rather than degrees, we need to
+ // convert from pixels to degrees in a sensible manner.
+ // It looks like 1/4 degrees per pixel behaves most native.
+ // (NB: Qt expects the unit for delta to be 8 per degree):
+ const int pixelsToDegrees = 2;
+ SInt32 mdelt = 0;
+ GetEventParameter(event, kEventParamMouseWheelSmoothHorizontalDelta, typeSInt32, 0,
+ sizeof(mdelt), 0, &mdelt);
+ wheel_deltaX = mdelt * pixelsToDegrees;
+ mdelt = 0;
+ GetEventParameter(event, kEventParamMouseWheelSmoothVerticalDelta, typeSInt32, 0,
+ sizeof(mdelt), 0, &mdelt);
+ wheel_deltaY = mdelt * pixelsToDegrees;
+ GetEventParameter(event, kEventParamEventRef, typeEventRef, 0,
+ sizeof(compatibilityEvent), 0, &compatibilityEvent);
+ } else if (ekind == kEventMouseWheelMoved) {
+ if (event != compatibilityEvent) {
+ compatibilityEvent = 0;
+ int mdelt = 0;
+ GetEventParameter(event, kEventParamMouseWheelDelta, typeSInt32, 0,
+ sizeof(mdelt), 0, &mdelt);
+ EventMouseWheelAxis axis;
+ GetEventParameter(event, kEventParamMouseWheelAxis, typeMouseWheelAxis, 0,
+ sizeof(axis), 0, &axis);
+
+ // Remove acceleration, and use either -120 or 120 as delta:
+ if (axis == kEventMouseWheelAxisX)
+ wheel_deltaX = qBound(-120, int(mdelt * 10000), 120);
+ else
+ wheel_deltaY = qBound(-120, int(mdelt * 10000), 120);
+ }
+ }
+
+ Qt::MouseButton button = Qt::NoButton;
+ if(ekind == kEventMouseDown || ekind == kEventMouseUp) {
+ EventMouseButton mac_button = 0;
+ GetEventParameter(event, kEventParamMouseButton, typeMouseButton, 0,
+ sizeof(mac_button), 0, &mac_button);
+ button = qt_mac_get_button(mac_button);
+ }
+
+ switch(ekind) {
+ case kEventMouseDown:
+ etype = QEvent::MouseButtonPress;
+ break;
+ case kEventMouseUp:
+ etype = QEvent::MouseButtonRelease;
+ break;
+ case kEventMouseDragged:
+ case kEventMouseMoved:
+ etype = QEvent::MouseMove;
+ break;
+ }
+
+ const bool inPopupMode = app->d_func()->inPopupMode();
+
+ // A click outside a popup closes the popup. Make sure
+ // that no events are generated for the release part of that click.
+ // (The press goes to the popup and closes it.)
+ if (etype == QEvent::MouseButtonPress) {
+ qt_mac_previous_press_in_popup_mode = inPopupMode;
+ } else if (qt_mac_previous_press_in_popup_mode && !inPopupMode && etype == QEvent::MouseButtonRelease) {
+ qt_mac_previous_press_in_popup_mode = false;
+ handled_event = true;
+#if defined(DEBUG_MOUSE_MAPS)
+ qDebug("Bail out early due to qt_mac_previous_press_in_popup_mode");
+#endif
+ break; // break from case kEventClassMouse
+ }
+
+ //figure out which widget to send it to
+ if(inPopupMode) {
+ QWidget *popup = qApp->activePopupWidget();
+ if (qt_button_down && qt_button_down->window() == popup) {
+ widget = qt_button_down;
+ } else {
+ QPoint pos = popup->mapFromGlobal(QPoint(where.h, where.v));
+ widget = popup->childAt(pos);
+ }
+ if(!widget)
+ widget = popup;
+ } else {
+ if(mac_mouse_grabber) {
+ widget = mac_mouse_grabber;
+ } else if (qt_button_down) {
+ widget = qt_button_down;
+ } else {
+ {
+ WindowPtr window = 0;
+ if(GetEventParameter(event, kEventParamWindowRef, typeWindowRef, 0,
+ sizeof(window), 0, &window) != noErr)
+ FindWindowOfClass(&where, kAllWindowClasses, &window, 0);
+ if(window) {
+ HIViewRef hiview;
+ if(HIViewGetViewForMouseEvent(HIViewGetRoot(window), event, &hiview) == noErr) {
+ widget = QWidget::find((WId)hiview);
+ if (widget) {
+ // Make sure we didn't pass over a widget with a "fake hole" in it.
+ QWidget *otherWidget = QApplication::widgetAt(where.h, where.v);
+ if (otherWidget && otherWidget->testAttribute(Qt::WA_MouseNoMask))
+ widget = otherWidget;
+ }
+ }
+ }
+ }
+ if(!widget) //fallback
+ widget = QApplication::widgetAt(where.h, where.v);
+ if(ekind == kEventMouseUp && widget) {
+ short part = qt_mac_window_at(where.h, where.v);
+ if(part == inDrag) {
+ UInt32 count = 0;
+ GetEventParameter(event, kEventParamClickCount, typeUInt32, NULL,
+ sizeof(count), NULL, &count);
+ if(count == 2 && qt_mac_collapse_on_dblclick) {
+ if (widget->macEvent(er, event))
+ return noErr;
+ widget->setWindowState(widget->windowState() | Qt::WindowMinimized);
+ //we send a hide to be like X11/Windows
+ QEvent e(QEvent::Hide);
+ QApplication::sendSpontaneousEvent(widget, &e);
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (widget && widget->macEvent(er, event))
+ return noErr;
+ WindowPartCode wpc = qt_mac_window_at(where.h, where.v, 0);
+ if (wpc == inProxyIcon && modifiers == Qt::ControlModifier && buttons != Qt::NoButton) {
+ QIconDragEvent e;
+ QApplication::sendSpontaneousEvent(widget, &e);
+ if (e.isAccepted()) {
+ return noErr; // IconDrag ate it.
+ }
+ }
+ if (inPopupMode == false
+ && (qt_button_down == 0 || qt_button_down_in_content == false)
+ && (wpc != inContent && wpc != inStructure)) {
+ inNonClientArea = true;
+ switch (etype) {
+ case QEvent::MouseButtonPress: {
+ UInt32 count = 0;
+ GetEventParameter(event, kEventParamClickCount, typeUInt32, 0,
+ sizeof(count), 0, &count);
+ if(count % 2 || count == 0) {
+ etype = QEvent::NonClientAreaMouseButtonPress;
+ } else {
+ etype = QEvent::NonClientAreaMouseButtonDblClick;
+ }} break;
+ case QEvent::MouseButtonRelease:
+ etype = QEvent::NonClientAreaMouseButtonRelease;
+ break;
+ case QEvent::MouseMove:
+ if (widget == 0 || widget->hasMouseTracking())
+ etype = QEvent::NonClientAreaMouseMove;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if(qt_mac_find_window((FrontWindow()))) { //set the cursor up
+ QCursor cursor(Qt::ArrowCursor);
+ QWidget *cursor_widget = widget;
+ if(cursor_widget && cursor_widget == qt_button_down && ekind == kEventMouseUp)
+ cursor_widget = QApplication::widgetAt(where.h, where.v);
+ if(cursor_widget) { //only over the app, do we set a cursor..
+ if(!qApp->d_func()->cursor_list.isEmpty()) {
+ cursor = qApp->d_func()->cursor_list.first();
+ } else {
+ for(; cursor_widget; cursor_widget = cursor_widget->parentWidget()) {
+ QWExtra *extra = cursor_widget->d_func()->extraData();
+ if(extra && extra->curs && cursor_widget->isEnabled()) {
+ cursor = *extra->curs;
+ break;
+ }
+ }
+ }
+ }
+ qt_mac_set_cursor(&cursor);
+ }
+
+ //This mouse button state stuff looks like this on purpose
+ //although it looks hacky it is VERY intentional..
+ if(widget && app_do_modal && !qt_try_modal(widget, event)) {
+ if(ekind == kEventMouseDown && qt_mac_is_macsheet(QApplication::activeModalWidget()))
+ QApplication::activeModalWidget()->parentWidget()->activateWindow(); //sheets have a parent
+ handled_event = false;
+#if defined(DEBUG_MOUSE_MAPS)
+ qDebug("Bail out early due to qt_try_modal");
+#endif
+ break;
+ }
+
+ UInt32 tabletEventType = 0;
+ GetEventParameter(event, kEventParamTabletEventType, typeUInt32, 0,
+ sizeof(tabletEventType), 0, &tabletEventType);
+ if (tabletEventType == kEventTabletPoint) {
+ TabletPointRec tabletPointRec;
+ GetEventParameter(event, kEventParamTabletPointRec, typeTabletPointRec, 0,
+ sizeof(tabletPointRec), 0, &tabletPointRec);
+ QEvent::Type t = QEvent::TabletMove; //default
+ int new_tablet_button_state = tabletPointRec.buttons ? 1 : 0;
+ if (new_tablet_button_state != tablet_button_state)
+ if (new_tablet_button_state)
+ t = QEvent::TabletPress;
+ else
+ t = QEvent::TabletRelease;
+ tablet_button_state = new_tablet_button_state;
+
+ QMacTabletHash *tabletHash = qt_mac_tablet_hash();
+ if (!tabletHash->contains(tabletPointRec.deviceID) && t != QEvent::TabletRelease) {
+ // Never discard TabletRelease events as they may be delivered *after* TabletLeaveProximity events
+ qWarning("handleTabletEvent: This tablet device is unknown"
+ " (received no proximity event for it). Discarding event.");
+ return false;
+ }
+ QTabletDeviceData &deviceData = tabletHash->operator[](tabletPointRec.deviceID);
+ if (t == QEvent::TabletPress) {
+ deviceData.widgetToGetPress = widget;
+ } else if (t == QEvent::TabletRelease && deviceData.widgetToGetPress) {
+ widget = deviceData.widgetToGetPress;
+ deviceData.widgetToGetPress = 0;
+ }
+
+ if (widget) {
+ int tiltX = ((int)tabletPointRec.tiltX)/(32767/64); // 32K -> 60
+ int tiltY = ((int)tabletPointRec.tiltY)/(-32767/64); // 32K -> 60
+ HIPoint hiPoint;
+ GetEventParameter(event, kEventParamMouseLocation, typeHIPoint, 0, sizeof(HIPoint), 0, &hiPoint);
+ QPointF hiRes(hiPoint.x, hiPoint.y);
+ QPoint global(where.h, where.v);
+
+
+
+ QPoint local(widget->mapFromGlobal(global));
+ int z = 0;
+ qreal rotation = 0.0;
+ qreal tp = 0.0;
+ // Again from the Wacom.h header
+
+ if (deviceData.capabilityMask & 0x0200) // Z-axis
+ z = tabletPointRec.absZ;
+
+ if (deviceData.capabilityMask & 0x0800) // Tangential pressure
+ tp = tabletPointRec.tangentialPressure / 32767.0;
+
+ if (deviceData.capabilityMask & 0x2000) // Rotation
+ rotation = qreal(tabletPointRec.rotation) / 64.0;
+
+ QTabletEvent e(t, local, global, hiRes, deviceData.tabletDeviceType,
+ deviceData.tabletPointerType,
+ qreal(tabletPointRec.pressure / qreal(0xffff)), tiltX, tiltY,
+ tp, rotation, z, modifiers, deviceData.tabletUniqueID);
+ QApplication::sendSpontaneousEvent(widget, &e);
+ if (e.isAccepted()) {
+ if (t == QEvent::TabletPress) {
+ qt_button_down = widget;
+ } else if (t == QEvent::TabletRelease) {
+ qt_button_down = 0;
+ }
+#if defined(DEBUG_MOUSE_MAPS)
+ qDebug("Bail out early due to tablet acceptance");
+#endif
+ break;
+ }
+ }
+ }
+
+ if(ekind == kEventMouseDown) {
+ qt_mac_no_click_through_mode = false;
+ const short windowPart = qt_mac_window_at(where.h, where.v, 0);
+ // Menubar almost always wins.
+ if (!inPopupMode && windowPart == inMenuBar) {
+ MenuSelect(where); //allow menu tracking
+ return noErr;
+ }
+
+ if (widget && !(GetCurrentKeyModifiers() & cmdKey)) {
+ extern bool qt_isGenuineQWidget(const QWidget *); // qwidget_mac.cpp
+ QWidget *window = widget->window();
+ bool genuineQtWidget = qt_isGenuineQWidget(widget); // the widget, not the window.
+ window->raise();
+
+ bool needActivate = (window->windowType() != Qt::Desktop)
+ && (window->windowType() != Qt::Popup)
+ && !qt_mac_is_macsheet(window);
+ if (needActivate && (!window->isModal() && qobject_cast<QDockWidget *>(window)))
+ needActivate = false;
+
+ if (genuineQtWidget && needActivate)
+ needActivate = !window->isActiveWindow()
+ || !IsWindowActive(qt_mac_window_for(window));
+
+ if (needActivate) {
+ window->activateWindow();
+ if (!qt_mac_can_clickThrough(widget)) {
+ qt_mac_no_click_through_mode = true;
+ handled_event = false;
+#if defined(DEBUG_MOUSE_MAPS)
+ qDebug("Bail out early due to qt_mac_canClickThrough %s::%s", widget->metaObject()->className(),
+ widget->objectName().toLocal8Bit().constData());
+#endif
+ break;
+ }
+ }
+ }
+
+ if(qt_mac_dblclick.last_widget &&
+ qt_mac_dblclick.last_x != -1 && qt_mac_dblclick.last_y != -1 &&
+ QRect(qt_mac_dblclick.last_x-2, qt_mac_dblclick.last_y-2, 4, 4).contains(QPoint(where.h, where.v))) {
+ if(qt_mac_dblclick.use_qt_time_limit) {
+ EventTime now = GetEventTime(event);
+ if(qt_mac_dblclick.last_time != -2 && qt_mac_dblclick.last_widget == widget &&
+ now - qt_mac_dblclick.last_time <= ((double)QApplicationPrivate::mouse_double_click_time)/1000 &&
+ qt_mac_dblclick.last_button == button)
+ etype = QEvent::MouseButtonDblClick;
+ } else {
+ UInt32 count = 0;
+ GetEventParameter(event, kEventParamClickCount, typeUInt32, 0,
+ sizeof(count), 0, &count);
+ if(!(count % 2) && qt_mac_dblclick.last_modifiers == modifiers &&
+ qt_mac_dblclick.last_widget == widget && qt_mac_dblclick.last_button == button)
+ etype = QEvent::MouseButtonDblClick;
+ }
+ if(etype == QEvent::MouseButtonDblClick)
+ qt_mac_dblclick.last_widget = 0;
+ }
+ if(etype != QEvent::MouseButtonDblClick) {
+ qt_mac_dblclick.last_x = where.h;
+ qt_mac_dblclick.last_y = where.v;
+ } else {
+ qt_mac_dblclick.last_x = qt_mac_dblclick.last_y = -1;
+ }
+ } else if(qt_mac_no_click_through_mode) {
+ if(ekind == kEventMouseUp)
+ qt_mac_no_click_through_mode = false;
+ handled_event = false;
+#if defined(DEBUG_MOUSE_MAPS)
+ qDebug("Bail out early due to qt_mac_no_click_through_mode");
+#endif
+ break;
+ }
+
+ QPointer<QWidget> leaveAfterRelease = 0;
+ switch(ekind) {
+ case kEventMouseUp:
+ if (!buttons) {
+ if (!inPopupMode && !QWidget::mouseGrabber())
+ leaveAfterRelease = qt_button_down;
+ qt_button_down = 0;
+ }
+ break;
+ case kEventMouseDown: {
+ if (!qt_button_down)
+ qt_button_down = widget;
+ WindowPartCode wpc = qt_mac_window_at(where.h, where.v, 0);
+ qt_button_down_in_content = (wpc == inContent || wpc == inStructure);
+ break; }
+ }
+
+ // Check if we should send enter/leave events:
+ switch(ekind) {
+ case kEventMouseDragged:
+ case kEventMouseMoved:
+ case kEventMouseUp:
+ case kEventMouseDown: {
+ // If we are in popup mode, widget will point to the current popup no matter
+ // where the mouse cursor is. In that case find out if the mouse cursor is
+ // really over the popup in order to send correct enter / leave envents.
+ QWidget * const enterLeaveWidget = (inPopupMode || ekind == kEventMouseUp) ?
+ QApplication::widgetAt(where.h, where.v) : static_cast<QWidget*>(widget);
+
+ if ((QWidget *) qt_last_mouse_receiver != enterLeaveWidget || inNonClientArea) {
+#ifdef DEBUG_MOUSE_MAPS
+ qDebug("Entering: %p - %s (%s), Leaving %s (%s)", (QWidget*)enterLeaveWidget,
+ enterLeaveWidget ? enterLeaveWidget->metaObject()->className() : "none",
+ enterLeaveWidget ? enterLeaveWidget->objectName().toLocal8Bit().constData() : "",
+ qt_last_mouse_receiver ? qt_last_mouse_receiver->metaObject()->className() : "none",
+ qt_last_mouse_receiver ? qt_last_mouse_receiver->objectName().toLocal8Bit().constData() : "");
+#endif
+
+ QWidget * const mouseGrabber = QWidget::mouseGrabber();
+
+ if (inPopupMode) {
+ QWidget *enter = enterLeaveWidget;
+ QWidget *leave = qt_last_mouse_receiver;
+ if (mouseGrabber) {
+ QWidget * const popupWidget = qApp->activePopupWidget();
+ if (leave == popupWidget)
+ enter = mouseGrabber;
+ if (enter == popupWidget)
+ leave = mouseGrabber;
+ if ((enter == mouseGrabber && leave == popupWidget)
+ || (leave == mouseGrabber && enter == popupWidget)) {
+ QApplicationPrivate::dispatchEnterLeave(enter, leave);
+ qt_last_mouse_receiver = enter;
+ }
+ } else {
+ QApplicationPrivate::dispatchEnterLeave(enter, leave);
+ qt_last_mouse_receiver = enter;
+ }
+ } else if ((!qt_button_down || !qt_last_mouse_receiver) && !mouseGrabber && !leaveAfterRelease) {
+ QApplicationPrivate::dispatchEnterLeave(enterLeaveWidget, qt_last_mouse_receiver);
+ qt_last_mouse_receiver = enterLeaveWidget;
+ }
+ }
+ break; }
+ }
+
+ if(widget) {
+ QPoint p(where.h, where.v);
+ QPoint plocal(widget->mapFromGlobal(p));
+ if(etype == QEvent::MouseButtonPress) {
+ qt_mac_dblclick.last_widget = widget;
+ qt_mac_dblclick.last_modifiers = modifiers;
+ qt_mac_dblclick.last_button = button;
+ qt_mac_dblclick.last_time = GetEventTime(event);
+ }
+
+ if (wheel_deltaX || wheel_deltaY) {
+#ifndef QT_NO_WHEELEVENT
+ if (wheel_deltaX) {
+ QWheelEvent qwe(plocal, p, wheel_deltaX, buttons, modifiers, Qt::Horizontal);
+ QApplication::sendSpontaneousEvent(widget, &qwe);
+ if (!qwe.isAccepted() && QApplicationPrivate::focus_widget && QApplicationPrivate::focus_widget != widget) {
+ QWheelEvent qwe2(QApplicationPrivate::focus_widget->mapFromGlobal(p), p,
+ wheel_deltaX, buttons, modifiers, Qt::Horizontal);
+ QApplication::sendSpontaneousEvent(QApplicationPrivate::focus_widget, &qwe2);
+ if (!qwe2.isAccepted())
+ handled_event = false;
+ }
+ }
+ if (wheel_deltaY) {
+ QWheelEvent qwe(plocal, p, wheel_deltaY, buttons, modifiers, Qt::Vertical);
+ QApplication::sendSpontaneousEvent(widget, &qwe);
+ if (!qwe.isAccepted() && QApplicationPrivate::focus_widget && QApplicationPrivate::focus_widget != widget) {
+ QWheelEvent qwe2(QApplicationPrivate::focus_widget->mapFromGlobal(p), p,
+ wheel_deltaY, buttons, modifiers, Qt::Vertical);
+ QApplication::sendSpontaneousEvent(QApplicationPrivate::focus_widget, &qwe2);
+ if (!qwe2.isAccepted())
+ handled_event = false;
+ }
+ }
+#endif // QT_NO_WHEELEVENT
+ } else {
+#ifdef QMAC_SPEAK_TO_ME
+ const int speak_keys = Qt::AltModifier | Qt::ShiftModifier;
+ if(etype == QMouseEvent::MouseButtonDblClick && ((modifiers & speak_keys) == speak_keys)) {
+ QVariant v = widget->property("displayText");
+ if(!v.isValid()) v = widget->property("text");
+ if(!v.isValid()) v = widget->property("windowTitle");
+ if(v.isValid()) {
+ QString s = v.toString();
+ s.replace(QRegExp(QString::fromLatin1("(\\&|\\<[^\\>]*\\>)")), QLatin1String(""));
+ SpeechChannel ch;
+ NewSpeechChannel(0, &ch);
+ SpeakText(ch, s.toLatin1().constData(), s.length());
+ DisposeSpeechChannel(ch);
+ }
+ }
+#endif
+ Qt::MouseButton buttonToSend = button;
+ static bool lastButtonTranslated = false;
+ if(ekind == kEventMouseDown &&
+ button == Qt::LeftButton && (modifiers & Qt::MetaModifier)) {
+ buttonToSend = Qt::RightButton;
+ lastButtonTranslated = true;
+ } else if(ekind == kEventMouseUp && lastButtonTranslated) {
+ buttonToSend = Qt::RightButton;
+ lastButtonTranslated = false;
+ }
+ QMouseEvent qme(etype, plocal, p, buttonToSend, buttons, modifiers);
+ QApplication::sendSpontaneousEvent(widget, &qme);
+ if(!qme.isAccepted() || inNonClientArea)
+ handled_event = false;
+ }
+
+ if (leaveAfterRelease) {
+ QWidget *enter = QApplication::widgetAt(where.h, where.v);
+ QApplicationPrivate::dispatchEnterLeave(enter, leaveAfterRelease);
+ qt_last_mouse_receiver = enter;
+ leaveAfterRelease = 0;
+ }
+
+ if(ekind == kEventMouseDown &&
+ ((button == Qt::RightButton) ||
+ (button == Qt::LeftButton && (modifiers & Qt::MetaModifier))))
+ qt_event_request_context();
+
+#ifdef DEBUG_MOUSE_MAPS
+ const char *event_desc = edesc;
+ if(etype == QEvent::MouseButtonDblClick)
+ event_desc = "Double Click";
+ else if(etype == QEvent::NonClientAreaMouseButtonPress)
+ event_desc = "NonClientMousePress";
+ else if(etype == QEvent::NonClientAreaMouseButtonRelease)
+ event_desc = "NonClientMouseRelease";
+ else if(etype == QEvent::NonClientAreaMouseMove)
+ event_desc = "NonClientMouseMove";
+ else if(etype == QEvent::NonClientAreaMouseButtonDblClick)
+ event_desc = "NonClientMouseDblClick";
+ qDebug("%d %d (%d %d) - Would send (%s) event to %p %s %s (%d 0x%08x 0x%08x %d)", p.x(), p.y(),
+ plocal.x(), plocal.y(), event_desc, (QWidget*)widget,
+ widget ? widget->objectName().toLocal8Bit().constData() : "*Unknown*",
+ widget ? widget->metaObject()->className() : "*Unknown*",
+ button, (int)buttons, (int)modifiers, wheel_deltaX);
+#endif
+ } else {
+ handled_event = false;
+ }
+ break;
+ }
+ case kEventClassTextInput:
+ case kEventClassKeyboard: {
+ EventRef key_event = event;
+ if(eclass == kEventClassTextInput) {
+ Q_ASSERT(ekind == kEventTextInputUnicodeForKeyEvent);
+ OSStatus err = GetEventParameter(event, kEventParamTextInputSendKeyboardEvent, typeEventRef, 0,
+ sizeof(key_event), 0, &key_event);
+ Q_ASSERT(err == noErr);
+ Q_UNUSED(err);
+ }
+ const UInt32 key_ekind = GetEventKind(key_event);
+ Q_ASSERT(GetEventClass(key_event) == kEventClassKeyboard);
+
+ if(key_ekind == kEventRawKeyDown)
+ qt_keymapper_private()->updateKeyMap(er, key_event, data);
+ if(mac_keyboard_grabber)
+ widget = mac_keyboard_grabber;
+ else if (app->activePopupWidget())
+ widget = (app->activePopupWidget()->focusWidget() ?
+ app->activePopupWidget()->focusWidget() : app->activePopupWidget());
+ else if(QApplication::focusWidget())
+ widget = QApplication::focusWidget();
+ else
+ widget = app->activeWindow();
+
+ if (widget) {
+ if (widget->macEvent(er, event))
+ return noErr;
+ } else {
+ // Darn, I need to update tho modifier state, even though
+ // Qt itself isn't getting them, otherwise the keyboard state get inconsistent.
+ if (key_ekind == kEventRawKeyModifiersChanged) {
+ UInt32 modifiers = 0;
+ GetEventParameter(key_event, kEventParamKeyModifiers, typeUInt32, 0,
+ sizeof(modifiers), 0, &modifiers);
+ extern void qt_mac_send_modifiers_changed(quint32 modifiers, QObject *object); // qkeymapper_mac.cpp
+ // Just send it to the qApp for the time being.
+ qt_mac_send_modifiers_changed(modifiers, qApp);
+ }
+ handled_event = false;
+ break;
+ }
+
+ if(app_do_modal && !qt_try_modal(widget, key_event))
+ break;
+ if (eclass == kEventClassTextInput) {
+ handled_event = false;
+ } else {
+ handled_event = qt_keymapper_private()->translateKeyEvent(widget, er, key_event, data,
+ widget == mac_keyboard_grabber);
+ }
+ break; }
+ case kEventClassWindow: {
+ WindowRef wid = 0;
+ GetEventParameter(event, kEventParamDirectObject, typeWindowRef, 0,
+ sizeof(WindowRef), 0, &wid);
+ widget = qt_mac_find_window(wid);
+ if (widget && widget->macEvent(er, event))
+ return noErr;
+ if(ekind == kEventWindowActivated) {
+ if(QApplicationPrivate::app_style) {
+ QEvent ev(QEvent::Style);
+ QApplication::sendSpontaneousEvent(QApplicationPrivate::app_style, &ev);
+ }
+
+ if(widget && app_do_modal && !qt_try_modal(widget, event))
+ break;
+
+ if(widget && widget->window()->isVisible()) {
+ QWidget *tlw = widget->window();
+ if(tlw->isWindow() && !(tlw->windowType() == Qt::Popup)
+ && !qt_mac_is_macdrawer(tlw)
+ && (!tlw->parentWidget() || tlw->isModal()
+ || !(tlw->windowType() == Qt::Tool))) {
+ bool just_send_event = false;
+ {
+ WindowActivationScope scope;
+ if(GetWindowActivationScope((WindowRef)wid, &scope) == noErr &&
+ scope == kWindowActivationScopeIndependent) {
+ if(GetFrontWindowOfClass(kAllWindowClasses, true) != wid)
+ just_send_event = true;
+ }
+ }
+ if(just_send_event) {
+ QEvent e(QEvent::WindowActivate);
+ QApplication::sendSpontaneousEvent(widget, &e);
+ } else {
+ app->setActiveWindow(tlw);
+ }
+ }
+ QMenuBar::macUpdateMenuBar();
+ }
+ } else if(ekind == kEventWindowDeactivated) {
+ if(widget && QApplicationPrivate::active_window == widget)
+ app->setActiveWindow(0);
+ } else {
+ handled_event = false;
+ }
+ break; }
+ case kEventClassApplication:
+ if(ekind == kEventAppActivated) {
+ if(QApplication::desktopSettingsAware())
+ qt_mac_update_os_settings();
+ if(qt_clipboard) { //manufacture an event so the clipboard can see if it has changed
+ QEvent ev(QEvent::Clipboard);
+ QApplication::sendSpontaneousEvent(qt_clipboard, &ev);
+ }
+ if(app) {
+ QEvent ev(QEvent::ApplicationActivate);
+ QApplication::sendSpontaneousEvent(app, &ev);
+ }
+ if(!app->activeWindow()) {
+ WindowPtr wp = ActiveNonFloatingWindow();
+ if(QWidget *tmp_w = qt_mac_find_window(wp))
+ app->setActiveWindow(tmp_w);
+ }
+ QMenuBar::macUpdateMenuBar();
+ } else if(ekind == kEventAppDeactivated) {
+ //qt_mac_no_click_through_mode = false;
+ while(app->d_func()->inPopupMode())
+ app->activePopupWidget()->close();
+ if(app) {
+ QEvent ev(QEvent::ApplicationDeactivate);
+ QApplication::sendSpontaneousEvent(app, &ev);
+ }
+ app->setActiveWindow(0);
+ } else if(ekind == kEventAppAvailableWindowBoundsChanged) {
+ QDesktopWidgetImplementation::instance()->onResize();
+ } else {
+ handled_event = false;
+ }
+ break;
+ case kAppearanceEventClass:
+ if(ekind == kAEAppearanceChanged) {
+ if(QApplication::desktopSettingsAware())
+ qt_mac_update_os_settings();
+ if(QApplicationPrivate::app_style) {
+ QEvent ev(QEvent::Style);
+ QApplication::sendSpontaneousEvent(QApplicationPrivate::app_style, &ev);
+ }
+ } else {
+ handled_event = false;
+ }
+ break;
+ case kEventClassAppleEvent:
+ if(ekind == kEventAppleEvent) {
+ EventRecord erec;
+ if(!ConvertEventRefToEventRecord(event, &erec))
+ qDebug("Qt: internal: WH0A, unexpected condition reached. %s:%d", __FILE__, __LINE__);
+ else if(AEProcessAppleEvent(&erec) != noErr)
+ handled_event = false;
+ } else {
+ handled_event = false;
+ }
+ break;
+ case kEventClassCommand:
+ if(ekind == kEventCommandProcess) {
+ HICommand cmd;
+ GetEventParameter(event, kEventParamDirectObject, typeHICommand,
+ 0, sizeof(cmd), 0, &cmd);
+ handled_event = false;
+ if(!cmd.menu.menuRef && GetApplicationDockTileMenu()) {
+ EventRef copy = CopyEvent(event);
+ HICommand copy_cmd;
+ GetEventParameter(event, kEventParamDirectObject, typeHICommand,
+ 0, sizeof(copy_cmd), 0, &copy_cmd);
+ copy_cmd.menu.menuRef = GetApplicationDockTileMenu();
+ SetEventParameter(copy, kEventParamDirectObject, typeHICommand, sizeof(copy_cmd), &copy_cmd);
+ if(SendEventToMenu(copy, copy_cmd.menu.menuRef) == noErr)
+ handled_event = true;
+ }
+ if(!handled_event) {
+ if(cmd.commandID == kHICommandQuit) {
+ // Quitting the application is not Qt's responsibility if
+ // used in a plugin or just embedded into a native application.
+ // In that case, let the event pass down to the native apps event handler.
+ if (!QApplication::testAttribute(Qt::AA_MacPluginApplication)) {
+ handled_event = true;
+ HiliteMenu(0);
+ bool handle_quit = true;
+ if(QApplicationPrivate::modalState()) {
+ int visible = 0;
+ const QWidgetList tlws = QApplication::topLevelWidgets();
+ for(int i = 0; i < tlws.size(); ++i) {
+ if(tlws.at(i)->isVisible())
+ ++visible;
+ }
+ handle_quit = (visible <= 1);
+ }
+ if(handle_quit) {
+ QCloseEvent ev;
+ QApplication::sendSpontaneousEvent(app, &ev);
+ if(ev.isAccepted())
+ app->quit();
+ } else {
+ QApplication::beep();
+ }
+ }
+ } else if(cmd.commandID == kHICommandSelectWindow) {
+ if((GetCurrentKeyModifiers() & cmdKey))
+ handled_event = true;
+ } else if(cmd.commandID == kHICommandAbout) {
+ QMessageBox::aboutQt(0);
+ HiliteMenu(0);
+ handled_event = true;
+ }
+ }
+ }
+ break;
+ }
+
+#ifdef DEBUG_EVENTS
+ qDebug("%shandled event %c%c%c%c %d", handled_event ? "(*) " : "",
+ char(eclass >> 24), char((eclass >> 16) & 255), char((eclass >> 8) & 255),
+ char(eclass & 255), (int)ekind);
+#endif
+ if(!handled_event) //let the event go through
+ return eventNotHandledErr;
+ return noErr; //we eat the event
+#else
+ Q_UNUSED(er);
+ Q_UNUSED(event);
+ Q_UNUSED(data);
+ return eventNotHandledErr;
+#endif
+}
+
+#ifdef QT_MAC_USE_COCOA
+void QApplicationPrivate::qt_initAfterNSAppStarted()
+{
+ setupAppleEvents();
+ qt_mac_update_cursor();
+}
+
+void QApplicationPrivate::setupAppleEvents()
+{
+ // This function is called from the event dispatcher when NSApplication has
+ // finished initialization, which appears to be just after [NSApplication run] has
+ // started to execute. By setting up our apple events handlers this late, we override
+ // the ones set up by NSApplication.
+
+ // If Qt is used as a plugin, we let the 3rd party application handle events
+ // like quit and open file events. Otherwise, if we install our own handlers, we
+ // easily end up breaking functionallity the 3rd party application depend on:
+ if (QApplication::testAttribute(Qt::AA_MacPluginApplication))
+ return;
+
+ QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) *newDelegate = [QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) sharedDelegate];
+ NSAppleEventManager *eventManager = [NSAppleEventManager sharedAppleEventManager];
+ [eventManager setEventHandler:newDelegate andSelector:@selector(appleEventQuit:withReplyEvent:)
+ forEventClass:kCoreEventClass andEventID:kAEQuitApplication];
+ [eventManager setEventHandler:newDelegate andSelector:@selector(getUrl:withReplyEvent:)
+ forEventClass:kInternetEventClass andEventID:kAEGetURL];
+}
+#endif
+
+// In Carbon this is your one stop for apple events.
+// In Cocoa, it ISN'T. This is the catch-all Apple Event handler that exists
+// for the time between instantiating the NSApplication, but before the
+// NSApplication has installed it's OWN Apple Event handler. When Cocoa has
+// that set up, we remove this. So, if you are debugging problems, you likely
+// want to check out QCocoaApplicationDelegate instead.
+OSStatus QApplicationPrivate::globalAppleEventProcessor(const AppleEvent *ae, AppleEvent *, long handlerRefcon)
+{
+ QApplication *app = (QApplication *)handlerRefcon;
+ bool handled_event=false;
+ OSType aeID=typeWildCard, aeClass=typeWildCard;
+ AEGetAttributePtr(ae, keyEventClassAttr, typeType, 0, &aeClass, sizeof(aeClass), 0);
+ AEGetAttributePtr(ae, keyEventIDAttr, typeType, 0, &aeID, sizeof(aeID), 0);
+ if(aeClass == kCoreEventClass) {
+ switch(aeID) {
+ case kAEQuitApplication: {
+ extern bool qt_mac_quit_menu_item_enabled; // qmenu_mac.cpp
+ if (qt_mac_quit_menu_item_enabled) {
+ QCloseEvent ev;
+ QApplication::sendSpontaneousEvent(app, &ev);
+ if(ev.isAccepted()) {
+ handled_event = true;
+ app->quit();
+ }
+ } else {
+ QApplication::beep(); // Sorry, you can't quit right now.
+ }
+ break; }
+ case kAEOpenDocuments: {
+ AEDescList docs;
+ if(AEGetParamDesc(ae, keyDirectObject, typeAEList, &docs) == noErr) {
+ long cnt = 0;
+ AECountItems(&docs, &cnt);
+ UInt8 *str_buffer = NULL;
+ for(int i = 0; i < cnt; i++) {
+ FSRef ref;
+ if(AEGetNthPtr(&docs, i+1, typeFSRef, 0, 0, &ref, sizeof(ref), 0) != noErr)
+ continue;
+ if(!str_buffer)
+ str_buffer = (UInt8 *)malloc(1024);
+ FSRefMakePath(&ref, str_buffer, 1024);
+ QFileOpenEvent ev(QString::fromUtf8((const char *)str_buffer));
+ QApplication::sendSpontaneousEvent(app, &ev);
+ }
+ if(str_buffer)
+ free(str_buffer);
+ }
+ break; }
+ default:
+ break;
+ }
+ } else if (aeClass == kInternetEventClass) {
+ switch (aeID) {
+ case kAEGetURL: {
+ char urlData[1024];
+ Size actualSize;
+ if (AEGetParamPtr(ae, keyDirectObject, typeChar, 0, urlData,
+ sizeof(urlData) - 1, &actualSize) == noErr) {
+ urlData[actualSize] = 0;
+ QFileOpenEvent ev(QUrl(QString::fromUtf8(urlData)));
+ QApplication::sendSpontaneousEvent(app, &ev);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+#ifdef DEBUG_EVENTS
+ qDebug("Qt: internal: %shandled Apple event! %c%c%c%c %c%c%c%c", handled_event ? "(*)" : "",
+ char(aeID >> 24), char((aeID >> 16) & 255), char((aeID >> 8) & 255),char(aeID & 255),
+ char(aeClass >> 24), char((aeClass >> 16) & 255), char((aeClass >> 8) & 255),char(aeClass & 255));
+#else
+ if(!handled_event) //let the event go through
+ return eventNotHandledErr;
+ return noErr; //we eat the event
+#endif
+}
+
+/*!
+ \fn bool QApplication::macEventFilter(EventHandlerCallRef caller, EventRef event)
+
+ \warning This virtual function is only used under Mac OS X, and behaves different
+ depending on if Qt is based on Carbon or Cocoa.
+
+ For the Carbon port, If you create an application that inherits QApplication and reimplement
+ this function, you get direct access to all Carbon Events that Qt registers
+ for from Mac OS X with this function being called with the \a caller and
+ the \a event.
+
+ For the Cocoa port, If you create an application that inherits QApplication and reimplement
+ this function, you get direct access to all Cocoa Events that Qt receives
+ from Mac OS X with this function being called with the \a caller being 0 and
+ the \a event being an NSEvent pointer:
+
+ NSEvent *e = reinterpret_cast<NSEvent *>(event);
+
+ Return true if you want to stop the event from being processed.
+ Return false for normal event dispatching. The default
+ implementation returns false.
+*/
+bool QApplication::macEventFilter(EventHandlerCallRef, EventRef)
+{
+ return false;
+}
+
+/*!
+ \internal
+*/
+void QApplicationPrivate::openPopup(QWidget *popup)
+{
+ if (!QApplicationPrivate::popupWidgets) // create list
+ QApplicationPrivate::popupWidgets = new QWidgetList;
+ QApplicationPrivate::popupWidgets->append(popup); // add to end of list
+
+ // popups are not focus-handled by the window system (the first
+ // popup grabbed the keyboard), so we have to do that manually: A
+ // new popup gets the focus
+ if (popup->focusWidget()) {
+ popup->focusWidget()->setFocus(Qt::PopupFocusReason);
+ } else if (QApplicationPrivate::popupWidgets->count() == 1) { // this was the first popup
+ popup->setFocus(Qt::PopupFocusReason);
+ }
+}
+
+/*!
+ \internal
+*/
+void QApplicationPrivate::closePopup(QWidget *popup)
+{
+ Q_Q(QApplication);
+ if (!QApplicationPrivate::popupWidgets)
+ return;
+
+ QApplicationPrivate::popupWidgets->removeAll(popup);
+ if (popup == qt_button_down)
+ qt_button_down = 0;
+ if (QApplicationPrivate::popupWidgets->isEmpty()) { // this was the last popup
+ delete QApplicationPrivate::popupWidgets;
+ QApplicationPrivate::popupWidgets = 0;
+
+ // Special case for Tool windows: since they are activated and deactived together
+ // with a normal window they never become the QApplicationPrivate::active_window.
+ QWidget *appFocusWidget = QApplication::focusWidget();
+ if (appFocusWidget && appFocusWidget->window()->windowType() == Qt::Tool) {
+ appFocusWidget->setFocus(Qt::PopupFocusReason);
+ } else if (QApplicationPrivate::active_window) {
+ if (QWidget *fw = QApplicationPrivate::active_window->focusWidget()) {
+ if (fw != QApplication::focusWidget()) {
+ fw->setFocus(Qt::PopupFocusReason);
+ } else {
+ QFocusEvent e(QEvent::FocusIn, Qt::PopupFocusReason);
+ q->sendEvent(fw, &e);
+ }
+ }
+ }
+ } else {
+ // popups are not focus-handled by the window system (the
+ // first popup grabbed the keyboard), so we have to do that
+ // manually: A popup was closed, so the previous popup gets
+ // the focus.
+ QWidget* aw = QApplicationPrivate::popupWidgets->last();
+ if (QWidget *fw = aw->focusWidget())
+ fw->setFocus(Qt::PopupFocusReason);
+ }
+}
+
+void QApplication::beep()
+{
+ qt_mac_beep();
+}
+
+void QApplication::alert(QWidget *widget, int duration)
+{
+ if (!QApplicationPrivate::checkInstance("alert"))
+ return;
+
+ QWidgetList windowsToMark;
+ if (!widget)
+ windowsToMark += topLevelWidgets();
+ else
+ windowsToMark.append(widget->window());
+
+ bool needNotification = false;
+ for (int i = 0; i < windowsToMark.size(); ++i) {
+ QWidget *window = windowsToMark.at(i);
+ if (!window->isActiveWindow() && window->isVisible()) {
+ needNotification = true; // yeah, we may set it multiple times, but that's OK.
+ if (duration != 0) {
+ QTimer *timer = new QTimer(qApp);
+ timer->setSingleShot(true);
+ connect(timer, SIGNAL(timeout()), qApp, SLOT(_q_alertTimeOut()));
+ if (QTimer *oldTimer = qApp->d_func()->alertTimerHash.value(widget)) {
+ qApp->d_func()->alertTimerHash.remove(widget);
+ delete oldTimer;
+ }
+ qApp->d_func()->alertTimerHash.insert(widget, timer);
+ timer->start(duration);
+ }
+ }
+ }
+ if (needNotification)
+ qt_mac_send_notification();
+}
+
+void QApplicationPrivate::_q_alertTimeOut()
+{
+ if (QTimer *timer = qobject_cast<QTimer *>(q_func()->sender())) {
+ QHash<QWidget *, QTimer *>::iterator it = alertTimerHash.begin();
+ while (it != alertTimerHash.end()) {
+ if (it.value() == timer) {
+ alertTimerHash.erase(it);
+ timer->deleteLater();
+ break;
+ }
+ ++it;
+ }
+ if (alertTimerHash.isEmpty()) {
+ qt_mac_cancel_notification();
+ }
+ }
+}
+
+void QApplication::setCursorFlashTime(int msecs)
+{
+ QApplicationPrivate::cursor_flash_time = msecs;
+}
+
+int QApplication::cursorFlashTime()
+{
+ return QApplicationPrivate::cursor_flash_time;
+}
+
+void QApplication::setDoubleClickInterval(int ms)
+{
+ qt_mac_dblclick.use_qt_time_limit = true;
+ QApplicationPrivate::mouse_double_click_time = ms;
+}
+
+int QApplication::doubleClickInterval()
+{
+ if (!qt_mac_dblclick.use_qt_time_limit) { //get it from the system
+ QSettings appleSettings(QLatin1String("apple.com"));
+ /* First worked as of 10.3.3 */
+ double dci = appleSettings.value(QLatin1String("com/apple/mouse/doubleClickThreshold"), 0.5).toDouble();
+ return int(dci * 1000);
+ }
+ return QApplicationPrivate::mouse_double_click_time;
+}
+
+void QApplication::setKeyboardInputInterval(int ms)
+{
+ QApplicationPrivate::keyboard_input_time = ms;
+}
+
+int QApplication::keyboardInputInterval()
+{
+ // FIXME: get from the system
+ return QApplicationPrivate::keyboard_input_time;
+}
+
+#ifndef QT_NO_WHEELEVENT
+void QApplication::setWheelScrollLines(int n)
+{
+ QApplicationPrivate::wheel_scroll_lines = n;
+}
+
+int QApplication::wheelScrollLines()
+{
+ return QApplicationPrivate::wheel_scroll_lines;
+}
+#endif
+
+void QApplication::setEffectEnabled(Qt::UIEffect effect, bool enable)
+{
+ switch (effect) {
+ case Qt::UI_FadeMenu:
+ QApplicationPrivate::fade_menu = enable;
+ break;
+ case Qt::UI_AnimateMenu:
+ QApplicationPrivate::animate_menu = enable;
+ break;
+ case Qt::UI_FadeTooltip:
+ QApplicationPrivate::fade_tooltip = enable;
+ break;
+ case Qt::UI_AnimateTooltip:
+ QApplicationPrivate::animate_tooltip = enable;
+ break;
+ case Qt::UI_AnimateCombo:
+ QApplicationPrivate::animate_combo = enable;
+ break;
+ case Qt::UI_AnimateToolBox:
+ QApplicationPrivate::animate_toolbox = enable;
+ break;
+ case Qt::UI_General:
+ QApplicationPrivate::fade_tooltip = true;
+ break;
+ default:
+ QApplicationPrivate::animate_ui = enable;
+ break;
+ }
+
+ if (enable)
+ QApplicationPrivate::animate_ui = true;
+}
+
+bool QApplication::isEffectEnabled(Qt::UIEffect effect)
+{
+ if (QColormap::instance().depth() < 16 || !QApplicationPrivate::animate_ui)
+ return false;
+
+ switch(effect) {
+ case Qt::UI_AnimateMenu:
+ return QApplicationPrivate::animate_menu;
+ case Qt::UI_FadeMenu:
+ return QApplicationPrivate::fade_menu;
+ case Qt::UI_AnimateCombo:
+ return QApplicationPrivate::animate_combo;
+ case Qt::UI_AnimateTooltip:
+ return QApplicationPrivate::animate_tooltip;
+ case Qt::UI_FadeTooltip:
+ return QApplicationPrivate::fade_tooltip;
+ case Qt::UI_AnimateToolBox:
+ return QApplicationPrivate::animate_toolbox;
+ default:
+ break;
+ }
+ return QApplicationPrivate::animate_ui;
+}
+
+/*!
+ \internal
+*/
+bool QApplicationPrivate::qt_mac_apply_settings()
+{
+ QSettings settings(QSettings::UserScope, QLatin1String("Trolltech"));
+ settings.beginGroup(QLatin1String("Qt"));
+
+ /*
+ Qt settings. This is how they are written into the datastream.
+ Palette/ * - QPalette
+ font - QFont
+ libraryPath - QStringList
+ style - QString
+ doubleClickInterval - int
+ cursorFlashTime - int
+ wheelScrollLines - int
+ colorSpec - QString
+ defaultCodec - QString
+ globalStrut/width - int
+ globalStrut/height - int
+ GUIEffects - QStringList
+ Font Substitutions/ * - QStringList
+ Font Substitutions/... - QStringList
+ */
+
+ // read library (ie. plugin) path list
+ QString libpathkey =
+ QString::fromLatin1("%1.%2/libraryPath")
+ .arg(QT_VERSION >> 16)
+ .arg((QT_VERSION & 0xff00) >> 8);
+ QStringList pathlist = settings.value(libpathkey).toString().split(QLatin1Char(':'));
+ if (!pathlist.isEmpty()) {
+ QStringList::ConstIterator it = pathlist.begin();
+ while(it != pathlist.end())
+ QApplication::addLibraryPath(*it++);
+ }
+
+ QString defaultcodec = settings.value(QLatin1String("defaultCodec"), QVariant(QLatin1String("none"))).toString();
+ if (defaultcodec != QLatin1String("none")) {
+ QTextCodec *codec = QTextCodec::codecForName(defaultcodec.toLatin1().constData());
+ if (codec)
+ QTextCodec::setCodecForTr(codec);
+ }
+
+ if (qt_is_gui_used) {
+ QString str;
+ QStringList strlist;
+ int num;
+
+ // read new palette
+ int i;
+ QPalette pal(QApplication::palette());
+ strlist = settings.value(QLatin1String("Palette/active")).toStringList();
+ if (strlist.count() == QPalette::NColorRoles) {
+ for (i = 0; i < QPalette::NColorRoles; i++)
+ pal.setColor(QPalette::Active, (QPalette::ColorRole) i,
+ QColor(strlist[i]));
+ }
+ strlist = settings.value(QLatin1String("Palette/inactive")).toStringList();
+ if (strlist.count() == QPalette::NColorRoles) {
+ for (i = 0; i < QPalette::NColorRoles; i++)
+ pal.setColor(QPalette::Inactive, (QPalette::ColorRole) i,
+ QColor(strlist[i]));
+ }
+ strlist = settings.value(QLatin1String("Palette/disabled")).toStringList();
+ if (strlist.count() == QPalette::NColorRoles) {
+ for (i = 0; i < QPalette::NColorRoles; i++)
+ pal.setColor(QPalette::Disabled, (QPalette::ColorRole) i,
+ QColor(strlist[i]));
+ }
+
+ if (pal != QApplication::palette())
+ QApplication::setPalette(pal);
+
+ // read new font
+ QFont font(QApplication::font());
+ str = settings.value(QLatin1String("font")).toString();
+ if (!str.isEmpty()) {
+ font.fromString(str);
+ if (font != QApplication::font())
+ QApplication::setFont(font);
+ }
+
+ // read new QStyle
+ QString stylename = settings.value(QLatin1String("style")).toString();
+ if (! stylename.isNull() && ! stylename.isEmpty()) {
+ QStyle *style = QStyleFactory::create(stylename);
+ if (style)
+ QApplication::setStyle(style);
+ else
+ stylename = QLatin1String("default");
+ } else {
+ stylename = QLatin1String("default");
+ }
+
+ num = settings.value(QLatin1String("doubleClickInterval"),
+ QApplication::doubleClickInterval()).toInt();
+ QApplication::setDoubleClickInterval(num);
+
+ num = settings.value(QLatin1String("cursorFlashTime"),
+ QApplication::cursorFlashTime()).toInt();
+ QApplication::setCursorFlashTime(num);
+
+#ifndef QT_NO_WHEELEVENT
+ num = settings.value(QLatin1String("wheelScrollLines"),
+ QApplication::wheelScrollLines()).toInt();
+ QApplication::setWheelScrollLines(num);
+#endif
+
+ QString colorspec = settings.value(QLatin1String("colorSpec"),
+ QVariant(QLatin1String("default"))).toString();
+ if (colorspec == QLatin1String("normal"))
+ QApplication::setColorSpec(QApplication::NormalColor);
+ else if (colorspec == QLatin1String("custom"))
+ QApplication::setColorSpec(QApplication::CustomColor);
+ else if (colorspec == QLatin1String("many"))
+ QApplication::setColorSpec(QApplication::ManyColor);
+ else if (colorspec != QLatin1String("default"))
+ colorspec = QLatin1String("default");
+
+ int w = settings.value(QLatin1String("globalStrut/width")).toInt();
+ int h = settings.value(QLatin1String("globalStrut/height")).toInt();
+ QSize strut(w, h);
+ if (strut.isValid())
+ QApplication::setGlobalStrut(strut);
+
+ QStringList effects = settings.value(QLatin1String("GUIEffects")).toStringList();
+ if (!effects.isEmpty()) {
+ if (effects.contains(QLatin1String("none")))
+ QApplication::setEffectEnabled(Qt::UI_General, false);
+ if (effects.contains(QLatin1String("general")))
+ QApplication::setEffectEnabled(Qt::UI_General, true);
+ if (effects.contains(QLatin1String("animatemenu")))
+ QApplication::setEffectEnabled(Qt::UI_AnimateMenu, true);
+ if (effects.contains(QLatin1String("fademenu")))
+ QApplication::setEffectEnabled(Qt::UI_FadeMenu, true);
+ if (effects.contains(QLatin1String("animatecombo")))
+ QApplication::setEffectEnabled(Qt::UI_AnimateCombo, true);
+ if (effects.contains(QLatin1String("animatetooltip")))
+ QApplication::setEffectEnabled(Qt::UI_AnimateTooltip, true);
+ if (effects.contains(QLatin1String("fadetooltip")))
+ QApplication::setEffectEnabled(Qt::UI_FadeTooltip, true);
+ if (effects.contains(QLatin1String("animatetoolbox")))
+ QApplication::setEffectEnabled(Qt::UI_AnimateToolBox, true);
+ } else {
+ QApplication::setEffectEnabled(Qt::UI_General, true);
+ }
+
+ settings.beginGroup(QLatin1String("Font Substitutions"));
+ QStringList fontsubs = settings.childKeys();
+ if (!fontsubs.isEmpty()) {
+ QStringList::Iterator it = fontsubs.begin();
+ for (; it != fontsubs.end(); ++it) {
+ QString fam = QString::fromLatin1((*it).toLatin1().constData());
+ QStringList subs = settings.value(fam).toStringList();
+ QFont::insertSubstitutions(fam, subs);
+ }
+ }
+ settings.endGroup();
+ }
+
+ settings.endGroup();
+ return true;
+}
+
+// DRSWAT
+
+bool QApplicationPrivate::canQuit()
+{
+#ifndef QT_MAC_USE_COCOA
+ return true;
+#else
+ Q_Q(QApplication);
+#ifdef QT_MAC_USE_COCOA
+ [[NSApp mainMenu] cancelTracking];
+#else
+ HiliteMenu(0);
+#endif
+
+ bool handle_quit = true;
+ if (QApplicationPrivate::modalState() && [[[[QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) sharedDelegate]
+ menuLoader] quitMenuItem] isEnabled]) {
+ int visible = 0;
+ const QWidgetList tlws = QApplication::topLevelWidgets();
+ for(int i = 0; i < tlws.size(); ++i) {
+ if (tlws.at(i)->isVisible())
+ ++visible;
+ }
+ handle_quit = (visible <= 1);
+ }
+ if (handle_quit) {
+ QCloseEvent ev;
+ QApplication::sendSpontaneousEvent(q, &ev);
+ if (ev.isAccepted()) {
+ return true;
+ }
+ }
+ return false;
+#endif
+}
+
+void onApplicationWindowChangedActivation(QWidget *widget, bool activated)
+{
+#if QT_MAC_USE_COCOA
+ if (!widget)
+ return;
+
+ if (activated) {
+ if (QApplicationPrivate::app_style) {
+ QEvent ev(QEvent::Style);
+ qt_sendSpontaneousEvent(QApplicationPrivate::app_style, &ev);
+ }
+ qApp->setActiveWindow(widget);
+ } else { // deactivated
+ if (QApplicationPrivate::active_window == widget)
+ qApp->setActiveWindow(0);
+ }
+
+ QMenuBar::macUpdateMenuBar();
+ qt_mac_update_cursor();
+#else
+ Q_UNUSED(widget);
+ Q_UNUSED(activated);
+#endif
+}
+
+
+void onApplicationChangedActivation( bool activated )
+{
+#if QT_MAC_USE_COCOA
+ QApplication *app = qApp;
+
+//NSLog(@"App Changed Activation\n");
+
+ if ( activated ) {
+ if (QApplication::desktopSettingsAware())
+ qt_mac_update_os_settings();
+
+ if (qt_clipboard) { //manufacture an event so the clipboard can see if it has changed
+ QEvent ev(QEvent::Clipboard);
+ qt_sendSpontaneousEvent(qt_clipboard, &ev);
+ }
+
+ if (app) {
+ QEvent ev(QEvent::ApplicationActivate);
+ qt_sendSpontaneousEvent(app, &ev);
+ }
+
+ if (!app->activeWindow()) {
+ OSWindowRef wp = [NSApp keyWindow];
+ if (QWidget *tmp_w = qt_mac_find_window(wp))
+ app->setActiveWindow(tmp_w);
+ }
+ QMenuBar::macUpdateMenuBar();
+ qt_mac_update_cursor();
+ } else { // de-activated
+ QApplicationPrivate *priv = [[QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) sharedDelegate] qAppPrivate];
+ while (priv->inPopupMode())
+ app->activePopupWidget()->close();
+ if (app) {
+ QEvent ev(QEvent::ApplicationDeactivate);
+ qt_sendSpontaneousEvent(app, &ev);
+ }
+ app->setActiveWindow(0);
+ }
+#else
+ Q_UNUSED(activated);
+#endif
+}
+
+void QApplicationPrivate::initializeMultitouch_sys()
+{ }
+void QApplicationPrivate::cleanupMultitouch_sys()
+{ }
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/mac/qclipboard_mac.cpp b/src/widgets/platforms/mac/qclipboard_mac.cpp
new file mode 100644
index 0000000000..002b8da967
--- /dev/null
+++ b/src/widgets/platforms/mac/qclipboard_mac.cpp
@@ -0,0 +1,634 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qclipboard.h"
+#include "qapplication.h"
+#include "qbitmap.h"
+#include "qdatetime.h"
+#include "qdebug.h"
+#include "qapplication_p.h"
+#include <private/qt_mac_p.h>
+#include "qevent.h"
+#include "qurl.h"
+#include <stdlib.h>
+#include <string.h>
+#include "qt_cocoa_helpers_mac_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QT_USE_NAMESPACE
+
+/*****************************************************************************
+ QClipboard debug facilities
+ *****************************************************************************/
+//#define DEBUG_PASTEBOARD
+
+#ifndef QT_NO_CLIPBOARD
+
+/*****************************************************************************
+ QClipboard member functions for mac.
+ *****************************************************************************/
+
+static QMacPasteboard *qt_mac_pasteboards[2] = {0, 0};
+
+static inline QMacPasteboard *qt_mac_pasteboard(QClipboard::Mode mode)
+{
+ Q_ASSERT(mode == QClipboard::Clipboard || mode == QClipboard::FindBuffer);
+ if (mode == QClipboard::Clipboard)
+ return qt_mac_pasteboards[0];
+ else
+ return qt_mac_pasteboards[1];
+}
+
+static void qt_mac_cleanupPasteboard() {
+ delete qt_mac_pasteboards[0];
+ delete qt_mac_pasteboards[1];
+ qt_mac_pasteboards[0] = 0;
+ qt_mac_pasteboards[1] = 0;
+}
+
+static bool qt_mac_updateScrap(QClipboard::Mode mode)
+{
+ if(!qt_mac_pasteboards[0]) {
+ qt_mac_pasteboards[0] = new QMacPasteboard(kPasteboardClipboard, QMacPasteboardMime::MIME_CLIP);
+ qt_mac_pasteboards[1] = new QMacPasteboard(kPasteboardFind, QMacPasteboardMime::MIME_CLIP);
+ qAddPostRoutine(qt_mac_cleanupPasteboard);
+ return true;
+ }
+ return qt_mac_pasteboard(mode)->sync();
+}
+
+void QClipboard::clear(Mode mode)
+{
+ if (!supportsMode(mode))
+ return;
+ qt_mac_updateScrap(mode);
+ qt_mac_pasteboard(mode)->clear();
+ setMimeData(0, mode);
+}
+
+void QClipboard::ownerDestroyed()
+{
+}
+
+
+void QClipboard::connectNotify(const char *signal)
+{
+ Q_UNUSED(signal);
+}
+
+bool QClipboard::event(QEvent *e)
+{
+ if(e->type() != QEvent::Clipboard)
+ return QObject::event(e);
+
+ if (qt_mac_updateScrap(QClipboard::Clipboard)) {
+ emitChanged(QClipboard::Clipboard);
+ }
+
+ if (qt_mac_updateScrap(QClipboard::FindBuffer)) {
+ emitChanged(QClipboard::FindBuffer);
+ }
+
+ return QObject::event(e);
+}
+
+const QMimeData *QClipboard::mimeData(Mode mode) const
+{
+ if (!supportsMode(mode))
+ return 0;
+ qt_mac_updateScrap(mode);
+ return qt_mac_pasteboard(mode)->mimeData();
+}
+
+void QClipboard::setMimeData(QMimeData *src, Mode mode)
+{
+ if (!supportsMode(mode))
+ return;
+ qt_mac_updateScrap(mode);
+ qt_mac_pasteboard(mode)->setMimeData(src);
+ emitChanged(mode);
+}
+
+bool QClipboard::supportsMode(Mode mode) const
+{
+ return (mode == Clipboard || mode == FindBuffer);
+}
+
+bool QClipboard::ownsMode(Mode mode) const
+{
+ Q_UNUSED(mode);
+ return false;
+}
+
+#endif // QT_NO_CLIPBOARD
+
+/*****************************************************************************
+ QMacPasteboard code
+*****************************************************************************/
+
+QMacPasteboard::QMacPasteboard(PasteboardRef p, uchar mt)
+{
+ mac_mime_source = false;
+ mime_type = mt ? mt : uchar(QMacPasteboardMime::MIME_ALL);
+ paste = p;
+ CFRetain(paste);
+}
+
+QMacPasteboard::QMacPasteboard(uchar mt)
+{
+ mac_mime_source = false;
+ mime_type = mt ? mt : uchar(QMacPasteboardMime::MIME_ALL);
+ paste = 0;
+ OSStatus err = PasteboardCreate(0, &paste);
+ if(err == noErr) {
+ PasteboardSetPromiseKeeper(paste, promiseKeeper, this);
+ } else {
+ qDebug("PasteBoard: Error creating pasteboard: [%d]", (int)err);
+ }
+}
+
+QMacPasteboard::QMacPasteboard(CFStringRef name, uchar mt)
+{
+ mac_mime_source = false;
+ mime_type = mt ? mt : uchar(QMacPasteboardMime::MIME_ALL);
+ paste = 0;
+ OSStatus err = PasteboardCreate(name, &paste);
+ if(err == noErr) {
+ PasteboardSetPromiseKeeper(paste, promiseKeeper, this);
+ } else {
+ qDebug("PasteBoard: Error creating pasteboard: %s [%d]", QCFString::toQString(name).toLatin1().constData(), (int)err);
+ }
+}
+
+QMacPasteboard::~QMacPasteboard()
+{
+ // commit all promises for paste after exit close
+ for (int i = 0; i < promises.count(); ++i) {
+ const Promise &promise = promises.at(i);
+ QCFString flavor = QCFString(promise.convertor->flavorFor(promise.mime));
+ promiseKeeper(paste, (PasteboardItemID)promise.itemId, flavor, this);
+ }
+
+ if(paste)
+ CFRelease(paste);
+}
+
+PasteboardRef
+QMacPasteboard::pasteBoard() const
+{
+ return paste;
+}
+
+OSStatus QMacPasteboard::promiseKeeper(PasteboardRef paste, PasteboardItemID id, CFStringRef flavor, void *_qpaste)
+{
+ QMacPasteboard *qpaste = (QMacPasteboard*)_qpaste;
+ const long promise_id = (long)id;
+
+ // Find the kept promise
+ const QString flavorAsQString = QCFString::toQString(flavor);
+ QMacPasteboard::Promise promise;
+ for (int i = 0; i < qpaste->promises.size(); i++){
+ QMacPasteboard::Promise tmp = qpaste->promises[i];
+ if (tmp.itemId == promise_id && tmp.convertor->canConvert(tmp.mime, flavorAsQString)){
+ promise = tmp;
+ break;
+ }
+ }
+
+ if (!promise.itemId && flavorAsQString == QLatin1String("com.trolltech.qt.MimeTypeName")) {
+ // we have promised this data, but wont be able to convert, so return null data.
+ // This helps in making the application/x-qt-mime-type-name hidden from normal use.
+ QByteArray ba;
+ QCFType<CFDataRef> data = CFDataCreate(0, (UInt8*)ba.constData(), ba.size());
+ PasteboardPutItemFlavor(paste, id, flavor, data, kPasteboardFlavorNoFlags);
+ return noErr;
+ }
+
+ if (!promise.itemId) {
+ // There was no promise that could deliver data for the
+ // given id and flavor. This should not happend.
+ qDebug("Pasteboard: %d: Request for %ld, %s, but no promise found!", __LINE__, promise_id, qPrintable(flavorAsQString));
+ return cantGetFlavorErr;
+ }
+
+#ifdef DEBUG_PASTEBOARD
+ qDebug("PasteBoard: Calling in promise for %s[%ld] [%s] (%s) [%d]", qPrintable(promise.mime), promise_id,
+ qPrintable(flavorAsQString), qPrintable(promise.convertor->convertorName()), promise.offset);
+#endif
+
+ QList<QByteArray> md = promise.convertor->convertFromMime(promise.mime, promise.data, flavorAsQString);
+ if (md.size() <= promise.offset)
+ return cantGetFlavorErr;
+ const QByteArray &ba = md[promise.offset];
+ QCFType<CFDataRef> data = CFDataCreate(0, (UInt8*)ba.constData(), ba.size());
+ PasteboardPutItemFlavor(paste, id, flavor, data, kPasteboardFlavorNoFlags);
+ return noErr;
+}
+
+bool
+QMacPasteboard::hasOSType(int c_flavor) const
+{
+ if (!paste)
+ return false;
+
+ sync();
+
+ ItemCount cnt = 0;
+ if(PasteboardGetItemCount(paste, &cnt) || !cnt)
+ return false;
+
+#ifdef DEBUG_PASTEBOARD
+ qDebug("PasteBoard: hasOSType [%c%c%c%c]", (c_flavor>>24)&0xFF, (c_flavor>>16)&0xFF,
+ (c_flavor>>8)&0xFF, (c_flavor>>0)&0xFF);
+#endif
+ for(uint index = 1; index <= cnt; ++index) {
+
+ PasteboardItemID id;
+ if(PasteboardGetItemIdentifier(paste, index, &id) != noErr)
+ return false;
+
+ QCFType<CFArrayRef> types;
+ if(PasteboardCopyItemFlavors(paste, id, &types ) != noErr)
+ return false;
+
+ const int type_count = CFArrayGetCount(types);
+ for(int i = 0; i < type_count; ++i) {
+ CFStringRef flavor = (CFStringRef)CFArrayGetValueAtIndex(types, i);
+ const int os_flavor = UTGetOSTypeFromString(UTTypeCopyPreferredTagWithClass(flavor, kUTTagClassOSType));
+ if(os_flavor == c_flavor) {
+#ifdef DEBUG_PASTEBOARD
+ qDebug(" - Found!");
+#endif
+ return true;
+ }
+ }
+ }
+#ifdef DEBUG_PASTEBOARD
+ qDebug(" - NotFound!");
+#endif
+ return false;
+}
+
+bool
+QMacPasteboard::hasFlavor(QString c_flavor) const
+{
+ if (!paste)
+ return false;
+
+ sync();
+
+ ItemCount cnt = 0;
+ if(PasteboardGetItemCount(paste, &cnt) || !cnt)
+ return false;
+
+#ifdef DEBUG_PASTEBOARD
+ qDebug("PasteBoard: hasFlavor [%s]", qPrintable(c_flavor));
+#endif
+ for(uint index = 1; index <= cnt; ++index) {
+
+ PasteboardItemID id;
+ if(PasteboardGetItemIdentifier(paste, index, &id) != noErr)
+ return false;
+
+ PasteboardFlavorFlags flags;
+ if(PasteboardGetItemFlavorFlags(paste, id, QCFString(c_flavor), &flags) == noErr) {
+#ifdef DEBUG_PASTEBOARD
+ qDebug(" - Found!");
+#endif
+ return true;
+ }
+ }
+#ifdef DEBUG_PASTEBOARD
+ qDebug(" - NotFound!");
+#endif
+ return false;
+}
+
+class QMacPasteboardMimeSource : public QMimeData {
+ const QMacPasteboard *paste;
+public:
+ QMacPasteboardMimeSource(const QMacPasteboard *p) : QMimeData(), paste(p) { }
+ ~QMacPasteboardMimeSource() { }
+ virtual QStringList formats() const { return paste->formats(); }
+ virtual QVariant retrieveData(const QString &format, QVariant::Type type) const { return paste->retrieveData(format, type); }
+};
+
+QMimeData
+*QMacPasteboard::mimeData() const
+{
+ if(!mime) {
+ mac_mime_source = true;
+ mime = new QMacPasteboardMimeSource(this);
+
+ }
+ return mime;
+}
+
+class QMacMimeData : public QMimeData
+{
+public:
+ QVariant variantData(const QString &mime) { return retrieveData(mime, QVariant::Invalid); }
+private:
+ QMacMimeData();
+};
+
+void
+QMacPasteboard::setMimeData(QMimeData *mime_src)
+{
+ if (!paste)
+ return;
+
+ if (mime == mime_src || (!mime_src && mime && mac_mime_source))
+ return;
+ mac_mime_source = false;
+ delete mime;
+ mime = mime_src;
+
+ QList<QMacPasteboardMime*> availableConverters = QMacPasteboardMime::all(mime_type);
+ if (mime != 0) {
+ clear_helper();
+ QStringList formats = mime_src->formats();
+
+#ifdef QT_MAC_USE_COCOA
+ // QMimeData sub classes reimplementing the formats() might not expose the
+ // temporary "application/x-qt-mime-type-name" mimetype. So check the existence
+ // of this mime type while doing drag and drop.
+ QString dummyMimeType(QLatin1String("application/x-qt-mime-type-name"));
+ if (!formats.contains(dummyMimeType)) {
+ QByteArray dummyType = mime_src->data(dummyMimeType);
+ if (!dummyType.isEmpty()) {
+ formats.append(dummyMimeType);
+ }
+ }
+#endif
+ for(int f = 0; f < formats.size(); ++f) {
+ QString mimeType = formats.at(f);
+ for (QList<QMacPasteboardMime *>::Iterator it = availableConverters.begin(); it != availableConverters.end(); ++it) {
+ QMacPasteboardMime *c = (*it);
+ QString flavor(c->flavorFor(mimeType));
+ if(!flavor.isEmpty()) {
+ QVariant mimeData = static_cast<QMacMimeData*>(mime_src)->variantData(mimeType);
+#if 0
+ //### Grrr, why didn't I put in a virtual int QMacPasteboardMime::count()? --Sam
+ const int numItems = c->convertFromMime(mimeType, mimeData, flavor).size();
+#else
+ int numItems = 1; //this is a hack but it is much faster than allowing conversion above
+ if(c->convertorName() == QLatin1String("FileURL"))
+ numItems = mime_src->urls().count();
+#endif
+ for(int item = 0; item < numItems; ++item) {
+ const int itemID = item+1; //id starts at 1
+ promises.append(QMacPasteboard::Promise(itemID, c, mimeType, mimeData, item));
+ PasteboardPutItemFlavor(paste, (PasteboardItemID)itemID, QCFString(flavor), 0, kPasteboardFlavorNoFlags);
+#ifdef DEBUG_PASTEBOARD
+ qDebug(" - adding %d %s [%s] <%s> [%d]",
+ itemID, qPrintable(mimeType), qPrintable(flavor), qPrintable(c->convertorName()), item);
+#endif
+ }
+ }
+ }
+ }
+ }
+}
+
+QStringList
+QMacPasteboard::formats() const
+{
+ if (!paste)
+ return QStringList();
+
+ sync();
+
+ QStringList ret;
+ ItemCount cnt = 0;
+ if(PasteboardGetItemCount(paste, &cnt) || !cnt)
+ return ret;
+
+#ifdef DEBUG_PASTEBOARD
+ qDebug("PasteBoard: Formats [%d]", (int)cnt);
+#endif
+ for(uint index = 1; index <= cnt; ++index) {
+
+ PasteboardItemID id;
+ if(PasteboardGetItemIdentifier(paste, index, &id) != noErr)
+ continue;
+
+ QCFType<CFArrayRef> types;
+ if(PasteboardCopyItemFlavors(paste, id, &types ) != noErr)
+ continue;
+
+ const int type_count = CFArrayGetCount(types);
+ for(int i = 0; i < type_count; ++i) {
+ const QString flavor = QCFString::toQString((CFStringRef)CFArrayGetValueAtIndex(types, i));
+#ifdef DEBUG_PASTEBOARD
+ qDebug(" -%s", qPrintable(QString(flavor)));
+#endif
+ QString mimeType = QMacPasteboardMime::flavorToMime(mime_type, flavor);
+ if(!mimeType.isEmpty() && !ret.contains(mimeType)) {
+#ifdef DEBUG_PASTEBOARD
+ qDebug(" -<%d> %s [%s]", ret.size(), qPrintable(mimeType), qPrintable(QString(flavor)));
+#endif
+ ret << mimeType;
+ }
+ }
+ }
+ return ret;
+}
+
+bool
+QMacPasteboard::hasFormat(const QString &format) const
+{
+ if (!paste)
+ return false;
+
+ sync();
+
+ ItemCount cnt = 0;
+ if(PasteboardGetItemCount(paste, &cnt) || !cnt)
+ return false;
+
+#ifdef DEBUG_PASTEBOARD
+ qDebug("PasteBoard: hasFormat [%s]", qPrintable(format));
+#endif
+ for(uint index = 1; index <= cnt; ++index) {
+
+ PasteboardItemID id;
+ if(PasteboardGetItemIdentifier(paste, index, &id) != noErr)
+ continue;
+
+ QCFType<CFArrayRef> types;
+ if(PasteboardCopyItemFlavors(paste, id, &types ) != noErr)
+ continue;
+
+ const int type_count = CFArrayGetCount(types);
+ for(int i = 0; i < type_count; ++i) {
+ const QString flavor = QCFString::toQString((CFStringRef)CFArrayGetValueAtIndex(types, i));
+#ifdef DEBUG_PASTEBOARD
+ qDebug(" -%s [0x%x]", qPrintable(QString(flavor)), mime_type);
+#endif
+ QString mimeType = QMacPasteboardMime::flavorToMime(mime_type, flavor);
+#ifdef DEBUG_PASTEBOARD
+ if(!mimeType.isEmpty())
+ qDebug(" - %s", qPrintable(mimeType));
+#endif
+ if(mimeType == format)
+ return true;
+ }
+ }
+ return false;
+}
+
+QVariant
+QMacPasteboard::retrieveData(const QString &format, QVariant::Type) const
+{
+ if (!paste)
+ return QVariant();
+
+ sync();
+
+ ItemCount cnt = 0;
+ if(PasteboardGetItemCount(paste, &cnt) || !cnt)
+ return QByteArray();
+
+#ifdef DEBUG_PASTEBOARD
+ qDebug("Pasteboard: retrieveData [%s]", qPrintable(format));
+#endif
+ const QList<QMacPasteboardMime *> mimes = QMacPasteboardMime::all(mime_type);
+ for(int mime = 0; mime < mimes.size(); ++mime) {
+ QMacPasteboardMime *c = mimes.at(mime);
+ QString c_flavor = c->flavorFor(format);
+ if(!c_flavor.isEmpty()) {
+ // Handle text/plain a little differently. Try handling Unicode first.
+ bool checkForUtf16 = (c_flavor == QLatin1String("com.apple.traditional-mac-plain-text")
+ || c_flavor == QLatin1String("public.utf8-plain-text"));
+ if (checkForUtf16 || c_flavor == QLatin1String("public.utf16-plain-text")) {
+ // Try to get the NSStringPboardType from NSPasteboard, newlines are mapped
+ // correctly (as '\n') in this data. The 'public.utf16-plain-text' type
+ // usually maps newlines to '\r' instead.
+ QString str = qt_mac_get_pasteboardString(paste);
+ if (!str.isEmpty())
+ return str;
+ }
+ if (checkForUtf16 && hasFlavor(QLatin1String("public.utf16-plain-text")))
+ c_flavor = QLatin1String("public.utf16-plain-text");
+
+ QVariant ret;
+ QList<QByteArray> retList;
+ for(uint index = 1; index <= cnt; ++index) {
+ PasteboardItemID id;
+ if(PasteboardGetItemIdentifier(paste, index, &id) != noErr)
+ continue;
+
+ QCFType<CFArrayRef> types;
+ if(PasteboardCopyItemFlavors(paste, id, &types ) != noErr)
+ continue;
+
+ const int type_count = CFArrayGetCount(types);
+ for(int i = 0; i < type_count; ++i) {
+ CFStringRef flavor = static_cast<CFStringRef>(CFArrayGetValueAtIndex(types, i));
+ if(c_flavor == QCFString::toQString(flavor)) {
+ QCFType<CFDataRef> macBuffer;
+ if(PasteboardCopyItemFlavorData(paste, id, flavor, &macBuffer) == noErr) {
+ QByteArray buffer((const char *)CFDataGetBytePtr(macBuffer), CFDataGetLength(macBuffer));
+ if(!buffer.isEmpty()) {
+#ifdef DEBUG_PASTEBOARD
+ qDebug(" - %s [%s] (%s)", qPrintable(format), qPrintable(QCFString::toQString(flavor)), qPrintable(c->convertorName()));
+#endif
+ buffer.detach(); //detach since we release the macBuffer
+ retList.append(buffer);
+ break; //skip to next element
+ }
+ }
+ } else {
+#ifdef DEBUG_PASTEBOARD
+ qDebug(" - NoMatch %s [%s] (%s)", qPrintable(c_flavor), qPrintable(QCFString::toQString(flavor)), qPrintable(c->convertorName()));
+#endif
+ }
+ }
+ }
+
+ if (!retList.isEmpty()) {
+ ret = c->convertToMime(format, retList, c_flavor);
+ return ret;
+ }
+ }
+ }
+ return QVariant();
+}
+
+void QMacPasteboard::clear_helper()
+{
+ if (paste)
+ PasteboardClear(paste);
+ promises.clear();
+}
+
+void
+QMacPasteboard::clear()
+{
+#ifdef DEBUG_PASTEBOARD
+ qDebug("PasteBoard: clear!");
+#endif
+ clear_helper();
+}
+
+bool
+QMacPasteboard::sync() const
+{
+ if (!paste)
+ return false;
+ const bool fromGlobal = PasteboardSynchronize(paste) & kPasteboardModified;
+
+ if (fromGlobal)
+ const_cast<QMacPasteboard *>(this)->setMimeData(0);
+
+#ifdef DEBUG_PASTEBOARD
+ if(fromGlobal)
+ qDebug("Pasteboard: Synchronize!");
+#endif
+ return fromGlobal;
+}
+
+
+
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/mac/qcocoaintrospection_mac.mm b/src/widgets/platforms/mac/qcocoaintrospection_mac.mm
new file mode 100644
index 0000000000..ed2fbeaeda
--- /dev/null
+++ b/src/widgets/platforms/mac/qcocoaintrospection_mac.mm
@@ -0,0 +1,125 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** Copyright (c) 2007-2008, Apple, Inc.
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**
+** * Redistributions of source code must retain the above copyright notice,
+** this list of conditions and the following disclaimer.
+**
+** * Redistributions in binary form must reproduce the above copyright notice,
+** this list of conditions and the following disclaimer in the documentation
+** and/or other materials provided with the distribution.
+**
+** * Neither the name of Apple, Inc. nor the names of its contributors
+** may be used to endorse or promote products derived from this software
+** without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**
+****************************************************************************/
+
+#include <private/qcocoaintrospection_p.h>
+
+QT_BEGIN_NAMESPACE
+
+void qt_cocoa_change_implementation(Class baseClass, SEL originalSel, Class proxyClass, SEL replacementSel, SEL backupSel)
+{
+#ifndef QT_MAC_USE_COCOA
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5)
+#endif
+ {
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
+ // The following code replaces the _implementation_ for the selector we want to hack
+ // (originalSel) with the implementation found in proxyClass. Then it creates
+ // a new 'backup' method inside baseClass containing the old, original,
+ // implementation (fakeSel). You can let the proxy implementation of originalSel
+ // call fakeSel if needed (similar approach to calling a super class implementation).
+ // fakeSel must also be implemented in proxyClass, as the signature is used
+ // as template for the method one we add into baseClass.
+ // NB: You will typically never create any instances of proxyClass; we use it
+ // only for stealing its contents and put it into baseClass.
+ if (!replacementSel)
+ replacementSel = originalSel;
+
+ Method originalMethod = class_getInstanceMethod(baseClass, originalSel);
+ Method replacementMethod = class_getInstanceMethod(proxyClass, replacementSel);
+ IMP originalImp = method_setImplementation(originalMethod, method_getImplementation(replacementMethod));
+
+ if (backupSel) {
+ Method backupMethod = class_getInstanceMethod(proxyClass, backupSel);
+ class_addMethod(baseClass, backupSel, originalImp, method_getTypeEncoding(backupMethod));
+ }
+#endif
+ }
+}
+
+void qt_cocoa_change_back_implementation(Class baseClass, SEL originalSel, SEL backupSel)
+{
+#ifndef QT_MAC_USE_COCOA
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5)
+#endif
+ {
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
+ Method originalMethod = class_getInstanceMethod(baseClass, originalSel);
+ Method backupMethodInBaseClass = class_getInstanceMethod(baseClass, backupSel);
+ method_setImplementation(originalMethod, method_getImplementation(backupMethodInBaseClass));
+#endif
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/mac/qcocoaintrospection_p.h b/src/widgets/platforms/mac/qcocoaintrospection_p.h
new file mode 100644
index 0000000000..e18646d063
--- /dev/null
+++ b/src/widgets/platforms/mac/qcocoaintrospection_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** Copyright (c) 2007-2008, Apple, Inc.
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**
+** * Redistributions of source code must retain the above copyright notice,
+** this list of conditions and the following disclaimer.
+**
+** * Redistributions in binary form must reproduce the above copyright notice,
+** this list of conditions and the following disclaimer in the documentation
+** and/or other materials provided with the distribution.
+**
+** * Neither the name of Apple, Inc. nor the names of its contributors
+** may be used to endorse or promote products derived from this software
+** without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**
+****************************************************************************/
+
+#include <qglobal.h>
+#import <objc/objc-class.h>
+
+QT_BEGIN_NAMESPACE
+
+void qt_cocoa_change_implementation(Class baseClass, SEL originalSel, Class proxyClass, SEL replacementSel = 0, SEL backupSel = 0);
+void qt_cocoa_change_back_implementation(Class baseClass, SEL originalSel, SEL backupSel);
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/mac/qcocoapanel_mac.mm b/src/widgets/platforms/mac/qcocoapanel_mac.mm
new file mode 100644
index 0000000000..5b490cf8b4
--- /dev/null
+++ b/src/widgets/platforms/mac/qcocoapanel_mac.mm
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#import <private/qcocoapanel_mac_p.h>
+#ifdef QT_MAC_USE_COCOA
+#import <private/qt_cocoa_helpers_mac_p.h>
+#import <private/qcocoawindow_mac_p.h>
+#import <private/qcocoawindowdelegate_mac_p.h>
+#import <private/qcocoaview_mac_p.h>
+#import <private/qcocoawindowcustomthemeframe_mac_p.h>
+#import <private/qcocoaapplication_mac_p.h>
+#import <private/qmultitouch_mac_p.h>
+#import <private/qapplication_p.h>
+#import <private/qbackingstore_p.h>
+#import <private/qdnd_p.h>
+
+#include <QtGui/QWidget>
+
+QT_FORWARD_DECLARE_CLASS(QWidget);
+QT_USE_NAMESPACE
+
+@implementation QT_MANGLE_NAMESPACE(QCocoaPanel)
+
+/***********************************************************************
+ Copy and Paste between QCocoaWindow and QCocoaPanel
+ This is a bit unfortunate, but thanks to the dynamic dispatch we
+ have to duplicate this code or resort to really silly forwarding methods
+**************************************************************************/
+#include "qcocoasharedwindowmethods_mac_p.h"
+
+@end
+#endif
diff --git a/src/widgets/platforms/mac/qcocoapanel_mac_p.h b/src/widgets/platforms/mac/qcocoapanel_mac_p.h
new file mode 100644
index 0000000000..ad02b16657
--- /dev/null
+++ b/src/widgets/platforms/mac/qcocoapanel_mac_p.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QCOCOAPANEL_MAC_P
+#define QCOCOAPANEL_MAC_P
+
+#include "qmacdefines_mac.h"
+#ifdef QT_MAC_USE_COCOA
+#import <Cocoa/Cocoa.h>
+
+QT_FORWARD_DECLARE_CLASS(QStringList);
+QT_FORWARD_DECLARE_CLASS(QCocoaDropData);
+
+@interface NSPanel (QtIntegration)
+- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender;
+- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender;
+- (void)draggingExited:(id <NSDraggingInfo>)sender;
+- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender;
+@end
+
+@interface QT_MANGLE_NAMESPACE(QCocoaPanel) : NSPanel {
+ QStringList *currentCustomDragTypes;
+ QCocoaDropData *dropData;
+ NSInteger dragEnterSequence;
+}
+
++ (Class)frameViewClassForStyleMask:(NSUInteger)styleMask;
+- (void)registerDragTypes;
+- (void)drawRectOriginal:(NSRect)rect;
+
+@end
+#endif
+
+#endif
diff --git a/src/widgets/platforms/mac/qcocoasharedwindowmethods_mac_p.h b/src/widgets/platforms/mac/qcocoasharedwindowmethods_mac_p.h
new file mode 100644
index 0000000000..f5a93d98d2
--- /dev/null
+++ b/src/widgets/platforms/mac/qcocoasharedwindowmethods_mac_p.h
@@ -0,0 +1,610 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+ NB: This is not a header file, dispite the file name suffix. This file is
+ included directly into the source code of qcocoawindow_mac.mm and
+ qcocoapanel_mac.mm to avoid manually doing copy and paste of the exact
+ same code needed at both places. This solution makes it more difficult
+ to e.g fix a bug in qcocoawindow_mac.mm, but forget to do the same in
+ qcocoapanel_mac.mm.
+ The reason we need to do copy and paste in the first place, rather than
+ resolve to method overriding, is that QCocoaPanel needs to inherit from
+ NSPanel, while QCocoaWindow needs to inherit NSWindow rather than NSPanel).
+****************************************************************************/
+
+// WARNING: Don't include any header files from within this file. Put them
+// directly into qcocoawindow_mac_p.h and qcocoapanel_mac_p.h
+
+QT_BEGIN_NAMESPACE
+extern Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum); // qcocoaview.mm
+extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp
+extern const QStringList& qEnabledDraggedTypes(); // qmime_mac.cpp
+extern void qt_event_request_window_change(QWidget *); // qapplication_mac.mm
+extern void qt_mac_send_posted_gl_updates(QWidget *widget); // qapplication_mac.mm
+
+Q_GLOBAL_STATIC(QPointer<QWidget>, currentDragTarget);
+QT_END_NAMESPACE
+
+- (id)initWithContentRect:(NSRect)contentRect
+ styleMask:(NSUInteger)windowStyle
+ backing:(NSBackingStoreType)bufferingType
+ defer:(BOOL)deferCreation
+{
+ self = [super initWithContentRect:contentRect styleMask:windowStyle
+ backing:bufferingType defer:deferCreation];
+ if (self) {
+ currentCustomDragTypes = 0;
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ delete currentCustomDragTypes;
+ [super dealloc];
+}
+
+- (BOOL)canBecomeKeyWindow
+{
+ QWidget *widget = [self QT_MANGLE_NAMESPACE(qt_qwidget)];
+ if (!widget)
+ return NO; // This should happen only for qt_root_win
+ if (QApplicationPrivate::isBlockedByModal(widget))
+ return NO;
+
+ bool isToolTip = (widget->windowType() == Qt::ToolTip);
+ bool isPopup = (widget->windowType() == Qt::Popup);
+ return !(isPopup || isToolTip);
+}
+
+- (BOOL)canBecomeMainWindow
+{
+ QWidget *widget = [self QT_MANGLE_NAMESPACE(qt_qwidget)];
+ if (!widget)
+ return NO; // This should happen only for qt_root_win
+ if ([self isSheet])
+ return NO;
+
+ bool isToolTip = (widget->windowType() == Qt::ToolTip);
+ bool isPopup = (widget->windowType() == Qt::Popup);
+ bool isTool = (widget->windowType() == Qt::Tool);
+ return !(isPopup || isToolTip || isTool);
+}
+
+- (void)becomeMainWindow
+{
+ [super becomeMainWindow];
+ // Cocoa sometimes tell a hidden window to become the
+ // main window (and as such, show it). This can e.g
+ // happend when the application gets activated. If
+ // this is the case, we tell it to hide again:
+ if (![self isVisible])
+ [self orderOut:self];
+}
+
+- (void)toggleToolbarShown:(id)sender
+{
+ macSendToolbarChangeEvent([self QT_MANGLE_NAMESPACE(qt_qwidget)]);
+ [super toggleToolbarShown:sender];
+}
+
+- (void)flagsChanged:(NSEvent *)theEvent
+{
+ qt_dispatchModifiersChanged(theEvent, [self QT_MANGLE_NAMESPACE(qt_qwidget)]);
+ [super flagsChanged:theEvent];
+}
+
+
+- (void)tabletProximity:(NSEvent *)tabletEvent
+{
+ qt_dispatchTabletProximityEvent(tabletEvent);
+}
+
+- (void)terminate:(id)sender
+{
+ // This function is called from the quit item in the menubar when this window
+ // is in the first responder chain (see also qtDispatcherToQAction above)
+ [NSApp terminate:sender];
+}
+
+- (void)setLevel:(NSInteger)windowLevel
+{
+ // Cocoa will upon activating/deactivating applications level modal
+ // windows up and down, regardsless of any explicit set window level.
+ // To ensure that modal stays-on-top dialogs actually stays on top after
+ // the application is activated (and therefore stacks in front of
+ // other stays-on-top windows), we need to add this little special-case override:
+ QWidget *widget = [[QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate] qt_qwidgetForWindow:self];
+ if (widget && widget->isModal() && (widget->windowFlags() & Qt::WindowStaysOnTopHint))
+ [super setLevel:NSPopUpMenuWindowLevel];
+ else
+ [super setLevel:windowLevel];
+}
+
+- (void)sendEvent:(NSEvent *)event
+{
+ [self retain];
+
+ bool handled = false;
+ switch([event type]) {
+ case NSMouseMoved:
+ // Cocoa sends move events to a parent and all its children under the mouse, much
+ // like Qt handles hover events. But we only want to handle the move event once, so
+ // to optimize a bit (since we subscribe for move event for all views), we handle it
+ // here before this logic happends. Note: it might be tempting to do this shortcut for
+ // all mouse events. The problem is that Cocoa does more than just find the correct view
+ // when sending the event, like raising windows etc. So avoid it as much as possible:
+ handled = qt_mac_handleMouseEvent(event, QEvent::MouseMove, Qt::NoButton, 0);
+ break;
+ default:
+ break;
+ }
+
+ if (!handled) {
+ [super sendEvent:event];
+ qt_mac_handleNonClientAreaMouseEvent(self, event);
+ }
+ [self release];
+}
+
+- (void)setInitialFirstResponder:(NSView *)view
+{
+ // This method is called the first time the window is placed on screen and
+ // is the earliest point in time we can connect OpenGL contexts to NSViews.
+ QWidget *qwidget = [[QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate] qt_qwidgetForWindow:self];
+ if (qwidget) {
+ qt_event_request_window_change(qwidget);
+ qt_mac_send_posted_gl_updates(qwidget);
+ }
+
+ [super setInitialFirstResponder:view];
+}
+
+- (BOOL)makeFirstResponder:(NSResponder *)responder
+{
+ // For some reason Cocoa wants to flip the first responder
+ // when Qt doesn't want to, sorry, but "No" :-)
+ if (responder == nil && qApp->focusWidget())
+ return NO;
+ return [super makeFirstResponder:responder];
+}
+
++ (Class)frameViewClassForStyleMask:(NSUInteger)styleMask
+{
+ if (styleMask & QtMacCustomizeWindow)
+ return [QT_MANGLE_NAMESPACE(QCocoaWindowCustomThemeFrame) class];
+ return [super frameViewClassForStyleMask:styleMask];
+}
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
+- (void)touchesBeganWithEvent:(NSEvent *)event;
+{
+ QPoint qlocal, qglobal;
+ QWidget *widgetToGetTouch = 0;
+ qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, 0, &widgetToGetTouch);
+ if (!widgetToGetTouch)
+ return;
+
+ bool all = widgetToGetTouch->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents);
+ qt_translateRawTouchEvent(widgetToGetTouch, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all));
+}
+
+- (void)touchesMovedWithEvent:(NSEvent *)event;
+{
+ QPoint qlocal, qglobal;
+ QWidget *widgetToGetTouch = 0;
+ qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, 0, &widgetToGetTouch);
+ if (!widgetToGetTouch)
+ return;
+
+ bool all = widgetToGetTouch->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents);
+ qt_translateRawTouchEvent(widgetToGetTouch, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all));
+}
+
+- (void)touchesEndedWithEvent:(NSEvent *)event;
+{
+ QPoint qlocal, qglobal;
+ QWidget *widgetToGetTouch = 0;
+ qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, 0, &widgetToGetTouch);
+ if (!widgetToGetTouch)
+ return;
+
+ bool all = widgetToGetTouch->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents);
+ qt_translateRawTouchEvent(widgetToGetTouch, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all));
+}
+
+- (void)touchesCancelledWithEvent:(NSEvent *)event;
+{
+ QPoint qlocal, qglobal;
+ QWidget *widgetToGetTouch = 0;
+ qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, 0, &widgetToGetTouch);
+ if (!widgetToGetTouch)
+ return;
+
+ bool all = widgetToGetTouch->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents);
+ qt_translateRawTouchEvent(widgetToGetTouch, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all));
+}
+#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
+
+-(void)registerDragTypes
+{
+ // Calling registerForDraggedTypes below is slow, so only do
+ // it once for each window, or when the custom types change.
+ QMacCocoaAutoReleasePool pool;
+ const QStringList& customTypes = qEnabledDraggedTypes();
+ if (currentCustomDragTypes == 0 || *currentCustomDragTypes != customTypes) {
+ if (currentCustomDragTypes == 0)
+ currentCustomDragTypes = new QStringList();
+ *currentCustomDragTypes = customTypes;
+ const NSString* mimeTypeGeneric = @"com.trolltech.qt.MimeTypeName";
+ NSMutableArray *supportedTypes = [NSMutableArray arrayWithObjects:NSColorPboardType,
+ NSFilenamesPboardType, NSStringPboardType,
+ NSFilenamesPboardType, NSPostScriptPboardType, NSTIFFPboardType,
+ NSRTFPboardType, NSTabularTextPboardType, NSFontPboardType,
+ NSRulerPboardType, NSFileContentsPboardType, NSColorPboardType,
+ NSRTFDPboardType, NSHTMLPboardType, NSPICTPboardType,
+ NSURLPboardType, NSPDFPboardType, NSVCardPboardType,
+ NSFilesPromisePboardType, NSInkTextPboardType,
+ NSMultipleTextSelectionPboardType, mimeTypeGeneric, nil];
+ // Add custom types supported by the application.
+ for (int i = 0; i < customTypes.size(); i++) {
+ [supportedTypes addObject:qt_mac_QStringToNSString(customTypes[i])];
+ }
+ [self registerForDraggedTypes:supportedTypes];
+ }
+}
+
+- (void)removeDropData
+{
+ if (dropData) {
+ delete dropData;
+ dropData = 0;
+ }
+}
+
+- (void)addDropData:(id <NSDraggingInfo>)sender
+{
+ [self removeDropData];
+ CFStringRef dropPasteboard = (CFStringRef) [[sender draggingPasteboard] name];
+ dropData = new QCocoaDropData(dropPasteboard);
+}
+
+- (void)changeDraggingCursor:(NSDragOperation)newOperation
+{
+ static SEL action = nil;
+ static bool operationSupported = false;
+ if (action == nil) {
+ action = NSSelectorFromString(@"operationNotAllowedCursor");
+ if ([NSCursor respondsToSelector:action]) {
+ operationSupported = true;
+ }
+ }
+ if (operationSupported) {
+ NSCursor *notAllowedCursor = [NSCursor performSelector:action];
+ bool isNotAllowedCursor = ([NSCursor currentCursor] == notAllowedCursor);
+ if (newOperation == NSDragOperationNone && !isNotAllowedCursor) {
+ [notAllowedCursor push];
+ } else if (newOperation != NSDragOperationNone && isNotAllowedCursor) {
+ [notAllowedCursor pop];
+ }
+
+ }
+}
+
+- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
+{
+ // The user dragged something into the window. Send a draggingEntered message
+ // to the QWidget under the mouse. As the drag moves over the window, and over
+ // different widgets, we will handle enter and leave events from within
+ // draggingUpdated below. The reason why we handle this ourselves rather than
+ // subscribing for drag events directly in QCocoaView is that calling
+ // registerForDraggedTypes on the views will severly degrade initialization time
+ // for an application that uses a lot of drag subscribing widgets.
+
+ NSPoint nswindowPoint = [sender draggingLocation];
+ NSPoint nsglobalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:nswindowPoint];
+ QPoint globalPoint = flipPoint(nsglobalPoint).toPoint();
+
+ QWidget *qwidget = QApplication::widgetAt(globalPoint);
+ *currentDragTarget() = qwidget;
+ if (!qwidget)
+ return [super draggingEntered:sender];
+ if (qwidget->testAttribute(Qt::WA_DropSiteRegistered) == false)
+ return NSDragOperationNone;
+
+ [self addDropData:sender];
+
+ QMimeData *mimeData = dropData;
+ if (QDragManager::self()->source())
+ mimeData = QDragManager::self()->dragPrivate()->data;
+
+ NSDragOperation nsActions = [sender draggingSourceOperationMask];
+ Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations(nsActions);
+ QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastOperation) = nsActions;
+ Qt::KeyboardModifiers modifiers = Qt::NoModifier;
+
+ if ([sender draggingSource] != nil) {
+ // modifier flags might have changed, update it here since we don't send any input events.
+ QApplicationPrivate::modifier_buttons = qt_cocoaModifiers2QtModifiers([[NSApp currentEvent] modifierFlags]);
+ modifiers = QApplication::keyboardModifiers();
+ } else {
+ // when the source is from another application the above technique will not work.
+ modifiers = qt_cocoaDragOperation2QtModifiers(nsActions);
+ }
+
+ // send the drag enter event to the widget.
+ QPoint localPoint(qwidget->mapFromGlobal(globalPoint));
+ QDragEnterEvent qDEEvent(localPoint, qtAllowed, mimeData, QApplication::mouseButtons(), modifiers);
+ QApplication::sendEvent(qwidget, &qDEEvent);
+
+ if (!qDEEvent.isAccepted()) {
+ // The enter event was not accepted. We mark this by removing
+ // the drop data so we don't send subsequent drag move events:
+ [self removeDropData];
+ [self changeDraggingCursor:NSDragOperationNone];
+ return NSDragOperationNone;
+ } else {
+ // Send a drag move event immediately after a drag enter event (as per documentation).
+ QDragMoveEvent qDMEvent(localPoint, qtAllowed, mimeData, QApplication::mouseButtons(), modifiers);
+ qDMEvent.setDropAction(qDEEvent.dropAction());
+ qDMEvent.accept(); // accept by default, since enter event was accepted.
+ QApplication::sendEvent(qwidget, &qDMEvent);
+
+ if (!qDMEvent.isAccepted() || qDMEvent.dropAction() == Qt::IgnoreAction) {
+ // Since we accepted the drag enter event, the widget expects
+ // future drage move events.
+ nsActions = NSDragOperationNone;
+ // Save as ignored in the answer rect.
+ qDMEvent.setDropAction(Qt::IgnoreAction);
+ } else {
+ nsActions = QT_PREPEND_NAMESPACE(qt_mac_mapDropAction)(qDMEvent.dropAction());
+ }
+
+ QT_PREPEND_NAMESPACE(qt_mac_copy_answer_rect)(qDMEvent);
+ [self changeDraggingCursor:nsActions];
+ return nsActions;
+ }
+ }
+
+- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
+{
+ NSPoint nswindowPoint = [sender draggingLocation];
+ NSPoint nsglobalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:nswindowPoint];
+ QPoint globalPoint = flipPoint(nsglobalPoint).toPoint();
+
+ QWidget *qwidget = QApplication::widgetAt(globalPoint);
+ if (!qwidget)
+ return [super draggingEntered:sender];
+
+ // First, check if the widget under the mouse has changed since the
+ // last drag move events. If so, we need to change target, and dispatch
+ // syntetic drag enter/leave events:
+ if (qwidget != *currentDragTarget()) {
+ if (*currentDragTarget() && dropData) {
+ QDragLeaveEvent de;
+ QApplication::sendEvent(*currentDragTarget(), &de);
+ [self removeDropData];
+ }
+ return [self draggingEntered:sender];
+ }
+
+ if (qwidget->testAttribute(Qt::WA_DropSiteRegistered) == false)
+ return NSDragOperationNone;
+
+ // If we have no drop data (which will be assigned inside draggingEntered), it means
+ // that the current drag target did not accept the enter event. If so, we ignore
+ // subsequent move events as well:
+ if (dropData == 0) {
+ [self changeDraggingCursor:NSDragOperationNone];
+ return NSDragOperationNone;
+ }
+
+ // If the mouse is still within the accepted rect (provided by
+ // the application on a previous event), we follow the optimization
+ // and just return the answer given at that point:
+ NSDragOperation nsActions = [sender draggingSourceOperationMask];
+ QPoint localPoint(qwidget->mapFromGlobal(globalPoint));
+ if (qt_mac_mouse_inside_answer_rect(localPoint)
+ && QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastOperation) == nsActions) {
+ NSDragOperation operation = QT_PREPEND_NAMESPACE(qt_mac_mapDropActions)(QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastAction));
+ [self changeDraggingCursor:operation];
+ return operation;
+ }
+
+ QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastOperation) = nsActions;
+ Qt::DropActions qtAllowed = QT_PREPEND_NAMESPACE(qt_mac_mapNSDragOperations)(nsActions);
+ Qt::KeyboardModifiers modifiers = Qt::NoModifier;
+
+ // Update modifiers:
+ if ([sender draggingSource] != nil) {
+ QApplicationPrivate::modifier_buttons = qt_cocoaModifiers2QtModifiers([[NSApp currentEvent] modifierFlags]);
+ modifiers = QApplication::keyboardModifiers();
+ } else {
+ modifiers = qt_cocoaDragOperation2QtModifiers(nsActions);
+ }
+
+ QMimeData *mimeData = dropData;
+ if (QDragManager::self()->source())
+ mimeData = QDragManager::self()->dragPrivate()->data;
+
+ // Insert the same drop action on the event according to
+ // what the application told us it should be on the previous event:
+ QDragMoveEvent qDMEvent(localPoint, qtAllowed, mimeData, QApplication::mouseButtons(), modifiers);
+ if (QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).lastAction != Qt::IgnoreAction
+ && QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).buttons == qDMEvent.mouseButtons()
+ && QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).modifiers == qDMEvent.keyboardModifiers())
+ qDMEvent.setDropAction(QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).lastAction);
+
+ // Now, end the drag move event to the widget:
+ qDMEvent.accept();
+ QApplication::sendEvent(qwidget, &qDMEvent);
+
+ NSDragOperation operation = qt_mac_mapDropAction(qDMEvent.dropAction());
+ if (!qDMEvent.isAccepted() || qDMEvent.dropAction() == Qt::IgnoreAction) {
+ // Ignore this event (we will still receive further
+ // notifications), save as ignored in the answer rect:
+ operation = NSDragOperationNone;
+ qDMEvent.setDropAction(Qt::IgnoreAction);
+ }
+
+ qt_mac_copy_answer_rect(qDMEvent);
+ [self changeDraggingCursor:operation];
+
+ return operation;
+}
+
+- (void)draggingExited:(id <NSDraggingInfo>)sender
+{
+ NSPoint nswindowPoint = [sender draggingLocation];
+ NSPoint nsglobalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:nswindowPoint];
+ QPoint globalPoint = flipPoint(nsglobalPoint).toPoint();
+
+ QWidget *qwidget = *currentDragTarget();
+ if (!qwidget)
+ return [super draggingExited:sender];
+
+ if (dropData) {
+ QDragLeaveEvent de;
+ QApplication::sendEvent(qwidget, &de);
+ [self removeDropData];
+ }
+
+ // Clean-up:
+ [self removeDropData];
+ *currentDragTarget() = 0;
+ [self changeDraggingCursor:NSDragOperationEvery];
+}
+
+- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
+{
+ QWidget *qwidget = *currentDragTarget();
+ if (!qwidget)
+ return NO;
+
+ *currentDragTarget() = 0;
+ NSPoint nswindowPoint = [sender draggingLocation];
+ NSPoint nsglobalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:nswindowPoint];
+ QPoint globalPoint = flipPoint(nsglobalPoint).toPoint();
+
+ [self addDropData:sender];
+
+ NSDragOperation nsActions = [sender draggingSourceOperationMask];
+ Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations(nsActions);
+ QMimeData *mimeData = dropData;
+
+ if (QDragManager::self()->source())
+ mimeData = QDragManager::self()->dragPrivate()->data;
+ if (QDragManager::self()->object)
+ QDragManager::self()->dragPrivate()->target = qwidget;
+
+ QPoint localPoint(qwidget->mapFromGlobal(globalPoint));
+ QDropEvent de(localPoint, qtAllowed, mimeData,
+ QApplication::mouseButtons(), QApplication::keyboardModifiers());
+ QApplication::sendEvent(qwidget, &de);
+
+ if (QDragManager::self()->object)
+ QDragManager::self()->dragPrivate()->executed_action = de.dropAction();
+
+ return de.isAccepted();
+}
+
+// This is a hack and it should be removed once we find the real cause for
+// the painting problems.
+// We have a static variable that signals if we have been called before or not.
+static bool firstDrawingInvocation = true;
+
+// The method below exists only as a workaround to draw/not draw the baseline
+// in the title bar. This is to support unifiedToolbar look.
+
+// This method is very special. To begin with, it is a
+// method that will get called only if we enable documentMode.
+// Furthermore, it won't get called as a normal method, we swap
+// this method with the normal implementation of drawRect in
+// _NSThemeFrame. When this method is active, its mission is to
+// first call the original drawRect implementation so the widget
+// gets proper painting. After that, it needs to detect if there
+// is a toolbar or not, in order to decide how to handle the unified
+// look. The distinction is important since the presence and
+// visibility of a toolbar change the way we enter into unified mode.
+// When there is a toolbar and that toolbar is visible, the problem
+// is as simple as to tell the toolbar not to draw its baseline.
+// However when there is not toolbar or the toolbar is not visible,
+// we need to draw a line on top of the baseline, because the baseline
+// in that case will belong to the title. For this case we need to draw
+// a line on top of the baseline.
+// As usual, there is a special case. When we first are called, we might
+// need to repaint ourselves one more time. We only need that if we
+// didn't get the activation, i.e. when we are launched via the command
+// line. And this only if the toolbar is visible from the beginning,
+// so we have a special flag that signals if we need to repaint or not.
+- (void)drawRectSpecial:(NSRect)rect
+{
+ // Call the original drawing method.
+ [id(self) drawRectOriginal:rect];
+ NSWindow *window = [id(self) window];
+ NSToolbar *toolbar = [window toolbar];
+ if(!toolbar) {
+ // There is no toolbar, we have to draw a line on top of the line drawn by Cocoa.
+ macDrawRectOnTop((void *)window);
+ } else {
+ if([toolbar isVisible]) {
+ // We tell Cocoa to avoid drawing the line at the end.
+ if(firstDrawingInvocation) {
+ firstDrawingInvocation = false;
+ macSyncDrawingOnFirstInvocation((void *)window);
+ } else
+ [toolbar setShowsBaselineSeparator:NO];
+ } else {
+ // There is a toolbar but it is not visible so
+ // we have to draw a line on top of the line drawn by Cocoa.
+ macDrawRectOnTop((void *)window);
+ }
+ }
+}
+
+- (void)drawRectOriginal:(NSRect)rect
+{
+ Q_UNUSED(rect)
+ // This method implementation is here to silenct the compiler.
+ // See drawRectSpecial for information.
+}
+
diff --git a/src/widgets/platforms/mac/qcocoaview_mac.mm b/src/widgets/platforms/mac/qcocoaview_mac.mm
new file mode 100644
index 0000000000..6c5e05b32a
--- /dev/null
+++ b/src/widgets/platforms/mac/qcocoaview_mac.mm
@@ -0,0 +1,1388 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#import <private/qcocoaview_mac_p.h>
+#ifdef QT_MAC_USE_COCOA
+
+#include <private/qwidget_p.h>
+#include <private/qt_mac_p.h>
+#include <private/qapplication_p.h>
+#include <private/qabstractscrollarea_p.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+#include <private/qdnd_p.h>
+#include <private/qmacinputcontext_p.h>
+#include <private/qevent_p.h>
+#include <private/qbackingstore_p.h>
+#include <private/qwindowsurface_raster_p.h>
+#include <private/qunifiedtoolbarsurface_mac_p.h>
+
+#include <qscrollarea.h>
+#include <qhash.h>
+#include <qtextformat.h>
+#include <qpaintengine.h>
+#include <QUrl>
+#include <QAccessible>
+#include <QFileInfo>
+#include <QFile>
+
+#include <qdebug.h>
+
+@interface NSEvent (Qt_Compile_Leopard_DeviceDelta)
+ - (CGFloat)deviceDeltaX;
+ - (CGFloat)deviceDeltaY;
+ - (CGFloat)deviceDeltaZ;
+@end
+
+@interface NSEvent (Qt_Compile_Leopard_Gestures)
+ - (CGFloat)magnification;
+@end
+
+QT_BEGIN_NAMESPACE
+
+extern void qt_mac_update_cursor(); // qcursor_mac.mm
+extern bool qt_sendSpontaneousEvent(QObject *, QEvent *); // qapplication.cpp
+extern QPointer<QWidget> qt_last_mouse_receiver; // qapplication_mac.cpp
+extern QPointer<QWidget> qt_last_native_mouse_receiver; // qt_cocoa_helpers_mac.mm
+extern OSViewRef qt_mac_nativeview_for(const QWidget *w); // qwidget_mac.mm
+extern OSViewRef qt_mac_effectiveview_for(const QWidget *w); // qwidget_mac.mm
+extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp
+extern Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum);
+extern QWidget *mac_mouse_grabber;
+extern bool qt_mac_clearDirtyOnWidgetInsideDrawWidget; // qwidget.cpp
+
+static QColor colorFrom(NSColor *color)
+{
+ QColor qtColor;
+ NSString *colorSpace = [color colorSpaceName];
+ if (colorSpace == NSDeviceCMYKColorSpace) {
+ CGFloat cyan, magenta, yellow, black, alpha;
+ [color getCyan:&cyan magenta:&magenta yellow:&yellow black:&black alpha:&alpha];
+ qtColor.setCmykF(cyan, magenta, yellow, black, alpha);
+ } else {
+ NSColor *tmpColor;
+ tmpColor = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace];
+ CGFloat red, green, blue, alpha;
+ [tmpColor getRed:&red green:&green blue:&blue alpha:&alpha];
+ qtColor.setRgbF(red, green, blue, alpha);
+ }
+ return qtColor;
+}
+
+QT_END_NAMESPACE
+
+QT_FORWARD_DECLARE_CLASS(QMacCocoaAutoReleasePool)
+QT_FORWARD_DECLARE_CLASS(QCFString)
+QT_FORWARD_DECLARE_CLASS(QDragManager)
+QT_FORWARD_DECLARE_CLASS(QMimeData)
+QT_FORWARD_DECLARE_CLASS(QPoint)
+QT_FORWARD_DECLARE_CLASS(QApplication)
+QT_FORWARD_DECLARE_CLASS(QApplicationPrivate)
+QT_FORWARD_DECLARE_CLASS(QDragEnterEvent)
+QT_FORWARD_DECLARE_CLASS(QDragMoveEvent)
+QT_FORWARD_DECLARE_CLASS(QStringList)
+QT_FORWARD_DECLARE_CLASS(QString)
+QT_FORWARD_DECLARE_CLASS(QRect)
+QT_FORWARD_DECLARE_CLASS(QRegion)
+QT_FORWARD_DECLARE_CLASS(QAbstractScrollArea)
+QT_FORWARD_DECLARE_CLASS(QAbstractScrollAreaPrivate)
+QT_FORWARD_DECLARE_CLASS(QPaintEvent)
+QT_FORWARD_DECLARE_CLASS(QPainter)
+QT_FORWARD_DECLARE_CLASS(QHoverEvent)
+QT_FORWARD_DECLARE_CLASS(QCursor)
+QT_USE_NAMESPACE
+extern "C" {
+ extern NSString *NSTextInputReplacementRangeAttributeName;
+}
+
+//#define ALIEN_DEBUG 1
+#ifdef ALIEN_DEBUG
+static int qCocoaViewCount = 0;
+#endif
+
+@implementation QT_MANGLE_NAMESPACE(QCocoaView)
+
+- (id)initWithQWidget:(QWidget *)widget widgetPrivate:(QWidgetPrivate *)widgetprivate
+{
+ self = [super init];
+ if (self) {
+ [self finishInitWithQWidget:widget widgetPrivate:widgetprivate];
+ }
+ [self setFocusRingType:NSFocusRingTypeNone];
+ composingText = new QString();
+
+#ifdef ALIEN_DEBUG
+ ++qCocoaViewCount;
+ qDebug() << "Alien: create native view for" << widget << ". qCocoaViewCount is:" << qCocoaViewCount;
+#endif
+
+ composing = false;
+ sendKeyEvents = true;
+ fromKeyDownEvent = false;
+ alienTouchCount = 0;
+
+ [self setHidden:YES];
+ return self;
+}
+
+- (void) finishInitWithQWidget:(QWidget *)widget widgetPrivate:(QWidgetPrivate *)widgetprivate
+{
+ qwidget = widget;
+ qwidgetprivate = widgetprivate;
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(frameDidChange:)
+ name:@"NSViewFrameDidChangeNotification"
+ object:self];
+}
+
+- (void)dealloc
+{
+ QMacCocoaAutoReleasePool pool;
+ delete composingText;
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+
+#ifdef ALIEN_DEBUG
+ --qCocoaViewCount;
+ qDebug() << "Alien: widget deallocated. qCocoaViewCount is:" << qCocoaViewCount;
+#endif
+
+ [super dealloc];
+}
+
+- (BOOL)isOpaque
+{
+ if (!qwidgetprivate)
+ return [super isOpaque];
+ return qwidgetprivate->isOpaque;
+}
+
+- (BOOL)isFlipped
+{
+ return YES;
+}
+
+// We preserve the content of the view if WA_StaticContents is defined.
+//
+// More info in the Cocoa documentation:
+// http://developer.apple.com/mac/library/documentation/cocoa/conceptual/CocoaViewsGuide/Optimizing/Optimizing.html
+- (BOOL) preservesContentDuringLiveResize
+{
+ return qwidget->testAttribute(Qt::WA_StaticContents);
+}
+
+- (void) setFrameSize:(NSSize)newSize
+{
+ [super setFrameSize:newSize];
+
+ // A change in size has required the view to be invalidated.
+ if ([self inLiveResize]) {
+ NSRect rects[4];
+ NSInteger count;
+ [self getRectsExposedDuringLiveResize:rects count:&count];
+ while (count-- > 0)
+ {
+ [self setNeedsDisplayInRect:rects[count]];
+ }
+ } else {
+ [self setNeedsDisplay:YES];
+ }
+
+ // Make sure the opengl context is updated on resize.
+ if (qwidgetprivate && qwidgetprivate->isGLWidget && [self window]) {
+ qwidgetprivate->needWindowChange = true;
+ QEvent event(QEvent::MacGLWindowChange);
+ qApp->sendEvent(qwidget, &event);
+ }
+}
+
+// We catch the 'setNeedsDisplay:' message in order to avoid a useless full repaint.
+// During the resize, the top of the widget is repainted, probably because of the
+// change of coordinate space (Quartz vs Qt). This is then followed by this message:
+// -[NSView _setNeedsDisplayIfTopLeftChanged]
+// which force a full repaint by sending the message 'setNeedsDisplay:'.
+// That is what we are preventing here.
+- (void)setNeedsDisplay:(BOOL)flag {
+ if (![self inLiveResize] || !(qwidget->testAttribute(Qt::WA_StaticContents))) {
+ [super setNeedsDisplay:flag];
+ }
+}
+
+- (void)drawRect:(NSRect)aRect
+{
+ if (!qwidget)
+ return;
+
+ // Getting context.
+ CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
+ qt_mac_retain_graphics_context(context);
+
+ // We use a different graphics system.
+ //
+ // Widgets that are set to paint on screen, specifically QGLWidget,
+ // requires the native engine to execute in order to be drawn.
+ if (QApplicationPrivate::graphicsSystem() != 0 && !qwidget->testAttribute(Qt::WA_PaintOnScreen)) {
+
+ // Raster engine.
+ if (QApplicationPrivate::graphics_system_name == QLatin1String("raster")) {
+
+ if (!qwidgetprivate->isInUnifiedToolbar) {
+
+ // Qt handles the painting occuring inside the window.
+ // Cocoa also keeps track of all widgets as NSView and therefore might
+ // ask for a repainting of a widget even if Qt is already taking care of it.
+ //
+ // The only valid reason for Cocoa to call drawRect: is for window manipulation
+ // (ie. resize, ...).
+ //
+ // Qt will then forward the update to the children.
+ if (!qwidget->isWindow()) {
+ qt_mac_release_graphics_context(context);
+ return;
+ }
+
+ QRasterWindowSurface *winSurface = dynamic_cast<QRasterWindowSurface *>(qwidget->windowSurface());
+ if (!winSurface || !winSurface->needsFlush) {
+ qt_mac_release_graphics_context(context);
+ return;
+ }
+
+ // Clip to region.
+ const QVector<QRect> &rects = winSurface->regionToFlush.rects();
+ for (int i = 0; i < rects.size(); ++i) {
+ const QRect &rect = rects.at(i);
+ CGContextAddRect(context, CGRectMake(rect.x(), rect.y(), rect.width(), rect.height()));
+ }
+ CGContextClip(context);
+
+ QRect r = winSurface->regionToFlush.boundingRect();
+ const CGRect area = CGRectMake(r.x(), r.y(), r.width(), r.height());
+
+ qt_mac_draw_image(context, winSurface->imageContext(), area, area);
+
+ winSurface->needsFlush = false;
+ winSurface->regionToFlush = QRegion();
+
+ } else {
+
+ QUnifiedToolbarSurface *unifiedSurface = qwidgetprivate->unifiedSurface;
+ if (!unifiedSurface) {
+ qt_mac_release_graphics_context(context);
+ return;
+ }
+
+ int areaX = qwidgetprivate->toolbar_offset.x();
+ int areaY = qwidgetprivate->toolbar_offset.y();
+ int areaWidth = qwidget->geometry().width();
+ int areaHeight = qwidget->geometry().height();
+ const CGRect area = CGRectMake(areaX, areaY, areaWidth, areaHeight);
+ const CGRect drawingArea = CGRectMake(0, 0, areaWidth, areaHeight);
+
+ qt_mac_draw_image(context, unifiedSurface->imageContext(), area, drawingArea);
+
+ qwidgetprivate->flushRequested = false;
+
+ }
+
+ CGContextFlush(context);
+ qt_mac_release_graphics_context(context);
+ return;
+ }
+
+ // Qt handles the painting occuring inside the window.
+ // Cocoa also keeps track of all widgets as NSView and therefore might
+ // ask for a repainting of a widget even if Qt is already taking care of it.
+ //
+ // The only valid reason for Cocoa to call drawRect: is for window manipulation
+ // (ie. resize, ...).
+ //
+ // Qt will then forward the update to the children.
+ if (qwidget->isWindow()) {
+ qwidgetprivate->syncBackingStore(qwidget->rect());
+ }
+ }
+
+ // Native engine.
+ qwidgetprivate->hd = context;
+
+ if (qwidget->isVisible() && qwidget->updatesEnabled()) { //process the actual paint event.
+ if (qwidget->testAttribute(Qt::WA_WState_InPaintEvent))
+ qWarning("QWidget::repaint: Recursive repaint detected");
+
+ const QRect qrect = QRect(aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height);
+ QRegion qrgn;
+
+ const NSRect *rects;
+ NSInteger count;
+ [self getRectsBeingDrawn:&rects count:&count];
+ for (int i = 0; i < count; ++i) {
+ QRect tmpRect = QRect(rects[i].origin.x, rects[i].origin.y, rects[i].size.width, rects[i].size.height);
+ qrgn += tmpRect;
+ }
+
+ if (!qwidget->isWindow() && !qobject_cast<QAbstractScrollArea *>(qwidget->parent())) {
+ const QRegion &parentMask = qwidget->window()->mask();
+ if (!parentMask.isEmpty()) {
+ const QPoint mappedPoint = qwidget->mapTo(qwidget->window(), qrect.topLeft());
+ qrgn.translate(mappedPoint);
+ qrgn &= parentMask;
+ qrgn.translate(-mappedPoint.x(), -mappedPoint.y());
+ }
+ }
+
+ QPoint redirectionOffset(0, 0);
+ //setup the context
+ qwidget->setAttribute(Qt::WA_WState_InPaintEvent);
+ QPaintEngine *engine = qwidget->paintEngine();
+ if (engine)
+ engine->setSystemClip(qrgn);
+ if (qwidgetprivate->extra && qwidgetprivate->extra->hasMask) {
+ CGRect widgetRect = CGRectMake(0, 0, qwidget->width(), qwidget->height());
+ CGContextTranslateCTM (context, 0, widgetRect.size.height);
+ CGContextScaleCTM(context, 1, -1);
+ if (qwidget->isWindow())
+ CGContextClearRect(context, widgetRect);
+ CGContextClipToMask(context, widgetRect, qwidgetprivate->extra->imageMask);
+ CGContextScaleCTM(context, 1, -1);
+ CGContextTranslateCTM (context, 0, -widgetRect.size.height);
+ }
+
+ if (qwidget->isWindow() && !qwidgetprivate->isOpaque
+ && !qwidget->testAttribute(Qt::WA_MacBrushedMetal)) {
+ CGContextClearRect(context, NSRectToCGRect(aRect));
+ }
+
+ qwidget->setAttribute(Qt::WA_WState_InPaintEvent, false);
+ QWidgetPrivate *qwidgetPrivate = qt_widget_private(qwidget);
+
+ // We specify that we want to draw the widget itself, and
+ // all its children recursive. But we skip native children, because
+ // they will receive drawRect calls by themselves as needed:
+ int flags = QWidgetPrivate::DrawPaintOnScreen
+ | QWidgetPrivate::DrawRecursive
+ | QWidgetPrivate::DontDrawNativeChildren;
+
+ if (qwidget->isWindow())
+ flags |= QWidgetPrivate::DrawAsRoot;
+
+ // Start to draw:
+ qt_mac_clearDirtyOnWidgetInsideDrawWidget = true;
+ qwidgetPrivate->drawWidget(qwidget, qrgn, QPoint(), flags, 0);
+ qt_mac_clearDirtyOnWidgetInsideDrawWidget = false;
+
+ if (!redirectionOffset.isNull())
+ QPainter::restoreRedirected(qwidget);
+ if (engine)
+ engine->setSystemClip(QRegion());
+ qwidget->setAttribute(Qt::WA_WState_InPaintEvent, false);
+ if(!qwidget->testAttribute(Qt::WA_PaintOutsidePaintEvent) && qwidget->paintingActive())
+ qWarning("QWidget: It is dangerous to leave painters active on a"
+ " widget outside of the PaintEvent");
+ }
+ qwidgetprivate->hd = 0;
+ qt_mac_release_graphics_context(context);
+}
+
+- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
+{
+ // Find the widget that should receive the event:
+ QPoint qlocal, qglobal;
+ QWidget *widgetToGetMouse = qt_mac_getTargetForMouseEvent(theEvent, QEvent::MouseButtonPress, qlocal, qglobal, qwidget, 0);
+ if (!widgetToGetMouse)
+ return NO;
+
+ return !widgetToGetMouse->testAttribute(Qt::WA_MacNoClickThrough);
+}
+
+- (NSView *)hitTest:(NSPoint)aPoint
+{
+ if (!qwidget)
+ return [super hitTest:aPoint];
+
+ if (qwidget->testAttribute(Qt::WA_TransparentForMouseEvents))
+ return nil; // You cannot hit a transparent for mouse event widget.
+ return [super hitTest:aPoint];
+}
+
+- (void)updateTrackingAreas
+{
+ if (!qwidget)
+ return;
+
+ // [NSView addTrackingArea] is slow, so bail out early if we can:
+ if (NSIsEmptyRect([self visibleRect]))
+ return;
+
+ QMacCocoaAutoReleasePool pool;
+ if (NSArray *trackingArray = [self trackingAreas]) {
+ NSUInteger size = [trackingArray count];
+ for (NSUInteger i = 0; i < size; ++i) {
+ NSTrackingArea *t = [trackingArray objectAtIndex:i];
+ [self removeTrackingArea:t];
+ }
+ }
+
+ // Ideally, we shouldn't have NSTrackingMouseMoved events included below, it should
+ // only be turned on if mouseTracking, hover is on or a tool tip is set.
+ // Unfortunately, Qt will send "tooltip" events on mouse moves, so we need to
+ // turn it on in ALL case. That means EVERY QCocoaView gets to pay the cost of
+ // mouse moves delivered to it (Apple recommends keeping it OFF because there
+ // is a performance hit). So it goes.
+ NSUInteger trackingOptions = NSTrackingMouseEnteredAndExited | NSTrackingActiveInActiveApp
+ | NSTrackingInVisibleRect | NSTrackingMouseMoved;
+ NSTrackingArea *ta = [[NSTrackingArea alloc] initWithRect:NSMakeRect(0, 0,
+ qwidget->width(),
+ qwidget->height())
+ options:trackingOptions
+ owner:self
+ userInfo:nil];
+ [self addTrackingArea:ta];
+ [ta release];
+}
+
+- (void)mouseEntered:(NSEvent *)event
+{
+ // Cocoa will not send a move event on mouseEnter. But since
+ // Qt expect this, we fake one now. See also mouseExited below
+ // for info about enter/leave event handling
+ NSEvent *nsmoveEvent = [NSEvent
+ mouseEventWithType:NSMouseMoved
+ location:[[self window] mouseLocationOutsideOfEventStream]
+ modifierFlags: [event modifierFlags]
+ timestamp: [event timestamp]
+ windowNumber: [event windowNumber]
+ context: [event context]
+ eventNumber: [event eventNumber]
+ clickCount: 0
+ pressure: 0];
+
+ // Important: Cocoa sends us mouseEnter on all views under the mouse
+ // and not just the one on top. Therefore, to we cannot use qwidget
+ // as native widget for this case. Instead, we let qt_mac_handleMouseEvent
+ // resolve it (last argument set to 0):
+ qt_mac_handleMouseEvent(nsmoveEvent, QEvent::MouseMove, Qt::NoButton, 0);
+}
+
+- (void)mouseExited:(NSEvent *)event
+{
+ // Note: normal enter/leave handling is done from within mouseMove. This handler
+ // catches the case when the mouse moves out of the window (which mouseMove do not).
+ // Updating the mouse cursor follows the same logic as enter/leave. And we update
+ // neither if a grab exists (even if the grab points to this widget, it seems, ref X11)
+ Q_UNUSED(event);
+ if (self == [[self window] contentView] && !qt_button_down && !QWidget::mouseGrabber()) {
+ qt_mac_update_cursor();
+ // If the mouse exits the content view, but qt_mac_getTargetForMouseEvent still
+ // reports a target, it means that either there is a grab involved, or the mouse
+ // hovered over another window in the application. In both cases, move events will
+ // cause qt_mac_handleMouseEvent to be called, which will handle enter/leave.
+ QPoint qlocal, qglobal;
+ QWidget *widgetUnderMouse = 0;
+ qt_mac_getTargetForMouseEvent(event, QEvent::Leave, qlocal, qglobal, qwidget, &widgetUnderMouse);
+
+ if (widgetUnderMouse == 0) {
+ QApplicationPrivate::dispatchEnterLeave(0, qt_last_mouse_receiver);
+ qt_last_mouse_receiver = 0;
+ qt_last_native_mouse_receiver = 0;
+ }
+ }
+}
+
+- (void)flagsChanged:(NSEvent *)theEvent
+{
+ QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget);
+ if (!widgetToGetKey)
+ return;
+
+ qt_dispatchModifiersChanged(theEvent, widgetToGetKey);
+ [super flagsChanged:theEvent];
+}
+
+- (void)mouseMoved:(NSEvent *)theEvent
+{
+ // Important: this method will only be called when the view's window is _not_ inside
+ // QCocoaWindow/QCocoaPanel. Otherwise, [QCocoaWindow sendEvent] will handle the event
+ // before it ends up here. So, this method is added for supporting QMacNativeWidget.
+ // TODO: Cocoa send move events to all views under the mouse. So make sure we only
+ // handle the event for the widget on top when using QMacNativeWidget.
+ qt_mac_handleMouseEvent(theEvent, QEvent::MouseMove, Qt::NoButton, qwidget);
+}
+
+- (void)mouseDown:(NSEvent *)theEvent
+{
+ qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonPress, Qt::LeftButton, qwidget);
+ // Don't call super here. This prevents us from getting the mouseUp event,
+ // which we need to send even if the mouseDown event was not accepted.
+ // (this is standard Qt behavior.)
+}
+
+- (void)mouseUp:(NSEvent *)theEvent
+{
+ qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonRelease, Qt::LeftButton, qwidget);
+}
+
+- (void)rightMouseDown:(NSEvent *)theEvent
+{
+ qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonPress, Qt::RightButton, qwidget);
+}
+
+- (void)rightMouseUp:(NSEvent *)theEvent
+{
+ qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonRelease, Qt::RightButton, qwidget);
+}
+
+- (void)otherMouseDown:(NSEvent *)theEvent
+{
+ Qt::MouseButton mouseButton = cocoaButton2QtButton([theEvent buttonNumber]);
+ qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonPress, mouseButton, qwidget);
+}
+
+- (void)otherMouseUp:(NSEvent *)theEvent
+{
+ Qt::MouseButton mouseButton = cocoaButton2QtButton([theEvent buttonNumber]);
+ qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonRelease, mouseButton, qwidget);
+}
+
+- (void)mouseDragged:(NSEvent *)theEvent
+{
+ qt_mac_handleMouseEvent(theEvent, QEvent::MouseMove, Qt::NoButton, qwidget);
+}
+
+- (void)rightMouseDragged:(NSEvent *)theEvent
+{
+ qt_mac_handleMouseEvent(theEvent, QEvent::MouseMove, Qt::NoButton, qwidget);
+}
+
+- (void)otherMouseDragged:(NSEvent *)theEvent
+{
+ qt_mac_handleMouseEvent(theEvent, QEvent::MouseMove, Qt::NoButton, qwidget);
+}
+
+- (void)scrollWheel:(NSEvent *)theEvent
+{
+ // Give the Input Manager a chance to process the wheel event.
+ NSInputManager *currentIManager = [NSInputManager currentInputManager];
+ if (currentIManager && [currentIManager wantsToHandleMouseEvents]) {
+ [currentIManager handleMouseEvent:theEvent];
+ }
+
+ Qt::MouseButtons buttons = QApplication::mouseButtons();
+ Qt::KeyboardModifiers keyMods = qt_cocoaModifiers2QtModifiers([theEvent modifierFlags]);
+
+ // Find the widget that should receive the event:
+ QPoint qlocal, qglobal;
+ QWidget *widgetToGetMouse = qt_mac_getTargetForMouseEvent(theEvent, QEvent::Wheel, qlocal, qglobal, qwidget, 0);
+ if (!widgetToGetMouse)
+ return;
+
+ int deltaX = 0;
+ int deltaY = 0;
+ int deltaZ = 0;
+
+ const EventRef carbonEvent = (EventRef)[theEvent eventRef];
+ const UInt32 carbonEventKind = carbonEvent ? ::GetEventKind(carbonEvent) : 0;
+ const bool scrollEvent = carbonEventKind == kEventMouseScroll;
+
+ if (scrollEvent) {
+ // The mouse device containts pixel scroll wheel support (Mighty Mouse, Trackpad).
+ // Since deviceDelta is delivered as pixels rather than degrees, we need to
+ // convert from pixels to degrees in a sensible manner.
+ // It looks like 1/4 degrees per pixel behaves most native.
+ // (NB: Qt expects the unit for delta to be 8 per degree):
+ const int pixelsToDegrees = 2; // 8 * 1/4
+ deltaX = [theEvent deviceDeltaX] * pixelsToDegrees;
+ deltaY = [theEvent deviceDeltaY] * pixelsToDegrees;
+ deltaZ = [theEvent deviceDeltaZ] * pixelsToDegrees;
+ } else {
+ // carbonEventKind == kEventMouseWheelMoved
+ // Remove acceleration, and use either -120 or 120 as delta:
+ deltaX = qBound(-120, int([theEvent deltaX] * 10000), 120);
+ deltaY = qBound(-120, int([theEvent deltaY] * 10000), 120);
+ deltaZ = qBound(-120, int([theEvent deltaZ] * 10000), 120);
+ }
+
+#ifndef QT_NO_WHEELEVENT
+ // ### Qt 5: Send one QWheelEvent with dx, dy and dz
+
+ if (deltaX != 0 && deltaY != 0)
+ QMacScrollOptimization::initDelayedScroll();
+
+ if (deltaX != 0) {
+ QWheelEvent qwe(qlocal, qglobal, deltaX, buttons, keyMods, Qt::Horizontal);
+ qt_sendSpontaneousEvent(widgetToGetMouse, &qwe);
+ }
+
+ if (deltaY != 0) {
+ QWheelEvent qwe(qlocal, qglobal, deltaY, buttons, keyMods, Qt::Vertical);
+ qt_sendSpontaneousEvent(widgetToGetMouse, &qwe);
+ }
+
+ if (deltaZ != 0) {
+ // Qt doesn't explicitly support wheels with a Z component. In a misguided attempt to
+ // try to be ahead of the pack, I'm adding this extra value.
+ QWheelEvent qwe(qlocal, qglobal, deltaZ, buttons, keyMods, (Qt::Orientation)3);
+ qt_sendSpontaneousEvent(widgetToGetMouse, &qwe);
+ }
+
+ if (deltaX != 0 && deltaY != 0)
+ QMacScrollOptimization::performDelayedScroll();
+#endif //QT_NO_WHEELEVENT
+}
+
+- (void)tabletProximity:(NSEvent *)tabletEvent
+{
+ qt_dispatchTabletProximityEvent(tabletEvent);
+}
+
+- (void)tabletPoint:(NSEvent *)tabletEvent
+{
+ if (!qt_mac_handleTabletEvent(self, tabletEvent))
+ [super tabletPoint:tabletEvent];
+}
+
+- (void)magnifyWithEvent:(NSEvent *)event
+{
+ QPoint qlocal, qglobal;
+ QWidget *widgetToGetGesture = 0;
+ qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, qwidget, &widgetToGetGesture);
+ if (!widgetToGetGesture)
+ return;
+ if (!QApplicationPrivate::tryModalHelper(widgetToGetGesture, 0))
+ return;
+
+#ifndef QT_NO_GESTURES
+ QNativeGestureEvent qNGEvent;
+ qNGEvent.gestureType = QNativeGestureEvent::Zoom;
+ NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]];
+ qNGEvent.position = flipPoint(p).toPoint();
+ qNGEvent.percentage = [event magnification];
+ qt_sendSpontaneousEvent(widgetToGetGesture, &qNGEvent);
+#endif // QT_NO_GESTURES
+}
+
+- (void)rotateWithEvent:(NSEvent *)event
+{
+ QPoint qlocal, qglobal;
+ QWidget *widgetToGetGesture = 0;
+ qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, qwidget, &widgetToGetGesture);
+ if (!widgetToGetGesture)
+ return;
+ if (!QApplicationPrivate::tryModalHelper(widgetToGetGesture, 0))
+ return;
+
+#ifndef QT_NO_GESTURES
+ QNativeGestureEvent qNGEvent;
+ qNGEvent.gestureType = QNativeGestureEvent::Rotate;
+ NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]];
+ qNGEvent.position = flipPoint(p).toPoint();
+ qNGEvent.percentage = -[event rotation];
+ qt_sendSpontaneousEvent(widgetToGetGesture, &qNGEvent);
+#endif // QT_NO_GESTURES
+}
+
+- (void)swipeWithEvent:(NSEvent *)event
+{
+ QPoint qlocal, qglobal;
+ QWidget *widgetToGetGesture = 0;
+ qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, qwidget, &widgetToGetGesture);
+ if (!widgetToGetGesture)
+ return;
+ if (!QApplicationPrivate::tryModalHelper(widgetToGetGesture, 0))
+ return;
+
+#ifndef QT_NO_GESTURES
+ QNativeGestureEvent qNGEvent;
+ qNGEvent.gestureType = QNativeGestureEvent::Swipe;
+ NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]];
+ qNGEvent.position = flipPoint(p).toPoint();
+ if ([event deltaX] == 1)
+ qNGEvent.angle = 180.0f;
+ else if ([event deltaX] == -1)
+ qNGEvent.angle = 0.0f;
+ else if ([event deltaY] == 1)
+ qNGEvent.angle = 90.0f;
+ else if ([event deltaY] == -1)
+ qNGEvent.angle = 270.0f;
+ qt_sendSpontaneousEvent(widgetToGetGesture, &qNGEvent);
+#endif // QT_NO_GESTURES
+}
+
+- (void)beginGestureWithEvent:(NSEvent *)event
+{
+ QPoint qlocal, qglobal;
+ QWidget *widgetToGetGesture = 0;
+ qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, qwidget, &widgetToGetGesture);
+ if (!widgetToGetGesture)
+ return;
+ if (!QApplicationPrivate::tryModalHelper(widgetToGetGesture, 0))
+ return;
+
+#ifndef QT_NO_GESTURES
+ QNativeGestureEvent qNGEvent;
+ qNGEvent.gestureType = QNativeGestureEvent::GestureBegin;
+ NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]];
+ qNGEvent.position = flipPoint(p).toPoint();
+ qt_sendSpontaneousEvent(widgetToGetGesture, &qNGEvent);
+#endif // QT_NO_GESTURES
+}
+
+- (void)endGestureWithEvent:(NSEvent *)event
+{
+ QPoint qlocal, qglobal;
+ QWidget *widgetToGetGesture = 0;
+ qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, qwidget, &widgetToGetGesture);
+ if (!widgetToGetGesture)
+ return;
+ if (!QApplicationPrivate::tryModalHelper(widgetToGetGesture, 0))
+ return;
+
+#ifndef QT_NO_GESTURES
+ QNativeGestureEvent qNGEvent;
+ qNGEvent.gestureType = QNativeGestureEvent::GestureEnd;
+ NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]];
+ qNGEvent.position = flipPoint(p).toPoint();
+ qt_sendSpontaneousEvent(widgetToGetGesture, &qNGEvent);
+}
+#endif // QT_NO_GESTURES
+
+- (void)frameDidChange:(NSNotification *)note
+{
+ Q_UNUSED(note);
+ if (!qwidget)
+ return;
+ if (qwidget->isWindow())
+ return;
+ NSRect newFrame = [self frame];
+ QRect newGeo(newFrame.origin.x, newFrame.origin.y, newFrame.size.width, newFrame.size.height);
+ bool moved = qwidget->testAttribute(Qt::WA_Moved);
+ bool resized = qwidget->testAttribute(Qt::WA_Resized);
+ qwidget->setGeometry(newGeo);
+ qwidget->setAttribute(Qt::WA_Moved, moved);
+ qwidget->setAttribute(Qt::WA_Resized, resized);
+ qwidgetprivate->syncCocoaMask();
+}
+
+- (BOOL)isEnabled
+{
+ if (!qwidget)
+ return [super isEnabled];
+ return [super isEnabled] && qwidget->isEnabled();
+}
+
+- (void)setEnabled:(BOOL)flag
+{
+ QMacCocoaAutoReleasePool pool;
+ [super setEnabled:flag];
+ if (qwidget && qwidget->isEnabled() != flag)
+ qwidget->setEnabled(flag);
+}
+
++ (Class)cellClass
+{
+ return [NSActionCell class];
+}
+
+- (BOOL)acceptsFirstResponder
+{
+ if (!qwidget)
+ return NO;
+
+ // Disabled widget shouldn't get focus even if it's a window.
+ // hence disabled windows will not get any key or mouse events.
+ if (!qwidget->isEnabled())
+ return NO;
+
+ if (qwidget->isWindow() && !qt_widget_private(qwidget)->topData()->embedded) {
+ QWidget *focusWidget = qApp->focusWidget();
+ if (!focusWidget) {
+ // There is no focus widget, but we still want to receive key events
+ // for shortcut handling etc. So we accept first responer for the
+ // content view as a last resort:
+ return YES;
+ }
+ if (!focusWidget->internalWinId() && focusWidget->nativeParentWidget() == qwidget) {
+ // The current focus widget is alien, and hence, cannot get acceptsFirstResponder
+ // calls. Since the focus widget is a child of qwidget, we let this view say YES:
+ return YES;
+ }
+ if (focusWidget->window() != qwidget) {
+ // The current focus widget is in another window. Since cocoa
+ // suggest that this window should be key now, we accept:
+ return YES;
+ }
+ }
+
+ return qwidget->focusPolicy() != Qt::NoFocus;
+}
+
+- (BOOL)resignFirstResponder
+{
+ if (!qwidget)
+ return YES;
+
+ // Seems like the following test only triggers if this
+ // view is inside a QMacNativeWidget:
+// if (QWidget *fw = QApplication::focusWidget()) {
+// if (qwidget == fw || qwidget == fw->nativeParentWidget())
+// fw->clearFocus();
+// }
+ return YES;
+}
+
+- (BOOL)becomeFirstResponder
+{
+ // see the comment in the acceptsFirstResponder - if the window "stole" focus
+ // let it become the responder, but don't tell Qt
+ if (qwidget && qt_widget_private(qwidget->window())->topData()->embedded
+ && !QApplication::focusWidget() && qwidget->focusPolicy() != Qt::NoFocus)
+ qwidget->setFocus(Qt::OtherFocusReason);
+ return YES;
+}
+
+- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
+{
+ Q_UNUSED(isLocal);
+ return supportedActions;
+}
+
+- (void)setSupportedActions:(NSDragOperation)actions
+{
+ supportedActions = actions;
+}
+
+- (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
+{
+ Q_UNUSED(anImage);
+ Q_UNUSED(aPoint);
+ macCurrentDnDParameters()->performedAction = operation;
+ if (QDragManager::self()->object
+ && QDragManager::self()->dragPrivate()->executed_action != Qt::ActionMask) {
+ macCurrentDnDParameters()->performedAction =
+ qt_mac_mapDropAction(QDragManager::self()->dragPrivate()->executed_action);
+ }
+}
+
+- (QWidget *)qt_qwidget
+{
+ return qwidget;
+}
+
+- (void) qt_clearQWidget
+{
+ qwidget = 0;
+ qwidgetprivate = 0;
+}
+
+- (void)keyDown:(NSEvent *)theEvent
+{
+ if (!qwidget)
+ return;
+ QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget);
+ if (!widgetToGetKey)
+ return;
+
+ sendKeyEvents = true;
+
+ if (widgetToGetKey->testAttribute(Qt::WA_InputMethodEnabled)
+ && !(widgetToGetKey->inputMethodHints() & Qt::ImhDigitsOnly
+ || widgetToGetKey->inputMethodHints() & Qt::ImhFormattedNumbersOnly
+ || widgetToGetKey->inputMethodHints() & Qt::ImhHiddenText)) {
+ fromKeyDownEvent = true;
+ [qt_mac_nativeview_for(qwidget) interpretKeyEvents:[NSArray arrayWithObject: theEvent]];
+ fromKeyDownEvent = false;
+ }
+
+ if (sendKeyEvents && !composing) {
+ bool keyEventEaten = qt_dispatchKeyEvent(theEvent, widgetToGetKey);
+ if (!keyEventEaten && qwidget) {
+ // The event is not yet eaten, and if Qt is embedded inside a native
+ // cocoa application, send it to first responder not owned by Qt.
+ // The exception is if widgetToGetKey was redirected to a popup.
+ QWidget *toplevel = qwidget->window();
+ if (toplevel == widgetToGetKey->window()) {
+ if (qt_widget_private(toplevel)->topData()->embedded) {
+ if (NSResponder *w = [qt_mac_nativeview_for(toplevel) superview])
+ [w keyDown:theEvent];
+ }
+ }
+ }
+ }
+}
+
+
+- (void)keyUp:(NSEvent *)theEvent
+{
+ if (sendKeyEvents) {
+ QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget);
+ if (!widgetToGetKey)
+ return;
+
+ bool keyEventEaten = qt_dispatchKeyEvent(theEvent, widgetToGetKey);
+ if (!keyEventEaten && qwidget) {
+ // The event is not yet eaten, and if Qt is embedded inside a native
+ // cocoa application, send it to first responder not owned by Qt.
+ // The exception is if widgetToGetKey was redirected to a popup.
+ QWidget *toplevel = qwidget->window();
+ if (toplevel == widgetToGetKey->window()) {
+ if (qt_widget_private(toplevel)->topData()->embedded) {
+ if (NSResponder *w = [qt_mac_nativeview_for(toplevel) superview])
+ [w keyUp:theEvent];
+ }
+ }
+ }
+ }
+}
+
+- (void)viewWillMoveToWindow:(NSWindow *)window
+{
+ if (qwidget == 0)
+ return;
+
+ if (qwidget->windowFlags() & Qt::MSWindowsOwnDC
+ && (window != [self window])) { // OpenGL Widget
+ QEvent event(QEvent::MacGLClearDrawable);
+ qApp->sendEvent(qwidget, &event);
+ }
+}
+
+- (void)viewDidMoveToWindow
+{
+ if (qwidget == 0)
+ return;
+
+ if (qwidget->windowFlags() & Qt::MSWindowsOwnDC && [self window]) {
+ // call update paint event
+ qwidgetprivate->needWindowChange = true;
+ QEvent event(QEvent::MacGLWindowChange);
+ qApp->sendEvent(qwidget, &event);
+ }
+}
+
+
+// NSTextInput Protocol implementation
+
+- (void) insertText:(id)aString
+{
+ QString commitText;
+ if ([aString length]) {
+ if ([aString isKindOfClass:[NSAttributedString class]]) {
+ commitText = QCFString::toQString(reinterpret_cast<CFStringRef>([aString string]));
+ } else {
+ commitText = QCFString::toQString(reinterpret_cast<CFStringRef>(aString));
+ };
+ }
+
+ // When entering characters through Character Viewer or Keyboard Viewer, the text is passed
+ // through this insertText method. Since we dont receive a keyDown Event in such cases, the
+ // composing flag will be false.
+ if (([aString length] && composing) || !fromKeyDownEvent) {
+ // Send the commit string to the widget.
+ composing = false;
+ sendKeyEvents = false;
+ QInputMethodEvent e;
+ e.setCommitString(commitText);
+ if (QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget))
+ qt_sendSpontaneousEvent(widgetToGetKey, &e);
+ } else {
+ // The key sequence "`q" on a French Keyboard will generate two calls to insertText before
+ // it returns from interpretKeyEvents. The first call will turn off 'composing' and accept
+ // the "`" key. The last keyDown event needs to be processed by the widget to get the
+ // character "q". The string parameter is ignored for the second call.
+ sendKeyEvents = true;
+ }
+
+ composingText->clear();
+}
+
+- (void) setMarkedText:(id)aString selectedRange:(NSRange)selRange
+{
+ // Generate the QInputMethodEvent with preedit string and the attributes
+ // for rendering it. The attributes handled here are 'underline',
+ // 'underline color' and 'cursor position'.
+ sendKeyEvents = false;
+ composing = true;
+ QString qtText;
+ // Cursor position is retrived from the range.
+ QList<QInputMethodEvent::Attribute> attrs;
+ attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, selRange.location + selRange.length, 1, QVariant());
+ if ([aString isKindOfClass:[NSAttributedString class]]) {
+ qtText = QCFString::toQString(reinterpret_cast<CFStringRef>([aString string]));
+ composingLength = qtText.length();
+ int index = 0;
+ // Create attributes for individual sections of preedit text
+ while (index < composingLength) {
+ NSRange effectiveRange;
+ NSRange range = NSMakeRange(index, composingLength-index);
+ NSDictionary *attributes = [aString attributesAtIndex:index
+ longestEffectiveRange:&effectiveRange
+ inRange:range];
+ NSNumber *underlineStyle = [attributes objectForKey:NSUnderlineStyleAttributeName];
+ if (underlineStyle) {
+ QColor clr (Qt::black);
+ NSColor *color = [attributes objectForKey:NSUnderlineColorAttributeName];
+ if (color) {
+ clr = colorFrom(color);
+ }
+ QTextCharFormat format;
+ format.setFontUnderline(true);
+ format.setUnderlineColor(clr);
+ attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
+ effectiveRange.location,
+ effectiveRange.length,
+ format);
+ }
+ index = effectiveRange.location + effectiveRange.length;
+ }
+ } else {
+ // No attributes specified, take only the preedit text.
+ qtText = QCFString::toQString(reinterpret_cast<CFStringRef>(aString));
+ composingLength = qtText.length();
+ }
+ // Make sure that we have at least one text format.
+ if (attrs.size() <= 1) {
+ QTextCharFormat format;
+ format.setFontUnderline(true);
+ attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
+ 0, composingLength, format);
+ }
+ *composingText = qtText;
+
+ QInputMethodEvent e(qtText, attrs);
+ if (QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget))
+ qt_sendSpontaneousEvent(widgetToGetKey, &e);
+
+ if (!composingLength)
+ composing = false;
+}
+
+- (void) unmarkText
+{
+ if (composing) {
+ QInputMethodEvent e;
+ e.setCommitString(*composingText);
+ if (QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget))
+ qt_sendSpontaneousEvent(widgetToGetKey, &e);
+ }
+ composingText->clear();
+ composing = false;
+}
+
+- (BOOL) hasMarkedText
+{
+ return (composing ? YES: NO);
+}
+
+- (void) doCommandBySelector:(SEL)aSelector
+{
+ Q_UNUSED(aSelector);
+}
+
+- (BOOL)isComposing
+{
+ return composing;
+}
+
+- (NSInteger) conversationIdentifier
+{
+ // Return a unique identifier fot this ime conversation
+ return (NSInteger)self;
+}
+
+- (NSAttributedString *) attributedSubstringFromRange:(NSRange)theRange
+{
+ QString selectedText(qwidget->inputMethodQuery(Qt::ImCurrentSelection).toString());
+ if (!selectedText.isEmpty()) {
+ QCFString string(selectedText.mid(theRange.location, theRange.length));
+ const NSString *tmpString = reinterpret_cast<const NSString *>((CFStringRef)string);
+ return [[[NSAttributedString alloc] initWithString:const_cast<NSString *>(tmpString)] autorelease];
+ } else {
+ return nil;
+ }
+}
+
+- (NSRange) markedRange
+{
+ NSRange range;
+ if (composing) {
+ range.location = 0;
+ range.length = composingLength;
+ } else {
+ range.location = NSNotFound;
+ range.length = 0;
+ }
+ return range;
+}
+
+- (NSRange) selectedRange
+{
+ NSRange selRange;
+ QString selectedText(qwidget->inputMethodQuery(Qt::ImCurrentSelection).toString());
+ if (!selectedText.isEmpty()) {
+ // Consider only the selected text.
+ selRange.location = 0;
+ selRange.length = selectedText.length();
+ } else {
+ // No selected text.
+ selRange.location = NSNotFound;
+ selRange.length = 0;
+ }
+ return selRange;
+
+}
+
+- (NSRect) firstRectForCharacterRange:(NSRange)theRange
+{
+ Q_UNUSED(theRange);
+ // The returned rect is always based on the internal cursor.
+ QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget);
+ if (!widgetToGetKey)
+ return NSZeroRect;
+
+ QRect mr(widgetToGetKey->inputMethodQuery(Qt::ImMicroFocus).toRect());
+ QPoint mp(widgetToGetKey->mapToGlobal(QPoint(mr.bottomLeft())));
+ NSRect rect ;
+ rect.origin.x = mp.x();
+ rect.origin.y = flipYCoordinate(mp.y());
+ rect.size.width = mr.width();
+ rect.size.height = mr.height();
+ return rect;
+}
+
+- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
+{
+ // We dont support cursor movements using mouse while composing.
+ Q_UNUSED(thePoint);
+ return NSNotFound;
+}
+
+- (NSArray*) validAttributesForMarkedText
+{
+ QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget);
+ if (!widgetToGetKey)
+ return nil;
+
+ if (!widgetToGetKey->testAttribute(Qt::WA_InputMethodEnabled))
+ return nil; // Not sure if that's correct, but it's saves a malloc.
+
+ // Support only underline color/style.
+ return [NSArray arrayWithObjects:NSUnderlineColorAttributeName,
+ NSUnderlineStyleAttributeName, nil];
+}
+@end
+
+QT_BEGIN_NAMESPACE
+void QMacInputContext::reset()
+{
+ QWidget *w = QInputContext::focusWidget();
+ if (w) {
+ NSView *view = qt_mac_effectiveview_for(w);
+ if ([view isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaView) class]]) {
+ QMacCocoaAutoReleasePool pool;
+ QT_MANGLE_NAMESPACE(QCocoaView) *qc = static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(view);
+ NSInputManager *currentIManager = [NSInputManager currentInputManager];
+ if (currentIManager) {
+ [currentIManager markedTextAbandoned:view];
+ [qc unmarkText];
+ }
+ }
+ }
+}
+
+bool QMacInputContext::isComposing() const
+{
+ QWidget *w = QInputContext::focusWidget();
+ if (w) {
+ NSView *view = qt_mac_effectiveview_for(w);
+ if ([view isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaView) class]]) {
+ return [static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(view) isComposing];
+ }
+ }
+ return false;
+}
+
+extern bool qt_mac_in_drag;
+void * /*NSImage */qt_mac_create_nsimage(const QPixmap &pm);
+static const int default_pm_hotx = -2;
+static const int default_pm_hoty = -16;
+static const char* default_pm[] = {
+ "13 9 3 1",
+ ". c None",
+ " c #000000",
+ "X c #FFFFFF",
+ "X X X X X X X",
+ " X X X X X X ",
+ "X ......... X",
+ " X.........X ",
+ "X ......... X",
+ " X.........X ",
+ "X ......... X",
+ " X X X X X X ",
+ "X X X X X X X",
+};
+
+Qt::DropAction QDragManager::drag(QDrag *o)
+{
+ if(qt_mac_in_drag) { //just make sure..
+ qWarning("Qt: Internal error: WH0A, unexpected condition reached");
+ return Qt::IgnoreAction;
+ }
+ if(object == o)
+ return Qt::IgnoreAction;
+ /* At the moment it seems clear that Mac OS X does not want to drag with a non-left button
+ so we just bail early to prevent it */
+ if(!(GetCurrentEventButtonState() & kEventMouseButtonPrimary))
+ return Qt::IgnoreAction;
+
+ if(object) {
+ dragPrivate()->source->removeEventFilter(this);
+ cancel();
+ beingCancelled = false;
+ }
+
+ object = o;
+ dragPrivate()->target = 0;
+
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(this, 0, QAccessible::DragDropStart);
+#endif
+
+ // setup the data
+ QMacPasteboard dragBoard((CFStringRef) NSDragPboard, QMacPasteboardMime::MIME_DND);
+ dragPrivate()->data->setData(QLatin1String("application/x-qt-mime-type-name"), QByteArray("dummy"));
+ dragBoard.setMimeData(dragPrivate()->data);
+
+ // create the image
+ QPoint hotspot;
+ QPixmap pix = dragPrivate()->pixmap;
+ if(pix.isNull()) {
+ if(dragPrivate()->data->hasText() || dragPrivate()->data->hasUrls()) {
+ // get the string
+ QString s = dragPrivate()->data->hasText() ? dragPrivate()->data->text()
+ : dragPrivate()->data->urls().first().toString();
+ if(s.length() > 26)
+ s = s.left(23) + QChar(0x2026);
+ if(!s.isEmpty()) {
+ // draw it
+ QFont f(qApp->font());
+ f.setPointSize(12);
+ QFontMetrics fm(f);
+ QPixmap tmp(fm.width(s), fm.height());
+ if(!tmp.isNull()) {
+ QPainter p(&tmp);
+ p.fillRect(0, 0, tmp.width(), tmp.height(), Qt::color0);
+ p.setPen(Qt::color1);
+ p.setFont(f);
+ p.drawText(0, fm.ascent(), s);
+ // save it
+ pix = tmp;
+ hotspot = QPoint(tmp.width() / 2, tmp.height() / 2);
+ }
+ }
+ } else {
+ pix = QPixmap(default_pm);
+ hotspot = QPoint(default_pm_hotx, default_pm_hoty);
+ }
+ } else {
+ hotspot = dragPrivate()->hotspot;
+ }
+
+ // Convert the image to NSImage:
+ NSImage *image = (NSImage *)qt_mac_create_nsimage(pix);
+ [image retain];
+
+ DnDParams *dndParams = macCurrentDnDParameters();
+ QT_MANGLE_NAMESPACE(QCocoaView) *theView = static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(dndParams->view);
+
+ // Save supported actions:
+ [theView setSupportedActions: qt_mac_mapDropActions(dragPrivate()->possible_actions)];
+ QPoint pointInView = [theView qt_qwidget]->mapFromGlobal(dndParams->globalPoint);
+ NSPoint imageLoc = {pointInView.x() - hotspot.x(), pointInView.y() + pix.height() - hotspot.y()};
+ NSSize mouseOffset = {0.0, 0.0};
+ NSPasteboard *pboard = [NSPasteboard pasteboardWithName:NSDragPboard];
+ dragPrivate()->executed_action = Qt::ActionMask;
+
+ // Execute the drag:
+ [theView retain];
+ [theView dragImage:image
+ at:imageLoc
+ offset:mouseOffset
+ event:dndParams->theEvent
+ pasteboard:pboard
+ source:theView
+ slideBack:YES];
+
+ // Reset the implicit grab widget when drag ends because we will not
+ // receive the mouse release event when DND is active:
+ qt_button_down = 0;
+ [theView release];
+ [image release];
+ if (dragPrivate())
+ dragPrivate()->executed_action = Qt::IgnoreAction;
+ object = 0;
+ Qt::DropAction performedAction(qt_mac_mapNSDragOperation(dndParams->performedAction));
+
+ // Do post drag processing, if required.
+ if (performedAction != Qt::IgnoreAction) {
+ // Check if the receiver points us to a file location.
+ // if so, we need to do the file copy/move ourselves.
+ QCFType<CFURLRef> pasteLocation = 0;
+ PasteboardCopyPasteLocation(dragBoard.pasteBoard(), &pasteLocation);
+ if (pasteLocation) {
+ QList<QUrl> urls = o->mimeData()->urls();
+ for (int i = 0; i < urls.size(); ++i) {
+ QUrl fromUrl = urls.at(i);
+ QString filename = QFileInfo(fromUrl.path()).fileName();
+ QUrl toUrl(QCFString::toQString(CFURLGetString(pasteLocation)) + filename);
+ if (performedAction == Qt::MoveAction)
+ QFile::rename(fromUrl.path(), toUrl.path());
+ else if (performedAction == Qt::CopyAction)
+ QFile::copy(fromUrl.path(), toUrl.path());
+ }
+ }
+ }
+
+ // Clean-up:
+ o->setMimeData(0);
+ o->deleteLater();
+ return performedAction;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_MAC_USE_COCOA
diff --git a/src/widgets/platforms/mac/qcocoaview_mac_p.h b/src/widgets/platforms/mac/qcocoaview_mac_p.h
new file mode 100644
index 0000000000..963cbe06bf
--- /dev/null
+++ b/src/widgets/platforms/mac/qcocoaview_mac_p.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qevent.h>
+#ifdef QT_MAC_USE_COCOA
+#import <Cocoa/Cocoa.h>
+
+@class QT_MANGLE_NAMESPACE(QCocoaView);
+QT_FORWARD_DECLARE_CLASS(QWidgetPrivate);
+QT_FORWARD_DECLARE_CLASS(QWidget);
+QT_FORWARD_DECLARE_CLASS(QEvent);
+QT_FORWARD_DECLARE_CLASS(QString);
+QT_FORWARD_DECLARE_CLASS(QStringList);
+
+Q_WIDGETS_EXPORT
+@interface QT_MANGLE_NAMESPACE(QCocoaView) : NSControl <NSTextInput> {
+ QWidget *qwidget;
+ QWidgetPrivate *qwidgetprivate;
+ NSDragOperation supportedActions;
+ bool composing;
+ int composingLength;
+ bool sendKeyEvents;
+ bool fromKeyDownEvent;
+ QString *composingText;
+ @public int alienTouchCount;
+}
+- (id)initWithQWidget:(QWidget *)widget widgetPrivate:(QWidgetPrivate *)widgetprivate;
+- (void) finishInitWithQWidget:(QWidget *)widget widgetPrivate:(QWidgetPrivate *)widgetprivate;
+- (void)frameDidChange:(NSNotification *)note;
+- (void)setSupportedActions:(NSDragOperation)actions;
+- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal;
+- (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation;
+- (BOOL)isComposing;
+- (QWidget *)qt_qwidget;
+- (void) qt_clearQWidget;
+
+@end
+#endif
diff --git a/src/widgets/platforms/mac/qcocoawindow_mac.mm b/src/widgets/platforms/mac/qcocoawindow_mac.mm
new file mode 100644
index 0000000000..fba69a3263
--- /dev/null
+++ b/src/widgets/platforms/mac/qcocoawindow_mac.mm
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmacdefines_mac.h"
+#ifdef QT_MAC_USE_COCOA
+#import <private/qcocoawindow_mac_p.h>
+#import <private/qcocoawindowdelegate_mac_p.h>
+#import <private/qcocoaview_mac_p.h>
+#import <private/qt_cocoa_helpers_mac_p.h>
+#import <private/qcocoawindowcustomthemeframe_mac_p.h>
+#import <private/qcocoaapplication_mac_p.h>
+#import <private/qdnd_p.h>
+#import <private/qmultitouch_mac_p.h>
+
+#include <QtGui/QWidget>
+
+QT_FORWARD_DECLARE_CLASS(QWidget);
+QT_USE_NAMESPACE
+
+@implementation NSWindow (QT_MANGLE_NAMESPACE(QWidgetIntegration))
+
+- (id)QT_MANGLE_NAMESPACE(qt_initWithQWidget):(QWidget*)widget contentRect:(NSRect)rect styleMask:(NSUInteger)mask
+{
+ self = [self initWithContentRect:rect styleMask:mask backing:NSBackingStoreBuffered defer:YES];
+ if (self) {
+ [[QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate] becomeDelegteForWindow:self widget:widget];
+ [self setReleasedWhenClosed:NO];
+ }
+ return self;
+}
+
+- (QWidget *)QT_MANGLE_NAMESPACE(qt_qwidget)
+{
+ QWidget *widget = 0;
+ if ([self delegate] == [QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate])
+ widget = [[QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate] qt_qwidgetForWindow:self];
+ return widget;
+}
+
+@end
+
+@implementation QT_MANGLE_NAMESPACE(QCocoaWindow)
+
+/***********************************************************************
+ Copy and Paste between QCocoaWindow and QCocoaPanel
+ This is a bit unfortunate, but thanks to the dynamic dispatch we
+ have to duplicate this code or resort to really silly forwarding methods
+**************************************************************************/
+#include "qcocoasharedwindowmethods_mac_p.h"
+
+@end
+#endif
diff --git a/src/widgets/platforms/mac/qcocoawindow_mac_p.h b/src/widgets/platforms/mac/qcocoawindow_mac_p.h
new file mode 100644
index 0000000000..9013f66080
--- /dev/null
+++ b/src/widgets/platforms/mac/qcocoawindow_mac_p.h
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QCOCOAWINDOW_MAC_P
+#define QCOCOAWINDOW_MAC_P
+
+#ifdef QT_MAC_USE_COCOA
+#include "qmacdefines_mac.h"
+#import <Cocoa/Cocoa.h>
+#include <private/qapplication_p.h>
+#include <private/qbackingstore_p.h>
+
+enum { QtMacCustomizeWindow = 1 << 21 }; // This will one day be run over by
+
+QT_FORWARD_DECLARE_CLASS(QWidget);
+QT_FORWARD_DECLARE_CLASS(QStringList);
+QT_FORWARD_DECLARE_CLASS(QCocoaDropData);
+
+@interface NSWindow (QtCoverForHackWithCategory)
++ (Class)frameViewClassForStyleMask:(NSUInteger)styleMask;
+@end
+
+@interface NSWindow (QT_MANGLE_NAMESPACE(QWidgetIntegration))
+- (id)QT_MANGLE_NAMESPACE(qt_initWithQWidget):(QWidget *)widget contentRect:(NSRect)rect styleMask:(NSUInteger)mask;
+- (QWidget *)QT_MANGLE_NAMESPACE(qt_qwidget);
+@end
+
+@interface NSWindow (QtIntegration)
+- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender;
+- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender;
+- (void)draggingExited:(id <NSDraggingInfo>)sender;
+- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender;
+@end
+
+@interface QT_MANGLE_NAMESPACE(QCocoaWindow) : NSWindow {
+ QStringList *currentCustomDragTypes;
+ QCocoaDropData *dropData;
+ NSInteger dragEnterSequence;
+}
+
++ (Class)frameViewClassForStyleMask:(NSUInteger)styleMask;
+- (void)registerDragTypes;
+- (void)drawRectOriginal:(NSRect)rect;
+
+@end
+#endif
+
+#endif
diff --git a/src/widgets/platforms/mac/qcocoawindowcustomthemeframe_mac.mm b/src/widgets/platforms/mac/qcocoawindowcustomthemeframe_mac.mm
new file mode 100644
index 0000000000..cc255420b5
--- /dev/null
+++ b/src/widgets/platforms/mac/qcocoawindowcustomthemeframe_mac.mm
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmacdefines_mac.h"
+
+#ifdef QT_MAC_USE_COCOA
+
+#import "private/qcocoawindowcustomthemeframe_mac_p.h"
+#import "private/qcocoawindow_mac_p.h"
+#include "private/qt_cocoa_helpers_mac_p.h"
+#include "qwidget.h"
+
+@implementation QT_MANGLE_NAMESPACE(QCocoaWindowCustomThemeFrame)
+
+- (void)_updateButtons
+{
+ [super _updateButtons];
+ NSWindow *window = [self window];
+ qt_syncCocoaTitleBarButtons(window, [window QT_MANGLE_NAMESPACE(qt_qwidget)]);
+}
+
+@end
+
+#endif
diff --git a/src/widgets/platforms/mac/qcocoawindowcustomthemeframe_mac_p.h b/src/widgets/platforms/mac/qcocoawindowcustomthemeframe_mac_p.h
new file mode 100644
index 0000000000..7ac8a28539
--- /dev/null
+++ b/src/widgets/platforms/mac/qcocoawindowcustomthemeframe_mac_p.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp, qcolor_x11.cpp, qfiledialog.cpp
+// and many other. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+//
+#import <Cocoa/Cocoa.h>
+#include "qmacdefines_mac.h"
+#import "qnsthemeframe_mac_p.h"
+
+@interface QT_MANGLE_NAMESPACE(QCocoaWindowCustomThemeFrame) : NSThemeFrame
+{
+}
+
+@end
diff --git a/src/widgets/platforms/mac/qcocoawindowdelegate_mac.mm b/src/widgets/platforms/mac/qcocoawindowdelegate_mac.mm
new file mode 100644
index 0000000000..772fd2bfc7
--- /dev/null
+++ b/src/widgets/platforms/mac/qcocoawindowdelegate_mac.mm
@@ -0,0 +1,439 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#import "private/qcocoawindowdelegate_mac_p.h"
+#ifdef QT_MAC_USE_COCOA
+#include <private/qwidget_p.h>
+#include <private/qapplication_p.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+#include <qevent.h>
+#include <qlayout.h>
+#include <qcoreapplication.h>
+#include <qmenubar.h>
+#include <QMainWindow>
+#include <QToolBar>
+#include <private/qmainwindowlayout_p.h>
+
+QT_BEGIN_NAMESPACE
+extern QWidgetData *qt_qwidget_data(QWidget *); // qwidget.cpp
+extern void onApplicationWindowChangedActivation(QWidget *, bool); //qapplication_mac.mm
+extern bool qt_sendSpontaneousEvent(QObject *, QEvent *); // qapplication.cpp
+QT_END_NAMESPACE
+
+QT_USE_NAMESPACE
+
+static QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) *sharedCocoaWindowDelegate = nil;
+
+// This is a singleton, but unlike most Cocoa singletons, it lives in a library and could be
+// pontentially loaded and unloaded. This means we should at least attempt to do the
+// memory management correctly.
+
+static void cleanupCocoaWindowDelegate()
+{
+ [sharedCocoaWindowDelegate release];
+}
+
+@implementation QT_MANGLE_NAMESPACE(QCocoaWindowDelegate)
+
+- (id)init
+{
+ self = [super init];
+ if (self != nil) {
+ m_windowHash = new QHash<NSWindow *, QWidget *>();
+ m_drawerHash = new QHash<NSDrawer *, QWidget *>();
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ sharedCocoaWindowDelegate = nil;
+ QHash<NSWindow *, QWidget *>::const_iterator windowIt = m_windowHash->constBegin();
+ while (windowIt != m_windowHash->constEnd()) {
+ [windowIt.key() setDelegate:nil];
+ ++windowIt;
+ }
+ delete m_windowHash;
+ QHash<NSDrawer *, QWidget *>::const_iterator drawerIt = m_drawerHash->constBegin();
+ while (drawerIt != m_drawerHash->constEnd()) {
+ [drawerIt.key() setDelegate:nil];
+ ++drawerIt;
+ }
+ delete m_drawerHash;
+ [super dealloc];
+}
+
++ (id)allocWithZone:(NSZone *)zone
+{
+ @synchronized(self) {
+ if (sharedCocoaWindowDelegate == nil) {
+ sharedCocoaWindowDelegate = [super allocWithZone:zone];
+ return sharedCocoaWindowDelegate;
+ qAddPostRoutine(cleanupCocoaWindowDelegate);
+ }
+ }
+ return nil;
+}
+
++ (QT_MANGLE_NAMESPACE(QCocoaWindowDelegate)*)sharedDelegate
+{
+ @synchronized(self) {
+ if (sharedCocoaWindowDelegate == nil)
+ [[self alloc] init];
+ }
+ return [[sharedCocoaWindowDelegate retain] autorelease];
+}
+
+-(void)syncSizeForWidget:(QWidget *)qwidget toSize:(const QSize &)newSize fromSize:(const QSize &)oldSize
+{
+ qt_qwidget_data(qwidget)->crect.setSize(newSize);
+ // ### static contents optimization needs to go here
+ const OSViewRef view = qt_mac_nativeview_for(qwidget);
+ [view setFrameSize:NSMakeSize(newSize.width(), newSize.height())];
+ if (!qwidget->isVisible()) {
+ qwidget->setAttribute(Qt::WA_PendingResizeEvent, true);
+ } else {
+ QResizeEvent qre(newSize, oldSize);
+ if (qwidget->testAttribute(Qt::WA_PendingResizeEvent)) {
+ qwidget->setAttribute(Qt::WA_PendingResizeEvent, false);
+ QApplication::sendEvent(qwidget, &qre);
+ } else {
+ qt_sendSpontaneousEvent(qwidget, &qre);
+ }
+ }
+}
+
+- (void)dumpMaximizedStateforWidget:(QWidget*)qwidget window:(NSWindow *)window
+{
+ if (!window)
+ return; // Nothing to do.
+ QWidgetData *widgetData = qt_qwidget_data(qwidget);
+ if ((widgetData->window_state & Qt::WindowMaximized) && ![window isZoomed]) {
+ widgetData->window_state &= ~Qt::WindowMaximized;
+ QWindowStateChangeEvent e(Qt::WindowState(widgetData->window_state | Qt::WindowMaximized));
+ qt_sendSpontaneousEvent(qwidget, &e);
+ }
+}
+
+- (NSSize)closestAcceptableSizeForWidget:(QWidget *)qwidget window:(NSWindow *)window
+ withNewSize:(NSSize)proposedSize
+{
+ [self dumpMaximizedStateforWidget:qwidget window:window];
+ QSize newSize = QLayout::closestAcceptableSize(qwidget,
+ QSize(proposedSize.width, proposedSize.height));
+ return [NSWindow frameRectForContentRect:
+ NSMakeRect(0., 0., newSize.width(), newSize.height())
+ styleMask:[window styleMask]].size;
+}
+
+- (NSSize)windowWillResize:(NSWindow *)windowToResize toSize:(NSSize)proposedFrameSize
+{
+ QWidget *qwidget = m_windowHash->value(windowToResize);
+ return [self closestAcceptableSizeForWidget:qwidget window:windowToResize
+ withNewSize:[NSWindow contentRectForFrameRect:
+ NSMakeRect(0, 0,
+ proposedFrameSize.width,
+ proposedFrameSize.height)
+ styleMask:[windowToResize styleMask]].size];
+}
+
+- (NSSize)drawerWillResizeContents:(NSDrawer *)sender toSize:(NSSize)contentSize
+{
+ QWidget *qwidget = m_drawerHash->value(sender);
+ return [self closestAcceptableSizeForWidget:qwidget window:nil withNewSize:contentSize];
+}
+
+-(void)windowDidMiniaturize:(NSNotification*)notification
+{
+ QWidget *qwidget = m_windowHash->value([notification object]);
+ if (!qwidget->isMinimized()) {
+ QWidgetData *widgetData = qt_qwidget_data(qwidget);
+ widgetData->window_state = widgetData->window_state | Qt::WindowMinimized;
+ QWindowStateChangeEvent e(Qt::WindowStates(widgetData->window_state & ~Qt::WindowMinimized));
+ qt_sendSpontaneousEvent(qwidget, &e);
+ }
+ // Send hide to match Qt on X11 and Windows
+ QEvent e(QEvent::Hide);
+ qt_sendSpontaneousEvent(qwidget, &e);
+}
+
+- (void)windowDidResize:(NSNotification *)notification
+{
+ NSWindow *window = [notification object];
+ QWidget *qwidget = m_windowHash->value(window);
+ QWidgetData *widgetData = qt_qwidget_data(qwidget);
+ if (!(qwidget->windowState() & (Qt::WindowMaximized | Qt::WindowFullScreen)) && [window isZoomed]) {
+ widgetData->window_state = widgetData->window_state | Qt::WindowMaximized;
+ QWindowStateChangeEvent e(Qt::WindowStates(widgetData->window_state
+ & ~Qt::WindowMaximized));
+ qt_sendSpontaneousEvent(qwidget, &e);
+ } else {
+ widgetData->window_state = widgetData->window_state & ~Qt::WindowMaximized;
+ QWindowStateChangeEvent e(Qt::WindowStates(widgetData->window_state
+ | Qt::WindowMaximized));
+ qt_sendSpontaneousEvent(qwidget, &e);
+ }
+ NSRect rect = [[window contentView] frame];
+ const QSize newSize(rect.size.width, rect.size.height);
+ const QSize &oldSize = widgetData->crect.size();
+ if (newSize != oldSize) {
+ QWidgetPrivate::qt_mac_update_sizer(qwidget);
+ [self syncSizeForWidget:qwidget toSize:newSize fromSize:oldSize];
+ }
+
+ // We force the repaint to be synchronized with the resize of the window.
+ // Otherwise, the resize looks sluggish because we paint one event loop later.
+ if ([[window contentView] inLiveResize]) {
+ qwidget->repaint();
+
+ // We need to repaint the toolbar as well.
+ QMainWindow* mWindow = qobject_cast<QMainWindow*>(qwidget->window());
+ if (mWindow) {
+ QMainWindowLayout *mLayout = qobject_cast<QMainWindowLayout*>(mWindow->layout());
+ QList<QToolBar *> toolbarList = mLayout->qtoolbarsInUnifiedToolbarList;
+
+ for (int i = 0; i < toolbarList.size(); ++i) {
+ QToolBar* toolbar = toolbarList.at(i);
+ toolbar->repaint();
+ }
+ }
+ }
+}
+
+- (void)windowDidMove:(NSNotification *)notification
+{
+ // The code underneath needs to translate the window location
+ // from bottom left (which is the origin used by Cocoa) to
+ // upper left (which is the origin used by Qt):
+ NSWindow *window = [notification object];
+ NSRect newRect = [window frame];
+ QWidget *qwidget = m_windowHash->value(window);
+ QPoint qtPoint = flipPoint(NSMakePoint(newRect.origin.x,
+ newRect.origin.y + newRect.size.height)).toPoint();
+ const QRect &oldRect = qwidget->frameGeometry();
+
+ if (qtPoint.x() != oldRect.x() || qtPoint.y() != oldRect.y()) {
+ QWidgetData *widgetData = qt_qwidget_data(qwidget);
+ QRect oldCRect = widgetData->crect;
+ QWidgetPrivate *widgetPrivate = qt_widget_private(qwidget);
+ const QRect &fStrut = widgetPrivate->frameStrut();
+ widgetData->crect.moveTo(qtPoint.x() + fStrut.left(), qtPoint.y() + fStrut.top());
+ if (!qwidget->isVisible()) {
+ qwidget->setAttribute(Qt::WA_PendingMoveEvent, true);
+ } else {
+ QMoveEvent qme(qtPoint, oldRect.topLeft());
+ qt_sendSpontaneousEvent(qwidget, &qme);
+ }
+ }
+}
+
+-(BOOL)windowShouldClose:(id)windowThatWantsToClose
+{
+ QWidget *qwidget = m_windowHash->value(windowThatWantsToClose);
+ QScopedLoopLevelCounter counter(qt_widget_private(qwidget)->threadData);
+ return qt_widget_private(qwidget)->close_helper(QWidgetPrivate::CloseWithSpontaneousEvent);
+}
+
+-(void)windowDidDeminiaturize:(NSNotification *)notification
+{
+ QWidget *qwidget = m_windowHash->value([notification object]);
+ QWidgetData *widgetData = qt_qwidget_data(qwidget);
+ Qt::WindowStates currState = Qt::WindowStates(widgetData->window_state);
+ Qt::WindowStates newState = currState;
+ if (currState & Qt::WindowMinimized)
+ newState &= ~Qt::WindowMinimized;
+ if (!(currState & Qt::WindowActive))
+ newState |= Qt::WindowActive;
+ if (newState != currState) {
+ widgetData->window_state = newState;
+ QWindowStateChangeEvent e(currState);
+ qt_sendSpontaneousEvent(qwidget, &e);
+ }
+ QShowEvent qse;
+ qt_sendSpontaneousEvent(qwidget, &qse);
+}
+
+-(void)windowDidBecomeMain:(NSNotification*)notification
+{
+ QWidget *qwidget = m_windowHash->value([notification object]);
+ Q_ASSERT(qwidget);
+ onApplicationWindowChangedActivation(qwidget, true);
+}
+
+-(void)windowDidResignMain:(NSNotification*)notification
+{
+ QWidget *qwidget = m_windowHash->value([notification object]);
+ Q_ASSERT(qwidget);
+ onApplicationWindowChangedActivation(qwidget, false);
+}
+
+// These are the same as main, but they are probably better to keep separate since there is a
+// tiny difference between main and key windows.
+-(void)windowDidBecomeKey:(NSNotification*)notification
+{
+ QWidget *qwidget = m_windowHash->value([notification object]);
+ Q_ASSERT(qwidget);
+ onApplicationWindowChangedActivation(qwidget, true);
+}
+
+-(void)windowDidResignKey:(NSNotification*)notification
+{
+ QWidget *qwidget = m_windowHash->value([notification object]);
+ Q_ASSERT(qwidget);
+ onApplicationWindowChangedActivation(qwidget, false);
+}
+
+-(QWidget *)qt_qwidgetForWindow:(NSWindow *)window
+{
+ return m_windowHash->value(window);
+}
+
+- (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame
+{
+ Q_UNUSED(newFrame);
+ // saving the current window geometry before the window is maximized
+ QWidget *qwidget = m_windowHash->value(window);
+ QWidgetPrivate *widgetPrivate = qt_widget_private(qwidget);
+ if (qwidget->isWindow()) {
+ if(qwidget->windowState() & Qt::WindowMaximized) {
+ // Restoring
+ widgetPrivate->topData()->wasMaximized = false;
+ } else {
+ // Maximizing
+ widgetPrivate->topData()->normalGeometry = qwidget->geometry();
+ // If the window was maximized we need to update the coordinates since now it will start at 0,0.
+ // We do this in a special field that is only used when not restoring but manually resizing the window.
+ // Since the coordinates are fixed we just set a boolean flag.
+ widgetPrivate->topData()->wasMaximized = true;
+ }
+ }
+ return YES;
+}
+
+- (NSRect)windowWillUseStandardFrame:(NSWindow *)window defaultFrame:(NSRect)defaultFrame
+{
+ NSRect frameToReturn = defaultFrame;
+ QWidget *qwidget = m_windowHash->value(window);
+ QSizeF size = qwidget->maximumSize();
+ NSRect windowFrameRect = [window frame];
+ NSRect viewFrameRect = [[window contentView] frame];
+ // consider additional size required for titlebar & frame
+ frameToReturn.size.width = qMin<CGFloat>(frameToReturn.size.width,
+ size.width()+(windowFrameRect.size.width - viewFrameRect.size.width));
+ frameToReturn.size.height = qMin<CGFloat>(frameToReturn.size.height,
+ size.height()+(windowFrameRect.size.height - viewFrameRect.size.height));
+ return frameToReturn;
+}
+
+- (void)becomeDelegteForWindow:(NSWindow *)window widget:(QWidget *)widget
+{
+ m_windowHash->insert(window, widget);
+ [window setDelegate:self];
+}
+
+- (void)resignDelegateForWindow:(NSWindow *)window
+{
+ [window setDelegate:nil];
+ m_windowHash->remove(window);
+}
+
+- (void)becomeDelegateForDrawer:(NSDrawer *)drawer widget:(QWidget *)widget
+{
+ m_drawerHash->insert(drawer, widget);
+ [drawer setDelegate:self];
+ NSWindow *window = [[drawer contentView] window];
+ [self becomeDelegteForWindow:window widget:widget];
+}
+
+- (void)resignDelegateForDrawer:(NSDrawer *)drawer
+{
+ QWidget *widget = m_drawerHash->value(drawer);
+ [drawer setDelegate:nil];
+ if (widget)
+ [self resignDelegateForWindow:[[drawer contentView] window]];
+ m_drawerHash->remove(drawer);
+}
+
+- (BOOL)window:(NSWindow *)window shouldPopUpDocumentPathMenu:(NSMenu *)menu
+{
+ Q_UNUSED(menu);
+ QWidget *qwidget = m_windowHash->value(window);
+ if (qwidget && !qwidget->windowFilePath().isEmpty()) {
+ return YES;
+ }
+ return NO;
+}
+
+- (BOOL)window:(NSWindow *)window shouldDragDocumentWithEvent:(NSEvent *)event
+ from:(NSPoint)dragImageLocation
+ withPasteboard:(NSPasteboard *)pasteboard
+{
+ Q_UNUSED(event);
+ Q_UNUSED(dragImageLocation);
+ Q_UNUSED(pasteboard);
+ QWidget *qwidget = m_windowHash->value(window);
+ if (qwidget && !qwidget->windowFilePath().isEmpty()) {
+ return YES;
+ }
+ return NO;
+}
+
+- (void)syncContentViewFrame: (NSNotification *)notification
+{
+ NSView *cView = [notification object];
+ if (cView) {
+ NSWindow *window = [cView window];
+ QWidget *qwidget = m_windowHash->value(window);
+ if (qwidget) {
+ QWidgetData *widgetData = qt_qwidget_data(qwidget);
+ NSRect rect = [cView frame];
+ const QSize newSize(rect.size.width, rect.size.height);
+ const QSize &oldSize = widgetData->crect.size();
+ if (newSize != oldSize) {
+ [self syncSizeForWidget:qwidget toSize:newSize fromSize:oldSize];
+ }
+ }
+
+ }
+}
+
+@end
+#endif// QT_MAC_USE_COCOA
diff --git a/src/widgets/platforms/mac/qcocoawindowdelegate_mac_p.h b/src/widgets/platforms/mac/qcocoawindowdelegate_mac_p.h
new file mode 100644
index 0000000000..a510ae48ed
--- /dev/null
+++ b/src/widgets/platforms/mac/qcocoawindowdelegate_mac_p.h
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qmacdefines_mac.h"
+
+#ifdef QT_MAC_USE_COCOA
+#import <Cocoa/Cocoa.h>
+
+QT_BEGIN_NAMESPACE
+template <class Key, class T> class QHash;
+QT_END_NAMESPACE
+using QT_PREPEND_NAMESPACE(QHash);
+QT_FORWARD_DECLARE_CLASS(QWidget)
+QT_FORWARD_DECLARE_CLASS(QSize)
+QT_FORWARD_DECLARE_CLASS(QWidgetData)
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5
+@protocol NSWindowDelegate <NSObject>
+- (NSSize)windowWillResize:(NSWindow *)window toSize:(NSSize)proposedFrameSize;
+- (void)windowDidMiniaturize:(NSNotification*)notification;
+- (void)windowDidResize:(NSNotification *)notification;
+- (NSRect)windowWillUseStandardFrame:(NSWindow *)window defaultFrame:(NSRect)defaultFrame;
+- (void)windowDidMove:(NSNotification *)notification;
+- (BOOL)windowShouldClose:(id)window;
+- (void)windowDidDeminiaturize:(NSNotification *)notification;
+- (void)windowDidBecomeMain:(NSNotification*)notification;
+- (void)windowDidResignMain:(NSNotification*)notification;
+- (void)windowDidBecomeKey:(NSNotification*)notification;
+- (void)windowDidResignKey:(NSNotification*)notification;
+- (BOOL)window:(NSWindow *)window shouldPopUpDocumentPathMenu:(NSMenu *)menu;
+- (BOOL)window:(NSWindow *)window shouldDragDocumentWithEvent:(NSEvent *)event from:(NSPoint)dragImageLocation withPasteboard:(NSPasteboard *)pasteboard;
+- (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame;
+@end
+
+@protocol NSDrawerDelegate <NSObject>
+- (NSSize)drawerWillResizeContents:(NSDrawer *)sender toSize:(NSSize)contentSize;
+@end
+
+#endif
+
+
+
+@interface QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) : NSObject<NSWindowDelegate, NSDrawerDelegate> {
+ QHash<NSWindow *, QWidget *> *m_windowHash;
+ QHash<NSDrawer *, QWidget *> *m_drawerHash;
+}
++ (QT_MANGLE_NAMESPACE(QCocoaWindowDelegate)*)sharedDelegate;
+- (void)becomeDelegteForWindow:(NSWindow *)window widget:(QWidget *)widget;
+- (void)resignDelegateForWindow:(NSWindow *)window;
+- (void)becomeDelegateForDrawer:(NSDrawer *)drawer widget:(QWidget *)widget;
+- (void)resignDelegateForDrawer:(NSDrawer *)drawer;
+- (void)dumpMaximizedStateforWidget:(QWidget*)qwidget window:(NSWindow *)window;
+- (void)syncSizeForWidget:(QWidget *)qwidget
+ toSize:(const QSize &)newSize
+ fromSize:(const QSize &)oldSize;
+- (NSSize)closestAcceptableSizeForWidget:(QWidget *)qwidget
+ window:(NSWindow *)window withNewSize:(NSSize)proposedSize;
+- (QWidget *)qt_qwidgetForWindow:(NSWindow *)window;
+- (void)syncContentViewFrame: (NSNotification *)notification;
+@end
+#endif
diff --git a/src/widgets/platforms/mac/qcolormap_mac.cpp b/src/widgets/platforms/mac/qcolormap_mac.cpp
new file mode 100644
index 0000000000..0c3fd70468
--- /dev/null
+++ b/src/widgets/platforms/mac/qcolormap_mac.cpp
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcolormap.h"
+#include "qcolor.h"
+
+QT_BEGIN_NAMESPACE
+
+class QColormapPrivate
+{
+public:
+ inline QColormapPrivate()
+ : ref(1)
+ { }
+
+ QAtomicInt ref;
+};
+static QColormap *qt_mac_global_map = 0;
+
+void QColormap::initialize()
+{
+ qt_mac_global_map = new QColormap;
+}
+
+void QColormap::cleanup()
+{
+ delete qt_mac_global_map;
+ qt_mac_global_map = 0;
+}
+
+QColormap QColormap::instance(int)
+{
+ return *qt_mac_global_map;
+}
+
+QColormap::QColormap() : d(new QColormapPrivate)
+{}
+
+QColormap::QColormap(const QColormap &colormap) :d (colormap.d)
+{ d->ref.ref(); }
+
+QColormap::~QColormap()
+{
+ if (!d->ref.deref())
+ delete d;
+}
+
+QColormap::Mode QColormap::mode() const
+{ return QColormap::Direct; }
+
+int QColormap::depth() const
+{
+ return 32;
+}
+
+int QColormap::size() const
+{
+ return -1;
+}
+
+uint QColormap::pixel(const QColor &color) const
+{ return color.rgba(); }
+
+const QColor QColormap::colorAt(uint pixel) const
+{ return QColor(pixel); }
+
+const QVector<QColor> QColormap::colormap() const
+{ return QVector<QColor>(); }
+
+QColormap &QColormap::operator=(const QColormap &colormap)
+{ qAtomicAssign(d, colormap.d); return *this; }
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/mac/qcursor_mac.mm b/src/widgets/platforms/mac/qcursor_mac.mm
new file mode 100644
index 0000000000..0b0abaa8c6
--- /dev/null
+++ b/src/widgets/platforms/mac/qcursor_mac.mm
@@ -0,0 +1,689 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qcursor_p.h>
+#include <private/qpixmap_mac_p.h>
+#include <qapplication.h>
+#include <qbitmap.h>
+#include <qcursor.h>
+#include <qevent.h>
+#include <string.h>
+#include <unistd.h>
+#include <AppKit/NSCursor.h>
+#include <qpainter.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+#include <private/qapplication_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*****************************************************************************
+ Externals
+ *****************************************************************************/
+extern QCursorData *qt_cursorTable[Qt::LastCursor + 1];
+extern OSWindowRef qt_mac_window_for(const QWidget *); //qwidget_mac.cpp
+extern GrafPtr qt_mac_qd_context(const QPaintDevice *); //qpaintdevice_mac.cpp
+extern bool qt_sendSpontaneousEvent(QObject *, QEvent *); //qapplication_mac.cpp
+extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp
+
+/*****************************************************************************
+ Internal QCursorData class
+ *****************************************************************************/
+
+class QMacAnimateCursor : public QObject
+{
+ int timerId, step;
+ ThemeCursor curs;
+public:
+ QMacAnimateCursor() : QObject(), timerId(-1) { }
+ void start(ThemeCursor c) {
+ step = 1;
+ if(timerId != -1)
+ killTimer(timerId);
+ timerId = startTimer(300);
+ curs = c;
+ }
+ void stop() {
+ if(timerId != -1) {
+ killTimer(timerId);
+ timerId = -1;
+ }
+ }
+protected:
+ void timerEvent(QTimerEvent *e) {
+ if(e->timerId() == timerId) {
+ /*
+ if(SetAnimatedThemeCursor(curs, step++) == themeBadCursorIndexErr)
+ stop();
+ */
+ }
+ }
+};
+
+inline void *qt_mac_nsCursorForQCursor(const QCursor &c)
+{
+ c.d->update();
+ return [[static_cast<NSCursor *>(c.d->curs.cp.nscursor) retain] autorelease];
+}
+
+static QCursorData *currentCursor = 0; //current cursor
+
+void qt_mac_set_cursor(const QCursor *c)
+{
+#ifdef QT_MAC_USE_COCOA
+ QMacCocoaAutoReleasePool pool;
+ [static_cast<NSCursor *>(qt_mac_nsCursorForQCursor(*c)) set];
+#else
+ if (!c) {
+ currentCursor = 0;
+ return;
+ }
+ c->handle(); //force the cursor to get loaded, if it's not
+
+ if(currentCursor && currentCursor->type == QCursorData::TYPE_ThemeCursor
+ && currentCursor->curs.tc.anim)
+ currentCursor->curs.tc.anim->stop();
+ if(c->d->type == QCursorData::TYPE_ImageCursor) {
+ [static_cast<NSCursor *>(c->d->curs.cp.nscursor) set];
+ } else if(c->d->type == QCursorData::TYPE_ThemeCursor) {
+ if(SetAnimatedThemeCursor(c->d->curs.tc.curs, 0) == themeBadCursorIndexErr) {
+ SetThemeCursor(c->d->curs.tc.curs);
+ } else {
+ if(!c->d->curs.tc.anim)
+ c->d->curs.tc.anim = new QMacAnimateCursor;
+ c->d->curs.tc.anim->start(c->d->curs.tc.curs);
+ }
+ }
+
+ currentCursor = c->d;
+#endif
+}
+
+static QPointer<QWidget> lastWidgetUnderMouse = 0;
+static QPointer<QWidget> lastMouseCursorWidget = 0;
+static bool qt_button_down_on_prev_call = false;
+static QCursor *grabCursor = 0;
+
+void qt_mac_updateCursorWithWidgetUnderMouse(QWidget *widgetUnderMouse)
+{
+ QCursor cursor(Qt::ArrowCursor);
+ if (qt_button_down) {
+ // The widget that is currently pressed
+ // grabs the mouse cursor:
+ widgetUnderMouse = qt_button_down;
+ qt_button_down_on_prev_call = true;
+ } else if (qt_button_down_on_prev_call) {
+ // Grab has been released, so do
+ // a full check:
+ qt_button_down_on_prev_call = false;
+ lastWidgetUnderMouse = 0;
+ lastMouseCursorWidget = 0;
+ }
+
+ if (QApplication::overrideCursor()) {
+ cursor = *QApplication::overrideCursor();
+ } else if (grabCursor) {
+ cursor = *grabCursor;
+ } else if (widgetUnderMouse) {
+ if (widgetUnderMouse == lastWidgetUnderMouse) {
+ // Optimization that should hit when the widget under
+ // the mouse does not change as the mouse moves:
+ if (lastMouseCursorWidget)
+ cursor = lastMouseCursorWidget->cursor();
+ } else {
+ QWidget *w = widgetUnderMouse;
+ for (; w; w = w->parentWidget()) {
+ if (w->testAttribute(Qt::WA_SetCursor)) {
+ cursor = w->cursor();
+ break;
+ }
+ if (w->isWindow())
+ break;
+ }
+ // One final check in case we ran out of parents in the loop:
+ if (w && !w->testAttribute(Qt::WA_SetCursor))
+ w = 0;
+
+ lastWidgetUnderMouse = widgetUnderMouse;
+ lastMouseCursorWidget = w;
+ }
+ }
+
+#ifdef QT_MAC_USE_COCOA
+ cursor.d->update();
+ NSCursor *nsCursor = static_cast<NSCursor *>(cursor.d->curs.cp.nscursor);
+ if ([NSCursor currentCursor] != nsCursor) {
+ QMacCocoaAutoReleasePool pool;
+ [nsCursor set];
+ }
+#else
+ qt_mac_set_cursor(&cursor);
+#endif
+}
+
+void qt_mac_update_cursor()
+{
+ // This function is similar to qt_mac_updateCursorWithWidgetUnderMouse
+ // except that is clears the optimization cache, and finds the widget
+ // under mouse itself. Clearing the cache is useful in cases where the
+ // application has been deactivated/activated etc.
+ // NB: since we dont have any true native widget, the call to
+ // qt_mac_getTargetForMouseEvent will fail when the mouse is over QMacNativeWidgets.
+#ifdef QT_MAC_USE_COCOA
+ lastWidgetUnderMouse = 0;
+ lastMouseCursorWidget = 0;
+ QWidget *widgetUnderMouse = 0;
+
+ if (qt_button_down) {
+ widgetUnderMouse = qt_button_down;
+ } else {
+ QPoint localPoint;
+ QPoint globalPoint;
+ qt_mac_getTargetForMouseEvent(0, QEvent::None, localPoint, globalPoint, 0, &widgetUnderMouse);
+ }
+ qt_mac_updateCursorWithWidgetUnderMouse(widgetUnderMouse);
+#else
+ qt_mac_updateCursorWithWidgetUnderMouse(QApplication::widgetAt(QCursor::pos()));
+#endif
+}
+
+void qt_mac_setMouseGrabCursor(bool set, QCursor *const cursor = 0)
+{
+ if (grabCursor) {
+ delete grabCursor;
+ grabCursor = 0;
+ }
+ if (set) {
+ if (cursor)
+ grabCursor = new QCursor(*cursor);
+ else if (lastMouseCursorWidget)
+ grabCursor = new QCursor(lastMouseCursorWidget->cursor());
+ else
+ grabCursor = new QCursor(Qt::ArrowCursor);
+ }
+ qt_mac_update_cursor();
+}
+
+#ifndef QT_MAC_USE_COCOA
+void qt_mac_update_cursor_at_global_pos(const QPoint &globalPos)
+{
+ qt_mac_updateCursorWithWidgetUnderMouse(QApplication::widgetAt(globalPos));
+}
+#endif
+
+static int nextCursorId = Qt::BitmapCursor;
+
+QCursorData::QCursorData(Qt::CursorShape s)
+ : cshape(s), bm(0), bmm(0), hx(-1), hy(-1), mId(s), type(TYPE_None)
+{
+ ref = 1;
+ memset(&curs, '\0', sizeof(curs));
+}
+
+QCursorData::~QCursorData()
+{
+ if (type == TYPE_ImageCursor) {
+ if (curs.cp.my_cursor) {
+ QMacCocoaAutoReleasePool pool;
+ [static_cast<NSCursor *>(curs.cp.nscursor) release];
+ }
+ } else if(type == TYPE_ThemeCursor) {
+ delete curs.tc.anim;
+ }
+ type = TYPE_None;
+
+ delete bm;
+ delete bmm;
+ if(currentCursor == this)
+ currentCursor = 0;
+}
+
+QCursorData *QCursorData::setBitmap(const QBitmap &bitmap, const QBitmap &mask, int hotX, int hotY)
+{
+ if (!QCursorData::initialized)
+ QCursorData::initialize();
+ if (bitmap.depth() != 1 || mask.depth() != 1 || bitmap.size() != mask.size()) {
+ qWarning("Qt: QCursor: Cannot create bitmap cursor; invalid bitmap(s)");
+ QCursorData *c = qt_cursorTable[0];
+ c->ref.ref();
+ return c;
+ }
+ // This is silly, but this is apparently called outside the constructor, so we have
+ // to be ready for that case.
+ QCursorData *x = new QCursorData;
+ x->ref = 1;
+ x->mId = ++nextCursorId;
+ x->bm = new QBitmap(bitmap);
+ x->bmm = new QBitmap(mask);
+ x->cshape = Qt::BitmapCursor;
+ x->hx = hotX >= 0 ? hotX : bitmap.width() / 2;
+ x->hy = hotY >= 0 ? hotY : bitmap.height() / 2;
+ return x;
+}
+
+Qt::HANDLE QCursor::handle() const
+{
+ if(!QCursorData::initialized)
+ QCursorData::initialize();
+ if(d->type == QCursorData::TYPE_None)
+ d->update();
+ return (Qt::HANDLE)d->mId;
+}
+
+QPoint QCursor::pos()
+{
+ return flipPoint([NSEvent mouseLocation]).toPoint();
+}
+
+void QCursor::setPos(int x, int y)
+{
+#ifdef QT_MAC_USE_COCOA
+ CGPoint pos;
+ pos.x = x;
+ pos.y = y;
+
+ CGEventRef e = CGEventCreateMouseEvent(0, kCGEventMouseMoved, pos, 0);
+ CGEventPost(kCGHIDEventTap, e);
+ CFRelease(e);
+#else
+ CGWarpMouseCursorPosition(CGPointMake(x, y));
+
+ /* I'm not too keen on doing this, but this makes it a lot easier, so I just
+ send the event back through the event system and let it get propagated correctly
+ ideally this would not really need to be faked --Sam
+ */
+ QWidget *widget = 0;
+ if(QWidget *grb = QWidget::mouseGrabber())
+ widget = grb;
+ else
+ widget = QApplication::widgetAt(QPoint(x, y));
+ if(widget) {
+ QMouseEvent me(QMouseEvent::MouseMove, widget->mapFromGlobal(QPoint(x, y)), Qt::NoButton,
+ QApplication::mouseButtons(), QApplication::keyboardModifiers());
+ qt_sendSpontaneousEvent(widget, &me);
+ }
+#endif
+}
+
+void QCursorData::initCursorFromBitmap()
+{
+ NSImage *nsimage;
+ QImage finalCursor(bm->size(), QImage::Format_ARGB32);
+ QImage bmi = bm->toImage().convertToFormat(QImage::Format_RGB32);
+ QImage bmmi = bmm->toImage().convertToFormat(QImage::Format_RGB32);
+ for (int row = 0; row < finalCursor.height(); ++row) {
+ QRgb *bmData = reinterpret_cast<QRgb *>(bmi.scanLine(row));
+ QRgb *bmmData = reinterpret_cast<QRgb *>(bmmi.scanLine(row));
+ QRgb *finalData = reinterpret_cast<QRgb *>(finalCursor.scanLine(row));
+ for (int col = 0; col < finalCursor.width(); ++col) {
+ if (bmmData[col] == 0xff000000 && bmData[col] == 0xffffffff) {
+ finalData[col] = 0xffffffff;
+ } else if (bmmData[col] == 0xff000000 && bmData[col] == 0xffffffff) {
+ finalData[col] = 0x7f000000;
+ } else if (bmmData[col] == 0xffffffff && bmData[col] == 0xffffffff) {
+ finalData[col] = 0x00000000;
+ } else {
+ finalData[col] = 0xff000000;
+ }
+ }
+ }
+ type = QCursorData::TYPE_ImageCursor;
+ curs.cp.my_cursor = true;
+ QPixmap bmCopy = QPixmap::fromImage(finalCursor);
+ NSPoint hotSpot = { hx, hy };
+ nsimage = static_cast<NSImage*>(qt_mac_create_nsimage(bmCopy));
+ curs.cp.nscursor = [[NSCursor alloc] initWithImage:nsimage hotSpot: hotSpot];
+ [nsimage release];
+}
+
+void QCursorData::initCursorFromPixmap()
+{
+ type = QCursorData::TYPE_ImageCursor;
+ curs.cp.my_cursor = true;
+ NSPoint hotSpot = { hx, hy };
+ NSImage *nsimage;
+ nsimage = static_cast<NSImage *>(qt_mac_create_nsimage(pixmap));
+ curs.cp.nscursor = [[NSCursor alloc] initWithImage:nsimage hotSpot: hotSpot];
+ [nsimage release];
+}
+
+void QCursorData::update()
+{
+ if(!QCursorData::initialized)
+ QCursorData::initialize();
+ if(type != QCursorData::TYPE_None)
+ return;
+
+ /* Note to self... ***
+ * mask x data
+ * 0xFF x 0x00 == fully opaque white
+ * 0x00 x 0xFF == xor'd black
+ * 0xFF x 0xFF == fully opaque black
+ * 0x00 x 0x00 == fully transparent
+ */
+
+ if (hx < 0)
+ hx = 0;
+ if (hy < 0)
+ hy = 0;
+
+#define QT_USE_APPROXIMATE_CURSORS
+#ifdef QT_USE_APPROXIMATE_CURSORS
+ static const uchar cur_ver_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x03, 0xc0, 0x07, 0xe0, 0x0f, 0xf0,
+ 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x0f, 0xf0,
+ 0x07, 0xe0, 0x03, 0xc0, 0x01, 0x80, 0x00, 0x00 };
+ static const uchar mcur_ver_bits[] = {
+ 0x00, 0x00, 0x03, 0x80, 0x07, 0xc0, 0x0f, 0xe0, 0x1f, 0xf0, 0x3f, 0xf8,
+ 0x7f, 0xfc, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x7f, 0xfc, 0x3f, 0xf8,
+ 0x1f, 0xf0, 0x0f, 0xe0, 0x07, 0xc0, 0x03, 0x80 };
+
+ static const uchar cur_hor_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x20, 0x18, 0x30,
+ 0x38, 0x38, 0x7f, 0xfc, 0x7f, 0xfc, 0x38, 0x38, 0x18, 0x30, 0x08, 0x20,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ static const uchar mcur_hor_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x40, 0x0c, 0x60, 0x1c, 0x70, 0x3c, 0x78,
+ 0x7f, 0xfc, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0x7f, 0xfc, 0x3c, 0x78,
+ 0x1c, 0x70, 0x0c, 0x60, 0x04, 0x40, 0x00, 0x00 };
+
+ static const uchar cur_fdiag_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf8, 0x00, 0xf8, 0x00, 0x78,
+ 0x00, 0xf8, 0x01, 0xd8, 0x23, 0x88, 0x37, 0x00, 0x3e, 0x00, 0x3c, 0x00,
+ 0x3e, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ static const uchar mcur_fdiag_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0xfc, 0x03, 0xfc, 0x01, 0xfc, 0x00, 0xfc,
+ 0x41, 0xfc, 0x63, 0xfc, 0x77, 0xdc, 0x7f, 0x8c, 0x7f, 0x04, 0x7e, 0x00,
+ 0x7f, 0x00, 0x7f, 0x80, 0x7f, 0xc0, 0x00, 0x00 };
+
+ static const uchar cur_bdiag_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x3e, 0x00,
+ 0x37, 0x00, 0x23, 0x88, 0x01, 0xd8, 0x00, 0xf8, 0x00, 0x78, 0x00, 0xf8,
+ 0x01, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ static const uchar mcur_bdiag_bits[] = {
+ 0x00, 0x00, 0x7f, 0xc0, 0x7f, 0x80, 0x7f, 0x00, 0x7e, 0x00, 0x7f, 0x04,
+ 0x7f, 0x8c, 0x77, 0xdc, 0x63, 0xfc, 0x41, 0xfc, 0x00, 0xfc, 0x01, 0xfc,
+ 0x03, 0xfc, 0x07, 0xfc, 0x00, 0x00, 0x00, 0x00 };
+
+ static const unsigned char cur_up_arrow_bits[] = {
+ 0x00, 0x80, 0x01, 0x40, 0x01, 0x40, 0x02, 0x20, 0x02, 0x20, 0x04, 0x10,
+ 0x04, 0x10, 0x08, 0x08, 0x0f, 0x78, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40,
+ 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0xc0 };
+ static const unsigned char mcur_up_arrow_bits[] = {
+ 0x00, 0x80, 0x01, 0xc0, 0x01, 0xc0, 0x03, 0xe0, 0x03, 0xe0, 0x07, 0xf0,
+ 0x07, 0xf0, 0x0f, 0xf8, 0x0f, 0xf8, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0,
+ 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0 };
+#endif
+ const uchar *cursorData = 0;
+ const uchar *cursorMaskData = 0;
+#ifdef QT_MAC_USE_COCOA
+ switch (cshape) { // map Q cursor to MAC cursor
+ case Qt::BitmapCursor: {
+ if (pixmap.isNull())
+ initCursorFromBitmap();
+ else
+ initCursorFromPixmap();
+ break; }
+ case Qt::BlankCursor: {
+ pixmap = QPixmap(16, 16);
+ pixmap.fill(Qt::transparent);
+ initCursorFromPixmap();
+ break; }
+ case Qt::ArrowCursor: {
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.cp.nscursor = [NSCursor arrowCursor];
+ break; }
+ case Qt::CrossCursor: {
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.cp.nscursor = [NSCursor crosshairCursor];
+ break; }
+ case Qt::WaitCursor: {
+ pixmap = QPixmap(QLatin1String(":/trolltech/mac/cursors/images/spincursor.png"));
+ initCursorFromPixmap();
+ break; }
+ case Qt::IBeamCursor: {
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.cp.nscursor = [NSCursor IBeamCursor];
+ break; }
+ case Qt::SizeAllCursor: {
+ pixmap = QPixmap(QLatin1String(":/trolltech/mac/cursors/images/pluscursor.png"));
+ initCursorFromPixmap();
+ break; }
+ case Qt::WhatsThisCursor: { //for now just use the pointing hand
+ case Qt::PointingHandCursor:
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.cp.nscursor = [NSCursor pointingHandCursor];
+ break; }
+ case Qt::BusyCursor: {
+ pixmap = QPixmap(QLatin1String(":/trolltech/mac/cursors/images/waitcursor.png"));
+ initCursorFromPixmap();
+ break; }
+ case Qt::SplitVCursor: {
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.cp.nscursor = [NSCursor resizeUpDownCursor];
+ break; }
+ case Qt::SplitHCursor: {
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.cp.nscursor = [NSCursor resizeLeftRightCursor];
+ break; }
+ case Qt::ForbiddenCursor: {
+ pixmap = QPixmap(QLatin1String(":/trolltech/mac/cursors/images/forbiddencursor.png"));
+ initCursorFromPixmap();
+ break; }
+ case Qt::OpenHandCursor:
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.cp.nscursor = [NSCursor openHandCursor];
+ break;
+ case Qt::ClosedHandCursor:
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.cp.nscursor = [NSCursor closedHandCursor];
+ break;
+ case Qt::DragCopyCursor:
+ type = QCursorData::TYPE_ThemeCursor;
+ if ([NSCursor respondsToSelector:@selector(dragCopyCursor)])
+ curs.cp.nscursor = [NSCursor performSelector:@selector(dragCopyCursor)];
+ break;
+ case Qt::DragMoveCursor:
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.cp.nscursor = [NSCursor arrowCursor];
+ break;
+ case Qt::DragLinkCursor:
+ type = QCursorData::TYPE_ThemeCursor;
+ if ([NSCursor respondsToSelector:@selector(dragLinkCursor)])
+ curs.cp.nscursor = [NSCursor performSelector:@selector(dragLinkCursor)];
+ break;
+#define QT_USE_APPROXIMATE_CURSORS
+#ifdef QT_USE_APPROXIMATE_CURSORS
+ case Qt::SizeVerCursor:
+ cursorData = cur_ver_bits;
+ cursorMaskData = mcur_ver_bits;
+ hx = hy = 8;
+ break;
+ case Qt::SizeHorCursor:
+ cursorData = cur_hor_bits;
+ cursorMaskData = mcur_hor_bits;
+ hx = hy = 8;
+ break;
+ case Qt::SizeBDiagCursor:
+ cursorData = cur_fdiag_bits;
+ cursorMaskData = mcur_fdiag_bits;
+ hx = hy = 8;
+ break;
+ case Qt::SizeFDiagCursor:
+ cursorData = cur_bdiag_bits;
+ cursorMaskData = mcur_bdiag_bits;
+ hx = hy = 8;
+ break;
+ case Qt::UpArrowCursor:
+ cursorData = cur_up_arrow_bits;
+ cursorMaskData = mcur_up_arrow_bits;
+ hx = 8;
+ break;
+#endif
+ default:
+ qWarning("Qt: QCursor::update: Invalid cursor shape %d", cshape);
+ return;
+ }
+#else
+ // Carbon
+ switch (cshape) { // map Q cursor to MAC cursor
+ case Qt::BitmapCursor: {
+ if (pixmap.isNull())
+ initCursorFromBitmap();
+ else
+ initCursorFromPixmap();
+ break; }
+ case Qt::BlankCursor: {
+ pixmap = QPixmap(16, 16);
+ pixmap.fill(Qt::transparent);
+ initCursorFromPixmap();
+ break; }
+ case Qt::ArrowCursor: {
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.tc.curs = kThemeArrowCursor;
+ break; }
+ case Qt::CrossCursor: {
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.tc.curs = kThemeCrossCursor;
+ break; }
+ case Qt::WaitCursor: {
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.tc.curs = kThemeWatchCursor;
+ break; }
+ case Qt::IBeamCursor: {
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.tc.curs = kThemeIBeamCursor;
+ break; }
+ case Qt::SizeAllCursor: {
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.tc.curs = kThemePlusCursor;
+ break; }
+ case Qt::WhatsThisCursor: { //for now just use the pointing hand
+ case Qt::PointingHandCursor:
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.tc.curs = kThemePointingHandCursor;
+ break; }
+ case Qt::BusyCursor: {
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.tc.curs = kThemeSpinningCursor;
+ break; }
+ case Qt::SplitVCursor: {
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.tc.curs = kThemeResizeUpDownCursor;
+ break; }
+ case Qt::SplitHCursor: {
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.tc.curs = kThemeResizeLeftRightCursor;
+ break; }
+ case Qt::ForbiddenCursor: {
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.tc.curs = kThemeNotAllowedCursor;
+ break; }
+ case Qt::OpenHandCursor:
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.tc.curs = kThemeOpenHandCursor;
+ break;
+ case Qt::ClosedHandCursor:
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.tc.curs = kThemeClosedHandCursor;
+ break;
+ case Qt::DragMoveCursor:
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.tc.curs = kThemeArrowCursor;
+ break;
+ case Qt::DragCopyCursor:
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.tc.curs = kThemeCopyArrowCursor;
+ break;
+ case Qt::DragLinkCursor:
+ type = QCursorData::TYPE_ThemeCursor;
+ curs.tc.curs = kThemeAliasArrowCursor;
+ break;
+#define QT_USE_APPROXIMATE_CURSORS
+#ifdef QT_USE_APPROXIMATE_CURSORS
+ case Qt::SizeVerCursor:
+ cursorData = cur_ver_bits;
+ cursorMaskData = mcur_ver_bits;
+ hx = hy = 8;
+ break;
+ case Qt::SizeHorCursor:
+ cursorData = cur_hor_bits;
+ cursorMaskData = mcur_hor_bits;
+ hx = hy = 8;
+ break;
+ case Qt::SizeBDiagCursor:
+ cursorData = cur_fdiag_bits;
+ cursorMaskData = mcur_fdiag_bits;
+ hx = hy = 8;
+ break;
+ case Qt::SizeFDiagCursor:
+ cursorData = cur_bdiag_bits;
+ cursorMaskData = mcur_bdiag_bits;
+ hx = hy = 8;
+ break;
+ case Qt::UpArrowCursor:
+ cursorData = cur_up_arrow_bits;
+ cursorMaskData = mcur_up_arrow_bits;
+ hx = 8;
+ break;
+#endif
+ default:
+ qWarning("Qt: QCursor::update: Invalid cursor shape %d", cshape);
+ return;
+ }
+#endif
+
+ if (cursorData) {
+ bm = new QBitmap(QBitmap::fromData(QSize(16, 16), cursorData,
+ QImage::Format_Mono));
+ bmm = new QBitmap(QBitmap::fromData(QSize(16, 16), cursorMaskData,
+ QImage::Format_Mono));
+ initCursorFromBitmap();
+ }
+
+#if 0
+ if(type == QCursorData::TYPE_CursPtr && curs.cp.hcurs && curs.cp.my_cursor) {
+ curs.cp.hcurs->hotSpot.h = hx >= 0 ? hx : 8;
+ curs.cp.hcurs->hotSpot.v = hy >= 0 ? hy : 8;
+ }
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/mac/qdesktopwidget_mac.mm b/src/widgets/platforms/mac/qdesktopwidget_mac.mm
new file mode 100644
index 0000000000..0bf213303e
--- /dev/null
+++ b/src/widgets/platforms/mac/qdesktopwidget_mac.mm
@@ -0,0 +1,257 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** Copyright (c) 2007-2008, Apple, Inc.
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**
+** * Redistributions of source code must retain the above copyright notice,
+** this list of conditions and the following disclaimer.
+**
+** * Redistributions in binary form must reproduce the above copyright notice,
+** this list of conditions and the following disclaimer in the documentation
+** and/or other materials provided with the distribution.
+**
+** * Neither the name of Apple, Inc. nor the names of its contributors
+** may be used to endorse or promote products derived from this software
+** without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**
+****************************************************************************/
+
+#import <Cocoa/Cocoa.h>
+
+#include "qapplication.h"
+#include "qdesktopwidget.h"
+#include <private/qt_mac_p.h>
+#include "qwidget_p.h"
+#include <private/qt_cocoa_helpers_mac_p.h>
+#include <private/qdesktopwidget_mac_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QT_USE_NAMESPACE
+
+/*****************************************************************************
+ Externals
+ *****************************************************************************/
+
+/*****************************************************************************
+ QDesktopWidget member functions
+ *****************************************************************************/
+
+Q_GLOBAL_STATIC(QDesktopWidgetImplementation, qdesktopWidgetImplementation)
+
+QDesktopWidgetImplementation::QDesktopWidgetImplementation()
+ : appScreen(0)
+{
+ onResize();
+}
+
+QDesktopWidgetImplementation::~QDesktopWidgetImplementation()
+{
+}
+
+QDesktopWidgetImplementation *QDesktopWidgetImplementation::instance()
+{
+ return qdesktopWidgetImplementation();
+}
+
+QRect QDesktopWidgetImplementation::availableRect(int screenIndex) const
+{
+ if (screenIndex < 0 || screenIndex >= screenCount)
+ screenIndex = appScreen;
+
+ return availableRects[screenIndex].toRect();
+}
+
+QRect QDesktopWidgetImplementation::screenRect(int screenIndex) const
+{
+ if (screenIndex < 0 || screenIndex >= screenCount)
+ screenIndex = appScreen;
+
+ return screenRects[screenIndex].toRect();
+}
+
+void QDesktopWidgetImplementation::onResize()
+{
+ QMacCocoaAutoReleasePool pool;
+ NSArray *displays = [NSScreen screens];
+ screenCount = [displays count];
+
+ screenRects.clear();
+ availableRects.clear();
+ NSRect primaryRect = [[displays objectAtIndex:0] frame];
+ for (int i = 0; i<screenCount; i++) {
+ NSRect r = [[displays objectAtIndex:i] frame];
+ int flippedY = - r.origin.y + // account for position offset and
+ primaryRect.size.height - r.size.height; // height difference.
+ screenRects.append(QRectF(r.origin.x, flippedY,
+ r.size.width, r.size.height));
+
+ r = [[displays objectAtIndex:i] visibleFrame];
+ flippedY = - r.origin.y + // account for position offset and
+ primaryRect.size.height - r.size.height; // height difference.
+ availableRects.append(QRectF(r.origin.x, flippedY,
+ r.size.width, r.size.height));
+ }
+}
+
+
+
+QDesktopWidget::QDesktopWidget()
+ : QWidget(0, Qt::Desktop)
+{
+ setObjectName(QLatin1String("desktop"));
+ setAttribute(Qt::WA_WState_Visible);
+}
+
+QDesktopWidget::~QDesktopWidget()
+{
+}
+
+bool QDesktopWidget::isVirtualDesktop() const
+{
+ return true;
+}
+
+int QDesktopWidget::primaryScreen() const
+{
+ return qdesktopWidgetImplementation()->appScreen;
+}
+
+int QDesktopWidget::numScreens() const
+{
+ return qdesktopWidgetImplementation()->screenCount;
+}
+
+QWidget *QDesktopWidget::screen(int)
+{
+ return this;
+}
+
+const QRect QDesktopWidget::availableGeometry(int screen) const
+{
+ return qdesktopWidgetImplementation()->availableRect(screen);
+}
+
+const QRect QDesktopWidget::screenGeometry(int screen) const
+{
+ return qdesktopWidgetImplementation()->screenRect(screen);
+}
+
+int QDesktopWidget::screenNumber(const QWidget *widget) const
+{
+ QDesktopWidgetImplementation *d = qdesktopWidgetImplementation();
+ if (!widget)
+ return d->appScreen;
+ QRect frame = widget->frameGeometry();
+ if (!widget->isWindow())
+ frame.moveTopLeft(widget->mapToGlobal(QPoint(0,0)));
+ int maxSize = -1, maxScreen = -1;
+ for (int i = 0; i < d->screenCount; ++i) {
+ QRect rr = d->screenRect(i);
+ QRect sect = rr.intersected(frame);
+ int size = sect.width() * sect.height();
+ if (size > maxSize && sect.width() > 0 && sect.height() > 0) {
+ maxSize = size;
+ maxScreen = i;
+ }
+ }
+ return maxScreen;
+}
+
+int QDesktopWidget::screenNumber(const QPoint &point) const
+{
+ QDesktopWidgetImplementation *d = qdesktopWidgetImplementation();
+ int closestScreen = -1;
+ int shortestDistance = INT_MAX;
+ for (int i = 0; i < d->screenCount; ++i) {
+ QRect rr = d->screenRect(i);
+ int thisDistance = QWidgetPrivate::pointToRect(point, rr);
+ if (thisDistance < shortestDistance) {
+ shortestDistance = thisDistance;
+ closestScreen = i;
+ }
+ }
+ return closestScreen;
+}
+
+void QDesktopWidget::resizeEvent(QResizeEvent *)
+{
+ QDesktopWidgetImplementation *d = qdesktopWidgetImplementation();
+
+ const int oldScreenCount = d->screenCount;
+ const QVector<QRectF> oldRects(d->screenRects);
+ const QVector<QRectF> oldWorks(d->availableRects);
+
+ d->onResize();
+
+ for (int i = 0; i < qMin(oldScreenCount, d->screenCount); ++i) {
+ if (oldRects.at(i) != d->screenRects.at(i))
+ emit resized(i);
+ }
+ for (int i = 0; i < qMin(oldScreenCount, d->screenCount); ++i) {
+ if (oldWorks.at(i) != d->availableRects.at(i))
+ emit workAreaResized(i);
+ }
+
+ if (oldScreenCount != d->screenCount)
+ emit screenCountChanged(d->screenCount);
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/mac/qdesktopwidget_mac_p.h b/src/widgets/platforms/mac/qdesktopwidget_mac_p.h
new file mode 100644
index 0000000000..e1ca7e7a37
--- /dev/null
+++ b/src/widgets/platforms/mac/qdesktopwidget_mac_p.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qrect.h>
+
+QT_BEGIN_NAMESPACE
+
+class QDesktopWidgetImplementation
+{
+public:
+ QDesktopWidgetImplementation();
+ ~QDesktopWidgetImplementation();
+ static QDesktopWidgetImplementation *instance();
+
+ int appScreen;
+ int screenCount;
+
+ QVector<QRectF> availableRects;
+ QVector<QRectF> screenRects;
+
+ QRect availableRect(int screenIndex) const;
+ QRect screenRect(int screenIndex) const;
+ void onResize();
+};
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/mac/qdnd_mac.mm b/src/widgets/platforms/mac/qdnd_mac.mm
new file mode 100644
index 0000000000..77926bde23
--- /dev/null
+++ b/src/widgets/platforms/mac/qdnd_mac.mm
@@ -0,0 +1,753 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qapplication.h"
+#ifndef QT_NO_DRAGANDDROP
+#include "qbitmap.h"
+#include "qcursor.h"
+#include "qevent.h"
+#include "qpainter.h"
+#include "qurl.h"
+#include "qwidget.h"
+#include "qfile.h"
+#include "qfileinfo.h"
+#include <stdlib.h>
+#include <string.h>
+#ifndef QT_NO_ACCESSIBILITY
+# include "qaccessible.h"
+#endif
+
+#include <private/qapplication_p.h>
+#include <private/qwidget_p.h>
+#include <private/qdnd_p.h>
+#include <private/qt_mac_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QT_USE_NAMESPACE
+
+QMacDndAnswerRecord qt_mac_dnd_answer_rec;
+
+/*****************************************************************************
+ QDnD debug facilities
+ *****************************************************************************/
+//#define DEBUG_DRAG_EVENTS
+//#define DEBUG_DRAG_PROMISES
+
+/*****************************************************************************
+ QDnD globals
+ *****************************************************************************/
+bool qt_mac_in_drag = false;
+#ifndef QT_MAC_USE_COCOA
+static DragReference qt_mac_current_dragRef = 0;
+#endif
+
+/*****************************************************************************
+ Externals
+ *****************************************************************************/
+extern void qt_mac_send_modifiers_changed(quint32, QObject *); //qapplication_mac.cpp
+extern uint qGlobalPostedEventsCount(); //qapplication.cpp
+extern RgnHandle qt_mac_get_rgn(); // qregion_mac.cpp
+extern void qt_mac_dispose_rgn(RgnHandle); // qregion_mac.cpp
+/*****************************************************************************
+ QDnD utility functions
+ *****************************************************************************/
+
+//default pixmap
+static const int default_pm_hotx = -2;
+static const int default_pm_hoty = -16;
+#ifndef QT_MAC_USE_COCOA
+static const char * const default_pm[] = {
+ "13 9 3 1",
+ ". c None",
+ " c #000000",
+ "X c #FFFFFF",
+ "X X X X X X X",
+ " X X X X X X ",
+ "X ......... X",
+ " X.........X ",
+ "X ......... X",
+ " X.........X ",
+ "X ......... X",
+ " X X X X X X ",
+ "X X X X X X X",
+};
+#endif
+
+//action management
+#ifdef DEBUG_DRAG_EVENTS
+# define MAP_MAC_ENUM(x) x, #x
+#else
+# define MAP_MAC_ENUM(x) x
+#endif
+struct mac_enum_mapper
+{
+ int mac_code;
+ int qt_code;
+#ifdef DEBUG_DRAG_EVENTS
+ char *qt_desc;
+#endif
+};
+#ifndef QT_MAC_USE_COCOA
+static mac_enum_mapper dnd_action_symbols[] = {
+ { kDragActionAlias, MAP_MAC_ENUM(Qt::LinkAction) },
+ { kDragActionMove, MAP_MAC_ENUM(Qt::MoveAction) },
+ { kDragActionGeneric, MAP_MAC_ENUM(Qt::CopyAction) },
+ { kDragActionCopy, MAP_MAC_ENUM(Qt::CopyAction) },
+ { 0, MAP_MAC_ENUM(0) }
+};
+static DragActions qt_mac_dnd_map_qt_actions(Qt::DropActions qActions)
+{
+ DragActions ret = kDragActionNothing;
+ for(int i = 0; dnd_action_symbols[i].qt_code; ++i) {
+ if(qActions & dnd_action_symbols[i].qt_code)
+ ret |= dnd_action_symbols[i].mac_code;
+ }
+ return ret;
+}
+static Qt::DropActions qt_mac_dnd_map_mac_actions(DragActions macActions)
+{
+#ifdef DEBUG_DRAG_EVENTS
+ qDebug("Converting DND ActionList: 0x%lx", macActions);
+#endif
+ Qt::DropActions ret = Qt::IgnoreAction;
+ for(int i = 0; dnd_action_symbols[i].qt_code; ++i) {
+#ifdef DEBUG_DRAG_EVENTS
+ qDebug(" %d) [%s] : %s", i, dnd_action_symbols[i].qt_desc,
+ (macActions & dnd_action_symbols[i].mac_code) ? "true" : "false");
+#endif
+ if(macActions & dnd_action_symbols[i].mac_code)
+ ret |= Qt::DropAction(dnd_action_symbols[i].qt_code);
+ }
+ return ret;
+}
+static Qt::DropAction qt_mac_dnd_map_mac_default_action(DragActions macActions)
+{
+ static Qt::DropAction preferred_actions[] = { Qt::CopyAction, Qt::LinkAction, //in order
+ Qt::MoveAction, Qt::IgnoreAction };
+ Qt::DropAction ret = Qt::IgnoreAction;
+ const Qt::DropActions qtActions = qt_mac_dnd_map_mac_actions(macActions);
+ for(int i = 0; preferred_actions[i] != Qt::IgnoreAction; ++i) {
+ if(qtActions & preferred_actions[i]) {
+ ret = preferred_actions[i];
+ break;
+ }
+ }
+#ifdef DEBUG_DRAG_EVENTS
+ for(int i = 0; dnd_action_symbols[i].qt_code; ++i) {
+ if(dnd_action_symbols[i].qt_code == ret) {
+ qDebug("Got default action: %s [0x%lx]", dnd_action_symbols[i].qt_desc, macActions);
+ break;
+ }
+ }
+#endif
+ return ret;
+}
+static void qt_mac_dnd_update_action(DragReference dragRef) {
+ SInt16 modifiers;
+ GetDragModifiers(dragRef, &modifiers, 0, 0);
+ qt_mac_send_modifiers_changed(modifiers, qApp);
+
+ Qt::DropAction qtAction = Qt::IgnoreAction;
+ {
+ DragActions macAllowed = kDragActionNothing;
+ GetDragDropAction(dragRef, &macAllowed);
+ Qt::DropActions qtAllowed = qt_mac_dnd_map_mac_actions(macAllowed);
+ qtAction = QDragManager::self()->defaultAction(qtAllowed, QApplication::keyboardModifiers());
+#if 1
+ if(!(qtAllowed & qtAction))
+ qtAction = qt_mac_dnd_map_mac_default_action(macAllowed);
+#endif
+ }
+ DragActions macAction = qt_mac_dnd_map_qt_actions(qtAction);
+
+ DragActions currentActions;
+ GetDragDropAction(dragRef, &currentActions);
+ if(currentActions != macAction) {
+ SetDragDropAction(dragRef, macAction);
+ QDragManager::self()->emitActionChanged(qtAction);
+ }
+}
+#endif // !QT_MAC_USE_COCOA
+
+/*****************************************************************************
+ DnD functions
+ *****************************************************************************/
+bool QDropData::hasFormat_sys(const QString &mime) const
+{
+#ifndef QT_MAC_USE_COCOA
+ OSPasteboardRef board;
+ if(GetDragPasteboard(qt_mac_current_dragRef, &board) != noErr) {
+ qDebug("DnD: Cannot get PasteBoard!");
+ return false;
+ }
+ return QMacPasteboard(board, QMacPasteboardMime::MIME_DND).hasFormat(mime);
+#else
+ Q_UNUSED(mime);
+ return false;
+#endif // !QT_MAC_USE_COCOA
+}
+
+QVariant QDropData::retrieveData_sys(const QString &mime, QVariant::Type type) const
+{
+#ifndef QT_MAC_USE_COCOA
+ OSPasteboardRef board;
+ if(GetDragPasteboard(qt_mac_current_dragRef, &board) != noErr) {
+ qDebug("DnD: Cannot get PasteBoard!");
+ return QVariant();
+ }
+ return QMacPasteboard(board, QMacPasteboardMime::MIME_DND).retrieveData(mime, type);
+#else
+ Q_UNUSED(mime);
+ Q_UNUSED(type);
+ return QVariant();
+#endif // !QT_MAC_USE_COCOA
+}
+
+QStringList QDropData::formats_sys() const
+{
+#ifndef QT_MAC_USE_COCOA
+ OSPasteboardRef board;
+ if(GetDragPasteboard(qt_mac_current_dragRef, &board) != noErr) {
+ qDebug("DnD: Cannot get PasteBoard!");
+ return QStringList();
+ }
+ return QMacPasteboard(board, QMacPasteboardMime::MIME_DND).formats();
+#else
+ return QStringList();
+#endif
+}
+
+void QDragManager::timerEvent(QTimerEvent*)
+{
+}
+
+bool QDragManager::eventFilter(QObject *, QEvent *)
+{
+ return false;
+}
+
+void QDragManager::updateCursor()
+{
+}
+
+void QDragManager::cancel(bool)
+{
+ if(object) {
+ beingCancelled = true;
+ object = 0;
+ }
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(this, 0, QAccessible::DragDropEnd);
+#endif
+}
+
+void QDragManager::move(const QPoint &)
+{
+}
+
+void QDragManager::drop()
+{
+}
+
+/**
+ If a drop action is already set on the carbon event
+ (from e.g. an earlier enter event), we insert the same
+ action on the new Qt event that has yet to be sendt.
+*/
+static inline bool qt_mac_set_existing_drop_action(const DragRef &dragRef, QDropEvent &event)
+{
+#ifndef QT_MAC_USE_COCOA
+ DragActions currentAction = kDragActionNothing;
+ OSStatus err = GetDragDropAction(dragRef, &currentAction);
+ if (err == noErr && currentAction != kDragActionNothing) {
+ // This looks a bit evil, but we only ever set one action, so it's OK.
+ event.setDropAction(Qt::DropAction(int(qt_mac_dnd_map_mac_actions(currentAction))));
+ return true;
+ }
+#else
+ Q_UNUSED(dragRef);
+ Q_UNUSED(event);
+#endif
+ return false;
+}
+
+/**
+ If an answer rect has been set on the event (after being sent
+ to the global event processor), we store that rect so we can
+ check if the mouse is in the same area upon next drag move event.
+*/
+void qt_mac_copy_answer_rect(const QDragMoveEvent &event)
+{
+ if (!event.answerRect().isEmpty()) {
+ qt_mac_dnd_answer_rec.rect = event.answerRect();
+ qt_mac_dnd_answer_rec.buttons = event.mouseButtons();
+ qt_mac_dnd_answer_rec.modifiers = event.keyboardModifiers();
+ qt_mac_dnd_answer_rec.lastAction = event.dropAction();
+ }
+}
+
+bool qt_mac_mouse_inside_answer_rect(QPoint mouse)
+{
+ if (!qt_mac_dnd_answer_rec.rect.isEmpty()
+ && qt_mac_dnd_answer_rec.rect.contains(mouse)
+ && QApplication::mouseButtons() == qt_mac_dnd_answer_rec.buttons
+ && QApplication::keyboardModifiers() == qt_mac_dnd_answer_rec.modifiers)
+ return true;
+ else
+ return false;
+}
+
+bool QWidgetPrivate::qt_mac_dnd_event(uint kind, DragRef dragRef)
+{
+#ifndef QT_MAC_USE_COCOA
+ Q_Q(QWidget);
+ qt_mac_current_dragRef = dragRef;
+ if (kind != kEventControlDragLeave)
+ qt_mac_dnd_update_action(dragRef);
+
+ Point mouse;
+ GetDragMouse(dragRef, &mouse, 0L);
+ if(!mouse.h && !mouse.v)
+ GetGlobalMouse(&mouse);
+
+ if(QApplicationPrivate::modalState()) {
+ for(QWidget *modal = q; modal; modal = modal->parentWidget()) {
+ if(modal->isWindow()) {
+ if(modal != QApplication::activeModalWidget())
+ return noErr;
+ break;
+ }
+ }
+ }
+
+ //lookup the possible actions
+ DragActions allowed = kDragActionNothing;
+ GetDragAllowableActions(dragRef, &allowed);
+ Qt::DropActions qtAllowed = qt_mac_dnd_map_mac_actions(allowed);
+
+ //lookup the source dragAccepted
+ QMimeData *dropdata = QDragManager::self()->dropData;
+ if(QDragManager::self()->source())
+ dropdata = QDragManager::self()->dragPrivate()->data;
+
+ // 'interrestedInDrag' should end up being 'true' if a later drop
+ // will be accepted by the widget for the current mouse position
+ bool interrestedInDrag = true;
+
+ //Dispatch events
+ if (kind == kEventControlDragWithin) {
+ if (qt_mac_mouse_inside_answer_rect(q->mapFromGlobal(QPoint(mouse.h, mouse.v))))
+ return qt_mac_dnd_answer_rec.lastAction == Qt::IgnoreAction;
+ else
+ qt_mac_dnd_answer_rec.clear();
+
+ QDragMoveEvent qDMEvent(q->mapFromGlobal(QPoint(mouse.h, mouse.v)), qtAllowed, dropdata,
+ QApplication::mouseButtons(), QApplication::keyboardModifiers());
+
+ // Accept the event by default if a
+ // drag action exists on the carbon event
+ if (qt_mac_set_existing_drop_action(dragRef, qDMEvent))
+ qDMEvent.accept();
+
+ QApplication::sendEvent(q, &qDMEvent);
+ if (!qDMEvent.isAccepted() || qDMEvent.dropAction() == Qt::IgnoreAction)
+ interrestedInDrag = false;
+
+ qt_mac_copy_answer_rect(qDMEvent);
+ SetDragDropAction(dragRef, qt_mac_dnd_map_qt_actions(qDMEvent.dropAction()));
+
+ } else if (kind == kEventControlDragEnter) {
+ qt_mac_dnd_answer_rec.clear();
+
+ QDragEnterEvent qDEEvent(q->mapFromGlobal(QPoint(mouse.h, mouse.v)), qtAllowed, dropdata,
+ QApplication::mouseButtons(), QApplication::keyboardModifiers());
+ qt_mac_set_existing_drop_action(dragRef, qDEEvent);
+ QApplication::sendEvent(q, &qDEEvent);
+ SetDragDropAction(dragRef, qt_mac_dnd_map_qt_actions(qDEEvent.dropAction()));
+
+ if (!qDEEvent.isAccepted())
+ // The widget is simply not interested in this
+ // drag. So tell carbon this by returning 'false'. We will then
+ // not receive any further move, drop or leave events for this widget.
+ return false;
+ else {
+ // Documentation states that a drag move event is sent immediately after
+ // a drag enter event. So we do that. This will honor widgets overriding
+ // 'dragMoveEvent' only, and not 'dragEnterEvent'
+ QDragMoveEvent qDMEvent(q->mapFromGlobal(QPoint(mouse.h, mouse.v)), qtAllowed, dropdata,
+ QApplication::mouseButtons(), QApplication::keyboardModifiers());
+ qDMEvent.accept(); // accept by default, since enter event was accepted.
+ qDMEvent.setDropAction(qDEEvent.dropAction());
+ QApplication::sendEvent(q, &qDMEvent);
+ if (!qDMEvent.isAccepted() || qDMEvent.dropAction() == Qt::IgnoreAction)
+ interrestedInDrag = false;
+
+ qt_mac_copy_answer_rect(qDMEvent);
+ SetDragDropAction(dragRef, qt_mac_dnd_map_qt_actions(qDMEvent.dropAction()));
+ }
+
+ } else if(kind == kEventControlDragLeave) {
+ QDragLeaveEvent de;
+ QApplication::sendEvent(q, &de);
+ } else if(kind == kEventControlDragReceive) {
+ QDropEvent de(q->mapFromGlobal(QPoint(mouse.h, mouse.v)), qtAllowed, dropdata,
+ QApplication::mouseButtons(), QApplication::keyboardModifiers());
+ if(QDragManager::self()->object)
+ QDragManager::self()->dragPrivate()->target = q;
+ QApplication::sendEvent(q, &de);
+ if(!de.isAccepted()) {
+ interrestedInDrag = false;
+ SetDragDropAction(dragRef, kDragActionNothing);
+ } else {
+ if(QDragManager::self()->object)
+ QDragManager::self()->dragPrivate()->executed_action = de.dropAction();
+ SetDragDropAction(dragRef, qt_mac_dnd_map_qt_actions(de.dropAction()));
+ }
+ }
+
+#ifdef DEBUG_DRAG_EVENTS
+ {
+ const char *desc = 0;
+ switch(kind) {
+ case kEventControlDragWithin: desc = "DragMove"; break;
+ case kEventControlDragEnter: desc = "DragEnter"; break;
+ case kEventControlDragLeave: desc = "DragLeave"; break;
+ case kEventControlDragReceive: desc = "DragDrop"; break;
+ }
+ if(desc) {
+ QPoint pos(q->mapFromGlobal(QPoint(mouse.h, mouse.v)));
+ qDebug("Sending <%s>(%d, %d) event to %p(%s::%s) [%d] (%p)",
+ desc, pos.x(), pos.y(), q, q->metaObject()->className(),
+ q->objectName().toLocal8Bit().constData(), ret, dragRef);
+ }
+ }
+#endif
+
+ //set the cursor
+ bool found_cursor = false;
+ if(kind == kEventControlDragWithin || kind == kEventControlDragEnter) {
+ ThemeCursor cursor = kThemeNotAllowedCursor;
+ found_cursor = true;
+ if (interrestedInDrag) {
+ DragActions action = kDragActionNothing;
+ GetDragDropAction(dragRef, &action);
+ switch(qt_mac_dnd_map_mac_default_action(action)) {
+ case Qt::IgnoreAction:
+ cursor = kThemeNotAllowedCursor;
+ break;
+ case Qt::MoveAction:
+ cursor = kThemeArrowCursor;
+ break;
+ case Qt::CopyAction:
+ cursor = kThemeCopyArrowCursor;
+ break;
+ case Qt::LinkAction:
+ cursor = kThemeAliasArrowCursor;
+ break;
+ default:
+ cursor = kThemeNotAllowedCursor;
+ break;
+ }
+ }
+ SetThemeCursor(cursor);
+ }
+ if(found_cursor) {
+ qt_mac_set_cursor(0); //just use our's
+ } else {
+ QCursor cursor(Qt::ArrowCursor);
+ if(qApp && qApp->overrideCursor()) {
+ cursor = *qApp->overrideCursor();
+ } else if(q) {
+ for(QWidget *p = q; p; p = p->parentWidget()) {
+ if(p->isEnabled() && p->testAttribute(Qt::WA_SetCursor)) {
+ cursor = p->cursor();
+ break;
+ }
+ }
+ }
+ qt_mac_set_cursor(&cursor);
+ }
+
+ //idle things
+ if(qGlobalPostedEventsCount()) {
+ QApplication::sendPostedEvents();
+ QApplication::flush();
+ }
+
+ // If this was not a drop, tell carbon that we will be interresed in receiving more
+ // events for the current drag. We do that by returning true.
+ if (kind == kEventControlDragReceive)
+ return interrestedInDrag;
+ else
+ return true;
+#else
+ Q_UNUSED(kind);
+ Q_UNUSED(dragRef);
+ return false;
+#endif // !QT_MAC_USE_COCOA
+}
+
+#ifndef QT_MAC_USE_COCOA
+Qt::DropAction QDragManager::drag(QDrag *o)
+{
+
+ if(qt_mac_in_drag) { //just make sure..
+ qWarning("Qt: Internal error: WH0A, unexpected condition reached");
+ return Qt::IgnoreAction;
+ }
+ if(object == o)
+ return Qt::IgnoreAction;
+ /* At the moment it seems clear that Mac OS X does not want to drag with a non-left button
+ so we just bail early to prevent it */
+ if(!(GetCurrentEventButtonState() & kEventMouseButtonPrimary))
+ return Qt::IgnoreAction;
+
+ if(object) {
+ dragPrivate()->source->removeEventFilter(this);
+ cancel();
+ beingCancelled = false;
+ }
+
+ object = o;
+ dragPrivate()->target = 0;
+
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(this, 0, QAccessible::DragDropStart);
+#endif
+
+ //setup the data
+ QMacPasteboard dragBoard(QMacPasteboardMime::MIME_DND);
+ dragBoard.setMimeData(dragPrivate()->data);
+
+ //create the drag
+ OSErr result;
+ DragRef dragRef;
+ if((result = NewDragWithPasteboard(dragBoard.pasteBoard(), &dragRef)))
+ return Qt::IgnoreAction;
+ //setup the actions
+ DragActions possibleActions = qt_mac_dnd_map_qt_actions(dragPrivate()->possible_actions);
+ SetDragAllowableActions(dragRef, //local
+ possibleActions,
+ true);
+ SetDragAllowableActions(dragRef, //remote (same as local)
+ possibleActions,
+ false);
+
+
+ QPoint hotspot;
+ QPixmap pix = dragPrivate()->pixmap;
+ if(pix.isNull()) {
+ if(dragPrivate()->data->hasText() || dragPrivate()->data->hasUrls()) {
+ //get the string
+ QString s = dragPrivate()->data->hasText() ? dragPrivate()->data->text()
+ : dragPrivate()->data->urls().first().toString();
+ if(s.length() > 26)
+ s = s.left(23) + QChar(0x2026);
+ if(!s.isEmpty()) {
+ //draw it
+ QFont f(qApp->font());
+ f.setPointSize(12);
+ QFontMetrics fm(f);
+ const int width = fm.width(s);
+ const int height = fm.height();
+ if(width > 0 && height > 0) {
+ QPixmap tmp(width, height);
+ QPainter p(&tmp);
+ p.fillRect(0, 0, tmp.width(), tmp.height(), Qt::color0);
+ p.setPen(Qt::color1);
+ p.setFont(f);
+ p.drawText(0, fm.ascent(), s);
+ p.end();
+ //save it
+ pix = tmp;
+ hotspot = QPoint(tmp.width() / 2, tmp.height() / 2);
+ }
+ }
+ } else {
+ pix = QPixmap(default_pm);
+ hotspot = QPoint(default_pm_hotx, default_pm_hoty);
+ }
+ } else {
+ hotspot = dragPrivate()->hotspot;
+ }
+
+ //so we must fake an event
+ EventRecord fakeEvent;
+ GetGlobalMouse(&(fakeEvent.where));
+ fakeEvent.message = 0;
+ fakeEvent.what = mouseDown;
+ fakeEvent.when = EventTimeToTicks(GetCurrentEventTime());
+ fakeEvent.modifiers = GetCurrentKeyModifiers();
+ if(GetCurrentEventButtonState() & 2)
+ fakeEvent.modifiers |= controlKey;
+
+ //find the hotspot in relation to the pixmap
+ Point boundsPoint;
+ boundsPoint.h = fakeEvent.where.h - hotspot.x();
+ boundsPoint.v = fakeEvent.where.v - hotspot.y();
+ Rect boundsRect;
+ SetRect(&boundsRect, boundsPoint.h, boundsPoint.v, boundsPoint.h + pix.width(), boundsPoint.v + pix.height());
+
+ //set the drag image
+ QRegion dragRegion(boundsPoint.h, boundsPoint.v, pix.width(), pix.height()), pixRegion;
+ if(!pix.isNull()) {
+ HIPoint hipoint = { -hotspot.x(), -hotspot.y() };
+ CGImageRef img = (CGImageRef)pix.macCGHandle();
+ SetDragImageWithCGImage(dragRef, img, &hipoint, 0);
+ CGImageRelease(img);
+ }
+
+ SetDragItemBounds(dragRef, (ItemReference)1 , &boundsRect);
+ { //do the drag
+ qt_mac_in_drag = true;
+ qt_mac_dnd_update_action(dragRef);
+ result = TrackDrag(dragRef, &fakeEvent, QMacSmartQuickDrawRegion(dragRegion.toQDRgn()));
+ qt_mac_in_drag = false;
+ }
+ object = 0;
+ if(result == noErr) {
+ // Check if the receiver points us to
+ // a file location. If so, we need to do
+ // the file copy/move ourselves:
+ QCFType<CFURLRef> pasteLocation = 0;
+ PasteboardCopyPasteLocation(dragBoard.pasteBoard(), &pasteLocation);
+ if (pasteLocation){
+ Qt::DropAction action = o->d_func()->defaultDropAction;
+ if (action == Qt::IgnoreAction){
+ if (o->d_func()->possible_actions & Qt::MoveAction)
+ action = Qt::MoveAction;
+ else if (o->d_func()->possible_actions & Qt::CopyAction)
+ action = Qt::CopyAction;
+
+ }
+ bool atleastOne = false;
+ QList<QUrl> urls = o->mimeData()->urls();
+ for (int i = 0; i < urls.size(); ++i){
+ QUrl fromUrl = urls.at(i);
+ QString filename = QFileInfo(fromUrl.path()).fileName();
+ QUrl toUrl(QCFString::toQString(CFURLGetString(pasteLocation)) + filename);
+ if (action == Qt::MoveAction){
+ if (QFile::rename(fromUrl.path(), toUrl.path()))
+ atleastOne = true;
+ } else if (action == Qt::CopyAction){
+ if (QFile::copy(fromUrl.path(), toUrl.path()))
+ atleastOne = true;
+ }
+ }
+ if (atleastOne){
+ DisposeDrag(dragRef);
+ o->setMimeData(0);
+ o->deleteLater();
+ return action;
+ }
+ }
+
+ DragActions ret = kDragActionNothing;
+ GetDragDropAction(dragRef, &ret);
+ DisposeDrag(dragRef); //cleanup
+ o->setMimeData(0);
+ o->deleteLater();
+ return qt_mac_dnd_map_mac_default_action(ret);
+ }
+ DisposeDrag(dragRef); //cleanup
+ return Qt::IgnoreAction;
+}
+#endif
+
+void QDragManager::updatePixmap()
+{
+}
+
+QCocoaDropData::QCocoaDropData(CFStringRef pasteboard)
+ : QInternalMimeData()
+{
+ NSString* pasteboardName = (NSString*)pasteboard;
+ [pasteboardName retain];
+ dropPasteboard = pasteboard;
+}
+
+QCocoaDropData::~QCocoaDropData()
+{
+ NSString* pasteboardName = (NSString*)dropPasteboard;
+ [pasteboardName release];
+}
+
+QStringList QCocoaDropData::formats_sys() const
+{
+ QStringList formats;
+ OSPasteboardRef board;
+ if (PasteboardCreate(dropPasteboard, &board) != noErr) {
+ qDebug("DnD: Cannot get PasteBoard!");
+ return formats;
+ }
+ formats = QMacPasteboard(board, QMacPasteboardMime::MIME_DND).formats();
+ return formats;
+}
+
+QVariant QCocoaDropData::retrieveData_sys(const QString &mimeType, QVariant::Type type) const
+{
+ QVariant data;
+ OSPasteboardRef board;
+ if (PasteboardCreate(dropPasteboard, &board) != noErr) {
+ qDebug("DnD: Cannot get PasteBoard!");
+ return data;
+ }
+ data = QMacPasteboard(board, QMacPasteboardMime::MIME_DND).retrieveData(mimeType, type);
+ CFRelease(board);
+ return data;
+}
+
+bool QCocoaDropData::hasFormat_sys(const QString &mimeType) const
+{
+ bool has = false;
+ OSPasteboardRef board;
+ if (PasteboardCreate(dropPasteboard, &board) != noErr) {
+ qDebug("DnD: Cannot get PasteBoard!");
+ return has;
+ }
+ has = QMacPasteboard(board, QMacPasteboardMime::MIME_DND).hasFormat(mimeType);
+ CFRelease(board);
+ return has;
+}
+
+#endif // QT_NO_DRAGANDDROP
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/mac/qeventdispatcher_mac.mm b/src/widgets/platforms/mac/qeventdispatcher_mac.mm
new file mode 100644
index 0000000000..f90fb5a1d5
--- /dev/null
+++ b/src/widgets/platforms/mac/qeventdispatcher_mac.mm
@@ -0,0 +1,1200 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** Copyright (c) 2007-2008, Apple, Inc.
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**
+** * Redistributions of source code must retain the above copyright notice,
+** this list of conditions and the following disclaimer.
+**
+** * Redistributions in binary form must reproduce the above copyright notice,
+** this list of conditions and the following disclaimer in the documentation
+** and/or other materials provided with the distribution.
+**
+** * Neither the name of Apple, Inc. nor the names of its contributors
+** may be used to endorse or promote products derived from this software
+** without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+#include "private/qt_mac_p.h"
+#include "qeventdispatcher_mac_p.h"
+#include "qapplication.h"
+#include "qevent.h"
+#include "qdialog.h"
+#include "qhash.h"
+#include "qsocketnotifier.h"
+#include "private/qwidget_p.h"
+#include "private/qthread_p.h"
+#include "private/qapplication_p.h"
+
+#include <private/qcocoaapplication_mac_p.h>
+#include "private/qt_cocoa_helpers_mac_p.h"
+
+#ifndef QT_NO_THREAD
+# include "qmutex.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+QT_USE_NAMESPACE
+
+/*****************************************************************************
+ Externals
+ *****************************************************************************/
+extern void qt_event_request_timer(MacTimerInfo *); //qapplication_mac.cpp
+extern MacTimerInfo *qt_event_get_timer(EventRef); //qapplication_mac.cpp
+extern void qt_event_request_select(QEventDispatcherMac *); //qapplication_mac.cpp
+extern void qt_event_request_updates(); //qapplication_mac.cpp
+extern OSWindowRef qt_mac_window_for(const QWidget *); //qwidget_mac.cpp
+extern bool qt_is_gui_used; //qapplication.cpp
+extern bool qt_sendSpontaneousEvent(QObject*, QEvent*); // qapplication.cpp
+extern bool qt_mac_is_macsheet(const QWidget *); //qwidget_mac.cpp
+
+static inline CFRunLoopRef mainRunLoop()
+{
+#ifndef QT_MAC_USE_COCOA
+ return reinterpret_cast<CFRunLoopRef>(const_cast<void *>(GetCFRunLoopFromEventLoop(GetMainEventLoop())));
+#else
+ return CFRunLoopGetMain();
+#endif
+}
+
+/*****************************************************************************
+ Timers stuff
+ *****************************************************************************/
+
+/* timer call back */
+void QEventDispatcherMacPrivate::activateTimer(CFRunLoopTimerRef, void *info)
+{
+ int timerID =
+#ifdef Q_OS_MAC64
+ qint64(info);
+#else
+ int(info);
+#endif
+
+ MacTimerInfo *tmr;
+ tmr = macTimerHash.value(timerID);
+ if (tmr == 0 || tmr->pending == true)
+ return; // Can't send another timer event if it's pending.
+
+
+ if (blockSendPostedEvents) {
+ QCoreApplication::postEvent(tmr->obj, new QTimerEvent(tmr->id));
+ } else {
+ tmr->pending = true;
+ QTimerEvent e(tmr->id);
+ qt_sendSpontaneousEvent(tmr->obj, &e);
+ // Get the value again in case the timer gets unregistered during the sendEvent.
+ tmr = macTimerHash.value(timerID);
+ if (tmr != 0)
+ tmr->pending = false;
+ }
+
+}
+
+void QEventDispatcherMac::registerTimer(int timerId, int interval, QObject *obj)
+{
+#ifndef QT_NO_DEBUG
+ if (timerId < 1 || interval < 0 || !obj) {
+ qWarning("QEventDispatcherMac::registerTimer: invalid arguments");
+ return;
+ } else if (obj->thread() != thread() || thread() != QThread::currentThread()) {
+ qWarning("QObject::startTimer: timers cannot be started from another thread");
+ return;
+ }
+#endif
+
+ MacTimerInfo *t = new MacTimerInfo();
+ t->id = timerId;
+ t->interval = interval;
+ t->obj = obj;
+ t->runLoopTimer = 0;
+ t->pending = false;
+
+ CFAbsoluteTime fireDate = CFAbsoluteTimeGetCurrent();
+ CFTimeInterval cfinterval = qMax(CFTimeInterval(interval) / 1000, 0.0000001);
+ fireDate += cfinterval;
+ QEventDispatcherMacPrivate::macTimerHash.insert(timerId, t);
+ CFRunLoopTimerContext info = { 0, (void *)timerId, 0, 0, 0 };
+ t->runLoopTimer = CFRunLoopTimerCreate(0, fireDate, cfinterval, 0, 0,
+ QEventDispatcherMacPrivate::activateTimer, &info);
+ if (t->runLoopTimer == 0) {
+ qFatal("QEventDispatcherMac::registerTimer: Cannot create timer");
+ }
+ CFRunLoopAddTimer(mainRunLoop(), t->runLoopTimer, kCFRunLoopCommonModes);
+}
+
+bool QEventDispatcherMac::unregisterTimer(int identifier)
+{
+#ifndef QT_NO_DEBUG
+ if (identifier < 1) {
+ qWarning("QEventDispatcherMac::unregisterTimer: invalid argument");
+ return false;
+ } else if (thread() != QThread::currentThread()) {
+ qWarning("QObject::killTimer: timers cannot be stopped from another thread");
+ return false;
+ }
+#endif
+ if (identifier <= 0)
+ return false; // not init'd or invalid timer
+
+ MacTimerInfo *timerInfo = QEventDispatcherMacPrivate::macTimerHash.take(identifier);
+ if (timerInfo == 0)
+ return false;
+
+ if (!QObjectPrivate::get(timerInfo->obj)->inThreadChangeEvent)
+ QAbstractEventDispatcherPrivate::releaseTimerId(identifier);
+ CFRunLoopTimerInvalidate(timerInfo->runLoopTimer);
+ CFRelease(timerInfo->runLoopTimer);
+ delete timerInfo;
+
+ return true;
+}
+
+bool QEventDispatcherMac::unregisterTimers(QObject *obj)
+{
+#ifndef QT_NO_DEBUG
+ if (!obj) {
+ qWarning("QEventDispatcherMac::unregisterTimers: invalid argument");
+ return false;
+ } else if (obj->thread() != thread() || thread() != QThread::currentThread()) {
+ qWarning("QObject::killTimers: timers cannot be stopped from another thread");
+ return false;
+ }
+#endif
+
+ MacTimerHash::iterator it = QEventDispatcherMacPrivate::macTimerHash.begin();
+ while (it != QEventDispatcherMacPrivate::macTimerHash.end()) {
+ MacTimerInfo *timerInfo = it.value();
+ if (timerInfo->obj != obj) {
+ ++it;
+ } else {
+ if (!QObjectPrivate::get(timerInfo->obj)->inThreadChangeEvent)
+ QAbstractEventDispatcherPrivate::releaseTimerId(timerInfo->id);
+ CFRunLoopTimerInvalidate(timerInfo->runLoopTimer);
+ CFRelease(timerInfo->runLoopTimer);
+ delete timerInfo;
+ it = QEventDispatcherMacPrivate::macTimerHash.erase(it);
+ }
+ }
+ return true;
+}
+
+QList<QEventDispatcherMac::TimerInfo>
+QEventDispatcherMac::registeredTimers(QObject *object) const
+{
+ if (!object) {
+ qWarning("QEventDispatcherMac:registeredTimers: invalid argument");
+ return QList<TimerInfo>();
+ }
+
+ QList<TimerInfo> list;
+
+ MacTimerHash::const_iterator it = QEventDispatcherMacPrivate::macTimerHash.constBegin();
+ while (it != QEventDispatcherMacPrivate::macTimerHash.constEnd()) {
+ MacTimerInfo *t = it.value();
+ if (t->obj == object)
+ list << TimerInfo(t->id, t->interval);
+ ++it;
+ }
+ return list;
+}
+
+/**************************************************************************
+ Socket Notifiers
+ *************************************************************************/
+void qt_mac_socket_callback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef,
+ const void *, void *info) {
+ QEventDispatcherMacPrivate *const eventDispatcher
+ = static_cast<QEventDispatcherMacPrivate *>(info);
+ int nativeSocket = CFSocketGetNative(s);
+ MacSocketInfo *socketInfo = eventDispatcher->macSockets.value(nativeSocket);
+ QEvent notifierEvent(QEvent::SockAct);
+
+ // There is a race condition that happen where we disable the notifier and
+ // the kernel still has a notification to pass on. We then get this
+ // notification after we've successfully disabled the CFSocket, but our Qt
+ // notifier is now gone. The upshot is we have to check the notifier
+ // everytime.
+ if (callbackType == kCFSocketReadCallBack) {
+ if (socketInfo->readNotifier)
+ QApplication::sendEvent(socketInfo->readNotifier, &notifierEvent);
+ } else if (callbackType == kCFSocketWriteCallBack) {
+ if (socketInfo->writeNotifier)
+ QApplication::sendEvent(socketInfo->writeNotifier, &notifierEvent);
+ }
+}
+
+/*
+ Adds a loop source for the given socket to the current run loop.
+*/
+CFRunLoopSourceRef qt_mac_add_socket_to_runloop(const CFSocketRef socket)
+{
+ CFRunLoopSourceRef loopSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0);
+ if (!loopSource)
+ return 0;
+
+ CFRunLoopAddSource(mainRunLoop(), loopSource, kCFRunLoopCommonModes);
+ return loopSource;
+}
+
+/*
+ Removes the loop source for the given socket from the current run loop.
+*/
+void qt_mac_remove_socket_from_runloop(const CFSocketRef socket, CFRunLoopSourceRef runloop)
+{
+ Q_ASSERT(runloop);
+ CFRunLoopRemoveSource(mainRunLoop(), runloop, kCFRunLoopCommonModes);
+ CFSocketDisableCallBacks(socket, kCFSocketReadCallBack);
+ CFSocketDisableCallBacks(socket, kCFSocketWriteCallBack);
+ CFRunLoopSourceInvalidate(runloop);
+}
+
+/*
+ Register a QSocketNotifier with the mac event system by creating a CFSocket with
+ with a read/write callback.
+
+ Qt has separate socket notifiers for reading and writing, but on the mac there is
+ a limitation of one CFSocket object for each native socket.
+*/
+void QEventDispatcherMac::registerSocketNotifier(QSocketNotifier *notifier)
+{
+ Q_ASSERT(notifier);
+ int nativeSocket = notifier->socket();
+ int type = notifier->type();
+#ifndef QT_NO_DEBUG
+ if (nativeSocket < 0 || nativeSocket > FD_SETSIZE) {
+ qWarning("QSocketNotifier: Internal error");
+ return;
+ } else if (notifier->thread() != thread()
+ || thread() != QThread::currentThread()) {
+ qWarning("QSocketNotifier: socket notifiers cannot be enabled from another thread");
+ return;
+ }
+#endif
+
+ Q_D(QEventDispatcherMac);
+
+ if (type == QSocketNotifier::Exception) {
+ qWarning("QSocketNotifier::Exception is not supported on Mac OS X");
+ return;
+ }
+
+ // Check if we have a CFSocket for the native socket, create one if not.
+ MacSocketInfo *socketInfo = d->macSockets.value(nativeSocket);
+ if (!socketInfo) {
+ socketInfo = new MacSocketInfo();
+
+ // Create CFSocket, specify that we want both read and write callbacks (the callbacks
+ // are enabled/disabled later on).
+ const int callbackTypes = kCFSocketReadCallBack | kCFSocketWriteCallBack;
+ CFSocketContext context = {0, d, 0, 0, 0};
+ socketInfo->socket = CFSocketCreateWithNative(kCFAllocatorDefault, nativeSocket, callbackTypes, qt_mac_socket_callback, &context);
+ if (CFSocketIsValid(socketInfo->socket) == false) {
+ qWarning("QEventDispatcherMac::registerSocketNotifier: Failed to create CFSocket");
+ return;
+ }
+
+ CFOptionFlags flags = CFSocketGetSocketFlags(socketInfo->socket);
+ flags |= kCFSocketAutomaticallyReenableWriteCallBack; //QSocketNotifier stays enabled after a write
+ flags &= ~kCFSocketCloseOnInvalidate; //QSocketNotifier doesn't close the socket upon destruction/invalidation
+ CFSocketSetSocketFlags(socketInfo->socket, flags);
+
+ // Add CFSocket to runloop.
+ if(!(socketInfo->runloop = qt_mac_add_socket_to_runloop(socketInfo->socket))) {
+ qWarning("QEventDispatcherMac::registerSocketNotifier: Failed to add CFSocket to runloop");
+ CFSocketInvalidate(socketInfo->socket);
+ CFRelease(socketInfo->socket);
+ return;
+ }
+
+ // Disable both callback types by default. This must be done after
+ // we add the CFSocket to the runloop, or else these calls will have
+ // no effect.
+ CFSocketDisableCallBacks(socketInfo->socket, kCFSocketReadCallBack);
+ CFSocketDisableCallBacks(socketInfo->socket, kCFSocketWriteCallBack);
+
+ d->macSockets.insert(nativeSocket, socketInfo);
+ }
+
+ // Increment read/write counters and select enable callbacks if necessary.
+ if (type == QSocketNotifier::Read) {
+ Q_ASSERT(socketInfo->readNotifier == 0);
+ socketInfo->readNotifier = notifier;
+ CFSocketEnableCallBacks(socketInfo->socket, kCFSocketReadCallBack);
+ } else if (type == QSocketNotifier::Write) {
+ Q_ASSERT(socketInfo->writeNotifier == 0);
+ socketInfo->writeNotifier = notifier;
+ CFSocketEnableCallBacks(socketInfo->socket, kCFSocketWriteCallBack);
+ }
+}
+
+/*
+ Unregister QSocketNotifer. The CFSocket correspoding to this notifier is
+ removed from the runloop of this is the last notifier that users
+ that CFSocket.
+*/
+void QEventDispatcherMac::unregisterSocketNotifier(QSocketNotifier *notifier)
+{
+ Q_ASSERT(notifier);
+ int nativeSocket = notifier->socket();
+ int type = notifier->type();
+#ifndef QT_NO_DEBUG
+ if (nativeSocket < 0 || nativeSocket > FD_SETSIZE) {
+ qWarning("QSocketNotifier: Internal error");
+ return;
+ } else if (notifier->thread() != thread() || thread() != QThread::currentThread()) {
+ qWarning("QSocketNotifier: socket notifiers cannot be disabled from another thread");
+ return;
+ }
+#endif
+
+ Q_D(QEventDispatcherMac);
+
+ if (type == QSocketNotifier::Exception) {
+ qWarning("QSocketNotifier::Exception is not supported on Mac OS X");
+ return;
+ }
+ MacSocketInfo *socketInfo = d->macSockets.value(nativeSocket);
+ if (!socketInfo) {
+ qWarning("QEventDispatcherMac::unregisterSocketNotifier: Tried to unregister a not registered notifier");
+ return;
+ }
+
+ // Decrement read/write counters and disable callbacks if necessary.
+ if (type == QSocketNotifier::Read) {
+ Q_ASSERT(notifier == socketInfo->readNotifier);
+ socketInfo->readNotifier = 0;
+ CFSocketDisableCallBacks(socketInfo->socket, kCFSocketReadCallBack);
+ } else if (type == QSocketNotifier::Write) {
+ Q_ASSERT(notifier == socketInfo->writeNotifier);
+ socketInfo->writeNotifier = 0;
+ CFSocketDisableCallBacks(socketInfo->socket, kCFSocketWriteCallBack);
+ }
+
+ // Remove CFSocket from runloop if this was the last QSocketNotifier.
+ if (socketInfo->readNotifier == 0 && socketInfo->writeNotifier == 0) {
+ if (CFSocketIsValid(socketInfo->socket))
+ qt_mac_remove_socket_from_runloop(socketInfo->socket, socketInfo->runloop);
+ CFRunLoopSourceInvalidate(socketInfo->runloop);
+ CFRelease(socketInfo->runloop);
+ CFSocketInvalidate(socketInfo->socket);
+ CFRelease(socketInfo->socket);
+ delete socketInfo;
+ d->macSockets.remove(nativeSocket);
+ }
+}
+
+bool QEventDispatcherMac::hasPendingEvents()
+{
+ extern uint qGlobalPostedEventsCount();
+ return qGlobalPostedEventsCount() || (qt_is_gui_used && GetNumEventsInQueue(GetMainEventQueue()));
+}
+
+
+static bool qt_mac_send_event(QEventLoop::ProcessEventsFlags, OSEventRef event, OSWindowRef pt)
+{
+#ifndef QT_MAC_USE_COCOA
+ if(pt && SendEventToWindow(event, pt) != eventNotHandledErr)
+ return true;
+ return !SendEventToEventTarget(event, GetEventDispatcherTarget());
+#else // QT_MAC_USE_COCOA
+ if (pt)
+ [pt sendEvent:event];
+ else
+ [NSApp sendEvent:event];
+ return true;
+#endif
+}
+
+#ifdef QT_MAC_USE_COCOA
+static bool IsMouseOrKeyEvent( NSEvent* event )
+{
+ bool result = false;
+
+ switch( [event type] )
+ {
+ case NSLeftMouseDown:
+ case NSLeftMouseUp:
+ case NSRightMouseDown:
+ case NSRightMouseUp:
+ case NSMouseMoved: // ??
+ case NSLeftMouseDragged:
+ case NSRightMouseDragged:
+ case NSMouseEntered:
+ case NSMouseExited:
+ case NSKeyDown:
+ case NSKeyUp:
+ case NSFlagsChanged: // key modifiers changed?
+ case NSCursorUpdate: // ??
+ case NSScrollWheel:
+ case NSTabletPoint:
+ case NSTabletProximity:
+ case NSOtherMouseDown:
+ case NSOtherMouseUp:
+ case NSOtherMouseDragged:
+#ifndef QT_NO_GESTURES
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
+ case NSEventTypeGesture: // touch events
+ case NSEventTypeMagnify:
+ case NSEventTypeSwipe:
+ case NSEventTypeRotate:
+ case NSEventTypeBeginGesture:
+ case NSEventTypeEndGesture:
+#endif
+#endif // QT_NO_GESTURES
+ result = true;
+ break;
+
+ default:
+ break;
+ }
+ return result;
+}
+#endif
+
+static inline void qt_mac_waitForMoreEvents()
+{
+#ifndef QT_MAC_USE_COCOA
+ while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e20, true) == kCFRunLoopRunTimedOut) ;
+#else
+ // If no event exist in the cocoa event que, wait
+ // (and free up cpu time) until at least one event occur.
+ // This implementation is a bit on the edge, but seems to
+ // work fine:
+ NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
+ untilDate:[NSDate distantFuture]
+ inMode:NSDefaultRunLoopMode
+ dequeue:YES];
+ if (event)
+ [NSApp postEvent:event atStart:YES];
+#endif
+}
+
+#ifdef QT_MAC_USE_COCOA
+static inline void qt_mac_waitForMoreModalSessionEvents()
+{
+ // If no event exist in the cocoa event que, wait
+ // (and free up cpu time) until at least one event occur.
+ // This implementation is a bit on the edge, but seems to
+ // work fine:
+ NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
+ untilDate:[NSDate distantFuture]
+ inMode:NSModalPanelRunLoopMode
+ dequeue:YES];
+ if (event)
+ [NSApp postEvent:event atStart:YES];
+}
+#endif
+
+bool QEventDispatcherMac::processEvents(QEventLoop::ProcessEventsFlags flags)
+{
+ Q_D(QEventDispatcherMac);
+ d->interrupt = false;
+
+#ifdef QT_MAC_USE_COCOA
+ bool interruptLater = false;
+ QtMacInterruptDispatcherHelp::cancelInterruptLater();
+#endif
+
+ // In case we end up recursing while we now process events, make sure
+ // that we send remaining posted Qt events before this call returns:
+ wakeUp();
+ emit awake();
+
+ bool excludeUserEvents = flags & QEventLoop::ExcludeUserInputEvents;
+ bool retVal = false;
+ forever {
+ if (d->interrupt)
+ break;
+
+#ifdef QT_MAC_USE_COCOA
+ QMacCocoaAutoReleasePool pool;
+ NSEvent* event = 0;
+
+ // First, send all previously excluded input events, if any:
+ if (!excludeUserEvents) {
+ while (!d->queuedUserInputEvents.isEmpty()) {
+ event = static_cast<NSEvent *>(d->queuedUserInputEvents.takeFirst());
+ if (!filterEvent(event)) {
+ qt_mac_send_event(flags, event, 0);
+ retVal = true;
+ }
+ [event release];
+ }
+ }
+
+ // If Qt is used as a plugin, or as an extension in a native cocoa
+ // application, we should not run or stop NSApplication; This will be
+ // done from the application itself. And if processEvents is called
+ // manually (rather than from a QEventLoop), we cannot enter a tight
+ // loop and block this call, but instead we need to return after one flush.
+ // Finally, if we are to exclude user input events, we cannot call [NSApp run]
+ // as we then loose control over which events gets dispatched:
+ const bool canExec_3rdParty = d->nsAppRunCalledByQt || ![NSApp isRunning];
+ const bool canExec_Qt = !excludeUserEvents &&
+ (flags & QEventLoop::DialogExec || flags & QEventLoop::EventLoopExec) ;
+
+ if (canExec_Qt && canExec_3rdParty) {
+ // We can use exec-mode, meaning that we can stay in a tight loop until
+ // interrupted. This is mostly an optimization, but it allow us to use
+ // [NSApp run], which is the normal code path for cocoa applications.
+ if (NSModalSession session = d->currentModalSession()) {
+ QBoolBlocker execGuard(d->currentExecIsNSAppRun, false);
+ while ([NSApp runModalSession:session] == NSRunContinuesResponse && !d->interrupt)
+ qt_mac_waitForMoreModalSessionEvents();
+
+ if (!d->interrupt && session == d->currentModalSessionCached) {
+ // Someone called [NSApp stopModal:] from outside the event
+ // dispatcher (e.g to stop a native dialog). But that call wrongly stopped
+ // 'session' as well. As a result, we need to restart all internal sessions:
+ d->temporarilyStopAllModalSessions();
+ }
+ } else {
+ d->nsAppRunCalledByQt = true;
+ QBoolBlocker execGuard(d->currentExecIsNSAppRun, true);
+ [NSApp run];
+ }
+ retVal = true;
+ } else {
+ // We cannot block the thread (and run in a tight loop).
+ // Instead we will process all current pending events and return.
+ d->ensureNSAppInitialized();
+ if (NSModalSession session = d->currentModalSession()) {
+ // INVARIANT: a modal window is executing.
+ if (!excludeUserEvents) {
+ // Since we can dispatch all kinds of events, we choose
+ // to use cocoa's native way of running modal sessions:
+ if (flags & QEventLoop::WaitForMoreEvents)
+ qt_mac_waitForMoreModalSessionEvents();
+ NSInteger status = [NSApp runModalSession:session];
+ if (status != NSRunContinuesResponse && session == d->currentModalSessionCached) {
+ // INVARIANT: Someone called [NSApp stopModal:] from outside the event
+ // dispatcher (e.g to stop a native dialog). But that call wrongly stopped
+ // 'session' as well. As a result, we need to restart all internal sessions:
+ d->temporarilyStopAllModalSessions();
+ }
+ retVal = true;
+ } else do {
+ // Dispatch all non-user events (but que non-user events up for later). In
+ // this case, we need more control over which events gets dispatched, and
+ // cannot use [NSApp runModalSession:session]:
+ event = [NSApp nextEventMatchingMask:NSAnyEventMask
+ untilDate:nil
+ inMode:NSModalPanelRunLoopMode
+ dequeue: YES];
+
+ if (event) {
+ if (IsMouseOrKeyEvent(event)) {
+ [event retain];
+ d->queuedUserInputEvents.append(event);
+ continue;
+ }
+ if (!filterEvent(event) && qt_mac_send_event(flags, event, 0))
+ retVal = true;
+ }
+ } while (!d->interrupt && event != nil);
+ } else do {
+ // INVARIANT: No modal window is executing.
+ event = [NSApp nextEventMatchingMask:NSAnyEventMask
+ untilDate:nil
+ inMode:NSDefaultRunLoopMode
+ dequeue: YES];
+
+ if (event) {
+ if (flags & QEventLoop::ExcludeUserInputEvents) {
+ if (IsMouseOrKeyEvent(event)) {
+ [event retain];
+ d->queuedUserInputEvents.append(event);
+ continue;
+ }
+ }
+ if (!filterEvent(event) && qt_mac_send_event(flags, event, 0))
+ retVal = true;
+ }
+ } while (!d->interrupt && event != nil);
+
+ // Be sure to flush the Qt posted events when not using exec mode
+ // (exec mode will always do this call from the event loop source):
+ if (!d->interrupt)
+ QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData);
+
+ // Since the window that holds modality might have changed while processing
+ // events, we we need to interrupt when we return back the previous process
+ // event recursion to ensure that we spin the correct modal session.
+ // We do the interruptLater at the end of the function to ensure that we don't
+ // disturb the 'wait for more events' below (as deleteLater will post an event):
+ interruptLater = true;
+ }
+#else
+ do {
+ EventRef event;
+ if (!(flags & QEventLoop::ExcludeUserInputEvents)
+ && !d->queuedUserInputEvents.isEmpty()) {
+ // process a pending user input event
+ event = static_cast<EventRef>(d->queuedUserInputEvents.takeFirst());
+ } else {
+ OSStatus err = ReceiveNextEvent(0,0, kEventDurationNoWait, true, &event);
+ if(err != noErr)
+ continue;
+ // else
+ if (flags & QEventLoop::ExcludeUserInputEvents) {
+ UInt32 ekind = GetEventKind(event),
+ eclass = GetEventClass(event);
+ switch(eclass) {
+ case kEventClassQt:
+ if(ekind != kEventQtRequestContext)
+ break;
+ // fall through
+ case kEventClassMouse:
+ case kEventClassKeyboard:
+ d->queuedUserInputEvents.append(event);
+ continue;
+ }
+ }
+ }
+
+ if (!filterEvent(&event) && qt_mac_send_event(flags, event, 0))
+ retVal = true;
+ ReleaseEvent(event);
+ } while(!d->interrupt && GetNumEventsInQueue(GetMainEventQueue()) > 0);
+
+#endif
+
+ bool canWait = (d->threadData->canWait
+ && !retVal
+ && !d->interrupt
+ && (flags & QEventLoop::WaitForMoreEvents));
+ if (canWait) {
+ // INVARIANT: We haven't processed any events yet. And we're told
+ // to stay inside this function until at least one event is processed.
+ qt_mac_waitForMoreEvents();
+ flags &= ~QEventLoop::WaitForMoreEvents;
+ } else {
+ // Done with event processing for now.
+ // Leave the function:
+ break;
+ }
+ }
+
+ // If we're interrupted, we need to interrupt the _current_
+ // recursion as well to check if it is still supposed to be
+ // executing. This way we wind down the stack until we land
+ // on a recursion that again calls processEvents (typically
+ // from QEventLoop), and set interrupt to false:
+ if (d->interrupt)
+ interrupt();
+
+#ifdef QT_MAC_USE_COCOA
+ if (interruptLater)
+ QtMacInterruptDispatcherHelp::interruptLater();
+#endif
+
+ return retVal;
+}
+
+void QEventDispatcherMac::wakeUp()
+{
+ Q_D(QEventDispatcherMac);
+ d->serialNumber.ref();
+ CFRunLoopSourceSignal(d->postedEventsSource);
+ CFRunLoopWakeUp(mainRunLoop());
+}
+
+void QEventDispatcherMac::flush()
+{
+ if(qApp) {
+ QWidgetList tlws = QApplication::topLevelWidgets();
+ for(int i = 0; i < tlws.size(); i++) {
+ QWidget *tlw = tlws.at(i);
+ if(tlw->isVisible())
+ macWindowFlush(qt_mac_window_for(tlw));
+ }
+ }
+}
+
+/*****************************************************************************
+ QEventDispatcherMac Implementation
+ *****************************************************************************/
+MacTimerHash QEventDispatcherMacPrivate::macTimerHash;
+bool QEventDispatcherMacPrivate::blockSendPostedEvents = false;
+bool QEventDispatcherMacPrivate::interrupt = false;
+
+#ifdef QT_MAC_USE_COCOA
+QStack<QCocoaModalSessionInfo> QEventDispatcherMacPrivate::cocoaModalSessionStack;
+bool QEventDispatcherMacPrivate::currentExecIsNSAppRun = false;
+bool QEventDispatcherMacPrivate::nsAppRunCalledByQt = false;
+bool QEventDispatcherMacPrivate::cleanupModalSessionsNeeded = false;
+NSModalSession QEventDispatcherMacPrivate::currentModalSessionCached = 0;
+
+void QEventDispatcherMacPrivate::ensureNSAppInitialized()
+{
+ // Some elements in Cocoa require NSApplication to be running before
+ // they get fully initialized, in particular the menu bar. This
+ // function is intended for cases where a dialog is told to execute before
+ // QApplication::exec is called, or the application spins the events loop
+ // manually rather than calling QApplication:exec.
+ // The function makes sure that NSApplication starts running, but stops
+ // it again as soon as the send posted events callback is called. That way
+ // we let Cocoa finish the initialization it seems to need. We'll only
+ // apply this trick at most once for any application, and we avoid doing it
+ // for the common case where main just starts QApplication::exec.
+ if (nsAppRunCalledByQt || [NSApp isRunning])
+ return;
+ nsAppRunCalledByQt = true;
+ QBoolBlocker block1(interrupt, true);
+ QBoolBlocker block2(currentExecIsNSAppRun, true);
+ [NSApp run];
+}
+
+void QEventDispatcherMacPrivate::temporarilyStopAllModalSessions()
+{
+ // Flush, and Stop, all created modal session, and as
+ // such, make them pending again. The next call to
+ // currentModalSession will recreate them again. The
+ // reason to stop all session like this is that otherwise
+ // a call [NSApp stop] would not stop NSApp, but rather
+ // the current modal session. So if we need to stop NSApp
+ // we need to stop all the modal session first. To avoid changing
+ // the stacking order of the windows while doing so, we put
+ // up a block that is used in QCocoaWindow and QCocoaPanel:
+ int stackSize = cocoaModalSessionStack.size();
+ for (int i=0; i<stackSize; ++i) {
+ QCocoaModalSessionInfo &info = cocoaModalSessionStack[i];
+ if (info.session) {
+ [NSApp endModalSession:info.session];
+ info.session = 0;
+ }
+ }
+ currentModalSessionCached = 0;
+}
+
+NSModalSession QEventDispatcherMacPrivate::currentModalSession()
+{
+ // If we have one or more modal windows, this function will create
+ // a session for each of those, and return the one for the top.
+ if (currentModalSessionCached)
+ return currentModalSessionCached;
+
+ if (cocoaModalSessionStack.isEmpty())
+ return 0;
+
+ int sessionCount = cocoaModalSessionStack.size();
+ for (int i=0; i<sessionCount; ++i) {
+ QCocoaModalSessionInfo &info = cocoaModalSessionStack[i];
+ if (!info.widget)
+ continue;
+ if (info.widget->testAttribute(Qt::WA_DontShowOnScreen))
+ continue;
+ if (!info.session) {
+ QMacCocoaAutoReleasePool pool;
+ NSWindow *window = qt_mac_window_for(info.widget);
+ if (!window)
+ continue;
+
+ ensureNSAppInitialized();
+ QBoolBlocker block1(blockSendPostedEvents, true);
+ info.nswindow = window;
+ [(NSWindow*) info.nswindow retain];
+ int levelBeforeEnterModal = [window level];
+ info.session = [NSApp beginModalSessionForWindow:window];
+ // Make sure we don't stack the window lower that it was before
+ // entering modal, in case it e.g. had the stays-on-top flag set:
+ if (levelBeforeEnterModal > [window level])
+ [window setLevel:levelBeforeEnterModal];
+ }
+ currentModalSessionCached = info.session;
+ cleanupModalSessionsNeeded = false;
+ }
+ return currentModalSessionCached;
+}
+
+static void setChildrenWorksWhenModal(QWidget *widget, bool worksWhenModal)
+{
+ // For NSPanels (but not NSWindows, sadly), we can set the flag
+ // worksWhenModal, so that they are active even when they are not modal.
+ QList<QDialog *> dialogs = widget->findChildren<QDialog *>();
+ for (int i=0; i<dialogs.size(); ++i){
+ NSWindow *window = qt_mac_window_for(dialogs[i]);
+ if (window && [window isKindOfClass:[NSPanel class]]) {
+ [static_cast<NSPanel *>(window) setWorksWhenModal:worksWhenModal];
+ if (worksWhenModal && [window isVisible]){
+ [window orderFront:window];
+ }
+ }
+ }
+}
+
+void QEventDispatcherMacPrivate::updateChildrenWorksWhenModal()
+{
+ // Make the dialog children of the widget
+ // active. And make the dialog children of
+ // the previous modal dialog unactive again:
+ QMacCocoaAutoReleasePool pool;
+ int size = cocoaModalSessionStack.size();
+ if (size > 0){
+ if (QWidget *prevModal = cocoaModalSessionStack[size-1].widget)
+ setChildrenWorksWhenModal(prevModal, true);
+ if (size > 1){
+ if (QWidget *prevModal = cocoaModalSessionStack[size-2].widget)
+ setChildrenWorksWhenModal(prevModal, false);
+ }
+ }
+}
+
+void QEventDispatcherMacPrivate::cleanupModalSessions()
+{
+ // Go through the list of modal sessions, and end those
+ // that no longer has a widget assosiated; no widget means
+ // the the session has logically ended. The reason we wait like
+ // this to actually end the sessions for real (rather than at the
+ // point they were marked as stopped), is that ending a session
+ // when no other session runs below it on the stack will make cocoa
+ // drop some events on the floor.
+ QMacCocoaAutoReleasePool pool;
+ int stackSize = cocoaModalSessionStack.size();
+
+ for (int i=stackSize-1; i>=0; --i) {
+ QCocoaModalSessionInfo &info = cocoaModalSessionStack[i];
+ if (info.widget) {
+ // This session has a widget, and is therefore not marked
+ // as stopped. So just make it current. There might still be other
+ // stopped sessions on the stack, but those will be stopped on
+ // a later "cleanup" call.
+ currentModalSessionCached = info.session;
+ break;
+ }
+ cocoaModalSessionStack.remove(i);
+ currentModalSessionCached = 0;
+ if (info.session) {
+ [NSApp endModalSession:info.session];
+ [(NSWindow *)info.nswindow release];
+ }
+ }
+
+ updateChildrenWorksWhenModal();
+ cleanupModalSessionsNeeded = false;
+}
+
+void QEventDispatcherMacPrivate::beginModalSession(QWidget *widget)
+{
+ // Add a new, empty (null), NSModalSession to the stack.
+ // It will become active the next time QEventDispatcher::processEvents is called.
+ // A QCocoaModalSessionInfo is considered pending to become active if the widget pointer
+ // is non-zero, and the session pointer is zero (it will become active upon a call to
+ // currentModalSession). A QCocoaModalSessionInfo is considered pending to be stopped if
+ // the widget pointer is zero, and the session pointer is non-zero (it will be fully
+ // stopped in cleanupModalSessions()).
+ QCocoaModalSessionInfo info = {widget, 0, 0};
+ cocoaModalSessionStack.push(info);
+ updateChildrenWorksWhenModal();
+ currentModalSessionCached = 0;
+}
+
+void QEventDispatcherMacPrivate::endModalSession(QWidget *widget)
+{
+ // Mark all sessions attached to widget as pending to be stopped. We do this
+ // by setting the widget pointer to zero, but leave the session pointer.
+ // We don't tell cocoa to stop any sessions just yet, because cocoa only understands
+ // when we stop the _current_ modal session (which is the session on top of
+ // the stack, and might not belong to 'widget').
+ int stackSize = cocoaModalSessionStack.size();
+ for (int i=stackSize-1; i>=0; --i) {
+ QCocoaModalSessionInfo &info = cocoaModalSessionStack[i];
+ if (info.widget == widget) {
+ info.widget = 0;
+ if (i == stackSize-1) {
+ // The top sessions ended. Interrupt the event dispatcher
+ // to start spinning the correct session immidiatly:
+ currentModalSessionCached = 0;
+ cleanupModalSessionsNeeded = true;
+ QEventDispatcherMac::instance()->interrupt();
+ }
+ }
+ }
+}
+
+#endif
+
+QEventDispatcherMacPrivate::QEventDispatcherMacPrivate()
+{
+}
+
+QEventDispatcherMac::QEventDispatcherMac(QObject *parent)
+ : QAbstractEventDispatcher(*new QEventDispatcherMacPrivate, parent)
+{
+ Q_D(QEventDispatcherMac);
+ CFRunLoopSourceContext context;
+ bzero(&context, sizeof(CFRunLoopSourceContext));
+ context.info = d;
+ context.equal = QEventDispatcherMacPrivate::postedEventSourceEqualCallback;
+ context.perform = QEventDispatcherMacPrivate::postedEventsSourcePerformCallback;
+ d->postedEventsSource = CFRunLoopSourceCreate(0, 0, &context);
+ Q_ASSERT(d->postedEventsSource);
+ CFRunLoopAddSource(mainRunLoop(), d->postedEventsSource, kCFRunLoopCommonModes);
+
+ CFRunLoopObserverContext observerContext;
+ bzero(&observerContext, sizeof(CFRunLoopObserverContext));
+ observerContext.info = this;
+ d->waitingObserver = CFRunLoopObserverCreate(kCFAllocatorDefault,
+ kCFRunLoopBeforeWaiting | kCFRunLoopAfterWaiting,
+ true, 0,
+ QEventDispatcherMacPrivate::waitingObserverCallback,
+ &observerContext);
+ CFRunLoopAddObserver(mainRunLoop(), d->waitingObserver, kCFRunLoopCommonModes);
+
+ /* The first cycle in the loop adds the source and the events of the source
+ are not processed.
+ We use an observer to process the posted events for the first
+ execution of the loop. */
+ CFRunLoopObserverContext firstTimeObserverContext;
+ bzero(&firstTimeObserverContext, sizeof(CFRunLoopObserverContext));
+ firstTimeObserverContext.info = d;
+ d->firstTimeObserver = CFRunLoopObserverCreate(kCFAllocatorDefault,
+ kCFRunLoopEntry,
+ /* repeats = */ false,
+ 0,
+ QEventDispatcherMacPrivate::firstLoopEntry,
+ &firstTimeObserverContext);
+ CFRunLoopAddObserver(mainRunLoop(), d->firstTimeObserver, kCFRunLoopCommonModes);
+}
+
+void QEventDispatcherMacPrivate::waitingObserverCallback(CFRunLoopObserverRef,
+ CFRunLoopActivity activity, void *info)
+{
+ if (activity == kCFRunLoopBeforeWaiting)
+ emit static_cast<QEventDispatcherMac*>(info)->aboutToBlock();
+ else
+ emit static_cast<QEventDispatcherMac*>(info)->awake();
+}
+
+Boolean QEventDispatcherMacPrivate::postedEventSourceEqualCallback(const void *info1, const void *info2)
+{
+ return info1 == info2;
+}
+
+inline static void processPostedEvents(QEventDispatcherMacPrivate *const d, const bool blockSendPostedEvents)
+{
+ if (blockSendPostedEvents) {
+ // We're told to not send posted events (because the event dispatcher
+ // is currently working on setting up the correct session to run). But
+ // we still need to make sure that we don't fall asleep until pending events
+ // are sendt, so we just signal this need, and return:
+ CFRunLoopSourceSignal(d->postedEventsSource);
+ return;
+ }
+
+#ifdef QT_MAC_USE_COCOA
+ if (d->cleanupModalSessionsNeeded)
+ d->cleanupModalSessions();
+#endif
+
+ if (d->interrupt) {
+#ifdef QT_MAC_USE_COCOA
+ if (d->currentExecIsNSAppRun) {
+ // The event dispatcher has been interrupted. But since
+ // [NSApplication run] is running the event loop, we
+ // delayed stopping it until now (to let cocoa process
+ // pending cocoa events first).
+ if (d->currentModalSessionCached)
+ d->temporarilyStopAllModalSessions();
+ [NSApp stop:NSApp];
+ d->cancelWaitForMoreEvents();
+ }
+#endif
+ return;
+ }
+
+ if (!d->threadData->canWait || (d->serialNumber != d->lastSerial)) {
+ d->lastSerial = d->serialNumber;
+ QApplicationPrivate::sendPostedEvents(0, 0, d->threadData);
+ }
+}
+
+void QEventDispatcherMacPrivate::firstLoopEntry(CFRunLoopObserverRef ref,
+ CFRunLoopActivity activity,
+ void *info)
+{
+ Q_UNUSED(ref);
+ Q_UNUSED(activity);
+#ifdef QT_MAC_USE_COCOA
+ QApplicationPrivate::qt_initAfterNSAppStarted();
+#endif
+ processPostedEvents(static_cast<QEventDispatcherMacPrivate *>(info), blockSendPostedEvents);
+}
+
+void QEventDispatcherMacPrivate::postedEventsSourcePerformCallback(void *info)
+{
+ processPostedEvents(static_cast<QEventDispatcherMacPrivate *>(info), blockSendPostedEvents);
+}
+
+#ifdef QT_MAC_USE_COCOA
+void QEventDispatcherMacPrivate::cancelWaitForMoreEvents()
+{
+ // In case the event dispatcher is waiting for more
+ // events somewhere, we post a dummy event to wake it up:
+ QMacCocoaAutoReleasePool pool;
+ [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined location:NSZeroPoint
+ modifierFlags:0 timestamp:0. windowNumber:0 context:0
+ subtype:QtCocoaEventSubTypeWakeup data1:0 data2:0] atStart:NO];
+}
+#endif
+
+void QEventDispatcherMac::interrupt()
+{
+ Q_D(QEventDispatcherMac);
+ d->interrupt = true;
+ wakeUp();
+
+#ifndef QT_MAC_USE_COCOA
+ CFRunLoopStop(mainRunLoop());
+#else
+ // We do nothing more here than setting d->interrupt = true, and
+ // poke the event loop if it is sleeping. Actually stopping
+ // NSApp, or the current modal session, is done inside the send
+ // posted events callback. We do this to ensure that all current pending
+ // cocoa events gets delivered before we stop. Otherwise, if we now stop
+ // the last event loop recursion, cocoa will just drop pending posted
+ // events on the floor before we get a chance to reestablish a new session.
+ d->cancelWaitForMoreEvents();
+#endif
+}
+
+QEventDispatcherMac::~QEventDispatcherMac()
+{
+ Q_D(QEventDispatcherMac);
+ //timer cleanup
+ MacTimerHash::iterator it = QEventDispatcherMacPrivate::macTimerHash.begin();
+ while (it != QEventDispatcherMacPrivate::macTimerHash.end()) {
+ MacTimerInfo *t = it.value();
+ if (t->runLoopTimer) {
+ CFRunLoopTimerInvalidate(t->runLoopTimer);
+ CFRelease(t->runLoopTimer);
+ }
+ delete t;
+ ++it;
+ }
+ QEventDispatcherMacPrivate::macTimerHash.clear();
+
+ // Remove CFSockets from the runloop.
+ for (MacSocketHash::ConstIterator it = d->macSockets.constBegin(); it != d->macSockets.constEnd(); ++it) {
+ MacSocketInfo *socketInfo = (*it);
+ if (CFSocketIsValid(socketInfo->socket)) {
+ qt_mac_remove_socket_from_runloop(socketInfo->socket, socketInfo->runloop);
+ CFRunLoopSourceInvalidate(socketInfo->runloop);
+ CFRelease(socketInfo->runloop);
+ CFSocketInvalidate(socketInfo->socket);
+ CFRelease(socketInfo->socket);
+ }
+ }
+ CFRunLoopRemoveSource(mainRunLoop(), d->postedEventsSource, kCFRunLoopCommonModes);
+ CFRelease(d->postedEventsSource);
+
+ CFRunLoopObserverInvalidate(d->waitingObserver);
+ CFRelease(d->waitingObserver);
+
+ CFRunLoopObserverInvalidate(d->firstTimeObserver);
+ CFRelease(d->firstTimeObserver);
+}
+
+#ifdef QT_MAC_USE_COCOA
+
+QtMacInterruptDispatcherHelp* QtMacInterruptDispatcherHelp::instance = 0;
+
+QtMacInterruptDispatcherHelp::QtMacInterruptDispatcherHelp() : cancelled(false)
+{
+ // The whole point of this class is that we enable a way to interrupt
+ // the event dispatcher when returning back to a lower recursion level
+ // than where interruptLater was called. This is needed to detect if
+ // [NSApp run] should still be running at the recursion level it is at.
+ // Since the interrupt is canceled if processEvents is called before
+ // this object gets deleted, we also avoid interrupting unnecessary.
+ deleteLater();
+}
+
+QtMacInterruptDispatcherHelp::~QtMacInterruptDispatcherHelp()
+{
+ if (cancelled)
+ return;
+ instance = 0;
+ QEventDispatcherMac::instance()->interrupt();
+}
+
+void QtMacInterruptDispatcherHelp::cancelInterruptLater()
+{
+ if (!instance)
+ return;
+ instance->cancelled = true;
+ delete instance;
+ instance = 0;
+}
+
+void QtMacInterruptDispatcherHelp::interruptLater()
+{
+ cancelInterruptLater();
+ instance = new QtMacInterruptDispatcherHelp;
+}
+
+#endif
+
+QT_END_NAMESPACE
+
diff --git a/src/widgets/platforms/mac/qeventdispatcher_mac_p.h b/src/widgets/platforms/mac/qeventdispatcher_mac_p.h
new file mode 100644
index 0000000000..f4cf16e8a0
--- /dev/null
+++ b/src/widgets/platforms/mac/qeventdispatcher_mac_p.h
@@ -0,0 +1,224 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** Copyright (c) 2007-2008, Apple, Inc.
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**
+** * Redistributions of source code must retain the above copyright notice,
+** this list of conditions and the following disclaimer.
+**
+** * Redistributions in binary form must reproduce the above copyright notice,
+** this list of conditions and the following disclaimer in the documentation
+** and/or other materials provided with the distribution.
+**
+** * Neither the name of Apple, Inc. nor the names of its contributors
+** may be used to endorse or promote products derived from this software
+** without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**
+****************************************************************************/
+
+#ifndef QEVENTDISPATCHER_MAC_P_H
+#define QEVENTDISPATCHER_MAC_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/qwindowdefs.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qstack.h>
+#include "private/qabstracteventdispatcher_p.h"
+#include "private/qt_mac_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifdef QT_MAC_USE_COCOA
+typedef struct _NSModalSession *NSModalSession;
+typedef struct _QCocoaModalSessionInfo {
+ QPointer<QWidget> widget;
+ NSModalSession session;
+ void *nswindow;
+} QCocoaModalSessionInfo;
+#endif
+
+class QEventDispatcherMacPrivate;
+
+class QEventDispatcherMac : public QAbstractEventDispatcher
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QEventDispatcherMac)
+
+public:
+ explicit QEventDispatcherMac(QObject *parent = 0);
+ ~QEventDispatcherMac();
+
+ bool processEvents(QEventLoop::ProcessEventsFlags flags);
+ bool hasPendingEvents();
+
+ void registerSocketNotifier(QSocketNotifier *notifier);
+ void unregisterSocketNotifier(QSocketNotifier *notifier);
+
+ void registerTimer(int timerId, int interval, QObject *object);
+ bool unregisterTimer(int timerId);
+ bool unregisterTimers(QObject *object);
+ QList<TimerInfo> registeredTimers(QObject *object) const;
+
+ void wakeUp();
+ void flush();
+ void interrupt();
+
+private:
+ friend void qt_mac_select_timer_callbk(__EventLoopTimer*, void*);
+ friend class QApplicationPrivate;
+};
+
+struct MacTimerInfo {
+ int id;
+ int interval;
+ QObject *obj;
+ bool pending;
+ CFRunLoopTimerRef runLoopTimer;
+ bool operator==(const MacTimerInfo &other) const
+ {
+ return (id == other.id);
+ }
+};
+typedef QHash<int, MacTimerInfo *> MacTimerHash;
+
+struct MacSocketInfo {
+ MacSocketInfo() : socket(0), runloop(0), readNotifier(0), writeNotifier(0) {}
+ CFSocketRef socket;
+ CFRunLoopSourceRef runloop;
+ QObject *readNotifier;
+ QObject *writeNotifier;
+};
+typedef QHash<int, MacSocketInfo *> MacSocketHash;
+
+class QEventDispatcherMacPrivate : public QAbstractEventDispatcherPrivate
+{
+ Q_DECLARE_PUBLIC(QEventDispatcherMac)
+
+public:
+ QEventDispatcherMacPrivate();
+
+ static MacTimerHash macTimerHash;
+ // Set 'blockSendPostedEvents' to true if you _really_ need
+ // to make sure that qt events are not posted while calling
+ // low-level cocoa functions (like beginModalForWindow). And
+ // use a QBoolBlocker to be safe:
+ static bool blockSendPostedEvents;
+#ifdef QT_MAC_USE_COCOA
+ // The following variables help organizing modal sessions:
+ static QStack<QCocoaModalSessionInfo> cocoaModalSessionStack;
+ static bool currentExecIsNSAppRun;
+ static bool nsAppRunCalledByQt;
+ static bool cleanupModalSessionsNeeded;
+ static NSModalSession currentModalSessionCached;
+ static NSModalSession currentModalSession();
+ static void updateChildrenWorksWhenModal();
+ static void temporarilyStopAllModalSessions();
+ static void beginModalSession(QWidget *widget);
+ static void endModalSession(QWidget *widget);
+ static void cancelWaitForMoreEvents();
+ static void cleanupModalSessions();
+ static void ensureNSAppInitialized();
+#endif
+
+ MacSocketHash macSockets;
+ QList<void *> queuedUserInputEvents; // List of EventRef in Carbon, and NSEvent * in Cocoa
+ CFRunLoopSourceRef postedEventsSource;
+ CFRunLoopObserverRef waitingObserver;
+ CFRunLoopObserverRef firstTimeObserver;
+ QAtomicInt serialNumber;
+ int lastSerial;
+ static bool interrupt;
+private:
+ static Boolean postedEventSourceEqualCallback(const void *info1, const void *info2);
+ static void postedEventsSourcePerformCallback(void *info);
+ static void activateTimer(CFRunLoopTimerRef, void *info);
+ static void waitingObserverCallback(CFRunLoopObserverRef observer,
+ CFRunLoopActivity activity, void *info);
+ static void firstLoopEntry(CFRunLoopObserverRef ref, CFRunLoopActivity activity, void *info);
+};
+
+#ifdef QT_MAC_USE_COCOA
+class QtMacInterruptDispatcherHelp : public QObject
+{
+ static QtMacInterruptDispatcherHelp *instance;
+ bool cancelled;
+
+ QtMacInterruptDispatcherHelp();
+ ~QtMacInterruptDispatcherHelp();
+
+ public:
+ static void interruptLater();
+ static void cancelInterruptLater();
+};
+#endif
+
+QT_END_NAMESPACE
+
+#endif // QEVENTDISPATCHER_MAC_P_H
diff --git a/src/widgets/platforms/mac/qfont_mac.cpp b/src/widgets/platforms/mac/qfont_mac.cpp
new file mode 100644
index 0000000000..3bbff7f4bb
--- /dev/null
+++ b/src/widgets/platforms/mac/qfont_mac.cpp
@@ -0,0 +1,166 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qfont.h"
+#include "qfont_p.h"
+#include "qfontengine_p.h"
+#include "qfontengine_mac_p.h"
+#include "qfontengine_coretext_p.h"
+#include "qfontinfo.h"
+#include "qfontmetrics.h"
+#include "qpaintdevice.h"
+#include "qstring.h"
+#include <private/qt_mac_p.h>
+#include <private/qtextengine_p.h>
+#include <private/qunicodetables_p.h>
+#include <qapplication.h>
+#include "qfontdatabase.h"
+#include <qpainter.h>
+#include "qtextengine_p.h"
+#include <stdlib.h>
+
+QT_BEGIN_NAMESPACE
+
+extern float qt_mac_defaultDpi_x(); //qpaintdevice_mac.cpp
+
+int qt_mac_pixelsize(const QFontDef &def, int dpi)
+{
+ float ret;
+ if(def.pixelSize == -1)
+ ret = def.pointSize * dpi / qt_mac_defaultDpi_x();
+ else
+ ret = def.pixelSize;
+ return qRound(ret);
+}
+int qt_mac_pointsize(const QFontDef &def, int dpi)
+{
+ float ret;
+ if(def.pointSize < 0)
+ ret = def.pixelSize * qt_mac_defaultDpi_x() / float(dpi);
+ else
+ ret = def.pointSize;
+ return qRound(ret);
+}
+
+QString QFont::rawName() const
+{
+ return family();
+}
+
+void QFont::setRawName(const QString &name)
+{
+ setFamily(name);
+}
+
+void QFont::cleanup()
+{
+ QFontCache::cleanup();
+}
+
+/*!
+ Returns an ATSUFontID
+*/
+quint32 QFont::macFontID() const // ### need 64-bit version
+{
+#ifdef QT_MAC_USE_COCOA
+ return 0;
+#elif 1
+ QFontEngine *fe = d->engineForScript(QUnicodeTables::Common);
+ if (fe && fe->type() == QFontEngine::Multi)
+ return static_cast<QFontEngineMacMulti*>(fe)->macFontID();
+#else
+ Str255 name;
+ if(FMGetFontFamilyName((FMFontFamily)((UInt32)handle()), name) == noErr) {
+ short fnum;
+ GetFNum(name, &fnum);
+ return fnum;
+ }
+#endif
+ return 0;
+}
+
+// Returns an ATSUFonFamilyRef
+Qt::HANDLE QFont::handle() const
+{
+#ifdef QT_MAC_USE_COCOA
+ QFontEngine *fe = d->engineForScript(QUnicodeTables::Common);
+ if (fe && fe->type() == QFontEngine::Multi)
+ return (Qt::HANDLE)static_cast<QCoreTextFontEngineMulti*>(fe)->macFontID();
+#endif
+ return 0;
+}
+
+void QFont::initialize()
+{ }
+
+QString QFont::defaultFamily() const
+{
+ switch(d->request.styleHint) {
+ case QFont::Times:
+ return QString::fromLatin1("Times New Roman");
+ case QFont::Courier:
+ return QString::fromLatin1("Courier New");
+ case QFont::Monospace:
+ return QString::fromLatin1("Courier");
+ case QFont::Decorative:
+ return QString::fromLatin1("Bookman Old Style");
+ case QFont::Cursive:
+ return QString::fromLatin1("Apple Chancery");
+ case QFont::Fantasy:
+ return QString::fromLatin1("Papyrus");
+ case QFont::Helvetica:
+ case QFont::System:
+ default:
+ return QString::fromLatin1("Helvetica");
+ }
+}
+
+QString QFont::lastResortFamily() const
+{
+ return QString::fromLatin1("Helvetica");
+}
+
+QString QFont::lastResortFont() const
+{
+ return QString::fromLatin1("Geneva");
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/mac/qfontdatabase_mac.cpp b/src/widgets/platforms/mac/qfontdatabase_mac.cpp
new file mode 100644
index 0000000000..6876fae630
--- /dev/null
+++ b/src/widgets/platforms/mac/qfontdatabase_mac.cpp
@@ -0,0 +1,494 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qt_mac_p.h>
+#include "qfontengine_p.h"
+#include <qfile.h>
+#include <qabstractfileengine.h>
+#include <stdlib.h>
+#include <qendian.h>
+#include <private/qfontengine_coretext_p.h>
+#include <private/qfontengine_mac_p.h>
+
+QT_BEGIN_NAMESPACE
+
+int qt_mac_pixelsize(const QFontDef &def, int dpi); //qfont_mac.cpp
+int qt_mac_pointsize(const QFontDef &def, int dpi); //qfont_mac.cpp
+
+#ifndef QT_MAC_USE_COCOA
+static void initWritingSystems(QtFontFamily *family, ATSFontRef atsFont)
+{
+ ByteCount length = 0;
+ if (ATSFontGetTable(atsFont, MAKE_TAG('O', 'S', '/', '2'), 0, 0, 0, &length) != noErr)
+ return;
+ QVarLengthArray<uchar> os2Table(length);
+ if (length < 86
+ || ATSFontGetTable(atsFont, MAKE_TAG('O', 'S', '/', '2'), 0, length, os2Table.data(), &length) != noErr)
+ return;
+
+ // See also qfontdatabase_win.cpp, offsets taken from OS/2 table in the TrueType spec
+ quint32 unicodeRange[4] = {
+ qFromBigEndian<quint32>(os2Table.data() + 42),
+ qFromBigEndian<quint32>(os2Table.data() + 46),
+ qFromBigEndian<quint32>(os2Table.data() + 50),
+ qFromBigEndian<quint32>(os2Table.data() + 54)
+ };
+ quint32 codePageRange[2] = { qFromBigEndian<quint32>(os2Table.data() + 78), qFromBigEndian<quint32>(os2Table.data() + 82) };
+ QList<QFontDatabase::WritingSystem> systems = qt_determine_writing_systems_from_truetype_bits(unicodeRange, codePageRange);
+#if 0
+ QCFString name;
+ ATSFontGetName(atsFont, kATSOptionFlagsDefault, &name);
+ qDebug() << systems.count() << "writing systems for" << QString(name);
+qDebug() << "first char" << hex << unicodeRange[0];
+ for (int i = 0; i < systems.count(); ++i)
+ qDebug() << QFontDatabase::writingSystemName(systems.at(i));
+#endif
+ for (int i = 0; i < systems.count(); ++i)
+ family->writingSystems[systems.at(i)] = QtFontFamily::Supported;
+}
+#endif
+
+static void initializeDb()
+{
+ QFontDatabasePrivate *db = privateDb();
+ if(!db || db->count)
+ return;
+
+#if defined(QT_MAC_USE_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
+if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
+ QCFType<CTFontCollectionRef> collection = CTFontCollectionCreateFromAvailableFonts(0);
+ if(!collection)
+ return;
+ QCFType<CFArrayRef> fonts = CTFontCollectionCreateMatchingFontDescriptors(collection);
+ if(!fonts)
+ return;
+ QString foundry_name = "CoreText";
+ const int numFonts = CFArrayGetCount(fonts);
+ for(int i = 0; i < numFonts; ++i) {
+ CTFontDescriptorRef font = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fonts, i);
+
+ QCFString family_name = (CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontFamilyNameAttribute);
+ QCFString style_name = (CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontStyleNameAttribute);
+ QtFontFamily *family = db->family(family_name, true);
+ for(int ws = 1; ws < QFontDatabase::WritingSystemsCount; ++ws)
+ family->writingSystems[ws] = QtFontFamily::Supported;
+ QtFontFoundry *foundry = family->foundry(foundry_name, true);
+
+ QtFontStyle::Key styleKey;
+ QString styleName = style_name;
+ if(QCFType<CFDictionaryRef> styles = (CFDictionaryRef)CTFontDescriptorCopyAttribute(font, kCTFontTraitsAttribute)) {
+ if(CFNumberRef weight = (CFNumberRef)CFDictionaryGetValue(styles, kCTFontWeightTrait)) {
+ Q_ASSERT(CFNumberIsFloatType(weight));
+ double d;
+ if(CFNumberGetValue(weight, kCFNumberDoubleType, &d)) {
+ //qDebug() << "BOLD" << (QString)family_name << d;
+ styleKey.weight = (d > 0.0) ? QFont::Bold : QFont::Normal;
+ }
+ }
+ if(CFNumberRef italic = (CFNumberRef)CFDictionaryGetValue(styles, kCTFontSlantTrait)) {
+ Q_ASSERT(CFNumberIsFloatType(italic));
+ double d;
+ if(CFNumberGetValue(italic, kCFNumberDoubleType, &d)) {
+ //qDebug() << "ITALIC" << (QString)family_name << d;
+ if (d > 0.0)
+ styleKey.style = QFont::StyleItalic;
+ }
+ }
+ }
+
+ QtFontStyle *style = foundry->style(styleKey, styleName, true);
+ style->smoothScalable = true;
+ if(QCFType<CFNumberRef> size = (CFNumberRef)CTFontDescriptorCopyAttribute(font, kCTFontSizeAttribute)) {
+ //qDebug() << "WHEE";
+ int pixel_size=0;
+ if(CFNumberIsFloatType(size)) {
+ double d;
+ CFNumberGetValue(size, kCFNumberDoubleType, &d);
+ pixel_size = d;
+ } else {
+ CFNumberGetValue(size, kCFNumberIntType, &pixel_size);
+ }
+ //qDebug() << "SIZE" << (QString)family_name << pixel_size;
+ if(pixel_size)
+ style->pixelSize(pixel_size, true);
+ } else {
+ //qDebug() << "WTF?";
+ }
+ }
+} else
+#endif
+ {
+#ifndef QT_MAC_USE_COCOA
+ FMFontIterator it;
+ if (!FMCreateFontIterator(0, 0, kFMUseGlobalScopeOption, &it)) {
+ while (true) {
+ FMFont fmFont;
+ if (FMGetNextFont(&it, &fmFont) != noErr)
+ break;
+
+ FMFontFamily fmFamily;
+ FMFontStyle fmStyle;
+ QString familyName;
+
+ QtFontStyle::Key styleKey;
+
+ ATSFontRef atsFont = FMGetATSFontRefFromFont(fmFont);
+
+ if (!FMGetFontFamilyInstanceFromFont(fmFont, &fmFamily, &fmStyle)) {
+ { //sanity check the font, and see if we can use it at all! --Sam
+ ATSUFontID fontID;
+ if(ATSUFONDtoFontID(fmFamily, 0, &fontID) != noErr)
+ continue;
+ }
+
+ if (fmStyle & ::italic)
+ styleKey.style = QFont::StyleItalic;
+ if (fmStyle & ::bold)
+ styleKey.weight = QFont::Bold;
+
+ ATSFontFamilyRef familyRef = FMGetATSFontFamilyRefFromFontFamily(fmFamily);
+ QCFString cfFamilyName;;
+ ATSFontFamilyGetName(familyRef, kATSOptionFlagsDefault, &cfFamilyName);
+ familyName = cfFamilyName;
+ } else {
+ QCFString cfFontName;
+ ATSFontGetName(atsFont, kATSOptionFlagsDefault, &cfFontName);
+ familyName = cfFontName;
+ quint16 macStyle = 0;
+ {
+ uchar data[4];
+ ByteCount len = 4;
+ if (ATSFontGetTable(atsFont, MAKE_TAG('h', 'e', 'a', 'd'), 44, 4, &data, &len) == noErr)
+ macStyle = qFromBigEndian<quint16>(data);
+ }
+ if (macStyle & 1)
+ styleKey.weight = QFont::Bold;
+ if (macStyle & 2)
+ styleKey.style = QFont::StyleItalic;
+ }
+
+ QtFontFamily *family = db->family(familyName, true);
+ QtFontFoundry *foundry = family->foundry(QString(), true);
+ QtFontStyle *style = foundry->style(styleKey, QString(), true);
+ style->pixelSize(0, true);
+ style->smoothScalable = true;
+
+ initWritingSystems(family, atsFont);
+ }
+ FMDisposeFontIterator(&it);
+ }
+#endif
+ }
+
+}
+
+static inline void load(const QString & = QString(), int = -1)
+{
+ initializeDb();
+}
+
+static const char *styleHint(const QFontDef &request)
+{
+ const char *stylehint = 0;
+ switch (request.styleHint) {
+ case QFont::SansSerif:
+ stylehint = "Arial";
+ break;
+ case QFont::Serif:
+ stylehint = "Times New Roman";
+ break;
+ case QFont::TypeWriter:
+ stylehint = "Courier New";
+ break;
+ default:
+ if (request.fixedPitch)
+ stylehint = "Courier New";
+ break;
+ }
+ return stylehint;
+}
+
+static inline float weightToFloat(unsigned int weight)
+{
+ return (weight - 50) / 100.0;
+}
+
+static QFontEngine *loadFromDatabase(const QFontDef &req, const QFontPrivate *d)
+{
+#if defined(QT_MAC_USE_COCOA)
+ QCFString fontName = NULL;
+#else
+ ATSFontFamilyRef familyRef = 0;
+ ATSFontRef fontRef = 0;
+#endif
+
+ QStringList family_list = familyList(req);
+
+ const char *stylehint = styleHint(req);
+ if (stylehint)
+ family_list << QLatin1String(stylehint);
+
+ // add QFont::defaultFamily() to the list, for compatibility with previous versions
+ family_list << QApplication::font().defaultFamily();
+
+ QMutexLocker locker(fontDatabaseMutex());
+ QFontDatabasePrivate *db = privateDb();
+ if (!db->count)
+ initializeDb();
+ for (int i = 0; i < family_list.size(); ++i) {
+ for (int k = 0; k < db->count; ++k) {
+ if (db->families[k]->name.compare(family_list.at(i), Qt::CaseInsensitive) == 0) {
+ QByteArray family_name = db->families[k]->name.toUtf8();
+#if defined(QT_MAC_USE_COCOA)
+ QCFType<CTFontRef> ctFont = CTFontCreateWithName(QCFString(db->families[k]->name), 12, NULL);
+ if (ctFont) {
+ fontName = CTFontCopyFullName(ctFont);
+ goto found;
+ }
+#else
+ familyRef = ATSFontFamilyFindFromName(QCFString(db->families[k]->name), kATSOptionFlagsDefault);
+ if (familyRef) {
+ fontRef = ATSFontFindFromName(QCFString(db->families[k]->name), kATSOptionFlagsDefault);
+ goto found;
+ }
+#endif
+ }
+ }
+ }
+found:
+#ifdef QT_MAC_USE_COCOA
+ if (fontName)
+ return new QCoreTextFontEngineMulti(fontName, req, d->kerning);
+#else
+ if (familyRef) {
+ QCFString actualName;
+ if (ATSFontFamilyGetName(familyRef, kATSOptionFlagsDefault, &actualName) == noErr)
+ req.family = actualName;
+ return new QFontEngineMacMulti(familyRef, req, fontDef, d->kerning);
+ }
+#endif
+ return NULL;
+}
+
+void QFontDatabase::load(const QFontPrivate *d, int script)
+{
+ // sanity checks
+ if(!qApp)
+ qWarning("QFont: Must construct a QApplication before a QFont");
+
+ Q_ASSERT(script >= 0 && script < QUnicodeTables::ScriptCount);
+ Q_UNUSED(script);
+
+ QFontDef req = d->request;
+ req.pixelSize = qt_mac_pixelsize(req, d->dpi);
+
+ // set the point size to 0 to get better caching
+ req.pointSize = 0;
+ QFontCache::Key key = QFontCache::Key(req, QUnicodeTables::Common, d->screen);
+
+ if(!(d->engineData = QFontCache::instance()->findEngineData(key))) {
+ d->engineData = new QFontEngineData;
+ QFontCache::instance()->insertEngineData(key, d->engineData);
+ } else {
+ d->engineData->ref.ref();
+ }
+ if(d->engineData->engine) // already loaded
+ return;
+
+ // set it to the actual pointsize, so QFontInfo will do the right thing
+ req.pointSize = qt_mac_pointsize(d->request, d->dpi);
+
+ QFontEngine *e = QFontCache::instance()->findEngine(key);
+ if(!e && qt_enable_test_font && req.family == QLatin1String("__Qt__Box__Engine__")) {
+ e = new QTestFontEngine(req.pixelSize);
+ e->fontDef = req;
+ }
+
+ if(e) {
+ e->ref.ref();
+ d->engineData->engine = e;
+ return; // the font info and fontdef should already be filled
+ }
+
+ QFontEngine *engine = NULL;
+#if defined(QT_MAC_USE_COCOA)
+ // Shortcut to get the font directly without going through the font database
+ if (!req.family.isEmpty() && !req.styleName.isEmpty()) {
+ QCFString expectedFamily = QCFString(req.family);
+ QCFString expectedStyle = QCFString(req.styleName);
+
+ QCFType<CFMutableDictionaryRef> attributes = CFDictionaryCreateMutable(NULL, 0,
+ &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+ CFDictionaryAddValue(attributes, kCTFontFamilyNameAttribute, expectedFamily);
+ CFDictionaryAddValue(attributes, kCTFontStyleNameAttribute, expectedStyle);
+
+ QCFType<CTFontDescriptorRef> descriptor = CTFontDescriptorCreateWithAttributes(attributes);
+ CGAffineTransform transform = qt_transform_from_fontdef(req);
+ QCFType<CTFontRef> ctFont = CTFontCreateWithFontDescriptor(descriptor, req.pixelSize, &transform);
+ if (ctFont) {
+ QCFString familyName = CTFontCopyFamilyName(ctFont);
+ // Only accept the font if the family name is exactly the same as we specified
+ if (CFEqual(expectedFamily, familyName)) {
+ engine = new QCoreTextFontEngineMulti(ctFont, req, d->kerning);
+ }
+ }
+ }
+#endif
+ if (!engine)
+ engine = loadFromDatabase(req, d);
+
+ if (engine) {
+ d->engineData->engine = engine;
+ engine->ref.ref();
+ QFontCache::instance()->insertEngine(key, engine);
+ }
+}
+
+static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt)
+{
+ ATSFontContainerRef handle;
+ OSStatus e = noErr;
+
+ if(fnt->data.isEmpty()) {
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
+ extern OSErr qt_mac_create_fsref(const QString &, FSRef *); // qglobal.cpp
+ FSRef ref;
+ if(qt_mac_create_fsref(fnt->fileName, &ref) != noErr)
+ return;
+
+ ATSFontActivateFromFileReference(&ref, kATSFontContextLocal, kATSFontFormatUnspecified, 0, kATSOptionFlagsDefault, &handle);
+ } else
+#endif
+ {
+#ifndef Q_WS_MAC64
+ extern Q_CORE_EXPORT OSErr qt_mac_create_fsspec(const QString &, FSSpec *); // global.cpp
+ FSSpec spec;
+ if(qt_mac_create_fsspec(fnt->fileName, &spec) != noErr)
+ return;
+
+ e = ATSFontActivateFromFileSpecification(&spec, kATSFontContextLocal, kATSFontFormatUnspecified,
+ 0, kATSOptionFlagsDefault, &handle);
+#endif
+ }
+ } else {
+ e = ATSFontActivateFromMemory((void *)fnt->data.constData(), fnt->data.size(), kATSFontContextLocal,
+ kATSFontFormatUnspecified, 0, kATSOptionFlagsDefault, &handle);
+
+ fnt->data = QByteArray();
+ }
+
+ if(e != noErr)
+ return;
+
+ ItemCount fontCount = 0;
+ e = ATSFontFindFromContainer(handle, kATSOptionFlagsDefault, 0, 0, &fontCount);
+ if(e != noErr)
+ return;
+
+ QVarLengthArray<ATSFontRef> containedFonts(fontCount);
+ e = ATSFontFindFromContainer(handle, kATSOptionFlagsDefault, fontCount, containedFonts.data(), &fontCount);
+ if(e != noErr)
+ return;
+
+ fnt->families.clear();
+#if defined(QT_MAC_USE_COCOA)
+ // Make sure that the family name set on the font matches what
+ // kCTFontFamilyNameAttribute returns in initializeDb().
+ // So far the best solution seems find the installed font
+ // using CoreText and get the family name from it.
+ // (ATSFontFamilyGetName appears to be the correct API, but also
+ // returns the font display name.)
+ for(int i = 0; i < containedFonts.size(); ++i) {
+ QCFString fontPostScriptName;
+ ATSFontGetPostScriptName(containedFonts[i], kATSOptionFlagsDefault, &fontPostScriptName);
+ QCFType<CTFontDescriptorRef> font = CTFontDescriptorCreateWithNameAndSize(fontPostScriptName, 14);
+ QCFString familyName = (CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontFamilyNameAttribute);
+ fnt->families.append(familyName);
+ }
+#else
+ for(int i = 0; i < containedFonts.size(); ++i) {
+ QCFString family;
+ ATSFontGetName(containedFonts[i], kATSOptionFlagsDefault, &family);
+ fnt->families.append(family);
+ }
+#endif
+
+ fnt->handle = handle;
+}
+
+bool QFontDatabase::removeApplicationFont(int handle)
+{
+ QMutexLocker locker(fontDatabaseMutex());
+
+ QFontDatabasePrivate *db = privateDb();
+ if(handle < 0 || handle >= db->applicationFonts.count())
+ return false;
+
+ OSStatus e = ATSFontDeactivate(db->applicationFonts.at(handle).handle,
+ /*iRefCon=*/0, kATSOptionFlagsDefault);
+ if(e != noErr)
+ return false;
+
+ db->applicationFonts[handle] = QFontDatabasePrivate::ApplicationFont();
+
+ db->invalidate();
+ return true;
+}
+
+bool QFontDatabase::removeAllApplicationFonts()
+{
+ QMutexLocker locker(fontDatabaseMutex());
+
+ QFontDatabasePrivate *db = privateDb();
+ for(int i = 0; i < db->applicationFonts.count(); ++i) {
+ if(!removeApplicationFont(i))
+ return false;
+ }
+ return true;
+}
+
+bool QFontDatabase::supportsThreadedFontRendering()
+{
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/mac/qfontengine_coretext.mm b/src/widgets/platforms/mac/qfontengine_coretext.mm
new file mode 100644
index 0000000000..9943229787
--- /dev/null
+++ b/src/widgets/platforms/mac/qfontengine_coretext.mm
@@ -0,0 +1,870 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qfontengine_coretext_p.h"
+
+#include <QtCore/qendian.h>
+#include <QtCore/qsettings.h>
+
+#include <private/qimage_p.h>
+
+#if !defined(Q_WS_MAC) || (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+
+QT_BEGIN_NAMESPACE
+
+static float SYNTHETIC_ITALIC_SKEW = tanf(14 * acosf(0) / 90);
+
+static void loadAdvancesForGlyphs(CTFontRef ctfont,
+ QVarLengthArray<CGGlyph> &cgGlyphs,
+ QGlyphLayout *glyphs, int len,
+ QTextEngine::ShaperFlags flags,
+ const QFontDef &fontDef)
+{
+ Q_UNUSED(flags);
+ QVarLengthArray<CGSize> advances(len);
+ CTFontGetAdvancesForGlyphs(ctfont, kCTFontHorizontalOrientation, cgGlyphs.data(), advances.data(), len);
+
+ for (int i = 0; i < len; ++i) {
+ if (glyphs->glyphs[i] & 0xff000000)
+ continue;
+ glyphs->advances_x[i] = QFixed::fromReal(advances[i].width);
+ glyphs->advances_y[i] = QFixed::fromReal(advances[i].height);
+ }
+
+ if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) {
+ for (int i = 0; i < len; ++i) {
+ glyphs->advances_x[i] = glyphs->advances_x[i].round();
+ glyphs->advances_y[i] = glyphs->advances_y[i].round();
+ }
+ }
+}
+
+QCoreTextFontEngineMulti::QCoreTextFontEngineMulti(const QCFString &name, const QFontDef &fontDef, bool kerning)
+ : QFontEngineMulti(0)
+{
+ this->fontDef = fontDef;
+ CTFontSymbolicTraits symbolicTraits = 0;
+ if (fontDef.weight >= QFont::Bold)
+ symbolicTraits |= kCTFontBoldTrait;
+ switch (fontDef.style) {
+ case QFont::StyleNormal:
+ break;
+ case QFont::StyleItalic:
+ case QFont::StyleOblique:
+ symbolicTraits |= kCTFontItalicTrait;
+ break;
+ }
+
+ transform = CGAffineTransformIdentity;
+ if (fontDef.stretch != 100) {
+ transform = CGAffineTransformMakeScale(float(fontDef.stretch) / float(100), 1);
+ }
+
+ QCFType<CTFontDescriptorRef> descriptor = CTFontDescriptorCreateWithNameAndSize(name, fontDef.pixelSize);
+ QCFType<CTFontRef> baseFont = CTFontCreateWithFontDescriptor(descriptor, fontDef.pixelSize, &transform);
+ ctfont = NULL;
+ // There is a side effect in Core Text: if we apply 0 as symbolic traits to a font in normal weight,
+ // we will get the light version of that font (while the way supposed to work doesn't:
+ // setting kCTFontWeightTrait to some value between -1.0 to 0.0 has no effect on font selection)
+ if (fontDef.weight != QFont::Normal || symbolicTraits)
+ ctfont = CTFontCreateCopyWithSymbolicTraits(baseFont, fontDef.pixelSize, &transform, symbolicTraits, symbolicTraits);
+
+ // CTFontCreateCopyWithSymbolicTraits returns NULL if we ask for a trait that does
+ // not exist for the given font. (for example italic)
+ if (ctfont == 0) {
+ ctfont = baseFont;
+ CFRetain(ctfont);
+ }
+ init(kerning);
+}
+
+QCoreTextFontEngineMulti::QCoreTextFontEngineMulti(CTFontRef ctFontRef, const QFontDef &fontDef, bool kerning)
+ : QFontEngineMulti(0)
+{
+ this->fontDef = fontDef;
+ ctfont = (CTFontRef) CFRetain(ctFontRef);
+ init(kerning);
+}
+
+QCoreTextFontEngineMulti::~QCoreTextFontEngineMulti()
+{
+ CFRelease(ctfont);
+}
+
+void QCoreTextFontEngineMulti::init(bool kerning)
+{
+ Q_ASSERT(ctfont != NULL);
+ attributeDict = CFDictionaryCreateMutable(0, 2,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ CFDictionaryAddValue(attributeDict, NSFontAttributeName, ctfont);
+ if (!kerning) {
+ float zero = 0.0;
+ QCFType<CFNumberRef> noKern = CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &zero);
+ CFDictionaryAddValue(attributeDict, kCTKernAttributeName, noKern);
+ }
+
+ QCoreTextFontEngine *fe = new QCoreTextFontEngine(ctfont, fontDef);
+ fontDef.family = fe->fontDef.family;
+ fontDef.styleName = fe->fontDef.styleName;
+ transform = fe->transform;
+ fe->ref.ref();
+ engines.append(fe);
+}
+
+uint QCoreTextFontEngineMulti::fontIndexForFont(CTFontRef font) const
+{
+ for (int i = 0; i < engines.count(); ++i) {
+ if (CFEqual(engineAt(i)->ctfont, font))
+ return i;
+ }
+
+ QCoreTextFontEngineMulti *that = const_cast<QCoreTextFontEngineMulti *>(this);
+ QCoreTextFontEngine *fe = new QCoreTextFontEngine(font, fontDef);
+ fe->ref.ref();
+ that->engines.append(fe);
+ return engines.count() - 1;
+}
+
+bool QCoreTextFontEngineMulti::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs,
+ int *nglyphs, QTextEngine::ShaperFlags flags,
+ unsigned short *logClusters, const HB_CharAttributes *,
+ QScriptItem *si) const
+{
+ QCFType<CFStringRef> cfstring = CFStringCreateWithCharactersNoCopy(0,
+ reinterpret_cast<const UniChar *>(str),
+ len, kCFAllocatorNull);
+ QCFType<CFAttributedStringRef> attributedString = CFAttributedStringCreate(0, cfstring, attributeDict);
+ QCFType<CTTypesetterRef> typeSetter;
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
+ if (flags & QTextEngine::RightToLeft) {
+ const void *optionKeys[] = { kCTTypesetterOptionForcedEmbeddingLevel };
+ const short rtlForcedEmbeddingLevelValue = 1;
+ const void *rtlOptionValues[] = { CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &rtlForcedEmbeddingLevelValue) };
+ QCFType<CFDictionaryRef> options = CFDictionaryCreate(kCFAllocatorDefault, optionKeys, rtlOptionValues, 1,
+ &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+ typeSetter = CTTypesetterCreateWithAttributedStringAndOptions(attributedString, options);
+ } else
+#else
+ Q_UNUSED(flags);
+#endif
+ typeSetter = CTTypesetterCreateWithAttributedString(attributedString);
+
+ CFRange range = {0, 0};
+ QCFType<CTLineRef> line = CTTypesetterCreateLine(typeSetter, range);
+ CFArrayRef array = CTLineGetGlyphRuns(line);
+ uint arraySize = CFArrayGetCount(array);
+ glyph_t *outGlyphs = glyphs->glyphs;
+ HB_GlyphAttributes *outAttributes = glyphs->attributes;
+ QFixed *outAdvances_x = glyphs->advances_x;
+ QFixed *outAdvances_y = glyphs->advances_y;
+ glyph_t *initialGlyph = outGlyphs;
+
+ if (arraySize == 0) {
+ // CoreText failed to shape the text we gave it, so we assume one glyph
+ // per character and build a list of invalid glyphs with zero advance
+ *nglyphs = len;
+ for (int i = 0; i < len; ++i) {
+ outGlyphs[i] = 0;
+ if (logClusters)
+ logClusters[i] = i;
+ outAdvances_x[i] = QFixed();
+ outAdvances_y[i] = QFixed();
+ outAttributes[i].clusterStart = true;
+ }
+ return true;
+ }
+
+ const bool rtl = (CTRunGetStatus(static_cast<CTRunRef>(CFArrayGetValueAtIndex(array, 0))) & kCTRunStatusRightToLeft);
+
+ bool outOBounds = false;
+ for (uint i = 0; i < arraySize; ++i) {
+ CTRunRef run = static_cast<CTRunRef>(CFArrayGetValueAtIndex(array, rtl ? (arraySize - 1 - i) : i));
+ CFIndex glyphCount = CTRunGetGlyphCount(run);
+ if (glyphCount == 0)
+ continue;
+
+ Q_ASSERT((CTRunGetStatus(run) & kCTRunStatusRightToLeft) == rtl);
+ CFRange stringRange = CTRunGetStringRange(run);
+ int prepend = 0;
+#if MAC_OS_X_VERSION_MAX_ALLOWED == MAC_OS_X_VERSION_10_5
+ UniChar beginGlyph = CFStringGetCharacterAtIndex(cfstring, stringRange.location);
+ QChar dir = QChar::direction(beginGlyph);
+ bool beginWithOverride = dir == QChar::DirLRO || dir == QChar::DirRLO || dir == QChar::DirLRE || dir == QChar::DirRLE;
+ if (beginWithOverride) {
+ logClusters[stringRange.location] = 0;
+ outGlyphs[0] = 0xFFFF;
+ outAdvances_x[0] = 0;
+ outAdvances_y[0] = 0;
+ outAttributes[0].clusterStart = true;
+ outAttributes[0].dontPrint = true;
+ outGlyphs++;
+ outAdvances_x++;
+ outAdvances_y++;
+ outAttributes++;
+ prepend = 1;
+ }
+#endif
+ UniChar endGlyph = CFStringGetCharacterAtIndex(cfstring, stringRange.location + stringRange.length - 1);
+ bool endWithPDF = QChar::direction(endGlyph) == QChar::DirPDF;
+ if (endWithPDF)
+ glyphCount++;
+
+ if (!outOBounds && outGlyphs + glyphCount - initialGlyph > *nglyphs) {
+ outOBounds = true;
+ }
+ if (!outOBounds) {
+ CFDictionaryRef runAttribs = CTRunGetAttributes(run);
+ //NSLog(@"Dictionary %@", runAttribs);
+ if (!runAttribs)
+ runAttribs = attributeDict;
+ CTFontRef runFont = static_cast<CTFontRef>(CFDictionaryGetValue(runAttribs, NSFontAttributeName));
+ uint fontIndex = fontIndexForFont(runFont);
+ const QFontEngine *engine = engineAt(fontIndex);
+ fontIndex <<= 24;
+ si->ascent = qMax(engine->ascent(), si->ascent);
+ si->descent = qMax(engine->descent(), si->descent);
+ si->leading = qMax(engine->leading(), si->leading);
+ //NSLog(@"Run Font Name = %@", CTFontCopyFamilyName(runFont));
+ if (endWithPDF)
+ glyphCount--;
+
+ QVarLengthArray<CGGlyph, 512> cgglyphs(0);
+ const CGGlyph *tmpGlyphs = CTRunGetGlyphsPtr(run);
+ if (!tmpGlyphs) {
+ cgglyphs.resize(glyphCount);
+ CTRunGetGlyphs(run, range, cgglyphs.data());
+ tmpGlyphs = cgglyphs.constData();
+ }
+ QVarLengthArray<CGPoint, 512> cgpoints(0);
+ const CGPoint *tmpPoints = CTRunGetPositionsPtr(run);
+ if (!tmpPoints) {
+ cgpoints.resize(glyphCount);
+ CTRunGetPositions(run, range, cgpoints.data());
+ tmpPoints = cgpoints.constData();
+ }
+
+ const int rtlOffset = rtl ? (glyphCount - 1) : 0;
+ const int rtlSign = rtl ? -1 : 1;
+
+ if (logClusters) {
+ CFRange stringRange = CTRunGetStringRange(run);
+ QVarLengthArray<CFIndex, 512> stringIndices(0);
+ const CFIndex *tmpIndices = CTRunGetStringIndicesPtr(run);
+ if (!tmpIndices) {
+ stringIndices.resize(glyphCount);
+ CTRunGetStringIndices(run, range, stringIndices.data());
+ tmpIndices = stringIndices.constData();
+ }
+
+ const int firstGlyphIndex = outGlyphs - initialGlyph;
+ outAttributes[0].clusterStart = true;
+
+ CFIndex k = 0;
+ CFIndex i = 0;
+ for (i = stringRange.location + prepend;
+ (i < stringRange.location + stringRange.length) && (k < glyphCount); ++i) {
+ if (tmpIndices[k * rtlSign + rtlOffset] == i || i == stringRange.location + prepend) {
+ logClusters[i] = k + firstGlyphIndex;
+ outAttributes[k].clusterStart = true;
+ ++k;
+ } else {
+ logClusters[i] = k + firstGlyphIndex - 1;
+ }
+ }
+ // in case of a ligature at the end, fill the remaining logcluster entries
+ for (;i < stringRange.location + stringRange.length; i++) {
+ logClusters[i] = k + firstGlyphIndex - 1;
+ }
+ }
+ for (CFIndex i = 0; i < glyphCount - 1; ++i) {
+ int idx = rtlOffset + rtlSign * i;
+ outGlyphs[idx] = tmpGlyphs[i] | fontIndex;
+ outAdvances_x[idx] = QFixed::fromReal(tmpPoints[i + 1].x - tmpPoints[i].x);
+ // Use negative y advance for flipped coordinate system
+ outAdvances_y[idx] = QFixed::fromReal(tmpPoints[i].y - tmpPoints[i + 1].y);
+
+ if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) {
+ outAdvances_x[idx] = outAdvances_x[idx].round();
+ outAdvances_y[idx] = outAdvances_y[idx].round();
+ }
+ }
+ CGSize lastGlyphAdvance;
+ CTFontGetAdvancesForGlyphs(runFont, kCTFontHorizontalOrientation, tmpGlyphs + glyphCount - 1, &lastGlyphAdvance, 1);
+
+ outGlyphs[rtl ? 0 : (glyphCount - 1)] = tmpGlyphs[glyphCount - 1] | fontIndex;
+ outAdvances_x[rtl ? 0 : (glyphCount - 1)] =
+ (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
+ ? QFixed::fromReal(lastGlyphAdvance.width).round()
+ : QFixed::fromReal(lastGlyphAdvance.width);
+
+ if (endWithPDF) {
+ logClusters[stringRange.location + stringRange.length - 1] = glyphCount + prepend;
+ outGlyphs[glyphCount] = 0xFFFF;
+ outAdvances_x[glyphCount] = 0;
+ outAdvances_y[glyphCount] = 0;
+ outAttributes[glyphCount].clusterStart = true;
+ outAttributes[glyphCount].dontPrint = true;
+ glyphCount++;
+ }
+ }
+ outGlyphs += glyphCount;
+ outAttributes += glyphCount;
+ outAdvances_x += glyphCount;
+ outAdvances_y += glyphCount;
+ }
+ *nglyphs = (outGlyphs - initialGlyph);
+ return !outOBounds;
+}
+
+bool QCoreTextFontEngineMulti::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs,
+ int *nglyphs, QTextEngine::ShaperFlags flags) const
+{
+ *nglyphs = len;
+ QCFType<CFStringRef> cfstring;
+
+ QVarLengthArray<CGGlyph> cgGlyphs(len);
+ CTFontGetGlyphsForCharacters(ctfont, (const UniChar*)str, cgGlyphs.data(), len);
+
+ for (int i = 0; i < len; ++i) {
+ if (cgGlyphs[i]) {
+ glyphs->glyphs[i] = cgGlyphs[i];
+ } else {
+ if (!cfstring)
+ cfstring = CFStringCreateWithCharactersNoCopy(0, reinterpret_cast<const UniChar *>(str), len, kCFAllocatorNull);
+ QCFType<CTFontRef> substituteFont = CTFontCreateForString(ctfont, cfstring, CFRangeMake(i, 1));
+ CGGlyph substituteGlyph = 0;
+ CTFontGetGlyphsForCharacters(substituteFont, (const UniChar*)str + i, &substituteGlyph, 1);
+ if (substituteGlyph) {
+ const uint fontIndex = (fontIndexForFont(substituteFont) << 24);
+ glyphs->glyphs[i] = substituteGlyph | fontIndex;
+ if (!(flags & QTextEngine::GlyphIndicesOnly)) {
+ CGSize advance;
+ CTFontGetAdvancesForGlyphs(substituteFont, kCTFontHorizontalOrientation, &substituteGlyph, &advance, 1);
+ glyphs->advances_x[i] = QFixed::fromReal(advance.width);
+ glyphs->advances_y[i] = QFixed::fromReal(advance.height);
+ }
+ }
+ }
+ }
+
+ if (flags & QTextEngine::GlyphIndicesOnly)
+ return true;
+
+ loadAdvancesForGlyphs(ctfont, cgGlyphs, glyphs, len, flags, fontDef);
+ return true;
+}
+
+void QCoreTextFontEngineMulti::loadEngine(int)
+{
+ // Do nothing
+ Q_ASSERT(false);
+}
+
+extern int qt_antialiasing_threshold; // from qapplication.cpp
+
+CGAffineTransform qt_transform_from_fontdef(const QFontDef &fontDef)
+{
+ CGAffineTransform transform = CGAffineTransformIdentity;
+ if (fontDef.stretch != 100)
+ transform = CGAffineTransformMakeScale(float(fontDef.stretch) / float(100), 1);
+ return transform;
+}
+
+QCoreTextFontEngine::QCoreTextFontEngine(CTFontRef font, const QFontDef &def)
+{
+ fontDef = def;
+ transform = qt_transform_from_fontdef(fontDef);
+ ctfont = font;
+ CFRetain(ctfont);
+ cgFont = CTFontCopyGraphicsFont(font, NULL);
+ init();
+}
+
+QCoreTextFontEngine::QCoreTextFontEngine(CGFontRef font, const QFontDef &def)
+{
+ fontDef = def;
+ transform = qt_transform_from_fontdef(fontDef);
+ cgFont = font;
+ // Keep reference count balanced
+ CFRetain(cgFont);
+ ctfont = CTFontCreateWithGraphicsFont(font, fontDef.pixelSize, &transform, NULL);
+ init();
+}
+
+QCoreTextFontEngine::~QCoreTextFontEngine()
+{
+ CFRelease(cgFont);
+ CFRelease(ctfont);
+}
+
+extern QFont::Weight weightFromInteger(int weight); // qfontdatabase.cpp
+
+int getTraitValue(CFDictionaryRef allTraits, CFStringRef trait)
+{
+ if (CFDictionaryContainsKey(allTraits, trait)) {
+ CFNumberRef traitNum = (CFNumberRef) CFDictionaryGetValue(allTraits, trait);
+ float v = 0;
+ CFNumberGetValue(traitNum, kCFNumberFloatType, &v);
+ // the value we get from CFNumberRef is from -1.0 to 1.0
+ int value = v * 500 + 500;
+ return value;
+ }
+
+ return 0;
+}
+
+void QCoreTextFontEngine::init()
+{
+ Q_ASSERT(ctfont != NULL);
+ Q_ASSERT(cgFont != NULL);
+
+ QCFString family = CTFontCopyFamilyName(ctfont);
+ fontDef.family = family;
+
+ QCFString styleName = (CFStringRef) CTFontCopyAttribute(ctfont, kCTFontStyleNameAttribute);
+ fontDef.styleName = styleName;
+
+ synthesisFlags = 0;
+ CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(ctfont);
+ if (traits & kCTFontItalicTrait)
+ fontDef.style = QFont::StyleItalic;
+
+ CFDictionaryRef allTraits = CTFontCopyTraits(ctfont);
+ fontDef.weight = weightFromInteger(getTraitValue(allTraits, kCTFontWeightTrait));
+ int slant = getTraitValue(allTraits, kCTFontSlantTrait);
+ if (slant > 500 && !(traits & kCTFontItalicTrait))
+ fontDef.style = QFont::StyleOblique;
+ CFRelease(allTraits);
+
+ if (fontDef.weight >= QFont::Bold && !(traits & kCTFontBoldTrait))
+ synthesisFlags |= SynthesizedBold;
+ // XXX: we probably don't need to synthesis italic for oblique font
+ if (fontDef.style != QFont::StyleNormal && !(traits & kCTFontItalicTrait))
+ synthesisFlags |= SynthesizedItalic;
+
+ avgCharWidth = 0;
+ QByteArray os2Table = getSfntTable(MAKE_TAG('O', 'S', '/', '2'));
+ unsigned emSize = CTFontGetUnitsPerEm(ctfont);
+ if (os2Table.size() >= 10) {
+ fsType = qFromBigEndian<quint16>(reinterpret_cast<const uchar *>(os2Table.constData() + 8));
+ // qAbs is a workaround for weird fonts like Lucida Grande
+ qint16 width = qAbs(qFromBigEndian<qint16>(reinterpret_cast<const uchar *>(os2Table.constData() + 2)));
+ avgCharWidth = QFixed::fromReal(width * fontDef.pixelSize / emSize);
+ } else
+ avgCharWidth = QFontEngine::averageCharWidth();
+}
+
+bool QCoreTextFontEngine::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs,
+ int *nglyphs, QTextEngine::ShaperFlags flags) const
+{
+ *nglyphs = len;
+ QCFType<CFStringRef> cfstring;
+
+ QVarLengthArray<CGGlyph> cgGlyphs(len);
+ CTFontGetGlyphsForCharacters(ctfont, (const UniChar*)str, cgGlyphs.data(), len);
+
+ for (int i = 0; i < len; ++i)
+ if (cgGlyphs[i])
+ glyphs->glyphs[i] = cgGlyphs[i];
+
+ if (flags & QTextEngine::GlyphIndicesOnly)
+ return true;
+
+ loadAdvancesForGlyphs(ctfont, cgGlyphs, glyphs, len, flags, fontDef);
+ return true;
+}
+
+glyph_metrics_t QCoreTextFontEngine::boundingBox(const QGlyphLayout &glyphs)
+{
+ QFixed w;
+ bool round = fontDef.styleStrategy & QFont::ForceIntegerMetrics;
+
+ for (int i = 0; i < glyphs.numGlyphs; ++i) {
+ w += round ? glyphs.effectiveAdvance(i).round()
+ : glyphs.effectiveAdvance(i);
+ }
+ return glyph_metrics_t(0, -(ascent()), w - lastRightBearing(glyphs, round), ascent()+descent(), w, 0);
+}
+
+glyph_metrics_t QCoreTextFontEngine::boundingBox(glyph_t glyph)
+{
+ glyph_metrics_t ret;
+ CGGlyph g = glyph;
+ CGRect rect = CTFontGetBoundingRectsForGlyphs(ctfont, kCTFontHorizontalOrientation, &g, 0, 1);
+ if (synthesisFlags & QFontEngine::SynthesizedItalic) {
+ rect.size.width += rect.size.height * SYNTHETIC_ITALIC_SKEW;
+ }
+ ret.width = QFixed::fromReal(rect.size.width);
+ ret.height = QFixed::fromReal(rect.size.height);
+ ret.x = QFixed::fromReal(rect.origin.x);
+ ret.y = -QFixed::fromReal(rect.origin.y) - ret.height;
+ CGSize advances[1];
+ CTFontGetAdvancesForGlyphs(ctfont, kCTFontHorizontalOrientation, &g, advances, 1);
+ ret.xoff = QFixed::fromReal(advances[0].width);
+ ret.yoff = QFixed::fromReal(advances[0].height);
+
+ if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) {
+ ret.xoff = ret.xoff.round();
+ ret.yoff = ret.yoff.round();
+ }
+
+ return ret;
+}
+
+QFixed QCoreTextFontEngine::ascent() const
+{
+ return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
+ ? QFixed::fromReal(CTFontGetAscent(ctfont)).round()
+ : QFixed::fromReal(CTFontGetAscent(ctfont));
+}
+QFixed QCoreTextFontEngine::descent() const
+{
+ QFixed d = QFixed::fromReal(CTFontGetDescent(ctfont));
+ if (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
+ d = d.round();
+
+ // subtract a pixel to even out the historical +1 in QFontMetrics::height().
+ // Fix in Qt 5.
+ return d - 1;
+}
+QFixed QCoreTextFontEngine::leading() const
+{
+ return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
+ ? QFixed::fromReal(CTFontGetLeading(ctfont)).round()
+ : QFixed::fromReal(CTFontGetLeading(ctfont));
+}
+QFixed QCoreTextFontEngine::xHeight() const
+{
+ return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
+ ? QFixed::fromReal(CTFontGetXHeight(ctfont)).round()
+ : QFixed::fromReal(CTFontGetXHeight(ctfont));
+}
+
+QFixed QCoreTextFontEngine::averageCharWidth() const
+{
+ return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
+ ? avgCharWidth.round() : avgCharWidth;
+}
+
+qreal QCoreTextFontEngine::maxCharWidth() const
+{
+ return 0;
+}
+
+qreal QCoreTextFontEngine::minLeftBearing() const
+{
+ return 0;
+}
+
+qreal QCoreTextFontEngine::minRightBearing() const
+{
+ return 0;
+}
+
+void QCoreTextFontEngine::draw(CGContextRef ctx, qreal x, qreal y, const QTextItemInt &ti, int paintDeviceHeight)
+{
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> glyphs;
+ QTransform matrix;
+ matrix.translate(x, y);
+ getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
+ if (glyphs.size() == 0)
+ return;
+
+ CGContextSetFontSize(ctx, fontDef.pixelSize);
+
+ CGAffineTransform oldTextMatrix = CGContextGetTextMatrix(ctx);
+
+ CGAffineTransform cgMatrix = CGAffineTransformMake(1, 0, 0, -1, 0, -paintDeviceHeight);
+
+ CGAffineTransformConcat(cgMatrix, oldTextMatrix);
+
+ if (synthesisFlags & QFontEngine::SynthesizedItalic)
+ cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, -SYNTHETIC_ITALIC_SKEW, 1, 0, 0));
+
+ cgMatrix = CGAffineTransformConcat(cgMatrix, transform);
+
+ CGContextSetTextMatrix(ctx, cgMatrix);
+
+ CGContextSetTextDrawingMode(ctx, kCGTextFill);
+
+
+ QVarLengthArray<CGSize> advances(glyphs.size());
+ QVarLengthArray<CGGlyph> cgGlyphs(glyphs.size());
+
+ for (int i = 0; i < glyphs.size() - 1; ++i) {
+ advances[i].width = (positions[i + 1].x - positions[i].x).toReal();
+ advances[i].height = (positions[i + 1].y - positions[i].y).toReal();
+ cgGlyphs[i] = glyphs[i];
+ }
+ advances[glyphs.size() - 1].width = 0;
+ advances[glyphs.size() - 1].height = 0;
+ cgGlyphs[glyphs.size() - 1] = glyphs[glyphs.size() - 1];
+
+ CGContextSetFont(ctx, cgFont);
+ //NSLog(@"Font inDraw %@ ctfont %@", CGFontCopyFullName(cgFont), CTFontCopyFamilyName(ctfont));
+
+ CGContextSetTextPosition(ctx, positions[0].x.toReal(), positions[0].y.toReal());
+
+ CGContextShowGlyphsWithAdvances(ctx, cgGlyphs.data(), advances.data(), glyphs.size());
+
+ if (synthesisFlags & QFontEngine::SynthesizedBold) {
+ CGContextSetTextPosition(ctx, positions[0].x.toReal() + 0.5 * lineThickness().toReal(),
+ positions[0].y.toReal());
+
+ CGContextShowGlyphsWithAdvances(ctx, cgGlyphs.data(), advances.data(), glyphs.size());
+ }
+
+ CGContextSetTextMatrix(ctx, oldTextMatrix);
+}
+
+struct ConvertPathInfo
+{
+ ConvertPathInfo(QPainterPath *newPath, const QPointF &newPos) : path(newPath), pos(newPos) {}
+ QPainterPath *path;
+ QPointF pos;
+};
+
+static void convertCGPathToQPainterPath(void *info, const CGPathElement *element)
+{
+ ConvertPathInfo *myInfo = static_cast<ConvertPathInfo *>(info);
+ switch(element->type) {
+ case kCGPathElementMoveToPoint:
+ myInfo->path->moveTo(element->points[0].x + myInfo->pos.x(),
+ element->points[0].y + myInfo->pos.y());
+ break;
+ case kCGPathElementAddLineToPoint:
+ myInfo->path->lineTo(element->points[0].x + myInfo->pos.x(),
+ element->points[0].y + myInfo->pos.y());
+ break;
+ case kCGPathElementAddQuadCurveToPoint:
+ myInfo->path->quadTo(element->points[0].x + myInfo->pos.x(),
+ element->points[0].y + myInfo->pos.y(),
+ element->points[1].x + myInfo->pos.x(),
+ element->points[1].y + myInfo->pos.y());
+ break;
+ case kCGPathElementAddCurveToPoint:
+ myInfo->path->cubicTo(element->points[0].x + myInfo->pos.x(),
+ element->points[0].y + myInfo->pos.y(),
+ element->points[1].x + myInfo->pos.x(),
+ element->points[1].y + myInfo->pos.y(),
+ element->points[2].x + myInfo->pos.x(),
+ element->points[2].y + myInfo->pos.y());
+ break;
+ case kCGPathElementCloseSubpath:
+ myInfo->path->closeSubpath();
+ break;
+ default:
+ qDebug() << "Unhandled path transform type: " << element->type;
+ }
+
+}
+
+void QCoreTextFontEngine::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nGlyphs,
+ QPainterPath *path, QTextItem::RenderFlags)
+{
+ CGAffineTransform cgMatrix = CGAffineTransformIdentity;
+ cgMatrix = CGAffineTransformScale(cgMatrix, 1, -1);
+
+ if (synthesisFlags & QFontEngine::SynthesizedItalic)
+ cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, -SYNTHETIC_ITALIC_SKEW, 1, 0, 0));
+
+ for (int i = 0; i < nGlyphs; ++i) {
+ QCFType<CGPathRef> cgpath = CTFontCreatePathForGlyph(ctfont, glyphs[i], &cgMatrix);
+ ConvertPathInfo info(path, positions[i].toPointF());
+ CGPathApply(cgpath, &info, convertCGPathToQPainterPath);
+ }
+}
+
+QImage QCoreTextFontEngine::imageForGlyph(glyph_t glyph, QFixed subPixelPosition, int /*margin*/, bool aa)
+{
+ const glyph_metrics_t br = boundingBox(glyph);
+ QImage im(qRound(br.width)+2, qRound(br.height)+2, QImage::Format_RGB32);
+ im.fill(0);
+
+ CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
+ uint cgflags = kCGImageAlphaNoneSkipFirst;
+#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version
+ cgflags |= kCGBitmapByteOrder32Host;
+#endif
+ CGContextRef ctx = CGBitmapContextCreate(im.bits(), im.width(), im.height(),
+ 8, im.bytesPerLine(), colorspace,
+ cgflags);
+ CGContextSetFontSize(ctx, fontDef.pixelSize);
+ CGContextSetShouldAntialias(ctx, (aa || fontDef.pointSize > qt_antialiasing_threshold)
+ && !(fontDef.styleStrategy & QFont::NoAntialias));
+ CGContextSetShouldSmoothFonts(ctx, aa);
+ CGAffineTransform oldTextMatrix = CGContextGetTextMatrix(ctx);
+ CGAffineTransform cgMatrix = CGAffineTransformMake(1, 0, 0, 1, 0, 0);
+
+ CGAffineTransformConcat(cgMatrix, oldTextMatrix);
+
+ if (synthesisFlags & QFontEngine::SynthesizedItalic)
+ cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, SYNTHETIC_ITALIC_SKEW, 1, 0, 0));
+
+ cgMatrix = CGAffineTransformConcat(cgMatrix, transform);
+
+ CGContextSetTextMatrix(ctx, cgMatrix);
+ CGContextSetRGBFillColor(ctx, 1, 1, 1, 1);
+ CGContextSetTextDrawingMode(ctx, kCGTextFill);
+
+ CGContextSetFont(ctx, cgFont);
+
+ qreal pos_x = -br.x.truncate() + subPixelPosition.toReal();
+ qreal pos_y = im.height() + br.y.toReal();
+ CGContextSetTextPosition(ctx, pos_x, pos_y);
+
+ CGSize advance;
+ advance.width = 0;
+ advance.height = 0;
+ CGGlyph cgGlyph = glyph;
+ CGContextShowGlyphsWithAdvances(ctx, &cgGlyph, &advance, 1);
+
+ if (synthesisFlags & QFontEngine::SynthesizedBold) {
+ CGContextSetTextPosition(ctx, pos_x + 0.5 * lineThickness().toReal(), pos_y);
+ CGContextShowGlyphsWithAdvances(ctx, &cgGlyph, &advance, 1);
+ }
+
+ CGContextRelease(ctx);
+
+ return im;
+}
+
+QImage QCoreTextFontEngine::alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition)
+{
+ QImage im = imageForGlyph(glyph, subPixelPosition, 0, false);
+
+ QImage indexed(im.width(), im.height(), QImage::Format_Indexed8);
+ QVector<QRgb> colors(256);
+ for (int i=0; i<256; ++i)
+ colors[i] = qRgba(0, 0, 0, i);
+ indexed.setColorTable(colors);
+
+ for (int y=0; y<im.height(); ++y) {
+ uint *src = (uint*) im.scanLine(y);
+ uchar *dst = indexed.scanLine(y);
+ for (int x=0; x<im.width(); ++x) {
+ *dst = qGray(*src);
+ ++dst;
+ ++src;
+ }
+ }
+
+ return indexed;
+}
+
+QImage QCoreTextFontEngine::alphaRGBMapForGlyph(glyph_t glyph, QFixed subPixelPosition, int margin, const QTransform &x)
+{
+ if (x.type() >= QTransform::TxScale)
+ return QFontEngine::alphaRGBMapForGlyph(glyph, subPixelPosition, margin, x);
+
+ QImage im = imageForGlyph(glyph, subPixelPosition, margin, true);
+ qGamma_correct_back_to_linear_cs(&im);
+ return im;
+}
+
+void QCoreTextFontEngine::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const
+{
+ int i, numGlyphs = glyphs->numGlyphs;
+ QVarLengthArray<CGGlyph> cgGlyphs(numGlyphs);
+
+ for (i = 0; i < numGlyphs; ++i) {
+ if (glyphs->glyphs[i] & 0xff000000)
+ cgGlyphs[i] = 0;
+ else
+ cgGlyphs[i] = glyphs->glyphs[i];
+ }
+
+ loadAdvancesForGlyphs(ctfont, cgGlyphs, glyphs, numGlyphs, flags, fontDef);
+}
+
+QFontEngine::FaceId QCoreTextFontEngine::faceId() const
+{
+ return QFontEngine::FaceId();
+}
+
+bool QCoreTextFontEngine::canRender(const QChar *string, int len)
+{
+ QVarLengthArray<CGGlyph> cgGlyphs(len);
+ return CTFontGetGlyphsForCharacters(ctfont, (const UniChar *) string, cgGlyphs.data(), len);
+}
+
+bool QCoreTextFontEngine::getSfntTableData(uint tag, uchar *buffer, uint *length) const
+{
+ QCFType<CFDataRef> table = CTFontCopyTable(ctfont, tag, 0);
+ if (!table || !length)
+ return false;
+ CFIndex tableLength = CFDataGetLength(table);
+ int availableLength = *length;
+ *length = tableLength;
+ if (buffer) {
+ if (tableLength > availableLength)
+ return false;
+ CFDataGetBytes(table, CFRangeMake(0, tableLength), buffer);
+ }
+ return true;
+}
+
+void QCoreTextFontEngine::getUnscaledGlyph(glyph_t, QPainterPath *, glyph_metrics_t *)
+{
+ // ###
+}
+
+QFixed QCoreTextFontEngine::emSquareSize() const
+{
+ return QFixed::QFixed(int(CTFontGetUnitsPerEm(ctfont)));
+}
+
+QFontEngine *QCoreTextFontEngine::cloneWithSize(qreal pixelSize) const
+{
+ QFontDef newFontDef = fontDef;
+ newFontDef.pixelSize = pixelSize;
+ newFontDef.pointSize = pixelSize * 72.0 / qt_defaultDpi();
+
+ return new QCoreTextFontEngine(cgFont, newFontDef);
+}
+
+QT_END_NAMESPACE
+
+#endif// !defined(Q_WS_MAC) || (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+
diff --git a/src/widgets/platforms/mac/qfontengine_coretext_p.h b/src/widgets/platforms/mac/qfontengine_coretext_p.h
new file mode 100644
index 0000000000..d8a3033be6
--- /dev/null
+++ b/src/widgets/platforms/mac/qfontengine_coretext_p.h
@@ -0,0 +1,153 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFONTENGINE_CORETEXT_P_H
+#define QFONTENGINE_CORETEXT_P_H
+
+#include <private/qfontengine_p.h>
+
+#if !defined(Q_WS_MAC) || (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QRawFontPrivate;
+class QCoreTextFontEngineMulti;
+class QCoreTextFontEngine : public QFontEngine
+{
+public:
+ QCoreTextFontEngine(CTFontRef font, const QFontDef &def);
+ QCoreTextFontEngine(CGFontRef font, const QFontDef &def);
+ ~QCoreTextFontEngine();
+
+ virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const;
+ virtual void recalcAdvances(QGlyphLayout *, QTextEngine::ShaperFlags) const;
+
+ virtual glyph_metrics_t boundingBox(const QGlyphLayout &glyphs);
+ virtual glyph_metrics_t boundingBox(glyph_t glyph);
+
+ virtual QFixed ascent() const;
+ virtual QFixed descent() const;
+ virtual QFixed leading() const;
+ virtual QFixed xHeight() const;
+ virtual qreal maxCharWidth() const;
+ virtual QFixed averageCharWidth() const;
+
+ virtual void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int numGlyphs,
+ QPainterPath *path, QTextItem::RenderFlags);
+
+ virtual const char *name() const { return "QCoreTextFontEngine"; }
+
+ virtual bool canRender(const QChar *string, int len);
+
+ virtual int synthesized() const { return synthesisFlags; }
+ virtual bool supportsSubPixelPositions() const { return true; }
+
+ virtual Type type() const { return QFontEngine::Mac; }
+
+ void draw(CGContextRef ctx, qreal x, qreal y, const QTextItemInt &ti, int paintDeviceHeight);
+
+ virtual FaceId faceId() const;
+ virtual bool getSfntTableData(uint /*tag*/, uchar * /*buffer*/, uint * /*length*/) const;
+ virtual void getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics);
+ virtual QImage alphaMapForGlyph(glyph_t, QFixed subPixelPosition);
+ virtual QImage alphaRGBMapForGlyph(glyph_t, QFixed subPixelPosition, int margin, const QTransform &t);
+ virtual qreal minRightBearing() const;
+ virtual qreal minLeftBearing() const;
+ virtual QFixed emSquareSize() const;
+
+ virtual QFontEngine *cloneWithSize(qreal pixelSize) const;
+
+private:
+ friend class QRawFontPrivate;
+
+ void init();
+ QImage imageForGlyph(glyph_t glyph, QFixed subPixelPosition, int margin, bool colorful);
+ CTFontRef ctfont;
+ CGFontRef cgFont;
+ int synthesisFlags;
+ CGAffineTransform transform;
+ QFixed avgCharWidth;
+ friend class QCoreTextFontEngineMulti;
+};
+
+class QCoreTextFontEngineMulti : public QFontEngineMulti
+{
+public:
+ QCoreTextFontEngineMulti(const QCFString &name, const QFontDef &fontDef, bool kerning);
+ QCoreTextFontEngineMulti(CTFontRef ctFontRef, const QFontDef &fontDef, bool kerning);
+ ~QCoreTextFontEngineMulti();
+
+ virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs,
+ QTextEngine::ShaperFlags flags) const;
+ bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs,
+ QTextEngine::ShaperFlags flags,
+ unsigned short *logClusters, const HB_CharAttributes *charAttributes,
+ QScriptItem *si) const;
+
+ virtual const char *name() const { return "CoreText"; }
+ inline CTFontRef macFontID() const { return ctfont; }
+
+protected:
+ virtual void loadEngine(int at);
+
+private:
+ void init(bool kerning);
+ inline const QCoreTextFontEngine *engineAt(int i) const
+ { return static_cast<const QCoreTextFontEngine *>(engines.at(i)); }
+
+ uint fontIndexForFont(CTFontRef font) const;
+ CTFontRef ctfont;
+ mutable QCFType<CFMutableDictionaryRef> attributeDict;
+ CGAffineTransform transform;
+ friend class QFontDialogPrivate;
+};
+
+CGAffineTransform qt_transform_from_fontdef(const QFontDef &fontDef);
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif// !defined(Q_WS_MAC) || (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+
+#endif // QFONTENGINE_CORETEXT_P_H
diff --git a/src/widgets/platforms/mac/qfontengine_mac.mm b/src/widgets/platforms/mac/qfontengine_mac.mm
new file mode 100644
index 0000000000..6186b2f514
--- /dev/null
+++ b/src/widgets/platforms/mac/qfontengine_mac.mm
@@ -0,0 +1,1229 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qfontengine_mac_p.h"
+
+#include <private/qapplication_p.h>
+#include <private/qfontengine_p.h>
+#include <private/qpainter_p.h>
+#include <private/qtextengine_p.h>
+#include <qbitmap.h>
+#include <private/qpaintengine_mac_p.h>
+#include <private/qprintengine_mac_p.h>
+#include <qglobal.h>
+#include <qpixmap.h>
+#include <qpixmapcache.h>
+#include <qvarlengtharray.h>
+#include <qdebug.h>
+#include <qendian.h>
+#include <qmath.h>
+#include <private/qimage_p.h>
+
+#include <ApplicationServices/ApplicationServices.h>
+#include <AppKit/AppKit.h>
+
+QT_BEGIN_NAMESPACE
+
+/*****************************************************************************
+ QFontEngine debug facilities
+ *****************************************************************************/
+//#define DEBUG_ADVANCES
+
+extern int qt_antialiasing_threshold; // QApplication.cpp
+
+#ifndef FixedToQFixed
+#define FixedToQFixed(a) QFixed::fromFixed((a) >> 10)
+#define QFixedToFixed(x) ((x).value() << 10)
+#endif
+
+class QMacFontPath
+{
+ float x, y;
+ QPainterPath *path;
+public:
+ inline QMacFontPath(float _x, float _y, QPainterPath *_path) : x(_x), y(_y), path(_path) { }
+ inline void setPosition(float _x, float _y) { x = _x; y = _y; }
+ inline void advance(float _x) { x += _x; }
+ static OSStatus lineTo(const Float32Point *, void *);
+ static OSStatus cubicTo(const Float32Point *, const Float32Point *,
+ const Float32Point *, void *);
+ static OSStatus moveTo(const Float32Point *, void *);
+ static OSStatus closePath(void *);
+};
+
+OSStatus QMacFontPath::lineTo(const Float32Point *pt, void *data)
+
+{
+ QMacFontPath *p = static_cast<QMacFontPath*>(data);
+ p->path->lineTo(p->x + pt->x, p->y + pt->y);
+ return noErr;
+}
+
+OSStatus QMacFontPath::cubicTo(const Float32Point *cp1, const Float32Point *cp2,
+ const Float32Point *ep, void *data)
+
+{
+ QMacFontPath *p = static_cast<QMacFontPath*>(data);
+ p->path->cubicTo(p->x + cp1->x, p->y + cp1->y,
+ p->x + cp2->x, p->y + cp2->y,
+ p->x + ep->x, p->y + ep->y);
+ return noErr;
+}
+
+OSStatus QMacFontPath::moveTo(const Float32Point *pt, void *data)
+{
+ QMacFontPath *p = static_cast<QMacFontPath*>(data);
+ p->path->moveTo(p->x + pt->x, p->y + pt->y);
+ return noErr;
+}
+
+OSStatus QMacFontPath::closePath(void *data)
+{
+ static_cast<QMacFontPath*>(data)->path->closeSubpath();
+ return noErr;
+}
+
+
+#ifndef QT_MAC_USE_COCOA
+QFontEngineMacMulti::QFontEngineMacMulti(const ATSFontFamilyRef &atsFamily, const ATSFontRef &atsFontRef, const QFontDef &fontDef, bool kerning)
+ : QFontEngineMulti(0)
+{
+ this->fontDef = fontDef;
+ this->kerning = kerning;
+
+ // hopefully (CTFontCreateWithName or CTFontCreateWithFontDescriptor) + CTFontCreateCopyWithSymbolicTraits
+ // (or CTFontCreateWithQuickdrawInstance)
+ FMFontFamily fmFamily;
+ FMFontStyle fntStyle = 0;
+ fmFamily = FMGetFontFamilyFromATSFontFamilyRef(atsFamily);
+ if (fmFamily == kInvalidFontFamily) {
+ // Use the ATSFont then...
+ fontID = FMGetFontFromATSFontRef(atsFontRef);
+ } else {
+ if (fontDef.weight >= QFont::Bold)
+ fntStyle |= ::bold;
+ if (fontDef.style != QFont::StyleNormal)
+ fntStyle |= ::italic;
+
+ FMFontStyle intrinsicStyle;
+ FMFont fnt = 0;
+ if (FMGetFontFromFontFamilyInstance(fmFamily, fntStyle, &fnt, &intrinsicStyle) == noErr)
+ fontID = FMGetATSFontRefFromFont(fnt);
+ }
+
+ // CFDictionaryRef, <CTStringAttributes.h>
+ OSStatus status;
+
+ status = ATSUCreateTextLayout(&textLayout);
+ Q_ASSERT(status == noErr);
+
+ const int maxAttributeCount = 5;
+ ATSUAttributeTag tags[maxAttributeCount + 1];
+ ByteCount sizes[maxAttributeCount + 1];
+ ATSUAttributeValuePtr values[maxAttributeCount + 1];
+ int attributeCount = 0;
+
+ Fixed size = FixRatio(fontDef.pixelSize, 1);
+ tags[attributeCount] = kATSUSizeTag;
+ sizes[attributeCount] = sizeof(size);
+ values[attributeCount] = &size;
+ ++attributeCount;
+
+ tags[attributeCount] = kATSUFontTag;
+ sizes[attributeCount] = sizeof(fontID);
+ values[attributeCount] = &this->fontID;
+ ++attributeCount;
+
+ transform = CGAffineTransformIdentity;
+ if (fontDef.stretch != 100) {
+ transform = CGAffineTransformMakeScale(float(fontDef.stretch) / float(100), 1);
+ tags[attributeCount] = kATSUFontMatrixTag;
+ sizes[attributeCount] = sizeof(transform);
+ values[attributeCount] = &transform;
+ ++attributeCount;
+ }
+
+ status = ATSUCreateStyle(&style);
+ Q_ASSERT(status == noErr);
+
+ Q_ASSERT(attributeCount < maxAttributeCount + 1);
+ status = ATSUSetAttributes(style, attributeCount, tags, sizes, values);
+ Q_ASSERT(status == noErr);
+
+ QFontEngineMac *fe = new QFontEngineMac(style, fontID, fontDef, this);
+ fe->ref.ref();
+ engines.append(fe);
+}
+
+QFontEngineMacMulti::~QFontEngineMacMulti()
+{
+ ATSUDisposeTextLayout(textLayout);
+ ATSUDisposeStyle(style);
+
+ for (int i = 0; i < engines.count(); ++i) {
+ QFontEngineMac *fe = const_cast<QFontEngineMac *>(static_cast<const QFontEngineMac *>(engines.at(i)));
+ fe->multiEngine = 0;
+ if (!fe->ref.deref())
+ delete fe;
+ }
+ engines.clear();
+}
+
+struct QGlyphLayoutInfo
+{
+ QGlyphLayout *glyphs;
+ int *numGlyphs;
+ bool callbackCalled;
+ int *mappedFonts;
+ QTextEngine::ShaperFlags flags;
+ QFontEngineMacMulti::ShaperItem *shaperItem;
+ unsigned int styleStrategy;
+};
+
+static OSStatus atsuPostLayoutCallback(ATSULayoutOperationSelector selector, ATSULineRef lineRef, URefCon refCon,
+ void *operationExtraParameter, ATSULayoutOperationCallbackStatus *callbackStatus)
+{
+ Q_UNUSED(selector);
+ Q_UNUSED(operationExtraParameter);
+
+ QGlyphLayoutInfo *nfo = reinterpret_cast<QGlyphLayoutInfo *>(refCon);
+ nfo->callbackCalled = true;
+
+ ATSLayoutRecord *layoutData = 0;
+ ItemCount itemCount = 0;
+
+ OSStatus e = noErr;
+ e = ATSUDirectGetLayoutDataArrayPtrFromLineRef(lineRef, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,
+ /*iCreate =*/ false,
+ (void **) &layoutData,
+ &itemCount);
+ if (e != noErr)
+ return e;
+
+ *nfo->numGlyphs = itemCount - 1;
+
+ Fixed *baselineDeltas = 0;
+
+ e = ATSUDirectGetLayoutDataArrayPtrFromLineRef(lineRef, kATSUDirectDataBaselineDeltaFixedArray,
+ /*iCreate =*/ true,
+ (void **) &baselineDeltas,
+ &itemCount);
+ if (e != noErr)
+ return e;
+
+ int nextCharStop = -1;
+ int currentClusterGlyph = -1; // first glyph in log cluster
+ QFontEngineMacMulti::ShaperItem *item = nfo->shaperItem;
+ if (item->charAttributes) {
+ item = nfo->shaperItem;
+#if !defined(QT_NO_DEBUG)
+ int surrogates = 0;
+ const QChar *str = item->string;
+ for (int i = item->from; i < item->from + item->length - 1; ++i)
+ surrogates += (str[i].isHighSurrogate() && str[i+1].isLowSurrogate());
+#endif
+ for (nextCharStop = item->from; nextCharStop < item->from + item->length; ++nextCharStop)
+ if (item->charAttributes[nextCharStop].charStop)
+ break;
+ nextCharStop -= item->from;
+ }
+
+ nfo->glyphs->attributes[0].clusterStart = true;
+ int glyphIdx = 0;
+ int glyphIncrement = 1;
+ if (nfo->flags & QTextEngine::RightToLeft) {
+ glyphIdx = itemCount - 2;
+ glyphIncrement = -1;
+ }
+ for (int i = 0; i < *nfo->numGlyphs; ++i, glyphIdx += glyphIncrement) {
+
+ int charOffset = layoutData[glyphIdx].originalOffset / sizeof(UniChar);
+ const int fontIdx = nfo->mappedFonts[charOffset];
+
+ ATSGlyphRef glyphId = layoutData[glyphIdx].glyphID;
+
+ QFixed yAdvance = FixedToQFixed(baselineDeltas[glyphIdx]);
+ QFixed xAdvance = FixedToQFixed(layoutData[glyphIdx + 1].realPos - layoutData[glyphIdx].realPos);
+
+ if (nfo->styleStrategy & QFont::ForceIntegerMetrics) {
+ yAdvance = yAdvance.round();
+ xAdvance = xAdvance.round();
+ }
+
+ if (glyphId != 0xffff || i == 0) {
+ if (i < nfo->glyphs->numGlyphs)
+ {
+ nfo->glyphs->glyphs[i] = (glyphId & 0x00ffffff) | (fontIdx << 24);
+
+ nfo->glyphs->advances_y[i] = yAdvance;
+ nfo->glyphs->advances_x[i] = xAdvance;
+ }
+ } else {
+ // ATSUI gives us 0xffff as glyph id at the index in the glyph array for
+ // a character position that maps to a ligtature. Such a glyph id does not
+ // result in any visual glyph, but it may have an advance, which is why we
+ // sum up the glyph advances.
+ --i;
+ nfo->glyphs->advances_y[i] += yAdvance;
+ nfo->glyphs->advances_x[i] += xAdvance;
+ *nfo->numGlyphs -= 1;
+ }
+
+ if (item->log_clusters) {
+ if (charOffset >= nextCharStop) {
+ nfo->glyphs->attributes[i].clusterStart = true;
+ currentClusterGlyph = i;
+
+ ++nextCharStop;
+ for (; nextCharStop < item->length; ++nextCharStop)
+ if (item->charAttributes[item->from + nextCharStop].charStop)
+ break;
+ } else {
+ if (currentClusterGlyph == -1)
+ currentClusterGlyph = i;
+ }
+ item->log_clusters[charOffset] = currentClusterGlyph;
+
+ // surrogate handling
+ if (charOffset < item->length - 1) {
+ QChar current = item->string[item->from + charOffset];
+ QChar next = item->string[item->from + charOffset + 1];
+ if (current.isHighSurrogate() && next.isLowSurrogate())
+ item->log_clusters[charOffset + 1] = currentClusterGlyph;
+ }
+ }
+ }
+
+ /*
+ if (item) {
+ qDebug() << "resulting logclusters:";
+ for (int i = 0; i < item->length; ++i)
+ qDebug() << "logClusters[" << i << "] =" << item->log_clusters[i];
+ qDebug() << "clusterstarts:";
+ for (int i = 0; i < *nfo->numGlyphs; ++i)
+ qDebug() << "clusterStart[" << i << "] =" << nfo->glyphs[i].attributes.clusterStart;
+ }
+ */
+
+ ATSUDirectReleaseLayoutDataArrayPtr(lineRef, kATSUDirectDataBaselineDeltaFixedArray,
+ (void **) &baselineDeltas);
+
+ ATSUDirectReleaseLayoutDataArrayPtr(lineRef, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,
+ (void **) &layoutData);
+
+ *callbackStatus = kATSULayoutOperationCallbackStatusHandled;
+ return noErr;
+}
+
+int QFontEngineMacMulti::fontIndexForFontID(ATSUFontID id) const
+{
+ for (int i = 0; i < engines.count(); ++i) {
+ if (engineAt(i)->fontID == id)
+ return i;
+ }
+
+ QFontEngineMacMulti *that = const_cast<QFontEngineMacMulti *>(this);
+ QFontEngineMac *fe = new QFontEngineMac(style, id, fontDef, that);
+ fe->ref.ref();
+ that->engines.append(fe);
+ return engines.count() - 1;
+}
+
+bool QFontEngineMacMulti::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const
+{
+ return stringToCMap(str, len, glyphs, nglyphs, flags, /*logClusters=*/0, /*charAttributes=*/0);
+}
+
+bool QFontEngineMacMulti::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags,
+ unsigned short *logClusters, const HB_CharAttributes *charAttributes, QScriptItem *) const
+{
+ if (*nglyphs < len) {
+ *nglyphs = len;
+ return false;
+ }
+
+ ShaperItem shaperItem;
+ shaperItem.string = str;
+ shaperItem.from = 0;
+ shaperItem.length = len;
+ shaperItem.glyphs = *glyphs;
+ shaperItem.glyphs.numGlyphs = *nglyphs;
+ shaperItem.flags = flags;
+ shaperItem.log_clusters = logClusters;
+ shaperItem.charAttributes = charAttributes;
+
+ const int maxChars = qMax(1,
+ int(SHRT_MAX / maxCharWidth())
+ - 10 // subtract a few to be on the safe side
+ );
+ if (len < maxChars || !charAttributes)
+ return stringToCMapInternal(str, len, glyphs, nglyphs, flags, &shaperItem);
+
+ int charIdx = 0;
+ int glyphIdx = 0;
+ ShaperItem tmpItem = shaperItem;
+
+ do {
+ tmpItem.from = shaperItem.from + charIdx;
+
+ int charCount = qMin(maxChars, len - charIdx);
+
+ int lastWhitespace = tmpItem.from + charCount - 1;
+ int lastSoftBreak = lastWhitespace;
+ int lastCharStop = lastSoftBreak;
+ for (int i = lastCharStop; i >= tmpItem.from; --i) {
+ if (tmpItem.charAttributes[i].whiteSpace) {
+ lastWhitespace = i;
+ break;
+ } if (tmpItem.charAttributes[i].lineBreakType != HB_NoBreak) {
+ lastSoftBreak = i;
+ } if (tmpItem.charAttributes[i].charStop) {
+ lastCharStop = i;
+ }
+ }
+ charCount = qMin(lastWhitespace, qMin(lastSoftBreak, lastCharStop)) - tmpItem.from + 1;
+
+ int glyphCount = shaperItem.glyphs.numGlyphs - glyphIdx;
+ if (glyphCount <= 0)
+ return false;
+ tmpItem.length = charCount;
+ tmpItem.glyphs = shaperItem.glyphs.mid(glyphIdx, glyphCount);
+ tmpItem.log_clusters = shaperItem.log_clusters + charIdx;
+ if (!stringToCMapInternal(tmpItem.string + tmpItem.from, tmpItem.length,
+ &tmpItem.glyphs, &glyphCount, flags,
+ &tmpItem)) {
+ *nglyphs = glyphIdx + glyphCount;
+ return false;
+ }
+ for (int i = 0; i < charCount; ++i)
+ tmpItem.log_clusters[i] += glyphIdx;
+ glyphIdx += glyphCount;
+ charIdx += charCount;
+ } while (charIdx < len);
+ *nglyphs = glyphIdx;
+ glyphs->numGlyphs = glyphIdx;
+
+ return true;
+}
+
+bool QFontEngineMacMulti::stringToCMapInternal(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags,ShaperItem *shaperItem) const
+{
+ //qDebug() << "stringToCMap" << QString(str, len);
+
+ OSStatus e = noErr;
+
+ e = ATSUSetTextPointerLocation(textLayout, (UniChar *)(str), 0, len, len);
+ if (e != noErr) {
+ qWarning("Qt: internal: %ld: Error ATSUSetTextPointerLocation %s: %d", long(e), __FILE__, __LINE__);
+ return false;
+ }
+
+ QGlyphLayoutInfo nfo;
+ nfo.glyphs = glyphs;
+ nfo.numGlyphs = nglyphs;
+ nfo.callbackCalled = false;
+ nfo.flags = flags;
+ nfo.shaperItem = shaperItem;
+ nfo.styleStrategy = fontDef.styleStrategy;
+
+ int prevNumGlyphs = *nglyphs;
+
+ QVarLengthArray<int> mappedFonts(len);
+ for (int i = 0; i < len; ++i)
+ mappedFonts[i] = 0;
+ nfo.mappedFonts = mappedFonts.data();
+
+ Q_ASSERT(sizeof(void *) <= sizeof(URefCon));
+ e = ATSUSetTextLayoutRefCon(textLayout, (URefCon)&nfo);
+ if (e != noErr) {
+ qWarning("Qt: internal: %ld: Error ATSUSetTextLayoutRefCon %s: %d", long(e), __FILE__, __LINE__);
+ return false;
+ }
+
+ {
+ const int maxAttributeCount = 3;
+ ATSUAttributeTag tags[maxAttributeCount + 1];
+ ByteCount sizes[maxAttributeCount + 1];
+ ATSUAttributeValuePtr values[maxAttributeCount + 1];
+ int attributeCount = 0;
+
+ tags[attributeCount] = kATSULineLayoutOptionsTag;
+ ATSLineLayoutOptions layopts = kATSLineHasNoOpticalAlignment
+ | kATSLineIgnoreFontLeading
+ | kATSLineNoSpecialJustification // we do kashidas ourselves
+ | kATSLineDisableAllJustification
+ ;
+
+ if (fontDef.styleStrategy & QFont::NoAntialias)
+ layopts |= kATSLineNoAntiAliasing;
+
+ if (!kerning)
+ layopts |= kATSLineDisableAllKerningAdjustments;
+
+ values[attributeCount] = &layopts;
+ sizes[attributeCount] = sizeof(layopts);
+ ++attributeCount;
+
+ tags[attributeCount] = kATSULayoutOperationOverrideTag;
+ ATSULayoutOperationOverrideSpecifier spec;
+ spec.operationSelector = kATSULayoutOperationPostLayoutAdjustment;
+ spec.overrideUPP = atsuPostLayoutCallback;
+ values[attributeCount] = &spec;
+ sizes[attributeCount] = sizeof(spec);
+ ++attributeCount;
+
+ // CTWritingDirection
+ Boolean direction;
+ if (flags & QTextEngine::RightToLeft)
+ direction = kATSURightToLeftBaseDirection;
+ else
+ direction = kATSULeftToRightBaseDirection;
+ tags[attributeCount] = kATSULineDirectionTag;
+ values[attributeCount] = &direction;
+ sizes[attributeCount] = sizeof(direction);
+ ++attributeCount;
+
+ Q_ASSERT(attributeCount < maxAttributeCount + 1);
+ e = ATSUSetLayoutControls(textLayout, attributeCount, tags, sizes, values);
+ if (e != noErr) {
+ qWarning("Qt: internal: %ld: Error ATSUSetLayoutControls %s: %d", long(e), __FILE__, __LINE__);
+ return false;
+ }
+
+ }
+
+ e = ATSUSetRunStyle(textLayout, style, 0, len);
+ if (e != noErr) {
+ qWarning("Qt: internal: %ld: Error ATSUSetRunStyle %s: %d", long(e), __FILE__, __LINE__);
+ return false;
+ }
+
+ if (!(fontDef.styleStrategy & QFont::NoFontMerging)) {
+ int pos = 0;
+ do {
+ ATSUFontID substFont = 0;
+ UniCharArrayOffset changedOffset = 0;
+ UniCharCount changeCount = 0;
+
+ e = ATSUMatchFontsToText(textLayout, pos, len - pos,
+ &substFont, &changedOffset,
+ &changeCount);
+ if (e == kATSUFontsMatched) {
+ int fontIdx = fontIndexForFontID(substFont);
+ for (uint i = 0; i < changeCount; ++i)
+ mappedFonts[changedOffset + i] = fontIdx;
+ pos = changedOffset + changeCount;
+ ATSUSetRunStyle(textLayout, engineAt(fontIdx)->style, changedOffset, changeCount);
+ } else if (e == kATSUFontsNotMatched) {
+ pos = changedOffset + changeCount;
+ }
+ } while (pos < len && e != noErr);
+ }
+ { // trigger the a layout
+ // CFAttributedStringCreate, CTFramesetterCreateWithAttributedString (or perhaps Typesetter)
+ Rect rect;
+ e = ATSUMeasureTextImage(textLayout, kATSUFromTextBeginning, kATSUToTextEnd,
+ /*iLocationX =*/ 0, /*iLocationY =*/ 0,
+ &rect);
+ if (e != noErr) {
+ qWarning("Qt: internal: %ld: Error ATSUMeasureTextImage %s: %d", long(e), __FILE__, __LINE__);
+ return false;
+ }
+ }
+
+ if (!nfo.callbackCalled) {
+ qWarning("Qt: internal: %ld: Error ATSUMeasureTextImage did not trigger callback %s: %d", long(e), __FILE__, __LINE__);
+ return false;
+ }
+
+ ATSUClearLayoutCache(textLayout, kATSUFromTextBeginning);
+ if (prevNumGlyphs < *nfo.numGlyphs)
+ return false;
+ return true;
+}
+
+void QFontEngineMacMulti::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const
+{
+ Q_ASSERT(false);
+ Q_UNUSED(glyphs);
+ Q_UNUSED(flags);
+}
+
+void QFontEngineMacMulti::doKerning(QGlyphLayout *, QTextEngine::ShaperFlags) const
+{
+ //Q_ASSERT(false);
+}
+
+void QFontEngineMacMulti::loadEngine(int /*at*/)
+{
+ // should never be called!
+ Q_ASSERT(false);
+}
+
+bool QFontEngineMacMulti::canRender(const QChar *string, int len)
+{
+ ATSUSetTextPointerLocation(textLayout, reinterpret_cast<const UniChar *>(string), 0, len, len);
+ ATSUSetRunStyle(textLayout, style, 0, len);
+
+ OSStatus e = noErr;
+ int pos = 0;
+ do {
+ FMFont substFont = 0;
+ UniCharArrayOffset changedOffset = 0;
+ UniCharCount changeCount = 0;
+
+ // CTFontCreateForString
+ e = ATSUMatchFontsToText(textLayout, pos, len - pos,
+ &substFont, &changedOffset,
+ &changeCount);
+ if (e == kATSUFontsMatched) {
+ pos = changedOffset + changeCount;
+ } else if (e == kATSUFontsNotMatched) {
+ break;
+ }
+ } while (pos < len && e != noErr);
+
+ return e == noErr || e == kATSUFontsMatched;
+}
+
+QFontEngineMac::QFontEngineMac(ATSUStyle baseStyle, ATSUFontID fontID, const QFontDef &def, QFontEngineMacMulti *multiEngine)
+ : fontID(fontID), multiEngine(multiEngine), cmap(0), symbolCMap(false)
+{
+ fontDef = def;
+ ATSUCreateAndCopyStyle(baseStyle, &style);
+ ATSFontRef atsFont = FMGetATSFontRefFromFont(fontID);
+ cgFont = CGFontCreateWithPlatformFont(&atsFont);
+
+ const int maxAttributeCount = 4;
+ ATSUAttributeTag tags[maxAttributeCount + 1];
+ ByteCount sizes[maxAttributeCount + 1];
+ ATSUAttributeValuePtr values[maxAttributeCount + 1];
+ int attributeCount = 0;
+
+ synthesisFlags = 0;
+
+ // synthesizing using CG is not recommended
+ quint16 macStyle = 0;
+ {
+ uchar data[4];
+ ByteCount len = 4;
+ if (ATSFontGetTable(atsFont, MAKE_TAG('h', 'e', 'a', 'd'), 44, 4, &data, &len) == noErr)
+ macStyle = qFromBigEndian<quint16>(data);
+ }
+
+ Boolean atsuBold = false;
+ Boolean atsuItalic = false;
+ if (fontDef.weight >= QFont::Bold) {
+ if (!(macStyle & 1)) {
+ synthesisFlags |= SynthesizedBold;
+ atsuBold = true;
+ tags[attributeCount] = kATSUQDBoldfaceTag;
+ sizes[attributeCount] = sizeof(atsuBold);
+ values[attributeCount] = &atsuBold;
+ ++attributeCount;
+ }
+ }
+ if (fontDef.style != QFont::StyleNormal) {
+ if (!(macStyle & 2)) {
+ synthesisFlags |= SynthesizedItalic;
+ atsuItalic = true;
+ tags[attributeCount] = kATSUQDItalicTag;
+ sizes[attributeCount] = sizeof(atsuItalic);
+ values[attributeCount] = &atsuItalic;
+ ++attributeCount;
+ }
+ }
+
+ tags[attributeCount] = kATSUFontTag;
+ values[attributeCount] = &fontID;
+ sizes[attributeCount] = sizeof(fontID);
+ ++attributeCount;
+
+ Q_ASSERT(attributeCount < maxAttributeCount + 1);
+ OSStatus err = ATSUSetAttributes(style, attributeCount, tags, sizes, values);
+ Q_ASSERT(err == noErr);
+ Q_UNUSED(err);
+
+ // CTFontCopyTable
+ quint16 tmpFsType;
+ if (ATSFontGetTable(atsFont, MAKE_TAG('O', 'S', '/', '2'), 8, 2, &tmpFsType, 0) == noErr)
+ fsType = qFromBigEndian<quint16>(tmpFsType);
+ else
+ fsType = 0;
+
+ if (multiEngine)
+ transform = multiEngine->transform;
+ else
+ transform = CGAffineTransformIdentity;
+
+ ATSUTextMeasurement metric;
+
+ ATSUGetAttribute(style, kATSUAscentTag, sizeof(metric), &metric, 0);
+ m_ascent = FixRound(metric);
+
+ ATSUGetAttribute(style, kATSUDescentTag, sizeof(metric), &metric, 0);
+ m_descent = FixRound(metric);
+
+ ATSUGetAttribute(style, kATSULeadingTag, sizeof(metric), &metric, 0);
+ m_leading = FixRound(metric);
+
+ ATSFontMetrics metrics;
+
+ ATSFontGetHorizontalMetrics(FMGetATSFontRefFromFont(fontID), kATSOptionFlagsDefault, &metrics);
+ m_maxCharWidth = metrics.maxAdvanceWidth * fontDef.pointSize;
+
+ ATSFontGetHorizontalMetrics(FMGetATSFontRefFromFont(fontID), kATSOptionFlagsDefault, &metrics);
+ m_xHeight = QFixed::fromReal(metrics.xHeight * fontDef.pointSize);
+
+ ATSFontGetHorizontalMetrics(FMGetATSFontRefFromFont(fontID), kATSOptionFlagsDefault, &metrics);
+ m_averageCharWidth = QFixed::fromReal(metrics.avgAdvanceWidth * fontDef.pointSize);
+
+ // Use width of 'X' if ATSFontGetHorizontalMetrics returns 0 for avgAdvanceWidth.
+ if (m_averageCharWidth == QFixed(0)) {
+ QChar c('X');
+ QGlyphLayoutArray<1> glyphs;
+ int nglyphs = 1;
+ stringToCMap(&c, 1, &glyphs, &nglyphs, 0);
+ glyph_metrics_t metrics = boundingBox(glyphs);
+ m_averageCharWidth = metrics.width;
+ }
+}
+
+QFontEngineMac::~QFontEngineMac()
+{
+ ATSUDisposeStyle(style);
+}
+
+static inline unsigned int getChar(const QChar *str, int &i, const int len)
+{
+ uint ucs4 = str[i].unicode();
+ if (str[i].isHighSurrogate() && i < len-1 && str[i+1].isLowSurrogate()) {
+ ++i;
+ ucs4 = QChar::surrogateToUcs4(ucs4, str[i].unicode());
+ }
+ return ucs4;
+}
+
+// Not used directly for shaping, only used to calculate m_averageCharWidth
+bool QFontEngineMac::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const
+{
+ if (!cmap) {
+ cmapTable = getSfntTable(MAKE_TAG('c', 'm', 'a', 'p'));
+ int size = 0;
+ cmap = getCMap(reinterpret_cast<const uchar *>(cmapTable.constData()), cmapTable.size(), &symbolCMap, &size);
+ if (!cmap)
+ return false;
+ }
+ if (symbolCMap) {
+ for (int i = 0; i < len; ++i) {
+ unsigned int uc = getChar(str, i, len);
+ glyphs->glyphs[i] = getTrueTypeGlyphIndex(cmap, uc);
+ if(!glyphs->glyphs[i] && uc < 0x100)
+ glyphs->glyphs[i] = getTrueTypeGlyphIndex(cmap, uc + 0xf000);
+ }
+ } else {
+ for (int i = 0; i < len; ++i) {
+ unsigned int uc = getChar(str, i, len);
+ glyphs->glyphs[i] = getTrueTypeGlyphIndex(cmap, uc);
+ }
+ }
+
+ *nglyphs = len;
+ glyphs->numGlyphs = *nglyphs;
+
+ if (!(flags & QTextEngine::GlyphIndicesOnly))
+ recalcAdvances(glyphs, flags);
+
+ return true;
+}
+
+void QFontEngineMac::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const
+{
+ Q_UNUSED(flags)
+
+ QVarLengthArray<GlyphID> atsuGlyphs(glyphs->numGlyphs);
+ for (int i = 0; i < glyphs->numGlyphs; ++i)
+ atsuGlyphs[i] = glyphs->glyphs[i];
+
+ QVarLengthArray<ATSGlyphScreenMetrics> metrics(glyphs->numGlyphs);
+
+ ATSUGlyphGetScreenMetrics(style, glyphs->numGlyphs, atsuGlyphs.data(), sizeof(GlyphID),
+ /* iForcingAntiAlias =*/ false,
+ /* iAntiAliasSwitch =*/true,
+ metrics.data());
+
+ for (int i = 0; i < glyphs->numGlyphs; ++i) {
+ glyphs->advances_x[i] = QFixed::fromReal(metrics[i].deviceAdvance.x);
+ glyphs->advances_y[i] = QFixed::fromReal(metrics[i].deviceAdvance.y);
+
+ if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) {
+ glyphs->advances_x[i] = glyphs->advances_x[i].round();
+ glyphs->advances_y[i] = glyphs->advances_y[i].round();
+ }
+ }
+}
+
+glyph_metrics_t QFontEngineMac::boundingBox(const QGlyphLayout &glyphs)
+{
+ QFixed w;
+ bool round = fontDef.styleStrategy & QFont::ForceIntegerMetrics;
+ for (int i = 0; i < glyphs.numGlyphs; ++i) {
+ w += round ? glyphs.effectiveAdvance(i).round()
+ : glyphs.effectiveAdvance(i);
+ }
+ return glyph_metrics_t(0, -(ascent()), w - lastRightBearing(glyphs, round), ascent()+descent(), w, 0);
+}
+
+glyph_metrics_t QFontEngineMac::boundingBox(glyph_t glyph)
+{
+ GlyphID atsuGlyph = glyph;
+
+ ATSGlyphScreenMetrics metrics;
+
+ ATSUGlyphGetScreenMetrics(style, 1, &atsuGlyph, 0,
+ /* iForcingAntiAlias =*/ false,
+ /* iAntiAliasSwitch =*/true,
+ &metrics);
+
+ // ### check again
+
+ glyph_metrics_t gm;
+ gm.width = int(metrics.width);
+ gm.height = int(metrics.height);
+ gm.x = QFixed::fromReal(metrics.topLeft.x);
+ gm.y = -QFixed::fromReal(metrics.topLeft.y);
+ gm.xoff = QFixed::fromReal(metrics.deviceAdvance.x);
+ gm.yoff = QFixed::fromReal(metrics.deviceAdvance.y);
+
+ if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) {
+ gm.x = gm.x.floor();
+ gm.y = gm.y.floor();
+ gm.xoff = gm.xoff.round();
+ gm.yoff = gm.yoff.round();
+ }
+
+ return gm;
+}
+
+QFixed QFontEngineMac::ascent() const
+{
+ return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
+ ? m_ascent.round()
+ : m_ascent;
+}
+
+QFixed QFontEngineMac::descent() const
+{
+ // subtract a pixel to even out the historical +1 in QFontMetrics::height().
+ // Fix in Qt 5.
+ return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
+ ? m_descent.round() - 1
+ : m_descent;
+}
+
+QFixed QFontEngineMac::leading() const
+{
+ return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
+ ? m_leading.round()
+ : m_leading;
+}
+
+qreal QFontEngineMac::maxCharWidth() const
+{
+ return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
+ ? qRound(m_maxCharWidth)
+ : m_maxCharWidth;
+}
+
+QFixed QFontEngineMac::xHeight() const
+{
+ return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
+ ? m_xHeight.round()
+ : m_xHeight;
+}
+
+QFixed QFontEngineMac::averageCharWidth() const
+{
+ return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
+ ? m_averageCharWidth.round()
+ : m_averageCharWidth;
+}
+
+static void addGlyphsToPathHelper(ATSUStyle style, glyph_t *glyphs, QFixedPoint *positions, int numGlyphs, QPainterPath *path)
+{
+ if (!numGlyphs)
+ return;
+
+ OSStatus e;
+
+ QMacFontPath fontpath(0, 0, path);
+ ATSCubicMoveToUPP moveTo = NewATSCubicMoveToUPP(QMacFontPath::moveTo);
+ ATSCubicLineToUPP lineTo = NewATSCubicLineToUPP(QMacFontPath::lineTo);
+ ATSCubicCurveToUPP cubicTo = NewATSCubicCurveToUPP(QMacFontPath::cubicTo);
+ ATSCubicClosePathUPP closePath = NewATSCubicClosePathUPP(QMacFontPath::closePath);
+
+ // CTFontCreatePathForGlyph
+ for (int i = 0; i < numGlyphs; ++i) {
+ GlyphID glyph = glyphs[i];
+
+ fontpath.setPosition(positions[i].x.toReal(), positions[i].y.toReal());
+ ATSUGlyphGetCubicPaths(style, glyph, moveTo, lineTo,
+ cubicTo, closePath, &fontpath, &e);
+ }
+
+ DisposeATSCubicMoveToUPP(moveTo);
+ DisposeATSCubicLineToUPP(lineTo);
+ DisposeATSCubicCurveToUPP(cubicTo);
+ DisposeATSCubicClosePathUPP(closePath);
+}
+
+void QFontEngineMac::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int numGlyphs, QPainterPath *path,
+ QTextItem::RenderFlags)
+{
+ addGlyphsToPathHelper(style, glyphs, positions, numGlyphs, path);
+}
+
+
+/*!
+ Helper function for alphaMapForGlyph and alphaRGBMapForGlyph. The two are identical, except for
+ the subpixel antialiasing...
+*/
+QImage QFontEngineMac::imageForGlyph(glyph_t glyph, int margin, bool colorful)
+{
+ const glyph_metrics_t br = boundingBox(glyph);
+ QImage im(qRound(br.width)+2, qRound(br.height)+4, QImage::Format_RGB32);
+ im.fill(0xff000000);
+
+ CGColorSpaceRef colorspace = QCoreGraphicsPaintEngine::macGenericColorSpace();
+ uint cgflags = kCGImageAlphaNoneSkipFirst;
+#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version
+ cgflags |= kCGBitmapByteOrder32Host;
+#endif
+ CGContextRef ctx = CGBitmapContextCreate(im.bits(), im.width(), im.height(),
+ 8, im.bytesPerLine(), colorspace,
+ cgflags);
+ CGContextSetFontSize(ctx, fontDef.pixelSize);
+ CGContextSetShouldAntialias(ctx, fontDef.pointSize > qt_antialiasing_threshold && !(fontDef.styleStrategy & QFont::NoAntialias));
+ // turn off sub-pixel hinting - no support for that in OpenGL
+ CGContextSetShouldSmoothFonts(ctx, colorful);
+ CGAffineTransform oldTextMatrix = CGContextGetTextMatrix(ctx);
+ CGAffineTransform cgMatrix = CGAffineTransformMake(1, 0, 0, 1, 0, 0);
+ CGAffineTransformConcat(cgMatrix, oldTextMatrix);
+
+ if (synthesisFlags & QFontEngine::SynthesizedItalic)
+ cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, tanf(14 * acosf(0) / 90), 1, 0, 0));
+
+ cgMatrix = CGAffineTransformConcat(cgMatrix, transform);
+
+ CGContextSetTextMatrix(ctx, cgMatrix);
+ CGContextSetRGBFillColor(ctx, 1, 1, 1, 1);
+ CGContextSetTextDrawingMode(ctx, kCGTextFill);
+ CGContextSetFont(ctx, cgFont);
+
+ qreal pos_x = -br.x.toReal() + 1;
+ qreal pos_y = im.height() + br.y.toReal() - 2;
+ CGContextSetTextPosition(ctx, pos_x, pos_y);
+
+ CGSize advance;
+ advance.width = 0;
+ advance.height = 0;
+ CGGlyph cgGlyph = glyph;
+ CGContextShowGlyphsWithAdvances(ctx, &cgGlyph, &advance, 1);
+
+ if (synthesisFlags & QFontEngine::SynthesizedBold) {
+ CGContextSetTextPosition(ctx, pos_x + 0.5 * lineThickness().toReal(), pos_y);
+ CGContextShowGlyphsWithAdvances(ctx, &cgGlyph, &advance, 1);
+ }
+
+ CGContextRelease(ctx);
+
+ return im;
+}
+
+QImage QFontEngineMac::alphaMapForGlyph(glyph_t glyph)
+{
+ QImage im = imageForGlyph(glyph, 2, false);
+
+ QImage indexed(im.width(), im.height(), QImage::Format_Indexed8);
+ QVector<QRgb> colors(256);
+ for (int i=0; i<256; ++i)
+ colors[i] = qRgba(0, 0, 0, i);
+ indexed.setColorTable(colors);
+
+ for (int y=0; y<im.height(); ++y) {
+ uint *src = (uint*) im.scanLine(y);
+ uchar *dst = indexed.scanLine(y);
+ for (int x=0; x<im.width(); ++x) {
+ *dst = qGray(*src);
+ ++dst;
+ ++src;
+ }
+ }
+
+ return indexed;
+}
+
+QImage QFontEngineMac::alphaRGBMapForGlyph(glyph_t glyph, QFixed, int margin, const QTransform &t)
+{
+ QImage im = imageForGlyph(glyph, margin, true);
+
+ if (t.type() >= QTransform::TxScale) {
+ im = im.transformed(t);
+ }
+
+ qGamma_correct_back_to_linear_cs(&im);
+
+ return im;
+}
+
+
+bool QFontEngineMac::canRender(const QChar *string, int len)
+{
+ Q_ASSERT(false);
+ Q_UNUSED(string);
+ Q_UNUSED(len);
+ return false;
+}
+
+void QFontEngineMac::draw(CGContextRef ctx, qreal x, qreal y, const QTextItemInt &ti, int paintDeviceHeight)
+{
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> glyphs;
+ QTransform matrix;
+ matrix.translate(x, y);
+ getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
+ if (glyphs.size() == 0)
+ return;
+
+ CGContextSetFontSize(ctx, fontDef.pixelSize);
+
+ CGAffineTransform oldTextMatrix = CGContextGetTextMatrix(ctx);
+
+ CGAffineTransform cgMatrix = CGAffineTransformMake(1, 0, 0, -1, 0, -paintDeviceHeight);
+
+ CGAffineTransformConcat(cgMatrix, oldTextMatrix);
+
+ if (synthesisFlags & QFontEngine::SynthesizedItalic)
+ cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, -tanf(14 * acosf(0) / 90), 1, 0, 0));
+
+ cgMatrix = CGAffineTransformConcat(cgMatrix, transform);
+
+ CGContextSetTextMatrix(ctx, cgMatrix);
+
+ CGContextSetTextDrawingMode(ctx, kCGTextFill);
+
+
+ QVarLengthArray<CGSize> advances(glyphs.size());
+ QVarLengthArray<CGGlyph> cgGlyphs(glyphs.size());
+
+ for (int i = 0; i < glyphs.size() - 1; ++i) {
+ advances[i].width = (positions[i + 1].x - positions[i].x).toReal();
+ advances[i].height = (positions[i + 1].y - positions[i].y).toReal();
+ cgGlyphs[i] = glyphs[i];
+ }
+ advances[glyphs.size() - 1].width = 0;
+ advances[glyphs.size() - 1].height = 0;
+ cgGlyphs[glyphs.size() - 1] = glyphs[glyphs.size() - 1];
+
+ CGContextSetFont(ctx, cgFont);
+
+ CGContextSetTextPosition(ctx, positions[0].x.toReal(), positions[0].y.toReal());
+
+ CGContextShowGlyphsWithAdvances(ctx, cgGlyphs.data(), advances.data(), glyphs.size());
+
+ if (synthesisFlags & QFontEngine::SynthesizedBold) {
+ CGContextSetTextPosition(ctx, positions[0].x.toReal() + 0.5 * lineThickness().toReal(),
+ positions[0].y.toReal());
+
+ CGContextShowGlyphsWithAdvances(ctx, cgGlyphs.data(), advances.data(), glyphs.size());
+ }
+
+ CGContextSetTextMatrix(ctx, oldTextMatrix);
+}
+
+QFontEngine::FaceId QFontEngineMac::faceId() const
+{
+ FaceId ret;
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
+ // CTFontGetPlatformFont
+ FSRef ref;
+ if (ATSFontGetFileReference(FMGetATSFontRefFromFont(fontID), &ref) != noErr)
+ return ret;
+ ret.filename = QByteArray(128, 0);
+ ret.index = fontID;
+ FSRefMakePath(&ref, (UInt8 *)ret.filename.data(), ret.filename.size());
+}else
+#endif
+{
+ FSSpec spec;
+ if (ATSFontGetFileSpecification(FMGetATSFontRefFromFont(fontID), &spec) != noErr)
+ return ret;
+
+ FSRef ref;
+ FSpMakeFSRef(&spec, &ref);
+ ret.filename = QByteArray(128, 0);
+ ret.index = fontID;
+ FSRefMakePath(&ref, (UInt8 *)ret.filename.data(), ret.filename.size());
+}
+ return ret;
+}
+
+QByteArray QFontEngineMac::getSfntTable(uint tag) const
+{
+ ATSFontRef atsFont = FMGetATSFontRefFromFont(fontID);
+
+ ByteCount length;
+ OSStatus status = ATSFontGetTable(atsFont, tag, 0, 0, 0, &length);
+ if (status != noErr)
+ return QByteArray();
+ QByteArray table(length, 0);
+ // CTFontCopyTable
+ status = ATSFontGetTable(atsFont, tag, 0, table.length(), table.data(), &length);
+ if (status != noErr)
+ return QByteArray();
+ return table;
+}
+
+QFontEngine::Properties QFontEngineMac::properties() const
+{
+ QFontEngine::Properties props;
+ ATSFontRef atsFont = FMGetATSFontRefFromFont(fontID);
+ quint16 tmp;
+ // CTFontGetUnitsPerEm
+ if (ATSFontGetTable(atsFont, MAKE_TAG('h', 'e', 'a', 'd'), 18, 2, &tmp, 0) == noErr)
+ props.emSquare = qFromBigEndian<quint16>(tmp);
+ struct {
+ qint16 xMin;
+ qint16 yMin;
+ qint16 xMax;
+ qint16 yMax;
+ } bbox;
+ bbox.xMin = bbox.xMax = bbox.yMin = bbox.yMax = 0;
+ // CTFontGetBoundingBox
+ if (ATSFontGetTable(atsFont, MAKE_TAG('h', 'e', 'a', 'd'), 36, 8, &bbox, 0) == noErr) {
+ bbox.xMin = qFromBigEndian<quint16>(bbox.xMin);
+ bbox.yMin = qFromBigEndian<quint16>(bbox.yMin);
+ bbox.xMax = qFromBigEndian<quint16>(bbox.xMax);
+ bbox.yMax = qFromBigEndian<quint16>(bbox.yMax);
+ }
+ struct {
+ qint16 ascender;
+ qint16 descender;
+ qint16 linegap;
+ } metrics;
+ metrics.ascender = metrics.descender = metrics.linegap = 0;
+ // CTFontGetAscent, etc.
+ if (ATSFontGetTable(atsFont, MAKE_TAG('h', 'h', 'e', 'a'), 4, 6, &metrics, 0) == noErr) {
+ metrics.ascender = qFromBigEndian<quint16>(metrics.ascender);
+ metrics.descender = qFromBigEndian<quint16>(metrics.descender);
+ metrics.linegap = qFromBigEndian<quint16>(metrics.linegap);
+ }
+ props.ascent = metrics.ascender;
+ props.descent = -metrics.descender;
+ props.leading = metrics.linegap;
+ props.boundingBox = QRectF(bbox.xMin, -bbox.yMax,
+ bbox.xMax - bbox.xMin,
+ bbox.yMax - bbox.yMin);
+ props.italicAngle = 0;
+ props.capHeight = props.ascent;
+
+ qint16 lw = 0;
+ // fonts lie
+ if (ATSFontGetTable(atsFont, MAKE_TAG('p', 'o', 's', 't'), 10, 2, &lw, 0) == noErr)
+ lw = qFromBigEndian<quint16>(lw);
+ props.lineWidth = lw;
+
+ // CTFontCopyPostScriptName
+ QCFString psName;
+ if (ATSFontGetPostScriptName(FMGetATSFontRefFromFont(fontID), kATSOptionFlagsDefault, &psName) == noErr)
+ props.postscriptName = QString(psName).toUtf8();
+ props.postscriptName = QFontEngine::convertToPostscriptFontFamilyName(props.postscriptName);
+ return props;
+}
+
+void QFontEngineMac::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics)
+{
+ ATSUStyle unscaledStyle;
+ ATSUCreateAndCopyStyle(style, &unscaledStyle);
+
+ int emSquare = properties().emSquare.toInt();
+
+ const int maxAttributeCount = 4;
+ ATSUAttributeTag tags[maxAttributeCount + 1];
+ ByteCount sizes[maxAttributeCount + 1];
+ ATSUAttributeValuePtr values[maxAttributeCount + 1];
+ int attributeCount = 0;
+
+ Fixed size = FixRatio(emSquare, 1);
+ tags[attributeCount] = kATSUSizeTag;
+ sizes[attributeCount] = sizeof(size);
+ values[attributeCount] = &size;
+ ++attributeCount;
+
+ Q_ASSERT(attributeCount < maxAttributeCount + 1);
+ OSStatus err = ATSUSetAttributes(unscaledStyle, attributeCount, tags, sizes, values);
+ Q_ASSERT(err == noErr);
+ Q_UNUSED(err);
+
+ // various CTFont metrics functions: CTFontGetBoundingRectsForGlyphs, CTFontGetAdvancesForGlyphs
+ GlyphID atsuGlyph = glyph;
+ ATSGlyphScreenMetrics atsuMetrics;
+ ATSUGlyphGetScreenMetrics(unscaledStyle, 1, &atsuGlyph, 0,
+ /* iForcingAntiAlias =*/ false,
+ /* iAntiAliasSwitch =*/true,
+ &atsuMetrics);
+
+ metrics->width = int(atsuMetrics.width);
+ metrics->height = int(atsuMetrics.height);
+ metrics->x = QFixed::fromReal(atsuMetrics.topLeft.x);
+ metrics->y = -QFixed::fromReal(atsuMetrics.topLeft.y);
+ metrics->xoff = QFixed::fromReal(atsuMetrics.deviceAdvance.x);
+ metrics->yoff = QFixed::fromReal(atsuMetrics.deviceAdvance.y);
+
+ QFixedPoint p;
+ addGlyphsToPathHelper(unscaledStyle, &glyph, &p, 1, path);
+
+ ATSUDisposeStyle(unscaledStyle);
+}
+#endif // !QT_MAC_USE_COCOA
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/mac/qfontengine_mac_p.h b/src/widgets/platforms/mac/qfontengine_mac_p.h
new file mode 100644
index 0000000000..10561e54d6
--- /dev/null
+++ b/src/widgets/platforms/mac/qfontengine_mac_p.h
@@ -0,0 +1,165 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFONTENGINE_MAC_P_H
+#define QFONTENGINE_MAC_P_H
+
+#include <private/qfontengine_p.h>
+
+#ifndef QT_MAC_USE_COCOA
+class QFontEngineMacMulti;
+class QFontEngineMac : public QFontEngine
+{
+ friend class QFontEngineMacMulti;
+public:
+ QFontEngineMac(ATSUStyle baseStyle, ATSUFontID fontID, const QFontDef &def, QFontEngineMacMulti *multiEngine = 0);
+ virtual ~QFontEngineMac();
+
+ virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *numGlyphs, QTextEngine::ShaperFlags flags) const;
+ virtual void recalcAdvances(QGlyphLayout *, QTextEngine::ShaperFlags) const;
+
+ virtual glyph_metrics_t boundingBox(const QGlyphLayout &glyphs);
+ virtual glyph_metrics_t boundingBox(glyph_t glyph);
+
+ virtual QFixed ascent() const;
+ virtual QFixed descent() const;
+ virtual QFixed leading() const;
+ virtual QFixed xHeight() const;
+ virtual qreal maxCharWidth() const;
+ virtual QFixed averageCharWidth() const;
+
+ virtual void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int numGlyphs,
+ QPainterPath *path, QTextItem::RenderFlags);
+
+ virtual const char *name() const { return "QFontEngineMac"; }
+
+ virtual bool canRender(const QChar *string, int len);
+
+ virtual int synthesized() const { return synthesisFlags; }
+
+ virtual Type type() const { return QFontEngine::Mac; }
+
+ void draw(CGContextRef ctx, qreal x, qreal y, const QTextItemInt &ti, int paintDeviceHeight);
+
+ virtual FaceId faceId() const;
+ virtual QByteArray getSfntTable(uint tag) const;
+ virtual Properties properties() const;
+ virtual void getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics);
+ virtual QImage alphaMapForGlyph(glyph_t);
+ virtual QImage alphaRGBMapForGlyph(glyph_t, QFixed subPixelPosition, int margin, const QTransform &t);
+
+private:
+ QImage imageForGlyph(glyph_t glyph, int margin, bool colorful);
+
+ ATSUFontID fontID;
+ QCFType<CGFontRef> cgFont;
+ ATSUStyle style;
+ int synthesisFlags;
+ mutable QGlyphLayout kashidaGlyph;
+ QFontEngineMacMulti *multiEngine;
+ mutable const unsigned char *cmap;
+ mutable bool symbolCMap;
+ mutable QByteArray cmapTable;
+ CGAffineTransform transform;
+ QFixed m_ascent;
+ QFixed m_descent;
+ QFixed m_leading;
+ qreal m_maxCharWidth;
+ QFixed m_xHeight;
+ QFixed m_averageCharWidth;
+};
+
+class QFontEngineMacMulti : public QFontEngineMulti
+{
+ friend class QFontEngineMac;
+public:
+ // internal
+ struct ShaperItem
+ {
+ inline ShaperItem() : string(0), from(0), length(0),
+ log_clusters(0), charAttributes(0) {}
+
+ const QChar *string;
+ int from;
+ int length;
+ QGlyphLayout glyphs;
+ unsigned short *log_clusters;
+ const HB_CharAttributes *charAttributes;
+ QTextEngine::ShaperFlags flags;
+ };
+
+ QFontEngineMacMulti(const ATSFontFamilyRef &atsFamily, const ATSFontRef &atsFontRef, const QFontDef &fontDef, bool kerning);
+ virtual ~QFontEngineMacMulti();
+
+ virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const;
+ bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags,
+ unsigned short *logClusters, const HB_CharAttributes *charAttributes, QScriptItem *) const;
+
+ virtual void recalcAdvances(QGlyphLayout *, QTextEngine::ShaperFlags) const;
+ virtual void doKerning(QGlyphLayout *, QTextEngine::ShaperFlags) const;
+
+ virtual const char *name() const { return "ATSUI"; }
+
+ virtual bool canRender(const QChar *string, int len);
+
+ inline ATSUFontID macFontID() const { return fontID; }
+
+protected:
+ virtual void loadEngine(int at);
+
+private:
+ inline const QFontEngineMac *engineAt(int i) const
+ { return static_cast<const QFontEngineMac *>(engines.at(i)); }
+
+ bool stringToCMapInternal(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags, ShaperItem *item) const;
+
+ int fontIndexForFontID(ATSUFontID id) const;
+
+ ATSUFontID fontID;
+ uint kerning : 1;
+
+ mutable ATSUTextLayout textLayout;
+ mutable ATSUStyle style;
+ CGAffineTransform transform;
+};
+#endif //!QT_MAC_USE_COCOA
+
+#endif // QFONTENGINE_MAC_P_H
diff --git a/src/widgets/platforms/mac/qkeymapper_mac.cpp b/src/widgets/platforms/mac/qkeymapper_mac.cpp
new file mode 100644
index 0000000000..e96caf1b9d
--- /dev/null
+++ b/src/widgets/platforms/mac/qkeymapper_mac.cpp
@@ -0,0 +1,1023 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qt_mac_p.h>
+#include <qdebug.h>
+#include <qevent.h>
+#include <private/qevent_p.h>
+#include <qtextcodec.h>
+#include <qapplication.h>
+#include <qinputcontext.h>
+#include <private/qkeymapper_p.h>
+#include <private/qapplication_p.h>
+#include <private/qmacinputcontext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QT_USE_NAMESPACE
+
+/*****************************************************************************
+ QKeyMapper debug facilities
+ *****************************************************************************/
+//#define DEBUG_KEY_BINDINGS
+//#define DEBUG_KEY_BINDINGS_MODIFIERS
+//#define DEBUG_KEY_MAPS
+
+/*****************************************************************************
+ Internal variables and functions
+ *****************************************************************************/
+bool qt_mac_eat_unicode_key = false;
+extern bool qt_sendSpontaneousEvent(QObject *obj, QEvent *event); //qapplication_mac.cpp
+
+Q_WIDGETS_EXPORT void qt_mac_secure_keyboard(bool b)
+{
+ static bool secure = false;
+ if (b != secure){
+ b ? EnableSecureEventInput() : DisableSecureEventInput();
+ secure = b;
+ }
+}
+
+/*
+ \internal
+ A Mac KeyboardLayoutItem has 8 possible states:
+ 1. Unmodified
+ 2. Shift
+ 3. Control
+ 4. Control + Shift
+ 5. Alt
+ 6. Alt + Shift
+ 7. Alt + Control
+ 8. Alt + Control + Shift
+ 9. Meta
+ 10. Meta + Shift
+ 11. Meta + Control
+ 12. Meta + Control + Shift
+ 13. Meta + Alt
+ 14. Meta + Alt + Shift
+ 15. Meta + Alt + Control
+ 16. Meta + Alt + Control + Shift
+*/
+struct KeyboardLayoutItem {
+ bool dirty;
+ quint32 qtKey[16]; // Can by any Qt::Key_<foo>, or unicode character
+};
+
+// Possible modifier states.
+// NOTE: The order of these states match the order in QKeyMapperPrivate::updatePossibleKeyCodes()!
+static const Qt::KeyboardModifiers ModsTbl[] = {
+ Qt::NoModifier, // 0
+ Qt::ShiftModifier, // 1
+ Qt::ControlModifier, // 2
+ Qt::ControlModifier | Qt::ShiftModifier, // 3
+ Qt::AltModifier, // 4
+ Qt::AltModifier | Qt::ShiftModifier, // 5
+ Qt::AltModifier | Qt::ControlModifier, // 6
+ Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier, // 7
+ Qt::MetaModifier, // 8
+ Qt::MetaModifier | Qt::ShiftModifier, // 9
+ Qt::MetaModifier | Qt::ControlModifier, // 10
+ Qt::MetaModifier | Qt::ControlModifier | Qt::ShiftModifier,// 11
+ Qt::MetaModifier | Qt::AltModifier, // 12
+ Qt::MetaModifier | Qt::AltModifier | Qt::ShiftModifier, // 13
+ Qt::MetaModifier | Qt::AltModifier | Qt::ControlModifier, // 14
+ Qt::MetaModifier | Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier, // 15
+};
+
+/* key maps */
+struct qt_mac_enum_mapper
+{
+ int mac_code;
+ int qt_code;
+#if defined(DEBUG_KEY_BINDINGS)
+# define QT_MAC_MAP_ENUM(x) x, #x
+ const char *desc;
+#else
+# define QT_MAC_MAP_ENUM(x) x
+#endif
+};
+
+//modifiers
+static qt_mac_enum_mapper qt_mac_modifier_symbols[] = {
+ { shiftKey, QT_MAC_MAP_ENUM(Qt::ShiftModifier) },
+ { rightShiftKey, QT_MAC_MAP_ENUM(Qt::ShiftModifier) },
+ { controlKey, QT_MAC_MAP_ENUM(Qt::MetaModifier) },
+ { rightControlKey, QT_MAC_MAP_ENUM(Qt::MetaModifier) },
+ { cmdKey, QT_MAC_MAP_ENUM(Qt::ControlModifier) },
+ { optionKey, QT_MAC_MAP_ENUM(Qt::AltModifier) },
+ { rightOptionKey, QT_MAC_MAP_ENUM(Qt::AltModifier) },
+ { kEventKeyModifierNumLockMask, QT_MAC_MAP_ENUM(Qt::KeypadModifier) },
+ { 0, QT_MAC_MAP_ENUM(0) }
+};
+Qt::KeyboardModifiers qt_mac_get_modifiers(int keys)
+{
+#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
+ qDebug("Qt: internal: **Mapping modifiers: %d (0x%04x)", keys, keys);
+#endif
+ Qt::KeyboardModifiers ret = Qt::NoModifier;
+ for (int i = 0; qt_mac_modifier_symbols[i].qt_code; i++) {
+ if (keys & qt_mac_modifier_symbols[i].mac_code) {
+#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
+ qDebug("Qt: internal: got modifier: %s", qt_mac_modifier_symbols[i].desc);
+#endif
+ ret |= Qt::KeyboardModifier(qt_mac_modifier_symbols[i].qt_code);
+ }
+ }
+ if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) {
+ Qt::KeyboardModifiers oldModifiers = ret;
+ ret &= ~(Qt::MetaModifier | Qt::ControlModifier);
+ if (oldModifiers & Qt::ControlModifier)
+ ret |= Qt::MetaModifier;
+ if (oldModifiers & Qt::MetaModifier)
+ ret |= Qt::ControlModifier;
+ }
+ return ret;
+}
+static int qt_mac_get_mac_modifiers(Qt::KeyboardModifiers keys)
+{
+#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
+ qDebug("Qt: internal: **Mapping modifiers: %d (0x%04x)", (int)keys, (int)keys);
+#endif
+ int ret = 0;
+ for (int i = 0; qt_mac_modifier_symbols[i].qt_code; i++) {
+ if (keys & qt_mac_modifier_symbols[i].qt_code) {
+#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
+ qDebug("Qt: internal: got modifier: %s", qt_mac_modifier_symbols[i].desc);
+#endif
+ ret |= qt_mac_modifier_symbols[i].mac_code;
+ }
+ }
+
+ if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) {
+ int oldModifiers = ret;
+ ret &= ~(controlKeyBit | cmdKeyBit);
+ if (oldModifiers & controlKeyBit)
+ ret |= cmdKeyBit;
+ if (oldModifiers & cmdKeyBit)
+ ret |= controlKeyBit;
+ }
+ return ret;
+}
+void qt_mac_send_modifiers_changed(quint32 modifiers, QObject *object)
+{
+ static quint32 cachedModifiers = 0;
+ quint32 lastModifiers = cachedModifiers,
+ changedModifiers = lastModifiers ^ modifiers;
+ cachedModifiers = modifiers;
+
+ //check the bits
+ static qt_mac_enum_mapper modifier_key_symbols[] = {
+ { shiftKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Shift) },
+ { rightShiftKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Shift) }, //???
+ { controlKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Meta) },
+ { rightControlKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Meta) }, //???
+ { cmdKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Control) },
+ { optionKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Alt) },
+ { rightOptionKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Alt) }, //???
+ { alphaLockBit, QT_MAC_MAP_ENUM(Qt::Key_CapsLock) },
+ { kEventKeyModifierNumLockBit, QT_MAC_MAP_ENUM(Qt::Key_NumLock) },
+ { 0, QT_MAC_MAP_ENUM(0) } };
+ for (int i = 0; i <= 32; i++) { //just check each bit
+ if (!(changedModifiers & (1 << i)))
+ continue;
+ QEvent::Type etype = QEvent::KeyPress;
+ if (lastModifiers & (1 << i))
+ etype = QEvent::KeyRelease;
+ int key = 0;
+ for (uint x = 0; modifier_key_symbols[x].mac_code; x++) {
+ if (modifier_key_symbols[x].mac_code == i) {
+#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
+ qDebug("got modifier changed: %s", modifier_key_symbols[x].desc);
+#endif
+ key = modifier_key_symbols[x].qt_code;
+ break;
+ }
+ }
+ if (!key) {
+#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
+ qDebug("could not get modifier changed: %d", i);
+#endif
+ continue;
+ }
+#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
+ qDebug("KeyEvent (modif): Sending %s to %s::%s: %d - 0x%08x",
+ etype == QEvent::KeyRelease ? "KeyRelease" : "KeyPress",
+ object ? object->metaObject()->className() : "none",
+ object ? object->objectName().toLatin1().constData() : "",
+ key, (int)modifiers);
+#endif
+ QKeyEvent ke(etype, key, qt_mac_get_modifiers(modifiers ^ (1 << i)), QLatin1String(""));
+ qt_sendSpontaneousEvent(object, &ke);
+ }
+}
+
+//keyboard keys (non-modifiers)
+static qt_mac_enum_mapper qt_mac_keyboard_symbols[] = {
+ { kHomeCharCode, QT_MAC_MAP_ENUM(Qt::Key_Home) },
+ { kEnterCharCode, QT_MAC_MAP_ENUM(Qt::Key_Enter) },
+ { kEndCharCode, QT_MAC_MAP_ENUM(Qt::Key_End) },
+ { kBackspaceCharCode, QT_MAC_MAP_ENUM(Qt::Key_Backspace) },
+ { kTabCharCode, QT_MAC_MAP_ENUM(Qt::Key_Tab) },
+ { kPageUpCharCode, QT_MAC_MAP_ENUM(Qt::Key_PageUp) },
+ { kPageDownCharCode, QT_MAC_MAP_ENUM(Qt::Key_PageDown) },
+ { kReturnCharCode, QT_MAC_MAP_ENUM(Qt::Key_Return) },
+ { kEscapeCharCode, QT_MAC_MAP_ENUM(Qt::Key_Escape) },
+ { kLeftArrowCharCode, QT_MAC_MAP_ENUM(Qt::Key_Left) },
+ { kRightArrowCharCode, QT_MAC_MAP_ENUM(Qt::Key_Right) },
+ { kUpArrowCharCode, QT_MAC_MAP_ENUM(Qt::Key_Up) },
+ { kDownArrowCharCode, QT_MAC_MAP_ENUM(Qt::Key_Down) },
+ { kHelpCharCode, QT_MAC_MAP_ENUM(Qt::Key_Help) },
+ { kDeleteCharCode, QT_MAC_MAP_ENUM(Qt::Key_Delete) },
+//ascii maps, for debug
+ { ':', QT_MAC_MAP_ENUM(Qt::Key_Colon) },
+ { ';', QT_MAC_MAP_ENUM(Qt::Key_Semicolon) },
+ { '<', QT_MAC_MAP_ENUM(Qt::Key_Less) },
+ { '=', QT_MAC_MAP_ENUM(Qt::Key_Equal) },
+ { '>', QT_MAC_MAP_ENUM(Qt::Key_Greater) },
+ { '?', QT_MAC_MAP_ENUM(Qt::Key_Question) },
+ { '@', QT_MAC_MAP_ENUM(Qt::Key_At) },
+ { ' ', QT_MAC_MAP_ENUM(Qt::Key_Space) },
+ { '!', QT_MAC_MAP_ENUM(Qt::Key_Exclam) },
+ { '"', QT_MAC_MAP_ENUM(Qt::Key_QuoteDbl) },
+ { '#', QT_MAC_MAP_ENUM(Qt::Key_NumberSign) },
+ { '$', QT_MAC_MAP_ENUM(Qt::Key_Dollar) },
+ { '%', QT_MAC_MAP_ENUM(Qt::Key_Percent) },
+ { '&', QT_MAC_MAP_ENUM(Qt::Key_Ampersand) },
+ { '\'', QT_MAC_MAP_ENUM(Qt::Key_Apostrophe) },
+ { '(', QT_MAC_MAP_ENUM(Qt::Key_ParenLeft) },
+ { ')', QT_MAC_MAP_ENUM(Qt::Key_ParenRight) },
+ { '*', QT_MAC_MAP_ENUM(Qt::Key_Asterisk) },
+ { '+', QT_MAC_MAP_ENUM(Qt::Key_Plus) },
+ { ',', QT_MAC_MAP_ENUM(Qt::Key_Comma) },
+ { '-', QT_MAC_MAP_ENUM(Qt::Key_Minus) },
+ { '.', QT_MAC_MAP_ENUM(Qt::Key_Period) },
+ { '/', QT_MAC_MAP_ENUM(Qt::Key_Slash) },
+ { '[', QT_MAC_MAP_ENUM(Qt::Key_BracketLeft) },
+ { ']', QT_MAC_MAP_ENUM(Qt::Key_BracketRight) },
+ { '\\', QT_MAC_MAP_ENUM(Qt::Key_Backslash) },
+ { '_', QT_MAC_MAP_ENUM(Qt::Key_Underscore) },
+ { '`', QT_MAC_MAP_ENUM(Qt::Key_QuoteLeft) },
+ { '{', QT_MAC_MAP_ENUM(Qt::Key_BraceLeft) },
+ { '}', QT_MAC_MAP_ENUM(Qt::Key_BraceRight) },
+ { '|', QT_MAC_MAP_ENUM(Qt::Key_Bar) },
+ { '~', QT_MAC_MAP_ENUM(Qt::Key_AsciiTilde) },
+ { '^', QT_MAC_MAP_ENUM(Qt::Key_AsciiCircum) },
+ { 0, QT_MAC_MAP_ENUM(0) }
+};
+
+static qt_mac_enum_mapper qt_mac_keyvkey_symbols[] = { //real scan codes
+ { 122, QT_MAC_MAP_ENUM(Qt::Key_F1) },
+ { 120, QT_MAC_MAP_ENUM(Qt::Key_F2) },
+ { 99, QT_MAC_MAP_ENUM(Qt::Key_F3) },
+ { 118, QT_MAC_MAP_ENUM(Qt::Key_F4) },
+ { 96, QT_MAC_MAP_ENUM(Qt::Key_F5) },
+ { 97, QT_MAC_MAP_ENUM(Qt::Key_F6) },
+ { 98, QT_MAC_MAP_ENUM(Qt::Key_F7) },
+ { 100, QT_MAC_MAP_ENUM(Qt::Key_F8) },
+ { 101, QT_MAC_MAP_ENUM(Qt::Key_F9) },
+ { 109, QT_MAC_MAP_ENUM(Qt::Key_F10) },
+ { 103, QT_MAC_MAP_ENUM(Qt::Key_F11) },
+ { 111, QT_MAC_MAP_ENUM(Qt::Key_F12) },
+ { 105, QT_MAC_MAP_ENUM(Qt::Key_F13) },
+ { 107, QT_MAC_MAP_ENUM(Qt::Key_F14) },
+ { 113, QT_MAC_MAP_ENUM(Qt::Key_F15) },
+ { 106, QT_MAC_MAP_ENUM(Qt::Key_F16) },
+ { 0, QT_MAC_MAP_ENUM(0) }
+};
+
+static qt_mac_enum_mapper qt_mac_private_unicode[] = {
+ { 0xF700, QT_MAC_MAP_ENUM(Qt::Key_Up) }, //NSUpArrowFunctionKey
+ { 0xF701, QT_MAC_MAP_ENUM(Qt::Key_Down) }, //NSDownArrowFunctionKey
+ { 0xF702, QT_MAC_MAP_ENUM(Qt::Key_Left) }, //NSLeftArrowFunctionKey
+ { 0xF703, QT_MAC_MAP_ENUM(Qt::Key_Right) }, //NSRightArrowFunctionKey
+ { 0xF727, QT_MAC_MAP_ENUM(Qt::Key_Insert) }, //NSInsertFunctionKey
+ { 0xF728, QT_MAC_MAP_ENUM(Qt::Key_Delete) }, //NSDeleteFunctionKey
+ { 0xF729, QT_MAC_MAP_ENUM(Qt::Key_Home) }, //NSHomeFunctionKey
+ { 0xF72B, QT_MAC_MAP_ENUM(Qt::Key_End) }, //NSEndFunctionKey
+ { 0xF72C, QT_MAC_MAP_ENUM(Qt::Key_PageUp) }, //NSPageUpFunctionKey
+ { 0xF72D, QT_MAC_MAP_ENUM(Qt::Key_PageDown) }, //NSPageDownFunctionKey
+ { 0xF72F, QT_MAC_MAP_ENUM(Qt::Key_ScrollLock) }, //NSScrollLockFunctionKey
+ { 0xF730, QT_MAC_MAP_ENUM(Qt::Key_Pause) }, //NSPauseFunctionKey
+ { 0xF731, QT_MAC_MAP_ENUM(Qt::Key_SysReq) }, //NSSysReqFunctionKey
+ { 0xF735, QT_MAC_MAP_ENUM(Qt::Key_Menu) }, //NSMenuFunctionKey
+ { 0xF738, QT_MAC_MAP_ENUM(Qt::Key_Print) }, //NSPrintFunctionKey
+ { 0xF73A, QT_MAC_MAP_ENUM(Qt::Key_Clear) }, //NSClearDisplayFunctionKey
+ { 0xF73D, QT_MAC_MAP_ENUM(Qt::Key_Insert) }, //NSInsertCharFunctionKey
+ { 0xF73E, QT_MAC_MAP_ENUM(Qt::Key_Delete) }, //NSDeleteCharFunctionKey
+ { 0xF741, QT_MAC_MAP_ENUM(Qt::Key_Select) }, //NSSelectFunctionKey
+ { 0xF742, QT_MAC_MAP_ENUM(Qt::Key_Execute) }, //NSExecuteFunctionKey
+ { 0xF746, QT_MAC_MAP_ENUM(Qt::Key_Help) }, //NSHelpFunctionKey
+ { 0xF747, QT_MAC_MAP_ENUM(Qt::Key_Mode_switch) }, //NSModeSwitchFunctionKey
+ { 0, QT_MAC_MAP_ENUM(0) }
+};
+
+static int qt_mac_get_key(int modif, const QChar &key, int virtualKey)
+{
+#ifdef DEBUG_KEY_BINDINGS
+ qDebug("**Mapping key: %d (0x%04x) - %d (0x%04x)", key.unicode(), key.unicode(), virtualKey, virtualKey);
+#endif
+
+ if (key == kClearCharCode && virtualKey == 0x47)
+ return Qt::Key_Clear;
+
+ if (key.isDigit()) {
+#ifdef DEBUG_KEY_BINDINGS
+ qDebug("%d: got key: %d", __LINE__, key.digitValue());
+#endif
+ return key.digitValue() + Qt::Key_0;
+ }
+
+ if (key.isLetter()) {
+#ifdef DEBUG_KEY_BINDINGS
+ qDebug("%d: got key: %d", __LINE__, (key.toUpper().unicode() - 'A'));
+#endif
+ return (key.toUpper().unicode() - 'A') + Qt::Key_A;
+ }
+ if (key.isSymbol()) {
+#ifdef DEBUG_KEY_BINDINGS
+ qDebug("%d: got key: %d", __LINE__, (key.unicode()));
+#endif
+ return key.unicode();
+ }
+
+ for (int i = 0; qt_mac_keyboard_symbols[i].qt_code; i++) {
+ if (qt_mac_keyboard_symbols[i].mac_code == key) {
+ /* To work like Qt for X11 we issue Backtab when Shift + Tab are pressed */
+ if (qt_mac_keyboard_symbols[i].qt_code == Qt::Key_Tab && (modif & Qt::ShiftModifier)) {
+#ifdef DEBUG_KEY_BINDINGS
+ qDebug("%d: got key: Qt::Key_Backtab", __LINE__);
+#endif
+ return Qt::Key_Backtab;
+ }
+
+#ifdef DEBUG_KEY_BINDINGS
+ qDebug("%d: got key: %s", __LINE__, qt_mac_keyboard_symbols[i].desc);
+#endif
+ return qt_mac_keyboard_symbols[i].qt_code;
+ }
+ }
+
+ //last ditch try to match the scan code
+ for (int i = 0; qt_mac_keyvkey_symbols[i].qt_code; i++) {
+ if (qt_mac_keyvkey_symbols[i].mac_code == virtualKey) {
+#ifdef DEBUG_KEY_BINDINGS
+ qDebug("%d: got key: %s", __LINE__, qt_mac_keyvkey_symbols[i].desc);
+#endif
+ return qt_mac_keyvkey_symbols[i].qt_code;
+ }
+ }
+
+ // check if they belong to key codes in private unicode range
+ if (key >= 0xf700 && key <= 0xf747) {
+ if (key >= 0xf704 && key <= 0xf726) {
+ return Qt::Key_F1 + (key.unicode() - 0xf704) ;
+ }
+ for (int i = 0; qt_mac_private_unicode[i].qt_code; i++) {
+ if (qt_mac_private_unicode[i].mac_code == key) {
+ return qt_mac_private_unicode[i].qt_code;
+ }
+ }
+
+ }
+
+ //oh well
+#ifdef DEBUG_KEY_BINDINGS
+ qDebug("Unknown case.. %s:%d %d[%d] %d", __FILE__, __LINE__, key.unicode(), key.toLatin1(), virtualKey);
+#endif
+ return Qt::Key_unknown;
+}
+
+static Boolean qt_KeyEventComparatorProc(EventRef inEvent, void *data)
+{
+ UInt32 ekind = GetEventKind(inEvent),
+ eclass = GetEventClass(inEvent);
+ return (eclass == kEventClassKeyboard && (void *)ekind == data);
+}
+
+static bool translateKeyEventInternal(EventHandlerCallRef er, EventRef keyEvent, int *qtKey,
+ QChar *outChar, Qt::KeyboardModifiers *outModifiers, bool *outHandled)
+{
+#if !defined(QT_MAC_USE_COCOA) || defined(Q_OS_MAC64)
+ Q_UNUSED(er);
+ Q_UNUSED(outHandled);
+#endif
+ const UInt32 ekind = GetEventKind(keyEvent);
+ {
+ UInt32 mac_modifiers = 0;
+ GetEventParameter(keyEvent, kEventParamKeyModifiers, typeUInt32, 0,
+ sizeof(mac_modifiers), 0, &mac_modifiers);
+#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
+ qDebug("************ Mapping modifiers and key ***********");
+#endif
+ *outModifiers = qt_mac_get_modifiers(mac_modifiers);
+#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
+ qDebug("------------ Mapping modifiers and key -----------");
+#endif
+ }
+
+ //get keycode
+ UInt32 keyCode = 0;
+ GetEventParameter(keyEvent, kEventParamKeyCode, typeUInt32, 0, sizeof(keyCode), 0, &keyCode);
+
+ //get mac mapping
+ static UInt32 tmp_unused_state = 0L;
+ const UCKeyboardLayout *uchrData = 0;
+#if defined(Q_OS_MAC32)
+ KeyboardLayoutRef keyLayoutRef = 0;
+ KLGetCurrentKeyboardLayout(&keyLayoutRef);
+ OSStatus err;
+ if (keyLayoutRef != 0) {
+ err = KLGetKeyboardLayoutProperty(keyLayoutRef, kKLuchrData,
+ (reinterpret_cast<const void **>(&uchrData)));
+ if (err != noErr) {
+ qWarning("Qt::internal::unable to get keyboardlayout %ld %s:%d",
+ long(err), __FILE__, __LINE__);
+ }
+ }
+#else
+ QCFType<TISInputSourceRef> inputSource = TISCopyCurrentKeyboardInputSource();
+ Q_ASSERT(inputSource != 0);
+ CFDataRef data = static_cast<CFDataRef>(TISGetInputSourceProperty(inputSource,
+ kTISPropertyUnicodeKeyLayoutData));
+ uchrData = data ? reinterpret_cast<const UCKeyboardLayout *>(CFDataGetBytePtr(data)) : 0;
+#endif
+ *qtKey = Qt::Key_unknown;
+ if (uchrData) {
+ // The easy stuff; use the unicode stuff!
+ UniChar string[4];
+ UniCharCount actualLength;
+ UInt32 currentModifiers = GetCurrentEventKeyModifiers();
+ UInt32 currentModifiersWOAltOrControl = currentModifiers & ~(controlKey | optionKey);
+ int keyAction;
+ switch (ekind) {
+ default:
+ case kEventRawKeyDown:
+ keyAction = kUCKeyActionDown;
+ break;
+ case kEventRawKeyUp:
+ keyAction = kUCKeyActionUp;
+ break;
+ case kEventRawKeyRepeat:
+ keyAction = kUCKeyActionAutoKey;
+ break;
+ }
+ OSStatus err = UCKeyTranslate(uchrData, keyCode, keyAction,
+ ((currentModifiersWOAltOrControl >> 8) & 0xff), LMGetKbdType(),
+ kUCKeyTranslateNoDeadKeysMask, &tmp_unused_state, 4, &actualLength,
+ string);
+ if (err == noErr) {
+ *outChar = QChar(string[0]);
+ *qtKey = qt_mac_get_key(*outModifiers, *outChar, keyCode);
+ if (currentModifiersWOAltOrControl != currentModifiers) {
+ // Now get the real char.
+ err = UCKeyTranslate(uchrData, keyCode, keyAction,
+ ((currentModifiers >> 8) & 0xff), LMGetKbdType(),
+ kUCKeyTranslateNoDeadKeysMask, &tmp_unused_state, 4, &actualLength,
+ string);
+ if (err == noErr)
+ *outChar = QChar(string[0]);
+ }
+ } else {
+ qWarning("Qt::internal::UCKeyTranslate is returnining %ld %s:%d",
+ long(err), __FILE__, __LINE__);
+ }
+ }
+#ifdef Q_OS_MAC32
+ else {
+ // The road less travelled; use KeyTranslate
+ const void *keyboard_layout;
+ KeyboardLayoutRef keyLayoutRef = 0;
+ KLGetCurrentKeyboardLayout(&keyLayoutRef);
+ err = KLGetKeyboardLayoutProperty(keyLayoutRef, kKLKCHRData,
+ reinterpret_cast<const void **>(&keyboard_layout));
+
+ int translatedChar = KeyTranslate(keyboard_layout, (GetCurrentEventKeyModifiers() &
+ (kEventKeyModifierNumLockMask|shiftKey|cmdKey|
+ rightShiftKey|alphaLock)) | keyCode,
+ &tmp_unused_state);
+ if (!translatedChar) {
+#ifdef QT_MAC_USE_COCOA
+ if (outHandled) {
+ qt_mac_eat_unicode_key = false;
+ if (er)
+ CallNextEventHandler(er, keyEvent);
+ *outHandled = qt_mac_eat_unicode_key;
+ }
+#endif
+ return false;
+ }
+
+ //map it into qt keys
+ *qtKey = qt_mac_get_key(*outModifiers, QChar(translatedChar), keyCode);
+ if (*outModifiers & (Qt::AltModifier | Qt::ControlModifier)) {
+ if (translatedChar & (1 << 7)) //high ascii
+ translatedChar = 0;
+ } else { //now get the real ascii value
+ UInt32 tmp_mod = 0L;
+ static UInt32 tmp_state = 0L;
+ if (*outModifiers & Qt::ShiftModifier)
+ tmp_mod |= shiftKey;
+ if (*outModifiers & Qt::MetaModifier)
+ tmp_mod |= controlKey;
+ if (*outModifiers & Qt::ControlModifier)
+ tmp_mod |= cmdKey;
+ if (GetCurrentEventKeyModifiers() & alphaLock) //no Qt mapper
+ tmp_mod |= alphaLock;
+ if (*outModifiers & Qt::AltModifier)
+ tmp_mod |= optionKey;
+ if (*outModifiers & Qt::KeypadModifier)
+ tmp_mod |= kEventKeyModifierNumLockMask;
+ translatedChar = KeyTranslate(keyboard_layout, tmp_mod | keyCode, &tmp_state);
+ }
+ {
+ ByteCount unilen = 0;
+ if (GetEventParameter(keyEvent, kEventParamKeyUnicodes, typeUnicodeText, 0, 0, &unilen, 0)
+ == noErr && unilen == 2) {
+ GetEventParameter(keyEvent, kEventParamKeyUnicodes, typeUnicodeText, 0, unilen, 0, outChar);
+ } else if (translatedChar) {
+ static QTextCodec *c = 0;
+ if (!c)
+ c = QTextCodec::codecForName("Apple Roman");
+ char tmpChar = (char)translatedChar; // **sigh**
+ *outChar = c->toUnicode(&tmpChar, 1).at(0);
+ } else {
+ *qtKey = qt_mac_get_key(*outModifiers, QChar(translatedChar), keyCode);
+ }
+ }
+ }
+#endif
+ if (*qtKey == Qt::Key_unknown)
+ *qtKey = qt_mac_get_key(*outModifiers, *outChar, keyCode);
+ return true;
+}
+
+QKeyMapperPrivate::QKeyMapperPrivate()
+{
+ memset(keyLayout, 0, sizeof(keyLayout));
+ keyboard_layout_format.unicode = 0;
+#ifdef Q_OS_MAC32
+ keyboard_mode = NullMode;
+#else
+ currentInputSource = 0;
+#endif
+}
+
+QKeyMapperPrivate::~QKeyMapperPrivate()
+{
+ deleteLayouts();
+}
+
+bool
+QKeyMapperPrivate::updateKeyboard()
+{
+ const UCKeyboardLayout *uchrData = 0;
+#ifdef Q_OS_MAC32
+ KeyboardLayoutRef keyLayoutRef = 0;
+ KLGetCurrentKeyboardLayout(&keyLayoutRef);
+
+ if (keyboard_mode != NullMode && currentKeyboardLayout == keyLayoutRef)
+ return false;
+
+ OSStatus err;
+ if (keyLayoutRef != 0) {
+ err = KLGetKeyboardLayoutProperty(keyLayoutRef, kKLuchrData,
+ const_cast<const void **>(reinterpret_cast<const void **>(&uchrData)));
+ if (err != noErr) {
+ qWarning("Qt::internal::unable to get unicode keyboardlayout %ld %s:%d",
+ long(err), __FILE__, __LINE__);
+ }
+ }
+#else
+ QCFType<TISInputSourceRef> source = TISCopyCurrentKeyboardInputSource();
+ if (keyboard_mode != NullMode && source == currentInputSource) {
+ return false;
+ }
+ Q_ASSERT(source != 0);
+ CFDataRef data = static_cast<CFDataRef>(TISGetInputSourceProperty(source,
+ kTISPropertyUnicodeKeyLayoutData));
+ uchrData = data ? reinterpret_cast<const UCKeyboardLayout *>(CFDataGetBytePtr(data)) : 0;
+#endif
+
+ keyboard_kind = LMGetKbdType();
+ if (uchrData) {
+ keyboard_layout_format.unicode = uchrData;
+ keyboard_mode = UnicodeMode;
+ }
+#ifdef Q_OS_MAC32
+ else {
+ void *happy;
+ err = KLGetKeyboardLayoutProperty(keyLayoutRef, kKLKCHRData,
+ const_cast<const void **>(reinterpret_cast<void **>(&happy)));
+ if (err != noErr) {
+ qFatal("Qt::internal::unable to get non-unicode layout, cannot procede %ld %s:%d",
+ long(err), __FILE__, __LINE__);
+ }
+ keyboard_layout_format.other = happy;
+ keyboard_mode = OtherMode;
+ }
+
+ currentKeyboardLayout = keyLayoutRef;
+#else
+ currentInputSource = source;
+#endif
+ keyboard_dead = 0;
+ CFStringRef iso639Code;
+#ifdef Q_OS_MAC32
+# ifndef kKLLanguageCode
+# define kKLLanguageCode 9
+# endif
+ KLGetKeyboardLayoutProperty(currentKeyboardLayout, kKLLanguageCode,
+ reinterpret_cast<const void **>(&iso639Code));
+#else
+ CFArrayRef array = static_cast<CFArrayRef>(TISGetInputSourceProperty(currentInputSource, kTISPropertyInputSourceLanguages));
+ iso639Code = static_cast<CFStringRef>(CFArrayGetValueAtIndex(array, 0)); // Actually a RFC3066bis, but it's close enough
+#endif
+ if (iso639Code) {
+ keyboardInputLocale = QLocale(QCFString::toQString(iso639Code));
+ keyboardInputDirection = keyboardInputLocale.textDirection();
+ } else {
+ keyboardInputLocale = QLocale::c();
+ keyboardInputDirection = Qt::LeftToRight;
+ }
+ return true;
+}
+
+void
+QKeyMapperPrivate::deleteLayouts()
+{
+ keyboard_mode = NullMode;
+ for (int i = 0; i < 255; ++i) {
+ if (keyLayout[i]) {
+ delete keyLayout[i];
+ keyLayout[i] = 0;
+ }
+ }
+}
+
+void
+QKeyMapperPrivate::clearMappings()
+{
+ deleteLayouts();
+ updateKeyboard();
+}
+
+QList<int>
+QKeyMapperPrivate::possibleKeys(QKeyEvent *e)
+{
+ QList<int> ret;
+
+ KeyboardLayoutItem *kbItem = keyLayout[e->nativeVirtualKey()];
+ if (!kbItem) // Key is not in any keyboard layout (e.g. eisu-key on Japanese keyboard)
+ return ret;
+
+ int baseKey = kbItem->qtKey[0];
+ Qt::KeyboardModifiers keyMods = e->modifiers();
+ ret << int(baseKey + keyMods); // The base key is _always_ valid, of course
+
+ for (int i = 1; i < 8; ++i) {
+ Qt::KeyboardModifiers neededMods = ModsTbl[i];
+ int key = kbItem->qtKey[i];
+ if (key && key != baseKey && ((keyMods & neededMods) == neededMods))
+ ret << int(key + (keyMods & ~neededMods));
+ }
+
+ return ret;
+}
+
+bool QKeyMapperPrivate::translateKeyEvent(QWidget *widget, EventHandlerCallRef er, EventRef event,
+ void *info, bool grab)
+{
+ Q_ASSERT(GetEventClass(event) == kEventClassKeyboard);
+ bool handled_event=true;
+ UInt32 ekind = GetEventKind(event);
+
+ // unfortunately modifiers changed event looks quite different, so I have a separate
+ // code path
+ if (ekind == kEventRawKeyModifiersChanged) {
+ //figure out changed modifiers, wish Apple would just send a delta
+ UInt32 modifiers = 0;
+ GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, 0,
+ sizeof(modifiers), 0, &modifiers);
+ qt_mac_send_modifiers_changed(modifiers, widget);
+ return true;
+ }
+
+ QInputContext *currentContext = qApp->inputContext();
+ if (currentContext && currentContext->isComposing()) {
+ if (ekind == kEventRawKeyDown) {
+ QMacInputContext *context = qobject_cast<QMacInputContext*>(currentContext);
+ if (context)
+ context->setLastKeydownEvent(event);
+ }
+ return false;
+ }
+ // Once we process the key down , we don't need to send the saved event again from
+ // kEventTextInputUnicodeForKeyEvent, so clear it.
+ if (currentContext && ekind == kEventRawKeyDown) {
+ QMacInputContext *context = qobject_cast<QMacInputContext*>(currentContext);
+ if (context)
+ context->setLastKeydownEvent(0);
+ }
+
+ //get modifiers
+ Qt::KeyboardModifiers modifiers;
+ int qtKey;
+ QChar ourChar;
+ if (translateKeyEventInternal(er, event, &qtKey, &ourChar, &modifiers,
+ &handled_event) == false)
+ return handled_event;
+ QString text(ourChar);
+ /* This is actually wrong - but unfortunately it is the best that can be
+ done for now because of the Control/Meta mapping problems */
+ if (modifiers & (Qt::ControlModifier | Qt::MetaModifier)
+ && !qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) {
+ text = QString();
+ }
+
+
+ if (widget) {
+#ifndef QT_MAC_USE_COCOA
+ Q_UNUSED(info);
+ // Try not to call "other" event handlers if we have a popup,
+ // However, if the key has text
+ // then we should pass it along because otherwise then people
+ // can use input method stuff.
+ if (!qApp->activePopupWidget()
+ || (qApp->activePopupWidget() && !text.isEmpty())) {
+ //Find out if someone else wants the event, namely
+ //is it of use to text services? If so we won't bother
+ //with a QKeyEvent.
+ qt_mac_eat_unicode_key = false;
+ if (er)
+ CallNextEventHandler(er, event);
+ extern bool qt_mac_menubar_is_open();
+ if (qt_mac_eat_unicode_key || qt_mac_menubar_is_open()) {
+ return true;
+ }
+ }
+#endif
+ // Try to compress key events.
+ if (!text.isEmpty() && widget->testAttribute(Qt::WA_KeyCompression)) {
+ EventTime lastTime = GetEventTime(event);
+ for (;;) {
+ EventRef releaseEvent = FindSpecificEventInQueue(GetMainEventQueue(),
+ qt_KeyEventComparatorProc,
+ (void*)kEventRawKeyUp);
+ if (!releaseEvent)
+ break;
+ const EventTime releaseTime = GetEventTime(releaseEvent);
+ if (releaseTime < lastTime)
+ break;
+ lastTime = releaseTime;
+
+ EventRef pressEvent = FindSpecificEventInQueue(GetMainEventQueue(),
+ qt_KeyEventComparatorProc,
+ (void*)kEventRawKeyDown);
+ if (!pressEvent)
+ break;
+ const EventTime pressTime = GetEventTime(pressEvent);
+ if (pressTime < lastTime)
+ break;
+ lastTime = pressTime;
+
+ Qt::KeyboardModifiers compressMod;
+ int compressQtKey = 0;
+ QChar compressChar;
+ if (translateKeyEventInternal(er, pressEvent,
+ &compressQtKey, &compressChar, &compressMod, 0)
+ == false) {
+ break;
+ }
+ // Copied from qapplication_x11.cpp (change both).
+
+ bool stopCompression =
+ // 1) misc keys
+ (compressQtKey >= Qt::Key_Escape && compressQtKey <= Qt::Key_SysReq)
+ // 2) cursor movement
+ || (compressQtKey >= Qt::Key_Home && compressQtKey <= Qt::Key_PageDown)
+ // 3) extra keys
+ || (compressQtKey >= Qt::Key_Super_L && compressQtKey <= Qt::Key_Direction_R)
+ // 4) something that a) doesn't translate to text or b) translates
+ // to newline text
+ || (compressQtKey == 0)
+ || (compressChar == QLatin1Char('\n'))
+ || (compressQtKey == Qt::Key_unknown);
+
+ if (compressMod == modifiers && !compressChar.isNull() && !stopCompression) {
+#ifdef DEBUG_KEY_BINDINGS
+ qDebug("compressing away %c", compressChar.toLatin1());
+#endif
+ text += compressChar;
+ // Clean up
+ RemoveEventFromQueue(GetMainEventQueue(), releaseEvent);
+ RemoveEventFromQueue(GetMainEventQueue(), pressEvent);
+ } else {
+#ifdef DEBUG_KEY_BINDINGS
+ qDebug("stoping compression..");
+#endif
+ break;
+ }
+ }
+ }
+
+ // There is no way to get the scan code from carbon. But we cannot use the value 0, since
+ // it indicates that the event originates from somewhere else than the keyboard
+ UInt32 macScanCode = 1;
+ UInt32 macVirtualKey = 0;
+ GetEventParameter(event, kEventParamKeyCode, typeUInt32, 0, sizeof(macVirtualKey), 0, &macVirtualKey);
+ UInt32 macModifiers = 0;
+ GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, 0,
+ sizeof(macModifiers), 0, &macModifiers);
+#ifdef QT_MAC_USE_COCOA
+ // The unicode characters in the range 0xF700-0xF747 are reserved
+ // by Mac OS X for transient use as keyboard function keys. We
+ // wont send 'text' for such key events. This is done to match
+ // behavior on other platforms.
+ unsigned int *unicodeKey = (unsigned int*)info;
+ if (*unicodeKey >= 0xf700 && *unicodeKey <= 0xf747)
+ text = QString();
+ bool isAccepted;
+#endif
+ handled_event = QKeyMapper::sendKeyEvent(widget, grab,
+ (ekind == kEventRawKeyUp) ? QEvent::KeyRelease : QEvent::KeyPress,
+ qtKey, modifiers, text, ekind == kEventRawKeyRepeat, 0,
+ macScanCode, macVirtualKey, macModifiers
+#ifdef QT_MAC_USE_COCOA
+ ,&isAccepted
+#endif
+ );
+#ifdef QT_MAC_USE_COCOA
+ *unicodeKey = (unsigned int)isAccepted;
+#endif
+ }
+ return handled_event;
+}
+
+void
+QKeyMapperPrivate::updateKeyMap(EventHandlerCallRef, EventRef event, void *
+#if defined(QT_MAC_USE_COCOA)
+ unicodeKey // unicode character from NSEvent (modifiers applied)
+#endif
+ )
+{
+ UInt32 macVirtualKey = 0;
+ GetEventParameter(event, kEventParamKeyCode, typeUInt32, 0, sizeof(macVirtualKey), 0, &macVirtualKey);
+ if (updateKeyboard())
+ QKeyMapper::changeKeyboard();
+ else if (keyLayout[macVirtualKey])
+ return;
+
+ UniCharCount buffer_size = 10;
+ UniChar buffer[buffer_size];
+ keyLayout[macVirtualKey] = new KeyboardLayoutItem;
+ for (int i = 0; i < 16; ++i) {
+ UniCharCount out_buffer_size = 0;
+ keyLayout[macVirtualKey]->qtKey[i] = 0;
+#ifdef Q_WS_MAC32
+ if (keyboard_mode == UnicodeMode) {
+#endif
+ const UInt32 keyModifier = ((qt_mac_get_mac_modifiers(ModsTbl[i]) >> 8) & 0xFF);
+ OSStatus err = UCKeyTranslate(keyboard_layout_format.unicode, macVirtualKey, kUCKeyActionDown, keyModifier,
+ keyboard_kind, 0, &keyboard_dead, buffer_size, &out_buffer_size, buffer);
+ if (err == noErr && out_buffer_size) {
+ const QChar unicode(buffer[0]);
+ int qtkey = qt_mac_get_key(keyModifier, unicode, macVirtualKey);
+ if (qtkey == Qt::Key_unknown)
+ qtkey = unicode.unicode();
+ keyLayout[macVirtualKey]->qtKey[i] = qtkey;
+ }
+#ifndef Q_WS_MAC32
+ else {
+ const QChar unicode(*((UniChar *)unicodeKey));
+ int qtkey = qt_mac_get_key(keyModifier, unicode, macVirtualKey);
+ if (qtkey == Qt::Key_unknown)
+ qtkey = unicode.unicode();
+ keyLayout[macVirtualKey]->qtKey[i] = qtkey;
+ }
+#endif
+#ifdef Q_WS_MAC32
+ } else {
+ const UInt32 keyModifier = (qt_mac_get_mac_modifiers(ModsTbl[i]));
+
+ uchar translatedChar = KeyTranslate(keyboard_layout_format.other, keyModifier | macVirtualKey, &keyboard_dead);
+ if (translatedChar) {
+ static QTextCodec *c = 0;
+ if (!c)
+ c = QTextCodec::codecForName("Apple Roman");
+ const QChar unicode(c->toUnicode((const char *)&translatedChar, 1).at(0));
+ int qtkey = qt_mac_get_key(keyModifier, unicode, macVirtualKey);
+ if (qtkey == Qt::Key_unknown)
+ qtkey = unicode.unicode();
+ keyLayout[macVirtualKey]->qtKey[i] = qtkey;
+ }
+ }
+#endif
+ }
+#ifdef DEBUG_KEY_MAPS
+ qDebug("updateKeyMap for virtual key = 0x%02x!", (uint)macVirtualKey);
+ for (int i = 0; i < 16; ++i) {
+ qDebug(" [%d] (%d,0x%02x,'%c')", i,
+ keyLayout[macVirtualKey]->qtKey[i],
+ keyLayout[macVirtualKey]->qtKey[i],
+ keyLayout[macVirtualKey]->qtKey[i]);
+ }
+#endif
+}
+
+bool
+QKeyMapper::sendKeyEvent(QWidget *widget, bool grab,
+ QEvent::Type type, int code, Qt::KeyboardModifiers modifiers,
+ const QString &text, bool autorepeat, int count,
+ quint32 nativeScanCode, quint32 nativeVirtualKey,
+ quint32 nativeModifiers, bool *isAccepted)
+{
+ Q_UNUSED(count);
+ if (widget && widget->isEnabled()) {
+ bool key_event = true;
+#if defined(QT3_SUPPORT) && !defined(QT_NO_SHORTCUT)
+ if (type == QEvent::KeyPress && !grab
+ && QApplicationPrivate::instance()->use_compat()) {
+ QKeyEventEx accel_ev(type, code, modifiers,
+ text, autorepeat, qMax(1, int(text.length())),
+ nativeScanCode, nativeVirtualKey, nativeModifiers);
+ if (QApplicationPrivate::instance()->qt_tryAccelEvent(widget, &accel_ev)) {
+#if defined(DEBUG_KEY_BINDINGS) || defined(DEBUG_KEY_BINDINGS_MODIFIERS)
+ qDebug("KeyEvent: %s::%s consumed Accel: %s",
+ widget ? widget->metaObject()->className() : "none",
+ widget ? widget->objectName().toLatin1().constData() : "",
+ text.toLatin1().constData());
+#endif
+ key_event = false;
+ } else {
+ if (accel_ev.isAccepted()) {
+#if defined(DEBUG_KEY_BINDINGS) || defined(DEBUG_KEY_BINDINGS_MODIFIERS)
+ qDebug("KeyEvent: %s::%s overrode Accel: %s",
+ widget ? widget->metaObject()->className() : "none",
+ widget ? widget->objectName().toLatin1().constData() : "",
+ text.toLatin1().constData());
+#endif
+ }
+ }
+ }
+#else
+Q_UNUSED(grab);
+#endif // QT3_SUPPORT && !QT_NO_SHORTCUT
+ if (key_event) {
+#if defined(DEBUG_KEY_BINDINGS) || defined(DEBUG_KEY_BINDINGS_MODIFIERS)
+ qDebug("KeyEvent: Sending %s to %s::%s: %s 0x%08x%s",
+ type == QEvent::KeyRelease ? "KeyRelease" : "KeyPress",
+ widget ? widget->metaObject()->className() : "none",
+ widget ? widget->objectName().toLatin1().constData() : "",
+ text.toLatin1().constData(), int(modifiers),
+ autorepeat ? " Repeat" : "");
+#endif
+ QKeyEventEx ke(type, code, modifiers, text, autorepeat, qMax(1, text.length()),
+ nativeScanCode, nativeVirtualKey, nativeModifiers);
+ bool retMe = qt_sendSpontaneousEvent(widget,&ke);
+ if (isAccepted)
+ *isAccepted = ke.isAccepted();
+ return retMe;
+ }
+ }
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/mac/qmacdefines_mac.h b/src/widgets/platforms/mac/qmacdefines_mac.h
new file mode 100644
index 0000000000..d0accf872f
--- /dev/null
+++ b/src/widgets/platforms/mac/qmacdefines_mac.h
@@ -0,0 +1,158 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** Copyright (c) 2007-2008, Apple, Inc.
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**
+** * Redistributions of source code must retain the above copyright notice,
+** this list of conditions and the following disclaimer.
+**
+** * Redistributions in binary form must reproduce the above copyright notice,
+** this list of conditions and the following disclaimer in the documentation
+** and/or other materials provided with the distribution.
+**
+** * Neither the name of Apple, Inc. nor the names of its contributors
+** may be used to endorse or promote products derived from this software
+** without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**
+****************************************************************************/
+
+/*
+ * qmacdefines_mac_p.h
+ * All the defines you'll ever need for Qt/Mac :-)
+ */
+
+/* This is just many defines. Therefore it doesn't need things like:
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+Yes, it is an informative comment ;-)
+*/
+
+#include <QtCore/qglobal.h>
+
+#ifdef qDebug
+# define old_qDebug qDebug
+# undef qDebug
+#endif
+
+#ifdef __LP64__
+typedef signed int OSStatus;
+#else
+typedef signed long OSStatus;
+#endif
+
+#ifdef __OBJC__
+# ifdef slots
+# define old_slots slots
+# undef slots
+# endif
+#include <Cocoa/Cocoa.h>
+# ifdef old_slots
+# undef slots
+# define slots
+# undef old_slots
+# endif
+#endif
+ typedef struct OpaqueEventHandlerCallRef * EventHandlerCallRef;
+ typedef struct OpaqueEventRef * EventRef;
+ typedef struct OpaqueMenuRef * MenuRef;
+ typedef struct OpaquePasteboardRef* PasteboardRef;
+ typedef struct OpaqueRgnHandle * RgnHandle;
+ typedef const struct __HIShape *HIShapeRef;
+ typedef struct __HIShape *HIMutableShapeRef;
+ typedef struct CGRect CGRect;
+ typedef struct CGImage *CGImageRef;
+ typedef struct CGContext *CGContextRef;
+ typedef struct GDevice * GDPtr;
+ typedef GDPtr * GDHandle;
+ typedef struct OpaqueIconRef * IconRef;
+# ifdef __OBJC__
+ typedef NSWindow* OSWindowRef;
+ typedef NSView *OSViewRef;
+ typedef NSMenu *OSMenuRef;
+ typedef NSEvent *OSEventRef;
+# else
+ typedef void *OSWindowRef;
+ typedef void *OSViewRef;
+ typedef void *OSMenuRef;
+ typedef void *OSEventRef;
+# endif
+
+typedef PasteboardRef OSPasteboardRef;
+typedef struct AEDesc AEDescList;
+typedef AEDescList AERecord;
+typedef AERecord AppleEvent;
+
+#ifdef check
+#undef check
+#endif
+
+#ifdef old_qDebug
+# undef qDebug
+# define qDebug QT_NO_QDEBUG_MACRO
+# undef old_qDebug
+#endif
diff --git a/src/widgets/platforms/mac/qmacgesturerecognizer_mac.mm b/src/widgets/platforms/mac/qmacgesturerecognizer_mac.mm
new file mode 100644
index 0000000000..321f4fd38e
--- /dev/null
+++ b/src/widgets/platforms/mac/qmacgesturerecognizer_mac.mm
@@ -0,0 +1,272 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmacgesturerecognizer_mac_p.h"
+#include "qgesture.h"
+#include "qgesture_p.h"
+#include "qevent.h"
+#include "qevent_p.h"
+#include "qwidget.h"
+#include "qdebug.h"
+
+#ifndef QT_NO_GESTURES
+
+QT_BEGIN_NAMESPACE
+
+QMacSwipeGestureRecognizer::QMacSwipeGestureRecognizer()
+{
+}
+
+QGesture *QMacSwipeGestureRecognizer::create(QObject * /*target*/)
+{
+ return new QSwipeGesture;
+}
+
+QGestureRecognizer::Result
+QMacSwipeGestureRecognizer::recognize(QGesture *gesture, QObject *obj, QEvent *event)
+{
+ if (event->type() == QEvent::NativeGesture && obj->isWidgetType()) {
+ QNativeGestureEvent *ev = static_cast<QNativeGestureEvent*>(event);
+ switch (ev->gestureType) {
+ case QNativeGestureEvent::Swipe: {
+ QSwipeGesture *g = static_cast<QSwipeGesture *>(gesture);
+ g->setSwipeAngle(ev->angle);
+ g->setHotSpot(ev->position);
+ return QGestureRecognizer::FinishGesture | QGestureRecognizer::ConsumeEventHint;
+ break; }
+ default:
+ break;
+ }
+ }
+
+ return QGestureRecognizer::Ignore;
+}
+
+void QMacSwipeGestureRecognizer::reset(QGesture *gesture)
+{
+ QSwipeGesture *g = static_cast<QSwipeGesture *>(gesture);
+ g->setSwipeAngle(0);
+ QGestureRecognizer::reset(gesture);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+QMacPinchGestureRecognizer::QMacPinchGestureRecognizer()
+{
+}
+
+QGesture *QMacPinchGestureRecognizer::create(QObject * /*target*/)
+{
+ return new QPinchGesture;
+}
+
+QGestureRecognizer::Result
+QMacPinchGestureRecognizer::recognize(QGesture *gesture, QObject *obj, QEvent *event)
+{
+ if (event->type() == QEvent::NativeGesture && obj->isWidgetType()) {
+ QPinchGesture *g = static_cast<QPinchGesture *>(gesture);
+ QNativeGestureEvent *ev = static_cast<QNativeGestureEvent*>(event);
+ switch(ev->gestureType) {
+ case QNativeGestureEvent::GestureBegin:
+ reset(gesture);
+ g->setStartCenterPoint(static_cast<QWidget*>(obj)->mapFromGlobal(ev->position));
+ g->setCenterPoint(g->startCenterPoint());
+ g->setChangeFlags(QPinchGesture::CenterPointChanged);
+ g->setTotalChangeFlags(g->totalChangeFlags() | g->changeFlags());
+ g->setHotSpot(ev->position);
+ return QGestureRecognizer::MayBeGesture | QGestureRecognizer::ConsumeEventHint;
+ case QNativeGestureEvent::Rotate: {
+ g->setLastScaleFactor(g->scaleFactor());
+ g->setLastRotationAngle(g->rotationAngle());
+ g->setRotationAngle(g->rotationAngle() + ev->percentage);
+ g->setChangeFlags(QPinchGesture::RotationAngleChanged);
+ g->setTotalChangeFlags(g->totalChangeFlags() | g->changeFlags());
+ g->setHotSpot(ev->position);
+ return QGestureRecognizer::TriggerGesture | QGestureRecognizer::ConsumeEventHint;
+ }
+ case QNativeGestureEvent::Zoom:
+ g->setLastScaleFactor(g->scaleFactor());
+ g->setLastRotationAngle(g->rotationAngle());
+ g->setScaleFactor(g->scaleFactor() * (1 + ev->percentage));
+ g->setChangeFlags(QPinchGesture::ScaleFactorChanged);
+ g->setTotalChangeFlags(g->totalChangeFlags() | g->changeFlags());
+ g->setHotSpot(ev->position);
+ return QGestureRecognizer::TriggerGesture | QGestureRecognizer::ConsumeEventHint;
+ case QNativeGestureEvent::GestureEnd:
+ return QGestureRecognizer::FinishGesture | QGestureRecognizer::ConsumeEventHint;
+ default:
+ break;
+ }
+ }
+
+ return QGestureRecognizer::Ignore;
+}
+
+void QMacPinchGestureRecognizer::reset(QGesture *gesture)
+{
+ QPinchGesture *g = static_cast<QPinchGesture *>(gesture);
+ g->setChangeFlags(0);
+ g->setTotalChangeFlags(0);
+ g->setScaleFactor(1.0f);
+ g->setTotalScaleFactor(1.0f);
+ g->setLastScaleFactor(1.0f);
+ g->setRotationAngle(0.0f);
+ g->setTotalRotationAngle(0.0f);
+ g->setLastRotationAngle(0.0f);
+ g->setCenterPoint(QPointF());
+ g->setStartCenterPoint(QPointF());
+ g->setLastCenterPoint(QPointF());
+ QGestureRecognizer::reset(gesture);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+#if defined(QT_MAC_USE_COCOA)
+
+QMacPanGestureRecognizer::QMacPanGestureRecognizer() : _panCanceled(true)
+{
+}
+
+QGesture *QMacPanGestureRecognizer::create(QObject *target)
+{
+ if (!target)
+ return new QPanGesture;
+
+ if (QWidget *w = qobject_cast<QWidget *>(target)) {
+ w->setAttribute(Qt::WA_AcceptTouchEvents);
+ w->setAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents);
+ return new QPanGesture;
+ }
+ return 0;
+}
+
+QGestureRecognizer::Result
+QMacPanGestureRecognizer::recognize(QGesture *gesture, QObject *target, QEvent *event)
+{
+ const int panBeginDelay = 300;
+ const int panBeginRadius = 3;
+
+ QPanGesture *g = static_cast<QPanGesture *>(gesture);
+
+ switch (event->type()) {
+ case QEvent::TouchBegin: {
+ const QTouchEvent *ev = static_cast<const QTouchEvent*>(event);
+ if (ev->touchPoints().size() == 1) {
+ reset(gesture);
+ _startPos = QCursor::pos();
+ _panTimer.start(panBeginDelay, target);
+ _panCanceled = false;
+ return QGestureRecognizer::MayBeGesture;
+ }
+ break;}
+ case QEvent::TouchEnd: {
+ if (_panCanceled)
+ break;
+
+ const QTouchEvent *ev = static_cast<const QTouchEvent*>(event);
+ if (ev->touchPoints().size() == 1)
+ return QGestureRecognizer::FinishGesture;
+ break;}
+ case QEvent::TouchUpdate: {
+ if (_panCanceled)
+ break;
+
+ const QTouchEvent *ev = static_cast<const QTouchEvent*>(event);
+ if (ev->touchPoints().size() == 1) {
+ if (_panTimer.isActive()) {
+ // INVARIANT: Still in maybeGesture. Check if the user
+ // moved his finger so much that it makes sense to cancel the pan:
+ const QPointF p = QCursor::pos();
+ if ((p - _startPos).manhattanLength() > panBeginRadius) {
+ _panCanceled = true;
+ _panTimer.stop();
+ return QGestureRecognizer::CancelGesture;
+ }
+ } else {
+ const QPointF p = QCursor::pos();
+ const QPointF posOffset = p - _startPos;
+ g->setLastOffset(g->offset());
+ g->setOffset(QPointF(posOffset.x(), posOffset.y()));
+ g->setHotSpot(_startPos);
+ return QGestureRecognizer::TriggerGesture;
+ }
+ } else if (_panTimer.isActive()) {
+ // I only want to cancel the pan if the user is pressing
+ // more than one finger, and the pan hasn't started yet:
+ _panCanceled = true;
+ _panTimer.stop();
+ return QGestureRecognizer::CancelGesture;
+ }
+ break;}
+ case QEvent::Timer: {
+ QTimerEvent *ev = static_cast<QTimerEvent *>(event);
+ if (ev->timerId() == _panTimer.timerId()) {
+ _panTimer.stop();
+ if (_panCanceled)
+ break;
+ // Begin new pan session!
+ _startPos = QCursor::pos();
+ g->setHotSpot(_startPos);
+ return QGestureRecognizer::TriggerGesture | QGestureRecognizer::ConsumeEventHint;
+ }
+ break; }
+ default:
+ break;
+ }
+
+ return QGestureRecognizer::Ignore;
+}
+
+void QMacPanGestureRecognizer::reset(QGesture *gesture)
+{
+ QPanGesture *g = static_cast<QPanGesture *>(gesture);
+ _startPos = QPointF();
+ _panCanceled = true;
+ g->setOffset(QPointF(0, 0));
+ g->setLastOffset(QPointF(0, 0));
+ g->setAcceleration(qreal(1));
+ QGestureRecognizer::reset(gesture);
+}
+#endif // QT_MAC_USE_COCOA
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_GESTURES
diff --git a/src/widgets/platforms/mac/qmacgesturerecognizer_mac_p.h b/src/widgets/platforms/mac/qmacgesturerecognizer_mac_p.h
new file mode 100644
index 0000000000..f6f207336d
--- /dev/null
+++ b/src/widgets/platforms/mac/qmacgesturerecognizer_mac_p.h
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMACSWIPEGESTURERECOGNIZER_MAC_P_H
+#define QMACSWIPEGESTURERECOGNIZER_MAC_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qtimer.h"
+#include "qpoint.h"
+#include "qgesturerecognizer.h"
+
+#ifndef QT_NO_GESTURES
+
+QT_BEGIN_NAMESPACE
+
+class QMacSwipeGestureRecognizer : public QGestureRecognizer
+{
+public:
+ QMacSwipeGestureRecognizer();
+
+ QGesture *create(QObject *target);
+ QGestureRecognizer::Result recognize(QGesture *gesture, QObject *watched, QEvent *event);
+ void reset(QGesture *gesture);
+};
+
+class QMacPinchGestureRecognizer : public QGestureRecognizer
+{
+public:
+ QMacPinchGestureRecognizer();
+
+ QGesture *create(QObject *target);
+ QGestureRecognizer::Result recognize(QGesture *gesture, QObject *watched, QEvent *event);
+ void reset(QGesture *gesture);
+};
+
+#if defined(QT_MAC_USE_COCOA)
+
+class QMacPanGestureRecognizer : public QObject, public QGestureRecognizer
+{
+public:
+ QMacPanGestureRecognizer();
+
+ QGesture *create(QObject *target);
+ QGestureRecognizer::Result recognize(QGesture *gesture, QObject *watched, QEvent *event);
+ void reset(QGesture *gesture);
+private:
+ QPointF _startPos;
+ QBasicTimer _panTimer;
+ bool _panCanceled;
+};
+
+#endif
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_GESTURES
+
+#endif // QMACSWIPEGESTURERECOGNIZER_MAC_P_H
diff --git a/src/widgets/platforms/mac/qmacinputcontext_mac.cpp b/src/widgets/platforms/mac/qmacinputcontext_mac.cpp
new file mode 100644
index 0000000000..a98cee714b
--- /dev/null
+++ b/src/widgets/platforms/mac/qmacinputcontext_mac.cpp
@@ -0,0 +1,378 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qvarlengtharray.h>
+#include <qwidget.h>
+#include <private/qmacinputcontext_p.h>
+#include "qtextformat.h"
+#include <qdebug.h>
+#include <private/qapplication_p.h>
+#include <private/qkeymapper_p.h>
+
+QT_BEGIN_NAMESPACE
+
+extern bool qt_sendSpontaneousEvent(QObject*, QEvent*);
+
+#if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5)
+# define typeRefCon typeSInt32
+# define typeByteCount typeSInt32
+#endif
+
+QMacInputContext::QMacInputContext(QObject *parent)
+ : QInputContext(parent), composing(false), recursionGuard(false), textDocument(0),
+ keydownEvent(0)
+{
+// createTextDocument();
+}
+
+QMacInputContext::~QMacInputContext()
+{
+#ifndef QT_MAC_USE_COCOA
+ if(textDocument)
+ DeleteTSMDocument(textDocument);
+#endif
+}
+
+void
+QMacInputContext::createTextDocument()
+{
+#ifndef QT_MAC_USE_COCOA
+ if(!textDocument) {
+ InterfaceTypeList itl = { kUnicodeDocument };
+ NewTSMDocument(1, itl, &textDocument, SRefCon(this));
+ }
+#endif
+}
+
+
+QString QMacInputContext::language()
+{
+ return QString();
+}
+
+
+void QMacInputContext::mouseHandler(int pos, QMouseEvent *e)
+{
+#ifndef QT_MAC_USE_COCOA
+ if(e->type() != QEvent::MouseButtonPress)
+ return;
+
+ if (!composing)
+ return;
+ if (pos < 0 || pos > currentText.length())
+ reset();
+ // ##### handle mouse position
+#else
+ Q_UNUSED(pos);
+ Q_UNUSED(e);
+#endif
+}
+
+#if !defined QT_MAC_USE_COCOA
+
+static QTextFormat qt_mac_compose_format()
+{
+ QTextCharFormat ret;
+ ret.setFontUnderline(true);
+ return ret;
+}
+
+void QMacInputContext::reset()
+{
+ if (recursionGuard)
+ return;
+ if (!currentText.isEmpty()){
+ QInputMethodEvent e;
+ e.setCommitString(currentText);
+ qt_sendSpontaneousEvent(focusWidget(), &e);
+ currentText = QString();
+ }
+ recursionGuard = true;
+ createTextDocument();
+ composing = false;
+ ActivateTSMDocument(textDocument);
+ FixTSMDocument(textDocument);
+ recursionGuard = false;
+}
+
+bool QMacInputContext::isComposing() const
+{
+ return composing;
+}
+#endif
+
+void QMacInputContext::setFocusWidget(QWidget *w)
+{
+ createTextDocument();
+#ifndef QT_MAC_USE_COCOA
+ if(w)
+ ActivateTSMDocument(textDocument);
+ else
+ DeactivateTSMDocument(textDocument);
+#endif
+ QInputContext::setFocusWidget(w);
+}
+
+
+#ifndef QT_MAC_USE_COCOA
+static EventTypeSpec input_events[] = {
+ { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent },
+ { kEventClassTextInput, kEventTextInputOffsetToPos },
+ { kEventClassTextInput, kEventTextInputUpdateActiveInputArea }
+};
+static EventHandlerUPP input_proc_handlerUPP = 0;
+static EventHandlerRef input_proc_handler = 0;
+#endif
+
+void
+QMacInputContext::initialize()
+{
+#ifndef QT_MAC_USE_COCOA
+ if(!input_proc_handler) {
+ input_proc_handlerUPP = NewEventHandlerUPP(QMacInputContext::globalEventProcessor);
+ InstallEventHandler(GetApplicationEventTarget(), input_proc_handlerUPP,
+ GetEventTypeCount(input_events), input_events,
+ 0, &input_proc_handler);
+ }
+#endif
+}
+
+void
+QMacInputContext::cleanup()
+{
+#ifndef QT_MAC_USE_COCOA
+ if(input_proc_handler) {
+ RemoveEventHandler(input_proc_handler);
+ input_proc_handler = 0;
+ }
+ if(input_proc_handlerUPP) {
+ DisposeEventHandlerUPP(input_proc_handlerUPP);
+ input_proc_handlerUPP = 0;
+ }
+#endif
+}
+
+void QMacInputContext::setLastKeydownEvent(EventRef event)
+{
+ EventRef tmpEvent = keydownEvent;
+ keydownEvent = event;
+ if (keydownEvent)
+ RetainEvent(keydownEvent);
+ if (tmpEvent)
+ ReleaseEvent(tmpEvent);
+}
+
+OSStatus
+QMacInputContext::globalEventProcessor(EventHandlerCallRef, EventRef event, void *)
+{
+#ifndef QT_MAC_USE_COCOA
+ QScopedLoopLevelCounter loopLevelCounter(QApplicationPrivate::instance()->threadData);
+
+ SRefCon refcon = 0;
+ GetEventParameter(event, kEventParamTextInputSendRefCon, typeRefCon, 0,
+ sizeof(refcon), 0, &refcon);
+ QMacInputContext *context = reinterpret_cast<QMacInputContext*>(refcon);
+
+ bool handled_event=true;
+ UInt32 ekind = GetEventKind(event), eclass = GetEventClass(event);
+ switch(eclass) {
+ case kEventClassTextInput: {
+ handled_event = false;
+ QWidget *widget = QApplicationPrivate::focus_widget;
+ bool canCompose = widget && (!context || widget->inputContext() == context)
+ && !(widget->inputMethodHints() & Qt::ImhDigitsOnly
+ || widget->inputMethodHints() & Qt::ImhFormattedNumbersOnly
+ || widget->inputMethodHints() & Qt::ImhHiddenText);
+ if(!canCompose) {
+ handled_event = false;
+ } else if(ekind == kEventTextInputOffsetToPos) {
+ if(!widget->testAttribute(Qt::WA_InputMethodEnabled)) {
+ handled_event = false;
+ break;
+ }
+
+ QRect mr(widget->inputMethodQuery(Qt::ImMicroFocus).toRect());
+ QPoint mp(widget->mapToGlobal(QPoint(mr.topLeft())));
+ Point pt;
+ pt.h = mp.x();
+ pt.v = mp.y() + mr.height();
+ SetEventParameter(event, kEventParamTextInputReplyPoint, typeQDPoint,
+ sizeof(pt), &pt);
+ handled_event = true;
+ } else if(ekind == kEventTextInputUpdateActiveInputArea) {
+ if(!widget->testAttribute(Qt::WA_InputMethodEnabled)) {
+ handled_event = false;
+ break;
+ }
+
+ if (context->recursionGuard)
+ break;
+
+ ByteCount unilen = 0;
+ GetEventParameter(event, kEventParamTextInputSendText, typeUnicodeText,
+ 0, 0, &unilen, 0);
+ UniChar *unicode = (UniChar*)NewPtr(unilen);
+ GetEventParameter(event, kEventParamTextInputSendText, typeUnicodeText,
+ 0, unilen, 0, unicode);
+ QString text((QChar*)unicode, unilen / sizeof(UniChar));
+ DisposePtr((char*)unicode);
+
+ ByteCount fixed_length = 0;
+ GetEventParameter(event, kEventParamTextInputSendFixLen, typeByteCount, 0,
+ sizeof(fixed_length), 0, &fixed_length);
+ if(fixed_length == ULONG_MAX || fixed_length == unilen) {
+ QInputMethodEvent e;
+ e.setCommitString(text);
+ context->currentText = QString();
+ qt_sendSpontaneousEvent(context->focusWidget(), &e);
+ handled_event = true;
+ context->reset();
+ } else {
+ ByteCount rngSize = 0;
+ OSStatus err = GetEventParameter(event, kEventParamTextInputSendHiliteRng, typeTextRangeArray, 0,
+ 0, &rngSize, 0);
+ QVarLengthArray<TextRangeArray> highlight(rngSize);
+ if (noErr == err) {
+ err = GetEventParameter(event, kEventParamTextInputSendHiliteRng, typeTextRangeArray, 0,
+ rngSize, &rngSize, highlight.data());
+ }
+ context->composing = true;
+ if(fixed_length > 0) {
+ const int qFixedLength = fixed_length / sizeof(UniChar);
+ QList<QInputMethodEvent::Attribute> attrs;
+ attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
+ qFixedLength, text.length()-qFixedLength,
+ qt_mac_compose_format());
+ QInputMethodEvent e(text, attrs);
+ context->currentText = text;
+ e.setCommitString(text.left(qFixedLength), 0, qFixedLength);
+ qt_sendSpontaneousEvent(widget, &e);
+ handled_event = true;
+ } else {
+ /* Apple's enums that they have removed from Tiger :(
+ enum {
+ kCaretPosition = 1,
+ kRawText = 2,
+ kSelectedRawText = 3,
+ kConvertedText = 4,
+ kSelectedConvertedText = 5,
+ kBlockFillText = 6,
+ kOutlineText = 7,
+ kSelectedText = 8
+ };
+ */
+#ifndef kConvertedText
+#define kConvertedText 4
+#endif
+#ifndef kCaretPosition
+#define kCaretPosition 1
+#endif
+ QList<QInputMethodEvent::Attribute> attrs;
+ if (!highlight.isEmpty()) {
+ TextRangeArray *data = highlight.data();
+ for (int i = 0; i < data->fNumOfRanges; ++i) {
+ int start = data->fRange[i].fStart / sizeof(UniChar);
+ int len = (data->fRange[i].fEnd - data->fRange[i].fStart) / sizeof(UniChar);
+ if (data->fRange[i].fHiliteStyle == kCaretPosition) {
+ attrs << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, start, 0, QVariant());
+ continue;
+ }
+ QTextCharFormat format;
+ format.setFontUnderline(true);
+ if (data->fRange[i].fHiliteStyle == kConvertedText)
+ format.setUnderlineColor(Qt::gray);
+ else
+ format.setUnderlineColor(Qt::black);
+ attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, start, len, format);
+ }
+ } else {
+ attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
+ 0, text.length(), qt_mac_compose_format());
+ }
+ context->currentText = text;
+ QInputMethodEvent e(text, attrs);
+ qt_sendSpontaneousEvent(widget, &e);
+ handled_event = true;
+ }
+ }
+#if 0
+ if(!context->composing)
+ handled_event = false;
+#endif
+
+ extern bool qt_mac_eat_unicode_key; //qapplication_mac.cpp
+ qt_mac_eat_unicode_key = handled_event;
+ } else if(ekind == kEventTextInputUnicodeForKeyEvent) {
+ EventRef key_ev = 0;
+ GetEventParameter(event, kEventParamTextInputSendKeyboardEvent, typeEventRef, 0,
+ sizeof(key_ev), 0, &key_ev);
+ QString text;
+ ByteCount unilen = 0;
+ if(GetEventParameter(key_ev, kEventParamKeyUnicodes, typeUnicodeText, 0, 0, &unilen, 0) == noErr) {
+ UniChar *unicode = (UniChar*)NewPtr(unilen);
+ GetEventParameter(key_ev, kEventParamKeyUnicodes, typeUnicodeText, 0, unilen, 0, unicode);
+ text = QString((QChar*)unicode, unilen / sizeof(UniChar));
+ DisposePtr((char*)unicode);
+ }
+ unsigned char chr = 0;
+ GetEventParameter(key_ev, kEventParamKeyMacCharCodes, typeChar, 0, sizeof(chr), 0, &chr);
+ if(!chr || chr >= 128 || (text.length() > 0 && (text.length() > 1 || text.at(0) != QLatin1Char(chr))))
+ handled_event = !widget->testAttribute(Qt::WA_InputMethodEnabled);
+ QMacInputContext *context = qobject_cast<QMacInputContext*>(qApp->inputContext());
+ if (context && context->lastKeydownEvent()) {
+ qt_keymapper_private()->translateKeyEvent(widget, 0, context->lastKeydownEvent(),
+ 0, false);
+ context->setLastKeydownEvent(0);
+ }
+ }
+ break; }
+ default:
+ break;
+ }
+ if(!handled_event) //let the event go through
+ return eventNotHandledErr;
+#else
+ Q_UNUSED(event);
+#endif
+ return noErr; //we eat the event
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/mac/qmacinputcontext_p.h b/src/widgets/platforms/mac/qmacinputcontext_p.h
new file mode 100644
index 0000000000..4fb3eb57b6
--- /dev/null
+++ b/src/widgets/platforms/mac/qmacinputcontext_p.h
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMACINPUTCONTEXT_P_H
+#define QMACINPUTCONTEXT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtWidgets/qinputcontext.h"
+#include "private/qt_mac_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q_WIDGETS_EXPORT QMacInputContext : public QInputContext
+{
+ Q_OBJECT
+ //Q_DECLARE_PRIVATE(QMacInputContext)
+ void createTextDocument();
+public:
+ explicit QMacInputContext(QObject* parent = 0);
+ virtual ~QMacInputContext();
+
+ virtual void setFocusWidget(QWidget *w);
+ virtual QString identifierName() { return QLatin1String("mac"); }
+ virtual QString language();
+
+ virtual void reset();
+
+ virtual bool isComposing() const;
+
+ static OSStatus globalEventProcessor(EventHandlerCallRef, EventRef, void *);
+ static void initialize();
+ static void cleanup();
+
+ EventRef lastKeydownEvent() { return keydownEvent; }
+ void setLastKeydownEvent(EventRef);
+
+protected:
+ void mouseHandler(int pos, QMouseEvent *);
+private:
+ bool composing;
+ bool recursionGuard;
+ TSMDocumentID textDocument;
+ QString currentText;
+ EventRef keydownEvent;
+};
+
+QT_END_NAMESPACE
+
+#endif // QMACINPUTCONTEXT_P_H
diff --git a/src/widgets/platforms/mac/qmime_mac.cpp b/src/widgets/platforms/mac/qmime_mac.cpp
new file mode 100644
index 0000000000..0eaaaa8f2a
--- /dev/null
+++ b/src/widgets/platforms/mac/qmime_mac.cpp
@@ -0,0 +1,1310 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmime.h"
+
+//#define USE_INTERNET_CONFIG
+
+#ifndef USE_INTERNET_CONFIG
+# include "qfile.h"
+# include "qfileinfo.h"
+# include "qtextstream.h"
+# include "qdir.h"
+# include <unistd.h>
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <sys/fcntl.h>
+#endif
+
+#include "qdebug.h"
+#include "qpixmap.h"
+#include "qimagewriter.h"
+#include "qimagereader.h"
+#include "qdatastream.h"
+#include "qbuffer.h"
+#include "qdatetime.h"
+#include "qapplication_p.h"
+#include "qtextcodec.h"
+#include "qregexp.h"
+#include "qurl.h"
+#include "qmap.h"
+#include <private/qt_mac_p.h>
+
+
+#ifdef Q_WS_MAC32
+#include <QuickTime/QuickTime.h>
+#include <qlibrary.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+extern CGImageRef qt_mac_createCGImageFromQImage(const QImage &img, const QImage **imagePtr = 0); // qpaintengine_mac.cpp
+
+typedef QList<QMacPasteboardMime*> MimeList;
+Q_GLOBAL_STATIC(MimeList, globalMimeList)
+
+static void cleanup_mimes()
+{
+ MimeList *mimes = globalMimeList();
+ while (!mimes->isEmpty())
+ delete mimes->takeFirst();
+}
+
+Q_GLOBAL_STATIC(QStringList, globalDraggedTypesList)
+
+/*!
+ \fn void qRegisterDraggedTypes(const QStringList &types)
+ \relates QMacPasteboardMime
+
+ Registers the given \a types as custom pasteboard types.
+
+ This function should be called to enable the Drag and Drop events
+ for custom pasteboard types on Cocoa implementations. This is required
+ in addition to a QMacPasteboardMime subclass implementation. By default
+ drag and drop is enabled for all standard pasteboard types.
+
+ \sa QMacPasteboardMime
+*/
+Q_WIDGETS_EXPORT void qRegisterDraggedTypes(const QStringList &types)
+{
+ (*globalDraggedTypesList()) += types;
+}
+
+const QStringList& qEnabledDraggedTypes()
+{
+ return (*globalDraggedTypesList());
+}
+
+
+/*****************************************************************************
+ QDnD debug facilities
+ *****************************************************************************/
+//#define DEBUG_MIME_MAPS
+
+//functions
+extern QString qt_mac_from_pascal_string(const Str255); //qglobal.cpp
+extern void qt_mac_from_pascal_string(QString, Str255, TextEncoding encoding=0, int len=-1); //qglobal.cpp
+
+ScrapFlavorType qt_mac_mime_type = 'CUTE';
+CFStringRef qt_mac_mime_typeUTI = CFSTR("com.pasteboard.trolltech.marker");
+
+/*!
+ \class QMacPasteboardMime
+ \brief The QMacPasteboardMime class converts between a MIME type and a
+ \l{http://developer.apple.com/macosx/uniformtypeidentifiers.html}{Uniform
+ Type Identifier (UTI)} format.
+ \since 4.2
+
+ \ingroup draganddrop
+
+ Qt's drag and drop and clipboard facilities use the MIME
+ standard. On X11, this maps trivially to the Xdnd protocol. On
+ Mac, although some applications use MIME to describe clipboard
+ contents, it is more common to use Apple's UTI format.
+
+ QMacPasteboardMime's role is to bridge the gap between MIME and UTI;
+ By subclasses this class, one can extend Qt's drag and drop
+ and clipboard handling to convert to and from unsupported, or proprietary, UTI formats.
+
+ A subclass of QMacPasteboardMime will automatically be registered, and active, upon instantiation.
+
+ Qt has predefined support for the following UTIs:
+ \list
+ \i public.utf8-plain-text - converts to "text/plain"
+ \i public.utf16-plain-text - converts to "text/plain"
+ \i public.html - converts to "text/html"
+ \i public.url - converts to "text/uri-list"
+ \i public.file-url - converts to "text/uri-list"
+ \i public.tiff - converts to "application/x-qt-image"
+ \i public.vcard - converts to "text/plain"
+ \i com.apple.traditional-mac-plain-text - converts to "text/plain"
+ \i com.apple.pict - converts to "application/x-qt-image"
+ \endlist
+
+ When working with MIME data, Qt will interate through all instances of QMacPasteboardMime to
+ find an instance that can convert to, or from, a specific MIME type. It will do this by calling
+ canConvert() on each instance, starting with (and choosing) the last created instance first.
+ The actual conversions will be done by using convertToMime() and convertFromMime().
+
+ \note The API uses the term "flavor" in some cases. This is for backwards
+ compatibility reasons, and should now be understood as UTIs.
+*/
+
+/*! \enum QMacPasteboardMime::QMacPasteboardMimeType
+ \internal
+*/
+
+/*!
+ Constructs a new conversion object of type \a t, adding it to the
+ globally accessed list of available convertors.
+*/
+QMacPasteboardMime::QMacPasteboardMime(char t) : type(t)
+{
+ globalMimeList()->append(this);
+}
+
+/*!
+ Destroys a conversion object, removing it from the global
+ list of available convertors.
+*/
+QMacPasteboardMime::~QMacPasteboardMime()
+{
+ if(!QApplication::closingDown())
+ globalMimeList()->removeAll(this);
+}
+
+class QMacPasteboardMimeAny : public QMacPasteboardMime {
+private:
+
+public:
+ QMacPasteboardMimeAny() : QMacPasteboardMime(MIME_QT_CONVERTOR|MIME_ALL) {
+ }
+ ~QMacPasteboardMimeAny() {
+ }
+ QString convertorName();
+
+ QString flavorFor(const QString &mime);
+ QString mimeFor(QString flav);
+ bool canConvert(const QString &mime, QString flav);
+ QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
+ QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
+};
+
+QString QMacPasteboardMimeAny::convertorName()
+{
+ return QLatin1String("Any-Mime");
+}
+
+QString QMacPasteboardMimeAny::flavorFor(const QString &mime)
+{
+ // do not handle the mime type name in the drag pasteboard
+ if(mime == QLatin1String("application/x-qt-mime-type-name"))
+ return QString();
+ QString ret = QLatin1String("com.trolltech.anymime.") + mime;
+ return ret.replace(QLatin1Char('/'), QLatin1String("--"));
+}
+
+QString QMacPasteboardMimeAny::mimeFor(QString flav)
+{
+ const QString any_prefix = QLatin1String("com.trolltech.anymime.");
+ if(flav.size() > any_prefix.length() && flav.startsWith(any_prefix))
+ return flav.mid(any_prefix.length()).replace(QLatin1String("--"), QLatin1String("/"));
+ return QString();
+}
+
+bool QMacPasteboardMimeAny::canConvert(const QString &mime, QString flav)
+{
+ return mimeFor(flav) == mime;
+}
+
+QVariant QMacPasteboardMimeAny::convertToMime(const QString &mime, QList<QByteArray> data, QString)
+{
+ if(data.count() > 1)
+ qWarning("QMacPasteboardMimeAny: Cannot handle multiple member data");
+ QVariant ret;
+ if (mime == QLatin1String("text/plain"))
+ ret = QString::fromUtf8(data.first());
+ else
+ ret = data.first();
+ return ret;
+}
+
+QList<QByteArray> QMacPasteboardMimeAny::convertFromMime(const QString &mime, QVariant data, QString)
+{
+ QList<QByteArray> ret;
+ if (mime == QLatin1String("text/plain"))
+ ret.append(data.toString().toUtf8());
+ else
+ ret.append(data.toByteArray());
+ return ret;
+}
+
+class QMacPasteboardMimeTypeName : public QMacPasteboardMime {
+private:
+
+public:
+ QMacPasteboardMimeTypeName() : QMacPasteboardMime(MIME_QT_CONVERTOR|MIME_ALL) {
+ }
+ ~QMacPasteboardMimeTypeName() {
+ }
+ QString convertorName();
+
+ QString flavorFor(const QString &mime);
+ QString mimeFor(QString flav);
+ bool canConvert(const QString &mime, QString flav);
+ QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
+ QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
+};
+
+QString QMacPasteboardMimeTypeName::convertorName()
+{
+ return QLatin1String("Qt-Mime-Type");
+}
+
+QString QMacPasteboardMimeTypeName::flavorFor(const QString &mime)
+{
+ if(mime == QLatin1String("application/x-qt-mime-type-name"))
+ return QLatin1String("com.trolltech.qt.MimeTypeName");
+ return QString();
+}
+
+QString QMacPasteboardMimeTypeName::mimeFor(QString)
+{
+ return QString();
+}
+
+bool QMacPasteboardMimeTypeName::canConvert(const QString &, QString)
+{
+ return false;
+}
+
+QVariant QMacPasteboardMimeTypeName::convertToMime(const QString &, QList<QByteArray>, QString)
+{
+ QVariant ret;
+ return ret;
+}
+
+QList<QByteArray> QMacPasteboardMimeTypeName::convertFromMime(const QString &, QVariant, QString)
+{
+ QList<QByteArray> ret;
+ ret.append(QString("x-qt-mime-type-name").toUtf8());
+ return ret;
+}
+
+class QMacPasteboardMimePlainText : public QMacPasteboardMime {
+public:
+ QMacPasteboardMimePlainText() : QMacPasteboardMime(MIME_ALL) { }
+ QString convertorName();
+
+ QString flavorFor(const QString &mime);
+ QString mimeFor(QString flav);
+ bool canConvert(const QString &mime, QString flav);
+ QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
+ QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
+};
+
+QString QMacPasteboardMimePlainText::convertorName()
+{
+ return QLatin1String("PlainText");
+}
+
+QString QMacPasteboardMimePlainText::flavorFor(const QString &mime)
+{
+ if (mime == QLatin1String("text/plain"))
+ return QLatin1String("com.apple.traditional-mac-plain-text");
+ return QString();
+}
+
+QString QMacPasteboardMimePlainText::mimeFor(QString flav)
+{
+ if (flav == QLatin1String("com.apple.traditional-mac-plain-text"))
+ return QLatin1String("text/plain");
+ return QString();
+}
+
+bool QMacPasteboardMimePlainText::canConvert(const QString &mime, QString flav)
+{
+ return flavorFor(mime) == flav;
+}
+
+QVariant QMacPasteboardMimePlainText::convertToMime(const QString &mimetype, QList<QByteArray> data, QString flavor)
+{
+ if(data.count() > 1)
+ qWarning("QMacPasteboardMimePlainText: Cannot handle multiple member data");
+ const QByteArray &firstData = data.first();
+ QVariant ret;
+ if(flavor == QCFString(QLatin1String("com.apple.traditional-mac-plain-text"))) {
+ QCFString str(CFStringCreateWithBytes(kCFAllocatorDefault,
+ reinterpret_cast<const UInt8 *>(firstData.constData()),
+ firstData.size(), CFStringGetSystemEncoding(), false));
+ ret = QString(str);
+ } else {
+ qWarning("QMime::convertToMime: unhandled mimetype: %s", qPrintable(mimetype));
+ }
+ return ret;
+}
+
+QList<QByteArray> QMacPasteboardMimePlainText::convertFromMime(const QString &, QVariant data, QString flavor)
+{
+ QList<QByteArray> ret;
+ QString string = data.toString();
+ if(flavor == QCFString(QLatin1String("com.apple.traditional-mac-plain-text")))
+ ret.append(string.toLatin1());
+ return ret;
+}
+
+class QMacPasteboardMimeUnicodeText : public QMacPasteboardMime {
+public:
+ QMacPasteboardMimeUnicodeText() : QMacPasteboardMime(MIME_ALL) { }
+ QString convertorName();
+
+ QString flavorFor(const QString &mime);
+ QString mimeFor(QString flav);
+ bool canConvert(const QString &mime, QString flav);
+ QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
+ QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
+};
+
+QString QMacPasteboardMimeUnicodeText::convertorName()
+{
+ return QLatin1String("UnicodeText");
+}
+
+QString QMacPasteboardMimeUnicodeText::flavorFor(const QString &mime)
+{
+ if (mime == QLatin1String("text/plain"))
+ return QLatin1String("public.utf16-plain-text");
+ int i = mime.indexOf(QLatin1String("charset="));
+ if (i >= 0) {
+ QString cs(mime.mid(i+8).toLower());
+ i = cs.indexOf(QLatin1Char(';'));
+ if (i>=0)
+ cs = cs.left(i);
+ if (cs == QLatin1String("system"))
+ return QLatin1String("public.utf8-plain-text");
+ else if (cs == QLatin1String("iso-10646-ucs-2")
+ || cs == QLatin1String("utf16"))
+ return QLatin1String("public.utf16-plain-text");
+ }
+ return QString();
+}
+
+QString QMacPasteboardMimeUnicodeText::mimeFor(QString flav)
+{
+ if (flav == QLatin1String("public.utf16-plain-text") || flav == QLatin1String("public.utf8-plain-text"))
+ return QLatin1String("text/plain");
+ return QString();
+}
+
+bool QMacPasteboardMimeUnicodeText::canConvert(const QString &mime, QString flav)
+{
+ return flavorFor(mime) == flav;
+}
+
+QVariant QMacPasteboardMimeUnicodeText::convertToMime(const QString &mimetype, QList<QByteArray> data, QString flavor)
+{
+ if(data.count() > 1)
+ qWarning("QMacPasteboardMimeUnicodeText: Cannot handle multiple member data");
+ const QByteArray &firstData = data.first();
+ // I can only handle two types (system and unicode) so deal with them that way
+ QVariant ret;
+ if(flavor == QLatin1String("public.utf8-plain-text")) {
+ QCFString str(CFStringCreateWithBytes(kCFAllocatorDefault,
+ reinterpret_cast<const UInt8 *>(firstData.constData()),
+ firstData.size(), CFStringGetSystemEncoding(), false));
+ ret = QString(str);
+ } else if (flavor == QLatin1String("public.utf16-plain-text")) {
+ ret = QString(reinterpret_cast<const QChar *>(firstData.constData()),
+ firstData.size() / sizeof(QChar));
+ } else {
+ qWarning("QMime::convertToMime: unhandled mimetype: %s", qPrintable(mimetype));
+ }
+ return ret;
+}
+
+QList<QByteArray> QMacPasteboardMimeUnicodeText::convertFromMime(const QString &, QVariant data, QString flavor)
+{
+ QList<QByteArray> ret;
+ QString string = data.toString();
+ if(flavor == QLatin1String("public.utf8-plain-text"))
+ ret.append(string.toUtf8());
+ else if (flavor == QLatin1String("public.utf16-plain-text"))
+ ret.append(QByteArray((char*)string.utf16(), string.length()*2));
+ return ret;
+}
+
+class QMacPasteboardMimeHTMLText : public QMacPasteboardMime {
+public:
+ QMacPasteboardMimeHTMLText() : QMacPasteboardMime(MIME_ALL) { }
+ QString convertorName();
+
+ QString flavorFor(const QString &mime);
+ QString mimeFor(QString flav);
+ bool canConvert(const QString &mime, QString flav);
+ QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
+ QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
+};
+
+QString QMacPasteboardMimeHTMLText::convertorName()
+{
+ return QLatin1String("HTML");
+}
+
+QString QMacPasteboardMimeHTMLText::flavorFor(const QString &mime)
+{
+ if (mime == QLatin1String("text/html"))
+ return QLatin1String("public.html");
+ return QString();
+}
+
+QString QMacPasteboardMimeHTMLText::mimeFor(QString flav)
+{
+ if (flav == QLatin1String("public.html"))
+ return QLatin1String("text/html");
+ return QString();
+}
+
+bool QMacPasteboardMimeHTMLText::canConvert(const QString &mime, QString flav)
+{
+ return flavorFor(mime) == flav;
+}
+
+QVariant QMacPasteboardMimeHTMLText::convertToMime(const QString &mimeType, QList<QByteArray> data, QString flavor)
+{
+ if (!canConvert(mimeType, flavor))
+ return QVariant();
+ if (data.count() > 1)
+ qWarning("QMacPasteboardMimeHTMLText: Cannot handle multiple member data");
+ return data.first();
+}
+
+QList<QByteArray> QMacPasteboardMimeHTMLText::convertFromMime(const QString &mime, QVariant data, QString flavor)
+{
+ QList<QByteArray> ret;
+ if (!canConvert(mime, flavor))
+ return ret;
+ ret.append(data.toByteArray());
+ return ret;
+}
+
+
+#ifdef Q_WS_MAC32
+
+// This can be removed once 10.6 is the minimum (or we have to require 64-bit) whichever comes first.
+
+typedef ComponentResult (*PtrGraphicsImportSetDataHandle)(GraphicsImportComponent, Handle);
+typedef ComponentResult (*PtrGraphicsImportCreateCGImage)(GraphicsImportComponent, CGImageRef*, UInt32);
+typedef ComponentResult (*PtrGraphicsExportSetInputCGImage)(GraphicsExportComponent, CGImageRef);
+typedef ComponentResult (*PtrGraphicsExportSetOutputHandle)(GraphicsExportComponent, Handle);
+typedef ComponentResult (*PtrGraphicsExportDoExport)(GraphicsExportComponent, unsigned long *);
+
+static PtrGraphicsImportSetDataHandle ptrGraphicsImportSetDataHandle = 0;
+static PtrGraphicsImportCreateCGImage ptrGraphicsImportCreateCGImage = 0;
+static PtrGraphicsExportSetInputCGImage ptrGraphicsExportSetInputCGImage = 0;
+static PtrGraphicsExportSetOutputHandle ptrGraphicsExportSetOutputHandle = 0;
+static PtrGraphicsExportDoExport ptrGraphicsExportDoExport = 0;
+
+static bool resolveMimeQuickTimeSymbols()
+{
+ if (ptrGraphicsImportSetDataHandle == 0) {
+ QLibrary library(QLatin1String("/System/Library/Frameworks/QuickTime.framework/QuickTime"));
+ ptrGraphicsImportSetDataHandle = reinterpret_cast<PtrGraphicsImportSetDataHandle>(library.resolve("GraphicsImportSetDataHandle"));
+ ptrGraphicsImportCreateCGImage = reinterpret_cast<PtrGraphicsImportCreateCGImage>(library.resolve("GraphicsImportCreateCGImage"));
+ ptrGraphicsExportSetInputCGImage = reinterpret_cast<PtrGraphicsExportSetInputCGImage>(library.resolve("GraphicsExportSetInputCGImage"));
+ ptrGraphicsExportSetOutputHandle = reinterpret_cast<PtrGraphicsExportSetOutputHandle>(library.resolve("GraphicsExportSetOutputHandle"));
+ ptrGraphicsExportDoExport = reinterpret_cast<PtrGraphicsExportDoExport>(library.resolve("GraphicsExportDoExport"));
+ }
+
+ return ptrGraphicsImportSetDataHandle != 0
+ && ptrGraphicsImportCreateCGImage != 0 && ptrGraphicsExportSetInputCGImage != 0
+ && ptrGraphicsExportSetOutputHandle != 0 && ptrGraphicsExportDoExport != 0;
+}
+
+class QMacPasteboardMimePict : public QMacPasteboardMime {
+public:
+ QMacPasteboardMimePict() : QMacPasteboardMime(MIME_ALL) { }
+ QString convertorName();
+
+ QString flavorFor(const QString &mime);
+ QString mimeFor(QString flav);
+ bool canConvert(const QString &mime, QString flav);
+ QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
+ QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
+};
+
+QString QMacPasteboardMimePict::convertorName()
+{
+ return QLatin1String("Pict");
+}
+
+QString QMacPasteboardMimePict::flavorFor(const QString &mime)
+{
+ if(mime.startsWith(QLatin1String("application/x-qt-image")))
+ return QLatin1String("com.apple.pict");
+ return QString();
+}
+
+QString QMacPasteboardMimePict::mimeFor(QString flav)
+{
+ if(flav == QLatin1String("com.apple.pict"))
+ return QLatin1String("application/x-qt-image");
+ return QString();
+}
+
+bool QMacPasteboardMimePict::canConvert(const QString &mime, QString flav)
+{
+ return flav == QLatin1String("com.apple.pict")
+ && mime == QLatin1String("application/x-qt-image");
+}
+
+
+QVariant QMacPasteboardMimePict::convertToMime(const QString &mime, QList<QByteArray> data, QString flav)
+{
+ if(data.count() > 1)
+ qWarning("QMacPasteboardMimePict: Cannot handle multiple member data");
+ QVariant ret;
+ if (!resolveMimeQuickTimeSymbols())
+ return ret;
+
+ if(!canConvert(mime, flav))
+ return ret;
+ const QByteArray &a = data.first();
+
+ // This function expects the 512 header (just to skip it, so create the extra space for it).
+ Handle pic = NewHandle(a.size() + 512);
+ memcpy(*pic + 512, a.constData(), a.size());
+
+ GraphicsImportComponent graphicsImporter;
+ ComponentResult result = OpenADefaultComponent(GraphicsImporterComponentType,
+ kQTFileTypePicture, &graphicsImporter);
+ QCFType<CGImageRef> cgImage;
+ if (!result)
+ result = ptrGraphicsImportSetDataHandle(graphicsImporter, pic);
+ if (!result)
+ result = ptrGraphicsImportCreateCGImage(graphicsImporter, &cgImage,
+ kGraphicsImportCreateCGImageUsingCurrentSettings);
+ if (!result)
+ ret = QVariant(QPixmap::fromMacCGImageRef(cgImage).toImage());
+ CloseComponent(graphicsImporter);
+ DisposeHandle(pic);
+ return ret;
+}
+
+QList<QByteArray> QMacPasteboardMimePict::convertFromMime(const QString &mime, QVariant variant,
+ QString flav)
+{
+ QList<QByteArray> ret;
+ if (!resolveMimeQuickTimeSymbols())
+ return ret;
+
+ if (!canConvert(mime, flav))
+ return ret;
+ QCFType<CGImageRef> cgimage = qt_mac_createCGImageFromQImage(qvariant_cast<QImage>(variant));
+ Handle pic = NewHandle(0);
+ GraphicsExportComponent graphicsExporter;
+ ComponentResult result = OpenADefaultComponent(GraphicsExporterComponentType,
+ kQTFileTypePicture, &graphicsExporter);
+ if (!result) {
+ unsigned long sizeWritten;
+ result = ptrGraphicsExportSetInputCGImage(graphicsExporter, cgimage);
+ if (!result)
+ result = ptrGraphicsExportSetOutputHandle(graphicsExporter, pic);
+ if (!result)
+ result = ptrGraphicsExportDoExport(graphicsExporter, &sizeWritten);
+
+ CloseComponent(graphicsExporter);
+ }
+
+ int size = GetHandleSize((Handle)pic);
+ // Skip the Picture File header (512 bytes) and feed the raw data
+ QByteArray ar(reinterpret_cast<char *>(*pic + 512), size - 512);
+ ret.append(ar);
+ DisposeHandle(pic);
+ return ret;
+}
+
+
+#endif //Q_WS_MAC32
+
+class QMacPasteboardMimeTiff : public QMacPasteboardMime {
+public:
+ QMacPasteboardMimeTiff() : QMacPasteboardMime(MIME_ALL) { }
+ QString convertorName();
+
+ QString flavorFor(const QString &mime);
+ QString mimeFor(QString flav);
+ bool canConvert(const QString &mime, QString flav);
+ QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
+ QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
+};
+
+QString QMacPasteboardMimeTiff::convertorName()
+{
+ return QLatin1String("Tiff");
+}
+
+QString QMacPasteboardMimeTiff::flavorFor(const QString &mime)
+{
+ if(mime.startsWith(QLatin1String("application/x-qt-image")))
+ return QLatin1String("public.tiff");
+ return QString();
+}
+
+QString QMacPasteboardMimeTiff::mimeFor(QString flav)
+{
+ if(flav == QLatin1String("public.tiff"))
+ return QLatin1String("application/x-qt-image");
+ return QString();
+}
+
+bool QMacPasteboardMimeTiff::canConvert(const QString &mime, QString flav)
+{
+ return flav == QLatin1String("public.tiff") && mime == QLatin1String("application/x-qt-image");
+}
+
+QVariant QMacPasteboardMimeTiff::convertToMime(const QString &mime, QList<QByteArray> data, QString flav)
+{
+ if(data.count() > 1)
+ qWarning("QMacPasteboardMimeTiff: Cannot handle multiple member data");
+ QVariant ret;
+ if (!canConvert(mime, flav))
+ return ret;
+ const QByteArray &a = data.first();
+ QCFType<CGImageRef> image;
+ QCFType<CFDataRef> tiffData = CFDataCreateWithBytesNoCopy(0,
+ reinterpret_cast<const UInt8 *>(a.constData()),
+ a.size(), kCFAllocatorNull);
+ QCFType<CGImageSourceRef> imageSource = CGImageSourceCreateWithData(tiffData, 0);
+ image = CGImageSourceCreateImageAtIndex(imageSource, 0, 0);
+
+ if (image != 0)
+ ret = QVariant(QPixmap::fromMacCGImageRef(image).toImage());
+ return ret;
+}
+
+QList<QByteArray> QMacPasteboardMimeTiff::convertFromMime(const QString &mime, QVariant variant, QString flav)
+{
+ QList<QByteArray> ret;
+ if (!canConvert(mime, flav))
+ return ret;
+
+ QImage img = qvariant_cast<QImage>(variant);
+ QCFType<CGImageRef> cgimage = qt_mac_createCGImageFromQImage(img);
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
+ QCFType<CFMutableDataRef> data = CFDataCreateMutable(0, 0);
+ QCFType<CGImageDestinationRef> imageDestination = CGImageDestinationCreateWithData(data, kUTTypeTIFF, 1, 0);
+ if (imageDestination != 0) {
+ CFTypeRef keys[2];
+ QCFType<CFTypeRef> values[2];
+ QCFType<CFDictionaryRef> options;
+ keys[0] = kCGImagePropertyPixelWidth;
+ keys[1] = kCGImagePropertyPixelHeight;
+ int width = img.width();
+ int height = img.height();
+ values[0] = CFNumberCreate(0, kCFNumberIntType, &width);
+ values[1] = CFNumberCreate(0, kCFNumberIntType, &height);
+ options = CFDictionaryCreate(0, reinterpret_cast<const void **>(keys),
+ reinterpret_cast<const void **>(values), 2,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ CGImageDestinationAddImage(imageDestination, cgimage, options);
+ CGImageDestinationFinalize(imageDestination);
+ }
+ QByteArray ar(CFDataGetLength(data), 0);
+ CFDataGetBytes(data,
+ CFRangeMake(0, ar.size()),
+ reinterpret_cast<UInt8 *>(ar.data()));
+ ret.append(ar);
+ } else
+#endif
+ {
+#ifdef Q_WS_MAC32
+ Handle tiff = NewHandle(0);
+ if (resolveMimeQuickTimeSymbols()) {
+ GraphicsExportComponent graphicsExporter;
+ ComponentResult result = OpenADefaultComponent(GraphicsExporterComponentType,
+ kQTFileTypeTIFF, &graphicsExporter);
+ if (!result) {
+ unsigned long sizeWritten;
+ result = ptrGraphicsExportSetInputCGImage(graphicsExporter, cgimage);
+ if (!result)
+ result = ptrGraphicsExportSetOutputHandle(graphicsExporter, tiff);
+ if (!result)
+ result = ptrGraphicsExportDoExport(graphicsExporter, &sizeWritten);
+
+ CloseComponent(graphicsExporter);
+ }
+ }
+ int size = GetHandleSize((Handle)tiff);
+ QByteArray ar(reinterpret_cast<char *>(*tiff), size);
+ ret.append(ar);
+ DisposeHandle(tiff);
+#endif
+ }
+ return ret;
+}
+
+
+class QMacPasteboardMimeFileUri : public QMacPasteboardMime {
+public:
+ QMacPasteboardMimeFileUri() : QMacPasteboardMime(MIME_ALL) { }
+ QString convertorName();
+
+ QString flavorFor(const QString &mime);
+ QString mimeFor(QString flav);
+ bool canConvert(const QString &mime, QString flav);
+ QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
+ QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
+};
+
+QString QMacPasteboardMimeFileUri::convertorName()
+{
+ return QLatin1String("FileURL");
+}
+
+QString QMacPasteboardMimeFileUri::flavorFor(const QString &mime)
+{
+ if (mime == QLatin1String("text/uri-list"))
+ return QCFString(UTTypeCreatePreferredIdentifierForTag(kUTTagClassOSType, CFSTR("furl"), 0));
+ return QString();
+}
+
+QString QMacPasteboardMimeFileUri::mimeFor(QString flav)
+{
+ if (flav == QCFString(UTTypeCreatePreferredIdentifierForTag(kUTTagClassOSType, CFSTR("furl"), 0)))
+ return QLatin1String("text/uri-list");
+ return QString();
+}
+
+bool QMacPasteboardMimeFileUri::canConvert(const QString &mime, QString flav)
+{
+ return mime == QLatin1String("text/uri-list")
+ && flav == QCFString(UTTypeCreatePreferredIdentifierForTag(kUTTagClassOSType, CFSTR("furl"), 0));
+}
+
+QVariant QMacPasteboardMimeFileUri::convertToMime(const QString &mime, QList<QByteArray> data, QString flav)
+{
+ if(!canConvert(mime, flav))
+ return QVariant();
+ QList<QVariant> ret;
+ for(int i = 0; i < data.size(); ++i) {
+ QUrl url = QUrl::fromEncoded(data.at(i));
+ if (url.host().toLower() == QLatin1String("localhost"))
+ url.setHost(QString());
+ url.setPath(url.path().normalized(QString::NormalizationForm_C));
+ ret.append(url);
+ }
+ return QVariant(ret);
+}
+
+QList<QByteArray> QMacPasteboardMimeFileUri::convertFromMime(const QString &mime, QVariant data, QString flav)
+{
+ QList<QByteArray> ret;
+ if (!canConvert(mime, flav))
+ return ret;
+ QList<QVariant> urls = data.toList();
+ for(int i = 0; i < urls.size(); ++i) {
+ QUrl url = urls.at(i).toUrl();
+ if (url.scheme().isEmpty())
+ url.setScheme(QLatin1String("file"));
+ if (url.scheme().toLower() == QLatin1String("file")) {
+ if (url.host().isEmpty())
+ url.setHost(QLatin1String("localhost"));
+ url.setPath(url.path().normalized(QString::NormalizationForm_D));
+ }
+ ret.append(url.toEncoded());
+ }
+ return ret;
+}
+
+class QMacPasteboardMimeUrl : public QMacPasteboardMime {
+public:
+ QMacPasteboardMimeUrl() : QMacPasteboardMime(MIME_ALL) { }
+ QString convertorName();
+
+ QString flavorFor(const QString &mime);
+ QString mimeFor(QString flav);
+ bool canConvert(const QString &mime, QString flav);
+ QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
+ QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
+};
+
+QString QMacPasteboardMimeUrl::convertorName()
+{
+ return QLatin1String("URL");
+}
+
+QString QMacPasteboardMimeUrl::flavorFor(const QString &mime)
+{
+ if(mime.startsWith(QLatin1String("text/uri-list")))
+ return QLatin1String("public.url");
+ return QString();
+}
+
+QString QMacPasteboardMimeUrl::mimeFor(QString flav)
+{
+ if(flav == QLatin1String("public.url"))
+ return QLatin1String("text/uri-list");
+ return QString();
+}
+
+bool QMacPasteboardMimeUrl::canConvert(const QString &mime, QString flav)
+{
+ return flav == QLatin1String("public.url")
+ && mime == QLatin1String("text/uri-list");
+}
+
+QVariant QMacPasteboardMimeUrl::convertToMime(const QString &mime, QList<QByteArray> data, QString flav)
+{
+ if(!canConvert(mime, flav))
+ return QVariant();
+
+ QList<QVariant> ret;
+ for (int i=0; i<data.size(); ++i) {
+ QUrl url = QUrl::fromEncoded(data.at(i));
+ if (url.host().toLower() == QLatin1String("localhost"))
+ url.setHost(QString());
+ url.setPath(url.path().normalized(QString::NormalizationForm_C));
+ ret.append(url);
+ }
+ return QVariant(ret);
+}
+
+QList<QByteArray> QMacPasteboardMimeUrl::convertFromMime(const QString &mime, QVariant data, QString flav)
+{
+ QList<QByteArray> ret;
+ if (!canConvert(mime, flav))
+ return ret;
+
+ QList<QVariant> urls = data.toList();
+ for(int i=0; i<urls.size(); ++i) {
+ QUrl url = urls.at(i).toUrl();
+ if (url.scheme().isEmpty())
+ url.setScheme(QLatin1String("file"));
+ if (url.scheme().toLower() == QLatin1String("file")) {
+ if (url.host().isEmpty())
+ url.setHost(QLatin1String("localhost"));
+ url.setPath(url.path().normalized(QString::NormalizationForm_D));
+ }
+ ret.append(url.toEncoded());
+ }
+ return ret;
+}
+
+class QMacPasteboardMimeVCard : public QMacPasteboardMime
+{
+public:
+ QMacPasteboardMimeVCard() : QMacPasteboardMime(MIME_ALL){ }
+ QString convertorName();
+
+ QString flavorFor(const QString &mime);
+ QString mimeFor(QString flav);
+ bool canConvert(const QString &mime, QString flav);
+ QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
+ QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
+};
+
+QString QMacPasteboardMimeVCard::convertorName()
+{
+ return QString("VCard");
+}
+
+bool QMacPasteboardMimeVCard::canConvert(const QString &mime, QString flav)
+{
+ return mimeFor(flav) == mime;
+}
+
+QString QMacPasteboardMimeVCard::flavorFor(const QString &mime)
+{
+ if(mime.startsWith(QLatin1String("text/plain")))
+ return QLatin1String("public.vcard");
+ return QString();
+}
+
+QString QMacPasteboardMimeVCard::mimeFor(QString flav)
+{
+ if (flav == QLatin1String("public.vcard"))
+ return QLatin1String("text/plain");
+ return QString();
+}
+
+QVariant QMacPasteboardMimeVCard::convertToMime(const QString &mime, QList<QByteArray> data, QString)
+{
+ QByteArray cards;
+ if (mime == QLatin1String("text/plain")) {
+ for (int i=0; i<data.size(); ++i)
+ cards += data[i];
+ }
+ return QVariant(cards);
+}
+
+QList<QByteArray> QMacPasteboardMimeVCard::convertFromMime(const QString &mime, QVariant data, QString)
+{
+ QList<QByteArray> ret;
+ if (mime == QLatin1String("text/plain"))
+ ret.append(data.toString().toUtf8());
+ return ret;
+}
+
+#ifdef QT3_SUPPORT
+class QMacPasteboardMimeQt3Any : public QMacPasteboardMime {
+private:
+ int current_max;
+ QFile library_file;
+ QDateTime mime_registry_loaded;
+ QMap<QString, int> mime_registry;
+ int registerMimeType(const QString &mime);
+ bool loadMimeRegistry();
+
+public:
+ QMacPasteboardMimeQt3Any() : QMacPasteboardMime(MIME_QT3_CONVERTOR) {
+ current_max = 'QT00';
+ }
+ ~QMacPasteboardMimeQt3Any() {
+ }
+ QString convertorName();
+
+ QString flavorFor(const QString &mime);
+ QString mimeFor(QString flav);
+ bool canConvert(const QString &mime, QString flav);
+ QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
+ QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
+};
+
+static bool qt_mac_openMimeRegistry(bool global, QIODevice::OpenMode mode, QFile &file)
+{
+ QString dir = QLatin1String("/Library/Qt");
+ if(!global)
+ dir.prepend(QDir::homePath());
+ file.setFileName(dir + QLatin1String("/.mime_types"));
+ if(mode != QIODevice::ReadOnly) {
+ if(!QFile::exists(dir)) {
+ // Do it with a system call as I don't see much worth in
+ // doing it with QDir since we have to chmod anyway.
+ bool success = ::mkdir(dir.toLocal8Bit().constData(), S_IRUSR | S_IWUSR | S_IXUSR) == 0;
+ if (success)
+ success = ::chmod(dir.toLocal8Bit().constData(), S_IRUSR | S_IWUSR | S_IXUSR
+ | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH) == 0;
+ if (!success)
+ return false;
+ }
+ if (!file.exists()) {
+ // Create the file and chmod it so that everyone can write to it.
+ int fd = ::open(file.fileName().toLocal8Bit().constData(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
+ bool success = fd != -1;
+ if (success)
+ success = ::fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) == 0;
+ if (fd != -1)
+ ::close(fd);
+ if(!success)
+ return false;
+ }
+ }
+ return file.open(mode);
+}
+
+static void qt_mac_loadMimeRegistry(QFile &file, QMap<QString, int> &registry, int &max)
+{
+ file.reset();
+ QTextStream stream(&file);
+ while(!stream.atEnd()) {
+ QString mime = stream.readLine();
+ int mactype = stream.readLine().toInt();
+ if(mactype > max)
+ max = mactype;
+ registry.insert(mime, mactype);
+ }
+}
+
+bool QMacPasteboardMimeQt3Any::loadMimeRegistry()
+{
+ if(!library_file.isOpen()) {
+ if(!qt_mac_openMimeRegistry(true, QIODevice::ReadWrite, library_file)) {
+ QFile global;
+ if(qt_mac_openMimeRegistry(true, QIODevice::ReadOnly, global)) {
+ qt_mac_loadMimeRegistry(global, mime_registry, current_max);
+ global.close();
+ }
+ if(!qt_mac_openMimeRegistry(false, QIODevice::ReadWrite, library_file)) {
+ qWarning("QMacPasteboardMimeAnyQt3Mime: Failure to open mime resources %s -- %s", library_file.fileName().toLatin1().constData(),
+ library_file.errorString().toLatin1().constData());
+ return false;
+ }
+ }
+ }
+
+ QFileInfo fi(library_file);
+ if(!mime_registry_loaded.isNull() && mime_registry_loaded == fi.lastModified())
+ return true;
+ mime_registry_loaded = fi.lastModified();
+ qt_mac_loadMimeRegistry(library_file, mime_registry, current_max);
+ return true;
+}
+
+int QMacPasteboardMimeQt3Any::registerMimeType(const QString &mime)
+{
+ if(!mime_registry.contains(mime)) {
+ if(!loadMimeRegistry()) {
+ qWarning("QMacPasteboardMimeAnyQt3Mime: Internal error");
+ return 0;
+ }
+ if(!mime_registry.contains(mime)) {
+ if(!library_file.isOpen()) {
+ if(!library_file.open(QIODevice::WriteOnly)) {
+ qWarning("QMacPasteboardMimeAnyQt3Mime: Failure to open %s -- %s", library_file.fileName().toLatin1().constData(),
+ library_file.errorString().toLatin1().constData());
+ return false;
+ }
+ }
+ int ret = ++current_max;
+ mime_registry_loaded = QFileInfo(library_file).lastModified();
+ QTextStream stream(&library_file);
+ stream << mime << endl;
+ stream << ret << endl;
+ mime_registry.insert(mime, ret);
+ library_file.flush(); //flush and set mtime
+ return ret;
+ }
+ }
+ return mime_registry[mime];
+}
+
+QString QMacPasteboardMimeQt3Any::convertorName()
+{
+ return QLatin1String("Qt3-Any-Mime");
+}
+
+QString QMacPasteboardMimeQt3Any::flavorFor(const QString &mime)
+{
+ const int os_flav = registerMimeType(mime);
+ QCFType<CFArrayRef> ids = UTTypeCreateAllIdentifiersForTag(0, kUTTagClassOSType,
+ QCFString(UTCreateStringForOSType(os_flav)));
+ if(ids) {
+ const int type_count = CFArrayGetCount(ids);
+ if(type_count) {
+ if(type_count > 1)
+ qDebug("Can't happen!");
+ return QCFString::toQString((CFStringRef)CFArrayGetValueAtIndex(ids, 0));
+ }
+ }
+ return QString();
+}
+
+QString QMacPasteboardMimeQt3Any::mimeFor(QString flav)
+{
+ loadMimeRegistry();
+ const int os_flav = UTGetOSTypeFromString(UTTypeCopyPreferredTagWithClass(QCFString(flav), kUTTagClassOSType));
+ for(QMap<QString, int>::const_iterator it = mime_registry.constBegin();
+ it != mime_registry.constEnd(); ++it) {
+ if(it.value() == os_flav)
+ return QString::fromLatin1(it.key().toLatin1());
+ }
+ return QString();
+}
+
+bool QMacPasteboardMimeQt3Any::canConvert(const QString &mime, QString flav)
+{
+ loadMimeRegistry();
+ const int os_flav = UTGetOSTypeFromString(UTTypeCopyPreferredTagWithClass(QCFString(flav), kUTTagClassOSType));
+ if(mime_registry.contains(mime) && mime_registry[mime] == os_flav)
+ return true;
+ return false;
+}
+
+QVariant QMacPasteboardMimeQt3Any::convertToMime(const QString &, QList<QByteArray>, QString)
+{
+ qWarning("QMacPasteboardMimeAnyQt3Mime: Cannot write anything!");
+ return QVariant();
+}
+
+QList<QByteArray> QMacPasteboardMimeQt3Any::convertFromMime(const QString &mime, QVariant data, QString)
+{
+ QList<QByteArray> ret;
+ if (mime == QLatin1String("text/plain")) {
+ ret.append(data.toString().toUtf8());
+ } else {
+ ret.append(data.toByteArray());
+ }
+ return ret;
+}
+#endif
+
+/*!
+ \internal
+
+ This is an internal function.
+*/
+void QMacPasteboardMime::initialize()
+{
+ if(globalMimeList()->isEmpty()) {
+ qAddPostRoutine(cleanup_mimes);
+
+ //standard types that we wrap
+ new QMacPasteboardMimeTiff;
+#ifdef Q_WS_MAC32
+ // 10.6 does automatic synthesis to and from PICT to standard image types (like TIFF),
+ // so don't bother doing it ourselves, especially since it's not available in 64-bit.
+ if (QSysInfo::MacintoshVersion < QSysInfo::MV_10_6)
+ new QMacPasteboardMimePict;
+#endif
+ new QMacPasteboardMimeUnicodeText;
+ new QMacPasteboardMimePlainText;
+ new QMacPasteboardMimeHTMLText;
+ new QMacPasteboardMimeFileUri;
+ new QMacPasteboardMimeUrl;
+ new QMacPasteboardMimeTypeName;
+ new QMacPasteboardMimeVCard;
+ //make sure our "non-standard" types are always last! --Sam
+ new QMacPasteboardMimeAny;
+#ifdef QT3_SUPPORT
+ new QMacPasteboardMimeQt3Any;
+#endif
+ }
+}
+
+/*!
+ Returns the most-recently created QMacPasteboardMime of type \a t that can convert
+ between the \a mime and \a flav formats. Returns 0 if no such convertor
+ exists.
+*/
+QMacPasteboardMime*
+QMacPasteboardMime::convertor(uchar t, const QString &mime, QString flav)
+{
+ MimeList *mimes = globalMimeList();
+ for(MimeList::const_iterator it = mimes->constBegin(); it != mimes->constEnd(); ++it) {
+#ifdef DEBUG_MIME_MAPS
+ qDebug("QMacPasteboardMime::convertor: seeing if %s (%d) can convert %s to %d[%c%c%c%c] [%d]",
+ (*it)->convertorName().toLatin1().constData(),
+ (*it)->type & t, mime.toLatin1().constData(),
+ flav, (flav >> 24) & 0xFF, (flav >> 16) & 0xFF, (flav >> 8) & 0xFF, (flav) & 0xFF,
+ (*it)->canConvert(mime,flav));
+ for(int i = 0; i < (*it)->countFlavors(); ++i) {
+ int f = (*it)->flavor(i);
+ qDebug(" %d) %d[%c%c%c%c] [%s]", i, f,
+ (f >> 24) & 0xFF, (f >> 16) & 0xFF, (f >> 8) & 0xFF, (f) & 0xFF,
+ (*it)->convertorName().toLatin1().constData());
+ }
+#endif
+ if(((*it)->type & t) && (*it)->canConvert(mime, flav))
+ return (*it);
+ }
+ return 0;
+}
+/*!
+ Returns a MIME type of type \a t for \a flav, or 0 if none exists.
+*/
+QString QMacPasteboardMime::flavorToMime(uchar t, QString flav)
+{
+ MimeList *mimes = globalMimeList();
+ for(MimeList::const_iterator it = mimes->constBegin(); it != mimes->constEnd(); ++it) {
+#ifdef DEBUG_MIME_MAPS
+ qDebug("QMacMIme::flavorToMime: attempting %s (%d) for flavor %d[%c%c%c%c] [%s]",
+ (*it)->convertorName().toLatin1().constData(),
+ (*it)->type & t, flav, (flav >> 24) & 0xFF, (flav >> 16) & 0xFF, (flav >> 8) & 0xFF, (flav) & 0xFF,
+ (*it)->mimeFor(flav).toLatin1().constData());
+
+#endif
+ if((*it)->type & t) {
+ QString mimeType = (*it)->mimeFor(flav);
+ if(!mimeType.isNull())
+ return mimeType;
+ }
+ }
+ return QString();
+}
+
+/*!
+ Returns a list of all currently defined QMacPasteboardMime objects of type \a t.
+*/
+QList<QMacPasteboardMime*> QMacPasteboardMime::all(uchar t)
+{
+ MimeList ret;
+ MimeList *mimes = globalMimeList();
+ for(MimeList::const_iterator it = mimes->constBegin(); it != mimes->constEnd(); ++it) {
+ if((*it)->type & t)
+ ret.append((*it));
+ }
+ return ret;
+}
+
+
+/*!
+ \fn QString QMacPasteboardMime::convertorName()
+
+ Returns a name for the convertor.
+
+ All subclasses must reimplement this pure virtual function.
+*/
+
+/*!
+ \fn bool QMacPasteboardMime::canConvert(const QString &mime, QString flav)
+
+ Returns true if the convertor can convert (both ways) between
+ \a mime and \a flav; otherwise returns false.
+
+ All subclasses must reimplement this pure virtual function.
+*/
+
+/*!
+ \fn QString QMacPasteboardMime::mimeFor(QString flav)
+
+ Returns the MIME UTI used for Mac flavor \a flav, or 0 if this
+ convertor does not support \a flav.
+
+ All subclasses must reimplement this pure virtual function.
+*/
+
+/*!
+ \fn QString QMacPasteboardMime::flavorFor(const QString &mime)
+
+ Returns the Mac UTI used for MIME type \a mime, or 0 if this
+ convertor does not support \a mime.
+
+ All subclasses must reimplement this pure virtual function.
+*/
+
+/*!
+ \fn QVariant QMacPasteboardMime::convertToMime(const QString &mime, QList<QByteArray> data, QString flav)
+
+ Returns \a data converted from Mac UTI \a flav to MIME type \a
+ mime.
+
+ Note that Mac flavors must all be self-terminating. The input \a
+ data may contain trailing data.
+
+ All subclasses must reimplement this pure virtual function.
+*/
+
+/*!
+ \fn QList<QByteArray> QMacPasteboardMime::convertFromMime(const QString &mime, QVariant data, QString flav)
+
+ Returns \a data converted from MIME type \a mime
+ to Mac UTI \a flav.
+
+ Note that Mac flavors must all be self-terminating. The return
+ value may contain trailing data.
+
+ All subclasses must reimplement this pure virtual function.
+*/
+
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/mac/qmultitouch_mac.mm b/src/widgets/platforms/mac/qmultitouch_mac.mm
new file mode 100644
index 0000000000..30b0acb388
--- /dev/null
+++ b/src/widgets/platforms/mac/qmultitouch_mac.mm
@@ -0,0 +1,218 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qmultitouch_mac_p.h>
+#include <qcursor.h>
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
+
+QT_BEGIN_NAMESPACE
+
+#ifdef QT_MAC_USE_COCOA
+
+QHash<qint64, QCocoaTouch*> QCocoaTouch::_currentTouches;
+QPointF QCocoaTouch::_screenReferencePos;
+QPointF QCocoaTouch::_trackpadReferencePos;
+int QCocoaTouch::_idAssignmentCount = 0;
+int QCocoaTouch::_touchCount = 0;
+bool QCocoaTouch::_updateInternalStateOnly = true;
+
+QCocoaTouch::QCocoaTouch(NSTouch *nstouch)
+{
+ if (_currentTouches.size() == 0)
+ _idAssignmentCount = 0;
+
+ _touchPoint.setId(_idAssignmentCount++);
+ _touchPoint.setPressure(1.0);
+ _identity = qint64([nstouch identity]);
+ _currentTouches.insert(_identity, this);
+ updateTouchData(nstouch, NSTouchPhaseBegan);
+}
+
+QCocoaTouch::~QCocoaTouch()
+{
+ _currentTouches.remove(_identity);
+}
+
+void QCocoaTouch::updateTouchData(NSTouch *nstouch, NSTouchPhase phase)
+{
+ if (_touchCount == 1)
+ _touchPoint.setState(toTouchPointState(phase) | Qt::TouchPointPrimary);
+ else
+ _touchPoint.setState(toTouchPointState(phase));
+
+ // From the normalized position on the trackpad, calculate
+ // where on screen the touchpoint should be according to the
+ // reference position:
+ NSPoint npos = [nstouch normalizedPosition];
+ QPointF qnpos = QPointF(npos.x, 1 - npos.y);
+ _touchPoint.setNormalizedPos(qnpos);
+
+ if (_touchPoint.id() == 0 && phase == NSTouchPhaseBegan) {
+ _trackpadReferencePos = qnpos;
+ _screenReferencePos = QCursor::pos();
+ }
+
+ NSSize dsize = [nstouch deviceSize];
+ float ppiX = (qnpos.x() - _trackpadReferencePos.x()) * dsize.width;
+ float ppiY = (qnpos.y() - _trackpadReferencePos.y()) * dsize.height;
+ QPointF relativePos = _trackpadReferencePos - QPointF(ppiX, ppiY);
+ _touchPoint.setScreenPos(_screenReferencePos - relativePos);
+}
+
+QCocoaTouch *QCocoaTouch::findQCocoaTouch(NSTouch *nstouch)
+{
+ qint64 identity = qint64([nstouch identity]);
+ if (_currentTouches.contains(identity))
+ return _currentTouches.value(identity);
+ return 0;
+}
+
+Qt::TouchPointState QCocoaTouch::toTouchPointState(NSTouchPhase nsState)
+{
+ Qt::TouchPointState qtState = Qt::TouchPointReleased;
+ switch (nsState) {
+ case NSTouchPhaseBegan:
+ qtState = Qt::TouchPointPressed;
+ break;
+ case NSTouchPhaseMoved:
+ qtState = Qt::TouchPointMoved;
+ break;
+ case NSTouchPhaseStationary:
+ qtState = Qt::TouchPointStationary;
+ break;
+ case NSTouchPhaseEnded:
+ case NSTouchPhaseCancelled:
+ qtState = Qt::TouchPointReleased;
+ break;
+ default:
+ break;
+ }
+ return qtState;
+}
+
+QList<QTouchEvent::TouchPoint>
+QCocoaTouch::getCurrentTouchPointList(NSEvent *event, bool acceptSingleTouch)
+{
+ QMap<int, QTouchEvent::TouchPoint> touchPoints;
+ NSSet *ended = [event touchesMatchingPhase:NSTouchPhaseEnded | NSTouchPhaseCancelled inView:nil];
+ NSSet *active = [event
+ touchesMatchingPhase:NSTouchPhaseBegan | NSTouchPhaseMoved | NSTouchPhaseStationary
+ inView:nil];
+ _touchCount = [active count];
+
+ // First: remove touches that were ended by the user. If we are
+ // currently not accepting single touches, a corresponding 'begin'
+ // has never been send to the app for these events.
+ // So should therefore not send the following removes either.
+
+ for (int i=0; i<int([ended count]); ++i) {
+ NSTouch *touch = [[ended allObjects] objectAtIndex:i];
+ QCocoaTouch *qcocoaTouch = findQCocoaTouch(touch);
+ if (qcocoaTouch) {
+ qcocoaTouch->updateTouchData(touch, [touch phase]);
+ if (!_updateInternalStateOnly)
+ touchPoints.insert(qcocoaTouch->_touchPoint.id(), qcocoaTouch->_touchPoint);
+ delete qcocoaTouch;
+ }
+ }
+
+ bool wasUpdateInternalStateOnly = _updateInternalStateOnly;
+ _updateInternalStateOnly = !acceptSingleTouch && _touchCount < 2;
+
+ // Next: update, or create, existing touches.
+ // We always keep track of all touch points, even
+ // when not accepting single touches.
+
+ for (int i=0; i<int([active count]); ++i) {
+ NSTouch *touch = [[active allObjects] objectAtIndex:i];
+ QCocoaTouch *qcocoaTouch = findQCocoaTouch(touch);
+ if (!qcocoaTouch)
+ qcocoaTouch = new QCocoaTouch(touch);
+ else
+ qcocoaTouch->updateTouchData(touch, wasUpdateInternalStateOnly ? NSTouchPhaseBegan : [touch phase]);
+ if (!_updateInternalStateOnly)
+ touchPoints.insert(qcocoaTouch->_touchPoint.id(), qcocoaTouch->_touchPoint);
+ }
+
+ // Next: sadly, we need to check that our touch hash is in
+ // sync with cocoa. This is typically not the case after a system
+ // gesture happend (like a four-finger-swipe to show expose).
+
+ if (_touchCount != _currentTouches.size()) {
+ // Remove all instances, and basically start from scratch:
+ touchPoints.clear();
+ foreach (QCocoaTouch *qcocoaTouch, _currentTouches.values()) {
+ if (!_updateInternalStateOnly) {
+ qcocoaTouch->_touchPoint.setState(Qt::TouchPointReleased);
+ touchPoints.insert(qcocoaTouch->_touchPoint.id(), qcocoaTouch->_touchPoint);
+ }
+ delete qcocoaTouch;
+ }
+ _currentTouches.clear();
+ _updateInternalStateOnly = !acceptSingleTouch;
+ return touchPoints.values();
+ }
+
+ // Finally: If this call _started_ to reject single
+ // touches, we need to fake a relase for the remaining
+ // touch now (and refake a begin for it later, if needed).
+
+ if (_updateInternalStateOnly && !wasUpdateInternalStateOnly && !_currentTouches.isEmpty()) {
+ QCocoaTouch *qcocoaTouch = _currentTouches.values().first();
+ qcocoaTouch->_touchPoint.setState(Qt::TouchPointReleased);
+ touchPoints.insert(qcocoaTouch->_touchPoint.id(), qcocoaTouch->_touchPoint);
+ // Since this last touch also will end up beeing the first
+ // touch (if the user adds a second finger without lifting
+ // the first), we promote it to be the primary touch:
+ qcocoaTouch->_touchPoint.setId(0);
+ _idAssignmentCount = 1;
+ }
+
+ return touchPoints.values();
+}
+
+#endif
+
+QT_END_NAMESPACE
+
+#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
+
diff --git a/src/widgets/platforms/mac/qmultitouch_mac_p.h b/src/widgets/platforms/mac/qmultitouch_mac_p.h
new file mode 100644
index 0000000000..c69c49984a
--- /dev/null
+++ b/src/widgets/platforms/mac/qmultitouch_mac_p.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QMULTITOUCH_MAC_P_H
+#define QMULTITOUCH_MAC_P_H
+
+#ifdef QT_MAC_USE_COCOA
+#import <Cocoa/Cocoa.h>
+#endif
+
+#include <qevent.h>
+#include <qhash.h>
+#include <QtCore>
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
+
+QT_BEGIN_NAMESPACE
+
+#ifdef QT_MAC_USE_COCOA
+
+class QCocoaTouch
+{
+ public:
+ static QList<QTouchEvent::TouchPoint> getCurrentTouchPointList(NSEvent *event, bool acceptSingleTouch);
+ static void setMouseInDraggingState(bool inDraggingState);
+
+ private:
+ static QHash<qint64, QCocoaTouch*> _currentTouches;
+ static QPointF _screenReferencePos;
+ static QPointF _trackpadReferencePos;
+ static int _idAssignmentCount;
+ static int _touchCount;
+ static bool _updateInternalStateOnly;
+
+ QTouchEvent::TouchPoint _touchPoint;
+ qint64 _identity;
+
+ QCocoaTouch(NSTouch *nstouch);
+ ~QCocoaTouch();
+
+ void updateTouchData(NSTouch *nstouch, NSTouchPhase phase);
+ static QCocoaTouch *findQCocoaTouch(NSTouch *nstouch);
+ static Qt::TouchPointState toTouchPointState(NSTouchPhase nsState);
+};
+
+#endif // QT_MAC_USE_COCOA
+
+QT_END_NAMESPACE
+
+#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
+
+#endif // QMULTITOUCH_MAC_P_H
+
diff --git a/src/widgets/platforms/mac/qnsframeview_mac_p.h b/src/widgets/platforms/mac/qnsframeview_mac_p.h
new file mode 100644
index 0000000000..72245c1782
--- /dev/null
+++ b/src/widgets/platforms/mac/qnsframeview_mac_p.h
@@ -0,0 +1,154 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp, qcolor_x11.cpp, qfiledialog.cpp
+// and many other. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+//
+
+// Private AppKit class (dumped from classdump).
+
+#import <Cocoa/Cocoa.h>
+
+@interface NSFrameView : NSView
+{
+ unsigned int styleMask;
+ NSString *_title;
+ NSCell *titleCell;
+ NSButton *closeButton;
+ NSButton *zoomButton;
+ NSButton *minimizeButton;
+ char resizeByIncrement;
+ char frameNeedsDisplay;
+ unsigned char tabViewCount;
+ NSSize resizeParameter;
+ int shadowState;
+}
+
++ (void)initialize;
++ (void)initTitleCell:fp8 styleMask:(unsigned int)fp12;
++ (struct _NSRect)frameRectForContentRect:(struct _NSRect)fp8 styleMask:(unsigned int)fp24;
++ (struct _NSRect)contentRectForFrameRect:(struct _NSRect)fp8 styleMask:(unsigned int)fp24;
++ (struct _NSSize)minFrameSizeForMinContentSize:(struct _NSSize)fp8 styleMask:(unsigned int)fp16;
++ (struct _NSSize)minContentSizeForMinFrameSize:(struct _NSSize)fp8 styleMask:(unsigned int)fp16;
++ (float)minFrameWidthWithTitle:fp8 styleMask:(unsigned int)fp12;
++ (unsigned int)_validateStyleMask:(unsigned int)fp8;
+- initWithFrame:(struct _NSRect)fp8 styleMask:(unsigned int)fp24 owner:fp28;
+- initWithFrame:(struct _NSRect)fp8;
+- (void)dealloc;
+- (void)shapeWindow;
+- (void)tileAndSetWindowShape:(char)fp8;
+- (void)tile;
+- (void)drawRect:(struct _NSRect)fp8;
+- (void)_drawFrameRects:(struct _NSRect)fp8;
+- (void)drawFrame:(struct _NSRect)fp8;
+- (void)drawThemeContentFill:(struct _NSRect)fp8 inView:fp24;
+- (void)drawWindowBackgroundRect:(struct _NSRect)fp8;
+- (void)drawWindowBackgroundRegion:(void *)fp8;
+- (float)contentAlpha;
+- (void)_windowChangedKeyState;
+- (void)_updateButtonState;
+- (char)_isSheet;
+- (char)_isUtility;
+- (void)setShadowState:(int)fp8;
+- (int)shadowState;
+- (char)_canHaveToolbar;
+- (char)_toolbarIsInTransition;
+- (char)_toolbarIsShown;
+- (char)_toolbarIsHidden;
+- (void)_showToolbarWithAnimation:(char)fp8;
+- (void)_hideToolbarWithAnimation:(char)fp8;
+- (float)_distanceFromToolbarBaseToTitlebar;
+- (int)_shadowType;
+- (unsigned int)_shadowFlags;
+- (void)_setShadowParameters;
+- (void)_drawFrameShadowAndFlushContext:fp8;
+- (void)setUpGState;
+- (void)adjustHalftonePhase;
+- (void)systemColorsDidChange:fp8;
+- frameColor;
+- contentFill;
+- (void)tabViewAdded;
+- (void)tabViewRemoved;
+- title;
+- (void)setTitle:fp8;
+- titleCell;
+- (void)initTitleCell:fp8;
+- (void)setResizeIncrements:(struct _NSSize)fp8;
+- (struct _NSSize)resizeIncrements;
+- (void)setAspectRatio:(struct _NSSize)fp8;
+- (struct _NSSize)aspectRatio;
+- (unsigned int)styleMask;
+- representedFilename;
+- (void)setRepresentedFilename:fp8;
+- (void)setDocumentEdited:(char)fp8;
+- (void)_setFrameNeedsDisplay:(char)fp8;
+- (char)frameNeedsDisplay;
+- titleFont;
+- (struct _NSRect)_maxTitlebarTitleRect;
+- (struct _NSRect)titlebarRect;
+- (void)_setUtilityWindow:(char)fp8;
+- (void)_setNonactivatingPanel:(char)fp8;
+- (void)setIsClosable:(char)fp8;
+- (void)setIsResizable:(char)fp8;
+- closeButton;
+- minimizeButton;
+- zoomButton;
+- (struct _NSSize)miniaturizedSize;
+- (void)_clearDragMargins;
+- (void)_resetDragMargins;
+- (void)setTitle:fp8 andDefeatWrap:(char)fp12;
+- (struct _NSRect)frameRectForContentRect:(struct _NSRect)fp8 styleMask:(unsigned int)fp24;
+- (struct _NSRect)contentRectForFrameRect:(struct _NSRect)fp8 styleMask:(unsigned int)fp24;
+- (struct _NSSize)minFrameSizeForMinContentSize:(struct _NSSize)fp8 styleMask:(unsigned int)fp16;
+- (struct _NSRect)dragRectForFrameRect:(struct _NSRect)fp8;
+- (struct _NSRect)contentRect;
+- (struct _NSSize)minFrameSize;
+- (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(struct _NSRect)fp8 isVisibleRect:(char)fp24 rectIsVisibleRectForView:fp28 topView:(char)fp32;
+
+@end
diff --git a/src/widgets/platforms/mac/qnsthemeframe_mac_p.h b/src/widgets/platforms/mac/qnsthemeframe_mac_p.h
new file mode 100644
index 0000000000..a1fa4ef1d8
--- /dev/null
+++ b/src/widgets/platforms/mac/qnsthemeframe_mac_p.h
@@ -0,0 +1,246 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp, qcolor_x11.cpp, qfiledialog.cpp
+// and many other. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+//
+
+// Private AppKit class (dumped from classdump).
+
+#import <Cocoa/Cocoa.h>
+#import "qnstitledframe_mac_p.h"
+
+@interface NSThemeFrame : NSTitledFrame
+{
+ NSButton *toolbarButton;
+ int toolbarVisibleStatus;
+ NSImage *showToolbarTransitionImage;
+ NSSize showToolbarPreWindowSize;
+ NSButton *modeButton;
+ int leftGroupTrackingTagNum;
+ int rightGroupTrackingTagNum;
+ char mouseInsideLeftGroup;
+ char mouseInsideRightGroup;
+ int widgetState;
+ NSString *displayName;
+}
+
++ (void)initialize;
++ (float)_windowBorderThickness:(unsigned int)fp8;
++ (float)_minXWindowBorderWidth:(unsigned int)fp8;
++ (float)_maxXWindowBorderWidth:(unsigned int)fp8;
++ (float)_minYWindowBorderHeight:(unsigned int)fp8;
++ (float)_windowTitlebarButtonSpacingWidth:(unsigned int)fp8;
++ (float)_windowFileButtonSpacingWidth:(unsigned int)fp8;
++ (float)_minXTitlebarWidgetInset:(unsigned int)fp8;
++ (float)_maxXTitlebarWidgetInset:(unsigned int)fp8;
++ (float)minFrameWidthWithTitle:fp8 styleMask:(unsigned int)fp12;
++ (float)_windowSideTitlebarTitleMinWidth:(unsigned int)fp8;
++ (float)_windowTitlebarTitleMinHeight:(unsigned int)fp8;
++ (float)_sideTitlebarWidth:(unsigned int)fp8;
++ (float)_titlebarHeight:(unsigned int)fp8;
++ (float)_resizeHeight:(unsigned int)fp8;
++ (char)_resizeFromEdge;
++ (struct _NSSize)sizeOfTitlebarButtons:(unsigned int)fp8;
++ (float)_contentToFrameMinXWidth:(unsigned int)fp8;
++ (float)_contentToFrameMaxXWidth:(unsigned int)fp8;
++ (float)_contentToFrameMinYHeight:(unsigned int)fp8;
++ (float)_contentToFrameMaxYHeight:(unsigned int)fp8;
++ (unsigned int)_validateStyleMask:(unsigned int)fp8;
+- (struct _NSSize)_topCornerSize;
+- (struct _NSSize)_bottomCornerSize;
+- (void *)_createWindowOpaqueShape;
+- (void)shapeWindow;
+- (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)fp8 isVisibleRect:(char)fp24 rectIsVisibleRectForView:fp28 topView:(char)fp32;
+- (void *)_regionForOpaqueDescendants:(NSRect)fp8 forMove:(char)fp24;
+- (void)_drawFrameInterior:(NSRect *)fp8 clip:(NSRect)fp12;
+- (void)_setTextShadow:(char)fp8;
+- (void)_drawTitleBar:(NSRect)fp8;
+- (void)_drawResizeIndicators:(NSRect)fp8;
+- (void)_drawFrameRects:(NSRect)fp8;
+- (void)drawFrame:(NSRect)fp8;
+- contentFill;
+- (void)viewDidEndLiveResize;
+- (float)contentAlpha;
+- (void)setThemeFrameWidgetState:(int)fp8;
+- (char)constrainResizeEdge:(int *)fp8 withDelta:(struct _NSSize)fp12 elapsedTime:(float)fp20;
+- (void)addFileButton:fp8;
+- (void)_updateButtons;
+- (void)_updateButtonState;
+- newCloseButton;
+- newZoomButton;
+- newMiniaturizeButton;
+- newToolbarButton;
+- newFileButton;
+- (void)_resetTitleBarButtons;
+- (void)setDocumentEdited:(char)fp8;
+- toolbarButton;
+- modeButton;
+- initWithFrame:(NSRect)fp8 styleMask:(unsigned int)fp24 owner:fp28;
+- (void)dealloc;
+- (void)setFrameSize:(struct _NSSize)fp8;
+- (char)_canHaveToolbar;
+- (char)_toolbarIsInTransition;
+- (char)_toolbarIsShown;
+- (char)_toolbarIsHidden;
+- _toolbarView;
+- _toolbar;
+- (float)_distanceFromToolbarBaseToTitlebar;
+- (unsigned int)_shadowFlags;
+- (NSRect)frameRectForContentRect:(NSRect)fp8 styleMask:(unsigned int)fp24;
+- (NSRect)contentRectForFrameRect:(NSRect)fp8 styleMask:(unsigned int)fp24;
+- (struct _NSSize)minFrameSizeForMinContentSize:(struct _NSSize)fp8 styleMask:(unsigned int)fp16;
+- (NSRect)contentRect;
+- (NSRect)_contentRectExcludingToolbar;
+- (NSRect)_contentRectIncludingToolbarAtHome;
+- (void)_setToolbarShowHideResizeWeightingOptimizationOn:(char)fp8;
+- (char)_usingToolbarShowHideWeightingOptimization;
+- (void)handleSetFrameCommonRedisplay;
+- (void)_startLiveResizeAsTopLevel;
+- (void)_endLiveResizeAsTopLevel;
+- (void)_growContentReshapeContentAndToolbarView:(int)fp8 animate:(char)fp12;
+- (char)_growWindowReshapeContentAndToolbarView:(int)fp8 animate:(char)fp12;
+- (void)_reshapeContentAndToolbarView:(int)fp8 resizeWindow:(char)fp12 animate:(char)fp16;
+- (void)_toolbarFrameSizeChanged:fp8 oldSize:(struct _NSSize)fp12;
+- (void)_syncToolbarPosition;
+- (void)_showHideToolbar:(int)fp8 resizeWindow:(char)fp12 animate:(char)fp16;
+- (void)_showToolbarWithAnimation:(char)fp8;
+- (void)_hideToolbarWithAnimation:(char)fp8;
+- (void)_drawToolbarTransitionIfNecessary;
+- (void)drawRect:(NSRect)fp8;
+- (void)resetCursorRects;
+- (char)shouldBeTreatedAsInkEvent:fp8;
+- (char)_shouldBeTreatedAsInkEventInInactiveWindow:fp8;
+//- hitTest:(struct _NSPoint)fp8; // collides with hittest in qcocoasharedwindowmethods_mac_p.h
+- (NSRect)_leftGroupRect;
+- (NSRect)_rightGroupRect;
+- (void)_updateWidgets;
+- (void)_updateMouseTracking;
+- (void)mouseEntered:fp8;
+- (void)mouseExited:fp8;
+- (void)_setMouseEnteredGroup:(char)fp8 entered:(char)fp12;
+- (char)_mouseInGroup:fp8;
+- (struct _NSSize)miniaturizedSize;
+- (float)_minXTitlebarDecorationMinWidth;
+- (float)_maxXTitlebarDecorationMinWidth;
+- (struct _NSSize)minFrameSize;
+- (float)_windowBorderThickness;
+- (float)_windowTitlebarXResizeBorderThickness;
+- (float)_windowTitlebarYResizeBorderThickness;
+- (float)_windowResizeBorderThickness;
+- (float)_minXWindowBorderWidth;
+- (float)_maxXWindowBorderWidth;
+- (float)_minYWindowBorderHeight;
+- (float)_maxYWindowBorderHeight;
+- (float)_minYTitlebarButtonsOffset;
+- (float)_minYTitlebarTitleOffset;
+- (float)_sideTitlebarWidth;
+- (float)_titlebarHeight;
+- (NSRect)_titlebarTitleRect;
+- (NSRect)titlebarRect;
+- (float)_windowTitlebarTitleMinHeight;
+- (struct _NSSize)_sizeOfTitlebarFileButton;
+- (struct _NSSize)sizeOfTitlebarToolbarButton;
+- (float)_windowTitlebarButtonSpacingWidth;
+- (float)_windowFileButtonSpacingWidth;
+- (float)_minXTitlebarWidgetInset;
+- (float)_maxXTitlebarWidgetInset;
+- (float)_minXTitlebarButtonsWidth;
+- (float)_maxXTitlebarButtonsWidth;
+- (struct _NSPoint)_closeButtonOrigin;
+- (struct _NSPoint)_zoomButtonOrigin;
+- (struct _NSPoint)_collapseButtonOrigin;
+- (struct _NSPoint)_toolbarButtonOrigin;
+- (struct _NSPoint)_fileButtonOrigin;
+- (void)_tileTitlebar;
+- (NSRect)_commandPopupRect;
+- (void)_resetDragMargins;
+- (float)_maxYTitlebarDragHeight;
+- (float)_minXTitlebarDragWidth;
+- (float)_maxXTitlebarDragWidth;
+- (float)_contentToFrameMinXWidth;
+- (float)_contentToFrameMaxXWidth;
+- (float)_contentToFrameMinYHeight;
+- (float)_contentToFrameMaxYHeight;
+- (float)_windowResizeCornerThickness;
+- (NSRect)_minYResizeRect;
+- (NSRect)_minYminXResizeRect;
+- (NSRect)_minYmaxXResizeRect;
+- (NSRect)_minXResizeRect;
+- (NSRect)_minXminYResizeRect;
+- (NSRect)_minXmaxYResizeRect;
+- (NSRect)_maxYResizeRect;
+- (NSRect)_maxYminXResizeRect;
+- (NSRect)_maxYmaxXResizeRect;
+- (NSRect)_maxXResizeRect;
+- (NSRect)_maxXminYResizeRect;
+- (NSRect)_maxXmaxYResizeRect;
+- (NSRect)_minXTitlebarResizeRect;
+- (NSRect)_maxXTitlebarResizeRect;
+- (NSRect)_minXBorderRect;
+- (NSRect)_maxXBorderRect;
+- (NSRect)_maxYBorderRect;
+- (NSRect)_minYBorderRect;
+- (void)_setUtilityWindow:(char)fp8;
+- (char)_isUtility;
+- (float)_sheetHeightAdjustment;
+- (void)_setSheet:(char)fp8;
+- (char)_isSheet;
+- (char)_isResizable;
+- (char)_isClosable;
+- (char)_isMiniaturizable;
+- (char)_hasToolbar;
+- (NSRect)_growBoxRect;
+- (void)_drawGrowBoxWithClip:(NSRect)fp8;
+- (char)_inactiveButtonsNeedMask;
+- (void)mouseDown:fp8;
+- _displayName;
+- (void)_setDisplayName:fp8;
+
+@end
diff --git a/src/widgets/platforms/mac/qnstitledframe_mac_p.h b/src/widgets/platforms/mac/qnstitledframe_mac_p.h
new file mode 100644
index 0000000000..18a6155925
--- /dev/null
+++ b/src/widgets/platforms/mac/qnstitledframe_mac_p.h
@@ -0,0 +1,205 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp, qcolor_x11.cpp, qfiledialog.cpp
+// and many other. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+//
+
+// Private AppKit class (dumped from classdump).
+
+#import <Cocoa/Cocoa.h>
+#import "qnsframeview_mac_p.h"
+
+
+@interface NSTitledFrame : NSFrameView
+{
+ int resizeFlags;
+ id fileButton; /* NSDocumentDragButton* */
+ NSSize titleCellSize;
+}
+
++ (float)_windowBorderThickness:(unsigned int)fp8;
++ (float)_minXWindowBorderWidth:(unsigned int)fp8;
++ (float)_maxXWindowBorderWidth:(unsigned int)fp8;
++ (float)_minYWindowBorderHeight:(unsigned int)fp8;
++ (char)_resizeFromEdge;
++ (NSRect)frameRectForContentRect:(NSRect)fp8 styleMask:(unsigned int)fp24;
++ (NSRect)contentRectForFrameRect:(NSRect)fp8 styleMask:(unsigned int)fp24;
++ (struct _NSSize)minFrameSizeForMinContentSize:(struct _NSSize)fp8 styleMask:(unsigned int)fp16;
++ (struct _NSSize)minContentSizeForMinFrameSize:(struct _NSSize)fp8 styleMask:(unsigned int)fp16;
++ (float)minFrameWidthWithTitle:fp8 styleMask:(unsigned int)fp12;
++ (struct _NSSize)_titleCellSizeForTitle:fp8 styleMask:(unsigned int)fp12;
++ (float)_titleCellHeight:(unsigned int)fp8;
++ (float)_windowTitlebarTitleMinHeight:(unsigned int)fp8;
++ (float)_titlebarHeight:(unsigned int)fp8;
++ (struct _NSSize)sizeOfTitlebarButtons:(unsigned int)fp8;
++ (float)windowTitlebarLinesSpacingWidth:(unsigned int)fp8;
++ (float)windowTitlebarTitleLinesSpacingWidth:(unsigned int)fp8;
++ (float)_contentToFrameMinXWidth:(unsigned int)fp8;
++ (float)_contentToFrameMaxXWidth:(unsigned int)fp8;
++ (float)_contentToFrameMinYHeight:(unsigned int)fp8;
++ (float)_contentToFrameMaxYHeight:(unsigned int)fp8;
+- initWithFrame:(NSRect)fp8 styleMask:(unsigned int)fp24 owner:fp28;
+- (void)dealloc;
+- (void)setIsClosable:(char)fp8;
+- (void)setIsResizable:(char)fp8;
+- (void)_resetTitleFont;
+- (void)_setUtilityWindow:(char)fp8;
+- (char)isOpaque;
+- (char)worksWhenModal;
+- (void)propagateFrameDirtyRects:(NSRect)fp8;
+- (void)_showDrawRect:(NSRect)fp8;
+- (void)_drawFrameInterior:(NSRect *)fp8 clip:(NSRect)fp12;
+- (void)drawFrame:(NSRect)fp8;
+- (void)_drawFrameRects:(NSRect)fp8;
+- (void)_drawTitlebar:(NSRect)fp8;
+- (void)_drawTitlebarPattern:(int)fp8 inRect:(NSRect)fp12 clippedByRect:(NSRect)fp28 forKey:(char)fp44 alignment:(int)fp48;
+- (void)_drawTitlebarLines:(int)fp8 inRect:(NSRect)fp12 clippedByRect:(NSRect)fp28;
+- frameHighlightColor;
+- frameShadowColor;
+- (void)setFrameSize:(struct _NSSize)fp8;
+- (void)setFrameOrigin:(struct _NSPoint)fp8;
+- (void)tileAndSetWindowShape:(char)fp8;
+- (void)tile;
+- (void)_tileTitlebar;
+- (void)setTitle:fp8;
+- (char)_shouldRepresentFilename;
+- (void)setRepresentedFilename:fp8;
+- (void)_drawTitleStringIn:(NSRect)fp8 withColor:fp24;
+- titleFont;
+- (void)_drawResizeIndicators:(NSRect)fp8;
+- titleButtonOfClass:(Class)fp8;
+- initTitleButton:fp8;
+- newCloseButton;
+- newZoomButton;
+- newMiniaturizeButton;
+- newFileButton;
+- fileButton;
+- (void)_removeButtons;
+- (void)_updateButtons;
+- (char)_eventInTitlebar:fp8;
+- (char)acceptsFirstMouse:fp8;
+- (void)mouseDown:fp8;
+- (void)mouseUp:fp8;
+- (void)rightMouseDown:fp8;
+- (void)rightMouseUp:fp8;
+- (int)resizeEdgeForEvent:fp8;
+- (struct _NSSize)_resizeDeltaFromPoint:(struct _NSPoint)fp8 toEvent:fp16;
+- (NSRect)_validFrameForResizeFrame:(NSRect)fp8 fromResizeEdge:(int)fp24;
+- (NSRect)frame:(NSRect)fp8 resizedFromEdge:(int)fp24 withDelta:(struct _NSSize)fp28;
+- (char)constrainResizeEdge:(int *)fp8 withDelta:(struct _NSSize)fp12 elapsedTime:(float)fp20;
+- (void)resizeWithEvent:fp8;
+- (int)resizeFlags;
+- (void)resetCursorRects;
+- (void)setDocumentEdited:(char)fp8;
+- (struct _NSSize)miniaturizedSize;
+- (struct _NSSize)minFrameSize;
+- (float)_windowBorderThickness;
+- (float)_windowTitlebarXResizeBorderThickness;
+- (float)_windowTitlebarYResizeBorderThickness;
+- (float)_windowResizeBorderThickness;
+- (float)_minXWindowBorderWidth;
+- (float)_maxXWindowBorderWidth;
+- (float)_minYWindowBorderHeight;
+- (void)_invalidateTitleCellSize;
+- (void)_invalidateTitleCellWidth;
+- (float)_titleCellHeight;
+- (struct _NSSize)_titleCellSize;
+- (float)_titlebarHeight;
+- (NSRect)titlebarRect;
+- (NSRect)_maxTitlebarTitleRect;
+- (NSRect)_titlebarTitleRect;
+- (float)_windowTitlebarTitleMinHeight;
+- (NSRect)dragRectForFrameRect:(NSRect)fp8;
+- (struct _NSSize)sizeOfTitlebarButtons;
+- (struct _NSSize)_sizeOfTitlebarFileButton;
+- (float)_windowTitlebarButtonSpacingWidth;
+- (float)_minXTitlebarButtonsWidth;
+- (float)_maxXTitlebarButtonsWidth;
+- (int)_numberOfTitlebarLines;
+- (float)windowTitlebarLinesSpacingWidth;
+- (float)windowTitlebarTitleLinesSpacingWidth;
+- (float)_minLinesWidthWithSpace;
+- (NSRect)_minXTitlebarLinesRectWithTitleCellRect:(NSRect)fp8;
+- (NSRect)_maxXTitlebarLinesRectWithTitleCellRect:(NSRect)fp8;
+- (float)_minXTitlebarDecorationMinWidth;
+- (float)_maxXTitlebarDecorationMinWidth;
+- (struct _NSPoint)_closeButtonOrigin;
+- (struct _NSPoint)_zoomButtonOrigin;
+- (struct _NSPoint)_collapseButtonOrigin;
+- (struct _NSPoint)_fileButtonOrigin;
+- (float)_maxYTitlebarDragHeight;
+- (float)_minXTitlebarDragWidth;
+- (float)_maxXTitlebarDragWidth;
+- (float)_contentToFrameMinXWidth;
+- (float)_contentToFrameMaxXWidth;
+- (float)_contentToFrameMinYHeight;
+- (float)_contentToFrameMaxYHeight;
+- (NSRect)contentRect;
+- (float)_windowResizeCornerThickness;
+- (NSRect)_minYResizeRect;
+- (NSRect)_minYminXResizeRect;
+- (NSRect)_minYmaxXResizeRect;
+- (NSRect)_minXResizeRect;
+- (NSRect)_minXminYResizeRect;
+- (NSRect)_minXmaxYResizeRect;
+- (NSRect)_maxYResizeRect;
+- (NSRect)_maxYminXResizeRect;
+- (NSRect)_maxYmaxXResizeRect;
+- (NSRect)_maxXResizeRect;
+- (NSRect)_maxXminYResizeRect;
+- (NSRect)_maxXmaxYResizeRect;
+- (NSRect)_minXTitlebarResizeRect;
+- (NSRect)_maxXTitlebarResizeRect;
+- (NSRect)_minXBorderRect;
+- (NSRect)_maxXBorderRect;
+- (NSRect)_maxYBorderRect;
+- (NSRect)_minYBorderRect;
+
+@end
diff --git a/src/widgets/platforms/mac/qpaintdevice_mac.cpp b/src/widgets/platforms/mac/qpaintdevice_mac.cpp
new file mode 100644
index 0000000000..50bd4b8490
--- /dev/null
+++ b/src/widgets/platforms/mac/qpaintdevice_mac.cpp
@@ -0,0 +1,152 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpaintdevice.h"
+#include "qpainter.h"
+#include "qwidget.h"
+#include "qbitmap.h"
+#include "qapplication.h"
+#include "qprinter.h"
+#include <qdebug.h>
+#include <private/qt_mac_p.h>
+#include <private/qprintengine_mac_p.h>
+#include <private/qpixmap_mac_p.h>
+#include <private/qpixmap_raster_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*****************************************************************************
+ Internal variables and functions
+ *****************************************************************************/
+
+/*! \internal */
+float qt_mac_defaultDpi_x()
+{
+ // Mac OS X currently assumes things to be 72 dpi.
+ // (see http://developer.apple.com/releasenotes/GraphicsImaging/RN-ResolutionIndependentUI/)
+ // This may need to be re-worked as we go further in the resolution-independence stuff.
+ return 72;
+}
+
+/*! \internal */
+float qt_mac_defaultDpi_y()
+{
+ // Mac OS X currently assumes things to be 72 dpi.
+ // (see http://developer.apple.com/releasenotes/GraphicsImaging/RN-ResolutionIndependentUI/)
+ // This may need to be re-worked as we go further in the resolution-independence stuff.
+ return 72;
+}
+
+
+/*! \internal
+
+ Returns the QuickDraw CGrafPtr of the paint device. 0 is returned
+ if it can't be obtained. Do not hold the pointer around for long
+ as it can be relocated.
+
+ \warning This function is only available on Mac OS X.
+*/
+
+Q_WIDGETS_EXPORT GrafPtr qt_mac_qd_context(const QPaintDevice *device)
+{
+ if (device->devType() == QInternal::Pixmap) {
+ return static_cast<GrafPtr>(static_cast<const QPixmap *>(device)->macQDHandle());
+ } else if(device->devType() == QInternal::Widget) {
+ return static_cast<GrafPtr>(static_cast<const QWidget *>(device)->macQDHandle());
+ } else if(device->devType() == QInternal::Printer) {
+ QPaintEngine *engine = static_cast<const QPrinter *>(device)->paintEngine();
+ return static_cast<GrafPtr>(static_cast<const QMacPrintEngine *>(engine)->handle());
+ }
+ return 0;
+}
+
+extern CGColorSpaceRef qt_mac_colorSpaceForDeviceType(const QPaintDevice *pdev);
+
+/*! \internal
+
+ Returns the CoreGraphics CGContextRef of the paint device. 0 is
+ returned if it can't be obtained. It is the caller's responsiblity to
+ CGContextRelease the context when finished using it.
+
+ \warning This function is only available on Mac OS X.
+*/
+
+Q_WIDGETS_EXPORT CGContextRef qt_mac_cg_context(const QPaintDevice *pdev)
+{
+ if (pdev->devType() == QInternal::Pixmap) {
+ const QPixmap *pm = static_cast<const QPixmap*>(pdev);
+ CGColorSpaceRef colorspace = qt_mac_colorSpaceForDeviceType(pdev);
+ uint flags = kCGImageAlphaPremultipliedFirst;
+#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version
+ flags |= kCGBitmapByteOrder32Host;
+#endif
+ CGContextRef ret = 0;
+
+ // It would make sense to put this into a mac #ifdef'ed
+ // virtual function in the QPlatformPixmap at some point
+ if (pm->data->classId() == QPlatformPixmap::MacClass) {
+ const QMacPlatformPixmap *pmData = static_cast<const QMacPlatformPixmap*>(pm->data.data());
+ ret = CGBitmapContextCreate(pmData->pixels, pmData->w, pmData->h,
+ 8, pmData->bytesPerRow, colorspace,
+ flags);
+ if(!ret)
+ qWarning("QPaintDevice: Unable to create context for pixmap (%d/%d/%d)",
+ pmData->w, pmData->h, (pmData->bytesPerRow * pmData->h));
+ } else if (pm->data->classId() == QPlatformPixmap::RasterClass) {
+ QImage *image = pm->data->buffer();
+ ret = CGBitmapContextCreate(image->bits(), image->width(), image->height(),
+ 8, image->bytesPerLine(), colorspace, flags);
+ }
+
+ CGContextTranslateCTM(ret, 0, pm->height());
+ CGContextScaleCTM(ret, 1, -1);
+ return ret;
+ } else if (pdev->devType() == QInternal::Widget) {
+ CGContextRef ret = static_cast<CGContextRef>(static_cast<const QWidget *>(pdev)->macCGHandle());
+ CGContextRetain(ret);
+ return ret;
+ } else if (pdev->devType() == QInternal::MacQuartz) {
+ return static_cast<const QMacQuartzPaintDevice *>(pdev)->cgContext();
+ }
+ return 0;
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/mac/qpaintengine_mac.cpp b/src/widgets/platforms/mac/qpaintengine_mac.cpp
new file mode 100644
index 0000000000..3def016b56
--- /dev/null
+++ b/src/widgets/platforms/mac/qpaintengine_mac.cpp
@@ -0,0 +1,1647 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qbitmap.h>
+#include <qpaintdevice.h>
+#include <private/qpaintengine_mac_p.h>
+#include <qpainterpath.h>
+#include <qpixmapcache.h>
+#include <private/qpaintengine_raster_p.h>
+#include <private/qprintengine_mac_p.h>
+#include <qprinter.h>
+#include <qstack.h>
+#include <qtextcodec.h>
+#include <qwidget.h>
+#include <qvarlengtharray.h>
+#include <qdebug.h>
+#include <qcoreapplication.h>
+#include <qmath.h>
+
+#include <private/qfont_p.h>
+#include <private/qfontengine_p.h>
+#include <private/qfontengine_coretext_p.h>
+#include <private/qfontengine_mac_p.h>
+#include <private/qnumeric_p.h>
+#include <private/qpainter_p.h>
+#include <private/qpainterpath_p.h>
+#include <private/qpixmap_mac_p.h>
+#include <private/qt_mac_p.h>
+#include <private/qtextengine_p.h>
+#include <private/qwidget_p.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+
+#include <string.h>
+
+QT_BEGIN_NAMESPACE
+
+extern int qt_antialiasing_threshold; // QApplication.cpp
+
+/*****************************************************************************
+ External functions
+ *****************************************************************************/
+extern CGImageRef qt_mac_create_imagemask(const QPixmap &px, const QRectF &sr); //qpixmap_mac.cpp
+extern QPoint qt_mac_posInWindow(const QWidget *w); //qwidget_mac.cpp
+extern OSWindowRef qt_mac_window_for(const QWidget *); //qwidget_mac.cpp
+extern CGContextRef qt_mac_cg_context(const QPaintDevice *); //qpaintdevice_mac.cpp
+extern void qt_mac_dispose_rgn(RgnHandle r); //qregion_mac.cpp
+extern QPixmap qt_pixmapForBrush(int, bool); //qbrush.cpp
+
+void qt_mac_clip_cg(CGContextRef hd, const QRegion &rgn, CGAffineTransform *orig_xform);
+
+
+//Implemented for qt_mac_p.h
+QMacCGContext::QMacCGContext(QPainter *p)
+{
+ QPaintEngine *pe = p->paintEngine();
+ if (pe->type() == QPaintEngine::MacPrinter)
+ pe = static_cast<QMacPrintEngine*>(pe)->paintEngine();
+ pe->syncState();
+ context = 0;
+ if(pe->type() == QPaintEngine::CoreGraphics)
+ context = static_cast<QCoreGraphicsPaintEngine*>(pe)->handle();
+
+ int devType = p->device()->devType();
+ if (pe->type() == QPaintEngine::Raster
+ && (devType == QInternal::Widget ||
+ devType == QInternal::Pixmap ||
+ devType == QInternal::Image)) {
+
+ extern CGColorSpaceRef qt_mac_colorSpaceForDeviceType(const QPaintDevice *paintDevice);
+ CGColorSpaceRef colorspace = qt_mac_colorSpaceForDeviceType(pe->paintDevice());
+ uint flags = kCGImageAlphaPremultipliedFirst;
+#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version
+ flags |= kCGBitmapByteOrder32Host;
+#endif
+ const QImage *image = (const QImage *) pe->paintDevice();
+
+ context = CGBitmapContextCreate((void *) image->bits(), image->width(), image->height(),
+ 8, image->bytesPerLine(), colorspace, flags);
+
+ CGContextTranslateCTM(context, 0, image->height());
+ CGContextScaleCTM(context, 1, -1);
+
+ if (devType == QInternal::Widget) {
+ QRegion clip = p->paintEngine()->systemClip();
+ QTransform native = p->deviceTransform();
+ QTransform logical = p->combinedTransform();
+
+ if (p->hasClipping()) {
+ QRegion r = p->clipRegion();
+ r.translate(native.dx(), native.dy());
+ if (clip.isEmpty())
+ clip = r;
+ else
+ clip &= r;
+ }
+ qt_mac_clip_cg(context, clip, 0);
+
+ CGContextTranslateCTM(context, native.dx(), native.dy());
+ }
+ } else {
+ CGContextRetain(context);
+ }
+}
+
+
+/*****************************************************************************
+ QCoreGraphicsPaintEngine utility functions
+ *****************************************************************************/
+
+//conversion
+inline static float qt_mac_convert_color_to_cg(int c) { return ((float)c * 1000 / 255) / 1000; }
+inline static int qt_mac_convert_color_from_cg(float c) { return qRound(c * 255); }
+CGAffineTransform qt_mac_convert_transform_to_cg(const QTransform &t) {
+ return CGAffineTransformMake(t.m11(), t.m12(), t.m21(), t.m22(), t.dx(), t.dy());
+}
+
+CGColorSpaceRef qt_mac_colorSpaceForDeviceType(const QPaintDevice *paintDevice)
+{
+ bool isWidget = (paintDevice->devType() == QInternal::Widget);
+ return QCoreGraphicsPaintEngine::macDisplayColorSpace(isWidget ? static_cast<const QWidget *>(paintDevice)
+ : 0);
+}
+
+inline static QCFType<CGColorRef> cgColorForQColor(const QColor &col, QPaintDevice *pdev)
+{
+ CGFloat components[] = {
+ qt_mac_convert_color_to_cg(col.red()),
+ qt_mac_convert_color_to_cg(col.green()),
+ qt_mac_convert_color_to_cg(col.blue()),
+ qt_mac_convert_color_to_cg(col.alpha())
+ };
+ return CGColorCreate(qt_mac_colorSpaceForDeviceType(pdev), components);
+}
+
+// There's architectural problems with using native gradients
+// on the Mac at the moment, so disable them.
+// #define QT_MAC_USE_NATIVE_GRADIENTS
+
+#ifdef QT_MAC_USE_NATIVE_GRADIENTS
+static bool drawGradientNatively(const QGradient *gradient)
+{
+ return gradient->spread() == QGradient::PadSpread;
+}
+
+// gradiant callback
+static void qt_mac_color_gradient_function(void *info, const CGFloat *in, CGFloat *out)
+{
+ QBrush *brush = static_cast<QBrush *>(info);
+ Q_ASSERT(brush && brush->gradient());
+
+ const QGradientStops stops = brush->gradient()->stops();
+ const int n = stops.count();
+ Q_ASSERT(n >= 1);
+ const QGradientStop *begin = stops.constBegin();
+ const QGradientStop *end = begin + n;
+
+ qreal p = in[0];
+ const QGradientStop *i = begin;
+ while (i != end && i->first < p)
+ ++i;
+
+ QRgb c;
+ if (i == begin) {
+ c = begin->second.rgba();
+ } else if (i == end) {
+ c = (end - 1)->second.rgba();
+ } else {
+ const QGradientStop &s1 = *(i - 1);
+ const QGradientStop &s2 = *i;
+ qreal p1 = s1.first;
+ qreal p2 = s2.first;
+ QRgb c1 = s1.second.rgba();
+ QRgb c2 = s2.second.rgba();
+ int idist = 256 * (p - p1) / (p2 - p1);
+ int dist = 256 - idist;
+ c = qRgba(INTERPOLATE_PIXEL_256(qRed(c1), dist, qRed(c2), idist),
+ INTERPOLATE_PIXEL_256(qGreen(c1), dist, qGreen(c2), idist),
+ INTERPOLATE_PIXEL_256(qBlue(c1), dist, qBlue(c2), idist),
+ INTERPOLATE_PIXEL_256(qAlpha(c1), dist, qAlpha(c2), idist));
+ }
+
+ out[0] = qt_mac_convert_color_to_cg(qRed(c));
+ out[1] = qt_mac_convert_color_to_cg(qGreen(c));
+ out[2] = qt_mac_convert_color_to_cg(qBlue(c));
+ out[3] = qt_mac_convert_color_to_cg(qAlpha(c));
+}
+#endif
+
+//clipping handling
+void QCoreGraphicsPaintEnginePrivate::resetClip()
+{
+ static bool inReset = false;
+ if (inReset)
+ return;
+ inReset = true;
+
+ CGAffineTransform old_xform = CGContextGetCTM(hd);
+
+ //setup xforms
+ CGContextConcatCTM(hd, CGAffineTransformInvert(old_xform));
+ while (stackCount > 0) {
+ restoreGraphicsState();
+ }
+ saveGraphicsState();
+ inReset = false;
+ //reset xforms
+ CGContextConcatCTM(hd, CGAffineTransformInvert(CGContextGetCTM(hd)));
+ CGContextConcatCTM(hd, old_xform);
+}
+
+static CGRect qt_mac_compose_rect(const QRectF &r, float off=0)
+{
+ return CGRectMake(r.x()+off, r.y()+off, r.width(), r.height());
+}
+
+static CGMutablePathRef qt_mac_compose_path(const QPainterPath &p, float off=0)
+{
+ CGMutablePathRef ret = CGPathCreateMutable();
+ QPointF startPt;
+ for (int i=0; i<p.elementCount(); ++i) {
+ const QPainterPath::Element &elm = p.elementAt(i);
+ switch (elm.type) {
+ case QPainterPath::MoveToElement:
+ if(i > 0
+ && p.elementAt(i - 1).x == startPt.x()
+ && p.elementAt(i - 1).y == startPt.y())
+ CGPathCloseSubpath(ret);
+ startPt = QPointF(elm.x, elm.y);
+ CGPathMoveToPoint(ret, 0, elm.x+off, elm.y+off);
+ break;
+ case QPainterPath::LineToElement:
+ CGPathAddLineToPoint(ret, 0, elm.x+off, elm.y+off);
+ break;
+ case QPainterPath::CurveToElement:
+ Q_ASSERT(p.elementAt(i+1).type == QPainterPath::CurveToDataElement);
+ Q_ASSERT(p.elementAt(i+2).type == QPainterPath::CurveToDataElement);
+ CGPathAddCurveToPoint(ret, 0,
+ elm.x+off, elm.y+off,
+ p.elementAt(i+1).x+off, p.elementAt(i+1).y+off,
+ p.elementAt(i+2).x+off, p.elementAt(i+2).y+off);
+ i+=2;
+ break;
+ default:
+ qFatal("QCoreGraphicsPaintEngine::drawPath(), unhandled type: %d", elm.type);
+ break;
+ }
+ }
+ if(!p.isEmpty()
+ && p.elementAt(p.elementCount() - 1).x == startPt.x()
+ && p.elementAt(p.elementCount() - 1).y == startPt.y())
+ CGPathCloseSubpath(ret);
+ return ret;
+}
+
+CGColorSpaceRef QCoreGraphicsPaintEngine::m_genericColorSpace = 0;
+QHash<CGDirectDisplayID, CGColorSpaceRef> QCoreGraphicsPaintEngine::m_displayColorSpaceHash;
+bool QCoreGraphicsPaintEngine::m_postRoutineRegistered = false;
+
+CGColorSpaceRef QCoreGraphicsPaintEngine::macGenericColorSpace()
+{
+#if 0
+ if (!m_genericColorSpace) {
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
+ m_genericColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
+ } else
+#endif
+ {
+ m_genericColorSpace = CGColorSpaceCreateDeviceRGB();
+ }
+ if (!m_postRoutineRegistered) {
+ m_postRoutineRegistered = true;
+ qAddPostRoutine(QCoreGraphicsPaintEngine::cleanUpMacColorSpaces);
+ }
+ }
+ return m_genericColorSpace;
+#else
+ // Just return the main display colorspace for the moment.
+ return macDisplayColorSpace();
+#endif
+}
+void qt_mac_clip_cg(CGContextRef hd, const QRegion &rgn, CGAffineTransform *orig_xform)
+{
+ CGAffineTransform old_xform = CGAffineTransformIdentity;
+ if(orig_xform) { //setup xforms
+ old_xform = CGContextGetCTM(hd);
+ CGContextConcatCTM(hd, CGAffineTransformInvert(old_xform));
+ CGContextConcatCTM(hd, *orig_xform);
+ }
+
+ //do the clipping
+ CGContextBeginPath(hd);
+ if(rgn.isEmpty()) {
+ CGContextAddRect(hd, CGRectMake(0, 0, 0, 0));
+ } else {
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
+ QCFType<HIMutableShapeRef> shape = rgn.toHIMutableShape();
+ Q_ASSERT(!HIShapeIsEmpty(shape));
+ HIShapeReplacePathInCGContext(shape, hd);
+ } else
+#endif
+ {
+ QVector<QRect> rects = rgn.rects();
+ const int count = rects.size();
+ for(int i = 0; i < count; i++) {
+ const QRect &r = rects[i];
+ CGRect mac_r = CGRectMake(r.x(), r.y(), r.width(), r.height());
+ CGContextAddRect(hd, mac_r);
+ }
+ }
+
+ }
+ CGContextClip(hd);
+
+ if(orig_xform) {//reset xforms
+ CGContextConcatCTM(hd, CGAffineTransformInvert(CGContextGetCTM(hd)));
+ CGContextConcatCTM(hd, old_xform);
+ }
+}
+
+
+//pattern handling (tiling)
+#if 1
+# define QMACPATTERN_MASK_MULTIPLIER 32
+#else
+# define QMACPATTERN_MASK_MULTIPLIER 1
+#endif
+class QMacPattern
+{
+public:
+ QMacPattern() : as_mask(false), pdev(0), image(0) { data.bytes = 0; }
+ ~QMacPattern() { CGImageRelease(image); }
+ int width() {
+ if(image)
+ return CGImageGetWidth(image);
+ if(data.bytes)
+ return 8*QMACPATTERN_MASK_MULTIPLIER;
+ return data.pixmap.width();
+ }
+ int height() {
+ if(image)
+ return CGImageGetHeight(image);
+ if(data.bytes)
+ return 8*QMACPATTERN_MASK_MULTIPLIER;
+ return data.pixmap.height();
+ }
+
+ //input
+ QColor foreground;
+ bool as_mask;
+ struct {
+ QPixmap pixmap;
+ const uchar *bytes;
+ } data;
+ QPaintDevice *pdev;
+ //output
+ CGImageRef image;
+};
+static void qt_mac_draw_pattern(void *info, CGContextRef c)
+{
+ QMacPattern *pat = (QMacPattern*)info;
+ int w = 0, h = 0;
+ bool isBitmap = (pat->data.pixmap.depth() == 1);
+ if(!pat->image) { //lazy cache
+ if(pat->as_mask) {
+ Q_ASSERT(pat->data.bytes);
+ w = h = 8;
+#if (QMACPATTERN_MASK_MULTIPLIER == 1)
+ CGDataProviderRef provider = CGDataProviderCreateWithData(0, pat->data.bytes, w*h, 0);
+ pat->image = CGImageMaskCreate(w, h, 1, 1, 1, provider, 0, false);
+ CGDataProviderRelease(provider);
+#else
+ const int numBytes = (w*h)/sizeof(uchar);
+ uchar xor_bytes[numBytes];
+ for(int i = 0; i < numBytes; ++i)
+ xor_bytes[i] = pat->data.bytes[i] ^ 0xFF;
+ CGDataProviderRef provider = CGDataProviderCreateWithData(0, xor_bytes, w*h, 0);
+ CGImageRef swatch = CGImageMaskCreate(w, h, 1, 1, 1, provider, 0, false);
+ CGDataProviderRelease(provider);
+
+ const QColor c0(0, 0, 0, 0), c1(255, 255, 255, 255);
+ QPixmap pm(w*QMACPATTERN_MASK_MULTIPLIER, h*QMACPATTERN_MASK_MULTIPLIER);
+ pm.fill(c0);
+ CGContextRef pm_ctx = qt_mac_cg_context(&pm);
+ CGContextSetFillColorWithColor(c, cgColorForQColor(c1, pat->pdev));
+ CGRect rect = CGRectMake(0, 0, w, h);
+ for(int x = 0; x < QMACPATTERN_MASK_MULTIPLIER; ++x) {
+ rect.origin.x = x * w;
+ for(int y = 0; y < QMACPATTERN_MASK_MULTIPLIER; ++y) {
+ rect.origin.y = y * h;
+ qt_mac_drawCGImage(pm_ctx, &rect, swatch);
+ }
+ }
+ pat->image = qt_mac_create_imagemask(pm, pm.rect());
+ CGImageRelease(swatch);
+ CGContextRelease(pm_ctx);
+ w *= QMACPATTERN_MASK_MULTIPLIER;
+ h *= QMACPATTERN_MASK_MULTIPLIER;
+#endif
+ } else {
+ w = pat->data.pixmap.width();
+ h = pat->data.pixmap.height();
+ if (isBitmap)
+ pat->image = qt_mac_create_imagemask(pat->data.pixmap, pat->data.pixmap.rect());
+ else
+ pat->image = (CGImageRef)pat->data.pixmap.macCGHandle();
+ }
+ } else {
+ w = CGImageGetWidth(pat->image);
+ h = CGImageGetHeight(pat->image);
+ }
+
+ //draw
+ bool needRestore = false;
+ if (CGImageIsMask(pat->image)) {
+ CGContextSaveGState(c);
+ CGContextSetFillColorWithColor(c, cgColorForQColor(pat->foreground, pat->pdev));
+ }
+ CGRect rect = CGRectMake(0, 0, w, h);
+ qt_mac_drawCGImage(c, &rect, pat->image);
+ if(needRestore)
+ CGContextRestoreGState(c);
+}
+static void qt_mac_dispose_pattern(void *info)
+{
+ QMacPattern *pat = (QMacPattern*)info;
+ delete pat;
+}
+
+/*****************************************************************************
+ QCoreGraphicsPaintEngine member functions
+ *****************************************************************************/
+
+inline static QPaintEngine::PaintEngineFeatures qt_mac_cg_features()
+{
+ return QPaintEngine::PaintEngineFeatures(QPaintEngine::AllFeatures & ~QPaintEngine::PaintOutsidePaintEvent
+ & ~QPaintEngine::PerspectiveTransform
+ & ~QPaintEngine::ConicalGradientFill
+ & ~QPaintEngine::LinearGradientFill
+ & ~QPaintEngine::RadialGradientFill
+ & ~QPaintEngine::BrushStroke);
+}
+
+QCoreGraphicsPaintEngine::QCoreGraphicsPaintEngine()
+: QPaintEngine(*(new QCoreGraphicsPaintEnginePrivate), qt_mac_cg_features())
+{
+}
+
+QCoreGraphicsPaintEngine::QCoreGraphicsPaintEngine(QPaintEnginePrivate &dptr)
+: QPaintEngine(dptr, qt_mac_cg_features())
+{
+}
+
+QCoreGraphicsPaintEngine::~QCoreGraphicsPaintEngine()
+{
+}
+
+bool
+QCoreGraphicsPaintEngine::begin(QPaintDevice *pdev)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ if(isActive()) { // already active painting
+ qWarning("QCoreGraphicsPaintEngine::begin: Painter already active");
+ return false;
+ }
+
+ //initialization
+ d->pdev = pdev;
+ d->complexXForm = false;
+ d->cosmeticPen = QCoreGraphicsPaintEnginePrivate::CosmeticSetPenWidth;
+ d->cosmeticPenSize = 1;
+ d->current.clipEnabled = false;
+ d->pixelSize = QPoint(1,1);
+ d->hd = qt_mac_cg_context(pdev);
+ if(d->hd) {
+ d->saveGraphicsState();
+ d->orig_xform = CGContextGetCTM(d->hd);
+ if (d->shading) {
+ CGShadingRelease(d->shading);
+ d->shading = 0;
+ }
+ d->setClip(0); //clear the context's clipping
+ }
+
+ setActive(true);
+
+ if(d->pdev->devType() == QInternal::Widget) { // device is a widget
+ QWidget *w = (QWidget*)d->pdev;
+ bool unclipped = w->testAttribute(Qt::WA_PaintUnclipped);
+
+ if((w->windowType() == Qt::Desktop)) {
+ if(!unclipped)
+ qWarning("QCoreGraphicsPaintEngine::begin: Does not support clipped desktop on Mac OS X");
+ // ## need to do [qt_mac_window_for(w) makeKeyAndOrderFront]; (need to rename the file)
+ } else if(unclipped) {
+ qWarning("QCoreGraphicsPaintEngine::begin: Does not support unclipped painting");
+ }
+ } else if(d->pdev->devType() == QInternal::Pixmap) { // device is a pixmap
+ QPixmap *pm = (QPixmap*)d->pdev;
+ if(pm->isNull()) {
+ qWarning("QCoreGraphicsPaintEngine::begin: Cannot paint null pixmap");
+ end();
+ return false;
+ }
+ }
+
+ setDirty(QPaintEngine::DirtyPen);
+ setDirty(QPaintEngine::DirtyBrush);
+ setDirty(QPaintEngine::DirtyBackground);
+ setDirty(QPaintEngine::DirtyHints);
+ return true;
+}
+
+bool
+QCoreGraphicsPaintEngine::end()
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ setActive(false);
+ if(d->pdev->devType() == QInternal::Widget && static_cast<QWidget*>(d->pdev)->windowType() == Qt::Desktop) {
+#ifndef QT_MAC_USE_COCOA
+ HideWindow(qt_mac_window_for(static_cast<QWidget*>(d->pdev)));
+#else
+// // ### need to do [qt_mac_window_for(static_cast<QWidget *>(d->pdev)) orderOut]; (need to rename)
+#endif
+
+ }
+ if(d->shading) {
+ CGShadingRelease(d->shading);
+ d->shading = 0;
+ }
+ d->pdev = 0;
+ if(d->hd) {
+ d->restoreGraphicsState();
+ CGContextSynchronize(d->hd);
+ CGContextRelease(d->hd);
+ d->hd = 0;
+ }
+ return true;
+}
+
+void
+QCoreGraphicsPaintEngine::updateState(const QPaintEngineState &state)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ QPaintEngine::DirtyFlags flags = state.state();
+
+ if (flags & DirtyTransform)
+ updateMatrix(state.transform());
+
+ if (flags & DirtyClipEnabled) {
+ if (state.isClipEnabled())
+ updateClipPath(painter()->clipPath(), Qt::ReplaceClip);
+ else
+ updateClipPath(QPainterPath(), Qt::NoClip);
+ }
+
+ if (flags & DirtyClipPath) {
+ updateClipPath(state.clipPath(), state.clipOperation());
+ } else if (flags & DirtyClipRegion) {
+ updateClipRegion(state.clipRegion(), state.clipOperation());
+ }
+
+ // If the clip has changed we need to update all other states
+ // too, since they are included in the system context on OSX,
+ // and changing the clip resets that context back to scratch.
+ if (flags & (DirtyClipPath | DirtyClipRegion | DirtyClipEnabled))
+ flags |= AllDirty;
+
+ if (flags & DirtyPen)
+ updatePen(state.pen());
+ if (flags & (DirtyBrush|DirtyBrushOrigin))
+ updateBrush(state.brush(), state.brushOrigin());
+ if (flags & DirtyFont)
+ updateFont(state.font());
+ if (flags & DirtyOpacity)
+ updateOpacity(state.opacity());
+ if (flags & DirtyHints)
+ updateRenderHints(state.renderHints());
+ if (flags & DirtyCompositionMode)
+ updateCompositionMode(state.compositionMode());
+
+ if (flags & (DirtyPen | DirtyTransform)) {
+ if (!d->current.pen.isCosmetic()) {
+ d->cosmeticPen = QCoreGraphicsPaintEnginePrivate::CosmeticNone;
+ } else if (d->current.transform.m11() < d->current.transform.m22()-1.0 ||
+ d->current.transform.m11() > d->current.transform.m22()+1.0) {
+ d->cosmeticPen = QCoreGraphicsPaintEnginePrivate::CosmeticTransformPath;
+ d->cosmeticPenSize = d->adjustPenWidth(d->current.pen.widthF());
+ if (!d->cosmeticPenSize)
+ d->cosmeticPenSize = 1.0;
+ } else {
+ d->cosmeticPen = QCoreGraphicsPaintEnginePrivate::CosmeticSetPenWidth;
+ static const float sqrt2 = sqrt(2);
+ qreal width = d->current.pen.widthF();
+ if (!width)
+ width = 1;
+ d->cosmeticPenSize = sqrt(pow(d->pixelSize.y(), 2) + pow(d->pixelSize.x(), 2)) / sqrt2 * width;
+ }
+ }
+}
+
+void
+QCoreGraphicsPaintEngine::updatePen(const QPen &pen)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+ d->current.pen = pen;
+ d->setStrokePen(pen);
+}
+
+void
+QCoreGraphicsPaintEngine::updateBrush(const QBrush &brush, const QPointF &brushOrigin)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+ d->current.brush = brush;
+
+#ifdef QT_MAC_USE_NATIVE_GRADIENTS
+ // Quartz supports only pad spread
+ if (const QGradient *gradient = brush.gradient()) {
+ if (drawGradientNatively(gradient)) {
+ gccaps |= QPaintEngine::LinearGradientFill | QPaintEngine::RadialGradientFill;
+ } else {
+ gccaps &= ~(QPaintEngine::LinearGradientFill | QPaintEngine::RadialGradientFill);
+ }
+ }
+#endif
+
+ if (d->shading) {
+ CGShadingRelease(d->shading);
+ d->shading = 0;
+ }
+ d->setFillBrush(brushOrigin);
+}
+
+void
+QCoreGraphicsPaintEngine::updateOpacity(qreal opacity)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ CGContextSetAlpha(d->hd, opacity);
+}
+
+void
+QCoreGraphicsPaintEngine::updateFont(const QFont &)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+ updatePen(d->current.pen);
+}
+
+void
+QCoreGraphicsPaintEngine::updateMatrix(const QTransform &transform)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+
+ if (qt_is_nan(transform.m11()) || qt_is_nan(transform.m12()) || qt_is_nan(transform.m13())
+ || qt_is_nan(transform.m21()) || qt_is_nan(transform.m22()) || qt_is_nan(transform.m23())
+ || qt_is_nan(transform.m31()) || qt_is_nan(transform.m32()) || qt_is_nan(transform.m33()))
+ return;
+
+ d->current.transform = transform;
+ d->setTransform(transform.isIdentity() ? 0 : &transform);
+ d->complexXForm = (transform.m11() != 1 || transform.m22() != 1
+ || transform.m12() != 0 || transform.m21() != 0);
+ d->pixelSize = d->devicePixelSize(d->hd);
+}
+
+void
+QCoreGraphicsPaintEngine::updateClipPath(const QPainterPath &p, Qt::ClipOperation op)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+ if(op == Qt::NoClip) {
+ if(d->current.clipEnabled) {
+ d->current.clipEnabled = false;
+ d->current.clip = QRegion();
+ d->setClip(0);
+ }
+ } else {
+ if(!d->current.clipEnabled)
+ op = Qt::ReplaceClip;
+ d->current.clipEnabled = true;
+ QRegion clipRegion(p.toFillPolygon().toPolygon(), p.fillRule());
+ if(op == Qt::ReplaceClip) {
+ d->current.clip = clipRegion;
+ d->setClip(0);
+ if(p.isEmpty()) {
+ CGRect rect = CGRectMake(0, 0, 0, 0);
+ CGContextClipToRect(d->hd, rect);
+ } else {
+ CGMutablePathRef path = qt_mac_compose_path(p);
+ CGContextBeginPath(d->hd);
+ CGContextAddPath(d->hd, path);
+ if(p.fillRule() == Qt::WindingFill)
+ CGContextClip(d->hd);
+ else
+ CGContextEOClip(d->hd);
+ CGPathRelease(path);
+ }
+ } else if(op == Qt::IntersectClip) {
+ d->current.clip = d->current.clip.intersected(clipRegion);
+ d->setClip(&d->current.clip);
+ }
+ }
+}
+
+void
+QCoreGraphicsPaintEngine::updateClipRegion(const QRegion &clipRegion, Qt::ClipOperation op)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+ if(op == Qt::NoClip) {
+ d->current.clipEnabled = false;
+ d->current.clip = QRegion();
+ d->setClip(0);
+ } else {
+ if(!d->current.clipEnabled)
+ op = Qt::ReplaceClip;
+ d->current.clipEnabled = true;
+ if(op == Qt::IntersectClip)
+ d->current.clip = d->current.clip.intersected(clipRegion);
+ else if(op == Qt::ReplaceClip)
+ d->current.clip = clipRegion;
+ d->setClip(&d->current.clip);
+ }
+}
+
+void
+QCoreGraphicsPaintEngine::drawPath(const QPainterPath &p)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+
+ if (state->compositionMode() == QPainter::CompositionMode_Destination)
+ return;
+
+ CGMutablePathRef path = qt_mac_compose_path(p);
+ uchar ops = QCoreGraphicsPaintEnginePrivate::CGStroke;
+ if(p.fillRule() == Qt::WindingFill)
+ ops |= QCoreGraphicsPaintEnginePrivate::CGFill;
+ else
+ ops |= QCoreGraphicsPaintEnginePrivate::CGEOFill;
+ CGContextBeginPath(d->hd);
+ d->drawPath(ops, path);
+ CGPathRelease(path);
+}
+
+void
+QCoreGraphicsPaintEngine::drawRects(const QRectF *rects, int rectCount)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+
+ if (state->compositionMode() == QPainter::CompositionMode_Destination)
+ return;
+
+ for (int i=0; i<rectCount; ++i) {
+ QRectF r = rects[i];
+
+ CGMutablePathRef path = CGPathCreateMutable();
+ CGPathAddRect(path, 0, qt_mac_compose_rect(r));
+ d->drawPath(QCoreGraphicsPaintEnginePrivate::CGFill|QCoreGraphicsPaintEnginePrivate::CGStroke,
+ path);
+ CGPathRelease(path);
+ }
+}
+
+void
+QCoreGraphicsPaintEngine::drawPoints(const QPointF *points, int pointCount)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+
+ if (state->compositionMode() == QPainter::CompositionMode_Destination)
+ return;
+
+ if (d->current.pen.capStyle() == Qt::FlatCap)
+ CGContextSetLineCap(d->hd, kCGLineCapSquare);
+
+ CGMutablePathRef path = CGPathCreateMutable();
+ for(int i=0; i < pointCount; i++) {
+ float x = points[i].x(), y = points[i].y();
+ CGPathMoveToPoint(path, 0, x, y);
+ CGPathAddLineToPoint(path, 0, x+0.001, y);
+ }
+
+ bool doRestore = false;
+ if(d->cosmeticPen == QCoreGraphicsPaintEnginePrivate::CosmeticNone && !(state->renderHints() & QPainter::Antialiasing)) {
+ //we don't want adjusted pens for point rendering
+ doRestore = true;
+ d->saveGraphicsState();
+ CGContextSetLineWidth(d->hd, d->current.pen.widthF());
+ }
+ d->drawPath(QCoreGraphicsPaintEnginePrivate::CGStroke, path);
+ if (doRestore)
+ d->restoreGraphicsState();
+ CGPathRelease(path);
+ if (d->current.pen.capStyle() == Qt::FlatCap)
+ CGContextSetLineCap(d->hd, kCGLineCapButt);
+}
+
+void
+QCoreGraphicsPaintEngine::drawEllipse(const QRectF &r)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+
+ if (state->compositionMode() == QPainter::CompositionMode_Destination)
+ return;
+
+ CGMutablePathRef path = CGPathCreateMutable();
+ CGAffineTransform transform = CGAffineTransformMakeScale(r.width() / r.height(), 1);
+ CGPathAddArc(path, &transform,(r.x() + (r.width() / 2)) / (r.width() / r.height()),
+ r.y() + (r.height() / 2), r.height() / 2, 0, (2 * M_PI), false);
+ d->drawPath(QCoreGraphicsPaintEnginePrivate::CGFill | QCoreGraphicsPaintEnginePrivate::CGStroke,
+ path);
+ CGPathRelease(path);
+}
+
+void
+QCoreGraphicsPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+
+ if (state->compositionMode() == QPainter::CompositionMode_Destination)
+ return;
+
+ CGMutablePathRef path = CGPathCreateMutable();
+ CGPathMoveToPoint(path, 0, points[0].x(), points[0].y());
+ for(int x = 1; x < pointCount; ++x)
+ CGPathAddLineToPoint(path, 0, points[x].x(), points[x].y());
+ if(mode != PolylineMode && points[0] != points[pointCount-1])
+ CGPathAddLineToPoint(path, 0, points[0].x(), points[0].y());
+ uint op = QCoreGraphicsPaintEnginePrivate::CGStroke;
+ if (mode != PolylineMode)
+ op |= mode == OddEvenMode ? QCoreGraphicsPaintEnginePrivate::CGEOFill
+ : QCoreGraphicsPaintEnginePrivate::CGFill;
+ d->drawPath(op, path);
+ CGPathRelease(path);
+}
+
+void
+QCoreGraphicsPaintEngine::drawLines(const QLineF *lines, int lineCount)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+
+ if (state->compositionMode() == QPainter::CompositionMode_Destination)
+ return;
+
+ CGMutablePathRef path = CGPathCreateMutable();
+ for(int i = 0; i < lineCount; i++) {
+ const QPointF start = lines[i].p1(), end = lines[i].p2();
+ CGPathMoveToPoint(path, 0, start.x(), start.y());
+ CGPathAddLineToPoint(path, 0, end.x(), end.y());
+ }
+ d->drawPath(QCoreGraphicsPaintEnginePrivate::CGStroke, path);
+ CGPathRelease(path);
+}
+
+void QCoreGraphicsPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+
+ if (state->compositionMode() == QPainter::CompositionMode_Destination)
+ return;
+
+ if(pm.isNull())
+ return;
+
+ bool differentSize = (QRectF(0, 0, pm.width(), pm.height()) != sr), doRestore = false;
+ CGRect rect = CGRectMake(r.x(), r.y(), r.width(), r.height());
+ QCFType<CGImageRef> image;
+ bool isBitmap = (pm.depth() == 1);
+ if (isBitmap) {
+ doRestore = true;
+ d->saveGraphicsState();
+
+ const QColor &col = d->current.pen.color();
+ CGContextSetFillColorWithColor(d->hd, cgColorForQColor(col, d->pdev));
+ image = qt_mac_create_imagemask(pm, sr);
+ } else if (differentSize) {
+ QCFType<CGImageRef> img = pm.toMacCGImageRef();
+ image = CGImageCreateWithImageInRect(img, CGRectMake(qRound(sr.x()), qRound(sr.y()), qRound(sr.width()), qRound(sr.height())));
+ } else {
+ image = (CGImageRef)pm.macCGHandle();
+ }
+ qt_mac_drawCGImage(d->hd, &rect, image);
+ if (doRestore)
+ d->restoreGraphicsState();
+}
+
+void QCoreGraphicsPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
+ Qt::ImageConversionFlags flags)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_UNUSED(flags);
+ Q_ASSERT(isActive());
+
+ if (img.isNull() || state->compositionMode() == QPainter::CompositionMode_Destination)
+ return;
+
+ const QImage *image;
+ QCFType<CGImageRef> cgimage = qt_mac_createCGImageFromQImage(img, &image);
+ CGRect rect = CGRectMake(r.x(), r.y(), r.width(), r.height());
+ if (QRectF(0, 0, img.width(), img.height()) != sr)
+ cgimage = CGImageCreateWithImageInRect(cgimage, CGRectMake(sr.x(), sr.y(),
+ sr.width(), sr.height()));
+ qt_mac_drawCGImage(d->hd, &rect, cgimage);
+}
+
+void QCoreGraphicsPaintEngine::initialize()
+{
+}
+
+void QCoreGraphicsPaintEngine::cleanup()
+{
+}
+
+CGContextRef
+QCoreGraphicsPaintEngine::handle() const
+{
+ return d_func()->hd;
+}
+
+void
+QCoreGraphicsPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap,
+ const QPointF &p)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+
+ if (state->compositionMode() == QPainter::CompositionMode_Destination)
+ return;
+
+ //save the old state
+ d->saveGraphicsState();
+
+ //setup the pattern
+ QMacPattern *qpattern = new QMacPattern;
+ qpattern->data.pixmap = pixmap;
+ qpattern->foreground = d->current.pen.color();
+ qpattern->pdev = d->pdev;
+ CGPatternCallbacks callbks;
+ callbks.version = 0;
+ callbks.drawPattern = qt_mac_draw_pattern;
+ callbks.releaseInfo = qt_mac_dispose_pattern;
+ const int width = qpattern->width(), height = qpattern->height();
+ CGAffineTransform trans = CGContextGetCTM(d->hd);
+ CGPatternRef pat = CGPatternCreate(qpattern, CGRectMake(0, 0, width, height),
+ trans, width, height,
+ kCGPatternTilingNoDistortion, true, &callbks);
+ CGColorSpaceRef cs = CGColorSpaceCreatePattern(0);
+ CGContextSetFillColorSpace(d->hd, cs);
+ CGFloat component = 1.0; //just one
+ CGContextSetFillPattern(d->hd, pat, &component);
+ CGSize phase = CGSizeApplyAffineTransform(CGSizeMake(-(p.x()-r.x()), -(p.y()-r.y())), trans);
+ CGContextSetPatternPhase(d->hd, phase);
+
+ //fill the rectangle
+ CGRect mac_rect = CGRectMake(r.x(), r.y(), r.width(), r.height());
+ CGContextFillRect(d->hd, mac_rect);
+
+ //restore the state
+ d->restoreGraphicsState();
+ //cleanup
+ CGColorSpaceRelease(cs);
+ CGPatternRelease(pat);
+}
+
+void QCoreGraphicsPaintEngine::drawTextItem(const QPointF &pos, const QTextItem &item)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ if (d->current.transform.type() == QTransform::TxProject
+#ifndef QMAC_NATIVE_GRADIENTS
+ || painter()->pen().brush().gradient() //Just let the base engine "emulate" the gradient
+#endif
+ ) {
+ QPaintEngine::drawTextItem(pos, item);
+ return;
+ }
+
+ if (state->compositionMode() == QPainter::CompositionMode_Destination)
+ return;
+
+ const QTextItemInt &ti = static_cast<const QTextItemInt &>(item);
+
+ QPen oldPen = painter()->pen();
+ QBrush oldBrush = painter()->brush();
+ QPointF oldBrushOrigin = painter()->brushOrigin();
+ updatePen(Qt::NoPen);
+ updateBrush(oldPen.brush(), QPointF(0, 0));
+
+ Q_ASSERT(type() == QPaintEngine::CoreGraphics);
+
+ QFontEngine *fe = ti.fontEngine;
+
+ const bool textAA = state->renderHints() & QPainter::TextAntialiasing && fe->fontDef.pointSize > qt_antialiasing_threshold && !(fe->fontDef.styleStrategy & QFont::NoAntialias);
+ const bool lineAA = state->renderHints() & QPainter::Antialiasing;
+ if(textAA != lineAA)
+ CGContextSetShouldAntialias(d->hd, textAA);
+
+ if (ti.glyphs.numGlyphs) {
+ switch (fe->type()) {
+ case QFontEngine::Mac:
+#ifdef QT_MAC_USE_COCOA
+ static_cast<QCoreTextFontEngine *>(fe)->draw(d->hd, pos.x(), pos.y(), ti, paintDevice()->height());
+#else
+ static_cast<QFontEngineMac *>(fe)->draw(d->hd, pos.x(), pos.y(), ti, paintDevice()->height());
+#endif
+ break;
+ case QFontEngine::Box:
+ d->drawBoxTextItem(pos, ti);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if(textAA != lineAA)
+ CGContextSetShouldAntialias(d->hd, !textAA);
+
+ updatePen(oldPen);
+ updateBrush(oldBrush, oldBrushOrigin);
+}
+
+QPainter::RenderHints
+QCoreGraphicsPaintEngine::supportedRenderHints() const
+{
+ return QPainter::RenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
+}
+enum CGCompositeMode {
+ kCGCompositeModeClear = 0,
+ kCGCompositeModeCopy = 1,
+ kCGCompositeModeSourceOver = 2,
+ kCGCompositeModeSourceIn = 3,
+ kCGCompositeModeSourceOut = 4,
+ kCGCompositeModeSourceAtop = 5,
+ kCGCompositeModeDestinationOver = 6,
+ kCGCompositeModeDestinationIn = 7,
+ kCGCompositeModeDestinationOut = 8,
+ kCGCompositeModeDestinationAtop = 9,
+ kCGCompositeModeXOR = 10,
+ kCGCompositeModePlusDarker = 11, // (max (0, (1-d) + (1-s)))
+ kCGCompositeModePlusLighter = 12, // (min (1, s + d))
+ };
+extern "C" {
+ extern void CGContextSetCompositeOperation(CGContextRef, int);
+} // private function, but is in all versions of OS X.
+void
+QCoreGraphicsPaintEngine::updateCompositionMode(QPainter::CompositionMode mode)
+{
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
+ int cg_mode = kCGBlendModeNormal;
+ switch(mode) {
+ case QPainter::CompositionMode_Multiply:
+ cg_mode = kCGBlendModeMultiply;
+ break;
+ case QPainter::CompositionMode_Screen:
+ cg_mode = kCGBlendModeScreen;
+ break;
+ case QPainter::CompositionMode_Overlay:
+ cg_mode = kCGBlendModeOverlay;
+ break;
+ case QPainter::CompositionMode_Darken:
+ cg_mode = kCGBlendModeDarken;
+ break;
+ case QPainter::CompositionMode_Lighten:
+ cg_mode = kCGBlendModeLighten;
+ break;
+ case QPainter::CompositionMode_ColorDodge:
+ cg_mode = kCGBlendModeColorDodge;
+ break;
+ case QPainter::CompositionMode_ColorBurn:
+ cg_mode = kCGBlendModeColorBurn;
+ break;
+ case QPainter::CompositionMode_HardLight:
+ cg_mode = kCGBlendModeHardLight;
+ break;
+ case QPainter::CompositionMode_SoftLight:
+ cg_mode = kCGBlendModeSoftLight;
+ break;
+ case QPainter::CompositionMode_Difference:
+ cg_mode = kCGBlendModeDifference;
+ break;
+ case QPainter::CompositionMode_Exclusion:
+ cg_mode = kCGBlendModeExclusion;
+ break;
+ case QPainter::CompositionMode_Plus:
+ cg_mode = kCGBlendModePlusLighter;
+ break;
+ case QPainter::CompositionMode_SourceOver:
+ cg_mode = kCGBlendModeNormal;
+ break;
+ case QPainter::CompositionMode_DestinationOver:
+ cg_mode = kCGBlendModeDestinationOver;
+ break;
+ case QPainter::CompositionMode_Clear:
+ cg_mode = kCGBlendModeClear;
+ break;
+ case QPainter::CompositionMode_Source:
+ cg_mode = kCGBlendModeCopy;
+ break;
+ case QPainter::CompositionMode_Destination:
+ cg_mode = -1;
+ break;
+ case QPainter::CompositionMode_SourceIn:
+ cg_mode = kCGBlendModeSourceIn;
+ break;
+ case QPainter::CompositionMode_DestinationIn:
+ cg_mode = kCGCompositeModeDestinationIn;
+ break;
+ case QPainter::CompositionMode_SourceOut:
+ cg_mode = kCGBlendModeSourceOut;
+ break;
+ case QPainter::CompositionMode_DestinationOut:
+ cg_mode = kCGBlendModeDestinationOver;
+ break;
+ case QPainter::CompositionMode_SourceAtop:
+ cg_mode = kCGBlendModeSourceAtop;
+ break;
+ case QPainter::CompositionMode_DestinationAtop:
+ cg_mode = kCGBlendModeDestinationAtop;
+ break;
+ case QPainter::CompositionMode_Xor:
+ cg_mode = kCGBlendModeXOR;
+ break;
+ default:
+ break;
+ }
+ if (cg_mode > -1) {
+ CGContextSetBlendMode(d_func()->hd, CGBlendMode(cg_mode));
+ }
+ } else
+#endif
+ // The standard porter duff ops.
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_3
+ && mode <= QPainter::CompositionMode_Xor) {
+ int cg_mode = kCGCompositeModeCopy;
+ switch (mode) {
+ case QPainter::CompositionMode_SourceOver:
+ cg_mode = kCGCompositeModeSourceOver;
+ break;
+ case QPainter::CompositionMode_DestinationOver:
+ cg_mode = kCGCompositeModeDestinationOver;
+ break;
+ case QPainter::CompositionMode_Clear:
+ cg_mode = kCGCompositeModeClear;
+ break;
+ default:
+ qWarning("QCoreGraphicsPaintEngine: Unhandled composition mode %d", (int)mode);
+ break;
+ case QPainter::CompositionMode_Source:
+ cg_mode = kCGCompositeModeCopy;
+ break;
+ case QPainter::CompositionMode_Destination:
+ cg_mode = CGCompositeMode(-1);
+ break;
+ case QPainter::CompositionMode_SourceIn:
+ cg_mode = kCGCompositeModeSourceIn;
+ break;
+ case QPainter::CompositionMode_DestinationIn:
+ cg_mode = kCGCompositeModeDestinationIn;
+ break;
+ case QPainter::CompositionMode_SourceOut:
+ cg_mode = kCGCompositeModeSourceOut;
+ break;
+ case QPainter::CompositionMode_DestinationOut:
+ cg_mode = kCGCompositeModeDestinationOut;
+ break;
+ case QPainter::CompositionMode_SourceAtop:
+ cg_mode = kCGCompositeModeSourceAtop;
+ break;
+ case QPainter::CompositionMode_DestinationAtop:
+ cg_mode = kCGCompositeModeDestinationAtop;
+ break;
+ case QPainter::CompositionMode_Xor:
+ cg_mode = kCGCompositeModeXOR;
+ break;
+ }
+ if (cg_mode > -1)
+ CGContextSetCompositeOperation(d_func()->hd, CGCompositeMode(cg_mode));
+ } else {
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ bool needPrivateAPI = false;
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
+ int cg_mode = kCGBlendModeNormal;
+ switch (mode) {
+ case QPainter::CompositionMode_Multiply:
+ cg_mode = kCGBlendModeMultiply;
+ break;
+ case QPainter::CompositionMode_Screen:
+ cg_mode = kCGBlendModeScreen;
+ break;
+ case QPainter::CompositionMode_Overlay:
+ cg_mode = kCGBlendModeOverlay;
+ break;
+ case QPainter::CompositionMode_Darken:
+ cg_mode = kCGBlendModeDarken;
+ break;
+ case QPainter::CompositionMode_Lighten:
+ cg_mode = kCGBlendModeLighten;
+ break;
+ case QPainter::CompositionMode_ColorDodge:
+ cg_mode = kCGBlendModeColorDodge;
+ break;
+ case QPainter::CompositionMode_ColorBurn:
+ cg_mode = kCGBlendModeColorBurn;
+ break;
+ case QPainter::CompositionMode_HardLight:
+ cg_mode = kCGBlendModeHardLight;
+ break;
+ case QPainter::CompositionMode_SoftLight:
+ cg_mode = kCGBlendModeSoftLight;
+ break;
+ case QPainter::CompositionMode_Difference:
+ cg_mode = kCGBlendModeDifference;
+ break;
+ case QPainter::CompositionMode_Exclusion:
+ cg_mode = kCGBlendModeExclusion;
+ break;
+ case QPainter::CompositionMode_Plus:
+ needPrivateAPI = true;
+ cg_mode = kCGCompositeModePlusLighter;
+ break;
+ default:
+ break;
+ }
+ if (!needPrivateAPI)
+ CGContextSetBlendMode(d_func()->hd, CGBlendMode(cg_mode));
+ else
+ CGContextSetCompositeOperation(d_func()->hd, CGCompositeMode(cg_mode));
+ }
+#endif
+ }
+}
+
+void
+QCoreGraphicsPaintEngine::updateRenderHints(QPainter::RenderHints hints)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ CGContextSetShouldAntialias(d->hd, hints & QPainter::Antialiasing);
+ static const CGFloat ScaleFactor = qt_mac_get_scalefactor();
+ if (ScaleFactor > 1.) {
+ CGContextSetInterpolationQuality(d->hd, kCGInterpolationHigh);
+ } else {
+ CGContextSetInterpolationQuality(d->hd, (hints & QPainter::SmoothPixmapTransform) ?
+ kCGInterpolationHigh : kCGInterpolationNone);
+ }
+ bool textAntialiasing = (hints & QPainter::TextAntialiasing) == QPainter::TextAntialiasing;
+ if (!textAntialiasing || d->disabledSmoothFonts) {
+ d->disabledSmoothFonts = !textAntialiasing;
+ CGContextSetShouldSmoothFonts(d->hd, textAntialiasing);
+ }
+}
+
+/*
+ Returns the size of one device pixel in user-space coordinates.
+*/
+QPointF QCoreGraphicsPaintEnginePrivate::devicePixelSize(CGContextRef)
+{
+ QPointF p1 = current.transform.inverted().map(QPointF(0, 0));
+ QPointF p2 = current.transform.inverted().map(QPointF(1, 1));
+ return QPointF(qAbs(p2.x() - p1.x()), qAbs(p2.y() - p1.y()));
+}
+
+/*
+ Adjusts the pen width so we get correct line widths in the
+ non-transformed, aliased case.
+*/
+float QCoreGraphicsPaintEnginePrivate::adjustPenWidth(float penWidth)
+{
+ Q_Q(QCoreGraphicsPaintEngine);
+ float ret = penWidth;
+ if (!complexXForm && !(q->state->renderHints() & QPainter::Antialiasing)) {
+ if (penWidth < 2)
+ ret = 1;
+ else if (penWidth < 3)
+ ret = 1.5;
+ else
+ ret = penWidth -1;
+ }
+ return ret;
+}
+
+void
+QCoreGraphicsPaintEnginePrivate::setStrokePen(const QPen &pen)
+{
+ //pencap
+ CGLineCap cglinecap = kCGLineCapButt;
+ if(pen.capStyle() == Qt::SquareCap)
+ cglinecap = kCGLineCapSquare;
+ else if(pen.capStyle() == Qt::RoundCap)
+ cglinecap = kCGLineCapRound;
+ CGContextSetLineCap(hd, cglinecap);
+ CGContextSetLineWidth(hd, adjustPenWidth(pen.widthF()));
+
+ //join
+ CGLineJoin cglinejoin = kCGLineJoinMiter;
+ if(pen.joinStyle() == Qt::BevelJoin)
+ cglinejoin = kCGLineJoinBevel;
+ else if(pen.joinStyle() == Qt::RoundJoin)
+ cglinejoin = kCGLineJoinRound;
+ CGContextSetLineJoin(hd, cglinejoin);
+// CGContextSetMiterLimit(hd, pen.miterLimit());
+
+ //pen style
+ QVector<CGFloat> linedashes;
+ if(pen.style() == Qt::CustomDashLine) {
+ QVector<qreal> customs = pen.dashPattern();
+ for(int i = 0; i < customs.size(); ++i)
+ linedashes.append(customs.at(i));
+ } else if(pen.style() == Qt::DashLine) {
+ linedashes.append(4);
+ linedashes.append(2);
+ } else if(pen.style() == Qt::DotLine) {
+ linedashes.append(1);
+ linedashes.append(2);
+ } else if(pen.style() == Qt::DashDotLine) {
+ linedashes.append(4);
+ linedashes.append(2);
+ linedashes.append(1);
+ linedashes.append(2);
+ } else if(pen.style() == Qt::DashDotDotLine) {
+ linedashes.append(4);
+ linedashes.append(2);
+ linedashes.append(1);
+ linedashes.append(2);
+ linedashes.append(1);
+ linedashes.append(2);
+ }
+ const CGFloat cglinewidth = pen.widthF() <= 0.0f ? 1.0f : float(pen.widthF());
+ for(int i = 0; i < linedashes.size(); ++i) {
+ linedashes[i] *= cglinewidth;
+ if(cglinewidth < 3 && (cglinecap == kCGLineCapSquare || cglinecap == kCGLineCapRound)) {
+ if((i%2))
+ linedashes[i] += cglinewidth/2;
+ else
+ linedashes[i] -= cglinewidth/2;
+ }
+ }
+ CGContextSetLineDash(hd, pen.dashOffset() * cglinewidth, linedashes.data(), linedashes.size());
+
+ // color
+ CGContextSetStrokeColorWithColor(hd, cgColorForQColor(pen.color(), pdev));
+}
+
+// Add our own patterns here to deal with the fact that the coordinate system
+// is flipped vertically with Quartz2D.
+static const uchar *qt_mac_patternForBrush(int brushStyle)
+{
+ Q_ASSERT(brushStyle > Qt::SolidPattern && brushStyle < Qt::LinearGradientPattern);
+ static const uchar dense1_pat[] = { 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x44, 0x00 };
+ static const uchar dense2_pat[] = { 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00, 0x88 };
+ static const uchar dense3_pat[] = { 0x11, 0xaa, 0x44, 0xaa, 0x11, 0xaa, 0x44, 0xaa };
+ static const uchar dense4_pat[] = { 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55 };
+ static const uchar dense5_pat[] = { 0xee, 0x55, 0xbb, 0x55, 0xee, 0x55, 0xbb, 0x55 };
+ static const uchar dense6_pat[] = { 0xff, 0xdd, 0xff, 0x77, 0xff, 0xdd, 0xff, 0x77 };
+ static const uchar dense7_pat[] = { 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, 0xff };
+ static const uchar hor_pat[] = { 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff };
+ static const uchar ver_pat[] = { 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef };
+ static const uchar cross_pat[] = { 0xef, 0xef, 0xef, 0xef, 0x00, 0xef, 0xef, 0xef };
+ static const uchar fdiag_pat[] = { 0x7f, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0xfe };
+ static const uchar bdiag_pat[] = { 0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f };
+ static const uchar dcross_pat[] = { 0x7e, 0xbd, 0xdb, 0xe7, 0xe7, 0xdb, 0xbd, 0x7e };
+ static const uchar *const pat_tbl[] = {
+ dense1_pat, dense2_pat, dense3_pat, dense4_pat, dense5_pat,
+ dense6_pat, dense7_pat,
+ hor_pat, ver_pat, cross_pat, bdiag_pat, fdiag_pat, dcross_pat };
+ return pat_tbl[brushStyle - Qt::Dense1Pattern];
+}
+
+void QCoreGraphicsPaintEnginePrivate::setFillBrush(const QPointF &offset)
+{
+ // pattern
+ Qt::BrushStyle bs = current.brush.style();
+#ifdef QT_MAC_USE_NATIVE_GRADIENTS
+ if (bs == Qt::LinearGradientPattern || bs == Qt::RadialGradientPattern) {
+ const QGradient *grad = static_cast<const QGradient*>(current.brush.gradient());
+ if (drawGradientNatively(grad)) {
+ Q_ASSERT(grad->spread() == QGradient::PadSpread);
+
+ static const CGFloat domain[] = { 0.0f, +1.0f };
+ static const CGFunctionCallbacks callbacks = { 0, qt_mac_color_gradient_function, 0 };
+ CGFunctionRef fill_func = CGFunctionCreate(reinterpret_cast<void *>(&current.brush),
+ 1, domain, 4, 0, &callbacks);
+
+ CGColorSpaceRef colorspace = qt_mac_colorSpaceForDeviceType(pdev);
+ if (bs == Qt::LinearGradientPattern) {
+ const QLinearGradient *linearGrad = static_cast<const QLinearGradient *>(grad);
+ const QPointF start(linearGrad->start());
+ const QPointF stop(linearGrad->finalStop());
+ shading = CGShadingCreateAxial(colorspace, CGPointMake(start.x(), start.y()),
+ CGPointMake(stop.x(), stop.y()), fill_func, true, true);
+ } else {
+ Q_ASSERT(bs == Qt::RadialGradientPattern);
+ const QRadialGradient *radialGrad = static_cast<const QRadialGradient *>(grad);
+ QPointF center(radialGrad->center());
+ QPointF focal(radialGrad->focalPoint());
+ qreal radius = radialGrad->radius();
+ qreal focalRadius = radialGrad->focalRadius();
+ shading = CGShadingCreateRadial(colorspace, CGPointMake(focal.x(), focal.y()),
+ focalRadius, CGPointMake(center.x(), center.y()), radius, fill_func, false, true);
+ }
+
+ CGFunctionRelease(fill_func);
+ }
+ } else
+#endif
+ if(bs != Qt::SolidPattern && bs != Qt::NoBrush
+#ifndef QT_MAC_USE_NATIVE_GRADIENTS
+ && (bs < Qt::LinearGradientPattern || bs > Qt::ConicalGradientPattern)
+#endif
+ )
+ {
+ QMacPattern *qpattern = new QMacPattern;
+ qpattern->pdev = pdev;
+ CGFloat components[4] = { 1.0, 1.0, 1.0, 1.0 };
+ CGColorSpaceRef base_colorspace = 0;
+ if(bs == Qt::TexturePattern) {
+ qpattern->data.pixmap = current.brush.texture();
+ if(qpattern->data.pixmap.isQBitmap()) {
+ const QColor &col = current.brush.color();
+ components[0] = qt_mac_convert_color_to_cg(col.red());
+ components[1] = qt_mac_convert_color_to_cg(col.green());
+ components[2] = qt_mac_convert_color_to_cg(col.blue());
+ base_colorspace = QCoreGraphicsPaintEngine::macGenericColorSpace();
+ }
+ } else {
+ qpattern->as_mask = true;
+
+ qpattern->data.bytes = qt_mac_patternForBrush(bs);
+ const QColor &col = current.brush.color();
+ components[0] = qt_mac_convert_color_to_cg(col.red());
+ components[1] = qt_mac_convert_color_to_cg(col.green());
+ components[2] = qt_mac_convert_color_to_cg(col.blue());
+ base_colorspace = QCoreGraphicsPaintEngine::macGenericColorSpace();
+ }
+ int width = qpattern->width(), height = qpattern->height();
+ qpattern->foreground = current.brush.color();
+
+ CGColorSpaceRef fill_colorspace = CGColorSpaceCreatePattern(base_colorspace);
+ CGContextSetFillColorSpace(hd, fill_colorspace);
+
+ CGAffineTransform xform = CGContextGetCTM(hd);
+ xform = CGAffineTransformConcat(qt_mac_convert_transform_to_cg(current.brush.transform()), xform);
+ xform = CGAffineTransformTranslate(xform, offset.x(), offset.y());
+
+ CGPatternCallbacks callbks;
+ callbks.version = 0;
+ callbks.drawPattern = qt_mac_draw_pattern;
+ callbks.releaseInfo = qt_mac_dispose_pattern;
+ CGPatternRef fill_pattern = CGPatternCreate(qpattern, CGRectMake(0, 0, width, height),
+ xform, width, height, kCGPatternTilingNoDistortion,
+ !base_colorspace, &callbks);
+ CGContextSetFillPattern(hd, fill_pattern, components);
+
+ CGPatternRelease(fill_pattern);
+ CGColorSpaceRelease(fill_colorspace);
+ } else if(bs != Qt::NoBrush) {
+ CGContextSetFillColorWithColor(hd, cgColorForQColor(current.brush.color(), pdev));
+ }
+}
+
+void
+QCoreGraphicsPaintEnginePrivate::setClip(const QRegion *rgn)
+{
+ Q_Q(QCoreGraphicsPaintEngine);
+ if(hd) {
+ resetClip();
+ QRegion sysClip = q->systemClip();
+ if(!sysClip.isEmpty())
+ qt_mac_clip_cg(hd, sysClip, &orig_xform);
+ if(rgn)
+ qt_mac_clip_cg(hd, *rgn, 0);
+ }
+}
+
+struct qt_mac_cg_transform_path {
+ CGMutablePathRef path;
+ CGAffineTransform transform;
+};
+
+void qt_mac_cg_transform_path_apply(void *info, const CGPathElement *element)
+{
+ Q_ASSERT(info && element);
+ qt_mac_cg_transform_path *t = (qt_mac_cg_transform_path*)info;
+ switch(element->type) {
+ case kCGPathElementMoveToPoint:
+ CGPathMoveToPoint(t->path, &t->transform, element->points[0].x, element->points[0].y);
+ break;
+ case kCGPathElementAddLineToPoint:
+ CGPathAddLineToPoint(t->path, &t->transform, element->points[0].x, element->points[0].y);
+ break;
+ case kCGPathElementAddQuadCurveToPoint:
+ CGPathAddQuadCurveToPoint(t->path, &t->transform, element->points[0].x, element->points[0].y,
+ element->points[1].x, element->points[1].y);
+ break;
+ case kCGPathElementAddCurveToPoint:
+ CGPathAddCurveToPoint(t->path, &t->transform, element->points[0].x, element->points[0].y,
+ element->points[1].x, element->points[1].y,
+ element->points[2].x, element->points[2].y);
+ break;
+ case kCGPathElementCloseSubpath:
+ CGPathCloseSubpath(t->path);
+ break;
+ default:
+ qDebug() << "Unhandled path transform type: " << element->type;
+ }
+}
+
+void QCoreGraphicsPaintEnginePrivate::drawPath(uchar ops, CGMutablePathRef path)
+{
+ Q_Q(QCoreGraphicsPaintEngine);
+ Q_ASSERT((ops & (CGFill | CGEOFill)) != (CGFill | CGEOFill)); //can't really happen
+ if((ops & (CGFill | CGEOFill))) {
+ if (shading) {
+ Q_ASSERT(path);
+ CGContextBeginPath(hd);
+ CGContextAddPath(hd, path);
+ saveGraphicsState();
+ if (ops & CGFill)
+ CGContextClip(hd);
+ else if (ops & CGEOFill)
+ CGContextEOClip(hd);
+ if (current.brush.gradient()->coordinateMode() == QGradient::ObjectBoundingMode) {
+ CGRect boundingBox = CGPathGetBoundingBox(path);
+ CGContextConcatCTM(hd,
+ CGAffineTransformMake(boundingBox.size.width, 0,
+ 0, boundingBox.size.height,
+ boundingBox.origin.x, boundingBox.origin.y));
+ }
+ CGContextDrawShading(hd, shading);
+ restoreGraphicsState();
+ ops &= ~CGFill;
+ ops &= ~CGEOFill;
+ } else if (current.brush.style() == Qt::NoBrush) {
+ ops &= ~CGFill;
+ ops &= ~CGEOFill;
+ }
+ }
+ if((ops & CGStroke) && current.pen.style() == Qt::NoPen)
+ ops &= ~CGStroke;
+
+ if(ops & (CGEOFill | CGFill)) {
+ CGContextBeginPath(hd);
+ CGContextAddPath(hd, path);
+ if (ops & CGEOFill) {
+ CGContextEOFillPath(hd);
+ } else {
+ CGContextFillPath(hd);
+ }
+ }
+
+ // Avoid saving and restoring the context if we can.
+ const bool needContextSave = (cosmeticPen != QCoreGraphicsPaintEnginePrivate::CosmeticNone ||
+ !(q->state->renderHints() & QPainter::Antialiasing));
+ if(ops & CGStroke) {
+ if (needContextSave)
+ saveGraphicsState();
+ CGContextBeginPath(hd);
+
+ // Translate a fraction of a pixel size in the y direction
+ // to make sure that primitives painted at pixel borders
+ // fills the right pixel. This is needed since the y xais
+ // in the Quartz coordinate system is inverted compared to Qt.
+ if (!(q->state->renderHints() & QPainter::Antialiasing)) {
+ if (current.pen.style() == Qt::SolidLine || current.pen.width() >= 3)
+ CGContextTranslateCTM(hd, double(pixelSize.x()) * 0.25, double(pixelSize.y()) * 0.25);
+ else if (current.pen.style() == Qt::DotLine && QSysInfo::MacintoshVersion == QSysInfo::MV_10_3)
+ ; // Do nothing.
+ else
+ CGContextTranslateCTM(hd, 0, double(pixelSize.y()) * 0.1);
+ }
+
+ if (cosmeticPen != QCoreGraphicsPaintEnginePrivate::CosmeticNone) {
+ // If antialiazing is enabled, use the cosmetic pen size directly.
+ if (q->state->renderHints() & QPainter::Antialiasing)
+ CGContextSetLineWidth(hd, cosmeticPenSize);
+ else if (current.pen.widthF() <= 1)
+ CGContextSetLineWidth(hd, cosmeticPenSize * 0.9f);
+ else
+ CGContextSetLineWidth(hd, cosmeticPenSize);
+ }
+ if(cosmeticPen == QCoreGraphicsPaintEnginePrivate::CosmeticTransformPath) {
+ qt_mac_cg_transform_path t;
+ t.transform = qt_mac_convert_transform_to_cg(current.transform);
+ t.path = CGPathCreateMutable();
+ CGPathApply(path, &t, qt_mac_cg_transform_path_apply); //transform the path
+ setTransform(0); //unset the context transform
+ CGContextSetLineWidth(hd, cosmeticPenSize);
+ CGContextAddPath(hd, t.path);
+ CGPathRelease(t.path);
+ } else {
+ CGContextAddPath(hd, path);
+ }
+
+ CGContextStrokePath(hd);
+ if (needContextSave)
+ restoreGraphicsState();
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/mac/qpaintengine_mac_p.h b/src/widgets/platforms/mac/qpaintengine_mac_p.h
new file mode 100644
index 0000000000..2434011e52
--- /dev/null
+++ b/src/widgets/platforms/mac/qpaintengine_mac_p.h
@@ -0,0 +1,256 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTENGINE_MAC_P_H
+#define QPAINTENGINE_MAC_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtGui/qpaintengine.h"
+#include "private/qt_mac_p.h"
+#include "private/qpaintengine_p.h"
+#include "private/qpolygonclipper_p.h"
+#include "private/qfont_p.h"
+#include "QtCore/qhash.h"
+
+typedef struct CGColorSpace *CGColorSpaceRef;
+QT_BEGIN_NAMESPACE
+
+class QCoreGraphicsPaintEnginePrivate;
+class QCoreGraphicsPaintEngine : public QPaintEngine
+{
+ Q_DECLARE_PRIVATE(QCoreGraphicsPaintEngine)
+
+public:
+ QCoreGraphicsPaintEngine();
+ ~QCoreGraphicsPaintEngine();
+
+ bool begin(QPaintDevice *pdev);
+ bool end();
+ static CGColorSpaceRef macGenericColorSpace();
+ static CGColorSpaceRef macDisplayColorSpace(const QWidget *widget = 0);
+
+ void updateState(const QPaintEngineState &state);
+
+ void updatePen(const QPen &pen);
+ void updateBrush(const QBrush &brush, const QPointF &pt);
+ void updateFont(const QFont &font);
+ void updateOpacity(qreal opacity);
+ void updateMatrix(const QTransform &matrix);
+ void updateTransform(const QTransform &matrix);
+ void updateClipRegion(const QRegion &region, Qt::ClipOperation op);
+ void updateClipPath(const QPainterPath &path, Qt::ClipOperation op);
+ void updateCompositionMode(QPainter::CompositionMode mode);
+ void updateRenderHints(QPainter::RenderHints hints);
+
+ void drawLines(const QLineF *lines, int lineCount);
+ void drawRects(const QRectF *rects, int rectCount);
+ void drawPoints(const QPointF *p, int pointCount);
+ void drawEllipse(const QRectF &r);
+ void drawPath(const QPainterPath &path);
+
+ void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode);
+ void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
+ void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s);
+
+ void drawTextItem(const QPointF &pos, const QTextItem &item);
+ void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr,
+ Qt::ImageConversionFlags flags = Qt::AutoColor);
+
+ Type type() const { return QPaintEngine::CoreGraphics; }
+
+ CGContextRef handle() const;
+
+ static void initialize();
+ static void cleanup();
+
+ QPainter::RenderHints supportedRenderHints() const;
+
+ //avoid partial shadowed overload warnings...
+ void drawLines(const QLine *lines, int lineCount) { QPaintEngine::drawLines(lines, lineCount); }
+ void drawRects(const QRect *rects, int rectCount) { QPaintEngine::drawRects(rects, rectCount); }
+ void drawPoints(const QPoint *p, int pointCount) { QPaintEngine::drawPoints(p, pointCount); }
+ void drawEllipse(const QRect &r) { QPaintEngine::drawEllipse(r); }
+ void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
+ { QPaintEngine::drawPolygon(points, pointCount, mode); }
+
+ bool supportsTransformations(qreal, const QTransform &) const { return true; };
+
+protected:
+ friend class QMacPrintEngine;
+ friend class QMacPrintEnginePrivate;
+ friend void qt_mac_display_change_callbk(CGDirectDisplayID, CGDisplayChangeSummaryFlags, void *);
+ friend void qt_color_profile_changed(CFNotificationCenterRef center, void *,
+ CFStringRef , const void *, CFDictionaryRef);
+ QCoreGraphicsPaintEngine(QPaintEnginePrivate &dptr);
+
+private:
+ static bool m_postRoutineRegistered;
+ static CGColorSpaceRef m_genericColorSpace;
+ static QHash<CGDirectDisplayID, CGColorSpaceRef> m_displayColorSpaceHash;
+ static void cleanUpMacColorSpaces();
+ Q_DISABLE_COPY(QCoreGraphicsPaintEngine)
+};
+
+/*****************************************************************************
+ Private data
+ *****************************************************************************/
+class QCoreGraphicsPaintEnginePrivate : public QPaintEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QCoreGraphicsPaintEngine)
+public:
+ QCoreGraphicsPaintEnginePrivate()
+ : hd(0), shading(0), stackCount(0), complexXForm(false), disabledSmoothFonts(false)
+ {
+ }
+
+ struct {
+ QPen pen;
+ QBrush brush;
+ uint clipEnabled : 1;
+ QRegion clip;
+ QTransform transform;
+ } current;
+
+ //state info (shared with QD)
+ CGAffineTransform orig_xform;
+
+ //cg structures
+ CGContextRef hd;
+ CGShadingRef shading;
+ int stackCount;
+ bool complexXForm;
+ bool disabledSmoothFonts;
+ enum { CosmeticNone, CosmeticTransformPath, CosmeticSetPenWidth } cosmeticPen;
+
+ // pixel and cosmetic pen size in user coordinates.
+ QPointF pixelSize;
+ float cosmeticPenSize;
+
+ //internal functions
+ enum { CGStroke=0x01, CGEOFill=0x02, CGFill=0x04 };
+ void drawPath(uchar ops, CGMutablePathRef path = 0);
+ void setClip(const QRegion *rgn=0);
+ void resetClip();
+ void setFillBrush(const QPointF &origin=QPoint());
+ void setStrokePen(const QPen &pen);
+ inline void saveGraphicsState();
+ inline void restoreGraphicsState();
+ float penOffset();
+ QPointF devicePixelSize(CGContextRef context);
+ float adjustPenWidth(float penWidth);
+ inline void setTransform(const QTransform *matrix=0)
+ {
+ CGContextConcatCTM(hd, CGAffineTransformInvert(CGContextGetCTM(hd)));
+ CGAffineTransform xform = orig_xform;
+ if(matrix) {
+ extern CGAffineTransform qt_mac_convert_transform_to_cg(const QTransform &);
+ xform = CGAffineTransformConcat(qt_mac_convert_transform_to_cg(*matrix), xform);
+ }
+ CGContextConcatCTM(hd, xform);
+ CGContextSetTextMatrix(hd, xform);
+ }
+};
+
+inline void QCoreGraphicsPaintEnginePrivate::saveGraphicsState()
+{
+ ++stackCount;
+ CGContextSaveGState(hd);
+}
+
+inline void QCoreGraphicsPaintEnginePrivate::restoreGraphicsState()
+{
+ --stackCount;
+ Q_ASSERT(stackCount >= 0);
+ CGContextRestoreGState(hd);
+}
+
+class QMacQuartzPaintDevice : public QPaintDevice
+{
+public:
+ QMacQuartzPaintDevice(CGContextRef cg, int width, int height, int bytesPerLine)
+ : mCG(cg), mWidth(width), mHeight(height), mBytesPerLine(bytesPerLine)
+ { }
+ int devType() const { return QInternal::MacQuartz; }
+ CGContextRef cgContext() const { return mCG; }
+ int metric(PaintDeviceMetric metric) const {
+ switch (metric) {
+ case PdmWidth:
+ return mWidth;
+ case PdmHeight:
+ return mHeight;
+ case PdmWidthMM:
+ return (qt_defaultDpiX() * mWidth) / 2.54;
+ case PdmHeightMM:
+ return (qt_defaultDpiY() * mHeight) / 2.54;
+ case PdmNumColors:
+ return 0;
+ case PdmDepth:
+ return 32;
+ case PdmDpiX:
+ case PdmPhysicalDpiX:
+ return qt_defaultDpiX();
+ case PdmDpiY:
+ case PdmPhysicalDpiY:
+ return qt_defaultDpiY();
+ }
+ return 0;
+ }
+ QPaintEngine *paintEngine() const { qWarning("This function should never be called."); return 0; }
+private:
+ CGContextRef mCG;
+ int mWidth;
+ int mHeight;
+ int mBytesPerLine;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPAINTENGINE_MAC_P_H
diff --git a/src/widgets/platforms/mac/qpixmap_mac.cpp b/src/widgets/platforms/mac/qpixmap_mac.cpp
new file mode 100644
index 0000000000..a375eda6a2
--- /dev/null
+++ b/src/widgets/platforms/mac/qpixmap_mac.cpp
@@ -0,0 +1,1174 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpixmap.h"
+#include "qimage.h"
+#include "qapplication.h"
+#include "qbitmap.h"
+#include "qmatrix.h"
+#include "qtransform.h"
+#include "qlibrary.h"
+#include "qvarlengtharray.h"
+#include "qdebug.h"
+#include <private/qdrawhelper_p.h>
+#include <private/qpixmap_mac_p.h>
+#include <private/qpixmap_raster_p.h>
+#include <private/qpaintengine_mac_p.h>
+#include <private/qt_mac_p.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+#include <private/qapplication_p.h>
+
+#include <limits.h>
+#include <string.h>
+
+QT_BEGIN_NAMESPACE
+
+/*****************************************************************************
+ Externals
+ *****************************************************************************/
+extern const uchar *qt_get_bitflip_array(); //qimage.cpp
+extern CGContextRef qt_mac_cg_context(const QPaintDevice *pdev); //qpaintdevice_mac.cpp
+extern RgnHandle qt_mac_get_rgn(); //qregion_mac.cpp
+extern void qt_mac_dispose_rgn(RgnHandle r); //qregion_mac.cpp
+extern QRegion qt_mac_convert_mac_region(RgnHandle rgn); //qregion_mac.cpp
+
+static int qt_pixmap_serial = 0;
+
+Q_WIDGETS_EXPORT quint32 *qt_mac_pixmap_get_base(const QPixmap *pix)
+{
+ if (QApplicationPrivate::graphics_system_name == QLatin1String("raster"))
+ return reinterpret_cast<quint32 *>(static_cast<QRasterPlatformPixmap*>(pix->data.data())->buffer()->bits());
+ else
+ return static_cast<QMacPlatformPixmap*>(pix->data.data())->pixels;
+}
+
+Q_WIDGETS_EXPORT int qt_mac_pixmap_get_bytes_per_line(const QPixmap *pix)
+{
+ if (QApplicationPrivate::graphics_system_name == QLatin1String("raster"))
+ return static_cast<QRasterPlatformPixmap*>(pix->data.data())->buffer()->bytesPerLine();
+ else
+ return static_cast<QMacPlatformPixmap*>(pix->data.data())->bytesPerRow;
+}
+
+void qt_mac_cgimage_data_free(void *info, const void *memoryToFree, size_t)
+{
+ QMacPlatformPixmap *pmdata = static_cast<QMacPlatformPixmap *>(info);
+ if (!pmdata) {
+ free(const_cast<void *>(memoryToFree));
+ } else {
+ if (QMacPlatformPixmap::validDataPointers.contains(pmdata) == false) {
+ free(const_cast<void *>(memoryToFree));
+ return;
+ }
+ if (pmdata->pixels == pmdata->pixelsToFree) {
+ // something we aren't expecting, just free it.
+ Q_ASSERT(memoryToFree != pmdata->pixelsToFree);
+ free(const_cast<void *>(memoryToFree));
+ } else {
+ free(pmdata->pixelsToFree);
+ pmdata->pixelsToFree = static_cast<quint32 *>(const_cast<void *>(memoryToFree));
+ }
+ pmdata->cg_dataBeingReleased = 0;
+ }
+}
+
+/*****************************************************************************
+ QPixmap member functions
+ *****************************************************************************/
+
+static inline QRgb qt_conv16ToRgb(ushort c) {
+ static const int qt_rbits = (565/100);
+ static const int qt_gbits = (565/10%10);
+ static const int qt_bbits = (565%10);
+ static const int qt_red_shift = qt_bbits+qt_gbits-(8-qt_rbits);
+ static const int qt_green_shift = qt_bbits-(8-qt_gbits);
+ static const int qt_neg_blue_shift = 8-qt_bbits;
+ static const int qt_blue_mask = (1<<qt_bbits)-1;
+ static const int qt_green_mask = (1<<(qt_gbits+qt_bbits))-((1<<qt_bbits)-1);
+ static const int qt_red_mask = (1<<(qt_rbits+qt_gbits+qt_bbits))-(1<<(qt_gbits+qt_bbits));
+
+ const int r=(c & qt_red_mask);
+ const int g=(c & qt_green_mask);
+ const int b=(c & qt_blue_mask);
+ const int tr = r >> qt_red_shift;
+ const int tg = g >> qt_green_shift;
+ const int tb = b << qt_neg_blue_shift;
+
+ return qRgb(tr,tg,tb);
+}
+
+QSet<QMacPlatformPixmap*> QMacPlatformPixmap::validDataPointers;
+
+QMacPlatformPixmap::QMacPlatformPixmap(PixelType type)
+ : QPlatformPixmap(type, MacClass), has_alpha(0), has_mask(0),
+ uninit(true), pixels(0), pixelsSize(0), pixelsToFree(0),
+ bytesPerRow(0), cg_data(0), cg_dataBeingReleased(0), cg_mask(0),
+ pengine(0)
+{
+}
+
+QPlatformPixmap *QMacPlatformPixmap::createCompatiblePlatformPixmap() const
+{
+ return new QMacPlatformPixmap(pixelType());
+}
+
+#define BEST_BYTE_ALIGNMENT 16
+#define COMPTUE_BEST_BYTES_PER_ROW(bpr) \
+ (((bpr) + (BEST_BYTE_ALIGNMENT - 1)) & ~(BEST_BYTE_ALIGNMENT - 1))
+
+void QMacPlatformPixmap::resize(int width, int height)
+{
+ setSerialNumber(++qt_pixmap_serial);
+
+ w = width;
+ h = height;
+ is_null = (w <= 0 || h <= 0);
+ d = (pixelType() == BitmapType ? 1 : 32);
+ bool make_null = w <= 0 || h <= 0; // create null pixmap
+ if (make_null || d == 0) {
+ w = 0;
+ h = 0;
+ is_null = true;
+ d = 0;
+ if (!make_null)
+ qWarning("Qt: QPixmap: Invalid pixmap parameters");
+ return;
+ }
+
+ if (w < 1 || h < 1)
+ return;
+
+ //create the pixels
+ bytesPerRow = w * sizeof(quint32); // Minimum bytes per row.
+
+ // Quartz2D likes things as a multple of 16 (for now).
+ bytesPerRow = COMPTUE_BEST_BYTES_PER_ROW(bytesPerRow);
+ macCreatePixels();
+}
+
+#undef COMPUTE_BEST_BYTES_PER_ROW
+
+void QMacPlatformPixmap::fromImage(const QImage &img,
+ Qt::ImageConversionFlags flags)
+{
+ setSerialNumber(++qt_pixmap_serial);
+
+ // the conversion code only handles format >=
+ // Format_ARGB32_Premultiplied at the moment..
+ if (img.format() > QImage::Format_ARGB32_Premultiplied) {
+ QImage image;
+ if (img.hasAlphaChannel())
+ image = img.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+ else
+ image = img.convertToFormat(QImage::Format_RGB32);
+ fromImage(image, flags);
+ return;
+ }
+
+ w = img.width();
+ h = img.height();
+ is_null = (w <= 0 || h <= 0);
+ d = (pixelType() == BitmapType ? 1 : img.depth());
+
+ QImage image = img;
+ int dd = QPixmap::defaultDepth();
+ bool force_mono = (dd == 1 ||
+ (flags & Qt::ColorMode_Mask)==Qt::MonoOnly);
+ if (force_mono) { // must be monochrome
+ if (d != 1) {
+ image = image.convertToFormat(QImage::Format_MonoLSB, flags); // dither
+ d = 1;
+ }
+ } else { // can be both
+ bool conv8 = false;
+ if(d > 8 && dd <= 8) { // convert to 8 bit
+ if ((flags & Qt::DitherMode_Mask) == Qt::AutoDither)
+ flags = (flags & ~Qt::DitherMode_Mask)
+ | Qt::PreferDither;
+ conv8 = true;
+ } else if ((flags & Qt::ColorMode_Mask) == Qt::ColorOnly) {
+ conv8 = d == 1; // native depth wanted
+ } else if (d == 1) {
+ if (image.colorCount() == 2) {
+ QRgb c0 = image.color(0); // Auto: convert to best
+ QRgb c1 = image.color(1);
+ conv8 = qMin(c0,c1) != qRgb(0,0,0) || qMax(c0,c1) != qRgb(255,255,255);
+ } else {
+ // eg. 1-color monochrome images (they do exist).
+ conv8 = true;
+ }
+ }
+ if (conv8) {
+ image = image.convertToFormat(QImage::Format_Indexed8, flags);
+ d = 8;
+ }
+ }
+
+ if (image.depth()==1) {
+ image.setColor(0, QColor(Qt::color0).rgba());
+ image.setColor(1, QColor(Qt::color1).rgba());
+ }
+
+ if (d == 16 || d == 24) {
+ image = image.convertToFormat(QImage::Format_RGB32, flags);
+ fromImage(image, flags);
+ return;
+ }
+
+ // different size or depth, make a new pixmap
+ resize(w, h);
+
+ quint32 *dptr = pixels, *drow;
+ const uint dbpr = bytesPerRow;
+
+ const QImage::Format sfmt = image.format();
+ const unsigned short sbpr = image.bytesPerLine();
+
+ // use const_cast to prevent a detach
+ const uchar *sptr = const_cast<const QImage &>(image).bits(), *srow;
+
+ for (int y = 0; y < h; ++y) {
+ drow = dptr + (y * (dbpr / 4));
+ srow = sptr + (y * sbpr);
+ switch(sfmt) {
+ case QImage::Format_MonoLSB:
+ case QImage::Format_Mono:{
+ for (int x = 0; x < w; ++x) {
+ char one_bit = *(srow + (x / 8));
+ if (sfmt == QImage::Format_Mono)
+ one_bit = one_bit >> (7 - (x % 8));
+ else
+ one_bit = one_bit >> (x % 8);
+ if ((one_bit & 0x01))
+ *(drow+x) = 0xFF000000;
+ else
+ *(drow+x) = 0xFFFFFFFF;
+ }
+ break;
+ }
+ case QImage::Format_Indexed8: {
+ int numColors = image.numColors();
+ if (numColors > 0) {
+ for (int x = 0; x < w; ++x) {
+ int index = *(srow + x);
+ *(drow+x) = PREMUL(image.color(qMin(index, numColors)));
+ }
+ }
+ } break;
+ case QImage::Format_RGB32:
+ for (int x = 0; x < w; ++x)
+ *(drow+x) = *(((quint32*)srow) + x) | 0xFF000000;
+ break;
+ case QImage::Format_ARGB32:
+ case QImage::Format_ARGB32_Premultiplied:
+ for (int x = 0; x < w; ++x) {
+ if(sfmt == QImage::Format_RGB32)
+ *(drow+x) = 0xFF000000 | (*(((quint32*)srow) + x) & 0x00FFFFFF);
+ else if(sfmt == QImage::Format_ARGB32_Premultiplied)
+ *(drow+x) = *(((quint32*)srow) + x);
+ else
+ *(drow+x) = PREMUL(*(((quint32*)srow) + x));
+ }
+ break;
+ default:
+ qWarning("Qt: internal: Oops: Forgot a format [%d] %s:%d", sfmt,
+ __FILE__, __LINE__);
+ break;
+ }
+ }
+ if (sfmt != QImage::Format_RGB32) { //setup the alpha
+ bool alphamap = image.depth() == 32;
+ if (sfmt == QImage::Format_Indexed8) {
+ const QVector<QRgb> rgb = image.colorTable();
+ for (int i = 0, count = image.colorCount(); i < count; ++i) {
+ const int alpha = qAlpha(rgb[i]);
+ if (alpha != 0xff) {
+ alphamap = true;
+ break;
+ }
+ }
+ }
+ macSetHasAlpha(alphamap);
+ }
+ uninit = false;
+}
+
+int get_index(QImage * qi,QRgb mycol)
+{
+ int loopc;
+ for(loopc=0;loopc<qi->colorCount();loopc++) {
+ if(qi->color(loopc)==mycol)
+ return loopc;
+ }
+ qi->setColorCount(qi->colorCount()+1);
+ qi->setColor(qi->colorCount(),mycol);
+ return qi->colorCount();
+}
+
+QImage QMacPlatformPixmap::toImage() const
+{
+ QImage::Format format = QImage::Format_MonoLSB;
+ if (d != 1) //Doesn't support index color modes
+ format = (has_alpha ? QImage::Format_ARGB32_Premultiplied :
+ QImage::Format_RGB32);
+
+ QImage image(w, h, format);
+ quint32 *sptr = pixels, *srow;
+ const uint sbpr = bytesPerRow;
+ if (format == QImage::Format_MonoLSB) {
+ image.fill(0);
+ image.setColorCount(2);
+ image.setColor(0, QColor(Qt::color0).rgba());
+ image.setColor(1, QColor(Qt::color1).rgba());
+ for (int y = 0; y < h; ++y) {
+ uchar *scanLine = image.scanLine(y);
+ srow = sptr + (y * (sbpr/4));
+ for (int x = 0; x < w; ++x) {
+ if (!(*(srow + x) & RGB_MASK))
+ scanLine[x >> 3] |= (1 << (x & 7));
+ }
+ }
+ } else {
+ for (int y = 0; y < h; ++y) {
+ srow = sptr + (y * (sbpr / 4));
+ memcpy(image.scanLine(y), srow, w * 4);
+ }
+
+ }
+
+ return image;
+}
+
+void QMacPlatformPixmap::fill(const QColor &fillColor)
+
+{
+ { //we don't know what backend to use so we cannot paint here
+ quint32 *dptr = pixels;
+ Q_ASSERT_X(dptr, "QPixmap::fill", "No dptr");
+ const quint32 colr = PREMUL(fillColor.rgba());
+ const int nbytes = bytesPerRow * h;
+ if (!colr) {
+ memset(dptr, 0, nbytes);
+ } else {
+ for (uint i = 0; i < nbytes / sizeof(quint32); ++i)
+ *(dptr + i) = colr;
+ }
+ }
+
+ // If we had an alpha channel from before, don't
+ // switch it off. Only go from no alpha to alpha:
+ if (fillColor.alpha() != 255)
+ macSetHasAlpha(true);
+}
+
+QPixmap QMacPlatformPixmap::alphaChannel() const
+{
+ if (!has_alpha)
+ return QPixmap();
+
+ QMacPlatformPixmap *alpha = new QMacPlatformPixmap(PixmapType);
+ alpha->resize(w, h);
+ macGetAlphaChannel(alpha, false);
+ return QPixmap(alpha);
+}
+
+void QMacPlatformPixmap::setAlphaChannel(const QPixmap &alpha)
+{
+ has_mask = true;
+ QMacPlatformPixmap *alphaData = static_cast<QMacPlatformPixmap*>(alpha.data.data());
+ macSetAlphaChannel(alphaData, false);
+}
+
+QBitmap QMacPlatformPixmap::mask() const
+{
+ if (!has_mask && !has_alpha)
+ return QBitmap();
+
+ QMacPlatformPixmap *mask = new QMacPlatformPixmap(BitmapType);
+ mask->resize(w, h);
+ macGetAlphaChannel(mask, true);
+ return QPixmap(mask);
+}
+
+void QMacPlatformPixmap::setMask(const QBitmap &mask)
+{
+ if (mask.isNull()) {
+ QMacPlatformPixmap opaque(PixmapType);
+ opaque.resize(w, h);
+ opaque.fill(QColor(255, 255, 255, 255));
+ macSetAlphaChannel(&opaque, true);
+ has_alpha = has_mask = false;
+ return;
+ }
+
+ has_alpha = false;
+ has_mask = true;
+ QMacPlatformPixmap *maskData = static_cast<QMacPlatformPixmap*>(mask.data.data());
+ macSetAlphaChannel(maskData, true);
+}
+
+int QMacPlatformPixmap::metric(QPaintDevice::PaintDeviceMetric theMetric) const
+{
+ switch (theMetric) {
+ case QPaintDevice::PdmWidth:
+ return w;
+ case QPaintDevice::PdmHeight:
+ return h;
+ case QPaintDevice::PdmWidthMM:
+ return qRound(metric(QPaintDevice::PdmWidth) * 25.4 / qreal(metric(QPaintDevice::PdmDpiX)));
+ case QPaintDevice::PdmHeightMM:
+ return qRound(metric(QPaintDevice::PdmHeight) * 25.4 / qreal(metric(QPaintDevice::PdmDpiY)));
+ case QPaintDevice::PdmNumColors:
+ return 1 << d;
+ case QPaintDevice::PdmDpiX:
+ case QPaintDevice::PdmPhysicalDpiX: {
+ extern float qt_mac_defaultDpi_x(); //qpaintdevice_mac.cpp
+ return int(qt_mac_defaultDpi_x());
+ }
+ case QPaintDevice::PdmDpiY:
+ case QPaintDevice::PdmPhysicalDpiY: {
+ extern float qt_mac_defaultDpi_y(); //qpaintdevice_mac.cpp
+ return int(qt_mac_defaultDpi_y());
+ }
+ case QPaintDevice::PdmDepth:
+ return d;
+ default:
+ qWarning("QPixmap::metric: Invalid metric command");
+ }
+ return 0;
+}
+
+QMacPlatformPixmap::~QMacPlatformPixmap()
+{
+ validDataPointers.remove(this);
+ if (cg_mask) {
+ CGImageRelease(cg_mask);
+ cg_mask = 0;
+ }
+
+ delete pengine; // Make sure we aren't drawing on the context anymore.
+ if (cg_data) {
+ CGImageRelease(cg_data);
+ } else if (!cg_dataBeingReleased && pixels != pixelsToFree) {
+ free(pixels);
+ }
+ free(pixelsToFree);
+}
+
+void QMacPlatformPixmap::macSetAlphaChannel(const QMacPlatformPixmap *pix, bool asMask)
+{
+ if (!pixels || !h || !w || pix->w != w || pix->h != h)
+ return;
+
+ quint32 *dptr = pixels, *drow;
+ const uint dbpr = bytesPerRow;
+ const unsigned short sbpr = pix->bytesPerRow;
+ quint32 *sptr = pix->pixels, *srow;
+ for (int y=0; y < h; ++y) {
+ drow = dptr + (y * (dbpr/4));
+ srow = sptr + (y * (sbpr/4));
+ if(d == 1) {
+ for (int x=0; x < w; ++x) {
+ if((*(srow+x) & RGB_MASK))
+ *(drow+x) = 0xFFFFFFFF;
+ }
+ } else if(d == 8) {
+ for (int x=0; x < w; ++x)
+ *(drow+x) = (*(drow+x) & RGB_MASK) | (*(srow+x) << 24);
+ } else if(asMask) {
+ for (int x=0; x < w; ++x) {
+ if(*(srow+x) & RGB_MASK)
+ *(drow+x) = (*(drow+x) & RGB_MASK);
+ else
+ *(drow+x) = (*(drow+x) & RGB_MASK) | 0xFF000000;
+ *(drow+x) = PREMUL(*(drow+x));
+ }
+ } else {
+ for (int x=0; x < w; ++x) {
+ const uchar alpha = qGray(qRed(*(srow+x)), qGreen(*(srow+x)), qBlue(*(srow+x)));
+ const uchar destAlpha = qt_div_255(alpha * qAlpha(*(drow+x)));
+#if 1
+ *(drow+x) = (*(drow+x) & RGB_MASK) | (destAlpha << 24);
+#else
+ *(drow+x) = qRgba(qt_div_255(qRed(*(drow+x) * alpha)),
+ qt_div_255(qGreen(*(drow+x) * alpha)),
+ qt_div_255(qBlue(*(drow+x) * alpha)), destAlpha);
+#endif
+ *(drow+x) = PREMUL(*(drow+x));
+ }
+ }
+ }
+ macSetHasAlpha(true);
+}
+
+void QMacPlatformPixmap::macGetAlphaChannel(QMacPlatformPixmap *pix, bool asMask) const
+{
+ quint32 *dptr = pix->pixels, *drow;
+ const uint dbpr = pix->bytesPerRow;
+ const unsigned short sbpr = bytesPerRow;
+ quint32 *sptr = pixels, *srow;
+ for(int y=0; y < h; ++y) {
+ drow = dptr + (y * (dbpr/4));
+ srow = sptr + (y * (sbpr/4));
+ if(asMask) {
+ for (int x = 0; x < w; ++x) {
+ if (*(srow + x) & qRgba(0, 0, 0, 255))
+ *(drow + x) = 0x00000000;
+ else
+ *(drow + x) = 0xFFFFFFFF;
+ }
+ } else {
+ for (int x = 0; x < w; ++x) {
+ const int alpha = qAlpha(*(srow + x));
+ *(drow + x) = qRgb(alpha, alpha, alpha);
+ }
+ }
+ }
+}
+
+void QMacPlatformPixmap::macSetHasAlpha(bool b)
+{
+ has_alpha = b;
+ macReleaseCGImageRef();
+}
+
+void QMacPlatformPixmap::macCreateCGImageRef()
+{
+ Q_ASSERT(cg_data == 0);
+ //create the cg data
+ CGColorSpaceRef colorspace = QCoreGraphicsPaintEngine::macDisplayColorSpace();
+ QCFType<CGDataProviderRef> provider = CGDataProviderCreateWithData(this,
+ pixels, bytesPerRow * h,
+ qt_mac_cgimage_data_free);
+ validDataPointers.insert(this);
+ uint cgflags = kCGImageAlphaPremultipliedFirst;
+#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version
+ cgflags |= kCGBitmapByteOrder32Host;
+#endif
+ cg_data = CGImageCreate(w, h, 8, 32, bytesPerRow, colorspace,
+ cgflags, provider, 0, 0, kCGRenderingIntentDefault);
+}
+
+void QMacPlatformPixmap::macReleaseCGImageRef()
+{
+ if (!cg_data)
+ return; // There's nothing we need to do
+
+ cg_dataBeingReleased = cg_data;
+ CGImageRelease(cg_data);
+ cg_data = 0;
+
+ if (pixels != pixelsToFree) {
+ macCreatePixels();
+ } else {
+ pixelsToFree = 0;
+ }
+}
+
+
+// We create our space in memory to paint on here. If we already have existing pixels
+// copy them over. This is to preserve the fact that CGImageRef's are immutable.
+void QMacPlatformPixmap::macCreatePixels()
+{
+ const int numBytes = bytesPerRow * h;
+ quint32 *base_pixels;
+ if (pixelsToFree && pixelsToFree != pixels) {
+ // Reuse unused block of memory lying around from a previous callback.
+ base_pixels = pixelsToFree;
+ pixelsToFree = 0;
+ } else {
+ // We need a block of memory to do stuff with.
+ base_pixels = static_cast<quint32 *>(malloc(numBytes));
+ }
+
+ if (pixels)
+ memcpy(base_pixels, pixels, pixelsSize);
+ pixels = base_pixels;
+ pixelsSize = numBytes;
+}
+
+#if 0
+QPixmap QMacPlatformPixmap::transformed(const QTransform &transform,
+ Qt::TransformationMode mode) const
+{
+ int w, h; // size of target pixmap
+ const int ws = width();
+ const int hs = height();
+
+ QTransform mat(transform.m11(), transform.m12(),
+ transform.m21(), transform.m22(), 0., 0.);
+ if (transform.m12() == 0.0F && transform.m21() == 0.0F &&
+ transform.m11() >= 0.0F && transform.m22() >= 0.0F)
+ {
+ h = int(qAbs(mat.m22()) * hs + 0.9999);
+ w = int(qAbs(mat.m11()) * ws + 0.9999);
+ h = qAbs(h);
+ w = qAbs(w);
+ } else { // rotation or shearing
+ QPolygonF a(QRectF(0,0,ws+1,hs+1));
+ a = mat.map(a);
+ QRectF r = a.boundingRect().normalized();
+ w = int(r.width() + 0.9999);
+ h = int(r.height() + 0.9999);
+ }
+ mat = QPixmap::trueMatrix(mat, ws, hs);
+ if (!h || !w)
+ return QPixmap();
+
+ // create destination
+ QMacPlatformPixmap *pm = new QMacPlatformPixmap(pixelType(), w, h);
+ const quint32 *sptr = pixels;
+ quint32 *dptr = pm->pixels;
+ memset(dptr, 0, (pm->bytesPerRow * pm->h));
+
+ // do the transform
+ if (mode == Qt::SmoothTransformation) {
+#warning QMacPlatformPixmap::transformed not properly implemented
+ qWarning("QMacPlatformPixmap::transformed not properly implemented");
+#if 0
+ QPainter p(&pm);
+ p.setRenderHint(QPainter::Antialiasing);
+ p.setRenderHint(QPainter::SmoothPixmapTransform);
+ p.setTransform(mat);
+ p.drawPixmap(0, 0, *this);
+#endif
+ } else {
+ bool invertible;
+ mat = mat.inverted(&invertible);
+ if (!invertible)
+ return QPixmap();
+
+ const int bpp = 32;
+ const int xbpl = (w * bpp) / 8;
+ if (!qt_xForm_helper(mat, 0, QT_XFORM_TYPE_MSBFIRST, bpp,
+ (uchar*)dptr, xbpl, (pm->bytesPerRow) - xbpl,
+ h, (uchar*)sptr, (bytesPerRow), ws, hs)) {
+ qWarning("QMacPlatformPixmap::transform(): failure");
+ return QPixmap();
+ }
+ }
+
+ // update the alpha
+ pm->macSetHasAlpha(true);
+ return QPixmap(pm);
+}
+#endif
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <OpenGL/OpenGL.h>
+#include <OpenGL/gl.h>
+QT_END_INCLUDE_NAMESPACE
+
+// Load and resolve the symbols we need from OpenGL manually so QtGui doesn't have to link against the OpenGL framework.
+typedef CGLError (*PtrCGLChoosePixelFormat)(const CGLPixelFormatAttribute *, CGLPixelFormatObj *, long *);
+typedef CGLError (*PtrCGLClearDrawable)(CGLContextObj);
+typedef CGLError (*PtrCGLCreateContext)(CGLPixelFormatObj, CGLContextObj, CGLContextObj *);
+typedef CGLError (*PtrCGLDestroyContext)(CGLContextObj);
+typedef CGLError (*PtrCGLDestroyPixelFormat)(CGLPixelFormatObj);
+typedef CGLError (*PtrCGLSetCurrentContext)(CGLContextObj);
+typedef CGLError (*PtrCGLSetFullScreen)(CGLContextObj);
+typedef void (*PtrglFinish)();
+typedef void (*PtrglPixelStorei)(GLenum, GLint);
+typedef void (*PtrglReadBuffer)(GLenum);
+typedef void (*PtrglReadPixels)(GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, GLvoid *);
+
+static PtrCGLChoosePixelFormat ptrCGLChoosePixelFormat = 0;
+static PtrCGLClearDrawable ptrCGLClearDrawable = 0;
+static PtrCGLCreateContext ptrCGLCreateContext = 0;
+static PtrCGLDestroyContext ptrCGLDestroyContext = 0;
+static PtrCGLDestroyPixelFormat ptrCGLDestroyPixelFormat = 0;
+static PtrCGLSetCurrentContext ptrCGLSetCurrentContext = 0;
+static PtrCGLSetFullScreen ptrCGLSetFullScreen = 0;
+static PtrglFinish ptrglFinish = 0;
+static PtrglPixelStorei ptrglPixelStorei = 0;
+static PtrglReadBuffer ptrglReadBuffer = 0;
+static PtrglReadPixels ptrglReadPixels = 0;
+
+static bool resolveOpenGLSymbols()
+{
+ if (ptrCGLChoosePixelFormat == 0) {
+ QLibrary library(QLatin1String("/System/Library/Frameworks/OpenGL.framework/OpenGL"));
+ ptrCGLChoosePixelFormat = (PtrCGLChoosePixelFormat)(library.resolve("CGLChoosePixelFormat"));
+ ptrCGLClearDrawable = (PtrCGLClearDrawable)(library.resolve("CGLClearDrawable"));
+ ptrCGLCreateContext = (PtrCGLCreateContext)(library.resolve("CGLCreateContext"));
+ ptrCGLDestroyContext = (PtrCGLDestroyContext)(library.resolve("CGLDestroyContext"));
+ ptrCGLDestroyPixelFormat = (PtrCGLDestroyPixelFormat)(library.resolve("CGLDestroyPixelFormat"));
+ ptrCGLSetCurrentContext = (PtrCGLSetCurrentContext)(library.resolve("CGLSetCurrentContext"));
+ ptrCGLSetFullScreen = (PtrCGLSetFullScreen)(library.resolve("CGLSetFullScreen"));
+ ptrglFinish = (PtrglFinish)(library.resolve("glFinish"));
+ ptrglPixelStorei = (PtrglPixelStorei)(library.resolve("glPixelStorei"));
+ ptrglReadBuffer = (PtrglReadBuffer)(library.resolve("glReadBuffer"));
+ ptrglReadPixels = (PtrglReadPixels)(library.resolve("glReadPixels"));
+ }
+ return ptrCGLChoosePixelFormat && ptrCGLClearDrawable && ptrCGLCreateContext
+ && ptrCGLDestroyContext && ptrCGLDestroyPixelFormat && ptrCGLSetCurrentContext
+ && ptrCGLSetFullScreen && ptrglFinish && ptrglPixelStorei
+ && ptrglReadBuffer && ptrglReadPixels;
+}
+
+// Inverts the given pixmap in the y direction.
+static void qt_mac_flipPixmap(void *data, int rowBytes, int height)
+{
+ int bottom = height - 1;
+ void *base = data;
+ void *buffer = malloc(rowBytes);
+
+ int top = 0;
+ while ( top < bottom )
+ {
+ void *topP = (void *)((top * rowBytes) + (intptr_t)base);
+ void *bottomP = (void *)((bottom * rowBytes) + (intptr_t)base);
+
+ bcopy( topP, buffer, rowBytes );
+ bcopy( bottomP, topP, rowBytes );
+ bcopy( buffer, bottomP, rowBytes );
+
+ ++top;
+ --bottom;
+ }
+ free(buffer);
+}
+
+// Grabs displayRect from display and places it into buffer.
+static void qt_mac_grabDisplayRect(CGDirectDisplayID display, const QRect &displayRect, void *buffer)
+{
+ if (display == kCGNullDirectDisplay)
+ return;
+
+ CGLPixelFormatAttribute attribs[] = {
+ kCGLPFAFullScreen,
+ kCGLPFADisplayMask,
+ (CGLPixelFormatAttribute)0, /* Display mask bit goes here */
+ (CGLPixelFormatAttribute)0
+ };
+
+ attribs[2] = (CGLPixelFormatAttribute)CGDisplayIDToOpenGLDisplayMask(display);
+
+ // Build a full-screen GL context
+ CGLPixelFormatObj pixelFormatObj;
+ long numPixelFormats;
+
+ ptrCGLChoosePixelFormat( attribs, &pixelFormatObj, &numPixelFormats );
+
+ if (!pixelFormatObj) // No full screen context support
+ return;
+
+ CGLContextObj glContextObj;
+ ptrCGLCreateContext(pixelFormatObj, 0, &glContextObj);
+ ptrCGLDestroyPixelFormat(pixelFormatObj) ;
+ if (!glContextObj)
+ return;
+
+ ptrCGLSetCurrentContext(glContextObj);
+ ptrCGLSetFullScreen(glContextObj) ;
+
+ ptrglReadBuffer(GL_FRONT);
+
+ ptrglFinish(); // Finish all OpenGL commands
+ ptrglPixelStorei(GL_PACK_ALIGNMENT, 4); // Force 4-byte alignment
+ ptrglPixelStorei(GL_PACK_ROW_LENGTH, 0);
+ ptrglPixelStorei(GL_PACK_SKIP_ROWS, 0);
+ ptrglPixelStorei(GL_PACK_SKIP_PIXELS, 0);
+
+ // Fetch the data in XRGB format, matching the bitmap context.
+ ptrglReadPixels(GLint(displayRect.x()), GLint(displayRect.y()),
+ GLint(displayRect.width()), GLint(displayRect.height()),
+#ifdef __BIG_ENDIAN__
+ GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer
+#else
+ GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, buffer
+#endif
+ );
+
+ ptrCGLSetCurrentContext(0);
+ ptrCGLClearDrawable(glContextObj); // disassociate from full screen
+ ptrCGLDestroyContext(glContextObj); // and destroy the context
+}
+
+// Returns a pixmap containing the screen contents at rect.
+static QPixmap qt_mac_grabScreenRect(const QRect &rect)
+{
+ if (!resolveOpenGLSymbols())
+ return QPixmap();
+
+ const int maxDisplays = 128; // 128 displays should be enough for everyone.
+ CGDirectDisplayID displays[maxDisplays];
+ CGDisplayCount displayCount;
+ const CGRect cgRect = CGRectMake(rect.x(), rect.y(), rect.width(), rect.height());
+ const CGDisplayErr err = CGGetDisplaysWithRect(cgRect, maxDisplays, displays, &displayCount);
+
+ if (err && displayCount == 0)
+ return QPixmap();
+
+ long bytewidth = rect.width() * 4; // Assume 4 bytes/pixel for now
+ bytewidth = (bytewidth + 3) & ~3; // Align to 4 bytes
+ QVarLengthArray<char> buffer(rect.height() * bytewidth);
+
+ for (uint i = 0; i < displayCount; ++i) {
+ const CGRect bounds = CGDisplayBounds(displays[i]);
+ // Translate to display-local coordinates
+ QRect displayRect = rect.translated(qRound(-bounds.origin.x), qRound(-bounds.origin.y));
+ // Adjust for inverted y axis.
+ displayRect.moveTop(qRound(bounds.size.height) - displayRect.y() - rect.height());
+ qt_mac_grabDisplayRect(displays[i], displayRect, buffer.data());
+ }
+
+ qt_mac_flipPixmap(buffer.data(), bytewidth, rect.height());
+ QCFType<CGContextRef> bitmap = CGBitmapContextCreate(buffer.data(), rect.width(),
+ rect.height(), 8, bytewidth,
+ QCoreGraphicsPaintEngine::macGenericColorSpace(),
+ kCGImageAlphaNoneSkipFirst);
+ QCFType<CGImageRef> image = CGBitmapContextCreateImage(bitmap);
+ return QPixmap::fromMacCGImageRef(image);
+}
+
+#ifndef QT_MAC_USE_COCOA // no QuickDraw in 64-bit mode
+static QPixmap qt_mac_grabScreenRect_10_3(int x, int y, int w, int h, QWidget *widget)
+{
+ QPixmap pm = QPixmap(w, h);
+ extern WindowPtr qt_mac_window_for(const QWidget *); // qwidget_mac.cpp
+ const BitMap *windowPort = 0;
+ if((widget->windowType() == Qt::Desktop)) {
+ GDHandle gdh;
+ if(!(gdh=GetMainDevice()))
+ qDebug("Qt: internal: Unexpected condition reached: %s:%d", __FILE__, __LINE__);
+ windowPort = (BitMap*)(*(*gdh)->gdPMap);
+ } else {
+ windowPort = GetPortBitMapForCopyBits(GetWindowPort(qt_mac_window_for(widget)));
+ }
+ const BitMap *pixmapPort = GetPortBitMapForCopyBits(static_cast<GWorldPtr>(pm.macQDHandle()));
+ Rect macSrcRect, macDstRect;
+ SetRect(&macSrcRect, x, y, x + w, y + h);
+ SetRect(&macDstRect, 0, 0, w, h);
+ CopyBits(windowPort, pixmapPort, &macSrcRect, &macDstRect, srcCopy, 0);
+ return pm;
+}
+#endif
+
+QPixmap QPixmap::grabWindow(WId window, int x, int y, int w, int h)
+{
+ QWidget *widget = QWidget::find(window);
+ if (widget == 0)
+ return QPixmap();
+
+ if(w == -1)
+ w = widget->width() - x;
+ if(h == -1)
+ h = widget->height() - y;
+
+ QPoint globalCoord(0, 0);
+ globalCoord = widget->mapToGlobal(globalCoord);
+ QRect rect(globalCoord.x() + x, globalCoord.y() + y, w, h);
+
+#ifdef QT_MAC_USE_COCOA
+ return qt_mac_grabScreenRect(rect);
+#else
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
+ return qt_mac_grabScreenRect(rect);
+ } else
+#endif
+ {
+ return qt_mac_grabScreenRect_10_3(x, y, w, h, widget);
+ }
+#endif // ifdef Q_WS_MAC64
+}
+
+/*! \internal
+
+ Returns the QuickDraw CGrafPtr of the pixmap. 0 is returned if it can't
+ be obtained. Do not hold the pointer around for long as it can be
+ relocated.
+
+ \warning This function is only available on Mac OS X.
+ \warning As of Qt 4.6, this function \e{always} returns zero.
+*/
+
+Qt::HANDLE QPixmap::macQDHandle() const
+{
+ return 0;
+}
+
+/*! \internal
+
+ Returns the QuickDraw CGrafPtr of the pixmap's alpha channel. 0 is
+ returned if it can't be obtained. Do not hold the pointer around for
+ long as it can be relocated.
+
+ \warning This function is only available on Mac OS X.
+ \warning As of Qt 4.6, this function \e{always} returns zero.
+*/
+
+Qt::HANDLE QPixmap::macQDAlphaHandle() const
+{
+ return 0;
+}
+
+/*! \internal
+
+ Returns the CoreGraphics CGContextRef of the pixmap. 0 is returned if
+ it can't be obtained. It is the caller's responsiblity to
+ CGContextRelease the context when finished using it.
+
+ \warning This function is only available on Mac OS X.
+*/
+
+Qt::HANDLE QPixmap::macCGHandle() const
+{
+ if (isNull())
+ return 0;
+
+ if (data->classId() == QPlatformPixmap::MacClass) {
+ QMacPlatformPixmap *d = static_cast<QMacPlatformPixmap *>(data.data());
+ if (!d->cg_data)
+ d->macCreateCGImageRef();
+ CGImageRef ret = d->cg_data;
+ CGImageRetain(ret);
+ return ret;
+ } else if (data->classId() == QPlatformPixmap::RasterClass) {
+ return qt_mac_image_to_cgimage(static_cast<QRasterPlatformPixmap *>(data.data())->image);
+ }
+ return 0;
+}
+
+bool QMacPlatformPixmap::hasAlphaChannel() const
+{
+ return has_alpha;
+}
+
+CGImageRef qt_mac_create_imagemask(const QPixmap &pixmap, const QRectF &sr)
+{
+ QMacPlatformPixmap *px = static_cast<QMacPlatformPixmap*>(pixmap.data.data());
+ if (px->cg_mask) {
+ if (px->cg_mask_rect == sr) {
+ CGImageRetain(px->cg_mask); //reference for the caller
+ return px->cg_mask;
+ }
+ CGImageRelease(px->cg_mask);
+ px->cg_mask = 0;
+ }
+
+ const int sx = qRound(sr.x()), sy = qRound(sr.y()), sw = qRound(sr.width()), sh = qRound(sr.height());
+ const int sbpr = px->bytesPerRow;
+ const uint nbytes = sw * sh;
+ // alpha is always 255 for bitmaps, ignore it in this case.
+ const quint32 mask = px->depth() == 1 ? 0x00ffffff : 0xffffffff;
+ quint8 *dptr = static_cast<quint8 *>(malloc(nbytes));
+ quint32 *sptr = px->pixels, *srow;
+ for(int y = sy, offset=0; y < sh; ++y) {
+ srow = sptr + (y * (sbpr / 4));
+ for(int x = sx; x < sw; ++x)
+ *(dptr+(offset++)) = (*(srow+x) & mask) ? 255 : 0;
+ }
+ QCFType<CGDataProviderRef> provider = CGDataProviderCreateWithData(0, dptr, nbytes, qt_mac_cgimage_data_free);
+ px->cg_mask = CGImageMaskCreate(sw, sh, 8, 8, nbytes / sh, provider, 0, 0);
+ px->cg_mask_rect = sr;
+ CGImageRetain(px->cg_mask); //reference for the caller
+ return px->cg_mask;
+}
+
+#ifndef QT_MAC_USE_COCOA
+IconRef qt_mac_create_iconref(const QPixmap &px)
+{
+ if (px.isNull())
+ return 0;
+
+ //create icon
+ IconFamilyHandle iconFamily = reinterpret_cast<IconFamilyHandle>(NewHandle(0));
+ //create data
+ {
+ struct {
+ OSType mac_type;
+ int width, height, depth;
+ bool mask;
+ } images[] = {
+ { kThumbnail32BitData, 128, 128, 32, false },
+ { kThumbnail8BitMask, 128, 128, 8, true },
+ { 0, 0, 0, 0, false } //end marker
+ };
+ for(int i = 0; images[i].mac_type; i++) {
+ //get QPixmap data
+ QImage scaled_px = px.toImage().scaled(images[i].width, images[i].height);
+
+ quint32 *sptr = (quint32 *) scaled_px.bits();
+ quint32 *srow;
+ uint sbpr = scaled_px.bytesPerLine();
+
+ //get Handle data
+ const int dbpr = images[i].width * (images[i].depth/8);
+ Handle hdl = NewHandle(dbpr*images[i].height);
+ if(!sptr) { //handle null pixmap
+ memset((*hdl), '\0', dbpr*images[i].height);
+ } else if(images[i].mask) {
+ if(images[i].mac_type == kThumbnail8BitMask) {
+ for(int y = 0, hindex = 0; y < images[i].height; ++y) {
+ srow = sptr + (y * (sbpr/4));
+ for(int x = 0; x < images[i].width; ++x)
+ *((*hdl)+(hindex++)) = qAlpha(*(srow+x));
+ }
+ }
+ } else {
+ char *dest = (*hdl);
+#if defined(__i386__)
+ if(images[i].depth == 32) {
+ for(int y = 0; y < images[i].height; ++y) {
+ uint *source = (uint*)((const uchar*)sptr+(sbpr*y));
+ for(int x = 0; x < images[i].width; ++x, dest += 4)
+ *((uint*)dest) = CFSwapInt32(*(source + x));
+ }
+ } else
+#endif
+ {
+ for(int y = 0; y < images[i].height; ++y)
+ memcpy(dest+(y*dbpr), ((const uchar*)sptr+(sbpr*y)), dbpr);
+ }
+ }
+
+ //set the family data to the Handle
+ OSStatus set = SetIconFamilyData(iconFamily, images[i].mac_type, hdl);
+ if(set != noErr)
+ qWarning("%s: %d -- Unable to create icon data[%d]!! %ld",
+ __FILE__, __LINE__, i, long(set));
+ DisposeHandle(hdl);
+ }
+ }
+
+ //acquire and cleanup
+ IconRef ret;
+ static int counter = 0;
+ const OSType kQtCreator = 'CUTE';
+ RegisterIconRefFromIconFamily(kQtCreator, (OSType)counter, iconFamily, &ret);
+ AcquireIconRef(ret);
+ UnregisterIconRef(kQtCreator, (OSType)counter);
+ DisposeHandle(reinterpret_cast<Handle>(iconFamily));
+ counter++;
+ return ret;
+
+}
+#endif
+
+/*! \internal */
+QPaintEngine* QMacPlatformPixmap::paintEngine() const
+{
+ if (!pengine) {
+ QMacPlatformPixmap *that = const_cast<QMacPlatformPixmap*>(this);
+ that->pengine = new QCoreGraphicsPaintEngine();
+ }
+ return pengine;
+}
+
+void QMacPlatformPixmap::copy(const QPlatformPixmap *data, const QRect &rect)
+{
+ if (data->pixelType() == BitmapType) {
+ QBitmap::fromImage(toImage().copy(rect));
+ return;
+ }
+
+ const QMacPlatformPixmap *macData = static_cast<const QMacPlatformPixmap*>(data);
+
+ resize(rect.width(), rect.height());
+
+ has_alpha = macData->has_alpha;
+ has_mask = macData->has_mask;
+ uninit = false;
+
+ const int x = rect.x();
+ const int y = rect.y();
+ char *dest = reinterpret_cast<char*>(pixels);
+ const char *src = reinterpret_cast<const char*>(macData->pixels + x) + y * macData->bytesPerRow;
+ for (int i = 0; i < h; ++i) {
+ memcpy(dest, src, w * 4);
+ dest += bytesPerRow;
+ src += macData->bytesPerRow;
+ }
+
+ has_alpha = macData->has_alpha;
+ has_mask = macData->has_mask;
+}
+
+bool QMacPlatformPixmap::scroll(int dx, int dy, const QRect &rect)
+{
+ Q_UNUSED(dx);
+ Q_UNUSED(dy);
+ Q_UNUSED(rect);
+ return false;
+}
+
+/*!
+ \since 4.2
+
+ Creates a \c CGImageRef equivalent to the QPixmap. Returns the \c CGImageRef handle.
+
+ It is the caller's responsibility to release the \c CGImageRef data
+ after use.
+
+ \warning This function is only available on Mac OS X.
+
+ \sa fromMacCGImageRef()
+*/
+CGImageRef QPixmap::toMacCGImageRef() const
+{
+ return (CGImageRef)macCGHandle();
+}
+
+/*!
+ \since 4.2
+
+ Returns a QPixmap that is equivalent to the given \a image.
+
+ \warning This function is only available on Mac OS X.
+
+ \sa toMacCGImageRef(), {QPixmap#Pixmap Conversion}{Pixmap Conversion}
+*/
+QPixmap QPixmap::fromMacCGImageRef(CGImageRef image)
+{
+ const size_t w = CGImageGetWidth(image),
+ h = CGImageGetHeight(image);
+ QPixmap ret(w, h);
+ ret.fill(Qt::transparent);
+ CGRect rect = CGRectMake(0, 0, w, h);
+ CGContextRef ctx = qt_mac_cg_context(&ret);
+ qt_mac_drawCGImage(ctx, &rect, image);
+ CGContextRelease(ctx);
+ return ret;
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/mac/qpixmap_mac_p.h b/src/widgets/platforms/mac/qpixmap_mac_p.h
new file mode 100644
index 0000000000..4f53160074
--- /dev/null
+++ b/src/widgets/platforms/mac/qpixmap_mac_p.h
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPIXMAP_MAC_P_H
+#define QPIXMAP_MAC_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/qplatformpixmap_qpa.h>
+#include <QtGui/qplatformpixmapfactory_p.h>
+#include <QtGui/private/qt_mac_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QMacPlatformPixmap : public QPlatformPixmap
+{
+public:
+ QMacPlatformPixmap(PixelType type);
+ ~QMacPlatformPixmap();
+
+ QPlatformPixmap *createCompatiblePlatformPixmap() const;
+
+ void resize(int width, int height);
+ void fromImage(const QImage &image, Qt::ImageConversionFlags flags);
+ void copy(const QPlatformPixmap *data, const QRect &rect);
+ bool scroll(int dx, int dy, const QRect &rect);
+
+ int metric(QPaintDevice::PaintDeviceMetric metric) const;
+ void fill(const QColor &color);
+ QBitmap mask() const;
+ void setMask(const QBitmap &mask);
+ bool hasAlphaChannel() const;
+// QPixmap transformed(const QTransform &matrix,
+// Qt::TransformationMode mode) const;
+ void setAlphaChannel(const QPixmap &alphaChannel);
+ QPixmap alphaChannel() const;
+ QImage toImage() const;
+ QPaintEngine* paintEngine() const;
+
+private:
+
+ uint has_alpha : 1, has_mask : 1, uninit : 1;
+
+ void macSetHasAlpha(bool b);
+ void macGetAlphaChannel(QMacPlatformPixmap *, bool asMask) const;
+ void macSetAlphaChannel(const QMacPlatformPixmap *, bool asMask);
+ void macCreateCGImageRef();
+ void macCreatePixels();
+ void macReleaseCGImageRef();
+ /*
+ pixels stores the pixmap data. pixelsToFree is either 0 or some memory
+ block that was bound to a CGImageRef and released, and for which the
+ release callback has been called. There are two uses to pixelsToFree:
+
+ 1. If pixels == pixelsToFree, then we know that the CGImageRef is done\
+ with the data and we can modify pixels without breaking CGImageRef's
+ mutability invariant.
+
+ 2. If pixels != pixelsToFree and pixelsToFree != 0, then we can reuse
+ pixelsToFree later on instead of malloc'ing memory.
+ */
+ quint32 *pixels;
+ uint pixelsSize;
+ quint32 *pixelsToFree;
+ uint bytesPerRow;
+ QRectF cg_mask_rect;
+ CGImageRef cg_data, cg_dataBeingReleased, cg_mask;
+ static QSet<QMacPlatformPixmap*> validDataPointers;
+
+ QPaintEngine *pengine;
+
+ friend class QPixmap;
+ friend class QRasterBuffer;
+ friend class QRasterPaintEngine;
+ friend class QCoreGraphicsPaintEngine;
+ friend CGImageRef qt_mac_create_imagemask(const QPixmap&, const QRectF&);
+ friend quint32 *qt_mac_pixmap_get_base(const QPixmap*);
+ friend int qt_mac_pixmap_get_bytes_per_line(const QPixmap*);
+ friend void qt_mac_cgimage_data_free(void *, const void*, size_t);
+ friend IconRef qt_mac_create_iconref(const QPixmap&);
+ friend CGContextRef qt_mac_cg_context(const QPaintDevice*);
+ friend QColor qcolorForThemeTextColor(ThemeTextColor themeColor);
+};
+
+QT_END_NAMESPACE
+
+#endif // QPIXMAP_MAC_P_H
diff --git a/src/widgets/platforms/mac/qprintengine_mac.mm b/src/widgets/platforms/mac/qprintengine_mac.mm
new file mode 100644
index 0000000000..fb40677e2d
--- /dev/null
+++ b/src/widgets/platforms/mac/qprintengine_mac.mm
@@ -0,0 +1,911 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qprintengine_mac_p.h>
+#include <qdebug.h>
+#include <qthread.h>
+#include <QtCore/qcoreapplication.h>
+
+#ifndef QT_NO_PRINTER
+
+QT_BEGIN_NAMESPACE
+
+extern QSizeF qt_paperSizeToQSizeF(QPrinter::PaperSize size);
+
+QMacPrintEngine::QMacPrintEngine(QPrinter::PrinterMode mode) : QPaintEngine(*(new QMacPrintEnginePrivate))
+{
+ Q_D(QMacPrintEngine);
+ d->mode = mode;
+ d->initialize();
+}
+
+bool QMacPrintEngine::begin(QPaintDevice *dev)
+{
+ Q_D(QMacPrintEngine);
+
+ Q_ASSERT(dev && dev->devType() == QInternal::Printer);
+ if (!static_cast<QPrinter *>(dev)->isValid())
+ return false;
+
+ if (d->state == QPrinter::Idle && !d->isPrintSessionInitialized()) // Need to reinitialize
+ d->initialize();
+
+ d->paintEngine->state = state;
+ d->paintEngine->begin(dev);
+ Q_ASSERT_X(d->state == QPrinter::Idle, "QMacPrintEngine", "printer already active");
+
+ if (PMSessionValidatePrintSettings(d->session, d->settings, kPMDontWantBoolean) != noErr
+ || PMSessionValidatePageFormat(d->session, d->format, kPMDontWantBoolean) != noErr) {
+ d->state = QPrinter::Error;
+ return false;
+ }
+
+ if (!d->outputFilename.isEmpty()) {
+ QCFType<CFURLRef> outFile = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault,
+ QCFString(d->outputFilename),
+ kCFURLPOSIXPathStyle,
+ false);
+ if (PMSessionSetDestination(d->session, d->settings, kPMDestinationFile,
+ kPMDocumentFormatPDF, outFile) != noErr) {
+ qWarning("QMacPrintEngine::begin: Problem setting file [%s]", d->outputFilename.toUtf8().constData());
+ return false;
+ }
+ }
+ OSStatus status = noErr;
+#ifndef QT_MAC_USE_COCOA
+ status = d->shouldSuppressStatus() ? PMSessionBeginCGDocumentNoDialog(d->session, d->settings, d->format)
+ : PMSessionBeginCGDocument(d->session, d->settings, d->format);
+#else
+ status = PMSessionBeginCGDocumentNoDialog(d->session, d->settings, d->format);
+#endif
+
+ if (status != noErr) {
+ d->state = QPrinter::Error;
+ return false;
+ }
+
+ d->state = QPrinter::Active;
+ setActive(true);
+ d->newPage_helper();
+ return true;
+}
+
+bool QMacPrintEngine::end()
+{
+ Q_D(QMacPrintEngine);
+ if (d->state == QPrinter::Aborted)
+ return true; // I was just here a function call ago :)
+ if(d->paintEngine->type() == QPaintEngine::CoreGraphics) {
+ // We dont need the paint engine to call restoreGraphicsState()
+ static_cast<QCoreGraphicsPaintEngine*>(d->paintEngine)->d_func()->stackCount = 0;
+ static_cast<QCoreGraphicsPaintEngine*>(d->paintEngine)->d_func()->hd = 0;
+ }
+ d->paintEngine->end();
+ if (d->state != QPrinter::Idle)
+ d->releaseSession();
+ d->state = QPrinter::Idle;
+ return true;
+}
+
+QPaintEngine *
+QMacPrintEngine::paintEngine() const
+{
+ return d_func()->paintEngine;
+}
+
+Qt::HANDLE QMacPrintEngine::handle() const
+{
+ QCoreGraphicsPaintEngine *cgEngine = static_cast<QCoreGraphicsPaintEngine*>(paintEngine());
+ return cgEngine->d_func()->hd;
+}
+
+QMacPrintEnginePrivate::~QMacPrintEnginePrivate()
+{
+#ifdef QT_MAC_USE_COCOA
+ [printInfo release];
+#endif
+ delete paintEngine;
+}
+
+void QMacPrintEnginePrivate::setPaperSize(QPrinter::PaperSize ps)
+{
+ Q_Q(QMacPrintEngine);
+ QSizeF newSize = qt_paperSizeToQSizeF(ps);
+ QCFType<CFArrayRef> formats;
+ PMPrinter printer;
+
+ if (PMSessionGetCurrentPrinter(session, &printer) == noErr
+ && PMSessionCreatePageFormatList(session, printer, &formats) == noErr) {
+ CFIndex total = CFArrayGetCount(formats);
+ PMPageFormat tmp;
+ PMRect paper;
+ for (CFIndex idx = 0; idx < total; ++idx) {
+ tmp = static_cast<PMPageFormat>(
+ const_cast<void *>(CFArrayGetValueAtIndex(formats, idx)));
+ PMGetUnadjustedPaperRect(tmp, &paper);
+ int wMM = int((paper.right - paper.left) / 72 * 25.4 + 0.5);
+ int hMM = int((paper.bottom - paper.top) / 72 * 25.4 + 0.5);
+ if (newSize.width() == wMM && newSize.height() == hMM) {
+ PMCopyPageFormat(tmp, format);
+ // reset the orientation and resolution as they are lost in the copy.
+ q->setProperty(QPrintEngine::PPK_Orientation, orient);
+ if (PMSessionValidatePageFormat(session, format, kPMDontWantBoolean) != noErr) {
+ // Don't know, warn for the moment.
+ qWarning("QMacPrintEngine, problem setting format and resolution for this page size");
+ }
+ break;
+ }
+ }
+ }
+}
+
+QPrinter::PaperSize QMacPrintEnginePrivate::paperSize() const
+{
+ PMRect paper;
+ PMGetUnadjustedPaperRect(format, &paper);
+ int wMM = int((paper.right - paper.left) / 72 * 25.4 + 0.5);
+ int hMM = int((paper.bottom - paper.top) / 72 * 25.4 + 0.5);
+ for (int i = QPrinter::A4; i < QPrinter::NPaperSize; ++i) {
+ QSizeF s = qt_paperSizeToQSizeF(QPrinter::PaperSize(i));
+ if (s.width() == wMM && s.height() == hMM)
+ return (QPrinter::PaperSize)i;
+ }
+ return QPrinter::Custom;
+}
+
+QList<QVariant> QMacPrintEnginePrivate::supportedResolutions() const
+{
+ Q_ASSERT_X(session, "QMacPrinterEngine::supportedResolutions",
+ "must have a valid printer session");
+ UInt32 resCount;
+ QList<QVariant> resolutions;
+ PMPrinter printer;
+ if (PMSessionGetCurrentPrinter(session, &printer) == noErr) {
+ PMResolution res;
+ OSStatus status = PMPrinterGetPrinterResolutionCount(printer, &resCount);
+ if (status == kPMNotImplemented) {
+#if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5)
+ // *Sigh* we have to use the non-indexed version.
+ if (PMPrinterGetPrinterResolution(printer, kPMMinSquareResolution, &res) == noErr)
+ resolutions.append(int(res.hRes));
+ if (PMPrinterGetPrinterResolution(printer, kPMMaxSquareResolution, &res) == noErr) {
+ QVariant var(int(res.hRes));
+ if (!resolutions.contains(var))
+ resolutions.append(var);
+ }
+ if (PMPrinterGetPrinterResolution(printer, kPMDefaultResolution, &res) == noErr) {
+ QVariant var(int(res.hRes));
+ if (!resolutions.contains(var))
+ resolutions.append(var);
+ }
+#endif
+ } else if (status == noErr) {
+ // According to the docs, index start at 1.
+ for (UInt32 i = 1; i <= resCount; ++i) {
+ if (PMPrinterGetIndexedPrinterResolution(printer, i, &res) == noErr)
+ resolutions.append(QVariant(int(res.hRes)));
+ }
+ } else {
+ qWarning("QMacPrintEngine::supportedResolutions: Unexpected error: %ld", long(status));
+ }
+ }
+ return resolutions;
+}
+
+bool QMacPrintEnginePrivate::shouldSuppressStatus() const
+{
+ if (suppressStatus == true)
+ return true;
+
+ // Supress displaying the automatic progress dialog if we are printing
+ // from a non-gui thread.
+ return (qApp->thread() != QThread::currentThread());
+}
+
+QPrinter::PrinterState QMacPrintEngine::printerState() const
+{
+ return d_func()->state;
+}
+
+bool QMacPrintEngine::newPage()
+{
+ Q_D(QMacPrintEngine);
+ Q_ASSERT(d->state == QPrinter::Active);
+ OSStatus err =
+#ifndef QT_MAC_USE_COCOA
+ d->shouldSuppressStatus() ? PMSessionEndPageNoDialog(d->session)
+ : PMSessionEndPage(d->session);
+#else
+ PMSessionEndPageNoDialog(d->session);
+#endif
+ if (err != noErr) {
+ if (err == kPMCancel) {
+ // User canceled, we need to abort!
+ abort();
+ } else {
+ // Not sure what the problem is...
+ qWarning("QMacPrintEngine::newPage: Cannot end current page. %ld", long(err));
+ d->state = QPrinter::Error;
+ }
+ return false;
+ }
+ return d->newPage_helper();
+}
+
+bool QMacPrintEngine::abort()
+{
+ Q_D(QMacPrintEngine);
+ if (d->state != QPrinter::Active)
+ return false;
+ bool ret = end();
+ d->state = QPrinter::Aborted;
+ return ret;
+}
+
+static inline int qt_get_PDMWidth(PMPageFormat pformat, bool fullPage,
+ const PMResolution &resolution)
+{
+ int val = 0;
+ PMRect r;
+ qreal hRatio = resolution.hRes / 72;
+ if (fullPage) {
+ if (PMGetAdjustedPaperRect(pformat, &r) == noErr)
+ val = qRound((r.right - r.left) * hRatio);
+ } else {
+ if (PMGetAdjustedPageRect(pformat, &r) == noErr)
+ val = qRound((r.right - r.left) * hRatio);
+ }
+ return val;
+}
+
+static inline int qt_get_PDMHeight(PMPageFormat pformat, bool fullPage,
+ const PMResolution &resolution)
+{
+ int val = 0;
+ PMRect r;
+ qreal vRatio = resolution.vRes / 72;
+ if (fullPage) {
+ if (PMGetAdjustedPaperRect(pformat, &r) == noErr)
+ val = qRound((r.bottom - r.top) * vRatio);
+ } else {
+ if (PMGetAdjustedPageRect(pformat, &r) == noErr)
+ val = qRound((r.bottom - r.top) * vRatio);
+ }
+ return val;
+}
+
+
+int QMacPrintEngine::metric(QPaintDevice::PaintDeviceMetric m) const
+{
+ Q_D(const QMacPrintEngine);
+ int val = 1;
+ switch (m) {
+ case QPaintDevice::PdmWidth:
+ if (d->hasCustomPaperSize) {
+ val = qRound(d->customSize.width());
+ if (d->hasCustomPageMargins) {
+ val -= qRound(d->leftMargin + d->rightMargin);
+ } else {
+ QList<QVariant> margins = property(QPrintEngine::PPK_PageMargins).toList();
+ val -= qRound(margins.at(0).toDouble() + margins.at(2).toDouble());
+ }
+ } else {
+ val = qt_get_PDMWidth(d->format, property(PPK_FullPage).toBool(), d->resolution);
+ }
+ break;
+ case QPaintDevice::PdmHeight:
+ if (d->hasCustomPaperSize) {
+ val = qRound(d->customSize.height());
+ if (d->hasCustomPageMargins) {
+ val -= qRound(d->topMargin + d->bottomMargin);
+ } else {
+ QList<QVariant> margins = property(QPrintEngine::PPK_PageMargins).toList();
+ val -= qRound(margins.at(1).toDouble() + margins.at(3).toDouble());
+ }
+ } else {
+ val = qt_get_PDMHeight(d->format, property(PPK_FullPage).toBool(), d->resolution);
+ }
+ break;
+ case QPaintDevice::PdmWidthMM:
+ val = metric(QPaintDevice::PdmWidth);
+ val = int((val * 254 + 5 * d->resolution.hRes) / (10 * d->resolution.hRes));
+ break;
+ case QPaintDevice::PdmHeightMM:
+ val = metric(QPaintDevice::PdmHeight);
+ val = int((val * 254 + 5 * d->resolution.vRes) / (10 * d->resolution.vRes));
+ break;
+ case QPaintDevice::PdmPhysicalDpiX:
+ case QPaintDevice::PdmPhysicalDpiY: {
+ PMPrinter printer;
+ if(PMSessionGetCurrentPrinter(d->session, &printer) == noErr) {
+ PMResolution resolution;
+#ifndef QT_MAC_USE_COCOA
+# if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
+ PMPrinterGetOutputResolution(printer, d->settings, &resolution);
+ } else
+# endif
+ {
+ PMPrinterGetPrinterResolution(printer, kPMCurrentValue, &resolution);
+ }
+#else
+ PMPrinterGetOutputResolution(printer, d->settings, &resolution);
+#endif
+ val = (int)resolution.vRes;
+ break;
+ }
+ //otherwise fall through
+ }
+ case QPaintDevice::PdmDpiY:
+ val = (int)d->resolution.vRes;
+ break;
+ case QPaintDevice::PdmDpiX:
+ val = (int)d->resolution.hRes;
+ break;
+ case QPaintDevice::PdmNumColors:
+ val = (1 << metric(QPaintDevice::PdmDepth));
+ break;
+ case QPaintDevice::PdmDepth:
+ val = 24;
+ break;
+ default:
+ val = 0;
+ qWarning("QPrinter::metric: Invalid metric command");
+ }
+ return val;
+}
+
+void QMacPrintEnginePrivate::initialize()
+{
+ Q_Q(QMacPrintEngine);
+
+#ifndef QT_MAC_USE_COCOA
+ Q_ASSERT(!session);
+#else
+ Q_ASSERT(!printInfo);
+#endif
+
+ if (!paintEngine)
+ paintEngine = new QCoreGraphicsPaintEngine();
+
+ q->gccaps = paintEngine->gccaps;
+
+ fullPage = false;
+
+#ifndef QT_MAC_USE_COCOA
+ if (PMCreateSession(&session) != 0)
+ session = 0;
+#else
+ QMacCocoaAutoReleasePool pool;
+ printInfo = [[NSPrintInfo alloc] initWithDictionary:[NSDictionary dictionary]];
+ session = static_cast<PMPrintSession>([printInfo PMPrintSession]);
+#endif
+
+ PMPrinter printer;
+ if (session && PMSessionGetCurrentPrinter(session, &printer) == noErr) {
+ QList<QVariant> resolutions = supportedResolutions();
+ if (!resolutions.isEmpty() && mode != QPrinter::ScreenResolution) {
+ if (resolutions.count() > 1 && mode == QPrinter::HighResolution) {
+ int max = 0;
+ for (int i = 0; i < resolutions.count(); ++i) {
+ int value = resolutions.at(i).toInt();
+ if (value > max)
+ max = value;
+ }
+ resolution.hRes = resolution.vRes = max;
+ } else {
+ resolution.hRes = resolution.vRes = resolutions.at(0).toInt();
+ }
+ if(resolution.hRes == 0)
+ resolution.hRes = resolution.vRes = 600;
+ } else {
+ resolution.hRes = resolution.vRes = qt_defaultDpi();
+ }
+ }
+
+#ifndef QT_MAC_USE_COCOA
+ bool settingsInitialized = (settings != 0);
+ bool settingsOK = !settingsInitialized ? PMCreatePrintSettings(&settings) == noErr : true;
+ if (settingsOK && !settingsInitialized)
+ settingsOK = PMSessionDefaultPrintSettings(session, settings) == noErr;
+
+
+ bool formatInitialized = (format != 0);
+ bool formatOK = !formatInitialized ? PMCreatePageFormat(&format) == noErr : true;
+ if (formatOK) {
+ if (!formatInitialized) {
+ formatOK = PMSessionDefaultPageFormat(session, format) == noErr;
+ }
+ formatOK = PMSessionValidatePageFormat(session, format, kPMDontWantBoolean) == noErr;
+ }
+#else
+ settings = static_cast<PMPrintSettings>([printInfo PMPrintSettings]);
+ format = static_cast<PMPageFormat>([printInfo PMPageFormat]);
+#endif
+
+#ifndef QT_MAC_USE_COCOA
+ if (!settingsOK || !formatOK) {
+ qWarning("QMacPrintEngine::initialize: Unable to initialize QPainter");
+ state = QPrinter::Error;
+ }
+#endif
+
+ QHash<QMacPrintEngine::PrintEnginePropertyKey, QVariant>::const_iterator propC;
+ for (propC = valueCache.constBegin(); propC != valueCache.constEnd(); propC++) {
+ q->setProperty(propC.key(), propC.value());
+ }
+}
+
+void QMacPrintEnginePrivate::releaseSession()
+{
+#ifndef QT_MAC_USE_COCOA
+ if (shouldSuppressStatus()) {
+ PMSessionEndPageNoDialog(session);
+ PMSessionEndDocumentNoDialog(session);
+ } else {
+ PMSessionEndPage(session);
+ PMSessionEndDocument(session);
+ }
+ PMRelease(session);
+#else
+ PMSessionEndPageNoDialog(session);
+ PMSessionEndDocumentNoDialog(session);
+ [printInfo release];
+#endif
+ printInfo = 0;
+ session = 0;
+}
+
+bool QMacPrintEnginePrivate::newPage_helper()
+{
+ Q_Q(QMacPrintEngine);
+ Q_ASSERT(state == QPrinter::Active);
+
+ if (PMSessionError(session) != noErr) {
+ q->abort();
+ return false;
+ }
+
+ // pop the stack of saved graphic states, in case we get the same
+ // context back - either way, the stack count should be 0 when we
+ // get the new one
+ QCoreGraphicsPaintEngine *cgEngine = static_cast<QCoreGraphicsPaintEngine*>(paintEngine);
+ while (cgEngine->d_func()->stackCount > 0)
+ cgEngine->d_func()->restoreGraphicsState();
+
+ OSStatus status =
+#ifndef QT_MAC_USE_COCOA
+ shouldSuppressStatus() ? PMSessionBeginPageNoDialog(session, format, 0)
+ : PMSessionBeginPage(session, format, 0);
+#else
+ PMSessionBeginPageNoDialog(session, format, 0);
+#endif
+ if(status != noErr) {
+ state = QPrinter::Error;
+ return false;
+ }
+
+ QRect page = q->property(QPrintEngine::PPK_PageRect).toRect();
+ QRect paper = q->property(QPrintEngine::PPK_PaperRect).toRect();
+
+ CGContextRef cgContext;
+ OSStatus err = noErr;
+ err = PMSessionGetCGGraphicsContext(session, &cgContext);
+ if(err != noErr) {
+ qWarning("QMacPrintEngine::newPage: Cannot retrieve CoreGraphics context: %ld", long(err));
+ state = QPrinter::Error;
+ return false;
+ }
+ cgEngine->d_func()->hd = cgContext;
+
+ // Set the resolution as a scaling ration of 72 (the default).
+ CGContextScaleCTM(cgContext, 72 / resolution.hRes, 72 / resolution.vRes);
+
+ CGContextScaleCTM(cgContext, 1, -1);
+ CGContextTranslateCTM(cgContext, 0, -paper.height());
+ if (!fullPage)
+ CGContextTranslateCTM(cgContext, page.x() - paper.x(), page.y() - paper.y());
+ cgEngine->d_func()->orig_xform = CGContextGetCTM(cgContext);
+ cgEngine->d_func()->setClip(0);
+ cgEngine->state->dirtyFlags = QPaintEngine::DirtyFlag(QPaintEngine::AllDirty
+ & ~(QPaintEngine::DirtyClipEnabled
+ | QPaintEngine::DirtyClipRegion
+ | QPaintEngine::DirtyClipPath));
+ if (cgEngine->painter()->hasClipping())
+ cgEngine->state->dirtyFlags |= QPaintEngine::DirtyClipEnabled;
+ cgEngine->syncState();
+ return true;
+}
+
+
+void QMacPrintEngine::updateState(const QPaintEngineState &state)
+{
+ d_func()->paintEngine->updateState(state);
+}
+
+void QMacPrintEngine::drawRects(const QRectF *r, int num)
+{
+ Q_D(QMacPrintEngine);
+ Q_ASSERT(d->state == QPrinter::Active);
+ d->paintEngine->drawRects(r, num);
+}
+
+void QMacPrintEngine::drawPoints(const QPointF *points, int pointCount)
+{
+ Q_D(QMacPrintEngine);
+ Q_ASSERT(d->state == QPrinter::Active);
+ d->paintEngine->drawPoints(points, pointCount);
+}
+
+void QMacPrintEngine::drawEllipse(const QRectF &r)
+{
+ Q_D(QMacPrintEngine);
+ Q_ASSERT(d->state == QPrinter::Active);
+ d->paintEngine->drawEllipse(r);
+}
+
+void QMacPrintEngine::drawLines(const QLineF *lines, int lineCount)
+{
+ Q_D(QMacPrintEngine);
+ Q_ASSERT(d->state == QPrinter::Active);
+ d->paintEngine->drawLines(lines, lineCount);
+}
+
+void QMacPrintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
+{
+ Q_D(QMacPrintEngine);
+ Q_ASSERT(d->state == QPrinter::Active);
+ d->paintEngine->drawPolygon(points, pointCount, mode);
+}
+
+void QMacPrintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
+{
+ Q_D(QMacPrintEngine);
+ Q_ASSERT(d->state == QPrinter::Active);
+ d->paintEngine->drawPixmap(r, pm, sr);
+}
+
+void QMacPrintEngine::drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, Qt::ImageConversionFlags flags)
+{
+ Q_D(QMacPrintEngine);
+ Q_ASSERT(d->state == QPrinter::Active);
+ d->paintEngine->drawImage(r, pm, sr, flags);
+}
+
+void QMacPrintEngine::drawTextItem(const QPointF &p, const QTextItem &ti)
+{
+ Q_D(QMacPrintEngine);
+ Q_ASSERT(d->state == QPrinter::Active);
+ d->paintEngine->drawTextItem(p, ti);
+}
+
+void QMacPrintEngine::drawTiledPixmap(const QRectF &dr, const QPixmap &pixmap, const QPointF &sr)
+{
+ Q_D(QMacPrintEngine);
+ Q_ASSERT(d->state == QPrinter::Active);
+ d->paintEngine->drawTiledPixmap(dr, pixmap, sr);
+}
+
+void QMacPrintEngine::drawPath(const QPainterPath &path)
+{
+ Q_D(QMacPrintEngine);
+ Q_ASSERT(d->state == QPrinter::Active);
+ d->paintEngine->drawPath(path);
+}
+
+
+void QMacPrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &value)
+{
+ Q_D(QMacPrintEngine);
+
+ d->valueCache.insert(key, value);
+ if (!d->session)
+ return;
+
+ switch (key) {
+ case PPK_CollateCopies:
+ break;
+ case PPK_ColorMode:
+ break;
+ case PPK_Creator:
+ break;
+ case PPK_DocumentName:
+ break;
+ case PPK_PageOrder:
+ break;
+ case PPK_PaperSource:
+ break;
+ case PPK_SelectionOption:
+ break;
+ case PPK_Resolution: {
+ PMPrinter printer;
+ UInt32 count;
+ if (PMSessionGetCurrentPrinter(d->session, &printer) != noErr)
+ break;
+ if (PMPrinterGetPrinterResolutionCount(printer, &count) != noErr)
+ break;
+ PMResolution resolution = { 0.0, 0.0 };
+ PMResolution bestResolution = { 0.0, 0.0 };
+ int dpi = value.toInt();
+ int bestDistance = INT_MAX;
+ for (UInt32 i = 1; i <= count; ++i) { // Yes, it starts at 1
+ if (PMPrinterGetIndexedPrinterResolution(printer, i, &resolution) == noErr) {
+ if (dpi == int(resolution.hRes)) {
+ bestResolution = resolution;
+ break;
+ } else {
+ int distance = qAbs(dpi - int(resolution.hRes));
+ if (distance < bestDistance) {
+ bestDistance = distance;
+ bestResolution = resolution;
+ }
+ }
+ }
+ }
+ PMSessionValidatePageFormat(d->session, d->format, kPMDontWantBoolean);
+ break;
+ }
+
+ case PPK_FullPage:
+ d->fullPage = value.toBool();
+ break;
+ case PPK_CopyCount: // fallthrough
+ case PPK_NumberOfCopies:
+ PMSetCopies(d->settings, value.toInt(), false);
+ break;
+ case PPK_Orientation: {
+ if (d->state == QPrinter::Active) {
+ qWarning("QMacPrintEngine::setOrientation: Orientation cannot be changed during a print job, ignoring change");
+ } else {
+ QPrinter::Orientation newOrientation = QPrinter::Orientation(value.toInt());
+ if (d->hasCustomPaperSize && (d->orient != newOrientation))
+ d->customSize = QSizeF(d->customSize.height(), d->customSize.width());
+ d->orient = newOrientation;
+ PMOrientation o = d->orient == QPrinter::Portrait ? kPMPortrait : kPMLandscape;
+ PMSetOrientation(d->format, o, false);
+ PMSessionValidatePageFormat(d->session, d->format, kPMDontWantBoolean);
+ }
+ break; }
+ case PPK_OutputFileName:
+ d->outputFilename = value.toString();
+ break;
+ case PPK_PaperSize:
+ d->setPaperSize(QPrinter::PaperSize(value.toInt()));
+ break;
+ case PPK_PrinterName: {
+ bool printerNameSet = false;
+ OSStatus status = noErr;
+ QCFType<CFArrayRef> printerList;
+ status = PMServerCreatePrinterList(kPMServerLocal, &printerList);
+ if (status == noErr) {
+ CFIndex count = CFArrayGetCount(printerList);
+ for (CFIndex i=0; i<count; ++i) {
+ PMPrinter printer = static_cast<PMPrinter>(const_cast<void *>(CFArrayGetValueAtIndex(printerList, i)));
+ QString name = QCFString::toQString(PMPrinterGetName(printer));
+ if (name == value.toString()) {
+ status = PMSessionSetCurrentPMPrinter(d->session, printer);
+ printerNameSet = true;
+ break;
+ }
+ }
+ }
+ if (status != noErr)
+ qWarning("QMacPrintEngine::setPrinterName: Error setting printer: %ld", long(status));
+ if (!printerNameSet) {
+ qWarning("QMacPrintEngine::setPrinterName: Failed to set printer named '%s'.", qPrintable(value.toString()));
+ d->releaseSession();
+ d->state = QPrinter::Idle;
+ }
+ break; }
+ case PPK_SuppressSystemPrintStatus:
+ d->suppressStatus = value.toBool();
+ break;
+ case PPK_CustomPaperSize:
+ {
+ PMOrientation orientation;
+ PMGetOrientation(d->format, &orientation);
+ d->hasCustomPaperSize = true;
+ d->customSize = value.toSizeF();
+ if (orientation != kPMPortrait)
+ d->customSize = QSizeF(d->customSize.height(), d->customSize.width());
+ break;
+ }
+ case PPK_PageMargins:
+ {
+ QList<QVariant> margins(value.toList());
+ Q_ASSERT(margins.size() == 4);
+ d->leftMargin = margins.at(0).toDouble();
+ d->topMargin = margins.at(1).toDouble();
+ d->rightMargin = margins.at(2).toDouble();
+ d->bottomMargin = margins.at(3).toDouble();
+ d->hasCustomPageMargins = true;
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+QVariant QMacPrintEngine::property(PrintEnginePropertyKey key) const
+{
+ Q_D(const QMacPrintEngine);
+ QVariant ret;
+
+ if (!d->session && d->valueCache.contains(key))
+ return *d->valueCache.find(key);
+
+ switch (key) {
+ case PPK_CollateCopies:
+ ret = false;
+ break;
+ case PPK_ColorMode:
+ ret = QPrinter::Color;
+ break;
+ case PPK_Creator:
+ break;
+ case PPK_DocumentName:
+ break;
+ case PPK_FullPage:
+ ret = d->fullPage;
+ break;
+ case PPK_NumberOfCopies:
+ ret = 1;
+ break;
+ case PPK_CopyCount: {
+ UInt32 copies = 1;
+ PMGetCopies(d->settings, &copies);
+ ret = (uint) copies;
+ break;
+ }
+ case PPK_SupportsMultipleCopies:
+ ret = true;
+ break;
+ case PPK_Orientation:
+ PMOrientation orientation;
+ PMGetOrientation(d->format, &orientation);
+ ret = orientation == kPMPortrait ? QPrinter::Portrait : QPrinter::Landscape;
+ break;
+ case PPK_OutputFileName:
+ ret = d->outputFilename;
+ break;
+ case PPK_PageOrder:
+ break;
+ case PPK_PaperSource:
+ break;
+ case PPK_PageRect: {
+ // PageRect is returned in device pixels
+ QRect r;
+ PMRect macrect, macpaper;
+ qreal hRatio = d->resolution.hRes / 72;
+ qreal vRatio = d->resolution.vRes / 72;
+ if (d->hasCustomPaperSize) {
+ r = QRect(0, 0, qRound(d->customSize.width() * hRatio), qRound(d->customSize.height() * vRatio));
+ if (d->hasCustomPageMargins) {
+ r.adjust(qRound(d->leftMargin * hRatio), qRound(d->topMargin * vRatio),
+ -qRound(d->rightMargin * hRatio), -qRound(d->bottomMargin * vRatio));
+ } else {
+ QList<QVariant> margins = property(QPrintEngine::PPK_PageMargins).toList();
+ r.adjust(qRound(margins.at(0).toDouble() * hRatio),
+ qRound(margins.at(1).toDouble() * vRatio),
+ -qRound(margins.at(2).toDouble() * hRatio),
+ -qRound(margins.at(3).toDouble()) * vRatio);
+ }
+ } else if (PMGetAdjustedPageRect(d->format, &macrect) == noErr
+ && PMGetAdjustedPaperRect(d->format, &macpaper) == noErr)
+ {
+ if (d->fullPage || d->hasCustomPageMargins) {
+ r.setCoords(int(macpaper.left * hRatio), int(macpaper.top * vRatio),
+ int(macpaper.right * hRatio), int(macpaper.bottom * vRatio));
+ r.translate(-r.x(), -r.y());
+ if (d->hasCustomPageMargins) {
+ r.adjust(qRound(d->leftMargin * hRatio), qRound(d->topMargin * vRatio),
+ -qRound(d->rightMargin * hRatio), -qRound(d->bottomMargin * vRatio));
+ }
+ } else {
+ r.setCoords(int(macrect.left * hRatio), int(macrect.top * vRatio),
+ int(macrect.right * hRatio), int(macrect.bottom * vRatio));
+ r.translate(int(-macpaper.left * hRatio), int(-macpaper.top * vRatio));
+ }
+ }
+ ret = r;
+ break; }
+ case PPK_PaperSize:
+ ret = d->paperSize();
+ break;
+ case PPK_PaperRect: {
+ QRect r;
+ PMRect macrect;
+ if (d->hasCustomPaperSize) {
+ r = QRect(0, 0, qRound(d->customSize.width()), qRound(d->customSize.height()));
+ } else if (PMGetAdjustedPaperRect(d->format, &macrect) == noErr) {
+ qreal hRatio = d->resolution.hRes / 72;
+ qreal vRatio = d->resolution.vRes / 72;
+ r.setCoords(int(macrect.left * hRatio), int(macrect.top * vRatio),
+ int(macrect.right * hRatio), int(macrect.bottom * vRatio));
+ r.translate(-r.x(), -r.y());
+ }
+ ret = r;
+ break; }
+ case PPK_PrinterName: {
+ PMPrinter printer;
+ OSStatus status = PMSessionGetCurrentPrinter(d->session, &printer);
+ if (status != noErr)
+ qWarning("QMacPrintEngine::printerName: Failed getting current PMPrinter: %ld", long(status));
+ if (printer)
+ ret = QCFString::toQString(PMPrinterGetName(printer));
+ break; }
+ case PPK_Resolution: {
+ ret = d->resolution.hRes;
+ break;
+ }
+ case PPK_SupportedResolutions:
+ ret = d->supportedResolutions();
+ break;
+ case PPK_CustomPaperSize:
+ ret = d->customSize;
+ break;
+ case PPK_PageMargins:
+ {
+ QList<QVariant> margins;
+ if (d->hasCustomPageMargins) {
+ margins << d->leftMargin << d->topMargin
+ << d->rightMargin << d->bottomMargin;
+ } else {
+ PMPaperMargins paperMargins;
+ PMPaper paper;
+ PMGetPageFormatPaper(d->format, &paper);
+ PMPaperGetMargins(paper, &paperMargins);
+ margins << paperMargins.left << paperMargins.top
+ << paperMargins.right << paperMargins.bottom;
+ }
+ ret = margins;
+ break;
+ }
+ default:
+ break;
+ }
+ return ret;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
diff --git a/src/widgets/platforms/mac/qprintengine_mac_p.h b/src/widgets/platforms/mac/qprintengine_mac_p.h
new file mode 100644
index 0000000000..5c4fe944e0
--- /dev/null
+++ b/src/widgets/platforms/mac/qprintengine_mac_p.h
@@ -0,0 +1,166 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPRINTENGINE_MAC_P_H
+#define QPRINTENGINE_MAC_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QT_NO_PRINTER
+
+#include "QtGui/qprinter.h"
+#include "QtGui/qprintengine.h"
+#include "private/qpaintengine_mac_p.h"
+#include "private/qpainter_p.h"
+
+#ifdef __OBJC__
+@class NSPrintInfo;
+#else
+typedef void NSPrintInfo;
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QPrinterPrivate;
+class QMacPrintEnginePrivate;
+class QMacPrintEngine : public QPaintEngine, public QPrintEngine
+{
+ Q_DECLARE_PRIVATE(QMacPrintEngine)
+public:
+ QMacPrintEngine(QPrinter::PrinterMode mode);
+
+ Qt::HANDLE handle() const;
+
+ bool begin(QPaintDevice *dev);
+ bool end();
+ virtual QPaintEngine::Type type() const { return QPaintEngine::MacPrinter; }
+
+ QPaintEngine *paintEngine() const;
+
+ void setProperty(PrintEnginePropertyKey key, const QVariant &value);
+ QVariant property(PrintEnginePropertyKey key) const;
+
+ QPrinter::PrinterState printerState() const;
+
+ bool newPage();
+ bool abort();
+ int metric(QPaintDevice::PaintDeviceMetric) const;
+
+ //forwarded functions
+
+ void updateState(const QPaintEngineState &state);
+
+ virtual void drawLines(const QLineF *lines, int lineCount);
+ virtual void drawRects(const QRectF *r, int num);
+ virtual void drawPoints(const QPointF *p, int pointCount);
+ virtual void drawEllipse(const QRectF &r);
+ virtual void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode);
+ virtual void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
+ virtual void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, Qt::ImageConversionFlags flags);
+ virtual void drawTextItem(const QPointF &p, const QTextItem &ti);
+ virtual void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s);
+ virtual void drawPath(const QPainterPath &);
+
+private:
+ friend class QPrintDialog;
+ friend class QPageSetupDialog;
+};
+
+class QMacPrintEnginePrivate : public QPaintEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QMacPrintEngine)
+public:
+ QPrinter::PrinterMode mode;
+ QPrinter::PrinterState state;
+ QPrinter::Orientation orient;
+ NSPrintInfo *printInfo;
+ PMPageFormat format;
+ PMPrintSettings settings;
+ PMPrintSession session;
+ PMResolution resolution;
+ QString outputFilename;
+ bool fullPage;
+ QPaintEngine *paintEngine;
+ bool suppressStatus;
+ bool hasCustomPaperSize;
+ QSizeF customSize;
+ bool hasCustomPageMargins;
+ qreal leftMargin;
+ qreal topMargin;
+ qreal rightMargin;
+ qreal bottomMargin;
+ QHash<QMacPrintEngine::PrintEnginePropertyKey, QVariant> valueCache;
+ QMacPrintEnginePrivate() : mode(QPrinter::ScreenResolution), state(QPrinter::Idle),
+ orient(QPrinter::Portrait), printInfo(0), format(0), settings(0),
+ session(0), paintEngine(0), suppressStatus(false),
+ hasCustomPaperSize(false), hasCustomPageMargins(false) {}
+ ~QMacPrintEnginePrivate();
+ void initialize();
+ void releaseSession();
+ bool newPage_helper();
+ void setPaperSize(QPrinter::PaperSize ps);
+ QPrinter::PaperSize paperSize() const;
+ QList<QVariant> supportedResolutions() const;
+ inline bool isPrintSessionInitialized() const
+ {
+#ifndef QT_MAC_USE_COCOA
+ return session != 0;
+#else
+ return printInfo != 0;
+#endif
+ }
+ bool shouldSuppressStatus() const;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
+
+#endif // QPRINTENGINE_WIN_P_H
diff --git a/src/widgets/platforms/mac/qprinterinfo_mac.cpp b/src/widgets/platforms/mac/qprinterinfo_mac.cpp
new file mode 100644
index 0000000000..455510d5d3
--- /dev/null
+++ b/src/widgets/platforms/mac/qprinterinfo_mac.cpp
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qprinterinfo.h"
+#include "qprinterinfo_p.h"
+
+#include "private/qt_mac_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_PRINTER
+
+extern QPrinter::PaperSize qSizeFTopaperSize(const QSizeF &size);
+
+QList<QPrinterInfo> QPrinterInfo::availablePrinters()
+{
+ QList<QPrinterInfo> printers;
+
+ QCFType<CFArrayRef> array;
+ if (PMServerCreatePrinterList(kPMServerLocal, &array) == noErr) {
+ CFIndex count = CFArrayGetCount(array);
+ for (int i = 0; i < count; ++i) {
+ PMPrinter printer = static_cast<PMPrinter>(const_cast<void *>(CFArrayGetValueAtIndex(array, i)));
+ QString printerName = QCFString::toQString(PMPrinterGetName(printer));
+
+ QPrinterInfo printerInfo(printerName);
+ if (PMPrinterIsDefault(printer))
+ printerInfo.d_ptr->isDefault = true;
+ printers.append(printerInfo);
+ }
+ }
+
+ return printers;
+}
+
+QPrinterInfo QPrinterInfo::defaultPrinter()
+{
+ QList<QPrinterInfo> printers = availablePrinters();
+ foreach (const QPrinterInfo &printerInfo, printers) {
+ if (printerInfo.isDefault())
+ return printerInfo;
+ }
+
+ return printers.value(0);
+}
+
+QList<QPrinter::PaperSize> QPrinterInfo::supportedPaperSizes() const
+{
+ const Q_D(QPrinterInfo);
+
+ QList<QPrinter::PaperSize> paperSizes;
+ if (isNull())
+ return paperSizes;
+
+ PMPrinter cfPrn = PMPrinterCreateFromPrinterID(QCFString::toCFStringRef(d->name));
+ if (!cfPrn)
+ return paperSizes;
+
+ CFArrayRef array;
+ if (PMPrinterGetPaperList(cfPrn, &array) != noErr) {
+ PMRelease(cfPrn);
+ return paperSizes;
+ }
+
+ int count = CFArrayGetCount(array);
+ for (int i = 0; i < count; ++i) {
+ PMPaper paper = static_cast<PMPaper>(const_cast<void *>(CFArrayGetValueAtIndex(array, i)));
+ double width, height;
+ if (PMPaperGetWidth(paper, &width) == noErr && PMPaperGetHeight(paper, &height) == noErr) {
+ QSizeF size(width * 0.3527, height * 0.3527);
+ paperSizes.append(qSizeFTopaperSize(size));
+ }
+ }
+
+ PMRelease(cfPrn);
+
+ return paperSizes;
+}
+
+#endif // QT_NO_PRINTER
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/mac/qrawfont_mac.cpp b/src/widgets/platforms/mac/qrawfont_mac.cpp
new file mode 100644
index 0000000000..40c719a1af
--- /dev/null
+++ b/src/widgets/platforms/mac/qrawfont_mac.cpp
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qglobal.h>
+
+#if !defined(QT_NO_RAWFONT)
+
+#include "qrawfont_p.h"
+#include "qfontengine_coretext_p.h"
+
+QT_BEGIN_NAMESPACE
+
+void QRawFontPrivate::platformCleanUp()
+{
+}
+
+extern int qt_defaultDpi();
+
+void QRawFontPrivate::platformLoadFromData(const QByteArray &fontData,
+ qreal pixelSize,
+ QFont::HintingPreference hintingPreference)
+{
+ // Mac OS X ignores it
+ Q_UNUSED(hintingPreference);
+
+ QCFType<CGDataProviderRef> dataProvider = CGDataProviderCreateWithData(NULL,
+ fontData.constData(), fontData.size(), NULL);
+
+ CGFontRef cgFont = CGFontCreateWithDataProvider(dataProvider);
+
+ if (cgFont == NULL) {
+ qWarning("QRawFont::platformLoadFromData: CGFontCreateWithDataProvider failed");
+ } else {
+ QFontDef def;
+ def.pixelSize = pixelSize;
+ def.pointSize = pixelSize * 72.0 / qt_defaultDpi();
+ fontEngine = new QCoreTextFontEngine(cgFont, def);
+ CFRelease(cgFont);
+ fontEngine->ref.ref();
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_RAWFONT
diff --git a/src/widgets/platforms/mac/qregion_mac.cpp b/src/widgets/platforms/mac/qregion_mac.cpp
new file mode 100644
index 0000000000..94805a7f80
--- /dev/null
+++ b/src/widgets/platforms/mac/qregion_mac.cpp
@@ -0,0 +1,286 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qt_mac_p.h>
+#include "qcoreapplication.h"
+#include <qlibrary.h>
+
+QT_BEGIN_NAMESPACE
+
+QRegion::QRegionData QRegion::shared_empty = { Q_BASIC_ATOMIC_INITIALIZER(1), 0 };
+
+#if defined(Q_WS_MAC32) && !defined(QT_MAC_USE_COCOA)
+#define RGN_CACHE_SIZE 200
+#ifdef RGN_CACHE_SIZE
+static bool rgncache_init = false;
+static int rgncache_used;
+static RgnHandle rgncache[RGN_CACHE_SIZE];
+static void qt_mac_cleanup_rgncache()
+{
+ rgncache_init = false;
+ for(int i = 0; i < RGN_CACHE_SIZE; ++i) {
+ if(rgncache[i]) {
+ --rgncache_used;
+ DisposeRgn(rgncache[i]);
+ rgncache[i] = 0;
+ }
+ }
+}
+#endif
+
+Q_WIDGETS_EXPORT RgnHandle qt_mac_get_rgn()
+{
+#ifdef RGN_CACHE_SIZE
+ if(!rgncache_init) {
+ rgncache_used = 0;
+ rgncache_init = true;
+ for(int i = 0; i < RGN_CACHE_SIZE; ++i)
+ rgncache[i] = 0;
+ qAddPostRoutine(qt_mac_cleanup_rgncache);
+ } else if(rgncache_used) {
+ for(int i = 0; i < RGN_CACHE_SIZE; ++i) {
+ if(rgncache[i]) {
+ RgnHandle ret = rgncache[i];
+ SetEmptyRgn(ret);
+ rgncache[i] = 0;
+ --rgncache_used;
+ return ret;
+ }
+ }
+ }
+#endif
+ return NewRgn();
+}
+
+Q_WIDGETS_EXPORT void qt_mac_dispose_rgn(RgnHandle r)
+{
+#ifdef RGN_CACHE_SIZE
+ if(rgncache_init && rgncache_used < RGN_CACHE_SIZE) {
+ for(int i = 0; i < RGN_CACHE_SIZE; ++i) {
+ if(!rgncache[i]) {
+ ++rgncache_used;
+ rgncache[i] = r;
+ return;
+ }
+ }
+ }
+#endif
+ DisposeRgn(r);
+}
+
+static OSStatus qt_mac_get_rgn_rect(UInt16 msg, RgnHandle, const Rect *rect, void *reg)
+{
+ if(msg == kQDRegionToRectsMsgParse) {
+ QRect rct(rect->left, rect->top, (rect->right - rect->left), (rect->bottom - rect->top));
+ if(!rct.isEmpty())
+ *((QRegion *)reg) += rct;
+ }
+ return noErr;
+}
+
+Q_WIDGETS_EXPORT QRegion qt_mac_convert_mac_region(RgnHandle rgn)
+{
+ return QRegion::fromQDRgn(rgn);
+}
+
+QRegion QRegion::fromQDRgn(RgnHandle rgn)
+{
+ QRegion ret;
+ ret.detach();
+ OSStatus oss = QDRegionToRects(rgn, kQDParseRegionFromTopLeft, qt_mac_get_rgn_rect, (void *)&ret);
+ if(oss != noErr)
+ return QRegion();
+ return ret;
+}
+
+/*!
+ \internal
+ Create's a RegionHandle, it's the caller's responsibility to release.
+*/
+RgnHandle QRegion::toQDRgn() const
+{
+ RgnHandle rgnHandle = qt_mac_get_rgn();
+ if(d->qt_rgn && d->qt_rgn->numRects) {
+ RgnHandle tmp_rgn = qt_mac_get_rgn();
+ int n = d->qt_rgn->numRects;
+ const QRect *qt_r = (n == 1) ? &d->qt_rgn->extents : d->qt_rgn->rects.constData();
+ while (n--) {
+ SetRectRgn(tmp_rgn,
+ qMax(SHRT_MIN, qt_r->x()),
+ qMax(SHRT_MIN, qt_r->y()),
+ qMin(SHRT_MAX, qt_r->right() + 1),
+ qMin(SHRT_MAX, qt_r->bottom() + 1));
+ UnionRgn(rgnHandle, tmp_rgn, rgnHandle);
+ ++qt_r;
+ }
+ qt_mac_dispose_rgn(tmp_rgn);
+ }
+ return rgnHandle;
+}
+
+/*!
+ \internal
+ Create's a RegionHandle, it's the caller's responsibility to release.
+ Returns 0 if the QRegion overflows.
+*/
+RgnHandle QRegion::toQDRgnForUpdate_sys() const
+{
+ RgnHandle rgnHandle = qt_mac_get_rgn();
+ if(d->qt_rgn && d->qt_rgn->numRects) {
+ RgnHandle tmp_rgn = qt_mac_get_rgn();
+ int n = d->qt_rgn->numRects;
+ const QRect *qt_r = (n == 1) ? &d->qt_rgn->extents : d->qt_rgn->rects.constData();
+ while (n--) {
+
+ // detect overflow. Tested for use with HIViewSetNeedsDisplayInRegion
+ // in QWidgetPrivate::update_sys().
+ enum { HIViewSetNeedsDisplayInRegionOverflow = 10000 }; // empirically determined conservative value
+ if (qt_r->right() > HIViewSetNeedsDisplayInRegionOverflow || qt_r->bottom() > HIViewSetNeedsDisplayInRegionOverflow) {
+ qt_mac_dispose_rgn(tmp_rgn);
+ qt_mac_dispose_rgn(rgnHandle);
+ return 0;
+ }
+
+ SetRectRgn(tmp_rgn,
+ qMax(SHRT_MIN, qt_r->x()),
+ qMax(SHRT_MIN, qt_r->y()),
+ qMin(SHRT_MAX, qt_r->right() + 1),
+ qMin(SHRT_MAX, qt_r->bottom() + 1));
+ UnionRgn(rgnHandle, tmp_rgn, rgnHandle);
+ ++qt_r;
+ }
+ qt_mac_dispose_rgn(tmp_rgn);
+ }
+ return rgnHandle;
+}
+
+#endif
+
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+OSStatus QRegion::shape2QRegionHelper(int inMessage, HIShapeRef,
+ const CGRect *inRect, void *inRefcon)
+{
+ QRegion *region = static_cast<QRegion *>(inRefcon);
+ if (!region)
+ return paramErr;
+
+ switch (inMessage) {
+ case kHIShapeEnumerateRect:
+ *region += QRect(inRect->origin.x, inRect->origin.y,
+ inRect->size.width, inRect->size.height);
+ break;
+ case kHIShapeEnumerateInit:
+ // Assume the region is already setup correctly
+ case kHIShapeEnumerateTerminate:
+ default:
+ break;
+ }
+ return noErr;
+}
+#endif
+
+/*!
+ \internal
+ Create's a mutable shape, it's the caller's responsibility to release.
+ WARNING: this function clamps the coordinates to SHRT_MIN/MAX on 10.4 and below.
+*/
+HIMutableShapeRef QRegion::toHIMutableShape() const
+{
+ HIMutableShapeRef shape = HIShapeCreateMutable();
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
+ if (d->qt_rgn && d->qt_rgn->numRects) {
+ int n = d->qt_rgn->numRects;
+ const QRect *qt_r = (n == 1) ? &d->qt_rgn->extents : d->qt_rgn->rects.constData();
+ while (n--) {
+ CGRect cgRect = CGRectMake(qt_r->x(), qt_r->y(), qt_r->width(), qt_r->height());
+ HIShapeUnionWithRect(shape, &cgRect);
+ ++qt_r;
+ }
+ }
+ } else
+#endif
+ {
+#ifndef QT_MAC_USE_COCOA
+ QCFType<HIShapeRef> qdShape = HIShapeCreateWithQDRgn(QMacSmartQuickDrawRegion(toQDRgn()));
+ HIShapeUnion(qdShape, shape, shape);
+#endif
+ }
+ return shape;
+}
+
+#if !defined(Q_WS_MAC64) && !defined(QT_MAC_USE_COCOA)
+typedef OSStatus (*PtrHIShapeGetAsQDRgn)(HIShapeRef, RgnHandle);
+static PtrHIShapeGetAsQDRgn ptrHIShapeGetAsQDRgn = 0;
+#endif
+
+
+QRegion QRegion::fromHIShapeRef(HIShapeRef shape)
+{
+ QRegion returnRegion;
+ returnRegion.detach();
+ // Begin gratuitous #if-defery
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+# ifndef Q_WS_MAC64
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
+# endif
+ HIShapeEnumerate(shape, kHIShapeParseFromTopLeft, shape2QRegionHelper, &returnRegion);
+# ifndef Q_WS_MAC64
+ } else
+# endif
+#endif
+ {
+#if !defined(Q_WS_MAC64) && !defined(QT_MAC_USE_COCOA)
+ if (ptrHIShapeGetAsQDRgn == 0) {
+ QLibrary library(QLatin1String("/System/Library/Frameworks/Carbon.framework/Carbon"));
+ library.setLoadHints(QLibrary::ExportExternalSymbolsHint);
+ ptrHIShapeGetAsQDRgn = reinterpret_cast<PtrHIShapeGetAsQDRgn>(library.resolve("HIShapeGetAsQDRgn"));
+ }
+ RgnHandle rgn = qt_mac_get_rgn();
+ ptrHIShapeGetAsQDRgn(shape, rgn);
+ returnRegion = QRegion::fromQDRgn(rgn);
+ qt_mac_dispose_rgn(rgn);
+#endif
+ }
+ return returnRegion;
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/mac/qsound_mac.mm b/src/widgets/platforms/mac/qsound_mac.mm
new file mode 100644
index 0000000000..8c8ac0818b
--- /dev/null
+++ b/src/widgets/platforms/mac/qsound_mac.mm
@@ -0,0 +1,190 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <qapplication.h>
+#include "qsound.h"
+#include "qsound_p.h"
+#include <private/qt_mac_p.h>
+#include <qhash.h>
+#include <qdebug.h>
+#import <AppKit/AppKit.h>
+
+#include <AppKit/NSSound.h>
+
+QT_BEGIN_NAMESPACE
+
+void qt_mac_beep()
+{
+ NSBeep();
+}
+
+QT_END_NAMESPACE
+
+#ifndef QT_NO_SOUND
+
+QT_BEGIN_NAMESPACE
+
+typedef QHash<QSound *, NSSound const *> Sounds;
+static Sounds sounds;
+
+class QAuServerMac : public QAuServer
+{
+ Q_OBJECT
+public:
+ QAuServerMac(QObject* parent) : QAuServer(parent) { }
+ void play(const QString& filename);
+ void play(QSound *s);
+ void stop(QSound*);
+ bool okay() { return true; }
+ using QAuServer::decLoop; // promote to public.
+protected:
+ NSSound *createNSSound(const QString &filename, QSound *qSound);
+};
+
+QT_END_NAMESPACE
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5
+@protocol NSSoundDelegate <NSObject>
+-(void)sound:(NSSound *)sound didFinishPlaying:(BOOL)aBool;
+@end
+#endif
+
+QT_USE_NAMESPACE
+
+@interface QT_MANGLE_NAMESPACE(QMacSoundDelegate) : NSObject<NSSoundDelegate> {
+ QSound *qSound; // may be null.
+ QAuServerMac* server;
+}
+-(id)initWithQSound:(QSound*)sound:(QAuServerMac*)server;
+@end
+
+@implementation QT_MANGLE_NAMESPACE(QMacSoundDelegate)
+-(id)initWithQSound:(QSound*)s:(QAuServerMac*)serv {
+ self = [super init];
+ if(self) {
+ qSound = s;
+ server = serv;
+ }
+ return self;
+}
+
+// Delegate function that gets called each time a sound finishes.
+-(void)sound:(NSSound *)sound didFinishPlaying:(BOOL)finishedOk
+{
+ // qSound is null if this sound was started by play(QString),
+ // in which case there is no corresponding QSound object.
+ if (qSound == 0) {
+ [sound release];
+ [self release];
+ return;
+ }
+
+ // finishedOk is false if the sound cold not be played or
+ // if it was interrupted by stop().
+ if (finishedOk == false) {
+ sounds.remove(qSound);
+ [sound release];
+ [self release];
+ return;
+ }
+
+ // Check if the sound should loop "forever" (until stop).
+ if (qSound->loops() == -1) {
+ [sound play];
+ return;
+ }
+
+ const int remainingIterations = server->decLoop(qSound);
+ if (remainingIterations > 0) {
+ [sound play];
+ } else {
+ sounds.remove(qSound);
+ [sound release];
+ [self release];
+ }
+}
+@end
+
+QT_BEGIN_NAMESPACE
+
+void QAuServerMac::play(const QString &fileName)
+{
+ QMacCocoaAutoReleasePool pool;
+ NSSound * const nsSound = createNSSound(fileName, 0);
+ [nsSound play];
+}
+
+void QAuServerMac::play(QSound *qSound)
+{
+ QMacCocoaAutoReleasePool pool;
+ NSSound * const nsSound = createNSSound(qSound->fileName(), qSound);
+ [nsSound play];
+ // Keep track of the nsSound object so we can find it again in stop().
+ sounds[qSound] = nsSound;
+}
+
+void QAuServerMac::stop(QSound *qSound)
+{
+ Sounds::const_iterator it = sounds.constFind(qSound);
+ if (it != sounds.constEnd())
+ [*it stop];
+}
+
+// Creates an NSSound object and installs a "sound finished" callack delegate on it.
+NSSound *QAuServerMac::createNSSound(const QString &fileName, QSound *qSound)
+{
+ NSString *nsFileName = const_cast<NSString *>(reinterpret_cast<const NSString *>(QCFString::toCFStringRef(fileName)));
+ NSSound * const nsSound = [[NSSound alloc] initWithContentsOfFile: nsFileName byReference:YES];
+ QT_MANGLE_NAMESPACE(QMacSoundDelegate) * const delegate = [[QT_MANGLE_NAMESPACE(QMacSoundDelegate) alloc] initWithQSound:qSound:this];
+ [nsSound setDelegate:delegate];
+ [nsFileName release];
+ return nsSound;
+}
+
+QAuServer* qt_new_audio_server()
+{
+ return new QAuServerMac(qApp);
+}
+
+QT_END_NAMESPACE
+
+#include "qsound_mac.moc"
+
+#endif // QT_NO_SOUND
diff --git a/src/widgets/platforms/mac/qt_cocoa_helpers_mac.mm b/src/widgets/platforms/mac/qt_cocoa_helpers_mac.mm
new file mode 100644
index 0000000000..d1620b1489
--- /dev/null
+++ b/src/widgets/platforms/mac/qt_cocoa_helpers_mac.mm
@@ -0,0 +1,1563 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** Copyright (c) 2007-2008, Apple, Inc.
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**
+** * Redistributions of source code must retain the above copyright notice,
+** this list of conditions and the following disclaimer.
+**
+** * Redistributions in binary form must reproduce the above copyright notice,
+** this list of conditions and the following disclaimer in the documentation
+** and/or other materials provided with the distribution.
+**
+** * Neither the name of Apple, Inc. nor the names of its contributors
+** may be used to endorse or promote products derived from this software
+** without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**
+****************************************************************************/
+
+#include <private/qcore_mac_p.h>
+#include <qaction.h>
+#include <qwidget.h>
+#include <qdesktopwidget.h>
+#include <qevent.h>
+#include <qpixmapcache.h>
+#include <qvarlengtharray.h>
+#include <private/qevent_p.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+#include <private/qt_mac_p.h>
+#include <private/qapplication_p.h>
+#include <private/qcocoaapplication_mac_p.h>
+#include <private/qcocoawindow_mac_p.h>
+#include <private/qcocoaview_mac_p.h>
+#include <private/qkeymapper_p.h>
+#include <private/qwidget_p.h>
+#include <private/qcocoawindow_mac_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifdef QT_MAC_USE_COCOA
+// Cmd + left mousebutton should produce a right button
+// press (mainly for mac users with one-button mice):
+static bool qt_leftButtonIsRightButton = false;
+#endif
+
+Q_GLOBAL_STATIC(QMacWindowFader, macwindowFader);
+
+QMacWindowFader::QMacWindowFader()
+ : m_duration(0.250)
+{
+}
+
+QMacWindowFader *QMacWindowFader::currentFader()
+{
+ return macwindowFader();
+}
+
+void QMacWindowFader::registerWindowToFade(QWidget *window)
+{
+ m_windowsToFade.append(window);
+}
+
+void QMacWindowFader::performFade()
+{
+ const QWidgetList myWidgetsToFade = m_windowsToFade;
+ const int widgetCount = myWidgetsToFade.count();
+#if QT_MAC_USE_COCOA
+ QMacCocoaAutoReleasePool pool;
+ [NSAnimationContext beginGrouping];
+ [[NSAnimationContext currentContext] setDuration:NSTimeInterval(m_duration)];
+#endif
+
+ for (int i = 0; i < widgetCount; ++i) {
+ QWidget *widget = m_windowsToFade.at(i);
+ OSWindowRef window = qt_mac_window_for(widget);
+#if QT_MAC_USE_COCOA
+ [[window animator] setAlphaValue:0.0];
+ QTimer::singleShot(qRound(m_duration * 1000), widget, SLOT(hide()));
+#else
+ TransitionWindowOptions options = {0, m_duration, 0, 0};
+ TransitionWindowWithOptions(window, kWindowFadeTransitionEffect, kWindowHideTransitionAction,
+ 0, 1, &options);
+#endif
+ }
+#if QT_MAC_USE_COCOA
+ [NSAnimationContext endGrouping];
+#endif
+ m_duration = 0.250;
+ m_windowsToFade.clear();
+}
+
+extern bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event); // qapplication.cpp;
+extern QWidget * mac_mouse_grabber;
+extern QWidget *qt_button_down; //qapplication_mac.cpp
+extern QPointer<QWidget> qt_last_mouse_receiver;
+extern OSViewRef qt_mac_effectiveview_for(const QWidget *w);
+extern void qt_mac_updateCursorWithWidgetUnderMouse(QWidget *widgetUnderMouse); // qcursor_mac.mm
+
+void macWindowFade(void * /*OSWindowRef*/ window, float durationSeconds)
+{
+#ifdef QT_MAC_USE_COCOA
+ QMacCocoaAutoReleasePool pool;
+#endif
+ OSWindowRef wnd = static_cast<OSWindowRef>(window);
+ if (wnd) {
+ QWidget *widget;
+#if QT_MAC_USE_COCOA
+ widget = [wnd QT_MANGLE_NAMESPACE(qt_qwidget)];
+#else
+ const UInt32 kWidgetCreatorQt = kEventClassQt;
+ enum {
+ kWidgetPropertyQWidget = 'QWId' //QWidget *
+ };
+ if (GetWindowProperty(static_cast<WindowRef>(window), kWidgetCreatorQt, kWidgetPropertyQWidget, sizeof(widget), 0, &widget) != noErr)
+ widget = 0;
+#endif
+ if (widget) {
+ QMacWindowFader::currentFader()->setFadeDuration(durationSeconds);
+ QMacWindowFader::currentFader()->registerWindowToFade(widget);
+ QMacWindowFader::currentFader()->performFade();
+ }
+ }
+}
+struct dndenum_mapper
+{
+ NSDragOperation mac_code;
+ Qt::DropAction qt_code;
+ bool Qt2Mac;
+};
+
+#if defined(QT_MAC_USE_COCOA) && defined(__OBJC__)
+
+static dndenum_mapper dnd_enums[] = {
+ { NSDragOperationLink, Qt::LinkAction, true },
+ { NSDragOperationMove, Qt::MoveAction, true },
+ { NSDragOperationCopy, Qt::CopyAction, true },
+ { NSDragOperationGeneric, Qt::CopyAction, false },
+ { NSDragOperationEvery, Qt::ActionMask, false },
+ { NSDragOperationNone, Qt::IgnoreAction, false }
+};
+
+NSDragOperation qt_mac_mapDropAction(Qt::DropAction action)
+{
+ for (int i=0; dnd_enums[i].qt_code; i++) {
+ if (dnd_enums[i].Qt2Mac && (action & dnd_enums[i].qt_code)) {
+ return dnd_enums[i].mac_code;
+ }
+ }
+ return NSDragOperationNone;
+}
+
+NSDragOperation qt_mac_mapDropActions(Qt::DropActions actions)
+{
+ NSDragOperation nsActions = NSDragOperationNone;
+ for (int i=0; dnd_enums[i].qt_code; i++) {
+ if (dnd_enums[i].Qt2Mac && (actions & dnd_enums[i].qt_code))
+ nsActions |= dnd_enums[i].mac_code;
+ }
+ return nsActions;
+}
+
+Qt::DropAction qt_mac_mapNSDragOperation(NSDragOperation nsActions)
+{
+ Qt::DropAction action = Qt::IgnoreAction;
+ for (int i=0; dnd_enums[i].mac_code; i++) {
+ if (nsActions & dnd_enums[i].mac_code)
+ return dnd_enums[i].qt_code;
+ }
+ return action;
+}
+
+Qt::DropActions qt_mac_mapNSDragOperations(NSDragOperation nsActions)
+{
+ Qt::DropActions actions = Qt::IgnoreAction;
+ for (int i=0; dnd_enums[i].mac_code; i++) {
+ if (nsActions & dnd_enums[i].mac_code)
+ actions |= dnd_enums[i].qt_code;
+ }
+ return actions;
+}
+
+Q_GLOBAL_STATIC(DnDParams, currentDnDParameters);
+DnDParams *macCurrentDnDParameters()
+{
+ return currentDnDParameters();
+}
+#endif
+
+bool macWindowIsTextured( void * /*OSWindowRef*/ window )
+{
+ OSWindowRef wnd = static_cast<OSWindowRef>(window);
+#if QT_MAC_USE_COCOA
+ return ( [wnd styleMask] & NSTexturedBackgroundWindowMask ) ? true : false;
+#else
+ WindowAttributes currentAttributes;
+ GetWindowAttributes(wnd, &currentAttributes);
+ return (currentAttributes & kWindowMetalAttribute) ? true : false;
+#endif
+}
+
+void macWindowToolbarShow(const QWidget *widget, bool show )
+{
+ OSWindowRef wnd = qt_mac_window_for(widget);
+#if QT_MAC_USE_COCOA
+ if (NSToolbar *toolbar = [wnd toolbar]) {
+ QMacCocoaAutoReleasePool pool;
+ if (show != [toolbar isVisible]) {
+ [toolbar setVisible:show];
+ } else {
+ // The toolbar may be in sync, but we are not, update our framestrut.
+ qt_widget_private(const_cast<QWidget *>(widget))->updateFrameStrut();
+ }
+ }
+#else
+ qt_widget_private(const_cast<QWidget *>(widget))->updateFrameStrut();
+ ShowHideWindowToolbar(wnd, show, false);
+#endif
+}
+
+
+void macWindowToolbarSet( void * /*OSWindowRef*/ window, void *toolbarRef )
+{
+ OSWindowRef wnd = static_cast<OSWindowRef>(window);
+#if QT_MAC_USE_COCOA
+ [wnd setToolbar:static_cast<NSToolbar *>(toolbarRef)];
+#else
+ SetWindowToolbar(wnd, static_cast<HIToolbarRef>(toolbarRef));
+#endif
+}
+
+bool macWindowToolbarIsVisible( void * /*OSWindowRef*/ window )
+{
+ OSWindowRef wnd = static_cast<OSWindowRef>(window);
+#if QT_MAC_USE_COCOA
+ if (NSToolbar *toolbar = [wnd toolbar])
+ return [toolbar isVisible];
+ return false;
+#else
+ return IsWindowToolbarVisible(wnd);
+#endif
+}
+
+void macWindowSetHasShadow( void * /*OSWindowRef*/ window, bool hasShadow )
+{
+ OSWindowRef wnd = static_cast<OSWindowRef>(window);
+#if QT_MAC_USE_COCOA
+ [wnd setHasShadow:BOOL(hasShadow)];
+#else
+ if (hasShadow)
+ ChangeWindowAttributes(wnd, 0, kWindowNoShadowAttribute);
+ else
+ ChangeWindowAttributes(wnd, kWindowNoShadowAttribute, 0);
+#endif
+}
+
+void macWindowFlush(void * /*OSWindowRef*/ window)
+{
+ OSWindowRef wnd = static_cast<OSWindowRef>(window);
+#if QT_MAC_USE_COCOA
+ [wnd flushWindowIfNeeded];
+#else
+ HIWindowFlush(wnd);
+#endif
+}
+
+void qt_mac_update_mouseTracking(QWidget *widget)
+{
+#ifdef QT_MAC_USE_COCOA
+ [qt_mac_nativeview_for(widget) updateTrackingAreas];
+#else
+ Q_UNUSED(widget);
+#endif
+}
+
+OSStatus qt_mac_drawCGImage(CGContextRef inContext, const CGRect *inBounds, CGImageRef inImage)
+{
+ // Verbatim copy if HIViewDrawCGImage (as shown on Carbon-Dev)
+ OSStatus err = noErr;
+
+ require_action(inContext != NULL, InvalidContext, err = paramErr);
+ require_action(inBounds != NULL, InvalidBounds, err = paramErr);
+ require_action(inImage != NULL, InvalidImage, err = paramErr);
+
+ CGContextSaveGState( inContext );
+ CGContextTranslateCTM (inContext, 0, inBounds->origin.y + CGRectGetMaxY(*inBounds));
+ CGContextScaleCTM(inContext, 1, -1);
+
+ CGContextDrawImage(inContext, *inBounds, inImage);
+
+ CGContextRestoreGState(inContext);
+InvalidImage:
+InvalidBounds:
+InvalidContext:
+ return err;
+}
+
+bool qt_mac_checkForNativeSizeGrip(const QWidget *widget)
+{
+#ifndef QT_MAC_USE_COCOA
+ OSViewRef nativeSizeGrip = 0;
+ HIViewFindByID(HIViewGetRoot(HIViewGetWindow(HIViewRef(widget->winId()))), kHIViewWindowGrowBoxID, &nativeSizeGrip);
+ return (nativeSizeGrip != 0);
+#else
+ return [[reinterpret_cast<NSView *>(widget->effectiveWinId()) window] showsResizeIndicator];
+#endif
+}
+struct qt_mac_enum_mapper
+{
+ int mac_code;
+ int qt_code;
+#if defined(DEBUG_MOUSE_MAPS)
+# define QT_MAC_MAP_ENUM(x) x, #x
+ const char *desc;
+#else
+# define QT_MAC_MAP_ENUM(x) x
+#endif
+};
+
+//mouse buttons
+static qt_mac_enum_mapper qt_mac_mouse_symbols[] = {
+{ kEventMouseButtonPrimary, QT_MAC_MAP_ENUM(Qt::LeftButton) },
+{ kEventMouseButtonSecondary, QT_MAC_MAP_ENUM(Qt::RightButton) },
+{ kEventMouseButtonTertiary, QT_MAC_MAP_ENUM(Qt::MidButton) },
+{ 4, QT_MAC_MAP_ENUM(Qt::XButton1) },
+{ 5, QT_MAC_MAP_ENUM(Qt::XButton2) },
+{ 0, QT_MAC_MAP_ENUM(0) }
+};
+Qt::MouseButtons qt_mac_get_buttons(int buttons)
+{
+#ifdef DEBUG_MOUSE_MAPS
+ qDebug("Qt: internal: **Mapping buttons: %d (0x%04x)", buttons, buttons);
+#endif
+ Qt::MouseButtons ret = Qt::NoButton;
+ for(int i = 0; qt_mac_mouse_symbols[i].qt_code; i++) {
+ if (buttons & (0x01<<(qt_mac_mouse_symbols[i].mac_code-1))) {
+#ifdef DEBUG_MOUSE_MAPS
+ qDebug("Qt: internal: got button: %s", qt_mac_mouse_symbols[i].desc);
+#endif
+ ret |= Qt::MouseButtons(qt_mac_mouse_symbols[i].qt_code);
+ }
+ }
+ return ret;
+}
+Qt::MouseButton qt_mac_get_button(EventMouseButton button)
+{
+#ifdef DEBUG_MOUSE_MAPS
+ qDebug("Qt: internal: **Mapping button: %d (0x%04x)", button, button);
+#endif
+ Qt::MouseButtons ret = 0;
+ for(int i = 0; qt_mac_mouse_symbols[i].qt_code; i++) {
+ if (button == qt_mac_mouse_symbols[i].mac_code) {
+#ifdef DEBUG_MOUSE_MAPS
+ qDebug("Qt: internal: got button: %s", qt_mac_mouse_symbols[i].desc);
+#endif
+ return Qt::MouseButton(qt_mac_mouse_symbols[i].qt_code);
+ }
+ }
+ return Qt::NoButton;
+}
+
+void macSendToolbarChangeEvent(QWidget *widget)
+{
+ QToolBarChangeEvent ev(!(GetCurrentKeyModifiers() & cmdKey));
+ qt_sendSpontaneousEvent(widget, &ev);
+}
+
+Q_GLOBAL_STATIC(QMacTabletHash, tablet_hash)
+QMacTabletHash *qt_mac_tablet_hash()
+{
+ return tablet_hash();
+}
+
+#ifdef QT_MAC_USE_COCOA
+
+// Clears the QWidget pointer that each QCocoaView holds.
+void qt_mac_clearCocoaViewQWidgetPointers(QWidget *widget)
+{
+ QT_MANGLE_NAMESPACE(QCocoaView) *cocoaView = reinterpret_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(qt_mac_nativeview_for(widget));
+ if (cocoaView && [cocoaView respondsToSelector:@selector(qt_qwidget)]) {
+ [cocoaView qt_clearQWidget];
+ }
+}
+
+void qt_dispatchTabletProximityEvent(void * /*NSEvent * */ tabletEvent)
+{
+ NSEvent *proximityEvent = static_cast<NSEvent *>(tabletEvent);
+ // simply construct a Carbon proximity record and handle it all in one spot.
+ TabletProximityRec carbonProximityRec = { [proximityEvent vendorID],
+ [proximityEvent tabletID],
+ [proximityEvent pointingDeviceID],
+ [proximityEvent deviceID],
+ [proximityEvent systemTabletID],
+ [proximityEvent vendorPointingDeviceType],
+ [proximityEvent pointingDeviceSerialNumber],
+ [proximityEvent uniqueID],
+ [proximityEvent capabilityMask],
+ [proximityEvent pointingDeviceType],
+ [proximityEvent isEnteringProximity] };
+ qt_dispatchTabletProximityEvent(carbonProximityRec);
+}
+#endif // QT_MAC_USE_COCOA
+
+void qt_dispatchTabletProximityEvent(const ::TabletProximityRec &proxRec)
+{
+ QTabletDeviceData proximityDevice;
+ proximityDevice.tabletUniqueID = proxRec.uniqueID;
+ proximityDevice.capabilityMask = proxRec.capabilityMask;
+
+ switch (proxRec.pointerType) {
+ case NSUnknownPointingDevice:
+ default:
+ proximityDevice.tabletPointerType = QTabletEvent::UnknownPointer;
+ break;
+ case NSPenPointingDevice:
+ proximityDevice.tabletPointerType = QTabletEvent::Pen;
+ break;
+ case NSCursorPointingDevice:
+ proximityDevice.tabletPointerType = QTabletEvent::Cursor;
+ break;
+ case NSEraserPointingDevice:
+ proximityDevice.tabletPointerType = QTabletEvent::Eraser;
+ break;
+ }
+ uint bits = proxRec.vendorPointerType;
+ if (bits == 0 && proximityDevice.tabletUniqueID != 0) {
+ // Fallback. It seems that the driver doesn't always include all the information.
+ // High-End Wacom devices store their "type" in the uper bits of the Unique ID.
+ // I'm not sure how to handle it for consumer devices, but I'll test that in a bit.
+ bits = proximityDevice.tabletUniqueID >> 32;
+ }
+ // Defined in the "EN0056-NxtGenImpGuideX"
+ // on Wacom's Developer Website (www.wacomeng.com)
+ if (((bits & 0x0006) == 0x0002) && ((bits & 0x0F06) != 0x0902)) {
+ proximityDevice.tabletDeviceType = QTabletEvent::Stylus;
+ } else {
+ switch (bits & 0x0F06) {
+ case 0x0802:
+ proximityDevice.tabletDeviceType = QTabletEvent::Stylus;
+ break;
+ case 0x0902:
+ proximityDevice.tabletDeviceType = QTabletEvent::Airbrush;
+ break;
+ case 0x0004:
+ proximityDevice.tabletDeviceType = QTabletEvent::FourDMouse;
+ break;
+ case 0x0006:
+ proximityDevice.tabletDeviceType = QTabletEvent::Puck;
+ break;
+ case 0x0804:
+ proximityDevice.tabletDeviceType = QTabletEvent::RotationStylus;
+ break;
+ default:
+ proximityDevice.tabletDeviceType = QTabletEvent::NoDevice;
+ }
+ }
+ // The deviceID is "unique" while in the proximity, it's a key that we can use for
+ // linking up TabletDeviceData to an event (especially if there are two devices in action).
+ bool entering = proxRec.enterProximity;
+ if (entering) {
+ qt_mac_tablet_hash()->insert(proxRec.deviceID, proximityDevice);
+ } else {
+ qt_mac_tablet_hash()->remove(proxRec.deviceID);
+ }
+
+ QTabletEvent qtabletProximity(entering ? QEvent::TabletEnterProximity
+ : QEvent::TabletLeaveProximity,
+ QPoint(), QPoint(), QPointF(), proximityDevice.tabletDeviceType,
+ proximityDevice.tabletPointerType, 0., 0, 0, 0., 0., 0, 0,
+ proximityDevice.tabletUniqueID);
+
+ qt_sendSpontaneousEvent(qApp, &qtabletProximity);
+}
+
+#ifdef QT_MAC_USE_COCOA
+
+Qt::KeyboardModifiers qt_cocoaModifiers2QtModifiers(ulong modifierFlags)
+{
+ Qt::KeyboardModifiers qtMods =Qt::NoModifier;
+ if (modifierFlags & NSShiftKeyMask)
+ qtMods |= Qt::ShiftModifier;
+ if (modifierFlags & NSControlKeyMask)
+ qtMods |= Qt::MetaModifier;
+ if (modifierFlags & NSAlternateKeyMask)
+ qtMods |= Qt::AltModifier;
+ if (modifierFlags & NSCommandKeyMask)
+ qtMods |= Qt::ControlModifier;
+ if (modifierFlags & NSNumericPadKeyMask)
+ qtMods |= Qt::KeypadModifier;
+ return qtMods;
+}
+
+Qt::KeyboardModifiers qt_cocoaDragOperation2QtModifiers(uint dragOperations)
+{
+ Qt::KeyboardModifiers qtMods =Qt::NoModifier;
+ if (dragOperations & NSDragOperationLink)
+ qtMods |= Qt::MetaModifier;
+ if (dragOperations & NSDragOperationGeneric)
+ qtMods |= Qt::ControlModifier;
+ if (dragOperations & NSDragOperationCopy)
+ qtMods |= Qt::AltModifier;
+ return qtMods;
+}
+
+static inline QEvent::Type cocoaEvent2QtEvent(NSUInteger eventType)
+{
+ // Handle the trivial cases that can be determined from the type.
+ switch (eventType) {
+ case NSKeyDown:
+ return QEvent::KeyPress;
+ case NSKeyUp:
+ return QEvent::KeyRelease;
+ case NSLeftMouseDown:
+ case NSRightMouseDown:
+ case NSOtherMouseDown:
+ return QEvent::MouseButtonPress;
+ case NSLeftMouseUp:
+ case NSRightMouseUp:
+ case NSOtherMouseUp:
+ return QEvent::MouseButtonRelease;
+ case NSMouseMoved:
+ case NSLeftMouseDragged:
+ case NSRightMouseDragged:
+ case NSOtherMouseDragged:
+ return QEvent::MouseMove;
+ case NSScrollWheel:
+ return QEvent::Wheel;
+ }
+ return QEvent::None;
+}
+
+static bool mustUseCocoaKeyEvent()
+{
+ QCFType<TISInputSourceRef> source = TISCopyCurrentKeyboardInputSource();
+ return TISGetInputSourceProperty(source, kTISPropertyUnicodeKeyLayoutData) == 0;
+}
+
+bool qt_dispatchKeyEventWithCocoa(void * /*NSEvent * */ keyEvent, QWidget *widgetToGetEvent)
+{
+ NSEvent *event = static_cast<NSEvent *>(keyEvent);
+ NSString *keyChars = [event charactersIgnoringModifiers];
+ int keyLength = [keyChars length];
+ if (keyLength == 0)
+ return false; // Dead Key, nothing to do!
+ bool ignoreText = false;
+ Qt::Key qtKey = Qt::Key_unknown;
+ if (keyLength == 1) {
+ QChar ch([keyChars characterAtIndex:0]);
+ if (ch.isLower())
+ ch = ch.toUpper();
+ qtKey = cocoaKey2QtKey(ch);
+ // Do not set the text for Function-Key Unicodes characters (0xF700–0xF8FF).
+ ignoreText = (ch.unicode() >= 0xF700 && ch.unicode() <= 0xF8FF);
+ }
+ Qt::KeyboardModifiers keyMods = qt_cocoaModifiers2QtModifiers([event modifierFlags]);
+ QString text;
+
+ // To quote from the Carbon port: This is actually wrong--but it is the best that
+ // can be done for now because of the Control/Meta mapping issues
+ // (we always get text on the Mac)
+ if (!ignoreText && !(keyMods & (Qt::ControlModifier | Qt::MetaModifier)))
+ text = QCFString::toQString(reinterpret_cast<CFStringRef>(keyChars));
+
+ UInt32 macScanCode = 1;
+ QKeyEventEx ke(cocoaEvent2QtEvent([event type]), qtKey, keyMods, text, [event isARepeat], qMax(1, keyLength),
+ macScanCode, [event keyCode], [event modifierFlags]);
+ return qt_sendSpontaneousEvent(widgetToGetEvent, &ke) && ke.isAccepted();
+}
+#endif
+
+Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum)
+{
+ if (buttonNum == 0)
+ return Qt::LeftButton;
+ if (buttonNum == 1)
+ return Qt::RightButton;
+ if (buttonNum == 2)
+ return Qt::MidButton;
+ if (buttonNum == 3)
+ return Qt::XButton1;
+ if (buttonNum == 4)
+ return Qt::XButton2;
+ return Qt::NoButton;
+}
+
+bool qt_dispatchKeyEvent(void * /*NSEvent * */ keyEvent, QWidget *widgetToGetEvent)
+{
+#ifndef QT_MAC_USE_COCOA
+ Q_UNUSED(keyEvent);
+ Q_UNUSED(widgetToGetEvent);
+ return false;
+#else
+ NSEvent *event = static_cast<NSEvent *>(keyEvent);
+ EventRef key_event = static_cast<EventRef>(const_cast<void *>([event eventRef]));
+ Q_ASSERT(key_event);
+ unsigned int info = 0;
+
+ if ([event type] == NSKeyDown) {
+ NSString *characters = [event characters];
+ if ([characters length]) {
+ unichar value = [characters characterAtIndex:0];
+ qt_keymapper_private()->updateKeyMap(0, key_event, (void *)&value);
+ info = value;
+ }
+ }
+
+ if (qt_mac_sendMacEventToWidget(widgetToGetEvent, key_event))
+ return true;
+
+ if (mustUseCocoaKeyEvent())
+ return qt_dispatchKeyEventWithCocoa(keyEvent, widgetToGetEvent);
+
+ bool consumed = qt_keymapper_private()->translateKeyEvent(widgetToGetEvent, 0, key_event, &info, true);
+ return consumed && (info != 0);
+#endif
+}
+
+void qt_dispatchModifiersChanged(void * /*NSEvent * */flagsChangedEvent, QWidget *widgetToGetEvent)
+{
+#ifndef QT_MAC_USE_COCOA
+ Q_UNUSED(flagsChangedEvent);
+ Q_UNUSED(widgetToGetEvent);
+#else
+ UInt32 modifiers = 0;
+ // Sync modifiers with Qt
+ NSEvent *event = static_cast<NSEvent *>(flagsChangedEvent);
+ EventRef key_event = static_cast<EventRef>(const_cast<void *>([event eventRef]));
+ Q_ASSERT(key_event);
+ GetEventParameter(key_event, kEventParamKeyModifiers, typeUInt32, 0,
+ sizeof(modifiers), 0, &modifiers);
+ extern void qt_mac_send_modifiers_changed(quint32 modifiers, QObject *object);
+ qt_mac_send_modifiers_changed(modifiers, widgetToGetEvent);
+#endif
+}
+
+QPointF flipPoint(const NSPoint &p)
+{
+ return QPointF(p.x, flipYCoordinate(p.y));
+}
+
+NSPoint flipPoint(const QPoint &p)
+{
+ return NSMakePoint(p.x(), flipYCoordinate(p.y()));
+}
+
+NSPoint flipPoint(const QPointF &p)
+{
+ return NSMakePoint(p.x(), flipYCoordinate(p.y()));
+}
+
+#if QT_MAC_USE_COCOA && __OBJC__
+
+void qt_mac_handleNonClientAreaMouseEvent(NSWindow *window, NSEvent *event)
+{
+ QWidget *widgetToGetEvent = [window QT_MANGLE_NAMESPACE(qt_qwidget)];
+ if (widgetToGetEvent == 0)
+ return;
+
+ NSEventType evtType = [event type];
+ QPoint qlocalPoint;
+ QPoint qglobalPoint;
+ bool processThisEvent = false;
+ bool fakeNCEvents = false;
+ bool fakeMouseEvents = false;
+
+ // Check if this is a mouse event.
+ if (evtType == NSLeftMouseDown || evtType == NSLeftMouseUp
+ || evtType == NSRightMouseDown || evtType == NSRightMouseUp
+ || evtType == NSOtherMouseDown || evtType == NSOtherMouseUp
+ || evtType == NSMouseMoved || evtType == NSLeftMouseDragged
+ || evtType == NSRightMouseDragged || evtType == NSOtherMouseDragged) {
+ // Check if we want to pass this message to another window
+ if (mac_mouse_grabber && mac_mouse_grabber != widgetToGetEvent) {
+ NSWindow *grabWindow = static_cast<NSWindow *>(qt_mac_window_for(mac_mouse_grabber));
+ if (window != grabWindow) {
+ window = grabWindow;
+ widgetToGetEvent = mac_mouse_grabber;
+ fakeNCEvents = true;
+ }
+ }
+ // Dont generate normal NC mouse events for Left Button dragged
+ if(evtType != NSLeftMouseDragged || fakeNCEvents) {
+ NSPoint windowPoint = [event locationInWindow];
+ NSPoint globalPoint = [[event window] convertBaseToScreen:windowPoint];
+ NSRect frameRect = [window frame];
+ if (fakeNCEvents || NSMouseInRect(globalPoint, frameRect, NO)) {
+ NSRect contentRect = [window contentRectForFrameRect:frameRect];
+ qglobalPoint = QPoint(flipPoint(globalPoint).toPoint());
+ QWidget *w = widgetToGetEvent->childAt(widgetToGetEvent->mapFromGlobal(qglobalPoint));
+ // check that the mouse pointer is on the non-client area and
+ // there are not widgets in it.
+ if (fakeNCEvents || (!NSMouseInRect(globalPoint, contentRect, NO) && !w)) {
+ qglobalPoint = QPoint(flipPoint(globalPoint).toPoint());
+ qlocalPoint = widgetToGetEvent->mapFromGlobal(qglobalPoint);
+ processThisEvent = true;
+ }
+ }
+ }
+ }
+ // This is not an NC area mouse message.
+ if (!processThisEvent)
+ return;
+
+ // If the window is frame less, generate fake mouse events instead. (floating QToolBar)
+ // or if someone already got an explicit or implicit grab
+ if (mac_mouse_grabber || qt_button_down ||
+ (fakeNCEvents && (widgetToGetEvent->window()->windowFlags() & Qt::FramelessWindowHint)))
+ fakeMouseEvents = true;
+
+ Qt::MouseButton button;
+ QEvent::Type eventType;
+ // Convert to Qt::Event type
+ switch (evtType) {
+ case NSLeftMouseDown:
+ button = Qt::LeftButton;
+ eventType = (!fakeMouseEvents) ? QEvent::NonClientAreaMouseButtonPress
+ : QEvent::MouseButtonPress;
+ break;
+ case NSLeftMouseUp:
+ button = Qt::LeftButton;
+ eventType = (!fakeMouseEvents) ? QEvent::NonClientAreaMouseButtonRelease
+ : QEvent::MouseButtonRelease;
+ break;
+ case NSRightMouseDown:
+ button = Qt::RightButton;
+ eventType = (!fakeMouseEvents) ? QEvent::NonClientAreaMouseButtonPress
+ : QEvent::MouseButtonPress;
+ break;
+ case NSRightMouseUp:
+ button = Qt::RightButton;
+ eventType = (!fakeMouseEvents) ? QEvent::NonClientAreaMouseButtonRelease
+ : QEvent::MouseButtonRelease;
+ break;
+ case NSOtherMouseDown:
+ button = cocoaButton2QtButton([event buttonNumber]);
+ eventType = (!fakeMouseEvents) ? QEvent::NonClientAreaMouseButtonPress
+ : QEvent::MouseButtonPress;
+ break;
+ case NSOtherMouseUp:
+ button = cocoaButton2QtButton([event buttonNumber]);
+ eventType = (!fakeMouseEvents) ? QEvent::NonClientAreaMouseButtonRelease
+ : QEvent::MouseButtonRelease;
+ break;
+ case NSMouseMoved:
+ button = Qt::NoButton;
+ eventType = (!fakeMouseEvents) ? QEvent::NonClientAreaMouseMove
+ : QEvent::MouseMove;
+ break;
+ case NSLeftMouseDragged:
+ button = Qt::LeftButton;
+ eventType = (!fakeMouseEvents) ? QEvent::NonClientAreaMouseMove
+ : QEvent::MouseMove;
+ break;
+ case NSRightMouseDragged:
+ button = Qt::RightButton;
+ eventType = (!fakeMouseEvents) ? QEvent::NonClientAreaMouseMove
+ : QEvent::MouseMove;
+ break;
+ case NSOtherMouseDragged:
+ button = cocoaButton2QtButton([event buttonNumber]);
+ eventType = (!fakeMouseEvents) ? QEvent::NonClientAreaMouseMove
+ : QEvent::MouseMove;
+ break;
+ default:
+ qWarning("not handled! Non client area mouse message");
+ return;
+ }
+
+ Qt::KeyboardModifiers keyMods = qt_cocoaModifiers2QtModifiers([event modifierFlags]);
+ if (eventType == QEvent::NonClientAreaMouseButtonPress || eventType == QEvent::MouseButtonPress) {
+ NSInteger clickCount = [event clickCount];
+ if (clickCount % 2 == 0)
+ eventType = (!fakeMouseEvents) ? QEvent::NonClientAreaMouseButtonDblClick
+ : QEvent::MouseButtonDblClick;
+ if (button == Qt::LeftButton && (keyMods & Qt::MetaModifier)) {
+ button = Qt::RightButton;
+ qt_leftButtonIsRightButton = true;
+ }
+ } else if (eventType == QEvent::NonClientAreaMouseButtonRelease || eventType == QEvent::MouseButtonRelease) {
+ if (button == Qt::LeftButton && qt_leftButtonIsRightButton) {
+ button = Qt::RightButton;
+ qt_leftButtonIsRightButton = false;
+ }
+ }
+
+ Qt::MouseButtons buttons = 0;
+ {
+ UInt32 mac_buttons;
+ if (GetEventParameter((EventRef)[event eventRef], kEventParamMouseChord, typeUInt32, 0,
+ sizeof(mac_buttons), 0, &mac_buttons) == noErr)
+ buttons = qt_mac_get_buttons(mac_buttons);
+ }
+
+ QMouseEvent qme(eventType, qlocalPoint, qglobalPoint, button, buttons, keyMods);
+ qt_sendSpontaneousEvent(widgetToGetEvent, &qme);
+
+ // We don't need to set the implicit grab widget here because we won't
+ // reach this point if then event type is Press over a Qt widget.
+ // However we might need to unset it if the event is Release.
+ if (eventType == QEvent::MouseButtonRelease)
+ qt_button_down = 0;
+}
+
+QWidget *qt_mac_getTargetForKeyEvent(QWidget *widgetThatReceivedEvent)
+{
+ if (QWidget *popup = QApplication::activePopupWidget()) {
+ QWidget *focusInPopup = popup->focusWidget();
+ return focusInPopup ? focusInPopup : popup;
+ }
+
+ QWidget *widgetToGetKey = qApp->focusWidget();
+ if (!widgetToGetKey)
+ widgetToGetKey = widgetThatReceivedEvent;
+
+ return widgetToGetKey;
+}
+
+// This function will find the widget that should receive the
+// mouse event. Because of explicit/implicit mouse grabs, popups,
+// etc, this might not end up being the same as the widget under
+// the mouse (which is more interresting when handling enter/leave
+// events
+QWidget *qt_mac_getTargetForMouseEvent(
+ // You can call this function without providing an event.
+ NSEvent *event,
+ QEvent::Type eventType,
+ QPoint &returnLocalPoint,
+ QPoint &returnGlobalPoint,
+ QWidget *nativeWidget,
+ QWidget **returnWidgetUnderMouse)
+{
+ Q_UNUSED(event);
+ NSPoint nsglobalpoint = event ? [[event window] convertBaseToScreen:[event locationInWindow]] : [NSEvent mouseLocation];
+ returnGlobalPoint = flipPoint(nsglobalpoint).toPoint();
+ QWidget *mouseGrabber = QWidget::mouseGrabber();
+ bool buttonDownNotBlockedByModal = qt_button_down && !QApplicationPrivate::isBlockedByModal(qt_button_down);
+ QWidget *popup = QApplication::activePopupWidget();
+
+ // Resolve the widget under the mouse:
+ QWidget *widgetUnderMouse = 0;
+ if (popup || qt_button_down || !nativeWidget || !nativeWidget->isVisible()) {
+ // Using QApplication::widgetAt for finding the widget under the mouse
+ // is most safe, since it ignores cocoas own mouse down redirections (which
+ // we need to be prepared for when using nativeWidget as starting point).
+ // (the only exception is for QMacNativeWidget, where QApplication::widgetAt fails).
+ // But it is also slower (I guess), so we try to avoid it and use nativeWidget if we can:
+ widgetUnderMouse = QApplication::widgetAt(returnGlobalPoint);
+ }
+
+ if (!widgetUnderMouse && nativeWidget) {
+ // Entering here should be the common case. We
+ // also handle the QMacNativeWidget fallback case.
+ QPoint p = nativeWidget->mapFromGlobal(returnGlobalPoint);
+ widgetUnderMouse = nativeWidget->childAt(p);
+ if (!widgetUnderMouse && nativeWidget->rect().contains(p))
+ widgetUnderMouse = nativeWidget;
+ }
+
+ if (widgetUnderMouse) {
+ // Check if widgetUnderMouse is blocked by a modal
+ // window, or the mouse if over the frame strut:
+ if (widgetUnderMouse == qt_button_down) {
+ // Small optimization to avoid an extra call to isBlockedByModal:
+ if (buttonDownNotBlockedByModal == false)
+ widgetUnderMouse = 0;
+ } else if (QApplicationPrivate::isBlockedByModal(widgetUnderMouse)) {
+ widgetUnderMouse = 0;
+ }
+
+ if (widgetUnderMouse && widgetUnderMouse->isWindow()) {
+ // Exclude the titlebar (and frame strut) when finding widget under mouse:
+ QPoint p = widgetUnderMouse->mapFromGlobal(returnGlobalPoint);
+ if (!widgetUnderMouse->rect().contains(p))
+ widgetUnderMouse = 0;
+ }
+ }
+ if (returnWidgetUnderMouse)
+ *returnWidgetUnderMouse = widgetUnderMouse;
+
+ // Resolve the target for the mouse event. Default will be
+ // widgetUnderMouse, except if there is a grab (popup/mouse/button-down):
+ if (popup && !mouseGrabber) {
+ // We special case handling of popups, since they have an implicitt mouse grab.
+ QWidget *candidate = buttonDownNotBlockedByModal ? qt_button_down : widgetUnderMouse;
+ if (!popup->isAncestorOf(candidate)) {
+ // INVARIANT: we have a popup, but the candidate is not
+ // in it. But the popup will grab the mouse anyway,
+ // except if the user scrolls:
+ if (eventType == QEvent::Wheel)
+ return 0;
+ returnLocalPoint = popup->mapFromGlobal(returnGlobalPoint);
+ return popup;
+ } else if (popup == candidate) {
+ // INVARIANT: The candidate is the popup itself, and not a child:
+ returnLocalPoint = popup->mapFromGlobal(returnGlobalPoint);
+ return popup;
+ } else {
+ // INVARIANT: The candidate is a child inside the popup:
+ returnLocalPoint = candidate->mapFromGlobal(returnGlobalPoint);
+ return candidate;
+ }
+ }
+
+ QWidget *target = mouseGrabber;
+ if (!target && buttonDownNotBlockedByModal)
+ target = qt_button_down;
+ if (!target)
+ target = widgetUnderMouse;
+ if (!target)
+ return 0;
+
+ returnLocalPoint = target->mapFromGlobal(returnGlobalPoint);
+ return target;
+}
+
+QPointer<QWidget> qt_last_native_mouse_receiver = 0;
+
+static inline void qt_mac_checkEnterLeaveForNativeWidgets(QWidget *maybeEnterWidget)
+{
+ // Dispatch enter/leave for the cases where QApplicationPrivate::sendMouseEvent do
+ // not. This will in general be the cases when alien widgets are not involved:
+ // 1. from a native widget to another native widget or
+ // 2. from a native widget to no widget
+ // 3. from no widget to a native or alien widget
+
+ if (qt_button_down || QWidget::mouseGrabber())
+ return;
+
+ if ((maybeEnterWidget == qt_last_native_mouse_receiver) && qt_last_native_mouse_receiver)
+ return;
+ if (maybeEnterWidget) {
+ if (!qt_last_native_mouse_receiver) {
+ // case 3
+ QApplicationPrivate::dispatchEnterLeave(maybeEnterWidget, 0);
+ qt_last_native_mouse_receiver = maybeEnterWidget->internalWinId() ? maybeEnterWidget : maybeEnterWidget->nativeParentWidget();
+ } else if (maybeEnterWidget->internalWinId()) {
+ // case 1
+ QApplicationPrivate::dispatchEnterLeave(maybeEnterWidget, qt_last_native_mouse_receiver);
+ qt_last_native_mouse_receiver = maybeEnterWidget->internalWinId() ? maybeEnterWidget : maybeEnterWidget->nativeParentWidget();
+ } // else at lest one of the widgets are alien, so enter/leave will be handled in QApplicationPrivate
+ } else {
+ if (qt_last_native_mouse_receiver) {
+ // case 2
+ QApplicationPrivate::dispatchEnterLeave(0, qt_last_native_mouse_receiver);
+ qt_last_mouse_receiver = 0;
+ qt_last_native_mouse_receiver = 0;
+ }
+ }
+}
+
+bool qt_mac_handleMouseEvent(NSEvent *event, QEvent::Type eventType, Qt::MouseButton button, QWidget *nativeWidget)
+{
+ // Give the Input Manager a chance to process the mouse events.
+ NSInputManager *currentIManager = [NSInputManager currentInputManager];
+ if (currentIManager && [currentIManager wantsToHandleMouseEvents]) {
+ [currentIManager handleMouseEvent:event];
+ }
+
+ // Find the widget that should receive the event, and the widget under the mouse. Those
+ // can differ if an implicit or explicit mouse grab is active:
+ QWidget *widgetUnderMouse = 0;
+ QPoint localPoint, globalPoint;
+ QWidget *widgetToGetMouse = qt_mac_getTargetForMouseEvent(event, eventType, localPoint, globalPoint, nativeWidget, &widgetUnderMouse);
+ if (!widgetToGetMouse)
+ return false;
+
+ // From here on, we let nativeWidget actually be the native widget under widgetUnderMouse. The reason
+ // for this, is that qt_mac_getTargetForMouseEvent will set cocoa's mouse event redirection aside when
+ // determining which widget is under the mouse (in other words, it will usually ignore nativeWidget).
+ // nativeWidget will be used in QApplicationPrivate::sendMouseEvent to correctly dispatch enter/leave events.
+ if (widgetUnderMouse)
+ nativeWidget = widgetUnderMouse->internalWinId() ? widgetUnderMouse : widgetUnderMouse->nativeParentWidget();
+ if (!nativeWidget)
+ return false;
+ NSView *view = qt_mac_effectiveview_for(nativeWidget);
+
+ // Handle tablet events (if any) first.
+ if (qt_mac_handleTabletEvent(view, event)) {
+ // Tablet event was handled. In Qt we aren't supposed to send the mouse event.
+ return true;
+ }
+
+ EventRef carbonEvent = static_cast<EventRef>(const_cast<void *>([event eventRef]));
+ if (qt_mac_sendMacEventToWidget(widgetToGetMouse, carbonEvent))
+ return true;
+
+ // Keep previousButton to make sure we don't send double click
+ // events when the user double clicks using two different buttons:
+ static Qt::MouseButton previousButton = Qt::NoButton;
+
+ Qt::KeyboardModifiers keyMods = qt_cocoaModifiers2QtModifiers([event modifierFlags]);
+ NSInteger clickCount = [event clickCount];
+ Qt::MouseButtons buttons = 0;
+ {
+ UInt32 mac_buttons;
+ if (GetEventParameter(carbonEvent, kEventParamMouseChord, typeUInt32, 0,
+ sizeof(mac_buttons), 0, &mac_buttons) == noErr)
+ buttons = qt_mac_get_buttons(mac_buttons);
+ }
+
+ // Send enter/leave events for the cases when QApplicationPrivate::sendMouseEvent do not:
+ qt_mac_checkEnterLeaveForNativeWidgets(widgetUnderMouse);
+
+ switch (eventType) {
+ default:
+ qWarning("not handled! %d", eventType);
+ break;
+ case QEvent::MouseMove:
+ if (button == Qt::LeftButton && qt_leftButtonIsRightButton)
+ button = Qt::RightButton;
+ break;
+ case QEvent::MouseButtonPress:
+ qt_button_down = widgetUnderMouse;
+ if (clickCount % 2 == 0 && (previousButton == Qt::NoButton || previousButton == button))
+ eventType = QEvent::MouseButtonDblClick;
+ if (button == Qt::LeftButton && (keyMods & Qt::MetaModifier)) {
+ button = Qt::RightButton;
+ qt_leftButtonIsRightButton = true;
+ }
+ break;
+ case QEvent::MouseButtonRelease:
+ if (button == Qt::LeftButton && qt_leftButtonIsRightButton) {
+ button = Qt::RightButton;
+ qt_leftButtonIsRightButton = false;
+ }
+ qt_button_down = 0;
+ break;
+ }
+
+ qt_mac_updateCursorWithWidgetUnderMouse(widgetUnderMouse);
+
+ DnDParams *dndParams = currentDnDParameters();
+ dndParams->view = view;
+ dndParams->theEvent = event;
+ dndParams->globalPoint = globalPoint;
+
+ // Send the mouse event:
+ QMouseEvent qme(eventType, localPoint, globalPoint, button, buttons, keyMods);
+ QApplicationPrivate::sendMouseEvent(
+ widgetToGetMouse, &qme, widgetUnderMouse, nativeWidget,
+ &qt_button_down, qt_last_mouse_receiver, true);
+
+ if (eventType == QEvent::MouseButtonPress && button == Qt::RightButton) {
+ QContextMenuEvent qcme(QContextMenuEvent::Mouse, localPoint, globalPoint, keyMods);
+ qt_sendSpontaneousEvent(widgetToGetMouse, &qcme);
+ }
+
+ if (eventType == QEvent::MouseButtonRelease) {
+ // A mouse button was released, which means that the implicit grab was
+ // released. We therefore need to re-check if should send (delayed) enter leave events:
+ // qt_button_down has now become NULL since the call at the top of the function. Also, since
+ // the relase might have closed a window, we dont give the nativeWidget hint
+ qt_mac_getTargetForMouseEvent(0, QEvent::None, localPoint, globalPoint, nativeWidget, &widgetUnderMouse);
+ qt_mac_checkEnterLeaveForNativeWidgets(widgetUnderMouse);
+ }
+
+ previousButton = button;
+ return true;
+}
+#endif
+
+bool qt_mac_handleTabletEvent(void * /*QCocoaView * */view, void * /*NSEvent * */tabletEvent)
+{
+#ifndef QT_MAC_USE_COCOA
+ Q_UNUSED(view);
+ Q_UNUSED(tabletEvent);
+ return false;
+#else
+ QT_MANGLE_NAMESPACE(QCocoaView) *theView = static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(view);
+ NSView *theNSView = static_cast<NSView *>(view);
+ NSEvent *theTabletEvent = static_cast<NSEvent *>(tabletEvent);
+
+ NSEventType eventType = [theTabletEvent type];
+ if (eventType != NSTabletPoint && [theTabletEvent subtype] != NSTabletPointEventSubtype)
+ return false; // Not a tablet event.
+
+ NSPoint windowPoint = [theTabletEvent locationInWindow];
+ NSPoint globalPoint = [[theTabletEvent window] convertBaseToScreen:windowPoint];
+
+ QWidget *qwidget = [theView qt_qwidget];
+ QWidget *widgetToGetMouse = qwidget;
+ QWidget *popup = qAppInstance()->activePopupWidget();
+ if (popup && popup != qwidget->window())
+ widgetToGetMouse = popup;
+
+ if (qt_mac_sendMacEventToWidget(widgetToGetMouse,
+ static_cast<EventRef>(const_cast<void *>([theTabletEvent eventRef]))))
+ return true;
+ if (widgetToGetMouse != qwidget) {
+ theNSView = qt_mac_nativeview_for(widgetToGetMouse);
+ windowPoint = [[theNSView window] convertScreenToBase:globalPoint];
+ }
+ NSPoint localPoint = [theNSView convertPoint:windowPoint fromView:nil];
+ // Tablet events do not handle WA_TransparentForMouseEvents ATM
+ // In theory, people who set the WA_TransparentForMouseEvents attribute won't handle
+ // tablet events either in which case they will fall into the mouse event case and get
+ // them passed on. This will NOT handle the raw events, but that might not be a big problem.
+
+ const QMacTabletHash *tabletHash = qt_mac_tablet_hash();
+ if (!tabletHash->contains([theTabletEvent deviceID])) {
+ qWarning("QCocoaView handleTabletEvent: This tablet device is unknown"
+ " (received no proximity event for it). Discarding event.");
+ return false;
+ }
+ const QTabletDeviceData &deviceData = tabletHash->value([theTabletEvent deviceID]);
+
+
+ QEvent::Type qType;
+ switch (eventType) {
+ case NSLeftMouseDown:
+ case NSRightMouseDown:
+ qType = QEvent::TabletPress;
+ break;
+ case NSLeftMouseUp:
+ case NSRightMouseUp:
+ qType = QEvent::TabletRelease;
+ break;
+ case NSMouseMoved:
+ case NSTabletPoint:
+ case NSLeftMouseDragged:
+ case NSRightMouseDragged:
+ default:
+ qType = QEvent::TabletMove;
+ break;
+ }
+
+ qreal pressure;
+ if (eventType != NSMouseMoved) {
+ pressure = [theTabletEvent pressure];
+ } else {
+ pressure = 0.0;
+ }
+
+ NSPoint tilt = [theTabletEvent tilt];
+ int xTilt = qRound(tilt.x * 60.0);
+ int yTilt = qRound(tilt.y * -60.0);
+ qreal tangentialPressure = 0;
+ qreal rotation = 0;
+ int z = 0;
+ if (deviceData.capabilityMask & 0x0200)
+ z = [theTabletEvent absoluteZ];
+
+ if (deviceData.capabilityMask & 0x0800)
+ tangentialPressure = [theTabletEvent tangentialPressure];
+
+ rotation = [theTabletEvent rotation];
+ QPointF hiRes = flipPoint(globalPoint);
+ QTabletEvent qtabletEvent(qType, QPoint(localPoint.x, localPoint.y),
+ hiRes.toPoint(), hiRes,
+ deviceData.tabletDeviceType, deviceData.tabletPointerType,
+ pressure, xTilt, yTilt, tangentialPressure, rotation, z,
+ qt_cocoaModifiers2QtModifiers([theTabletEvent modifierFlags]),
+ deviceData.tabletUniqueID);
+
+ qt_sendSpontaneousEvent(widgetToGetMouse, &qtabletEvent);
+ return qtabletEvent.isAccepted();
+#endif
+}
+
+void qt_mac_updateContentBorderMetricts(void * /*OSWindowRef */window, const ::HIContentBorderMetrics &metrics)
+{
+ OSWindowRef theWindow = static_cast<OSWindowRef>(window);
+#if !defined(QT_MAC_USE_COCOA)
+# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
+ ::HIWindowSetContentBorderThickness(theWindow, &metrics);
+ }
+# else
+ Q_UNUSED(window);
+ Q_UNUSED(metrics);
+# endif
+#else
+ if ([theWindow styleMask] & NSTexturedBackgroundWindowMask)
+ [theWindow setContentBorderThickness:metrics.top forEdge:NSMaxYEdge];
+ [theWindow setContentBorderThickness:metrics.bottom forEdge:NSMinYEdge];
+#endif
+}
+
+#if QT_MAC_USE_COCOA
+void qt_mac_replaceDrawRect(void * /*OSWindowRef */window, QWidgetPrivate *widget)
+{
+ QMacCocoaAutoReleasePool pool;
+ OSWindowRef theWindow = static_cast<OSWindowRef>(window);
+ if(!theWindow)
+ return;
+ id theClass = [[[theWindow contentView] superview] class];
+ // What we do here is basically to add a new selector to NSThemeFrame called
+ // "drawRectOriginal:" which will contain the original implementation of
+ // "drawRect:". After that we get the new implementation from QCocoaWindow
+ // and exchange them. The new implementation is called drawRectSpecial.
+ // We cannot just add the method because it might have been added before and since
+ // we cannot remove a method once it has been added we need to ask QCocoaWindow if
+ // we did the swap or not.
+ if(!widget->drawRectOriginalAdded) {
+ Method m2 = class_getInstanceMethod(theClass, @selector(drawRect:));
+ if(!m2) {
+ // This case is pretty extreme, no drawRect means no drawing!
+ return;
+ }
+ class_addMethod(theClass, @selector(drawRectOriginal:), method_getImplementation(m2), method_getTypeEncoding(m2));
+ widget->drawRectOriginalAdded = true;
+ }
+ if(widget->originalDrawMethod) {
+ Method m0 = class_getInstanceMethod([theWindow class], @selector(drawRectSpecial:));
+ if(!m0) {
+ // Ok, this means the methods were never swapped. Just ignore
+ return;
+ }
+ Method m1 = class_getInstanceMethod(theClass, @selector(drawRect:));
+ if(!m1) {
+ // Ok, this means the methods were never swapped. Just ignore
+ return;
+ }
+ // We have the original method here. Proceed and swap the methods.
+ method_exchangeImplementations(m1, m0);
+ widget->originalDrawMethod = false;
+ [theWindow display];
+ }
+}
+
+void qt_mac_replaceDrawRectOriginal(void * /*OSWindowRef */window, QWidgetPrivate *widget)
+{
+ QMacCocoaAutoReleasePool pool;
+ OSWindowRef theWindow = static_cast<OSWindowRef>(window);
+ id theClass = [[[theWindow contentView] superview] class];
+ // Now we need to revert the methods to their original state.
+ // We cannot remove the method, so we just keep track of it in QCocoaWindow.
+ Method m0 = class_getInstanceMethod([theWindow class], @selector(drawRectSpecial:));
+ if(!m0) {
+ // Ok, this means the methods were never swapped. Just ignore
+ return;
+ }
+ Method m1 = class_getInstanceMethod(theClass, @selector(drawRect:));
+ if(!m1) {
+ // Ok, this means the methods were never swapped. Just ignore
+ return;
+ }
+ method_exchangeImplementations(m1, m0);
+ widget->originalDrawMethod = true;
+ [theWindow display];
+}
+#endif // QT_MAC_USE_COCOA
+
+#if QT_MAC_USE_COCOA
+void qt_mac_showBaseLineSeparator(void * /*OSWindowRef */window, bool show)
+{
+ if(!window)
+ return;
+ QMacCocoaAutoReleasePool pool;
+ OSWindowRef theWindow = static_cast<OSWindowRef>(window);
+ NSToolbar *macToolbar = [theWindow toolbar];
+ [macToolbar setShowsBaselineSeparator:show];
+}
+#endif // QT_MAC_USE_COCOA
+
+QStringList qt_mac_NSArrayToQStringList(void *nsarray)
+{
+ QStringList result;
+ NSArray *array = static_cast<NSArray *>(nsarray);
+ for (NSUInteger i=0; i<[array count]; ++i)
+ result << qt_mac_NSStringToQString([array objectAtIndex:i]);
+ return result;
+}
+
+void *qt_mac_QStringListToNSMutableArrayVoid(const QStringList &list)
+{
+ NSMutableArray *result = [NSMutableArray arrayWithCapacity:list.size()];
+ for (int i=0; i<list.size(); ++i){
+ [result addObject:reinterpret_cast<const NSString *>(QCFString::toCFStringRef(list[i]))];
+ }
+ return result;
+}
+
+#if QT_MAC_USE_COCOA
+void qt_syncCocoaTitleBarButtons(OSWindowRef window, QWidget *widgetForWindow)
+{
+ if (!widgetForWindow)
+ return;
+
+ Qt::WindowFlags flags = widgetForWindow->windowFlags();
+ bool customize = flags & Qt::CustomizeWindowHint;
+
+ NSButton *btn = [window standardWindowButton:NSWindowZoomButton];
+ // BOOL is not an int, so the bitwise AND doesn't work.
+ bool go = uint(customize && !(flags & Qt::WindowMaximizeButtonHint)) == 0;
+ [btn setEnabled:go];
+
+ btn = [window standardWindowButton:NSWindowMiniaturizeButton];
+ go = uint(customize && !(flags & Qt::WindowMinimizeButtonHint)) == 0;
+ [btn setEnabled:go];
+
+ btn = [window standardWindowButton:NSWindowCloseButton];
+ go = uint(customize && !(flags & Qt::WindowSystemMenuHint
+ || flags & Qt::WindowCloseButtonHint)) == 0;
+ [btn setEnabled:go];
+
+ [window setShowsToolbarButton:uint(flags & Qt::MacWindowToolBarButtonHint) != 0];
+}
+#endif // QT_MAC_USE_COCOA
+
+// Carbon: Make sure you call QDEndContext on the context when done with it.
+CGContextRef qt_mac_graphicsContextFor(QWidget *widget)
+{
+ if (!widget)
+ return 0;
+
+#ifndef QT_MAC_USE_COCOA
+ CGContextRef context;
+ CGrafPtr port = GetWindowPort(qt_mac_window_for(widget));
+ QDBeginCGContext(port, &context);
+#else
+ CGContextRef context = (CGContextRef)[[NSGraphicsContext graphicsContextWithWindow:qt_mac_window_for(widget)] graphicsPort];
+#endif
+ return context;
+}
+
+void qt_mac_dispatchPendingUpdateRequests(QWidget *widget)
+{
+ if (!widget)
+ return;
+#ifndef QT_MAC_USE_COCOA
+ HIViewRender(qt_mac_nativeview_for(widget));
+#else
+ [qt_mac_nativeview_for(widget) displayIfNeeded];
+#endif
+}
+
+CGFloat qt_mac_get_scalefactor()
+{
+#ifndef QT_MAC_USE_COCOA
+ return HIGetScaleFactor();
+#else
+ return [[NSScreen mainScreen] userSpaceScaleFactor];
+#endif
+}
+
+QString qt_mac_get_pasteboardString(OSPasteboardRef paste)
+{
+ QMacCocoaAutoReleasePool pool;
+ NSPasteboard *pb = nil;
+ CFStringRef pbname;
+ if (PasteboardCopyName(paste, &pbname) == noErr) {
+ pb = [NSPasteboard pasteboardWithName:const_cast<NSString *>(reinterpret_cast<const NSString *>(pbname))];
+ CFRelease(pbname);
+ } else {
+ pb = [NSPasteboard generalPasteboard];
+ }
+ if (pb) {
+ NSString *text = [pb stringForType:NSStringPboardType];
+ if (text)
+ return qt_mac_NSStringToQString(text);
+ }
+ return QString();
+}
+
+QPixmap qt_mac_convert_iconref(const IconRef icon, int width, int height)
+{
+ QPixmap ret(width, height);
+ ret.fill(QColor(0, 0, 0, 0));
+
+ CGRect rect = CGRectMake(0, 0, width, height);
+
+ CGContextRef ctx = qt_mac_cg_context(&ret);
+ CGAffineTransform old_xform = CGContextGetCTM(ctx);
+ CGContextConcatCTM(ctx, CGAffineTransformInvert(old_xform));
+ CGContextConcatCTM(ctx, CGAffineTransformIdentity);
+
+ ::RGBColor b;
+ b.blue = b.green = b.red = 255*255;
+ PlotIconRefInContext(ctx, &rect, kAlignNone, kTransformNone, &b, kPlotIconRefNormalFlags, icon);
+ CGContextRelease(ctx);
+ return ret;
+}
+
+void qt_mac_constructQIconFromIconRef(const IconRef icon, const IconRef overlayIcon, QIcon *retIcon, QStyle::StandardPixmap standardIcon)
+{
+ int size = 16;
+ while (size <= 128) {
+
+ const QString cacheKey = QLatin1String("qt_mac_constructQIconFromIconRef") + QString::number(standardIcon) + QString::number(size);
+ QPixmap mainIcon;
+ if (standardIcon >= QStyle::SP_CustomBase) {
+ mainIcon = qt_mac_convert_iconref(icon, size, size);
+ } else if (QPixmapCache::find(cacheKey, mainIcon) == false) {
+ mainIcon = qt_mac_convert_iconref(icon, size, size);
+ QPixmapCache::insert(cacheKey, mainIcon);
+ }
+
+ if (overlayIcon) {
+ int littleSize = size / 2;
+ QPixmap overlayPix = qt_mac_convert_iconref(overlayIcon, littleSize, littleSize);
+ QPainter painter(&mainIcon);
+ painter.drawPixmap(size - littleSize, size - littleSize, overlayPix);
+ }
+
+ retIcon->addPixmap(mainIcon);
+ size += size; // 16 -> 32 -> 64 -> 128
+ }
+}
+
+void qt_mac_post_retranslateAppMenu()
+{
+#ifdef QT_MAC_USE_COCOA
+ QMacCocoaAutoReleasePool pool;
+ qt_cocoaPostMessage([NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)], @selector(qtTranslateApplicationMenu));
+#endif
+}
+
+QWidgetPrivate *QMacScrollOptimization::_target = 0;
+bool QMacScrollOptimization::_inWheelEvent = false;
+int QMacScrollOptimization::_dx = 0;
+int QMacScrollOptimization::_dy = 0;
+QRect QMacScrollOptimization::_scrollRect = QRect(0, 0, -1, -1);
+
+#ifdef QT_MAC_USE_COCOA
+// This method implements the magic for the drawRectSpecial method.
+// We draw a line at the upper edge of the content view in order to
+// override the title baseline.
+void macDrawRectOnTop(void * /*OSWindowRef */window)
+{
+ OSWindowRef theWindow = static_cast<OSWindowRef>(window);
+ NSView *contentView = [theWindow contentView];
+ if(!contentView)
+ return;
+ // Get coordinates of the content view
+ NSRect contentRect = [contentView frame];
+ // Draw a line on top of the already drawn line.
+ // We need to check if we are active or not to use the proper color.
+ if([theWindow isKeyWindow] || [theWindow isMainWindow]) {
+ [[NSColor colorWithCalibratedRed:1.0 green:1.0 blue:1.0 alpha:1.0] set];
+ } else {
+ [[NSColor colorWithCalibratedRed:1.0 green:1.0 blue:1.0 alpha:1.0] set];
+ }
+ NSPoint origin = NSMakePoint(0, contentRect.size.height);
+ NSPoint end = NSMakePoint(contentRect.size.width, contentRect.size.height);
+ [NSBezierPath strokeLineFromPoint:origin toPoint:end];
+}
+
+// This method will (or at least should) get called only once.
+// Its mission is to find out if we are active or not. If we are active
+// we assume that we were launched via finder, otherwise we assume
+// we were called from the command line. The distinction is important,
+// since in the first case we don't need to trigger a paintEvent, while
+// in the second case we do.
+void macSyncDrawingOnFirstInvocation(void * /*OSWindowRef */window)
+{
+ OSWindowRef theWindow = static_cast<OSWindowRef>(window);
+ NSApplication *application = [NSApplication sharedApplication];
+ NSToolbar *toolbar = [theWindow toolbar];
+ if([application isActive]) {
+ // Launched from finder
+ [toolbar setShowsBaselineSeparator:NO];
+ } else {
+ // Launched from commandline
+ [toolbar setVisible:false];
+ [toolbar setShowsBaselineSeparator:NO];
+ [toolbar setVisible:true];
+ [theWindow display];
+ }
+}
+
+void qt_cocoaStackChildWindowOnTopOfOtherChildren(QWidget *childWidget)
+{
+ if (!childWidget)
+ return;
+
+ QWidget *parent = childWidget->parentWidget();
+ if (childWidget->isWindow() && parent) {
+ if ([[qt_mac_window_for(parent) childWindows] containsObject:qt_mac_window_for(childWidget)]) {
+ QWidgetPrivate *d = qt_widget_private(childWidget);
+ d->setSubWindowStacking(false);
+ d->setSubWindowStacking(true);
+ }
+ }
+}
+
+void qt_mac_display(QWidget *widget)
+{
+ NSView *theNSView = qt_mac_nativeview_for(widget);
+ [theNSView display];
+}
+
+void qt_mac_setNeedsDisplay(QWidget *widget)
+{
+ NSView *theNSView = qt_mac_nativeview_for(widget);
+ [theNSView setNeedsDisplay:YES];
+}
+
+void qt_mac_setNeedsDisplayInRect(QWidget *widget, QRegion region)
+{
+ NSView *theNSView = qt_mac_nativeview_for(widget);
+ if (region.isEmpty()) {
+ [theNSView setNeedsDisplay:YES];
+ return;
+ }
+
+ QVector<QRect> rects = region.rects();
+ for (int i = 0; i < rects.count(); ++i) {
+ const QRect &rect = rects.at(i);
+ NSRect nsrect = NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height());
+ [theNSView setNeedsDisplayInRect:nsrect];
+ }
+
+}
+
+#endif // QT_MAC_USE_COCOA
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/mac/qt_cocoa_helpers_mac_p.h b/src/widgets/platforms/mac/qt_cocoa_helpers_mac_p.h
new file mode 100644
index 0000000000..b46946c121
--- /dev/null
+++ b/src/widgets/platforms/mac/qt_cocoa_helpers_mac_p.h
@@ -0,0 +1,310 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** Copyright (c) 2007-2008, Apple, Inc.
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**
+** * Redistributions of source code must retain the above copyright notice,
+** this list of conditions and the following disclaimer.
+**
+** * Redistributions in binary form must reproduce the above copyright notice,
+** this list of conditions and the following disclaimer in the documentation
+** and/or other materials provided with the distribution.
+**
+** * Neither the name of Apple, Inc. nor the names of its contributors
+** may be used to endorse or promote products derived from this software
+** without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**
+****************************************************************************/
+
+#ifndef QT_COCOA_HELPERS_MAC_P_H
+#define QT_COCOA_HELPERS_MAC_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp, qcolor_x11.cpp, qfiledialog.cpp
+// and many other. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qt_mac_p.h>
+
+#include <qapplication.h>
+#include <qdesktopwidget.h>
+#include <qwidget.h>
+#include <qevent.h>
+#include <qhash.h>
+#include <qlabel.h>
+#include <qpointer.h>
+#include <qstyle.h>
+#include <qstyleoption.h>
+#include <qstylepainter.h>
+#include <qtimer.h>
+#include <qtooltip.h>
+#include <private/qeffects_p.h>
+#include <private/qwidget_p.h>
+#include <qtextdocument.h>
+#include <qdebug.h>
+#include <qpoint.h>
+#include "private/qt_mac_p.h"
+
+struct HIContentBorderMetrics;
+
+#ifdef __OBJC__
+ // If the source file including this file also includes e.g. Cocoa/Cocoa.h, typedef-ing NSPoint will
+ // fail since NSPoint will already be a type. So we try to detect this. If the build fails, ensure
+ // that the inclusion of cocoa headers happends before the inclusion of this file.
+ #include <Foundation/NSGeometry.h>
+#else
+ #ifdef Q_WS_MAC32
+ typedef struct _NSPoint NSPoint; // Just redefine here so I don't have to pull in all of Cocoa.
+ #else
+ typedef struct CGPoint NSPoint;
+ #endif
+#endif
+
+QT_BEGIN_NAMESPACE
+
+Qt::MouseButtons qt_mac_get_buttons(int buttons);
+Qt::MouseButton qt_mac_get_button(EventMouseButton button);
+void macWindowFade(void * /*OSWindowRef*/ window, float durationSeconds = 0.15);
+bool macWindowIsTextured(void * /*OSWindowRef*/ window);
+void macWindowToolbarShow(const QWidget *widget, bool show );
+void macWindowToolbarSet( void * /*OSWindowRef*/ window, void* toolbarRef );
+bool macWindowToolbarIsVisible( void * /*OSWindowRef*/ window );
+void macWindowSetHasShadow( void * /*OSWindowRef*/ window, bool hasShadow );
+void macWindowFlush(void * /*OSWindowRef*/ window);
+void macSendToolbarChangeEvent(QWidget *widget);
+void qt_mac_updateContentBorderMetricts(void * /*OSWindowRef */window, const ::HIContentBorderMetrics &metrics);
+void qt_mac_replaceDrawRect(void * /*OSWindowRef */window, QWidgetPrivate *widget);
+void qt_mac_replaceDrawRectOriginal(void * /*OSWindowRef */window, QWidgetPrivate *widget);
+void qt_mac_showBaseLineSeparator(void * /*OSWindowRef */window, bool show);
+void * /*NSImage */qt_mac_create_nsimage(const QPixmap &pm);
+void qt_mac_update_mouseTracking(QWidget *widget);
+OSStatus qt_mac_drawCGImage(CGContextRef cg, const CGRect *inbounds, CGImageRef);
+bool qt_mac_checkForNativeSizeGrip(const QWidget *widget);
+void qt_dispatchTabletProximityEvent(void * /*NSEvent * */ tabletEvent);
+bool qt_dispatchKeyEventWithCocoa(void * /*NSEvent * */ keyEvent, QWidget *widgetToGetEvent);
+// These methods exists only for supporting unified mode.
+void macDrawRectOnTop(void * /*OSWindowRef */ window);
+void macSyncDrawingOnFirstInvocation(void * /*OSWindowRef */window);
+void qt_cocoaStackChildWindowOnTopOfOtherChildren(QWidget *widget);
+bool qt_dispatchKeyEvent(void * /*NSEvent * */ keyEvent, QWidget *widgetToGetEvent);
+void qt_dispatchModifiersChanged(void * /*NSEvent * */flagsChangedEvent, QWidget *widgetToGetEvent);
+bool qt_mac_handleTabletEvent(void * /*QCocoaView * */view, void * /*NSEvent * */event);
+inline QApplication *qAppInstance() { return static_cast<QApplication *>(QCoreApplication::instance()); }
+struct ::TabletProximityRec;
+void qt_dispatchTabletProximityEvent(const ::TabletProximityRec &proxRec);
+Qt::KeyboardModifiers qt_cocoaModifiers2QtModifiers(ulong modifierFlags);
+Qt::KeyboardModifiers qt_cocoaDragOperation2QtModifiers(uint dragOperations);
+QPixmap qt_mac_convert_iconref(const IconRef icon, int width, int height);
+void qt_mac_constructQIconFromIconRef(const IconRef icon, const IconRef overlayIcon, QIcon *retIcon,
+ QStyle::StandardPixmap standardIcon = QStyle::SP_CustomBase);
+
+#ifdef __OBJC__
+struct DnDParams
+{
+ NSView *view;
+ NSEvent *theEvent;
+ QPoint globalPoint;
+ NSDragOperation performedAction;
+};
+
+DnDParams *macCurrentDnDParameters();
+NSDragOperation qt_mac_mapDropAction(Qt::DropAction action);
+NSDragOperation qt_mac_mapDropActions(Qt::DropActions actions);
+Qt::DropAction qt_mac_mapNSDragOperation(NSDragOperation nsActions);
+Qt::DropActions qt_mac_mapNSDragOperations(NSDragOperation nsActions);
+
+QWidget *qt_mac_getTargetForKeyEvent(QWidget *widgetThatReceivedEvent);
+QWidget *qt_mac_getTargetForMouseEvent(NSEvent *event, QEvent::Type eventType,
+ QPoint &returnLocalPoint, QPoint &returnGlobalPoint, QWidget *nativeWidget, QWidget **returnWidgetUnderMouse);
+bool qt_mac_handleMouseEvent(NSEvent *event, QEvent::Type eventType, Qt::MouseButton button, QWidget *nativeWidget);
+void qt_mac_handleNonClientAreaMouseEvent(NSWindow *window, NSEvent *event);
+#endif
+
+inline int flipYCoordinate(int y)
+{
+ return QApplication::desktop()->screenGeometry(0).height() - y;
+}
+
+inline qreal flipYCoordinate(qreal y)
+{
+ return QApplication::desktop()->screenGeometry(0).height() - y;
+}
+
+QPointF flipPoint(const NSPoint &p);
+NSPoint flipPoint(const QPoint &p);
+NSPoint flipPoint(const QPointF &p);
+
+QStringList qt_mac_NSArrayToQStringList(void *nsarray);
+void *qt_mac_QStringListToNSMutableArrayVoid(const QStringList &list);
+
+void qt_syncCocoaTitleBarButtons(OSWindowRef window, QWidget *widgetForWindow);
+
+CGFloat qt_mac_get_scalefactor();
+QString qt_mac_get_pasteboardString(OSPasteboardRef paste);
+
+#ifdef __OBJC__
+inline NSMutableArray *qt_mac_QStringListToNSMutableArray(const QStringList &qstrlist)
+{ return reinterpret_cast<NSMutableArray *>(qt_mac_QStringListToNSMutableArrayVoid(qstrlist)); }
+
+inline QString qt_mac_NSStringToQString(const NSString *nsstr)
+{ return QCFString::toQString(reinterpret_cast<const CFStringRef>(nsstr)); }
+
+inline NSString *qt_mac_QStringToNSString(const QString &qstr)
+{ return [reinterpret_cast<const NSString *>(QCFString::toCFStringRef(qstr)) autorelease]; }
+
+#endif
+
+class QMacScrollOptimization {
+ // This class is made to optimize for the case when the user
+ // scrolls both horizontally and vertically at the same
+ // time. This will result in two QWheelEvents (one for each
+ // direction), which will typically result in two calls to
+ // QWidget::_scroll_sys. Rather than copying pixels twize on
+ // screen because of this, we add this helper class to try to
+ // get away with only one blit.
+ static QWidgetPrivate *_target;
+ static bool _inWheelEvent;
+ static int _dx;
+ static int _dy;
+ static QRect _scrollRect;
+
+public:
+ static void initDelayedScroll()
+ {
+ _inWheelEvent = true;
+ }
+
+ static bool delayScroll(QWidgetPrivate *target, int dx, int dy, const QRect &scrollRect)
+ {
+ if (!_inWheelEvent)
+ return false;
+ if (_target && _target != target)
+ return false;
+ if (_scrollRect.width() != -1 && _scrollRect != scrollRect)
+ return false;
+
+ _target = target;
+ _dx += dx;
+ _dy += dy;
+ _scrollRect = scrollRect;
+ return true;
+ }
+
+ static void performDelayedScroll()
+ {
+ if (!_inWheelEvent)
+ return;
+ _inWheelEvent = false;
+ if (!_target)
+ return;
+
+ _target->scroll_sys(_dx, _dy, _scrollRect);
+
+ _target = 0;
+ _dx = 0;
+ _dy = 0;
+ _scrollRect = QRect(0, 0, -1, -1);
+ }
+};
+
+void qt_mac_post_retranslateAppMenu();
+
+void qt_mac_display(QWidget *widget);
+void qt_mac_setNeedsDisplay(QWidget *widget);
+void qt_mac_setNeedsDisplayInRect(QWidget *widget, QRegion region);
+
+
+// Utility functions to ease the use of Core Graphics contexts.
+
+inline void qt_mac_retain_graphics_context(CGContextRef context)
+{
+ CGContextRetain(context);
+ CGContextSaveGState(context);
+}
+
+inline void qt_mac_release_graphics_context(CGContextRef context)
+{
+ CGContextRestoreGState(context);
+ CGContextRelease(context);
+}
+
+inline void qt_mac_draw_image(CGContextRef context, CGContextRef imageContext, CGRect area, CGRect drawingArea)
+{
+ CGImageRef image = CGBitmapContextCreateImage(imageContext);
+ CGImageRef subImage = CGImageCreateWithImageInRect(image, area);
+
+ CGContextTranslateCTM (context, 0, drawingArea.origin.y + CGRectGetMaxY(drawingArea));
+ CGContextScaleCTM(context, 1, -1);
+ CGContextDrawImage(context, drawingArea, subImage);
+
+ CGImageRelease(subImage);
+ CGImageRelease(image);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_COCOA_HELPERS_MAC_P_H
diff --git a/src/widgets/platforms/mac/qt_mac.cpp b/src/widgets/platforms/mac/qt_mac.cpp
new file mode 100644
index 0000000000..339bc82ff5
--- /dev/null
+++ b/src/widgets/platforms/mac/qt_mac.cpp
@@ -0,0 +1,174 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qt_mac_p.h>
+#include <private/qpixmap_mac_p.h>
+#include <private/qnativeimage_p.h>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+#ifdef QT_MAC_USE_COCOA
+static CTFontRef CopyCTThemeFont(ThemeFontID themeID)
+{
+ CTFontUIFontType ctID = HIThemeGetUIFontType(themeID);
+ return CTFontCreateUIFontForLanguage(ctID, 0, 0);
+}
+#endif
+
+QFont qfontForThemeFont(ThemeFontID themeID)
+{
+#ifndef QT_MAC_USE_COCOA
+ static const ScriptCode Script = smRoman;
+ Str255 f_name;
+ SInt16 f_size;
+ Style f_style;
+ GetThemeFont(themeID, Script, f_name, &f_size, &f_style);
+ extern QString qt_mac_from_pascal_string(const Str255); //qglobal.cpp
+ return QFont(qt_mac_from_pascal_string(f_name), f_size,
+ (f_style & ::bold) ? QFont::Bold : QFont::Normal,
+ (bool)(f_style & ::italic));
+#else
+ QCFType<CTFontRef> ctfont = CopyCTThemeFont(themeID);
+ QString familyName = QCFString(CTFontCopyFamilyName(ctfont));
+ QCFType<CFDictionaryRef> dict = CTFontCopyTraits(ctfont);
+ CFNumberRef num = static_cast<CFNumberRef>(CFDictionaryGetValue(dict, kCTFontWeightTrait));
+ float fW;
+ CFNumberGetValue(num, kCFNumberFloat32Type, &fW);
+ QFont::Weight wght = fW > 0. ? QFont::Bold : QFont::Normal;
+ num = static_cast<CFNumberRef>(CFDictionaryGetValue(dict, kCTFontSlantTrait));
+ CFNumberGetValue(num, kCFNumberFloatType, &fW);
+ bool italic = (fW != 0.0);
+ return QFont(familyName, CTFontGetSize(ctfont), wght, italic);
+#endif
+}
+
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+static QColor qcolorFromCGColor(CGColorRef cgcolor)
+{
+ QColor pc;
+ CGColorSpaceModel model = CGColorSpaceGetModel(CGColorGetColorSpace(cgcolor));
+ const CGFloat *components = CGColorGetComponents(cgcolor);
+ if (model == kCGColorSpaceModelRGB) {
+ pc.setRgbF(components[0], components[1], components[2], components[3]);
+ } else if (model == kCGColorSpaceModelCMYK) {
+ pc.setCmykF(components[0], components[1], components[2], components[3]);
+ } else if (model == kCGColorSpaceModelMonochrome) {
+ pc.setRgbF(components[0], components[0], components[0], components[1]);
+ } else {
+ // Colorspace we can't deal with.
+ qWarning("Qt: qcolorFromCGColor: cannot convert from colorspace model: %d", model);
+ Q_ASSERT(false);
+ }
+ return pc;
+}
+
+static inline QColor leopardBrush(ThemeBrush brush)
+{
+ QCFType<CGColorRef> cgClr = 0;
+ HIThemeBrushCreateCGColor(brush, &cgClr);
+ return qcolorFromCGColor(cgClr);
+}
+#endif
+
+QColor qcolorForTheme(ThemeBrush brush)
+{
+#ifndef QT_MAC_USE_COCOA
+# if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
+ return leopardBrush(brush);
+ } else
+# endif
+ {
+ RGBColor rgbcolor;
+ GetThemeBrushAsColor(brush, 32, true, &rgbcolor);
+ return QColor(rgbcolor.red / 256, rgbcolor.green / 256, rgbcolor.blue / 256);
+ }
+#else
+ return leopardBrush(brush);
+#endif
+}
+
+QColor qcolorForThemeTextColor(ThemeTextColor themeColor)
+{
+#ifdef Q_OS_MAC32
+ RGBColor c;
+ GetThemeTextColor(themeColor, 32, true, &c);
+ QColor color = QColor(c.red / 256, c.green / 256, c.blue / 256);
+ return color;
+#else
+ // There is no equivalent to GetThemeTextColor in 64-bit and it was rather bad that
+ // I didn't file a request to implement this for Snow Leopard. So, in the meantime
+ // I've encoded the values from the GetThemeTextColor. This is not exactly ideal
+ // as if someone really wants to mess with themeing, these colors will be wrong.
+ // It also means that we need to make sure the values for differences between
+ // OS releases (and it will be likely that we are a step behind.)
+ switch (themeColor) {
+ case kThemeTextColorAlertActive:
+ case kThemeTextColorTabFrontActive:
+ case kThemeTextColorBevelButtonActive:
+ case kThemeTextColorListView:
+ case kThemeTextColorPlacardActive:
+ case kThemeTextColorPopupButtonActive:
+ case kThemeTextColorPopupLabelActive:
+ case kThemeTextColorPushButtonActive:
+ return Qt::black;
+ case kThemeTextColorAlertInactive:
+ case kThemeTextColorDialogInactive:
+ case kThemeTextColorPlacardInactive:
+ return QColor(69, 69, 69, 255);
+ case kThemeTextColorPopupButtonInactive:
+ case kThemeTextColorPopupLabelInactive:
+ case kThemeTextColorPushButtonInactive:
+ case kThemeTextColorTabFrontInactive:
+ case kThemeTextColorBevelButtonInactive:
+ return QColor(127, 127, 127, 255);
+ default: {
+ QNativeImage nativeImage(16,16, QNativeImage::systemFormat());
+ CGRect cgrect = CGRectMake(0, 0, 16, 16);
+ HIThemeSetTextFill(themeColor, 0, nativeImage.cg, kHIThemeOrientationNormal);
+ CGContextFillRect(nativeImage.cg, cgrect);
+ QColor color = nativeImage.image.pixel(0,0);
+ return QColor(nativeImage.image.pixel(0 , 0));
+ }
+ }
+#endif
+}
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/mac/qt_mac_p.h b/src/widgets/platforms/mac/qt_mac_p.h
new file mode 100644
index 0000000000..80328c3497
--- /dev/null
+++ b/src/widgets/platforms/mac/qt_mac_p.h
@@ -0,0 +1,286 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT_MAC_P_H
+#define QT_MAC_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qmacdefines_mac.h"
+
+#ifdef __OBJC__
+#include <Cocoa/Cocoa.h>
+#ifdef QT_MAC_USE_COCOA
+#include <objc/runtime.h>
+#endif // QT_MAC_USE_COCOA
+#endif
+
+#include <CoreServices/CoreServices.h>
+
+#include "QtCore/qglobal.h"
+#include "QtCore/qvariant.h"
+#include "QtCore/qmimedata.h"
+#include "QtCore/qpointer.h"
+#include "private/qcore_mac_p.h"
+
+
+#include "QtGui/qpainter.h"
+
+#include <Carbon/Carbon.h>
+
+QT_BEGIN_NAMESPACE
+class QWidget;
+class QDragMoveEvent;
+
+/* Event masks */
+// internal Qt types
+
+ // Event class for our own Carbon events.
+#if defined(QT_NAMESPACE) && defined(QT_NAMESPACE_MAC_CRC)
+// Take the CRC we generated at configure time. This *may* result in a
+// collision with another value If that is the case, please change the value
+// here to something other than 'Cute'.
+const UInt32 kEventClassQt = QT_NAMESPACE_MAC_CRC;
+#else
+const UInt32 kEventClassQt = 'Cute';
+#endif
+
+enum {
+ //AE types
+ typeAEClipboardChanged = 1,
+ //types
+ typeQWidget = 1, /* QWidget * */
+ //params
+ kEventParamQWidget = 'qwid', /* typeQWidget */
+ //events
+ kEventQtRequestContext = 13,
+ kEventQtRequestMenubarUpdate = 14,
+ kEventQtRequestShowSheet = 17,
+ kEventQtRequestActivate = 18,
+ kEventQtRequestWindowChange = 20
+};
+
+// Simple class to manage short-lived regions
+class QMacSmartQuickDrawRegion
+{
+ RgnHandle qdRgn;
+ Q_DISABLE_COPY(QMacSmartQuickDrawRegion)
+public:
+ explicit QMacSmartQuickDrawRegion(RgnHandle rgn) : qdRgn(rgn) {}
+ ~QMacSmartQuickDrawRegion() {
+ extern void qt_mac_dispose_rgn(RgnHandle); // qregion_mac.cpp
+ qt_mac_dispose_rgn(qdRgn);
+ }
+ operator RgnHandle() {
+ return qdRgn;
+ }
+};
+
+// Class for chaining to gether a bunch of fades. It pretty much is only used for qmenu fading.
+class QMacWindowFader
+{
+ QWidgetList m_windowsToFade;
+ float m_duration;
+ Q_DISABLE_COPY(QMacWindowFader)
+public:
+ QMacWindowFader(); // PLEASE DON'T CALL THIS.
+ static QMacWindowFader *currentFader();
+ void registerWindowToFade(QWidget *window);
+ void setFadeDuration(float durationInSecs) { m_duration = durationInSecs; }
+ float fadeDuration() const { return m_duration; }
+ void performFade();
+};
+
+class Q_WIDGETS_EXPORT QMacCocoaAutoReleasePool
+{
+private:
+ void *pool;
+public:
+ QMacCocoaAutoReleasePool();
+ ~QMacCocoaAutoReleasePool();
+
+ inline void *handle() const { return pool; }
+};
+
+QString qt_mac_removeMnemonics(const QString &original); //implemented in qmacstyle_mac.cpp
+
+class Q_WIDGETS_EXPORT QMacWindowChangeEvent
+{
+private:
+ static QList<QMacWindowChangeEvent*> *change_events;
+public:
+ QMacWindowChangeEvent() {
+ }
+ virtual ~QMacWindowChangeEvent() {
+ }
+ static inline void exec(bool ) {
+ }
+protected:
+ virtual void windowChanged() = 0;
+ virtual void flushWindowChanged() = 0;
+};
+
+class QMacCGContext
+{
+ CGContextRef context;
+public:
+ QMacCGContext(QPainter *p); //qpaintengine_mac.cpp
+ inline QMacCGContext() { context = 0; }
+ inline QMacCGContext(const QPaintDevice *pdev) {
+ extern CGContextRef qt_mac_cg_context(const QPaintDevice *);
+ context = qt_mac_cg_context(pdev);
+ }
+ inline QMacCGContext(CGContextRef cg, bool takeOwnership=false) {
+ context = cg;
+ if(!takeOwnership)
+ CGContextRetain(context);
+ }
+ inline QMacCGContext(const QMacCGContext &copy) : context(0) { *this = copy; }
+ inline ~QMacCGContext() {
+ if(context)
+ CGContextRelease(context);
+ }
+ inline bool isNull() const { return context; }
+ inline operator CGContextRef() { return context; }
+ inline QMacCGContext &operator=(const QMacCGContext &copy) {
+ if(context)
+ CGContextRelease(context);
+ context = copy.context;
+ CGContextRetain(context);
+ return *this;
+ }
+ inline QMacCGContext &operator=(CGContextRef cg) {
+ if(context)
+ CGContextRelease(context);
+ context = cg;
+ CGContextRetain(context); //we do not take ownership
+ return *this;
+ }
+};
+
+class QMacPasteboardMime;
+class QMimeData;
+
+class QMacPasteboard
+{
+ struct Promise {
+ Promise() : itemId(0), convertor(0) { }
+ Promise(int itemId, QMacPasteboardMime *c, QString m, QVariant d, int o=0) : itemId(itemId), offset(o), convertor(c), mime(m), data(d) { }
+ int itemId, offset;
+ QMacPasteboardMime *convertor;
+ QString mime;
+ QVariant data;
+ };
+ QList<Promise> promises;
+
+ OSPasteboardRef paste;
+ uchar mime_type;
+ mutable QPointer<QMimeData> mime;
+ mutable bool mac_mime_source;
+ static OSStatus promiseKeeper(OSPasteboardRef, PasteboardItemID, CFStringRef, void *);
+ void clear_helper();
+public:
+ QMacPasteboard(OSPasteboardRef p, uchar mime_type=0);
+ QMacPasteboard(uchar mime_type);
+ QMacPasteboard(CFStringRef name=0, uchar mime_type=0);
+ ~QMacPasteboard();
+
+ bool hasFlavor(QString flavor) const;
+ bool hasOSType(int c_flavor) const;
+
+ OSPasteboardRef pasteBoard() const;
+ QMimeData *mimeData() const;
+ void setMimeData(QMimeData *mime);
+
+ QStringList formats() const;
+ bool hasFormat(const QString &format) const;
+ QVariant retrieveData(const QString &format, QVariant::Type) const;
+
+ void clear();
+ bool sync() const;
+};
+
+extern QPaintDevice *qt_mac_safe_pdev; //qapplication_mac.cpp
+
+extern OSWindowRef qt_mac_window_for(const QWidget*); //qwidget_mac.mm
+extern OSViewRef qt_mac_nativeview_for(const QWidget *); //qwidget_mac.mm
+extern QPoint qt_mac_nativeMapFromParent(const QWidget *child, const QPoint &pt); //qwidget_mac.mm
+
+#ifdef check
+# undef check
+#endif
+
+QFont qfontForThemeFont(ThemeFontID themeID);
+
+QColor qcolorForTheme(ThemeBrush brush);
+
+QColor qcolorForThemeTextColor(ThemeTextColor themeColor);
+
+struct QMacDndAnswerRecord {
+ QRect rect;
+ Qt::KeyboardModifiers modifiers;
+ Qt::MouseButtons buttons;
+ Qt::DropAction lastAction;
+ unsigned int lastOperation;
+ void clear() {
+ rect = QRect();
+ modifiers = Qt::NoModifier;
+ buttons = Qt::NoButton;
+ lastAction = Qt::IgnoreAction;
+ lastOperation = 0;
+ }
+};
+extern QMacDndAnswerRecord qt_mac_dnd_answer_rec;
+void qt_mac_copy_answer_rect(const QDragMoveEvent &event);
+bool qt_mac_mouse_inside_answer_rect(QPoint mouse);
+
+QT_END_NAMESPACE
+
+#endif // QT_MAC_P_H
diff --git a/src/widgets/platforms/mac/qtextengine_mac.cpp b/src/widgets/platforms/mac/qtextengine_mac.cpp
new file mode 100644
index 0000000000..251d9b5072
--- /dev/null
+++ b/src/widgets/platforms/mac/qtextengine_mac.cpp
@@ -0,0 +1,656 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtextengine_p.h"
+
+#include <private/qfontengine_coretext_p.h>
+#include <private/qfontengine_mac_p.h>
+
+QT_BEGIN_NAMESPACE
+
+// set the glyph attributes heuristically. Assumes a 1 to 1 relationship between chars and glyphs
+// and no reordering.
+// also computes logClusters heuristically
+static void heuristicSetGlyphAttributes(const QChar *uc, int length, QGlyphLayout *glyphs, unsigned short *logClusters, int num_glyphs)
+{
+ // ### zeroWidth and justification are missing here!!!!!
+
+ Q_UNUSED(num_glyphs);
+
+// qDebug("QScriptEngine::heuristicSetGlyphAttributes, num_glyphs=%d", item->num_glyphs);
+
+ const bool symbolFont = false; // ####
+ glyphs->attributes[0].mark = false;
+ glyphs->attributes[0].clusterStart = true;
+ glyphs->attributes[0].dontPrint = (!symbolFont && uc[0].unicode() == 0x00ad) || qIsControlChar(uc[0].unicode());
+
+ int pos = 0;
+ int lastCat = QChar::category(uc[0].unicode());
+ for (int i = 1; i < length; ++i) {
+ if (logClusters[i] == pos)
+ // same glyph
+ continue;
+ ++pos;
+ while (pos < logClusters[i]) {
+ ++pos;
+ }
+ // hide soft-hyphens by default
+ if ((!symbolFont && uc[i].unicode() == 0x00ad) || qIsControlChar(uc[i].unicode()))
+ glyphs->attributes[pos].dontPrint = true;
+ const QUnicodeTables::Properties *prop = QUnicodeTables::properties(uc[i].unicode());
+ int cat = prop->category;
+
+ // one gets an inter character justification point if the current char is not a non spacing mark.
+ // as then the current char belongs to the last one and one gets a space justification point
+ // after the space char.
+ if (lastCat == QChar::Separator_Space)
+ glyphs->attributes[pos-1].justification = HB_Space;
+ else if (cat != QChar::Mark_NonSpacing)
+ glyphs->attributes[pos-1].justification = HB_Character;
+ else
+ glyphs->attributes[pos-1].justification = HB_NoJustification;
+
+ lastCat = cat;
+ }
+ pos = logClusters[length-1];
+ if (lastCat == QChar::Separator_Space)
+ glyphs->attributes[pos].justification = HB_Space;
+ else
+ glyphs->attributes[pos].justification = HB_Character;
+}
+
+struct QArabicProperties {
+ unsigned char shape;
+ unsigned char justification;
+};
+Q_DECLARE_TYPEINFO(QArabicProperties, Q_PRIMITIVE_TYPE);
+
+enum QArabicShape {
+ XIsolated,
+ XFinal,
+ XInitial,
+ XMedial,
+ // intermediate state
+ XCausing
+};
+
+
+// these groups correspond to the groups defined in the Unicode standard.
+// Some of these groups are equal with regards to both joining and line breaking behaviour,
+// and thus have the same enum value
+//
+// I'm not sure the mapping of syriac to arabic enums is correct with regards to justification, but as
+// I couldn't find any better document I'll hope for the best.
+enum ArabicGroup {
+ // NonJoining
+ ArabicNone,
+ ArabicSpace,
+ // Transparent
+ Transparent,
+ // Causing
+ Center,
+ Kashida,
+
+ // Arabic
+ // Dual
+ Beh,
+ Noon,
+ Meem = Noon,
+ Heh = Noon,
+ KnottedHeh = Noon,
+ HehGoal = Noon,
+ SwashKaf = Noon,
+ Yeh,
+ Hah,
+ Seen,
+ Sad = Seen,
+ Tah,
+ Kaf = Tah,
+ Gaf = Tah,
+ Lam = Tah,
+ Ain,
+ Feh = Ain,
+ Qaf = Ain,
+ // Right
+ Alef,
+ Waw,
+ Dal,
+ TehMarbuta = Dal,
+ Reh,
+ HamzaOnHehGoal,
+ YehWithTail = HamzaOnHehGoal,
+ YehBarre = HamzaOnHehGoal,
+
+ // Syriac
+ // Dual
+ Beth = Beh,
+ Gamal = Ain,
+ Heth = Noon,
+ Teth = Hah,
+ Yudh = Noon,
+ Kaph = Noon,
+ Lamadh = Lam,
+ Mim = Noon,
+ Nun = Noon,
+ Semakh = Noon,
+ FinalSemakh = Noon,
+ SyriacE = Ain,
+ Pe = Ain,
+ ReversedPe = Hah,
+ Qaph = Noon,
+ Shin = Noon,
+ Fe = Ain,
+
+ // Right
+ Alaph = Alef,
+ Dalath = Dal,
+ He = Dal,
+ SyriacWaw = Waw,
+ Zain = Alef,
+ YudhHe = Waw,
+ Sadhe = HamzaOnHehGoal,
+ Taw = Dal,
+
+ // Compiler bug? Otherwise ArabicGroupsEnd would be equal to Dal + 1.
+ Dummy = HamzaOnHehGoal,
+ ArabicGroupsEnd
+};
+
+static const unsigned char arabic_group[0x150] = {
+ ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+ ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+ ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+ ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+
+ Transparent, Transparent, Transparent, Transparent,
+ Transparent, Transparent, ArabicNone, ArabicNone,
+ ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+ ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+
+ ArabicNone, ArabicNone, Alef, Alef,
+ Waw, Alef, Yeh, Alef,
+ Beh, TehMarbuta, Beh, Beh,
+ Hah, Hah, Hah, Dal,
+
+ Dal, Reh, Reh, Seen,
+ Seen, Sad, Sad, Tah,
+ Tah, Ain, Ain, ArabicNone,
+ ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+
+ // 0x640
+ Kashida, Feh, Qaf, Kaf,
+ Lam, Meem, Noon, Heh,
+ Waw, Yeh, Yeh, Transparent,
+ Transparent, Transparent, Transparent, Transparent,
+
+ Transparent, Transparent, Transparent, Transparent,
+ Transparent, Transparent, Transparent, Transparent,
+ Transparent, ArabicNone, ArabicNone, ArabicNone,
+ ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+
+ ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+ ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+ ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+ ArabicNone, ArabicNone, Beh, Qaf,
+
+ Transparent, Alef, Alef, Alef,
+ ArabicNone, Alef, Waw, Waw,
+ Yeh, Beh, Beh, Beh,
+ Beh, Beh, Beh, Beh,
+
+ // 0x680
+ Beh, Hah, Hah, Hah,
+ Hah, Hah, Hah, Hah,
+ Dal, Dal, Dal, Dal,
+ Dal, Dal, Dal, Dal,
+
+ Dal, Reh, Reh, Reh,
+ Reh, Reh, Reh, Reh,
+ Reh, Reh, Seen, Seen,
+ Seen, Sad, Sad, Tah,
+
+ Ain, Feh, Feh, Feh,
+ Feh, Feh, Feh, Qaf,
+ Qaf, Gaf, SwashKaf, Gaf,
+ Kaf, Kaf, Kaf, Gaf,
+
+ Gaf, Gaf, Gaf, Gaf,
+ Gaf, Lam, Lam, Lam,
+ Lam, Noon, Noon, Noon,
+ Noon, Noon, KnottedHeh, Hah,
+
+ // 0x6c0
+ TehMarbuta, HehGoal, HamzaOnHehGoal, HamzaOnHehGoal,
+ Waw, Waw, Waw, Waw,
+ Waw, Waw, Waw, Waw,
+ Yeh, YehWithTail, Yeh, Waw,
+
+ Yeh, Yeh, YehBarre, YehBarre,
+ ArabicNone, TehMarbuta, Transparent, Transparent,
+ Transparent, Transparent, Transparent, Transparent,
+ Transparent, ArabicNone, ArabicNone, Transparent,
+
+ Transparent, Transparent, Transparent, Transparent,
+ Transparent, ArabicNone, ArabicNone, Transparent,
+ Transparent, ArabicNone, Transparent, Transparent,
+ Transparent, Transparent, Dal, Reh,
+
+ ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+ ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+ ArabicNone, ArabicNone, Seen, Sad,
+ Ain, ArabicNone, ArabicNone, KnottedHeh,
+
+ // 0x700
+ ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+ ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+ ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+ ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+
+ Alaph, Transparent, Beth, Gamal,
+ Gamal, Dalath, Dalath, He,
+ SyriacWaw, Zain, Heth, Teth,
+ Teth, Yudh, YudhHe, Kaph,
+
+ Lamadh, Mim, Nun, Semakh,
+ FinalSemakh, SyriacE, Pe, ReversedPe,
+ Sadhe, Qaph, Dalath, Shin,
+ Taw, Beth, Gamal, Dalath,
+
+ Transparent, Transparent, Transparent, Transparent,
+ Transparent, Transparent, Transparent, Transparent,
+ Transparent, Transparent, Transparent, Transparent,
+ Transparent, Transparent, Transparent, Transparent,
+
+ Transparent, Transparent, Transparent, Transparent,
+ Transparent, Transparent, Transparent, Transparent,
+ Transparent, Transparent, Transparent, ArabicNone,
+ ArabicNone, Zain, Kaph, Fe,
+};
+
+static inline ArabicGroup arabicGroup(unsigned short uc)
+{
+ if (uc >= 0x0600 && uc < 0x750)
+ return (ArabicGroup) arabic_group[uc-0x600];
+ else if (uc == 0x200d)
+ return Center;
+ else if (QChar::category(uc) == QChar::Separator_Space)
+ return ArabicSpace;
+ else
+ return ArabicNone;
+}
+
+
+/*
+ Arabic shaping obeys a number of rules according to the joining classes (see Unicode book, section on
+ arabic).
+
+ Each unicode char has a joining class (right, dual (left&right), center (joincausing) or transparent).
+ transparent joining is not encoded in QChar::joining(), but applies to all combining marks and format marks.
+
+ Right join-causing: dual + center
+ Left join-causing: dual + right + center
+
+ Rules are as follows (for a string already in visual order, as we have it here):
+
+ R1 Transparent characters do not affect joining behaviour.
+ R2 A right joining character, that has a right join-causing char on the right will get form XRight
+ (R3 A left joining character, that has a left join-causing char on the left will get form XLeft)
+ Note: the above rule is meaningless, as there are no pure left joining characters defined in Unicode
+ R4 A dual joining character, that has a left join-causing char on the left and a right join-causing char on
+ the right will get form XMedial
+ R5 A dual joining character, that has a right join causing char on the right, and no left join causing char on the left
+ will get form XRight
+ R6 A dual joining character, that has a left join causing char on the left, and no right join causing char on the right
+ will get form XLeft
+ R7 Otherwise the character will get form XIsolated
+
+ Additionally we have to do the minimal ligature support for lam-alef ligatures:
+
+ L1 Transparent characters do not affect ligature behaviour.
+ L2 Any sequence of Alef(XRight) + Lam(XMedial) will form the ligature Alef.Lam(XLeft)
+ L3 Any sequence of Alef(XRight) + Lam(XLeft) will form the ligature Alef.Lam(XIsolated)
+
+ The state table below handles rules R1-R7.
+*/
+
+enum Joining {
+ JNone,
+ JCausing,
+ JDual,
+ JRight,
+ JTransparent
+};
+
+static const Joining joining_for_group[ArabicGroupsEnd] = {
+ // NonJoining
+ JNone, // ArabicNone
+ JNone, // ArabicSpace
+ // Transparent
+ JTransparent, // Transparent
+ // Causing
+ JCausing, // Center
+ JCausing, // Kashida
+ // Dual
+ JDual, // Beh
+ JDual, // Noon
+ JDual, // Yeh
+ JDual, // Hah
+ JDual, // Seen
+ JDual, // Tah
+ JDual, // Ain
+ // Right
+ JRight, // Alef
+ JRight, // Waw
+ JRight, // Dal
+ JRight, // Reh
+ JRight // HamzaOnHehGoal
+};
+
+
+struct JoiningPair {
+ QArabicShape form1;
+ QArabicShape form2;
+};
+
+static const JoiningPair joining_table[5][4] =
+// None, Causing, Dual, Right
+{
+ { { XIsolated, XIsolated }, { XIsolated, XCausing }, { XIsolated, XInitial }, { XIsolated, XIsolated } }, // XIsolated
+ { { XFinal, XIsolated }, { XFinal, XCausing }, { XFinal, XInitial }, { XFinal, XIsolated } }, // XFinal
+ { { XIsolated, XIsolated }, { XInitial, XCausing }, { XInitial, XMedial }, { XInitial, XFinal } }, // XInitial
+ { { XFinal, XIsolated }, { XMedial, XCausing }, { XMedial, XMedial }, { XMedial, XFinal } }, // XMedial
+ { { XIsolated, XIsolated }, { XIsolated, XCausing }, { XIsolated, XMedial }, { XIsolated, XFinal } }, // XCausing
+};
+
+
+/*
+According to http://www.microsoft.com/middleeast/Arabicdev/IE6/KBase.asp
+
+1. Find the priority of the connecting opportunities in each word
+2. Add expansion at the highest priority connection opportunity
+3. If more than one connection opportunity have the same highest value,
+ use the opportunity closest to the end of the word.
+
+Following is a chart that provides the priority for connection
+opportunities and where expansion occurs. The character group names
+are those in table 6.6 of the UNICODE 2.0 book.
+
+
+PrioritY Glyph Condition Kashida Location
+
+Arabic_Kashida User inserted Kashida The user entered a Kashida in a position. After the user
+ (Shift+j or Shift+[E with hat]) Thus, it is the highest priority to insert an inserted kashida
+ automatic kashida.
+
+Arabic_Seen Seen, Sad Connecting to the next character. After the character.
+ (Initial or medial form).
+
+Arabic_HaaDal Teh Marbutah, Haa, Dal Connecting to previous character. Before the final form
+ of these characters.
+
+Arabic_Alef Alef, Tah, Lam, Connecting to previous character. Before the final form
+ Kaf and Gaf of these characters.
+
+Arabic_BaRa Reh, Yeh Connected to medial Beh Before preceding medial Baa
+
+Arabic_Waw Waw, Ain, Qaf, Feh Connecting to previous character. Before the final form of
+ these characters.
+
+Arabic_Normal Other connecting Connecting to previous character. Before the final form
+ characters of these characters.
+
+
+
+This seems to imply that we have at most one kashida point per arabic word.
+
+*/
+
+void qt_getArabicProperties(const unsigned short *chars, int len, QArabicProperties *properties)
+{
+// qDebug("arabicSyriacOpenTypeShape: properties:");
+ int lastPos = 0;
+ int lastGroup = ArabicNone;
+
+ ArabicGroup group = arabicGroup(chars[0]);
+ Joining j = joining_for_group[group];
+ QArabicShape shape = joining_table[XIsolated][j].form2;
+ properties[0].justification = HB_NoJustification;
+
+ for (int i = 1; i < len; ++i) {
+ // #### fix handling for spaces and punktuation
+ properties[i].justification = HB_NoJustification;
+
+ group = arabicGroup(chars[i]);
+ j = joining_for_group[group];
+
+ if (j == JTransparent) {
+ properties[i].shape = XIsolated;
+ continue;
+ }
+
+ properties[lastPos].shape = joining_table[shape][j].form1;
+ shape = joining_table[shape][j].form2;
+
+ switch(lastGroup) {
+ case Seen:
+ if (properties[lastPos].shape == XInitial || properties[lastPos].shape == XMedial)
+ properties[i-1].justification = HB_Arabic_Seen;
+ break;
+ case Hah:
+ if (properties[lastPos].shape == XFinal)
+ properties[lastPos-1].justification = HB_Arabic_HaaDal;
+ break;
+ case Alef:
+ if (properties[lastPos].shape == XFinal)
+ properties[lastPos-1].justification = HB_Arabic_Alef;
+ break;
+ case Ain:
+ if (properties[lastPos].shape == XFinal)
+ properties[lastPos-1].justification = HB_Arabic_Waw;
+ break;
+ case Noon:
+ if (properties[lastPos].shape == XFinal)
+ properties[lastPos-1].justification = HB_Arabic_Normal;
+ break;
+ case ArabicNone:
+ break;
+
+ default:
+ Q_ASSERT(false);
+ }
+
+ lastGroup = ArabicNone;
+
+ switch(group) {
+ case ArabicNone:
+ case Transparent:
+ // ### Center should probably be treated as transparent when it comes to justification.
+ case Center:
+ break;
+ case ArabicSpace:
+ properties[i].justification = HB_Arabic_Space;
+ break;
+ case Kashida:
+ properties[i].justification = HB_Arabic_Kashida;
+ break;
+ case Seen:
+ lastGroup = Seen;
+ break;
+
+ case Hah:
+ case Dal:
+ lastGroup = Hah;
+ break;
+
+ case Alef:
+ case Tah:
+ lastGroup = Alef;
+ break;
+
+ case Yeh:
+ case Reh:
+ if (properties[lastPos].shape == XMedial && arabicGroup(chars[lastPos]) == Beh)
+ properties[lastPos-1].justification = HB_Arabic_BaRa;
+ break;
+
+ case Ain:
+ case Waw:
+ lastGroup = Ain;
+ break;
+
+ case Noon:
+ case Beh:
+ case HamzaOnHehGoal:
+ lastGroup = Noon;
+ break;
+ case ArabicGroupsEnd:
+ Q_ASSERT(false);
+ }
+
+ lastPos = i;
+ }
+ properties[lastPos].shape = joining_table[shape][JNone].form1;
+
+
+// for (int i = 0; i < len; ++i)
+// qDebug("arabic properties(%d): uc=%x shape=%d, justification=%d", i, chars[i], properties[i].shape, properties[i].justification);
+}
+
+void QTextEngine::shapeTextMac(int item) const
+{
+ QScriptItem &si = layoutData->items[item];
+
+ si.glyph_data_offset = layoutData->used;
+
+ QFontEngine *font = fontEngine(si, &si.ascent, &si.descent, &si.leading);
+ if (font->type() != QFontEngine::Multi) {
+ shapeTextWithHarfbuzz(item);
+ return;
+ }
+
+#ifndef QT_MAC_USE_COCOA
+ QFontEngineMacMulti *fe = static_cast<QFontEngineMacMulti *>(font);
+#else
+ QCoreTextFontEngineMulti *fe = static_cast<QCoreTextFontEngineMulti *>(font);
+#endif
+ QTextEngine::ShaperFlags flags;
+ if (si.analysis.bidiLevel % 2)
+ flags |= RightToLeft;
+ if (option.useDesignMetrics())
+ flags |= DesignMetrics;
+
+ attributes(); // pre-initialize char attributes
+
+ const int len = length(item);
+ int num_glyphs = length(item);
+ const QChar *str = layoutData->string.unicode() + si.position;
+ ushort upperCased[256];
+ if (si.analysis.flags == QScriptAnalysis::SmallCaps || si.analysis.flags == QScriptAnalysis::Uppercase
+ || si.analysis.flags == QScriptAnalysis::Lowercase) {
+ ushort *uc = upperCased;
+ if (len > 256)
+ uc = new ushort[len];
+ for (int i = 0; i < len; ++i) {
+ if(si.analysis.flags == QScriptAnalysis::Lowercase)
+ uc[i] = str[i].toLower().unicode();
+ else
+ uc[i] = str[i].toUpper().unicode();
+ }
+ str = reinterpret_cast<const QChar *>(uc);
+ }
+
+ ensureSpace(num_glyphs);
+ num_glyphs = layoutData->glyphLayout.numGlyphs - layoutData->used;
+
+ QGlyphLayout g = availableGlyphs(&si);
+ g.numGlyphs = num_glyphs;
+ unsigned short *log_clusters = logClusters(&si);
+
+ bool stringToCMapFailed = false;
+ if (!fe->stringToCMap(str, len, &g, &num_glyphs, flags, log_clusters, attributes(), &si)) {
+ ensureSpace(num_glyphs);
+ g = availableGlyphs(&si);
+ stringToCMapFailed = !fe->stringToCMap(str, len, &g, &num_glyphs, flags, log_clusters,
+ attributes(), &si);
+ }
+
+ if (!stringToCMapFailed) {
+ heuristicSetGlyphAttributes(str, len, &g, log_clusters, num_glyphs);
+
+ si.num_glyphs = num_glyphs;
+
+ layoutData->used += si.num_glyphs;
+
+ QGlyphLayout g = shapedGlyphs(&si);
+
+ if (si.analysis.script == QUnicodeTables::Arabic) {
+ QVarLengthArray<QArabicProperties> props(len + 2);
+ QArabicProperties *properties = props.data();
+ int f = si.position;
+ int l = len;
+ if (f > 0) {
+ --f;
+ ++l;
+ ++properties;
+ }
+ if (f + l < layoutData->string.length()) {
+ ++l;
+ }
+ qt_getArabicProperties((const unsigned short *)(layoutData->string.unicode()+f), l, props.data());
+
+ unsigned short *log_clusters = logClusters(&si);
+
+ for (int i = 0; i < len; ++i) {
+ int gpos = log_clusters[i];
+ g.attributes[gpos].justification = properties[i].justification;
+ }
+ }
+ }
+
+ const ushort *uc = reinterpret_cast<const ushort *>(str);
+
+ if ((si.analysis.flags == QScriptAnalysis::SmallCaps || si.analysis.flags == QScriptAnalysis::Uppercase
+ || si.analysis.flags == QScriptAnalysis::Lowercase)
+ && uc != upperCased)
+ delete [] uc;
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/mac/qwidget_mac.mm b/src/widgets/platforms/mac/qwidget_mac.mm
new file mode 100644
index 0000000000..4adaa6b288
--- /dev/null
+++ b/src/widgets/platforms/mac/qwidget_mac.mm
@@ -0,0 +1,5418 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** Copyright (c) 2007-2008, Apple, Inc.
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**
+** * Redistributions of source code must retain the above copyright notice,
+** this list of conditions and the following disclaimer.
+**
+** * Redistributions in binary form must reproduce the above copyright notice,
+** this list of conditions and the following disclaimer in the documentation
+** and/or other materials provided with the distribution.
+**
+** * Neither the name of Apple, Inc. nor the names of its contributors
+** may be used to endorse or promote products derived from this software
+** without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**
+****************************************************************************/
+
+#include <private/qt_mac_p.h>
+#include <private/qeventdispatcher_mac_p.h>
+
+#include "qapplication.h"
+#include "qapplication_p.h"
+#include "qbitmap.h"
+#include "qcursor.h"
+#include "qdesktopwidget.h"
+#include "qevent.h"
+#include "qfileinfo.h"
+#include "qimage.h"
+#include "qlayout.h"
+#include "qmenubar.h"
+#include <private/qbackingstore_p.h>
+#include <private/qwindowsurface_mac_p.h>
+#include <private/qpaintengine_mac_p.h>
+#include "qpainter.h"
+#include "qstyle.h"
+#include "qtimer.h"
+#include "qfocusframe.h"
+#include "qdebug.h"
+#include <private/qmainwindowlayout_p.h>
+
+#include <private/qabstractscrollarea_p.h>
+#include <qabstractscrollarea.h>
+#include <ApplicationServices/ApplicationServices.h>
+#include <limits.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+#include <private/qcocoaview_mac_p.h>
+#include <private/qcocoawindow_mac_p.h>
+#include <private/qcocoawindowdelegate_mac_p.h>
+#include <private/qcocoapanel_mac_p.h>
+
+#include "qwidget_p.h"
+#include "qevent_p.h"
+#include "qdnd_p.h"
+#include <QtGui/qgraphicsproxywidget.h>
+#include "qmainwindow.h"
+
+QT_BEGIN_NAMESPACE
+
+// qmainwindow.cpp
+extern QMainWindowLayout *qt_mainwindow_layout(const QMainWindow *window);
+
+#define XCOORD_MAX 16383
+#define WRECT_MAX 8191
+
+#ifndef QT_MAC_USE_COCOA
+
+extern "C" {
+ extern OSStatus _HIViewScrollRectWithOptions(HIViewRef, const HIRect *, CGFloat, CGFloat,
+ OptionBits) __attribute__ ((weak));
+}
+#define kHIViewScrollRectAdjustInvalid 1
+#define kHIViewScrollRectDontInvalidateRevealedArea 2
+#endif
+
+
+/*****************************************************************************
+ QWidget debug facilities
+ *****************************************************************************/
+//#define DEBUG_WINDOW_RGNS
+//#define DEBUG_WINDOW_CREATE
+//#define DEBUG_WINDOW_STATE
+//#define DEBUG_WIDGET_PAINT
+
+/*****************************************************************************
+ QWidget globals
+ *****************************************************************************/
+#ifndef QT_MAC_USE_COCOA
+typedef QHash<Qt::WindowFlags, WindowGroupRef> WindowGroupHash;
+Q_GLOBAL_STATIC(WindowGroupHash, qt_mac_window_groups)
+const UInt32 kWidgetCreatorQt = kEventClassQt;
+enum {
+ kWidgetPropertyQWidget = 'QWId' //QWidget *
+};
+#endif
+
+static bool qt_mac_raise_process = true;
+static OSWindowRef qt_root_win = 0;
+QWidget *mac_mouse_grabber = 0;
+QWidget *mac_keyboard_grabber = 0;
+
+#ifndef QT_MAC_USE_COCOA
+#ifdef QT_NAMESPACE
+
+// produce the string "com.trolltech.qt-namespace.widget", where "namespace" is the contents of QT_NAMESPACE.
+#define SS(x) #x
+#define S0(x) SS(x)
+#define S "com.trolltech.qt-" S0(QT_NAMESPACE) ".widget"
+
+static CFStringRef kObjectQWidget = CFSTR(S);
+
+#undef SS
+#undef S0
+#undef S
+
+#else
+static CFStringRef kObjectQWidget = CFSTR("com.trolltech.qt.widget");
+#endif // QT_NAMESPACE
+#endif // QT_MAC_USE_COCOA
+
+/*****************************************************************************
+ Externals
+ *****************************************************************************/
+extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp
+extern QWidget *qt_mac_modal_blocked(QWidget *); //qapplication_mac.mm
+extern void qt_event_request_activate(QWidget *); //qapplication_mac.mm
+extern bool qt_event_remove_activate(); //qapplication_mac.mm
+extern void qt_mac_event_release(QWidget *w); //qapplication_mac.mm
+extern void qt_event_request_showsheet(QWidget *); //qapplication_mac.mm
+extern void qt_event_request_window_change(QWidget *); //qapplication_mac.mm
+extern QPointer<QWidget> qt_last_mouse_receiver; //qapplication_mac.mm
+extern QPointer<QWidget> qt_last_native_mouse_receiver; //qt_cocoa_helpers_mac.mm
+extern IconRef qt_mac_create_iconref(const QPixmap &); //qpixmap_mac.cpp
+extern void qt_mac_set_cursor(const QCursor *, const QPoint &); //qcursor_mac.mm
+extern void qt_mac_update_cursor(); //qcursor_mac.mm
+extern bool qt_nograb();
+extern CGImageRef qt_mac_create_cgimage(const QPixmap &, bool); //qpixmap_mac.cpp
+extern RgnHandle qt_mac_get_rgn(); //qregion_mac.cpp
+extern QRegion qt_mac_convert_mac_region(RgnHandle rgn); //qregion_mac.cpp
+extern void qt_mac_setMouseGrabCursor(bool set, QCursor *cursor = 0); // qcursor_mac.mm
+extern QPointer<QWidget> topLevelAt_cache; // qapplication_mac.mm
+/*****************************************************************************
+ QWidget utility functions
+ *****************************************************************************/
+void Q_GUI_EXPORT qt_mac_set_raise_process(bool b) { qt_mac_raise_process = b; }
+static QSize qt_mac_desktopSize()
+{
+ int w = 0, h = 0;
+ CGDisplayCount cg_count;
+ CGGetActiveDisplayList(0, 0, &cg_count);
+ QVector<CGDirectDisplayID> displays(cg_count);
+ CGGetActiveDisplayList(cg_count, displays.data(), &cg_count);
+ Q_ASSERT(cg_count == (CGDisplayCount)displays.size());
+ for(int i = 0; i < (int)cg_count; ++i) {
+ CGRect r = CGDisplayBounds(displays.at(i));
+ w = qMax<int>(w, qRound(r.origin.x + r.size.width));
+ h = qMax<int>(h, qRound(r.origin.y + r.size.height));
+ }
+ return QSize(w, h);
+}
+
+#ifdef QT_MAC_USE_COCOA
+static NSDrawer *qt_mac_drawer_for(const QWidget *widget)
+{
+ NSView *widgetView = reinterpret_cast<NSView *>(widget->window()->effectiveWinId());
+ NSArray *windows = [NSApp windows];
+ for (NSWindow *window in windows) {
+ NSArray *drawers = [window drawers];
+ for (NSDrawer *drawer in drawers) {
+ if ([drawer contentView] == widgetView)
+ return drawer;
+ }
+ }
+ return 0;
+}
+#endif
+
+static void qt_mac_destructView(OSViewRef view)
+{
+#ifdef QT_MAC_USE_COCOA
+ NSWindow *window = [view window];
+ if ([window contentView] == view)
+ [window setContentView:[[NSView alloc] initWithFrame:[view bounds]]];
+ [view removeFromSuperview];
+ [view release];
+#else
+ HIViewRemoveFromSuperview(view);
+ CFRelease(view);
+#endif
+}
+
+static void qt_mac_destructWindow(OSWindowRef window)
+{
+#ifdef QT_MAC_USE_COCOA
+ if ([window isVisible] && [window isSheet]){
+ [NSApp endSheet:window];
+ [window orderOut:window];
+ }
+
+ [[QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate] resignDelegateForWindow:window];
+ [window release];
+#else
+ // Remove property to clean up memory:
+ RemoveWindowProperty(window, kWidgetCreatorQt, kWidgetPropertyQWidget);
+ CFRelease(window);
+#endif
+}
+
+static void qt_mac_destructDrawer(NSDrawer *drawer)
+{
+#ifdef QT_MAC_USE_COCOA
+ [[QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate] resignDelegateForDrawer:drawer];
+ [drawer release];
+#else
+ Q_UNUSED(drawer);
+#endif
+}
+
+bool qt_mac_can_clickThrough(const QWidget *w)
+{
+ static int qt_mac_carbon_clickthrough = -1;
+ if (qt_mac_carbon_clickthrough < 0)
+ qt_mac_carbon_clickthrough = !qgetenv("QT_MAC_NO_COCOA_CLICKTHROUGH").isEmpty();
+ bool ret = !qt_mac_carbon_clickthrough;
+ for ( ; w; w = w->parentWidget()) {
+ if (w->testAttribute(Qt::WA_MacNoClickThrough)) {
+ ret = false;
+ break;
+ }
+ }
+ return ret;
+}
+
+bool qt_mac_is_macsheet(const QWidget *w)
+{
+ if (!w)
+ return false;
+
+ Qt::WindowModality modality = w->windowModality();
+ if (modality == Qt::ApplicationModal)
+ return false;
+ return w->parentWidget() && (modality == Qt::WindowModal || w->windowType() == Qt::Sheet);
+}
+
+bool qt_mac_is_macdrawer(const QWidget *w)
+{
+ return (w && w->parentWidget() && w->windowType() == Qt::Drawer);
+}
+
+bool qt_mac_insideKeyWindow(const QWidget *w)
+{
+#ifdef QT_MAC_USE_COCOA
+ return [[reinterpret_cast<NSView *>(w->effectiveWinId()) window] isKeyWindow];
+#else
+ Q_UNUSED(w);
+#endif
+ return false;
+}
+
+bool qt_mac_set_drawer_preferred_edge(QWidget *w, Qt::DockWidgetArea where) //users of Qt for Mac OS X can use this..
+{
+ if(!qt_mac_is_macdrawer(w))
+ return false;
+
+#if QT_MAC_USE_COCOA
+ NSDrawer *drawer = qt_mac_drawer_for(w);
+ if (!drawer)
+ return false;
+ NSRectEdge edge;
+ if (where & Qt::LeftDockWidgetArea)
+ edge = NSMinXEdge;
+ else if (where & Qt::RightDockWidgetArea)
+ edge = NSMaxXEdge;
+ else if (where & Qt::TopDockWidgetArea)
+ edge = NSMaxYEdge;
+ else if (where & Qt::BottomDockWidgetArea)
+ edge = NSMinYEdge;
+ else
+ return false;
+
+ if (edge == [drawer preferredEdge]) //no-op
+ return false;
+
+ if (w->isVisible()) {
+ [drawer close];
+ [drawer openOnEdge:edge];
+ }
+ [drawer setPreferredEdge:edge];
+#else
+ OSWindowRef window = qt_mac_window_for(w);
+ OptionBits edge;
+ if(where & Qt::LeftDockWidgetArea)
+ edge = kWindowEdgeLeft;
+ else if(where & Qt::RightDockWidgetArea)
+ edge = kWindowEdgeRight;
+ else if(where & Qt::TopDockWidgetArea)
+ edge = kWindowEdgeTop;
+ else if(where & Qt::BottomDockWidgetArea)
+ edge = kWindowEdgeBottom;
+ else
+ return false;
+
+ if(edge == GetDrawerPreferredEdge(window)) //no-op
+ return false;
+
+ //do it
+ SetDrawerPreferredEdge(window, edge);
+ if(w->isVisible()) {
+ CloseDrawer(window, false);
+ OpenDrawer(window, edge, true);
+ }
+#endif
+ return true;
+}
+
+#ifndef QT_MAC_USE_COCOA
+Q_GUI_EXPORT
+#endif
+QPoint qt_mac_posInWindow(const QWidget *w)
+{
+ QPoint ret = w->data->wrect.topLeft();
+ while(w && !w->isWindow()) {
+ ret += w->pos();
+ w = w->parentWidget();
+ }
+ return ret;
+}
+
+//find a QWidget from a OSWindowRef
+QWidget *qt_mac_find_window(OSWindowRef window)
+{
+#ifdef QT_MAC_USE_COCOA
+ return [window QT_MANGLE_NAMESPACE(qt_qwidget)];
+#else
+ if(!window)
+ return 0;
+
+ QWidget *ret;
+ if(GetWindowProperty(window, kWidgetCreatorQt, kWidgetPropertyQWidget, sizeof(ret), 0, &ret) == noErr)
+ return ret;
+ return 0;
+#endif
+}
+
+inline static void qt_mac_set_fullscreen_mode(bool b)
+{
+ extern bool qt_mac_app_fullscreen; //qapplication_mac.mm
+ if(qt_mac_app_fullscreen == b)
+ return;
+ qt_mac_app_fullscreen = b;
+ if (b) {
+ SetSystemUIMode(kUIModeAllHidden, kUIOptionAutoShowMenuBar);
+ } else {
+ SetSystemUIMode(kUIModeNormal, 0);
+ }
+}
+
+Q_GUI_EXPORT OSViewRef qt_mac_nativeview_for(const QWidget *w)
+{
+ return reinterpret_cast<OSViewRef>(w->internalWinId());
+}
+
+Q_GUI_EXPORT OSViewRef qt_mac_effectiveview_for(const QWidget *w)
+{
+ // Get the first non-alien (parent) widget for
+ // w, and return its NSView (if it has one):
+ return reinterpret_cast<OSViewRef>(w->effectiveWinId());
+}
+
+Q_GUI_EXPORT OSViewRef qt_mac_get_contentview_for(OSWindowRef w)
+{
+#ifdef QT_MAC_USE_COCOA
+ return [w contentView];
+#else
+ HIViewRef contentView = 0;
+ OSStatus err = GetRootControl(w, &contentView); // Returns the window's content view (Apple QA1214)
+ if (err == errUnknownControl) {
+ contentView = HIViewGetRoot(w);
+ } else if (err != noErr) {
+ qWarning("Qt:Could not get content or root view of window! %s:%d [%ld]",
+ __FILE__, __LINE__, err);
+ }
+ return contentView;
+#endif
+}
+
+bool qt_mac_sendMacEventToWidget(QWidget *widget, EventRef ref)
+{
+ return widget->macEvent(0, ref);
+}
+
+Q_GUI_EXPORT OSWindowRef qt_mac_window_for(OSViewRef view)
+{
+#ifdef QT_MAC_USE_COCOA
+ if (view)
+ return [view window];
+ return 0;
+#else
+ return HIViewGetWindow(view);
+#endif
+}
+
+static bool qt_isGenuineQWidget(OSViewRef ref)
+{
+#ifdef QT_MAC_USE_COCOA
+ return [ref isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaView) class]];
+#else
+ return HIObjectIsOfClass(HIObjectRef(ref), kObjectQWidget);
+#endif
+}
+
+bool qt_isGenuineQWidget(const QWidget *window)
+{
+ if (!window)
+ return false;
+
+ if (!window->internalWinId())
+ return true; //alien
+
+ return qt_isGenuineQWidget(OSViewRef(window->internalWinId()));
+}
+
+Q_GUI_EXPORT OSWindowRef qt_mac_window_for(const QWidget *w)
+{
+ if (OSViewRef hiview = qt_mac_effectiveview_for(w)) {
+ OSWindowRef window = qt_mac_window_for(hiview);
+ if (window)
+ return window;
+
+ if (qt_isGenuineQWidget(hiview)) {
+ // This is a workaround for NSToolbar. When a widget is hidden
+ // by clicking the toolbar button, Cocoa reparents the widgets
+ // to another window (but Qt doesn't know about it).
+ // When we start showing them, it reparents back,
+ // but at this point it's window is nil, but the window it's being brought
+ // into (the Qt one) is for sure created.
+ // This stops the hierarchy moving under our feet.
+ QWidget *toplevel = w->window();
+ if (toplevel != w) {
+ hiview = qt_mac_nativeview_for(toplevel);
+ if (OSWindowRef w = qt_mac_window_for(hiview))
+ return w;
+ }
+
+ toplevel->d_func()->createWindow_sys();
+ // Reget the hiview since "create window" could potentially move the view (I guess).
+ hiview = qt_mac_nativeview_for(toplevel);
+ return qt_mac_window_for(hiview);
+ }
+ }
+ return 0;
+}
+
+#ifndef QT_MAC_USE_COCOA
+/* Checks if the current group is a 'stay on top' group. If so, the
+ group gets removed from the hash table */
+static void qt_mac_release_stays_on_top_group(WindowGroupRef group)
+{
+ for (WindowGroupHash::iterator it = qt_mac_window_groups()->begin(); it != qt_mac_window_groups()->end(); ++it) {
+ if (it.value() == group) {
+ qt_mac_window_groups()->remove(it.key());
+ return;
+ }
+ }
+}
+
+/* Use this function instead of ReleaseWindowGroup, this will be sure to release the
+ stays on top window group (created with qt_mac_get_stays_on_top_group below) */
+static void qt_mac_release_window_group(WindowGroupRef group)
+{
+ ReleaseWindowGroup(group);
+ if (GetWindowGroupRetainCount(group) == 0)
+ qt_mac_release_stays_on_top_group(group);
+}
+#define ReleaseWindowGroup(x) Are you sure you wanted to do that? (you wanted qt_mac_release_window_group)
+
+SInt32 qt_mac_get_group_level(WindowClass wclass)
+{
+ SInt32 group_level;
+ CGWindowLevel tmpLevel;
+ GetWindowGroupLevelOfType(GetWindowGroupOfClass(wclass), kWindowGroupLevelActive, &tmpLevel);
+ group_level = tmpLevel;
+ return group_level;
+}
+#endif
+
+#ifndef QT_MAC_USE_COCOA
+static void qt_mac_set_window_group(OSWindowRef window, Qt::WindowFlags flags, int level)
+{
+ WindowGroupRef group = 0;
+ if (qt_mac_window_groups()->contains(flags)) {
+ group = qt_mac_window_groups()->value(flags);
+ RetainWindowGroup(group);
+ } else {
+ CreateWindowGroup(kWindowActivationScopeNone, &group);
+ SetWindowGroupLevel(group, level);
+ SetWindowGroupParent(group, GetWindowGroupOfClass(kAllWindowClasses));
+ qt_mac_window_groups()->insert(flags, group);
+ }
+ SetWindowGroup(window, group);
+}
+
+inline static void qt_mac_set_window_group_to_stays_on_top(OSWindowRef window, Qt::WindowType type)
+{
+ // We create one static stays on top window group so that
+ // all stays on top (aka popups) will fall into the same
+ // group and be able to be raise()'d with releation to one another (from
+ // within the same window group).
+ qt_mac_set_window_group(window, type|Qt::WindowStaysOnTopHint, qt_mac_get_group_level(kOverlayWindowClass));
+}
+
+inline static void qt_mac_set_window_group_to_tooltip(OSWindowRef window)
+{
+ // Since new groups are created for 'stays on top' windows, the
+ // same must be done for tooltips. Otherwise, tooltips would be drawn
+ // below 'stays on top' widgets even tough they are on the same level.
+ // Also, add 'two' to the group level to make sure they also get on top of popups.
+ qt_mac_set_window_group(window, Qt::ToolTip, qt_mac_get_group_level(kHelpWindowClass)+2);
+}
+
+inline static void qt_mac_set_window_group_to_popup(OSWindowRef window)
+{
+ // In Qt, a popup is seen as a 'stay on top' window.
+ // Since new groups are created for 'stays on top' windows, the
+ // same must be done for popups. Otherwise, popups would be drawn
+ // below 'stays on top' windows. Add 1 to get above pure stay-on-top windows.
+ qt_mac_set_window_group(window, Qt::Popup, qt_mac_get_group_level(kOverlayWindowClass)+1);
+}
+#endif
+
+inline static bool updateRedirectedToGraphicsProxyWidget(QWidget *widget, const QRect &rect)
+{
+ if (!widget)
+ return false;
+
+#ifndef QT_NO_GRAPHICSVIEW
+ QWidget *tlw = widget->window();
+ QWExtra *extra = qt_widget_private(tlw)->extra;
+ if (extra && extra->proxyWidget) {
+ extra->proxyWidget->update(rect.translated(widget->mapTo(tlw, QPoint())));
+ return true;
+ }
+#endif
+
+ return false;
+}
+
+inline static bool updateRedirectedToGraphicsProxyWidget(QWidget *widget, const QRegion &rgn)
+{
+ if (!widget)
+ return false;
+
+#ifndef QT_NO_GRAPHICSVIEW
+ QWidget *tlw = widget->window();
+ QWExtra *extra = qt_widget_private(tlw)->extra;
+ if (extra && extra->proxyWidget) {
+ const QPoint offset(widget->mapTo(tlw, QPoint()));
+ const QVector<QRect> rects = rgn.rects();
+ for (int i = 0; i < rects.size(); ++i)
+ extra->proxyWidget->update(rects.at(i).translated(offset));
+ return true;
+ }
+#endif
+
+ return false;
+}
+
+void QWidgetPrivate::macSetNeedsDisplay(QRegion region)
+{
+ Q_Q(QWidget);
+#ifndef QT_MAC_USE_COCOA
+ if (region.isEmpty())
+ HIViewSetNeedsDisplay(qt_mac_nativeview_for(q), true);
+ else if (RgnHandle rgnHandle = region.toQDRgnForUpdate_sys())
+ HIViewSetNeedsDisplayInRegion(qt_mac_nativeview_for(q), QMacSmartQuickDrawRegion(rgnHandle), true);
+ else
+ HIViewSetNeedsDisplay(qt_mac_nativeview_for(q), true); // do a complete repaint on overflow.
+#else
+ if (NSView *nativeView = qt_mac_nativeview_for(q)) {
+ // INVARIANT: q is _not_ alien. So we can optimize a little:
+ if (region.isEmpty()) {
+ [nativeView setNeedsDisplay:YES];
+ } else {
+ QVector<QRect> rects = region.rects();
+ for (int i = 0; i<rects.count(); ++i) {
+ const QRect &rect = rects.at(i);
+ NSRect nsrect = NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height());
+ [nativeView setNeedsDisplayInRect:nsrect];
+ }
+ }
+ } else if (QWidget *effectiveWidget = q->nativeParentWidget()) {
+ // INVARIANT: q is alien, and effectiveWidget is native.
+ if (NSView *effectiveView = qt_mac_nativeview_for(effectiveWidget)) {
+ if (region.isEmpty()) {
+ const QRect &rect = q->rect();
+ QPoint p = q->mapTo(effectiveWidget, rect.topLeft());
+ NSRect nsrect = NSMakeRect(p.x(), p.y(), rect.width(), rect.height());
+ [effectiveView setNeedsDisplayInRect:nsrect];
+ } else {
+ QVector<QRect> rects = region.rects();
+ for (int i = 0; i<rects.count(); ++i) {
+ const QRect &rect = rects.at(i);
+ QPoint p = q->mapTo(effectiveWidget, rect.topLeft());
+ NSRect nsrect = NSMakeRect(p.x(), p.y(), rect.width(), rect.height());
+ [effectiveView setNeedsDisplayInRect:nsrect];
+ }
+ }
+ }
+ }
+#endif
+}
+
+void QWidgetPrivate::macUpdateIsOpaque()
+{
+ Q_Q(QWidget);
+ if (!q->testAttribute(Qt::WA_WState_Created))
+ return;
+#ifndef QT_MAC_USE_COCOA
+ HIViewFeatures bits;
+ HIViewRef hiview = qt_mac_nativeview_for(q);
+ HIViewGetFeatures(hiview, &bits);
+ if ((bits & kHIViewIsOpaque) == isOpaque)
+ return;
+ if (isOpaque) {
+ HIViewChangeFeatures(hiview, kHIViewIsOpaque, 0);
+ } else {
+ HIViewChangeFeatures(hiview, 0, kHIViewIsOpaque);
+ }
+ if (q->isVisible())
+ HIViewReshapeStructure(qt_mac_nativeview_for(q));
+#else
+ if (isRealWindow() && !q->testAttribute(Qt::WA_MacBrushedMetal)) {
+ bool opaque = isOpaque;
+ if (extra && extra->imageMask)
+ opaque = false; // we are never opaque when we have a mask.
+ [qt_mac_window_for(q) setOpaque:opaque];
+ }
+#endif
+}
+#ifdef QT_MAC_USE_COCOA
+static OSWindowRef qt_mac_create_window(QWidget *widget, WindowClass wclass,
+ NSUInteger wattr, const QRect &crect)
+{
+ // Determine if we need to add in our "custom window" attribute. Cocoa is rather clever
+ // in deciding if we need the maximize button or not (i.e., it's resizeable, so you
+ // must need a maximize button). So, the only buttons we have control over are the
+ // close and minimize buttons. If someone wants to customize and NOT have the maximize
+ // button, then we have to do our hack. We only do it for these cases because otherwise
+ // the window looks different when activated. This "QtMacCustomizeWindow" attribute is
+ // intruding on a public space and WILL BREAK in the future.
+ // One can hope that there is a more public API available by that time.
+ Qt::WindowFlags flags = widget ? widget->windowFlags() : Qt::WindowFlags(0);
+ if ((flags & Qt::CustomizeWindowHint)) {
+ if ((flags & (Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint
+ | Qt::WindowMinimizeButtonHint | Qt::WindowTitleHint))
+ && !(flags & Qt::WindowMaximizeButtonHint))
+ wattr |= QtMacCustomizeWindow;
+ }
+
+ // If we haven't created the desktop widget, you have to pass the rectangle
+ // in "cocoa coordinates" (i.e., top points to the lower left coordinate).
+ // Otherwise, we do the conversion for you. Since we are the only ones that
+ // create the desktop widget, this is OK (but confusing).
+ NSRect geo = NSMakeRect(crect.left(),
+ (qt_root_win != 0) ? flipYCoordinate(crect.bottom() + 1) : crect.top(),
+ crect.width(), crect.height());
+ QMacCocoaAutoReleasePool pool;
+ OSWindowRef window;
+ switch (wclass) {
+ case kMovableModalWindowClass:
+ case kModalWindowClass:
+ case kSheetWindowClass:
+ case kFloatingWindowClass:
+ case kOverlayWindowClass:
+ case kHelpWindowClass: {
+ NSPanel *panel;
+ BOOL needFloating = NO;
+ BOOL worksWhenModal = widget && (widget->windowType() == Qt::Popup);
+ // Add in the extra flags if necessary.
+ switch (wclass) {
+ case kSheetWindowClass:
+ wattr |= NSDocModalWindowMask;
+ break;
+ case kFloatingWindowClass:
+ case kHelpWindowClass:
+ needFloating = YES;
+ wattr |= NSUtilityWindowMask;
+ break;
+ default:
+ break;
+ }
+ panel = [[QT_MANGLE_NAMESPACE(QCocoaPanel) alloc] QT_MANGLE_NAMESPACE(qt_initWithQWidget):widget contentRect:geo styleMask:wattr];
+ [panel setFloatingPanel:needFloating];
+ [panel setWorksWhenModal:worksWhenModal];
+ window = panel;
+ break;
+ }
+ case kDrawerWindowClass: {
+ NSDrawer *drawer = [[NSDrawer alloc] initWithContentSize:geo.size preferredEdge:NSMinXEdge];
+ [[QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate] becomeDelegateForDrawer:drawer widget:widget];
+ QWidget *parentWidget = widget->parentWidget();
+ if (parentWidget)
+ [drawer setParentWindow:qt_mac_window_for(parentWidget)];
+ [drawer setLeadingOffset:0.0];
+ [drawer setTrailingOffset:25.0];
+ window = [[drawer contentView] window]; // Just to make sure we actually return a window
+ break;
+ }
+ default:
+ window = [[QT_MANGLE_NAMESPACE(QCocoaWindow) alloc] QT_MANGLE_NAMESPACE(qt_initWithQWidget):widget contentRect:geo styleMask:wattr];
+ break;
+ }
+ qt_syncCocoaTitleBarButtons(window, widget);
+ return window;
+}
+#else
+static OSWindowRef qt_mac_create_window(QWidget *, WindowClass wclass, WindowAttributes wattr,
+ const QRect &crect)
+{
+ OSWindowRef window;
+ Rect geo;
+ SetRect(&geo, crect.left(), crect.top(), crect.right() + 1, crect.bottom() + 1);
+ OSStatus err;
+ if(geo.right <= geo.left) geo.right = geo.left + 1;
+ if(geo.bottom <= geo.top) geo.bottom = geo.top + 1;
+ Rect null_rect;
+ SetRect(&null_rect, 0, 0, 1, 1);
+ err = CreateNewWindow(wclass, wattr, &null_rect, &window);
+ if(err == noErr) {
+ err = SetWindowBounds(window, kWindowContentRgn, &geo);
+ if(err != noErr)
+ qWarning("QWidget: Internal error (%s:%d)", __FILE__, __LINE__);
+ }
+ return window;
+}
+
+#ifndef QT_NO_GESTURES
+#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6
+/* We build the release package against the 10.4 SDK.
+ So, to enable gestures for applications running on
+ 10.6+, we define the missing constants here: */
+enum {
+ kEventClassGesture = 'gest',
+ kEventGestureStarted = 1,
+ kEventGestureEnded = 2,
+ kEventGestureMagnify = 4,
+ kEventGestureSwipe = 5,
+ kEventGestureRotate = 6,
+ kEventParamRotationAmount = 'rota',
+ kEventParamSwipeDirection = 'swip',
+ kEventParamMagnificationAmount = 'magn'
+};
+#endif
+#endif // QT_NO_GESTURES
+
+// window events
+static EventTypeSpec window_events[] = {
+ { kEventClassWindow, kEventWindowClose },
+ { kEventClassWindow, kEventWindowExpanded },
+ { kEventClassWindow, kEventWindowHidden },
+ { kEventClassWindow, kEventWindowZoom },
+ { kEventClassWindow, kEventWindowZoomed },
+ { kEventClassWindow, kEventWindowCollapsed },
+ { kEventClassWindow, kEventWindowToolbarSwitchMode },
+ { kEventClassWindow, kEventWindowProxyBeginDrag },
+ { kEventClassWindow, kEventWindowProxyEndDrag },
+ { kEventClassWindow, kEventWindowResizeCompleted },
+ { kEventClassWindow, kEventWindowBoundsChanging },
+ { kEventClassWindow, kEventWindowGetRegion },
+ { kEventClassWindow, kEventWindowGetClickModality },
+ { kEventClassWindow, kEventWindowTransitionCompleted },
+ { kEventClassGesture, kEventGestureStarted },
+ { kEventClassGesture, kEventGestureEnded },
+ { kEventClassGesture, kEventGestureMagnify },
+ { kEventClassGesture, kEventGestureSwipe },
+ { kEventClassGesture, kEventGestureRotate },
+ { kEventClassMouse, kEventMouseDown }
+};
+static EventHandlerUPP mac_win_eventUPP = 0;
+static void cleanup_win_eventUPP()
+{
+ DisposeEventHandlerUPP(mac_win_eventUPP);
+ mac_win_eventUPP = 0;
+}
+static const EventHandlerUPP make_win_eventUPP()
+{
+ if(mac_win_eventUPP)
+ return mac_win_eventUPP;
+ qAddPostRoutine(cleanup_win_eventUPP);
+ return mac_win_eventUPP = NewEventHandlerUPP(QWidgetPrivate::qt_window_event);
+}
+OSStatus QWidgetPrivate::qt_window_event(EventHandlerCallRef er, EventRef event, void *)
+{
+ QScopedLoopLevelCounter loopLevelCounter(qApp->d_func()->threadData);
+ bool handled_event = true;
+ UInt32 ekind = GetEventKind(event), eclass = GetEventClass(event);
+ switch(eclass) {
+ case kEventClassWindow: {
+ WindowRef wid = 0;
+ GetEventParameter(event, kEventParamDirectObject, typeWindowRef, 0,
+ sizeof(WindowRef), 0, &wid);
+ QWidget *widget = qt_mac_find_window(wid);
+ if(!widget) {
+ handled_event = false;
+ } else if(ekind == kEventWindowGetClickModality) {
+ // Carbon will send us kEventWindowGetClickModality before every
+ // mouse press / release event. By returning 'true', we tell Carbon
+ // that we would like the event target to receive the mouse event even
+ // if the target is modally shaddowed. In Qt, this makes sense when we
+ // e.g. have a popup showing, as the popup will grab the event
+ // and perhaps use it to close itself.
+ // By also setting the current modal window back into the event, we
+ // help Carbon determining which window is supposed to be raised.
+ handled_event = qApp->activePopupWidget() ? true : false;
+ } else if(ekind == kEventWindowClose) {
+ widget->d_func()->close_helper(QWidgetPrivate::CloseWithSpontaneousEvent);
+ QMenuBar::macUpdateMenuBar();
+ } else if (ekind == kEventWindowTransitionCompleted) {
+ WindowTransitionAction transitionAction;
+ GetEventParameter(event, kEventParamWindowTransitionAction, typeWindowTransitionAction,
+ 0, sizeof(transitionAction), 0, &transitionAction);
+ if (transitionAction == kWindowHideTransitionAction)
+ widget->hide();
+ } else if(ekind == kEventWindowExpanded) {
+ Qt::WindowStates currState = Qt::WindowStates(widget->data->window_state);
+ Qt::WindowStates newState = currState;
+ if (currState & Qt::WindowMinimized)
+ newState &= ~Qt::WindowMinimized;
+ if (!(currState & Qt::WindowActive))
+ newState |= Qt::WindowActive;
+ if (newState != currState) {
+ // newState will differ from currState if the window
+ // was expanded after clicking on the jewels (as opposed
+ // to calling QWidget::setWindowState)
+ widget->data->window_state = newState;
+ QWindowStateChangeEvent e(currState);
+ QApplication::sendSpontaneousEvent(widget, &e);
+ }
+
+ QShowEvent qse;
+ QApplication::sendSpontaneousEvent(widget, &qse);
+ } else if(ekind == kEventWindowZoom) {
+ widget->d_func()->topData()->normalGeometry = widget->geometry();
+ handled_event = false;
+ } else if(ekind == kEventWindowZoomed) {
+ WindowPartCode windowPart;
+ GetEventParameter(event, kEventParamWindowPartCode,
+ typeWindowPartCode, 0, sizeof(windowPart), 0, &windowPart);
+ if(windowPart == inZoomIn && widget->isMaximized()) {
+
+ widget->data->window_state = widget->data->window_state & ~Qt::WindowMaximized;
+ QWindowStateChangeEvent e(Qt::WindowStates(widget->data->window_state | Qt::WindowMaximized));
+ QApplication::sendSpontaneousEvent(widget, &e);
+ } else if(windowPart == inZoomOut && !widget->isMaximized()) {
+ widget->data->window_state = widget->data->window_state | Qt::WindowMaximized;
+ QWindowStateChangeEvent e(Qt::WindowStates(widget->data->window_state
+ & ~Qt::WindowMaximized));
+ QApplication::sendSpontaneousEvent(widget, &e);
+ }
+ qt_button_down = 0;
+ } else if(ekind == kEventWindowCollapsed) {
+ if (!widget->isMinimized()) {
+ widget->data->window_state = widget->data->window_state | Qt::WindowMinimized;
+ QWindowStateChangeEvent e(Qt::WindowStates(widget->data->window_state & ~Qt::WindowMinimized));
+ QApplication::sendSpontaneousEvent(widget, &e);
+ }
+
+ // Deactivate this window:
+ if (widget->isActiveWindow() && !(widget->windowType() == Qt::Popup)) {
+ QWidget *w = 0;
+ if (widget->parentWidget())
+ w = widget->parentWidget()->window();
+ if (!w || (!w->isVisible() && !w->isMinimized())) {
+ for (WindowPtr wp = GetFrontWindowOfClass(kDocumentWindowClass, true);
+ wp; wp = GetNextWindowOfClass(wp, kDocumentWindowClass, true)) {
+ if ((w = qt_mac_find_window(wp)))
+ break;
+ }
+ }
+ if(!(w && w->isVisible() && !w->isMinimized()))
+ qApp->setActiveWindow(0);
+ }
+
+ //we send a hide to be like X11/Windows
+ QEvent e(QEvent::Hide);
+ QApplication::sendSpontaneousEvent(widget, &e);
+ qt_button_down = 0;
+ } else if(ekind == kEventWindowToolbarSwitchMode) {
+ macSendToolbarChangeEvent(widget);
+ HIToolbarRef toolbar;
+ if (GetWindowToolbar(wid, &toolbar) == noErr) {
+ if (toolbar) {
+ // Let HIToolbar do its thang, but things like the OpenGL context
+ // needs to know about it.
+ CallNextEventHandler(er, event);
+ qt_event_request_window_change(widget);
+ widget->data->fstrut_dirty = true;
+ }
+ }
+ } else if(ekind == kEventWindowGetRegion) {
+ WindowRef window;
+ GetEventParameter(event, kEventParamDirectObject, typeWindowRef, 0,
+ sizeof(window), 0, &window);
+ WindowRegionCode wcode;
+ GetEventParameter(event, kEventParamWindowRegionCode, typeWindowRegionCode, 0,
+ sizeof(wcode), 0, &wcode);
+ if (wcode != kWindowOpaqueRgn){
+ // If the region is kWindowOpaqueRgn, don't call next
+ // event handler cause this will make the shadow of
+ // masked windows become offset. Unfortunately, we're not sure why.
+ CallNextEventHandler(er, event);
+ }
+ RgnHandle rgn;
+ GetEventParameter(event, kEventParamRgnHandle, typeQDRgnHandle, 0,
+ sizeof(rgn), 0, &rgn);
+
+ if(QWidgetPrivate::qt_widget_rgn(qt_mac_find_window(window), wcode, rgn, false))
+ SetEventParameter(event, kEventParamRgnHandle, typeQDRgnHandle, sizeof(rgn), &rgn);
+ } else if(ekind == kEventWindowProxyBeginDrag) {
+ QIconDragEvent e;
+ QApplication::sendSpontaneousEvent(widget, &e);
+ } else if(ekind == kEventWindowResizeCompleted) {
+ // Create a mouse up event, since such an event is not send by carbon to the
+ // application event handler (while a mouse down <b>is</b> on kEventWindowResizeStarted)
+ EventRef mouseUpEvent;
+ CreateEvent(0, kEventClassMouse, kEventMouseUp, 0, kEventAttributeUserEvent, &mouseUpEvent);
+ UInt16 mbutton = kEventMouseButtonPrimary;
+ SetEventParameter(mouseUpEvent, kEventParamMouseButton, typeMouseButton, sizeof(mbutton), &mbutton);
+ WindowRef window;
+ GetEventParameter(event, kEventParamDirectObject, typeWindowRef, 0, sizeof(window), 0, &window);
+ Rect dragRect;
+ GetWindowBounds(window, kWindowGrowRgn, &dragRect);
+ Point pos = {dragRect.bottom, dragRect.right};
+ SetEventParameter(mouseUpEvent, kEventParamMouseLocation, typeQDPoint, sizeof(pos), &pos);
+ SendEventToApplication(mouseUpEvent);
+ ReleaseEvent(mouseUpEvent);
+ } else if(ekind == kEventWindowBoundsChanging) {
+ UInt32 flags = 0;
+ GetEventParameter(event, kEventParamAttributes, typeUInt32, 0,
+ sizeof(flags), 0, &flags);
+ Rect nr;
+ GetEventParameter(event, kEventParamCurrentBounds, typeQDRectangle, 0,
+ sizeof(nr), 0, &nr);
+
+ QRect newRect(nr.left, nr.top, nr.right - nr.left, nr.bottom - nr.top);
+
+ QTLWExtra * const tlwExtra = widget->d_func()->maybeTopData();
+ if (tlwExtra && tlwExtra->isSetGeometry == 1) {
+ widget->d_func()->setGeometry_sys_helper(newRect.left(), newRect.top(), newRect.width(), newRect.height(), tlwExtra->isMove);
+ } else {
+ //implicitly removes the maximized bit
+ if((widget->data->window_state & Qt::WindowMaximized) &&
+ IsWindowInStandardState(wid, 0, 0)) {
+ widget->data->window_state &= ~Qt::WindowMaximized;
+ QWindowStateChangeEvent e(Qt::WindowStates(widget->data->window_state
+ | Qt::WindowMaximized));
+ QApplication::sendSpontaneousEvent(widget, &e);
+
+ }
+
+ handled_event = false;
+ const QRect oldRect = widget->data->crect;
+ if((flags & kWindowBoundsChangeOriginChanged)) {
+ if(nr.left != oldRect.x() || nr.top != oldRect.y()) {
+ widget->data->crect.moveTo(nr.left, nr.top);
+ QMoveEvent qme(widget->data->crect.topLeft(), oldRect.topLeft());
+ QApplication::sendSpontaneousEvent(widget, &qme);
+ }
+ }
+ if((flags & kWindowBoundsChangeSizeChanged)) {
+ if (widget->isWindow()) {
+ QSize newSize = QLayout::closestAcceptableSize(widget, newRect.size());
+ int dh = newSize.height() - newRect.height();
+ int dw = newSize.width() - newRect.width();
+ if (dw != 0 || dh != 0) {
+ handled_event = true; // We want to change the bounds, so we handle the event
+
+ // set the rect, so we can also do the resize down below (yes, we need to resize).
+ newRect.setBottom(newRect.bottom() + dh);
+ newRect.setRight(newRect.right() + dw);
+
+ nr.left = newRect.x();
+ nr.top = newRect.y();
+ nr.right = nr.left + newRect.width();
+ nr.bottom = nr.top + newRect.height();
+ SetEventParameter(event, kEventParamCurrentBounds, typeQDRectangle, sizeof(Rect), &nr);
+ }
+ }
+
+ if (oldRect.width() != newRect.width() || oldRect.height() != newRect.height()) {
+ widget->data->crect.setSize(newRect.size());
+ HIRect bounds = CGRectMake(0, 0, newRect.width(), newRect.height());
+
+ // If the WA_StaticContents attribute is set we can optimize the resize
+ // by only repainting the newly exposed area. We do this by disabling
+ // painting when setting the size of the view. The OS will invalidate
+ // the newly exposed area for us.
+ const bool staticContents = widget->testAttribute(Qt::WA_StaticContents);
+ const HIViewRef view = qt_mac_nativeview_for(widget);
+ if (staticContents)
+ HIViewSetDrawingEnabled(view, false);
+ HIViewSetFrame(view, &bounds);
+ if (staticContents)
+ HIViewSetDrawingEnabled(view, true);
+
+ QResizeEvent qre(newRect.size(), oldRect.size());
+ QApplication::sendSpontaneousEvent(widget, &qre);
+ qt_event_request_window_change(widget);
+ }
+ }
+ }
+ } else if (ekind == kEventWindowHidden) {
+ // Make sure that we also hide any visible sheets on our window.
+ // Cocoa does the right thing for us.
+ const QObjectList children = widget->children();
+ const int childCount = children.count();
+ for (int i = 0; i < childCount; ++i) {
+ QObject *obj = children.at(i);
+ if (obj->isWidgetType()) {
+ QWidget *widget = static_cast<QWidget *>(obj);
+ if (qt_mac_is_macsheet(widget) && widget->isVisible())
+ widget->hide();
+ }
+ }
+ } else {
+ handled_event = false;
+ }
+ break; }
+ case kEventClassMouse: {
+#if 0
+ return SendEventToApplication(event);
+#endif
+
+ bool send_to_app = false;
+ {
+ WindowPartCode wpc;
+ if (GetEventParameter(event, kEventParamWindowPartCode, typeWindowPartCode, 0,
+ sizeof(wpc), 0, &wpc) == noErr && wpc != inContent)
+ send_to_app = true;
+ }
+ if(!send_to_app) {
+ WindowRef window;
+ if(GetEventParameter(event, kEventParamWindowRef, typeWindowRef, 0,
+ sizeof(window), 0, &window) == noErr) {
+ HIViewRef hiview;
+ if(HIViewGetViewForMouseEvent(HIViewGetRoot(window), event, &hiview) == noErr) {
+ if(QWidget *w = QWidget::find((WId)hiview)) {
+#if 0
+ send_to_app = !w->isActiveWindow();
+#else
+ Q_UNUSED(w);
+ send_to_app = true;
+#endif
+ }
+ }
+ }
+ }
+ if(send_to_app)
+ return SendEventToApplication(event);
+ handled_event = false;
+ break; }
+
+#ifndef QT_NO_GESTURES
+ case kEventClassGesture: {
+ // First, find the widget that was under
+ // the mouse when the gesture happened:
+ HIPoint screenLocation;
+ if (GetEventParameter(event, kEventParamMouseLocation, typeHIPoint, 0,
+ sizeof(screenLocation), 0, &screenLocation) != noErr) {
+ handled_event = false;
+ break;
+ }
+ QWidget *widget = QApplication::widgetAt(screenLocation.x, screenLocation.y);
+ if (!widget) {
+ handled_event = false;
+ break;
+ }
+
+ QNativeGestureEvent qNGEvent;
+ qNGEvent.position = QPoint(screenLocation.x, screenLocation.y);
+
+ switch (ekind) {
+ case kEventGestureStarted:
+ qNGEvent.gestureType = QNativeGestureEvent::GestureBegin;
+ break;
+ case kEventGestureEnded:
+ qNGEvent.gestureType = QNativeGestureEvent::GestureEnd;
+ break;
+ case kEventGestureRotate: {
+ CGFloat amount;
+ if (GetEventParameter(event, kEventParamRotationAmount, 'cgfl', 0,
+ sizeof(amount), 0, &amount) != noErr) {
+ handled_event = false;
+ break;
+ }
+ qNGEvent.gestureType = QNativeGestureEvent::Rotate;
+ qNGEvent.percentage = float(-amount);
+ break; }
+ case kEventGestureSwipe: {
+ HIPoint swipeDirection;
+ if (GetEventParameter(event, kEventParamSwipeDirection, typeHIPoint, 0,
+ sizeof(swipeDirection), 0, &swipeDirection) != noErr) {
+ handled_event = false;
+ break;
+ }
+ qNGEvent.gestureType = QNativeGestureEvent::Swipe;
+ if (swipeDirection.x == 1)
+ qNGEvent.angle = 180.0f;
+ else if (swipeDirection.x == -1)
+ qNGEvent.angle = 0.0f;
+ else if (swipeDirection.y == 1)
+ qNGEvent.angle = 90.0f;
+ else if (swipeDirection.y == -1)
+ qNGEvent.angle = 270.0f;
+ break; }
+ case kEventGestureMagnify: {
+ CGFloat amount;
+ if (GetEventParameter(event, kEventParamMagnificationAmount, 'cgfl', 0,
+ sizeof(amount), 0, &amount) != noErr) {
+ handled_event = false;
+ break;
+ }
+ qNGEvent.gestureType = QNativeGestureEvent::Zoom;
+ qNGEvent.percentage = float(amount);
+ break; }
+ }
+
+ QApplication::sendSpontaneousEvent(widget, &qNGEvent);
+ break; }
+#endif // QT_NO_GESTURES
+
+ default:
+ handled_event = false;
+ }
+ if(!handled_event) //let the event go through
+ return eventNotHandledErr;
+ return noErr; //we eat the event
+}
+
+// widget events
+static HIObjectClassRef widget_class = 0;
+static EventTypeSpec widget_events[] = {
+ { kEventClassHIObject, kEventHIObjectConstruct },
+ { kEventClassHIObject, kEventHIObjectDestruct },
+
+ { kEventClassControl, kEventControlDraw },
+ { kEventClassControl, kEventControlInitialize },
+ { kEventClassControl, kEventControlGetPartRegion },
+ { kEventClassControl, kEventControlGetClickActivation },
+ { kEventClassControl, kEventControlSetFocusPart },
+ { kEventClassControl, kEventControlDragEnter },
+ { kEventClassControl, kEventControlDragWithin },
+ { kEventClassControl, kEventControlDragLeave },
+ { kEventClassControl, kEventControlDragReceive },
+ { kEventClassControl, kEventControlOwningWindowChanged },
+ { kEventClassControl, kEventControlBoundsChanged },
+ { kEventClassControl, kEventControlGetSizeConstraints },
+ { kEventClassControl, kEventControlVisibilityChanged },
+
+ { kEventClassMouse, kEventMouseDown },
+ { kEventClassMouse, kEventMouseUp },
+ { kEventClassMouse, kEventMouseMoved },
+ { kEventClassMouse, kEventMouseDragged }
+};
+static EventHandlerUPP mac_widget_eventUPP = 0;
+static void cleanup_widget_eventUPP()
+{
+ DisposeEventHandlerUPP(mac_widget_eventUPP);
+ mac_widget_eventUPP = 0;
+}
+static const EventHandlerUPP make_widget_eventUPP()
+{
+ if(mac_widget_eventUPP)
+ return mac_widget_eventUPP;
+ qAddPostRoutine(cleanup_widget_eventUPP);
+ return mac_widget_eventUPP = NewEventHandlerUPP(QWidgetPrivate::qt_widget_event);
+}
+OSStatus QWidgetPrivate::qt_widget_event(EventHandlerCallRef er, EventRef event, void *)
+{
+ QScopedLoopLevelCounter loopLevelCounter(QApplicationPrivate::instance()->threadData);
+
+ bool handled_event = true;
+ UInt32 ekind = GetEventKind(event), eclass = GetEventClass(event);
+ switch(eclass) {
+ case kEventClassHIObject: {
+ HIViewRef view = 0;
+ GetEventParameter(event, kEventParamHIObjectInstance, typeHIObjectRef,
+ 0, sizeof(view), 0, &view);
+ if(ekind == kEventHIObjectConstruct) {
+ if(view) {
+ HIViewChangeFeatures(view, kHIViewAllowsSubviews, 0);
+ SetEventParameter(event, kEventParamHIObjectInstance,
+ typeVoidPtr, sizeof(view), &view);
+ }
+ } else if(ekind == kEventHIObjectDestruct) {
+ //nothing to really do.. or is there?
+ } else {
+ handled_event = false;
+ }
+ break; }
+ case kEventClassControl: {
+ QWidget *widget = 0;
+ HIViewRef hiview = 0;
+ if(GetEventParameter(event, kEventParamDirectObject, typeControlRef,
+ 0, sizeof(hiview), 0, &hiview) == noErr)
+ widget = QWidget::find((WId)hiview);
+ if (widget && widget->macEvent(er, event))
+ return noErr;
+ if(ekind == kEventControlDraw) {
+ if(widget && qt_isGenuineQWidget(hiview)) {
+
+ // if there is a window change event pending for any gl child wigets,
+ // send it immediately. (required for flicker-free resizing)
+ extern void qt_mac_send_posted_gl_updates(QWidget *widget);
+ qt_mac_send_posted_gl_updates(widget);
+
+ if (QApplicationPrivate::graphicsSystem() && !widget->d_func()->paintOnScreen()) {
+ widget->d_func()->syncBackingStore();
+ widget->d_func()->dirtyOnWidget = QRegion();
+ return noErr;
+ }
+
+ //requested rgn
+ RgnHandle rgn;
+ GetEventParameter(event, kEventParamRgnHandle, typeQDRgnHandle, 0, sizeof(rgn), 0, &rgn);
+ QRegion qrgn(qt_mac_convert_mac_region(rgn));
+
+ //update handles
+ GrafPtr qd = 0;
+ CGContextRef cg = 0;
+ if(GetEventParameter(event, kEventParamCGContextRef, typeCGContextRef, 0, sizeof(cg), 0, &cg) != noErr) {
+ Q_ASSERT(false);
+ }
+ widget->d_func()->hd = cg;
+ widget->d_func()->qd_hd = qd;
+ CGContextSaveGState(cg);
+
+#ifdef DEBUG_WIDGET_PAINT
+ const bool doDebug = true;
+ if(doDebug) {
+ qDebug("asked to draw %p[%p] [%s::%s] %p[%p] [%d] [%dx%d]", widget, hiview, widget->metaObject()->className(),
+ widget->objectName().local8Bit().data(), widget->parentWidget(),
+ (HIViewRef)(widget->parentWidget() ? qt_mac_nativeview_for(widget->parentWidget()) : (HIViewRef)0),
+ HIViewIsCompositingEnabled(hiview), qt_mac_posInWindow(widget).x(), qt_mac_posInWindow(widget).y());
+#if 0
+ QVector<QRect> region_rects = qrgn.rects();
+ qDebug("Region! %d", region_rects.count());
+ for(int i = 0; i < region_rects.count(); i++)
+ qDebug("%d %d %d %d", region_rects[i].x(), region_rects[i].y(),
+ region_rects[i].width(), region_rects[i].height());
+ region_rects = widget->d_func()->clp.rects();
+ qDebug("Widget Region! %d", region_rects.count());
+ for(int i = 0; i < region_rects.count(); i++)
+ qDebug("%d %d %d %d", region_rects[i].x(), region_rects[i].y(),
+ region_rects[i].width(), region_rects[i].height());
+#endif
+ }
+#endif
+ if (widget->isVisible() && widget->updatesEnabled()) { //process the actual paint event.
+ if(widget->testAttribute(Qt::WA_WState_InPaintEvent))
+ qWarning("QWidget::repaint: Recursive repaint detected");
+ if (widget->isWindow() && !widget->d_func()->isOpaque
+ && !widget->testAttribute(Qt::WA_MacBrushedMetal)) {
+ QRect qrgnRect = qrgn.boundingRect();
+ CGContextClearRect(cg, CGRectMake(qrgnRect.x(), qrgnRect.y(), qrgnRect.width(), qrgnRect.height()));
+ }
+
+ QPoint redirectionOffset(0, 0);
+ QWidget *tl = widget->window();
+ if (tl) {
+ Qt::WindowFlags flags = tl->windowFlags();
+ if (flags & Qt::FramelessWindowHint
+ || (flags & Qt::CustomizeWindowHint && !(flags & Qt::WindowTitleHint))) {
+ if(tl->d_func()->extra && !tl->d_func()->extra->mask.isEmpty())
+ redirectionOffset += tl->d_func()->extra->mask.boundingRect().topLeft();
+ }
+ }
+
+ //setup the context
+ widget->setAttribute(Qt::WA_WState_InPaintEvent);
+ QPaintEngine *engine = widget->paintEngine();
+ if (engine)
+ engine->setSystemClip(qrgn);
+
+ //handle the erase
+ if (engine && (!widget->testAttribute(Qt::WA_NoSystemBackground)
+ && (widget->isWindow() || widget->autoFillBackground())
+ || widget->testAttribute(Qt::WA_TintedBackground)
+ || widget->testAttribute(Qt::WA_StyledBackground))) {
+#ifdef DEBUG_WIDGET_PAINT
+ if(doDebug)
+ qDebug(" Handling erase for [%s::%s]", widget->metaObject()->className(),
+ widget->objectName().local8Bit().data());
+#endif
+ if (!redirectionOffset.isNull())
+ widget->d_func()->setRedirected(widget, redirectionOffset);
+
+ bool was_unclipped = widget->testAttribute(Qt::WA_PaintUnclipped);
+ widget->setAttribute(Qt::WA_PaintUnclipped, false);
+ QPainter p(widget);
+ p.setClipping(false);
+ if(was_unclipped)
+ widget->setAttribute(Qt::WA_PaintUnclipped);
+ widget->d_func()->paintBackground(&p, qrgn, widget->isWindow() ? DrawAsRoot : 0);
+ if (widget->testAttribute(Qt::WA_TintedBackground)) {
+ QColor tint = widget->palette().window().color();
+ tint.setAlphaF(.6);
+ const QVector<QRect> &rects = qrgn.rects();
+ for (int i = 0; i < rects.size(); ++i)
+ p.fillRect(rects.at(i), tint);
+ }
+ p.end();
+ if (!redirectionOffset.isNull())
+ widget->d_func()->restoreRedirected();
+ }
+
+ if(!HIObjectIsOfClass((HIObjectRef)hiview, kObjectQWidget))
+ CallNextEventHandler(er, event);
+
+ //send the paint
+ redirectionOffset += widget->data->wrect.topLeft(); // Map from system to qt coordinates
+ if (!redirectionOffset.isNull())
+ widget->d_func()->setRedirected(widget, redirectionOffset);
+ qrgn.translate(redirectionOffset);
+ QPaintEvent e(qrgn);
+ widget->d_func()->dirtyOnWidget = QRegion();
+#ifdef QT3_SUPPORT
+ e.setErased(true);
+#endif
+ QApplication::sendSpontaneousEvent(widget, &e);
+ if (!redirectionOffset.isNull())
+ widget->d_func()->restoreRedirected();
+
+ //cleanup
+ if (engine)
+ engine->setSystemClip(QRegion());
+
+ widget->setAttribute(Qt::WA_WState_InPaintEvent, false);
+ if(!widget->testAttribute(Qt::WA_PaintOutsidePaintEvent) && widget->paintingActive())
+ qWarning("QWidget: It is dangerous to leave painters active on a widget outside of the PaintEvent");
+ }
+
+ widget->d_func()->hd = 0;
+ widget->d_func()->qd_hd = 0;
+ CGContextRestoreGState(cg);
+ } else if(!HIObjectIsOfClass((HIObjectRef)hiview, kObjectQWidget)) {
+ CallNextEventHandler(er, event);
+ }
+ } else if(ekind == kEventControlInitialize) {
+ if(HIObjectIsOfClass((HIObjectRef)hiview, kObjectQWidget)) {
+ UInt32 features = kControlSupportsDragAndDrop | kControlSupportsClickActivation | kControlSupportsFocus;
+ SetEventParameter(event, kEventParamControlFeatures, typeUInt32, sizeof(features), &features);
+ } else {
+ handled_event = false;
+ }
+ } else if(ekind == kEventControlSetFocusPart) {
+ if(widget) {
+ ControlPartCode part;
+ GetEventParameter(event, kEventParamControlPart, typeControlPartCode, 0,
+ sizeof(part), 0, &part);
+ if(part == kControlFocusNoPart){
+ if (widget->hasFocus())
+ QApplicationPrivate::setFocusWidget(0, Qt::OtherFocusReason);
+ } else
+ widget->setFocus();
+ }
+ if(!HIObjectIsOfClass((HIObjectRef)hiview, kObjectQWidget))
+ CallNextEventHandler(er, event);
+ } else if(ekind == kEventControlGetClickActivation) {
+ ClickActivationResult clickT = kActivateAndIgnoreClick;
+ SetEventParameter(event, kEventParamClickActivation, typeClickActivationResult,
+ sizeof(clickT), &clickT);
+ } else if(ekind == kEventControlGetPartRegion) {
+ handled_event = false;
+ if(!HIObjectIsOfClass((HIObjectRef)hiview, kObjectQWidget) && CallNextEventHandler(er, event) == noErr) {
+ handled_event = true;
+ break;
+ }
+ if(widget && !widget->isWindow()) {
+ ControlPartCode part;
+ GetEventParameter(event, kEventParamControlPart, typeControlPartCode, 0,
+ sizeof(part), 0, &part);
+ if(part == kControlClickableMetaPart && widget->testAttribute(Qt::WA_TransparentForMouseEvents)) {
+ RgnHandle rgn;
+ GetEventParameter(event, kEventParamControlRegion, typeQDRgnHandle, 0,
+ sizeof(rgn), 0, &rgn);
+ SetEmptyRgn(rgn);
+ handled_event = true;
+ } else if(part == kControlStructureMetaPart || part == kControlClickableMetaPart) {
+ RgnHandle rgn;
+ GetEventParameter(event, kEventParamControlRegion, typeQDRgnHandle, 0,
+ sizeof(rgn), 0, &rgn);
+ SetRectRgn(rgn, 0, 0, widget->width(), widget->height());
+ if(QWidgetPrivate::qt_widget_rgn(widget, kWindowStructureRgn, rgn, false))
+ handled_event = true;
+ } else if(part == kControlOpaqueMetaPart) {
+ if(widget->d_func()->isOpaque) {
+ RgnHandle rgn;
+ GetEventParameter(event, kEventParamControlRegion, typeQDRgnHandle, 0,
+ sizeof(RgnHandle), 0, &rgn);
+ SetRectRgn(rgn, 0, 0, widget->width(), widget->height());
+ QWidgetPrivate::qt_widget_rgn(widget, kWindowStructureRgn, rgn, false);
+ SetEventParameter(event, kEventParamControlRegion, typeQDRgnHandle,
+ sizeof(RgnHandle), &rgn);
+ handled_event = true;
+ }
+ }
+ }
+ } else if(ekind == kEventControlOwningWindowChanged) {
+ if(!HIObjectIsOfClass((HIObjectRef)hiview, kObjectQWidget))
+ CallNextEventHandler(er, event);
+ if(widget && qt_mac_window_for(hiview)) {
+ WindowRef foo = 0;
+ GetEventParameter(event, kEventParamControlCurrentOwningWindow, typeWindowRef, 0,
+ sizeof(foo), 0, &foo);
+ widget->d_func()->initWindowPtr();
+ }
+ if (widget)
+ qt_event_request_window_change(widget);
+ } else if(ekind == kEventControlDragEnter || ekind == kEventControlDragWithin ||
+ ekind == kEventControlDragLeave || ekind == kEventControlDragReceive) {
+ // dnd are really handled in qdnd_mac.cpp,
+ // just modularize the code a little...
+ DragRef drag;
+ GetEventParameter(event, kEventParamDragRef, typeDragRef, 0, sizeof(drag), 0, &drag);
+ handled_event = false;
+ bool drag_allowed = false;
+
+ QWidget *dropWidget = widget;
+ if (qobject_cast<QFocusFrame *>(widget)){
+ // We might shadow widgets underneath the focus
+ // frame, so stay interrested, and let the dnd through
+ drag_allowed = true;
+ handled_event = true;
+ Point where;
+ GetDragMouse(drag, &where, 0);
+ dropWidget = QApplication::widgetAt(QPoint(where.h, where.v));
+
+ if (dropWidget != QDragManager::self()->currentTarget()) {
+ // We have to 'fake' enter and leave events for the shaddowed widgets:
+ if (ekind == kEventControlDragEnter) {
+ if (QDragManager::self()->currentTarget())
+ QDragManager::self()->currentTarget()->d_func()->qt_mac_dnd_event(kEventControlDragLeave, drag);
+ if (dropWidget) {
+ dropWidget->d_func()->qt_mac_dnd_event(kEventControlDragEnter, drag);
+ }
+ // Set dropWidget to zero, so qt_mac_dnd_event
+ // doesn't get called a second time below:
+ dropWidget = 0;
+ } else if (ekind == kEventControlDragLeave) {
+ dropWidget = QDragManager::self()->currentTarget();
+ if (dropWidget) {
+ dropWidget->d_func()->qt_mac_dnd_event(kEventControlDragLeave, drag);
+ }
+ // Set dropWidget to zero, so qt_mac_dnd_event
+ // doesn't get called a second time below:
+ dropWidget = 0;
+ }
+ }
+ }
+
+ // Send the dnd event to the widget:
+ if (dropWidget && dropWidget->d_func()->qt_mac_dnd_event(ekind, drag)) {
+ drag_allowed = true;
+ handled_event = true;
+ }
+
+ if (ekind == kEventControlDragEnter) {
+ // If we don't accept the enter event, we will
+ // receive no more drag events for this widget
+ const Boolean wouldAccept = drag_allowed ? true : false;
+ SetEventParameter(event, kEventParamControlWouldAcceptDrop, typeBoolean,
+ sizeof(wouldAccept), &wouldAccept);
+ }
+ } else if (ekind == kEventControlBoundsChanged) {
+ if (!widget || widget->isWindow() || widget->testAttribute(Qt::WA_Moved) || widget->testAttribute(Qt::WA_Resized)) {
+ handled_event = false;
+ } else {
+ // Sync our view in case some other (non-Qt) view is controlling us.
+ handled_event = true;
+ Rect newBounds;
+ GetEventParameter(event, kEventParamCurrentBounds,
+ typeQDRectangle, 0, sizeof(Rect), 0, &newBounds);
+ QRect rect(newBounds.left, newBounds.top,
+ newBounds.right - newBounds.left, newBounds.bottom - newBounds.top);
+
+ bool moved = widget->testAttribute(Qt::WA_Moved);
+ bool resized = widget->testAttribute(Qt::WA_Resized);
+ widget->setGeometry(rect);
+ widget->setAttribute(Qt::WA_Moved, moved);
+ widget->setAttribute(Qt::WA_Resized, resized);
+ qt_event_request_window_change(widget);
+ }
+ } else if (ekind == kEventControlGetSizeConstraints) {
+ if (!widget || !qt_isGenuineQWidget(widget)) {
+ handled_event = false;
+ } else {
+ handled_event = true;
+ QWidgetItem item(widget);
+ QSize size = item.minimumSize();
+ HISize hisize = { size.width(), size.height() };
+ SetEventParameter(event, kEventParamMinimumSize, typeHISize, sizeof(HISize), &hisize);
+ size = item.maximumSize();
+ hisize.width = size.width() + 2; // ### shouldn't have to add 2 (but it works).
+ hisize.height = size.height();
+ SetEventParameter(event, kEventParamMaximumSize, typeHISize, sizeof(HISize), &hisize);
+ }
+ } else if (ekind == kEventControlVisibilityChanged) {
+ handled_event = false;
+ if (widget) {
+ qt_event_request_window_change(widget);
+ if (!HIViewIsVisible(HIViewRef(widget->winId()))) {
+ if (widget == qt_button_down)
+ qt_button_down = 0;
+ }
+ }
+ }
+ break; }
+ case kEventClassMouse: {
+ bool send_to_app = false;
+ if(qt_button_down)
+ send_to_app = true;
+ if(send_to_app) {
+ OSStatus err = SendEventToApplication(event);
+ if(err != noErr)
+ handled_event = false;
+ } else {
+ CallNextEventHandler(er, event);
+ }
+ break; }
+ default:
+ handled_event = false;
+ break;
+ }
+ if(!handled_event) //let the event go through
+ return eventNotHandledErr;
+ return noErr; //we eat the event
+}
+#endif
+
+OSViewRef qt_mac_create_widget(QWidget *widget, QWidgetPrivate *widgetPrivate, OSViewRef parent)
+{
+#ifdef QT_MAC_USE_COCOA
+ QMacCocoaAutoReleasePool pool;
+ QT_MANGLE_NAMESPACE(QCocoaView) *view = [[QT_MANGLE_NAMESPACE(QCocoaView) alloc] initWithQWidget:widget widgetPrivate:widgetPrivate];
+
+#ifdef ALIEN_DEBUG
+ qDebug() << "Creating NSView for" << widget;
+#endif
+
+ if (view && parent)
+ [parent addSubview:view];
+ return view;
+#else
+ Q_UNUSED(widget);
+ Q_UNUSED(widgetPrivate);
+ if(!widget_class) {
+ OSStatus err = HIObjectRegisterSubclass(kObjectQWidget, kHIViewClassID, 0, make_widget_eventUPP(),
+ GetEventTypeCount(widget_events), widget_events,
+ 0, &widget_class);
+ if (err && err != hiObjectClassExistsErr)
+ qWarning("QWidget: Internal error (%d)", __LINE__);
+ }
+ HIViewRef ret = 0;
+ if(HIObjectCreate(kObjectQWidget, 0, (HIObjectRef*)&ret) != noErr)
+ qWarning("QWidget: Internal error (%d)", __LINE__);
+ if(ret && parent)
+ HIViewAddSubview(parent, ret);
+ return ret;
+#endif
+}
+
+void qt_mac_unregister_widget()
+{
+#ifndef QT_MAC_USE_COCOA
+ HIObjectUnregisterClass(widget_class);
+ widget_class = 0;
+#endif
+}
+
+void QWidgetPrivate::toggleDrawers(bool visible)
+{
+ for (int i = 0; i < children.size(); ++i) {
+ register QObject *object = children.at(i);
+ if (!object->isWidgetType())
+ continue;
+ QWidget *widget = static_cast<QWidget*>(object);
+ if(qt_mac_is_macdrawer(widget)) {
+ bool oldState = widget->testAttribute(Qt::WA_WState_ExplicitShowHide);
+ if(visible) {
+ if (!widget->testAttribute(Qt::WA_WState_ExplicitShowHide))
+ widget->show();
+ } else {
+ widget->hide();
+ if(!oldState)
+ widget->setAttribute(Qt::WA_WState_ExplicitShowHide, false);
+ }
+ }
+ }
+}
+
+/*****************************************************************************
+ QWidgetPrivate member functions
+ *****************************************************************************/
+bool QWidgetPrivate::qt_mac_update_sizer(QWidget *w, int up)
+{
+ // I'm not sure what "up" is
+ if(!w || !w->isWindow())
+ return false;
+
+ QTLWExtra *topData = w->d_func()->topData();
+ QWExtra *extraData = w->d_func()->extraData();
+ // topData->resizer is only 4 bits, so subtracting -1 from zero causes bad stuff
+ // to happen, prevent that here (you really want the thing hidden).
+ if (up >= 0 || topData->resizer != 0)
+ topData->resizer += up;
+ OSWindowRef windowRef = qt_mac_window_for(OSViewRef(w->effectiveWinId()));
+ {
+#ifndef QT_MAC_USE_COCOA
+ WindowClass wclass;
+ GetWindowClass(windowRef, &wclass);
+ if(!(GetAvailableWindowAttributes(wclass) & kWindowResizableAttribute))
+ return true;
+#endif
+ }
+ bool remove_grip = (topData->resizer || (w->windowFlags() & Qt::FramelessWindowHint)
+ || (extraData->maxw && extraData->maxh &&
+ extraData->maxw == extraData->minw && extraData->maxh == extraData->minh));
+#ifndef QT_MAC_USE_COCOA
+ WindowAttributes attr;
+ GetWindowAttributes(windowRef, &attr);
+ if(remove_grip) {
+ if(attr & kWindowResizableAttribute) {
+ ChangeWindowAttributes(qt_mac_window_for(w), kWindowNoAttributes,
+ kWindowResizableAttribute);
+ ReshapeCustomWindow(qt_mac_window_for(w));
+ }
+ } else if(!(attr & kWindowResizableAttribute)) {
+ ChangeWindowAttributes(windowRef, kWindowResizableAttribute,
+ kWindowNoAttributes);
+ ReshapeCustomWindow(windowRef);
+ }
+#else
+ [windowRef setShowsResizeIndicator:!remove_grip];
+#endif
+ return true;
+}
+
+void QWidgetPrivate::qt_clean_root_win()
+{
+#ifdef QT_MAC_USE_COCOA
+ QMacCocoaAutoReleasePool pool;
+ [qt_root_win release];
+#else
+ if(!qt_root_win)
+ return;
+ CFRelease(qt_root_win);
+#endif
+ qt_root_win = 0;
+}
+
+bool QWidgetPrivate::qt_create_root_win()
+{
+ if(qt_root_win)
+ return false;
+ const QSize desktopSize = qt_mac_desktopSize();
+ QRect desktopRect(QPoint(0, 0), desktopSize);
+#ifdef QT_MAC_USE_COCOA
+ qt_root_win = qt_mac_create_window(0, kOverlayWindowClass, NSBorderlessWindowMask, desktopRect);
+#else
+ WindowAttributes wattr = (kWindowCompositingAttribute | kWindowStandardHandlerAttribute);
+ qt_root_win = qt_mac_create_window(0, kOverlayWindowClass, wattr, desktopRect);
+#endif
+ if(!qt_root_win)
+ return false;
+ qAddPostRoutine(qt_clean_root_win);
+ return true;
+}
+
+bool QWidgetPrivate::qt_widget_rgn(QWidget *widget, short wcode, RgnHandle rgn, bool force = false)
+{
+ bool ret = false;
+#ifndef QT_MAC_USE_COCOA
+ switch(wcode) {
+ case kWindowStructureRgn: {
+ if(widget) {
+ if(widget->d_func()->extra && !widget->d_func()->extra->mask.isEmpty()) {
+ QRegion rin = qt_mac_convert_mac_region(rgn);
+ if(!rin.isEmpty()) {
+ QPoint rin_tl = rin.boundingRect().topLeft(); //in offset
+ rin.translate(-rin_tl.x(), -rin_tl.y()); //bring into same space as below
+ QRegion mask = widget->d_func()->extra->mask;
+ Qt::WindowFlags flags = widget->windowFlags();
+ if(widget->isWindow()
+ && !(flags & Qt::FramelessWindowHint
+ || (flags & Qt::CustomizeWindowHint && !(flags & Qt::WindowTitleHint)))) {
+ QRegion title;
+ {
+ QMacSmartQuickDrawRegion rgn(qt_mac_get_rgn());
+ GetWindowRegion(qt_mac_window_for(widget), kWindowTitleBarRgn, rgn);
+ title = qt_mac_convert_mac_region(rgn);
+ }
+ QRect br = title.boundingRect();
+ mask.translate(0, br.height()); //put the mask 'under' the title bar..
+ title.translate(-br.x(), -br.y());
+ mask += title;
+ }
+
+ QRegion cr = rin & mask;
+ cr.translate(rin_tl.x(), rin_tl.y()); //translate back to incoming space
+ CopyRgn(QMacSmartQuickDrawRegion(cr.toQDRgn()), rgn);
+ }
+ ret = true;
+ } else if(force) {
+ QRegion cr(widget->geometry());
+ CopyRgn(QMacSmartQuickDrawRegion(cr.toQDRgn()), rgn);
+ ret = true;
+ }
+ }
+ break; }
+ default: break;
+ }
+ //qDebug() << widget << ret << wcode << qt_mac_convert_mac_region(rgn);
+#else
+ Q_UNUSED(widget);
+ Q_UNUSED(wcode);
+ Q_UNUSED(rgn);
+ Q_UNUSED(force);
+#endif
+ return ret;
+}
+
+/*****************************************************************************
+ QWidget member functions
+ *****************************************************************************/
+void QWidgetPrivate::determineWindowClass()
+{
+ Q_Q(QWidget);
+#if !defined(QT_NO_MAINWINDOW) && !defined(QT_NO_TOOLBAR)
+ // Make sure that QMainWindow has the MacWindowToolBarButtonHint when the
+ // unifiedTitleAndToolBarOnMac property is ON. This is to avoid reentry of
+ // setParent() triggered by the QToolBar::event(QEvent::ParentChange).
+ QMainWindow *mainWindow = qobject_cast<QMainWindow *>(q);
+ if (mainWindow && mainWindow->unifiedTitleAndToolBarOnMac()) {
+ data.window_flags |= Qt::MacWindowToolBarButtonHint;
+ }
+#endif
+#ifndef QT_MAC_USE_COCOA
+// ### COCOA:Interleave these better!
+
+ const Qt::WindowType type = q->windowType();
+ Qt::WindowFlags &flags = data.window_flags;
+ const bool popup = (type == Qt::Popup);
+ if (type == Qt::ToolTip || type == Qt::SplashScreen || popup)
+ flags |= Qt::FramelessWindowHint;
+
+ WindowClass wclass = kSheetWindowClass;
+ if(qt_mac_is_macdrawer(q))
+ wclass = kDrawerWindowClass;
+ else if (q->testAttribute(Qt::WA_ShowModal) && flags & Qt::CustomizeWindowHint)
+ wclass = kDocumentWindowClass;
+ else if(popup || (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5 && type == Qt::SplashScreen))
+ wclass = kModalWindowClass;
+ else if(q->testAttribute(Qt::WA_ShowModal))
+ wclass = kMovableModalWindowClass;
+ else if(type == Qt::ToolTip)
+ wclass = kHelpWindowClass;
+ else if(type == Qt::Tool || (QSysInfo::MacintoshVersion < QSysInfo::MV_10_5
+ && type == Qt::SplashScreen))
+ wclass = kFloatingWindowClass;
+ else
+ wclass = kDocumentWindowClass;
+
+ WindowGroupRef grp = 0;
+ WindowAttributes wattr = (kWindowCompositingAttribute | kWindowStandardHandlerAttribute);
+ if (q->testAttribute(Qt::WA_MacFrameworkScaled))
+ wattr |= kWindowFrameworkScaledAttribute;
+ if(qt_mac_is_macsheet(q)) {
+ //grp = GetWindowGroupOfClass(kMovableModalWindowClass);
+ wclass = kSheetWindowClass;
+ } else {
+ grp = GetWindowGroupOfClass(wclass);
+ // Shift things around a bit to get the correct window class based on the presence
+ // (or lack) of the border.
+ bool customize = flags & Qt::CustomizeWindowHint;
+ bool framelessWindow = (flags & Qt::FramelessWindowHint || (customize && !(flags & Qt::WindowTitleHint)));
+ if (framelessWindow) {
+ if(wclass == kDocumentWindowClass) {
+ wattr |= kWindowNoTitleBarAttribute;
+ } else if(wclass == kFloatingWindowClass) {
+ wattr |= kWindowNoTitleBarAttribute;
+ } else if (wclass == kMovableModalWindowClass) {
+ wclass = kModalWindowClass;
+ }
+ } else {
+ if(wclass != kModalWindowClass)
+ wattr |= kWindowResizableAttribute;
+ }
+ // Only add extra decorations (well, buttons) for widgets that can have them
+ // and have an actual border we can put them on.
+ if(wclass != kModalWindowClass && wclass != kMovableModalWindowClass
+ && wclass != kSheetWindowClass && wclass != kPlainWindowClass
+ && !framelessWindow && wclass != kDrawerWindowClass
+ && wclass != kHelpWindowClass) {
+ if (flags & Qt::WindowMaximizeButtonHint)
+ wattr |= kWindowFullZoomAttribute;
+ if (flags & Qt::WindowMinimizeButtonHint)
+ wattr |= kWindowCollapseBoxAttribute;
+ if (flags & Qt::WindowSystemMenuHint || flags & Qt::WindowCloseButtonHint)
+ wattr |= kWindowCloseBoxAttribute;
+ if (flags & Qt::MacWindowToolBarButtonHint)
+ wattr |= kWindowToolbarButtonAttribute;
+ } else {
+ // Clear these hints so that we aren't call them on invalid windows
+ flags &= ~(Qt::WindowMaximizeButtonHint | Qt::WindowMinimizeButtonHint
+ | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint);
+ }
+ }
+ if((popup || type == Qt::Tool) && !q->isModal())
+ wattr |= kWindowHideOnSuspendAttribute;
+ wattr |= kWindowLiveResizeAttribute;
+
+#ifdef DEBUG_WINDOW_CREATE
+#define ADD_DEBUG_WINDOW_NAME(x) { x, #x }
+ struct {
+ UInt32 tag;
+ const char *name;
+ } known_attribs[] = {
+ ADD_DEBUG_WINDOW_NAME(kWindowCompositingAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowStandardHandlerAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowMetalAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowHideOnSuspendAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowStandardHandlerAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowCollapseBoxAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowHorizontalZoomAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowVerticalZoomAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowResizableAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowNoActivatesAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowNoUpdatesAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowOpaqueForEventsAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowLiveResizeAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowCloseBoxAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowHideOnSuspendAttribute),
+ { 0, 0 }
+ }, known_classes[] = {
+ ADD_DEBUG_WINDOW_NAME(kHelpWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kPlainWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kDrawerWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kUtilityWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kToolbarWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kSheetWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kFloatingWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kUtilityWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kDocumentWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kToolbarWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kMovableModalWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kModalWindowClass),
+ { 0, 0 }
+ };
+ qDebug("Qt: internal: ************* Creating new window %p (%s::%s)", q, q->metaObject()->className(),
+ q->objectName().toLocal8Bit().constData());
+ bool found_class = false;
+ for(int i = 0; known_classes[i].name; i++) {
+ if(wclass == known_classes[i].tag) {
+ found_class = true;
+ qDebug("Qt: internal: ** Class: %s", known_classes[i].name);
+ break;
+ }
+ }
+ if(!found_class)
+ qDebug("Qt: internal: !! Class: Unknown! (%d)", (int)wclass);
+ if(wattr) {
+ WindowAttributes tmp_wattr = wattr;
+ qDebug("Qt: internal: ** Attributes:");
+ for(int i = 0; tmp_wattr && known_attribs[i].name; i++) {
+ if((tmp_wattr & known_attribs[i].tag) == known_attribs[i].tag) {
+ tmp_wattr ^= known_attribs[i].tag;
+ qDebug("Qt: internal: * %s %s", known_attribs[i].name,
+ (GetAvailableWindowAttributes(wclass) & known_attribs[i].tag) ? "" : "(*)");
+ }
+ }
+ if(tmp_wattr)
+ qDebug("Qt: internal: !! Attributes: Unknown (%d)", (int)tmp_wattr);
+ }
+#endif
+
+ /* Just to be extra careful we will change to the kUtilityWindowClass if the
+ requested attributes cannot be used */
+ if((GetAvailableWindowAttributes(wclass) & wattr) != wattr) {
+ WindowClass tmp_class = wclass;
+ if(wclass == kToolbarWindowClass || wclass == kUtilityWindowClass)
+ wclass = kFloatingWindowClass;
+ if(tmp_class != wclass) {
+ if(!grp)
+ grp = GetWindowGroupOfClass(wclass);
+ wclass = tmp_class;
+ }
+ }
+ topData()->wclass = wclass;
+ topData()->wattr = wattr;
+#else
+ const Qt::WindowType type = q->windowType();
+ Qt::WindowFlags &flags = data.window_flags;
+ const bool popup = (type == Qt::Popup);
+ if (type == Qt::ToolTip || type == Qt::SplashScreen || popup)
+ flags |= Qt::FramelessWindowHint;
+
+ WindowClass wclass = kSheetWindowClass;
+ if(qt_mac_is_macdrawer(q))
+ wclass = kDrawerWindowClass;
+ else if (q->testAttribute(Qt::WA_ShowModal) && flags & Qt::CustomizeWindowHint)
+ wclass = kDocumentWindowClass;
+ else if(popup || (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5 && type == Qt::SplashScreen))
+ wclass = kModalWindowClass;
+ else if(type == Qt::Dialog)
+ wclass = kMovableModalWindowClass;
+ else if(type == Qt::ToolTip)
+ wclass = kHelpWindowClass;
+ else if(type == Qt::Tool || (QSysInfo::MacintoshVersion < QSysInfo::MV_10_5
+ && type == Qt::SplashScreen))
+ wclass = kFloatingWindowClass;
+ else if(q->testAttribute(Qt::WA_ShowModal))
+ wclass = kMovableModalWindowClass;
+ else
+ wclass = kDocumentWindowClass;
+
+ WindowAttributes wattr = NSBorderlessWindowMask;
+ if(qt_mac_is_macsheet(q)) {
+ //grp = GetWindowGroupOfClass(kMovableModalWindowClass);
+ wclass = kSheetWindowClass;
+ wattr = NSTitledWindowMask | NSResizableWindowMask;
+ } else {
+#ifndef QT_MAC_USE_COCOA
+ grp = GetWindowGroupOfClass(wclass);
+#endif
+ // Shift things around a bit to get the correct window class based on the presence
+ // (or lack) of the border.
+ bool customize = flags & Qt::CustomizeWindowHint;
+ bool framelessWindow = (flags & Qt::FramelessWindowHint || (customize && !(flags & Qt::WindowTitleHint)));
+ if (framelessWindow) {
+ if (wclass == kDocumentWindowClass) {
+ wclass = kSimpleWindowClass;
+ } else if (wclass == kFloatingWindowClass) {
+ wclass = kToolbarWindowClass;
+ } else if (wclass == kMovableModalWindowClass) {
+ wclass = kModalWindowClass;
+ }
+ } else {
+ wattr |= NSTitledWindowMask;
+ if (wclass != kModalWindowClass)
+ wattr |= NSResizableWindowMask;
+ }
+ // Only add extra decorations (well, buttons) for widgets that can have them
+ // and have an actual border we can put them on.
+ if (wclass != kModalWindowClass
+ && wclass != kSheetWindowClass && wclass != kPlainWindowClass
+ && !framelessWindow && wclass != kDrawerWindowClass
+ && wclass != kHelpWindowClass) {
+ if (flags & Qt::WindowMinimizeButtonHint)
+ wattr |= NSMiniaturizableWindowMask;
+ if (flags & Qt::WindowSystemMenuHint || flags & Qt::WindowCloseButtonHint)
+ wattr |= NSClosableWindowMask;
+ } else {
+ // Clear these hints so that we aren't call them on invalid windows
+ flags &= ~(Qt::WindowMaximizeButtonHint | Qt::WindowMinimizeButtonHint
+ | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint);
+ }
+ }
+ if (q->testAttribute(Qt::WA_MacBrushedMetal))
+ wattr |= NSTexturedBackgroundWindowMask;
+
+#ifdef DEBUG_WINDOW_CREATE
+#define ADD_DEBUG_WINDOW_NAME(x) { x, #x }
+ struct {
+ UInt32 tag;
+ const char *name;
+ } known_attribs[] = {
+ ADD_DEBUG_WINDOW_NAME(kWindowCompositingAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowStandardHandlerAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowMetalAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowHideOnSuspendAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowStandardHandlerAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowCollapseBoxAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowHorizontalZoomAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowVerticalZoomAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowResizableAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowNoActivatesAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowNoUpdatesAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowOpaqueForEventsAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowLiveResizeAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowCloseBoxAttribute),
+ ADD_DEBUG_WINDOW_NAME(kWindowHideOnSuspendAttribute),
+ { 0, 0 }
+ }, known_classes[] = {
+ ADD_DEBUG_WINDOW_NAME(kHelpWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kPlainWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kDrawerWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kUtilityWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kToolbarWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kSheetWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kFloatingWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kUtilityWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kDocumentWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kToolbarWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kMovableModalWindowClass),
+ ADD_DEBUG_WINDOW_NAME(kModalWindowClass),
+ { 0, 0 }
+ };
+ qDebug("Qt: internal: ************* Creating new window %p (%s::%s)", q, q->metaObject()->className(),
+ q->objectName().toLocal8Bit().constData());
+ bool found_class = false;
+ for(int i = 0; known_classes[i].name; i++) {
+ if(wclass == known_classes[i].tag) {
+ found_class = true;
+ qDebug("Qt: internal: ** Class: %s", known_classes[i].name);
+ break;
+ }
+ }
+ if(!found_class)
+ qDebug("Qt: internal: !! Class: Unknown! (%d)", (int)wclass);
+ if(wattr) {
+ WindowAttributes tmp_wattr = wattr;
+ qDebug("Qt: internal: ** Attributes:");
+ for(int i = 0; tmp_wattr && known_attribs[i].name; i++) {
+ if((tmp_wattr & known_attribs[i].tag) == known_attribs[i].tag) {
+ tmp_wattr ^= known_attribs[i].tag;
+ }
+ }
+ if(tmp_wattr)
+ qDebug("Qt: internal: !! Attributes: Unknown (%d)", (int)tmp_wattr);
+ }
+#endif
+
+#ifndef QT_MAC_USE_COCOA
+ /* Just to be extra careful we will change to the kUtilityWindowClass if the
+ requested attributes cannot be used */
+ if((GetAvailableWindowAttributes(wclass) & wattr) != wattr) {
+ WindowClass tmp_class = wclass;
+ if(wclass == kToolbarWindowClass || wclass == kUtilityWindowClass)
+ wclass = kFloatingWindowClass;
+ if(tmp_class != wclass) {
+ if(!grp)
+ grp = GetWindowGroupOfClass(wclass);
+ wclass = tmp_class;
+ }
+ }
+#endif
+#endif
+ topData()->wclass = wclass;
+ topData()->wattr = wattr;
+}
+
+#ifndef QT_MAC_USE_COCOA // This is handled in Cocoa via our category.
+void QWidgetPrivate::initWindowPtr()
+{
+ Q_Q(QWidget);
+ OSWindowRef windowRef = qt_mac_window_for(qt_mac_nativeview_for(q)); //do not create!
+ if(!windowRef)
+ return;
+ QWidget *window = q->window(), *oldWindow = 0;
+ if(GetWindowProperty(windowRef, kWidgetCreatorQt, kWidgetPropertyQWidget, sizeof(oldWindow), 0, &oldWindow) == noErr) {
+ Q_ASSERT(window == oldWindow);
+ return;
+ }
+
+ if(SetWindowProperty(windowRef, kWidgetCreatorQt, kWidgetPropertyQWidget, sizeof(window), &window) != noErr)
+ qWarning("Qt:Internal error (%s:%d)", __FILE__, __LINE__); //no real way to recover
+ if(!q->windowType() != Qt::Desktop) { //setup an event callback handler on the window
+ InstallWindowEventHandler(windowRef, make_win_eventUPP(), GetEventTypeCount(window_events),
+ window_events, static_cast<void *>(qApp), &window_event);
+ }
+}
+
+void QWidgetPrivate::finishCreateWindow_sys_Carbon(OSWindowRef windowRef)
+{
+ Q_Q(QWidget);
+ const Qt::WindowType type = q->windowType();
+ Qt::WindowFlags &flags = data.window_flags;
+ QWidget *parentWidget = q->parentWidget();
+
+ const bool desktop = (type == Qt::Desktop);
+ const bool dialog = (type == Qt::Dialog
+ || type == Qt::Sheet
+ || type == Qt::Drawer
+ || (flags & Qt::MSWindowsFixedSizeDialogHint));
+ QTLWExtra *topExtra = topData();
+ quint32 wattr = topExtra->wattr;
+ if (!desktop)
+ SetAutomaticControlDragTrackingEnabledForWindow(windowRef, true);
+ HIWindowChangeFeatures(windowRef, kWindowCanCollapse, 0);
+ if (wattr & kWindowHideOnSuspendAttribute)
+ HIWindowChangeAvailability(windowRef, kHIWindowExposeHidden, 0);
+ else
+ HIWindowChangeAvailability(windowRef, 0, kHIWindowExposeHidden);
+ if ((flags & Qt::WindowStaysOnTopHint))
+ ChangeWindowAttributes(windowRef, kWindowNoAttributes, kWindowHideOnSuspendAttribute);
+ if (qt_mac_is_macdrawer(q) && parentWidget)
+ SetDrawerParent(windowRef, qt_mac_window_for (parentWidget));
+ if (topExtra->group) {
+ qt_mac_release_window_group(topExtra->group);
+ topExtra->group = 0;
+ }
+ if (type == Qt::ToolTip)
+ qt_mac_set_window_group_to_tooltip(windowRef);
+ else if (type == Qt::Popup && (flags & Qt::WindowStaysOnTopHint))
+ qt_mac_set_window_group_to_popup(windowRef);
+ else if (flags & Qt::WindowStaysOnTopHint)
+ qt_mac_set_window_group_to_stays_on_top(windowRef, type);
+ else if (dialog)
+ SetWindowGroup(windowRef, GetWindowGroupOfClass(kMovableModalWindowClass));
+
+#ifdef DEBUG_WINDOW_CREATE
+ if (WindowGroupRef grpf = GetWindowGroup(windowRef)) {
+ QCFString cfname;
+ CopyWindowGroupName(grpf, &cfname);
+ SInt32 lvl;
+ GetWindowGroupLevel(grpf, &lvl);
+ const char *from = "Default";
+ if (topExtra && grpf == topData()->group)
+ from = "Created";
+ else if (grpf == grp)
+ from = "Copied";
+ qDebug("Qt: internal: With window group '%s' [%p] @ %d: %s",
+ static_cast<QString>(cfname).toLatin1().constData(), grpf, (int)lvl, from);
+ } else {
+ qDebug("Qt: internal: No window group!!!");
+ }
+ HIWindowAvailability hi_avail = 0;
+ if (HIWindowGetAvailability(windowRef, &hi_avail) == noErr) {
+ struct {
+ UInt32 tag;
+ const char *name;
+ } known_avail[] = {
+ ADD_DEBUG_WINDOW_NAME(kHIWindowExposeHidden),
+ { 0, 0 }
+ };
+ qDebug("Qt: internal: ** HIWindowAvailibility:");
+ for (int i = 0; hi_avail && known_avail[i].name; i++) {
+ if ((hi_avail & known_avail[i].tag) == known_avail[i].tag) {
+ hi_avail ^= known_avail[i].tag;
+ qDebug("Qt: internal: * %s", known_avail[i].name);
+ }
+ }
+ if (hi_avail)
+ qDebug("Qt: internal: !! Attributes: Unknown (%d)", (int)hi_avail);
+ }
+#undef ADD_DEBUG_WINDOW_NAME
+#endif
+ if (extra && !extra->mask.isEmpty())
+ ReshapeCustomWindow(windowRef);
+ SetWindowModality(windowRef, kWindowModalityNone, 0);
+ if (qt_mac_is_macdrawer(q))
+ SetDrawerOffsets(windowRef, 0.0, 25.0);
+ data.fstrut_dirty = true; // when we create a toplevel widget, the frame strut should be dirty
+ HIViewRef hiview = (HIViewRef)data.winid;
+ HIViewRef window_hiview = qt_mac_get_contentview_for(windowRef);
+ if(!hiview) {
+ hiview = qt_mac_create_widget(q, this, window_hiview);
+ setWinId((WId)hiview);
+ } else {
+ HIViewAddSubview(window_hiview, hiview);
+ }
+ if (hiview) {
+ Rect win_rect;
+ GetWindowBounds(qt_mac_window_for (window_hiview), kWindowContentRgn, &win_rect);
+ HIRect bounds = CGRectMake(0, 0, win_rect.right-win_rect.left, win_rect.bottom-win_rect.top);
+ HIViewSetFrame(hiview, &bounds);
+ HIViewSetVisible(hiview, true);
+ if (q->testAttribute(Qt::WA_DropSiteRegistered))
+ registerDropSite(true);
+ transferChildren();
+ }
+ initWindowPtr();
+
+ if (topExtra->posFromMove) {
+ updateFrameStrut();
+ const QRect &fStrut = frameStrut();
+ Rect r;
+ SetRect(&r, data.crect.left(), data.crect.top(), data.crect.right() + 1, data.crect.bottom() + 1);
+ SetRect(&r, r.left + fStrut.left(), r.top + fStrut.top(),
+ (r.left + fStrut.left() + data.crect.width()) - fStrut.right(),
+ (r.top + fStrut.top() + data.crect.height()) - fStrut.bottom());
+ SetWindowBounds(windowRef, kWindowContentRgn, &r);
+ topExtra->posFromMove = false;
+ }
+
+ if (q->testAttribute(Qt::WA_WState_WindowOpacitySet)){
+ q->setWindowOpacity(topExtra->opacity / 255.0f);
+ } else if (qt_mac_is_macsheet(q)){
+ SetThemeWindowBackground(qt_mac_window_for(q), kThemeBrushSheetBackgroundTransparent, true);
+ CGFloat alpha = 0;
+ GetWindowAlpha(qt_mac_window_for(q), &alpha);
+ if (alpha == 1){
+ // For some reason the 'SetThemeWindowBackground' does not seem
+ // to work. So we do this little hack until it hopefully starts to
+ // work in newer versions of mac OS.
+ q->setWindowOpacity(0.95f);
+ q->setAttribute(Qt::WA_WState_WindowOpacitySet, false);
+ }
+ } else{
+ // If the window has been recreated after beeing e.g. a sheet,
+ // make sure that we don't report a faulty opacity:
+ q->setWindowOpacity(1.0f);
+ q->setAttribute(Qt::WA_WState_WindowOpacitySet, false);
+ }
+
+ // Since we only now have a window, sync our state.
+ macUpdateHideOnSuspend();
+ macUpdateOpaqueSizeGrip();
+ macUpdateMetalAttribute();
+ macUpdateIgnoreMouseEvents();
+ setWindowTitle_helper(extra->topextra->caption);
+ setWindowIconText_helper(extra->topextra->iconText);
+ setWindowFilePath_helper(extra->topextra->filePath);
+ setWindowModified_sys(q->isWindowModified());
+ updateFrameStrut();
+ qt_mac_update_sizer(q);
+ applyMaxAndMinSizeOnWindow();
+}
+#else // QT_MAC_USE_COCOA
+
+void QWidgetPrivate::setWindowLevel()
+{
+ Q_Q(QWidget);
+ const QWidget * const windowParent = q->window()->parentWidget();
+ const QWidget * const primaryWindow = windowParent ? windowParent->window() : 0;
+ NSInteger winLevel = -1;
+
+ if (q->windowType() == Qt::Popup) {
+ winLevel = NSPopUpMenuWindowLevel;
+ // Popup should be in at least the same level as its parent.
+ if (primaryWindow) {
+ OSWindowRef parentRef = qt_mac_window_for(primaryWindow);
+ winLevel = qMax([parentRef level], winLevel);
+ }
+ } else if (q->windowType() == Qt::Tool) {
+ winLevel = NSFloatingWindowLevel;
+ } else if (q->windowType() == Qt::Dialog) {
+ // Correct modality level (NSModalPanelWindowLevel) will be
+ // set by cocoa when creating a modal session later.
+ winLevel = NSNormalWindowLevel;
+ }
+
+ // StayOnTop window should appear above Tool windows.
+ if (data.window_flags & Qt::WindowStaysOnTopHint)
+ winLevel = NSPopUpMenuWindowLevel;
+ // Tooltips should appear above StayOnTop windows.
+ if (q->windowType() == Qt::ToolTip)
+ winLevel = NSScreenSaverWindowLevel;
+ // All other types are Normal level.
+ if (winLevel == -1)
+ winLevel = NSNormalWindowLevel;
+ [qt_mac_window_for(q) setLevel:winLevel];
+}
+
+void QWidgetPrivate::finishCreateWindow_sys_Cocoa(void * /*NSWindow * */ voidWindowRef)
+{
+ Q_Q(QWidget);
+ QMacCocoaAutoReleasePool pool;
+ NSWindow *windowRef = static_cast<NSWindow *>(voidWindowRef);
+ const Qt::WindowType type = q->windowType();
+ Qt::WindowFlags &flags = data.window_flags;
+ QWidget *parentWidget = q->parentWidget();
+
+ const bool popup = (type == Qt::Popup);
+ const bool dialog = (type == Qt::Dialog
+ || type == Qt::Sheet
+ || type == Qt::Drawer
+ || (flags & Qt::MSWindowsFixedSizeDialogHint));
+ QTLWExtra *topExtra = topData();
+
+ if ((popup || type == Qt::Tool || type == Qt::ToolTip) && !q->isModal()) {
+ [windowRef setHidesOnDeactivate:YES];
+ } else {
+ [windowRef setHidesOnDeactivate:NO];
+ }
+ if (q->testAttribute(Qt::WA_MacNoShadow))
+ [windowRef setHasShadow:NO];
+ else
+ [windowRef setHasShadow:YES];
+ Q_UNUSED(parentWidget);
+ Q_UNUSED(dialog);
+
+ data.fstrut_dirty = true; // when we create a toplevel widget, the frame strut should be dirty
+
+ OSViewRef nsview = (OSViewRef)data.winid;
+ if (!nsview) {
+ nsview = qt_mac_create_widget(q, this, 0);
+ setWinId(WId(nsview));
+ }
+ [windowRef setContentView:nsview];
+ [nsview setHidden:NO];
+ transferChildren();
+
+ // Tell Cocoa explicit that we wan't the view to receive key events
+ // (regardless of focus policy) because this is how it works on other
+ // platforms (and in the carbon port):
+ [windowRef makeFirstResponder:nsview];
+
+ if (topExtra->posFromMove) {
+ updateFrameStrut();
+
+ const QRect &fStrut = frameStrut();
+ const QRect &crect = data.crect;
+ const QRect frameRect(QPoint(crect.left(), crect.top()),
+ QSize(fStrut.left() + fStrut.right() + crect.width(),
+ fStrut.top() + fStrut.bottom() + crect.height()));
+ NSRect cocoaFrameRect = NSMakeRect(frameRect.x(), flipYCoordinate(frameRect.bottom() + 1),
+ frameRect.width(), frameRect.height());
+ [windowRef setFrame:cocoaFrameRect display:NO];
+ topExtra->posFromMove = false;
+ }
+
+ if (q->testAttribute(Qt::WA_WState_WindowOpacitySet)){
+ q->setWindowOpacity(topExtra->opacity / 255.0f);
+ } else if (qt_mac_is_macsheet(q)){
+ CGFloat alpha = [qt_mac_window_for(q) alphaValue];
+ if (alpha >= 1.0) {
+ q->setWindowOpacity(0.95f);
+ q->setAttribute(Qt::WA_WState_WindowOpacitySet, false);
+ }
+ } else{
+ // If the window has been recreated after beeing e.g. a sheet,
+ // make sure that we don't report a faulty opacity:
+ q->setWindowOpacity(1.0f);
+ q->setAttribute(Qt::WA_WState_WindowOpacitySet, false);
+ }
+
+ // Its more performant to handle the mouse cursor
+ // ourselves, expecially when using alien widgets:
+ [windowRef disableCursorRects];
+
+ setWindowLevel();
+ macUpdateHideOnSuspend();
+ macUpdateOpaqueSizeGrip();
+ macUpdateIgnoreMouseEvents();
+ setWindowTitle_helper(extra->topextra->caption);
+ setWindowIconText_helper(extra->topextra->iconText);
+ setWindowModified_sys(q->isWindowModified());
+ updateFrameStrut();
+ syncCocoaMask();
+ macUpdateIsOpaque();
+ qt_mac_update_sizer(q);
+ applyMaxAndMinSizeOnWindow();
+}
+
+#endif // QT_MAC_USE_COCOA
+
+/*
+ Recreates widget window. Useful if immutable
+ properties for it has changed.
+ */
+void QWidgetPrivate::recreateMacWindow()
+{
+ Q_Q(QWidget);
+ OSViewRef myView = qt_mac_nativeview_for(q);
+ OSWindowRef oldWindow = qt_mac_window_for(myView);
+#ifndef QT_MAC_USE_COCOA
+ HIViewRemoveFromSuperview(myView);
+ determineWindowClass();
+ createWindow_sys();
+
+ if (QMainWindowLayout *mwl = qt_mainwindow_layout(qobject_cast<QMainWindow *>(q))) {
+ mwl->updateHIToolBarStatus();
+ }
+
+ if (IsWindowVisible(oldWindow))
+ show_sys();
+#else
+ QMacCocoaAutoReleasePool pool;
+ [myView removeFromSuperview];
+ determineWindowClass();
+ createWindow_sys();
+ if (NSToolbar *toolbar = [oldWindow toolbar]) {
+ OSWindowRef newWindow = qt_mac_window_for(myView);
+ [newWindow setToolbar:toolbar];
+ [toolbar setVisible:[toolbar isVisible]];
+ }
+ if ([oldWindow isVisible]){
+ if ([oldWindow isSheet])
+ [NSApp endSheet:oldWindow];
+ [oldWindow orderOut:oldWindow];
+ show_sys();
+ }
+#endif // QT_MAC_USE_COCOA
+
+ // Release the window after creating the new window, because releasing it early
+ // may cause the app to quit ("close on last window closed attribute")
+ qt_mac_destructWindow(oldWindow);
+}
+
+void QWidgetPrivate::createWindow_sys()
+{
+ Q_Q(QWidget);
+ Qt::WindowFlags &flags = data.window_flags;
+ QWidget *parentWidget = q->parentWidget();
+
+ QTLWExtra *topExtra = topData();
+ if (topExtra->embedded)
+ return; // Simply return because this view "is" the top window.
+ quint32 wattr = topExtra->wattr;
+
+ if(parentWidget && (parentWidget->window()->windowFlags() & Qt::WindowStaysOnTopHint)) // If our parent has Qt::WStyle_StaysOnTop, so must we
+ flags |= Qt::WindowStaysOnTopHint;
+
+ data.fstrut_dirty = true;
+
+ OSWindowRef windowRef = qt_mac_create_window(q, topExtra->wclass, wattr, data.crect);
+ if (windowRef == 0)
+ qWarning("QWidget: Internal error: %s:%d: If you reach this error please contact Qt Support and include the\n"
+ " WidgetFlags used in creating the widget.", __FILE__, __LINE__);
+#ifndef QT_MAC_USE_COCOA
+ finishCreateWindow_sys_Carbon(windowRef);
+#else
+ finishCreateWindow_sys_Cocoa(windowRef);
+#endif
+}
+
+void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyOldWindow)
+{
+ Q_Q(QWidget);
+ QMacCocoaAutoReleasePool pool;
+
+ OSViewRef destroyid = 0;
+#ifndef QT_MAC_USE_COCOA
+ window_event = 0;
+#endif
+
+ Qt::WindowType type = q->windowType();
+ Qt::WindowFlags flags = data.window_flags;
+ QWidget *parentWidget = q->parentWidget();
+
+ bool topLevel = (flags & Qt::Window);
+ bool popup = (type == Qt::Popup);
+ bool dialog = (type == Qt::Dialog
+ || type == Qt::Sheet
+ || type == Qt::Drawer
+ || (flags & Qt::MSWindowsFixedSizeDialogHint));
+ bool desktop = (type == Qt::Desktop);
+
+ // Determine this early for top-levels so, we can use it later.
+ if (topLevel)
+ determineWindowClass();
+
+ if (desktop) {
+ QSize desktopSize = qt_mac_desktopSize();
+ q->setAttribute(Qt::WA_WState_Visible);
+ data.crect.setRect(0, 0, desktopSize.width(), desktopSize.height());
+ dialog = popup = false; // force these flags off
+ } else {
+ if (topLevel && (type != Qt::Drawer)) {
+ if (QDesktopWidget *dsk = QApplication::desktop()) { // calc pos/size from screen
+ const bool wasResized = q->testAttribute(Qt::WA_Resized);
+ const bool wasMoved = q->testAttribute(Qt::WA_Moved);
+ int deskn = dsk->primaryScreen();
+ if (parentWidget && parentWidget->windowType() != Qt::Desktop)
+ deskn = dsk->screenNumber(parentWidget);
+ QRect screenGeo = dsk->screenGeometry(deskn);
+ if (!wasResized) {
+#ifndef QT_MAC_USE_COCOA
+ data.crect.setSize(QSize(screenGeo.width()/2, 4*screenGeo.height()/10));
+#else
+ NSRect newRect = [NSWindow frameRectForContentRect:NSMakeRect(0, 0,
+ screenGeo.width() / 2.,
+ 4 * screenGeo.height() / 10.)
+ styleMask:topData()->wattr];
+ data.crect.setSize(QSize(newRect.size.width, newRect.size.height));
+#endif
+ // Constrain to minimums and maximums we've set
+ if (extra->minw > 0)
+ data.crect.setWidth(qMax(extra->minw, data.crect.width()));
+ if (extra->minh > 0)
+ data.crect.setHeight(qMax(extra->minh, data.crect.height()));
+ if (extra->maxw > 0)
+ data.crect.setWidth(qMin(extra->maxw, data.crect.width()));
+ if (extra->maxh > 0)
+ data.crect.setHeight(qMin(extra->maxh, data.crect.height()));
+ }
+ if (!wasMoved && !q->testAttribute(Qt::WA_DontShowOnScreen))
+ data.crect.moveTopLeft(QPoint(screenGeo.width()/4,
+ 3 * screenGeo.height() / 10));
+ }
+ }
+ }
+
+
+ if(!window) // always initialize
+ initializeWindow=true;
+
+ hd = 0;
+ if(window) { // override the old window (with a new NSView)
+ OSViewRef nativeView = OSViewRef(window);
+ OSViewRef parent = 0;
+#ifndef QT_MAC_USE_COCOA
+ CFRetain(nativeView);
+#else
+ [nativeView retain];
+#endif
+ if (destroyOldWindow)
+ destroyid = qt_mac_nativeview_for(q);
+ bool transfer = false;
+ setWinId((WId)nativeView);
+#ifndef QT_MAC_USE_COCOA
+#ifndef HIViewInstallEventHandler
+ // Macro taken from the CarbonEvents Header on Tiger
+#define HIViewInstallEventHandler( target, handler, numTypes, list, userData, outHandlerRef ) \
+ InstallEventHandler( HIObjectGetEventTarget( (HIObjectRef) (target) ), (handler), (numTypes), (list), (userData), (outHandlerRef) )
+#endif
+ HIViewInstallEventHandler(nativeView, make_widget_eventUPP(), GetEventTypeCount(widget_events), widget_events, 0, 0);
+#endif
+ if(topLevel) {
+ for(int i = 0; i < 2; ++i) {
+ if(i == 1) {
+ if(!initializeWindow)
+ break;
+ createWindow_sys();
+ }
+ if(OSWindowRef windowref = qt_mac_window_for(nativeView)) {
+#ifndef QT_MAC_USE_COCOA
+ CFRetain(windowref);
+#else
+ [windowref retain];
+#endif
+ if (initializeWindow) {
+ parent = qt_mac_get_contentview_for(windowref);
+ } else {
+#ifndef QT_MAC_USE_COCOA
+ parent = HIViewGetSuperview(nativeView);
+#else
+ parent = [nativeView superview];
+#endif
+ }
+ break;
+ }
+ }
+ if(!parent)
+ transfer = true;
+ } else if (parentWidget) {
+ // I need to be added to my parent, therefore my parent needs an NSView
+ // Alien note: a 'window' was supplied as argument, meaning this widget
+ // is not alien. So therefore the parent cannot be alien either.
+ parentWidget->createWinId();
+ parent = qt_mac_nativeview_for(parentWidget);
+ }
+ if(parent != nativeView && parent) {
+#ifndef QT_MAC_USE_COCOA
+ HIViewAddSubview(parent, nativeView);
+#else
+ [parent addSubview:nativeView];
+#endif
+ }
+ if(transfer)
+ transferChildren();
+ data.fstrut_dirty = true; // we'll re calculate this later
+ q->setAttribute(Qt::WA_WState_Visible,
+#ifndef QT_MAC_USE_COCOA
+ HIViewIsVisible(nativeView)
+#else
+ ![nativeView isHidden]
+#endif
+ );
+ if(initializeWindow) {
+#ifndef QT_MAC_USE_COCOA
+ HIRect bounds = CGRectMake(data.crect.x(), data.crect.y(), data.crect.width(), data.crect.height());
+ HIViewSetFrame(nativeView, &bounds);
+ q->setAttribute(Qt::WA_WState_Visible, HIViewIsVisible(nativeView));
+#else
+ NSRect bounds = NSMakeRect(data.crect.x(), data.crect.y(), data.crect.width(), data.crect.height());
+ [nativeView setFrame:bounds];
+ q->setAttribute(Qt::WA_WState_Visible, [nativeView isHidden]);
+#endif
+ }
+#ifndef QT_MAC_USE_COCOA
+ initWindowPtr();
+#endif
+ } else if (desktop) { // desktop widget
+ if (!qt_root_win)
+ QWidgetPrivate::qt_create_root_win();
+ Q_ASSERT(qt_root_win);
+ WId rootWinID = 0;
+#ifndef QT_MAC_USE_COCOA
+ CFRetain(qt_root_win);
+ if(HIViewRef rootContentView = HIViewGetRoot(qt_root_win)) {
+ rootWinID = (WId)rootContentView;
+ CFRetain(rootContentView);
+ }
+#else
+ [qt_root_win retain];
+ if (OSViewRef rootContentView = [qt_root_win contentView]) {
+ rootWinID = (WId)rootContentView;
+ [rootContentView retain];
+ }
+#endif
+ setWinId(rootWinID);
+ } else if (topLevel) {
+ determineWindowClass();
+ if(OSViewRef osview = qt_mac_create_widget(q, this, 0)) {
+#ifndef QT_MAC_USE_COCOA
+ HIRect bounds = CGRectMake(data.crect.x(), data.crect.y(),
+ data.crect.width(), data.crect.height());
+ HIViewSetFrame(osview, &bounds);
+#else
+ NSRect bounds = NSMakeRect(data.crect.x(), flipYCoordinate(data.crect.y()),
+ data.crect.width(), data.crect.height());
+ [osview setFrame:bounds];
+#endif
+ setWinId((WId)osview);
+ }
+ } else {
+ data.fstrut_dirty = false; // non-toplevel widgets don't have a frame, so no need to update the strut
+
+#ifdef QT_MAC_USE_COCOA
+ if (q->testAttribute(Qt::WA_NativeWindow) == false || q->internalWinId() != 0) {
+ // INVARIANT: q is Alien, and we should not create an NSView to back it up.
+ } else
+#endif
+ if (OSViewRef osview = qt_mac_create_widget(q, this, qt_mac_nativeview_for(parentWidget))) {
+#ifndef QT_MAC_USE_COCOA
+ HIRect bounds = CGRectMake(data.crect.x(), data.crect.y(), data.crect.width(), data.crect.height());
+ HIViewSetFrame(osview, &bounds);
+ setWinId((WId)osview);
+#else
+ NSRect bounds = NSMakeRect(data.crect.x(), data.crect.y(), data.crect.width(), data.crect.height());
+ [osview setFrame:bounds];
+ setWinId((WId)osview);
+ if (q->isVisible()) {
+ // If q were Alien before, but now became native (e.g. if a call to
+ // winId was done from somewhere), we need to show the view immidiatly:
+ QMacCocoaAutoReleasePool pool;
+ [osview setHidden:NO];
+ }
+#endif
+ }
+ }
+
+ updateIsOpaque();
+
+ if (q->testAttribute(Qt::WA_DropSiteRegistered))
+ registerDropSite(true);
+ if (q->hasFocus())
+ setFocus_sys();
+ if (!topLevel && initializeWindow)
+ setWSGeometry();
+ if (destroyid)
+ qt_mac_destructView(destroyid);
+}
+
+/*!
+ Returns the QuickDraw handle of the widget. Use of this function is not
+ portable. This function will return 0 if QuickDraw is not supported, or
+ if the handle could not be created.
+
+ \warning This function is only available on Mac OS X.
+*/
+
+Qt::HANDLE
+QWidget::macQDHandle() const
+{
+#ifndef QT_MAC_USE_COCOA
+ return d_func()->qd_hd;
+#else
+ return 0;
+#endif
+}
+
+/*!
+ Returns the CoreGraphics handle of the widget. Use of this function is
+ not portable. This function will return 0 if no painter context can be
+ established, or if the handle could not be created.
+
+ \warning This function is only available on Mac OS X.
+*/
+Qt::HANDLE
+QWidget::macCGHandle() const
+{
+ return handle();
+}
+
+void qt_mac_updateParentUnderAlienWidget(QWidget *alienWidget)
+{
+ QWidget *nativeParent = alienWidget->nativeParentWidget();
+ if (!nativeParent)
+ return;
+
+ QPoint globalPos = alienWidget->mapToGlobal(QPoint(0, 0));
+ QRect dirtyRect = QRect(nativeParent->mapFromGlobal(globalPos), alienWidget->size());
+ nativeParent->update(dirtyRect);
+}
+
+void QWidget::destroy(bool destroyWindow, bool destroySubWindows)
+{
+ Q_D(QWidget);
+ QMacCocoaAutoReleasePool pool;
+ d->aboutToDestroy();
+ if (!isWindow() && parentWidget())
+ parentWidget()->d_func()->invalidateBuffer(d->effectiveRectFor(geometry()));
+ if (!internalWinId())
+ qt_mac_updateParentUnderAlienWidget(this);
+ d->deactivateWidgetCleanup();
+ qt_mac_event_release(this);
+ if(testAttribute(Qt::WA_WState_Created)) {
+ setAttribute(Qt::WA_WState_Created, false);
+ QObjectList chldrn = children();
+ for(int i = 0; i < chldrn.size(); i++) { // destroy all widget children
+ QObject *obj = chldrn.at(i);
+ if(obj->isWidgetType())
+ static_cast<QWidget*>(obj)->destroy(destroySubWindows, destroySubWindows);
+ }
+ if(mac_mouse_grabber == this)
+ releaseMouse();
+ if(mac_keyboard_grabber == this)
+ releaseKeyboard();
+
+ if(testAttribute(Qt::WA_ShowModal)) // just be sure we leave modal
+ QApplicationPrivate::leaveModal(this);
+ else if((windowType() == Qt::Popup))
+ qApp->d_func()->closePopup(this);
+ if (destroyWindow) {
+ if(OSViewRef hiview = qt_mac_nativeview_for(this)) {
+ OSWindowRef window = 0;
+ NSDrawer *drawer = nil;
+#ifdef QT_MAC_USE_COCOA
+ if (qt_mac_is_macdrawer(this)) {
+ drawer = qt_mac_drawer_for(this);
+ } else
+#endif
+ if (isWindow())
+ window = qt_mac_window_for(hiview);
+
+ // Because of how "destruct" works, we have to do just a normal release for the root_win.
+ if (window && window == qt_root_win) {
+#ifndef QT_MAC_USE_COCOA
+ CFRelease(hiview);
+#else
+ [hiview release];
+#endif
+ } else {
+ qt_mac_destructView(hiview);
+ }
+ if (drawer)
+ qt_mac_destructDrawer(drawer);
+ if (window)
+ qt_mac_destructWindow(window);
+ }
+ }
+ QT_TRY {
+ d->setWinId(0);
+ } QT_CATCH (const std::bad_alloc &) {
+ // swallow - destructors must not throw
+ }
+ }
+}
+
+void QWidgetPrivate::transferChildren()
+{
+ Q_Q(QWidget);
+ if (!q->internalWinId())
+ return; // Can't add any views anyway
+
+ QObjectList chlist = q->children();
+ for (int i = 0; i < chlist.size(); ++i) {
+ QObject *obj = chlist.at(i);
+ if (obj->isWidgetType()) {
+ QWidget *w = (QWidget *)obj;
+ if (!w->isWindow()) {
+ // This seems weird, no need to call it in a loop right?
+ if (!topData()->caption.isEmpty())
+ setWindowTitle_helper(extra->topextra->caption);
+ if (w->internalWinId()) {
+#ifndef QT_MAC_USE_COCOA
+ HIViewAddSubview(qt_mac_nativeview_for(q), qt_mac_nativeview_for(w));
+#else
+ // New NSWindows get an extra reference when drops are
+ // registered (at least in 10.5) which means that we may
+ // access the window later and get a crash (becasue our
+ // widget is dead). Work around this be having the drop
+ // site disabled until it is part of the new hierarchy.
+ bool oldRegistered = w->testAttribute(Qt::WA_DropSiteRegistered);
+ w->setAttribute(Qt::WA_DropSiteRegistered, false);
+ [qt_mac_nativeview_for(w) retain];
+ [qt_mac_nativeview_for(w) removeFromSuperview];
+ [qt_mac_nativeview_for(q) addSubview:qt_mac_nativeview_for(w)];
+ [qt_mac_nativeview_for(w) release];
+ w->setAttribute(Qt::WA_DropSiteRegistered, oldRegistered);
+#endif
+ }
+ }
+ }
+ }
+}
+
+#ifdef QT_MAC_USE_COCOA
+void QWidgetPrivate::setSubWindowStacking(bool set)
+{
+ // After hitting too many unforeseen bugs trying to put Qt on top of the cocoa child
+ // window API, we have decided to revert this behaviour as much as we can. We
+ // therefore now only allow child windows to exist for children of modal dialogs.
+ static bool use_behaviour_qt473 = !qgetenv("QT_MAC_USE_CHILDWINDOWS").isEmpty();
+
+ // This will set/remove a visual relationship between parent and child on screen.
+ // The reason for doing this is to ensure that a child always stacks infront of
+ // its parent. Unfortunatly is turns out that [NSWindow addChildWindow] has
+ // several unwanted side-effects, one of them being the moving of a child when
+ // moving the parent, which we choose to accept. A way tougher side-effect is
+ // that Cocoa will hide the parent if you hide the child. And in the case of
+ // a tool window, since it will normally hide when you deactivate the
+ // application, Cocoa will hide the parent upon deactivate as well. The result often
+ // being no more visible windows on screen. So, to make a long story short, we only
+ // allow parent-child relationships between windows that both are either a plain window
+ // or a dialog.
+
+ Q_Q(QWidget);
+ if (!q->isWindow())
+ return;
+ NSWindow *qwin = [qt_mac_nativeview_for(q) window];
+ if (!qwin)
+ return;
+ Qt::WindowType qtype = q->windowType();
+ if (set && !(qtype == Qt::Window || qtype == Qt::Dialog))
+ return;
+ if (set && ![qwin isVisible])
+ return;
+
+ if (QWidget *parent = q->parentWidget()) {
+ if (NSWindow *pwin = [qt_mac_nativeview_for(parent) window]) {
+ if (set) {
+ Qt::WindowType ptype = parent->window()->windowType();
+ if ([pwin isVisible]
+ && (ptype == Qt::Window || ptype == Qt::Dialog)
+ && ![qwin parentWindow]
+ && (use_behaviour_qt473 || parent->windowModality() == Qt::ApplicationModal)) {
+ NSInteger level = [qwin level];
+ [pwin addChildWindow:qwin ordered:NSWindowAbove];
+ if ([qwin level] < level)
+ [qwin setLevel:level];
+ }
+ } else {
+ [pwin removeChildWindow:qwin];
+ }
+ }
+ }
+
+ // Only set-up child windows for q if q is modal:
+ if (set && !use_behaviour_qt473 && q->windowModality() != Qt::ApplicationModal)
+ return;
+
+ QObjectList widgets = q->children();
+ for (int i=0; i<widgets.size(); ++i) {
+ QWidget *child = qobject_cast<QWidget *>(widgets.at(i));
+ if (child && child->isWindow()) {
+ if (NSWindow *cwin = [qt_mac_nativeview_for(child) window]) {
+ if (set) {
+ Qt::WindowType ctype = child->window()->windowType();
+ if ([cwin isVisible] && (ctype == Qt::Window || ctype == Qt::Dialog) && ![cwin parentWindow]) {
+ NSInteger level = [cwin level];
+ [qwin addChildWindow:cwin ordered:NSWindowAbove];
+ if ([cwin level] < level)
+ [cwin setLevel:level];
+ }
+ } else {
+ [qwin removeChildWindow:qt_mac_window_for(child)];
+ }
+ }
+ }
+ }
+}
+#endif
+
+void QWidgetPrivate::setParent_sys(QWidget *parent, Qt::WindowFlags f)
+{
+ Q_Q(QWidget);
+ QMacCocoaAutoReleasePool pool;
+ QTLWExtra *topData = maybeTopData();
+ bool wasCreated = q->testAttribute(Qt::WA_WState_Created);
+#ifdef QT_MAC_USE_COCOA
+ bool wasWindow = q->isWindow();
+#endif
+ OSViewRef old_id = 0;
+
+ if (q->isVisible() && q->parentWidget() && parent != q->parentWidget())
+ q->parentWidget()->d_func()->invalidateBuffer(effectiveRectFor(q->geometry()));
+
+ // Maintain the glWidgets list on parent change: remove "our" gl widgets
+ // from the list on the old parent and grandparents.
+ if (glWidgets.isEmpty() == false) {
+ QWidget *current = q->parentWidget();
+ while (current) {
+ for (QList<QWidgetPrivate::GlWidgetInfo>::const_iterator it = glWidgets.constBegin();
+ it != glWidgets.constEnd(); ++it)
+ current->d_func()->glWidgets.removeAll(*it);
+
+ if (current->isWindow())
+ break;
+ current = current->parentWidget();
+ }
+ }
+
+#ifndef QT_MAC_USE_COCOA
+ EventHandlerRef old_window_event = 0;
+#else
+ bool oldToolbarVisible = false;
+ NSDrawer *oldDrawer = nil;
+ NSToolbar *oldToolbar = 0;
+#endif
+ if (wasCreated && !(q->windowType() == Qt::Desktop)) {
+ old_id = qt_mac_nativeview_for(q);
+#ifndef QT_MAC_USE_COCOA
+ old_window_event = window_event;
+#else
+ if (qt_mac_is_macdrawer(q)) {
+ oldDrawer = qt_mac_drawer_for(q);
+ }
+ if (wasWindow) {
+ OSWindowRef oldWindow = qt_mac_window_for(old_id);
+ oldToolbar = [oldWindow toolbar];
+ if (oldToolbar) {
+ [oldToolbar retain];
+ oldToolbarVisible = [oldToolbar isVisible];
+ [oldWindow setToolbar:nil];
+ }
+ }
+#endif
+ }
+ QWidget* oldtlw = q->window();
+
+ if (q->testAttribute(Qt::WA_DropSiteRegistered))
+ q->setAttribute(Qt::WA_DropSiteRegistered, false);
+
+ //recreate and setup flags
+ QObjectPrivate::setParent_helper(parent);
+ bool explicitlyHidden = q->testAttribute(Qt::WA_WState_Hidden) && q->testAttribute(Qt::WA_WState_ExplicitShowHide);
+ if (wasCreated && !qt_isGenuineQWidget(q))
+ return;
+
+ if (!q->testAttribute(Qt::WA_WState_WindowOpacitySet)) {
+ q->setWindowOpacity(1.0f);
+ q->setAttribute(Qt::WA_WState_WindowOpacitySet, false);
+ }
+
+ setWinId(0); //do after the above because they may want the id
+
+ data.window_flags = f;
+ q->setAttribute(Qt::WA_WState_Created, false);
+ q->setAttribute(Qt::WA_WState_Visible, false);
+ q->setAttribute(Qt::WA_WState_Hidden, false);
+ adjustFlags(data.window_flags, q);
+ // keep compatibility with previous versions, we need to preserve the created state.
+ // (but we recreate the winId for the widget being reparented, again for compatibility,
+ // unless this is an alien widget. )
+ const bool nonWindowWithCreatedParent = !q->isWindow() && parent->testAttribute(Qt::WA_WState_Created);
+ const bool nativeWidget = q->internalWinId() != 0;
+ if (wasCreated || (nativeWidget && nonWindowWithCreatedParent)) {
+ createWinId();
+ if (q->isWindow()) {
+#ifndef QT_MAC_USE_COCOA
+ // We do this down below for wasCreated, so avoid doing this twice
+ // (only for performance, it gets called a lot anyway).
+ if (!wasCreated) {
+ if (QMainWindowLayout *mwl = qt_mainwindow_layout(qobject_cast<QMainWindow *>(q))) {
+ mwl->updateHIToolBarStatus();
+ }
+ }
+#else
+ // Simply transfer our toolbar over. Everything should stay put, unlike in Carbon.
+ if (oldToolbar && !(f & Qt::FramelessWindowHint)) {
+ OSWindowRef newWindow = qt_mac_window_for(q);
+ [newWindow setToolbar:oldToolbar];
+ [oldToolbar release];
+ [oldToolbar setVisible:oldToolbarVisible];
+ }
+#endif
+ }
+ }
+ if (q->isWindow() || (!parent || parent->isVisible()) || explicitlyHidden)
+ q->setAttribute(Qt::WA_WState_Hidden);
+ q->setAttribute(Qt::WA_WState_ExplicitShowHide, explicitlyHidden);
+
+ if (wasCreated) {
+ transferChildren();
+#ifndef QT_MAC_USE_COCOA
+ // If we were a unified window, We just transfered our toolbars out of the unified toolbar.
+ // So redo the status one more time. It apparently is not an issue with Cocoa.
+ if (q->isWindow()) {
+ if (QMainWindowLayout *mwl = qt_mainwindow_layout(qobject_cast<QMainWindow *>(q))) {
+ mwl->updateHIToolBarStatus();
+ }
+ }
+#endif
+
+ if (topData &&
+ (!topData->caption.isEmpty() || !topData->filePath.isEmpty()))
+ setWindowTitle_helper(q->windowTitle());
+ }
+
+ if (q->testAttribute(Qt::WA_AcceptDrops)
+ || (!q->isWindow() && q->parentWidget()
+ && q->parentWidget()->testAttribute(Qt::WA_DropSiteRegistered)))
+ q->setAttribute(Qt::WA_DropSiteRegistered, true);
+
+ //cleanup
+#ifndef QT_MAC_USE_COCOA
+ if (old_window_event)
+ RemoveEventHandler(old_window_event);
+#endif
+ if (old_id) { //don't need old window anymore
+ OSWindowRef window = (oldtlw == q) ? qt_mac_window_for(old_id) : 0;
+ qt_mac_destructView(old_id);
+
+#ifdef QT_MAC_USE_COCOA
+ if (oldDrawer) {
+ qt_mac_destructDrawer(oldDrawer);
+ } else
+#endif
+ if (window)
+ qt_mac_destructWindow(window);
+ }
+
+ // Maintain the glWidgets list on parent change: add "our" gl widgets
+ // to the list on the new parent and grandparents.
+ if (glWidgets.isEmpty() == false) {
+ QWidget *current = q->parentWidget();
+ while (current) {
+ current->d_func()->glWidgets += glWidgets;
+ if (current->isWindow())
+ break;
+ current = current->parentWidget();
+ }
+ }
+ invalidateBuffer(q->rect());
+ qt_event_request_window_change(q);
+}
+
+QPoint QWidget::mapToGlobal(const QPoint &pos) const
+{
+ Q_D(const QWidget);
+ if (!internalWinId()) {
+ QPoint p = pos + data->crect.topLeft();
+ return isWindow() ? p : parentWidget()->mapToGlobal(p);
+ }
+#ifndef QT_MAC_USE_COCOA
+ QPoint tmp = d->mapToWS(pos);
+ HIPoint hi_pos = CGPointMake(tmp.x(), tmp.y());
+ HIViewConvertPoint(&hi_pos, qt_mac_nativeview_for(this), 0);
+ Rect win_rect;
+ GetWindowBounds(qt_mac_window_for(this), kWindowStructureRgn, &win_rect);
+ return QPoint((int)hi_pos.x+win_rect.left, (int)hi_pos.y+win_rect.top);
+#else
+ QPoint tmp = d->mapToWS(pos);
+ NSPoint hi_pos = NSMakePoint(tmp.x(), tmp.y());
+ hi_pos = [qt_mac_nativeview_for(this) convertPoint:hi_pos toView:nil];
+ NSRect win_rect = [qt_mac_window_for(this) frame];
+ hi_pos.x += win_rect.origin.x;
+ hi_pos.y += win_rect.origin.y;
+ // If we aren't the desktop we need to flip, if you flip the desktop on itself, you get the other problem.
+ return ((window()->windowFlags() & Qt::Desktop) == Qt::Desktop) ? QPointF(hi_pos.x, hi_pos.y).toPoint()
+ : flipPoint(hi_pos).toPoint();
+#endif
+}
+
+QPoint QWidget::mapFromGlobal(const QPoint &pos) const
+{
+ Q_D(const QWidget);
+ if (!internalWinId()) {
+ QPoint p = isWindow() ? pos : parentWidget()->mapFromGlobal(pos);
+ return p - data->crect.topLeft();
+ }
+#ifndef QT_MAC_USE_COCOA
+ Rect win_rect;
+ GetWindowBounds(qt_mac_window_for(this), kWindowStructureRgn, &win_rect);
+ HIPoint hi_pos = CGPointMake(pos.x()-win_rect.left, pos.y()-win_rect.top);
+ HIViewConvertPoint(&hi_pos, 0, qt_mac_nativeview_for(this));
+ return d->mapFromWS(QPoint((int)hi_pos.x, (int)hi_pos.y));
+#else
+ NSRect win_rect = [qt_mac_window_for(this) frame];
+ // The Window point is in "Cocoa coordinates," but the view is in "Qt coordinates"
+ // so make sure to keep them in sync.
+ NSPoint hi_pos = NSMakePoint(pos.x()-win_rect.origin.x,
+ flipYCoordinate(pos.y())-win_rect.origin.y);
+ hi_pos = [qt_mac_nativeview_for(this) convertPoint:hi_pos fromView:0];
+ return d->mapFromWS(QPoint(qRound(hi_pos.x), qRound(hi_pos.y)));
+#endif
+}
+
+void QWidgetPrivate::updateSystemBackground()
+{
+}
+
+void QWidgetPrivate::setCursor_sys(const QCursor &)
+{
+ qt_mac_update_cursor();
+}
+
+void QWidgetPrivate::unsetCursor_sys()
+{
+ qt_mac_update_cursor();
+}
+
+void QWidgetPrivate::setWindowTitle_sys(const QString &caption)
+{
+ Q_Q(QWidget);
+ if (q->isWindow()) {
+#ifndef QT_MAC_USE_COCOA
+ SetWindowTitleWithCFString(qt_mac_window_for(q), QCFString(caption));
+#else
+ QMacCocoaAutoReleasePool pool;
+ [qt_mac_window_for(q) setTitle:qt_mac_QStringToNSString(caption)];
+#endif
+ }
+}
+
+void QWidgetPrivate::setWindowModified_sys(bool mod)
+{
+ Q_Q(QWidget);
+ if (q->isWindow() && q->testAttribute(Qt::WA_WState_Created)) {
+#ifndef QT_MAC_USE_COCOA
+ SetWindowModified(qt_mac_window_for(q), mod);
+#else
+ [qt_mac_window_for(q) setDocumentEdited:mod];
+#endif
+ }
+}
+
+void QWidgetPrivate::setWindowFilePath_sys(const QString &filePath)
+{
+ Q_Q(QWidget);
+#ifdef QT_MAC_USE_COCOA
+ QMacCocoaAutoReleasePool pool;
+ QFileInfo fi(filePath);
+ [qt_mac_window_for(q) setRepresentedFilename:fi.exists() ? qt_mac_QStringToNSString(filePath) : @""];
+#else
+ bool validRef = false;
+ FSRef ref;
+ bzero(&ref, sizeof(ref));
+ OSStatus status;
+
+ if (!filePath.isEmpty()) {
+ status = FSPathMakeRef(reinterpret_cast<const UInt8 *>(filePath.toUtf8().constData()), &ref, 0);
+ validRef = (status == noErr);
+ }
+ // Set the proxy regardless, since this is our way of clearing it as well, but ignore the
+ // return value as well.
+ if (validRef) {
+ status = HIWindowSetProxyFSRef(qt_mac_window_for(q), &ref);
+ } else {
+ status = RemoveWindowProxy(qt_mac_window_for(q));
+ }
+ if (status != noErr)
+ qWarning("QWidget::setWindowFilePath: Error setting proxyicon for path (%s):%ld",
+ qPrintable(filePath), status);
+#endif
+}
+
+void QWidgetPrivate::setWindowIcon_sys(bool forceReset)
+{
+ Q_Q(QWidget);
+
+ if (!q->testAttribute(Qt::WA_WState_Created))
+ return;
+
+ QTLWExtra *topData = this->topData();
+ if (topData->iconPixmap && !forceReset) // already set
+ return;
+
+ QIcon icon = q->windowIcon();
+ QPixmap *pm = 0;
+ if (!icon.isNull()) {
+ // now create the extra
+ if (!topData->iconPixmap) {
+ pm = new QPixmap(icon.pixmap(QSize(22, 22)));
+ topData->iconPixmap = pm;
+ } else {
+ pm = topData->iconPixmap;
+ }
+ }
+ if (q->isWindow()) {
+#ifndef QT_MAC_USE_COCOA
+ IconRef previousIcon = 0;
+ if (icon.isNull()) {
+ RemoveWindowProxy(qt_mac_window_for(q));
+ previousIcon = topData->windowIcon;
+ topData->windowIcon = 0;
+ } else {
+ WindowClass wclass;
+ GetWindowClass(qt_mac_window_for(q), &wclass);
+
+ if (wclass == kDocumentWindowClass) {
+ IconRef newIcon = qt_mac_create_iconref(*pm);
+ previousIcon = topData->windowIcon;
+ topData->windowIcon = newIcon;
+ SetWindowProxyIcon(qt_mac_window_for(q), newIcon);
+ }
+ }
+
+ // Release the previous icon if it was set by this function.
+ if (previousIcon != 0)
+ ReleaseIconRef(previousIcon);
+#else
+ QMacCocoaAutoReleasePool pool;
+ if (icon.isNull())
+ return;
+ NSButton *iconButton = [qt_mac_window_for(q) standardWindowButton:NSWindowDocumentIconButton];
+ if (iconButton == nil) {
+ QCFString string(q->windowTitle());
+ const NSString *tmpString = reinterpret_cast<const NSString *>((CFStringRef)string);
+ [qt_mac_window_for(q) setRepresentedURL:[NSURL fileURLWithPath:const_cast<NSString *>(tmpString)]];
+ iconButton = [qt_mac_window_for(q) standardWindowButton:NSWindowDocumentIconButton];
+ }
+ if (icon.isNull()) {
+ [iconButton setImage:nil];
+ } else {
+ QPixmap scaled = pm->scaled(QSize(16,16), Qt::KeepAspectRatio, Qt::SmoothTransformation);
+ NSImage *image = static_cast<NSImage *>(qt_mac_create_nsimage(scaled));
+ [iconButton setImage:image];
+ [image release];
+ }
+#endif
+ }
+}
+
+void QWidgetPrivate::setWindowIconText_sys(const QString &iconText)
+{
+ Q_Q(QWidget);
+ if(q->isWindow() && !iconText.isEmpty()) {
+#ifndef QT_MAC_USE_COCOA
+ SetWindowAlternateTitle(qt_mac_window_for(q), QCFString(iconText));
+#else
+ QMacCocoaAutoReleasePool pool;
+ [qt_mac_window_for(q) setMiniwindowTitle:qt_mac_QStringToNSString(iconText)];
+#endif
+ }
+}
+
+void QWidget::grabMouse()
+{
+ if(isVisible() && !qt_nograb()) {
+ if(mac_mouse_grabber)
+ mac_mouse_grabber->releaseMouse();
+ mac_mouse_grabber=this;
+ qt_mac_setMouseGrabCursor(true);
+ }
+}
+
+#ifndef QT_NO_CURSOR
+void QWidget::grabMouse(const QCursor &cursor)
+{
+ if(isVisible() && !qt_nograb()) {
+ if(mac_mouse_grabber)
+ mac_mouse_grabber->releaseMouse();
+ mac_mouse_grabber=this;
+ qt_mac_setMouseGrabCursor(true, const_cast<QCursor *>(&cursor));
+ }
+}
+#endif
+
+void QWidget::releaseMouse()
+{
+ if(!qt_nograb() && mac_mouse_grabber == this) {
+ mac_mouse_grabber = 0;
+ qt_mac_setMouseGrabCursor(false);
+ }
+}
+
+void QWidget::grabKeyboard()
+{
+ if(!qt_nograb()) {
+ if(mac_keyboard_grabber)
+ mac_keyboard_grabber->releaseKeyboard();
+ mac_keyboard_grabber = this;
+ }
+}
+
+void QWidget::releaseKeyboard()
+{
+ if(!qt_nograb() && mac_keyboard_grabber == this)
+ mac_keyboard_grabber = 0;
+}
+
+QWidget *QWidget::mouseGrabber()
+{
+ return mac_mouse_grabber;
+}
+
+QWidget *QWidget::keyboardGrabber()
+{
+ return mac_keyboard_grabber;
+}
+
+void QWidget::activateWindow()
+{
+ QWidget *tlw = window();
+ if(!tlw->isVisible() || !tlw->isWindow() || (tlw->windowType() == Qt::Desktop))
+ return;
+ qt_event_remove_activate();
+
+ QWidget *fullScreenWidget = tlw;
+ QWidget *parentW = tlw;
+ // Find the oldest parent or the parent with fullscreen, whichever comes first.
+ while (parentW) {
+ fullScreenWidget = parentW->window();
+ if (fullScreenWidget->windowState() & Qt::WindowFullScreen)
+ break;
+ parentW = fullScreenWidget->parentWidget();
+ }
+
+ if (fullScreenWidget->windowType() != Qt::ToolTip) {
+ qt_mac_set_fullscreen_mode((fullScreenWidget->windowState() & Qt::WindowFullScreen) &&
+ qApp->desktop()->screenNumber(this) == 0);
+ }
+
+ bool windowActive;
+ OSWindowRef win = qt_mac_window_for(tlw);
+#ifndef QT_MAC_USE_COCOA
+ windowActive = IsWindowActive(win);
+#else
+ QMacCocoaAutoReleasePool pool;
+ windowActive = [win isKeyWindow];
+#endif
+ if ((tlw->windowType() == Qt::Popup)
+ || (tlw->windowType() == Qt::Tool)
+ || qt_mac_is_macdrawer(tlw)
+ || windowActive) {
+#ifndef QT_MAC_USE_COCOA
+ ActivateWindow(win, true);
+ qApp->setActiveWindow(tlw);
+#else
+ [win makeKeyWindow];
+#endif
+ } else if(!isMinimized()) {
+#ifndef QT_MAC_USE_COCOA
+ SelectWindow(win);
+#else
+ [win makeKeyAndOrderFront:win];
+#endif
+ }
+}
+
+QWindowSurface *QWidgetPrivate::createDefaultWindowSurface_sys()
+{
+ return new QMacWindowSurface(q_func());
+}
+
+void QWidgetPrivate::update_sys(const QRect &r)
+{
+ Q_Q(QWidget);
+ if (updateRedirectedToGraphicsProxyWidget(q, r))
+ return;
+ dirtyOnWidget += r;
+ macSetNeedsDisplay(r != q->rect() ? r : QRegion());
+}
+
+void QWidgetPrivate::update_sys(const QRegion &rgn)
+{
+ Q_Q(QWidget);
+ if (updateRedirectedToGraphicsProxyWidget(q, rgn))
+ return;
+ dirtyOnWidget += rgn;
+ macSetNeedsDisplay(rgn);
+}
+
+bool QWidgetPrivate::isRealWindow() const
+{
+ return q_func()->isWindow() && !topData()->embedded;
+}
+
+void QWidgetPrivate::show_sys()
+{
+ Q_Q(QWidget);
+ if ((q->windowType() == Qt::Desktop)) //desktop is always visible
+ return;
+
+ invalidateBuffer(q->rect());
+ if (q->testAttribute(Qt::WA_OutsideWSRange))
+ return;
+ QMacCocoaAutoReleasePool pool;
+ q->setAttribute(Qt::WA_Mapped);
+ if (q->testAttribute(Qt::WA_DontShowOnScreen))
+ return;
+
+ bool realWindow = isRealWindow();
+#ifndef QT_MAC_USE_COCOA
+ if (realWindow && !q->testAttribute(Qt::WA_Moved)) {
+ if (qt_mac_is_macsheet(q))
+ recreateMacWindow();
+ q->createWinId();
+ if (QWidget *p = q->parentWidget()) {
+ p->createWinId();
+ RepositionWindow(qt_mac_window_for(q), qt_mac_window_for(p), kWindowCenterOnParentWindow);
+ } else {
+ RepositionWindow(qt_mac_window_for(q), 0, kWindowCenterOnMainScreen);
+ }
+ }
+#endif
+
+ data.fstrut_dirty = true;
+ if (realWindow) {
+ bool isCurrentlyMinimized = (q->windowState() & Qt::WindowMinimized);
+ setModal_sys();
+ OSWindowRef window = qt_mac_window_for(q);
+#ifndef QT_MAC_USE_COCOA
+ SizeWindow(window, q->width(), q->height(), true);
+#endif
+
+#ifdef QT_MAC_USE_COCOA
+ // Make sure that we end up sending a repaint event to
+ // the widget if the window has been visible one before:
+ [qt_mac_get_contentview_for(window) setNeedsDisplay:YES];
+#endif
+ if(qt_mac_is_macsheet(q)) {
+ qt_event_request_showsheet(q);
+ } else if(qt_mac_is_macdrawer(q)) {
+#ifndef QT_MAC_USE_COCOA
+ OpenDrawer(window, kWindowEdgeDefault, false);
+#else
+ NSDrawer *drawer = qt_mac_drawer_for(q);
+ [drawer openOnEdge:[drawer preferredEdge]];
+#endif
+ } else {
+#ifndef QT_MAC_USE_COCOA
+ ShowHide(window, true);
+#else
+ // sync the opacity value back (in case of a fade).
+ [window setAlphaValue:q->windowOpacity()];
+
+ QWidget *top = 0;
+ if (QApplicationPrivate::tryModalHelper(q, &top)) {
+ [window makeKeyAndOrderFront:window];
+ // If this window is app modal, we need to start spinning
+ // a modal session for it. Interrupting
+ // the event dispatcher will make this happend:
+ if (data.window_modality == Qt::ApplicationModal)
+ QEventDispatcherMac::instance()->interrupt();
+ } else {
+ // The window is modally shaddowed, so we need to make
+ // sure that we don't pop in front of the modal window:
+ [window orderFront:window];
+ if (!top->testAttribute(Qt::WA_DontShowOnScreen)) {
+ if (NSWindow *modalWin = qt_mac_window_for(top))
+ [modalWin orderFront:window];
+ }
+ }
+ setSubWindowStacking(true);
+ qt_mac_update_cursor();
+#endif
+ if (q->windowType() == Qt::Popup) {
+ qt_button_down = 0;
+ if (q->focusWidget())
+ q->focusWidget()->d_func()->setFocus_sys();
+ else
+ setFocus_sys();
+ }
+ toggleDrawers(true);
+ }
+ if (isCurrentlyMinimized) { //show in collapsed state
+#ifndef QT_MAC_USE_COCOA
+ CollapseWindow(window, true);
+#else
+ [window miniaturize:window];
+#endif
+ } else if (!q->testAttribute(Qt::WA_ShowWithoutActivating)) {
+#ifndef QT_MAC_USE_COCOA
+ qt_event_request_activate(q);
+#endif
+ }
+ } else if(topData()->embedded || !q->parentWidget() || q->parentWidget()->isVisible()) {
+#ifndef QT_MAC_USE_COCOA
+ HIViewSetVisible(qt_mac_nativeview_for(q), true);
+#else
+ if (NSView *view = qt_mac_nativeview_for(q)) {
+ // INVARIANT: q is native. Just show the view:
+ [view setHidden:NO];
+ } else {
+ // INVARIANT: q is alien. Update q instead:
+ q->update();
+ }
+#endif
+ }
+
+#ifdef QT_MAC_USE_COCOA
+ if ([NSApp isActive] && !qt_button_down && !QWidget::mouseGrabber()){
+ // Update enter/leave immidiatly, don't wait for a move event. But only
+ // if no grab exists (even if the grab points to this widget, it seems, ref X11)
+ QPoint qlocal, qglobal;
+ QWidget *widgetUnderMouse = 0;
+ qt_mac_getTargetForMouseEvent(0, QEvent::Enter, qlocal, qglobal, 0, &widgetUnderMouse);
+ QApplicationPrivate::dispatchEnterLeave(widgetUnderMouse, qt_last_mouse_receiver);
+ qt_last_mouse_receiver = widgetUnderMouse;
+ qt_last_native_mouse_receiver = widgetUnderMouse ?
+ (widgetUnderMouse->internalWinId() ? widgetUnderMouse : widgetUnderMouse->nativeParentWidget()) : 0;
+ }
+#endif
+
+ topLevelAt_cache = 0;
+ qt_event_request_window_change(q);
+}
+
+QPoint qt_mac_nativeMapFromParent(const QWidget *child, const QPoint &pt)
+{
+#ifndef QT_MAC_USE_COCOA
+ CGPoint nativePoint = CGPointMake(pt.x(), pt.y());
+ HIViewConvertPoint(&nativePoint, qt_mac_nativeview_for(child->parentWidget()),
+ qt_mac_nativeview_for(child));
+#else
+ NSPoint nativePoint = [qt_mac_nativeview_for(child) convertPoint:NSMakePoint(pt.x(), pt.y()) fromView:qt_mac_nativeview_for(child->parentWidget())];
+#endif
+ return QPoint(nativePoint.x, nativePoint.y);
+}
+
+
+void QWidgetPrivate::hide_sys()
+{
+ Q_Q(QWidget);
+ if((q->windowType() == Qt::Desktop)) //you can't hide the desktop!
+ return;
+ QMacCocoaAutoReleasePool pool;
+ if(q->isWindow()) {
+#ifdef QT_MAC_USE_COCOA
+ setSubWindowStacking(false);
+#endif
+ OSWindowRef window = qt_mac_window_for(q);
+ if(qt_mac_is_macsheet(q)) {
+#ifndef QT_MAC_USE_COCOA
+ WindowRef parent = 0;
+ if(GetSheetWindowParent(window, &parent) != noErr || !parent)
+ ShowHide(window, false);
+ else
+ HideSheetWindow(window);
+#else
+ [NSApp endSheet:window];
+ [window orderOut:window];
+#endif
+ } else if(qt_mac_is_macdrawer(q)) {
+#ifndef QT_MAC_USE_COCOA
+ CloseDrawer(window, false);
+#else
+ [qt_mac_drawer_for(q) close];
+#endif
+ } else {
+#ifndef QT_MAC_USE_COCOA
+ ShowHide(window, false);
+#else
+ [window orderOut:window];
+ // Unfortunately it is not as easy as just hiding the window, we need
+ // to find out if we were in full screen mode. If we were and this is
+ // the last window in full screen mode then we need to unset the full screen
+ // mode. If this is not the last visible window in full screen mode then we
+ // don't change the full screen mode.
+ if(q->isFullScreen())
+ {
+ bool keepFullScreen = false;
+ QWidgetList windowList = qApp->topLevelWidgets();
+ int windowCount = windowList.count();
+ for(int i = 0; i < windowCount; i++)
+ {
+ QWidget *w = windowList[i];
+ // If it is the same window, we don't need to check :-)
+ if(q == w)
+ continue;
+ // If they are not visible or if they are minimized then
+ // we just ignore them.
+ if(!w->isVisible() || w->isMinimized())
+ continue;
+ // Is it full screen?
+ // Notice that if there is one window in full screen mode then we
+ // cannot switch the full screen mode off, therefore we just abort.
+ if(w->isFullScreen()) {
+ keepFullScreen = true;
+ break;
+ }
+ }
+ // No windows in full screen mode, so let just unset that flag.
+ if(!keepFullScreen)
+ qt_mac_set_fullscreen_mode(false);
+ }
+#endif
+ toggleDrawers(false);
+ qt_mac_update_cursor();
+#ifndef QT_MAC_USE_COCOA
+ // Clear modality (because it seems something that we've always done).
+ if (data.window_modality != Qt::NonModal) {
+ SetWindowModality(window, kWindowModalityNone,
+ q->parentWidget() ? qt_mac_window_for(q->parentWidget()->window()) : 0);
+ }
+#endif
+ }
+#ifndef QT_MAC_USE_COCOA
+ // If the window we now hide was the active window, we need
+ // to find, and activate another window on screen. NB: Cocoa takes care of this
+ // logic for us (and distinquishes between main windows and key windows)
+ if (q->isActiveWindow() && !(q->windowType() == Qt::Popup)) {
+ QWidget *w = 0;
+ if(q->parentWidget())
+ w = q->parentWidget()->window();
+ if(!w || (!w->isVisible() && !w->isMinimized())) {
+ for (WindowPtr wp = GetFrontWindowOfClass(kMovableModalWindowClass, true);
+ wp; wp = GetNextWindowOfClass(wp, kMovableModalWindowClass, true)) {
+ if((w = qt_mac_find_window(wp)))
+ break;
+ }
+ if (!w){
+ for (WindowPtr wp = GetFrontWindowOfClass(kDocumentWindowClass, true);
+ wp; wp = GetNextWindowOfClass(wp, kDocumentWindowClass, true)) {
+ if((w = qt_mac_find_window(wp)))
+ break;
+ }
+ }
+ if (!w){
+ for(WindowPtr wp = GetFrontWindowOfClass(kSimpleWindowClass, true);
+ wp; wp = GetNextWindowOfClass(wp, kSimpleWindowClass, true)) {
+ if((w = qt_mac_find_window(wp)))
+ break;
+ }
+ }
+ }
+ if(w && w->isVisible() && !w->isMinimized()) {
+ qt_event_request_activate(w);
+ }
+ }
+#endif
+ } else {
+ invalidateBuffer(q->rect());
+#ifndef QT_MAC_USE_COCOA
+ HIViewSetVisible(qt_mac_nativeview_for(q), false);
+#else
+ if (NSView *view = qt_mac_nativeview_for(q)) {
+ // INVARIANT: q is native. Just hide the view:
+ [view setHidden:YES];
+ } else {
+ // INVARIANT: q is alien. Repaint where q is placed instead:
+ qt_mac_updateParentUnderAlienWidget(q);
+ }
+#endif
+ }
+
+#ifdef QT_MAC_USE_COCOA
+ if ([NSApp isActive] && !qt_button_down && !QWidget::mouseGrabber()){
+ // Update enter/leave immidiatly, don't wait for a move event. But only
+ // if no grab exists (even if the grab points to this widget, it seems, ref X11)
+ QPoint qlocal, qglobal;
+ QWidget *widgetUnderMouse = 0;
+ qt_mac_getTargetForMouseEvent(0, QEvent::Leave, qlocal, qglobal, 0, &widgetUnderMouse);
+ QApplicationPrivate::dispatchEnterLeave(widgetUnderMouse, qt_last_native_mouse_receiver);
+ qt_last_mouse_receiver = widgetUnderMouse;
+ qt_last_native_mouse_receiver = widgetUnderMouse ?
+ (widgetUnderMouse->internalWinId() ? widgetUnderMouse : widgetUnderMouse->nativeParentWidget()) : 0;
+ }
+#endif
+
+ topLevelAt_cache = 0;
+ qt_event_request_window_change(q);
+ deactivateWidgetCleanup();
+ qt_mac_event_release(q);
+}
+
+void QWidget::setWindowState(Qt::WindowStates newstate)
+{
+ Q_D(QWidget);
+ bool needShow = false;
+ Qt::WindowStates oldstate = windowState();
+ if (oldstate == newstate)
+ return;
+
+#ifdef QT_MAC_USE_COCOA
+ QMacCocoaAutoReleasePool pool;
+#endif
+ bool needSendStateChange = true;
+ if(isWindow()) {
+ if((oldstate & Qt::WindowFullScreen) != (newstate & Qt::WindowFullScreen)) {
+ if(newstate & Qt::WindowFullScreen) {
+ if(QTLWExtra *tlextra = d->topData()) {
+ if(tlextra->normalGeometry.width() < 0) {
+ if(!testAttribute(Qt::WA_Resized))
+ adjustSize();
+ tlextra->normalGeometry = geometry();
+ }
+ tlextra->savedFlags = windowFlags();
+ }
+ needShow = isVisible();
+ const QRect fullscreen(qApp->desktop()->screenGeometry(qApp->desktop()->screenNumber(this)));
+ setParent(parentWidget(), Qt::Window | Qt::FramelessWindowHint | (windowFlags() & 0xffff0000)); //save
+ setGeometry(fullscreen);
+ if(!qApp->desktop()->screenNumber(this))
+ qt_mac_set_fullscreen_mode(true);
+ } else {
+ needShow = isVisible();
+ if(!qApp->desktop()->screenNumber(this))
+ qt_mac_set_fullscreen_mode(false);
+ setParent(parentWidget(), d->topData()->savedFlags);
+ setGeometry(d->topData()->normalGeometry);
+ d->topData()->normalGeometry.setRect(0, 0, -1, -1);
+ }
+ }
+
+ d->createWinId();
+
+ OSWindowRef window = qt_mac_window_for(this);
+ if((oldstate & Qt::WindowMinimized) != (newstate & Qt::WindowMinimized)) {
+ if (newstate & Qt::WindowMinimized) {
+#ifndef QT_MAC_USE_COCOA
+ CollapseWindow(window, true);
+#else
+ [window miniaturize:window];
+#endif
+ } else {
+#ifndef QT_MAC_USE_COCOA
+ CollapseWindow(window, false);
+#else
+ [window deminiaturize:window];
+#endif
+ }
+ needSendStateChange = oldstate == windowState(); // Collapse didn't change our flags.
+ }
+
+ if((newstate & Qt::WindowMaximized) && !((newstate & Qt::WindowFullScreen))) {
+ if(QTLWExtra *tlextra = d->topData()) {
+ if(tlextra->normalGeometry.width() < 0) {
+ if(!testAttribute(Qt::WA_Resized))
+ adjustSize();
+ tlextra->normalGeometry = geometry();
+ }
+ }
+ } else if(!(newstate & Qt::WindowFullScreen)) {
+// d->topData()->normalGeometry = QRect(0, 0, -1, -1);
+ }
+
+#ifdef DEBUG_WINDOW_STATE
+#define WSTATE(x) qDebug("%s -- %s --> %s", #x, (oldstate & x) ? "true" : "false", (newstate & x) ? "true" : "false")
+ WSTATE(Qt::WindowMinimized);
+ WSTATE(Qt::WindowMaximized);
+ WSTATE(Qt::WindowFullScreen);
+#undef WSTATE
+#endif
+ if(!(newstate & (Qt::WindowMinimized|Qt::WindowFullScreen)) &&
+ ((oldstate & Qt::WindowFullScreen) || (oldstate & Qt::WindowMinimized) ||
+ (oldstate & Qt::WindowMaximized) != (newstate & Qt::WindowMaximized))) {
+ if(newstate & Qt::WindowMaximized) {
+ data->fstrut_dirty = true;
+#ifndef QT_MAC_USE_COCOA
+ HIToolbarRef toolbarRef;
+ if (GetWindowToolbar(window, &toolbarRef) == noErr && toolbarRef
+ && !isVisible() && !IsWindowToolbarVisible(window)) {
+ // HIToolbar, needs to be shown so that it's in the structure window
+ // Typically this is part of a main window and will get shown
+ // during the show, but it's will make the maximize all wrong.
+ ShowHideWindowToolbar(window, true, false);
+ d->updateFrameStrut(); // In theory the dirty would work, but it's optimized out if the window is not visible :(
+ }
+ Rect bounds;
+ QDesktopWidget *dsk = QApplication::desktop();
+ QRect avail = dsk->availableGeometry(dsk->screenNumber(this));
+ SetRect(&bounds, avail.x(), avail.y(), avail.x() + avail.width(), avail.y() + avail.height());
+ if(QWExtra *extra = d->extraData()) {
+ if(bounds.right - bounds.left > extra->maxw)
+ bounds.right = bounds.left + extra->maxw;
+ if(bounds.bottom - bounds.top > extra->maxh)
+ bounds.bottom = bounds.top + extra->maxh;
+ }
+ if(d->topData()) {
+ QRect fs = d->frameStrut();
+ bounds.left += fs.left();
+ if(bounds.right < avail.x()+avail.width())
+ bounds.right = qMin<short>((uint)avail.x()+avail.width(), bounds.right+fs.left());
+ if(bounds.bottom < avail.y()+avail.height())
+ bounds.bottom = qMin<short>((uint)avail.y()+avail.height(), bounds.bottom+fs.top());
+ bounds.top += fs.top();
+ bounds.right -= fs.right();
+ bounds.bottom -= fs.bottom();
+ }
+ QRect orect(geometry().x(), geometry().y(), width(), height()),
+ nrect(bounds.left, bounds.top, bounds.right - bounds.left,
+ bounds.bottom - bounds.top);
+ if(orect != nrect) { // the new rect differ from the old
+ Point idealSize = { nrect.height(), nrect.width() };
+ ZoomWindowIdeal(window, inZoomOut, &idealSize);
+ }
+#else
+ NSToolbar *toolbarRef = [window toolbar];
+ if (toolbarRef && !isVisible() && ![toolbarRef isVisible]) {
+ // HIToolbar, needs to be shown so that it's in the structure window
+ // Typically this is part of a main window and will get shown
+ // during the show, but it's will make the maximize all wrong.
+ // ### Not sure this is right for NSToolbar...
+ [toolbarRef setVisible:true];
+// ShowHideWindowToolbar(window, true, false);
+ d->updateFrameStrut(); // In theory the dirty would work, but it's optimized out if the window is not visible :(
+ }
+ // Everything should be handled by Cocoa.
+ [window zoom:window];
+#endif
+ needSendStateChange = oldstate == windowState(); // Zoom didn't change flags.
+ } else if(oldstate & Qt::WindowMaximized && !(oldstate & Qt::WindowFullScreen)) {
+#ifndef QT_MAC_USE_COCOA
+ Point idealSize;
+ ZoomWindowIdeal(window, inZoomIn, &idealSize);
+#else
+ [window zoom:window];
+#endif
+ if(QTLWExtra *tlextra = d->topData()) {
+ setGeometry(tlextra->normalGeometry);
+ tlextra->normalGeometry.setRect(0, 0, -1, -1);
+ }
+ }
+ }
+ }
+
+ data->window_state = newstate;
+
+ if(needShow)
+ show();
+
+ if(newstate & Qt::WindowActive)
+ activateWindow();
+
+ qt_event_request_window_change(this);
+ if (needSendStateChange) {
+ QWindowStateChangeEvent e(oldstate);
+ QApplication::sendEvent(this, &e);
+ }
+}
+
+void QWidgetPrivate::setFocus_sys()
+{
+ Q_Q(QWidget);
+ if (q->testAttribute(Qt::WA_WState_Created)) {
+#ifdef QT_MAC_USE_COCOA
+ QMacCocoaAutoReleasePool pool;
+ NSView *view = qt_mac_nativeview_for(q);
+ [[view window] makeFirstResponder:view];
+#else
+ SetKeyboardFocus(qt_mac_window_for(q), qt_mac_nativeview_for(q), 1);
+#endif
+ }
+}
+
+NSComparisonResult compareViews2Raise(id view1, id view2, void *context)
+{
+ id topView = reinterpret_cast<id>(context);
+ if (view1 == topView)
+ return NSOrderedDescending;
+ if (view2 == topView)
+ return NSOrderedAscending;
+ return NSOrderedSame;
+}
+
+void QWidgetPrivate::raise_sys()
+{
+ Q_Q(QWidget);
+ if((q->windowType() == Qt::Desktop))
+ return;
+
+#if QT_MAC_USE_COCOA
+ QMacCocoaAutoReleasePool pool;
+ if (isRealWindow()) {
+ // With the introduction of spaces it is not as simple as just raising the window.
+ // First we need to check if we are in the right space. If we are, then we just continue
+ // as usual. The problem comes when we are not in the active space. There are two main cases:
+ // 1. Our parent was moved to a new space. In this case we want the window to be raised
+ // in the same space as its parent.
+ // 2. We don't have a parent. For this case we will just raise the window and let Cocoa
+ // switch to the corresponding space.
+ // NOTICE: There are a lot of corner cases here. We are keeping this simple for now, if
+ // required we will introduce special handling for some of them.
+ if (!q->testAttribute(Qt::WA_DontShowOnScreen) && q->isVisible()) {
+ OSWindowRef window = qt_mac_window_for(q);
+ // isOnActiveSpace is available only from 10.6 onwards, so we need to check if it is
+ // available before calling it.
+ if([window respondsToSelector:@selector(isOnActiveSpace)]) {
+ if(![window performSelector:@selector(isOnActiveSpace)]) {
+ QWidget *parentWidget = q->parentWidget();
+ if(parentWidget) {
+ OSWindowRef parentWindow = qt_mac_window_for(parentWidget);
+ if(parentWindow && [parentWindow respondsToSelector:@selector(isOnActiveSpace)]) {
+ if ([parentWindow performSelector:@selector(isOnActiveSpace)]) {
+ // The window was created in a different space. Therefore if we want
+ // to show it in the current space we need to recreate it in the new
+ // space.
+ recreateMacWindow();
+ window = qt_mac_window_for(q);
+ }
+ }
+ }
+ }
+ }
+ [window orderFront:window];
+ }
+ if (qt_mac_raise_process) { //we get to be the active process now
+ ProcessSerialNumber psn;
+ GetCurrentProcess(&psn);
+ SetFrontProcessWithOptions(&psn, kSetFrontProcessFrontWindowOnly);
+ }
+ } else {
+ NSView *view = qt_mac_nativeview_for(q);
+ NSView *parentView = [view superview];
+ [parentView sortSubviewsUsingFunction:compareViews2Raise context:reinterpret_cast<void *>(view)];
+ }
+ topLevelAt_cache = 0;
+#else
+ if(q->isWindow()) {
+ //raise this window
+ BringToFront(qt_mac_window_for(q));
+ if(qt_mac_raise_process) { //we get to be the active process now
+ ProcessSerialNumber psn;
+ GetCurrentProcess(&psn);
+ SetFrontProcessWithOptions(&psn, kSetFrontProcessFrontWindowOnly);
+ }
+ } else if(q->parentWidget()) {
+ HIViewSetZOrder(qt_mac_nativeview_for(q), kHIViewZOrderAbove, 0);
+ qt_event_request_window_change(q);
+ }
+#endif
+}
+
+NSComparisonResult compareViews2Lower(id view1, id view2, void *context)
+{
+ id topView = reinterpret_cast<id>(context);
+ if (view1 == topView)
+ return NSOrderedAscending;
+ if (view2 == topView)
+ return NSOrderedDescending;
+ return NSOrderedSame;
+}
+
+void QWidgetPrivate::lower_sys()
+{
+ Q_Q(QWidget);
+ if((q->windowType() == Qt::Desktop))
+ return;
+#ifdef QT_MAC_USE_COCOA
+ if (isRealWindow()) {
+ OSWindowRef window = qt_mac_window_for(q);
+ [window orderBack:window];
+ } else {
+ NSView *view = qt_mac_nativeview_for(q);
+ NSView *parentView = [view superview];
+ [parentView sortSubviewsUsingFunction:compareViews2Lower context:reinterpret_cast<void *>(view)];
+ }
+ topLevelAt_cache = 0;
+#else
+ if(q->isWindow()) {
+ SendBehind(qt_mac_window_for(q), 0);
+ } else if(q->parentWidget()) {
+ invalidateBuffer(q->rect());
+ HIViewSetZOrder(qt_mac_nativeview_for(q), kHIViewZOrderBelow, 0);
+ qt_event_request_window_change(q);
+ }
+#endif
+}
+
+NSComparisonResult compareViews2StackUnder(id view1, id view2, void *context)
+{
+ const QHash<NSView *, int> &viewOrder = *reinterpret_cast<QHash<NSView *, int> *>(context);
+ if (viewOrder[view1] < viewOrder[view2])
+ return NSOrderedAscending;
+ if (viewOrder[view1] > viewOrder[view2])
+ return NSOrderedDescending;
+ return NSOrderedSame;
+}
+
+void QWidgetPrivate::stackUnder_sys(QWidget *w)
+{
+ // stackUnder
+ Q_Q(QWidget);
+ if(!w || q->isWindow() || (q->windowType() == Qt::Desktop))
+ return;
+#ifdef QT_MAC_USE_COCOA
+ // Do the same trick as lower_sys() and put this widget before the widget passed in.
+ NSView *myView = qt_mac_nativeview_for(q);
+ NSView *wView = qt_mac_nativeview_for(w);
+
+ QHash<NSView *, int> viewOrder;
+ NSView *parentView = [myView superview];
+ NSArray *subviews = [parentView subviews];
+ NSUInteger index = 1;
+ // make a hash of view->zorderindex and make sure z-value is always odd,
+ // so that when we modify the order we create a new (even) z-value which
+ // will not interfere with others.
+ for (NSView *subview in subviews) {
+ viewOrder.insert(subview, index * 2);
+ ++index;
+ }
+ viewOrder[myView] = viewOrder[wView] - 1;
+
+ [parentView sortSubviewsUsingFunction:compareViews2StackUnder context:reinterpret_cast<void *>(&viewOrder)];
+#else
+ QWidget *p = q->parentWidget();
+ if(!p || p != w->parentWidget())
+ return;
+ invalidateBuffer(q->rect());
+ HIViewSetZOrder(qt_mac_nativeview_for(q), kHIViewZOrderBelow, qt_mac_nativeview_for(w));
+ qt_event_request_window_change(q);
+#endif
+}
+
+#ifndef QT_MAC_USE_COCOA
+/*
+ Modifies the bounds for a widgets backing HIView during moves and resizes. Also updates the
+ widget, either by scrolling its contents or repainting, depending on the WA_StaticContents
+ flag
+*/
+static void qt_mac_update_widget_position(QWidget *q, QRect oldRect, QRect newRect)
+{
+ HIRect bounds = CGRectMake(newRect.x(), newRect.y(),
+ newRect.width(), newRect.height());
+
+ const HIViewRef view = qt_mac_nativeview_for(q);
+ const bool isMove = (oldRect.topLeft() != newRect.topLeft());
+ const bool isResize = (oldRect.size() != newRect.size());
+
+// qDebug() << oldRect << newRect << isMove << isResize << q->testAttribute(Qt::WA_OpaquePaintEvent) << q->testAttribute(Qt::WA_StaticContents);
+ QWidgetPrivate *qd = qt_widget_private(q);
+
+ // Perform a normal (complete repaint) update in some cases:
+ if (
+ // always repaint on move.
+ (isMove) ||
+
+ // limited update on resize requires WA_StaticContents.
+ (isResize && q->testAttribute(Qt::WA_StaticContents) == false) ||
+
+ // one of the rects are invalid
+ (oldRect.isValid() == false || newRect.isValid() == false) ||
+
+ // the position update is a part of a drag-and-drop operation
+ QDragManager::self()->object ||
+
+ // we are on Panther (no HIViewSetNeedsDisplayInRect)
+ QSysInfo::MacintoshVersion < QSysInfo::MV_10_4
+ ){
+ HIViewSetFrame(view, &bounds);
+ return;
+ }
+
+ const int dx = newRect.x() - oldRect.x();
+ const int dy = newRect.y() - oldRect.y();
+
+ if (isMove) {
+ // HIViewScrollRect silently fails if we try to scroll anything under the grow box.
+ // Check if there's one present within the widget rect, and if there is fall back
+ // to repainting the entire widget.
+ QWidget const * const parentWidget = q->parentWidget();
+ const HIViewRef parentView = qt_mac_nativeview_for(parentWidget);
+ HIViewRef nativeSizeGrip = 0;
+ if (q->testAttribute(Qt::WA_WState_Created))
+ HIViewFindByID(HIViewGetRoot(HIViewGetWindow(HIViewRef(q->winId()))), kHIViewWindowGrowBoxID, &nativeSizeGrip);
+ if (nativeSizeGrip) {
+ QWidget * const window = q->window();
+
+ const int sizeGripSize = 20;
+ const QRect oldWidgetRect = QRect(q->mapTo(window, QPoint(0, 0)), QSize(oldRect.width(), oldRect.height()));
+ const QRect newWidgetRect = QRect(q->mapTo(window, QPoint(0, 0)), QSize(newRect.width(), newRect.height()));
+ const QRect sizeGripRect = QRect(window->rect().bottomRight() - QPoint(sizeGripSize, sizeGripSize),
+ window->rect().bottomRight());
+
+ if (sizeGripRect.intersects(oldWidgetRect) || sizeGripRect.intersects(newWidgetRect)) {
+ HIViewSetFrame(view, &bounds);
+ return;
+ }
+ }
+
+ // Don't scroll anything outside the parent widget rect.
+ const QRect scrollRect = (oldRect | newRect) & parentWidget->rect();
+ const HIRect scrollBounds =
+ CGRectMake(scrollRect.x(), scrollRect.y(), scrollRect.width(), scrollRect.height());
+
+ // We cannot scroll when the widget has a mask as that would
+ // scroll the masked out areas too
+ if (qd->extra && qd->extra->hasMask) {
+ HIViewMoveBy(view, dx, dy);
+ return;
+ }
+
+ OSStatus err = HIViewScrollRect(parentView, &scrollBounds, dx, dy);
+ if (err != noErr) {
+ HIViewSetNeedsDisplay(view, true);
+ qWarning("QWidget: Internal error (%s:%d)", __FILE__, __LINE__);
+ }
+ }
+ // Set the view bounds with drawing disabled to prevent repaints.
+ HIViewSetDrawingEnabled(view, false);
+ HIViewSetFrame(view, &bounds);
+ HIViewSetDrawingEnabled(view, true);
+
+ // Update any newly exposed areas due to resizing.
+ const int startx = oldRect.width();
+ const int stopx = newRect.width();
+ const int starty = oldRect.height();
+ const int stopy = newRect.height();
+
+ const HIRect verticalSlice = CGRectMake(startx, 0, stopx , stopy);
+ HIViewSetNeedsDisplayInRect(view, &verticalSlice, true);
+ const HIRect horizontalSlice = CGRectMake(0, starty, startx, stopy);
+ HIViewSetNeedsDisplayInRect(view, &horizontalSlice, true);
+}
+#endif
+
+/*
+ Helper function for non-toplevel widgets. Helps to map Qt's 32bit
+ coordinate system to OS X's 16bit coordinate system.
+
+ Sets the geometry of the widget to data.crect, but clipped to sizes
+ that OS X can handle. Unmaps widgets that are completely outside the
+ valid range.
+
+ Maintains data.wrect, which is the geometry of the OS X widget,
+ measured in this widget's coordinate system.
+
+ if the parent is not clipped, parentWRect is empty, otherwise
+ parentWRect is the geometry of the parent's OS X rect, measured in
+ parent's coord sys
+*/
+void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &oldRect)
+{
+ Q_Q(QWidget);
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+
+ if (!q->internalWinId() && QApplicationPrivate::graphicsSystem() != 0) {
+ // We have no view to move, and no paint engine that
+ // we can update dirty regions on. So just return:
+ return;
+ }
+
+ QMacCocoaAutoReleasePool pool;
+
+ /*
+ There are up to four different coordinate systems here:
+ Qt coordinate system for this widget.
+ X coordinate system for this widget (relative to wrect).
+ Qt coordinate system for parent
+ X coordinate system for parent (relative to parent's wrect).
+ */
+
+ // wrect is the same as crect, except that it is
+ // clipped to fit inside parent (and screen):
+ QRect wrect;
+
+ // wrectInParentCoordSys will be the same as wrect, except that it is
+ // originated in q's parent rather than q itself. It starts out in
+ // parent's Qt coord system, and ends up in parent's coordinate system:
+ QRect wrectInParentCoordSys = data.crect;
+
+ // If q's parent has been clipped, parentWRect will
+ // be filled with the parents clipped crect:
+ QRect parentWRect;
+
+ // Embedded have different meaning on each platform, and on
+ // Mac, it means that q is a QMacNativeWidget.
+ bool isEmbeddedWindow = (q->isWindow() && topData()->embedded);
+#ifdef QT_MAC_USE_COCOA
+ NSView *nsview = qt_mac_nativeview_for(q);
+#endif
+ if (!isEmbeddedWindow) {
+ parentWRect = q->parentWidget()->data->wrect;
+ } else {
+ // INVARIANT: q's parent view is not owned by Qt. So we need to
+ // do some extra calls to get the clipped rect of the parent view:
+#ifndef QT_MAC_USE_COCOA
+ HIViewRef parentView = HIViewGetSuperview(qt_mac_nativeview_for(q));
+#else
+ NSView *parentView = [qt_mac_nativeview_for(q) superview];
+#endif
+ if (parentView) {
+#ifndef QT_MAC_USE_COCOA
+ HIRect tmpRect;
+ HIViewGetFrame(parentView, &tmpRect);
+#else
+ NSRect tmpRect = [parentView frame];
+#endif
+ parentWRect = QRect(tmpRect.origin.x, tmpRect.origin.y,
+ tmpRect.size.width, tmpRect.size.height);
+ } else {
+ const QRect wrectRange(-WRECT_MAX,-WRECT_MAX, 2*WRECT_MAX, 2*WRECT_MAX);
+ parentWRect = wrectRange;
+ }
+ }
+
+ if (parentWRect.isValid()) {
+ // INVARIANT: q's parent has been clipped.
+ // So we fit our own wrects inside it:
+ if (!parentWRect.contains(wrectInParentCoordSys) && !isEmbeddedWindow) {
+ wrectInParentCoordSys &= parentWRect;
+ wrect = wrectInParentCoordSys;
+ // Make sure wrect is originated in q's coordinate system:
+ wrect.translate(-data.crect.topLeft());
+ }
+ // // Make sure wrectInParentCoordSys originated in q's parent coordinate system:
+ wrectInParentCoordSys.translate(-parentWRect.topLeft());
+ } else {
+ // INVARIANT: we dont know yet the clipping rect of q's parent.
+ // So we may or may not have to adjust our wrects:
+
+ if (data.wrect.isValid() && QRect(QPoint(),data.crect.size()).contains(data.wrect)) {
+ // This is where the main optimization is: we have an old wrect from an earlier
+ // setGeometry call, and the new crect is smaller than it. If the final wrect is
+ // also inside the old wrect, we can just move q and its children to the new
+ // location without any clipping:
+
+ // vrect will be the part of q that's will be visible inside
+ // q's parent. If it inside the old wrect, then we can just move:
+ QRect vrect = wrectInParentCoordSys & q->parentWidget()->rect();
+ vrect.translate(-data.crect.topLeft());
+
+ if (data.wrect.contains(vrect)) {
+ wrectInParentCoordSys = data.wrect;
+ wrectInParentCoordSys.translate(data.crect.topLeft());
+#ifndef QT_MAC_USE_COCOA
+ HIRect bounds = CGRectMake(wrectInParentCoordSys.x(), wrectInParentCoordSys.y(),
+ wrectInParentCoordSys.width(), wrectInParentCoordSys.height());
+ HIViewSetFrame(qt_mac_nativeview_for(q), &bounds);
+#else
+ if (nsview) {
+ // INVARIANT: q is native. Set view frame:
+ NSRect bounds = NSMakeRect(wrectInParentCoordSys.x(), wrectInParentCoordSys.y(),
+ wrectInParentCoordSys.width(), wrectInParentCoordSys.height());
+ [nsview setFrame:bounds];
+ } else {
+ // INVARIANT: q is alien. Repaint wrect instead (includes old and new wrect):
+ QWidget *parent = q->parentWidget();
+ QPoint globalPosWRect = parent->mapToGlobal(data.wrect.topLeft());
+
+ QWidget *nativeParent = q->nativeParentWidget();
+ QRect dirtyWRect = QRect(nativeParent->mapFromGlobal(globalPosWRect), data.wrect.size());
+
+ nativeParent->update(dirtyWRect);
+ }
+#endif
+ if (q->testAttribute(Qt::WA_OutsideWSRange)) {
+ q->setAttribute(Qt::WA_OutsideWSRange, false);
+ if (!dontShow) {
+ q->setAttribute(Qt::WA_Mapped);
+#ifndef QT_MAC_USE_COCOA
+ HIViewSetVisible(qt_mac_nativeview_for(q), true);
+#else
+ // If q is Alien, the following call does nothing:
+ [nsview setHidden:NO];
+#endif
+ }
+ }
+ return;
+ }
+ }
+
+#ifndef QT_MAC_USE_COCOA
+ const QRect validRange(-XCOORD_MAX,-XCOORD_MAX, 2*XCOORD_MAX, 2*XCOORD_MAX);
+ if (!validRange.contains(wrectInParentCoordSys)) {
+ // We're too big, and must clip:
+ QPoint screenOffset(0, 0); // offset of the part being on screen
+ const QWidget *parentWidget = q->parentWidget();
+ while (parentWidget && !parentWidget->isWindow()) {
+ screenOffset -= parentWidget->data->crect.topLeft();
+ parentWidget = parentWidget->parentWidget();
+ }
+ QRect cropRect(screenOffset.x() - WRECT_MAX,
+ screenOffset.y() - WRECT_MAX,
+ 2*WRECT_MAX,
+ 2*WRECT_MAX);
+
+ wrectInParentCoordSys &=cropRect;
+ wrect = wrectInParentCoordSys;
+ wrect.translate(-data.crect.topLeft());
+ }
+#endif //QT_MAC_USE_COCOA
+ }
+
+ // unmap if we are outside the valid window system coord system
+ bool outsideRange = !wrectInParentCoordSys.isValid();
+ bool mapWindow = false;
+ if (q->testAttribute(Qt::WA_OutsideWSRange) != outsideRange) {
+ q->setAttribute(Qt::WA_OutsideWSRange, outsideRange);
+ if (outsideRange) {
+#ifndef QT_MAC_USE_COCOA
+ HIViewSetVisible(qt_mac_nativeview_for(q), false);
+#else
+ // If q is Alien, the following call does nothing:
+ [nsview setHidden:YES];
+#endif
+ q->setAttribute(Qt::WA_Mapped, false);
+ } else if (!q->isHidden()) {
+ mapWindow = true;
+ }
+ }
+
+ if (outsideRange)
+ return;
+
+ // Store the new clipped rect:
+ bool jump = (data.wrect != wrect);
+ data.wrect = wrect;
+
+ // and now recursively for all children...
+ // ### can be optimized
+ for (int i = 0; i < children.size(); ++i) {
+ QObject *object = children.at(i);
+ if (object->isWidgetType()) {
+ QWidget *w = static_cast<QWidget *>(object);
+ if (!w->isWindow() && w->testAttribute(Qt::WA_WState_Created))
+ w->d_func()->setWSGeometry();
+ }
+ }
+
+#ifndef QT_MAC_USE_COCOA
+ // Move the actual HIView:
+ qt_mac_update_widget_position(q, oldRect, wrectInParentCoordSys);
+ if (jump)
+ q->update();
+#else
+ if (nsview) {
+ // INVARIANT: q is native. Move the actual NSView:
+ NSRect bounds = NSMakeRect(
+ wrectInParentCoordSys.x(), wrectInParentCoordSys.y(),
+ wrectInParentCoordSys.width(), wrectInParentCoordSys.height());
+ [nsview setFrame:bounds];
+ if (jump)
+ q->update();
+ } else if (QApplicationPrivate::graphicsSystem() == 0){
+ // INVARIANT: q is alien and we use native paint engine.
+ // Schedule updates where q is moved from and to:
+ const QWidget *parent = q->parentWidget();
+ const QPoint globalPosOldWRect = parent->mapToGlobal(oldRect.topLeft());
+ const QPoint globalPosNewWRect = parent->mapToGlobal(wrectInParentCoordSys.topLeft());
+
+ QWidget *nativeParent = q->nativeParentWidget();
+ const QRegion dirtyOldWRect = QRect(nativeParent->mapFromGlobal(globalPosOldWRect), oldRect.size());
+ const QRegion dirtyNewWRect = QRect(nativeParent->mapFromGlobal(globalPosNewWRect), wrectInParentCoordSys.size());
+
+ const bool sizeUnchanged = oldRect.size() == wrectInParentCoordSys.size();
+ const bool posUnchanged = oldRect.topLeft() == wrectInParentCoordSys.topLeft();
+
+ // Resolve/minimize the region that needs to update:
+ if (sizeUnchanged && q->testAttribute(Qt::WA_OpaquePaintEvent)) {
+ // INVARIANT: q is opaque, and is only moved (not resized). So in theory we only
+ // need to blit pixels, and skip a repaint. But we can only make this work if we
+ // had access to the backbuffer, so we need to update all:
+ nativeParent->update(dirtyOldWRect | dirtyNewWRect);
+ } else if (posUnchanged && q->testAttribute(Qt::WA_StaticContents)) {
+ // We only need to redraw exposed areas:
+ nativeParent->update(dirtyNewWRect - dirtyOldWRect);
+ } else {
+ nativeParent->update(dirtyOldWRect | dirtyNewWRect);
+ }
+ }
+#endif
+
+ if (mapWindow && !dontShow) {
+ q->setAttribute(Qt::WA_Mapped);
+#ifndef QT_MAC_USE_COCOA
+ HIViewSetVisible(qt_mac_nativeview_for(q), true);
+#else
+ // If q is Alien, the following call does nothing:
+ [nsview setHidden:NO];
+#endif
+ }
+}
+
+void QWidgetPrivate::adjustWithinMaxAndMinSize(int &w, int &h)
+{
+ if (QWExtra *extra = extraData()) {
+ w = qMin(w, extra->maxw);
+ h = qMin(h, extra->maxh);
+ w = qMax(w, extra->minw);
+ h = qMax(h, extra->minh);
+
+ // Deal with size increment
+ if (QTLWExtra *top = topData()) {
+ if(top->incw) {
+ w = w/top->incw;
+ w *= top->incw;
+ }
+ if(top->inch) {
+ h = h/top->inch;
+ h *= top->inch;
+ }
+ }
+ }
+
+ if (isRealWindow()) {
+ w = qMax(0, w);
+ h = qMax(0, h);
+ }
+}
+
+void QWidgetPrivate::applyMaxAndMinSizeOnWindow()
+{
+ Q_Q(QWidget);
+ QMacCocoaAutoReleasePool pool;
+
+ const float max_f(20000);
+#ifndef QT_MAC_USE_COCOA
+#define SF(x) ((x > max_f) ? max_f : x)
+ HISize max = CGSizeMake(SF(extra->maxw), SF(extra->maxh));
+ HISize min = CGSizeMake(SF(extra->minw), SF(extra->minh));
+#undef SF
+ SetWindowResizeLimits(qt_mac_window_for(q), &min, &max);
+#else
+#define SF(x) ((x > max_f) ? max_f : x)
+ NSSize max = NSMakeSize(SF(extra->maxw), SF(extra->maxh));
+ NSSize min = NSMakeSize(SF(extra->minw), SF(extra->minh));
+#undef SF
+ [qt_mac_window_for(q) setContentMinSize:min];
+ [qt_mac_window_for(q) setContentMaxSize:max];
+#endif
+}
+
+void QWidgetPrivate::setGeometry_sys(int x, int y, int w, int h, bool isMove)
+{
+ Q_Q(QWidget);
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+
+ if(q->windowType() == Qt::Desktop)
+ return;
+
+ QMacCocoaAutoReleasePool pool;
+ bool realWindow = isRealWindow();
+
+ if (realWindow && !q->testAttribute(Qt::WA_DontShowOnScreen)){
+ adjustWithinMaxAndMinSize(w, h);
+#ifndef QT_MAC_USE_COCOA
+ if (w != 0 && h != 0) {
+ topData()->isSetGeometry = 1;
+ topData()->isMove = isMove;
+ Rect r; SetRect(&r, x, y, x + w, y + h);
+ SetWindowBounds(qt_mac_window_for(q), kWindowContentRgn, &r);
+ topData()->isSetGeometry = 0;
+ } else {
+ setGeometry_sys_helper(x, y, w, h, isMove);
+ }
+#else
+ if (!isMove && !q->testAttribute(Qt::WA_Moved) && !q->isVisible()) {
+ // INVARIANT: The location of the window has not yet been set. The default will
+ // instead be to center it on the desktop, or over the parent, if any. Since we now
+ // resize the window, we need to adjust the top left position to keep the window
+ // centeralized. And we need to to this now (and before show) in case the positioning
+ // of other windows (e.g. sub-windows) depend on this position:
+ if (QWidget *p = q->parentWidget()) {
+ x = p->geometry().center().x() - (w / 2);
+ y = p->geometry().center().y() - (h / 2);
+ } else {
+ QRect availGeo = QApplication::desktop()->availableGeometry(q);
+ x = availGeo.center().x() - (w / 2);
+ y = availGeo.center().y() - (h / 2);
+ }
+ }
+
+ QSize olds = q->size();
+ const bool isResize = (olds != QSize(w, h));
+ NSWindow *window = qt_mac_window_for(q);
+ const QRect &fStrut = frameStrut();
+ const QRect frameRect(QPoint(x - fStrut.left(), y - fStrut.top()),
+ QSize(fStrut.left() + fStrut.right() + w,
+ fStrut.top() + fStrut.bottom() + h));
+ NSRect cocoaFrameRect = NSMakeRect(frameRect.x(), flipYCoordinate(frameRect.bottom() + 1),
+ frameRect.width(), frameRect.height());
+ // The setFrame call will trigger a 'windowDidResize' notification for the corresponding
+ // NSWindow. The pending flag is set, so that the resize event can be send as non-spontaneous.
+ if (isResize)
+ q->setAttribute(Qt::WA_PendingResizeEvent);
+ QPoint currTopLeft = data.crect.topLeft();
+ if (currTopLeft.x() == x && currTopLeft.y() == y
+ && cocoaFrameRect.size.width != 0
+ && cocoaFrameRect.size.height != 0) {
+ [window setFrame:cocoaFrameRect display:realWindow];
+ } else {
+ // The window is moved and resized (or resized to zero).
+ // Since Cocoa usually only sends us a resize callback after
+ // setting a window frame, we issue an explicit move as
+ // well. To stop Cocoa from optimize away the move (since the move
+ // would have the same origin as the setFrame call) we shift the
+ // window back and forth inbetween.
+ cocoaFrameRect.origin.y += 1;
+ [window setFrame:cocoaFrameRect display:realWindow];
+ cocoaFrameRect.origin.y -= 1;
+ [window setFrameOrigin:cocoaFrameRect.origin];
+ }
+#endif
+ } else {
+ setGeometry_sys_helper(x, y, w, h, isMove);
+ }
+
+ topLevelAt_cache = 0;
+}
+
+void QWidgetPrivate::setGeometry_sys_helper(int x, int y, int w, int h, bool isMove)
+{
+ Q_Q(QWidget);
+ bool realWindow = isRealWindow();
+
+ QPoint oldp = q->pos();
+ QSize olds = q->size();
+ // Apply size restrictions, applicable for Windows & Widgets.
+ if (QWExtra *extra = extraData()) {
+ w = qBound(extra->minw, w, extra->maxw);
+ h = qBound(extra->minh, h, extra->maxh);
+ }
+ const bool isResize = (olds != QSize(w, h));
+
+ if (!realWindow && !isResize && QPoint(x, y) == oldp)
+ return;
+
+ if (isResize)
+ data.window_state = data.window_state & ~Qt::WindowMaximized;
+
+ const bool visible = q->isVisible();
+ data.crect = QRect(x, y, w, h);
+
+ if (realWindow) {
+ adjustWithinMaxAndMinSize(w, h);
+ qt_mac_update_sizer(q);
+
+#ifndef QT_MAC_USE_COCOA
+ if (q->windowFlags() & Qt::WindowMaximizeButtonHint) {
+ OSWindowRef window = qt_mac_window_for(q);
+ if (extra->maxw && extra->maxh && extra->maxw == extra->minw
+ && extra->maxh == extra->minh) {
+ ChangeWindowAttributes(window, kWindowNoAttributes, kWindowFullZoomAttribute);
+ } else {
+ ChangeWindowAttributes(window, kWindowFullZoomAttribute, kWindowNoAttributes);
+ }
+ }
+ HIRect bounds = CGRectMake(0, 0, w, h);
+ HIViewSetFrame(qt_mac_nativeview_for(q), &bounds);
+#else
+ [qt_mac_nativeview_for(q) setFrame:NSMakeRect(0, 0, w, h)];
+#endif
+ } else {
+ const QRect oldRect(oldp, olds);
+ if (!isResize && QApplicationPrivate::graphicsSystem())
+ moveRect(oldRect, x - oldp.x(), y - oldp.y());
+
+ setWSGeometry(false, oldRect);
+
+ if (isResize && QApplicationPrivate::graphicsSystem())
+ invalidateBuffer_resizeHelper(oldp, olds);
+ }
+
+ if(isMove || isResize) {
+ if(!visible) {
+ if(isMove && q->pos() != oldp)
+ q->setAttribute(Qt::WA_PendingMoveEvent, true);
+ if(isResize)
+ q->setAttribute(Qt::WA_PendingResizeEvent, true);
+ } else {
+ if(isResize) { //send the resize event..
+ QResizeEvent e(q->size(), olds);
+ QApplication::sendEvent(q, &e);
+ }
+ if(isMove && q->pos() != oldp) { //send the move event..
+ QMoveEvent e(q->pos(), oldp);
+ QApplication::sendEvent(q, &e);
+ }
+ }
+ }
+ qt_event_request_window_change(q);
+}
+
+void QWidgetPrivate::setConstraints_sys()
+{
+ updateMaximizeButton_sys();
+ applyMaxAndMinSizeOnWindow();
+}
+
+void QWidgetPrivate::updateMaximizeButton_sys()
+{
+ Q_Q(QWidget);
+ if (q->data->window_flags & Qt::CustomizeWindowHint)
+ return;
+
+ OSWindowRef window = qt_mac_window_for(q);
+ QTLWExtra * tlwExtra = topData();
+#ifdef QT_MAC_USE_COCOA
+ QMacCocoaAutoReleasePool pool;
+ NSButton *maximizeButton = [window standardWindowButton:NSWindowZoomButton];
+#endif
+ if (extra->maxw && extra->maxh
+ && extra->maxw == extra->minw
+ && extra->maxh == extra->minh) {
+ // The window has a fixed size, so gray out the maximize button:
+ if (!tlwExtra->savedWindowAttributesFromMaximized) {
+#ifndef QT_MAC_USE_COCOA
+ GetWindowAttributes(window,
+ (WindowAttributes*)&extra->topextra->savedWindowAttributesFromMaximized);
+
+#else
+ tlwExtra->savedWindowAttributesFromMaximized = (![maximizeButton isHidden] && [maximizeButton isEnabled]);
+#endif
+ }
+#ifndef QT_MAC_USE_COCOA
+ ChangeWindowAttributes(window, kWindowNoAttributes, kWindowFullZoomAttribute);
+#else
+ [maximizeButton setEnabled:NO];
+#endif
+
+
+ } else {
+ if (tlwExtra->savedWindowAttributesFromMaximized) {
+#ifndef QT_MAC_USE_COCOA
+ ChangeWindowAttributes(window,
+ extra->topextra->savedWindowAttributesFromMaximized,
+ kWindowNoAttributes);
+#else
+ [maximizeButton setEnabled:YES];
+#endif
+ tlwExtra->savedWindowAttributesFromMaximized = 0;
+ }
+ }
+
+
+}
+
+void QWidgetPrivate::scroll_sys(int dx, int dy)
+{
+ if (QApplicationPrivate::graphicsSystem() && !paintOnScreen()) {
+ // INVARIANT: Alien paint engine
+ scrollChildren(dx, dy);
+ scrollRect(q_func()->rect(), dx, dy);
+ } else {
+ scroll_sys(dx, dy, QRect());
+ }
+}
+
+void QWidgetPrivate::scroll_sys(int dx, int dy, const QRect &qscrollRect)
+{
+ if (QMacScrollOptimization::delayScroll(this, dx, dy, qscrollRect))
+ return;
+
+ Q_Q(QWidget);
+ if (QApplicationPrivate::graphicsSystem() && !paintOnScreen()) {
+ // INVARIANT: Alien paint engine
+ scrollRect(qscrollRect, dx, dy);
+ return;
+ }
+
+ static int accelEnv = -1;
+ if (accelEnv == -1) {
+ accelEnv = qgetenv("QT_NO_FAST_SCROLL").toInt() == 0;
+ }
+
+ // Scroll the whole widget if qscrollRect is not valid:
+ QRect validScrollRect = qscrollRect.isValid() ? qscrollRect : q->rect();
+ validScrollRect &= clipRect();
+
+ // If q is overlapped by other widgets, we cannot just blit pixels since
+ // this will move overlapping widgets as well. In case we just update:
+ const bool overlapped = isOverlapped(validScrollRect.translated(data.crect.topLeft()));
+ const bool accelerateScroll = accelEnv && isOpaque && !overlapped;
+ const bool isAlien = (q->internalWinId() == 0);
+ const QPoint scrollDelta(dx, dy);
+
+ // If qscrollRect is valid, we are _not_ supposed to scroll q's children (as documented).
+ // But we do scroll children (and the whole of q) if qscrollRect is invalid. This case is
+ // documented as undefined, but we exploit it to help factor our code into one function.
+ const bool scrollChildren = !qscrollRect.isValid();
+
+ if (!q->updatesEnabled()) {
+ // We are told not to update anything on q at this point. So unless
+ // we are supposed to scroll children, we bail out early:
+ if (!scrollChildren || q->children().isEmpty())
+ return;
+ }
+
+ if (!accelerateScroll) {
+ if (overlapped) {
+ QRegion region(validScrollRect);
+ subtractOpaqueSiblings(region);
+ update_sys(region);
+ }else {
+ update_sys(qscrollRect);
+ }
+ return;
+ }
+
+#ifdef QT_MAC_USE_COCOA
+ QMacCocoaAutoReleasePool pool;
+#else
+ Q_UNUSED(isAlien);
+ // We're not sure what the following call is supposed to achive
+ // but until we see what it breaks, we don't bring it into the
+ // Cocoa port:
+ qt_event_request_window_change(q);
+#endif
+
+ // First move all native children. Alien children will indirectly be
+ // moved when the parent is scrolled. All directly or indirectly moved
+ // children will receive a move event before the function call returns.
+ QWidgetList movedChildren;
+ if (scrollChildren) {
+ QObjectList children = q->children();
+
+ for (int i=0; i<children.size(); i++) {
+ QObject *obj = children.at(i);
+ if (QWidget *w = qobject_cast<QWidget*>(obj)) {
+ if (!w->isWindow()) {
+ w->data->crect = QRect(w->pos() + scrollDelta, w->size());
+#ifndef QT_MAC_USE_COCOA
+ if (w->testAttribute(Qt::WA_WState_Created)) {
+ HIRect bounds = CGRectMake(w->data->crect.x(), w->data->crect.y(),
+ w->data->crect.width(), w->data->crect.height());
+ HIViewRef hiview = qt_mac_nativeview_for(w);
+ const bool opaque = q->testAttribute(Qt::WA_OpaquePaintEvent);
+
+ if (opaque)
+ HIViewSetDrawingEnabled(hiview, false);
+ HIViewSetFrame(hiview, &bounds);
+ if (opaque)
+ HIViewSetDrawingEnabled(hiview, true);
+ }
+#else
+ if (NSView *view = qt_mac_nativeview_for(w)) {
+ // INVARIANT: w is not alien
+ [view setFrame:NSMakeRect(
+ w->data->crect.x(), w->data->crect.y(),
+ w->data->crect.width(), w->data->crect.height())];
+ }
+#endif
+ movedChildren.append(w);
+ }
+ }
+ }
+ }
+
+ if (q->testAttribute(Qt::WA_WState_Created) && q->isVisible()) {
+ // Scroll q itself according to the qscrollRect, and
+ // call update on any exposed areas so that they get redrawn:
+
+#ifndef QT_MAC_USE_COCOA
+ OSViewRef view = qt_mac_nativeview_for(q);
+ HIRect scrollrect = CGRectMake(qscrollRect.x(), qscrollRect.y(), qscrollRect.width(), qscrollRect.height());
+ OSStatus err = _HIViewScrollRectWithOptions(view, qscrollRect.isValid() ? &scrollrect : 0, dx, dy, kHIViewScrollRectAdjustInvalid);
+ if (err) {
+ // The only parameter that can go wrong, is the rect.
+ qWarning("QWidget::scroll: Your rectangle was too big for the widget, clipping rect");
+ scrollrect = CGRectMake(qMax(qscrollRect.x(), 0), qMax(qscrollRect.y(), 0),
+ qMin(qscrollRect.width(), q->width()), qMin(qscrollRect.height(), q->height()));
+ _HIViewScrollRectWithOptions(view, qscrollRect.isValid() ? &scrollrect : 0, dx, dy, kHIViewScrollRectAdjustInvalid);
+ }
+#else
+
+ QWidget *nativeWidget = isAlien ? q->nativeParentWidget() : q;
+ if (!nativeWidget)
+ return;
+ OSViewRef view = qt_mac_nativeview_for(nativeWidget);
+ if (!view)
+ return;
+
+ // Calculate the rectangles that needs to be redrawn
+ // after the scroll. This will be source rect minus destination rect:
+ QRect deltaXRect;
+ if (dx != 0) {
+ deltaXRect.setY(validScrollRect.y());
+ deltaXRect.setHeight(validScrollRect.height());
+ if (dx > 0) {
+ deltaXRect.setX(validScrollRect.x());
+ deltaXRect.setWidth(dx);
+ } else {
+ deltaXRect.setX(validScrollRect.x() + validScrollRect.width() + dx);
+ deltaXRect.setWidth(-dx);
+ }
+ }
+
+ QRect deltaYRect;
+ if (dy != 0) {
+ deltaYRect.setX(validScrollRect.x());
+ deltaYRect.setWidth(validScrollRect.width());
+ if (dy > 0) {
+ deltaYRect.setY(validScrollRect.y());
+ deltaYRect.setHeight(dy);
+ } else {
+ deltaYRect.setY(validScrollRect.y() + validScrollRect.height() + dy);
+ deltaYRect.setHeight(-dy);
+ }
+ }
+
+ if (isAlien) {
+ // Adjust the scroll rect to the location as seen from the native parent:
+ QPoint scrollTopLeftInsideNative = nativeWidget->mapFromGlobal(q->mapToGlobal(validScrollRect.topLeft()));
+ validScrollRect.moveTo(scrollTopLeftInsideNative);
+ }
+
+ // Make the pixel copy rect within the validScrollRect bounds:
+ NSRect nsscrollRect = NSMakeRect(
+ validScrollRect.x() + (dx < 0 ? -dx : 0),
+ validScrollRect.y() + (dy < 0 ? -dy : 0),
+ validScrollRect.width() + (dx > 0 ? -dx : 0),
+ validScrollRect.height() + (dy > 0 ? -dy : 0));
+
+ NSSize deltaSize = NSMakeSize(dx, dy);
+ [view scrollRect:nsscrollRect by:deltaSize];
+
+ // Some areas inside the scroll rect might have been marked as dirty from before, which
+ // means that they are scheduled to be redrawn. But as we now scroll, those dirty rects
+ // should also move along to ensure that q receives repaints on the correct places.
+ // Since some of the dirty rects might lay outside, or only intersect with, the scroll
+ // rect, the old calls to setNeedsDisplay still makes sense.
+ // NB: Using [view translateRectsNeedingDisplayInRect:nsscrollRect by:deltaSize] have
+ // so far not been proven fruitful to solve this problem.
+ const QVector<QRect> &dirtyRectsToScroll = dirtyOnWidget.rects();
+ for (int i=0; i<dirtyRectsToScroll.size(); ++i) {
+ QRect qdirtyRect = dirtyRectsToScroll[i];
+ qdirtyRect.translate(dx, dy);
+ update_sys(qdirtyRect);
+ }
+
+ // Update newly exposed areas. This will generate new dirty areas on
+ // q, and therefore, we do it after updating the old dirty rects above:
+ if (dx != 0)
+ update_sys(deltaXRect);
+ if (dy != 0)
+ update_sys(deltaYRect);
+
+#endif // QT_MAC_USE_COCOA
+ }
+
+ for (int i=0; i<movedChildren.size(); i++) {
+ QWidget *w = movedChildren.at(i);
+ QMoveEvent e(w->pos(), w->pos() - scrollDelta);
+ QApplication::sendEvent(w, &e);
+ }
+}
+
+int QWidget::metric(PaintDeviceMetric m) const
+{
+ switch(m) {
+ case PdmHeightMM:
+ return qRound(metric(PdmHeight) * 25.4 / qreal(metric(PdmDpiY)));
+ case PdmWidthMM:
+ return qRound(metric(PdmWidth) * 25.4 / qreal(metric(PdmDpiX)));
+ case PdmHeight:
+ case PdmWidth:
+#ifndef QT_MAC_USE_COCOA
+ { HIRect rect;
+ HIViewGetFrame(qt_mac_nativeview_for(this), &rect);
+ if(m == PdmWidth)
+ return (int)rect.size.width;
+ return (int)rect.size.height; }
+#else
+ if (m == PdmWidth)
+ return data->crect.width();
+ else
+ return data->crect.height();
+#endif
+ case PdmDepth:
+ return 32;
+ case PdmNumColors:
+ return INT_MAX;
+ case PdmDpiX:
+ case PdmPhysicalDpiX: {
+ Q_D(const QWidget);
+ if (d->extra && d->extra->customDpiX)
+ return d->extra->customDpiX;
+ else if (d->parent)
+ return static_cast<QWidget *>(d->parent)->metric(m);
+ extern float qt_mac_defaultDpi_x(); //qpaintdevice_mac.cpp
+ return int(qt_mac_defaultDpi_x()); }
+ case PdmDpiY:
+ case PdmPhysicalDpiY: {
+ Q_D(const QWidget);
+ if (d->extra && d->extra->customDpiY)
+ return d->extra->customDpiY;
+ else if (d->parent)
+ return static_cast<QWidget *>(d->parent)->metric(m);
+ extern float qt_mac_defaultDpi_y(); //qpaintdevice_mac.cpp
+ return int(qt_mac_defaultDpi_y()); }
+ default: //leave this so the compiler complains when new ones are added
+ qWarning("QWidget::metric: Unhandled parameter %d", m);
+ return QPaintDevice::metric(m);
+ }
+ return 0;
+}
+
+void QWidgetPrivate::createSysExtra()
+{
+#ifdef QT_MAC_USE_COCOA
+ extra->imageMask = 0;
+#endif
+}
+
+void QWidgetPrivate::deleteSysExtra()
+{
+#ifdef QT_MAC_USE_COCOA
+ if (extra->imageMask)
+ CFRelease(extra->imageMask);
+#endif
+}
+
+void QWidgetPrivate::createTLSysExtra()
+{
+ extra->topextra->resizer = 0;
+ extra->topextra->isSetGeometry = 0;
+ extra->topextra->isMove = 0;
+ extra->topextra->wattr = 0;
+ extra->topextra->wclass = 0;
+ extra->topextra->group = 0;
+ extra->topextra->windowIcon = 0;
+ extra->topextra->savedWindowAttributesFromMaximized = 0;
+}
+
+void QWidgetPrivate::deleteTLSysExtra()
+{
+#ifndef QT_MAC_USE_COCOA
+ if (extra->topextra->group) {
+ qt_mac_release_window_group(extra->topextra->group);
+ extra->topextra->group = 0;
+ }
+ if (extra->topextra->windowIcon) {
+ ReleaseIconRef(extra->topextra->windowIcon);
+ extra->topextra->windowIcon = 0;
+ }
+#endif
+}
+
+void QWidgetPrivate::updateFrameStrut()
+{
+ Q_Q(QWidget);
+
+ QWidgetPrivate *that = const_cast<QWidgetPrivate*>(this);
+
+ that->data.fstrut_dirty = false;
+ QTLWExtra *top = that->topData();
+
+#if QT_MAC_USE_COCOA
+ // 1 Get the window frame
+ OSWindowRef oswnd = qt_mac_window_for(q);
+ NSRect frameW = [oswnd frame];
+ // 2 Get the content frame - so now
+ NSRect frameC = [oswnd contentRectForFrameRect:frameW];
+ top->frameStrut.setCoords(frameC.origin.x - frameW.origin.x,
+ (frameW.origin.y + frameW.size.height) - (frameC.origin.y + frameC.size.height),
+ (frameW.origin.x + frameW.size.width) - (frameC.origin.x + frameC.size.width),
+ frameC.origin.y - frameW.origin.y);
+#else
+ Rect window_r;
+ GetWindowStructureWidths(qt_mac_window_for(q), &window_r);
+ top->frameStrut.setCoords(window_r.left, window_r.top, window_r.right, window_r.bottom);
+#endif
+}
+
+void QWidgetPrivate::registerDropSite(bool on)
+{
+ Q_Q(QWidget);
+ if (!q->testAttribute(Qt::WA_WState_Created))
+ return;
+#ifndef QT_MAC_USE_COCOA
+ SetControlDragTrackingEnabled(qt_mac_nativeview_for(q), on);
+#else
+ NSWindow *win = qt_mac_window_for(q);
+ if (on) {
+ if ([win isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaWindow) class]])
+ [static_cast<QT_MANGLE_NAMESPACE(QCocoaWindow) *>(win) registerDragTypes];
+ else if ([win isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaPanel) class]])
+ [static_cast<QT_MANGLE_NAMESPACE(QCocoaPanel) *>(win) registerDragTypes];
+ }
+#endif
+}
+
+void QWidgetPrivate::registerTouchWindow(bool enable)
+{
+ Q_UNUSED(enable);
+#ifdef QT_MAC_USE_COCOA
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
+ if (QSysInfo::MacintoshVersion < QSysInfo::MV_10_6)
+ return;
+
+ Q_Q(QWidget);
+ if (enable == touchEventsEnabled)
+ return;
+
+ QT_MANGLE_NAMESPACE(QCocoaView) *view = static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(qt_mac_effectiveview_for(q));
+ if (!view)
+ return;
+
+ if (enable) {
+ ++view->alienTouchCount;
+ if (view->alienTouchCount == 1) {
+ touchEventsEnabled = true;
+ [view setAcceptsTouchEvents:YES];
+ }
+ } else {
+ --view->alienTouchCount;
+ if (view->alienTouchCount == 0) {
+ touchEventsEnabled = false;
+ [view setAcceptsTouchEvents:NO];
+ }
+ }
+#endif
+#endif
+}
+
+void QWidgetPrivate::setMask_sys(const QRegion &region)
+{
+ Q_UNUSED(region);
+ Q_Q(QWidget);
+
+#ifndef QT_MAC_USE_COCOA
+ if (q->isWindow())
+ ReshapeCustomWindow(qt_mac_window_for(q));
+ else
+ HIViewReshapeStructure(qt_mac_nativeview_for(q));
+#else
+ if (!q->internalWinId())
+ return;
+
+ if (extra->mask.isEmpty()) {
+ extra->maskBits = QImage();
+ finishCocoaMaskSetup();
+ } else {
+ syncCocoaMask();
+ }
+
+ topLevelAt_cache = 0;
+#endif
+}
+
+void QWidgetPrivate::setWindowOpacity_sys(qreal level)
+{
+ Q_Q(QWidget);
+
+ if (!q->isWindow())
+ return;
+
+ level = qBound(0.0, level, 1.0);
+ topData()->opacity = (uchar)(level * 255);
+ if (!q->testAttribute(Qt::WA_WState_Created))
+ return;
+
+ OSWindowRef oswindow = qt_mac_window_for(q);
+#if QT_MAC_USE_COCOA
+ [oswindow setAlphaValue:level];
+#else
+ SetWindowAlpha(oswindow, level);
+#endif
+}
+
+#ifdef QT_MAC_USE_COCOA
+void QWidgetPrivate::syncCocoaMask()
+{
+ Q_Q(QWidget);
+ if (!q->testAttribute(Qt::WA_WState_Created) || !extra)
+ return;
+
+ if (extra->hasMask) {
+ if(extra->maskBits.size() != q->size()) {
+ extra->maskBits = QImage(q->size(), QImage::Format_Mono);
+ }
+ extra->maskBits.fill(QColor(Qt::color1).rgba());
+ extra->maskBits.setNumColors(2);
+ extra->maskBits.setColor(0, QColor(Qt::color0).rgba());
+ extra->maskBits.setColor(1, QColor(Qt::color1).rgba());
+ QPainter painter(&extra->maskBits);
+ painter.setBrush(Qt::color1);
+ painter.setPen(Qt::NoPen);
+ painter.drawRects(extra->mask.rects());
+ painter.end();
+ finishCocoaMaskSetup();
+ }
+}
+
+void QWidgetPrivate::finishCocoaMaskSetup()
+{
+ Q_Q(QWidget);
+
+ if (!q->testAttribute(Qt::WA_WState_Created) || !extra)
+ return;
+
+ // Technically this is too late to release, because the data behind the image
+ // has already been released. But it's more tidy to do it here.
+ // If you are seeing a crash, consider doing a CFRelease before changing extra->maskBits.
+ if (extra->imageMask) {
+ CFRelease(extra->imageMask);
+ extra->imageMask = 0;
+ }
+
+ if (!extra->maskBits.isNull()) {
+ QCFType<CGDataProviderRef> dataProvider = CGDataProviderCreateWithData(0,
+ extra->maskBits.bits(),
+ extra->maskBits.numBytes(),
+ 0); // shouldn't need to release.
+ CGFloat decode[2] = {1, 0};
+ extra->imageMask = CGImageMaskCreate(extra->maskBits.width(), extra->maskBits.height(),
+ 1, 1, extra->maskBits.bytesPerLine(), dataProvider,
+ decode, false);
+ }
+ if (q->isWindow()) {
+ NSWindow *window = qt_mac_window_for(q);
+ [window setOpaque:(extra->imageMask == 0)];
+ [window invalidateShadow];
+ }
+ macSetNeedsDisplay(QRegion());
+}
+#endif
+
+struct QPaintEngineCleanupHandler
+{
+ inline QPaintEngineCleanupHandler() : engine(0) {}
+ inline ~QPaintEngineCleanupHandler() { delete engine; }
+ QPaintEngine *engine;
+};
+
+Q_GLOBAL_STATIC(QPaintEngineCleanupHandler, engineHandler)
+
+QPaintEngine *QWidget::paintEngine() const
+{
+ QPaintEngine *&pe = engineHandler()->engine;
+ if (!pe)
+ pe = new QCoreGraphicsPaintEngine();
+ if (pe->isActive()) {
+ QPaintEngine *engine = new QCoreGraphicsPaintEngine();
+ engine->setAutoDestruct(true);
+ return engine;
+ }
+ return pe;
+}
+
+void QWidgetPrivate::setModal_sys()
+{
+ Q_Q(QWidget);
+ if (!q->testAttribute(Qt::WA_WState_Created) || !q->isWindow())
+ return;
+ const QWidget * const windowParent = q->window()->parentWidget();
+ const QWidget * const primaryWindow = windowParent ? windowParent->window() : 0;
+ OSWindowRef windowRef = qt_mac_window_for(q);
+
+#ifdef QT_MAC_USE_COCOA
+ QMacCocoaAutoReleasePool pool;
+ bool alreadySheet = [windowRef styleMask] & NSDocModalWindowMask;
+
+ if (windowParent && q->windowModality() == Qt::WindowModal){
+ // INVARIANT: Window should be window-modal (which implies a sheet).
+ if (!alreadySheet) {
+ // NB: the following call will call setModal_sys recursivly:
+ recreateMacWindow();
+ windowRef = qt_mac_window_for(q);
+ }
+ if ([windowRef isKindOfClass:[NSPanel class]]){
+ // If the primary window of the sheet parent is a child of a modal dialog,
+ // the sheet parent should not be modally shaddowed.
+ // This goes for the sheet as well:
+ OSWindowRef ref = primaryWindow ? qt_mac_window_for(primaryWindow) : 0;
+ bool isDialog = ref ? [ref isKindOfClass:[NSPanel class]] : false;
+ bool worksWhenModal = isDialog ? [static_cast<NSPanel *>(ref) worksWhenModal] : false;
+ if (worksWhenModal)
+ [static_cast<NSPanel *>(windowRef) setWorksWhenModal:YES];
+ }
+ } else {
+ // INVARIANT: Window shold _not_ be window-modal (and as such, not a sheet).
+ if (alreadySheet){
+ // NB: the following call will call setModal_sys recursivly:
+ recreateMacWindow();
+ windowRef = qt_mac_window_for(q);
+ }
+ if (q->windowModality() == Qt::NonModal
+ && primaryWindow && primaryWindow->windowModality() == Qt::ApplicationModal) {
+ // INVARIANT: Our window has a parent that is application modal.
+ // This means that q is supposed to be on top of this window and
+ // not be modally shaddowed:
+ if ([windowRef isKindOfClass:[NSPanel class]])
+ [static_cast<NSPanel *>(windowRef) setWorksWhenModal:YES];
+ }
+ }
+
+#else
+ const bool primaryWindowModal = primaryWindow ? primaryWindow->testAttribute(Qt::WA_ShowModal) : false;
+ const bool modal = q->testAttribute(Qt::WA_ShowModal);
+
+ WindowClass old_wclass;
+ GetWindowClass(windowRef, &old_wclass);
+
+ if (modal || primaryWindowModal) {
+ if (q->windowModality() == Qt::WindowModal
+ || (primaryWindow && primaryWindow->windowModality() == Qt::WindowModal)){
+ // Window should be window-modal (which implies a sheet).
+ if (old_wclass != kSheetWindowClass){
+ // We cannot convert a created window to a sheet.
+ // So we recreate the window:
+ recreateMacWindow();
+ return;
+ }
+ } else {
+ // Window should be application-modal (which implies NOT using a sheet).
+ if (old_wclass == kSheetWindowClass){
+ // We cannot convert a sheet to a window.
+ // So we recreate the window:
+ recreateMacWindow();
+ return;
+ } else if (!(q->data->window_flags & Qt::CustomizeWindowHint)) {
+ if (old_wclass == kDocumentWindowClass || old_wclass == kFloatingWindowClass || old_wclass == kUtilityWindowClass){
+ // Only change the class to kMovableModalWindowClass if the no explicit jewels
+ // are set (kMovableModalWindowClass can't contain them), and the current window class
+ // can be converted to modal (according to carbon doc). Mind the order of
+ // HIWindowChangeClass and ChangeWindowAttributes.
+ WindowGroupRef group = GetWindowGroup(windowRef);
+ HIWindowChangeClass(windowRef, kMovableModalWindowClass);
+ quint32 tmpWattr = kWindowCloseBoxAttribute | kWindowHorizontalZoomAttribute;
+ ChangeWindowAttributes(windowRef, tmpWattr, kWindowNoAttributes);
+ ChangeWindowAttributes(windowRef, kWindowNoAttributes, tmpWattr);
+ // If the window belongs to a qt-created group, set that group once more:
+ if (data.window_flags & Qt::WindowStaysOnTopHint
+ || q->windowType() == Qt::Popup
+ || q->windowType() == Qt::ToolTip)
+ SetWindowGroup(windowRef, group);
+ }
+ // Popups are usually handled "special" and are never modal.
+ Qt::WindowType winType = q->windowType();
+ if (winType != Qt::Popup && winType != Qt::ToolTip)
+ SetWindowModality(windowRef, kWindowModalityAppModal, 0);
+ }
+ }
+ } else if (windowRef) {
+ if (old_wclass == kSheetWindowClass){
+ // Converting a sheet to a window is complex. It's easier to recreate:
+ recreateMacWindow();
+ return;
+ }
+
+ SetWindowModality(windowRef, kWindowModalityNone, 0);
+ if (!(q->data->window_flags & Qt::CustomizeWindowHint)) {
+ if (q->window()->d_func()->topData()->wattr |= kWindowCloseBoxAttribute)
+ ChangeWindowAttributes(windowRef, kWindowCloseBoxAttribute, kWindowNoAttributes);
+ if (q->window()->d_func()->topData()->wattr |= kWindowHorizontalZoomAttribute)
+ ChangeWindowAttributes(windowRef, kWindowHorizontalZoomAttribute, kWindowNoAttributes);
+ if (q->window()->d_func()->topData()->wattr |= kWindowCollapseBoxAttribute)
+ ChangeWindowAttributes(windowRef, kWindowCollapseBoxAttribute, kWindowNoAttributes);
+ }
+
+ WindowClass newClass = q->window()->d_func()->topData()->wclass;
+ if (old_wclass != newClass && newClass != 0){
+ WindowGroupRef group = GetWindowGroup(windowRef);
+ HIWindowChangeClass(windowRef, newClass);
+ // If the window belongs to a qt-created group, set that group once more:
+ if (data.window_flags & Qt::WindowStaysOnTopHint
+ || q->windowType() == Qt::Popup
+ || q->windowType() == Qt::ToolTip)
+ SetWindowGroup(windowRef, group);
+ }
+ }
+
+ // Make sure that HIWindowChangeClass didn't remove drag support
+ // or reset the opaque size grip setting:
+ SetAutomaticControlDragTrackingEnabledForWindow(windowRef, true);
+ macUpdateOpaqueSizeGrip();
+#endif
+}
+
+void QWidgetPrivate::macUpdateHideOnSuspend()
+{
+ Q_Q(QWidget);
+ if (!q->testAttribute(Qt::WA_WState_Created) || !q->isWindow() || q->windowType() != Qt::Tool)
+ return;
+#ifndef QT_MAC_USE_COCOA
+ if(q->testAttribute(Qt::WA_MacAlwaysShowToolWindow))
+ ChangeWindowAttributes(qt_mac_window_for(q), 0, kWindowHideOnSuspendAttribute);
+ else
+ ChangeWindowAttributes(qt_mac_window_for(q), kWindowHideOnSuspendAttribute, 0);
+#else
+ if(q->testAttribute(Qt::WA_MacAlwaysShowToolWindow))
+ [qt_mac_window_for(q) setHidesOnDeactivate:NO];
+ else
+ [qt_mac_window_for(q) setHidesOnDeactivate:YES];
+#endif
+}
+
+void QWidgetPrivate::macUpdateOpaqueSizeGrip()
+{
+ Q_Q(QWidget);
+
+ if (!q->testAttribute(Qt::WA_WState_Created) || !q->isWindow())
+ return;
+
+#ifndef QT_MAC_USE_COCOA // Growbox is always transparent on Cocoa. Can emulate with setting a QSizeGrip
+ HIViewRef growBox;
+ HIViewFindByID(HIViewGetRoot(qt_mac_window_for(q)), kHIViewWindowGrowBoxID, &growBox);
+ if (!growBox)
+ return;
+ HIGrowBoxViewSetTransparent(growBox, !q->testAttribute(Qt::WA_MacOpaqueSizeGrip));
+#endif
+}
+
+void QWidgetPrivate::macUpdateSizeAttribute()
+{
+ Q_Q(QWidget);
+ QEvent event(QEvent::MacSizeChange);
+ QApplication::sendEvent(q, &event);
+ for (int i = 0; i < children.size(); ++i) {
+ QWidget *w = qobject_cast<QWidget *>(children.at(i));
+ if (w && (!w->isWindow() || w->testAttribute(Qt::WA_WindowPropagation))
+ && !q->testAttribute(Qt::WA_MacMiniSize) // no attribute set? inherit from parent
+ && !w->testAttribute(Qt::WA_MacSmallSize)
+ && !w->testAttribute(Qt::WA_MacNormalSize))
+ w->d_func()->macUpdateSizeAttribute();
+ }
+ resolveFont();
+}
+
+void QWidgetPrivate::macUpdateIgnoreMouseEvents()
+{
+#ifndef QT_MAC_USE_COCOA // This is handled inside the mouse handler on Cocoa.
+ Q_Q(QWidget);
+ if (!q->testAttribute(Qt::WA_WState_Created))
+ return;
+
+ if(q->isWindow())
+ {
+ if(q->testAttribute(Qt::WA_TransparentForMouseEvents))
+ ChangeWindowAttributes(qt_mac_window_for(q), kWindowIgnoreClicksAttribute, 0);
+ else
+ ChangeWindowAttributes(qt_mac_window_for(q), 0, kWindowIgnoreClicksAttribute);
+ ReshapeCustomWindow(qt_mac_window_for(q));
+ } else {
+#ifndef kHIViewFeatureIgnoresClicks
+#define kHIViewFeatureIgnoresClicks kHIViewIgnoresClicks
+#endif
+ if(q->testAttribute(Qt::WA_TransparentForMouseEvents))
+ HIViewChangeFeatures(qt_mac_nativeview_for(q), kHIViewFeatureIgnoresClicks, 0);
+ else
+ HIViewChangeFeatures(qt_mac_nativeview_for(q), 0, kHIViewFeatureIgnoresClicks);
+ HIViewReshapeStructure(qt_mac_nativeview_for(q));
+ }
+#endif
+}
+
+void QWidgetPrivate::macUpdateMetalAttribute()
+{
+ Q_Q(QWidget);
+ bool realWindow = isRealWindow();
+ if (!q->testAttribute(Qt::WA_WState_Created) || !realWindow)
+ return;
+
+ if (realWindow) {
+#if QT_MAC_USE_COCOA
+ // Cocoa doesn't let us change the style mask once it's been changed
+ // So, that means we need to recreate the window.
+ OSWindowRef cocoaWindow = qt_mac_window_for(q);
+ if ([cocoaWindow styleMask] & NSTexturedBackgroundWindowMask)
+ return;
+ recreateMacWindow();
+#else
+ QMainWindowLayout *layout = qt_mainwindow_layout(qobject_cast<QMainWindow *>(q));
+ if (q->testAttribute(Qt::WA_MacBrushedMetal)) {
+ if (layout)
+ layout->updateHIToolBarStatus();
+ ChangeWindowAttributes(qt_mac_window_for(q), kWindowMetalAttribute, 0);
+ ChangeWindowAttributes(qt_mac_window_for(q), kWindowMetalNoContentSeparatorAttribute, 0);
+ } else {
+ ChangeWindowAttributes(qt_mac_window_for(q), 0, kWindowMetalNoContentSeparatorAttribute);
+ ChangeWindowAttributes(qt_mac_window_for(q), 0, kWindowMetalAttribute);
+ if (layout)
+ layout->updateHIToolBarStatus();
+ }
+#endif
+ }
+}
+
+void QWidgetPrivate::setEnabled_helper_sys(bool enable)
+{
+#ifdef QT_MAC_USE_COCOA
+ Q_Q(QWidget);
+ NSView *view = qt_mac_nativeview_for(q);
+ if ([view isKindOfClass:[NSControl class]])
+ [static_cast<NSControl *>(view) setEnabled:enable];
+#else
+ Q_UNUSED(enable);
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/s60/qapplication_s60.cpp b/src/widgets/platforms/s60/qapplication_s60.cpp
new file mode 100644
index 0000000000..69d321dd65
--- /dev/null
+++ b/src/widgets/platforms/s60/qapplication_s60.cpp
@@ -0,0 +1,2715 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qapplication_p.h"
+#include "qsessionmanager.h"
+#include "qevent.h"
+#include "qsymbianevent.h"
+#include "qeventdispatcher_s60_p.h"
+#include "qwidget.h"
+#include "qdesktopwidget.h"
+#include "private/qbackingstore_p.h"
+#include "qt_s60_p.h"
+#include "private/qevent_p.h"
+#include "qstring.h"
+#include "qdebug.h"
+#include "qimage.h"
+#include "qcombobox.h"
+#include "private/qkeymapper_p.h"
+#include "private/qfont_p.h"
+#ifndef QT_NO_STYLE_S60
+#include "private/qs60style_p.h"
+#endif
+#include "private/qwindowsurface_s60_p.h"
+#include "qpaintengine.h"
+#include "private/qmenubar_p.h"
+#include "private/qsoftkeymanager_p.h"
+#ifdef QT_GRAPHICSSYSTEM_RUNTIME
+#include "private/qgraphicssystem_runtime_p.h"
+#endif
+
+#include "apgwgnam.h" // For CApaWindowGroupName
+#include <mdaaudiotoneplayer.h> // For CMdaAudioToneUtility
+
+#if defined(Q_OS_SYMBIAN)
+# include <private/qs60mainapplication_p.h>
+# include <centralrepository.h>
+# include "qs60mainappui.h"
+# include "qinputcontext.h"
+#endif
+
+#if defined(Q_WS_S60)
+# if !defined(QT_NO_IM)
+# include <private/qcoefepinputcontext_p.h>
+# endif
+#endif
+
+#include "private/qstylesheetstyle_p.h"
+
+#include <hal.h>
+#include <hal_data.h>
+
+#ifdef Q_SYMBIAN_TRANSITION_EFFECTS
+#include <graphics/wstfxconst.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+// Goom Events through Window Server
+static const int KGoomMemoryLowEvent = 0x10282DBF;
+static const int KGoomMemoryGoodEvent = 0x20026790;
+// Split view open/close events from AVKON
+static const int KSplitViewOpenEvent = 0x2001E2C0;
+static const int KSplitViewCloseEvent = 0x2001E2C1;
+
+#if defined(QT_DEBUG)
+static bool appNoGrab = false; // Grabbing enabled
+#endif
+static bool app_do_modal = false; // modal mode
+Q_GLOBAL_STATIC(QS60Data, qt_s60Data);
+
+extern bool qt_sendSpontaneousEvent(QObject*,QEvent*);
+extern QWidgetList *qt_modal_stack; // stack of modal widgets
+extern QDesktopWidget *qt_desktopWidget; // qapplication.cpp
+
+QWidget *qt_button_down = 0; // widget got last button-down
+
+QSymbianControl *QSymbianControl::lastFocusedControl = 0;
+
+QS60Data* qGlobalS60Data()
+{
+ return qt_s60Data();
+}
+
+#ifdef Q_WS_S60
+void QS60Data::setStatusPaneAndButtonGroupVisibility(bool statusPaneVisible, bool buttonGroupVisible)
+{
+ bool buttonGroupVisibilityChanged = false;
+ if (CEikButtonGroupContainer *const b = buttonGroupContainer()) {
+ buttonGroupVisibilityChanged = (b->IsVisible() != buttonGroupVisible);
+ b->MakeVisible(buttonGroupVisible);
+ }
+ bool statusPaneVisibilityChanged = false;
+ if (CEikStatusPane *const s = statusPane()) {
+ statusPaneVisibilityChanged = (s->IsVisible() != statusPaneVisible);
+ s->MakeVisible(statusPaneVisible);
+ }
+ if (buttonGroupVisibilityChanged || statusPaneVisibilityChanged) {
+ const QSize size = qt_TRect2QRect(static_cast<CEikAppUi*>(S60->appUi())->ClientRect()).size();
+ const QSize oldSize; // note that QDesktopWidget::resizeEvent ignores the QResizeEvent contents
+ QResizeEvent event(size, oldSize);
+ QApplication::instance()->sendEvent(QApplication::desktop(), &event);
+ }
+ if (buttonGroupVisibilityChanged && !statusPaneVisibilityChanged && QApplication::activeWindow())
+ // Ensure that control rectangle is updated
+ static_cast<QSymbianControl *>(QApplication::activeWindow()->winId())->handleClientAreaChange();
+}
+
+bool QS60Data::setRecursiveDecorationsVisibility(QWidget *window, Qt::WindowStates newState)
+{
+ // Show statusbar:
+ // Topmost parent: Show unless fullscreen/minimized.
+ // Child windows: Follow topmost parent, unless fullscreen, in which case do not show statusbar
+ // Show CBA:
+ // Topmost parent: Show unless fullscreen/minimized.
+ // Exception: Show if fullscreen with Qt::WindowSoftkeysVisibleHint.
+ // Child windows:
+ // Minimized: Unclear if there is an use case for having focused minimized window at all.
+ // Always follow topmost parent just to be safe.
+ // Maximized and normal: follow topmost parent.
+ // Exception: If topmost parent is not showing CBA, show CBA if any softkey actions are
+ // defined.
+ // Fullscreen: Show only if Qt::WindowSoftkeysVisibleHint set.
+
+ Qt::WindowStates comparisonState = newState;
+ QWidget *parentWindow = window->parentWidget();
+ if (parentWindow) {
+ while (parentWindow->parentWidget())
+ parentWindow = parentWindow->parentWidget();
+ comparisonState = parentWindow->windowState();
+ } else {
+ parentWindow = window;
+ }
+
+ bool decorationsVisible = !(comparisonState & (Qt::WindowFullScreen | Qt::WindowMinimized));
+ const bool parentIsFullscreen = comparisonState & Qt::WindowFullScreen;
+ const bool parentCbaVisibilityHint = parentWindow->windowFlags() & Qt::WindowSoftkeysVisibleHint;
+ bool buttonGroupVisibility = (decorationsVisible || (parentIsFullscreen && parentCbaVisibilityHint));
+
+ // Do extra checking for child windows
+ if (window->parentWidget()) {
+ if (newState & Qt::WindowFullScreen) {
+ decorationsVisible = false;
+ if (window->windowFlags() & Qt::WindowSoftkeysVisibleHint)
+ buttonGroupVisibility = true;
+ else
+ buttonGroupVisibility = false;
+ } else if (!(newState & Qt::WindowMinimized) && !buttonGroupVisibility) {
+ for (int i = 0; i < window->actions().size(); ++i) {
+ if (window->actions().at(i)->softKeyRole() != QAction::NoSoftKey) {
+ buttonGroupVisibility = true;
+ break;
+ }
+ }
+ }
+ }
+
+ S60->setStatusPaneAndButtonGroupVisibility(decorationsVisible, buttonGroupVisibility);
+
+ return decorationsVisible;
+}
+#endif
+
+void QS60Data::controlVisibilityChanged(CCoeControl *control, bool visible)
+{
+ if (QWidgetPrivate::mapper && QWidgetPrivate::mapper->contains(control)) {
+ QWidget *const widget = QWidgetPrivate::mapper->value(control);
+ QWidget *const window = widget->window();
+ if (QTLWExtra *topData = qt_widget_private(window)->maybeTopData()) {
+ QWidgetBackingStoreTracker &backingStore = topData->backingStore;
+ if (visible) {
+ if (backingStore.data()) {
+ backingStore.registerWidget(widget);
+ } else {
+ backingStore.create(window);
+ backingStore.registerWidget(widget);
+ qt_widget_private(widget)->invalidateBuffer(widget->rect());
+ widget->repaint();
+ }
+ } else {
+ // In certain special scenarios we may get an ENotVisible event
+ // without a previous EPartiallyVisible. The backingstore must
+ // still be destroyed, hence the registerWidget() call below.
+ if (backingStore.data() && widget->internalWinId()
+ && qt_widget_private(widget)->maybeBackingStore() == backingStore.data())
+ backingStore.registerWidget(widget);
+ backingStore.unregisterWidget(widget);
+ // In order to ensure that any resources used by the window surface
+ // are immediately freed, we flush the WSERV command buffer.
+ S60->wsSession().Flush();
+ }
+ }
+ }
+}
+
+bool qt_nograb() // application no-grab option
+{
+#if defined(QT_DEBUG)
+ return appNoGrab;
+#else
+ return false;
+#endif
+}
+
+// Modified from http://www3.symbian.com/faq.nsf/0/0F1464EE96E737E780256D5E00503DD1?OpenDocument
+class QS60Beep : public CBase, public MMdaAudioToneObserver
+{
+public:
+ static QS60Beep* NewL(TInt aFrequency, TTimeIntervalMicroSeconds iDuration);
+ void Play();
+ ~QS60Beep();
+private:
+ void ConstructL(TInt aFrequency, TTimeIntervalMicroSeconds iDuration);
+ void MatoPrepareComplete(TInt aError);
+ void MatoPlayComplete(TInt aError);
+private:
+ typedef enum
+ {
+ EBeepNotPrepared,
+ EBeepPrepared,
+ EBeepPlaying
+ } TBeepState;
+private:
+ CMdaAudioToneUtility* iToneUtil;
+ TBeepState iState;
+ TInt iFrequency;
+ TTimeIntervalMicroSeconds iDuration;
+};
+
+static QS60Beep* qt_S60Beep = 0;
+
+QS60Beep::~QS60Beep()
+{
+ if (iToneUtil) {
+ switch (iState) {
+ case EBeepPlaying:
+ iToneUtil->CancelPlay();
+ break;
+ case EBeepNotPrepared:
+ iToneUtil->CancelPrepare();
+ break;
+ }
+ }
+ delete iToneUtil;
+}
+
+QS60Beep* QS60Beep::NewL(TInt aFrequency, TTimeIntervalMicroSeconds aDuration)
+{
+ QS60Beep* self = new (ELeave) QS60Beep();
+ CleanupStack::PushL(self);
+ self->ConstructL(aFrequency, aDuration);
+ CleanupStack::Pop();
+ return self;
+}
+
+void QS60Beep::ConstructL(TInt aFrequency, TTimeIntervalMicroSeconds aDuration)
+{
+ iToneUtil = CMdaAudioToneUtility::NewL(*this);
+ iState = EBeepNotPrepared;
+ iFrequency = aFrequency;
+ iDuration = aDuration;
+ iToneUtil->PrepareToPlayTone(iFrequency, iDuration);
+}
+
+void QS60Beep::Play()
+{
+ if (iState == EBeepPlaying) {
+ iToneUtil->CancelPlay();
+ iState = EBeepPrepared;
+ }
+
+ iToneUtil->Play();
+ iState = EBeepPlaying;
+}
+
+void QS60Beep::MatoPrepareComplete(TInt aError)
+{
+ if (aError == KErrNone) {
+ iState = EBeepPrepared;
+ Play();
+ }
+}
+
+void QS60Beep::MatoPlayComplete(TInt aError)
+{
+ Q_UNUSED(aError);
+ iState = EBeepPrepared;
+}
+
+
+static Qt::KeyboardModifiers mapToQtModifiers(TUint s60Modifiers)
+{
+ Qt::KeyboardModifiers result = Qt::NoModifier;
+
+ if (s60Modifiers & EModifierKeypad)
+ result |= Qt::KeypadModifier;
+ if (s60Modifiers & EModifierShift || s60Modifiers & EModifierLeftShift
+ || s60Modifiers & EModifierRightShift)
+ result |= Qt::ShiftModifier;
+ if (s60Modifiers & EModifierCtrl || s60Modifiers & EModifierLeftCtrl
+ || s60Modifiers & EModifierRightCtrl)
+ result |= Qt::ControlModifier;
+ if (s60Modifiers & EModifierAlt || s60Modifiers & EModifierLeftAlt
+ || s60Modifiers & EModifierRightAlt)
+ result |= Qt::AltModifier;
+
+ return result;
+}
+
+static void mapS60MouseEventTypeToQt(QEvent::Type *type, Qt::MouseButton *button, const TPointerEvent *pEvent)
+{
+ switch (pEvent->iType) {
+ case TPointerEvent::EButton1Down:
+ *type = QEvent::MouseButtonPress;
+ *button = Qt::LeftButton;
+ break;
+ case TPointerEvent::EButton1Up:
+ *type = QEvent::MouseButtonRelease;
+ *button = Qt::LeftButton;
+ break;
+ case TPointerEvent::EButton2Down:
+ *type = QEvent::MouseButtonPress;
+ *button = Qt::MidButton;
+ break;
+ case TPointerEvent::EButton2Up:
+ *type = QEvent::MouseButtonRelease;
+ *button = Qt::MidButton;
+ break;
+ case TPointerEvent::EButton3Down:
+ *type = QEvent::MouseButtonPress;
+ *button = Qt::RightButton;
+ break;
+ case TPointerEvent::EButton3Up:
+ *type = QEvent::MouseButtonRelease;
+ *button = Qt::RightButton;
+ break;
+ case TPointerEvent::EDrag:
+ *type = QEvent::MouseMove;
+ *button = Qt::NoButton;
+ break;
+ case TPointerEvent::EMove:
+ // Qt makes no distinction between move and drag
+ *type = QEvent::MouseMove;
+ *button = Qt::NoButton;
+ break;
+ default:
+ *type = QEvent::None;
+ *button = Qt::NoButton;
+ break;
+ }
+ if (pEvent->iModifiers & EModifierDoubleClick){
+ *type = QEvent::MouseButtonDblClick;
+ }
+
+ if (*type == QEvent::MouseButtonPress || *type == QEvent::MouseButtonDblClick)
+ QApplicationPrivate::mouse_buttons = QApplicationPrivate::mouse_buttons | (*button);
+ else if (*type == QEvent::MouseButtonRelease)
+ QApplicationPrivate::mouse_buttons = QApplicationPrivate::mouse_buttons &(~(*button));
+
+ QApplicationPrivate::mouse_buttons = QApplicationPrivate::mouse_buttons & Qt::MouseButtonMask;
+}
+
+//### Can be replaced with CAknLongTapDetector if animation is required.
+//NOTE: if CAknLongTapDetector is used make sure it gets variated out of 3.1 and 3.2,.
+//also MLongTapObserver needs to be changed to MAknLongTapDetectorCallBack if CAknLongTapDetector is used.
+class QLongTapTimer : public CTimer
+{
+public:
+ static QLongTapTimer* NewL(QAbstractLongTapObserver *observer);
+ QLongTapTimer(QAbstractLongTapObserver *observer);
+ void ConstructL();
+public:
+ void PointerEventL(const TPointerEvent &event);
+ void RunL();
+protected:
+private:
+ QAbstractLongTapObserver *m_observer;
+ TPointerEvent m_event;
+ QPoint m_pressedCoordinates;
+ int m_dragDistance;
+};
+
+QLongTapTimer* QLongTapTimer::NewL(QAbstractLongTapObserver *observer)
+{
+ QLongTapTimer* self = new QLongTapTimer(observer);
+ self->ConstructL();
+ return self;
+}
+void QLongTapTimer::ConstructL()
+{
+ CTimer::ConstructL();
+}
+
+QLongTapTimer::QLongTapTimer(QAbstractLongTapObserver *observer):CTimer(CActive::EPriorityHigh)
+{
+ m_observer = observer;
+ m_dragDistance = qApp->startDragDistance();
+ CActiveScheduler::Add(this);
+}
+
+void QLongTapTimer::PointerEventL(const TPointerEvent& event)
+{
+ if ( event.iType == TPointerEvent::EDrag || event.iType == TPointerEvent::EButtonRepeat)
+ {
+ QPoint diff(QPoint(event.iPosition.iX,event.iPosition.iY) - m_pressedCoordinates);
+ if (diff.manhattanLength() < m_dragDistance)
+ return;
+ }
+ Cancel();
+ m_event = event;
+ if (event.iType == TPointerEvent::EButton1Down)
+ {
+ m_pressedCoordinates = QPoint(event.iPosition.iX,event.iPosition.iY);
+ // must be same as KLongTapDelay in aknlongtapdetector.h
+ After(800000);
+ }
+}
+void QLongTapTimer::RunL()
+{
+ if (m_observer)
+ m_observer->HandleLongTapEventL(m_event.iPosition, m_event.iParentPosition);
+}
+
+QSymbianControl::QSymbianControl(QWidget *w)
+ : CCoeControl()
+ , qwidget(w)
+ , m_longTapDetector(0)
+ , m_ignoreFocusChanged(0)
+ , m_symbianPopupIsOpen(0)
+ , m_inExternalScreenOverride(false)
+ , m_lastStatusPaneVisibility(0)
+{
+}
+
+void QSymbianControl::ConstructL(bool isWindowOwning, bool desktop)
+{
+ if (!desktop)
+ {
+ if (isWindowOwning || !qwidget->parentWidget()
+ || qwidget->parentWidget()->windowType() == Qt::Desktop) {
+ RWindowGroup &wg(S60->windowGroup(qwidget));
+ CreateWindowL(wg);
+ } else {
+ /**
+ * TODO: in order to avoid creating windows for all ancestors of
+ * this widget up to the root window, the parameter passed to
+ * CreateWindowL should be
+ * qwidget->parentWidget()->effectiveWinId(). However, if we do
+ * this, then we need to take care of re-parenting when a window
+ * is created for a widget between this one and the root window.
+ */
+ CreateWindowL(qwidget->parentWidget()->winId());
+ }
+
+ // Necessary in order to be able to track the activation status of
+ // the control's window
+ qwidget->d_func()->createExtra();
+
+ SetFocusing(true);
+ m_longTapDetector = QLongTapTimer::NewL(this);
+ m_doubleClickTimer.invalidate();
+
+ DrawableWindow()->SetPointerGrab(ETrue);
+ }
+
+#ifdef Q_SYMBIAN_TRANSITION_EFFECTS
+ if (OwnsWindow()) {
+ TTfxWindowPurpose windowPurpose(ETfxPurposeNone);
+ switch (qwidget->windowType()) {
+ case Qt::Dialog:
+ windowPurpose = ETfxPurposeDialogWindow;
+ break;
+ case Qt::Popup:
+ windowPurpose = ETfxPurposePopupWindow;
+ break;
+ case Qt::Tool:
+ windowPurpose = ETfxPurposeToolWindow;
+ break;
+ case Qt::ToolTip:
+ windowPurpose = ETfxPurposeToolTipWindow;
+ break;
+ case Qt::SplashScreen:
+ windowPurpose = ETfxPurposeSplashScreenWindow;
+ break;
+ default:
+ windowPurpose = (isWindowOwning || !qwidget->parentWidget() || qwidget->parentWidget()->windowType() == Qt::Desktop)
+ ? ETfxPurposeWindow : ETfxPurposeChildWindow;
+ break;
+ }
+ Window().SetPurpose(windowPurpose);
+ }
+#endif
+}
+
+QSymbianControl::~QSymbianControl()
+{
+ // Ensure backing store is deleted before the top-level
+ // window is destroyed
+ qt_widget_private(qwidget)->topData()->backingStore.destroy();
+
+ if (S60->curWin == this)
+ S60->curWin = 0;
+ if (!QApplicationPrivate::is_app_closing) {
+ QT_TRY {
+ setFocusSafely(false);
+ } QT_CATCH(const std::exception&) {
+ // ignore exceptions, nothing can be done
+ }
+ }
+ S60->appUi()->RemoveFromStack(this);
+ delete m_longTapDetector;
+}
+
+void QSymbianControl::setWidget(QWidget *w)
+{
+ qwidget = w;
+}
+
+QPoint QSymbianControl::translatePointForFixedNativeOrientation(const TPoint &pointerEventPos) const
+{
+ QPoint pos(pointerEventPos.iX, pointerEventPos.iY);
+ if (qwidget->d_func()->fixNativeOrientationCalled) {
+ QSize wsize = qwidget->size();
+ TSize size = Size();
+ if (size.iWidth == wsize.height() && size.iHeight == wsize.width()) {
+ qreal x = pos.x();
+ qreal y = pos.y();
+ pos.setX(size.iHeight - y);
+ pos.setY(x);
+ }
+ }
+ return pos;
+}
+
+TRect QSymbianControl::translateRectForFixedNativeOrientation(const TRect &controlRect) const
+{
+ TRect rect = controlRect;
+ if (qwidget->d_func()->fixNativeOrientationCalled) {
+ QPoint a = translatePointForFixedNativeOrientation(rect.iTl);
+ QPoint b = translatePointForFixedNativeOrientation(rect.iBr);
+ if (a.x() < b.x()) {
+ rect.iTl.iX = a.x();
+ rect.iBr.iX = b.x();
+ } else {
+ rect.iTl.iX = b.x();
+ rect.iBr.iX = a.x();
+ }
+ if (a.y() < b.y()) {
+ rect.iTl.iY = a.y();
+ rect.iBr.iY = b.y();
+ } else {
+ rect.iTl.iY = b.y();
+ rect.iBr.iY = a.y();
+ }
+ }
+ return rect;
+}
+
+void QSymbianControl::HandleLongTapEventL( const TPoint& aPenEventLocation, const TPoint& aPenEventScreenLocation )
+{
+ QWidget *alienWidget;
+ QPoint widgetPos = translatePointForFixedNativeOrientation(aPenEventLocation);
+ QPoint globalPos = translatePointForFixedNativeOrientation(aPenEventScreenLocation);
+ alienWidget = qwidget->childAt(widgetPos);
+ if (!alienWidget)
+ alienWidget = qwidget;
+
+#if !defined(QT_NO_CONTEXTMENU)
+ QContextMenuEvent contextMenuEvent(QContextMenuEvent::Mouse, widgetPos, globalPos, Qt::NoModifier);
+ qt_sendSpontaneousEvent(alienWidget, &contextMenuEvent);
+#endif
+}
+
+#ifdef QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER
+void QSymbianControl::translateAdvancedPointerEvent(const TAdvancedPointerEvent *event)
+{
+ QApplicationPrivate *d = QApplicationPrivate::instance();
+ QPointF screenPos = qwidget->mapToGlobal(translatePointForFixedNativeOrientation(event->iPosition));
+ qreal pressure;
+ if(d->pressureSupported
+ && event->Pressure() > 0) //workaround for misconfigured HAL
+ pressure = event->Pressure() / qreal(d->maxTouchPressure);
+ else
+ pressure = qreal(1.0);
+ processTouchEvent(event->PointerNumber(), event->iType, screenPos, pressure);
+}
+#endif
+
+void QSymbianControl::processTouchEvent(int pointerNumber, TPointerEvent::TType type, QPointF screenPos, qreal pressure)
+{
+ QRect screenGeometry = qApp->desktop()->screenGeometry(qwidget);
+
+ QApplicationPrivate *d = QApplicationPrivate::instance();
+
+ QList<QTouchEvent::TouchPoint> points = d->appAllTouchPoints;
+ while (points.count() <= pointerNumber)
+ points.append(QTouchEvent::TouchPoint(points.count()));
+
+ Qt::TouchPointStates allStates = 0;
+ for (int i = 0; i < points.count(); ++i) {
+ QTouchEvent::TouchPoint &touchPoint = points[i];
+
+ if (touchPoint.id() == pointerNumber) {
+ Qt::TouchPointStates state;
+ switch (type) {
+ case TPointerEvent::EButton1Down:
+#ifdef QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER
+ case TPointerEvent::EEnterHighPressure:
+#endif
+ state = Qt::TouchPointPressed;
+ break;
+ case TPointerEvent::EButton1Up:
+#ifdef QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER
+ case TPointerEvent::EExitCloseProximity:
+#endif
+ state = Qt::TouchPointReleased;
+ break;
+ case TPointerEvent::EDrag:
+ state = Qt::TouchPointMoved;
+ break;
+ default:
+ // how likely is this to happen?
+ state = Qt::TouchPointStationary;
+ break;
+ }
+ if (pointerNumber == 0)
+ state |= Qt::TouchPointPrimary;
+ touchPoint.setState(state);
+
+ touchPoint.setScreenPos(screenPos);
+ touchPoint.setNormalizedPos(QPointF(screenPos.x() / screenGeometry.width(),
+ screenPos.y() / screenGeometry.height()));
+
+ touchPoint.setPressure(pressure);
+ } else if (touchPoint.state() != Qt::TouchPointReleased) {
+ // all other active touch points should be marked as stationary
+ touchPoint.setState(Qt::TouchPointStationary);
+ }
+
+ allStates |= touchPoint.state();
+ }
+
+ if ((allStates & Qt::TouchPointStateMask) == Qt::TouchPointReleased) {
+ // all touch points released
+ d->appAllTouchPoints.clear();
+ } else {
+ d->appAllTouchPoints = points;
+ }
+
+ QApplicationPrivate::translateRawTouchEvent(qwidget,
+ QTouchEvent::TouchScreen,
+ points);
+}
+
+void QSymbianControl::HandlePointerEventL(const TPointerEvent& pEvent)
+{
+#ifdef QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER
+ if (pEvent.IsAdvancedPointerEvent()) {
+ const TAdvancedPointerEvent *advancedPointerEvent = pEvent.AdvancedPointerEvent();
+ translateAdvancedPointerEvent(advancedPointerEvent);
+ if (advancedPointerEvent->PointerNumber() != 0) {
+ // only send mouse events for the first touch point
+ return;
+ }
+ }
+#endif
+
+ m_longTapDetector->PointerEventL(pEvent);
+ QT_TRYCATCH_LEAVING(HandlePointerEvent(pEvent));
+}
+
+void QSymbianControl::HandlePointerEvent(const TPointerEvent& pEvent)
+{
+ QMouseEvent::Type type;
+ Qt::MouseButton button;
+ mapS60MouseEventTypeToQt(&type, &button, &pEvent);
+ Qt::KeyboardModifiers modifiers = mapToQtModifiers(pEvent.iModifiers);
+
+ QPoint widgetPos = translatePointForFixedNativeOrientation(pEvent.iPosition);
+ TPoint controlScreenPos = PositionRelativeToScreen();
+ QPoint globalPos = QPoint(controlScreenPos.iX, controlScreenPos.iY) + widgetPos;
+ S60->lastCursorPos = globalPos;
+ S60->lastPointerEventPos = widgetPos;
+
+ QWidget *mouseGrabber = QWidget::mouseGrabber();
+
+ QWidget *popupWidget = qApp->activePopupWidget();
+ QWidget *popupReceiver = 0;
+ if (popupWidget) {
+ QWidget *popupChild = popupWidget->childAt(popupWidget->mapFromGlobal(globalPos));
+ popupReceiver = popupChild ? popupChild : popupWidget;
+ }
+
+ if (mouseGrabber) {
+ if (popupReceiver) {
+ sendMouseEvent(popupReceiver, type, globalPos, button, modifiers);
+ } else {
+ sendMouseEvent(mouseGrabber, type, globalPos, button, modifiers);
+ }
+ // No Enter/Leave events in grabbing mode.
+ return;
+ }
+
+ QWidget *widgetUnderPointer = qwidget->childAt(widgetPos);
+ if (!widgetUnderPointer)
+ widgetUnderPointer = qwidget;
+
+ QApplicationPrivate::dispatchEnterLeave(widgetUnderPointer, S60->lastPointerEventTarget);
+ S60->lastPointerEventTarget = widgetUnderPointer;
+
+ QWidget *receiver;
+ if (!popupReceiver && S60->mousePressTarget && type != QEvent::MouseButtonPress) {
+ receiver = S60->mousePressTarget;
+ if (type == QEvent::MouseButtonRelease)
+ S60->mousePressTarget = 0;
+ } else {
+ receiver = popupReceiver ? popupReceiver : widgetUnderPointer;
+ if (type == QEvent::MouseButtonPress)
+ S60->mousePressTarget = receiver;
+ }
+
+#if !defined(QT_NO_CURSOR) && !defined(Q_SYMBIAN_FIXED_POINTER_CURSORS)
+ if (S60->brokenPointerCursors)
+ qt_symbian_move_cursor_sprite();
+#endif
+
+//Generate single touch event for S60 5.0 (has touchscreen, does not have advanced pointers)
+#ifndef QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER
+ if (S60->hasTouchscreen) {
+ processTouchEvent(0, pEvent.iType, QPointF(globalPos), 1.0);
+ }
+#endif
+
+ sendMouseEvent(receiver, type, globalPos, button, modifiers);
+}
+
+#ifdef Q_WS_S60
+void QSymbianControl::HandleStatusPaneSizeChange()
+{
+ QS60MainAppUi *s60AppUi = static_cast<QS60MainAppUi *>(S60->appUi());
+ s60AppUi->HandleStatusPaneSizeChange();
+}
+#endif
+
+void QSymbianControl::sendMouseEvent(
+ QWidget *receiver,
+ QEvent::Type type,
+ const QPoint &globalPos,
+ Qt::MouseButton button,
+ Qt::KeyboardModifiers modifiers)
+{
+ Q_ASSERT(receiver);
+ QMouseEvent mEvent(type, receiver->mapFromGlobal(globalPos), globalPos,
+ button, QApplicationPrivate::mouse_buttons, modifiers);
+ QEventDispatcherS60 *dispatcher;
+ // It is theoretically possible for someone to install a different event dispatcher.
+ if ((dispatcher = qobject_cast<QEventDispatcherS60 *>(receiver->d_func()->threadData->eventDispatcher)) != 0) {
+ if (dispatcher->excludeUserInputEvents()) {
+ dispatcher->saveInputEvent(this, receiver, new QMouseEvent(mEvent));
+ return;
+ }
+ }
+
+ sendMouseEvent(receiver, &mEvent);
+}
+
+bool QSymbianControl::sendMouseEvent(QWidget *widget, QMouseEvent *mEvent)
+{
+ return qt_sendSpontaneousEvent(widget, mEvent);
+}
+
+TKeyResponse QSymbianControl::OfferKeyEventL(const TKeyEvent& keyEvent, TEventCode type)
+{
+ TKeyResponse r = EKeyWasNotConsumed;
+ QT_TRYCATCH_LEAVING(r = OfferKeyEvent(keyEvent, type));
+ return r;
+}
+
+TKeyResponse QSymbianControl::OfferKeyEvent(const TKeyEvent& keyEvent, TEventCode type)
+{
+ /*
+ S60 has a confusing way of delivering key events. There are three types of
+ events: EEventKey, EEventKeyDown and EEventKeyUp. When a key is pressed,
+ EEventKeyDown is first generated, followed by EEventKey. Then, when the key is
+ released, EEventKeyUp is generated.
+ However, it is possible that only the EEventKey is generated alone, typically
+ in relation to virtual keyboards. In that case we need to take care to
+ generate both press and release events in Qt, since applications expect that.
+ We do this by having three states for each used scan code, depending on the
+ events received. See the switch below for what happens in each state
+ transition.
+ */
+
+ if (type != EEventKeyDown)
+ if (handleVirtualMouse(keyEvent, type) == EKeyWasConsumed)
+ return EKeyWasConsumed;
+
+ TKeyResponse ret = EKeyWasNotConsumed;
+#define GET_RETURN(x) (ret = ((x) == EKeyWasConsumed) ? EKeyWasConsumed : ret)
+
+ // This top level switch corresponds to the states, and the inner switches
+ // correspond to the transitions.
+ QS60Data::ScanCodeState &scanCodeState = S60->scanCodeStates[keyEvent.iScanCode];
+ switch (scanCodeState) {
+ case QS60Data::Unpressed:
+ switch (type) {
+ case EEventKeyDown:
+ scanCodeState = QS60Data::KeyDown;
+ break;
+ case EEventKey:
+ GET_RETURN(sendSymbianKeyEvent(keyEvent, QEvent::KeyPress));
+ GET_RETURN(sendSymbianKeyEvent(keyEvent, QEvent::KeyRelease));
+ break;
+ case EEventKeyUp:
+ // No action.
+ break;
+ }
+ break;
+ case QS60Data::KeyDown:
+ switch (type) {
+ case EEventKeyDown:
+ // This should never happen, just stay in this state to be safe.
+ break;
+ case EEventKey:
+ GET_RETURN(sendSymbianKeyEvent(keyEvent, QEvent::KeyPress));
+ scanCodeState = QS60Data::KeyDownAndKey;
+ break;
+ case EEventKeyUp:
+ scanCodeState = QS60Data::Unpressed;
+ break;
+ }
+ break;
+ case QS60Data::KeyDownAndKey:
+ switch (type) {
+ case EEventKeyDown:
+ // This should never happen, just stay in this state to be safe.
+ break;
+ case EEventKey:
+ GET_RETURN(sendSymbianKeyEvent(keyEvent, QEvent::KeyRelease));
+ GET_RETURN(sendSymbianKeyEvent(keyEvent, QEvent::KeyPress));
+ break;
+ case EEventKeyUp:
+ GET_RETURN(sendSymbianKeyEvent(keyEvent, QEvent::KeyRelease));
+ scanCodeState = QS60Data::Unpressed;
+ break;
+ }
+ break;
+ }
+ return ret;
+
+#undef GET_RETURN
+}
+
+TKeyResponse QSymbianControl::sendSymbianKeyEvent(const TKeyEvent &keyEvent, QEvent::Type type)
+{
+ // Because S60 does not generate keysyms for EKeyEventDown and EKeyEventUp
+ // events, we need to cache the keysyms from the EKeyEvent events. This is what
+ // resolveS60ScanCode does.
+ TUint s60Keysym = QApplicationPrivate::resolveS60ScanCode(keyEvent.iScanCode,
+ keyEvent.iCode);
+ int keyCode;
+ if (s60Keysym == EKeyNull){ //some key events have 0 in iCode, for them iScanCode should be used
+ keyCode = qt_keymapper_private()->mapS60ScanCodesToQt(keyEvent.iScanCode);
+ } else if (s60Keysym >= 0x20 && s60Keysym < ENonCharacterKeyBase) {
+ // Normal characters keys.
+ keyCode = s60Keysym;
+ } else {
+ // Special S60 keys.
+ keyCode = qt_keymapper_private()->mapS60KeyToQt(s60Keysym);
+ }
+
+ Qt::KeyboardModifiers mods = mapToQtModifiers(keyEvent.iModifiers);
+ QKeyEventEx qKeyEvent(type, keyCode, mods, qt_keymapper_private()->translateKeyEvent(keyCode, mods),
+ (keyEvent.iRepeats != 0), 1, keyEvent.iScanCode, s60Keysym, keyEvent.iModifiers);
+ QWidget *widget;
+ widget = QWidget::keyboardGrabber();
+ if (!widget) {
+ if (QApplicationPrivate::popupWidgets != 0) {
+ widget = QApplication::activePopupWidget()->focusWidget();
+ if (!widget) {
+ widget = QApplication::activePopupWidget();
+ }
+ } else {
+ widget = QApplicationPrivate::focus_widget;
+ if (!widget) {
+ widget = qwidget;
+ }
+ }
+ }
+
+ QEventDispatcherS60 *dispatcher;
+ // It is theoretically possible for someone to install a different event dispatcher.
+ if ((dispatcher = qobject_cast<QEventDispatcherS60 *>(widget->d_func()->threadData->eventDispatcher)) != 0) {
+ if (dispatcher->excludeUserInputEvents()) {
+ dispatcher->saveInputEvent(this, widget, new QKeyEventEx(qKeyEvent));
+ return EKeyWasConsumed;
+ }
+ }
+ return sendKeyEvent(widget, &qKeyEvent);
+}
+
+TKeyResponse QSymbianControl::handleVirtualMouse(const TKeyEvent& keyEvent,TEventCode type)
+{
+#ifndef QT_NO_CURSOR
+ if (S60->mouseInteractionEnabled && S60->virtualMouseRequired) {
+ //translate keys to pointer
+ if ((keyEvent.iScanCode >= EStdKeyLeftArrow && keyEvent.iScanCode <= EStdKeyDownArrow) ||
+ (keyEvent.iScanCode >= EStdKeyDevice10 && keyEvent.iScanCode <= EStdKeyDevice13) ||
+ keyEvent.iScanCode == EStdKeyDevice3) {
+ QPoint pos = QCursor::pos();
+ TPointerEvent fakeEvent;
+ fakeEvent.iType = (TPointerEvent::TType)(-1);
+ fakeEvent.iModifiers = keyEvent.iModifiers;
+ TInt x = pos.x();
+ TInt y = pos.y();
+ if (type == EEventKeyUp) {
+ S60->virtualMouseAccelTimeout.start();
+ switch (keyEvent.iScanCode) {
+ case EStdKeyLeftArrow:
+ S60->virtualMousePressedKeys &= ~QS60Data::Left;
+ break;
+ case EStdKeyRightArrow:
+ S60->virtualMousePressedKeys &= ~QS60Data::Right;
+ break;
+ case EStdKeyUpArrow:
+ S60->virtualMousePressedKeys &= ~QS60Data::Up;
+ break;
+ case EStdKeyDownArrow:
+ S60->virtualMousePressedKeys &= ~QS60Data::Down;
+ break;
+ // diagonal keys (named aliases don't exist in 3.1 SDK)
+ case EStdKeyDevice10:
+ S60->virtualMousePressedKeys &= ~QS60Data::LeftUp;
+ break;
+ case EStdKeyDevice11:
+ S60->virtualMousePressedKeys &= ~QS60Data::RightUp;
+ break;
+ case EStdKeyDevice12:
+ S60->virtualMousePressedKeys &= ~QS60Data::RightDown;
+ break;
+ case EStdKeyDevice13:
+ S60->virtualMousePressedKeys &= ~QS60Data::LeftDown;
+ break;
+ case EStdKeyDevice3: //select
+ if (S60->virtualMousePressedKeys & QS60Data::Select)
+ fakeEvent.iType = TPointerEvent::EButton1Up;
+ S60->virtualMousePressedKeys &= ~QS60Data::Select;
+ break;
+ }
+ }
+ else if (type == EEventKey) {
+ int dx = 0;
+ int dy = 0;
+ if (keyEvent.iScanCode != EStdKeyDevice3) {
+ m_doubleClickTimer.invalidate();
+ //reset mouse accelleration after a short time with no moves
+ const int maxTimeBetweenKeyEventsMs = 500;
+ if (S60->virtualMouseAccelTimeout.isValid() &&
+ S60->virtualMouseAccelTimeout.hasExpired(maxTimeBetweenKeyEventsMs)) {
+ S60->virtualMouseAccelDX = 0;
+ S60->virtualMouseAccelDY = 0;
+ }
+ S60->virtualMouseAccelTimeout.invalidate();
+ }
+ switch (keyEvent.iScanCode) {
+ case EStdKeyLeftArrow:
+ S60->virtualMousePressedKeys |= QS60Data::Left;
+ dx = -1;
+ fakeEvent.iType = TPointerEvent::EMove;
+ break;
+ case EStdKeyRightArrow:
+ S60->virtualMousePressedKeys |= QS60Data::Right;
+ dx = 1;
+ fakeEvent.iType = TPointerEvent::EMove;
+ break;
+ case EStdKeyUpArrow:
+ S60->virtualMousePressedKeys |= QS60Data::Up;
+ dy = -1;
+ fakeEvent.iType = TPointerEvent::EMove;
+ break;
+ case EStdKeyDownArrow:
+ S60->virtualMousePressedKeys |= QS60Data::Down;
+ dy = 1;
+ fakeEvent.iType = TPointerEvent::EMove;
+ break;
+ case EStdKeyDevice10:
+ S60->virtualMousePressedKeys |= QS60Data::LeftUp;
+ dx = -1;
+ dy = -1;
+ fakeEvent.iType = TPointerEvent::EMove;
+ break;
+ case EStdKeyDevice11:
+ S60->virtualMousePressedKeys |= QS60Data::RightUp;
+ dx = 1;
+ dy = -1;
+ fakeEvent.iType = TPointerEvent::EMove;
+ break;
+ case EStdKeyDevice12:
+ S60->virtualMousePressedKeys |= QS60Data::RightDown;
+ dx = 1;
+ dy = 1;
+ fakeEvent.iType = TPointerEvent::EMove;
+ break;
+ case EStdKeyDevice13:
+ S60->virtualMousePressedKeys |= QS60Data::LeftDown;
+ dx = -1;
+ dy = 1;
+ fakeEvent.iType = TPointerEvent::EMove;
+ break;
+ case EStdKeyDevice3:
+ // Platform bug. If you start pressing several keys simultaneously (for
+ // example for drag'n'drop), Symbian starts producing spurious up and
+ // down messages for some keys. Therefore, make sure we have a clean slate
+ // of pressed keys before starting a new button press.
+ if (S60->virtualMousePressedKeys & QS60Data::Select) {
+ return EKeyWasConsumed;
+ } else {
+ S60->virtualMousePressedKeys |= QS60Data::Select;
+ fakeEvent.iType = TPointerEvent::EButton1Down;
+ if (m_doubleClickTimer.isValid()
+ && !m_doubleClickTimer.hasExpired(QApplication::doubleClickInterval())) {
+ fakeEvent.iModifiers |= EModifierDoubleClick;
+ m_doubleClickTimer.invalidate();
+ } else {
+ m_doubleClickTimer.start();
+ }
+ }
+ break;
+ }
+ if (dx) {
+ int cdx = S60->virtualMouseAccelDX;
+ //reset accel on change of sign, else double accel
+ if (dx * cdx <= 0)
+ cdx = dx;
+ else
+ cdx *= 4;
+ //cap accelleration
+ if (dx * cdx > S60->virtualMouseMaxAccel)
+ cdx = dx * S60->virtualMouseMaxAccel;
+ //move mouse position
+ x += cdx;
+ S60->virtualMouseAccelDX = cdx;
+ }
+
+ if (dy) {
+ int cdy = S60->virtualMouseAccelDY;
+ if (dy * cdy <= 0)
+ cdy = dy;
+ else
+ cdy *= 4;
+ if (dy * cdy > S60->virtualMouseMaxAccel)
+ cdy = dy * S60->virtualMouseMaxAccel;
+ y += cdy;
+ S60->virtualMouseAccelDY = cdy;
+ }
+ }
+ //clip to screen size (window server allows a sprite hotspot to be outside the screen)
+ int screenNumber = S60->screenNumberForWidget(qwidget);
+ if (x < 0)
+ x = 0;
+ else if (x >= S60->screenWidthInPixelsForScreen[screenNumber])
+ x = S60->screenWidthInPixelsForScreen[screenNumber] - 1;
+ if (y < 0)
+ y = 0;
+ else if (y >= S60->screenHeightInPixelsForScreen[screenNumber])
+ y = S60->screenHeightInPixelsForScreen[screenNumber] - 1;
+ TPoint epos(x, y);
+ TPoint cpos = epos - PositionRelativeToScreen();
+ fakeEvent.iPosition = cpos;
+ fakeEvent.iParentPosition = epos;
+ if(fakeEvent.iType != -1)
+ HandlePointerEvent(fakeEvent);
+ return EKeyWasConsumed;
+ }
+ }
+#endif
+
+ return EKeyWasNotConsumed;
+}
+
+void QSymbianControl::sendInputEvent(QWidget *widget, QInputEvent *inputEvent)
+{
+ switch (inputEvent->type()) {
+ case QEvent::KeyPress:
+ case QEvent::KeyRelease:
+ sendKeyEvent(widget, static_cast<QKeyEvent *>(inputEvent));
+ break;
+ case QEvent::MouseButtonDblClick:
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseMove:
+ sendMouseEvent(widget, static_cast<QMouseEvent *>(inputEvent));
+ break;
+ default:
+ // Shouldn't get here.
+ Q_ASSERT_X(0 == 1, "QSymbianControl::sendInputEvent()", "inputEvent->type() is unknown");
+ break;
+ }
+}
+
+TKeyResponse QSymbianControl::sendKeyEvent(QWidget *widget, QKeyEvent *keyEvent)
+{
+#if !defined(QT_NO_IM) && defined(Q_WS_S60)
+ if (widget && widget->isEnabled() && widget->testAttribute(Qt::WA_InputMethodEnabled)) {
+ QInputContext *qic = widget->inputContext();
+ if (qic && qic->filterEvent(keyEvent))
+ return EKeyWasConsumed;
+ }
+#endif // !defined(QT_NO_IM) && defined(Q_OS_SYMBIAN)
+
+ if (widget && qt_sendSpontaneousEvent(widget, keyEvent))
+ if (keyEvent->isAccepted())
+ return EKeyWasConsumed;
+
+ return EKeyWasNotConsumed;
+}
+
+#if !defined(QT_NO_IM) && defined(Q_WS_S60)
+TCoeInputCapabilities QSymbianControl::InputCapabilities() const
+{
+ QWidget *w = 0;
+
+ if (qwidget->hasFocus())
+ w = qwidget;
+ else
+ w = qwidget->focusWidget();
+
+ QCoeFepInputContext *ic;
+ if (w && w->isEnabled() && w->testAttribute(Qt::WA_InputMethodEnabled)
+ && (ic = qobject_cast<QCoeFepInputContext *>(w->inputContext()))) {
+ return ic->inputCapabilities();
+ } else {
+ return TCoeInputCapabilities(TCoeInputCapabilities::ENone, 0, 0);
+ }
+}
+#endif
+
+void QSymbianControl::Draw(const TRect& controlRect) const
+{
+ // Set flag to avoid calling DrawNow in window surface
+ QWidget *window = qwidget->window();
+ Q_ASSERT(window);
+ QTLWExtra *topExtra = window->d_func()->maybeTopData();
+ Q_ASSERT(topExtra);
+
+ TRect wcontrolRect = translateRectForFixedNativeOrientation(controlRect);
+
+ if (!topExtra->inExpose) {
+ topExtra->inExpose = true;
+ if (!qwidget->isWindow()) {
+ // If we get here, then it means we have a native child window
+ // Since no content should ever be painted to these windows, we
+ // erase them with a transparent brush when they get an expose.
+ CWindowGc &gc = SystemGc();
+ gc.SetBrushColor(TRgb(0, 0, 0, 0));
+ gc.Clear(controlRect);
+ }
+ QRect exposeRect = qt_TRect2QRect(wcontrolRect);
+ qwidget->d_func()->syncBackingStore(exposeRect);
+ topExtra->inExpose = false;
+ }
+
+ QWindowSurface *surface = qwidget->windowSurface();
+ QPaintEngine *engine = surface ? surface->paintDevice()->paintEngine() : NULL;
+
+ if (!engine)
+ return;
+
+ const bool sendNativePaintEvents = qwidget->d_func()->extraData()->receiveNativePaintEvents;
+ if (sendNativePaintEvents) {
+ const QRect r = qt_TRect2QRect(wcontrolRect);
+ QMetaObject::invokeMethod(qwidget, "beginNativePaintEvent", Qt::DirectConnection, Q_ARG(QRect, r));
+ }
+
+ // Map source rectangle into coordinates of the backing store.
+ const QPoint controlBase(controlRect.iTl.iX, controlRect.iTl.iY);
+ const QPoint backingStoreBase = qwidget->mapTo(qwidget->window(), controlBase);
+ const TRect backingStoreRect(TPoint(backingStoreBase.x(), backingStoreBase.y()), controlRect.Size());
+
+ if (engine->type() == QPaintEngine::Raster) {
+ QS60WindowSurface *s60Surface;
+#ifdef QT_GRAPHICSSYSTEM_RUNTIME
+ if (QApplicationPrivate::runtime_graphics_system) {
+ QRuntimeWindowSurface *rtSurface =
+ static_cast<QRuntimeWindowSurface*>(qwidget->windowSurface());
+ s60Surface = static_cast<QS60WindowSurface *>(rtSurface->m_windowSurface.data());
+ } else
+#endif
+ s60Surface = static_cast<QS60WindowSurface *>(qwidget->windowSurface());
+
+ CFbsBitmap *bitmap = s60Surface->symbianBitmap();
+ CWindowGc &gc = SystemGc();
+
+ QWExtra::NativePaintMode nativePaintMode = qwidget->d_func()->extraData()->nativePaintMode;
+ if(qwidget->d_func()->paintOnScreen())
+ nativePaintMode = QWExtra::Disable;
+
+ switch(nativePaintMode) {
+ case QWExtra::Disable:
+ // Do nothing
+ break;
+ case QWExtra::Blit:
+ case QWExtra::BlitWriteAlpha:
+ if (qwidget->d_func()->isOpaque || nativePaintMode == QWExtra::BlitWriteAlpha)
+ gc.SetDrawMode(CGraphicsContext::EDrawModeWriteAlpha);
+ gc.BitBlt(controlRect.iTl, bitmap, backingStoreRect);
+ break;
+ case QWExtra::ZeroFill:
+ if (Window().DisplayMode() == EColor16MA
+ || Window().DisplayMode() == Q_SYMBIAN_ECOLOR16MAP) {
+ gc.SetBrushStyle(CGraphicsContext::ESolidBrush);
+ gc.SetDrawMode(CGraphicsContext::EDrawModeWriteAlpha);
+ gc.SetBrushColor(TRgb::Color16MA(0));
+ gc.Clear(controlRect);
+ } else {
+ gc.SetBrushColor(TRgb(0x000000));
+ gc.Clear(controlRect);
+ };
+ break;
+ default:
+ Q_ASSERT(false);
+ }
+ }
+
+ if (sendNativePaintEvents) {
+ const QRect r = qt_TRect2QRect(wcontrolRect);
+ // The draw ops aren't actually sent to WSERV until the graphics
+ // context is deactivated, which happens in the function calling
+ // this one. We therefore delay the delivery of endNativePaintEvent,
+ // to ensure that drawing has completed by the time the widget
+ // receives the event. Note that, if the widget needs to ensure
+ // that the draw ops have actually been executed into the output
+ // framebuffer, a call to RWsSession::Flush is required in the
+ // endNativePaintEvent implementation.
+ QMetaObject::invokeMethod(qwidget, "endNativePaintEvent", Qt::QueuedConnection, Q_ARG(QRect, r));
+ }
+}
+
+void QSymbianControl::qwidgetResize_helper(const QSize &newSize)
+{
+ QRect cr = qwidget->geometry();
+ QSize oldSize(cr.size());
+ cr.setSize(newSize);
+ qwidget->data->crect = cr;
+ if (qwidget->isVisible()) {
+ QTLWExtra *tlwExtra = qwidget->d_func()->maybeTopData();
+ bool slowResize = qgetenv("QT_SLOW_TOPLEVEL_RESIZE").toInt();
+ if (!slowResize && tlwExtra)
+ tlwExtra->inTopLevelResize = true;
+ QResizeEvent e(newSize, oldSize);
+ qt_sendSpontaneousEvent(qwidget, &e);
+ if (!qwidget->testAttribute(Qt::WA_StaticContents))
+ qwidget->d_func()->syncBackingStore();
+ if (!slowResize && tlwExtra)
+ tlwExtra->inTopLevelResize = false;
+ } else {
+ if (!qwidget->testAttribute(Qt::WA_PendingResizeEvent)) {
+ QResizeEvent *e = new QResizeEvent(newSize, oldSize);
+ QApplication::postEvent(qwidget, e);
+ }
+ }
+}
+
+void QSymbianControl::SizeChanged()
+{
+ CCoeControl::SizeChanged();
+
+ // When FixNativeOrientation had been called, the RWindow/CCoeControl size
+ // and the surface/QWidget size have nothing to do with each other.
+ if (qwidget->d_func()->fixNativeOrientationCalled)
+ return;
+
+ QSize oldSize = qwidget->size();
+ QSize newSize(Size().iWidth, Size().iHeight);
+
+ if (oldSize != newSize) {
+ // Enforce the proper size for fullscreen widgets on the secondary screen.
+ const bool isFullscreen = qwidget->windowState() & Qt::WindowFullScreen;
+ const int screenNumber = S60->screenNumberForWidget(qwidget);
+ if (!m_inExternalScreenOverride && isFullscreen && screenNumber > 0) {
+ int screenWidth = S60->screenWidthInPixelsForScreen[screenNumber];
+ int screenHeight = S60->screenHeightInPixelsForScreen[screenNumber];
+ TSize screenSize(screenWidth, screenHeight);
+ if (screenWidth > 0 && screenHeight > 0 && screenSize != Size()) {
+ m_inExternalScreenOverride = true;
+ SetExtent(TPoint(0, 0), screenSize);
+ return;
+ }
+ }
+
+ qwidgetResize_helper(newSize);
+ }
+
+ m_inExternalScreenOverride = false;
+
+ // CCoeControl::SetExtent calls SizeChanged, but does not call
+ // PositionChanged, so we call it here to ensure that the widget's
+ // position is updated.
+ PositionChanged();
+}
+
+void QSymbianControl::PositionChanged()
+{
+ CCoeControl::PositionChanged();
+
+ QPoint oldPos = qwidget->geometry().topLeft();
+ QPoint newPos(Position().iX, Position().iY);
+
+ if (oldPos != newPos) {
+ QRect cr = qwidget->geometry();
+ cr.moveTopLeft(newPos);
+ qwidget->data->crect = cr;
+ QTLWExtra *top = qwidget->d_func()->maybeTopData();
+ if (top && (qwidget->windowState() & (~Qt::WindowActive)) == Qt::WindowNoState)
+ top->normalGeometry.moveTopLeft(newPos);
+ if (qwidget->isVisible()) {
+ QMoveEvent e(newPos, oldPos);
+ qt_sendSpontaneousEvent(qwidget, &e);
+ } else {
+ QMoveEvent * e = new QMoveEvent(newPos, oldPos);
+ QApplication::postEvent(qwidget, e);
+ }
+ }
+}
+
+void QSymbianControl::FocusChanged(TDrawNow /* aDrawNow */)
+{
+ if (m_ignoreFocusChanged || (qwidget->windowType() & Qt::WindowType_Mask) == Qt::Desktop)
+ return;
+
+#ifdef Q_WS_S60
+ if (S60->splitViewLastWidget)
+ return;
+#endif
+
+ // Popups never get focused, but still receive the FocusChanged when they are hidden.
+ if (QApplicationPrivate::popupWidgets != 0
+ || (qwidget->windowType() & Qt::Popup) == Qt::Popup)
+ return;
+
+ if (IsFocused() && IsVisible()) {
+ if (m_symbianPopupIsOpen) {
+ QWidget *fw = QApplication::focusWidget();
+ if (fw) {
+ QFocusEvent event(QEvent::FocusIn, Qt::PopupFocusReason);
+ QCoreApplication::sendEvent(fw, &event);
+ }
+ m_symbianPopupIsOpen = false;
+ }
+
+ QApplication::setActiveWindow(qwidget->window());
+ qwidget->d_func()->setWindowIcon_sys(true);
+ qwidget->d_func()->setWindowTitle_sys(qwidget->windowTitle());
+#ifdef Q_WS_S60
+ if (qwidget->isWindow())
+ S60->setRecursiveDecorationsVisibility(qwidget, qwidget->windowState());
+#endif
+ } else if (QApplication::activeWindow() == qwidget->window()) {
+ bool focusedControlFound = false;
+ WId winId = 0;
+ for (QWidget *w = qwidget->parentWidget(); w && (winId = w->internalWinId()); w = w->parentWidget()) {
+ if (winId->IsFocused() && winId->IsVisible()) {
+ focusedControlFound = true;
+ break;
+ } else if (w->isWindow())
+ break;
+ }
+ if (!focusedControlFound) {
+ if (CCoeEnv::Static()->AppUi()->IsDisplayingMenuOrDialog() || S60->menuBeingConstructed) {
+ QWidget *fw = QApplication::focusWidget();
+ if (fw) {
+ QFocusEvent event(QEvent::FocusOut, Qt::PopupFocusReason);
+ QCoreApplication::sendEvent(fw, &event);
+ }
+ m_symbianPopupIsOpen = true;
+ return;
+ }
+
+ QApplication::setActiveWindow(0);
+ }
+ }
+ // else { We don't touch the active window unless we were explicitly activated or deactivated }
+}
+
+void QSymbianControl::handleClientAreaChange()
+{
+ const bool cbaVisibilityHint = qwidget->windowFlags() & Qt::WindowSoftkeysVisibleHint;
+ if (qwidget->isFullScreen() && !cbaVisibilityHint) {
+ SetExtentToWholeScreen();
+ } else if (qwidget->isMaximized() || (qwidget->isFullScreen() && cbaVisibilityHint)) {
+ TRect r = static_cast<CEikAppUi*>(S60->appUi())->ClientRect();
+ SetExtent(r.iTl, r.Size());
+ } else if (!qwidget->isMinimized()) { // Normal geometry
+ if (!qwidget->testAttribute(Qt::WA_Resized)) {
+ qwidget->adjustSize();
+ qwidget->setAttribute(Qt::WA_Resized, false); //not a user resize
+ }
+ if (!qwidget->testAttribute(Qt::WA_Moved) && qwidget->windowType() != Qt::Dialog) {
+ TRect r = static_cast<CEikAppUi*>(S60->appUi())->ClientRect();
+ SetPosition(r.iTl);
+ qwidget->setAttribute(Qt::WA_Moved, false); // not really an explicit position
+ }
+ }
+}
+
+bool QSymbianControl::isSplitViewWidget(QWidget *widget) {
+ bool returnValue = true;
+ //Ignore events sent to non-active windows, not visible widgets and not parents of input widget.
+ if (!qwidget->isActiveWindow()
+ || !qwidget->isVisible()
+ || !qwidget->isAncestorOf(widget)) {
+
+ returnValue = false;
+ }
+ return returnValue;
+}
+
+void QSymbianControl::HandleResourceChange(int resourceType)
+{
+ switch (resourceType) {
+ case KSplitViewCloseEvent: //intentional fall-through
+ case KSplitViewOpenEvent: {
+#if !defined(QT_NO_IM) && defined(Q_WS_S60)
+
+ //Fetch widget getting the text input
+ QWidget *widget = QWidget::keyboardGrabber();
+ if (!widget) {
+ if (QApplicationPrivate::popupWidgets) {
+ widget = QApplication::activePopupWidget()->focusWidget();
+ if (!widget) {
+ widget = QApplication::activePopupWidget();
+ }
+ } else {
+ widget = QApplicationPrivate::focus_widget;
+ if (!widget) {
+ widget = qwidget;
+ }
+ }
+ }
+ if (widget) {
+ QCoeFepInputContext *ic = qobject_cast<QCoeFepInputContext *>(widget->inputContext());
+ if (!ic) {
+ ic = qobject_cast<QCoeFepInputContext *>(qApp->inputContext());
+ }
+ if (ic && isSplitViewWidget(widget)) {
+ if (resourceType == KSplitViewCloseEvent) {
+ ic->resetSplitViewWidget();
+ } else {
+ ic->ensureFocusWidgetVisible(widget);
+ }
+ }
+ }
+#endif // !defined(QT_NO_IM) && defined(Q_WS_S60)
+ }
+ break;
+ case KInternalStatusPaneChange:
+ // When status pane is not visible, only handle client area change if status pane was
+ // previously visible, as size changes to hidden status pane should not affect
+ // client area.
+ if (S60->statusPane() && (S60->statusPane()->IsVisible() || m_lastStatusPaneVisibility)) {
+ m_lastStatusPaneVisibility = S60->statusPane()->IsVisible();
+ handleClientAreaChange();
+ }
+ if (IsFocused() && IsVisible()) {
+ qwidget->d_func()->setWindowIcon_sys(true);
+ qwidget->d_func()->setWindowTitle_sys(qwidget->windowTitle());
+ }
+ break;
+ case KUidValueCoeFontChangeEvent:
+ // font change event
+ break;
+#ifdef Q_WS_S60
+ case KEikDynamicLayoutVariantSwitch:
+ {
+ handleClientAreaChange();
+ // Send resize event to trigger desktopwidget workAreaResized signal
+ if (qt_desktopWidget) {
+ QResizeEvent e(qt_desktopWidget->size(), qt_desktopWidget->size());
+ QApplication::sendEvent(qt_desktopWidget, &e);
+ }
+ break;
+ }
+#endif
+ default:
+ break;
+ }
+
+ CCoeControl::HandleResourceChange(resourceType);
+
+}
+void QSymbianControl::CancelLongTapTimer()
+{
+ m_longTapDetector->Cancel();
+}
+
+TTypeUid::Ptr QSymbianControl::MopSupplyObject(TTypeUid id)
+{
+ if (id.iUid == ETypeId)
+ return id.MakePtr(this);
+
+ return CCoeControl::MopSupplyObject(id);
+}
+
+void QSymbianControl::setFocusSafely(bool focus)
+{
+ // The stack hack in here is very unfortunate, but it is the only way to ensure proper
+ // focus in Symbian. If this is not executed, the control which happens to be on
+ // the top of the stack may randomly be assigned focus by Symbian, for example
+ // when creating new windows (specifically in CCoeAppUi::HandleStackChanged()).
+
+ // Close any popups.
+ CEikonEnv::Static()->EikAppUi()->StopDisplayingMenuBar();
+
+ if (focus) {
+ S60->appUi()->RemoveFromStack(this);
+ // Symbian doesn't automatically remove focus from the last focused control, so we need to
+ // remember it and clear focus ourselves.
+ if (lastFocusedControl && lastFocusedControl != this)
+ lastFocusedControl->SetFocus(false);
+ QT_TRAP_THROWING(S60->appUi()->AddToStackL(this,
+ ECoeStackPriorityDefault + 1, ECoeStackFlagStandard)); // Note the + 1
+ lastFocusedControl = this;
+ this->SetFocus(true);
+ } else {
+ S60->appUi()->RemoveFromStack(this);
+ QT_TRAP_THROWING(S60->appUi()->AddToStackL(this,
+ ECoeStackPriorityDefault, ECoeStackFlagStandard));
+ if(this == lastFocusedControl)
+ lastFocusedControl = 0;
+ this->SetFocus(false);
+ }
+}
+
+bool QSymbianControl::isControlActive()
+{
+ return IsActivated() ? true : false;
+}
+
+void QSymbianControl::ensureFixNativeOrientation()
+{
+#if defined(Q_SYMBIAN_SUPPORTS_FIXNATIVEORIENTATION)
+ if (!qwidget->isWindow() || qwidget->windowType() == Qt::Desktop)
+ return;
+ if (S60->screenNumberForWidget(qwidget) > 0)
+ return;
+ const bool isFixed = qwidget->d_func()->fixNativeOrientationCalled;
+ const bool isFixEnabled = qwidget->testAttribute(Qt::WA_SymbianNoSystemRotation);
+ const bool isFullScreen = qwidget->windowState().testFlag(Qt::WindowFullScreen);
+ if (isFullScreen && isFixEnabled) {
+ const bool surfaceBasedGs =
+ QApplicationPrivate::graphics_system_name == QLatin1String("openvg")
+ || QApplicationPrivate::graphics_system_name == QLatin1String("opengl");
+ if (!surfaceBasedGs)
+ qwidget->setAttribute(Qt::WA_SymbianNoSystemRotation, false);
+ if (!isFixed && surfaceBasedGs) {
+ if (Window().FixNativeOrientation() == KErrNone) {
+ qwidget->d_func()->fixNativeOrientationCalled = true;
+ // The EGL window surface is now fixed to the native orientation
+ // of the device, no matter what size we pass when creating it.
+ // Enforce the same size for the QWidget too. For the underlying
+ // CCoeControl and RWindow it is up to the system to resize them
+ // when the standard auto-rotation mechanism is in use, we must not
+ // change that behavior by forcing any size for those. In practice
+ // this means that the QWidget and the underlying native control
+ // dimensions will be out of sync when FixNativeOrientation was
+ // called and the device is turned to the non-native (typically
+ // landscape) orientation. The pointer event handling and certain
+ // functions like Draw() will need to compensate for this.
+ QSize newSize(S60->nativeScreenWidthInPixels, S60->nativeScreenHeightInPixels);
+ if (qwidget->size() != newSize)
+ qwidgetResize_helper(newSize);
+ } else {
+ qwidget->setAttribute(Qt::WA_SymbianNoSystemRotation, false);
+ }
+ }
+ } else if (isFixed) {
+ qwidget->setAttribute(Qt::WA_SymbianNoSystemRotation, false);
+ qwidget->d_func()->fixNativeOrientationCalled = false;
+ qwidget->hide();
+ qwidget->d_func()->create_sys(0, false, true);
+ qwidget->show();
+ }
+#else
+ qwidget->setAttribute(Qt::WA_SymbianNoSystemRotation, false);
+#endif
+}
+
+/*!
+ \typedef QApplication::QS60MainApplicationFactory
+ \since 4.6
+
+ This is a typedef for a pointer to a function with the following
+ signature:
+
+ \snippet doc/src/snippets/code/src_corelib_global_qglobal.cpp 47
+
+ \sa QApplication::QApplication()
+*/
+
+/*!
+ \since 4.6
+
+ Creates an application using the application factory given in
+ \a factory, and using \a argc command line arguments in \a argv.
+ \a factory can be leaving, but the error will be converted to a
+ standard exception.
+
+ This function is only available on S60.
+*/
+QApplication::QApplication(QApplication::QS60MainApplicationFactory factory, int &argc, char **argv)
+ : QCoreApplication(*new QApplicationPrivate(argc, argv, GuiClient, 0x040000))
+{
+ Q_D(QApplication);
+ S60->s60ApplicationFactory = factory;
+ d->construct();
+}
+
+QApplication::QApplication(QApplication::QS60MainApplicationFactory factory, int &argc, char **argv, int _internal)
+ : QCoreApplication(*new QApplicationPrivate(argc, argv, GuiClient, _internal))
+{
+ Q_D(QApplication);
+ S60->s60ApplicationFactory = factory;
+ d->construct();
+ QApplicationPrivate::app_compile_version = _internal;
+}
+
+void qt_init(QApplicationPrivate * /* priv */, int)
+{
+ if (!CCoeEnv::Static()) {
+ // The S60 framework creates a new trap handler which will render any existing traps
+ // invalid as long as it is active. This means that all code in main() that occurs after
+ // the QApplication construction needs to be surrounded by a new trap, despite having
+ // an outer one already. To avoid this, we save the original trap handler here, and set
+ // it back after the S60 framework is constructed. Then we restore it right before the S60
+ // framework destruction.
+ TTrapHandler *origTrapHandler = User::TrapHandler();
+
+ // The S60 framework has not been initialized. We need to do it.
+ TApaApplicationFactory factory(S60->s60ApplicationFactory ?
+ S60->s60ApplicationFactory : newS60Application);
+ CApaCommandLine* commandLine = q_check_ptr(QCoreApplicationPrivate::symbianCommandLine());
+ if (commandLine) {
+ // After this construction, CEikonEnv will be available from CEikonEnv::Static().
+ // (much like our qApp).
+ QtEikonEnv* coe = new QtEikonEnv;
+ //not using QT_TRAP_THROWING, because coe owns the cleanupstack so it can't be pushed there.
+ TRAPD(err, coe->ConstructAppFromCommandLineL(factory, *commandLine));
+ if(err != KErrNone) {
+ qWarning() << "qt_init: Eikon application construct failed ("
+ << err
+ << "), maybe missing resource file on S60 3.1?";
+ delete coe;
+ qt_symbian_throwIfError(err);
+ }
+ }
+
+ S60->s60InstalledTrapHandler = User::SetTrapHandler(origTrapHandler);
+
+ S60->qtOwnsS60Environment = true;
+ } else {
+ S60->qtOwnsS60Environment = false;
+ }
+
+#ifdef QT_NO_DEBUG
+ if (!qgetenv("QT_S60_AUTO_FLUSH_WSERV").isEmpty())
+#endif
+ S60->wsSession().SetAutoFlush(ETrue);
+
+#ifdef Q_SYMBIAN_WINDOW_SIZE_CACHE
+ TRAP_IGNORE(S60->wsSession().EnableWindowSizeCacheL());
+#endif
+
+ S60->updateScreenSize();
+
+
+ TDisplayMode mode = S60->screenDevice()->DisplayMode();
+ S60->screenDepth = TDisplayModeUtils::NumDisplayModeBitsPerPixel(mode);
+
+ //NB: RWsSession::GetColorModeList tells you what window modes are supported,
+ //not what bitmap formats.
+ if(QSysInfo::symbianVersion() == QSysInfo::SV_9_2)
+ S60->supportsPremultipliedAlpha = 0;
+ else
+ S60->supportsPremultipliedAlpha = 1;
+
+ RProcess me;
+ TSecureId securId = me.SecureId();
+ S60->uid = securId.operator TUid();
+
+ // enable focus events - used to re-enable mouse after focus changed between mouse and non mouse app,
+ // and for dimming behind modal windows
+ S60->windowGroup().EnableFocusChangeEvents();
+
+ //Check if mouse interaction is supported (either EMouse=1 in the HAL, or EMachineUID is one of the phones known to support this)
+ const TInt KMachineUidSamsungI8510 = 0x2000C51E;
+ // HAL::Get(HALData::EPen, TInt& result) may set 'result' to 1 on some 3.1 systems (e.g. N95).
+ // But we know that S60 systems below 5.0 did not support touch.
+ static const bool touchIsUnsupportedOnSystem =
+ QSysInfo::s60Version() == QSysInfo::SV_S60_3_1
+ || QSysInfo::s60Version() == QSysInfo::SV_S60_3_2;
+ TInt machineUID;
+ TInt mouse;
+ TInt touch;
+ TInt err;
+ err = HAL::Get(HALData::EMouse, mouse);
+ if (err != KErrNone)
+ mouse = 0;
+ err = HAL::Get(HALData::EMachineUid, machineUID);
+ if (err != KErrNone)
+ machineUID = 0;
+ err = HAL::Get(HALData::EPen, touch);
+ if (err != KErrNone || touchIsUnsupportedOnSystem)
+ touch = 0;
+#ifdef __WINS__
+ if(QSysInfo::symbianVersion() <= QSysInfo::SV_9_4) {
+ //for symbian SDK emulator, force values to match typical devices.
+ mouse = 0;
+ touch = touchIsUnsupportedOnSystem ? 0 : 1;
+ }
+#endif
+ if (mouse || machineUID == KMachineUidSamsungI8510) {
+ S60->hasTouchscreen = false;
+ S60->virtualMouseRequired = false;
+ }
+ else if (!touch) {
+ S60->hasTouchscreen = false;
+ S60->virtualMouseRequired = true;
+ }
+ else {
+ S60->hasTouchscreen = true;
+ S60->virtualMouseRequired = false;
+ }
+
+ S60->avkonComponentsSupportTransparency = false;
+ S60->menuBeingConstructed = false;
+
+#ifdef Q_WS_S60
+ TUid KCRUidAvkon = { 0x101F876E };
+ TUint32 KAknAvkonTransparencyEnabled = 0x0000000D;
+
+ CRepository* repository = 0;
+ TRAP(err, repository = CRepository::NewL(KCRUidAvkon));
+
+ if(err == KErrNone) {
+ TInt value = 0;
+ err = repository->Get(KAknAvkonTransparencyEnabled, value);
+ if(err == KErrNone) {
+ S60->avkonComponentsSupportTransparency = (value==1) ? true : false;
+ }
+ }
+ delete repository;
+ repository = 0;
+#endif
+
+ qt_keymapper_private()->updateInputLanguage();
+
+#ifdef QT_KEYPAD_NAVIGATION
+ if (touch) {
+ QApplicationPrivate::navigationMode = Qt::NavigationModeNone;
+ } else {
+ QApplicationPrivate::navigationMode = Qt::NavigationModeKeypadDirectional;
+ }
+#endif
+
+#ifndef QT_NO_CURSOR
+ //Check if window server pointer cursors are supported or not
+#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS
+ //In generic binary, use the HAL and OS version
+ //Any other known good phones should be added here.
+ if (machineUID == KMachineUidSamsungI8510 || (QSysInfo::symbianVersion() != QSysInfo::SV_9_4
+ && QSysInfo::symbianVersion() != QSysInfo::SV_9_3 && QSysInfo::symbianVersion()
+ != QSysInfo::SV_9_2)) {
+ S60->brokenPointerCursors = false;
+ qt_symbian_setWindowGroupCursor(Qt::ArrowCursor, S60->windowGroup());
+ }
+ else
+ S60->brokenPointerCursors = true;
+#endif
+
+ if (S60->mouseInteractionEnabled) {
+#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS
+ if (S60->brokenPointerCursors) {
+ qt_symbian_set_pointer_sprite(Qt::ArrowCursor);
+ qt_symbian_show_pointer_sprite();
+ }
+ else
+#endif
+ S60->wsSession().SetPointerCursorMode(EPointerCursorNormal);
+ }
+#endif
+
+ QFont systemFont;
+ systemFont.setFamily(systemFont.defaultFamily());
+ QApplicationPrivate::setSystemFont(systemFont);
+
+ QObject::connect(qApp, SIGNAL(aboutToQuit()), qApp, SLOT(_q_aboutToQuit()));
+
+#ifdef Q_SYMBIAN_SEMITRANSPARENT_BG_SURFACE
+ QApplicationPrivate::instance()->useTranslucentEGLSurfaces = true;
+
+ const TUid KIvePropertyCat = {0x2726beef};
+ enum TIvePropertyChipType {
+ EVCBCM2727B1 = 0x00000000,
+ EVCBCM2763A0 = 0x04000100,
+ EVCBCM2763B0 = 0x04000102,
+ EVCBCM2763C0 = 0x04000103,
+ EVCBCM2763C1 = 0x04000104,
+ EVCBCMUnknown = 0x7fffffff
+ };
+
+ TInt chipType = EVCBCMUnknown;
+ if (RProperty::Get(KIvePropertyCat, 0 /*chip type*/, chipType) == KErrNone) {
+ if (chipType == EVCBCM2727B1) {
+ // We have only 32MB GPU memory. Use raster surfaces
+ // for transparent TLWs.
+ QApplicationPrivate::instance()->useTranslucentEGLSurfaces = false;
+ }
+ } else {
+ QApplicationPrivate::instance()->useTranslucentEGLSurfaces = false;
+ }
+ if (QApplicationPrivate::graphics_system_name == QLatin1String("raster"))
+ QApplicationPrivate::instance()->useTranslucentEGLSurfaces = false;
+#else
+ QApplicationPrivate::instance()->useTranslucentEGLSurfaces = false;
+#endif
+/*
+ ### Commented out for now as parameter handling not needed in SOS(yet). Code below will break testlib with -o flag
+ int argc = priv->argc;
+ char **argv = priv->argv;
+
+ // Get command line params
+ int j = argc ? 1 : 0;
+ for (int i=1; i<argc; i++) {
+ if (argv[i] && *argv[i] != '-') {
+ argv[j++] = argv[i];
+ continue;
+ }
+
+#if defined(QT_DEBUG)
+ if (qstrcmp(argv[i], "-nograb") == 0)
+ appNoGrab = !appNoGrab;
+ else
+#endif // QT_DEBUG
+ ;
+ }
+*/
+
+ // Register WId with the metatype system. This is to enable
+ // QWidgetPrivate::create_sys to used delayed slot invocation in order
+ // to destroy WId objects during reparenting.
+ qRegisterMetaType<WId>("WId");
+}
+
+#ifdef QT_NO_FREETYPE
+extern void qt_cleanup_symbianFontDatabase(); // qfontdatabase_s60.cpp
+#endif
+
+/*****************************************************************************
+ qt_cleanup() - cleans up when the application is finished
+ *****************************************************************************/
+void qt_cleanup()
+{
+#ifdef Q_WS_S60
+ S60->setButtonGroupContainer(0);
+#endif
+ if(qt_S60Beep) {
+ delete qt_S60Beep;
+ qt_S60Beep = 0;
+ }
+ QFontCache::cleanup(); // Has to happen now, since QFontEngineS60 has FBS handles
+ QPixmapCache::clear(); // Has to happen now, since QS60PlatformPixmap has FBS handles
+
+#ifdef QT_NO_FREETYPE
+ qt_cleanup_symbianFontDatabase();
+#endif
+// S60 structure and window server session are freed in eventdispatcher destructor as they are needed there
+
+ // It's important that this happens here, before the event dispatcher gets
+ // deleted, because the input context needs the event loop one last time before
+ // it dies.
+ delete QApplicationPrivate::inputContext;
+ QApplicationPrivate::inputContext = 0;
+
+ //Change mouse pointer back
+ S60->wsSession().SetPointerCursorMode(EPointerCursorNone);
+
+#ifdef Q_WS_S60
+ // Clear CBA
+ CEikonEnv::Static()->AppUiFactory()->SwapButtonGroup(0);
+ delete S60->buttonGroupContainer();
+ S60->setButtonGroupContainer(0);
+#endif
+
+ // Call EndFullScreen() to prevent confusing the system effect state machine.
+ qt_endFullScreenEffect();
+
+ if (S60->qtOwnsS60Environment) {
+ // Restore the S60 framework trap handler. See qt_init().
+ User::SetTrapHandler(S60->s60InstalledTrapHandler);
+
+ CEikonEnv* coe = CEikonEnv::Static();
+ coe->PrepareToExit();
+ // The CEikonEnv itself is destroyed in here.
+ coe->DestroyEnvironment();
+ }
+}
+
+void QApplicationPrivate::initializeWidgetPaletteHash()
+{
+ // TODO: Implement QApplicationPrivate::initializeWidgetPaletteHash()
+ // Possibly a task fot the S60Style guys
+}
+
+void QApplicationPrivate::createEventDispatcher()
+{
+ Q_Q(QApplication);
+ eventDispatcher = new QEventDispatcherS60(q);
+}
+
+QString QApplicationPrivate::appName() const
+{
+ return QCoreApplicationPrivate::appName();
+}
+
+bool QApplicationPrivate::modalState()
+{
+ return app_do_modal;
+}
+
+void QApplicationPrivate::enterModal_sys(QWidget *widget)
+{
+#ifdef Q_SYMBIAN_TRANSITION_EFFECTS
+ S60->wsSession().SendEffectCommand(ETfxCmdAppModalModeEnter);
+#endif
+ if (widget) {
+ static_cast<QSymbianControl *>(widget->effectiveWinId())->FadeBehindPopup(ETrue);
+ // Modal partial screen dialogs (like queries) capture pointer events.
+ // ### FixMe: Add specialized behaviour for fullscreen modal dialogs
+ widget->effectiveWinId()->SetGloballyCapturing(ETrue);
+ widget->effectiveWinId()->SetPointerCapture(ETrue);
+ }
+ if (!qt_modal_stack)
+ qt_modal_stack = new QWidgetList;
+ qt_modal_stack->insert(0, widget);
+ app_do_modal = true;
+}
+
+void QApplicationPrivate::leaveModal_sys(QWidget *widget)
+{
+#ifdef Q_SYMBIAN_TRANSITION_EFFECTS
+ S60->wsSession().SendEffectCommand(ETfxCmdAppModalModeExit);
+#endif
+ if (widget) {
+ static_cast<QSymbianControl *>(widget->effectiveWinId())->FadeBehindPopup(EFalse);
+ // ### FixMe: Add specialized behaviour for fullscreen modal dialogs
+ widget->effectiveWinId()->SetGloballyCapturing(EFalse);
+ widget->effectiveWinId()->SetPointerCapture(EFalse);
+ }
+ if (qt_modal_stack && qt_modal_stack->removeAll(widget)) {
+ if (qt_modal_stack->isEmpty()) {
+ delete qt_modal_stack;
+ qt_modal_stack = 0;
+ }
+ }
+ app_do_modal = qt_modal_stack != 0;
+}
+
+void QApplicationPrivate::openPopup(QWidget *popup)
+{
+ if (popup && qobject_cast<QComboBox *>(popup->parentWidget()))
+ static_cast<QSymbianControl *>(popup->effectiveWinId())->FadeBehindPopup(ETrue);
+
+ if (!QApplicationPrivate::popupWidgets)
+ QApplicationPrivate::popupWidgets = new QWidgetList;
+ QApplicationPrivate::popupWidgets->append(popup);
+
+ // Cancel focus widget pointer capture and long tap timer
+ if (QApplication::focusWidget()) {
+ static_cast<QSymbianControl*>(QApplication::focusWidget()->effectiveWinId())->CancelLongTapTimer();
+ QApplication::focusWidget()->effectiveWinId()->SetPointerCapture(false);
+ }
+
+ if (!qt_nograb()) {
+ // Cancel pointer capture and long tap timer for earlier popup
+ int popupCount = QApplicationPrivate::popupWidgets->count();
+ if (popupCount > 1) {
+ QWidget* prevPopup = QApplicationPrivate::popupWidgets->at(popupCount-2);
+ static_cast<QSymbianControl*>(prevPopup->effectiveWinId())->CancelLongTapTimer();
+ prevPopup->effectiveWinId()->SetPointerCapture(false);
+ }
+
+ // Enable pointer capture for this (topmost) popup
+ Q_ASSERT(popup->testAttribute(Qt::WA_WState_Created));
+ WId id = popup->effectiveWinId();
+ id->SetPointerCapture(true);
+ }
+
+ // popups are not focus-handled by the window system (the first
+ // popup grabbed the keyboard), so we have to do that manually: A
+ // new popup gets the focus
+ QWidget *fw = popup->focusWidget();
+ if (fw) {
+ fw->setFocus(Qt::PopupFocusReason);
+ } else if (QApplicationPrivate::popupWidgets->count() == 1) { // this was the first popup
+ fw = QApplication::focusWidget();
+ if (fw) {
+ QFocusEvent e(QEvent::FocusOut, Qt::PopupFocusReason);
+ q_func()->sendEvent(fw, &e);
+ }
+ }
+}
+
+void QApplicationPrivate::closePopup(QWidget *popup)
+{
+ if (popup && qobject_cast<QComboBox *>(popup->parentWidget()))
+ static_cast<QSymbianControl *>(popup->effectiveWinId())->FadeBehindPopup(EFalse);
+
+ if (!QApplicationPrivate::popupWidgets)
+ return;
+ QApplicationPrivate::popupWidgets->removeAll(popup);
+
+ // Cancel pointer capture and long tap for this popup
+ WId id = popup->effectiveWinId();
+ id->SetPointerCapture(false);
+ static_cast<QSymbianControl*>(id)->CancelLongTapTimer();
+
+ if (QApplicationPrivate::popupWidgets->isEmpty()) { // this was the last popup
+ delete QApplicationPrivate::popupWidgets;
+ QApplicationPrivate::popupWidgets = 0;
+ if (!qt_nograb()) { // grabbing not disabled
+ Q_ASSERT(popup->testAttribute(Qt::WA_WState_Created));
+ if (QWidgetPrivate::mouseGrabber != 0)
+ QWidgetPrivate::mouseGrabber->grabMouse();
+
+ if (QWidgetPrivate::keyboardGrabber != 0)
+ QWidgetPrivate::keyboardGrabber->grabKeyboard();
+
+ QWidget *fw = QApplicationPrivate::active_window ? QApplicationPrivate::active_window->focusWidget()
+ : q_func()->focusWidget();
+ if (fw) {
+ if(fw->window()->isModal()) // restore pointer capture for modal window
+ fw->effectiveWinId()->SetPointerCapture(true);
+
+ if (fw != q_func()->focusWidget()) {
+ fw->setFocus(Qt::PopupFocusReason);
+ } else {
+ QFocusEvent e(QEvent::FocusIn, Qt::PopupFocusReason);
+ q_func()->sendEvent(fw, &e);
+ }
+ }
+ }
+ } else {
+
+ // popups are not focus-handled by the window system (the
+ // first popup grabbed the keyboard), so we have to do that
+ // manually: A popup was closed, so the previous popup gets
+ // the focus.
+ QWidget* aw = QApplicationPrivate::popupWidgets->last();
+ if (QWidget *fw = QApplication::focusWidget()) {
+ QFocusEvent e(QEvent::FocusOut, Qt::PopupFocusReason);
+ q_func()->sendEvent(fw, &e);
+ }
+
+ // Enable pointer capture for previous popup
+ if (aw) {
+ aw->effectiveWinId()->SetPointerCapture(true);
+ }
+ }
+}
+
+QWidget * QApplication::topLevelAt(QPoint const& point)
+{
+ QWidget *found = 0;
+ int lowestZ = INT_MAX;
+ QWidgetList list = QApplication::topLevelWidgets();
+ for (int i = 0; i < list.count(); ++i) {
+ QWidget *widget = list.at(i);
+ if (widget->isVisible() && !(widget->windowType() == Qt::Desktop)) {
+ Q_ASSERT(widget->testAttribute(Qt::WA_WState_Created));
+ if (widget->geometry().adjusted(0,0,1,1).contains(point)) {
+ // At this point we know there is a Qt widget under the point.
+ // Now we need to make sure it is the top most in the z-order.
+ RDrawableWindow *const window = widget->effectiveWinId()->DrawableWindow();
+ int z = window->OrdinalPosition();
+ if (z < lowestZ) {
+ lowestZ = z;
+ found = widget;
+ }
+ }
+ }
+ }
+ return found;
+}
+
+void QApplication::alert(QWidget * /* widget */, int /* duration */)
+{
+ // TODO: Implement QApplication::alert(QWidget *widget, int duration)
+}
+
+int QApplication::doubleClickInterval()
+{
+ TTimeIntervalMicroSeconds32 us;
+ TInt distance;
+ S60->wsSession().GetDoubleClickSettings(us, distance);
+ return (us.Int() / 1000);
+}
+
+void QApplication::setDoubleClickInterval(int ms)
+{
+ TTimeIntervalMicroSeconds32 newUs( ms * 1000);
+ TTimeIntervalMicroSeconds32 us;
+ TInt distance;
+ S60->wsSession().GetDoubleClickSettings(us, distance);
+ if (us != newUs)
+ S60->wsSession().SetDoubleClick(newUs, distance);
+}
+
+int QApplication::keyboardInputInterval()
+{
+ return QApplicationPrivate::keyboard_input_time;
+}
+
+void QApplication::setKeyboardInputInterval(int ms)
+{
+ QApplicationPrivate::keyboard_input_time = ms;
+}
+
+int QApplication::cursorFlashTime()
+{
+ return QApplicationPrivate::cursor_flash_time;
+}
+
+void QApplication::setCursorFlashTime(int msecs)
+{
+ QApplicationPrivate::cursor_flash_time = msecs;
+}
+
+void QApplication::beep()
+{
+ if (!qt_S60Beep) {
+ TInt frequency = 880;
+ TTimeIntervalMicroSeconds duration(500000);
+ TRAP_IGNORE(qt_S60Beep=QS60Beep::NewL(frequency, duration));
+ }
+ if (qt_S60Beep)
+ qt_S60Beep->Play();
+}
+
+static inline bool callSymbianEventFilters(const QSymbianEvent *event)
+{
+ long unused;
+ return qApp->filterEvent(const_cast<QSymbianEvent *>(event), &unused);
+}
+
+/*!
+ \warning This function is only available on Symbian.
+ \since 4.6
+
+ This function processes an individual Symbian event
+ \a event. It returns 1 if the event was handled, 0 if
+ the \a event was not handled, and -1 if the event was
+ not handled because the event is not known to Qt.
+ */
+
+int QApplication::symbianProcessEvent(const QSymbianEvent *event)
+{
+ Q_D(QApplication);
+
+ QScopedLoopLevelCounter counter(d->threadData);
+
+ if (d->eventDispatcher->filterEvent(const_cast<QSymbianEvent *>(event)))
+ return 1;
+
+ QWidget *w = qApp ? qApp->focusWidget() : 0;
+ if (w) {
+ QInputContext *ic = w->inputContext();
+ if (ic && ic->symbianFilterEvent(w, event))
+ return 1;
+ }
+
+ if (symbianEventFilter(event))
+ return 1;
+
+ switch (event->type()) {
+ case QSymbianEvent::WindowServerEvent:
+ return d->symbianProcessWsEvent(event);
+ case QSymbianEvent::CommandEvent:
+ return d->symbianHandleCommand(event);
+ case QSymbianEvent::ResourceChangeEvent:
+ return d->symbianResourceChange(event);
+ default:
+ return -1;
+ }
+}
+
+int QApplicationPrivate::symbianProcessWsEvent(const QSymbianEvent *symbianEvent)
+{
+ // Qt event handling. Handle some events regardless of if the handle is in our
+ // widget map or not.
+ const TWsEvent *event = symbianEvent->windowServerEvent();
+ CCoeControl* control = reinterpret_cast<CCoeControl*>(event->Handle());
+ const bool controlInMap = QWidgetPrivate::mapper && QWidgetPrivate::mapper->contains(control);
+ switch (event->Type()) {
+ case EEventPointerEnter:
+ if (controlInMap) {
+ callSymbianEventFilters(symbianEvent);
+ return 1; // Qt::Enter will be generated in HandlePointerL
+ }
+ break;
+ case EEventPointerExit:
+ if (controlInMap) {
+ if (callSymbianEventFilters(symbianEvent))
+ return 1;
+ if (S60) {
+ // mouseEvent outside our window, send leave event to last focused widget
+ QMouseEvent mEvent(QEvent::Leave, S60->lastPointerEventPos, S60->lastCursorPos,
+ Qt::NoButton, QApplicationPrivate::mouse_buttons, Qt::NoModifier);
+ if (S60->lastPointerEventTarget)
+ qt_sendSpontaneousEvent(S60->lastPointerEventTarget,&mEvent);
+ S60->lastPointerEventTarget = 0;
+ }
+ return 1;
+ }
+ break;
+ case EEventScreenDeviceChanged: // fallthrough
+#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS)
+ case EEventDisplayChanged:
+#endif
+ if (callSymbianEventFilters(symbianEvent))
+ return 1;
+ if (S60)
+ S60->updateScreenSize();
+ if (qt_desktopWidget) {
+ QSize oldSize = qt_desktopWidget->size();
+ qt_desktopWidget->data->crect.setWidth(S60->screenWidthInPixels);
+ qt_desktopWidget->data->crect.setHeight(S60->screenHeightInPixels);
+ QResizeEvent e(qt_desktopWidget->size(), oldSize);
+ QApplication::sendEvent(qt_desktopWidget, &e);
+ }
+ return 0; // Propagate to CONE
+ case EEventWindowVisibilityChanged:
+ if (controlInMap) {
+ if (callSymbianEventFilters(symbianEvent))
+ return 1;
+ const TWsVisibilityChangedEvent *visChangedEvent = event->VisibilityChanged();
+ if (visChangedEvent->iFlags & TWsVisibilityChangedEvent::ENotVisible)
+ S60->controlVisibilityChanged(control, false);
+ else if (visChangedEvent->iFlags & TWsVisibilityChangedEvent::EPartiallyVisible)
+ S60->controlVisibilityChanged(control, true);
+ return 1;
+ }
+ break;
+ case EEventFocusGained:
+ if (callSymbianEventFilters(symbianEvent))
+ return 1;
+#ifndef QT_NO_CURSOR
+ //re-enable mouse interaction
+ if (S60->mouseInteractionEnabled) {
+#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS
+ if (S60->brokenPointerCursors)
+ qt_symbian_show_pointer_sprite();
+ else
+#endif
+ S60->wsSession().SetPointerCursorMode(EPointerCursorNormal);
+ }
+#endif
+#ifdef QT_SOFTKEYS_ENABLED
+ if (!CEikonEnv::Static()->EikAppUi()->IsDisplayingMenuOrDialog())
+ QSoftKeyManager::updateSoftKeys();
+#endif
+ break;
+ case EEventFocusLost:
+ if (callSymbianEventFilters(symbianEvent))
+ return 1;
+#ifndef QT_NO_CURSOR
+ //disable mouse as may be moving to application that does not support it
+ if (S60->mouseInteractionEnabled) {
+#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS
+ if (S60->brokenPointerCursors)
+ qt_symbian_hide_pointer_sprite();
+ else
+#endif
+ S60->wsSession().SetPointerCursorMode(EPointerCursorNone);
+ }
+#endif
+ break;
+ case KGoomMemoryLowEvent:
+#ifdef QT_DEBUG
+ qDebug() << "QApplicationPrivate::symbianProcessWsEvent - KGoomMemoryLowEvent";
+#endif
+ if (callSymbianEventFilters(symbianEvent))
+ return 1;
+#ifdef QT_GRAPHICSSYSTEM_RUNTIME
+ if(QApplicationPrivate::runtime_graphics_system) {
+ bool switchToSwRendering(false);
+
+ foreach (QWidget *w, QApplication::topLevelWidgets()) {
+ if(w->d_func()->topData()->backingStore) {
+ switchToSwRendering = true;
+ break;
+ }
+ }
+
+ if (switchToSwRendering) {
+ QRuntimeGraphicsSystem *gs =
+ static_cast<QRuntimeGraphicsSystem*>(QApplicationPrivate::graphics_system);
+ gs->setGraphicsSystem(QLatin1String("raster"));
+ }
+ }
+#endif
+ break;
+ case KGoomMemoryGoodEvent:
+#ifdef QT_DEBUG
+ qDebug() << "QApplicationPrivate::symbianProcessWsEvent - KGoomMemoryGoodEvent";
+#endif
+ if (callSymbianEventFilters(symbianEvent))
+ return 1;
+#ifdef QT_GRAPHICSSYSTEM_RUNTIME
+ if(QApplicationPrivate::runtime_graphics_system) {
+ QRuntimeGraphicsSystem *gs =
+ static_cast<QRuntimeGraphicsSystem*>(QApplicationPrivate::graphics_system);
+ gs->setGraphicsSystem(QLatin1String("openvg"));
+ }
+#endif
+ break;
+#ifdef Q_SYMBIAN_SUPPORTS_SURFACES
+ case EEventUser:
+ {
+ // GOOM is looking for candidates to kill so indicate that we are
+ // capable of cleaning up by handling this event
+ TInt32 *data = reinterpret_cast<TInt32 *>(event->EventData());
+ if (data[0] == EApaSystemEventShutdown && data[1] == KGoomMemoryLowEvent)
+ return 1;
+ }
+ break;
+#endif
+
+#ifdef Q_WS_S60
+ case KEikInputLanguageChange:
+ qt_keymapper_private()->updateInputLanguage();
+ break;
+#endif
+
+ default:
+ break;
+ }
+
+ if (!controlInMap)
+ return -1;
+
+ return 0;
+}
+
+/*!
+ \warning This virtual function is only available on Symbian.
+ \since 4.6
+
+ If you create an application that inherits QApplication and reimplement
+ this function, you get direct access to events that the are received
+ from Symbian. The events are passed in the \a event parameter.
+
+ Return true if you want to stop the event from being processed. Return
+ false for normal event dispatching. The default implementation returns
+ false, and does nothing with \a event.
+ */
+bool QApplication::symbianEventFilter(const QSymbianEvent *event)
+{
+ Q_UNUSED(event);
+ return false;
+}
+
+/*!
+ \warning This function is only available on Symbian.
+ \since 4.6
+
+ Handles \a{command}s which are typically handled by
+ CAknAppUi::HandleCommandL(). Qts Ui integration into Symbian is
+ partially achieved by deriving from CAknAppUi. Currently, exit,
+ menu and softkey commands are handled.
+
+ \sa s60EventFilter(), s60ProcessEvent()
+*/
+int QApplicationPrivate::symbianHandleCommand(const QSymbianEvent *symbianEvent)
+{
+ Q_Q(QApplication);
+ int ret = 0;
+
+ if (callSymbianEventFilters(symbianEvent))
+ return 1;
+
+ int command = symbianEvent->command();
+
+ switch (command) {
+#ifdef Q_WS_S60
+ case EAknSoftkeyExit: {
+ QCloseEvent ev;
+ QApplication::sendSpontaneousEvent(q, &ev);
+ if (ev.isAccepted()) {
+ q->quit();
+ ret = 1;
+ }
+ break;
+ }
+#endif
+ case EEikCmdExit:
+ q->quit();
+ ret = 1;
+ break;
+ default:
+#ifdef Q_WS_S60
+ bool handled = QSoftKeyManager::handleCommand(command);
+ if (handled)
+ ret = 1;
+ else
+ ret = QMenuBarPrivate::symbianCommands(command);
+#endif
+ break;
+ }
+
+ return ret;
+}
+
+/*!
+ \warning This function is only available on Symbian.
+ \since 4.6
+
+ Handles the resource change specified by \a type.
+
+ Currently, KEikDynamicLayoutVariantSwitch and
+ KAknsMessageSkinChange are handled.
+ */
+int QApplicationPrivate::symbianResourceChange(const QSymbianEvent *symbianEvent)
+{
+ int ret = 0;
+
+ int type = symbianEvent->resourceChangeType();
+
+ switch (type) {
+#ifdef Q_WS_S60
+ case KEikDynamicLayoutVariantSwitch:
+ {
+ if (callSymbianEventFilters(symbianEvent))
+ return 1;
+ if (S60)
+ S60->updateScreenSize();
+
+#ifndef QT_NO_STYLE_S60
+ QS60Style *s60Style = 0;
+
+#ifndef QT_NO_STYLE_STYLESHEET
+ QStyleSheetStyle *proxy = qobject_cast<QStyleSheetStyle*>(QApplication::style());
+ if (proxy)
+ s60Style = qobject_cast<QS60Style*>(proxy->baseStyle());
+ else
+#endif
+ s60Style = qobject_cast<QS60Style*>(QApplication::style());
+
+ if (s60Style) {
+ s60Style->d_func()->handleDynamicLayoutVariantSwitch();
+ ret = 1;
+ }
+#endif
+ }
+ break;
+
+#ifndef QT_NO_STYLE_S60
+ case KAknsMessageSkinChange:
+ if (callSymbianEventFilters(symbianEvent))
+ return 1;
+ if (QS60Style *s60Style = qobject_cast<QS60Style*>(QApplication::style())) {
+ s60Style->d_func()->handleSkinChange();
+ ret = 1;
+ }
+ break;
+#endif
+#endif // Q_WS_S60
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+#ifndef QT_NO_WHEELEVENT
+int QApplication::wheelScrollLines()
+{
+ return QApplicationPrivate::wheel_scroll_lines;
+}
+
+void QApplication::setWheelScrollLines(int n)
+{
+ QApplicationPrivate::wheel_scroll_lines = n;
+}
+#endif //QT_NO_WHEELEVENT
+
+bool QApplication::isEffectEnabled(Qt::UIEffect /* effect */)
+{
+ // TODO: Implement QApplication::isEffectEnabled(Qt::UIEffect effect)
+ return false;
+}
+
+void QApplication::setEffectEnabled(Qt::UIEffect /* effect */, bool /* enable */)
+{
+ // TODO: Implement QApplication::setEffectEnabled(Qt::UIEffect effect, bool enable)
+}
+
+TUint QApplicationPrivate::resolveS60ScanCode(TInt scanCode, TUint keysym)
+{
+ if (!scanCode)
+ return keysym;
+
+ QApplicationPrivate *d = QApplicationPrivate::instance();
+
+ if (keysym) {
+ // If keysym is specified, cache it.
+ d->scanCodeCache.insert(scanCode, keysym);
+ return keysym;
+ } else {
+ // If not, retrieve the cached version.
+ return d->scanCodeCache[scanCode];
+ }
+}
+
+void QApplicationPrivate::initializeMultitouch_sys()
+{
+#ifdef QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER
+ if (HAL::Get(HALData::EPointer3DPressureSupported, pressureSupported) != KErrNone)
+ pressureSupported = 0;
+ if (HAL::Get(HALData::EPointer3DMaxPressure, maxTouchPressure) != KErrNone)
+ maxTouchPressure = KMaxTInt;
+#else
+ pressureSupported = 0;
+ maxTouchPressure = KMaxTInt;
+#endif
+}
+
+void QApplicationPrivate::cleanupMultitouch_sys()
+{ }
+
+#ifndef QT_NO_SESSIONMANAGER
+QSessionManager::QSessionManager(QApplication * /* app */, QString & /* id */, QString& /* key */)
+{
+
+}
+
+QSessionManager::~QSessionManager()
+{
+
+}
+
+bool QSessionManager::allowsInteraction()
+{
+ return false;
+}
+
+void QSessionManager::cancel()
+{
+
+}
+#endif //QT_NO_SESSIONMANAGER
+
+#ifdef QT_KEYPAD_NAVIGATION
+/*
+ * Show/Hide the mouse cursor depending on phone type and chosen mode
+ */
+void QApplicationPrivate::setNavigationMode(Qt::NavigationMode mode)
+{
+#ifndef QT_NO_CURSOR
+ const bool wasCursorOn = (QApplicationPrivate::navigationMode == Qt::NavigationModeCursorAuto
+ && !S60->hasTouchscreen)
+ || QApplicationPrivate::navigationMode == Qt::NavigationModeCursorForceVisible;
+ const bool isCursorOn = (mode == Qt::NavigationModeCursorAuto
+ && !S60->hasTouchscreen)
+ || mode == Qt::NavigationModeCursorForceVisible;
+
+ if (!wasCursorOn && isCursorOn) {
+ //Show the cursor, when changing from another mode to cursor mode
+ qt_symbian_set_cursor_visible(true);
+ }
+ else if (wasCursorOn && !isCursorOn) {
+ //Hide the cursor, when leaving cursor mode
+ qt_symbian_set_cursor_visible(false);
+ }
+#endif
+ QApplicationPrivate::navigationMode = mode;
+}
+#endif
+
+#ifndef QT_NO_CURSOR
+/*****************************************************************************
+ QApplication cursor stack
+ *****************************************************************************/
+
+void QApplication::setOverrideCursor(const QCursor &cursor)
+{
+ qApp->d_func()->cursor_list.prepend(cursor);
+ qt_symbian_setGlobalCursor(cursor);
+}
+
+void QApplication::restoreOverrideCursor()
+{
+ if (qApp->d_func()->cursor_list.isEmpty())
+ return;
+ qApp->d_func()->cursor_list.removeFirst();
+
+ if (!qApp->d_func()->cursor_list.isEmpty()) {
+ qt_symbian_setGlobalCursor(qApp->d_func()->cursor_list.first());
+ }
+ else {
+ //determine which widget has focus
+ QWidget *w = QApplication::widgetAt(QCursor::pos());
+#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS
+ if (S60->brokenPointerCursors) {
+ qt_symbian_set_pointer_sprite(w ? w->cursor() : Qt::ArrowCursor);
+ }
+ else
+#endif
+ {
+ //because of the internals of window server, we need to force the cursor
+ //to be set in all child windows too, otherwise when the cursor is over
+ //the child window it may show a widget cursor or arrow cursor instead,
+ //depending on construction order.
+ QListIterator<WId> iter(QWidgetPrivate::mapper->uniqueKeys());
+ while (iter.hasNext()) {
+ CCoeControl *ctrl = iter.next();
+ if(ctrl->OwnsWindow()) {
+ ctrl->DrawableWindow()->ClearPointerCursor();
+ }
+ }
+ if (w)
+ qt_symbian_setWindowCursor(w->cursor(), w->effectiveWinId());
+ else
+ qt_symbian_setWindowGroupCursor(Qt::ArrowCursor, S60->windowGroup());
+ }
+ }
+}
+
+#endif // QT_NO_CURSOR
+
+void QApplicationPrivate::_q_aboutToQuit()
+{
+ qt_beginFullScreenEffect();
+
+#ifdef Q_SYMBIAN_TRANSITION_EFFECTS
+ // Send the shutdown tfx command
+ S60->wsSession().SendEffectCommand(ETfxCmdAppShutDown);
+#endif
+}
+
+QS60ThreadLocalData::QS60ThreadLocalData()
+{
+ CCoeEnv *env = CCoeEnv::Static();
+ if (env) {
+ //if this is the UI thread, share objects owned by CONE
+ usingCONEinstances = true;
+ wsSession = env->WsSession();
+ screenDevice = env->ScreenDevice();
+ }
+ else {
+ usingCONEinstances = false;
+ qt_symbian_throwIfError(wsSession.Connect(qt_s60GetRFs()));
+ screenDevice = new CWsScreenDevice(wsSession);
+ screenDevice->Construct();
+ }
+}
+
+QS60ThreadLocalData::~QS60ThreadLocalData()
+{
+ for (int i = 0; i < releaseFuncs.count(); ++i)
+ releaseFuncs[i]();
+ releaseFuncs.clear();
+ if (!usingCONEinstances) {
+ delete screenDevice;
+ wsSession.Close();
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/s60/qclipboard_s60.cpp b/src/widgets/platforms/s60/qclipboard_s60.cpp
new file mode 100644
index 0000000000..f5314bd9e7
--- /dev/null
+++ b/src/widgets/platforms/s60/qclipboard_s60.cpp
@@ -0,0 +1,331 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qclipboard.h"
+
+#ifndef QT_NO_CLIPBOARD
+
+#include "qapplication.h"
+#include "qbitmap.h"
+#include "qdatetime.h"
+#include "qbuffer.h"
+#include "qwidget.h"
+#include "qevent.h"
+#include "private/qcore_symbian_p.h"
+#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
+#include "txtclipboard.h"
+#endif
+#include "txtetext.h"
+#include <QtDebug>
+
+// Symbian's clipboard
+#include <baclipb.h>
+QT_BEGIN_NAMESPACE
+
+const TUid KQtCbDataStream = {0x2001B2DD};
+const TInt KPlainTextBegin = 0;
+
+class QClipboardData
+{
+public:
+ QClipboardData();
+ ~QClipboardData();
+
+ void setSource(QMimeData* s)
+ {
+ if (s == src)
+ return;
+ delete src;
+ src = s;
+ }
+ QMimeData* source()
+ { return src; }
+ bool connected()
+ { return connection; }
+ void clear();
+
+private:
+ QMimeData* src;
+ bool connection;
+};
+
+QClipboardData::QClipboardData():src(0),connection(true)
+{
+ clear();
+}
+
+QClipboardData::~QClipboardData()
+{
+ connection = false;
+ delete src;
+}
+
+void QClipboardData::clear()
+{
+ QMimeData* newSrc = new QMimeData;
+ delete src;
+ src = newSrc;
+}
+
+static QClipboardData *internalCbData = 0;
+
+static void cleanupClipboardData()
+{
+ delete internalCbData;
+ internalCbData = 0;
+}
+
+static QClipboardData *clipboardData()
+{
+ if (internalCbData == 0) {
+ internalCbData = new QClipboardData;
+ if (internalCbData)
+ {
+ if (!internalCbData->connected())
+ {
+ delete internalCbData;
+ internalCbData = 0;
+ }
+ else
+ {
+ qAddPostRoutine(cleanupClipboardData);
+ }
+ }
+ }
+ return internalCbData;
+}
+
+void writeToStreamLX(const QMimeData* aData, RWriteStream& aStream)
+{
+ // This function both leaves and throws exceptions. There must be no destructor
+ // dependencies between cleanup styles, and no cleanup stack dependencies on stacked objects.
+ QStringList headers = aData->formats();
+ aStream << TCardinality(headers.count());
+ for (QStringList::const_iterator iter= headers.constBegin();iter != headers.constEnd();iter++)
+ {
+ HBufC* stringData = TPtrC(reinterpret_cast<const TUint16*>((*iter).utf16())).AllocLC();
+ QByteArray ba = aData->data((*iter));
+ // mime type
+ aStream << TCardinality(stringData->Size());
+ aStream << *(stringData);
+ // mime data
+ aStream << TCardinality(ba.size());
+ aStream.WriteL(reinterpret_cast<const uchar*>(ba.constData()),ba.size());
+ CleanupStack::PopAndDestroy(stringData);
+ }
+}
+
+void writeToSymbianStoreLX(const QMimeData* aData, CClipboard* clipboard)
+{
+ // This function both leaves and throws exceptions. There must be no destructor
+ // dependencies between cleanup styles, and no cleanup stack dependencies on stacked objects.
+ if (aData->hasText()) {
+ CPlainText* text = CPlainText::NewL();
+ CleanupStack::PushL(text);
+
+ TPtrC textPtr(qt_QString2TPtrC(aData->text()));
+ text->InsertL(KPlainTextBegin, textPtr);
+ text->CopyToStoreL(clipboard->Store(), clipboard->StreamDictionary(),
+ KPlainTextBegin, textPtr.Length());
+ CleanupStack::PopAndDestroy(text);
+ }
+}
+
+void readSymbianStoreLX(QMimeData* aData, CClipboard* clipboard)
+{
+ // This function both leaves and throws exceptions. There must be no destructor
+ // dependencies between cleanup styles, and no cleanup stack dependencies on stacked objects.
+ CPlainText* text = CPlainText::NewL();
+ CleanupStack::PushL(text);
+ TInt dataLength = text->PasteFromStoreL(clipboard->Store(), clipboard->StreamDictionary(),
+ KPlainTextBegin);
+ if (dataLength == 0) {
+ User::Leave(KErrNotFound);
+ }
+ HBufC* hBuf = HBufC::NewL(dataLength);
+ TPtr buf = hBuf->Des();
+ text->Extract(buf, KPlainTextBegin, dataLength);
+
+ QString string = qt_TDesC2QString(buf);
+ CleanupStack::PopAndDestroy(text);
+
+ aData->setText(string);
+}
+
+void readFromStreamLX(QMimeData* aData,RReadStream& aStream)
+{
+ // This function both leaves and throws exceptions. There must be no destructor
+ // dependencies between cleanup styles, and no cleanup stack dependencies on stacked objects.
+ TCardinality mimeTypeCount;
+ aStream >> mimeTypeCount;
+ for (int i = 0; i< mimeTypeCount;i++)
+ {
+ // mime type
+ TCardinality mimeTypeSize;
+ aStream >> mimeTypeSize;
+ HBufC* mimeTypeBuf = HBufC::NewLC(aStream,mimeTypeSize);
+ QString mimeType = QString(reinterpret_cast<const QChar *>(mimeTypeBuf->Des().Ptr()),
+ mimeTypeBuf->Length());
+ CleanupStack::PopAndDestroy(mimeTypeBuf);
+ // mime data
+ TCardinality dataSize;
+ aStream >> dataSize;
+ QByteArray ba;
+ ba.reserve(dataSize);
+ aStream.ReadL(reinterpret_cast<uchar*>(ba.data_ptr()->data),dataSize);
+ ba.data_ptr()->size = dataSize;
+ aData->setData(mimeType,ba);
+ }
+}
+
+
+/*****************************************************************************
+ QClipboard member functions
+ *****************************************************************************/
+
+void QClipboard::clear(Mode mode)
+{
+ setText(QString(), mode);
+}
+const QMimeData* QClipboard::mimeData(Mode mode) const
+{
+ if (mode != Clipboard) return 0;
+ QClipboardData *d = clipboardData();
+ bool dataExists(false);
+ if (d)
+ {
+ TRAPD(err,{
+ RFs fs = qt_s60GetRFs();
+ CClipboard* cb = CClipboard::NewForReadingLC(fs);
+ Q_ASSERT(cb);
+ //stream for qt
+ RStoreReadStream stream;
+ TStreamId stid = (cb->StreamDictionary()).At(KQtCbDataStream);
+ if (stid != 0) {
+ stream.OpenLC(cb->Store(),stid);
+ QT_TRYCATCH_LEAVING(readFromStreamLX(d->source(),stream));
+ CleanupStack::PopAndDestroy(&stream);
+ dataExists = true;
+ }
+ else {
+ //symbian clipboard
+ RStoreReadStream symbianStream;
+ TStreamId symbianStId = (cb->StreamDictionary()).At(KClipboardUidTypePlainText);
+ if (symbianStId != 0) {
+ symbianStream.OpenLC(cb->Store(), symbianStId);
+ QT_TRYCATCH_LEAVING(readSymbianStoreLX(d->source(), cb));
+ CleanupStack::PopAndDestroy(&symbianStream);
+ dataExists = true;
+ }
+ }
+ CleanupStack::PopAndDestroy(cb);
+ });
+ if (err != KErrNone){
+ qDebug()<< "clipboard is empty/err: " << err;
+ }
+
+ if (dataExists) {
+ return d->source();
+ }
+ }
+ return 0;
+}
+
+
+void QClipboard::setMimeData(QMimeData* src, Mode mode)
+{
+ if (mode != Clipboard) return;
+ QClipboardData *d = clipboardData();
+ if (d)
+ {
+ TRAPD(err,{
+ RFs fs = qt_s60GetRFs();
+ CClipboard* cb = CClipboard::NewForWritingLC(fs);
+ //stream for qt
+ RStoreWriteStream stream;
+ TStreamId stid = stream.CreateLC(cb->Store());
+ QT_TRYCATCH_LEAVING(writeToStreamLX(src,stream));
+ d->setSource(src);
+ stream.CommitL();
+ (cb->StreamDictionary()).AssignL(KQtCbDataStream,stid);
+ cb->CommitL();
+
+ //stream for symbian
+ RStoreWriteStream symbianStream;
+ TStreamId symbianStId = symbianStream.CreateLC(cb->Store());
+ QT_TRYCATCH_LEAVING(writeToSymbianStoreLX(src, cb));
+ (cb->StreamDictionary()).AssignL(KClipboardUidTypePlainText, symbianStId);
+ cb->CommitL();
+ CleanupStack::PopAndDestroy(3,cb);
+ });
+ if (err != KErrNone){
+ qDebug()<< "clipboard write err :" << err;
+ }
+ }
+ emitChanged(QClipboard::Clipboard);
+}
+
+bool QClipboard::supportsMode(Mode mode) const
+{
+ return (mode == Clipboard);
+}
+
+bool QClipboard::ownsMode(Mode mode) const
+{
+ if (mode == Clipboard)
+ qWarning("QClipboard::ownsClipboard: UNIMPLEMENTED!");
+ return false;
+}
+
+bool QClipboard::event(QEvent * /* e */)
+{
+ return true;
+}
+
+void QClipboard::connectNotify( const char * )
+{
+}
+
+void QClipboard::ownerDestroyed()
+{
+}
+QT_END_NAMESPACE
+#endif // QT_NO_CLIPBOARD
diff --git a/src/widgets/platforms/s60/qcoefepinputcontext_p.h b/src/widgets/platforms/s60/qcoefepinputcontext_p.h
new file mode 100644
index 0000000000..148f092ac5
--- /dev/null
+++ b/src/widgets/platforms/s60/qcoefepinputcontext_p.h
@@ -0,0 +1,176 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOEFEPINPUTCONTEXT_P_H
+#define QCOEFEPINPUTCONTEXT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QT_NO_IM
+
+#include "qinputcontext.h"
+#include <qhash.h>
+#include <qtimer.h>
+#include <private/qcore_symbian_p.h>
+#include <private/qt_s60_p.h>
+
+#include <fepbase.h>
+#include <aknedsts.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_AUTOTEST_EXPORT QCoeFepInputContext : public QInputContext,
+ public MCoeFepAwareTextEditor,
+ public MCoeFepAwareTextEditor_Extension1,
+ public MObjectProvider
+{
+ Q_OBJECT
+
+public:
+ QCoeFepInputContext(QObject *parent = 0);
+ ~QCoeFepInputContext();
+
+ QString identifierName() { return QLatin1String("coefep"); }
+ QString language();
+
+ void reset();
+ void update();
+
+ bool filterEvent(const QEvent *event);
+ bool symbianFilterEvent(QWidget *keyWidget, const QSymbianEvent *event);
+ void mouseHandler( int x, QMouseEvent *event);
+ bool isComposing() const { return !m_preeditString.isEmpty(); }
+
+ void setFocusWidget(QWidget * w);
+ void widgetDestroyed(QWidget *w);
+
+ TCoeInputCapabilities inputCapabilities();
+
+ void resetSplitViewWidget(bool keepInputWidget = false);
+ void ensureFocusWidgetVisible(QWidget *widget);
+
+protected:
+ void timerEvent(QTimerEvent *timerEvent);
+
+private:
+ void commitCurrentString(bool cancelFepTransaction);
+ void updateHints(bool mustUpdateInputCapabilities);
+ void applyHints(Qt::InputMethodHints hints);
+ void applyFormat(QList<QInputMethodEvent::Attribute> *attributes);
+ void queueInputCapabilitiesChanged();
+ bool needsInputPanel();
+ void commitTemporaryPreeditString();
+ bool isWidgetVisible(QWidget *widget, int offset = 0);
+
+private Q_SLOTS:
+ void ensureInputCapabilitiesChanged();
+ void translateInputWidget();
+
+ // From MCoeFepAwareTextEditor
+public:
+ void StartFepInlineEditL(const TDesC& aInitialInlineText, TInt aPositionOfInsertionPointInInlineText,
+ TBool aCursorVisibility, const MFormCustomDraw* aCustomDraw,
+ MFepInlineTextFormatRetriever& aInlineTextFormatRetriever,
+ MFepPointerEventHandlerDuringInlineEdit& aPointerEventHandlerDuringInlineEdit);
+ void UpdateFepInlineTextL(const TDesC& aNewInlineText, TInt aPositionOfInsertionPointInInlineText);
+ void SetInlineEditingCursorVisibilityL(TBool aCursorVisibility);
+ void CancelFepInlineEdit();
+ TInt DocumentLengthForFep() const;
+ TInt DocumentMaximumLengthForFep() const;
+ void SetCursorSelectionForFepL(const TCursorSelection& aCursorSelection);
+ void GetCursorSelectionForFep(TCursorSelection& aCursorSelection) const;
+ void GetEditorContentForFep(TDes& aEditorContent, TInt aDocumentPosition, TInt aLengthToRetrieve) const;
+ void GetFormatForFep(TCharFormat& aFormat, TInt aDocumentPosition) const;
+ void GetScreenCoordinatesForFepL(TPoint& aLeftSideOfBaseLine, TInt& aHeight, TInt& aAscent,
+ TInt aDocumentPosition) const;
+private:
+ void DoCommitFepInlineEditL();
+ MCoeFepAwareTextEditor_Extension1* Extension1(TBool& aSetToTrue);
+ void ReportAknEdStateEvent(MAknEdStateObserver::EAknEdwinStateEvent aEventType);
+
+ // From MCoeFepAwareTextEditor_Extension1
+public:
+ void SetStateTransferingOwnershipL(MCoeFepAwareTextEditor_Extension1::CState* aState, TUid aTypeSafetyUid);
+ MCoeFepAwareTextEditor_Extension1::CState* State(TUid aTypeSafetyUid);
+
+ // From MObjectProvider
+public:
+ TTypeUid::Ptr MopSupplyObject(TTypeUid id);
+ MObjectProvider *MopNext();
+
+private:
+ QSymbianControl *m_parent;
+ CAknEdwinState *m_fepState;
+ QString m_preeditString;
+ Qt::InputMethodHints m_lastImHints;
+ TUint m_textCapabilities;
+ bool m_inDestruction;
+ bool m_pendingInputCapabilitiesChanged;
+ int m_cursorVisibility;
+ int m_inlinePosition;
+ MFepInlineTextFormatRetriever *m_formatRetriever;
+ MFepPointerEventHandlerDuringInlineEdit *m_pointerHandler;
+ QBasicTimer m_tempPreeditStringTimeout;
+ bool m_hasTempPreeditString;
+
+ int m_splitViewResizeBy;
+ Qt::WindowStates m_splitViewPreviousWindowStates;
+ QRectF m_transformation;
+
+ friend class tst_QInputContext;
+};
+
+Q_WIDGETS_EXPORT void qt_s60_setPartialScreenInputMode(bool enable);
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_IM
+
+#endif // QCOEFEPINPUTCONTEXT_P_H
diff --git a/src/widgets/platforms/s60/qcoefepinputcontext_s60.cpp b/src/widgets/platforms/s60/qcoefepinputcontext_s60.cpp
new file mode 100644
index 0000000000..8c215360d8
--- /dev/null
+++ b/src/widgets/platforms/s60/qcoefepinputcontext_s60.cpp
@@ -0,0 +1,1200 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT_NO_IM
+
+#include "qcoefepinputcontext_p.h"
+#include <qapplication.h>
+#include <qtextformat.h>
+#include <qgraphicsview.h>
+#include <qgraphicsscene.h>
+#include <qgraphicswidget.h>
+#include <qsymbianevent.h>
+#include <qlayout.h>
+#include <qdesktopwidget.h>
+#include <private/qcore_symbian_p.h>
+
+#include <fepitfr.h>
+#include <hal.h>
+
+#include <limits.h>
+// You only find these enumerations on SDK 5 onwards, so we need to provide our own
+// to remain compatible with older releases. They won't be called by pre-5.0 SDKs.
+
+// MAknEdStateObserver::EAknCursorPositionChanged
+#define QT_EAknCursorPositionChanged MAknEdStateObserver::EAknEdwinStateEvent(6)
+// MAknEdStateObserver::EAknActivatePenInputRequest
+#define QT_EAknActivatePenInputRequest MAknEdStateObserver::EAknEdwinStateEvent(7)
+
+// EAknEditorFlagSelectionVisible is only valid from 3.2 onwards.
+// Sym^3 AVKON FEP manager expects that this flag is used for FEP-aware editors
+// that support text selection.
+#define QT_EAknEditorFlagSelectionVisible 0x100000
+
+// EAknEditorFlagEnablePartialScreen is only valid from Sym^3 onwards.
+#define QT_EAknEditorFlagEnablePartialScreen 0x200000
+
+QT_BEGIN_NAMESPACE
+
+Q_WIDGETS_EXPORT void qt_s60_setPartialScreenInputMode(bool enable)
+{
+ S60->partial_keyboard = enable;
+
+ QInputContext *ic = 0;
+ if (QApplication::focusWidget()) {
+ ic = QApplication::focusWidget()->inputContext();
+ } else if (qApp && qApp->inputContext()) {
+ ic = qApp->inputContext();
+ }
+ if (ic)
+ ic->update();
+}
+
+QCoeFepInputContext::QCoeFepInputContext(QObject *parent)
+ : QInputContext(parent),
+ m_fepState(q_check_ptr(new CAknEdwinState)), // CBase derived object needs check on new
+ m_lastImHints(Qt::ImhNone),
+ m_textCapabilities(TCoeInputCapabilities::EAllText),
+ m_inDestruction(false),
+ m_pendingInputCapabilitiesChanged(false),
+ m_cursorVisibility(1),
+ m_inlinePosition(0),
+ m_formatRetriever(0),
+ m_pointerHandler(0),
+ m_hasTempPreeditString(false),
+ m_splitViewResizeBy(0),
+ m_splitViewPreviousWindowStates(Qt::WindowNoState)
+{
+ m_fepState->SetObjectProvider(this);
+ int defaultFlags = EAknEditorFlagDefault;
+ if (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0) {
+ if (S60->partial_keyboard) {
+ defaultFlags |= QT_EAknEditorFlagEnablePartialScreen;
+ }
+ defaultFlags |= QT_EAknEditorFlagSelectionVisible;
+ }
+ m_fepState->SetFlags(defaultFlags);
+ m_fepState->SetDefaultInputMode( EAknEditorTextInputMode );
+ m_fepState->SetPermittedInputModes( EAknEditorAllInputModes );
+ m_fepState->SetDefaultCase( EAknEditorTextCase );
+ m_fepState->SetPermittedCases( EAknEditorAllCaseModes );
+ m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_SPECIAL_CHARACTER_TABLE_DIALOG);
+ m_fepState->SetNumericKeymap(EAknEditorAlphanumericNumberModeKeymap);
+}
+
+QCoeFepInputContext::~QCoeFepInputContext()
+{
+ m_inDestruction = true;
+
+ // This is to make sure that the FEP manager "forgets" about us,
+ // otherwise we may get callbacks even after we're destroyed.
+ // The call below is essentially equivalent to InputCapabilitiesChanged(),
+ // but is synchronous, rather than asynchronous.
+ CCoeEnv::Static()->SyncNotifyFocusObserversOfChangeInFocus();
+
+ if (m_fepState)
+ delete m_fepState;
+}
+
+void QCoeFepInputContext::reset()
+{
+ commitCurrentString(true);
+}
+
+void QCoeFepInputContext::ReportAknEdStateEvent(MAknEdStateObserver::EAknEdwinStateEvent aEventType)
+{
+ QT_TRAP_THROWING(m_fepState->ReportAknEdStateEventL(aEventType));
+}
+
+void QCoeFepInputContext::update()
+{
+ updateHints(false);
+
+ // For pre-5.0 SDKs, we don't do text updates on S60 side.
+ if (QSysInfo::s60Version() < QSysInfo::SV_S60_5_0) {
+ return;
+ }
+
+ // Don't be fooled (as I was) by the name of this enumeration.
+ // What it really does is tell the virtual keyboard UI that the text has been
+ // updated and it should be reflected in the internal display of the VK.
+ ReportAknEdStateEvent(QT_EAknCursorPositionChanged);
+}
+
+void QCoeFepInputContext::setFocusWidget(QWidget *w)
+{
+ commitCurrentString(true);
+
+ QInputContext::setFocusWidget(w);
+
+ updateHints(true);
+}
+
+void QCoeFepInputContext::widgetDestroyed(QWidget *w)
+{
+ // Make sure that the input capabilities of whatever new widget got focused are queried.
+ CCoeControl *ctrl = w->effectiveWinId();
+ if (ctrl->IsFocused()) {
+ queueInputCapabilitiesChanged();
+ }
+}
+
+QString QCoeFepInputContext::language()
+{
+ TLanguage lang = m_fepState->LocalLanguage();
+ const QByteArray localeName = qt_symbianLocaleName(lang);
+ if (!localeName.isEmpty()) {
+ return QString::fromLatin1(localeName);
+ } else {
+ return QString::fromLatin1("C");
+ }
+}
+
+bool QCoeFepInputContext::needsInputPanel()
+{
+ switch (QSysInfo::s60Version()) {
+ case QSysInfo::SV_S60_3_1:
+ case QSysInfo::SV_S60_3_2:
+ // There are no touch phones for pre-5.0 SDKs.
+ return false;
+#ifdef Q_CC_NOKIAX86
+ default:
+ // For emulator we assume that we need an input panel, since we can't
+ // separate between phone types.
+ return true;
+#else
+ case QSysInfo::SV_S60_5_0: {
+ // For SDK == 5.0, we need phone specific detection, since the HAL API
+ // is no good on most phones. However, all phones at the time of writing use the
+ // input panel, except N97 in landscape mode, but in this mode it refuses to bring
+ // up the panel anyway, so we don't have to care.
+ return true;
+ }
+ default:
+ // For unknown/newer types, we try to use the HAL API.
+ int keyboardEnabled;
+ int keyboardType;
+ int err[2];
+ err[0] = HAL::Get(HAL::EKeyboard, keyboardType);
+ err[1] = HAL::Get(HAL::EKeyboardState, keyboardEnabled);
+ if (err[0] == KErrNone && err[1] == KErrNone
+ && keyboardType != 0 && keyboardEnabled)
+ // Means that we have some sort of keyboard.
+ return false;
+
+ // Fall back to using the input panel.
+ return true;
+#endif // !Q_CC_NOKIAX86
+ }
+}
+
+bool QCoeFepInputContext::filterEvent(const QEvent *event)
+{
+ // The CloseSoftwareInputPanel event is not handled here, because the VK will automatically
+ // close when it discovers that the underlying widget does not have input capabilities.
+
+ if (!focusWidget())
+ return false;
+
+ switch (event->type()) {
+ case QEvent::MouseButtonPress:
+ // Alphanumeric keypad doesn't like it when we click and text is still getting displayed
+ // It ignores the mouse event, so we need to commit and send a selection event (which will get triggered
+ // after the commit)
+ if (!m_preeditString.isEmpty()) {
+ commitCurrentString(true);
+
+ int pos = focusWidget()->inputMethodQuery(Qt::ImCursorPosition).toInt();
+
+ QList<QInputMethodEvent::Attribute> selectAttributes;
+ selectAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, pos, 0, QVariant());
+ QInputMethodEvent selectEvent(QLatin1String(""), selectAttributes);
+ sendEvent(selectEvent);
+ }
+ break;
+ case QEvent::KeyPress:
+ commitTemporaryPreeditString();
+ // fall through intended
+ case QEvent::KeyRelease:
+ const QKeyEvent *keyEvent = static_cast<const QKeyEvent *>(event);
+ //If proxy exists, always use hints from proxy.
+ QWidget *proxy = focusWidget()->focusProxy();
+ Qt::InputMethodHints currentHints = proxy ? proxy->inputMethodHints() : focusWidget()->inputMethodHints();
+
+ switch (keyEvent->key()) {
+ case Qt::Key_F20:
+ Q_ASSERT(m_lastImHints == currentHints);
+ if (m_lastImHints & Qt::ImhHiddenText) {
+ // Special case in Symbian. On editors with secret text, F20 is for some reason
+ // considered to be a backspace.
+ QKeyEvent modifiedEvent(keyEvent->type(), Qt::Key_Backspace, keyEvent->modifiers(),
+ keyEvent->text(), keyEvent->isAutoRepeat(), keyEvent->count());
+ QApplication::sendEvent(focusWidget(), &modifiedEvent);
+ return true;
+ }
+ break;
+ case Qt::Key_Select:
+ if (!m_preeditString.isEmpty()) {
+ commitCurrentString(true);
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ QString widgetText = focusWidget()->inputMethodQuery(Qt::ImSurroundingText).toString();
+ bool validLength;
+ int maxLength = focusWidget()->inputMethodQuery(Qt::ImMaximumTextLength).toInt(&validLength);
+ if (!keyEvent->text().isEmpty() && validLength
+ && widgetText.size() + m_preeditString.size() >= maxLength) {
+ // Don't send key events with string content if the widget is "full".
+ return true;
+ }
+
+ if (keyEvent->type() == QEvent::KeyPress
+ && currentHints & Qt::ImhHiddenText
+ && !keyEvent->text().isEmpty()) {
+ // Send some temporary preedit text in order to make text visible for a moment.
+ m_preeditString = keyEvent->text();
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent imEvent(m_preeditString, attributes);
+ sendEvent(imEvent);
+ m_tempPreeditStringTimeout.start(1000, this);
+ m_hasTempPreeditString = true;
+ update();
+ return true;
+ }
+ break;
+ }
+
+ if (!needsInputPanel())
+ return false;
+
+ if (event->type() == QEvent::RequestSoftwareInputPanel) {
+ // Notify S60 that we want the virtual keyboard to show up.
+ QSymbianControl *sControl;
+ sControl = focusWidget()->effectiveWinId()->MopGetObject(sControl);
+ Q_ASSERT(sControl);
+
+ // The FEP UI temporarily steals focus when it shows up the first time, causing
+ // all sorts of weird effects on the focused widgets. Since it will immediately give
+ // back focus to us, we temporarily disable focus handling until the job's done.
+ if (sControl) {
+ sControl->setIgnoreFocusChanged(true);
+ }
+
+ ensureInputCapabilitiesChanged();
+ m_fepState->ReportAknEdStateEventL(MAknEdStateObserver::QT_EAknActivatePenInputRequest);
+
+ if (sControl) {
+ sControl->setIgnoreFocusChanged(false);
+ }
+ return true;
+ }
+
+ return false;
+}
+
+bool QCoeFepInputContext::symbianFilterEvent(QWidget *keyWidget, const QSymbianEvent *event)
+{
+ Q_UNUSED(keyWidget);
+ if (event->type() == QSymbianEvent::CommandEvent)
+ // A command basically means the same as a button being pushed. With Qt buttons
+ // that would normally result in a reset of the input method due to the focus change.
+ // This should also happen for commands.
+ reset();
+
+ if (event->type() == QSymbianEvent::WindowServerEvent
+ && event->windowServerEvent()
+ && event->windowServerEvent()->Type() == EEventWindowVisibilityChanged
+ && S60->splitViewLastWidget) {
+
+ QGraphicsView *gv = qobject_cast<QGraphicsView*>(S60->splitViewLastWidget);
+ const bool alwaysResize = (gv && gv->verticalScrollBarPolicy() != Qt::ScrollBarAlwaysOff);
+
+ if (alwaysResize) {
+ TUint visibleFlags = event->windowServerEvent()->VisibilityChanged()->iFlags;
+ if (visibleFlags & TWsVisibilityChangedEvent::EPartiallyVisible)
+ ensureFocusWidgetVisible(S60->splitViewLastWidget);
+ if (visibleFlags & TWsVisibilityChangedEvent::ENotVisible)
+ resetSplitViewWidget(true);
+ }
+ }
+
+ return false;
+}
+
+void QCoeFepInputContext::timerEvent(QTimerEvent *timerEvent)
+{
+ if (timerEvent->timerId() == m_tempPreeditStringTimeout.timerId())
+ commitTemporaryPreeditString();
+}
+
+void QCoeFepInputContext::commitTemporaryPreeditString()
+{
+ if (m_tempPreeditStringTimeout.isActive())
+ m_tempPreeditStringTimeout.stop();
+
+ if (!m_hasTempPreeditString)
+ return;
+
+ commitCurrentString(false);
+}
+
+void QCoeFepInputContext::mouseHandler( int x, QMouseEvent *event)
+{
+ Q_ASSERT(focusWidget());
+
+ if (event->type() == QEvent::MouseButtonPress && event->button() == Qt::LeftButton) {
+ commitCurrentString(true);
+ int pos = focusWidget()->inputMethodQuery(Qt::ImCursorPosition).toInt();
+
+ QList<QInputMethodEvent::Attribute> attributes;
+ attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, pos + x, 0, QVariant());
+ QInputMethodEvent event(QLatin1String(""), attributes);
+ sendEvent(event);
+ }
+}
+
+TCoeInputCapabilities QCoeFepInputContext::inputCapabilities()
+{
+ if (m_inDestruction || !focusWidget()) {
+ return TCoeInputCapabilities(TCoeInputCapabilities::ENone, 0, 0);
+ }
+
+ return TCoeInputCapabilities(m_textCapabilities, this, 0);
+}
+
+void QCoeFepInputContext::resetSplitViewWidget(bool keepInputWidget)
+{
+ QGraphicsView *gv = qobject_cast<QGraphicsView*>(S60->splitViewLastWidget);
+
+ if (!gv) {
+ return;
+ }
+
+ QSymbianControl *symControl = static_cast<QSymbianControl*>(gv->effectiveWinId());
+ symControl->CancelLongTapTimer();
+
+ const bool alwaysResize = (gv->verticalScrollBarPolicy() != Qt::ScrollBarAlwaysOff);
+ QWidget *windowToMove = gv->window();
+
+ bool userResize = gv->testAttribute(Qt::WA_Resized);
+
+ windowToMove->setUpdatesEnabled(false);
+
+ if (!alwaysResize) {
+ if (gv->scene()) {
+ if (gv->scene()->focusItem()) {
+ // Check if the widget contains cursorPositionChanged signal and disconnect from it.
+ QByteArray signal = QMetaObject::normalizedSignature(SIGNAL(cursorPositionChanged()));
+ int index = gv->scene()->focusItem()->toGraphicsObject()->metaObject()->indexOfSignal(signal.right(signal.length() - 1));
+ if (index != -1)
+ disconnect(gv->scene()->focusItem()->toGraphicsObject(), SIGNAL(cursorPositionChanged()), this, SLOT(translateInputWidget()));
+ }
+
+ QGraphicsItem *rootItem = 0;
+ foreach (QGraphicsItem *item, gv->scene()->items()) {
+ if (!item->parentItem()) {
+ rootItem = item;
+ break;
+ }
+ }
+ if (rootItem)
+ rootItem->resetTransform();
+ }
+ } else {
+ if (m_splitViewResizeBy)
+ gv->resize(gv->rect().width(), m_splitViewResizeBy);
+ }
+ // Resizing might have led to widget losing its original windowstate.
+ // Restore previous window state.
+
+ if (m_splitViewPreviousWindowStates != windowToMove->windowState())
+ windowToMove->setWindowState(m_splitViewPreviousWindowStates);
+
+ windowToMove->setUpdatesEnabled(true);
+
+ gv->setAttribute(Qt::WA_Resized, userResize); //not a user resize
+
+ m_splitViewResizeBy = 0;
+ if (!keepInputWidget) {
+ m_splitViewPreviousWindowStates = Qt::WindowNoState;
+ S60->splitViewLastWidget = 0;
+ }
+}
+
+// Checks if a given widget is visible in the splitview rect. The offset
+// parameter can be used to validate if moving widget upwards or downwards
+// by the offset would make a difference for the visibility.
+
+bool QCoeFepInputContext::isWidgetVisible(QWidget *widget, int offset)
+{
+ bool visible = false;
+ if (widget) {
+ QRect splitViewRect = qt_TRect2QRect(static_cast<CEikAppUi*>(S60->appUi())->ClientRect());
+ QWidget *window = QApplication::activeWindow();
+ QGraphicsView *gv = qobject_cast<QGraphicsView*>(widget);
+ if (gv && window) {
+ if (QGraphicsScene *scene = gv->scene()) {
+ if (QGraphicsItem *focusItem = scene->focusItem()) {
+ QPoint cursorPos = window->mapToGlobal(focusItem->cursor().pos());
+ cursorPos.setY(cursorPos.y() + offset);
+ if (splitViewRect.contains(cursorPos)) {
+ visible = true;
+ }
+ }
+ }
+ }
+ }
+ return visible;
+}
+
+// Ensure that the input widget is visible in the splitview rect.
+
+void QCoeFepInputContext::ensureFocusWidgetVisible(QWidget *widget)
+{
+ // Native side opening and closing its virtual keyboard when it changes the keyboard layout,
+ // has an adverse impact on long tap timer. Cancel the timer when splitview opens to avoid this.
+ QSymbianControl *symControl = static_cast<QSymbianControl*>(widget->effectiveWinId());
+ symControl->CancelLongTapTimer();
+
+ // Graphicsviews that have vertical scrollbars should always be resized to the splitview area.
+ // Graphicsviews without scrollbars should be translated.
+
+ QGraphicsView *gv = qobject_cast<QGraphicsView*>(widget);
+ if (!gv)
+ return;
+
+ const bool alwaysResize = (gv && gv->verticalScrollBarPolicy() != Qt::ScrollBarAlwaysOff);
+ const bool moveWithinVisibleArea = (S60->splitViewLastWidget != 0);
+
+ QWidget *windowToMove = gv ? gv : symControl->widget();
+ if (!windowToMove->isWindow())
+ windowToMove = windowToMove->window();
+ if (!windowToMove) {
+ return;
+ }
+
+ // When opening the keyboard (not moving within the splitview area), save the original
+ // window state. In some cases, ensuring input widget visibility might lead to window
+ // states getting changed.
+
+ if (!moveWithinVisibleArea) {
+ // Check if the widget contains cursorPositionChanged signal and connect to it.
+ QByteArray signal = QMetaObject::normalizedSignature(SIGNAL(cursorPositionChanged()));
+ if (gv->scene() && gv->scene()->focusItem()) {
+ int index = gv->scene()->focusItem()->toGraphicsObject()->metaObject()->indexOfSignal(signal.right(signal.length() - 1));
+ if (index != -1)
+ connect(gv->scene()->focusItem()->toGraphicsObject(), SIGNAL(cursorPositionChanged()), this, SLOT(translateInputWidget()));
+ }
+ S60->splitViewLastWidget = widget;
+ m_splitViewPreviousWindowStates = windowToMove->windowState();
+ }
+
+ int windowTop = widget->window()->pos().y();
+
+ const bool userResize = widget->testAttribute(Qt::WA_Resized);
+
+ QRect splitViewRect = qt_TRect2QRect(static_cast<CEikAppUi*>(S60->appUi())->ClientRect());
+
+
+ // When resizing a window widget, it will lose its maximized window state.
+ // Native applications hide statuspane in splitview state, so lets move to
+ // fullscreen mode. This makes available area slightly bigger, which helps usability
+ // and greatly reduces event passing in orientation switch cases,
+ // as the statuspane size is not changing.
+
+ if (alwaysResize)
+ windowToMove->setUpdatesEnabled(false);
+
+ if (!(windowToMove->windowState() & Qt::WindowFullScreen)) {
+ windowToMove->setWindowState(
+ (windowToMove->windowState() & ~(Qt::WindowMinimized | Qt::WindowFullScreen)) | Qt::WindowFullScreen);
+ }
+
+ if (alwaysResize) {
+ if (!moveWithinVisibleArea) {
+ m_splitViewResizeBy = widget->height();
+ windowTop = widget->geometry().top();
+ widget->resize(widget->width(), splitViewRect.height() - windowTop);
+ }
+
+ if (gv->scene()) {
+ const QRectF microFocusRect = gv->scene()->inputMethodQuery(Qt::ImMicroFocus).toRectF();
+ gv->ensureVisible(microFocusRect);
+ }
+ } else {
+ translateInputWidget();
+ }
+
+ if (alwaysResize)
+ windowToMove->setUpdatesEnabled(true);
+
+ widget->setAttribute(Qt::WA_Resized, userResize); //not a user resize
+}
+
+static QTextCharFormat qt_TCharFormat2QTextCharFormat(const TCharFormat &cFormat, bool validStyleColor)
+{
+ QTextCharFormat qFormat;
+
+ if (validStyleColor) {
+ QBrush foreground(QColor(cFormat.iFontPresentation.iTextColor.Internal()));
+ qFormat.setForeground(foreground);
+ }
+
+ qFormat.setFontStrikeOut(cFormat.iFontPresentation.iStrikethrough == EStrikethroughOn);
+ qFormat.setFontUnderline(cFormat.iFontPresentation.iUnderline == EUnderlineOn);
+
+ return qFormat;
+}
+
+void QCoeFepInputContext::updateHints(bool mustUpdateInputCapabilities)
+{
+ QWidget *w = focusWidget();
+ if (w) {
+ QWidget *proxy = w->focusProxy();
+ Qt::InputMethodHints hints = proxy ? proxy->inputMethodHints() : w->inputMethodHints();
+
+ // Since splitview support works like an input method hint, yet it is private flag,
+ // we need to update its state separately.
+ if (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0) {
+ TInt currentFlags = m_fepState->Flags();
+ if (S60->partial_keyboard)
+ currentFlags |= QT_EAknEditorFlagEnablePartialScreen;
+ else
+ currentFlags &= ~QT_EAknEditorFlagEnablePartialScreen;
+ if (currentFlags != m_fepState->Flags())
+ m_fepState->SetFlags(currentFlags);
+ }
+
+ if (hints != m_lastImHints) {
+ m_lastImHints = hints;
+ applyHints(hints);
+ } else if (!mustUpdateInputCapabilities) {
+ // Optimization. Return immediately if there was no change.
+ return;
+ }
+ }
+ queueInputCapabilitiesChanged();
+}
+
+void QCoeFepInputContext::applyHints(Qt::InputMethodHints hints)
+{
+ using namespace Qt;
+
+ commitTemporaryPreeditString();
+
+ const bool anynumbermodes = hints & (ImhDigitsOnly | ImhFormattedNumbersOnly | ImhDialableCharactersOnly);
+ const bool anytextmodes = hints & (ImhUppercaseOnly | ImhLowercaseOnly | ImhEmailCharactersOnly | ImhUrlCharactersOnly);
+ const bool numbersOnly = anynumbermodes && !anytextmodes;
+ const bool noOnlys = !(hints & ImhExclusiveInputMask);
+ // if alphanumeric input, or if multiple incompatible number modes are selected;
+ // then make all symbols available in numeric mode too.
+ const bool needsCharMap= !numbersOnly || ((hints & ImhFormattedNumbersOnly) && (hints & ImhDialableCharactersOnly));
+ TInt flags;
+ Qt::InputMethodHints oldHints = hints;
+
+ // Some sanity checking. Make sure that only one preference is set.
+ InputMethodHints prefs = ImhPreferNumbers | ImhPreferUppercase | ImhPreferLowercase;
+ prefs &= hints;
+ if (prefs != ImhPreferNumbers && prefs != ImhPreferUppercase && prefs != ImhPreferLowercase) {
+ hints &= ~prefs;
+ }
+ if (!noOnlys) {
+ // Make sure that the preference is within the permitted set.
+ if (hints & ImhPreferNumbers && !anynumbermodes) {
+ hints &= ~ImhPreferNumbers;
+ } else if (hints & ImhPreferUppercase && !(hints & ImhUppercaseOnly)) {
+ hints &= ~ImhPreferUppercase;
+ } else if (hints & ImhPreferLowercase && !(hints & ImhLowercaseOnly)) {
+ hints &= ~ImhPreferLowercase;
+ }
+ // If there is no preference, set it to something within the permitted set.
+ if (!(hints & ImhPreferNumbers || hints & ImhPreferUppercase || hints & ImhPreferLowercase)) {
+ if (hints & ImhLowercaseOnly) {
+ hints |= ImhPreferLowercase;
+ } else if (hints & ImhUppercaseOnly) {
+ hints |= ImhPreferUppercase;
+ } else if (numbersOnly) {
+ hints |= ImhPreferNumbers;
+ }
+ }
+ }
+
+ if (hints & ImhPreferNumbers) {
+ m_fepState->SetDefaultInputMode(EAknEditorNumericInputMode);
+ m_fepState->SetCurrentInputMode(EAknEditorNumericInputMode);
+ } else {
+ m_fepState->SetDefaultInputMode(EAknEditorTextInputMode);
+ m_fepState->SetCurrentInputMode(EAknEditorTextInputMode);
+ }
+ flags = 0;
+ if (noOnlys || (anynumbermodes && anytextmodes)) {
+ flags = EAknEditorAllInputModes;
+ }
+ else if (anynumbermodes) {
+ flags |= EAknEditorNumericInputMode;
+ if (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0
+ && ((hints & ImhFormattedNumbersOnly) || (hints & ImhDialableCharactersOnly))) {
+ //workaround - the * key does not launch the symbols menu, making it impossible to use these modes unless text mode is enabled.
+ flags |= EAknEditorTextInputMode;
+ }
+ }
+ else if (anytextmodes) {
+ flags |= EAknEditorTextInputMode;
+ }
+ else {
+ flags = EAknEditorAllInputModes;
+ }
+ m_fepState->SetPermittedInputModes(flags);
+ ReportAknEdStateEvent(MAknEdStateObserver::EAknEdwinStateInputModeUpdate);
+
+ if (hints & ImhPreferLowercase) {
+ m_fepState->SetDefaultCase(EAknEditorLowerCase);
+ m_fepState->SetCurrentCase(EAknEditorLowerCase);
+ } else if (hints & ImhPreferUppercase) {
+ m_fepState->SetDefaultCase(EAknEditorUpperCase);
+ m_fepState->SetCurrentCase(EAknEditorUpperCase);
+ } else if (hints & ImhNoAutoUppercase) {
+ m_fepState->SetDefaultCase(EAknEditorLowerCase);
+ m_fepState->SetCurrentCase(EAknEditorLowerCase);
+ } else {
+ m_fepState->SetDefaultCase(EAknEditorTextCase);
+ m_fepState->SetCurrentCase(EAknEditorTextCase);
+ }
+ flags = 0;
+ if (hints & ImhUppercaseOnly) {
+ flags |= EAknEditorUpperCase;
+ }
+ if (hints & ImhLowercaseOnly) {
+ flags |= EAknEditorLowerCase;
+ }
+ if (flags == 0) {
+ flags = EAknEditorAllCaseModes;
+ if (hints & ImhNoAutoUppercase) {
+ flags &= ~EAknEditorTextCase;
+ }
+ }
+ m_fepState->SetPermittedCases(flags);
+ ReportAknEdStateEvent(MAknEdStateObserver::EAknEdwinStateCaseModeUpdate);
+
+ flags = 0;
+ if (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0) {
+ if (S60->partial_keyboard)
+ flags |= QT_EAknEditorFlagEnablePartialScreen;
+ flags |= QT_EAknEditorFlagSelectionVisible;
+ }
+ if (hints & ImhUppercaseOnly && !(hints & ImhLowercaseOnly)
+ || hints & ImhLowercaseOnly && !(hints & ImhUppercaseOnly)) {
+ flags |= EAknEditorFlagFixedCase;
+ }
+ // Using T9 and hidden text together may actually crash the FEP, so check for hidden text too.
+ if (hints & ImhNoPredictiveText || hints & ImhHiddenText) {
+ flags |= EAknEditorFlagNoT9;
+ }
+ if (needsCharMap)
+ flags |= EAknEditorFlagUseSCTNumericCharmap;
+ m_fepState->SetFlags(flags);
+ ReportAknEdStateEvent(MAknEdStateObserver::EAknEdwinStateFlagsUpdate);
+
+ if (hints & ImhDialableCharactersOnly) {
+ // This is first, because if (ImhDialableCharactersOnly | ImhFormattedNumbersOnly)
+ // is specified, this one is more natural (# key enters a #)
+ flags = EAknEditorStandardNumberModeKeymap;
+ } else if (hints & ImhFormattedNumbersOnly) {
+ // # key enters decimal point
+ flags = EAknEditorCalculatorNumberModeKeymap;
+ } else if (hints & ImhDigitsOnly) {
+ // This is last, because it is most restrictive (# key is inactive)
+ flags = EAknEditorPlainNumberModeKeymap;
+ } else {
+ flags = EAknEditorStandardNumberModeKeymap;
+ }
+ m_fepState->SetNumericKeymap(static_cast<TAknEditorNumericKeymap>(flags));
+
+ if (hints & ImhUrlCharactersOnly) {
+ // URL characters is everything except space, so a superset of the other restrictions
+ m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_URL_SPECIAL_CHARACTER_TABLE_DIALOG);
+ } else if (hints & ImhEmailCharactersOnly) {
+ m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_EMAIL_ADDR_SPECIAL_CHARACTER_TABLE_DIALOG);
+ } else if (needsCharMap) {
+ m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_SPECIAL_CHARACTER_TABLE_DIALOG);
+ } else if ((hints & ImhFormattedNumbersOnly) || (hints & ImhDialableCharactersOnly)) {
+ m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_SPECIAL_CHARACTER_TABLE_DIALOG);
+ } else {
+ m_fepState->SetSpecialCharacterTableResourceId(0);
+ }
+
+ if (hints & ImhHiddenText) {
+ m_textCapabilities = TCoeInputCapabilities::EAllText | TCoeInputCapabilities::ESecretText;
+ } else {
+ m_textCapabilities = TCoeInputCapabilities::EAllText;
+ }
+}
+
+void QCoeFepInputContext::applyFormat(QList<QInputMethodEvent::Attribute> *attributes)
+{
+ TCharFormat cFormat;
+ QColor styleTextColor;
+ if (QWidget *focused = focusWidget()) {
+ QGraphicsView *gv = qobject_cast<QGraphicsView*>(focused);
+ if (!gv) // could be either the QGV or its viewport that has focus
+ gv = qobject_cast<QGraphicsView*>(focused->parentWidget());
+ if (gv) {
+ if (QGraphicsScene *scene = gv->scene()) {
+ if (QGraphicsItem *focusItem = scene->focusItem()) {
+ if (focusItem->isWidget()) {
+ styleTextColor = static_cast<QGraphicsWidget*>(focusItem)->palette().text().color();
+ }
+ }
+ }
+ } else {
+ styleTextColor = focused->palette().text().color();
+ }
+ } else {
+ styleTextColor = QApplication::palette("QLineEdit").text().color();
+ }
+
+ if (styleTextColor.isValid()) {
+ const TLogicalRgb fontColor(TRgb(styleTextColor.red(), styleTextColor.green(), styleTextColor.blue(), styleTextColor.alpha()));
+ cFormat.iFontPresentation.iTextColor = fontColor;
+ }
+
+ TInt numChars = 0;
+ TInt charPos = 0;
+ int oldSize = attributes->size();
+ while (m_formatRetriever) {
+ m_formatRetriever->GetFormatOfFepInlineText(cFormat, numChars, charPos);
+ if (numChars <= 0) {
+ // This shouldn't happen according to S60 docs, but apparently does sometimes.
+ break;
+ }
+ attributes->append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
+ charPos,
+ numChars,
+ QVariant(qt_TCharFormat2QTextCharFormat(cFormat, styleTextColor.isValid()))));
+ charPos += numChars;
+ if (charPos >= m_preeditString.size()) {
+ break;
+ }
+ }
+
+ if (attributes->size() == oldSize) {
+ // S60 didn't provide any format, so let's give our own instead.
+ attributes->append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
+ 0,
+ m_preeditString.size(),
+ standardFormat(PreeditFormat)));
+ }
+}
+
+void QCoeFepInputContext::queueInputCapabilitiesChanged()
+{
+ if (m_pendingInputCapabilitiesChanged)
+ return;
+
+ // Call ensureInputCapabilitiesChanged asynchronously. This is done to improve performance
+ // by not updating input capabilities too often. The reason we don't call the Symbian
+ // asynchronous version of InputCapabilitiesChanged is because we need to ensure that it
+ // is synchronous in some specific cases. Those will call ensureInputCapabilitesChanged.
+ QMetaObject::invokeMethod(this, "ensureInputCapabilitiesChanged", Qt::QueuedConnection);
+ m_pendingInputCapabilitiesChanged = true;
+}
+
+void QCoeFepInputContext::ensureInputCapabilitiesChanged()
+{
+ if (!m_pendingInputCapabilitiesChanged)
+ return;
+
+ // The call below is essentially equivalent to InputCapabilitiesChanged(),
+ // but is synchronous, rather than asynchronous.
+ CCoeEnv::Static()->SyncNotifyFocusObserversOfChangeInFocus();
+ m_pendingInputCapabilitiesChanged = false;
+}
+
+void QCoeFepInputContext::translateInputWidget()
+{
+ QGraphicsView *gv = qobject_cast<QGraphicsView *>(S60->splitViewLastWidget);
+ QRect splitViewRect = qt_TRect2QRect(static_cast<CEikAppUi*>(S60->appUi())->ClientRect());
+
+ QRectF cursor = gv->scene()->inputMethodQuery(Qt::ImMicroFocus).toRectF();
+ QPolygon cursorP = gv->mapFromScene(cursor);
+ QRectF vkbRect = QRectF(splitViewRect.bottomLeft(), qApp->desktop()->rect().bottomRight());
+ if (cursor.isEmpty() || vkbRect.isEmpty())
+ return;
+
+ // Fetch root item (i.e. graphicsitem with no parent)
+ QGraphicsItem *rootItem = 0;
+ foreach (QGraphicsItem *item, gv->scene()->items()) {
+ if (!item->parentItem()) {
+ rootItem = item;
+ break;
+ }
+ }
+ if (!rootItem)
+ return;
+
+ m_transformation = (rootItem->transform().isTranslating()) ? QRectF(0,0, gv->width(), rootItem->transform().dy()) : QRectF();
+
+ // Do nothing if the cursor is visible in the splitview area.
+ if (splitViewRect.contains(cursorP.boundingRect()))
+ return;
+
+ // New Y position should be ideally at the center of the splitview area.
+ // If that would expose unpainted canvas, limit the tranformation to the visible scene bottom.
+
+ const qreal maxY = gv->sceneRect().bottom() - splitViewRect.bottom() + m_transformation.height();
+ qreal dy = -(qMin(maxY, (cursor.bottom() - vkbRect.top() / 2)));
+
+ // Do not allow transform above screen top.
+ if (m_transformation.height() + dy > 0)
+ return;
+
+ rootItem->setTransform(QTransform::fromTranslate(0, dy), true);
+}
+
+void QCoeFepInputContext::StartFepInlineEditL(const TDesC& aInitialInlineText,
+ TInt aPositionOfInsertionPointInInlineText, TBool aCursorVisibility, const MFormCustomDraw* /*aCustomDraw*/,
+ MFepInlineTextFormatRetriever& aInlineTextFormatRetriever,
+ MFepPointerEventHandlerDuringInlineEdit& aPointerEventHandlerDuringInlineEdit)
+{
+ QWidget *w = focusWidget();
+ if (!w)
+ return;
+
+ commitTemporaryPreeditString();
+
+ QList<QInputMethodEvent::Attribute> attributes;
+
+ m_cursorVisibility = aCursorVisibility ? 1 : 0;
+ m_inlinePosition = aPositionOfInsertionPointInInlineText;
+ m_preeditString = qt_TDesC2QString(aInitialInlineText);
+
+ m_formatRetriever = &aInlineTextFormatRetriever;
+ m_pointerHandler = &aPointerEventHandlerDuringInlineEdit;
+
+ // With T9 aInitialInlineText is typically empty when StartFepInlineEditL is called,
+ // but FEP requires that selected text is always removed at StartFepInlineEditL.
+ // Let's remove the selected text if aInitialInlineText is empty and there is selected text
+ if (m_preeditString.isEmpty()) {
+ int anchor = w->inputMethodQuery(Qt::ImAnchorPosition).toInt();
+ int cursorPos = w->inputMethodQuery(Qt::ImCursorPosition).toInt();
+ int replacementLength = qAbs(cursorPos-anchor);
+ if (replacementLength > 0) {
+ int replacementStart = cursorPos < anchor ? 0 : -replacementLength;
+ QList<QInputMethodEvent::Attribute> clearSelectionAttributes;
+ QInputMethodEvent clearSelectionEvent(QLatin1String(""), clearSelectionAttributes);
+ clearSelectionEvent.setCommitString(QLatin1String(""), replacementStart, replacementLength);
+ sendEvent(clearSelectionEvent);
+ }
+ }
+
+ applyFormat(&attributes);
+
+ attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor,
+ m_inlinePosition,
+ m_cursorVisibility,
+ QVariant()));
+ QInputMethodEvent event(m_preeditString, attributes);
+ sendEvent(event);
+}
+
+void QCoeFepInputContext::UpdateFepInlineTextL(const TDesC& aNewInlineText,
+ TInt aPositionOfInsertionPointInInlineText)
+{
+ QWidget *w = focusWidget();
+ if (!w)
+ return;
+
+ commitTemporaryPreeditString();
+
+ m_inlinePosition = aPositionOfInsertionPointInInlineText;
+
+ QList<QInputMethodEvent::Attribute> attributes;
+ applyFormat(&attributes);
+ attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor,
+ m_inlinePosition,
+ m_cursorVisibility,
+ QVariant()));
+ QString newPreeditString = qt_TDesC2QString(aNewInlineText);
+ QInputMethodEvent event(newPreeditString, attributes);
+ if (newPreeditString.isEmpty() && m_preeditString.isEmpty()) {
+ // In Symbian world this means "erase last character".
+ event.setCommitString(QLatin1String(""), -1, 1);
+ }
+ m_preeditString = newPreeditString;
+ sendEvent(event);
+}
+
+void QCoeFepInputContext::SetInlineEditingCursorVisibilityL(TBool aCursorVisibility)
+{
+ QWidget *w = focusWidget();
+ if (!w)
+ return;
+
+ m_cursorVisibility = aCursorVisibility ? 1 : 0;
+
+ QList<QInputMethodEvent::Attribute> attributes;
+ attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor,
+ m_inlinePosition,
+ m_cursorVisibility,
+ QVariant()));
+ QInputMethodEvent event(m_preeditString, attributes);
+ sendEvent(event);
+}
+
+void QCoeFepInputContext::CancelFepInlineEdit()
+{
+ // We are not supposed to ever have a tempPreeditString and a real preedit string
+ // from S60 at the same time, so it should be safe to rely on this test to determine
+ // whether we should honor S60's request to clear the text or not.
+ if (m_hasTempPreeditString)
+ return;
+
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent event(QLatin1String(""), attributes);
+ event.setCommitString(QLatin1String(""), 0, 0);
+ m_preeditString.clear();
+ m_inlinePosition = 0;
+ sendEvent(event);
+}
+
+TInt QCoeFepInputContext::DocumentLengthForFep() const
+{
+ QWidget *w = focusWidget();
+ if (!w)
+ return 0;
+
+ QVariant variant = w->inputMethodQuery(Qt::ImSurroundingText);
+ return variant.value<QString>().size() + m_preeditString.size();
+}
+
+TInt QCoeFepInputContext::DocumentMaximumLengthForFep() const
+{
+ QWidget *w = focusWidget();
+ if (!w)
+ return 0;
+
+ QVariant variant = w->inputMethodQuery(Qt::ImMaximumTextLength);
+ int size;
+ if (variant.isValid()) {
+ size = variant.toInt();
+ } else {
+ size = INT_MAX; // Sensible default for S60.
+ }
+ return size;
+}
+
+void QCoeFepInputContext::SetCursorSelectionForFepL(const TCursorSelection& aCursorSelection)
+{
+ QWidget *w = focusWidget();
+ if (!w)
+ return;
+
+ commitTemporaryPreeditString();
+
+ int pos = aCursorSelection.iAnchorPos;
+ int length = aCursorSelection.iCursorPos - pos;
+
+ QList<QInputMethodEvent::Attribute> attributes;
+ attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, pos, length, QVariant());
+ QInputMethodEvent event(m_preeditString, attributes);
+ sendEvent(event);
+}
+
+void QCoeFepInputContext::GetCursorSelectionForFep(TCursorSelection& aCursorSelection) const
+{
+ QWidget *w = focusWidget();
+ if (!w) {
+ aCursorSelection.SetSelection(0,0);
+ return;
+ }
+
+ int cursor = w->inputMethodQuery(Qt::ImCursorPosition).toInt() + m_preeditString.size();
+ int anchor = w->inputMethodQuery(Qt::ImAnchorPosition).toInt() + m_preeditString.size();
+ QString text = w->inputMethodQuery(Qt::ImSurroundingText).value<QString>();
+ int combinedSize = text.size() + m_preeditString.size();
+ if (combinedSize < anchor || combinedSize < cursor) {
+ // ### TODO! FIXME! QTBUG-5050
+ // This is a hack to prevent crashing in 4.6 with QLineEdits that use input masks.
+ // The root problem is that cursor position is relative to displayed text instead of the
+ // actual text we get.
+ //
+ // To properly fix this we would need to know the displayText of QLineEdits instead
+ // of just the text, which on itself should be a trivial change. The difficulties start
+ // when we need to commit the changes back to the QLineEdit, which would have to be somehow
+ // able to handle displayText, too.
+ //
+ // Until properly fixed, the cursor and anchor positions will not reflect correct positions
+ // for masked QLineEdits, unless all the masked positions are filled in order so that
+ // cursor position relative to the displayed text matches position relative to actual text.
+ aCursorSelection.iAnchorPos = combinedSize;
+ aCursorSelection.iCursorPos = combinedSize;
+ } else {
+ aCursorSelection.iAnchorPos = anchor;
+ aCursorSelection.iCursorPos = cursor;
+ }
+}
+
+void QCoeFepInputContext::GetEditorContentForFep(TDes& aEditorContent, TInt aDocumentPosition,
+ TInt aLengthToRetrieve) const
+{
+ QWidget *w = focusWidget();
+ if (!w) {
+ aEditorContent.FillZ(aLengthToRetrieve);
+ return;
+ }
+
+ QString text = w->inputMethodQuery(Qt::ImSurroundingText).value<QString>();
+ // FEP expects the preedit string to be part of the editor content, so let's mix it in.
+ int cursor = w->inputMethodQuery(Qt::ImCursorPosition).toInt();
+ text.insert(cursor, m_preeditString);
+ aEditorContent.Copy(qt_QString2TPtrC(text.mid(aDocumentPosition, aLengthToRetrieve)));
+}
+
+void QCoeFepInputContext::GetFormatForFep(TCharFormat& aFormat, TInt /* aDocumentPosition */) const
+{
+ QWidget *w = focusWidget();
+ if (!w) {
+ aFormat = TCharFormat();
+ return;
+ }
+
+ QFont font = w->inputMethodQuery(Qt::ImFont).value<QFont>();
+ QFontMetrics metrics(font);
+ //QString name = font.rawName();
+ QString name = font.defaultFamily(); // TODO! FIXME! Should be the above.
+ QHBufC hBufC(name);
+ aFormat = TCharFormat(hBufC->Des(), metrics.height());
+}
+
+void QCoeFepInputContext::GetScreenCoordinatesForFepL(TPoint& aLeftSideOfBaseLine, TInt& aHeight,
+ TInt& aAscent, TInt /* aDocumentPosition */) const
+{
+ QWidget *w = focusWidget();
+ if (!w) {
+ aLeftSideOfBaseLine = TPoint(0,0);
+ aHeight = 0;
+ aAscent = 0;
+ return;
+ }
+
+ QRect rect = w->inputMethodQuery(Qt::ImMicroFocus).value<QRect>();
+ aLeftSideOfBaseLine.iX = rect.left();
+ aLeftSideOfBaseLine.iY = rect.bottom();
+
+ QFont font = w->inputMethodQuery(Qt::ImFont).value<QFont>();
+ QFontMetrics metrics(font);
+ aHeight = metrics.height();
+ aAscent = metrics.ascent();
+}
+
+void QCoeFepInputContext::DoCommitFepInlineEditL()
+{
+ commitCurrentString(false);
+ if (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0)
+ ReportAknEdStateEvent(QT_EAknCursorPositionChanged);
+
+}
+
+void QCoeFepInputContext::commitCurrentString(bool cancelFepTransaction)
+{
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent event(QLatin1String(""), attributes);
+ event.setCommitString(m_preeditString, 0, 0);
+ m_preeditString.clear();
+ m_inlinePosition = 0;
+ sendEvent(event);
+
+ m_hasTempPreeditString = false;
+
+ if (cancelFepTransaction) {
+ CCoeFep* fep = CCoeEnv::Static()->Fep();
+ if (fep)
+ fep->CancelTransaction();
+ }
+}
+
+MCoeFepAwareTextEditor_Extension1* QCoeFepInputContext::Extension1(TBool& aSetToTrue)
+{
+ aSetToTrue = ETrue;
+ return this;
+}
+
+void QCoeFepInputContext::SetStateTransferingOwnershipL(MCoeFepAwareTextEditor_Extension1::CState* aState,
+ TUid /*aTypeSafetyUid*/)
+{
+ // Note: The S60 docs are wrong! See the State() function.
+ if (m_fepState)
+ delete m_fepState;
+ m_fepState = static_cast<CAknEdwinState *>(aState);
+}
+
+MCoeFepAwareTextEditor_Extension1::CState* QCoeFepInputContext::State(TUid /*aTypeSafetyUid*/)
+{
+ // Note: The S60 docs are horribly wrong when describing the
+ // SetStateTransferingOwnershipL function and this function. They say that the former
+ // sets a CState object identified by the TUid, and the latter retrieves it.
+ // In reality, the CState is expected to always be a CAknEdwinState (even if it was not
+ // previously set), and the TUid is ignored. All in all, there is a single CAknEdwinState
+ // per QCoeFepInputContext, which should be deleted if the SetStateTransferingOwnershipL
+ // function is used to set a new one.
+ return m_fepState;
+}
+
+TTypeUid::Ptr QCoeFepInputContext::MopSupplyObject(TTypeUid /*id*/)
+{
+ return TTypeUid::Null();
+}
+
+MObjectProvider *QCoeFepInputContext::MopNext()
+{
+ QWidget *w = focusWidget();
+ if (w)
+ return w->effectiveWinId();
+ return 0;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_IM
diff --git a/src/widgets/platforms/s60/qcolormap_s60.cpp b/src/widgets/platforms/s60/qcolormap_s60.cpp
new file mode 100644
index 0000000000..9dd5135d29
--- /dev/null
+++ b/src/widgets/platforms/s60/qcolormap_s60.cpp
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcolormap.h"
+#include "qcolor.h"
+
+QT_BEGIN_NAMESPACE
+
+class QColormapPrivate
+{
+public:
+ inline QColormapPrivate()
+ : ref(1)
+ { }
+
+ QAtomicInt ref;
+};
+
+void QColormap::initialize()
+{
+}
+
+void QColormap::cleanup()
+{
+}
+
+QColormap QColormap::instance(int)
+{
+ return QColormap();
+}
+
+QColormap::QColormap() : d(new QColormapPrivate)
+{}
+
+QColormap::QColormap(const QColormap &colormap) :d (colormap.d)
+{ d->ref.ref(); }
+
+QColormap::~QColormap()
+{
+ if (!d->ref.deref())
+ delete d;
+}
+
+QColormap::Mode QColormap::mode() const
+{ return QColormap::Direct; }
+
+int QColormap::depth() const
+{
+ return 32;
+}
+
+int QColormap::size() const
+{
+ return -1;
+}
+
+uint QColormap::pixel(const QColor &color) const
+{ return color.rgba(); }
+
+const QColor QColormap::colorAt(uint pixel) const
+{ return QColor(pixel); }
+
+const QVector<QColor> QColormap::colormap() const
+{ return QVector<QColor>(); }
+
+QColormap &QColormap::operator=(const QColormap &colormap)
+{ qAtomicAssign(d, colormap.d); return *this; }
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/s60/qcursor_s60.cpp b/src/widgets/platforms/s60/qcursor_s60.cpp
new file mode 100644
index 0000000000..2e9a2bfa33
--- /dev/null
+++ b/src/widgets/platforms/s60/qcursor_s60.cpp
@@ -0,0 +1,533 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qcursor_p.h>
+#include <private/qwidget_p.h>
+#include <private/qapplication_p.h>
+#include <coecntrl.h>
+#include <qcursor.h>
+#include <private/qt_s60_p.h>
+#include <qbitmap.h>
+#include <w32std.h>
+#include <qapplication.h>
+#include <qwidget.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_CURSOR
+static QCursor cursorSprite;
+static int cursorSpriteVisible;
+#endif
+
+//pos and setpos are required whether cursors are configured or not.
+QPoint QCursor::pos()
+{
+ return S60->lastCursorPos;
+}
+
+void QCursor::setPos(int x, int y)
+{
+ //clip to screen size (window server allows a sprite hotspot to be outside the screen)
+ if (x < 0)
+ x=0;
+ else if (x >= S60->screenWidthInPixels)
+ x = S60->screenWidthInPixels - 1;
+ if (y < 0)
+ y = 0;
+ else if (y >= S60->screenHeightInPixels)
+ y = S60->screenHeightInPixels - 1;
+
+#ifndef QT_NO_CURSOR
+#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS
+ if (S60->brokenPointerCursors && cursorSpriteVisible)
+ cursorSprite.d->scurs.SetPosition(TPoint(x,y));
+ else
+#endif
+ S60->wsSession().SetPointerCursorPosition(TPoint(x, y));
+#endif
+ S60->lastCursorPos = QPoint(x, y);
+ //send a fake mouse move event, so that enter/leave events go to the widget hierarchy
+ QWidget *w = QApplication::topLevelAt(S60->lastCursorPos);
+ if (w) {
+ CCoeControl* ctrl = w->effectiveWinId();
+ TPoint epos(x, y);
+ TPoint cpos = epos - ctrl->PositionRelativeToScreen();
+ TPointerEvent fakeEvent;
+ fakeEvent.iType = TPointerEvent::EMove;
+ fakeEvent.iModifiers = 0U;
+ fakeEvent.iPosition = cpos;
+ fakeEvent.iParentPosition = epos;
+ ctrl->HandlePointerEventL(fakeEvent);
+ }
+}
+
+#ifndef QT_NO_CURSOR
+/*
+ * Request cursor to be turned on or off.
+ * Reference counted, so 2 on + 1 off = on, for example
+ */
+void qt_symbian_set_cursor_visible(bool visible) {
+ if (visible)
+ cursorSpriteVisible++;
+ else
+ cursorSpriteVisible--;
+ Q_ASSERT(cursorSpriteVisible >=0);
+
+ if (cursorSpriteVisible && !S60->mouseInteractionEnabled) {
+#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS
+ if (S60->brokenPointerCursors)
+ qt_symbian_show_pointer_sprite();
+ else
+#endif
+ S60->wsSession().SetPointerCursorMode(EPointerCursorNormal);
+ } else if (!cursorSpriteVisible && S60->mouseInteractionEnabled) {
+#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS
+ if (S60->brokenPointerCursors)
+ qt_symbian_hide_pointer_sprite();
+ else
+#endif
+ S60->wsSession().SetPointerCursorMode(EPointerCursorNone);
+ }
+ S60->mouseInteractionEnabled = ((cursorSpriteVisible > 0) ? true : false);
+}
+
+/*
+ * Check if the cursor is on or off
+ */
+bool qt_symbian_is_cursor_visible() {
+ return S60->mouseInteractionEnabled;
+}
+
+QCursorData::QCursorData(Qt::CursorShape s) :
+ cshape(s), bm(0), bmm(0), hx(0), hy(0), pcurs()
+{
+ ref = 1;
+}
+
+QCursorData::~QCursorData()
+{
+ for(int i=0;i<nativeSpriteMembers.Count();i++) {
+ delete nativeSpriteMembers[i]->iBitmap;
+ delete nativeSpriteMembers[i]->iMaskBitmap;
+ }
+ nativeSpriteMembers.ResetAndDestroy();
+ pcurs.Close();
+ delete bm;
+ delete bmm;
+}
+
+/* Create a bitmap cursor, this is called by public constructors in the
+ * generic QCursor code.
+ */
+QCursorData *QCursorData::setBitmap(const QBitmap &bitmap, const QBitmap &mask, int hotX, int hotY)
+{
+ if (!QCursorData::initialized)
+ QCursorData::initialize();
+ if (bitmap.depth() != 1 || mask.depth() != 1 || bitmap.size() != mask.size()) {
+ qWarning("QCursor: Cannot create bitmap cursor; invalid bitmap(s)");
+ QCursorData *c = qt_cursorTable[0];
+ c->ref.ref();
+ return c;
+ }
+ QCursorData *d = new QCursorData;
+ d->bm = new QBitmap(bitmap);
+ d->bmm = new QBitmap(mask);
+ d->cshape = Qt::BitmapCursor;
+ d->hx = hotX >= 0 ? hotX : bitmap.width() / 2;
+ d->hy = hotY >= 0 ? hotY : bitmap.height() / 2;
+ return d;
+}
+
+/*
+ * returns an opaque native handle to a cursor.
+ * It happens to be the address of the native handle, as window server handles
+ * are not POD types. Note there is no QCursor(HANDLE) constructor on Symbian,
+ * Mac or QWS.
+ */
+Qt::HANDLE QCursor::handle() const
+{
+ if (d->pcurs.WsHandle())
+ return reinterpret_cast<Qt::HANDLE> (&(d->pcurs));
+
+#ifdef Q_SYMBIAN_HAS_SYSTEM_CURSORS
+ // don't construct shape cursors, QApplication_s60 will use the system cursor instead
+ if (!(d->bm))
+ return 0;
+#endif
+
+ d->pcurs = RWsPointerCursor(S60->wsSession());
+ d->pcurs.Construct(0);
+ d->constructCursorSprite(d->pcurs);
+ d->pcurs.Activate();
+
+ return reinterpret_cast<Qt::HANDLE> (&(d->pcurs));
+}
+
+#ifndef Q_SYMBIAN_HAS_SYSTEM_CURSORS
+/*
+ * Loads a single cursor shape from resources and appends it to a native sprite.
+ * Animated cursors (e.g. the busy cursor) have multiple members.
+ */
+void QCursorData::loadShapeFromResource(RWsSpriteBase& target, QString resource, int hx, int hy, int interval)
+{
+ QPixmap pix;
+ CFbsBitmap* native;
+ QScopedPointer<TSpriteMember> member(new TSpriteMember);
+ member->iInterval = interval;
+ member->iInvertMask = false;
+ member->iMaskBitmap = 0; // all shapes are RGBA
+ member->iDrawMode = CGraphicsContext::EDrawModePEN;
+ member->iOffset = TPoint(-hx, -hy);
+ QString res(QLatin1String(":/trolltech/symbian/cursors/images/%1.png"));
+ pix.load(res.arg(resource));
+ native = pix.toSymbianCFbsBitmap();
+ member->iBitmap = native;
+ qt_symbian_throwIfError(nativeSpriteMembers.Append(member.data()));
+ target.AppendMember(*(member.take()));
+}
+
+//TODO: after 4.6, connect with style & skins?
+/*
+ * Constructs the native cursor from resources compiled into QtGui
+ * This is needed only when the platform doesn't have system cursors.
+ *
+ * System cursors are higher performance, since they are constructed once
+ * and shared by all applications by specifying the shape number.
+ * Due to symbian platform security considerations, and the fact most
+ * existing phones have a broken RWsPointerCursor, system cursors are not
+ * being used.
+ */
+void QCursorData::constructShapeSprite(RWsSpriteBase& target)
+{
+ int i;
+ switch (cshape) {
+ default:
+ qWarning("QCursorData::constructShapeSprite unknown shape %d", cshape);
+ //fall through and give arrow cursor
+ case Qt::ArrowCursor:
+ loadShapeFromResource(target, QLatin1String("pointer"), 1, 1);
+ break;
+ case Qt::UpArrowCursor:
+ loadShapeFromResource(target, QLatin1String("uparrow"), 4, 0);
+ break;
+ case Qt::CrossCursor:
+ loadShapeFromResource(target, QLatin1String("cross"), 7, 7);
+ break;
+ case Qt::WaitCursor:
+ for (i = 1; i <= 12; i++) {
+ loadShapeFromResource(target, QString(QLatin1String("wait%1")).arg(i), 7, 7, 1000000);
+ }
+ break;
+ case Qt::IBeamCursor:
+ loadShapeFromResource(target, QLatin1String("ibeam"), 3, 10);
+ break;
+ case Qt::SizeVerCursor:
+ loadShapeFromResource(target, QLatin1String("sizever"), 4, 8);
+ break;
+ case Qt::SizeHorCursor:
+ loadShapeFromResource(target, QLatin1String("sizehor"), 8, 4);
+ break;
+ case Qt::SizeBDiagCursor:
+ loadShapeFromResource(target, QLatin1String("sizebdiag"), 8, 8);
+ break;
+ case Qt::SizeFDiagCursor:
+ loadShapeFromResource(target, QLatin1String("sizefdiag"), 8, 8);
+ break;
+ case Qt::SizeAllCursor:
+ loadShapeFromResource(target, QLatin1String("sizeall"), 7, 7);
+ break;
+ case Qt::BlankCursor:
+ loadShapeFromResource(target, QLatin1String("blank"), 0, 0);
+ break;
+ case Qt::SplitVCursor:
+ loadShapeFromResource(target, QLatin1String("splitv"), 7, 7);
+ break;
+ case Qt::SplitHCursor:
+ loadShapeFromResource(target, QLatin1String("splith"), 7, 7);
+ break;
+ case Qt::PointingHandCursor:
+ loadShapeFromResource(target, QLatin1String("handpoint"), 5, 0);
+ break;
+ case Qt::ForbiddenCursor:
+ loadShapeFromResource(target, QLatin1String("forbidden"), 7, 7);
+ break;
+ case Qt::WhatsThisCursor:
+ loadShapeFromResource(target, QLatin1String("whatsthis"), 1, 1);
+ break;
+ case Qt::BusyCursor:
+ loadShapeFromResource(target, QLatin1String("busy3"), 1, 1, 1000000);
+ loadShapeFromResource(target, QLatin1String("busy6"), 1, 1, 1000000);
+ loadShapeFromResource(target, QLatin1String("busy9"), 1, 1, 1000000);
+ loadShapeFromResource(target, QLatin1String("busy12"), 1, 1, 1000000);
+ break;
+ case Qt::OpenHandCursor:
+ loadShapeFromResource(target, QLatin1String("openhand"), 7, 7);
+ break;
+ case Qt::ClosedHandCursor:
+ loadShapeFromResource(target, QLatin1String("closehand"), 7, 7);
+ break;
+ }
+}
+#endif
+
+/*
+ * Common code between the sprite workaround and standard modes of operation.
+ * RWsSpriteBase is the base class for both RWsSprite and RWsPointerCursor.
+ * It is called from both handle() and qt_s60_show_pointer_sprite()
+ */
+void QCursorData::constructCursorSprite(RWsSpriteBase& target)
+{
+ int count = nativeSpriteMembers.Count();
+ if (count) {
+ // already constructed
+ for (int i = 0; i < count; i++)
+ target.AppendMember(*(nativeSpriteMembers[i]));
+
+ return;
+ }
+ if (pixmap.isNull() && !bm) {
+#ifndef Q_SYMBIAN_HAS_SYSTEM_CURSORS
+ //shape cursor
+ constructShapeSprite(target);
+#endif
+ return;
+ }
+ QScopedPointer<TSpriteMember> member(new TSpriteMember);
+ if (pixmap.isNull()) {
+ //construct mono cursor
+ member->iBitmap = bm->toSymbianCFbsBitmap();
+ member->iMaskBitmap = bmm->toSymbianCFbsBitmap();
+ }
+ else {
+ //construct normal cursor
+ member->iBitmap = pixmap.toSymbianCFbsBitmap();
+ if (pixmap.hasAlphaChannel()) {
+ member->iMaskBitmap = 0; //use alpha blending
+ }
+ else if (pixmap.hasAlpha()) {
+ member->iMaskBitmap = pixmap.mask().toSymbianCFbsBitmap();
+ }
+ else {
+ member->iMaskBitmap = 0; //opaque rectangle cursor (due to EDrawModePEN)
+ }
+ }
+
+ member->iDrawMode = CGraphicsContext::EDrawModePEN;
+ member->iInvertMask = EFalse;
+ member->iInterval = 0;
+ member->iOffset = TPoint(-(hx), -(hy)); //Symbian hotspot coordinates are negative
+ qt_symbian_throwIfError(nativeSpriteMembers.Append(member.data()));
+ target.AppendMember(*(member.take()));
+}
+
+/*
+ * shows the pointer sprite by constructing a native handle, and registering
+ * it with the window server.
+ * Only used when the sprite workaround is in use.
+ */
+void qt_symbian_show_pointer_sprite()
+{
+ if (cursorSprite.d) {
+ if (cursorSprite.d->scurs.WsHandle())
+ cursorSprite.d->scurs.Close();
+ } else {
+ cursorSprite = QCursor(Qt::ArrowCursor);
+ }
+
+ cursorSprite.d->scurs = RWsSprite(S60->wsSession());
+ QPoint pos = QCursor::pos();
+ cursorSprite.d->scurs.Construct(S60->windowGroup(), TPoint(pos.x(), pos.y()), ESpriteNoChildClip | ESpriteNoShadows);
+
+ cursorSprite.d->constructCursorSprite(cursorSprite.d->scurs);
+ cursorSprite.d->scurs.Activate();
+}
+
+/*
+ * hides the pointer sprite by closing the native handle.
+ * Only used when the sprite workaround is in use.
+ */
+void qt_symbian_hide_pointer_sprite()
+{
+ if (cursorSprite.d) {
+ cursorSprite.d->scurs.Close();
+ }
+}
+
+/*
+ * Changes the cursor sprite to the cursor specified.
+ * Only used when the sprite workaround is in use.
+ */
+void qt_symbian_set_pointer_sprite(const QCursor& cursor)
+{
+ if (S60->mouseInteractionEnabled)
+ qt_symbian_hide_pointer_sprite();
+ cursorSprite = cursor;
+ if (S60->mouseInteractionEnabled)
+ qt_symbian_show_pointer_sprite();
+}
+
+/*
+ * When using sprites as a workaround on phones that have a broken
+ * RWsPointerCursor, this function is called in response to pointer events
+ * and when QCursor::setPos() is called.
+ * Performance is worse than a real pointer cursor, due to extra context
+ * switches vs. the window server moving the cursor by itself.
+ */
+void qt_symbian_move_cursor_sprite()
+{
+ if (S60->mouseInteractionEnabled) {
+ cursorSprite.d->scurs.SetPosition(TPoint(S60->lastCursorPos.x(), S60->lastCursorPos.y()));
+ }
+}
+
+/*
+ * Translate from Qt::CursorShape to OS system pointer cursor list index.
+ * Currently we control the implementation of the system pointer cursor list,
+ * so this function is trivial. That may not always be the case.
+ */
+TInt qt_symbian_translate_cursor_shape(Qt::CursorShape shape)
+{
+ return (TInt) shape;
+}
+
+/*
+ Internal function called from QWidget::setCursor()
+ force is true if this function is called from dispatchEnterLeave, it means that the
+ mouse is actually directly under this widget.
+*/
+void qt_symbian_set_cursor(QWidget *w, bool force)
+{
+ static QPointer<QWidget> lastUnderMouse = 0;
+ if (force) {
+ lastUnderMouse = w;
+ }
+ else if (w->testAttribute(Qt::WA_WState_Created) && lastUnderMouse
+ && lastUnderMouse->effectiveWinId() == w->effectiveWinId()) {
+ w = lastUnderMouse;
+ }
+
+ if (!S60->curWin && w && w->internalWinId())
+ return;
+ QWidget* cW = w && !w->internalWinId() ? w : QWidget::find(S60->curWin);
+ if (!cW || cW->window() != w->window() || !cW->isVisible() || !cW->underMouse()
+ || QApplication::overrideCursor())
+ return;
+
+#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS
+ if (S60->brokenPointerCursors)
+ qt_symbian_set_pointer_sprite(cW->cursor());
+ else
+#endif
+ qt_symbian_setWindowCursor(cW->cursor(), w->effectiveWinId());
+}
+
+/*
+ * Makes the specified cursor appear above a specific native window group
+ * Called from QSymbianControl and QApplication::restoreOverrideCursor
+ *
+ * Window server is needed for this, so there is no equivalent when using
+ * the sprite workaround.
+ */
+void qt_symbian_setWindowGroupCursor(const QCursor &cursor, RWindowTreeNode &node)
+{
+ Qt::HANDLE handle = cursor.handle();
+ if (handle) {
+ RWsPointerCursor *pcurs = reinterpret_cast<RWsPointerCursor *> (handle);
+ node.SetCustomPointerCursor(*pcurs);
+ } else
+#ifdef Q_SYMBIAN_HAS_SYSTEM_CURSORS
+ {
+ TInt shape = qt_symbian_translate_cursor_shape(cursor.shape());
+ node.SetPointerCursor(shape);
+ }
+#else
+ qWarning("qt_s60_setWindowGroupCursor - null handle");
+#endif
+}
+
+/*
+ * Makes the specified cursor appear above a specific native window
+ * Called from QSymbianControl and QApplication::restoreOverrideCursor
+ *
+ * Window server is needed for this, so there is no equivalent when using
+ * the sprite workaround.
+ */
+void qt_symbian_setWindowCursor(const QCursor &cursor, const CCoeControl* wid)
+{
+ //find the window for this control
+ while (!wid->OwnsWindow()) {
+ wid = wid->Parent();
+ if (!wid)
+ return;
+ }
+ RWindowTreeNode *node = wid->DrawableWindow();
+ qt_symbian_setWindowGroupCursor(cursor, *node);
+}
+
+/*
+ * Makes the specified cursor appear everywhere.
+ * Called from QApplication::setOverrideCursor
+ */
+void qt_symbian_setGlobalCursor(const QCursor &cursor)
+{
+#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS
+ if (S60->brokenPointerCursors) {
+ qt_symbian_set_pointer_sprite(cursor);
+ } else
+#endif
+ {
+ //because of the internals of window server, we need to force the cursor
+ //to be set in all child windows too, otherwise when the cursor is over
+ //the child window it may show a widget cursor or arrow cursor instead,
+ //depending on construction order.
+ QListIterator<WId> iter(QWidgetPrivate::mapper->uniqueKeys());
+ while(iter.hasNext())
+ {
+ CCoeControl *ctrl = iter.next();
+ if(ctrl->OwnsWindow()) {
+ RWindowTreeNode *node = ctrl->DrawableWindow();
+ qt_symbian_setWindowGroupCursor(cursor, *node);
+ }
+ }
+ }
+}
+QT_END_NAMESPACE
+#endif // QT_NO_CURSOR
diff --git a/src/widgets/platforms/s60/qdesktopwidget_s60.cpp b/src/widgets/platforms/s60/qdesktopwidget_s60.cpp
new file mode 100644
index 0000000000..156c9700ed
--- /dev/null
+++ b/src/widgets/platforms/s60/qdesktopwidget_s60.cpp
@@ -0,0 +1,316 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdesktopwidget.h"
+#include "qapplication_p.h"
+#include "qwidget_p.h"
+#include "qt_s60_p.h"
+#include <w32std.h>
+#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS)
+#include <graphics/displaycontrol.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+extern int qt_symbian_create_desktop_on_screen;
+
+class QSingleDesktopWidget : public QWidget
+{
+public:
+ QSingleDesktopWidget();
+ ~QSingleDesktopWidget();
+};
+
+QSingleDesktopWidget::QSingleDesktopWidget()
+ : QWidget(0, Qt::Desktop)
+{
+}
+
+QSingleDesktopWidget::~QSingleDesktopWidget()
+{
+ const QObjectList &childList = children();
+ for (int i = childList.size(); i > 0 ;) {
+ --i;
+ childList.at(i)->setParent(0);
+ }
+}
+
+class QDesktopWidgetPrivate : public QWidgetPrivate
+{
+public:
+ QDesktopWidgetPrivate();
+ ~QDesktopWidgetPrivate();
+ static void init(QDesktopWidget *that);
+ static void cleanup();
+ static void init_sys();
+
+ static int screenCount;
+ static int primaryScreen;
+
+ static QVector<QRect> *rects;
+ static QVector<QRect> *workrects;
+ static QVector<QWidget *> *screens;
+
+ static int refcount;
+
+#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS)
+ static MDisplayControl *displayControl;
+#endif
+};
+
+int QDesktopWidgetPrivate::screenCount = 1;
+int QDesktopWidgetPrivate::primaryScreen = 0;
+QVector<QRect> *QDesktopWidgetPrivate::rects = 0;
+QVector<QRect> *QDesktopWidgetPrivate::workrects = 0;
+QVector<QWidget *> *QDesktopWidgetPrivate::screens = 0;
+int QDesktopWidgetPrivate::refcount = 0;
+#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS)
+MDisplayControl *QDesktopWidgetPrivate::displayControl = 0;
+#endif
+
+QDesktopWidgetPrivate::QDesktopWidgetPrivate()
+{
+ ++refcount;
+}
+
+QDesktopWidgetPrivate::~QDesktopWidgetPrivate()
+{
+ if (!--refcount)
+ cleanup();
+}
+
+void QDesktopWidgetPrivate::init(QDesktopWidget *that)
+{
+ // Note that on S^3 devices the screen count retrieved via RWsSession
+ // will always be 2 but the width and height for screen number 1 will
+ // be 0 as long as TV-out is not connected.
+ //
+ // On the other hand a valid size for screen 1 will be reported even
+ // after the cable is disconnected. In order to overcome this, we use
+ // MDisplayControl::NumberOfResolutions() to check if the display is
+ // valid or not.
+
+ screenCount = S60->screenCount();
+#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS)
+ if (displayControl) {
+ if (displayControl->NumberOfResolutions() < 1)
+ screenCount = 1;
+ }
+#endif
+ if (screenCount < 1) {
+ qWarning("No screen available");
+ screenCount = 1;
+ }
+
+ rects = new QVector<QRect>();
+ workrects = new QVector<QRect>();
+ screens = new QVector<QWidget *>();
+
+ rects->resize(screenCount);
+ workrects->resize(screenCount);
+ screens->resize(screenCount);
+
+ for (int i = 0; i < screenCount; ++i) {
+ // All screens will have a position of (0, 0) as there is no true virtual desktop
+ // or pointer event support for multiple screens on Symbian.
+ QRect r(0, 0,
+ S60->screenWidthInPixelsForScreen[i], S60->screenHeightInPixelsForScreen[i]);
+ // Stop here if empty and ignore this screen.
+ if (r.isEmpty()) {
+ screenCount = i;
+ break;
+ }
+ (*rects)[i] = r;
+ QRect wr;
+ if (i == 0)
+ wr = qt_TRect2QRect(static_cast<CEikAppUi*>(S60->appUi())->ClientRect());
+ else
+ wr = rects->at(i);
+ (*workrects)[i].setRect(wr.x(), wr.y(), wr.width(), wr.height());
+ (*screens)[i] = 0;
+ }
+ (*screens)[0] = that;
+}
+
+void QDesktopWidgetPrivate::cleanup()
+{
+ delete rects;
+ rects = 0;
+ delete workrects;
+ workrects = 0;
+ if (screens) {
+ // First item is the QDesktopWidget so skip it.
+ for (int i = 1; i < screens->count(); ++i)
+ delete screens->at(i);
+ }
+ delete screens;
+ screens = 0;
+}
+
+void QDesktopWidgetPrivate::init_sys()
+{
+#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS)
+ if (S60->screenCount() > 1) {
+ CWsScreenDevice *dev = S60->screenDevice(1);
+ if (dev) {
+ displayControl = static_cast<MDisplayControl *>(
+ dev->GetInterface(MDisplayControl::ETypeId));
+ if (displayControl) {
+ displayControl->EnableDisplayChangeEvents(ETrue);
+ }
+ }
+ }
+#endif
+}
+
+
+QDesktopWidget::QDesktopWidget()
+ : QWidget(*new QDesktopWidgetPrivate, 0, Qt::Desktop)
+{
+ setObjectName(QLatin1String("desktop"));
+ QDesktopWidgetPrivate::init_sys();
+ QDesktopWidgetPrivate::init(this);
+}
+
+QDesktopWidget::~QDesktopWidget()
+{
+}
+
+bool QDesktopWidget::isVirtualDesktop() const
+{
+ return false;
+}
+
+int QDesktopWidget::primaryScreen() const
+{
+ return QDesktopWidgetPrivate::primaryScreen;
+}
+
+int QDesktopWidget::numScreens() const
+{
+ Q_D(const QDesktopWidget);
+ return QDesktopWidgetPrivate::screenCount;
+}
+
+static inline QWidget *newSingleDesktopWidget(int screen)
+{
+ qt_symbian_create_desktop_on_screen = screen;
+ QWidget *w = new QSingleDesktopWidget;
+ qt_symbian_create_desktop_on_screen = -1;
+ return w;
+}
+
+QWidget *QDesktopWidget::screen(int screen)
+{
+ Q_D(QDesktopWidget);
+ if (screen < 0 || screen >= d->screenCount)
+ screen = d->primaryScreen;
+ if (!d->screens->at(screen)
+ || d->screens->at(screen)->windowType() != Qt::Desktop)
+ (*d->screens)[screen] = newSingleDesktopWidget(screen);
+ return (*d->screens)[screen];
+}
+
+const QRect QDesktopWidget::availableGeometry(int screen) const
+{
+ Q_D(const QDesktopWidget);
+ if (screen < 0 || screen >= d->screenCount)
+ screen = d->primaryScreen;
+
+ return d->workrects->at(screen);
+}
+
+const QRect QDesktopWidget::screenGeometry(int screen) const
+{
+ Q_D(const QDesktopWidget);
+ if (screen < 0 || screen >= d->screenCount)
+ screen = d->primaryScreen;
+
+ return d->rects->at(screen);
+}
+
+int QDesktopWidget::screenNumber(const QWidget *widget) const
+{
+ Q_D(const QDesktopWidget);
+ return widget
+ ? S60->screenNumberForWidget(widget)
+ : d->primaryScreen;
+}
+
+int QDesktopWidget::screenNumber(const QPoint &point) const
+{
+ Q_UNUSED(point);
+ Q_D(const QDesktopWidget);
+ return d->primaryScreen;
+}
+
+void QDesktopWidget::resizeEvent(QResizeEvent *)
+{
+ Q_D(QDesktopWidget);
+ QVector<QRect> oldrects;
+ oldrects = *d->rects;
+ QVector<QRect> oldworkrects;
+ oldworkrects = *d->workrects;
+ int oldscreencount = d->screenCount;
+
+ QDesktopWidgetPrivate::cleanup();
+ QDesktopWidgetPrivate::init(this);
+
+ for (int i = 0; i < qMin(oldscreencount, d->screenCount); ++i) {
+ QRect oldrect = oldrects[i];
+ QRect newrect = d->rects->at(i);
+ if (oldrect != newrect)
+ emit resized(i);
+ }
+
+ for (int j = 0; j < qMin(oldscreencount, d->screenCount); ++j) {
+ QRect oldrect = oldworkrects[j];
+ QRect newrect = d->workrects->at(j);
+ if (oldrect != newrect)
+ emit workAreaResized(j);
+ }
+
+ if (oldscreencount != d->screenCount) {
+ emit screenCountChanged(d->screenCount);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/s60/qdnd_s60.cpp b/src/widgets/platforms/s60/qdnd_s60.cpp
new file mode 100644
index 0000000000..53afa71828
--- /dev/null
+++ b/src/widgets/platforms/s60/qdnd_s60.cpp
@@ -0,0 +1,359 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qapplication.h"
+
+#ifndef QT_NO_DRAGANDDROP
+
+#include "qwidget.h"
+#include "qdatetime.h"
+#include "qbitmap.h"
+#include "qcursor.h"
+#include "qevent.h"
+#include "qpainter.h"
+#include "qdnd_p.h"
+#include "qt_s60_p.h"
+
+#include <coecntrl.h>
+// pointer cursor
+#include <w32std.h>
+#include <gdi.h>
+#include <QCursor>
+
+QT_BEGIN_NAMESPACE
+//### artistic impression of Symbians default DnD cursor ?
+
+static QPixmap *defaultPm = 0;
+static const int default_pm_hotx = -50;
+static const int default_pm_hoty = -50;
+static const char *const default_pm[] = {
+"13 9 3 1",
+". c None",
+" c #000000",
+"X c #FFFFFF",
+"X X X X X X X",
+" X X X X X X ",
+"X ......... X",
+" X.........X ",
+"X ......... X",
+" X.........X ",
+"X ......... X",
+" X X X X X X ",
+"X X X X X X X",
+};
+//### actions need to be redefined for S60
+// Shift/Ctrl handling, and final drop status
+static Qt::DropAction global_accepted_action = Qt::MoveAction;
+static Qt::DropActions possible_actions = Qt::IgnoreAction;
+
+
+// static variables in place of a proper cross-process solution
+static QDrag *drag_object;
+static bool qt_symbian_dnd_dragging = false;
+
+
+static Qt::KeyboardModifiers oldstate;
+
+void QDragManager::updatePixmap()
+{
+ QPixmap pm;
+ QPoint pm_hot(default_pm_hotx,default_pm_hoty);
+ if (drag_object) {
+ pm = drag_object->pixmap();
+ if (!pm.isNull())
+ pm_hot = drag_object->hotSpot();
+ }
+ if (pm.isNull()) {
+ if (!defaultPm)
+ defaultPm = new QPixmap(default_pm);
+ pm = *defaultPm;
+ }
+#ifndef QT_NO_CURSOR
+ QCursor cursor(pm, pm_hot.x(), pm_hot.y());
+ overrideCursor = cursor;
+#endif
+}
+
+void QDragManager::timerEvent(QTimerEvent *) { }
+
+void QDragManager::move(const QPoint&) {
+}
+
+void QDragManager::updateCursor()
+{
+#ifndef QT_NO_CURSOR
+ QCursor cursor = willDrop ? overrideCursor : Qt::ForbiddenCursor;
+ if (!restoreCursor) {
+ QApplication::setOverrideCursor(cursor);
+ restoreCursor = true;
+ }
+ else {
+ QApplication::changeOverrideCursor(cursor);
+ }
+#endif
+}
+
+
+bool QDragManager::eventFilter(QObject *o, QEvent *e)
+{
+ if (beingCancelled) {
+ return false;
+ }
+ if (!o->isWidgetType())
+ return false;
+
+ switch(e->type()) {
+ case QEvent::MouseButtonPress:
+ {
+ }
+ case QEvent::MouseMove:
+ {
+ if (!object) { //#### this should not happen
+ qWarning("QDragManager::eventFilter: No object");
+ return true;
+ }
+ QDragManager *manager = QDragManager::self();
+ QMimeData *dropData = manager->object ? manager->dragPrivate()->data : manager->dropData;
+ if (manager->object)
+ possible_actions = manager->dragPrivate()->possible_actions;
+ else
+ possible_actions = Qt::IgnoreAction;
+
+ QMouseEvent *me = (QMouseEvent *)e;
+
+ if (me->buttons()) {
+ Qt::DropAction prevAction = global_accepted_action;
+ QWidget *cw = QApplication::widgetAt(me->globalPos());
+ // map the Coords relative to the window.
+ if (!cw)
+ return true;
+
+ while (cw && !cw->acceptDrops() && !cw->isWindow())
+ cw = cw->parentWidget();
+
+ bool oldWillDrop = willDrop;
+ if (object->target() != cw) {
+ if (object->target()) {
+ QDragLeaveEvent dle;
+ QApplication::sendEvent(object->target(), &dle);
+ willDrop = false;
+ global_accepted_action = Qt::IgnoreAction;
+ if (oldWillDrop != willDrop)
+ updateCursor();
+ object->d_func()->target = 0;
+ }
+ if (cw && cw->acceptDrops()) {
+ object->d_func()->target = cw;
+ QDragEnterEvent dee(cw->mapFromGlobal(me->globalPos()), possible_actions, dropData,
+ me->buttons(), me->modifiers());
+ QApplication::sendEvent(object->target(), &dee);
+ willDrop = dee.isAccepted() && dee.dropAction() != Qt::IgnoreAction;
+ global_accepted_action = willDrop ? dee.dropAction() : Qt::IgnoreAction;
+ if (oldWillDrop != willDrop)
+ updateCursor();
+ }
+ } else if (cw) {
+ QDragMoveEvent dme(cw->mapFromGlobal(me->globalPos()), possible_actions, dropData,
+ me->buttons(), me->modifiers());
+ if (global_accepted_action != Qt::IgnoreAction) {
+ dme.setDropAction(global_accepted_action);
+ dme.accept();
+ }
+ QApplication::sendEvent(cw, &dme);
+ willDrop = dme.isAccepted();
+ global_accepted_action = willDrop ? dme.dropAction() : Qt::IgnoreAction;
+ if (oldWillDrop != willDrop) {
+ updatePixmap();
+ updateCursor();
+ }
+ }
+ if (global_accepted_action != prevAction)
+ emitActionChanged(global_accepted_action);
+ }
+ return true; // Eat all mouse events
+ }
+
+ case QEvent::MouseButtonRelease:
+ {
+ qApp->removeEventFilter(this);
+#ifndef QT_NO_CURSOR
+ if (restoreCursor) {
+ QApplication::restoreOverrideCursor();
+ willDrop = false;
+ restoreCursor = false;
+ }
+#endif
+ if (object && object->target()) {
+
+ QMouseEvent *me = (QMouseEvent *)e;
+
+ QDragManager *manager = QDragManager::self();
+ QMimeData *dropData = manager->object ? manager->dragPrivate()->data : manager->dropData;
+
+ QDropEvent de(object->target()->mapFromGlobal(me->globalPos()), possible_actions, dropData,
+ me->buttons(), me->modifiers());
+ QApplication::sendEvent(object->target(), &de);
+ if (de.isAccepted())
+ global_accepted_action = de.dropAction();
+ else
+ global_accepted_action = Qt::IgnoreAction;
+
+ if (object)
+ object->deleteLater();
+ drag_object = object = 0;
+ }
+ eventLoop->exit();
+ return true; // Eat all mouse events
+ }
+
+ default:
+ break;
+ }
+ return false;
+}
+
+Qt::DropAction QDragManager::drag(QDrag *o)
+{
+ Q_ASSERT(!qt_symbian_dnd_dragging);
+ if (object == o || !o || !o->source())
+ return Qt::IgnoreAction;
+
+ if (object) {
+ cancel();
+ qApp->removeEventFilter(this);
+ beingCancelled = false;
+ }
+
+ object = drag_object = o;
+
+ oldstate = Qt::NoModifier; // #### Should use state that caused the drag
+ willDrop = false;
+ updatePixmap();
+ updateCursor();
+
+#ifndef QT_NO_CURSOR
+ qt_symbian_set_cursor_visible(true); //force cursor on even for touch phone
+#endif
+
+ object->d_func()->target = 0;
+
+ qApp->installEventFilter(this);
+
+ global_accepted_action = defaultAction(dragPrivate()->possible_actions, Qt::NoModifier);
+ qt_symbian_dnd_dragging = true;
+
+ eventLoop = new QEventLoop;
+ // block
+ (void) eventLoop->exec(QEventLoop::AllEvents);
+ delete eventLoop;
+ eventLoop = 0;
+
+#ifndef QT_NO_CURSOR
+ qt_symbian_set_cursor_visible(false);
+
+ overrideCursor = QCursor(); //deref the cursor data
+ qt_symbian_dnd_dragging = false;
+#endif
+
+ return global_accepted_action;
+}
+
+
+void QDragManager::cancel(bool deleteSource)
+{
+ beingCancelled = true;
+
+ if (object->target()) {
+ QDragLeaveEvent dle;
+ QApplication::sendEvent(object->target(), &dle);
+ }
+
+ if (drag_object) {
+ if (deleteSource)
+ object->deleteLater();
+ drag_object = object = 0;
+ }
+
+#ifndef QT_NO_CURSOR
+ if (restoreCursor) {
+ QApplication::restoreOverrideCursor();
+ restoreCursor = false;
+ }
+#endif
+
+ global_accepted_action = Qt::IgnoreAction;
+}
+
+
+void QDragManager::drop()
+{
+#ifndef QT_NO_CURSOR
+ if (restoreCursor) {
+ QApplication::restoreOverrideCursor();
+ restoreCursor = false;
+ }
+#endif
+}
+
+QVariant QDropData::retrieveData_sys(const QString &mimetype, QVariant::Type type) const
+{
+ if (!drag_object)
+ return QVariant();
+ QByteArray data = drag_object->mimeData()->data(mimetype);
+ if (type == QVariant::String)
+ return QString::fromUtf8(data);
+ return data;
+}
+
+bool QDropData::hasFormat_sys(const QString &format) const
+{
+ return formats().contains(format);
+}
+
+QStringList QDropData::formats_sys() const
+{
+ if (drag_object)
+ return drag_object->mimeData()->formats();
+ return QStringList();
+}
+
+QT_END_NAMESPACE
+#endif // QT_NO_DRAGANDDROP
diff --git a/src/widgets/platforms/s60/qeventdispatcher_s60.cpp b/src/widgets/platforms/s60/qeventdispatcher_s60.cpp
new file mode 100644
index 0000000000..3f20c08084
--- /dev/null
+++ b/src/widgets/platforms/s60/qeventdispatcher_s60.cpp
@@ -0,0 +1,196 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qwidget.h>
+
+#include "qeventdispatcher_s60_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QtEikonEnv::QtEikonEnv()
+ : m_lastIterationCount(0)
+ , m_savedStatusCode(KRequestPending)
+ , m_hasAlreadyRun(false)
+{
+}
+
+QtEikonEnv::~QtEikonEnv()
+{
+}
+
+void QtEikonEnv::RunL()
+{
+ QEventDispatcherS60 *dispatcher = qobject_cast<QEventDispatcherS60 *>(QAbstractEventDispatcher::instance());
+ if (!dispatcher) {
+ CEikonEnv::RunL();
+ return;
+ }
+
+ if (m_lastIterationCount != dispatcher->iterationCount()) {
+ m_hasAlreadyRun = false;
+ m_lastIterationCount = dispatcher->iterationCount();
+ }
+
+ if (m_hasAlreadyRun) {
+ // Fool the active scheduler into believing we are still waiting for events.
+ // The window server thinks we are not, however.
+ m_savedStatusCode = iStatus.Int();
+ iStatus = KRequestPending;
+ SetActive();
+ dispatcher->queueDeferredActiveObjectsCompletion();
+ } else {
+ m_hasAlreadyRun = true;
+ CEikonEnv::RunL();
+ }
+}
+
+void QtEikonEnv::DoCancel()
+{
+ complete();
+
+ CEikonEnv::DoCancel();
+}
+
+void QtEikonEnv::complete()
+{
+ if (m_hasAlreadyRun) {
+ if (m_savedStatusCode != KRequestPending) {
+ TRequestStatus *status = &iStatus;
+ QEventDispatcherSymbian::RequestComplete(status, m_savedStatusCode);
+ m_savedStatusCode = KRequestPending;
+ }
+ m_hasAlreadyRun = false;
+ }
+}
+
+QEventDispatcherS60::QEventDispatcherS60(QObject *parent)
+ : QEventDispatcherSymbian(parent),
+ m_noInputEvents(false)
+{
+}
+
+QEventDispatcherS60::~QEventDispatcherS60()
+{
+ for (int c = 0; c < m_deferredInputEvents.size(); ++c) {
+ delete m_deferredInputEvents[c].event;
+ }
+}
+
+bool QEventDispatcherS60::processEvents ( QEventLoop::ProcessEventsFlags flags )
+{
+ bool ret = false;
+
+ QT_TRY {
+ bool oldNoInputEventsValue = m_noInputEvents;
+ if (flags & QEventLoop::ExcludeUserInputEvents) {
+ m_noInputEvents = true;
+ } else {
+ m_noInputEvents = false;
+ ret = sendDeferredInputEvents() || ret;
+ }
+
+ ret = QEventDispatcherSymbian::processEvents(flags) || ret;
+
+ m_noInputEvents = oldNoInputEventsValue;
+ } QT_CATCH (const std::exception& ex) {
+#ifndef QT_NO_EXCEPTIONS
+ CActiveScheduler::Current()->Error(qt_symbian_exception2Error(ex));
+#endif
+ }
+
+ return ret;
+}
+
+bool QEventDispatcherS60::hasPendingEvents()
+{
+ return !m_deferredInputEvents.isEmpty() || QEventDispatcherSymbian::hasPendingEvents();
+}
+
+void QEventDispatcherS60::saveInputEvent(QSymbianControl *control, QWidget *widget, QInputEvent *event)
+{
+ DeferredInputEvent inputEvent = {control, widget, event};
+ m_deferredInputEvents.append(inputEvent);
+ connect(widget, SIGNAL(destroyed(QObject*)), SLOT(removeInputEventsForWidget(QObject*)));
+}
+
+bool QEventDispatcherS60::sendDeferredInputEvents()
+{
+ bool eventsSent = false;
+ while (!m_deferredInputEvents.isEmpty()) {
+ DeferredInputEvent inputEvent = m_deferredInputEvents.takeFirst();
+#ifndef QT_NO_EXCEPTIONS
+ try {
+#endif
+ inputEvent.control->sendInputEvent(inputEvent.widget, inputEvent.event);
+#ifndef QT_NO_EXCEPTIONS
+ } catch (...) {
+ delete inputEvent.event;
+ throw;
+ }
+#endif
+ delete inputEvent.event;
+ eventsSent = true;
+ }
+
+ return eventsSent;
+}
+
+void QEventDispatcherS60::removeInputEventsForWidget(QObject *object)
+{
+ for (int c = 0; c < m_deferredInputEvents.size(); ++c) {
+ if (m_deferredInputEvents[c].widget == object) {
+ delete m_deferredInputEvents[c].event;
+ m_deferredInputEvents.removeAt(c--);
+ }
+ }
+}
+
+// reimpl
+void QEventDispatcherS60::reactivateDeferredActiveObjects()
+{
+ if (S60->qtOwnsS60Environment) {
+ static_cast<QtEikonEnv *>(CCoeEnv::Static())->complete();
+ }
+
+ QEventDispatcherSymbian::reactivateDeferredActiveObjects();
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/s60/qeventdispatcher_s60_p.h b/src/widgets/platforms/s60/qeventdispatcher_s60_p.h
new file mode 100644
index 0000000000..8e644a2d06
--- /dev/null
+++ b/src/widgets/platforms/s60/qeventdispatcher_s60_p.h
@@ -0,0 +1,127 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QEVENTDISPATCHER_S60_P_H
+#define QEVENTDISPATCHER_S60_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qeventdispatcher_symbian_p.h>
+#include "qt_s60_p.h"
+
+#include <eikenv.h>
+
+QT_BEGIN_NAMESPACE
+
+class QEventDispatcherS60;
+
+class QtEikonEnv : public CEikonEnv
+{
+public:
+ QtEikonEnv();
+ ~QtEikonEnv();
+
+ // from CActive.
+ void RunL();
+ void DoCancel();
+
+ void complete();
+
+private:
+ // Workaround for a BC break from S60 3.2 -> 5.0, where the CEikonEnv override was removed.
+ // To avoid linking to that when we build against 3.2, define an empty body here.
+ // Reserved_*() have been verified to be empty in the S60 code.
+ void Reserved_1() {}
+ void Reserved_2() {}
+
+private:
+ int m_lastIterationCount;
+ TInt m_savedStatusCode;
+ bool m_hasAlreadyRun;
+};
+
+class Q_WIDGETS_EXPORT QEventDispatcherS60 : public QEventDispatcherSymbian
+{
+ Q_OBJECT
+
+public:
+ QEventDispatcherS60(QObject *parent = 0);
+ ~QEventDispatcherS60();
+
+ bool processEvents ( QEventLoop::ProcessEventsFlags flags );
+ bool hasPendingEvents();
+
+ bool excludeUserInputEvents() { return m_noInputEvents; }
+
+ void saveInputEvent(QSymbianControl *control, QWidget *widget, QInputEvent *event);
+
+ void reactivateDeferredActiveObjects();
+
+private:
+ bool sendDeferredInputEvents();
+
+private Q_SLOTS:
+ void removeInputEventsForWidget(QObject *object);
+
+private:
+ bool m_noInputEvents;
+
+ struct DeferredInputEvent
+ {
+ QSymbianControl *control;
+ QWidget *widget;
+ QInputEvent *event;
+ };
+ QList<DeferredInputEvent> m_deferredInputEvents;
+};
+
+QT_END_NAMESPACE
+
+#endif // QEVENTDISPATCHER_S60_P_H
diff --git a/src/widgets/platforms/s60/qfont_s60.cpp b/src/widgets/platforms/s60/qfont_s60.cpp
new file mode 100644
index 0000000000..e0f4bad527
--- /dev/null
+++ b/src/widgets/platforms/s60/qfont_s60.cpp
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qfont.h"
+#include "qfont_p.h"
+#include <private/qt_s60_p.h>
+#include <private/qpixmap_s60_p.h>
+#include "qmutex.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifdef QT_NO_FREETYPE
+Q_GLOBAL_STATIC(QMutex, lastResortFamilyMutex);
+#endif // QT_NO_FREETYPE
+
+extern QStringList qt_symbian_fontFamiliesOnFontServer(); // qfontdatabase_s60.cpp
+Q_GLOBAL_STATIC_WITH_INITIALIZER(QStringList, fontFamiliesOnFontServer, {
+ // We are only interested in the initial font families. No Application fonts.
+ // Therefore, we are allowed to cache the list.
+ x->append(qt_symbian_fontFamiliesOnFontServer());
+});
+
+QString QFont::lastResortFont() const
+{
+ // Symbian's font Api does not distinguish between font and family.
+ // Therefore we try to get a "Family" first, then fall back to "Sans".
+ static QString font = lastResortFamily();
+ if (font.isEmpty())
+ font = QLatin1String("Sans");
+ return font;
+}
+
+QString QFont::lastResortFamily() const
+{
+#ifdef QT_NO_FREETYPE
+ QMutexLocker locker(lastResortFamilyMutex());
+ static QString family;
+ if (family.isEmpty()) {
+
+ QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
+
+ CFont *font;
+ const TInt err = S60->screenDevice()->GetNearestFontInTwips(font, TFontSpec());
+ Q_ASSERT(err == KErrNone);
+ const TFontSpec spec = font->FontSpecInTwips();
+ family = QString((const QChar *)spec.iTypeface.iName.Ptr(), spec.iTypeface.iName.Length());
+ S60->screenDevice()->ReleaseFont(font);
+
+ lock.relock();
+ }
+ return family;
+#else // QT_NO_FREETYPE
+ // For the FreeType case we just hard code the face name, since otherwise on
+ // East Asian systems we may get a name for a stroke based (non-ttf) font.
+
+ // TODO: Get the type face name in a proper way
+
+ const bool isJapaneseOrChineseSystem =
+ User::Language() == ELangJapanese || User::Language() == ELangPrcChinese;
+
+ static QString family;
+ if (family.isEmpty()) {
+ QStringList families = qt_symbian_fontFamiliesOnFontServer();
+ const char* const preferredFamilies[] = {"Nokia Sans S60", "Series 60 Sans"};
+ for (int i = 0; i < sizeof preferredFamilies / sizeof preferredFamilies[0]; ++i) {
+ const QString preferredFamily = QLatin1String(preferredFamilies[i]);
+ if (families.contains(preferredFamily)) {
+ family = preferredFamily;
+ break;
+ }
+ }
+ }
+
+ return QLatin1String(isJapaneseOrChineseSystem?"Heisei Kaku Gothic S60":family.toLatin1());
+#endif // QT_NO_FREETYPE
+}
+
+QString QFont::defaultFamily() const
+{
+#ifdef QT_NO_FREETYPE
+ switch(d->request.styleHint) {
+ case QFont::SansSerif: {
+ static const char* const preferredSansSerif[] = {"Nokia Sans S60", "Series 60 Sans"};
+ for (int i = 0; i < sizeof preferredSansSerif / sizeof preferredSansSerif[0]; ++i) {
+ const QString sansSerif = QLatin1String(preferredSansSerif[i]);
+ if (fontFamiliesOnFontServer()->contains(sansSerif))
+ return sansSerif;
+ }
+ }
+ // No break. Intentional fall through.
+ default:
+ return lastResortFamily();
+ }
+#endif // QT_NO_FREETYPE
+ return lastResortFamily();
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/s60/qfontdatabase_s60.cpp b/src/widgets/platforms/s60/qfontdatabase_s60.cpp
new file mode 100644
index 0000000000..cfa405dbc1
--- /dev/null
+++ b/src/widgets/platforms/s60/qfontdatabase_s60.cpp
@@ -0,0 +1,1099 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qapplication_p.h>
+#include "qdir.h"
+#include "qfont_p.h"
+#include "qfontengine_s60_p.h"
+#include "qabstractfileengine.h"
+#include "qdesktopservices.h"
+#include "qtemporaryfile.h"
+#include "qtextcodec.h"
+#include <private/qpixmap_s60_p.h>
+#include <private/qt_s60_p.h>
+#include "qendian.h"
+#include <private/qcore_symbian_p.h>
+#ifdef QT_NO_FREETYPE
+#include <openfont.h>
+#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
+#include <graphics/openfontrasterizer.h> // COpenFontRasterizer has moved to a new header file
+#endif // SYMBIAN_ENABLE_SPLIT_HEADERS
+#endif // QT_NO_FREETYPE
+
+QT_BEGIN_NAMESPACE
+
+QStringList qt_symbian_fontFamiliesOnFontServer() // Also used in qfont_s60.cpp
+{
+ QStringList result;
+ QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
+ const int numTypeFaces = S60->screenDevice()->NumTypefaces();
+ for (int i = 0; i < numTypeFaces; i++) {
+ TTypefaceSupport typefaceSupport;
+ S60->screenDevice()->TypefaceSupport(typefaceSupport, i);
+ const QString familyName((const QChar *)typefaceSupport.iTypeface.iName.Ptr(), typefaceSupport.iTypeface.iName.Length());
+ result.append(familyName);
+ }
+ lock.relock();
+ return result;
+}
+
+QFileInfoList alternativeFilePaths(const QString &path, const QStringList &nameFilters,
+ QDir::Filters filters = QDir::NoFilter, QDir::SortFlags sort = QDir::NoSort,
+ bool uniqueFileNames = true)
+{
+ QFileInfoList result;
+
+ // Prepare a 'soft to hard' drive list: W:, X: ... A:, Z:
+ QStringList driveStrings;
+ foreach (const QFileInfo &drive, QDir::drives())
+ driveStrings.append(drive.absolutePath());
+ driveStrings.sort();
+ const QString zDriveString(QLatin1String("Z:/"));
+ driveStrings.removeAll(zDriveString);
+ driveStrings.prepend(zDriveString);
+
+ QStringList uniqueFileNameList;
+ for (int i = driveStrings.count() - 1; i >= 0; --i) {
+ const QDir dirOnDrive(driveStrings.at(i) + path);
+ const QFileInfoList entriesOnDrive = dirOnDrive.entryInfoList(nameFilters, filters, sort);
+ if (uniqueFileNames) {
+ foreach(const QFileInfo &entry, entriesOnDrive) {
+ if (!uniqueFileNameList.contains(entry.fileName())) {
+ uniqueFileNameList.append(entry.fileName());
+ result.append(entry);
+ }
+ }
+ } else {
+ result.append(entriesOnDrive);
+ }
+ }
+ return result;
+}
+
+#ifdef QT_NO_FREETYPE
+class QSymbianFontDatabaseExtrasImplementation : public QSymbianFontDatabaseExtras
+{
+public:
+ QSymbianFontDatabaseExtrasImplementation();
+ ~QSymbianFontDatabaseExtrasImplementation();
+
+ const QSymbianTypeFaceExtras *extras(const QString &typeface, bool bold, bool italic) const;
+ void removeAppFontData(QFontDatabasePrivate::ApplicationFont *fnt);
+ static inline bool appFontLimitReached();
+ TUid addFontFileToFontStore(const QFileInfo &fontFileInfo);
+ static void clear();
+
+ static inline QString tempAppFontFolder();
+ static const QString appFontMarkerPrefix;
+ static QString appFontMarker(); // 'qaf<shortUid[+shortPid]>'
+
+ struct CFontFromFontStoreReleaser {
+ static inline void cleanup(CFont *font)
+ {
+ if (!font)
+ return;
+ const QSymbianFontDatabaseExtrasImplementation *dbExtras =
+ static_cast<const QSymbianFontDatabaseExtrasImplementation*>(privateDb()->symbianExtras);
+ dbExtras->m_store->ReleaseFont(font);
+ }
+ };
+
+ struct CFontFromScreenDeviceReleaser {
+ static inline void cleanup(CFont *font)
+ {
+ if (!font)
+ return;
+ S60->screenDevice()->ReleaseFont(font);
+ }
+ };
+
+// m_heap, m_store, m_rasterizer and m_extras are used if Symbian
+// does not provide the Font Table API
+ RHeap* m_heap;
+ CFontStore *m_store;
+ COpenFontRasterizer *m_rasterizer;
+ mutable QList<const QSymbianTypeFaceExtras *> m_extras;
+
+ mutable QSet<QString> m_applicationFontFamilies;
+};
+
+const QString QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix =
+ QLatin1String("Q");
+
+inline QString QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder()
+{
+ return QDir::toNativeSeparators(QDir::tempPath()) + QLatin1Char('\\');
+}
+
+QString QSymbianFontDatabaseExtrasImplementation::appFontMarker()
+{
+ static QString result;
+ if (result.isEmpty()) {
+ quint16 id = 0;
+ if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
+ // We are allowed to load app fonts even from previous, crashed runs
+ // of this application, since we can access the font tables.
+ const quint32 uid = RProcess().Type().MostDerived().iUid;
+ id = static_cast<quint16>(uid + (uid >> 16));
+ } else {
+ // If no font table Api is available, we must not even load a font
+ // from a previous (crashed) run of this application. Reason: we
+ // won't get the font tables, they are not in the CFontStore.
+ // So, we use the pid, for more uniqueness.
+ id = static_cast<quint16>(RProcess().Id().Id());
+ }
+ result = appFontMarkerPrefix + QString::fromLatin1("%1").arg(id & 0x7fff, 3, 32, QLatin1Char('0'));
+ Q_ASSERT(appFontMarkerPrefix.length() == 1 && result.length() == 4);
+ }
+ return result;
+}
+
+static inline bool qt_symbian_fontNameHasAppFontMarker(const QString &fontName)
+{
+ const int idLength = 3; // Keep in sync with id length in appFontMarker().
+ const QString &prefix = QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix;
+ if (fontName.length() < prefix.length() + idLength
+ || fontName.mid(fontName.length() - idLength - prefix.length(), prefix.length()) != prefix)
+ return false;
+ // Testing if the the id is base32 data
+ for (int i = fontName.length() - idLength; i < fontName.length(); ++i) {
+ const QChar &c = fontName.at(i);
+ if (!(c >= QLatin1Char('0') && c <= QLatin1Char('9')
+ || c >= QLatin1Char('a') && c <= QLatin1Char('v')))
+ return false;
+ }
+ return true;
+}
+
+// If fontName is an application font of this app, prepend the app font marker
+QString qt_symbian_fontNameWithAppFontMarker(const QString &fontName)
+{
+ QFontDatabasePrivate *db = privateDb();
+ Q_ASSERT(db);
+ const QSymbianFontDatabaseExtrasImplementation *dbExtras =
+ static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
+ return dbExtras->m_applicationFontFamilies.contains(fontName) ?
+ fontName + QSymbianFontDatabaseExtrasImplementation::appFontMarker()
+ : fontName;
+}
+
+static inline QString qt_symbian_appFontNameWithoutMarker(const QString &markedFontName)
+{
+ return markedFontName.left(markedFontName.length()
+ - QSymbianFontDatabaseExtrasImplementation::appFontMarker().length());
+}
+
+QSymbianFontDatabaseExtrasImplementation::QSymbianFontDatabaseExtrasImplementation()
+{
+ if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
+ QStringList filters;
+ filters.append(QLatin1String("*.ttf"));
+ filters.append(QLatin1String("*.ccc"));
+ filters.append(QLatin1String("*.ltt"));
+ const QFileInfoList fontFiles = alternativeFilePaths(QLatin1String("resource\\Fonts"), filters);
+
+ const TInt heapMinLength = 0x1000;
+ const TInt heapMaxLength = qMax(0x20000 * fontFiles.count(), heapMinLength);
+ m_heap = User::ChunkHeap(NULL, heapMinLength, heapMaxLength);
+ QT_TRAP_THROWING(
+ m_store = CFontStore::NewL(m_heap);
+ m_rasterizer = COpenFontRasterizer::NewL(TUid::Uid(0x101F7F5E));
+ CleanupStack::PushL(m_rasterizer);
+ m_store->InstallRasterizerL(m_rasterizer);
+ CleanupStack::Pop(m_rasterizer););
+
+ foreach (const QFileInfo &fontFileInfo, fontFiles)
+ addFontFileToFontStore(fontFileInfo);
+ }
+}
+
+void QSymbianFontDatabaseExtrasImplementation::clear()
+{
+ QFontDatabasePrivate *db = privateDb();
+ if (!db)
+ return;
+ const QSymbianFontDatabaseExtrasImplementation *dbExtras =
+ static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
+ if (!dbExtras)
+ return; // initializeDb() has never been called
+ QSymbianTypeFaceExtrasHash &extrasHash = S60->fontData();
+ if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
+ qDeleteAll(extrasHash);
+ } else {
+ typedef QList<const QSymbianTypeFaceExtras *>::iterator iterator;
+ for (iterator p = dbExtras->m_extras.begin(); p != dbExtras->m_extras.end(); ++p) {
+ dbExtras->m_store->ReleaseFont((*p)->fontOwner());
+ delete *p;
+ }
+ dbExtras->m_extras.clear();
+ }
+ extrasHash.clear();
+}
+
+void qt_cleanup_symbianFontDatabase()
+{
+ static bool cleanupDone = false;
+ if (cleanupDone)
+ return;
+ cleanupDone = true;
+
+ QFontDatabasePrivate *db = privateDb();
+ if (!db)
+ return;
+
+ QSymbianFontDatabaseExtrasImplementation::clear();
+
+ if (!db->applicationFonts.isEmpty()) {
+ QFontDatabase::removeAllApplicationFonts();
+ // We remove the left over temporary font files of Qt application.
+ // Active fonts are undeletable since the font server holds a handle
+ // on them, so we do not need to worry to delete other running
+ // applications' fonts.
+ const QDir dir(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder());
+ const QStringList filter(
+ QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix + QLatin1String("*.ttf"));
+ foreach (const QFileInfo &ttfFile, dir.entryInfoList(filter))
+ QFile(ttfFile.absoluteFilePath()).remove();
+ db->applicationFonts.clear();
+ }
+}
+
+QSymbianFontDatabaseExtrasImplementation::~QSymbianFontDatabaseExtrasImplementation()
+{
+ qt_cleanup_symbianFontDatabase();
+ if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
+ delete m_store;
+ m_heap->Close();
+ }
+}
+
+#ifndef FNTSTORE_H_INLINES_SUPPORT_FMM
+/*
+ Workaround: fntstore.h has an inlined function 'COpenFont* CBitmapFont::OpenFont()'
+ that returns a private data member. The header will change between SDKs. But Qt has
+ to build on any SDK version and run on other versions of Symbian OS.
+ This function performs the needed pointer arithmetic to get the right COpenFont*
+*/
+COpenFont* OpenFontFromBitmapFont(const CBitmapFont* aBitmapFont)
+{
+ const TInt offsetIOpenFont = 92; // '_FOFF(CBitmapFont, iOpenFont)' ..if iOpenFont weren't private
+ const TUint valueIOpenFont = *(TUint*)PtrAdd(aBitmapFont, offsetIOpenFont);
+ return (valueIOpenFont & 1) ?
+ (COpenFont*)PtrAdd(aBitmapFont, valueIOpenFont & ~1) : // New behavior: iOpenFont is offset
+ (COpenFont*)valueIOpenFont; // Old behavior: iOpenFont is pointer
+}
+#endif // FNTSTORE_H_INLINES_SUPPORT_FMM
+
+const QSymbianTypeFaceExtras *QSymbianFontDatabaseExtrasImplementation::extras(const QString &aTypeface,
+ bool bold, bool italic) const
+{
+ QSymbianTypeFaceExtrasHash &extrasHash = S60->fontData();
+ if (extrasHash.isEmpty() && QThread::currentThread() != QApplication::instance()->thread())
+ S60->addThreadLocalReleaseFunc(clear);
+ const QString typeface = qt_symbian_fontNameWithAppFontMarker(aTypeface);
+ const QString searchKey = typeface + QString::number(int(bold)) + QString::number(int(italic));
+ if (!extrasHash.contains(searchKey)) {
+ TFontSpec searchSpec(qt_QString2TPtrC(typeface), 1);
+ if (bold)
+ searchSpec.iFontStyle.SetStrokeWeight(EStrokeWeightBold);
+ if (italic)
+ searchSpec.iFontStyle.SetPosture(EPostureItalic);
+
+ CFont* font = NULL;
+ if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
+ const TInt err = S60->screenDevice()->GetNearestFontToDesignHeightInPixels(font, searchSpec);
+ Q_ASSERT(err == KErrNone && font);
+ QScopedPointer<CFont, CFontFromScreenDeviceReleaser> sFont(font);
+ QSymbianTypeFaceExtras *extras = new QSymbianTypeFaceExtras(font);
+ sFont.take();
+ extrasHash.insert(searchKey, extras);
+ } else {
+ const TInt err = m_store->GetNearestFontToDesignHeightInPixels(font, searchSpec);
+ Q_ASSERT(err == KErrNone && font);
+ const CBitmapFont *bitmapFont = static_cast<CBitmapFont*>(font);
+ COpenFont *openFont =
+#ifdef FNTSTORE_H_INLINES_SUPPORT_FMM
+ bitmapFont->OpenFont();
+#else // FNTSTORE_H_INLINES_SUPPORT_FMM
+ OpenFontFromBitmapFont(bitmapFont);
+#endif // FNTSTORE_H_INLINES_SUPPORT_FMM
+ const TOpenFontFaceAttrib* const attrib = openFont->FaceAttrib();
+ const QString foundKey =
+ QString((const QChar*)attrib->FullName().Ptr(), attrib->FullName().Length());
+ if (!extrasHash.contains(foundKey)) {
+ QScopedPointer<CFont, CFontFromFontStoreReleaser> sFont(font);
+ QSymbianTypeFaceExtras *extras = new QSymbianTypeFaceExtras(font, openFont);
+ sFont.take();
+ m_extras.append(extras);
+ extrasHash.insert(searchKey, extras);
+ extrasHash.insert(foundKey, extras);
+ } else {
+ m_store->ReleaseFont(font);
+ extrasHash.insert(searchKey, extrasHash.value(foundKey));
+ }
+ }
+ }
+ return extrasHash.value(searchKey);
+}
+
+void QSymbianFontDatabaseExtrasImplementation::removeAppFontData(
+ QFontDatabasePrivate::ApplicationFont *fnt)
+{
+ clear();
+ if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()
+ && fnt->fontStoreFontFileUid.iUid != 0)
+ m_store->RemoveFile(fnt->fontStoreFontFileUid);
+ if (!fnt->families.isEmpty())
+ m_applicationFontFamilies.remove(fnt->families.first());
+ if (fnt->screenDeviceFontFileId != 0)
+ S60->screenDevice()->RemoveFile(fnt->screenDeviceFontFileId);
+ QFile::remove(fnt->temporaryFileName);
+ *fnt = QFontDatabasePrivate::ApplicationFont();
+}
+
+bool QSymbianFontDatabaseExtrasImplementation::appFontLimitReached()
+{
+ QFontDatabasePrivate *db = privateDb();
+ if (!db)
+ return false;
+ const int maxAppFonts = 5;
+ int registeredAppFonts = 0;
+ foreach (const QFontDatabasePrivate::ApplicationFont &appFont, db->applicationFonts)
+ if (!appFont.families.isEmpty() && ++registeredAppFonts == maxAppFonts)
+ return true;
+ return false;
+}
+
+TUid QSymbianFontDatabaseExtrasImplementation::addFontFileToFontStore(const QFileInfo &fontFileInfo)
+{
+ Q_ASSERT(!QSymbianTypeFaceExtras::symbianFontTableApiAvailable());
+ const QString fontFile = QDir::toNativeSeparators(fontFileInfo.absoluteFilePath());
+ const TPtrC fontFilePtr(qt_QString2TPtrC(fontFile));
+ TUid fontUid = {0};
+ TRAP_IGNORE(fontUid = m_store->AddFileL(fontFilePtr));
+ return fontUid;
+}
+
+#else // QT_NO_FREETYPE
+class QFontEngineFTS60 : public QFontEngineFT
+{
+public:
+ QFontEngineFTS60(const QFontDef &fd);
+};
+
+QFontEngineFTS60::QFontEngineFTS60(const QFontDef &fd)
+ : QFontEngineFT(fd)
+{
+ default_hint_style = HintFull;
+}
+#endif // QT_NO_FREETYPE
+
+/*
+ QFontEngineS60::pixelsToPoints, QFontEngineS60::pointsToPixels, QFontEngineMultiS60::QFontEngineMultiS60
+ and QFontEngineMultiS60::QFontEngineMultiS60 should be in qfontengine_s60.cpp. But since also the
+ Freetype based font rendering need them, they are here.
+*/
+qreal QFontEngineS60::pixelsToPoints(qreal pixels, Qt::Orientation orientation)
+{
+ CWsScreenDevice* device = S60->screenDevice();
+ return (orientation == Qt::Horizontal?
+ device->HorizontalPixelsToTwips(pixels)
+ :device->VerticalPixelsToTwips(pixels)) / KTwipsPerPoint;
+}
+
+qreal QFontEngineS60::pointsToPixels(qreal points, Qt::Orientation orientation)
+{
+ CWsScreenDevice* device = S60->screenDevice();
+ const int twips = points * KTwipsPerPoint;
+ return orientation == Qt::Horizontal?
+ device->HorizontalTwipsToPixels(twips)
+ :device->VerticalTwipsToPixels(twips);
+}
+
+QFontEngineMultiS60::QFontEngineMultiS60(QFontEngine *first, int script, const QStringList &fallbackFamilies)
+ : QFontEngineMulti(fallbackFamilies.size() + 1)
+ , m_script(script)
+ , m_fallbackFamilies(fallbackFamilies)
+{
+ engines[0] = first;
+ first->ref.ref();
+ fontDef = engines[0]->fontDef;
+}
+
+void QFontEngineMultiS60::loadEngine(int at)
+{
+ Q_ASSERT(at < engines.size());
+ Q_ASSERT(engines.at(at) == 0);
+
+ QFontDef request = fontDef;
+ request.styleStrategy |= QFont::NoFontMerging;
+ request.family = m_fallbackFamilies.at(at-1);
+ engines[at] = QFontDatabase::findFont(m_script,
+ /*fontprivate*/0,
+ request);
+ Q_ASSERT(engines[at]);
+}
+
+#ifdef QT_NO_FREETYPE
+static bool registerScreenDeviceFont(int screenDeviceFontIndex,
+ const QSymbianFontDatabaseExtrasImplementation *dbExtras)
+{
+ TTypefaceSupport typefaceSupport;
+ S60->screenDevice()->TypefaceSupport(typefaceSupport, screenDeviceFontIndex);
+
+ QString familyName((const QChar*)typefaceSupport.iTypeface.iName.Ptr(), typefaceSupport.iTypeface.iName.Length());
+ if (qt_symbian_fontNameHasAppFontMarker(familyName)) {
+ const QString &marker = QSymbianFontDatabaseExtrasImplementation::appFontMarker();
+ if (familyName.endsWith(marker)) {
+ familyName = qt_symbian_appFontNameWithoutMarker(familyName);
+ dbExtras->m_applicationFontFamilies.insert(familyName);
+ } else {
+ return false; // This was somebody else's application font. Skip it.
+ }
+ }
+
+ CFont *font; // We have to get a font instance in order to know all the details
+ TFontSpec fontSpec(typefaceSupport.iTypeface.iName, 11);
+ if (S60->screenDevice()->GetNearestFontInPixels(font, fontSpec) != KErrNone)
+ return false;
+ QScopedPointer<CFont, QSymbianFontDatabaseExtrasImplementation::CFontFromScreenDeviceReleaser> sFont(font);
+ if (font->TypeUid() != KCFbsFontUid)
+ return false;
+ TOpenFontFaceAttrib faceAttrib;
+ const CFbsFont *cfbsFont = static_cast<const CFbsFont *>(font);
+ cfbsFont->GetFaceAttrib(faceAttrib);
+
+ QtFontStyle::Key styleKey;
+ styleKey.style = faceAttrib.IsItalic()?QFont::StyleItalic:QFont::StyleNormal;
+ styleKey.weight = faceAttrib.IsBold()?QFont::Bold:QFont::Normal;
+
+ QtFontFamily *family = privateDb()->family(familyName, true);
+ family->fixedPitch = faceAttrib.IsMonoWidth();
+ QtFontFoundry *foundry = family->foundry(QString(), true);
+ QtFontStyle *style = foundry->style(styleKey, QString(), true);
+ style->smoothScalable = typefaceSupport.iIsScalable;
+ style->pixelSize(0, true);
+
+ const QSymbianTypeFaceExtras *typeFaceExtras =
+ dbExtras->extras(familyName, faceAttrib.IsBold(), faceAttrib.IsItalic());
+ const QByteArray os2Table = typeFaceExtras->getSfntTable(MAKE_TAG('O', 'S', '/', '2'));
+ const unsigned char* data = reinterpret_cast<const unsigned char*>(os2Table.constData());
+ const unsigned char* ulUnicodeRange = data + 42;
+ quint32 unicodeRange[4] = {
+ qFromBigEndian<quint32>(ulUnicodeRange),
+ qFromBigEndian<quint32>(ulUnicodeRange + 4),
+ qFromBigEndian<quint32>(ulUnicodeRange + 8),
+ qFromBigEndian<quint32>(ulUnicodeRange + 12)
+ };
+ const unsigned char* ulCodePageRange = data + 78;
+ quint32 codePageRange[2] = {
+ qFromBigEndian<quint32>(ulCodePageRange),
+ qFromBigEndian<quint32>(ulCodePageRange + 4)
+ };
+ const QList<QFontDatabase::WritingSystem> writingSystems =
+ qt_determine_writing_systems_from_truetype_bits(unicodeRange, codePageRange);
+ foreach (const QFontDatabase::WritingSystem system, writingSystems)
+ family->writingSystems[system] = QtFontFamily::Supported;
+ return true;
+}
+#endif
+
+static void initializeDb()
+{
+ QFontDatabasePrivate *db = privateDb();
+ if(!db || db->count)
+ return;
+
+#ifdef QT_NO_FREETYPE
+ if (!db->symbianExtras)
+ db->symbianExtras = new QSymbianFontDatabaseExtrasImplementation;
+
+ QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
+
+ const int numTypeFaces = S60->screenDevice()->NumTypefaces();
+ const QSymbianFontDatabaseExtrasImplementation *dbExtras =
+ static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
+ for (int i = 0; i < numTypeFaces; i++)
+ registerScreenDeviceFont(i, dbExtras);
+
+ // We have to clear/release all CFonts, here, in case one of the fonts is
+ // an application font of another running Qt app. Otherwise the other Qt app
+ // cannot remove it's application font, anymore -> "Zombie Font".
+ QSymbianFontDatabaseExtrasImplementation::clear();
+
+ lock.relock();
+
+#else // QT_NO_FREETYPE
+ QDir dir(QDesktopServices::storageLocation(QDesktopServices::FontsLocation));
+ dir.setNameFilters(QStringList() << QLatin1String("*.ttf")
+ << QLatin1String("*.ttc") << QLatin1String("*.pfa")
+ << QLatin1String("*.pfb"));
+ for (int i = 0; i < int(dir.count()); ++i) {
+ const QByteArray file = QFile::encodeName(dir.absoluteFilePath(dir[i]));
+ db->addTTFile(file);
+ }
+#endif // QT_NO_FREETYPE
+}
+
+static inline void load(const QString &family = QString(), int script = -1)
+{
+ Q_UNUSED(family)
+ Q_UNUSED(script)
+ initializeDb();
+}
+
+struct OffsetTable {
+ quint32 sfntVersion;
+ quint16 numTables, searchRange, entrySelector, rangeShift;
+};
+
+struct TableRecord {
+ quint32 tag, checkSum, offset, length;
+};
+
+struct NameTableHead {
+ quint16 format, count, stringOffset;
+};
+
+struct NameRecord {
+ quint16 platformID, encodingID, languageID, nameID, length, offset;
+};
+
+static quint32 ttfCalcChecksum(const char *data, quint32 bytesCount)
+{
+ quint32 result = 0;
+ const quint32 *ptr = reinterpret_cast<const quint32*>(data);
+ const quint32 *endPtr =
+ ptr + (bytesCount + sizeof(quint32) - 1) / sizeof(quint32);
+ while (ptr < endPtr) {
+ const quint32 unit32Value = *ptr++;
+ result += qFromBigEndian(unit32Value);
+ }
+ return result;
+}
+
+static inline quint32 toDWordBoundary(quint32 value)
+{
+ return (value + 3) & ~3;
+}
+
+static inline quint32 dWordPadding(quint32 value)
+{
+ return (4 - (value & 3)) & 3;
+}
+
+static inline bool ttfMarkNameTable(QByteArray &table, const QString &marker)
+{
+ const quint32 tableLength = static_cast<quint32>(table.size());
+
+ if (tableLength > 50000 // hard limit
+ || tableLength < sizeof(NameTableHead)) // corrupt name table
+ return false;
+
+ const NameTableHead *head = reinterpret_cast<const NameTableHead*>(table.constData());
+ const quint16 count = qFromBigEndian(head->count);
+ const quint16 stringOffset = qFromBigEndian(head->stringOffset);
+ if (count > 200 // hard limit
+ || stringOffset >= tableLength // corrupt name table
+ || sizeof(NameTableHead) + count * sizeof(NameRecord) >= tableLength) // corrupt name table
+ return false;
+
+ QTextEncoder encoder(QTextCodec::codecForName("UTF-16BE"), QTextCodec::IgnoreHeader);
+ const QByteArray markerUtf16BE = encoder.fromUnicode(marker);
+ const QByteArray markerAscii = marker.toAscii();
+
+ QByteArray markedTable;
+ markedTable.reserve(tableLength + marker.length() * 20); // Original size plus some extra
+ markedTable.append(table, stringOffset);
+ QByteArray markedStrings;
+ quint32 stringDataCount = stringOffset;
+ for (quint16 i = 0; i < count; ++i) {
+ const quint32 nameRecordOffset = sizeof(NameTableHead) + sizeof(NameRecord) * i;
+ NameRecord *nameRecord =
+ reinterpret_cast<NameRecord*>(markedTable.data() + nameRecordOffset);
+ const quint16 nameID = qFromBigEndian(nameRecord->nameID);
+ const quint16 platformID = qFromBigEndian(nameRecord->platformID);
+ const quint16 encodingID = qFromBigEndian(nameRecord->encodingID);
+ const quint16 offset = qFromBigEndian(nameRecord->offset);
+ const quint16 length = qFromBigEndian(nameRecord->length);
+ stringDataCount += length;
+ if (stringDataCount > 80000 // hard limit. String data may be > name table size. Multiple records can reference the same string.
+ || static_cast<quint32>(stringOffset + offset + length) > tableLength) // String outside bounds
+ return false;
+ const bool needsMarker =
+ nameID == 1 || nameID == 3 || nameID == 4 || nameID == 16 || nameID == 21;
+ const bool isUnicode =
+ platformID == 0 || platformID == 3 && encodingID == 1;
+ const QByteArray originalString =
+ QByteArray::fromRawData(table.constData() + stringOffset + offset, length);
+ QByteArray markedString;
+ if (needsMarker) {
+ const int maxBytesLength = (KMaxTypefaceNameLength - marker.length()) * (isUnicode ? 2 : 1);
+ markedString = originalString.left(maxBytesLength) + (isUnicode ? markerUtf16BE : markerAscii);
+ } else {
+ markedString = originalString;
+ }
+ nameRecord->offset = qToBigEndian(static_cast<quint16>(markedStrings.length()));
+ nameRecord->length = qToBigEndian(static_cast<quint16>(markedString.length()));
+ markedStrings.append(markedString);
+ }
+ markedTable.append(markedStrings);
+ table = markedTable;
+ return true;
+}
+
+const quint32 ttfMaxFileSize = 3500000;
+
+static inline bool ttfMarkAppFont(QByteArray &ttf, const QString &marker)
+{
+ const quint32 ttfChecksumNumber = 0xb1b0afba;
+ const quint32 alignment = 4;
+ const quint32 ttfLength = static_cast<quint32>(ttf.size());
+ if (ttfLength > ttfMaxFileSize // hard limit
+ || ttfLength % alignment != 0 // ttf sizes are always factors of 4
+ || ttfLength <= sizeof(OffsetTable) // ttf too short
+ || ttfCalcChecksum(ttf.constData(), ttf.size()) != ttfChecksumNumber) // ttf checksum is invalid
+ return false;
+
+ const OffsetTable *offsetTable = reinterpret_cast<const OffsetTable*>(ttf.constData());
+ const quint16 numTables = qFromBigEndian(offsetTable->numTables);
+ const quint32 recordsLength =
+ toDWordBoundary(sizeof(OffsetTable) + numTables * sizeof(TableRecord));
+ if (numTables > 30 // hard limit
+ || recordsLength + numTables * alignment > ttfLength) // Corrupt ttf. Tables would not fit, even if empty.
+ return false;
+
+ QByteArray markedTtf;
+ markedTtf.reserve(ttfLength + marker.length() * 20); // Original size plus some extra
+ markedTtf.append(ttf.constData(), recordsLength);
+
+ const quint32 ttfCheckSumAdjustmentOffset = 8; // Offset from the start of 'head'
+ int indexOfHeadTable = -1;
+ quint32 ttfDataSize = recordsLength;
+ typedef QPair<quint32, quint32> Range;
+ QList<Range> memoryRanges;
+ memoryRanges.reserve(numTables);
+ for (int i = 0; i < numTables; ++i) {
+ TableRecord *tableRecord =
+ reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + i * sizeof(TableRecord));
+ const quint32 offset = qFromBigEndian(tableRecord->offset);
+ const quint32 length = qFromBigEndian(tableRecord->length);
+ const quint32 lengthAligned = toDWordBoundary(length);
+ ttfDataSize += lengthAligned;
+ if (offset < recordsLength // must not intersect ttf header/records
+ || offset % alignment != 0 // must be aligned
+ || offset > ttfLength - alignment // table out of bounds
+ || offset + lengthAligned > ttfLength // table out of bounds
+ || ttfDataSize > ttfLength) // tables would not fit into the ttf
+ return false;
+
+ foreach (const Range &range, memoryRanges)
+ if (offset < range.first + range.second && offset + lengthAligned > range.first)
+ return false; // Overlaps with another table
+ memoryRanges.append(Range(offset, lengthAligned));
+
+ quint32 checkSum = qFromBigEndian(tableRecord->checkSum);
+ if (tableRecord->tag == qToBigEndian(static_cast<quint32>('head'))) {
+ if (length < ttfCheckSumAdjustmentOffset + sizeof(quint32))
+ return false; // Invalid 'head' table
+ const quint32 *checkSumAdjustmentTag =
+ reinterpret_cast<const quint32*>(ttf.constData() + offset + ttfCheckSumAdjustmentOffset);
+ const quint32 checkSumAdjustment = qFromBigEndian(*checkSumAdjustmentTag);
+ checkSum += checkSumAdjustment;
+ indexOfHeadTable = i; // For the ttf checksum re-calculation, later
+ }
+ if (checkSum != ttfCalcChecksum(ttf.constData() + offset, length))
+ return false; // Table checksum is invalid
+
+ bool updateTableChecksum = false;
+ QByteArray table;
+ if (tableRecord->tag == qToBigEndian(static_cast<quint32>('name'))) {
+ table = QByteArray(ttf.constData() + offset, length);
+ if (!ttfMarkNameTable(table, marker))
+ return false; // Name table was not markable.
+ updateTableChecksum = true;
+ } else {
+ table = QByteArray::fromRawData(ttf.constData() + offset, length);
+ }
+
+ tableRecord->offset = qToBigEndian(markedTtf.size());
+ tableRecord->length = qToBigEndian(table.size());
+ markedTtf.append(table);
+ markedTtf.append(QByteArray(dWordPadding(table.size()), 0)); // 0-padding
+ if (updateTableChecksum) {
+ TableRecord *tableRecord = // Need to recalculate, since markedTtf changed
+ reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + i * sizeof(TableRecord));
+ const quint32 offset = qFromBigEndian(tableRecord->offset);
+ const quint32 length = qFromBigEndian(tableRecord->length);
+ tableRecord->checkSum = qToBigEndian(ttfCalcChecksum(markedTtf.constData() + offset, length));
+ }
+ }
+ if (indexOfHeadTable == -1 // 'head' table is mandatory
+ || ttfDataSize != ttfLength) // We do not allow ttf data "holes". Neither does Symbian.
+ return false;
+ TableRecord *headRecord =
+ reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + indexOfHeadTable * sizeof(TableRecord));
+ quint32 *checkSumAdjustmentTag =
+ reinterpret_cast<quint32*>(markedTtf.data() + qFromBigEndian(headRecord->offset) + ttfCheckSumAdjustmentOffset);
+ *checkSumAdjustmentTag = 0;
+ const quint32 ttfChecksum = ttfCalcChecksum(markedTtf.constData(), markedTtf.count());
+ *checkSumAdjustmentTag = qToBigEndian(ttfChecksumNumber - ttfChecksum);
+ ttf = markedTtf;
+ return true;
+}
+
+static inline bool ttfCanSymbianLoadFont(const QByteArray &data, const QString &fileName)
+{
+ bool result = false;
+ QString ttfFileName;
+ QFile tempFileGuard;
+ QFileInfo info(fileName);
+ if (!data.isEmpty()) {
+ QTemporaryFile tempfile(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder()
+ + QSymbianFontDatabaseExtrasImplementation::appFontMarker()
+ + QLatin1String("XXXXXX.ttf"));
+ if (!tempfile.open() || tempfile.write(data) == -1)
+ return false;
+ ttfFileName = QDir::toNativeSeparators(QFileInfo(tempfile).canonicalFilePath());
+ tempfile.setAutoRemove(false);
+ tempfile.close();
+ tempFileGuard.setFileName(ttfFileName);
+ if (!tempFileGuard.open(QIODevice::ReadOnly))
+ return false;
+ } else if (info.isFile()) {
+ ttfFileName = QDir::toNativeSeparators(info.canonicalFilePath());
+ } else {
+ return false;
+ }
+
+ CFontStore *store = 0;
+ RHeap* heap = User::ChunkHeap(NULL, 0x1000, 0x20000);
+ if (heap) {
+ QT_TRAP_THROWING(
+ CleanupClosePushL(*heap);
+ store = CFontStore::NewL(heap);
+ CleanupStack::PushL(store);
+ COpenFontRasterizer *rasterizer = COpenFontRasterizer::NewL(TUid::Uid(0x101F7F5E));
+ CleanupStack::PushL(rasterizer);
+ store->InstallRasterizerL(rasterizer);
+ CleanupStack::Pop(rasterizer);
+ TUid fontUid = {-1};
+ TRAP_IGNORE(fontUid = store->AddFileL(qt_QString2TPtrC(ttfFileName)));
+ if (fontUid.iUid != -1)
+ result = true;
+ CleanupStack::PopAndDestroy(2, heap); // heap, store
+ );
+ }
+
+ if (tempFileGuard.isOpen())
+ tempFileGuard.remove();
+
+ return result;
+}
+
+static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt)
+{
+ if (QSymbianFontDatabaseExtrasImplementation::appFontLimitReached()
+ || fnt->data.size() > ttfMaxFileSize // hard limit
+ || fnt->data.isEmpty() && (!fnt->fileName.endsWith(QLatin1String(".ttf"), Qt::CaseInsensitive) // Only buffer or .ttf
+ || QFileInfo(fnt->fileName).size() > ttfMaxFileSize)) // hard limit
+ return;
+
+// Using ttfCanSymbianLoadFont() causes crashes on app destruction (Symbian^3|PR1 and lower).
+// Therefore, not using it for now, but eventually in a later version.
+// if (!ttfCanSymbianLoadFont(fnt->data, fnt->fileName))
+// return;
+
+ QFontDatabasePrivate *db = privateDb();
+ if (!db)
+ return;
+
+ if (!db->count)
+ initializeDb();
+
+ QSymbianFontDatabaseExtrasImplementation *dbExtras =
+ static_cast<QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
+ if (!dbExtras)
+ return;
+
+ const QString &marker = QSymbianFontDatabaseExtrasImplementation::appFontMarker();
+
+ // The QTemporaryFile object being used in the following section must be
+ // destructed before letting Symbian load the TTF file. Symbian would not
+ // load it otherwise, because QTemporaryFile will still keep some handle
+ // on it. The scope is used to reduce the life time of the QTemporaryFile.
+ // In order to prevent other processes from modifying the file between the
+ // moment where the QTemporaryFile is destructed and the file is loaded by
+ // Symbian, we have a QFile "tempFileGuard" outside the scope which opens
+ // the file in ReadOnly mode while the QTemporaryFile is still alive.
+ QFile tempFileGuard;
+ {
+ QTemporaryFile tempfile(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder()
+ + marker + QLatin1String("XXXXXX.ttf"));
+ if (!tempfile.open())
+ return;
+ const QString tempFileName = QFileInfo(tempfile).canonicalFilePath();
+ if (fnt->data.isEmpty()) {
+ QFile sourceFile(fnt->fileName);
+ if (!sourceFile.open(QIODevice::ReadOnly))
+ return;
+ fnt->data = sourceFile.readAll();
+ }
+ if (!ttfMarkAppFont(fnt->data, marker) || tempfile.write(fnt->data) == -1)
+ return;
+ tempfile.setAutoRemove(false);
+ tempfile.close(); // Tempfile still keeps a file handle, forbidding write access
+ fnt->data.clear(); // The TTF data was marked and saved. Not needed in memory, anymore.
+ tempFileGuard.setFileName(tempFileName);
+ if (!tempFileGuard.open(QIODevice::ReadOnly))
+ return;
+ fnt->temporaryFileName = tempFileName;
+ }
+
+ const QString fullFileName = QDir::toNativeSeparators(fnt->temporaryFileName);
+ QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
+ const QStringList fontsOnServerBefore = qt_symbian_fontFamiliesOnFontServer();
+ const TInt err =
+ S60->screenDevice()->AddFile(qt_QString2TPtrC(fullFileName), fnt->screenDeviceFontFileId);
+ tempFileGuard.close(); // Did its job
+ const QStringList fontsOnServerAfter = qt_symbian_fontFamiliesOnFontServer();
+ if (err == KErrNone && fontsOnServerBefore.count() < fontsOnServerAfter.count()) { // Added to screen device?
+ int fontOnServerIndex = fontsOnServerAfter.count() - 1;
+ for (int i = 0; i < fontsOnServerBefore.count(); i++) {
+ if (fontsOnServerBefore.at(i) != fontsOnServerAfter.at(i)) {
+ fontOnServerIndex = i;
+ break;
+ }
+ }
+
+ // Must remove all font engines with their CFonts, first.
+ QFontCache::instance()->clear();
+ db->free();
+ QSymbianFontDatabaseExtrasImplementation::clear();
+
+ if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable())
+ fnt->fontStoreFontFileUid = dbExtras->addFontFileToFontStore(QFileInfo(fullFileName));
+
+ const QString &appFontName = fontsOnServerAfter.at(fontOnServerIndex);
+ fnt->families.append(qt_symbian_appFontNameWithoutMarker(appFontName));
+ if (!qt_symbian_fontNameHasAppFontMarker(appFontName)
+ || !registerScreenDeviceFont(fontOnServerIndex, dbExtras))
+ dbExtras->removeAppFontData(fnt);
+ } else {
+ if (fnt->screenDeviceFontFileId > 0)
+ S60->screenDevice()->RemoveFile(fnt->screenDeviceFontFileId); // May still have the file open!
+ QFile::remove(fnt->temporaryFileName);
+ *fnt = QFontDatabasePrivate::ApplicationFont();
+ }
+ lock.relock();
+}
+
+bool QFontDatabase::removeApplicationFont(int handle)
+{
+ QMutexLocker locker(fontDatabaseMutex());
+
+ QFontDatabasePrivate *db = privateDb();
+ if (!db || handle < 0 || handle >= db->applicationFonts.count())
+ return false;
+ QSymbianFontDatabaseExtrasImplementation *dbExtras =
+ static_cast<QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
+ if (!dbExtras)
+ return false;
+
+ QFontDatabasePrivate::ApplicationFont *fnt = &db->applicationFonts[handle];
+ if (fnt->families.isEmpty())
+ return true; // Nothing to remove. Return peacefully.
+
+ // Must remove all font engines with their CFonts, first
+ QFontCache::instance()->clear();
+ db->free();
+ dbExtras->removeAppFontData(fnt);
+
+ db->invalidate(); // This will just emit 'fontDatabaseChanged()'
+ return true;
+}
+
+bool QFontDatabase::removeAllApplicationFonts()
+{
+ QMutexLocker locker(fontDatabaseMutex());
+
+ const int applicationFontsCount = privateDb()->applicationFonts.count();
+ for (int i = 0; i < applicationFontsCount; ++i)
+ if (!removeApplicationFont(i))
+ return false;
+ return true;
+}
+
+bool QFontDatabase::supportsThreadedFontRendering()
+{
+ return QSymbianTypeFaceExtras::symbianFontTableApiAvailable();
+}
+
+static
+QFontDef cleanedFontDef(const QFontDef &req)
+{
+ QFontDef result = req;
+ if (result.pixelSize <= 0) {
+ result.pixelSize = QFontEngineS60::pointsToPixels(qMax(qreal(1.0), result.pointSize));
+ result.pointSize = 0;
+ }
+ return result;
+}
+
+QFontEngine *QFontDatabase::findFont(int script, const QFontPrivate *d, const QFontDef &req)
+{
+ const QFontCache::Key key(cleanedFontDef(req), script);
+
+ if (!privateDb()->count)
+ initializeDb();
+
+ QFontEngine *fe = QFontCache::instance()->findEngine(key);
+ if (!fe) {
+ // Making sure that fe->fontDef.family will be an existing font.
+ initializeDb();
+ QFontDatabasePrivate *db = privateDb();
+ QtFontDesc desc;
+ QList<int> blacklistedFamilies;
+ match(script, key.def, key.def.family, QString(), -1, &desc, blacklistedFamilies);
+ if (!desc.family) // falling back to application font
+ desc.family = db->family(QApplication::font().defaultFamily());
+ Q_ASSERT(desc.family);
+
+ // Making sure that desc.family supports the requested script
+ QtFontDesc mappedDesc;
+ bool supportsScript = false;
+ do {
+ match(script, req, QString(), QString(), -1, &mappedDesc, blacklistedFamilies);
+ if (mappedDesc.family == desc.family) {
+ supportsScript = true;
+ break;
+ }
+ blacklistedFamilies.append(mappedDesc.familyIndex);
+ } while (mappedDesc.family);
+ if (!supportsScript) {
+ blacklistedFamilies.clear();
+ match(script, req, QString(), QString(), -1, &mappedDesc, blacklistedFamilies);
+ if (mappedDesc.family)
+ desc = mappedDesc;
+ }
+
+ const QString fontFamily = desc.family->name;
+ QFontDef request = req;
+ request.family = fontFamily;
+#ifdef QT_NO_FREETYPE
+ const QSymbianFontDatabaseExtrasImplementation *dbExtras =
+ static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
+ const QSymbianTypeFaceExtras *typeFaceExtras =
+ dbExtras->extras(fontFamily, request.weight > QFont::Normal, request.style != QFont::StyleNormal);
+
+ // We need a valid pixelSize, e.g. for lineThickness()
+ if (request.pixelSize < 0)
+ request.pixelSize = request.pointSize * d->dpi / 72;
+
+ fe = new QFontEngineS60(request, typeFaceExtras);
+#else // QT_NO_FREETYPE
+ Q_UNUSED(d)
+ QFontEngine::FaceId faceId;
+ const QtFontFamily * const reqQtFontFamily = db->family(fontFamily);
+ faceId.filename = reqQtFontFamily->fontFilename;
+ faceId.index = reqQtFontFamily->fontFileIndex;
+
+ QFontEngineFTS60 *fte = new QFontEngineFTS60(cleanedFontDef(request));
+ if (fte->init(faceId, true, QFontEngineFT::Format_A8))
+ fe = fte;
+ else
+ delete fte;
+#endif // QT_NO_FREETYPE
+
+ Q_ASSERT(fe);
+ if (script == QUnicodeTables::Common
+ && !(req.styleStrategy & QFont::NoFontMerging)
+ && !fe->symbol) {
+
+ QStringList commonFonts;
+ for (int ws = 1; ws < QFontDatabase::WritingSystemsCount; ++ws) {
+ if (scriptForWritingSystem[ws] != script)
+ continue;
+ for (int i = 0; i < db->count; ++i) {
+ if (db->families[i]->writingSystems[ws] & QtFontFamily::Supported)
+ commonFonts.append(db->families[i]->name);
+ }
+ }
+
+ // Hack: Prioritize .ccc fonts
+ const QString niceEastAsianFont(QLatin1String("Sans MT 936_S60"));
+ if (commonFonts.removeAll(niceEastAsianFont) > 0)
+ commonFonts.prepend(niceEastAsianFont);
+
+ fe = new QFontEngineMultiS60(fe, script, commonFonts);
+ }
+ }
+ fe->ref.ref();
+ QFontCache::instance()->insertEngine(key, fe);
+ return fe;
+}
+
+void QFontDatabase::load(const QFontPrivate *d, int script)
+{
+ QFontEngine *fe = 0;
+ QFontDef req = d->request;
+
+ if (!d->engineData) {
+ const QFontCache::Key key(cleanedFontDef(req), script);
+ getEngineData(d, key);
+ }
+
+ // the cached engineData could have already loaded the engine we want
+ if (d->engineData->engines[script])
+ fe = d->engineData->engines[script];
+
+ if (!fe) {
+ if (qt_enable_test_font && req.family == QLatin1String("__Qt__Box__Engine__")) {
+ fe = new QTestFontEngine(req.pixelSize);
+ fe->fontDef = req;
+ } else {
+ fe = findFont(script, d, req);
+ }
+ d->engineData->engines[script] = fe;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/s60/qfontengine_s60.cpp b/src/widgets/platforms/s60/qfontengine_s60.cpp
new file mode 100644
index 0000000000..b4de066306
--- /dev/null
+++ b/src/widgets/platforms/s60/qfontengine_s60.cpp
@@ -0,0 +1,566 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qfontengine_s60_p.h"
+#include "qtextengine_p.h"
+#include "qendian.h"
+#include "qglobal.h"
+#include <private/qapplication_p.h>
+#include "qimage.h"
+#include <private/qt_s60_p.h>
+#include <private/qpixmap_s60_p.h>
+
+#include <e32base.h>
+#include <e32std.h>
+#include <eikenv.h>
+#include <gdi.h>
+#if defined(Q_SYMBIAN_HAS_GLYPHOUTLINE_API)
+#include <graphics/gdi/gdiplatapi.h>
+#endif // Q_SYMBIAN_HAS_GLYPHOUTLINE_API
+
+// Replication of TGetFontTableParam & friends.
+// There is unfortunately no compile time flag like SYMBIAN_FONT_TABLE_API
+// that would help us to only replicate these things for Symbian versions
+// that do not yet have the font table Api. Symbian's public SDK does
+// generally not define any usable macros.
+class QSymbianTGetFontTableParam
+{
+public:
+ TUint32 iTag;
+ TAny *iContent;
+ TInt iLength;
+};
+const TUid QSymbianKFontGetFontTable = {0x102872C1};
+const TUid QSymbianKFontReleaseFontTable = {0x2002AC24};
+
+QT_BEGIN_NAMESPACE
+
+QSymbianTypeFaceExtras::QSymbianTypeFaceExtras(CFont* cFont, COpenFont *openFont)
+ : m_cFont(cFont)
+ , m_symbolCMap(false)
+ , m_openFont(openFont)
+{
+ if (!symbianFontTableApiAvailable()) {
+ TAny *trueTypeExtension = NULL;
+ m_openFont->ExtendedInterface(KUidOpenFontTrueTypeExtension, trueTypeExtension);
+ m_trueTypeExtension = static_cast<MOpenFontTrueTypeExtension*>(trueTypeExtension);
+ Q_ASSERT(m_trueTypeExtension);
+ }
+}
+
+QSymbianTypeFaceExtras::~QSymbianTypeFaceExtras()
+{
+ if (symbianFontTableApiAvailable())
+ S60->screenDevice()->ReleaseFont(m_cFont);
+}
+
+QByteArray QSymbianTypeFaceExtras::getSfntTable(uint tag) const
+{
+ if (symbianFontTableApiAvailable()) {
+ QSymbianTGetFontTableParam fontTableParams = { tag, 0, 0 };
+ if (m_cFont->ExtendedFunction(QSymbianKFontGetFontTable, &fontTableParams) == KErrNone) {
+ const char* const fontTableContent =
+ static_cast<const char *>(fontTableParams.iContent);
+ const QByteArray fontTable(fontTableContent, fontTableParams.iLength);
+ m_cFont->ExtendedFunction(QSymbianKFontReleaseFontTable, &fontTableParams);
+ return fontTable;
+ }
+ return QByteArray();
+ } else {
+ Q_ASSERT(m_trueTypeExtension->HasTrueTypeTable(tag));
+ TInt error = KErrNone;
+ TInt tableByteLength = 0;
+ TAny *table = m_trueTypeExtension->GetTrueTypeTable(error, tag, &tableByteLength);
+ Q_CHECK_PTR(table);
+ const QByteArray result(static_cast<const char*>(table), tableByteLength);
+ m_trueTypeExtension->ReleaseTrueTypeTable(table);
+ return result;
+ }
+}
+
+bool QSymbianTypeFaceExtras::getSfntTableData(uint tag, uchar *buffer, uint *length) const
+{
+ bool result = true;
+ if (symbianFontTableApiAvailable()) {
+ QSymbianTGetFontTableParam fontTableParams = { tag, 0, 0 };
+ if (m_cFont->ExtendedFunction(QSymbianKFontGetFontTable, &fontTableParams) == KErrNone) {
+ if (*length > 0 && *length < fontTableParams.iLength) {
+ result = false; // Caller did not allocate enough memory
+ } else {
+ *length = fontTableParams.iLength;
+ if (buffer)
+ memcpy(buffer, fontTableParams.iContent, fontTableParams.iLength);
+ }
+ m_cFont->ExtendedFunction(QSymbianKFontReleaseFontTable, &fontTableParams);
+ } else {
+ result = false;
+ }
+ } else {
+ if (!m_trueTypeExtension->HasTrueTypeTable(tag))
+ return false;
+
+ TInt error = KErrNone;
+ TInt tableByteLength;
+ TAny *table = m_trueTypeExtension->GetTrueTypeTable(error, tag, &tableByteLength);
+ Q_CHECK_PTR(table);
+
+ if (error != KErrNone) {
+ return false;
+ } else if (*length > 0 && *length < tableByteLength) {
+ result = false; // Caller did not allocate enough memory
+ } else {
+ *length = tableByteLength;
+ if (buffer)
+ memcpy(buffer, table, tableByteLength);
+ }
+
+ m_trueTypeExtension->ReleaseTrueTypeTable(table);
+ }
+ return result;
+}
+
+const uchar *QSymbianTypeFaceExtras::cmap() const
+{
+ if (m_cmapTable.isNull()) {
+ const QByteArray cmapTable = getSfntTable(MAKE_TAG('c', 'm', 'a', 'p'));
+ int size = 0;
+ const uchar *cmap = QFontEngine::getCMap(reinterpret_cast<const uchar *>
+ (cmapTable.constData()), cmapTable.size(), &m_symbolCMap, &size);
+ m_cmapTable = QByteArray(reinterpret_cast<const char *>(cmap), size);
+ }
+ return reinterpret_cast<const uchar *>(m_cmapTable.constData());
+}
+
+bool QSymbianTypeFaceExtras::isSymbolCMap() const
+{
+ return m_symbolCMap;
+}
+
+CFont *QSymbianTypeFaceExtras::fontOwner() const
+{
+ return m_cFont;
+}
+
+QFixed QSymbianTypeFaceExtras::unitsPerEm() const
+{
+ if (m_unitsPerEm.value() != 0)
+ return m_unitsPerEm;
+ const QByteArray head = getSfntTable(MAKE_TAG('h', 'e', 'a', 'd'));
+ const int unitsPerEmOffset = 18;
+ if (head.size() > unitsPerEmOffset + sizeof(quint16)) {
+ const uchar* tableData = reinterpret_cast<const uchar*>(head.constData());
+ const uchar* unitsPerEm = tableData + unitsPerEmOffset;
+ m_unitsPerEm = qFromBigEndian<quint16>(unitsPerEm);
+ } else {
+ // Bitmap font? Corrupt font?
+ // We return -1 and let the QFontEngineS60 return the pixel size.
+ m_unitsPerEm = -1;
+ }
+ return m_unitsPerEm;
+}
+
+bool QSymbianTypeFaceExtras::symbianFontTableApiAvailable()
+{
+ enum FontTableApiAvailability {
+ Unknown,
+ Available,
+ Unavailable
+ };
+ static FontTableApiAvailability availability =
+ QSysInfo::symbianVersion() < QSysInfo::SV_SF_3 ?
+ Unavailable : Unknown;
+ if (availability == Unknown) {
+ // Actually, we should ask CFeatureDiscovery::IsFeatureSupportedL()
+ // with FfFontTable here. But since at the time of writing, the
+ // FfFontTable flag check either gave false positives or false
+ // negatives. Here comes an implicit check via CFont::ExtendedFunction.
+ QSymbianTGetFontTableParam fontTableParams = {
+ MAKE_TAG('O', 'S', '/', '2'), 0, 0 };
+ QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
+ CFont *font;
+ const TInt getFontErr = S60->screenDevice()->GetNearestFontInTwips(font, TFontSpec());
+ Q_ASSERT(getFontErr == KErrNone);
+ if (font->ExtendedFunction(QSymbianKFontGetFontTable, &fontTableParams) == KErrNone) {
+ font->ExtendedFunction(QSymbianKFontReleaseFontTable, &fontTableParams);
+ availability = Available;
+ } else {
+ availability = Unavailable;
+ }
+ S60->screenDevice()->ReleaseFont(font);
+ lock.relock();
+ }
+ return availability == Available;
+}
+
+// duplicated from qfontengine_xyz.cpp
+static inline unsigned int getChar(const QChar *str, int &i, const int len)
+{
+ uint ucs4 = str[i].unicode();
+ if (str[i].isHighSurrogate() && i < len-1 && str[i+1].isLowSurrogate()) {
+ ++i;
+ ucs4 = QChar::surrogateToUcs4(ucs4, str[i].unicode());
+ }
+ return ucs4;
+}
+
+extern QString qt_symbian_fontNameWithAppFontMarker(const QString &fontName); // qfontdatabase_s60.cpp
+
+CFont *QFontEngineS60::fontWithSize(qreal size) const
+{
+ CFont *result = 0;
+ const QString family = qt_symbian_fontNameWithAppFontMarker(QFontEngine::fontDef.family);
+ TFontSpec fontSpec(qt_QString2TPtrC(family), TInt(size));
+ fontSpec.iFontStyle.SetBitmapType(EAntiAliasedGlyphBitmap);
+ fontSpec.iFontStyle.SetPosture(QFontEngine::fontDef.style == QFont::StyleNormal?EPostureUpright:EPostureItalic);
+ fontSpec.iFontStyle.SetStrokeWeight(QFontEngine::fontDef.weight > QFont::Normal?EStrokeWeightBold:EStrokeWeightNormal);
+ const TInt errorCode = S60->screenDevice()->GetNearestFontToDesignHeightInPixels(result, fontSpec);
+ Q_ASSERT(result && (errorCode == 0));
+ return result;
+}
+
+void QFontEngineS60::setFontScale(qreal scale)
+{
+ if (qFuzzyCompare(scale, qreal(1))) {
+ if (!m_originalFont)
+ m_originalFont = fontWithSize(m_originalFontSizeInPixels);
+ m_activeFont = m_originalFont;
+ } else {
+ const qreal scaledFontSizeInPixels = m_originalFontSizeInPixels * scale;
+ if (!m_scaledFont ||
+ (TInt(scaledFontSizeInPixels) != TInt(m_scaledFontSizeInPixels))) {
+ releaseFont(m_scaledFont);
+ m_scaledFontSizeInPixels = scaledFontSizeInPixels;
+ m_scaledFont = fontWithSize(m_scaledFontSizeInPixels);
+ }
+ m_activeFont = m_scaledFont;
+ }
+}
+
+void QFontEngineS60::releaseFont(CFont *&font)
+{
+ if (font) {
+ S60->screenDevice()->ReleaseFont(font);
+ font = 0;
+ }
+}
+
+QFontEngineS60::QFontEngineS60(const QFontDef &request, const QSymbianTypeFaceExtras *extras)
+ : m_extras(extras)
+ , m_originalFont(0)
+ , m_originalFontSizeInPixels((request.pixelSize >= 0)?
+ request.pixelSize:pointsToPixels(request.pointSize))
+ , m_scaledFont(0)
+ , m_scaledFontSizeInPixels(0)
+ , m_activeFont(0)
+{
+ QFontEngine::fontDef = request;
+ setFontScale(1.0);
+ cache_cost = sizeof(QFontEngineS60);
+}
+
+QFontEngineS60::~QFontEngineS60()
+{
+ releaseFont(m_originalFont);
+ releaseFont(m_scaledFont);
+}
+
+QFixed QFontEngineS60::emSquareSize() const
+{
+ const QFixed unitsPerEm = m_extras->unitsPerEm();
+ return unitsPerEm.toInt() == -1 ?
+ QFixed::fromReal(m_originalFontSizeInPixels) : unitsPerEm;
+}
+
+bool QFontEngineS60::stringToCMap(const QChar *characters, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const
+{
+ if (*nglyphs < len) {
+ *nglyphs = len;
+ return false;
+ }
+
+ HB_Glyph *g = glyphs->glyphs;
+ const unsigned char* cmap = m_extras->cmap();
+ const bool isRtl = (flags & QTextEngine::RightToLeft);
+ for (int i = 0; i < len; ++i) {
+ const unsigned int uc = getChar(characters, i, len);
+ *g++ = QFontEngine::getTrueTypeGlyphIndex(cmap,
+ (isRtl && !m_extras->isSymbolCMap()) ? QChar::mirroredChar(uc) : uc);
+ }
+
+ glyphs->numGlyphs = g - glyphs->glyphs;
+ *nglyphs = glyphs->numGlyphs;
+
+ if (flags & QTextEngine::GlyphIndicesOnly)
+ return true;
+
+ recalcAdvances(glyphs, flags);
+ return true;
+}
+
+void QFontEngineS60::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const
+{
+ Q_UNUSED(flags);
+ TOpenFontCharMetrics metrics;
+ const TUint8 *glyphBitmapBytes;
+ TSize glyphBitmapSize;
+ for (int i = 0; i < glyphs->numGlyphs; i++) {
+ getCharacterData(glyphs->glyphs[i], metrics, glyphBitmapBytes, glyphBitmapSize);
+ glyphs->advances_x[i] = metrics.HorizAdvance();
+ glyphs->advances_y[i] = 0;
+ }
+}
+
+#ifdef Q_SYMBIAN_HAS_GLYPHOUTLINE_API
+static bool parseGlyphPathData(const char *dataStr, const char *dataEnd, QPainterPath &path,
+ qreal fontPixelSize, const QPointF &offset, bool hinted);
+#endif //Q_SYMBIAN_HAS_GLYPHOUTLINE_API
+
+void QFontEngineS60::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions,
+ int nglyphs, QPainterPath *path,
+ QTextItem::RenderFlags flags)
+{
+#ifdef Q_SYMBIAN_HAS_GLYPHOUTLINE_API
+ Q_UNUSED(flags)
+ RGlyphOutlineIterator iterator;
+ const TInt error = iterator.Open(*m_activeFont, glyphs, nglyphs);
+ if (KErrNone != error)
+ return;
+ const qreal fontSizeInPixels = qreal(m_activeFont->HeightInPixels());
+ int count = 0;
+ do {
+ const TUint8* outlineUint8 = iterator.Outline();
+ const char* const outlineChar = reinterpret_cast<const char*>(outlineUint8);
+ const char* const outlineEnd = outlineChar + iterator.OutlineLength();
+ parseGlyphPathData(outlineChar, outlineEnd, *path, fontSizeInPixels,
+ positions[count++].toPointF(), false);
+ } while(KErrNone == iterator.Next() && count <= nglyphs);
+ iterator.Close();
+#else // Q_SYMBIAN_HAS_GLYPHOUTLINE_API
+ QFontEngine::addGlyphsToPath(glyphs, positions, nglyphs, path, flags);
+#endif //Q_SYMBIAN_HAS_GLYPHOUTLINE_API
+}
+
+QImage QFontEngineS60::alphaMapForGlyph(glyph_t glyph)
+{
+ // Note: On some Symbian versions (apparently <= Symbian^1), this
+ // function will return gray values 0x00, 0x10 ... 0xe0, 0xf0 due
+ // to a bug. The glyphs are nowhere perfectly opaque.
+ // This has been fixed for Symbian^3.
+
+ TOpenFontCharMetrics metrics;
+ const TUint8 *glyphBitmapBytes;
+ TSize glyphBitmapSize;
+ getCharacterData(glyph, metrics, glyphBitmapBytes, glyphBitmapSize);
+ QImage result(glyphBitmapBytes, glyphBitmapSize.iWidth, glyphBitmapSize.iHeight, glyphBitmapSize.iWidth, QImage::Format_Indexed8);
+ result.setColorTable(grayPalette());
+ return result;
+}
+
+glyph_metrics_t QFontEngineS60::boundingBox(const QGlyphLayout &glyphs)
+{
+ if (glyphs.numGlyphs == 0)
+ return glyph_metrics_t();
+
+ QFixed w = 0;
+ for (int i = 0; i < glyphs.numGlyphs; ++i)
+ w += glyphs.effectiveAdvance(i);
+
+ return glyph_metrics_t(0, -ascent(), w - lastRightBearing(glyphs), ascent()+descent()+1, w, 0);
+}
+
+glyph_metrics_t QFontEngineS60::boundingBox_const(glyph_t glyph) const
+{
+ TOpenFontCharMetrics metrics;
+ const TUint8 *glyphBitmapBytes;
+ TSize glyphBitmapSize;
+ getCharacterData(glyph, metrics, glyphBitmapBytes, glyphBitmapSize);
+ const glyph_metrics_t result(
+ metrics.HorizBearingX(),
+ -metrics.HorizBearingY(),
+ metrics.Width(),
+ metrics.Height(),
+ metrics.HorizAdvance(),
+ 0
+ );
+ return result;
+}
+
+glyph_metrics_t QFontEngineS60::boundingBox(glyph_t glyph)
+{
+ return boundingBox_const(glyph);
+}
+
+QFixed QFontEngineS60::ascent() const
+{
+ // Workaround for QTBUG-8013
+ // Stroke based fonts may return an incorrect FontMaxAscent of 0.
+ const QFixed ascent = m_originalFont->FontMaxAscent();
+ return (ascent > 0) ? ascent : QFixed::fromReal(m_originalFontSizeInPixels) - descent();
+}
+
+QFixed QFontEngineS60::descent() const
+{
+ return m_originalFont->FontMaxDescent();
+}
+
+QFixed QFontEngineS60::leading() const
+{
+ return 0;
+}
+
+qreal QFontEngineS60::maxCharWidth() const
+{
+ return m_originalFont->MaxCharWidthInPixels();
+}
+
+const char *QFontEngineS60::name() const
+{
+ return "QFontEngineS60";
+}
+
+bool QFontEngineS60::canRender(const QChar *string, int len)
+{
+ const unsigned char *cmap = m_extras->cmap();
+ for (int i = 0; i < len; ++i) {
+ const unsigned int uc = getChar(string, i, len);
+ if (QFontEngine::getTrueTypeGlyphIndex(cmap, uc) == 0)
+ return false;
+ }
+ return true;
+}
+
+QByteArray QFontEngineS60::getSfntTable(uint tag) const
+{
+ return m_extras->getSfntTable(tag);
+}
+
+bool QFontEngineS60::getSfntTableData(uint tag, uchar *buffer, uint *length) const
+{
+ return m_extras->getSfntTableData(tag, buffer, length);
+}
+
+QFontEngine::Type QFontEngineS60::type() const
+{
+ return QFontEngine::S60FontEngine;
+}
+
+void QFontEngineS60::getCharacterData(glyph_t glyph, TOpenFontCharMetrics& metrics, const TUint8*& bitmap, TSize& bitmapSize) const
+{
+ // Setting the most significant bit tells GetCharacterData
+ // that 'code' is a Glyph ID, rather than a UTF-16 value
+ const TUint specialCode = (TUint)glyph | 0x80000000;
+
+ const CFont::TCharacterDataAvailability availability =
+ m_activeFont->GetCharacterData(specialCode, metrics, bitmap, bitmapSize);
+ const glyph_t fallbackGlyph = '?';
+ if (availability != CFont::EAllCharacterData) {
+ const CFont::TCharacterDataAvailability fallbackAvailability =
+ m_activeFont->GetCharacterData(fallbackGlyph, metrics, bitmap, bitmapSize);
+ Q_ASSERT(fallbackAvailability == CFont::EAllCharacterData);
+ }
+}
+
+#ifdef Q_SYMBIAN_HAS_GLYPHOUTLINE_API
+static inline void skipSpacesAndComma(const char* &str, const char* const strEnd)
+{
+ while (str <= strEnd && (*str == ' ' || *str == ','))
+ ++str;
+}
+
+static bool parseGlyphPathData(const char *svgPath, const char *svgPathEnd, QPainterPath &path,
+ qreal fontPixelSize, const QPointF &offset, bool hinted)
+{
+ Q_UNUSED(hinted)
+ QPointF p1, p2, firstSubPathPoint;
+ qreal *elementValues[] =
+ {&p1.rx(), &p1.ry(), &p2.rx(), &p2.ry()};
+ const int unitsPerEm = 2048; // See: http://en.wikipedia.org/wiki/Em_%28typography%29
+ const qreal resizeFactor = fontPixelSize / unitsPerEm;
+
+ while (svgPath < svgPathEnd) {
+ skipSpacesAndComma(svgPath, svgPathEnd);
+ const char pathElem = *svgPath++;
+ skipSpacesAndComma(svgPath, svgPathEnd);
+
+ if (pathElem != 'Z') {
+ char *endStr = 0;
+ int elementValuesCount = 0;
+ for (int i = 0; i < 4; ++i) { // 4 = size of elementValues[]
+ qreal coordinateValue = strtod(svgPath, &endStr);
+ if (svgPath == endStr)
+ break;
+ if (i % 2) // Flip vertically
+ coordinateValue = -coordinateValue;
+ *elementValues[i] = coordinateValue * resizeFactor;
+ elementValuesCount++;
+ svgPath = endStr;
+ skipSpacesAndComma(svgPath, svgPathEnd);
+ }
+ p1 += offset;
+ if (elementValuesCount == 2)
+ p2 = firstSubPathPoint;
+ else
+ p2 += offset;
+ }
+
+ switch (pathElem) {
+ case 'M':
+ firstSubPathPoint = p1;
+ path.moveTo(p1);
+ break;
+ case 'Z':
+ path.closeSubpath();
+ break;
+ case 'L':
+ path.lineTo(p1);
+ break;
+ case 'Q':
+ path.quadTo(p1, p2);
+ break;
+ default:
+ return false;
+ }
+ }
+ return true;
+}
+#endif // Q_SYMBIAN_HAS_GLYPHOUTLINE_API
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/s60/qfontengine_s60_p.h b/src/widgets/platforms/s60/qfontengine_s60_p.h
new file mode 100644
index 0000000000..c4fa9d16fe
--- /dev/null
+++ b/src/widgets/platforms/s60/qfontengine_s60_p.h
@@ -0,0 +1,167 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFONTENGINE_S60_P_H
+#define QFONTENGINE_S60_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qconfig.h"
+#include <private/qfontengine_p.h>
+#include "qsize.h"
+#include <openfont.h>
+
+// The glyph outline code is intentionally disabled. It will be reactivated as
+// soon as the glyph outline API is backported from Symbian(^4) to Symbian(^3).
+#if 0
+#define Q_SYMBIAN_HAS_GLYPHOUTLINE_API
+#endif
+
+class CFont;
+
+QT_BEGIN_NAMESPACE
+
+// ..gives us access to truetype tables
+class QSymbianTypeFaceExtras
+{
+public:
+ QSymbianTypeFaceExtras(CFont* cFont, COpenFont *openFont = 0);
+ ~QSymbianTypeFaceExtras();
+
+ QByteArray getSfntTable(uint tag) const;
+ bool getSfntTableData(uint tag, uchar *buffer, uint *length) const;
+ const uchar *cmap() const;
+ CFont *fontOwner() const;
+ bool isSymbolCMap() const;
+ QFixed unitsPerEm() const;
+ static bool symbianFontTableApiAvailable();
+
+private:
+ CFont* m_cFont;
+ mutable bool m_symbolCMap;
+ mutable QByteArray m_cmapTable;
+ mutable QFixed m_unitsPerEm;
+
+ // m_openFont and m_openFont are used if Symbian does not provide
+ // the Font Table API
+ COpenFont *m_openFont;
+ mutable MOpenFontTrueTypeExtension *m_trueTypeExtension;
+};
+
+class QFontEngineS60 : public QFontEngine
+{
+public:
+ QFontEngineS60(const QFontDef &fontDef, const QSymbianTypeFaceExtras *extras);
+ ~QFontEngineS60();
+
+ QFixed emSquareSize() const;
+ bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const;
+ void recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const;
+
+ void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs,
+ QPainterPath *path, QTextItem::RenderFlags flags);
+
+ QImage alphaMapForGlyph(glyph_t glyph);
+
+ glyph_metrics_t boundingBox(const QGlyphLayout &glyphs);
+ glyph_metrics_t boundingBox_const(glyph_t glyph) const; // Const correctnes quirk.
+ glyph_metrics_t boundingBox(glyph_t glyph);
+
+ QFixed ascent() const;
+ QFixed descent() const;
+ QFixed leading() const;
+ qreal maxCharWidth() const;
+ qreal minLeftBearing() const { return 0; }
+ qreal minRightBearing() const { return 0; }
+
+ QByteArray getSfntTable(uint tag) const;
+ bool getSfntTableData(uint tag, uchar *buffer, uint *length) const;
+
+ static qreal pixelsToPoints(qreal pixels, Qt::Orientation orientation = Qt::Horizontal);
+ static qreal pointsToPixels(qreal points, Qt::Orientation orientation = Qt::Horizontal);
+
+ const char *name() const;
+
+ bool canRender(const QChar *string, int len);
+
+ Type type() const;
+
+ void getCharacterData(glyph_t glyph, TOpenFontCharMetrics& metrics, const TUint8*& bitmap, TSize& bitmapSize) const;
+ void setFontScale(qreal scale);
+
+private:
+ friend class QFontPrivate;
+ friend class QSymbianVGFontGlyphCache;
+
+ QFixed glyphAdvance(HB_Glyph glyph) const;
+ CFont *fontWithSize(qreal size) const;
+ static void releaseFont(CFont *&font);
+
+ const QSymbianTypeFaceExtras *m_extras;
+ CFont* m_originalFont;
+ const qreal m_originalFontSizeInPixels;
+ CFont* m_scaledFont;
+ qreal m_scaledFontSizeInPixels;
+ CFont* m_activeFont;
+};
+
+class QFontEngineMultiS60 : public QFontEngineMulti
+{
+public:
+ QFontEngineMultiS60(QFontEngine *first, int script, const QStringList &fallbackFamilies);
+ void loadEngine(int at);
+
+ int m_script;
+ QStringList m_fallbackFamilies;
+};
+
+QT_END_NAMESPACE
+
+#endif // QFONTENGINE_S60_P_H
diff --git a/src/widgets/platforms/s60/qkeymapper_s60.cpp b/src/widgets/platforms/s60/qkeymapper_s60.cpp
new file mode 100644
index 0000000000..1113b77875
--- /dev/null
+++ b/src/widgets/platforms/s60/qkeymapper_s60.cpp
@@ -0,0 +1,258 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qkeymapper_p.h"
+#include <private/qcore_symbian_p.h>
+#include <e32keys.h>
+#include <e32cmn.h>
+#include <centralrepository.h>
+#include <biditext.h>
+
+QT_BEGIN_NAMESPACE
+
+QKeyMapperPrivate::QKeyMapperPrivate()
+{
+}
+
+QKeyMapperPrivate::~QKeyMapperPrivate()
+{
+}
+
+QList<int> QKeyMapperPrivate::possibleKeys(QKeyEvent * /* e */)
+{
+ QList<int> result;
+ return result;
+}
+
+void QKeyMapperPrivate::clearMappings()
+{
+ // stub
+}
+
+QString QKeyMapperPrivate::translateKeyEvent(int keySym, Qt::KeyboardModifiers /* modifiers */)
+{
+ if (keySym >= Qt::Key_Escape) {
+ switch (keySym) {
+ case Qt::Key_Tab:
+ return QString(QChar('\t'));
+ case Qt::Key_Return: // fall through
+ case Qt::Key_Enter:
+ return QString(QChar('\r'));
+ default:
+ return QString();
+ }
+ }
+
+ // Symbian doesn't actually use modifiers, but gives us the character code directly.
+
+ return QString(QChar(keySym));
+}
+
+#include <e32keys.h>
+struct KeyMapping{
+ TKeyCode s60KeyCode;
+ TStdScanCode s60ScanCode;
+ Qt::Key qtKey;
+};
+
+using namespace Qt;
+
+static const KeyMapping keyMapping[] = {
+ {EKeyBackspace, EStdKeyBackspace, Key_Backspace},
+ {EKeyTab, EStdKeyTab, Key_Tab},
+ {EKeyEnter, EStdKeyEnter, Key_Enter},
+ {EKeyEscape, EStdKeyEscape, Key_Escape},
+ {EKeySpace, EStdKeySpace, Key_Space},
+ {EKeyDelete, EStdKeyDelete, Key_Delete},
+ {EKeyPrintScreen, EStdKeyPrintScreen, Key_SysReq},
+ {EKeyPause, EStdKeyPause, Key_Pause},
+ {EKeyHome, EStdKeyHome, Key_Home},
+ {EKeyEnd, EStdKeyEnd, Key_End},
+ {EKeyPageUp, EStdKeyPageUp, Key_PageUp},
+ {EKeyPageDown, EStdKeyPageDown, Key_PageDown},
+ {EKeyInsert, EStdKeyInsert, Key_Insert},
+ {EKeyLeftArrow, EStdKeyLeftArrow, Key_Left},
+ {EKeyRightArrow, EStdKeyRightArrow, Key_Right},
+ {EKeyUpArrow, EStdKeyUpArrow, Key_Up},
+ {EKeyDownArrow, EStdKeyDownArrow, Key_Down},
+ {EKeyLeftShift, EStdKeyLeftShift, Key_Shift},
+ {EKeyRightShift, EStdKeyRightShift, Key_Shift},
+ {EKeyLeftAlt, EStdKeyLeftAlt, Key_Alt},
+ {EKeyRightAlt, EStdKeyRightAlt, Key_AltGr},
+ {EKeyLeftCtrl, EStdKeyLeftCtrl, Key_Control},
+ {EKeyRightCtrl, EStdKeyRightCtrl, Key_Control},
+ {EKeyLeftFunc, EStdKeyLeftFunc, Key_Super_L},
+ {EKeyRightFunc, EStdKeyRightFunc, Key_Super_R},
+ {EKeyCapsLock, EStdKeyCapsLock, Key_CapsLock},
+ {EKeyNumLock, EStdKeyNumLock, Key_NumLock},
+ {EKeyScrollLock, EStdKeyScrollLock, Key_ScrollLock},
+ {EKeyF1, EStdKeyF1, Key_F1},
+ {EKeyF2, EStdKeyF2, Key_F2},
+ {EKeyF3, EStdKeyF3, Key_F3},
+ {EKeyF4, EStdKeyF4, Key_F4},
+ {EKeyF5, EStdKeyF5, Key_F5},
+ {EKeyF6, EStdKeyF6, Key_F6},
+ {EKeyF7, EStdKeyF7, Key_F7},
+ {EKeyF8, EStdKeyF8, Key_F8},
+ {EKeyF9, EStdKeyF9, Key_F9},
+ {EKeyF10, EStdKeyF10, Key_F10},
+ {EKeyF11, EStdKeyF11, Key_F11},
+ {EKeyF12, EStdKeyF12, Key_F12},
+ {EKeyF13, EStdKeyF13, Key_F13},
+ {EKeyF14, EStdKeyF14, Key_F14},
+ {EKeyF15, EStdKeyF15, Key_F15},
+ {EKeyF16, EStdKeyF16, Key_F16},
+ {EKeyF17, EStdKeyF17, Key_F17},
+ {EKeyF18, EStdKeyF18, Key_F18},
+ {EKeyF19, EStdKeyF19, Key_F19},
+ {EKeyF20, EStdKeyF20, Key_F20},
+ {EKeyF21, EStdKeyF21, Key_F21},
+ {EKeyF22, EStdKeyF22, Key_F22},
+ {EKeyF23, EStdKeyF23, Key_F23},
+ {EKeyF24, EStdKeyF24, Key_F24},
+ {EKeyOff, EStdKeyOff, Key_PowerOff},
+// {EKeyMenu, EStdKeyMenu, Key_Menu}, // Menu is EKeyApplication0
+ {EKeyHelp, EStdKeyHelp, Key_Help},
+ {EKeyDial, EStdKeyDial, Key_Call},
+ {EKeyIncVolume, EStdKeyIncVolume, Key_VolumeUp},
+ {EKeyDecVolume, EStdKeyDecVolume, Key_VolumeDown},
+ {EKeyDevice0, EStdKeyDevice0, Key_Context1}, // Found by manual testing.
+ {EKeyDevice1, EStdKeyDevice1, Key_Context2}, // Found by manual testing.
+ {EKeyDevice3, EStdKeyDevice3, Key_Select},
+ {EKeyDevice7, EStdKeyDevice7, Key_Camera},
+ {EKeyApplication0, EStdKeyApplication0, Key_Menu}, // Found by manual testing.
+ {EKeyApplication1, EStdKeyApplication1, Key_Launch1}, // Found by manual testing.
+ {EKeyApplication2, EStdKeyApplication2, Key_MediaPlay}, // Found by manual testing.
+ {EKeyApplication3, EStdKeyApplication3, Key_MediaStop}, // Found by manual testing.
+ {EKeyApplication4, EStdKeyApplication4, Key_MediaNext}, // Found by manual testing.
+ {EKeyApplication5, EStdKeyApplication5, Key_MediaPrevious}, // Found by manual testing.
+ {EKeyApplication6, EStdKeyApplication6, Key_Launch6},
+ {EKeyApplication7, EStdKeyApplication7, Key_Launch7},
+ {EKeyApplication8, EStdKeyApplication8, Key_Launch8},
+ {EKeyApplication9, EStdKeyApplication9, Key_Launch9},
+ {EKeyApplicationA, EStdKeyApplicationA, Key_LaunchA},
+ {EKeyApplicationB, EStdKeyApplicationB, Key_LaunchB},
+ {EKeyApplicationC, EStdKeyApplicationC, Key_LaunchC},
+ {EKeyApplicationD, EStdKeyApplicationD, Key_LaunchD},
+ {EKeyApplicationE, EStdKeyApplicationE, Key_LaunchE},
+ {EKeyApplicationF, EStdKeyApplicationF, Key_LaunchF},
+ {EKeyApplication19, EStdKeyApplication19, Key_CameraFocus},
+ {EKeyYes, EStdKeyYes, Key_Yes},
+ {EKeyNo, EStdKeyNo, Key_No},
+ {TKeyCode(0), TStdScanCode(0), Qt::Key(0)}
+};
+
+int QKeyMapperPrivate::mapS60KeyToQt(TUint s60key)
+{
+ int res = Qt::Key_unknown;
+ for (int i = 0; keyMapping[i].s60KeyCode != 0; i++) {
+ if (keyMapping[i].s60KeyCode == s60key) {
+ res = keyMapping[i].qtKey;
+ break;
+ }
+ }
+ return res;
+}
+
+int QKeyMapperPrivate::mapS60ScanCodesToQt(TUint s60scanCode)
+{
+ int res = Qt::Key_unknown;
+ for (int i = 0; keyMapping[i].s60KeyCode != 0; i++) {
+ if (keyMapping[i].s60ScanCode == s60scanCode) {
+ res = keyMapping[i].qtKey;
+ break;
+ }
+ }
+ return res;
+}
+
+int QKeyMapperPrivate::mapQtToS60Key(int qtKey)
+{
+ int res = KErrUnknown;
+ for (int i = 0; keyMapping[i].s60KeyCode != 0; i++) {
+ if (keyMapping[i].qtKey == qtKey) {
+ res = keyMapping[i].s60KeyCode;
+ break;
+ }
+ }
+ return res;
+}
+
+int QKeyMapperPrivate::mapQtToS60ScanCodes(int qtKey)
+{
+ int res = KErrUnknown;
+ for (int i = 0; keyMapping[i].s60KeyCode != 0; i++) {
+ if (keyMapping[i].qtKey == qtKey) {
+ res = keyMapping[i].s60ScanCode;
+ break;
+ }
+ }
+ return res;
+}
+
+void QKeyMapperPrivate::updateInputLanguage()
+{
+#ifdef Q_WS_S60
+ TInt err;
+ CRepository *repo;
+ const TUid KCRUidAknFep = TUid::Uid(0x101F876D);
+ const TUint32 KAknFepInputTxtLang = 0x00000005;
+ TRAP(err, repo = CRepository::NewL(KCRUidAknFep));
+ if (err != KErrNone)
+ return;
+
+ TInt symbianLang;
+ err = repo->Get(KAknFepInputTxtLang, symbianLang);
+ delete repo;
+ if (err != KErrNone)
+ return;
+
+ QString qtLang = QString::fromAscii(qt_symbianLocaleName(symbianLang));
+ keyboardInputLocale = QLocale(qtLang);
+ keyboardInputDirection = (TBidiText::ScriptDirectionality(TLanguage(symbianLang)) == TBidiText::ERightToLeft)
+ ? Qt::RightToLeft : Qt::LeftToRight;
+#else
+ keyboardInputLocale = QLocale();
+ keyboardInputDirection = Qt::LeftToRight;
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/s60/qpaintengine_s60.cpp b/src/widgets/platforms/s60/qpaintengine_s60.cpp
new file mode 100644
index 0000000000..67984008ca
--- /dev/null
+++ b/src/widgets/platforms/s60/qpaintengine_s60.cpp
@@ -0,0 +1,145 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <private/qpaintengine_s60_p.h>
+#include <private/qpixmap_s60_p.h>
+#include <private/qt_s60_p.h>
+#include <private/qvolatileimage_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QS60PaintEnginePrivate : public QRasterPaintEnginePrivate
+{
+public:
+ QS60PaintEnginePrivate() {}
+};
+
+QS60PaintEngine::QS60PaintEngine(QPaintDevice *device, QS60PlatformPixmap *data)
+ : QRasterPaintEngine(*(new QS60PaintEnginePrivate), device), handle(data)
+{
+}
+
+bool QS60PaintEngine::begin(QPaintDevice *device)
+{
+ Q_D(QS60PaintEngine);
+
+ if (handle->classId() == QPlatformPixmap::RasterClass) {
+ handle->beginDataAccess();
+ bool ret = QRasterPaintEngine::begin(device);
+ // Make sure QPaintEngine::paintDevice() returns the proper device.
+ // QRasterPaintEngine changes pdev to QImage in case of RasterClass QPlatformPixmap
+ // which is incorrect in Symbian.
+ d->pdev = device;
+ return ret;
+ }
+
+ return QRasterPaintEngine::begin(device);
+}
+
+bool QS60PaintEngine::end()
+{
+ if (handle->classId() == QPlatformPixmap::RasterClass) {
+ bool ret = QRasterPaintEngine::end();
+ handle->endDataAccess();
+ return ret;
+ }
+ return QRasterPaintEngine::end();
+}
+
+void QS60PaintEngine::drawPixmap(const QPointF &p, const QPixmap &pm)
+{
+ if (pm.handle()->classId() == QPlatformPixmap::RasterClass) {
+ QS60PlatformPixmap *srcData = static_cast<QS60PlatformPixmap *>(pm.handle());
+ srcData->beginDataAccess();
+ QRasterPaintEngine::drawPixmap(p, pm);
+ srcData->endDataAccess();
+ } else {
+ void *nativeData = pm.handle()->toNativeType(QPlatformPixmap::VolatileImage);
+ if (nativeData) {
+ QVolatileImage *img = static_cast<QVolatileImage *>(nativeData);
+ img->beginDataAccess();
+ QRasterPaintEngine::drawImage(p, img->imageRef());
+ img->endDataAccess(true);
+ } else {
+ QRasterPaintEngine::drawPixmap(p, pm);
+ }
+ }
+}
+
+void QS60PaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
+{
+ if (pm.handle()->classId() == QPlatformPixmap::RasterClass) {
+ QS60PlatformPixmap *srcData = static_cast<QS60PlatformPixmap *>(pm.handle());
+ srcData->beginDataAccess();
+ QRasterPaintEngine::drawPixmap(r, pm, sr);
+ srcData->endDataAccess();
+ } else {
+ void *nativeData = pm.handle()->toNativeType(QPlatformPixmap::VolatileImage);
+ if (nativeData) {
+ QVolatileImage *img = static_cast<QVolatileImage *>(nativeData);
+ img->beginDataAccess();
+ QRasterPaintEngine::drawImage(r, img->imageRef(), sr);
+ img->endDataAccess(true);
+ } else {
+ QRasterPaintEngine::drawPixmap(r, pm, sr);
+ }
+ }
+}
+
+void QS60PaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &sr)
+{
+ if (pm.handle()->classId() == QPlatformPixmap::RasterClass) {
+ QS60PlatformPixmap *srcData = static_cast<QS60PlatformPixmap *>(pm.handle());
+ srcData->beginDataAccess();
+ QRasterPaintEngine::drawTiledPixmap(r, pm, sr);
+ srcData->endDataAccess();
+ } else {
+ QRasterPaintEngine::drawTiledPixmap(r, pm, sr);
+ }
+}
+
+void QS60PaintEngine::prepare(QImage *image)
+{
+ QRasterBuffer *buffer = d_func()->rasterBuffer.data();
+ if (buffer)
+ buffer->prepare(image);
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/s60/qpaintengine_s60_p.h b/src/widgets/platforms/s60/qpaintengine_s60_p.h
new file mode 100644
index 0000000000..4cea87954f
--- /dev/null
+++ b/src/widgets/platforms/s60/qpaintengine_s60_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTENGINE_S60_P_H
+#define QPAINTENGINE_S60_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qpaintengine_raster_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QS60PaintEnginePrivate;
+class QS60PlatformPixmap;
+
+class QS60PaintEngine : public QRasterPaintEngine
+{
+ Q_DECLARE_PRIVATE(QS60PaintEngine)
+
+public:
+ QS60PaintEngine(QPaintDevice *device, QS60PlatformPixmap* data);
+ bool begin(QPaintDevice *device);
+ bool end();
+
+ void drawPixmap(const QPointF &p, const QPixmap &pm);
+ void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
+ void drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &sr);
+
+ void prepare(QImage* image);
+
+private:
+ QS60PlatformPixmap *handle;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPAINTENGINE_S60_P_H
diff --git a/src/widgets/platforms/s60/qpixmap_s60.cpp b/src/widgets/platforms/s60/qpixmap_s60.cpp
new file mode 100644
index 0000000000..5e1474546e
--- /dev/null
+++ b/src/widgets/platforms/s60/qpixmap_s60.cpp
@@ -0,0 +1,1040 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <exception>
+#include <w32std.h>
+#include <fbs.h>
+
+#include <private/qapplication_p.h>
+#include <private/qgraphicssystem_p.h>
+#include <private/qt_s60_p.h>
+#include <private/qpaintengine_s60_p.h>
+#include <private/qfont_p.h>
+
+#include "qpixmap.h"
+#include "qpixmap_raster_p.h"
+#include <qwidget.h>
+#include "qpixmap_s60_p.h"
+#include "qnativeimage_p.h"
+#include "qbitmap.h"
+#include "qimage.h"
+#include "qimage_p.h"
+
+#include <fbs.h>
+
+QT_BEGIN_NAMESPACE
+
+const uchar qt_pixmap_bit_mask[] = { 0x01, 0x02, 0x04, 0x08,
+ 0x10, 0x20, 0x40, 0x80 };
+
+static bool cleanup_function_registered = false;
+static QS60PlatformPixmap *firstPixmap = 0;
+
+// static
+void QS60PlatformPixmap::qt_symbian_register_pixmap(QS60PlatformPixmap *pd)
+{
+ if (!cleanup_function_registered) {
+ qAddPostRoutine(qt_symbian_release_pixmaps);
+ cleanup_function_registered = true;
+ }
+
+ pd->next = firstPixmap;
+ pd->prev = 0;
+ if (firstPixmap)
+ firstPixmap->prev = pd;
+ firstPixmap = pd;
+}
+
+// static
+void QS60PlatformPixmap::qt_symbian_unregister_pixmap(QS60PlatformPixmap *pd)
+{
+ if (pd->next)
+ pd->next->prev = pd->prev;
+ if (pd->prev)
+ pd->prev->next = pd->next;
+ else
+ firstPixmap = pd->next;
+}
+
+// static
+void QS60PlatformPixmap::qt_symbian_release_pixmaps()
+{
+ // Scan all QS60PlatformPixmap objects in the system and destroy them.
+ QS60PlatformPixmap *pd = firstPixmap;
+ while (pd != 0) {
+ pd->release();
+ pd = pd->next;
+ }
+}
+
+/*
+ \class QSymbianFbsClient
+ \since 4.6
+ \internal
+
+ Symbian Font And Bitmap server client that is
+ used to lock the global bitmap heap. Only used in
+ S60 v3.1 and S60 v3.2.
+*/
+_LIT(KFBSERVLargeBitmapAccessName,"FbsLargeBitmapAccess");
+class QSymbianFbsClient
+{
+public:
+
+ QSymbianFbsClient() : heapLocked(false)
+ {
+ heapLock.OpenGlobal(KFBSERVLargeBitmapAccessName);
+ }
+
+ ~QSymbianFbsClient()
+ {
+ heapLock.Close();
+ }
+
+ bool lockHeap()
+ {
+ bool wasLocked = heapLocked;
+
+ if (heapLock.Handle() && !heapLocked) {
+ heapLock.Wait();
+ heapLocked = true;
+ }
+
+ return wasLocked;
+ }
+
+ bool unlockHeap()
+ {
+ bool wasLocked = heapLocked;
+
+ if (heapLock.Handle() && heapLocked) {
+ heapLock.Signal();
+ heapLocked = false;
+ }
+
+ return wasLocked;
+ }
+
+
+private:
+
+ RMutex heapLock;
+ bool heapLocked;
+};
+
+Q_GLOBAL_STATIC(QSymbianFbsClient, qt_symbianFbsClient);
+
+
+
+// QSymbianFbsHeapLock
+
+QSymbianFbsHeapLock::QSymbianFbsHeapLock(LockAction a)
+: action(a), wasLocked(false)
+{
+ QSysInfo::SymbianVersion symbianVersion = QSysInfo::symbianVersion();
+ if (symbianVersion == QSysInfo::SV_9_2 || symbianVersion == QSysInfo::SV_9_3)
+ wasLocked = qt_symbianFbsClient()->unlockHeap();
+}
+
+QSymbianFbsHeapLock::~QSymbianFbsHeapLock()
+{
+ // Do nothing
+}
+
+void QSymbianFbsHeapLock::relock()
+{
+ QSysInfo::SymbianVersion symbianVersion = QSysInfo::symbianVersion();
+ if (wasLocked && (symbianVersion == QSysInfo::SV_9_2 || symbianVersion == QSysInfo::SV_9_3))
+ qt_symbianFbsClient()->lockHeap();
+}
+
+/*
+ \class QSymbianBitmapDataAccess
+ \since 4.6
+ \internal
+
+ Data access class that is used to locks/unlocks pixel data
+ when drawing or modifying CFbsBitmap pixel data.
+*/
+class QSymbianBitmapDataAccess
+{
+public:
+
+ static int heapRefCount;
+ QSysInfo::SymbianVersion symbianVersion;
+
+ explicit QSymbianBitmapDataAccess()
+ {
+ symbianVersion = QSysInfo::symbianVersion();
+ };
+
+ ~QSymbianBitmapDataAccess() {};
+
+ inline void beginDataAccess(CFbsBitmap *bitmap)
+ {
+ if (symbianVersion == QSysInfo::SV_9_2) {
+ if (heapRefCount == 0)
+ qt_symbianFbsClient()->lockHeap();
+ } else {
+ bitmap->LockHeap(ETrue);
+ }
+
+ heapRefCount++;
+ }
+
+ inline void endDataAccess(CFbsBitmap *bitmap)
+ {
+ heapRefCount--;
+
+ if (symbianVersion == QSysInfo::SV_9_2) {
+ if (heapRefCount == 0)
+ qt_symbianFbsClient()->unlockHeap();
+ } else {
+ bitmap->UnlockHeap(ETrue);
+ }
+ }
+};
+
+int QSymbianBitmapDataAccess::heapRefCount = 0;
+
+
+#define UPDATE_BUFFER() \
+ { \
+ beginDataAccess(); \
+ endDataAccess(); \
+}
+
+
+static CFbsBitmap* createSymbianCFbsBitmap(const TSize& size, TDisplayMode mode)
+{
+ QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
+
+ CFbsBitmap* bitmap = 0;
+ QT_TRAP_THROWING(bitmap = new (ELeave) CFbsBitmap);
+
+ if (bitmap->Create(size, mode) != KErrNone) {
+ delete bitmap;
+ bitmap = 0;
+ }
+
+ lock.relock();
+
+ return bitmap;
+}
+
+static CFbsBitmap* uncompress(CFbsBitmap* bitmap)
+{
+ if(bitmap->IsCompressedInRAM()) {
+ QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
+
+ CFbsBitmap *uncompressed = 0;
+ QT_TRAP_THROWING(uncompressed = new (ELeave) CFbsBitmap);
+
+ if (uncompressed->Create(bitmap->SizeInPixels(), bitmap->DisplayMode()) != KErrNone) {
+ delete bitmap;
+ bitmap = 0;
+ lock.relock();
+
+ return bitmap;
+ }
+
+ lock.relock();
+
+ CFbsBitmapDevice* bitmapDevice = 0;
+ CFbsBitGc *bitmapGc = 0;
+ QT_TRAP_THROWING(bitmapDevice = CFbsBitmapDevice::NewL(uncompressed));
+ QT_TRAP_THROWING(bitmapGc = CFbsBitGc::NewL());
+ bitmapGc->Activate(bitmapDevice);
+
+ bitmapGc->BitBlt(TPoint(), bitmap);
+
+ delete bitmapGc;
+ delete bitmapDevice;
+
+ return uncompressed;
+ } else {
+ return bitmap;
+ }
+}
+
+QPixmap QPixmap::grabWindow(WId winId, int x, int y, int w, int h)
+{
+ CWsScreenDevice* screenDevice = S60->screenDevice();
+ TSize screenSize = screenDevice->SizeInPixels();
+
+ TSize srcSize;
+ // Find out if this is one of our windows.
+ QSymbianControl *sControl;
+ sControl = winId->MopGetObject(sControl);
+ if (sControl && sControl->widget()->windowType() == Qt::Desktop) {
+ // Grabbing desktop widget
+ srcSize = screenSize;
+ } else {
+ TPoint relativePos = winId->PositionRelativeToScreen();
+ x += relativePos.iX;
+ y += relativePos.iY;
+ srcSize = winId->Size();
+ }
+
+ TRect srcRect(TPoint(x, y), srcSize);
+ // Clip to the screen
+ srcRect.Intersection(TRect(screenSize));
+
+ if (w > 0 && h > 0) {
+ TRect subRect(TPoint(x, y), TSize(w, h));
+ // Clip to the subRect
+ srcRect.Intersection(subRect);
+ }
+
+ if (srcRect.IsEmpty())
+ return QPixmap();
+
+ CFbsBitmap* temporary = createSymbianCFbsBitmap(srcRect.Size(), screenDevice->DisplayMode());
+
+ QPixmap pix;
+
+ if (temporary && screenDevice->CopyScreenToBitmap(temporary, srcRect) == KErrNone) {
+ pix = QPixmap::fromSymbianCFbsBitmap(temporary);
+ }
+
+ delete temporary;
+ return pix;
+}
+
+/*!
+ \fn CFbsBitmap *QPixmap::toSymbianCFbsBitmap() const
+ \since 4.6
+
+ Creates a \c CFbsBitmap that is equivalent to the QPixmap. Internally this
+ function will try to duplicate the handle instead of copying the data,
+ however in scenarios where this is not possible the data will be copied.
+ If the creation fails or the pixmap is null, then this function returns 0.
+
+ It is the caller's responsibility to release the \c CFbsBitmap data
+ after use either by deleting the bitmap or calling \c Reset().
+
+ \warning On S60 3.1 and S60 3.2, semi-transparent pixmaps are always copied
+ and not duplicated.
+ \warning This function is only available on Symbian OS.
+
+ \sa fromSymbianCFbsBitmap()
+*/
+CFbsBitmap *QPixmap::toSymbianCFbsBitmap() const
+{
+ QPlatformPixmap *data = handle();
+ if (!data || data->isNull())
+ return 0;
+
+ return reinterpret_cast<CFbsBitmap*>(data->toNativeType(QPlatformPixmap::FbsBitmap));
+}
+
+/*!
+ \fn QPixmap QPixmap::fromSymbianCFbsBitmap(CFbsBitmap *bitmap)
+ \since 4.6
+
+ Creates a QPixmap from a \c CFbsBitmap \a bitmap. Internally this function
+ will try to duplicate the bitmap handle instead of copying the data, however
+ in scenarios where this is not possible the data will be copied.
+ To be sure that QPixmap does not modify your original instance, you should
+ make a copy of your \c CFbsBitmap before calling this function.
+ If the CFbsBitmap is not valid this function will return a null QPixmap.
+ For performance reasons it is recommended to use a \a bitmap with a display
+ mode of EColor16MAP or EColor16MU whenever possible.
+
+ \warning This function is only available on Symbian OS.
+
+ \sa toSymbianCFbsBitmap(), {QPixmap#Pixmap Conversion}{Pixmap Conversion}
+*/
+QPixmap QPixmap::fromSymbianCFbsBitmap(CFbsBitmap *bitmap)
+{
+ if (!bitmap)
+ return QPixmap();
+
+ QScopedPointer<QPlatformPixmap> data(QPlatformPixmap::create(0,0, QPlatformPixmap::PixmapType));
+ data->fromNativeType(reinterpret_cast<void*>(bitmap), QPlatformPixmap::FbsBitmap);
+ QPixmap pixmap(data.take());
+ return pixmap;
+}
+
+QS60PlatformPixmap::QS60PlatformPixmap(PixelType type) : QRasterPlatformPixmap(type),
+ symbianBitmapDataAccess(new QSymbianBitmapDataAccess),
+ cfbsBitmap(0),
+ pengine(0),
+ bytes(0),
+ formatLocked(false),
+ next(0),
+ prev(0)
+{
+ qt_symbian_register_pixmap(this);
+}
+
+QS60PlatformPixmap::~QS60PlatformPixmap()
+{
+ release();
+ delete symbianBitmapDataAccess;
+ qt_symbian_unregister_pixmap(this);
+}
+
+void QS60PlatformPixmap::resize(int width, int height)
+{
+ if (width <= 0 || height <= 0) {
+ w = width;
+ h = height;
+ is_null = true;
+
+ release();
+ return;
+ } else if (!cfbsBitmap) {
+ TDisplayMode mode;
+ if (pixelType() == BitmapType)
+ mode = EGray2;
+ else
+ mode = EColor16MU;
+
+ CFbsBitmap* bitmap = createSymbianCFbsBitmap(TSize(width, height), mode);
+ fromSymbianBitmap(bitmap);
+ } else {
+
+ TSize newSize(width, height);
+
+ if(cfbsBitmap->SizeInPixels() != newSize) {
+ cfbsBitmap->Resize(TSize(width, height));
+ if(pengine) {
+ delete pengine;
+ pengine = 0;
+ }
+ }
+
+ UPDATE_BUFFER();
+ }
+}
+
+void QS60PlatformPixmap::release()
+{
+ if (cfbsBitmap) {
+ QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
+ delete cfbsBitmap;
+ lock.relock();
+ }
+
+ delete pengine;
+ image = QImage();
+ cfbsBitmap = 0;
+ pengine = 0;
+ bytes = 0;
+}
+
+/*!
+ * Takes ownership of bitmap. Used by window surface
+ */
+void QS60PlatformPixmap::fromSymbianBitmap(CFbsBitmap* bitmap, bool lockFormat)
+{
+ Q_ASSERT(bitmap);
+
+ release();
+
+ cfbsBitmap = bitmap;
+ formatLocked = lockFormat;
+
+ setSerialNumber(cfbsBitmap->Handle());
+
+ UPDATE_BUFFER();
+
+ // Create default palette if needed
+ if (cfbsBitmap->DisplayMode() == EGray2) {
+ image.setColorCount(2);
+ image.setColor(0, QColor(Qt::color0).rgba());
+ image.setColor(1, QColor(Qt::color1).rgba());
+
+ //Symbian thinks set pixels are white/transparent, Qt thinks they are foreground/solid
+ //So invert mono bitmaps so that masks work correctly.
+ image.invertPixels();
+ } else if (cfbsBitmap->DisplayMode() == EGray256) {
+ for (int i=0; i < 256; ++i)
+ image.setColor(i, qRgb(i, i, i));
+ } else if (cfbsBitmap->DisplayMode() == EColor256) {
+ const TColor256Util *palette = TColor256Util::Default();
+ for (int i=0; i < 256; ++i)
+ image.setColor(i, (QRgb)(palette->Color256(i).Value()));
+ }
+}
+
+QImage QS60PlatformPixmap::toImage(const QRect &r) const
+{
+ QS60PlatformPixmap *that = const_cast<QS60PlatformPixmap*>(this);
+ that->beginDataAccess();
+ QImage copy = that->image.copy(r);
+ that->endDataAccess();
+
+ return copy;
+}
+
+void QS60PlatformPixmap::fromImage(const QImage &img, Qt::ImageConversionFlags flags)
+{
+ release();
+
+ QImage sourceImage;
+
+ if (pixelType() == BitmapType) {
+ sourceImage = img.convertToFormat(QImage::Format_MonoLSB);
+ } else {
+ if (img.depth() == 1) {
+ sourceImage = img.hasAlphaChannel()
+ ? img.convertToFormat(QImage::Format_ARGB32_Premultiplied)
+ : img.convertToFormat(QImage::Format_RGB32);
+ } else {
+
+ QImage::Format opaqueFormat = QNativeImage::systemFormat();
+ QImage::Format alphaFormat = QImage::Format_ARGB32_Premultiplied;
+
+ if (!img.hasAlphaChannel()
+ || ((flags & Qt::NoOpaqueDetection) == 0
+ && !const_cast<QImage &>(img).data_ptr()->checkForAlphaPixels())) {
+ sourceImage = img.convertToFormat(opaqueFormat);
+ } else {
+ sourceImage = img.convertToFormat(alphaFormat);
+ }
+ }
+ }
+
+
+ QImage::Format destFormat = sourceImage.format();
+ TDisplayMode mode;
+ switch (destFormat) {
+ case QImage::Format_MonoLSB:
+ mode = EGray2;
+ break;
+ case QImage::Format_RGB32:
+ mode = EColor16MU;
+ break;
+ case QImage::Format_ARGB32_Premultiplied:
+ if (S60->supportsPremultipliedAlpha) {
+ mode = Q_SYMBIAN_ECOLOR16MAP;
+ break;
+ } else {
+ destFormat = QImage::Format_ARGB32;
+ }
+ // Fall through intended
+ case QImage::Format_ARGB32:
+ mode = EColor16MA;
+ break;
+ case QImage::Format_Invalid:
+ return;
+ default:
+ qWarning("Image format not supported: %d", image.format());
+ return;
+ }
+
+ cfbsBitmap = createSymbianCFbsBitmap(TSize(sourceImage.width(), sourceImage.height()), mode);
+ if (!cfbsBitmap) {
+ qWarning("Could not create CFbsBitmap");
+ release();
+ return;
+ }
+
+ setSerialNumber(cfbsBitmap->Handle());
+
+ const uchar *sptr = const_cast<const QImage &>(sourceImage).bits();
+ symbianBitmapDataAccess->beginDataAccess(cfbsBitmap);
+ uchar *dptr = (uchar*)cfbsBitmap->DataAddress();
+ Mem::Copy(dptr, sptr, sourceImage.byteCount());
+ symbianBitmapDataAccess->endDataAccess(cfbsBitmap);
+
+ UPDATE_BUFFER();
+
+ if (destFormat == QImage::Format_MonoLSB) {
+ image.setColorCount(2);
+ image.setColor(0, QColor(Qt::color0).rgba());
+ image.setColor(1, QColor(Qt::color1).rgba());
+ } else {
+ image.setColorTable(sourceImage.colorTable());
+ }
+}
+
+void QS60PlatformPixmap::copy(const QPlatformPixmap *data, const QRect &rect)
+{
+ const QS60PlatformPixmap *s60Data = static_cast<const QS60PlatformPixmap*>(data);
+ fromImage(s60Data->toImage(rect), Qt::AutoColor | Qt::OrderedAlphaDither);
+}
+
+bool QS60PlatformPixmap::scroll(int dx, int dy, const QRect &rect)
+{
+ beginDataAccess();
+ bool res = QRasterPlatformPixmap::scroll(dx, dy, rect);
+ endDataAccess();
+ return res;
+}
+
+int QS60PlatformPixmap::metric(QPaintDevice::PaintDeviceMetric metric) const
+{
+ if (!cfbsBitmap)
+ return 0;
+
+ switch (metric) {
+ case QPaintDevice::PdmWidth:
+ return cfbsBitmap->SizeInPixels().iWidth;
+ case QPaintDevice::PdmHeight:
+ return cfbsBitmap->SizeInPixels().iHeight;
+ case QPaintDevice::PdmWidthMM:
+ return qRound(cfbsBitmap->SizeInPixels().iWidth * 25.4 / qt_defaultDpiX());
+ case QPaintDevice::PdmHeightMM:
+ return qRound(cfbsBitmap->SizeInPixels().iHeight * 25.4 / qt_defaultDpiY());
+ case QPaintDevice::PdmNumColors:
+ return TDisplayModeUtils::NumDisplayModeColors(cfbsBitmap->DisplayMode());
+ case QPaintDevice::PdmDpiX:
+ case QPaintDevice::PdmPhysicalDpiX:
+ return qt_defaultDpiX();
+ case QPaintDevice::PdmDpiY:
+ case QPaintDevice::PdmPhysicalDpiY:
+ return qt_defaultDpiY();
+ case QPaintDevice::PdmDepth:
+ return TDisplayModeUtils::NumDisplayModeBitsPerPixel(cfbsBitmap->DisplayMode());
+ default:
+ qWarning("QPixmap::metric: Invalid metric command");
+ }
+ return 0;
+
+}
+
+void QS60PlatformPixmap::fill(const QColor &color)
+{
+ if (color.alpha() != 255) {
+ QImage im(width(), height(), QImage::Format_ARGB32_Premultiplied);
+ im.fill(PREMUL(color.rgba()));
+ release();
+ fromImage(im, Qt::AutoColor | Qt::OrderedAlphaDither);
+ } else {
+ beginDataAccess();
+ QRasterPlatformPixmap::fill(color);
+ endDataAccess();
+ }
+}
+
+void QS60PlatformPixmap::setMask(const QBitmap &mask)
+{
+ if (mask.size().isEmpty()) {
+ if (image.depth() != 1) {
+ QImage newImage = image.convertToFormat(QImage::Format_RGB32);
+ release();
+ fromImage(newImage, Qt::AutoColor | Qt::OrderedAlphaDither);
+ }
+ } else if (image.depth() == 1) {
+ beginDataAccess();
+ QRasterPlatformPixmap::setMask(mask);
+ endDataAccess();
+ } else {
+ const int w = image.width();
+ const int h = image.height();
+
+ const QImage imageMask = mask.toImage().convertToFormat(QImage::Format_MonoLSB);
+ QImage newImage = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+ for (int y = 0; y < h; ++y) {
+ const uchar *mscan = imageMask.scanLine(y);
+ QRgb *tscan = (QRgb *)newImage.scanLine(y);
+ for (int x = 0; x < w; ++x) {
+ if (!(mscan[x>>3] & qt_pixmap_bit_mask[x&7]))
+ tscan[x] = 0;
+ }
+ }
+ release();
+ fromImage(newImage, Qt::AutoColor | Qt::OrderedAlphaDither);
+ }
+}
+
+void QS60PlatformPixmap::setAlphaChannel(const QPixmap &alphaChannel)
+{
+ QImage img(toImage());
+ img.setAlphaChannel(alphaChannel.toImage());
+ release();
+ fromImage(img, Qt::OrderedDither | Qt::OrderedAlphaDither);
+}
+
+QImage QS60PlatformPixmap::toImage() const
+{
+ return toImage(QRect());
+}
+
+QPaintEngine* QS60PlatformPixmap::paintEngine() const
+{
+ if (!pengine) {
+ QS60PlatformPixmap *that = const_cast<QS60PlatformPixmap*>(this);
+ that->pengine = new QS60PaintEngine(&that->image, that);
+ }
+ return pengine;
+}
+
+void QS60PlatformPixmap::beginDataAccess()
+{
+ if(!cfbsBitmap)
+ return;
+
+ symbianBitmapDataAccess->beginDataAccess(cfbsBitmap);
+
+ uchar* newBytes = (uchar*)cfbsBitmap->DataAddress();
+
+ TSize size = cfbsBitmap->SizeInPixels();
+
+ if (newBytes == bytes && image.width() == size.iWidth && image.height() == size.iHeight)
+ return;
+
+ bytes = newBytes;
+ TDisplayMode mode = cfbsBitmap->DisplayMode();
+ QImage::Format format = qt_TDisplayMode2Format(mode);
+ // On S60 3.1, premultiplied alpha pixels are stored in a bitmap with 16MA type.
+ // S60 window surface needs backing store pixmap for transparent window in ARGB32 format.
+ // In that case formatLocked is true.
+ if (!formatLocked && format == QImage::Format_ARGB32)
+ format = QImage::Format_ARGB32_Premultiplied; // pixel data is actually in premultiplied format
+
+ QVector<QRgb> savedColorTable;
+ if (!image.isNull())
+ savedColorTable = image.colorTable();
+
+ image = QImage(bytes, size.iWidth, size.iHeight, format);
+
+ // Restore the palette or create a default
+ if (!savedColorTable.isEmpty()) {
+ image.setColorTable(savedColorTable);
+ }
+
+ w = size.iWidth;
+ h = size.iHeight;
+ d = image.depth();
+ is_null = (w <= 0 || h <= 0);
+
+ if (pengine) {
+ QS60PaintEngine *engine = static_cast<QS60PaintEngine *>(pengine);
+ engine->prepare(&image);
+ }
+}
+
+void QS60PlatformPixmap::endDataAccess(bool readOnly) const
+{
+ Q_UNUSED(readOnly);
+
+ if(!cfbsBitmap)
+ return;
+
+ symbianBitmapDataAccess->endDataAccess(cfbsBitmap);
+}
+
+/*!
+ \since 4.6
+
+ Returns a QPixmap that wraps given \a sgImage graphics resource.
+ The data should be valid even when original RSgImage handle has been
+ closed.
+
+ \warning This function is only available on Symbian OS.
+
+ \sa toSymbianRSgImage(), {QPixmap#Pixmap Conversion}{Pixmap Conversion}
+*/
+
+QPixmap QPixmap::fromSymbianRSgImage(RSgImage *sgImage)
+{
+ // It is expected that RSgImage will
+ // CURRENTLY be used in conjuction with
+ // OpenVG graphics system
+ //
+ // Surely things might change in future
+
+ if (!sgImage)
+ return QPixmap();
+
+ QScopedPointer<QPlatformPixmap> data(QPlatformPixmap::create(0,0, QPlatformPixmap::PixmapType));
+ data->fromNativeType(reinterpret_cast<void*>(sgImage), QPlatformPixmap::SgImage);
+ QPixmap pixmap(data.take());
+ return pixmap;
+}
+
+/*!
+\since 4.6
+
+Returns a \c RSgImage that is equivalent to the QPixmap by copying the data.
+
+It is the caller's responsibility to close/delete the \c RSgImage after use.
+
+\warning This function is only available on Symbian OS.
+
+\sa fromSymbianRSgImage()
+*/
+
+RSgImage *QPixmap::toSymbianRSgImage() const
+{
+ // It is expected that RSgImage will
+ // CURRENTLY be used in conjuction with
+ // OpenVG graphics system
+ //
+ // Surely things might change in future
+
+ if (isNull())
+ return 0;
+
+ RSgImage *sgImage = reinterpret_cast<RSgImage*>(handle()->toNativeType(QPlatformPixmap::SgImage));
+
+ return sgImage;
+}
+
+void* QS60PlatformPixmap::toNativeType(NativeType type)
+{
+ if (type == QPlatformPixmap::SgImage) {
+ return 0;
+ } else if (type == QPlatformPixmap::FbsBitmap) {
+
+ if (isNull() || !cfbsBitmap)
+ return 0;
+
+ bool convertToArgb32 = false;
+ bool needsCopy = false;
+
+ if (!(S60->supportsPremultipliedAlpha)) {
+ // Convert argb32_premultiplied to argb32 since Symbian 9.2 does
+ // not support premultipied format.
+
+ if (image.format() == QImage::Format_ARGB32_Premultiplied) {
+ needsCopy = true;
+ convertToArgb32 = true;
+ }
+ }
+
+ CFbsBitmap *bitmap = 0;
+
+ TDisplayMode displayMode = cfbsBitmap->DisplayMode();
+
+ if(displayMode == EGray2) {
+ //Symbian thinks set pixels are white/transparent, Qt thinks they are foreground/solid
+ //So invert mono bitmaps so that masks work correctly.
+ beginDataAccess();
+ image.invertPixels();
+ endDataAccess();
+ needsCopy = true;
+ }
+
+ if (needsCopy) {
+ QImage source;
+
+ if (convertToArgb32) {
+ beginDataAccess();
+ source = image.convertToFormat(QImage::Format_ARGB32);
+ endDataAccess();
+ displayMode = EColor16MA;
+ } else {
+ source = image;
+ }
+
+ CFbsBitmap *newBitmap = createSymbianCFbsBitmap(TSize(source.width(), source.height()), displayMode);
+ const uchar *sptr = source.bits();
+ symbianBitmapDataAccess->beginDataAccess(newBitmap);
+
+ uchar *dptr = (uchar*)newBitmap->DataAddress();
+ Mem::Copy(dptr, sptr, source.byteCount());
+
+ symbianBitmapDataAccess->endDataAccess(newBitmap);
+
+ bitmap = newBitmap;
+ } else {
+
+ QT_TRAP_THROWING(bitmap = new (ELeave) CFbsBitmap);
+
+ TInt err = bitmap->Duplicate(cfbsBitmap->Handle());
+ if (err != KErrNone) {
+ qWarning("Could not duplicate CFbsBitmap");
+ delete bitmap;
+ bitmap = 0;
+ }
+ }
+
+ if(displayMode == EGray2) {
+ // restore pixels
+ beginDataAccess();
+ image.invertPixels();
+ endDataAccess();
+ }
+
+ return reinterpret_cast<void*>(bitmap);
+
+ }
+
+ return 0;
+}
+
+void QS60PlatformPixmap::fromNativeType(void* pixmap, NativeType nativeType)
+{
+ if (nativeType == QPlatformPixmap::SgImage) {
+ return;
+ } else if (nativeType == QPlatformPixmap::FbsBitmap && pixmap) {
+
+ CFbsBitmap *bitmap = reinterpret_cast<CFbsBitmap*>(pixmap);
+
+ bool deleteSourceBitmap = false;
+ bool needsCopy = false;
+
+#ifdef Q_SYMBIAN_HAS_EXTENDED_BITMAP_TYPE
+
+ // Rasterize extended bitmaps
+
+ TUid extendedBitmapType = bitmap->ExtendedBitmapType();
+ if (extendedBitmapType != KNullUid) {
+ CFbsBitmap *rasterBitmap = createSymbianCFbsBitmap(bitmap->SizeInPixels(), EColor16MA);
+
+ CFbsBitmapDevice *rasterBitmapDev = 0;
+ QT_TRAP_THROWING(rasterBitmapDev = CFbsBitmapDevice::NewL(rasterBitmap));
+
+ CFbsBitGc *rasterBitmapGc = 0;
+ TInt err = rasterBitmapDev->CreateContext(rasterBitmapGc);
+ if (err != KErrNone) {
+ delete rasterBitmap;
+ delete rasterBitmapDev;
+ rasterBitmapDev = 0;
+ return;
+ }
+
+ rasterBitmapGc->BitBlt(TPoint( 0, 0), bitmap);
+
+ bitmap = rasterBitmap;
+
+ delete rasterBitmapDev;
+ delete rasterBitmapGc;
+
+ rasterBitmapDev = 0;
+ rasterBitmapGc = 0;
+
+ deleteSourceBitmap = true;
+ }
+#endif
+
+
+ deleteSourceBitmap = bitmap->IsCompressedInRAM();
+ CFbsBitmap *sourceBitmap = uncompress(bitmap);
+
+ TDisplayMode displayMode = sourceBitmap->DisplayMode();
+ QImage::Format format = qt_TDisplayMode2Format(displayMode);
+
+ QImage::Format opaqueFormat = QNativeImage::systemFormat();
+ QImage::Format alphaFormat = QImage::Format_ARGB32_Premultiplied;
+
+ if (format != opaqueFormat && format != alphaFormat && format != QImage::Format_MonoLSB)
+ needsCopy = true;
+
+
+ type = (format != QImage::Format_MonoLSB)
+ ? QPlatformPixmap::PixmapType
+ : QPlatformPixmap::BitmapType;
+
+ if (needsCopy) {
+
+ TSize size = sourceBitmap->SizeInPixels();
+ int bytesPerLine = sourceBitmap->ScanLineLength(size.iWidth, displayMode);
+
+ QSymbianBitmapDataAccess da;
+ da.beginDataAccess(sourceBitmap);
+ uchar *bytes = (uchar*)sourceBitmap->DataAddress();
+ QImage img = QImage(bytes, size.iWidth, size.iHeight, bytesPerLine, format);
+ img = img.copy();
+ da.endDataAccess(sourceBitmap);
+
+ if(displayMode == EGray2) {
+ //Symbian thinks set pixels are white/transparent, Qt thinks they are foreground/solid
+ //So invert mono bitmaps so that masks work correctly.
+ img.invertPixels();
+ } else if(displayMode == EColor16M) {
+ img = img.rgbSwapped(); // EColor16M is BGR
+ }
+
+ fromImage(img, Qt::AutoColor);
+
+ if(deleteSourceBitmap)
+ delete sourceBitmap;
+ } else {
+ CFbsBitmap* duplicate = 0;
+ QT_TRAP_THROWING(duplicate = new (ELeave) CFbsBitmap);
+
+ TInt err = duplicate->Duplicate(sourceBitmap->Handle());
+ if (err != KErrNone) {
+ qWarning("Could not duplicate CFbsBitmap");
+
+ if(deleteSourceBitmap)
+ delete sourceBitmap;
+
+ delete duplicate;
+ return;
+ }
+
+ fromSymbianBitmap(duplicate);
+
+ if(deleteSourceBitmap)
+ delete sourceBitmap;
+ }
+ }
+}
+
+void QS60PlatformPixmap::convertToDisplayMode(int mode)
+{
+ const TDisplayMode displayMode = static_cast<TDisplayMode>(mode);
+ if (!cfbsBitmap || cfbsBitmap->DisplayMode() == displayMode)
+ return;
+ if (image.depth() != TDisplayModeUtils::NumDisplayModeBitsPerPixel(displayMode)) {
+ qWarning("Cannot convert display mode due to depth mismatch");
+ return;
+ }
+
+ const TSize size = cfbsBitmap->SizeInPixels();
+ QScopedPointer<CFbsBitmap> newBitmap(createSymbianCFbsBitmap(size, displayMode));
+
+ const uchar *sptr = const_cast<const QImage &>(image).bits();
+ symbianBitmapDataAccess->beginDataAccess(newBitmap.data());
+ uchar *dptr = (uchar*)newBitmap->DataAddress();
+ Mem::Copy(dptr, sptr, image.byteCount());
+ symbianBitmapDataAccess->endDataAccess(newBitmap.data());
+
+ QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
+ delete cfbsBitmap;
+ lock.relock();
+ cfbsBitmap = newBitmap.take();
+ setSerialNumber(cfbsBitmap->Handle());
+ UPDATE_BUFFER();
+}
+
+QPlatformPixmap *QS60PlatformPixmap::createCompatiblePlatformPixmap() const
+{
+ return new QS60PlatformPixmap(pixelType());
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/s60/qpixmap_s60_p.h b/src/widgets/platforms/s60/qpixmap_s60_p.h
new file mode 100644
index 0000000000..2f282e9498
--- /dev/null
+++ b/src/widgets/platforms/s60/qpixmap_s60_p.h
@@ -0,0 +1,141 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPIXMAPDATA_S60_P_H
+#define QPIXMAPDATA_S60_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/private/qpixmap_raster_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class CFbsBitmap;
+class CFbsBitmapDevice;
+class CFbsBitGc;
+
+class QSymbianBitmapDataAccess;
+
+class QSymbianFbsHeapLock
+{
+public:
+
+ enum LockAction {
+ Unlock
+ };
+
+ explicit QSymbianFbsHeapLock(LockAction a);
+ ~QSymbianFbsHeapLock();
+ void relock();
+
+private:
+
+ LockAction action;
+ bool wasLocked;
+};
+
+class QS60PlatformPixmap : public QRasterPlatformPixmap
+{
+public:
+ QS60PlatformPixmap(PixelType type);
+ ~QS60PlatformPixmap();
+
+ QPlatformPixmap *createCompatiblePlatformPixmap() const;
+
+ void resize(int width, int height);
+ void fromImage(const QImage &image, Qt::ImageConversionFlags flags);
+ void copy(const QPlatformPixmap *data, const QRect &rect);
+ bool scroll(int dx, int dy, const QRect &rect);
+
+ int metric(QPaintDevice::PaintDeviceMetric metric) const;
+ void fill(const QColor &color);
+ void setMask(const QBitmap &mask);
+ void setAlphaChannel(const QPixmap &alphaChannel);
+ QImage toImage() const;
+ QPaintEngine* paintEngine() const;
+
+ void beginDataAccess();
+ void endDataAccess(bool readOnly=false) const;
+
+ void* toNativeType(NativeType type);
+ void fromNativeType(void* pixmap, NativeType type);
+
+ void convertToDisplayMode(int mode);
+
+private:
+ void release();
+ void fromSymbianBitmap(CFbsBitmap* bitmap, bool lockFormat=false);
+ QImage toImage(const QRect &r) const;
+
+ QSymbianBitmapDataAccess *symbianBitmapDataAccess;
+
+ CFbsBitmap *cfbsBitmap;
+ QPaintEngine *pengine;
+ uchar* bytes;
+
+ bool formatLocked;
+
+ QS60PlatformPixmap *next;
+ QS60PlatformPixmap *prev;
+
+ static void qt_symbian_register_pixmap(QS60PlatformPixmap *pd);
+ static void qt_symbian_unregister_pixmap(QS60PlatformPixmap *pd);
+ static void qt_symbian_release_pixmaps();
+
+ friend class QPixmap;
+ friend class QS60WindowSurface;
+ friend class QS60PaintEngine;
+ friend class QS60Data;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPIXMAPDATA_S60_P_H
+
diff --git a/src/widgets/platforms/s60/qregion_s60.cpp b/src/widgets/platforms/s60/qregion_s60.cpp
new file mode 100644
index 0000000000..bd5b701ca8
--- /dev/null
+++ b/src/widgets/platforms/s60/qregion_s60.cpp
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbitmap.h"
+#include "qbuffer.h"
+#include "qimage.h"
+#include "qpolygon.h"
+#include "qregion.h"
+
+QT_BEGIN_NAMESPACE
+
+QRegion::QRegionData QRegion::shared_empty = { Q_BASIC_ATOMIC_INITIALIZER(1), 0 };
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/s60/qsoftkeymanager_s60.cpp b/src/widgets/platforms/s60/qsoftkeymanager_s60.cpp
new file mode 100644
index 0000000000..a7f83aaa43
--- /dev/null
+++ b/src/widgets/platforms/s60/qsoftkeymanager_s60.cpp
@@ -0,0 +1,440 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qapplication.h"
+#include "qevent.h"
+#include "qbitmap.h"
+#include "qstyle.h"
+#include "qmenubar.h"
+#include "private/qt_s60_p.h"
+#include "private/qmenu_p.h"
+#include "private/qaction_p.h"
+#include "private/qsoftkeymanager_p.h"
+#include "private/qsoftkeymanager_s60_p.h"
+#include "private/qobject_p.h"
+#include <eiksoftkeyimage.h>
+#include <eikcmbut.h>
+
+#ifndef QT_NO_SOFTKEYMANAGER
+QT_BEGIN_NAMESPACE
+
+const int S60_COMMAND_START = 6000;
+const int LSK_POSITION = 0;
+const int MSK_POSITION = 3;
+const int RSK_POSITION = 2;
+
+QSoftKeyManagerPrivateS60::QSoftKeyManagerPrivateS60() : cbaHasImage(4) // 4 since MSK position index is 3
+{
+ cachedCbaIconSize[0] = QSize(0,0);
+ cachedCbaIconSize[1] = QSize(0,0);
+ cachedCbaIconSize[2] = QSize(0,0);
+ cachedCbaIconSize[3] = QSize(0,0);
+}
+
+bool QSoftKeyManagerPrivateS60::skipCbaUpdate()
+{
+ // Lets not update softkeys if
+ // 1. We don't have application panes, i.e. cba
+ // 2. Our CBA is not active, i.e. S60 native dialog or menu with custom CBA is shown
+ // 2.1. Except if thre is no current CBA at all and WindowSoftkeysRespondHint is set
+
+ // Note: Cannot use IsDisplayingMenuOrDialog since CBA update can be triggered before
+ // menu/dialog CBA is actually displayed i.e. it is being costructed.
+ CEikButtonGroupContainer *appUiCba = S60->buttonGroupContainer();
+ if (!appUiCba)
+ return true;
+ // CEikButtonGroupContainer::Current returns 0 if CBA is not visible at all
+ CEikButtonGroupContainer *currentCba = CEikButtonGroupContainer::Current();
+ // Check if softkey need to be update even they are not visible
+ bool cbaRespondsWhenInvisible = false;
+ QWidget *window = QApplication::activeWindow();
+ if (window && (window->windowFlags() & Qt::WindowSoftkeysRespondHint))
+ cbaRespondsWhenInvisible = true;
+
+ if (QApplication::testAttribute(Qt::AA_S60DontConstructApplicationPanes)
+ || (appUiCba != currentCba && !cbaRespondsWhenInvisible)) {
+ return true;
+ }
+ return false;
+}
+
+void QSoftKeyManagerPrivateS60::ensureCbaVisibilityAndResponsiviness(CEikButtonGroupContainer &cba)
+{
+ RDrawableWindow *cbaWindow = cba.DrawableWindow();
+ Q_ASSERT_X(cbaWindow, Q_FUNC_INFO, "Native CBA does not have window!");
+ // Make sure CBA is visible, i.e. CBA window is on top
+ cbaWindow->SetOrdinalPosition(0);
+ // Qt shares same CBA instance between top-level widgets,
+ // make sure we are not faded by underlying window.
+ cbaWindow->SetFaded(EFalse, RWindowTreeNode::EFadeIncludeChildren);
+ // Modal dialogs capture pointer events, but shared cba instance
+ // shall stay responsive. Raise pointer capture priority to keep
+ // softkeys responsive in modal dialogs
+ cbaWindow->SetPointerCapturePriority(1);
+}
+
+void QSoftKeyManagerPrivateS60::clearSoftkeys(CEikButtonGroupContainer &cba)
+{
+#ifdef SYMBIAN_VERSION_SYMBIAN3
+ QT_TRAP_THROWING(
+ //EAknSoftkeyEmpty is used, because using -1 adds softkeys without actions on Symbian3
+ cba.SetCommandL(0, EAknSoftkeyEmpty, KNullDesC);
+ cba.SetCommandL(2, EAknSoftkeyEmpty, KNullDesC);
+ );
+#else
+ QT_TRAP_THROWING(
+ //Using -1 instead of EAknSoftkeyEmpty to avoid flickering.
+ cba.SetCommandL(0, -1, KNullDesC);
+ // TODO: Should we clear also middle SK?
+ cba.SetCommandL(2, -1, KNullDesC);
+ );
+#endif
+ realSoftKeyActions.clear();
+}
+
+QString QSoftKeyManagerPrivateS60::softkeyText(QAction &softkeyAction)
+{
+ // In S60 softkeys and menu items do not support key accelerators (i.e.
+ // CTRL+X). Therefore, removing the accelerator characters from both softkey
+ // and menu item texts.
+ const int underlineShortCut = QApplication::style()->styleHint(QStyle::SH_UnderlineShortcut);
+ QString iconText = softkeyAction.iconText();
+ return underlineShortCut ? softkeyAction.text() : iconText;
+}
+
+QAction *QSoftKeyManagerPrivateS60::highestPrioritySoftkey(QAction::SoftKeyRole role)
+{
+ QAction *ret = NULL;
+ // Priority look up is two level
+ // 1. First widget with softkeys always has highest priority
+ for (int level = 0; !ret; level++) {
+ // 2. Highest priority action within widget
+ QList<QAction*> actions = requestedSoftKeyActions.values(level);
+ if (actions.isEmpty())
+ break;
+ qSort(actions.begin(), actions.end(), QSoftKeyManagerPrivateS60::actionPriorityMoreThan);
+ foreach (QAction *action, actions) {
+ if (action->softKeyRole() == role) {
+ ret = action;
+ break;
+ }
+ }
+ }
+ return ret;
+}
+
+bool QSoftKeyManagerPrivateS60::actionPriorityMoreThan(const QAction *firstItem, const QAction *secondItem)
+{
+ return firstItem->priority() > secondItem->priority();
+}
+
+void QSoftKeyManagerPrivateS60::setNativeSoftkey(CEikButtonGroupContainer &cba,
+ TInt position, TInt command, const TDesC &text)
+{
+ // Calling SetCommandL causes CBA redraw
+ QT_TRAP_THROWING(cba.SetCommandL(position, command, text));
+}
+
+QPoint QSoftKeyManagerPrivateS60::softkeyIconPosition(int position, QSize sourceSize, QSize targetSize)
+{
+ QPoint iconPosition(0,0);
+ switch( AknLayoutUtils::CbaLocation() )
+ {
+ case AknLayoutUtils::EAknCbaLocationBottom:
+ // RSK must be moved to right, LSK in on correct position by default
+ if (position == RSK_POSITION)
+ iconPosition.setX(targetSize.width() - sourceSize.width());
+ break;
+ case AknLayoutUtils::EAknCbaLocationRight:
+ case AknLayoutUtils::EAknCbaLocationLeft:
+ // Already in correct position
+ default:
+ break;
+ }
+
+ // Align horizontally to center
+ iconPosition.setY((targetSize.height() - sourceSize.height()) >> 1);
+ return iconPosition;
+}
+
+QPixmap QSoftKeyManagerPrivateS60::prepareSoftkeyPixmap(QPixmap src, int position, QSize targetSize)
+{
+ QPixmap target(targetSize);
+ target.fill(Qt::transparent);
+ QPainter p;
+ p.begin(&target);
+ p.drawPixmap(softkeyIconPosition(position, src.size(), targetSize), src);
+ p.end();
+ return target;
+}
+
+bool QSoftKeyManagerPrivateS60::isOrientationLandscape()
+{
+ // Hard to believe that there is no public API in S60 to
+ // get current orientation. This workaround works with currently supported resolutions
+ return S60->screenHeightInPixels < S60->screenWidthInPixels;
+}
+
+QSize QSoftKeyManagerPrivateS60::cbaIconSize(CEikButtonGroupContainer *cba, int position)
+{
+
+ int index = position;
+ index += isOrientationLandscape() ? 0 : 1;
+ if(cachedCbaIconSize[index].isNull()) {
+ // Only way I figured out to get CBA icon size without RnD SDK, was
+ // to set some dummy icon to CBA first and then ask CBA button CCoeControl::Size()
+ // The returned value is cached to avoid unnecessary icon setting every time.
+ const bool left = (position == LSK_POSITION);
+ if(position == LSK_POSITION || position == RSK_POSITION) {
+ CEikImage* tmpImage = NULL;
+ QT_TRAP_THROWING(tmpImage = new (ELeave) CEikImage);
+ EikSoftkeyImage::SetImage(cba, *tmpImage, left); // Takes myimage ownership
+ int command = S60_COMMAND_START + position;
+ setNativeSoftkey(*cba, position, command, KNullDesC());
+ cachedCbaIconSize[index] = qt_TSize2QSize(cba->ControlOrNull(command)->Size());
+ EikSoftkeyImage::SetLabel(cba, left);
+
+ if(cachedCbaIconSize[index] == QSize(138,72)) {
+ // Hack for S60 5.0 (5800) landscape orientation, which return wrong icon size
+ cachedCbaIconSize[index] = QSize(60,60);
+ }
+ }
+ }
+
+ return cachedCbaIconSize[index];
+}
+
+bool QSoftKeyManagerPrivateS60::setSoftkeyImage(CEikButtonGroupContainer *cba,
+ QAction &action, int position)
+{
+ bool ret = false;
+
+ const bool left = (position == LSK_POSITION);
+ if(position == LSK_POSITION || position == RSK_POSITION) {
+ QIcon icon = action.icon();
+ if (!icon.isNull()) {
+ // Get size of CBA icon area based on button position and orientation
+ QSize requiredIconSize = cbaIconSize(cba, position);
+ // Get pixmap out of icon based on preferred size, the aspect ratio is kept
+ QPixmap pmWihtAspectRatio = icon.pixmap(requiredIconSize);
+ // Native softkeys require that pixmap size is exactly the same as requiredIconSize
+ // prepareSoftkeyPixmap creates a new pixmap with requiredIconSize and blits the 'pmWihtAspectRatio'
+ // to correct location of it
+ QPixmap softkeyPixmap = prepareSoftkeyPixmap(pmWihtAspectRatio, position, requiredIconSize);
+
+ QPixmap softkeyAlpha = softkeyPixmap.alphaChannel();
+ // Alpha channel in 5.1 and older devices need to be inverted
+ // TODO: Switch to use toSymbianCFbsBitmap with invert when available
+ if(QSysInfo::s60Version() <= QSysInfo::SV_S60_5_1) {
+ QImage alphaImage = softkeyAlpha.toImage();
+ alphaImage.invertPixels();
+ softkeyAlpha = QPixmap::fromImage(alphaImage);
+ }
+
+ CFbsBitmap* nBitmap = softkeyPixmap.toSymbianCFbsBitmap();
+ CFbsBitmap* nMask = softkeyAlpha.toSymbianCFbsBitmap();
+
+ CEikImage* myimage = new (ELeave) CEikImage;
+ myimage->SetPicture( nBitmap, nMask ); // nBitmap and nMask ownership transferred
+
+ EikSoftkeyImage::SetImage(cba, *myimage, left); // Takes myimage ownership
+ cbaHasImage[position] = true;
+ ret = true;
+ } else {
+ // Restore softkey to text based
+ if (cbaHasImage[position]) {
+ EikSoftkeyImage::SetLabel(cba, left);
+ cbaHasImage[position] = false;
+ }
+ }
+ }
+ return ret;
+}
+
+bool QSoftKeyManagerPrivateS60::setSoftkey(CEikButtonGroupContainer &cba,
+ QAction::SoftKeyRole role, int position)
+{
+ QAction *action = highestPrioritySoftkey(role);
+ if (action) {
+ setSoftkeyImage(&cba, *action, position);
+ QString text = softkeyText(*action);
+ TPtrC nativeText = qt_QString2TPtrC(text);
+ int command = S60_COMMAND_START + position;
+#ifdef SYMBIAN_VERSION_SYMBIAN3
+ if (softKeyCommandActions.contains(action))
+ command = softKeyCommandActions.value(action);
+#endif
+ setNativeSoftkey(cba, position, command, nativeText);
+ const bool dimmed = !action->isEnabled() && !QSoftKeyManager::isForceEnabledInSofkeys(action);
+ cba.DimCommand(command, dimmed);
+ realSoftKeyActions.insert(command, action);
+ return true;
+ }
+ return false;
+}
+
+bool QSoftKeyManagerPrivateS60::setLeftSoftkey(CEikButtonGroupContainer &cba)
+{
+ return setSoftkey(cba, QAction::PositiveSoftKey, LSK_POSITION);
+}
+
+bool QSoftKeyManagerPrivateS60::setMiddleSoftkey(CEikButtonGroupContainer &cba)
+{
+ // Note: In order to get MSK working, application has to have EAknEnableMSK flag set
+ // Currently it is not possible very easily)
+ // For more information see: http://wiki.forum.nokia.com/index.php/Middle_softkey_usage
+ return setSoftkey(cba, QAction::SelectSoftKey, MSK_POSITION);
+}
+
+bool QSoftKeyManagerPrivateS60::setRightSoftkey(CEikButtonGroupContainer &cba)
+{
+ if (!setSoftkey(cba, QAction::NegativeSoftKey, RSK_POSITION)) {
+ const Qt::WindowType windowType = initialSoftKeySource
+ ? initialSoftKeySource->window()->windowType() : Qt::Window;
+ if (windowType != Qt::Dialog && windowType != Qt::Popup) {
+ QString text(QSoftKeyManager::tr("Exit"));
+ TPtrC nativeText = qt_QString2TPtrC(text);
+ if (cbaHasImage[RSK_POSITION]) {
+ EikSoftkeyImage::SetLabel(&cba, false);
+ cbaHasImage[RSK_POSITION] = false;
+ }
+ setNativeSoftkey(cba, RSK_POSITION, EAknSoftkeyExit, nativeText);
+ cba.DimCommand(EAknSoftkeyExit, false);
+ return true;
+ }
+ }
+ return false;
+}
+
+void QSoftKeyManagerPrivateS60::setSoftkeys(CEikButtonGroupContainer &cba)
+{
+ int requestedSoftkeyCount = requestedSoftKeyActions.count();
+ const int maxSoftkeyCount = 2; // TODO: differs based on orientation ans S60 versions (some have MSK)
+ if (requestedSoftkeyCount > maxSoftkeyCount) {
+ // We have more softkeys than available slots
+ // Put highest priority negative action to RSK and Options menu with rest of softkey actions to LSK
+ // TODO: Build menu
+ setLeftSoftkey(cba);
+ if(AknLayoutUtils::MSKEnabled())
+ setMiddleSoftkey(cba);
+ setRightSoftkey(cba);
+ } else {
+ // We have less softkeys than available slots
+ // Put softkeys to request slots based on role
+ setLeftSoftkey(cba);
+ if(AknLayoutUtils::MSKEnabled())
+ setMiddleSoftkey(cba);
+ setRightSoftkey(cba);
+ }
+}
+
+void QSoftKeyManagerPrivateS60::updateSoftKeys_sys()
+{
+ if (skipCbaUpdate())
+ return;
+
+ CEikButtonGroupContainer *nativeContainer = S60->buttonGroupContainer();
+ Q_ASSERT_X(nativeContainer, Q_FUNC_INFO, "Native CBA does not exist!");
+ ensureCbaVisibilityAndResponsiviness(*nativeContainer);
+ clearSoftkeys(*nativeContainer);
+ setSoftkeys(*nativeContainer);
+
+ nativeContainer->DrawDeferred(); // 3.1 needs an extra invitation
+}
+
+static void resetMenuBeingConstructed(TAny* /*aAny*/)
+{
+ S60->menuBeingConstructed = false;
+}
+
+void QSoftKeyManagerPrivateS60::tryDisplayMenuBarL()
+{
+ CleanupStack::PushL(TCleanupItem(resetMenuBeingConstructed, NULL));
+ S60->menuBeingConstructed = true;
+ S60->menuBar()->TryDisplayMenuBarL();
+ CleanupStack::PopAndDestroy(); // Reset menuBeingConstructed to false in all cases
+}
+
+bool QSoftKeyManagerPrivateS60::handleCommand(int command)
+{
+ QAction *action = realSoftKeyActions.value(command);
+ if (action) {
+ bool property = QActionPrivate::get(action)->menuActionSoftkeys;
+ if (property) {
+ QT_TRAP_THROWING(tryDisplayMenuBarL());
+ } else if (action->menu()) {
+ // TODO: This is hack, in order to use exising QMenuBar implementation for Symbian
+ // menubar needs to have widget to which it is associated. Since we want to associate
+ // menubar to action (which is inherited from QObject), we create and associate QWidget
+ // to action and pass that for QMenuBar. This associates the menubar to action, and we
+ // can have own menubar for each action.
+ QWidget *actionContainer = action->property("_q_action_widget").value<QWidget*>();
+ if(!actionContainer) {
+ actionContainer = new QWidget(action->parentWidget());
+ QMenuBar *menuBar = new QMenuBar(actionContainer);
+ foreach(QAction *menuAction, action->menu()->actions()) {
+ QMenu *menu = menuAction->menu();
+ if(menu)
+ menuBar->addMenu(menu);
+ else
+ menuBar->addAction(menuAction);
+ }
+ QVariant v;
+ v.setValue(actionContainer);
+ action->setProperty("_q_action_widget", v);
+ }
+ qt_symbian_next_menu_from_action(actionContainer);
+ QT_TRAP_THROWING(tryDisplayMenuBarL());
+ }
+
+ Q_ASSERT(action->softKeyRole() != QAction::NoSoftKey);
+ QWidget *actionParent = action->parentWidget();
+ Q_ASSERT_X(actionParent, Q_FUNC_INFO, "No parent set for softkey action!");
+ if (actionParent->isEnabled()) {
+ action->activate(QAction::Trigger);
+ return true;
+ }
+ }
+ return false;
+}
+
+QT_END_NAMESPACE
+#endif //QT_NO_SOFTKEYMANAGER
diff --git a/src/widgets/platforms/s60/qsoftkeymanager_s60_p.h b/src/widgets/platforms/s60/qsoftkeymanager_s60_p.h
new file mode 100644
index 0000000000..a16a8ca2ac
--- /dev/null
+++ b/src/widgets/platforms/s60/qsoftkeymanager_s60_p.h
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSOFTKEYMANAGER_S60_P_H
+#define QSOFTKEYMANAGER_S60_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qbitarray.h"
+#include "private/qobject_p.h"
+#include "private/qsoftkeymanager_common_p.h"
+
+QT_BEGIN_HEADER
+
+#ifndef QT_NO_SOFTKEYMANAGER
+
+QT_BEGIN_NAMESPACE
+
+class CEikButtonGroupContainer;
+class QAction;
+
+class QSoftKeyManagerPrivateS60 : public QSoftKeyManagerPrivate
+{
+ Q_DECLARE_PUBLIC(QSoftKeyManager)
+
+public:
+ QSoftKeyManagerPrivateS60();
+
+public:
+ void updateSoftKeys_sys();
+ bool handleCommand(int command);
+
+private:
+ void tryDisplayMenuBarL();
+ bool skipCbaUpdate();
+ void ensureCbaVisibilityAndResponsiviness(CEikButtonGroupContainer &cba);
+ void clearSoftkeys(CEikButtonGroupContainer &cba);
+ QString softkeyText(QAction &softkeyAction);
+ QAction *highestPrioritySoftkey(QAction::SoftKeyRole role);
+ static bool actionPriorityMoreThan(const QAction* item1, const QAction* item2);
+ void setNativeSoftkey(CEikButtonGroupContainer &cba, TInt position, TInt command, const TDesC& text);
+ QPoint softkeyIconPosition(int position, QSize sourceSize, QSize targetSize);
+ QPixmap prepareSoftkeyPixmap(QPixmap src, int position, QSize targetSize);
+ bool isOrientationLandscape();
+ QSize cbaIconSize(CEikButtonGroupContainer *cba, int position);
+ bool setSoftkeyImage(CEikButtonGroupContainer *cba, QAction &action, int position);
+ bool setSoftkey(CEikButtonGroupContainer &cba, QAction::SoftKeyRole role, int position);
+ bool setLeftSoftkey(CEikButtonGroupContainer &cba);
+ bool setMiddleSoftkey(CEikButtonGroupContainer &cba);
+ bool setRightSoftkey(CEikButtonGroupContainer &cba);
+ void setSoftkeys(CEikButtonGroupContainer &cba);
+
+private:
+ QHash<int, QAction*> realSoftKeyActions;
+ QSize cachedCbaIconSize[4];
+ QBitArray cbaHasImage;
+};
+
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_SOFTKEYMANAGER
+
+QT_END_HEADER
+
+#endif // QSOFTKEYMANAGER_S60_P_H
diff --git a/src/widgets/platforms/s60/qsound_s60.cpp b/src/widgets/platforms/s60/qsound_s60.cpp
new file mode 100644
index 0000000000..1ed2ef3131
--- /dev/null
+++ b/src/widgets/platforms/s60/qsound_s60.cpp
@@ -0,0 +1,224 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+
+#ifndef QT_NO_SOUND
+
+#include "qdir.h"
+#include "qapplication.h"
+#include "qsound.h"
+#include "qsound_p.h"
+#include "qfileinfo.h"
+#include <private/qcore_symbian_p.h>
+
+#include <e32std.h>
+#include <mdaaudiosampleplayer.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAuServerS60;
+
+class QAuBucketS60 : public QAuBucket, public MMdaAudioPlayerCallback
+{
+public:
+ QAuBucketS60(QAuServerS60 *server, QSound *sound);
+ ~QAuBucketS60();
+
+ void play();
+ void stop();
+
+ inline QSound *sound() const { return m_sound; }
+
+public: // from MMdaAudioPlayerCallback
+ void MapcInitComplete(TInt aError, const TTimeIntervalMicroSeconds& aDuration);
+ void MapcPlayComplete(TInt aError);
+
+private:
+ QSound *m_sound;
+ QAuServerS60 *m_server;
+ bool m_prepared;
+ bool m_playCalled;
+ CMdaAudioPlayerUtility *m_playUtility;
+};
+
+
+class QAuServerS60 : public QAuServer
+{
+public:
+ QAuServerS60(QObject *parent);
+
+ void init(QSound *s)
+ {
+ QAuBucketS60 *bucket = new QAuBucketS60(this, s);
+ setBucket(s, bucket);
+ }
+
+ void play(QSound *s)
+ {
+ bucket(s)->play();
+ }
+
+ void stop(QSound *s)
+ {
+ bucket(s)->stop();
+ }
+
+ bool okay() { return true; }
+
+ void play(const QString& filename);
+
+protected:
+ void playCompleted(QAuBucketS60 *bucket, int error);
+
+protected:
+ QAuBucketS60 *bucket(QSound *s)
+ {
+ return (QAuBucketS60 *)QAuServer::bucket( s );
+ }
+
+ friend class QAuBucketS60;
+
+ // static QSound::play(filename) cannot be stopped, meaning that playCompleted
+ // will get always called and QSound gets removed form this list.
+ QList<QSound *> staticPlayingSounds;
+};
+
+QAuServerS60::QAuServerS60(QObject *parent) :
+ QAuServer(parent)
+{
+ setObjectName(QLatin1String("QAuServerS60"));
+}
+
+void QAuServerS60::play(const QString& filename)
+{
+ QSound *s = new QSound(filename);
+ staticPlayingSounds.append(s);
+ play(s);
+}
+
+void QAuServerS60::playCompleted(QAuBucketS60 *bucket, int error)
+{
+ QSound *sound = bucket->sound();
+ if (!error) {
+ // We need to handle repeats by ourselves, since with Symbian API we don't
+ // know how many loops have been played when user asks it
+ if (decLoop(sound)) {
+ play(sound);
+ } else {
+ if (staticPlayingSounds.removeAll(sound))
+ delete sound;
+ }
+ } else {
+ // We don't have a way to inform about errors -> just decrement loops
+ // in order that QSound::isFinished will return true;
+ while (decLoop(sound) > 0) {}
+ if (staticPlayingSounds.removeAll(sound))
+ delete sound;
+ }
+}
+
+QAuServer *qt_new_audio_server()
+{
+ return new QAuServerS60(qApp);
+}
+
+QAuBucketS60::QAuBucketS60(QAuServerS60 *server, QSound *sound)
+ : m_sound(sound), m_server(server), m_prepared(false), m_playCalled(false)
+{
+ QString filepath = QFileInfo(m_sound->fileName()).absoluteFilePath();
+ filepath = QDir::toNativeSeparators(filepath);
+ TPtrC filepathPtr(qt_QString2TPtrC(filepath));
+ TRAPD(err, m_playUtility = CMdaAudioPlayerUtility::NewL(*this);
+ m_playUtility->OpenFileL(filepathPtr));
+ if (err) {
+ m_server->playCompleted(this, err);
+ }
+}
+
+void QAuBucketS60::play()
+{
+ if (m_prepared) {
+ // OpenFileL call is completed we can start playing immediately
+ m_playUtility->Play();
+ } else {
+ m_playCalled = true;
+ }
+
+}
+
+void QAuBucketS60::stop()
+{
+ m_playCalled = false;
+ m_playUtility->Stop();
+}
+
+void QAuBucketS60::MapcPlayComplete(TInt aError)
+{
+ m_server->playCompleted(this, aError);
+}
+
+void QAuBucketS60::MapcInitComplete(TInt aError, const TTimeIntervalMicroSeconds& /*aDuration*/)
+{
+ if (aError) {
+ m_server->playCompleted(this, aError);
+ } else {
+ m_prepared = true;
+ if (m_playCalled){
+ play();
+ }
+ }
+}
+
+QAuBucketS60::~QAuBucketS60()
+{
+ if (m_playUtility){
+ m_playUtility->Stop();
+ m_playUtility->Close();
+ }
+
+ delete m_playUtility;
+}
+
+
+#endif // QT_NO_SOUND
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/s60/qt_s60_p.h b/src/widgets/platforms/s60/qt_s60_p.h
new file mode 100644
index 0000000000..aa60d16fab
--- /dev/null
+++ b/src/widgets/platforms/s60/qt_s60_p.h
@@ -0,0 +1,651 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT_S60_P_H
+#define QT_S60_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtGui/qwindowdefs.h"
+#include "private/qcore_symbian_p.h"
+#include "qhash.h"
+#include "qpoint.h"
+#include "QtGui/qfont.h"
+#include "QtGui/qimage.h"
+#include "QtGui/qevent.h"
+#include "qpointer.h"
+#include "qapplication.h"
+#include "qelapsedtimer.h"
+#include "QtCore/qthreadstorage.h"
+#include "qwidget_p.h"
+#include <w32std.h>
+#include <coecntrl.h>
+#include <eikenv.h>
+#include <eikappui.h>
+
+#ifdef Q_WS_S60
+#include <AknUtils.h> // AknLayoutUtils
+#include <avkon.hrh> // EEikStatusPaneUidTitle
+#include <akntitle.h> // CAknTitlePane
+#include <akncontext.h> // CAknContextPane
+#include <eikspane.h> // CEikStatusPane
+#include <AknPopupFader.h> // MAknFadedComponent and TAknPopupFader
+#include <gfxtranseffect/gfxtranseffect.h> // BeginFullScreen
+#ifdef QT_SYMBIAN_HAVE_AKNTRANSEFFECT_H
+#include <akntranseffect.h> // BeginFullScreen
+#endif
+#endif
+
+QT_BEGIN_NAMESPACE
+
+// Application internal HandleResourceChangeL events,
+// system events seems to start with 0x10
+const TInt KInternalStatusPaneChange = 0x50000000;
+
+// For BeginFullScreen().
+const TUint KQtAppExitFlag = 0x400;
+
+static const int qt_symbian_max_screens = 4;
+
+//this macro exists because EColor16MAP enum value doesn't exist in Symbian OS 9.2
+#define Q_SYMBIAN_ECOLOR16MAP TDisplayMode(13)
+
+class QSymbianTypeFaceExtras;
+typedef QHash<QString, const QSymbianTypeFaceExtras *> QSymbianTypeFaceExtrasHash;
+typedef void (*QThreadLocalReleaseFunc)();
+
+class Q_AUTOTEST_EXPORT QS60ThreadLocalData
+{
+public:
+ QS60ThreadLocalData();
+ ~QS60ThreadLocalData();
+ bool usingCONEinstances;
+ RWsSession wsSession;
+ CWsScreenDevice *screenDevice;
+ QSymbianTypeFaceExtrasHash fontData;
+ QVector<QThreadLocalReleaseFunc> releaseFuncs;
+};
+
+class QS60Data
+{
+public:
+ QS60Data();
+ QThreadStorage<QS60ThreadLocalData *> tls;
+ TUid uid;
+ int screenDepth;
+ QPoint lastCursorPos;
+ QPoint lastPointerEventPos;
+ QPointer<QWidget> lastPointerEventTarget;
+ QPointer<QWidget> mousePressTarget;
+ int screenWidthInPixels;
+ int screenHeightInPixels;
+ int screenWidthInTwips;
+ int screenHeightInTwips;
+ int defaultDpiX;
+ int defaultDpiY;
+ WId curWin;
+ enum PressedKeys {
+ Select = 0x1,
+ Right = 0x2,
+ Down = 0x4,
+ Left = 0x8,
+ Up = 0x10,
+ LeftUp = 0x20,
+ RightUp = 0x40,
+ RightDown = 0x80,
+ LeftDown = 0x100
+ };
+ int virtualMousePressedKeys; // of the above type, but avoids casting problems
+ int virtualMouseAccelDX;
+ int virtualMouseAccelDY;
+ QElapsedTimer virtualMouseAccelTimeout;
+ int virtualMouseMaxAccel;
+#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS
+ int brokenPointerCursors : 1;
+#endif
+ int hasTouchscreen : 1;
+ int mouseInteractionEnabled : 1;
+ int virtualMouseRequired : 1;
+ int qtOwnsS60Environment : 1;
+ int supportsPremultipliedAlpha : 1;
+ int avkonComponentsSupportTransparency : 1;
+ int menuBeingConstructed : 1;
+ int orientationSet : 1;
+ int partial_keyboard : 1;
+ QApplication::QS60MainApplicationFactory s60ApplicationFactory; // typedef'ed pointer type
+ QPointer<QWidget> splitViewLastWidget;
+
+ static CEikButtonGroupContainer *cba;
+
+ enum ScanCodeState {
+ Unpressed,
+ KeyDown,
+ KeyDownAndKey
+ };
+ QHash<TInt, ScanCodeState> scanCodeStates;
+
+ static inline void updateScreenSize();
+ inline RWsSession& wsSession();
+ static inline int screenCount();
+ static inline RWindowGroup& windowGroup();
+ static inline RWindowGroup& windowGroup(const QWidget *widget);
+ static inline RWindowGroup& windowGroup(int screenNumber);
+ inline CWsScreenDevice* screenDevice();
+ inline CWsScreenDevice* screenDevice(const QWidget *widget);
+ inline CWsScreenDevice* screenDevice(int screenNumber);
+ static inline int screenNumberForWidget(const QWidget *widget);
+ inline QSymbianTypeFaceExtrasHash& fontData();
+ inline void addThreadLocalReleaseFunc(QThreadLocalReleaseFunc func);
+ static inline CCoeAppUi* appUi();
+ static inline CEikMenuBar* menuBar();
+#ifdef Q_WS_S60
+ static inline CEikStatusPane* statusPane();
+ static inline CCoeControl* statusPaneSubPane(TInt aPaneId);
+ static inline CAknTitlePane* titlePane();
+ static inline CAknContextPane* contextPane();
+ static inline CEikButtonGroupContainer* buttonGroupContainer();
+ static inline void setButtonGroupContainer(CEikButtonGroupContainer* newCba);
+ static void setStatusPaneAndButtonGroupVisibility(bool statusPaneVisible, bool buttonGroupVisible);
+ static bool setRecursiveDecorationsVisibility(QWidget *window, Qt::WindowStates newState);
+#endif
+ static void controlVisibilityChanged(CCoeControl *control, bool visible);
+
+#ifdef Q_OS_SYMBIAN
+ TTrapHandler *s60InstalledTrapHandler;
+#endif
+
+ int screenWidthInPixelsForScreen[qt_symbian_max_screens];
+ int screenHeightInPixelsForScreen[qt_symbian_max_screens];
+ int screenWidthInTwipsForScreen[qt_symbian_max_screens];
+ int screenHeightInTwipsForScreen[qt_symbian_max_screens];
+
+ int nativeScreenWidthInPixels;
+ int nativeScreenHeightInPixels;
+
+ int beginFullScreenCalled : 1;
+ int endFullScreenCalled : 1;
+};
+
+Q_AUTOTEST_EXPORT QS60Data* qGlobalS60Data();
+#define S60 qGlobalS60Data()
+
+class QAbstractLongTapObserver
+{
+public:
+ virtual void HandleLongTapEventL( const TPoint& aPenEventLocation,
+ const TPoint& aPenEventScreenLocation ) = 0;
+};
+class QLongTapTimer;
+
+
+class QSymbianControl : public CCoeControl, public QAbstractLongTapObserver
+#ifdef Q_WS_S60
+, public MAknFadedComponent, public MEikStatusPaneObserver
+#endif
+{
+public:
+ DECLARE_TYPE_ID(0x51740000) // Fun fact: the two first values are "Qt" in ASCII.
+
+public:
+ QSymbianControl(QWidget *w);
+ void ConstructL(bool isWindowOwning = false, bool desktop = false);
+ ~QSymbianControl();
+ void HandleResourceChange(int resourceType);
+ void HandlePointerEventL(const TPointerEvent& aPointerEvent);
+ TKeyResponse OfferKeyEventL(const TKeyEvent& aKeyEvent,TEventCode aType);
+#if !defined(QT_NO_IM) && defined(Q_WS_S60)
+ TCoeInputCapabilities InputCapabilities() const;
+#endif
+ TTypeUid::Ptr MopSupplyObject(TTypeUid id);
+
+ inline QWidget* widget() const { return qwidget; }
+ void setWidget(QWidget *w);
+ void sendInputEvent(QWidget *widget, QInputEvent *inputEvent);
+ void setIgnoreFocusChanged(bool enabled) { m_ignoreFocusChanged = enabled; }
+ void CancelLongTapTimer();
+
+ void setFocusSafely(bool focus);
+
+ bool isControlActive();
+
+ void ensureFixNativeOrientation();
+ QPoint translatePointForFixedNativeOrientation(const TPoint &pointerEventPos) const;
+ TRect translateRectForFixedNativeOrientation(const TRect &controlRect) const;
+
+#ifdef Q_WS_S60
+ void FadeBehindPopup(bool fade){ popupFader.FadeBehindPopup( this, this, fade); }
+ void HandleStatusPaneSizeChange();
+
+protected: // from MAknFadedComponent
+ TInt CountFadedComponents() {return 1;}
+ CCoeControl* FadedComponent(TInt /*aIndex*/) {return this;}
+#else
+ // #warning No fallback implementation for QSymbianControl::FadeBehindPopup
+ void FadeBehindPopup(bool /*fade*/){ }
+#endif
+
+protected:
+ void Draw(const TRect& aRect) const;
+ void SizeChanged();
+ void PositionChanged();
+ void FocusChanged(TDrawNow aDrawNow);
+
+protected:
+ void qwidgetResize_helper(const QSize &newSize);
+
+private:
+ void HandlePointerEvent(const TPointerEvent& aPointerEvent);
+ TKeyResponse OfferKeyEvent(const TKeyEvent& aKeyEvent,TEventCode aType);
+ TKeyResponse sendSymbianKeyEvent(const TKeyEvent &keyEvent, QEvent::Type type);
+ TKeyResponse sendKeyEvent(QWidget *widget, QKeyEvent *keyEvent);
+ TKeyResponse handleVirtualMouse(const TKeyEvent& keyEvent,TEventCode type);
+ bool sendMouseEvent(QWidget *widget, QMouseEvent *mEvent);
+ void sendMouseEvent(
+ QWidget *receiver,
+ QEvent::Type type,
+ const QPoint &globalPos,
+ Qt::MouseButton button,
+ Qt::KeyboardModifiers modifiers);
+ void processTouchEvent(int pointerNumber, TPointerEvent::TType type, QPointF screenPos, qreal pressure);
+ void HandleLongTapEventL( const TPoint& aPenEventLocation, const TPoint& aPenEventScreenLocation );
+#ifdef QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER
+ void translateAdvancedPointerEvent(const TAdvancedPointerEvent *event);
+#endif
+ bool isSplitViewWidget(QWidget *widget);
+
+public:
+ void handleClientAreaChange();
+
+private:
+ static QSymbianControl *lastFocusedControl;
+
+private:
+ QWidget *qwidget;
+ QLongTapTimer* m_longTapDetector;
+ QElapsedTimer m_doubleClickTimer;
+ bool m_ignoreFocusChanged : 1;
+ bool m_symbianPopupIsOpen : 1;
+
+#ifdef Q_WS_S60
+ // Fader object used to fade everything except this menu and the CBA.
+ TAknPopupFader popupFader;
+#endif
+
+ bool m_inExternalScreenOverride : 1;
+ bool m_lastStatusPaneVisibility : 1;
+};
+
+inline QS60Data::QS60Data()
+: uid(TUid::Null()),
+ screenDepth(0),
+ screenWidthInPixels(0),
+ screenHeightInPixels(0),
+ screenWidthInTwips(0),
+ screenHeightInTwips(0),
+ defaultDpiX(0),
+ defaultDpiY(0),
+ curWin(0),
+ virtualMousePressedKeys(0),
+ virtualMouseAccelDX(0),
+ virtualMouseAccelDY(0),
+ virtualMouseMaxAccel(0),
+#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS
+ brokenPointerCursors(0),
+#endif
+ hasTouchscreen(0),
+ mouseInteractionEnabled(0),
+ virtualMouseRequired(0),
+ qtOwnsS60Environment(0),
+ supportsPremultipliedAlpha(0),
+ avkonComponentsSupportTransparency(0),
+ menuBeingConstructed(0),
+ orientationSet(0),
+ partial_keyboard(0),
+ s60ApplicationFactory(0)
+#ifdef Q_OS_SYMBIAN
+ ,s60InstalledTrapHandler(0)
+#endif
+ ,beginFullScreenCalled(0),
+ endFullScreenCalled(0)
+{
+}
+
+inline void QS60Data::updateScreenSize()
+{
+ CWsScreenDevice *dev = S60->screenDevice();
+ int screenModeCount = dev->NumScreenModes();
+ int mode = dev->CurrentScreenMode();
+ TPixelsTwipsAndRotation params;
+ dev->GetScreenModeSizeAndRotation(mode, params);
+ S60->screenWidthInPixels = params.iPixelSize.iWidth;
+ S60->screenHeightInPixels = params.iPixelSize.iHeight;
+ S60->screenWidthInTwips = params.iTwipsSize.iWidth;
+ S60->screenHeightInTwips = params.iTwipsSize.iHeight;
+
+ S60->virtualMouseMaxAccel = qMax(S60->screenHeightInPixels, S60->screenWidthInPixels) / 10;
+
+ TReal inches = S60->screenHeightInTwips / (TReal)KTwipsPerInch;
+ S60->defaultDpiY = S60->screenHeightInPixels / inches;
+ inches = S60->screenWidthInTwips / (TReal)KTwipsPerInch;
+ S60->defaultDpiX = S60->screenWidthInPixels / inches;
+
+ int screens = S60->screenCount();
+ for (int i = 0; i < screens; ++i) {
+ CWsScreenDevice *dev = S60->screenDevice(i);
+ mode = dev->CurrentScreenMode();
+ dev->GetScreenModeSizeAndRotation(mode, params);
+ S60->screenWidthInPixelsForScreen[i] = params.iPixelSize.iWidth;
+ S60->screenHeightInPixelsForScreen[i] = params.iPixelSize.iHeight;
+ S60->screenWidthInTwipsForScreen[i] = params.iTwipsSize.iWidth;
+ S60->screenHeightInTwipsForScreen[i] = params.iTwipsSize.iHeight;
+ }
+
+ // Look for a screen mode with rotation 0
+ // in order to decide what the native orientation is.
+ for (mode = 0; mode < screenModeCount; ++mode) {
+ TPixelsAndRotation sizeAndRotation;
+ dev->GetScreenModeSizeAndRotation(mode, sizeAndRotation);
+ if (sizeAndRotation.iRotation == CFbsBitGc::EGraphicsOrientationNormal) {
+ S60->nativeScreenWidthInPixels = sizeAndRotation.iPixelSize.iWidth;
+ S60->nativeScreenHeightInPixels = sizeAndRotation.iPixelSize.iHeight;
+ break;
+ }
+ }
+}
+
+inline RWsSession& QS60Data::wsSession()
+{
+ if(!tls.hasLocalData()) {
+ tls.setLocalData(new QS60ThreadLocalData);
+ }
+ return tls.localData()->wsSession;
+}
+
+inline int QS60Data::screenCount()
+{
+#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS)
+ CCoeEnv *env = CCoeEnv::Static();
+ if (env) {
+ return qMin(env->WsSession().NumberOfScreens(), qt_symbian_max_screens);
+ }
+#endif
+ return 1;
+}
+
+inline RWindowGroup& QS60Data::windowGroup()
+{
+ return CCoeEnv::Static()->RootWin();
+}
+
+inline RWindowGroup& QS60Data::windowGroup(const QWidget *widget)
+{
+ return windowGroup(screenNumberForWidget(widget));
+}
+
+inline RWindowGroup& QS60Data::windowGroup(int screenNumber)
+{
+#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS)
+ RWindowGroup *wg = CCoeEnv::Static()->RootWin(screenNumber);
+ return wg ? *wg : windowGroup();
+#else
+ Q_UNUSED(screenNumber);
+ return windowGroup();
+#endif
+}
+
+inline CWsScreenDevice* QS60Data::screenDevice()
+{
+ if(!tls.hasLocalData()) {
+ tls.setLocalData(new QS60ThreadLocalData);
+ }
+ return tls.localData()->screenDevice;
+}
+
+inline CWsScreenDevice* QS60Data::screenDevice(const QWidget *widget)
+{
+ return screenDevice(screenNumberForWidget(widget));
+}
+
+inline CWsScreenDevice* QS60Data::screenDevice(int screenNumber)
+{
+#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS)
+ CCoeEnv *env = CCoeEnv::Static();
+ if (env) {
+ CWsScreenDevice *dev = env->ScreenDevice(screenNumber);
+ return dev ? dev : screenDevice();
+ } else {
+ return screenDevice();
+ }
+#else
+ return screenDevice();
+#endif
+}
+
+inline int QS60Data::screenNumberForWidget(const QWidget *widget)
+{
+ if (!widget)
+ return 0;
+ const QWidget *w = widget;
+ while (w->parentWidget())
+ w = w->parentWidget();
+ return qt_widget_private(const_cast<QWidget *>(w))->symbianScreenNumber;
+}
+
+inline QSymbianTypeFaceExtrasHash& QS60Data::fontData()
+{
+ if (!tls.hasLocalData()) {
+ tls.setLocalData(new QS60ThreadLocalData);
+ }
+ return tls.localData()->fontData;
+}
+
+inline void QS60Data::addThreadLocalReleaseFunc(QThreadLocalReleaseFunc func)
+{
+ if (!tls.hasLocalData()) {
+ tls.setLocalData(new QS60ThreadLocalData);
+ }
+ QS60ThreadLocalData *data = tls.localData();
+ if (!data->releaseFuncs.contains(func))
+ data->releaseFuncs.append(func);
+}
+
+inline CCoeAppUi* QS60Data::appUi()
+{
+ return CCoeEnv::Static()-> AppUi();
+}
+
+inline CEikMenuBar* QS60Data::menuBar()
+{
+ return CEikonEnv::Static()->AppUiFactory()->MenuBar();
+}
+
+#ifdef Q_WS_S60
+inline CEikStatusPane* QS60Data::statusPane()
+{
+ return CEikonEnv::Static()->AppUiFactory()->StatusPane();
+}
+
+// Returns the application's status pane control, if not present returns NULL.
+inline CCoeControl* QS60Data::statusPaneSubPane( TInt aPaneId )
+{
+ const TUid paneUid = { aPaneId };
+ CEikStatusPane* statusPane = S60->statusPane();
+ if (statusPane && statusPane->PaneCapabilities(paneUid).IsPresent()) {
+ CCoeControl* control = NULL;
+ // ControlL shouldn't leave because the pane is present
+ TRAPD(err, control = statusPane->ControlL(paneUid));
+ return err != KErrNone ? NULL : control;
+ }
+ return NULL;
+}
+
+// Returns the application's title pane, if not present returns NULL.
+inline CAknTitlePane* QS60Data::titlePane()
+{
+ return static_cast<CAknTitlePane*>(S60->statusPaneSubPane(EEikStatusPaneUidTitle));
+}
+
+// Returns the application's title pane, if not present returns NULL.
+inline CAknContextPane* QS60Data::contextPane()
+{
+ return static_cast<CAknContextPane*>(S60->statusPaneSubPane(EEikStatusPaneUidContext));
+}
+
+inline CEikButtonGroupContainer* QS60Data::buttonGroupContainer()
+{
+ return QS60Data::cba;
+}
+
+inline void QS60Data::setButtonGroupContainer(CEikButtonGroupContainer *newCba)
+{
+ QS60Data::cba = newCba;
+}
+#endif // Q_WS_S60
+
+static inline QFont qt_TFontSpec2QFontL(const TFontSpec &fontSpec)
+{
+ return QFont(
+ qt_TDesC2QString(fontSpec.iTypeface.iName),
+ fontSpec.iHeight / KTwipsPerPoint,
+ fontSpec.iFontStyle.StrokeWeight() == EStrokeWeightNormal ? QFont::Normal : QFont::Bold,
+ fontSpec.iFontStyle.Posture() == EPostureItalic
+ );
+}
+
+static inline QImage::Format qt_TDisplayMode2Format(TDisplayMode mode)
+{
+ QImage::Format format;
+ switch(mode) {
+ case EGray2:
+ format = QImage::Format_MonoLSB;
+ break;
+ case EColor256:
+ case EGray256:
+ format = QImage::Format_Indexed8;
+ break;
+ case EColor4K:
+ format = QImage::Format_RGB444;
+ break;
+ case EColor64K:
+ format = QImage::Format_RGB16;
+ break;
+ case EColor16M:
+ format = QImage::Format_RGB888;
+ break;
+ case EColor16MU:
+ format = QImage::Format_RGB32;
+ break;
+ case EColor16MA:
+ format = QImage::Format_ARGB32;
+ break;
+ case Q_SYMBIAN_ECOLOR16MAP:
+ format = QImage::Format_ARGB32_Premultiplied;
+ break;
+ default:
+ format = QImage::Format_Invalid;
+ break;
+ }
+ return format;
+}
+
+#ifndef QT_NO_CURSOR
+void qt_symbian_setWindowCursor(const QCursor &cursor, const CCoeControl* wid);
+void qt_symbian_setWindowGroupCursor(const QCursor &cursor, RWindowTreeNode &node);
+void qt_symbian_setGlobalCursor(const QCursor &cursor);
+void qt_symbian_set_cursor_visible(bool visible);
+bool qt_symbian_is_cursor_visible();
+#endif
+
+static inline bool qt_beginFullScreenEffect()
+{
+#if defined(Q_WS_S60) && defined(QT_SYMBIAN_HAVE_AKNTRANSEFFECT_H)
+ // Only for post-S^3. On earlier versions the system transition effects
+ // may not be able to capture the non-Avkon content, leading to confusing
+ // looking effects, so just skip the whole thing.
+ if (S60->beginFullScreenCalled || QSysInfo::s60Version() <= QSysInfo::SV_S60_5_2)
+ return false;
+ S60->beginFullScreenCalled = true;
+ // For Avkon apps the app-exit effect is triggered from CAknAppUi::PrepareToExit().
+ // That is good for Avkon apps, but in case of Qt the RWindows are destroyed earlier.
+ // Therefore we call BeginFullScreen() ourselves.
+ GfxTransEffect::BeginFullScreen(AknTransEffect::EApplicationExit,
+ TRect(0, 0, 0, 0),
+ AknTransEffect::EParameterType,
+ AknTransEffect::GfxTransParam(S60->uid,
+ AknTransEffect::TParameter::EAvkonCheck | KQtAppExitFlag));
+ return true;
+#else
+ return false;
+#endif
+}
+
+static inline void qt_abortFullScreenEffect()
+{
+#if defined(Q_WS_S60) && defined(QT_SYMBIAN_HAVE_AKNTRANSEFFECT_H)
+ if (!S60->beginFullScreenCalled || QSysInfo::s60Version() <= QSysInfo::SV_S60_5_2)
+ return;
+ GfxTransEffect::AbortFullScreen();
+ S60->beginFullScreenCalled = S60->endFullScreenCalled = false;
+#endif
+}
+
+static inline void qt_endFullScreenEffect()
+{
+#if defined(Q_WS_S60) && defined(QT_SYMBIAN_HAVE_AKNTRANSEFFECT_H)
+ if (S60->endFullScreenCalled || QSysInfo::s60Version() <= QSysInfo::SV_S60_5_2)
+ return;
+ S60->endFullScreenCalled = true;
+ GfxTransEffect::EndFullScreen();
+#endif
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_S60_P_H
diff --git a/src/widgets/platforms/s60/qwidget_s60.cpp b/src/widgets/platforms/s60/qwidget_s60.cpp
new file mode 100644
index 0000000000..d0b1299898
--- /dev/null
+++ b/src/widgets/platforms/s60/qwidget_s60.cpp
@@ -0,0 +1,1464 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwidget_p.h"
+#include "qdesktopwidget.h"
+#include "qapplication.h"
+#include "qapplication_p.h"
+#include "private/qbackingstore_p.h"
+#include "qevent.h"
+#include "qt_s60_p.h"
+
+#include "qbitmap.h"
+#include "private/qwindowsurface_s60_p.h"
+
+#include <qinputcontext.h>
+
+#ifdef Q_WS_S60
+#include <aknappui.h>
+#include <eikbtgpc.h>
+#endif
+
+// This is necessary in order to be able to perform delayed invocation on slots
+// which take arguments of type WId. One example is
+// QWidgetPrivate::_q_delayedDestroy, which is used to delay destruction of
+// CCoeControl objects until after the CONE event handler has finished running.
+Q_DECLARE_METATYPE(WId)
+
+// Workaround for the fact that S60 SDKs 3.x do not contain the akntoolbar.h
+// header, even though the documentation says that it should be there, and indeed
+// it is present in the library.
+class CAknToolbar : public CAknControl,
+ public MCoeControlObserver,
+ public MCoeControlBackground,
+ public MEikCommandObserver,
+ public MAknFadedComponent
+{
+public:
+ IMPORT_C void SetToolbarVisibility(const TBool visible);
+};
+
+QT_BEGIN_NAMESPACE
+
+extern bool qt_nograb();
+
+QWidget *QWidgetPrivate::mouseGrabber = 0;
+QWidget *QWidgetPrivate::keyboardGrabber = 0;
+CEikButtonGroupContainer *QS60Data::cba = 0;
+
+int qt_symbian_create_desktop_on_screen = -1;
+
+static bool isEqual(const QList<QAction*>& a, const QList<QAction*>& b)
+{
+ if ( a.count() != b.count())
+ return false;
+ int index=0;
+ while (index<a.count()) {
+ if (a.at(index)->softKeyRole() != b.at(index)->softKeyRole())
+ return false;
+ if (a.at(index)->text().compare(b.at(index)->text())!=0)
+ return false;
+ index++;
+ }
+ return true;
+}
+
+void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &)
+{
+ // Note: based on x11 implementation
+
+ static const int XCOORD_MAX = 16383;
+ static const int WRECT_MAX = 16383;
+
+ Q_Q(QWidget);
+
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+
+ /*
+ There are up to four different coordinate systems here:
+ Qt coordinate system for this widget.
+ Symbian coordinate system for this widget (relative to wrect).
+ Qt coordinate system for parent
+ Symbian coordinate system for parent (relative to parent's wrect).
+ */
+
+ QRect validRange(-XCOORD_MAX,-XCOORD_MAX, 2*XCOORD_MAX, 2*XCOORD_MAX);
+ QRect wrectRange(-WRECT_MAX,-WRECT_MAX, 2*WRECT_MAX, 2*WRECT_MAX);
+ QRect wrect;
+ //xrect is the Symbian geometry of my widget. (starts out in parent's Qt coord sys, and ends up in parent's Symbian coord sys)
+ QRect xrect = data.crect;
+
+ const QWidget *const parent = q->parentWidget();
+ QRect parentWRect = parent->data->wrect;
+
+ if (parentWRect.isValid()) {
+ // parent is clipped, and we have to clip to the same limit as parent
+ if (!parentWRect.contains(xrect)) {
+ xrect &= parentWRect;
+ wrect = xrect;
+ //translate from parent's to my Qt coord sys
+ wrect.translate(-data.crect.topLeft());
+ }
+ //translate from parent's Qt coords to parent's X coords
+ xrect.translate(-parentWRect.topLeft());
+
+ } else {
+ // parent is not clipped, we may or may not have to clip
+
+ if (data.wrect.isValid() && QRect(QPoint(),data.crect.size()).contains(data.wrect)) {
+ // This is where the main optimization is: we are already
+ // clipped, and if our clip is still valid, we can just
+ // move our window, and do not need to move or clip
+ // children
+
+ QRect vrect = xrect & parent->rect();
+ vrect.translate(-data.crect.topLeft()); //the part of me that's visible through parent, in my Qt coords
+ if (data.wrect.contains(vrect)) {
+ xrect = data.wrect;
+ xrect.translate(data.crect.topLeft());
+ if (data.winid)
+ data.winid->SetExtent(TPoint(xrect.x(), xrect.y()), TSize(xrect.width(), xrect.height()));
+ return;
+ }
+ }
+
+ if (!validRange.contains(xrect)) {
+ // we are too big, and must clip
+ xrect &=wrectRange;
+ wrect = xrect;
+ wrect.translate(-data.crect.topLeft());
+ //parent's X coord system is equal to parent's Qt coord
+ //sys, so we don't need to map xrect.
+ }
+ }
+
+ // unmap if we are outside the valid window system coord system
+ bool outsideRange = !xrect.isValid();
+ bool mapWindow = false;
+ if (q->testAttribute(Qt::WA_OutsideWSRange) != outsideRange) {
+ q->setAttribute(Qt::WA_OutsideWSRange, outsideRange);
+ if (outsideRange) {
+ if (data.winid)
+ data.winid->DrawableWindow()->SetVisible(EFalse);
+ q->setAttribute(Qt::WA_Mapped, false);
+ } else if (!q->isHidden()) {
+ mapWindow = true;
+ }
+ }
+
+ if (outsideRange)
+ return;
+
+ bool jump = (data.wrect != wrect);
+ data.wrect = wrect;
+
+ // and now recursively for all children...
+ for (int i = 0; i < children.size(); ++i) {
+ QObject *object = children.at(i);
+ if (object->isWidgetType()) {
+ QWidget *w = static_cast<QWidget *>(object);
+ if (!w->isWindow() && w->testAttribute(Qt::WA_WState_Created))
+ w->d_func()->setWSGeometry(jump);
+ }
+ }
+
+ if (data.winid) {
+ // move ourselves to the new position and map (if necessary) after
+ // the movement. Rationale: moving unmapped windows is much faster
+ // than moving mapped windows
+ if (!parent->internalWinId())
+ xrect.translate(parent->mapTo(q->nativeParentWidget(), QPoint(0, 0)));
+
+ data.winid->SetExtent(TPoint(xrect.x(), xrect.y()), TSize(xrect.width(), xrect.height()));
+ }
+
+ if (mapWindow and !dontShow) {
+ q->setAttribute(Qt::WA_Mapped);
+ if (q->internalWinId())
+ q->internalWinId()->DrawableWindow()->SetVisible(ETrue);
+ }
+
+ if (jump && data.winid) {
+ RWindow *const window = static_cast<RWindow *>(data.winid->DrawableWindow());
+ window->Invalidate(TRect(0, 0, wrect.width(), wrect.height()));
+ }
+}
+
+void QWidgetPrivate::setGeometry_sys(int x, int y, int w, int h, bool isMove)
+{
+ Q_Q(QWidget);
+
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+
+ if ((q->windowType() == Qt::Desktop))
+ return;
+
+ QPoint oldPos(q->pos());
+ QSize oldSize(q->size());
+ QRect oldGeom(data.crect);
+
+ // Lose maximized status if deliberate resize
+ if (w != oldSize.width() || h != oldSize.height())
+ data.window_state &= ~Qt::WindowMaximized;
+
+ bool checkExtra = true;
+ if (q->isWindow() && (data.window_state & Qt::WindowFullScreen)) {
+ // Do not modity window size for fullscreen windows, if requested
+ // size is already equal to clientRect.
+ TRect r = static_cast<CEikAppUi*>(S60->appUi())->ClientRect();
+ if (w == r.Width() && h == r.Height())
+ checkExtra = false;
+ }
+
+ if (checkExtra && extra) { // any size restrictions?
+ w = qMin(w,extra->maxw);
+ h = qMin(h,extra->maxh);
+ w = qMax(w,extra->minw);
+ h = qMax(h,extra->minh);
+ }
+
+ if (q->isWindow())
+ topData()->normalGeometry = QRect(0, 0, -1, -1);
+ else {
+ uint s = data.window_state;
+ s &= ~(Qt::WindowMaximized | Qt::WindowFullScreen);
+ data.window_state = s;
+ }
+
+ bool isResize = w != oldSize.width() || h != oldSize.height();
+ if (!isMove && !isResize)
+ return;
+
+ if (q->isWindow()) {
+ if (w == 0 || h == 0) {
+ q->setAttribute(Qt::WA_OutsideWSRange, true);
+ if (q->isVisible() && q->testAttribute(Qt::WA_Mapped))
+ hide_sys();
+ data.crect = QRect(x, y, w, h);
+ data.window_state &= ~Qt::WindowFullScreen;
+ } else if (q->isVisible() && q->testAttribute(Qt::WA_OutsideWSRange)) {
+ q->setAttribute(Qt::WA_OutsideWSRange, false);
+
+ // put the window in its place and show it
+ q->internalWinId()->SetRect(TRect(TPoint(x, y), TSize(w, h)));
+ data.crect.setRect(x, y, w, h);
+ show_sys();
+ } else {
+ QRect r = QRect(x, y, w, h);
+ data.crect = r;
+ q->internalWinId()->SetRect(TRect(TPoint(x, y), TSize(w, h)));
+ topData()->normalGeometry = data.crect;
+ }
+ QSymbianControl *window = static_cast<QSymbianControl *>(q->internalWinId());
+ window->ensureFixNativeOrientation();
+ } else {
+ data.crect.setRect(x, y, w, h);
+
+ QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData();
+ const bool inTopLevelResize = tlwExtra ? tlwExtra->inTopLevelResize : false;
+
+ if (q->isVisible() && (!inTopLevelResize || q->internalWinId())) {
+ // Top-level resize optimization does not work for native child widgets;
+ // disable it for this particular widget.
+ if (inTopLevelResize)
+ tlwExtra->inTopLevelResize = false;
+ if (!isResize && maybeBackingStore())
+ moveRect(QRect(oldPos, oldSize), x - oldPos.x(), y - oldPos.y());
+ else
+ invalidateBuffer_resizeHelper(oldPos, oldSize);
+
+ if (inTopLevelResize)
+ tlwExtra->inTopLevelResize = true;
+ }
+ if (q->testAttribute(Qt::WA_WState_Created))
+ setWSGeometry();
+ }
+
+ if (q->isVisible()) {
+ if (isMove && q->pos() != oldPos) {
+ QMoveEvent e(q->pos(), oldPos);
+ QApplication::sendEvent(q, &e);
+ }
+ if (isResize) {
+ bool slowResize = qgetenv("QT_SLOW_TOPLEVEL_RESIZE").toInt();
+ const bool setTopLevelResize = !slowResize && q->isWindow() && extra && extra->topextra
+ && !extra->topextra->inTopLevelResize;
+ if (setTopLevelResize)
+ extra->topextra->inTopLevelResize = true;
+ QResizeEvent e(q->size(), oldSize);
+ QApplication::sendEvent(q, &e);
+ if (!q->testAttribute(Qt::WA_StaticContents) && q->internalWinId())
+ q->internalWinId()->DrawDeferred();
+ if (setTopLevelResize)
+ extra->topextra->inTopLevelResize = false;
+ }
+ } else {
+ if (isMove && q->pos() != oldPos)
+ q->setAttribute(Qt::WA_PendingMoveEvent, true);
+ if (isResize)
+ q->setAttribute(Qt::WA_PendingResizeEvent, true);
+ }
+}
+
+void QWidgetPrivate::create_sys(WId window, bool /* initializeWindow */, bool destroyOldWindow)
+{
+ Q_Q(QWidget);
+
+ Qt::WindowType type = q->windowType();
+ Qt::WindowFlags &flags = data.window_flags;
+ QWidget *parentWidget = q->parentWidget();
+
+ bool topLevel = (flags & Qt::Window);
+ bool popup = (type == Qt::Popup);
+ bool dialog = (type == Qt::Dialog
+ || type == Qt::Sheet
+ || (flags & Qt::MSWindowsFixedSizeDialogHint));
+ bool desktop = (type == Qt::Desktop);
+ //bool tool = (type == Qt::Tool || type == Qt::Drawer);
+
+ if (popup)
+ flags |= Qt::WindowStaysOnTopHint; // a popup stays on top
+
+ TRect clientRect = static_cast<CEikAppUi*>(S60->appUi())->ClientRect();
+ int sw = clientRect.Width();
+ int sh = clientRect.Height();
+
+ if (desktop) {
+ symbianScreenNumber = qMax(qt_symbian_create_desktop_on_screen, 0);
+ TSize screenSize = S60->screenDevice(symbianScreenNumber)->SizeInPixels();
+ data.crect.setRect(0, 0, screenSize.iWidth, screenSize.iHeight);
+ q->setAttribute(Qt::WA_DontShowOnScreen);
+ } else if (topLevel && !q->testAttribute(Qt::WA_Resized)){
+ int width = sw;
+ int height = sh;
+ if (symbianScreenNumber > 0) {
+ TSize screenSize = S60->screenDevice(symbianScreenNumber)->SizeInPixels();
+ width = screenSize.iWidth;
+ height = screenSize.iHeight;
+ }
+ if (extra) {
+ width = qMax(qMin(width, extra->maxw), extra->minw);
+ height = qMax(qMin(height, extra->maxh), extra->minh);
+ }
+ data.crect.setSize(QSize(width, height));
+ }
+
+ CCoeControl *const destroyw = destroyOldWindow ? data.winid : 0;
+
+ createExtra();
+ if (window) {
+ setWinId(window);
+ TRect tr = window->Rect();
+ data.crect.setRect(tr.iTl.iX, tr.iTl.iY, tr.Width(), tr.Height());
+
+ } else if (topLevel) {
+ if (!q->testAttribute(Qt::WA_Moved) && !q->testAttribute(Qt::WA_DontShowOnScreen))
+ data.crect.moveTopLeft(QPoint(clientRect.iTl.iX, clientRect.iTl.iY));
+
+ QScopedPointer<QSymbianControl> control( new QSymbianControl(q) );
+ Q_CHECK_PTR(control);
+
+ QT_TRAP_THROWING(control->ConstructL(true, desktop));
+ control->SetMopParent(static_cast<CEikAppUi*>(S60->appUi()));
+
+ // Symbian windows are always created in an inactive state
+ // We perform this assignment for the case where the window is being re-created
+ // as a result of a call to setParent_sys, on either this widget or one of its
+ // ancestors.
+ extra->activated = 0;
+
+ if (!desktop) {
+ TInt stackingFlags;
+ if ((q->windowType() & Qt::Popup) == Qt::Popup) {
+ stackingFlags = ECoeStackFlagRefusesAllKeys | ECoeStackFlagRefusesFocus;
+ } else {
+ stackingFlags = ECoeStackFlagStandard;
+ }
+ control->MakeVisible(false);
+ QT_TRAP_THROWING(control->ControlEnv()->AppUi()->AddToStackL(control.data(), ECoeStackPriorityDefault, stackingFlags));
+ // Avoid keyboard focus to a hidden window.
+ control->setFocusSafely(false);
+
+ RDrawableWindow *const drawableWindow = control->DrawableWindow();
+ // Request mouse move events.
+ drawableWindow->PointerFilter(EPointerFilterEnterExit
+ | EPointerFilterMove | EPointerFilterDrag, 0);
+ drawableWindow->EnableVisibilityChangeEvents();
+
+ }
+
+ q->setAttribute(Qt::WA_WState_Created);
+
+ int x, y, w, h;
+ data.crect.getRect(&x, &y, &w, &h);
+ control->SetRect(TRect(TPoint(x, y), TSize(w, h)));
+
+ // We wait until the control is fully constructed before calling setWinId, because
+ // this generates a WinIdChanged event.
+ setWinId(control.take());
+
+ if (!desktop)
+ s60UpdateIsOpaque(); // must be called after setWinId()
+
+ } else if (q->testAttribute(Qt::WA_NativeWindow) || paintOnScreen()) { // create native child widget
+
+ QScopedPointer<QSymbianControl> control( new QSymbianControl(q) );
+ Q_CHECK_PTR(control);
+
+ QT_TRAP_THROWING(control->ConstructL(!parentWidget));
+
+ // Symbian windows are always created in an inactive state
+ // We perform this assignment for the case where the window is being re-created
+ // as a result of a call to setParent_sys, on either this widget or one of its
+ // ancestors.
+ extra->activated = 0;
+
+ TInt stackingFlags;
+ if ((q->windowType() & Qt::Popup) == Qt::Popup) {
+ stackingFlags = ECoeStackFlagRefusesAllKeys | ECoeStackFlagRefusesFocus;
+ } else {
+ stackingFlags = ECoeStackFlagStandard;
+ }
+ control->MakeVisible(false);
+ QT_TRAP_THROWING(control->ControlEnv()->AppUi()->AddToStackL(control.data(), ECoeStackPriorityDefault, stackingFlags));
+ // Avoid keyboard focus to a hidden window.
+ control->setFocusSafely(false);
+
+ q->setAttribute(Qt::WA_WState_Created);
+ int x, y, w, h;
+ data.crect.getRect(&x, &y, &w, &h);
+ control->SetRect(TRect(TPoint(x, y), TSize(w, h)));
+
+ RDrawableWindow *const drawableWindow = control->DrawableWindow();
+ // Request mouse move events.
+ drawableWindow->PointerFilter(EPointerFilterEnterExit
+ | EPointerFilterMove | EPointerFilterDrag, 0);
+ drawableWindow->EnableVisibilityChangeEvents();
+
+ if (q->isVisible() && q->testAttribute(Qt::WA_Mapped)) {
+ activateSymbianWindow(control.data());
+ control->MakeVisible(true);
+ }
+
+ // We wait until the control is fully constructed before calling setWinId, because
+ // this generates a WinIdChanged event.
+ setWinId(control.take());
+ }
+
+ if (destroyw) {
+ destroyw->ControlEnv()->AppUi()->RemoveFromStack(destroyw);
+
+ // Delay deletion of the control in case this function is called in the
+ // context of a CONE event handler such as
+ // CCoeControl::ProcessPointerEventL
+ QMetaObject::invokeMethod(q, "_q_delayedDestroy",
+ Qt::QueuedConnection, Q_ARG(WId, destroyw));
+ }
+
+ if (q->testAttribute(Qt::WA_AcceptTouchEvents))
+ registerTouchWindow();
+}
+
+
+void QWidgetPrivate::show_sys()
+{
+ Q_Q(QWidget);
+
+ if (q->testAttribute(Qt::WA_OutsideWSRange))
+ return;
+
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+
+ q->setAttribute(Qt::WA_Mapped);
+
+ if (q->testAttribute(Qt::WA_DontShowOnScreen)) {
+ invalidateBuffer(q->rect());
+ return;
+ }
+
+ if (q->internalWinId()) {
+ if (!extra->activated)
+ activateSymbianWindow();
+
+ QSymbianControl *id = static_cast<QSymbianControl *>(q->internalWinId());
+ const bool isFullscreen = q->windowState() & Qt::WindowFullScreen;
+ const TBool cbaRequested = q->windowFlags() & Qt::WindowSoftkeysVisibleHint;
+
+#ifdef Q_WS_S60
+ // Lazily initialize the S60 screen furniture when the first window is shown.
+ if (q->isWindow() && !QApplication::testAttribute(Qt::AA_S60DontConstructApplicationPanes)
+ && !S60->buttonGroupContainer() && !S60->statusPane()) {
+
+ if (!q->testAttribute(Qt::WA_DontShowOnScreen)) {
+
+ // Create the status pane and CBA here
+ CEikAppUi *ui = static_cast<CEikAppUi *>(S60->appUi());
+ MEikAppUiFactory *factory = CEikonEnv::Static()->AppUiFactory();
+
+ QT_TRAP_THROWING(
+ factory->CreateResourceIndependentFurnitureL(ui);
+
+ TRect boundingRect = static_cast<CEikAppUi*>(S60->appUi())->ClientRect();
+
+ CEikButtonGroupContainer *cba = CEikButtonGroupContainer::NewL(CEikButtonGroupContainer::ECba,
+ CEikButtonGroupContainer::EHorizontal,ui,R_AVKON_SOFTKEYS_EMPTY_WITH_IDS);
+ if (isFullscreen && !cbaRequested)
+ cba->MakeVisible(false);
+
+ CEikButtonGroupContainer *oldCba = factory->SwapButtonGroup(cba);
+ Q_ASSERT(!oldCba);
+ S60->setButtonGroupContainer(cba);
+
+ // If the creation of the first widget is delayed, for example by doing it
+ // inside the event loop, S60 somehow "forgets" to set the visibility of the
+ // toolbar (the three middle softkeys) when you flip the phone over, so we
+ // need to do it ourselves to avoid a "hole" in the application, even though
+ // Qt itself does not use the toolbar directly..
+ CAknAppUi *appui = dynamic_cast<CAknAppUi *>(CEikonEnv::Static()->AppUi());
+ if (appui) {
+ CAknToolbar *toolbar = appui->PopupToolbar();
+ if (toolbar && !toolbar->IsVisible())
+ toolbar->SetToolbarVisibility(ETrue);
+ }
+
+ CEikMenuBar *menuBar = new(ELeave) CEikMenuBar;
+ menuBar->ConstructL(ui, 0, R_AVKON_MENUPANE_EMPTY);
+ menuBar->SetMenuType(CEikMenuBar::EMenuOptions);
+ S60->appUi()->AddToStackL(menuBar,ECoeStackPriorityMenu,ECoeStackFlagRefusesFocus);
+
+ CEikMenuBar *oldMenu = factory->SwapMenuBar(menuBar);
+ Q_ASSERT(!oldMenu);
+ )
+
+ if (S60->statusPane()) {
+ // Use QDesktopWidget as the status pane observer to proxy for the AppUi.
+ // Can't use AppUi directly because it privately inherits from MEikStatusPaneObserver.
+ QSymbianControl *desktopControl = static_cast<QSymbianControl *>(QApplication::desktop()->winId());
+ S60->statusPane()->SetObserver(desktopControl);
+ if (isFullscreen) {
+ const bool cbaVisible = S60->buttonGroupContainer() && S60->buttonGroupContainer()->IsVisible();
+ S60->setStatusPaneAndButtonGroupVisibility(false, cbaVisible);
+ if (cbaVisible) {
+ // Fix window dimensions as without screen furniture they will have
+ // defaulted to full screen dimensions initially.
+ id->handleClientAreaChange();
+ }
+ }
+ }
+ }
+ }
+#endif
+
+ // Fill client area if maximized OR
+ // Put window below status pane unless the window has an explicit position.
+ if (!isFullscreen) {
+ if (q->windowState() & Qt::WindowMaximized) {
+ TRect r = static_cast<CEikAppUi*>(S60->appUi())->ClientRect();
+ id->SetExtent(r.iTl, r.Size());
+ } else if (!q->testAttribute(Qt::WA_Moved) && q->windowType() != Qt::Dialog) {
+ id->SetPosition(static_cast<CEikAppUi*>(S60->appUi())->ClientRect().iTl);
+ }
+ }
+
+ id->MakeVisible(true);
+
+ if(q->isWindow()&&!q->testAttribute(Qt::WA_ShowWithoutActivating))
+ id->setFocusSafely(true);
+ }
+
+ invalidateBuffer(q->rect());
+}
+
+void QWidgetPrivate::activateSymbianWindow(WId wid)
+{
+ Q_Q(QWidget);
+
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ Q_ASSERT(q->testAttribute(Qt::WA_Mapped));
+ Q_ASSERT(!extra->activated);
+
+ if(!wid)
+ wid = q->internalWinId();
+
+ Q_ASSERT(wid);
+
+ QT_TRAP_THROWING(wid->ActivateL());
+ extra->activated = 1;
+}
+
+void QWidgetPrivate::hide_sys()
+{
+ Q_Q(QWidget);
+
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ deactivateWidgetCleanup();
+ QSymbianControl *id = static_cast<QSymbianControl *>(q->internalWinId());
+
+ if (id) {
+ //Incorrect optimization - for popup windows, Qt's focus is moved before
+ //hide_sys is called, resulting in the popup window keeping its elevated
+ //position in the CONE control stack.
+ //This can result in keyboard focus being in an invisible widget in some
+ //conditions - e.g. QTBUG-4733
+ //if(id->IsFocused()) // Avoid unnecessary calls to FocusChanged()
+ id->setFocusSafely(false);
+ id->MakeVisible(false);
+ if (QWidgetBackingStore *bs = maybeBackingStore())
+ bs->releaseBuffer();
+ } else {
+ invalidateBuffer(q->rect());
+ }
+
+ q->setAttribute(Qt::WA_Mapped, false);
+}
+
+void QWidgetPrivate::setFocus_sys()
+{
+ Q_Q(QWidget);
+ if (q->testAttribute(Qt::WA_WState_Created) && q->window()->windowType() != Qt::Popup)
+ if (!q->effectiveWinId()->IsFocused()) // Avoid unnecessry calls to FocusChanged()
+ static_cast<QSymbianControl *>(q->effectiveWinId())->setFocusSafely(true);
+}
+
+void QWidgetPrivate::raise_sys()
+{
+ Q_Q(QWidget);
+
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ if (q->internalWinId()) {
+ q->internalWinId()->DrawableWindow()->SetOrdinalPosition(0);
+
+ // If toplevel widget, raise app to foreground
+ if (q->isWindow())
+ S60->wsSession().SetWindowGroupOrdinalPosition(S60->windowGroup(q).Identifier(), 0);
+ }
+}
+
+void QWidgetPrivate::lower_sys()
+{
+ Q_Q(QWidget);
+
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ if (q->internalWinId()) {
+ // If toplevel widget, lower app to background
+ if (q->isWindow())
+ S60->wsSession().SetWindowGroupOrdinalPosition(S60->windowGroup(q).Identifier(), -1);
+ else
+ q->internalWinId()->DrawableWindow()->SetOrdinalPosition(-1);
+ }
+
+ if (!q->isWindow())
+ invalidateBuffer(q->rect());
+}
+
+void QWidgetPrivate::setModal_sys()
+{
+
+}
+
+void QWidgetPrivate::stackUnder_sys(QWidget* w)
+{
+ Q_Q(QWidget);
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+
+ if (q->internalWinId() && w->internalWinId()) {
+ RDrawableWindow *const thisWindow = q->internalWinId()->DrawableWindow();
+ RDrawableWindow *const otherWindow = w->internalWinId()->DrawableWindow();
+ thisWindow->SetOrdinalPosition(otherWindow->OrdinalPosition() + 1);
+ }
+
+ if (!q->isWindow() || !w->internalWinId())
+ invalidateBuffer(q->rect());
+}
+
+void QWidgetPrivate::reparentChildren()
+{
+ Q_Q(QWidget);
+
+ QObjectList chlist = q->children();
+ for (int i = 0; i < chlist.size(); ++i) { // reparent children
+ QObject *obj = chlist.at(i);
+ if (obj->isWidgetType()) {
+ QWidget *w = (QWidget *)obj;
+ if (!w->testAttribute(Qt::WA_WState_Created))
+ continue;
+ if (!w->isWindow()) {
+ w->d_func()->invalidateBuffer(w->rect());
+ WId parent = q->effectiveWinId();
+ WId child = w->effectiveWinId();
+ if (parent != child) {
+ // Child widget is native. Because Symbian windows cannot be
+ // re-parented, we must re-create the window.
+ const WId window = 0;
+ const bool initializeWindow = false;
+ const bool destroyOldWindow = true;
+ w->d_func()->create_sys(window, initializeWindow, destroyOldWindow);
+ }
+ // ### TODO: We probably also need to update the component array here
+ w->d_func()->reparentChildren();
+ } else {
+ bool showIt = w->isVisible();
+ QPoint old_pos = w->pos();
+ w->setParent(q, w->windowFlags());
+ w->move(old_pos);
+ if (showIt)
+ w->show();
+ }
+ }
+ }
+}
+
+void QWidgetPrivate::setParent_sys(QWidget *parent, Qt::WindowFlags f)
+{
+ Q_Q(QWidget);
+
+ if (parent && parent->windowType() == Qt::Desktop) {
+ symbianScreenNumber = qt_widget_private(parent)->symbianScreenNumber;
+ parent = 0;
+ }
+
+ bool wasCreated = q->testAttribute(Qt::WA_WState_Created);
+
+ if (q->isVisible() && q->parentWidget() && parent != q->parentWidget())
+ q->parentWidget()->d_func()->invalidateBuffer(q->geometry());
+
+ if (q->testAttribute(Qt::WA_DropSiteRegistered))
+ q->setAttribute(Qt::WA_DropSiteRegistered, false);
+
+ QSymbianControl *old_winid = static_cast<QSymbianControl *>(wasCreated ? data.winid : 0);
+ if ((q->windowType() == Qt::Desktop))
+ old_winid = 0;
+
+ // old_winid may not have received a 'not visible' visibility
+ // changed event before being destroyed; make sure that it is
+ // removed from the backing store's list of visible windows.
+ if (old_winid)
+ S60->controlVisibilityChanged(old_winid, false);
+
+ setWinId(0);
+
+ // hide and reparent our own window away. Otherwise we might get
+ // destroyed when emitting the child remove event below. See QWorkspace.
+ if (wasCreated && old_winid) {
+ old_winid->MakeVisible(false);
+ if (old_winid->IsFocused()) // Avoid unnecessary calls to FocusChanged()
+ old_winid->setFocusSafely(false);
+ old_winid->SetParent(0);
+ }
+
+ QObjectPrivate::setParent_helper(parent);
+ bool explicitlyHidden = q->testAttribute(Qt::WA_WState_Hidden) && q->testAttribute(Qt::WA_WState_ExplicitShowHide);
+
+ data.window_flags = f;
+ data.fstrut_dirty = true;
+ q->setAttribute(Qt::WA_WState_Created, false);
+ q->setAttribute(Qt::WA_WState_Visible, false);
+ q->setAttribute(Qt::WA_WState_Hidden, false);
+ adjustFlags(data.window_flags, q);
+ // keep compatibility with previous versions, we need to preserve the created state
+ // (but we recreate the winId for the widget being reparented, again for compatibility)
+ if (wasCreated || (!q->isWindow() && parent->testAttribute(Qt::WA_WState_Created)))
+ createWinId();
+ if (q->isWindow() || (!parent || parent->isVisible()) || explicitlyHidden)
+ q->setAttribute(Qt::WA_WState_Hidden);
+ q->setAttribute(Qt::WA_WState_ExplicitShowHide, explicitlyHidden);
+
+ if (wasCreated)
+ reparentChildren();
+
+ if (old_winid) {
+ CBase::Delete(old_winid);
+ }
+
+ if (q->testAttribute(Qt::WA_AcceptDrops)
+ || (!q->isWindow() && q->parentWidget() && q->parentWidget()->testAttribute(Qt::WA_DropSiteRegistered)))
+ q->setAttribute(Qt::WA_DropSiteRegistered, true);
+
+ invalidateBuffer(q->rect());
+}
+
+void QWidgetPrivate::setConstraints_sys()
+{
+
+}
+
+
+void QWidgetPrivate::s60UpdateIsOpaque()
+{
+ Q_Q(QWidget);
+
+ if (!q->testAttribute(Qt::WA_WState_Created))
+ return;
+
+ const bool writeAlpha = extraData()->nativePaintMode == QWExtra::BlitWriteAlpha;
+ if (!q->testAttribute(Qt::WA_TranslucentBackground) && !writeAlpha)
+ return;
+ const bool requireAlphaChannel = !isOpaque || writeAlpha;
+
+ createTLExtra();
+
+ RWindow *const window = static_cast<RWindow *>(q->effectiveWinId()->DrawableWindow());
+
+#ifdef Q_SYMBIAN_SEMITRANSPARENT_BG_SURFACE
+ if (QApplicationPrivate::instance()->useTranslucentEGLSurfaces) {
+ window->SetSurfaceTransparency(!isOpaque);
+ extra->topextra->nativeWindowTransparencyEnabled = !isOpaque;
+ return;
+ }
+#endif
+ if (requireAlphaChannel) {
+ const TDisplayMode displayMode = static_cast<TDisplayMode>(window->SetRequiredDisplayMode(EColor16MA));
+ if (window->SetTransparencyAlphaChannel() == KErrNone) {
+ window->SetBackgroundColor(TRgb(255, 255, 255, 0));
+ extra->topextra->nativeWindowTransparencyEnabled = 1;
+ if (extra->topextra->backingStore.data() && (
+ QApplicationPrivate::graphics_system_name == QLatin1String("openvg")
+ || QApplicationPrivate::graphics_system_name == QLatin1String("opengl"))) {
+ // Semi-transparent EGL surfaces aren't supported. We need to
+ // recreate backing store to get translucent surface (raster surface).
+ extra->topextra->backingStore.create(q);
+ extra->topextra->backingStore.registerWidget(q);
+ // FixNativeOrientation() will not work without an EGL surface.
+ q->setAttribute(Qt::WA_SymbianNoSystemRotation, false);
+ }
+ }
+ } else if (extra->topextra->nativeWindowTransparencyEnabled) {
+ window->SetTransparentRegion(TRegionFix<1>());
+ extra->topextra->nativeWindowTransparencyEnabled = 0;
+ }
+}
+
+void QWidgetPrivate::setWindowIcon_sys(bool forceReset)
+{
+#ifdef Q_WS_S60
+ Q_Q(QWidget);
+
+ if (!q->testAttribute(Qt::WA_WState_Created) || !q->isWindow() )
+ return;
+
+ QTLWExtra* topData = this->topData();
+ if (topData->iconPixmap && !forceReset)
+ // already been set
+ return;
+
+ TRect cPaneRect;
+ TBool found = AknLayoutUtils::LayoutMetricsRect( AknLayoutUtils::EContextPane, cPaneRect );
+ CAknContextPane* contextPane = S60->contextPane();
+ if (found && contextPane) { // We have context pane with valid metrics
+ QIcon icon = q->windowIcon();
+ if (!icon.isNull()) {
+ // Valid icon -> set it as an context pane picture
+ QSize size = icon.actualSize(QSize(cPaneRect.Size().iWidth, cPaneRect.Size().iHeight));
+ QPixmap pm = icon.pixmap(size);
+ QBitmap mask = pm.mask();
+ if (mask.isNull()) {
+ mask = QBitmap(pm.size());
+ mask.fill(Qt::color1);
+ }
+
+ CFbsBitmap* nBitmap = pm.toSymbianCFbsBitmap();
+ CFbsBitmap* nMask = mask.toSymbianCFbsBitmap();
+ contextPane->SetPicture(nBitmap,nMask);
+ } else {
+ // Icon set to null -> set context pane picture to default
+ QT_TRAP_THROWING(contextPane->SetPictureToDefaultL());
+ }
+ } else {
+ // Context pane does not exist, try setting small icon to title pane
+ TRect titlePaneRect;
+ TBool found = AknLayoutUtils::LayoutMetricsRect( AknLayoutUtils::ETitlePane, titlePaneRect );
+ CAknTitlePane* titlePane = S60->titlePane();
+ if (found && titlePane) { // We have title pane with valid metrics
+ // The API to get title_pane graphics size is not public -> assume square space based
+ // on titlebar font height. CAknBitmap would be optimum, wihtout setting the size, since
+ // then title pane would automatically scale the bitmap. Unfortunately it is not public API
+ // Also this function is leaving, although it is not named as such.
+ const CFont * font;
+ QT_TRAP_THROWING(font = AknLayoutUtils::FontFromId(EAknLogicalFontTitleFont));
+ TSize iconSize(font->HeightInPixels(), font->HeightInPixels());
+
+ QIcon icon = q->windowIcon();
+ if (!icon.isNull()) {
+ // Valid icon -> set it as an title pane small picture
+ QSize size = icon.actualSize(QSize(iconSize.iWidth, iconSize.iHeight));
+ QPixmap pm = icon.pixmap(size);
+ QBitmap mask = pm.mask();
+ if (mask.isNull()) {
+ mask = QBitmap(pm.size());
+ mask.fill(Qt::color1);
+ }
+
+ CFbsBitmap* nBitmap = pm.toSymbianCFbsBitmap();
+ CFbsBitmap* nMask = mask.toSymbianCFbsBitmap();
+ titlePane->SetSmallPicture( nBitmap, nMask, ETrue );
+ } else {
+ // Icon set to null -> set context pane picture to default
+ titlePane->SetSmallPicture( NULL, NULL, EFalse );
+ }
+ }
+ }
+
+#else
+ Q_UNUSED(forceReset)
+#endif
+}
+
+void QWidgetPrivate::setWindowTitle_sys(const QString &caption)
+{
+#ifdef Q_WS_S60
+ Q_Q(QWidget);
+ if (q->isWindow()) {
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ CAknTitlePane* titlePane = S60->titlePane();
+ if (titlePane) {
+ if (caption.isEmpty()) {
+ QT_TRAP_THROWING(titlePane->SetTextToDefaultL());
+ } else {
+ QT_TRAP_THROWING(titlePane->SetTextL(qt_QString2TPtrC(caption)));
+ }
+ }
+ }
+#else
+ Q_UNUSED(caption)
+#endif
+}
+
+void QWidgetPrivate::setWindowIconText_sys(const QString & /*iconText */)
+{
+
+}
+
+void QWidgetPrivate::scroll_sys(int dx, int dy)
+{
+ Q_Q(QWidget);
+
+ scrollChildren(dx, dy);
+ if (!paintOnScreen() || !q->internalWinId() || !q->internalWinId()->OwnsWindow()) {
+ scrollRect(q->rect(), dx, dy);
+ } else {
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ RDrawableWindow *const window = q->internalWinId()->DrawableWindow();
+ window->Scroll(TPoint(dx, dy));
+ }
+}
+
+void QWidgetPrivate::scroll_sys(int dx, int dy, const QRect &r)
+{
+ Q_Q(QWidget);
+
+ if (!paintOnScreen() || !q->internalWinId() || !q->internalWinId()->OwnsWindow()) {
+ scrollRect(r, dx, dy);
+ } else {
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ RDrawableWindow *const window = q->internalWinId()->DrawableWindow();
+ window->Scroll(TPoint(dx, dy), qt_QRect2TRect(r));
+ }
+}
+
+/*!
+ For this function to work in the emulator, you must add:
+ TRANSPARENCY
+ To a line in the wsini.ini file.
+*/
+void QWidgetPrivate::setWindowOpacity_sys(qreal)
+{
+ // ### TODO: Implement uniform window transparency
+}
+
+void QWidgetPrivate::updateFrameStrut()
+{
+
+}
+
+void QWidgetPrivate::updateSystemBackground()
+{
+
+}
+
+void QWidgetPrivate::registerDropSite(bool /* on */)
+{
+
+}
+
+void QWidgetPrivate::createTLSysExtra()
+{
+ extra->topextra->inExpose = 0;
+ extra->topextra->nativeWindowTransparencyEnabled = 0;
+}
+
+void QWidgetPrivate::deleteTLSysExtra()
+{
+ extra->topextra->backingStore.destroy();
+}
+
+void QWidgetPrivate::createSysExtra()
+{
+ extra->activated = 0;
+ extra->nativePaintMode = QWExtra::Default;
+ extra->receiveNativePaintEvents = 0;
+}
+
+void QWidgetPrivate::deleteSysExtra()
+{
+ // this should only be non-zero if destroy() has not run due to constructor fail
+ if (data.winid) {
+ data.winid->ControlEnv()->AppUi()->RemoveFromStack(data.winid);
+ delete data.winid;
+ data.winid = 0;
+ }
+}
+
+QWindowSurface *QWidgetPrivate::createDefaultWindowSurface_sys()
+{
+ return new QS60WindowSurface(q_func());
+}
+
+void QWidgetPrivate::setMask_sys(const QRegion& /* region */)
+{
+
+}
+
+void QWidgetPrivate::registerTouchWindow()
+{
+#ifdef QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER
+ Q_Q(QWidget);
+ if (q->testAttribute(Qt::WA_WState_Created) && q->windowType() != Qt::Desktop) {
+ RWindow *rwindow = static_cast<RWindow *>(q->effectiveWinId()->DrawableWindow());
+ QSymbianControl *window = static_cast<QSymbianControl *>(q->effectiveWinId());
+ //Enabling advanced pointer events for controls that already have active windows causes a panic.
+ if (!window->isControlActive())
+ rwindow->EnableAdvancedPointers();
+ }
+#endif
+}
+
+int QWidget::metric(PaintDeviceMetric m) const
+{
+ Q_D(const QWidget);
+ int val;
+ if (m == PdmWidth) {
+ val = data->crect.width();
+ } else if (m == PdmHeight) {
+ val = data->crect.height();
+ } else {
+ CWsScreenDevice *scr = S60->screenDevice(this);
+ switch(m) {
+ case PdmDpiX:
+ case PdmPhysicalDpiX:
+ if (d->extra && d->extra->customDpiX) {
+ val = d->extra->customDpiX;
+ } else {
+ const QWidgetPrivate *p = d;
+ while (p->parent) {
+ p = static_cast<const QWidget *>(p->parent)->d_func();
+ if (p->extra && p->extra->customDpiX) {
+ val = p->extra->customDpiX;
+ break;
+ }
+ }
+ if (p == d || !(p->extra && p->extra->customDpiX))
+ val = S60->defaultDpiX;
+ }
+ break;
+ case PdmDpiY:
+ case PdmPhysicalDpiY:
+ if (d->extra && d->extra->customDpiY) {
+ val = d->extra->customDpiY;
+ } else {
+ const QWidgetPrivate *p = d;
+ while (p->parent) {
+ p = static_cast<const QWidget *>(p->parent)->d_func();
+ if (p->extra && p->extra->customDpiY) {
+ val = p->extra->customDpiY;
+ break;
+ }
+ }
+ if (p == d || !(p->extra && p->extra->customDpiY))
+ val = S60->defaultDpiY;
+ }
+ break;
+ case PdmWidthMM:
+ {
+ TInt twips = scr->HorizontalPixelsToTwips(data->crect.width());
+ val = (int)(twips * (25.4/KTwipsPerInch));
+ break;
+ }
+ case PdmHeightMM:
+ {
+ TInt twips = scr->VerticalPixelsToTwips(data->crect.height());
+ val = (int)(twips * (25.4/KTwipsPerInch));
+ break;
+ }
+ case PdmNumColors:
+ val = TDisplayModeUtils::NumDisplayModeColors(scr->DisplayMode());
+ break;
+ case PdmDepth:
+ val = TDisplayModeUtils::NumDisplayModeBitsPerPixel(scr->DisplayMode());
+ break;
+ default:
+ val = 0;
+ qWarning("QWidget::metric: Invalid metric command");
+ }
+ }
+ return val;
+}
+
+QPaintEngine *QWidget::paintEngine() const
+{
+ return 0;
+}
+
+QPoint QWidget::mapToGlobal(const QPoint &pos) const
+{
+ Q_D(const QWidget);
+ if (!testAttribute(Qt::WA_WState_Created) || !internalWinId()) {
+
+ QPoint p = pos + data->crect.topLeft();
+ return (isWindow() || !parentWidget()) ? p : parentWidget()->mapToGlobal(p);
+
+ } else if ((d->data.window_flags & Qt::Window) && internalWinId()) { //toplevel
+ QPoint tp = geometry().topLeft();
+ return pos + tp;
+ }
+
+ // Native window case
+ const TPoint widgetScreenOffset = internalWinId()->PositionRelativeToScreen();
+ const QPoint globalPos = QPoint(widgetScreenOffset.iX, widgetScreenOffset.iY) + pos;
+ return globalPos;
+}
+
+QPoint QWidget::mapFromGlobal(const QPoint &pos) const
+{
+ Q_D(const QWidget);
+ if (!testAttribute(Qt::WA_WState_Created) || !internalWinId()) {
+ QPoint p = (isWindow() || !parentWidget()) ? pos : parentWidget()->mapFromGlobal(pos);
+ return p - data->crect.topLeft();
+ } else if ((d->data.window_flags & Qt::Window) && internalWinId()) { //toplevel
+ QPoint tp = geometry().topLeft();
+ return pos - tp;
+ }
+
+ // Native window case
+ const TPoint widgetScreenOffset = internalWinId()->PositionRelativeToScreen();
+ const QPoint widgetPos = pos - QPoint(widgetScreenOffset.iX, widgetScreenOffset.iY);
+ return widgetPos;
+}
+
+static Qt::WindowStates effectiveState(Qt::WindowStates state)
+{
+ if (state & Qt::WindowMinimized)
+ return Qt::WindowMinimized;
+ else if (state & Qt::WindowFullScreen)
+ return Qt::WindowFullScreen;
+ else if (state & Qt::WindowMaximized)
+ return Qt::WindowMaximized;
+ return Qt::WindowNoState;
+}
+
+void QWidget::setWindowState(Qt::WindowStates newstate)
+{
+ Q_D(QWidget);
+
+ Qt::WindowStates oldstate = windowState();
+
+ const TBool isFullscreen = newstate & Qt::WindowFullScreen;
+#ifdef Q_WS_S60
+ const TBool cbaRequested = windowFlags() & Qt::WindowSoftkeysVisibleHint;
+ const TBool cbaVisible = CEikButtonGroupContainer::Current() ? true : false;
+ const TBool softkeyVisibilityChange = isFullscreen && (cbaRequested != cbaVisible);
+
+ if (oldstate == newstate && !softkeyVisibilityChange)
+ return;
+#endif // Q_WS_S60
+
+ if (isWindow()) {
+ createWinId();
+ Q_ASSERT(testAttribute(Qt::WA_WState_Created));
+
+ const bool wasResized = testAttribute(Qt::WA_Resized);
+ const bool wasMoved = testAttribute(Qt::WA_Moved);
+
+ QSymbianControl *window = static_cast<QSymbianControl *>(effectiveWinId());
+ if (window && newstate & Qt::WindowMinimized) {
+ window->setFocusSafely(false);
+ window->MakeVisible(false);
+ } else if (window && oldstate & Qt::WindowMinimized) {
+ window->setFocusSafely(true);
+ window->MakeVisible(true);
+ }
+
+#ifdef Q_WS_S60
+ // The window decoration visibility has to be changed before doing actual window state
+ // change since in that order the availableGeometry will return directly the right size and
+ // we will avoid unnecessary redraws
+ bool decorationsVisible = S60->setRecursiveDecorationsVisibility(this, newstate);
+#endif // Q_WS_S60
+
+ // Ensure the initial size is valid, since we store it as normalGeometry below.
+ if (!wasResized && !isVisible())
+ adjustSize();
+
+ QTLWExtra *top = d->topData();
+ QRect normalGeometry = (top->normalGeometry.width() < 0) ? geometry() : top->normalGeometry;
+
+ const bool cbaVisibilityHint = windowFlags() & Qt::WindowSoftkeysVisibleHint;
+ if (newstate & Qt::WindowFullScreen && !cbaVisibilityHint) {
+ setAttribute(Qt::WA_OutsideWSRange, false);
+ if (d->symbianScreenNumber > 0) {
+ int w = S60->screenWidthInPixelsForScreen[d->symbianScreenNumber];
+ int h = S60->screenHeightInPixelsForScreen[d->symbianScreenNumber];
+ if (w <= 0 || h <= 0)
+ window->SetExtentToWholeScreen();
+ else
+ window->SetExtent(TPoint(0, 0), TSize(w, h));
+ } else {
+ window->SetExtentToWholeScreen();
+ }
+ } else if (newstate & Qt::WindowMaximized || ((newstate & Qt::WindowFullScreen) && cbaVisibilityHint)) {
+ setAttribute(Qt::WA_OutsideWSRange, false);
+ TRect maxExtent = qt_QRect2TRect(qApp->desktop()->availableGeometry(this));
+ window->SetExtent(maxExtent.iTl, maxExtent.Size());
+ } else {
+#ifdef Q_WS_S60
+ // With delayed creation of S60 app panes, the normalGeometry calculated above is not
+ // accurate because it did not consider the status pane. This means that when returning
+ // normal mode after showing the status pane, the geometry would overlap so we should
+ // move it if it never had an explicit position.
+ if (!wasMoved && S60->statusPane() && decorationsVisible) {
+ TPoint tl = static_cast<CEikAppUi*>(S60->appUi())->ClientRect().iTl;
+ normalGeometry.setTopLeft(QPoint(tl.iX, tl.iY));
+ }
+#endif
+ setGeometry(normalGeometry);
+ }
+
+ //restore normal geometry
+ top->normalGeometry = normalGeometry;
+
+ // FixMe QTBUG-8977
+ // In some platforms, WA_Resized and WA_Moved are also not set when application window state is
+ // anything else than normal. In Symbian we can restore them only for normal window state since
+ // restoring for other modes, will make fluidlauncher to be launched in wrong size (200x100)
+ if (effectiveState(newstate) == Qt::WindowNoState) {
+ setAttribute(Qt::WA_Resized, wasResized);
+ setAttribute(Qt::WA_Moved, wasMoved);
+ }
+ }
+
+ data->window_state = newstate;
+
+ if (newstate & Qt::WindowActive)
+ activateWindow();
+
+ if (isWindow()) {
+ // Now that the new state is set, fix the display memory layout, if needed.
+ QSymbianControl *window = static_cast<QSymbianControl *>(effectiveWinId());
+ window->ensureFixNativeOrientation();
+ }
+
+ QWindowStateChangeEvent e(oldstate);
+ QApplication::sendEvent(this, &e);
+}
+
+
+void QWidget::destroy(bool destroyWindow, bool destroySubWindows)
+{
+ Q_D(QWidget);
+ d->aboutToDestroy();
+ if (!isWindow() && parentWidget())
+ parentWidget()->d_func()->invalidateBuffer(geometry());
+ d->deactivateWidgetCleanup();
+ QSymbianControl *id = static_cast<QSymbianControl *>(internalWinId());
+ if (testAttribute(Qt::WA_WState_Created)) {
+
+#ifndef QT_NO_IM
+ if (d->ic) {
+ delete d->ic;
+ } else {
+ QInputContext *ic = QApplicationPrivate::inputContext;
+ if (ic) {
+ ic->widgetDestroyed(this);
+ }
+ }
+#endif
+
+ if (QWidgetPrivate::mouseGrabber == this)
+ releaseMouse();
+ if (QWidgetPrivate::keyboardGrabber == this)
+ releaseKeyboard();
+ setAttribute(Qt::WA_WState_Created, false);
+ QObjectList childList = children();
+ for (int i = 0; i < childList.size(); ++i) { // destroy all widget children
+ register QObject *obj = childList.at(i);
+ if (obj->isWidgetType())
+ static_cast<QWidget*>(obj)->destroy(destroySubWindows,
+ destroySubWindows);
+ }
+ if (destroyWindow && !(windowType() == Qt::Desktop) && id) {
+ if (id->IsFocused()) // Avoid unnecessry calls to FocusChanged()
+ id->setFocusSafely(false);
+ id->ControlEnv()->AppUi()->RemoveFromStack(id);
+ }
+ }
+
+ QT_TRY {
+ d->setWinId(0);
+ } QT_CATCH (const std::bad_alloc &) {
+ // swallow - destructors must not throw
+ }
+
+ if (destroyWindow) {
+ delete id;
+ // At this point the backing store should already be destroyed
+ // so we flush the command buffer to ensure that the freeing of
+ // those resources and deleting the window can happen "atomically"
+ if (qApp)
+ S60->wsSession().Flush();
+ }
+}
+
+QWidget *QWidget::mouseGrabber()
+{
+ return QWidgetPrivate::mouseGrabber;
+}
+
+QWidget *QWidget::keyboardGrabber()
+{
+ return QWidgetPrivate::keyboardGrabber;
+}
+
+void QWidget::grabKeyboard()
+{
+ if (!qt_nograb()) {
+ if (QWidgetPrivate::keyboardGrabber && QWidgetPrivate::keyboardGrabber != this)
+ QWidgetPrivate::keyboardGrabber->releaseKeyboard();
+
+ // ### TODO: Native keyboard grab
+
+ QWidgetPrivate::keyboardGrabber = this;
+ }
+}
+
+void QWidget::releaseKeyboard()
+{
+ if (!qt_nograb() && QWidgetPrivate::keyboardGrabber == this) {
+ // ### TODO: Native keyboard release
+ QWidgetPrivate::keyboardGrabber = 0;
+ }
+}
+
+void QWidget::grabMouse()
+{
+ if (isVisible() && !qt_nograb()) {
+ if (QWidgetPrivate::mouseGrabber && QWidgetPrivate::mouseGrabber != this)
+ QWidgetPrivate::mouseGrabber->releaseMouse();
+ Q_ASSERT(testAttribute(Qt::WA_WState_Created));
+ WId id = effectiveWinId();
+ id->SetPointerCapture(true);
+ QWidgetPrivate::mouseGrabber = this;
+
+#ifndef QT_NO_CURSOR
+ QApplication::setOverrideCursor(cursor());
+#endif
+ }
+}
+
+#ifndef QT_NO_CURSOR
+void QWidget::grabMouse(const QCursor &cursor)
+{
+ if (isVisible() && !qt_nograb()) {
+ if (QWidgetPrivate::mouseGrabber && QWidgetPrivate::mouseGrabber != this)
+ QWidgetPrivate::mouseGrabber->releaseMouse();
+ Q_ASSERT(testAttribute(Qt::WA_WState_Created));
+ WId id = effectiveWinId();
+ id->SetPointerCapture(true);
+ QWidgetPrivate::mouseGrabber = this;
+
+ QApplication::setOverrideCursor(cursor);
+ }
+}
+#endif
+
+void QWidget::releaseMouse()
+{
+ if (!qt_nograb() && QWidgetPrivate::mouseGrabber == this) {
+ Q_ASSERT(testAttribute(Qt::WA_WState_Created));
+ if(!window()->isModal()) {
+ WId id = effectiveWinId();
+ id->SetPointerCapture(false);
+ }
+ QWidgetPrivate::mouseGrabber = 0;
+#ifndef QT_NO_CURSOR
+ QApplication::restoreOverrideCursor();
+#endif
+ }
+}
+
+void QWidget::activateWindow()
+{
+ Q_D(QWidget);
+
+ QWidget *tlw = window();
+ if (tlw->isVisible()) {
+ window()->createWinId();
+ QSymbianControl *id = static_cast<QSymbianControl *>(tlw->internalWinId());
+ if (!id->IsFocused())
+ id->setFocusSafely(true);
+ }
+}
+
+#ifndef QT_NO_CURSOR
+
+void QWidgetPrivate::setCursor_sys(const QCursor &cursor)
+{
+ Q_UNUSED(cursor);
+ Q_Q(QWidget);
+ qt_symbian_set_cursor(q, false);
+}
+
+void QWidgetPrivate::unsetCursor_sys()
+{
+ Q_Q(QWidget);
+ qt_symbian_set_cursor(q, false);
+}
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/win/qapplication_win.cpp b/src/widgets/platforms/win/qapplication_win.cpp
new file mode 100644
index 0000000000..72b8870fe1
--- /dev/null
+++ b/src/widgets/platforms/win/qapplication_win.cpp
@@ -0,0 +1,4248 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifdef Q_WS_WINCE
+#include "qguifunctions_wince.h"
+#include "qmenubar.h"
+extern bool qt_wince_is_mobile(); //defined in qguifunctions_wince.cpp
+extern bool qt_wince_is_high_dpi(); //defined in qguifunctions_wince.cpp
+extern bool qt_wince_is_smartphone(); //defined in qguifunctions_wince.cpp
+extern bool qt_wince_is_pocket_pc(); //defined in qguifunctions_wince.cpp
+extern void qt_wince_hide_taskbar(HWND hwnd); //defined in qguifunctions_wince.cpp
+#endif
+#ifdef Q_WS_WINCE_WM
+#include <windowsm.h>
+#include <tpcshell.h>
+#ifdef QT_WINCE_GESTURES
+#ifndef QT_NO_GESTURES
+#include <gesture.h>
+#endif
+#endif
+#endif
+
+#include "qapplication.h"
+#include "qdesktopwidget.h"
+#include "qevent.h"
+#include "private/qeventdispatcher_win_p.h"
+#include "qeventloop.h"
+#include "qclipboard.h"
+#include "qcursor.h"
+#include "qdatetime.h"
+#include "qpointer.h"
+#include "qhash.h"
+#include "qmetaobject.h"
+#include "qmime.h"
+#include "qpainter.h"
+#include "qpixmapcache.h"
+#include "qsessionmanager.h"
+#include "qstyle.h"
+#include "qwhatsthis.h" // ######## dependency
+#include "qwidget.h"
+#include "qcolormap.h"
+#include "qlayout.h"
+#include "qtooltip.h"
+#include "qt_windows.h"
+#include "qscrollbar.h"
+#if defined(QT_NON_COMMERCIAL)
+#include "qnc_win.h"
+#endif
+#include "private/qwininputcontext_p.h"
+#include "private/qcursor_p.h"
+#include "private/qmath_p.h"
+#include "private/qapplication_p.h"
+#include "private/qbackingstore_p.h"
+#include "private/qwindowsurface_raster_p.h"
+#include "qdebug.h"
+#include <private/qkeymapper_p.h>
+#include <private/qlocale_p.h>
+#include <private/qsystemlibrary_p.h>
+#include "qevent_p.h"
+
+//#define ALIEN_DEBUG
+
+#ifndef QT_NO_THREAD
+#include "qmutex.h"
+#endif
+
+#ifndef QT_NO_ACCESSIBILITY
+#include "qaccessible.h"
+
+#include <oleacc.h>
+#ifndef WM_GETOBJECT
+#define WM_GETOBJECT 0x003D
+#endif
+#endif // QT_NO_ACCESSIBILITY
+
+#if !defined(WINABLEAPI)
+# if defined(Q_WS_WINCE)
+# include <bldver.h>
+# endif
+# if !defined(Q_WS_WINCE)
+# include <winable.h>
+# endif
+#endif
+
+#ifndef QT_NO_GESTURES
+# ifndef GID_ZOOM
+# define GID_ZOOM 3
+# define GID_TWOFINGERTAP 6
+# define GID_PRESSANDTAP 7
+# define GID_ROLLOVER GID_PRESSANDTAP
+# endif
+#endif
+
+#ifndef WM_TOUCH
+# define WM_TOUCH 0x0240
+#endif
+
+#ifndef TOUCHEVENTF_MOVE
+# define TOUCHEVENTF_MOVE 0x0001
+# define TOUCHEVENTF_DOWN 0x0002
+# define TOUCHEVENTF_UP 0x0004
+# define TOUCHEVENTF_INRANGE 0x0008
+# define TOUCHEVENTF_PRIMARY 0x0010
+# define TOUCHEVENTF_NOCOALESCE 0x0020
+# define TOUCHEVENTF_PEN 0x0040
+# define TOUCHEVENTF_PALM 0x0080
+
+# define TOUCHINPUTMASKF_TIMEFROMSYSTEM 0x0001
+# define TOUCHINPUTMASKF_EXTRAINFO 0x0002
+# define TOUCHINPUTMASKF_CONTACTAREA 0x0004
+
+typedef struct tagTOUCHINPUT
+{
+ LONG x;
+ LONG y;
+ HANDLE hSource;
+ DWORD dwID;
+ DWORD dwFlags;
+ DWORD dwMask;
+ DWORD dwTime;
+ ULONG_PTR dwExtraInfo;
+ DWORD cxContact;
+ DWORD cyContact;
+} TOUCHINPUT, *PTOUCHINPUT;
+
+#endif
+
+#include <windowsx.h>
+#include <limits.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <math.h>
+
+#define PACKETDATA (PK_X | PK_Y | PK_BUTTONS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE \
+ | PK_ORIENTATION | PK_CURSOR | PK_Z)
+#define PACKETMODE 0
+
+#include <wintab.h>
+#ifndef CSR_TYPE
+#define CSR_TYPE 20 // Some old Wacom wintab.h may not provide this constant.
+#endif
+#include <pktdef.h>
+
+#if defined(__CYGWIN32__)
+#define __INSIDE_CYGWIN32__
+#include <mywinsock.h>
+#endif
+
+#ifndef IMR_RECONVERTSTRING
+#define IMR_RECONVERTSTRING 4
+#endif
+
+#ifndef IMR_CONFIRMRECONVERTSTRING
+#define IMR_CONFIRMRECONVERTSTRING 0x0005
+#endif
+QT_BEGIN_NAMESPACE
+
+#ifdef Q_WS_WINCE
+#ifndef SHRG_RETURNCMD
+struct SHRGINFO {
+ DWORD cbSize;
+ HWND hwndClient;
+ POINT ptDown;
+ DWORD dwFlags;
+};
+#define GN_CONTEXTMENU 1000
+#define SHRG_RETURNCMD 0x00000001
+#define SHRG_NOANIMATION 0x00000010
+#endif
+
+#ifndef SPI_SETSIPINFO
+#define SPI_SETSIPINFO 224
+#endif
+
+#ifndef QT_NO_GESTURES
+typedef DWORD (API *AygRecognizeGesture)(SHRGINFO*);
+static AygRecognizeGesture ptrRecognizeGesture = 0;
+static bool aygResolved = false;
+static void resolveAygLibs()
+{
+ if (!aygResolved) {
+ aygResolved = true;
+ QSystemLibrary ayglib(QLatin1String("aygshell"));
+ ptrRecognizeGesture = (AygRecognizeGesture) ayglib.resolve("SHRecognizeGesture");
+ }
+}
+#endif // QT_NO_GESTURES
+
+#endif
+
+#ifndef SPI_SETFONTSMOOTHINGTYPE
+# define SPI_SETFONTSMOOTHINGTYPE 0x200B
+#endif
+#ifndef SPI_GETFONTSMOOTHINGTYPE
+# define SPI_GETFONTSMOOTHINGTYPE 0x200A
+#endif
+#ifndef FE_FONTSMOOTHINGCLEARTYPE
+# define FE_FONTSMOOTHINGCLEARTYPE 0x0002
+#endif
+
+Q_WIDGETS_EXPORT qreal qt_fontsmoothing_gamma;
+Q_WIDGETS_EXPORT bool qt_cleartype_enabled;
+Q_WIDGETS_EXPORT bool qt_win_owndc_required; // CS_OWNDC is required if we use the GL graphicssystem as default
+
+typedef HCTX (API *PtrWTOpen)(HWND, LPLOGCONTEXT, BOOL);
+typedef BOOL (API *PtrWTClose)(HCTX);
+typedef UINT (API *PtrWTInfo)(UINT, UINT, LPVOID);
+typedef BOOL (API *PtrWTEnable)(HCTX, BOOL);
+typedef BOOL (API *PtrWTOverlap)(HCTX, BOOL);
+typedef int (API *PtrWTPacketsGet)(HCTX, int, LPVOID);
+typedef BOOL (API *PtrWTGet)(HCTX, LPLOGCONTEXT);
+typedef int (API *PtrWTQueueSizeGet)(HCTX);
+typedef BOOL (API *PtrWTQueueSizeSet)(HCTX, int);
+
+static PtrWTInfo ptrWTInfo = 0;
+static PtrWTEnable ptrWTEnable = 0;
+static PtrWTOverlap ptrWTOverlap = 0;
+static PtrWTPacketsGet ptrWTPacketsGet = 0;
+static PtrWTGet ptrWTGet = 0;
+
+static PACKET localPacketBuf[QT_TABLET_NPACKETQSIZE]; // our own tablet packet queue.
+HCTX qt_tablet_context; // the hardware context for the tablet (like a window handle)
+bool qt_tablet_tilt_support;
+
+#ifndef QT_NO_TABLETEVENT
+static void tabletInit(const quint64 uniqueId, const UINT csr_type, HCTX hTab);
+static void tabletUpdateCursor(QTabletDeviceData &tdd, const UINT currentCursor);
+static void initWinTabFunctions(); // resolve the WINTAB api functions
+#endif // QT_NO_TABLETEVENT
+
+
+#ifndef QT_NO_ACCESSIBILITY
+extern IAccessible *qt_createWindowsAccessible(QAccessibleInterface *object);
+#endif // QT_NO_ACCESSIBILITY
+
+extern bool qt_tabletChokeMouse;
+extern QWidget* qt_get_tablet_widget();
+extern bool qt_sendSpontaneousEvent(QObject*, QEvent*);
+extern QRegion qt_dirtyRegion(QWidget *);
+
+typedef QHash<quint64, QTabletDeviceData> QTabletCursorInfo;
+Q_GLOBAL_STATIC(QTabletCursorInfo, tCursorInfo)
+QTabletDeviceData currentTabletPointer;
+
+// from qregion_win.cpp
+extern HRGN qt_tryCreateRegion(QRegion::RegionType type, int left, int top, int right, int bottom);
+
+// support for on-the-fly changes of the XP theme engine
+#ifndef WM_THEMECHANGED
+#define WM_THEMECHANGED 0x031A
+#endif
+#ifndef COLOR_MENUHILIGHT
+#define COLOR_MENUHILIGHT 29
+#define COLOR_MENUBAR 30
+#endif
+
+// support for xbuttons
+#ifndef WM_XBUTTONDOWN
+#define WM_XBUTTONDOWN 0x020B
+#define WM_XBUTTONUP 0x020C
+#define WM_XBUTTONDBLCLK 0x020D
+#endif
+#ifndef GET_KEYSTATE_WPARAM
+#define GET_KEYSTATE_WPARAM(wParam) (LOWORD(wParam))
+#define GET_XBUTTON_WPARAM(wParam) (HIWORD(wParam))
+#define XBUTTON1 0x0001
+#define XBUTTON2 0x0002
+#endif
+#ifndef MK_XBUTTON1
+#define MK_XBUTTON1 0x0020
+#define MK_XBUTTON2 0x0040
+#endif
+
+// support for multi-media-keys
+#ifndef WM_APPCOMMAND
+#define WM_APPCOMMAND 0x0319
+#endif
+
+#ifndef FAPPCOMMAND_MOUSE
+#define FAPPCOMMAND_MOUSE 0x8000
+#define FAPPCOMMAND_KEY 0
+#define FAPPCOMMAND_OEM 0x1000
+#define FAPPCOMMAND_MASK 0xF000
+#define GET_APPCOMMAND_LPARAM(lParam) ((short)(HIWORD(lParam) & ~FAPPCOMMAND_MASK))
+#define GET_DEVICE_LPARAM(lParam) ((WORD)(HIWORD(lParam) & FAPPCOMMAND_MASK))
+#define GET_MOUSEORKEY_LPARAM GET_DEVICE_LPARAM
+#define GET_FLAGS_LPARAM(lParam) (LOWORD(lParam))
+#define GET_KEYSTATE_LPARAM(lParam) GET_FLAGS_LPARAM(lParam)
+
+#define APPCOMMAND_BROWSER_BACKWARD 1
+#define APPCOMMAND_BROWSER_FORWARD 2
+#define APPCOMMAND_BROWSER_REFRESH 3
+#define APPCOMMAND_BROWSER_STOP 4
+#define APPCOMMAND_BROWSER_SEARCH 5
+#define APPCOMMAND_BROWSER_FAVORITES 6
+#define APPCOMMAND_BROWSER_HOME 7
+#define APPCOMMAND_VOLUME_MUTE 8
+#define APPCOMMAND_VOLUME_DOWN 9
+#define APPCOMMAND_VOLUME_UP 10
+#define APPCOMMAND_MEDIA_NEXTTRACK 11
+#define APPCOMMAND_MEDIA_PREVIOUSTRACK 12
+#define APPCOMMAND_MEDIA_STOP 13
+#define APPCOMMAND_MEDIA_PLAY_PAUSE 14
+#define APPCOMMAND_LAUNCH_MAIL 15
+#define APPCOMMAND_LAUNCH_MEDIA_SELECT 16
+#define APPCOMMAND_LAUNCH_APP1 17
+#define APPCOMMAND_LAUNCH_APP2 18
+#define APPCOMMAND_BASS_DOWN 19
+#define APPCOMMAND_BASS_BOOST 20
+#define APPCOMMAND_BASS_UP 21
+#define APPCOMMAND_TREBLE_DOWN 22
+#define APPCOMMAND_TREBLE_UP 23
+#endif // FAPPCOMMAND_MOUSE
+
+// New commands from Windows XP (some even Sp1)
+#ifndef APPCOMMAND_MICROPHONE_VOLUME_MUTE
+#define APPCOMMAND_MICROPHONE_VOLUME_MUTE 24
+#define APPCOMMAND_MICROPHONE_VOLUME_DOWN 25
+#define APPCOMMAND_MICROPHONE_VOLUME_UP 26
+#define APPCOMMAND_HELP 27
+#define APPCOMMAND_FIND 28
+#define APPCOMMAND_NEW 29
+#define APPCOMMAND_OPEN 30
+#define APPCOMMAND_CLOSE 31
+#define APPCOMMAND_SAVE 32
+#define APPCOMMAND_PRINT 33
+#define APPCOMMAND_UNDO 34
+#define APPCOMMAND_REDO 35
+#define APPCOMMAND_COPY 36
+#define APPCOMMAND_CUT 37
+#define APPCOMMAND_PASTE 38
+#define APPCOMMAND_REPLY_TO_MAIL 39
+#define APPCOMMAND_FORWARD_MAIL 40
+#define APPCOMMAND_SEND_MAIL 41
+#define APPCOMMAND_SPELL_CHECK 42
+#define APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE 43
+#define APPCOMMAND_MIC_ON_OFF_TOGGLE 44
+#define APPCOMMAND_CORRECTION_LIST 45
+#define APPCOMMAND_MEDIA_PLAY 46
+#define APPCOMMAND_MEDIA_PAUSE 47
+#define APPCOMMAND_MEDIA_RECORD 48
+#define APPCOMMAND_MEDIA_FAST_FORWARD 49
+#define APPCOMMAND_MEDIA_REWIND 50
+#define APPCOMMAND_MEDIA_CHANNEL_UP 51
+#define APPCOMMAND_MEDIA_CHANNEL_DOWN 52
+#endif // APPCOMMAND_MICROPHONE_VOLUME_MUTE
+
+#if (_WIN32_WINNT < 0x0400)
+// This struct is defined in winuser.h if the _WIN32_WINNT >= 0x0400 -- in the
+// other cases we have to define it on our own.
+typedef struct tagTRACKMOUSEEVENT {
+ DWORD cbSize;
+ DWORD dwFlags;
+ HWND hwndTrack;
+ DWORD dwHoverTime;
+} TRACKMOUSEEVENT, *LPTRACKMOUSEEVENT;
+#endif
+#ifndef WM_MOUSELEAVE
+#define WM_MOUSELEAVE 0x02A3
+#endif
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include "private/qwidget_p.h"
+QT_END_INCLUDE_NAMESPACE
+
+static int translateButtonState(int s, int type, int button);
+
+// ##### get rid of this!
+QRgb qt_colorref2qrgb(COLORREF col)
+{
+ return qRgb(GetRValue(col),GetGValue(col),GetBValue(col));
+}
+
+
+/*****************************************************************************
+ Internal variables and functions
+ *****************************************************************************/
+
+static HWND curWin = 0; // current window
+static HDC displayDC = 0; // display device context
+
+// Session management
+static bool sm_blockUserInput = false;
+static bool sm_smActive = false;
+extern QSessionManager* qt_session_manager_self;
+static bool sm_cancel;
+
+static bool replayPopupMouseEvent = false; // replay handling when popups close
+
+// ignore the next release event if return from a modal widget
+Q_WIDGETS_EXPORT bool qt_win_ignoreNextMouseReleaseEvent = false;
+
+
+#if defined(QT_DEBUG)
+static bool appNoGrab = false; // mouse/keyboard grabbing
+#endif
+
+static bool app_do_modal = false; // modal mode
+extern QWidgetList *qt_modal_stack;
+extern QDesktopWidget *qt_desktopWidget;
+static QPointer<QWidget> popupButtonFocus;
+static bool qt_try_modal(QWidget *, MSG *, int& ret);
+
+QWidget *qt_button_down = 0; // widget got last button-down
+QPointer<QWidget> qt_last_mouse_receiver = 0;
+
+static HWND autoCaptureWnd = 0;
+static HWND imeParentWnd = 0;
+static void setAutoCapture(HWND); // automatic capture
+static void releaseAutoCapture();
+
+static void unregWinClasses();
+
+extern QCursor *qt_grab_cursor();
+
+#if defined(Q_WS_WIN)
+#define __export
+#endif
+
+extern "C" LRESULT QT_WIN_CALLBACK QtWndProc(HWND, UINT, WPARAM, LPARAM);
+
+class QETWidget : public QWidget // event translator widget
+{
+public:
+ QWExtra *xtra() { return d_func()->extraData(); }
+ QTLWExtra *topData() { return d_func()->topData(); }
+ QTLWExtra *maybeTopData() { return d_func()->maybeTopData(); }
+ void syncBackingStore(const QRegion &rgn) { d_func()->syncBackingStore(rgn); }
+ void syncBackingStore() { d_func()->syncBackingStore(); }
+ QWidgetData *dataPtr() { return data; }
+ QWidgetPrivate *dptr() { return d_func(); }
+ QRect frameStrut() const { return d_func()->frameStrut(); }
+ bool winEvent(MSG *m, long *r) { return QWidget::winEvent(m, r); }
+ void markFrameStrutDirty() { data->fstrut_dirty = 1; }
+ bool translateMouseEvent(const MSG &msg);
+ bool translateWheelEvent(const MSG &msg);
+ bool translatePaintEvent(const MSG &msg);
+ bool translateConfigEvent(const MSG &msg);
+ bool translateCloseEvent(const MSG &msg);
+ bool translateTabletEvent(const MSG &msg, PACKET *localPacketBuf, int numPackets);
+#ifndef QT_NO_GESTURES
+ bool translateGestureEvent(const MSG &msg, const GESTUREINFO &gi);
+#endif
+ void repolishStyle(QStyle &style);
+ inline void showChildren(bool spontaneous) { d_func()->showChildren(spontaneous); }
+ inline void hideChildren(bool spontaneous) { d_func()->hideChildren(spontaneous); }
+ inline uint testWindowState(uint teststate){ return dataPtr()->window_state & teststate; }
+ inline void setWindowTitle_helper(const QString &title) { d_func()->setWindowTitle_helper(title); }
+ inline void forceUpdate() {
+ QTLWExtra *tlwExtra = window()->d_func()->maybeTopData();
+ if (tlwExtra && tlwExtra->backingStore)
+ tlwExtra->backingStore->markDirty(rect(), this, true, true);
+ }
+};
+
+// need to get default font?
+extern bool qt_app_has_font;
+
+extern QFont qt_LOGFONTtoQFont(LOGFONT& lf,bool scale);
+
+static void qt_set_windows_color_resources()
+{
+ // Do the color settings
+ QPalette pal;
+ pal.setColor(QPalette::WindowText,
+ QColor(qt_colorref2qrgb(GetSysColor(COLOR_WINDOWTEXT))));
+ pal.setColor(QPalette::Button,
+ QColor(qt_colorref2qrgb(GetSysColor(COLOR_BTNFACE))));
+ pal.setColor(QPalette::Light,
+ QColor(qt_colorref2qrgb(GetSysColor(COLOR_BTNHIGHLIGHT))));
+ pal.setColor(QPalette::Dark,
+ QColor(qt_colorref2qrgb(GetSysColor(COLOR_BTNSHADOW))));
+ pal.setColor(QPalette::Mid, pal.button().color().darker(150));
+ pal.setColor(QPalette::Text,
+ QColor(qt_colorref2qrgb(GetSysColor(COLOR_WINDOWTEXT))));
+ pal.setColor(QPalette::BrightText,
+ QColor(qt_colorref2qrgb(GetSysColor(COLOR_BTNHIGHLIGHT))));
+ pal.setColor(QPalette::Base,
+ QColor(qt_colorref2qrgb(GetSysColor(COLOR_WINDOW))));
+ pal.setColor(QPalette::Window,
+ QColor(qt_colorref2qrgb(GetSysColor(COLOR_BTNFACE))));
+ pal.setColor(QPalette::ButtonText,
+ QColor(qt_colorref2qrgb(GetSysColor(COLOR_BTNTEXT))));
+ pal.setColor(QPalette::Midlight,
+ QColor(qt_colorref2qrgb(GetSysColor(COLOR_3DLIGHT))));
+ pal.setColor(QPalette::Shadow,
+ QColor(qt_colorref2qrgb(GetSysColor(COLOR_3DDKSHADOW))));
+ pal.setColor(QPalette::Highlight,
+ QColor(qt_colorref2qrgb(GetSysColor(COLOR_HIGHLIGHT))));
+ pal.setColor(QPalette::HighlightedText,
+ QColor(qt_colorref2qrgb(GetSysColor(COLOR_HIGHLIGHTTEXT))));
+
+#if defined(Q_WS_WINCE)
+ // ### hardcoded until I find out how to get it from the system settings.
+ pal.setColor(QPalette::LinkVisited, pal.highlight().color().dark(150));
+ pal.setColor(QPalette::Link, pal.highlight().color().light(130));
+ // Background == Base on Windows CE
+ if (qt_wince_is_smartphone() || qt_wince_is_pocket_pc())
+ pal.setColor(QPalette::Background, pal.base().color());
+#else
+ pal.setColor(QPalette::Link, Qt::blue);
+ pal.setColor(QPalette::LinkVisited, Qt::magenta);
+#endif
+
+
+
+ pal.setColor(QPalette::Inactive, QPalette::Button, pal.button().color());
+ pal.setColor(QPalette::Inactive, QPalette::Window, pal.background().color());
+ pal.setColor(QPalette::Inactive, QPalette::Light, pal.light().color());
+ pal.setColor(QPalette::Inactive, QPalette::Dark, pal.dark().color());
+
+ if (pal.midlight() == pal.button())
+ pal.setColor(QPalette::Midlight, pal.button().color().lighter(110));
+ if (pal.background() != pal.base()) {
+ pal.setColor(QPalette::Inactive, QPalette::Highlight, pal.color(QPalette::Inactive, QPalette::Window));
+ pal.setColor(QPalette::Inactive, QPalette::HighlightedText, pal.color(QPalette::Inactive, QPalette::Text));
+ }
+
+ const QColor bg = pal.background().color();
+ const QColor fg = pal.foreground().color(), btn = pal.button().color();
+ QColor disabled((fg.red()+btn.red())/2,(fg.green()+btn.green())/2,
+ (fg.blue()+btn.blue())/2);
+ pal.setColorGroup(QPalette::Disabled, pal.foreground(), pal.button(), pal.light(),
+ pal.dark(), pal.mid(), pal.text(), pal.brightText(), pal.base(), pal.background() );
+ pal.setColor(QPalette::Disabled, QPalette::WindowText, disabled);
+ pal.setColor(QPalette::Disabled, QPalette::Text, disabled);
+ pal.setColor(QPalette::Disabled, QPalette::ButtonText, disabled);
+ pal.setColor(QPalette::Disabled, QPalette::Highlight,
+ QColor(qt_colorref2qrgb(GetSysColor(COLOR_HIGHLIGHT))));
+ pal.setColor(QPalette::Disabled, QPalette::HighlightedText,
+ QColor(qt_colorref2qrgb(GetSysColor(COLOR_HIGHLIGHTTEXT))));
+ pal.setColor(QPalette::Disabled, QPalette::Base, bg);
+
+ QApplicationPrivate::setSystemPalette(pal);
+
+ QApplicationPrivate::initializeWidgetPaletteHash();
+
+ QColor ttip(qt_colorref2qrgb(GetSysColor(COLOR_INFOBK)));
+
+ QColor ttipText(qt_colorref2qrgb(GetSysColor(COLOR_INFOTEXT)));
+ {
+#ifndef QT_NO_TOOLTIP
+ QPalette tiplabel(pal);
+ tiplabel.setColor(QPalette::All, QPalette::Button, ttip);
+ tiplabel.setColor(QPalette::All, QPalette::Window, ttip);
+ tiplabel.setColor(QPalette::All, QPalette::Text, ttipText);
+ tiplabel.setColor(QPalette::All, QPalette::WindowText, ttipText);
+ tiplabel.setColor(QPalette::All, QPalette::ButtonText, ttipText);
+ tiplabel.setColor(QPalette::All, QPalette::Button, ttip);
+ tiplabel.setColor(QPalette::All, QPalette::Window, ttip);
+ tiplabel.setColor(QPalette::All, QPalette::Text, ttipText);
+ tiplabel.setColor(QPalette::All, QPalette::WindowText, ttipText);
+ tiplabel.setColor(QPalette::All, QPalette::ButtonText, ttipText);
+ const QColor fg = tiplabel.foreground().color(), btn = tiplabel.button().color();
+ QColor disabled((fg.red()+btn.red())/2,(fg.green()+btn.green())/2,
+ (fg.blue()+btn.blue())/2);
+ tiplabel.setColor(QPalette::Disabled, QPalette::WindowText, disabled);
+ tiplabel.setColor(QPalette::Disabled, QPalette::Text, disabled);
+ tiplabel.setColor(QPalette::Disabled, QPalette::Base, Qt::white);
+ tiplabel.setColor(QPalette::Disabled, QPalette::BrightText, Qt::white);
+ QToolTip::setPalette(tiplabel);
+#endif //QT_NO_TOOLTIP
+ }
+}
+
+static void qt_set_windows_font_resources()
+{
+#ifndef Q_WS_WINCE
+ NONCLIENTMETRICS ncm;
+ ncm.cbSize = FIELD_OFFSET(NONCLIENTMETRICS, lfMessageFont) + sizeof(LOGFONT);
+ SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize , &ncm, 0);
+
+ QFont menuFont = qt_LOGFONTtoQFont(ncm.lfMenuFont, true);
+ QFont messageFont = qt_LOGFONTtoQFont(ncm.lfMessageFont, true);
+ QFont statusFont = qt_LOGFONTtoQFont(ncm.lfStatusFont, true);
+ QFont titleFont = qt_LOGFONTtoQFont(ncm.lfCaptionFont, true);
+
+ LOGFONT lfIconTitleFont;
+ SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lfIconTitleFont), &lfIconTitleFont, 0);
+ QFont iconTitleFont = qt_LOGFONTtoQFont(lfIconTitleFont, true);
+
+ QApplication::setFont(menuFont, "QMenu");
+ QApplication::setFont(menuFont, "QMenuBar");
+ QApplication::setFont(messageFont, "QMessageBox");
+ QApplication::setFont(statusFont, "QTipLabel");
+ QApplication::setFont(statusFont, "QStatusBar");
+ QApplication::setFont(titleFont, "Q3TitleBar");
+ QApplication::setFont(titleFont, "QWorkspaceTitleBar");
+ QApplication::setFont(iconTitleFont, "QAbstractItemView");
+ QApplication::setFont(iconTitleFont, "QDockWidgetTitle");
+
+#else
+ LOGFONT lf;
+ HGDIOBJ stockFont = GetStockObject(SYSTEM_FONT);
+ GetObject(stockFont, sizeof(lf), &lf);
+ QFont systemFont = qt_LOGFONTtoQFont(lf, true);
+ QApplicationPrivate::setSystemFont(systemFont);
+ QFont smallerFont = systemFont;
+ if (qt_wince_is_mobile()) {
+ smallerFont.setPointSize(systemFont.pointSize()-1);
+ QApplication::setFont(smallerFont, "QTabBar");
+ smallerFont.setBold(true);
+ QApplication::setFont(smallerFont, "QAbstractButton");
+ }
+#endif// Q_WS_WINCE
+}
+
+static void qt_win_read_cleartype_settings()
+{
+ UINT result = 0;
+#ifdef Q_OS_WINCE
+ if (SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &result, 0))
+ qt_cleartype_enabled = result;
+#else
+ if (SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &result, 0))
+ qt_cleartype_enabled = (result == FE_FONTSMOOTHINGCLEARTYPE);
+#endif
+
+ int winSmooth;
+ if (SystemParametersInfo(0x200C /* SPI_GETFONTSMOOTHINGCONTRAST */, 0, &winSmooth, 0)) {
+ qt_fontsmoothing_gamma = winSmooth / qreal(1000.0);
+ } else {
+ qt_fontsmoothing_gamma = 1.0;
+ }
+
+ // Safeguard ourselves against corrupt registry values...
+ if (qt_fontsmoothing_gamma > 5 || qt_fontsmoothing_gamma < 1)
+ qt_fontsmoothing_gamma = qreal(1.4);
+}
+
+static void qt_set_windows_resources()
+{
+ if (QApplication::type() != QApplication::Tty)
+ (void) QApplication::style(); // trigger creation of application style
+ qt_set_windows_font_resources();
+ qt_set_windows_color_resources();
+}
+
+void QApplicationPrivate::initializeWidgetPaletteHash()
+{
+ QPalette pal = *QApplicationPrivate::sys_pal;
+ QColor menuCol(qt_colorref2qrgb(GetSysColor(COLOR_MENU)));
+ QColor menuText(qt_colorref2qrgb(GetSysColor(COLOR_MENUTEXT)));
+ BOOL isFlat = false;
+ if ((QSysInfo::WindowsVersion >= QSysInfo::WV_XP
+ && QSysInfo::WindowsVersion < QSysInfo::WV_NT_based))
+ SystemParametersInfo(SPI_GETFLATMENU, 0, &isFlat, 0);
+ QPalette menu(pal);
+ // we might need a special color group for the menu.
+ menu.setColor(QPalette::Active, QPalette::Button, menuCol);
+ menu.setColor(QPalette::Active, QPalette::Text, menuText);
+ menu.setColor(QPalette::Active, QPalette::WindowText, menuText);
+ menu.setColor(QPalette::Active, QPalette::ButtonText, menuText);
+ const QColor fg = menu.foreground().color(), btn = menu.button().color();
+ QColor disabled(qt_colorref2qrgb(GetSysColor(COLOR_GRAYTEXT)));
+ menu.setColor(QPalette::Disabled, QPalette::WindowText, disabled);
+ menu.setColor(QPalette::Disabled, QPalette::Text, disabled);
+ menu.setColor(QPalette::Disabled, QPalette::Highlight,
+ QColor(qt_colorref2qrgb(GetSysColor(
+ (QSysInfo::WindowsVersion >= QSysInfo::WV_XP
+ && QSysInfo::WindowsVersion < QSysInfo::WV_NT_based)
+ && isFlat ? COLOR_MENUHILIGHT : COLOR_HIGHLIGHT))));
+ menu.setColor(QPalette::Disabled, QPalette::HighlightedText, disabled);
+ menu.setColor(QPalette::Disabled, QPalette::Button,
+ menu.color(QPalette::Active, QPalette::Button));
+ menu.setColor(QPalette::Inactive, QPalette::Button,
+ menu.color(QPalette::Active, QPalette::Button));
+ menu.setColor(QPalette::Inactive, QPalette::Text,
+ menu.color(QPalette::Active, QPalette::Text));
+ menu.setColor(QPalette::Inactive, QPalette::WindowText,
+ menu.color(QPalette::Active, QPalette::WindowText));
+ menu.setColor(QPalette::Inactive, QPalette::ButtonText,
+ menu.color(QPalette::Active, QPalette::ButtonText));
+ menu.setColor(QPalette::Inactive, QPalette::Highlight,
+ menu.color(QPalette::Active, QPalette::Highlight));
+ menu.setColor(QPalette::Inactive, QPalette::HighlightedText,
+ menu.color(QPalette::Active, QPalette::HighlightedText));
+ menu.setColor(QPalette::Inactive, QPalette::ButtonText,
+ pal.color(QPalette::Inactive, QPalette::Dark));
+ QApplication::setPalette(menu, "QMenu");
+
+ if ((QSysInfo::WindowsVersion >= QSysInfo::WV_XP
+ && QSysInfo::WindowsVersion < QSysInfo::WV_NT_based) && isFlat) {
+ QColor menubar(qt_colorref2qrgb(GetSysColor(COLOR_MENUBAR)));
+ menu.setColor(QPalette::Active, QPalette::Button, menubar);
+ menu.setColor(QPalette::Disabled, QPalette::Button, menubar);
+ menu.setColor(QPalette::Inactive, QPalette::Button, menubar);
+ }
+ QApplication::setPalette(menu, "QMenuBar");
+}
+
+static void qt_set_windows_updateScrollBar(QWidget *widget)
+{
+ QList<QObject*> children = widget->children();
+ for (int i = 0; i < children.size(); ++i) {
+ QObject *o = children.at(i);
+ if(!o->isWidgetType())
+ continue;
+ if (QWidget *w = static_cast<QWidget *>(o))
+ qt_set_windows_updateScrollBar(w);
+ }
+#ifndef QT_NO_SCROLLBAR
+ if (qobject_cast<QScrollBar*>(widget))
+ widget->updateGeometry();
+#endif
+}
+
+
+/*****************************************************************************
+ qt_init() - initializes Qt for Windows
+ *****************************************************************************/
+
+typedef BOOL (WINAPI *PtrSetProcessDPIAware) (VOID);
+static PtrSetProcessDPIAware ptrSetProcessDPIAware = 0;
+PtrUpdateLayeredWindow ptrUpdateLayeredWindow = 0;
+PtrUpdateLayeredWindowIndirect ptrUpdateLayeredWindowIndirect = 0;
+static BOOL WINAPI qt_updateLayeredWindowIndirect(HWND hwnd, const Q_UPDATELAYEREDWINDOWINFO *info)
+{
+ return (*ptrUpdateLayeredWindow)(hwnd, info->hdcDst, info->pptDst, info->psize, info->hdcSrc,
+ info->pptSrc, info->crKey, info->pblend, info->dwFlags);
+}
+
+void qt_init(QApplicationPrivate *priv, int)
+{
+
+ int argc = priv->argc;
+ char **argv = priv->argv;
+ int i, j;
+
+ // Get command line params
+
+ j = argc ? 1 : 0;
+ for (i=1; i<argc; i++) {
+ if (argv[i] && *argv[i] != '-') {
+ argv[j++] = argv[i];
+ continue;
+ }
+#if defined(QT_DEBUG)
+ if (qstrcmp(argv[i], "-nograb") == 0)
+ appNoGrab = !appNoGrab;
+ else
+#endif // QT_DEBUG
+ argv[j++] = argv[i];
+ }
+ if(j < priv->argc) {
+ priv->argv[j] = 0;
+ priv->argc = j;
+ }
+
+#ifndef Q_WS_WINCE
+ // No message boxes but important ones
+ SetErrorMode(SetErrorMode(0) | SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
+#endif
+
+#ifndef Q_WS_WINCE
+ // Initialize OLE/COM
+ // S_OK means success and S_FALSE means that it has already
+ // been initialized
+ HRESULT r;
+ r = OleInitialize(0);
+ if (r != S_OK && r != S_FALSE) {
+ qWarning("Qt: Could not initialize OLE (error %x)", (unsigned int)r);
+ }
+#endif
+
+ // Misc. initialization
+#if defined(QT_DEBUG) && !defined(Q_WS_WINCE)
+ GdiSetBatchLimit(1);
+#endif
+
+ // initialize key mapper
+ QKeyMapper::changeKeyboard();
+
+ QColormap::initialize();
+ QFont::initialize();
+#ifndef QT_NO_CURSOR
+ QCursorData::initialize();
+#endif
+ qApp->setObjectName(priv->appName());
+
+ // default font
+#ifndef Q_WS_WINCE
+ HGDIOBJ stockFont = GetStockObject(DEFAULT_GUI_FONT);
+#else
+ HGDIOBJ stockFont = GetStockObject(SYSTEM_FONT);
+#endif
+
+ LOGFONT lf;
+ GetObject(stockFont, sizeof(lf), &lf);
+ QFont systemFont = qt_LOGFONTtoQFont(lf, true);
+
+#ifndef Q_WS_WINCE
+ if (systemFont.family() == QLatin1String("MS Shell Dlg")) {
+ systemFont.setFamily(QLatin1String("MS Shell Dlg 2"));
+ }
+#endif
+
+ QApplicationPrivate::setSystemFont(systemFont);
+
+ // QFont::locale_init(); ### Uncomment when it does something on Windows
+
+ if (QApplication::desktopSettingsAware())
+ qt_set_windows_resources();
+
+#ifndef QT_NO_TABLETEVENT
+ initWinTabFunctions();
+#endif // QT_NO_TABLETEVENT
+ QApplicationPrivate::inputContext = new QWinInputContext(0);
+
+ // Read the initial cleartype settings...
+ qt_win_read_cleartype_settings();
+ qt_win_owndc_required = false;
+
+ extern void qt_win_initialize_directdraw();
+ qt_win_initialize_directdraw();
+
+#ifndef Q_OS_WINCE
+ ptrUpdateLayeredWindowIndirect =
+ (PtrUpdateLayeredWindowIndirect) QSystemLibrary::resolve(QLatin1String("user32"),
+ "UpdateLayeredWindowIndirect");
+ ptrUpdateLayeredWindow =
+ (PtrUpdateLayeredWindow) QSystemLibrary::resolve(QLatin1String("user32"),
+ "UpdateLayeredWindow");
+
+ if (ptrUpdateLayeredWindow && !ptrUpdateLayeredWindowIndirect)
+ ptrUpdateLayeredWindowIndirect = qt_updateLayeredWindowIndirect;
+
+ // Notify Vista and Windows 7 that we support highter DPI settings
+ ptrSetProcessDPIAware = (PtrSetProcessDPIAware)
+ QSystemLibrary::resolve(QLatin1String("user32"), "SetProcessDPIAware");
+ if (ptrSetProcessDPIAware)
+ ptrSetProcessDPIAware();
+#endif
+
+#ifndef QT_NO_GESTURES
+ priv->GetGestureInfo = 0;
+ priv->GetGestureExtraArgs = 0;
+ priv->CloseGestureInfoHandle = 0;
+ priv->SetGestureConfig = 0;
+ priv->GetGestureConfig = 0;
+ priv->BeginPanningFeedback = 0;
+ priv->UpdatePanningFeedback = 0;
+ priv->EndPanningFeedback = 0;
+
+#if defined(Q_WS_WINCE_WM) && defined(QT_WINCE_GESTURES)
+ priv->GetGestureInfo = (PtrGetGestureInfo) &TKGetGestureInfo;
+ priv->GetGestureExtraArgs = (PtrGetGestureExtraArgs) &TKGetGestureExtraArguments;
+#elif !defined(Q_WS_WINCE)
+ #if !defined(QT_NO_NATIVE_GESTURES)
+ priv->GetGestureInfo =
+ (PtrGetGestureInfo)QSystemLibrary::resolve(QLatin1String("user32"),
+ "GetGestureInfo");
+ priv->GetGestureExtraArgs =
+ (PtrGetGestureExtraArgs)QSystemLibrary::resolve(QLatin1String("user32"),
+ "GetGestureExtraArgs");
+ priv->CloseGestureInfoHandle =
+ (PtrCloseGestureInfoHandle)QSystemLibrary::resolve(QLatin1String("user32"),
+ "CloseGestureInfoHandle");
+ priv->SetGestureConfig =
+ (PtrSetGestureConfig)QSystemLibrary::resolve(QLatin1String("user32"),
+ "SetGestureConfig");
+ priv->GetGestureConfig =
+ (PtrGetGestureConfig)QSystemLibrary::resolve(QLatin1String("user32"),
+ "GetGestureConfig");
+ #endif // QT_NO_NATIVE_GESTURES
+ QSystemLibrary libTheme(QLatin1String("uxtheme"));
+ priv->BeginPanningFeedback =
+ (PtrBeginPanningFeedback)libTheme.resolve("BeginPanningFeedback");
+ priv->UpdatePanningFeedback =
+ (PtrUpdatePanningFeedback)libTheme.resolve("UpdatePanningFeedback");
+ priv->EndPanningFeedback =
+ (PtrEndPanningFeedback)libTheme.resolve("EndPanningFeedback");
+#endif
+#endif // QT_NO_GESTURES
+}
+
+/*****************************************************************************
+ qt_cleanup() - cleans up when the application is finished
+ *****************************************************************************/
+
+void qt_cleanup()
+{
+ unregWinClasses();
+ QPixmapCache::clear();
+
+#ifndef QT_NO_CURSOR
+ QCursorData::cleanup();
+#endif
+ QFont::cleanup();
+ QColormap::cleanup();
+ if (displayDC) {
+ ReleaseDC(0, displayDC);
+ displayDC = 0;
+ }
+
+ delete QApplicationPrivate::inputContext;
+ QApplicationPrivate::inputContext = 0;
+
+#ifndef Q_WS_WINCE
+ // Deinitialize OLE/COM
+ OleUninitialize();
+#endif
+}
+
+
+/*****************************************************************************
+ Platform specific global and internal functions
+ *****************************************************************************/
+
+Q_WIDGETS_EXPORT HDC qt_win_display_dc() // get display DC
+{
+ Q_ASSERT(qApp && qApp->thread() == QThread::currentThread());
+ if (!displayDC)
+ displayDC = GetDC(0);
+ return displayDC;
+}
+
+bool qt_nograb() // application no-grab option
+{
+#if defined(QT_DEBUG)
+ return appNoGrab;
+#else
+ return false;
+#endif
+}
+
+typedef QHash<QString, int> WinClassNameHash;
+Q_GLOBAL_STATIC(WinClassNameHash, winclassNames)
+
+//
+// If 0 is passed as the widget pointer, register a window class
+// for QWidget as default. This is used in QGLTemporaryContext
+// during GL initialization, where we don't want to use temporary
+// QWidgets or QGLWidgets, neither do we want to have separate code
+// to register window classes.
+//
+const QString qt_reg_winclass(QWidget *w) // register window class
+{
+ Qt::WindowFlags flags = w ? w->windowFlags() : (Qt::WindowFlags)0;
+ Qt::WindowFlags type = flags & Qt::WindowType_Mask;
+
+ uint style;
+ bool icon;
+ QString cname;
+ if (w && qt_widget_private(w)->isGLWidget) {
+ cname = QLatin1String("QGLWidget");
+ style = CS_DBLCLKS;
+#ifndef Q_WS_WINCE
+ style |= CS_OWNDC;
+#endif
+ icon = true;
+ } else if (w && (flags & Qt::MSWindowsOwnDC)) {
+ cname = QLatin1String("QWidgetOwnDC");
+ style = CS_DBLCLKS;
+#ifndef Q_WS_WINCE
+ style |= CS_OWNDC;
+#endif
+ icon = true;
+ } else if (w && (type == Qt::Tool || type == Qt::ToolTip)) {
+ style = CS_DBLCLKS;
+ if (w->inherits("QTipLabel") || w->inherits("QAlphaWidget")) {
+ if ((QSysInfo::WindowsVersion >= QSysInfo::WV_XP
+ && QSysInfo::WindowsVersion < QSysInfo::WV_NT_based)) {
+ style |= CS_DROPSHADOW;
+ }
+ cname = QLatin1String("QToolTip");
+ } else {
+ cname = QLatin1String("QTool");
+ }
+#ifndef Q_WS_WINCE
+ style |= CS_SAVEBITS;
+#endif
+ icon = false;
+ } else if (w && (type == Qt::Popup)) {
+ cname = QLatin1String("QPopup");
+ style = CS_DBLCLKS;
+#ifndef Q_WS_WINCE
+ style |= CS_SAVEBITS;
+#endif
+ if ((QSysInfo::WindowsVersion >= QSysInfo::WV_XP
+ && QSysInfo::WindowsVersion < QSysInfo::WV_NT_based))
+ style |= CS_DROPSHADOW;
+ icon = false;
+ } else {
+ cname = QLatin1String("QWidget");
+ style = CS_DBLCLKS;
+ icon = true;
+ }
+
+#ifndef Q_WS_WINCE
+ // force CS_OWNDC when the GL graphics system is
+ // used as the default renderer
+ if (qt_win_owndc_required)
+ style |= CS_OWNDC;
+#endif
+
+#ifdef Q_OS_WINCE
+ // We need to register the classes with the
+ // unique ID on WinCE to make sure we can
+ // move the windows to the front when starting
+ // a second instance.
+ wchar_t uniqueAppID[MAX_PATH];
+ GetModuleFileName(0, uniqueAppID, MAX_PATH);
+ cname = QString::number(RegisterWindowMessage(
+ (const wchar_t *) QString::fromWCharArray(uniqueAppID).toLower().replace(QLatin1Char('\\'),
+ QLatin1Char('_')).utf16()));
+#endif
+
+ // since multiple Qt versions can be used in one process
+ // each one has to have window class names with a unique name
+ // The first instance gets the unmodified name; if the class
+ // has already been registered by another instance of Qt then
+ // add an instance-specific ID, the address of the window proc.
+ static int classExists = -1;
+
+ if (classExists == -1) {
+ WNDCLASS wcinfo;
+ classExists = GetClassInfo((HINSTANCE)qWinAppInst(), (wchar_t*)cname.utf16(), &wcinfo);
+ classExists = classExists && wcinfo.lpfnWndProc != QtWndProc;
+ }
+
+ if (classExists)
+ cname += QString::number((quintptr)QtWndProc);
+
+ if (winclassNames()->contains(cname)) // already registered in our list
+ return cname;
+
+#ifndef Q_WS_WINCE
+ WNDCLASSEX wc;
+ wc.cbSize = sizeof(WNDCLASSEX);
+#else
+ WNDCLASS wc;
+#endif
+ wc.style = style;
+ wc.lpfnWndProc = (WNDPROC)QtWndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = qWinAppInst();
+ if (icon) {
+ wc.hIcon = (HICON)LoadImage(qWinAppInst(), L"IDI_ICON1", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
+#ifndef Q_WS_WINCE
+ if (wc.hIcon) {
+ int sw = GetSystemMetrics(SM_CXSMICON);
+ int sh = GetSystemMetrics(SM_CYSMICON);
+ wc.hIconSm = (HICON)LoadImage(qWinAppInst(), L"IDI_ICON1", IMAGE_ICON, sw, sh, 0);
+ } else {
+ wc.hIcon = (HICON)LoadImage(0, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
+ wc.hIconSm = 0;
+ }
+#endif
+ } else {
+ wc.hIcon = 0;
+#ifndef Q_WS_WINCE
+ wc.hIconSm = 0;
+#endif
+ }
+ wc.hCursor = 0;
+#ifndef Q_WS_WINCE
+ HBRUSH brush = 0;
+ if (w && !qt_widget_private(w)->isGLWidget)
+ brush = (HBRUSH)GetSysColorBrush(COLOR_WINDOW);
+ wc.hbrBackground = brush;
+#else
+ wc.hbrBackground = 0;
+#endif
+ wc.lpszMenuName = 0;
+ wc.lpszClassName = (wchar_t*)cname.utf16();
+
+#ifndef Q_WS_WINCE
+ ATOM atom = RegisterClassEx(&wc);
+#else
+ ATOM atom = RegisterClass(&wc);
+#endif
+
+#ifndef QT_NO_DEBUG
+ if (!atom)
+ qErrnoWarning("QApplication::regClass: Registering window class failed.");
+#else
+ Q_UNUSED(atom);
+#endif
+
+ winclassNames()->insert(cname, 1);
+ return cname;
+}
+
+Q_WIDGETS_EXPORT const QString qt_getRegisteredWndClass()
+{
+ return qt_reg_winclass(0);
+}
+
+static void unregWinClasses()
+{
+ WinClassNameHash *hash = winclassNames();
+ QHash<QString, int>::ConstIterator it = hash->constBegin();
+ while (it != hash->constEnd()) {
+ UnregisterClass((wchar_t*)it.key().utf16(), qWinAppInst());
+ ++it;
+ }
+ hash->clear();
+}
+
+
+/*****************************************************************************
+ Safe configuration (move,resize,setGeometry) mechanism to avoid
+ recursion when processing messages.
+ *****************************************************************************/
+
+struct QWinConfigRequest {
+ WId id; // widget to be configured
+ int req; // 0=move, 1=resize, 2=setGeo
+ int x, y, w, h; // request parameters
+};
+
+static QList<QWinConfigRequest*> *configRequests = 0;
+
+void qWinRequestConfig(WId id, int req, int x, int y, int w, int h)
+{
+ if (!configRequests) // create queue
+ configRequests = new QList<QWinConfigRequest*>;
+ QWinConfigRequest *r = new QWinConfigRequest;
+ r->id = id; // create new request
+ r->req = req;
+ r->x = x;
+ r->y = y;
+ r->w = w;
+ r->h = h;
+ configRequests->append(r); // store request in queue
+}
+
+static void qWinProcessConfigRequests() // perform requests in queue
+{
+ if (!configRequests)
+ return;
+ QWinConfigRequest *r;
+ for (;;) {
+ if (configRequests->isEmpty())
+ break;
+ r = configRequests->takeLast();
+ QWidget *w = QWidget::find(r->id);
+ QRect rect(r->x, r->y, r->w, r->h);
+ int req = r->req;
+ delete r;
+
+ if ( w ) { // widget exists
+ if (w->testAttribute(Qt::WA_WState_ConfigPending))
+ return; // biting our tail
+ if (req == 0)
+ w->move(rect.topLeft());
+ else if (req == 1)
+ w->resize(rect.size());
+ else
+ w->setGeometry(rect);
+ }
+ }
+ delete configRequests;
+ configRequests = 0;
+}
+
+
+/*****************************************************************************
+ GUI event dispatcher
+ *****************************************************************************/
+
+class QGuiEventDispatcherWin32 : public QEventDispatcherWin32
+{
+ Q_DECLARE_PRIVATE(QEventDispatcherWin32)
+public:
+ QGuiEventDispatcherWin32(QObject *parent = 0);
+ bool processEvents(QEventLoop::ProcessEventsFlags flags);
+};
+
+QGuiEventDispatcherWin32::QGuiEventDispatcherWin32(QObject *parent)
+ : QEventDispatcherWin32(parent)
+{
+ createInternalHwnd();
+}
+
+bool QGuiEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
+{
+ if (!QEventDispatcherWin32::processEvents(flags))
+ return false;
+
+ if (configRequests) // any pending configs?
+ qWinProcessConfigRequests();
+
+ return true;
+}
+
+void QApplicationPrivate::createEventDispatcher()
+{
+ Q_Q(QApplication);
+ if (q->type() != QApplication::Tty)
+ eventDispatcher = new QGuiEventDispatcherWin32(q);
+ else
+ eventDispatcher = new QEventDispatcherWin32(q);
+}
+
+/*****************************************************************************
+ Platform specific QApplication members
+ *****************************************************************************/
+
+#ifdef QT3_SUPPORT
+void QApplication::setMainWidget(QWidget *mainWidget)
+{
+ QApplicationPrivate::main_widget = mainWidget;
+ if (QApplicationPrivate::main_widget && windowIcon().isNull()
+ && QApplicationPrivate::main_widget->testAttribute(Qt::WA_SetWindowIcon))
+ setWindowIcon(QApplicationPrivate::main_widget->windowIcon());
+}
+#endif
+
+#ifndef QT_NO_CURSOR
+
+/*****************************************************************************
+ QApplication cursor stack
+ *****************************************************************************/
+
+void QApplication::setOverrideCursor(const QCursor &cursor)
+{
+ qApp->d_func()->cursor_list.prepend(cursor);
+ SetCursor(qApp->d_func()->cursor_list.first().handle());
+}
+
+void QApplication::restoreOverrideCursor()
+{
+ if (qApp->d_func()->cursor_list.isEmpty())
+ return;
+ qApp->d_func()->cursor_list.removeFirst();
+
+ if (!qApp->d_func()->cursor_list.isEmpty()) {
+ SetCursor(qApp->d_func()->cursor_list.first().handle());
+ } else {
+ QWidget *w = QWidget::find(curWin);
+ if (w)
+ SetCursor(w->cursor().handle());
+ else
+ SetCursor(QCursor(Qt::ArrowCursor).handle());
+ }
+}
+
+#endif
+
+/*
+ Internal function called from QWidget::setCursor()
+ force is true if this function is called from dispatchEnterLeave, it means that the
+ mouse is actually directly under this widget.
+*/
+
+#ifndef QT_NO_CURSOR
+void qt_win_set_cursor(QWidget *w, bool force)
+{
+ static QPointer<QWidget> lastUnderMouse = 0;
+ if (force) {
+ lastUnderMouse = w;
+ } else if (w->testAttribute(Qt::WA_WState_Created) && lastUnderMouse
+ && lastUnderMouse->effectiveWinId() == w->effectiveWinId()) {
+ w = lastUnderMouse;
+ }
+
+ if (!curWin && w && w->internalWinId())
+ return;
+ QWidget* cW = w && !w->internalWinId() ? w : QWidget::find(curWin);
+ if (!cW || cW->window() != w->window() ||
+ !cW->isVisible() || !cW->underMouse() || QApplication::overrideCursor())
+ return;
+
+ SetCursor(cW->cursor().handle());
+}
+#endif // QT_NO_CURSOR
+
+Qt::KeyboardModifiers qt_win_getKeyboardModifiers()
+{
+ Qt::KeyboardModifiers modifiers = Qt::NoModifier;
+ if (GetKeyState(VK_SHIFT) < 0)
+ modifiers |= Qt::ShiftModifier;
+ if (GetKeyState(VK_CONTROL) < 0)
+ modifiers |= Qt::ControlModifier;
+ if (GetKeyState(VK_MENU) < 0)
+ modifiers |= Qt::AltModifier;
+ return modifiers;
+}
+
+/*****************************************************************************
+ Routines to find a Qt widget from a screen position
+ *****************************************************************************/
+
+QWidget *QApplication::topLevelAt(const QPoint &pos)
+{
+ POINT p;
+ HWND win;
+ QWidget *w;
+ p.x = pos.x();
+ p.y = pos.y();
+ win = WindowFromPoint(p);
+ if (!win)
+ return 0;
+
+ w = QWidget::find(win);
+ while (!w && win) {
+ win = GetParent(win);
+ w = QWidget::find(win);
+ }
+ return w ? w->window() : 0;
+}
+
+void QApplication::beep()
+{
+ MessageBeep(MB_OK);
+}
+
+static void alert_widget(QWidget *widget, int duration)
+{
+#ifdef Q_OS_WINCE
+ Q_UNUSED(widget);
+ Q_UNUSED(duration);
+#else
+ bool stopFlash = duration < 0;
+
+ if (widget && (!widget->isActiveWindow() || stopFlash)) {
+ DWORD timeOut = GetCaretBlinkTime();
+ if (timeOut <= 0)
+ timeOut = 250;
+
+ UINT flashCount;
+ if (duration == 0)
+ flashCount = 10;
+ else
+ flashCount = duration/timeOut;
+
+ FLASHWINFO info;
+ info.cbSize = sizeof(info);
+ info.hwnd = widget->window()->winId();
+ info.dwFlags = stopFlash ? FLASHW_STOP : FLASHW_TRAY;
+ info.dwTimeout = stopFlash ? 0 : timeOut;
+ info.uCount = stopFlash ? 0 : flashCount;
+
+ FlashWindowEx(&info);
+ }
+#endif
+}
+
+void QApplication::alert(QWidget *widget, int duration)
+{
+ if (!QApplicationPrivate::checkInstance("alert"))
+ return;
+
+ if (widget) {
+ alert_widget(widget, duration);
+ } else {
+ const QWidgetList toplevels(topLevelWidgets());
+ for (int i = 0; i < toplevels.count(); ++i) {
+ QWidget *topLevel = toplevels.at(i);
+ alert_widget(topLevel, duration);
+ }
+ }
+}
+
+QString QApplicationPrivate::appName() const
+{
+ return QCoreApplicationPrivate::appName();
+}
+
+
+/*****************************************************************************
+ Main event loop
+ *****************************************************************************/
+
+extern uint qGlobalPostedEventsCount();
+
+void QApplication::winFocus(QWidget *widget, bool gotFocus)
+{
+ if (d_func()->inPopupMode()) // some delayed focus event to ignore
+ return;
+ if (gotFocus) {
+ setActiveWindow(widget);
+ if (QApplicationPrivate::active_window
+ && (QApplicationPrivate::active_window->windowType() == Qt::Dialog)) {
+ // raise the entire application, not just the dialog
+ QWidget* mw = QApplicationPrivate::active_window;
+#ifndef Q_WS_WINCE
+ while(mw->parentWidget() && (mw->windowType() == Qt::Dialog))
+ mw = mw->parentWidget()->window();
+ if (mw->testAttribute(Qt::WA_WState_Created) && mw != QApplicationPrivate::active_window)
+ SetWindowPos(mw->internalWinId(), HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
+#else
+ // On Desktop Windows, we set the first parent of the dialog on top
+ // Child windows will be automatically set above again.
+ // On Windows CE we pass no parent in CreateWindowEx as otherwise
+ // dialogs get embedded into the parent window. Thus we need to
+ // manually iterate and reactivate all windows from bottom up.
+ QList<QWidget*> raiseList;
+ raiseList.push_back(mw);
+ while(mw->parentWidget() && (mw->windowType() == Qt::Dialog)) {
+ mw = mw->parentWidget()->window();
+ raiseList.push_back(mw);
+ }
+ while(!raiseList.isEmpty()) {
+ mw = raiseList.takeLast();
+ if (mw->testAttribute(Qt::WA_WState_Created)) {
+ HWND state = HWND_TOP;
+ if (mw->windowFlags() & Qt::WindowStaysOnBottomHint)
+ state = HWND_BOTTOM;
+ else if (mw->windowFlags() & Qt::WindowStaysOnTopHint)
+ state = HWND_TOPMOST;
+ SetWindowPos(mw->internalWinId(), state, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
+ }
+ }
+#endif
+ }
+ } else {
+ setActiveWindow(0);
+ }
+}
+
+
+//
+// QtWndProc() receives all messages from the main event loop
+//
+
+static bool inLoop = false;
+static int inputcharset = CP_ACP;
+
+#define RETURN(x) { inLoop=false;return x; }
+
+static bool qt_is_translatable_mouse_event(UINT message)
+{
+ return (((message >= WM_MOUSEFIRST && message <= WM_MOUSELAST) ||
+ (message >= WM_XBUTTONDOWN && message <= WM_XBUTTONDBLCLK))
+ && message != WM_MOUSEWHEEL
+ && message != WM_MOUSEHWHEEL)
+
+#ifndef Q_WS_WINCE
+ || (message >= WM_NCMOUSEMOVE && message <= WM_NCMBUTTONDBLCLK)
+#endif
+ ;
+}
+
+extern "C" LRESULT QT_WIN_CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ bool result = true;
+ QEvent::Type evt_type = QEvent::None;
+ QETWidget *widget = 0;
+
+ // there is no need to process pakcets from tablet unless
+ // it is actually on the tablet, a flag to let us know...
+ int nPackets; // the number of packets we get from the queue
+
+ long res = 0;
+ if (!qApp) // unstable app state
+ RETURN(QWinInputContext::DefWindowProc(hwnd,message,wParam,lParam))
+
+ QScopedLoopLevelCounter loopLevelCounter(QThreadData::get2(qApp->thread()));
+
+#if 0
+ // make sure we update widgets also when the user resizes
+ if (inLoop && qApp->loopLevel())
+ qApp->sendPostedEvents(0, QEvent::Paint);
+#endif
+
+ inLoop = true;
+
+ MSG msg;
+ msg.hwnd = hwnd; // create MSG structure
+ msg.message = message; // time and pt fields ignored
+ msg.wParam = wParam;
+ msg.lParam = lParam;
+ msg.pt.x = GET_X_LPARAM(lParam);
+ msg.pt.y = GET_Y_LPARAM(lParam);
+ // If it's a non-client-area message the coords are screen coords, otherwise they are
+ // client coords.
+#ifndef Q_WS_WINCE
+ if (message < WM_NCMOUSEMOVE || message > WM_NCMBUTTONDBLCLK)
+#endif
+ ClientToScreen(msg.hwnd, &msg.pt);
+
+ /*
+ // sometimes the autograb is not released, so the clickevent is sent
+ // to the wrong window. We ignore this for now, because it doesn't
+ // cause any problems.
+ if (msg.message == WM_LBUTTONDOWN || msg.message == WM_RBUTTONDOWN || msg.message == WM_MBUTTONDOWN) {
+ HWND handle = WindowFromPoint(msg.pt);
+ if (msg.hwnd != handle) {
+ msg.hwnd = handle;
+ hwnd = handle;
+ }
+ }
+ */
+
+#if defined(QT_NON_COMMERCIAL)
+ QT_NC_WNDPROC
+#endif
+
+ // send through app filter
+ if (qApp->filterEvent(&msg, &res))
+ return res;
+
+ // close any opened ime candidate window (enabled only on a popup widget)
+ if (imeParentWnd && QApplication::activePopupWidget()
+ && (message == WM_MBUTTONDOWN || message == WM_XBUTTONDOWN
+ || message == WM_LBUTTONDOWN || message == WM_RBUTTONDOWN
+#ifndef Q_WS_WINCE
+ || message == WM_NCMBUTTONDOWN || message == WM_NCLBUTTONDOWN
+ || message == WM_NCRBUTTONDOWN)) {
+#else
+ )) {
+#endif
+ ::SendMessage(imeParentWnd, WM_IME_ENDCOMPOSITION, 0, 0);
+ }
+
+ switch (message) {
+#ifndef Q_WS_WINCE
+#ifndef QT_NO_SESSIONMANAGER
+ case WM_QUERYENDSESSION: {
+ if (sm_smActive) // bogus message from windows
+ RETURN(true);
+
+ sm_smActive = true;
+ sm_blockUserInput = true; // prevent user-interaction outside interaction windows
+ sm_cancel = false;
+ if (qt_session_manager_self)
+ qApp->commitData(*qt_session_manager_self);
+ if (lParam & ENDSESSION_LOGOFF) {
+ _flushall();
+ }
+ RETURN(!sm_cancel);
+ }
+ case WM_ENDSESSION: {
+ sm_smActive = false;
+ sm_blockUserInput = false;
+ bool endsession = (bool) wParam;
+
+ // we receive the message for each toplevel window included internal hidden ones,
+ // but the aboutToQuit signal should be emitted only once.
+ QApplicationPrivate *qAppPriv = QApplicationPrivate::instance();
+ if (endsession && !qAppPriv->aboutToQuitEmitted) {
+ qAppPriv->aboutToQuitEmitted = true;
+ int index = QApplication::staticMetaObject.indexOfSignal("aboutToQuit()");
+ qApp->qt_metacall(QMetaObject::InvokeMetaMethod, index,0);
+ // since the process will be killed immediately quit() has no real effect
+ QApplication::quit();
+ }
+
+ RETURN(0);
+ }
+#endif
+ case WM_DISPLAYCHANGE:
+ if (QApplication::type() == QApplication::Tty)
+ break;
+ if (qt_desktopWidget) {
+ qt_desktopWidget->move(GetSystemMetrics(76), GetSystemMetrics(77));
+ QSize sz(GetSystemMetrics(78), GetSystemMetrics(79));
+ if (sz == qt_desktopWidget->size()) {
+ // a screen resized without changing size of the virtual desktop
+ QResizeEvent rs(sz, qt_desktopWidget->size());
+ QApplication::sendEvent(qt_desktopWidget, &rs);
+ } else {
+ qt_desktopWidget->resize(sz);
+ }
+ }
+ break;
+#endif
+
+ case WM_SETTINGCHANGE:
+#ifdef Q_WS_WINCE
+ // CE SIP hide/show
+ if (qt_desktopWidget && wParam == SPI_SETSIPINFO) {
+ QResizeEvent re(QSize(0, 0), QSize(0, 0)); // Calculated by QDesktopWidget
+ QApplication::sendEvent(qt_desktopWidget, &re);
+ break;
+ }
+#endif
+ // ignore spurious XP message when user logs in again after locking
+ if (QApplication::type() == QApplication::Tty)
+ break;
+ if (QApplication::desktopSettingsAware() && wParam != SPI_SETWORKAREA) {
+ widget = (QETWidget*)QWidget::find(hwnd);
+ if (widget) {
+ if (wParam == SPI_SETNONCLIENTMETRICS)
+ widget->markFrameStrutDirty();
+ }
+ }
+ else if (qt_desktopWidget && wParam == SPI_SETWORKAREA) {
+ qt_desktopWidget->move(GetSystemMetrics(76), GetSystemMetrics(77));
+ QSize sz(GetSystemMetrics(78), GetSystemMetrics(79));
+ if (sz == qt_desktopWidget->size()) {
+ // a screen resized without changing size of the virtual desktop
+ QResizeEvent rs(sz, qt_desktopWidget->size());
+ QApplication::sendEvent(qt_desktopWidget, &rs);
+ } else {
+ qt_desktopWidget->resize(sz);
+ }
+ }
+
+ if (wParam == SPI_SETFONTSMOOTHINGTYPE) {
+ qt_win_read_cleartype_settings();
+ foreach (QWidget *w, QApplication::topLevelWidgets()) {
+ if (!w->isVisible())
+ continue;
+ ((QETWidget *) w)->forceUpdate();
+ }
+ }
+
+ break;
+ case WM_SYSCOLORCHANGE:
+ if (QApplication::type() == QApplication::Tty)
+ break;
+ if (QApplication::desktopSettingsAware()) {
+ widget = (QETWidget*)QWidget::find(hwnd);
+ if (widget && !widget->parentWidget())
+ qt_set_windows_color_resources();
+ }
+ break;
+
+ case WM_LBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ case WM_XBUTTONDOWN:
+ case WM_LBUTTONDBLCLK:
+ case WM_RBUTTONDBLCLK:
+ case WM_MBUTTONDBLCLK:
+ case WM_XBUTTONDBLCLK:
+ if (qt_win_ignoreNextMouseReleaseEvent)
+ qt_win_ignoreNextMouseReleaseEvent = false;
+ break;
+
+ case WM_LBUTTONUP:
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP:
+ case WM_XBUTTONUP:
+ if (qt_win_ignoreNextMouseReleaseEvent) {
+ qt_win_ignoreNextMouseReleaseEvent = false;
+ if (qt_button_down && qt_button_down->internalWinId() == autoCaptureWnd) {
+ releaseAutoCapture();
+ qt_button_down = 0;
+ }
+
+ RETURN(0);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (!widget)
+ widget = (QETWidget*)QWidget::find(hwnd);
+ if (!widget) // don't know this widget
+ goto do_default;
+
+ if (app_do_modal) { // modal event handling
+ int ret = 0;
+ if (!qt_try_modal(widget, &msg, ret))
+ RETURN(ret);
+ }
+
+ res = 0;
+ if (widget->winEvent(&msg, &res)) // send through widget filter
+ RETURN(res);
+
+ if (qt_is_translatable_mouse_event(message)) {
+ if (QApplication::activePopupWidget() != 0) { // in popup mode
+ POINT curPos = msg.pt;
+ QWidget* w = QApplication::widgetAt(curPos.x, curPos.y);
+ if (w)
+ widget = (QETWidget*)w;
+ }
+
+ if (!qt_tabletChokeMouse) {
+ result = widget->translateMouseEvent(msg); // mouse event
+#if defined(Q_WS_WINCE) && !defined(QT_NO_CONTEXTMENU)
+ if (message == WM_LBUTTONDOWN && widget != QApplication::activePopupWidget()) {
+ QWidget* alienWidget = widget;
+ if ((alienWidget != QApplication::activePopupWidget()) && (alienWidget->contextMenuPolicy() != Qt::PreventContextMenu)) {
+ QPoint pos(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
+ QPoint globalPos(msg.pt.x, msg.pt.y);
+ // In case we are using Alien, then the widget to
+ // send the context menu event is a different one
+ if (!alienWidget->testAttribute(Qt::WA_NativeWindow) && !alienWidget->testAttribute(Qt::WA_PaintOnScreen)) {
+ alienWidget = QApplication::widgetAt(globalPos);
+ if (alienWidget)
+ pos = alienWidget->mapFromGlobal(globalPos);
+ }
+ if (alienWidget) {
+ SHRGINFO shrg;
+ shrg.cbSize = sizeof(shrg);
+ shrg.hwndClient = hwnd;
+ shrg.ptDown.x = GET_X_LPARAM(lParam);
+ shrg.ptDown.y = GET_Y_LPARAM(lParam);
+ shrg.dwFlags = SHRG_RETURNCMD | SHRG_NOANIMATION;
+ resolveAygLibs();
+#ifndef QT_NO_GESTURES
+ if (ptrRecognizeGesture && (ptrRecognizeGesture(&shrg) == GN_CONTEXTMENU)) {
+ if (QApplication::activePopupWidget())
+ QApplication::activePopupWidget()->close();
+ QContextMenuEvent e(QContextMenuEvent::Mouse, pos, globalPos);
+ result = qt_sendSpontaneousEvent(alienWidget, &e);
+ }
+#endif // QT_NO_GESTURES
+ }
+ }
+ }
+#endif
+ } else {
+ // Sometimes we only get a WM_MOUSEMOVE message
+ // and sometimes we get both a WM_MOUSEMOVE and
+ // a WM_LBUTTONDOWN/UP, this creates a spurious mouse
+ // press/release event, using the PeekMessage
+ // will help us fix this. This leaves us with a
+ // question:
+ // This effectively kills using the mouse AND the
+ // tablet simultaneously, well creates wacky input.
+ // Is this going to be a problem? (probably not)
+ bool next_is_button = false;
+ bool is_mouse_move = (message == WM_MOUSEMOVE);
+ if (is_mouse_move) {
+ MSG msg1;
+ if (PeekMessage(&msg1, msg.hwnd, WM_MOUSEFIRST,
+ WM_MOUSELAST, PM_NOREMOVE))
+ next_is_button = (msg1.message == WM_LBUTTONUP
+ || msg1.message == WM_LBUTTONDOWN);
+ }
+ if (!is_mouse_move || (is_mouse_move && !next_is_button))
+ qt_tabletChokeMouse = false;
+ }
+ } else {
+ switch (message) {
+ case WM_TOUCH:
+ result = QApplicationPrivate::instance()->translateTouchEvent(msg);
+ break;
+ case WM_KEYDOWN: // keyboard event
+ case WM_SYSKEYDOWN:
+ qt_keymapper_private()->updateKeyMap(msg);
+ // fall-through intended
+ case WM_KEYUP:
+ case WM_SYSKEYUP:
+#if Q_OS_WINCE_WM
+ case WM_HOTKEY:
+ if(HIWORD(msg.lParam) == VK_TBACK) {
+ const bool hotKeyDown = !(LOWORD(msg.lParam) & MOD_KEYUP);
+ msg.lParam = 0x69 << 16;
+ msg.wParam = VK_BACK;
+ if (hotKeyDown) {
+ msg.message = WM_KEYDOWN;
+ qt_keymapper_private()->updateKeyMap(msg);
+ } else {
+ msg.message = WM_KEYUP;
+ }
+ }
+ // fall-through intended
+#endif
+ case WM_IME_CHAR:
+ case WM_IME_KEYDOWN:
+ case WM_CHAR: {
+ MSG msg1;
+ bool anyMsg = PeekMessage(&msg1, msg.hwnd, 0, 0, PM_NOREMOVE);
+ if (anyMsg && msg1.message == WM_DEADCHAR) {
+ result = true; // consume event since there is a dead char next
+ break;
+ }
+ QWidget *g = QWidget::keyboardGrabber();
+ if (g && qt_get_tablet_widget() && hwnd == qt_get_tablet_widget()->winId()) {
+ // if we get an event for the internal tablet widget,
+ // then don't send it to the keyboard grabber, but
+ // send it to the widget itself (we don't use it right
+ // now, just in case).
+ g = 0;
+ }
+ if (g)
+ widget = (QETWidget*)g;
+ else if (QApplication::activePopupWidget())
+ widget = (QETWidget*)QApplication::activePopupWidget()->focusWidget()
+ ? (QETWidget*)QApplication::activePopupWidget()->focusWidget()
+ : (QETWidget*)QApplication::activePopupWidget();
+ else if (QApplication::focusWidget())
+ widget = (QETWidget*)QApplication::focusWidget();
+ else if (!widget || widget->internalWinId() == GetFocus()) // We faked the message to go to exactly that widget.
+ widget = (QETWidget*)widget->window();
+ if (widget->isEnabled())
+ result = sm_blockUserInput
+ ? true
+ : qt_keymapper_private()->translateKeyEvent(widget, msg, g != 0);
+ break;
+ }
+ case WM_SYSCHAR:
+ result = true; // consume event
+ break;
+
+ case WM_MOUSEWHEEL:
+ case WM_MOUSEHWHEEL:
+ result = widget->translateWheelEvent(msg);
+ break;
+
+ case WM_APPCOMMAND:
+ {
+ uint cmd = GET_APPCOMMAND_LPARAM(lParam);
+ uint uDevice = GET_DEVICE_LPARAM(lParam);
+ uint dwKeys = GET_KEYSTATE_LPARAM(lParam);
+
+ int state = translateButtonState(dwKeys, QEvent::KeyPress, 0);
+
+ switch (uDevice) {
+ case FAPPCOMMAND_KEY:
+ {
+ int key = 0;
+
+ switch(cmd) {
+ case APPCOMMAND_BASS_BOOST:
+ key = Qt::Key_BassBoost;
+ break;
+ case APPCOMMAND_BASS_DOWN:
+ key = Qt::Key_BassDown;
+ break;
+ case APPCOMMAND_BASS_UP:
+ key = Qt::Key_BassUp;
+ break;
+ case APPCOMMAND_TREBLE_DOWN:
+ key = Qt::Key_TrebleDown;
+ break;
+ case APPCOMMAND_TREBLE_UP:
+ key = Qt::Key_TrebleUp;
+ break;
+ case APPCOMMAND_HELP:
+ key = Qt::Key_Help;
+ break;
+ case APPCOMMAND_FIND:
+ key = Qt::Key_Search;
+ break;
+ default:
+ break;
+ }
+ if (key) {
+ bool res = false;
+ QWidget *g = QWidget::keyboardGrabber();
+ if (g)
+ widget = (QETWidget*)g;
+ else if (QApplication::focusWidget())
+ widget = (QETWidget*)QApplication::focusWidget();
+ else
+ widget = (QETWidget*)widget->window();
+ if (widget->isEnabled()) {
+ res = QKeyMapper::sendKeyEvent(widget, g != 0, QEvent::KeyPress, key,
+ Qt::KeyboardModifier(state),
+ QString(), false, 0, 0, 0, 0);
+ }
+ if (res)
+ return true;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ result = false;
+ }
+ break;
+
+#ifndef Q_WS_WINCE
+ case WM_NCHITTEST:
+ if (widget->isWindow()) {
+ QPoint pos = widget->mapFromGlobal(QPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
+ // don't show resize-cursors for fixed-size widgets
+ QRect fs = widget->frameStrut();
+ if (!widget->isMinimized()) {
+ if (widget->minimumHeight() == widget->maximumHeight()) {
+ if (pos.y() < -(fs.top() - fs.left()))
+ return HTCAPTION;
+ if (pos.y() >= widget->height())
+ return HTBORDER;
+ }
+ if (widget->minimumWidth() == widget->maximumWidth() && (pos.x() < 0 || pos.x() >= widget->width()))
+ return HTBORDER;
+ }
+ }
+
+ result = false;
+ break;
+#endif
+
+ case WM_SYSCOMMAND: {
+#ifndef Q_WS_WINCE
+ bool window_state_change = false;
+ Qt::WindowStates oldstate = Qt::WindowStates(widget->dataPtr()->window_state);
+ // MSDN:In WM_SYSCOMMAND messages, the four low-order bits of the wParam parameter are
+ // used internally by the system. To obtain the correct result when testing the value of
+ // wParam, an application must combine the value 0xFFF0 with the wParam value by using
+ // the bitwise AND operator.
+ switch(0xfff0 & wParam) {
+ case SC_CONTEXTHELP:
+#ifndef QT_NO_WHATSTHIS
+ QWhatsThis::enterWhatsThisMode();
+#endif
+ DefWindowProc(hwnd, WM_NCPAINT, 1, 0);
+ break;
+#if defined(QT_NON_COMMERCIAL)
+ QT_NC_SYSCOMMAND
+#endif
+ case SC_MINIMIZE:
+ window_state_change = true;
+ widget->dataPtr()->window_state |= Qt::WindowMinimized;
+ if (widget->isVisible()) {
+ QHideEvent e;
+ qt_sendSpontaneousEvent(widget, &e);
+ widget->hideChildren(true);
+ const QString title = widget->windowIconText();
+ if (!title.isEmpty())
+ widget->setWindowTitle_helper(title);
+ }
+ result = false;
+ break;
+ case SC_MAXIMIZE:
+ if(widget->isWindow())
+ widget->topData()->normalGeometry = widget->geometry();
+ case SC_RESTORE:
+ window_state_change = true;
+ if ((0xfff0 & wParam) == SC_MAXIMIZE)
+ widget->dataPtr()->window_state |= Qt::WindowMaximized;
+ else if (!widget->isMinimized())
+ widget->dataPtr()->window_state &= ~Qt::WindowMaximized;
+
+ if (widget->isMinimized()) {
+ widget->dataPtr()->window_state &= ~Qt::WindowMinimized;
+ widget->showChildren(true);
+ QShowEvent e;
+ qt_sendSpontaneousEvent(widget, &e);
+ const QString title = widget->windowTitle();
+ if (!title.isEmpty())
+ widget->setWindowTitle_helper(title);
+ }
+ result = false;
+ break;
+ default:
+ result = false;
+ break;
+ }
+
+ if (window_state_change) {
+ QWindowStateChangeEvent e(oldstate);
+ qt_sendSpontaneousEvent(widget, &e);
+ }
+#endif // #ifndef Q_OS_WINCE
+
+ break;
+ }
+
+ case WM_SETTINGCHANGE:
+ if ( QApplication::type() == QApplication::Tty )
+ break;
+
+ if (!msg.wParam) {
+#ifdef Q_WS_WINCE
+ // On Windows CE, lParam parameter is a constant, not a char pointer.
+ if (msg.lParam == INI_INTL) {
+#else
+ QString area = QString::fromWCharArray((wchar_t*)msg.lParam);
+ if (area == QLatin1String("intl")) {
+#endif
+ QLocalePrivate::updateSystemPrivate();
+ if (!widget->testAttribute(Qt::WA_SetLocale))
+ widget->dptr()->setLocale_helper(QLocale(), true);
+ QEvent e(QEvent::LocaleChange);
+ QApplication::sendEvent(qApp, &e);
+ }
+ }
+ else if (msg.wParam == SPI_SETICONTITLELOGFONT) {
+ if (QApplication::desktopSettingsAware()) {
+ widget = (QETWidget*)QWidget::find(hwnd);
+ if (widget && !widget->parentWidget()) {
+ qt_set_windows_font_resources();
+ }
+ }
+ }
+ else if (msg.wParam == SPI_SETNONCLIENTMETRICS) {
+ widget = (QETWidget*)QWidget::find(hwnd);
+ if (widget && !widget->parentWidget()) {
+ qt_set_windows_updateScrollBar(widget);
+ QEvent e(QEvent::LayoutRequest);
+ QApplication::sendEvent(widget, &e);
+ }
+ }
+
+ break;
+
+ case WM_PAINT: // paint event
+ case WM_ERASEBKGND: // erase window background
+ result = widget->translatePaintEvent(msg);
+ break;
+
+#ifndef Q_WS_WINCE
+ case WM_ENTERSIZEMOVE:
+ autoCaptureWnd = hwnd;
+ break;
+ case WM_EXITSIZEMOVE:
+ autoCaptureWnd = 0;
+ break;
+#endif
+ case WM_MOVE: // move window
+ case WM_SIZE: // resize window
+ result = widget->translateConfigEvent(msg);
+ break;
+
+ case WM_ACTIVATEAPP:
+ if (wParam == FALSE) {
+ QApplication::setActiveWindow(0);
+ // Another application was activated while our popups are open,
+ // then close all popups. In case some popup refuses to close,
+ // we give up after 1024 attempts (to avoid an infinite loop).
+ int maxiter = 1024;
+ QWidget *popup;
+ while ((popup=QApplication::activePopupWidget()) && maxiter--)
+ popup->close();
+ }
+ break;
+
+ case WM_ACTIVATE:
+ if ( QApplication::type() == QApplication::Tty )
+ break;
+
+ if (ptrWTOverlap && ptrWTEnable) {
+ // cooperate with other tablet applications, but when
+ // we get focus, I want to use the tablet...
+ if (qt_tablet_context && GET_WM_ACTIVATE_STATE(wParam, lParam)) {
+ if (ptrWTEnable(qt_tablet_context, true))
+ ptrWTOverlap(qt_tablet_context, true);
+ }
+ }
+ if (QApplication::activePopupWidget() && LOWORD(wParam) == WA_INACTIVE &&
+ QWidget::find((HWND)lParam) == 0) {
+ // Another application was activated while our popups are open,
+ // then close all popups. In case some popup refuses to close,
+ // we give up after 1024 attempts (to avoid an infinite loop).
+ int maxiter = 1024;
+ QWidget *popup;
+ while ((popup=QApplication::activePopupWidget()) && maxiter--)
+ popup->close();
+ }
+
+ if (LOWORD(wParam) != WA_INACTIVE) {
+ // WM_ACTIVATEAPP handles the "true" false case, as this is only when the application
+ // loses focus. Doing it here would result in the widget getting focus to not know
+ // where it got it from; it would simply get a 0 value as the old focus widget.
+#ifdef Q_WS_WINCE
+ {
+#ifdef Q_WS_WINCE_WM
+ // On Windows mobile we do not receive WM_SYSCOMMAND / SC_MINIMIZE messages.
+ // Thus we have to unset the minimized state explicitly. We must do this for all
+ // top-level widgets, because we get the HWND of a random widget here.
+ foreach (QWidget* tlw, QApplication::topLevelWidgets()) {
+ if (tlw->isMinimized())
+ tlw->setWindowState(tlw->windowState() & ~Qt::WindowMinimized);
+ }
+#else
+ // On Windows CE we do not receive WM_SYSCOMMAND / SC_MINIMIZE messages.
+ // Thus we have to unset the minimized state explicitly.
+ if (widget->windowState() & Qt::WindowMinimized)
+ widget->setWindowState(widget->windowState() & ~Qt::WindowMinimized);
+#endif // Q_WS_WINCE_WM
+
+#else
+ if (!(widget->windowState() & Qt::WindowMinimized)) {
+#endif
+ // Ignore the activate message send by WindowsXP to a minimized window
+#ifdef Q_WS_WINCE_WM
+ if (widget->windowState() & Qt::WindowFullScreen)
+ qt_wince_hide_taskbar(widget->winId());
+#endif
+ qApp->winFocus(widget, true);
+ // reset any window alert flashes
+ alert_widget(widget, -1);
+ }
+ }
+
+ // Windows tries to activate a modally blocked window.
+ // This happens when restoring an application after "Show Desktop"
+ if (app_do_modal && LOWORD(wParam) == WA_ACTIVE) {
+ QWidget *top = 0;
+ if (!QApplicationPrivate::tryModalHelper(widget, &top) && top && widget != top) {
+ if (top->isVisible()) {
+ top->activateWindow();
+ } else {
+ // This is the case when native file dialogs are shown
+ QWidget *p = (top->parentWidget() ? top->parentWidget()->window() : 0);
+ if (p && p->isVisible())
+ p->activateWindow();
+ }
+ }
+ }
+ break;
+
+#ifndef Q_WS_WINCE
+ case WM_MOUSEACTIVATE:
+ if (widget->window()->windowType() == Qt::Tool) {
+ QWidget *w = widget;
+ if (!w->window()->focusWidget()) {
+ while (w && (w->focusPolicy() & Qt::ClickFocus) == 0) {
+ if (w->isWindow()) {
+ QWidget *fw = w;
+ while ((fw = fw->nextInFocusChain()) != w && fw->focusPolicy() == Qt::NoFocus)
+ ;
+ if (fw != w)
+ break;
+ QWidget *pw = w->parentWidget();
+ while (pw) {
+ pw = pw->window();
+ if (pw && pw->isVisible() && pw->focusWidget()) {
+ Q_ASSERT(pw->testAttribute(Qt::WA_WState_Created));
+ SetWindowPos(pw->internalWinId(), HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
+ break;
+ }
+ pw = pw->parentWidget();
+ }
+ RETURN(MA_NOACTIVATE);
+ }
+ w = w->parentWidget();
+ }
+ }
+ }
+ RETURN(MA_ACTIVATE);
+ break;
+#endif
+ case WM_SHOWWINDOW:
+ if (lParam == SW_PARENTOPENING) {
+ if (widget->testAttribute(Qt::WA_WState_Hidden))
+ RETURN(0);
+ }
+ if (widget->isWindow() && widget->testAttribute(Qt::WA_WState_Visible)
+ && !widget->testWindowState(Qt::WindowMinimized)) {
+ if (lParam == SW_PARENTOPENING) {
+ QShowEvent e;
+ qt_sendSpontaneousEvent(widget, &e);
+ widget->showChildren(true);
+ } else if (lParam == SW_PARENTCLOSING) {
+ QHideEvent e;
+ qt_sendSpontaneousEvent(widget, &e);
+ widget->hideChildren(true);
+ }
+ }
+ if (!wParam && autoCaptureWnd == widget->internalWinId())
+ releaseAutoCapture();
+ result = false;
+ break;
+
+ case WM_PALETTECHANGED: // our window changed palette
+ if (QColormap::hPal() && (WId)wParam == widget->internalWinId())
+ RETURN(0); // otherwise: FALL THROUGH!
+ // FALL THROUGH
+ case WM_QUERYNEWPALETTE: // realize own palette
+ if (QColormap::hPal()) {
+ Q_ASSERT(widget->testAttribute(Qt::WA_WState_Created));
+ HDC hdc = GetDC(widget->internalWinId());
+ HPALETTE hpalOld = SelectPalette(hdc, QColormap::hPal(), FALSE);
+ uint n = RealizePalette(hdc);
+ if (n)
+ InvalidateRect(widget->internalWinId(), 0, TRUE);
+ SelectPalette(hdc, hpalOld, TRUE);
+ RealizePalette(hdc);
+ ReleaseDC(widget->internalWinId(), hdc);
+ RETURN(n);
+ }
+ break;
+ case WM_CLOSE: // close window
+ widget->translateCloseEvent(msg);
+ RETURN(0); // always handled
+
+ case WM_DESTROY: // destroy window
+ if (hwnd == curWin) {
+ QWidget *enter = QWidget::mouseGrabber();
+ if (enter == widget)
+ enter = 0;
+ QApplicationPrivate::dispatchEnterLeave(enter, widget);
+ curWin = enter ? enter->effectiveWinId() : 0;
+ qt_last_mouse_receiver = enter;
+ }
+ if (widget == popupButtonFocus)
+ popupButtonFocus = 0;
+ result = false;
+ break;
+
+#ifndef Q_WS_WINCE
+ case WM_WINDOWPOSCHANGING:
+ {
+ result = false;
+ if (widget->isWindow()) {
+ WINDOWPOS *winPos = (WINDOWPOS *)lParam;
+ if (widget->layout() && widget->layout()->hasHeightForWidth()
+ && !(winPos->flags & (SWP_NOCOPYBITS | SWP_NOSIZE))) {
+ QRect fs = widget->frameStrut();
+ QRect rect = widget->geometry();
+ QRect newRect = QRect(winPos->x + fs.left(),
+ winPos->y + fs.top(),
+ winPos->cx - fs.left() - fs.right(),
+ winPos->cy - fs.top() - fs.bottom());
+
+ QSize newSize = QLayout::closestAcceptableSize(widget, newRect.size());
+
+ int dh = newSize.height() - newRect.height();
+ int dw = newSize.width() - newRect.width();
+ if (!dw && ! dh)
+ break; // Size OK
+
+ if (rect.y() != newRect.y()) {
+ newRect.setTop(newRect.top() - dh);
+ } else {
+ newRect.setBottom(newRect.bottom() + dh);
+ }
+
+ if (rect.x() != newRect.x()) {
+ newRect.setLeft(newRect.left() - dw);
+ } else {
+ newRect.setRight(newRect.right() + dw);
+ }
+
+ winPos->x = newRect.x() - fs.left();
+ winPos->y = newRect.y() - fs.top();
+ winPos->cx = newRect.width() + fs.left() + fs.right();
+ winPos->cy = newRect.height() + fs.top() + fs.bottom();
+
+ RETURN(0);
+ }
+ if (widget->windowFlags() & Qt::WindowStaysOnBottomHint) {
+ winPos->hwndInsertAfter = HWND_BOTTOM;
+ }
+ }
+ }
+ break;
+
+ case WM_GETMINMAXINFO:
+ if (widget->xtra()) {
+ MINMAXINFO *mmi = (MINMAXINFO *)lParam;
+ QWExtra *x = widget->xtra();
+ QRect fs = widget->frameStrut();
+ if ( x->minw > 0 )
+ mmi->ptMinTrackSize.x = x->minw + fs.right() + fs.left();
+ if ( x->minh > 0 )
+ mmi->ptMinTrackSize.y = x->minh + fs.top() + fs.bottom();
+ qint32 maxw = (x->maxw >= x->minw) ? x->maxw : x->minw;
+ qint32 maxh = (x->maxh >= x->minh) ? x->maxh : x->minh;
+ if ( maxw < QWIDGETSIZE_MAX ) {
+ mmi->ptMaxTrackSize.x = maxw + fs.right() + fs.left();
+ // windows with title bar have an implicit size limit of 112 pixels
+ if (widget->windowFlags() & Qt::WindowTitleHint)
+ mmi->ptMaxTrackSize.x = qMax<long>(mmi->ptMaxTrackSize.x, 112);
+ }
+ if ( maxh < QWIDGETSIZE_MAX )
+ mmi->ptMaxTrackSize.y = maxh + fs.top() + fs.bottom();
+ RETURN(0);
+ }
+ break;
+
+#ifndef QT_NO_CONTEXTMENU
+ case WM_CONTEXTMENU:
+ {
+ // it's not VK_APPS or Shift+F10, but a click in the NC area
+ if (lParam != (int)0xffffffff) {
+ result = false;
+ break;
+ }
+
+ QWidget *fw = QWidget::keyboardGrabber();
+ if (!fw) {
+ if (QApplication::activePopupWidget())
+ fw = (QApplication::activePopupWidget()->focusWidget()
+ ? QApplication::activePopupWidget()->focusWidget()
+ : QApplication::activePopupWidget());
+ else if (QApplication::focusWidget())
+ fw = QApplication::focusWidget();
+ else if (widget)
+ fw = widget->window();
+ }
+ if (fw && fw->isEnabled()) {
+ QPoint pos = fw->inputMethodQuery(Qt::ImMicroFocus).toRect().center();
+ QContextMenuEvent e(QContextMenuEvent::Keyboard, pos, fw->mapToGlobal(pos),
+ qt_win_getKeyboardModifiers());
+ result = qt_sendSpontaneousEvent(fw, &e);
+ }
+ }
+ break;
+#endif
+#endif
+
+ case WM_IME_STARTCOMPOSITION:
+ case WM_IME_ENDCOMPOSITION:
+ case WM_IME_COMPOSITION: {
+ QWidget *fw = QApplication::focusWidget();
+ QWinInputContext *im = fw ? qobject_cast<QWinInputContext *>(fw->inputContext()) : 0;
+ if (fw && im) {
+ if(message == WM_IME_STARTCOMPOSITION)
+ result = im->startComposition();
+ else if (message == WM_IME_ENDCOMPOSITION)
+ result = im->endComposition();
+ else if (message == WM_IME_COMPOSITION)
+ result = im->composition(lParam);
+ }
+ break;
+ }
+ case WM_IME_REQUEST: {
+ QWidget *fw = QApplication::focusWidget();
+ QWinInputContext *im = fw ? qobject_cast<QWinInputContext *>(fw->inputContext()) : 0;
+ if (fw && im) {
+ if(wParam == IMR_RECONVERTSTRING) {
+ int ret = im->reconvertString((RECONVERTSTRING *)lParam);
+ if (ret == -1) {
+ result = false;
+ } else {
+ return ret;
+ }
+ } else if (wParam == IMR_CONFIRMRECONVERTSTRING) {
+ RETURN(TRUE);
+ } else {
+ // in all other cases, call DefWindowProc()
+ result = false;
+ }
+ }
+ break;
+ }
+#ifndef Q_WS_WINCE
+ case WM_CHANGECBCHAIN:
+ case WM_DRAWCLIPBOARD:
+#endif
+ case WM_RENDERFORMAT:
+ case WM_RENDERALLFORMATS:
+#ifndef QT_NO_CLIPBOARD
+ case WM_DESTROYCLIPBOARD:
+ if (qt_clipboard) {
+ QClipboardEvent e(reinterpret_cast<QEventPrivate *>(&msg));
+ qt_sendSpontaneousEvent(qt_clipboard, &e);
+ RETURN(0);
+ }
+ result = false;
+ break;
+#endif //QT_NO_CLIPBOARD
+#ifndef QT_NO_ACCESSIBILITY
+ case WM_GETOBJECT:
+ {
+ /* On Win64, lParam can be 0x00000000fffffffc or 0xfffffffffffffffc (!),
+ but MSDN says that lParam should be converted to a DWORD
+ before its compared against OBJID_CLIENT
+ */
+ const DWORD dwObjId = (DWORD)lParam;
+ // Ignoring all requests while starting up
+ if (QApplication::startingUp() || QApplication::closingDown() || dwObjId != OBJID_CLIENT) {
+ result = false;
+ break;
+ }
+
+ typedef LRESULT (WINAPI *PtrLresultFromObject)(REFIID, WPARAM, LPUNKNOWN);
+ static PtrLresultFromObject ptrLresultFromObject = 0;
+ static bool oleaccChecked = false;
+
+ if (!oleaccChecked) {
+ oleaccChecked = true;
+#if !defined(Q_OS_WINCE)
+ ptrLresultFromObject = (PtrLresultFromObject)QSystemLibrary::resolve(QLatin1String("oleacc"), "LresultFromObject");
+#endif
+ }
+ if (ptrLresultFromObject) {
+ QAccessibleInterface *acc = QAccessible::queryAccessibleInterface(widget);
+ if (!acc) {
+ result = false;
+ break;
+ }
+
+ // and get an instance of the IAccessibile implementation
+ IAccessible *iface = qt_createWindowsAccessible(acc);
+ res = ptrLresultFromObject(IID_IAccessible, wParam, iface); // ref == 2
+ iface->Release(); // the client will release the object again, and then it will destroy itself
+
+ if (res > 0)
+ RETURN(res);
+ }
+ }
+ result = false;
+ break;
+ case WM_GETTEXT:
+ if (!widget->isWindow()) {
+ int ret = 0;
+ QAccessibleInterface *acc = QAccessible::queryAccessibleInterface(widget);
+ if (acc) {
+ QString text = acc->text(QAccessible::Name, 0);
+ if (text.isEmpty())
+ text = widget->objectName();
+ ret = qMin<int>(wParam - 1, text.size());
+ text.resize(ret);
+ memcpy((void *)lParam, text.utf16(), (text.size() + 1) * sizeof(ushort));
+ delete acc;
+ }
+ if (!ret) {
+ result = false;
+ break;
+ }
+ RETURN(ret);
+ }
+ result = false;
+ break;
+#endif
+ case WT_PACKET:
+ if (ptrWTPacketsGet) {
+ if ((nPackets = ptrWTPacketsGet(qt_tablet_context, QT_TABLET_NPACKETQSIZE, &localPacketBuf))) {
+ result = widget->translateTabletEvent(msg, localPacketBuf, nPackets);
+ }
+ }
+ break;
+ case WT_PROXIMITY:
+
+ #ifndef QT_NO_TABLETEVENT
+ if (ptrWTPacketsGet && ptrWTInfo) {
+ const bool enteredProximity = LOWORD(lParam) != 0;
+ PACKET proximityBuffer[1]; // we are only interested in the first packet in this case
+ const int totalPacks = ptrWTPacketsGet(qt_tablet_context, 1, proximityBuffer);
+ if (totalPacks > 0) {
+ const UINT currentCursor = proximityBuffer[0].pkCursor;
+
+ UINT csr_physid;
+ ptrWTInfo(WTI_CURSORS + currentCursor, CSR_PHYSID, &csr_physid);
+ UINT csr_type;
+ ptrWTInfo(WTI_CURSORS + currentCursor, CSR_TYPE, &csr_type);
+ const UINT deviceIdMask = 0xFF6; // device type mask && device color mask
+ quint64 uniqueId = (csr_type & deviceIdMask);
+ uniqueId = (uniqueId << 32) | csr_physid;
+
+ // initialising and updating the cursor should be done in response to
+ // WT_CSRCHANGE. We do it in WT_PROXIMITY because some wintab never send
+ // the event WT_CSRCHANGE even if asked with CXO_CSRMESSAGES
+ const QTabletCursorInfo *const globalCursorInfo = tCursorInfo();
+ if (!globalCursorInfo->contains(uniqueId))
+ tabletInit(uniqueId, csr_type, qt_tablet_context);
+
+ currentTabletPointer = globalCursorInfo->value(uniqueId);
+ tabletUpdateCursor(currentTabletPointer, currentCursor);
+ }
+ qt_tabletChokeMouse = false;
+
+ QTabletEvent tabletProximity(enteredProximity ? QEvent::TabletEnterProximity
+ : QEvent::TabletLeaveProximity,
+ QPoint(), QPoint(), QPointF(), currentTabletPointer.currentDevice, currentTabletPointer.currentPointerType, 0, 0,
+ 0, 0, 0, 0, 0, currentTabletPointer.llId);
+ QApplication::sendEvent(qApp, &tabletProximity);
+ }
+ #endif // QT_NO_TABLETEVENT
+
+ break;
+#ifdef Q_WS_WINCE_WM
+ case WM_SETFOCUS: {
+ HIMC hC;
+ hC = ImmGetContext(hwnd);
+ ImmSetOpenStatus(hC, TRUE);
+ ImmEscape(NULL, hC, IME_ESC_SET_MODE, (LPVOID)IM_SPELL);
+ result = false;
+ }
+ break;
+#endif
+ case WM_KILLFOCUS:
+ if (!QWidget::find((HWND)wParam)) { // we don't get focus, so unset it now
+ if (!widget->hasFocus()) // work around Windows bug after minimizing/restoring
+ widget = (QETWidget*)QApplication::focusWidget();
+ HWND focus = ::GetFocus();
+ //if there is a current widget and the new widget belongs to the same toplevel window
+ //or if the current widget was embedded into non-qt window (i.e. we won't get WM_ACTIVATEAPP)
+ //then we clear the focus on the widget
+ //in case the new widget belongs to a different widget hierarchy, clearing the focus
+ //will be handled because the active window will change
+ const bool embedded = widget && ((QETWidget*)widget->window())->topData()->embedded;
+ if (widget && (embedded || ::IsChild(widget->window()->internalWinId(), focus))) {
+ widget->clearFocus();
+ result = true;
+ } else {
+ result = false;
+ }
+ } else {
+ result = false;
+ }
+ break;
+ case WM_THEMECHANGED:
+ if ((widget->windowType() == Qt::Desktop) || !qApp || QApplication::closingDown()
+ || QApplication::type() == QApplication::Tty)
+ break;
+
+ if (widget->testAttribute(Qt::WA_WState_Polished))
+ QApplication::style()->unpolish(widget);
+
+ if (widget->testAttribute(Qt::WA_WState_Polished))
+ QApplication::style()->polish(widget);
+ widget->repolishStyle(*QApplication::style());
+ if (widget->isVisible())
+ widget->update();
+ break;
+
+#ifndef Q_WS_WINCE
+ case WM_INPUTLANGCHANGE: {
+ wchar_t info[7];
+ if (!GetLocaleInfo(MAKELCID(lParam, SORT_DEFAULT), LOCALE_IDEFAULTANSICODEPAGE, info, 6)) {
+ inputcharset = CP_ACP;
+ } else {
+ inputcharset = QString::fromWCharArray(info).toInt();
+ }
+ QKeyMapper::changeKeyboard();
+ break;
+ }
+#else
+ case WM_COMMAND: {
+ bool OkCommand = (LOWORD(wParam) == 0x1);
+ bool CancelCommand = (LOWORD(wParam) == 0x2);
+ if (OkCommand)
+ QApplication::postEvent(widget, new QEvent(QEvent::OkRequest));
+ if (CancelCommand)
+ widget->showMinimized();
+ else
+#ifndef QT_NO_MENUBAR
+ QMenuBar::wceCommands(LOWORD(wParam));
+#endif
+ result = true;
+ }
+ break;
+ case WM_HELP:
+ QApplication::postEvent(widget, new QEvent(QEvent::HelpRequest));
+ result = true;
+ break;
+#endif
+
+ case WM_MOUSELEAVE:
+ // We receive a mouse leave for curWin, meaning
+ // the mouse was moved outside our widgets
+ if (widget->internalWinId() == curWin) {
+ bool dispatch = !widget->underMouse();
+ // hasMouse is updated when dispatching enter/leave,
+ // so test if it is actually up-to-date
+ if (!dispatch) {
+ QRect geom = widget->geometry();
+ if (widget->parentWidget() && !widget->isWindow()) {
+ QPoint gp = widget->parentWidget()->mapToGlobal(widget->pos());
+ geom.setX(gp.x());
+ geom.setY(gp.y());
+ }
+ QPoint cpos = QCursor::pos();
+ dispatch = !geom.contains(cpos);
+ if ( !dispatch && !QWidget::mouseGrabber()) {
+ QWidget *hittest = QApplication::widgetAt(cpos);
+ dispatch = !hittest || hittest->internalWinId() != curWin;
+ }
+ if (!dispatch) {
+ HRGN hrgn = qt_tryCreateRegion(QRegion::Rectangle, 0,0,0,0);
+ if (GetWindowRgn(curWin, hrgn) != ERROR) {
+ QPoint lcpos = widget->mapFromGlobal(cpos);
+ dispatch = !PtInRegion(hrgn, lcpos.x(), lcpos.y());
+ }
+ DeleteObject(hrgn);
+ }
+ }
+ if (dispatch) {
+ if (qt_last_mouse_receiver && !qt_last_mouse_receiver->internalWinId())
+ QApplicationPrivate::dispatchEnterLeave(0, qt_last_mouse_receiver);
+ else
+ QApplicationPrivate::dispatchEnterLeave(0, QWidget::find((WId)curWin));
+ curWin = 0;
+ qt_last_mouse_receiver = 0;
+ }
+ }
+ break;
+
+ case WM_CANCELMODE:
+ {
+ // this goes through QMenuBar's event filter
+ QEvent e(QEvent::ActivationChange);
+ QApplication::sendEvent(qApp, &e);
+ }
+ break;
+
+ case WM_IME_NOTIFY:
+ // special handling for ime, only for widgets in a popup
+ if (wParam == IMN_OPENCANDIDATE) {
+ imeParentWnd = hwnd;
+ if (QApplication::activePopupWidget()) {
+ // temporarily disable the mouse grab to allow mouse input in
+ // the ime candidate window. The actual handle is untouched
+ if (autoCaptureWnd)
+ ReleaseCapture();
+ }
+ } else if (wParam == IMN_CLOSECANDIDATE) {
+ imeParentWnd = 0;
+ if (QApplication::activePopupWidget()) {
+ // undo the action above, when candidate window is closed
+ if (autoCaptureWnd)
+ SetCapture(autoCaptureWnd);
+ }
+ }
+ result = false;
+ break;
+#ifndef QT_NO_GESTURES
+#if !defined(Q_WS_WINCE) || defined(QT_WINCE_GESTURES)
+ case WM_GESTURE: {
+ GESTUREINFO gi;
+ memset(&gi, 0, sizeof(GESTUREINFO));
+ gi.cbSize = sizeof(GESTUREINFO);
+
+ QApplicationPrivate *qAppPriv = QApplicationPrivate::instance();
+ BOOL bResult = false;
+ if (qAppPriv->GetGestureInfo)
+ bResult = qAppPriv->GetGestureInfo((HANDLE)msg.lParam, &gi);
+ if (bResult) {
+ if (gi.dwID == GID_BEGIN) {
+ // find the alien widget for the gesture position.
+ // This might not be accurate as the position is the center
+ // point of two fingers for multi-finger gestures.
+ QPoint pt(gi.ptsLocation.x, gi.ptsLocation.y);
+ QWidget *w = widget->childAt(widget->mapFromGlobal(pt));
+ qAppPriv->gestureWidget = w ? w : widget;
+ }
+ if (qAppPriv->gestureWidget)
+ static_cast<QETWidget*>(qAppPriv->gestureWidget)->translateGestureEvent(msg, gi);
+ if (qAppPriv->CloseGestureInfoHandle)
+ qAppPriv->CloseGestureInfoHandle((HANDLE)msg.lParam);
+ if (gi.dwID == GID_END)
+ qAppPriv->gestureWidget = 0;
+ } else {
+ DWORD dwErr = GetLastError();
+ if (dwErr > 0)
+ qWarning() << "translateGestureEvent: error = " << dwErr;
+ }
+ result = true;
+ break;
+ }
+#endif // !defined(Q_WS_WINCE) || defined(QT_WINCE_GESTURES)
+#endif // QT_NO_GESTURES
+#ifndef QT_NO_CURSOR
+ case WM_SETCURSOR: {
+ QCursor *ovr = QApplication::overrideCursor();
+ if (ovr) {
+ SetCursor(ovr->handle());
+ RETURN(TRUE);
+ }
+ result = false;
+ break;
+ }
+#endif
+ default:
+ result = false; // event was not processed
+ break;
+ }
+ }
+
+ if (evt_type != QEvent::None) { // simple event
+ QEvent e(evt_type);
+ result = qt_sendSpontaneousEvent(widget, &e);
+ }
+
+ if (result)
+ RETURN(false);
+
+do_default:
+ RETURN(QWinInputContext::DefWindowProc(hwnd,message,wParam,lParam))
+}
+
+
+/*****************************************************************************
+ Modal widgets; We have implemented our own modal widget mechanism
+ to get total control.
+ A modal widget without a parent becomes application-modal.
+ A modal widget with a parent becomes modal to its parent and grandparents..
+
+ QApplicationPrivate::enterModal()
+ Enters modal state
+ Arguments:
+ QWidget *widget A modal widget
+
+ QApplicationPrivate::leaveModal()
+ Leaves modal state for a widget
+ Arguments:
+ QWidget *widget A modal widget
+ *****************************************************************************/
+
+bool QApplicationPrivate::modalState()
+{
+ return app_do_modal;
+}
+
+void QApplicationPrivate::enterModal_sys(QWidget *widget)
+{
+ if (!qt_modal_stack)
+ qt_modal_stack = new QWidgetList;
+
+ releaseAutoCapture();
+ ClipCursor(0);
+ QWidget *leave = qt_last_mouse_receiver;
+ if (!leave)
+ leave = QWidget::find((WId)curWin);
+ QApplicationPrivate::dispatchEnterLeave(0, leave);
+ qt_modal_stack->insert(0, widget);
+ app_do_modal = true;
+ curWin = 0;
+ qt_last_mouse_receiver = 0;
+ qt_win_ignoreNextMouseReleaseEvent = false;
+}
+
+void QApplicationPrivate::leaveModal_sys(QWidget *widget)
+{
+ if (qt_modal_stack && qt_modal_stack->removeAll(widget)) {
+ if (qt_modal_stack->isEmpty()) {
+ delete qt_modal_stack;
+ qt_modal_stack = 0;
+ QPoint p(QCursor::pos());
+ app_do_modal = false; // necessary, we may get recursively into qt_try_modal below
+ QWidget* w = QApplication::widgetAt(p.x(), p.y());
+ QWidget *leave = qt_last_mouse_receiver;
+ if (!leave)
+ leave = QWidget::find((WId)curWin);
+ if (QWidget *grabber = QWidget::mouseGrabber()) {
+ w = grabber;
+ if (leave == w)
+ leave = 0;
+ }
+ QApplicationPrivate::dispatchEnterLeave(w, leave); // send synthetic enter event
+ curWin = w ? w->effectiveWinId() : 0;
+ qt_last_mouse_receiver = w;
+ }
+ qt_win_ignoreNextMouseReleaseEvent = true;
+ }
+ app_do_modal = qt_modal_stack != 0;
+}
+
+bool qt_try_modal(QWidget *widget, MSG *msg, int& ret)
+{
+#if defined(Q_OS_WINCE)
+ Q_UNUSED(ret);
+#endif
+ QWidget * top = 0;
+
+ if (QApplicationPrivate::tryModalHelper(widget, &top))
+ return true;
+
+ int type = msg->message;
+
+ bool block_event = false;
+#ifndef Q_WS_WINCE
+ if (type != WM_NCHITTEST) {
+#endif
+ if ((type >= WM_MOUSEFIRST && type <= WM_MOUSELAST) ||
+ type == WM_MOUSEWHEEL || type == WM_MOUSEHWHEEL ||
+ type == WM_MOUSELEAVE ||
+ (type >= WM_KEYFIRST && type <= WM_KEYLAST)
+#ifndef Q_WS_WINCE
+ || type == WM_NCMOUSEMOVE
+#endif
+ ) {
+ if (type == WM_MOUSEMOVE
+#ifndef Q_WS_WINCE
+ || type == WM_NCMOUSEMOVE
+#endif
+ ) {
+#ifndef QT_NO_CURSOR
+ QCursor *c = qt_grab_cursor();
+ if (!c)
+ c = QApplication::overrideCursor();
+ if (c) // application cursor defined
+ SetCursor(c->handle());
+ else
+ SetCursor(QCursor(Qt::ArrowCursor).handle());
+#endif // QT_NO_CURSOR
+ }
+ block_event = true;
+ } else if (type == WM_CLOSE) {
+ block_event = true;
+ }
+#ifndef Q_WS_WINCE
+ else if (type == WM_MOUSEACTIVATE || type == WM_NCLBUTTONDOWN){
+ if (!top->isActiveWindow()) {
+ top->activateWindow();
+ } else {
+ QApplication::beep();
+ }
+ block_event = true;
+ ret = MA_NOACTIVATEANDEAT;
+ } else if (type == WM_SYSCOMMAND) {
+ if (!(msg->wParam == SC_RESTORE && widget->isMinimized()))
+ block_event = true;
+ }
+ }
+#endif
+
+ return !block_event;
+}
+
+
+/*****************************************************************************
+ Popup widget mechanism
+
+ openPopup()
+ Adds a widget to the list of popup widgets
+ Arguments:
+ QWidget *widget The popup widget to be added
+
+ closePopup()
+ Removes a widget from the list of popup widgets
+ Arguments:
+ QWidget *widget The popup widget to be removed
+ *****************************************************************************/
+
+void QApplicationPrivate::openPopup(QWidget *popup)
+{
+ if (!QApplicationPrivate::popupWidgets)
+ QApplicationPrivate::popupWidgets = new QWidgetList;
+ QApplicationPrivate::popupWidgets->append(popup);
+ if (!popup->isEnabled())
+ return;
+
+ // close any opened 'ime candidate window'
+ if (imeParentWnd)
+ ::SendMessage(imeParentWnd, WM_IME_ENDCOMPOSITION, 0, 0);
+
+ if (QApplicationPrivate::popupWidgets->count() == 1 && !qt_nograb()) {
+ Q_ASSERT(popup->testAttribute(Qt::WA_WState_Created));
+ setAutoCapture(popup->internalWinId()); // grab mouse/keyboard
+ }
+ // Popups are not focus-handled by the window system (the first
+ // popup grabbed the keyboard), so we have to do that manually: A
+ // new popup gets the focus
+ if (popup->focusWidget()) {
+ popup->focusWidget()->setFocus(Qt::PopupFocusReason);
+ } else if (QApplicationPrivate::popupWidgets->count() == 1) { // this was the first popup
+ if (QWidget *fw = QApplication::focusWidget()) {
+ QFocusEvent e(QEvent::FocusOut, Qt::PopupFocusReason);
+ QApplication::sendEvent(fw, &e);
+ }
+ }
+}
+
+void QApplicationPrivate::closePopup(QWidget *popup)
+{
+ if (!QApplicationPrivate::popupWidgets)
+ return;
+ QApplicationPrivate::popupWidgets->removeAll(popup);
+ POINT curPos;
+ GetCursorPos(&curPos);
+
+ // close any opened 'ime candidate window'
+ if (imeParentWnd)
+ ::SendMessage(imeParentWnd, WM_IME_ENDCOMPOSITION, 0, 0);
+
+ if (QApplicationPrivate::popupWidgets->isEmpty()) { // this was the last popup
+ delete QApplicationPrivate::popupWidgets;
+ QApplicationPrivate::popupWidgets = 0;
+ replayPopupMouseEvent = (!popup->geometry().contains(QPoint(curPos.x, curPos.y))
+ && !popup->testAttribute(Qt::WA_NoMouseReplay));
+ if (!popup->isEnabled())
+ return;
+ if (!qt_nograb()) // grabbing not disabled
+ releaseAutoCapture();
+ QWidget *fw = QApplicationPrivate::active_window ? QApplicationPrivate::active_window->focusWidget()
+ : QApplication::focusWidget();
+ if (fw) {
+ if (fw != QApplication::focusWidget()) {
+ fw->setFocus(Qt::PopupFocusReason);
+ } else {
+ QFocusEvent e(QEvent::FocusIn, Qt::PopupFocusReason);
+ QApplication::sendEvent(fw, &e);
+ }
+ }
+ } else {
+ // Popups are not focus-handled by the window system (the
+ // first popup grabbed the keyboard), so we have to do that
+ // manually: A popup was closed, so the previous popup gets
+ // the focus.
+ QWidget* aw = QApplicationPrivate::popupWidgets->last();
+ if (QApplicationPrivate::popupWidgets->count() == 1) {
+ Q_ASSERT(aw->testAttribute(Qt::WA_WState_Created));
+ setAutoCapture(aw->internalWinId());
+ }
+ if (QWidget *fw = aw->focusWidget())
+ fw->setFocus(Qt::PopupFocusReason);
+ }
+}
+
+
+
+
+/*****************************************************************************
+ Event translation; translates Windows events to Qt events
+ *****************************************************************************/
+
+//
+// Auto-capturing for mouse press and mouse release
+//
+
+static void setAutoCapture(HWND h)
+{
+ if (autoCaptureWnd)
+ releaseAutoCapture();
+ autoCaptureWnd = h;
+ SetCapture(h);
+}
+
+static void releaseAutoCapture()
+{
+ if (autoCaptureWnd) {
+ ReleaseCapture();
+ autoCaptureWnd = 0;
+ }
+}
+
+
+//
+// Mouse event translation
+//
+// Non-client mouse messages are not translated
+//
+
+static const ushort mouseTbl[] = {
+ WM_MOUSEMOVE, QEvent::MouseMove, 0,
+ WM_LBUTTONDOWN, QEvent::MouseButtonPress, Qt::LeftButton,
+ WM_LBUTTONUP, QEvent::MouseButtonRelease, Qt::LeftButton,
+ WM_LBUTTONDBLCLK, QEvent::MouseButtonDblClick, Qt::LeftButton,
+ WM_RBUTTONDOWN, QEvent::MouseButtonPress, Qt::RightButton,
+ WM_RBUTTONUP, QEvent::MouseButtonRelease, Qt::RightButton,
+ WM_RBUTTONDBLCLK, QEvent::MouseButtonDblClick, Qt::RightButton,
+ WM_MBUTTONDOWN, QEvent::MouseButtonPress, Qt::MidButton,
+ WM_MBUTTONUP, QEvent::MouseButtonRelease, Qt::MidButton,
+ WM_MBUTTONDBLCLK, QEvent::MouseButtonDblClick, Qt::MidButton,
+ // use XButton1 for now, the real X button is decided later
+ WM_XBUTTONDOWN, QEvent::MouseButtonPress, Qt::XButton1,
+ WM_XBUTTONUP, QEvent::MouseButtonRelease, Qt::XButton1,
+ WM_XBUTTONDBLCLK, QEvent::MouseButtonDblClick, Qt::XButton1,
+
+#ifndef Q_WS_WINCE
+ WM_NCMOUSEMOVE, QEvent::NonClientAreaMouseMove, 0,
+ WM_NCLBUTTONDOWN, QEvent::NonClientAreaMouseButtonPress, Qt::LeftButton,
+ WM_NCLBUTTONUP, QEvent::NonClientAreaMouseButtonRelease, Qt::LeftButton,
+ WM_NCLBUTTONDBLCLK, QEvent::NonClientAreaMouseButtonDblClick, Qt::LeftButton,
+ WM_NCRBUTTONDOWN, QEvent::NonClientAreaMouseButtonPress, Qt::RightButton,
+ WM_NCRBUTTONUP, QEvent::NonClientAreaMouseButtonRelease, Qt::RightButton,
+ WM_NCRBUTTONDBLCLK, QEvent::NonClientAreaMouseButtonDblClick, Qt::RightButton,
+ WM_NCMBUTTONDOWN, QEvent::NonClientAreaMouseButtonPress, Qt::MidButton,
+ WM_NCMBUTTONUP, QEvent::NonClientAreaMouseButtonRelease, Qt::MidButton,
+ WM_NCMBUTTONDBLCLK, QEvent::NonClientAreaMouseButtonDblClick, Qt::MidButton,
+#endif
+
+ 0, 0, 0
+};
+
+static int translateButtonState(int s, int type, int button)
+{
+ Q_UNUSED(type);
+ Q_UNUSED(button);
+ int bst = 0;
+ if (s & MK_LBUTTON)
+ bst |= Qt::LeftButton;
+ if (s & MK_MBUTTON)
+ bst |= Qt::MidButton;
+ if (s & MK_RBUTTON)
+ bst |= Qt::RightButton;
+ if (s & MK_SHIFT)
+ bst |= Qt::ShiftModifier;
+ if (s & MK_CONTROL)
+ bst |= Qt::ControlModifier;
+
+ if (s & MK_XBUTTON1)
+ bst |= Qt::XButton1;
+ if (s & MK_XBUTTON2)
+ bst |= Qt::XButton2;
+
+ if (GetKeyState(VK_MENU) < 0)
+ bst |= Qt::AltModifier;
+
+ if ((GetKeyState(VK_LWIN) < 0) ||
+ (GetKeyState(VK_RWIN) < 0))
+ bst |= Qt::MetaModifier;
+
+ return bst;
+}
+
+void qt_win_eatMouseMove()
+{
+ // after closing a windows dialog with a double click (i.e. open a file)
+ // the message queue still contains a dubious WM_MOUSEMOVE message where
+ // the left button is reported to be down (wParam != 0).
+ // remove all those messages (usually 1) and post the last one with a
+ // reset button state
+
+ MSG msg = {0, 0, 0, 0, 0, {0, 0} };
+ while (PeekMessage(&msg, 0, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE))
+ ;
+ if (msg.message == WM_MOUSEMOVE)
+ PostMessage(msg.hwnd, msg.message, 0, msg.lParam);
+}
+
+// In DnD, the mouse release event never appears, so the
+// mouse button state machine must be manually reset
+void QApplication::winMouseButtonUp()
+{
+ qt_button_down = 0;
+ releaseAutoCapture();
+}
+
+void QETWidget::repolishStyle(QStyle &)
+{
+ QEvent e(QEvent::StyleChange);
+ QApplication::sendEvent(this, &e);
+}
+
+bool QETWidget::translateMouseEvent(const MSG &msg)
+{
+ if (!isWindow() && testAttribute(Qt::WA_NativeWindow))
+ Q_ASSERT(internalWinId());
+
+ static QPoint pos;
+ static POINT gpos={-1,-1};
+ QEvent::Type type; // event parameters
+ int button;
+ int state;
+ int i;
+
+ if (sm_blockUserInput) //block user interaction during session management
+ return true;
+
+ // Compress mouse move events
+ if (msg.message == WM_MOUSEMOVE) {
+ MSG mouseMsg;
+ while (PeekMessage(&mouseMsg, msg.hwnd, WM_MOUSEFIRST,
+ WM_MOUSELAST, PM_NOREMOVE)) {
+ if (mouseMsg.message == WM_MOUSEMOVE) {
+#define PEEKMESSAGE_IS_BROKEN 1
+#ifdef PEEKMESSAGE_IS_BROKEN
+ // Since the Windows PeekMessage() function doesn't
+ // correctly return the wParam for WM_MOUSEMOVE events
+ // if there is a key release event in the queue
+ // _before_ the mouse event, we have to also consider
+ // key release events (kls 2003-05-13):
+ MSG keyMsg;
+ bool done = false;
+ while (PeekMessage(&keyMsg, 0, WM_KEYFIRST, WM_KEYLAST,
+ PM_NOREMOVE)) {
+ if (keyMsg.time < mouseMsg.time) {
+ if ((keyMsg.lParam & 0xC0000000) == 0x40000000) {
+ PeekMessage(&keyMsg, 0, keyMsg.message,
+ keyMsg.message, PM_REMOVE);
+ } else {
+ done = true;
+ break;
+ }
+ } else {
+ break; // no key event before the WM_MOUSEMOVE event
+ }
+ }
+ if (done)
+ break;
+#else
+ // Actually the following 'if' should work instead of
+ // the above key event checking, but apparently
+ // PeekMessage() is broken :-(
+ if (mouseMsg.wParam != msg.wParam)
+ break; // leave the message in the queue because
+ // the key state has changed
+#endif
+ MSG *msgPtr = (MSG *)(&msg);
+ // Update the passed in MSG structure with the
+ // most recent one.
+ msgPtr->lParam = mouseMsg.lParam;
+ msgPtr->wParam = mouseMsg.wParam;
+ // Extract the x,y coordinates from the lParam as we do in the WndProc
+ msgPtr->pt.x = GET_X_LPARAM(mouseMsg.lParam);
+ msgPtr->pt.y = GET_Y_LPARAM(mouseMsg.lParam);
+ ClientToScreen(msg.hwnd, &(msgPtr->pt));
+ // Remove the mouse move message
+ PeekMessage(&mouseMsg, msg.hwnd, WM_MOUSEMOVE,
+ WM_MOUSEMOVE, PM_REMOVE);
+ } else {
+ break; // there was no more WM_MOUSEMOVE event
+ }
+ }
+ }
+
+ for (i=0; (UINT)mouseTbl[i] != msg.message && mouseTbl[i]; i += 3)
+ ;
+ if (!mouseTbl[i])
+ return false;
+ type = (QEvent::Type)mouseTbl[++i]; // event type
+ button = mouseTbl[++i]; // which button
+ if (button == Qt::XButton1) {
+ switch(GET_XBUTTON_WPARAM(msg.wParam)) {
+ case XBUTTON1:
+ button = Qt::XButton1;
+ break;
+ case XBUTTON2:
+ button = Qt::XButton2;
+ break;
+ }
+ }
+#ifndef Q_OS_WINCE
+ static bool trackMouseEventLookup = false;
+ typedef BOOL (WINAPI *PtrTrackMouseEvent)(LPTRACKMOUSEEVENT);
+ static PtrTrackMouseEvent ptrTrackMouseEvent = 0;
+#endif
+ state = translateButtonState(msg.wParam, type, button); // button state
+ const QPoint widgetPos = mapFromGlobal(QPoint(msg.pt.x, msg.pt.y));
+ QWidget *alienWidget = !internalWinId() ? this : childAt(widgetPos);
+ if (alienWidget && alienWidget->internalWinId())
+ alienWidget = 0;
+
+ if (type == QEvent::MouseMove || type == QEvent::NonClientAreaMouseMove
+ || type == QEvent::TabletMove) {
+
+ if (!(state & Qt::MouseButtonMask))
+ qt_button_down = 0;
+#ifndef QT_NO_CURSOR
+ QCursor *c = qt_grab_cursor();
+ if (!c)
+ c = QApplication::overrideCursor();
+ if (c) // application cursor defined
+ SetCursor(c->handle());
+ else if (type != QEvent::NonClientAreaMouseMove && !qt_button_down) {
+ // use widget cursor if widget is enabled
+ QWidget *w = alienWidget ? alienWidget : this;
+ while (!w->isWindow() && !w->isEnabled())
+ w = w->parentWidget();
+ SetCursor(w->cursor().handle());
+ }
+#endif // QT_NO_CURSOR
+
+ HWND id = effectiveWinId();
+ QWidget *mouseGrabber = QWidget::mouseGrabber();
+ QWidget *activePopupWidget = QApplication::activePopupWidget();
+ if (mouseGrabber) {
+ if (!activePopupWidget || (activePopupWidget == this && !rect().contains(widgetPos)))
+ id = mouseGrabber->effectiveWinId();
+ } else if (type == QEvent::NonClientAreaMouseMove) {
+ id = 0;
+ }
+
+ if (curWin != id) { // new current window
+ if (id == 0) {
+ QWidget *leave = qt_last_mouse_receiver;
+ if (!leave)
+ leave = QWidget::find(curWin);
+ QApplicationPrivate::dispatchEnterLeave(0, leave);
+ qt_last_mouse_receiver = 0;
+ curWin = 0;
+ } else {
+ QWidget *leave = 0;
+ if (curWin && qt_last_mouse_receiver)
+ leave = qt_last_mouse_receiver;
+ else
+ leave = QWidget::find(curWin);
+ QWidget *enter = alienWidget ? alienWidget : this;
+ if (mouseGrabber && activePopupWidget) {
+ if (leave != mouseGrabber)
+ enter = mouseGrabber;
+ else
+ enter = activePopupWidget == this ? this : mouseGrabber;
+ }
+ QApplicationPrivate::dispatchEnterLeave(enter, leave);
+ qt_last_mouse_receiver = enter;
+ curWin = enter ? enter->effectiveWinId() : 0;
+ }
+#ifndef Q_OS_WINCE
+
+ if (curWin != 0) {
+ if (!trackMouseEventLookup) {
+ trackMouseEventLookup = true;
+ ptrTrackMouseEvent = (PtrTrackMouseEvent)QSystemLibrary::resolve(QLatin1String("comctl32"), "_TrackMouseEvent");
+ }
+ if (ptrTrackMouseEvent && !qApp->d_func()->inPopupMode()) {
+ // We always have to set the tracking, since
+ // Windows detects more leaves than we do..
+ TRACKMOUSEEVENT tme;
+ tme.cbSize = sizeof(TRACKMOUSEEVENT);
+ tme.dwFlags = 0x00000002; // TME_LEAVE
+ tme.hwndTrack = curWin; // Track on window receiving msgs
+ tme.dwHoverTime = (DWORD)-1; // HOVER_DEFAULT
+ ptrTrackMouseEvent(&tme);
+ }
+ }
+#endif // Q_OS_WINCE
+ }
+
+ POINT curPos = msg.pt;
+ if (curPos.x == gpos.x && curPos.y == gpos.y)
+ return true; // same global position
+ gpos = curPos;
+
+ Q_ASSERT(testAttribute(Qt::WA_WState_Created));
+ ScreenToClient(internalWinId(), &curPos);
+
+ pos.rx() = curPos.x;
+ pos.ry() = curPos.y;
+ pos = d_func()->mapFromWS(pos);
+ } else {
+ gpos = msg.pt;
+ pos = mapFromGlobal(QPoint(gpos.x, gpos.y));
+
+ // mouse button pressed
+ if (!qt_button_down && (type == QEvent::MouseButtonPress || type == QEvent::MouseButtonDblClick)) {
+ QWidget *tlw = window();
+ if (QWidget *child = tlw->childAt(mapTo(tlw, pos)))
+ qt_button_down = child;
+ else
+ qt_button_down = this;
+ }
+ }
+
+ bool res = false;
+
+ bool nonClientAreaEvent = type >= QEvent::NonClientAreaMouseMove
+ && type <= QEvent::NonClientAreaMouseButtonDblClick;
+
+ if (qApp->d_func()->inPopupMode()) { // in popup mode
+
+ if (nonClientAreaEvent)
+ return false;
+
+ replayPopupMouseEvent = false;
+ QWidget* activePopupWidget = QApplication::activePopupWidget();
+ QWidget *target = activePopupWidget;
+ const QPoint globalPos(gpos.x, gpos.y);
+
+ if (target != this) {
+ if ((windowType() == Qt::Popup) && rect().contains(pos) && 0)
+ target = this;
+ else // send to last popup
+ pos = target->mapFromGlobal(globalPos);
+ }
+ QWidget *popupChild = target->childAt(pos);
+ bool releaseAfter = false;
+ switch (type) {
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonDblClick:
+ popupButtonFocus = popupChild;
+ break;
+ case QEvent::MouseButtonRelease:
+ case QEvent::TabletRelease:
+
+ releaseAfter = true;
+ break;
+ default:
+ break; // nothing for mouse move
+ }
+
+ if (target->isEnabled()) {
+ if (popupButtonFocus) {
+ target = popupButtonFocus;
+ } else if (popupChild) {
+ target = popupChild;
+ }
+
+ pos = target->mapFromGlobal(globalPos);
+ QMouseEvent e(type, pos, globalPos,
+ Qt::MouseButton(button),
+ Qt::MouseButtons(state & Qt::MouseButtonMask),
+ Qt::KeyboardModifiers(state & Qt::KeyboardModifierMask));
+ res = QApplicationPrivate::sendMouseEvent(target, &e, alienWidget, this, &qt_button_down,
+ qt_last_mouse_receiver);
+ res = res && e.isAccepted();
+ } else {
+ // close disabled popups when a mouse button is pressed or released
+ switch (type) {
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonDblClick:
+ case QEvent::MouseButtonRelease:
+ target->close();
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (releaseAfter) {
+ popupButtonFocus = 0;
+ qt_button_down = 0;
+ }
+
+#ifndef Q_OS_WINCE
+ if (type == QEvent::MouseButtonPress
+ && QApplication::activePopupWidget() != activePopupWidget
+ && ptrTrackMouseEvent
+ && curWin) {
+ // Since curWin is already the window we clicked on,
+ // we have to setup the mouse tracking here.
+ TRACKMOUSEEVENT tme;
+ tme.cbSize = sizeof(TRACKMOUSEEVENT);
+ tme.dwFlags = 0x00000002; // TME_LEAVE
+ tme.hwndTrack = curWin; // Track on window receiving msgs
+ tme.dwHoverTime = (DWORD)-1; // HOVER_DEFAULT
+ ptrTrackMouseEvent(&tme);
+ }
+#endif
+ if (type == QEvent::MouseButtonPress
+ && QApplication::activePopupWidget() != activePopupWidget
+ && replayPopupMouseEvent) {
+ // the popup disappeared. Replay the event
+ QWidget* w = QApplication::widgetAt(gpos.x, gpos.y);
+ if (w && !QApplicationPrivate::isBlockedByModal(w)) {
+ Q_ASSERT(w->testAttribute(Qt::WA_WState_Created));
+ HWND hwndTarget = w->effectiveWinId();
+ if (QWidget::mouseGrabber() == 0)
+ setAutoCapture(hwndTarget);
+ if (!w->isActiveWindow())
+ w->activateWindow();
+ POINT widgetpt = gpos;
+ ScreenToClient(hwndTarget, &widgetpt);
+ LPARAM lParam = MAKELPARAM(widgetpt.x, widgetpt.y);
+ PostMessage(hwndTarget, msg.message, msg.wParam, lParam);
+ }
+ } else if (type == QEvent::MouseButtonRelease && button == Qt::RightButton
+ && QApplication::activePopupWidget() == activePopupWidget) {
+ // popup still alive and received right-button-release
+#if !defined(QT_NO_CONTEXTMENU)
+ QContextMenuEvent e2(QContextMenuEvent::Mouse, pos, globalPos,
+ qt_win_getKeyboardModifiers());
+ bool res2 = QApplication::sendSpontaneousEvent( target, &e2 );
+ if (!res) // RMB not accepted
+ res = res2 && e2.isAccepted();
+#endif
+ }
+ } else { // not popup mode
+ int bs = state & Qt::MouseButtonMask;
+ if ((type == QEvent::MouseButtonPress ||
+ type == QEvent::MouseButtonDblClick) && bs == button) {
+ Q_ASSERT(testAttribute(Qt::WA_WState_Created));
+ if (QWidget::mouseGrabber() == 0)
+ setAutoCapture(internalWinId());
+ } else if (type == QEvent::MouseButtonRelease && bs == 0) {
+ if (QWidget::mouseGrabber() == 0)
+ releaseAutoCapture();
+ }
+
+ const QPoint globalPos(gpos.x,gpos.y);
+ QWidget *widget = QApplicationPrivate::pickMouseReceiver(this, globalPos, pos, type,
+ Qt::MouseButtons(bs),
+ qt_button_down, alienWidget);
+ if (!widget)
+ return false; // don't send event
+
+ QMouseEvent e(type, pos, globalPos, Qt::MouseButton(button),
+ Qt::MouseButtons(state & Qt::MouseButtonMask),
+ Qt::KeyboardModifiers(state & Qt::KeyboardModifierMask));
+
+ res = QApplicationPrivate::sendMouseEvent(widget, &e, alienWidget, this, &qt_button_down,
+ qt_last_mouse_receiver);
+
+ // non client area events are only informational, you cannot "handle" them
+ res = res && e.isAccepted() && !nonClientAreaEvent;
+#if !defined(QT_NO_CONTEXTMENU)
+ if (type == QEvent::MouseButtonRelease && button == Qt::RightButton) {
+ QContextMenuEvent e2(QContextMenuEvent::Mouse, pos, globalPos,
+ qt_win_getKeyboardModifiers());
+ bool res2 = QApplication::sendSpontaneousEvent(widget, &e2);
+ if (!res)
+ res = res2 && e2.isAccepted();
+ }
+#endif
+
+ if (type != QEvent::MouseMove)
+ pos.rx() = pos.ry() = -9999; // init for move compression
+ }
+ return res;
+}
+
+bool QETWidget::translateWheelEvent(const MSG &msg)
+{
+ int state = 0;
+
+ if (sm_blockUserInput) // block user interaction during session management
+ return true;
+
+ state = translateButtonState(GET_KEYSTATE_WPARAM(msg.wParam), 0, 0);
+
+ int delta;
+ if (msg.message == WM_MOUSEWHEEL || msg.message == WM_MOUSEHWHEEL)
+ delta = (short) HIWORD (msg.wParam);
+ else
+ delta = (int) msg.wParam;
+
+ Qt::Orientation orient = (msg.message == WM_MOUSEHWHEEL || state&Qt::AltModifier
+#if 0
+ // disabled for now - Trenton's one-wheel mouse makes trouble...
+ // "delta" for usual wheels is +-120. +-240 seems to indicate
+ // the second wheel see more recent MSDN for WM_MOUSEWHEEL
+
+ ( // <- parantheses added to make update happy, remove if the
+ // #if 0 is removed
+ || delta == 240 || delta == -240)?Qt::Horizontal:Vertical;
+ if (delta == 240 || delta == -240)
+ delta /= 2;
+#endif
+ ) ? Qt::Horizontal : Qt::Vertical;
+
+ // according to the MSDN documentation on WM_MOUSEHWHEEL:
+ // a positive value indicates that the wheel was rotated to the right;
+ // a negative value indicates that the wheel was rotated to the left.
+ // Qt defines this value as the exact opposite, so we have to flip the value!
+ if (msg.message == WM_MOUSEHWHEEL)
+ delta = -delta;
+
+ QPoint globalPos;
+
+ globalPos.rx() = (short)LOWORD (msg.lParam);
+ globalPos.ry() = (short)HIWORD (msg.lParam);
+
+
+ // if there is a widget under the mouse and it is not shadowed
+ // by modality, we send the event to it first
+ int ret = 0;
+ QWidget* w = QApplication::widgetAt(globalPos);
+ if (!w || !qt_try_modal(w, (MSG*)&msg, ret)) {
+ //synaptics touchpad shows its own widget at this position
+ //so widgetAt() will fail with that HWND, try child of this widget
+ w = this->childAt(this->mapFromGlobal(globalPos));
+ if (!w)
+ w = this;
+ }
+
+ // send the event to the widget or its ancestors
+ {
+ QWidget* popup = QApplication::activePopupWidget();
+ if (popup && w->window() != popup)
+ popup->close();
+#ifndef QT_NO_WHEELEVENT
+ QWheelEvent e(w->mapFromGlobal(globalPos), globalPos, delta,
+ Qt::MouseButtons(state & Qt::MouseButtonMask),
+ Qt::KeyboardModifier(state & Qt::KeyboardModifierMask), orient);
+
+ if (QApplication::sendSpontaneousEvent(w, &e))
+#else
+ Q_UNUSED(orient);
+#endif //QT_NO_WHEELEVENT
+ return true;
+ }
+
+ // send the event to the widget that has the focus or its ancestors, if different
+ if (w != QApplication::focusWidget() && (w = QApplication::focusWidget())) {
+ QWidget* popup = QApplication::activePopupWidget();
+ if (popup && w->window() != popup)
+ popup->close();
+#ifndef QT_NO_WHEELEVENT
+ QWheelEvent e(w->mapFromGlobal(globalPos), globalPos, delta,
+ Qt::MouseButtons(state & Qt::MouseButtonMask),
+ Qt::KeyboardModifier(state & Qt::KeyboardModifierMask), orient);
+ if (QApplication::sendSpontaneousEvent(w, &e))
+#endif //QT_NO_WHEELEVENT
+ return true;
+ }
+ return false;
+}
+
+
+//
+// Windows Wintab to QTabletEvent translation
+//
+
+// the following is adapted from the wintab syspress example (public domain)
+/* -------------------------------------------------------------------------- */
+// Initialize the "static" information of a cursor device (pen, airbrush, etc).
+// The QTabletDeviceData is initialized with the data that do not change in time
+// (number of button, type of device, etc) but do not initialize the variable data
+// (e.g.: pen or eraser)
+#ifndef QT_NO_TABLETEVENT
+
+static void tabletInit(const quint64 uniqueId, const UINT csr_type, HCTX hTab)
+{
+ Q_ASSERT(ptrWTInfo);
+ Q_ASSERT(ptrWTGet);
+
+ Q_ASSERT(!tCursorInfo()->contains(uniqueId));
+
+ /* browse WinTab's many info items to discover pressure handling. */
+ AXIS np;
+ LOGCONTEXT lc;
+
+ /* get the current context for its device variable. */
+ ptrWTGet(hTab, &lc);
+
+ /* get the size of the pressure axis. */
+ QTabletDeviceData tdd;
+ tdd.llId = uniqueId;
+
+ ptrWTInfo(WTI_DEVICES + lc.lcDevice, DVC_NPRESSURE, &np);
+ tdd.minPressure = int(np.axMin);
+ tdd.maxPressure = int(np.axMax);
+
+ ptrWTInfo(WTI_DEVICES + lc.lcDevice, DVC_TPRESSURE, &np);
+ tdd.minTanPressure = int(np.axMin);
+ tdd.maxTanPressure = int(np.axMax);
+
+ LOGCONTEXT lcMine;
+
+ /* get default region */
+ ptrWTInfo(WTI_DEFCONTEXT, 0, &lcMine);
+
+ tdd.minX = 0;
+ tdd.maxX = int(lcMine.lcInExtX) - int(lcMine.lcInOrgX);
+
+ tdd.minY = 0;
+ tdd.maxY = int(lcMine.lcInExtY) - int(lcMine.lcInOrgY);
+
+ tdd.minZ = 0;
+ tdd.maxZ = int(lcMine.lcInExtZ) - int(lcMine.lcInOrgZ);
+
+ const uint cursorTypeBitMask = 0x0F06; // bitmask to find the specific cursor type (see Wacom FAQ)
+ if (((csr_type & 0x0006) == 0x0002) && ((csr_type & cursorTypeBitMask) != 0x0902)) {
+ tdd.currentDevice = QTabletEvent::Stylus;
+ } else {
+ switch (csr_type & cursorTypeBitMask) {
+ case 0x0802:
+ tdd.currentDevice = QTabletEvent::Stylus;
+ break;
+ case 0x0902:
+ tdd.currentDevice = QTabletEvent::Airbrush;
+ break;
+ case 0x0004:
+ tdd.currentDevice = QTabletEvent::FourDMouse;
+ break;
+ case 0x0006:
+ tdd.currentDevice = QTabletEvent::Puck;
+ break;
+ case 0x0804:
+ tdd.currentDevice = QTabletEvent::RotationStylus;
+ break;
+ default:
+ tdd.currentDevice = QTabletEvent::NoDevice;
+ }
+ }
+ tCursorInfo()->insert(uniqueId, tdd);
+}
+#endif // QT_NO_TABLETEVENT
+
+// Update the "dynamic" information of a cursor device (pen, airbrush, etc).
+// The dynamic information is the information of QTabletDeviceData that can change
+// in time (eraser or pen if a device is turned around).
+#ifndef QT_NO_TABLETEVENT
+
+static void tabletUpdateCursor(QTabletDeviceData &tdd, const UINT currentCursor)
+{
+ switch (currentCursor % 3) { // %3 for dual track
+ case 0:
+ tdd.currentPointerType = QTabletEvent::Cursor;
+ break;
+ case 1:
+ tdd.currentPointerType = QTabletEvent::Pen;
+ break;
+ case 2:
+ tdd.currentPointerType = QTabletEvent::Eraser;
+ break;
+ default:
+ tdd.currentPointerType = QTabletEvent::UnknownPointer;
+ }
+}
+#endif // QT_NO_TABLETEVENT
+
+bool QETWidget::translateTabletEvent(const MSG &msg, PACKET *localPacketBuf,
+ int numPackets)
+{
+ Q_UNUSED(msg);
+ POINT ptNew;
+ static DWORD btnNew, btnOld, btnChange;
+ qreal prsNew;
+ ORIENTATION ort;
+ static bool button_pressed = false;
+ int i,
+ tiltX,
+ tiltY;
+ bool sendEvent = false;
+ QEvent::Type t;
+ int z = 0;
+ qreal rotation = 0.0;
+ qreal tangentialPressure;
+
+ // the most common event that we get...
+ t = QEvent::TabletMove;
+ for (i = 0; i < numPackets; i++) {
+ // get the unique ID of the device...
+ btnOld = btnNew;
+ btnNew = localPacketBuf[i].pkButtons;
+ btnChange = btnOld ^ btnNew;
+
+ if (btnNew & btnChange) {
+ button_pressed = true;
+ t = QEvent::TabletPress;
+ }
+ ptNew.x = UINT(localPacketBuf[i].pkX);
+ ptNew.y = UINT(localPacketBuf[i].pkY);
+#ifndef QT_NO_TABLETEVENT
+ z = (currentTabletPointer.currentDevice == QTabletEvent::FourDMouse) ? UINT(localPacketBuf[i].pkZ) : 0;
+#else
+ Q_UNUSED(z);
+#endif // QT_NO_TABLETEVENT
+ prsNew = 0.0;
+ QRect desktopArea = QApplication::desktop()->geometry();
+ QPointF hiResGlobal = currentTabletPointer.scaleCoord(ptNew.x, ptNew.y, desktopArea.left(),
+ desktopArea.width(), desktopArea.top(),
+ desktopArea.height());
+
+ if (btnNew) {
+#ifndef QT_NO_TABLETEVENT
+ if (currentTabletPointer.currentPointerType == QTabletEvent::Pen || currentTabletPointer.currentPointerType == QTabletEvent::Eraser)
+ prsNew = localPacketBuf[i].pkNormalPressure
+ / qreal(currentTabletPointer.maxPressure
+ - currentTabletPointer.minPressure);
+ else
+#endif // QT_NO_TABLETEVENT
+ prsNew = 0;
+ } else if (button_pressed) {
+ // One button press, should only give one button release
+ t = QEvent::TabletRelease;
+ button_pressed = false;
+ }
+ QPoint globalPos(qRound(hiResGlobal.x()), qRound(hiResGlobal.y()));
+
+ if (t == QEvent::TabletPress)
+ {
+ qt_button_down = QApplication::widgetAt(globalPos);
+ }
+
+ // make sure the tablet event get's sent to the proper widget...
+ QWidget *w = 0;
+
+ if (qt_button_down)
+ w = qt_button_down; // Pass it to the thing that's grabbed it.
+ else
+ w = QApplication::widgetAt(globalPos);
+
+ if (!w)
+ w = this;
+
+ if (t == QEvent::TabletRelease)
+ {
+ if (qt_win_ignoreNextMouseReleaseEvent) {
+ qt_win_ignoreNextMouseReleaseEvent = false;
+ if (qt_button_down && qt_button_down->internalWinId() == autoCaptureWnd) {
+ releaseAutoCapture();
+ qt_button_down = 0;
+ }
+ }
+
+ }
+
+ QPoint localPos = w->mapFromGlobal(globalPos);
+#ifndef QT_NO_TABLETEVENT
+ if (currentTabletPointer.currentDevice == QTabletEvent::Airbrush) {
+ tangentialPressure = localPacketBuf[i].pkTangentPressure
+ / qreal(currentTabletPointer.maxTanPressure
+ - currentTabletPointer.minTanPressure);
+ } else {
+ tangentialPressure = 0.0;
+ }
+#else
+ tangentialPressure = 0.0;
+#endif // QT_NO_TABLETEVENT
+
+ if (!qt_tablet_tilt_support) {
+ tiltX = tiltY = 0;
+ rotation = 0.0;
+ } else {
+ ort = localPacketBuf[i].pkOrientation;
+ // convert from azimuth and altitude to x tilt and y tilt
+ // what follows is the optimized version. Here are the equations
+ // I used to get to this point (in case things change :)
+ // X = sin(azimuth) * cos(altitude)
+ // Y = cos(azimuth) * cos(altitude)
+ // Z = sin(altitude)
+ // X Tilt = arctan(X / Z)
+ // Y Tilt = arctan(Y / Z)
+ double radAzim = (ort.orAzimuth / 10) * (Q_PI / 180);
+ //double radAlt = abs(ort.orAltitude / 10) * (Q_PI / 180);
+ double tanAlt = tan((abs(ort.orAltitude / 10)) * (Q_PI / 180));
+
+ double degX = atan(sin(radAzim) / tanAlt);
+ double degY = atan(cos(radAzim) / tanAlt);
+ tiltX = int(degX * (180 / Q_PI));
+ tiltY = int(-degY * (180 / Q_PI));
+ rotation = ort.orTwist;
+ }
+#ifndef QT_NO_TABLETEVENT
+ QTabletEvent e(t, localPos, globalPos, hiResGlobal, currentTabletPointer.currentDevice,
+ currentTabletPointer.currentPointerType, prsNew, tiltX, tiltY,
+ tangentialPressure, rotation, z, QApplication::keyboardModifiers(), currentTabletPointer.llId);
+ sendEvent = QApplication::sendSpontaneousEvent(w, &e);
+#endif // QT_NO_TABLETEVENT
+ }
+ return sendEvent;
+}
+
+extern bool qt_is_gui_used;
+
+
+#ifndef QT_NO_TABLETEVENT
+
+static void initWinTabFunctions()
+{
+#if defined(Q_OS_WINCE)
+ return;
+#else
+ if (!qt_is_gui_used)
+ return;
+
+ QSystemLibrary library(QLatin1String("wintab32"));
+ ptrWTInfo = (PtrWTInfo)library.resolve("WTInfoW");
+ ptrWTGet = (PtrWTGet)library.resolve("WTGetW");
+ ptrWTEnable = (PtrWTEnable)library.resolve("WTEnable");
+ ptrWTOverlap = (PtrWTEnable)library.resolve("WTOverlap");
+ ptrWTPacketsGet = (PtrWTPacketsGet)library.resolve("WTPacketsGet");
+#endif // Q_OS_WINCE
+}
+#endif // QT_NO_TABLETEVENT
+
+
+//
+// Paint event translation
+//
+bool QETWidget::translatePaintEvent(const MSG &msg)
+{
+ if (!isWindow() && testAttribute(Qt::WA_NativeWindow))
+ Q_ASSERT(internalWinId());
+
+ Q_ASSERT(testAttribute(Qt::WA_WState_Created));
+ if (!GetUpdateRect(internalWinId(), 0, FALSE)) { // The update bounding rect is invalid
+ d_func()->hd = 0;
+ setAttribute(Qt::WA_PendingUpdate, false);
+ return false;
+ }
+
+ if (msg.message == WM_ERASEBKGND)
+ return true;
+
+ setAttribute(Qt::WA_PendingUpdate, false);
+
+ if (d_func()->isGLWidget) {
+ if (d_func()->usesDoubleBufferedGLContext)
+ InvalidateRect(internalWinId(), 0, false);
+ } else {
+ const QRegion dirtyInBackingStore(qt_dirtyRegion(this));
+ // Make sure the invalidated region contains the region we're about to repaint.
+ // BeginPaint will set the clip to the invalidated region and it is impossible
+ // to enlarge it afterwards (only shrink it). Using GetDCEx is not suffient
+ // as it may return an invalid context (especially on Windows Vista).
+ if (!dirtyInBackingStore.isEmpty())
+ InvalidateRgn(internalWinId(), dirtyInBackingStore.handle(), false);
+ }
+ PAINTSTRUCT ps;
+ d_func()->hd = BeginPaint(internalWinId(), &ps);
+
+ const QRect updateRect(QPoint(ps.rcPaint.left, ps.rcPaint.top),
+ QPoint(ps.rcPaint.right, ps.rcPaint.bottom));
+
+ // Mapping region from system to qt (32 bit) coordinate system.
+ d_func()->syncBackingStore(updateRect.translated(data->wrect.topLeft()));
+
+ d_func()->hd = 0;
+ EndPaint(internalWinId(), &ps);
+
+ return true;
+}
+
+//
+// Window move and resize (configure) events
+//
+
+bool QETWidget::translateConfigEvent(const MSG &msg)
+{
+ if (!testAttribute(Qt::WA_WState_Created)) // in QWidget::create()
+ return true;
+ if (testAttribute(Qt::WA_WState_ConfigPending))
+ return true;
+ if (testAttribute(Qt::WA_DontShowOnScreen))
+ return true;
+ if (!isWindow())
+ return true;
+ setAttribute(Qt::WA_WState_ConfigPending); // set config flag
+ QRect cr = geometry();
+ if (msg.message == WM_SIZE) { // resize event
+ WORD a = LOWORD(msg.lParam);
+ WORD b = HIWORD(msg.lParam);
+ QSize oldSize = size();
+ QSize newSize(a, b);
+#ifdef Q_WS_WINCE_WM
+ if (isFullScreen() && (oldSize.width() == newSize.height()) && (oldSize.height() == newSize.width()))
+ qt_wince_hide_taskbar(internalWinId());
+#endif
+ cr.setSize(newSize);
+ if (msg.wParam != SIZE_MINIMIZED)
+ data->crect = cr;
+ if (isWindow()) { // update title/icon text
+ d_func()->createTLExtra();
+ // Capture SIZE_MINIMIZED without preceding WM_SYSCOMMAND
+ // (like Windows+M)
+ if (msg.wParam == SIZE_MINIMIZED && !isMinimized()) {
+#ifndef Q_WS_WINCE
+ const QString title = windowIconText();
+ if (!title.isEmpty())
+ d_func()->setWindowTitle_helper(title);
+#endif
+ data->window_state |= Qt::WindowMinimized;
+ if (isVisible()) {
+ QHideEvent e;
+ QApplication::sendSpontaneousEvent(this, &e);
+ hideChildren(true);
+ }
+ } else if (msg.wParam != SIZE_MINIMIZED) {
+ bool window_state_changed = false;
+ Qt::WindowStates oldstate = Qt::WindowStates(dataPtr()->window_state);
+ if (isMinimized()) {
+#ifndef Q_WS_WINCE
+ const QString title = windowTitle();
+ if (!title.isEmpty())
+ d_func()->setWindowTitle_helper(title);
+#endif
+ data->window_state &= ~Qt::WindowMinimized;
+ showChildren(true);
+ QShowEvent e;
+ QApplication::sendSpontaneousEvent(this, &e);
+ // Capture SIZE_MAXIMIZED and SIZE_RESTORED without preceding WM_SYSCOMMAND
+ // (Aero Snap on Win7)
+ } else if (msg.wParam == SIZE_MAXIMIZED && !isMaximized()) {
+ data->window_state |= Qt::WindowMaximized;
+ window_state_changed = true;
+ } else if (msg.wParam == SIZE_RESTORED && isMaximized()) {
+ data->window_state &= ~(Qt::WindowMaximized);
+ window_state_changed = true;
+ }
+ if (window_state_changed) {
+ QWindowStateChangeEvent e(oldstate);
+ QApplication::sendSpontaneousEvent(this, &e);
+ }
+ }
+ }
+ if (msg.wParam != SIZE_MINIMIZED && oldSize != newSize) {
+ if (isVisible()) {
+ QTLWExtra *tlwExtra = maybeTopData();
+ static bool slowResize = qgetenv("QT_SLOW_TOPLEVEL_RESIZE").toInt();
+ const bool hasStaticContents = tlwExtra && tlwExtra->backingStore
+ && tlwExtra->backingStore->hasStaticContents();
+ // If we have a backing store with static contents, we have to disable the top-level
+ // resize optimization in order to get invalidated regions for resized widgets.
+ // The optimization discards all invalidateBuffer() calls since we're going to
+ // repaint everything anyways, but that's not the case with static contents.
+ if (!slowResize && tlwExtra && !hasStaticContents)
+ tlwExtra->inTopLevelResize = true;
+ QResizeEvent e(newSize, oldSize);
+ QApplication::sendSpontaneousEvent(this, &e);
+ if (d_func()->paintOnScreen()) {
+ QRegion updateRegion(rect());
+ if (testAttribute(Qt::WA_StaticContents))
+ updateRegion -= QRect(0, 0, oldSize.width(), oldSize.height());
+ d_func()->syncBackingStore(updateRegion);
+ } else {
+ d_func()->syncBackingStore();
+ }
+ if (!slowResize && tlwExtra)
+ tlwExtra->inTopLevelResize = false;
+ } else {
+ QResizeEvent *e = new QResizeEvent(newSize, oldSize);
+ QApplication::postEvent(this, e);
+ }
+ }
+ } else if (msg.message == WM_MOVE) { // move event
+ int a = (int) (short) LOWORD(msg.lParam);
+ int b = (int) (short) HIWORD(msg.lParam);
+ QPoint oldPos = geometry().topLeft();
+ QPoint newCPos(a, b);
+ // Ignore silly Windows move event to wild pos after iconify.
+#if !defined(Q_WS_WINCE)
+ if (!IsIconic(internalWinId()) && newCPos != oldPos) {
+#endif
+ cr.moveTopLeft(newCPos);
+ data->crect = cr;
+ if (isVisible()) {
+ QMoveEvent e(newCPos, oldPos); // cpos (client position)
+ QApplication::sendSpontaneousEvent(this, &e);
+ } else {
+ QMoveEvent * e = new QMoveEvent(newCPos, oldPos);
+ QApplication::postEvent(this, e);
+ }
+#if !defined(Q_WS_WINCE)
+ }
+#endif
+ }
+ setAttribute(Qt::WA_WState_ConfigPending, false); // clear config flag
+ return true;
+}
+
+
+//
+// Close window event translation.
+//
+// This class is a friend of QApplication because it needs to emit the
+// lastWindowClosed() signal when the last top level widget is closed.
+//
+
+bool QETWidget::translateCloseEvent(const MSG &)
+{
+ return d_func()->close_helper(QWidgetPrivate::CloseWithSpontaneousEvent);
+}
+
+#ifndef QT_NO_GESTURES
+bool QETWidget::translateGestureEvent(const MSG &, const GESTUREINFO &gi)
+{
+ const QPoint widgetPos = QPoint(gi.ptsLocation.x, gi.ptsLocation.y);
+ QWidget *alienWidget = !internalWinId() ? this : childAt(widgetPos);
+ if (alienWidget && alienWidget->internalWinId())
+ alienWidget = 0;
+ QWidget *widget = alienWidget ? alienWidget : this;
+
+ QNativeGestureEvent event;
+ event.sequenceId = gi.dwSequenceID;
+ event.position = QPoint(gi.ptsLocation.x, gi.ptsLocation.y);
+ event.argument = gi.ullArguments;
+
+ switch (gi.dwID) {
+ case GID_BEGIN:
+ event.gestureType = QNativeGestureEvent::GestureBegin;
+ break;
+ case GID_END:
+ event.gestureType = QNativeGestureEvent::GestureEnd;
+ break;
+ case GID_ZOOM:
+ event.gestureType = QNativeGestureEvent::Zoom;
+ break;
+ case GID_PAN:
+ event.gestureType = QNativeGestureEvent::Pan;
+ break;
+ case GID_ROTATE:
+ event.gestureType = QNativeGestureEvent::Rotate;
+ break;
+ case GID_TWOFINGERTAP:
+ case GID_ROLLOVER:
+ default:
+ break;
+ }
+ if (event.gestureType != QNativeGestureEvent::None)
+ qt_sendSpontaneousEvent(widget, &event);
+ return true;
+}
+#endif // QT_NO_GESTURES
+
+void QApplication::setCursorFlashTime(int msecs)
+{
+ SetCaretBlinkTime(msecs / 2);
+ QApplicationPrivate::cursor_flash_time = msecs;
+}
+
+
+int QApplication::cursorFlashTime()
+{
+ int blink = (int)GetCaretBlinkTime();
+ if (!blink)
+ return QApplicationPrivate::cursor_flash_time;
+ if (blink > 0)
+ return 2*blink;
+ return 0;
+}
+
+
+void QApplication::setDoubleClickInterval(int ms)
+{
+#ifndef Q_WS_WINCE
+ SetDoubleClickTime(ms);
+#endif
+ QApplicationPrivate::mouse_double_click_time = ms;
+}
+
+int QApplication::doubleClickInterval()
+{
+ int ms = GetDoubleClickTime();
+ if (ms != 0)
+ return ms;
+ return QApplicationPrivate::mouse_double_click_time;
+}
+
+
+void QApplication::setKeyboardInputInterval(int ms)
+{
+ QApplicationPrivate::keyboard_input_time = ms;
+}
+
+int QApplication::keyboardInputInterval()
+{
+ // FIXME: get from the system
+ return QApplicationPrivate::keyboard_input_time;
+}
+
+#ifndef QT_NO_WHEELEVENT
+void QApplication::setWheelScrollLines(int n)
+{
+#ifdef SPI_SETWHEELSCROLLLINES
+ if (n < 0)
+ n = 0;
+ SystemParametersInfo(SPI_SETWHEELSCROLLLINES, (uint)n, 0, 0);
+#else
+ QApplicationPrivate::wheel_scroll_lines = n;
+#endif
+}
+
+int QApplication::wheelScrollLines()
+{
+#ifdef SPI_GETWHEELSCROLLLINES
+ uint i = 3;
+ SystemParametersInfo(SPI_GETWHEELSCROLLLINES, sizeof(uint), &i, 0);
+ if (i > INT_MAX)
+ i = INT_MAX;
+ return i;
+#else
+ return QApplicationPrivate::wheel_scroll_lines;
+#endif
+}
+#endif //QT_NO_WHEELEVENT
+
+static bool effect_override = false;
+
+void QApplication::setEffectEnabled(Qt::UIEffect effect, bool enable)
+{
+ effect_override = true;
+ switch (effect) {
+ case Qt::UI_AnimateMenu:
+ QApplicationPrivate::animate_menu = enable;
+ break;
+ case Qt::UI_FadeMenu:
+ QApplicationPrivate::fade_menu = enable;
+ break;
+ case Qt::UI_AnimateCombo:
+ QApplicationPrivate::animate_combo = enable;
+ break;
+ case Qt::UI_AnimateTooltip:
+ QApplicationPrivate::animate_tooltip = enable;
+ break;
+ case Qt::UI_FadeTooltip:
+ QApplicationPrivate::fade_tooltip = enable;
+ break;
+ case Qt::UI_AnimateToolBox:
+ QApplicationPrivate::animate_toolbox = enable;
+ break;
+ default:
+ QApplicationPrivate::animate_ui = enable;
+ break;
+ }
+}
+
+bool QApplication::isEffectEnabled(Qt::UIEffect effect)
+{
+ if (QColormap::instance().depth() < 16)
+ return false;
+
+ if (!effect_override && desktopSettingsAware()) {
+ // we know that they can be used when we are here
+ BOOL enabled = false;
+ UINT api;
+ switch (effect) {
+ case Qt::UI_AnimateMenu:
+ api = SPI_GETMENUANIMATION;
+ break;
+ case Qt::UI_FadeMenu:
+ api = SPI_GETMENUFADE;
+ break;
+ case Qt::UI_AnimateCombo:
+ api = SPI_GETCOMBOBOXANIMATION;
+ break;
+ case Qt::UI_AnimateTooltip:
+ api = SPI_GETTOOLTIPANIMATION;
+ break;
+ case Qt::UI_FadeTooltip:
+ api = SPI_GETTOOLTIPFADE;
+ break;
+ default:
+ api = SPI_GETUIEFFECTS;
+ break;
+ }
+ SystemParametersInfo(api, 0, &enabled, 0);
+ return enabled;
+ }
+
+ switch(effect) {
+ case Qt::UI_AnimateMenu:
+ return QApplicationPrivate::animate_menu;
+ case Qt::UI_FadeMenu:
+ return QApplicationPrivate::fade_menu;
+ case Qt::UI_AnimateCombo:
+ return QApplicationPrivate::animate_combo;
+ case Qt::UI_AnimateTooltip:
+ return QApplicationPrivate::animate_tooltip;
+ case Qt::UI_FadeTooltip:
+ return QApplicationPrivate::fade_tooltip;
+ case Qt::UI_AnimateToolBox:
+ return QApplicationPrivate::animate_toolbox;
+ default:
+ return QApplicationPrivate::animate_ui;
+ }
+}
+
+#ifndef QT_NO_SESSIONMANAGER
+
+bool QSessionManager::allowsInteraction()
+{
+ sm_blockUserInput = false;
+ return true;
+}
+
+bool QSessionManager::allowsErrorInteraction()
+{
+ sm_blockUserInput = false;
+ return true;
+}
+
+void QSessionManager::release()
+{
+ if (sm_smActive)
+ sm_blockUserInput = true;
+}
+
+void QSessionManager::cancel()
+{
+ sm_cancel = true;
+}
+
+#endif //QT_NO_SESSIONMANAGER
+
+
+bool QApplicationPrivate::HasTouchSupport = false;
+PtrRegisterTouchWindow QApplicationPrivate::RegisterTouchWindow = 0;
+PtrGetTouchInputInfo QApplicationPrivate::GetTouchInputInfo = 0;
+PtrCloseTouchInputHandle QApplicationPrivate::CloseTouchInputHandle = 0;
+
+void QApplicationPrivate::initializeMultitouch_sys()
+{
+ if (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS7) {
+ static const int QT_SM_DIGITIZER = 94;
+ int value = GetSystemMetrics(QT_SM_DIGITIZER);
+ static const int QT_NID_INTEGRATED_TOUCH = 0x01;
+ static const int QT_NID_EXTERNAL_TOUCH = 0x02;
+ static const int QT_NID_MULTI_INPUT = 0x40;
+ QApplicationPrivate::HasTouchSupport =
+ value & (QT_NID_INTEGRATED_TOUCH | QT_NID_EXTERNAL_TOUCH | QT_NID_MULTI_INPUT);
+ }
+
+ QSystemLibrary library(QLatin1String("user32"));
+ // MinGW (g++ 3.4.5) accepts only C casts.
+ RegisterTouchWindow = (PtrRegisterTouchWindow)(library.resolve("RegisterTouchWindow"));
+ GetTouchInputInfo = (PtrGetTouchInputInfo)(library.resolve("GetTouchInputInfo"));
+ CloseTouchInputHandle = (PtrCloseTouchInputHandle)(library.resolve("CloseTouchInputHandle"));
+
+ touchInputIDToTouchPointID.clear();
+}
+
+void QApplicationPrivate::cleanupMultitouch_sys()
+{
+ touchInputIDToTouchPointID.clear();
+}
+
+bool QApplicationPrivate::translateTouchEvent(const MSG &msg)
+{
+ QWidget *widgetForHwnd = QWidget::find(msg.hwnd);
+ if (!widgetForHwnd)
+ return false;
+
+ QRect screenGeometry = QApplication::desktop()->screenGeometry(widgetForHwnd);
+
+ QList<QTouchEvent::TouchPoint> touchPoints;
+
+ QVector<TOUCHINPUT> winTouchInputs(msg.wParam);
+ memset(winTouchInputs.data(), 0, sizeof(TOUCHINPUT) * winTouchInputs.count());
+ Qt::TouchPointStates allStates = 0;
+ QApplicationPrivate::GetTouchInputInfo((HANDLE) msg.lParam, msg.wParam, winTouchInputs.data(), sizeof(TOUCHINPUT));
+ for (int i = 0; i < winTouchInputs.count(); ++i) {
+ const TOUCHINPUT &touchInput = winTouchInputs.at(i);
+
+ int touchPointID = touchInputIDToTouchPointID.value(touchInput.dwID, -1);
+ if (touchPointID == -1) {
+ touchPointID = touchInputIDToTouchPointID.count();
+ touchInputIDToTouchPointID.insert(touchInput.dwID, touchPointID);
+ }
+
+ QTouchEvent::TouchPoint touchPoint(touchPointID);
+
+ // update state
+ QPointF screenPos(qreal(touchInput.x) / qreal(100.), qreal(touchInput.y) / qreal(100.));
+ QRectF screenRect;
+ if (touchInput.dwMask & TOUCHINPUTMASKF_CONTACTAREA)
+ screenRect.setSize(QSizeF(qreal(touchInput.cxContact) / qreal(100.),
+ qreal(touchInput.cyContact) / qreal(100.)));
+ screenRect.moveCenter(screenPos);
+
+ Qt::TouchPointStates state;
+ if (touchInput.dwFlags & TOUCHEVENTF_DOWN) {
+ state = Qt::TouchPointPressed;
+ } else if (touchInput.dwFlags & TOUCHEVENTF_UP) {
+ state = Qt::TouchPointReleased;
+ } else {
+ state = (screenPos == touchPoint.screenPos()
+ ? Qt::TouchPointStationary
+ : Qt::TouchPointMoved);
+ }
+ if (touchInput.dwFlags & TOUCHEVENTF_PRIMARY)
+ state |= Qt::TouchPointPrimary;
+ touchPoint.setState(state);
+ touchPoint.setScreenRect(screenRect);
+ touchPoint.setNormalizedPos(QPointF(screenPos.x() / screenGeometry.width(),
+ screenPos.y() / screenGeometry.height()));
+
+ allStates |= state;
+
+ touchPoints.append(touchPoint);
+ }
+ QApplicationPrivate::CloseTouchInputHandle((HANDLE) msg.lParam);
+
+ if ((allStates & Qt::TouchPointStateMask) == Qt::TouchPointReleased) {
+ // all touch points released, forget the ids we've seen, they may not be reused
+ touchInputIDToTouchPointID.clear();
+ }
+
+ translateRawTouchEvent(widgetForHwnd, QTouchEvent::TouchScreen, touchPoints);
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/win/qclipboard_win.cpp b/src/widgets/platforms/win/qclipboard_win.cpp
new file mode 100644
index 0000000000..841eea1e34
--- /dev/null
+++ b/src/widgets/platforms/win/qclipboard_win.cpp
@@ -0,0 +1,398 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qclipboard.h"
+
+#ifndef QT_NO_CLIPBOARD
+
+#include "qapplication.h"
+#include "qapplication_p.h"
+#include "qeventloop.h"
+#include "qwidget.h"
+#include "qevent.h"
+#include "qmime.h"
+#include "qt_windows.h"
+#include "qdnd_p.h"
+#include <private/qwidget_p.h>
+#include <private/qsystemlibrary_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#if defined(Q_OS_WINCE)
+QT_BEGIN_INCLUDE_NAMESPACE
+#include "qguifunctions_wince.h"
+QT_END_INCLUDE_NAMESPACE
+
+HRESULT QtCeGetClipboard(IDataObject** obj);
+HRESULT QtCeSetClipboard(IDataObject* obj);
+void QtCeFlushClipboard();
+
+#define OleGetClipboard QtCeGetClipboard
+#define OleSetClipboard QtCeSetClipboard
+#define OleFlushClipboard QtCeFlushClipboard
+
+#endif
+
+typedef BOOL (WINAPI *PtrIsHungAppWindow)(HWND);
+
+static PtrIsHungAppWindow ptrIsHungAppWindow = 0;
+
+class QClipboardWatcher : public QInternalMimeData {
+public:
+ QClipboardWatcher()
+ : QInternalMimeData()
+ {
+ }
+
+ bool hasFormat_sys(const QString &mimetype) const;
+ QStringList formats_sys() const;
+ QVariant retrieveData_sys(const QString &mimetype, QVariant::Type preferredType) const;
+};
+
+
+bool QClipboardWatcher::hasFormat_sys(const QString &mime) const
+{
+ IDataObject * pDataObj = 0;
+
+ if (OleGetClipboard(&pDataObj) != S_OK && !pDataObj) // Sanity
+ return false;
+
+ bool has = QWindowsMime::converterToMime(mime, pDataObj) != 0;
+
+ pDataObj->Release();
+
+ return has;
+}
+
+QStringList QClipboardWatcher::formats_sys() const
+{
+ QStringList fmts;
+ IDataObject * pDataObj = 0;
+
+ if (OleGetClipboard(&pDataObj) != S_OK && !pDataObj) // Sanity
+ return QStringList();
+
+ fmts = QWindowsMime::allMimesForFormats(pDataObj);
+
+ pDataObj->Release();
+
+ return fmts;
+}
+
+QVariant QClipboardWatcher::retrieveData_sys(const QString &mimeType, QVariant::Type type) const
+{
+ QVariant result;
+ IDataObject * pDataObj = 0;
+
+ if (OleGetClipboard(&pDataObj) != S_OK && !pDataObj) // Sanity
+ return result;
+
+ QWindowsMime *converter = QWindowsMime::converterToMime(mimeType, pDataObj);
+
+ if (converter)
+ result = converter->convertToMime(mimeType, pDataObj, type);
+
+ pDataObj->Release();
+
+ return result;
+}
+
+class QClipboardData
+{
+public:
+ QClipboardData()
+ : iData(0)
+ , nextClipboardViewer(0)
+ {
+ clipBoardViewer = new QWidget();
+ clipBoardViewer->createWinId();
+ clipBoardViewer->setObjectName(QLatin1String("internal clipboard owner"));
+ // We don't need this internal widget to appear in QApplication::topLevelWidgets()
+ if (QWidgetPrivate::allWidgets)
+ QWidgetPrivate::allWidgets->remove(clipBoardViewer);
+ }
+
+ ~QClipboardData()
+ {
+ Q_ASSERT(clipBoardViewer->testAttribute(Qt::WA_WState_Created));
+ ChangeClipboardChain(clipBoardViewer->internalWinId(), nextClipboardViewer);
+ delete clipBoardViewer;
+ releaseIData();
+ }
+
+ void releaseIData()
+ {
+ if (iData) {
+ delete iData->mimeData();
+ iData->releaseQt();
+ iData->Release();
+ iData = 0;
+ }
+ }
+
+ QOleDataObject * iData;
+ QWidget *clipBoardViewer;
+ HWND nextClipboardViewer;
+ QClipboardWatcher watcher;
+};
+
+static QClipboardData *ptrClipboardData = 0;
+
+static QClipboardData *clipboardData()
+{
+ if (ptrClipboardData == 0) {
+ ptrClipboardData = new QClipboardData;
+ // this needs to be done here to avoid recursion
+ Q_ASSERT(ptrClipboardData->clipBoardViewer->testAttribute(Qt::WA_WState_Created));
+ ptrClipboardData->nextClipboardViewer = SetClipboardViewer(ptrClipboardData->clipBoardViewer->internalWinId());
+ }
+ return ptrClipboardData;
+}
+
+static void cleanupClipboardData()
+{
+ delete ptrClipboardData;
+ ptrClipboardData = 0;
+}
+
+#if defined(Q_OS_WINCE)
+HRESULT QtCeGetClipboard(IDataObject** obj)
+{
+ HWND owner = ptrClipboardData->clipBoardViewer->internalWinId();
+ if (!OpenClipboard(owner))
+ return !S_OK;
+
+ if (!IsClipboardFormatAvailable(CF_TEXT) && !IsClipboardFormatAvailable(CF_UNICODETEXT))
+ return !S_OK;
+
+ HANDLE clipData = GetClipboardData(CF_TEXT);
+ QString clipText;
+ if (clipData == 0) {
+ clipData = GetClipboardData(CF_UNICODETEXT);
+ if (clipData != 0)
+ clipText = QString::fromWCharArray((wchar_t *)clipData);
+ } else {
+ clipText = QString::fromLatin1((const char*)clipData);
+ }
+
+ QMimeData *mimeData = new QMimeData();
+ mimeData->setText(clipText);
+ QOleDataObject* data = new QOleDataObject(mimeData);
+ *obj = data;
+ CloseClipboard();
+ return S_OK;
+}
+
+HRESULT QtCeSetClipboard(IDataObject* obj)
+{
+ HWND owner = ptrClipboardData->clipBoardViewer->internalWinId();
+ if (!OpenClipboard(owner))
+ return !S_OK;
+
+ bool result = false;
+ if (obj == 0) {
+ result = true;
+ EmptyClipboard();
+ CloseClipboard();
+ } else {
+ QOleDataObject* qobj = static_cast<QOleDataObject*>(obj);
+
+ const QMimeData* data = qobj->mimeData();
+ if (data->hasText()) {
+ EmptyClipboard();
+ result = SetClipboardData(CF_UNICODETEXT, wcsdup(reinterpret_cast<const wchar_t *> (data->text().utf16()))) != NULL;
+ CloseClipboard();
+ result = true;
+ }
+ }
+ return result ? S_OK : !S_OK;
+}
+
+void QtCeFlushClipboard() { }
+#endif
+
+
+
+QClipboard::~QClipboard()
+{
+ cleanupClipboardData();
+}
+
+void QClipboard::setMimeData(QMimeData *src, Mode mode)
+{
+ if (mode != Clipboard)
+ return;
+ QClipboardData *d = clipboardData();
+
+ if (!(d->iData && d->iData->mimeData() == src)) {
+ d->releaseIData();
+ d->iData = new QOleDataObject(src);
+ }
+
+ if (OleSetClipboard(d->iData) != S_OK) {
+ d->releaseIData();
+ qErrnoWarning("QClipboard::setMimeData: Failed to set data on clipboard");
+ return;
+ }
+#if defined(Q_OS_WINCE)
+ // As WinCE does not support notifications we send the signal here
+ // We will get no event when the clipboard changes outside...
+ emit dataChanged();
+ emit changed(Clipboard);
+#endif
+}
+
+void QClipboard::clear(Mode mode)
+{
+ if (mode != Clipboard) return;
+
+ QClipboardData *d = clipboardData();
+
+ d->releaseIData();
+
+ if (OleSetClipboard(0) != S_OK) {
+ qErrnoWarning("QClipboard::clear: Failed to clear data on clipboard");
+ return;
+ }
+#if defined(Q_OS_WINCE)
+ // As WinCE does not support notifications we send the signal here
+ // We will get no event when the clipboard changes outside...
+ emit dataChanged();
+ emit changed(Clipboard);
+#endif
+}
+
+bool QClipboard::event(QEvent *e)
+{
+ if (e->type() != QEvent::Clipboard)
+ return QObject::event(e);
+
+ QClipboardData *d = clipboardData();
+
+ MSG *m = (MSG *)((QClipboardEvent*)e)->data();
+ if (!m) {
+ // this is sent to render all formats at app shut down
+ if (ownsClipboard()) {
+ OleFlushClipboard();
+ d->releaseIData();
+ }
+ return true;
+ }
+
+ bool propagate = false;
+
+ if (m->message == WM_CHANGECBCHAIN) {
+ if ((HWND)m->wParam == d->nextClipboardViewer)
+ d->nextClipboardViewer = (HWND)m->lParam;
+ else
+ propagate = true;
+ } else if (m->message == WM_DRAWCLIPBOARD) {
+ emitChanged(QClipboard::Clipboard);
+ if (!ownsClipboard() && d->iData)
+ // clean up the clipboard object if we no longer own the clipboard
+ d->releaseIData();
+ propagate = true;
+ }
+ if (propagate && d->nextClipboardViewer) {
+ if (ptrIsHungAppWindow == 0) {
+ QSystemLibrary library(QLatin1String("User32"));
+ ptrIsHungAppWindow = (PtrIsHungAppWindow)library.resolve("IsHungAppWindow");
+ }
+ if (ptrIsHungAppWindow && ptrIsHungAppWindow(d->nextClipboardViewer)) {
+ qWarning("%s: Cowardly refusing to send clipboard message to hung application...", Q_FUNC_INFO);
+ } else {
+ SendMessage(d->nextClipboardViewer, m->message, m->wParam, m->lParam);
+ }
+ }
+
+ return true;
+}
+
+void QClipboard::connectNotify(const char *signal)
+{
+ if (qstrcmp(signal,SIGNAL(dataChanged())) == 0) {
+ // ensure we are up and running but block signals so the dataChange signal
+ // is not emitted while being connected to.
+ bool blocked = blockSignals(true);
+ QClipboardData *d = clipboardData();
+ blockSignals(blocked);
+ Q_UNUSED(d);
+ }
+}
+
+const QMimeData *QClipboard::mimeData(Mode mode) const
+{
+ if (mode != Clipboard)
+ return 0;
+
+ QClipboardData *data = clipboardData();
+ // sort cut for local copy / paste
+ if (ownsClipboard() && data->iData->mimeData())
+ return data->iData->mimeData();
+ return &data->watcher;
+}
+
+bool QClipboard::supportsMode(Mode mode) const
+{
+ return (mode == Clipboard);
+}
+
+bool QClipboard::ownsMode(Mode mode) const
+{
+ if (mode == Clipboard) {
+ QClipboardData *d = clipboardData();
+#if !defined(Q_OS_WINCE)
+ return d->iData && OleIsCurrentClipboard(d->iData) == S_OK;
+#else
+ return d->iData && GetClipboardOwner() == d->clipBoardViewer->internalWinId();
+#endif
+ } else {
+ return false;
+ }
+}
+
+void QClipboard::ownerDestroyed()
+{
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_CLIPBOARD
diff --git a/src/widgets/platforms/win/qcolormap_win.cpp b/src/widgets/platforms/win/qcolormap_win.cpp
new file mode 100644
index 0000000000..4a95977438
--- /dev/null
+++ b/src/widgets/platforms/win/qcolormap_win.cpp
@@ -0,0 +1,201 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcolor.h"
+#include "qcolormap.h"
+#include "qvector.h"
+#include "qt_windows.h"
+
+#if defined(Q_WS_WINCE)
+#include "qguifunctions_wince.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QColormapPrivate
+{
+public:
+ inline QColormapPrivate()
+ : ref(1), mode(QColormap::Direct), depth(0), hpal(0)
+ { }
+
+ QAtomicInt ref;
+
+ QColormap::Mode mode;
+ int depth;
+ int numcolors;
+
+ HPALETTE hpal;
+ QVector<QColor> palette;
+};
+
+static QColormapPrivate *screenMap = 0;
+
+void QColormap::initialize()
+{
+ HDC dc = qt_win_display_dc();
+
+ screenMap = new QColormapPrivate;
+ screenMap->depth = GetDeviceCaps(dc, BITSPIXEL);
+
+ screenMap->numcolors = -1;
+ if (GetDeviceCaps(dc, RASTERCAPS) & RC_PALETTE)
+ screenMap->numcolors = GetDeviceCaps(dc, SIZEPALETTE);
+
+ if (screenMap->numcolors <= 16 || screenMap->numcolors > 256) // no need to create palette
+ return;
+
+ LOGPALETTE* pal = 0;
+ int numPalEntries = 6*6*6; // color cube
+
+ pal = (LOGPALETTE*)malloc(sizeof(LOGPALETTE) + numPalEntries * sizeof(PALETTEENTRY));
+ // Make 6x6x6 color cube
+ int idx = 0;
+ for(int ir = 0x0; ir <= 0xff; ir+=0x33) {
+ for(int ig = 0x0; ig <= 0xff; ig+=0x33) {
+ for(int ib = 0x0; ib <= 0xff; ib+=0x33) {
+ pal->palPalEntry[idx].peRed = ir;
+ pal->palPalEntry[idx].peGreen = ig;
+ pal->palPalEntry[idx].peBlue = ib;
+ pal->palPalEntry[idx].peFlags = 0;
+ idx++;
+ }
+ }
+ }
+
+ pal->palVersion = 0x300;
+ pal->palNumEntries = numPalEntries;
+
+ screenMap->hpal = CreatePalette(pal);
+ if (!screenMap->hpal)
+ qErrnoWarning("QColor::initialize: Failed to create logical palette");
+ free (pal);
+
+ SelectPalette(dc, screenMap->hpal, FALSE);
+ RealizePalette(dc);
+
+ PALETTEENTRY paletteEntries[256];
+ screenMap->numcolors = GetPaletteEntries(screenMap->hpal, 0, 255, paletteEntries);
+
+ screenMap->palette.resize(screenMap->numcolors);
+ for (int i = 0; i < screenMap->numcolors; i++) {
+ screenMap->palette[i] = qRgb(paletteEntries[i].peRed,
+ paletteEntries[i].peGreen,
+ paletteEntries[i].peBlue);
+ }
+}
+
+void QColormap::cleanup()
+{
+ if (!screenMap)
+ return;
+
+ if (screenMap->hpal) { // delete application global
+ DeleteObject(screenMap->hpal); // palette
+ screenMap->hpal = 0;
+ }
+
+ delete screenMap;
+ screenMap = 0;
+}
+
+QColormap QColormap::instance(int)
+{
+ Q_ASSERT_X(screenMap, "QColormap",
+ "A QApplication object needs to be constructed before QColormap is used.");
+ return QColormap();
+}
+
+QColormap::QColormap()
+ : d(screenMap)
+{ d->ref.ref(); }
+
+QColormap::QColormap(const QColormap &colormap)
+ :d (colormap.d)
+{ d->ref.ref(); }
+
+QColormap::~QColormap()
+{
+ if (!d->ref.deref())
+ delete d;
+}
+
+QColormap::Mode QColormap::mode() const
+{ return d->mode; }
+
+int QColormap::depth() const
+{ return d->depth; }
+
+int QColormap::size() const
+{ return d->numcolors; }
+
+uint QColormap::pixel(const QColor &color) const
+{
+ const QColor c = color.toRgb();
+ COLORREF rgb = RGB(c.red(), c.green(), c.blue());
+ if (d->hpal)
+ return PALETTEINDEX(GetNearestPaletteIndex(d->hpal, rgb));
+ return rgb;
+}
+
+const QColor QColormap::colorAt(uint pixel) const
+{
+ if (d->hpal) {
+ if (pixel < uint(d->numcolors))
+ return d->palette.at(pixel);
+ return QColor();
+ }
+ return QColor(GetRValue(pixel), GetGValue(pixel), GetBValue(pixel));
+}
+
+
+HPALETTE QColormap::hPal()
+{ return screenMap ? screenMap->hpal : 0; }
+
+
+const QVector<QColor> QColormap::colormap() const
+{ return d->palette; }
+
+QColormap &QColormap::operator=(const QColormap &colormap)
+{ qAtomicAssign(d, colormap.d); return *this; }
+
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/win/qcursor_win.cpp b/src/widgets/platforms/win/qcursor_win.cpp
new file mode 100644
index 0000000000..cef83f5a1b
--- /dev/null
+++ b/src/widgets/platforms/win/qcursor_win.cpp
@@ -0,0 +1,493 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qcursor_p.h>
+#include <qbitmap.h>
+#include <qcursor.h>
+
+#ifndef QT_NO_CURSOR
+
+#include <qimage.h>
+#include <qt_windows.h>
+#include <private/qapplication_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*****************************************************************************
+ Internal QCursorData class
+ *****************************************************************************/
+
+QCursorData::QCursorData(Qt::CursorShape s)
+ : cshape(s), bm(0), bmm(0), hx(0), hy(0), hcurs(0)
+{
+ ref = 1;
+}
+
+QCursorData::~QCursorData()
+{
+ delete bm;
+ delete bmm;
+#if !defined(Q_WS_WINCE) || defined(GWES_ICONCURS)
+ if (hcurs)
+ DestroyCursor(hcurs);
+#endif
+}
+
+
+QCursorData *QCursorData::setBitmap(const QBitmap &bitmap, const QBitmap &mask, int hotX, int hotY)
+{
+ if (!QCursorData::initialized)
+ QCursorData::initialize();
+ if (bitmap.depth() != 1 || mask.depth() != 1 || bitmap.size() != mask.size()) {
+ qWarning("QCursor: Cannot create bitmap cursor; invalid bitmap(s)");
+ QCursorData *c = qt_cursorTable[0];
+ c->ref.ref();
+ return c;
+ }
+ QCursorData *d = new QCursorData;
+ d->bm = new QBitmap(bitmap);
+ d->bmm = new QBitmap(mask);
+ d->hcurs = 0;
+ d->cshape = Qt::BitmapCursor;
+ d->hx = hotX >= 0 ? hotX : bitmap.width()/2;
+ d->hy = hotY >= 0 ? hotY : bitmap.height()/2;
+ return d;
+}
+
+HCURSOR QCursor::handle() const
+{
+ if (!QCursorData::initialized)
+ QCursorData::initialize();
+ if (!d->hcurs)
+ d->update();
+ return d->hcurs;
+}
+
+QCursor::QCursor(HCURSOR handle)
+{
+ d = new QCursorData(Qt::CustomCursor);
+ d->hcurs = handle;
+}
+
+#endif //QT_NO_CURSOR
+
+QPoint QCursor::pos()
+{
+ POINT p;
+ GetCursorPos(&p);
+ return QPoint(p.x, p.y);
+}
+
+void QCursor::setPos(int x, int y)
+{
+ SetCursorPos(x, y);
+}
+
+#ifndef QT_NO_CURSOR
+
+extern HBITMAP qt_createIconMask(const QBitmap &bitmap);
+
+static HCURSOR create32BitCursor(const QPixmap &pixmap, int hx, int hy)
+{
+ HCURSOR cur = 0;
+#if !defined(Q_WS_WINCE)
+ QBitmap mask = pixmap.mask();
+ if (mask.isNull()) {
+ mask = QBitmap(pixmap.size());
+ mask.fill(Qt::color1);
+ }
+
+ HBITMAP ic = pixmap.toWinHBITMAP(QPixmap::Alpha);
+ HBITMAP im = qt_createIconMask(mask);
+
+ ICONINFO ii;
+ ii.fIcon = 0;
+ ii.xHotspot = hx;
+ ii.yHotspot = hy;
+ ii.hbmMask = im;
+ ii.hbmColor = ic;
+
+ cur = CreateIconIndirect(&ii);
+
+ DeleteObject(ic);
+ DeleteObject(im);
+#elif defined(GWES_ICONCURS)
+ QImage bbits, mbits;
+ bool invb, invm;
+ bbits = pixmap.toImage().convertToFormat(QImage::Format_Mono);
+ mbits = pixmap.toImage().convertToFormat(QImage::Format_Mono);
+ invb = bbits.colorCount() > 1 && qGray(bbits.color(0)) < qGray(bbits.color(1));
+ invm = mbits.colorCount() > 1 && qGray(mbits.color(0)) < qGray(mbits.color(1));
+
+ int sysW = GetSystemMetrics(SM_CXCURSOR);
+ int sysH = GetSystemMetrics(SM_CYCURSOR);
+ int sysN = qMax(1, sysW / 8);
+ int n = qMax(1, bbits.width() / 8);
+ int h = bbits.height();
+
+ uchar* xBits = new uchar[sysH * sysN];
+ uchar* xMask = new uchar[sysH * sysN];
+ int x = 0;
+ for (int i = 0; i < sysH; ++i) {
+ if (i >= h) {
+ memset(&xBits[x] , 255, sysN);
+ memset(&xMask[x] , 0, sysN);
+ x += sysN;
+ } else {
+ int fillWidth = n > sysN ? sysN : n;
+ uchar *bits = bbits.scanLine(i);
+ uchar *mask = mbits.scanLine(i);
+ for (int j = 0; j < fillWidth; ++j) {
+ uchar b = bits[j];
+ uchar m = mask[j];
+ if (invb)
+ b ^= 0xFF;
+ if (invm)
+ m ^= 0xFF;
+ xBits[x] = ~m;
+ xMask[x] = b ^ m;
+ ++x;
+ }
+ for (int j = fillWidth; j < sysN; ++j ) {
+ xBits[x] = 255;
+ xMask[x] = 0;
+ ++x;
+ }
+ }
+ }
+
+ cur = CreateCursor(qWinAppInst(), hx, hy, sysW, sysH,
+ xBits, xMask);
+#else
+ Q_UNUSED(pixmap);
+ Q_UNUSED(hx);
+ Q_UNUSED(hy);
+#endif
+ return cur;
+}
+
+void QCursorData::update()
+{
+ if (!QCursorData::initialized)
+ QCursorData::initialize();
+ if (hcurs)
+ return;
+
+ if (cshape == Qt::BitmapCursor && !pixmap.isNull()) {
+ hcurs = create32BitCursor(pixmap, hx, hy);
+ if (hcurs)
+ return;
+ }
+
+
+ // Non-standard Windows cursors are created from bitmaps
+
+ static const uchar vsplit_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00,
+ 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ static const uchar vsplitm_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xf0, 0x07, 0x00,
+ 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00,
+ 0x00, 0xc0, 0x01, 0x00, 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00,
+ 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00,
+ 0x80, 0xff, 0xff, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00,
+ 0x00, 0xc0, 0x01, 0x00, 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xf0, 0x07, 0x00,
+ 0x00, 0xe0, 0x03, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ static const uchar hsplit_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00,
+ 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00,
+ 0x00, 0x41, 0x82, 0x00, 0x80, 0x41, 0x82, 0x01, 0xc0, 0x7f, 0xfe, 0x03,
+ 0x80, 0x41, 0x82, 0x01, 0x00, 0x41, 0x82, 0x00, 0x00, 0x40, 0x02, 0x00,
+ 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00,
+ 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ static const uchar hsplitm_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00,
+ 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe2, 0x47, 0x00, 0x00, 0xe3, 0xc7, 0x00,
+ 0x80, 0xe3, 0xc7, 0x01, 0xc0, 0xff, 0xff, 0x03, 0xe0, 0xff, 0xff, 0x07,
+ 0xc0, 0xff, 0xff, 0x03, 0x80, 0xe3, 0xc7, 0x01, 0x00, 0xe3, 0xc7, 0x00,
+ 0x00, 0xe2, 0x47, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00,
+ 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ static const uchar phand_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00,
+ 0x80, 0x04, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00,
+ 0x80, 0x1c, 0x00, 0x00, 0x80, 0xe4, 0x00, 0x00, 0x80, 0x24, 0x03, 0x00,
+ 0x80, 0x24, 0x05, 0x00, 0xb8, 0x24, 0x09, 0x00, 0xc8, 0x00, 0x09, 0x00,
+ 0x88, 0x00, 0x08, 0x00, 0x90, 0x00, 0x08, 0x00, 0xa0, 0x00, 0x08, 0x00,
+ 0x20, 0x00, 0x08, 0x00, 0x40, 0x00, 0x08, 0x00, 0x40, 0x00, 0x04, 0x00,
+ 0x80, 0x00, 0x04, 0x00, 0x80, 0x00, 0x04, 0x00, 0x00, 0x01, 0x02, 0x00,
+ 0x00, 0x01, 0x02, 0x00, 0x00, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ static const uchar phandm_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00,
+ 0x80, 0x07, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00,
+ 0x80, 0x1f, 0x00, 0x00, 0x80, 0xff, 0x00, 0x00, 0x80, 0xff, 0x03, 0x00,
+ 0x80, 0xff, 0x07, 0x00, 0xb8, 0xff, 0x0f, 0x00, 0xf8, 0xff, 0x0f, 0x00,
+ 0xf8, 0xff, 0x0f, 0x00, 0xf0, 0xff, 0x0f, 0x00, 0xe0, 0xff, 0x0f, 0x00,
+ 0xe0, 0xff, 0x0f, 0x00, 0xc0, 0xff, 0x0f, 0x00, 0xc0, 0xff, 0x07, 0x00,
+ 0x80, 0xff, 0x07, 0x00, 0x80, 0xff, 0x07, 0x00, 0x00, 0xff, 0x03, 0x00,
+ 0x00, 0xff, 0x03, 0x00, 0x00, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ static const uchar openhand_bits[] = {
+ 0x80,0x01,0x58,0x0e,0x64,0x12,0x64,0x52,0x48,0xb2,0x48,0x92,
+ 0x16,0x90,0x19,0x80,0x11,0x40,0x02,0x40,0x04,0x40,0x04,0x20,
+ 0x08,0x20,0x10,0x10,0x20,0x10,0x00,0x00};
+ static const uchar openhandm_bits[] = {
+ 0x80,0x01,0xd8,0x0f,0xfc,0x1f,0xfc,0x5f,0xf8,0xff,0xf8,0xff,
+ 0xf6,0xff,0xff,0xff,0xff,0x7f,0xfe,0x7f,0xfc,0x7f,0xfc,0x3f,
+ 0xf8,0x3f,0xf0,0x1f,0xe0,0x1f,0x00,0x00};
+ static const uchar closedhand_bits[] = {
+ 0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x0d,0x48,0x32,0x08,0x50,
+ 0x10,0x40,0x18,0x40,0x04,0x40,0x04,0x20,0x08,0x20,0x10,0x10,
+ 0x20,0x10,0x20,0x10,0x00,0x00,0x00,0x00};
+ static const uchar closedhandm_bits[] = {
+ 0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x0d,0xf8,0x3f,0xf8,0x7f,
+ 0xf0,0x7f,0xf8,0x7f,0xfc,0x7f,0xfc,0x3f,0xf8,0x3f,0xf0,0x1f,
+ 0xe0,0x1f,0xe0,0x1f,0x00,0x00,0x00,0x00};
+
+ static const uchar * const cursor_bits32[] = {
+ vsplit_bits, vsplitm_bits, hsplit_bits, hsplitm_bits,
+ phand_bits, phandm_bits
+ };
+
+ wchar_t *sh = 0;
+ switch (cshape) { // map to windows cursor
+ case Qt::ArrowCursor:
+ sh = IDC_ARROW;
+ break;
+ case Qt::UpArrowCursor:
+ sh = IDC_UPARROW;
+ break;
+ case Qt::CrossCursor:
+ sh = IDC_CROSS;
+ break;
+ case Qt::WaitCursor:
+ sh = IDC_WAIT;
+ break;
+ case Qt::IBeamCursor:
+ sh = IDC_IBEAM;
+ break;
+ case Qt::SizeVerCursor:
+ sh = IDC_SIZENS;
+ break;
+ case Qt::SizeHorCursor:
+ sh = IDC_SIZEWE;
+ break;
+ case Qt::SizeBDiagCursor:
+ sh = IDC_SIZENESW;
+ break;
+ case Qt::SizeFDiagCursor:
+ sh = IDC_SIZENWSE;
+ break;
+ case Qt::SizeAllCursor:
+ sh = IDC_SIZEALL;
+ break;
+ case Qt::ForbiddenCursor:
+ sh = IDC_NO;
+ break;
+ case Qt::WhatsThisCursor:
+ sh = IDC_HELP;
+ break;
+ case Qt::BusyCursor:
+ sh = IDC_APPSTARTING;
+ break;
+ case Qt::PointingHandCursor:
+ sh = IDC_HAND;
+ break;
+ case Qt::BlankCursor:
+ case Qt::SplitVCursor:
+ case Qt::SplitHCursor:
+ case Qt::OpenHandCursor:
+ case Qt::ClosedHandCursor:
+ case Qt::BitmapCursor: {
+ QImage bbits, mbits;
+ bool invb, invm;
+ if (cshape == Qt::BlankCursor) {
+ bbits = QImage(32, 32, QImage::Format_Mono);
+ bbits.fill(0); // ignore color table
+ mbits = bbits.copy();
+ hx = hy = 16;
+ invb = invm = false;
+ } else if (cshape == Qt::OpenHandCursor || cshape == Qt::ClosedHandCursor) {
+ bool open = cshape == Qt::OpenHandCursor;
+ QBitmap cb = QBitmap::fromData(QSize(16, 16), open ? openhand_bits : closedhand_bits);
+ QBitmap cm = QBitmap::fromData(QSize(16, 16), open ? openhandm_bits : closedhandm_bits);
+ bbits = cb.toImage().convertToFormat(QImage::Format_Mono);
+ mbits = cm.toImage().convertToFormat(QImage::Format_Mono);
+ hx = hy = 8;
+ invb = invm = false;
+ } else if (cshape != Qt::BitmapCursor) {
+ int i = cshape - Qt::SplitVCursor;
+ QBitmap cb = QBitmap::fromData(QSize(32, 32), cursor_bits32[i * 2]);
+ QBitmap cm = QBitmap::fromData(QSize(32, 32), cursor_bits32[i * 2 + 1]);
+ bbits = cb.toImage().convertToFormat(QImage::Format_Mono);
+ mbits = cm.toImage().convertToFormat(QImage::Format_Mono);
+ if (cshape == Qt::PointingHandCursor) {
+ hx = 7;
+ hy = 0;
+ } else
+ hx = hy = 16;
+ invb = invm = false;
+ } else {
+ bbits = bm->toImage().convertToFormat(QImage::Format_Mono);
+ mbits = bmm->toImage().convertToFormat(QImage::Format_Mono);
+ invb = bbits.colorCount() > 1 && qGray(bbits.color(0)) < qGray(bbits.color(1));
+ invm = mbits.colorCount() > 1 && qGray(mbits.color(0)) < qGray(mbits.color(1));
+ }
+ int n = qMax(1, bbits.width() / 8);
+ int h = bbits.height();
+#if !defined(Q_WS_WINCE)
+ uchar* xBits = new uchar[h * n];
+ uchar* xMask = new uchar[h * n];
+ int x = 0;
+ for (int i = 0; i < h; ++i) {
+ uchar *bits = bbits.scanLine(i);
+ uchar *mask = mbits.scanLine(i);
+ for (int j = 0; j < n; ++j) {
+ uchar b = bits[j];
+ uchar m = mask[j];
+ if (invb)
+ b ^= 0xff;
+ if (invm)
+ m ^= 0xff;
+ xBits[x] = ~m;
+ xMask[x] = b ^ m;
+ ++x;
+ }
+ }
+ hcurs = CreateCursor(qWinAppInst(), hx, hy, bbits.width(), bbits.height(),
+ xBits, xMask);
+ delete [] xBits;
+ delete [] xMask;
+#elif defined(GWES_ICONCURS) // Q_WS_WINCE
+ // Windows CE only supports fixed cursor size.
+ int sysW = GetSystemMetrics(SM_CXCURSOR);
+ int sysH = GetSystemMetrics(SM_CYCURSOR);
+ int sysN = qMax(1, sysW / 8);
+ uchar* xBits = new uchar[sysH * sysN];
+ uchar* xMask = new uchar[sysH * sysN];
+ int x = 0;
+ for (int i = 0; i < sysH; ++i) {
+ if (i >= h) {
+ memset(&xBits[x] , 255, sysN);
+ memset(&xMask[x] , 0, sysN);
+ x += sysN;
+ } else {
+ int fillWidth = n > sysN ? sysN : n;
+ uchar *bits = bbits.scanLine(i);
+ uchar *mask = mbits.scanLine(i);
+ for (int j = 0; j < fillWidth; ++j) {
+ uchar b = bits[j];
+ uchar m = mask[j];
+ if (invb)
+ b ^= 0xFF;
+ if (invm)
+ m ^= 0xFF;
+ xBits[x] = ~m;
+ xMask[x] = b ^ m;
+ ++x;
+ }
+ for (int j = fillWidth; j < sysN; ++j ) {
+ xBits[x] = 255;
+ xMask[x] = 0;
+ ++x;
+ }
+ }
+ }
+
+ hcurs = CreateCursor(qWinAppInst(), hx, hy, sysW, sysH,
+ xBits, xMask);
+ delete [] xBits;
+ delete [] xMask;
+#else
+ Q_UNUSED(n);
+ Q_UNUSED(h);
+#endif
+ return;
+ }
+ case Qt::DragCopyCursor:
+ case Qt::DragMoveCursor:
+ case Qt::DragLinkCursor: {
+ QPixmap pixmap = QApplicationPrivate::instance()->getPixmapCursor(cshape);
+ hcurs = create32BitCursor(pixmap, hx, hy);
+ }
+ break;
+ default:
+ qWarning("QCursor::update: Invalid cursor shape %d", cshape);
+ return;
+ }
+#ifdef Q_WS_WINCE
+ hcurs = LoadCursor(0, sh);
+#else
+ hcurs = (HCURSOR)LoadImage(0, sh, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
+#endif
+}
+
+QT_END_NAMESPACE
+#endif // QT_NO_CURSOR
diff --git a/src/widgets/platforms/win/qdesktopwidget_win.cpp b/src/widgets/platforms/win/qdesktopwidget_win.cpp
new file mode 100644
index 0000000000..f77cc1f847
--- /dev/null
+++ b/src/widgets/platforms/win/qdesktopwidget_win.cpp
@@ -0,0 +1,387 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdesktopwidget.h"
+#include "qt_windows.h"
+#include "qapplication_p.h"
+#include <private/qsystemlibrary_p.h>
+#include <qvector.h>
+#include <limits.h>
+#ifdef Q_WS_WINCE
+#include <sipapi.h>
+#endif
+#include "qwidget_p.h"
+#include "qdebug.h"
+
+QT_BEGIN_NAMESPACE
+
+class QDesktopWidgetPrivate : public QWidgetPrivate
+{
+public:
+ QDesktopWidgetPrivate();
+ ~QDesktopWidgetPrivate();
+
+ static void init(QDesktopWidget *that);
+ static void cleanup();
+ static int screenCount;
+ static int primaryScreen;
+
+ static QVector<QRect> *rects;
+ static QVector<QRect> *workrects;
+
+ struct MONITORINFO
+ {
+ DWORD cbSize;
+ RECT rcMonitor;
+ RECT rcWork;
+ DWORD dwFlags;
+ };
+
+ typedef BOOL (WINAPI *InfoFunc)(HMONITOR, MONITORINFO*);
+ typedef BOOL (QT_WIN_CALLBACK *EnumProc)(HMONITOR, HDC, LPRECT, LPARAM);
+ typedef BOOL (WINAPI *EnumFunc)(HDC, LPCRECT, EnumProc, LPARAM);
+
+ static EnumFunc enumDisplayMonitors;
+ static InfoFunc getMonitorInfo;
+ static int refcount;
+};
+
+int QDesktopWidgetPrivate::screenCount = 1;
+int QDesktopWidgetPrivate::primaryScreen = 0;
+QDesktopWidgetPrivate::EnumFunc QDesktopWidgetPrivate::enumDisplayMonitors = 0;
+QDesktopWidgetPrivate::InfoFunc QDesktopWidgetPrivate::getMonitorInfo = 0;
+QVector<QRect> *QDesktopWidgetPrivate::rects = 0;
+QVector<QRect> *QDesktopWidgetPrivate::workrects = 0;
+static int screen_number = 0;
+int QDesktopWidgetPrivate::refcount = 0;
+#ifdef Q_WS_WINCE_WM
+// Use SIP information, if available
+// SipGetInfo is not supported by SSDK (no definition!).
+static inline void qt_get_sip_info(QRect &rect)
+{
+ SIPINFO sip;
+ memset(&sip, 0, sizeof(SIPINFO));
+ sip.cbSize = sizeof(SIPINFO);
+ if (SipGetInfo(&sip))
+ rect = QRect(QPoint(sip.rcVisibleDesktop.left, sip.rcVisibleDesktop.top),
+ QPoint(sip.rcVisibleDesktop.right - 1, sip.rcVisibleDesktop.bottom - 1));
+}
+#endif
+
+
+BOOL QT_WIN_CALLBACK enumCallback(HMONITOR hMonitor, HDC, LPRECT, LPARAM)
+{
+ QDesktopWidgetPrivate::screenCount++;
+ QDesktopWidgetPrivate::rects->resize(QDesktopWidgetPrivate::screenCount);
+ QDesktopWidgetPrivate::workrects->resize(QDesktopWidgetPrivate::screenCount);
+ // Get the MONITORINFO block
+ QDesktopWidgetPrivate::MONITORINFO info;
+ memset(&info, 0, sizeof(QDesktopWidgetPrivate::MONITORINFO));
+ info.cbSize = sizeof(QDesktopWidgetPrivate::MONITORINFO);
+ BOOL res = QDesktopWidgetPrivate::getMonitorInfo(hMonitor, &info);
+ if (!res) {
+ (*QDesktopWidgetPrivate::rects)[screen_number] = QRect();
+ (*QDesktopWidgetPrivate::workrects)[screen_number] = QRect();
+ return true;
+ }
+
+ // Fill list of rects
+ RECT r = info.rcMonitor;
+ QRect qr(QPoint(r.left, r.top), QPoint(r.right - 1, r.bottom - 1));
+ (*QDesktopWidgetPrivate::rects)[screen_number] = qr;
+
+ r = info.rcWork;
+ qr = QRect(QPoint(r.left, r.top), QPoint(r.right - 1, r.bottom - 1));
+ (*QDesktopWidgetPrivate::workrects)[screen_number] = qr;
+
+ if (info.dwFlags & 0x00000001) //MONITORINFOF_PRIMARY
+ QDesktopWidgetPrivate::primaryScreen = screen_number;
+
+ ++screen_number;
+ // Stop the enumeration if we have them all
+ return true;
+}
+
+QDesktopWidgetPrivate::QDesktopWidgetPrivate()
+{
+ ++refcount;
+}
+
+void QDesktopWidgetPrivate::init(QDesktopWidget *that)
+{
+ if (rects)
+ return;
+
+ rects = new QVector<QRect>();
+ workrects = new QVector<QRect>();
+ screenCount = 0;
+
+#ifndef Q_OS_WINCE
+ QSystemLibrary user32Lib(QLatin1String("user32"));
+ enumDisplayMonitors = (EnumFunc)user32Lib.resolve("EnumDisplayMonitors");
+ getMonitorInfo = (InfoFunc)user32Lib.resolve("GetMonitorInfoW");
+
+ if (!enumDisplayMonitors || !getMonitorInfo) {
+ screenCount = GetSystemMetrics(80); // SM_CMONITORS
+ rects->resize(screenCount);
+ for (int i = 0; i < screenCount; ++i)
+ rects->replace(i, that->rect());
+ return;
+ }
+ // Calls enumCallback
+ enumDisplayMonitors(0, 0, enumCallback, 0);
+ enumDisplayMonitors = 0;
+ getMonitorInfo = 0;
+#else
+ QSystemLibrary coreLib(QLatin1String("coredll"));
+ // CE >= 4.0 case
+ enumDisplayMonitors = (EnumFunc)coreLib.resolve("EnumDisplayMonitors");
+ getMonitorInfo = (InfoFunc)coreLib.resolve("GetMonitorInfo");
+
+ if ((!enumDisplayMonitors || !getMonitorInfo)) {
+ screenCount = GetSystemMetrics(SM_CMONITORS);
+ return;
+ }
+
+ if (!coreLib.isLoaded() || !enumDisplayMonitors || !getMonitorInfo) {
+ rects->resize(screenCount);
+ for (int i = 0; i < screenCount; ++i)
+ (*rects)[i] = that->rect();
+
+ RECT r;
+ SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
+ QRect qr = QRect(QPoint(r.left, r.top), QPoint(r.right - 1, r.bottom - 1));
+
+#if defined(Q_WS_WINCE_WM)
+ qt_get_sip_info(qr);
+#endif
+
+ workrects->resize(screenCount);
+ for (int j = 0; j < screenCount; ++j)
+ (*workrects)[j] = qr;
+ return;
+ }
+
+ // Calls enumCallback
+ enumDisplayMonitors(0, 0, enumCallback, 0);
+ enumDisplayMonitors = 0;
+ getMonitorInfo = 0;
+#endif // Q_WS_WINCE
+}
+
+QDesktopWidgetPrivate::~QDesktopWidgetPrivate()
+{
+ if (!--refcount)
+ cleanup();
+}
+
+void QDesktopWidgetPrivate::cleanup()
+{
+ screen_number = 0;
+ screenCount = 1;
+ primaryScreen = 0;
+ enumDisplayMonitors = 0;
+ getMonitorInfo = 0;
+ delete rects;
+ rects = 0;
+ delete workrects;
+ workrects = 0;
+}
+
+/*
+ \omit
+ Function is commented out in header
+ \fn void *QDesktopWidget::handle(int screen) const
+
+ Returns the window system handle of the display device with the
+ index \a screen, for low-level access. Using this function is not
+ portable.
+
+ The return type varies with platform; see qwindowdefs.h for details.
+
+ \sa x11Display(), QPaintDevice::handle()
+ \endomit
+*/
+
+QDesktopWidget::QDesktopWidget()
+ : QWidget(*new QDesktopWidgetPrivate, 0, Qt::Desktop)
+{
+ setObjectName(QLatin1String("desktop"));
+ QDesktopWidgetPrivate::init(this);
+}
+
+QDesktopWidget::~QDesktopWidget()
+{
+}
+
+bool QDesktopWidget::isVirtualDesktop() const
+{
+ return true;
+}
+
+int QDesktopWidget::primaryScreen() const
+{
+ return d_func()->primaryScreen;
+}
+
+int QDesktopWidget::numScreens() const
+{
+ return d_func()->screenCount;
+}
+
+QWidget *QDesktopWidget::screen(int /* screen */)
+{
+ // It seems that a Qt::WType_Desktop cannot be moved?
+ return this;
+}
+
+//
+// MSVC 7.10 warns that d (the result of the expanded Q_D macro) as a local variable that is not referenced.
+// Therefore, we ignore that warning with the following pragmas
+// I've also tried to eliminate the macro, but to no use...
+// We pop it further down
+#ifdef Q_CC_MSVC
+# pragma warning(push)
+# pragma warning(disable : 4189)
+#endif
+const QRect QDesktopWidget::availableGeometry(int screen) const
+{
+ Q_D(const QDesktopWidget);
+#ifdef Q_WS_WINCE_WM
+ for(int i=0; i < d->workrects->size(); ++i)
+ qt_get_sip_info((*d->workrects)[i]);
+#endif
+ if (screen < 0 || screen >= d->screenCount)
+ screen = d->primaryScreen;
+
+ return d->workrects->at(screen);
+}
+
+const QRect QDesktopWidget::screenGeometry(int screen) const
+{
+ const QDesktopWidgetPrivate *d = d_func();
+ if (screen < 0 || screen >= d->screenCount)
+ screen = d->primaryScreen;
+
+ return d->rects->at(screen);
+}
+
+int QDesktopWidget::screenNumber(const QWidget *widget) const
+{
+ Q_D(const QDesktopWidget);
+ if (!widget)
+ return d->primaryScreen;
+
+ QRect frame = widget->frameGeometry();
+ if (!widget->isWindow())
+ frame.moveTopLeft(widget->mapToGlobal(QPoint(0,0)));
+
+ int maxSize = -1;
+ int maxScreen = -1;
+
+ for (int i = 0; i < d->screenCount; ++i) {
+ QRect sect = d->rects->at(i).intersected(frame);
+ int size = sect.width() * sect.height();
+ if (size > maxSize && sect.width() > 0 && sect.height() > 0) {
+ maxSize = size;
+ maxScreen = i;
+ }
+ }
+
+ return maxScreen;
+}
+
+int QDesktopWidget::screenNumber(const QPoint &point) const
+{
+ Q_D(const QDesktopWidget);
+
+ int closestScreen = -1;
+ int shortestDistance = INT_MAX;
+
+ for (int i = 0; i < d->screenCount; ++i) {
+ int thisDistance = d->pointToRect(point, d->rects->at(i));
+ if (thisDistance < shortestDistance) {
+ shortestDistance = thisDistance;
+ closestScreen = i;
+ }
+ }
+
+ return closestScreen;
+}
+
+void QDesktopWidget::resizeEvent(QResizeEvent *)
+{
+ Q_D(QDesktopWidget);
+ const QVector<QRect> oldrects(*d->rects);
+ const QVector<QRect> oldworkrects(*d->workrects);
+ int oldscreencount = d->screenCount;
+
+ QDesktopWidgetPrivate::cleanup();
+ QDesktopWidgetPrivate::init(this);
+#ifdef Q_WS_WINCE_WM
+ for(int i=0; i < d->workrects->size(); ++i)
+ qt_get_sip_info((*d->workrects)[i]);
+#endif
+
+ for (int i = 0; i < qMin(oldscreencount, d->screenCount); ++i) {
+ const QRect oldrect = oldrects[i];
+ const QRect newrect = d->rects->at(i);
+ if (oldrect != newrect)
+ emit resized(i);
+ }
+
+ for (int j = 0; j < qMin(oldscreencount, d->screenCount); ++j) {
+ const QRect oldrect = oldworkrects[j];
+ const QRect newrect = d->workrects->at(j);
+ if (oldrect != newrect)
+ emit workAreaResized(j);
+ }
+
+ if (oldscreencount != d->screenCount) {
+ emit screenCountChanged(d->screenCount);
+ }
+}
+
+#ifdef Q_CC_MSVC
+# pragma warning(pop)
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/win/qdnd_win.cpp b/src/widgets/platforms/win/qdnd_win.cpp
new file mode 100644
index 0000000000..073937fc45
--- /dev/null
+++ b/src/widgets/platforms/win/qdnd_win.cpp
@@ -0,0 +1,1027 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qapplication.h"
+
+#include "qapplication_p.h"
+#include "qevent.h"
+#include "qpainter.h"
+#include "qwidget.h"
+#include "qbuffer.h"
+#include "qdatastream.h"
+#include "qcursor.h"
+#include "qt_windows.h"
+#include <shlobj.h>
+#ifndef QT_NO_ACCESSIBILITY
+#include "qaccessible.h"
+#endif
+#include "qdnd_p.h"
+#include "qdebug.h"
+
+#if defined(Q_OS_WINCE)
+#include "qguifunctions_wince.h"
+#endif
+
+// support for xbuttons
+#ifndef MK_XBUTTON1
+#define MK_XBUTTON1 0x0020
+#define MK_XBUTTON2 0x0040
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#if !(defined(QT_NO_DRAGANDDROP) && defined(QT_NO_CLIPBOARD))
+
+//---------------------------------------------------------------------
+// QOleDataObject Constructor
+//---------------------------------------------------------------------
+
+QOleDataObject::QOleDataObject(QMimeData *mimeData)
+{
+ m_refs = 1;
+ data = mimeData;
+ CF_PERFORMEDDROPEFFECT = RegisterClipboardFormat(CFSTR_PERFORMEDDROPEFFECT);
+ performedEffect = DROPEFFECT_NONE;
+}
+
+QOleDataObject::~QOleDataObject()
+{
+}
+
+void QOleDataObject::releaseQt()
+{
+ data = 0;
+}
+
+const QMimeData *QOleDataObject::mimeData() const
+{
+ return data;
+}
+
+DWORD QOleDataObject::reportedPerformedEffect() const
+{
+ return performedEffect;
+}
+
+//---------------------------------------------------------------------
+// IUnknown Methods
+//---------------------------------------------------------------------
+
+STDMETHODIMP
+QOleDataObject::QueryInterface(REFIID iid, void FAR* FAR* ppv)
+{
+ if (iid == IID_IUnknown || iid == IID_IDataObject) {
+ *ppv = this;
+ AddRef();
+ return NOERROR;
+ }
+ *ppv = NULL;
+ return ResultFromScode(E_NOINTERFACE);
+}
+
+STDMETHODIMP_(ULONG)
+QOleDataObject::AddRef(void)
+{
+ return ++m_refs;
+}
+
+STDMETHODIMP_(ULONG)
+QOleDataObject::Release(void)
+{
+ if (--m_refs == 0) {
+ releaseQt();
+ delete this;
+ return 0;
+ }
+ return m_refs;
+}
+
+//---------------------------------------------------------------------
+// IDataObject Methods
+//
+// The following methods are NOT supported for data transfer using the
+// clipboard or drag-drop:
+//
+// IDataObject::SetData -- return E_NOTIMPL
+// IDataObject::DAdvise -- return OLE_E_ADVISENOTSUPPORTED
+// ::DUnadvise
+// ::EnumDAdvise
+// IDataObject::GetCanonicalFormatEtc -- return E_NOTIMPL
+// (NOTE: must set pformatetcOut->ptd = NULL)
+//---------------------------------------------------------------------
+
+STDMETHODIMP
+QOleDataObject::GetData(LPFORMATETC pformatetc, LPSTGMEDIUM pmedium)
+{
+#ifdef QDND_DEBUG
+ qDebug("QOleDataObject::GetData(LPFORMATETC pformatetc, LPSTGMEDIUM pmedium)");
+#ifndef Q_OS_WINCE
+ wchar_t buf[256] = {0};
+ GetClipboardFormatName(pformatetc->cfFormat, buf, 255);
+ qDebug("CF = %d : %s", pformatetc->cfFormat, QString::fromWCharArray(buf));
+#endif
+#endif
+
+ if (!data)
+ return ResultFromScode(DATA_E_FORMATETC);
+
+ QWindowsMime *converter = QWindowsMime::converterFromMime(*pformatetc, data);
+
+ if (converter && converter->convertFromMime(*pformatetc, data, pmedium))
+ return ResultFromScode(S_OK);
+ else
+ return ResultFromScode(DATA_E_FORMATETC);
+}
+
+STDMETHODIMP
+QOleDataObject::GetDataHere(LPFORMATETC, LPSTGMEDIUM)
+{
+ return ResultFromScode(DATA_E_FORMATETC);
+}
+
+STDMETHODIMP
+QOleDataObject::QueryGetData(LPFORMATETC pformatetc)
+{
+#ifdef QDND_DEBUG
+ qDebug("QOleDataObject::QueryGetData(LPFORMATETC pformatetc)");
+#endif
+
+ if (!data)
+ return ResultFromScode(DATA_E_FORMATETC);
+
+ if (QWindowsMime::converterFromMime(*pformatetc, data))
+ return ResultFromScode(S_OK);
+ return ResultFromScode(S_FALSE);
+}
+
+STDMETHODIMP
+QOleDataObject::GetCanonicalFormatEtc(LPFORMATETC, LPFORMATETC pformatetcOut)
+{
+ pformatetcOut->ptd = NULL;
+ return ResultFromScode(E_NOTIMPL);
+}
+
+STDMETHODIMP
+QOleDataObject::SetData(LPFORMATETC pFormatetc, STGMEDIUM *pMedium, BOOL fRelease)
+{
+ if (pFormatetc->cfFormat == CF_PERFORMEDDROPEFFECT && pMedium->tymed == TYMED_HGLOBAL) {
+ DWORD * val = (DWORD*)GlobalLock(pMedium->hGlobal);
+ performedEffect = *val;
+ GlobalUnlock(pMedium->hGlobal);
+ if (fRelease)
+ ReleaseStgMedium(pMedium);
+ return ResultFromScode(S_OK);
+ }
+ return ResultFromScode(E_NOTIMPL);
+}
+
+
+STDMETHODIMP
+QOleDataObject::EnumFormatEtc(DWORD dwDirection, LPENUMFORMATETC FAR* ppenumFormatEtc)
+{
+#ifdef QDND_DEBUG
+ qDebug("QOleDataObject::EnumFormatEtc(DWORD dwDirection, LPENUMFORMATETC FAR* ppenumFormatEtc)");
+#endif
+
+ if (!data)
+ return ResultFromScode(DATA_E_FORMATETC);
+
+ SCODE sc = S_OK;
+
+ QVector<FORMATETC> fmtetcs;
+ if (dwDirection == DATADIR_GET) {
+ fmtetcs = QWindowsMime::allFormatsForMime(data);
+ } else {
+ FORMATETC formatetc;
+ formatetc.cfFormat = CF_PERFORMEDDROPEFFECT;
+ formatetc.dwAspect = DVASPECT_CONTENT;
+ formatetc.lindex = -1;
+ formatetc.ptd = NULL;
+ formatetc.tymed = TYMED_HGLOBAL;
+ fmtetcs.append(formatetc);
+ }
+
+ QOleEnumFmtEtc *enumFmtEtc = new QOleEnumFmtEtc(fmtetcs);
+ *ppenumFormatEtc = enumFmtEtc;
+ if (enumFmtEtc->isNull()) {
+ delete enumFmtEtc;
+ *ppenumFormatEtc = NULL;
+ sc = E_OUTOFMEMORY;
+ }
+
+ return ResultFromScode(sc);
+}
+
+STDMETHODIMP
+QOleDataObject::DAdvise(FORMATETC FAR*, DWORD,
+ LPADVISESINK, DWORD FAR*)
+{
+ return ResultFromScode(OLE_E_ADVISENOTSUPPORTED);
+}
+
+
+STDMETHODIMP
+QOleDataObject::DUnadvise(DWORD)
+{
+ return ResultFromScode(OLE_E_ADVISENOTSUPPORTED);
+}
+
+STDMETHODIMP
+QOleDataObject::EnumDAdvise(LPENUMSTATDATA FAR*)
+{
+ return ResultFromScode(OLE_E_ADVISENOTSUPPORTED);
+}
+
+#endif // QT_NO_DRAGANDDROP && QT_NO_CLIPBOARD
+
+#ifndef QT_NO_DRAGANDDROP
+
+//#define QDND_DEBUG
+
+#ifdef QDND_DEBUG
+extern QString dragActionsToString(Qt::DropActions actions);
+#endif
+
+Qt::DropActions translateToQDragDropActions(DWORD pdwEffects)
+{
+ Qt::DropActions actions = Qt::IgnoreAction;
+ if (pdwEffects & DROPEFFECT_LINK)
+ actions |= Qt::LinkAction;
+ if (pdwEffects & DROPEFFECT_COPY)
+ actions |= Qt::CopyAction;
+ if (pdwEffects & DROPEFFECT_MOVE)
+ actions |= Qt::MoveAction;
+ return actions;
+}
+
+Qt::DropAction translateToQDragDropAction(DWORD pdwEffect)
+{
+ if (pdwEffect & DROPEFFECT_LINK)
+ return Qt::LinkAction;
+ if (pdwEffect & DROPEFFECT_COPY)
+ return Qt::CopyAction;
+ if (pdwEffect & DROPEFFECT_MOVE)
+ return Qt::MoveAction;
+ return Qt::IgnoreAction;
+}
+
+DWORD translateToWinDragEffects(Qt::DropActions action)
+{
+ DWORD effect = DROPEFFECT_NONE;
+ if (action & Qt::LinkAction)
+ effect |= DROPEFFECT_LINK;
+ if (action & Qt::CopyAction)
+ effect |= DROPEFFECT_COPY;
+ if (action & Qt::MoveAction)
+ effect |= DROPEFFECT_MOVE;
+ return effect;
+}
+
+Qt::KeyboardModifiers toQtKeyboardModifiers(DWORD keyState)
+{
+ Qt::KeyboardModifiers modifiers = Qt::NoModifier;
+
+ if (keyState & MK_SHIFT)
+ modifiers |= Qt::ShiftModifier;
+ if (keyState & MK_CONTROL)
+ modifiers |= Qt::ControlModifier;
+ if (keyState & MK_ALT)
+ modifiers |= Qt::AltModifier;
+
+ return modifiers;
+}
+
+Qt::MouseButtons toQtMouseButtons(DWORD keyState)
+{
+ Qt::MouseButtons buttons = Qt::NoButton;
+
+ if (keyState & MK_LBUTTON)
+ buttons |= Qt::LeftButton;
+ if (keyState & MK_RBUTTON)
+ buttons |= Qt::RightButton;
+ if (keyState & MK_MBUTTON)
+ buttons |= Qt::MidButton;
+
+ return buttons;
+}
+
+class QOleDropSource : public IDropSource
+{
+public:
+ QOleDropSource();
+ virtual ~QOleDropSource();
+
+ void createCursors();
+
+ // IUnknown methods
+ STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObj);
+ STDMETHOD_(ULONG,AddRef)(void);
+ STDMETHOD_(ULONG,Release)(void);
+
+ // IDropSource methods
+ STDMETHOD(QueryContinueDrag)(BOOL fEscapePressed, DWORD grfKeyState);
+ STDMETHOD(GiveFeedback)(DWORD dwEffect);
+
+private:
+ Qt::MouseButtons currentButtons;
+ Qt::DropAction currentAction;
+ QMap <Qt::DropAction, QCursor> cursors;
+
+ ULONG m_refs;
+};
+
+
+QOleDropSource::QOleDropSource()
+{
+ currentButtons = Qt::NoButton;
+ m_refs = 1;
+ currentAction = Qt::IgnoreAction;
+}
+
+QOleDropSource::~QOleDropSource()
+{
+}
+
+void QOleDropSource::createCursors()
+{
+ QDragManager *manager = QDragManager::self();
+ if (manager && manager->object
+ && (!manager->object->pixmap().isNull()
+ || manager->hasCustomDragCursors())) {
+ QPixmap pm = manager->object->pixmap();
+ QList<Qt::DropAction> actions;
+ actions << Qt::MoveAction << Qt::CopyAction << Qt::LinkAction;
+ if (!manager->object->pixmap().isNull())
+ actions << Qt::IgnoreAction;
+ QPoint hotSpot = manager->object->hotSpot();
+ for (int cnum = 0; cnum < actions.size(); ++cnum) {
+ QPixmap cpm = manager->dragCursor(actions.at(cnum));
+ int w = cpm.width();
+ int h = cpm.height();
+
+ if (!pm.isNull()) {
+ int x1 = qMin(-hotSpot.x(), 0);
+ int x2 = qMax(pm.width() - hotSpot.x(), cpm.width());
+ int y1 = qMin(-hotSpot.y(), 0);
+ int y2 = qMax(pm.height() - hotSpot.y(), cpm.height());
+
+ w = x2 - x1 + 1;
+ h = y2 - y1 + 1;
+ }
+
+ QRect srcRect = pm.rect();
+ QPoint pmDest = QPoint(qMax(0, -hotSpot.x()), qMax(0, -hotSpot.y()));
+ QPoint newHotSpot = hotSpot;
+
+#if defined(Q_OS_WINCE)
+ // Limited cursor size
+ int reqw = GetSystemMetrics(SM_CXCURSOR);
+ int reqh = GetSystemMetrics(SM_CYCURSOR);
+
+ QPoint hotspotInPM = newHotSpot - pmDest;
+ if (reqw < w) {
+ // Not wide enough - move objectpm right
+ qreal r = qreal(newHotSpot.x()) / w;
+ newHotSpot = QPoint(int(r * reqw), newHotSpot.y());
+ if (newHotSpot.x() + cpm.width() > reqw)
+ newHotSpot.setX(reqw - cpm.width());
+
+ srcRect = QRect(QPoint(hotspotInPM.x() - newHotSpot.x(), srcRect.top()), QSize(reqw, srcRect.height()));
+ }
+ if (reqh < h) {
+ qreal r = qreal(newHotSpot.y()) / h;
+ newHotSpot = QPoint(newHotSpot.x(), int(r * reqh));
+ if (newHotSpot.y() + cpm.height() > reqh)
+ newHotSpot.setY(reqh - cpm.height());
+
+ srcRect = QRect(QPoint(srcRect.left(), hotspotInPM.y() - newHotSpot.y()), QSize(srcRect.width(), reqh));
+ }
+ // Always use system cursor size
+ w = reqw;
+ h = reqh;
+#endif
+
+ QPixmap newCursor(w, h);
+ if (!pm.isNull()) {
+ newCursor.fill(QColor(0, 0, 0, 0));
+ QPainter p(&newCursor);
+ p.drawPixmap(pmDest, pm, srcRect);
+ p.drawPixmap(qMax(0,newHotSpot.x()),qMax(0,newHotSpot.y()),cpm);
+ } else {
+ newCursor = cpm;
+ }
+
+#ifndef QT_NO_CURSOR
+ cursors[actions.at(cnum)] = QCursor(newCursor, pm.isNull() ? 0 : qMax(0,newHotSpot.x()),
+ pm.isNull() ? 0 : qMax(0,newHotSpot.y()));
+#endif
+ }
+ }
+}
+
+
+
+//---------------------------------------------------------------------
+// IUnknown Methods
+//---------------------------------------------------------------------
+
+
+STDMETHODIMP
+QOleDropSource::QueryInterface(REFIID iid, void FAR* FAR* ppv)
+{
+ if(iid == IID_IUnknown || iid == IID_IDropSource)
+ {
+ *ppv = this;
+ ++m_refs;
+ return NOERROR;
+ }
+ *ppv = NULL;
+ return ResultFromScode(E_NOINTERFACE);
+}
+
+
+STDMETHODIMP_(ULONG)
+QOleDropSource::AddRef(void)
+{
+ return ++m_refs;
+}
+
+
+STDMETHODIMP_(ULONG)
+QOleDropSource::Release(void)
+{
+ if(--m_refs == 0)
+ {
+ delete this;
+ return 0;
+ }
+ return m_refs;
+}
+
+static inline Qt::MouseButtons keystate_to_mousebutton(DWORD grfKeyState)
+{
+ Qt::MouseButtons result;
+ if (grfKeyState & MK_LBUTTON)
+ result |= Qt::LeftButton;
+ if (grfKeyState & MK_MBUTTON)
+ result |= Qt::MidButton;
+ if (grfKeyState & MK_RBUTTON)
+ result |= Qt::RightButton;
+ if (grfKeyState & MK_XBUTTON1)
+ result |= Qt::XButton1;
+ if (grfKeyState & MK_XBUTTON2)
+ result |= Qt::XButton2;
+ return result;
+}
+
+//---------------------------------------------------------------------
+// IDropSource Methods
+//---------------------------------------------------------------------
+QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP
+QOleDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState)
+{
+#ifdef QDND_DEBUG
+ qDebug("QOleDropSource::QueryContinueDrag(fEscapePressed %d, grfKeyState %d)", fEscapePressed, grfKeyState);
+#endif
+
+ if (fEscapePressed) {
+ return ResultFromScode(DRAGDROP_S_CANCEL);
+ } else if ((GetAsyncKeyState(VK_LBUTTON) == 0)
+ && (GetAsyncKeyState(VK_MBUTTON) == 0)
+ && (GetAsyncKeyState(VK_RBUTTON) == 0)) {
+ // grfKeyState is broken on CE & some Windows XP versions,
+ // therefore we need to check the state manually
+ return ResultFromScode(DRAGDROP_S_DROP);
+ } else {
+#if !defined(Q_OS_WINCE)
+ if (currentButtons == Qt::NoButton) {
+ currentButtons = keystate_to_mousebutton(grfKeyState);
+ } else {
+ Qt::MouseButtons buttons = keystate_to_mousebutton(grfKeyState);
+ if (!(currentButtons & buttons))
+ return ResultFromScode(DRAGDROP_S_DROP);
+ }
+#endif
+ QApplication::processEvents();
+ return NOERROR;
+ }
+}
+
+QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP
+QOleDropSource::GiveFeedback(DWORD dwEffect)
+{
+ Qt::DropAction action = translateToQDragDropAction(dwEffect);
+
+#ifdef QDND_DEBUG
+ qDebug("QOleDropSource::GiveFeedback(DWORD dwEffect)");
+ qDebug("dwEffect = %s", dragActionsToString(action).toLatin1().data());
+#endif
+
+ if (currentAction != action) {
+ currentAction = action;
+ QDragManager::self()->emitActionChanged(currentAction);
+ }
+
+ if (cursors.contains(currentAction)) {
+#ifndef QT_NO_CURSOR
+ SetCursor(cursors[currentAction].handle());
+#endif
+ return ResultFromScode(S_OK);
+ }
+
+ return ResultFromScode(DRAGDROP_S_USEDEFAULTCURSORS);
+}
+
+//---------------------------------------------------------------------
+// QOleDropTarget
+//---------------------------------------------------------------------
+
+QOleDropTarget::QOleDropTarget(QWidget* w)
+: widget(w)
+{
+ m_refs = 1;
+}
+
+void QOleDropTarget::releaseQt()
+{
+ widget = 0;
+}
+
+//---------------------------------------------------------------------
+// IUnknown Methods
+//---------------------------------------------------------------------
+
+
+STDMETHODIMP
+QOleDropTarget::QueryInterface(REFIID iid, void FAR* FAR* ppv)
+{
+ if(iid == IID_IUnknown || iid == IID_IDropTarget)
+ {
+ *ppv = this;
+ AddRef();
+ return NOERROR;
+ }
+ *ppv = NULL;
+ return ResultFromScode(E_NOINTERFACE);
+}
+
+
+STDMETHODIMP_(ULONG)
+QOleDropTarget::AddRef(void)
+{
+ return ++m_refs;
+}
+
+
+STDMETHODIMP_(ULONG)
+QOleDropTarget::Release(void)
+{
+ if(--m_refs == 0)
+ {
+ delete this;
+ return 0;
+ }
+ return m_refs;
+}
+
+//---------------------------------------------------------------------
+// IDropTarget Methods
+//---------------------------------------------------------------------
+
+QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP
+QOleDropTarget::DragEnter(LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
+{
+#ifdef QDND_DEBUG
+ qDebug("QOleDropTarget::DragEnter(LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)");
+#endif
+
+ if (!QApplicationPrivate::tryModalHelper(widget)) {
+ *pdwEffect = DROPEFFECT_NONE;
+ return NOERROR;
+ }
+
+ QDragManager *manager = QDragManager::self();
+ manager->dropData->currentDataObject = pDataObj;
+ manager->dropData->currentDataObject->AddRef();
+ sendDragEnterEvent(widget, grfKeyState, pt, pdwEffect);
+ *pdwEffect = chosenEffect;
+
+ return NOERROR;
+}
+
+void QOleDropTarget::sendDragEnterEvent(QWidget *dragEnterWidget, DWORD grfKeyState,
+ POINTL pt, LPDWORD pdwEffect)
+{
+ Q_ASSERT(dragEnterWidget);
+ lastPoint = dragEnterWidget->mapFromGlobal(QPoint(pt.x,pt.y));
+ lastKeyState = grfKeyState;
+
+ chosenEffect = DROPEFFECT_NONE;
+ currentWidget = dragEnterWidget;
+
+ QDragManager *manager = QDragManager::self();
+ QMimeData * md = manager->source() ? manager->dragPrivate()->data : manager->dropData;
+ QDragEnterEvent enterEvent(lastPoint, translateToQDragDropActions(*pdwEffect), md,
+ toQtMouseButtons(grfKeyState), toQtKeyboardModifiers(grfKeyState));
+ QApplication::sendEvent(dragEnterWidget, &enterEvent);
+ answerRect = enterEvent.answerRect();
+
+ if (enterEvent.isAccepted()) {
+ chosenEffect = translateToWinDragEffects(enterEvent.dropAction());
+ }
+
+ // Documentation states that a drag move event is sendt immidiatly after
+ // a drag enter event. This will honor widgets overriding dragMoveEvent only:
+ if (enterEvent.isAccepted()) {
+ QDragMoveEvent moveEvent(lastPoint, translateToQDragDropActions(*pdwEffect), md,
+ toQtMouseButtons(grfKeyState), toQtKeyboardModifiers(grfKeyState));
+ answerRect = enterEvent.answerRect();
+ moveEvent.setDropAction(enterEvent.dropAction());
+ moveEvent.accept(); // accept by default, since enter event was accepted.
+
+ QApplication::sendEvent(dragEnterWidget, &moveEvent);
+ if (moveEvent.isAccepted()) {
+ answerRect = moveEvent.answerRect();
+ chosenEffect = translateToWinDragEffects(moveEvent.dropAction());
+ } else {
+ chosenEffect = DROPEFFECT_NONE;
+ }
+ }
+
+}
+
+QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP
+QOleDropTarget::DragOver(DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
+{
+#ifdef QDND_DEBUG
+ qDebug("QOleDropTarget::DragOver(grfKeyState %d, pt (%d,%d), pdwEffect %d)", grfKeyState, pt.x, pt.y, pdwEffect);
+#endif
+
+ QWidget *dragOverWidget = widget->childAt(widget->mapFromGlobal(QPoint(pt.x, pt.y)));
+ if (!dragOverWidget)
+ dragOverWidget = widget;
+
+
+ if (!QApplicationPrivate::tryModalHelper(dragOverWidget)
+ || !dragOverWidget->testAttribute(Qt::WA_DropSiteRegistered)) {
+ *pdwEffect = DROPEFFECT_NONE;
+ return NOERROR;
+ }
+
+ QPoint tmpPoint = dragOverWidget->mapFromGlobal(QPoint(pt.x, pt.y));
+ // see if we should compress this event
+ if ((tmpPoint == lastPoint || answerRect.contains(tmpPoint)) && lastKeyState == grfKeyState) {
+ *pdwEffect = chosenEffect;
+ return NOERROR;
+ }
+
+ if (!dragOverWidget->internalWinId() && dragOverWidget != currentWidget) {
+ QPointer<QWidget> dragOverWidgetGuard(dragOverWidget);
+ // Send drag leave event to the previous drag widget.
+ QDragLeaveEvent dragLeave;
+ if (currentWidget)
+ QApplication::sendEvent(currentWidget, &dragLeave);
+ if (!dragOverWidgetGuard) {
+ dragOverWidget = widget->childAt(widget->mapFromGlobal(QPoint(pt.x, pt.y)));
+ if (!dragOverWidget)
+ dragOverWidget = widget;
+ }
+ // Send drag enter event to the current drag widget.
+ sendDragEnterEvent(dragOverWidget, grfKeyState, pt, pdwEffect);
+ }
+
+ QDragManager *manager = QDragManager::self();
+ QMimeData *md = manager->source() ? manager->dragPrivate()->data : manager->dropData;
+
+ QDragMoveEvent oldEvent(lastPoint, translateToQDragDropActions(*pdwEffect), md,
+ toQtMouseButtons(lastKeyState), toQtKeyboardModifiers(lastKeyState));
+
+
+ lastPoint = tmpPoint;
+ lastKeyState = grfKeyState;
+
+ QDragMoveEvent e(lastPoint, translateToQDragDropActions(*pdwEffect), md,
+ toQtMouseButtons(grfKeyState), toQtKeyboardModifiers(grfKeyState));
+ if (chosenEffect != DROPEFFECT_NONE) {
+ if (oldEvent.dropAction() == e.dropAction() &&
+ oldEvent.keyboardModifiers() == e.keyboardModifiers())
+ e.setDropAction(translateToQDragDropAction(chosenEffect));
+ e.accept();
+ }
+ QApplication::sendEvent(dragOverWidget, &e);
+
+ answerRect = e.answerRect();
+ if (e.isAccepted())
+ chosenEffect = translateToWinDragEffects(e.dropAction());
+ else
+ chosenEffect = DROPEFFECT_NONE;
+ *pdwEffect = chosenEffect;
+
+ return NOERROR;
+}
+
+QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP
+QOleDropTarget::DragLeave()
+{
+#ifdef QDND_DEBUG
+ qDebug("QOleDropTarget::DragLeave()");
+#endif
+
+ if (!QApplicationPrivate::tryModalHelper(widget)) {
+ return NOERROR;
+ }
+
+ currentWidget = 0;
+ QDragLeaveEvent e;
+ QApplication::sendEvent(widget, &e);
+
+ QDragManager *manager = QDragManager::self();
+
+ if (manager->dropData->currentDataObject) { // Sanity
+ manager->dropData->currentDataObject->Release();
+ manager->dropData->currentDataObject = 0;
+ }
+
+ return NOERROR;
+}
+
+#define KEY_STATE_BUTTON_MASK (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)
+
+QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP
+QOleDropTarget::Drop(LPDATAOBJECT /*pDataObj*/, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
+{
+#ifdef QDND_DEBUG
+ qDebug("QOleDropTarget::Drop(LPDATAOBJECT /*pDataObj*/, grfKeyState %d, POINTL pt, LPDWORD pdwEffect)", grfKeyState);
+#endif
+
+ QWidget *dropWidget = widget->childAt(widget->mapFromGlobal(QPoint(pt.x, pt.y)));
+ if (!dropWidget)
+ dropWidget = widget;
+
+ if (!QApplicationPrivate::tryModalHelper(dropWidget)
+ || !dropWidget->testAttribute(Qt::WA_DropSiteRegistered)) {
+ *pdwEffect = DROPEFFECT_NONE;
+ return NOERROR;
+ }
+
+ lastPoint = dropWidget->mapFromGlobal(QPoint(pt.x,pt.y));
+ // grfKeyState does not all ways contain button state in the drop so if
+ // it doesn't then use the last known button state;
+ if ((grfKeyState & KEY_STATE_BUTTON_MASK) == 0)
+ grfKeyState |= lastKeyState & KEY_STATE_BUTTON_MASK;
+ lastKeyState = grfKeyState;
+
+ QDragManager *manager = QDragManager::self();
+ QMimeData *md = manager->source() ? manager->dragPrivate()->data : manager->dropData;
+ QDropEvent e(lastPoint, translateToQDragDropActions(*pdwEffect), md,
+ toQtMouseButtons(grfKeyState), toQtKeyboardModifiers(grfKeyState));
+ if (chosenEffect != DROPEFFECT_NONE) {
+ e.setDropAction(translateToQDragDropAction(chosenEffect));
+ }
+ QApplication::sendEvent(dropWidget, &e);
+
+ if (chosenEffect != DROPEFFECT_NONE) {
+ e.accept();
+ }
+
+
+ if (e.isAccepted()) {
+ if (e.dropAction() == Qt::MoveAction || e.dropAction() == Qt::TargetMoveAction) {
+ if (e.dropAction() == Qt::MoveAction)
+ chosenEffect = DROPEFFECT_MOVE;
+ else
+ chosenEffect = DROPEFFECT_COPY;
+ HGLOBAL hData = GlobalAlloc(0, sizeof(DWORD));
+ if (hData) {
+ DWORD *moveEffect = (DWORD *)GlobalLock(hData);;
+ *moveEffect = DROPEFFECT_MOVE;
+ GlobalUnlock(hData);
+ STGMEDIUM medium;
+ memset(&medium, 0, sizeof(STGMEDIUM));
+ medium.tymed = TYMED_HGLOBAL;
+ medium.hGlobal = hData;
+ FORMATETC format;
+ format.cfFormat = RegisterClipboardFormat(CFSTR_PERFORMEDDROPEFFECT);
+ format.tymed = TYMED_HGLOBAL;
+ format.ptd = 0;
+ format.dwAspect = 1;
+ format.lindex = -1;
+ manager->dropData->currentDataObject->SetData(&format, &medium, true);
+ }
+ } else {
+ chosenEffect = translateToWinDragEffects(e.dropAction());
+ }
+ } else {
+ chosenEffect = DROPEFFECT_NONE;
+ }
+ *pdwEffect = chosenEffect;
+
+
+ if (manager->dropData->currentDataObject) {
+ manager->dropData->currentDataObject->Release();
+ manager->dropData->currentDataObject = 0;
+ }
+
+ return NOERROR;
+
+ // We won't get any mouserelease-event, so manually adjust qApp state:
+///### test this QApplication::winMouseButtonUp();
+}
+
+//---------------------------------------------------------------------
+// QDropData
+//---------------------------------------------------------------------
+
+bool QDropData::hasFormat_sys(const QString &mimeType) const
+{
+ if (!currentDataObject) // Sanity
+ return false;
+
+ return QWindowsMime::converterToMime(mimeType, currentDataObject) != 0;
+}
+
+QStringList QDropData::formats_sys() const
+{
+ QStringList fmts;
+ if (!currentDataObject) // Sanity
+ return fmts;
+
+ fmts = QWindowsMime::allMimesForFormats(currentDataObject);
+
+ return fmts;
+}
+
+QVariant QDropData::retrieveData_sys(const QString &mimeType, QVariant::Type type) const
+{
+ QVariant result;
+
+ if (!currentDataObject) // Sanity
+ return result;
+
+ QWindowsMime *converter = QWindowsMime::converterToMime(mimeType, currentDataObject);
+
+ if (converter)
+ result = converter->convertToMime(mimeType, currentDataObject, type);
+
+ return result;
+}
+
+Qt::DropAction QDragManager::drag(QDrag *o)
+
+{
+#ifdef QDND_DEBUG
+ qDebug("QDragManager::drag(QDrag *drag)");
+#endif
+
+ if (object == o || !o || !o->d_func()->source)
+ return Qt::IgnoreAction;
+
+ if (object) {
+ cancel();
+ qApp->removeEventFilter(this);
+ beingCancelled = false;
+ }
+
+ object = o;
+
+#ifdef QDND_DEBUG
+ qDebug("actions = %s", dragActionsToString(dragPrivate()->possible_actions).toLatin1().data());
+#endif
+
+ dragPrivate()->target = 0;
+
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(this, 0, QAccessible::DragDropStart);
+#endif
+
+ DWORD resultEffect;
+ QOleDropSource *src = new QOleDropSource();
+ src->createCursors();
+ QOleDataObject *obj = new QOleDataObject(o->mimeData());
+ DWORD allowedEffects = translateToWinDragEffects(dragPrivate()->possible_actions);
+
+#if !defined(Q_OS_WINCE) || defined(GWES_ICONCURS)
+ HRESULT r = DoDragDrop(obj, src, allowedEffects, &resultEffect);
+#else
+ HRESULT r = DRAGDROP_S_CANCEL;
+ resultEffect = DROPEFFECT_MOVE;
+#endif
+
+ Qt::DropAction ret = Qt::IgnoreAction;
+ if (r == DRAGDROP_S_DROP) {
+ if (obj->reportedPerformedEffect() == DROPEFFECT_MOVE && resultEffect != DROPEFFECT_MOVE) {
+ ret = Qt::TargetMoveAction;
+ resultEffect = DROPEFFECT_MOVE;
+ } else {
+ ret = translateToQDragDropAction(resultEffect);
+ }
+ // Force it to be a copy if an unsupported operation occurred.
+ // This indicates a bug in the drop target.
+ if (resultEffect != DROPEFFECT_NONE && !(resultEffect & allowedEffects))
+ ret = Qt::CopyAction;
+ } else {
+ dragPrivate()->target = 0;
+ }
+
+ // clean up
+ obj->releaseQt();
+ obj->Release(); // Will delete obj if refcount becomes 0
+ src->Release(); // Will delete src if refcount becomes 0
+ object = 0;
+ o->setMimeData(0);
+ o->deleteLater();
+
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(this, 0, QAccessible::DragDropEnd);
+#endif
+
+ return ret;
+}
+
+void QDragManager::cancel(bool /* deleteSource */)
+{
+ if (object) {
+ beingCancelled = true;
+ object = 0;
+ }
+
+#ifndef QT_NO_CURSOR
+ // insert cancel code here ######## todo
+
+ if (restoreCursor) {
+ QApplication::restoreOverrideCursor();
+ restoreCursor = false;
+ }
+#endif
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::updateAccessibility(this, 0, QAccessible::DragDropEnd);
+#endif
+}
+
+void QDragManager::updatePixmap()
+{
+ // not used in windows implementation
+}
+
+bool QDragManager::eventFilter(QObject *, QEvent *)
+{
+ // not used in windows implementation
+ return false;
+}
+
+void QDragManager::timerEvent(QTimerEvent*)
+{
+ // not used in windows implementation
+}
+
+void QDragManager::move(const QPoint &)
+{
+ // not used in windows implementation
+}
+
+void QDragManager::drop()
+{
+ // not used in windows implementation
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_DRAGANDDROP
diff --git a/src/widgets/platforms/win/qfont_win.cpp b/src/widgets/platforms/win/qfont_win.cpp
new file mode 100644
index 0000000000..7a0f234ca6
--- /dev/null
+++ b/src/widgets/platforms/win/qfont_win.cpp
@@ -0,0 +1,166 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qfont.h"
+#include "qfont_p.h"
+#include "qfontengine_p.h"
+#include "qtextengine_p.h"
+#include "qfontmetrics.h"
+#include "qfontinfo.h"
+
+#include "qwidget.h"
+#include "qpainter.h"
+#include <limits.h>
+#include "qt_windows.h"
+#include <private/qapplication_p.h>
+#include "qapplication.h"
+#include <private/qunicodetables_p.h>
+#include <qfontdatabase.h>
+
+QT_BEGIN_NAMESPACE
+
+extern HDC shared_dc(); // common dc for all fonts
+extern QFont::Weight weightFromInteger(int weight); // qfontdatabase.cpp
+
+// ### maybe move to qapplication_win
+QFont qt_LOGFONTtoQFont(LOGFONT& lf, bool /*scale*/)
+{
+ QString family = QString::fromWCharArray(lf.lfFaceName);
+ QFont qf(family);
+ qf.setItalic(lf.lfItalic);
+ if (lf.lfWeight != FW_DONTCARE)
+ qf.setWeight(weightFromInteger(lf.lfWeight));
+ int lfh = qAbs(lf.lfHeight);
+ qf.setPointSizeF(lfh * 72.0 / GetDeviceCaps(shared_dc(),LOGPIXELSY));
+ qf.setUnderline(false);
+ qf.setOverline(false);
+ qf.setStrikeOut(false);
+ return qf;
+}
+
+
+static inline float pixelSize(const QFontDef &request, int dpi)
+{
+ float pSize;
+ if (request.pointSize != -1)
+ pSize = request.pointSize * dpi/ 72.;
+ else
+ pSize = request.pixelSize;
+ return pSize;
+}
+
+static inline float pointSize(const QFontDef &fd, int dpi)
+{
+ float pSize;
+ if (fd.pointSize < 0)
+ pSize = fd.pixelSize * 72. / ((float)dpi);
+ else
+ pSize = fd.pointSize;
+ return pSize;
+}
+
+/*****************************************************************************
+ QFont member functions
+ *****************************************************************************/
+
+void QFont::initialize()
+{
+}
+
+void QFont::cleanup()
+{
+ QFontCache::cleanup();
+}
+
+HFONT QFont::handle() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ if (engine->type() == QFontEngine::Multi)
+ engine = static_cast<QFontEngineMulti *>(engine)->engine(0);
+ if (engine->type() == QFontEngine::Win)
+ return static_cast<QFontEngineWin *>(engine)->hfont;
+ return 0;
+}
+
+QString QFont::rawName() const
+{
+ return family();
+}
+
+void QFont::setRawName(const QString &name)
+{
+ setFamily(name);
+}
+
+QString QFont::defaultFamily() const
+{
+ switch(d->request.styleHint) {
+ case QFont::Times:
+ return QString::fromLatin1("Times New Roman");
+ case QFont::Courier:
+ case QFont::Monospace:
+ return QString::fromLatin1("Courier New");
+ case QFont::Decorative:
+ return QString::fromLatin1("Bookman Old Style");
+ case QFont::Cursive:
+ return QString::fromLatin1("Comic Sans MS");
+ case QFont::Fantasy:
+ return QString::fromLatin1("Impact");
+ case QFont::Helvetica:
+ return QString::fromLatin1("Arial");
+ case QFont::System:
+ default:
+ return QString::fromLatin1("MS Sans Serif");
+ }
+}
+
+QString QFont::lastResortFamily() const
+{
+ return QString::fromLatin1("helvetica");
+}
+
+QString QFont::lastResortFont() const
+{
+ return QString::fromLatin1("arial");
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/win/qfontdatabase_win.cpp b/src/widgets/platforms/win/qfontdatabase_win.cpp
new file mode 100644
index 0000000000..788eb307c3
--- /dev/null
+++ b/src/widgets/platforms/win/qfontdatabase_win.cpp
@@ -0,0 +1,1348 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qt_windows.h"
+#include <qmath.h>
+#include <private/qapplication_p.h>
+#include "qfont_p.h"
+#include "qfontengine_p.h"
+#include "qpaintdevice.h"
+#include <private/qsystemlibrary_p.h>
+#include "qabstractfileengine.h"
+#include "qendian.h"
+
+#if !defined(QT_NO_DIRECTWRITE)
+# include "qsettings.h"
+# include "qfontenginedirectwrite_p.h"
+#endif
+
+#ifdef Q_OS_WINCE
+# include <QTemporaryFile>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+extern HDC shared_dc(); // common dc for all fonts
+
+#ifdef MAKE_TAG
+#undef MAKE_TAG
+#endif
+// GetFontData expects the tags in little endian ;(
+#define MAKE_TAG(ch1, ch2, ch3, ch4) (\
+ (((quint32)(ch4)) << 24) | \
+ (((quint32)(ch3)) << 16) | \
+ (((quint32)(ch2)) << 8) | \
+ ((quint32)(ch1)) \
+ )
+
+static HFONT stock_sysfont = 0;
+
+static bool localizedName(const QString &name)
+{
+ const QChar *c = name.unicode();
+ for(int i = 0; i < name.length(); ++i) {
+ if(c[i].unicode() >= 0x100)
+ return true;
+ }
+ return false;
+}
+
+static inline quint16 getUShort(const unsigned char *p)
+{
+ quint16 val;
+ val = *p++ << 8;
+ val |= *p;
+
+ return val;
+}
+
+static QString getEnglishName(const uchar *table, quint32 bytes)
+{
+ QString i18n_name;
+ enum {
+ NameRecordSize = 12,
+ FamilyId = 1,
+ MS_LangIdEnglish = 0x009
+ };
+
+ // get the name table
+ quint16 count;
+ quint16 string_offset;
+ const unsigned char *names;
+
+ int microsoft_id = -1;
+ int apple_id = -1;
+ int unicode_id = -1;
+
+ if(getUShort(table) != 0)
+ goto error;
+
+ count = getUShort(table+2);
+ string_offset = getUShort(table+4);
+ names = table + 6;
+
+ if(string_offset >= bytes || 6 + count*NameRecordSize > string_offset)
+ goto error;
+
+ for(int i = 0; i < count; ++i) {
+ // search for the correct name entry
+
+ quint16 platform_id = getUShort(names + i*NameRecordSize);
+ quint16 encoding_id = getUShort(names + 2 + i*NameRecordSize);
+ quint16 language_id = getUShort(names + 4 + i*NameRecordSize);
+ quint16 name_id = getUShort(names + 6 + i*NameRecordSize);
+
+ if(name_id != FamilyId)
+ continue;
+
+ enum {
+ PlatformId_Unicode = 0,
+ PlatformId_Apple = 1,
+ PlatformId_Microsoft = 3
+ };
+
+ quint16 length = getUShort(names + 8 + i*NameRecordSize);
+ quint16 offset = getUShort(names + 10 + i*NameRecordSize);
+ if(DWORD(string_offset + offset + length) >= bytes)
+ continue;
+
+ if ((platform_id == PlatformId_Microsoft
+ && (encoding_id == 0 || encoding_id == 1))
+ && (language_id & 0x3ff) == MS_LangIdEnglish
+ && microsoft_id == -1)
+ microsoft_id = i;
+ // not sure if encoding id 4 for Unicode is utf16 or ucs4...
+ else if(platform_id == PlatformId_Unicode && encoding_id < 4 && unicode_id == -1)
+ unicode_id = i;
+ else if(platform_id == PlatformId_Apple && encoding_id == 0 && language_id == 0)
+ apple_id = i;
+ }
+ {
+ bool unicode = false;
+ int id = -1;
+ if(microsoft_id != -1) {
+ id = microsoft_id;
+ unicode = true;
+ } else if(apple_id != -1) {
+ id = apple_id;
+ unicode = false;
+ } else if (unicode_id != -1) {
+ id = unicode_id;
+ unicode = true;
+ }
+ if(id != -1) {
+ quint16 length = getUShort(names + 8 + id*NameRecordSize);
+ quint16 offset = getUShort(names + 10 + id*NameRecordSize);
+ if(unicode) {
+ // utf16
+
+ length /= 2;
+ i18n_name.resize(length);
+ QChar *uc = (QChar *) i18n_name.unicode();
+ const unsigned char *string = table + string_offset + offset;
+ for(int i = 0; i < length; ++i)
+ uc[i] = getUShort(string + 2*i);
+ } else {
+ // Apple Roman
+
+ i18n_name.resize(length);
+ QChar *uc = (QChar *) i18n_name.unicode();
+ const unsigned char *string = table + string_offset + offset;
+ for(int i = 0; i < length; ++i)
+ uc[i] = QLatin1Char(string[i]);
+ }
+ }
+ }
+ error:
+ //qDebug("got i18n name of '%s' for font '%s'", i18n_name.latin1(), familyName.toLocal8Bit().data());
+ return i18n_name;
+}
+
+static QString getEnglishName(const QString &familyName)
+{
+ QString i18n_name;
+
+ HDC hdc = GetDC( 0 );
+ LOGFONT lf;
+ memset(&lf, 0, sizeof(LOGFONT));
+ memcpy(lf.lfFaceName, familyName.utf16(), qMin(LF_FACESIZE, familyName.length()) * sizeof(wchar_t));
+ lf.lfCharSet = DEFAULT_CHARSET;
+ HFONT hfont = CreateFontIndirect(&lf);
+
+ if(!hfont) {
+ ReleaseDC(0, hdc);
+ return QString();
+ }
+
+ HGDIOBJ oldobj = SelectObject( hdc, hfont );
+
+ const DWORD name_tag = MAKE_TAG( 'n', 'a', 'm', 'e' );
+
+ // get the name table
+ unsigned char *table = 0;
+
+ DWORD bytes = GetFontData( hdc, name_tag, 0, 0, 0 );
+ if ( bytes == GDI_ERROR ) {
+ // ### Unused variable
+ /* int err = GetLastError(); */
+ goto error;
+ }
+
+ table = new unsigned char[bytes];
+ GetFontData(hdc, name_tag, 0, table, bytes);
+ if ( bytes == GDI_ERROR )
+ goto error;
+
+ i18n_name = getEnglishName(table, bytes);
+error:
+ delete [] table;
+ SelectObject( hdc, oldobj );
+ DeleteObject( hfont );
+ ReleaseDC( 0, hdc );
+
+ //qDebug("got i18n name of '%s' for font '%s'", i18n_name.latin1(), familyName.toLocal8Bit().data());
+ return i18n_name;
+}
+
+extern QFont::Weight weightFromInteger(int weight); // qfontdatabase.cpp
+
+static
+void addFontToDatabase(QString familyName, const QString &scriptName,
+ TEXTMETRIC *textmetric,
+ const FONTSIGNATURE *signature,
+ int type)
+{
+ const int script = -1;
+ const QString foundryName;
+ Q_UNUSED(script);
+
+ bool italic = false;
+ int weight;
+ bool fixed;
+ bool ttf;
+ bool scalable;
+ int size;
+
+// QString escript = QString::fromWCharArray(f->elfScript);
+// qDebug("script=%s", escript.latin1());
+
+ NEWTEXTMETRIC *tm = (NEWTEXTMETRIC *)textmetric;
+ fixed = !(tm->tmPitchAndFamily & TMPF_FIXED_PITCH);
+ ttf = (tm->tmPitchAndFamily & TMPF_TRUETYPE);
+ scalable = tm->tmPitchAndFamily & (TMPF_VECTOR|TMPF_TRUETYPE);
+ size = scalable ? SMOOTH_SCALABLE : tm->tmHeight;
+ italic = tm->tmItalic;
+ weight = tm->tmWeight;
+
+ // the "@family" fonts are just the same as "family". Ignore them.
+ if (familyName[0] != QLatin1Char('@') && !familyName.startsWith(QLatin1String("WST_"))) {
+ QtFontStyle::Key styleKey;
+ styleKey.style = italic ? QFont::StyleItalic : QFont::StyleNormal;
+ styleKey.weight = weightFromInteger(weight);
+
+ QtFontFamily *family = privateDb()->family(familyName, true);
+
+ if(ttf && localizedName(familyName) && family->english_name.isEmpty())
+ family->english_name = getEnglishName(familyName);
+
+ QtFontFoundry *foundry = family->foundry(foundryName, true);
+ QtFontStyle *style = foundry->style(styleKey, QString(), true);
+ style->smoothScalable = scalable;
+ style->pixelSize( size, TRUE);
+
+ // add fonts windows can generate for us:
+ if (styleKey.weight <= QFont::DemiBold) {
+ QtFontStyle::Key key(styleKey);
+ key.weight = QFont::Bold;
+ QtFontStyle *style = foundry->style(key, QString(), true);
+ style->smoothScalable = scalable;
+ style->pixelSize( size, TRUE);
+ }
+ if (styleKey.style != QFont::StyleItalic) {
+ QtFontStyle::Key key(styleKey);
+ key.style = QFont::StyleItalic;
+ QtFontStyle *style = foundry->style(key, QString(), true);
+ style->smoothScalable = scalable;
+ style->pixelSize( size, TRUE);
+ }
+ if (styleKey.weight <= QFont::DemiBold && styleKey.style != QFont::StyleItalic) {
+ QtFontStyle::Key key(styleKey);
+ key.weight = QFont::Bold;
+ key.style = QFont::StyleItalic;
+ QtFontStyle *style = foundry->style(key, QString(), true);
+ style->smoothScalable = scalable;
+ style->pixelSize( size, TRUE);
+ }
+
+ family->fixedPitch = fixed;
+
+ if (!family->writingSystemCheck && type & TRUETYPE_FONTTYPE) {
+ quint32 unicodeRange[4] = {
+ signature->fsUsb[0], signature->fsUsb[1],
+ signature->fsUsb[2], signature->fsUsb[3]
+ };
+#ifdef Q_WS_WINCE
+ if (signature->fsUsb[0] == 0) {
+ // If the unicode ranges bit mask is zero then
+ // EnumFontFamiliesEx failed to determine it properly.
+ // In this case we just pretend that the font supports all languages.
+ unicodeRange[0] = 0xbfffffff; // second most significant bit must be zero
+ unicodeRange[1] = 0xffffffff;
+ unicodeRange[2] = 0xffffffff;
+ unicodeRange[3] = 0xffffffff;
+ }
+#endif
+ quint32 codePageRange[2] = {
+ signature->fsCsb[0], signature->fsCsb[1]
+ };
+ QList<QFontDatabase::WritingSystem> systems = qt_determine_writing_systems_from_truetype_bits(unicodeRange, codePageRange);
+
+ for (int i = 0; i < systems.count(); ++i) {
+ QFontDatabase::WritingSystem writingSystem = systems.at(i);
+
+ // ### Hack to work around problem with Thai text on Windows 7. Segoe UI contains
+ // the symbol for Baht, and Windows thus reports that it supports the Thai script.
+ // Since it's the default UI font on this platform, most widgets will be unable to
+ // display Thai text by default. As a temporary work around, we special case Segoe UI
+ // and remove the Thai script from its list of supported writing systems.
+ if (writingSystem != QFontDatabase::Thai || familyName != QLatin1String("Segoe UI"))
+ family->writingSystems[writingSystem] = QtFontFamily::Supported;
+ }
+ } else if (!family->writingSystemCheck) {
+ //qDebug("family='%s' script=%s", family->name.latin1(), script.latin1());
+ if (scriptName == QLatin1String("Western")
+ || scriptName == QLatin1String("Baltic")
+ || scriptName == QLatin1String("Central European")
+ || scriptName == QLatin1String("Turkish")
+ || scriptName == QLatin1String("Vietnamese"))
+ family->writingSystems[QFontDatabase::Latin] = QtFontFamily::Supported;
+ else if (scriptName == QLatin1String("Thai"))
+ family->writingSystems[QFontDatabase::Thai] = QtFontFamily::Supported;
+ else if (scriptName == QLatin1String("Symbol")
+ || scriptName == QLatin1String("Other"))
+ family->writingSystems[QFontDatabase::Symbol] = QtFontFamily::Supported;
+ else if (scriptName == QLatin1String("OEM/Dos"))
+ family->writingSystems[QFontDatabase::Latin] = QtFontFamily::Supported;
+ else if (scriptName == QLatin1String("CHINESE_GB2312"))
+ family->writingSystems[QFontDatabase::SimplifiedChinese] = QtFontFamily::Supported;
+ else if (scriptName == QLatin1String("CHINESE_BIG5"))
+ family->writingSystems[QFontDatabase::TraditionalChinese] = QtFontFamily::Supported;
+ else if (scriptName == QLatin1String("Cyrillic"))
+ family->writingSystems[QFontDatabase::Cyrillic] = QtFontFamily::Supported;
+ else if (scriptName == QLatin1String("Hangul"))
+ family->writingSystems[QFontDatabase::Korean] = QtFontFamily::Supported;
+ else if (scriptName == QLatin1String("Hebrew"))
+ family->writingSystems[QFontDatabase::Hebrew] = QtFontFamily::Supported;
+ else if (scriptName == QLatin1String("Greek"))
+ family->writingSystems[QFontDatabase::Greek] = QtFontFamily::Supported;
+ else if (scriptName == QLatin1String("Japanese"))
+ family->writingSystems[QFontDatabase::Japanese] = QtFontFamily::Supported;
+ else if (scriptName == QLatin1String("Arabic"))
+ family->writingSystems[QFontDatabase::Arabic] = QtFontFamily::Supported;
+ }
+ }
+}
+
+static
+int CALLBACK
+storeFont(ENUMLOGFONTEX* f, NEWTEXTMETRICEX *textmetric, int type, LPARAM /*p*/)
+{
+ QString familyName = QString::fromWCharArray(f->elfLogFont.lfFaceName);
+ QString script = QString::fromWCharArray(f->elfScript);
+
+ FONTSIGNATURE signature = textmetric->ntmFontSig;
+
+ // NEWTEXTMETRICEX is a NEWTEXTMETRIC, which according to the documentation is
+ // identical to a TEXTMETRIC except for the last four members, which we don't use
+ // anyway
+ addFontToDatabase(familyName, script, (TEXTMETRIC *)textmetric, &signature, type);
+ // keep on enumerating
+ return 1;
+}
+
+static
+void populate_database(const QString& fam)
+{
+ QFontDatabasePrivate *d = privateDb();
+ if (!d)
+ return;
+
+ QtFontFamily *family = 0;
+ if(!fam.isEmpty()) {
+ family = d->family(fam);
+ if(family && family->loaded)
+ return;
+ } else if (d->count) {
+ return;
+ }
+
+ HDC dummy = GetDC(0);
+
+ LOGFONT lf;
+ lf.lfCharSet = DEFAULT_CHARSET;
+ if (fam.isNull()) {
+ lf.lfFaceName[0] = 0;
+ } else {
+ memcpy(lf.lfFaceName, fam.utf16(), sizeof(wchar_t) * qMin(fam.length() + 1, 32)); // 32 = Windows hard-coded
+ }
+ lf.lfPitchAndFamily = 0;
+
+ EnumFontFamiliesEx(dummy, &lf,
+ (FONTENUMPROC)storeFont, (LPARAM)privateDb(), 0);
+
+ ReleaseDC(0, dummy);
+
+ for (int i = 0; i < d->applicationFonts.count(); ++i) {
+ QFontDatabasePrivate::ApplicationFont fnt = d->applicationFonts.at(i);
+ if (!fnt.memoryFont)
+ continue;
+ for (int j = 0; j < fnt.families.count(); ++j) {
+ const QString familyName = fnt.families.at(j);
+ HDC hdc = GetDC(0);
+ LOGFONT lf;
+ memset(&lf, 0, sizeof(LOGFONT));
+ memcpy(lf.lfFaceName, familyName.utf16(), sizeof(wchar_t) * qMin(LF_FACESIZE, familyName.size()));
+ lf.lfCharSet = DEFAULT_CHARSET;
+ HFONT hfont = CreateFontIndirect(&lf);
+ HGDIOBJ oldobj = SelectObject(hdc, hfont);
+
+ TEXTMETRIC textMetrics;
+ GetTextMetrics(hdc, &textMetrics);
+
+ addFontToDatabase(familyName, QString(),
+ &textMetrics,
+ &fnt.signatures.at(j),
+ TRUETYPE_FONTTYPE);
+
+ SelectObject(hdc, oldobj);
+ DeleteObject(hfont);
+ ReleaseDC(0, hdc);
+ }
+ }
+
+ if(!fam.isEmpty()) {
+ family = d->family(fam);
+ if(family) {
+ if(!family->writingSystemCheck) {
+ }
+ family->loaded = true;
+ }
+ }
+}
+
+static void initializeDb()
+{
+ QFontDatabasePrivate *db = privateDb();
+ if (!db || db->count)
+ return;
+
+ populate_database(QString());
+
+#ifdef QFONTDATABASE_DEBUG
+ // print the database
+ for (int f = 0; f < db->count; f++) {
+ QtFontFamily *family = db->families[f];
+ qDebug(" %s: %p", qPrintable(family->name), family);
+ populate_database(family->name);
+
+#if 0
+ qDebug(" scripts supported:");
+ for (int i = 0; i < QUnicodeTables::ScriptCount; i++)
+ if(family->writingSystems[i] & QtFontFamily::Supported)
+ qDebug(" %d", i);
+ for (int fd = 0; fd < family->count; fd++) {
+ QtFontFoundry *foundry = family->foundries[fd];
+ qDebug(" %s", foundry->name.latin1());
+ for (int s = 0; s < foundry->count; s++) {
+ QtFontStyle *style = foundry->styles[s];
+ qDebug(" style: style=%d weight=%d smooth=%d", style->key.style,
+ style->key.weight, style->smoothScalable );
+ if(!style->smoothScalable) {
+ for(int i = 0; i < style->count; ++i) {
+ qDebug(" %d", style->pixelSizes[i].pixelSize);
+ }
+ }
+ }
+ }
+#endif
+ }
+#endif // QFONTDATABASE_DEBUG
+
+}
+
+static inline void load(const QString &family = QString(), int = -1)
+{
+ populate_database(family);
+}
+
+
+
+
+
+// --------------------------------------------------------------------------------------
+// font loader
+// --------------------------------------------------------------------------------------
+
+
+
+static void initFontInfo(QFontEngineWin *fe, const QFontDef &request, HDC fontHdc, int dpi)
+{
+ fe->fontDef = request; // most settings are equal
+
+ HDC dc = ((request.styleStrategy & QFont::PreferDevice) && fontHdc) ? fontHdc : shared_dc();
+ SelectObject(dc, fe->hfont);
+ wchar_t n[64];
+ GetTextFace(dc, 64, n);
+ fe->fontDef.family = QString::fromWCharArray(n);
+ fe->fontDef.fixedPitch = !(fe->tm.tmPitchAndFamily & TMPF_FIXED_PITCH);
+ if (fe->fontDef.pointSize < 0) {
+ fe->fontDef.pointSize = fe->fontDef.pixelSize * 72. / dpi;
+ } else if (fe->fontDef.pixelSize == -1) {
+ fe->fontDef.pixelSize = qRound(fe->fontDef.pointSize * dpi / 72.);
+ }
+}
+
+#if !defined(QT_NO_DIRECTWRITE)
+static void initFontInfo(QFontEngineDirectWrite *fe, const QFontDef &request,
+ int dpi, IDWriteFont *font)
+{
+ fe->fontDef = request;
+
+ IDWriteFontFamily *fontFamily = NULL;
+ HRESULT hr = font->GetFontFamily(&fontFamily);
+
+ IDWriteLocalizedStrings *familyNames = NULL;
+ if (SUCCEEDED(hr))
+ hr = fontFamily->GetFamilyNames(&familyNames);
+
+ UINT32 index = 0;
+ BOOL exists = false;
+
+ wchar_t localeName[LOCALE_NAME_MAX_LENGTH];
+
+ if (SUCCEEDED(hr)) {
+ int defaultLocaleSuccess = GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH);
+
+ if (defaultLocaleSuccess)
+ hr = familyNames->FindLocaleName(localeName, &index, &exists);
+
+ if (SUCCEEDED(hr) && !exists)
+ hr = familyNames->FindLocaleName(L"en-us", &index, &exists);
+ }
+
+ if (!exists)
+ index = 0;
+
+ UINT32 length = 0;
+ if (SUCCEEDED(hr))
+ hr = familyNames->GetStringLength(index, &length);
+
+ wchar_t *name = new (std::nothrow) wchar_t[length+1];
+ if (name == NULL)
+ hr = E_OUTOFMEMORY;
+
+ // Get the family name.
+ if (SUCCEEDED(hr))
+ hr = familyNames->GetString(index, name, length + 1);
+
+ if (SUCCEEDED(hr))
+ fe->fontDef.family = QString::fromWCharArray(name);
+
+ delete[] name;
+ if (familyNames != NULL)
+ familyNames->Release();
+
+ if (FAILED(hr))
+ qErrnoWarning(hr, "initFontInfo: Failed to get family name");
+
+ if (fe->fontDef.pointSize < 0)
+ fe->fontDef.pointSize = fe->fontDef.pixelSize * 72. / dpi;
+ else if (fe->fontDef.pixelSize == -1)
+ fe->fontDef.pixelSize = qRound(fe->fontDef.pointSize * dpi / 72.);
+}
+#endif
+
+static const char *other_tryFonts[] = {
+ "Arial",
+ "MS UI Gothic",
+ "Gulim",
+ "SimSun",
+ "PMingLiU",
+ "Arial Unicode MS",
+ 0
+};
+
+static const char *jp_tryFonts [] = {
+ "MS UI Gothic",
+ "Arial",
+ "Gulim",
+ "SimSun",
+ "PMingLiU",
+ "Arial Unicode MS",
+ 0
+};
+
+static const char *ch_CN_tryFonts [] = {
+ "SimSun",
+ "Arial",
+ "PMingLiU",
+ "Gulim",
+ "MS UI Gothic",
+ "Arial Unicode MS",
+ 0
+};
+
+static const char *ch_TW_tryFonts [] = {
+ "PMingLiU",
+ "Arial",
+ "SimSun",
+ "Gulim",
+ "MS UI Gothic",
+ "Arial Unicode MS",
+ 0
+};
+
+static const char *kr_tryFonts[] = {
+ "Gulim",
+ "Arial",
+ "PMingLiU",
+ "SimSun",
+ "MS UI Gothic",
+ "Arial Unicode MS",
+ 0
+};
+
+static const char **tryFonts = 0;
+
+#if !defined(QT_NO_DIRECTWRITE)
+static QString fontNameSubstitute(const QString &familyName)
+{
+ QLatin1String key("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\"
+ "FontSubstitutes");
+ return QSettings(key, QSettings::NativeFormat).value(familyName, familyName).toString();
+}
+#endif
+
+static inline HFONT systemFont()
+{
+ if (stock_sysfont == 0)
+ stock_sysfont = (HFONT)GetStockObject(SYSTEM_FONT);
+ return stock_sysfont;
+}
+
+#if !defined(DEFAULT_GUI_FONT)
+#define DEFAULT_GUI_FONT 17
+#endif
+
+static QFontEngine *loadEngine(int script, const QFontDef &request,
+ HDC fontHdc, int dpi, bool rawMode,
+ const QtFontDesc *desc,
+ const QStringList &family_list)
+{
+ LOGFONT lf;
+ memset(&lf, 0, sizeof(LOGFONT));
+
+ bool useDevice = (request.styleStrategy & QFont::PreferDevice) && fontHdc;
+
+ HDC hdc = shared_dc();
+ QString font_name = desc != 0 ? desc->family->name : request.family;
+
+ if (useDevice) {
+ hdc = fontHdc;
+ font_name = request.family;
+ }
+
+ bool stockFont = false;
+ bool preferClearTypeAA = false;
+
+ HFONT hfont = 0;
+
+
+#if !defined(QT_NO_DIRECTWRITE)
+ bool useDirectWrite = (request.hintingPreference == QFont::PreferNoHinting)
+ || (request.hintingPreference == QFont::PreferVerticalHinting);
+ IDWriteFont *directWriteFont = 0;
+#else
+ bool useDirectWrite = false;
+#endif
+
+ if (rawMode) { // will choose a stock font
+ int f, deffnt = SYSTEM_FONT;
+ QString fam = desc != 0 ? desc->family->name.toLower() : request.family.toLower();
+ if (fam == QLatin1String("default"))
+ f = deffnt;
+ else if (fam == QLatin1String("system"))
+ f = SYSTEM_FONT;
+#ifndef Q_WS_WINCE
+ else if (fam == QLatin1String("system_fixed"))
+ f = SYSTEM_FIXED_FONT;
+ else if (fam == QLatin1String("ansi_fixed"))
+ f = ANSI_FIXED_FONT;
+ else if (fam == QLatin1String("ansi_var"))
+ f = ANSI_VAR_FONT;
+ else if (fam == QLatin1String("device_default"))
+ f = DEVICE_DEFAULT_FONT;
+ else if (fam == QLatin1String("oem_fixed"))
+ f = OEM_FIXED_FONT;
+#endif
+ else if (fam[0] == QLatin1Char('#'))
+ f = fam.right(fam.length()-1).toInt();
+ else
+ f = deffnt;
+ hfont = (HFONT)GetStockObject(f);
+ if (!hfont) {
+ qErrnoWarning("QFontEngine::loadEngine: GetStockObject failed");
+ hfont = systemFont();
+ }
+ stockFont = true;
+ } else {
+
+ int hint = FF_DONTCARE;
+ switch (request.styleHint) {
+ case QFont::Helvetica:
+ hint = FF_SWISS;
+ break;
+ case QFont::Times:
+ hint = FF_ROMAN;
+ break;
+ case QFont::Courier:
+ hint = FF_MODERN;
+ break;
+ case QFont::OldEnglish:
+ hint = FF_DECORATIVE;
+ break;
+ case QFont::System:
+ hint = FF_MODERN;
+ break;
+ default:
+ break;
+ }
+
+ lf.lfHeight = -qRound(request.pixelSize);
+ lf.lfWidth = 0;
+ lf.lfEscapement = 0;
+ lf.lfOrientation = 0;
+ if (desc == 0 || desc->style->key.weight == 50)
+ lf.lfWeight = FW_DONTCARE;
+ else
+ lf.lfWeight = (desc->style->key.weight*900)/99;
+ lf.lfItalic = (desc != 0 && desc->style->key.style != QFont::StyleNormal);
+ lf.lfCharSet = DEFAULT_CHARSET;
+
+ int strat = OUT_DEFAULT_PRECIS;
+ if (request.styleStrategy & QFont::PreferBitmap) {
+ strat = OUT_RASTER_PRECIS;
+#ifndef Q_WS_WINCE
+ } else if (request.styleStrategy & QFont::PreferDevice) {
+ strat = OUT_DEVICE_PRECIS;
+ } else if (request.styleStrategy & QFont::PreferOutline) {
+ strat = OUT_OUTLINE_PRECIS;
+ } else if (request.styleStrategy & QFont::ForceOutline) {
+ strat = OUT_TT_ONLY_PRECIS;
+#endif
+ }
+
+ lf.lfOutPrecision = strat;
+
+ int qual = DEFAULT_QUALITY;
+
+ if (request.styleStrategy & QFont::PreferMatch)
+ qual = DRAFT_QUALITY;
+#ifndef Q_WS_WINCE
+ else if (request.styleStrategy & QFont::PreferQuality)
+ qual = PROOF_QUALITY;
+#endif
+
+ if (request.styleStrategy & QFont::PreferAntialias) {
+ if (QSysInfo::WindowsVersion >= QSysInfo::WV_XP) {
+ qual = CLEARTYPE_QUALITY;
+ preferClearTypeAA = true;
+ } else {
+ qual = ANTIALIASED_QUALITY;
+ }
+ } else if (request.styleStrategy & QFont::NoAntialias) {
+ qual = NONANTIALIASED_QUALITY;
+ }
+
+ lf.lfQuality = qual;
+
+ lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ lf.lfPitchAndFamily = DEFAULT_PITCH | hint;
+
+ QString fam = font_name;
+
+ if(fam.isEmpty())
+ fam = QLatin1String("MS Sans Serif");
+
+ if ((fam == QLatin1String("MS Sans Serif"))
+ && (request.style == QFont::StyleItalic || (-lf.lfHeight > 18 && -lf.lfHeight != 24))) {
+ fam = QLatin1String("Arial"); // MS Sans Serif has bearing problems in italic, and does not scale
+ }
+ if (fam == QLatin1String("Courier") && !(request.styleStrategy & QFont::PreferBitmap))
+ fam = QLatin1String("Courier New");
+
+ memcpy(lf.lfFaceName, fam.utf16(), sizeof(wchar_t) * qMin(fam.length() + 1, 32)); // 32 = Windows hard-coded
+
+ hfont = CreateFontIndirect(&lf);
+ if (!hfont)
+ qErrnoWarning("QFontEngine::loadEngine: CreateFontIndirect failed");
+
+ stockFont = (hfont == 0);
+ bool ttf = false;
+ int avWidth = 0;
+ BOOL res;
+ HGDIOBJ oldObj = SelectObject(hdc, hfont);
+
+ TEXTMETRIC tm;
+ res = GetTextMetrics(hdc, &tm);
+ avWidth = tm.tmAveCharWidth;
+ ttf = tm.tmPitchAndFamily & TMPF_TRUETYPE;
+ SelectObject(hdc, oldObj);
+
+ if (!ttf || !useDirectWrite) {
+ useDirectWrite = false;
+
+ if (hfont && (!ttf || request.stretch != 100)) {
+ DeleteObject(hfont);
+ if (!res)
+ qErrnoWarning("QFontEngine::loadEngine: GetTextMetrics failed");
+ lf.lfWidth = avWidth * request.stretch/100;
+ hfont = CreateFontIndirect(&lf);
+ if (!hfont)
+ qErrnoWarning("QFontEngine::loadEngine: CreateFontIndirect with stretch failed");
+ }
+
+#ifndef Q_WS_WINCE
+ if (hfont == 0) {
+ hfont = (HFONT)GetStockObject(ANSI_VAR_FONT);
+ stockFont = true;
+ }
+#else
+ if (hfont == 0) {
+ hfont = (HFONT)GetStockObject(SYSTEM_FONT);
+ stockFont = true;
+ }
+#endif
+
+ }
+
+#if !defined(QT_NO_DIRECTWRITE)
+ else {
+ // Default to false for DirectWrite (and re-enable once/if everything
+ // turns out okay)
+ useDirectWrite = false;
+
+ QFontDatabasePrivate *db = privateDb();
+ if (db->directWriteFactory == 0) {
+ HRESULT hr = DWriteCreateFactory(
+ DWRITE_FACTORY_TYPE_SHARED,
+ __uuidof(IDWriteFactory),
+ reinterpret_cast<IUnknown **>(&db->directWriteFactory)
+ );
+ if (FAILED(hr)) {
+ qErrnoWarning("QFontEngine::loadEngine: DWriteCreateFactory failed");
+ } else {
+ hr = db->directWriteFactory->GetGdiInterop(&db->directWriteGdiInterop);
+ if (FAILED(hr))
+ qErrnoWarning("QFontEngine::loadEngine: GetGdiInterop failed");
+ }
+ }
+
+ if (db->directWriteGdiInterop != 0) {
+ QString nameSubstitute = fontNameSubstitute(QString::fromWCharArray(lf.lfFaceName));
+ memcpy(lf.lfFaceName, nameSubstitute.utf16(),
+ sizeof(wchar_t) * qMin(nameSubstitute.length() + 1, LF_FACESIZE));
+
+ HRESULT hr = db->directWriteGdiInterop->CreateFontFromLOGFONT(
+ &lf,
+ &directWriteFont);
+ if (FAILED(hr)) {
+#ifndef QT_NO_DEBUG
+ qErrnoWarning("QFontEngine::loadEngine: CreateFontFromLOGFONT failed "
+ "for %ls (0x%lx)",
+ lf.lfFaceName, hr);
+#endif
+ } else {
+ DeleteObject(hfont);
+ useDirectWrite = true;
+ }
+ }
+ }
+#endif
+
+ }
+
+ QFontEngine *fe = 0;
+ if (!useDirectWrite) {
+ QFontEngineWin *few = new QFontEngineWin(font_name, hfont, stockFont, lf);
+ if (preferClearTypeAA)
+ few->glyphFormat = QFontEngineGlyphCache::Raster_RGBMask;
+
+ // Also check for OpenType tables when using complex scripts
+ // ### TODO: This only works for scripts that require OpenType. More generally
+ // for scripts that do not require OpenType we should just look at the list of
+ // supported writing systems in the font's OS/2 table.
+ if (scriptRequiresOpenType(script)) {
+ HB_Face hbFace = few->harfbuzzFace();
+ if (!hbFace || !hbFace->supported_scripts[script]) {
+ FM_DEBUG(" OpenType support missing for script\n");
+ delete few;
+ return 0;
+ }
+ }
+
+ initFontInfo(few, request, fontHdc, dpi);
+ fe = few;
+ }
+
+#if !defined(QT_NO_DIRECTWRITE)
+ else {
+ QFontDatabasePrivate *db = privateDb();
+
+ IDWriteFontFace *directWriteFontFace = NULL;
+ HRESULT hr = directWriteFont->CreateFontFace(&directWriteFontFace);
+ if (SUCCEEDED(hr)) {
+ QFontEngineDirectWrite *fedw = new QFontEngineDirectWrite(db->directWriteFactory,
+ directWriteFontFace,
+ request.pixelSize);
+
+ initFontInfo(fedw, request, dpi, directWriteFont);
+
+ fe = fedw;
+ } else {
+ qErrnoWarning(hr, "QFontEngine::loadEngine: CreateFontFace failed");
+ }
+ }
+
+ if (directWriteFont != 0)
+ directWriteFont->Release();
+#endif
+
+ if(script == QUnicodeTables::Common
+ && !(request.styleStrategy & QFont::NoFontMerging)
+ && desc != 0
+ && !(desc->family->writingSystems[QFontDatabase::Symbol] & QtFontFamily::Supported)) {
+ if(!tryFonts) {
+ LANGID lid = GetUserDefaultLangID();
+ switch( lid&0xff ) {
+ case LANG_CHINESE: // Chinese (Taiwan)
+ if ( lid == 0x0804 ) // Taiwan
+ tryFonts = ch_TW_tryFonts;
+ else
+ tryFonts = ch_CN_tryFonts;
+ break;
+ case LANG_JAPANESE:
+ tryFonts = jp_tryFonts;
+ break;
+ case LANG_KOREAN:
+ tryFonts = kr_tryFonts;
+ break;
+ default:
+ tryFonts = other_tryFonts;
+ break;
+ }
+ }
+ QStringList fm = QFontDatabase().families();
+ QStringList list = family_list;
+ const char **tf = tryFonts;
+ while(tf && *tf) {
+ if(fm.contains(QLatin1String(*tf)))
+ list << QLatin1String(*tf);
+ ++tf;
+ }
+ QFontEngine *mfe = new QFontEngineMultiWin(fe, list);
+ mfe->fontDef = fe->fontDef;
+ fe = mfe;
+ }
+ return fe;
+}
+
+QFontEngine *qt_load_font_engine_win(const QFontDef &request)
+{
+ // From qfont.cpp
+ extern int qt_defaultDpi();
+
+ QFontCache::Key key(request, QUnicodeTables::Common);
+ QFontEngine *fe = QFontCache::instance()->findEngine(key);
+ if (fe != 0)
+ return fe;
+ else
+ return loadEngine(QUnicodeTables::Common, request, 0, qt_defaultDpi(), false, 0,
+ QStringList());
+}
+
+const char *styleHint(const QFontDef &request)
+{
+ const char *stylehint = 0;
+ switch (request.styleHint) {
+ case QFont::SansSerif:
+ stylehint = "Arial";
+ break;
+ case QFont::Serif:
+ stylehint = "Times New Roman";
+ break;
+ case QFont::TypeWriter:
+ stylehint = "Courier New";
+ break;
+ default:
+ if (request.fixedPitch)
+ stylehint = "Courier New";
+ break;
+ }
+ return stylehint;
+}
+
+static QFontEngine *loadWin(const QFontPrivate *d, int script, const QFontDef &req)
+{
+ // list of families to try
+ QStringList family_list = familyList(req);
+
+ const char *stylehint = styleHint(d->request);
+ if (stylehint)
+ family_list << QLatin1String(stylehint);
+
+ // append the default fallback font for the specified script
+ // family_list << ... ; ###########
+
+ // add the default family
+ QString defaultFamily = QApplication::font().family();
+ if (! family_list.contains(defaultFamily))
+ family_list << defaultFamily;
+
+ // add QFont::defaultFamily() to the list, for compatibility with
+ // previous versions
+ family_list << QApplication::font().defaultFamily();
+
+ // null family means find the first font matching the specified script
+ family_list << QString();
+
+ QtFontDesc desc;
+ QFontEngine *fe = 0;
+ QList<int> blacklistedFamilies;
+
+ while (!fe) {
+ for (int i = 0; i < family_list.size(); ++i) {
+ QString family, foundry;
+ parseFontName(family_list.at(i), foundry, family);
+ FM_DEBUG("loadWin: >>>>>>>>>>>>>>trying to match '%s'", family.toLatin1().data());
+ QT_PREPEND_NAMESPACE(match)(script, req, family, foundry, -1, &desc, blacklistedFamilies);
+ if (desc.family)
+ break;
+ }
+ if (!desc.family)
+ break;
+ fe = loadEngine(script, req, d->hdc, d->dpi, d->rawMode, &desc, family_list);
+ if (!fe)
+ blacklistedFamilies.append(desc.familyIndex);
+ }
+ return fe;
+}
+
+void QFontDatabase::load(const QFontPrivate *d, int script)
+{
+ // sanity checks
+ if (!qApp)
+ qWarning("QFontDatabase::load: Must construct QApplication first");
+ Q_ASSERT(script >= 0 && script < QUnicodeTables::ScriptCount);
+
+ // normalize the request to get better caching
+ QFontDef req = d->request;
+ if (req.pixelSize <= 0)
+ req.pixelSize = floor((100.0 * req.pointSize * d->dpi) / 72. + 0.5) / 100;
+ if (req.pixelSize < 1)
+ req.pixelSize = 1;
+ if (req.weight == 0)
+ req.weight = QFont::Normal;
+ if (req.stretch == 0)
+ req.stretch = 100;
+
+ QFontCache::Key key(req, d->rawMode ? QUnicodeTables::Common : script, d->screen);
+ if (!d->engineData)
+ getEngineData(d, key);
+
+ // the cached engineData could have already loaded the engine we want
+ if (d->engineData->engines[script])
+ return;
+
+ QFontEngine *fe = QFontCache::instance()->findEngine(key);
+
+ // set it to the actual pointsize, so QFontInfo will do the right thing
+ if (req.pointSize < 0)
+ req.pointSize = req.pixelSize*72./d->dpi;
+
+ if (!fe) {
+ if (qt_enable_test_font && req.family == QLatin1String("__Qt__Box__Engine__")) {
+ fe = new QTestFontEngine(req.pixelSize);
+ fe->fontDef = req;
+ } else {
+ QMutexLocker locker(fontDatabaseMutex());
+ if (!privateDb()->count)
+ initializeDb();
+ fe = loadWin(d, script, req);
+ }
+ if (!fe) {
+ fe = new QFontEngineBox(req.pixelSize);
+ fe->fontDef = QFontDef();
+ }
+ }
+ d->engineData->engines[script] = fe;
+ fe->ref.ref();
+ QFontCache::instance()->insertEngine(key, fe);
+}
+
+#if !defined(FR_PRIVATE)
+#define FR_PRIVATE 0x10
+#endif
+
+typedef int (WINAPI *PtrAddFontResourceExW)(LPCWSTR, DWORD, PVOID);
+typedef HANDLE (WINAPI *PtrAddFontMemResourceEx)(PVOID, DWORD, PVOID, DWORD *);
+typedef BOOL (WINAPI *PtrRemoveFontResourceExW)(LPCWSTR, DWORD, PVOID);
+typedef BOOL (WINAPI *PtrRemoveFontMemResourceEx)(HANDLE);
+
+static QList<quint32> getTrueTypeFontOffsets(const uchar *fontData)
+{
+ QList<quint32> offsets;
+ const quint32 headerTag = *reinterpret_cast<const quint32 *>(fontData);
+ if (headerTag != MAKE_TAG('t', 't', 'c', 'f')) {
+ if (headerTag != MAKE_TAG(0, 1, 0, 0)
+ && headerTag != MAKE_TAG('O', 'T', 'T', 'O')
+ && headerTag != MAKE_TAG('t', 'r', 'u', 'e')
+ && headerTag != MAKE_TAG('t', 'y', 'p', '1'))
+ return offsets;
+ offsets << 0;
+ return offsets;
+ }
+ const quint32 numFonts = qFromBigEndian<quint32>(fontData + 8);
+ for (uint i = 0; i < numFonts; ++i) {
+ offsets << qFromBigEndian<quint32>(fontData + 12 + i * 4);
+ }
+ return offsets;
+}
+
+static void getFontTable(const uchar *fileBegin, const uchar *data, quint32 tag, const uchar **table, quint32 *length)
+{
+ const quint16 numTables = qFromBigEndian<quint16>(data + 4);
+ for (uint i = 0; i < numTables; ++i) {
+ const quint32 offset = 12 + 16 * i;
+ if (*reinterpret_cast<const quint32 *>(data + offset) == tag) {
+ *table = fileBegin + qFromBigEndian<quint32>(data + offset + 8);
+ *length = qFromBigEndian<quint32>(data + offset + 12);
+ return;
+ }
+ }
+ *table = 0;
+ *length = 0;
+ return;
+}
+
+static void getFamiliesAndSignatures(const QByteArray &fontData, QFontDatabasePrivate::ApplicationFont *appFont)
+{
+ const uchar *data = reinterpret_cast<const uchar *>(fontData.constData());
+
+ QList<quint32> offsets = getTrueTypeFontOffsets(data);
+ if (offsets.isEmpty())
+ return;
+
+ for (int i = 0; i < offsets.count(); ++i) {
+ const uchar *font = data + offsets.at(i);
+ const uchar *table;
+ quint32 length;
+ getFontTable(data, font, MAKE_TAG('n', 'a', 'm', 'e'), &table, &length);
+ if (!table)
+ continue;
+ QString name = getEnglishName(table, length);
+ if (name.isEmpty())
+ continue;
+
+ appFont->families << name;
+ FONTSIGNATURE signature;
+ getFontTable(data, font, MAKE_TAG('O', 'S', '/', '2'), &table, &length);
+ if (table && length >= 86) {
+ // See also qfontdatabase_mac.cpp, offsets taken from OS/2 table in the TrueType spec
+ signature.fsUsb[0] = qFromBigEndian<quint32>(table + 42);
+ signature.fsUsb[1] = qFromBigEndian<quint32>(table + 46);
+ signature.fsUsb[2] = qFromBigEndian<quint32>(table + 50);
+ signature.fsUsb[3] = qFromBigEndian<quint32>(table + 54);
+
+ signature.fsCsb[0] = qFromBigEndian<quint32>(table + 78);
+ signature.fsCsb[1] = qFromBigEndian<quint32>(table + 82);
+ } else {
+ memset(&signature, 0, sizeof(signature));
+ }
+ appFont->signatures << signature;
+ }
+}
+
+static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt)
+{
+ if(!fnt->data.isEmpty()) {
+#ifndef Q_OS_WINCE
+ PtrAddFontMemResourceEx ptrAddFontMemResourceEx = (PtrAddFontMemResourceEx)QSystemLibrary::resolve(QLatin1String("gdi32"),
+ "AddFontMemResourceEx");
+ if (!ptrAddFontMemResourceEx)
+ return;
+#endif
+ getFamiliesAndSignatures(fnt->data, fnt);
+ if (fnt->families.isEmpty())
+ return;
+
+#ifdef Q_OS_WINCE
+ HANDLE handle = 0;
+
+ {
+#ifdef QT_NO_TEMPORARYFILE
+ wchar_t lpBuffer[MAX_PATH];
+ GetTempPath(MAX_PATH, lpBuffer);
+ QString s = QString::fromWCharArray(lpBuffer);
+ QFile tempfile(s + QLatin1String("/font") + QString::number(GetTickCount()) + QLatin1String(".ttf"));
+ if (!tempfile.open(QIODevice::ReadWrite))
+#else
+ QTemporaryFile tempfile(QLatin1String("XXXXXXXX.ttf"));
+ if (!tempfile.open())
+#endif // QT_NO_TEMPORARYFILE
+ return;
+ if (tempfile.write(fnt->data) == -1)
+ return;
+
+#ifndef QT_NO_TEMPORARYFILE
+ tempfile.setAutoRemove(false);
+#endif
+ fnt->fileName = QFileInfo(tempfile.fileName()).absoluteFilePath();
+ }
+
+ if (AddFontResource((LPCWSTR)fnt->fileName.utf16()) == 0) {
+ QFile(fnt->fileName).remove();
+ return;
+ }
+#else
+ DWORD dummy = 0;
+ HANDLE handle = ptrAddFontMemResourceEx((void *)fnt->data.constData(), fnt->data.size(), 0,
+ &dummy);
+ if (handle == 0)
+ return;
+#endif // Q_OS_WINCE
+
+ fnt->handle = handle;
+ fnt->data = QByteArray();
+ fnt->memoryFont = true;
+ } else {
+ QFile f(fnt->fileName);
+ if (!f.open(QIODevice::ReadOnly))
+ return;
+ QByteArray data = f.readAll();
+ f.close();
+ getFamiliesAndSignatures(data, fnt);
+
+#ifdef Q_OS_WINCE
+ QFileInfo fileinfo(fnt->fileName);
+ fnt->fileName = fileinfo.absoluteFilePath();
+ if (AddFontResource((LPCWSTR)fnt->fileName.utf16()) == 0)
+ return;
+#else
+ PtrAddFontResourceExW ptrAddFontResourceExW = (PtrAddFontResourceExW)QSystemLibrary::resolve(QLatin1String("gdi32"),
+ "AddFontResourceExW");
+ if (!ptrAddFontResourceExW
+ || ptrAddFontResourceExW((wchar_t*)fnt->fileName.utf16(), FR_PRIVATE, 0) == 0)
+ return;
+#endif // Q_OS_WINCE
+
+ fnt->memoryFont = false;
+ }
+}
+
+bool QFontDatabase::removeApplicationFont(int handle)
+{
+ QMutexLocker locker(fontDatabaseMutex());
+
+ QFontDatabasePrivate *db = privateDb();
+ if (handle < 0 || handle >= db->applicationFonts.count())
+ return false;
+
+ const QFontDatabasePrivate::ApplicationFont font = db->applicationFonts.at(handle);
+ db->applicationFonts[handle] = QFontDatabasePrivate::ApplicationFont();
+ if (font.memoryFont) {
+#ifdef Q_OS_WINCE
+ bool removeSucceeded = RemoveFontResource((LPCWSTR)font.fileName.utf16());
+ QFile tempfile(font.fileName);
+ tempfile.remove();
+ if (!removeSucceeded)
+ return false;
+#else
+ PtrRemoveFontMemResourceEx ptrRemoveFontMemResourceEx = (PtrRemoveFontMemResourceEx)QSystemLibrary::resolve(QLatin1String("gdi32"),
+ "RemoveFontMemResourceEx");
+ if (!ptrRemoveFontMemResourceEx
+ || !ptrRemoveFontMemResourceEx(font.handle))
+ return false;
+#endif // Q_OS_WINCE
+ } else {
+#ifdef Q_OS_WINCE
+ if (!RemoveFontResource((LPCWSTR)font.fileName.utf16()))
+ return false;
+#else
+ PtrRemoveFontResourceExW ptrRemoveFontResourceExW = (PtrRemoveFontResourceExW)QSystemLibrary::resolve(QLatin1String("gdi32"),
+ "RemoveFontResourceExW");
+ if (!ptrRemoveFontResourceExW
+ || !ptrRemoveFontResourceExW((LPCWSTR)font.fileName.utf16(), FR_PRIVATE, 0))
+ return false;
+#endif // Q_OS_WINCE
+ }
+
+ db->invalidate();
+ return true;
+}
+
+bool QFontDatabase::removeAllApplicationFonts()
+{
+ QMutexLocker locker(fontDatabaseMutex());
+
+ QFontDatabasePrivate *db = privateDb();
+ for (int i = 0; i < db->applicationFonts.count(); ++i)
+ if (!removeApplicationFont(i))
+ return false;
+ return true;
+}
+
+bool QFontDatabase::supportsThreadedFontRendering()
+{
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/win/qfontengine_win.cpp b/src/widgets/platforms/win/qfontengine_win.cpp
new file mode 100644
index 0000000000..fc11387367
--- /dev/null
+++ b/src/widgets/platforms/win/qfontengine_win.cpp
@@ -0,0 +1,1336 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#if _WIN32_WINNT < 0x0500
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0500
+#endif
+
+#include "qfontengine_p.h"
+#include "qtextengine_p.h"
+#include <qglobal.h>
+#include "qt_windows.h"
+#include <private/qapplication_p.h>
+
+#include <private/qsystemlibrary_p.h>
+#include <qpaintdevice.h>
+#include <qpainter.h>
+#include <limits.h>
+
+#include <qendian.h>
+#include <qmath.h>
+#include <qthreadstorage.h>
+
+#include <private/qunicodetables_p.h>
+#include <qbitmap.h>
+
+#include <private/qpainter_p.h>
+#include "qpaintengine.h"
+#include "qvarlengtharray.h"
+#include <private/qpaintengine_raster_p.h>
+#include <private/qnativeimage_p.h>
+
+#if defined(Q_WS_WINCE)
+#include "qguifunctions_wince.h"
+#endif
+
+//### mingw needed define
+#ifndef TT_PRIM_CSPLINE
+#define TT_PRIM_CSPLINE 3
+#endif
+
+#ifdef MAKE_TAG
+#undef MAKE_TAG
+#endif
+// GetFontData expects the tags in little endian ;(
+#define MAKE_TAG(ch1, ch2, ch3, ch4) (\
+ (((quint32)(ch4)) << 24) | \
+ (((quint32)(ch3)) << 16) | \
+ (((quint32)(ch2)) << 8) | \
+ ((quint32)(ch1)) \
+ )
+
+// common DC for all fonts
+
+QT_BEGIN_NAMESPACE
+
+class QtHDC
+{
+ HDC _hdc;
+public:
+ QtHDC()
+ {
+ HDC displayDC = GetDC(0);
+ _hdc = CreateCompatibleDC(displayDC);
+ ReleaseDC(0, displayDC);
+ }
+ ~QtHDC()
+ {
+ if (_hdc)
+ DeleteDC(_hdc);
+ }
+ HDC hdc() const
+ {
+ return _hdc;
+ }
+};
+
+#ifndef QT_NO_THREAD
+Q_GLOBAL_STATIC(QThreadStorage<QtHDC *>, local_shared_dc)
+HDC shared_dc()
+{
+ QtHDC *&hdc = local_shared_dc()->localData();
+ if (!hdc)
+ hdc = new QtHDC;
+ return hdc->hdc();
+}
+#else
+HDC shared_dc()
+{
+ return 0;
+}
+#endif
+
+#ifndef Q_WS_WINCE
+typedef BOOL (WINAPI *PtrGetCharWidthI)(HDC, UINT, UINT, LPWORD, LPINT);
+static PtrGetCharWidthI ptrGetCharWidthI = 0;
+static bool resolvedGetCharWidthI = false;
+
+static void resolveGetCharWidthI()
+{
+ if (resolvedGetCharWidthI)
+ return;
+ resolvedGetCharWidthI = true;
+ ptrGetCharWidthI = (PtrGetCharWidthI)QSystemLibrary::resolve(QLatin1String("gdi32"), "GetCharWidthI");
+}
+#endif // !defined(Q_WS_WINCE)
+
+// defined in qtextengine_win.cpp
+typedef void *SCRIPT_CACHE;
+typedef HRESULT (WINAPI *fScriptFreeCache)(SCRIPT_CACHE *);
+extern fScriptFreeCache ScriptFreeCache;
+
+static inline quint32 getUInt(unsigned char *p)
+{
+ quint32 val;
+ val = *p++ << 24;
+ val |= *p++ << 16;
+ val |= *p++ << 8;
+ val |= *p;
+
+ return val;
+}
+
+static inline quint16 getUShort(unsigned char *p)
+{
+ quint16 val;
+ val = *p++ << 8;
+ val |= *p;
+
+ return val;
+}
+
+// general font engine
+
+QFixed QFontEngineWin::lineThickness() const
+{
+ if(lineWidth > 0)
+ return lineWidth;
+
+ return QFontEngine::lineThickness();
+}
+
+static OUTLINETEXTMETRIC *getOutlineTextMetric(HDC hdc)
+{
+ int size;
+ size = GetOutlineTextMetrics(hdc, 0, 0);
+ OUTLINETEXTMETRIC *otm = (OUTLINETEXTMETRIC *)malloc(size);
+ GetOutlineTextMetrics(hdc, size, otm);
+ return otm;
+}
+
+void QFontEngineWin::getCMap()
+{
+ ttf = (bool)(tm.tmPitchAndFamily & TMPF_TRUETYPE);
+ HDC hdc = shared_dc();
+ SelectObject(hdc, hfont);
+ bool symb = false;
+ if (ttf) {
+ cmapTable = getSfntTable(qbswap<quint32>(MAKE_TAG('c', 'm', 'a', 'p')));
+ int size = 0;
+ cmap = QFontEngine::getCMap(reinterpret_cast<const uchar *>(cmapTable.constData()),
+ cmapTable.size(), &symb, &size);
+ }
+ if (!cmap) {
+ ttf = false;
+ symb = false;
+ }
+ symbol = symb;
+ designToDevice = 1;
+ _faceId.index = 0;
+ if(cmap) {
+ OUTLINETEXTMETRIC *otm = getOutlineTextMetric(hdc);
+ designToDevice = QFixed((int)otm->otmEMSquare)/int(otm->otmTextMetrics.tmHeight);
+ unitsPerEm = otm->otmEMSquare;
+ x_height = (int)otm->otmsXHeight;
+ loadKerningPairs(designToDevice);
+ _faceId.filename = QString::fromWCharArray((wchar_t *)((char *)otm + (quintptr)otm->otmpFullName)).toLatin1();
+ lineWidth = otm->otmsUnderscoreSize;
+ fsType = otm->otmfsType;
+ free(otm);
+ } else {
+ unitsPerEm = tm.tmHeight;
+ }
+}
+
+
+inline unsigned int getChar(const QChar *str, int &i, const int len)
+{
+ uint ucs4 = str[i].unicode();
+ if (str[i].isHighSurrogate() && i < len-1 && str[i+1].isLowSurrogate()) {
+ ++i;
+ ucs4 = QChar::surrogateToUcs4(ucs4, str[i].unicode());
+ }
+ return ucs4;
+}
+
+int QFontEngineWin::getGlyphIndexes(const QChar *str, int numChars, QGlyphLayout *glyphs, bool mirrored) const
+{
+ int i = 0;
+ int glyph_pos = 0;
+ if (mirrored) {
+#if defined(Q_WS_WINCE)
+ {
+#else
+ if (symbol) {
+ for (; i < numChars; ++i, ++glyph_pos) {
+ unsigned int uc = getChar(str, i, numChars);
+ glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc);
+ if (!glyphs->glyphs[glyph_pos] && uc < 0x100)
+ glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc + 0xf000);
+ }
+ } else if (ttf) {
+ for (; i < numChars; ++i, ++glyph_pos) {
+ unsigned int uc = getChar(str, i, numChars);
+ glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, QChar::mirroredChar(uc));
+ }
+ } else {
+#endif
+ wchar_t first = tm.tmFirstChar;
+ wchar_t last = tm.tmLastChar;
+
+ for (; i < numChars; ++i, ++glyph_pos) {
+ uint ucs = QChar::mirroredChar(getChar(str, i, numChars));
+ if (
+#ifdef Q_WS_WINCE
+ tm.tmFirstChar > 60000 || // see line 375
+#endif
+ ucs >= first && ucs <= last)
+ glyphs->glyphs[glyph_pos] = ucs;
+ else
+ glyphs->glyphs[glyph_pos] = 0;
+ }
+ }
+ } else {
+#if defined(Q_WS_WINCE)
+ {
+#else
+ if (symbol) {
+ for (; i < numChars; ++i, ++glyph_pos) {
+ unsigned int uc = getChar(str, i, numChars);
+ glyphs->glyphs[i] = getTrueTypeGlyphIndex(cmap, uc);
+ if(!glyphs->glyphs[glyph_pos] && uc < 0x100)
+ glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc + 0xf000);
+ }
+ } else if (ttf) {
+ for (; i < numChars; ++i, ++glyph_pos) {
+ unsigned int uc = getChar(str, i, numChars);
+ glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc);
+ }
+ } else {
+#endif
+ wchar_t first = tm.tmFirstChar;
+ wchar_t last = tm.tmLastChar;
+
+ for (; i < numChars; ++i, ++glyph_pos) {
+ uint uc = getChar(str, i, numChars);
+ if (
+#ifdef Q_WS_WINCE
+ tm.tmFirstChar > 60000 || // see comment in QFontEngineWin
+#endif
+ uc >= first && uc <= last)
+ glyphs->glyphs[glyph_pos] = uc;
+ else
+ glyphs->glyphs[glyph_pos] = 0;
+ }
+ }
+ }
+ glyphs->numGlyphs = glyph_pos;
+ return glyph_pos;
+}
+
+
+QFontEngineWin::QFontEngineWin(const QString &name, HFONT _hfont, bool stockFont, LOGFONT lf)
+{
+ //qDebug("regular windows font engine created: font='%s', size=%d", name, lf.lfHeight);
+
+ _name = name;
+
+ cmap = 0;
+ hfont = _hfont;
+ logfont = lf;
+ HDC hdc = shared_dc();
+ SelectObject(hdc, hfont);
+ this->stockFont = stockFont;
+ fontDef.pixelSize = -lf.lfHeight;
+
+ lbearing = SHRT_MIN;
+ rbearing = SHRT_MIN;
+ synthesized_flags = -1;
+ lineWidth = -1;
+ x_height = -1;
+
+ BOOL res = GetTextMetrics(hdc, &tm);
+ fontDef.fixedPitch = !(tm.tmPitchAndFamily & TMPF_FIXED_PITCH);
+ if (!res) {
+ qErrnoWarning("QFontEngineWin: GetTextMetrics failed");
+ ZeroMemory(&tm, sizeof(TEXTMETRIC));
+ }
+
+ cache_cost = tm.tmHeight * tm.tmAveCharWidth * 2000;
+ getCMap();
+
+ widthCache = 0;
+ widthCacheSize = 0;
+ designAdvances = 0;
+ designAdvancesSize = 0;
+
+#ifndef Q_WS_WINCE
+ if (!resolvedGetCharWidthI)
+ resolveGetCharWidthI();
+#endif
+}
+
+QFontEngineWin::~QFontEngineWin()
+{
+ if (designAdvances)
+ free(designAdvances);
+
+ if (widthCache)
+ free(widthCache);
+
+ // make sure we aren't by accident still selected
+ SelectObject(shared_dc(), (HFONT)GetStockObject(SYSTEM_FONT));
+
+ if (!stockFont) {
+ if (!DeleteObject(hfont))
+ qErrnoWarning("QFontEngineWin: failed to delete non-stock font...");
+ }
+}
+
+HGDIOBJ QFontEngineWin::selectDesignFont() const
+{
+ LOGFONT f = logfont;
+ f.lfHeight = unitsPerEm;
+ HFONT designFont = CreateFontIndirect(&f);
+ return SelectObject(shared_dc(), designFont);
+}
+
+bool QFontEngineWin::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const
+{
+ if (*nglyphs < len) {
+ *nglyphs = len;
+ return false;
+ }
+
+ *nglyphs = getGlyphIndexes(str, len, glyphs, flags & QTextEngine::RightToLeft);
+
+ if (flags & QTextEngine::GlyphIndicesOnly)
+ return true;
+
+ recalcAdvances(glyphs, flags);
+ return true;
+}
+
+inline void calculateTTFGlyphWidth(HDC hdc, UINT glyph, int &width)
+{
+#if defined(Q_WS_WINCE)
+ GetCharWidth32(hdc, glyph, glyph, &width);
+#else
+ if (ptrGetCharWidthI)
+ ptrGetCharWidthI(hdc, glyph, 1, 0, &width);
+#endif
+}
+
+void QFontEngineWin::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const
+{
+ HGDIOBJ oldFont = 0;
+ HDC hdc = shared_dc();
+ if (ttf && (flags & QTextEngine::DesignMetrics)) {
+ for(int i = 0; i < glyphs->numGlyphs; i++) {
+ unsigned int glyph = glyphs->glyphs[i];
+ if(int(glyph) >= designAdvancesSize) {
+ int newSize = (glyph + 256) >> 8 << 8;
+ designAdvances = q_check_ptr((QFixed *)realloc(designAdvances,
+ newSize*sizeof(QFixed)));
+ for(int i = designAdvancesSize; i < newSize; ++i)
+ designAdvances[i] = -1000000;
+ designAdvancesSize = newSize;
+ }
+ if (designAdvances[glyph] < -999999) {
+ if (!oldFont)
+ oldFont = selectDesignFont();
+
+ int width = 0;
+ calculateTTFGlyphWidth(hdc, glyph, width);
+ designAdvances[glyph] = QFixed(width) / designToDevice;
+ }
+ glyphs->advances_x[i] = designAdvances[glyph];
+ glyphs->advances_y[i] = 0;
+ }
+ if(oldFont)
+ DeleteObject(SelectObject(hdc, oldFont));
+ } else {
+ for(int i = 0; i < glyphs->numGlyphs; i++) {
+ unsigned int glyph = glyphs->glyphs[i];
+
+ glyphs->advances_y[i] = 0;
+
+ if (glyph >= widthCacheSize) {
+ int newSize = (glyph + 256) >> 8 << 8;
+ widthCache = q_check_ptr((unsigned char *)realloc(widthCache,
+ newSize*sizeof(QFixed)));
+ memset(widthCache + widthCacheSize, 0, newSize - widthCacheSize);
+ widthCacheSize = newSize;
+ }
+ glyphs->advances_x[i] = widthCache[glyph];
+ // font-width cache failed
+ if (glyphs->advances_x[i] == 0) {
+ int width = 0;
+ if (!oldFont)
+ oldFont = SelectObject(hdc, hfont);
+
+ if (!ttf) {
+ QChar ch[2] = { ushort(glyph), 0 };
+ int chrLen = 1;
+ if (glyph > 0xffff) {
+ ch[0] = QChar::highSurrogate(glyph);
+ ch[1] = QChar::lowSurrogate(glyph);
+ ++chrLen;
+ }
+ SIZE size = {0, 0};
+ GetTextExtentPoint32(hdc, (wchar_t *)ch, chrLen, &size);
+ width = size.cx;
+ } else {
+ calculateTTFGlyphWidth(hdc, glyph, width);
+ }
+ glyphs->advances_x[i] = width;
+ // if glyph's within cache range, store it for later
+ if (width > 0 && width < 0x100)
+ widthCache[glyph] = width;
+ }
+ }
+
+ if (oldFont)
+ SelectObject(hdc, oldFont);
+ }
+}
+
+glyph_metrics_t QFontEngineWin::boundingBox(const QGlyphLayout &glyphs)
+{
+ if (glyphs.numGlyphs == 0)
+ return glyph_metrics_t();
+
+ QFixed w = 0;
+ for (int i = 0; i < glyphs.numGlyphs; ++i)
+ w += glyphs.effectiveAdvance(i);
+
+ return glyph_metrics_t(0, -tm.tmAscent, w - lastRightBearing(glyphs), tm.tmHeight, w, 0);
+}
+
+#ifndef Q_WS_WINCE
+bool QFontEngineWin::getOutlineMetrics(glyph_t glyph, const QTransform &t, glyph_metrics_t *metrics) const
+{
+ Q_ASSERT(metrics != 0);
+
+ HDC hdc = shared_dc();
+
+ GLYPHMETRICS gm;
+ DWORD res = 0;
+ MAT2 mat;
+ mat.eM11.value = mat.eM22.value = 1;
+ mat.eM11.fract = mat.eM22.fract = 0;
+ mat.eM21.value = mat.eM12.value = 0;
+ mat.eM21.fract = mat.eM12.fract = 0;
+
+ if (t.type() > QTransform::TxTranslate) {
+ // We need to set the transform using the HDC's world
+ // matrix rather than using the MAT2 above, because the
+ // results provided when transforming via MAT2 does not
+ // match the glyphs that are drawn using a WorldTransform
+ XFORM xform;
+ xform.eM11 = t.m11();
+ xform.eM12 = t.m12();
+ xform.eM21 = t.m21();
+ xform.eM22 = t.m22();
+ xform.eDx = 0;
+ xform.eDy = 0;
+ SetGraphicsMode(hdc, GM_ADVANCED);
+ SetWorldTransform(hdc, &xform);
+ }
+
+ uint format = GGO_METRICS;
+ if (ttf)
+ format |= GGO_GLYPH_INDEX;
+ res = GetGlyphOutline(hdc, glyph, format, &gm, 0, 0, &mat);
+
+ if (t.type() > QTransform::TxTranslate) {
+ XFORM xform;
+ xform.eM11 = xform.eM22 = 1;
+ xform.eM12 = xform.eM21 = xform.eDx = xform.eDy = 0;
+ SetWorldTransform(hdc, &xform);
+ SetGraphicsMode(hdc, GM_COMPATIBLE);
+ }
+
+ if (res != GDI_ERROR) {
+ *metrics = glyph_metrics_t(gm.gmptGlyphOrigin.x, -gm.gmptGlyphOrigin.y,
+ (int)gm.gmBlackBoxX, (int)gm.gmBlackBoxY, gm.gmCellIncX, gm.gmCellIncY);
+ return true;
+ } else {
+ return false;
+ }
+}
+#endif
+
+glyph_metrics_t QFontEngineWin::boundingBox(glyph_t glyph, const QTransform &t)
+{
+#ifndef Q_WS_WINCE
+ HDC hdc = shared_dc();
+ SelectObject(hdc, hfont);
+
+ glyph_metrics_t glyphMetrics;
+ bool success = getOutlineMetrics(glyph, t, &glyphMetrics);
+
+ if (!ttf && !success) {
+ // Bitmap fonts
+ wchar_t ch = glyph;
+ ABCFLOAT abc;
+ GetCharABCWidthsFloat(hdc, ch, ch, &abc);
+ int width = qRound(abc.abcfB);
+
+ return glyph_metrics_t(QFixed::fromReal(abc.abcfA), -tm.tmAscent, width, tm.tmHeight, width, 0).transformed(t);
+ }
+
+ return glyphMetrics;
+#else
+ HDC hdc = shared_dc();
+ HGDIOBJ oldFont = SelectObject(hdc, hfont);
+
+ ABC abc;
+ int width;
+ int advance;
+#ifdef GWES_MGTT // true type fonts
+ if (GetCharABCWidths(hdc, glyph, glyph, &abc)) {
+ width = qAbs(abc.abcA) + abc.abcB + qAbs(abc.abcC);
+ advance = abc.abcA + abc.abcB + abc.abcC;
+ }
+ else
+#endif
+#if defined(GWES_MGRAST) || defined(GWES_MGRAST2) // raster fonts
+ if (GetCharWidth32(hdc, glyph, glyph, &width)) {
+ advance = width;
+ }
+ else
+#endif
+ { // fallback
+ width = tm.tmMaxCharWidth;
+ advance = width;
+ }
+
+ SelectObject(hdc, oldFont);
+ return glyph_metrics_t(0, -tm.tmAscent, width, tm.tmHeight, advance, 0).transformed(t);
+#endif
+}
+
+QFixed QFontEngineWin::ascent() const
+{
+ return tm.tmAscent;
+}
+
+QFixed QFontEngineWin::descent() const
+{
+ // ### we substract 1 to even out the historical +1 in QFontMetrics's
+ // ### height=asc+desc+1 equation. Fix in Qt5.
+ return tm.tmDescent - 1;
+}
+
+QFixed QFontEngineWin::leading() const
+{
+ return tm.tmExternalLeading;
+}
+
+
+QFixed QFontEngineWin::xHeight() const
+{
+ if(x_height >= 0)
+ return x_height;
+ return QFontEngine::xHeight();
+}
+
+QFixed QFontEngineWin::averageCharWidth() const
+{
+ return tm.tmAveCharWidth;
+}
+
+qreal QFontEngineWin::maxCharWidth() const
+{
+ return tm.tmMaxCharWidth;
+}
+
+enum { max_font_count = 256 };
+static const ushort char_table[] = {
+ 40,
+ 67,
+ 70,
+ 75,
+ 86,
+ 88,
+ 89,
+ 91,
+ 102,
+ 114,
+ 124,
+ 127,
+ 205,
+ 645,
+ 884,
+ 922,
+ 1070,
+ 12386,
+ 0
+};
+
+static const int char_table_entries = sizeof(char_table)/sizeof(ushort);
+
+#ifndef Q_CC_MINGW
+void QFontEngineWin::getGlyphBearings(glyph_t glyph, qreal *leftBearing, qreal *rightBearing)
+{
+ HDC hdc = shared_dc();
+ SelectObject(hdc, hfont);
+
+#ifndef Q_WS_WINCE
+ if (ttf)
+#endif
+
+ {
+ ABC abcWidths;
+ GetCharABCWidthsI(hdc, glyph, 1, 0, &abcWidths);
+ if (leftBearing)
+ *leftBearing = abcWidths.abcA;
+ if (rightBearing)
+ *rightBearing = abcWidths.abcC;
+ }
+
+#ifndef Q_WS_WINCE
+ else {
+ QFontEngine::getGlyphBearings(glyph, leftBearing, rightBearing);
+ }
+#endif
+}
+#endif // Q_CC_MINGW
+
+qreal QFontEngineWin::minLeftBearing() const
+{
+ if (lbearing == SHRT_MIN)
+ minRightBearing(); // calculates both
+
+ return lbearing;
+}
+
+qreal QFontEngineWin::minRightBearing() const
+{
+#ifdef Q_WS_WINCE
+ if (rbearing == SHRT_MIN) {
+ int ml = 0;
+ int mr = 0;
+ HDC hdc = shared_dc();
+ SelectObject(hdc, hfont);
+ if (ttf) {
+ ABC *abc = 0;
+ int n = tm.tmLastChar - tm.tmFirstChar;
+ if (n <= max_font_count) {
+ abc = new ABC[n+1];
+ GetCharABCWidths(hdc, tm.tmFirstChar, tm.tmLastChar, abc);
+ } else {
+ abc = new ABC[char_table_entries+1];
+ for(int i = 0; i < char_table_entries; i++)
+ GetCharABCWidths(hdc, char_table[i], char_table[i], abc+i);
+ n = char_table_entries;
+ }
+ ml = abc[0].abcA;
+ mr = abc[0].abcC;
+ for (int i = 1; i < n; i++) {
+ if (abc[i].abcA + abc[i].abcB + abc[i].abcC != 0) {
+ ml = qMin(ml,abc[i].abcA);
+ mr = qMin(mr,abc[i].abcC);
+ }
+ }
+ delete [] abc;
+ }
+ lbearing = ml;
+ rbearing = mr;
+ }
+
+ return rbearing;
+#else
+ if (rbearing == SHRT_MIN) {
+ int ml = 0;
+ int mr = 0;
+ HDC hdc = shared_dc();
+ SelectObject(hdc, hfont);
+ if (ttf) {
+ ABC *abc = 0;
+ int n = tm.tmLastChar - tm.tmFirstChar;
+ if (n <= max_font_count) {
+ abc = new ABC[n+1];
+ GetCharABCWidths(hdc, tm.tmFirstChar, tm.tmLastChar, abc);
+ } else {
+ abc = new ABC[char_table_entries+1];
+ for(int i = 0; i < char_table_entries; i++)
+ GetCharABCWidths(hdc, char_table[i], char_table[i], abc + i);
+ n = char_table_entries;
+ }
+ ml = abc[0].abcA;
+ mr = abc[0].abcC;
+ for (int i = 1; i < n; i++) {
+ if (abc[i].abcA + abc[i].abcB + abc[i].abcC != 0) {
+ ml = qMin(ml,abc[i].abcA);
+ mr = qMin(mr,abc[i].abcC);
+ }
+ }
+ delete [] abc;
+ } else {
+ ABCFLOAT *abc = 0;
+ int n = tm.tmLastChar - tm.tmFirstChar+1;
+ if (n <= max_font_count) {
+ abc = new ABCFLOAT[n];
+ GetCharABCWidthsFloat(hdc, tm.tmFirstChar, tm.tmLastChar, abc);
+ } else {
+ abc = new ABCFLOAT[char_table_entries];
+ for(int i = 0; i < char_table_entries; i++)
+ GetCharABCWidthsFloat(hdc, char_table[i], char_table[i], abc+i);
+ n = char_table_entries;
+ }
+ float fml = abc[0].abcfA;
+ float fmr = abc[0].abcfC;
+ for (int i=1; i<n; i++) {
+ if (abc[i].abcfA + abc[i].abcfB + abc[i].abcfC != 0) {
+ fml = qMin(fml,abc[i].abcfA);
+ fmr = qMin(fmr,abc[i].abcfC);
+ }
+ }
+ ml = int(fml - 0.9999);
+ mr = int(fmr - 0.9999);
+ delete [] abc;
+ }
+ lbearing = ml;
+ rbearing = mr;
+ }
+
+ return rbearing;
+#endif
+}
+
+
+const char *QFontEngineWin::name() const
+{
+ return 0;
+}
+
+bool QFontEngineWin::canRender(const QChar *string, int len)
+{
+ if (symbol) {
+ for (int i = 0; i < len; ++i) {
+ unsigned int uc = getChar(string, i, len);
+ if (getTrueTypeGlyphIndex(cmap, uc) == 0) {
+ if (uc < 0x100) {
+ if (getTrueTypeGlyphIndex(cmap, uc + 0xf000) == 0)
+ return false;
+ } else {
+ return false;
+ }
+ }
+ }
+ } else if (ttf) {
+ for (int i = 0; i < len; ++i) {
+ unsigned int uc = getChar(string, i, len);
+ if (getTrueTypeGlyphIndex(cmap, uc) == 0)
+ return false;
+ }
+ } else {
+ while(len--) {
+ if (tm.tmFirstChar > string->unicode() || tm.tmLastChar < string->unicode())
+ return false;
+ }
+ }
+ return true;
+}
+
+QFontEngine::Type QFontEngineWin::type() const
+{
+ return QFontEngine::Win;
+}
+
+static inline double qt_fixed_to_double(const FIXED &p) {
+ return ((p.value << 16) + p.fract) / 65536.0;
+}
+
+static inline QPointF qt_to_qpointf(const POINTFX &pt, qreal scale) {
+ return QPointF(qt_fixed_to_double(pt.x) * scale, -qt_fixed_to_double(pt.y) * scale);
+}
+
+#ifndef GGO_UNHINTED
+#define GGO_UNHINTED 0x0100
+#endif
+
+static bool addGlyphToPath(glyph_t glyph, const QFixedPoint &position, HDC hdc,
+ QPainterPath *path, bool ttf, glyph_metrics_t *metric = 0, qreal scale = 1)
+{
+#if defined(Q_WS_WINCE)
+ Q_UNUSED(glyph);
+ Q_UNUSED(hdc);
+#endif
+ MAT2 mat;
+ mat.eM11.value = mat.eM22.value = 1;
+ mat.eM11.fract = mat.eM22.fract = 0;
+ mat.eM21.value = mat.eM12.value = 0;
+ mat.eM21.fract = mat.eM12.fract = 0;
+ uint glyphFormat = GGO_NATIVE;
+
+ if (ttf)
+ glyphFormat |= GGO_GLYPH_INDEX;
+
+ GLYPHMETRICS gMetric;
+ memset(&gMetric, 0, sizeof(GLYPHMETRICS));
+ int bufferSize = GDI_ERROR;
+#if !defined(Q_WS_WINCE)
+ bufferSize = GetGlyphOutline(hdc, glyph, glyphFormat, &gMetric, 0, 0, &mat);
+#endif
+ if ((DWORD)bufferSize == GDI_ERROR) {
+ return false;
+ }
+
+ void *dataBuffer = new char[bufferSize];
+ DWORD ret = GDI_ERROR;
+#if !defined(Q_WS_WINCE)
+ ret = GetGlyphOutline(hdc, glyph, glyphFormat, &gMetric, bufferSize, dataBuffer, &mat);
+#endif
+ if (ret == GDI_ERROR) {
+ delete [](char *)dataBuffer;
+ return false;
+ }
+
+ if(metric) {
+ // #### obey scale
+ *metric = glyph_metrics_t(gMetric.gmptGlyphOrigin.x, -gMetric.gmptGlyphOrigin.y,
+ (int)gMetric.gmBlackBoxX, (int)gMetric.gmBlackBoxY,
+ gMetric.gmCellIncX, gMetric.gmCellIncY);
+ }
+
+ int offset = 0;
+ int headerOffset = 0;
+ TTPOLYGONHEADER *ttph = 0;
+
+ QPointF oset = position.toPointF();
+ while (headerOffset < bufferSize) {
+ ttph = (TTPOLYGONHEADER*)((char *)dataBuffer + headerOffset);
+
+ QPointF lastPoint(qt_to_qpointf(ttph->pfxStart, scale));
+ path->moveTo(lastPoint + oset);
+ offset += sizeof(TTPOLYGONHEADER);
+ TTPOLYCURVE *curve;
+ while (offset<int(headerOffset + ttph->cb)) {
+ curve = (TTPOLYCURVE*)((char*)(dataBuffer) + offset);
+ switch (curve->wType) {
+ case TT_PRIM_LINE: {
+ for (int i=0; i<curve->cpfx; ++i) {
+ QPointF p = qt_to_qpointf(curve->apfx[i], scale) + oset;
+ path->lineTo(p);
+ }
+ break;
+ }
+ case TT_PRIM_QSPLINE: {
+ const QPainterPath::Element &elm = path->elementAt(path->elementCount()-1);
+ QPointF prev(elm.x, elm.y);
+ QPointF endPoint;
+ for (int i=0; i<curve->cpfx - 1; ++i) {
+ QPointF p1 = qt_to_qpointf(curve->apfx[i], scale) + oset;
+ QPointF p2 = qt_to_qpointf(curve->apfx[i+1], scale) + oset;
+ if (i < curve->cpfx - 2) {
+ endPoint = QPointF((p1.x() + p2.x()) / 2, (p1.y() + p2.y()) / 2);
+ } else {
+ endPoint = p2;
+ }
+
+ path->quadTo(p1, endPoint);
+ prev = endPoint;
+ }
+
+ break;
+ }
+ case TT_PRIM_CSPLINE: {
+ for (int i=0; i<curve->cpfx; ) {
+ QPointF p2 = qt_to_qpointf(curve->apfx[i++], scale) + oset;
+ QPointF p3 = qt_to_qpointf(curve->apfx[i++], scale) + oset;
+ QPointF p4 = qt_to_qpointf(curve->apfx[i++], scale) + oset;
+ path->cubicTo(p2, p3, p4);
+ }
+ break;
+ }
+ default:
+ qWarning("QFontEngineWin::addOutlineToPath, unhandled switch case");
+ }
+ offset += sizeof(TTPOLYCURVE) + (curve->cpfx-1) * sizeof(POINTFX);
+ }
+ path->closeSubpath();
+ headerOffset += ttph->cb;
+ }
+ delete [] (char*)dataBuffer;
+
+ return true;
+}
+
+void QFontEngineWin::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs,
+ QPainterPath *path, QTextItem::RenderFlags)
+{
+ LOGFONT lf = logfont;
+ // The sign must be negative here to make sure we match against character height instead of
+ // hinted cell height. This ensures that we get linear matching, and we need this for
+ // paths since we later on apply a scaling transform to the glyph outline to get the
+ // font at the correct pixel size.
+ lf.lfHeight = -unitsPerEm;
+ lf.lfWidth = 0;
+ HFONT hf = CreateFontIndirect(&lf);
+ HDC hdc = shared_dc();
+ HGDIOBJ oldfont = SelectObject(hdc, hf);
+
+ for(int i = 0; i < nglyphs; ++i) {
+ if (!addGlyphToPath(glyphs[i], positions[i], hdc, path, ttf, /*metric*/0,
+ qreal(fontDef.pixelSize) / unitsPerEm)) {
+ // Some windows fonts, like "Modern", are vector stroke
+ // fonts, which are reported as TMPF_VECTOR but do not
+ // support GetGlyphOutline, and thus we set this bit so
+ // that addOutLineToPath can check it and return safely...
+ hasOutline = false;
+ break;
+ }
+ }
+ DeleteObject(SelectObject(hdc, oldfont));
+}
+
+void QFontEngineWin::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs,
+ QPainterPath *path, QTextItem::RenderFlags flags)
+{
+#if !defined(Q_WS_WINCE)
+ if(tm.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_VECTOR)) {
+ hasOutline = true;
+ QFontEngine::addOutlineToPath(x, y, glyphs, path, flags);
+ if (hasOutline) {
+ // has_outline is set to false if addGlyphToPath gets
+ // false from GetGlyphOutline, meaning its not an outline
+ // font.
+ return;
+ }
+ }
+#endif
+ QFontEngine::addBitmapFontToPath(x, y, glyphs, path, flags);
+}
+
+QFontEngine::FaceId QFontEngineWin::faceId() const
+{
+ return _faceId;
+}
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <qdebug.h>
+QT_END_INCLUDE_NAMESPACE
+
+int QFontEngineWin::synthesized() const
+{
+ if(synthesized_flags == -1) {
+ synthesized_flags = 0;
+ if(ttf) {
+ const DWORD HEAD = MAKE_TAG('h', 'e', 'a', 'd');
+ HDC hdc = shared_dc();
+ SelectObject(hdc, hfont);
+ uchar data[4];
+ GetFontData(hdc, HEAD, 44, &data, 4);
+ USHORT macStyle = getUShort(data);
+ if (tm.tmItalic && !(macStyle & 2))
+ synthesized_flags = SynthesizedItalic;
+ if (fontDef.stretch != 100 && ttf)
+ synthesized_flags |= SynthesizedStretch;
+ if (tm.tmWeight >= 500 && !(macStyle & 1))
+ synthesized_flags |= SynthesizedBold;
+ //qDebug() << "font is" << _name <<
+ // "it=" << (macStyle & 2) << fontDef.style << "flags=" << synthesized_flags;
+ }
+ }
+ return synthesized_flags;
+}
+
+QFixed QFontEngineWin::emSquareSize() const
+{
+ return unitsPerEm;
+}
+
+QFontEngine::Properties QFontEngineWin::properties() const
+{
+ LOGFONT lf = logfont;
+ lf.lfHeight = unitsPerEm;
+ HFONT hf = CreateFontIndirect(&lf);
+ HDC hdc = shared_dc();
+ HGDIOBJ oldfont = SelectObject(hdc, hf);
+ OUTLINETEXTMETRIC *otm = getOutlineTextMetric(hdc);
+ Properties p;
+ p.emSquare = unitsPerEm;
+ p.italicAngle = otm->otmItalicAngle;
+ p.postscriptName = QString::fromWCharArray((wchar_t *)((char *)otm + (quintptr)otm->otmpFamilyName)).toLatin1();
+ p.postscriptName += QString::fromWCharArray((wchar_t *)((char *)otm + (quintptr)otm->otmpStyleName)).toLatin1();
+ p.postscriptName = QFontEngine::convertToPostscriptFontFamilyName(p.postscriptName);
+ p.boundingBox = QRectF(otm->otmrcFontBox.left, -otm->otmrcFontBox.top,
+ otm->otmrcFontBox.right - otm->otmrcFontBox.left,
+ otm->otmrcFontBox.top - otm->otmrcFontBox.bottom);
+ p.ascent = otm->otmAscent;
+ p.descent = -otm->otmDescent;
+ p.leading = (int)otm->otmLineGap;
+ p.capHeight = 0;
+ p.lineWidth = otm->otmsUnderscoreSize;
+ free(otm);
+ DeleteObject(SelectObject(hdc, oldfont));
+ return p;
+}
+
+void QFontEngineWin::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics)
+{
+ LOGFONT lf = logfont;
+ lf.lfHeight = unitsPerEm;
+ int flags = synthesized();
+ if(flags & SynthesizedItalic)
+ lf.lfItalic = false;
+ lf.lfWidth = 0;
+ HFONT hf = CreateFontIndirect(&lf);
+ HDC hdc = shared_dc();
+ HGDIOBJ oldfont = SelectObject(hdc, hf);
+ QFixedPoint p;
+ p.x = 0;
+ p.y = 0;
+ addGlyphToPath(glyph, p, hdc, path, ttf, metrics);
+ DeleteObject(SelectObject(hdc, oldfont));
+}
+
+bool QFontEngineWin::getSfntTableData(uint tag, uchar *buffer, uint *length) const
+{
+ if (!ttf)
+ return false;
+ HDC hdc = shared_dc();
+ SelectObject(hdc, hfont);
+ DWORD t = qbswap<quint32>(tag);
+ *length = GetFontData(hdc, t, 0, buffer, *length);
+ return *length != GDI_ERROR;
+}
+
+#if !defined(CLEARTYPE_QUALITY)
+# define CLEARTYPE_QUALITY 5
+#endif
+
+extern bool qt_cleartype_enabled;
+
+QNativeImage *QFontEngineWin::drawGDIGlyph(HFONT font, glyph_t glyph, int margin,
+ const QTransform &t, QImage::Format mask_format)
+{
+ Q_UNUSED(mask_format)
+ glyph_metrics_t gm = boundingBox(glyph);
+
+// printf(" -> for glyph %4x\n", glyph);
+
+ int gx = gm.x.toInt();
+ int gy = gm.y.toInt();
+ int iw = gm.width.toInt();
+ int ih = gm.height.toInt();
+
+ if (iw <= 0 || iw <= 0)
+ return 0;
+
+ bool has_transformation = t.type() > QTransform::TxTranslate;
+
+#ifndef Q_WS_WINCE
+ unsigned int options = ttf ? ETO_GLYPH_INDEX : 0;
+ XFORM xform;
+
+ if (has_transformation) {
+ xform.eM11 = t.m11();
+ xform.eM12 = t.m12();
+ xform.eM21 = t.m21();
+ xform.eM22 = t.m22();
+ xform.eDx = margin;
+ xform.eDy = margin;
+
+ QtHDC qthdc;
+ HDC hdc = qthdc.hdc();
+
+ SetGraphicsMode(hdc, GM_ADVANCED);
+ SetWorldTransform(hdc, &xform);
+ HGDIOBJ old_font = SelectObject(hdc, font);
+
+ int ggo_options = GGO_METRICS | (ttf ? GGO_GLYPH_INDEX : 0);
+ GLYPHMETRICS tgm;
+ MAT2 mat;
+ memset(&mat, 0, sizeof(mat));
+ mat.eM11.value = mat.eM22.value = 1;
+
+ if (GetGlyphOutline(hdc, glyph, ggo_options, &tgm, 0, 0, &mat) == GDI_ERROR) {
+ qWarning("QWinFontEngine: unable to query transformed glyph metrics...");
+ return 0;
+ }
+
+ iw = tgm.gmBlackBoxX;
+ ih = tgm.gmBlackBoxY;
+
+ xform.eDx -= tgm.gmptGlyphOrigin.x;
+ xform.eDy += tgm.gmptGlyphOrigin.y;
+
+ SetGraphicsMode(hdc, GM_COMPATIBLE);
+ SelectObject(hdc, old_font);
+ }
+#else // else winc
+ unsigned int options = 0;
+#ifdef DEBUG
+ Q_ASSERT(!has_transformation);
+#else
+ Q_UNUSED(has_transformation);
+#endif
+#endif
+
+ QNativeImage *ni = new QNativeImage(iw + 2 * margin + 4,
+ ih + 2 * margin + 4,
+ QNativeImage::systemFormat(), !qt_cleartype_enabled);
+
+ /*If cleartype is enabled we use the standard system format even on Windows CE
+ and not the special textbuffer format we have to use if cleartype is disabled*/
+
+ ni->image.fill(0xffffffff);
+
+ HDC hdc = ni->hdc;
+
+ SelectObject(hdc, GetStockObject(NULL_BRUSH));
+ SelectObject(hdc, GetStockObject(BLACK_PEN));
+ SetTextColor(hdc, RGB(0,0,0));
+ SetBkMode(hdc, TRANSPARENT);
+ SetTextAlign(hdc, TA_BASELINE);
+
+ HGDIOBJ old_font = SelectObject(hdc, font);
+
+#ifndef Q_OS_WINCE
+ if (has_transformation) {
+ SetGraphicsMode(hdc, GM_ADVANCED);
+ SetWorldTransform(hdc, &xform);
+ ExtTextOut(hdc, 0, 0, options, 0, (LPCWSTR) &glyph, 1, 0);
+ } else
+#endif
+ {
+ ExtTextOut(hdc, -gx + margin, -gy + margin, options, 0, (LPCWSTR) &glyph, 1, 0);
+ }
+
+ SelectObject(hdc, old_font);
+ return ni;
+}
+
+
+extern uint qt_pow_gamma[256];
+
+QImage QFontEngineWin::alphaMapForGlyph(glyph_t glyph, const QTransform &xform)
+{
+ HFONT font = hfont;
+ if (qt_cleartype_enabled) {
+ LOGFONT lf = logfont;
+ lf.lfQuality = ANTIALIASED_QUALITY;
+ font = CreateFontIndirect(&lf);
+ }
+ QImage::Format mask_format = QNativeImage::systemFormat();
+#ifndef Q_OS_WINCE
+ mask_format = QImage::Format_RGB32;
+#endif
+
+ QNativeImage *mask = drawGDIGlyph(font, glyph, 0, xform, mask_format);
+ if (mask == 0)
+ return QImage();
+
+ QImage indexed(mask->width(), mask->height(), QImage::Format_Indexed8);
+
+ // ### This part is kinda pointless, but we'll crash later if we don't because some
+ // code paths expects there to be colortables for index8-bit...
+ QVector<QRgb> colors(256);
+ for (int i=0; i<256; ++i)
+ colors[i] = qRgba(0, 0, 0, i);
+ indexed.setColorTable(colors);
+
+ // Copy data... Cannot use QPainter here as GDI has messed up the
+ // Alpha channel of the ni.image pixels...
+ for (int y=0; y<mask->height(); ++y) {
+ uchar *dest = indexed.scanLine(y);
+ if (mask->image.format() == QImage::Format_RGB16) {
+ const qint16 *src = (qint16 *) ((const QImage &) mask->image).scanLine(y);
+ for (int x=0; x<mask->width(); ++x)
+ dest[x] = 255 - qGray(src[x]);
+ } else {
+ const uint *src = (uint *) ((const QImage &) mask->image).scanLine(y);
+ for (int x=0; x<mask->width(); ++x) {
+#ifdef Q_OS_WINCE
+ dest[x] = 255 - qGray(src[x]);
+#else
+ if (QNativeImage::systemFormat() == QImage::Format_RGB16)
+ dest[x] = 255 - qGray(src[x]);
+ else
+ dest[x] = 255 - (qt_pow_gamma[qGray(src[x])] * 255. / 2047.);
+#endif
+ }
+ }
+ }
+
+ // Cleanup...
+ delete mask;
+ if (qt_cleartype_enabled) {
+ DeleteObject(font);
+ }
+
+ return indexed;
+}
+
+#define SPI_GETFONTSMOOTHINGCONTRAST 0x200C
+#define SPI_SETFONTSMOOTHINGCONTRAST 0x200D
+
+QImage QFontEngineWin::alphaRGBMapForGlyph(glyph_t glyph, QFixed, int margin, const QTransform &t)
+{
+ HFONT font = hfont;
+
+ int contrast;
+ SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &contrast, 0);
+ SystemParametersInfo(SPI_SETFONTSMOOTHINGCONTRAST, 0, (void *) 1000, 0);
+
+ QNativeImage *mask = drawGDIGlyph(font, glyph, margin, t, QImage::Format_RGB32);
+ SystemParametersInfo(SPI_SETFONTSMOOTHINGCONTRAST, 0, (void *) contrast, 0);
+
+ if (mask == 0)
+ return QImage();
+
+ // Gracefully handle the odd case when the display is 16-bit
+ const QImage source = mask->image.depth() == 32
+ ? mask->image
+ : mask->image.convertToFormat(QImage::Format_RGB32);
+
+ QImage rgbMask(mask->width(), mask->height(), QImage::Format_RGB32);
+ for (int y=0; y<mask->height(); ++y) {
+ uint *dest = (uint *) rgbMask.scanLine(y);
+ const uint *src = (uint *) source.scanLine(y);
+ for (int x=0; x<mask->width(); ++x) {
+ dest[x] = 0xffffffff - (0x00ffffff & src[x]);
+ }
+ }
+
+ delete mask;
+
+ return rgbMask;
+}
+
+// From qfontdatabase_win.cpp
+extern QFontEngine *qt_load_font_engine_win(const QFontDef &request);
+QFontEngine *QFontEngineWin::cloneWithSize(qreal pixelSize) const
+{
+ QFontDef request = fontDef;
+ QString actualFontName = request.family;
+ if (!uniqueFamilyName.isEmpty())
+ request.family = uniqueFamilyName;
+ request.pixelSize = pixelSize;
+
+ QFontEngine *fontEngine = qt_load_font_engine_win(request);
+ if (fontEngine != NULL)
+ fontEngine->fontDef.family = actualFontName;
+
+ return fontEngine;
+}
+
+// -------------------------------------- Multi font engine
+
+QFontEngineMultiWin::QFontEngineMultiWin(QFontEngine *first, const QStringList &fallbacks)
+ : QFontEngineMulti(fallbacks.size()+1),
+ fallbacks(fallbacks)
+{
+ engines[0] = first;
+ first->ref.ref();
+ fontDef = engines[0]->fontDef;
+ cache_cost = first->cache_cost;
+}
+
+void QFontEngineMultiWin::loadEngine(int at)
+{
+ Q_ASSERT(at < engines.size());
+ Q_ASSERT(engines.at(at) == 0);
+
+ QString fam = fallbacks.at(at-1);
+
+ LOGFONT lf = static_cast<QFontEngineWin *>(engines.at(0))->logfont;
+ memcpy(lf.lfFaceName, fam.utf16(), sizeof(wchar_t) * qMin(fam.length() + 1, 32)); // 32 = Windows hard-coded
+ HFONT hfont = CreateFontIndirect(&lf);
+
+ bool stockFont = false;
+ if (hfont == 0) {
+ hfont = (HFONT)GetStockObject(ANSI_VAR_FONT);
+ stockFont = true;
+ }
+ engines[at] = new QFontEngineWin(fam, hfont, stockFont, lf);
+ engines[at]->ref.ref();
+ engines[at]->fontDef = fontDef;
+
+ // TODO: increase cost in QFontCache for the font engine loaded here
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/win/qfontengine_win_p.h b/src/widgets/platforms/win/qfontengine_win_p.h
new file mode 100644
index 0000000000..ebcafffceb
--- /dev/null
+++ b/src/widgets/platforms/win/qfontengine_win_p.h
@@ -0,0 +1,164 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFONTENGINE_WIN_P_H
+#define QFONTENGINE_WIN_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qconfig.h>
+
+QT_BEGIN_NAMESPACE
+
+class QNativeImage;
+
+class QFontEngineWin : public QFontEngine
+{
+public:
+ QFontEngineWin(const QString &name, HFONT, bool, LOGFONT);
+ ~QFontEngineWin();
+
+ virtual QFixed lineThickness() const;
+ virtual Properties properties() const;
+ virtual void getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics);
+ virtual FaceId faceId() const;
+ virtual bool getSfntTableData(uint tag, uchar *buffer, uint *length) const;
+ virtual int synthesized() const;
+ virtual QFixed emSquareSize() const;
+
+ virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const;
+ virtual void recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags) const;
+
+ virtual void addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags);
+ virtual void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs,
+ QPainterPath *path, QTextItem::RenderFlags flags);
+
+ HGDIOBJ selectDesignFont() const;
+
+ virtual glyph_metrics_t boundingBox(const QGlyphLayout &glyphs);
+ virtual glyph_metrics_t boundingBox(glyph_t g) { return boundingBox(g, QTransform()); }
+ virtual glyph_metrics_t boundingBox(glyph_t g, const QTransform &t);
+
+
+ virtual QFixed ascent() const;
+ virtual QFixed descent() const;
+ virtual QFixed leading() const;
+ virtual QFixed xHeight() const;
+ virtual QFixed averageCharWidth() const;
+ virtual qreal maxCharWidth() const;
+ virtual qreal minLeftBearing() const;
+ virtual qreal minRightBearing() const;
+
+ virtual const char *name() const;
+
+ bool canRender(const QChar *string, int len);
+
+ Type type() const;
+
+ virtual QImage alphaMapForGlyph(glyph_t t) { return alphaMapForGlyph(t, QTransform()); }
+ virtual QImage alphaMapForGlyph(glyph_t, const QTransform &xform);
+ virtual QImage alphaRGBMapForGlyph(glyph_t t, QFixed subPixelPosition, int margin, const QTransform &xform);
+
+ virtual QFontEngine *cloneWithSize(qreal pixelSize) const;
+
+#ifndef Q_CC_MINGW
+ virtual void getGlyphBearings(glyph_t glyph, qreal *leftBearing = 0, qreal *rightBearing = 0);
+#endif
+
+ int getGlyphIndexes(const QChar *ch, int numChars, QGlyphLayout *glyphs, bool mirrored) const;
+ void getCMap();
+
+#ifndef Q_WS_WINCE
+ bool getOutlineMetrics(glyph_t glyph, const QTransform &t, glyph_metrics_t *metrics) const;
+#endif
+
+ QString _name;
+ QString uniqueFamilyName;
+ HFONT hfont;
+ LOGFONT logfont;
+ uint stockFont : 1;
+ uint ttf : 1;
+ uint hasOutline : 1;
+ TEXTMETRIC tm;
+ int lw;
+ const unsigned char *cmap;
+ QByteArray cmapTable;
+ mutable qreal lbearing;
+ mutable qreal rbearing;
+ QFixed designToDevice;
+ int unitsPerEm;
+ QFixed x_height;
+ FaceId _faceId;
+
+ mutable int synthesized_flags;
+ mutable QFixed lineWidth;
+ mutable unsigned char *widthCache;
+ mutable uint widthCacheSize;
+ mutable QFixed *designAdvances;
+ mutable int designAdvancesSize;
+
+private:
+ QNativeImage *drawGDIGlyph(HFONT font, glyph_t, int margin, const QTransform &xform,
+ QImage::Format mask_format);
+
+};
+
+class QFontEngineMultiWin : public QFontEngineMulti
+{
+public:
+ QFontEngineMultiWin(QFontEngine *first, const QStringList &fallbacks);
+ void loadEngine(int at);
+
+ QStringList fallbacks;
+};
+
+QT_END_NAMESPACE
+
+#endif // QFONTENGINE_WIN_P_H
diff --git a/src/widgets/platforms/win/qguifunctions_wince.cpp b/src/widgets/platforms/win/qguifunctions_wince.cpp
new file mode 100644
index 0000000000..78dc469b88
--- /dev/null
+++ b/src/widgets/platforms/win/qguifunctions_wince.cpp
@@ -0,0 +1,408 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qguifunctions_wince.h"
+#include <shellapi.h>
+#include <QtCore/qlibrary.h>
+
+QT_USE_NAMESPACE
+
+struct AygSHINITDLGINFO
+{
+ DWORD dwMask;
+ HWND hDlg;
+ DWORD dwFlags;
+};
+
+struct AygSIPINFO
+{
+ DWORD cbSize;
+ DWORD fdwFlags;
+ RECT rcVisibleDesktop;
+ RECT rcSipRect;
+ DWORD dwImDataSize;
+ void *pvImData;
+};
+
+#ifndef SHIDIF_CANCELBUTTON
+#define SHIDIF_CANCELBUTTON 0x0080
+#endif
+
+#ifndef SHIDIM_FLAGS
+#define SHIDIM_FLAGS 0x0001
+#endif
+
+#ifndef SHIDIF_DONEBUTTON
+#define SHIDIF_DONEBUTTON 0x0001
+#endif
+#ifndef SHIDIF_SIZEDLGFULLSCREEN
+#define SHIDIF_SIZEDLGFULLSCREEN 0x0004
+#endif
+
+#ifndef SHDB_HIDE
+#define SHDB_HIDE 0x0002
+#endif
+
+#ifndef SHFS_SHOWTASKBAR
+#define SHFS_SHOWTASKBAR 0x0001
+#endif
+#ifndef SHFS_HIDETASKBAR
+#define SHFS_HIDETASKBAR 0x0002
+#endif
+#ifndef SHFS_SHOWSIPBUTTON
+#define SHFS_SHOWSIPBUTTON 0x0004
+#endif
+#ifndef SHFS_HIDESIPBUTTON
+#define SHFS_HIDESIPBUTTON 0x0008
+#endif
+#ifndef SHFS_SHOWSTARTICON
+#define SHFS_SHOWSTARTICON 0x0010
+#endif
+#ifndef SHFS_HIDESTARTICON
+#define SHFS_HIDESTARTICON 0x0020
+#endif
+
+#ifndef SIPF_OFF
+#define SIPF_OFF 0x00000000
+#endif
+#ifndef SIPF_ON
+#define SIPF_ON 0x00000001
+#endif
+
+#ifndef SPI_SETSIPINFO
+#define SPI_SETSIPINFO 224
+#endif
+#ifndef SPI_GETSIPINFO
+#define SPI_GETSIPINFO 225
+#endif
+#ifndef SPI_GETPLATFORMTYPE
+#define SPI_GETPLATFORMTYPE 257
+#endif
+
+typedef BOOL (*AygInitDialog)(AygSHINITDLGINFO*);
+typedef BOOL (*AygFullScreen)(HWND, DWORD);
+typedef BOOL (*AygSHSipInfo)(UINT, UINT, PVOID, UINT);
+typedef BOOL (*AygSHDoneButton)(HWND, DWORD);
+
+static AygInitDialog ptrAygInitDialog = 0;
+static AygFullScreen ptrAygFullScreen = 0;
+static AygSHSipInfo ptrAygSHSipInfo = 0;
+static AygSHDoneButton ptrAygSHDoneButton = 0;
+static bool aygResolved = false;
+
+static void resolveAygLibs()
+{
+ if (!aygResolved) {
+ aygResolved = true;
+ QLibrary ayglib(QLatin1String("aygshell"));
+ ptrAygInitDialog = (AygInitDialog) ayglib.resolve("SHInitDialog");
+ ptrAygFullScreen = (AygFullScreen) ayglib.resolve("SHFullScreen");
+ ptrAygSHSipInfo = (AygSHSipInfo) ayglib.resolve("SHSipInfo");
+ ptrAygSHDoneButton = (AygSHDoneButton) ayglib.resolve("SHDoneButton");
+ }
+}
+
+int qt_wince_GetDIBits(HDC /*hdc*/ , HBITMAP hSourceBitmap, uint, uint, LPVOID lpvBits, LPBITMAPINFO, uint)
+{
+ if (!lpvBits) {
+ qWarning("::GetDIBits(), lpvBits NULL");
+ return 0;
+ }
+ BITMAP bm;
+ GetObject(hSourceBitmap, sizeof(BITMAP), &bm);
+ bm.bmHeight = qAbs(bm.bmHeight);
+
+ HBITMAP hTargetBitmap;
+ void *pixels;
+
+ BITMAPINFO dibInfo;
+ memset(&dibInfo, 0, sizeof(dibInfo));
+ dibInfo.bmiHeader.biBitCount = 32;
+ dibInfo.bmiHeader.biClrImportant = 0;
+ dibInfo.bmiHeader.biClrUsed = 0;
+ dibInfo.bmiHeader.biCompression = BI_RGB;;
+ dibInfo.bmiHeader.biHeight = -bm.bmHeight;
+ dibInfo.bmiHeader.biWidth = bm.bmWidth;
+ dibInfo.bmiHeader.biPlanes = 1;
+ dibInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ dibInfo.bmiHeader.biSizeImage = bm.bmWidth * bm.bmHeight * 4;
+
+ HDC displayDC = GetDC(NULL);
+ if (!displayDC) {
+ qWarning("::GetDIBits(), failed to GetDC");
+ return 0;
+ }
+
+ int ret = bm.bmHeight;
+
+ hTargetBitmap = CreateDIBSection(displayDC, (const BITMAPINFO*) &dibInfo, DIB_RGB_COLORS,
+ (void**)&pixels, NULL, 0);
+ if (!hTargetBitmap) {
+ qWarning("::GetDIBits(), failed to CreateDIBSection");
+ return 0;
+ }
+
+ HDC hdcSrc = CreateCompatibleDC(displayDC);
+ HDC hdcDst = CreateCompatibleDC(displayDC);
+
+ if (!(hdcDst && hdcSrc)) {
+ qWarning("::GetDIBits(), failed to CreateCompatibleDC");
+ ret = 0;
+ }
+
+ HBITMAP hOldBitmap1 = (HBITMAP) SelectObject(hdcSrc, hSourceBitmap);
+ HBITMAP hOldBitmap2 = (HBITMAP) SelectObject(hdcDst, hTargetBitmap);
+
+ if (!(hOldBitmap1 && hOldBitmap2)) {
+ qWarning("::GetDIBits(), failed to SelectObject for bitmaps");
+ ret = 0;
+ }
+
+ if (!BitBlt(hdcDst, 0, 0, bm.bmWidth, bm.bmHeight, hdcSrc, 0, 0, SRCCOPY)) {
+ qWarning("::GetDIBits(), BitBlt failed");
+ ret = 0;
+ }
+
+ SelectObject(hdcSrc, hOldBitmap1);
+ SelectObject(hdcDst, hOldBitmap2);
+
+ DeleteDC(hdcSrc);
+ DeleteDC(hdcDst);
+
+ ReleaseDC(NULL, displayDC);
+
+ memcpy(lpvBits, pixels, dibInfo.bmiHeader.biSizeImage);
+
+ DeleteObject(hTargetBitmap);
+ return ret;
+}
+
+HINSTANCE qt_wince_ShellExecute(HWND hwnd, LPCWSTR, LPCWSTR file, LPCWSTR params, LPCWSTR dir, int showCmd)
+{
+ SHELLEXECUTEINFO info;
+ info.hwnd = hwnd;
+ info.lpVerb = L"Open";
+ info.lpFile = file;
+ info.lpParameters = params;
+ info.lpDirectory = dir;
+ info.nShow = showCmd;
+ info.cbSize = sizeof(info);
+ ShellExecuteEx(&info);
+ return info.hInstApp;
+}
+
+// Clipboard --------------------------------------------------------
+BOOL qt_wince_ChangeClipboardChain( HWND /*hWndRemove*/, HWND /*hWndNewNext*/ )
+{
+ return FALSE;
+}
+
+HWND qt_wince_SetClipboardViewer( HWND /*hWndNewViewer*/ )
+{
+ return NULL;
+}
+
+
+// Graphics ---------------------------------------------------------
+COLORREF qt_wince_PALETTEINDEX( WORD /*wPaletteIndex*/)
+{
+ return 0;
+}
+
+// Internal Qt -----------------------------------------------------
+bool qt_wince_is_platform(const QString &platformString) {
+ wchar_t tszPlatform[64];
+ if (SystemParametersInfo(SPI_GETPLATFORMTYPE, sizeof(tszPlatform) / sizeof(wchar_t), tszPlatform, 0))
+ if (0 == _tcsicmp(reinterpret_cast<const wchar_t *> (platformString.utf16()), tszPlatform))
+ return true;
+ return false;
+}
+
+int qt_wince_get_build()
+{
+ OSVERSIONINFO osvi;
+ osvi.dwOSVersionInfoSize = sizeof(osvi);
+ if (GetVersionEx(&osvi)) {
+ return osvi.dwBuildNumber;
+ }
+ return 0;
+}
+
+int qt_wince_get_version()
+{
+ OSVERSIONINFO osvi;
+ osvi.dwOSVersionInfoSize = sizeof(osvi);
+ if (GetVersionEx(&osvi)) {
+ return (osvi.dwMajorVersion * 10 + osvi.dwMinorVersion);
+ }
+ return 0;
+}
+
+bool qt_wince_is_windows_mobile_65()
+{
+ const DWORD dwFirstWM65BuildNumber = 21139;
+ OSVERSIONINFO osvi;
+ osvi.dwOSVersionInfoSize = sizeof(osvi);
+ if (!GetVersionEx(&osvi))
+ return false;
+ return osvi.dwMajorVersion > 5
+ || (osvi.dwMajorVersion == 5 && (osvi.dwMinorVersion > 2 ||
+ (osvi.dwMinorVersion == 2 && osvi.dwBuildNumber >= dwFirstWM65BuildNumber)));
+}
+
+bool qt_wince_is_pocket_pc() {
+ return qt_wince_is_platform(QString::fromLatin1("PocketPC"));
+}
+
+bool qt_wince_is_smartphone() {
+ return qt_wince_is_platform(QString::fromLatin1("Smartphone"));
+}
+bool qt_wince_is_mobile() {
+ return (qt_wince_is_smartphone() || qt_wince_is_pocket_pc());
+}
+
+bool qt_wince_is_high_dpi() {
+ if (!qt_wince_is_pocket_pc())
+ return false;
+ HDC deviceContext = GetDC(0);
+ int dpi = GetDeviceCaps(deviceContext, LOGPIXELSX);
+ ReleaseDC(0, deviceContext);
+ if ((dpi < 1000) && (dpi > 0))
+ return dpi > 96;
+ else
+ return false;
+}
+
+void qt_wince_maximize(QWidget *widget)
+{
+ HWND hwnd = widget->winId();
+ if (qt_wince_is_mobile()) {
+ AygSHINITDLGINFO shidi;
+ shidi.dwMask = SHIDIM_FLAGS;
+ shidi.hDlg = hwnd;
+ shidi.dwFlags = SHIDIF_SIZEDLGFULLSCREEN;
+ if (widget->windowFlags() & Qt::WindowCancelButtonHint)
+ shidi.dwFlags |= SHIDIF_CANCELBUTTON;
+ if (widget->windowFlags() & Qt::WindowOkButtonHint)
+ shidi.dwFlags |= SHIDIF_DONEBUTTON;
+ if (!(widget->windowFlags() & (Qt::WindowCancelButtonHint | Qt::WindowOkButtonHint)))
+ shidi.dwFlags |= SHIDIF_CANCELBUTTON;
+ resolveAygLibs();
+ if (ptrAygInitDialog)
+ ptrAygInitDialog(&shidi);
+ } else {
+ RECT r;
+ SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
+ MoveWindow(hwnd, r.top, r.left, r.right - r.left, r.bottom - r.top, true);
+ SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong (hwnd, GWL_EXSTYLE) | WS_EX_NODRAG);
+ }
+}
+
+void qt_wince_unmaximize(QWidget *widget)
+{
+ if (ptrAygSHDoneButton && qt_wince_is_mobile()
+ && !(widget->windowFlags() & (Qt::WindowCancelButtonHint | Qt::WindowOkButtonHint)))
+ {
+ // Hide the [X] button, we've added in qt_wince_maximize.
+ ptrAygSHDoneButton(widget->winId(), SHDB_HIDE);
+ }
+}
+
+void qt_wince_minimize(HWND hwnd)
+{
+#ifdef Q_OS_WINCE_WM
+ ShowWindow(hwnd, SW_HIDE);
+#else
+ if (!IsWindowVisible(hwnd)) {
+ // Hack for an initial showMinimized.
+ // Without it, our widget doesn't appear in the task bar.
+ ShowWindow(hwnd, SW_SHOW);
+ }
+ ShowWindow(hwnd, SW_MINIMIZE);
+#endif
+}
+
+void qt_wince_hide_taskbar(HWND hwnd) {
+ if (ptrAygFullScreen)
+ ptrAygFullScreen(hwnd, SHFS_HIDETASKBAR | SHFS_HIDESIPBUTTON | SHFS_HIDESTARTICON);
+}
+
+void qt_wince_full_screen(HWND hwnd, bool fullScreen, UINT swpf) {
+ resolveAygLibs();
+ if (fullScreen) {
+ QRect r = qApp->desktop()->screenGeometry(QWidget::find(hwnd));
+ SetWindowPos(hwnd, HWND_TOP, r.left(), r.top(), r.width(), r.height(), swpf);
+ if (ptrAygFullScreen)
+ ptrAygFullScreen(hwnd, SHFS_HIDETASKBAR | SHFS_HIDESIPBUTTON | SHFS_HIDESTARTICON);
+ if (!qt_wince_is_mobile()) {
+ HWND handle = FindWindow(L"HHTaskBar", L"");
+ if (handle) {
+ ShowWindow(handle, 0);
+ EnableWindow(handle, false);
+ }
+ }
+ } else {
+ if (ptrAygFullScreen)
+ ptrAygFullScreen(hwnd, SHFS_SHOWTASKBAR | SHFS_SHOWSIPBUTTON | SHFS_SHOWSTARTICON);
+ SetWindowPos(hwnd, 0, 0, 0, 0, 0, swpf);
+ if (!qt_wince_is_mobile()) {
+ HWND handle = FindWindow(L"HHTaskBar", L"");
+ if (handle) {
+ ShowWindow(handle, 1);
+ EnableWindow(handle, true);
+ }
+ }
+ }
+}
+
+void qt_wince_show_SIP(bool show)
+{
+ resolveAygLibs();
+ if (!ptrAygSHSipInfo)
+ return;
+
+ AygSIPINFO si;
+ memset(&si, 0, sizeof(si));
+ si.cbSize = sizeof(si);
+ ptrAygSHSipInfo(SPI_GETSIPINFO, 0, &si, 0);
+ si.cbSize = sizeof(si);
+ si.fdwFlags = (show ? SIPF_ON : SIPF_OFF);
+ ptrAygSHSipInfo(SPI_SETSIPINFO, 0, &si, 0);
+}
diff --git a/src/widgets/platforms/win/qguifunctions_wince.h b/src/widgets/platforms/win/qguifunctions_wince.h
new file mode 100644
index 0000000000..6384e4ac62
--- /dev/null
+++ b/src/widgets/platforms/win/qguifunctions_wince.h
@@ -0,0 +1,151 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QGUIFUNCTIONS_WCE_H
+#define QGUIFUNCTIONS_WCE_H
+#ifdef Q_OS_WINCE
+#include <QtCore/qfunctions_wince.h>
+#define UNDER_NT
+#include <wingdi.h>
+
+#ifdef QT_BUILD_GUI_LIB
+QT_BEGIN_HEADER
+QT_BEGIN_NAMESPACE
+QT_MODULE(Gui)
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif
+
+// application defines
+#define SPI_SETNONCLIENTMETRICS 72
+#define SPI_SETICONTITLELOGFONT 0x0022
+#define WM_ACTIVATEAPP 0x001c
+#define SW_PARENTCLOSING 1
+#define SW_OTHERMAXIMIZED 2
+#define SW_PARENTOPENING 3
+#define SW_OTHERRESTORED 4
+#define GET_XBUTTON_WPARAM(wParam) (HIWORD(wParam))
+
+// drag n drop
+#ifndef CFSTR_PERFORMEDDROPEFFECT
+#define CFSTR_PERFORMEDDROPEFFECT TEXT("Performed DropEffect")
+#endif
+int qt_wince_GetDIBits(HDC, HBITMAP, uint, uint, void*, LPBITMAPINFO, uint);
+#define GetDIBits(a,b,c,d,e,f,g) qt_wince_GetDIBits(a,b,c,d,e,f,g)
+
+// QWidget
+#define SW_SHOWMINIMIZED SW_MINIMIZE
+
+// QRegion
+#define ALTERNATE 0
+#define WINDING 1
+
+// QFontEngine
+typedef struct _FIXED {
+ WORD fract;
+ short value;
+} FIXED;
+
+typedef struct tagPOINTFX {
+ FIXED x;
+ FIXED y;
+} POINTFX;
+
+typedef struct _MAT2 {
+ FIXED eM11;
+ FIXED eM12;
+ FIXED eM21;
+ FIXED eM22;
+} MAT2;
+
+typedef struct _GLYPHMETRICS {
+ UINT gmBlackBoxX;
+ UINT gmBlackBoxY;
+ POINT gmptGlyphOrigin;
+ short gmCellIncX;
+ short gmCellIncY;
+} GLYPHMETRICS;
+
+typedef struct tagTTPOLYGONHEADER
+{
+ DWORD cb;
+ DWORD dwType;
+ POINTFX pfxStart;
+} TTPOLYGONHEADER;
+
+typedef struct tagTTPOLYCURVE
+{
+ WORD wType;
+ WORD cpfx;
+ POINTFX apfx[1];
+} TTPOLYCURVE;
+
+#define GGO_NATIVE 2
+#define GGO_GLYPH_INDEX 0x0080
+#define TT_PRIM_LINE 1
+#define TT_PRIM_QSPLINE 2
+#define TT_PRIM_CSPLINE 3
+#define ANSI_VAR_FONT 12
+
+HINSTANCE qt_wince_ShellExecute(HWND hwnd, LPCWSTR operation, LPCWSTR file, LPCWSTR params, LPCWSTR dir, int showCmd);
+#define ShellExecute(a,b,c,d,e,f) qt_wince_ShellExecute(a,b,c,d,e,f)
+
+
+// Clipboard --------------------------------------------------------
+#define WM_CHANGECBCHAIN 1
+#define WM_DRAWCLIPBOARD 2
+
+BOOL qt_wince_ChangeClipboardChain(
+ HWND hWndRemove, // handle to window to remove
+ HWND hWndNewNext // handle to next window
+);
+#define ChangeClipboardChain(a,b) qt_wince_ChangeClipboardChain(a,b);
+
+HWND qt_wince_SetClipboardViewer(
+ HWND hWndNewViewer // handle to clipboard viewer window
+);
+#define SetClipboardViewer(a) qt_wince_SetClipboardViewer(a)
+
+// Graphics ---------------------------------------------------------
+COLORREF qt_wince_PALETTEINDEX( WORD wPaletteIndex );
+#define PALETTEINDEX(a) qt_wince_PALETTEINDEX(a)
+
+#endif // Q_OS_WINCE
+#endif // QGUIFUNCTIONS_WCE_H
diff --git a/src/widgets/platforms/win/qkeymapper_win.cpp b/src/widgets/platforms/win/qkeymapper_win.cpp
new file mode 100644
index 0000000000..78389c1160
--- /dev/null
+++ b/src/widgets/platforms/win/qkeymapper_win.cpp
@@ -0,0 +1,1207 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qkeymapper_p.h"
+
+#include <qt_windows.h>
+#include <qdebug.h>
+#include <private/qevent_p.h>
+#include <private/qlocale_p.h>
+#include <private/qapplication_p.h>
+#include <qwidget.h>
+#include <qapplication.h>
+#include <ctype.h>
+
+QT_BEGIN_NAMESPACE
+
+// Uncommend, to show debugging information for the keymapper
+//#define DEBUG_KEYMAPPER
+
+// Implemented elsewhere
+extern "C" LRESULT QT_WIN_CALLBACK QtWndProc(HWND, UINT, WPARAM, LPARAM);
+
+extern Q_CORE_EXPORT QLocale qt_localeFromLCID(LCID id);
+#ifndef LANG_PASHTO
+#define LANG_PASHTO 0x63
+#endif
+#ifndef LANG_SYRIAC
+#define LANG_SYRIAC 0x5a
+#endif
+#ifndef LANG_DIVEHI
+#define LANG_DIVEHI 0x65
+#endif
+#ifndef VK_OEM_PLUS
+#define VK_OEM_PLUS 0xBB
+#endif
+#ifndef VK_OEM_3
+#define VK_OEM_3 0xC0
+#endif
+
+#if defined(Q_OS_WINCE)
+bool GetKeyboardState(unsigned char* kbuffer)
+{
+ for (int i=0; i< 256; ++i)
+ kbuffer[i] = GetAsyncKeyState(i);
+ return true;
+}
+#endif
+// Key recorder ------------------------------------------------------------------------[ start ] --
+struct KeyRecord {
+ KeyRecord(int c, int a, int s, const QString &t) : code(c), ascii(a), state(s), text(t) {}
+ KeyRecord() {}
+
+ int code;
+ int ascii;
+ int state;
+ QString text;
+};
+
+static const int QT_MAX_KEY_RECORDINGS = 64; // User has LOTS of fingers...
+struct KeyRecorder
+{
+ KeyRecorder() : nrecs(0) {}
+
+ inline KeyRecord *findKey(int code, bool remove);
+ inline void storeKey(int code, int ascii, int state, const QString& text);
+ inline void clearKeys();
+
+ int nrecs;
+ KeyRecord deleted_record; // A copy of last entry removed from records[]
+ KeyRecord records[QT_MAX_KEY_RECORDINGS];
+};
+static KeyRecorder key_recorder;
+
+KeyRecord *KeyRecorder::findKey(int code, bool remove)
+{
+ KeyRecord *result = 0;
+ for (int i = 0; i < nrecs; ++i) {
+ if (records[i].code == code) {
+ if (remove) {
+ deleted_record = records[i];
+ // Move rest down, and decrease count
+ while (i + 1 < nrecs) {
+ records[i] = records[i + 1];
+ ++i;
+ }
+ --nrecs;
+ result = &deleted_record;
+ } else {
+ result = &records[i];
+ }
+ break;
+ }
+ }
+ return result;
+}
+
+void KeyRecorder::storeKey(int code, int ascii, int state, const QString& text)
+{
+ Q_ASSERT_X(nrecs != QT_MAX_KEY_RECORDINGS,
+ "Internal KeyRecorder",
+ "Keyboard recorder buffer overflow, consider increasing QT_MAX_KEY_RECORDINGS");
+
+ if (nrecs == QT_MAX_KEY_RECORDINGS) {
+ qWarning("Qt: Internal keyboard buffer overflow");
+ return;
+ }
+ records[nrecs++] = KeyRecord(code,ascii,state,text);
+}
+
+void KeyRecorder::clearKeys()
+{
+ nrecs = 0;
+}
+// Key recorder --------------------------------------------------------------------------[ end ] --
+
+
+// Key translation ---------------------------------------------------------------------[ start ] --
+// Meaning of values:
+// 0 = Character output key, needs keyboard driver mapping
+// Key_unknown = Unknown Virtual Key, no translation possible, ignore
+static const uint KeyTbl[] = { // Keyboard mapping table
+ // Dec | Hex | Windows Virtual key
+ Qt::Key_unknown, // 0 0x00
+ Qt::Key_unknown, // 1 0x01 VK_LBUTTON | Left mouse button
+ Qt::Key_unknown, // 2 0x02 VK_RBUTTON | Right mouse button
+ Qt::Key_Cancel, // 3 0x03 VK_CANCEL | Control-Break processing
+ Qt::Key_unknown, // 4 0x04 VK_MBUTTON | Middle mouse button
+ Qt::Key_unknown, // 5 0x05 VK_XBUTTON1 | X1 mouse button
+ Qt::Key_unknown, // 6 0x06 VK_XBUTTON2 | X2 mouse button
+ Qt::Key_unknown, // 7 0x07 -- unassigned --
+ Qt::Key_Backspace, // 8 0x08 VK_BACK | BackSpace key
+ Qt::Key_Tab, // 9 0x09 VK_TAB | Tab key
+ Qt::Key_unknown, // 10 0x0A -- reserved --
+ Qt::Key_unknown, // 11 0x0B -- reserved --
+ Qt::Key_Clear, // 12 0x0C VK_CLEAR | Clear key
+ Qt::Key_Return, // 13 0x0D VK_RETURN | Enter key
+ Qt::Key_unknown, // 14 0x0E -- unassigned --
+ Qt::Key_unknown, // 15 0x0F -- unassigned --
+ Qt::Key_Shift, // 16 0x10 VK_SHIFT | Shift key
+ Qt::Key_Control, // 17 0x11 VK_CONTROL | Ctrl key
+ Qt::Key_Alt, // 18 0x12 VK_MENU | Alt key
+ Qt::Key_Pause, // 19 0x13 VK_PAUSE | Pause key
+ Qt::Key_CapsLock, // 20 0x14 VK_CAPITAL | Caps-Lock
+ Qt::Key_unknown, // 21 0x15 VK_KANA / VK_HANGUL | IME Kana or Hangul mode
+ Qt::Key_unknown, // 22 0x16 -- unassigned --
+ Qt::Key_unknown, // 23 0x17 VK_JUNJA | IME Junja mode
+ Qt::Key_unknown, // 24 0x18 VK_FINAL | IME final mode
+ Qt::Key_unknown, // 25 0x19 VK_HANJA / VK_KANJI | IME Hanja or Kanji mode
+ Qt::Key_unknown, // 26 0x1A -- unassigned --
+ Qt::Key_Escape, // 27 0x1B VK_ESCAPE | Esc key
+ Qt::Key_unknown, // 28 0x1C VK_CONVERT | IME convert
+ Qt::Key_unknown, // 29 0x1D VK_NONCONVERT | IME non-convert
+ Qt::Key_unknown, // 30 0x1E VK_ACCEPT | IME accept
+ Qt::Key_Mode_switch,// 31 0x1F VK_MODECHANGE | IME mode change request
+ Qt::Key_Space, // 32 0x20 VK_SPACE | Spacebar
+ Qt::Key_PageUp, // 33 0x21 VK_PRIOR | Page Up key
+ Qt::Key_PageDown, // 34 0x22 VK_NEXT | Page Down key
+ Qt::Key_End, // 35 0x23 VK_END | End key
+ Qt::Key_Home, // 36 0x24 VK_HOME | Home key
+ Qt::Key_Left, // 37 0x25 VK_LEFT | Left arrow key
+ Qt::Key_Up, // 38 0x26 VK_UP | Up arrow key
+ Qt::Key_Right, // 39 0x27 VK_RIGHT | Right arrow key
+ Qt::Key_Down, // 40 0x28 VK_DOWN | Down arrow key
+ Qt::Key_Select, // 41 0x29 VK_SELECT | Select key
+ Qt::Key_Printer, // 42 0x2A VK_PRINT | Print key
+ Qt::Key_Execute, // 43 0x2B VK_EXECUTE | Execute key
+ Qt::Key_Print, // 44 0x2C VK_SNAPSHOT | Print Screen key
+ Qt::Key_Insert, // 45 0x2D VK_INSERT | Ins key
+ Qt::Key_Delete, // 46 0x2E VK_DELETE | Del key
+ Qt::Key_Help, // 47 0x2F VK_HELP | Help key
+ 0, // 48 0x30 (VK_0) | 0 key
+ 0, // 49 0x31 (VK_1) | 1 key
+ 0, // 50 0x32 (VK_2) | 2 key
+ 0, // 51 0x33 (VK_3) | 3 key
+ 0, // 52 0x34 (VK_4) | 4 key
+ 0, // 53 0x35 (VK_5) | 5 key
+ 0, // 54 0x36 (VK_6) | 6 key
+ 0, // 55 0x37 (VK_7) | 7 key
+ 0, // 56 0x38 (VK_8) | 8 key
+ 0, // 57 0x39 (VK_9) | 9 key
+ Qt::Key_unknown, // 58 0x3A -- unassigned --
+ Qt::Key_unknown, // 59 0x3B -- unassigned --
+ Qt::Key_unknown, // 60 0x3C -- unassigned --
+ Qt::Key_unknown, // 61 0x3D -- unassigned --
+ Qt::Key_unknown, // 62 0x3E -- unassigned --
+ Qt::Key_unknown, // 63 0x3F -- unassigned --
+ Qt::Key_unknown, // 64 0x40 -- unassigned --
+ 0, // 65 0x41 (VK_A) | A key
+ 0, // 66 0x42 (VK_B) | B key
+ 0, // 67 0x43 (VK_C) | C key
+ 0, // 68 0x44 (VK_D) | D key
+ 0, // 69 0x45 (VK_E) | E key
+ 0, // 70 0x46 (VK_F) | F key
+ 0, // 71 0x47 (VK_G) | G key
+ 0, // 72 0x48 (VK_H) | H key
+ 0, // 73 0x49 (VK_I) | I key
+ 0, // 74 0x4A (VK_J) | J key
+ 0, // 75 0x4B (VK_K) | K key
+ 0, // 76 0x4C (VK_L) | L key
+ 0, // 77 0x4D (VK_M) | M key
+ 0, // 78 0x4E (VK_N) | N key
+ 0, // 79 0x4F (VK_O) | O key
+ 0, // 80 0x50 (VK_P) | P key
+ 0, // 81 0x51 (VK_Q) | Q key
+ 0, // 82 0x52 (VK_R) | R key
+ 0, // 83 0x53 (VK_S) | S key
+ 0, // 84 0x54 (VK_T) | T key
+ 0, // 85 0x55 (VK_U) | U key
+ 0, // 86 0x56 (VK_V) | V key
+ 0, // 87 0x57 (VK_W) | W key
+ 0, // 88 0x58 (VK_X) | X key
+ 0, // 89 0x59 (VK_Y) | Y key
+ 0, // 90 0x5A (VK_Z) | Z key
+ Qt::Key_Meta, // 91 0x5B VK_LWIN | Left Windows - MS Natural kbd
+ Qt::Key_Meta, // 92 0x5C VK_RWIN | Right Windows - MS Natural kbd
+ Qt::Key_Menu, // 93 0x5D VK_APPS | Application key-MS Natural kbd
+ Qt::Key_unknown, // 94 0x5E -- reserved --
+ Qt::Key_Sleep, // 95 0x5F VK_SLEEP
+ Qt::Key_0, // 96 0x60 VK_NUMPAD0 | Numeric keypad 0 key
+ Qt::Key_1, // 97 0x61 VK_NUMPAD1 | Numeric keypad 1 key
+ Qt::Key_2, // 98 0x62 VK_NUMPAD2 | Numeric keypad 2 key
+ Qt::Key_3, // 99 0x63 VK_NUMPAD3 | Numeric keypad 3 key
+ Qt::Key_4, // 100 0x64 VK_NUMPAD4 | Numeric keypad 4 key
+ Qt::Key_5, // 101 0x65 VK_NUMPAD5 | Numeric keypad 5 key
+ Qt::Key_6, // 102 0x66 VK_NUMPAD6 | Numeric keypad 6 key
+ Qt::Key_7, // 103 0x67 VK_NUMPAD7 | Numeric keypad 7 key
+ Qt::Key_8, // 104 0x68 VK_NUMPAD8 | Numeric keypad 8 key
+ Qt::Key_9, // 105 0x69 VK_NUMPAD9 | Numeric keypad 9 key
+ Qt::Key_Asterisk, // 106 0x6A VK_MULTIPLY | Multiply key
+ Qt::Key_Plus, // 107 0x6B VK_ADD | Add key
+ Qt::Key_Comma, // 108 0x6C VK_SEPARATOR | Separator key
+ Qt::Key_Minus, // 109 0x6D VK_SUBTRACT | Subtract key
+ Qt::Key_Period, // 110 0x6E VK_DECIMAL | Decimal key
+ Qt::Key_Slash, // 111 0x6F VK_DIVIDE | Divide key
+ Qt::Key_F1, // 112 0x70 VK_F1 | F1 key
+ Qt::Key_F2, // 113 0x71 VK_F2 | F2 key
+ Qt::Key_F3, // 114 0x72 VK_F3 | F3 key
+ Qt::Key_F4, // 115 0x73 VK_F4 | F4 key
+ Qt::Key_F5, // 116 0x74 VK_F5 | F5 key
+ Qt::Key_F6, // 117 0x75 VK_F6 | F6 key
+ Qt::Key_F7, // 118 0x76 VK_F7 | F7 key
+ Qt::Key_F8, // 119 0x77 VK_F8 | F8 key
+ Qt::Key_F9, // 120 0x78 VK_F9 | F9 key
+ Qt::Key_F10, // 121 0x79 VK_F10 | F10 key
+ Qt::Key_F11, // 122 0x7A VK_F11 | F11 key
+ Qt::Key_F12, // 123 0x7B VK_F12 | F12 key
+ Qt::Key_F13, // 124 0x7C VK_F13 | F13 key
+ Qt::Key_F14, // 125 0x7D VK_F14 | F14 key
+ Qt::Key_F15, // 126 0x7E VK_F15 | F15 key
+ Qt::Key_F16, // 127 0x7F VK_F16 | F16 key
+ Qt::Key_F17, // 128 0x80 VK_F17 | F17 key
+ Qt::Key_F18, // 129 0x81 VK_F18 | F18 key
+ Qt::Key_F19, // 130 0x82 VK_F19 | F19 key
+ Qt::Key_F20, // 131 0x83 VK_F20 | F20 key
+ Qt::Key_F21, // 132 0x84 VK_F21 | F21 key
+ Qt::Key_F22, // 133 0x85 VK_F22 | F22 key
+ Qt::Key_F23, // 134 0x86 VK_F23 | F23 key
+ Qt::Key_F24, // 135 0x87 VK_F24 | F24 key
+ Qt::Key_unknown, // 136 0x88 -- unassigned --
+ Qt::Key_unknown, // 137 0x89 -- unassigned --
+ Qt::Key_unknown, // 138 0x8A -- unassigned --
+ Qt::Key_unknown, // 139 0x8B -- unassigned --
+ Qt::Key_unknown, // 140 0x8C -- unassigned --
+ Qt::Key_unknown, // 141 0x8D -- unassigned --
+ Qt::Key_unknown, // 142 0x8E -- unassigned --
+ Qt::Key_unknown, // 143 0x8F -- unassigned --
+ Qt::Key_NumLock, // 144 0x90 VK_NUMLOCK | Num Lock key
+ Qt::Key_ScrollLock, // 145 0x91 VK_SCROLL | Scroll Lock key
+ // Fujitsu/OASYS kbd --------------------
+ 0, //Qt::Key_Jisho, // 146 0x92 VK_OEM_FJ_JISHO | 'Dictionary' key /
+ // VK_OEM_NEC_EQUAL = key on numpad on NEC PC-9800 kbd
+ Qt::Key_Massyo, // 147 0x93 VK_OEM_FJ_MASSHOU | 'Unregister word' key
+ Qt::Key_Touroku, // 148 0x94 VK_OEM_FJ_TOUROKU | 'Register word' key
+ 0, //Qt::Key_Oyayubi_Left,//149 0x95 VK_OEM_FJ_LOYA | 'Left OYAYUBI' key
+ 0, //Qt::Key_Oyayubi_Right,//150 0x96 VK_OEM_FJ_ROYA | 'Right OYAYUBI' key
+ Qt::Key_unknown, // 151 0x97 -- unassigned --
+ Qt::Key_unknown, // 152 0x98 -- unassigned --
+ Qt::Key_unknown, // 153 0x99 -- unassigned --
+ Qt::Key_unknown, // 154 0x9A -- unassigned --
+ Qt::Key_unknown, // 155 0x9B -- unassigned --
+ Qt::Key_unknown, // 156 0x9C -- unassigned --
+ Qt::Key_unknown, // 157 0x9D -- unassigned --
+ Qt::Key_unknown, // 158 0x9E -- unassigned --
+ Qt::Key_unknown, // 159 0x9F -- unassigned --
+ Qt::Key_Shift, // 160 0xA0 VK_LSHIFT | Left Shift key
+ Qt::Key_Shift, // 161 0xA1 VK_RSHIFT | Right Shift key
+ Qt::Key_Control, // 162 0xA2 VK_LCONTROL | Left Ctrl key
+ Qt::Key_Control, // 163 0xA3 VK_RCONTROL | Right Ctrl key
+ Qt::Key_Alt, // 164 0xA4 VK_LMENU | Left Menu key
+ Qt::Key_Alt, // 165 0xA5 VK_RMENU | Right Menu key
+ Qt::Key_Back, // 166 0xA6 VK_BROWSER_BACK | Browser Back key
+ Qt::Key_Forward, // 167 0xA7 VK_BROWSER_FORWARD | Browser Forward key
+ Qt::Key_Refresh, // 168 0xA8 VK_BROWSER_REFRESH | Browser Refresh key
+ Qt::Key_Stop, // 169 0xA9 VK_BROWSER_STOP | Browser Stop key
+ Qt::Key_Search, // 170 0xAA VK_BROWSER_SEARCH | Browser Search key
+ Qt::Key_Favorites, // 171 0xAB VK_BROWSER_FAVORITES| Browser Favorites key
+ Qt::Key_HomePage, // 172 0xAC VK_BROWSER_HOME | Browser Start and Home key
+ Qt::Key_VolumeMute, // 173 0xAD VK_VOLUME_MUTE | Volume Mute key
+ Qt::Key_VolumeDown, // 174 0xAE VK_VOLUME_DOWN | Volume Down key
+ Qt::Key_VolumeUp, // 175 0xAF VK_VOLUME_UP | Volume Up key
+ Qt::Key_MediaNext, // 176 0xB0 VK_MEDIA_NEXT_TRACK | Next Track key
+ Qt::Key_MediaPrevious, //177 0xB1 VK_MEDIA_PREV_TRACK | Previous Track key
+ Qt::Key_MediaStop, // 178 0xB2 VK_MEDIA_STOP | Stop Media key
+ Qt::Key_MediaPlay, // 179 0xB3 VK_MEDIA_PLAY_PAUSE | Play/Pause Media key
+ Qt::Key_LaunchMail, // 180 0xB4 VK_LAUNCH_MAIL | Start Mail key
+ Qt::Key_LaunchMedia,// 181 0xB5 VK_LAUNCH_MEDIA_SELECT Select Media key
+ Qt::Key_Launch0, // 182 0xB6 VK_LAUNCH_APP1 | Start Application 1 key
+ Qt::Key_Launch1, // 183 0xB7 VK_LAUNCH_APP2 | Start Application 2 key
+ Qt::Key_unknown, // 184 0xB8 -- reserved --
+ Qt::Key_unknown, // 185 0xB9 -- reserved --
+ 0, // 186 0xBA VK_OEM_1 | ';:' for US
+ 0, // 187 0xBB VK_OEM_PLUS | '+' any country
+ 0, // 188 0xBC VK_OEM_COMMA | ',' any country
+ 0, // 189 0xBD VK_OEM_MINUS | '-' any country
+ 0, // 190 0xBE VK_OEM_PERIOD | '.' any country
+ 0, // 191 0xBF VK_OEM_2 | '/?' for US
+ 0, // 192 0xC0 VK_OEM_3 | '`~' for US
+ Qt::Key_unknown, // 193 0xC1 -- reserved --
+ Qt::Key_unknown, // 194 0xC2 -- reserved --
+ Qt::Key_unknown, // 195 0xC3 -- reserved --
+ Qt::Key_unknown, // 196 0xC4 -- reserved --
+ Qt::Key_unknown, // 197 0xC5 -- reserved --
+ Qt::Key_unknown, // 198 0xC6 -- reserved --
+ Qt::Key_unknown, // 199 0xC7 -- reserved --
+ Qt::Key_unknown, // 200 0xC8 -- reserved --
+ Qt::Key_unknown, // 201 0xC9 -- reserved --
+ Qt::Key_unknown, // 202 0xCA -- reserved --
+ Qt::Key_unknown, // 203 0xCB -- reserved --
+ Qt::Key_unknown, // 204 0xCC -- reserved --
+ Qt::Key_unknown, // 205 0xCD -- reserved --
+ Qt::Key_unknown, // 206 0xCE -- reserved --
+ Qt::Key_unknown, // 207 0xCF -- reserved --
+ Qt::Key_unknown, // 208 0xD0 -- reserved --
+ Qt::Key_unknown, // 209 0xD1 -- reserved --
+ Qt::Key_unknown, // 210 0xD2 -- reserved --
+ Qt::Key_unknown, // 211 0xD3 -- reserved --
+ Qt::Key_unknown, // 212 0xD4 -- reserved --
+ Qt::Key_unknown, // 213 0xD5 -- reserved --
+ Qt::Key_unknown, // 214 0xD6 -- reserved --
+ Qt::Key_unknown, // 215 0xD7 -- reserved --
+ Qt::Key_unknown, // 216 0xD8 -- unassigned --
+ Qt::Key_unknown, // 217 0xD9 -- unassigned --
+ Qt::Key_unknown, // 218 0xDA -- unassigned --
+ 0, // 219 0xDB VK_OEM_4 | '[{' for US
+ 0, // 220 0xDC VK_OEM_5 | '\|' for US
+ 0, // 221 0xDD VK_OEM_6 | ']}' for US
+ 0, // 222 0xDE VK_OEM_7 | ''"' for US
+ 0, // 223 0xDF VK_OEM_8
+ Qt::Key_unknown, // 224 0xE0 -- reserved --
+ Qt::Key_unknown, // 225 0xE1 VK_OEM_AX | 'AX' key on Japanese AX kbd
+ Qt::Key_unknown, // 226 0xE2 VK_OEM_102 | "<>" or "\|" on RT 102-key kbd
+ Qt::Key_unknown, // 227 0xE3 VK_ICO_HELP | Help key on ICO
+ Qt::Key_unknown, // 228 0xE4 VK_ICO_00 | 00 key on ICO
+ Qt::Key_unknown, // 229 0xE5 VK_PROCESSKEY | IME Process key
+ Qt::Key_unknown, // 230 0xE6 VK_ICO_CLEAR |
+ Qt::Key_unknown, // 231 0xE7 VK_PACKET | Unicode char as keystrokes
+ Qt::Key_unknown, // 232 0xE8 -- unassigned --
+ // Nokia/Ericsson definitions ---------------
+ Qt::Key_unknown, // 233 0xE9 VK_OEM_RESET
+ Qt::Key_unknown, // 234 0xEA VK_OEM_JUMP
+ Qt::Key_unknown, // 235 0xEB VK_OEM_PA1
+ Qt::Key_unknown, // 236 0xEC VK_OEM_PA2
+ Qt::Key_unknown, // 237 0xED VK_OEM_PA3
+ Qt::Key_unknown, // 238 0xEE VK_OEM_WSCTRL
+ Qt::Key_unknown, // 239 0xEF VK_OEM_CUSEL
+ Qt::Key_unknown, // 240 0xF0 VK_OEM_ATTN
+ Qt::Key_unknown, // 241 0xF1 VK_OEM_FINISH
+ Qt::Key_unknown, // 242 0xF2 VK_OEM_COPY
+ Qt::Key_unknown, // 243 0xF3 VK_OEM_AUTO
+ Qt::Key_unknown, // 244 0xF4 VK_OEM_ENLW
+ Qt::Key_unknown, // 245 0xF5 VK_OEM_BACKTAB
+ Qt::Key_unknown, // 246 0xF6 VK_ATTN | Attn key
+ Qt::Key_unknown, // 247 0xF7 VK_CRSEL | CrSel key
+ Qt::Key_unknown, // 248 0xF8 VK_EXSEL | ExSel key
+ Qt::Key_unknown, // 249 0xF9 VK_EREOF | Erase EOF key
+ Qt::Key_Play, // 250 0xFA VK_PLAY | Play key
+ Qt::Key_Zoom, // 251 0xFB VK_ZOOM | Zoom key
+ Qt::Key_unknown, // 252 0xFC VK_NONAME | Reserved
+ Qt::Key_unknown, // 253 0xFD VK_PA1 | PA1 key
+ Qt::Key_Clear, // 254 0xFE VK_OEM_CLEAR | Clear key
+ 0
+};
+
+// Possible modifier states.
+// NOTE: The order of these states match the order in QKeyMapperPrivate::updatePossibleKeyCodes()!
+static const Qt::KeyboardModifiers ModsTbl[] = {
+ Qt::NoModifier, // 0
+ Qt::ShiftModifier, // 1
+ Qt::ControlModifier, // 2
+ Qt::ControlModifier | Qt::ShiftModifier, // 3
+ Qt::AltModifier, // 4
+ Qt::AltModifier | Qt::ShiftModifier, // 5
+ Qt::AltModifier | Qt::ControlModifier, // 6
+ Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier, // 7
+ Qt::NoModifier, // Fall-back to raw Key_*
+};
+
+/**
+ Remap return or action key to select key for windows mobile.
+*/
+inline int winceKeyBend(int keyCode)
+{
+#if defined(Q_OS_WINCE_WM) && defined(QT_KEYPAD_NAVIGATION)
+ // remap return or action key to select key for windows mobile.
+ // will be changed to a table remapping function in the next version (4.6/7).
+ if (keyCode == VK_RETURN && QApplication::keypadNavigationEnabled())
+ return Qt::Key_Select;
+ else
+ return KeyTbl[keyCode];
+#else
+ return KeyTbl[keyCode];
+#endif
+}
+
+#if defined(Q_OS_WINCE)
+ // Use the KeyTbl to resolve a Qt::Key out of the virtual keys.
+ // In case it is not resolvable, continue using the virtual key itself.
+
+QT_BEGIN_INCLUDE_NAMESPACE
+
+int ToUnicode(UINT vk, int /*scancode*/, unsigned char* /*kbdBuffer*/, LPWSTR unicodeBuffer, int, int)
+{
+ QT_USE_NAMESPACE
+ QChar* buf = reinterpret_cast< QChar*>(unicodeBuffer);
+ if (KeyTbl[vk] == 0) {
+ buf[0] = vk;
+ return 1;
+ }
+ return 0;
+}
+
+int ToAscii(UINT vk, int scancode, unsigned char *kbdBuffer, LPWORD unicodeBuffer, int flag)
+{
+ return ToUnicode(vk, scancode, kbdBuffer, (LPWSTR) unicodeBuffer, 0, flag);
+
+}
+QT_END_INCLUDE_NAMESPACE
+
+#endif
+
+// Translate a VK into a Qt key code, or unicode character
+static inline int toKeyOrUnicode(int vk, int scancode, unsigned char *kbdBuffer, bool *isDeadkey = 0)
+{
+ Q_ASSERT(vk > 0 && vk < 256);
+ int code = 0;
+ QChar unicodeBuffer[5];
+ int res = ToUnicode(vk, scancode, kbdBuffer, reinterpret_cast<LPWSTR>(unicodeBuffer), 5, 0);
+ if (res)
+ code = unicodeBuffer[0].toUpper().unicode();
+
+ // Qt::Key_*'s are not encoded below 0x20, so try again, and DEL keys (0x7f) is encoded with a
+ // proper Qt::Key_ code
+ if (code < 0x20 || code == 0x7f) // Handles res==0 too
+ code = winceKeyBend(vk);
+
+ if (isDeadkey)
+ *isDeadkey = (res == -1);
+
+ return code == Qt::Key_unknown ? 0 : code;
+}
+
+Q_WIDGETS_EXPORT int qt_translateKeyCode(int vk)
+{
+ int code = winceKeyBend((vk < 0 || vk > 255) ? 0 : vk);
+ return code == Qt::Key_unknown ? 0 : code;
+}
+
+static inline int asciiToKeycode(char a, int state)
+{
+ if (a >= 'a' && a <= 'z')
+ a = toupper(a);
+ if ((state & Qt::ControlModifier) != 0) {
+ if (a >= 0 && a <= 31) // Ctrl+@..Ctrl+A..CTRL+Z..Ctrl+_
+ a += '@'; // to @..A..Z.._
+ }
+ return a & 0xff;
+}
+
+static inline bool isModifierKey(int code)
+{
+ return (code >= Qt::Key_Shift) && (code <= Qt::Key_ScrollLock);
+}
+// Key translation -----------------------------------------------------------------------[ end ]---
+
+
+static void qt_show_system_menu(QWidget* tlw)
+{
+ Q_ASSERT(tlw->testAttribute(Qt::WA_WState_Created));
+ HMENU menu = GetSystemMenu(tlw->internalWinId(), FALSE);
+ if (!menu)
+ return; // no menu for this window
+
+#define enabled (MF_BYCOMMAND | MF_ENABLED)
+#define disabled (MF_BYCOMMAND | MF_GRAYED)
+
+#ifndef Q_OS_WINCE
+ EnableMenuItem(menu, SC_MINIMIZE, (tlw->windowFlags() & Qt::WindowMinimizeButtonHint)?enabled:disabled);
+ bool maximized = IsZoomed(tlw->internalWinId());
+
+ EnableMenuItem(menu, SC_MAXIMIZE, ! (tlw->windowFlags() & Qt::WindowMaximizeButtonHint) || maximized?disabled:enabled);
+ EnableMenuItem(menu, SC_RESTORE, maximized?enabled:disabled);
+
+ // We should _not_ check with the setFixedSize(x,y) case here, since Windows is not able to check
+ // this and our menu here would be out-of-sync with the menu produced by mouse-click on the
+ // System Menu, or right-click on the title bar.
+ EnableMenuItem(menu, SC_SIZE, (tlw->windowFlags() & Qt::MSWindowsFixedSizeDialogHint) || maximized?disabled:enabled);
+ EnableMenuItem(menu, SC_MOVE, maximized?disabled:enabled);
+ EnableMenuItem(menu, SC_CLOSE, enabled);
+ // Set bold on close menu item
+ MENUITEMINFO closeItem;
+ closeItem.cbSize = sizeof(MENUITEMINFO);
+ closeItem.fMask = MIIM_STATE;
+ closeItem.fState = MFS_DEFAULT;
+ SetMenuItemInfo(menu, SC_CLOSE, FALSE, &closeItem);
+#endif
+
+#undef enabled
+#undef disabled
+ int ret = TrackPopupMenuEx(menu,
+ TPM_LEFTALIGN | TPM_TOPALIGN | TPM_NONOTIFY | TPM_RETURNCMD,
+ tlw->geometry().x(), tlw->geometry().y(),
+ tlw->internalWinId(),
+ 0);
+ if (ret)
+ QtWndProc(tlw->internalWinId(), WM_SYSCOMMAND, ret, 0);
+}
+
+
+// QETWidget class is only for accessing the sendSpontaneousEvent function in QApplication
+class QETWidget : public QWidget {
+public:
+ static bool sendSpontaneousEvent(QObject *r, QEvent *e)
+ { return QApplication::sendSpontaneousEvent(r, e); }
+};
+
+
+// Keyboard map private ----------------------------------------------------------------[ start ]---
+
+/*
+ \internal
+ A Windows KeyboardLayoutItem has 8 possible states:
+ 1. Unmodified
+ 2. Shift
+ 3. Control
+ 4. Control + Shift
+ 5. Alt
+ 6. Alt + Shift
+ 7. Alt + Control
+ 8. Alt + Control + Shift
+*/
+struct KeyboardLayoutItem {
+ bool dirty;
+ quint8 deadkeys;
+ quint32 qtKey[9]; // Can by any Qt::Key_<foo>, or unicode character
+};
+
+QKeyMapperPrivate::QKeyMapperPrivate()
+{
+ memset(keyLayout, 0, sizeof(keyLayout));
+}
+
+QKeyMapperPrivate::~QKeyMapperPrivate()
+{
+ deleteLayouts();
+}
+
+void QKeyMapperPrivate::deleteLayouts()
+{
+ for (int i = 0; i < 255; ++i) {
+ if (keyLayout[i]) {
+ delete keyLayout[i];
+ keyLayout[i] = 0;
+ }
+ }
+}
+
+void QKeyMapperPrivate::clearMappings()
+{
+ deleteLayouts();
+
+ /* MAKELCID()'s first argument is a WORD, and GetKeyboardLayout()
+ * returns a DWORD. */
+
+ LCID newLCID = MAKELCID((quintptr)GetKeyboardLayout(0), SORT_DEFAULT);
+// keyboardInputLocale = qt_localeFromLCID(newLCID);
+
+ bool bidi = false;
+ wchar_t LCIDFontSig[16];
+ if (GetLocaleInfo(newLCID, LOCALE_FONTSIGNATURE, LCIDFontSig, sizeof(LCIDFontSig) / sizeof(wchar_t))
+ && (LCIDFontSig[7] & (wchar_t)0x0800))
+ bidi = true;
+
+ keyboardInputDirection = bidi ? Qt::RightToLeft : Qt::LeftToRight;
+}
+
+void QKeyMapperPrivate::clearRecordedKeys()
+{
+ key_recorder.clearKeys();
+}
+
+
+inline void setKbdState(unsigned char *kbd, bool shift, bool ctrl, bool alt)
+{
+ kbd[VK_LSHIFT ] = (shift ? 0x80 : 0);
+ kbd[VK_SHIFT ] = (shift ? 0x80 : 0);
+ kbd[VK_LCONTROL] = (ctrl ? 0x80 : 0);
+ kbd[VK_CONTROL ] = (ctrl ? 0x80 : 0);
+ kbd[VK_RMENU ] = (alt ? 0x80 : 0);
+ kbd[VK_MENU ] = (alt ? 0x80 : 0);
+}
+
+void QKeyMapperPrivate::updateKeyMap(const MSG &msg)
+{
+ unsigned char kbdBuffer[256]; // Will hold the complete keyboard state
+ GetKeyboardState(kbdBuffer);
+ quint32 scancode = (msg.lParam >> 16) & 0xfff;
+ updatePossibleKeyCodes(kbdBuffer, scancode, msg.wParam);
+}
+
+void QKeyMapperPrivate::updatePossibleKeyCodes(unsigned char *kbdBuffer, quint32 scancode,
+ quint32 vk_key)
+{
+ if (!vk_key || (keyLayout[vk_key] && !keyLayout[vk_key]->dirty))
+ return;
+
+ if (!keyLayout[vk_key])
+ keyLayout[vk_key] = new KeyboardLayoutItem;
+
+ // Copy keyboard state, so we can modify and query output for each possible permutation
+ unsigned char buffer[256];
+ memcpy(buffer, kbdBuffer, sizeof(buffer));
+ // Always 0, as Windows doesn't treat these as modifiers;
+ buffer[VK_LWIN ] = 0;
+ buffer[VK_RWIN ] = 0;
+ buffer[VK_CAPITAL ] = 0;
+ buffer[VK_NUMLOCK ] = 0;
+ buffer[VK_SCROLL ] = 0;
+ // Always 0, since we'll only change the other versions
+ buffer[VK_RSHIFT ] = 0;
+ buffer[VK_RCONTROL] = 0;
+ buffer[VK_LMENU ] = 0; // Use right Alt, since left Ctrl + right Alt is considered AltGraph
+
+ bool isDeadKey = false;
+ keyLayout[vk_key]->deadkeys = 0;
+ keyLayout[vk_key]->dirty = false;
+ setKbdState(buffer, false, false, false);
+ keyLayout[vk_key]->qtKey[0] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
+ keyLayout[vk_key]->deadkeys |= isDeadKey ? 0x01 : 0;
+ setKbdState(buffer, true, false, false);
+ keyLayout[vk_key]->qtKey[1] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
+ keyLayout[vk_key]->deadkeys |= isDeadKey ? 0x02 : 0;
+ setKbdState(buffer, false, true, false);
+ keyLayout[vk_key]->qtKey[2] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
+ keyLayout[vk_key]->deadkeys |= isDeadKey ? 0x04 : 0;
+ setKbdState(buffer, true, true, false);
+ keyLayout[vk_key]->qtKey[3] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
+ keyLayout[vk_key]->deadkeys |= isDeadKey ? 0x08 : 0;
+ setKbdState(buffer, false, false, true);
+ keyLayout[vk_key]->qtKey[4] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
+ keyLayout[vk_key]->deadkeys |= isDeadKey ? 0x10 : 0;
+ setKbdState(buffer, true, false, true);
+ keyLayout[vk_key]->qtKey[5] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
+ keyLayout[vk_key]->deadkeys |= isDeadKey ? 0x20 : 0;
+ setKbdState(buffer, false, true, true);
+ keyLayout[vk_key]->qtKey[6] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
+ keyLayout[vk_key]->deadkeys |= isDeadKey ? 0x40 : 0;
+ setKbdState(buffer, true, true, true);
+ keyLayout[vk_key]->qtKey[7] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey);
+ keyLayout[vk_key]->deadkeys |= isDeadKey ? 0x80 : 0;
+ // Add a fall back key for layouts which don't do composition and show non-latin1 characters
+ int fallbackKey = winceKeyBend(vk_key);
+ if (!fallbackKey || fallbackKey == Qt::Key_unknown) {
+ fallbackKey = 0;
+ if (vk_key != keyLayout[vk_key]->qtKey[0] && vk_key < 0x5B && vk_key > 0x2F)
+ fallbackKey = vk_key;
+ }
+ keyLayout[vk_key]->qtKey[8] = fallbackKey;
+
+ // If this vk_key a Dead Key
+ if (MapVirtualKey(vk_key, 2) & 0x80000000) {
+ // Push a Space, then the original key through the low-level ToAscii functions.
+ // We do this because these functions (ToAscii / ToUnicode) will alter the internal state of
+ // the keyboard driver By doing the following, we set the keyboard driver state back to what
+ // it was before we wrecked it with the code above.
+ // We need to push the space with an empty keystate map, since the driver checks the map for
+ // transitions in modifiers, so this helps us capture all possible deadkeys.
+ unsigned char emptyBuffer[256];
+ memset(emptyBuffer, 0, sizeof(emptyBuffer));
+ ::ToAscii(VK_SPACE, 0, emptyBuffer, reinterpret_cast<LPWORD>(&buffer), 0);
+ ::ToAscii(vk_key, scancode, kbdBuffer, reinterpret_cast<LPWORD>(&buffer), 0);
+ }
+
+#ifdef DEBUG_KEYMAPPER
+ qDebug("updatePossibleKeyCodes for virtual key = 0x%02x!", vk_key);
+ for (int i = 0; i < 9; ++i) {
+ qDebug(" [%d] (%d,0x%02x,'%c') %s", i,
+ keyLayout[vk_key]->qtKey[i],
+ keyLayout[vk_key]->qtKey[i],
+ keyLayout[vk_key]->qtKey[i] ? keyLayout[vk_key]->qtKey[i] : 0x03,
+ keyLayout[vk_key]->deadkeys & (1<<i) ? "deadkey" : "");
+ }
+#endif // DEBUG_KEYMAPPER
+}
+
+bool QKeyMapperPrivate::isADeadKey(unsigned int vk_key, unsigned int modifiers)
+{
+ if (keyLayout && (vk_key < 256) && keyLayout[vk_key]) {
+ for(register int i = 0; i < 9; ++i) {
+ if (uint(ModsTbl[i]) == modifiers)
+ return bool(keyLayout[vk_key]->deadkeys & 1<<i);
+ }
+ }
+ return false;
+}
+
+extern bool qt_use_rtl_extensions;
+
+QList<int> QKeyMapperPrivate::possibleKeys(QKeyEvent *e)
+{
+ QList<int> result;
+
+ KeyboardLayoutItem *kbItem = keyLayout[e->nativeVirtualKey()];
+ if(!kbItem)
+ return result;
+
+ quint32 baseKey = kbItem->qtKey[0];
+ Qt::KeyboardModifiers keyMods = e->modifiers();
+ if (baseKey == Qt::Key_Return && (e->nativeModifiers() & ExtendedKey)) {
+ result << int(Qt::Key_Enter + keyMods);
+ return result;
+ }
+ result << int(baseKey + keyMods); // The base key is _always_ valid, of course
+
+ for(int i = 1; i < 9; ++i) {
+ Qt::KeyboardModifiers neededMods = ModsTbl[i];
+ quint32 key = kbItem->qtKey[i];
+ if (key && key != baseKey && ((keyMods & neededMods) == neededMods))
+ result << int(key + (keyMods & ~neededMods));
+ }
+
+ return result;
+}
+
+bool QKeyMapperPrivate::translateKeyEvent(QWidget *widget, const MSG &msg, bool grab)
+{
+ Q_Q(QKeyMapper);
+ Q_UNUSED(q); // Strange, but the compiler complains on q not being referenced, even if it is..
+ bool k0 = false;
+ bool k1 = false;
+ int msgType = msg.message;
+
+ quint32 scancode = (msg.lParam >> 16) & 0xfff;
+ quint32 vk_key = MapVirtualKey(scancode, 1);
+ bool isNumpad = (msg.wParam >= VK_NUMPAD0 && msg.wParam <= VK_NUMPAD9);
+ quint32 nModifiers = 0;
+
+#if defined(Q_OS_WINCE)
+ nModifiers |= (GetKeyState(VK_SHIFT ) < 0 ? ShiftAny : 0);
+ nModifiers |= (GetKeyState(VK_CONTROL) < 0 ? ControlAny : 0);
+ nModifiers |= (GetKeyState(VK_MENU ) < 0 ? AltAny : 0);
+ nModifiers |= (GetKeyState(VK_LWIN ) < 0 ? MetaLeft : 0);
+ nModifiers |= (GetKeyState(VK_RWIN ) < 0 ? MetaRight : 0);
+#else
+ // Map native modifiers to some bit representation
+ nModifiers |= (GetKeyState(VK_LSHIFT ) & 0x80 ? ShiftLeft : 0);
+ nModifiers |= (GetKeyState(VK_RSHIFT ) & 0x80 ? ShiftRight : 0);
+ nModifiers |= (GetKeyState(VK_LCONTROL) & 0x80 ? ControlLeft : 0);
+ nModifiers |= (GetKeyState(VK_RCONTROL) & 0x80 ? ControlRight : 0);
+ nModifiers |= (GetKeyState(VK_LMENU ) & 0x80 ? AltLeft : 0);
+ nModifiers |= (GetKeyState(VK_RMENU ) & 0x80 ? AltRight : 0);
+ nModifiers |= (GetKeyState(VK_LWIN ) & 0x80 ? MetaLeft : 0);
+ nModifiers |= (GetKeyState(VK_RWIN ) & 0x80 ? MetaRight : 0);
+ // Add Lock keys to the same bits
+ nModifiers |= (GetKeyState(VK_CAPITAL ) & 0x01 ? CapsLock : 0);
+ nModifiers |= (GetKeyState(VK_NUMLOCK ) & 0x01 ? NumLock : 0);
+ nModifiers |= (GetKeyState(VK_SCROLL ) & 0x01 ? ScrollLock : 0);
+#endif // Q_OS_WINCE
+
+ if (msg.lParam & ExtendedKey)
+ nModifiers |= msg.lParam & ExtendedKey;
+
+ // Get the modifier states (may be altered later, depending on key code)
+ int state = 0;
+ state |= (nModifiers & ShiftAny ? Qt::ShiftModifier : 0);
+ state |= (nModifiers & ControlAny ? Qt::ControlModifier : 0);
+ state |= (nModifiers & AltAny ? Qt::AltModifier : 0);
+ state |= (nModifiers & MetaAny ? Qt::MetaModifier : 0);
+
+ // Now we know enough to either have MapVirtualKey or our own keymap tell us if it's a deadkey
+ bool isDeadKey = isADeadKey(msg.wParam, state)
+ || MapVirtualKey(msg.wParam, 2) & 0x80000000;
+
+ // A multi-character key not found by our look-ahead
+ if (msgType == WM_CHAR) {
+ QString s;
+ QChar ch = QChar((ushort)msg.wParam);
+ if (!ch.isNull())
+ s += ch;
+
+ k0 = q->sendKeyEvent(widget, grab, QEvent::KeyPress, 0, Qt::KeyboardModifier(state), s, false, 0, scancode, vk_key, nModifiers);
+ k1 = q->sendKeyEvent(widget, grab, QEvent::KeyRelease, 0, Qt::KeyboardModifier(state), s, false, 0, scancode, vk_key, nModifiers);
+ }
+
+ // Input method characters not found by our look-ahead
+ else if (msgType == WM_IME_CHAR) {
+ QString s;
+ QChar ch = QChar((ushort)msg.wParam);
+ if (!ch.isNull())
+ s += ch;
+
+ k0 = q->sendKeyEvent(widget, grab, QEvent::KeyPress, 0, Qt::KeyboardModifier(state), s, false, 0, scancode, vk_key, nModifiers);
+ k1 = q->sendKeyEvent(widget, grab, QEvent::KeyRelease, 0, Qt::KeyboardModifier(state), s, false, 0, scancode, vk_key, nModifiers);
+ }
+
+ else {
+ // handle Directionality changes (BiDi) with RTL extensions
+ if (qt_use_rtl_extensions) {
+ static int dirStatus = 0;
+ if (!dirStatus && state == Qt::ControlModifier
+ && msg.wParam == VK_CONTROL
+ && msgType == WM_KEYDOWN) {
+ if (GetKeyState(VK_LCONTROL) < 0)
+ dirStatus = VK_LCONTROL;
+ else if (GetKeyState(VK_RCONTROL) < 0)
+ dirStatus = VK_RCONTROL;
+ } else if (dirStatus) {
+ if (msgType == WM_KEYDOWN) {
+ if (msg.wParam == VK_SHIFT) {
+ if (dirStatus == VK_LCONTROL && GetKeyState(VK_LSHIFT) < 0)
+ dirStatus = VK_LSHIFT;
+ else if (dirStatus == VK_RCONTROL && GetKeyState(VK_RSHIFT) < 0)
+ dirStatus = VK_RSHIFT;
+ } else {
+ dirStatus = 0;
+ }
+ } else if (msgType == WM_KEYUP) {
+ if (dirStatus == VK_LSHIFT
+ && ((msg.wParam == VK_SHIFT && GetKeyState(VK_LCONTROL))
+ || (msg.wParam == VK_CONTROL && GetKeyState(VK_LSHIFT)))) {
+ k0 = q->sendKeyEvent(widget, grab, QEvent::KeyPress, Qt::Key_Direction_L, 0,
+ QString(), false, 0,
+ scancode, msg.wParam, nModifiers);
+ k1 = q->sendKeyEvent(widget, grab, QEvent::KeyRelease, Qt::Key_Direction_L, 0,
+ QString(), false, 0,
+ scancode, msg.wParam, nModifiers);
+ dirStatus = 0;
+ } else if (dirStatus == VK_RSHIFT
+ && ( (msg.wParam == VK_SHIFT && GetKeyState(VK_RCONTROL))
+ || (msg.wParam == VK_CONTROL && GetKeyState(VK_RSHIFT)))) {
+ k0 = q->sendKeyEvent(widget, grab, QEvent::KeyPress, Qt::Key_Direction_R,
+ 0, QString(), false, 0,
+ scancode, msg.wParam, nModifiers);
+ k1 = q->sendKeyEvent(widget, grab, QEvent::KeyRelease, Qt::Key_Direction_R,
+ 0, QString(), false, 0,
+ scancode, msg.wParam, nModifiers);
+ dirStatus = 0;
+ } else {
+ dirStatus = 0;
+ }
+ } else {
+ dirStatus = 0;
+ }
+ }
+ }
+
+ // IME will process these keys, so simply return
+ if(msg.wParam == VK_PROCESSKEY)
+ return true;
+
+ // Ignore invalid virtual keycodes (see bugs 127424, QTBUG-3630)
+ if (msg.wParam == 0 || msg.wParam == 0xFF)
+ return true;
+
+ // Translate VK_* (native) -> Key_* (Qt) keys
+ // If it's a dead key, we cannot use the toKeyOrUnicode() function, since that will change
+ // the internal state of the keyboard driver, resulting in that dead keys no longer works.
+ // ..also if we're typing numbers on the keypad, while holding down the Alt modifier.
+ int code = 0;
+ if (isNumpad && (nModifiers & AltAny)) {
+ code = winceKeyBend(msg.wParam);
+ } else if (!isDeadKey) {
+ unsigned char kbdBuffer[256]; // Will hold the complete keyboard state
+ GetKeyboardState(kbdBuffer);
+ code = toKeyOrUnicode(msg.wParam, scancode, kbdBuffer);
+ }
+
+ // Invert state logic:
+ // If the key actually pressed is a modifier key, then we remove its modifier key from the
+ // state, since a modifier-key can't have itself as a modifier
+ if (code == Qt::Key_Control)
+ state = state ^ Qt::ControlModifier;
+ else if (code == Qt::Key_Shift)
+ state = state ^ Qt::ShiftModifier;
+ else if (code == Qt::Key_Alt)
+ state = state ^ Qt::AltModifier;
+
+ // If the bit 24 of lParm is set you received a enter,
+ // otherwise a Return. (This is the extended key bit)
+ if ((code == Qt::Key_Return) && (msg.lParam & 0x1000000))
+ code = Qt::Key_Enter;
+
+ // All cursor keys without extended bit
+ if (!(msg.lParam & 0x1000000)) {
+ switch (code) {
+ case Qt::Key_Left:
+ case Qt::Key_Right:
+ case Qt::Key_Up:
+ case Qt::Key_Down:
+ case Qt::Key_PageUp:
+ case Qt::Key_PageDown:
+ case Qt::Key_Home:
+ case Qt::Key_End:
+ case Qt::Key_Insert:
+ case Qt::Key_Delete:
+ case Qt::Key_Asterisk:
+ case Qt::Key_Plus:
+ case Qt::Key_Minus:
+ case Qt::Key_Period:
+ case Qt::Key_0:
+ case Qt::Key_1:
+ case Qt::Key_2:
+ case Qt::Key_3:
+ case Qt::Key_4:
+ case Qt::Key_5:
+ case Qt::Key_6:
+ case Qt::Key_7:
+ case Qt::Key_8:
+ case Qt::Key_9:
+ state |= ((msg.wParam >= '0' && msg.wParam <= '9')
+ || (msg.wParam >= VK_OEM_PLUS && msg.wParam <= VK_OEM_3))
+ ? 0 : Qt::KeypadModifier;
+ default:
+ if ((uint)msg.lParam == 0x004c0001 || (uint)msg.lParam == 0xc04c0001)
+ state |= Qt::KeypadModifier;
+ break;
+ }
+ }
+ // Other keys with with extended bit
+ else {
+ switch (code) {
+ case Qt::Key_Enter:
+ case Qt::Key_Slash:
+ case Qt::Key_NumLock:
+ state |= Qt::KeypadModifier;
+ default:
+ break;
+ }
+ }
+
+ // KEYDOWN ---------------------------------------------------------------------------------
+ if (msgType == WM_KEYDOWN || msgType == WM_IME_KEYDOWN || msgType == WM_SYSKEYDOWN) {
+ // Get the last record of this key press, so we can validate the current state
+ // The record is not removed from the list
+ KeyRecord *rec = key_recorder.findKey(msg.wParam, false);
+
+ // If rec's state doesn't match the current state, something has changed behind our back
+ // (Consumed by modal widget is one possibility) So, remove the record from the list
+ // This will stop the auto-repeat of the key, should a modifier change, for example
+ if (rec && rec->state != state) {
+ key_recorder.findKey(msg.wParam, true);
+ rec = 0;
+ }
+
+ // Find unicode character from Windows Message Queue
+ MSG wm_char;
+ UINT charType = (msgType == WM_KEYDOWN
+ ? WM_CHAR
+ : msgType == WM_IME_KEYDOWN ? WM_IME_CHAR : WM_SYSCHAR);
+
+ QChar uch;
+ if (PeekMessage(&wm_char, 0, charType, charType, PM_REMOVE)) {
+ // Found a ?_CHAR
+ uch = QChar((ushort)wm_char.wParam);
+ if (msgType == WM_SYSKEYDOWN && uch.isLetter() && (msg.lParam & KF_ALTDOWN))
+ uch = uch.toLower(); // (See doc of WM_SYSCHAR) Alt-letter
+ if (!code && !uch.row())
+ code = asciiToKeycode(uch.cell(), state);
+ }
+
+ // Special handling for the WM_IME_KEYDOWN message. Microsoft IME (Korean) will not
+ // generate a WM_IME_CHAR message corresponding to this message. We might get wrong
+ // results, if we map this virtual key-code directly (for eg '?' US layouts). So try
+ // to find the correct key using the current message parameters & keyboard state.
+ if (uch.isNull() && msgType == WM_IME_KEYDOWN) {
+ BYTE keyState[256];
+ wchar_t newKey[3] = {0};
+ GetKeyboardState(keyState);
+ int val = ToUnicode(vk_key, scancode, keyState, newKey, 2, 0);
+ if (val == 1) {
+ uch = QChar(newKey[0]);
+ } else {
+ // If we are still not able to find a unicode key, pass the WM_IME_KEYDOWN
+ // message to DefWindowProc() for generating a proper WM_KEYDOWN.
+ return false;
+ }
+ }
+
+ // If no ?_CHAR was found in the queue; deduct character from the ?_KEYDOWN parameters
+ if (uch.isNull()) {
+ if (msg.wParam == VK_DELETE) {
+ uch = QChar(QLatin1Char(0x7f)); // Windows doesn't know this one.
+ } else {
+ if (msgType != WM_SYSKEYDOWN || !code) {
+ UINT map = MapVirtualKey(msg.wParam, 2);
+ // If the high bit of the return value is set, it's a deadkey
+ if (!(map & 0x80000000))
+ uch = QChar((ushort)map);
+ }
+ }
+ if (!code && !uch.row())
+ code = asciiToKeycode(uch.cell(), state);
+ }
+
+ // Special handling of global Windows hotkeys
+ if (state == Qt::AltModifier) {
+ switch (code) {
+ case Qt::Key_Escape:
+ case Qt::Key_Tab:
+ case Qt::Key_Enter:
+ case Qt::Key_F4:
+ return false; // Send the event on to Windows
+ case Qt::Key_Space:
+ // do not pass this key to windows, we will process it ourselves
+ qt_show_system_menu(widget->window());
+ return true;
+ default:
+ break;
+ }
+ }
+
+ // Map SHIFT + Tab to SHIFT + BackTab, QShortcutMap knows about this translation
+ if (code == Qt::Key_Tab && (state & Qt::ShiftModifier) == Qt::ShiftModifier)
+ code = Qt::Key_Backtab;
+
+ // If we have a record, it means that the key is already pressed, the state is the same
+ // so, we have an auto-repeating key
+ if (rec) {
+ if (code < Qt::Key_Shift || code > Qt::Key_ScrollLock) {
+ k0 = q->sendKeyEvent(widget, grab, QEvent::KeyRelease, code,
+ Qt::KeyboardModifier(state), rec->text, true, 0,
+ scancode, msg.wParam, nModifiers);
+ k1 = q->sendKeyEvent(widget, grab, QEvent::KeyPress, code,
+ Qt::KeyboardModifier(state), rec->text, true, 0,
+ scancode, msg.wParam, nModifiers);
+ }
+ }
+ // No record of the key being previous pressed, so we now send a QEvent::KeyPress event,
+ // and store the key data into our records.
+ else {
+ QString text;
+ if (!uch.isNull())
+ text += uch;
+ char a = uch.row() ? 0 : uch.cell();
+ key_recorder.storeKey(msg.wParam, a, state, text);
+ k0 = q->sendKeyEvent(widget, grab, QEvent::KeyPress, code, Qt::KeyboardModifier(state),
+ text, false, 0, scancode, msg.wParam, nModifiers);
+
+ bool store = true;
+ // Alt+<alphanumerical> go to the Win32 menu system if unhandled by Qt
+#if !defined(Q_OS_WINCE)
+ if (msgType == WM_SYSKEYDOWN && !k0 && a) {
+ HWND parent = GetParent(widget->internalWinId());
+ while (parent) {
+ if (GetMenu(parent)) {
+ SendMessage(parent, WM_SYSCOMMAND, SC_KEYMENU, a);
+ store = false;
+ k0 = true;
+ break;
+ }
+ parent = GetParent(parent);
+ }
+ }
+#endif
+ if (!store)
+ key_recorder.findKey(msg.wParam, true);
+ }
+ }
+
+ // KEYUP -----------------------------------------------------------------------------------
+ else {
+ // Try to locate the key in our records, and remove it if it exists.
+ // The key may not be in our records if, for example, the down event was handled by
+ // win32 natively, or our window gets focus while a key is already press, but now gets
+ // the key release event.
+ KeyRecord* rec = key_recorder.findKey(msg.wParam, true);
+ if (!rec && !(code == Qt::Key_Shift
+ || code == Qt::Key_Control
+ || code == Qt::Key_Meta
+ || code == Qt::Key_Alt)) {
+ // Someone ate the key down event
+ } else {
+ if (!code)
+ code = asciiToKeycode(rec->ascii ? rec->ascii : msg.wParam, state);
+
+ // Map SHIFT + Tab to SHIFT + BackTab, QShortcutMap knows about this translation
+ if (code == Qt::Key_Tab && (state & Qt::ShiftModifier) == Qt::ShiftModifier)
+ code = Qt::Key_Backtab;
+
+ k0 = q->sendKeyEvent(widget, grab, QEvent::KeyRelease, code, Qt::KeyboardModifier(state),
+ (rec ? rec->text : QString()), false, 0, scancode, msg.wParam, nModifiers);
+
+ // don't pass Alt to Windows unless we are embedded in a non-Qt window
+#if !defined(Q_OS_WINCE)
+ if (code == Qt::Key_Alt) {
+ k0 = true;
+ HWND parent = GetParent(widget->internalWinId());
+ while (parent) {
+ if (!QWidget::find(parent) && GetMenu(parent)) {
+ k0 = false;
+ break;
+ }
+ parent = GetParent(parent);
+ }
+ }
+#endif
+ }
+ }
+ }
+
+ // Return true, if a QKeyEvent was sent to a widget
+ return k0 || k1;
+}
+
+
+// QKeyMapper (Windows) implementation -------------------------------------------------[ start ]---
+
+bool QKeyMapper::sendKeyEvent(QWidget *widget, bool grab,
+ QEvent::Type type, int code, Qt::KeyboardModifiers modifiers,
+ const QString &text, bool autorepeat, int count,
+ quint32 nativeScanCode, quint32 nativeVirtualKey, quint32 nativeModifiers,
+ bool *)
+{
+#if defined(Q_OS_WINCE)
+ Q_UNUSED(grab);
+#endif
+ Q_UNUSED(count);
+#if defined QT3_SUPPORT && !defined(QT_NO_SHORTCUT)
+ if (type == QEvent::KeyPress
+ && !grab
+ && QApplicationPrivate::instance()->use_compat()) {
+ // send accel events if the keyboard is not grabbed
+ QKeyEventEx a(type, code, modifiers,
+ text, autorepeat, qMax(1, int(text.length())),
+ nativeScanCode, nativeVirtualKey, nativeModifiers);
+ if (QApplicationPrivate::instance()->qt_tryAccelEvent(widget, &a))
+ return true;
+ }
+#else
+ Q_UNUSED(grab);
+#endif
+ if (!widget->isEnabled())
+ return false;
+
+ QKeyEventEx e(type, code, modifiers,
+ text, autorepeat, qMax(1, int(text.length())),
+ nativeScanCode, nativeVirtualKey, nativeModifiers);
+ QETWidget::sendSpontaneousEvent(widget, &e);
+
+ if (!isModifierKey(code)
+ && modifiers == Qt::AltModifier
+ && ((code >= Qt::Key_A && code <= Qt::Key_Z) || (code >= Qt::Key_0 && code <= Qt::Key_9))
+ && type == QEvent::KeyPress
+ && !e.isAccepted())
+ QApplication::beep(); // Emulate windows behavior
+
+ return e.isAccepted();
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/win/qmime_win.cpp b/src/widgets/platforms/win/qmime_win.cpp
new file mode 100644
index 0000000000..c974d53163
--- /dev/null
+++ b/src/widgets/platforms/win/qmime_win.cpp
@@ -0,0 +1,1556 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmime.h"
+
+#include "qimagereader.h"
+#include "qimagewriter.h"
+#include "qdatastream.h"
+#include "qbuffer.h"
+#include "qt_windows.h"
+#include "qapplication_p.h"
+#include "qtextcodec.h"
+#include "qregexp.h"
+#include "qalgorithms.h"
+#include "qmap.h"
+#include "qdnd_p.h"
+#include <shlobj.h>
+#include "qurl.h"
+#include "qvariant.h"
+#include "qtextdocument.h"
+#include "qdir.h"
+
+#if defined(Q_OS_WINCE)
+#include "qguifunctions_wince.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_IMAGEFORMAT_BMP
+#ifndef CF_DIBV5
+#define CF_DIBV5 17
+#endif
+/* The MSVC compilers allows multi-byte characters, that has the behavior of
+ * that each character gets shifted into position. 0x73524742 below is for MSVC
+ * equivalent to doing 'sRGB', but this does of course not work
+ * on conformant C++ compilers. */
+#define BMP_LCS_sRGB 0x73524742
+#define BMP_LCS_GM_IMAGES 0x00000004L
+
+struct _CIEXYZ {
+ long ciexyzX, ciexyzY, ciexyzZ;
+};
+
+struct _CIEXYZTRIPLE {
+ _CIEXYZ ciexyzRed, ciexyzGreen, ciexyzBlue;
+};
+
+struct BMP_BITMAPV5HEADER {
+ DWORD bV5Size;
+ LONG bV5Width;
+ LONG bV5Height;
+ WORD bV5Planes;
+ WORD bV5BitCount;
+ DWORD bV5Compression;
+ DWORD bV5SizeImage;
+ LONG bV5XPelsPerMeter;
+ LONG bV5YPelsPerMeter;
+ DWORD bV5ClrUsed;
+ DWORD bV5ClrImportant;
+ DWORD bV5RedMask;
+ DWORD bV5GreenMask;
+ DWORD bV5BlueMask;
+ DWORD bV5AlphaMask;
+ DWORD bV5CSType;
+ _CIEXYZTRIPLE bV5Endpoints;
+ DWORD bV5GammaRed;
+ DWORD bV5GammaGreen;
+ DWORD bV5GammaBlue;
+ DWORD bV5Intent;
+ DWORD bV5ProfileData;
+ DWORD bV5ProfileSize;
+ DWORD bV5Reserved;
+};
+static const int BMP_BITFIELDS = 3;
+
+extern bool qt_read_dib(QDataStream&, QImage&); // qimage.cpp
+extern bool qt_write_dib(QDataStream&, QImage); // qimage.cpp
+static bool qt_write_dibv5(QDataStream &s, QImage image);
+static bool qt_read_dibv5(QDataStream &s, QImage &image);
+#endif
+
+//#define QMIME_DEBUG
+
+
+// helpers for using global memory
+
+static int getCf(const FORMATETC &formatetc)
+{
+ return formatetc.cfFormat;
+}
+
+static FORMATETC setCf(int cf)
+{
+ FORMATETC formatetc;
+ formatetc.cfFormat = cf;
+ formatetc.dwAspect = DVASPECT_CONTENT;
+ formatetc.lindex = -1;
+ formatetc.ptd = NULL;
+ formatetc.tymed = TYMED_HGLOBAL;
+ return formatetc;
+}
+
+static bool setData(const QByteArray &data, STGMEDIUM *pmedium)
+{
+ HGLOBAL hData = GlobalAlloc(0, data.size());
+ if (!hData)
+ return false;
+
+ void *out = GlobalLock(hData);
+ memcpy(out, data.data(), data.size());
+ GlobalUnlock(hData);
+ pmedium->tymed = TYMED_HGLOBAL;
+ pmedium->hGlobal = hData;
+ pmedium->pUnkForRelease = 0;
+ return true;
+}
+
+static QByteArray getData(int cf, IDataObject *pDataObj)
+{
+ QByteArray data;
+ FORMATETC formatetc = setCf(cf);
+ STGMEDIUM s;
+ if (pDataObj->GetData(&formatetc, &s) == S_OK) {
+ DWORD * val = (DWORD*)GlobalLock(s.hGlobal);
+ data = QByteArray::fromRawData((char*)val, GlobalSize(s.hGlobal));
+ data.detach();
+ GlobalUnlock(s.hGlobal);
+ ReleaseStgMedium(&s);
+ } else {
+ //Try reading IStream data
+ formatetc.tymed = TYMED_ISTREAM;
+ if (pDataObj->GetData(&formatetc, &s) == S_OK) {
+ char szBuffer[4096];
+ ULONG actualRead = 0;
+ LARGE_INTEGER pos = {{0, 0}};
+ //Move to front (can fail depending on the data model implemented)
+ HRESULT hr = s.pstm->Seek(pos, STREAM_SEEK_SET, NULL);
+ while(SUCCEEDED(hr)){
+ hr = s.pstm->Read(szBuffer, sizeof(szBuffer), &actualRead);
+ if (SUCCEEDED(hr) && actualRead > 0) {
+ data += QByteArray::fromRawData(szBuffer, actualRead);
+ }
+ if (actualRead != sizeof(szBuffer))
+ break;
+ }
+ data.detach();
+ ReleaseStgMedium(&s);
+ }
+ }
+ return data;
+}
+
+static bool canGetData(int cf, IDataObject * pDataObj)
+{
+ FORMATETC formatetc = setCf(cf);
+ if (pDataObj->QueryGetData(&formatetc) != S_OK){
+ formatetc.tymed = TYMED_ISTREAM;
+ return pDataObj->QueryGetData(&formatetc) == S_OK;
+ }
+ return true;
+}
+
+class QWindowsMimeList
+{
+public:
+ QWindowsMimeList();
+ ~QWindowsMimeList();
+ void addWindowsMime(QWindowsMime * mime);
+ void removeWindowsMime(QWindowsMime * mime);
+ QList<QWindowsMime*> windowsMimes();
+
+private:
+ void init();
+ bool initialized;
+ QList<QWindowsMime*> mimes;
+};
+
+Q_GLOBAL_STATIC(QWindowsMimeList, theMimeList);
+
+
+/*!
+ \class QWindowsMime
+ \brief The QWindowsMime class maps open-standard MIME to Window Clipboard formats.
+ \ingroup draganddrop
+
+ Qt's drag-and-drop and clipboard facilities use the MIME standard.
+ On X11, this maps trivially to the Xdnd protocol, but on Windows
+ although some applications use MIME types to describe clipboard
+ formats, others use arbitrary non-standardized naming conventions,
+ or unnamed built-in formats of Windows.
+
+ By instantiating subclasses of QWindowsMime that provide conversions
+ between Windows Clipboard and MIME formats, you can convert
+ proprietary clipboard formats to MIME formats.
+
+ Qt has predefined support for the following Windows Clipboard formats:
+
+ \table
+ \header \o Windows Format \o Equivalent MIME type
+ \row \o \c CF_UNICODETEXT \o \c text/plain
+ \row \o \c CF_TEXT \o \c text/plain
+ \row \o \c CF_DIB \o \c{image/xyz}, where \c xyz is
+ a \l{QImageWriter::supportedImageFormats()}{Qt image format}
+ \row \o \c CF_HDROP \o \c text/uri-list
+ \row \o \c CF_INETURL \o \c text/uri-list
+ \row \o \c CF_HTML \o \c text/html
+ \endtable
+
+ An example use of this class would be to map the Windows Metafile
+ clipboard format (\c CF_METAFILEPICT) to and from the MIME type
+ \c{image/x-wmf}. This conversion might simply be adding or removing
+ a header, or even just passing on the data. See \l{Drag and Drop}
+ for more information on choosing and definition MIME types.
+
+ You can check if a MIME type is convertible using canConvertFromMime() and
+ can perform conversions with convertToMime() and convertFromMime().
+*/
+
+/*!
+Constructs a new conversion object, adding it to the globally accessed
+list of available converters.
+*/
+QWindowsMime::QWindowsMime()
+{
+ theMimeList()->addWindowsMime(this);
+}
+
+/*!
+Destroys a conversion object, removing it from the global
+list of available converters.
+*/
+QWindowsMime::~QWindowsMime()
+{
+ theMimeList()->removeWindowsMime(this);
+}
+
+
+/*!
+ Registers the MIME type \a mime, and returns an ID number
+ identifying the format on Windows.
+*/
+int QWindowsMime::registerMimeType(const QString &mime)
+{
+ int f = RegisterClipboardFormat(reinterpret_cast<const wchar_t *> (mime.utf16()));
+ if (!f)
+ qErrnoWarning("QWindowsMime::registerMimeType: Failed to register clipboard format");
+
+ return f;
+}
+
+
+/*!
+\fn bool QWindowsMime::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
+
+ Returns true if the converter can convert from the \a mimeData to
+ the format specified in \a formatetc.
+
+ All subclasses must reimplement this pure virtual function.
+*/
+
+/*!
+ \fn bool QWindowsMime::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const
+
+ Returns true if the converter can convert to the \a mimeType from
+ the available formats in \a pDataObj.
+
+ All subclasses must reimplement this pure virtual function.
+*/
+
+/*!
+\fn QString QWindowsMime::mimeForFormat(const FORMATETC &formatetc) const
+
+ Returns the mime type that will be created form the format specified
+ in \a formatetc, or an empty string if this converter does not support
+ \a formatetc.
+
+ All subclasses must reimplement this pure virtual function.
+*/
+
+/*!
+\fn QVector<FORMATETC> QWindowsMime::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const
+
+ Returns a QVector of FORMATETC structures representing the different windows clipboard
+ formats that can be provided for the \a mimeType from the \a mimeData.
+
+ All subclasses must reimplement this pure virtual function.
+*/
+
+/*!
+ \fn QVariant QWindowsMime::convertToMime(const QString &mimeType, IDataObject *pDataObj,
+ QVariant::Type preferredType) const
+
+ Returns a QVariant containing the converted data for \a mimeType from \a pDataObj.
+ If possible the QVariant should be of the \a preferredType to avoid needless conversions.
+
+ All subclasses must reimplement this pure virtual function.
+*/
+
+/*!
+\fn bool QWindowsMime::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const
+
+ Convert the \a mimeData to the format specified in \a formatetc.
+ The converted data should then be placed in \a pmedium structure.
+
+ Return true if the conversion was successful.
+
+ All subclasses must reimplement this pure virtual function.
+*/
+
+
+QWindowsMime *QWindowsMime::converterFromMime(const FORMATETC &formatetc, const QMimeData *mimeData)
+{
+ QList<QWindowsMime*> mimes = theMimeList()->windowsMimes();
+ for (int i=mimes.size()-1; i>=0; --i) {
+ if (mimes.at(i)->canConvertFromMime(formatetc, mimeData))
+ return mimes.at(i);
+ }
+ return 0;
+}
+
+QWindowsMime *QWindowsMime::converterToMime(const QString &mimeType, IDataObject *pDataObj)
+{
+ QList<QWindowsMime*> mimes = theMimeList()->windowsMimes();
+ for (int i=mimes.size()-1; i>=0; --i) {
+ if (mimes.at(i)->canConvertToMime(mimeType, pDataObj))
+ return mimes.at(i);
+ }
+ return 0;
+}
+
+QVector<FORMATETC> QWindowsMime::allFormatsForMime(const QMimeData *mimeData)
+{
+ QList<QWindowsMime*> mimes = theMimeList()->windowsMimes();
+ QVector<FORMATETC> formatics;
+ formatics.reserve(20);
+#ifndef QT_NO_DRAGANDDROP
+ QStringList formats = QInternalMimeData::formatsHelper(mimeData);
+ for (int f=0; f<formats.size(); ++f) {
+ for (int i=mimes.size()-1; i>=0; --i)
+ formatics += mimes.at(i)->formatsForMime(formats.at(f), mimeData);
+ }
+#else
+ Q_UNUSED(mimeData);
+#endif //QT_NO_DRAGANDDROP
+ return formatics;
+}
+
+QStringList QWindowsMime::allMimesForFormats(IDataObject *pDataObj)
+{
+ QList<QWindowsMime*> mimes = theMimeList()->windowsMimes();
+ QStringList formats;
+ LPENUMFORMATETC FAR fmtenum;
+ HRESULT hr = pDataObj->EnumFormatEtc(DATADIR_GET, &fmtenum);
+
+ if (hr == NOERROR) {
+ FORMATETC fmtetc;
+ while (S_OK == fmtenum->Next(1, &fmtetc, 0)) {
+#if defined(QMIME_DEBUG) && !defined(Q_OS_WINCE)
+ qDebug("QWindowsMime::allMimesForFormats()");
+ wchar_t buf[256] = {0};
+ GetClipboardFormatName(fmtetc.cfFormat, buf, 255);
+ qDebug("CF = %d : %s", fmtetc.cfFormat, QString::fromWCharArray(buf));
+#endif
+ for (int i=mimes.size()-1; i>=0; --i) {
+ QString format = mimes.at(i)->mimeForFormat(fmtetc);
+ if (!format.isEmpty() && !formats.contains(format)) {
+ formats += format;
+ }
+ }
+ // as documented in MSDN to avoid possible memleak
+ if (fmtetc.ptd)
+ CoTaskMemFree(fmtetc.ptd);
+ }
+ fmtenum->Release();
+ }
+
+ return formats;
+}
+
+
+class QWindowsMimeText : public QWindowsMime
+{
+public:
+ bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const;
+ QVariant convertToMime(const QString &mime, LPDATAOBJECT pDataObj, QVariant::Type preferredType) const;
+ QString mimeForFormat(const FORMATETC &formatetc) const;
+ bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const;
+ bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const;
+ QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const;
+};
+
+bool QWindowsMimeText::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
+{
+ int cf = getCf(formatetc);
+ return (cf == CF_UNICODETEXT || cf == CF_TEXT) && mimeData->hasText();
+}
+
+/*
+text/plain is defined as using CRLF, but so many programs don't,
+and programmers just look for '\n' in strings.
+Windows really needs CRLF, so we ensure it here.
+*/
+bool QWindowsMimeText::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const
+{
+ if (canConvertFromMime(formatetc, mimeData)) {
+ QByteArray data;
+ int cf = getCf(formatetc);
+ if (cf == CF_TEXT) {
+ data = mimeData->text().toLocal8Bit();
+ // Anticipate required space for CRLFs at 1/40
+ int maxsize=data.size()+data.size()/40+3;
+ QByteArray r(maxsize, '\0');
+ char* o = r.data();
+ const char* d = data.data();
+ const int s = data.size();
+ bool cr=false;
+ int j=0;
+ for (int i=0; i<s; i++) {
+ char c = d[i];
+ if (c=='\r')
+ cr=true;
+ else {
+ if (c=='\n') {
+ if (!cr)
+ o[j++]='\r';
+ }
+ cr=false;
+ }
+ o[j++]=c;
+ if (j+3 >= maxsize) {
+ maxsize += maxsize/4;
+ r.resize(maxsize);
+ o = r.data();
+ }
+ }
+ o[j]=0;
+ return setData(r, pmedium);
+ } else if (cf == CF_UNICODETEXT) {
+ QString str = mimeData->text();
+ const QChar *u = str.unicode();
+ QString res;
+ const int s = str.length();
+ int maxsize = s + s/40 + 3;
+ res.resize(maxsize);
+ int ri = 0;
+ bool cr = false;
+ for (int i=0; i < s; ++i) {
+ if (*u == QLatin1Char('\r'))
+ cr = true;
+ else {
+ if (*u == QLatin1Char('\n') && !cr)
+ res[ri++] = QLatin1Char('\r');
+ cr = false;
+ }
+ res[ri++] = *u;
+ if (ri+3 >= maxsize) {
+ maxsize += maxsize/4;
+ res.resize(maxsize);
+ }
+ ++u;
+ }
+ res.truncate(ri);
+ const int byteLength = res.length() * sizeof(ushort);
+ QByteArray r(byteLength + 2, '\0');
+ memcpy(r.data(), res.unicode(), byteLength);
+ r[byteLength] = 0;
+ r[byteLength+1] = 0;
+ return setData(r, pmedium);
+ }
+ }
+ return false;
+}
+
+bool QWindowsMimeText::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const
+{
+ return mimeType.startsWith(QLatin1String("text/plain"))
+ && (canGetData(CF_UNICODETEXT, pDataObj)
+ || canGetData(CF_TEXT, pDataObj));
+}
+
+QString QWindowsMimeText::mimeForFormat(const FORMATETC &formatetc) const
+{
+ int cf = getCf(formatetc);
+ if (cf == CF_UNICODETEXT || cf == CF_TEXT)
+ return QLatin1String("text/plain");
+ return QString();
+}
+
+
+QVector<FORMATETC> QWindowsMimeText::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const
+{
+ QVector<FORMATETC> formatics;
+ if (mimeType.startsWith(QLatin1String("text/plain")) && mimeData->hasText()) {
+ formatics += setCf(CF_UNICODETEXT);
+ formatics += setCf(CF_TEXT);
+ }
+ return formatics;
+}
+
+QVariant QWindowsMimeText::convertToMime(const QString &mime, LPDATAOBJECT pDataObj, QVariant::Type preferredType) const
+{
+ QVariant ret;
+
+ if (canConvertToMime(mime, pDataObj)) {
+ QString str;
+ QByteArray data = getData(CF_UNICODETEXT, pDataObj);
+ if (!data.isEmpty()) {
+ str = QString::fromWCharArray((const wchar_t *)data.data());
+ str.replace(QLatin1String("\r\n"), QLatin1String("\n"));
+ } else {
+ data = getData(CF_TEXT, pDataObj);
+ if (!data.isEmpty()) {
+ const char* d = data.data();
+ const int s = qstrlen(d);
+ QByteArray r(data.size()+1, '\0');
+ char* o = r.data();
+ int j=0;
+ for (int i=0; i<s; i++) {
+ char c = d[i];
+ if (c!='\r')
+ o[j++]=c;
+ }
+ o[j]=0;
+ str = QString::fromLocal8Bit(r);
+ }
+ }
+ if (preferredType == QVariant::String)
+ ret = str;
+ else
+ ret = str.toUtf8();
+ }
+
+ return ret;
+}
+
+class QWindowsMimeURI : public QWindowsMime
+{
+public:
+ QWindowsMimeURI();
+ bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const;
+ QVariant convertToMime(const QString &mime, LPDATAOBJECT pDataObj, QVariant::Type preferredType) const;
+ QString mimeForFormat(const FORMATETC &formatetc) const;
+ bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const;
+ bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const;
+ QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const;
+private:
+ int CF_INETURL_W; // wide char version
+ int CF_INETURL;
+};
+
+QWindowsMimeURI::QWindowsMimeURI()
+{
+ CF_INETURL_W = QWindowsMime::registerMimeType(QLatin1String("UniformResourceLocatorW"));
+ CF_INETURL = QWindowsMime::registerMimeType(QLatin1String("UniformResourceLocator"));
+}
+
+bool QWindowsMimeURI::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
+{
+ if (getCf(formatetc) == CF_HDROP) {
+ QList<QUrl> urls = mimeData->urls();
+ for (int i=0; i<urls.size(); i++) {
+ if (!urls.at(i).toLocalFile().isEmpty())
+ return true;
+ }
+ }
+ return (getCf(formatetc) == CF_INETURL_W || getCf(formatetc) == CF_INETURL) && mimeData->hasFormat(QLatin1String("text/uri-list"));
+}
+
+bool QWindowsMimeURI::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const
+{
+ if (canConvertFromMime(formatetc, mimeData)) {
+ if (getCf(formatetc) == CF_HDROP) {
+ QList<QUrl> urls = mimeData->urls();
+ QStringList fileNames;
+ int size = sizeof(DROPFILES)+2;
+ for (int i=0; i<urls.size(); i++) {
+ QString fn = QDir::toNativeSeparators(urls.at(i).toLocalFile());
+ if (!fn.isEmpty()) {
+ size += sizeof(ushort) * (fn.length() + 1);
+ fileNames.append(fn);
+ }
+ }
+
+ QByteArray result(size, '\0');
+ DROPFILES* d = (DROPFILES*)result.data();
+ d->pFiles = sizeof(DROPFILES);
+ GetCursorPos(&d->pt); // try
+ d->fNC = true;
+ char* files = ((char*)d) + d->pFiles;
+
+ d->fWide = true;
+ wchar_t* f = (wchar_t*)files;
+ for (int i=0; i<fileNames.size(); i++) {
+ int l = fileNames.at(i).length();
+ memcpy(f, fileNames.at(i).utf16(), l * sizeof(ushort));
+ f += l;
+ *f++ = 0;
+ }
+ *f = 0;
+
+ return setData(result, pmedium);
+ } else if (getCf(formatetc) == CF_INETURL_W) {
+ QList<QUrl> urls = mimeData->urls();
+ QByteArray result;
+ if (!urls.isEmpty()) {
+ QString url = urls.at(0).toString();
+ result = QByteArray((const char *)url.utf16(), url.length() * sizeof(ushort));
+ }
+ result.append('\0');
+ result.append('\0');
+ return setData(result, pmedium);
+ } else if (getCf(formatetc) == CF_INETURL) {
+ QList<QUrl> urls = mimeData->urls();
+ QByteArray result;
+ if (!urls.isEmpty())
+ result = urls.at(0).toString().toLocal8Bit();
+ return setData(result, pmedium);
+ }
+ }
+
+ return false;
+}
+
+bool QWindowsMimeURI::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const
+{
+ return mimeType == QLatin1String("text/uri-list")
+ && (canGetData(CF_HDROP, pDataObj) || canGetData(CF_INETURL_W, pDataObj) || canGetData(CF_INETURL, pDataObj));
+}
+
+QString QWindowsMimeURI::mimeForFormat(const FORMATETC &formatetc) const
+{
+ QString format;
+ if (getCf(formatetc) == CF_HDROP || getCf(formatetc) == CF_INETURL_W || getCf(formatetc) == CF_INETURL)
+ format = QLatin1String("text/uri-list");
+ return format;
+}
+
+QVector<FORMATETC> QWindowsMimeURI::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const
+{
+ QVector<FORMATETC> formatics;
+ if (mimeType == QLatin1String("text/uri-list")) {
+ if (canConvertFromMime(setCf(CF_HDROP), mimeData))
+ formatics += setCf(CF_HDROP);
+ if (canConvertFromMime(setCf(CF_INETURL_W), mimeData))
+ formatics += setCf(CF_INETURL_W);
+ if (canConvertFromMime(setCf(CF_INETURL), mimeData))
+ formatics += setCf(CF_INETURL);
+ }
+ return formatics;
+}
+
+QVariant QWindowsMimeURI::convertToMime(const QString &mimeType, LPDATAOBJECT pDataObj, QVariant::Type preferredType) const
+{
+ if (mimeType == QLatin1String("text/uri-list")) {
+ if (canGetData(CF_HDROP, pDataObj)) {
+ QByteArray texturi;
+ QList<QVariant> urls;
+
+ QByteArray data = getData(CF_HDROP, pDataObj);
+ if (data.isEmpty())
+ return QVariant();
+
+ LPDROPFILES hdrop = (LPDROPFILES)data.data();
+ if (hdrop->fWide) {
+ const wchar_t* filesw = (const wchar_t *)(data.data() + hdrop->pFiles);
+ int i = 0;
+ while (filesw[i]) {
+ QString fileurl = QString::fromWCharArray(filesw + i);
+ urls += QUrl::fromLocalFile(fileurl);
+ i += fileurl.length()+1;
+ }
+ } else {
+ const char* files = (const char *)data.data() + hdrop->pFiles;
+ int i=0;
+ while (files[i]) {
+ urls += QUrl::fromLocalFile(QString::fromLocal8Bit(files+i));
+ i += int(strlen(files+i))+1;
+ }
+ }
+
+ if (preferredType == QVariant::Url && urls.size() == 1)
+ return urls.at(0);
+ else if (!urls.isEmpty())
+ return urls;
+ } else if (canGetData(CF_INETURL_W, pDataObj)) {
+ QByteArray data = getData(CF_INETURL_W, pDataObj);
+ if (data.isEmpty())
+ return QVariant();
+ return QUrl(QString::fromWCharArray((const wchar_t *)data.constData()));
+ } else if (canGetData(CF_INETURL, pDataObj)) {
+ QByteArray data = getData(CF_INETURL, pDataObj);
+ if (data.isEmpty())
+ return QVariant();
+ return QUrl(QString::fromLocal8Bit(data.constData()));
+ }
+ }
+ return QVariant();
+}
+
+class QWindowsMimeHtml : public QWindowsMime
+{
+public:
+ QWindowsMimeHtml();
+
+ // for converting from Qt
+ bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const;
+ bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const;
+ QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const;
+
+ // for converting to Qt
+ bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const;
+ QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const;
+ QString mimeForFormat(const FORMATETC &formatetc) const;
+
+private:
+ int CF_HTML;
+};
+
+QWindowsMimeHtml::QWindowsMimeHtml()
+{
+ CF_HTML = QWindowsMime::registerMimeType(QLatin1String("HTML Format"));
+}
+
+QVector<FORMATETC> QWindowsMimeHtml::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const
+{
+ QVector<FORMATETC> formatetcs;
+ if (mimeType == QLatin1String("text/html") && (!mimeData->html().isEmpty()))
+ formatetcs += setCf(CF_HTML);
+ return formatetcs;
+}
+
+QString QWindowsMimeHtml::mimeForFormat(const FORMATETC &formatetc) const
+{
+ if (getCf(formatetc) == CF_HTML)
+ return QLatin1String("text/html");
+ return QString();
+}
+
+bool QWindowsMimeHtml::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const
+{
+ return mimeType == QLatin1String("text/html") && canGetData(CF_HTML, pDataObj);
+}
+
+
+bool QWindowsMimeHtml::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
+{
+ return getCf(formatetc) == CF_HTML && (!mimeData->html().isEmpty());
+}
+
+/*
+The windows HTML clipboard format is as follows (xxxxxxxxxx is a 10 integer number giving the positions
+in bytes). Charset used is mostly utf8, but can be different, ie. we have to look for the <meta> charset tag
+
+ Version: 1.0
+ StartHTML:xxxxxxxxxx
+ EndHTML:xxxxxxxxxx
+ StartFragment:xxxxxxxxxx
+ EndFragment:xxxxxxxxxx
+ ...html...
+
+*/
+QVariant QWindowsMimeHtml::convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const
+{
+ Q_UNUSED(preferredType);
+ QVariant result;
+ if (canConvertToMime(mime, pDataObj)) {
+ QByteArray html = getData(CF_HTML, pDataObj);
+#ifdef QMIME_DEBUG
+ qDebug("QWindowsMimeHtml::convertToMime");
+ qDebug("raw :");
+ qDebug(html);
+#endif
+ int start = html.indexOf("StartFragment:");
+ int end = html.indexOf("EndFragment:");
+
+ if (start != -1) {
+ int startOffset = start + 14;
+ int i = startOffset;
+ while (html.at(i) != '\r' && html.at(i) != '\n')
+ ++i;
+ QByteArray bytecount = html.mid(startOffset, i - startOffset);
+ start = bytecount.toInt();
+ }
+
+ if (end != -1) {
+ int endOffset = end + 12;
+ int i = endOffset ;
+ while (html.at(i) != '\r' && html.at(i) != '\n')
+ ++i;
+ QByteArray bytecount = html.mid(endOffset , i - endOffset);
+ end = bytecount.toInt();
+ }
+
+ if (end > start && start > 0) {
+ html = "<!--StartFragment-->" + html.mid(start, end - start);
+ html += "<!--EndFragment-->";
+ html.replace('\r', "");
+ result = QString::fromUtf8(html);
+ }
+ }
+ return result;
+}
+
+bool QWindowsMimeHtml::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const
+{
+ if (canConvertFromMime(formatetc, mimeData)) {
+ QByteArray data = mimeData->html().toUtf8();
+ QByteArray result =
+ "Version:1.0\r\n" // 0-12
+ "StartHTML:0000000105\r\n" // 13-35
+ "EndHTML:0000000000\r\n" // 36-55
+ "StartFragment:0000000000\r\n" // 58-86
+ "EndFragment:0000000000\r\n\r\n"; // 87-105
+
+ if (data.indexOf("<!--StartFragment-->") == -1)
+ result += "<!--StartFragment-->";
+ result += data;
+ if (data.indexOf("<!--EndFragment-->") == -1)
+ result += "<!--EndFragment-->";
+
+ // set the correct number for EndHTML
+ QByteArray pos = QString::number(result.size()).toLatin1();
+ memcpy((char *)(result.data() + 53 - pos.length()), pos.constData(), pos.length());
+
+ // set correct numbers for StartFragment and EndFragment
+ pos = QString::number(result.indexOf("<!--StartFragment-->") + 20).toLatin1();
+ memcpy((char *)(result.data() + 79 - pos.length()), pos.constData(), pos.length());
+ pos = QString::number(result.indexOf("<!--EndFragment-->")).toLatin1();
+ memcpy((char *)(result.data() + 103 - pos.length()), pos.constData(), pos.length());
+
+ return setData(result, pmedium);
+ }
+ return false;
+}
+
+
+#ifndef QT_NO_IMAGEFORMAT_BMP
+class QWindowsMimeImage : public QWindowsMime
+{
+public:
+ QWindowsMimeImage();
+ // for converting from Qt
+ bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const;
+ bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const;
+ QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const;
+
+ // for converting to Qt
+ bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const;
+ QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const;
+ QString mimeForFormat(const FORMATETC &formatetc) const;
+private:
+ bool hasOriginalDIBV5(IDataObject *pDataObj) const;
+ UINT CF_PNG;
+};
+
+QWindowsMimeImage::QWindowsMimeImage()
+{
+ CF_PNG = RegisterClipboardFormat(L"PNG");
+}
+
+QVector<FORMATETC> QWindowsMimeImage::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const
+{
+ QVector<FORMATETC> formatetcs;
+ if (mimeData->hasImage() && mimeType == QLatin1String("application/x-qt-image")) {
+ //add DIBV5 if image has alpha channel
+ QImage image = qvariant_cast<QImage>(mimeData->imageData());
+ if (!image.isNull() && image.hasAlphaChannel())
+ formatetcs += setCf(CF_DIBV5);
+ formatetcs += setCf(CF_DIB);
+ }
+ return formatetcs;
+}
+
+QString QWindowsMimeImage::mimeForFormat(const FORMATETC &formatetc) const
+{
+ int cf = getCf(formatetc);
+ if (cf == CF_DIB || cf == CF_DIBV5 || cf == int(CF_PNG))
+ return QLatin1String("application/x-qt-image");
+ return QString();
+}
+
+bool QWindowsMimeImage::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const
+{
+ if ((mimeType == QLatin1String("application/x-qt-image")) &&
+ (canGetData(CF_DIB, pDataObj) || canGetData(CF_PNG, pDataObj)))
+ return true;
+ return false;
+}
+
+bool QWindowsMimeImage::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
+{
+ int cf = getCf(formatetc);
+ if (mimeData->hasImage()) {
+ if (cf == CF_DIB)
+ return true;
+ else if (cf == CF_DIBV5) {
+ //support DIBV5 conversion only if the image has alpha channel
+ QImage image = qvariant_cast<QImage>(mimeData->imageData());
+ if (!image.isNull() && image.hasAlphaChannel())
+ return true;
+ }
+ }
+ return false;
+}
+
+bool QWindowsMimeImage::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const
+{
+ int cf = getCf(formatetc);
+ if ((cf == CF_DIB || cf == CF_DIBV5) && mimeData->hasImage()) {
+ QImage img = qvariant_cast<QImage>(mimeData->imageData());
+ if (img.isNull())
+ return false;
+ QByteArray ba;
+ QDataStream s(&ba, QIODevice::WriteOnly);
+ s.setByteOrder(QDataStream::LittleEndian);// Intel byte order ####
+ if (cf == CF_DIB) {
+ if (img.format() > QImage::Format_ARGB32)
+ img = img.convertToFormat(QImage::Format_RGB32);
+ if (qt_write_dib(s, img))
+ return setData(ba, pmedium);
+ } else {
+ if (qt_write_dibv5(s, img))
+ return setData(ba, pmedium);
+ }
+ }
+ return false;
+}
+
+bool QWindowsMimeImage::hasOriginalDIBV5(IDataObject *pDataObj) const
+{
+ bool isSynthesized = true;
+ IEnumFORMATETC *pEnum =NULL;
+ HRESULT res = pDataObj->EnumFormatEtc(1, &pEnum);
+ if (res == S_OK && pEnum) {
+ FORMATETC fc;
+ while ((res = pEnum->Next(1, &fc, 0)) == S_OK) {
+ if (fc.ptd)
+ CoTaskMemFree(fc.ptd);
+ if (fc.cfFormat == CF_DIB)
+ break;
+ else if (fc.cfFormat == CF_DIBV5) {
+ isSynthesized = false;
+ break;
+ }
+ }
+ pEnum->Release();
+ }
+ return !isSynthesized;
+}
+
+QVariant QWindowsMimeImage::convertToMime(const QString &mimeType, IDataObject *pDataObj, QVariant::Type preferredType) const
+{
+ Q_UNUSED(preferredType);
+ QVariant result;
+ if (mimeType != QLatin1String("application/x-qt-image"))
+ return result;
+ //Try to convert from a format which has more data
+ //DIBV5, use only if its is not synthesized
+ if (canGetData(CF_DIBV5, pDataObj) && hasOriginalDIBV5(pDataObj)) {
+ QImage img;
+ QByteArray data = getData(CF_DIBV5, pDataObj);
+ QDataStream s(&data, QIODevice::ReadOnly);
+ s.setByteOrder(QDataStream::LittleEndian);
+ if (qt_read_dibv5(s, img)) { // #### supports only 32bit DIBV5
+ return img;
+ }
+ }
+ //PNG, MS Office place this (undocumented)
+ if (canGetData(CF_PNG, pDataObj)) {
+ QImage img;
+ QByteArray data = getData(CF_PNG, pDataObj);
+ if (img.loadFromData(data, "PNG")) {
+ return img;
+ }
+ }
+ //Fallback to DIB
+ if (canGetData(CF_DIB, pDataObj)) {
+ QImage img;
+ QByteArray data = getData(CF_DIB, pDataObj);
+ QDataStream s(&data, QIODevice::ReadOnly);
+ s.setByteOrder(QDataStream::LittleEndian);// Intel byte order ####
+ if (qt_read_dib(s, img)) { // ##### encaps "-14"
+ return img;
+ }
+ }
+ // Failed
+ return result;
+}
+#endif
+
+class QBuiltInMimes : public QWindowsMime
+{
+public:
+ QBuiltInMimes();
+
+ // for converting from Qt
+ bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const;
+ bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const;
+ QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const;
+
+ // for converting to Qt
+ bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const;
+ QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const;
+ QString mimeForFormat(const FORMATETC &formatetc) const;
+
+private:
+ QMap<int, QString> outFormats;
+ QMap<int, QString> inFormats;
+};
+
+QBuiltInMimes::QBuiltInMimes()
+: QWindowsMime()
+{
+ outFormats.insert(QWindowsMime::registerMimeType(QLatin1String("application/x-color")), QLatin1String("application/x-color"));
+ inFormats.insert(QWindowsMime::registerMimeType(QLatin1String("application/x-color")), QLatin1String("application/x-color"));
+}
+
+bool QBuiltInMimes::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
+{
+ // really check
+ return formatetc.tymed & TYMED_HGLOBAL
+ && outFormats.contains(formatetc.cfFormat)
+ && mimeData->formats().contains(outFormats.value(formatetc.cfFormat));
+}
+
+bool QBuiltInMimes::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const
+{
+ if (canConvertFromMime(formatetc, mimeData)) {
+ QByteArray data;
+ if (outFormats.value(getCf(formatetc)) == QLatin1String("text/html")) {
+ // text/html is in wide chars on windows (compatible with mozillia)
+ QString html = mimeData->html();
+ // same code as in the text converter up above
+ const QChar *u = html.unicode();
+ QString res;
+ const int s = html.length();
+ int maxsize = s + s/40 + 3;
+ res.resize(maxsize);
+ int ri = 0;
+ bool cr = false;
+ for (int i=0; i < s; ++i) {
+ if (*u == QLatin1Char('\r'))
+ cr = true;
+ else {
+ if (*u == QLatin1Char('\n') && !cr)
+ res[ri++] = QLatin1Char('\r');
+ cr = false;
+ }
+ res[ri++] = *u;
+ if (ri+3 >= maxsize) {
+ maxsize += maxsize/4;
+ res.resize(maxsize);
+ }
+ ++u;
+ }
+ res.truncate(ri);
+ const int byteLength = res.length() * sizeof(ushort);
+ QByteArray r(byteLength + 2, '\0');
+ memcpy(r.data(), res.unicode(), byteLength);
+ r[byteLength] = 0;
+ r[byteLength+1] = 0;
+ data = r;
+ } else {
+#ifndef QT_NO_DRAGANDDROP
+ data = QInternalMimeData::renderDataHelper(outFormats.value(getCf(formatetc)), mimeData);
+#endif //QT_NO_DRAGANDDROP
+ }
+ return setData(data, pmedium);
+ }
+ return false;
+}
+
+QVector<FORMATETC> QBuiltInMimes::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const
+{
+ QVector<FORMATETC> formatetcs;
+ if (!outFormats.keys(mimeType).isEmpty() && mimeData->formats().contains(mimeType))
+ formatetcs += setCf(outFormats.key(mimeType));
+ return formatetcs;
+}
+
+bool QBuiltInMimes::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const
+{
+ return (!inFormats.keys(mimeType).isEmpty())
+ && canGetData(inFormats.key(mimeType), pDataObj);
+}
+
+QVariant QBuiltInMimes::convertToMime(const QString &mimeType, IDataObject *pDataObj, QVariant::Type preferredType) const
+{
+ QVariant val;
+ if (canConvertToMime(mimeType, pDataObj)) {
+ QByteArray data = getData(inFormats.key(mimeType), pDataObj);
+ if (!data.isEmpty()) {
+#ifdef QMIME_DEBUG
+ qDebug("QBuiltInMimes::convertToMime()");
+#endif
+ if (mimeType == QLatin1String("text/html") && preferredType == QVariant::String) {
+ // text/html is in wide chars on windows (compatible with Mozilla)
+ val = QString::fromWCharArray((const wchar_t *)data.data());
+ } else {
+ val = data; // it should be enough to return the data and let QMimeData do the rest.
+ }
+ }
+ }
+ return val;
+}
+
+QString QBuiltInMimes::mimeForFormat(const FORMATETC &formatetc) const
+{
+ return inFormats.value(getCf(formatetc));
+}
+
+
+class QLastResortMimes : public QWindowsMime
+{
+public:
+
+ QLastResortMimes();
+ // for converting from Qt
+ bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const;
+ bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const;
+ QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const;
+
+ // for converting to Qt
+ bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const;
+ QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const;
+ QString mimeForFormat(const FORMATETC &formatetc) const;
+
+private:
+ QMap<int, QString> formats;
+ static QStringList ianaTypes;
+ static QStringList excludeList;
+};
+
+QStringList QLastResortMimes::ianaTypes;
+QStringList QLastResortMimes::excludeList;
+
+QLastResortMimes::QLastResortMimes()
+{
+ //MIME Media-Types
+ if (!ianaTypes.size()) {
+ ianaTypes.append(QLatin1String("application/"));
+ ianaTypes.append(QLatin1String("audio/"));
+ ianaTypes.append(QLatin1String("example/"));
+ ianaTypes.append(QLatin1String("image/"));
+ ianaTypes.append(QLatin1String("message/"));
+ ianaTypes.append(QLatin1String("model/"));
+ ianaTypes.append(QLatin1String("multipart/"));
+ ianaTypes.append(QLatin1String("text/"));
+ ianaTypes.append(QLatin1String("video/"));
+ }
+ //Types handled by other classes
+ if (!excludeList.size()) {
+ excludeList.append(QLatin1String("HTML Format"));
+ excludeList.append(QLatin1String("UniformResourceLocator"));
+ excludeList.append(QLatin1String("text/html"));
+ excludeList.append(QLatin1String("text/plain"));
+ excludeList.append(QLatin1String("text/uri-list"));
+ excludeList.append(QLatin1String("application/x-qt-image"));
+ excludeList.append(QLatin1String("application/x-color"));
+ }
+}
+
+bool QLastResortMimes::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
+{
+ // really check
+#ifndef QT_NO_DRAGANDDROP
+ return formatetc.tymed & TYMED_HGLOBAL
+ && (formats.contains(formatetc.cfFormat)
+ && QInternalMimeData::hasFormatHelper(formats.value(formatetc.cfFormat), mimeData));
+#else
+ Q_UNUSED(mimeData);
+ Q_UNUSED(formatetc);
+ return formatetc.tymed & TYMED_HGLOBAL
+ && formats.contains(formatetc.cfFormat);
+#endif //QT_NO_DRAGANDDROP
+}
+
+bool QLastResortMimes::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const
+{
+#ifndef QT_NO_DRAGANDDROP
+ return canConvertFromMime(formatetc, mimeData)
+ && setData(QInternalMimeData::renderDataHelper(formats.value(getCf(formatetc)), mimeData), pmedium);
+#else
+ Q_UNUSED(mimeData);
+ Q_UNUSED(formatetc);
+ Q_UNUSED(pmedium);
+ return false;
+#endif //QT_NO_DRAGANDDROP
+}
+
+QVector<FORMATETC> QLastResortMimes::formatsForMime(const QString &mimeType, const QMimeData * /*mimeData*/) const
+{
+ QVector<FORMATETC> formatetcs;
+ if (!formats.keys(mimeType).isEmpty()) {
+ formatetcs += setCf(formats.key(mimeType));
+ } else if (!excludeList.contains(mimeType, Qt::CaseInsensitive)){
+ // register any other available formats
+ int cf = QWindowsMime::registerMimeType(mimeType);
+ QLastResortMimes *that = const_cast<QLastResortMimes *>(this);
+ that->formats.insert(cf, mimeType);
+ formatetcs += setCf(cf);
+ }
+ return formatetcs;
+}
+static const char x_qt_windows_mime[] = "application/x-qt-windows-mime;value=\"";
+
+static bool isCustomMimeType(const QString &mimeType)
+{
+ return mimeType.startsWith(QLatin1String(x_qt_windows_mime), Qt::CaseInsensitive);
+}
+
+static QString customMimeType(const QString &mimeType)
+{
+ int len = sizeof(x_qt_windows_mime) - 1;
+ int n = mimeType.lastIndexOf(QLatin1Char('\"'))-len;
+ return mimeType.mid(len, n);
+}
+
+bool QLastResortMimes::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const
+{
+ if (isCustomMimeType(mimeType)) {
+ QString clipFormat = customMimeType(mimeType);
+ int cf = RegisterClipboardFormat(reinterpret_cast<const wchar_t *> (clipFormat.utf16()));
+ return canGetData(cf, pDataObj);
+ } else if (formats.keys(mimeType).isEmpty()) {
+ // if it is not in there then register it an see if we can get it
+ int cf = QWindowsMime::registerMimeType(mimeType);
+ return canGetData(cf, pDataObj);
+ } else {
+ return canGetData(formats.key(mimeType), pDataObj);
+ }
+ return false;
+}
+
+QVariant QLastResortMimes::convertToMime(const QString &mimeType, IDataObject *pDataObj, QVariant::Type preferredType) const
+{
+ Q_UNUSED(preferredType);
+ QVariant val;
+ if (canConvertToMime(mimeType, pDataObj)) {
+ QByteArray data;
+ if (isCustomMimeType(mimeType)) {
+ QString clipFormat = customMimeType(mimeType);
+ int cf = RegisterClipboardFormat(reinterpret_cast<const wchar_t *> (clipFormat.utf16()));
+ data = getData(cf, pDataObj);
+ } else if (formats.keys(mimeType).isEmpty()) {
+ int cf = QWindowsMime::registerMimeType(mimeType);
+ data = getData(cf, pDataObj);
+ } else {
+ data = getData(formats.key(mimeType), pDataObj);
+ }
+ if (!data.isEmpty())
+ val = data; // it should be enough to return the data and let QMimeData do the rest.
+ }
+ return val;
+}
+
+QString QLastResortMimes::mimeForFormat(const FORMATETC &formatetc) const
+{
+ QString format = formats.value(getCf(formatetc));
+ if (!format.isEmpty())
+ return format;
+
+ wchar_t buffer[256];
+ int len = GetClipboardFormatName(getCf(formatetc), buffer, 256);
+
+ if (len) {
+ QString clipFormat = QString::fromWCharArray(buffer, len);
+#ifndef QT_NO_DRAGANDDROP
+ if (QInternalMimeData::canReadData(clipFormat))
+ format = clipFormat;
+ else if((formatetc.cfFormat >= 0xC000)){
+ //create the mime as custom. not registered.
+ if (!excludeList.contains(clipFormat, Qt::CaseInsensitive)) {
+ //check if this is a mime type
+ bool ianaType = false;
+ int sz = ianaTypes.size();
+ for (int i = 0; i < sz; i++) {
+ if (clipFormat.startsWith(ianaTypes[i], Qt::CaseInsensitive)) {
+ ianaType = true;
+ break;
+ }
+ }
+ if (!ianaType)
+ format = QLatin1String(x_qt_windows_mime) + clipFormat + QLatin1Char('\"');
+ else
+ format = clipFormat;
+ }
+ }
+#endif //QT_NO_DRAGANDDROP
+ }
+
+ return format;
+}
+
+QWindowsMimeList::QWindowsMimeList()
+ : initialized(false)
+{
+}
+
+QWindowsMimeList::~QWindowsMimeList()
+{
+ while (mimes.size())
+ delete mimes.first();
+}
+
+
+void QWindowsMimeList::init()
+{
+ if (!initialized) {
+ initialized = true;
+#ifndef QT_NO_IMAGEFORMAT_BMP
+ new QWindowsMimeImage;
+#endif
+ new QLastResortMimes;
+ new QWindowsMimeText;
+ new QWindowsMimeURI;
+
+ new QWindowsMimeHtml;
+ new QBuiltInMimes;
+ }
+}
+
+void QWindowsMimeList::addWindowsMime(QWindowsMime * mime)
+{
+ init();
+ mimes.append(mime);
+}
+
+void QWindowsMimeList::removeWindowsMime(QWindowsMime * mime)
+{
+ init();
+ mimes.removeAll(mime);
+}
+
+QList<QWindowsMime*> QWindowsMimeList::windowsMimes()
+{
+ init();
+ return mimes;
+}
+
+#ifndef QT_NO_IMAGEFORMAT_BMP
+static bool qt_write_dibv5(QDataStream &s, QImage image)
+{
+ QIODevice* d = s.device();
+ if (!d->isWritable())
+ return false;
+
+ //depth will be always 32
+ int bpl_bmp = image.width()*4;
+
+ BMP_BITMAPV5HEADER bi ={0};
+ bi.bV5Size = sizeof(BMP_BITMAPV5HEADER);
+ bi.bV5Width = image.width();
+ bi.bV5Height = image.height();
+ bi.bV5Planes = 1;
+ bi.bV5BitCount = 32;
+ bi.bV5Compression = BI_BITFIELDS;
+ bi.bV5SizeImage = bpl_bmp*image.height();
+ bi.bV5XPelsPerMeter = 0;
+ bi.bV5YPelsPerMeter = 0;
+ bi.bV5ClrUsed = 0;
+ bi.bV5ClrImportant = 0;
+ bi.bV5BlueMask = 0x000000ff;
+ bi.bV5GreenMask = 0x0000ff00;
+ bi.bV5RedMask = 0x00ff0000;
+ bi.bV5AlphaMask = 0xff000000;
+ bi.bV5CSType = BMP_LCS_sRGB; //LCS_sRGB
+ bi.bV5Intent = BMP_LCS_GM_IMAGES; //LCS_GM_IMAGES
+
+ d->write(reinterpret_cast<const char*>(&bi), bi.bV5Size);
+ if (s.status() != QDataStream::Ok)
+ return false;
+
+ DWORD colorSpace[3] = {0x00ff0000,0x0000ff00,0x000000ff};
+ d->write(reinterpret_cast<const char*>(colorSpace), sizeof(colorSpace));
+ if (s.status() != QDataStream::Ok)
+ return false;
+
+ if (image.format() != QImage::Format_ARGB32)
+ image = image.convertToFormat(QImage::Format_ARGB32);
+
+ uchar *buf = new uchar[bpl_bmp];
+ uchar *b;
+
+ memset(buf, 0, bpl_bmp);
+ for (int y=image.height()-1; y>=0; y--) {
+ // write the image bits
+ QRgb *p = (QRgb *)image.scanLine(y);
+ QRgb *end = p + image.width();
+ b = buf;
+ while (p < end) {
+ int alpha = qAlpha(*p);
+ if (alpha) {
+ *b++ = qBlue(*p);
+ *b++ = qGreen(*p);
+ *b++ = qRed(*p);
+ } else {
+ //white for fully transparent pixels.
+ *b++ = 0xff;
+ *b++ = 0xff;
+ *b++ = 0xff;
+ }
+ *b++ = alpha;
+ p++;
+ }
+ d->write((char*)buf, bpl_bmp);
+ if (s.status() != QDataStream::Ok) {
+ delete[] buf;
+ return false;
+ }
+ }
+ delete[] buf;
+ return true;
+}
+
+static int calc_shift(int mask)
+{
+ int result = 0;
+ while (!(mask & 1)) {
+ result++;
+ mask >>= 1;
+ }
+ return result;
+}
+
+//Supports only 32 bit DIBV5
+static bool qt_read_dibv5(QDataStream &s, QImage &image)
+{
+ BMP_BITMAPV5HEADER bi;
+ QIODevice* d = s.device();
+ if (d->atEnd())
+ return false;
+
+ d->read((char *)&bi, sizeof(bi)); // read BITMAPV5HEADER header
+ if (s.status() != QDataStream::Ok)
+ return false;
+
+ int nbits = bi.bV5BitCount;
+ int comp = bi.bV5Compression;
+ if (nbits != 32 || bi.bV5Planes != 1 || comp != BMP_BITFIELDS)
+ return false; //Unsupported DIBV5 format
+
+ int w = bi.bV5Width, h = bi.bV5Height;
+ int red_mask = bi.bV5RedMask;
+ int green_mask = bi.bV5GreenMask;
+ int blue_mask = bi.bV5BlueMask;
+ int alpha_mask = bi.bV5AlphaMask;
+ int red_shift = 0;
+ int green_shift = 0;
+ int blue_shift = 0;
+ int alpha_shift = 0;
+ QImage::Format format = QImage::Format_ARGB32;
+
+ if (bi.bV5Height < 0)
+ h = -h; // support images with negative height
+ if (image.size() != QSize(w, h) || image.format() != format) {
+ image = QImage(w, h, format);
+ if (image.isNull()) // could not create image
+ return false;
+ }
+ image.setDotsPerMeterX(bi.bV5XPelsPerMeter);
+ image.setDotsPerMeterY(bi.bV5YPelsPerMeter);
+ // read color table
+ DWORD colorSpace[3];
+ if (d->read((char *)colorSpace, sizeof(colorSpace)) != sizeof(colorSpace))
+ return false;
+
+ red_shift = calc_shift(red_mask);
+ green_shift = calc_shift(green_mask);
+ blue_shift = calc_shift(blue_mask);
+ if (alpha_mask) {
+ alpha_shift = calc_shift(alpha_mask);
+ }
+
+ int bpl = image.bytesPerLine();
+ uchar *data = image.bits();
+ register QRgb *p;
+ QRgb *end;
+ uchar *buf24 = new uchar[bpl];
+ int bpl24 = ((w*nbits+31)/32)*4;
+ uchar *b;
+ unsigned int c;
+
+ while (--h >= 0) {
+ p = (QRgb *)(data + h*bpl);
+ end = p + w;
+ if (d->read((char *)buf24,bpl24) != bpl24)
+ break;
+ b = buf24;
+ while (p < end) {
+ c = *b | (*(b+1))<<8 | (*(b+2))<<16 | (*(b+3))<<24;
+ *p++ = qRgba(((c & red_mask) >> red_shift) ,
+ ((c & green_mask) >> green_shift),
+ ((c & blue_mask) >> blue_shift),
+ ((c & alpha_mask) >> alpha_shift));
+ b += 4;
+ }
+ }
+ delete[] buf24;
+
+ if (bi.bV5Height < 0) {
+ // Flip the image
+ uchar *buf = new uchar[bpl];
+ h = -bi.bV5Height;
+ for (int y = 0; y < h/2; ++y) {
+ memcpy(buf, data + y*bpl, bpl);
+ memcpy(data + y*bpl, data + (h-y-1)*bpl, bpl);
+ memcpy(data + (h-y-1)*bpl, buf, bpl);
+ }
+ delete [] buf;
+ }
+
+ return true;
+}
+
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/win/qole_win.cpp b/src/widgets/platforms/win/qole_win.cpp
new file mode 100644
index 0000000000..1e1a672ac5
--- /dev/null
+++ b/src/widgets/platforms/win/qole_win.cpp
@@ -0,0 +1,255 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdnd_p.h"
+
+#if !(defined(QT_NO_DRAGANDDROP) && defined(QT_NO_CLIPBOARD))
+
+#if defined(Q_OS_WINCE)
+#include <shlobj.h>
+#include "qguifunctions_wince.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+QOleEnumFmtEtc::QOleEnumFmtEtc(const QVector<FORMATETC> &fmtetcs)
+{
+ m_isNull = false;
+ m_dwRefs = 1;
+ m_nIndex = 0;
+
+ for (int idx = 0; idx < fmtetcs.count(); ++idx) {
+ LPFORMATETC destetc = new FORMATETC();
+ if (copyFormatEtc(destetc, (LPFORMATETC)&(fmtetcs.at(idx)))) {
+ m_lpfmtetcs.append(destetc);
+ } else {
+ m_isNull = true;
+ delete destetc;
+ break;
+ }
+ }
+}
+
+QOleEnumFmtEtc::QOleEnumFmtEtc(const QVector<LPFORMATETC> &lpfmtetcs)
+{
+ m_isNull = false;
+ m_dwRefs = 1;
+ m_nIndex = 0;
+
+ for (int idx = 0; idx < lpfmtetcs.count(); ++idx) {
+ LPFORMATETC srcetc = lpfmtetcs.at(idx);
+ LPFORMATETC destetc = new FORMATETC();
+ if (copyFormatEtc(destetc, srcetc)) {
+ m_lpfmtetcs.append(destetc);
+ } else {
+ m_isNull = true;
+ delete destetc;
+ break;
+ }
+ }
+}
+
+QOleEnumFmtEtc::~QOleEnumFmtEtc()
+{
+ LPMALLOC pmalloc;
+
+#if !defined(Q_OS_WINCE)
+ if (CoGetMalloc(MEMCTX_TASK, &pmalloc) == NOERROR) {
+#else
+ if (SHGetMalloc(&pmalloc) == NOERROR) {
+#endif
+ for (int idx = 0; idx < m_lpfmtetcs.count(); ++idx) {
+ LPFORMATETC tmpetc = m_lpfmtetcs.at(idx);
+ if (tmpetc->ptd)
+ pmalloc->Free(tmpetc->ptd);
+ delete tmpetc;
+ }
+
+ pmalloc->Release();
+ }
+ m_lpfmtetcs.clear();
+}
+
+bool QOleEnumFmtEtc::isNull() const
+{
+ return m_isNull;
+}
+
+// IUnknown methods
+STDMETHODIMP
+QOleEnumFmtEtc::QueryInterface(REFIID riid, void FAR* FAR* ppvObj)
+{
+ if (riid == IID_IUnknown || riid == IID_IEnumFORMATETC) {
+ *ppvObj = this;
+ AddRef();
+ return NOERROR;
+ }
+ *ppvObj = NULL;
+ return ResultFromScode(E_NOINTERFACE);
+}
+
+STDMETHODIMP_(ULONG)
+QOleEnumFmtEtc::AddRef(void)
+{
+ return ++m_dwRefs;
+}
+
+STDMETHODIMP_(ULONG)
+QOleEnumFmtEtc::Release(void)
+{
+ if (--m_dwRefs == 0) {
+ delete this;
+ return 0;
+ }
+ return m_dwRefs;
+}
+
+// IEnumFORMATETC methods
+STDMETHODIMP
+QOleEnumFmtEtc::Next(ULONG celt, LPFORMATETC rgelt, ULONG FAR* pceltFetched)
+{
+ ULONG i=0;
+ ULONG nOffset;
+
+ if (rgelt == NULL)
+ return ResultFromScode(E_INVALIDARG);
+
+ while (i < celt) {
+ nOffset = m_nIndex + i;
+
+ if (nOffset < ULONG(m_lpfmtetcs.count())) {
+ copyFormatEtc((LPFORMATETC)&(rgelt[i]), m_lpfmtetcs.at(nOffset));
+ i++;
+ } else {
+ break;
+ }
+ }
+
+ m_nIndex += (WORD)i;
+
+ if (pceltFetched != NULL)
+ *pceltFetched = i;
+
+ if (i != celt)
+ return ResultFromScode(S_FALSE);
+
+ return NOERROR;
+}
+
+STDMETHODIMP
+QOleEnumFmtEtc::Skip(ULONG celt)
+{
+ ULONG i=0;
+ ULONG nOffset;
+
+ while (i < celt) {
+ nOffset = m_nIndex + i;
+
+ if (nOffset < ULONG(m_lpfmtetcs.count())) {
+ i++;
+ } else {
+ break;
+ }
+ }
+
+ m_nIndex += (WORD)i;
+
+ if (i != celt)
+ return ResultFromScode(S_FALSE);
+
+ return NOERROR;
+}
+
+STDMETHODIMP
+QOleEnumFmtEtc::Reset()
+{
+ m_nIndex = 0;
+ return NOERROR;
+}
+
+STDMETHODIMP
+QOleEnumFmtEtc::Clone(LPENUMFORMATETC FAR* newEnum)
+{
+ if (newEnum == NULL)
+ return ResultFromScode(E_INVALIDARG);
+
+ QOleEnumFmtEtc *result = new QOleEnumFmtEtc(m_lpfmtetcs);
+ result->m_nIndex = m_nIndex;
+
+ if (result->isNull()) {
+ delete result;
+ return ResultFromScode(E_OUTOFMEMORY);
+ } else {
+ *newEnum = result;
+ }
+
+ return NOERROR;
+}
+
+bool QOleEnumFmtEtc::copyFormatEtc(LPFORMATETC dest, LPFORMATETC src) const
+{
+ if (dest == NULL || src == NULL)
+ return false;
+
+ *dest = *src;
+
+ if (src->ptd) {
+ LPVOID pout;
+ LPMALLOC pmalloc;
+
+#if !defined(Q_OS_WINCE)
+ if (CoGetMalloc(MEMCTX_TASK, &pmalloc) != NOERROR)
+#else
+ if (SHGetMalloc(&pmalloc) != NOERROR)
+#endif
+ return false;
+
+ pout = (LPVOID)pmalloc->Alloc(src->ptd->tdSize);
+ memcpy(dest->ptd, src->ptd, size_t(src->ptd->tdSize));
+
+ pmalloc->Release();
+ }
+
+ return true;
+}
+
+QT_END_NAMESPACE
+#endif // QT_NO_DRAGANDDROP && QT_NO_CLIPBOARD
diff --git a/src/widgets/platforms/win/qpaintdevice_win.cpp b/src/widgets/platforms/win/qpaintdevice_win.cpp
new file mode 100644
index 0000000000..a6d79d8341
--- /dev/null
+++ b/src/widgets/platforms/win/qpaintdevice_win.cpp
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpaintdevice.h"
+#include "qpainter.h"
+#include "qwidget.h"
+#include "qbitmap.h"
+#include "qapplication.h"
+#include <private/qapplication_p.h>
+#include "qt_windows.h"
+#include "qprinter.h"
+
+QT_BEGIN_NAMESPACE
+
+HDC QPaintDevice::getDC() const
+{
+ return 0;
+}
+
+void QPaintDevice::releaseDC(HDC) const
+{
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/win/qpixmap_win.cpp b/src/widgets/platforms/win/qpixmap_win.cpp
new file mode 100644
index 0000000000..c5adb48f5d
--- /dev/null
+++ b/src/widgets/platforms/win/qpixmap_win.cpp
@@ -0,0 +1,477 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpixmap.h"
+#include "qpixmap_raster_p.h"
+
+#include "qbitmap.h"
+#include "qimage.h"
+#include "qwidget.h"
+#include "qpainter.h"
+#include "qdatastream.h"
+#include "qbuffer.h"
+#include "qapplication.h"
+#include "qevent.h"
+#include "qfile.h"
+#include "qfileinfo.h"
+#include "qdatetime.h"
+#include "qpixmapcache.h"
+#include "qimagereader.h"
+#include "qimagewriter.h"
+#include "qdebug.h"
+#include "qt_windows.h"
+
+#if defined(Q_WS_WINCE)
+#include <winbase.h>
+#include "qguifunctions_wince.h"
+extern bool qt_wince_is_high_dpi();
+extern bool qt_wince_is_pocket_pc();
+#endif
+
+#ifndef CAPTUREBLT
+#define CAPTUREBLT ((DWORD)0x40000000)
+#endif
+
+QT_BEGIN_NAMESPACE
+
+QPixmap QPixmap::grabWindow(WId winId, int x, int y, int w, int h )
+{
+ RECT r;
+ GetClientRect(winId, &r);
+
+ if (w < 0) w = r.right - r.left;
+ if (h < 0) h = r.bottom - r.top;
+
+#ifdef Q_WS_WINCE_WM
+ if (qt_wince_is_pocket_pc()) {
+ QWidget *widget = QWidget::find(winId);
+ if (qobject_cast<QDesktopWidget *>(widget)) {
+ RECT rect = {0,0,0,0};
+ AdjustWindowRectEx(&rect, WS_BORDER | WS_CAPTION, FALSE, 0);
+ int magicNumber = qt_wince_is_high_dpi() ? 4 : 2;
+ y += rect.top - magicNumber;
+ }
+ }
+#endif
+
+ // Create and setup bitmap
+ HDC display_dc = GetDC(0);
+ HDC bitmap_dc = CreateCompatibleDC(display_dc);
+ HBITMAP bitmap = CreateCompatibleBitmap(display_dc, w, h);
+ HGDIOBJ null_bitmap = SelectObject(bitmap_dc, bitmap);
+
+ // copy data
+ HDC window_dc = GetDC(winId);
+ BitBlt(bitmap_dc, 0, 0, w, h, window_dc, x, y, SRCCOPY
+#ifndef Q_WS_WINCE
+ | CAPTUREBLT
+#endif
+ );
+
+ // clean up all but bitmap
+ ReleaseDC(winId, window_dc);
+ SelectObject(bitmap_dc, null_bitmap);
+ DeleteDC(bitmap_dc);
+
+ QPixmap pixmap = QPixmap::fromWinHBITMAP(bitmap);
+
+ DeleteObject(bitmap);
+ ReleaseDC(0, display_dc);
+
+ return pixmap;
+}
+
+HBITMAP QPixmap::toWinHBITMAP(HBitmapFormat format) const
+{
+ if (isNull())
+ return 0;
+
+ HBITMAP bitmap = 0;
+ if (data->classId() == QPlatformPixmap::RasterClass) {
+ QRasterPlatformPixmap* d = static_cast<QRasterPlatformPixmap*>(data.data());
+ int w = d->image.width();
+ int h = d->image.height();
+
+ HDC display_dc = GetDC(0);
+
+ // Define the header
+ BITMAPINFO bmi;
+ memset(&bmi, 0, sizeof(bmi));
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmi.bmiHeader.biWidth = w;
+ bmi.bmiHeader.biHeight = -h;
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 32;
+ bmi.bmiHeader.biCompression = BI_RGB;
+ bmi.bmiHeader.biSizeImage = w * h * 4;
+
+ // Create the pixmap
+ uchar *pixels = 0;
+ bitmap = CreateDIBSection(display_dc, &bmi, DIB_RGB_COLORS, (void **) &pixels, 0, 0);
+ ReleaseDC(0, display_dc);
+ if (!bitmap) {
+ qErrnoWarning("QPixmap::toWinHBITMAP(), failed to create dibsection");
+ return 0;
+ }
+ if (!pixels) {
+ qErrnoWarning("QPixmap::toWinHBITMAP(), did not allocate pixel data");
+ return 0;
+ }
+
+ // Copy over the data
+ QImage::Format imageFormat = QImage::Format_ARGB32;
+ if (format == NoAlpha)
+ imageFormat = QImage::Format_RGB32;
+ else if (format == PremultipliedAlpha)
+ imageFormat = QImage::Format_ARGB32_Premultiplied;
+ const QImage image = d->image.convertToFormat(imageFormat);
+ int bytes_per_line = w * 4;
+ for (int y=0; y<h; ++y)
+ memcpy(pixels + y * bytes_per_line, image.scanLine(y), bytes_per_line);
+
+ } else {
+ QPlatformPixmap *data = new QRasterPlatformPixmap(depth() == 1 ?
+ QPlatformPixmap::BitmapType : QPlatformPixmap::PixmapType);
+ data->fromImage(toImage(), Qt::AutoColor);
+ return QPixmap(data).toWinHBITMAP(format);
+ }
+ return bitmap;
+}
+
+QPixmap QPixmap::fromWinHBITMAP(HBITMAP bitmap, HBitmapFormat format)
+{
+ // Verify size
+ BITMAP bitmap_info;
+ memset(&bitmap_info, 0, sizeof(BITMAP));
+
+ int res = GetObject(bitmap, sizeof(BITMAP), &bitmap_info);
+ if (!res) {
+ qErrnoWarning("QPixmap::fromWinHBITMAP(), failed to get bitmap info");
+ return QPixmap();
+ }
+ int w = bitmap_info.bmWidth;
+ int h = bitmap_info.bmHeight;
+
+ BITMAPINFO bmi;
+ memset(&bmi, 0, sizeof(bmi));
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmi.bmiHeader.biWidth = w;
+ bmi.bmiHeader.biHeight = -h;
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 32;
+ bmi.bmiHeader.biCompression = BI_RGB;
+ bmi.bmiHeader.biSizeImage = w * h * 4;
+
+ QImage result;
+ // Get bitmap bits
+ uchar *data = (uchar *) qMalloc(bmi.bmiHeader.biSizeImage);
+
+ HDC display_dc = GetDC(0);
+ if (GetDIBits(display_dc, bitmap, 0, h, data, &bmi, DIB_RGB_COLORS)) {
+
+ QImage::Format imageFormat = QImage::Format_ARGB32_Premultiplied;
+ uint mask = 0;
+ if (format == NoAlpha) {
+ imageFormat = QImage::Format_RGB32;
+ mask = 0xff000000;
+ }
+
+ // Create image and copy data into image.
+ QImage image(w, h, imageFormat);
+ if (!image.isNull()) { // failed to alloc?
+ int bytes_per_line = w * sizeof(QRgb);
+ for (int y=0; y<h; ++y) {
+ QRgb *dest = (QRgb *) image.scanLine(y);
+ const QRgb *src = (const QRgb *) (data + y * bytes_per_line);
+ for (int x=0; x<w; ++x) {
+ const uint pixel = src[x];
+ if ((pixel & 0xff000000) == 0 && (pixel & 0x00ffffff) != 0)
+ dest[x] = pixel | 0xff000000;
+ else
+ dest[x] = pixel | mask;
+ }
+ }
+ }
+ result = image;
+ } else {
+ qWarning("QPixmap::fromWinHBITMAP(), failed to get bitmap bits");
+ }
+ ReleaseDC(0, display_dc);
+ qFree(data);
+ return fromImage(result);
+}
+
+HBITMAP qt_createIconMask(const QBitmap &bitmap)
+{
+ QImage bm = bitmap.toImage().convertToFormat(QImage::Format_Mono);
+ int w = bm.width();
+ int h = bm.height();
+ int bpl = ((w+15)/16)*2; // bpl, 16 bit alignment
+ uchar *bits = new uchar[bpl*h];
+ bm.invertPixels();
+ for (int y=0; y<h; y++)
+ memcpy(bits+y*bpl, bm.scanLine(y), bpl);
+ HBITMAP hbm = CreateBitmap(w, h, 1, 1, bits);
+ delete [] bits;
+ return hbm;
+}
+
+HICON QPixmap::toWinHICON() const
+{
+ QBitmap maskBitmap = mask();
+ if (maskBitmap.isNull()) {
+ maskBitmap= QBitmap(size());
+ maskBitmap.fill(Qt::color1);
+ }
+
+ ICONINFO ii;
+ ii.fIcon = true;
+ ii.hbmMask = qt_createIconMask(maskBitmap);
+ ii.hbmColor = toWinHBITMAP(QPixmap::Alpha);
+ ii.xHotspot = 0;
+ ii.yHotspot = 0;
+
+ HICON hIcon = CreateIconIndirect(&ii);
+
+ DeleteObject(ii.hbmColor);
+ DeleteObject(ii.hbmMask);
+
+ return hIcon;
+}
+
+#ifdef Q_WS_WIN
+#ifndef Q_WS_WINCE
+
+static QImage qt_fromWinHBITMAP(HDC hdc, HBITMAP bitmap, int w, int h)
+{
+ BITMAPINFO bmi;
+ memset(&bmi, 0, sizeof(bmi));
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmi.bmiHeader.biWidth = w;
+ bmi.bmiHeader.biHeight = -h;
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 32;
+ bmi.bmiHeader.biCompression = BI_RGB;
+ bmi.bmiHeader.biSizeImage = w * h * 4;
+
+ QImage image(w, h, QImage::Format_ARGB32_Premultiplied);
+ if (image.isNull())
+ return image;
+
+ // Get bitmap bits
+ uchar *data = (uchar *) qMalloc(bmi.bmiHeader.biSizeImage);
+
+ if (GetDIBits(hdc, bitmap, 0, h, data, &bmi, DIB_RGB_COLORS)) {
+ // Create image and copy data into image.
+ for (int y=0; y<h; ++y) {
+ void *dest = (void *) image.scanLine(y);
+ void *src = data + y * image.bytesPerLine();
+ memcpy(dest, src, image.bytesPerLine());
+ }
+ } else {
+ qWarning("qt_fromWinHBITMAP(), failed to get bitmap bits");
+ }
+ qFree(data);
+
+ return image;
+}
+
+QPixmap QPixmap::fromWinHICON(HICON icon)
+{
+ bool foundAlpha = false;
+ HDC screenDevice = GetDC(0);
+ HDC hdc = CreateCompatibleDC(screenDevice);
+ ReleaseDC(0, screenDevice);
+
+ ICONINFO iconinfo;
+ bool result = GetIconInfo(icon, &iconinfo); //x and y Hotspot describes the icon center
+ if (!result)
+ qWarning("QPixmap::fromWinHICON(), failed to GetIconInfo()");
+
+ int w = iconinfo.xHotspot * 2;
+ int h = iconinfo.yHotspot * 2;
+
+ BITMAPINFOHEADER bitmapInfo;
+ bitmapInfo.biSize = sizeof(BITMAPINFOHEADER);
+ bitmapInfo.biWidth = w;
+ bitmapInfo.biHeight = h;
+ bitmapInfo.biPlanes = 1;
+ bitmapInfo.biBitCount = 32;
+ bitmapInfo.biCompression = BI_RGB;
+ bitmapInfo.biSizeImage = 0;
+ bitmapInfo.biXPelsPerMeter = 0;
+ bitmapInfo.biYPelsPerMeter = 0;
+ bitmapInfo.biClrUsed = 0;
+ bitmapInfo.biClrImportant = 0;
+ DWORD* bits;
+
+ HBITMAP winBitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bitmapInfo, DIB_RGB_COLORS, (VOID**)&bits, NULL, 0);
+ HGDIOBJ oldhdc = (HBITMAP)SelectObject(hdc, winBitmap);
+ DrawIconEx( hdc, 0, 0, icon, iconinfo.xHotspot * 2, iconinfo.yHotspot * 2, 0, 0, DI_NORMAL);
+ QImage image = qt_fromWinHBITMAP(hdc, winBitmap, w, h);
+
+ for (int y = 0 ; y < h && !foundAlpha ; y++) {
+ QRgb *scanLine= reinterpret_cast<QRgb *>(image.scanLine(y));
+ for (int x = 0; x < w ; x++) {
+ if (qAlpha(scanLine[x]) != 0) {
+ foundAlpha = true;
+ break;
+ }
+ }
+ }
+ if (!foundAlpha) {
+ //If no alpha was found, we use the mask to set alpha values
+ DrawIconEx( hdc, 0, 0, icon, w, h, 0, 0, DI_MASK);
+ QImage mask = qt_fromWinHBITMAP(hdc, winBitmap, w, h);
+
+ for (int y = 0 ; y < h ; y++){
+ QRgb *scanlineImage = reinterpret_cast<QRgb *>(image.scanLine(y));
+ QRgb *scanlineMask = mask.isNull() ? 0 : reinterpret_cast<QRgb *>(mask.scanLine(y));
+ for (int x = 0; x < w ; x++){
+ if (scanlineMask && qRed(scanlineMask[x]) != 0)
+ scanlineImage[x] = 0; //mask out this pixel
+ else
+ scanlineImage[x] |= 0xff000000; // set the alpha channel to 255
+ }
+ }
+ }
+ //dispose resources created by iconinfo call
+ DeleteObject(iconinfo.hbmMask);
+ DeleteObject(iconinfo.hbmColor);
+
+ SelectObject(hdc, oldhdc); //restore state
+ DeleteObject(winBitmap);
+ DeleteDC(hdc);
+ return QPixmap::fromImage(image);
+}
+#else //ifndef Q_WS_WINCE
+QPixmap QPixmap::fromWinHICON(HICON icon)
+{
+ HDC screenDevice = GetDC(0);
+ HDC hdc = CreateCompatibleDC(screenDevice);
+ ReleaseDC(0, screenDevice);
+
+ ICONINFO iconinfo;
+ bool result = GetIconInfo(icon, &iconinfo);
+ if (!result)
+ qWarning("QPixmap::fromWinHICON(), failed to GetIconInfo()");
+
+ int w = 0;
+ int h = 0;
+ if (!iconinfo.xHotspot || !iconinfo.yHotspot) {
+ // We could not retrieve the icon size via GetIconInfo,
+ // so we try again using the icon bitmap.
+ BITMAP bm;
+ int result = GetObject(iconinfo.hbmColor, sizeof(BITMAP), &bm);
+ if (!result) result = GetObject(iconinfo.hbmMask, sizeof(BITMAP), &bm);
+ if (!result) {
+ qWarning("QPixmap::fromWinHICON(), failed to retrieve icon size");
+ return QPixmap();
+ }
+ w = bm.bmWidth;
+ h = bm.bmHeight;
+ } else {
+ // x and y Hotspot describes the icon center
+ w = iconinfo.xHotspot * 2;
+ h = iconinfo.yHotspot * 2;
+ }
+ const DWORD dwImageSize = w * h * 4;
+
+ BITMAPINFO bmi;
+ memset(&bmi, 0, sizeof(bmi));
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFO);
+ bmi.bmiHeader.biWidth = w;
+ bmi.bmiHeader.biHeight = -h;
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 32;
+ bmi.bmiHeader.biCompression = BI_RGB;
+ bmi.bmiHeader.biSizeImage = dwImageSize;
+
+ uchar* bits;
+
+ HBITMAP winBitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, (void**) &bits, 0, 0);
+ if (winBitmap )
+ memset(bits, 0xff, dwImageSize);
+ if (!winBitmap) {
+ qWarning("QPixmap::fromWinHICON(), failed to CreateDIBSection()");
+ return QPixmap();
+ }
+
+ HGDIOBJ oldhdc = (HBITMAP)SelectObject(hdc, winBitmap);
+ if (!DrawIconEx( hdc, 0, 0, icon, w, h, 0, 0, DI_NORMAL))
+ qWarning("QPixmap::fromWinHICON(), failed to DrawIcon()");
+
+ uint mask = 0xff000000;
+ // Create image and copy data into image.
+ QImage image(w, h, QImage::Format_ARGB32);
+
+ if (!image.isNull()) { // failed to alloc?
+ int bytes_per_line = w * sizeof(QRgb);
+ for (int y=0; y < h; ++y) {
+ QRgb *dest = (QRgb *) image.scanLine(y);
+ const QRgb *src = (const QRgb *) (bits + y * bytes_per_line);
+ for (int x=0; x < w; ++x) {
+ dest[x] = src[x];
+ }
+ }
+ }
+ if (!DrawIconEx( hdc, 0, 0, icon, w, h, 0, 0, DI_MASK))
+ qWarning("QPixmap::fromWinHICON(), failed to DrawIcon()");
+ if (!image.isNull()) { // failed to alloc?
+ int bytes_per_line = w * sizeof(QRgb);
+ for (int y=0; y < h; ++y) {
+ QRgb *dest = (QRgb *) image.scanLine(y);
+ const QRgb *src = (const QRgb *) (bits + y * bytes_per_line);
+ for (int x=0; x < w; ++x) {
+ if (!src[x])
+ dest[x] = dest[x] | mask;
+ }
+ }
+ }
+ SelectObject(hdc, oldhdc); //restore state
+ DeleteObject(winBitmap);
+ DeleteDC(hdc);
+ return QPixmap::fromImage(image);
+}
+#endif //ifndef Q_WS_WINCE
+#endif //ifdef Q_WS_WIN
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/win/qprintengine_win.cpp b/src/widgets/platforms/win/qprintengine_win.cpp
new file mode 100644
index 0000000000..5ba33c043c
--- /dev/null
+++ b/src/widgets/platforms/win/qprintengine_win.cpp
@@ -0,0 +1,1775 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT_NO_PRINTER
+
+#include "qprinter_p.h"
+#include "qprintengine_win_p.h"
+
+#include <limits.h>
+
+#include <private/qfont_p.h>
+#include <private/qfontengine_p.h>
+#include <private/qpainter_p.h>
+
+#include <qbitmap.h>
+#include <qdebug.h>
+#include <qvector.h>
+#include <qpicture.h>
+#include <private/qpicture_p.h>
+
+QT_BEGIN_NAMESPACE
+
+extern QPainterPath qt_regionToPath(const QRegion &region);
+
+// #define QT_DEBUG_DRAW
+
+static void draw_text_item_win(const QPointF &_pos, const QTextItemInt &ti, HDC hdc,
+ bool convertToText, const QTransform &xform, const QPointF &topLeft);
+
+static const struct {
+ int winSizeName;
+ QPrinter::PaperSize qtSizeName;
+} dmMapping[] = {
+ { DMPAPER_LETTER, QPrinter::Letter },
+ { DMPAPER_LETTERSMALL, QPrinter::Letter },
+ { DMPAPER_TABLOID, QPrinter::Tabloid },
+ { DMPAPER_LEDGER, QPrinter::Ledger },
+ { DMPAPER_LEGAL, QPrinter::Legal },
+ { DMPAPER_EXECUTIVE, QPrinter::Executive },
+ { DMPAPER_A3, QPrinter::A3 },
+ { DMPAPER_A4, QPrinter::A4 },
+ { DMPAPER_A4SMALL, QPrinter::A4 },
+ { DMPAPER_A5, QPrinter::A5 },
+ { DMPAPER_B4, QPrinter::B4 },
+ { DMPAPER_B5, QPrinter::B5 },
+ { DMPAPER_FOLIO, QPrinter::Folio },
+ { DMPAPER_ENV_10, QPrinter::Comm10E },
+ { DMPAPER_ENV_DL, QPrinter::DLE },
+ { DMPAPER_ENV_C3, QPrinter::C5E },
+ { DMPAPER_LETTER_EXTRA, QPrinter::Letter },
+ { DMPAPER_LEGAL_EXTRA, QPrinter::Legal },
+ { DMPAPER_TABLOID_EXTRA, QPrinter::Tabloid },
+ { DMPAPER_A4_EXTRA, QPrinter::A4},
+ { DMPAPER_LETTER_TRANSVERSE, QPrinter::Letter},
+ { DMPAPER_A4_TRANSVERSE, QPrinter::A4},
+ { DMPAPER_LETTER_EXTRA_TRANSVERSE, QPrinter::Letter },
+ { DMPAPER_A_PLUS, QPrinter::A4 },
+ { DMPAPER_B_PLUS, QPrinter::A3 },
+ { DMPAPER_LETTER_PLUS, QPrinter::Letter },
+ { DMPAPER_A4_PLUS, QPrinter::A4 },
+ { DMPAPER_A5_TRANSVERSE, QPrinter::A5 },
+ { DMPAPER_B5_TRANSVERSE, QPrinter::B5 },
+ { DMPAPER_A3_EXTRA, QPrinter::A3 },
+ { DMPAPER_A5_EXTRA, QPrinter::A5 },
+ { DMPAPER_B5_EXTRA, QPrinter::B5 },
+ { DMPAPER_A2, QPrinter::A2 },
+ { DMPAPER_A3_TRANSVERSE, QPrinter::A3 },
+ { DMPAPER_A3_EXTRA_TRANSVERSE,QPrinter::A3 },
+ { 0, QPrinter::Custom }
+};
+
+QPrinter::PaperSize mapDevmodePaperSize(int s)
+{
+ int i = 0;
+ while ((dmMapping[i].winSizeName > 0) && (dmMapping[i].winSizeName != s))
+ i++;
+ return dmMapping[i].qtSizeName;
+}
+
+static int mapPaperSizeDevmode(QPrinter::PaperSize s)
+{
+ int i = 0;
+ while ((dmMapping[i].winSizeName > 0) && (dmMapping[i].qtSizeName != s))
+ i++;
+ return dmMapping[i].winSizeName;
+}
+
+static const struct {
+ int winSourceName;
+ QPrinter::PaperSource qtSourceName;
+} sources[] = {
+ { DMBIN_ONLYONE, QPrinter::OnlyOne },
+ { DMBIN_LOWER, QPrinter::Lower },
+ { DMBIN_MIDDLE, QPrinter::Middle },
+ { DMBIN_MANUAL, QPrinter::Manual },
+ { DMBIN_ENVELOPE, QPrinter::Envelope },
+ { DMBIN_ENVMANUAL, QPrinter::EnvelopeManual },
+ { DMBIN_AUTO, QPrinter::Auto },
+ { DMBIN_TRACTOR, QPrinter::Tractor },
+ { DMBIN_SMALLFMT, QPrinter::SmallFormat },
+ { DMBIN_LARGEFMT, QPrinter::LargeFormat },
+ { DMBIN_LARGECAPACITY, QPrinter::LargeCapacity },
+ { DMBIN_CASSETTE, QPrinter::Cassette },
+ { DMBIN_FORMSOURCE, QPrinter::FormSource },
+ { 0, (QPrinter::PaperSource) -1 }
+};
+
+static QPrinter::PaperSource mapDevmodePaperSource(int s)
+{
+ int i = 0;
+ while ((sources[i].winSourceName > 0) && (sources[i].winSourceName != s))
+ i++;
+ return sources[i].winSourceName ? sources[i].qtSourceName : (QPrinter::PaperSource) s;
+}
+
+static int mapPaperSourceDevmode(QPrinter::PaperSource s)
+{
+ int i = 0;
+ while ((sources[i].qtSourceName >= 0) && (sources[i].qtSourceName != s))
+ i++;
+ return sources[i].winSourceName ? sources[i].winSourceName : s;
+}
+
+QWin32PrintEngine::QWin32PrintEngine(QPrinter::PrinterMode mode)
+ : QAlphaPaintEngine(*(new QWin32PrintEnginePrivate),
+ PaintEngineFeatures(PrimitiveTransform
+ | PixmapTransform
+ | PerspectiveTransform
+ | PainterPaths
+ | Antialiasing
+ | PaintOutsidePaintEvent))
+{
+ Q_D(QWin32PrintEngine);
+ d->docName = QLatin1String("document1");
+ d->mode = mode;
+ d->queryDefault();
+ d->initialize();
+}
+
+bool QWin32PrintEngine::begin(QPaintDevice *pdev)
+{
+ Q_D(QWin32PrintEngine);
+
+ QAlphaPaintEngine::begin(pdev);
+ if (!continueCall())
+ return true;
+
+ if (d->reinit) {
+ d->resetDC();
+ d->reinit = false;
+ }
+
+ // ### set default colors and stuff...
+
+ bool ok = d->state == QPrinter::Idle;
+
+ if (!d->hdc)
+ return false;
+
+ // Assign the FILE: to get the query...
+ if (d->printToFile && d->fileName.isEmpty())
+ d->fileName = d->port;
+
+ d->devMode->dmCopies = d->num_copies;
+
+ DOCINFO di;
+ memset(&di, 0, sizeof(DOCINFO));
+ di.cbSize = sizeof(DOCINFO);
+ di.lpszDocName = reinterpret_cast<const wchar_t *>(d->docName.utf16());
+ if (d->printToFile && !d->fileName.isEmpty())
+ di.lpszOutput = reinterpret_cast<const wchar_t *>(d->fileName.utf16());
+ if (ok && StartDoc(d->hdc, &di) == SP_ERROR) {
+ qErrnoWarning("QWin32PrintEngine::begin: StartDoc failed");
+ ok = false;
+ }
+
+ if (StartPage(d->hdc) <= 0) {
+ qErrnoWarning("QWin32PrintEngine::begin: StartPage failed");
+ ok = false;
+ }
+
+ if (!ok) {
+ d->state = QPrinter::Idle;
+ } else {
+ d->state = QPrinter::Active;
+ }
+
+ d->matrix = QTransform();
+ d->has_pen = true;
+ d->pen = QColor(Qt::black);
+ d->has_brush = false;
+
+ d->complex_xform = false;
+
+ updateMatrix(d->matrix);
+
+ if (!ok)
+ cleanUp();
+
+ return ok;
+}
+
+bool QWin32PrintEngine::end()
+{
+ Q_D(QWin32PrintEngine);
+
+ if (d->hdc) {
+ if (d->state == QPrinter::Aborted) {
+ cleanUp();
+ AbortDoc(d->hdc);
+ return true;
+ }
+ }
+
+ QAlphaPaintEngine::end();
+ if (!continueCall())
+ return true;
+
+ if (d->hdc) {
+ EndPage(d->hdc); // end; printing done
+ EndDoc(d->hdc);
+ }
+
+ d->state = QPrinter::Idle;
+ d->reinit = true;
+ return true;
+}
+
+bool QWin32PrintEngine::newPage()
+{
+ Q_D(QWin32PrintEngine);
+ Q_ASSERT(isActive());
+
+ Q_ASSERT(d->hdc);
+
+ flushAndInit();
+
+ bool transparent = GetBkMode(d->hdc) == TRANSPARENT;
+
+ if (!EndPage(d->hdc)) {
+ qErrnoWarning("QWin32PrintEngine::newPage: EndPage failed");
+ return false;
+ }
+
+ if (d->reinit) {
+ if (!d->resetDC()) {
+ qErrnoWarning("QWin32PrintEngine::newPage: ResetDC failed");
+ return false;
+ }
+ d->reinit = false;
+ }
+
+ if (!StartPage(d->hdc)) {
+ qErrnoWarning("Win32PrintEngine::newPage: StartPage failed");
+ return false;
+ }
+
+ SetTextAlign(d->hdc, TA_BASELINE);
+ if (transparent)
+ SetBkMode(d->hdc, TRANSPARENT);
+
+ // ###
+ return true;
+
+ bool success = false;
+ if (d->hdc && d->state == QPrinter::Active) {
+ if (EndPage(d->hdc) != SP_ERROR) {
+ // reinitialize the DC before StartPage if needed,
+ // because resetdc is disabled between calls to the StartPage and EndPage functions
+ // (see StartPage documentation in the Platform SDK:Windows GDI)
+// state = PST_ACTIVEDOC;
+// reinit();
+// state = PST_ACTIVE;
+ // start the new page now
+ if (d->reinit) {
+ if (!d->resetDC())
+ qErrnoWarning("QWin32PrintEngine::newPage(), ResetDC failed (2)");
+ d->reinit = false;
+ }
+ success = (StartPage(d->hdc) != SP_ERROR);
+ }
+ if (!success) {
+ d->state = QPrinter::Aborted;
+ return false;
+ }
+ }
+ return true;
+}
+
+bool QWin32PrintEngine::abort()
+{
+ // do nothing loop.
+ return false;
+}
+
+void QWin32PrintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
+{
+ Q_D(const QWin32PrintEngine);
+
+ QAlphaPaintEngine::drawTextItem(p, textItem);
+ if (!continueCall())
+ return;
+
+ const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
+ QRgb brushColor = state->pen().brush().color().rgb();
+ bool fallBack = state->pen().brush().style() != Qt::SolidPattern
+ || qAlpha(brushColor) != 0xff
+ || d->txop >= QTransform::TxProject
+ || ti.fontEngine->type() != QFontEngine::Win;
+
+
+ if (!fallBack) {
+ QFontEngineWin *fe = static_cast<QFontEngineWin *>(ti.fontEngine);
+
+ // Try selecting the font to see if we get a substitution font
+ SelectObject(d->hdc, fe->hfont);
+
+ if (GetDeviceCaps(d->hdc, TECHNOLOGY) != DT_CHARSTREAM) {
+ wchar_t n[64];
+ GetTextFace(d->hdc, 64, n);
+ fallBack = QString::fromWCharArray(n)
+ != QString::fromWCharArray(fe->logfont.lfFaceName);
+ }
+ }
+
+
+ if (fallBack) {
+ QPaintEngine::drawTextItem(p, textItem);
+ return ;
+ }
+
+ // We only want to convert the glyphs to text if the entire string is compatible with ASCII
+ // and if we actually have access to the chars.
+ bool convertToText = ti.chars != 0;
+ for (int i=0; i < ti.num_chars; ++i) {
+ if (ti.chars[i].unicode() >= 0x80) {
+ convertToText = false;
+ break;
+ }
+
+ if (ti.logClusters[i] != i) {
+ convertToText = false;
+ break;
+ }
+ }
+
+ COLORREF cf = RGB(qRed(brushColor), qGreen(brushColor), qBlue(brushColor));
+ SelectObject(d->hdc, CreateSolidBrush(cf));
+ SelectObject(d->hdc, CreatePen(PS_SOLID, 1, cf));
+ SetTextColor(d->hdc, cf);
+
+ draw_text_item_win(p, ti, d->hdc, convertToText, d->matrix, d->devPaperRect.topLeft());
+ DeleteObject(SelectObject(d->hdc,GetStockObject(HOLLOW_BRUSH)));
+ DeleteObject(SelectObject(d->hdc,GetStockObject(BLACK_PEN)));
+}
+
+static inline qreal mmToInches(double mm)
+{
+ return mm*0.039370147;
+}
+
+static inline qreal inchesToMM(double in)
+{
+ return in/0.039370147;
+}
+
+int QWin32PrintEngine::metric(QPaintDevice::PaintDeviceMetric m) const
+{
+ Q_D(const QWin32PrintEngine);
+
+ if (!d->hdc)
+ return 0;
+
+ int val;
+ int res = d->resolution;
+
+ switch (m) {
+ case QPaintDevice::PdmWidth:
+ if (d->has_custom_paper_size) {
+ val = qRound(d->paper_size.width() * res / 72.0);
+ } else {
+ int logPixelsX = GetDeviceCaps(d->hdc, LOGPIXELSX);
+ if (logPixelsX == 0) {
+ qWarning("QWin32PrintEngine::metric: GetDeviceCaps() failed, "
+ "might be a driver problem");
+ logPixelsX = 600; // Reasonable default
+ }
+ val = res
+ * GetDeviceCaps(d->hdc, d->fullPage ? PHYSICALWIDTH : HORZRES)
+ / logPixelsX;
+ }
+ if (d->pageMarginsSet)
+ val -= int(mmToInches((d->previousDialogMargins.left() +
+ d->previousDialogMargins.width()) / 100.0) * res);
+ break;
+ case QPaintDevice::PdmHeight:
+ if (d->has_custom_paper_size) {
+ val = qRound(d->paper_size.height() * res / 72.0);
+ } else {
+ int logPixelsY = GetDeviceCaps(d->hdc, LOGPIXELSY);
+ if (logPixelsY == 0) {
+ qWarning("QWin32PrintEngine::metric: GetDeviceCaps() failed, "
+ "might be a driver problem");
+ logPixelsY = 600; // Reasonable default
+ }
+ val = res
+ * GetDeviceCaps(d->hdc, d->fullPage ? PHYSICALHEIGHT : VERTRES)
+ / logPixelsY;
+ }
+ if (d->pageMarginsSet)
+ val -= int(mmToInches((d->previousDialogMargins.top() +
+ d->previousDialogMargins.height()) / 100.0) * res);
+ break;
+ case QPaintDevice::PdmDpiX:
+ val = res;
+ break;
+ case QPaintDevice::PdmDpiY:
+ val = res;
+ break;
+ case QPaintDevice::PdmPhysicalDpiX:
+ val = GetDeviceCaps(d->hdc, LOGPIXELSX);
+ break;
+ case QPaintDevice::PdmPhysicalDpiY:
+ val = GetDeviceCaps(d->hdc, LOGPIXELSY);
+ break;
+ case QPaintDevice::PdmWidthMM:
+ if (d->has_custom_paper_size) {
+ val = qRound(d->paper_size.width()*25.4/72);
+ } else {
+ if (!d->fullPage) {
+ val = GetDeviceCaps(d->hdc, HORZSIZE);
+ } else {
+ float wi = 25.4 * GetDeviceCaps(d->hdc, PHYSICALWIDTH);
+ int logPixelsX = GetDeviceCaps(d->hdc, LOGPIXELSX);
+ if (logPixelsX == 0) {
+ qWarning("QWin32PrintEngine::metric: GetDeviceCaps() failed, "
+ "might be a driver problem");
+ logPixelsX = 600; // Reasonable default
+ }
+ val = qRound(wi / logPixelsX);
+ }
+ }
+ if (d->pageMarginsSet)
+ val -= (d->previousDialogMargins.left() +
+ d->previousDialogMargins.width()) / 100.0;
+ break;
+ case QPaintDevice::PdmHeightMM:
+ if (d->has_custom_paper_size) {
+ val = qRound(d->paper_size.height()*25.4/72);
+ } else {
+ if (!d->fullPage) {
+ val = GetDeviceCaps(d->hdc, VERTSIZE);
+ } else {
+ float hi = 25.4 * GetDeviceCaps(d->hdc, PHYSICALHEIGHT);
+ int logPixelsY = GetDeviceCaps(d->hdc, LOGPIXELSY);
+ if (logPixelsY == 0) {
+ qWarning("QWin32PrintEngine::metric: GetDeviceCaps() failed, "
+ "might be a driver problem");
+ logPixelsY = 600; // Reasonable default
+ }
+ val = qRound(hi / logPixelsY);
+ }
+ }
+ if (d->pageMarginsSet)
+ val -= (d->previousDialogMargins.top() +
+ d->previousDialogMargins.height()) / 100.0;
+ break;
+ case QPaintDevice::PdmNumColors:
+ {
+ int bpp = GetDeviceCaps(d->hdc, BITSPIXEL);
+ if(bpp==32)
+ val = INT_MAX;
+ else if(bpp<=8)
+ val = GetDeviceCaps(d->hdc, NUMCOLORS);
+ else
+ val = 1 << (bpp * GetDeviceCaps(d->hdc, PLANES));
+ }
+ break;
+ case QPaintDevice::PdmDepth:
+ val = GetDeviceCaps(d->hdc, PLANES);
+ break;
+ default:
+ qWarning("QPrinter::metric: Invalid metric command");
+ return 0;
+ }
+ return val;
+}
+
+void QWin32PrintEngine::updateState(const QPaintEngineState &state)
+{
+ Q_D(QWin32PrintEngine);
+
+ QAlphaPaintEngine::updateState(state);
+ if (!continueCall())
+ return;
+
+ if (state.state() & DirtyTransform) {
+ updateMatrix(state.transform());
+ }
+
+ if (state.state() & DirtyPen) {
+ d->pen = state.pen();
+ d->has_pen = d->pen.style() != Qt::NoPen && d->pen.isSolid();
+ }
+
+ if (state.state() & DirtyBrush) {
+ QBrush brush = state.brush();
+ d->has_brush = brush.style() == Qt::SolidPattern;
+ d->brush_color = brush.color();
+ }
+
+ if (state.state() & DirtyClipEnabled) {
+ if (state.isClipEnabled())
+ updateClipPath(painter()->clipPath(), Qt::ReplaceClip);
+ else
+ updateClipPath(QPainterPath(), Qt::NoClip);
+ }
+
+ if (state.state() & DirtyClipPath) {
+ updateClipPath(state.clipPath(), state.clipOperation());
+ }
+
+ if (state.state() & DirtyClipRegion) {
+ QRegion clipRegion = state.clipRegion();
+ QPainterPath clipPath = qt_regionToPath(clipRegion);
+ updateClipPath(clipPath, state.clipOperation());
+ }
+}
+
+void QWin32PrintEngine::updateClipPath(const QPainterPath &clipPath, Qt::ClipOperation op)
+{
+ Q_D(QWin32PrintEngine);
+
+ bool doclip = true;
+ if (op == Qt::NoClip) {
+ SelectClipRgn(d->hdc, 0);
+ doclip = false;
+ }
+
+ if (doclip) {
+ QPainterPath xformed = clipPath * d->matrix;
+
+ if (xformed.isEmpty()) {
+ QRegion empty(-0x1000000, -0x1000000, 1, 1);
+ SelectClipRgn(d->hdc, empty.handle());
+ } else {
+ d->composeGdiPath(xformed);
+ const int ops[] = {
+ -1, // Qt::NoClip, covered above
+ RGN_COPY, // Qt::ReplaceClip
+ RGN_AND // Qt::IntersectClip
+ };
+ Q_ASSERT(op > 0 && unsigned(op) <= sizeof(ops) / sizeof(int));
+ SelectClipPath(d->hdc, ops[op]);
+ }
+ }
+
+ QPainterPath aclip = qt_regionToPath(alphaClipping());
+ if (!aclip.isEmpty()) {
+ QTransform tx(d->stretch_x, 0, 0, d->stretch_y, d->origin_x, d->origin_y);
+ d->composeGdiPath(tx.map(aclip));
+ SelectClipPath(d->hdc, RGN_DIFF);
+ }
+}
+
+void QWin32PrintEngine::updateMatrix(const QTransform &m)
+{
+ Q_D(QWin32PrintEngine);
+
+ QTransform stretch(d->stretch_x, 0, 0, d->stretch_y, d->origin_x, d->origin_y);
+ d->painterMatrix = m;
+ d->matrix = d->painterMatrix * stretch;
+ d->txop = d->matrix.type();
+ d->complex_xform = (d->txop > QTransform::TxScale);
+}
+
+void QWin32PrintEngine::drawPixmap(const QRectF &targetRect,
+ const QPixmap &originalPixmap,
+ const QRectF &sourceRect)
+{
+ Q_D(QWin32PrintEngine);
+
+ QAlphaPaintEngine::drawPixmap(targetRect, originalPixmap, sourceRect);
+ if (!continueCall())
+ return;
+
+ const int tileSize = 2048;
+
+ QRectF r = targetRect;
+ QRectF sr = sourceRect;
+
+ QPixmap pixmap = originalPixmap;
+ if (sr.size() != pixmap.size()) {
+ pixmap = pixmap.copy(sr.toRect());
+ }
+
+ qreal scaleX = 1.0f;
+ qreal scaleY = 1.0f;
+
+ QTransform scaleMatrix = QTransform::fromScale(r.width() / pixmap.width(), r.height() / pixmap.height());
+ QTransform adapted = QPixmap::trueMatrix(d->painterMatrix * scaleMatrix,
+ pixmap.width(), pixmap.height());
+
+ qreal xform_offset_x = adapted.dx();
+ qreal xform_offset_y = adapted.dy();
+
+ if (d->complex_xform) {
+ pixmap = pixmap.transformed(adapted);
+ scaleX = d->stretch_x;
+ scaleY = d->stretch_y;
+ } else {
+ scaleX = d->stretch_x * (r.width() / pixmap.width()) * d->painterMatrix.m11();
+ scaleY = d->stretch_y * (r.height() / pixmap.height()) * d->painterMatrix.m22();
+ }
+
+ QPointF topLeft = r.topLeft() * d->painterMatrix;
+ int tx = int(topLeft.x() * d->stretch_x + d->origin_x);
+ int ty = int(topLeft.y() * d->stretch_y + d->origin_y);
+ int tw = qAbs(int(pixmap.width() * scaleX));
+ int th = qAbs(int(pixmap.height() * scaleY));
+
+ xform_offset_x *= d->stretch_x;
+ xform_offset_y *= d->stretch_y;
+
+ int dc_state = SaveDC(d->hdc);
+
+ int tilesw = pixmap.width() / tileSize;
+ int tilesh = pixmap.height() / tileSize;
+ ++tilesw;
+ ++tilesh;
+
+ int txinc = tileSize*scaleX;
+ int tyinc = tileSize*scaleY;
+
+ for (int y = 0; y < tilesh; ++y) {
+ int tposy = ty + (y * tyinc);
+ int imgh = tileSize;
+ int height = tyinc;
+ if (y == (tilesh - 1)) {
+ imgh = pixmap.height() - (y * tileSize);
+ height = (th - (y * tyinc));
+ }
+ for (int x = 0; x < tilesw; ++x) {
+ int tposx = tx + (x * txinc);
+ int imgw = tileSize;
+ int width = txinc;
+ if (x == (tilesw - 1)) {
+ imgw = pixmap.width() - (x * tileSize);
+ width = (tw - (x * txinc));
+ }
+
+ QPixmap p = pixmap.copy(tileSize * x, tileSize * y, imgw, imgh);
+ HBITMAP hbitmap = p.toWinHBITMAP(QPixmap::NoAlpha);
+ HDC display_dc = GetDC(0);
+ HDC hbitmap_hdc = CreateCompatibleDC(display_dc);
+ HGDIOBJ null_bitmap = SelectObject(hbitmap_hdc, hbitmap);
+
+ ReleaseDC(0, display_dc);
+
+ if (!StretchBlt(d->hdc, qRound(tposx - xform_offset_x), qRound(tposy - xform_offset_y), width, height,
+ hbitmap_hdc, 0, 0, p.width(), p.height(), SRCCOPY))
+ qErrnoWarning("QWin32PrintEngine::drawPixmap, StretchBlt failed");
+
+ SelectObject(hbitmap_hdc, null_bitmap);
+ DeleteObject(hbitmap);
+ DeleteDC(hbitmap_hdc);
+ }
+ }
+
+ RestoreDC(d->hdc, dc_state);
+}
+
+
+void QWin32PrintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &pos)
+{
+ Q_D(QWin32PrintEngine);
+
+ QAlphaPaintEngine::drawTiledPixmap(r, pm, pos);
+ if (!continueCall())
+ return;
+
+ if (d->complex_xform || !pos.isNull()) {
+ QPaintEngine::drawTiledPixmap(r, pm, pos);
+ } else {
+ int dc_state = SaveDC(d->hdc);
+
+ HDC display_dc = GetDC(0);
+ HBITMAP hbitmap = pm.toWinHBITMAP(QPixmap::NoAlpha);
+ HDC hbitmap_hdc = CreateCompatibleDC(display_dc);
+ HGDIOBJ null_bitmap = SelectObject(hbitmap_hdc, hbitmap);
+
+ ReleaseDC(0, display_dc);
+
+ QRectF trect = d->painterMatrix.mapRect(r);
+ int tx = int(trect.left() * d->stretch_x + d->origin_x);
+ int ty = int(trect.top() * d->stretch_y + d->origin_y);
+
+ int xtiles = int(trect.width() / pm.width()) + 1;
+ int ytiles = int(trect.height() / pm.height()) + 1;
+ int xinc = int(pm.width() * d->stretch_x);
+ int yinc = int(pm.height() * d->stretch_y);
+
+ for (int y = 0; y < ytiles; ++y) {
+ int ity = ty + (yinc * y);
+ int ith = pm.height();
+ if (y == (ytiles - 1)) {
+ ith = int(trect.height() - (pm.height() * y));
+ }
+
+ for (int x = 0; x < xtiles; ++x) {
+ int itx = tx + (xinc * x);
+ int itw = pm.width();
+ if (x == (xtiles - 1)) {
+ itw = int(trect.width() - (pm.width() * x));
+ }
+
+ if (!StretchBlt(d->hdc, itx, ity, int(itw * d->stretch_x), int(ith * d->stretch_y),
+ hbitmap_hdc, 0, 0, itw, ith, SRCCOPY))
+ qErrnoWarning("QWin32PrintEngine::drawPixmap, StretchBlt failed");
+
+ }
+ }
+
+ SelectObject(hbitmap_hdc, null_bitmap);
+ DeleteObject(hbitmap);
+ DeleteDC(hbitmap_hdc);
+
+ RestoreDC(d->hdc, dc_state);
+ }
+}
+
+
+void QWin32PrintEnginePrivate::composeGdiPath(const QPainterPath &path)
+{
+ if (!BeginPath(hdc))
+ qErrnoWarning("QWin32PrintEnginePrivate::drawPath: BeginPath failed");
+
+ // Drawing the subpaths
+ int start = -1;
+ for (int i=0; i<path.elementCount(); ++i) {
+ const QPainterPath::Element &elm = path.elementAt(i);
+ switch (elm.type) {
+ case QPainterPath::MoveToElement:
+ if (start >= 0
+ && path.elementAt(start).x == path.elementAt(i-1).x
+ && path.elementAt(start).y == path.elementAt(i-1).y)
+ CloseFigure(hdc);
+ start = i;
+ MoveToEx(hdc, qRound(elm.x), qRound(elm.y), 0);
+ break;
+ case QPainterPath::LineToElement:
+ LineTo(hdc, qRound(elm.x), qRound(elm.y));
+ break;
+ case QPainterPath::CurveToElement: {
+ POINT pts[3] = {
+ { qRound(elm.x), qRound(elm.y) },
+ { qRound(path.elementAt(i+1).x), qRound(path.elementAt(i+1).y) },
+ { qRound(path.elementAt(i+2).x), qRound(path.elementAt(i+2).y) }
+ };
+ i+=2;
+ PolyBezierTo(hdc, pts, 3);
+ break;
+ }
+ default:
+ qFatal("QWin32PaintEngine::drawPath: Unhandled type: %d", elm.type);
+ }
+ }
+
+ if (start >= 0
+ && path.elementAt(start).x == path.elementAt(path.elementCount()-1).x
+ && path.elementAt(start).y == path.elementAt(path.elementCount()-1).y)
+ CloseFigure(hdc);
+
+ if (!EndPath(hdc))
+ qErrnoWarning("QWin32PaintEngine::drawPath: EndPath failed");
+
+ SetPolyFillMode(hdc, path.fillRule() == Qt::WindingFill ? WINDING : ALTERNATE);
+}
+
+
+void QWin32PrintEnginePrivate::fillPath_dev(const QPainterPath &path, const QColor &color)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << " --- QWin32PrintEnginePrivate::fillPath() bound:" << path.boundingRect() << color;
+#endif
+
+ composeGdiPath(path);
+
+ HBRUSH brush = CreateSolidBrush(RGB(color.red(), color.green(), color.blue()));
+ HGDIOBJ old_brush = SelectObject(hdc, brush);
+ FillPath(hdc);
+ DeleteObject(SelectObject(hdc, old_brush));
+}
+
+void QWin32PrintEnginePrivate::strokePath_dev(const QPainterPath &path, const QColor &color, qreal penWidth)
+{
+ composeGdiPath(path);
+ LOGBRUSH brush;
+ brush.lbStyle = BS_SOLID;
+ brush.lbColor = RGB(color.red(), color.green(), color.blue());
+ DWORD capStyle = PS_ENDCAP_SQUARE;
+ DWORD joinStyle = PS_JOIN_BEVEL;
+ if (pen.capStyle() == Qt::FlatCap)
+ capStyle = PS_ENDCAP_FLAT;
+ else if (pen.capStyle() == Qt::RoundCap)
+ capStyle = PS_ENDCAP_ROUND;
+
+ if (pen.joinStyle() == Qt::MiterJoin)
+ joinStyle = PS_JOIN_MITER;
+ else if (pen.joinStyle() == Qt::RoundJoin)
+ joinStyle = PS_JOIN_ROUND;
+
+ HPEN pen = ExtCreatePen(((penWidth == 0) ? PS_COSMETIC : PS_GEOMETRIC)
+ | PS_SOLID | capStyle | joinStyle,
+ (penWidth == 0) ? 1 : penWidth, &brush, 0, 0);
+
+ HGDIOBJ old_pen = SelectObject(hdc, pen);
+ StrokePath(hdc);
+ DeleteObject(SelectObject(hdc, old_pen));
+}
+
+
+void QWin32PrintEnginePrivate::fillPath(const QPainterPath &path, const QColor &color)
+{
+ fillPath_dev(path * matrix, color);
+}
+
+void QWin32PrintEnginePrivate::strokePath(const QPainterPath &path, const QColor &color)
+{
+ QPainterPathStroker stroker;
+ if (pen.style() == Qt::CustomDashLine) {
+ stroker.setDashPattern(pen.dashPattern());
+ stroker.setDashOffset(pen.dashOffset());
+ } else {
+ stroker.setDashPattern(pen.style());
+ }
+ stroker.setCapStyle(pen.capStyle());
+ stroker.setJoinStyle(pen.joinStyle());
+ stroker.setMiterLimit(pen.miterLimit());
+
+ QPainterPath stroke;
+ qreal width = pen.widthF();
+ if (pen.style() == Qt::SolidLine && (pen.isCosmetic() || matrix.type() < QTransform::TxScale)) {
+ strokePath_dev(path * matrix, color, width);
+ } else {
+ stroker.setWidth(width);
+ if (pen.isCosmetic()) {
+ stroke = stroker.createStroke(path * matrix);
+ } else {
+ stroke = stroker.createStroke(path) * painterMatrix;
+ QTransform stretch(stretch_x, 0, 0, stretch_y, origin_x, origin_y);
+ stroke = stroke * stretch;
+ }
+
+ if (stroke.isEmpty())
+ return;
+
+ fillPath_dev(stroke, color);
+ }
+}
+
+
+void QWin32PrintEngine::drawPath(const QPainterPath &path)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << " - QWin32PrintEngine::drawPath(), bounds: " << path.boundingRect();
+#endif
+
+ Q_D(QWin32PrintEngine);
+
+ QAlphaPaintEngine::drawPath(path);
+ if (!continueCall())
+ return;
+
+ if (d->has_brush)
+ d->fillPath(path, d->brush_color);
+
+ if (d->has_pen)
+ d->strokePath(path, d->pen.color());
+}
+
+
+void QWin32PrintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << " - QWin32PrintEngine::drawPolygon(), pointCount: " << pointCount;
+#endif
+
+ QAlphaPaintEngine::drawPolygon(points, pointCount, mode);
+ if (!continueCall())
+ return;
+
+ Q_ASSERT(pointCount > 1);
+
+ QPainterPath path(points[0]);
+
+ for (int i=1; i<pointCount; ++i) {
+ path.lineTo(points[i]);
+ }
+
+ Q_D(QWin32PrintEngine);
+
+ bool has_brush = d->has_brush;
+
+ if (mode == PolylineMode)
+ d->has_brush = false; // No brush for polylines
+ else
+ path.closeSubpath(); // polygons are should always be closed.
+
+ drawPath(path);
+ d->has_brush = has_brush;
+}
+
+void QWin32PrintEnginePrivate::queryDefault()
+{
+ /* Read the default printer name, driver and port with the intuitive function
+ * Strings "windows" and "device" are specified in the MSDN under EnumPrinters()
+ */
+ QString noPrinters(QLatin1String("qt_no_printers"));
+ wchar_t buffer[256];
+ GetProfileString(L"windows", L"device",
+ reinterpret_cast<const wchar_t *>(noPrinters.utf16()),
+ buffer, 256);
+ QString output = QString::fromWCharArray(buffer);
+ if (output.isEmpty() || output == noPrinters) // no printers
+ return;
+
+ QStringList info = output.split(QLatin1Char(','));
+ int infoSize = info.size();
+ if (infoSize > 0) {
+ if (name.isEmpty())
+ name = info.at(0);
+ if (program.isEmpty() && infoSize > 1)
+ program = info.at(1);
+ if (port.isEmpty() && infoSize > 2)
+ port = info.at(2);
+ }
+}
+
+QWin32PrintEnginePrivate::~QWin32PrintEnginePrivate()
+{
+ if (hdc)
+ release();
+}
+
+void QWin32PrintEnginePrivate::initialize()
+{
+ if (hdc)
+ release();
+ Q_ASSERT(!hPrinter);
+ Q_ASSERT(!hdc);
+ Q_ASSERT(!devMode);
+ Q_ASSERT(!pInfo);
+
+ if (name.isEmpty())
+ return;
+
+ txop = QTransform::TxNone;
+
+ bool ok = OpenPrinter((LPWSTR)name.utf16(), (LPHANDLE)&hPrinter, 0);
+ if (!ok) {
+ qErrnoWarning("QWin32PrintEngine::initialize: OpenPrinter failed");
+ return;
+ }
+
+ // Fetch the PRINTER_INFO_2 with DEVMODE data containing the
+ // printer settings.
+ DWORD infoSize, numBytes;
+ GetPrinter(hPrinter, 2, NULL, 0, &infoSize);
+ hMem = GlobalAlloc(GHND, infoSize);
+ pInfo = (PRINTER_INFO_2*) GlobalLock(hMem);
+ ok = GetPrinter(hPrinter, 2, (LPBYTE)pInfo, infoSize, &numBytes);
+
+ if (!ok) {
+ qErrnoWarning("QWin32PrintEngine::initialize: GetPrinter failed");
+ GlobalUnlock(pInfo);
+ GlobalFree(hMem);
+ ClosePrinter(hPrinter);
+ pInfo = 0;
+ hMem = 0;
+ hPrinter = 0;
+ return;
+ }
+
+ devMode = pInfo->pDevMode;
+ hdc = CreateDC(reinterpret_cast<const wchar_t *>(program.utf16()),
+ reinterpret_cast<const wchar_t *>(name.utf16()), 0, devMode);
+
+ Q_ASSERT(hPrinter);
+ Q_ASSERT(pInfo);
+
+ if (devMode) {
+ num_copies = devMode->dmCopies;
+ }
+
+ initHDC();
+
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QWin32PrintEngine::initialize()" << endl
+ << " - paperRect" << devPaperRect << endl
+ << " - pageRect" << devPageRect << endl
+ << " - stretch_x" << stretch_x << endl
+ << " - stretch_y" << stretch_y << endl
+ << " - origin_x" << origin_x << endl
+ << " - origin_y" << origin_y << endl;
+#endif
+}
+
+void QWin32PrintEnginePrivate::initHDC()
+{
+ Q_ASSERT(hdc);
+
+ HDC display_dc = GetDC(0);
+ dpi_x = GetDeviceCaps(hdc, LOGPIXELSX);
+ dpi_y = GetDeviceCaps(hdc, LOGPIXELSY);
+ dpi_display = GetDeviceCaps(display_dc, LOGPIXELSY);
+ ReleaseDC(0, display_dc);
+ if (dpi_display == 0) {
+ qWarning("QWin32PrintEngine::metric: GetDeviceCaps() failed, "
+ "might be a driver problem");
+ dpi_display = 96; // Reasonable default
+ }
+
+ switch(mode) {
+ case QPrinter::ScreenResolution:
+ resolution = dpi_display;
+ stretch_x = dpi_x / double(dpi_display);
+ stretch_y = dpi_y / double(dpi_display);
+ break;
+ case QPrinter::PrinterResolution:
+ case QPrinter::HighResolution:
+ resolution = dpi_y;
+ stretch_x = 1;
+ stretch_y = 1;
+ break;
+ default:
+ break;
+ }
+
+ initDevRects();
+}
+
+void QWin32PrintEnginePrivate::initDevRects()
+{
+ devPaperRect = QRect(0, 0,
+ GetDeviceCaps(hdc, PHYSICALWIDTH),
+ GetDeviceCaps(hdc, PHYSICALHEIGHT));
+ devPhysicalPageRect = QRect(GetDeviceCaps(hdc, PHYSICALOFFSETX),
+ GetDeviceCaps(hdc, PHYSICALOFFSETY),
+ GetDeviceCaps(hdc, HORZRES),
+ GetDeviceCaps(hdc, VERTRES));
+ if (!pageMarginsSet)
+ devPageRect = devPhysicalPageRect;
+ else
+ devPageRect = devPaperRect.adjusted(qRound(mmToInches(previousDialogMargins.left() / 100.0) * dpi_x),
+ qRound(mmToInches(previousDialogMargins.top() / 100.0) * dpi_y),
+ -qRound(mmToInches(previousDialogMargins.width() / 100.0) * dpi_x),
+ -qRound(mmToInches(previousDialogMargins.height() / 100.0) * dpi_y));
+ updateOrigin();
+}
+
+void QWin32PrintEnginePrivate::setPageMargins(int marginLeft, int marginTop, int marginRight, int marginBottom)
+{
+ pageMarginsSet = true;
+ previousDialogMargins = QRect(marginLeft, marginTop, marginRight, marginBottom);
+
+ devPageRect = devPaperRect.adjusted(qRound(mmToInches(marginLeft / 100.0) * dpi_x),
+ qRound(mmToInches(marginTop / 100.0) * dpi_y),
+ - qRound(mmToInches(marginRight / 100.0) * dpi_x),
+ - qRound(mmToInches(marginBottom / 100.0) * dpi_y));
+ updateOrigin();
+}
+
+QRect QWin32PrintEnginePrivate::getPageMargins() const
+{
+ if (pageMarginsSet)
+ return previousDialogMargins;
+ else
+ return QRect(qRound(inchesToMM(devPhysicalPageRect.left()) * 100.0 / dpi_x),
+ qRound(inchesToMM(devPhysicalPageRect.top()) * 100.0 / dpi_y),
+ qRound(inchesToMM(devPaperRect.right() - devPhysicalPageRect.right()) * 100.0 / dpi_x),
+ qRound(inchesToMM(devPaperRect.bottom() - devPhysicalPageRect.bottom()) * 100.0 / dpi_y));
+}
+
+void QWin32PrintEnginePrivate::release()
+{
+ if (hdc == 0)
+ return;
+
+ if (globalDevMode) { // Devmode comes from print dialog
+ GlobalUnlock(globalDevMode);
+ } else { // Devmode comes from initialize...
+ // devMode is a part of the same memory block as pInfo so one free is enough...
+ GlobalUnlock(hMem);
+ GlobalFree(hMem);
+ }
+ if (hPrinter)
+ ClosePrinter(hPrinter);
+ DeleteDC(hdc);
+
+ hdc = 0;
+ hPrinter = 0;
+ pInfo = 0;
+ hMem = 0;
+ devMode = 0;
+}
+
+QList<QVariant> QWin32PrintEnginePrivate::queryResolutions() const
+{
+ // Read the supported resolutions of the printer.
+ QList<QVariant> list;
+
+ DWORD numRes = DeviceCapabilities(reinterpret_cast<const wchar_t *>(name.utf16()),
+ reinterpret_cast<const wchar_t *>(port.utf16()),
+ DC_ENUMRESOLUTIONS, 0, 0);
+ if (numRes == (DWORD)-1)
+ return list;
+
+ LONG *enumRes = (LONG*)malloc(numRes * 2 * sizeof(LONG));
+ DWORD errRes = DeviceCapabilities(reinterpret_cast<const wchar_t *>(name.utf16()),
+ reinterpret_cast<const wchar_t *>(port.utf16()),
+ DC_ENUMRESOLUTIONS, (LPWSTR)enumRes, 0);
+
+ if (errRes == (DWORD)-1) {
+ qErrnoWarning("QWin32PrintEngine::queryResolutions: DeviceCapabilities failed");
+ return list;
+ }
+
+ for (uint i=0; i<numRes; ++i)
+ list.append(int(enumRes[i * 2]));
+
+ return list;
+}
+
+void QWin32PrintEnginePrivate::doReinit()
+{
+ if (state == QPrinter::Active) {
+ reinit = true;
+ } else {
+ resetDC();
+ initDevRects();
+ reinit = false;
+ }
+}
+
+void QWin32PrintEnginePrivate::updateOrigin()
+{
+ if (fullPage) {
+ // subtract physical margins to make (0,0) absolute top corner of paper
+ // then add user defined margins
+ origin_x = -devPhysicalPageRect.x();
+ origin_y = -devPhysicalPageRect.y();
+ if (pageMarginsSet) {
+ origin_x += devPageRect.left();
+ origin_y += devPageRect.top();
+ }
+ } else {
+ origin_x = 0;
+ origin_y = 0;
+ if (pageMarginsSet) {
+ origin_x = devPageRect.left() - devPhysicalPageRect.x();
+ origin_y = devPageRect.top() - devPhysicalPageRect.y();
+ }
+ }
+}
+
+void QWin32PrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &value)
+{
+ Q_D(QWin32PrintEngine);
+ switch (key) {
+ case PPK_CollateCopies:
+ {
+ if (!d->devMode)
+ break;
+ d->devMode->dmCollate = value.toBool() ? DMCOLLATE_TRUE : DMCOLLATE_FALSE;
+ d->doReinit();
+ }
+ break;
+
+ case PPK_ColorMode:
+ {
+ if (!d->devMode)
+ break;
+ d->devMode->dmColor = (value.toInt() == QPrinter::Color) ? DMCOLOR_COLOR : DMCOLOR_MONOCHROME;
+ d->doReinit();
+ }
+ break;
+
+ case PPK_Creator:
+
+ break;
+
+ case PPK_DocumentName:
+ if (isActive()) {
+ qWarning("QWin32PrintEngine: Cannot change document name while printing is active");
+ return;
+ }
+ d->docName = value.toString();
+ break;
+
+ case PPK_FullPage:
+ d->fullPage = value.toBool();
+ d->updateOrigin();
+ break;
+
+ case PPK_CopyCount: // fallthrough
+ case PPK_NumberOfCopies:
+ if (!d->devMode)
+ break;
+ d->num_copies = value.toInt();
+ d->devMode->dmCopies = d->num_copies;
+ d->doReinit();
+ break;
+
+ case PPK_Orientation:
+ {
+ if (!d->devMode)
+ break;
+ int orientation = value.toInt() == QPrinter::Landscape ? DMORIENT_LANDSCAPE : DMORIENT_PORTRAIT;
+ int old_orientation = d->devMode->dmOrientation;
+ d->devMode->dmOrientation = orientation;
+ if (d->has_custom_paper_size && old_orientation != orientation)
+ d->paper_size = QSizeF(d->paper_size.height(), d->paper_size.width());
+ d->doReinit();
+ }
+ break;
+
+ case PPK_OutputFileName:
+ if (isActive()) {
+ qWarning("QWin32PrintEngine: Cannot change filename while printing");
+ } else {
+ d->fileName = value.toString();
+ d->printToFile = !value.toString().isEmpty();
+ }
+ break;
+
+ case PPK_PaperSize:
+ if (!d->devMode)
+ break;
+ d->devMode->dmPaperSize = mapPaperSizeDevmode(QPrinter::PaperSize(value.toInt()));
+ d->has_custom_paper_size = (QPrinter::PaperSize(value.toInt()) == QPrinter::Custom);
+ d->doReinit();
+ break;
+
+ case PPK_PaperSource:
+ {
+ if (!d->devMode)
+ break;
+ int dmMapped = DMBIN_AUTO;
+
+ QList<QVariant> v = property(PPK_PaperSources).toList();
+ if (v.contains(value))
+ dmMapped = mapPaperSourceDevmode(QPrinter::PaperSource(value.toInt()));
+
+ d->devMode->dmDefaultSource = dmMapped;
+ d->doReinit();
+ }
+ break;
+
+ case PPK_PrinterName:
+ d->name = value.toString();
+ if(d->name.isEmpty())
+ d->queryDefault();
+ d->initialize();
+ break;
+
+ case PPK_Resolution:
+ {
+ d->resolution = value.toInt();
+
+ d->stretch_x = d->dpi_x / double(d->resolution);
+ d->stretch_y = d->dpi_y / double(d->resolution);
+ }
+ break;
+
+ case PPK_SelectionOption:
+
+ break;
+
+ case PPK_SupportedResolutions:
+
+ break;
+
+
+ case PPK_WindowsPageSize:
+ if (!d->devMode)
+ break;
+ d->has_custom_paper_size = false;
+ d->devMode->dmPaperSize = value.toInt();
+ d->doReinit();
+ break;
+
+ case PPK_CustomPaperSize:
+ {
+ d->has_custom_paper_size = true;
+ d->paper_size = value.toSizeF();
+ if (!d->devMode)
+ break;
+ int orientation = d->devMode->dmOrientation;
+ DWORD needed = 0;
+ DWORD returned = 0;
+ if (!EnumForms(d->hPrinter, 1, 0, 0, &needed, &returned)) {
+ BYTE *forms = (BYTE *) malloc(needed);
+ if (EnumForms(d->hPrinter, 1, forms, needed, &needed, &returned)) {
+ for (DWORD i=0; i< returned; ++i) {
+ FORM_INFO_1 *formArray = reinterpret_cast<FORM_INFO_1 *>(forms);
+ // the form sizes are specified in 1000th of a mm,
+ // convert the size to Points
+ QSizeF size((formArray[i].Size.cx * 72/25.4)/1000.0,
+ (formArray[i].Size.cy * 72/25.4)/1000.0);
+ if (qAbs(d->paper_size.width() - size.width()) <= 2
+ && qAbs(d->paper_size.height() - size.height()) <= 2)
+ {
+ d->devMode->dmPaperSize = i + 1;
+ break;
+ }
+ }
+ }
+ free(forms);
+ }
+ if (orientation != DMORIENT_PORTRAIT)
+ d->paper_size = QSizeF(d->paper_size.height(), d->paper_size.width());
+ break;
+ }
+
+ case PPK_PageMargins:
+ {
+ QList<QVariant> margins(value.toList());
+ Q_ASSERT(margins.size() == 4);
+ int left, top, right, bottom;
+ // specified in 1/100 mm
+ left = (margins.at(0).toReal()*25.4/72.0) * 100;
+ top = (margins.at(1).toReal()*25.4/72.0) * 100;
+ right = (margins.at(2).toReal()*25.4/72.0) * 100;
+ bottom = (margins.at(3).toReal()*25.4/72.0) * 100;
+ d->setPageMargins(left, top, right, bottom);
+ break;
+ }
+ default:
+ // Do nothing
+ break;
+ }
+}
+
+QVariant QWin32PrintEngine::property(PrintEnginePropertyKey key) const
+{
+ Q_D(const QWin32PrintEngine);
+ QVariant value;
+ switch (key) {
+
+ case PPK_CollateCopies:
+ value = false;
+ break;
+
+ case PPK_ColorMode:
+ {
+ if (!d->devMode) {
+ value = QPrinter::Color;
+ } else {
+ value = (d->devMode->dmColor == DMCOLOR_COLOR) ? QPrinter::Color : QPrinter::GrayScale;
+ }
+ }
+ break;
+
+ case PPK_DocumentName:
+ value = d->docName;
+ break;
+
+ case PPK_FullPage:
+ value = d->fullPage;
+ break;
+
+ case PPK_CopyCount:
+ value = d->num_copies;
+ break;
+
+ case PPK_SupportsMultipleCopies:
+ value = true;
+ break;
+
+ case PPK_NumberOfCopies:
+ value = 1;
+ break;
+
+ case PPK_Orientation:
+ {
+ if (!d->devMode) {
+ value = QPrinter::Portrait;
+ } else {
+ value = (d->devMode->dmOrientation == DMORIENT_LANDSCAPE) ? QPrinter::Landscape : QPrinter::Portrait;
+ }
+ }
+ break;
+
+ case PPK_OutputFileName:
+ value = d->fileName;
+ break;
+
+ case PPK_PageRect:
+ if (d->has_custom_paper_size) {
+ QRect rect(0, 0,
+ qRound(d->paper_size.width() * d->resolution / 72.0),
+ qRound(d->paper_size.height() * d->resolution / 72.0));
+ if (d->pageMarginsSet) {
+ rect = rect.adjusted(qRound(mmToInches(d->previousDialogMargins.left()/100.0) * d->resolution),
+ qRound(mmToInches(d->previousDialogMargins.top()/100.0) * d->resolution),
+ -qRound(mmToInches(d->previousDialogMargins.width()/100.0) * d->resolution),
+ -qRound(mmToInches(d->previousDialogMargins.height()/100.0) * d->resolution));
+ }
+ value = rect;
+ } else {
+ value = QTransform(1/d->stretch_x, 0, 0, 1/d->stretch_y, 0, 0)
+ .mapRect(d->fullPage ? d->devPhysicalPageRect : d->devPageRect);
+ }
+ break;
+
+ case PPK_PaperSize:
+ if (d->has_custom_paper_size) {
+ value = QPrinter::Custom;
+ } else {
+ if (!d->devMode) {
+ value = QPrinter::A4;
+ } else {
+ value = mapDevmodePaperSize(d->devMode->dmPaperSize);
+ }
+ }
+ break;
+
+ case PPK_PaperRect:
+ if (d->has_custom_paper_size) {
+ value = QRect(0, 0,
+ qRound(d->paper_size.width() * d->resolution / 72.0),
+ qRound(d->paper_size.height() * d->resolution / 72.0));
+ } else {
+ value = QTransform(1/d->stretch_x, 0, 0, 1/d->stretch_y, 0, 0).mapRect(d->devPaperRect);
+ }
+ break;
+
+ case PPK_PaperSource:
+ if (!d->devMode) {
+ value = QPrinter::Auto;
+ } else {
+ value = mapDevmodePaperSource(d->devMode->dmDefaultSource);
+ }
+ break;
+
+ case PPK_PrinterName:
+ value = d->name;
+ break;
+
+ case PPK_Resolution:
+ if (d->resolution || !d->name.isEmpty())
+ value = d->resolution;
+ break;
+
+ case PPK_SupportedResolutions:
+ value = d->queryResolutions();
+ break;
+
+ case PPK_WindowsPageSize:
+ if (!d->devMode) {
+ value = -1;
+ } else {
+ value = d->devMode->dmPaperSize;
+ }
+ break;
+
+ case PPK_PaperSources:
+ {
+ int available = DeviceCapabilities((const wchar_t *)d->name.utf16(),
+ (const wchar_t *)d->port.utf16(), DC_BINS, 0, d->devMode);
+
+ if (available <= 0)
+ break;
+
+ wchar_t *data = new wchar_t[available];
+ int count = DeviceCapabilities((const wchar_t *)d->name.utf16(),
+ (const wchar_t *)d->port.utf16(), DC_BINS, data, d->devMode);
+
+ QList<QVariant> out;
+ for (int i=0; i<count; ++i) {
+ QPrinter::PaperSource src = mapDevmodePaperSource(data[i]);
+ if (src != -1)
+ out << (int) src;
+ }
+ value = out;
+
+ delete [] data;
+ }
+ break;
+
+ case PPK_CustomPaperSize:
+ value = d->paper_size;
+ break;
+
+ case PPK_PageMargins:
+ {
+ QList<QVariant> margins;
+ QRect pageMargins(d->getPageMargins());
+
+ // specified in 1/100 mm
+ margins << (mmToInches(pageMargins.left()/100.0) * 72)
+ << (mmToInches(pageMargins.top()/100.0) * 72)
+ << (mmToInches(pageMargins.width()/100.0) * 72)
+ << (mmToInches(pageMargins.height()/100.0) * 72);
+ value = margins;
+ break;
+ }
+ default:
+ // Do nothing
+ break;
+ }
+ return value;
+}
+
+QPrinter::PrinterState QWin32PrintEngine::printerState() const
+{
+ return d_func()->state;
+}
+
+HDC QWin32PrintEngine::getDC() const
+{
+ return d_func()->hdc;
+}
+
+void QWin32PrintEngine::releaseDC(HDC) const
+{
+
+}
+
+HGLOBAL *QWin32PrintEnginePrivate::createDevNames()
+{
+ int size = sizeof(DEVNAMES)
+ + program.length() * 2 + 2
+ + name.length() * 2 + 2
+ + port.length() * 2 + 2;
+ HGLOBAL *hGlobal = (HGLOBAL *) GlobalAlloc(GMEM_MOVEABLE, size);
+ DEVNAMES *dn = (DEVNAMES*) GlobalLock(hGlobal);
+
+ dn->wDriverOffset = sizeof(DEVNAMES) / sizeof(wchar_t);
+ dn->wDeviceOffset = dn->wDriverOffset + program.length() + 1;
+ dn->wOutputOffset = dn->wDeviceOffset + name.length() + 1;
+
+ memcpy((ushort*)dn + dn->wDriverOffset, program.utf16(), program.length() * 2 + 2);
+ memcpy((ushort*)dn + dn->wDeviceOffset, name.utf16(), name.length() * 2 + 2);
+ memcpy((ushort*)dn + dn->wOutputOffset, port.utf16(), port.length() * 2 + 2);
+ dn->wDefault = 0;
+
+ GlobalUnlock(hGlobal);
+
+// printf("QPrintDialogWinPrivate::createDevNames()\n"
+// " -> wDriverOffset: %d\n"
+// " -> wDeviceOffset: %d\n"
+// " -> wOutputOffset: %d\n",
+// dn->wDriverOffset,
+// dn->wDeviceOffset,
+// dn->wOutputOffset);
+
+// printf("QPrintDialogWinPrivate::createDevNames(): %s, %s, %s\n",
+// QString::fromWCharArray((wchar_t*)(dn) + dn->wDriverOffset).latin1(),
+// QString::fromWCharArray((wchar_t*)(dn) + dn->wDeviceOffset).latin1(),
+// QString::fromWCharArray((wchar_t*)(dn) + dn->wOutputOffset).latin1());
+
+ return hGlobal;
+}
+
+void QWin32PrintEnginePrivate::readDevnames(HGLOBAL globalDevnames)
+{
+ if (globalDevnames) {
+ DEVNAMES *dn = (DEVNAMES*) GlobalLock(globalDevnames);
+ name = QString::fromWCharArray((wchar_t*)(dn) + dn->wDeviceOffset);
+ port = QString::fromWCharArray((wchar_t*)(dn) + dn->wOutputOffset);
+ program = QString::fromWCharArray((wchar_t*)(dn) + dn->wDriverOffset);
+ GlobalUnlock(globalDevnames);
+ }
+}
+
+void QWin32PrintEnginePrivate::readDevmode(HGLOBAL globalDevmode)
+{
+ if (globalDevmode) {
+ DEVMODE *dm = (DEVMODE*) GlobalLock(globalDevmode);
+ release();
+ globalDevMode = globalDevmode;
+ devMode = dm;
+ hdc = CreateDC(reinterpret_cast<const wchar_t *>(program.utf16()),
+ reinterpret_cast<const wchar_t *>(name.utf16()), 0, dm);
+
+ num_copies = devMode->dmCopies;
+ if (!OpenPrinter((wchar_t*)name.utf16(), &hPrinter, 0))
+ qWarning("QPrinter: OpenPrinter() failed after reading DEVMODE.");
+ }
+
+ if (hdc)
+ initHDC();
+}
+
+static void draw_text_item_win(const QPointF &pos, const QTextItemInt &ti, HDC hdc,
+ bool convertToText, const QTransform &xform, const QPointF &topLeft)
+{
+ QFontEngine *fe = ti.fontEngine;
+ QPointF baseline_pos = xform.inverted().map(xform.map(pos) - topLeft);
+
+ SetTextAlign(hdc, TA_BASELINE);
+ SetBkMode(hdc, TRANSPARENT);
+
+ bool has_kerning = ti.f && ti.f->kerning();
+ QFontEngineWin *winfe = (fe->type() == QFontEngine::Win) ? static_cast<QFontEngineWin *>(fe) : 0;
+
+ HFONT hfont;
+ bool ttf = false;
+
+ if (winfe) {
+ hfont = winfe->hfont;
+ ttf = winfe->ttf;
+ } else {
+ hfont = (HFONT)GetStockObject(ANSI_VAR_FONT);
+ }
+
+ HGDIOBJ old_font = SelectObject(hdc, hfont);
+ unsigned int options = (ttf && !convertToText) ? ETO_GLYPH_INDEX : 0;
+ wchar_t *convertedGlyphs = (wchar_t *)ti.chars;
+ QGlyphLayout glyphs = ti.glyphs;
+
+ bool fast = !has_kerning && !(ti.flags & QTextItem::RightToLeft);
+ for (int i = 0; fast && i < glyphs.numGlyphs; i++) {
+ if (glyphs.offsets[i].x != 0 || glyphs.offsets[i].y != 0 || glyphs.justifications[i].space_18d6 != 0
+ || glyphs.attributes[i].dontPrint) {
+ fast = false;
+ break;
+ }
+ }
+
+#if !defined(Q_OS_WINCE)
+ // Scale, rotate and translate here.
+ XFORM win_xform;
+ win_xform.eM11 = xform.m11();
+ win_xform.eM12 = xform.m12();
+ win_xform.eM21 = xform.m21();
+ win_xform.eM22 = xform.m22();
+ win_xform.eDx = xform.dx();
+ win_xform.eDy = xform.dy();
+
+ SetGraphicsMode(hdc, GM_ADVANCED);
+ SetWorldTransform(hdc, &win_xform);
+#endif
+
+ if (fast) {
+ // fast path
+ QVarLengthArray<wchar_t> g(glyphs.numGlyphs);
+ for (int i = 0; i < glyphs.numGlyphs; ++i)
+ g[i] = glyphs.glyphs[i];
+ ExtTextOut(hdc,
+ qRound(baseline_pos.x() + glyphs.offsets[0].x.toReal()),
+ qRound(baseline_pos.y() + glyphs.offsets[0].y.toReal()),
+ options, 0, convertToText ? convertedGlyphs : g.data(), glyphs.numGlyphs, 0);
+ } else {
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> _glyphs;
+
+ QTransform matrix = QTransform::fromTranslate(baseline_pos.x(), baseline_pos.y());
+ ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags,
+ _glyphs, positions);
+ if (_glyphs.size() == 0) {
+ SelectObject(hdc, old_font);
+ return;
+ }
+
+ convertToText = convertToText && glyphs.numGlyphs == _glyphs.size();
+ bool outputEntireItem = _glyphs.size() > 0;
+
+ if (outputEntireItem) {
+ options |= ETO_PDY;
+ QVarLengthArray<INT> glyphDistances(_glyphs.size() * 2);
+ QVarLengthArray<wchar_t> g(_glyphs.size());
+ for (int i=0; i<_glyphs.size() - 1; ++i) {
+ glyphDistances[i * 2] = qRound(positions[i + 1].x) - qRound(positions[i].x);
+ glyphDistances[i * 2 + 1] = qRound(positions[i + 1].y) - qRound(positions[i].y);
+ g[i] = _glyphs[i];
+ }
+ glyphDistances[(_glyphs.size() - 1) * 2] = 0;
+ glyphDistances[(_glyphs.size() - 1) * 2 + 1] = 0;
+ g[_glyphs.size() - 1] = _glyphs[_glyphs.size() - 1];
+ ExtTextOut(hdc, qRound(positions[0].x), qRound(positions[0].y), options, 0,
+ convertToText ? convertedGlyphs : g.data(), _glyphs.size(),
+ glyphDistances.data());
+ } else {
+ int i = 0;
+ while(i < _glyphs.size()) {
+ wchar_t g = _glyphs[i];
+
+ ExtTextOut(hdc, qRound(positions[i].x),
+ qRound(positions[i].y), options, 0,
+ convertToText ? convertedGlyphs + i : &g, 1, 0);
+ ++i;
+ }
+ }
+ }
+
+#if !defined(Q_OS_WINCE)
+ win_xform.eM11 = win_xform.eM22 = 1.0;
+ win_xform.eM12 = win_xform.eM21 = win_xform.eDx = win_xform.eDy = 0.0;
+ SetWorldTransform(hdc, &win_xform);
+#endif
+
+ SelectObject(hdc, old_font);
+}
+
+
+void QWin32PrintEnginePrivate::updateCustomPaperSize()
+{
+ uint paperSize = devMode->dmPaperSize;
+ if (paperSize > 0 && mapDevmodePaperSize(paperSize) == QPrinter::Custom) {
+ has_custom_paper_size = true;
+ DWORD needed = 0;
+ DWORD returned = 0;
+ if (!EnumForms(hPrinter, 1, 0, 0, &needed, &returned)) {
+ BYTE *forms = (BYTE *) malloc(needed);
+ if (EnumForms(hPrinter, 1, forms, needed, &needed, &returned)) {
+ if (paperSize <= returned) {
+ FORM_INFO_1 *formArray = (FORM_INFO_1 *) forms;
+ int width = formArray[paperSize - 1].Size.cx; // 1/1000 of a mm
+ int height = formArray[paperSize - 1].Size.cy; // 1/1000 of a mm
+ paper_size = QSizeF((width * 72 /25.4) / 1000.0, (height * 72 / 25.4) / 1000.0);
+ } else {
+ has_custom_paper_size = false;
+ }
+ }
+ free(forms);
+ }
+ } else {
+ has_custom_paper_size = false;
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
diff --git a/src/widgets/platforms/win/qprintengine_win_p.h b/src/widgets/platforms/win/qprintengine_win_p.h
new file mode 100644
index 0000000000..eeb097fd17
--- /dev/null
+++ b/src/widgets/platforms/win/qprintengine_win_p.h
@@ -0,0 +1,261 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPRINTENGINE_WIN_P_H
+#define QPRINTENGINE_WIN_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QT_NO_PRINTER
+
+#include "QtGui/qprinter.h"
+#include "QtGui/qprintengine.h"
+#include "QtGui/qpaintengine.h"
+#include "QtCore/qt_windows.h"
+#include "private/qpaintengine_alpha_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QWin32PrintEnginePrivate;
+class QPrinterPrivate;
+class QPainterState;
+
+class QWin32PrintEngine : public QAlphaPaintEngine, public QPrintEngine
+{
+ Q_DECLARE_PRIVATE(QWin32PrintEngine)
+public:
+ QWin32PrintEngine(QPrinter::PrinterMode mode);
+
+ // override QWin32PaintEngine
+ bool begin(QPaintDevice *dev);
+ bool end();
+
+ void updateState(const QPaintEngineState &state);
+
+ void updateMatrix(const QTransform &matrix);
+ void updateClipPath(const QPainterPath &clip, Qt::ClipOperation op);
+
+ void drawPath(const QPainterPath &path);
+ void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode);
+ void drawTextItem(const QPointF &p, const QTextItem &textItem);
+
+ void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
+ void drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &p);
+ void setProperty(PrintEnginePropertyKey key, const QVariant &value);
+ QVariant property(PrintEnginePropertyKey key) const;
+
+ bool newPage();
+ bool abort();
+ int metric(QPaintDevice::PaintDeviceMetric) const;
+
+ QPrinter::PrinterState printerState() const;
+
+ QPaintEngine::Type type() const { return Windows; }
+
+ HDC getDC() const;
+ void releaseDC(HDC) const;
+
+ HDC getPrinterDC() const { return getDC(); }
+ void releasePrinterDC(HDC dc) const { releaseDC(dc); }
+
+private:
+ friend class QPrintDialog;
+ friend class QPageSetupDialog;
+};
+
+class QWin32PrintEnginePrivate : public QAlphaPaintEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QWin32PrintEngine)
+public:
+ QWin32PrintEnginePrivate() :
+ hPrinter(0),
+ globalDevMode(0),
+ devMode(0),
+ pInfo(0),
+ hdc(0),
+ mode(QPrinter::ScreenResolution),
+ state(QPrinter::Idle),
+ resolution(0),
+ pageMarginsSet(false),
+ num_copies(1),
+ printToFile(false),
+ fullPage(false),
+ reinit(false),
+ has_custom_paper_size(false)
+ {
+ }
+
+ ~QWin32PrintEnginePrivate();
+
+
+ /* Reads the default printer name and its driver (printerProgram) into
+ the engines private data. */
+ void queryDefault();
+
+ /* Initializes the printer data based on the current printer name. This
+ function creates a DEVMODE struct, HDC and a printer handle. If these
+ structures are already in use, they are freed using release
+ */
+ void initialize();
+
+ /* Initializes data in the print engine whenever the HDC has been renewed
+ */
+ void initHDC();
+
+ /* Releases all the handles the printer currently holds, HDC, DEVMODE,
+ etc and resets the corresponding members to 0. */
+ void release();
+
+ /* Queries the resolutions for the current printer, and returns them
+ in a list. */
+ QList<QVariant> queryResolutions() const;
+
+ /* Resets the DC with changes in devmode. If the printer is active
+ this function only sets the reinit variable to true so it
+ is handled in the next begin or newpage. */
+ void doReinit();
+
+ /* Used by print/page setup dialogs */
+ HGLOBAL *createDevNames();
+
+ void readDevmode(HGLOBAL globalDevmode);
+ void readDevnames(HGLOBAL globalDevnames);
+
+ inline bool resetDC() {
+ hdc = ResetDC(hdc, devMode);
+ return hdc != 0;
+ }
+
+ void strokePath(const QPainterPath &path, const QColor &color);
+ void fillPath(const QPainterPath &path, const QColor &color);
+
+ void composeGdiPath(const QPainterPath &path);
+ void fillPath_dev(const QPainterPath &path, const QColor &color);
+ void strokePath_dev(const QPainterPath &path, const QColor &color, qreal width);
+
+ void updateOrigin();
+
+ void initDevRects();
+ void setPageMargins(int margin_left, int margin_top, int margin_right, int margin_bottom);
+ QRect getPageMargins() const;
+ void updateCustomPaperSize();
+
+ // Windows GDI printer references.
+ HANDLE hPrinter;
+
+ HGLOBAL globalDevMode;
+ DEVMODE *devMode;
+ PRINTER_INFO_2 *pInfo;
+ HGLOBAL hMem;
+
+ HDC hdc;
+
+ QPrinter::PrinterMode mode;
+
+ // Printer info
+ QString name;
+ QString program;
+ QString port;
+
+ // Document info
+ QString docName;
+ QString fileName;
+
+ QPrinter::PrinterState state;
+ int resolution;
+
+ // This QRect is used to store the exact values
+ // entered into the PageSetup Dialog because those are
+ // entered in mm but are since converted to device coordinates.
+ // If they were to be converted back when displaying the dialog
+ // again, there would be inaccuracies so when the user entered 10
+ // it may show up as 9.99 the next time the dialog is opened.
+ // We don't want that confusion.
+ QRect previousDialogMargins;
+
+ bool pageMarginsSet;
+ QRect devPageRect;
+ QRect devPhysicalPageRect;
+ QRect devPaperRect;
+ qreal stretch_x;
+ qreal stretch_y;
+ int origin_x;
+ int origin_y;
+
+ int dpi_x;
+ int dpi_y;
+ int dpi_display;
+ int num_copies;
+
+ uint printToFile : 1;
+ uint fullPage : 1;
+ uint reinit : 1;
+
+ uint complex_xform : 1;
+ uint has_pen : 1;
+ uint has_brush : 1;
+ uint has_custom_paper_size : 1;
+
+ uint txop;
+
+ QColor brush_color;
+ QPen pen;
+ QColor pen_color;
+ QSizeF paper_size;
+
+ QTransform painterMatrix;
+ QTransform matrix;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
+
+#endif // QPRINTENGINE_WIN_P_H
diff --git a/src/widgets/platforms/win/qprinterinfo_win.cpp b/src/widgets/platforms/win/qprinterinfo_win.cpp
new file mode 100644
index 0000000000..cc0cd0232d
--- /dev/null
+++ b/src/widgets/platforms/win/qprinterinfo_win.cpp
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qprinterinfo.h"
+#include "qprinterinfo_p.h"
+
+#include <qstringlist.h>
+
+#include <qt_windows.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_PRINTER
+
+extern QPrinter::PaperSize mapDevmodePaperSize(int s);
+
+QList<QPrinterInfo> QPrinterInfo::availablePrinters()
+{
+ QList<QPrinterInfo> printers;
+
+ DWORD needed = 0;
+ DWORD returned = 0;
+ if (!EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 4, 0, 0, &needed, &returned)) {
+ LPBYTE buffer = new BYTE[needed];
+ if (EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 4, buffer, needed, &needed, &returned)) {
+ PPRINTER_INFO_4 infoList = reinterpret_cast<PPRINTER_INFO_4>(buffer);
+ QPrinterInfo defPrn = defaultPrinter();
+ for (uint i = 0; i < returned; ++i) {
+ QString printerName(QString::fromWCharArray(infoList[i].pPrinterName));
+
+ QPrinterInfo printerInfo(printerName);
+ if (printerInfo.printerName() == defPrn.printerName())
+ printerInfo.d_ptr->isDefault = true;
+ printers.append(printerInfo);
+ }
+ }
+ delete [] buffer;
+ }
+
+ return printers;
+}
+
+QPrinterInfo QPrinterInfo::defaultPrinter()
+{
+ QString noPrinters(QLatin1String("qt_no_printers"));
+ wchar_t buffer[256];
+ GetProfileString(L"windows", L"device", (wchar_t*)noPrinters.utf16(), buffer, 256);
+ QString output = QString::fromWCharArray(buffer);
+ if (output != noPrinters) {
+ // Filter out the name of the printer, which should be everything before a comma.
+ QString printerName = output.split(QLatin1Char(',')).value(0);
+ QPrinterInfo printerInfo(printerName);
+ printerInfo.d_ptr->isDefault = true;
+ return printerInfo;
+ }
+
+ return QPrinterInfo();
+}
+
+QList<QPrinter::PaperSize> QPrinterInfo::supportedPaperSizes() const
+{
+ const Q_D(QPrinterInfo);
+
+ QList<QPrinter::PaperSize> paperSizes;
+ if (isNull())
+ return paperSizes;
+
+ DWORD size = DeviceCapabilities(reinterpret_cast<const wchar_t*>(d->name.utf16()),
+ NULL, DC_PAPERS, NULL, NULL);
+ if ((int)size != -1) {
+ wchar_t *papers = new wchar_t[size];
+ size = DeviceCapabilities(reinterpret_cast<const wchar_t*>(d->name.utf16()),
+ NULL, DC_PAPERS, papers, NULL);
+ for (int c = 0; c < (int)size; ++c)
+ paperSizes.append(mapDevmodePaperSize(papers[c]));
+ delete [] papers;
+ }
+
+ return paperSizes;
+}
+
+#endif // QT_NO_PRINTER
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/win/qrawfont_win.cpp b/src/widgets/platforms/win/qrawfont_win.cpp
new file mode 100644
index 0000000000..d8aa557975
--- /dev/null
+++ b/src/widgets/platforms/win/qrawfont_win.cpp
@@ -0,0 +1,709 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qrawfont_p.h"
+#include <private/qsystemlibrary_p.h>
+
+#if !defined(QT_NO_DIRECTWRITE)
+# include "qfontenginedirectwrite_p.h"
+# include <dwrite.h>
+#endif
+
+#if !defined(QT_NO_RAWFONT)
+
+QT_BEGIN_NAMESPACE
+
+namespace {
+
+ template<typename T>
+ struct BigEndian
+ {
+ quint8 data[sizeof(T)];
+
+ operator T() const
+ {
+ T littleEndian = 0;
+ for (int i=0; i<sizeof(T); ++i) {
+ littleEndian |= data[i] << ((sizeof(T) - i - 1) * 8);
+ }
+
+ return littleEndian;
+ }
+
+ BigEndian<T> &operator=(const T &t)
+ {
+ for (int i=0; i<sizeof(T); ++i) {
+ data[i] = ((t >> (sizeof(T) - i - 1) * 8) & 0xff);
+ }
+
+ return *this;
+ }
+ };
+
+# pragma pack(1)
+
+ // Common structure for all formats of the "name" table
+ struct NameTable
+ {
+ BigEndian<quint16> format;
+ BigEndian<quint16> count;
+ BigEndian<quint16> stringOffset;
+ };
+
+ struct NameRecord
+ {
+ BigEndian<quint16> platformID;
+ BigEndian<quint16> encodingID;
+ BigEndian<quint16> languageID;
+ BigEndian<quint16> nameID;
+ BigEndian<quint16> length;
+ BigEndian<quint16> offset;
+ };
+
+ struct OffsetSubTable
+ {
+ BigEndian<quint32> scalerType;
+ BigEndian<quint16> numTables;
+ BigEndian<quint16> searchRange;
+ BigEndian<quint16> entrySelector;
+ BigEndian<quint16> rangeShift;
+ };
+
+ struct TableDirectory
+ {
+ BigEndian<quint32> identifier;
+ BigEndian<quint32> checkSum;
+ BigEndian<quint32> offset;
+ BigEndian<quint32> length;
+ };
+
+ struct OS2Table
+ {
+ BigEndian<quint16> version;
+ BigEndian<qint16> avgCharWidth;
+ BigEndian<quint16> weightClass;
+ BigEndian<quint16> widthClass;
+ BigEndian<quint16> type;
+ BigEndian<qint16> subscriptXSize;
+ BigEndian<qint16> subscriptYSize;
+ BigEndian<qint16> subscriptXOffset;
+ BigEndian<qint16> subscriptYOffset;
+ BigEndian<qint16> superscriptXSize;
+ BigEndian<qint16> superscriptYSize;
+ BigEndian<qint16> superscriptXOffset;
+ BigEndian<qint16> superscriptYOffset;
+ BigEndian<qint16> strikeOutSize;
+ BigEndian<qint16> strikeOutPosition;
+ BigEndian<qint16> familyClass;
+ quint8 panose[10];
+ BigEndian<quint32> unicodeRanges[4];
+ quint8 vendorID[4];
+ BigEndian<quint16> selection;
+ BigEndian<quint16> firstCharIndex;
+ BigEndian<quint16> lastCharIndex;
+ BigEndian<qint16> typoAscender;
+ BigEndian<qint16> typoDescender;
+ BigEndian<qint16> typoLineGap;
+ BigEndian<quint16> winAscent;
+ BigEndian<quint16> winDescent;
+ BigEndian<quint32> codepageRanges[2];
+ BigEndian<qint16> height;
+ BigEndian<qint16> capHeight;
+ BigEndian<quint16> defaultChar;
+ BigEndian<quint16> breakChar;
+ BigEndian<quint16> maxContext;
+ };
+
+# pragma pack()
+
+ class EmbeddedFont
+ {
+ public:
+ EmbeddedFont(const QByteArray &fontData);
+
+ QString changeFamilyName(const QString &newFamilyName);
+ QByteArray data() const { return m_fontData; }
+ TableDirectory *tableDirectoryEntry(const QByteArray &tagName);
+ QString familyName(TableDirectory *nameTableDirectory = 0);
+
+ private:
+ QByteArray m_fontData;
+ };
+
+ EmbeddedFont::EmbeddedFont(const QByteArray &fontData) : m_fontData(fontData)
+ {
+ }
+
+ TableDirectory *EmbeddedFont::tableDirectoryEntry(const QByteArray &tagName)
+ {
+ Q_ASSERT(tagName.size() == 4);
+
+ const BigEndian<quint32> *tagIdPtr =
+ reinterpret_cast<const BigEndian<quint32> *>(tagName.constData());
+ quint32 tagId = *tagIdPtr;
+
+ OffsetSubTable *offsetSubTable = reinterpret_cast<OffsetSubTable *>(m_fontData.data());
+ TableDirectory *tableDirectory = reinterpret_cast<TableDirectory *>(offsetSubTable + 1);
+
+ TableDirectory *nameTableDirectoryEntry = 0;
+ for (int i=0; i<offsetSubTable->numTables; ++i, ++tableDirectory) {
+ if (tableDirectory->identifier == tagId) {
+ nameTableDirectoryEntry = tableDirectory;
+ break;
+ }
+ }
+
+ return nameTableDirectoryEntry;
+ }
+
+ QString EmbeddedFont::familyName(TableDirectory *nameTableDirectoryEntry)
+ {
+ QString name;
+
+ if (nameTableDirectoryEntry == 0)
+ nameTableDirectoryEntry = tableDirectoryEntry("name");
+
+ if (nameTableDirectoryEntry != 0) {
+ NameTable *nameTable = reinterpret_cast<NameTable *>(m_fontData.data()
+ + nameTableDirectoryEntry->offset);
+ NameRecord *nameRecord = reinterpret_cast<NameRecord *>(nameTable + 1);
+ for (int i=0; i<nameTable->count; ++i, ++nameRecord) {
+ if (nameRecord->nameID == 1
+ && nameRecord->platformID == 3 // Windows
+ && nameRecord->languageID == 0x0409) { // US English
+ const void *ptr = reinterpret_cast<const quint8 *>(nameTable)
+ + nameTable->stringOffset
+ + nameRecord->offset;
+
+ const BigEndian<quint16> *s = reinterpret_cast<const BigEndian<quint16> *>(ptr);
+ for (int j=0; j<nameRecord->length / sizeof(quint16); ++j)
+ name += QChar(s[j]);
+
+ break;
+ }
+ }
+ }
+
+ return name;
+ }
+
+ QString EmbeddedFont::changeFamilyName(const QString &newFamilyName)
+ {
+ TableDirectory *nameTableDirectoryEntry = tableDirectoryEntry("name");
+ if (nameTableDirectoryEntry == 0)
+ return QString();
+
+ QString oldFamilyName = familyName(nameTableDirectoryEntry);
+
+ // Reserve size for name table header, five required name records and string
+ const int requiredRecordCount = 5;
+ quint16 nameIds[requiredRecordCount] = { 1, 2, 3, 4, 6 };
+
+ int sizeOfHeader = sizeof(NameTable) + sizeof(NameRecord) * requiredRecordCount;
+ int newFamilyNameSize = newFamilyName.size() * sizeof(quint16);
+
+ const QString regularString = QString::fromLatin1("Regular");
+ int regularStringSize = regularString.size() * sizeof(quint16);
+
+ // Align table size of table to 32 bits (pad with 0)
+ int fullSize = ((sizeOfHeader + newFamilyNameSize + regularStringSize) & ~3) + 4;
+
+ QByteArray newNameTable(fullSize, char(0));
+
+ {
+ NameTable *nameTable = reinterpret_cast<NameTable *>(newNameTable.data());
+ nameTable->count = requiredRecordCount;
+ nameTable->stringOffset = sizeOfHeader;
+
+ NameRecord *nameRecord = reinterpret_cast<NameRecord *>(nameTable + 1);
+ for (int i=0; i<requiredRecordCount; ++i, nameRecord++) {
+ nameRecord->nameID = nameIds[i];
+ nameRecord->encodingID = 1;
+ nameRecord->languageID = 0x0409;
+ nameRecord->platformID = 3;
+ nameRecord->length = newFamilyNameSize;
+
+ // Special case for sub-family
+ if (nameIds[i] == 4) {
+ nameRecord->offset = newFamilyNameSize;
+ nameRecord->length = regularStringSize;
+ }
+ }
+
+ // nameRecord now points to string data
+ BigEndian<quint16> *stringStorage = reinterpret_cast<BigEndian<quint16> *>(nameRecord);
+ const quint16 *sourceString = newFamilyName.utf16();
+ for (int i=0; i<newFamilyName.size(); ++i)
+ stringStorage[i] = sourceString[i];
+ stringStorage += newFamilyName.size();
+
+ sourceString = regularString.utf16();
+ for (int i=0; i<regularString.size(); ++i)
+ stringStorage[i] = sourceString[i];
+ }
+
+ quint32 *p = reinterpret_cast<quint32 *>(newNameTable.data());
+ quint32 *tableEnd = reinterpret_cast<quint32 *>(newNameTable.data() + fullSize);
+
+ quint32 checkSum = 0;
+ while (p < tableEnd)
+ checkSum += *(p++);
+
+ nameTableDirectoryEntry->checkSum = checkSum;
+ nameTableDirectoryEntry->offset = m_fontData.size();
+ nameTableDirectoryEntry->length = fullSize;
+
+ m_fontData.append(newNameTable);
+
+ return oldFamilyName;
+ }
+
+#if !defined(QT_NO_DIRECTWRITE)
+
+ class DirectWriteFontFileStream: public IDWriteFontFileStream
+ {
+ public:
+ DirectWriteFontFileStream(const QByteArray &fontData)
+ : m_fontData(fontData)
+ , m_referenceCount(0)
+ {
+ }
+
+ ~DirectWriteFontFileStream()
+ {
+ }
+
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **object);
+ ULONG STDMETHODCALLTYPE AddRef();
+ ULONG STDMETHODCALLTYPE Release();
+
+ HRESULT STDMETHODCALLTYPE ReadFileFragment(const void **fragmentStart, UINT64 fileOffset,
+ UINT64 fragmentSize, OUT void **fragmentContext);
+ void STDMETHODCALLTYPE ReleaseFileFragment(void *fragmentContext);
+ HRESULT STDMETHODCALLTYPE GetFileSize(OUT UINT64 *fileSize);
+ HRESULT STDMETHODCALLTYPE GetLastWriteTime(OUT UINT64 *lastWriteTime);
+
+ private:
+ QByteArray m_fontData;
+ ULONG m_referenceCount;
+ };
+
+ HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::QueryInterface(REFIID iid, void **object)
+ {
+ if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileStream)) {
+ *object = this;
+ AddRef();
+ return S_OK;
+ } else {
+ *object = NULL;
+ return E_NOINTERFACE;
+ }
+ }
+
+ ULONG STDMETHODCALLTYPE DirectWriteFontFileStream::AddRef()
+ {
+ return InterlockedIncrement(&m_referenceCount);
+ }
+
+ ULONG STDMETHODCALLTYPE DirectWriteFontFileStream::Release()
+ {
+ ULONG newCount = InterlockedDecrement(&m_referenceCount);
+ if (newCount == 0)
+ delete this;
+ return newCount;
+ }
+
+ HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::ReadFileFragment(
+ const void **fragmentStart,
+ UINT64 fileOffset,
+ UINT64 fragmentSize,
+ OUT void **fragmentContext)
+ {
+ *fragmentContext = NULL;
+ if (fragmentSize + fileOffset <= m_fontData.size()) {
+ *fragmentStart = m_fontData.data() + fileOffset;
+ return S_OK;
+ } else {
+ *fragmentStart = NULL;
+ return E_FAIL;
+ }
+ }
+
+ void STDMETHODCALLTYPE DirectWriteFontFileStream::ReleaseFileFragment(void *)
+ {
+ }
+
+ HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::GetFileSize(UINT64 *fileSize)
+ {
+ *fileSize = m_fontData.size();
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::GetLastWriteTime(UINT64 *lastWriteTime)
+ {
+ *lastWriteTime = 0;
+ return E_NOTIMPL;
+ }
+
+ class DirectWriteFontFileLoader: public IDWriteFontFileLoader
+ {
+ public:
+ DirectWriteFontFileLoader() : m_referenceCount(0) {}
+
+ ~DirectWriteFontFileLoader()
+ {
+ }
+
+ inline void addKey(const void *key, const QByteArray &fontData)
+ {
+ Q_ASSERT(!m_fontDatas.contains(key));
+ m_fontDatas.insert(key, fontData);
+ }
+
+ inline void removeKey(const void *key)
+ {
+ m_fontDatas.remove(key);
+ }
+
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **object);
+ ULONG STDMETHODCALLTYPE AddRef();
+ ULONG STDMETHODCALLTYPE Release();
+
+ HRESULT STDMETHODCALLTYPE CreateStreamFromKey(void const *fontFileReferenceKey,
+ UINT32 fontFileReferenceKeySize,
+ OUT IDWriteFontFileStream **fontFileStream);
+
+ private:
+ ULONG m_referenceCount;
+ QHash<const void *, QByteArray> m_fontDatas;
+ };
+
+ HRESULT STDMETHODCALLTYPE DirectWriteFontFileLoader::QueryInterface(const IID &iid,
+ void **object)
+ {
+ if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileLoader)) {
+ *object = this;
+ AddRef();
+ return S_OK;
+ } else {
+ *object = NULL;
+ return E_NOINTERFACE;
+ }
+ }
+
+ ULONG STDMETHODCALLTYPE DirectWriteFontFileLoader::AddRef()
+ {
+ return InterlockedIncrement(&m_referenceCount);
+ }
+
+ ULONG STDMETHODCALLTYPE DirectWriteFontFileLoader::Release()
+ {
+ ULONG newCount = InterlockedDecrement(&m_referenceCount);
+ if (newCount == 0)
+ delete this;
+ return newCount;
+ }
+
+ HRESULT STDMETHODCALLTYPE DirectWriteFontFileLoader::CreateStreamFromKey(
+ void const *fontFileReferenceKey,
+ UINT32 fontFileReferenceKeySize,
+ IDWriteFontFileStream **fontFileStream)
+ {
+ Q_UNUSED(fontFileReferenceKeySize);
+
+ if (fontFileReferenceKeySize != sizeof(const void *)) {
+ qWarning("DirectWriteFontFileLoader::CreateStreamFromKey: Wrong key size");
+ return E_FAIL;
+ }
+
+ const void *key = *reinterpret_cast<void * const *>(fontFileReferenceKey);
+ *fontFileStream = NULL;
+ if (!m_fontDatas.contains(key))
+ return E_FAIL;
+
+ QByteArray fontData = m_fontDatas.value(key);
+ DirectWriteFontFileStream *stream = new DirectWriteFontFileStream(fontData);
+ stream->AddRef();
+ *fontFileStream = stream;
+
+ return S_OK;
+ }
+
+ class CustomFontFileLoader
+ {
+ public:
+ CustomFontFileLoader() : m_directWriteFactory(0), m_directWriteFontFileLoader(0)
+ {
+ HRESULT hres = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED,
+ __uuidof(IDWriteFactory),
+ reinterpret_cast<IUnknown **>(&m_directWriteFactory));
+ if (FAILED(hres)) {
+ qErrnoWarning(hres, "CustomFontFileLoader::CustomFontFileLoader: "
+ "DWriteCreateFactory failed.");
+ } else {
+ m_directWriteFontFileLoader = new DirectWriteFontFileLoader();
+ m_directWriteFactory->RegisterFontFileLoader(m_directWriteFontFileLoader);
+ }
+ }
+
+ ~CustomFontFileLoader()
+ {
+ if (m_directWriteFactory != 0 && m_directWriteFontFileLoader != 0)
+ m_directWriteFactory->UnregisterFontFileLoader(m_directWriteFontFileLoader);
+
+ if (m_directWriteFactory != 0)
+ m_directWriteFactory->Release();
+ }
+
+ void addKey(const void *key, const QByteArray &fontData)
+ {
+ if (m_directWriteFontFileLoader != 0)
+ m_directWriteFontFileLoader->addKey(key, fontData);
+ }
+
+ void removeKey(const void *key)
+ {
+ if (m_directWriteFontFileLoader != 0)
+ m_directWriteFontFileLoader->removeKey(key);
+ }
+
+ IDWriteFontFileLoader *loader() const
+ {
+ return m_directWriteFontFileLoader;
+ }
+
+ private:
+ IDWriteFactory *m_directWriteFactory;
+ DirectWriteFontFileLoader *m_directWriteFontFileLoader;
+ };
+
+#endif
+
+} // Anonymous namespace
+
+
+// From qfontdatabase_win.cpp
+extern QFontEngine *qt_load_font_engine_win(const QFontDef &request);
+// From qfontdatabase.cpp
+extern QFont::Weight weightFromInteger(int weight);
+
+void QRawFontPrivate::platformCleanUp()
+{
+ if (fontHandle != NULL) {
+ if (ptrRemoveFontMemResourceEx == NULL) {
+ QFunctionPointer func =
+ QSystemLibrary::resolve(QLatin1String("gdi32"), "RemoveFontMemResourceEx");
+ ptrRemoveFontMemResourceEx =
+ reinterpret_cast<QRawFontPrivate::PtrRemoveFontMemResourceEx>(func);
+ }
+
+ if (ptrRemoveFontMemResourceEx == NULL) {
+ qWarning("QRawFont::platformCleanUp: Can't find RemoveFontMemResourceEx in gdi32");
+ fontHandle = NULL;
+ } else {
+ ptrRemoveFontMemResourceEx(fontHandle);
+ fontHandle = NULL;
+ }
+ }
+}
+
+void QRawFontPrivate::platformLoadFromData(const QByteArray &_fontData,
+ qreal pixelSize,
+ QFont::HintingPreference hintingPreference)
+{
+ QByteArray fontData(_fontData);
+ EmbeddedFont font(fontData);
+
+#if !defined(QT_NO_DIRECTWRITE)
+ if (hintingPreference == QFont::PreferDefaultHinting
+ || hintingPreference == QFont::PreferFullHinting)
+#endif
+ {
+ GUID guid;
+ CoCreateGuid(&guid);
+
+ QString uniqueFamilyName = QString::fromLatin1("f")
+ + QString::number(guid.Data1, 36) + QLatin1Char('-')
+ + QString::number(guid.Data2, 36) + QLatin1Char('-')
+ + QString::number(guid.Data3, 36) + QLatin1Char('-')
+ + QString::number(*reinterpret_cast<quint64 *>(guid.Data4), 36);
+
+ QString actualFontName = font.changeFamilyName(uniqueFamilyName);
+ if (actualFontName.isEmpty()) {
+ qWarning("QRawFont::platformLoadFromData: Can't change family name of font");
+ return;
+ }
+
+ if (ptrAddFontMemResourceEx == NULL || ptrRemoveFontMemResourceEx == NULL) {
+ QFunctionPointer func =
+ QSystemLibrary::resolve(QLatin1String("gdi32"), "RemoveFontMemResourceEx");
+ ptrRemoveFontMemResourceEx =
+ reinterpret_cast<QRawFontPrivate::PtrRemoveFontMemResourceEx>(func);
+
+ func = QSystemLibrary::resolve(QLatin1String("gdi32"), "AddFontMemResourceEx");
+ ptrAddFontMemResourceEx =
+ reinterpret_cast<QRawFontPrivate::PtrAddFontMemResourceEx>(func);
+ }
+
+ Q_ASSERT(fontHandle == NULL);
+ if (ptrAddFontMemResourceEx != NULL && ptrRemoveFontMemResourceEx != NULL) {
+ DWORD count = 0;
+ fontData = font.data();
+ fontHandle = ptrAddFontMemResourceEx(fontData.data(), fontData.size(), 0, &count);
+
+ if (count == 0 && fontHandle != NULL) {
+ ptrRemoveFontMemResourceEx(fontHandle);
+ fontHandle = NULL;
+ }
+ }
+
+ if (fontHandle == NULL) {
+ qWarning("QRawFont::platformLoadFromData: AddFontMemResourceEx failed");
+ } else {
+ QFontDef request;
+ request.family = uniqueFamilyName;
+ request.pixelSize = pixelSize;
+ request.styleStrategy = QFont::NoFontMerging | QFont::PreferMatch;
+ request.hintingPreference = hintingPreference;
+
+ fontEngine = qt_load_font_engine_win(request);
+ if (request.family != fontEngine->fontDef.family) {
+ qWarning("QRawFont::platformLoadFromData: Failed to load font. "
+ "Got fallback instead: %s", qPrintable(fontEngine->fontDef.family));
+ if (fontEngine->cache_count == 0 && fontEngine->ref == 0)
+ delete fontEngine;
+ fontEngine = 0;
+ } else {
+ Q_ASSERT(fontEngine->cache_count == 0 && fontEngine->ref == 0);
+
+ // Override the generated font name
+ static_cast<QFontEngineWin *>(fontEngine)->uniqueFamilyName = uniqueFamilyName;
+ fontEngine->fontDef.family = actualFontName;
+ fontEngine->ref.ref();
+ }
+ }
+ }
+#if !defined(QT_NO_DIRECTWRITE)
+ else {
+ CustomFontFileLoader fontFileLoader;
+ fontFileLoader.addKey(this, fontData);
+
+ IDWriteFactory *factory = NULL;
+ HRESULT hres = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED,
+ __uuidof(IDWriteFactory),
+ reinterpret_cast<IUnknown **>(&factory));
+ if (FAILED(hres)) {
+ qErrnoWarning(hres, "QRawFont::platformLoadFromData: DWriteCreateFactory failed");
+ return;
+ }
+
+ IDWriteFontFile *fontFile = NULL;
+ void *key = this;
+
+ hres = factory->CreateCustomFontFileReference(&key, sizeof(void *),
+ fontFileLoader.loader(), &fontFile);
+ if (FAILED(hres)) {
+ qErrnoWarning(hres, "QRawFont::platformLoadFromData: "
+ "CreateCustomFontFileReference failed");
+ factory->Release();
+ return;
+ }
+
+ BOOL isSupportedFontType;
+ DWRITE_FONT_FILE_TYPE fontFileType;
+ DWRITE_FONT_FACE_TYPE fontFaceType;
+ UINT32 numberOfFaces;
+ fontFile->Analyze(&isSupportedFontType, &fontFileType, &fontFaceType, &numberOfFaces);
+ if (!isSupportedFontType) {
+ fontFile->Release();
+ factory->Release();
+ return;
+ }
+
+ IDWriteFontFace *directWriteFontFace = NULL;
+ hres = factory->CreateFontFace(fontFaceType, 1, &fontFile, 0, DWRITE_FONT_SIMULATIONS_NONE,
+ &directWriteFontFace);
+ if (FAILED(hres)) {
+ qErrnoWarning(hres, "QRawFont::platformLoadFromData: CreateFontFace failed");
+ fontFile->Release();
+ factory->Release();
+ return;
+ }
+
+ fontFile->Release();
+
+ fontEngine = new QFontEngineDirectWrite(factory, directWriteFontFace, pixelSize);
+
+ // Get font family from font data
+ fontEngine->fontDef.family = font.familyName();
+ fontEngine->ref.ref();
+
+ directWriteFontFace->Release();
+ factory->Release();
+ }
+#endif
+
+ // Get style and weight info
+ if (fontEngine != 0) {
+ TableDirectory *os2TableEntry = font.tableDirectoryEntry("OS/2");
+ if (os2TableEntry != 0) {
+ const OS2Table *os2Table =
+ reinterpret_cast<const OS2Table *>(fontData.constData()
+ + os2TableEntry->offset);
+
+ bool italic = os2Table->selection & 1;
+ bool oblique = os2Table->selection & 128;
+
+ if (italic)
+ fontEngine->fontDef.style = QFont::StyleItalic;
+ else if (oblique)
+ fontEngine->fontDef.style = QFont::StyleOblique;
+ else
+ fontEngine->fontDef.style = QFont::StyleNormal;
+
+ fontEngine->fontDef.weight = weightFromInteger(os2Table->weightClass);
+ }
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_RAWFONT
diff --git a/src/widgets/platforms/win/qregion_win.cpp b/src/widgets/platforms/win/qregion_win.cpp
new file mode 100644
index 0000000000..57fd0858e0
--- /dev/null
+++ b/src/widgets/platforms/win/qregion_win.cpp
@@ -0,0 +1,149 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qatomic.h"
+#include "qbitmap.h"
+#include "qbuffer.h"
+#include "qimage.h"
+#include "qpolygon.h"
+#include "qregion.h"
+#include "qt_windows.h"
+#include "qpainterpath.h"
+
+QT_BEGIN_NAMESPACE
+
+QRegion::QRegionData QRegion::shared_empty = { Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0 };
+
+HRGN qt_tryCreateRegion(QRegion::RegionType type, int left, int top, int right, int bottom)
+{
+ const int tries = 10;
+ for (int i = 0; i < tries; ++i) {
+ HRGN region = 0;
+ switch (type) {
+ case QRegion::Rectangle:
+ region = CreateRectRgn(left, top, right, bottom);
+ break;
+ case QRegion::Ellipse:
+#ifndef Q_OS_WINCE
+ region = CreateEllipticRgn(left, top, right, bottom);
+#endif
+ break;
+ }
+ if (region) {
+ if (GetRegionData(region, 0, 0))
+ return region;
+ else
+ DeleteObject(region);
+ }
+ }
+ return 0;
+}
+
+QRegion qt_region_from_HRGN(HRGN rgn)
+{
+ int numBytes = GetRegionData(rgn, 0, 0);
+ if (numBytes == 0)
+ return QRegion();
+
+ char *buf = new char[numBytes];
+ if (buf == 0)
+ return QRegion();
+
+ RGNDATA *rd = reinterpret_cast<RGNDATA*>(buf);
+ if (GetRegionData(rgn, numBytes, rd) == 0) {
+ delete [] buf;
+ return QRegion();
+ }
+
+ QRegion region;
+ RECT *r = reinterpret_cast<RECT*>(rd->Buffer);
+ for (uint i = 0; i < rd->rdh.nCount; ++i) {
+ QRect rect;
+ rect.setCoords(r->left, r->top, r->right - 1, r->bottom - 1);
+ ++r;
+ region |= rect;
+ }
+
+ delete [] buf;
+
+ return region;
+}
+
+void qt_win_dispose_rgn(HRGN r)
+{
+ if (r)
+ DeleteObject(r);
+}
+
+static void qt_add_rect(HRGN &winRegion, QRect r)
+{
+ HRGN rgn = CreateRectRgn(r.left(), r.top(), r.x() + r.width(), r.y() + r.height());
+ if (rgn) {
+ HRGN dest = CreateRectRgn(0,0,0,0);
+ int result = CombineRgn(dest, winRegion, rgn, RGN_OR);
+ if (result) {
+ DeleteObject(winRegion);
+ winRegion = dest;
+ }
+ DeleteObject(rgn);
+ }
+}
+
+void QRegion::ensureHandle() const
+{
+ if (d->rgn)
+ DeleteObject(d->rgn);
+ d->rgn = CreateRectRgn(0,0,0,0);
+ if (d->qt_rgn) {
+ if (d->qt_rgn->numRects == 1) {
+ QRect r = d->qt_rgn->extents;
+ qt_add_rect(d->rgn, r);
+ return;
+ }
+ for (int i = 0;i < d->qt_rgn->numRects;i++) {
+ QRect r = d->qt_rgn->rects.at(i);
+ qt_add_rect(d->rgn, r);
+ }
+ }
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/win/qsound_win.cpp b/src/widgets/platforms/win/qsound_win.cpp
new file mode 100644
index 0000000000..3c77dacf2a
--- /dev/null
+++ b/src/widgets/platforms/win/qsound_win.cpp
@@ -0,0 +1,205 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsound.h"
+
+#ifndef QT_NO_SOUND
+
+#include "qapplication.h"
+#include "qapplication_p.h"
+#include <qfile.h>
+#include "qpointer.h"
+#include "qsound_p.h"
+
+#include <qt_windows.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAuServerWindows : public QAuServer {
+ Q_OBJECT
+
+public:
+ QAuServerWindows(QObject* parent);
+ ~QAuServerWindows();
+
+ void playHelper(const QString &filename, int loop, QSound *snd);
+ void play(const QString& filename, int loop);
+ void play(QSound*);
+
+ void stop(QSound*);
+ bool okay();
+
+ int decLoop(QSound *snd) { return QAuServer::decLoop(snd); }
+
+ HANDLE current;
+ HANDLE mutex;
+ HANDLE event;
+};
+
+QAuServerWindows::QAuServerWindows(QObject* parent) :
+ QAuServer(parent), current(0)
+{
+ mutex = CreateMutex(0, 0, 0);
+ event = CreateEvent(0, FALSE, FALSE, 0);
+}
+
+QAuServerWindows::~QAuServerWindows()
+{
+ HANDLE mtx = mutex;
+ WaitForSingleObject(mtx, INFINITE);
+ mutex = 0;
+
+ ReleaseMutex(mtx);
+ CloseHandle(mtx);
+ CloseHandle(event);
+}
+
+struct SoundInfo
+{
+ SoundInfo(const QString &fn, int lp, QSound *snd, QAuServerWindows *srv)
+ : sound(snd), server(srv), filename(fn), loops(lp)
+ {
+ }
+
+ QSound *sound;
+ QAuServerWindows *server;
+ QString filename;
+ int loops;
+};
+
+DWORD WINAPI SoundPlayProc(LPVOID param)
+{
+ SoundInfo *info = (SoundInfo*)param;
+
+ // copy data before waking up GUI thread
+ QAuServerWindows *server = info->server;
+ QSound *sound = info->sound;
+ int loops = info->loops;
+ QString filename = info->filename;
+ HANDLE mutex = server->mutex;
+ HANDLE event = server->event;
+ info = 0;
+
+ // server must not be destroyed until thread finishes
+ // and all other sounds have to wait
+ WaitForSingleObject(mutex, INFINITE);
+
+ if (loops <= 1) {
+ server->current = 0;
+ int flags = SND_FILENAME|SND_ASYNC;
+ if (loops == -1)
+ flags |= SND_LOOP;
+
+ PlaySound((wchar_t*)filename.utf16(), 0, flags);
+ if (sound && loops == 1)
+ server->decLoop(sound);
+
+ // GUI thread continues, but we are done as well.
+ SetEvent(event);
+ } else {
+ // signal GUI thread to continue - sound might be reset!
+ QPointer<QSound> guarded_sound = sound;
+ SetEvent(event);
+
+ for (int l = 0; l < loops && server->current; ++l) {
+ PlaySound((wchar_t*)filename.utf16(), 0, SND_FILENAME | SND_SYNC);
+
+ if (guarded_sound)
+ server->decLoop(guarded_sound);
+ }
+ server->current = 0;
+ }
+ ReleaseMutex(mutex);
+
+ return 0;
+}
+
+void QAuServerWindows::playHelper(const QString &filename, int loop, QSound *snd)
+{
+ if (loop == 0)
+ return;
+ // busy?
+ if (WaitForSingleObject(mutex, 0) == WAIT_TIMEOUT)
+ return;
+ ReleaseMutex(mutex);
+
+ DWORD threadid = 0;
+ SoundInfo info(filename, loop, snd, this);
+ current = CreateThread(0, 0, SoundPlayProc, &info, 0, &threadid);
+ CloseHandle(current);
+
+ WaitForSingleObject(event, INFINITE);
+}
+
+void QAuServerWindows::play(const QString& filename, int loop)
+{
+ playHelper(filename, loop, 0);
+}
+
+void QAuServerWindows::play(QSound* s)
+{
+ playHelper(s->fileName(), s->loops(), s);
+}
+
+void QAuServerWindows::stop(QSound*)
+{
+ // stop unlooped sound
+ if (!current)
+ PlaySound(0, 0, 0);
+ // stop after loop is done
+ current = 0;
+}
+
+bool QAuServerWindows::okay()
+{
+ return true;
+}
+
+QAuServer* qt_new_audio_server()
+{
+ return new QAuServerWindows(qApp);
+}
+
+QT_END_NAMESPACE
+
+#include "qsound_win.moc"
+
+#endif // QT_NO_SOUND
diff --git a/src/widgets/platforms/win/qwidget_win.cpp b/src/widgets/platforms/win/qwidget_win.cpp
new file mode 100644
index 0000000000..60446ddb08
--- /dev/null
+++ b/src/widgets/platforms/win/qwidget_win.cpp
@@ -0,0 +1,2139 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qapplication.h"
+#include "qapplication_p.h"
+#include "qbitmap.h"
+#include "qcursor.h"
+#include "qdesktopwidget.h"
+#include "qevent.h"
+#include "qimage.h"
+#include "qlayout.h"
+#include "qpainter.h"
+#include "qstack.h"
+#include "qt_windows.h"
+#include "qwidget.h"
+#include "qwidget_p.h"
+#include "private/qbackingstore_p.h"
+#include "private/qwindowsurface_raster_p.h"
+
+#include "qscrollbar.h"
+#include "qabstractscrollarea.h"
+#include <private/qabstractscrollarea_p.h>
+
+#include <qdebug.h>
+
+#include <private/qapplication_p.h>
+#include <private/qwininputcontext_p.h>
+#include <private/qpaintengine_raster_p.h>
+#include <private/qsystemlibrary_p.h>
+
+#if defined(Q_WS_WINCE)
+#include "qguifunctions_wince.h"
+QT_USE_NAMESPACE
+extern void qt_wince_maximize(QWidget *widget); //defined in qguifunctions_wince.cpp
+extern void qt_wince_unmaximize(QWidget *widget); //defined in qguifunctions_wince.cpp
+extern void qt_wince_minimize(HWND hwnd); //defined in qguifunctions_wince.cpp
+extern void qt_wince_full_screen(HWND hwnd, bool fullScreen, UINT swpf); //defined in qguifunctions_wince.cpp
+extern bool qt_wince_is_mobile(); //defined in qguifunctions_wince.cpp
+#endif
+
+typedef BOOL (WINAPI *PtrSetLayeredWindowAttributes)(HWND hwnd, COLORREF crKey, BYTE bAlpha, DWORD dwFlags);
+static PtrSetLayeredWindowAttributes ptrSetLayeredWindowAttributes = 0;
+
+#ifndef QT_NO_DIRECTDRAW
+#include <ddraw.h>
+#include <private/qimage_p.h>
+static IDirectDraw *qt_ddraw_object;
+static IDirectDrawSurface *qt_ddraw_primary;
+#endif
+
+
+
+#if defined(QT_NON_COMMERCIAL)
+#include "qnc_win.h"
+#endif
+
+#if !defined(WS_EX_TOOLWINDOW)
+#define WS_EX_TOOLWINDOW 0x00000080
+#endif
+
+#if !defined(GWLP_WNDPROC)
+#define GWLP_WNDPROC GWL_WNDPROC
+#endif
+
+//#define TABLET_DEBUG
+#define PACKETDATA (PK_X | PK_Y | PK_BUTTONS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE \
+ | PK_ORIENTATION | PK_CURSOR | PK_Z)
+#define PACKETMODE 0
+#include <wintab.h>
+#include <pktdef.h>
+
+QT_BEGIN_NAMESPACE
+
+typedef HCTX (API *PtrWTOpen)(HWND, LPLOGCONTEXT, BOOL);
+typedef BOOL (API *PtrWTClose)(HCTX);
+typedef UINT (API *PtrWTInfo)(UINT, UINT, LPVOID);
+typedef BOOL (API *PtrWTEnable)(HCTX, BOOL);
+typedef BOOL (API *PtrWTOverlap)(HCTX, BOOL);
+typedef int (API *PtrWTPacketsGet)(HCTX, int, LPVOID);
+typedef BOOL (API *PtrWTGet)(HCTX, LPLOGCONTEXT);
+typedef int (API *PtrWTQueueSizeGet)(HCTX);
+typedef BOOL (API *PtrWTQueueSizeSet)(HCTX, int);
+
+static PtrWTOpen ptrWTOpen = 0;
+static PtrWTClose ptrWTClose = 0;
+static PtrWTInfo ptrWTInfo = 0;
+static PtrWTQueueSizeGet ptrWTQueueSizeGet = 0;
+static PtrWTQueueSizeSet ptrWTQueueSizeSet = 0;
+#ifndef QT_NO_TABLETEVENT
+static void init_wintab_functions();
+static void qt_tablet_init();
+static void qt_tablet_cleanup();
+#endif // QT_NO_TABLETEVENT
+extern HCTX qt_tablet_context;
+extern bool qt_tablet_tilt_support;
+
+static QWidget *qt_tablet_widget = 0;
+QWidget* qt_get_tablet_widget()
+{
+ return qt_tablet_widget;
+}
+
+extern bool qt_is_gui_used;
+
+#ifndef QT_NO_TABLETEVENT
+static void init_wintab_functions()
+{
+#if defined(Q_OS_WINCE)
+ return;
+#else
+ if (!qt_is_gui_used)
+ return;
+ QSystemLibrary library(QLatin1String("wintab32"));
+ ptrWTOpen = (PtrWTOpen)library.resolve("WTOpenW");
+ ptrWTInfo = (PtrWTInfo)library.resolve("WTInfoW");
+ ptrWTClose = (PtrWTClose)library.resolve("WTClose");
+ ptrWTQueueSizeGet = (PtrWTQueueSizeGet)library.resolve("WTQueueSizeGet");
+ ptrWTQueueSizeSet = (PtrWTQueueSizeSet)library.resolve("WTQueueSizeSet");
+#endif // Q_OS_WINCE
+}
+
+static void qt_tablet_init()
+{
+ static bool firstTime = true;
+ if (!firstTime)
+ return;
+ firstTime = false;
+ qt_tablet_widget = new QWidget(0);
+ qt_tablet_widget->createWinId();
+ qt_tablet_widget->setObjectName(QLatin1String("Qt internal tablet widget"));
+ // We don't need this internal widget to appear in QApplication::topLevelWidgets()
+ if (QWidgetPrivate::allWidgets)
+ QWidgetPrivate::allWidgets->remove(qt_tablet_widget);
+ LOGCONTEXT lcMine;
+ qAddPostRoutine(qt_tablet_cleanup);
+ struct tagAXIS tpOri[3];
+ init_wintab_functions();
+ if (ptrWTInfo && ptrWTOpen && ptrWTQueueSizeGet && ptrWTQueueSizeSet) {
+ // make sure we have WinTab
+ if (!ptrWTInfo(0, 0, NULL)) {
+#ifdef TABLET_DEBUG
+ qWarning("QWidget: Wintab services not available");
+#endif
+ return;
+ }
+
+ // some tablets don't support tilt, check if it is possible,
+ qt_tablet_tilt_support = ptrWTInfo(WTI_DEVICES, DVC_ORIENTATION, &tpOri);
+ if (qt_tablet_tilt_support) {
+ // check for azimuth and altitude
+ qt_tablet_tilt_support = tpOri[0].axResolution && tpOri[1].axResolution;
+ }
+ // build our context from the default context
+ ptrWTInfo(WTI_DEFSYSCTX, 0, &lcMine);
+ // Go for the raw coordinates, the tablet event will return good stuff
+ lcMine.lcOptions |= CXO_MESSAGES | CXO_CSRMESSAGES;
+ lcMine.lcPktData = PACKETDATA;
+ lcMine.lcPktMode = PACKETMODE;
+ lcMine.lcMoveMask = PACKETDATA;
+ lcMine.lcOutOrgX = 0;
+ lcMine.lcOutExtX = lcMine.lcInExtX;
+ lcMine.lcOutOrgY = 0;
+ lcMine.lcOutExtY = -lcMine.lcInExtY;
+ qt_tablet_context = ptrWTOpen(qt_tablet_widget->winId(), &lcMine, true);
+#ifdef TABLET_DEBUG
+ qDebug("Tablet is %p", qt_tablet_context);
+#endif
+ if (!qt_tablet_context) {
+#ifdef TABLET_DEBUG
+ qWarning("QWidget: Failed to open the tablet");
+#endif
+ return;
+ }
+ // Set the size of the Packet Queue to the correct size...
+ int currSize = ptrWTQueueSizeGet(qt_tablet_context);
+ if (!ptrWTQueueSizeSet(qt_tablet_context, QT_TABLET_NPACKETQSIZE)) {
+ // Ideally one might want to use a smaller
+ // multiple, but for now, since we managed to destroy
+ // the existing Q with the previous call, set it back
+ // to the other size, which should work. If not,
+ // there will be trouble.
+ if (!ptrWTQueueSizeSet(qt_tablet_context, currSize)) {
+ Q_ASSERT_X(0, "Qt::Internal", "There is no packet queue for"
+ " the tablet. The tablet will not work");
+ }
+ }
+ }
+}
+
+static void qt_tablet_cleanup()
+{
+ if (ptrWTClose)
+ ptrWTClose(qt_tablet_context);
+ delete qt_tablet_widget;
+ qt_tablet_widget = 0;
+}
+#endif // QT_NO_TABLETEVENT
+
+const QString qt_reg_winclass(QWidget *w); // defined in qapplication_win.cpp
+
+#ifndef QT_NO_DRAGANDDROP
+void qt_olednd_unregister(QWidget* widget, QOleDropTarget *dst); // dnd_win
+QOleDropTarget* qt_olednd_register(QWidget* widget);
+#endif
+
+extern bool qt_nograb();
+extern HRGN qt_win_bitmapToRegion(const QBitmap& bitmap);
+
+static QWidget *mouseGrb = 0;
+static QCursor *mouseGrbCur = 0;
+static QWidget *keyboardGrb = 0;
+static HHOOK journalRec = 0;
+
+extern "C" LRESULT QT_WIN_CALLBACK QtWndProc(HWND, UINT, WPARAM, LPARAM);
+
+#define XCOORD_MAX 16383
+#define WRECT_MAX 16383
+
+/*****************************************************************************
+ QWidget member functions
+ *****************************************************************************/
+
+#ifndef Q_WS_WINCE
+void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyOldWindow)
+{
+ Q_Q(QWidget);
+ static int sw = -1, sh = -1;
+
+ Qt::WindowType type = q->windowType();
+ Qt::WindowFlags flags = data.window_flags;
+
+ bool topLevel = (flags & Qt::Window);
+ bool popup = (type == Qt::Popup);
+ bool dialog = (type == Qt::Dialog
+ || type == Qt::Sheet
+ || (flags & Qt::MSWindowsFixedSizeDialogHint));
+ bool desktop = (type == Qt::Desktop);
+ bool tool = (type == Qt::Tool || type == Qt::Drawer);
+
+ HINSTANCE appinst = qWinAppInst();
+ HWND parentw, destroyw = 0;
+ WId id = 0;
+
+ QString windowClassName = qt_reg_winclass(q);
+
+ if (!window) // always initialize
+ initializeWindow = true;
+
+ if (popup)
+ flags |= Qt::WindowStaysOnTopHint; // a popup stays on top
+
+ if (sw < 0) { // get the (primary) screen size
+ sw = GetSystemMetrics(SM_CXSCREEN);
+ sh = GetSystemMetrics(SM_CYSCREEN);
+ }
+
+ if (desktop && !q->testAttribute(Qt::WA_DontShowOnScreen)) { // desktop widget
+ popup = false; // force this flags off
+ data.crect.setRect(GetSystemMetrics(76 /* SM_XVIRTUALSCREEN */), GetSystemMetrics(77 /* SM_YVIRTUALSCREEN */),
+ GetSystemMetrics(78 /* SM_CXVIRTUALSCREEN */), GetSystemMetrics(79 /* SM_CYVIRTUALSCREEN */));
+ }
+
+ parentw = q->parentWidget() ? q->parentWidget()->effectiveWinId() : 0;
+
+ QString title;
+ int style = WS_CHILD;
+ int exsty = 0;
+
+ if (window) {
+ style = GetWindowLong(window, GWL_STYLE);
+ if (!style)
+ qErrnoWarning("QWidget::create: GetWindowLong failed");
+ topLevel = false; // #### needed for some IE plugins??
+ } else if (popup || (type == Qt::ToolTip) || (type == Qt::SplashScreen)) {
+ style = WS_POPUP;
+ } else if (topLevel && !desktop) {
+ if (flags & Qt::FramelessWindowHint)
+ style = WS_POPUP; // no border
+ else if (flags & Qt::WindowTitleHint)
+ style = WS_OVERLAPPED;
+ else
+ style = 0;
+ }
+ if (!desktop) {
+ // if (!testAttribute(Qt::WA_PaintUnclipped))
+ // ### Commented out for now as it causes some problems, but
+ // this should be correct anyway, so dig some more into this
+#ifndef Q_FLATTEN_EXPOSE
+ style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN ;
+#endif
+ if (topLevel) {
+ if ((type == Qt::Window || dialog || tool)) {
+ if (!(flags & Qt::FramelessWindowHint)) {
+ style |= WS_POPUP;
+ if (!(flags & Qt::MSWindowsFixedSizeDialogHint))
+ style |= WS_THICKFRAME;
+ else
+ style |= WS_DLGFRAME;
+ }
+ if (flags & Qt::WindowTitleHint)
+ style |= WS_CAPTION;
+ if (flags & Qt::WindowSystemMenuHint)
+ style |= WS_SYSMENU;
+ if (flags & Qt::WindowMinimizeButtonHint)
+ style |= WS_MINIMIZEBOX;
+ if (shouldShowMaximizeButton())
+ style |= WS_MAXIMIZEBOX;
+ if (tool)
+ exsty |= WS_EX_TOOLWINDOW;
+ if (flags & Qt::WindowContextHelpButtonHint)
+ exsty |= WS_EX_CONTEXTHELP;
+ } else {
+ exsty |= WS_EX_TOOLWINDOW;
+ }
+ }
+ }
+
+ if (flags & Qt::WindowTitleHint) {
+ title = q->isWindow() ? qAppName() : q->objectName();
+ }
+
+ // The Qt::WA_WState_Created flag is checked by translateConfigEvent() in
+ // qapplication_win.cpp. We switch it off temporarily to avoid move
+ // and resize events during creationt
+ q->setAttribute(Qt::WA_WState_Created, false);
+
+ if (window) { // override the old window
+ if (destroyOldWindow)
+ destroyw = data.winid;
+ id = window;
+ setWinId(window);
+ LONG res = SetWindowLong(window, GWL_STYLE, style);
+ if (!res)
+ qErrnoWarning("QWidget::create: Failed to set window style");
+#ifdef _WIN64
+ res = SetWindowLongPtr( window, GWLP_WNDPROC, (LONG_PTR)QtWndProc );
+#else
+ res = SetWindowLong( window, GWL_WNDPROC, (LONG)QtWndProc );
+#endif
+ if (!res)
+ qErrnoWarning("QWidget::create: Failed to set window procedure");
+ } else if (desktop) { // desktop widget
+ id = GetDesktopWindow();
+// QWidget *otherDesktop = QWidget::find(id); // is there another desktop?
+// if (otherDesktop && otherDesktop->testWFlags(Qt::WPaintDesktop)) {
+// otherDesktop->d_func()->setWinId(0); // remove id from widget mapper
+// d->setWinId(id); // make sure otherDesktop is
+// otherDesktop->d_func()->setWinId(id); // found first
+// } else {
+ setWinId(id);
+// }
+ } else if (topLevel) { // create top-level widget
+ if (popup)
+ parentw = 0;
+
+ const bool wasMoved = q->testAttribute(Qt::WA_Moved);
+ int x = wasMoved ? data.crect.left() : CW_USEDEFAULT;
+ int y = wasMoved ? data.crect.top() : CW_USEDEFAULT;
+ int w = CW_USEDEFAULT;
+ int h = CW_USEDEFAULT;
+
+ // Adjust for framestrut when needed
+ RECT rect = {0,0,0,0};
+ bool isVisibleOnScreen = !q->testAttribute(Qt::WA_DontShowOnScreen);
+ if (isVisibleOnScreen && AdjustWindowRectEx(&rect, style & ~WS_OVERLAPPED, FALSE, exsty)) {
+ QTLWExtra *td = maybeTopData();
+ if (wasMoved && (td && !td->posFromMove)) {
+ x = data.crect.x() + rect.left;
+ y = data.crect.y() + rect.top;
+ }
+
+ if (q->testAttribute(Qt::WA_Resized)) {
+ w = data.crect.width() + (rect.right - rect.left);
+ h = data.crect.height() + (rect.bottom - rect.top);
+ }
+ }
+ //update position & initial size of POPUP window
+ if (isVisibleOnScreen && topLevel && initializeWindow && (style & WS_POPUP)) {
+ if (!q->testAttribute(Qt::WA_Resized)) {
+ w = sw/2;
+ h = 4*sh/10;
+ if (extra) {
+ int dx = rect.right - rect.left;
+ int dy = rect.bottom - rect.top;
+ w = qMin(w, extra->maxw + dx);
+ h = qMin(h, extra->maxh + dy);
+ w = qMax(w, extra->minw + dx);
+ h = qMax(h, extra->minh + dy);
+ }
+ }
+ if (!wasMoved) {
+ x = sw/2 - w/2;
+ y = sh/2 - h/2;
+ }
+ }
+
+ id = CreateWindowEx(exsty, reinterpret_cast<const wchar_t *>(windowClassName.utf16()),
+ reinterpret_cast<const wchar_t *>(title.utf16()), style,
+ x, y, w, h,
+ parentw, NULL, appinst, NULL);
+ if (!id)
+ qErrnoWarning("QWidget::create: Failed to create window");
+ setWinId(id);
+ if ((flags & Qt::WindowStaysOnTopHint) || (type == Qt::ToolTip)) {
+ SetWindowPos(id, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
+ if (flags & Qt::WindowStaysOnBottomHint)
+ qWarning() << "QWidget: Incompatible window flags: the window can't be on top and on bottom at the same time";
+ } else if (flags & Qt::WindowStaysOnBottomHint)
+ SetWindowPos(id, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
+ winUpdateIsOpaque();
+ } else if (q->testAttribute(Qt::WA_NativeWindow) || paintOnScreen()) { // create child widget
+ id = CreateWindowEx(exsty, reinterpret_cast<const wchar_t *>(windowClassName.utf16()),
+ reinterpret_cast<const wchar_t *>(title.utf16()), style,
+ data.crect.left(), data.crect.top(), data.crect.width(), data.crect.height(),
+ parentw, NULL, appinst, NULL);
+ if (!id)
+ qErrnoWarning("QWidget::create: Failed to create window");
+ SetWindowPos(id, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+ setWinId(id);
+ }
+
+ if (desktop) {
+ q->setAttribute(Qt::WA_WState_Visible);
+ } else if (topLevel && !q->testAttribute(Qt::WA_DontShowOnScreen)) {
+ RECT cr;
+ GetClientRect(id, &cr);
+ // one cannot trust cr.left and cr.top, use a correction POINT instead
+ POINT pt;
+ pt.x = 0;
+ pt.y = 0;
+ ClientToScreen(id, &pt);
+
+ if (data.crect.width() == 0 || data.crect.height() == 0) {
+ data.crect = QRect(pt.x, pt.y, data.crect.width(), data.crect.height());
+ } else {
+ data.crect = QRect(QPoint(pt.x, pt.y),
+ QPoint(pt.x + cr.right - 1, pt.y + cr.bottom - 1));
+ }
+
+ if (data.fstrut_dirty) {
+ // be nice to activeqt
+ updateFrameStrut();
+ }
+ }
+
+ if (topLevel) {
+ if (data.window_flags & Qt::CustomizeWindowHint
+ && data.window_flags & Qt::WindowTitleHint) {
+ HMENU systemMenu = GetSystemMenu((HWND)q->internalWinId(), FALSE);
+ if (data.window_flags & Qt::WindowCloseButtonHint)
+ EnableMenuItem(systemMenu, SC_CLOSE, MF_BYCOMMAND|MF_ENABLED);
+ else
+ EnableMenuItem(systemMenu, SC_CLOSE, MF_BYCOMMAND|MF_GRAYED);
+ }
+ }
+
+ q->setAttribute(Qt::WA_WState_Created); // accept move/resize events
+ hd = 0; // no display context
+
+ if (q->testAttribute(Qt::WA_AcceptTouchEvents))
+ registerTouchWindow();
+
+ if (window) { // got window from outside
+ if (IsWindowVisible(window))
+ q->setAttribute(Qt::WA_WState_Visible);
+ else
+ q->setAttribute(Qt::WA_WState_Visible, false);
+ }
+
+ if (extra && !extra->mask.isEmpty())
+ setMask_sys(extra->mask);
+
+#if defined(QT_NON_COMMERCIAL)
+ QT_NC_WIDGET_CREATE
+#endif
+
+ if (q->hasFocus() && q->testAttribute(Qt::WA_InputMethodEnabled))
+ q->inputContext()->setFocusWidget(q);
+
+ if (destroyw) {
+ DestroyWindow(destroyw);
+ }
+
+#ifndef QT_NO_TABLETEVENT
+ if (q != qt_tablet_widget && QWidgetPrivate::mapper)
+ qt_tablet_init();
+#endif // QT_NO_TABLETEVENT
+
+ if (q->testAttribute(Qt::WA_DropSiteRegistered))
+ registerDropSite(true);
+
+ if (maybeTopData() && maybeTopData()->opacity != 255)
+ q->setWindowOpacity(maybeTopData()->opacity/255.);
+
+ if (topLevel && (data.crect.width() == 0 || data.crect.height() == 0)) {
+ q->setAttribute(Qt::WA_OutsideWSRange, true);
+ }
+
+ if (!topLevel && q->testAttribute(Qt::WA_NativeWindow) && q->testAttribute(Qt::WA_Mapped)) {
+ Q_ASSERT(q->internalWinId());
+ ShowWindow(q->internalWinId(), SW_SHOW);
+ }
+}
+
+#endif //Q_WS_WINCE
+
+
+void QWidget::destroy(bool destroyWindow, bool destroySubWindows)
+{
+ Q_D(QWidget);
+ d->aboutToDestroy();
+ if (!isWindow() && parentWidget())
+ parentWidget()->d_func()->invalidateBuffer(d->effectiveRectFor(geometry()));
+ d->deactivateWidgetCleanup();
+ if (testAttribute(Qt::WA_WState_Created)) {
+ setAttribute(Qt::WA_WState_Created, false);
+ for(int i = 0; i < d->children.size(); ++i) { // destroy all widget children
+ register QObject *obj = d->children.at(i);
+ if (obj->isWidgetType())
+ ((QWidget*)obj)->destroy(destroySubWindows,
+ destroySubWindows);
+ }
+ if (mouseGrb == this)
+ releaseMouse();
+ if (keyboardGrb == this)
+ releaseKeyboard();
+ if (testAttribute(Qt::WA_ShowModal)) // just be sure we leave modal
+ QApplicationPrivate::leaveModal(this);
+ else if ((windowType() == Qt::Popup))
+ qApp->d_func()->closePopup(this);
+ if (destroyWindow && !(windowType() == Qt::Desktop) && internalWinId()) {
+ DestroyWindow(internalWinId());
+ }
+#ifdef Q_WS_WINCE
+ if (destroyWindow && (windowType() == Qt::Desktop) && !GetDesktopWindow()) {
+ DestroyWindow(internalWinId());
+ }
+
+#endif
+ QT_TRY {
+ d->setWinId(0);
+ } QT_CATCH (const std::bad_alloc &) {
+ // swallow - destructors must not throw
+ }
+ }
+}
+
+void QWidgetPrivate::reparentChildren()
+{
+ Q_Q(QWidget);
+ QObjectList chlist = q->children();
+ for(int i = 0; i < chlist.size(); ++i) { // reparent children
+ QObject *obj = chlist.at(i);
+ if (obj->isWidgetType()) {
+ QWidget *w = (QWidget *)obj;
+ if ((w->windowType() == Qt::Popup)) {
+ ;
+ } else if (w->isWindow()) {
+ bool showIt = w->isVisible();
+ QPoint old_pos = w->pos();
+ w->setParent(q, w->windowFlags());
+ w->move(old_pos);
+ if (showIt)
+ w->show();
+ } else {
+ w->d_func()->invalidateBuffer(w->rect());
+ SetParent(w->effectiveWinId(), q->effectiveWinId());
+ w->d_func()->reparentChildren();
+ }
+ }
+ }
+}
+
+void QWidgetPrivate::setParent_sys(QWidget *parent, Qt::WindowFlags f)
+{
+ Q_Q(QWidget);
+ bool wasCreated = q->testAttribute(Qt::WA_WState_Created);
+ if (q->isVisible() && q->parentWidget() && parent != q->parentWidget())
+ q->parentWidget()->d_func()->invalidateBuffer(effectiveRectFor(q->geometry()));
+
+ WId old_winid = data.winid;
+ // hide and reparent our own window away. Otherwise we might get
+ // destroyed when emitting the child remove event below. See QWorkspace.
+ if (q->isVisible() && data.winid) {
+ ShowWindow(data.winid, SW_HIDE);
+ SetParent(data.winid, 0);
+ }
+ bool dropSiteWasRegistered = false;
+ if (q->testAttribute(Qt::WA_DropSiteRegistered)) {
+ dropSiteWasRegistered = true;
+ q->setAttribute(Qt::WA_DropSiteRegistered, false); // ole dnd unregister (we will register again below)
+ }
+
+ if ((q->windowType() == Qt::Desktop))
+ old_winid = 0;
+ setWinId(0);
+
+ QObjectPrivate::setParent_helper(parent);
+ bool explicitlyHidden = q->testAttribute(Qt::WA_WState_Hidden) && q->testAttribute(Qt::WA_WState_ExplicitShowHide);
+
+ data.window_flags = f;
+ data.fstrut_dirty = true;
+ q->setAttribute(Qt::WA_WState_Created, false);
+ q->setAttribute(Qt::WA_WState_Visible, false);
+ q->setAttribute(Qt::WA_WState_Hidden, false);
+ adjustFlags(data.window_flags, q);
+ // keep compatibility with previous versions, we need to preserve the created state
+ // (but we recreate the winId for the widget being reparented, again for compatibility)
+ if (wasCreated || (!q->isWindow() && parent->testAttribute(Qt::WA_WState_Created)))
+ createWinId();
+ if (q->isWindow() || (!parent || parent->isVisible()) || explicitlyHidden)
+ q->setAttribute(Qt::WA_WState_Hidden);
+ q->setAttribute(Qt::WA_WState_ExplicitShowHide, explicitlyHidden);
+
+ if (wasCreated) {
+ reparentChildren();
+ }
+
+ if (extra && !extra->mask.isEmpty()) {
+ QRegion r = extra->mask;
+ extra->mask = QRegion();
+ q->setMask(r);
+ }
+ if (extra && extra->topextra && !extra->topextra->caption.isEmpty()) {
+ setWindowIcon_sys(true);
+ setWindowTitle_helper(extra->topextra->caption);
+ }
+ if (old_winid)
+ DestroyWindow(old_winid);
+
+ if (q->testAttribute(Qt::WA_AcceptDrops) || dropSiteWasRegistered
+ || (!q->isWindow() && q->parentWidget() && q->parentWidget()->testAttribute(Qt::WA_DropSiteRegistered)))
+ q->setAttribute(Qt::WA_DropSiteRegistered, true);
+
+#ifdef Q_WS_WINCE
+ // Show borderless toplevel windows in tasklist & NavBar
+ if (!parent) {
+ QString txt = q->windowTitle().isEmpty()?qAppName():q->windowTitle();
+ SetWindowText(q->internalWinId(), (wchar_t*)txt.utf16());
+ }
+#endif
+ invalidateBuffer(q->rect());
+}
+
+
+QPoint QWidget::mapToGlobal(const QPoint &pos) const
+{
+ Q_D(const QWidget);
+ QWidget *parentWindow = window();
+ QWExtra *extra = parentWindow->d_func()->extra;
+ if (!isVisible() || parentWindow->isMinimized() || !testAttribute(Qt::WA_WState_Created) || !internalWinId()
+ || (extra
+#ifndef QT_NO_GRAPHICSVIEW
+ && extra->proxyWidget
+#endif //QT_NO_GRAPHICSVIEW
+ )) {
+ if (extra && extra->topextra && extra->topextra->embedded) {
+ QPoint pt = mapTo(parentWindow, pos);
+ POINT p = {pt.x(), pt.y()};
+ ClientToScreen(parentWindow->effectiveWinId(), &p);
+ return QPoint(p.x, p.y);
+ } else {
+ QPoint toGlobal = mapTo(parentWindow, pos) + parentWindow->pos();
+ // Adjust for window decorations
+ toGlobal += parentWindow->geometry().topLeft() - parentWindow->frameGeometry().topLeft();
+ return toGlobal;
+ }
+ }
+ POINT p;
+ QPoint tmp = d->mapToWS(pos);
+ p.x = tmp.x();
+ p.y = tmp.y();
+ ClientToScreen(internalWinId(), &p);
+ return QPoint(p.x, p.y);
+}
+
+QPoint QWidget::mapFromGlobal(const QPoint &pos) const
+{
+ Q_D(const QWidget);
+ QWidget *parentWindow = window();
+ QWExtra *extra = parentWindow->d_func()->extra;
+ if (!isVisible() || parentWindow->isMinimized() || !testAttribute(Qt::WA_WState_Created) || !internalWinId()
+ || (extra
+#ifndef QT_NO_GRAPHICSVIEW
+ && extra->proxyWidget
+#endif //QT_NO_GRAPHICSVIEW
+ )) {
+ if (extra && extra->topextra && extra->topextra->embedded) {
+ POINT p = {pos.x(), pos.y()};
+ ScreenToClient(parentWindow->effectiveWinId(), &p);
+ return mapFrom(parentWindow, QPoint(p.x, p.y));
+ } else {
+ QPoint fromGlobal = mapFrom(parentWindow, pos - parentWindow->pos());
+ // Adjust for window decorations
+ fromGlobal -= parentWindow->geometry().topLeft() - parentWindow->frameGeometry().topLeft();
+ return fromGlobal;
+ }
+ }
+ POINT p;
+ p.x = pos.x();
+ p.y = pos.y();
+ ScreenToClient(internalWinId(), &p);
+ return d->mapFromWS(QPoint(p.x, p.y));
+}
+
+void QWidgetPrivate::updateSystemBackground() {}
+
+#ifndef QT_NO_CURSOR
+void QWidgetPrivate::setCursor_sys(const QCursor &cursor)
+{
+ Q_UNUSED(cursor);
+ Q_Q(QWidget);
+ qt_win_set_cursor(q, false);
+}
+
+void QWidgetPrivate::unsetCursor_sys()
+{
+ Q_Q(QWidget);
+ qt_win_set_cursor(q, false);
+}
+#endif
+
+void QWidgetPrivate::setWindowTitle_sys(const QString &caption)
+{
+ Q_Q(QWidget);
+ if (!q->isWindow())
+ return;
+
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ SetWindowText(q->internalWinId(), (wchar_t*)caption.utf16());
+}
+
+HICON qt_createIcon(QIcon icon, int xSize, int ySize, QPixmap **cache)
+{
+ HICON result = 0;
+ if (!icon.isNull()) { // valid icon
+ QSize size = icon.actualSize(QSize(xSize, ySize));
+ QPixmap pm = icon.pixmap(size);
+ if (pm.isNull())
+ return 0;
+
+ result = pm.toWinHICON();
+
+ if (cache) {
+ delete *cache;
+ *cache = new QPixmap(pm);;
+ }
+ }
+ return result;
+}
+
+void QWidgetPrivate::setWindowIcon_sys(bool forceReset)
+{
+ Q_Q(QWidget);
+ if (!q->testAttribute(Qt::WA_WState_Created) || !q->isWindow())
+ return;
+ QTLWExtra* x = topData();
+ if (x->iconPixmap && !forceReset)
+ // already been set
+ return;
+
+ if (x->winIconBig) {
+ DestroyIcon(x->winIconBig);
+ x->winIconBig = 0;
+ }
+ if (x->winIconSmall) {
+ DestroyIcon(x->winIconSmall);
+ x->winIconSmall = 0;
+ }
+
+ x->winIconSmall = qt_createIcon(q->windowIcon(),
+ GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
+ &(x->iconPixmap));
+ x->winIconBig = qt_createIcon(q->windowIcon(),
+ GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON),
+ &(x->iconPixmap));
+ if (x->winIconBig) {
+ SendMessage(q->internalWinId(), WM_SETICON, 0 /* ICON_SMALL */, (LPARAM)x->winIconSmall);
+ SendMessage(q->internalWinId(), WM_SETICON, 1 /* ICON_BIG */, (LPARAM)x->winIconBig);
+ } else {
+ SendMessage(q->internalWinId(), WM_SETICON, 0 /* ICON_SMALL */, (LPARAM)x->winIconSmall);
+ SendMessage(q->internalWinId(), WM_SETICON, 1 /* ICON_BIG */, (LPARAM)x->winIconSmall);
+ }
+}
+
+
+void QWidgetPrivate::setWindowIconText_sys(const QString &iconText)
+{
+ Q_UNUSED(iconText);
+}
+
+
+QCursor *qt_grab_cursor()
+{
+ return mouseGrbCur;
+}
+
+// The procedure does nothing, but is required for mousegrabbing to work
+#ifndef Q_WS_WINCE
+LRESULT QT_WIN_CALLBACK qJournalRecordProc(int nCode, WPARAM wParam, LPARAM lParam)
+{
+ return CallNextHookEx(journalRec, nCode, wParam, lParam);
+}
+#endif //Q_WS_WINCE
+
+/* Works only as long as pointer is inside the application's window,
+ which is good enough for QDockWidget.
+
+ Doesn't call SetWindowsHookEx() - this function causes a system-wide
+ freeze if any other app on the system installs a hook and fails to
+ process events. */
+void QWidgetPrivate::grabMouseWhileInWindow()
+{
+ Q_Q(QWidget);
+ if (!qt_nograb()) {
+ if (mouseGrb)
+ mouseGrb->releaseMouse();
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ SetCapture(q->effectiveWinId());
+ mouseGrb = q;
+#ifndef QT_NO_CURSOR
+ mouseGrbCur = new QCursor(mouseGrb->cursor());
+#endif
+ }
+}
+
+#ifndef Q_WS_WINCE
+void QWidget::grabMouse()
+{
+ if (!qt_nograb()) {
+ if (mouseGrb)
+ mouseGrb->releaseMouse();
+ journalRec = SetWindowsHookEx(WH_JOURNALRECORD, (HOOKPROC)qJournalRecordProc, GetModuleHandle(0), 0);
+ Q_ASSERT(testAttribute(Qt::WA_WState_Created));
+ SetCapture(effectiveWinId());
+ mouseGrb = this;
+#ifndef QT_NO_CURSOR
+ mouseGrbCur = new QCursor(mouseGrb->cursor());
+#endif
+ }
+}
+
+#ifndef QT_NO_CURSOR
+void QWidget::grabMouse(const QCursor &cursor)
+{
+ if (!qt_nograb()) {
+ if (mouseGrb)
+ mouseGrb->releaseMouse();
+ journalRec = SetWindowsHookEx(WH_JOURNALRECORD, (HOOKPROC)qJournalRecordProc, GetModuleHandle(0), 0);
+ Q_ASSERT(testAttribute(Qt::WA_WState_Created));
+ SetCapture(effectiveWinId());
+ mouseGrbCur = new QCursor(cursor);
+ SetCursor(mouseGrbCur->handle());
+ mouseGrb = this;
+ }
+}
+#endif
+
+void QWidget::releaseMouse()
+{
+ if (!qt_nograb() && mouseGrb == this) {
+ ReleaseCapture();
+ if (journalRec) {
+ UnhookWindowsHookEx(journalRec);
+ journalRec = 0;
+ }
+ if (mouseGrbCur) {
+ delete mouseGrbCur;
+ mouseGrbCur = 0;
+ }
+ mouseGrb = 0;
+ }
+}
+#endif
+
+void QWidget::grabKeyboard()
+{
+ if (!qt_nograb()) {
+ if (keyboardGrb)
+ keyboardGrb->releaseKeyboard();
+ keyboardGrb = this;
+ }
+}
+
+void QWidget::releaseKeyboard()
+{
+ if (!qt_nograb() && keyboardGrb == this)
+ keyboardGrb = 0;
+}
+
+
+QWidget *QWidget::mouseGrabber()
+{
+ return mouseGrb;
+}
+
+QWidget *QWidget::keyboardGrabber()
+{
+ return keyboardGrb;
+}
+
+void QWidget::activateWindow()
+{
+ window()->createWinId();
+ SetForegroundWindow(window()->internalWinId());
+}
+
+#ifndef Q_WS_WINCE
+void QWidget::setWindowState(Qt::WindowStates newstate)
+{
+ Q_D(QWidget);
+ Qt::WindowStates oldstate = windowState();
+ if (oldstate == newstate)
+ return;
+
+ int max = SW_MAXIMIZE;
+ int min = SW_MINIMIZE;
+
+ int normal = SW_SHOWNOACTIVATE;
+ if (newstate & Qt::WindowActive) {
+ max = SW_SHOWMAXIMIZED;
+ min = SW_SHOWMINIMIZED;
+ normal = SW_SHOWNORMAL;
+ }
+
+ if (isWindow()) {
+ createWinId();
+ Q_ASSERT(testAttribute(Qt::WA_WState_Created));
+
+ // Ensure the initial size is valid, since we store it as normalGeometry below.
+ if (!testAttribute(Qt::WA_Resized) && !isVisible())
+ adjustSize();
+
+ if ((oldstate & Qt::WindowMaximized) != (newstate & Qt::WindowMaximized)) {
+ if (newstate & Qt::WindowMaximized && !(oldstate & Qt::WindowFullScreen))
+ d->topData()->normalGeometry = geometry();
+ if (isVisible() && !(newstate & Qt::WindowMinimized)) {
+ ShowWindow(internalWinId(), (newstate & Qt::WindowMaximized) ? max : normal);
+ if (!(newstate & Qt::WindowFullScreen)) {
+ QRect r = d->topData()->normalGeometry;
+ if (!(newstate & Qt::WindowMaximized) && r.width() >= 0) {
+ if (pos() != r.topLeft() || size() !=r.size()) {
+ d->topData()->normalGeometry = QRect(0,0,-1,-1);
+ setGeometry(r);
+ }
+ }
+ } else {
+ d->updateFrameStrut();
+ }
+ }
+ }
+
+ if ((oldstate & Qt::WindowFullScreen) != (newstate & Qt::WindowFullScreen)) {
+ if (newstate & Qt::WindowFullScreen) {
+ if (d->topData()->normalGeometry.width() < 0 && !(oldstate & Qt::WindowMaximized))
+ d->topData()->normalGeometry = geometry();
+ d->topData()->savedFlags = Qt::WindowFlags(GetWindowLong(internalWinId(), GWL_STYLE));
+#ifndef Q_FLATTEN_EXPOSE
+ UINT style = WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_POPUP;
+#else
+ UINT style = WS_POPUP;
+#endif
+ if (ulong(d->topData()->savedFlags) & WS_SYSMENU)
+ style |= WS_SYSMENU;
+ if (isVisible())
+ style |= WS_VISIBLE;
+ SetWindowLong(internalWinId(), GWL_STYLE, style);
+ QRect r = QApplication::desktop()->screenGeometry(this);
+ UINT swpf = SWP_FRAMECHANGED;
+ if (newstate & Qt::WindowActive)
+ swpf |= SWP_NOACTIVATE;
+
+ SetWindowPos(internalWinId(), HWND_TOP, r.left(), r.top(), r.width(), r.height(), swpf);
+ d->updateFrameStrut();
+ } else {
+ UINT style = d->topData()->savedFlags;
+ if (isVisible())
+ style |= WS_VISIBLE;
+ SetWindowLong(internalWinId(), GWL_STYLE, style);
+
+ UINT swpf = SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOSIZE | SWP_NOMOVE;
+ if (newstate & Qt::WindowActive)
+ swpf |= SWP_NOACTIVATE;
+ SetWindowPos(internalWinId(), 0, 0, 0, 0, 0, swpf);
+ d->updateFrameStrut();
+
+ // preserve maximized state
+ if (isVisible())
+ ShowWindow(internalWinId(), (newstate & Qt::WindowMaximized) ? max : normal);
+
+ if (!(newstate & Qt::WindowMaximized)) {
+ QRect r = d->topData()->normalGeometry;
+ d->topData()->normalGeometry = QRect(0,0,-1,-1);
+ if (r.isValid())
+ setGeometry(r);
+ }
+ }
+ }
+
+ if ((oldstate & Qt::WindowMinimized) != (newstate & Qt::WindowMinimized)) {
+ if (isVisible())
+ ShowWindow(internalWinId(), (newstate & Qt::WindowMinimized) ? min :
+ (newstate & Qt::WindowMaximized) ? max : normal);
+ }
+ }
+ data->window_state = newstate;
+ QWindowStateChangeEvent e(oldstate);
+ QApplication::sendEvent(this, &e);
+}
+#endif //Q_WS_WINCE
+
+
+/*
+ \internal
+ Platform-specific part of QWidget::hide().
+*/
+
+void QWidgetPrivate::hide_sys()
+{
+ Q_Q(QWidget);
+ deactivateWidgetCleanup();
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+#ifdef Q_WS_WINCE
+ if (!qt_wince_is_mobile() && q->isFullScreen()) {
+ HWND handle = FindWindow(L"HHTaskBar", L"");
+ if (handle) {
+ ShowWindow(handle, 1);
+ EnableWindow(handle, true);
+ }
+ }
+#endif
+ if (q->windowFlags() != Qt::Desktop) {
+ if ((q->windowFlags() & Qt::Popup) && q->internalWinId())
+ ShowWindow(q->internalWinId(), SW_HIDE);
+ else if (q->internalWinId())
+ SetWindowPos(q->internalWinId(),0, 0,0,0,0, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER);
+ }
+ if (q->isWindow()) {
+ if (QWidgetBackingStore *bs = maybeBackingStore())
+ bs->releaseBuffer();
+ } else {
+ invalidateBuffer(q->rect());
+ }
+ q->setAttribute(Qt::WA_Mapped, false);
+}
+
+
+/*
+ \internal
+ Platform-specific part of QWidget::show().
+*/
+#ifndef Q_WS_WINCE
+void QWidgetPrivate::show_sys()
+{
+ Q_Q(QWidget);
+#if defined(QT_NON_COMMERCIAL)
+ QT_NC_SHOW_WINDOW
+#endif
+ if (q->testAttribute(Qt::WA_OutsideWSRange))
+ return;
+ q->setAttribute(Qt::WA_Mapped);
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+
+ if (q->testAttribute(Qt::WA_DontShowOnScreen)) {
+ invalidateBuffer(q->rect());
+ return;
+ }
+
+ if (data.window_flags & Qt::Window) {
+ QTLWExtra *extra = topData();
+ if (!extra->hotkeyRegistered) {
+ // Try to set the hotkey using information from STARTUPINFO
+ STARTUPINFO startupInfo;
+ GetStartupInfo(&startupInfo);
+ // If STARTF_USEHOTKEY is set, hStdInput is the virtual keycode
+ if (startupInfo.dwFlags & 0x00000200) {
+ WPARAM hotKey = (WPARAM)startupInfo.hStdInput;
+ SendMessage(data.winid, WM_SETHOTKEY, hotKey, 0);
+ }
+ extra->hotkeyRegistered = 1;
+ }
+ }
+
+ int sm = SW_SHOWNORMAL;
+ bool fakedMaximize = false;
+ if (q->isWindow()) {
+ if (q->isMinimized()) {
+ sm = SW_SHOWMINIMIZED;
+ if (!IsWindowVisible(q->internalWinId()))
+ sm = SW_SHOWMINNOACTIVE;
+ } else if (q->isMaximized()) {
+ sm = SW_SHOWMAXIMIZED;
+ // Windows will not behave correctly when we try to maximize a window which does not
+ // have minimize nor maximize buttons in the window frame. Windows would then ignore
+ // non-available geometry, and rather maximize the widget to the full screen, minus the
+ // window frame (caption). So, we do a trick here, by adding a maximize button before
+ // maximizing the widget, and then remove the maximize button afterwards.
+ Qt::WindowFlags &flags = data.window_flags;
+ if (flags & Qt::WindowTitleHint &&
+ !(flags & (Qt::WindowMinMaxButtonsHint | Qt::FramelessWindowHint))) {
+ fakedMaximize = TRUE;
+ int style = GetWindowLong(q->internalWinId(), GWL_STYLE);
+ SetWindowLong(q->internalWinId(), GWL_STYLE, style | WS_MAXIMIZEBOX);
+ }
+ }
+ }
+ if (q->testAttribute(Qt::WA_ShowWithoutActivating)
+ || (q->windowType() == Qt::Popup)
+ || (q->windowType() == Qt::ToolTip)
+ || (q->windowType() == Qt::Tool)) {
+ sm = SW_SHOWNOACTIVATE;
+ }
+
+
+ if (q->internalWinId())
+ ShowWindow(q->internalWinId(), sm);
+
+ if (fakedMaximize) {
+ int style = GetWindowLong(q->internalWinId(), GWL_STYLE);
+ SetWindowLong(q->internalWinId(), GWL_STYLE, style & ~WS_MAXIMIZEBOX);
+ SetWindowPos(q->internalWinId(), 0, 0, 0, 0, 0,
+ SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER
+ | SWP_FRAMECHANGED);
+ }
+
+ if (q->internalWinId()) {
+ if (IsIconic(q->internalWinId()))
+ data.window_state |= Qt::WindowMinimized;
+ if (IsZoomed(q->internalWinId()))
+ data.window_state |= Qt::WindowMaximized;
+ // This is to resolve the problem where popups are opened from the
+ // system tray and not being implicitly activated
+ if (q->windowType() == Qt::Popup &&
+ !q->parentWidget() && !qApp->activeWindow())
+ q->activateWindow();
+ }
+
+ winSetupGestures();
+
+ invalidateBuffer(q->rect());
+}
+#endif //Q_WS_WINCE
+
+void QWidgetPrivate::setFocus_sys()
+{
+ Q_Q(QWidget);
+ if (q->testAttribute(Qt::WA_WState_Created) && q->window()->windowType() != Qt::Popup)
+ SetFocus(q->effectiveWinId());
+}
+
+void QWidgetPrivate::raise_sys()
+{
+ Q_Q(QWidget);
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ if (q->internalWinId())
+ SetWindowPos(q->internalWinId(), HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
+}
+
+void QWidgetPrivate::lower_sys()
+{
+ Q_Q(QWidget);
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ if (q->internalWinId())
+ SetWindowPos(q->internalWinId(), HWND_BOTTOM, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
+ invalidateBuffer(q->rect());
+}
+
+void QWidgetPrivate::stackUnder_sys(QWidget* w)
+{
+ Q_Q(QWidget);
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ if (q->internalWinId() && w->internalWinId())
+ SetWindowPos(q->internalWinId(), w->internalWinId() , 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+ invalidateBuffer(q->rect());
+}
+
+
+/*
+ Helper function for non-toplevel widgets. Helps to map Qt's 32bit
+ coordinate system to Windpws's 16bit coordinate system.
+
+ This code is duplicated from the X11 code, so any changes there
+ should also (most likely) be reflected here.
+
+ (In all comments below: s/X/Windows/g)
+ */
+
+void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &)
+{
+ Q_Q(QWidget);
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+
+ /*
+ There are up to four different coordinate systems here:
+ Qt coordinate system for this widget.
+ X coordinate system for this widget (relative to wrect).
+ Qt coordinate system for parent
+ X coordinate system for parent (relative to parent's wrect).
+ */
+ QRect validRange(-XCOORD_MAX,-XCOORD_MAX, 2*XCOORD_MAX, 2*XCOORD_MAX);
+ QRect wrectRange(-WRECT_MAX,-WRECT_MAX, 2*WRECT_MAX, 2*WRECT_MAX);
+ QRect wrect;
+ //xrect is the X geometry of my X widget. (starts out in parent's Qt coord sys, and ends up in parent's X coord sys)
+ QRect xrect = data.crect;
+
+ const QWidget *const parent = q->parentWidget();
+ QRect parentWRect = parent->data->wrect;
+
+ if (parentWRect.isValid()) {
+ // parent is clipped, and we have to clip to the same limit as parent
+ if (!parentWRect.contains(xrect)) {
+ xrect &= parentWRect;
+ wrect = xrect;
+ //translate from parent's to my Qt coord sys
+ wrect.translate(-data.crect.topLeft());
+ }
+ //translate from parent's Qt coords to parent's X coords
+ xrect.translate(-parentWRect.topLeft());
+
+ } else {
+ // parent is not clipped, we may or may not have to clip
+
+ if (data.wrect.isValid() && QRect(QPoint(),data.crect.size()).contains(data.wrect)) {
+ // This is where the main optimization is: we are already
+ // clipped, and if our clip is still valid, we can just
+ // move our window, and do not need to move or clip
+ // children
+
+ QRect vrect = xrect & parent->rect();
+ vrect.translate(-data.crect.topLeft()); //the part of me that's visible through parent, in my Qt coords
+ if (data.wrect.contains(vrect)) {
+ xrect = data.wrect;
+ xrect.translate(data.crect.topLeft());
+ if (q->internalWinId())
+ MoveWindow(q->internalWinId(), xrect.x(), xrect.y(), xrect.width(), xrect.height(), true);
+ return;
+ }
+ }
+
+ if (!validRange.contains(xrect)) {
+ // we are too big, and must clip
+ xrect &=wrectRange;
+ wrect = xrect;
+ wrect.translate(-data.crect.topLeft());
+ //parent's X coord system is equal to parent's Qt coord
+ //sys, so we don't need to map xrect.
+ }
+
+ }
+
+
+ // unmap if we are outside the valid window system coord system
+ bool outsideRange = !xrect.isValid();
+ bool mapWindow = false;
+ if (q->testAttribute(Qt::WA_OutsideWSRange) != outsideRange) {
+ q->setAttribute(Qt::WA_OutsideWSRange, outsideRange);
+ if (outsideRange) {
+ if (q->internalWinId())
+ ShowWindow(q->internalWinId(), SW_HIDE);
+ q->setAttribute(Qt::WA_Mapped, false);
+ } else if (!q->isHidden()) {
+ mapWindow = true;
+ }
+ }
+
+ if (outsideRange)
+ return;
+
+ bool jump = (data.wrect != wrect);
+ data.wrect = wrect;
+
+ // and now recursively for all children...
+ for (int i = 0; i < children.size(); ++i) {
+ QObject *object = children.at(i);
+ if (object->isWidgetType()) {
+ QWidget *w = static_cast<QWidget *>(object);
+ if (!w->isWindow() && w->testAttribute(Qt::WA_WState_Created))
+ w->d_func()->setWSGeometry();
+ }
+ }
+
+ // move ourselves to the new position and map (if necessary) after
+ // the movement. Rationale: moving unmapped windows is much faster
+ // than moving mapped windows
+ if (q->internalWinId()) {
+ if (!parent->internalWinId())
+ xrect.translate(parent->mapTo(q->nativeParentWidget(), QPoint(0, 0)));
+ MoveWindow(q->internalWinId(), xrect.x(), xrect.y(), xrect.width(), xrect.height(), !jump);
+ }
+ if (mapWindow && !dontShow) {
+ q->setAttribute(Qt::WA_Mapped);
+ if (q->internalWinId())
+ ShowWindow(q->internalWinId(), SW_SHOWNOACTIVATE);
+ }
+
+ if (jump && q->internalWinId())
+ InvalidateRect(q->internalWinId(), 0, false);
+
+}
+
+//
+// The internal qWinRequestConfig, defined in qapplication_win.cpp, stores move,
+// resize and setGeometry requests for a widget that is already
+// processing a config event. The purpose is to avoid recursion.
+//
+void qWinRequestConfig(WId, int, int, int, int, int);
+
+void QWidgetPrivate::setGeometry_sys(int x, int y, int w, int h, bool isMove)
+{
+ Q_Q(QWidget);
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ if (extra) { // any size restrictions?
+ w = qMin(w,extra->maxw);
+ h = qMin(h,extra->maxh);
+ w = qMax(w,extra->minw);
+ h = qMax(h,extra->minh);
+ }
+ if (q->isWindow())
+ topData()->normalGeometry = QRect(0, 0, -1, -1);
+
+ QSize oldSize(q->size());
+ QPoint oldPos(q->pos());
+
+ if (!q->isWindow())
+ isMove = (data.crect.topLeft() != QPoint(x, y));
+ bool isResize = w != oldSize.width() || h != oldSize.height();
+
+ if (!isMove && !isResize)
+ return;
+
+ if (isResize && !q->testAttribute(Qt::WA_StaticContents) && q->internalWinId())
+ ValidateRgn(q->internalWinId(), 0);
+
+#ifdef Q_WS_WINCE
+ // On Windows CE we can't just fiddle around with the window state.
+ // Too much magic in setWindowState.
+ if (isResize && q->isMaximized())
+ q->setWindowState(q->windowState() & ~Qt::WindowMaximized);
+#else
+ if (isResize)
+ data.window_state &= ~Qt::WindowMaximized;
+#endif
+
+ if (data.window_state & Qt::WindowFullScreen) {
+ QTLWExtra *top = topData();
+
+ if (q->isWindow()) {
+ // We need to update these flags when we remove the full screen state
+ // or the frame will not be updated
+ UINT style = top->savedFlags;
+ if (q->isVisible())
+ style |= WS_VISIBLE;
+ SetWindowLong(q->internalWinId(), GWL_STYLE, style);
+
+ UINT swpf = SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOSIZE | SWP_NOMOVE;
+ if (data.window_state & Qt::WindowActive)
+ swpf |= SWP_NOACTIVATE;
+ SetWindowPos(q->internalWinId(), 0, 0, 0, 0, 0, swpf);
+ updateFrameStrut();
+ }
+ data.window_state &= ~Qt::WindowFullScreen;
+ topData()->savedFlags = 0;
+ }
+
+ QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData();
+ const bool inTopLevelResize = tlwExtra ? tlwExtra->inTopLevelResize : false;
+ const bool isTranslucentWindow = !isOpaque && ptrUpdateLayeredWindowIndirect && (data.window_flags & Qt::FramelessWindowHint)
+ && GetWindowLong(q->internalWinId(), GWL_EXSTYLE) & Q_WS_EX_LAYERED;
+
+ if (q->testAttribute(Qt::WA_WState_ConfigPending)) { // processing config event
+ if (q->internalWinId())
+ qWinRequestConfig(q->internalWinId(), isMove ? 2 : 1, x, y, w, h);
+ } else {
+ if (!q->testAttribute(Qt::WA_DontShowOnScreen))
+ q->setAttribute(Qt::WA_WState_ConfigPending);
+ if (q->windowType() == Qt::Desktop) {
+ data.crect.setRect(x, y, w, h);
+ } else if (q->isWindow()) {
+ QRect fs(frameStrut());
+ if (extra) {
+ fs.setLeft(x - fs.left());
+ fs.setTop(y - fs.top());
+ fs.setRight((x + w - 1) + fs.right());
+ fs.setBottom((y + h - 1) + fs.bottom());
+ }
+ if (w == 0 || h == 0) {
+ q->setAttribute(Qt::WA_OutsideWSRange, true);
+ if (q->isVisible() && q->testAttribute(Qt::WA_Mapped))
+ hide_sys();
+ data.crect = QRect(x, y, w, h);
+ } else if (q->isVisible() && q->testAttribute(Qt::WA_OutsideWSRange)) {
+ q->setAttribute(Qt::WA_OutsideWSRange, false);
+
+ // put the window in its place and show it
+ MoveWindow(q->internalWinId(), fs.x(), fs.y(), fs.width(), fs.height(), true);
+ RECT rect;
+ if (!q->testAttribute(Qt::WA_DontShowOnScreen)) {
+ GetClientRect(q->internalWinId(), &rect);
+ data.crect.setRect(x, y, rect.right - rect.left, rect.bottom - rect.top);
+ } else {
+ data.crect.setRect(x, y, w, h);
+ }
+
+ show_sys();
+ } else if (!q->testAttribute(Qt::WA_DontShowOnScreen)) {
+ q->setAttribute(Qt::WA_OutsideWSRange, false);
+#ifndef Q_WS_WINCE
+ // If the window is hidden and in maximized state or minimized, instead of moving the
+ // window, set the normal position of the window.
+ WINDOWPLACEMENT wndpl;
+ GetWindowPlacement(q->internalWinId(), &wndpl);
+ if ((wndpl.showCmd == SW_MAXIMIZE && !IsWindowVisible(q->internalWinId())) || wndpl.showCmd == SW_SHOWMINIMIZED) {
+ RECT normal = {fs.x(), fs.y(), fs.x()+fs.width(), fs.y()+fs.height()};
+ wndpl.rcNormalPosition = normal;
+ wndpl.showCmd = wndpl.showCmd == SW_SHOWMINIMIZED ? SW_SHOWMINIMIZED : SW_HIDE;
+ SetWindowPlacement(q->internalWinId(), &wndpl);
+ } else {
+#else
+ if (data.window_state & Qt::WindowMaximized) {
+ qt_wince_maximize(q);
+ } else {
+#endif
+ MoveWindow(q->internalWinId(), fs.x(), fs.y(), fs.width(), fs.height(), true);
+ }
+ if (!q->isVisible())
+ InvalidateRect(q->internalWinId(), 0, FALSE);
+ RECT rect;
+ // If the layout has heightForWidth, the MoveWindow() above can
+ // change the size/position, so refresh them.
+
+ if (isTranslucentWindow) {
+ data.crect.setRect(x, y, w, h);
+ } else {
+ GetClientRect(q->internalWinId(), &rect);
+ RECT rcNormalPosition ={0};
+ // Use (0,0) as window position for embedded ActiveQt controls.
+ if (!tlwExtra || !tlwExtra->embedded)
+ GetWindowRect(q->internalWinId(), &rcNormalPosition);
+ QRect fStrut(frameStrut());
+ data.crect.setRect(rcNormalPosition.left + fStrut.left(),
+ rcNormalPosition.top + fStrut.top(),
+ rect.right - rect.left,
+ rect.bottom - rect.top);
+ isResize = data.crect.size() != oldSize;
+ }
+ } else {
+ q->setAttribute(Qt::WA_OutsideWSRange, false);
+ data.crect.setRect(x, y, w, h);
+ }
+ } else {
+ QRect oldGeom(data.crect);
+ data.crect.setRect(x, y, w, h);
+ if (q->isVisible() && (!inTopLevelResize || q->internalWinId())) {
+ // Top-level resize optimization does not work for native child widgets;
+ // disable it for this particular widget.
+ if (inTopLevelResize)
+ tlwExtra->inTopLevelResize = false;
+
+ if (!isResize)
+ moveRect(QRect(oldPos, oldSize), x - oldPos.x(), y - oldPos.y());
+ else
+ invalidateBuffer_resizeHelper(oldPos, oldSize);
+
+ if (inTopLevelResize)
+ tlwExtra->inTopLevelResize = true;
+ }
+ if (q->testAttribute(Qt::WA_WState_Created))
+ setWSGeometry();
+ }
+ q->setAttribute(Qt::WA_WState_ConfigPending, false);
+ }
+
+ if (q->isWindow() && q->isVisible() && isResize && !inTopLevelResize) {
+ invalidateBuffer(q->rect()); //after the resize
+ }
+
+ // Process events immediately rather than in translateConfigEvent to
+ // avoid windows message process delay.
+ if (q->isVisible()) {
+ if (isMove && q->pos() != oldPos) {
+ QMoveEvent e(q->pos(), oldPos);
+ QApplication::sendEvent(q, &e);
+ }
+ if (isResize) {
+ static bool slowResize = qgetenv("QT_SLOW_TOPLEVEL_RESIZE").toInt();
+ // If we have a backing store with static contents, we have to disable the top-level
+ // resize optimization in order to get invalidated regions for resized widgets.
+ // The optimization discards all invalidateBuffer() calls since we're going to
+ // repaint everything anyways, but that's not the case with static contents.
+ const bool setTopLevelResize = !slowResize && q->isWindow() && extra && extra->topextra
+ && !extra->topextra->inTopLevelResize
+ && (!extra->topextra->backingStore
+ || !extra->topextra->backingStore->hasStaticContents());
+ if (setTopLevelResize)
+ extra->topextra->inTopLevelResize = true;
+ QResizeEvent e(q->size(), oldSize);
+ QApplication::sendEvent(q, &e);
+ if (setTopLevelResize)
+ extra->topextra->inTopLevelResize = false;
+ }
+ } else {
+ if (isMove && q->pos() != oldPos)
+ q->setAttribute(Qt::WA_PendingMoveEvent, true);
+ if (isResize)
+ q->setAttribute(Qt::WA_PendingResizeEvent, true);
+ }
+}
+
+bool QWidgetPrivate::shouldShowMaximizeButton()
+{
+ if (data.window_flags & Qt::MSWindowsFixedSizeDialogHint)
+ return false;
+ // if the user explicitly asked for the maximize button, we try to add
+ // it even if the window has fixed size.
+ if (data.window_flags & Qt::CustomizeWindowHint &&
+ data.window_flags & Qt::WindowMaximizeButtonHint)
+ return true;
+ if (extra) {
+ if ((extra->maxw && extra->maxw != QWIDGETSIZE_MAX && extra->maxw != QLAYOUTSIZE_MAX)
+ || (extra->maxh && extra->maxh != QWIDGETSIZE_MAX && extra->maxh != QLAYOUTSIZE_MAX))
+ return false;
+ }
+ return data.window_flags & Qt::WindowMaximizeButtonHint;
+}
+
+void QWidgetPrivate::winUpdateIsOpaque()
+{
+#ifndef Q_WS_WINCE
+ Q_Q(QWidget);
+
+ if (!q->isWindow() || !q->testAttribute(Qt::WA_TranslucentBackground))
+ return;
+
+ if ((data.window_flags & Qt::FramelessWindowHint) == 0)
+ return;
+
+ if (!isOpaque && ptrUpdateLayeredWindowIndirect) {
+ SetWindowLong(q->internalWinId(), GWL_EXSTYLE,
+ GetWindowLong(q->internalWinId(), GWL_EXSTYLE) | Q_WS_EX_LAYERED);
+ } else {
+ SetWindowLong(q->internalWinId(), GWL_EXSTYLE,
+ GetWindowLong(q->internalWinId(), GWL_EXSTYLE) & ~Q_WS_EX_LAYERED);
+ }
+#endif
+}
+
+void QWidgetPrivate::setConstraints_sys()
+{
+#ifndef Q_WS_WINCE_WM
+ Q_Q(QWidget);
+ if (q->isWindow() && q->testAttribute(Qt::WA_WState_Created)) {
+ int style = GetWindowLong(q->internalWinId(), GWL_STYLE);
+ if (shouldShowMaximizeButton())
+ style |= WS_MAXIMIZEBOX;
+ else
+ style &= ~WS_MAXIMIZEBOX;
+ SetWindowLong(q->internalWinId(), GWL_STYLE, style);
+ }
+#endif
+}
+
+void QWidgetPrivate::scroll_sys(int dx, int dy)
+{
+ Q_Q(QWidget);
+ scrollChildren(dx, dy);
+
+ if (!paintOnScreen()) {
+ scrollRect(q->rect(), dx, dy);
+ } else {
+ UINT flags = SW_INVALIDATE;
+ if (!q->testAttribute(Qt::WA_OpaquePaintEvent))
+ flags |= SW_ERASE;
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ ScrollWindowEx(q->internalWinId(), dx, dy, 0, 0, 0, 0, flags);
+ UpdateWindow(q->internalWinId());
+ }
+}
+
+void QWidgetPrivate::scroll_sys(int dx, int dy, const QRect &r)
+{
+ Q_Q(QWidget);
+
+ if (!paintOnScreen()) {
+ scrollRect(r, dx, dy);
+ } else {
+ RECT wr;
+ wr.top = r.top();
+ wr.left = r.left();
+ wr.bottom = r.bottom()+1;
+ wr.right = r.right()+1;
+
+ UINT flags = SW_INVALIDATE;
+ if (!q->testAttribute(Qt::WA_OpaquePaintEvent))
+ flags |= SW_ERASE;
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ ScrollWindowEx(q->internalWinId(), dx, dy, &wr, &wr, 0, 0, flags);
+ UpdateWindow(q->internalWinId());
+ }
+}
+
+int QWidget::metric(PaintDeviceMetric m) const
+{
+ Q_D(const QWidget);
+ int val;
+ if (m == PdmWidth) {
+ val = data->crect.width();
+ } else if (m == PdmHeight) {
+ val = data->crect.height();
+ } else {
+ HDC gdc = GetDC(0);
+ switch (m) {
+ case PdmDpiX:
+ case PdmPhysicalDpiX:
+ if (d->extra && d->extra->customDpiX)
+ val = d->extra->customDpiX;
+ else if (d->parent)
+ val = static_cast<QWidget *>(d->parent)->metric(m);
+ else
+ val = GetDeviceCaps(gdc, LOGPIXELSX);
+ break;
+ case PdmDpiY:
+ case PdmPhysicalDpiY:
+ if (d->extra && d->extra->customDpiY)
+ val = d->extra->customDpiY;
+ else if (d->parent)
+ val = static_cast<QWidget *>(d->parent)->metric(m);
+ else
+ val = GetDeviceCaps(gdc, LOGPIXELSY);
+ break;
+ case PdmWidthMM:
+ val = data->crect.width()
+ * GetDeviceCaps(gdc, HORZSIZE)
+ / GetDeviceCaps(gdc, HORZRES);
+ break;
+ case PdmHeightMM:
+ val = data->crect.height()
+ * GetDeviceCaps(gdc, VERTSIZE)
+ / GetDeviceCaps(gdc, VERTRES);
+ break;
+ case PdmNumColors:
+ if (GetDeviceCaps(gdc, RASTERCAPS) & RC_PALETTE)
+ val = GetDeviceCaps(gdc, SIZEPALETTE);
+ else {
+ HDC hd = d->hd ? HDC(d->hd) : gdc;
+ int bpp = GetDeviceCaps(hd, BITSPIXEL);
+ if (bpp == 32)
+ val = INT_MAX; // ### this is bogus, it should be 2^24 colors for 32 bit as well
+ else if(bpp<=8)
+ val = GetDeviceCaps(hd, NUMCOLORS);
+ else
+ val = 1 << (bpp * GetDeviceCaps(hd, PLANES));
+ }
+ break;
+ case PdmDepth:
+ val = GetDeviceCaps(gdc, BITSPIXEL);
+ break;
+ default:
+ val = 0;
+ qWarning("QWidget::metric: Invalid metric command");
+ }
+ ReleaseDC(0, gdc);
+ }
+ return val;
+}
+
+void QWidgetPrivate::createSysExtra()
+{
+#ifndef QT_NO_DRAGANDDROP
+ extra->dropTarget = 0;
+#endif
+}
+
+#ifndef Q_WS_WINCE
+void QWidgetPrivate::deleteSysExtra()
+{
+}
+#endif //Q_WS_WINCE
+
+void QWidgetPrivate::createTLSysExtra()
+{
+ extra->topextra->hotkeyRegistered = 0;
+ extra->topextra->savedFlags = 0;
+ extra->topextra->winIconBig = 0;
+ extra->topextra->winIconSmall = 0;
+}
+
+void QWidgetPrivate::deleteTLSysExtra()
+{
+ if (extra->topextra->winIconSmall)
+ DestroyIcon(extra->topextra->winIconSmall);
+ if (extra->topextra->winIconBig)
+ DestroyIcon(extra->topextra->winIconBig);
+}
+
+void QWidgetPrivate::registerDropSite(bool on)
+{
+ Q_Q(QWidget);
+ if (!q->testAttribute(Qt::WA_WState_Created))
+ return;
+ // Enablement is defined by d->extra->dropTarget != 0.
+ if (on) {
+ // Turn on.
+ createExtra();
+#ifndef QT_NO_DRAGANDDROP
+ if (!q->internalWinId())
+ q->nativeParentWidget()->d_func()->createExtra();
+ QWExtra *extra = extraData();
+ if (!extra->dropTarget)
+ extra->dropTarget = registerOleDnd(q);
+#endif
+ } else {
+ // Turn off.
+ QWExtra *extra = extraData();
+#ifndef QT_NO_DRAGANDDROP
+ if (extra && extra->dropTarget) {
+ unregisterOleDnd(q, extra->dropTarget);
+ extra->dropTarget = 0;
+ }
+#endif
+ }
+}
+
+#ifndef QT_NO_DRAGANDDROP
+QOleDropTarget* QWidgetPrivate::registerOleDnd(QWidget *widget)
+{
+ QOleDropTarget *dropTarget = new QOleDropTarget(widget);
+ Q_ASSERT(widget->testAttribute(Qt::WA_WState_Created));
+ if (!widget->internalWinId()) {
+ QWidget *nativeParent = widget->nativeParentWidget();
+ Q_ASSERT(nativeParent);
+ QWExtra *nativeExtra = nativeParent->d_func()->extra;
+ Q_ASSERT(nativeExtra);
+ if (!nativeParent->acceptDrops())
+ nativeParent->setAcceptDrops(true);
+ if (!nativeExtra->oleDropWidgets.contains(widget))
+ nativeExtra->oleDropWidgets.append(widget);
+ if (!nativeExtra->dropTarget) {
+ nativeExtra->dropTarget = registerOleDnd(nativeParent);
+ Q_ASSERT(nativeExtra->dropTarget);
+#ifndef Q_OS_WINCE
+ CoLockObjectExternal(nativeExtra->dropTarget, false, true);
+#endif
+ RegisterDragDrop(nativeParent->internalWinId(), nativeExtra->dropTarget);
+ }
+ } else {
+ RegisterDragDrop(widget->internalWinId(), dropTarget);
+#ifndef Q_OS_WINCE
+ CoLockObjectExternal(dropTarget, true, true);
+#endif
+ }
+ return dropTarget;
+}
+
+void QWidgetPrivate::unregisterOleDnd(QWidget *widget, QOleDropTarget *dropTarget)
+{
+ dropTarget->releaseQt();
+ dropTarget->Release();
+ Q_ASSERT(widget->testAttribute(Qt::WA_WState_Created));
+ if (!widget->internalWinId()) {
+ QWidget *nativeParent = widget->nativeParentWidget();
+ Q_ASSERT(nativeParent);
+ QWExtra *nativeExtra = nativeParent->d_func()->extra;
+ Q_ASSERT(nativeExtra);
+ nativeExtra->oleDropWidgets.removeAll(widget);
+ nativeExtra->oleDropWidgets.removeAll(static_cast<QWidget *>(0));
+ if (nativeExtra->oleDropWidgets.isEmpty() && nativeExtra->dropTarget
+ && !nativeParent->testAttribute(Qt::WA_DropSiteRegistered)) {
+#ifndef Q_OS_WINCE
+ CoLockObjectExternal(nativeExtra->dropTarget, false, true);
+#endif
+ RevokeDragDrop(nativeParent->internalWinId());
+ nativeExtra->dropTarget = 0;
+ }
+ } else {
+#ifndef Q_OS_WINCE
+ CoLockObjectExternal(dropTarget, false, true);
+#endif
+ RevokeDragDrop(widget->internalWinId());
+ }
+}
+
+#endif //QT_NO_DRAGANDDROP
+
+// from qregion_win.cpp
+extern HRGN qt_tryCreateRegion(QRegion::RegionType type, int left, int top, int right, int bottom);
+void QWidgetPrivate::setMask_sys(const QRegion &region)
+{
+ Q_Q(QWidget);
+ if (!q->internalWinId())
+ return;
+
+ if (region.isEmpty()) {
+ SetWindowRgn(q->internalWinId(), 0, true);
+ return;
+ }
+
+ // Since SetWindowRegion takes ownership, and we need to translate,
+ // we take a copy.
+ HRGN wr = qt_tryCreateRegion(QRegion::Rectangle, 0,0,0,0);
+ CombineRgn(wr, region.handle(), 0, RGN_COPY);
+
+ QPoint offset = (q->isWindow()
+ ? frameStrut().topLeft()
+ : QPoint(0, 0));
+ OffsetRgn(wr, offset.x(), offset.y());
+
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ if (!SetWindowRgn(data.winid, wr, true))
+ DeleteObject(wr);
+}
+
+void QWidgetPrivate::updateFrameStrut()
+{
+ Q_Q(QWidget);
+
+ if (!q->testAttribute(Qt::WA_WState_Created))
+ return;
+
+ if (!q->internalWinId()) {
+ data.fstrut_dirty = false;
+ return;
+ }
+
+ RECT rect = {0,0,0,0};
+
+ QTLWExtra *top = topData();
+ uint exstyle = GetWindowLong(q->internalWinId(), GWL_EXSTYLE);
+ uint style = GetWindowLong(q->internalWinId(), GWL_STYLE);
+#ifndef Q_WS_WINCE
+ if (AdjustWindowRectEx(&rect, style & ~(WS_OVERLAPPED), FALSE, exstyle)) {
+#else
+ if (AdjustWindowRectEx(&rect, style, FALSE, exstyle)) {
+#endif
+ top->frameStrut.setCoords(-rect.left, -rect.top, rect.right, rect.bottom);
+ data.fstrut_dirty = false;
+ }
+}
+
+#ifndef Q_WS_WINCE
+void QWidgetPrivate::setWindowOpacity_sys(qreal level)
+{
+ Q_Q(QWidget);
+
+ if (!isOpaque && ptrUpdateLayeredWindow && (data.window_flags & Qt::FramelessWindowHint)) {
+ if (GetWindowLong(q->internalWinId(), GWL_EXSTYLE) & Q_WS_EX_LAYERED) {
+ BLENDFUNCTION blend = {AC_SRC_OVER, 0, (int)(255.0 * level), AC_SRC_ALPHA};
+ ptrUpdateLayeredWindow(q->internalWinId(), NULL, NULL, NULL, NULL, NULL, 0, &blend, Q_ULW_ALPHA);
+ }
+ return;
+ }
+
+ static bool function_resolved = false;
+ if (!function_resolved) {
+ ptrSetLayeredWindowAttributes =
+ (PtrSetLayeredWindowAttributes) QSystemLibrary::resolve(QLatin1String("user32"),
+ "SetLayeredWindowAttributes");
+ function_resolved = true;
+ }
+
+ if (!ptrSetLayeredWindowAttributes)
+ return;
+
+ int wl = GetWindowLong(q->internalWinId(), GWL_EXSTYLE);
+
+ if (level != 1.0) {
+ if ((wl&Q_WS_EX_LAYERED) == 0)
+ SetWindowLong(q->internalWinId(), GWL_EXSTYLE, wl | Q_WS_EX_LAYERED);
+ } else if (wl&Q_WS_EX_LAYERED) {
+ SetWindowLong(q->internalWinId(), GWL_EXSTYLE, wl & ~Q_WS_EX_LAYERED);
+ }
+ ptrSetLayeredWindowAttributes(q->internalWinId(), 0, (int)(level * 255), Q_LWA_ALPHA);
+}
+#endif //Q_WS_WINCE
+
+// class QGlobalRasterPaintEngine: public QRasterPaintEngine
+// {
+// public:
+// inline QGlobalRasterPaintEngine() : QRasterPaintEngine() { setFlushOnEnd(false); }
+// };
+// Q_GLOBAL_STATIC(QGlobalRasterPaintEngine, globalRasterPaintEngine)
+
+
+#ifndef QT_NO_DIRECTDRAW
+static uchar *qt_primary_surface_bits;
+static int qt_primary_surface_stride;
+static QImage::Format qt_primary_surface_format;
+
+void qt_win_initialize_directdraw()
+{
+ HRESULT res;
+
+ // Some initialization...
+ if (!qt_ddraw_object) {
+ res = DirectDrawCreate(0, &qt_ddraw_object, 0);
+
+ if (res != DD_OK)
+ qWarning("DirectDrawCreate failed: %d", res);
+
+ qt_ddraw_object->SetCooperativeLevel(0, DDSCL_NORMAL);
+
+ DDSURFACEDESC surfaceDesc;
+ memset(&surfaceDesc, 0, sizeof(DDSURFACEDESC));
+
+ surfaceDesc.dwSize = sizeof(DDSURFACEDESC);
+ surfaceDesc.dwFlags = DDSD_CAPS;
+ surfaceDesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
+
+ res = qt_ddraw_object->CreateSurface(&surfaceDesc, &qt_ddraw_primary, 0);
+ if (res != DD_OK)
+ qWarning("CreateSurface failed: %d", res);
+
+ memset(&surfaceDesc, 0, sizeof(DDSURFACEDESC));
+ surfaceDesc.dwSize = sizeof(DDSURFACEDESC);
+ res = qt_ddraw_primary->Lock(0, &surfaceDesc, DDLOCK_WAIT | DDLOCK_SURFACEMEMORYPTR, 0);
+ if (res != DD_OK)
+ qWarning("Locking surface failed: %d", res);
+
+ if (surfaceDesc.ddpfPixelFormat.dwFlags == DDPF_RGB) {
+ qt_primary_surface_bits = (uchar *) surfaceDesc.lpSurface;
+ qt_primary_surface_stride = surfaceDesc.lPitch;
+ qt_primary_surface_format = QImage::Format_RGB32;
+ } else {
+ qWarning("QWidget painting: unsupported device depth for onscreen painting...\n");
+ }
+
+ qt_ddraw_primary->Unlock(0);
+ }
+}
+
+class QOnScreenRasterPaintEngine : public QRasterPaintEngine
+{
+public:
+ // The image allocated here leaks... Fix if this code is ifdef'ed
+ // in
+ QOnScreenRasterPaintEngine()
+ : QRasterPaintEngine(new QImage(qt_primary_surface_bits,
+ QApplication::desktop()->width(),
+ QApplication::desktop()->height(),
+ qt_primary_surface_stride,
+ qt_primary_surface_format))
+ {
+ device = static_cast<QImage *>(d_func()->device);
+ }
+
+ bool begin(QPaintDevice *)
+ {
+ QRegion clip = systemClip();
+ originalSystemClip = clip;
+ clip.translate(widget->mapToGlobal(QPoint(0, 0)));
+ setSystemClip(clip);
+
+ QRect bounds = clip.boundingRect();
+ DDSURFACEDESC surface;
+ surface.dwSize = sizeof(DDSURFACEDESC);
+ HRESULT res = qt_ddraw_primary->Lock((RECT *) &bounds, &surface, DDLOCK_WAIT, 0);
+ if (res != DD_OK) {
+ qWarning("QWidget painting: locking onscreen bits failed: %d\n", res);
+ return false;
+ }
+
+ if (surface.lpSurface == qt_primary_surface_bits) {
+ qt_primary_surface_bits = (uchar *) surface.lpSurface;
+ device->data_ptr()->data = qt_primary_surface_bits;
+ }
+
+ return QRasterPaintEngine::begin(device);
+ }
+
+ bool end()
+ {
+ HRESULT res = qt_ddraw_primary->Unlock(0);
+ if (res != DD_OK)
+ qWarning("QWidget::paint, failed to unlock DirectDraw surface: %d", res);
+ bool ok = QRasterPaintEngine::end();
+ setSystemClip(originalSystemClip);
+ return ok;
+ }
+
+ QPoint coordinateOffset() const {
+ return -widget->mapToGlobal(QPoint(0, 0));
+ }
+
+ const QWidget *widget;
+ QImage *device;
+ QRegion originalSystemClip;
+};
+Q_GLOBAL_STATIC(QOnScreenRasterPaintEngine, onScreenPaintEngine)
+#else
+void qt_win_initialize_directdraw() { }
+#endif
+
+QPaintEngine *QWidget::paintEngine() const
+{
+#ifndef QT_NO_DIRECTDRAW
+ QOnScreenRasterPaintEngine *pe = onScreenPaintEngine();
+ pe->widget = this;
+ return pe;
+#endif
+
+ // We set this bit which is checked in setAttribute for
+ // Qt::WA_PaintOnScreen. We do this to allow these two scenarios:
+ //
+ // 1. Users accidentally set Qt::WA_PaintOnScreen on X and port to
+ // windows which would mean suddenly their widgets stop working.
+ //
+ // 2. Users set paint on screen and subclass paintEngine() to
+ // return 0, in which case we have a "hole" in the backingstore
+ // allowing use of GDI or DirectX directly.
+ //
+ // 1 is WRONG, but to minimize silent failures, we have set this
+ // bit to ignore the setAttribute call. 2. needs to be
+ // supported because its our only means of embeddeding native
+ // graphics stuff.
+ const_cast<QWidgetPrivate *>(d_func())->noPaintOnScreen = 1;
+
+ return 0;
+}
+
+QWindowSurface *QWidgetPrivate::createDefaultWindowSurface_sys()
+{
+ Q_Q(QWidget);
+ return new QRasterWindowSurface(q);
+}
+
+void QWidgetPrivate::setModal_sys()
+{
+}
+
+void QWidgetPrivate::registerTouchWindow()
+{
+ Q_Q(QWidget);
+
+ // enable WM_TOUCH* messages on our window
+ if (q->testAttribute(Qt::WA_WState_Created)
+ && QApplicationPrivate::RegisterTouchWindow
+ && q->windowType() != Qt::Desktop)
+ QApplicationPrivate::RegisterTouchWindow(q->effectiveWinId(), 0);
+}
+
+void QWidgetPrivate::winSetupGestures()
+{
+#if !defined(QT_NO_GESTURES) && !defined(QT_NO_NATIVE_GESTURES)
+ Q_Q(QWidget);
+ if (!q || !q->isVisible() || !nativeGesturePanEnabled)
+ return;
+
+ if (!QApplicationPrivate::HasTouchSupport)
+ return;
+ QApplicationPrivate *qAppPriv = QApplicationPrivate::instance();
+ if (!qAppPriv->SetGestureConfig)
+ return;
+ WId winid = q->internalWinId();
+
+ bool needh = false;
+ bool needv = false;
+ bool singleFingerPanEnabled = false;
+
+#ifndef QT_NO_SCROLLAREA
+ if (QAbstractScrollArea *asa = qobject_cast<QAbstractScrollArea*>(q->parent())) {
+ QScrollBar *hbar = asa->horizontalScrollBar();
+ QScrollBar *vbar = asa->verticalScrollBar();
+ Qt::ScrollBarPolicy hbarpolicy = asa->horizontalScrollBarPolicy();
+ Qt::ScrollBarPolicy vbarpolicy = asa->verticalScrollBarPolicy();
+ needh = (hbarpolicy == Qt::ScrollBarAlwaysOn ||
+ (hbarpolicy == Qt::ScrollBarAsNeeded && hbar->minimum() < hbar->maximum()));
+ needv = (vbarpolicy == Qt::ScrollBarAlwaysOn ||
+ (vbarpolicy == Qt::ScrollBarAsNeeded && vbar->minimum() < vbar->maximum()));
+ singleFingerPanEnabled = asa->d_func()->singleFingerPanEnabled;
+ if (!winid) {
+ winid = q->winId(); // enforces the native winid on the viewport
+ }
+ }
+#endif //QT_NO_SCROLLAREA
+ if (winid) {
+ GESTURECONFIG gc[1];
+ memset(gc, 0, sizeof(gc));
+ gc[0].dwID = GID_PAN;
+ if (nativeGesturePanEnabled) {
+ gc[0].dwWant = GC_PAN;
+ if (needv && singleFingerPanEnabled)
+ gc[0].dwWant |= GC_PAN_WITH_SINGLE_FINGER_VERTICALLY;
+ else
+ gc[0].dwBlock |= GC_PAN_WITH_SINGLE_FINGER_VERTICALLY;
+ if (needh && singleFingerPanEnabled)
+ gc[0].dwWant |= GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY;
+ else
+ gc[0].dwBlock |= GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY;
+ } else {
+ gc[0].dwBlock = GC_PAN;
+ }
+
+ qAppPriv->SetGestureConfig(winid, 0, sizeof(gc)/sizeof(gc[0]), gc, sizeof(gc[0]));
+ }
+#endif
+}
+
+QT_END_NAMESPACE
+
+#ifdef Q_WS_WINCE
+# include "qwidget_wince.cpp"
+#endif
diff --git a/src/widgets/platforms/win/qwidget_wince.cpp b/src/widgets/platforms/win/qwidget_wince.cpp
new file mode 100644
index 0000000000..b16be300e7
--- /dev/null
+++ b/src/widgets/platforms/win/qwidget_wince.cpp
@@ -0,0 +1,675 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifdef Q_WS_WINCE
+
+#include "qguifunctions_wince.h"
+
+QT_BEGIN_NAMESPACE
+
+const QString qt_reg_winclass(QWidget *w); // defined in qapplication_win.cpp
+extern "C" LRESULT QT_WIN_CALLBACK QtWndProc(HWND, UINT, WPARAM, LPARAM);
+
+//#define TABLET_DEBUG
+#define PACKETDATA (PK_X | PK_Y | PK_BUTTONS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE \
+ | PK_ORIENTATION | PK_CURSOR | PK_Z)
+#define PACKETMODE 0
+
+typedef HCTX (API *PtrWTOpen)(HWND, LPLOGCONTEXT, BOOL);
+typedef BOOL (API *PtrWTClose)(HCTX);
+typedef UINT (API *PtrWTInfo)(UINT, UINT, LPVOID);
+typedef BOOL (API *PtrWTEnable)(HCTX, BOOL);
+typedef BOOL (API *PtrWTOverlap)(HCTX, BOOL);
+typedef int (API *PtrWTPacketsGet)(HCTX, int, LPVOID);
+typedef BOOL (API *PtrWTGet)(HCTX, LPLOGCONTEXT);
+typedef int (API *PtrWTQueueSizeGet)(HCTX);
+typedef BOOL (API *PtrWTQueueSizeSet)(HCTX, int);
+
+#ifndef QT_NO_TABLETEVENT
+static void qt_tablet_init_wce();
+static void qt_tablet_cleanup_wce();
+
+static void qt_tablet_init_wce() {
+ static bool firstTime = true;
+ if (!firstTime)
+ return;
+ firstTime = false;
+ qt_tablet_widget = new QWidget(0);
+ qt_tablet_widget->createWinId();
+ qt_tablet_widget->setObjectName(QLatin1String("Qt internal tablet widget"));
+ LOGCONTEXT lcMine;
+ qAddPostRoutine(qt_tablet_cleanup_wce);
+ struct tagAXIS tpOri[3];
+ if (ptrWTInfo && ptrWTOpen && ptrWTQueueSizeGet && ptrWTQueueSizeSet) {
+ // make sure we have WinTab
+ if (!ptrWTInfo(0, 0, NULL)) {
+#ifdef TABLET_DEBUG
+ qWarning("QWidget: Wintab services not available");
+#endif
+ return;
+ }
+
+ // some tablets don't support tilt, check if it is possible,
+ qt_tablet_tilt_support = ptrWTInfo(WTI_DEVICES, DVC_ORIENTATION, &tpOri);
+ if (qt_tablet_tilt_support) {
+ // check for azimuth and altitude
+ qt_tablet_tilt_support = tpOri[0].axResolution && tpOri[1].axResolution;
+ }
+ // build our context from the default context
+ ptrWTInfo(WTI_DEFSYSCTX, 0, &lcMine);
+ // Go for the raw coordinates, the tablet event will return good stuff
+ lcMine.lcOptions |= CXO_MESSAGES | CXO_CSRMESSAGES;
+ lcMine.lcPktData = PACKETDATA;
+ lcMine.lcPktMode = PACKETMODE;
+ lcMine.lcMoveMask = PACKETDATA;
+ lcMine.lcOutOrgX = 0;
+ lcMine.lcOutExtX = lcMine.lcInExtX;
+ lcMine.lcOutOrgY = 0;
+ lcMine.lcOutExtY = -lcMine.lcInExtY;
+ qt_tablet_context = ptrWTOpen(qt_tablet_widget->winId(), &lcMine, true);
+#ifdef TABLET_DEBUG
+ qDebug("Tablet is %p", qt_tablet_context);
+#endif
+ if (!qt_tablet_context) {
+#ifdef TABLET_DEBUG
+ qWarning("QWidget: Failed to open the tablet");
+#endif
+ return;
+ }
+ // Set the size of the Packet Queue to the correct size...
+ int currSize = ptrWTQueueSizeGet(qt_tablet_context);
+ if (!ptrWTQueueSizeSet(qt_tablet_context, QT_TABLET_NPACKETQSIZE)) {
+ // Ideally one might want to use a smaller
+ // multiple, but for now, since we managed to destroy
+ // the existing Q with the previous call, set it back
+ // to the other size, which should work. If not,
+ // there will be trouble.
+ if (!ptrWTQueueSizeSet(qt_tablet_context, currSize)) {
+ Q_ASSERT_X(0, "Qt::Internal", "There is no packet queue for"
+ " the tablet. The tablet will not work");
+ }
+ }
+ }
+}
+
+static void qt_tablet_cleanup_wce() {
+ if (ptrWTClose)
+ ptrWTClose(qt_tablet_context);
+ delete qt_tablet_widget;
+ qt_tablet_widget = 0;
+}
+#endif // QT_NO_TABLETEVENT
+
+
+// The internal qWinRequestConfig, defined in qapplication_win.cpp, stores move,
+// resize and setGeometry requests for a widget that is already
+// processing a config event. The purpose is to avoid recursion.
+//
+void qWinRequestConfig(WId, int, int, int, int, int);
+
+void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyOldWindow) {
+ Q_Q(QWidget);
+ static int sw = -1, sh = -1;
+
+ Qt::WindowType type = q->windowType();
+ Qt::WindowFlags flags = data.window_flags;
+
+ bool topLevel = (flags & Qt::Window);
+ bool popup = (type == Qt::Popup);
+ bool dialog = (type == Qt::Dialog
+ || type == Qt::Sheet
+ || (flags & Qt::MSWindowsFixedSizeDialogHint));
+ bool desktop = (type == Qt::Desktop);
+ bool tool = (type == Qt::Tool || type == Qt::Drawer);
+
+ HINSTANCE appinst = qWinAppInst();
+ HWND parentw, destroyw = 0;
+ WId id;
+
+ QString windowClassName = qt_reg_winclass(q);
+
+ if (!window) // always initialize
+ initializeWindow = true;
+
+ if (popup)
+ flags |= Qt::WindowStaysOnTopHint; // a popup stays on top
+
+ if (flags & (Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint | Qt::WindowContextHelpButtonHint)) {
+ flags |= Qt::WindowSystemMenuHint;
+ flags |= Qt::WindowTitleHint;
+ flags &= ~Qt::FramelessWindowHint;
+ }
+
+ if (sw < 0) { // get the (primary) screen size
+ sw = GetSystemMetrics(SM_CXSCREEN);
+ sh = GetSystemMetrics(SM_CYSCREEN);
+ }
+
+ if (desktop) { // desktop widget
+ popup = false; // force this flags off
+ data.crect.setRect(0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
+ }
+
+ parentw = q->parentWidget() ? q->parentWidget()->effectiveWinId() : 0;
+
+ QString title;
+ int style = WS_CHILD;
+ int exsty = WS_EX_NOPARENTNOTIFY;
+
+ if (topLevel) {
+ if (!(flags & Qt::FramelessWindowHint) && !tool && !q->testAttribute(Qt::WA_DontShowOnScreen))
+ style = (WS_OVERLAPPED) | WS_SYSMENU;
+ else
+ style = WS_POPUP;
+ if ((type == Qt::ToolTip) || (type == Qt::SplashScreen)) {
+ style = WS_POPUP;
+ exsty |= WS_EX_NOANIMATION;
+ } else {
+ if (flags & Qt::WindowTitleHint)
+ style |= WS_CAPTION;
+ if (flags & Qt::WindowSystemMenuHint)
+ style |= WS_SYSMENU;
+ if (flags & Qt::WindowContextHelpButtonHint)
+ exsty |= WS_EX_CONTEXTHELP;
+#ifndef Q_WS_WINCE_WM
+ if (flags & Qt::WindowMinimizeButtonHint)
+ style |= WS_MINIMIZEBOX;
+ if (shouldShowMaximizeButton())
+ style |= WS_MAXIMIZEBOX;
+#endif
+ if (tool)
+ exsty |= WS_EX_TOOLWINDOW;
+ }
+ }
+ if (dialog) {
+ style = WS_BORDER | WS_CAPTION;
+ if (flags & Qt::WindowOkButtonHint)
+ exsty |= WS_EX_CAPTIONOKBTN;
+ if (flags & Qt::WindowCancelButtonHint || flags & Qt::WA_DeleteOnClose)
+ style |= WS_SYSMENU;
+ if (flags & Qt::WindowContextHelpButtonHint)
+ exsty |= WS_EX_CONTEXTHELP;
+ }
+ if (popup) {
+ style = WS_POPUP;
+ exsty |= WS_EX_NOANIMATION;
+ }
+
+ if (flags & Qt::WindowTitleHint) {
+ title = q->isWindow() ? qAppName() : q->objectName();
+ }
+
+ // The Qt::WA_WState_Created flag is checked by translateConfigEvent() in
+ // qapplication_win.cpp. We switch it off temporarily to avoid move
+ // and resize events during creationt
+ q->setAttribute(Qt::WA_WState_Created, false);
+
+ if (window) { // override the old window
+ if (destroyOldWindow)
+ destroyw = data.winid;
+ id = window;
+ setWinId(window);
+ LONG res = SetWindowLong(window, GWL_STYLE, style);
+ if (!res)
+ qErrnoWarning("QWidget::create: Failed to set window style");
+
+ res = SetWindowLong( window, GWL_WNDPROC, (LONG)QtWndProc );
+
+ if (!res)
+ qErrnoWarning("QWidget::create: Failed to set window procedure");
+ } else if (desktop) { // desktop widget
+ id = GetDesktopWindow();
+ if (!id) { //Create a dummy desktop
+ RECT r;
+ SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
+ id = CreateWindow(reinterpret_cast<const wchar_t *>(windowClassName.utf16()),
+ reinterpret_cast<const wchar_t *>(title.utf16()), style,
+ r.left, r.top, r.right - r.left, r.bottom - r.top,
+ 0, 0, appinst, 0);
+ }
+ setWinId(id);
+ } else if (topLevel) { // create top-level widget
+ const bool wasMoved = q->testAttribute(Qt::WA_Moved);
+
+ int x, y;
+ if (qt_wince_is_mobile()) {
+ x = wasMoved ? data.crect.left() : CW_USEDEFAULT;
+ y = wasMoved ? data.crect.top() : CW_USEDEFAULT;
+ } else {
+ x = wasMoved ? data.crect.left() : 100;
+ y = wasMoved ? data.crect.top() : 100;
+ }
+
+ int w = CW_USEDEFAULT;
+ int h = CW_USEDEFAULT;
+
+ // Adjust for framestrut when needed
+ RECT rect = {0,0,0,0};
+ if (AdjustWindowRectEx(&rect, style, FALSE, exsty)) {
+ QTLWExtra *td = maybeTopData();
+ if (wasMoved && (td && !td->posFromMove)) {
+ x = data.crect.x() + rect.left;
+ y = data.crect.y() + rect.top;
+ }
+
+ if (q->testAttribute(Qt::WA_Resized)) {
+ w = data.crect.width() + (rect.right - rect.left);
+ h = data.crect.height() + (rect.bottom - rect.top);
+ }
+ }
+
+ id = CreateWindowEx(exsty, reinterpret_cast<const wchar_t *>(windowClassName.utf16()),
+ reinterpret_cast<const wchar_t *>(title.utf16()), style,
+ x, y, w, h,
+ 0, 0, appinst, 0);
+
+ if (!id)
+ qErrnoWarning("QWidget::create: Failed to create window");
+ setWinId(id);
+ if ((flags & Qt::WindowStaysOnTopHint) || (type == Qt::ToolTip))
+ SetWindowPos(id, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
+ } else if (q->testAttribute(Qt::WA_NativeWindow) || paintOnScreen()) { // create child widget
+ id = CreateWindowEx(exsty, (wchar_t*)windowClassName.utf16(), (wchar_t*)title.utf16(), style,
+ data.crect.left(), data.crect.top(), data.crect.width(), data.crect.height(),
+ parentw, NULL, appinst, NULL);
+ if (!id)
+ qErrnoWarning("QWidget::create: Failed to create window");
+ SetWindowPos(id, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+ setWinId(id);
+ }
+
+ if (desktop) {
+ q->setAttribute(Qt::WA_WState_Visible);
+ } else if (topLevel && !q->testAttribute(Qt::WA_DontShowOnScreen)) {
+ RECT cr;
+ GetClientRect(id, &cr);
+ // one cannot trust cr.left and cr.top, use a correction POINT instead
+ POINT pt;
+ pt.x = 0;
+ pt.y = 0;
+ if (!q->testAttribute(Qt::WA_DontShowOnScreen) || q->testAttribute(Qt::WA_Moved))
+ ClientToScreen(id, &pt);
+ data.crect = QRect(QPoint(pt.x, pt.y),
+ QPoint(pt.x + cr.right - 1, pt.y + cr.bottom - 1));
+
+ if (data.fstrut_dirty) {
+ // be nice to activeqt
+ updateFrameStrut();
+ }
+ }
+
+ q->setAttribute(Qt::WA_WState_Created); // accept move/resize events
+ hd = 0; // no display context
+
+ if (window) { // got window from outside
+ if (IsWindowVisible(window))
+ q->setAttribute(Qt::WA_WState_Visible);
+ else
+ q->setAttribute(Qt::WA_WState_Visible, false);
+ }
+
+ if (extra && !extra->mask.isEmpty())
+ setMask_sys(extra->mask);
+
+#if defined(QT_NON_COMMERCIAL)
+ QT_NC_WIDGET_CREATE
+#endif
+
+ if (q->hasFocus() && q->testAttribute(Qt::WA_InputMethodEnabled))
+ q->inputContext()->setFocusWidget(q);
+
+ if (destroyw) {
+ DestroyWindow(destroyw);
+ }
+
+#ifndef QT_NO_TABLETEVENT
+ if (q != qt_tablet_widget && QWidgetPrivate::mapper)
+ qt_tablet_init_wce();
+#endif // QT_NO_TABLETEVENT
+
+ if (q->testAttribute(Qt::WA_DropSiteRegistered))
+ registerDropSite(true);
+
+ if (maybeTopData() && maybeTopData()->opacity != 255)
+ q->setWindowOpacity(maybeTopData()->opacity/255.);
+
+ if (!topLevel && q->testAttribute(Qt::WA_NativeWindow) && q->testAttribute(Qt::WA_Mapped)) {
+ Q_ASSERT(q->internalWinId());
+ ShowWindow(q->internalWinId(), SW_SHOW);
+ }
+}
+
+/*
+ \internal
+ Platform-specific part of QWidget::show().
+*/
+void QWidgetPrivate::show_sys() {
+ Q_Q(QWidget);
+#if defined(QT_NON_COMMERCIAL)
+ QT_NC_SHOW_WINDOW
+#endif
+ if (q->testAttribute(Qt::WA_OutsideWSRange))
+ return;
+
+ q->setAttribute(Qt::WA_Mapped);
+
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+
+ if (q->testAttribute(Qt::WA_DontShowOnScreen)) {
+ invalidateBuffer(q->rect());
+ return;
+ }
+
+
+ int sm = SW_SHOW;
+ bool fakedMaximize = false;
+ if (q->isWindow()) {
+#ifndef Q_WS_WINCE_WM
+ if (q->isMinimized()) {
+ sm = SW_SHOWMINIMIZED;
+ } else if (q->isMaximized()) {
+ sm = SW_SHOWMAXIMIZED;
+ // Windows will not behave correctly when we try to maximize a window which does not
+ // have minimize nor maximize buttons in the window frame. Windows would then ignore
+ // non-available geometry, and rather maximize the widget to the full screen, minus the
+ // window frame (caption). So, we do a trick here, by adding a maximize button before
+ // maximizing the widget, and then remove the maximize button afterwards.
+ Qt::WindowFlags &flags = data.window_flags;
+ if (flags & Qt::WindowTitleHint &&
+ !(flags & (Qt::WindowMinMaxButtonsHint | Qt::FramelessWindowHint))) {
+ fakedMaximize = TRUE;
+ int style = GetWindowLong(q->internalWinId(), GWL_STYLE);
+ SetWindowLong(q->internalWinId(), GWL_STYLE, style | WS_MAXIMIZEBOX);
+ }
+ } else
+#else
+ // Imitate minimizing on Windows mobile by hiding the widget.
+ if (q->isMinimized())
+ sm = SW_HIDE;
+#endif
+ if (q->isHidden()) {
+ sm = SW_HIDE;
+ }
+ }
+ if (q->testAttribute(Qt::WA_ShowWithoutActivating)
+ || (q->windowType() == Qt::Popup)
+ || (q->windowType() == Qt::ToolTip)
+ || (q->windowType() == Qt::Tool)) {
+ sm = SW_SHOWNOACTIVATE;
+ }
+
+ ShowWindow(q->internalWinId(), sm);
+
+ if (q->isMaximized() && q->isWindow())
+ qt_wince_maximize(q);
+
+#ifndef Q_WS_WINCE_WM
+ if (!qt_wince_is_mobile() && q->isFullScreen()) {
+ HWND handle = FindWindow(L"HHTaskBar", L"");
+ if (handle) {
+ ShowWindow(handle, SW_HIDE);
+ EnableWindow(handle, false);
+ }
+ }
+
+ if (fakedMaximize) {
+ int style = GetWindowLong(q->internalWinId(), GWL_STYLE);
+ SetWindowLong(q->internalWinId(), GWL_STYLE, style & ~WS_MAXIMIZEBOX);
+ SetWindowPos(q->internalWinId(), 0, 0, 0, 0, 0,
+ SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER
+ | SWP_FRAMECHANGED);
+ }
+#else
+ Q_UNUSED(fakedMaximize);
+#endif
+
+ if (q->isWindow() && sm == SW_SHOW)
+ SetForegroundWindow(q->internalWinId());
+
+ invalidateBuffer(q->rect());
+}
+
+void QWidget::setWindowState(Qt::WindowStates newstate)
+{
+ Q_D(QWidget);
+ Qt::WindowStates oldstate = windowState();
+ if (oldstate == newstate)
+ return;
+
+ int max = SW_SHOWNORMAL;
+ int normal = SW_SHOWNOACTIVATE;
+
+ if ((oldstate & Qt::WindowMinimized) && !(newstate & Qt::WindowMinimized))
+ newstate |= Qt::WindowActive;
+ if (newstate & Qt::WindowActive)
+ normal = SW_SHOWNORMAL;
+ if (isWindow()) {
+ createWinId();
+ Q_ASSERT(testAttribute(Qt::WA_WState_Created));
+ // Ensure the initial size is valid, since we store it as normalGeometry below.
+ if ((!testAttribute(Qt::WA_Resized) && !isVisible()))
+ adjustSize();
+ if (!d->topData()->normalGeometry.isValid()) {
+ if (newstate & Qt::WindowMaximized && !(oldstate & Qt::WindowFullScreen))
+ d->topData()->normalGeometry = geometry();
+ if (newstate & Qt::WindowMinimized && !(oldstate & Qt::WindowFullScreen))
+ d->topData()->normalGeometry = geometry();
+ }
+ if ((oldstate & Qt::WindowMaximized) != (newstate & Qt::WindowMaximized)) {
+ if (!(newstate & Qt::WindowMaximized)) {
+ int style = GetWindowLong(internalWinId(), GWL_STYLE) | WS_BORDER | WS_POPUP | WS_CAPTION;
+ SetWindowLong(internalWinId(), GWL_STYLE, style);
+ SetWindowLong(internalWinId(), GWL_EXSTYLE, GetWindowLong (internalWinId(), GWL_EXSTYLE) & ~ WS_EX_NODRAG);
+ qt_wince_unmaximize(this);
+ }
+ if (isVisible() && newstate & Qt::WindowMaximized)
+ qt_wince_maximize(this);
+ if (isVisible() && !(newstate & Qt::WindowMinimized)) {
+ ShowWindow(internalWinId(), (newstate & Qt::WindowMaximized) ? max : normal);
+ if (!(newstate & Qt::WindowFullScreen)) {
+ QRect r = d->topData()->normalGeometry;
+ if (!(newstate & Qt::WindowMaximized) && r.width() >= 0) {
+ if (pos() != r.topLeft() || size() !=r.size()) {
+ d->topData()->normalGeometry = QRect(0,0,-1,-1);
+ setGeometry(r);
+ }
+ }
+ } else {
+ d->updateFrameStrut();
+ }
+ }
+ }
+ if ((oldstate & Qt::WindowFullScreen) != (newstate & Qt::WindowFullScreen)) {
+ if (newstate & Qt::WindowFullScreen) {
+ if (d->topData()->normalGeometry.width() < 0 && !(oldstate & Qt::WindowMaximized))
+ d->topData()->normalGeometry = geometry();
+ d->topData()->savedFlags = (Qt::WindowFlags)GetWindowLong(internalWinId(), GWL_STYLE);
+ UINT style = WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_POPUP;
+ if (isVisible())
+ style |= WS_VISIBLE;
+ SetWindowLong(internalWinId(), GWL_STYLE, style);
+ QRect r = qApp->desktop()->screenGeometry(this);
+ UINT swpf = SWP_FRAMECHANGED;
+ if (newstate & Qt::WindowActive)
+ swpf |= SWP_NOACTIVATE;
+ qt_wince_full_screen(internalWinId(), true, swpf);
+ d->updateFrameStrut();
+ } else {
+ UINT style = d->topData()->savedFlags;
+ if (isVisible())
+ style |= WS_VISIBLE;
+ SetWindowLong(internalWinId(), GWL_STYLE, style);
+ UINT swpf = SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOSIZE | SWP_NOMOVE;
+ if (newstate & Qt::WindowActive)
+ swpf |= SWP_NOACTIVATE;
+ qt_wince_full_screen(internalWinId(), false, swpf);
+ d->updateFrameStrut();
+
+ // preserve maximized state
+ if (isVisible()) {
+ ShowWindow(internalWinId(), (newstate & Qt::WindowMaximized) ? max : normal);
+ if (newstate & Qt::WindowMaximized)
+ qt_wince_maximize(this);
+ }
+ if (!(newstate & Qt::WindowMaximized)) {
+ QRect r = d->topData()->normalGeometry;
+ d->topData()->normalGeometry = QRect(0,0,-1,-1);
+ if (r.isValid())
+ setGeometry(r);
+ }
+ }
+ }
+ if ((oldstate & Qt::WindowMinimized) != (newstate & Qt::WindowMinimized)) {
+ if (newstate & Qt::WindowMinimized)
+ qt_wince_minimize(internalWinId());
+ else if (newstate & Qt::WindowMaximized) {
+ ShowWindow(internalWinId(), max);
+ qt_wince_maximize(this);
+ } else {
+ ShowWindow(internalWinId(), normal);
+ }
+ }
+ }
+ data->window_state = newstate;
+ QWindowStateChangeEvent e(oldstate);
+ QApplication::sendEvent(this, &e);
+}
+
+void QWidgetPrivate::deleteSysExtra()
+{
+ Q_Q(QWidget);
+ if (!qt_wince_is_mobile() && q->isFullScreen()) {
+ HWND handle = FindWindow(L"HHTaskBar", L"");
+ if (handle) {
+ ShowWindow(handle, SW_SHOWNORMAL);
+ EnableWindow(handle, true);
+ }
+ }
+}
+
+void QWidgetPrivate::setWindowOpacity_sys(qreal level) {
+ Q_UNUSED(level);
+ return;
+}
+
+// The procedure does nothing, but is required for mousegrabbing to work
+LRESULT QT_WIN_CALLBACK qJournalRecordProc(int nCode, WPARAM wParam, LPARAM lParam) {
+ Q_UNUSED(nCode);
+ Q_UNUSED(wParam);
+ Q_UNUSED(lParam);
+ return 0;
+}
+
+void QWidget::grabMouse() {
+ if (!qt_nograb()) {
+ if (mouseGrb)
+ mouseGrb->releaseMouse();
+ Q_ASSERT(testAttribute(Qt::WA_WState_Created));
+ SetCapture(internalWinId());
+ mouseGrb = this;
+ }
+}
+
+#ifndef QT_NO_CURSOR
+void QWidget::grabMouse(const QCursor &cursor) {
+ if (!qt_nograb()) {
+ if (mouseGrb)
+ mouseGrb->releaseMouse();
+ Q_ASSERT(testAttribute(Qt::WA_WState_Created));
+ SetCapture(internalWinId());
+ mouseGrbCur = new QCursor(cursor);
+ SetCursor(mouseGrbCur->handle());
+ mouseGrb = this;
+ }
+}
+#endif
+
+void QWidget::releaseMouse() {
+ if (!qt_nograb() && mouseGrb == this) {
+ ReleaseCapture();
+ if (journalRec) {
+ journalRec = 0;
+ }
+ if (mouseGrbCur) {
+ delete mouseGrbCur;
+ mouseGrbCur = 0;
+ }
+ mouseGrb = 0;
+ }
+}
+
+void QWidget::show()
+{
+ Qt::WindowFlags flags = windowFlags() & 0xff;
+ int threshold = qApp->autoMaximizeThreshold();
+ if ((threshold < 0) || (windowState() & Qt::WindowFullScreen) || (windowState() & Qt::WindowMaximized)) {
+ setVisible(true);
+ return;
+ }
+ int height = sizeHint().height();
+ int screenHeight = (qreal(threshold) / 100.0f * qApp->desktop()->screenGeometry(this).height());
+ bool maximize = height > screenHeight;
+ if (!maximize) {
+ // If we do not maximize yet we check the widget and its child widgets whether they are
+ //vertically expanding. If one of the widgets is expanding we maximize.
+ QList<QWidget *> list = findChildren<QWidget *>();
+ bool expandingChild = sizePolicy().verticalPolicy () == QSizePolicy::Expanding;
+ for (int i = 0; (i < list.size()) && !expandingChild; ++i) {
+ expandingChild = list.at(i)->sizePolicy().verticalPolicy () == QSizePolicy::Expanding;
+ }
+ maximize = expandingChild;
+ }
+ if ((minimumSizeHint().height() > qApp->desktop()->screenGeometry(this).height()) || (minimumSizeHint().width() > qApp->desktop()->screenGeometry(this).width()))
+ maximize = false;
+
+ if ((flags == Qt::Window || flags == Qt::Dialog) && maximize) {
+ setWindowState((windowState() & ~(Qt::WindowMinimized | Qt::WindowFullScreen))
+ | Qt::WindowMaximized);
+ setVisible(true);
+ }
+ else {
+ setVisible(true);
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // Q_WS_WINCE
diff --git a/src/widgets/platforms/win/qwininputcontext_p.h b/src/widgets/platforms/win/qwininputcontext_p.h
new file mode 100644
index 0000000000..c0a6ac6b6f
--- /dev/null
+++ b/src/widgets/platforms/win/qwininputcontext_p.h
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWININPUTCONTEXT_P_H
+#define QWININPUTCONTEXT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qinputcontext.cpp. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtWidgets/qinputcontext.h"
+#include "QtCore/qt_windows.h"
+
+#if !defined(IMR_RECONVERTSTRING)
+typedef struct tagRECONVERTSTRING {
+ DWORD dwSize;
+ DWORD dwVersion;
+ DWORD dwStrLen;
+ DWORD dwStrOffset;
+ DWORD dwCompStrLen;
+ DWORD dwCompStrOffset;
+ DWORD dwTargetStrLen;
+ DWORD dwTargetStrOffset;
+} RECONVERTSTRING, *PRECONVERTSTRING;
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QWinInputContext : public QInputContext
+{
+ Q_OBJECT
+public:
+ explicit QWinInputContext(QObject* parent = 0);
+ virtual ~QWinInputContext();
+
+ virtual QString identifierName() { return QLatin1String("win"); }
+ virtual QString language();
+
+ virtual void reset();
+ virtual void update();
+
+ virtual void mouseHandler(int x, QMouseEvent *event);
+ virtual bool isComposing() const;
+
+ virtual void setFocusWidget(QWidget *w);
+
+ bool startComposition();
+ bool endComposition();
+ bool composition(LPARAM lparam);
+ int reconvertString(RECONVERTSTRING *reconv);
+
+ static void TranslateMessage(const MSG *msg);
+ static LRESULT DefWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+ static void updateImeStatus(QWidget *w, bool hasFocus);
+ static void enablePopupChild(QWidget *w, bool e);
+ static void enable(QWidget *w, bool e);
+
+private:
+ void init();
+ bool recursionGuard;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWININPUTCONTEXT_P_H
diff --git a/src/widgets/platforms/win/qwininputcontext_win.cpp b/src/widgets/platforms/win/qwininputcontext_win.cpp
new file mode 100644
index 0000000000..9ec9942af8
--- /dev/null
+++ b/src/widgets/platforms/win/qwininputcontext_win.cpp
@@ -0,0 +1,847 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwininputcontext_p.h"
+#include "qinputcontext_p.h"
+
+#include "qfont.h"
+#include "qwidget.h"
+#include "qapplication.h"
+#include "qevent.h"
+#include "qtextformat.h"
+#include "qtextboundaryfinder.h"
+
+//#define Q_IME_DEBUG
+
+#ifdef Q_IME_DEBUG
+#include "qdebug.h"
+#endif
+
+#if defined(Q_WS_WINCE)
+extern void qt_wince_show_SIP(bool show); // defined in qguifunctions_wince.cpp
+#endif
+
+QT_BEGIN_NAMESPACE
+
+extern bool qt_sendSpontaneousEvent(QObject*, QEvent*);
+
+
+DEFINE_GUID(IID_IActiveIMMApp,
+0x08c0e040, 0x62d1, 0x11d1, 0x93, 0x26, 0x0, 0x60, 0xb0, 0x67, 0xb8, 0x6e);
+
+
+
+DEFINE_GUID(CLSID_CActiveIMM,
+0x4955DD33, 0xB159, 0x11d0, 0x8F, 0xCF, 0x0, 0xAA, 0x00, 0x6B, 0xCC, 0x59);
+
+
+
+DEFINE_GUID(IID_IActiveIMMMessagePumpOwner,
+0xb5cf2cfa, 0x8aeb, 0x11d1, 0x93, 0x64, 0x0, 0x60, 0xb0, 0x67, 0xb8, 0x6e);
+
+
+
+interface IEnumRegisterWordW;
+interface IEnumInputContext;
+
+
+bool qt_sendSpontaneousEvent(QObject*, QEvent*);
+
+
+#define IFMETHOD HRESULT STDMETHODCALLTYPE
+
+interface IActiveIMMApp : public IUnknown
+{
+public:
+ virtual IFMETHOD AssociateContext(HWND hWnd, HIMC hIME, HIMC __RPC_FAR *phPrev) = 0;
+ virtual IFMETHOD dummy_ConfigureIMEA() = 0;
+ virtual IFMETHOD ConfigureIMEW(HKL hKL, HWND hWnd, DWORD dwMode, REGISTERWORDW __RPC_FAR *pData) = 0;
+ virtual IFMETHOD CreateContext(HIMC __RPC_FAR *phIMC) = 0;
+ virtual IFMETHOD DestroyContext(HIMC hIME) = 0;
+ virtual IFMETHOD dummy_EnumRegisterWordA() = 0;
+ virtual IFMETHOD EnumRegisterWordW(HKL hKL, LPWSTR szReading, DWORD dwStyle, LPWSTR szRegister, LPVOID pData,
+ IEnumRegisterWordW __RPC_FAR *__RPC_FAR *pEnum) = 0;
+ virtual IFMETHOD dummy_EscapeA() = 0;
+ virtual IFMETHOD EscapeW(HKL hKL, HIMC hIMC, UINT uEscape, LPVOID pData, LRESULT __RPC_FAR *plResult) = 0;
+ virtual IFMETHOD dummy_GetCandidateListA() = 0;
+ virtual IFMETHOD GetCandidateListW(HIMC hIMC, DWORD dwIndex, UINT uBufLen, CANDIDATELIST __RPC_FAR *pCandList,
+ UINT __RPC_FAR *puCopied) = 0;
+ virtual IFMETHOD dummy_GetCandidateListCountA() = 0;
+ virtual IFMETHOD GetCandidateListCountW(HIMC hIMC, DWORD __RPC_FAR *pdwListSize, DWORD __RPC_FAR *pdwBufLen) = 0;
+ virtual IFMETHOD GetCandidateWindow(HIMC hIMC, DWORD dwIndex, CANDIDATEFORM __RPC_FAR *pCandidate) = 0;
+ virtual IFMETHOD dummy_GetCompositionFontA() = 0;
+ virtual IFMETHOD GetCompositionFontW(HIMC hIMC, LOGFONTW __RPC_FAR *plf) = 0;
+ virtual IFMETHOD dummy_GetCompositionStringA() = 0;
+ virtual IFMETHOD GetCompositionStringW(HIMC hIMC, DWORD dwIndex, DWORD dwBufLen, LONG __RPC_FAR *plCopied, LPVOID pBuf) = 0;
+ virtual IFMETHOD GetCompositionWindow(HIMC hIMC, COMPOSITIONFORM __RPC_FAR *pCompForm) = 0;
+ virtual IFMETHOD GetContext(HWND hWnd, HIMC __RPC_FAR *phIMC) = 0;
+ virtual IFMETHOD dummy_GetConversionListA() = 0;
+ virtual IFMETHOD GetConversionListW(HKL hKL, HIMC hIMC, LPWSTR pSrc, UINT uBufLen, UINT uFlag,
+ CANDIDATELIST __RPC_FAR *pDst, UINT __RPC_FAR *puCopied) = 0;
+ virtual IFMETHOD GetConversionStatus(HIMC hIMC, DWORD __RPC_FAR *pfdwConversion, DWORD __RPC_FAR *pfdwSentence) = 0;
+ virtual IFMETHOD GetDefaultIMEWnd(HWND hWnd, HWND __RPC_FAR *phDefWnd) = 0;
+ virtual IFMETHOD dummy_GetDescriptionA() = 0;
+ virtual IFMETHOD GetDescriptionW(HKL hKL, UINT uBufLen, LPWSTR szDescription, UINT __RPC_FAR *puCopied) = 0;
+ virtual IFMETHOD dummy_GetGuideLineA() = 0;
+ virtual IFMETHOD GetGuideLineW(HIMC hIMC, DWORD dwIndex, DWORD dwBufLen, LPWSTR pBuf, DWORD __RPC_FAR *pdwResult) = 0;
+ virtual IFMETHOD dummy_GetIMEFileNameA() = 0;
+ virtual IFMETHOD GetIMEFileNameW(HKL hKL, UINT uBufLen, LPWSTR szFileName, UINT __RPC_FAR *puCopied) = 0;
+ virtual IFMETHOD GetOpenStatus(HIMC hIMC) = 0;
+ virtual IFMETHOD GetProperty(HKL hKL, DWORD fdwIndex, DWORD __RPC_FAR *pdwProperty) = 0;
+ virtual IFMETHOD dummy_GetRegisterWordStyleA() = 0;
+ virtual IFMETHOD GetRegisterWordStyleW(HKL hKL, UINT nItem, STYLEBUFW __RPC_FAR *pStyleBuf, UINT __RPC_FAR *puCopied) = 0;
+ virtual IFMETHOD GetStatusWindowPos(HIMC hIMC, POINT __RPC_FAR *pptPos) = 0;
+ virtual IFMETHOD GetVirtualKey(HWND hWnd, UINT __RPC_FAR *puVirtualKey) = 0;
+ virtual IFMETHOD dummy_InstallIMEA() = 0;
+ virtual IFMETHOD InstallIMEW(LPWSTR szIMEFileName, LPWSTR szLayoutText, HKL __RPC_FAR *phKL) = 0;
+ virtual IFMETHOD IsIME(HKL hKL) = 0;
+ virtual IFMETHOD dummy_IsUIMessageA() = 0;
+ virtual IFMETHOD IsUIMessageW(HWND hWndIME, UINT msg, WPARAM wParam, LPARAM lParam) = 0;
+ virtual IFMETHOD NotifyIME(HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue) = 0;
+ virtual IFMETHOD dummy_RegisterWordA() = 0;
+ virtual IFMETHOD RegisterWordW(HKL hKL, LPWSTR szReading, DWORD dwStyle, LPWSTR szRegister) = 0;
+ virtual IFMETHOD ReleaseContext(HWND hWnd, HIMC hIMC) = 0;
+ virtual IFMETHOD SetCandidateWindow(HIMC hIMC, CANDIDATEFORM __RPC_FAR *pCandidate) = 0;
+ virtual IFMETHOD SetCompositionFontA(HIMC hIMC, LOGFONTA __RPC_FAR *plf) = 0;
+ virtual IFMETHOD SetCompositionFontW(HIMC hIMC, LOGFONTW __RPC_FAR *plf) = 0;
+ virtual IFMETHOD dummy_SetCompositionStringA() = 0;
+ virtual IFMETHOD SetCompositionStringW(HIMC hIMC, DWORD dwIndex, LPVOID pComp, DWORD dwCompLen,
+ LPVOID pRead, DWORD dwReadLen) = 0;
+ virtual IFMETHOD SetCompositionWindow(HIMC hIMC, COMPOSITIONFORM __RPC_FAR *pCompForm) = 0;
+ virtual IFMETHOD SetConversionStatus(HIMC hIMC, DWORD fdwConversion, DWORD fdwSentence) = 0;
+ virtual IFMETHOD SetOpenStatus(HIMC hIMC, BOOL fOpen) = 0;
+ virtual IFMETHOD SetStatusWindowPos(HIMC hIMC, POINT __RPC_FAR *pptPos) = 0;
+ virtual IFMETHOD SimulateHotKey(HWND hWnd, DWORD dwHotKeyID) = 0;
+ virtual IFMETHOD dummy_UnregisterWordA() = 0;
+ virtual IFMETHOD UnregisterWordW(HKL hKL, LPWSTR szReading, DWORD dwStyle, LPWSTR szUnregister) = 0;
+ virtual IFMETHOD Activate(BOOL fRestoreLayout) = 0;
+ virtual IFMETHOD Deactivate(void) = 0;
+ virtual IFMETHOD OnDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT __RPC_FAR *plResult) = 0;
+ virtual IFMETHOD FilterClientWindows(ATOM __RPC_FAR *aaClassList, UINT uSize) = 0;
+ virtual IFMETHOD dummy_GetCodePageA() = 0;
+ virtual IFMETHOD GetLangId(HKL hKL, LANGID __RPC_FAR *plid) = 0;
+ virtual IFMETHOD AssociateContextEx(HWND hWnd, HIMC hIMC, DWORD dwFlags) = 0;
+ virtual IFMETHOD DisableIME(DWORD idThread) = 0;
+ virtual IFMETHOD dummy_GetImeMenuItemsA() = 0;
+ virtual IFMETHOD GetImeMenuItemsW(HIMC hIMC, DWORD dwFlags, DWORD dwType, /*IMEMENUITEMINFOW*/ void __RPC_FAR *pImeParentMenu,
+ /*IMEMENUITEMINFOW*/ void __RPC_FAR *pImeMenu, DWORD dwSize, DWORD __RPC_FAR *pdwResult) = 0;
+ virtual IFMETHOD EnumInputContext(DWORD idThread, IEnumInputContext __RPC_FAR *__RPC_FAR *ppEnum) = 0;
+};
+
+interface IActiveIMMMessagePumpOwner : public IUnknown
+{
+public:
+ virtual IFMETHOD Start(void) = 0;
+ virtual IFMETHOD End(void) = 0;
+ virtual IFMETHOD OnTranslateMessage(const MSG __RPC_FAR *pMsg) = 0;
+ virtual IFMETHOD Pause(DWORD __RPC_FAR *pdwCookie) = 0;
+ virtual IFMETHOD Resume(DWORD dwCookie) = 0;
+};
+
+
+static IActiveIMMApp *aimm = 0;
+static IActiveIMMMessagePumpOwner *aimmpump = 0;
+static QString *imeComposition = 0;
+static int imePosition = -1;
+bool qt_use_rtl_extensions = false;
+static bool haveCaret = false;
+
+#ifndef LGRPID_INSTALLED
+#define LGRPID_INSTALLED 0x00000001 // installed language group ids
+#define LGRPID_SUPPORTED 0x00000002 // supported language group ids
+#endif
+
+#ifndef LGRPID_ARABIC
+#define LGRPID_WESTERN_EUROPE 0x0001 // Western Europe & U.S.
+#define LGRPID_CENTRAL_EUROPE 0x0002 // Central Europe
+#define LGRPID_BALTIC 0x0003 // Baltic
+#define LGRPID_GREEK 0x0004 // Greek
+#define LGRPID_CYRILLIC 0x0005 // Cyrillic
+#define LGRPID_TURKISH 0x0006 // Turkish
+#define LGRPID_JAPANESE 0x0007 // Japanese
+#define LGRPID_KOREAN 0x0008 // Korean
+#define LGRPID_TRADITIONAL_CHINESE 0x0009 // Traditional Chinese
+#define LGRPID_SIMPLIFIED_CHINESE 0x000a // Simplified Chinese
+#define LGRPID_THAI 0x000b // Thai
+#define LGRPID_HEBREW 0x000c // Hebrew
+#define LGRPID_ARABIC 0x000d // Arabic
+#define LGRPID_VIETNAMESE 0x000e // Vietnamese
+#define LGRPID_INDIC 0x000f // Indic
+#define LGRPID_GEORGIAN 0x0010 // Georgian
+#define LGRPID_ARMENIAN 0x0011 // Armenian
+#endif
+
+static DWORD WM_MSIME_MOUSE = 0;
+
+QWinInputContext::QWinInputContext(QObject *parent)
+ : QInputContext(parent), recursionGuard(false)
+{
+#ifndef Q_WS_WINCE
+ QSysInfo::WinVersion ver = QSysInfo::windowsVersion();
+ if (ver & QSysInfo::WV_NT_based && ver >= QSysInfo::WV_VISTA) {
+ // Since the IsValidLanguageGroup/IsValidLocale functions always return true on
+ // Vista, check the Keyboard Layouts for enabling RTL.
+ UINT nLayouts = GetKeyboardLayoutList(0, 0);
+ if (nLayouts) {
+ HKL *lpList = new HKL[nLayouts];
+ GetKeyboardLayoutList(nLayouts, lpList);
+ for (int i = 0; i<(int)nLayouts; i++) {
+ WORD plangid = PRIMARYLANGID((quintptr)lpList[i]);
+ if (plangid == LANG_ARABIC
+ || plangid == LANG_HEBREW
+ || plangid == LANG_FARSI
+#ifdef LANG_SYRIAC
+ || plangid == LANG_SYRIAC
+#endif
+ ) {
+ qt_use_rtl_extensions = true;
+ break;
+ }
+ }
+ delete []lpList;
+ }
+ } else {
+ // figure out whether a RTL language is installed
+ qt_use_rtl_extensions = IsValidLanguageGroup(LGRPID_ARABIC, LGRPID_INSTALLED)
+ || IsValidLanguageGroup(LGRPID_HEBREW, LGRPID_INSTALLED)
+ || IsValidLocale(MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_DEFAULT), SORT_DEFAULT), LCID_INSTALLED)
+ || IsValidLocale(MAKELCID(MAKELANGID(LANG_HEBREW, SUBLANG_DEFAULT), SORT_DEFAULT), LCID_INSTALLED)
+#ifdef LANG_SYRIAC
+ || IsValidLocale(MAKELCID(MAKELANGID(LANG_SYRIAC, SUBLANG_DEFAULT), SORT_DEFAULT), LCID_INSTALLED)
+#endif
+ || IsValidLocale(MAKELCID(MAKELANGID(LANG_FARSI, SUBLANG_DEFAULT), SORT_DEFAULT), LCID_INSTALLED);
+ }
+#else
+ qt_use_rtl_extensions = false;
+#endif
+
+ WM_MSIME_MOUSE = RegisterWindowMessage(L"MSIMEMouseOperation");
+}
+
+QWinInputContext::~QWinInputContext()
+{
+ // release active input method if we have one
+ if (aimm) {
+ aimmpump->End();
+ aimmpump->Release();
+ aimm->Deactivate();
+ aimm->Release();
+ aimm = 0;
+ aimmpump = 0;
+ }
+ delete imeComposition;
+ imeComposition = 0;
+}
+
+static HWND getDefaultIMEWnd(HWND wnd)
+{
+ HWND ime_wnd;
+ if(aimm)
+ aimm->GetDefaultIMEWnd(wnd, &ime_wnd);
+ else
+ ime_wnd = ImmGetDefaultIMEWnd(wnd);
+ return ime_wnd;
+}
+
+static HIMC getContext(HWND wnd)
+{
+ HIMC imc;
+ if (aimm)
+ aimm->GetContext(wnd, &imc);
+ else
+ imc = ImmGetContext(wnd);
+
+ return imc;
+}
+
+static void releaseContext(HWND wnd, HIMC imc)
+{
+ if (aimm)
+ aimm->ReleaseContext(wnd, imc);
+ else
+ ImmReleaseContext(wnd, imc);
+}
+
+static void notifyIME(HIMC imc, DWORD dwAction, DWORD dwIndex, DWORD dwValue)
+{
+ if (!imc)
+ return;
+ if (aimm)
+ aimm->NotifyIME(imc, dwAction, dwIndex, dwValue);
+ else
+ ImmNotifyIME(imc, dwAction, dwIndex, dwValue);
+}
+
+static LONG getCompositionString(HIMC himc, DWORD dwIndex, LPVOID lpbuf, DWORD dBufLen)
+{
+ LONG len = 0;
+ if (aimm)
+ aimm->GetCompositionStringW(himc, dwIndex, dBufLen, &len, lpbuf);
+ else
+ len = ImmGetCompositionString(himc, dwIndex, lpbuf, dBufLen);
+ return len;
+}
+
+static int getCursorPosition(HIMC himc)
+{
+ return getCompositionString(himc, GCS_CURSORPOS, 0, 0);
+}
+
+static QString getString(HIMC himc, DWORD dwindex, int *selStart = 0, int *selLength = 0)
+{
+ const int bufferSize = 256;
+ wchar_t buffer[bufferSize];
+ int len = getCompositionString(himc, dwindex, buffer, bufferSize * sizeof(wchar_t));
+
+ if (selStart) {
+ char attrbuffer[bufferSize];
+ int attrlen = getCompositionString(himc, GCS_COMPATTR, attrbuffer, bufferSize);
+ *selStart = attrlen+1;
+ *selLength = -1;
+ for (int i = 0; i < attrlen; i++) {
+ if (attrbuffer[i] & ATTR_TARGET_CONVERTED) {
+ *selStart = qMin(*selStart, i);
+ *selLength = qMax(*selLength, i);
+ }
+ }
+ *selLength = qMax(0, *selLength - *selStart + 1);
+ }
+
+ if (len <= 0)
+ return QString();
+
+ return QString((QChar*)buffer, len / sizeof(QChar));
+}
+
+void QWinInputContext::TranslateMessage(const MSG *msg)
+{
+ if (!aimmpump || aimmpump->OnTranslateMessage(msg) != S_OK)
+ ::TranslateMessage(msg);
+}
+
+LRESULT QWinInputContext::DefWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ LRESULT retval;
+ if (!aimm || aimm->OnDefWindowProc(hwnd, msg, wParam, lParam, &retval) != S_OK)
+ {
+ retval = ::DefWindowProc(hwnd, msg, wParam, lParam);
+ }
+ return retval;
+}
+
+
+void QWinInputContext::update()
+{
+ QWidget *w = focusWidget();
+ if(!w)
+ return;
+
+ Q_ASSERT(w->testAttribute(Qt::WA_WState_Created));
+ HIMC imc = getContext(w->effectiveWinId());
+
+ if (!imc)
+ return;
+
+ QFont f = qvariant_cast<QFont>(w->inputMethodQuery(Qt::ImFont));
+ HFONT hf;
+ hf = f.handle();
+
+ LOGFONT lf;
+ if (GetObject(hf, sizeof(lf), &lf)) {
+ if (aimm)
+ aimm->SetCompositionFontW(imc, &lf);
+ else
+ ImmSetCompositionFont(imc, &lf);
+ }
+
+ QRect r = w->inputMethodQuery(Qt::ImMicroFocus).toRect();
+
+ // The ime window positions are based on the WinId with active focus.
+ QWidget *imeWnd = QWidget::find(::GetFocus());
+ if (imeWnd && !aimm) {
+ QPoint pt (r.topLeft());
+ pt = w->mapToGlobal(pt);
+ pt = imeWnd->mapFromGlobal(pt);
+ r.moveTo(pt);
+ }
+
+ COMPOSITIONFORM cf;
+ // ### need X-like inputStyle config settings
+ cf.dwStyle = CFS_FORCE_POSITION;
+ cf.ptCurrentPos.x = r.x();
+ cf.ptCurrentPos.y = r.y();
+
+ CANDIDATEFORM candf;
+ candf.dwIndex = 0;
+ candf.dwStyle = CFS_EXCLUDE;
+ candf.ptCurrentPos.x = r.x();
+ candf.ptCurrentPos.y = r.y() + r.height();
+ candf.rcArea.left = r.x();
+ candf.rcArea.top = r.y();
+ candf.rcArea.right = r.x() + r.width();
+ candf.rcArea.bottom = r.y() + r.height();
+
+ if(haveCaret)
+ SetCaretPos(r.x(), r.y());
+
+ if (aimm) {
+ aimm->SetCompositionWindow(imc, &cf);
+ aimm->SetCandidateWindow(imc, &candf);
+ } else {
+ ImmSetCompositionWindow(imc, &cf);
+ ImmSetCandidateWindow(imc, &candf);
+ }
+
+ releaseContext(w->effectiveWinId(), imc);
+}
+
+
+bool QWinInputContext::endComposition()
+{
+ QWidget *fw = focusWidget();
+#ifdef Q_IME_DEBUG
+ qDebug("endComposition! fw = %s", fw ? fw->className() : "(null)");
+#endif
+ bool result = true;
+ if(imePosition == -1 || recursionGuard)
+ return result;
+
+ // Googles Pinyin Input Method likes to call endComposition again
+ // when we call notifyIME with CPS_CANCEL, so protect ourselves
+ // against that.
+ recursionGuard = true;
+
+ if (fw) {
+ Q_ASSERT(fw->testAttribute(Qt::WA_WState_Created));
+ HIMC imc = getContext(fw->effectiveWinId());
+ notifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
+ releaseContext(fw->effectiveWinId(), imc);
+ if(haveCaret) {
+ DestroyCaret();
+ haveCaret = false;
+ }
+ }
+
+ if (!fw)
+ fw = QApplication::focusWidget();
+
+ if (fw) {
+ QInputMethodEvent e;
+ result = qt_sendSpontaneousEvent(fw, &e);
+ }
+
+ if (imeComposition)
+ imeComposition->clear();
+ imePosition = -1;
+
+ recursionGuard = false;
+
+ return result;
+}
+
+void QWinInputContext::reset()
+{
+ QWidget *fw = focusWidget();
+
+#ifdef Q_IME_DEBUG
+ qDebug("sending accept to focus widget %s", fw ? fw->className() : "(null)");
+#endif
+
+ if (fw && imePosition != -1) {
+ QInputMethodEvent e;
+ if (imeComposition)
+ e.setCommitString(*imeComposition);
+ imePosition = -1;
+ qt_sendSpontaneousEvent(fw, &e);
+ }
+
+ if (imeComposition)
+ imeComposition->clear();
+ imePosition = -1;
+
+ if (fw) {
+ Q_ASSERT(fw->testAttribute(Qt::WA_WState_Created));
+ HIMC imc = getContext(fw->effectiveWinId());
+ notifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
+ releaseContext(fw->effectiveWinId(), imc);
+ }
+
+}
+
+
+bool QWinInputContext::startComposition()
+{
+#ifdef Q_IME_DEBUG
+ qDebug("startComposition");
+#endif
+
+ if (!imeComposition)
+ imeComposition = new QString();
+
+ QWidget *fw = focusWidget();
+ if (fw) {
+ Q_ASSERT(fw->testAttribute(Qt::WA_WState_Created));
+ imePosition = 0;
+ haveCaret = CreateCaret(fw->effectiveWinId(), 0, 1, 1);
+ HideCaret(fw->effectiveWinId());
+ update();
+ }
+ return fw != 0;
+}
+
+enum StandardFormat {
+ PreeditFormat,
+ SelectionFormat
+};
+
+bool QWinInputContext::composition(LPARAM lParam)
+{
+#ifdef Q_IME_DEBUG
+ QString str;
+ if (lParam & GCS_RESULTSTR)
+ str += "RESULTSTR ";
+ if (lParam & GCS_COMPSTR)
+ str += "COMPSTR ";
+ if (lParam & GCS_COMPATTR)
+ str += "COMPATTR ";
+ if (lParam & GCS_CURSORPOS)
+ str += "CURSORPOS ";
+ if (lParam & GCS_COMPCLAUSE)
+ str += "COMPCLAUSE ";
+ if (lParam & CS_INSERTCHAR)
+ str += "INSERTCHAR ";
+ if (lParam & CS_NOMOVECARET)
+ str += "NOMOVECARET ";
+ qDebug("composition, lParam=(%x) %s imePosition=%d", lParam, str.latin1(), imePosition);
+#endif
+
+ bool result = true;
+
+ if(!lParam)
+ // bogus event
+ return true;
+
+ QWidget *fw = QApplication::focusWidget();
+ if (fw) {
+ Q_ASSERT(fw->testAttribute(Qt::WA_WState_Created));
+ HIMC imc = getContext(fw->effectiveWinId());
+ QInputMethodEvent e;
+ if (lParam & (GCS_COMPSTR | GCS_COMPATTR | GCS_CURSORPOS)) {
+ if (imePosition == -1)
+ // need to send a start event
+ startComposition();
+
+ // some intermediate composition result
+ int selStart, selLength;
+ *imeComposition = getString(imc, GCS_COMPSTR, &selStart, &selLength);
+ imePosition = getCursorPosition(imc);
+ if (lParam & CS_INSERTCHAR && lParam & CS_NOMOVECARET) {
+ // make korean work correctly. Hope this is correct for all IMEs
+ selStart = 0;
+ selLength = imeComposition->length();
+ }
+ if(selLength == 0)
+ selStart = 0;
+
+ QList<QInputMethodEvent::Attribute> attrs;
+ if (selStart > 0)
+ attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, selStart,
+ standardFormat(PreeditFormat));
+ if (selLength)
+ attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, selStart, selLength,
+ standardFormat(SelectionFormat));
+ if (selStart + selLength < imeComposition->length())
+ attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, selStart + selLength,
+ imeComposition->length() - selStart - selLength,
+ standardFormat(PreeditFormat));
+ if(imePosition >= 0)
+ attrs << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, imePosition, selLength ? 0 : 1, QVariant());
+
+ e = QInputMethodEvent(*imeComposition, attrs);
+ }
+ if (lParam & GCS_RESULTSTR) {
+ if(imePosition == -1)
+ startComposition();
+ // a fixed result, return the converted string
+ *imeComposition = getString(imc, GCS_RESULTSTR);
+ imePosition = -1;
+ e.setCommitString(*imeComposition);
+ imeComposition->clear();
+ }
+ result = qt_sendSpontaneousEvent(fw, &e);
+ update();
+ releaseContext(fw->effectiveWinId(), imc);
+ }
+#ifdef Q_IME_DEBUG
+ qDebug("imecomposition: cursor pos at %d, str=%x", imePosition, str[0].unicode());
+#endif
+ return result;
+}
+
+static HIMC defaultContext = 0;
+
+// checks whether widget is a popup
+inline bool isPopup(QWidget *w)
+{
+ if (w && (w->windowFlags() & Qt::Popup) == Qt::Popup)
+ return true;
+ else
+ return false;
+}
+// checks whether widget is in a popup
+inline bool isInPopup(QWidget *w)
+{
+ if (w && (isPopup(w) || isPopup(w->window())))
+ return true;
+ else
+ return false;
+}
+
+// find the parent widget, which is a non popup toplevel
+// this is valid only if the widget is/in a popup
+inline QWidget *findParentforPopup(QWidget *w)
+{
+ QWidget *e = QWidget::find(w->effectiveWinId());
+ // check if this or its parent is a popup
+ while (isInPopup(e)) {
+ e = e->window()->parentWidget();
+ if (!e)
+ break;
+ e = QWidget::find(e->effectiveWinId());
+ }
+ if (e)
+ return e->window();
+ else
+ return 0;
+}
+
+// enables or disables the ime
+inline void enableIme(QWidget *w, bool value)
+{
+ if (value) {
+ // enable ime
+ if (defaultContext)
+ ImmAssociateContext(w->effectiveWinId(), defaultContext);
+#ifdef Q_WS_WINCE
+ if (qApp->autoSipEnabled())
+ qt_wince_show_SIP(true);
+#endif
+ } else {
+ // disable ime
+ HIMC oldimc = ImmAssociateContext(w->effectiveWinId(), 0);
+ if (!defaultContext)
+ defaultContext = oldimc;
+#ifdef Q_WS_WINCE
+ if (qApp->autoSipEnabled())
+ qt_wince_show_SIP(false);
+#endif
+ }
+}
+
+
+void QWinInputContext::updateImeStatus(QWidget *w, bool hasFocus)
+{
+ if (!w)
+ return;
+ // It's always the proxy that carries the hints.
+ QWidget *focusProxyWidget = w->focusProxy();
+ if (!focusProxyWidget)
+ focusProxyWidget = w;
+ bool e = w->testAttribute(Qt::WA_InputMethodEnabled) && w->isEnabled()
+ && !(focusProxyWidget->inputMethodHints() & (Qt::ImhExclusiveInputMask | Qt::ImhHiddenText));
+ bool hasIme = e && hasFocus;
+#ifdef Q_IME_DEBUG
+ qDebug("%s HasFocus = %d hasIme = %d e = %d ", w->className(), hasFocus, hasIme, e);
+#endif
+ if (hasFocus || e) {
+ if (isInPopup(w))
+ QWinInputContext::enablePopupChild(w, hasIme);
+ else
+ QWinInputContext::enable(w, hasIme);
+ }
+}
+
+void QWinInputContext::enablePopupChild(QWidget *w, bool e)
+{
+ if (aimm) {
+ enable(w, e);
+ return;
+ }
+
+ if (!w || !isInPopup(w))
+ return;
+#ifdef Q_IME_DEBUG
+ qDebug("enablePopupChild: w=%s, enable = %s", w ? w->className() : "(null)" , e ? "true" : "false");
+#endif
+ QWidget *parent = findParentforPopup(w);
+ if (parent) {
+ // update ime status of the normal toplevel parent of the popup
+ enableIme(parent, e);
+ }
+ QWidget *toplevel = w->window();
+ if (toplevel) {
+ // update ime status of the toplevel popup
+ enableIme(toplevel, e);
+ }
+}
+
+void QWinInputContext::enable(QWidget *w, bool e)
+{
+ if(w) {
+#ifdef Q_IME_DEBUG
+ qDebug("enable: w=%s, enable = %s", w ? w->className() : "(null)" , e ? "true" : "false");
+#endif
+ if (!w->testAttribute(Qt::WA_WState_Created))
+ return;
+ if(aimm) {
+ HIMC oldimc;
+ if (!e) {
+ aimm->AssociateContext(w->effectiveWinId(), 0, &oldimc);
+ if (!defaultContext)
+ defaultContext = oldimc;
+ } else if (defaultContext) {
+ aimm->AssociateContext(w->effectiveWinId(), defaultContext, &oldimc);
+ }
+ } else {
+ // update ime status on the widget
+ QWidget *p = QWidget::find(w->effectiveWinId());
+ if (p)
+ enableIme(p, e);
+ }
+ }
+}
+
+void QWinInputContext::setFocusWidget(QWidget *w)
+{
+ QWidget *oldFocus = focusWidget();
+ if (oldFocus == w)
+ return;
+ if (w) {
+ QWinInputContext::updateImeStatus(w, true);
+ } else {
+ if (oldFocus)
+ QWinInputContext::updateImeStatus(oldFocus , false);
+ }
+ QInputContext::setFocusWidget(w);
+ update();
+}
+
+bool QWinInputContext::isComposing() const
+{
+ return imeComposition && !imeComposition->isEmpty();
+}
+
+void QWinInputContext::mouseHandler(int pos, QMouseEvent *e)
+{
+ if(e->type() != QEvent::MouseButtonPress)
+ return;
+
+ if (pos < 0 || pos > imeComposition->length())
+ reset();
+
+ // Probably should pass the correct button, but it seems to work fine like this.
+ DWORD button = MK_LBUTTON;
+
+ QWidget *fw = focusWidget();
+ if (fw) {
+ Q_ASSERT(fw->testAttribute(Qt::WA_WState_Created));
+ HIMC himc = getContext(fw->effectiveWinId());
+ HWND ime_wnd = getDefaultIMEWnd(fw->effectiveWinId());
+ SendMessage(ime_wnd, WM_MSIME_MOUSE, MAKELONG(MAKEWORD(button, pos == 0 ? 2 : 1), pos), (LPARAM)himc);
+ releaseContext(fw->effectiveWinId(), himc);
+ }
+ //qDebug("mouseHandler: got value %d pos=%d", ret,pos);
+}
+
+QString QWinInputContext::language()
+{
+ return QString();
+}
+
+int QWinInputContext::reconvertString(RECONVERTSTRING *reconv)
+{
+ QWidget *w = focusWidget();
+ if(!w)
+ return -1;
+
+ Q_ASSERT(w->testAttribute(Qt::WA_WState_Created));
+ QString surroundingText = qvariant_cast<QString>(w->inputMethodQuery(Qt::ImSurroundingText));
+ int memSize = sizeof(RECONVERTSTRING)+(surroundingText.length()+1)*sizeof(ushort);
+ // If memory is not allocated, return the required size.
+ if (!reconv) {
+ if (surroundingText.isEmpty())
+ return -1;
+ else
+ return memSize;
+ }
+ int pos = qvariant_cast<int>(w->inputMethodQuery(Qt::ImCursorPosition));
+ // find the word in the surrounding text.
+ QTextBoundaryFinder bounds(QTextBoundaryFinder::Word, surroundingText);
+ bounds.setPosition(pos);
+ if (bounds.isAtBoundary()) {
+ if (QTextBoundaryFinder::EndWord == bounds.boundaryReasons())
+ bounds.toPreviousBoundary();
+ } else {
+ bounds.toPreviousBoundary();
+ }
+ int startPos = bounds.position();
+ bounds.toNextBoundary();
+ int endPos = bounds.position();
+ // select the text, this will be overwritten by following ime events.
+ QList<QInputMethodEvent::Attribute> attrs;
+ attrs << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, startPos, endPos-startPos, QVariant());
+ QInputMethodEvent e(QString(), attrs);
+ qt_sendSpontaneousEvent(w, &e);
+
+ reconv->dwSize = memSize;
+ reconv->dwVersion = 0;
+
+ reconv->dwStrLen = surroundingText.length();
+ reconv->dwStrOffset = sizeof(RECONVERTSTRING);
+ reconv->dwCompStrLen = endPos-startPos;
+ reconv->dwCompStrOffset = startPos*sizeof(ushort);
+ reconv->dwTargetStrLen = reconv->dwCompStrLen;
+ reconv->dwTargetStrOffset = reconv->dwCompStrOffset;
+ memcpy((char*)(reconv+1), surroundingText.utf16(), surroundingText.length()*sizeof(ushort));
+ return memSize;
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/win/qwinnativepangesturerecognizer_win.cpp b/src/widgets/platforms/win/qwinnativepangesturerecognizer_win.cpp
new file mode 100644
index 0000000000..85ef0a949f
--- /dev/null
+++ b/src/widgets/platforms/win/qwinnativepangesturerecognizer_win.cpp
@@ -0,0 +1,133 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qwinnativepangesturerecognizer_win_p.h"
+
+#include "qevent.h"
+#include "qgraphicsitem.h"
+#include "qgesture.h"
+
+#include "private/qgesture_p.h"
+#include "private/qevent_p.h"
+#include "private/qapplication_p.h"
+#include "private/qwidget_p.h"
+
+#ifndef QT_NO_GESTURES
+
+QT_BEGIN_NAMESPACE
+
+#if !defined(QT_NO_NATIVE_GESTURES)
+
+QWinNativePanGestureRecognizer::QWinNativePanGestureRecognizer()
+{
+}
+
+QGesture *QWinNativePanGestureRecognizer::create(QObject *target)
+{
+ if (!target)
+ return new QPanGesture; // a special case
+ if (!target->isWidgetType())
+ return 0;
+ if (qobject_cast<QGraphicsObject *>(target))
+ return 0;
+
+ QWidget *q = static_cast<QWidget *>(target);
+ QWidgetPrivate *d = q->d_func();
+ d->nativeGesturePanEnabled = true;
+ d->winSetupGestures();
+
+ return new QPanGesture;
+}
+
+QGestureRecognizer::Result QWinNativePanGestureRecognizer::recognize(QGesture *state,
+ QObject *,
+ QEvent *event)
+{
+ QPanGesture *q = static_cast<QPanGesture*>(state);
+ QPanGesturePrivate *d = q->d_func();
+
+ QGestureRecognizer::Result result = QGestureRecognizer::Ignore;
+ if (event->type() == QEvent::NativeGesture) {
+ QNativeGestureEvent *ev = static_cast<QNativeGestureEvent*>(event);
+ switch(ev->gestureType) {
+ case QNativeGestureEvent::GestureBegin:
+ break;
+ case QNativeGestureEvent::Pan:
+ result = QGestureRecognizer::TriggerGesture;
+ event->accept();
+ break;
+ case QNativeGestureEvent::GestureEnd:
+ if (q->state() == Qt::NoGesture)
+ return QGestureRecognizer::Ignore; // some other gesture has ended
+ result = QGestureRecognizer::FinishGesture;
+ break;
+ default:
+ return QGestureRecognizer::Ignore;
+ }
+ if (q->state() == Qt::NoGesture) {
+ d->lastOffset = d->offset = QPointF();
+ d->startPosition = ev->position;
+ } else {
+ d->lastOffset = d->offset;
+ d->offset = QPointF(ev->position.x() - d->startPosition.x(),
+ ev->position.y() - d->startPosition.y());
+ }
+ }
+ return result;
+}
+
+void QWinNativePanGestureRecognizer::reset(QGesture *state)
+{
+ QPanGesture *pan = static_cast<QPanGesture*>(state);
+ QPanGesturePrivate *d = pan->d_func();
+
+ d->lastOffset = d->offset = QPointF();
+ d->startPosition = QPoint();
+ d->acceleration = 0;
+
+ QGestureRecognizer::reset(state);
+}
+
+#endif // QT_NO_NATIVE_GESTURES
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_GESTURES
diff --git a/src/widgets/platforms/win/qwinnativepangesturerecognizer_win_p.h b/src/widgets/platforms/win/qwinnativepangesturerecognizer_win_p.h
new file mode 100644
index 0000000000..4f15a82b7e
--- /dev/null
+++ b/src/widgets/platforms/win/qwinnativepangesturerecognizer_win_p.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINNATIVEPANGESTURERECOGNIZER_WIN_P_H
+#define QWINNATIVEPANGESTURERECOGNIZER_WIN_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QGestureRecognizer>
+
+#ifndef QT_NO_GESTURES
+
+QT_BEGIN_NAMESPACE
+
+#if !defined(QT_NO_NATIVE_GESTURES)
+
+class QWinNativePanGestureRecognizer : public QGestureRecognizer
+{
+public:
+ QWinNativePanGestureRecognizer();
+
+ QGesture *create(QObject *target);
+ QGestureRecognizer::Result recognize(QGesture *state, QObject *watched, QEvent *event);
+ void reset(QGesture *state);
+};
+
+#endif // QT_NO_NATIVE_GESTURES
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_GESTURES
+
+#endif // QWINNATIVEPANGESTURERECOGNIZER_WIN_P_H
diff --git a/src/widgets/platforms/x11/qapplication_x11.cpp b/src/widgets/platforms/x11/qapplication_x11.cpp
new file mode 100644
index 0000000000..4a392d2a19
--- /dev/null
+++ b/src/widgets/platforms/x11/qapplication_x11.cpp
@@ -0,0 +1,6239 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// ### 4.0: examine Q_EXPORT's below. The respective symbols had all
+// been in use (e.g. in the KDE wm) before the introduction of a version
+// map. One might want to turn some of them into proper public API and
+// provide a proper alternative for others. See also the exports in
+// qapplication_win.cpp, which suggest a unification.
+
+#include "qplatformdefs.h"
+
+#include "qcolormap.h"
+#include "qdesktopwidget.h"
+#include "qapplication.h"
+#include "qapplication_p.h"
+#include "qcursor.h"
+#include "qwidget.h"
+#include "qbitarray.h"
+#include "qpainter.h"
+#include "qfile.h"
+#include "qpixmapcache.h"
+#include "qdatetime.h"
+#include "qtextcodec.h"
+#include "qdatastream.h"
+#include "qbuffer.h"
+#include "qsocketnotifier.h"
+#include "qsessionmanager.h"
+#include "qclipboard.h"
+#include "qwhatsthis.h"
+#include "qsettings.h"
+#include "qstylefactory.h"
+#include "qfileinfo.h"
+#include "qdir.h"
+#include "qhash.h"
+#include "qevent.h"
+#include "qevent_p.h"
+#include "qvarlengtharray.h"
+#include "qdebug.h"
+#include <private/qcrashhandler_p.h>
+#include <private/qcolor_p.h>
+#include <private/qcursor_p.h>
+#include <private/qiconloader_p.h>
+#include <qgtkstyle.h>
+#include "qstyle.h"
+#include "qmetaobject.h"
+#include "qtimer.h"
+#include "qlibrary.h"
+#include <private/qgraphicssystemfactory_p.h>
+#include "qguiplatformplugin_p.h"
+#include "qkde_p.h"
+
+#if !defined (QT_NO_TABLET)
+extern "C" {
+# define class c_class //XIproto.h has a name member named 'class' which the c++ compiler doesn't like
+# include <wacomcfg.h>
+# undef class
+}
+#endif
+
+#ifndef QT_GUI_DOUBLE_CLICK_RADIUS
+#define QT_GUI_DOUBLE_CLICK_RADIUS 5
+#endif
+
+
+//#define ALIEN_DEBUG
+
+#if !defined(QT_NO_GLIB)
+# include "qguieventdispatcher_glib_p.h"
+#endif
+#include "qeventdispatcher_x11_p.h"
+#include <private/qpaintengine_x11_p.h>
+
+#include <private/qkeymapper_p.h>
+
+// Input method stuff
+#ifndef QT_NO_IM
+#include "qinputcontext.h"
+#include "qinputcontextfactory.h"
+#endif // QT_NO_IM
+
+#ifndef QT_NO_XFIXES
+#include <X11/extensions/Xfixes.h>
+#endif // QT_NO_XFIXES
+
+#include "qt_x11_p.h"
+#include "qx11info_x11.h"
+
+#define XK_MISCELLANY
+#include <X11/keysymdef.h>
+#if !defined(QT_NO_XINPUT)
+#include <X11/extensions/XI.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <locale.h>
+
+#include "qwidget_p.h"
+
+#include <private/qbackingstore_p.h>
+
+#ifdef QT_RX71_MULTITOUCH
+# include <qsocketnotifier.h>
+# include <linux/input.h>
+# include <errno.h>
+#endif
+
+#if _POSIX_VERSION+0 < 200112L && !defined(Q_OS_BSD4)
+# define QT_NO_UNSETENV
+#endif
+
+QT_BEGIN_NAMESPACE
+
+//#define X_NOT_BROKEN
+#ifdef X_NOT_BROKEN
+// Some X libraries are built with setlocale #defined to _Xsetlocale,
+// even though library users are then built WITHOUT such a definition.
+// This creates a problem - Qt might setlocale() one value, but then
+// X looks and doesn't see the value Qt set. The solution here is to
+// implement _Xsetlocale just in case X calls it - redirecting it to
+// the real libC version.
+//
+# ifndef setlocale
+extern "C" char *_Xsetlocale(int category, const char *locale);
+char *_Xsetlocale(int category, const char *locale)
+{
+ //qDebug("_Xsetlocale(%d,%s),category,locale");
+ return setlocale(category,locale);
+}
+# endif // setlocale
+#endif // X_NOT_BROKEN
+
+/* Warning: if you modify this string, modify the list of atoms in qt_x11_p.h as well! */
+static const char * x11_atomnames = {
+ // window-manager <-> client protocols
+ "WM_PROTOCOLS\0"
+ "WM_DELETE_WINDOW\0"
+ "WM_TAKE_FOCUS\0"
+ "_NET_WM_PING\0"
+ "_NET_WM_CONTEXT_HELP\0"
+ "_NET_WM_SYNC_REQUEST\0"
+ "_NET_WM_SYNC_REQUEST_COUNTER\0"
+
+ // ICCCM window state
+ "WM_STATE\0"
+ "WM_CHANGE_STATE\0"
+
+ // Session management
+ "WM_CLIENT_LEADER\0"
+ "WM_WINDOW_ROLE\0"
+ "SM_CLIENT_ID\0"
+
+ // Clipboard
+ "CLIPBOARD\0"
+ "INCR\0"
+ "TARGETS\0"
+ "MULTIPLE\0"
+ "TIMESTAMP\0"
+ "SAVE_TARGETS\0"
+ "CLIP_TEMPORARY\0"
+ "_QT_SELECTION\0"
+ "_QT_CLIPBOARD_SENTINEL\0"
+ "_QT_SELECTION_SENTINEL\0"
+ "CLIPBOARD_MANAGER\0"
+
+ "RESOURCE_MANAGER\0"
+
+ "_XSETROOT_ID\0"
+
+ "_QT_SCROLL_DONE\0"
+ "_QT_INPUT_ENCODING\0"
+
+ "_MOTIF_WM_HINTS\0"
+
+ "DTWM_IS_RUNNING\0"
+ "ENLIGHTENMENT_DESKTOP\0"
+ "_DT_SAVE_MODE\0"
+ "_SGI_DESKS_MANAGER\0"
+
+ // EWMH (aka NETWM)
+ "_NET_SUPPORTED\0"
+ "_NET_VIRTUAL_ROOTS\0"
+ "_NET_WORKAREA\0"
+
+ "_NET_MOVERESIZE_WINDOW\0"
+ "_NET_WM_MOVERESIZE\0"
+
+ "_NET_WM_NAME\0"
+ "_NET_WM_ICON_NAME\0"
+ "_NET_WM_ICON\0"
+
+ "_NET_WM_PID\0"
+
+ "_NET_WM_WINDOW_OPACITY\0"
+
+ "_NET_WM_STATE\0"
+ "_NET_WM_STATE_ABOVE\0"
+ "_NET_WM_STATE_BELOW\0"
+ "_NET_WM_STATE_FULLSCREEN\0"
+ "_NET_WM_STATE_MAXIMIZED_HORZ\0"
+ "_NET_WM_STATE_MAXIMIZED_VERT\0"
+ "_NET_WM_STATE_MODAL\0"
+ "_NET_WM_STATE_STAYS_ON_TOP\0"
+ "_NET_WM_STATE_DEMANDS_ATTENTION\0"
+
+ "_NET_WM_USER_TIME\0"
+ "_NET_WM_USER_TIME_WINDOW\0"
+ "_NET_WM_FULL_PLACEMENT\0"
+
+ "_NET_WM_WINDOW_TYPE\0"
+ "_NET_WM_WINDOW_TYPE_DESKTOP\0"
+ "_NET_WM_WINDOW_TYPE_DOCK\0"
+ "_NET_WM_WINDOW_TYPE_TOOLBAR\0"
+ "_NET_WM_WINDOW_TYPE_MENU\0"
+ "_NET_WM_WINDOW_TYPE_UTILITY\0"
+ "_NET_WM_WINDOW_TYPE_SPLASH\0"
+ "_NET_WM_WINDOW_TYPE_DIALOG\0"
+ "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU\0"
+ "_NET_WM_WINDOW_TYPE_POPUP_MENU\0"
+ "_NET_WM_WINDOW_TYPE_TOOLTIP\0"
+ "_NET_WM_WINDOW_TYPE_NOTIFICATION\0"
+ "_NET_WM_WINDOW_TYPE_COMBO\0"
+ "_NET_WM_WINDOW_TYPE_DND\0"
+ "_NET_WM_WINDOW_TYPE_NORMAL\0"
+ "_KDE_NET_WM_WINDOW_TYPE_OVERRIDE\0"
+
+ "_KDE_NET_WM_FRAME_STRUT\0"
+
+ "_NET_STARTUP_INFO\0"
+ "_NET_STARTUP_INFO_BEGIN\0"
+
+ "_NET_SUPPORTING_WM_CHECK\0"
+
+ "_NET_WM_CM_S0\0"
+
+ "_NET_SYSTEM_TRAY_VISUAL\0"
+
+ "_NET_ACTIVE_WINDOW\0"
+
+ // Property formats
+ "COMPOUND_TEXT\0"
+ "TEXT\0"
+ "UTF8_STRING\0"
+
+ // xdnd
+ "XdndEnter\0"
+ "XdndPosition\0"
+ "XdndStatus\0"
+ "XdndLeave\0"
+ "XdndDrop\0"
+ "XdndFinished\0"
+ "XdndTypeList\0"
+ "XdndActionList\0"
+
+ "XdndSelection\0"
+
+ "XdndAware\0"
+ "XdndProxy\0"
+
+ "XdndActionCopy\0"
+ "XdndActionLink\0"
+ "XdndActionMove\0"
+ "XdndActionPrivate\0"
+
+ // Motif DND
+ "_MOTIF_DRAG_AND_DROP_MESSAGE\0"
+ "_MOTIF_DRAG_INITIATOR_INFO\0"
+ "_MOTIF_DRAG_RECEIVER_INFO\0"
+ "_MOTIF_DRAG_WINDOW\0"
+ "_MOTIF_DRAG_TARGETS\0"
+
+ "XmTRANSFER_SUCCESS\0"
+ "XmTRANSFER_FAILURE\0"
+
+ // Xkb
+ "_XKB_RULES_NAMES\0"
+
+ // XEMBED
+ "_XEMBED\0"
+ "_XEMBED_INFO\0"
+
+ // Wacom old. (before version 0.10)
+ "Wacom Stylus\0"
+ "Wacom Cursor\0"
+ "Wacom Eraser\0"
+
+ // Tablet
+ "STYLUS\0"
+ "ERASER\0"
+};
+
+Q_WIDGETS_EXPORT QX11Data *qt_x11Data = 0;
+
+/*****************************************************************************
+ Internal variables and functions
+ *****************************************************************************/
+static const char *appName = 0; // application name
+static const char *appClass = 0; // application class
+static const char *appFont = 0; // application font
+static const char *appBGCol = 0; // application bg color
+static const char *appFGCol = 0; // application fg color
+static const char *appBTNCol = 0; // application btn color
+static const char *mwGeometry = 0; // main widget geometry
+static const char *mwTitle = 0; // main widget title
+char *qt_ximServer = 0; // XIM Server will connect to
+static bool appSync = false; // X11 synchronization
+#if defined(QT_DEBUG)
+static bool appNoGrab = false; // X11 grabbing enabled
+static bool appDoGrab = false; // X11 grabbing override (gdb)
+#endif
+static bool app_save_rootinfo = false; // save root info
+static bool app_do_modal = false; // modal mode
+static Window curWin = 0; // current window
+
+
+// function to update the workarea of the screen - in qdesktopwidget_x11.cpp
+extern void qt_desktopwidget_update_workarea();
+
+// Function to change the window manager state (from qwidget_x11.cpp)
+extern void qt_change_net_wm_state(const QWidget *w, bool set, Atom one, Atom two = 0);
+
+// modifier masks for alt, meta, super, hyper, and mode_switch - detected when the application starts
+// and/or keyboard layout changes
+uchar qt_alt_mask = 0;
+uchar qt_meta_mask = 0;
+uchar qt_super_mask = 0;
+uchar qt_hyper_mask = 0;
+uchar qt_mode_switch_mask = 0;
+
+// flags for extensions for special Languages, currently only for RTL languages
+bool qt_use_rtl_extensions = false;
+
+static Window mouseActWindow = 0; // window where mouse is
+static Qt::MouseButton mouseButtonPressed = Qt::NoButton; // last mouse button pressed
+static Qt::MouseButtons mouseButtonState = Qt::NoButton; // mouse button state
+static Time mouseButtonPressTime = 0; // when was a button pressed
+static short mouseXPos, mouseYPos; // mouse pres position in act window
+static short mouseGlobalXPos, mouseGlobalYPos; // global mouse press position
+
+extern QWidgetList *qt_modal_stack; // stack of modal widgets
+
+// window where mouse buttons have been pressed
+static Window pressed_window = XNone;
+
+// popup control
+static bool replayPopupMouseEvent = false;
+static bool popupGrabOk;
+
+bool qt_sm_blockUserInput = false; // session management
+
+Q_WIDGETS_EXPORT int qt_xfocusout_grab_counter = 0;
+
+#if !defined (QT_NO_TABLET)
+Q_GLOBAL_STATIC(QTabletDeviceDataList, tablet_devices)
+QTabletDeviceDataList *qt_tablet_devices()
+{
+ return tablet_devices();
+}
+
+extern bool qt_tabletChokeMouse;
+#endif
+
+typedef bool(*QX11FilterFunction)(XEvent *event);
+
+Q_GLOBAL_STATIC(QList<QX11FilterFunction>, x11Filters)
+
+Q_WIDGETS_EXPORT void qt_installX11EventFilter(QX11FilterFunction func)
+{
+ Q_ASSERT(func);
+
+ if (QList<QX11FilterFunction> *list = x11Filters())
+ list->append(func);
+}
+
+Q_WIDGETS_EXPORT void qt_removeX11EventFilter(QX11FilterFunction func)
+{
+ Q_ASSERT(func);
+
+ if (QList<QX11FilterFunction> *list = x11Filters())
+ list->removeOne(func);
+}
+
+
+static bool qt_x11EventFilter(XEvent* ev)
+{
+ long unused;
+ if (qApp->filterEvent(ev, &unused))
+ return true;
+ if (const QList<QX11FilterFunction> *list = x11Filters()) {
+ for (QList<QX11FilterFunction>::const_iterator it = list->constBegin(); it != list->constEnd(); ++it) {
+ if ((*it)(ev))
+ return true;
+ }
+ }
+
+ return qApp->x11EventFilter(ev);
+}
+
+#if !defined(QT_NO_XIM)
+XIMStyle qt_xim_preferred_style = 0;
+#endif
+int qt_ximComposingKeycode=0;
+QTextCodec * qt_input_mapper = 0;
+
+extern bool qt_check_clipboard_sentinel(); //def in qclipboard_x11.cpp
+extern bool qt_check_selection_sentinel(); //def in qclipboard_x11.cpp
+extern bool qt_xfixes_clipboard_changed(Window clipboardOwner, Time timestamp); //def in qclipboard_x11.cpp
+extern bool qt_xfixes_selection_changed(Window selectionOwner, Time timestamp); //def in qclipboard_x11.cpp
+
+static void qt_save_rootinfo();
+Q_WIDGETS_EXPORT bool qt_try_modal(QWidget *, XEvent *);
+
+QWidget *qt_button_down = 0; // last widget to be pressed with the mouse
+QPointer<QWidget> qt_last_mouse_receiver = 0;
+static QWidget *qt_popup_down = 0; // popup that contains the pressed widget
+
+extern bool qt_xdnd_dragging;
+
+// gui or non-gui from qapplication.cpp
+extern bool qt_is_gui_used;
+
+/*!
+ \internal
+ Try to resolve a \a symbol from \a library with the version specified
+ by \a vernum.
+
+ Note that, in the case of the Xfixes library, \a vernum is not the same as
+ \c XFIXES_MAJOR - it is a part of soname and may differ from the Xfixes
+ version.
+*/
+static void* qt_load_library_runtime(const char *library, int vernum,
+ int highestVernum, const char *symbol)
+{
+ QList<int> versions;
+ // we try to load in the following order:
+ // explicit version -> the default one -> (from the highest (highestVernum) to the lowest (vernum) )
+ if (vernum != -1)
+ versions << vernum;
+ versions << -1;
+ if (vernum != -1) {
+ for(int i = highestVernum; i > vernum; --i)
+ versions << i;
+ }
+ Q_FOREACH(int version, versions) {
+ QLatin1String libName(library);
+ QLibrary xfixesLib(libName, version);
+ void *ptr = xfixesLib.resolve(symbol);
+ if (ptr)
+ return ptr;
+ }
+ return 0;
+}
+
+#ifndef QT_NO_XINPUT
+# ifdef QT_RUNTIME_XINPUT
+# define XINPUT_LOAD_RUNTIME(vernum, symbol, symbol_type) \
+ (symbol_type)qt_load_library_runtime("libXi", vernum, 6, #symbol);
+# define XINPUT_LOAD(symbol) \
+ XINPUT_LOAD_RUNTIME(1, symbol, Ptr##symbol)
+# else // not runtime XInput
+# define XINPUT_LOAD(symbol) symbol
+# endif // QT_RUNTIME_XINPUT
+#else // not using Xinput at all
+# define XINPUT_LOAD(symbol) 0
+#endif // QT_NO_XINPUT
+
+#ifndef QT_NO_XFIXES
+# ifdef QT_RUNTIME_XFIXES
+# define XFIXES_LOAD_RUNTIME(vernum, symbol, symbol_type) \
+ (symbol_type)qt_load_library_runtime("libXfixes", vernum, 4, #symbol);
+# define XFIXES_LOAD_V1(symbol) \
+ XFIXES_LOAD_RUNTIME(1, symbol, Ptr##symbol)
+# define XFIXES_LOAD_V2(symbol) \
+ XFIXES_LOAD_RUNTIME(2, symbol, Ptr##symbol)
+
+# else // not runtime Xfixes
+
+# if XFIXES_MAJOR >= 2
+# define XFIXES_LOAD_V1(symbol) symbol
+# define XFIXES_LOAD_V2(symbol) symbol
+# elif XFIXES_MAJOR >= 1
+# define XFIXES_LOAD_V1(symbol) symbol
+# define XFIXES_LOAD_V2(symbol) 0
+# else
+# error Unsupported version of Xfixes
+# endif
+# endif // QT_RUNTIME_XFIXES
+#else // not using Xfixes at all
+# define XFIXES_LOAD_V1(symbol) 0
+# define XFIXES_LOAD_V2(symbol) 0
+#endif // QT_NO_XFIXES
+
+#ifndef QT_NO_XFIXES
+
+struct qt_xfixes_selection_event_data
+{
+ // which selection to filter out.
+ Atom selection;
+};
+
+#if defined(Q_C_CALLBACKS)
+extern "C" {
+#endif
+
+static Bool qt_xfixes_scanner(Display*, XEvent *event, XPointer arg)
+{
+ qt_xfixes_selection_event_data *data =
+ reinterpret_cast<qt_xfixes_selection_event_data*>(arg);
+ if (event->type == X11->xfixes_eventbase + XFixesSelectionNotify) {
+ XFixesSelectionNotifyEvent *xfixes_event = reinterpret_cast<XFixesSelectionNotifyEvent*>(event);
+ if (xfixes_event->selection == data->selection)
+ return true;
+ }
+ return false;
+}
+
+#if defined(Q_C_CALLBACKS)
+}
+#endif
+
+#endif // QT_NO_XFIXES
+
+class QETWidget : public QWidget // event translator widget
+{
+public:
+ QWidgetPrivate* d_func() { return QWidget::d_func(); }
+ bool translateMouseEvent(const XEvent *);
+ void translatePaintEvent(const XEvent *);
+ bool translateConfigEvent(const XEvent *);
+ bool translateCloseEvent(const XEvent *);
+ bool translateScrollDoneEvent(const XEvent *);
+ bool translateWheelEvent(int global_x, int global_y, int delta, Qt::MouseButtons buttons,
+ Qt::KeyboardModifiers modifiers, Qt::Orientation orient);
+#if !defined (QT_NO_TABLET)
+ bool translateXinputEvent(const XEvent*, QTabletDeviceData *tablet);
+#endif
+ bool translatePropertyEvent(const XEvent *);
+
+ void doDeferredMap()
+ {
+ Q_ASSERT(testAttribute(Qt::WA_WState_Created));
+ if (!testAttribute(Qt::WA_Resized)) {
+ adjustSize();
+ setAttribute(Qt::WA_Resized, false);
+ }
+
+ /*
+ workaround for WM's that throw away ConfigureRequests from the following:
+
+ window->hide();
+ window->move(x, y); // could also be resize(), move()+resize(), or setGeometry()
+ window->show();
+ */
+ QRect r = geometry();
+
+ XMoveResizeWindow(X11->display,
+ internalWinId(),
+ r.x(),
+ r.y(),
+ r.width(),
+ r.height());
+
+ // static gravity!
+ XSizeHints sh;
+ long unused;
+ XGetWMNormalHints(X11->display, internalWinId(), &sh, &unused);
+ sh.flags |= USPosition | PPosition | USSize | PSize | PWinGravity;
+ sh.x = r.x();
+ sh.y = r.y();
+ sh.width = r.width();
+ sh.height = r.height();
+ sh.win_gravity = StaticGravity;
+ XSetWMNormalHints(X11->display, internalWinId(), &sh);
+
+ setAttribute(Qt::WA_Mapped);
+ if (testAttribute(Qt::WA_DontShowOnScreen))
+ return;
+ d_func()->topData()->waitingForMapNotify = 1;
+ XMapWindow(X11->display, internalWinId());
+ }
+};
+
+
+void QApplicationPrivate::createEventDispatcher()
+{
+ Q_Q(QApplication);
+#if !defined(QT_NO_GLIB)
+ if (qgetenv("QT_NO_GLIB").isEmpty() && QEventDispatcherGlib::versionSupported())
+ eventDispatcher = (q->type() != QApplication::Tty
+ ? new QGuiEventDispatcherGlib(q)
+ : new QEventDispatcherGlib(q));
+ else
+#endif
+ eventDispatcher = (q->type() != QApplication::Tty
+ ? new QEventDispatcherX11(q)
+ : new QEventDispatcherUNIX(q));
+}
+
+/*****************************************************************************
+ Default X error handlers
+ *****************************************************************************/
+
+#if defined(Q_C_CALLBACKS)
+extern "C" {
+#endif
+
+static int (*original_x_errhandler)(Display *dpy, XErrorEvent *);
+static int (*original_xio_errhandler)(Display *dpy);
+
+static int qt_x_errhandler(Display *dpy, XErrorEvent *err)
+{
+ if (X11->display != dpy) {
+ // only handle X errors for our display
+ return 0;
+ }
+
+ switch (err->error_code) {
+ case BadAtom:
+ if (err->request_code == 20 /* X_GetProperty */
+ && (err->resourceid == XA_RESOURCE_MANAGER
+ || err->resourceid == XA_RGB_DEFAULT_MAP
+ || err->resourceid == ATOM(_NET_SUPPORTED)
+ || err->resourceid == ATOM(_NET_SUPPORTING_WM_CHECK)
+ || err->resourceid == ATOM(XdndProxy)
+ || err->resourceid == ATOM(XdndAware))) {
+ // Perhaps we're running under SECURITY reduction? :/
+ return 0;
+ }
+ break;
+
+ case BadWindow:
+ if (err->request_code == 2 /* X_ChangeWindowAttributes */
+ || err->request_code == 38 /* X_QueryPointer */) {
+ for (int i = 0; i < ScreenCount(dpy); ++i) {
+ if (err->resourceid == RootWindow(dpy, i)) {
+ // Perhaps we're running under SECURITY reduction? :/
+ return 0;
+ }
+ }
+ }
+ X11->seen_badwindow = true;
+ if (err->request_code == 25 /* X_SendEvent */) {
+ for (int i = 0; i < ScreenCount(dpy); ++i) {
+ if (err->resourceid == RootWindow(dpy, i)) {
+ // Perhaps we're running under SECURITY reduction? :/
+ return 0;
+ }
+ }
+ if (X11->xdndHandleBadwindow()) {
+ qDebug("xdndHandleBadwindow returned true");
+ return 0;
+ }
+ }
+ if (X11->ignore_badwindow)
+ return 0;
+ break;
+
+ default:
+#if !defined(QT_NO_XINPUT)
+ if (err->request_code == X11->xinput_major
+ && err->error_code == (X11->xinput_errorbase + XI_BadDevice)
+ && err->minor_code == 3 /* X_OpenDevice */) {
+ return 0;
+ }
+#endif
+ break;
+ }
+
+ char errstr[256];
+ XGetErrorText( dpy, err->error_code, errstr, 256 );
+ char buffer[256];
+ char request_str[256];
+ qsnprintf(buffer, 256, "%d", err->request_code);
+ XGetErrorDatabaseText(dpy, "XRequest", buffer, "", request_str, 256);
+ if (err->request_code < 128) {
+ // X error for a normal protocol request
+ qWarning( "X Error: %s %d\n"
+ " Major opcode: %d (%s)\n"
+ " Resource id: 0x%lx",
+ errstr, err->error_code,
+ err->request_code,
+ request_str,
+ err->resourceid );
+ } else {
+ // X error for an extension request
+ const char *extensionName = 0;
+ if (err->request_code == X11->xrender_major)
+ extensionName = "RENDER";
+ else if (err->request_code == X11->xrandr_major)
+ extensionName = "RANDR";
+ else if (err->request_code == X11->xinput_major)
+ extensionName = "XInputExtension";
+ else if (err->request_code == X11->mitshm_major)
+ extensionName = "MIT-SHM";
+#ifndef QT_NO_XKB
+ else if(err->request_code == X11->xkb_major)
+ extensionName = "XKEYBOARD";
+#endif
+
+ char minor_str[256];
+ if (extensionName) {
+ qsnprintf(buffer, 256, "%s.%d", extensionName, err->minor_code);
+ XGetErrorDatabaseText(dpy, "XRequest", buffer, "", minor_str, 256);
+ } else {
+ extensionName = "Uknown extension";
+ qsnprintf(minor_str, 256, "Unknown request");
+ }
+ qWarning( "X Error: %s %d\n"
+ " Extension: %d (%s)\n"
+ " Minor opcode: %d (%s)\n"
+ " Resource id: 0x%lx",
+ errstr, err->error_code,
+ err->request_code,
+ extensionName,
+ err->minor_code,
+ minor_str,
+ err->resourceid );
+ }
+
+ // ### we really should distinguish between severe, non-severe and
+ // ### application specific errors
+
+ return 0;
+}
+
+
+static int qt_xio_errhandler(Display *)
+{
+ qWarning("%s: Fatal IO error: client killed", appName);
+ QApplicationPrivate::reset_instance_pointer();
+ exit(1);
+ //### give the application a chance for a proper shutdown instead,
+ //### exit(1) doesn't help.
+ return 0;
+}
+
+#if defined(Q_C_CALLBACKS)
+}
+#endif
+
+#ifndef QT_NO_XSYNC
+struct qt_sync_request_event_data
+{
+ WId window;
+};
+
+#if defined(Q_C_CALLBACKS)
+extern "C" {
+#endif
+
+static Bool qt_sync_request_scanner(Display*, XEvent *event, XPointer arg)
+{
+ qt_sync_request_event_data *data =
+ reinterpret_cast<qt_sync_request_event_data*>(arg);
+ if (event->type == ClientMessage &&
+ event->xany.window == data->window &&
+ event->xclient.message_type == ATOM(WM_PROTOCOLS) &&
+ (Atom)event->xclient.data.l[0] == ATOM(_NET_WM_SYNC_REQUEST)) {
+ QWidget *w = QWidget::find(event->xany.window);
+ if (QTLWExtra *tlw = ((QETWidget*)w)->d_func()->maybeTopData()) {
+ const ulong timestamp = (const ulong) event->xclient.data.l[1];
+ if (timestamp > X11->time)
+ X11->time = timestamp;
+ if (timestamp == CurrentTime || timestamp > tlw->syncRequestTimestamp) {
+ tlw->syncRequestTimestamp = timestamp;
+ tlw->newCounterValueLo = event->xclient.data.l[2];
+ tlw->newCounterValueHi = event->xclient.data.l[3];
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+#if defined(Q_C_CALLBACKS)
+}
+#endif
+#endif // QT_NO_XSYNC
+
+static void qt_x11_create_intern_atoms()
+{
+ const char *names[QX11Data::NAtoms];
+ const char *ptr = x11_atomnames;
+
+ int i = 0;
+ while (*ptr) {
+ names[i++] = ptr;
+ while (*ptr)
+ ++ptr;
+ ++ptr;
+ }
+
+ Q_ASSERT(i == QX11Data::NPredefinedAtoms);
+
+ QByteArray settings_atom_name("_QT_SETTINGS_TIMESTAMP_");
+ settings_atom_name += XDisplayName(X11->displayName);
+ names[i++] = settings_atom_name;
+
+ Q_ASSERT(i == QX11Data::NAtoms);
+#if defined(XlibSpecificationRelease) && (XlibSpecificationRelease >= 6)
+ XInternAtoms(X11->display, (char **)names, i, False, X11->atoms);
+#else
+ for (i = 0; i < QX11Data::NAtoms; ++i)
+ X11->atoms[i] = XInternAtom(X11->display, (char *)names[i], False);
+#endif
+}
+
+Q_WIDGETS_EXPORT void qt_x11_apply_settings_in_all_apps()
+{
+ QByteArray stamp;
+ QDataStream s(&stamp, QIODevice::WriteOnly);
+ s << QDateTime::currentDateTime();
+
+ XChangeProperty(QX11Info::display(), QX11Info::appRootWindow(0),
+ ATOM(_QT_SETTINGS_TIMESTAMP), ATOM(_QT_SETTINGS_TIMESTAMP), 8,
+ PropModeReplace, (unsigned char *)stamp.data(), stamp.size());
+}
+
+/*! \internal
+ apply the settings to the application
+*/
+bool QApplicationPrivate::x11_apply_settings()
+{
+ QSettings settings(QSettings::UserScope, QLatin1String("Trolltech"));
+
+ settings.beginGroup(QLatin1String("Qt"));
+
+ /*
+ Qt settings. This is now they are written into the datastream.
+
+ Palette / * - QPalette
+ font - QFont
+ libraryPath - QStringList
+ style - QString
+ doubleClickInterval - int
+ keyboardInputInterval - int
+ cursorFlashTime - int
+ wheelScrollLines - int
+ colorSpec - QString
+ defaultCodec - QString
+ globalStrut/width - int
+ globalStrut/height - int
+ GUIEffects - QStringList
+ Font Substitutions/ * - QStringList
+ Font Substitutions/... - QStringList
+ */
+
+ QStringList strlist;
+ int i;
+ QPalette pal(Qt::black);
+ int groupCount = 0;
+ strlist = settings.value(QLatin1String("Palette/active")).toStringList();
+ if (!strlist.isEmpty()) {
+ ++groupCount;
+ for (i = 0; i < qMin(strlist.count(), int(QPalette::NColorRoles)); i++)
+ pal.setColor(QPalette::Active, (QPalette::ColorRole) i,
+ QColor(strlist[i]));
+ }
+ strlist = settings.value(QLatin1String("Palette/inactive")).toStringList();
+ if (!strlist.isEmpty()) {
+ ++groupCount;
+ for (i = 0; i < qMin(strlist.count(), int(QPalette::NColorRoles)); i++)
+ pal.setColor(QPalette::Inactive, (QPalette::ColorRole) i,
+ QColor(strlist[i]));
+ }
+ strlist = settings.value(QLatin1String("Palette/disabled")).toStringList();
+ if (!strlist.isEmpty()) {
+ ++groupCount;
+ for (i = 0; i < qMin(strlist.count(), int(QPalette::NColorRoles)); i++)
+ pal.setColor(QPalette::Disabled, (QPalette::ColorRole) i,
+ QColor(strlist[i]));
+ }
+
+ // ### Fix properly for 4.6
+ bool usingGtkSettings = QApplicationPrivate::app_style && QApplicationPrivate::app_style->inherits("QGtkStyle");
+ if (!usingGtkSettings) {
+ if (groupCount == QPalette::NColorGroups)
+ QApplicationPrivate::setSystemPalette(pal);
+ }
+
+ if (!appFont) {
+ // ### Fix properly for 4.6
+ if (!usingGtkSettings) {
+ QFont font(QApplication::font());
+ QString fontDescription;
+ // Override Qt font if KDE4 settings can be used
+ if (X11->desktopVersion == 4) {
+ QSettings kdeSettings(QKde::kdeHome() + QLatin1String("/share/config/kdeglobals"), QSettings::IniFormat);
+ fontDescription = kdeSettings.value(QLatin1String("font")).toString();
+ if (fontDescription.isEmpty()) {
+ // KDE stores fonts without quotes
+ fontDescription = kdeSettings.value(QLatin1String("font")).toStringList().join(QLatin1String(","));
+ }
+ }
+ if (fontDescription.isEmpty())
+ fontDescription = settings.value(QLatin1String("font")).toString();
+ if (!fontDescription .isEmpty()) {
+ font.fromString(fontDescription );
+ QApplicationPrivate::setSystemFont(font);
+ }
+ }
+ }
+
+ // read library (ie. plugin) path list
+ QString libpathkey =
+ QString::fromLatin1("%1.%2/libraryPath")
+ .arg(QT_VERSION >> 16)
+ .arg((QT_VERSION & 0xff00) >> 8);
+ QStringList pathlist = settings.value(libpathkey).toString().split(QLatin1Char(':'));
+ if (! pathlist.isEmpty()) {
+ QStringList::ConstIterator it = pathlist.constBegin();
+ while (it != pathlist.constEnd())
+ QApplication::addLibraryPath(*it++);
+ }
+
+ // read new QStyle
+ QString stylename = settings.value(QLatin1String("style")).toString();
+
+ if (stylename.isEmpty() && QApplicationPrivate::styleOverride.isNull() && X11->use_xrender) {
+ stylename = qt_guiPlatformPlugin()->styleName();
+ }
+
+ static QString currentStyleName = stylename;
+ if (QCoreApplication::startingUp()) {
+ if (!stylename.isEmpty() && QApplicationPrivate::styleOverride.isNull())
+ QApplicationPrivate::styleOverride = stylename;
+ } else {
+ if (currentStyleName != stylename) {
+ currentStyleName = stylename;
+ QApplication::setStyle(stylename);
+ }
+ }
+
+ int num =
+ settings.value(QLatin1String("doubleClickInterval"),
+ QApplication::doubleClickInterval()).toInt();
+ QApplication::setDoubleClickInterval(num);
+
+ num =
+ settings.value(QLatin1String("cursorFlashTime"),
+ QApplication::cursorFlashTime()).toInt();
+ QApplication::setCursorFlashTime(num);
+
+#ifndef QT_NO_WHEELEVENT
+ num =
+ settings.value(QLatin1String("wheelScrollLines"),
+ QApplication::wheelScrollLines()).toInt();
+ QApplication::setWheelScrollLines(num);
+#endif
+
+ QString colorspec = settings.value(QLatin1String("colorSpec"),
+ QVariant(QLatin1String("default"))).toString();
+ if (colorspec == QLatin1String("normal"))
+ QApplication::setColorSpec(QApplication::NormalColor);
+ else if (colorspec == QLatin1String("custom"))
+ QApplication::setColorSpec(QApplication::CustomColor);
+ else if (colorspec == QLatin1String("many"))
+ QApplication::setColorSpec(QApplication::ManyColor);
+ else if (colorspec != QLatin1String("default"))
+ colorspec = QLatin1String("default");
+
+ QString defaultcodec = settings.value(QLatin1String("defaultCodec"),
+ QVariant(QLatin1String("none"))).toString();
+ if (defaultcodec != QLatin1String("none")) {
+ QTextCodec *codec = QTextCodec::codecForName(defaultcodec.toLatin1());
+ if (codec)
+ QTextCodec::setCodecForTr(codec);
+ }
+
+ int w = settings.value(QLatin1String("globalStrut/width")).toInt();
+ int h = settings.value(QLatin1String("globalStrut/height")).toInt();
+ QSize strut(w, h);
+ if (strut.isValid())
+ QApplication::setGlobalStrut(strut);
+
+ QStringList effects = settings.value(QLatin1String("GUIEffects")).toStringList();
+ QApplication::setEffectEnabled(Qt::UI_General,
+ effects.contains(QLatin1String("general")));
+ QApplication::setEffectEnabled(Qt::UI_AnimateMenu,
+ effects.contains(QLatin1String("animatemenu")));
+ QApplication::setEffectEnabled(Qt::UI_FadeMenu,
+ effects.contains(QLatin1String("fademenu")));
+ QApplication::setEffectEnabled(Qt::UI_AnimateCombo,
+ effects.contains(QLatin1String("animatecombo")));
+ QApplication::setEffectEnabled(Qt::UI_AnimateTooltip,
+ effects.contains(QLatin1String("animatetooltip")));
+ QApplication::setEffectEnabled(Qt::UI_FadeTooltip,
+ effects.contains(QLatin1String("fadetooltip")));
+ QApplication::setEffectEnabled(Qt::UI_AnimateToolBox,
+ effects.contains(QLatin1String("animatetoolbox")));
+
+ if (!X11->has_fontconfig) {
+ settings.beginGroup(QLatin1String("Font Substitutions"));
+ QStringList fontsubs = settings.childKeys();
+ if (!fontsubs.isEmpty()) {
+ QStringList::Iterator it = fontsubs.begin();
+ for (; it != fontsubs.end(); ++it) {
+ QString fam = *it;
+ QStringList subs = settings.value(fam).toStringList();
+ QFont::insertSubstitutions(fam, subs);
+ }
+ }
+ settings.endGroup();
+ }
+
+ qt_use_rtl_extensions =
+ settings.value(QLatin1String("useRtlExtensions"), false).toBool();
+
+#ifndef QT_NO_XIM
+ if (qt_xim_preferred_style == 0) {
+ QString ximInputStyle = settings.value(QLatin1String("XIMInputStyle"),
+ QVariant(QLatin1String("on the spot"))).toString().toLower();
+ if (ximInputStyle == QLatin1String("on the spot"))
+ qt_xim_preferred_style = XIMPreeditCallbacks | XIMStatusNothing;
+ else if (ximInputStyle == QLatin1String("over the spot"))
+ qt_xim_preferred_style = XIMPreeditPosition | XIMStatusNothing;
+ else if (ximInputStyle == QLatin1String("off the spot"))
+ qt_xim_preferred_style = XIMPreeditArea | XIMStatusArea;
+ else if (ximInputStyle == QLatin1String("root"))
+ qt_xim_preferred_style = XIMPreeditNothing | XIMStatusNothing;
+ }
+#endif
+ QStringList inputMethods = QInputContextFactory::keys();
+ if (inputMethods.size() > 2 && inputMethods.contains(QLatin1String("imsw-multi"))) {
+ X11->default_im = QLatin1String("imsw-multi");
+ } else {
+ X11->default_im = settings.value(QLatin1String("DefaultInputMethod"),
+ QLatin1String("xim")).toString();
+ }
+
+ settings.endGroup(); // Qt
+
+ return true;
+}
+
+
+/*! \internal
+ Resets the QApplication::instance() pointer to zero
+*/
+void QApplicationPrivate::reset_instance_pointer()
+{ QApplication::self = 0; }
+
+
+// read the _QT_INPUT_ENCODING property and apply the settings to
+// the application
+static void qt_set_input_encoding()
+{
+ Atom type;
+ int format;
+ ulong nitems, after = 1;
+ unsigned char *data = 0;
+
+ int e = XGetWindowProperty(X11->display, QX11Info::appRootWindow(),
+ ATOM(_QT_INPUT_ENCODING), 0, 1024,
+ False, XA_STRING, &type, &format, &nitems,
+ &after, &data);
+ if (e != Success || !nitems || type == XNone) {
+ // Always use the locale codec, since we have no examples of non-local
+ // XIMs, and since we cannot get a sensible answer about the encoding
+ // from the XIM.
+ qt_input_mapper = QTextCodec::codecForLocale();
+
+ } else {
+ if (!qstricmp((char *)data, "locale"))
+ qt_input_mapper = QTextCodec::codecForLocale();
+ else
+ qt_input_mapper = QTextCodec::codecForName((char *)data);
+ // make sure we have an input codec
+ if(!qt_input_mapper)
+ qt_input_mapper = QTextCodec::codecForName("ISO 8859-1");
+ }
+ if (qt_input_mapper && qt_input_mapper->mibEnum() == 11) // 8859-8
+ qt_input_mapper = QTextCodec::codecForName("ISO 8859-8-I");
+ if(data)
+ XFree((char *)data);
+}
+
+// set font, foreground and background from x11 resources. The
+// arguments may override the resource settings.
+static void qt_set_x11_resources(const char* font = 0, const char* fg = 0,
+ const char* bg = 0, const char* button = 0)
+{
+
+ QString resFont, resFG, resBG, resButton, resEF, sysFont, selectBackground, selectForeground;
+
+ QApplication::setEffectEnabled(Qt::UI_General, false);
+ QApplication::setEffectEnabled(Qt::UI_AnimateMenu, false);
+ QApplication::setEffectEnabled(Qt::UI_FadeMenu, false);
+ QApplication::setEffectEnabled(Qt::UI_AnimateCombo, false);
+ QApplication::setEffectEnabled(Qt::UI_AnimateTooltip, false);
+ QApplication::setEffectEnabled(Qt::UI_FadeTooltip, false);
+ QApplication::setEffectEnabled(Qt::UI_AnimateToolBox, false);
+
+ bool paletteAlreadySet = false;
+ if (QApplication::desktopSettingsAware()) {
+ // first, read from settings
+ QApplicationPrivate::x11_apply_settings();
+ // the call to QApplication::style() below creates the system
+ // palette, which breaks the logic after the RESOURCE_MANAGER
+ // loop... so I have to save this value to be able to use it later
+ paletteAlreadySet = (QApplicationPrivate::sys_pal != 0);
+
+ // second, parse the RESOURCE_MANAGER property
+ int format;
+ ulong nitems, after = 1;
+ QString res;
+ long offset = 0;
+ Atom type = XNone;
+
+ while (after > 0) {
+ uchar *data = 0;
+ if (XGetWindowProperty(X11->display, QX11Info::appRootWindow(0),
+ ATOM(RESOURCE_MANAGER),
+ offset, 8192, False, AnyPropertyType,
+ &type, &format, &nitems, &after,
+ &data) != Success) {
+ res = QString();
+ break;
+ }
+ if (type == XA_STRING)
+ res += QString::fromLatin1((char*)data);
+ else
+ res += QString::fromLocal8Bit((char*)data);
+ offset += 2048; // offset is in 32bit quantities... 8192/4 == 2048
+ if (data)
+ XFree((char *)data);
+ }
+
+ QString key, value;
+ int l = 0, r;
+ QString apn = QString::fromLocal8Bit(appName);
+ QString apc = QString::fromLocal8Bit(appClass);
+ int apnl = apn.length();
+ int apcl = apc.length();
+ int resl = res.length();
+
+ while (l < resl) {
+ r = res.indexOf(QLatin1Char('\n'), l);
+ if (r < 0)
+ r = resl;
+ while (res.at(l).isSpace())
+ l++;
+ bool mine = false;
+ QChar sc = res.at(l + 1);
+ if (res.at(l) == QLatin1Char('*') &&
+ (sc == QLatin1Char('f') || sc == QLatin1Char('b') || sc == QLatin1Char('g') ||
+ sc == QLatin1Char('F') || sc == QLatin1Char('B') || sc == QLatin1Char('G') ||
+ sc == QLatin1Char('s') || sc == QLatin1Char('S')
+ // capital T only, since we're looking for "Text.selectSomething"
+ || sc == QLatin1Char('T'))) {
+ // OPTIMIZED, since we only want "*[fbgsT].."
+ QString item = res.mid(l, r - l).simplified();
+ int i = item.indexOf(QLatin1Char(':'));
+ key = item.left(i).trimmed().mid(1).toLower();
+ value = item.right(item.length() - i - 1).trimmed();
+ mine = true;
+ } else if ((apnl && res.at(l) == apn.at(0)) || (appClass && apcl && res.at(l) == apc.at(0))) {
+ if (res.mid(l,apnl) == apn && (res.at(l+apnl) == QLatin1Char('.')
+ || res.at(l+apnl) == QLatin1Char('*'))) {
+ QString item = res.mid(l, r - l).simplified();
+ int i = item.indexOf(QLatin1Char(':'));
+ key = item.left(i).trimmed().mid(apnl+1).toLower();
+ value = item.right(item.length() - i - 1).trimmed();
+ mine = true;
+ } else if (res.mid(l,apcl) == apc && (res.at(l+apcl) == QLatin1Char('.')
+ || res.at(l+apcl) == QLatin1Char('*'))) {
+ QString item = res.mid(l, r - l).simplified();
+ int i = item.indexOf(QLatin1Char(':'));
+ key = item.left(i).trimmed().mid(apcl+1).toLower();
+ value = item.right(item.length() - i - 1).trimmed();
+ mine = true;
+ }
+ }
+
+ if (mine) {
+ if (!font && key == QLatin1String("systemfont"))
+ sysFont = value.left(value.lastIndexOf(QLatin1Char(':')));
+ if (!font && key == QLatin1String("font"))
+ resFont = value;
+ else if (!fg && !paletteAlreadySet) {
+ if (key == QLatin1String("foreground"))
+ resFG = value;
+ else if (!bg && key == QLatin1String("background"))
+ resBG = value;
+ else if (!bg && !button && key == QLatin1String("button.background"))
+ resButton = value;
+ else if (key == QLatin1String("text.selectbackground")) {
+ selectBackground = value;
+ } else if (key == QLatin1String("text.selectforeground")) {
+ selectForeground = value;
+ }
+ } else if (key == QLatin1String("guieffects"))
+ resEF = value;
+ // NOTE: if you add more, change the [fbg] stuff above
+ }
+
+ l = r + 1;
+ }
+ }
+ if (!sysFont.isEmpty())
+ resFont = sysFont;
+ if (resFont.isEmpty())
+ resFont = QString::fromLocal8Bit(font);
+ if (resFG.isEmpty())
+ resFG = QString::fromLocal8Bit(fg);
+ if (resBG.isEmpty())
+ resBG = QString::fromLocal8Bit(bg);
+ if (resButton.isEmpty())
+ resButton = QString::fromLocal8Bit(button);
+ if (!resFont.isEmpty()
+ && !X11->has_fontconfig
+ && !QApplicationPrivate::sys_font) {
+ // set application font
+ QFont fnt;
+ fnt.setRawName(resFont);
+
+ // the font we get may actually be an alias for another font,
+ // so we reset the application font to the real font info.
+ if (! fnt.exactMatch()) {
+ QFontInfo fontinfo(fnt);
+ fnt.setFamily(fontinfo.family());
+ fnt.setRawMode(fontinfo.rawMode());
+
+ if (! fnt.rawMode()) {
+ fnt.setItalic(fontinfo.italic());
+ fnt.setWeight(fontinfo.weight());
+ fnt.setUnderline(fontinfo.underline());
+ fnt.setStrikeOut(fontinfo.strikeOut());
+ fnt.setStyleHint(fontinfo.styleHint());
+
+ if (fnt.pointSize() <= 0 && fnt.pixelSize() <= 0) {
+ // size is all wrong... fix it
+ qreal pointSize = fontinfo.pixelSize() * 72. / (float) QX11Info::appDpiY();
+ if (pointSize <= 0)
+ pointSize = 12;
+ fnt.setPointSize(qRound(pointSize));
+ }
+ }
+ }
+
+ QApplicationPrivate::setSystemFont(fnt);
+ }
+ // QGtkStyle sets it's own system palette
+ bool gtkStyle = QApplicationPrivate::app_style && QApplicationPrivate::app_style->inherits("QGtkStyle");
+ bool kdeColors = (QApplication::desktopSettingsAware() && X11->desktopEnvironment == DE_KDE);
+ if (!gtkStyle && (kdeColors || (button || !resBG.isEmpty() || !resFG.isEmpty()))) {// set app colors
+ bool allowX11ColorNames = QColor::allowX11ColorNames();
+ QColor::setAllowX11ColorNames(true);
+
+ (void) QApplication::style(); // trigger creation of application style and system palettes
+ QColor btn;
+ QColor bg;
+ QColor fg;
+ QColor bfg;
+ QColor wfg;
+ if (!resBG.isEmpty())
+ bg = QColor(resBG);
+ if (!bg.isValid())
+ bg = QApplicationPrivate::sys_pal->color(QPalette::Active, QPalette::Window);
+
+ if (!resFG.isEmpty())
+ fg = QColor(resFG);
+ if (!fg.isValid())
+ fg = QApplicationPrivate::sys_pal->color(QPalette::Active, QPalette::WindowText);
+
+ if (!resButton.isEmpty())
+ btn = QColor(resButton);
+ else if (!resBG.isEmpty())
+ btn = bg;
+ if (!btn.isValid())
+ btn = QApplicationPrivate::sys_pal->color(QPalette::Active, QPalette::Button);
+
+ int h,s,v;
+ fg.getHsv(&h,&s,&v);
+ QColor base = Qt::white;
+ bool bright_mode = false;
+ if (v >= 255 - 50) {
+ base = btn.darker(150);
+ bright_mode = true;
+ }
+
+ QPalette pal(fg, btn, btn.lighter(125), btn.darker(130), btn.darker(120), wfg.isValid() ? wfg : fg, Qt::white, base, bg);
+ QColor disabled((fg.red() + btn.red()) / 2,
+ (fg.green() + btn.green())/ 2,
+ (fg.blue() + btn.blue()) / 2);
+ pal.setColorGroup(QPalette::Disabled, disabled, btn, btn.lighter(125),
+ btn.darker(130), btn.darker(150), disabled, Qt::white, Qt::white, bg);
+
+ QColor highlight, highlightText;
+ if (!selectBackground.isEmpty() && !selectForeground.isEmpty()) {
+ highlight = QColor(selectBackground);
+ highlightText = QColor(selectForeground);
+ }
+
+ if (highlight.isValid() && highlightText.isValid()) {
+ pal.setColor(QPalette::Highlight, highlight);
+ pal.setColor(QPalette::HighlightedText, highlightText);
+
+ // calculate disabled colors by removing saturation
+ highlight.setHsv(highlight.hue(), 0, highlight.value(), highlight.alpha());
+ highlightText.setHsv(highlightText.hue(), 0, highlightText.value(), highlightText.alpha());
+ pal.setColor(QPalette::Disabled, QPalette::Highlight, highlight);
+ pal.setColor(QPalette::Disabled, QPalette::HighlightedText, highlightText);
+ } else if (bright_mode) {
+ pal.setColor(QPalette::HighlightedText, base);
+ pal.setColor(QPalette::Highlight, Qt::white);
+ pal.setColor(QPalette::Disabled, QPalette::HighlightedText, base);
+ pal.setColor(QPalette::Disabled, QPalette::Highlight, Qt::white);
+ } else {
+ pal.setColor(QPalette::HighlightedText, Qt::white);
+ pal.setColor(QPalette::Highlight, Qt::darkBlue);
+ pal.setColor(QPalette::Disabled, QPalette::HighlightedText, Qt::white);
+ pal.setColor(QPalette::Disabled, QPalette::Highlight, Qt::darkBlue);
+ }
+
+ pal = qt_guiPlatformPlugin()->palette().resolve(pal);
+ QApplicationPrivate::setSystemPalette(pal);
+ QColor::setAllowX11ColorNames(allowX11ColorNames);
+ }
+
+ if (!resEF.isEmpty()) {
+ QStringList effects = resEF.split(QLatin1Char(' '));
+ QApplication::setEffectEnabled(Qt::UI_General, effects.contains(QLatin1String("general")));
+ QApplication::setEffectEnabled(Qt::UI_AnimateMenu,
+ effects.contains(QLatin1String("animatemenu")));
+ QApplication::setEffectEnabled(Qt::UI_FadeMenu,
+ effects.contains(QLatin1String("fademenu")));
+ QApplication::setEffectEnabled(Qt::UI_AnimateCombo,
+ effects.contains(QLatin1String("animatecombo")));
+ QApplication::setEffectEnabled(Qt::UI_AnimateTooltip,
+ effects.contains(QLatin1String("animatetooltip")));
+ QApplication::setEffectEnabled(Qt::UI_FadeTooltip,
+ effects.contains(QLatin1String("fadetooltip")));
+ QApplication::setEffectEnabled(Qt::UI_AnimateToolBox,
+ effects.contains(QLatin1String("animatetoolbox")));
+ }
+
+ QIconLoader::instance()->updateSystemTheme();
+}
+
+
+// update the supported array
+static void qt_get_net_supported()
+{
+ Atom type;
+ int format;
+ long offset = 0;
+ unsigned long nitems, after;
+ unsigned char *data = 0;
+
+ int e = XGetWindowProperty(X11->display, QX11Info::appRootWindow(),
+ ATOM(_NET_SUPPORTED), 0, 0,
+ False, XA_ATOM, &type, &format, &nitems, &after, &data);
+ if (data)
+ XFree(data);
+
+ if (X11->net_supported_list)
+ delete [] X11->net_supported_list;
+ X11->net_supported_list = 0;
+
+ if (e == Success && type == XA_ATOM && format == 32) {
+ QBuffer ts;
+ ts.open(QIODevice::WriteOnly);
+
+ while (after > 0) {
+ XGetWindowProperty(X11->display, QX11Info::appRootWindow(),
+ ATOM(_NET_SUPPORTED), offset, 1024,
+ False, XA_ATOM, &type, &format, &nitems, &after, &data);
+
+ if (type == XA_ATOM && format == 32) {
+ ts.write(reinterpret_cast<char *>(data), nitems * sizeof(long));
+ offset += nitems;
+ } else
+ after = 0;
+ if (data)
+ XFree(data);
+ }
+
+ // compute nitems
+ QByteArray buffer(ts.buffer());
+ nitems = buffer.size() / sizeof(Atom);
+ X11->net_supported_list = new Atom[nitems + 1];
+ Atom *a = (Atom *) buffer.data();
+ uint i;
+ for (i = 0; i < nitems; i++)
+ X11->net_supported_list[i] = a[i];
+ X11->net_supported_list[nitems] = 0;
+ }
+}
+
+
+bool QX11Data::isSupportedByWM(Atom atom)
+{
+ if (!X11->net_supported_list)
+ return false;
+
+ bool supported = false;
+ int i = 0;
+ while (X11->net_supported_list[i] != 0) {
+ if (X11->net_supported_list[i++] == atom) {
+ supported = true;
+ break;
+ }
+ }
+
+ return supported;
+}
+
+
+// update the virtual roots array
+static void qt_get_net_virtual_roots()
+{
+ if (X11->net_virtual_root_list)
+ delete [] X11->net_virtual_root_list;
+ X11->net_virtual_root_list = 0;
+
+ if (!X11->isSupportedByWM(ATOM(_NET_VIRTUAL_ROOTS)))
+ return;
+
+ Atom type;
+ int format;
+ long offset = 0;
+ unsigned long nitems, after;
+ unsigned char *data;
+
+ int e = XGetWindowProperty(X11->display, QX11Info::appRootWindow(),
+ ATOM(_NET_VIRTUAL_ROOTS), 0, 0,
+ False, XA_ATOM, &type, &format, &nitems, &after, &data);
+ if (data)
+ XFree(data);
+
+ if (e == Success && type == XA_ATOM && format == 32) {
+ QBuffer ts;
+ ts.open(QIODevice::WriteOnly);
+
+ while (after > 0) {
+ XGetWindowProperty(X11->display, QX11Info::appRootWindow(),
+ ATOM(_NET_VIRTUAL_ROOTS), offset, 1024,
+ False, XA_ATOM, &type, &format, &nitems, &after, &data);
+
+ if (type == XA_ATOM && format == 32) {
+ ts.write(reinterpret_cast<char *>(data), nitems * 4);
+ offset += nitems;
+ } else
+ after = 0;
+ if (data)
+ XFree(data);
+ }
+
+ // compute nitems
+ QByteArray buffer(ts.buffer());
+ nitems = buffer.size() / sizeof(Window);
+ X11->net_virtual_root_list = new Window[nitems + 1];
+ Window *a = (Window *) buffer.data();
+ uint i;
+ for (i = 0; i < nitems; i++)
+ X11->net_virtual_root_list[i] = a[i];
+ X11->net_virtual_root_list[nitems] = 0;
+ }
+}
+
+void qt_net_remove_user_time(QWidget *tlw)
+{
+ Q_ASSERT(tlw);
+ QTLWExtra *extra = tlw->d_func()->maybeTopData();
+ if (extra && extra->userTimeWindow) {
+ Q_ASSERT(tlw->internalWinId());
+ XDeleteProperty(X11->display, tlw->internalWinId(), ATOM(_NET_WM_USER_TIME_WINDOW));
+ XDestroyWindow(X11->display, extra->userTimeWindow);
+ extra->userTimeWindow = 0;
+ }
+}
+
+void qt_net_update_user_time(QWidget *tlw, unsigned long timestamp)
+{
+ Q_ASSERT(tlw);
+ Q_ASSERT(tlw->isWindow());
+ Q_ASSERT(tlw->testAttribute(Qt::WA_WState_Created));
+ QTLWExtra *extra = tlw->d_func()->topData();
+ WId wid = tlw->internalWinId();
+ const bool isSupportedByWM = X11->isSupportedByWM(ATOM(_NET_WM_USER_TIME_WINDOW));
+ if (extra->userTimeWindow || isSupportedByWM) {
+ if (!extra->userTimeWindow) {
+ extra->userTimeWindow = XCreateSimpleWindow(X11->display,
+ tlw->internalWinId(),
+ -1, -1, 1, 1, 0, 0, 0);
+ wid = extra->userTimeWindow;
+ XChangeProperty(X11->display, tlw->internalWinId(), ATOM(_NET_WM_USER_TIME_WINDOW),
+ XA_WINDOW, 32, PropModeReplace,
+ (unsigned char *)&wid, 1);
+ XDeleteProperty(X11->display, tlw->internalWinId(), ATOM(_NET_WM_USER_TIME));
+ } else if (!isSupportedByWM) {
+ // WM no longer supports it, then we should remove the
+ // _NET_WM_USER_TIME_WINDOW atom.
+ qt_net_remove_user_time(tlw);
+ } else {
+ wid = extra->userTimeWindow;
+ }
+ }
+ XChangeProperty(X11->display, wid, ATOM(_NET_WM_USER_TIME),
+ XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &timestamp, 1);
+}
+
+static void qt_check_focus_model()
+{
+ Window fw = XNone;
+ int unused;
+ XGetInputFocus(X11->display, &fw, &unused);
+ if (fw == PointerRoot)
+ X11->focus_model = QX11Data::FM_PointerRoot;
+ else
+ X11->focus_model = QX11Data::FM_Other;
+}
+
+#ifndef QT_NO_TABLET
+
+#if !defined (Q_OS_IRIX)
+// from include/Xwacom.h
+# define XWACOM_PARAM_TOOLID 322
+# define XWACOM_PARAM_TOOLSERIAL 323
+
+typedef WACOMCONFIG * (*PtrWacomConfigInit) (Display*, WACOMERRORFUNC);
+typedef WACOMDEVICE * (*PtrWacomConfigOpenDevice) (WACOMCONFIG*, const char*);
+typedef int *(*PtrWacomConfigGetRawParam) (WACOMDEVICE*, int, int*, int, unsigned*);
+typedef int (*PtrWacomConfigCloseDevice) (WACOMDEVICE *);
+typedef void (*PtrWacomConfigTerm) (WACOMCONFIG *);
+
+static PtrWacomConfigInit ptrWacomConfigInit = 0;
+static PtrWacomConfigOpenDevice ptrWacomConfigOpenDevice = 0;
+static PtrWacomConfigGetRawParam ptrWacomConfigGetRawParam = 0;
+static PtrWacomConfigCloseDevice ptrWacomConfigCloseDevice = 0;
+static PtrWacomConfigTerm ptrWacomConfigTerm = 0;
+Q_GLOBAL_STATIC(QByteArray, wacomDeviceName)
+#endif
+
+#endif
+
+/*****************************************************************************
+ qt_init() - initializes Qt for X11
+ *****************************************************************************/
+
+#if !defined(QT_NO_FONTCONFIG)
+static void getXDefault(const char *group, const char *key, int *val)
+{
+ char *str = XGetDefault(X11->display, group, key);
+ if (str) {
+ char *end = 0;
+ int v = strtol(str, &end, 0);
+ if (str != end)
+ *val = v;
+ // otherwise use fontconfig to convert the string to integer
+ else
+ FcNameConstant((FcChar8 *) str, val);
+ }
+}
+
+static void getXDefault(const char *group, const char *key, double *val)
+{
+ char *str = XGetDefault(X11->display, group, key);
+ if (str) {
+ bool ok;
+ double v = QByteArray(str).toDouble(&ok);
+ if (ok)
+ *val = v;
+ }
+}
+
+static void getXDefault(const char *group, const char *key, bool *val)
+{
+ char *str = XGetDefault(X11->display, group, key);
+ if (str) {
+ char c = str[0];
+ if (isupper((int)c))
+ c = tolower(c);
+ if (c == 't' || c == 'y' || c == '1')
+ *val = true;
+ else if (c == 'f' || c == 'n' || c == '0')
+ *val = false;
+ if (c == 'o') {
+ c = str[1];
+ if (isupper((int)c))
+ c = tolower(c);
+ if (c == 'n')
+ *val = true;
+ if (c == 'f')
+ *val = false;
+ }
+ }
+}
+#endif
+
+// ### This should be static but it isn't because of the friend declaration
+// ### in qpaintdevice.h which then should have a static too but can't have
+// ### it because "storage class specifiers invalid in friend function
+// ### declarations" :-) Ideas anyone?
+void qt_init(QApplicationPrivate *priv, int,
+ Display *display, Qt::HANDLE visual, Qt::HANDLE colormap)
+{
+ X11 = new QX11Data;
+ X11->display = display;
+ X11->displayName = 0;
+ X11->foreignDisplay = (display != 0);
+ X11->focus_model = -1;
+
+ // RANDR
+ X11->use_xrandr = false;
+ X11->xrandr_major = 0;
+ X11->xrandr_eventbase = 0;
+ X11->xrandr_errorbase = 0;
+
+ // RENDER
+ X11->use_xrender = false;
+ X11->xrender_major = 0;
+ X11->xrender_version = 0;
+
+ // XFIXES
+ X11->use_xfixes = false;
+ X11->xfixes_major = 0;
+ X11->xfixes_eventbase = 0;
+ X11->xfixes_errorbase = 0;
+
+ // XInputExtension
+ X11->use_xinput = false;
+ X11->xinput_major = 0;
+ X11->xinput_eventbase = 0;
+ X11->xinput_errorbase = 0;
+
+ X11->use_xkb = false;
+ X11->xkb_major = 0;
+ X11->xkb_eventbase = 0;
+ X11->xkb_errorbase = 0;
+
+ // MIT-SHM
+ X11->use_mitshm = false;
+ X11->use_mitshm_pixmaps = false;
+ X11->mitshm_major = 0;
+
+ X11->sip_serial = 0;
+ X11->net_supported_list = 0;
+ X11->net_virtual_root_list = 0;
+ X11->wm_client_leader = 0;
+ X11->screens = 0;
+ X11->argbVisuals = 0;
+ X11->argbColormaps = 0;
+ X11->screenCount = 0;
+ X11->time = CurrentTime;
+ X11->userTime = CurrentTime;
+ X11->ignore_badwindow = false;
+ X11->seen_badwindow = false;
+
+ X11->motifdnd_active = false;
+
+ X11->default_im = QLatin1String("imsw-multi");
+ priv->inputContext = 0;
+
+ // colormap control
+ X11->visual_class = -1;
+ X11->visual_id = -1;
+ X11->color_count = 0;
+ X11->custom_cmap = false;
+
+ // outside visual/colormap
+ X11->visual = reinterpret_cast<Visual *>(visual);
+ X11->colormap = colormap;
+
+ // Fontconfig
+ X11->has_fontconfig = false;
+#if !defined(QT_NO_FONTCONFIG)
+ if (qgetenv("QT_X11_NO_FONTCONFIG").isNull())
+ X11->has_fontconfig = FcInit();
+ X11->fc_antialias = true;
+#endif
+
+#ifndef QT_NO_XRENDER
+ memset(X11->solid_fills, 0, sizeof(X11->solid_fills));
+ for (int i = 0; i < X11->solid_fill_count; ++i)
+ X11->solid_fills[i].screen = -1;
+ memset(X11->pattern_fills, 0, sizeof(X11->pattern_fills));
+ for (int i = 0; i < X11->pattern_fill_count; ++i)
+ X11->pattern_fills[i].screen = -1;
+#endif
+
+ X11->startupId = 0;
+
+ int argc = priv->argc;
+ char **argv = priv->argv;
+
+ if (X11->display) {
+ // Qt part of other application
+
+ // Set application name and class
+ appName = qstrdup("Qt-subapplication");
+ char *app_class = 0;
+ if (argv) {
+ const char* p = strrchr(argv[0], '/');
+ app_class = qstrdup(p ? p + 1 : argv[0]);
+ if (app_class[0])
+ app_class[0] = toupper(app_class[0]);
+ }
+ appClass = app_class;
+ } else {
+ // Qt controls everything (default)
+
+ // With the threaded QML renderer, we always need this.
+ XInitThreads();
+
+ // Set application name and class
+ char *app_class = 0;
+ if (argv && argv[0]) {
+ const char *p = strrchr(argv[0], '/');
+ appName = p ? p + 1 : argv[0];
+ app_class = qstrdup(appName);
+ if (app_class[0])
+ app_class[0] = toupper(app_class[0]);
+ }
+ appClass = app_class;
+ }
+
+ // Install default error handlers
+ original_x_errhandler = XSetErrorHandler(qt_x_errhandler);
+ original_xio_errhandler = XSetIOErrorHandler(qt_xio_errhandler);
+
+ // Get command line params
+ int j = argc ? 1 : 0;
+ for (int i=1; i<argc; i++) {
+ if (argv[i] && *argv[i] != '-') {
+ argv[j++] = argv[i];
+ continue;
+ }
+ QByteArray arg(argv[i]);
+ if (arg == "-display") {
+ if (++i < argc && !X11->display)
+ X11->displayName = argv[i];
+ } else if (arg == "-fn" || arg == "-font") {
+ if (++i < argc)
+ appFont = argv[i];
+ } else if (arg == "-bg" || arg == "-background") {
+ if (++i < argc)
+ appBGCol = argv[i];
+ } else if (arg == "-btn" || arg == "-button") {
+ if (++i < argc)
+ appBTNCol = argv[i];
+ } else if (arg == "-fg" || arg == "-foreground") {
+ if (++i < argc)
+ appFGCol = argv[i];
+ } else if (arg == "-name") {
+ if (++i < argc)
+ appName = argv[i];
+ } else if (arg == "-title") {
+ if (++i < argc)
+ mwTitle = argv[i];
+ } else if (arg == "-geometry") {
+ if (++i < argc)
+ mwGeometry = argv[i];
+ } else if (arg == "-im") {
+ if (++i < argc)
+ qt_ximServer = argv[i];
+ } else if (arg == "-ncols") { // xv and netscape use this name
+ if (++i < argc)
+ X11->color_count = qMax(0,atoi(argv[i]));
+ } else if (arg == "-visual") { // xv and netscape use this name
+ if (++i < argc && !X11->visual) {
+ QString s = QString::fromLocal8Bit(argv[i]).toLower();
+ if (s == QLatin1String("staticgray"))
+ X11->visual_class = StaticGray;
+ else if (s == QLatin1String("grayscale"))
+ X11->visual_class = XGrayScale;
+ else if (s == QLatin1String("staticcolor"))
+ X11->visual_class = StaticColor;
+ else if (s == QLatin1String("pseudocolor"))
+ X11->visual_class = PseudoColor;
+ else if (s == QLatin1String("truecolor"))
+ X11->visual_class = TrueColor;
+ else if (s == QLatin1String("directcolor"))
+ X11->visual_class = DirectColor;
+ else
+ X11->visual_id = static_cast<int>(strtol(argv[i], 0, 0));
+ }
+#ifndef QT_NO_XIM
+ } else if (arg == "-inputstyle") {
+ if (++i < argc) {
+ QString s = QString::fromLocal8Bit(argv[i]).toLower();
+ if (s == QLatin1String("onthespot"))
+ qt_xim_preferred_style = XIMPreeditCallbacks |
+ XIMStatusNothing;
+ else if (s == QLatin1String("overthespot"))
+ qt_xim_preferred_style = XIMPreeditPosition |
+ XIMStatusNothing;
+ else if (s == QLatin1String("offthespot"))
+ qt_xim_preferred_style = XIMPreeditArea |
+ XIMStatusArea;
+ else if (s == QLatin1String("root"))
+ qt_xim_preferred_style = XIMPreeditNothing |
+ XIMStatusNothing;
+ }
+#endif
+ } else if (arg == "-cmap") { // xv uses this name
+ if (!X11->colormap)
+ X11->custom_cmap = true;
+ }
+ else if (arg == "-sync")
+ appSync = !appSync;
+#if defined(QT_DEBUG)
+ else if (arg == "-nograb")
+ appNoGrab = !appNoGrab;
+ else if (arg == "-dograb")
+ appDoGrab = !appDoGrab;
+#endif
+ else
+ argv[j++] = argv[i];
+ }
+
+ priv->argc = j;
+
+#if defined(QT_DEBUG) && defined(Q_OS_LINUX)
+ if (!appNoGrab && !appDoGrab) {
+ QString s;
+ s.sprintf("/proc/%d/cmdline", getppid());
+ QFile f(s);
+ if (f.open(QIODevice::ReadOnly)) {
+ s.clear();
+ char c;
+ while (f.getChar(&c) && c) {
+ if (c == '/')
+ s.clear();
+ else
+ s += QLatin1Char(c);
+ }
+ if (s == QLatin1String("gdb")) {
+ appNoGrab = true;
+ qDebug("Qt: gdb: -nograb added to command-line options.\n"
+ "\t Use the -dograb option to enforce grabbing.");
+ }
+ f.close();
+ }
+ }
+#endif
+
+ // Connect to X server
+ if (qt_is_gui_used && !X11->display) {
+ if ((X11->display = XOpenDisplay(X11->displayName)) == 0) {
+ qWarning("%s: cannot connect to X server %s", appName,
+ XDisplayName(X11->displayName));
+ QApplicationPrivate::reset_instance_pointer();
+ exit(1);
+ }
+
+ if (appSync) // if "-sync" argument
+ XSynchronize(X11->display, true);
+ }
+
+ // Common code, regardless of whether display is foreign.
+
+ // Get X parameters
+
+ if (qt_is_gui_used) {
+ X11->defaultScreen = DefaultScreen(X11->display);
+ X11->screenCount = ScreenCount(X11->display);
+
+ X11->screens = new QX11InfoData[X11->screenCount];
+ X11->argbVisuals = new Visual *[X11->screenCount];
+ X11->argbColormaps = new Colormap[X11->screenCount];
+
+ for (int s = 0; s < X11->screenCount; s++) {
+ QX11InfoData *screen = X11->screens + s;
+ screen->ref = 1; // ensures it doesn't get deleted
+ screen->screen = s;
+
+ int widthMM = DisplayWidthMM(X11->display, s);
+ if (widthMM != 0) {
+ screen->dpiX = (DisplayWidth(X11->display, s) * 254 + widthMM * 5) / (widthMM * 10);
+ } else {
+ screen->dpiX = 72;
+ }
+
+ int heightMM = DisplayHeightMM(X11->display, s);
+ if (heightMM != 0) {
+ screen->dpiY = (DisplayHeight(X11->display, s) * 254 + heightMM * 5) / (heightMM * 10);
+ } else {
+ screen->dpiY = 72;
+ }
+
+ X11->argbVisuals[s] = 0;
+ X11->argbColormaps[s] = 0;
+ }
+
+
+#ifndef QT_NO_XRENDER
+ int xrender_eventbase, xrender_errorbase;
+ // See if XRender is supported on the connected display
+ if (XQueryExtension(X11->display, "RENDER", &X11->xrender_major,
+ &xrender_eventbase, &xrender_errorbase)
+ && XRenderQueryExtension(X11->display, &xrender_eventbase,
+ &xrender_errorbase)) {
+ // Check the version as well - we need v0.4 or higher
+ int major = 0;
+ int minor = 0;
+ XRenderQueryVersion(X11->display, &major, &minor);
+ if (qgetenv("QT_X11_NO_XRENDER").isNull()) {
+ X11->use_xrender = (major >= 0 && minor >= 5);
+ X11->xrender_version = major*100+minor;
+ // workaround for broken XServer on Ubuntu Breezy (6.8 compiled with 7.0
+ // protocol headers)
+ if (X11->xrender_version == 10
+ && VendorRelease(X11->display) < 60900000
+ && QByteArray(ServerVendor(X11->display)).contains("X.Org"))
+ X11->xrender_version = 9;
+ }
+ }
+#endif // QT_NO_XRENDER
+
+#ifndef QT_NO_MITSHM
+ int mitshm_minor;
+ int mitshm_major;
+ int mitshm_eventbase;
+ int mitshm_errorbase;
+ int mitshm_pixmaps;
+ if (XQueryExtension(X11->display, "MIT-SHM", &X11->mitshm_major,
+ &mitshm_eventbase, &mitshm_errorbase)
+ && XShmQueryVersion(X11->display, &mitshm_major, &mitshm_minor,
+ &mitshm_pixmaps))
+ {
+ QString displayName = QLatin1String(XDisplayName(NULL));
+
+ // MITSHM only works for local displays, so do a quick check here
+ // to determine whether the display is local or not (not 100 % accurate).
+ // BGR server layouts are not supported either, since it requires the raster
+ // engine to work on a QImage with BGR layout.
+ bool local = displayName.isEmpty() || displayName.lastIndexOf(QLatin1Char(':')) == 0;
+ if (local && (qgetenv("QT_X11_NO_MITSHM").toInt() == 0)) {
+ Visual *defaultVisual = DefaultVisual(X11->display, DefaultScreen(X11->display));
+ X11->use_mitshm = ((defaultVisual->red_mask == 0xff0000
+ || defaultVisual->red_mask == 0xf800)
+ && (defaultVisual->green_mask == 0xff00
+ || defaultVisual->green_mask == 0x7e0)
+ && (defaultVisual->blue_mask == 0xff
+ || defaultVisual->blue_mask == 0x1f));
+ X11->use_mitshm_pixmaps = X11->use_mitshm && mitshm_pixmaps;
+ }
+ }
+#endif // QT_NO_MITSHM
+
+ // initialize the graphics system - order is imporant here - it must be done before
+ // the QColormap::initialize() call
+ QApplicationPrivate::graphics_system = QGraphicsSystemFactory::create(QApplicationPrivate::graphics_system_name);
+ QColormap::initialize();
+
+ // Support protocols
+ X11->xdndSetup();
+
+ // Finally create all atoms
+ qt_x11_create_intern_atoms();
+
+ // initialize NET lists
+ qt_get_net_supported();
+ qt_get_net_virtual_roots();
+
+#ifndef QT_NO_XRANDR
+ // See if XRandR is supported on the connected display
+ if (XQueryExtension(X11->display, "RANDR", &X11->xrandr_major,
+ &X11->xrandr_eventbase, &X11->xrandr_errorbase)) {
+
+# ifdef QT_RUNTIME_XRANDR
+ X11->ptrXRRSelectInput = 0;
+ X11->ptrXRRUpdateConfiguration = 0;
+ X11->ptrXRRRootToScreen = 0;
+ X11->ptrXRRQueryExtension = 0;
+ QLibrary xrandrLib(QLatin1String("Xrandr"), 2);
+ if (!xrandrLib.load()) { // try without the version number
+ xrandrLib.setFileName(QLatin1String("Xrandr"));
+ xrandrLib.load();
+ }
+ if (xrandrLib.isLoaded()) {
+ X11->ptrXRRSelectInput =
+ (PtrXRRSelectInput) xrandrLib.resolve("XRRSelectInput");
+ X11->ptrXRRUpdateConfiguration =
+ (PtrXRRUpdateConfiguration) xrandrLib.resolve("XRRUpdateConfiguration");
+ X11->ptrXRRRootToScreen =
+ (PtrXRRRootToScreen) xrandrLib.resolve("XRRRootToScreen");
+ X11->ptrXRRQueryExtension =
+ (PtrXRRQueryExtension) xrandrLib.resolve("XRRQueryExtension");
+ X11->ptrXRRSizes =
+ (PtrXRRSizes) xrandrLib.resolve("XRRSizes");
+ }
+# else
+ X11->ptrXRRSelectInput = XRRSelectInput;
+ X11->ptrXRRUpdateConfiguration = XRRUpdateConfiguration;
+ X11->ptrXRRRootToScreen = XRRRootToScreen;
+ X11->ptrXRRQueryExtension = XRRQueryExtension;
+ X11->ptrXRRSizes = XRRSizes;
+# endif
+
+ if (X11->ptrXRRQueryExtension
+ && X11->ptrXRRQueryExtension(X11->display, &X11->xrandr_eventbase, &X11->xrandr_errorbase)) {
+ // XRandR is supported
+ X11->use_xrandr = true;
+ }
+ }
+#endif // QT_NO_XRANDR
+
+#ifndef QT_NO_XRENDER
+ if (X11->use_xrender) {
+ // XRender is supported, let's see if we have a PictFormat for the
+ // default visual
+ XRenderPictFormat *format =
+ XRenderFindVisualFormat(X11->display,
+ (Visual *) QX11Info::appVisual(X11->defaultScreen));
+
+ if (!format) {
+ X11->use_xrender = false;
+ }
+ }
+#endif // QT_NO_XRENDER
+
+#ifndef QT_NO_XFIXES
+ // See if Xfixes is supported on the connected display
+ if (XQueryExtension(X11->display, "XFIXES", &X11->xfixes_major,
+ &X11->xfixes_eventbase, &X11->xfixes_errorbase)) {
+ X11->ptrXFixesQueryExtension = XFIXES_LOAD_V1(XFixesQueryExtension);
+ X11->ptrXFixesQueryVersion = XFIXES_LOAD_V1(XFixesQueryVersion);
+ X11->ptrXFixesSetCursorName = XFIXES_LOAD_V2(XFixesSetCursorName);
+ X11->ptrXFixesSelectSelectionInput = XFIXES_LOAD_V2(XFixesSelectSelectionInput);
+
+ if(X11->ptrXFixesQueryExtension && X11->ptrXFixesQueryVersion
+ && X11->ptrXFixesQueryExtension(X11->display, &X11->xfixes_eventbase,
+ &X11->xfixes_errorbase)) {
+ // Xfixes is supported.
+ // Note: the XFixes protocol version is negotiated using QueryVersion.
+ // We supply the highest version we support, the X server replies with
+ // the highest version it supports, but no higher than the version we
+ // asked for. The version sent back is the protocol version the X server
+ // will use to talk us. If this call is removed, the behavior of the
+ // X server when it receives an XFixes request is undefined.
+ int major = 3;
+ int minor = 0;
+ X11->ptrXFixesQueryVersion(X11->display, &major, &minor);
+ X11->use_xfixes = (major >= 1);
+ X11->xfixes_major = major;
+ }
+ }
+#endif // QT_NO_XFIXES
+
+#ifndef QT_NO_XCURSOR
+#ifdef QT_RUNTIME_XCURSOR
+ X11->ptrXcursorLibraryLoadCursor = 0;
+ QLibrary xcursorLib(QLatin1String("Xcursor"), 1);
+ bool xcursorFound = xcursorLib.load();
+ if (!xcursorFound) { //try without the version number
+ xcursorLib.setFileName(QLatin1String("Xcursor"));
+ xcursorFound = xcursorLib.load();
+ }
+ if (xcursorFound) {
+ X11->ptrXcursorLibraryLoadCursor =
+ (PtrXcursorLibraryLoadCursor) xcursorLib.resolve("XcursorLibraryLoadCursor");
+ }
+#else
+ X11->ptrXcursorLibraryLoadCursor = XcursorLibraryLoadCursor;
+#endif // QT_RUNTIME_XCURSOR
+#endif // QT_NO_XCURSOR
+
+#ifndef QT_NO_XSYNC
+ int xsync_evbase, xsync_errbase;
+ int major, minor;
+ if (XSyncQueryExtension(X11->display, &xsync_evbase, &xsync_errbase))
+ XSyncInitialize(X11->display, &major, &minor);
+#endif // QT_NO_XSYNC
+
+#ifndef QT_NO_XINERAMA
+#ifdef QT_RUNTIME_XINERAMA
+ X11->ptrXineramaQueryExtension = 0;
+ X11->ptrXineramaIsActive = 0;
+ X11->ptrXineramaQueryScreens = 0;
+ QLibrary xineramaLib(QLatin1String("Xinerama"), 1);
+ bool xineramaFound = xineramaLib.load();
+ if (!xineramaFound) { //try without the version number
+ xineramaLib.setFileName(QLatin1String("Xinerama"));
+ xineramaFound = xineramaLib.load();
+ }
+ if (xineramaFound) {
+ X11->ptrXineramaQueryExtension =
+ (PtrXineramaQueryExtension) xineramaLib.resolve("XineramaQueryExtension");
+ X11->ptrXineramaIsActive =
+ (PtrXineramaIsActive) xineramaLib.resolve("XineramaIsActive");
+ X11->ptrXineramaQueryScreens =
+ (PtrXineramaQueryScreens) xineramaLib.resolve("XineramaQueryScreens");
+ }
+#else
+ X11->ptrXineramaQueryScreens = XineramaQueryScreens;
+ X11->ptrXineramaIsActive = XineramaIsActive;
+ X11->ptrXineramaQueryExtension = XineramaQueryExtension;
+#endif // QT_RUNTIME_XINERAMA
+#endif // QT_NO_XINERAMA
+
+#ifndef QT_NO_XINPUT
+ // See if Xinput is supported on the connected display
+ X11->ptrXCloseDevice = 0;
+ X11->ptrXListInputDevices = 0;
+ X11->ptrXOpenDevice = 0;
+ X11->ptrXFreeDeviceList = 0;
+ X11->ptrXSelectExtensionEvent = 0;
+ X11->use_xinput = XQueryExtension(X11->display, "XInputExtension", &X11->xinput_major,
+ &X11->xinput_eventbase, &X11->xinput_errorbase);
+ if (X11->use_xinput) {
+ X11->ptrXCloseDevice = XINPUT_LOAD(XCloseDevice);
+ X11->ptrXListInputDevices = XINPUT_LOAD(XListInputDevices);
+ X11->ptrXOpenDevice = XINPUT_LOAD(XOpenDevice);
+ X11->ptrXFreeDeviceList = XINPUT_LOAD(XFreeDeviceList);
+ X11->ptrXSelectExtensionEvent = XINPUT_LOAD(XSelectExtensionEvent);
+ }
+#endif // QT_NO_XINPUT
+
+#ifndef QT_NO_XKB
+ int xkblibMajor = XkbMajorVersion;
+ int xkblibMinor = XkbMinorVersion;
+ X11->use_xkb = XkbQueryExtension(X11->display,
+ &X11->xkb_major,
+ &X11->xkb_eventbase,
+ &X11->xkb_errorbase,
+ &xkblibMajor,
+ &xkblibMinor);
+ if (X11->use_xkb) {
+ // If XKB is detected, set the GrabsUseXKBState option so input method
+ // compositions continue to work (ie. deadkeys)
+ unsigned int state = XkbPCF_GrabsUseXKBStateMask;
+ (void) XkbSetPerClientControls(X11->display, state, &state);
+
+ // select for group change events
+ XkbSelectEventDetails(X11->display,
+ XkbUseCoreKbd,
+ XkbStateNotify,
+ XkbAllStateComponentsMask,
+ XkbGroupStateMask);
+
+ // current group state is queried when creating the keymapper, no need to do it here
+ }
+#endif
+
+
+#if !defined(QT_NO_FONTCONFIG)
+ int dpi = 0;
+ getXDefault("Xft", FC_DPI, &dpi);
+ if (dpi) {
+ for (int s = 0; s < ScreenCount(X11->display); ++s) {
+ QX11Info::setAppDpiX(s, dpi);
+ QX11Info::setAppDpiY(s, dpi);
+ }
+ }
+ double fc_scale = 1.;
+ getXDefault("Xft", FC_SCALE, &fc_scale);
+ X11->fc_scale = fc_scale;
+ for (int s = 0; s < ScreenCount(X11->display); ++s) {
+ int subpixel = FC_RGBA_UNKNOWN;
+#if !defined(QT_NO_XRENDER) && (RENDER_MAJOR > 0 || RENDER_MINOR >= 6)
+ if (X11->use_xrender) {
+ int rsp = XRenderQuerySubpixelOrder(X11->display, s);
+ switch (rsp) {
+ default:
+ case SubPixelUnknown:
+ subpixel = FC_RGBA_UNKNOWN;
+ break;
+ case SubPixelHorizontalRGB:
+ subpixel = FC_RGBA_RGB;
+ break;
+ case SubPixelHorizontalBGR:
+ subpixel = FC_RGBA_BGR;
+ break;
+ case SubPixelVerticalRGB:
+ subpixel = FC_RGBA_VRGB;
+ break;
+ case SubPixelVerticalBGR:
+ subpixel = FC_RGBA_VBGR;
+ break;
+ case SubPixelNone:
+ subpixel = FC_RGBA_NONE;
+ break;
+ }
+ }
+#endif
+
+ char *rgba = XGetDefault(X11->display, "Xft", FC_RGBA);
+ if (rgba) {
+ char *end = 0;
+ int v = strtol(rgba, &end, 0);
+ if (rgba != end) {
+ subpixel = v;
+ } else if (qstrncmp(rgba, "unknown", 7) == 0) {
+ subpixel = FC_RGBA_UNKNOWN;
+ } else if (qstrncmp(rgba, "rgb", 3) == 0) {
+ subpixel = FC_RGBA_RGB;
+ } else if (qstrncmp(rgba, "bgr", 3) == 0) {
+ subpixel = FC_RGBA_BGR;
+ } else if (qstrncmp(rgba, "vrgb", 4) == 0) {
+ subpixel = FC_RGBA_VRGB;
+ } else if (qstrncmp(rgba, "vbgr", 4) == 0) {
+ subpixel = FC_RGBA_VBGR;
+ } else if (qstrncmp(rgba, "none", 4) == 0) {
+ subpixel = FC_RGBA_NONE;
+ }
+ }
+ X11->screens[s].subpixel = subpixel;
+ }
+ getXDefault("Xft", FC_ANTIALIAS, &X11->fc_antialias);
+#ifdef FC_HINT_STYLE
+ X11->fc_hint_style = -1;
+ getXDefault("Xft", FC_HINT_STYLE, &X11->fc_hint_style);
+#endif
+#if 0
+ // ###### these are implemented by Xft, not sure we need them
+ getXDefault("Xft", FC_AUTOHINT, &X11->fc_autohint);
+ getXDefault("Xft", FC_HINTING, &X11->fc_autohint);
+ getXDefault("Xft", FC_MINSPACE, &X11->fc_autohint);
+#endif
+#endif // QT_NO_XRENDER
+
+ // initialize key mapper
+ QKeyMapper::changeKeyboard();
+
+ // Misc. initialization
+#if 0 //disabled for now..
+ QSegfaultHandler::initialize(priv->argv, priv->argc);
+#endif
+ QCursorData::initialize();
+ }
+ QFont::initialize();
+
+ if(qt_is_gui_used) {
+ qApp->setObjectName(QString::fromLocal8Bit(appName));
+
+ int screen;
+ for (screen = 0; screen < X11->screenCount; ++screen) {
+ XSelectInput(X11->display, QX11Info::appRootWindow(screen),
+ KeymapStateMask | EnterWindowMask | LeaveWindowMask | PropertyChangeMask);
+
+#ifndef QT_NO_XRANDR
+ if (X11->use_xrandr)
+ X11->ptrXRRSelectInput(X11->display, QX11Info::appRootWindow(screen), True);
+#endif // QT_NO_XRANDR
+ }
+ }
+
+ if (qt_is_gui_used) {
+ // Attempt to determine the current running X11 Desktop Enviornment
+ // Use dbus if/when we can, but fall back to using windowManagerName() for now
+
+#ifndef QT_NO_XFIXES
+ if (X11->ptrXFixesSelectSelectionInput)
+ X11->ptrXFixesSelectSelectionInput(X11->display, QX11Info::appRootWindow(), ATOM(_NET_WM_CM_S0),
+ XFixesSetSelectionOwnerNotifyMask
+ | XFixesSelectionWindowDestroyNotifyMask
+ | XFixesSelectionClientCloseNotifyMask);
+#endif // QT_NO_XFIXES
+ X11->compositingManagerRunning = XGetSelectionOwner(X11->display,
+ ATOM(_NET_WM_CM_S0));
+ X11->desktopEnvironment = DE_UNKNOWN;
+ X11->desktopVersion = 0;
+
+ Atom type;
+ int format;
+ unsigned long length, after;
+ uchar *data = 0;
+ int rc;
+
+ do {
+ if (!qgetenv("KDE_FULL_SESSION").isEmpty()) {
+ X11->desktopEnvironment = DE_KDE;
+ X11->desktopVersion = qgetenv("KDE_SESSION_VERSION").toInt();
+ break;
+ }
+
+ if (qgetenv("DESKTOP_SESSION") == "gnome") {
+ X11->desktopEnvironment = DE_GNOME;
+ break;
+ }
+
+ // GNOME_DESKTOP_SESSION_ID is deprecated for some reason, but still check it
+ if (!qgetenv("GNOME_DESKTOP_SESSION_ID").isEmpty()) {
+ X11->desktopEnvironment = DE_GNOME;
+ break;
+ }
+
+ rc = XGetWindowProperty(X11->display, QX11Info::appRootWindow(), ATOM(_DT_SAVE_MODE),
+ 0, 2, False, XA_STRING, &type, &format, &length,
+ &after, &data);
+ if (rc == Success && length) {
+ if (!strcmp(reinterpret_cast<char *>(data), "xfce4")) {
+ // Pretend that xfce4 is gnome, as it uses the same libraries.
+ // The detection above is stolen from xdg-open.
+ X11->desktopEnvironment = DE_GNOME;
+ break;
+ }
+
+ // We got the property but it wasn't xfce4. Free data before it gets overwritten.
+ XFree(data);
+ data = 0;
+ }
+
+ rc = XGetWindowProperty(X11->display, QX11Info::appRootWindow(), ATOM(DTWM_IS_RUNNING),
+ 0, 1, False, AnyPropertyType, &type, &format, &length,
+ &after, &data);
+ if (rc == Success && length) {
+ // DTWM is running, meaning most likely CDE is running...
+ X11->desktopEnvironment = DE_CDE;
+ break;
+ }
+
+ rc = XGetWindowProperty(X11->display, QX11Info::appRootWindow(),
+ ATOM(_SGI_DESKS_MANAGER), 0, 1, False, XA_WINDOW,
+ &type, &format, &length, &after, &data);
+ if (rc == Success && length) {
+ X11->desktopEnvironment = DE_4DWM;
+ break;
+ }
+
+ if (XGetWindowProperty(X11->display, QX11Info::appRootWindow(),
+ ATOM(_NET_SUPPORTING_WM_CHECK),
+ 0, 1024, False, XA_WINDOW, &type,
+ &format, &length, &after, &data) == Success) {
+ if (type == XA_WINDOW && format == 32) {
+ Window windowManagerWindow = *((Window*) data);
+ XFree(data);
+ data = 0;
+
+ if (windowManagerWindow != XNone) {
+ Atom utf8atom = ATOM(UTF8_STRING);
+ if (XGetWindowProperty(QX11Info::display(), windowManagerWindow, ATOM(_NET_WM_NAME),
+ 0, 1024, False, utf8atom, &type,
+ &format, &length, &after, &data) == Success) {
+ if (type == utf8atom && format == 8) {
+ if (qstrcmp((const char *)data, "MCompositor") == 0)
+ X11->desktopEnvironment = DE_MEEGO_COMPOSITOR;
+ }
+ }
+ }
+ }
+ }
+
+ } while(0);
+
+ if (data)
+ XFree((char *)data);
+
+#if !defined(QT_NO_STYLE_GTK)
+ if (X11->desktopEnvironment == DE_GNOME) {
+ static bool menusHaveIcons = QGtkStyle::getGConfBool(QLatin1String("/desktop/gnome/interface/menus_have_icons"), true);
+ QApplication::setAttribute(Qt::AA_DontShowIconsInMenus, !menusHaveIcons);
+ }
+#endif
+ qt_set_input_encoding();
+
+ qt_set_x11_resources(appFont, appFGCol, appBGCol, appBTNCol);
+
+ // be smart about the size of the default font. most X servers have helvetica
+ // 12 point available at 2 resolutions:
+ // 75dpi (12 pixels) and 100dpi (17 pixels).
+ // At 95 DPI, a 12 point font should be 16 pixels tall - in which case a 17
+ // pixel font is a closer match than a 12 pixel font
+ int ptsz = (X11->use_xrender
+ ? 9
+ : (int) (((QX11Info::appDpiY() >= 95 ? 17. : 12.) *
+ 72. / (float) QX11Info::appDpiY()) + 0.5));
+
+ if (!QApplicationPrivate::sys_font) {
+ // no font from settings or RESOURCE_MANAGER, provide a fallback
+ QFont f(X11->has_fontconfig ? QLatin1String("Sans Serif") : QLatin1String("Helvetica"),
+ ptsz);
+ QApplicationPrivate::setSystemFont(f);
+ }
+
+#if !defined (QT_NO_TABLET)
+ if (X11->use_xinput) {
+ int ndev,
+ i,
+ j;
+ bool gotStylus,
+ gotEraser;
+ XDeviceInfo *devices = 0, *devs;
+ XInputClassInfo *ip;
+ XAnyClassPtr any;
+ XValuatorInfoPtr v;
+ XAxisInfoPtr a;
+ XDevice *dev = 0;
+
+ if (X11->ptrXListInputDevices) {
+ devices = X11->ptrXListInputDevices(X11->display, &ndev);
+ if (!devices)
+ qWarning("QApplication: Failed to get list of tablet devices");
+ }
+ if (!devices)
+ ndev = -1;
+ QTabletEvent::TabletDevice deviceType;
+ for (devs = devices, i = 0; i < ndev && devs; i++, devs++) {
+ dev = 0;
+ deviceType = QTabletEvent::NoDevice;
+ gotStylus = false;
+ gotEraser = false;
+
+#if defined(Q_OS_IRIX)
+ QString devName = QString::fromLocal8Bit(devs->name).toLower();
+ if (devName == QLatin1String(WACOM_NAME)) {
+ deviceType = QTabletEvent::Stylus;
+ gotStylus = true;
+ }
+#else
+ if (devs->type == ATOM(XWacomStylus) || devs->type == ATOM(XTabletStylus)) {
+ deviceType = QTabletEvent::Stylus;
+ if (wacomDeviceName()->isEmpty())
+ wacomDeviceName()->append(devs->name);
+ gotStylus = true;
+ } else if (devs->type == ATOM(XWacomEraser) || devs->type == ATOM(XTabletEraser)) {
+ deviceType = QTabletEvent::XFreeEraser;
+ gotEraser = true;
+ }
+#endif
+ if (deviceType == QTabletEvent::NoDevice)
+ continue;
+
+ if (gotStylus || gotEraser) {
+ if (X11->ptrXOpenDevice)
+ dev = X11->ptrXOpenDevice(X11->display, devs->id);
+
+ if (!dev)
+ continue;
+
+ QTabletDeviceData device_data;
+ device_data.deviceType = deviceType;
+ device_data.eventCount = 0;
+ device_data.device = dev;
+ device_data.xinput_motion = -1;
+ device_data.xinput_key_press = -1;
+ device_data.xinput_key_release = -1;
+ device_data.xinput_button_press = -1;
+ device_data.xinput_button_release = -1;
+ device_data.xinput_proximity_in = -1;
+ device_data.xinput_proximity_out = -1;
+ device_data.widgetToGetPress = 0;
+
+ if (dev->num_classes > 0) {
+ for (ip = dev->classes, j = 0; j < dev->num_classes;
+ ip++, j++) {
+ switch (ip->input_class) {
+ case KeyClass:
+ DeviceKeyPress(dev, device_data.xinput_key_press,
+ device_data.eventList[device_data.eventCount]);
+ if (device_data.eventList[device_data.eventCount])
+ ++device_data.eventCount;
+ DeviceKeyRelease(dev, device_data.xinput_key_release,
+ device_data.eventList[device_data.eventCount]);
+ if (device_data.eventList[device_data.eventCount])
+ ++device_data.eventCount;
+ break;
+ case ButtonClass:
+ DeviceButtonPress(dev, device_data.xinput_button_press,
+ device_data.eventList[device_data.eventCount]);
+ if (device_data.eventList[device_data.eventCount])
+ ++device_data.eventCount;
+ DeviceButtonRelease(dev, device_data.xinput_button_release,
+ device_data.eventList[device_data.eventCount]);
+ if (device_data.eventList[device_data.eventCount])
+ ++device_data.eventCount;
+ break;
+ case ValuatorClass:
+ // I'm only going to be interested in motion when the
+ // stylus is already down anyway!
+ DeviceMotionNotify(dev, device_data.xinput_motion,
+ device_data.eventList[device_data.eventCount]);
+ if (device_data.eventList[device_data.eventCount])
+ ++device_data.eventCount;
+ ProximityIn(dev, device_data.xinput_proximity_in, device_data.eventList[device_data.eventCount]);
+ if (device_data.eventList[device_data.eventCount])
+ ++device_data.eventCount;
+ ProximityOut(dev, device_data.xinput_proximity_out, device_data.eventList[device_data.eventCount]);
+ if (device_data.eventList[device_data.eventCount])
+ ++device_data.eventCount;
+ default:
+ break;
+ }
+ }
+ }
+
+ // get the min/max value for pressure!
+ any = (XAnyClassPtr) (devs->inputclassinfo);
+ for (j = 0; j < devs->num_classes; j++) {
+ if (any->c_class == ValuatorClass) {
+ v = (XValuatorInfoPtr) any;
+ a = (XAxisInfoPtr) ((char *) v +
+ sizeof (XValuatorInfo));
+#if defined (Q_OS_IRIX)
+ // I'm not exaclty wild about this, but the
+ // dimensions of the tablet are more relevant here
+ // than the min and max values from the axis
+ // (actually it seems to be 2/3 or what is in the
+ // axis. So we'll try to parse it from this
+ // string. --tws
+ char returnString[SGIDeviceRtrnLen];
+ int tmp;
+ if (XSGIMiscQueryExtension(X11->display, &tmp, &tmp)
+ && XSGIDeviceQuery(X11->display, devs->id,
+ "dimensions", returnString)) {
+ QString str = QLatin1String(returnString);
+ int comma = str.indexOf(',');
+ device_data.minX = 0;
+ device_data.minY = 0;
+ device_data.maxX = str.left(comma).toInt();
+ device_data.maxY = str.mid(comma + 1).toInt();
+ } else {
+ device_data.minX = a[WAC_XCOORD_I].min_value;
+ device_data.maxX = a[WAC_XCOORD_I].max_value;
+ device_data.minY = a[WAC_YCOORD_I].min_value;
+ device_data.maxY = a[WAC_YCOORD_I].max_value;
+ }
+ device_data.minPressure = a[WAC_PRESSURE_I].min_value;
+ device_data.maxPressure = a[WAC_PRESSURE_I].max_value;
+ device_data.minTanPressure = a[WAC_TAN_PRESSURE_I].min_value;
+ device_data.maxTanPressure = a[WAC_TAN_PRESSURE_I].max_value;
+ device_data.minZ = a[WAC_ZCOORD_I].min_value;
+ device_data.maxZ = a[WAC_ZCOORD_I].max_value;
+#else
+ device_data.minX = a[0].min_value;
+ device_data.maxX = a[0].max_value;
+ device_data.minY = a[1].min_value;
+ device_data.maxY = a[1].max_value;
+ device_data.minPressure = a[2].min_value;
+ device_data.maxPressure = a[2].max_value;
+ device_data.minTanPressure = 0;
+ device_data.maxTanPressure = 0;
+ device_data.minZ = 0;
+ device_data.maxZ = 0;
+#endif
+
+ // got the max pressure no need to go further...
+ break;
+ }
+ any = (XAnyClassPtr) ((char *) any + any->length);
+ } // end of for loop
+
+ tablet_devices()->append(device_data);
+ } // if (gotStylus || gotEraser)
+ }
+ if (X11->ptrXFreeDeviceList)
+ X11->ptrXFreeDeviceList(devices);
+ }
+#endif // QT_NO_TABLET
+
+ X11->startupId = getenv("DESKTOP_STARTUP_ID");
+ if (X11->startupId) {
+#ifndef QT_NO_UNSETENV
+ unsetenv("DESKTOP_STARTUP_ID");
+#else
+ // it's a small memory leak, however we won't crash if Qt is
+ // unloaded and someones tries to use the envoriment.
+ putenv(strdup("DESKTOP_STARTUP_ID="));
+#endif
+ }
+ } else {
+ // read some non-GUI settings when not using the X server...
+
+ if (QApplication::desktopSettingsAware()) {
+ QSettings settings(QSettings::UserScope, QLatin1String("Trolltech"));
+ settings.beginGroup(QLatin1String("Qt"));
+
+ // read library (ie. plugin) path list
+ QString libpathkey = QString::fromLatin1("%1.%2/libraryPath")
+ .arg(QT_VERSION >> 16)
+ .arg((QT_VERSION & 0xff00) >> 8);
+ QStringList pathlist =
+ settings.value(libpathkey).toString().split(QLatin1Char(':'));
+ if (! pathlist.isEmpty()) {
+ QStringList::ConstIterator it = pathlist.constBegin();
+ while (it != pathlist.constEnd())
+ QApplication::addLibraryPath(*it++);
+ }
+
+ QString defaultcodec = settings.value(QLatin1String("defaultCodec"),
+ QVariant(QLatin1String("none"))).toString();
+ if (defaultcodec != QLatin1String("none")) {
+ QTextCodec *codec = QTextCodec::codecForName(defaultcodec.toLatin1());
+ if (codec)
+ QTextCodec::setCodecForTr(codec);
+ }
+
+ settings.endGroup(); // Qt
+ }
+ }
+
+#if !defined (Q_OS_IRIX) && !defined (QT_NO_TABLET)
+ QLibrary wacom(QString::fromLatin1("wacomcfg"), 0); // version 0 is the latest release at time of writing this.
+ // NOTE: C casts instead of reinterpret_cast for GCC 3.3.x
+ ptrWacomConfigInit = (PtrWacomConfigInit)wacom.resolve("WacomConfigInit");
+ ptrWacomConfigOpenDevice = (PtrWacomConfigOpenDevice)wacom.resolve("WacomConfigOpenDevice");
+ ptrWacomConfigGetRawParam = (PtrWacomConfigGetRawParam)wacom.resolve("WacomConfigGetRawParam");
+ ptrWacomConfigCloseDevice = (PtrWacomConfigCloseDevice)wacom.resolve("WacomConfigCloseDevice");
+ ptrWacomConfigTerm = (PtrWacomConfigTerm)wacom.resolve("WacomConfigTerm");
+
+ if (ptrWacomConfigInit == 0 || ptrWacomConfigOpenDevice == 0 || ptrWacomConfigGetRawParam == 0
+ || ptrWacomConfigCloseDevice == 0 || ptrWacomConfigTerm == 0) { // either we have all, or we have none.
+ ptrWacomConfigInit = 0;
+ ptrWacomConfigOpenDevice = 0;
+ ptrWacomConfigGetRawParam = 0;
+ ptrWacomConfigCloseDevice = 0;
+ ptrWacomConfigTerm = 0;
+ }
+#endif
+}
+
+void QApplicationPrivate::initializeWidgetPaletteHash()
+{
+}
+
+/*****************************************************************************
+ qt_cleanup() - cleans up when the application is finished
+ *****************************************************************************/
+
+void qt_cleanup()
+{
+ if (app_save_rootinfo) // root window must keep state
+ qt_save_rootinfo();
+
+ if (qt_is_gui_used) {
+ QPixmapCache::clear();
+ QCursorData::cleanup();
+ QFont::cleanup();
+ QColormap::cleanup();
+
+#if !defined (QT_NO_TABLET)
+ QTabletDeviceDataList *devices = qt_tablet_devices();
+ if (X11->ptrXCloseDevice)
+ for (int i = 0; i < devices->size(); ++i)
+ X11->ptrXCloseDevice(X11->display, (XDevice*)devices->at(i).device);
+ devices->clear();
+#endif
+ }
+
+#ifndef QT_NO_XRENDER
+ for (int i = 0; i < X11->solid_fill_count; ++i) {
+ if (X11->solid_fills[i].picture)
+ XRenderFreePicture(X11->display, X11->solid_fills[i].picture);
+ }
+ for (int i = 0; i < X11->pattern_fill_count; ++i) {
+ if (X11->pattern_fills[i].picture)
+ XRenderFreePicture(X11->display, X11->pattern_fills[i].picture);
+ }
+#endif
+
+#if !defined(QT_NO_IM)
+ delete QApplicationPrivate::inputContext;
+ QApplicationPrivate::inputContext = 0;
+#endif
+
+ // Reset the error handlers
+ if (qt_is_gui_used)
+ XSync(X11->display, False); // sync first to process all possible errors
+ XSetErrorHandler(original_x_errhandler);
+ XSetIOErrorHandler(original_xio_errhandler);
+
+ if (X11->argbColormaps) {
+ for (int s = 0; s < X11->screenCount; s++) {
+ if (X11->argbColormaps[s])
+ XFreeColormap(X11->display, X11->argbColormaps[s]);
+ }
+ }
+
+ if (qt_is_gui_used && !X11->foreignDisplay)
+ XCloseDisplay(X11->display); // close X display
+ X11->display = 0;
+
+ delete [] X11->screens;
+ delete [] X11->argbVisuals;
+ delete [] X11->argbColormaps;
+
+ if (X11->foreignDisplay) {
+ delete [] (char *)appName;
+ appName = 0;
+ }
+
+ delete [] (char *)appClass;
+ appClass = 0;
+
+ if (X11->net_supported_list)
+ delete [] X11->net_supported_list;
+ X11->net_supported_list = 0;
+
+ if (X11->net_virtual_root_list)
+ delete [] X11->net_virtual_root_list;
+ X11->net_virtual_root_list = 0;
+
+ delete X11;
+ X11 = 0;
+}
+
+
+/*****************************************************************************
+ Platform specific global and internal functions
+ *****************************************************************************/
+
+void qt_save_rootinfo() // save new root info
+{
+ Atom type;
+ int format;
+ unsigned long length, after;
+ uchar *data = 0;
+
+ if (ATOM(_XSETROOT_ID)) { // kill old pixmap
+ if (XGetWindowProperty(X11->display, QX11Info::appRootWindow(),
+ ATOM(_XSETROOT_ID), 0, 1,
+ True, AnyPropertyType, &type, &format,
+ &length, &after, &data) == Success) {
+ if (type == XA_PIXMAP && format == 32 && length == 1 &&
+ after == 0 && data) {
+ XKillClient(X11->display, *((Pixmap*)data));
+ }
+ Pixmap dummy = XCreatePixmap(X11->display, QX11Info::appRootWindow(),
+ 1, 1, 1);
+ XChangeProperty(X11->display, QX11Info::appRootWindow(),
+ ATOM(_XSETROOT_ID), XA_PIXMAP, 32,
+ PropModeReplace, (uchar *)&dummy, 1);
+ XSetCloseDownMode(X11->display, RetainPermanent);
+ }
+ }
+ if (data)
+ XFree((char *)data);
+}
+
+void qt_updated_rootinfo()
+{
+ app_save_rootinfo = true;
+}
+
+// ### Cleanup, this function is not in use!
+bool qt_wstate_iconified(WId winid)
+{
+ Atom type;
+ int format;
+ unsigned long length, after;
+ uchar *data = 0;
+ int r = XGetWindowProperty(X11->display, winid, ATOM(WM_STATE), 0, 2,
+ False, AnyPropertyType, &type, &format,
+ &length, &after, &data);
+ bool iconic = false;
+ if (r == Success && data && format == 32) {
+ // quint32 *wstate = (quint32*)data;
+ unsigned long *wstate = (unsigned long *) data;
+ iconic = (*wstate == IconicState);
+ XFree((char *)data);
+ }
+ return iconic;
+}
+
+QString QApplicationPrivate::appName() const
+{
+ return QString::fromLocal8Bit(QT_PREPEND_NAMESPACE(appName));
+}
+
+const char *QX11Info::appClass() // get application class
+{
+ return QT_PREPEND_NAMESPACE(appClass);
+}
+
+bool qt_nograb() // application no-grab option
+{
+#if defined(QT_DEBUG)
+ return appNoGrab;
+#else
+ return false;
+#endif
+}
+
+
+/*****************************************************************************
+ Platform specific QApplication members
+ *****************************************************************************/
+
+#ifdef QT3_SUPPORT
+void QApplication::setMainWidget(QWidget *mainWidget)
+{
+#ifndef QT_NO_DEBUG
+ if (mainWidget && mainWidget->parentWidget() && mainWidget->isWindow())
+ qWarning("QApplication::setMainWidget: New main widget (%s/%s) "
+ "has a parent",
+ mainWidget->metaObject()->className(), mainWidget->objectName().toLocal8Bit().constData());
+#endif
+ if (mainWidget)
+ mainWidget->d_func()->createWinId();
+ QApplicationPrivate::main_widget = mainWidget;
+ if (QApplicationPrivate::main_widget) // give WM command line
+ QApplicationPrivate::applyX11SpecificCommandLineArguments(QApplicationPrivate::main_widget);
+}
+#endif
+
+void QApplicationPrivate::applyX11SpecificCommandLineArguments(QWidget *main_widget)
+{
+ static bool beenHereDoneThat = false;
+ if (beenHereDoneThat)
+ return;
+ beenHereDoneThat = true;
+ Q_ASSERT(main_widget->testAttribute(Qt::WA_WState_Created));
+ if (mwTitle) {
+ XStoreName(X11->display, main_widget->effectiveWinId(), (char*)mwTitle);
+ QByteArray net_wm_name = QString::fromLocal8Bit(mwTitle).toUtf8();
+ XChangeProperty(X11->display, main_widget->effectiveWinId(), ATOM(_NET_WM_NAME), ATOM(UTF8_STRING), 8,
+ PropModeReplace, (unsigned char *)net_wm_name.data(), net_wm_name.size());
+ }
+ if (mwGeometry) { // parse geometry
+ int x, y;
+ int w, h;
+ int m = XParseGeometry((char*)mwGeometry, &x, &y, (uint*)&w, (uint*)&h);
+ QSize minSize = main_widget->minimumSize();
+ QSize maxSize = main_widget->maximumSize();
+ if ((m & XValue) == 0)
+ x = main_widget->geometry().x();
+ if ((m & YValue) == 0)
+ y = main_widget->geometry().y();
+ if ((m & WidthValue) == 0)
+ w = main_widget->width();
+ if ((m & HeightValue) == 0)
+ h = main_widget->height();
+ w = qMin(w,maxSize.width());
+ h = qMin(h,maxSize.height());
+ w = qMax(w,minSize.width());
+ h = qMax(h,minSize.height());
+ if ((m & XNegative)) {
+ x = QApplication::desktop()->width() + x - w;
+ }
+ if ((m & YNegative)) {
+ y = QApplication::desktop()->height() + y - h;
+ }
+ main_widget->setGeometry(x, y, w, h);
+ }
+}
+
+#ifndef QT_NO_CURSOR
+
+/*****************************************************************************
+ QApplication cursor stack
+ *****************************************************************************/
+
+void QApplication::setOverrideCursor(const QCursor &cursor)
+{
+ qApp->d_func()->cursor_list.prepend(cursor);
+
+ QWidgetList all = allWidgets();
+ for (QWidgetList::ConstIterator it = all.constBegin(); it != all.constEnd(); ++it) {
+ register QWidget *w = *it;
+ if ((w->testAttribute(Qt::WA_SetCursor) || w->isWindow()) && (w->windowType() != Qt::Desktop))
+ qt_x11_enforce_cursor(w);
+ }
+ XFlush(X11->display); // make X execute it NOW
+}
+
+void QApplication::restoreOverrideCursor()
+{
+ if (qApp->d_func()->cursor_list.isEmpty())
+ return;
+ qApp->d_func()->cursor_list.removeFirst();
+
+ if (QWidgetPrivate::mapper != 0 && !closingDown()) {
+ QWidgetList all = allWidgets();
+ for (QWidgetList::ConstIterator it = all.constBegin(); it != all.constEnd(); ++it) {
+ register QWidget *w = *it;
+ if ((w->testAttribute(Qt::WA_SetCursor) || w->isWindow()) && (w->windowType() != Qt::Desktop))
+ qt_x11_enforce_cursor(w);
+ }
+ XFlush(X11->display);
+ }
+}
+
+#endif
+
+
+/*****************************************************************************
+ Routines to find a Qt widget from a screen position
+ *****************************************************************************/
+
+Window QX11Data::findClientWindow(Window win, Atom property, bool leaf)
+{
+ Atom type = XNone;
+ int format, i;
+ ulong nitems, after;
+ uchar *data = 0;
+ Window root, parent, target=0, *children=0;
+ uint nchildren;
+ if (XGetWindowProperty(X11->display, win, property, 0, 0, false, AnyPropertyType,
+ &type, &format, &nitems, &after, &data) == Success) {
+ if (data)
+ XFree((char *)data);
+ if (type)
+ return win;
+ }
+ if (!XQueryTree(X11->display,win,&root,&parent,&children,&nchildren)) {
+ if (children)
+ XFree((char *)children);
+ return 0;
+ }
+ for (i=nchildren-1; !target && i >= 0; i--)
+ target = X11->findClientWindow(children[i], property, leaf);
+ if (children)
+ XFree((char *)children);
+ return target;
+}
+
+QWidget *QApplication::topLevelAt(const QPoint &p)
+{
+#ifdef QT_NO_CURSOR
+ Q_UNUSED(p);
+ return 0;
+#else
+ int screen = QCursor::x11Screen();
+ int unused;
+
+ int x = p.x();
+ int y = p.y();
+ Window target;
+ if (!XTranslateCoordinates(X11->display,
+ QX11Info::appRootWindow(screen),
+ QX11Info::appRootWindow(screen),
+ x, y, &unused, &unused, &target)) {
+ return 0;
+ }
+ if (!target || target == QX11Info::appRootWindow(screen))
+ return 0;
+ QWidget *w;
+ w = QWidget::find((WId)target);
+
+ if (!w) {
+ X11->ignoreBadwindow();
+ target = X11->findClientWindow(target, ATOM(WM_STATE), true);
+ if (X11->badwindow())
+ return 0;
+ w = QWidget::find((WId)target);
+ if (!w) {
+ // Perhaps the widget at (x,y) is inside a foreign application?
+ // Search all toplevel widgets to see if one is within target
+ QWidgetList list = QApplication::topLevelWidgets();
+ for (int i = 0; i < list.count(); ++i) {
+ QWidget *widget = list.at(i);
+ Window ctarget = target;
+ if (widget->isVisible() && !(widget->windowType() == Qt::Desktop)) {
+ Q_ASSERT(widget->testAttribute(Qt::WA_WState_Created));
+ Window wid = widget->internalWinId();
+ while (ctarget && !w) {
+ X11->ignoreBadwindow();
+ if (!XTranslateCoordinates(X11->display,
+ QX11Info::appRootWindow(screen),
+ ctarget, x, y, &unused, &unused, &ctarget)
+ || X11->badwindow())
+ break;
+ if (ctarget == wid) {
+ // Found!
+ w = widget;
+ break;
+ }
+ }
+ }
+ if (w)
+ break;
+ }
+ }
+ }
+ return w ? w->window() : 0;
+#endif
+}
+
+void QApplication::syncX()
+{
+ if (X11->display)
+ XSync(X11->display, False); // don't discard events
+}
+
+
+void QApplication::beep()
+{
+ if (X11->display)
+ XBell(X11->display, 0);
+ else
+ printf("\7");
+}
+
+void QApplication::alert(QWidget *widget, int msec)
+{
+ if (!QApplicationPrivate::checkInstance("alert"))
+ return;
+
+ QWidgetList windowsToMark;
+ if (!widget) {
+ windowsToMark += topLevelWidgets();
+ } else {
+ windowsToMark.append(widget->window());
+ }
+
+ for (int i = 0; i < windowsToMark.size(); ++i) {
+ QWidget *window = windowsToMark.at(i);
+ if (!window->isActiveWindow()) {
+ qt_change_net_wm_state(window, true, ATOM(_NET_WM_STATE_DEMANDS_ATTENTION));
+ if (msec != 0) {
+ QTimer *timer = new QTimer(qApp);
+ timer->setSingleShot(true);
+ connect(timer, SIGNAL(timeout()), qApp, SLOT(_q_alertTimeOut()));
+ if (QTimer *oldTimer = qApp->d_func()->alertTimerHash.value(window)) {
+ qApp->d_func()->alertTimerHash.remove(window);
+ delete oldTimer;
+ }
+ qApp->d_func()->alertTimerHash.insert(window, timer);
+ timer->start(msec);
+ }
+ }
+ }
+}
+
+void QApplicationPrivate::_q_alertTimeOut()
+{
+ if (QTimer *timer = qobject_cast<QTimer *>(q_func()->sender())) {
+ QHash<QWidget *, QTimer *>::iterator it = alertTimerHash.begin();
+ while (it != alertTimerHash.end()) {
+ if (it.value() == timer) {
+ QWidget *window = it.key();
+ qt_change_net_wm_state(window, false, ATOM(_NET_WM_STATE_DEMANDS_ATTENTION));
+ alertTimerHash.erase(it);
+ timer->deleteLater();
+ break;
+ }
+ ++it;
+ }
+ }
+}
+
+/*****************************************************************************
+ Special lookup functions for windows that have been reparented recently
+ *****************************************************************************/
+
+static QWidgetMapper *wPRmapper = 0; // alternative widget mapper
+
+void qPRCreate(const QWidget *widget, Window oldwin)
+{ // QWidget::reparent mechanism
+ if (!wPRmapper)
+ wPRmapper = new QWidgetMapper;
+
+ QETWidget *w = static_cast<QETWidget *>(const_cast<QWidget *>(widget));
+ wPRmapper->insert((int)oldwin, w); // add old window to mapper
+ w->setAttribute(Qt::WA_WState_Reparented); // set reparented flag
+}
+
+void qPRCleanup(QWidget *widget)
+{
+ QETWidget *etw = static_cast<QETWidget *>(const_cast<QWidget *>(widget));
+ if (!(wPRmapper && widget->testAttribute(Qt::WA_WState_Reparented)))
+ return; // not a reparented widget
+ QWidgetMapper::Iterator it = wPRmapper->begin();
+ while (it != wPRmapper->constEnd()) {
+ QWidget *w = *it;
+ if (w == etw) { // found widget
+ etw->setAttribute(Qt::WA_WState_Reparented, false); // clear flag
+ it = wPRmapper->erase(it);// old window no longer needed
+ } else {
+ ++it;
+ }
+ }
+ if (wPRmapper->size() == 0) { // became empty
+ delete wPRmapper; // then reset alt mapper
+ wPRmapper = 0;
+ }
+}
+
+static QETWidget *qPRFindWidget(Window oldwin)
+{
+ return wPRmapper ? (QETWidget*)wPRmapper->value((int)oldwin, 0) : 0;
+}
+
+int QApplication::x11ClientMessage(QWidget* w, XEvent* event, bool passive_only)
+{
+ if (w && !w->internalWinId())
+ return 0;
+ QETWidget *widget = (QETWidget*)w;
+ if (event->xclient.format == 32 && event->xclient.message_type) {
+ if (event->xclient.message_type == ATOM(WM_PROTOCOLS)) {
+ Atom a = event->xclient.data.l[0];
+ if (a == ATOM(WM_DELETE_WINDOW)) {
+ if (passive_only) return 0;
+ widget->translateCloseEvent(event);
+ }
+ else if (a == ATOM(WM_TAKE_FOCUS)) {
+ if ((ulong) event->xclient.data.l[1] > X11->time)
+ X11->time = event->xclient.data.l[1];
+ QWidget *amw = activeModalWidget();
+ if (amw && amw->testAttribute(Qt::WA_X11DoNotAcceptFocus))
+ amw = 0;
+ if (amw && !QApplicationPrivate::tryModalHelper(widget, 0)) {
+ QWidget *p = amw->parentWidget();
+ while (p && p != widget)
+ p = p->parentWidget();
+ if (!p || !X11->net_supported_list)
+ amw->raise(); // help broken window managers
+ amw->activateWindow();
+ }
+#ifndef QT_NO_WHATSTHIS
+ } else if (a == ATOM(_NET_WM_CONTEXT_HELP)) {
+ QWhatsThis::enterWhatsThisMode();
+#endif // QT_NO_WHATSTHIS
+ } else if (a == ATOM(_NET_WM_PING)) {
+ // avoid send/reply loops
+ Window root = RootWindow(X11->display, w->x11Info().screen());
+ if (event->xclient.window != root) {
+ event->xclient.window = root;
+ XSendEvent(event->xclient.display, event->xclient.window,
+ False, SubstructureNotifyMask|SubstructureRedirectMask, event);
+ }
+#ifndef QT_NO_XSYNC
+ } else if (a == ATOM(_NET_WM_SYNC_REQUEST)) {
+ const ulong timestamp = (const ulong) event->xclient.data.l[1];
+ if (timestamp > X11->time)
+ X11->time = timestamp;
+ if (QTLWExtra *tlw = w->d_func()->maybeTopData()) {
+ if (timestamp == CurrentTime || timestamp > tlw->syncRequestTimestamp) {
+ tlw->syncRequestTimestamp = timestamp;
+ tlw->newCounterValueLo = event->xclient.data.l[2];
+ tlw->newCounterValueHi = event->xclient.data.l[3];
+ }
+ }
+#endif
+ }
+ } else if (event->xclient.message_type == ATOM(_QT_SCROLL_DONE)) {
+ widget->translateScrollDoneEvent(event);
+ } else if (event->xclient.message_type == ATOM(XdndPosition)) {
+ X11->xdndHandlePosition(widget, event, passive_only);
+ } else if (event->xclient.message_type == ATOM(XdndEnter)) {
+ X11->xdndHandleEnter(widget, event, passive_only);
+ } else if (event->xclient.message_type == ATOM(XdndStatus)) {
+ X11->xdndHandleStatus(widget, event, passive_only);
+ } else if (event->xclient.message_type == ATOM(XdndLeave)) {
+ X11->xdndHandleLeave(widget, event, passive_only);
+ } else if (event->xclient.message_type == ATOM(XdndDrop)) {
+ X11->xdndHandleDrop(widget, event, passive_only);
+ } else if (event->xclient.message_type == ATOM(XdndFinished)) {
+ X11->xdndHandleFinished(widget, event, passive_only);
+ } else {
+ if (passive_only) return 0;
+ // All other are interactions
+ }
+ } else {
+ X11->motifdndHandle(widget, event, passive_only);
+ }
+
+ return 0;
+}
+
+int QApplication::x11ProcessEvent(XEvent* event)
+{
+ Q_D(QApplication);
+ QScopedLoopLevelCounter loopLevelCounter(d->threadData);
+
+#ifdef ALIEN_DEBUG
+ //qDebug() << "QApplication::x11ProcessEvent:" << event->type;
+#endif
+ switch (event->type) {
+ case ButtonPress:
+ pressed_window = event->xbutton.window;
+ X11->userTime = event->xbutton.time;
+ // fallthrough intended
+ case ButtonRelease:
+ X11->time = event->xbutton.time;
+ break;
+ case MotionNotify:
+ X11->time = event->xmotion.time;
+ break;
+ case XKeyPress:
+ X11->userTime = event->xkey.time;
+ // fallthrough intended
+ case XKeyRelease:
+ X11->time = event->xkey.time;
+ break;
+ case PropertyNotify:
+ X11->time = event->xproperty.time;
+ break;
+ case EnterNotify:
+ case LeaveNotify:
+ X11->time = event->xcrossing.time;
+ break;
+ case SelectionClear:
+ X11->time = event->xselectionclear.time;
+ break;
+ default:
+ break;
+ }
+#ifndef QT_NO_XFIXES
+ if (X11->use_xfixes && event->type == (X11->xfixes_eventbase + XFixesSelectionNotify)) {
+ XFixesSelectionNotifyEvent *req =
+ reinterpret_cast<XFixesSelectionNotifyEvent *>(event);
+ X11->time = req->selection_timestamp;
+ if (req->selection == ATOM(_NET_WM_CM_S0))
+ X11->compositingManagerRunning = req->owner;
+ }
+#endif
+
+ QETWidget *widget = (QETWidget*)QWidget::find((WId)event->xany.window);
+
+ if (wPRmapper) { // just did a widget reparent?
+ if (widget == 0) { // not in std widget mapper
+ switch (event->type) { // only for mouse/key events
+ case ButtonPress:
+ case ButtonRelease:
+ case MotionNotify:
+ case XKeyPress:
+ case XKeyRelease:
+ widget = qPRFindWidget(event->xany.window);
+ break;
+ }
+ }
+ else if (widget->testAttribute(Qt::WA_WState_Reparented))
+ qPRCleanup(widget); // remove from alt mapper
+ }
+
+ QETWidget *keywidget=0;
+ bool grabbed=false;
+ if (event->type==XKeyPress || event->type==XKeyRelease) {
+ keywidget = (QETWidget*)QWidget::keyboardGrabber();
+ if (keywidget) {
+ grabbed = true;
+ } else if (!keywidget) {
+ if (d->inPopupMode()) // no focus widget, see if we have a popup
+ keywidget = (QETWidget*) (activePopupWidget()->focusWidget() ? activePopupWidget()->focusWidget() : activePopupWidget());
+ else if (QApplicationPrivate::focus_widget)
+ keywidget = (QETWidget*)QApplicationPrivate::focus_widget;
+ else if (widget)
+ keywidget = (QETWidget*)widget->window();
+ }
+ }
+
+#ifndef QT_NO_IM
+ // Filtering input events by the input context. It has to be taken
+ // place before any other key event consumers such as eventfilters
+ // and accelerators because some input methods require quite
+ // various key combination and sequences. It often conflicts with
+ // accelerators and so on, so we must give the input context the
+ // filtering opportunity first to ensure all input methods work
+ // properly regardless of application design.
+
+ if(keywidget && keywidget->isEnabled() && keywidget->testAttribute(Qt::WA_InputMethodEnabled)) {
+ // block user interaction during session management
+ if((event->type==XKeyPress || event->type==XKeyRelease) && qt_sm_blockUserInput)
+ return true;
+
+ // for XIM handling
+ QInputContext *qic = keywidget->inputContext();
+ if(qic && qic->x11FilterEvent(keywidget, event))
+ return true;
+
+ // filterEvent() accepts QEvent *event rather than preexpanded
+ // key event attribute values. This is intended to pass other
+ // QInputEvent in future. Other non IM-related events should
+ // not be forwarded to input contexts to prevent weird event
+ // handling.
+ if ((event->type == XKeyPress || event->type == XKeyRelease)) {
+ int code = -1;
+ int count = 0;
+ Qt::KeyboardModifiers modifiers;
+ QEvent::Type type;
+ QString text;
+ KeySym keySym;
+
+ qt_keymapper_private()->translateKeyEventInternal(keywidget, event, keySym, count,
+ text, modifiers, code, type, false);
+
+ // both key press/release is required for some complex
+ // input methods. don't eliminate anything.
+ QKeyEventEx keyevent(type, code, modifiers, text, false, qMax(qMax(count, 1), text.length()),
+ event->xkey.keycode, keySym, event->xkey.state);
+ if(qic && qic->filterEvent(&keyevent))
+ return true;
+ }
+ } else
+#endif // QT_NO_IM
+ {
+ if (XFilterEvent(event, XNone))
+ return true;
+ }
+
+ if (qt_x11EventFilter(event)) // send through app filter
+ return 1;
+
+ if (event->type == MappingNotify) {
+ // keyboard mapping changed
+ XRefreshKeyboardMapping(&event->xmapping);
+
+ QKeyMapper::changeKeyboard();
+ return 0;
+ }
+#ifndef QT_NO_XKB
+ else if (X11->use_xkb && event->type == X11->xkb_eventbase) {
+ XkbAnyEvent *xkbevent = (XkbAnyEvent *) event;
+ switch (xkbevent->xkb_type) {
+ case XkbStateNotify:
+ {
+ XkbStateNotifyEvent *xkbstateevent = (XkbStateNotifyEvent *) xkbevent;
+ if ((xkbstateevent->changed & XkbGroupStateMask) != 0) {
+ qt_keymapper_private()->xkb_currentGroup = xkbstateevent->group;
+ QKeyMapper::changeKeyboard();
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+#endif
+
+ if (!widget) { // don't know this windows
+ QWidget* popup = QApplication::activePopupWidget();
+ if (popup) {
+
+ /*
+ That is more than suboptimal. The real solution should
+ do some keyevent and buttonevent translation, so that
+ the popup still continues to work as the user expects.
+ Unfortunately this translation is currently only
+ possible with a known widget. I'll change that soon
+ (Matthias).
+ */
+
+ // Danger - make sure we don't lock the server
+ switch (event->type) {
+ case ButtonPress:
+ case ButtonRelease:
+ case XKeyPress:
+ case XKeyRelease:
+ do {
+ popup->close();
+ } while ((popup = qApp->activePopupWidget()));
+ return 1;
+ }
+ }
+ return -1;
+ }
+
+ if (event->type == XKeyPress || event->type == XKeyRelease)
+ widget = keywidget; // send XKeyEvents through keywidget->x11Event()
+
+ if (app_do_modal) // modal event handling
+ if (!qt_try_modal(widget, event)) {
+ if (event->type == ClientMessage && !widget->x11Event(event))
+ x11ClientMessage(widget, event, true);
+ return 1;
+ }
+
+
+ if (widget->x11Event(event)) // send through widget filter
+ return 1;
+#if !defined (QT_NO_TABLET)
+ if (!qt_xdnd_dragging) {
+ QTabletDeviceDataList *tablets = qt_tablet_devices();
+ for (int i = 0; i < tablets->size(); ++i) {
+ QTabletDeviceData &tab = tablets->operator [](i);
+ if (event->type == tab.xinput_motion
+ || event->type == tab.xinput_button_release
+ || event->type == tab.xinput_button_press
+ || event->type == tab.xinput_proximity_in
+ || event->type == tab.xinput_proximity_out) {
+ widget->translateXinputEvent(event, &tab);
+ return 0;
+ }
+ }
+ }
+#endif
+
+#ifndef QT_NO_XRANDR
+ if (X11->use_xrandr && event->type == (X11->xrandr_eventbase + RRScreenChangeNotify)) {
+ // update Xlib internals with the latest screen configuration
+ X11->ptrXRRUpdateConfiguration(event);
+
+ // update the size for desktop widget
+ int scr = X11->ptrXRRRootToScreen(X11->display, event->xany.window);
+ QDesktopWidget *desktop = QApplication::desktop();
+ QWidget *w = desktop->screen(scr);
+ QSize oldSize(w->size());
+ w->data->crect.setWidth(DisplayWidth(X11->display, scr));
+ w->data->crect.setHeight(DisplayHeight(X11->display, scr));
+ QResizeEvent e(w->size(), oldSize);
+ QApplication::sendEvent(w, &e);
+ if (w != desktop)
+ QApplication::sendEvent(desktop, &e);
+ }
+#endif // QT_NO_XRANDR
+
+#ifndef QT_NO_XFIXES
+ if (X11->use_xfixes && event->type == (X11->xfixes_eventbase + XFixesSelectionNotify)) {
+ XFixesSelectionNotifyEvent *req = reinterpret_cast<XFixesSelectionNotifyEvent *>(event);
+
+ // compress all XFixes events related to this selection
+ // we don't want to handle old SelectionNotify events.
+ qt_xfixes_selection_event_data xfixes_event;
+ xfixes_event.selection = req->selection;
+ for (XEvent ev;;) {
+ if (!XCheckIfEvent(X11->display, &ev, &qt_xfixes_scanner, (XPointer)&xfixes_event))
+ break;
+ }
+
+ if (req->selection == ATOM(CLIPBOARD)) {
+ if (qt_xfixes_clipboard_changed(req->owner, req->selection_timestamp)) {
+ emit clipboard()->changed(QClipboard::Clipboard);
+ emit clipboard()->dataChanged();
+ }
+ } else if (req->selection == XA_PRIMARY) {
+ if (qt_xfixes_selection_changed(req->owner, req->selection_timestamp)) {
+ emit clipboard()->changed(QClipboard::Selection);
+ emit clipboard()->selectionChanged();
+ }
+ }
+ }
+#endif // QT_NO_XFIXES
+
+ switch (event->type) {
+
+ case ButtonRelease: // mouse event
+ if (!d->inPopupMode() && !QWidget::mouseGrabber() && pressed_window != widget->internalWinId()
+ && (widget = (QETWidget*) QWidget::find((WId)pressed_window)) == 0)
+ break;
+ // fall through intended
+ case ButtonPress:
+ if (event->xbutton.root != RootWindow(X11->display, widget->x11Info().screen())
+ && ! qt_xdnd_dragging) {
+ while (activePopupWidget())
+ activePopupWidget()->close();
+ return 1;
+ }
+ if (event->type == ButtonPress)
+ qt_net_update_user_time(widget->window(), X11->userTime);
+ // fall through intended
+ case MotionNotify:
+#if !defined(QT_NO_TABLET)
+ if (!qt_tabletChokeMouse) {
+#endif
+ if (widget->testAttribute(Qt::WA_TransparentForMouseEvents)) {
+ QPoint pos(event->xbutton.x, event->xbutton.y);
+ pos = widget->d_func()->mapFromWS(pos);
+ QWidget *window = widget->window();
+ pos = widget->mapTo(window, pos);
+ if (QWidget *child = window->childAt(pos)) {
+ widget = static_cast<QETWidget *>(child);
+ pos = child->mapFrom(window, pos);
+ event->xbutton.x = pos.x();
+ event->xbutton.y = pos.y();
+ }
+ }
+ widget->translateMouseEvent(event);
+#if !defined(QT_NO_TABLET)
+ } else {
+ qt_tabletChokeMouse = false;
+ }
+#endif
+ break;
+
+ case XKeyPress: // keyboard event
+ qt_net_update_user_time(widget->window(), X11->userTime);
+ // fallthrough intended
+ case XKeyRelease:
+ {
+ if (keywidget && keywidget->isEnabled()) { // should always exist
+ // qDebug("sending key event");
+ qt_keymapper_private()->translateKeyEvent(keywidget, event, grabbed);
+ }
+ break;
+ }
+
+ case GraphicsExpose:
+ case Expose: // paint event
+ widget->translatePaintEvent(event);
+ break;
+
+ case ConfigureNotify: // window move/resize event
+ if (event->xconfigure.event == event->xconfigure.window)
+ widget->translateConfigEvent(event);
+ break;
+
+ case XFocusIn: { // got focus
+ if ((widget->windowType() == Qt::Desktop))
+ break;
+ if (d->inPopupMode()) // some delayed focus event to ignore
+ break;
+ if (!widget->isWindow())
+ break;
+ if (event->xfocus.detail != NotifyAncestor &&
+ event->xfocus.detail != NotifyInferior &&
+ event->xfocus.detail != NotifyNonlinear)
+ break;
+ setActiveWindow(widget);
+ if (X11->focus_model == QX11Data::FM_PointerRoot) {
+ // We got real input focus from somewhere, but we were in PointerRoot
+ // mode, so we don't trust this event. Check the focus model to make
+ // sure we know what focus mode we are using...
+ qt_check_focus_model();
+ }
+ }
+ break;
+
+ case XFocusOut: // lost focus
+ if ((widget->windowType() == Qt::Desktop))
+ break;
+ if (!widget->isWindow())
+ break;
+ if (event->xfocus.mode == NotifyGrab) {
+ qt_xfocusout_grab_counter++;
+ break;
+ }
+ if (event->xfocus.detail != NotifyAncestor &&
+ event->xfocus.detail != NotifyNonlinearVirtual &&
+ event->xfocus.detail != NotifyNonlinear)
+ break;
+ if (!d->inPopupMode() && widget == QApplicationPrivate::active_window) {
+ XEvent ev;
+ bool focus_will_change = false;
+ if (XCheckTypedEvent(X11->display, XFocusIn, &ev)) {
+ // we're about to get an XFocusIn, if we know we will
+ // get a new active window, we don't want to set the
+ // active window to 0 now
+ QWidget *w2 = QWidget::find(ev.xany.window);
+ if (w2
+ && w2->windowType() != Qt::Desktop
+ && !d->inPopupMode() // some delayed focus event to ignore
+ && w2->isWindow()
+ && (ev.xfocus.detail == NotifyAncestor
+ || ev.xfocus.detail == NotifyInferior
+ || ev.xfocus.detail == NotifyNonlinear))
+ focus_will_change = true;
+
+ XPutBackEvent(X11->display, &ev);
+ }
+ if (!focus_will_change)
+ setActiveWindow(0);
+ }
+ break;
+
+ case EnterNotify: { // enter window
+ if (QWidget::mouseGrabber() && (!d->inPopupMode() || widget->window() != activePopupWidget()))
+ break;
+ if ((event->xcrossing.mode != NotifyNormal
+ && event->xcrossing.mode != NotifyUngrab)
+ || event->xcrossing.detail == NotifyVirtual
+ || event->xcrossing.detail == NotifyNonlinearVirtual)
+ break;
+ if (event->xcrossing.focus &&
+ !(widget->windowType() == Qt::Desktop) && !widget->isActiveWindow()) {
+ if (X11->focus_model == QX11Data::FM_Unknown) // check focus model
+ qt_check_focus_model();
+ if (X11->focus_model == QX11Data::FM_PointerRoot) // PointerRoot mode
+ setActiveWindow(widget);
+ }
+
+ if (qt_button_down && !d->inPopupMode())
+ break;
+
+ QWidget *alien = widget->childAt(widget->d_func()->mapFromWS(QPoint(event->xcrossing.x,
+ event->xcrossing.y)));
+ QWidget *enter = alien ? alien : widget;
+ QWidget *leave = 0;
+ if (qt_last_mouse_receiver && !qt_last_mouse_receiver->internalWinId())
+ leave = qt_last_mouse_receiver;
+ else
+ leave = QWidget::find(curWin);
+
+ // ### Alien: enter/leave might be wrong here with overlapping siblings
+ // if the enter widget is native and stacked under a non-native widget.
+ QApplicationPrivate::dispatchEnterLeave(enter, leave);
+ curWin = widget->internalWinId();
+ qt_last_mouse_receiver = enter;
+ if (!d->inPopupMode() || widget->window() == activePopupWidget())
+ widget->translateMouseEvent(event); //we don't get MotionNotify, emulate it
+ }
+ break;
+ case LeaveNotify: { // leave window
+ QWidget *mouseGrabber = QWidget::mouseGrabber();
+ if (mouseGrabber && !d->inPopupMode())
+ break;
+ if (curWin && widget->internalWinId() != curWin)
+ break;
+ if ((event->xcrossing.mode != NotifyNormal
+ && event->xcrossing.mode != NotifyUngrab)
+ || event->xcrossing.detail == NotifyInferior)
+ break;
+ if (!(widget->windowType() == Qt::Desktop))
+ widget->translateMouseEvent(event); //we don't get MotionNotify, emulate it
+
+ QWidget* enter = 0;
+ QPoint enterPoint;
+ XEvent ev;
+ while (XCheckMaskEvent(X11->display, EnterWindowMask | LeaveWindowMask , &ev)
+ && !qt_x11EventFilter(&ev)) {
+ QWidget* event_widget = QWidget::find(ev.xcrossing.window);
+ if(event_widget && event_widget->x11Event(&ev))
+ break;
+ if (ev.type == LeaveNotify
+ || (ev.xcrossing.mode != NotifyNormal
+ && ev.xcrossing.mode != NotifyUngrab)
+ || ev.xcrossing.detail == NotifyVirtual
+ || ev.xcrossing.detail == NotifyNonlinearVirtual)
+ continue;
+ enter = event_widget;
+ if (enter)
+ enterPoint = enter->d_func()->mapFromWS(QPoint(ev.xcrossing.x, ev.xcrossing.y));
+ if (ev.xcrossing.focus &&
+ enter && !(enter->windowType() == Qt::Desktop) && !enter->isActiveWindow()) {
+ if (X11->focus_model == QX11Data::FM_Unknown) // check focus model
+ qt_check_focus_model();
+ if (X11->focus_model == QX11Data::FM_PointerRoot) // PointerRoot mode
+ setActiveWindow(enter);
+ }
+ break;
+ }
+
+ if ((! enter || (enter->windowType() == Qt::Desktop)) &&
+ event->xcrossing.focus && widget == QApplicationPrivate::active_window &&
+ X11->focus_model == QX11Data::FM_PointerRoot // PointerRoot mode
+ ) {
+ setActiveWindow(0);
+ }
+
+ if (qt_button_down && !d->inPopupMode())
+ break;
+
+ if (!curWin)
+ QApplicationPrivate::dispatchEnterLeave(widget, 0);
+
+ if (enter) {
+ QWidget *alienEnter = enter->childAt(enterPoint);
+ if (alienEnter)
+ enter = alienEnter;
+ }
+
+ QWidget *leave = qt_last_mouse_receiver ? qt_last_mouse_receiver : widget;
+ QWidget *activePopupWidget = qApp->activePopupWidget();
+
+ if (mouseGrabber && activePopupWidget && leave == activePopupWidget)
+ enter = mouseGrabber;
+ else if (enter != widget && mouseGrabber) {
+ if (!widget->rect().contains(widget->d_func()->mapFromWS(QPoint(event->xcrossing.x,
+ event->xcrossing.y))))
+ break;
+ }
+
+ QApplicationPrivate::dispatchEnterLeave(enter, leave);
+ qt_last_mouse_receiver = enter;
+
+ if (enter && QApplicationPrivate::tryModalHelper(enter, 0)) {
+ QWidget *nativeEnter = enter->internalWinId() ? enter : enter->nativeParentWidget();
+ curWin = nativeEnter->internalWinId();
+ static_cast<QETWidget *>(nativeEnter)->translateMouseEvent(&ev); //we don't get MotionNotify, emulate it
+ } else {
+ curWin = 0;
+ qt_last_mouse_receiver = 0;
+ }
+ }
+ break;
+
+ case UnmapNotify: // window hidden
+ if (widget->isWindow()) {
+ Q_ASSERT(widget->testAttribute(Qt::WA_WState_Created));
+ widget->d_func()->topData()->waitingForMapNotify = 0;
+
+ if (widget->windowType() != Qt::Popup && !widget->testAttribute(Qt::WA_DontShowOnScreen)) {
+ widget->setAttribute(Qt::WA_Mapped, false);
+ if (widget->isVisible()) {
+ widget->d_func()->topData()->spont_unmapped = 1;
+ QHideEvent e;
+ QApplication::sendSpontaneousEvent(widget, &e);
+ widget->d_func()->hideChildren(true);
+ }
+ }
+
+ if (!widget->d_func()->topData()->validWMState && X11->deferred_map.removeAll(widget))
+ widget->doDeferredMap();
+ }
+ break;
+
+ case MapNotify: // window shown
+ if (widget->isWindow()) {
+ // if we got a MapNotify when we were not waiting for it, it most
+ // likely means the user has already asked to hide the window before
+ // it ever being shown, so we try to withdraw a window after sending
+ // the QShowEvent.
+ bool pendingHide = widget->testAttribute(Qt::WA_WState_ExplicitShowHide) && widget->testAttribute(Qt::WA_WState_Hidden);
+ widget->d_func()->topData()->waitingForMapNotify = 0;
+
+ if (widget->windowType() != Qt::Popup) {
+ widget->setAttribute(Qt::WA_Mapped);
+ if (widget->d_func()->topData()->spont_unmapped) {
+ widget->d_func()->topData()->spont_unmapped = 0;
+ widget->d_func()->showChildren(true);
+ QShowEvent e;
+ QApplication::sendSpontaneousEvent(widget, &e);
+
+ // show() must have been called on this widget in
+ // order to reach this point, but we could have
+ // cleared these 2 attributes in case something
+ // previously forced us into WithdrawnState
+ // (e.g. kdocker)
+ widget->setAttribute(Qt::WA_WState_ExplicitShowHide, true);
+ widget->setAttribute(Qt::WA_WState_Visible, true);
+ }
+ }
+ if (pendingHide) // hide the window
+ XWithdrawWindow(X11->display, widget->internalWinId(), widget->x11Info().screen());
+ }
+ break;
+
+ case ClientMessage: // client message
+ return x11ClientMessage(widget,event,False);
+
+ case ReparentNotify: { // window manager reparents
+ // compress old reparent events to self
+ XEvent ev;
+ while (XCheckTypedWindowEvent(X11->display,
+ widget->effectiveWinId(),
+ ReparentNotify,
+ &ev)) {
+ if (ev.xreparent.window != ev.xreparent.event) {
+ XPutBackEvent(X11->display, &ev);
+ break;
+ }
+ }
+ if (widget->isWindow()) {
+ QTLWExtra *topData = widget->d_func()->topData();
+
+ // store the parent. Useful for many things, embedding for instance.
+ topData->parentWinId = event->xreparent.parent;
+
+ // the widget frame strut should also be invalidated
+ widget->data->fstrut_dirty = 1;
+
+ // work around broken window managers... if we get a
+ // ReparentNotify before the MapNotify, we assume that
+ // we're being managed by a reparenting window
+ // manager.
+ //
+ // however, the WM_STATE property may not have been set
+ // yet, but we are going to assume that it will
+ // be... otherwise we could try to map again after getting
+ // an UnmapNotify... which could then, in turn, trigger a
+ // race in the window manager which causes the window to
+ // disappear when it really should be hidden.
+ if (topData->waitingForMapNotify && !topData->validWMState) {
+ topData->waitingForMapNotify = 0;
+ topData->validWMState = 1;
+ }
+
+ if (X11->focus_model != QX11Data::FM_Unknown) {
+ // toplevel reparented...
+ QWidget *newparent = QWidget::find(event->xreparent.parent);
+ if (! newparent || (newparent->windowType() == Qt::Desktop)) {
+ // we don't know about the new parent (or we've been
+ // reparented to root), perhaps a window manager
+ // has been (re)started? reset the focus model to unknown
+ X11->focus_model = QX11Data::FM_Unknown;
+ }
+ }
+ }
+ break;
+ }
+ case SelectionRequest: {
+ XSelectionRequestEvent *req = &event->xselectionrequest;
+ if (! req)
+ break;
+
+ if (ATOM(XdndSelection) && req->selection == ATOM(XdndSelection)) {
+ X11->xdndHandleSelectionRequest(req);
+
+ } else if (qt_clipboard) {
+ QClipboardEvent e(reinterpret_cast<QEventPrivate*>(event));
+ QApplication::sendSpontaneousEvent(qt_clipboard, &e);
+ }
+ break;
+ }
+ case SelectionClear: {
+ XSelectionClearEvent *req = &event->xselectionclear;
+ // don't deliver dnd events to the clipboard, it gets confused
+ if (! req || (ATOM(XdndSelection) && req->selection == ATOM(XdndSelection)))
+ break;
+
+ if (qt_clipboard && !X11->use_xfixes) {
+ QClipboardEvent e(reinterpret_cast<QEventPrivate*>(event));
+ QApplication::sendSpontaneousEvent(qt_clipboard, &e);
+ }
+ break;
+ }
+
+ case SelectionNotify: {
+ XSelectionEvent *req = &event->xselection;
+ // don't deliver dnd events to the clipboard, it gets confused
+ if (! req || (ATOM(XdndSelection) && req->selection == ATOM(XdndSelection)))
+ break;
+
+ if (qt_clipboard) {
+ QClipboardEvent e(reinterpret_cast<QEventPrivate*>(event));
+ QApplication::sendSpontaneousEvent(qt_clipboard, &e);
+ }
+ break;
+ }
+ case PropertyNotify:
+ // some properties changed
+ if (event->xproperty.window == QX11Info::appRootWindow(0)) {
+ // root properties for the first screen
+ if (!X11->use_xfixes && event->xproperty.atom == ATOM(_QT_CLIPBOARD_SENTINEL)) {
+ if (qt_check_clipboard_sentinel()) {
+ emit clipboard()->changed(QClipboard::Clipboard);
+ emit clipboard()->dataChanged();
+ }
+ } else if (!X11->use_xfixes && event->xproperty.atom == ATOM(_QT_SELECTION_SENTINEL)) {
+ if (qt_check_selection_sentinel()) {
+ emit clipboard()->changed(QClipboard::Selection);
+ emit clipboard()->selectionChanged();
+ }
+ } else if (QApplicationPrivate::obey_desktop_settings) {
+ if (event->xproperty.atom == ATOM(RESOURCE_MANAGER))
+ qt_set_x11_resources();
+ else if (event->xproperty.atom == ATOM(_QT_SETTINGS_TIMESTAMP))
+ qt_set_x11_resources();
+ }
+ }
+ if (event->xproperty.window == QX11Info::appRootWindow()) {
+ // root properties for the default screen
+ if (event->xproperty.atom == ATOM(_QT_INPUT_ENCODING)) {
+ qt_set_input_encoding();
+ } else if (event->xproperty.atom == ATOM(_NET_SUPPORTED)) {
+ qt_get_net_supported();
+ } else if (event->xproperty.atom == ATOM(_NET_VIRTUAL_ROOTS)) {
+ qt_get_net_virtual_roots();
+ } else if (event->xproperty.atom == ATOM(_NET_WORKAREA)) {
+ qt_desktopwidget_update_workarea();
+
+ // emit the workAreaResized() signal
+ QDesktopWidget *desktop = QApplication::desktop();
+ int numScreens = desktop->numScreens();
+ for (int i = 0; i < numScreens; ++i)
+ emit desktop->workAreaResized(i);
+ }
+ } else if (widget) {
+ widget->translatePropertyEvent(event);
+ } else {
+ return -1; // don't know this window
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+bool QApplication::x11EventFilter(XEvent *)
+{
+ return false;
+}
+
+
+
+/*****************************************************************************
+ Modal widgets; Since Xlib has little support for this we roll our own
+ modal widget mechanism.
+ A modal widget without a parent becomes application-modal.
+ A modal widget with a parent becomes modal to its parent and grandparents..
+
+ QApplicationPrivate::enterModal()
+ Enters modal state
+ Arguments:
+ QWidget *widget A modal widget
+
+ QApplicationPrivate::leaveModal()
+ Leaves modal state for a widget
+ Arguments:
+ QWidget *widget A modal widget
+ *****************************************************************************/
+
+bool QApplicationPrivate::modalState()
+{
+ return app_do_modal;
+}
+
+void QApplicationPrivate::enterModal_sys(QWidget *widget)
+{
+ if (!qt_modal_stack)
+ qt_modal_stack = new QWidgetList;
+
+ QWidget *leave = qt_last_mouse_receiver;
+ if (!leave)
+ leave = QWidget::find((WId)curWin);
+ QApplicationPrivate::dispatchEnterLeave(0, leave);
+ qt_modal_stack->insert(0, widget);
+ app_do_modal = true;
+ curWin = 0;
+ qt_last_mouse_receiver = 0;
+}
+
+void QApplicationPrivate::leaveModal_sys(QWidget *widget)
+{
+ if (qt_modal_stack && qt_modal_stack->removeAll(widget)) {
+ if (qt_modal_stack->isEmpty()) {
+ delete qt_modal_stack;
+ qt_modal_stack = 0;
+ QPoint p(QCursor::pos());
+ QWidget* w = QApplication::widgetAt(p.x(), p.y());
+ QWidget *leave = qt_last_mouse_receiver;
+ if (!leave)
+ leave = QWidget::find((WId)curWin);
+ if (QWidget *grabber = QWidget::mouseGrabber()) {
+ w = grabber;
+ if (leave == w)
+ leave = 0;
+ }
+ QApplicationPrivate::dispatchEnterLeave(w, leave); // send synthetic enter event
+ curWin = w ? w->effectiveWinId() : 0;
+ qt_last_mouse_receiver = w;
+ }
+ }
+ app_do_modal = qt_modal_stack != 0;
+}
+
+bool qt_try_modal(QWidget *widget, XEvent *event)
+{
+ if (qt_xdnd_dragging) {
+ // allow mouse events while DnD is active
+ switch (event->type) {
+ case ButtonPress:
+ case ButtonRelease:
+ case MotionNotify:
+ return true;
+ default:
+ break;
+ }
+ }
+
+ // allow mouse release events to be sent to widgets that have been pressed
+ if (event->type == ButtonRelease) {
+ QWidget *alienWidget = widget->childAt(widget->mapFromGlobal(QPoint(event->xbutton.x_root,
+ event->xbutton.y_root)));
+ if (widget == qt_button_down || (alienWidget && alienWidget == qt_button_down))
+ return true;
+ }
+
+ if (QApplicationPrivate::tryModalHelper(widget))
+ return true;
+
+ // disallow mouse/key events
+ switch (event->type) {
+ case ButtonPress:
+ case ButtonRelease:
+ case MotionNotify:
+ case XKeyPress:
+ case XKeyRelease:
+ case EnterNotify:
+ case LeaveNotify:
+ case ClientMessage:
+ return false;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+
+/*****************************************************************************
+ Popup widget mechanism
+
+ openPopup()
+ Adds a widget to the list of popup widgets
+ Arguments:
+ QWidget *widget The popup widget to be added
+
+ closePopup()
+ Removes a widget from the list of popup widgets
+ Arguments:
+ QWidget *widget The popup widget to be removed
+ *****************************************************************************/
+
+
+static int openPopupCount = 0;
+void QApplicationPrivate::openPopup(QWidget *popup)
+{
+ Q_Q(QApplication);
+ openPopupCount++;
+ if (!QApplicationPrivate::popupWidgets) { // create list
+ QApplicationPrivate::popupWidgets = new QWidgetList;
+ }
+ QApplicationPrivate::popupWidgets->append(popup); // add to end of list
+ Display *dpy = X11->display;
+ if (QApplicationPrivate::popupWidgets->count() == 1 && !qt_nograb()){ // grab mouse/keyboard
+ Q_ASSERT(popup->testAttribute(Qt::WA_WState_Created));
+ int r = XGrabKeyboard(dpy, popup->effectiveWinId(), false,
+ GrabModeAsync, GrabModeAsync, X11->time);
+ if ((popupGrabOk = (r == GrabSuccess))) {
+ r = XGrabPointer(dpy, popup->effectiveWinId(), true,
+ (ButtonPressMask | ButtonReleaseMask | ButtonMotionMask
+ | EnterWindowMask | LeaveWindowMask | PointerMotionMask),
+ GrabModeAsync, GrabModeAsync, XNone, XNone, X11->time);
+ if (!(popupGrabOk = (r == GrabSuccess))) {
+ // transfer grab back to the keyboard grabber if any
+ if (QWidgetPrivate::keyboardGrabber != 0)
+ QWidgetPrivate::keyboardGrabber->grabKeyboard();
+ else
+ XUngrabKeyboard(dpy, X11->time);
+ }
+ }
+ }
+
+ // popups are not focus-handled by the window system (the first
+ // popup grabbed the keyboard), so we have to do that manually: A
+ // new popup gets the focus
+ if (popup->focusWidget()) {
+ popup->focusWidget()->setFocus(Qt::PopupFocusReason);
+ } else if (QApplicationPrivate::popupWidgets->count() == 1) { // this was the first popup
+ if (QWidget *fw = QApplication::focusWidget()) {
+ QFocusEvent e(QEvent::FocusOut, Qt::PopupFocusReason);
+ q->sendEvent(fw, &e);
+ }
+ }
+}
+
+void QApplicationPrivate::closePopup(QWidget *popup)
+{
+ Q_Q(QApplication);
+ if (!QApplicationPrivate::popupWidgets)
+ return;
+ QApplicationPrivate::popupWidgets->removeAll(popup);
+ if (popup == qt_popup_down) {
+ qt_button_down = 0;
+ qt_popup_down = 0;
+ }
+ if (QApplicationPrivate::popupWidgets->count() == 0) { // this was the last popup
+ delete QApplicationPrivate::popupWidgets;
+ QApplicationPrivate::popupWidgets = 0;
+ if (!qt_nograb() && popupGrabOk) { // grabbing not disabled
+ Display *dpy = X11->display;
+ if (popup->geometry().contains(QPoint(mouseGlobalXPos, mouseGlobalYPos))
+ || popup->testAttribute(Qt::WA_NoMouseReplay)) {
+ // mouse release event or inside
+ replayPopupMouseEvent = false;
+ } else { // mouse press event
+ mouseButtonPressTime -= 10000; // avoid double click
+ replayPopupMouseEvent = true;
+ }
+ // transfer grab back to mouse grabber if any, otherwise release the grab
+ if (QWidgetPrivate::mouseGrabber != 0)
+ QWidgetPrivate::mouseGrabber->grabMouse();
+ else
+ XUngrabPointer(dpy, X11->time);
+
+ // transfer grab back to keyboard grabber if any, otherwise release the grab
+ if (QWidgetPrivate::keyboardGrabber != 0)
+ QWidgetPrivate::keyboardGrabber->grabKeyboard();
+ else
+ XUngrabKeyboard(dpy, X11->time);
+
+ XFlush(dpy);
+ }
+ if (QApplicationPrivate::active_window) {
+ if (QWidget *fw = QApplicationPrivate::active_window->focusWidget()) {
+ if (fw != QApplication::focusWidget()) {
+ fw->setFocus(Qt::PopupFocusReason);
+ } else {
+ QFocusEvent e(QEvent::FocusIn, Qt::PopupFocusReason);
+ q->sendEvent(fw, &e);
+ }
+ }
+ }
+ } else {
+ // popups are not focus-handled by the window system (the
+ // first popup grabbed the keyboard), so we have to do that
+ // manually: A popup was closed, so the previous popup gets
+ // the focus.
+ QWidget* aw = QApplicationPrivate::popupWidgets->last();
+ if (QWidget *fw = aw->focusWidget())
+ fw->setFocus(Qt::PopupFocusReason);
+
+ // regrab the keyboard and mouse in case 'popup' lost the grab
+ if (QApplicationPrivate::popupWidgets->count() == 1 && !qt_nograb()){ // grab mouse/keyboard
+ Display *dpy = X11->display;
+ Q_ASSERT(aw->testAttribute(Qt::WA_WState_Created));
+ int r = XGrabKeyboard(dpy, aw->effectiveWinId(), false,
+ GrabModeAsync, GrabModeAsync, X11->time);
+ if ((popupGrabOk = (r == GrabSuccess))) {
+ r = XGrabPointer(dpy, aw->effectiveWinId(), true,
+ (ButtonPressMask | ButtonReleaseMask | ButtonMotionMask
+ | EnterWindowMask | LeaveWindowMask | PointerMotionMask),
+ GrabModeAsync, GrabModeAsync, XNone, XNone, X11->time);
+ if (!(popupGrabOk = (r == GrabSuccess))) {
+ // transfer grab back to keyboard grabber
+ if (QWidgetPrivate::keyboardGrabber != 0)
+ QWidgetPrivate::keyboardGrabber->grabKeyboard();
+ else
+ XUngrabKeyboard(dpy, X11->time);
+ }
+ }
+ }
+ }
+}
+
+/*****************************************************************************
+ Event translation; translates X11 events to Qt events
+ *****************************************************************************/
+
+//
+// Mouse event translation
+//
+// Xlib doesn't give mouse double click events, so we generate them by
+// comparing window, time and position between two mouse press events.
+//
+
+static Qt::MouseButtons translateMouseButtons(int s)
+{
+ Qt::MouseButtons ret = 0;
+ if (s & Button1Mask)
+ ret |= Qt::LeftButton;
+ if (s & Button2Mask)
+ ret |= Qt::MidButton;
+ if (s & Button3Mask)
+ ret |= Qt::RightButton;
+ return ret;
+}
+
+Qt::KeyboardModifiers QX11Data::translateModifiers(int s)
+{
+ Qt::KeyboardModifiers ret = 0;
+ if (s & ShiftMask)
+ ret |= Qt::ShiftModifier;
+ if (s & ControlMask)
+ ret |= Qt::ControlModifier;
+ if (s & qt_alt_mask)
+ ret |= Qt::AltModifier;
+ if (s & qt_meta_mask)
+ ret |= Qt::MetaModifier;
+ if (s & qt_mode_switch_mask)
+ ret |= Qt::GroupSwitchModifier;
+ return ret;
+}
+
+bool QETWidget::translateMouseEvent(const XEvent *event)
+{
+ if (!isWindow() && testAttribute(Qt::WA_NativeWindow))
+ Q_ASSERT(internalWinId());
+
+ Q_D(QWidget);
+ QEvent::Type type; // event parameters
+ QPoint pos;
+ QPoint globalPos;
+ Qt::MouseButton button = Qt::NoButton;
+ Qt::MouseButtons buttons;
+ Qt::KeyboardModifiers modifiers;
+ XEvent nextEvent;
+
+ if (qt_sm_blockUserInput) // block user interaction during session management
+ return true;
+
+ if (event->type == MotionNotify) { // mouse move
+ if (event->xmotion.root != RootWindow(X11->display, x11Info().screen()) &&
+ ! qt_xdnd_dragging)
+ return false;
+
+ XMotionEvent lastMotion = event->xmotion;
+ while(XPending(X11->display)) { // compress mouse moves
+ XNextEvent(X11->display, &nextEvent);
+ if (nextEvent.type == ConfigureNotify
+ || nextEvent.type == PropertyNotify
+ || nextEvent.type == Expose
+ || nextEvent.type == GraphicsExpose
+ || nextEvent.type == NoExpose
+ || nextEvent.type == KeymapNotify
+ || ((nextEvent.type == EnterNotify || nextEvent.type == LeaveNotify)
+ && qt_button_down == this)
+ || (nextEvent.type == ClientMessage
+ && (nextEvent.xclient.message_type == ATOM(_QT_SCROLL_DONE) ||
+ (nextEvent.xclient.message_type == ATOM(WM_PROTOCOLS) &&
+ (Atom)nextEvent.xclient.data.l[0] == ATOM(_NET_WM_SYNC_REQUEST))))) {
+ qApp->x11ProcessEvent(&nextEvent);
+ continue;
+ } else if (nextEvent.type != MotionNotify ||
+ nextEvent.xmotion.window != event->xmotion.window ||
+ nextEvent.xmotion.state != event->xmotion.state) {
+ XPutBackEvent(X11->display, &nextEvent);
+ break;
+ }
+ if (!qt_x11EventFilter(&nextEvent)
+ && !x11Event(&nextEvent)) // send event through filter
+ lastMotion = nextEvent.xmotion;
+ else
+ break;
+ }
+ type = QEvent::MouseMove;
+ pos.rx() = lastMotion.x;
+ pos.ry() = lastMotion.y;
+ pos = d->mapFromWS(pos);
+ globalPos.rx() = lastMotion.x_root;
+ globalPos.ry() = lastMotion.y_root;
+ buttons = translateMouseButtons(lastMotion.state);
+ modifiers = X11->translateModifiers(lastMotion.state);
+ if (qt_button_down && !buttons)
+ qt_button_down = 0;
+ } else if (event->type == EnterNotify || event->type == LeaveNotify) {
+ XEvent *xevent = (XEvent *)event;
+ //unsigned int xstate = event->xcrossing.state;
+ type = QEvent::MouseMove;
+ pos.rx() = xevent->xcrossing.x;
+ pos.ry() = xevent->xcrossing.y;
+ pos = d->mapFromWS(pos);
+ globalPos.rx() = xevent->xcrossing.x_root;
+ globalPos.ry() = xevent->xcrossing.y_root;
+ buttons = translateMouseButtons(xevent->xcrossing.state);
+ modifiers = X11->translateModifiers(xevent->xcrossing.state);
+ if (qt_button_down && !buttons)
+ qt_button_down = 0;
+ if (qt_button_down)
+ return true;
+ } else { // button press or release
+ pos.rx() = event->xbutton.x;
+ pos.ry() = event->xbutton.y;
+ pos = d->mapFromWS(pos);
+ globalPos.rx() = event->xbutton.x_root;
+ globalPos.ry() = event->xbutton.y_root;
+ buttons = translateMouseButtons(event->xbutton.state);
+ modifiers = X11->translateModifiers(event->xbutton.state);
+ switch (event->xbutton.button) {
+ case Button1: button = Qt::LeftButton; break;
+ case Button2: button = Qt::MidButton; break;
+ case Button3: button = Qt::RightButton; break;
+ case Button4:
+ case Button5:
+ case 6:
+ case 7:
+ // the fancy mouse wheel.
+
+ // We are only interested in ButtonPress.
+ if (event->type == ButtonPress){
+ // compress wheel events (the X Server will simply
+ // send a button press for each single notch,
+ // regardless whether the application can catch up
+ // or not)
+ int delta = 1;
+ XEvent xevent;
+ while (XCheckTypedWindowEvent(X11->display, effectiveWinId(), ButtonPress, &xevent)){
+ if (xevent.xbutton.button != event->xbutton.button){
+ XPutBackEvent(X11->display, &xevent);
+ break;
+ }
+ delta++;
+ }
+
+ // the delta is defined as multiples of
+ // WHEEL_DELTA, which is set to 120. Future wheels
+ // may offer a finer-resolution. A positive delta
+ // indicates forward rotation, a negative one
+ // backward rotation respectively.
+ int btn = event->xbutton.button;
+ delta *= 120 * ((btn == Button4 || btn == 6) ? 1 : -1);
+ bool hor = (((btn == Button4 || btn == Button5) && (modifiers & Qt::AltModifier)) ||
+ (btn == 6 || btn == 7));
+ translateWheelEvent(globalPos.x(), globalPos.y(), delta, buttons,
+ modifiers, (hor) ? Qt::Horizontal: Qt::Vertical);
+ }
+ return true;
+ case 8: button = Qt::XButton1; break;
+ case 9: button = Qt::XButton2; break;
+ }
+ if (event->type == ButtonPress) { // mouse button pressed
+ buttons |= button;
+#if defined(Q_OS_IRIX) && !defined(QT_NO_TABLET)
+ QTabletDeviceDataList *tablets = qt_tablet_devices();
+ for (int i = 0; i < tablets->size(); ++i) {
+ QTabletDeviceData &tab = tablets->operator[](i);
+ XEvent myEv;
+ if (XCheckTypedEvent(X11->display, tab.xinput_button_press, &myEv)) {
+ if (translateXinputEvent(&myEv, &tab)) {
+ //Spontaneous event sent. Check if we need to continue.
+ if (qt_tabletChokeMouse) {
+ qt_tabletChokeMouse = false;
+ return false;
+ }
+ }
+ }
+ }
+#endif
+ if (!qt_button_down) {
+ qt_button_down = childAt(pos); //magic for masked widgets
+ if (!qt_button_down)
+ qt_button_down = this;
+ }
+ if (mouseActWindow == event->xbutton.window &&
+ mouseButtonPressed == button &&
+ (long)event->xbutton.time -(long)mouseButtonPressTime
+ < QApplication::doubleClickInterval() &&
+ qAbs(event->xbutton.x - mouseXPos) < QT_GUI_DOUBLE_CLICK_RADIUS &&
+ qAbs(event->xbutton.y - mouseYPos) < QT_GUI_DOUBLE_CLICK_RADIUS) {
+ type = QEvent::MouseButtonDblClick;
+ mouseButtonPressTime -= 2000; // no double-click next time
+ } else {
+ type = QEvent::MouseButtonPress;
+ mouseButtonPressTime = event->xbutton.time;
+ }
+ mouseButtonPressed = button; // save event params for
+ mouseXPos = event->xbutton.x; // future double click tests
+ mouseYPos = event->xbutton.y;
+ mouseGlobalXPos = globalPos.x();
+ mouseGlobalYPos = globalPos.y();
+ } else { // mouse button released
+ buttons &= ~button;
+#if defined(Q_OS_IRIX) && !defined(QT_NO_TABLET)
+ QTabletDeviceDataList *tablets = qt_tablet_devices();
+ for (int i = 0; i < tablets->size(); ++i) {
+ QTabletDeviceData &tab = tablets->operator[](i);
+ XEvent myEv;
+ if (XCheckTypedEvent(X11->display, tab.xinput_button_press, &myEv)) {
+ if (translateXinputEvent(&myEv, &tab)) {
+ //Spontaneous event sent. Check if we need to continue.
+ if (qt_tabletChokeMouse) {
+ qt_tabletChokeMouse = false;
+ return false;
+ }
+ }
+ }
+ }
+#endif
+ type = QEvent::MouseButtonRelease;
+ }
+ }
+ mouseActWindow = effectiveWinId(); // save some event params
+ mouseButtonState = buttons;
+ if (type == 0) // don't send event
+ return false;
+
+ if (qApp->d_func()->inPopupMode()) { // in popup mode
+ QWidget *activePopupWidget = qApp->activePopupWidget();
+ QWidget *popup = qApp->activePopupWidget();
+ if (popup != this) {
+ if (event->type == LeaveNotify)
+ return false;
+ if ((windowType() == Qt::Popup) && rect().contains(pos) && 0)
+ popup = this;
+ else // send to last popup
+ pos = popup->mapFromGlobal(globalPos);
+ }
+ bool releaseAfter = false;
+ QWidget *popupChild = popup->childAt(pos);
+
+ if (popup != qt_popup_down){
+ qt_button_down = 0;
+ qt_popup_down = 0;
+ }
+
+ switch (type) {
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonDblClick:
+ qt_button_down = popupChild;
+ qt_popup_down = popup;
+ break;
+ case QEvent::MouseButtonRelease:
+ releaseAfter = true;
+ break;
+ default:
+ break; // nothing for mouse move
+ }
+
+ int oldOpenPopupCount = openPopupCount;
+
+ if (popup->isEnabled()) {
+ // deliver event
+ replayPopupMouseEvent = false;
+ QWidget *receiver = popup;
+ QPoint widgetPos = pos;
+ if (qt_button_down)
+ receiver = qt_button_down;
+ else if (popupChild)
+ receiver = popupChild;
+ if (receiver != popup)
+ widgetPos = receiver->mapFromGlobal(globalPos);
+ QWidget *alien = childAt(mapFromGlobal(globalPos));
+ QMouseEvent e(type, widgetPos, globalPos, button, buttons, modifiers);
+ QApplicationPrivate::sendMouseEvent(receiver, &e, alien, this, &qt_button_down, qt_last_mouse_receiver);
+ } else {
+ // close disabled popups when a mouse button is pressed or released
+ switch (type) {
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonDblClick:
+ case QEvent::MouseButtonRelease:
+ popup->close();
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (qApp->activePopupWidget() != activePopupWidget
+ && replayPopupMouseEvent) {
+ // the active popup was closed, replay the mouse event
+ if (!(windowType() == Qt::Popup)) {
+#if 1
+ qt_button_down = 0;
+#else
+ if (buttons == button)
+ qt_button_down = this;
+ QMouseEvent e(type, mapFromGlobal(globalPos), globalPos, button,
+ buttons, modifiers);
+ QApplication::sendSpontaneousEvent(this, &e);
+
+ if (type == QEvent::MouseButtonPress
+ && button == Qt::RightButton
+ && (openPopupCount == oldOpenPopupCount)) {
+ QContextMenuEvent e(QContextMenuEvent::Mouse, mapFromGlobal(globalPos),
+ globalPos, modifiers);
+ QApplication::sendSpontaneousEvent(this, &e);
+ }
+#endif
+ }
+ replayPopupMouseEvent = false;
+ } else if (type == QEvent::MouseButtonPress
+ && button == Qt::RightButton
+ && (openPopupCount == oldOpenPopupCount)) {
+ QWidget *popupEvent = popup;
+ if (qt_button_down)
+ popupEvent = qt_button_down;
+ else if(popupChild)
+ popupEvent = popupChild;
+ QContextMenuEvent e(QContextMenuEvent::Mouse, pos, globalPos, modifiers);
+ QApplication::sendSpontaneousEvent(popupEvent, &e);
+ }
+
+ if (releaseAfter) {
+ qt_button_down = 0;
+ qt_popup_down = 0;
+ }
+ } else {
+ QWidget *alienWidget = childAt(pos);
+ QWidget *widget = QApplicationPrivate::pickMouseReceiver(this, globalPos, pos, type, buttons,
+ qt_button_down, alienWidget);
+ if (!widget) {
+ if (type == QEvent::MouseButtonRelease)
+ QApplicationPrivate::mouse_buttons &= ~button;
+ return false; // don't send event
+ }
+
+ int oldOpenPopupCount = openPopupCount;
+ QMouseEvent e(type, pos, globalPos, button, buttons, modifiers);
+ QApplicationPrivate::sendMouseEvent(widget, &e, alienWidget, this, &qt_button_down,
+ qt_last_mouse_receiver);
+ if (type == QEvent::MouseButtonPress
+ && button == Qt::RightButton
+ && (openPopupCount == oldOpenPopupCount)) {
+ QContextMenuEvent e(QContextMenuEvent::Mouse, pos, globalPos, modifiers);
+ QApplication::sendSpontaneousEvent(widget, &e);
+ }
+ }
+ return true;
+}
+
+
+//
+// Wheel event translation
+//
+bool QETWidget::translateWheelEvent(int global_x, int global_y, int delta,
+ Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers,
+ Qt::Orientation orient)
+{
+ const QPoint globalPos = QPoint(global_x, global_y);
+ QPoint pos = mapFromGlobal(globalPos);
+ QWidget *widget = childAt(pos);
+ if (!widget)
+ widget = this;
+ else if (!widget->internalWinId())
+ pos = widget->mapFromGlobal(globalPos);
+
+#ifdef ALIEN_DEBUG
+ qDebug() << "QETWidget::translateWheelEvent: receiver:" << widget << "pos:" << pos;
+#endif
+
+ // send the event to the widget or its ancestors
+ {
+ QWidget* popup = qApp->activePopupWidget();
+ if (popup && window() != popup)
+ popup->close();
+#ifndef QT_NO_WHEELEVENT
+ QWheelEvent e(pos, globalPos, delta, buttons, modifiers, orient);
+ if (QApplication::sendSpontaneousEvent(widget, &e))
+#endif
+ return true;
+ }
+
+ // send the event to the widget that has the focus or its ancestors, if different
+ if (widget != qApp->focusWidget() && (widget = qApp->focusWidget())) {
+ if (widget && !widget->internalWinId())
+ pos = widget->mapFromGlobal(globalPos);
+ QWidget* popup = qApp->activePopupWidget();
+ if (popup && widget != popup)
+ popup->hide();
+#ifndef QT_NO_WHEELEVENT
+ QWheelEvent e(pos, globalPos, delta, buttons, modifiers, orient);
+ if (QApplication::sendSpontaneousEvent(widget, &e))
+#endif
+ return true;
+ }
+ return false;
+}
+
+
+//
+// XInput Translation Event
+//
+#if !defined (QT_NO_TABLET)
+
+#if !defined (Q_OS_IRIX)
+void fetchWacomToolId(int &deviceType, qint64 &serialId)
+{
+ if (ptrWacomConfigInit == 0) // we actually have the lib
+ return;
+ WACOMCONFIG *config = ptrWacomConfigInit(X11->display, 0);
+ if (config == 0)
+ return;
+ WACOMDEVICE *device = ptrWacomConfigOpenDevice (config, wacomDeviceName()->constData());
+ if (device == 0)
+ return;
+ unsigned keys[1];
+ int serialInt;
+ ptrWacomConfigGetRawParam (device, XWACOM_PARAM_TOOLSERIAL, &serialInt, 1, keys);
+ serialId = serialInt;
+ int toolId;
+ ptrWacomConfigGetRawParam (device, XWACOM_PARAM_TOOLID, &toolId, 1, keys);
+ switch(toolId) {
+ case 0x007: /* Mouse 4D and 2D */
+ case 0x017: /* Intuos3 2D Mouse */
+ case 0x094:
+ case 0x09c:
+ deviceType = QTabletEvent::FourDMouse;
+ break;
+ case 0x096: /* Lens cursor */
+ case 0x097: /* Intuos3 Lens cursor */
+ deviceType = QTabletEvent::Puck;
+ break;
+ case 0x0fa:
+ case 0x81b: /* Intuos3 Classic Pen Eraser */
+ case 0x82a: /* Eraser */
+ case 0x82b: /* Intuos3 Grip Pen Eraser */
+ case 0x85a:
+ case 0x91a:
+ case 0x91b: /* Intuos3 Airbrush Eraser */
+ case 0xd1a:
+ deviceType = QTabletEvent::XFreeEraser;
+ break;
+ case 0x112:
+ case 0x912:
+ case 0x913: /* Intuos3 Airbrush */
+ case 0xd12:
+ deviceType = QTabletEvent::Airbrush;
+ break;
+ case 0x012:
+ case 0x022:
+ case 0x032:
+ case 0x801: /* Intuos3 Inking pen */
+ case 0x812: /* Inking pen */
+ case 0x813: /* Intuos3 Classic Pen */
+ case 0x822: /* Pen */
+ case 0x823: /* Intuos3 Grip Pen */
+ case 0x832: /* Stroke pen */
+ case 0x842:
+ case 0x852:
+ case 0x885: /* Intuos3 Marker Pen */
+ default: /* Unknown tool */
+ deviceType = QTabletEvent::Stylus;
+ }
+
+ /* Close device and return */
+ ptrWacomConfigCloseDevice (device);
+ ptrWacomConfigTerm(config);
+}
+#endif
+
+struct qt_tablet_motion_data
+{
+ bool filterByWidget;
+ const QWidget *widget;
+ const QWidget *etWidget;
+ int tabletMotionType;
+ bool error; // found a reason to stop searching
+};
+
+static Bool qt_mouseMotion_scanner(Display *, XEvent *event, XPointer arg)
+{
+ qt_tablet_motion_data *data = (qt_tablet_motion_data *) arg;
+ if (data->error)
+ return false;
+
+ if (event->type == MotionNotify)
+ return true;
+
+ data->error = event->type != data->tabletMotionType; // we stop compression when another event gets in between.
+ return false;
+}
+
+static Bool qt_tabletMotion_scanner(Display *, XEvent *event, XPointer arg)
+{
+ qt_tablet_motion_data *data = (qt_tablet_motion_data *) arg;
+ if (data->error)
+ return false;
+ if (event->type == data->tabletMotionType) {
+ const XDeviceMotionEvent *const motion = reinterpret_cast<const XDeviceMotionEvent*>(event);
+ if (data->filterByWidget) {
+ const QPoint curr(motion->x, motion->y);
+ const QWidget *w = data->etWidget;
+ const QWidget *const child = w->childAt(curr);
+ if (child) {
+ w = child;
+ }
+ if (w == data->widget)
+ return true;
+ } else {
+ return true;
+ }
+ }
+
+ data->error = event->type != MotionNotify; // we stop compression when another event gets in between.
+ return false;
+}
+
+bool QETWidget::translateXinputEvent(const XEvent *ev, QTabletDeviceData *tablet)
+{
+#if defined (Q_OS_IRIX)
+ // Wacom has put defines in their wacom.h file so it would be quite wise
+ // to use them, need to think of a decent way of not using
+ // it when it doesn't exist...
+ XDeviceState *s;
+ XInputClass *iClass;
+ XValuatorState *vs;
+ int j;
+#endif
+
+ Q_ASSERT(tablet != 0);
+
+ QWidget *w = this;
+ QPoint global,
+ curr;
+ QPointF hiRes;
+ qreal pressure = 0;
+ int xTilt = 0,
+ yTilt = 0,
+ z = 0;
+ qreal tangentialPressure = 0;
+ qreal rotation = 0;
+ int deviceType = QTabletEvent::NoDevice;
+ int pointerType = QTabletEvent::UnknownPointer;
+ const XDeviceMotionEvent *motion = 0;
+ XDeviceButtonEvent *button = 0;
+ const XProximityNotifyEvent *proximity = 0;
+ QEvent::Type t;
+ Qt::KeyboardModifiers modifiers = 0;
+#if !defined (Q_OS_IRIX)
+ XID device_id;
+#endif
+
+ if (ev->type == tablet->xinput_motion) {
+ motion = reinterpret_cast<const XDeviceMotionEvent*>(ev);
+ t = QEvent::TabletMove;
+ global = QPoint(motion->x_root, motion->y_root);
+ curr = QPoint(motion->x, motion->y);
+#if !defined (Q_OS_IRIX)
+ device_id = motion->deviceid;
+#endif
+ } else if (ev->type == tablet->xinput_button_press || ev->type == tablet->xinput_button_release) {
+ if (ev->type == tablet->xinput_button_press) {
+ t = QEvent::TabletPress;
+ } else {
+ t = QEvent::TabletRelease;
+ }
+ button = (XDeviceButtonEvent*)ev;
+
+ global = QPoint(button->x_root, button->y_root);
+ curr = QPoint(button->x, button->y);
+#if !defined (Q_OS_IRIX)
+ device_id = button->deviceid;
+#endif
+ } else { // Proximity
+ if (ev->type == tablet->xinput_proximity_in)
+ t = QEvent::TabletEnterProximity;
+ else
+ t = QEvent::TabletLeaveProximity;
+ proximity = (const XProximityNotifyEvent*)ev;
+#if !defined (Q_OS_IRIX)
+ device_id = proximity->deviceid;
+#endif
+ }
+
+ qint64 uid = 0;
+#if defined (Q_OS_IRIX)
+ QRect screenArea = qApp->desktop()->screenGeometry(this);
+ s = XQueryDeviceState(X11->display, static_cast<XDevice *>(tablet->device));
+ if (!s)
+ return false;
+ iClass = s->data;
+ for (j = 0; j < s->num_classes; j++) {
+ if (iClass->c_class == ValuatorClass) {
+ vs = reinterpret_cast<XValuatorState *>(iClass);
+ // figure out what device we have, based on bitmasking...
+ if (vs->valuators[WAC_TRANSDUCER_I]
+ & WAC_TRANSDUCER_PROX_MSK) {
+ switch (vs->valuators[WAC_TRANSDUCER_I]
+ & WAC_TRANSDUCER_MSK) {
+ case WAC_PUCK_ID:
+ pointerType = QTabletEvent::Puck;
+ break;
+ case WAC_STYLUS_ID:
+ pointerType = QTabletEvent::Pen;
+ break;
+ case WAC_ERASER_ID:
+ pointerType = QTabletEvent::Eraser;
+ break;
+ }
+ // Get a Unique Id for the device, Wacom gives us this ability
+ uid = vs->valuators[WAC_TRANSDUCER_I] & WAC_TRANSDUCER_ID_MSK;
+ uid = (uid << 24) | vs->valuators[WAC_SERIAL_NUM_I];
+ switch (WAC_TRANSDUCER_I & 0x0F0600) {
+ case 0x080200:
+ deviceType = QTabletEvent::Stylus;
+ break;
+ case 0x090200:
+ deviceType = QTabletEvent::Airbrush;
+ break;
+ case 0x000400:
+ deviceType = QTabletEvent::FourDMouse;
+ break;
+ case 0x000600:
+ deviceType = QTabletEvent::Puck;
+ break;
+ case 0x080400:
+ deviceType = QTabletEvent::RotationStylus;
+ break;
+ }
+ } else {
+ pointerType = QTabletEvent::UnknownPointer;
+ deviceType = QTabletEvent::NoDevice;
+ uid = 0;
+ }
+
+ if (!proximity) {
+ // apparently Wacom needs a cast for the +/- values to make sense
+ xTilt = short(vs->valuators[WAC_XTILT_I]);
+ yTilt = short(vs->valuators[WAC_YTILT_I]);
+ pressure = vs->valuators[WAC_PRESSURE_I];
+ if (deviceType == QTabletEvent::FourDMouse
+ || deviceType == QTabletEvent::RotationStylus) {
+ rotation = vs->valuators[WAC_ROTATION_I] / 64.0;
+ if (deviceType == QTabletEvent::FourDMouse)
+ z = vs->valuators[WAC_ZCOORD_I];
+ } else if (deviceType == QTabletEvent::Airbrush) {
+ tangentialPressure = vs->valuators[WAC_TAN_PRESSURE_I]
+ / qreal(tablet->maxTanPressure - tablet->minTanPressure);
+ }
+
+ hiRes = tablet->scaleCoord(vs->valuators[WAC_XCOORD_I], vs->valuators[WAC_YCOORD_I],
+ screenArea.x(), screenArea.width(),
+ screenArea.y(), screenArea.height());
+ }
+ break;
+ }
+ iClass = reinterpret_cast<XInputClass*>(reinterpret_cast<char*>(iClass) + iClass->length);
+ }
+ XFreeDeviceState(s);
+#else
+ QTabletDeviceDataList *tablet_list = qt_tablet_devices();
+ for (int i = 0; i < tablet_list->size(); ++i) {
+ const QTabletDeviceData &t = tablet_list->at(i);
+ if (device_id == static_cast<XDevice *>(t.device)->device_id) {
+ deviceType = t.deviceType;
+ if (t.deviceType == QTabletEvent::XFreeEraser) {
+ deviceType = QTabletEvent::Stylus;
+ pointerType = QTabletEvent::Eraser;
+ } else if (t.deviceType == QTabletEvent::Stylus) {
+ pointerType = QTabletEvent::Pen;
+ }
+ break;
+ }
+ }
+
+ fetchWacomToolId(deviceType, uid);
+
+ QRect screenArea = qApp->desktop()->rect();
+ if (motion) {
+ xTilt = (short) motion->axis_data[3];
+ yTilt = (short) motion->axis_data[4];
+ rotation = ((short) motion->axis_data[5]) / 64.0;
+ pressure = (short) motion->axis_data[2];
+ modifiers = X11->translateModifiers(motion->state);
+ hiRes = tablet->scaleCoord(motion->axis_data[0], motion->axis_data[1],
+ screenArea.x(), screenArea.width(),
+ screenArea.y(), screenArea.height());
+ } else if (button) {
+ xTilt = (short) button->axis_data[3];
+ yTilt = (short) button->axis_data[4];
+ rotation = ((short) button->axis_data[5]) / 64.0;
+ pressure = (short) button->axis_data[2];
+ modifiers = X11->translateModifiers(button->state);
+ hiRes = tablet->scaleCoord(button->axis_data[0], button->axis_data[1],
+ screenArea.x(), screenArea.width(),
+ screenArea.y(), screenArea.height());
+ } else if (proximity) {
+ pressure = 0;
+ modifiers = 0;
+ }
+ if (deviceType == QTabletEvent::Airbrush) {
+ tangentialPressure = rotation;
+ rotation = 0.;
+ }
+#endif
+
+ if (tablet->widgetToGetPress) {
+ w = tablet->widgetToGetPress;
+ } else {
+ QWidget *child = w->childAt(curr);
+ if (child)
+ w = child;
+ }
+ curr = w->mapFromGlobal(global);
+
+ if (t == QEvent::TabletPress) {
+ tablet->widgetToGetPress = w;
+ } else if (t == QEvent::TabletRelease && tablet->widgetToGetPress) {
+ w = tablet->widgetToGetPress;
+ curr = w->mapFromGlobal(global);
+ tablet->widgetToGetPress = 0;
+ }
+
+ QTabletEvent e(t, curr, global, hiRes,
+ deviceType, pointerType,
+ qreal(pressure / qreal(tablet->maxPressure - tablet->minPressure)),
+ xTilt, yTilt, tangentialPressure, rotation, z, modifiers, uid);
+ if (proximity) {
+ QApplication::sendSpontaneousEvent(qApp, &e);
+ } else {
+ QApplication::sendSpontaneousEvent(w, &e);
+ const bool accepted = e.isAccepted();
+ if (!accepted && ev->type == tablet->xinput_motion) {
+ // If the widget does not accept tablet events, we drop the next ones from the event queue
+ // for this widget so it is not overloaded with the numerous tablet events.
+ qt_tablet_motion_data tabletMotionData;
+ tabletMotionData.tabletMotionType = tablet->xinput_motion;
+ tabletMotionData.widget = w;
+ tabletMotionData.etWidget = this;
+ // if nothing is pressed, the events are filtered by position
+ tabletMotionData.filterByWidget = (tablet->widgetToGetPress == 0);
+
+ bool reinsertMouseEvent = false;
+ XEvent mouseMotionEvent;
+ while (true) {
+ // Find first mouse event since we expect them in pairs inside Qt
+ tabletMotionData.error =false;
+ if (XCheckIfEvent(X11->display, &mouseMotionEvent, &qt_mouseMotion_scanner, (XPointer) &tabletMotionData)) {
+ reinsertMouseEvent = true;
+ } else {
+ break;
+ }
+
+ // Now discard any duplicate tablet events.
+ tabletMotionData.error = false;
+ XEvent dummy;
+ while (XCheckIfEvent(X11->display, &dummy, &qt_tabletMotion_scanner, (XPointer) &tabletMotionData)) {
+ // just discard the event
+ }
+ }
+
+ if (reinsertMouseEvent) {
+ XPutBackEvent(X11->display, &mouseMotionEvent);
+ }
+ }
+ }
+ return true;
+}
+#endif
+
+bool QETWidget::translatePropertyEvent(const XEvent *event)
+{
+ Q_D(QWidget);
+ if (!isWindow()) return true;
+
+ Atom ret;
+ int format, e;
+ unsigned char *data = 0;
+ unsigned long nitems, after;
+
+ if (event->xproperty.atom == ATOM(_KDE_NET_WM_FRAME_STRUT)) {
+ this->data->fstrut_dirty = 1;
+
+ if (event->xproperty.state == PropertyNewValue) {
+ e = XGetWindowProperty(X11->display, event->xproperty.window, ATOM(_KDE_NET_WM_FRAME_STRUT),
+ 0, 4, // struts are 4 longs
+ False, XA_CARDINAL, &ret, &format, &nitems, &after, &data);
+
+ if (e == Success && ret == XA_CARDINAL &&
+ format == 32 && nitems == 4) {
+ long *strut = (long *) data;
+ d->topData()->frameStrut.setCoords(strut[0], strut[2], strut[1], strut[3]);
+ this->data->fstrut_dirty = 0;
+ }
+ }
+ } else if (event->xproperty.atom == ATOM(_NET_WM_STATE)) {
+ bool max = false;
+ bool full = false;
+ Qt::WindowStates oldState = Qt::WindowStates(this->data->window_state);
+
+ if (event->xproperty.state == PropertyNewValue) {
+ // using length of 1024 should be safe for all current and
+ // possible NET states...
+ e = XGetWindowProperty(X11->display, event->xproperty.window, ATOM(_NET_WM_STATE), 0, 1024,
+ False, XA_ATOM, &ret, &format, &nitems, &after, &data);
+
+ if (e == Success && ret == XA_ATOM && format == 32 && nitems > 0) {
+ Atom *states = (Atom *) data;
+
+ unsigned long i;
+ uint maximized = 0;
+ for (i = 0; i < nitems; i++) {
+ if (states[i] == ATOM(_NET_WM_STATE_MAXIMIZED_VERT))
+ maximized |= 1;
+ else if (states[i] == ATOM(_NET_WM_STATE_MAXIMIZED_HORZ))
+ maximized |= 2;
+ else if (states[i] == ATOM(_NET_WM_STATE_FULLSCREEN))
+ full = true;
+ }
+ if (maximized == 3) {
+ // only set maximized if both horizontal and vertical properties are set
+ max = true;
+ }
+ }
+ }
+
+ bool send_event = false;
+
+ if (X11->isSupportedByWM(ATOM(_NET_WM_STATE_MAXIMIZED_VERT))
+ && X11->isSupportedByWM(ATOM(_NET_WM_STATE_MAXIMIZED_HORZ))) {
+ if (max && !isMaximized()) {
+ this->data->window_state = this->data->window_state | Qt::WindowMaximized;
+ send_event = true;
+ } else if (!max && isMaximized()) {
+ this->data->window_state &= ~Qt::WindowMaximized;
+ send_event = true;
+ }
+ }
+
+ if (X11->isSupportedByWM(ATOM(_NET_WM_STATE_FULLSCREEN))) {
+ if (full && !isFullScreen()) {
+ this->data->window_state = this->data->window_state | Qt::WindowFullScreen;
+ send_event = true;
+ } else if (!full && isFullScreen()) {
+ this->data->window_state &= ~Qt::WindowFullScreen;
+ send_event = true;
+ }
+ }
+
+ if (send_event) {
+ QWindowStateChangeEvent e(oldState);
+ QApplication::sendSpontaneousEvent(this, &e);
+ }
+ } else if (event->xproperty.atom == ATOM(WM_STATE)) {
+ // the widget frame strut should also be invalidated
+ this->data->fstrut_dirty = 1;
+
+ if (event->xproperty.state == PropertyDelete) {
+ // the window manager has removed the WM State property,
+ // so it is now in the withdrawn state (ICCCM 4.1.3.1) and
+ // we are free to reuse this window
+ d->topData()->parentWinId = 0;
+ d->topData()->validWMState = 0;
+ // map the window if we were waiting for a transition to
+ // withdrawn
+ if (X11->deferred_map.removeAll(this)) {
+ doDeferredMap();
+ } else if (isVisible()
+ && !testAttribute(Qt::WA_Mapped)
+ && !testAttribute(Qt::WA_OutsideWSRange)) {
+ // so that show() will work again. As stated in the
+ // ICCCM section 4.1.4: "Only the client can effect a
+ // transition into or out of the Withdrawn state.",
+ // but apparently this particular window manager
+ // doesn't seem to care
+ setAttribute(Qt::WA_WState_ExplicitShowHide, false);
+ setAttribute(Qt::WA_WState_Visible, false);
+ }
+ } else {
+ // the window manager has changed the WM State property...
+ // we are wanting to see if we are withdrawn so that we
+ // can reuse this window...
+ e = XGetWindowProperty(X11->display, internalWinId(), ATOM(WM_STATE), 0, 2, False,
+ ATOM(WM_STATE), &ret, &format, &nitems, &after, &data);
+
+ if (e == Success && ret == ATOM(WM_STATE) && format == 32 && nitems > 0) {
+ long *state = (long *) data;
+ switch (state[0]) {
+ case WithdrawnState:
+ // if we are in the withdrawn state, we are free
+ // to reuse this window provided we remove the
+ // WM_STATE property (ICCCM 4.1.3.1)
+ XDeleteProperty(X11->display, internalWinId(), ATOM(WM_STATE));
+
+ // set the parent id to zero, so that show() will
+ // work again
+ d->topData()->parentWinId = 0;
+ d->topData()->validWMState = 0;
+ // map the window if we were waiting for a
+ // transition to withdrawn
+ if (X11->deferred_map.removeAll(this)) {
+ doDeferredMap();
+ } else if (isVisible()
+ && !testAttribute(Qt::WA_Mapped)
+ && !testAttribute(Qt::WA_OutsideWSRange)) {
+ // so that show() will work again. As stated
+ // in the ICCCM section 4.1.4: "Only the
+ // client can effect a transition into or out
+ // of the Withdrawn state.", but apparently
+ // this particular window manager doesn't seem
+ // to care
+ setAttribute(Qt::WA_WState_ExplicitShowHide, false);
+ setAttribute(Qt::WA_WState_Visible, false);
+ }
+ break;
+
+ case IconicState:
+ d->topData()->validWMState = 1;
+ if (!isMinimized()) {
+ // window was minimized
+ this->data->window_state = this->data->window_state | Qt::WindowMinimized;
+ QWindowStateChangeEvent e(Qt::WindowStates(this->data->window_state & ~Qt::WindowMinimized));
+ QApplication::sendSpontaneousEvent(this, &e);
+ }
+ break;
+
+ default:
+ d->topData()->validWMState = 1;
+ if (isMinimized()) {
+ // window was un-minimized
+ this->data->window_state &= ~Qt::WindowMinimized;
+ QWindowStateChangeEvent e(Qt::WindowStates(this->data->window_state | Qt::WindowMinimized));
+ QApplication::sendSpontaneousEvent(this, &e);
+ }
+ break;
+ }
+ }
+ }
+ } else if (event->xproperty.atom == ATOM(_NET_WM_WINDOW_OPACITY)) {
+ // the window opacity was changed
+ if (event->xproperty.state == PropertyNewValue) {
+ e = XGetWindowProperty(event->xclient.display,
+ event->xclient.window,
+ ATOM(_NET_WM_WINDOW_OPACITY),
+ 0, 1, False, XA_CARDINAL,
+ &ret, &format, &nitems, &after, &data);
+
+ if (e == Success && ret == XA_CARDINAL && format == 32 && nitems == 1
+ && after == 0 && data) {
+ ulong value = *(ulong*)(data);
+ d->topData()->opacity = uint(value >> 24);
+ }
+ } else
+ d->topData()->opacity = 255;
+ }
+
+ if (data)
+ XFree(data);
+
+ return true;
+}
+
+
+//
+// Paint event translation
+//
+// When receiving many expose events, we compress them (union of all expose
+// rectangles) into one event which is sent to the widget.
+
+struct PaintEventInfo {
+ Window window;
+};
+
+#if defined(Q_C_CALLBACKS)
+extern "C" {
+#endif
+
+static Bool isPaintOrScrollDoneEvent(Display *, XEvent *ev, XPointer a)
+{
+ PaintEventInfo *info = (PaintEventInfo *)a;
+ if (ev->type == Expose || ev->type == GraphicsExpose
+ || (ev->type == ClientMessage && ev->xclient.message_type == ATOM(_QT_SCROLL_DONE)))
+ {
+ if (ev->xexpose.window == info->window)
+ return True;
+ }
+ return False;
+}
+
+#if defined(Q_C_CALLBACKS)
+}
+#endif
+
+
+
+static
+bool translateBySips(QWidget* that, QRect& paintRect)
+{
+ int dx=0, dy=0;
+ int sips=0;
+ for (int i = 0; i < X11->sip_list.size(); ++i) {
+ const QX11Data::ScrollInProgress &sip = X11->sip_list.at(i);
+ if (sip.scrolled_widget == that) {
+ if (sips) {
+ dx += sip.dx;
+ dy += sip.dy;
+ }
+ sips++;
+ }
+ }
+ if (sips > 1) {
+ paintRect.translate(dx, dy);
+ return true;
+ }
+ return false;
+}
+
+void QETWidget::translatePaintEvent(const XEvent *event)
+{
+ if (!isWindow() && testAttribute(Qt::WA_NativeWindow))
+ Q_ASSERT(internalWinId());
+
+ Q_D(QWidget);
+ QRect paintRect(event->xexpose.x, event->xexpose.y,
+ event->xexpose.width, event->xexpose.height);
+ XEvent xevent;
+ PaintEventInfo info;
+ info.window = internalWinId();
+ translateBySips(this, paintRect);
+ paintRect = d->mapFromWS(paintRect);
+
+ QRegion paintRegion = paintRect;
+
+ // WARNING: this is O(number_of_events * number_of_matching_events)
+ while (XCheckIfEvent(X11->display,&xevent,isPaintOrScrollDoneEvent,
+ (XPointer)&info) &&
+ !qt_x11EventFilter(&xevent) &&
+ !x11Event(&xevent)) // send event through filter
+ {
+ if (xevent.type == Expose || xevent.type == GraphicsExpose) {
+ QRect exposure(xevent.xexpose.x,
+ xevent.xexpose.y,
+ xevent.xexpose.width,
+ xevent.xexpose.height);
+ translateBySips(this, exposure);
+ exposure = d->mapFromWS(exposure);
+ paintRegion |= exposure;
+ } else {
+ translateScrollDoneEvent(&xevent);
+ }
+ }
+
+ if (!paintRegion.isEmpty() && !testAttribute(Qt::WA_WState_ConfigPending))
+ d->syncBackingStore(paintRegion);
+}
+
+//
+// Scroll-done event translation.
+//
+
+bool QETWidget::translateScrollDoneEvent(const XEvent *event)
+{
+ long id = event->xclient.data.l[0];
+
+ // Remove any scroll-in-progress record for the given id.
+ for (int i = 0; i < X11->sip_list.size(); ++i) {
+ const QX11Data::ScrollInProgress &sip = X11->sip_list.at(i);
+ if (sip.id == id) {
+ X11->sip_list.removeAt(i);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//
+// ConfigureNotify (window move and resize) event translation
+
+bool QETWidget::translateConfigEvent(const XEvent *event)
+{
+ Q_ASSERT((!isWindow() && !testAttribute(Qt::WA_NativeWindow)) ? internalWinId() : true);
+
+ Q_D(QWidget);
+ bool wasResize = testAttribute(Qt::WA_WState_ConfigPending); // set in QWidget::setGeometry_sys()
+ setAttribute(Qt::WA_WState_ConfigPending, false);
+
+ if (testAttribute(Qt::WA_OutsideWSRange)) {
+ // discard events for windows that have a geometry X can't handle
+ XEvent xevent;
+ while (XCheckTypedWindowEvent(X11->display,internalWinId(), ConfigureNotify,&xevent) &&
+ !qt_x11EventFilter(&xevent) &&
+ !x11Event(&xevent)) // send event through filter
+ ;
+ return true;
+ }
+
+ const QSize oldSize = size();
+
+ if (isWindow()) {
+ QPoint newCPos(geometry().topLeft());
+ QSize newSize(event->xconfigure.width, event->xconfigure.height);
+
+ bool trust = isVisible()
+ && (d->topData()->parentWinId == XNone ||
+ d->topData()->parentWinId == QX11Info::appRootWindow());
+ bool isCPos = false;
+
+ if (event->xconfigure.send_event || trust) {
+ // if a ConfigureNotify comes from a real sendevent request, we can
+ // trust its values.
+ newCPos.rx() = event->xconfigure.x + event->xconfigure.border_width;
+ newCPos.ry() = event->xconfigure.y + event->xconfigure.border_width;
+ isCPos = true;
+ }
+ if (isVisible())
+ QApplication::syncX();
+
+ if (d->extra->compress_events) {
+ // ConfigureNotify compression for faster opaque resizing
+ XEvent otherEvent;
+ while (XCheckTypedWindowEvent(X11->display, internalWinId(), ConfigureNotify,
+ &otherEvent)) {
+ if (qt_x11EventFilter(&otherEvent))
+ continue;
+
+ if (x11Event(&otherEvent))
+ continue;
+
+ if (otherEvent.xconfigure.event != otherEvent.xconfigure.window)
+ continue;
+
+ newSize.setWidth(otherEvent.xconfigure.width);
+ newSize.setHeight(otherEvent.xconfigure.height);
+
+ if (otherEvent.xconfigure.send_event || trust) {
+ newCPos.rx() = otherEvent.xconfigure.x +
+ otherEvent.xconfigure.border_width;
+ newCPos.ry() = otherEvent.xconfigure.y +
+ otherEvent.xconfigure.border_width;
+ isCPos = true;
+ }
+ }
+#ifndef QT_NO_XSYNC
+ qt_sync_request_event_data sync_event;
+ sync_event.window = internalWinId();
+ for (XEvent ev;;) {
+ if (!XCheckIfEvent(X11->display, &ev, &qt_sync_request_scanner, (XPointer)&sync_event))
+ break;
+ }
+#endif // QT_NO_XSYNC
+ }
+
+ if (!isCPos) {
+ // we didn't get an updated position of the toplevel.
+ // either we haven't moved or there is a bug in the window manager.
+ // anyway, let's query the position to be certain.
+ int x, y;
+ Window child;
+ XTranslateCoordinates(X11->display, internalWinId(),
+ QApplication::desktop()->screen(d->xinfo.screen())->internalWinId(),
+ 0, 0, &x, &y, &child);
+ newCPos.rx() = x;
+ newCPos.ry() = y;
+ }
+
+ QRect cr (geometry());
+ if (newCPos != cr.topLeft()) { // compare with cpos (exluding frame)
+ QPoint oldPos = geometry().topLeft();
+ cr.moveTopLeft(newCPos);
+ data->crect = cr;
+ if (isVisible()) {
+ QMoveEvent e(newCPos, oldPos); // pos (including frame), not cpos
+ QApplication::sendSpontaneousEvent(this, &e);
+ } else {
+ setAttribute(Qt::WA_PendingMoveEvent, true);
+ }
+ }
+ if (newSize != cr.size()) { // size changed
+ cr.setSize(newSize);
+ data->crect = cr;
+
+ uint old_state = data->window_state;
+ if (!X11->isSupportedByWM(ATOM(_NET_WM_STATE_MAXIMIZED_VERT))
+ && !X11->isSupportedByWM(ATOM(_NET_WM_STATE_MAXIMIZED_HORZ)))
+ data->window_state &= ~Qt::WindowMaximized;
+ if (!X11->isSupportedByWM(ATOM(_NET_WM_STATE_FULLSCREEN)))
+ data->window_state &= ~Qt::WindowFullScreen;
+
+ if (old_state != data->window_state) {
+ QWindowStateChangeEvent e((Qt::WindowStates) old_state);
+ QApplication::sendEvent(this, &e);
+ }
+
+ if (!isVisible())
+ setAttribute(Qt::WA_PendingResizeEvent, true);
+ wasResize = true;
+ }
+
+ } else {
+ XEvent xevent;
+ while (XCheckTypedWindowEvent(X11->display,internalWinId(), ConfigureNotify,&xevent) &&
+ !qt_x11EventFilter(&xevent) &&
+ !x11Event(&xevent)) // send event through filter
+ ;
+ }
+
+ if (wasResize) {
+ if (isVisible() && data->crect.size() != oldSize) {
+ Q_ASSERT(d->extra->topextra);
+ QWidgetBackingStore *bs = d->extra->topextra->backingStore.data();
+ const bool hasStaticContents = bs && bs->hasStaticContents();
+ // If we have a backing store with static contents, we have to disable the top-level
+ // resize optimization in order to get invalidated regions for resized widgets.
+ // The optimization discards all invalidateBuffer() calls since we're going to
+ // repaint everything anyways, but that's not the case with static contents.
+ if (!hasStaticContents)
+ d->extra->topextra->inTopLevelResize = true;
+ QResizeEvent e(data->crect.size(), oldSize);
+ QApplication::sendSpontaneousEvent(this, &e);
+ }
+
+ const bool waitingForMapNotify = d->extra->topextra && d->extra->topextra->waitingForMapNotify;
+ if (!waitingForMapNotify) {
+ if (d->paintOnScreen()) {
+ QRegion updateRegion(rect());
+ if (testAttribute(Qt::WA_StaticContents))
+ updateRegion -= QRect(0, 0, oldSize.width(), oldSize.height());
+ d->syncBackingStore(updateRegion);
+ } else {
+ d->syncBackingStore();
+ }
+ }
+
+ if (d->extra && d->extra->topextra)
+ d->extra->topextra->inTopLevelResize = false;
+ }
+#ifndef QT_NO_XSYNC
+ if (QTLWExtra *tlwExtra = d->maybeTopData()) {
+ if (tlwExtra->newCounterValueLo != 0 || tlwExtra->newCounterValueHi != 0) {
+ XSyncValue value;
+ XSyncIntsToValue(&value,
+ tlwExtra->newCounterValueLo,
+ tlwExtra->newCounterValueHi);
+
+ XSyncSetCounter(X11->display, tlwExtra->syncUpdateCounter, value);
+ tlwExtra->newCounterValueHi = 0;
+ tlwExtra->newCounterValueLo = 0;
+ }
+ }
+#endif
+ return true;
+}
+
+//
+// Close window event translation.
+//
+bool QETWidget::translateCloseEvent(const XEvent *)
+{
+ Q_D(QWidget);
+ return d->close_helper(QWidgetPrivate::CloseWithSpontaneousEvent);
+}
+
+
+void QApplication::setCursorFlashTime(int msecs)
+{
+ QApplicationPrivate::cursor_flash_time = msecs;
+}
+
+int QApplication::cursorFlashTime()
+{
+ return QApplicationPrivate::cursor_flash_time;
+}
+
+void QApplication::setDoubleClickInterval(int ms)
+{
+ QApplicationPrivate::mouse_double_click_time = ms;
+}
+
+int QApplication::doubleClickInterval()
+{
+ return QApplicationPrivate::mouse_double_click_time;
+}
+
+void QApplication::setKeyboardInputInterval(int ms)
+{
+ QApplicationPrivate::keyboard_input_time = ms;
+}
+
+int QApplication::keyboardInputInterval()
+{
+ return QApplicationPrivate::keyboard_input_time;
+}
+
+#ifndef QT_NO_WHEELEVENT
+void QApplication::setWheelScrollLines(int n)
+{
+ QApplicationPrivate::wheel_scroll_lines = n;
+}
+
+int QApplication::wheelScrollLines()
+{
+ return QApplicationPrivate::wheel_scroll_lines;
+}
+#endif
+
+void QApplication::setEffectEnabled(Qt::UIEffect effect, bool enable)
+{
+ switch (effect) {
+ case Qt::UI_AnimateMenu:
+ if (enable) QApplicationPrivate::fade_menu = false;
+ QApplicationPrivate::animate_menu = enable;
+ break;
+ case Qt::UI_FadeMenu:
+ if (enable)
+ QApplicationPrivate::animate_menu = true;
+ QApplicationPrivate::fade_menu = enable;
+ break;
+ case Qt::UI_AnimateCombo:
+ QApplicationPrivate::animate_combo = enable;
+ break;
+ case Qt::UI_AnimateTooltip:
+ if (enable) QApplicationPrivate::fade_tooltip = false;
+ QApplicationPrivate::animate_tooltip = enable;
+ break;
+ case Qt::UI_FadeTooltip:
+ if (enable)
+ QApplicationPrivate::animate_tooltip = true;
+ QApplicationPrivate::fade_tooltip = enable;
+ break;
+ case Qt::UI_AnimateToolBox:
+ QApplicationPrivate::animate_toolbox = enable;
+ break;
+ default:
+ QApplicationPrivate::animate_ui = enable;
+ break;
+ }
+}
+
+bool QApplication::isEffectEnabled(Qt::UIEffect effect)
+{
+ if (QColormap::instance().depth() < 16 || !QApplicationPrivate::animate_ui)
+ return false;
+
+ switch(effect) {
+ case Qt::UI_AnimateMenu:
+ return QApplicationPrivate::animate_menu;
+ case Qt::UI_FadeMenu:
+ return QApplicationPrivate::fade_menu;
+ case Qt::UI_AnimateCombo:
+ return QApplicationPrivate::animate_combo;
+ case Qt::UI_AnimateTooltip:
+ return QApplicationPrivate::animate_tooltip;
+ case Qt::UI_FadeTooltip:
+ return QApplicationPrivate::fade_tooltip;
+ case Qt::UI_AnimateToolBox:
+ return QApplicationPrivate::animate_toolbox;
+ default:
+ return QApplicationPrivate::animate_ui;
+ }
+}
+
+/*****************************************************************************
+ Session management support
+ *****************************************************************************/
+
+#ifndef QT_NO_SESSIONMANAGER
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <X11/SM/SMlib.h>
+QT_END_INCLUDE_NAMESPACE
+
+class QSessionManagerPrivate : public QObjectPrivate
+{
+public:
+ QSessionManagerPrivate(QSessionManager* mgr, QString& id, QString& key)
+ : QObjectPrivate(), sm(mgr), sessionId(id), sessionKey(key),
+ restartHint(QSessionManager::RestartIfRunning), eventLoop(0) {}
+ QSessionManager* sm;
+ QStringList restartCommand;
+ QStringList discardCommand;
+ QString& sessionId;
+ QString& sessionKey;
+ QSessionManager::RestartHint restartHint;
+ QEventLoop *eventLoop;
+};
+
+class QSmSocketReceiver : public QObject
+{
+ Q_OBJECT
+public:
+ QSmSocketReceiver(int socket)
+ {
+ QSocketNotifier* sn = new QSocketNotifier(socket, QSocketNotifier::Read, this);
+ connect(sn, SIGNAL(activated(int)), this, SLOT(socketActivated(int)));
+ }
+
+public slots:
+ void socketActivated(int);
+};
+
+
+static SmcConn smcConnection = 0;
+static bool sm_interactionActive;
+static bool sm_smActive;
+static int sm_interactStyle;
+static int sm_saveType;
+static bool sm_cancel;
+// static bool sm_waitingForPhase2; ### never used?!?
+static bool sm_waitingForInteraction;
+static bool sm_isshutdown;
+// static bool sm_shouldbefast; ### never used?!?
+static bool sm_phase2;
+static bool sm_in_phase2;
+
+static QSmSocketReceiver* sm_receiver = 0;
+
+static void resetSmState();
+static void sm_setProperty(const char* name, const char* type,
+ int num_vals, SmPropValue* vals);
+static void sm_saveYourselfCallback(SmcConn smcConn, SmPointer clientData,
+ int saveType, Bool shutdown , int interactStyle, Bool fast);
+static void sm_saveYourselfPhase2Callback(SmcConn smcConn, SmPointer clientData) ;
+static void sm_dieCallback(SmcConn smcConn, SmPointer clientData) ;
+static void sm_shutdownCancelledCallback(SmcConn smcConn, SmPointer clientData);
+static void sm_saveCompleteCallback(SmcConn smcConn, SmPointer clientData);
+static void sm_interactCallback(SmcConn smcConn, SmPointer clientData);
+static void sm_performSaveYourself(QSessionManagerPrivate*);
+
+static void resetSmState()
+{
+// sm_waitingForPhase2 = false; ### never used?!?
+ sm_waitingForInteraction = false;
+ sm_interactionActive = false;
+ sm_interactStyle = SmInteractStyleNone;
+ sm_smActive = false;
+ qt_sm_blockUserInput = false;
+ sm_isshutdown = false;
+// sm_shouldbefast = false; ### never used?!?
+ sm_phase2 = false;
+ sm_in_phase2 = false;
+}
+
+
+// theoretically it's possible to set several properties at once. For
+// simplicity, however, we do just one property at a time
+static void sm_setProperty(const char* name, const char* type,
+ int num_vals, SmPropValue* vals)
+{
+ if (num_vals) {
+ SmProp prop;
+ prop.name = (char*)name;
+ prop.type = (char*)type;
+ prop.num_vals = num_vals;
+ prop.vals = vals;
+
+ SmProp* props[1];
+ props[0] = &prop;
+ SmcSetProperties(smcConnection, 1, props);
+ }
+ else {
+ char* names[1];
+ names[0] = (char*) name;
+ SmcDeleteProperties(smcConnection, 1, names);
+ }
+}
+
+static void sm_setProperty(const QString& name, const QString& value)
+{
+ QByteArray v = value.toUtf8();
+ SmPropValue prop;
+ prop.length = v.length();
+ prop.value = (SmPointer) v.constData();
+ sm_setProperty(name.toLatin1().data(), SmARRAY8, 1, &prop);
+}
+
+static void sm_setProperty(const QString& name, const QStringList& value)
+{
+ SmPropValue *prop = new SmPropValue[value.count()];
+ int count = 0;
+ QList<QByteArray> vl;
+ for (QStringList::ConstIterator it = value.begin(); it != value.end(); ++it) {
+ prop[count].length = (*it).length();
+ vl.append((*it).toUtf8());
+ prop[count].value = (char*)vl.last().data();
+ ++count;
+ }
+ sm_setProperty(name.toLatin1().data(), SmLISTofARRAY8, count, prop);
+ delete [] prop;
+}
+
+
+// workaround for broken libsm, see below
+struct QT_smcConn {
+ unsigned int save_yourself_in_progress : 1;
+ unsigned int shutdown_in_progress : 1;
+};
+
+static void sm_saveYourselfCallback(SmcConn smcConn, SmPointer clientData,
+ int saveType, Bool shutdown , int interactStyle, Bool /*fast*/)
+{
+ if (smcConn != smcConnection)
+ return;
+ sm_cancel = false;
+ sm_smActive = true;
+ sm_isshutdown = shutdown;
+ sm_saveType = saveType;
+ sm_interactStyle = interactStyle;
+// sm_shouldbefast = fast; ### never used?!?
+
+ // ugly workaround for broken libSM. libSM should do that _before_
+ // actually invoking the callback in sm_process.c
+ ((QT_smcConn*)smcConn)->save_yourself_in_progress = true;
+ if (sm_isshutdown)
+ ((QT_smcConn*)smcConn)->shutdown_in_progress = true;
+
+ sm_performSaveYourself((QSessionManagerPrivate*) clientData);
+ if (!sm_isshutdown) // we cannot expect a confirmation message in that case
+ resetSmState();
+}
+
+static void sm_performSaveYourself(QSessionManagerPrivate* smd)
+{
+ if (sm_isshutdown)
+ qt_sm_blockUserInput = true;
+
+ QSessionManager* sm = smd->sm;
+
+ // generate a new session key
+ timeval tv;
+ gettimeofday(&tv, 0);
+ smd->sessionKey = QString::number(qulonglong(tv.tv_sec)) + QLatin1Char('_') + QString::number(qulonglong(tv.tv_usec));
+
+ QStringList arguments = qApp->arguments();
+ QString argument0 = arguments.isEmpty() ? qApp->applicationFilePath() : arguments.at(0);
+
+ // tell the session manager about our program in best POSIX style
+ sm_setProperty(QString::fromLatin1(SmProgram), argument0);
+ // tell the session manager about our user as well.
+ struct passwd *entryPtr = 0;
+#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && (_POSIX_THREAD_SAFE_FUNCTIONS - 0 > 0)
+ QVarLengthArray<char, 1024> buf(qMax<long>(sysconf(_SC_GETPW_R_SIZE_MAX), 1024L));
+ struct passwd entry;
+ while (getpwuid_r(geteuid(), &entry, buf.data(), buf.size(), &entryPtr) == ERANGE) {
+ if (buf.size() >= 32768) {
+ // too big already, fail
+ static char badusername[] = "";
+ entryPtr = &entry;
+ entry.pw_name = badusername;
+ break;
+ }
+
+ // retry with a bigger buffer
+ buf.resize(buf.size() * 2);
+ }
+#else
+ entryPtr = getpwuid(geteuid());
+#endif
+ if (entryPtr)
+ sm_setProperty(QString::fromLatin1(SmUserID), QString::fromLatin1(entryPtr->pw_name));
+
+ // generate a restart and discard command that makes sense
+ QStringList restart;
+ restart << argument0 << QLatin1String("-session")
+ << smd->sessionId + QLatin1Char('_') + smd->sessionKey;
+ if (qstricmp(appName, QX11Info::appClass()) != 0)
+ restart << QLatin1String("-name") << qAppName();
+ sm->setRestartCommand(restart);
+ QStringList discard;
+ sm->setDiscardCommand(discard);
+
+ switch (sm_saveType) {
+ case SmSaveBoth:
+ qApp->commitData(*sm);
+ if (sm_isshutdown && sm_cancel)
+ break; // we cancelled the shutdown, no need to save state
+ // fall through
+ case SmSaveLocal:
+ qApp->saveState(*sm);
+ break;
+ case SmSaveGlobal:
+ qApp->commitData(*sm);
+ break;
+ default:
+ break;
+ }
+
+ if (sm_phase2 && !sm_in_phase2) {
+ SmcRequestSaveYourselfPhase2(smcConnection, sm_saveYourselfPhase2Callback, (SmPointer*) smd);
+ qt_sm_blockUserInput = false;
+ }
+ else {
+ // close eventual interaction monitors and cancel the
+ // shutdown, if required. Note that we can only cancel when
+ // performing a shutdown, it does not work for checkpoints
+ if (sm_interactionActive) {
+ SmcInteractDone(smcConnection, sm_isshutdown && sm_cancel);
+ sm_interactionActive = false;
+ }
+ else if (sm_cancel && sm_isshutdown) {
+ if (sm->allowsErrorInteraction()) {
+ SmcInteractDone(smcConnection, True);
+ sm_interactionActive = false;
+ }
+ }
+
+ // set restart and discard command in session manager
+ sm_setProperty(QString::fromLatin1(SmRestartCommand), sm->restartCommand());
+ sm_setProperty(QString::fromLatin1(SmDiscardCommand), sm->discardCommand());
+
+ // set the restart hint
+ SmPropValue prop;
+ prop.length = sizeof(int);
+ int value = sm->restartHint();
+ prop.value = (SmPointer) &value;
+ sm_setProperty(SmRestartStyleHint, SmCARD8, 1, &prop);
+
+ // we are done
+ SmcSaveYourselfDone(smcConnection, !sm_cancel);
+ }
+}
+
+static void sm_dieCallback(SmcConn smcConn, SmPointer /* clientData */)
+{
+ if (smcConn != smcConnection)
+ return;
+ resetSmState();
+ QEvent quitEvent(QEvent::Quit);
+ QApplication::sendEvent(qApp, &quitEvent);
+}
+
+static void sm_shutdownCancelledCallback(SmcConn smcConn, SmPointer clientData)
+{
+ if (smcConn != smcConnection)
+ return;
+ if (sm_waitingForInteraction)
+ ((QSessionManagerPrivate *) clientData)->eventLoop->exit();
+ resetSmState();
+}
+
+static void sm_saveCompleteCallback(SmcConn smcConn, SmPointer /*clientData */)
+{
+ if (smcConn != smcConnection)
+ return;
+ resetSmState();
+}
+
+static void sm_interactCallback(SmcConn smcConn, SmPointer clientData)
+{
+ if (smcConn != smcConnection)
+ return;
+ if (sm_waitingForInteraction)
+ ((QSessionManagerPrivate *) clientData)->eventLoop->exit();
+}
+
+static void sm_saveYourselfPhase2Callback(SmcConn smcConn, SmPointer clientData)
+{
+ if (smcConn != smcConnection)
+ return;
+ sm_in_phase2 = true;
+ sm_performSaveYourself((QSessionManagerPrivate*) clientData);
+}
+
+
+void QSmSocketReceiver::socketActivated(int)
+{
+ IceProcessMessages(SmcGetIceConnection(smcConnection), 0, 0);
+}
+
+
+#undef Bool
+QT_BEGIN_INCLUDE_NAMESPACE
+#include "qapplication_x11.moc"
+QT_END_INCLUDE_NAMESPACE
+
+QSessionManager::QSessionManager(QApplication * app, QString &id, QString& key)
+ : QObject(*new QSessionManagerPrivate(this, id, key), app)
+{
+ Q_D(QSessionManager);
+ d->restartHint = RestartIfRunning;
+
+ resetSmState();
+ char cerror[256];
+ char* myId = 0;
+ QByteArray b_id = id.toLatin1();
+ char* prevId = b_id.data();
+
+ SmcCallbacks cb;
+ cb.save_yourself.callback = sm_saveYourselfCallback;
+ cb.save_yourself.client_data = (SmPointer) d;
+ cb.die.callback = sm_dieCallback;
+ cb.die.client_data = (SmPointer) d;
+ cb.save_complete.callback = sm_saveCompleteCallback;
+ cb.save_complete.client_data = (SmPointer) d;
+ cb.shutdown_cancelled.callback = sm_shutdownCancelledCallback;
+ cb.shutdown_cancelled.client_data = (SmPointer) d;
+
+ // avoid showing a warning message below
+ if (qgetenv("SESSION_MANAGER").isEmpty())
+ return;
+
+ smcConnection = SmcOpenConnection(0, 0, 1, 0,
+ SmcSaveYourselfProcMask |
+ SmcDieProcMask |
+ SmcSaveCompleteProcMask |
+ SmcShutdownCancelledProcMask,
+ &cb,
+ prevId,
+ &myId,
+ 256, cerror);
+
+ id = QString::fromLatin1(myId);
+ ::free(myId); // it was allocated by C
+
+ QString error = QString::fromLocal8Bit(cerror);
+ if (!smcConnection) {
+ qWarning("Qt: Session management error: %s", qPrintable(error));
+ }
+ else {
+ sm_receiver = new QSmSocketReceiver(IceConnectionNumber(SmcGetIceConnection(smcConnection)));
+ }
+}
+
+QSessionManager::~QSessionManager()
+{
+ if (smcConnection)
+ SmcCloseConnection(smcConnection, 0, 0);
+ smcConnection = 0;
+ delete sm_receiver;
+}
+
+QString QSessionManager::sessionId() const
+{
+ Q_D(const QSessionManager);
+ return d->sessionId;
+}
+
+QString QSessionManager::sessionKey() const
+{
+ Q_D(const QSessionManager);
+ return d->sessionKey;
+}
+
+
+void* QSessionManager::handle() const
+{
+ return (void*) smcConnection;
+}
+
+
+bool QSessionManager::allowsInteraction()
+{
+ Q_D(QSessionManager);
+ if (sm_interactionActive)
+ return true;
+
+ if (sm_waitingForInteraction)
+ return false;
+
+ if (sm_interactStyle == SmInteractStyleAny) {
+ sm_waitingForInteraction = SmcInteractRequest(smcConnection, SmDialogNormal,
+ sm_interactCallback, (SmPointer*) d);
+ }
+ if (sm_waitingForInteraction) {
+ QEventLoop eventLoop;
+ d->eventLoop = &eventLoop;
+ (void) eventLoop.exec();
+ d->eventLoop = 0;
+
+ sm_waitingForInteraction = false;
+ if (sm_smActive) { // not cancelled
+ sm_interactionActive = true;
+ qt_sm_blockUserInput = false;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool QSessionManager::allowsErrorInteraction()
+{
+ Q_D(QSessionManager);
+ if (sm_interactionActive)
+ return true;
+
+ if (sm_waitingForInteraction)
+ return false;
+
+ if (sm_interactStyle == SmInteractStyleAny || sm_interactStyle == SmInteractStyleErrors) {
+ sm_waitingForInteraction = SmcInteractRequest(smcConnection, SmDialogError,
+ sm_interactCallback, (SmPointer*) d);
+ }
+ if (sm_waitingForInteraction) {
+ QEventLoop eventLoop;
+ d->eventLoop = &eventLoop;
+ (void) eventLoop.exec();
+ d->eventLoop = 0;
+
+ sm_waitingForInteraction = false;
+ if (sm_smActive) { // not cancelled
+ sm_interactionActive = true;
+ qt_sm_blockUserInput = false;
+ return true;
+ }
+ }
+ return false;
+}
+
+void QSessionManager::release()
+{
+ if (sm_interactionActive) {
+ SmcInteractDone(smcConnection, False);
+ sm_interactionActive = false;
+ if (sm_smActive && sm_isshutdown)
+ qt_sm_blockUserInput = true;
+ }
+}
+
+void QSessionManager::cancel()
+{
+ sm_cancel = true;
+}
+
+void QSessionManager::setRestartHint(QSessionManager::RestartHint hint)
+{
+ Q_D(QSessionManager);
+ d->restartHint = hint;
+}
+
+QSessionManager::RestartHint QSessionManager::restartHint() const
+{
+ Q_D(const QSessionManager);
+ return d->restartHint;
+}
+
+void QSessionManager::setRestartCommand(const QStringList& command)
+{
+ Q_D(QSessionManager);
+ d->restartCommand = command;
+}
+
+QStringList QSessionManager::restartCommand() const
+{
+ Q_D(const QSessionManager);
+ return d->restartCommand;
+}
+
+void QSessionManager::setDiscardCommand(const QStringList& command)
+{
+ Q_D(QSessionManager);
+ d->discardCommand = command;
+}
+
+QStringList QSessionManager::discardCommand() const
+{
+ Q_D(const QSessionManager);
+ return d->discardCommand;
+}
+
+void QSessionManager::setManagerProperty(const QString& name, const QString& value)
+{
+ sm_setProperty(name, value);
+}
+
+void QSessionManager::setManagerProperty(const QString& name, const QStringList& value)
+{
+ sm_setProperty(name, value);
+}
+
+bool QSessionManager::isPhase2() const
+{
+ return sm_in_phase2;
+}
+
+void QSessionManager::requestPhase2()
+{
+ sm_phase2 = true;
+}
+
+#endif // QT_NO_SESSIONMANAGER
+
+#if defined(QT_RX71_MULTITOUCH)
+
+static inline int testBit(const char *array, int bit)
+{
+ return (array[bit/8] & (1<<(bit%8)));
+}
+
+static int openRX71Device(const QByteArray &deviceName)
+{
+ int fd = open(deviceName, O_RDONLY | O_NONBLOCK);
+ if (fd == -1) {
+ fd = -errno;
+ return fd;
+ }
+
+ // fetch the event type mask and check that the device reports absolute coordinates
+ char eventTypeMask[(EV_MAX + sizeof(char) - 1) * sizeof(char) + 1];
+ memset(eventTypeMask, 0, sizeof(eventTypeMask));
+ if (ioctl(fd, EVIOCGBIT(0, sizeof(eventTypeMask)), eventTypeMask) < 0) {
+ close(fd);
+ return -1;
+ }
+ if (!testBit(eventTypeMask, EV_ABS)) {
+ close(fd);
+ return -1;
+ }
+
+ // make sure that we can get the absolute X and Y positions from the device
+ char absMask[(ABS_MAX + sizeof(char) - 1) * sizeof(char) + 1];
+ memset(absMask, 0, sizeof(absMask));
+ if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absMask)), absMask) < 0) {
+ close(fd);
+ return -1;
+ }
+ if (!testBit(absMask, ABS_X) || !testBit(absMask, ABS_Y)) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+void QApplicationPrivate::initializeMultitouch_sys()
+{
+ Q_Q(QApplication);
+
+ QByteArray deviceName = QByteArray("/dev/input/event");
+ int currentDeviceNumber = 0;
+ for (;;) {
+ int fd = openRX71Device(QByteArray(deviceName + QByteArray::number(currentDeviceNumber++)));
+ if (fd == -ENOENT) {
+ // no more devices
+ break;
+ }
+ if (fd < 0) {
+ // not a touch device
+ continue;
+ }
+
+ struct input_absinfo abs_x, abs_y, abs_z;
+ ioctl(fd, EVIOCGABS(ABS_X), &abs_x);
+ ioctl(fd, EVIOCGABS(ABS_Y), &abs_y);
+ ioctl(fd, EVIOCGABS(ABS_Z), &abs_z);
+
+ int deviceNumber = allRX71TouchPoints.count();
+
+ QSocketNotifier *socketNotifier = new QSocketNotifier(fd, QSocketNotifier::Read, q);
+ QObject::connect(socketNotifier, SIGNAL(activated(int)), q, SLOT(_q_readRX71MultiTouchEvents()));
+
+ RX71TouchPointState touchPointState = {
+ socketNotifier,
+ QTouchEvent::TouchPoint(deviceNumber),
+
+ abs_x.minimum, abs_x.maximum, q->desktop()->screenGeometry().width(),
+ abs_y.minimum, abs_y.maximum, q->desktop()->screenGeometry().height(),
+ abs_z.minimum, abs_z.maximum
+ };
+ allRX71TouchPoints.append(touchPointState);
+ }
+
+ hasRX71MultiTouch = allRX71TouchPoints.count() > 1;
+ if (!hasRX71MultiTouch) {
+ for (int i = 0; i < allRX71TouchPoints.count(); ++i) {
+ QSocketNotifier *socketNotifier = allRX71TouchPoints.at(i).socketNotifier;
+ close(socketNotifier->socket());
+ delete socketNotifier;
+ }
+ allRX71TouchPoints.clear();
+ }
+}
+
+void QApplicationPrivate::cleanupMultitouch_sys()
+{
+ hasRX71MultiTouch = false;
+ for (int i = 0; i < allRX71TouchPoints.count(); ++i) {
+ QSocketNotifier *socketNotifier = allRX71TouchPoints.at(i).socketNotifier;
+ close(socketNotifier->socket());
+ delete socketNotifier;
+ }
+ allRX71TouchPoints.clear();
+}
+
+bool QApplicationPrivate::readRX71MultiTouchEvents(int deviceNumber)
+{
+ RX71TouchPointState &touchPointState = allRX71TouchPoints[deviceNumber];
+ QSocketNotifier *socketNotifier = touchPointState.socketNotifier;
+ int fd = socketNotifier->socket();
+
+ QTouchEvent::TouchPoint &touchPoint = touchPointState.touchPoint;
+
+ bool down = touchPoint.state() != Qt::TouchPointReleased;
+ if (down)
+ touchPoint.setState(Qt::TouchPointStationary);
+
+ bool changed = false;
+ for (;;) {
+ struct input_event inputEvent;
+ int bytesRead = read(fd, &inputEvent, sizeof(inputEvent));
+ if (bytesRead <= 0)
+ break;
+ if (bytesRead != sizeof(inputEvent)) {
+ qWarning("Qt: INTERNAL ERROR: short read in readRX71MultiTouchEvents()");
+ return false;
+ }
+
+ switch (inputEvent.type) {
+ case EV_SYN:
+ changed = true;
+ switch (touchPoint.state()) {
+ case Qt::TouchPointPressed:
+ case Qt::TouchPointReleased:
+ // make sure we don't compress pressed and releases with any other events
+ return changed;
+ default:
+ break;
+ }
+ continue;
+ case EV_KEY:
+ case EV_ABS:
+ break;
+ default:
+ qWarning("Qt: WARNING: unknown event type %d on multitouch device", inputEvent.type);
+ continue;
+ }
+
+ QPointF screenPos = touchPoint.screenPos();
+ switch (inputEvent.code) {
+ case BTN_TOUCH:
+ if (!down && inputEvent.value != 0)
+ touchPoint.setState(Qt::TouchPointPressed);
+ else if (down && inputEvent.value == 0)
+ touchPoint.setState(Qt::TouchPointReleased);
+ break;
+ case ABS_TOOL_WIDTH:
+ case ABS_VOLUME:
+ case ABS_PRESSURE:
+ // ignore for now
+ break;
+ case ABS_X:
+ {
+ qreal newValue = ((qreal(inputEvent.value - touchPointState.minX)
+ / qreal(touchPointState.maxX - touchPointState.minX))
+ * touchPointState.scaleX);
+ screenPos.rx() = newValue;
+ touchPoint.setScreenPos(screenPos);
+ break;
+ }
+ case ABS_Y:
+ {
+ qreal newValue = ((qreal(inputEvent.value - touchPointState.minY)
+ / qreal(touchPointState.maxY - touchPointState.minY))
+ * touchPointState.scaleY);
+ screenPos.ry() = newValue;
+ touchPoint.setScreenPos(screenPos);
+ break;
+ }
+ case ABS_Z:
+ {
+ // map Z (signal strength) to pressure for now
+ qreal newValue = (qreal(inputEvent.value - touchPointState.minZ)
+ / qreal(touchPointState.maxZ - touchPointState.minZ));
+ touchPoint.setPressure(newValue);
+ break;
+ }
+ default:
+ qWarning("Qt: WARNING: unknown event code %d on multitouch device", inputEvent.code);
+ continue;
+ }
+ }
+
+ if (down && touchPoint.state() != Qt::TouchPointReleased)
+ touchPoint.setState(changed ? Qt::TouchPointMoved : Qt::TouchPointStationary);
+
+ return changed;
+}
+
+void QApplicationPrivate::_q_readRX71MultiTouchEvents()
+{
+ // read touch events from all devices
+ bool changed = false;
+ for (int i = 0; i < allRX71TouchPoints.count(); ++i)
+ changed = readRX71MultiTouchEvents(i) || changed;
+ if (!changed)
+ return;
+
+ QList<QTouchEvent::TouchPoint> touchPoints;
+ for (int i = 0; i < allRX71TouchPoints.count(); ++i)
+ touchPoints.append(allRX71TouchPoints.at(i).touchPoint);
+
+ translateRawTouchEvent(0, QTouchEvent::TouchScreen, touchPoints);
+}
+
+#else // !QT_RX71_MULTITOUCH
+
+void QApplicationPrivate::initializeMultitouch_sys()
+{ }
+void QApplicationPrivate::cleanupMultitouch_sys()
+{ }
+
+#endif // QT_RX71_MULTITOUCH
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/x11/qclipboard_x11.cpp b/src/widgets/platforms/x11/qclipboard_x11.cpp
new file mode 100644
index 0000000000..97e076cbc2
--- /dev/null
+++ b/src/widgets/platforms/x11/qclipboard_x11.cpp
@@ -0,0 +1,1539 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// #define QCLIPBOARD_DEBUG
+// #define QCLIPBOARD_DEBUG_VERBOSE
+
+#ifdef QCLIPBOARD_DEBUG
+# define DEBUG qDebug
+#else
+# define DEBUG if (false) qDebug
+#endif
+
+#ifdef QCLIPBOARD_DEBUG_VERBOSE
+# define VDEBUG qDebug
+#else
+# define VDEBUG if (false) qDebug
+#endif
+
+#include "qplatformdefs.h"
+
+#include "qclipboard.h"
+#include "qclipboard_p.h"
+
+#ifndef QT_NO_CLIPBOARD
+
+#include "qabstracteventdispatcher.h"
+#include "qapplication.h"
+#include "qdesktopwidget.h"
+#include "qbitmap.h"
+#include "qiodevice.h"
+#include "qbuffer.h"
+#include "qtextcodec.h"
+#include "qlist.h"
+#include "qmap.h"
+#include "qapplication_p.h"
+#include "qevent.h"
+#include "qt_x11_p.h"
+#include "qx11info_x11.h"
+#include "qimagewriter.h"
+#include "qelapsedtimer.h"
+#include "qvariant.h"
+#include "qdnd_p.h"
+#include <private/qwidget_p.h>
+
+#ifndef QT_NO_XFIXES
+#include <X11/extensions/Xfixes.h>
+#endif // QT_NO_XFIXES
+
+QT_BEGIN_NAMESPACE
+
+/*****************************************************************************
+ Internal QClipboard functions for X11.
+ *****************************************************************************/
+
+static int clipboard_timeout = 5000; // 5s timeout on clipboard operations
+
+static QWidget * owner = 0;
+static QWidget *requestor = 0;
+static bool timer_event_clear = false;
+static int timer_id = 0;
+
+static int pending_timer_id = 0;
+static bool pending_clipboard_changed = false;
+static bool pending_selection_changed = false;
+
+
+// event capture mechanism for qt_xclb_wait_for_event
+static bool waiting_for_data = false;
+static bool has_captured_event = false;
+static Window capture_event_win = XNone;
+static int capture_event_type = -1;
+static XEvent captured_event;
+
+class QClipboardWatcher; // forward decl
+static QClipboardWatcher *selection_watcher = 0;
+static QClipboardWatcher *clipboard_watcher = 0;
+
+static void cleanup()
+{
+ delete owner;
+ delete requestor;
+ owner = 0;
+ requestor = 0;
+}
+
+static
+void setupOwner()
+{
+ if (owner)
+ return;
+ owner = new QWidget(0);
+ owner->setObjectName(QLatin1String("internal clipboard owner"));
+ owner->createWinId();
+ requestor = new QWidget(0);
+ requestor->createWinId();
+ requestor->setObjectName(QLatin1String("internal clipboard requestor"));
+ // We don't need this internal widgets to appear in QApplication::topLevelWidgets()
+ if (QWidgetPrivate::allWidgets) {
+ QWidgetPrivate::allWidgets->remove(owner);
+ QWidgetPrivate::allWidgets->remove(requestor);
+ }
+ qAddPostRoutine(cleanup);
+}
+
+
+class QClipboardWatcher : public QInternalMimeData {
+public:
+ QClipboardWatcher(QClipboard::Mode mode);
+ ~QClipboardWatcher();
+ bool empty() const;
+ virtual bool hasFormat_sys(const QString &mimetype) const;
+ virtual QStringList formats_sys() const;
+
+ QVariant retrieveData_sys(const QString &mimetype, QVariant::Type type) const;
+ QByteArray getDataInFormat(Atom fmtatom) const;
+
+ Atom atom;
+ mutable QStringList formatList;
+ mutable QByteArray format_atoms;
+};
+
+class QClipboardData
+{
+private:
+ QMimeData *&mimeDataRef() const
+ {
+ if(mode == QClipboard::Selection)
+ return selectionData;
+ return clipboardData;
+ }
+
+public:
+ QClipboardData(QClipboard::Mode mode);
+ ~QClipboardData();
+
+ void setSource(QMimeData* s)
+ {
+ if ((mode == QClipboard::Selection && selectionData == s)
+ || clipboardData == s) {
+ return;
+ }
+
+ if (selectionData != clipboardData) {
+ delete mimeDataRef();
+ }
+
+ mimeDataRef() = s;
+ }
+
+ QMimeData *source() const
+ {
+ return mimeDataRef();
+ }
+
+ void clear()
+ {
+ timestamp = CurrentTime;
+ if (selectionData == clipboardData) {
+ mimeDataRef() = 0;
+ } else {
+ QMimeData *&src = mimeDataRef();
+ delete src;
+ src = 0;
+ }
+ }
+
+ static QMimeData *selectionData;
+ static QMimeData *clipboardData;
+ Time timestamp;
+ QClipboard::Mode mode;
+};
+
+QMimeData *QClipboardData::selectionData = 0;
+QMimeData *QClipboardData::clipboardData = 0;
+
+QClipboardData::QClipboardData(QClipboard::Mode clipboardMode)
+{
+ timestamp = CurrentTime;
+ mode = clipboardMode;
+}
+
+QClipboardData::~QClipboardData()
+{ clear(); }
+
+
+static QClipboardData *internalCbData = 0;
+static QClipboardData *internalSelData = 0;
+
+static void cleanupClipboardData()
+{
+ delete internalCbData;
+ internalCbData = 0;
+}
+
+static QClipboardData *clipboardData()
+{
+ if (internalCbData == 0) {
+ internalCbData = new QClipboardData(QClipboard::Clipboard);
+ qAddPostRoutine(cleanupClipboardData);
+ }
+ return internalCbData;
+}
+
+static void cleanupSelectionData()
+{
+ delete internalSelData;
+ internalSelData = 0;
+}
+
+static QClipboardData *selectionData()
+{
+ if (internalSelData == 0) {
+ internalSelData = new QClipboardData(QClipboard::Selection);
+ qAddPostRoutine(cleanupSelectionData);
+ }
+ return internalSelData;
+}
+
+class QClipboardINCRTransaction
+{
+public:
+ QClipboardINCRTransaction(Window w, Atom p, Atom t, int f, QByteArray d, unsigned int i);
+ ~QClipboardINCRTransaction(void);
+
+ int x11Event(XEvent *event);
+
+ Window window;
+ Atom property, target;
+ int format;
+ QByteArray data;
+ unsigned int increment;
+ unsigned int offset;
+};
+
+typedef QMap<Window,QClipboardINCRTransaction*> TransactionMap;
+static TransactionMap *transactions = 0;
+static QApplication::EventFilter prev_event_filter = 0;
+static int incr_timer_id = 0;
+
+static bool qt_x11_incr_event_filter(void *message, long *result)
+{
+ XEvent *event = reinterpret_cast<XEvent *>(message);
+ TransactionMap::Iterator it = transactions->find(event->xany.window);
+ if (it != transactions->end()) {
+ if ((*it)->x11Event(event) != 0)
+ return true;
+ }
+ if (prev_event_filter)
+ return prev_event_filter(event, result);
+ return false;
+}
+
+/*
+ called when no INCR activity has happened for 'clipboard_timeout'
+ milliseconds... we assume that all unfinished transactions have
+ timed out and remove everything from the transaction map
+*/
+static void qt_xclb_incr_timeout(void)
+{
+ qWarning("QClipboard: Timed out while sending data");
+
+ while (transactions)
+ delete *transactions->begin();
+}
+
+QClipboardINCRTransaction::QClipboardINCRTransaction(Window w, Atom p, Atom t, int f,
+ QByteArray d, unsigned int i)
+ : window(w), property(p), target(t), format(f), data(d), increment(i), offset(0u)
+{
+ DEBUG("QClipboard: sending %d bytes (INCR transaction %p)", d.size(), this);
+
+ XSelectInput(X11->display, window, PropertyChangeMask);
+
+ if (! transactions) {
+ VDEBUG("QClipboard: created INCR transaction map");
+ transactions = new TransactionMap;
+ prev_event_filter = qApp->setEventFilter(qt_x11_incr_event_filter);
+ incr_timer_id = QApplication::clipboard()->startTimer(clipboard_timeout);
+ }
+ transactions->insert(window, this);
+}
+
+QClipboardINCRTransaction::~QClipboardINCRTransaction(void)
+{
+ VDEBUG("QClipboard: destroyed INCR transacton %p", this);
+
+ XSelectInput(X11->display, window, NoEventMask);
+
+ transactions->remove(window);
+ if (transactions->isEmpty()) {
+ VDEBUG("QClipboard: no more INCR transactions");
+ delete transactions;
+ transactions = 0;
+
+ (void)qApp->setEventFilter(prev_event_filter);
+
+ if (incr_timer_id != 0) {
+ QApplication::clipboard()->killTimer(incr_timer_id);
+ incr_timer_id = 0;
+ }
+ }
+}
+
+int QClipboardINCRTransaction::x11Event(XEvent *event)
+{
+ if (event->type != PropertyNotify
+ || (event->xproperty.state != PropertyDelete
+ || event->xproperty.atom != property))
+ return 0;
+
+ // restart the INCR timer
+ if (incr_timer_id) QApplication::clipboard()->killTimer(incr_timer_id);
+ incr_timer_id = QApplication::clipboard()->startTimer(clipboard_timeout);
+
+ unsigned int bytes_left = data.size() - offset;
+ if (bytes_left > 0) {
+ unsigned int xfer = qMin(increment, bytes_left);
+ VDEBUG("QClipboard: sending %d bytes, %d remaining (INCR transaction %p)",
+ xfer, bytes_left - xfer, this);
+
+ XChangeProperty(X11->display, window, property, target, format,
+ PropModeReplace, (uchar *) data.data() + offset, xfer);
+ offset += xfer;
+ } else {
+ // INCR transaction finished...
+ XChangeProperty(X11->display, window, property, target, format,
+ PropModeReplace, (uchar *) data.data(), 0);
+ delete this;
+ }
+
+ return 1;
+}
+
+
+/*****************************************************************************
+ QClipboard member functions for X11.
+ *****************************************************************************/
+
+struct qt_init_timestamp_data
+{
+ Time timestamp;
+};
+
+#if defined(Q_C_CALLBACKS)
+extern "C" {
+#endif
+
+static Bool qt_init_timestamp_scanner(Display*, XEvent *event, XPointer arg)
+{
+ qt_init_timestamp_data *data =
+ reinterpret_cast<qt_init_timestamp_data*>(arg);
+ switch(event->type)
+ {
+ case ButtonPress:
+ case ButtonRelease:
+ data->timestamp = event->xbutton.time;
+ break;
+ case MotionNotify:
+ data->timestamp = event->xmotion.time;
+ break;
+ case XKeyPress:
+ case XKeyRelease:
+ data->timestamp = event->xkey.time;
+ break;
+ case PropertyNotify:
+ data->timestamp = event->xproperty.time;
+ break;
+ case EnterNotify:
+ case LeaveNotify:
+ data->timestamp = event->xcrossing.time;
+ break;
+ case SelectionClear:
+ data->timestamp = event->xselectionclear.time;
+ break;
+ default:
+ break;
+ }
+#ifndef QT_NO_XFIXES
+ if (X11->use_xfixes && event->type == (X11->xfixes_eventbase + XFixesSelectionNotify)) {
+ XFixesSelectionNotifyEvent *req =
+ reinterpret_cast<XFixesSelectionNotifyEvent *>(event);
+ data->timestamp = req->selection_timestamp;
+ }
+#endif
+ return false;
+}
+
+#if defined(Q_C_CALLBACKS)
+}
+#endif
+
+QClipboard::QClipboard(QObject *parent)
+ : QObject(*new QClipboardPrivate, parent)
+{
+ // create desktop widget since we need it to get PropertyNotify or
+ // XFixesSelectionNotify events when someone changes the
+ // clipboard.
+ (void)QApplication::desktop();
+
+#ifndef QT_NO_XFIXES
+ if (X11->use_xfixes && X11->ptrXFixesSelectSelectionInput) {
+ const unsigned long eventMask =
+ XFixesSetSelectionOwnerNotifyMask | XFixesSelectionWindowDestroyNotifyMask | XFixesSelectionClientCloseNotifyMask;
+ for (int i = 0; i < X11->screenCount; ++i) {
+ X11->ptrXFixesSelectSelectionInput(X11->display, QX11Info::appRootWindow(i),
+ XA_PRIMARY, eventMask);
+ X11->ptrXFixesSelectSelectionInput(X11->display, QX11Info::appRootWindow(i),
+ ATOM(CLIPBOARD), eventMask);
+ }
+ }
+#endif // QT_NO_XFIXES
+
+ if (X11->time == CurrentTime) {
+ // send a dummy event to myself to get the timestamp from X11.
+ qt_init_timestamp_data data;
+ data.timestamp = CurrentTime;
+ XEvent ev;
+ XCheckIfEvent(X11->display, &ev, &qt_init_timestamp_scanner, (XPointer)&data);
+ if (data.timestamp == CurrentTime) {
+ setupOwner();
+ // We need this value just for completeness, we don't use it.
+ long dummy = 0;
+ Window ownerId = owner->internalWinId();
+ XChangeProperty(X11->display, ownerId,
+ ATOM(CLIP_TEMPORARY), XA_INTEGER, 32,
+ PropModeReplace, (uchar*)&dummy, 1);
+ XWindowEvent(X11->display, ownerId, PropertyChangeMask, &ev);
+ data.timestamp = ev.xproperty.time;
+ XDeleteProperty(X11->display, ownerId, ATOM(CLIP_TEMPORARY));
+ }
+ X11->time = data.timestamp;
+ }
+}
+
+void QClipboard::clear(Mode mode)
+{
+ setMimeData(0, mode);
+}
+
+
+bool QClipboard::supportsMode(Mode mode) const
+{
+ return (mode == Clipboard || mode == Selection);
+}
+
+bool QClipboard::ownsMode(Mode mode) const
+{
+ if (mode == Clipboard)
+ return clipboardData()->timestamp != CurrentTime;
+ else if(mode == Selection)
+ return selectionData()->timestamp != CurrentTime;
+ else
+ return false;
+}
+
+
+// event filter function... captures interesting events while
+// qt_xclb_wait_for_event is running the event loop
+static bool qt_x11_clipboard_event_filter(void *message, long *)
+{
+ XEvent *event = reinterpret_cast<XEvent *>(message);
+ if (event->xany.type == capture_event_type &&
+ event->xany.window == capture_event_win) {
+ VDEBUG("QClipboard: event_filter(): caught event type %d", event->type);
+ has_captured_event = true;
+ captured_event = *event;
+ return true;
+ }
+ return false;
+}
+
+static Bool checkForClipboardEvents(Display *, XEvent *e, XPointer)
+{
+ return ((e->type == SelectionRequest && (e->xselectionrequest.selection == XA_PRIMARY
+ || e->xselectionrequest.selection == ATOM(CLIPBOARD)))
+ || (e->type == SelectionClear && (e->xselectionclear.selection == XA_PRIMARY
+ || e->xselectionclear.selection == ATOM(CLIPBOARD))));
+}
+
+bool QX11Data::clipboardWaitForEvent(Window win, int type, XEvent *event, int timeout)
+{
+ QElapsedTimer started;
+ started.start();
+ QElapsedTimer now = started;
+
+ if (QAbstractEventDispatcher::instance()->inherits("QtMotif")
+ || QApplication::clipboard()->property("useEventLoopWhenWaiting").toBool()) {
+ if (waiting_for_data) {
+ Q_ASSERT(!"QClipboard: internal error, qt_xclb_wait_for_event recursed");
+ return false;
+ }
+ waiting_for_data = true;
+
+
+ has_captured_event = false;
+ capture_event_win = win;
+ capture_event_type = type;
+
+ QApplication::EventFilter old_event_filter =
+ qApp->setEventFilter(qt_x11_clipboard_event_filter);
+
+ do {
+ if (XCheckTypedWindowEvent(display, win, type, event)) {
+ waiting_for_data = false;
+ qApp->setEventFilter(old_event_filter);
+ return true;
+ }
+
+ XSync(X11->display, false);
+ usleep(50000);
+
+ now.start();
+
+ QEventLoop::ProcessEventsFlags flags(QEventLoop::ExcludeUserInputEvents
+ | QEventLoop::ExcludeSocketNotifiers
+ | QEventLoop::WaitForMoreEvents
+ | QEventLoop::X11ExcludeTimers);
+ QAbstractEventDispatcher *eventDispatcher = QAbstractEventDispatcher::instance();
+ eventDispatcher->processEvents(flags);
+
+ if (has_captured_event) {
+ waiting_for_data = false;
+ *event = captured_event;
+ qApp->setEventFilter(old_event_filter);
+ return true;
+ }
+ } while (started.msecsTo(now) < timeout);
+
+ waiting_for_data = false;
+ qApp->setEventFilter(old_event_filter);
+ } else {
+ do {
+ if (XCheckTypedWindowEvent(X11->display,win,type,event))
+ return true;
+
+ // process other clipboard events, since someone is probably requesting data from us
+ XEvent e;
+ if (XCheckIfEvent(X11->display, &e, checkForClipboardEvents, 0))
+ qApp->x11ProcessEvent(&e);
+
+ now.start();
+
+ XFlush(X11->display);
+
+ // sleep 50 ms, so we don't use up CPU cycles all the time.
+ struct timeval usleep_tv;
+ usleep_tv.tv_sec = 0;
+ usleep_tv.tv_usec = 50000;
+ select(0, 0, 0, 0, &usleep_tv);
+ } while (started.msecsTo(now) < timeout);
+ }
+ return false;
+}
+
+
+static inline int maxSelectionIncr(Display *dpy)
+{ return XMaxRequestSize(dpy) > 65536 ? 65536*4 : XMaxRequestSize(dpy)*4 - 100; }
+
+bool QX11Data::clipboardReadProperty(Window win, Atom property, bool deleteProperty,
+ QByteArray *buffer, int *size, Atom *type, int *format)
+{
+ int maxsize = maxSelectionIncr(display);
+ ulong bytes_left; // bytes_after
+ ulong length; // nitems
+ uchar *data;
+ Atom dummy_type;
+ int dummy_format;
+ int r;
+
+ if (!type) // allow null args
+ type = &dummy_type;
+ if (!format)
+ format = &dummy_format;
+
+ // Don't read anything, just get the size of the property data
+ r = XGetWindowProperty(display, win, property, 0, 0, False,
+ AnyPropertyType, type, format,
+ &length, &bytes_left, &data);
+ if (r != Success || (type && *type == XNone)) {
+ buffer->resize(0);
+ return false;
+ }
+ XFree((char*)data);
+
+ int offset = 0, buffer_offset = 0, format_inc = 1, proplen = bytes_left;
+
+ VDEBUG("QClipboard: read_property(): initial property length: %d", proplen);
+
+ switch (*format) {
+ case 8:
+ default:
+ format_inc = sizeof(char) / 1;
+ break;
+
+ case 16:
+ format_inc = sizeof(short) / 2;
+ proplen *= sizeof(short) / 2;
+ break;
+
+ case 32:
+ format_inc = sizeof(long) / 4;
+ proplen *= sizeof(long) / 4;
+ break;
+ }
+
+ int newSize = proplen;
+ buffer->resize(newSize);
+
+ bool ok = (buffer->size() == newSize);
+ VDEBUG("QClipboard: read_property(): buffer resized to %d", buffer->size());
+
+ if (ok && newSize) {
+ // could allocate buffer
+
+ while (bytes_left) {
+ // more to read...
+
+ r = XGetWindowProperty(display, win, property, offset, maxsize/4,
+ False, AnyPropertyType, type, format,
+ &length, &bytes_left, &data);
+ if (r != Success || (type && *type == XNone))
+ break;
+
+ offset += length / (32 / *format);
+ length *= format_inc * (*format) / 8;
+
+ // Here we check if we get a buffer overflow and tries to
+ // recover -- this shouldn't normally happen, but it doesn't
+ // hurt to be defensive
+ if ((int)(buffer_offset + length) > buffer->size()) {
+ length = buffer->size() - buffer_offset;
+
+ // escape loop
+ bytes_left = 0;
+ }
+
+ memcpy(buffer->data() + buffer_offset, data, length);
+ buffer_offset += length;
+
+ XFree((char*)data);
+ }
+
+ if (*format == 8 && *type == ATOM(COMPOUND_TEXT)) {
+ // convert COMPOUND_TEXT to a multibyte string
+ XTextProperty textprop;
+ textprop.encoding = *type;
+ textprop.format = *format;
+ textprop.nitems = buffer_offset;
+ textprop.value = (unsigned char *) buffer->data();
+
+ char **list_ret = 0;
+ int count;
+ if (XmbTextPropertyToTextList(display, &textprop, &list_ret,
+ &count) == Success && count && list_ret) {
+ offset = buffer_offset = strlen(list_ret[0]);
+ buffer->resize(offset);
+ memcpy(buffer->data(), list_ret[0], offset);
+ }
+ if (list_ret) XFreeStringList(list_ret);
+ }
+ }
+
+ // correct size, not 0-term.
+ if (size)
+ *size = buffer_offset;
+
+ VDEBUG("QClipboard: read_property(): buffer size %d, buffer offset %d, offset %d",
+ buffer->size(), buffer_offset, offset);
+
+ if (deleteProperty)
+ XDeleteProperty(display, win, property);
+
+ XFlush(display);
+
+ return ok;
+}
+
+QByteArray QX11Data::clipboardReadIncrementalProperty(Window win, Atom property, int nbytes, bool nullterm)
+{
+ XEvent event;
+
+ QByteArray buf;
+ QByteArray tmp_buf;
+ bool alloc_error = false;
+ int length;
+ int offset = 0;
+
+ if (nbytes > 0) {
+ // Reserve buffer + zero-terminator (for text data)
+ // We want to complete the INCR transfer even if we cannot
+ // allocate more memory
+ buf.resize(nbytes+1);
+ alloc_error = buf.size() != nbytes+1;
+ }
+
+ for (;;) {
+ XFlush(display);
+ if (!clipboardWaitForEvent(win,PropertyNotify,&event,clipboard_timeout))
+ break;
+ if (event.xproperty.atom != property ||
+ event.xproperty.state != PropertyNewValue)
+ continue;
+ if (X11->clipboardReadProperty(win, property, true, &tmp_buf, &length, 0, 0)) {
+ if (length == 0) { // no more data, we're done
+ if (nullterm) {
+ buf.resize(offset+1);
+ buf[offset] = '\0';
+ } else {
+ buf.resize(offset);
+ }
+ return buf;
+ } else if (!alloc_error) {
+ if (offset+length > (int)buf.size()) {
+ buf.resize(offset+length+65535);
+ if (buf.size() != offset+length+65535) {
+ alloc_error = true;
+ length = buf.size() - offset;
+ }
+ }
+ memcpy(buf.data()+offset, tmp_buf.constData(), length);
+ tmp_buf.resize(0);
+ offset += length;
+ }
+ } else {
+ break;
+ }
+ }
+
+ // timed out ... create a new requestor window, otherwise the requestor
+ // could consider next request to be still part of this timed out request
+ delete requestor;
+ requestor = new QWidget(0);
+ requestor->setObjectName(QLatin1String("internal clipboard requestor"));
+ // We don't need this internal widget to appear in QApplication::topLevelWidgets()
+ if (QWidgetPrivate::allWidgets)
+ QWidgetPrivate::allWidgets->remove(requestor);
+
+ return QByteArray();
+}
+
+static Atom send_targets_selection(QClipboardData *d, Window window, Atom property)
+{
+ QVector<Atom> types;
+ QStringList formats = QInternalMimeData::formatsHelper(d->source());
+ for (int i = 0; i < formats.size(); ++i) {
+ QList<Atom> atoms = X11->xdndMimeAtomsForFormat(formats.at(i));
+ for (int j = 0; j < atoms.size(); ++j) {
+ if (!types.contains(atoms.at(j)))
+ types.append(atoms.at(j));
+ }
+ }
+ types.append(ATOM(TARGETS));
+ types.append(ATOM(MULTIPLE));
+ types.append(ATOM(TIMESTAMP));
+ types.append(ATOM(SAVE_TARGETS));
+
+ XChangeProperty(X11->display, window, property, XA_ATOM, 32,
+ PropModeReplace, (uchar *) types.data(), types.size());
+ return property;
+}
+
+static Atom send_selection(QClipboardData *d, Atom target, Window window, Atom property)
+{
+ Atom atomFormat = target;
+ int dataFormat = 0;
+ QByteArray data;
+
+ QByteArray fmt = X11->xdndAtomToString(target);
+ if (fmt.isEmpty()) { // Not a MIME type we have
+ DEBUG("QClipboard: send_selection(): converting to type '%s' is not supported", fmt.data());
+ return XNone;
+ }
+ DEBUG("QClipboard: send_selection(): converting to type '%s'", fmt.data());
+
+ if (X11->xdndMimeDataForAtom(target, d->source(), &data, &atomFormat, &dataFormat)) {
+
+ VDEBUG("QClipboard: send_selection():\n"
+ " property type %lx\n"
+ " property name '%s'\n"
+ " format %d\n"
+ " %d bytes\n",
+ target, X11->xdndMimeAtomToString(atomFormat).toLatin1().data(), dataFormat, data.size());
+
+ // don't allow INCR transfers when using MULTIPLE or to
+ // Motif clients (since Motif doesn't support INCR)
+ static Atom motif_clip_temporary = ATOM(CLIP_TEMPORARY);
+ bool allow_incr = property != motif_clip_temporary;
+
+ // X_ChangeProperty protocol request is 24 bytes
+ const int increment = (XMaxRequestSize(X11->display) * 4) - 24;
+ if (data.size() > increment && allow_incr) {
+ long bytes = data.size();
+ XChangeProperty(X11->display, window, property,
+ ATOM(INCR), 32, PropModeReplace, (uchar *) &bytes, 1);
+
+ (void)new QClipboardINCRTransaction(window, property, atomFormat, dataFormat, data, increment);
+ return property;
+ }
+
+ // make sure we can perform the XChangeProperty in a single request
+ if (data.size() > increment)
+ return XNone; // ### perhaps use several XChangeProperty calls w/ PropModeAppend?
+ int dataSize = data.size() / (dataFormat / 8);
+ // use a single request to transfer data
+ XChangeProperty(X11->display, window, property, atomFormat,
+ dataFormat, PropModeReplace, (uchar *) data.data(),
+ dataSize);
+ }
+ return property;
+}
+
+/*! \internal
+ Internal cleanup for Windows.
+*/
+void QClipboard::ownerDestroyed()
+{ }
+
+
+/*! \internal
+ Internal optimization for Windows.
+*/
+void QClipboard::connectNotify(const char *)
+{ }
+
+
+bool QClipboard::event(QEvent *e)
+{
+ if (e->type() == QEvent::Timer) {
+ QTimerEvent *te = (QTimerEvent *) e;
+
+ if (waiting_for_data) // should never happen
+ return false;
+
+ if (te->timerId() == timer_id) {
+ killTimer(timer_id);
+ timer_id = 0;
+
+ timer_event_clear = true;
+ if (selection_watcher) // clear selection
+ selectionData()->clear();
+ if (clipboard_watcher) // clear clipboard
+ clipboardData()->clear();
+ timer_event_clear = false;
+
+ return true;
+ } else if (te->timerId() == pending_timer_id) {
+ // I hate klipper
+ killTimer(pending_timer_id);
+ pending_timer_id = 0;
+
+ if (pending_clipboard_changed) {
+ pending_clipboard_changed = false;
+ clipboardData()->clear();
+ emitChanged(QClipboard::Clipboard);
+ }
+ if (pending_selection_changed) {
+ pending_selection_changed = false;
+ selectionData()->clear();
+ emitChanged(QClipboard::Selection);
+ }
+
+ return true;
+ } else if (te->timerId() == incr_timer_id) {
+ killTimer(incr_timer_id);
+ incr_timer_id = 0;
+
+ qt_xclb_incr_timeout();
+
+ return true;
+ } else {
+ return QObject::event(e);
+ }
+ } else if (e->type() != QEvent::Clipboard) {
+ return QObject::event(e);
+ }
+
+ XEvent *xevent = (XEvent *)(((QClipboardEvent *)e)->data());
+ Display *dpy = X11->display;
+
+ if (!xevent) {
+ // That means application exits and we need to give clipboard
+ // content to the clipboard manager.
+ // First we check if there is a clipboard manager.
+ if (XGetSelectionOwner(X11->display, ATOM(CLIPBOARD_MANAGER)) == XNone
+ || !owner)
+ return true;
+
+ Window ownerId = owner->internalWinId();
+ Q_ASSERT(ownerId);
+ // we delete the property so the manager saves all TARGETS.
+ XDeleteProperty(X11->display, ownerId, ATOM(_QT_SELECTION));
+ XConvertSelection(X11->display, ATOM(CLIPBOARD_MANAGER), ATOM(SAVE_TARGETS),
+ ATOM(_QT_SELECTION), ownerId, X11->time);
+ XSync(dpy, false);
+
+ XEvent event;
+ // waiting until the clipboard manager fetches the content.
+ if (!X11->clipboardWaitForEvent(ownerId, SelectionNotify, &event, 10000)) {
+ qWarning("QClipboard: Unable to receive an event from the "
+ "clipboard manager in a reasonable time");
+ }
+
+ return true;
+ }
+
+ switch (xevent->type) {
+
+ case SelectionClear:
+ // new selection owner
+ if (xevent->xselectionclear.selection == XA_PRIMARY) {
+ QClipboardData *d = selectionData();
+
+ // ignore the event if it was generated before we gained selection ownership
+ if (d->timestamp != CurrentTime && xevent->xselectionclear.time <= d->timestamp)
+ break;
+
+ DEBUG("QClipboard: new selection owner 0x%lx at time %lx (ours %lx)",
+ XGetSelectionOwner(dpy, XA_PRIMARY),
+ xevent->xselectionclear.time, d->timestamp);
+
+ if (! waiting_for_data) {
+ d->clear();
+ emitChanged(QClipboard::Selection);
+ } else {
+ pending_selection_changed = true;
+ if (! pending_timer_id)
+ pending_timer_id = QApplication::clipboard()->startTimer(0);
+ }
+ } else if (xevent->xselectionclear.selection == ATOM(CLIPBOARD)) {
+ QClipboardData *d = clipboardData();
+
+ // ignore the event if it was generated before we gained selection ownership
+ if (d->timestamp != CurrentTime && xevent->xselectionclear.time <= d->timestamp)
+ break;
+
+ DEBUG("QClipboard: new clipboard owner 0x%lx at time %lx (%lx)",
+ XGetSelectionOwner(dpy, ATOM(CLIPBOARD)),
+ xevent->xselectionclear.time, d->timestamp);
+
+ if (! waiting_for_data) {
+ d->clear();
+ emitChanged(QClipboard::Clipboard);
+ } else {
+ pending_clipboard_changed = true;
+ if (! pending_timer_id)
+ pending_timer_id = QApplication::clipboard()->startTimer(0);
+ }
+ } else {
+ qWarning("QClipboard: Unknown SelectionClear event received");
+ return false;
+ }
+ break;
+
+ case SelectionNotify:
+ /*
+ Something has delivered data to us, but this was not caught
+ by QClipboardWatcher::getDataInFormat()
+
+ Just skip the event to prevent Bad Things (tm) from
+ happening later on...
+ */
+ break;
+
+ case SelectionRequest:
+ {
+ // someone wants our data
+ XSelectionRequestEvent *req = &xevent->xselectionrequest;
+
+ if (requestor && req->requestor == requestor->internalWinId())
+ break;
+
+ XEvent event;
+ event.xselection.type = SelectionNotify;
+ event.xselection.display = req->display;
+ event.xselection.requestor = req->requestor;
+ event.xselection.selection = req->selection;
+ event.xselection.target = req->target;
+ event.xselection.property = XNone;
+ event.xselection.time = req->time;
+
+ DEBUG("QClipboard: SelectionRequest from %lx\n"
+ " selection 0x%lx (%s) target 0x%lx (%s)",
+ req->requestor,
+ req->selection,
+ X11->xdndAtomToString(req->selection).data(),
+ req->target,
+ X11->xdndAtomToString(req->target).data());
+
+ QClipboardData *d;
+ if (req->selection == XA_PRIMARY) {
+ d = selectionData();
+ } else if (req->selection == ATOM(CLIPBOARD)) {
+ d = clipboardData();
+ } else {
+ qWarning("QClipboard: Unknown selection '%lx'", req->selection);
+ XSendEvent(dpy, req->requestor, False, NoEventMask, &event);
+ break;
+ }
+
+ if (! d->source()) {
+ qWarning("QClipboard: Cannot transfer data, no data available");
+ XSendEvent(dpy, req->requestor, False, NoEventMask, &event);
+ break;
+ }
+
+ DEBUG("QClipboard: SelectionRequest at time %lx (ours %lx)",
+ req->time, d->timestamp);
+
+ if (d->timestamp == CurrentTime // we don't own the selection anymore
+ || (req->time != CurrentTime && req->time < d->timestamp)) {
+ DEBUG("QClipboard: SelectionRequest too old");
+ XSendEvent(dpy, req->requestor, False, NoEventMask, &event);
+ break;
+ }
+
+ Atom xa_targets = ATOM(TARGETS);
+ Atom xa_multiple = ATOM(MULTIPLE);
+ Atom xa_timestamp = ATOM(TIMESTAMP);
+
+ struct AtomPair { Atom target; Atom property; } *multi = 0;
+ Atom multi_type = XNone;
+ int multi_format = 0;
+ int nmulti = 0;
+ int imulti = -1;
+ bool multi_writeback = false;
+
+ if (req->target == xa_multiple) {
+ QByteArray multi_data;
+ if (req->property == XNone
+ || !X11->clipboardReadProperty(req->requestor, req->property, false, &multi_data,
+ 0, &multi_type, &multi_format)
+ || multi_format != 32) {
+ // MULTIPLE property not formatted correctly
+ XSendEvent(dpy, req->requestor, False, NoEventMask, &event);
+ break;
+ }
+ nmulti = multi_data.size()/sizeof(*multi);
+ multi = new AtomPair[nmulti];
+ memcpy(multi,multi_data.data(),multi_data.size());
+ imulti = 0;
+ }
+
+ for (; imulti < nmulti; ++imulti) {
+ Atom target;
+ Atom property;
+
+ if (multi) {
+ target = multi[imulti].target;
+ property = multi[imulti].property;
+ } else {
+ target = req->target;
+ property = req->property;
+ if (property == XNone) // obsolete client
+ property = target;
+ }
+
+ Atom ret = XNone;
+ if (target == XNone || property == XNone) {
+ ;
+ } else if (target == xa_timestamp) {
+ if (d->timestamp != CurrentTime) {
+ XChangeProperty(dpy, req->requestor, property, XA_INTEGER, 32,
+ PropModeReplace, (uchar *) &d->timestamp, 1);
+ ret = property;
+ } else {
+ qWarning("QClipboard: Invalid data timestamp");
+ }
+ } else if (target == xa_targets) {
+ ret = send_targets_selection(d, req->requestor, property);
+ } else {
+ ret = send_selection(d, target, req->requestor, property);
+ }
+
+ if (nmulti > 0) {
+ if (ret == XNone) {
+ multi[imulti].property = XNone;
+ multi_writeback = true;
+ }
+ } else {
+ event.xselection.property = ret;
+ break;
+ }
+ }
+
+ if (nmulti > 0) {
+ if (multi_writeback) {
+ // according to ICCCM 2.6.2 says to put None back
+ // into the original property on the requestor window
+ XChangeProperty(dpy, req->requestor, req->property, multi_type, 32,
+ PropModeReplace, (uchar *) multi, nmulti * 2);
+ }
+
+ delete [] multi;
+ event.xselection.property = req->property;
+ }
+
+ // send selection notify to requestor
+ XSendEvent(dpy, req->requestor, False, NoEventMask, &event);
+
+ DEBUG("QClipboard: SelectionNotify to 0x%lx\n"
+ " property 0x%lx (%s)",
+ req->requestor, event.xselection.property,
+ X11->xdndAtomToString(event.xselection.property).data());
+ }
+ break;
+ }
+
+ return true;
+}
+
+
+
+
+
+
+QClipboardWatcher::QClipboardWatcher(QClipboard::Mode mode)
+ : QInternalMimeData()
+{
+ switch (mode) {
+ case QClipboard::Selection:
+ atom = XA_PRIMARY;
+ break;
+
+ case QClipboard::Clipboard:
+ atom = ATOM(CLIPBOARD);
+ break;
+
+ default:
+ qWarning("QClipboardWatcher: Internal error: Unsupported clipboard mode");
+ break;
+ }
+
+ setupOwner();
+}
+
+QClipboardWatcher::~QClipboardWatcher()
+{
+ if(selection_watcher == this)
+ selection_watcher = 0;
+ if(clipboard_watcher == this)
+ clipboard_watcher = 0;
+}
+
+bool QClipboardWatcher::empty() const
+{
+ Display *dpy = X11->display;
+ Window win = XGetSelectionOwner(dpy, atom);
+
+ if(win == requestor->internalWinId()) {
+ qWarning("QClipboardWatcher::empty: Internal error: Application owns the selection");
+ return true;
+ }
+
+ return win == XNone;
+}
+
+QStringList QClipboardWatcher::formats_sys() const
+{
+ if (empty())
+ return QStringList();
+
+ if (!formatList.count()) {
+ // get the list of targets from the current clipboard owner - we do this
+ // once so that multiple calls to this function don't require multiple
+ // server round trips...
+
+ format_atoms = getDataInFormat(ATOM(TARGETS));
+
+ if (format_atoms.size() > 0) {
+ Atom *targets = (Atom *) format_atoms.data();
+ int size = format_atoms.size() / sizeof(Atom);
+
+ for (int i = 0; i < size; ++i) {
+ if (targets[i] == 0)
+ continue;
+
+ QStringList formatsForAtom = X11->xdndMimeFormatsForAtom(targets[i]);
+ for (int j = 0; j < formatsForAtom.size(); ++j) {
+ if (!formatList.contains(formatsForAtom.at(j)))
+ formatList.append(formatsForAtom.at(j));
+ }
+ VDEBUG(" format: %s", X11->xdndAtomToString(targets[i]).data());
+ VDEBUG(" data:\n%s\n", getDataInFormat(targets[i]).data());
+ }
+ DEBUG("QClipboardWatcher::format: %d formats available", formatList.count());
+ }
+ }
+
+ return formatList;
+}
+
+bool QClipboardWatcher::hasFormat_sys(const QString &format) const
+{
+ QStringList list = formats();
+ return list.contains(format);
+}
+
+QVariant QClipboardWatcher::retrieveData_sys(const QString &fmt, QVariant::Type requestedType) const
+{
+ if (fmt.isEmpty() || empty())
+ return QByteArray();
+
+ (void)formats(); // trigger update of format list
+ DEBUG("QClipboardWatcher::data: fetching format '%s'", fmt.toLatin1().data());
+
+ QList<Atom> atoms;
+ Atom *targets = (Atom *) format_atoms.data();
+ int size = format_atoms.size() / sizeof(Atom);
+ for (int i = 0; i < size; ++i)
+ atoms.append(targets[i]);
+
+ QByteArray encoding;
+ Atom fmtatom = X11->xdndMimeAtomForFormat(fmt, requestedType, atoms, &encoding);
+
+ if (fmtatom == 0)
+ return QVariant();
+
+ return X11->xdndMimeConvertToFormat(fmtatom, getDataInFormat(fmtatom), fmt, requestedType, encoding);
+}
+
+QByteArray QClipboardWatcher::getDataInFormat(Atom fmtatom) const
+{
+ QByteArray buf;
+
+ Display *dpy = X11->display;
+ requestor->createWinId();
+ Window win = requestor->internalWinId();
+ Q_ASSERT(requestor->testAttribute(Qt::WA_WState_Created));
+
+ DEBUG("QClipboardWatcher::getDataInFormat: selection '%s' format '%s'",
+ X11->xdndAtomToString(atom).data(), X11->xdndAtomToString(fmtatom).data());
+
+ XSelectInput(dpy, win, NoEventMask); // don't listen for any events
+
+ XDeleteProperty(dpy, win, ATOM(_QT_SELECTION));
+ XConvertSelection(dpy, atom, fmtatom, ATOM(_QT_SELECTION), win, X11->time);
+ XSync(dpy, false);
+
+ VDEBUG("QClipboardWatcher::getDataInFormat: waiting for SelectionNotify event");
+
+ XEvent xevent;
+ if (!X11->clipboardWaitForEvent(win,SelectionNotify,&xevent,clipboard_timeout) ||
+ xevent.xselection.property == XNone) {
+ DEBUG("QClipboardWatcher::getDataInFormat: format not available");
+ return buf;
+ }
+
+ VDEBUG("QClipboardWatcher::getDataInFormat: fetching data...");
+
+ Atom type;
+ XSelectInput(dpy, win, PropertyChangeMask);
+
+ if (X11->clipboardReadProperty(win, ATOM(_QT_SELECTION), true, &buf, 0, &type, 0)) {
+ if (type == ATOM(INCR)) {
+ int nbytes = buf.size() >= 4 ? *((int*)buf.data()) : 0;
+ buf = X11->clipboardReadIncrementalProperty(win, ATOM(_QT_SELECTION), nbytes, false);
+ }
+ }
+
+ XSelectInput(dpy, win, NoEventMask);
+
+ DEBUG("QClipboardWatcher::getDataInFormat: %d bytes received", buf.size());
+
+ return buf;
+}
+
+
+const QMimeData* QClipboard::mimeData(Mode mode) const
+{
+ QClipboardData *d = 0;
+ switch (mode) {
+ case Selection:
+ d = selectionData();
+ break;
+ case Clipboard:
+ d = clipboardData();
+ break;
+ default:
+ qWarning("QClipboard::mimeData: unsupported mode '%d'", mode);
+ return 0;
+ }
+
+ if (! d->source() && ! timer_event_clear) {
+ if (mode == Selection) {
+ if (! selection_watcher)
+ selection_watcher = new QClipboardWatcher(mode);
+ d->setSource(selection_watcher);
+ } else {
+ if (! clipboard_watcher)
+ clipboard_watcher = new QClipboardWatcher(mode);
+ d->setSource(clipboard_watcher);
+ }
+
+ if (! timer_id) {
+ // start a zero timer - we will clear cached data when the timer
+ // times out, which will be the next time we hit the event loop...
+ // that way, the data is cached long enough for calls within a single
+ // loop/function, but the data doesn't linger around in case the selection
+ // changes
+ QClipboard *that = ((QClipboard *) this);
+ timer_id = that->startTimer(0);
+ }
+ }
+
+ return d->source();
+}
+
+
+void QClipboard::setMimeData(QMimeData* src, Mode mode)
+{
+ Atom atom, sentinel_atom;
+ QClipboardData *d;
+ switch (mode) {
+ case Selection:
+ atom = XA_PRIMARY;
+ sentinel_atom = ATOM(_QT_SELECTION_SENTINEL);
+ d = selectionData();
+ break;
+
+ case Clipboard:
+ atom = ATOM(CLIPBOARD);
+ sentinel_atom = ATOM(_QT_CLIPBOARD_SENTINEL);
+ d = clipboardData();
+ break;
+
+ default:
+ qWarning("QClipboard::setMimeData: unsupported mode '%d'", mode);
+ return;
+ }
+
+ Display *dpy = X11->display;
+ Window newOwner;
+
+ if (! src) { // no data, clear clipboard contents
+ newOwner = XNone;
+ d->clear();
+ } else {
+ setupOwner();
+
+ newOwner = owner->internalWinId();
+
+ d->setSource(src);
+ d->timestamp = X11->time;
+ }
+
+ Window prevOwner = XGetSelectionOwner(dpy, atom);
+ // use X11->time, since d->timestamp == CurrentTime when clearing
+ XSetSelectionOwner(dpy, atom, newOwner, X11->time);
+
+ if (mode == Selection)
+ emitChanged(QClipboard::Selection);
+ else
+ emitChanged(QClipboard::Clipboard);
+
+ if (XGetSelectionOwner(dpy, atom) != newOwner) {
+ qWarning("QClipboard::setData: Cannot set X11 selection owner for %s",
+ X11->xdndAtomToString(atom).data());
+ d->clear();
+ return;
+ }
+
+ // Signal to other Qt processes that the selection has changed
+ Window owners[2];
+ owners[0] = newOwner;
+ owners[1] = prevOwner;
+ XChangeProperty(dpy, QApplication::desktop()->screen(0)->internalWinId(),
+ sentinel_atom, XA_WINDOW, 32, PropModeReplace,
+ (unsigned char*)&owners, 2);
+}
+
+
+/*
+ Called by the main event loop in qapplication_x11.cpp when either
+ the _QT_SELECTION_SENTINEL property has been changed (i.e. when some
+ Qt process has performed QClipboard::setData()) or when Xfixes told
+ us that an other application changed the selection. If it returns
+ true, the QClipBoard dataChanged() signal should be emitted.
+*/
+
+bool qt_check_selection_sentinel()
+{
+ bool doIt = true;
+ if (owner && !X11->use_xfixes) {
+ /*
+ Since the X selection mechanism cannot give any signal when
+ the selection has changed, we emulate it (for Qt processes) here.
+ The notification should be ignored in case of either
+ a) This is the process that did setData (because setData()
+ then has already emitted dataChanged())
+ b) This is the process that owned the selection when dataChanged()
+ was called (because we have then received a SelectionClear event,
+ and have already emitted dataChanged() as a result of that)
+ */
+
+ unsigned char *retval;
+ Atom actualType;
+ int actualFormat;
+ ulong nitems;
+ ulong bytesLeft;
+
+ if (XGetWindowProperty(X11->display,
+ QApplication::desktop()->screen(0)->internalWinId(),
+ ATOM(_QT_SELECTION_SENTINEL), 0, 2, False, XA_WINDOW,
+ &actualType, &actualFormat, &nitems,
+ &bytesLeft, &retval) == Success) {
+ Window *owners = (Window *)retval;
+ if (actualType == XA_WINDOW && actualFormat == 32 && nitems == 2) {
+ Window win = owner->internalWinId();
+ if (owners[0] == win || owners[1] == win)
+ doIt = false;
+ }
+
+ XFree(owners);
+ }
+ }
+
+ if (doIt) {
+ if (waiting_for_data) {
+ pending_selection_changed = true;
+ if (! pending_timer_id)
+ pending_timer_id = QApplication::clipboard()->startTimer(0);
+ doIt = false;
+ } else {
+ selectionData()->clear();
+ }
+ }
+
+ return doIt;
+}
+
+
+bool qt_check_clipboard_sentinel()
+{
+ bool doIt = true;
+ if (owner && !X11->use_xfixes) {
+ unsigned char *retval;
+ Atom actualType;
+ int actualFormat;
+ unsigned long nitems, bytesLeft;
+
+ if (XGetWindowProperty(X11->display,
+ QApplication::desktop()->screen(0)->internalWinId(),
+ ATOM(_QT_CLIPBOARD_SENTINEL), 0, 2, False, XA_WINDOW,
+ &actualType, &actualFormat, &nitems, &bytesLeft,
+ &retval) == Success) {
+ Window *owners = (Window *)retval;
+ if (actualType == XA_WINDOW && actualFormat == 32 && nitems == 2) {
+ Window win = owner->internalWinId();
+ if (owners[0] == win || owners[1] == win)
+ doIt = false;
+ }
+
+ XFree(owners);
+ }
+ }
+
+ if (doIt) {
+ if (waiting_for_data) {
+ pending_clipboard_changed = true;
+ if (! pending_timer_id)
+ pending_timer_id = QApplication::clipboard()->startTimer(0);
+ doIt = false;
+ } else {
+ clipboardData()->clear();
+ }
+ }
+
+ return doIt;
+}
+
+bool qt_xfixes_selection_changed(Window selectionOwner, Time timestamp)
+{
+ QClipboardData *d = selectionData();
+#ifdef QCLIPBOARD_DEBUG
+ DEBUG("qt_xfixes_selection_changed: owner = %u; selectionOwner = %u; internal timestamp = %u; external timestamp = %u",
+ (unsigned int)(owner ? (int)owner->internalWinId() : 0), (unsigned int)selectionOwner,
+ (unsigned int)(d ? d->timestamp : 0), (unsigned int)timestamp);
+#endif
+ if (!owner || (selectionOwner && selectionOwner != owner->internalWinId()) ||
+ (!selectionOwner && (d->timestamp == CurrentTime || d->timestamp < timestamp)))
+ return qt_check_selection_sentinel();
+ return false;
+}
+
+bool qt_xfixes_clipboard_changed(Window clipboardOwner, Time timestamp)
+{
+ QClipboardData *d = clipboardData();
+#ifdef QCLIPBOARD_DEBUG
+ DEBUG("qt_xfixes_clipboard_changed: owner = %u; clipboardOwner = %u; internal timestamp = %u; external timestamp = %u",
+ (unsigned int)(owner ? (int)owner->internalWinId() : 0), (unsigned int)clipboardOwner,
+ (unsigned int)(d ? d->timestamp : 0), (unsigned int)timestamp);
+#endif
+ if (!owner || (clipboardOwner && clipboardOwner != owner->internalWinId()) ||
+ (!clipboardOwner && (d->timestamp == CurrentTime || d->timestamp < timestamp)))
+ return qt_check_clipboard_sentinel();
+ return false;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_CLIPBOARD
diff --git a/src/widgets/platforms/x11/qcolormap_x11.cpp b/src/widgets/platforms/x11/qcolormap_x11.cpp
new file mode 100644
index 0000000000..1d6d7b8adb
--- /dev/null
+++ b/src/widgets/platforms/x11/qcolormap_x11.cpp
@@ -0,0 +1,670 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcolormap.h"
+
+#include "qapplication.h"
+#include "qdebug.h"
+#include "qdesktopwidget.h"
+#include "qvarlengtharray.h"
+
+#include "qx11info_x11.h"
+#include <private/qt_x11_p.h>
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+class QColormapPrivate
+{
+public:
+ QColormapPrivate()
+ : ref(1), mode(QColormap::Direct), depth(0),
+ colormap(0), defaultColormap(true),
+ visual(0), defaultVisual(true),
+ r_max(0), g_max(0), b_max(0),
+ r_shift(0), g_shift(0), b_shift(0)
+ {}
+
+ QAtomicInt ref;
+
+ QColormap::Mode mode;
+ int depth;
+
+ Colormap colormap;
+ bool defaultColormap;
+
+ Visual *visual;
+ bool defaultVisual;
+
+ int r_max;
+ int g_max;
+ int b_max;
+
+ uint r_shift;
+ uint g_shift;
+ uint b_shift;
+
+ QVector<QColor> colors;
+ QVector<int> pixels;
+};
+
+
+static uint right_align(uint v)
+{
+ while (!(v & 0x1))
+ v >>= 1;
+ return v;
+}
+
+static int lowest_bit(uint v)
+{
+ int i;
+ uint b = 1u;
+ for (i = 0; ((v & b) == 0u) && i < 32; ++i)
+ b <<= 1u;
+ return i == 32 ? -1 : i;
+}
+
+static int cube_root(int v)
+{
+ if (v == 1)
+ return 1;
+ // brute force algorithm
+ int i = 1;
+ for (;;) {
+ const int b = i * i * i;
+ if (b <= v) {
+ ++i;
+ } else {
+ --i;
+ break;
+ }
+ }
+ return i;
+}
+
+static Visual *find_visual(Display *display,
+ int screen,
+ int visual_class,
+ int visual_id,
+ int *depth,
+ bool *defaultVisual)
+{
+ XVisualInfo *vi, rvi;
+ int count;
+
+ uint mask = VisualScreenMask;
+ rvi.screen = screen;
+
+ if (visual_class != -1) {
+ rvi.c_class = visual_class;
+ mask |= VisualClassMask;
+ }
+ if (visual_id != -1) {
+ rvi.visualid = visual_id;
+ mask |= VisualIDMask;
+ }
+
+ Visual *visual = DefaultVisual(display, screen);
+ *defaultVisual = true;
+ *depth = DefaultDepth(display, screen);
+
+ vi = XGetVisualInfo(display, mask, &rvi, &count);
+ if (vi) {
+ int best = 0;
+ for (int x = 0; x < count; ++x) {
+ if (vi[x].depth > vi[best].depth)
+ best = x;
+ }
+ if (best >= 0 && best <= count && vi[best].visualid != XVisualIDFromVisual(visual)) {
+ visual = vi[best].visual;
+ *defaultVisual = (visual == DefaultVisual(display, screen));
+ *depth = vi[best].depth;
+ }
+ }
+ if (vi)
+ XFree((char *)vi);
+ return visual;
+}
+
+static void query_colormap(QColormapPrivate *d, int screen)
+{
+ Display *display = QX11Info::display();
+
+ // query existing colormap
+ int q_colors = (((1u << d->depth) > 256u) ? 256u : (1u << d->depth));
+ XColor queried[256];
+ memset(queried, 0, sizeof(queried));
+ for (int x = 0; x < q_colors; ++x)
+ queried[x].pixel = x;
+ XQueryColors(display, d->colormap, queried, q_colors);
+
+ d->colors.resize(q_colors);
+ for (int x = 0; x < q_colors; ++x) {
+ if (queried[x].red == 0
+ && queried[x].green == 0
+ && queried[x].blue == 0
+ && queried[x].pixel != BlackPixel(display, screen)) {
+ // unallocated color cell, skip it
+ continue;
+ }
+
+ d->colors[x] = QColor::fromRgbF(queried[x].red / float(USHRT_MAX),
+ queried[x].green / float(USHRT_MAX),
+ queried[x].blue / float(USHRT_MAX));
+ }
+
+ // for missing colors, find the closest color in the existing colormap
+ Q_ASSERT(d->pixels.size());
+ for (int x = 0; x < d->pixels.size(); ++x) {
+ if (d->pixels.at(x) != -1)
+ continue;
+
+ QRgb rgb;
+ if (d->mode == QColormap::Indexed) {
+ const int r = (x / (d->g_max * d->b_max)) % d->r_max;
+ const int g = (x / d->b_max) % d->g_max;
+ const int b = x % d->b_max;
+ rgb = qRgb((r * 0xff + (d->r_max - 1) / 2) / (d->r_max - 1),
+ (g * 0xff + (d->g_max - 1) / 2) / (d->g_max - 1),
+ (b * 0xff + (d->b_max - 1) / 2) / (d->b_max - 1));
+ } else {
+ rgb = qRgb(x, x, x);
+ }
+
+ // find closest color
+ int mindist = INT_MAX, best = -1;
+ for (int y = 0; y < q_colors; ++y) {
+ int r = qRed(rgb) - (queried[y].red >> 8);
+ int g = qGreen(rgb) - (queried[y].green >> 8);
+ int b = qBlue(rgb) - (queried[y].blue >> 8);
+ int dist = (r * r) + (g * g) + (b * b);
+ if (dist < mindist) {
+ mindist = dist;
+ best = y;
+ }
+ }
+
+ Q_ASSERT(best >= 0 && best < q_colors);
+ if (d->visual->c_class & 1) {
+ XColor xcolor;
+ xcolor.red = queried[best].red;
+ xcolor.green = queried[best].green;
+ xcolor.blue = queried[best].blue;
+ xcolor.pixel = queried[best].pixel;
+
+ if (XAllocColor(display, d->colormap, &xcolor)) {
+ d->pixels[x] = xcolor.pixel;
+ } else {
+ // some weird stuff is going on...
+ d->pixels[x] = (qGray(rgb) < 127
+ ? BlackPixel(display, screen)
+ : WhitePixel(display, screen));
+ }
+ } else {
+ d->pixels[x] = best;
+ }
+ }
+}
+
+static void init_gray(QColormapPrivate *d, int screen)
+{
+ d->pixels.resize(d->r_max);
+
+ for (int g = 0; g < d->g_max; ++g) {
+ const int gray = (g * 0xff + (d->r_max - 1) / 2) / (d->r_max - 1);
+ const QRgb rgb = qRgb(gray, gray, gray);
+
+ d->pixels[g] = -1;
+
+ if (d->visual->c_class & 1) {
+ XColor xcolor;
+ xcolor.red = qRed(rgb) * 0x101;
+ xcolor.green = qGreen(rgb) * 0x101;
+ xcolor.blue = qBlue(rgb) * 0x101;
+ xcolor.pixel = 0ul;
+
+ if (XAllocColor(QX11Info::display(), d->colormap, &xcolor))
+ d->pixels[g] = xcolor.pixel;
+ }
+ }
+
+ query_colormap(d, screen);
+}
+
+static void init_indexed(QColormapPrivate *d, int screen)
+{
+ d->pixels.resize(d->r_max * d->g_max * d->b_max);
+
+ // create color cube
+ for (int x = 0, r = 0; r < d->r_max; ++r) {
+ for (int g = 0; g < d->g_max; ++g) {
+ for (int b = 0; b < d->b_max; ++b, ++x) {
+ const QRgb rgb = qRgb((r * 0xff + (d->r_max - 1) / 2) / (d->r_max - 1),
+ (g * 0xff + (d->g_max - 1) / 2) / (d->g_max - 1),
+ (b * 0xff + (d->b_max - 1) / 2) / (d->b_max - 1));
+
+ d->pixels[x] = -1;
+
+ if (d->visual->c_class & 1) {
+ XColor xcolor;
+ xcolor.red = qRed(rgb) * 0x101;
+ xcolor.green = qGreen(rgb) * 0x101;
+ xcolor.blue = qBlue(rgb) * 0x101;
+ xcolor.pixel = 0ul;
+
+ if (XAllocColor(QX11Info::display(), d->colormap, &xcolor))
+ d->pixels[x] = xcolor.pixel;
+ }
+ }
+ }
+ }
+
+ query_colormap(d, screen);
+}
+
+static void init_direct(QColormapPrivate *d, bool ownColormap)
+{
+ if (d->visual->c_class != DirectColor || !ownColormap)
+ return;
+
+ // preallocate 768 on the stack, so that we don't have to malloc
+ // for the common case (<= 24 bpp)
+ QVarLengthArray<XColor, 768> colorTable(d->r_max + d->g_max + d->b_max);
+ int i = 0;
+
+ for (int r = 0; r < d->r_max; ++r) {
+ colorTable[i].red = r << 8 | r;
+ colorTable[i].pixel = r << d->r_shift;
+ colorTable[i].flags = DoRed;
+ ++i;
+ }
+
+ for (int g = 0; g < d->g_max; ++g) {
+ colorTable[i].green = g << 8 | g;
+ colorTable[i].pixel = g << d->g_shift;
+ colorTable[i].flags = DoGreen;
+ ++i;
+ }
+
+ for (int b = 0; b < d->b_max; ++b) {
+ colorTable[i].blue = (b << 8 | b);
+ colorTable[i].pixel = b << d->b_shift;
+ colorTable[i].flags = DoBlue;
+ ++i;
+ }
+
+ XStoreColors(X11->display, d->colormap, colorTable.data(), colorTable.count());
+}
+
+static QColormap **cmaps = 0;
+
+void QColormap::initialize()
+{
+ Display *display = QX11Info::display();
+ const int screens = ScreenCount(display);
+
+ cmaps = new QColormap*[screens];
+
+ for (int i = 0; i < screens; ++i) {
+ cmaps[i] = new QColormap;
+ QColormapPrivate * const d = cmaps[i]->d;
+
+ bool use_stdcmap = false;
+ int color_count = X11->color_count;
+
+ // defaults
+ d->depth = DefaultDepth(display, i);
+ d->colormap = DefaultColormap(display, i);
+ d->defaultColormap = true;
+ d->visual = DefaultVisual(display, i);
+ d->defaultVisual = true;
+
+ Visual *argbVisual = 0;
+
+ if (X11->visual && i == DefaultScreen(display)) {
+ // only use the outside colormap on the default screen
+ d->visual = find_visual(display, i, X11->visual->c_class,
+ XVisualIDFromVisual(X11->visual),
+ &d->depth, &d->defaultVisual);
+ } else if ((X11->visual_class != -1 && X11->visual_class >= 0 && X11->visual_class < 6)
+ || (X11->visual_id != -1)) {
+ // look for a specific visual or type of visual
+ d->visual = find_visual(display, i, X11->visual_class, X11->visual_id,
+ &d->depth, &d->defaultVisual);
+ } else if (QApplication::colorSpec() == QApplication::ManyColor) {
+ // look for a TrueColor w/ a depth higher than 8bpp
+ d->visual = find_visual(display, i, TrueColor, -1, &d->depth, &d->defaultVisual);
+ if (d->depth <= 8) {
+ d->visual = DefaultVisual(display, i);
+ d->defaultVisual = true;
+ color_count = 216;
+ }
+ } else if (!X11->custom_cmap) {
+ XStandardColormap *stdcmap = 0;
+ int ncmaps = 0;
+
+#ifndef QT_NO_XRENDER
+ if (X11->use_xrender) {
+ int nvi;
+ XVisualInfo templ;
+ templ.screen = i;
+ templ.depth = 32;
+ templ.c_class = TrueColor;
+ XVisualInfo *xvi = XGetVisualInfo(X11->display, VisualScreenMask |
+ VisualDepthMask |
+ VisualClassMask, &templ, &nvi);
+ for (int idx = 0; idx < nvi; ++idx) {
+ XRenderPictFormat *format = XRenderFindVisualFormat(X11->display,
+ xvi[idx].visual);
+ if (format->type == PictTypeDirect && format->direct.alphaMask) {
+ argbVisual = xvi[idx].visual;
+ break;
+ }
+ }
+ XFree(xvi);
+ }
+#endif
+ if (XGetRGBColormaps(display, RootWindow(display, i),
+ &stdcmap, &ncmaps, XA_RGB_DEFAULT_MAP)) {
+ if (stdcmap) {
+ for (int c = 0; c < ncmaps; ++c) {
+ if (!stdcmap[c].red_max ||
+ !stdcmap[c].green_max ||
+ !stdcmap[c].blue_max ||
+ !stdcmap[c].red_mult ||
+ !stdcmap[c].green_mult ||
+ !stdcmap[c].blue_mult)
+ continue; // invalid stdcmap
+
+ XVisualInfo proto;
+ proto.visualid = stdcmap[c].visualid;
+ proto.screen = i;
+
+ int nvisuals = 0;
+ XVisualInfo *vi = XGetVisualInfo(display, VisualIDMask | VisualScreenMask,
+ &proto, &nvisuals);
+ if (vi) {
+ if (nvisuals > 0) {
+ use_stdcmap = true;
+
+ d->mode = ((vi[0].visual->c_class < StaticColor)
+ ? Gray
+ : ((vi[0].visual->c_class < TrueColor)
+ ? Indexed
+ : Direct));
+
+ d->depth = vi[0].depth;
+ d->colormap = stdcmap[c].colormap;
+ d->defaultColormap = true;
+ d->visual = vi[0].visual;
+ d->defaultVisual = (d->visual == DefaultVisual(display, i));
+
+ d->r_max = stdcmap[c].red_max + 1;
+ d->g_max = stdcmap[c].green_max + 1;
+ d->b_max = stdcmap[c].blue_max + 1;
+
+ if (d->mode == Direct) {
+ // calculate offsets
+ d->r_shift = lowest_bit(d->visual->red_mask);
+ d->g_shift = lowest_bit(d->visual->green_mask);
+ d->b_shift = lowest_bit(d->visual->blue_mask);
+ } else {
+ d->r_shift = 0;
+ d->g_shift = 0;
+ d->b_shift = 0;
+ }
+ }
+ XFree(vi);
+ }
+ break;
+ }
+ XFree(stdcmap);
+ }
+ }
+ }
+ if (!use_stdcmap) {
+ switch (d->visual->c_class) {
+ case StaticGray:
+ d->mode = Gray;
+
+ d->r_max = d->g_max = d->b_max = d->visual->map_entries;
+ break;
+
+ case XGrayScale:
+ d->mode = Gray;
+
+ // follow precedent set in libXmu...
+ if (color_count != 0)
+ d->r_max = d->g_max = d->b_max = color_count;
+ else if (d->visual->map_entries > 65000)
+ d->r_max = d->g_max = d->b_max = 4096;
+ else if (d->visual->map_entries > 4000)
+ d->r_max = d->g_max = d->b_max = 512;
+ else if (d->visual->map_entries > 250)
+ d->r_max = d->g_max = d->b_max = 12;
+ else
+ d->r_max = d->g_max = d->b_max = 4;
+ break;
+
+ case StaticColor:
+ d->mode = Indexed;
+
+ d->r_max = right_align(d->visual->red_mask) + 1;
+ d->g_max = right_align(d->visual->green_mask) + 1;
+ d->b_max = right_align(d->visual->blue_mask) + 1;
+ break;
+
+ case PseudoColor:
+ d->mode = Indexed;
+
+ // follow precedent set in libXmu...
+ if (color_count != 0)
+ d->r_max = d->g_max = d->b_max = cube_root(color_count);
+ else if (d->visual->map_entries > 65000)
+ d->r_max = d->g_max = d->b_max = 27;
+ else if (d->visual->map_entries > 4000)
+ d->r_max = d->g_max = d->b_max = 12;
+ else if (d->visual->map_entries > 250)
+ d->r_max = d->g_max = d->b_max = cube_root(d->visual->map_entries - 125);
+ else
+ d->r_max = d->g_max = d->b_max = cube_root(d->visual->map_entries);
+ break;
+
+ case TrueColor:
+ case DirectColor:
+ d->mode = Direct;
+
+ d->r_max = right_align(d->visual->red_mask) + 1;
+ d->g_max = right_align(d->visual->green_mask) + 1;
+ d->b_max = right_align(d->visual->blue_mask) + 1;
+
+ d->r_shift = lowest_bit(d->visual->red_mask);
+ d->g_shift = lowest_bit(d->visual->green_mask);
+ d->b_shift = lowest_bit(d->visual->blue_mask);
+ break;
+ }
+ }
+
+ bool ownColormap = false;
+ if (X11->colormap && i == DefaultScreen(display)) {
+ // only use the outside colormap on the default screen
+ d->colormap = X11->colormap;
+ d->defaultColormap = (d->colormap == DefaultColormap(display, i));
+ } else if ((!use_stdcmap
+ && (((d->visual->c_class & 1) && X11->custom_cmap)
+ || d->visual != DefaultVisual(display, i)))
+ || d->visual->c_class == DirectColor) {
+ // allocate custom colormap (we always do this when using DirectColor visuals)
+ d->colormap =
+ XCreateColormap(display, RootWindow(display, i), d->visual,
+ d->visual->c_class == DirectColor ? AllocAll : AllocNone);
+ d->defaultColormap = false;
+ ownColormap = true;
+ }
+
+ switch (d->mode) {
+ case Gray:
+ init_gray(d, i);
+ break;
+ case Indexed:
+ init_indexed(d, i);
+ break;
+ case Direct:
+ init_direct(d, ownColormap);
+ break;
+ }
+
+ QX11InfoData *screen = X11->screens + i;
+ screen->depth = d->depth;
+ screen->visual = d->visual;
+ screen->defaultVisual = d->defaultVisual;
+ screen->colormap = d->colormap;
+ screen->defaultColormap = d->defaultColormap;
+ screen->cells = screen->visual->map_entries;
+
+ if (argbVisual) {
+ X11->argbVisuals[i] = argbVisual;
+ X11->argbColormaps[i] = XCreateColormap(display, RootWindow(display, i), argbVisual, AllocNone);
+ }
+
+ // ###
+ // We assume that 8bpp == pseudocolor, but this is not
+ // always the case (according to the X server), so we need
+ // to make sure that our internal data is setup in a way
+ // that is compatible with our assumptions
+ if (screen->visual->c_class == TrueColor && screen->depth == 8 && screen->cells == 8)
+ screen->cells = 256;
+ }
+}
+
+void QColormap::cleanup()
+{
+ Display *display = QX11Info::display();
+ const int screens = ScreenCount(display);
+
+ for (int i = 0; i < screens; ++i)
+ delete cmaps[i];
+
+ delete [] cmaps;
+ cmaps = 0;
+}
+
+
+QColormap QColormap::instance(int screen)
+{
+ if (screen == -1)
+ screen = QX11Info::appScreen();
+ return *cmaps[screen];
+}
+
+/*! \internal
+ Constructs a new colormap.
+*/
+QColormap::QColormap()
+ : d(new QColormapPrivate)
+{}
+
+QColormap::QColormap(const QColormap &colormap)
+ :d (colormap.d)
+{ d->ref.ref(); }
+
+QColormap::~QColormap()
+{
+ if (!d->ref.deref()) {
+ if (!d->defaultColormap)
+ XFreeColormap(QX11Info::display(), d->colormap);
+ delete d;
+ }
+}
+
+QColormap::Mode QColormap::mode() const
+{ return d->mode; }
+
+int QColormap::depth() const
+{ return d->depth; }
+
+int QColormap::size() const
+{
+ return (d->mode == Gray
+ ? d->r_max
+ : (d->mode == Indexed
+ ? d->r_max * d->g_max * d->b_max
+ : -1));
+}
+
+uint QColormap::pixel(const QColor &color) const
+{
+ const QColor c = color.toRgb();
+ const uint r = (c.ct.argb.red * d->r_max) >> 16;
+ const uint g = (c.ct.argb.green * d->g_max) >> 16;
+ const uint b = (c.ct.argb.blue * d->b_max) >> 16;
+ if (d->mode != Direct) {
+ if (d->mode == Gray)
+ return d->pixels.at((r * 30 + g * 59 + b * 11) / 100);
+ return d->pixels.at(r * d->g_max * d->b_max + g * d->b_max + b);
+ }
+ return (r << d->r_shift) + (g << d->g_shift) + (b << d->b_shift);
+}
+
+const QColor QColormap::colorAt(uint pixel) const
+{
+ if (d->mode != Direct) {
+ Q_ASSERT(pixel <= (uint)d->colors.size());
+ return d->colors.at(pixel);
+ }
+
+ const int r = (((pixel & d->visual->red_mask) >> d->r_shift) << 8) / d->r_max;
+ const int g = (((pixel & d->visual->green_mask) >> d->g_shift) << 8) / d->g_max;
+ const int b = (((pixel & d->visual->blue_mask) >> d->b_shift) << 8) / d->b_max;
+ return QColor(r, g, b);
+}
+
+const QVector<QColor> QColormap::colormap() const
+{ return d->colors; }
+
+QColormap &QColormap::operator=(const QColormap &colormap)
+{
+ qAtomicAssign(d, colormap.d);
+ return *this;
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/x11/qcursor_x11.cpp b/src/widgets/platforms/x11/qcursor_x11.cpp
new file mode 100644
index 0000000000..1132d3737d
--- /dev/null
+++ b/src/widgets/platforms/x11/qcursor_x11.cpp
@@ -0,0 +1,640 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qdebug.h>
+#include <qdatastream.h>
+#include <private/qcursor_p.h>
+#include <private/qt_x11_p.h>
+#include <private/qapplication_p.h>
+#include <qbitmap.h>
+#include <qcursor.h>
+#include <X11/cursorfont.h>
+
+#include <qlibrary.h>
+
+#ifndef QT_NO_XCURSOR
+# include <X11/Xcursor/Xcursor.h>
+#endif // QT_NO_XCURSOR
+
+#ifndef QT_NO_XFIXES
+#ifndef Status
+#define Status int
+#endif
+# include <X11/extensions/Xfixes.h>
+#endif // QT_NO_XFIXES
+
+#include "qx11info_x11.h"
+#include <private/qpixmap_x11_p.h>
+
+QT_BEGIN_NAMESPACE
+
+// Define QT_USE_APPROXIMATE_CURSORS when compiling if you REALLY want to
+// use the ugly X11 cursors.
+
+/*****************************************************************************
+ Internal QCursorData class
+ *****************************************************************************/
+
+QCursorData::QCursorData(Qt::CursorShape s)
+ : cshape(s), bm(0), bmm(0), hx(0), hy(0), hcurs(0), pm(0), pmm(0)
+{
+ ref = 1;
+}
+
+QCursorData::~QCursorData()
+{
+ Display *dpy = X11 ? X11->display : (Display*)0;
+
+ // Add in checking for the display too as on HP-UX
+ // we seem to get a core dump as the cursor data is
+ // deleted again from main() on exit...
+ if (hcurs && dpy)
+ XFreeCursor(dpy, hcurs);
+ if (pm && dpy)
+ XFreePixmap(dpy, pm);
+ if (pmm && dpy)
+ XFreePixmap(dpy, pmm);
+ delete bm;
+ delete bmm;
+}
+
+#ifndef QT_NO_CURSOR
+QCursor::QCursor(Qt::HANDLE cursor)
+{
+ if (!QCursorData::initialized)
+ QCursorData::initialize();
+ d = new QCursorData(Qt::CustomCursor);
+ d->hcurs = cursor;
+}
+
+#endif
+
+QCursorData *QCursorData::setBitmap(const QBitmap &bitmap, const QBitmap &mask, int hotX, int hotY)
+{
+ if (!QCursorData::initialized)
+ QCursorData::initialize();
+ if (bitmap.depth() != 1 || mask.depth() != 1 || bitmap.size() != mask.size()) {
+ qWarning("QCursor: Cannot create bitmap cursor; invalid bitmap(s)");
+ QCursorData *c = qt_cursorTable[0];
+ c->ref.ref();
+ return c;
+ }
+ QCursorData *d = new QCursorData;
+ d->ref = 1;
+
+ extern QPixmap qt_toX11Pixmap(const QPixmap &pixmap); // qpixmap_x11.cpp
+ d->bm = new QBitmap(qt_toX11Pixmap(bitmap));
+ d->bmm = new QBitmap(qt_toX11Pixmap(mask));
+
+ d->hcurs = 0;
+ d->cshape = Qt::BitmapCursor;
+ d->hx = hotX >= 0 ? hotX : bitmap.width() / 2;
+ d->hy = hotY >= 0 ? hotY : bitmap.height() / 2;
+ d->fg.red = 0x0000;
+ d->fg.green = 0x0000;
+ d->fg.blue = 0x0000;
+ d->bg.red = 0xffff;
+ d->bg.green = 0xffff;
+ d->bg.blue = 0xffff;
+ return d;
+}
+
+
+
+#ifndef QT_NO_CURSOR
+Qt::HANDLE QCursor::handle() const
+{
+ if (!QCursorData::initialized)
+ QCursorData::initialize();
+ if (!d->hcurs)
+ d->update();
+ return d->hcurs;
+}
+#endif
+
+QPoint QCursor::pos()
+{
+ Window root;
+ Window child;
+ int root_x, root_y, win_x, win_y;
+ uint buttons;
+ Display* dpy = X11->display;
+ for (int i = 0; i < ScreenCount(dpy); ++i) {
+ if (XQueryPointer(dpy, QX11Info::appRootWindow(i), &root, &child, &root_x, &root_y,
+ &win_x, &win_y, &buttons))
+
+ return QPoint(root_x, root_y);
+ }
+ return QPoint();
+}
+
+/*! \internal
+*/
+#ifndef QT_NO_CURSOR
+int QCursor::x11Screen()
+{
+ Window root;
+ Window child;
+ int root_x, root_y, win_x, win_y;
+ uint buttons;
+ Display* dpy = X11->display;
+ for (int i = 0; i < ScreenCount(dpy); ++i) {
+ if (XQueryPointer(dpy, QX11Info::appRootWindow(i), &root, &child, &root_x, &root_y,
+ &win_x, &win_y, &buttons))
+ return i;
+ }
+ return -1;
+}
+#endif
+
+void QCursor::setPos(int x, int y)
+{
+ QPoint current, target(x, y);
+
+ // this is copied from pos(), since we need the screen number for the correct
+ // root window in the XWarpPointer call
+ Window root;
+ Window child;
+ int root_x, root_y, win_x, win_y;
+ uint buttons;
+ Display* dpy = X11->display;
+ int screen;
+ for (screen = 0; screen < ScreenCount(dpy); ++screen) {
+ if (XQueryPointer(dpy, QX11Info::appRootWindow(screen), &root, &child, &root_x, &root_y,
+ &win_x, &win_y, &buttons)) {
+ current = QPoint(root_x, root_y);
+ break;
+ }
+ }
+
+ if (screen >= ScreenCount(dpy))
+ return;
+
+ // Need to check, since some X servers generate null mouse move
+ // events, causing looping in applications which call setPos() on
+ // every mouse move event.
+ //
+ if (current == target)
+ return;
+
+ XWarpPointer(X11->display, XNone, QX11Info::appRootWindow(screen), 0, 0, 0, 0, x, y);
+}
+
+
+/*!
+ \internal
+
+ Creates the cursor.
+*/
+
+void QCursorData::update()
+{
+ if (!QCursorData::initialized)
+ QCursorData::initialize();
+ if (hcurs)
+ return;
+
+ Display *dpy = X11->display;
+ Window rootwin = QX11Info::appRootWindow();
+
+ if (cshape == Qt::BitmapCursor) {
+ extern QPixmap qt_toX11Pixmap(const QPixmap &pixmap); // qpixmap_x11.cpp
+#ifndef QT_NO_XRENDER
+ if (!pixmap.isNull() && X11->use_xrender) {
+ pixmap = qt_toX11Pixmap(pixmap);
+ hcurs = XRenderCreateCursor (X11->display, pixmap.x11PictureHandle(), hx, hy);
+ } else
+#endif
+ {
+ hcurs = XCreatePixmapCursor(dpy, bm->handle(), bmm->handle(), &fg, &bg, hx, hy);
+ }
+ return;
+ }
+
+ static const char *cursorNames[] = {
+ "left_ptr",
+ "up_arrow",
+ "cross",
+ "wait",
+ "ibeam",
+ "size_ver",
+ "size_hor",
+ "size_bdiag",
+ "size_fdiag",
+ "size_all",
+ "blank",
+ "split_v",
+ "split_h",
+ "pointing_hand",
+ "forbidden",
+ "whats_this",
+ "left_ptr_watch",
+ "openhand",
+ "closedhand",
+ "copy",
+ "move",
+ "link"
+ };
+
+#ifndef QT_NO_XCURSOR
+ if (X11->ptrXcursorLibraryLoadCursor) {
+ // special case for non-standard dnd-* cursors
+ switch (cshape) {
+ case Qt::DragCopyCursor:
+ hcurs = X11->ptrXcursorLibraryLoadCursor(dpy, "dnd-copy");
+ break;
+ case Qt::DragMoveCursor:
+ hcurs = X11->ptrXcursorLibraryLoadCursor(dpy, "dnd-move");
+ break;
+ case Qt::DragLinkCursor:
+ hcurs = X11->ptrXcursorLibraryLoadCursor(dpy, "dnd-link");
+ break;
+ default:
+ break;
+ }
+ if (!hcurs)
+ hcurs = X11->ptrXcursorLibraryLoadCursor(dpy, cursorNames[cshape]);
+ }
+ if (hcurs)
+ return;
+#endif // QT_NO_XCURSOR
+
+ static const uchar cur_blank_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ // Non-standard X11 cursors are created from bitmaps
+
+#ifndef QT_USE_APPROXIMATE_CURSORS
+ static const uchar cur_ver_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xc0, 0x03, 0xe0, 0x07, 0xf0, 0x0f,
+ 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0xf0, 0x0f,
+ 0xe0, 0x07, 0xc0, 0x03, 0x80, 0x01, 0x00, 0x00 };
+ static const uchar mcur_ver_bits[] = {
+ 0x00, 0x00, 0x80, 0x03, 0xc0, 0x07, 0xe0, 0x0f, 0xf0, 0x1f, 0xf8, 0x3f,
+ 0xfc, 0x7f, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xfc, 0x7f, 0xf8, 0x3f,
+ 0xf0, 0x1f, 0xe0, 0x0f, 0xc0, 0x07, 0x80, 0x03 };
+ static const uchar cur_hor_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x08, 0x30, 0x18,
+ 0x38, 0x38, 0xfc, 0x7f, 0xfc, 0x7f, 0x38, 0x38, 0x30, 0x18, 0x20, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ static const uchar mcur_hor_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x40, 0x04, 0x60, 0x0c, 0x70, 0x1c, 0x78, 0x3c,
+ 0xfc, 0x7f, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfc, 0x7f, 0x78, 0x3c,
+ 0x70, 0x1c, 0x60, 0x0c, 0x40, 0x04, 0x00, 0x00 };
+ static const uchar cur_bdiag_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x3e,
+ 0x00, 0x37, 0x88, 0x23, 0xd8, 0x01, 0xf8, 0x00, 0x78, 0x00, 0xf8, 0x00,
+ 0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ static const uchar mcur_bdiag_bits[] = {
+ 0x00, 0x00, 0xc0, 0x7f, 0x80, 0x7f, 0x00, 0x7f, 0x00, 0x7e, 0x04, 0x7f,
+ 0x8c, 0x7f, 0xdc, 0x77, 0xfc, 0x63, 0xfc, 0x41, 0xfc, 0x00, 0xfc, 0x01,
+ 0xfc, 0x03, 0xfc, 0x07, 0x00, 0x00, 0x00, 0x00 };
+ static const uchar cur_fdiag_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0xf8, 0x00, 0x78, 0x00,
+ 0xf8, 0x00, 0xd8, 0x01, 0x88, 0x23, 0x00, 0x37, 0x00, 0x3e, 0x00, 0x3c,
+ 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00 };
+ static const uchar mcur_fdiag_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0xfc, 0x03, 0xfc, 0x01, 0xfc, 0x00,
+ 0xfc, 0x41, 0xfc, 0x63, 0xdc, 0x77, 0x8c, 0x7f, 0x04, 0x7f, 0x00, 0x7e,
+ 0x00, 0x7f, 0x80, 0x7f, 0xc0, 0x7f, 0x00, 0x00 };
+ static const uchar *cursor_bits16[] = {
+ cur_ver_bits, mcur_ver_bits, cur_hor_bits, mcur_hor_bits,
+ cur_bdiag_bits, mcur_bdiag_bits, cur_fdiag_bits, mcur_fdiag_bits,
+ 0, 0, cur_blank_bits, cur_blank_bits };
+
+ static const uchar vsplit_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00,
+ 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ static const uchar vsplitm_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xf0, 0x07, 0x00,
+ 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00,
+ 0x00, 0xc0, 0x01, 0x00, 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00,
+ 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00,
+ 0x80, 0xff, 0xff, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00,
+ 0x00, 0xc0, 0x01, 0x00, 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xf0, 0x07, 0x00,
+ 0x00, 0xe0, 0x03, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ static const uchar hsplit_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00,
+ 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00,
+ 0x00, 0x41, 0x82, 0x00, 0x80, 0x41, 0x82, 0x01, 0xc0, 0x7f, 0xfe, 0x03,
+ 0x80, 0x41, 0x82, 0x01, 0x00, 0x41, 0x82, 0x00, 0x00, 0x40, 0x02, 0x00,
+ 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00,
+ 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ static const uchar hsplitm_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00,
+ 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe2, 0x47, 0x00, 0x00, 0xe3, 0xc7, 0x00,
+ 0x80, 0xe3, 0xc7, 0x01, 0xc0, 0xff, 0xff, 0x03, 0xe0, 0xff, 0xff, 0x07,
+ 0xc0, 0xff, 0xff, 0x03, 0x80, 0xe3, 0xc7, 0x01, 0x00, 0xe3, 0xc7, 0x00,
+ 0x00, 0xe2, 0x47, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00,
+ 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ static const uchar whatsthis_bits[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x05, 0xf0, 0x07, 0x00,
+ 0x09, 0x18, 0x0e, 0x00, 0x11, 0x1c, 0x0e, 0x00, 0x21, 0x1c, 0x0e, 0x00,
+ 0x41, 0x1c, 0x0e, 0x00, 0x81, 0x1c, 0x0e, 0x00, 0x01, 0x01, 0x07, 0x00,
+ 0x01, 0x82, 0x03, 0x00, 0xc1, 0xc7, 0x01, 0x00, 0x49, 0xc0, 0x01, 0x00,
+ 0x95, 0xc0, 0x01, 0x00, 0x93, 0xc0, 0x01, 0x00, 0x21, 0x01, 0x00, 0x00,
+ 0x20, 0xc1, 0x01, 0x00, 0x40, 0xc2, 0x01, 0x00, 0x40, 0x02, 0x00, 0x00,
+ 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
+ static const uchar whatsthism_bits[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x03, 0xf0, 0x07, 0x00, 0x07, 0xf8, 0x0f, 0x00,
+ 0x0f, 0xfc, 0x1f, 0x00, 0x1f, 0x3e, 0x1f, 0x00, 0x3f, 0x3e, 0x1f, 0x00,
+ 0x7f, 0x3e, 0x1f, 0x00, 0xff, 0x3e, 0x1f, 0x00, 0xff, 0x9d, 0x0f, 0x00,
+ 0xff, 0xc3, 0x07, 0x00, 0xff, 0xe7, 0x03, 0x00, 0x7f, 0xe0, 0x03, 0x00,
+ 0xf7, 0xe0, 0x03, 0x00, 0xf3, 0xe0, 0x03, 0x00, 0xe1, 0xe1, 0x03, 0x00,
+ 0xe0, 0xe1, 0x03, 0x00, 0xc0, 0xe3, 0x03, 0x00, 0xc0, 0xe3, 0x03, 0x00,
+ 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
+ static const uchar busy_bits[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
+ 0x41, 0xe0, 0xff, 0x00, 0x81, 0x20, 0x80, 0x00, 0x01, 0xe1, 0xff, 0x00,
+ 0x01, 0x42, 0x40, 0x00, 0xc1, 0x47, 0x40, 0x00, 0x49, 0x40, 0x55, 0x00,
+ 0x95, 0x80, 0x2a, 0x00, 0x93, 0x00, 0x15, 0x00, 0x21, 0x01, 0x0a, 0x00,
+ 0x20, 0x01, 0x11, 0x00, 0x40, 0x82, 0x20, 0x00, 0x40, 0x42, 0x44, 0x00,
+ 0x80, 0x41, 0x4a, 0x00, 0x00, 0x40, 0x55, 0x00, 0x00, 0xe0, 0xff, 0x00,
+ 0x00, 0x20, 0x80, 0x00, 0x00, 0xe0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ static const uchar busym_bits[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x0f, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00,
+ 0x7f, 0xe0, 0xff, 0x00, 0xff, 0xe0, 0xff, 0x00, 0xff, 0xe1, 0xff, 0x00,
+ 0xff, 0xc3, 0x7f, 0x00, 0xff, 0xc7, 0x7f, 0x00, 0x7f, 0xc0, 0x7f, 0x00,
+ 0xf7, 0x80, 0x3f, 0x00, 0xf3, 0x00, 0x1f, 0x00, 0xe1, 0x01, 0x0e, 0x00,
+ 0xe0, 0x01, 0x1f, 0x00, 0xc0, 0x83, 0x3f, 0x00, 0xc0, 0xc3, 0x7f, 0x00,
+ 0x80, 0xc1, 0x7f, 0x00, 0x00, 0xc0, 0x7f, 0x00, 0x00, 0xe0, 0xff, 0x00,
+ 0x00, 0xe0, 0xff, 0x00, 0x00, 0xe0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+ static const uchar * const cursor_bits32[] = {
+ vsplit_bits, vsplitm_bits, hsplit_bits, hsplitm_bits,
+ 0, 0, 0, 0, whatsthis_bits, whatsthism_bits, busy_bits, busym_bits
+ };
+
+ static const uchar forbidden_bits[] = {
+ 0x00,0x00,0x00,0x80,0x1f,0x00,0xe0,0x7f,0x00,0xf0,0xf0,0x00,0x38,0xc0,0x01,
+ 0x7c,0x80,0x03,0xec,0x00,0x03,0xce,0x01,0x07,0x86,0x03,0x06,0x06,0x07,0x06,
+ 0x06,0x0e,0x06,0x06,0x1c,0x06,0x0e,0x38,0x07,0x0c,0x70,0x03,0x1c,0xe0,0x03,
+ 0x38,0xc0,0x01,0xf0,0xe0,0x00,0xe0,0x7f,0x00,0x80,0x1f,0x00,0x00,0x00,0x00 };
+
+ static const uchar forbiddenm_bits[] = {
+ 0x80,0x1f,0x00,0xe0,0x7f,0x00,0xf0,0xff,0x00,0xf8,0xff,0x01,0xfc,0xf0,0x03,
+ 0xfe,0xc0,0x07,0xfe,0x81,0x07,0xff,0x83,0x0f,0xcf,0x07,0x0f,0x8f,0x0f,0x0f,
+ 0x0f,0x1f,0x0f,0x0f,0x3e,0x0f,0x1f,0xfc,0x0f,0x1e,0xf8,0x07,0x3e,0xf0,0x07,
+ 0xfc,0xe0,0x03,0xf8,0xff,0x01,0xf0,0xff,0x00,0xe0,0x7f,0x00,0x80,0x1f,0x00};
+
+ static const uchar openhand_bits[] = {
+ 0x80,0x01,0x58,0x0e,0x64,0x12,0x64,0x52,0x48,0xb2,0x48,0x92,
+ 0x16,0x90,0x19,0x80,0x11,0x40,0x02,0x40,0x04,0x40,0x04,0x20,
+ 0x08,0x20,0x10,0x10,0x20,0x10,0x00,0x00};
+ static const uchar openhandm_bits[] = {
+ 0x80,0x01,0xd8,0x0f,0xfc,0x1f,0xfc,0x5f,0xf8,0xff,0xf8,0xff,
+ 0xf6,0xff,0xff,0xff,0xff,0x7f,0xfe,0x7f,0xfc,0x7f,0xfc,0x3f,
+ 0xf8,0x3f,0xf0,0x1f,0xe0,0x1f,0x00,0x00};
+ static const uchar closedhand_bits[] = {
+ 0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x0d,0x48,0x32,0x08,0x50,
+ 0x10,0x40,0x18,0x40,0x04,0x40,0x04,0x20,0x08,0x20,0x10,0x10,
+ 0x20,0x10,0x20,0x10,0x00,0x00,0x00,0x00};
+ static const uchar closedhandm_bits[] = {
+ 0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x0d,0xf8,0x3f,0xf8,0x7f,
+ 0xf0,0x7f,0xf8,0x7f,0xfc,0x7f,0xfc,0x3f,0xf8,0x3f,0xf0,0x1f,
+ 0xe0,0x1f,0xe0,0x1f,0x00,0x00,0x00,0x00};
+
+ static const uchar * const cursor_bits20[] = {
+ forbidden_bits, forbiddenm_bits
+ };
+
+ if ((cshape >= Qt::SizeVerCursor && cshape < Qt::SizeAllCursor)
+ || cshape == Qt::BlankCursor) {
+ XColor bg, fg;
+ bg.red = 255 << 8;
+ bg.green = 255 << 8;
+ bg.blue = 255 << 8;
+ fg.red = 0;
+ fg.green = 0;
+ fg.blue = 0;
+ int i = (cshape - Qt::SizeVerCursor) * 2;
+ pm = XCreateBitmapFromData(dpy, rootwin, reinterpret_cast<const char*>(cursor_bits16[i]), 16, 16);
+ pmm = XCreateBitmapFromData(dpy, rootwin, reinterpret_cast<const char*>(cursor_bits16[i + 1]), 16, 16);
+ hcurs = XCreatePixmapCursor(dpy, pm, pmm, &fg, &bg, 8, 8);
+ } else if ((cshape >= Qt::SplitVCursor && cshape <= Qt::SplitHCursor)
+ || cshape == Qt::WhatsThisCursor || cshape == Qt::BusyCursor) {
+ XColor bg, fg;
+ bg.red = 255 << 8;
+ bg.green = 255 << 8;
+ bg.blue = 255 << 8;
+ fg.red = 0;
+ fg.green = 0;
+ fg.blue = 0;
+ int i = (cshape - Qt::SplitVCursor) * 2;
+ pm = XCreateBitmapFromData(dpy, rootwin, reinterpret_cast<const char *>(cursor_bits32[i]), 32, 32);
+ pmm = XCreateBitmapFromData(dpy, rootwin, reinterpret_cast<const char *>(cursor_bits32[i + 1]), 32, 32);
+ int hs = (cshape == Qt::PointingHandCursor || cshape == Qt::WhatsThisCursor
+ || cshape == Qt::BusyCursor) ? 0 : 16;
+ hcurs = XCreatePixmapCursor(dpy, pm, pmm, &fg, &bg, hs, hs);
+ } else if (cshape == Qt::ForbiddenCursor) {
+ XColor bg, fg;
+ bg.red = 255 << 8;
+ bg.green = 255 << 8;
+ bg.blue = 255 << 8;
+ fg.red = 0;
+ fg.green = 0;
+ fg.blue = 0;
+ int i = (cshape - Qt::ForbiddenCursor) * 2;
+ pm = XCreateBitmapFromData(dpy, rootwin, reinterpret_cast<const char *>(cursor_bits20[i]), 20, 20);
+ pmm = XCreateBitmapFromData(dpy, rootwin, reinterpret_cast<const char *>(cursor_bits20[i + 1]), 20, 20);
+ hcurs = XCreatePixmapCursor(dpy, pm, pmm, &fg, &bg, 10, 10);
+ } else if (cshape == Qt::OpenHandCursor || cshape == Qt::ClosedHandCursor) {
+ XColor bg, fg;
+ bg.red = 255 << 8;
+ bg.green = 255 << 8;
+ bg.blue = 255 << 8;
+ fg.red = 0;
+ fg.green = 0;
+ fg.blue = 0;
+ bool open = cshape == Qt::OpenHandCursor;
+ pm = XCreateBitmapFromData(dpy, rootwin, reinterpret_cast<const char *>(open ? openhand_bits : closedhand_bits), 16, 16);
+ pmm = XCreateBitmapFromData(dpy, rootwin, reinterpret_cast<const char *>(open ? openhandm_bits : closedhandm_bits), 16, 16);
+ hcurs = XCreatePixmapCursor(dpy, pm, pmm, &fg, &bg, 8, 8);
+ } else if (cshape == Qt::DragCopyCursor || cshape == Qt::DragMoveCursor
+ || cshape == Qt::DragLinkCursor) {
+ XColor bg, fg;
+ bg.red = 255 << 8;
+ bg.green = 255 << 8;
+ bg.blue = 255 << 8;
+ fg.red = 0;
+ fg.green = 0;
+ fg.blue = 0;
+ QImage image = QApplicationPrivate::instance()->getPixmapCursor(cshape).toImage();
+ pm = QX11PlatformPixmap::createBitmapFromImage(image);
+ pmm = QX11PlatformPixmap::createBitmapFromImage(image.createAlphaMask().convertToFormat(QImage::Format_MonoLSB));
+ hcurs = XCreatePixmapCursor(dpy, pm, pmm, &fg, &bg, 8, 8);
+ }
+
+ if (hcurs)
+ {
+#ifndef QT_NO_XFIXES
+ if (X11->use_xfixes && X11->ptrXFixesSetCursorName)
+ X11->ptrXFixesSetCursorName(dpy, hcurs, cursorNames[cshape]);
+#endif /* ! QT_NO_XFIXES */
+ return;
+ }
+
+#endif /* ! QT_USE_APPROXIMATE_CURSORS */
+
+ uint sh;
+ switch (cshape) { // map Q cursor to X cursor
+ case Qt::ArrowCursor:
+ sh = XC_left_ptr;
+ break;
+ case Qt::UpArrowCursor:
+ sh = XC_center_ptr;
+ break;
+ case Qt::CrossCursor:
+ sh = XC_crosshair;
+ break;
+ case Qt::WaitCursor:
+ sh = XC_watch;
+ break;
+ case Qt::IBeamCursor:
+ sh = XC_xterm;
+ break;
+ case Qt::SizeAllCursor:
+ sh = XC_fleur;
+ break;
+ case Qt::PointingHandCursor:
+ sh = XC_hand2;
+ break;
+#ifdef QT_USE_APPROXIMATE_CURSORS
+ case Qt::SizeBDiagCursor:
+ sh = XC_top_right_corner;
+ break;
+ case Qt::SizeFDiagCursor:
+ sh = XC_bottom_right_corner;
+ break;
+ case Qt::BlankCursor:
+ XColor bg, fg;
+ bg.red = 255 << 8;
+ bg.green = 255 << 8;
+ bg.blue = 255 << 8;
+ fg.red = 0;
+ fg.green = 0;
+ fg.blue = 0;
+ pm = XCreateBitmapFromData(dpy, rootwin, cur_blank_bits, 16, 16);
+ pmm = XCreateBitmapFromData(dpy, rootwin, cur_blank_bits, 16, 16);
+ hcurs = XCreatePixmapCursor(dpy, pm, pmm, &fg, &bg, 8, 8);
+ return;
+ break;
+ case Qt::SizeVerCursor:
+ case Qt::SplitVCursor:
+ sh = XC_sb_v_double_arrow;
+ break;
+ case Qt::SizeHorCursor:
+ case Qt::SplitHCursor:
+ sh = XC_sb_h_double_arrow;
+ break;
+ case Qt::WhatsThisCursor:
+ sh = XC_question_arrow;
+ break;
+ case Qt::ForbiddenCursor:
+ sh = XC_circle;
+ break;
+ case Qt::BusyCursor:
+ sh = XC_watch;
+ break;
+ case Qt::DragCopyCursor:
+ sh = XC_tcross;
+ break;
+ case Qt::DragLinkCursor:
+ sh = XC_center_ptr;
+ break;
+ case Qt::DragMoveCursor:
+ sh = XC_top_left_arrow;
+ break;
+#endif /* QT_USE_APPROXIMATE_CURSORS */
+ default:
+ qWarning("QCursor::update: Invalid cursor shape %d", cshape);
+ return;
+ }
+ hcurs = XCreateFontCursor(dpy, sh);
+
+#ifndef QT_NO_XFIXES
+ if (X11->use_xfixes && X11->ptrXFixesSetCursorName)
+ X11->ptrXFixesSetCursorName(dpy, hcurs, cursorNames[cshape]);
+#endif /* ! QT_NO_XFIXES */
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/x11/qdesktopwidget_x11.cpp b/src/widgets/platforms/x11/qdesktopwidget_x11.cpp
new file mode 100644
index 0000000000..52d3be18e9
--- /dev/null
+++ b/src/widgets/platforms/x11/qdesktopwidget_x11.cpp
@@ -0,0 +1,406 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qapplication.h"
+#include "qdesktopwidget.h"
+#include "qlibrary.h"
+#include "qt_x11_p.h"
+#include "qvariant.h"
+#include "qwidget_p.h"
+#include "qx11info_x11.h"
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+// defined in qwidget_x11.cpp
+extern int qt_x11_create_desktop_on_screen;
+
+
+// function to update the workarea of the screen
+static bool qt_desktopwidget_workarea_dirty = true;
+void qt_desktopwidget_update_workarea()
+{
+ qt_desktopwidget_workarea_dirty = true;
+}
+
+
+class QSingleDesktopWidget : public QWidget
+{
+public:
+ QSingleDesktopWidget();
+ ~QSingleDesktopWidget();
+};
+
+QSingleDesktopWidget::QSingleDesktopWidget()
+ : QWidget(0, Qt::Desktop)
+{
+}
+
+QSingleDesktopWidget::~QSingleDesktopWidget()
+{
+ const QObjectList &childList = children();
+ for (int i = childList.size(); i > 0 ;) {
+ --i;
+ childList.at(i)->setParent(0);
+ }
+}
+
+
+class QDesktopWidgetPrivate : public QWidgetPrivate
+{
+public:
+ QDesktopWidgetPrivate();
+ ~QDesktopWidgetPrivate();
+
+ void init();
+
+ bool use_xinerama;
+ int defaultScreen;
+ int screenCount;
+
+ QWidget **screens;
+ QRect *rects;
+ QRect *workareas;
+};
+
+QDesktopWidgetPrivate::QDesktopWidgetPrivate()
+ : use_xinerama(false), defaultScreen(0), screenCount(1),
+ screens(0), rects(0), workareas(0)
+{
+}
+
+QDesktopWidgetPrivate::~QDesktopWidgetPrivate()
+{
+ if (screens) {
+ for (int i = 0; i < screenCount; ++i) {
+ if (i == defaultScreen) continue;
+ delete screens[i];
+ screens[i] = 0;
+ }
+
+ free (screens);
+ }
+
+ if (rects) delete [] rects;
+ if (workareas) delete [] workareas;
+}
+
+void QDesktopWidgetPrivate::init()
+{
+ // get the screen count
+ int newScreenCount = ScreenCount(X11->display);
+#ifndef QT_NO_XINERAMA
+
+ XineramaScreenInfo *xinerama_screeninfo = 0;
+
+ // we ignore the Xinerama extension when using the display is
+ // using traditional multi-screen (with multiple root windows)
+ if (newScreenCount == 1
+ && X11->ptrXineramaQueryExtension
+ && X11->ptrXineramaIsActive
+ && X11->ptrXineramaQueryScreens) {
+ int unused;
+ use_xinerama = (X11->ptrXineramaQueryExtension(X11->display, &unused, &unused)
+ && X11->ptrXineramaIsActive(X11->display));
+ }
+
+ if (use_xinerama) {
+ xinerama_screeninfo =
+ X11->ptrXineramaQueryScreens(X11->display, &newScreenCount);
+ }
+
+ if (xinerama_screeninfo) {
+ defaultScreen = 0;
+ } else
+#endif // QT_NO_XINERAMA
+ {
+ defaultScreen = DefaultScreen(X11->display);
+ newScreenCount = ScreenCount(X11->display);
+ use_xinerama = false;
+ }
+
+ delete [] rects;
+ rects = new QRect[newScreenCount];
+ delete [] workareas;
+ workareas = new QRect[newScreenCount];
+
+ // get the geometry of each screen
+ int i, j, x, y, w, h;
+ for (i = 0, j = 0; i < newScreenCount; i++, j++) {
+
+#ifndef QT_NO_XINERAMA
+ if (use_xinerama) {
+ x = xinerama_screeninfo[i].x_org;
+ y = xinerama_screeninfo[i].y_org;
+ w = xinerama_screeninfo[i].width;
+ h = xinerama_screeninfo[i].height;
+ } else
+#endif // QT_NO_XINERAMA
+ {
+ x = 0;
+ y = 0;
+ w = WidthOfScreen(ScreenOfDisplay(X11->display, i));
+ h = HeightOfScreen(ScreenOfDisplay(X11->display, i));
+ }
+
+ rects[j].setRect(x, y, w, h);
+
+ if (use_xinerama && j > 0 && rects[j-1].intersects(rects[j])) {
+ // merge a "cloned" screen with the previous, hiding all crtcs
+ // that are currently showing a sub-rect of the previous screen
+ if ((rects[j].width()*rects[j].height()) >
+ (rects[j-1].width()*rects[j-1].height()))
+ rects[j-1] = rects[j];
+ j--;
+ }
+
+ workareas[i] = QRect();
+ }
+
+ if (screens) {
+ // leaks QWidget* pointers on purpose, can't delete them as pointer escapes
+ screens = q_check_ptr((QWidget**) realloc(screens, j * sizeof(QWidget*)));
+ if (j > screenCount)
+ memset(&screens[screenCount], 0, (j-screenCount) * sizeof(QWidget*));
+ }
+
+ screenCount = j;
+
+#ifndef QT_NO_XINERAMA
+ if (use_xinerama && screenCount == 1)
+ use_xinerama = false;
+
+ if (xinerama_screeninfo)
+ XFree(xinerama_screeninfo);
+#endif // QT_NO_XINERAMA
+
+}
+
+// the QDesktopWidget itself will be created on the default screen
+// as qt_x11_create_desktop_on_screen defaults to -1
+QDesktopWidget::QDesktopWidget()
+ : QWidget(*new QDesktopWidgetPrivate, 0, Qt::Desktop)
+{
+ Q_D(QDesktopWidget);
+ d->init();
+}
+
+QDesktopWidget::~QDesktopWidget()
+{
+}
+
+bool QDesktopWidget::isVirtualDesktop() const
+{
+ Q_D(const QDesktopWidget);
+ return d->use_xinerama;
+}
+
+int QDesktopWidget::primaryScreen() const
+{
+ Q_D(const QDesktopWidget);
+ return d->defaultScreen;
+}
+
+int QDesktopWidget::numScreens() const
+{
+ Q_D(const QDesktopWidget);
+ return d->screenCount;
+}
+
+QWidget *QDesktopWidget::screen(int screen)
+{
+ Q_D(QDesktopWidget);
+ if (d->use_xinerama)
+ return this;
+
+ if (screen < 0 || screen >= d->screenCount)
+ screen = d->defaultScreen;
+
+ if (! d->screens) {
+ d->screens = (QWidget**) calloc( d->screenCount, sizeof(QWidget*));
+ d->screens[d->defaultScreen] = this;
+ }
+
+ if (! d->screens[screen] || // not created yet
+ ! (d->screens[screen]->windowType() == Qt::Desktop)) { // reparented away
+ qt_x11_create_desktop_on_screen = screen;
+ d->screens[screen] = new QSingleDesktopWidget;
+ qt_x11_create_desktop_on_screen = -1;
+ }
+
+ return d->screens[screen];
+}
+
+const QRect QDesktopWidget::availableGeometry(int screen) const
+{
+ Q_D(const QDesktopWidget);
+ if (qt_desktopwidget_workarea_dirty) {
+ // the workareas are dirty, invalidate them
+ for (int i = 0; i < d->screenCount; ++i)
+ d->workareas[i] = QRect();
+ qt_desktopwidget_workarea_dirty = false;
+ }
+
+ if (screen < 0 || screen >= d->screenCount)
+ screen = d->defaultScreen;
+
+ if (d->workareas[screen].isValid())
+ return d->workareas[screen];
+
+ if (X11->isSupportedByWM(ATOM(_NET_WORKAREA))) {
+ int x11Screen = isVirtualDesktop() ? DefaultScreen(X11->display) : screen;
+
+ Atom ret;
+ int format, e;
+ unsigned char *data = 0;
+ unsigned long nitems, after;
+
+ e = XGetWindowProperty(X11->display,
+ QX11Info::appRootWindow(x11Screen),
+ ATOM(_NET_WORKAREA), 0, 4, False, XA_CARDINAL,
+ &ret, &format, &nitems, &after, &data);
+
+ QRect workArea;
+ if (e == Success && ret == XA_CARDINAL &&
+ format == 32 && nitems == 4) {
+ long *workarea = (long *) data;
+ workArea = QRect(workarea[0], workarea[1], workarea[2], workarea[3]);
+ } else {
+ workArea = screenGeometry(screen);
+ }
+
+ if (isVirtualDesktop()) {
+ // intersect the workarea (which spawns all Xinerama screens) with the rect for the
+ // requested screen
+ workArea &= screenGeometry(screen);
+ }
+
+ d->workareas[screen] = workArea;
+
+ if (data)
+ XFree(data);
+ } else {
+ d->workareas[screen] = screenGeometry(screen);
+ }
+
+ return d->workareas[screen];
+}
+
+const QRect QDesktopWidget::screenGeometry(int screen) const
+{
+ Q_D(const QDesktopWidget);
+ if (screen < 0 || screen >= d->screenCount)
+ screen = d->defaultScreen;
+
+ return d->rects[screen];
+}
+
+int QDesktopWidget::screenNumber(const QWidget *widget) const
+{
+ Q_D(const QDesktopWidget);
+ if (!widget)
+ return d->defaultScreen;
+
+#ifndef QT_NO_XINERAMA
+ if (d->use_xinerama) {
+ // this is how we do it for xinerama
+ QRect frame = widget->frameGeometry();
+ if (!widget->isWindow())
+ frame.moveTopLeft(widget->mapToGlobal(QPoint(0, 0)));
+
+ int maxSize = -1;
+ int maxScreen = -1;
+
+ for (int i = 0; i < d->screenCount; ++i) {
+ QRect sect = d->rects[i].intersected(frame);
+ int size = sect.width() * sect.height();
+ if (size > maxSize && sect.width() > 0 && sect.height() > 0) {
+ maxSize = size;
+ maxScreen = i;
+ }
+ }
+ return maxScreen;
+ }
+#endif // QT_NO_XINERAMA
+
+ return widget->x11Info().screen();
+}
+
+int QDesktopWidget::screenNumber(const QPoint &point) const
+{
+ Q_D(const QDesktopWidget);
+ int closestScreen = -1;
+ int shortestDistance = INT_MAX;
+ for (int i = 0; i < d->screenCount; ++i) {
+ int thisDistance = d->pointToRect(point, d->rects[i]);
+ if (thisDistance < shortestDistance) {
+ shortestDistance = thisDistance;
+ closestScreen = i;
+ }
+ }
+ return closestScreen;
+}
+
+void QDesktopWidget::resizeEvent(QResizeEvent *event)
+{
+ Q_D(QDesktopWidget);
+ int oldScreenCount = d->screenCount;
+ QVector<QRect> oldRects(oldScreenCount);
+ for (int i = 0; i < oldScreenCount; ++i) {
+ oldRects[i] = d->rects[i];
+ }
+
+ d->init();
+
+ for (int i = 0; i < qMin(oldScreenCount, d->screenCount); ++i) {
+ if (oldRects.at(i) != d->rects[i])
+ emit resized(i);
+ }
+
+ if (oldScreenCount != d->screenCount) {
+ emit screenCountChanged(d->screenCount);
+ }
+
+ qt_desktopwidget_workarea_dirty = true;
+ QWidget::resizeEvent(event);
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/x11/qdnd_x11.cpp b/src/widgets/platforms/x11/qdnd_x11.cpp
new file mode 100644
index 0000000000..2718853920
--- /dev/null
+++ b/src/widgets/platforms/x11/qdnd_x11.cpp
@@ -0,0 +1,2072 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+
+#include "qapplication.h"
+
+#ifndef QT_NO_DRAGANDDROP
+
+#include "qwidget.h"
+#include "qpainter.h"
+#include "qpixmap.h"
+#include "qbitmap.h"
+#include "qdesktopwidget.h"
+#include "qevent.h"
+#include "qiodevice.h"
+#include "qpointer.h"
+#include "qcursor.h"
+#include "qelapsedtimer.h"
+#include "qvariant.h"
+#include "qvector.h"
+#include "qurl.h"
+#include "qdebug.h"
+#include "qimagewriter.h"
+#include "qbuffer.h"
+#include "qtextcodec.h"
+
+#include "qdnd_p.h"
+#include "qapplication_p.h"
+#include "qt_x11_p.h"
+#include "qx11info_x11.h"
+
+#include "qwidget_p.h"
+#include "qcursor_p.h"
+
+QT_BEGIN_NAMESPACE
+
+// #define DND_DEBUG
+#ifdef DND_DEBUG
+#define DEBUG qDebug
+#else
+#define DEBUG if(0) qDebug
+#endif
+
+#ifdef DND_DEBUG
+#define DNDDEBUG qDebug()
+#else
+#define DNDDEBUG if(0) qDebug()
+#endif
+
+static int findXdndDropTransactionByWindow(Window window)
+{
+ int at = -1;
+ for (int i = 0; i < X11->dndDropTransactions.count(); ++i) {
+ const QXdndDropTransaction &t = X11->dndDropTransactions.at(i);
+ if (t.target == window || t.proxy_target == window) {
+ at = i;
+ break;
+ }
+ }
+ return at;
+}
+
+static int findXdndDropTransactionByTime(Time timestamp)
+{
+ int at = -1;
+ for (int i = 0; i < X11->dndDropTransactions.count(); ++i) {
+ const QXdndDropTransaction &t = X11->dndDropTransactions.at(i);
+ if (t.timestamp == timestamp) {
+ at = i;
+ break;
+ }
+ }
+ return at;
+}
+
+// timer used to discard old XdndDrop transactions
+static int transaction_expiry_timer = -1;
+enum { XdndDropTransactionTimeout = 5000 }; // 5 seconds
+
+static void restartXdndDropExpiryTimer()
+{
+ if (transaction_expiry_timer != -1)
+ QDragManager::self()->killTimer(transaction_expiry_timer);
+ transaction_expiry_timer = QDragManager::self()->startTimer(XdndDropTransactionTimeout);
+}
+
+
+// find an ancestor with XdndAware on it
+static Window findXdndAwareParent(Window window)
+{
+ Window target = 0;
+ forever {
+ // check if window has XdndAware
+ Atom type = 0;
+ int f;
+ unsigned long n, a;
+ unsigned char *data = 0;
+ if (XGetWindowProperty(X11->display, window, ATOM(XdndAware), 0, 0, False,
+ AnyPropertyType, &type, &f,&n,&a,&data) == Success) {
+ if (data)
+ XFree(data);
+ if (type) {
+ target = window;
+ break;
+ }
+ }
+
+ // try window's parent
+ Window root;
+ Window parent;
+ Window *children;
+ uint unused;
+ if (!XQueryTree(X11->display, window, &root, &parent, &children, &unused))
+ break;
+ if (children)
+ XFree(children);
+ if (window == root)
+ break;
+ window = parent;
+ }
+ return target;
+}
+
+
+
+
+// and all this stuff is copied -into- qapp_x11.cpp
+
+static void handle_xdnd_position(QWidget *, const XEvent *, bool);
+static void handle_xdnd_status(QWidget * w, const XEvent * xe, bool /*passive*/);
+
+const int xdnd_version = 5;
+
+static Qt::DropAction xdndaction_to_qtaction(Atom atom)
+{
+ if (atom == ATOM(XdndActionCopy) || atom == 0)
+ return Qt::CopyAction;
+ if (atom == ATOM(XdndActionLink))
+ return Qt::LinkAction;
+ if (atom == ATOM(XdndActionMove))
+ return Qt::MoveAction;
+ return Qt::CopyAction;
+}
+
+static int qtaction_to_xdndaction(Qt::DropAction a)
+{
+ switch (a) {
+ case Qt::CopyAction:
+ return ATOM(XdndActionCopy);
+ case Qt::LinkAction:
+ return ATOM(XdndActionLink);
+ case Qt::MoveAction:
+ case Qt::TargetMoveAction:
+ return ATOM(XdndActionMove);
+ case Qt::IgnoreAction:
+ return XNone;
+ default:
+ return ATOM(XdndActionCopy);
+ }
+}
+
+// clean up the stuff used.
+static void qt_xdnd_cleanup();
+
+static void qt_xdnd_send_leave();
+
+// real variables:
+// xid of current drag source
+static Atom qt_xdnd_dragsource_xid = 0;
+
+// the types in this drop. 100 is no good, but at least it's big.
+const int qt_xdnd_max_type = 100;
+static Atom qt_xdnd_types[qt_xdnd_max_type + 1];
+
+// timer used when target wants "continuous" move messages (eg. scroll)
+static int heartbeat = -1;
+// rectangle in which the answer will be the same
+static QRect qt_xdnd_source_sameanswer;
+// top-level window we sent position to last.
+static Window qt_xdnd_current_target;
+// window to send events to (always valid if qt_xdnd_current_target)
+static Window qt_xdnd_current_proxy_target;
+static Time qt_xdnd_source_current_time;
+
+// widget we forwarded position to last, and local position
+static QPointer<QWidget> qt_xdnd_current_widget;
+static QPoint qt_xdnd_current_position;
+// timestamp from the XdndPosition and XdndDrop
+static Time qt_xdnd_target_current_time;
+// screen number containing the pointer... -1 means default
+static int qt_xdnd_current_screen = -1;
+// state of dragging... true if dragging, false if not
+bool qt_xdnd_dragging = false;
+
+static bool waiting_for_status = false;
+
+// used to preset each new QDragMoveEvent
+static Qt::DropAction last_target_accepted_action = Qt::IgnoreAction;
+
+// Shift/Ctrl handling, and final drop status
+static Qt::DropAction global_accepted_action = Qt::CopyAction;
+static Qt::DropActions possible_actions = Qt::IgnoreAction;
+
+// for embedding only
+static QWidget* current_embedding_widget = 0;
+static XEvent last_enter_event;
+
+// cursors
+static QCursor *noDropCursor = 0;
+static QCursor *moveCursor = 0;
+static QCursor *copyCursor = 0;
+static QCursor *linkCursor = 0;
+
+static QPixmap *defaultPm = 0;
+
+static const int default_pm_hotx = -2;
+static const int default_pm_hoty = -16;
+static const char* const default_pm[] = {
+"13 9 3 1",
+". c None",
+" c #000000",
+"X c #FFFFFF",
+"X X X X X X X",
+" X X X X X X ",
+"X ......... X",
+" X.........X ",
+"X ......... X",
+" X.........X ",
+"X ......... X",
+" X X X X X X ",
+"X X X X X X X"
+};
+
+class QShapedPixmapWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ QShapedPixmapWidget(QWidget* w) :
+ QWidget(w,
+ Qt::Tool | Qt::FramelessWindowHint
+ | Qt::X11BypassWindowManagerHint
+ | Qt::BypassGraphicsProxyWidget)
+ {
+ setAttribute(Qt::WA_X11NetWmWindowTypeDND);
+ }
+
+ void setPixmap(const QPixmap &pm)
+ {
+ QBitmap mask = pm.mask();
+ if (!mask.isNull()) {
+ setMask(mask);
+ } else {
+ clearMask();
+ }
+ resize(pm.width(),pm.height());
+ pixmap = pm;
+ update();
+ }
+ QPoint pm_hot;
+
+protected:
+ QPixmap pixmap;
+ void paintEvent(QPaintEvent*)
+ {
+ QPainter p(this);
+ p.drawPixmap(0, 0, pixmap);
+ }
+};
+
+#include "qdnd_x11.moc"
+
+struct XdndData {
+ QShapedPixmapWidget *deco;
+ QWidget* desktop_proxy;
+};
+
+static XdndData xdnd_data = { 0, 0 };
+
+class QExtraWidget : public QWidget
+{
+ Q_DECLARE_PRIVATE(QWidget)
+public:
+ inline QWExtra* extraData();
+ inline QTLWExtra* topData();
+};
+
+inline QWExtra* QExtraWidget::extraData() { return d_func()->extraData(); }
+inline QTLWExtra* QExtraWidget::topData() { return d_func()->topData(); }
+
+
+static WId xdndProxy(WId w)
+{
+ Atom type = XNone;
+ int f;
+ unsigned long n, a;
+ unsigned char *retval = 0;
+ XGetWindowProperty(X11->display, w, ATOM(XdndProxy), 0, 1, False,
+ XA_WINDOW, &type, &f,&n,&a,&retval);
+ WId *proxy_id_ptr = (WId *)retval;
+ WId proxy_id = 0;
+ if (type == XA_WINDOW && proxy_id_ptr) {
+ proxy_id = *proxy_id_ptr;
+ XFree(proxy_id_ptr);
+ proxy_id_ptr = 0;
+ // Already exists. Real?
+ X11->ignoreBadwindow();
+ XGetWindowProperty(X11->display, proxy_id, ATOM(XdndProxy), 0, 1, False,
+ XA_WINDOW, &type, &f,&n,&a,&retval);
+ proxy_id_ptr = (WId *)retval;
+ if (X11->badwindow() || type != XA_WINDOW || !proxy_id_ptr || *proxy_id_ptr != proxy_id)
+ // Bogus - we will overwrite.
+ proxy_id = 0;
+ }
+ if (proxy_id_ptr)
+ XFree(proxy_id_ptr);
+ return proxy_id;
+}
+
+static bool xdndEnable(QWidget* w, bool on)
+{
+ DNDDEBUG << "xdndEnable" << w << on;
+ if (on) {
+ QWidget * xdnd_widget = 0;
+ if ((w->windowType() == Qt::Desktop)) {
+ if (xdnd_data.desktop_proxy) // *WE* already have one.
+ return false;
+
+ // As per Xdnd4, use XdndProxy
+ XGrabServer(X11->display);
+ Q_ASSERT(w->testAttribute(Qt::WA_WState_Created));
+ WId proxy_id = xdndProxy(w->effectiveWinId());
+
+ if (!proxy_id) {
+ xdnd_widget = xdnd_data.desktop_proxy = new QWidget;
+ proxy_id = xdnd_data.desktop_proxy->effectiveWinId();
+ XChangeProperty (X11->display, w->effectiveWinId(), ATOM(XdndProxy),
+ XA_WINDOW, 32, PropModeReplace, (unsigned char *)&proxy_id, 1);
+ XChangeProperty (X11->display, proxy_id, ATOM(XdndProxy),
+ XA_WINDOW, 32, PropModeReplace, (unsigned char *)&proxy_id, 1);
+ }
+
+ XUngrabServer(X11->display);
+ } else {
+ xdnd_widget = w->window();
+ }
+ if (xdnd_widget) {
+ DNDDEBUG << "setting XdndAware for" << xdnd_widget << xdnd_widget->effectiveWinId();
+ Atom atm = (Atom)xdnd_version;
+ Q_ASSERT(xdnd_widget->testAttribute(Qt::WA_WState_Created));
+ XChangeProperty(X11->display, xdnd_widget->effectiveWinId(), ATOM(XdndAware),
+ XA_ATOM, 32, PropModeReplace, (unsigned char *)&atm, 1);
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ if ((w->windowType() == Qt::Desktop)) {
+ XDeleteProperty(X11->display, w->internalWinId(), ATOM(XdndProxy));
+ delete xdnd_data.desktop_proxy;
+ xdnd_data.desktop_proxy = 0;
+ } else {
+ DNDDEBUG << "not deleting XDndAware";
+ }
+ return true;
+ }
+}
+
+QByteArray QX11Data::xdndAtomToString(Atom a)
+{
+ if (!a) return 0;
+
+ if (a == XA_STRING || a == ATOM(UTF8_STRING)) {
+ return "text/plain"; // some Xdnd clients are dumb
+ }
+ char *atom = XGetAtomName(display, a);
+ QByteArray result = atom;
+ XFree(atom);
+ return result;
+}
+
+Atom QX11Data::xdndStringToAtom(const char *mimeType)
+{
+ if (!mimeType || !*mimeType)
+ return 0;
+ return XInternAtom(display, mimeType, False);
+}
+
+//$$$
+QString QX11Data::xdndMimeAtomToString(Atom a)
+{
+ QString atomName;
+ if (a) {
+ char *atom = XGetAtomName(display, a);
+ atomName = QString::fromLatin1(atom);
+ XFree(atom);
+ }
+ return atomName;
+}
+
+//$$$
+Atom QX11Data::xdndMimeStringToAtom(const QString &mimeType)
+{
+ if (mimeType.isEmpty())
+ return 0;
+ return XInternAtom(display, mimeType.toLatin1().constData(), False);
+}
+
+//$$$ replace ccxdndAtomToString()
+QStringList QX11Data::xdndMimeFormatsForAtom(Atom a)
+{
+ QStringList formats;
+ if (a) {
+ QString atomName = xdndMimeAtomToString(a);
+ formats.append(atomName);
+
+ // special cases for string type
+ if (a == ATOM(UTF8_STRING) || a == XA_STRING
+ || a == ATOM(TEXT) || a == ATOM(COMPOUND_TEXT))
+ formats.append(QLatin1String("text/plain"));
+
+ // special cases for uris
+ if (atomName == QLatin1String("text/x-moz-url"))
+ formats.append(QLatin1String("text/uri-list"));
+
+ // special case for images
+ if (a == XA_PIXMAP)
+ formats.append(QLatin1String("image/ppm"));
+ }
+ return formats;
+}
+
+//$$$
+bool QX11Data::xdndMimeDataForAtom(Atom a, QMimeData *mimeData, QByteArray *data, Atom *atomFormat, int *dataFormat)
+{
+ bool ret = false;
+ *atomFormat = a;
+ *dataFormat = 8;
+ QString atomName = xdndMimeAtomToString(a);
+ if (QInternalMimeData::hasFormatHelper(atomName, mimeData)) {
+ *data = QInternalMimeData::renderDataHelper(atomName, mimeData);
+ if (atomName == QLatin1String("application/x-color"))
+ *dataFormat = 16;
+ ret = true;
+ } else {
+ if ((a == ATOM(UTF8_STRING) || a == XA_STRING
+ || a == ATOM(TEXT) || a == ATOM(COMPOUND_TEXT))
+ && QInternalMimeData::hasFormatHelper(QLatin1String("text/plain"), mimeData)) {
+ if (a == ATOM(UTF8_STRING)){
+ *data = QInternalMimeData::renderDataHelper(QLatin1String("text/plain"), mimeData);
+ ret = true;
+ } else if (a == XA_STRING) {
+ *data = QString::fromUtf8(QInternalMimeData::renderDataHelper(
+ QLatin1String("text/plain"), mimeData)).toLocal8Bit();
+ ret = true;
+ } else if (a == ATOM(TEXT) || a == ATOM(COMPOUND_TEXT)) {
+ // the ICCCM states that TEXT and COMPOUND_TEXT are in the
+ // encoding of choice, so we choose the encoding of the locale
+ QByteArray strData = QString::fromUtf8(QInternalMimeData::renderDataHelper(
+ QLatin1String("text/plain"), mimeData)).toLocal8Bit();
+ char *list[] = { strData.data(), NULL };
+
+ XICCEncodingStyle style = (a == ATOM(COMPOUND_TEXT))
+ ? XCompoundTextStyle : XStdICCTextStyle;
+ XTextProperty textprop;
+ if (list[0] != NULL
+ && XmbTextListToTextProperty(X11->display, list, 1, style,
+ &textprop) == Success) {
+ *atomFormat = textprop.encoding;
+ *dataFormat = textprop.format;
+ *data = QByteArray((const char *) textprop.value, textprop.nitems * textprop.format / 8);
+ ret = true;
+
+ DEBUG(" textprop type %lx\n"
+ " textprop name '%s'\n"
+ " format %d\n"
+ " %ld items\n"
+ " %d bytes\n",
+ textprop.encoding,
+ X11->xdndMimeAtomToString(textprop.encoding).toLatin1().data(),
+ textprop.format, textprop.nitems, data->size());
+
+ XFree(textprop.value);
+ }
+ }
+ } else if (atomName == QLatin1String("text/x-moz-url") &&
+ QInternalMimeData::hasFormatHelper(QLatin1String("text/uri-list"), mimeData)) {
+ QByteArray uri = QInternalMimeData::renderDataHelper(
+ QLatin1String("text/uri-list"), mimeData).split('\n').first();
+ QString mozUri = QString::fromLatin1(uri, uri.size());
+ mozUri += QLatin1Char('\n');
+ *data = QByteArray(reinterpret_cast<const char *>(mozUri.utf16()), mozUri.length() * 2);
+ ret = true;
+ } else if ((a == XA_PIXMAP || a == XA_BITMAP) && mimeData->hasImage()) {
+ QPixmap pm = qvariant_cast<QPixmap>(mimeData->imageData());
+ if (a == XA_BITMAP && pm.depth() != 1) {
+ QImage img = pm.toImage();
+ img = img.convertToFormat(QImage::Format_MonoLSB);
+ pm = QPixmap::fromImage(img);
+ }
+ QDragManager *dm = QDragManager::self();
+ if (dm) {
+ Pixmap handle = pm.handle();
+ *data = QByteArray((const char *) &handle, sizeof(Pixmap));
+ dm->xdndMimeTransferedPixmap[dm->xdndMimeTransferedPixmapIndex] = pm;
+ dm->xdndMimeTransferedPixmapIndex =
+ (dm->xdndMimeTransferedPixmapIndex + 1) % 2;
+ ret = true;
+ }
+ } else {
+ DEBUG("QClipboard: xdndMimeDataForAtom(): converting to type '%s' is not supported", qPrintable(atomName));
+ }
+ }
+ return ret && data != 0;
+}
+
+//$$$
+QList<Atom> QX11Data::xdndMimeAtomsForFormat(const QString &format)
+{
+ QList<Atom> atoms;
+ atoms.append(xdndMimeStringToAtom(format));
+
+ // special cases for strings
+ if (format == QLatin1String("text/plain")) {
+ atoms.append(ATOM(UTF8_STRING));
+ atoms.append(XA_STRING);
+ atoms.append(ATOM(TEXT));
+ atoms.append(ATOM(COMPOUND_TEXT));
+ }
+
+ // special cases for uris
+ if (format == QLatin1String("text/uri-list")) {
+ atoms.append(xdndMimeStringToAtom(QLatin1String("text/x-moz-url")));
+ }
+
+ //special cases for images
+ if (format == QLatin1String("image/ppm"))
+ atoms.append(XA_PIXMAP);
+ if (format == QLatin1String("image/pbm"))
+ atoms.append(XA_BITMAP);
+
+ return atoms;
+}
+
+//$$$
+QVariant QX11Data::xdndMimeConvertToFormat(Atom a, const QByteArray &data, const QString &format, QVariant::Type requestedType, const QByteArray &encoding)
+{
+ QString atomName = xdndMimeAtomToString(a);
+ if (atomName == format)
+ return data;
+
+ if (!encoding.isEmpty()
+ && atomName == format + QLatin1String(";charset=") + QString::fromLatin1(encoding)) {
+
+ if (requestedType == QVariant::String) {
+ QTextCodec *codec = QTextCodec::codecForName(encoding);
+ if (codec)
+ return codec->toUnicode(data);
+ }
+
+ return data;
+ }
+
+ // special cases for string types
+ if (format == QLatin1String("text/plain")) {
+ if (a == ATOM(UTF8_STRING))
+ return QString::fromUtf8(data);
+ if (a == XA_STRING)
+ return QString::fromLatin1(data);
+ if (a == ATOM(TEXT) || a == ATOM(COMPOUND_TEXT))
+ // #### might be wrong for COMPUND_TEXT
+ return QString::fromLocal8Bit(data, data.size());
+ }
+
+ // special case for uri types
+ if (format == QLatin1String("text/uri-list")) {
+ if (atomName == QLatin1String("text/x-moz-url")) {
+ // we expect this as utf16 <url><space><title>
+ // the first part is a url that should only contain ascci char
+ // so it should be safe to check that the second char is 0
+ // to verify that it is utf16
+ if (data.size() > 1 && data.at(1) == 0)
+ return QString::fromRawData((const QChar *)data.constData(),
+ data.size() / 2).split(QLatin1Char('\n')).first().toLatin1();
+ }
+ }
+
+ // special cas for images
+ if (format == QLatin1String("image/ppm")) {
+ if (a == XA_PIXMAP && data.size() == sizeof(Pixmap)) {
+ Pixmap xpm = *((Pixmap*)data.data());
+ if (!xpm)
+ return QByteArray();
+ QPixmap qpm = QPixmap::fromX11Pixmap(xpm);
+ QImageWriter imageWriter;
+ imageWriter.setFormat("PPMRAW");
+ QImage imageToWrite = qpm.toImage();
+ QBuffer buf;
+ buf.open(QIODevice::WriteOnly);
+ imageWriter.setDevice(&buf);
+ imageWriter.write(imageToWrite);
+ return buf.buffer();
+ }
+ }
+ return QVariant();
+}
+
+//$$$ middle of xdndObtainData
+Atom QX11Data::xdndMimeAtomForFormat(const QString &format, QVariant::Type requestedType, const QList<Atom> &atoms, QByteArray *encoding)
+{
+ encoding->clear();
+
+ // find matches for string types
+ if (format == QLatin1String("text/plain")) {
+ if (atoms.contains(ATOM(UTF8_STRING)))
+ return ATOM(UTF8_STRING);
+ if (atoms.contains(ATOM(COMPOUND_TEXT)))
+ return ATOM(COMPOUND_TEXT);
+ if (atoms.contains(ATOM(TEXT)))
+ return ATOM(TEXT);
+ if (atoms.contains(XA_STRING))
+ return XA_STRING;
+ }
+
+ // find matches for uri types
+ if (format == QLatin1String("text/uri-list")) {
+ Atom a = xdndMimeStringToAtom(format);
+ if (a && atoms.contains(a))
+ return a;
+ a = xdndMimeStringToAtom(QLatin1String("text/x-moz-url"));
+ if (a && atoms.contains(a))
+ return a;
+ }
+
+ // find match for image
+ if (format == QLatin1String("image/ppm")) {
+ if (atoms.contains(XA_PIXMAP))
+ return XA_PIXMAP;
+ }
+
+ // for string/text requests try to use a format with a well-defined charset
+ // first to avoid encoding problems
+ if (requestedType == QVariant::String
+ && format.startsWith(QLatin1String("text/"))
+ && !format.contains(QLatin1String("charset="))) {
+
+ QString formatWithCharset = format;
+ formatWithCharset.append(QLatin1String(";charset=utf-8"));
+
+ Atom a = xdndMimeStringToAtom(formatWithCharset);
+ if (a && atoms.contains(a)) {
+ *encoding = "utf-8";
+ return a;
+ }
+ }
+
+ Atom a = xdndMimeStringToAtom(format);
+ if (a && atoms.contains(a))
+ return a;
+
+ return 0;
+}
+
+void QX11Data::xdndSetup() {
+ QCursorData::initialize();
+ qAddPostRoutine(qt_xdnd_cleanup);
+}
+
+
+void qt_xdnd_cleanup()
+{
+ delete noDropCursor;
+ noDropCursor = 0;
+ delete copyCursor;
+ copyCursor = 0;
+ delete moveCursor;
+ moveCursor = 0;
+ delete linkCursor;
+ linkCursor = 0;
+ delete defaultPm;
+ defaultPm = 0;
+ delete xdnd_data.desktop_proxy;
+ xdnd_data.desktop_proxy = 0;
+ delete xdnd_data.deco;
+ xdnd_data.deco = 0;
+}
+
+
+static QWidget *find_child(QWidget *tlw, QPoint & p)
+{
+ QWidget *widget = tlw;
+
+ p = widget->mapFromGlobal(p);
+ bool done = false;
+ while (!done) {
+ done = true;
+ if (((QExtraWidget*)widget)->extraData() &&
+ ((QExtraWidget*)widget)->extraData()->xDndProxy != 0)
+ break; // stop searching for widgets under the mouse cursor if found widget is a proxy.
+ QObjectList children = widget->children();
+ if (!children.isEmpty()) {
+ for(int i = children.size(); i > 0;) {
+ --i;
+ QWidget *w = qobject_cast<QWidget *>(children.at(i));
+ if (!w)
+ continue;
+ if (w->testAttribute(Qt::WA_TransparentForMouseEvents))
+ continue;
+ if (w->isVisible() &&
+ w->geometry().contains(p) &&
+ !w->isWindow()) {
+ widget = w;
+ done = false;
+ p = widget->mapFromParent(p);
+ break;
+ }
+ }
+ }
+ }
+ return widget;
+}
+
+
+static bool checkEmbedded(QWidget* w, const XEvent* xe)
+{
+ if (!w)
+ return false;
+
+ if (current_embedding_widget != 0 && current_embedding_widget != w) {
+ qt_xdnd_current_target = ((QExtraWidget*)current_embedding_widget)->extraData()->xDndProxy;
+ qt_xdnd_current_proxy_target = qt_xdnd_current_target;
+ qt_xdnd_send_leave();
+ qt_xdnd_current_target = 0;
+ qt_xdnd_current_proxy_target = 0;
+ current_embedding_widget = 0;
+ }
+
+ QWExtra* extra = ((QExtraWidget*)w)->extraData();
+ if (extra && extra->xDndProxy != 0) {
+
+ if (current_embedding_widget != w) {
+
+ last_enter_event.xany.window = extra->xDndProxy;
+ XSendEvent(X11->display, extra->xDndProxy, False, NoEventMask, &last_enter_event);
+ current_embedding_widget = w;
+ }
+
+ ((XEvent*)xe)->xany.window = extra->xDndProxy;
+ XSendEvent(X11->display, extra->xDndProxy, False, NoEventMask, (XEvent*)xe);
+ if (qt_xdnd_current_widget != w) {
+ qt_xdnd_current_widget = w;
+ }
+ return true;
+ }
+ current_embedding_widget = 0;
+ return false;
+}
+
+void QX11Data::xdndHandleEnter(QWidget *, const XEvent * xe, bool /*passive*/)
+{
+ motifdnd_active = false;
+
+ last_enter_event.xclient = xe->xclient;
+
+ const long *l = xe->xclient.data.l;
+ int version = (int)(((unsigned long)(l[1])) >> 24);
+
+ if (version > xdnd_version)
+ return;
+
+ qt_xdnd_dragsource_xid = l[0];
+
+ int j = 0;
+ if (l[1] & 1) {
+ // get the types from XdndTypeList
+ Atom type = XNone;
+ int f;
+ unsigned long n, a;
+ unsigned char *retval = 0;
+ XGetWindowProperty(X11->display, qt_xdnd_dragsource_xid, ATOM(XdndTypelist), 0,
+ qt_xdnd_max_type, False, XA_ATOM, &type, &f,&n,&a,&retval);
+ if (retval) {
+ Atom *data = (Atom *)retval;
+ for (; j<qt_xdnd_max_type && j < (int)n; j++) {
+ qt_xdnd_types[j] = data[j];
+ }
+ XFree((uchar*)data);
+ }
+ } else {
+ // get the types from the message
+ int i;
+ for(i=2; i < 5; i++) {
+ qt_xdnd_types[j++] = l[i];
+ }
+ }
+ qt_xdnd_types[j] = 0;
+}
+
+static void handle_xdnd_position(QWidget *w, const XEvent * xe, bool passive)
+{
+ const unsigned long *l = (const unsigned long *)xe->xclient.data.l;
+
+ QPoint p((l[2] & 0xffff0000) >> 16, l[2] & 0x0000ffff);
+ QWidget * c = find_child(w, p); // changes p to to c-local coordinates
+
+ if (!passive && checkEmbedded(c, xe))
+ return;
+
+ if (!c || (!c->acceptDrops() && (c->windowType() == Qt::Desktop)))
+ return;
+
+ if (l[0] != qt_xdnd_dragsource_xid) {
+ DEBUG("xdnd drag position from unexpected source (%08lx not %08lx)", l[0], qt_xdnd_dragsource_xid);
+ return;
+ }
+
+ // timestamp from the source
+ if (l[3] != 0) {
+ // Some X server/client combination swallow the first 32 bit and
+ // interpret a set bit 31 as negative sign.
+ qt_xdnd_target_current_time = X11->userTime =
+ ((sizeof(Time) == 8 && xe->xclient.data.l[3] < 0)
+ ? uint(l[3])
+ : l[3]);
+ }
+
+ QDragManager *manager = QDragManager::self();
+ QMimeData *dropData = manager->object ? manager->dragPrivate()->data : manager->dropData;
+
+ XClientMessageEvent response;
+ response.type = ClientMessage;
+ response.window = qt_xdnd_dragsource_xid;
+ response.format = 32;
+ response.message_type = ATOM(XdndStatus);
+ response.data.l[0] = w->effectiveWinId();
+ response.data.l[1] = 0; // flags
+ response.data.l[2] = 0; // x, y
+ response.data.l[3] = 0; // w, h
+ response.data.l[4] = 0; // action
+
+ if (!passive) { // otherwise just reject
+ while (c && !c->acceptDrops() && !c->isWindow()) {
+ p = c->mapToParent(p);
+ c = c->parentWidget();
+ }
+ QWidget *target_widget = c && c->acceptDrops() ? c : 0;
+
+ QRect answerRect(c->mapToGlobal(p), QSize(1,1));
+
+ if (manager->object) {
+ possible_actions = manager->dragPrivate()->possible_actions;
+ } else {
+ possible_actions = Qt::DropActions(xdndaction_to_qtaction(l[4]));
+// possible_actions |= Qt::CopyAction;
+ }
+ QDragMoveEvent me(p, possible_actions, dropData, QApplication::mouseButtons(), QApplication::keyboardModifiers());
+
+ Qt::DropAction accepted_action = Qt::IgnoreAction;
+
+
+ if (target_widget != qt_xdnd_current_widget) {
+ if (qt_xdnd_current_widget) {
+ QDragLeaveEvent e;
+ QApplication::sendEvent(qt_xdnd_current_widget, &e);
+ }
+ if (qt_xdnd_current_widget != target_widget) {
+ qt_xdnd_current_widget = target_widget;
+ }
+ if (target_widget) {
+ qt_xdnd_current_position = p;
+
+ last_target_accepted_action = Qt::IgnoreAction;
+ QDragEnterEvent de(p, possible_actions, dropData, QApplication::mouseButtons(), QApplication::keyboardModifiers());
+ QApplication::sendEvent(target_widget, &de);
+ if (de.isAccepted() && de.dropAction() != Qt::IgnoreAction)
+ last_target_accepted_action = de.dropAction();
+ }
+ }
+
+ DEBUG() << "qt_handle_xdnd_position action=" << X11->xdndAtomToString(l[4]);
+ if (!target_widget) {
+ answerRect = QRect(p, QSize(1, 1));
+ } else {
+ qt_xdnd_current_widget = c;
+ qt_xdnd_current_position = p;
+
+ if (last_target_accepted_action != Qt::IgnoreAction) {
+ me.setDropAction(last_target_accepted_action);
+ me.accept();
+ }
+ QApplication::sendEvent(c, &me);
+ if (me.isAccepted()) {
+ response.data.l[1] = 1; // yes
+ accepted_action = me.dropAction();
+ last_target_accepted_action = accepted_action;
+ } else {
+ response.data.l[0] = 0;
+ last_target_accepted_action = Qt::IgnoreAction;
+ }
+ answerRect = me.answerRect().intersected(c->rect());
+ }
+ answerRect = QRect(c->mapToGlobal(answerRect.topLeft()), answerRect.size());
+
+ if (answerRect.left() < 0)
+ answerRect.setLeft(0);
+ if (answerRect.right() > 4096)
+ answerRect.setRight(4096);
+ if (answerRect.top() < 0)
+ answerRect.setTop(0);
+ if (answerRect.bottom() > 4096)
+ answerRect.setBottom(4096);
+ if (answerRect.width() < 0)
+ answerRect.setWidth(0);
+ if (answerRect.height() < 0)
+ answerRect.setHeight(0);
+
+ response.data.l[2] = (answerRect.x() << 16) + answerRect.y();
+ response.data.l[3] = (answerRect.width() << 16) + answerRect.height();
+ response.data.l[4] = qtaction_to_xdndaction(accepted_action);
+ }
+
+ // reset
+ qt_xdnd_target_current_time = CurrentTime;
+
+ QWidget * source = QWidget::find(qt_xdnd_dragsource_xid);
+ if (source && (source->windowType() == Qt::Desktop) && !source->acceptDrops())
+ source = 0;
+
+ DEBUG() << "sending XdndStatus";
+ if (source)
+ handle_xdnd_status(source, (const XEvent *)&response, passive);
+ else
+ XSendEvent(X11->display, qt_xdnd_dragsource_xid, False, NoEventMask, (XEvent*)&response);
+}
+
+static Bool xdnd_position_scanner(Display *, XEvent *event, XPointer)
+{
+ if (event->type != ClientMessage)
+ return false;
+ XClientMessageEvent *ev = &event->xclient;
+
+ if (ev->message_type == ATOM(XdndPosition))
+ return true;
+
+ return false;
+}
+
+void QX11Data::xdndHandlePosition(QWidget * w, const XEvent * xe, bool passive)
+{
+ DEBUG("xdndHandlePosition");
+ while (XCheckIfEvent(X11->display, (XEvent *)xe, xdnd_position_scanner, 0))
+ ;
+
+ handle_xdnd_position(w, xe, passive);
+}
+
+
+static void handle_xdnd_status(QWidget *, const XEvent * xe, bool)
+{
+ const unsigned long *l = (const unsigned long *)xe->xclient.data.l;
+ // ignore late status messages
+ if (l[0] && l[0] != qt_xdnd_current_proxy_target)
+ return;
+ Qt::DropAction newAction = (l[1] & 0x1) ? xdndaction_to_qtaction(l[4]) : Qt::IgnoreAction;
+
+ if ((int)(l[1] & 2) == 0) {
+ QPoint p((l[2] & 0xffff0000) >> 16, l[2] & 0x0000ffff);
+ QSize s((l[3] & 0xffff0000) >> 16, l[3] & 0x0000ffff);
+ qt_xdnd_source_sameanswer = QRect(p, s);
+ } else {
+ qt_xdnd_source_sameanswer = QRect();
+ }
+ QDragManager *manager = QDragManager::self();
+ manager->willDrop = (l[1] & 0x1);
+ if (global_accepted_action != newAction)
+ manager->emitActionChanged(newAction);
+ global_accepted_action = newAction;
+ manager->updateCursor();
+ waiting_for_status = false;
+}
+
+static Bool xdnd_status_scanner(Display *, XEvent *event, XPointer)
+{
+ if (event->type != ClientMessage)
+ return false;
+ XClientMessageEvent *ev = &event->xclient;
+
+ if (ev->message_type == ATOM(XdndStatus))
+ return true;
+
+ return false;
+}
+
+void QX11Data::xdndHandleStatus(QWidget * w, const XEvent * xe, bool passive)
+{
+ DEBUG("xdndHandleStatus");
+ while (XCheckIfEvent(X11->display, (XEvent *)xe, xdnd_status_scanner, 0))
+ ;
+
+ handle_xdnd_status(w, xe, passive);
+ DEBUG("xdndHandleStatus end");
+}
+
+void QX11Data::xdndHandleLeave(QWidget *w, const XEvent * xe, bool /*passive*/)
+{
+ DEBUG("xdnd leave");
+ if (!qt_xdnd_current_widget ||
+ w->window() != qt_xdnd_current_widget->window()) {
+ return; // sanity
+ }
+
+ if (checkEmbedded(current_embedding_widget, xe)) {
+ current_embedding_widget = 0;
+ qt_xdnd_current_widget = 0;
+ return;
+ }
+
+ const unsigned long *l = (const unsigned long *)xe->xclient.data.l;
+
+ QDragLeaveEvent e;
+ QApplication::sendEvent(qt_xdnd_current_widget, &e);
+
+ if (l[0] != qt_xdnd_dragsource_xid) {
+ // This often happens - leave other-process window quickly
+ DEBUG("xdnd drag leave from unexpected source (%08lx not %08lx", l[0], qt_xdnd_dragsource_xid);
+ qt_xdnd_current_widget = 0;
+ return;
+ }
+
+ qt_xdnd_dragsource_xid = 0;
+ qt_xdnd_types[0] = 0;
+ qt_xdnd_current_widget = 0;
+}
+
+
+void qt_xdnd_send_leave()
+{
+ if (!qt_xdnd_current_target)
+ return;
+
+ QDragManager *manager = QDragManager::self();
+
+ XClientMessageEvent leave;
+ leave.type = ClientMessage;
+ leave.window = qt_xdnd_current_target;
+ leave.format = 32;
+ leave.message_type = ATOM(XdndLeave);
+ leave.data.l[0] = manager->dragPrivate()->source->effectiveWinId();
+ leave.data.l[1] = 0; // flags
+ leave.data.l[2] = 0; // x, y
+ leave.data.l[3] = 0; // w, h
+ leave.data.l[4] = 0; // just null
+
+ QWidget * w = QWidget::find(qt_xdnd_current_proxy_target);
+
+ if (w && (w->windowType() == Qt::Desktop) && !w->acceptDrops())
+ w = 0;
+
+ if (w)
+ X11->xdndHandleLeave(w, (const XEvent *)&leave, false);
+ else
+ XSendEvent(X11->display, qt_xdnd_current_proxy_target, False,
+ NoEventMask, (XEvent*)&leave);
+
+ // reset the drag manager state
+ manager->willDrop = false;
+ if (global_accepted_action != Qt::IgnoreAction)
+ manager->emitActionChanged(Qt::IgnoreAction);
+ global_accepted_action = Qt::IgnoreAction;
+ manager->updateCursor();
+ qt_xdnd_current_target = 0;
+ qt_xdnd_current_proxy_target = 0;
+ qt_xdnd_source_current_time = 0;
+ waiting_for_status = false;
+}
+
+// TODO: remove and use QApplication::currentKeyboardModifiers() in Qt 4.8.
+static Qt::KeyboardModifiers currentKeyboardModifiers()
+{
+ Window root;
+ Window child;
+ int root_x, root_y, win_x, win_y;
+ uint keybstate;
+ for (int i = 0; i < ScreenCount(X11->display); ++i) {
+ if (XQueryPointer(X11->display, QX11Info::appRootWindow(i), &root, &child,
+ &root_x, &root_y, &win_x, &win_y, &keybstate))
+ return X11->translateModifiers(keybstate & 0x00ff);
+ }
+ return 0;
+}
+
+void QX11Data::xdndHandleDrop(QWidget *, const XEvent * xe, bool passive)
+{
+ DEBUG("xdndHandleDrop");
+ if (!qt_xdnd_current_widget) {
+ qt_xdnd_dragsource_xid = 0;
+ return; // sanity
+ }
+
+ if (!passive && checkEmbedded(qt_xdnd_current_widget, xe)){
+ current_embedding_widget = 0;
+ qt_xdnd_dragsource_xid = 0;
+ qt_xdnd_current_widget = 0;
+ return;
+ }
+ const unsigned long *l = (const unsigned long *)xe->xclient.data.l;
+
+ QDragManager *manager = QDragManager::self();
+ DEBUG("xdnd drop");
+
+ if (l[0] != qt_xdnd_dragsource_xid) {
+ DEBUG("xdnd drop from unexpected source (%08lx not %08lx", l[0], qt_xdnd_dragsource_xid);
+ return;
+ }
+
+ // update the "user time" from the timestamp in the event.
+ if (l[2] != 0) {
+ // Some X server/client combination swallow the first 32 bit and
+ // interpret a set bit 31 as negative sign.
+ qt_xdnd_target_current_time = X11->userTime =
+ ((sizeof(Time) == 8 && xe->xclient.data.l[2] < 0)
+ ? uint(l[2])
+ : l[2]);
+ }
+
+ if (!passive) {
+ // this could be a same-application drop, just proxied due to
+ // some XEMBEDding, so try to find the real QMimeData used
+ // based on the timestamp for this drop.
+ QMimeData *dropData = 0;
+ int at = findXdndDropTransactionByTime(qt_xdnd_target_current_time);
+ if (at != -1)
+ dropData = QDragManager::dragPrivate(X11->dndDropTransactions.at(at).object)->data;
+ // if we can't find it, then use the data in the drag manager
+ if (!dropData)
+ dropData = (manager->object) ? manager->dragPrivate()->data : manager->dropData;
+
+ // Drop coming from another app? Update keyboard modifiers.
+ if (!qt_xdnd_dragging) {
+ QApplicationPrivate::modifier_buttons = currentKeyboardModifiers();
+ }
+
+ QDropEvent de(qt_xdnd_current_position, possible_actions, dropData,
+ QApplication::mouseButtons(), QApplication::keyboardModifiers());
+ QApplication::sendEvent(qt_xdnd_current_widget, &de);
+ if (!de.isAccepted()) {
+ // Ignore a failed drag
+ global_accepted_action = Qt::IgnoreAction;
+ } else {
+ global_accepted_action = de.dropAction();
+ }
+ XClientMessageEvent finished;
+ finished.type = ClientMessage;
+ finished.window = qt_xdnd_dragsource_xid;
+ finished.format = 32;
+ finished.message_type = ATOM(XdndFinished);
+ DNDDEBUG << "xdndHandleDrop"
+ << "qt_xdnd_current_widget" << qt_xdnd_current_widget
+ << (qt_xdnd_current_widget ? qt_xdnd_current_widget->effectiveWinId() : 0)
+ << "t_xdnd_current_widget->window()"
+ << (qt_xdnd_current_widget ? qt_xdnd_current_widget->window() : 0)
+ << (qt_xdnd_current_widget ? qt_xdnd_current_widget->window()->internalWinId() : 0);
+ finished.data.l[0] = qt_xdnd_current_widget?qt_xdnd_current_widget->window()->internalWinId():0;
+ finished.data.l[1] = de.isAccepted() ? 1 : 0; // flags
+ finished.data.l[2] = qtaction_to_xdndaction(global_accepted_action);
+ XSendEvent(X11->display, qt_xdnd_dragsource_xid, False,
+ NoEventMask, (XEvent*)&finished);
+ } else {
+ QDragLeaveEvent e;
+ QApplication::sendEvent(qt_xdnd_current_widget, &e);
+ }
+ qt_xdnd_dragsource_xid = 0;
+ qt_xdnd_current_widget = 0;
+ waiting_for_status = false;
+
+ // reset
+ qt_xdnd_target_current_time = CurrentTime;
+}
+
+
+void QX11Data::xdndHandleFinished(QWidget *, const XEvent * xe, bool passive)
+{
+ DEBUG("xdndHandleFinished");
+ const unsigned long *l = (const unsigned long *)xe->xclient.data.l;
+
+ DNDDEBUG << "xdndHandleFinished, l[0]" << l[0]
+ << "qt_xdnd_current_target" << qt_xdnd_current_target
+ << "qt_xdnd_current_proxy_targe" << qt_xdnd_current_proxy_target;
+
+ if (l[0]) {
+ int at = findXdndDropTransactionByWindow(l[0]);
+ if (at != -1) {
+ restartXdndDropExpiryTimer();
+
+ QXdndDropTransaction t = X11->dndDropTransactions.takeAt(at);
+ QDragManager *manager = QDragManager::self();
+
+ Window target = qt_xdnd_current_target;
+ Window proxy_target = qt_xdnd_current_proxy_target;
+ QWidget *embedding_widget = current_embedding_widget;
+ QDrag *currentObject = manager->object;
+
+ qt_xdnd_current_target = t.target;
+ qt_xdnd_current_proxy_target = t.proxy_target;
+ current_embedding_widget = t.embedding_widget;
+ manager->object = t.object;
+
+ if (!passive)
+ (void) checkEmbedded(qt_xdnd_current_widget, xe);
+
+ current_embedding_widget = 0;
+ qt_xdnd_current_target = 0;
+ qt_xdnd_current_proxy_target = 0;
+
+ if (t.object)
+ t.object->deleteLater();
+
+ qt_xdnd_current_target = target;
+ qt_xdnd_current_proxy_target = proxy_target;
+ current_embedding_widget = embedding_widget;
+ manager->object = currentObject;
+ }
+ }
+ waiting_for_status = false;
+}
+
+
+void QDragManager::timerEvent(QTimerEvent* e)
+{
+ if (e->timerId() == heartbeat && qt_xdnd_source_sameanswer.isNull()) {
+ move(QCursor::pos());
+ } else if (e->timerId() == transaction_expiry_timer) {
+ for (int i = 0; i < X11->dndDropTransactions.count(); ++i) {
+ const QXdndDropTransaction &t = X11->dndDropTransactions.at(i);
+ if (t.targetWidget) {
+ // dnd within the same process, don't delete these
+ continue;
+ }
+ t.object->deleteLater();
+ X11->dndDropTransactions.removeAt(i--);
+ }
+
+ killTimer(transaction_expiry_timer);
+ transaction_expiry_timer = -1;
+ }
+}
+
+bool QDragManager::eventFilter(QObject * o, QEvent * e)
+{
+ if (beingCancelled) {
+ if (e->type() == QEvent::KeyRelease && ((QKeyEvent*)e)->key() == Qt::Key_Escape) {
+ qApp->removeEventFilter(this);
+ Q_ASSERT(object == 0);
+ beingCancelled = false;
+ eventLoop->exit();
+ return true; // block the key release
+ }
+ return false;
+ }
+
+ Q_ASSERT(object != 0);
+
+ if (!o->isWidgetType())
+ return false;
+
+ if (e->type() == QEvent::MouseMove) {
+ QMouseEvent* me = (QMouseEvent *)e;
+ move(me->globalPos());
+ return true;
+ } else if (e->type() == QEvent::MouseButtonRelease) {
+ DEBUG("pre drop");
+ qApp->removeEventFilter(this);
+ if (willDrop)
+ drop();
+ else
+ cancel();
+ DEBUG("drop, resetting object");
+ beingCancelled = false;
+ eventLoop->exit();
+ return true;
+ }
+
+ if (e->type() == QEvent::ShortcutOverride) {
+ // prevent accelerators from firing while dragging
+ e->accept();
+ return true;
+ }
+
+ if (e->type() == QEvent::KeyPress || e->type() == QEvent::KeyRelease) {
+ QKeyEvent *ke = ((QKeyEvent*)e);
+ if (ke->key() == Qt::Key_Escape && e->type() == QEvent::KeyPress) {
+ cancel();
+ qApp->removeEventFilter(this);
+ beingCancelled = false;
+ eventLoop->exit();
+ } else {
+ qt_xdnd_source_sameanswer = QRect(); // force move
+ move(QCursor::pos());
+ }
+ return true; // Eat all key events
+ }
+
+ // ### We bind modality to widgets, so we have to do this
+ // ### "manually".
+ // DnD is modal - eat all other interactive events
+ switch (e->type()) {
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseButtonDblClick:
+ case QEvent::MouseMove:
+ case QEvent::KeyPress:
+ case QEvent::KeyRelease:
+ case QEvent::Wheel:
+ case QEvent::ShortcutOverride:
+ return true;
+ default:
+ return false;
+ }
+}
+
+void QDragManager::updateCursor()
+{
+ if (!noDropCursor) {
+#ifndef QT_NO_CURSOR
+ noDropCursor = new QCursor(Qt::ForbiddenCursor);
+ moveCursor = new QCursor(Qt::DragMoveCursor);
+ copyCursor = new QCursor(Qt::DragCopyCursor);
+ linkCursor = new QCursor(Qt::DragLinkCursor);
+#endif
+ }
+
+ QCursor *c;
+ if (willDrop) {
+ if (global_accepted_action == Qt::CopyAction) {
+ c = copyCursor;
+ } else if (global_accepted_action == Qt::LinkAction) {
+ c = linkCursor;
+ } else {
+ c = moveCursor;
+ }
+ if (xdnd_data.deco) {
+ xdnd_data.deco->show();
+ xdnd_data.deco->raise();
+ }
+ } else {
+ c = noDropCursor;
+ //if (qt_xdnd_deco)
+ // qt_xdnd_deco->hide();
+ }
+#ifndef QT_NO_CURSOR
+ if (c)
+ qApp->changeOverrideCursor(*c);
+#endif
+}
+
+
+void QDragManager::cancel(bool deleteSource)
+{
+ DEBUG("QDragManager::cancel");
+ Q_ASSERT(heartbeat != -1);
+ killTimer(heartbeat);
+ heartbeat = -1;
+ beingCancelled = true;
+ qt_xdnd_dragging = false;
+
+ if (qt_xdnd_current_target)
+ qt_xdnd_send_leave();
+
+#ifndef QT_NO_CURSOR
+ if (restoreCursor) {
+ QApplication::restoreOverrideCursor();
+ restoreCursor = false;
+ }
+#endif
+
+ if (deleteSource && object)
+ object->deleteLater();
+ object = 0;
+ qDeleteInEventHandler(xdnd_data.deco);
+ xdnd_data.deco = 0;
+
+ global_accepted_action = Qt::IgnoreAction;
+}
+
+static
+Window findRealWindow(const QPoint & pos, Window w, int md)
+{
+ if (xdnd_data.deco && w == xdnd_data.deco->effectiveWinId())
+ return 0;
+
+ if (md) {
+ X11->ignoreBadwindow();
+ XWindowAttributes attr;
+ XGetWindowAttributes(X11->display, w, &attr);
+ if (X11->badwindow())
+ return 0;
+
+ if (attr.map_state == IsViewable
+ && QRect(attr.x,attr.y,attr.width,attr.height).contains(pos)) {
+ {
+ Atom type = XNone;
+ int f;
+ unsigned long n, a;
+ unsigned char *data;
+
+ XGetWindowProperty(X11->display, w, ATOM(XdndAware), 0, 0, False,
+ AnyPropertyType, &type, &f,&n,&a,&data);
+ if (data) XFree(data);
+ if (type)
+ return w;
+ }
+
+ Window r, p;
+ Window* c;
+ uint nc;
+ if (XQueryTree(X11->display, w, &r, &p, &c, &nc)) {
+ r=0;
+ for (uint i=nc; !r && i--;) {
+ r = findRealWindow(pos-QPoint(attr.x,attr.y),
+ c[i], md-1);
+ }
+ XFree(c);
+ if (r)
+ return r;
+
+ // We didn't find a client window! Just use the
+ // innermost window.
+ }
+
+ // No children!
+ return w;
+ }
+ }
+ return 0;
+}
+
+void QDragManager::move(const QPoint & globalPos)
+{
+#ifdef QT_NO_CURSOR
+ Q_UNUSED(globalPos);
+ return;
+#else
+ DEBUG() << "QDragManager::move enter";
+ if (!object) {
+ // perhaps the target crashed?
+ return;
+ }
+
+ int screen = QCursor::x11Screen();
+ if ((qt_xdnd_current_screen == -1 && screen != X11->defaultScreen) || (screen != qt_xdnd_current_screen)) {
+ // recreate the pixmap on the new screen...
+ delete xdnd_data.deco;
+ QWidget* parent = object->source()->window()->x11Info().screen() == screen
+ ? object->source()->window() : QApplication::desktop()->screen(screen);
+ xdnd_data.deco = new QShapedPixmapWidget(parent);
+ if (!QWidget::mouseGrabber()) {
+ updatePixmap();
+ xdnd_data.deco->grabMouse();
+ }
+ }
+ xdnd_data.deco->move(QCursor::pos() - xdnd_data.deco->pm_hot);
+
+ if (qt_xdnd_source_sameanswer.contains(globalPos) && qt_xdnd_source_sameanswer.isValid())
+ return;
+
+ qt_xdnd_current_screen = screen;
+ Window rootwin = QX11Info::appRootWindow(qt_xdnd_current_screen);
+ Window target = 0;
+ int lx = 0, ly = 0;
+ if (!XTranslateCoordinates(X11->display, rootwin, rootwin, globalPos.x(), globalPos.y(), &lx, &ly, &target))
+ // some weird error...
+ return;
+
+ if (target == rootwin) {
+ // Ok.
+ } else if (target) {
+ //me
+ Window src = rootwin;
+ while (target != 0) {
+ DNDDEBUG << "checking target for XdndAware" << QWidget::find(target) << target;
+ int lx2, ly2;
+ Window t;
+ // translate coordinates
+ if (!XTranslateCoordinates(X11->display, src, target, lx, ly, &lx2, &ly2, &t)) {
+ target = 0;
+ break;
+ }
+ lx = lx2;
+ ly = ly2;
+ src = target;
+
+ // check if it has XdndAware
+ Atom type = 0;
+ int f;
+ unsigned long n, a;
+ unsigned char *data = 0;
+ XGetWindowProperty(X11->display, target, ATOM(XdndAware), 0, 0, False,
+ AnyPropertyType, &type, &f,&n,&a,&data);
+ if (data)
+ XFree(data);
+ if (type) {
+ DNDDEBUG << "Found XdndAware on " << QWidget::find(target) << target;
+ break;
+ }
+
+ // find child at the coordinates
+ if (!XTranslateCoordinates(X11->display, src, src, lx, ly, &lx2, &ly2, &target)) {
+ target = 0;
+ break;
+ }
+ }
+ if (xdnd_data.deco && (!target || target == xdnd_data.deco->effectiveWinId())) {
+ DNDDEBUG << "need to find real window";
+ target = findRealWindow(globalPos, rootwin, 6);
+ DNDDEBUG << "real window found" << QWidget::find(target) << target;
+ }
+ }
+
+ QWidget* w;
+ if (target) {
+ w = QWidget::find((WId)target);
+ if (w && (w->windowType() == Qt::Desktop) && !w->acceptDrops())
+ w = 0;
+ } else {
+ w = 0;
+ target = rootwin;
+ }
+
+ DNDDEBUG << "and the final target is " << QWidget::find(target) << target;
+ DNDDEBUG << "the widget w is" << w;
+
+ WId proxy_target = xdndProxy(target);
+ if (!proxy_target)
+ proxy_target = target;
+ int target_version = 1;
+
+ if (proxy_target) {
+ Atom type = XNone;
+ int r, f;
+ unsigned long n, a;
+ unsigned char *retval;
+ X11->ignoreBadwindow();
+ r = XGetWindowProperty(X11->display, proxy_target, ATOM(XdndAware), 0,
+ 1, False, AnyPropertyType, &type, &f,&n,&a,&retval);
+ int *tv = (int *)retval;
+ if (r != Success || X11->badwindow()) {
+ target = 0;
+ } else {
+ target_version = qMin(xdnd_version,tv ? *tv : 1);
+ if (tv)
+ XFree(tv);
+// if (!(!X11->badwindow() && type))
+// target = 0;
+ }
+ }
+
+ if (target != qt_xdnd_current_target) {
+ if (qt_xdnd_current_target)
+ qt_xdnd_send_leave();
+
+ qt_xdnd_current_target = target;
+ qt_xdnd_current_proxy_target = proxy_target;
+ if (target) {
+ QVector<Atom> types;
+ int flags = target_version << 24;
+ QStringList fmts = QInternalMimeData::formatsHelper(dragPrivate()->data);
+ for (int i = 0; i < fmts.size(); ++i) {
+ QList<Atom> atoms = X11->xdndMimeAtomsForFormat(fmts.at(i));
+ for (int j = 0; j < atoms.size(); ++j) {
+ if (!types.contains(atoms.at(j)))
+ types.append(atoms.at(j));
+ }
+ }
+ if (types.size() > 3) {
+ XChangeProperty(X11->display,
+ dragPrivate()->source->effectiveWinId(), ATOM(XdndTypelist),
+ XA_ATOM, 32, PropModeReplace,
+ (unsigned char *)types.data(),
+ types.size());
+ flags |= 0x0001;
+ }
+ XClientMessageEvent enter;
+ enter.type = ClientMessage;
+ enter.window = target;
+ enter.format = 32;
+ enter.message_type = ATOM(XdndEnter);
+ enter.data.l[0] = dragPrivate()->source->effectiveWinId();
+ enter.data.l[1] = flags;
+ enter.data.l[2] = types.size()>0 ? types.at(0) : 0;
+ enter.data.l[3] = types.size()>1 ? types.at(1) : 0;
+ enter.data.l[4] = types.size()>2 ? types.at(2) : 0;
+ // provisionally set the rectangle to 5x5 pixels...
+ qt_xdnd_source_sameanswer = QRect(globalPos.x() - 2,
+ globalPos.y() -2 , 5, 5);
+
+ DEBUG("sending Xdnd enter");
+ if (w)
+ X11->xdndHandleEnter(w, (const XEvent *)&enter, false);
+ else if (target)
+ XSendEvent(X11->display, proxy_target, False, NoEventMask, (XEvent*)&enter);
+ waiting_for_status = false;
+ }
+ }
+ if (waiting_for_status)
+ return;
+
+ if (target) {
+ waiting_for_status = true;
+
+ XClientMessageEvent move;
+ move.type = ClientMessage;
+ move.window = target;
+ move.format = 32;
+ move.message_type = ATOM(XdndPosition);
+ move.window = target;
+ move.data.l[0] = dragPrivate()->source->effectiveWinId();
+ move.data.l[1] = 0; // flags
+ move.data.l[2] = (globalPos.x() << 16) + globalPos.y();
+ move.data.l[3] = X11->time;
+ move.data.l[4] = qtaction_to_xdndaction(defaultAction(dragPrivate()->possible_actions, QApplication::keyboardModifiers()));
+ DEBUG("sending Xdnd position");
+
+ qt_xdnd_source_current_time = X11->time;
+
+ if (w)
+ handle_xdnd_position(w, (const XEvent *)&move, false);
+ else
+ XSendEvent(X11->display, proxy_target, False, NoEventMask,
+ (XEvent*)&move);
+ } else {
+ if (willDrop) {
+ willDrop = false;
+ updateCursor();
+ }
+ }
+ DEBUG() << "QDragManager::move leave";
+#endif
+}
+
+
+void QDragManager::drop()
+{
+ Q_ASSERT(heartbeat != -1);
+ killTimer(heartbeat);
+ heartbeat = -1;
+ qt_xdnd_dragging = false;
+
+ if (!qt_xdnd_current_target)
+ return;
+
+ qDeleteInEventHandler(xdnd_data.deco);
+ xdnd_data.deco = 0;
+
+ XClientMessageEvent drop;
+ drop.type = ClientMessage;
+ drop.window = qt_xdnd_current_target;
+ drop.format = 32;
+ drop.message_type = ATOM(XdndDrop);
+ drop.data.l[0] = dragPrivate()->source->effectiveWinId();
+ drop.data.l[1] = 0; // flags
+ drop.data.l[2] = X11->time;
+
+ drop.data.l[3] = 0;
+ drop.data.l[4] = 0;
+
+ QWidget * w = QWidget::find(qt_xdnd_current_proxy_target);
+
+ if (w && (w->windowType() == Qt::Desktop) && !w->acceptDrops())
+ w = 0;
+
+ QXdndDropTransaction t = {
+ X11->time,
+ qt_xdnd_current_target,
+ qt_xdnd_current_proxy_target,
+ w,
+ current_embedding_widget,
+ object
+ };
+ X11->dndDropTransactions.append(t);
+ restartXdndDropExpiryTimer();
+
+ if (w)
+ X11->xdndHandleDrop(w, (const XEvent *)&drop, false);
+ else
+ XSendEvent(X11->display, qt_xdnd_current_proxy_target, False,
+ NoEventMask, (XEvent*)&drop);
+
+ qt_xdnd_current_target = 0;
+ qt_xdnd_current_proxy_target = 0;
+ qt_xdnd_source_current_time = 0;
+ current_embedding_widget = 0;
+ object = 0;
+
+#ifndef QT_NO_CURSOR
+ if (restoreCursor) {
+ QApplication::restoreOverrideCursor();
+ restoreCursor = false;
+ }
+#endif
+}
+
+
+
+bool QX11Data::xdndHandleBadwindow()
+{
+ if (qt_xdnd_current_target) {
+ QDragManager *manager = QDragManager::self();
+ if (manager->object) {
+ qt_xdnd_current_target = 0;
+ qt_xdnd_current_proxy_target = 0;
+ manager->object->deleteLater();
+ manager->object = 0;
+ delete xdnd_data.deco;
+ xdnd_data.deco = 0;
+ return true;
+ }
+ }
+ if (qt_xdnd_dragsource_xid) {
+ qt_xdnd_dragsource_xid = 0;
+ if (qt_xdnd_current_widget) {
+ QApplication::postEvent(qt_xdnd_current_widget, new QDragLeaveEvent);
+ qt_xdnd_current_widget = 0;
+ }
+ return true;
+ }
+ return false;
+}
+
+void QX11Data::xdndHandleSelectionRequest(const XSelectionRequestEvent * req)
+{
+ if (!req)
+ return;
+ XEvent evt;
+ evt.xselection.type = SelectionNotify;
+ evt.xselection.display = req->display;
+ evt.xselection.requestor = req->requestor;
+ evt.xselection.selection = req->selection;
+ evt.xselection.target = XNone;
+ evt.xselection.property = XNone;
+ evt.xselection.time = req->time;
+
+ QDragManager *manager = QDragManager::self();
+ QDrag *currentObject = manager->object;
+
+ // which transaction do we use? (note: -2 means use current manager->object)
+ int at = -1;
+
+ // figure out which data the requestor is really interested in
+ if (manager->object && req->time == qt_xdnd_source_current_time) {
+ // requestor wants the current drag data
+ at = -2;
+ } else {
+ // if someone has requested data in response to XdndDrop, find the corresponding transaction. the
+ // spec says to call XConvertSelection() using the timestamp from the XdndDrop
+ at = findXdndDropTransactionByTime(req->time);
+ if (at == -1) {
+ // no dice, perhaps the client was nice enough to use the same window id in XConvertSelection()
+ // that we sent the XdndDrop event to.
+ at = findXdndDropTransactionByWindow(req->requestor);
+ }
+ if (at == -1 && req->time == CurrentTime) {
+ // previous Qt versions always requested the data on a child of the target window
+ // using CurrentTime... but it could be asking for either drop data or the current drag's data
+ Window target = findXdndAwareParent(req->requestor);
+ if (target) {
+ if (qt_xdnd_current_target && qt_xdnd_current_target == target)
+ at = -2;
+ else
+ at = findXdndDropTransactionByWindow(target);
+ }
+ }
+ }
+ if (at >= 0) {
+ restartXdndDropExpiryTimer();
+
+ // use the drag object from an XdndDrop tansaction
+ manager->object = X11->dndDropTransactions.at(at).object;
+ } else if (at != -2) {
+ // no transaction found, we'll have to reject the request
+ manager->object = 0;
+ }
+ if (manager->object) {
+ Atom atomFormat = req->target;
+ int dataFormat = 0;
+ QByteArray data;
+ if (X11->xdndMimeDataForAtom(req->target, manager->dragPrivate()->data,
+ &data, &atomFormat, &dataFormat)) {
+ int dataSize = data.size() / (dataFormat / 8);
+ XChangeProperty (X11->display, req->requestor, req->property,
+ atomFormat, dataFormat, PropModeReplace,
+ (unsigned char *)data.data(), dataSize);
+ evt.xselection.property = req->property;
+ evt.xselection.target = atomFormat;
+ }
+ }
+
+ // reset manager->object in case we modified it above
+ manager->object = currentObject;
+
+ // ### this can die if req->requestor crashes at the wrong
+ // ### moment
+ XSendEvent(X11->display, req->requestor, False, 0, &evt);
+}
+
+static QVariant xdndObtainData(const char *format, QVariant::Type requestedType)
+{
+ QByteArray result;
+
+ QWidget* w;
+ QDragManager *manager = QDragManager::self();
+ if (qt_xdnd_dragsource_xid && manager->object &&
+ (w=QWidget::find(qt_xdnd_dragsource_xid))
+ && (!(w->windowType() == Qt::Desktop) || w->acceptDrops()))
+ {
+ QDragPrivate * o = QDragManager::self()->dragPrivate();
+ if (o->data->hasFormat(QLatin1String(format)))
+ result = o->data->data(QLatin1String(format));
+ return result;
+ }
+
+ QList<Atom> atoms;
+ int i = 0;
+ while ((qt_xdnd_types[i])) {
+ atoms.append(qt_xdnd_types[i]);
+ ++i;
+ }
+ QByteArray encoding;
+ Atom a = X11->xdndMimeAtomForFormat(QLatin1String(format), requestedType, atoms, &encoding);
+ if (!a)
+ return result;
+
+ if (XGetSelectionOwner(X11->display, ATOM(XdndSelection)) == XNone)
+ return result; // should never happen?
+
+ QWidget* tw = qt_xdnd_current_widget;
+ if (!qt_xdnd_current_widget || (qt_xdnd_current_widget->windowType() == Qt::Desktop))
+ tw = new QWidget;
+
+ XConvertSelection(X11->display, ATOM(XdndSelection), a, ATOM(XdndSelection), tw->effectiveWinId(),
+ qt_xdnd_target_current_time);
+ XFlush(X11->display);
+
+ XEvent xevent;
+ bool got=X11->clipboardWaitForEvent(tw->effectiveWinId(), SelectionNotify, &xevent, 5000);
+ if (got) {
+ Atom type;
+
+ if (X11->clipboardReadProperty(tw->effectiveWinId(), ATOM(XdndSelection), true, &result, 0, &type, 0)) {
+ if (type == ATOM(INCR)) {
+ int nbytes = result.size() >= 4 ? *((int*)result.data()) : 0;
+ result = X11->clipboardReadIncrementalProperty(tw->effectiveWinId(), ATOM(XdndSelection), nbytes, false);
+ } else if (type != a && type != XNone) {
+ DEBUG("Qt clipboard: unknown atom %ld", type);
+ }
+ }
+ }
+ if (!qt_xdnd_current_widget || (qt_xdnd_current_widget->windowType() == Qt::Desktop))
+ delete tw;
+
+ return X11->xdndMimeConvertToFormat(a, result, QLatin1String(format), requestedType, encoding);
+}
+
+
+/*
+ Enable drag and drop for widget w by installing the proper
+ properties on w's toplevel widget.
+*/
+bool QX11Data::dndEnable(QWidget* w, bool on)
+{
+ w = w->window();
+
+ if (bool(((QExtraWidget*)w)->topData()->dnd) == on)
+ return true; // been there, done that
+ ((QExtraWidget*)w)->topData()->dnd = on ? 1 : 0;
+
+ motifdndEnable(w, on);
+ return xdndEnable(w, on);
+}
+
+Qt::DropAction QDragManager::drag(QDrag * o)
+{
+ if (object == o || !o || !o->d_func()->source)
+ return Qt::IgnoreAction;
+
+ if (object) {
+ cancel();
+ qApp->removeEventFilter(this);
+ beingCancelled = false;
+ }
+
+ if (object) {
+ // the last drag and drop operation hasn't finished, so we are going to wait
+ // for one second to see if it does... if the finish message comes after this,
+ // then we could still have problems, but this is highly unlikely
+ QApplication::flush();
+
+ QElapsedTimer timer;
+ timer.start();
+ do {
+ XEvent event;
+ if (XCheckTypedEvent(X11->display, ClientMessage, &event))
+ qApp->x11ProcessEvent(&event);
+
+ // sleep 50 ms, so we don't use up CPU cycles all the time.
+ struct timeval usleep_tv;
+ usleep_tv.tv_sec = 0;
+ usleep_tv.tv_usec = 50000;
+ select(0, 0, 0, 0, &usleep_tv);
+ } while (object && timer.hasExpired(1000));
+ }
+
+ object = o;
+ object->d_func()->target = 0;
+ xdnd_data.deco = new QShapedPixmapWidget(object->source()->window());
+
+ willDrop = false;
+
+ updatePixmap();
+
+ qApp->installEventFilter(this);
+ XSetSelectionOwner(X11->display, ATOM(XdndSelection), dragPrivate()->source->window()->internalWinId(), X11->time);
+ global_accepted_action = Qt::CopyAction;
+ qt_xdnd_source_sameanswer = QRect();
+#ifndef QT_NO_CURSOR
+ // set the override cursor (must be done here, since it is updated
+ // in the call to move() below)
+ qApp->setOverrideCursor(Qt::ArrowCursor);
+ restoreCursor = true;
+#endif
+ move(QCursor::pos());
+ heartbeat = startTimer(200);
+
+ qt_xdnd_dragging = true;
+
+ if (!QWidget::mouseGrabber())
+ xdnd_data.deco->grabMouse();
+
+ eventLoop = new QEventLoop;
+ (void) eventLoop->exec();
+ delete eventLoop;
+ eventLoop = 0;
+
+#ifndef QT_NO_CURSOR
+ if (restoreCursor) {
+ qApp->restoreOverrideCursor();
+ restoreCursor = false;
+ }
+#endif
+
+ // delete cursors as they may be different next drag.
+ delete noDropCursor;
+ noDropCursor = 0;
+ delete copyCursor;
+ copyCursor = 0;
+ delete moveCursor;
+ moveCursor = 0;
+ delete linkCursor;
+ linkCursor = 0;
+
+ delete xdnd_data.deco;
+ xdnd_data.deco = 0;
+ if (heartbeat != -1)
+ killTimer(heartbeat);
+ heartbeat = -1;
+ qt_xdnd_current_screen = -1;
+ qt_xdnd_dragging = false;
+
+ return global_accepted_action;
+ // object persists until we get an xdnd_finish message
+}
+
+void QDragManager::updatePixmap()
+{
+ if (xdnd_data.deco) {
+ QPixmap pm;
+ QPoint pm_hot(default_pm_hotx,default_pm_hoty);
+ if (object) {
+ pm = dragPrivate()->pixmap;
+ if (!pm.isNull())
+ pm_hot = dragPrivate()->hotspot;
+ }
+ if (pm.isNull()) {
+ if (!defaultPm)
+ defaultPm = new QPixmap(default_pm);
+ pm = *defaultPm;
+ }
+ xdnd_data.deco->pm_hot = pm_hot;
+ xdnd_data.deco->setPixmap(pm);
+ xdnd_data.deco->move(QCursor::pos()-pm_hot);
+ xdnd_data.deco->show();
+ }
+}
+
+QVariant QDropData::retrieveData_sys(const QString &mimetype, QVariant::Type requestedType) const
+{
+ QByteArray mime = mimetype.toLatin1();
+ QVariant data = X11->motifdnd_active
+ ? X11->motifdndObtainData(mime)
+ : xdndObtainData(mime, requestedType);
+ return data;
+}
+
+bool QDropData::hasFormat_sys(const QString &format) const
+{
+ return formats().contains(format);
+}
+
+QStringList QDropData::formats_sys() const
+{
+ QStringList formats;
+ if (X11->motifdnd_active) {
+ int i = 0;
+ QByteArray fmt;
+ while (!(fmt = X11->motifdndFormat(i)).isEmpty()) {
+ formats.append(QLatin1String(fmt));
+ ++i;
+ }
+ } else {
+ int i = 0;
+ while ((qt_xdnd_types[i])) {
+ QStringList formatsForAtom = X11->xdndMimeFormatsForAtom(qt_xdnd_types[i]);
+ for (int j = 0; j < formatsForAtom.size(); ++j) {
+ if (!formats.contains(formatsForAtom.at(j)))
+ formats.append(formatsForAtom.at(j));
+ }
+ ++i;
+ }
+ }
+ return formats;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_DRAGANDDROP
diff --git a/src/widgets/platforms/x11/qeventdispatcher_x11.cpp b/src/widgets/platforms/x11/qeventdispatcher_x11.cpp
new file mode 100644
index 0000000000..e31d8ed301
--- /dev/null
+++ b/src/widgets/platforms/x11/qeventdispatcher_x11.cpp
@@ -0,0 +1,191 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qeventdispatcher_x11_p.h"
+
+#include "qapplication.h"
+#include "qx11info_x11.h"
+
+#include "qt_x11_p.h"
+#include <private/qeventdispatcher_unix_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QEventDispatcherX11Private : public QEventDispatcherUNIXPrivate
+{
+ Q_DECLARE_PUBLIC(QEventDispatcherX11)
+public:
+ inline QEventDispatcherX11Private()
+ : xfd(-1)
+ { }
+ int xfd;
+ QList<XEvent> queuedUserInputEvents;
+};
+
+QEventDispatcherX11::QEventDispatcherX11(QObject *parent)
+ : QEventDispatcherUNIX(*new QEventDispatcherX11Private, parent)
+{ }
+
+QEventDispatcherX11::~QEventDispatcherX11()
+{ }
+
+bool QEventDispatcherX11::processEvents(QEventLoop::ProcessEventsFlags flags)
+{
+ Q_D(QEventDispatcherX11);
+
+ d->interrupt = false;
+ QApplication::sendPostedEvents();
+
+ ulong marker = XNextRequest(X11->display);
+ int nevents = 0;
+ do {
+ while (!d->interrupt) {
+ XEvent event;
+ if (!(flags & QEventLoop::ExcludeUserInputEvents)
+ && !d->queuedUserInputEvents.isEmpty()) {
+ // process a pending user input event
+ event = d->queuedUserInputEvents.takeFirst();
+ } else if (XEventsQueued(X11->display, QueuedAlready)) {
+ // process events from the X server
+ XNextEvent(X11->display, &event);
+
+ if (flags & QEventLoop::ExcludeUserInputEvents) {
+ // queue user input events
+ switch (event.type) {
+ case ButtonPress:
+ case ButtonRelease:
+ case MotionNotify:
+ case XKeyPress:
+ case XKeyRelease:
+ case EnterNotify:
+ case LeaveNotify:
+ d->queuedUserInputEvents.append(event);
+ continue;
+
+ case ClientMessage:
+ // only keep the wm_take_focus and
+ // _qt_scrolldone protocols, queue all other
+ // client messages
+ if (event.xclient.format == 32) {
+ if (event.xclient.message_type == ATOM(WM_PROTOCOLS) &&
+ (Atom) event.xclient.data.l[0] == ATOM(WM_TAKE_FOCUS)) {
+ break;
+ } else if (event.xclient.message_type == ATOM(_QT_SCROLL_DONE)) {
+ break;
+ }
+ }
+ d->queuedUserInputEvents.append(event);
+ continue;
+
+ default:
+ break;
+ }
+ }
+ } else {
+ // no event to process
+ break;
+ }
+
+ // send through event filter
+ if (filterEvent(&event))
+ continue;
+
+ nevents++;
+ if (qApp->x11ProcessEvent(&event) == 1)
+ return true;
+
+ if (event.xany.serial >= marker) {
+ if (XEventsQueued(X11->display, QueuedAfterFlush))
+ flags &= ~QEventLoop::WaitForMoreEvents;
+ goto out;
+ }
+ }
+ } while (!d->interrupt && XEventsQueued(X11->display, QueuedAfterFlush));
+
+ out:
+ if (!d->interrupt) {
+ const uint exclude_all =
+ QEventLoop::ExcludeSocketNotifiers | QEventLoop::X11ExcludeTimers | QEventLoop::WaitForMoreEvents;
+ if (nevents > 0 && ((uint)flags & exclude_all) == exclude_all) {
+ QApplication::sendPostedEvents();
+ return nevents > 0;
+ }
+ // return true if we handled events, false otherwise
+ return QEventDispatcherUNIX::processEvents(flags) || (nevents > 0);
+ }
+ return nevents > 0;
+}
+
+bool QEventDispatcherX11::hasPendingEvents()
+{
+ extern uint qGlobalPostedEventsCount(); // from qapplication.cpp
+ return (qGlobalPostedEventsCount() || XPending(X11->display));
+}
+
+void QEventDispatcherX11::flush()
+{
+ XFlush(X11->display);
+}
+
+void QEventDispatcherX11::startingUp()
+{
+ Q_D(QEventDispatcherX11);
+ d->xfd = XConnectionNumber(X11->display);
+}
+
+void QEventDispatcherX11::closingDown()
+{
+ Q_D(QEventDispatcherX11);
+ d->xfd = -1;
+}
+
+int QEventDispatcherX11::select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
+ timeval *timeout)
+{
+ Q_D(QEventDispatcherX11);
+ if (d->xfd > 0) {
+ nfds = qMax(nfds - 1, d->xfd) + 1;
+ FD_SET(d->xfd, readfds);
+ }
+ return QEventDispatcherUNIX::select(nfds, readfds, writefds, exceptfds, timeout);
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/x11/qeventdispatcher_x11_p.h b/src/widgets/platforms/x11/qeventdispatcher_x11_p.h
new file mode 100644
index 0000000000..1b6620d90f
--- /dev/null
+++ b/src/widgets/platforms/x11/qeventdispatcher_x11_p.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QEVENTDISPATCHER_X11_P_H
+#define QEVENTDISPATCHER_X11_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qeventdispatcher_unix_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QEventDispatcherX11Private;
+
+class QEventDispatcherX11 : public QEventDispatcherUNIX
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QEventDispatcherX11)
+
+public:
+ explicit QEventDispatcherX11(QObject *parent = 0);
+ ~QEventDispatcherX11();
+
+ bool processEvents(QEventLoop::ProcessEventsFlags flags);
+ bool hasPendingEvents();
+
+ void flush();
+
+ void startingUp();
+ void closingDown();
+
+protected:
+ int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
+ timeval *timeout);
+};
+
+QT_END_NAMESPACE
+
+#endif // QEVENTDISPATCHER_X11_P_H
diff --git a/src/widgets/platforms/x11/qfont_x11.cpp b/src/widgets/platforms/x11/qfont_x11.cpp
new file mode 100644
index 0000000000..ba69871855
--- /dev/null
+++ b/src/widgets/platforms/x11/qfont_x11.cpp
@@ -0,0 +1,368 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#define QT_FATAL_ASSERT
+
+#include "qplatformdefs.h"
+
+#include "qfont.h"
+#include "qapplication.h"
+#include "qfontinfo.h"
+#include "qfontdatabase.h"
+#include "qfontmetrics.h"
+#include "qpaintdevice.h"
+#include "qtextcodec.h"
+#include "qiodevice.h"
+#include "qhash.h"
+
+#include <private/qunicodetables_p.h>
+#include "qfont_p.h"
+#include "qfontengine_p.h"
+#include "qfontengine_x11_p.h"
+#include "qtextengine_p.h"
+
+#include <private/qt_x11_p.h>
+#include "qx11info_x11.h"
+
+#include <time.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#define QFONTLOADER_DEBUG
+#define QFONTLOADER_DEBUG_VERBOSE
+
+QT_BEGIN_NAMESPACE
+
+double qt_pixelSize(double pointSize, int dpi)
+{
+ if (pointSize < 0)
+ return -1.;
+ if (dpi == 75) // the stupid 75 dpi setting on X11
+ dpi = 72;
+ return (pointSize * dpi) /72.;
+}
+
+double qt_pointSize(double pixelSize, int dpi)
+{
+ if (pixelSize < 0)
+ return -1.;
+ if (dpi == 75) // the stupid 75 dpi setting on X11
+ dpi = 72;
+ return pixelSize * 72. / ((double) dpi);
+}
+
+/*
+ Removes wildcards from an XLFD.
+
+ Returns \a xlfd with all wildcards removed if a match for \a xlfd is
+ found, otherwise it returns \a xlfd.
+*/
+static QByteArray qt_fixXLFD(const QByteArray &xlfd)
+{
+ QByteArray ret = xlfd;
+ int count = 0;
+ char **fontNames =
+ XListFonts(QX11Info::display(), xlfd, 32768, &count);
+ if (count > 0)
+ ret = fontNames[0];
+ XFreeFontNames(fontNames);
+ return ret ;
+}
+
+typedef QHash<int, QString> FallBackHash;
+Q_GLOBAL_STATIC(FallBackHash, fallBackHash)
+
+// Returns the user-configured fallback family for the specified script.
+QString qt_fallback_font_family(int script)
+{
+ FallBackHash *hash = fallBackHash();
+ return hash->value(script);
+}
+
+// Sets the fallback family for the specified script.
+Q_WIDGETS_EXPORT void qt_x11_set_fallback_font_family(int script, const QString &family)
+{
+ FallBackHash *hash = fallBackHash();
+ if (!family.isEmpty())
+ hash->insert(script, family);
+ else
+ hash->remove(script);
+}
+
+int QFontPrivate::defaultEncodingID = -1;
+
+void QFont::initialize()
+{
+ extern int qt_encoding_id_for_mib(int mib); // from qfontdatabase_x11.cpp
+ QTextCodec *codec = QTextCodec::codecForLocale();
+ // determine the default encoding id using the locale, otherwise
+ // fallback to latin1 (mib == 4)
+ int mib = codec ? codec->mibEnum() : 4;
+
+ // for asian locales, use the mib for the font codec instead of the locale codec
+ switch (mib) {
+ case 38: // eucKR
+ mib = 36;
+ break;
+
+ case 2025: // GB2312
+ mib = 57;
+ break;
+
+ case 113: // GBK
+ mib = -113;
+ break;
+
+ case 114: // GB18030
+ mib = -114;
+ break;
+
+ case 2026: // Big5
+ mib = -2026;
+ break;
+
+ case 2101: // Big5-HKSCS
+ mib = -2101;
+ break;
+
+ case 16: // JIS7
+ mib = 15;
+ break;
+
+ case 17: // SJIS
+ case 18: // eucJP
+ mib = 63;
+ break;
+ }
+
+ // get the default encoding id for the locale encoding...
+ QFontPrivate::defaultEncodingID = qt_encoding_id_for_mib(mib);
+}
+
+void QFont::cleanup()
+{
+ QFontCache::cleanup();
+}
+
+/*!
+ \internal
+ X11 Only: Returns the screen with which this font is associated.
+*/
+int QFont::x11Screen() const
+{
+ return d->screen;
+}
+
+/*! \internal
+ X11 Only: Associate the font with the specified \a screen.
+*/
+void QFont::x11SetScreen(int screen)
+{
+ if (screen < 0) // assume default
+ screen = QX11Info::appScreen();
+
+ if (screen == d->screen)
+ return; // nothing to do
+
+ detach();
+ d->screen = screen;
+}
+
+Qt::HANDLE QFont::handle() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ if (engine->type() == QFontEngine::Multi)
+ engine = static_cast<QFontEngineMulti *>(engine)->engine(0);
+ if (engine->type() == QFontEngine::XLFD)
+ return static_cast<QFontEngineXLFD *>(engine)->fontStruct()->fid;
+ return 0;
+}
+
+
+FT_Face QFont::freetypeFace() const
+{
+#ifndef QT_NO_FREETYPE
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ if (engine->type() == QFontEngine::Multi)
+ engine = static_cast<QFontEngineMulti *>(engine)->engine(0);
+#ifndef QT_NO_FONTCONFIG
+ if (engine->type() == QFontEngine::Freetype) {
+ const QFontEngineFT *ft = static_cast<const QFontEngineFT *>(engine);
+ return ft->non_locked_face();
+ } else
+#endif
+ if (engine->type() == QFontEngine::XLFD) {
+ const QFontEngineXLFD *xlfd = static_cast<const QFontEngineXLFD *>(engine);
+ return xlfd->non_locked_face();
+ }
+#endif
+ return 0;
+}
+
+QString QFont::rawName() const
+{
+ QFontEngine *engine = d->engineForScript(QUnicodeTables::Common);
+ Q_ASSERT(engine != 0);
+ if (engine->type() == QFontEngine::Multi)
+ engine = static_cast<QFontEngineMulti *>(engine)->engine(0);
+ if (engine->type() == QFontEngine::XLFD)
+ return QString::fromLatin1(engine->name());
+ return QString();
+}
+struct QtFontDesc;
+
+void QFont::setRawName(const QString &name)
+{
+ detach();
+
+ // from qfontdatabase_x11.cpp
+ extern bool qt_fillFontDef(const QByteArray &xlfd, QFontDef *fd, int dpi, QtFontDesc *desc);
+
+ if (!qt_fillFontDef(qt_fixXLFD(name.toLatin1()), &d->request, d->dpi, 0)) {
+ qWarning("QFont::setRawName: Invalid XLFD: \"%s\"", name.toLatin1().constData());
+
+ setFamily(name);
+ setRawMode(true);
+ } else {
+ resolve_mask = QFont::AllPropertiesResolved;
+ }
+}
+
+QString QFont::lastResortFamily() const
+{
+ return QString::fromLatin1("Helvetica");
+}
+
+QString QFont::defaultFamily() const
+{
+ switch (d->request.styleHint) {
+ case QFont::Times:
+ return QString::fromLatin1("Times");
+
+ case QFont::Courier:
+ return QString::fromLatin1("Courier");
+
+ case QFont::Monospace:
+ return QString::fromLatin1("Courier New");
+
+ case QFont::Cursive:
+ return QString::fromLatin1("Comic Sans MS");
+
+ case QFont::Fantasy:
+ return QString::fromLatin1("Impact");
+
+ case QFont::Decorative:
+ return QString::fromLatin1("Old English");
+
+ case QFont::Helvetica:
+ case QFont::System:
+ default:
+ return QString::fromLatin1("Helvetica");
+ }
+}
+
+/*
+ Returns a last resort raw font name for the font matching algorithm.
+ This is used if even the last resort family is not available. It
+ returns \e something, almost no matter what. The current
+ implementation tries a wide variety of common fonts, returning the
+ first one it finds. The implementation may change at any time.
+*/
+static const char * const tryFonts[] = {
+ "-*-helvetica-medium-r-*-*-*-120-*-*-*-*-*-*",
+ "-*-courier-medium-r-*-*-*-120-*-*-*-*-*-*",
+ "-*-times-medium-r-*-*-*-120-*-*-*-*-*-*",
+ "-*-lucida-medium-r-*-*-*-120-*-*-*-*-*-*",
+ "-*-helvetica-*-*-*-*-*-120-*-*-*-*-*-*",
+ "-*-courier-*-*-*-*-*-120-*-*-*-*-*-*",
+ "-*-times-*-*-*-*-*-120-*-*-*-*-*-*",
+ "-*-lucida-*-*-*-*-*-120-*-*-*-*-*-*",
+ "-*-helvetica-*-*-*-*-*-*-*-*-*-*-*-*",
+ "-*-courier-*-*-*-*-*-*-*-*-*-*-*-*",
+ "-*-times-*-*-*-*-*-*-*-*-*-*-*-*",
+ "-*-lucida-*-*-*-*-*-*-*-*-*-*-*-*",
+ "-*-fixed-*-*-*-*-*-*-*-*-*-*-*-*",
+ "6x13",
+ "7x13",
+ "8x13",
+ "9x15",
+ "fixed",
+ 0
+};
+
+// Returns true if the font exists, false otherwise
+static bool fontExists(const QString &fontName)
+{
+ int count;
+ char **fontNames = XListFonts(QX11Info::display(), (char*)fontName.toLatin1().constData(), 32768, &count);
+ if (fontNames) XFreeFontNames(fontNames);
+
+ return count != 0;
+}
+
+QString QFont::lastResortFont() const
+{
+ static QString last;
+
+ // already found
+ if (! last.isNull())
+ return last;
+
+ int i = 0;
+ const char* f;
+
+ while ((f = tryFonts[i])) {
+ last = QString::fromLatin1(f);
+
+ if (fontExists(last))
+ return last;
+
+ i++;
+ }
+
+#if defined(CHECK_NULL)
+ qFatal("QFontPrivate::lastResortFont: Cannot find any reasonable font");
+#endif
+ return last;
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/x11/qfontdatabase_x11.cpp b/src/widgets/platforms/x11/qfontdatabase_x11.cpp
new file mode 100644
index 0000000000..922a97f3aa
--- /dev/null
+++ b/src/widgets/platforms/x11/qfontdatabase_x11.cpp
@@ -0,0 +1,2154 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qplatformdefs.h>
+
+#include <qdebug.h>
+#include <qpaintdevice.h>
+#include <qelapsedtimer.h>
+
+#include <private/qt_x11_p.h>
+#include "qx11info_x11.h"
+#include <qdebug.h>
+#include <qfile.h>
+#include <qtemporaryfile.h>
+#include <qabstractfileengine.h>
+#include <qmath.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#include <private/qfontengine_x11_p.h>
+
+#ifndef QT_NO_FONTCONFIG
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+#if FC_VERSION >= 20402
+#include <fontconfig/fcfreetype.h>
+#endif
+#endif
+
+QT_BEGIN_NAMESPACE
+
+// from qfont_x11.cpp
+extern double qt_pointSize(double pixelSize, int dpi);
+extern double qt_pixelSize(double pointSize, int dpi);
+
+// from qapplication.cpp
+extern bool qt_is_gui_used;
+
+static inline void capitalize (char *s)
+{
+ bool space = true;
+ while(*s) {
+ if (space)
+ *s = toupper(*s);
+ space = (*s == ' ');
+ ++s;
+ }
+}
+
+
+/*
+ To regenerate the writingSystems_for_xlfd_encoding table, run
+ 'util/unicode/x11/makeencodings' and paste the generated
+ 'encodings.c' here.
+*/
+// ----- begin of generated code -----
+
+#define make_tag( c1, c2, c3, c4 ) \
+ ((((unsigned int)c1)<<24) | (((unsigned int)c2)<<16) | \
+ (((unsigned int)c3)<<8) | ((unsigned int)c4))
+
+struct XlfdEncoding {
+ const char *name;
+ int id;
+ int mib;
+ unsigned int hash1;
+ unsigned int hash2;
+};
+
+static const XlfdEncoding xlfd_encoding[] = {
+ { "iso8859-1", 0, 4, make_tag('i','s','o','8'), make_tag('5','9','-','1') },
+ { "iso8859-2", 1, 5, make_tag('i','s','o','8'), make_tag('5','9','-','2') },
+ { "iso8859-3", 2, 6, make_tag('i','s','o','8'), make_tag('5','9','-','3') },
+ { "iso8859-4", 3, 7, make_tag('i','s','o','8'), make_tag('5','9','-','4') },
+ { "iso8859-9", 4, 12, make_tag('i','s','o','8'), make_tag('5','9','-','9') },
+ { "iso8859-10", 5, 13, make_tag('i','s','o','8'), make_tag('9','-','1','0') },
+ { "iso8859-13", 6, 109, make_tag('i','s','o','8'), make_tag('9','-','1','3') },
+ { "iso8859-14", 7, 110, make_tag('i','s','o','8'), make_tag('9','-','1','4') },
+ { "iso8859-15", 8, 111, make_tag('i','s','o','8'), make_tag('9','-','1','5') },
+ { "hp-roman8", 9, 2004, make_tag('h','p','-','r'), make_tag('m','a','n','8') },
+ { "iso8859-5", 10, 8, make_tag('i','s','o','8'), make_tag('5','9','-','5') },
+ { "*-cp1251", 11, 2251, 0, make_tag('1','2','5','1') },
+ { "koi8-ru", 12, 2084, make_tag('k','o','i','8'), make_tag('8','-','r','u') },
+ { "koi8-u", 13, 2088, make_tag('k','o','i','8'), make_tag('i','8','-','u') },
+ { "koi8-r", 14, 2084, make_tag('k','o','i','8'), make_tag('i','8','-','r') },
+ { "iso8859-7", 15, 10, make_tag('i','s','o','8'), make_tag('5','9','-','7') },
+ { "iso8859-8", 16, 85, make_tag('i','s','o','8'), make_tag('5','9','-','8') },
+ { "gb18030-0", 17, -114, make_tag('g','b','1','8'), make_tag('3','0','-','0') },
+ { "gb18030.2000-0", 18, -113, make_tag('g','b','1','8'), make_tag('0','0','-','0') },
+ { "gbk-0", 19, -113, make_tag('g','b','k','-'), make_tag('b','k','-','0') },
+ { "gb2312.*-0", 20, 57, make_tag('g','b','2','3'), 0 },
+ { "jisx0201*-0", 21, 15, make_tag('j','i','s','x'), 0 },
+ { "jisx0208*-0", 22, 63, make_tag('j','i','s','x'), 0 },
+ { "ksc5601*-*", 23, 36, make_tag('k','s','c','5'), 0 },
+ { "big5hkscs-0", 24, -2101, make_tag('b','i','g','5'), make_tag('c','s','-','0') },
+ { "hkscs-1", 25, -2101, make_tag('h','k','s','c'), make_tag('c','s','-','1') },
+ { "big5*-*", 26, -2026, make_tag('b','i','g','5'), 0 },
+ { "tscii-*", 27, 2028, make_tag('t','s','c','i'), 0 },
+ { "tis620*-*", 28, 2259, make_tag('t','i','s','6'), 0 },
+ { "iso8859-11", 29, 2259, make_tag('i','s','o','8'), make_tag('9','-','1','1') },
+ { "mulelao-1", 30, -4242, make_tag('m','u','l','e'), make_tag('a','o','-','1') },
+ { "ethiopic-unicode", 31, 0, make_tag('e','t','h','i'), make_tag('c','o','d','e') },
+ { "iso10646-1", 32, 0, make_tag('i','s','o','1'), make_tag('4','6','-','1') },
+ { "unicode-*", 33, 0, make_tag('u','n','i','c'), 0 },
+ { "*-symbol", 34, 0, 0, make_tag('m','b','o','l') },
+ { "*-fontspecific", 35, 0, 0, make_tag('i','f','i','c') },
+ { "fontspecific-*", 36, 0, make_tag('f','o','n','t'), 0 },
+ { 0, 0, 0, 0, 0 }
+};
+
+static const char writingSystems_for_xlfd_encoding[sizeof(xlfd_encoding)][QFontDatabase::WritingSystemsCount] = {
+ // iso8859-1
+ { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ // iso8859-2
+ { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ // iso8859-3
+ { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ // iso8859-4
+ { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ // iso8859-9
+ { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ // iso8859-10
+ { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ // iso8859-13
+ { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ // iso8859-14
+ { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ // iso8859-15
+ { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ // hp-roman8
+ { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ // iso8859-5
+ { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ // *-cp1251
+ { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ // koi8-ru
+ { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ // koi8-u
+ { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ // koi8-r
+ { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ // iso8859-7
+ { 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ // iso8859-8
+ { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ // gb18030-0
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
+ 0, 0 },
+ // gb18030.2000-0
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
+ 0, 0 },
+ // gbk-0
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
+ 0, 0 },
+ // gb2312.*-0
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
+ 0, 0 },
+ // jisx0201*-0
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
+ 0, 0 },
+ // jisx0208*-0
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
+ 0, 0 },
+ // ksc5601*-*
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
+ 0, 0 },
+ // big5hkscs-0
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
+ 0, 0 },
+ // hkscs-1
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
+ 0, 0 },
+ // big5*-*
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
+ 0, 0 },
+ // tscii-*
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ // tis620*-*
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ // iso8859-11
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ // mulelao-1
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ // ethiopic-unicode
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0 },
+ // iso10646-1
+ { 0, 1, 1, 1, 1, 1, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+ 1, 1, 0, 1, 0, 1, 1, 0, 0, 0,
+ 0, 0 },
+ // unicode-*
+ { 0, 1, 1, 1, 1, 1, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+ 1, 1, 0, 1, 0, 1, 1, 0, 0, 0,
+ 0, 0 },
+ // *-symbol
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0 },
+ // *-fontspecific
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0 },
+ // fontspecific-*
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0 }
+
+};
+
+// ----- end of generated code -----
+
+
+const int numEncodings = sizeof(xlfd_encoding) / sizeof(XlfdEncoding) - 1;
+
+int qt_xlfd_encoding_id(const char *encoding)
+{
+ // qDebug("looking for encoding id for '%s'", encoding);
+ int len = strlen(encoding);
+ if (len < 4)
+ return -1;
+ unsigned int hash1 = make_tag(encoding[0], encoding[1], encoding[2], encoding[3]);
+ const char *ch = encoding + len - 4;
+ unsigned int hash2 = make_tag(ch[0], ch[1], ch[2], ch[3]);
+
+ const XlfdEncoding *enc = xlfd_encoding;
+ for (; enc->name; ++enc) {
+ if ((enc->hash1 && enc->hash1 != hash1) ||
+ (enc->hash2 && enc->hash2 != hash2))
+ continue;
+ // hashes match, do a compare if strings match
+ // the enc->name can contain '*'s we have to interpret correctly
+ const char *n = enc->name;
+ const char *e = encoding;
+ while (1) {
+ // qDebug("bol: *e='%c', *n='%c'", *e, *n);
+ if (*e == '\0') {
+ if (*n)
+ break;
+ // qDebug("found encoding id %d", enc->id);
+ return enc->id;
+ }
+ if (*e == *n) {
+ ++e;
+ ++n;
+ continue;
+ }
+ if (*n != '*')
+ break;
+ ++n;
+ // qDebug("skip: *e='%c', *n='%c'", *e, *n);
+ while (*e && *e != *n)
+ ++e;
+ }
+ }
+ // qDebug("couldn't find encoding %s", encoding);
+ return -1;
+}
+
+int qt_mib_for_xlfd_encoding(const char *encoding)
+{
+ int id = qt_xlfd_encoding_id(encoding);
+ if (id != -1) return xlfd_encoding[id].mib;
+ return 0;
+}
+
+int qt_encoding_id_for_mib(int mib)
+{
+ const XlfdEncoding *enc = xlfd_encoding;
+ for (; enc->name; ++enc) {
+ if (enc->mib == mib)
+ return enc->id;
+ }
+ return -1;
+}
+
+static const char * xlfd_for_id(int id)
+{
+ // special case: -1 returns the "*-*" encoding, allowing us to do full
+ // database population in a single X server round trip.
+ if (id < 0 || id > numEncodings)
+ return "*-*";
+ return xlfd_encoding[id].name;
+}
+
+enum XLFDFieldNames {
+ Foundry,
+ Family,
+ Weight,
+ Slant,
+ Width,
+ AddStyle,
+ PixelSize,
+ PointSize,
+ ResolutionX,
+ ResolutionY,
+ Spacing,
+ AverageWidth,
+ CharsetRegistry,
+ CharsetEncoding,
+ NFontFields
+};
+
+// Splits an X font name into fields separated by '-'
+static bool parseXFontName(char *fontName, char **tokens)
+{
+ if (! fontName || fontName[0] == '0' || fontName[0] != '-') {
+ tokens[0] = 0;
+ return false;
+ }
+
+ int i;
+ ++fontName;
+ for (i = 0; i < NFontFields && fontName && fontName[0]; ++i) {
+ tokens[i] = fontName;
+ for (;; ++fontName) {
+ if (*fontName == '-')
+ break;
+ if (! *fontName) {
+ fontName = 0;
+ break;
+ }
+ }
+
+ if (fontName) *fontName++ = '\0';
+ }
+
+ if (i < NFontFields) {
+ for (int j = i ; j < NFontFields; ++j)
+ tokens[j] = 0;
+ return false;
+ }
+
+ return true;
+}
+
+static inline bool isZero(char *x)
+{
+ return (x[0] == '0' && x[1] == 0);
+}
+
+static inline bool isScalable(char **tokens)
+{
+ return (isZero(tokens[PixelSize]) &&
+ isZero(tokens[PointSize]) &&
+ isZero(tokens[AverageWidth]));
+}
+
+static inline bool isSmoothlyScalable(char **tokens)
+{
+ return (isZero(tokens[ResolutionX]) &&
+ isZero(tokens[ResolutionY]));
+}
+
+static inline bool isFixedPitch(char **tokens)
+{
+ return (tokens[Spacing][0] == 'm' ||
+ tokens[Spacing][0] == 'c' ||
+ tokens[Spacing][0] == 'M' ||
+ tokens[Spacing][0] == 'C');
+}
+
+/*
+ Fills in a font definition (QFontDef) from an XLFD (X Logical Font
+ Description).
+
+ Returns true if the given xlfd is valid.
+*/
+bool qt_fillFontDef(const QByteArray &xlfd, QFontDef *fd, int dpi, QtFontDesc *desc)
+{
+ char *tokens[NFontFields];
+ QByteArray buffer = xlfd;
+ if (! parseXFontName(buffer.data(), tokens))
+ return false;
+
+ capitalize(tokens[Family]);
+ capitalize(tokens[Foundry]);
+
+ fd->styleStrategy |= QFont::NoAntialias;
+ fd->family = QString::fromLatin1(tokens[Family]);
+ QString foundry = QString::fromLatin1(tokens[Foundry]);
+ if (! foundry.isEmpty() && foundry != QLatin1String("*") && (!desc || desc->family->count > 1))
+ fd->family +=
+ QLatin1String(" [") + foundry + QLatin1Char(']');
+
+ if (qstrlen(tokens[AddStyle]) > 0)
+ fd->addStyle = QString::fromLatin1(tokens[AddStyle]);
+ else
+ fd->addStyle.clear();
+
+ fd->pointSize = atoi(tokens[PointSize])/10.;
+ fd->styleHint = QFont::AnyStyle; // ### any until we match families
+
+ char slant = tolower((uchar) tokens[Slant][0]);
+ fd->style = (slant == 'o' ? QFont::StyleOblique : (slant == 'i' ? QFont::StyleItalic : QFont::StyleNormal));
+ char fixed = tolower((uchar) tokens[Spacing][0]);
+ fd->fixedPitch = (fixed == 'm' || fixed == 'c');
+ fd->weight = getFontWeight(QLatin1String(tokens[Weight]));
+
+ int r = atoi(tokens[ResolutionY]);
+ fd->pixelSize = atoi(tokens[PixelSize]);
+ // not "0" or "*", or required DPI
+ if (r && fd->pixelSize && r != dpi) {
+ // calculate actual pointsize for display DPI
+ fd->pointSize = qt_pointSize(fd->pixelSize, dpi);
+ } else if (fd->pixelSize == 0 && fd->pointSize) {
+ // calculate pixel size from pointsize/dpi
+ fd->pixelSize = qRound(qt_pixelSize(fd->pointSize, dpi));
+ }
+
+ return true;
+}
+
+/*
+ Fills in a font definition (QFontDef) from the font properties in an
+ XFontStruct.
+
+ Returns true if the QFontDef could be filled with properties from
+ the XFontStruct.
+*/
+static bool qt_fillFontDef(XFontStruct *fs, QFontDef *fd, int dpi, QtFontDesc *desc)
+{
+ unsigned long value;
+ if (!fs || !XGetFontProperty(fs, XA_FONT, &value))
+ return false;
+
+ char *n = XGetAtomName(QX11Info::display(), value);
+ QByteArray xlfd(n);
+ if (n)
+ XFree(n);
+ return qt_fillFontDef(xlfd.toLower(), fd, dpi, desc);
+}
+
+
+static QtFontStyle::Key getStyle(char ** tokens)
+{
+ QtFontStyle::Key key;
+
+ char slant0 = tolower((uchar) tokens[Slant][0]);
+
+ if (slant0 == 'r') {
+ if (tokens[Slant][1]) {
+ char slant1 = tolower((uchar) tokens[Slant][1]);
+
+ if (slant1 == 'o')
+ key.style = QFont::StyleOblique;
+ else if (slant1 == 'i')
+ key.style = QFont::StyleItalic;
+ }
+ } else if (slant0 == 'o')
+ key.style = QFont::StyleOblique;
+ else if (slant0 == 'i')
+ key.style = QFont::StyleItalic;
+
+ key.weight = getFontWeight(QLatin1String(tokens[Weight]));
+
+ if (qstrcmp(tokens[Width], "normal") == 0) {
+ key.stretch = 100;
+ } else if (qstrcmp(tokens[Width], "semi condensed") == 0 ||
+ qstrcmp(tokens[Width], "semicondensed") == 0) {
+ key.stretch = 90;
+ } else if (qstrcmp(tokens[Width], "condensed") == 0) {
+ key.stretch = 80;
+ } else if (qstrcmp(tokens[Width], "narrow") == 0) {
+ key.stretch = 60;
+ }
+
+ return key;
+}
+
+
+static bool xlfdsFullyLoaded = false;
+static unsigned char encodingLoaded[numEncodings];
+
+static void loadXlfds(const char *reqFamily, int encoding_id)
+{
+ QFontDatabasePrivate *db = privateDb();
+ QtFontFamily *fontFamily = reqFamily ? db->family(QLatin1String(reqFamily)) : 0;
+
+ // make sure we don't load twice
+ if ((encoding_id == -1 && xlfdsFullyLoaded)
+ || (encoding_id != -1 && encodingLoaded[encoding_id]))
+ return;
+ if (fontFamily && fontFamily->xlfdLoaded)
+ return;
+
+ int fontCount;
+ // force the X server to give us XLFDs
+ QByteArray xlfd_pattern("-*-");
+ xlfd_pattern += (reqFamily && reqFamily[0] != '\0') ? reqFamily : "*";
+ xlfd_pattern += "-*-*-*-*-*-*-*-*-*-*-";
+ xlfd_pattern += xlfd_for_id(encoding_id);
+
+ char **fontList = XListFonts(QX11Info::display(),
+ xlfd_pattern,
+ 0xffff, &fontCount);
+ // qDebug("requesting xlfd='%s', got %d fonts", xlfd_pattern.data(), fontCount);
+
+
+ char *tokens[NFontFields];
+
+ for(int i = 0 ; i < fontCount ; i++) {
+ if (! parseXFontName(fontList[i], tokens))
+ continue;
+
+ // get the encoding_id for this xlfd. we need to do this
+ // here, since we can pass -1 to this function to do full
+ // database population
+ *(tokens[CharsetEncoding] - 1) = '-';
+ int encoding_id = qt_xlfd_encoding_id(tokens[CharsetRegistry]);
+ if (encoding_id == -1)
+ continue;
+
+ char *familyName = tokens[Family];
+ capitalize(familyName);
+ char *foundryName = tokens[Foundry];
+ capitalize(foundryName);
+ QtFontStyle::Key styleKey = getStyle(tokens);
+
+ bool smooth_scalable = false;
+ bool bitmap_scalable = false;
+ if (isScalable(tokens)) {
+ if (isSmoothlyScalable(tokens))
+ smooth_scalable = true;
+ else
+ bitmap_scalable = true;
+ }
+ uint pixelSize = atoi(tokens[PixelSize]);
+ uint xpointSize = atoi(tokens[PointSize]);
+ uint xres = atoi(tokens[ResolutionX]);
+ uint yres = atoi(tokens[ResolutionY]);
+ uint avgwidth = atoi(tokens[AverageWidth]);
+ bool fixedPitch = isFixedPitch(tokens);
+
+ if (avgwidth == 0 && pixelSize != 0) {
+ /*
+ Ignore bitmap scalable fonts that are automatically
+ generated by some X servers. We know they are bitmap
+ scalable because even though they have a specified pixel
+ size, the average width is zero.
+ */
+ continue;
+ }
+
+ QtFontFamily *family = fontFamily ? fontFamily : db->family(QLatin1String(familyName), true);
+ family->fontFileIndex = -1;
+ family->symbol_checked = true;
+ QtFontFoundry *foundry = family->foundry(QLatin1String(foundryName), true);
+ QtFontStyle *style = foundry->style(styleKey, QString(), true);
+
+ delete [] style->weightName;
+ style->weightName = qstrdup(tokens[Weight]);
+ delete [] style->setwidthName;
+ style->setwidthName = qstrdup(tokens[Width]);
+
+ if (smooth_scalable) {
+ style->smoothScalable = true;
+ style->bitmapScalable = false;
+ pixelSize = SMOOTH_SCALABLE;
+ }
+ if (!style->smoothScalable && bitmap_scalable)
+ style->bitmapScalable = true;
+ if (!fixedPitch)
+ family->fixedPitch = false;
+
+ QtFontSize *size = style->pixelSize(pixelSize, true);
+ QtFontEncoding *enc =
+ size->encodingID(encoding_id, xpointSize, xres, yres, avgwidth, true);
+ enc->pitch = *tokens[Spacing];
+ if (!enc->pitch) enc->pitch = '*';
+
+ for (int i = 0; i < QFontDatabase::WritingSystemsCount; ++i) {
+ if (writingSystems_for_xlfd_encoding[encoding_id][i])
+ family->writingSystems[i] = QtFontFamily::Supported;
+ }
+ }
+ if (!reqFamily) {
+ // mark encoding as loaded
+ if (encoding_id == -1)
+ xlfdsFullyLoaded = true;
+ else
+ encodingLoaded[encoding_id] = true;
+ }
+
+ XFreeFontNames(fontList);
+}
+
+
+#ifndef QT_NO_FONTCONFIG
+
+#ifndef FC_WIDTH
+#define FC_WIDTH "width"
+#endif
+
+static int getFCWeight(int fc_weight)
+{
+ int qtweight = QFont::Black;
+ if (fc_weight <= (FC_WEIGHT_LIGHT + FC_WEIGHT_MEDIUM) / 2)
+ qtweight = QFont::Light;
+ else if (fc_weight <= (FC_WEIGHT_MEDIUM + FC_WEIGHT_DEMIBOLD) / 2)
+ qtweight = QFont::Normal;
+ else if (fc_weight <= (FC_WEIGHT_DEMIBOLD + FC_WEIGHT_BOLD) / 2)
+ qtweight = QFont::DemiBold;
+ else if (fc_weight <= (FC_WEIGHT_BOLD + FC_WEIGHT_BLACK) / 2)
+ qtweight = QFont::Bold;
+
+ return qtweight;
+}
+
+QFontDef qt_FcPatternToQFontDef(FcPattern *pattern, const QFontDef &request)
+{
+ QFontDef fontDef;
+ fontDef.styleStrategy = request.styleStrategy;
+
+ fontDef.hintingPreference = request.hintingPreference;
+ FcChar8 *value = 0;
+ if (FcPatternGetString(pattern, FC_FAMILY, 0, &value) == FcResultMatch) {
+ fontDef.family = QString::fromUtf8(reinterpret_cast<const char *>(value));
+ }
+
+ double dpi;
+ if (FcPatternGetDouble(pattern, FC_DPI, 0, &dpi) != FcResultMatch) {
+ if (X11->display)
+ dpi = QX11Info::appDpiY();
+ else
+ dpi = qt_defaultDpiY();
+ }
+
+ double size;
+ if (FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &size) == FcResultMatch)
+ fontDef.pixelSize = size;
+ else
+ fontDef.pixelSize = 12;
+
+ fontDef.pointSize = qt_pointSize(fontDef.pixelSize, qRound(dpi));
+
+ /* ###
+ fontDef.styleHint
+ */
+
+ int weight;
+ if (FcPatternGetInteger(pattern, FC_WEIGHT, 0, &weight) != FcResultMatch)
+ weight = FC_WEIGHT_MEDIUM;
+ fontDef.weight = getFCWeight(weight);
+
+ int slant;
+ if (FcPatternGetInteger(pattern, FC_SLANT, 0, &slant) != FcResultMatch)
+ slant = FC_SLANT_ROMAN;
+ fontDef.style = (slant == FC_SLANT_ITALIC)
+ ? QFont::StyleItalic
+ : ((slant == FC_SLANT_OBLIQUE)
+ ? QFont::StyleOblique
+ : QFont::StyleNormal);
+
+
+ FcBool scalable;
+ if (FcPatternGetBool(pattern, FC_SCALABLE, 0, &scalable) != FcResultMatch)
+ scalable = false;
+ if (scalable) {
+ fontDef.stretch = request.stretch;
+ fontDef.style = request.style;
+ } else {
+ int width;
+ if (FcPatternGetInteger(pattern, FC_WIDTH, 0, &width) == FcResultMatch)
+ fontDef.stretch = width;
+ else
+ fontDef.stretch = 100;
+ }
+
+ int spacing;
+ if (FcPatternGetInteger(pattern, FC_SPACING, 0, &spacing) == FcResultMatch) {
+ fontDef.fixedPitch = (spacing >= FC_MONO);
+ fontDef.ignorePitch = false;
+ } else {
+ fontDef.ignorePitch = true;
+ }
+
+ return fontDef;
+}
+
+static const char *specialLanguages[] = {
+ "en", // Common
+ "el", // Greek
+ "ru", // Cyrillic
+ "hy", // Armenian
+ "he", // Hebrew
+ "ar", // Arabic
+ "syr", // Syriac
+ "div", // Thaana
+ "hi", // Devanagari
+ "bn", // Bengali
+ "pa", // Gurmukhi
+ "gu", // Gujarati
+ "or", // Oriya
+ "ta", // Tamil
+ "te", // Telugu
+ "kn", // Kannada
+ "ml", // Malayalam
+ "si", // Sinhala
+ "th", // Thai
+ "lo", // Lao
+ "bo", // Tibetan
+ "my", // Myanmar
+ "ka", // Georgian
+ "ko", // Hangul
+ "", // Ogham
+ "", // Runic
+ "km", // Khmer
+ "" // N'Ko
+};
+enum { SpecialLanguageCount = sizeof(specialLanguages) / sizeof(const char *) };
+
+static const ushort specialChars[] = {
+ 0, // English
+ 0, // Greek
+ 0, // Cyrillic
+ 0, // Armenian
+ 0, // Hebrew
+ 0, // Arabic
+ 0, // Syriac
+ 0, // Thaana
+ 0, // Devanagari
+ 0, // Bengali
+ 0, // Gurmukhi
+ 0, // Gujarati
+ 0, // Oriya
+ 0, // Tamil
+ 0xc15, // Telugu
+ 0xc95, // Kannada
+ 0xd15, // Malayalam
+ 0xd9a, // Sinhala
+ 0, // Thai
+ 0, // Lao
+ 0, // Tibetan
+ 0x1000, // Myanmar
+ 0, // Georgian
+ 0, // Hangul
+ 0x1681, // Ogham
+ 0x16a0, // Runic
+ 0, // Khmer
+ 0x7ca // N'Ko
+};
+enum { SpecialCharCount = sizeof(specialChars) / sizeof(ushort) };
+
+// this could become a list of all languages used for each writing
+// system, instead of using the single most common language.
+static const char *languageForWritingSystem[] = {
+ 0, // Any
+ "en", // Latin
+ "el", // Greek
+ "ru", // Cyrillic
+ "hy", // Armenian
+ "he", // Hebrew
+ "ar", // Arabic
+ "syr", // Syriac
+ "div", // Thaana
+ "hi", // Devanagari
+ "bn", // Bengali
+ "pa", // Gurmukhi
+ "gu", // Gujarati
+ "or", // Oriya
+ "ta", // Tamil
+ "te", // Telugu
+ "kn", // Kannada
+ "ml", // Malayalam
+ "si", // Sinhala
+ "th", // Thai
+ "lo", // Lao
+ "bo", // Tibetan
+ "my", // Myanmar
+ "ka", // Georgian
+ "km", // Khmer
+ "zh-cn", // SimplifiedChinese
+ "zh-tw", // TraditionalChinese
+ "ja", // Japanese
+ "ko", // Korean
+ "vi", // Vietnamese
+ 0, // Symbol
+ 0, // Ogham
+ 0, // Runic
+ 0 // N'Ko
+};
+enum { LanguageCount = sizeof(languageForWritingSystem) / sizeof(const char *) };
+
+// Unfortunately FontConfig doesn't know about some languages. We have to test these through the
+// charset. The lists below contain the systems where we need to do this.
+static const ushort sampleCharForWritingSystem[] = {
+ 0, // Any
+ 0, // Latin
+ 0, // Greek
+ 0, // Cyrillic
+ 0, // Armenian
+ 0, // Hebrew
+ 0, // Arabic
+ 0, // Syriac
+ 0, // Thaana
+ 0, // Devanagari
+ 0, // Bengali
+ 0, // Gurmukhi
+ 0, // Gujarati
+ 0, // Oriya
+ 0, // Tamil
+ 0xc15, // Telugu
+ 0xc95, // Kannada
+ 0xd15, // Malayalam
+ 0xd9a, // Sinhala
+ 0, // Thai
+ 0, // Lao
+ 0, // Tibetan
+ 0x1000, // Myanmar
+ 0, // Georgian
+ 0, // Khmer
+ 0, // SimplifiedChinese
+ 0, // TraditionalChinese
+ 0, // Japanese
+ 0, // Korean
+ 0, // Vietnamese
+ 0, // Symbol
+ 0x1681, // Ogham
+ 0x16a0, // Runic
+ 0x7ca // N'Ko
+};
+enum { SampleCharCount = sizeof(sampleCharForWritingSystem) / sizeof(ushort) };
+
+// Newer FontConfig let's us sort out fonts that contain certain glyphs, but no
+// open type tables for is directly. Do this so we don't pick some strange
+// pseudo unicode font
+static const char *openType[] = {
+ 0, // Any
+ 0, // Latin
+ 0, // Greek
+ 0, // Cyrillic
+ 0, // Armenian
+ 0, // Hebrew
+ 0, // Arabic
+ "syrc", // Syriac
+ "thaa", // Thaana
+ "deva", // Devanagari
+ "beng", // Bengali
+ "guru", // Gurmukhi
+ "gurj", // Gujarati
+ "orya", // Oriya
+ "taml", // Tamil
+ "telu", // Telugu
+ "knda", // Kannada
+ "mlym", // Malayalam
+ "sinh", // Sinhala
+ 0, // Thai
+ 0, // Lao
+ "tibt", // Tibetan
+ "mymr", // Myanmar
+ 0, // Georgian
+ "khmr", // Khmer
+ 0, // SimplifiedChinese
+ 0, // TraditionalChinese
+ 0, // Japanese
+ 0, // Korean
+ 0, // Vietnamese
+ 0, // Symbol
+ 0, // Ogham
+ 0, // Runic
+ "nko " // N'Ko
+};
+enum { OpenTypeCount = sizeof(openType) / sizeof(const char *) };
+
+
+static void loadFontConfig()
+{
+ Q_ASSERT_X(X11, "QFontDatabase",
+ "A QApplication object needs to be constructed before FontConfig is used.");
+ if (!X11->has_fontconfig)
+ return;
+
+ Q_ASSERT_X(int(QUnicodeTables::ScriptCount) == SpecialLanguageCount,
+ "QFontDatabase", "New scripts have been added.");
+ Q_ASSERT_X(int(QUnicodeTables::ScriptCount) == SpecialCharCount,
+ "QFontDatabase", "New scripts have been added.");
+ Q_ASSERT_X(int(QFontDatabase::WritingSystemsCount) == LanguageCount,
+ "QFontDatabase", "New writing systems have been added.");
+ Q_ASSERT_X(int(QFontDatabase::WritingSystemsCount) == SampleCharCount,
+ "QFontDatabase", "New writing systems have been added.");
+ Q_ASSERT_X(int(QFontDatabase::WritingSystemsCount) == OpenTypeCount,
+ "QFontDatabase", "New writing systems have been added.");
+
+ QFontDatabasePrivate *db = privateDb();
+ FcFontSet *fonts;
+
+ FcPattern *pattern = FcPatternCreate();
+ FcDefaultSubstitute(pattern);
+ FcChar8 *lang = 0;
+ if (FcPatternGetString(pattern, FC_LANG, 0, &lang) == FcResultMatch)
+ db->systemLang = QString::fromUtf8((const char *) lang);
+ FcPatternDestroy(pattern);
+
+ QString familyName;
+ FcChar8 *value = 0;
+ int weight_value;
+ int slant_value;
+ int spacing_value;
+ FcChar8 *file_value;
+ int index_value;
+ FcChar8 *foundry_value;
+ FcChar8 *style_value;
+ FcBool scalable;
+
+ {
+ FcObjectSet *os = FcObjectSetCreate();
+ FcPattern *pattern = FcPatternCreate();
+ const char *properties [] = {
+ FC_FAMILY, FC_STYLE, FC_WEIGHT, FC_SLANT,
+ FC_SPACING, FC_FILE, FC_INDEX,
+ FC_LANG, FC_CHARSET, FC_FOUNDRY, FC_SCALABLE, FC_PIXEL_SIZE, FC_WEIGHT,
+ FC_WIDTH,
+#if FC_VERSION >= 20297
+ FC_CAPABILITY,
+#endif
+ (const char *)0
+ };
+ const char **p = properties;
+ while (*p) {
+ FcObjectSetAdd(os, *p);
+ ++p;
+ }
+ fonts = FcFontList(0, pattern, os);
+ FcObjectSetDestroy(os);
+ FcPatternDestroy(pattern);
+ }
+
+ for (int i = 0; i < fonts->nfont; i++) {
+ if (FcPatternGetString(fonts->fonts[i], FC_FAMILY, 0, &value) != FcResultMatch)
+ continue;
+ // capitalize(value);
+ familyName = QString::fromUtf8((const char *)value);
+ slant_value = FC_SLANT_ROMAN;
+ weight_value = FC_WEIGHT_MEDIUM;
+ spacing_value = FC_PROPORTIONAL;
+ file_value = 0;
+ index_value = 0;
+ scalable = FcTrue;
+
+ if (FcPatternGetInteger (fonts->fonts[i], FC_SLANT, 0, &slant_value) != FcResultMatch)
+ slant_value = FC_SLANT_ROMAN;
+ if (FcPatternGetInteger (fonts->fonts[i], FC_WEIGHT, 0, &weight_value) != FcResultMatch)
+ weight_value = FC_WEIGHT_MEDIUM;
+ if (FcPatternGetInteger (fonts->fonts[i], FC_SPACING, 0, &spacing_value) != FcResultMatch)
+ spacing_value = FC_PROPORTIONAL;
+ if (FcPatternGetString (fonts->fonts[i], FC_FILE, 0, &file_value) != FcResultMatch)
+ file_value = 0;
+ if (FcPatternGetInteger (fonts->fonts[i], FC_INDEX, 0, &index_value) != FcResultMatch)
+ index_value = 0;
+ if (FcPatternGetBool(fonts->fonts[i], FC_SCALABLE, 0, &scalable) != FcResultMatch)
+ scalable = FcTrue;
+ if (FcPatternGetString(fonts->fonts[i], FC_FOUNDRY, 0, &foundry_value) != FcResultMatch)
+ foundry_value = 0;
+ if (FcPatternGetString(fonts->fonts[i], FC_STYLE, 0, &style_value) != FcResultMatch)
+ style_value = 0;
+ QtFontFamily *family = db->family(familyName, true);
+
+ FcLangSet *langset = 0;
+ FcResult res = FcPatternGetLangSet(fonts->fonts[i], FC_LANG, 0, &langset);
+ if (res == FcResultMatch) {
+ for (int i = 1; i < LanguageCount; ++i) {
+ const FcChar8 *lang = (const FcChar8*) languageForWritingSystem[i];
+ if (!lang) {
+ family->writingSystems[i] |= QtFontFamily::UnsupportedFT;
+ } else {
+ FcLangResult langRes = FcLangSetHasLang(langset, lang);
+ if (langRes != FcLangDifferentLang)
+ family->writingSystems[i] = QtFontFamily::Supported;
+ else
+ family->writingSystems[i] |= QtFontFamily::UnsupportedFT;
+ }
+ }
+ family->writingSystems[QFontDatabase::Other] = QtFontFamily::UnsupportedFT;
+ family->ftWritingSystemCheck = true;
+ } else {
+ // we set Other to supported for symbol fonts. It makes no
+ // sense to merge these with other ones, as they are
+ // special in a way.
+ for (int i = 1; i < LanguageCount; ++i)
+ family->writingSystems[i] |= QtFontFamily::UnsupportedFT;
+ family->writingSystems[QFontDatabase::Other] = QtFontFamily::Supported;
+ }
+
+ FcCharSet *cs = 0;
+ res = FcPatternGetCharSet(fonts->fonts[i], FC_CHARSET, 0, &cs);
+ if (res == FcResultMatch) {
+ // some languages are not supported by FontConfig, we rather check the
+ // charset to detect these
+ for (int i = 1; i < SampleCharCount; ++i) {
+ if (!sampleCharForWritingSystem[i])
+ continue;
+ if (FcCharSetHasChar(cs, sampleCharForWritingSystem[i]))
+ family->writingSystems[i] = QtFontFamily::Supported;
+ }
+ }
+
+#if FC_VERSION >= 20297
+ for (int j = 1; j < LanguageCount; ++j) {
+ if (family->writingSystems[j] == QtFontFamily::Supported && requiresOpenType(j) && openType[j]) {
+ FcChar8 *cap;
+ res = FcPatternGetString (fonts->fonts[i], FC_CAPABILITY, 0, &cap);
+ if (res != FcResultMatch || !strstr((const char *)cap, openType[j]))
+ family->writingSystems[j] = QtFontFamily::UnsupportedFT;
+ }
+ }
+#endif
+
+ QByteArray file((const char *)file_value);
+ family->fontFilename = file;
+ family->fontFileIndex = index_value;
+
+ QtFontStyle::Key styleKey;
+ QString styleName = style_value ? QString::fromUtf8((const char *) style_value) : QString();
+ styleKey.style = (slant_value == FC_SLANT_ITALIC)
+ ? QFont::StyleItalic
+ : ((slant_value == FC_SLANT_OBLIQUE)
+ ? QFont::StyleOblique
+ : QFont::StyleNormal);
+ styleKey.weight = getFCWeight(weight_value);
+ if (!scalable) {
+ int width = 100;
+ FcPatternGetInteger (fonts->fonts[i], FC_WIDTH, 0, &width);
+ styleKey.stretch = width;
+ }
+
+ QtFontFoundry *foundry
+ = family->foundry(foundry_value ? QString::fromUtf8((const char *)foundry_value) : QString(), true);
+ QtFontStyle *style = foundry->style(styleKey, styleName, true);
+
+ if (spacing_value < FC_MONO)
+ family->fixedPitch = false;
+
+ QtFontSize *size;
+ if (scalable) {
+ style->smoothScalable = true;
+ size = style->pixelSize(SMOOTH_SCALABLE, true);
+ } else {
+ double pixel_size = 0;
+ FcPatternGetDouble (fonts->fonts[i], FC_PIXEL_SIZE, 0, &pixel_size);
+ size = style->pixelSize((int)pixel_size, true);
+ }
+ QtFontEncoding *enc = size->encodingID(-1, 0, 0, 0, 0, true);
+ enc->pitch = (spacing_value >= FC_CHARCELL ? 'c' :
+ (spacing_value >= FC_MONO ? 'm' : 'p'));
+ }
+
+ FcFontSetDestroy (fonts);
+
+ struct FcDefaultFont {
+ const char *qtname;
+ const char *rawname;
+ bool fixed;
+ };
+ const FcDefaultFont defaults[] = {
+ { "Serif", "serif", false },
+ { "Sans Serif", "sans-serif", false },
+ { "Monospace", "monospace", true },
+ { 0, 0, false }
+ };
+ const FcDefaultFont *f = defaults;
+ while (f->qtname) {
+ QtFontFamily *family = db->family(QLatin1String(f->qtname), true);
+ family->fixedPitch = f->fixed;
+ family->synthetic = true;
+ QtFontFoundry *foundry = family->foundry(QString(), true);
+
+ // aliases only make sense for 'common', not for any of the specials
+ for (int i = 1; i < LanguageCount; ++i) {
+ if (requiresOpenType(i))
+ family->writingSystems[i] = QtFontFamily::UnsupportedFT;
+ else
+ family->writingSystems[i] = QtFontFamily::Supported;
+ }
+ family->writingSystems[QFontDatabase::Other] = QtFontFamily::UnsupportedFT;
+
+ QtFontStyle::Key styleKey;
+ for (int i = 0; i < 4; ++i) {
+ styleKey.style = (i%2) ? QFont::StyleNormal : QFont::StyleItalic;
+ styleKey.weight = (i > 1) ? QFont::Bold : QFont::Normal;
+ QtFontStyle *style = foundry->style(styleKey, QString(), true);
+ style->smoothScalable = true;
+ QtFontSize *size = style->pixelSize(SMOOTH_SCALABLE, true);
+ QtFontEncoding *enc = size->encodingID(-1, 0, 0, 0, 0, true);
+ enc->pitch = (f->fixed ? 'm' : 'p');
+ }
+ ++f;
+ }
+}
+#endif // QT_NO_FONTCONFIG
+
+static void initializeDb();
+
+static void load(const QString &family = QString(), int script = -1, bool forceXLFD = false)
+{
+ if (X11->has_fontconfig && !forceXLFD) {
+ initializeDb();
+ return;
+ }
+
+#ifdef QFONTDATABASE_DEBUG
+ QElapsedTimer t;
+ t.start();
+#endif
+
+ if (family.isNull() && script == -1) {
+ loadXlfds(0, -1);
+ } else {
+ if (family.isNull()) {
+ // load all families in all writing systems that match \a script
+ for (int ws = 1; ws < QFontDatabase::WritingSystemsCount; ++ws) {
+ if (scriptForWritingSystem[ws] != script)
+ continue;
+ for (int i = 0; i < numEncodings; ++i) {
+ if (writingSystems_for_xlfd_encoding[i][ws])
+ loadXlfds(0, i);
+ }
+ }
+ } else {
+ QtFontFamily *f = privateDb()->family(family);
+ // could reduce this further with some more magic:
+ // would need to remember the encodings loaded for the family.
+ if (!f || !f->xlfdLoaded)
+ loadXlfds(family.toLatin1(), -1);
+ }
+ }
+
+#ifdef QFONTDATABASE_DEBUG
+ FD_DEBUG("QFontDatabase: load(%s, %d) took %d ms",
+ family.toLatin1().constData(), script, t.elapsed());
+#endif
+}
+
+static void checkSymbolFont(QtFontFamily *family)
+{
+ if (!family || family->symbol_checked || family->fontFilename.isEmpty())
+ return;
+// qDebug() << "checking " << family->rawName;
+ family->symbol_checked = true;
+
+ QFontEngine::FaceId id;
+ id.filename = family->fontFilename;
+ id.index = family->fontFileIndex;
+ QFreetypeFace *f = QFreetypeFace::getFace(id);
+ if (!f) {
+ qWarning("checkSymbolFonts: Couldn't open face %s (%s/%d)",
+ qPrintable(family->name), family->fontFilename.data(), family->fontFileIndex);
+ return;
+ }
+ for (int i = 0; i < f->face->num_charmaps; ++i) {
+ FT_CharMap cm = f->face->charmaps[i];
+ if (cm->encoding == FT_ENCODING_ADOBE_CUSTOM
+ || cm->encoding == FT_ENCODING_MS_SYMBOL) {
+ for (int x = QFontDatabase::Latin; x < QFontDatabase::Other; ++x)
+ family->writingSystems[x] = QtFontFamily::Unsupported;
+ family->writingSystems[QFontDatabase::Other] = QtFontFamily::Supported;
+ break;
+ }
+ }
+ f->release(id);
+}
+
+static void checkSymbolFonts(const QString &family = QString())
+{
+#ifndef QT_NO_FONTCONFIG
+ QFontDatabasePrivate *d = privateDb();
+
+ if (family.isEmpty()) {
+ for (int i = 0; i < d->count; ++i)
+ checkSymbolFont(d->families[i]);
+ } else {
+ checkSymbolFont(d->family(family));
+ }
+#endif
+}
+
+static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt);
+
+static void initializeDb()
+{
+ QFontDatabasePrivate *db = privateDb();
+ if (!db || db->count)
+ return;
+
+ QElapsedTimer t;
+ t.start();
+
+#ifndef QT_NO_FONTCONFIG
+ if (db->reregisterAppFonts) {
+ db->reregisterAppFonts = false;
+ for (int i = 0; i < db->applicationFonts.count(); ++i)
+ if (!db->applicationFonts.at(i).families.isEmpty()) {
+ registerFont(&db->applicationFonts[i]);
+ }
+ }
+
+ loadFontConfig();
+ FD_DEBUG("QFontDatabase: loaded FontConfig: %d ms", int(t.elapsed()));
+#endif
+
+ t.start();
+
+#ifndef QT_NO_FONTCONFIG
+ for (int i = 0; i < db->count; i++) {
+ for (int j = 0; j < db->families[i]->count; ++j) { // each foundry
+ QtFontFoundry *foundry = db->families[i]->foundries[j];
+ for (int k = 0; k < foundry->count; ++k) {
+ QtFontStyle *style = foundry->styles[k];
+ if (style->key.style != QFont::StyleNormal) continue;
+
+ QtFontSize *size = style->pixelSize(SMOOTH_SCALABLE);
+ if (! size) continue; // should not happen
+ QtFontEncoding *enc = size->encodingID(-1, 0, 0, 0, 0, true);
+ if (! enc) continue; // should not happen either
+
+ QtFontStyle::Key key = style->key;
+
+ // does this style have an italic equivalent?
+ key.style = QFont::StyleItalic;
+ QtFontStyle *equiv = foundry->style(key);
+ if (equiv) continue;
+
+ // does this style have an oblique equivalent?
+ key.style = QFont::StyleOblique;
+ equiv = foundry->style(key);
+ if (equiv) continue;
+
+ // let's fake one...
+ equiv = foundry->style(key, QString(), true);
+ equiv->smoothScalable = true;
+
+ QtFontSize *equiv_size = equiv->pixelSize(SMOOTH_SCALABLE, true);
+ QtFontEncoding *equiv_enc = equiv_size->encodingID(-1, 0, 0, 0, 0, true);
+
+ // keep the same pitch
+ equiv_enc->pitch = enc->pitch;
+ }
+ }
+ }
+#endif
+
+
+#ifdef QFONTDATABASE_DEBUG
+#ifndef QT_NO_FONTCONFIG
+ if (!X11->has_fontconfig)
+#endif
+ // load everything at startup in debug mode.
+ loadXlfds(0, -1);
+
+ // print the database
+ for (int f = 0; f < db->count; f++) {
+ QtFontFamily *family = db->families[f];
+ FD_DEBUG("'%s' %s fixed=%s", family->name.latin1(), (family->fixedPitch ? "fixed" : ""),
+ (family->fixedPitch ? "yes" : "no"));
+ for (int i = 0; i < QFontDatabase::WritingSystemsCount; ++i) {
+ QFontDatabase::WritingSystem ws = QFontDatabase::WritingSystem(i);
+ FD_DEBUG("\t%s: %s", QFontDatabase::writingSystemName(ws).toLatin1().constData(),
+ ((family->writingSystems[i] & QtFontFamily::Supported) ? "Supported" :
+ (family->writingSystems[i] & QtFontFamily::Unsupported) == QtFontFamily::Unsupported ?
+ "Unsupported" : "Unknown"));
+ }
+
+ for (int fd = 0; fd < family->count; fd++) {
+ QtFontFoundry *foundry = family->foundries[fd];
+ FD_DEBUG("\t\t'%s'", foundry->name.latin1());
+ for (int s = 0; s < foundry->count; s++) {
+ QtFontStyle *style = foundry->styles[s];
+ FD_DEBUG("\t\t\tstyle: style=%d weight=%d (%s)\n"
+ "\t\t\tstretch=%d (%s)",
+ style->key.style, style->key.weight,
+ style->weightName, style->key.stretch,
+ style->setwidthName ? style->setwidthName : "nil");
+ if (style->smoothScalable)
+ FD_DEBUG("\t\t\t\tsmooth scalable");
+ else if (style->bitmapScalable)
+ FD_DEBUG("\t\t\t\tbitmap scalable");
+ if (style->pixelSizes) {
+ qDebug("\t\t\t\t%d pixel sizes", style->count);
+ for (int z = 0; z < style->count; ++z) {
+ QtFontSize *size = style->pixelSizes + z;
+ for (int e = 0; e < size->count; ++e) {
+ FD_DEBUG("\t\t\t\t size %5d pitch %c encoding %s",
+ size->pixelSize,
+ size->encodings[e].pitch,
+ xlfd_for_id(size->encodings[e].encoding));
+ }
+ }
+ }
+ }
+ }
+ }
+#endif // QFONTDATABASE_DEBUG
+}
+
+
+// --------------------------------------------------------------------------------------
+// font loader
+// --------------------------------------------------------------------------------------
+
+static const char *styleHint(const QFontDef &request)
+{
+ const char *stylehint = 0;
+ switch (request.styleHint) {
+ case QFont::SansSerif:
+ stylehint = "sans-serif";
+ break;
+ case QFont::Serif:
+ stylehint = "serif";
+ break;
+ case QFont::TypeWriter:
+ stylehint = "monospace";
+ break;
+ default:
+ if (request.fixedPitch)
+ stylehint = "monospace";
+ break;
+ }
+ return stylehint;
+}
+
+#ifndef QT_NO_FONTCONFIG
+
+void qt_addPatternProps(FcPattern *pattern, int screen, int script, const QFontDef &request)
+{
+ double size_value = qMax(qreal(1.), request.pixelSize);
+ FcPatternDel(pattern, FC_PIXEL_SIZE);
+ FcPatternAddDouble(pattern, FC_PIXEL_SIZE, size_value);
+
+ if (X11->display && QX11Info::appDepth(screen) <= 8) {
+ FcPatternDel(pattern, FC_ANTIALIAS);
+ // can't do antialiasing on 8bpp
+ FcPatternAddBool(pattern, FC_ANTIALIAS, false);
+ } else if (request.styleStrategy & (QFont::PreferAntialias|QFont::NoAntialias)) {
+ FcPatternDel(pattern, FC_ANTIALIAS);
+ FcPatternAddBool(pattern, FC_ANTIALIAS,
+ !(request.styleStrategy & QFont::NoAntialias));
+ }
+
+ if (script != QUnicodeTables::Common && *specialLanguages[script] != '\0') {
+ Q_ASSERT(script < QUnicodeTables::ScriptCount);
+ FcLangSet *ls = FcLangSetCreate();
+ FcLangSetAdd(ls, (const FcChar8*)specialLanguages[script]);
+ FcPatternDel(pattern, FC_LANG);
+ FcPatternAddLangSet(pattern, FC_LANG, ls);
+ FcLangSetDestroy(ls);
+ }
+
+ if (!request.styleName.isEmpty()) {
+ QByteArray cs = request.styleName.toUtf8();
+ FcPatternAddString(pattern, FC_STYLE, (const FcChar8 *) cs.constData());
+ return;
+ }
+
+ int weight_value = FC_WEIGHT_BLACK;
+ if (request.weight == 0)
+ weight_value = FC_WEIGHT_MEDIUM;
+ else if (request.weight < (QFont::Light + QFont::Normal) / 2)
+ weight_value = FC_WEIGHT_LIGHT;
+ else if (request.weight < (QFont::Normal + QFont::DemiBold) / 2)
+ weight_value = FC_WEIGHT_MEDIUM;
+ else if (request.weight < (QFont::DemiBold + QFont::Bold) / 2)
+ weight_value = FC_WEIGHT_DEMIBOLD;
+ else if (request.weight < (QFont::Bold + QFont::Black) / 2)
+ weight_value = FC_WEIGHT_BOLD;
+ FcPatternDel(pattern, FC_WEIGHT);
+ FcPatternAddInteger(pattern, FC_WEIGHT, weight_value);
+
+ int slant_value = FC_SLANT_ROMAN;
+ if (request.style == QFont::StyleItalic)
+ slant_value = FC_SLANT_ITALIC;
+ else if (request.style == QFont::StyleOblique)
+ slant_value = FC_SLANT_OBLIQUE;
+ FcPatternDel(pattern, FC_SLANT);
+ FcPatternAddInteger(pattern, FC_SLANT, slant_value);
+
+ int stretch = request.stretch;
+ if (!stretch)
+ stretch = 100;
+ FcPatternDel(pattern, FC_WIDTH);
+ FcPatternAddInteger(pattern, FC_WIDTH, stretch);
+}
+
+static bool preferScalable(const QFontDef &request)
+{
+ return request.styleStrategy & (QFont::PreferOutline|QFont::ForceOutline|QFont::PreferQuality|QFont::PreferAntialias);
+}
+
+
+static FcPattern *getFcPattern(const QFontPrivate *fp, int script, const QFontDef &request)
+{
+ if (!X11->has_fontconfig)
+ return 0;
+
+ FcPattern *pattern = FcPatternCreate();
+ if (!pattern)
+ return 0;
+
+ FcValue value;
+ value.type = FcTypeString;
+
+ QtFontDesc desc;
+ QStringList families_and_foundries = familyList(request);
+ for (int i = 0; i < families_and_foundries.size(); ++i) {
+ QString family, foundry;
+ parseFontName(families_and_foundries.at(i), foundry, family);
+ if (!family.isEmpty()) {
+ QByteArray cs = family.toUtf8();
+ value.u.s = (const FcChar8 *)cs.data();
+ FcPatternAdd(pattern, FC_FAMILY, value, FcTrue);
+ }
+ if (i == 0) {
+ QT_PREPEND_NAMESPACE(match)(script, request, family, foundry, -1, &desc);
+ if (!foundry.isEmpty()) {
+ QByteArray cs = foundry.toUtf8();
+ value.u.s = (const FcChar8 *)cs.data();
+ FcPatternAddWeak(pattern, FC_FOUNDRY, value, FcTrue);
+ }
+ }
+ }
+
+ const char *stylehint = styleHint(request);
+ if (stylehint) {
+ value.u.s = (const FcChar8 *)stylehint;
+ FcPatternAddWeak(pattern, FC_FAMILY, value, FcTrue);
+ }
+
+ if (!request.ignorePitch) {
+ char pitch_value = FC_PROPORTIONAL;
+ if (request.fixedPitch || (desc.family && desc.family->fixedPitch))
+ pitch_value = FC_MONO;
+ FcPatternAddInteger(pattern, FC_SPACING, pitch_value);
+ }
+ FcPatternAddBool(pattern, FC_OUTLINE, !(request.styleStrategy & QFont::PreferBitmap));
+ if (preferScalable(request) || (desc.style && desc.style->smoothScalable))
+ FcPatternAddBool(pattern, FC_SCALABLE, true);
+
+ qt_addPatternProps(pattern, fp->screen, script, request);
+
+ FcConfigSubstitute(0, pattern, FcMatchPattern);
+ FcDefaultSubstitute(pattern);
+
+ // these should only get added to the pattern _after_ substitution
+ // append the default fallback font for the specified script
+ extern QString qt_fallback_font_family(int);
+ QString fallback = qt_fallback_font_family(script);
+ if (!fallback.isEmpty()) {
+ QByteArray cs = fallback.toUtf8();
+ value.u.s = (const FcChar8 *)cs.data();
+ FcPatternAddWeak(pattern, FC_FAMILY, value, FcTrue);
+ }
+
+ // add the default family
+ QString defaultFamily = QApplication::font().family();
+ QByteArray cs = defaultFamily.toUtf8();
+ value.u.s = (const FcChar8 *)cs.data();
+ FcPatternAddWeak(pattern, FC_FAMILY, value, FcTrue);
+
+ // add QFont::defaultFamily() to the list, for compatibility with
+ // previous versions
+ defaultFamily = QApplication::font().defaultFamily();
+ cs = defaultFamily.toUtf8();
+ value.u.s = (const FcChar8 *)cs.data();
+ FcPatternAddWeak(pattern, FC_FAMILY, value, FcTrue);
+
+ return pattern;
+}
+
+
+static void FcFontSetRemove(FcFontSet *fs, int at)
+{
+ Q_ASSERT(at < fs->nfont);
+ FcPatternDestroy(fs->fonts[at]);
+ int len = (--fs->nfont - at) * sizeof(FcPattern *);;
+ if (len > 0)
+ memmove(fs->fonts + at, fs->fonts + at + 1, len);
+}
+
+static QFontEngine *tryPatternLoad(FcPattern *match, int screen,
+ const QFontDef &request, int script)
+{
+#ifdef FONT_MATCH_DEBUG
+ FcChar8 *fam;
+ FcPatternGetString(match, FC_FAMILY, 0, &fam);
+ FM_DEBUG("==== trying %s\n", fam);
+#endif
+ FM_DEBUG("passes charset test\n");
+
+ QFontEngineX11FT *engine = 0;
+ if (!match) // probably no fonts available.
+ goto done;
+
+ if (script != QUnicodeTables::Common) {
+ // skip font if it doesn't support the language we want
+ if (specialChars[script]) {
+ // need to check the charset, as the langset doesn't work for these scripts
+ FcCharSet *cs;
+ if (FcPatternGetCharSet(match, FC_CHARSET, 0, &cs) != FcResultMatch)
+ goto done;
+ if (!FcCharSetHasChar(cs, specialChars[script]))
+ goto done;
+ } else if (*specialLanguages[script] != '\0'){
+ FcLangSet *langSet = 0;
+ if (FcPatternGetLangSet(match, FC_LANG, 0, &langSet) != FcResultMatch)
+ goto done;
+ if (FcLangSetHasLang(langSet, (const FcChar8*)specialLanguages[script]) != FcLangEqual)
+ goto done;
+ }
+ }
+
+ // enforce non-antialiasing if requested. the ft font engine looks at this property.
+ if (request.styleStrategy & QFont::NoAntialias) {
+ FcPatternDel(match, FC_ANTIALIAS);
+ FcPatternAddBool(match, FC_ANTIALIAS, false);
+ }
+
+ engine = new QFontEngineX11FT(match, qt_FcPatternToQFontDef(match, request), screen);
+ if (engine->invalid()) {
+ FM_DEBUG(" --> invalid!\n");
+ delete engine;
+ engine = 0;
+ } else if (scriptRequiresOpenType(script)) {
+ HB_Face hbFace = engine->harfbuzzFace();
+ if (!hbFace || !hbFace->supported_scripts[script]) {
+ FM_DEBUG(" OpenType support missing for script\n");
+ delete engine;
+ engine = 0;
+ }
+ }
+done:
+ return engine;
+}
+
+FcFontSet *qt_fontSetForPattern(FcPattern *pattern, const QFontDef &request)
+{
+ FcResult result;
+ FcFontSet *fs = FcFontSort(0, pattern, FcTrue, 0, &result);
+#ifdef FONT_MATCH_DEBUG
+ FM_DEBUG("first font in fontset:\n");
+ FcPatternPrint(fs->fonts[0]);
+#endif
+
+ FcBool forceScalable = request.styleStrategy & QFont::ForceOutline;
+
+ // remove fonts if they are not scalable (and should be)
+ if (forceScalable && fs) {
+ for (int i = 0; i < fs->nfont; ++i) {
+ FcPattern *font = fs->fonts[i];
+ FcResult res;
+ FcBool scalable;
+ res = FcPatternGetBool(font, FC_SCALABLE, 0, &scalable);
+ if (res != FcResultMatch || !scalable) {
+ FcFontSetRemove(fs, i);
+#ifdef FONT_MATCH_DEBUG
+ FM_DEBUG("removing pattern:");
+ FcPatternPrint(font);
+#endif
+ --i; // go back one
+ }
+ }
+ }
+
+ FM_DEBUG("final pattern contains %d fonts\n", fs->nfont);
+
+ return fs;
+}
+
+static QFontEngine *loadFc(const QFontPrivate *fp, int script, const QFontDef &request)
+{
+ FM_DEBUG("===================== loadFc: script=%d family='%s'\n", script, request.family.toLatin1().data());
+ FcPattern *pattern = getFcPattern(fp, script, request);
+
+#ifdef FONT_MATCH_DEBUG
+ FM_DEBUG("\n\nfinal FcPattern contains:\n");
+ FcPatternPrint(pattern);
+#endif
+
+ QFontEngine *fe = 0;
+ FcResult res;
+ FcPattern *match = FcFontMatch(0, pattern, &res);
+ fe = tryPatternLoad(match, fp->screen, request, script);
+ if (!fe) {
+ FcFontSet *fs = qt_fontSetForPattern(pattern, request);
+
+ if (match) {
+ FcPatternDestroy(match);
+ match = 0;
+ }
+
+ if (fs) {
+ for (int i = 0; !fe && i < fs->nfont; ++i) {
+ match = FcFontRenderPrepare(NULL, pattern, fs->fonts[i]);
+ fe = tryPatternLoad(match, fp->screen, request, script);
+ if (fe)
+ break;
+ FcPatternDestroy(match);
+ match = 0;
+ }
+ FcFontSetDestroy(fs);
+ }
+ FM_DEBUG("engine for script %d is %s\n", script, fe ? fe->fontDef.family.toLatin1().data(): "(null)");
+ }
+ if (fe
+ && script == QUnicodeTables::Common
+ && !(request.styleStrategy & QFont::NoFontMerging) && !fe->symbol) {
+ fe = new QFontEngineMultiFT(fe, match, pattern, fp->screen, request);
+ } else {
+ FcPatternDestroy(pattern);
+ if (match)
+ FcPatternDestroy(match);
+ }
+ return fe;
+}
+
+static FcPattern *queryFont(const FcChar8 *file, const QByteArray &data, int id, FcBlanks *blanks, int *count)
+{
+#if FC_VERSION < 20402
+ Q_UNUSED(data)
+ return FcFreeTypeQuery(file, id, blanks, count);
+#else
+ if (data.isEmpty())
+ return FcFreeTypeQuery(file, id, blanks, count);
+
+ extern FT_Library qt_getFreetype();
+ FT_Library lib = qt_getFreetype();
+
+ FcPattern *pattern = 0;
+
+ FT_Face face;
+ if (!FT_New_Memory_Face(lib, (const FT_Byte *)data.constData(), data.size(), id, &face)) {
+ *count = face->num_faces;
+
+ pattern = FcFreeTypeQueryFace(face, file, id, blanks);
+
+ FT_Done_Face(face);
+ }
+
+ return pattern;
+#endif
+}
+#endif // QT_NO_FONTCONFIG
+
+static QFontEngine *loadRaw(const QFontPrivate *fp, const QFontDef &request)
+{
+ Q_ASSERT(fp && fp->rawMode);
+
+ QByteArray xlfd = request.family.toLatin1();
+ FM_DEBUG("Loading XLFD (rawmode) '%s'", xlfd.data());
+
+ QFontEngine *fe;
+ XFontStruct *xfs;
+ if (!(xfs = XLoadQueryFont(QX11Info::display(), xlfd.data())))
+ if (!(xfs = XLoadQueryFont(QX11Info::display(), "fixed")))
+ return 0;
+
+ fe = new QFontEngineXLFD(xfs, xlfd, 0);
+ if (! qt_fillFontDef(xfs, &fe->fontDef, fp->dpi, 0) &&
+ ! qt_fillFontDef(xlfd, &fe->fontDef, fp->dpi, 0))
+ fe->fontDef = QFontDef();
+ return fe;
+}
+
+QFontEngine *QFontDatabase::loadXlfd(int screen, int script, const QFontDef &request, int force_encoding_id)
+{
+ QMutexLocker locker(fontDatabaseMutex());
+
+ QtFontDesc desc;
+ FM_DEBUG() << "---> loadXlfd: request is" << request.family;
+ QStringList families_and_foundries = familyList(request);
+ const char *stylehint = styleHint(request);
+ if (stylehint)
+ families_and_foundries << QString::fromLatin1(stylehint);
+ families_and_foundries << QString();
+ FM_DEBUG() << "loadXlfd: list is" << families_and_foundries;
+ for (int i = 0; i < families_and_foundries.size(); ++i) {
+ QString family, foundry;
+ QT_PREPEND_NAMESPACE(parseFontName)(families_and_foundries.at(i), foundry, family);
+ FM_DEBUG("loadXlfd: >>>>>>>>>>>>>>trying to match '%s' encoding=%d", family.toLatin1().data(), force_encoding_id);
+ QT_PREPEND_NAMESPACE(match)(script, request, family, foundry, force_encoding_id, &desc, QList<int>(), true);
+ if (desc.family)
+ break;
+ }
+
+ QFontEngine *fe = 0;
+ if (force_encoding_id != -1
+ || (request.styleStrategy & QFont::NoFontMerging)
+ || (desc.family && desc.family->writingSystems[QFontDatabase::Symbol] & QtFontFamily::Supported)) {
+ if (desc.family) {
+ int px = desc.size->pixelSize;
+ if (desc.style->smoothScalable && px == SMOOTH_SCALABLE)
+ px = request.pixelSize;
+ else if (desc.style->bitmapScalable && px == 0)
+ px = request.pixelSize;
+
+ QByteArray xlfd("-");
+ xlfd += desc.foundry->name.isEmpty() ? QByteArray("*") : desc.foundry->name.toLatin1();
+ xlfd += '-';
+ xlfd += desc.family->name.isEmpty() ? QByteArray("*") : desc.family->name.toLatin1();
+ xlfd += '-';
+ xlfd += desc.style->weightName ? desc.style->weightName : "*";
+ xlfd += '-';
+ xlfd += (desc.style->key.style == QFont::StyleItalic
+ ? 'i'
+ : (desc.style->key.style == QFont::StyleOblique ? 'o' : 'r'));
+ xlfd += '-';
+ xlfd += desc.style->setwidthName ? desc.style->setwidthName : "*";
+ // ### handle add-style
+ xlfd += "-*-";
+ xlfd += QByteArray::number(px);
+ xlfd += '-';
+ xlfd += QByteArray::number(desc.encoding->xpoint);
+ xlfd += '-';
+ xlfd += QByteArray::number(desc.encoding->xres);
+ xlfd += '-';
+ xlfd += QByteArray::number(desc.encoding->yres);
+ xlfd += '-';
+ xlfd += desc.encoding->pitch;
+ xlfd += '-';
+ xlfd += QByteArray::number(desc.encoding->avgwidth);
+ xlfd += '-';
+ xlfd += xlfd_for_id(desc.encoding->encoding);
+
+ FM_DEBUG(" using XLFD: %s\n", xlfd.data());
+
+ const int mib = xlfd_encoding[desc.encoding->encoding].mib;
+ XFontStruct *xfs;
+ if ((xfs = XLoadQueryFont(QX11Info::display(), xlfd))) {
+ fe = new QFontEngineXLFD(xfs, xlfd, mib);
+ const int dpi = QX11Info::appDpiY();
+ if (!qt_fillFontDef(xfs, &fe->fontDef, dpi, &desc)
+ && !qt_fillFontDef(xlfd, &fe->fontDef, dpi, &desc)) {
+ initFontDef(desc, request, &fe->fontDef);
+ }
+ }
+ }
+ if (!fe) {
+ fe = new QFontEngineBox(request.pixelSize);
+ fe->fontDef = QFontDef();
+ }
+ } else {
+ QList<int> encodings;
+ if (desc.encoding) {
+ if (desc.encoding->encoding >= 0)
+ encodings.append(int(desc.encoding->encoding));
+ }
+
+ if (desc.size) {
+ // append all other encodings for the matched font
+ for (int i = 0; i < desc.size->count; ++i) {
+ QtFontEncoding *e = desc.size->encodings + i;
+ if (e == desc.encoding || e->encoding < 0)
+ continue;
+ encodings.append(int(e->encoding));
+ }
+ }
+ // fill in the missing encodings
+ const XlfdEncoding *enc = xlfd_encoding;
+ for (; enc->name; ++enc) {
+ if (!encodings.contains(enc->id) && enc->id >= 0) {
+ encodings.append(enc->id);
+ }
+ }
+
+#if defined(FONT_MATCH_DEBUG)
+ FM_DEBUG(" using MultiXLFD, encodings:");
+ for (int i = 0; i < encodings.size(); ++i) {
+ const int id = encodings.at(i);
+ FM_DEBUG(" %2d: %s", xlfd_encoding[id].id, xlfd_encoding[id].name);
+ }
+#endif
+
+ fe = new QFontEngineMultiXLFD(request, encodings, screen);
+ }
+ return fe;
+}
+
+#if (defined(QT_ARCH_ARM) || defined(QT_ARCH_ARMV6)) && defined(Q_CC_GNU) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 3)
+#define NEEDS_GCC_BUG_WORKAROUND
+#endif
+
+#ifdef NEEDS_GCC_BUG_WORKAROUND
+static inline void gccBugWorkaround(const QFontDef &req)
+{
+ char buffer[8];
+ snprintf(buffer, 8, "%f", req.pixelSize);
+}
+#endif
+
+/*! \internal
+ Loads a QFontEngine for the specified \a script that matches the
+ QFontDef \e request member variable.
+*/
+void QFontDatabase::load(const QFontPrivate *d, int script)
+{
+ Q_ASSERT(script >= 0 && script < QUnicodeTables::ScriptCount);
+
+ // normalize the request to get better caching
+ QFontDef req = d->request;
+ if (req.pixelSize <= 0)
+ req.pixelSize = qFloor(qt_pixelSize(req.pointSize, d->dpi) * 100.0 + 0.5) * 0.01;
+ if (req.pixelSize < 1)
+ req.pixelSize = 1;
+
+#ifdef NEEDS_GCC_BUG_WORKAROUND
+ // req.pixelSize ends up with a bogus value unless this workaround is called
+ gccBugWorkaround(req);
+#endif
+
+ if (req.weight == 0)
+ req.weight = QFont::Normal;
+ if (req.stretch == 0)
+ req.stretch = 100;
+
+ QFontCache::Key key(req, d->rawMode ? QUnicodeTables::Common : script, d->screen);
+ if (!d->engineData)
+ getEngineData(d, key);
+
+ // the cached engineData could have already loaded the engine we want
+ if (d->engineData->engines[script])
+ return;
+
+ // set it to the actual pointsize, so QFontInfo will do the right thing
+ if (req.pointSize < 0)
+ req.pointSize = qt_pointSize(req.pixelSize, d->dpi);
+
+
+ QFontEngine *fe = QFontCache::instance()->findEngine(key);
+
+ if (!fe) {
+ QMutexLocker locker(fontDatabaseMutex());
+ if (!privateDb()->count)
+ initializeDb();
+
+ const bool mainThread = (qApp->thread() == QThread::currentThread());
+ if (qt_enable_test_font && req.family == QLatin1String("__Qt__Box__Engine__")) {
+ fe = new QTestFontEngine(req.pixelSize);
+ fe->fontDef = req;
+ } else if (d->rawMode) {
+ if (mainThread)
+ fe = loadRaw(d, req);
+#ifndef QT_NO_FONTCONFIG
+ } else if (X11->has_fontconfig) {
+ fe = loadFc(d, script, req);
+#endif
+ } else if (mainThread && qt_is_gui_used) {
+ fe = loadXlfd(d->screen, script, req);
+ }
+ if (!fe) {
+ fe = new QFontEngineBox(req.pixelSize);
+ fe->fontDef = QFontDef();
+ }
+ }
+ if (fe->symbol || (d->request.styleStrategy & QFont::NoFontMerging)) {
+ for (int i = 0; i < QUnicodeTables::ScriptCount; ++i) {
+ if (!d->engineData->engines[i]) {
+ d->engineData->engines[i] = fe;
+ fe->ref.ref();
+ }
+ }
+ } else {
+ d->engineData->engines[script] = fe;
+ fe->ref.ref();
+ }
+ QFontCache::instance()->insertEngine(key, fe);
+}
+
+// Needed for fontconfig version < 2.2.97
+#ifndef FC_FAMILYLANG
+#define FC_FAMILYLANG "familylang"
+#endif
+
+static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt)
+{
+#if defined(QT_NO_FONTCONFIG)
+ return;
+#else
+ if (!X11->has_fontconfig)
+ return;
+
+ FcConfig *config = FcConfigGetCurrent();
+ if (!config)
+ return;
+
+ FcFontSet *set = FcConfigGetFonts(config, FcSetApplication);
+ if (!set) {
+ FcConfigAppFontAddFile(config, (const FcChar8 *)":/non-existent");
+ set = FcConfigGetFonts(config, FcSetApplication); // try again
+ if (!set)
+ return;
+ }
+
+ QString fileNameForQuery = fnt->fileName;
+#if FC_VERSION < 20402
+ QTemporaryFile tmp;
+
+ if (!fnt->data.isEmpty()) {
+ if (!tmp.open())
+ return;
+ tmp.write(fnt->data);
+ tmp.flush();
+ fileNameForQuery = tmp.fileName();
+ }
+#endif
+
+ int id = 0;
+ FcBlanks *blanks = FcConfigGetBlanks(0);
+ int count = 0;
+
+ QStringList families;
+ QFontDatabasePrivate *db = privateDb();
+
+ FcPattern *pattern = 0;
+ do {
+ pattern = queryFont((const FcChar8 *)QFile::encodeName(fileNameForQuery).constData(),
+ fnt->data, id, blanks, &count);
+ if (!pattern)
+ return;
+
+ FcPatternDel(pattern, FC_FILE);
+ QByteArray cs = fnt->fileName.toUtf8();
+ FcPatternAddString(pattern, FC_FILE, (const FcChar8 *) cs.constData());
+
+ FcChar8 *fam = 0, *familylang = 0;
+ int i, n = 0;
+ for (i = 0; ; i++) {
+ if (FcPatternGetString(pattern, FC_FAMILYLANG, i, &familylang) != FcResultMatch)
+ break;
+ QString familyLang = QString::fromUtf8((const char *) familylang);
+ if (familyLang.compare(db->systemLang, Qt::CaseInsensitive) == 0) {
+ n = i;
+ break;
+ }
+ }
+
+ if (FcPatternGetString(pattern, FC_FAMILY, n, &fam) == FcResultMatch) {
+ QString family = QString::fromUtf8(reinterpret_cast<const char *>(fam));
+ families << family;
+ }
+
+ if (!FcFontSetAdd(set, pattern))
+ return;
+
+ ++id;
+ } while (pattern && id < count);
+
+ fnt->families = families;
+#endif
+}
+
+bool QFontDatabase::removeApplicationFont(int handle)
+{
+#if defined(QT_NO_FONTCONFIG)
+ return false;
+#else
+ QMutexLocker locker(fontDatabaseMutex());
+
+ QFontDatabasePrivate *db = privateDb();
+ if (handle < 0 || handle >= db->applicationFonts.count())
+ return false;
+
+ FcConfigAppFontClear(0);
+
+ db->applicationFonts[handle] = QFontDatabasePrivate::ApplicationFont();
+
+ db->reregisterAppFonts = true;
+ db->invalidate();
+ return true;
+#endif
+}
+
+bool QFontDatabase::removeAllApplicationFonts()
+{
+#if defined(QT_NO_FONTCONFIG)
+ return false;
+#else
+ QMutexLocker locker(fontDatabaseMutex());
+
+ QFontDatabasePrivate *db = privateDb();
+ if (db->applicationFonts.isEmpty())
+ return false;
+
+ FcConfigAppFontClear(0);
+ db->applicationFonts.clear();
+ db->invalidate();
+ return true;
+#endif
+}
+
+bool QFontDatabase::supportsThreadedFontRendering()
+{
+#if defined(QT_NO_FONTCONFIG)
+ return false;
+#else
+ return X11->has_fontconfig;
+#endif
+}
+
+QString QFontDatabase::resolveFontFamilyAlias(const QString &family)
+{
+#if defined(QT_NO_FONTCONFIG)
+ return family;
+#else
+ FcPattern *pattern = FcPatternCreate();
+ if (!pattern)
+ return family;
+
+ QByteArray cs = family.toUtf8();
+ FcPatternAddString(pattern, FC_FAMILY, (const FcChar8 *) cs.constData());
+ FcConfigSubstitute(0, pattern, FcMatchPattern);
+ FcDefaultSubstitute(pattern);
+
+ FcChar8 *familyAfterSubstitution;
+ FcPatternGetString(pattern, FC_FAMILY, 0, &familyAfterSubstitution);
+ QString resolved = QString::fromUtf8((const char *) familyAfterSubstitution);
+ FcPatternDestroy(pattern);
+
+ return resolved;
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/x11/qfontengine_x11.cpp b/src/widgets/platforms/x11/qfontengine_x11.cpp
new file mode 100644
index 0000000000..be421976fa
--- /dev/null
+++ b/src/widgets/platforms/x11/qfontengine_x11.cpp
@@ -0,0 +1,1205 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbitmap.h"
+
+// #define FONTENGINE_DEBUG
+
+#include <qapplication.h>
+#include <qbytearray.h>
+#include <qdebug.h>
+#include <qtextcodec.h>
+#include <qthread.h>
+
+#include "qfontdatabase.h"
+#include "qpaintdevice.h"
+#include "qpainter.h"
+#include "qvarlengtharray.h"
+#include "qwidget.h"
+#include "qsettings.h"
+#include "qfile.h"
+
+#include <private/qpaintengine_x11_p.h>
+#include "qfont.h"
+#include "qfont_p.h"
+#include "qfontengine_p.h"
+#include <qhash.h>
+
+#include <private/qpainter_p.h>
+#include <private/qunicodetables_p.h>
+
+#include <private/qt_x11_p.h>
+#include <private/qpixmap_x11_p.h>
+#include "qx11info_x11.h"
+#include "qfontengine_x11_p.h"
+
+#include <limits.h>
+
+#include <ft2build.h>
+#if defined(FT_LCD_FILTER_H)
+#include FT_LCD_FILTER_H
+#endif
+
+#if defined(FC_LCD_FILTER)
+
+#ifndef FC_LCD_FILTER_NONE
+#define FC_LCD_FILTER_NONE FC_LCD_NONE
+#endif
+
+#ifndef FC_LCD_FILTER_DEFAULT
+#define FC_LCD_FILTER_DEFAULT FC_LCD_DEFAULT
+#endif
+
+#ifndef FC_LCD_FILTER_LIGHT
+#define FC_LCD_FILTER_LIGHT FC_LCD_LIGHT
+#endif
+
+#ifndef FC_LCD_FILTER_LEGACY
+#define FC_LCD_FILTER_LEGACY FC_LCD_LEGACY
+#endif
+
+#endif
+
+QT_BEGIN_NAMESPACE
+
+
+// ------------------------------------------------------------------
+// Multi XLFD engine
+// ------------------------------------------------------------------
+
+QFontEngineMultiXLFD::QFontEngineMultiXLFD(const QFontDef &r, const QList<int> &l, int s)
+ : QFontEngineMulti(l.size()), encodings(l), screen(s), request(r)
+{
+ loadEngine(0);
+ fontDef = engines[0]->fontDef;
+}
+
+QFontEngineMultiXLFD::~QFontEngineMultiXLFD()
+{ }
+
+void QFontEngineMultiXLFD::loadEngine(int at)
+{
+ Q_ASSERT(at < engines.size());
+ Q_ASSERT(engines.at(at) == 0);
+ const int encoding = encodings.at(at);
+ QFontEngine *fontEngine = QFontDatabase::loadXlfd(0, QUnicodeTables::Common, request, encoding);
+ Q_ASSERT(fontEngine != 0);
+ fontEngine->ref.ref();
+ engines[at] = fontEngine;
+}
+
+// ------------------------------------------------------------------
+// Xlfd font engine
+// ------------------------------------------------------------------
+
+#ifndef QT_NO_FREETYPE
+
+static QStringList *qt_fontpath = 0;
+
+static QStringList fontPath()
+{
+ if (qt_fontpath)
+ return *qt_fontpath;
+
+ // append qsettings fontpath
+ QSettings settings(QSettings::UserScope, QLatin1String("Trolltech"));
+ settings.beginGroup(QLatin1String("Qt"));
+
+ QStringList fontpath;
+
+ int npaths;
+ char** font_path;
+ font_path = XGetFontPath(X11->display, &npaths);
+ bool xfsconfig_read = false;
+ for (int i=0; i<npaths; i++) {
+ // If we're using xfs, append font paths from /etc/X11/fs/config
+ // can't hurt, and chances are we'll get all fonts that way.
+ if (((font_path[i])[0] != '/') && !xfsconfig_read) {
+ // We're using xfs -> read its config
+ bool finished = false;
+ QFile f(QLatin1String("/etc/X11/fs/config"));
+ if (!f.exists())
+ f.setFileName(QLatin1String("/usr/X11R6/lib/X11/fs/config"));
+ if (!f.exists())
+ f.setFileName(QLatin1String("/usr/X11/lib/X11/fs/config"));
+ if (f.exists()) {
+ f.open(QIODevice::ReadOnly);
+ while (f.error()==QFile::NoError && !finished) {
+ QString fs = QString::fromLocal8Bit(f.readLine(1024));
+ fs=fs.trimmed();
+ if (fs.left(9)==QLatin1String("catalogue") && fs.contains(QLatin1Char('='))) {
+ fs = fs.mid(fs.indexOf(QLatin1Char('=')) + 1).trimmed();
+ bool end = false;
+ while (f.error()==QFile::NoError && !end) {
+ if (fs[int(fs.length())-1] == QLatin1Char(','))
+ fs = fs.left(fs.length()-1);
+ else
+ end = true;
+
+ fs = fs.left(fs.indexOf(QLatin1String(":unscaled")));
+ if (fs[0] != QLatin1Char('#'))
+ fontpath += fs;
+ fs = QLatin1String(f.readLine(1024));
+ fs = fs.trimmed();
+ if (fs.isEmpty())
+ end = true;
+ }
+ finished = true;
+ }
+ }
+ f.close();
+ }
+ xfsconfig_read = true;
+ } else {
+ QString fs = QString::fromLocal8Bit(font_path[i]);
+ fontpath += fs.left(fs.indexOf(QLatin1String(":unscaled")));
+ }
+ }
+ XFreeFontPath(font_path);
+
+ // append qsettings fontpath
+ QStringList fp = settings.value(QLatin1String("fontPath")).toStringList();
+ if (!fp.isEmpty())
+ fontpath += fp;
+
+ qt_fontpath = new QStringList(fontpath);
+ return fontpath;
+}
+
+static QFontEngine::FaceId fontFile(const QByteArray &_xname, QFreetypeFace **freetype, int *synth)
+{
+ *freetype = 0;
+ *synth = 0;
+
+ QByteArray xname = _xname.toLower();
+
+ int pos = 0;
+ int minus = 0;
+ while (minus < 5 && (pos = xname.indexOf('-', pos + 1)))
+ ++minus;
+ QByteArray searchname = xname.left(pos);
+ while (minus < 12 && (pos = xname.indexOf('-', pos + 1)))
+ ++minus;
+ QByteArray encoding = xname.mid(pos + 1);
+ //qDebug("xname='%s', searchname='%s', encoding='%s'", xname.data(), searchname.data(), encoding.data());
+ QStringList fontpath = fontPath();
+ QFontEngine::FaceId face_id;
+ face_id.index = 0;
+
+ QByteArray best_mapping;
+
+ for (QStringList::ConstIterator it = fontpath.constBegin(); it != fontpath.constEnd(); ++it) {
+ if (!(*it).startsWith(QLatin1Char('/')))
+ continue; // not a path name, a font server
+ QString fontmapname;
+ int num = 0;
+ // search font.dir and font.scale for the right file
+ while (num < 2) {
+ if (num == 0)
+ fontmapname = (*it) + QLatin1String("/fonts.scale");
+ else
+ fontmapname = (*it) + QLatin1String("/fonts.dir");
+ ++num;
+ //qWarning(fontmapname);
+ QFile fontmap(fontmapname);
+ if (!fontmap.open(QIODevice::ReadOnly))
+ continue;
+ while (!fontmap.atEnd()) {
+ QByteArray mapping = fontmap.readLine();
+ QByteArray lmapping = mapping.toLower();
+
+ //qWarning(xfontname);
+ //qWarning(mapping);
+ if (!lmapping.contains(searchname))
+ continue;
+ int index = mapping.indexOf(' ');
+ QByteArray ffn = mapping.mid(0,index);
+ // remove bitmap formats freetype can't handle
+ if (ffn.contains(".spd") || ffn.contains(".phont"))
+ continue;
+ bool best_match = false;
+ if (!best_mapping.isEmpty()) {
+ if (lmapping.contains("-0-0-0-0-")) { // scalable font
+ best_match = true;
+ goto found;
+ }
+ if (lmapping.contains(encoding) && !best_mapping.toLower().contains(encoding))
+ goto found;
+ continue;
+ }
+
+ found:
+ int colon = ffn.lastIndexOf(':');
+ if (colon != -1) {
+ QByteArray s = ffn.left(colon);
+ ffn = ffn.mid(colon + 1);
+ if (s.contains("ds="))
+ *synth |= QFontEngine::SynthesizedBold;
+ if (s.contains("ai="))
+ *synth |= QFontEngine::SynthesizedItalic;
+ }
+ face_id.filename = (*it).toLocal8Bit() + '/' + ffn;
+ best_mapping = mapping;
+ if (best_match)
+ goto end;
+ }
+ }
+ }
+end:
+// qDebug("fontfile for %s is from '%s'\n got %s synth=%d", xname.data(),
+// best_mapping.data(), face_id.filename.data(), *synth);
+ *freetype = QFreetypeFace::getFace(face_id);
+ if (!*freetype) {
+ face_id.index = 0;
+ face_id.filename = QByteArray();
+ }
+ return face_id;
+}
+
+#endif // QT_NO_FREETYPE
+
+// defined in qfontdatabase_x11.cpp
+extern int qt_mib_for_xlfd_encoding(const char *encoding);
+extern int qt_xlfd_encoding_id(const char *encoding);
+
+static inline XCharStruct *charStruct(XFontStruct *xfs, uint ch)
+{
+ XCharStruct *xcs = 0;
+ unsigned char r = ch>>8;
+ unsigned char c = ch&0xff;
+ if (xfs->per_char &&
+ r >= xfs->min_byte1 &&
+ r <= xfs->max_byte1 &&
+ c >= xfs->min_char_or_byte2 &&
+ c <= xfs->max_char_or_byte2) {
+ xcs = xfs->per_char + ((r - xfs->min_byte1) *
+ (xfs->max_char_or_byte2 -
+ xfs->min_char_or_byte2 + 1)) +
+ (c - xfs->min_char_or_byte2);
+ if (xcs->width == 0 && xcs->ascent == 0 && xcs->descent == 0)
+ xcs = 0;
+ }
+ return xcs;
+}
+
+QFontEngineXLFD::QFontEngineXLFD(XFontStruct *fs, const QByteArray &name, int mib)
+ : _fs(fs), _name(name), _codec(0), _cmap(mib)
+{
+ if (_cmap) _codec = QTextCodec::codecForMib(_cmap);
+
+ cache_cost = (((fs->max_byte1 - fs->min_byte1) *
+ (fs->max_char_or_byte2 - fs->min_char_or_byte2 + 1)) +
+ fs->max_char_or_byte2 - fs->min_char_or_byte2);
+ cache_cost = ((fs->max_bounds.ascent + fs->max_bounds.descent) *
+ (fs->max_bounds.width * cache_cost / 8));
+ lbearing = SHRT_MIN;
+ rbearing = SHRT_MIN;
+ face_id.index = -1;
+ freetype = 0;
+ synth = 0;
+}
+
+QFontEngineXLFD::~QFontEngineXLFD()
+{
+ XFreeFont(QX11Info::display(), _fs);
+ _fs = 0;
+#ifndef QT_NO_FREETYPE
+ if (freetype)
+ freetype->release(face_id);
+#endif
+}
+
+bool QFontEngineXLFD::stringToCMap(const QChar *s, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const
+{
+ if (*nglyphs < len) {
+ *nglyphs = len;
+ return false;
+ }
+
+ // filter out surrogates, we can't handle them anyway with XLFD fonts
+ QVarLengthArray<ushort> _s(len);
+ QChar *str = (QChar *)_s.data();
+ for (int i = 0; i < len; ++i) {
+ if (i < len - 1
+ && s[i].unicode() >= 0xd800 && s[i].unicode() < 0xdc00
+ && s[i+1].unicode() >= 0xdc00 && s[i].unicode() < 0xe000) {
+ *str = QChar();
+ ++i;
+ } else {
+ *str = s[i];
+ }
+ ++str;
+ }
+
+ len = str - (QChar *)_s.data();
+ str = (QChar *)_s.data();
+
+ bool mirrored = flags & QTextEngine::RightToLeft;
+ if (_codec) {
+ bool haveNbsp = false;
+ for (int i = 0; i < len; i++)
+ if (str[i].unicode() == 0xa0) {
+ haveNbsp = true;
+ break;
+ }
+
+ QVarLengthArray<unsigned short> ch(len);
+ QChar *chars = (QChar *)ch.data();
+ if (haveNbsp || mirrored) {
+ for (int i = 0; i < len; i++)
+ chars[i] = (str[i].unicode() == 0xa0 ? 0x20 :
+ (mirrored ? QChar::mirroredChar(str[i].unicode()) : str[i].unicode()));
+ } else {
+ for (int i = 0; i < len; i++)
+ chars[i] = str[i].unicode();
+ }
+ QTextCodec::ConverterState state;
+ state.flags = QTextCodec::ConvertInvalidToNull;
+ QByteArray ba = _codec->fromUnicode(chars, len, &state);
+ if (ba.length() == 2*len) {
+ // double byte encoding
+ const uchar *data = (const uchar *)ba.constData();
+ for (int i = 0; i < len; i++) {
+ glyphs->glyphs[i] = ((ushort)data[0] << 8) + data[1];
+ data += 2;
+ }
+ } else {
+ const uchar *data = (const uchar *)ba.constData();
+ for (int i = 0; i < len; i++)
+ glyphs->glyphs[i] = (ushort)data[i];
+ }
+ } else {
+ int i = len;
+ const QChar *c = str + len;
+ if (mirrored) {
+ while (c != str)
+ glyphs->glyphs[--i] = (--c)->unicode() == 0xa0 ? 0x20 : QChar::mirroredChar(c->unicode());
+ } else {
+ while (c != str)
+ glyphs->glyphs[--i] = (--c)->unicode() == 0xa0 ? 0x20 : c->unicode();
+ }
+ }
+ *nglyphs = len;
+ glyphs->numGlyphs = len;
+
+ if (!(flags & QTextEngine::GlyphIndicesOnly))
+ recalcAdvances(glyphs, flags);
+ return true;
+}
+
+void QFontEngineXLFD::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags /*flags*/) const
+{
+ int i = glyphs->numGlyphs;
+ XCharStruct *xcs;
+ // inlined for better performance
+ if (!_fs->per_char) {
+ xcs = &_fs->min_bounds;
+ while (i != 0) {
+ --i;
+ const unsigned char r = glyphs->glyphs[i] >> 8;
+ const unsigned char c = glyphs->glyphs[i] & 0xff;
+ if (r >= _fs->min_byte1 &&
+ r <= _fs->max_byte1 &&
+ c >= _fs->min_char_or_byte2 &&
+ c <= _fs->max_char_or_byte2) {
+ glyphs->advances_x[i] = xcs->width;
+ } else {
+ glyphs->glyphs[i] = 0;
+ }
+ }
+ }
+ else if (!_fs->max_byte1) {
+ XCharStruct *base = _fs->per_char - _fs->min_char_or_byte2;
+ while (i != 0) {
+ unsigned int gl = glyphs->glyphs[--i];
+ xcs = (gl >= _fs->min_char_or_byte2 && gl <= _fs->max_char_or_byte2) ?
+ base + gl : 0;
+ if (!xcs || (!xcs->width && !xcs->ascent && !xcs->descent)) {
+ glyphs->glyphs[i] = 0;
+ } else {
+ glyphs->advances_x[i] = xcs->width;
+ }
+ }
+ }
+ else {
+ while (i != 0) {
+ xcs = charStruct(_fs, glyphs->glyphs[--i]);
+ if (!xcs) {
+ glyphs->glyphs[i] = 0;
+ } else {
+ glyphs->advances_x[i] = xcs->width;
+ }
+ }
+ }
+}
+
+glyph_metrics_t QFontEngineXLFD::boundingBox(const QGlyphLayout &glyphs)
+{
+ int i;
+
+ glyph_metrics_t overall;
+ // initialize with line height, we get the same behaviour on all platforms
+ overall.y = -ascent();
+ overall.height = ascent() + descent() + 1;
+ QFixed ymax;
+ QFixed xmax;
+ for (i = 0; i < glyphs.numGlyphs; i++) {
+ XCharStruct *xcs = charStruct(_fs, glyphs.glyphs[i]);
+ if (xcs) {
+ QFixed x = overall.xoff + glyphs.offsets[i].x + xcs->lbearing;
+ QFixed y = overall.yoff + glyphs.offsets[i].y - xcs->ascent;
+ overall.x = qMin(overall.x, x);
+ overall.y = qMin(overall.y, y);
+ // XCharStruct::rbearing is defined as distance from left edge to rightmost pixel
+ xmax = qMax(xmax, overall.xoff + glyphs.offsets[i].x + xcs->rbearing);
+ ymax = qMax(ymax, y + xcs->ascent + xcs->descent);
+ overall.xoff += glyphs.advances_x[i] + QFixed::fromFixed(glyphs.justifications[i].space_18d6);
+ } else {
+ QFixed size = _fs->ascent;
+ overall.x = qMin(overall.x, overall.xoff);
+ overall.y = qMin(overall.y, overall.yoff - size);
+ ymax = qMax(ymax, overall.yoff);
+ overall.xoff += size;
+ xmax = qMax(xmax, overall.xoff);
+ }
+ }
+ overall.height = qMax(overall.height, ymax - overall.y);
+ overall.width = xmax - overall.x;
+
+ return overall;
+}
+
+glyph_metrics_t QFontEngineXLFD::boundingBox(glyph_t glyph)
+{
+ glyph_metrics_t gm;
+ XCharStruct *xcs = charStruct(_fs, glyph);
+ if (xcs) {
+ // XCharStruct::rbearing is defined as distance from left edge to rightmost pixel
+ // XCharStruct::width is defined as the advance
+ gm = glyph_metrics_t(xcs->lbearing, -xcs->ascent, xcs->rbearing- xcs->lbearing, xcs->ascent + xcs->descent,
+ xcs->width, 0);
+ } else {
+ QFixed size = ascent();
+ gm = glyph_metrics_t(0, size, size, size, size, 0);
+ }
+ return gm;
+}
+
+QFixed QFontEngineXLFD::ascent() const
+{
+ return _fs->ascent;
+}
+
+QFixed QFontEngineXLFD::descent() const
+{
+ return (_fs->descent-1);
+}
+
+QFixed QFontEngineXLFD::leading() const
+{
+ QFixed l = QFixed(qMin<int>(_fs->ascent, _fs->max_bounds.ascent)
+ + qMin<int>(_fs->descent, _fs->max_bounds.descent)) * QFixed::fromReal(0.15);
+ return l.ceil();
+}
+
+qreal QFontEngineXLFD::maxCharWidth() const
+{
+ return _fs->max_bounds.width;
+}
+
+
+// Loads the font for the specified script
+static inline int maxIndex(XFontStruct *f) {
+ return (((f->max_byte1 - f->min_byte1) *
+ (f->max_char_or_byte2 - f->min_char_or_byte2 + 1)) +
+ f->max_char_or_byte2 - f->min_char_or_byte2);
+}
+
+qreal QFontEngineXLFD::minLeftBearing() const
+{
+ if (lbearing == SHRT_MIN) {
+ if (_fs->per_char) {
+ XCharStruct *cs = _fs->per_char;
+ int nc = maxIndex(_fs) + 1;
+ int mx = cs->lbearing;
+
+ for (int c = 1; c < nc; c++) {
+ // ignore the bearings for characters whose ink is
+ // completely outside the normal bounding box
+ if ((cs[c].lbearing <= 0 && cs[c].rbearing <= 0) ||
+ (cs[c].lbearing >= cs[c].width && cs[c].rbearing >= cs[c].width))
+ continue;
+
+ int nmx = cs[c].lbearing;
+
+ if (nmx < mx)
+ mx = nmx;
+ }
+
+ ((QFontEngineXLFD *)this)->lbearing = mx;
+ } else
+ ((QFontEngineXLFD *)this)->lbearing = _fs->min_bounds.lbearing;
+ }
+ return lbearing;
+}
+
+qreal QFontEngineXLFD::minRightBearing() const
+{
+ if (rbearing == SHRT_MIN) {
+ if (_fs->per_char) {
+ XCharStruct *cs = _fs->per_char;
+ int nc = maxIndex(_fs) + 1;
+ int mx = cs->rbearing;
+
+ for (int c = 1; c < nc; c++) {
+ // ignore the bearings for characters whose ink is
+ // completely outside the normal bounding box
+ if ((cs[c].lbearing <= 0 && cs[c].rbearing <= 0) ||
+ (cs[c].lbearing >= cs[c].width && cs[c].rbearing >= cs[c].width))
+ continue;
+
+ int nmx = cs[c].rbearing;
+
+ if (nmx < mx)
+ mx = nmx;
+ }
+
+ ((QFontEngineXLFD *)this)->rbearing = mx;
+ } else
+ ((QFontEngineXLFD *)this)->rbearing = _fs->min_bounds.rbearing;
+ }
+ return rbearing;
+}
+
+const char *QFontEngineXLFD::name() const
+{
+ return _name;
+}
+
+bool QFontEngineXLFD::canRender(const QChar *string, int len)
+{
+ QVarLengthGlyphLayoutArray glyphs(len);
+ int nglyphs = len;
+ if (stringToCMap(string, len, &glyphs, &nglyphs, 0) == false) {
+ glyphs.resize(nglyphs);
+ stringToCMap(string, len, &glyphs, &nglyphs, 0);
+ }
+
+ bool allExist = true;
+ for (int i = 0; i < nglyphs; i++) {
+ if (!glyphs.glyphs[i] || !charStruct(_fs, glyphs.glyphs[i])) {
+ allExist = false;
+ break;
+ }
+ }
+
+ return allExist;
+}
+
+QBitmap QFontEngineXLFD::bitmapForGlyphs(const QGlyphLayout &glyphs, const glyph_metrics_t &metrics, QTextItem::RenderFlags flags)
+{
+ int w = metrics.width.toInt();
+ int h = metrics.height.toInt();
+ if (w <= 0 || h <= 0)
+ return QBitmap();
+
+ QPlatformPixmap *data = new QX11PlatformPixmap(QPlatformPixmap::BitmapType);
+ data->resize(w, h);
+ QPixmap bm(data);
+ QPainter p(&bm);
+ p.fillRect(0, 0, w, h, Qt::color0);
+ p.setPen(Qt::color1);
+
+ QTextItemInt item;
+ item.flags = flags;
+ item.ascent = -metrics.y;
+ item.descent = metrics.height - item.ascent;
+ item.width = metrics.width;
+ item.chars = 0;
+ item.num_chars = 0;
+ item.logClusters = 0;
+ item.glyphs = glyphs;
+ item.fontEngine = this;
+ item.f = 0;
+
+ p.drawTextItem(QPointF(-metrics.x.toReal(), item.ascent.toReal()), item);
+ p.end();
+
+ return QBitmap(bm);
+}
+
+void QFontEngineXLFD::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags)
+{
+ // cannot use QFontEngine::addBitmapFontToPath(), since we don't
+ // have direct access to the glyph bitmaps, so we have to draw
+ // onto a QBitmap, then convert to QImage, then to path
+ glyph_metrics_t metrics = boundingBox(glyphs);
+
+ QImage image = bitmapForGlyphs(glyphs, metrics, flags).toImage();
+ if (image.isNull())
+ return;
+
+ image = image.convertToFormat(QImage::Format_Mono);
+ const uchar *image_data = image.bits();
+ uint bpl = image.bytesPerLine();
+ // from qfontengine.cpp
+ extern void qt_addBitmapToPath(qreal x0, qreal y0, const uchar *image_data,
+ int bpl, int w, int h, QPainterPath *path);
+ qt_addBitmapToPath(x, y + metrics.y.toReal(), image_data, bpl, image.width(), image.height(), path);
+}
+
+QFontEngine::FaceId QFontEngineXLFD::faceId() const
+{
+#ifndef QT_NO_FREETYPE
+ if (face_id.index == -1) {
+ face_id = fontFile(_name, &freetype, &synth);
+ if (_codec)
+ face_id.encoding = _codec->mibEnum();
+ if (freetype) {
+ const_cast<QFontEngineXLFD *>(this)->fsType = freetype->fsType();
+ } else {
+ face_id.index = 0;
+ face_id.filename = '-' + QFontEngine::properties().postscriptName;
+ }
+ }
+#endif
+
+ return face_id;
+}
+
+QFontEngine::Properties QFontEngineXLFD::properties() const
+{
+ if (face_id.index == -1)
+ (void)faceId();
+
+#ifndef QT_NO_FREETYPE
+ if (freetype)
+ return freetype->properties();
+#endif
+ return QFontEngine::properties();
+}
+
+void QFontEngineXLFD::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics)
+{
+ if (face_id.index == -1)
+ (void)faceId();
+#ifndef QT_NO_FREETYPE
+ if (!freetype)
+#endif
+ {
+ QFontEngine::getUnscaledGlyph(glyph, path, metrics);
+ return;
+ }
+
+#ifndef QT_NO_FREETYPE
+ freetype->lock();
+
+ FT_Face face = freetype->face;
+ FT_Set_Char_Size(face, face->units_per_EM << 6, face->units_per_EM << 6, 0, 0);
+ freetype->xsize = face->units_per_EM << 6;
+ freetype->ysize = face->units_per_EM << 6;
+ FT_Set_Transform(face, 0, 0);
+ glyph = glyphIndexToFreetypeGlyphIndex(glyph);
+ FT_Load_Glyph(face, glyph, FT_LOAD_NO_BITMAP);
+
+ int left = face->glyph->metrics.horiBearingX;
+ int right = face->glyph->metrics.horiBearingX + face->glyph->metrics.width;
+ int top = face->glyph->metrics.horiBearingY;
+ int bottom = face->glyph->metrics.horiBearingY - face->glyph->metrics.height;
+
+ QFixedPoint p;
+ p.x = 0;
+ p.y = 0;
+ metrics->width = QFixed::fromFixed(right-left);
+ metrics->height = QFixed::fromFixed(top-bottom);
+ metrics->x = QFixed::fromFixed(left);
+ metrics->y = QFixed::fromFixed(-top);
+ metrics->xoff = QFixed::fromFixed(face->glyph->advance.x);
+
+ if (!FT_IS_SCALABLE(freetype->face))
+ QFreetypeFace::addBitmapToPath(face->glyph, p, path);
+ else
+ QFreetypeFace::addGlyphToPath(face, face->glyph, p, path, face->units_per_EM << 6, face->units_per_EM << 6);
+
+ FT_Set_Transform(face, &freetype->matrix, 0);
+ freetype->unlock();
+#endif // QT_NO_FREETYPE
+}
+
+
+bool QFontEngineXLFD::getSfntTableData(uint tag, uchar *buffer, uint *length) const
+{
+#ifndef QT_NO_FREETYPE
+ if (face_id.index == -1)
+ (void)faceId();
+ if (!freetype)
+ return false;
+ return freetype->getSfntTable(tag, buffer, length);
+#else
+ Q_UNUSED(tag);
+ Q_UNUSED(buffer);
+ Q_UNUSED(length);
+ return false;
+#endif
+}
+
+int QFontEngineXLFD::synthesized() const
+{
+ return synth;
+}
+
+QImage QFontEngineXLFD::alphaMapForGlyph(glyph_t glyph)
+{
+ glyph_metrics_t metrics = boundingBox(glyph);
+
+/*
+ printf("a) w=%.2f, h=%.2f, xoff=%.2f, yoff=%.2f, x=%.2f, y=%.2f\n",
+ metrics.width.toReal(),
+ metrics.height.toReal(),
+ metrics.xoff.toReal(),
+ metrics.yoff.toReal(),
+ metrics.x.toReal(),
+ metrics.y.toReal());
+*/
+
+ QGlyphLayoutArray<1> glyphs;
+ glyphs.glyphs[0] = glyph;
+
+ QImage image = bitmapForGlyphs(glyphs, metrics).toImage();
+//image.save(QString::fromLatin1("x11cache-%1.png").arg((int)glyph));
+
+ image = image.convertToFormat(QImage::Format_Indexed8);
+ QVector<QRgb> colors(256);
+ for (int i = 0; i < 256; ++i)
+ colors[i] = qRgba(0, 0, 0, i);
+ image.setColorTable(colors);
+
+ int width = image.width();
+ int height = image.height();
+ for (int y = 0; y < height; ++y) {
+ uchar *bits = image.scanLine(y);
+ for (int x = 0; x < width; ++x)
+ bits[x] = ~(bits[x]-1);
+ }
+
+ return image;
+}
+
+#ifndef QT_NO_FREETYPE
+
+FT_Face QFontEngineXLFD::non_locked_face() const
+{
+ return freetype ? freetype->face : 0;
+}
+
+uint QFontEngineXLFD::toUnicode(glyph_t g) const
+{
+ if (_codec) {
+ QTextCodec::ConverterState state;
+ state.flags = QTextCodec::ConvertInvalidToNull;
+ uchar data[2];
+ int l = 1;
+ if (g > 255) {
+ data[0] = (g >> 8);
+ data[1] = (g & 255);
+ l = 2;
+ } else {
+ data[0] = g;
+ }
+ QString s = _codec->toUnicode((char *)data, l, &state);
+ Q_ASSERT(s.length() == 1);
+ g = s.at(0).unicode();
+ }
+ return g;
+}
+
+glyph_t QFontEngineXLFD::glyphIndexToFreetypeGlyphIndex(glyph_t g) const
+{
+ return FT_Get_Char_Index(freetype->face, toUnicode(g));
+}
+#endif
+
+#ifndef QT_NO_FONTCONFIG
+
+// ------------------------------------------------------------------
+// Multi FT engine
+// ------------------------------------------------------------------
+
+static QFontEngine *engineForPattern(FcPattern *match, const QFontDef &request, int screen)
+{
+ QFontEngineX11FT *engine = new QFontEngineX11FT(match, request, screen);
+ if (!engine->invalid())
+ return engine;
+
+ delete engine;
+ QFontEngine *fe = new QFontEngineBox(request.pixelSize);
+ fe->fontDef = request;
+ return fe;
+}
+
+QFontEngineMultiFT::QFontEngineMultiFT(QFontEngine *fe, FcPattern *matchedPattern, FcPattern *p, int s, const QFontDef &req)
+ : QFontEngineMulti(2), request(req), pattern(p), fontSet(0), screen(s)
+{
+ firstEnginePattern = FcPatternDuplicate(matchedPattern);
+ engines[0] = fe;
+ engines.at(0)->ref.ref();
+ fontDef = engines[0]->fontDef;
+ cache_cost = 100;
+ firstFontIndex = 1;
+}
+
+QFontEngineMultiFT::~QFontEngineMultiFT()
+{
+ extern QMutex *qt_fontdatabase_mutex();
+ QMutexLocker locker(qt_fontdatabase_mutex());
+
+ FcPatternDestroy(pattern);
+ if (firstEnginePattern)
+ FcPatternDestroy(firstEnginePattern);
+ if (fontSet)
+ FcFontSetDestroy(fontSet);
+}
+
+
+void QFontEngineMultiFT::loadEngine(int at)
+{
+ extern QMutex *qt_fontdatabase_mutex();
+ QMutexLocker locker(qt_fontdatabase_mutex());
+
+ extern QFontDef qt_FcPatternToQFontDef(FcPattern *pattern, const QFontDef &);
+ extern FcFontSet *qt_fontSetForPattern(FcPattern *pattern, const QFontDef &request);
+
+ Q_ASSERT(at > 0);
+ if (!fontSet) {
+ fontSet = qt_fontSetForPattern(pattern, request);
+
+ // it may happen that the fontset of fallbacks consists of only one font. In this case we
+ // have to fall back to the box fontengine as we cannot render the glyph.
+ if (fontSet->nfont == 1 && at == 1 && engines.size() == 2) {
+ Q_ASSERT(engines.at(at) == 0);
+ QFontEngine *fe = new QFontEngineBox(request.pixelSize);
+ fe->fontDef = request;
+ engines[at] = fe;
+ return;
+ }
+
+ if (firstEnginePattern) {
+
+ if (!FcPatternEqual(firstEnginePattern, fontSet->fonts[0]))
+ firstFontIndex = 0;
+
+ FcPatternDestroy(firstEnginePattern);
+ firstEnginePattern = 0;
+ }
+
+ engines.resize(fontSet->nfont + 1 - firstFontIndex);
+ }
+ Q_ASSERT(at < engines.size());
+ Q_ASSERT(engines.at(at) == 0);
+
+ FcPattern *match = FcFontRenderPrepare(NULL, pattern, fontSet->fonts[at + firstFontIndex - 1]);
+ QFontDef fontDef = qt_FcPatternToQFontDef(match, this->request);
+
+ // note: we use -1 for the script to make sure that we keep real
+ // FT engines separate from Multi engines in the font cache
+ QFontCache::Key key(fontDef, -1, screen);
+ QFontEngine *fontEngine = QFontCache::instance()->findEngine(key);
+ if (!fontEngine) {
+ fontEngine = engineForPattern(match, request, screen);
+ QFontCache::instance()->insertEngine(key, fontEngine);
+ }
+ FcPatternDestroy(match);
+ fontEngine->ref.ref();
+ engines[at] = fontEngine;
+}
+
+// ------------------------------------------------------------------
+// X11 FT engine
+// ------------------------------------------------------------------
+
+
+
+Q_WIDGETS_EXPORT void qt_x11ft_convert_pattern(FcPattern *pattern, QByteArray *file_name, int *index, bool *antialias)
+{
+ FcChar8 *fileName;
+ FcPatternGetString(pattern, FC_FILE, 0, &fileName);
+ *file_name = (const char *)fileName;
+ if (!FcPatternGetInteger(pattern, FC_INDEX, 0, index))
+ index = 0;
+ FcBool b;
+ if (FcPatternGetBool(pattern, FC_ANTIALIAS, 0, &b) == FcResultMatch)
+ *antialias = b;
+}
+
+
+QFontEngineX11FT::QFontEngineX11FT(FcPattern *pattern, const QFontDef &fd, int screen)
+ : QFontEngineFT(fd)
+{
+// FcPatternPrint(pattern);
+
+ bool antialias = X11->fc_antialias;
+ QByteArray file_name;
+ int face_index;
+ qt_x11ft_convert_pattern(pattern, &file_name, &face_index, &antialias);
+ QFontEngine::FaceId face_id;
+ face_id.filename = file_name;
+ face_id.index = face_index;
+
+ canUploadGlyphsToServer = QApplication::testAttribute(Qt::AA_X11InitThreads) || (qApp->thread() == QThread::currentThread());
+
+ subpixelType = Subpixel_None;
+ if (antialias) {
+ int subpixel = X11->display ? X11->screens[screen].subpixel : FC_RGBA_UNKNOWN;
+ if (subpixel == FC_RGBA_UNKNOWN)
+ (void) FcPatternGetInteger(pattern, FC_RGBA, 0, &subpixel);
+ if (!antialias || subpixel == FC_RGBA_UNKNOWN)
+ subpixel = FC_RGBA_NONE;
+
+ switch (subpixel) {
+ case FC_RGBA_NONE: subpixelType = Subpixel_None; break;
+ case FC_RGBA_RGB: subpixelType = Subpixel_RGB; break;
+ case FC_RGBA_BGR: subpixelType = Subpixel_BGR; break;
+ case FC_RGBA_VRGB: subpixelType = Subpixel_VRGB; break;
+ case FC_RGBA_VBGR: subpixelType = Subpixel_VBGR; break;
+ default: break;
+ }
+ }
+
+ if (fd.hintingPreference != QFont::PreferDefaultHinting) {
+ switch (fd.hintingPreference) {
+ case QFont::PreferNoHinting:
+ default_hint_style = HintNone;
+ break;
+ case QFont::PreferVerticalHinting:
+ default_hint_style = HintLight;
+ break;
+ case QFont::PreferFullHinting:
+ default:
+ default_hint_style = HintFull;
+ break;
+ }
+ }
+#ifdef FC_HINT_STYLE
+ else {
+ int hint_style = 0;
+ // Try to use Xft.hintstyle from XDefaults first if running in GNOME, to match
+ // the behavior of cairo
+ if (X11->fc_hint_style > -1 && X11->desktopEnvironment == DE_GNOME)
+ hint_style = X11->fc_hint_style;
+ else if (FcPatternGetInteger (pattern, FC_HINT_STYLE, 0, &hint_style) == FcResultNoMatch
+ && X11->fc_hint_style > -1)
+ hint_style = X11->fc_hint_style;
+
+ switch (hint_style) {
+ case FC_HINT_NONE:
+ default_hint_style = HintNone;
+ break;
+ case FC_HINT_SLIGHT:
+ default_hint_style = HintLight;
+ break;
+ case FC_HINT_MEDIUM:
+ default_hint_style = HintMedium;
+ break;
+ default:
+ default_hint_style = HintFull;
+ break;
+ }
+ }
+#endif
+
+#if defined(FC_AUTOHINT) && defined(FT_LOAD_FORCE_AUTOHINT)
+ {
+ bool autohint = false;
+
+ FcBool b;
+ if (FcPatternGetBool(pattern, FC_AUTOHINT, 0, &b) == FcResultMatch)
+ autohint = b;
+
+ if (autohint)
+ default_load_flags |= FT_LOAD_FORCE_AUTOHINT;
+ }
+#endif
+
+#if defined(FC_LCD_FILTER) && defined(FT_LCD_FILTER_H)
+ {
+ int filter = FC_LCD_FILTER_NONE;
+ if (FcPatternGetInteger(pattern, FC_LCD_FILTER, 0, &filter) == FcResultMatch) {
+ switch (filter) {
+ case FC_LCD_FILTER_NONE:
+ lcdFilterType = FT_LCD_FILTER_NONE;
+ break;
+ case FC_LCD_FILTER_DEFAULT:
+ lcdFilterType = FT_LCD_FILTER_DEFAULT;
+ break;
+ case FC_LCD_FILTER_LIGHT:
+ lcdFilterType = FT_LCD_FILTER_LIGHT;
+ break;
+ case FC_LCD_FILTER_LEGACY:
+ lcdFilterType = FT_LCD_FILTER_LEGACY;
+ break;
+ default:
+ // new unknown lcd filter type?!
+ break;
+ }
+ }
+ }
+#endif
+
+#ifdef FC_EMBEDDED_BITMAP
+ {
+ FcBool b;
+ if (FcPatternGetBool(pattern, FC_EMBEDDED_BITMAP, 0, &b) == FcResultMatch)
+ embeddedbitmap = b;
+ }
+#endif
+
+ GlyphFormat defaultFormat = Format_None;
+
+#ifndef QT_NO_XRENDER
+ if (X11->use_xrender) {
+ int format = PictStandardA8;
+ if (!antialias)
+ format = PictStandardA1;
+ else if (subpixelType == Subpixel_RGB
+ || subpixelType == Subpixel_BGR
+ || subpixelType == Subpixel_VRGB
+ || subpixelType == Subpixel_VBGR)
+ format = PictStandardARGB32;
+ xglyph_format = format;
+
+ if (subpixelType != QFontEngineFT::Subpixel_None)
+ defaultFormat = Format_A32;
+ else if (antialias)
+ defaultFormat = Format_A8;
+ else
+ defaultFormat = Format_Mono;
+ }
+#endif
+
+ if (!init(face_id, antialias, defaultFormat))
+ return;
+
+ if (!freetype->charset) {
+ FcCharSet *cs;
+ FcPatternGetCharSet (pattern, FC_CHARSET, 0, &cs);
+ freetype->charset = FcCharSetCopy(cs);
+ }
+}
+
+QFontEngineX11FT::~QFontEngineX11FT()
+{
+ freeGlyphSets();
+}
+
+unsigned long QFontEngineX11FT::allocateServerGlyphSet()
+{
+#ifndef QT_NO_XRENDER
+ if (!canUploadGlyphsToServer || !X11->use_xrender)
+ return 0;
+ return XRenderCreateGlyphSet(X11->display, XRenderFindStandardFormat(X11->display, xglyph_format));
+#else
+ return 0;
+#endif
+}
+
+void QFontEngineX11FT::freeServerGlyphSet(unsigned long id)
+{
+#ifndef QT_NO_XRENDER
+ if (!id)
+ return;
+ XRenderFreeGlyphSet(X11->display, id);
+#endif
+}
+
+bool QFontEngineX11FT::uploadGlyphToServer(QGlyphSet *set, uint glyphid, Glyph *g, GlyphInfo *info, int glyphDataSize) const
+{
+#ifndef QT_NO_XRENDER
+ if (!canUploadGlyphsToServer)
+ return false;
+ if (g->format == Format_Mono) {
+ /*
+ * swap bit order around; FreeType is always MSBFirst
+ */
+ if (BitmapBitOrder(X11->display) != MSBFirst) {
+ unsigned char *line = g->data;
+ int i = glyphDataSize;
+ while (i--) {
+ unsigned char c;
+ c = *line;
+ c = ((c << 1) & 0xaa) | ((c >> 1) & 0x55);
+ c = ((c << 2) & 0xcc) | ((c >> 2) & 0x33);
+ c = ((c << 4) & 0xf0) | ((c >> 4) & 0x0f);
+ *line++ = c;
+ }
+ }
+ }
+
+ ::Glyph xglyph = glyphid;
+ XRenderAddGlyphs (X11->display, set->id, &xglyph, info, 1, (const char *)g->data, glyphDataSize);
+ delete [] g->data;
+ g->data = 0;
+ g->format = Format_None;
+ g->uploadedToServer = true;
+ return true;
+#else
+ return false;
+#endif
+}
+
+QFontEngine *QFontEngineX11FT::cloneWithSize(qreal pixelSize) const
+{
+ QFontDef fontDef;
+ fontDef.pixelSize = pixelSize;
+ QFontEngineX11FT *fe = new QFontEngineX11FT(fontDef);
+ if (!fe->initFromFontEngine(this)) {
+ delete fe;
+ return 0;
+ } else {
+#ifndef QT_NO_XRENDER
+ fe->xglyph_format = xglyph_format;
+#endif
+ return fe;
+ }
+}
+
+#endif // QT_NO_FONTCONFIG
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/x11/qfontengine_x11_p.h b/src/widgets/platforms/x11/qfontengine_x11_p.h
new file mode 100644
index 0000000000..b12fbc28d4
--- /dev/null
+++ b/src/widgets/platforms/x11/qfontengine_x11_p.h
@@ -0,0 +1,180 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFONTENGINE_X11_P_H
+#define QFONTENGINE_X11_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+#include <private/qt_x11_p.h>
+
+#include <private/qfontengine_ft_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QFreetypeFace;
+
+// --------------------------------------------------------------------------
+
+class QFontEngineMultiXLFD : public QFontEngineMulti
+{
+public:
+ QFontEngineMultiXLFD(const QFontDef &r, const QList<int> &l, int s);
+ ~QFontEngineMultiXLFD();
+
+ void loadEngine(int at);
+
+private:
+ QList<int> encodings;
+ int screen;
+ QFontDef request;
+};
+
+/**
+ * \internal The font engine for X Logical Font Description (XLFD) fonts, which is for X11 systems without freetype.
+ */
+class QFontEngineXLFD : public QFontEngine
+{
+public:
+ QFontEngineXLFD(XFontStruct *f, const QByteArray &name, int mib);
+ ~QFontEngineXLFD();
+
+ virtual QFontEngine::FaceId faceId() const;
+ QFontEngine::Properties properties() const;
+ virtual void getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics);
+ virtual bool getSfntTableData(uint tag, uchar *buffer, uint *length) const;
+ virtual int synthesized() const;
+
+ virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs,
+ QTextEngine::ShaperFlags flags) const;
+ virtual void recalcAdvances(QGlyphLayout *, QTextEngine::ShaperFlags) const;
+
+ virtual glyph_metrics_t boundingBox(const QGlyphLayout &glyphs);
+ virtual glyph_metrics_t boundingBox(glyph_t glyph);
+
+ virtual void addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags);
+ virtual QFixed ascent() const;
+ virtual QFixed descent() const;
+ virtual QFixed leading() const;
+ virtual qreal maxCharWidth() const;
+ virtual qreal minLeftBearing() const;
+ virtual qreal minRightBearing() const;
+ virtual QImage alphaMapForGlyph(glyph_t);
+
+ virtual inline Type type() const
+ { return QFontEngine::XLFD; }
+
+ virtual bool canRender(const QChar *string, int len);
+ virtual const char *name() const;
+
+ inline XFontStruct *fontStruct() const
+ { return _fs; }
+
+#ifndef QT_NO_FREETYPE
+ FT_Face non_locked_face() const;
+ glyph_t glyphIndexToFreetypeGlyphIndex(glyph_t g) const;
+#endif
+ uint toUnicode(glyph_t g) const;
+
+private:
+ QBitmap bitmapForGlyphs(const QGlyphLayout &glyphs, const glyph_metrics_t &metrics, QTextItem::RenderFlags flags = 0);
+
+ XFontStruct *_fs;
+ QByteArray _name;
+ QTextCodec *_codec;
+ int _cmap;
+ int lbearing, rbearing;
+ mutable QFontEngine::FaceId face_id;
+ mutable QFreetypeFace *freetype;
+ mutable int synth;
+};
+
+#ifndef QT_NO_FONTCONFIG
+
+class Q_WIDGETS_EXPORT QFontEngineMultiFT : public QFontEngineMulti
+{
+public:
+ QFontEngineMultiFT(QFontEngine *fe, FcPattern *firstEnginePattern, FcPattern *p, int s, const QFontDef &request);
+ ~QFontEngineMultiFT();
+
+ void loadEngine(int at);
+
+private:
+ QFontDef request;
+ FcPattern *pattern;
+ FcPattern *firstEnginePattern;
+ FcFontSet *fontSet;
+ int screen;
+ int firstFontIndex; // first font in fontset
+};
+
+class Q_WIDGETS_EXPORT QFontEngineX11FT : public QFontEngineFT
+{
+public:
+ explicit QFontEngineX11FT(const QFontDef &fontDef) : QFontEngineFT(fontDef) {}
+ explicit QFontEngineX11FT(FcPattern *pattern, const QFontDef &fd, int screen);
+ ~QFontEngineX11FT();
+
+ QFontEngine *cloneWithSize(qreal pixelSize) const;
+
+#ifndef QT_NO_XRENDER
+ int xglyph_format;
+#endif
+
+protected:
+ virtual bool uploadGlyphToServer(QGlyphSet *set, uint glyphid, Glyph *g, GlyphInfo *info, int glyphDataSize) const;
+ virtual unsigned long allocateServerGlyphSet();
+ virtual void freeServerGlyphSet(unsigned long id);
+};
+
+#endif // QT_NO_FONTCONFIG
+
+QT_END_NAMESPACE
+
+#endif // QFONTENGINE_X11_P_H
diff --git a/src/widgets/platforms/x11/qkde.cpp b/src/widgets/platforms/x11/qkde.cpp
new file mode 100644
index 0000000000..b89951b5cd
--- /dev/null
+++ b/src/widgets/platforms/x11/qkde.cpp
@@ -0,0 +1,176 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include "qkde_p.h"
+#include <QtCore/QLibrary>
+#include <QtCore/QDir>
+#include <QtCore/qdebug.h>
+#include <QtCore/QSettings>
+#include "QtGui/qstylefactory.h"
+#include "qt_x11_p.h"
+
+#if defined(Q_WS_X11)
+
+QT_BEGIN_NAMESPACE
+
+/*! \internal
+Gets the current KDE home path
+like "/home/troll/.kde"
+*/
+QString QKde::kdeHome()
+{
+ static QString kdeHomePath;
+ if (kdeHomePath.isEmpty()) {
+ kdeHomePath = QString::fromLocal8Bit(qgetenv("KDEHOME"));
+ if (kdeHomePath.isEmpty()) {
+ QDir homeDir(QDir::homePath());
+ QString kdeConfDir(QLatin1String("/.kde"));
+ if (4 == X11->desktopVersion && homeDir.exists(QLatin1String(".kde4")))
+ kdeConfDir = QLatin1String("/.kde4");
+ kdeHomePath = QDir::homePath() + kdeConfDir;
+ }
+ }
+ return kdeHomePath;
+}
+
+/*!\internal
+ Reads the color from the config, and store it in the palette with the given color role if found
+ */
+static bool kdeColor(QPalette *pal, QPalette::ColorRole role, const QSettings &kdeSettings, const QString &kde4Key, const QString &kde3Key = QString())
+{
+ QVariant variant = kdeSettings.value(kde4Key);
+ if (!variant.isValid())
+ QVariant variant = kdeSettings.value(kde3Key);
+ if (variant.isValid()) {
+ QStringList values = variant.toStringList();
+ if (values.size() == 3) {
+ int r = values[0].toInt();
+ int g = values[1].toInt();
+ int b = values[2].toInt();
+ pal->setBrush(role, QColor(r, g, b));
+ return true;
+ }
+ }
+ return false;
+}
+
+
+/*!\internal
+ Returns the KDE palette
+*/
+QPalette QKde::kdePalette()
+{
+ const QSettings theKdeSettings(QKde::kdeHome() +
+ QLatin1String("/share/config/kdeglobals"), QSettings::IniFormat);
+ QPalette pal;
+
+ // Setup KDE palette
+ kdeColor(&pal, QPalette::Button, theKdeSettings, QLatin1String("Colors:Button/BackgroundNormal"), QLatin1String("buttonBackground"));
+ kdeColor(&pal, QPalette::Window, theKdeSettings, QLatin1String("Colors:Window/BackgroundNormal"), QLatin1String("background"));
+ kdeColor(&pal, QPalette::Text, theKdeSettings, QLatin1String("Colors:View/ForegroundNormal"), QLatin1String("foreground"));
+ kdeColor(&pal, QPalette::WindowText, theKdeSettings, QLatin1String("Colors:Window/ForegroundNormal"), QLatin1String("windowForeground"));
+ kdeColor(&pal, QPalette::Base, theKdeSettings, QLatin1String("Colors:View/BackgroundNormal"), QLatin1String("windowBackground"));
+ kdeColor(&pal, QPalette::Highlight, theKdeSettings, QLatin1String("Colors:Selection/BackgroundNormal"), QLatin1String("selectBackground"));
+ kdeColor(&pal, QPalette::HighlightedText, theKdeSettings, QLatin1String("Colors:Selection/ForegroundNormal"), QLatin1String("selectForeground"));
+ kdeColor(&pal, QPalette::AlternateBase, theKdeSettings, QLatin1String("Colors:View/BackgroundAlternate"), QLatin1String("alternateBackground"));
+ kdeColor(&pal, QPalette::ButtonText, theKdeSettings, QLatin1String("Colors:Button/ForegroundNormal"), QLatin1String("buttonForeground"));
+ kdeColor(&pal, QPalette::Link, theKdeSettings, QLatin1String("Colors:View/ForegroundLink"), QLatin1String("linkColor"));
+ kdeColor(&pal, QPalette::LinkVisited, theKdeSettings, QLatin1String("Colors:View/ForegroundVisited"), QLatin1String("visitedLinkColor"));
+ //## TODO tooltip color
+
+ return pal;
+}
+
+/*!\internal
+ Returns the name of the QStyle to use.
+ (read from the kde config if needed)
+*/
+QString QKde::kdeStyle()
+{
+ if (X11->desktopVersion >= 4) {
+ QSettings kdeSettings(QKde::kdeHome() + QLatin1String("/share/config/kdeglobals"), QSettings::IniFormat);
+ QString style = kdeSettings.value(QLatin1String("widgetStyle"), QLatin1String("Oxygen")).toString();
+
+ QStringList availableStyles = QStyleFactory::keys();
+ if(availableStyles.contains(style, Qt::CaseInsensitive))
+ return style;
+ }
+
+ if (X11->use_xrender)
+ return QLatin1String("plastique");
+ else
+ return QLatin1String("windows");
+}
+
+
+int QKde::kdeToolButtonStyle()
+{
+ QSettings settings(QKde::kdeHome() + QLatin1String("/share/config/kdeglobals"),
+ QSettings::IniFormat);
+ settings.beginGroup(QLatin1String("Toolbar style"));
+ QString toolbarStyle = settings.value(QLatin1String("ToolButtonStyle"), QLatin1String("TextBesideIcon")).toString();
+ if (toolbarStyle == QLatin1String("TextBesideIcon"))
+ return Qt::ToolButtonTextBesideIcon;
+ else if (toolbarStyle == QLatin1String("TextOnly"))
+ return Qt::ToolButtonTextOnly;
+ else if (toolbarStyle == QLatin1String("TextUnderIcon"))
+ return Qt::ToolButtonTextUnderIcon;
+
+ return Qt::ToolButtonTextBesideIcon;
+}
+
+int QKde::kdeToolBarIconSize()
+{
+ static int iconSize = -1;
+ if (iconSize == -1) {
+ QSettings settings(QKde::kdeHome() + QLatin1String("/share/config/kdeglobals"),
+ QSettings::IniFormat);
+ settings.beginGroup(QLatin1String("ToolbarIcons"));
+ iconSize = settings.value(QLatin1String("Size")).toInt();
+ }
+ return iconSize;
+}
+
+QT_END_NAMESPACE
+
+#endif //Q_WS_X11
+
diff --git a/src/widgets/platforms/x11/qkde_p.h b/src/widgets/platforms/x11/qkde_p.h
new file mode 100644
index 0000000000..5abe824a72
--- /dev/null
+++ b/src/widgets/platforms/x11/qkde_p.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QKDE_H
+#define QKDE_H
+
+#include <QtCore/qglobal.h>
+#include <QtGui/QPalette>
+#include <QtWidgets/QIcon>
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+#if defined(Q_WS_X11)
+
+
+QT_BEGIN_NAMESPACE
+
+/*!\internal
+ This namespace contains helper function to help KDE integration
+ They are only used if we detect the use of KDE and the KDE platform plugin is not found (old KDE version)
+ Or if the detected KDE version is KDE3
+*/
+namespace QKde {
+ QString kdeHome();
+ QString kdeStyle();
+ QPalette kdePalette();
+ int kdeToolButtonStyle();
+ int kdeToolBarIconSize();
+}
+
+
+QT_END_NAMESPACE
+
+#endif // Q_WS_X11
+#endif // QKDE_H
diff --git a/src/widgets/platforms/x11/qkeymapper_x11.cpp b/src/widgets/platforms/x11/qkeymapper_x11.cpp
new file mode 100644
index 0000000000..455840f8b2
--- /dev/null
+++ b/src/widgets/platforms/x11/qkeymapper_x11.cpp
@@ -0,0 +1,1869 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qkeymapper_p.h"
+
+#include "qdebug.h"
+#include "qtextcodec.h"
+#include "qwidget.h"
+
+#include "qapplication_p.h"
+#include "qevent_p.h"
+#include "qt_x11_p.h"
+
+#ifndef QT_NO_XKB
+# include <X11/XKBlib.h>
+#endif
+
+#define XK_MISCELLANY
+#define XK_LATIN1
+#define XK_KOREAN
+#define XK_XKB_KEYS
+#include <X11/keysymdef.h>
+
+#include <ctype.h>
+
+#ifdef QT_LINUXBASE
+// LSB's IsKeypadKey define is wrong - see
+// http://bugs.linuxbase.org/show_bug.cgi?id=2521
+#undef IsKeypadKey
+#define IsKeypadKey(keysym) \
+ (((KeySym)(keysym) >= XK_KP_Space) && ((KeySym)(keysym) <= XK_KP_Equal))
+
+#undef IsPrivateKeypadKey
+#define IsPrivateKeypadKey(keysym) \
+ (((KeySym)(keysym) >= 0x11000000) && ((KeySym)(keysym) <= 0x1100FFFF))
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_XKB
+
+// bring in the auto-generated xkbLayoutData
+#include "qkeymapper_x11_p.cpp"
+
+QLocale q_getKeyboardLocale(const QByteArray &layoutName, const QByteArray &variantName)
+{
+ int i = 0;
+ while (xkbLayoutData[i].layout != 0) {
+ if (layoutName == xkbLayoutData[i].layout && variantName == xkbLayoutData[i].variant)
+ return QLocale(xkbLayoutData[i].language, xkbLayoutData[i].country);
+ ++i;
+ }
+ return QLocale::c();
+}
+#endif // QT_NO_XKB
+
+// from qapplication_x11.cpp
+extern uchar qt_alt_mask;
+extern uchar qt_meta_mask;
+extern uchar qt_super_mask;
+extern uchar qt_hyper_mask;
+extern uchar qt_mode_switch_mask;
+uchar qt_num_lock_mask = 0;
+extern bool qt_sendSpontaneousEvent(QObject*, QEvent*);
+
+// ### we should really resolve conflicts with other masks by
+// ### decomposing the Qt::KeyboardModifers in possibleKeys()
+#define SETMASK(sym, mask) \
+ do { \
+ if (qt_alt_mask == 0 \
+ && qt_meta_mask != mask \
+ && qt_super_mask != mask \
+ && qt_hyper_mask != mask \
+ && (sym == XK_Alt_L || sym == XK_Alt_R)) { \
+ qt_alt_mask = mask; \
+ } \
+ if (qt_meta_mask == 0 \
+ && qt_alt_mask != mask \
+ && qt_super_mask != mask \
+ && qt_hyper_mask != mask \
+ && (sym == XK_Meta_L || sym == XK_Meta_R)) { \
+ qt_meta_mask = mask; \
+ } \
+ if (qt_super_mask == 0 \
+ && qt_alt_mask != mask \
+ && qt_meta_mask != mask \
+ && qt_hyper_mask != mask \
+ && (sym == XK_Super_L || sym == XK_Super_R)) { \
+ qt_super_mask = mask; \
+ } \
+ if (qt_hyper_mask == 0 \
+ && qt_alt_mask != mask \
+ && qt_meta_mask != mask \
+ && qt_super_mask != mask \
+ && (sym == XK_Hyper_L || sym == XK_Hyper_R)) { \
+ qt_hyper_mask = mask; \
+ } \
+ if (qt_mode_switch_mask == 0 \
+ && qt_alt_mask != mask \
+ && qt_meta_mask != mask \
+ && qt_super_mask != mask \
+ && qt_hyper_mask != mask \
+ && sym == XK_Mode_switch) { \
+ qt_mode_switch_mask = mask; \
+ } \
+ if (qt_num_lock_mask == 0 \
+ && sym == XK_Num_Lock) { \
+ qt_num_lock_mask = mask; \
+ } \
+ } while(false)
+
+// qt_XTranslateKey() is based on _XTranslateKey() taken from:
+
+/* $Xorg: KeyBind.c,v 1.4 2001/02/09 02:03:34 xorgcvs Exp $ */
+
+/*
+
+Copyright 1985, 1987, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+
+*/
+static int
+qt_XTranslateKey(register QXCoreDesc *dpy,
+ KeyCode keycode,
+ register unsigned int modifiers,
+ unsigned int *modifiers_return,
+ KeySym *keysym_return)
+{
+ int per;
+ register KeySym *syms;
+ KeySym sym, lsym, usym;
+
+ if (! dpy->keysyms)
+ return 0;
+ *modifiers_return = ((ShiftMask|LockMask)
+ | dpy->mode_switch | dpy->num_lock);
+ if (((int)keycode < dpy->min_keycode) || ((int)keycode > dpy->max_keycode))
+ {
+ *keysym_return = NoSymbol;
+ return 1;
+ }
+ per = dpy->keysyms_per_keycode;
+ syms = &dpy->keysyms[(keycode - dpy->min_keycode) * per];
+ while ((per > 2) && (syms[per - 1] == NoSymbol))
+ per--;
+ if ((per > 2) && (modifiers & dpy->mode_switch)) {
+ syms += 2;
+ per -= 2;
+ }
+ if ((modifiers & dpy->num_lock) &&
+ (per > 1 && (IsKeypadKey(syms[1]) || IsPrivateKeypadKey(syms[1])))) {
+ if ((modifiers & ShiftMask) ||
+ ((modifiers & LockMask) && (dpy->lock_meaning == XK_Shift_Lock)))
+ *keysym_return = syms[0];
+ else
+ *keysym_return = syms[1];
+ } else if (!(modifiers & ShiftMask) &&
+ (!(modifiers & LockMask) || (dpy->lock_meaning == NoSymbol))) {
+ if ((per == 1) || (syms[1] == NoSymbol))
+ XConvertCase(syms[0], keysym_return, &usym);
+ else
+ *keysym_return = syms[0];
+ } else if (!(modifiers & LockMask) ||
+ (dpy->lock_meaning != XK_Caps_Lock)) {
+ if ((per == 1) || ((usym = syms[1]) == NoSymbol))
+ XConvertCase(syms[0], &lsym, &usym);
+ *keysym_return = usym;
+ } else {
+ if ((per == 1) || ((sym = syms[1]) == NoSymbol))
+ sym = syms[0];
+ XConvertCase(sym, &lsym, &usym);
+ if (!(modifiers & ShiftMask) && (sym != syms[0]) &&
+ ((sym != usym) || (lsym == usym)))
+ XConvertCase(syms[0], &lsym, &usym);
+ *keysym_return = usym;
+ }
+ if (*keysym_return == XK_VoidSymbol)
+ *keysym_return = NoSymbol;
+ return 1;
+}
+
+
+
+
+QKeyMapperPrivate::QKeyMapperPrivate()
+ : keyboardInputDirection(Qt::LeftToRight), xkb_currentGroup(0)
+{
+ memset(&coreDesc, 0, sizeof(coreDesc));
+
+#ifndef QT_NO_XKB
+ if (X11->use_xkb) {
+ // get the current group
+ XkbStateRec xkbState;
+ if (XkbGetState(X11->display, XkbUseCoreKbd, &xkbState) == Success)
+ xkb_currentGroup = xkbState.group;
+ }
+#endif
+}
+
+QKeyMapperPrivate::~QKeyMapperPrivate()
+{
+ if (coreDesc.keysyms)
+ XFree(coreDesc.keysyms);
+}
+
+QList<int> QKeyMapperPrivate::possibleKeys(QKeyEvent *event)
+{
+#ifndef QT_NO_XKB
+ if (X11->use_xkb)
+ return possibleKeysXKB(event);
+#endif
+ return possibleKeysCore(event);
+}
+
+enum { MaxBits = sizeof(uint) * 8 };
+static QString translateKeySym(KeySym keysym, uint xmodifiers,
+ int &code, Qt::KeyboardModifiers &modifiers,
+ QByteArray &chars, int &count);
+
+QList<int> QKeyMapperPrivate::possibleKeysXKB(QKeyEvent *event)
+{
+#ifndef QT_NO_XKB
+ const int xkeycode = event->nativeScanCode();
+ const uint xmodifiers = event->nativeModifiers();
+
+ // first, translate key only using lock modifiers (there are no Qt equivalents for these, so we must
+ // always use them when determining the baseKeySym)
+ KeySym baseKeySym;
+ uint consumedModifiers;
+ if (!XkbLookupKeySym(X11->display, xkeycode, (xmodifiers & (LockMask | qt_num_lock_mask)),
+ &consumedModifiers, &baseKeySym))
+ return QList<int>();
+
+ QList<int> result;
+
+ // translate sym -> code
+ Qt::KeyboardModifiers baseModifiers = 0;
+ int baseCode = -1;
+ QByteArray chars;
+ int count = 0;
+ QString text = translateKeySym(baseKeySym, xmodifiers, baseCode, baseModifiers, chars, count);
+ if (baseCode == -1) {
+ if (text.isEmpty())
+ return QList<int>();
+ baseCode = text.at(0).unicode();
+ }
+
+ if (baseCode && baseCode < 0xfffe)
+ baseCode = QChar(baseCode).toUpper().unicode();
+ result += (baseCode | baseModifiers);
+
+ int pos1Bits[MaxBits];
+ int num1Bits = 0;
+
+ for (int i = 0; i < MaxBits; ++i) {
+ if (consumedModifiers & (1 << i))
+ pos1Bits[num1Bits++] = i;
+ }
+
+ const int numPerms = (1 << num1Bits);
+
+ // translate the key again using each permutation of consumedModifiers
+ for (int i = 1; i < numPerms; ++i) {
+ uint val = 0;
+ for (int j = 0; j < num1Bits; ++j) {
+ if (i & (1 << j))
+ val |= (1 << pos1Bits[j]);
+ }
+
+ if ((xmodifiers & val) != val)
+ continue;
+
+ KeySym sym;
+ uint mods;
+ if (!XkbLookupKeySym(X11->display, xkeycode, val, &mods, &sym))
+ continue;
+
+ // translate sym -> code
+ Qt::KeyboardModifiers modifiers = 0;
+ int code = -1;
+ chars.clear();
+ count = 0;
+ // mask out the modifiers needed to translate keycode
+ text = translateKeySym(sym, xmodifiers & ~val, code, modifiers, chars, count);
+ if (code == -1) {
+ if (text.isEmpty())
+ continue;
+ code = text.at(0).unicode();
+ }
+
+ if (code && code < 0xfffe)
+ code = QChar(code).toUpper().unicode();
+
+ if (code == Qt::Key_Tab && (baseModifiers & Qt::ShiftModifier)) {
+ // map shift+tab to shift+backtab
+ code = Qt::Key_Backtab;
+ text = QString();
+ }
+
+ if (code == baseCode)
+ continue;
+
+ result += (code | modifiers);
+ }
+
+#if 0
+ qDebug() << "possibleKeysXKB()" << hex << result;
+#endif
+ return result;
+#else
+ Q_UNUSED(event);
+ return QList<int>();
+#endif // QT_NO_XKB
+}
+
+QList<int> QKeyMapperPrivate::possibleKeysCore(QKeyEvent *event)
+{
+ const int xkeycode = event->nativeScanCode();
+ const uint xmodifiers = event->nativeModifiers();
+
+ // first, translate key only using lock modifiers (there are no Qt equivalents for these, so we must
+ // always use them when determining the baseKeySym)
+ KeySym baseKeySym;
+ uint consumedModifiers;
+ if (!qt_XTranslateKey(&coreDesc, xkeycode, (xmodifiers & (LockMask | qt_num_lock_mask)),
+ &consumedModifiers, &baseKeySym))
+ return QList<int>();
+
+ QList<int> result;
+
+ // translate sym -> code
+ Qt::KeyboardModifiers baseModifiers = 0;
+ int baseCode = -1;
+ QByteArray chars;
+ int count = 0;
+ QString text = translateKeySym(baseKeySym, xmodifiers, baseCode, baseModifiers, chars, count);
+ if (baseCode == -1) {
+ if (text.isEmpty())
+ return QList<int>();
+ baseCode = text.at(0).unicode();
+ }
+
+ if (baseCode && baseCode < 0xfffe)
+ baseCode = QChar(baseCode).toUpper().unicode();
+ result += (baseCode | baseModifiers);
+
+ int pos1Bits[MaxBits];
+ int num1Bits = 0;
+
+ for (int i = 0; i < MaxBits; ++i) {
+ if (consumedModifiers & (1 << i))
+ pos1Bits[num1Bits++] = i;
+ }
+
+ const int numPerms = (1 << num1Bits);
+
+ // translate the key again using each permutation of consumedModifiers
+ for (int i = 1; i < numPerms; ++i) {
+ uint val = 0;
+ for (int j = 0; j < num1Bits; ++j) {
+ if (i & (1 << j))
+ val |= (1 << pos1Bits[j]);
+ }
+
+ if ((xmodifiers & val) != val)
+ continue;
+
+ KeySym sym;
+ uint mods;
+ if (!qt_XTranslateKey(&coreDesc, xkeycode, val, &mods, &sym))
+ continue;
+
+ // translate sym -> code
+ Qt::KeyboardModifiers modifiers = 0;
+ int code = -1;
+ chars.clear();
+ count = 0;
+ // mask out the modifiers needed to translate keycode
+ text = translateKeySym(sym, xmodifiers & ~val, code, modifiers, chars, count);
+ if (code == -1) {
+ if (text.isEmpty())
+ continue;
+ code = text.at(0).unicode();
+ }
+
+ if (code && code < 0xfffe)
+ code = QChar(code).toUpper().unicode();
+
+ if (code == Qt::Key_Tab && (baseModifiers & Qt::ShiftModifier)) {
+ // map shift+tab to shift+backtab
+ code = Qt::Key_Backtab;
+ text = QString();
+ }
+
+ if (code == baseCode)
+ continue;
+
+ result += (code | modifiers);
+ }
+
+#if 0
+ qDebug() << "possibleKeysCore()" << hex << result;
+#endif
+ return result;
+}
+
+// for parsing the _XKB_RULES_NAMES property
+enum {
+ RulesFileIndex = 0,
+ ModelIndex = 1,
+ LayoutIndex = 2,
+ VariantIndex = 3,
+ OptionsIndex = 4
+};
+
+void QKeyMapperPrivate::clearMappings()
+{
+#ifndef QT_NO_XKB
+ if (X11->use_xkb) {
+ // try to determine the layout name and input direction by reading the _XKB_RULES_NAMES property off
+ // the root window
+ QByteArray layoutName;
+ QByteArray variantName;
+
+ Atom type = XNone;
+ int format = 0;
+ ulong nitems = 0;
+ ulong bytesAfter = 0;
+ uchar *data = 0;
+ if (XGetWindowProperty(X11->display, RootWindow(X11->display, 0), ATOM(_XKB_RULES_NAMES), 0, 1024,
+ false, XA_STRING, &type, &format, &nitems, &bytesAfter, &data) == Success
+ && type == XA_STRING && format == 8 && nitems > 2) {
+ /*
+ index 0 == rules file name
+ index 1 == model name
+ index 2 == layout name
+ index 3 == variant name
+ index 4 == options
+ */
+ char *names[5] = { 0, 0, 0, 0, 0 };
+ char *p = reinterpret_cast<char *>(data), *end = p + nitems;
+ int i = 0;
+ do {
+ names[i++] = p;
+ p += qstrlen(p) + 1;
+ } while (p < end);
+
+ // the layout names and variants are saved in the _XKB_RULES_NAMES property as a comma separated list
+ QList<QByteArray> layoutNames = QByteArray::fromRawData(names[2], qstrlen(names[2])).split(',');
+ if (uint(xkb_currentGroup) < uint(layoutNames.count()))
+ layoutName = layoutNames.at(xkb_currentGroup);
+ QList<QByteArray> variantNames = QByteArray::fromRawData(names[3], qstrlen(names[3])).split(',');
+ if (uint(xkb_currentGroup) < uint(variantNames.count()))
+ variantName = variantNames.at(xkb_currentGroup);
+ }
+
+ // ### ???
+ // if (keyboardLayoutName.isEmpty())
+ // qWarning("Qt: unable to determine keyboard layout, please talk to qt-info@nokia.com"); ?
+
+ keyboardInputLocale = q_getKeyboardLocale(layoutName, variantName);
+ keyboardInputDirection = keyboardInputLocale.textDirection();
+
+#if 0
+ qDebug() << "keyboard input locale ="
+ << keyboardInputLocale.name()
+ << "direction ="
+ << keyboardInputDirection;
+#endif
+ if (data)
+ XFree(data);
+ } else
+#endif // QT_NO_XKB
+ {
+ if (coreDesc.keysyms)
+ XFree(coreDesc.keysyms);
+
+ coreDesc.min_keycode = 8;
+ coreDesc.max_keycode = 255;
+ XDisplayKeycodes(X11->display, &coreDesc.min_keycode, &coreDesc.max_keycode);
+
+ coreDesc.keysyms_per_keycode = 0;
+ coreDesc.keysyms = XGetKeyboardMapping(X11->display,
+ coreDesc.min_keycode,
+ coreDesc.max_keycode - coreDesc.min_keycode + 1,
+ &coreDesc.keysyms_per_keycode);
+
+#if 0
+ qDebug() << "min_keycode =" << coreDesc.min_keycode;
+ qDebug() << "max_keycode =" << coreDesc.max_keycode;
+ qDebug() << "keysyms_per_keycode =" << coreDesc.keysyms_per_keycode;
+ qDebug() << "keysyms =" << coreDesc.keysyms;
+#endif
+
+ // ### cannot get/guess the locale with the core protocol
+ keyboardInputLocale = QLocale::c();
+ // ### could examine group 0 for RTL keys
+ keyboardInputDirection = Qt::LeftToRight;
+ }
+
+ qt_alt_mask = 0;
+ qt_meta_mask = 0;
+ qt_super_mask = 0;
+ qt_hyper_mask = 0;
+ qt_mode_switch_mask = 0;
+
+ // look at the modifier mapping, and get the correct masks for alt, meta, super, hyper, and mode_switch
+#ifndef QT_NO_XKB
+ if (X11->use_xkb) {
+ XkbDescPtr xkbDesc = XkbGetMap(X11->display, XkbAllClientInfoMask, XkbUseCoreKbd);
+ for (int i = xkbDesc->min_key_code; i < xkbDesc->max_key_code; ++i) {
+ const uint mask = xkbDesc->map->modmap ? xkbDesc->map->modmap[i] : 0;
+ if (mask == 0) {
+ // key is not bound to a modifier
+ continue;
+ }
+
+ for (int j = 0; j < XkbKeyGroupsWidth(xkbDesc, i); ++j) {
+ KeySym keySym = XkbKeySym(xkbDesc, i, j);
+ if (keySym == NoSymbol)
+ continue;
+ SETMASK(keySym, mask);
+ }
+ }
+ XkbFreeKeyboard(xkbDesc, XkbAllComponentsMask, true);
+ } else
+#endif // QT_NO_XKB
+ {
+ coreDesc.lock_meaning = NoSymbol;
+
+ XModifierKeymap *map = XGetModifierMapping(X11->display);
+
+ if (map) {
+ int i, maskIndex = 0, mapIndex = 0;
+ for (maskIndex = 0; maskIndex < 8; maskIndex++) {
+ for (i = 0; i < map->max_keypermod; i++) {
+ if (map->modifiermap[mapIndex]) {
+ KeySym sym;
+ int x = 0;
+ do {
+ sym = XKeycodeToKeysym(X11->display, map->modifiermap[mapIndex], x++);
+ } while (sym == NoSymbol && x < coreDesc.keysyms_per_keycode);
+ const uchar mask = 1 << maskIndex;
+ SETMASK(sym, mask);
+ }
+ mapIndex++;
+ }
+ }
+
+ // determine the meaning of the Lock modifier
+ for (i = 0; i < map->max_keypermod; ++i) {
+ for (int x = 0; x < coreDesc.keysyms_per_keycode; ++x) {
+ KeySym sym = XKeycodeToKeysym(X11->display, map->modifiermap[LockMapIndex], x);
+ if (sym == XK_Caps_Lock || sym == XK_ISO_Lock) {
+ coreDesc.lock_meaning = XK_Caps_Lock;
+ break;
+ } else if (sym == XK_Shift_Lock) {
+ coreDesc.lock_meaning = XK_Shift_Lock;
+ }
+ }
+ }
+
+ XFreeModifiermap(map);
+ }
+
+ // for qt_XTranslateKey()
+ coreDesc.num_lock = qt_num_lock_mask;
+ coreDesc.mode_switch = qt_mode_switch_mask;
+
+#if 0
+ qDebug() << "lock_meaning =" << coreDesc.lock_meaning;
+ qDebug() << "num_lock =" << coreDesc.num_lock;
+ qDebug() << "mode_switch =" << coreDesc.mode_switch;
+#endif
+ }
+
+ // set default modifier masks if needed
+ if( qt_alt_mask == 0 )
+ qt_alt_mask = Mod1Mask;
+ if( qt_meta_mask == 0 )
+ qt_meta_mask = Mod4Mask;
+
+ // if we don't have a meta key (or it's hidden behind alt), use super or hyper to generate
+ // Qt::Key_Meta and Qt::MetaModifier, since most newer XFree86/Xorg installations map the Windows
+ // key to Super
+ if (qt_meta_mask == 0 || qt_meta_mask == qt_alt_mask) {
+ // no meta keys... s,meta,super,
+ qt_meta_mask = qt_super_mask;
+ if (qt_meta_mask == 0 || qt_meta_mask == qt_alt_mask) {
+ // no super keys either? guess we'll use hyper then
+ qt_meta_mask = qt_hyper_mask;
+ }
+ }
+
+#if 0
+ qDebug() << "qt_alt_mask =" << hex << qt_alt_mask;
+ qDebug() << "qt_meta_mask =" << hex << qt_meta_mask;
+ qDebug() << "qt_super_mask =" << hex << qt_super_mask;
+ qDebug() << "qt_hyper_mask =" << hex << qt_hyper_mask;
+ qDebug() << "qt_mode_switch_mask =" << hex << qt_mode_switch_mask;
+ qDebug() << "qt_num_lock_mask =" << hex << qt_num_lock_mask;
+#endif
+}
+
+extern bool qt_sm_blockUserInput;
+
+//
+// Keyboard event translation
+//
+
+#ifndef XK_ISO_Left_Tab
+#define XK_ISO_Left_Tab 0xFE20
+#endif
+
+#ifndef XK_dead_hook
+#define XK_dead_hook 0xFE61
+#endif
+
+#ifndef XK_dead_horn
+#define XK_dead_horn 0xFE62
+#endif
+
+#ifndef XK_Codeinput
+#define XK_Codeinput 0xFF37
+#endif
+
+#ifndef XK_Kanji_Bangou
+#define XK_Kanji_Bangou 0xFF37 /* same as codeinput */
+#endif
+
+// Fix old X libraries
+#ifndef XK_KP_Home
+#define XK_KP_Home 0xFF95
+#endif
+#ifndef XK_KP_Left
+#define XK_KP_Left 0xFF96
+#endif
+#ifndef XK_KP_Up
+#define XK_KP_Up 0xFF97
+#endif
+#ifndef XK_KP_Right
+#define XK_KP_Right 0xFF98
+#endif
+#ifndef XK_KP_Down
+#define XK_KP_Down 0xFF99
+#endif
+#ifndef XK_KP_Prior
+#define XK_KP_Prior 0xFF9A
+#endif
+#ifndef XK_KP_Next
+#define XK_KP_Next 0xFF9B
+#endif
+#ifndef XK_KP_End
+#define XK_KP_End 0xFF9C
+#endif
+#ifndef XK_KP_Insert
+#define XK_KP_Insert 0xFF9E
+#endif
+#ifndef XK_KP_Delete
+#define XK_KP_Delete 0xFF9F
+#endif
+
+// the next lines are taken on 10/2009 from X.org (X11/XF86keysym.h), defining some special
+// multimedia keys. They are included here as not every system has them.
+#define XF86XK_MonBrightnessUp 0x1008FF02
+#define XF86XK_MonBrightnessDown 0x1008FF03
+#define XF86XK_KbdLightOnOff 0x1008FF04
+#define XF86XK_KbdBrightnessUp 0x1008FF05
+#define XF86XK_KbdBrightnessDown 0x1008FF06
+#define XF86XK_Standby 0x1008FF10
+#define XF86XK_AudioLowerVolume 0x1008FF11
+#define XF86XK_AudioMute 0x1008FF12
+#define XF86XK_AudioRaiseVolume 0x1008FF13
+#define XF86XK_AudioPlay 0x1008FF14
+#define XF86XK_AudioStop 0x1008FF15
+#define XF86XK_AudioPrev 0x1008FF16
+#define XF86XK_AudioNext 0x1008FF17
+#define XF86XK_HomePage 0x1008FF18
+#define XF86XK_Mail 0x1008FF19
+#define XF86XK_Start 0x1008FF1A
+#define XF86XK_Search 0x1008FF1B
+#define XF86XK_AudioRecord 0x1008FF1C
+#define XF86XK_Calculator 0x1008FF1D
+#define XF86XK_Memo 0x1008FF1E
+#define XF86XK_ToDoList 0x1008FF1F
+#define XF86XK_Calendar 0x1008FF20
+#define XF86XK_PowerDown 0x1008FF21
+#define XF86XK_ContrastAdjust 0x1008FF22
+#define XF86XK_Back 0x1008FF26
+#define XF86XK_Forward 0x1008FF27
+#define XF86XK_Stop 0x1008FF28
+#define XF86XK_Refresh 0x1008FF29
+#define XF86XK_PowerOff 0x1008FF2A
+#define XF86XK_WakeUp 0x1008FF2B
+#define XF86XK_Eject 0x1008FF2C
+#define XF86XK_ScreenSaver 0x1008FF2D
+#define XF86XK_WWW 0x1008FF2E
+#define XF86XK_Sleep 0x1008FF2F
+#define XF86XK_Favorites 0x1008FF30
+#define XF86XK_AudioPause 0x1008FF31
+#define XF86XK_AudioMedia 0x1008FF32
+#define XF86XK_MyComputer 0x1008FF33
+#define XF86XK_LightBulb 0x1008FF35
+#define XF86XK_Shop 0x1008FF36
+#define XF86XK_History 0x1008FF37
+#define XF86XK_OpenURL 0x1008FF38
+#define XF86XK_AddFavorite 0x1008FF39
+#define XF86XK_HotLinks 0x1008FF3A
+#define XF86XK_BrightnessAdjust 0x1008FF3B
+#define XF86XK_Finance 0x1008FF3C
+#define XF86XK_Community 0x1008FF3D
+#define XF86XK_AudioRewind 0x1008FF3E
+#define XF86XK_BackForward 0x1008FF3F
+#define XF86XK_Launch0 0x1008FF40
+#define XF86XK_Launch1 0x1008FF41
+#define XF86XK_Launch2 0x1008FF42
+#define XF86XK_Launch3 0x1008FF43
+#define XF86XK_Launch4 0x1008FF44
+#define XF86XK_Launch5 0x1008FF45
+#define XF86XK_Launch6 0x1008FF46
+#define XF86XK_Launch7 0x1008FF47
+#define XF86XK_Launch8 0x1008FF48
+#define XF86XK_Launch9 0x1008FF49
+#define XF86XK_LaunchA 0x1008FF4A
+#define XF86XK_LaunchB 0x1008FF4B
+#define XF86XK_LaunchC 0x1008FF4C
+#define XF86XK_LaunchD 0x1008FF4D
+#define XF86XK_LaunchE 0x1008FF4E
+#define XF86XK_LaunchF 0x1008FF4F
+#define XF86XK_ApplicationLeft 0x1008FF50
+#define XF86XK_ApplicationRight 0x1008FF51
+#define XF86XK_Book 0x1008FF52
+#define XF86XK_CD 0x1008FF53
+#define XF86XK_Calculater 0x1008FF54
+#define XF86XK_Clear 0x1008FF55
+#define XF86XK_ClearGrab 0x1008FE21
+#define XF86XK_Close 0x1008FF56
+#define XF86XK_Copy 0x1008FF57
+#define XF86XK_Cut 0x1008FF58
+#define XF86XK_Display 0x1008FF59
+#define XF86XK_DOS 0x1008FF5A
+#define XF86XK_Documents 0x1008FF5B
+#define XF86XK_Excel 0x1008FF5C
+#define XF86XK_Explorer 0x1008FF5D
+#define XF86XK_Game 0x1008FF5E
+#define XF86XK_Go 0x1008FF5F
+#define XF86XK_iTouch 0x1008FF60
+#define XF86XK_LogOff 0x1008FF61
+#define XF86XK_Market 0x1008FF62
+#define XF86XK_Meeting 0x1008FF63
+#define XF86XK_MenuKB 0x1008FF65
+#define XF86XK_MenuPB 0x1008FF66
+#define XF86XK_MySites 0x1008FF67
+#define XF86XK_News 0x1008FF69
+#define XF86XK_OfficeHome 0x1008FF6A
+#define XF86XK_Option 0x1008FF6C
+#define XF86XK_Paste 0x1008FF6D
+#define XF86XK_Phone 0x1008FF6E
+#define XF86XK_Reply 0x1008FF72
+#define XF86XK_Reload 0x1008FF73
+#define XF86XK_RotateWindows 0x1008FF74
+#define XF86XK_RotationPB 0x1008FF75
+#define XF86XK_RotationKB 0x1008FF76
+#define XF86XK_Save 0x1008FF77
+#define XF86XK_Send 0x1008FF7B
+#define XF86XK_Spell 0x1008FF7C
+#define XF86XK_SplitScreen 0x1008FF7D
+#define XF86XK_Support 0x1008FF7E
+#define XF86XK_TaskPane 0x1008FF7F
+#define XF86XK_Terminal 0x1008FF80
+#define XF86XK_Tools 0x1008FF81
+#define XF86XK_Travel 0x1008FF82
+#define XF86XK_Video 0x1008FF87
+#define XF86XK_Word 0x1008FF89
+#define XF86XK_Xfer 0x1008FF8A
+#define XF86XK_ZoomIn 0x1008FF8B
+#define XF86XK_ZoomOut 0x1008FF8C
+#define XF86XK_Away 0x1008FF8D
+#define XF86XK_Messenger 0x1008FF8E
+#define XF86XK_WebCam 0x1008FF8F
+#define XF86XK_MailForward 0x1008FF90
+#define XF86XK_Pictures 0x1008FF91
+#define XF86XK_Music 0x1008FF92
+#define XF86XK_Battery 0x1008FF93
+#define XF86XK_Bluetooth 0x1008FF94
+#define XF86XK_WLAN 0x1008FF95
+#define XF86XK_UWB 0x1008FF96
+#define XF86XK_AudioForward 0x1008FF97
+#define XF86XK_AudioRepeat 0x1008FF98
+#define XF86XK_AudioRandomPlay 0x1008FF99
+#define XF86XK_Subtitle 0x1008FF9A
+#define XF86XK_AudioCycleTrack 0x1008FF9B
+#define XF86XK_Time 0x1008FF9F
+#define XF86XK_Select 0x1008FFA0
+#define XF86XK_View 0x1008FFA1
+#define XF86XK_TopMenu 0x1008FFA2
+#define XF86XK_Suspend 0x1008FFA7
+#define XF86XK_Hibernate 0x1008FFA8
+
+
+// end of XF86keysyms.h
+
+// Special keys used by Qtopia, mapped into the X11 private keypad range.
+#define QTOPIAXK_Select 0x11000601
+#define QTOPIAXK_Yes 0x11000602
+#define QTOPIAXK_No 0x11000603
+#define QTOPIAXK_Cancel 0x11000604
+#define QTOPIAXK_Printer 0x11000605
+#define QTOPIAXK_Execute 0x11000606
+#define QTOPIAXK_Sleep 0x11000607
+#define QTOPIAXK_Play 0x11000608
+#define QTOPIAXK_Zoom 0x11000609
+#define QTOPIAXK_Context1 0x1100060A
+#define QTOPIAXK_Context2 0x1100060B
+#define QTOPIAXK_Context3 0x1100060C
+#define QTOPIAXK_Context4 0x1100060D
+#define QTOPIAXK_Call 0x1100060E
+#define QTOPIAXK_Hangup 0x1100060F
+#define QTOPIAXK_Flip 0x11000610
+
+// keyboard mapping table
+static const unsigned int KeyTbl[] = {
+
+ // misc keys
+
+ XK_Escape, Qt::Key_Escape,
+ XK_Tab, Qt::Key_Tab,
+ XK_ISO_Left_Tab, Qt::Key_Backtab,
+ XK_BackSpace, Qt::Key_Backspace,
+ XK_Return, Qt::Key_Return,
+ XK_Insert, Qt::Key_Insert,
+ XK_Delete, Qt::Key_Delete,
+ XK_Clear, Qt::Key_Delete,
+ XK_Pause, Qt::Key_Pause,
+ XK_Print, Qt::Key_Print,
+ 0x1005FF60, Qt::Key_SysReq, // hardcoded Sun SysReq
+ 0x1007ff00, Qt::Key_SysReq, // hardcoded X386 SysReq
+
+ // cursor movement
+
+ XK_Home, Qt::Key_Home,
+ XK_End, Qt::Key_End,
+ XK_Left, Qt::Key_Left,
+ XK_Up, Qt::Key_Up,
+ XK_Right, Qt::Key_Right,
+ XK_Down, Qt::Key_Down,
+ XK_Prior, Qt::Key_PageUp,
+ XK_Next, Qt::Key_PageDown,
+
+ // modifiers
+
+ XK_Shift_L, Qt::Key_Shift,
+ XK_Shift_R, Qt::Key_Shift,
+ XK_Shift_Lock, Qt::Key_Shift,
+ XK_Control_L, Qt::Key_Control,
+ XK_Control_R, Qt::Key_Control,
+ XK_Meta_L, Qt::Key_Meta,
+ XK_Meta_R, Qt::Key_Meta,
+ XK_Alt_L, Qt::Key_Alt,
+ XK_Alt_R, Qt::Key_Alt,
+ XK_Caps_Lock, Qt::Key_CapsLock,
+ XK_Num_Lock, Qt::Key_NumLock,
+ XK_Scroll_Lock, Qt::Key_ScrollLock,
+ XK_Super_L, Qt::Key_Super_L,
+ XK_Super_R, Qt::Key_Super_R,
+ XK_Menu, Qt::Key_Menu,
+ XK_Hyper_L, Qt::Key_Hyper_L,
+ XK_Hyper_R, Qt::Key_Hyper_R,
+ XK_Help, Qt::Key_Help,
+ 0x1000FF74, Qt::Key_Backtab, // hardcoded HP backtab
+ 0x1005FF10, Qt::Key_F11, // hardcoded Sun F36 (labeled F11)
+ 0x1005FF11, Qt::Key_F12, // hardcoded Sun F37 (labeled F12)
+
+ // numeric and function keypad keys
+
+ XK_KP_Space, Qt::Key_Space,
+ XK_KP_Tab, Qt::Key_Tab,
+ XK_KP_Enter, Qt::Key_Enter,
+ //XK_KP_F1, Qt::Key_F1,
+ //XK_KP_F2, Qt::Key_F2,
+ //XK_KP_F3, Qt::Key_F3,
+ //XK_KP_F4, Qt::Key_F4,
+ XK_KP_Home, Qt::Key_Home,
+ XK_KP_Left, Qt::Key_Left,
+ XK_KP_Up, Qt::Key_Up,
+ XK_KP_Right, Qt::Key_Right,
+ XK_KP_Down, Qt::Key_Down,
+ XK_KP_Prior, Qt::Key_PageUp,
+ XK_KP_Next, Qt::Key_PageDown,
+ XK_KP_End, Qt::Key_End,
+ XK_KP_Begin, Qt::Key_Clear,
+ XK_KP_Insert, Qt::Key_Insert,
+ XK_KP_Delete, Qt::Key_Delete,
+ XK_KP_Equal, Qt::Key_Equal,
+ XK_KP_Multiply, Qt::Key_Asterisk,
+ XK_KP_Add, Qt::Key_Plus,
+ XK_KP_Separator, Qt::Key_Comma,
+ XK_KP_Subtract, Qt::Key_Minus,
+ XK_KP_Decimal, Qt::Key_Period,
+ XK_KP_Divide, Qt::Key_Slash,
+
+ // International input method support keys
+
+ // International & multi-key character composition
+ XK_ISO_Level3_Shift, Qt::Key_AltGr,
+ XK_Multi_key, Qt::Key_Multi_key,
+ XK_Codeinput, Qt::Key_Codeinput,
+ XK_SingleCandidate, Qt::Key_SingleCandidate,
+ XK_MultipleCandidate, Qt::Key_MultipleCandidate,
+ XK_PreviousCandidate, Qt::Key_PreviousCandidate,
+
+ // Misc Functions
+ XK_Mode_switch, Qt::Key_Mode_switch,
+ XK_script_switch, Qt::Key_Mode_switch,
+
+ // Japanese keyboard support
+ XK_Kanji, Qt::Key_Kanji,
+ XK_Muhenkan, Qt::Key_Muhenkan,
+ //XK_Henkan_Mode, Qt::Key_Henkan_Mode,
+ XK_Henkan_Mode, Qt::Key_Henkan,
+ XK_Henkan, Qt::Key_Henkan,
+ XK_Romaji, Qt::Key_Romaji,
+ XK_Hiragana, Qt::Key_Hiragana,
+ XK_Katakana, Qt::Key_Katakana,
+ XK_Hiragana_Katakana, Qt::Key_Hiragana_Katakana,
+ XK_Zenkaku, Qt::Key_Zenkaku,
+ XK_Hankaku, Qt::Key_Hankaku,
+ XK_Zenkaku_Hankaku, Qt::Key_Zenkaku_Hankaku,
+ XK_Touroku, Qt::Key_Touroku,
+ XK_Massyo, Qt::Key_Massyo,
+ XK_Kana_Lock, Qt::Key_Kana_Lock,
+ XK_Kana_Shift, Qt::Key_Kana_Shift,
+ XK_Eisu_Shift, Qt::Key_Eisu_Shift,
+ XK_Eisu_toggle, Qt::Key_Eisu_toggle,
+ //XK_Kanji_Bangou, Qt::Key_Kanji_Bangou,
+ //XK_Zen_Koho, Qt::Key_Zen_Koho,
+ //XK_Mae_Koho, Qt::Key_Mae_Koho,
+ XK_Kanji_Bangou, Qt::Key_Codeinput,
+ XK_Zen_Koho, Qt::Key_MultipleCandidate,
+ XK_Mae_Koho, Qt::Key_PreviousCandidate,
+
+#ifdef XK_KOREAN
+ // Korean keyboard support
+ XK_Hangul, Qt::Key_Hangul,
+ XK_Hangul_Start, Qt::Key_Hangul_Start,
+ XK_Hangul_End, Qt::Key_Hangul_End,
+ XK_Hangul_Hanja, Qt::Key_Hangul_Hanja,
+ XK_Hangul_Jamo, Qt::Key_Hangul_Jamo,
+ XK_Hangul_Romaja, Qt::Key_Hangul_Romaja,
+ //XK_Hangul_Codeinput, Qt::Key_Hangul_Codeinput,
+ XK_Hangul_Codeinput, Qt::Key_Codeinput,
+ XK_Hangul_Jeonja, Qt::Key_Hangul_Jeonja,
+ XK_Hangul_Banja, Qt::Key_Hangul_Banja,
+ XK_Hangul_PreHanja, Qt::Key_Hangul_PreHanja,
+ XK_Hangul_PostHanja, Qt::Key_Hangul_PostHanja,
+ //XK_Hangul_SingleCandidate,Qt::Key_Hangul_SingleCandidate,
+ //XK_Hangul_MultipleCandidate,Qt::Key_Hangul_MultipleCandidate,
+ //XK_Hangul_PreviousCandidate,Qt::Key_Hangul_PreviousCandidate,
+ XK_Hangul_SingleCandidate, Qt::Key_SingleCandidate,
+ XK_Hangul_MultipleCandidate,Qt::Key_MultipleCandidate,
+ XK_Hangul_PreviousCandidate,Qt::Key_PreviousCandidate,
+ XK_Hangul_Special, Qt::Key_Hangul_Special,
+ //XK_Hangul_switch, Qt::Key_Hangul_switch,
+ XK_Hangul_switch, Qt::Key_Mode_switch,
+#endif // XK_KOREAN
+
+ // dead keys
+ XK_dead_grave, Qt::Key_Dead_Grave,
+ XK_dead_acute, Qt::Key_Dead_Acute,
+ XK_dead_circumflex, Qt::Key_Dead_Circumflex,
+ XK_dead_tilde, Qt::Key_Dead_Tilde,
+ XK_dead_macron, Qt::Key_Dead_Macron,
+ XK_dead_breve, Qt::Key_Dead_Breve,
+ XK_dead_abovedot, Qt::Key_Dead_Abovedot,
+ XK_dead_diaeresis, Qt::Key_Dead_Diaeresis,
+ XK_dead_abovering, Qt::Key_Dead_Abovering,
+ XK_dead_doubleacute, Qt::Key_Dead_Doubleacute,
+ XK_dead_caron, Qt::Key_Dead_Caron,
+ XK_dead_cedilla, Qt::Key_Dead_Cedilla,
+ XK_dead_ogonek, Qt::Key_Dead_Ogonek,
+ XK_dead_iota, Qt::Key_Dead_Iota,
+ XK_dead_voiced_sound, Qt::Key_Dead_Voiced_Sound,
+ XK_dead_semivoiced_sound, Qt::Key_Dead_Semivoiced_Sound,
+ XK_dead_belowdot, Qt::Key_Dead_Belowdot,
+ XK_dead_hook, Qt::Key_Dead_Hook,
+ XK_dead_horn, Qt::Key_Dead_Horn,
+
+ // Special keys from X.org - This include multimedia keys,
+ // wireless/bluetooth/uwb keys, special launcher keys, etc.
+ XF86XK_Back, Qt::Key_Back,
+ XF86XK_Forward, Qt::Key_Forward,
+ XF86XK_Stop, Qt::Key_Stop,
+ XF86XK_Refresh, Qt::Key_Refresh,
+ XF86XK_Favorites, Qt::Key_Favorites,
+ XF86XK_AudioMedia, Qt::Key_LaunchMedia,
+ XF86XK_OpenURL, Qt::Key_OpenUrl,
+ XF86XK_HomePage, Qt::Key_HomePage,
+ XF86XK_Search, Qt::Key_Search,
+ XF86XK_AudioLowerVolume, Qt::Key_VolumeDown,
+ XF86XK_AudioMute, Qt::Key_VolumeMute,
+ XF86XK_AudioRaiseVolume, Qt::Key_VolumeUp,
+ XF86XK_AudioPlay, Qt::Key_MediaPlay,
+ XF86XK_AudioStop, Qt::Key_MediaStop,
+ XF86XK_AudioPrev, Qt::Key_MediaPrevious,
+ XF86XK_AudioNext, Qt::Key_MediaNext,
+ XF86XK_AudioRecord, Qt::Key_MediaRecord,
+ XF86XK_Mail, Qt::Key_LaunchMail,
+ XF86XK_MyComputer, Qt::Key_Launch0, // ### Qt 5: remap properly
+ XF86XK_Calculator, Qt::Key_Launch1,
+ XF86XK_Memo, Qt::Key_Memo,
+ XF86XK_ToDoList, Qt::Key_ToDoList,
+ XF86XK_Calendar, Qt::Key_Calendar,
+ XF86XK_PowerDown, Qt::Key_PowerDown,
+ XF86XK_ContrastAdjust, Qt::Key_ContrastAdjust,
+ XF86XK_Standby, Qt::Key_Standby,
+ XF86XK_MonBrightnessUp, Qt::Key_MonBrightnessUp,
+ XF86XK_MonBrightnessDown, Qt::Key_MonBrightnessDown,
+ XF86XK_KbdLightOnOff, Qt::Key_KeyboardLightOnOff,
+ XF86XK_KbdBrightnessUp, Qt::Key_KeyboardBrightnessUp,
+ XF86XK_KbdBrightnessDown, Qt::Key_KeyboardBrightnessDown,
+ XF86XK_PowerOff, Qt::Key_PowerOff,
+ XF86XK_WakeUp, Qt::Key_WakeUp,
+ XF86XK_Eject, Qt::Key_Eject,
+ XF86XK_ScreenSaver, Qt::Key_ScreenSaver,
+ XF86XK_WWW, Qt::Key_WWW,
+ XF86XK_Sleep, Qt::Key_Sleep,
+ XF86XK_LightBulb, Qt::Key_LightBulb,
+ XF86XK_Shop, Qt::Key_Shop,
+ XF86XK_History, Qt::Key_History,
+ XF86XK_AddFavorite, Qt::Key_AddFavorite,
+ XF86XK_HotLinks, Qt::Key_HotLinks,
+ XF86XK_BrightnessAdjust, Qt::Key_BrightnessAdjust,
+ XF86XK_Finance, Qt::Key_Finance,
+ XF86XK_Community, Qt::Key_Community,
+ XF86XK_AudioRewind, Qt::Key_AudioRewind,
+ XF86XK_BackForward, Qt::Key_BackForward,
+ XF86XK_ApplicationLeft, Qt::Key_ApplicationLeft,
+ XF86XK_ApplicationRight, Qt::Key_ApplicationRight,
+ XF86XK_Book, Qt::Key_Book,
+ XF86XK_CD, Qt::Key_CD,
+ XF86XK_Calculater, Qt::Key_Calculator,
+ XF86XK_Clear, Qt::Key_Clear,
+ XF86XK_ClearGrab, Qt::Key_ClearGrab,
+ XF86XK_Close, Qt::Key_Close,
+ XF86XK_Copy, Qt::Key_Copy,
+ XF86XK_Cut, Qt::Key_Cut,
+ XF86XK_Display, Qt::Key_Display,
+ XF86XK_DOS, Qt::Key_DOS,
+ XF86XK_Documents, Qt::Key_Documents,
+ XF86XK_Excel, Qt::Key_Excel,
+ XF86XK_Explorer, Qt::Key_Explorer,
+ XF86XK_Game, Qt::Key_Game,
+ XF86XK_Go, Qt::Key_Go,
+ XF86XK_iTouch, Qt::Key_iTouch,
+ XF86XK_LogOff, Qt::Key_LogOff,
+ XF86XK_Market, Qt::Key_Market,
+ XF86XK_Meeting, Qt::Key_Meeting,
+ XF86XK_MenuKB, Qt::Key_MenuKB,
+ XF86XK_MenuPB, Qt::Key_MenuPB,
+ XF86XK_MySites, Qt::Key_MySites,
+ XF86XK_News, Qt::Key_News,
+ XF86XK_OfficeHome, Qt::Key_OfficeHome,
+ XF86XK_Option, Qt::Key_Option,
+ XF86XK_Paste, Qt::Key_Paste,
+ XF86XK_Phone, Qt::Key_Phone,
+ XF86XK_Reply, Qt::Key_Reply,
+ XF86XK_Reload, Qt::Key_Reload,
+ XF86XK_RotateWindows, Qt::Key_RotateWindows,
+ XF86XK_RotationPB, Qt::Key_RotationPB,
+ XF86XK_RotationKB, Qt::Key_RotationKB,
+ XF86XK_Save, Qt::Key_Save,
+ XF86XK_Send, Qt::Key_Send,
+ XF86XK_Spell, Qt::Key_Spell,
+ XF86XK_SplitScreen, Qt::Key_SplitScreen,
+ XF86XK_Support, Qt::Key_Support,
+ XF86XK_TaskPane, Qt::Key_TaskPane,
+ XF86XK_Terminal, Qt::Key_Terminal,
+ XF86XK_Tools, Qt::Key_Tools,
+ XF86XK_Travel, Qt::Key_Travel,
+ XF86XK_Video, Qt::Key_Video,
+ XF86XK_Word, Qt::Key_Word,
+ XF86XK_Xfer, Qt::Key_Xfer,
+ XF86XK_ZoomIn, Qt::Key_ZoomIn,
+ XF86XK_ZoomOut, Qt::Key_ZoomOut,
+ XF86XK_Away, Qt::Key_Away,
+ XF86XK_Messenger, Qt::Key_Messenger,
+ XF86XK_WebCam, Qt::Key_WebCam,
+ XF86XK_MailForward, Qt::Key_MailForward,
+ XF86XK_Pictures, Qt::Key_Pictures,
+ XF86XK_Music, Qt::Key_Music,
+ XF86XK_Battery, Qt::Key_Battery,
+ XF86XK_Bluetooth, Qt::Key_Bluetooth,
+ XF86XK_WLAN, Qt::Key_WLAN,
+ XF86XK_UWB, Qt::Key_UWB,
+ XF86XK_AudioForward, Qt::Key_AudioForward,
+ XF86XK_AudioRepeat, Qt::Key_AudioRepeat,
+ XF86XK_AudioRandomPlay, Qt::Key_AudioRandomPlay,
+ XF86XK_Subtitle, Qt::Key_Subtitle,
+ XF86XK_AudioCycleTrack, Qt::Key_AudioCycleTrack,
+ XF86XK_Time, Qt::Key_Time,
+ XF86XK_Select, Qt::Key_Select,
+ XF86XK_View, Qt::Key_View,
+ XF86XK_TopMenu, Qt::Key_TopMenu,
+ XF86XK_Bluetooth, Qt::Key_Bluetooth,
+ XF86XK_Suspend, Qt::Key_Suspend,
+ XF86XK_Hibernate, Qt::Key_Hibernate,
+ XF86XK_Launch0, Qt::Key_Launch2, // ### Qt 5: remap properly
+ XF86XK_Launch1, Qt::Key_Launch3,
+ XF86XK_Launch2, Qt::Key_Launch4,
+ XF86XK_Launch3, Qt::Key_Launch5,
+ XF86XK_Launch4, Qt::Key_Launch6,
+ XF86XK_Launch5, Qt::Key_Launch7,
+ XF86XK_Launch6, Qt::Key_Launch8,
+ XF86XK_Launch7, Qt::Key_Launch9,
+ XF86XK_Launch8, Qt::Key_LaunchA,
+ XF86XK_Launch9, Qt::Key_LaunchB,
+ XF86XK_LaunchA, Qt::Key_LaunchC,
+ XF86XK_LaunchB, Qt::Key_LaunchD,
+ XF86XK_LaunchC, Qt::Key_LaunchE,
+ XF86XK_LaunchD, Qt::Key_LaunchF,
+ XF86XK_LaunchE, Qt::Key_LaunchG,
+ XF86XK_LaunchF, Qt::Key_LaunchH,
+
+ // Qtopia keys
+ QTOPIAXK_Select, Qt::Key_Select,
+ QTOPIAXK_Yes, Qt::Key_Yes,
+ QTOPIAXK_No, Qt::Key_No,
+ QTOPIAXK_Cancel, Qt::Key_Cancel,
+ QTOPIAXK_Printer, Qt::Key_Printer,
+ QTOPIAXK_Execute, Qt::Key_Execute,
+ QTOPIAXK_Sleep, Qt::Key_Sleep,
+ QTOPIAXK_Play, Qt::Key_Play,
+ QTOPIAXK_Zoom, Qt::Key_Zoom,
+ QTOPIAXK_Context1, Qt::Key_Context1,
+ QTOPIAXK_Context2, Qt::Key_Context2,
+ QTOPIAXK_Context3, Qt::Key_Context3,
+ QTOPIAXK_Context4, Qt::Key_Context4,
+ QTOPIAXK_Call, Qt::Key_Call,
+ QTOPIAXK_Hangup, Qt::Key_Hangup,
+ QTOPIAXK_Flip, Qt::Key_Flip,
+
+ 0, 0
+};
+
+static int translateKeySym(uint key)
+{
+ int code = -1;
+ int i = 0; // any other keys
+ while (KeyTbl[i]) {
+ if (key == KeyTbl[i]) {
+ code = (int)KeyTbl[i+1];
+ break;
+ }
+ i += 2;
+ }
+ if (qt_meta_mask) {
+ // translate Super/Hyper keys to Meta if we're using them as the MetaModifier
+ if (qt_meta_mask == qt_super_mask && (code == Qt::Key_Super_L || code == Qt::Key_Super_R)) {
+ code = Qt::Key_Meta;
+ } else if (qt_meta_mask == qt_hyper_mask && (code == Qt::Key_Hyper_L || code == Qt::Key_Hyper_R)) {
+ code = Qt::Key_Meta;
+ }
+ }
+ return code;
+}
+
+#if !defined(QT_NO_XIM)
+static const unsigned short katakanaKeysymsToUnicode[] = {
+ 0x0000, 0x3002, 0x300C, 0x300D, 0x3001, 0x30FB, 0x30F2, 0x30A1,
+ 0x30A3, 0x30A5, 0x30A7, 0x30A9, 0x30E3, 0x30E5, 0x30E7, 0x30C3,
+ 0x30FC, 0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, 0x30AB, 0x30AD,
+ 0x30AF, 0x30B1, 0x30B3, 0x30B5, 0x30B7, 0x30B9, 0x30BB, 0x30BD,
+ 0x30BF, 0x30C1, 0x30C4, 0x30C6, 0x30C8, 0x30CA, 0x30CB, 0x30CC,
+ 0x30CD, 0x30CE, 0x30CF, 0x30D2, 0x30D5, 0x30D8, 0x30DB, 0x30DE,
+ 0x30DF, 0x30E0, 0x30E1, 0x30E2, 0x30E4, 0x30E6, 0x30E8, 0x30E9,
+ 0x30EA, 0x30EB, 0x30EC, 0x30ED, 0x30EF, 0x30F3, 0x309B, 0x309C
+};
+
+static const unsigned short cyrillicKeysymsToUnicode[] = {
+ 0x0000, 0x0452, 0x0453, 0x0451, 0x0454, 0x0455, 0x0456, 0x0457,
+ 0x0458, 0x0459, 0x045a, 0x045b, 0x045c, 0x0000, 0x045e, 0x045f,
+ 0x2116, 0x0402, 0x0403, 0x0401, 0x0404, 0x0405, 0x0406, 0x0407,
+ 0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x0000, 0x040e, 0x040f,
+ 0x044e, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433,
+ 0x0445, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e,
+ 0x043f, 0x044f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432,
+ 0x044c, 0x044b, 0x0437, 0x0448, 0x044d, 0x0449, 0x0447, 0x044a,
+ 0x042e, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413,
+ 0x0425, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e,
+ 0x041f, 0x042f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412,
+ 0x042c, 0x042b, 0x0417, 0x0428, 0x042d, 0x0429, 0x0427, 0x042a
+};
+
+static const unsigned short greekKeysymsToUnicode[] = {
+ 0x0000, 0x0386, 0x0388, 0x0389, 0x038a, 0x03aa, 0x0000, 0x038c,
+ 0x038e, 0x03ab, 0x0000, 0x038f, 0x0000, 0x0000, 0x0385, 0x2015,
+ 0x0000, 0x03ac, 0x03ad, 0x03ae, 0x03af, 0x03ca, 0x0390, 0x03cc,
+ 0x03cd, 0x03cb, 0x03b0, 0x03ce, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397,
+ 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f,
+ 0x03a0, 0x03a1, 0x03a3, 0x0000, 0x03a4, 0x03a5, 0x03a6, 0x03a7,
+ 0x03a8, 0x03a9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7,
+ 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf,
+ 0x03c0, 0x03c1, 0x03c3, 0x03c2, 0x03c4, 0x03c5, 0x03c6, 0x03c7,
+ 0x03c8, 0x03c9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+};
+
+static const unsigned short technicalKeysymsToUnicode[] = {
+ 0x0000, 0x23B7, 0x250C, 0x2500, 0x2320, 0x2321, 0x2502, 0x23A1,
+ 0x23A3, 0x23A4, 0x23A6, 0x239B, 0x239D, 0x239E, 0x23A0, 0x23A8,
+ 0x23AC, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x2264, 0x2260, 0x2265, 0x222B,
+ 0x2234, 0x221D, 0x221E, 0x0000, 0x0000, 0x2207, 0x0000, 0x0000,
+ 0x223C, 0x2243, 0x0000, 0x0000, 0x0000, 0x21D4, 0x21D2, 0x2261,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x221A, 0x0000,
+ 0x0000, 0x0000, 0x2282, 0x2283, 0x2229, 0x222A, 0x2227, 0x2228,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2202,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0192, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x2190, 0x2191, 0x2192, 0x2193, 0x0000
+};
+
+static const unsigned short specialKeysymsToUnicode[] = {
+ 0x25C6, 0x2592, 0x2409, 0x240C, 0x240D, 0x240A, 0x0000, 0x0000,
+ 0x2424, 0x240B, 0x2518, 0x2510, 0x250C, 0x2514, 0x253C, 0x23BA,
+ 0x23BB, 0x2500, 0x23BC, 0x23BD, 0x251C, 0x2524, 0x2534, 0x252C,
+ 0x2502, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+};
+
+static const unsigned short publishingKeysymsToUnicode[] = {
+ 0x0000, 0x2003, 0x2002, 0x2004, 0x2005, 0x2007, 0x2008, 0x2009,
+ 0x200a, 0x2014, 0x2013, 0x0000, 0x0000, 0x0000, 0x2026, 0x2025,
+ 0x2153, 0x2154, 0x2155, 0x2156, 0x2157, 0x2158, 0x2159, 0x215a,
+ 0x2105, 0x0000, 0x0000, 0x2012, 0x2329, 0x0000, 0x232a, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x215b, 0x215c, 0x215d, 0x215e, 0x0000,
+ 0x0000, 0x2122, 0x2613, 0x0000, 0x25c1, 0x25b7, 0x25cb, 0x25af,
+ 0x2018, 0x2019, 0x201c, 0x201d, 0x211e, 0x0000, 0x2032, 0x2033,
+ 0x0000, 0x271d, 0x0000, 0x25ac, 0x25c0, 0x25b6, 0x25cf, 0x25ae,
+ 0x25e6, 0x25ab, 0x25ad, 0x25b3, 0x25bd, 0x2606, 0x2022, 0x25aa,
+ 0x25b2, 0x25bc, 0x261c, 0x261e, 0x2663, 0x2666, 0x2665, 0x0000,
+ 0x2720, 0x2020, 0x2021, 0x2713, 0x2717, 0x266f, 0x266d, 0x2642,
+ 0x2640, 0x260e, 0x2315, 0x2117, 0x2038, 0x201a, 0x201e, 0x0000
+};
+
+static const unsigned short aplKeysymsToUnicode[] = {
+ 0x0000, 0x0000, 0x0000, 0x003c, 0x0000, 0x0000, 0x003e, 0x0000,
+ 0x2228, 0x2227, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x00af, 0x0000, 0x22a5, 0x2229, 0x230a, 0x0000, 0x005f, 0x0000,
+ 0x0000, 0x0000, 0x2218, 0x0000, 0x2395, 0x0000, 0x22a4, 0x25cb,
+ 0x0000, 0x0000, 0x0000, 0x2308, 0x0000, 0x0000, 0x222a, 0x0000,
+ 0x2283, 0x0000, 0x2282, 0x0000, 0x22a2, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x22a3, 0x0000, 0x0000, 0x0000
+};
+
+static const unsigned short koreanKeysymsToUnicode[] = {
+ 0x0000, 0x3131, 0x3132, 0x3133, 0x3134, 0x3135, 0x3136, 0x3137,
+ 0x3138, 0x3139, 0x313a, 0x313b, 0x313c, 0x313d, 0x313e, 0x313f,
+ 0x3140, 0x3141, 0x3142, 0x3143, 0x3144, 0x3145, 0x3146, 0x3147,
+ 0x3148, 0x3149, 0x314a, 0x314b, 0x314c, 0x314d, 0x314e, 0x314f,
+ 0x3150, 0x3151, 0x3152, 0x3153, 0x3154, 0x3155, 0x3156, 0x3157,
+ 0x3158, 0x3159, 0x315a, 0x315b, 0x315c, 0x315d, 0x315e, 0x315f,
+ 0x3160, 0x3161, 0x3162, 0x3163, 0x11a8, 0x11a9, 0x11aa, 0x11ab,
+ 0x11ac, 0x11ad, 0x11ae, 0x11af, 0x11b0, 0x11b1, 0x11b2, 0x11b3,
+ 0x11b4, 0x11b5, 0x11b6, 0x11b7, 0x11b8, 0x11b9, 0x11ba, 0x11bb,
+ 0x11bc, 0x11bd, 0x11be, 0x11bf, 0x11c0, 0x11c1, 0x11c2, 0x316d,
+ 0x3171, 0x3178, 0x317f, 0x3181, 0x3184, 0x3186, 0x318d, 0x318e,
+ 0x11eb, 0x11f0, 0x11f9, 0x0000, 0x0000, 0x0000, 0x0000, 0x20a9
+};
+
+static QChar keysymToUnicode(unsigned char byte3, unsigned char byte4)
+{
+ switch (byte3) {
+ case 0x04:
+ // katakana
+ if (byte4 > 0xa0 && byte4 < 0xe0)
+ return QChar(katakanaKeysymsToUnicode[byte4 - 0xa0]);
+ else if (byte4 == 0x7e)
+ return QChar(0x203e); // Overline
+ break;
+ case 0x06:
+ // russian, use lookup table
+ if (byte4 > 0xa0)
+ return QChar(cyrillicKeysymsToUnicode[byte4 - 0xa0]);
+ break;
+ case 0x07:
+ // greek
+ if (byte4 > 0xa0)
+ return QChar(greekKeysymsToUnicode[byte4 - 0xa0]);
+ break;
+ case 0x08:
+ // technical
+ if (byte4 > 0xa0)
+ return QChar(technicalKeysymsToUnicode[byte4 - 0xa0]);
+ break;
+ case 0x09:
+ // special
+ if (byte4 >= 0xe0)
+ return QChar(specialKeysymsToUnicode[byte4 - 0xe0]);
+ break;
+ case 0x0a:
+ // publishing
+ if (byte4 > 0xa0)
+ return QChar(publishingKeysymsToUnicode[byte4 - 0xa0]);
+ break;
+ case 0x0b:
+ // APL
+ if (byte4 > 0xa0)
+ return QChar(aplKeysymsToUnicode[byte4 - 0xa0]);
+ break;
+ case 0x0e:
+ // Korean
+ if (byte4 > 0xa0)
+ return QChar(koreanKeysymsToUnicode[byte4 - 0xa0]);
+ break;
+ default:
+ break;
+ }
+ return QChar(0x0);
+}
+#endif
+
+static QString translateKeySym(KeySym keysym, uint xmodifiers,
+ int &code, Qt::KeyboardModifiers &modifiers,
+ QByteArray &chars, int &count)
+{
+ // all keysyms smaller than 0xff00 are actally keys that can be mapped to unicode chars
+
+ extern QTextCodec *qt_input_mapper; // from qapplication_x11.cpp
+ QTextCodec *mapper = qt_input_mapper;
+ QChar converted;
+
+ if (count == 0 && keysym < 0xff00) {
+ unsigned char byte3 = (unsigned char)(keysym >> 8);
+ int mib = -1;
+ switch(byte3) {
+ case 0: // Latin 1
+ case 1: // Latin 2
+ case 2: //latin 3
+ case 3: // latin4
+ mib = byte3 + 4; break;
+ case 5: // arabic
+ mib = 82; break;
+ case 12: // Hebrew
+ mib = 85; break;
+ case 13: // Thai
+ mib = 2259; break;
+ case 4: // kana
+ case 6: // cyrillic
+ case 7: // greek
+ case 8: // technical, no mapping here at the moment
+ case 9: // Special
+ case 10: // Publishing
+ case 11: // APL
+ case 14: // Korean, no mapping
+ mib = -1; // manual conversion
+ mapper = 0;
+#if !defined(QT_NO_XIM)
+ converted = keysymToUnicode(byte3, keysym & 0xff);
+#endif
+ case 0x20:
+ // currency symbols
+ if (keysym >= 0x20a0 && keysym <= 0x20ac) {
+ mib = -1; // manual conversion
+ mapper = 0;
+ converted = (uint)keysym;
+ }
+ break;
+ default:
+ break;
+ }
+ if (mib != -1) {
+ mapper = QTextCodec::codecForMib(mib);
+ if (chars.isEmpty())
+ chars.resize(1);
+ chars[0] = (unsigned char) (keysym & 0xff); // get only the fourth bit for conversion later
+ count++;
+ }
+ } else if (keysym >= 0x1000000 && keysym <= 0x100ffff) {
+ converted = (ushort) (keysym - 0x1000000);
+ mapper = 0;
+ }
+ if (count < (int)chars.size()-1)
+ chars[count] = '\0';
+
+ QString text;
+ if (!mapper && converted.unicode() != 0x0) {
+ text = converted;
+ } else if (!chars.isEmpty()) {
+ // convert chars (8bit) to text (unicode).
+ if (mapper)
+ text = mapper->toUnicode(chars.data(), count, 0);
+ if (text.isEmpty()) {
+ // no mapper, or codec couldn't convert to unicode (this
+ // can happen when running in the C locale or with no LANG
+ // set). try converting from latin-1
+ text = QString::fromLatin1(chars);
+ }
+ }
+
+ modifiers = X11->translateModifiers(xmodifiers);
+
+ // Commentary in X11/keysymdef says that X codes match ASCII, so it
+ // is safe to use the locale functions to process X codes in ISO8859-1.
+ //
+ // This is mainly for compatibility - applications should not use the
+ // Qt keycodes between 128 and 255, but should rather use the
+ // QKeyEvent::text().
+ //
+ extern QTextCodec *qt_input_mapper; // from qapplication_x11.cpp
+ if (keysym < 128 || (keysym < 256 && (!qt_input_mapper || qt_input_mapper->mibEnum()==4))) {
+ // upper-case key, if known
+ code = isprint((int)keysym) ? toupper((int)keysym) : 0;
+ } else if (keysym >= XK_F1 && keysym <= XK_F35) {
+ // function keys
+ code = Qt::Key_F1 + ((int)keysym - XK_F1);
+ } else if (keysym >= XK_KP_Space && keysym <= XK_KP_9) {
+ if (keysym >= XK_KP_0) {
+ // numeric keypad keys
+ code = Qt::Key_0 + ((int)keysym - XK_KP_0);
+ } else {
+ code = translateKeySym(keysym);
+ }
+ modifiers |= Qt::KeypadModifier;
+ } else if (text.length() == 1 && text.unicode()->unicode() > 0x1f && text.unicode()->unicode() != 0x7f && !(keysym >= XK_dead_grave && keysym <= XK_dead_horn)) {
+ code = text.unicode()->toUpper().unicode();
+ } else {
+ // any other keys
+ code = translateKeySym(keysym);
+
+ if (code == Qt::Key_Tab && (modifiers & Qt::ShiftModifier)) {
+ // map shift+tab to shift+backtab, QShortcutMap knows about it
+ // and will handle it.
+ code = Qt::Key_Backtab;
+ text = QString();
+ }
+ }
+
+ return text;
+}
+
+extern bool qt_use_rtl_extensions; // from qapplication_x11.cpp
+
+bool QKeyMapperPrivate::translateKeyEventInternal(QWidget *keyWidget,
+ const XEvent *event,
+ KeySym &keysym,
+ int& count,
+ QString& text,
+ Qt::KeyboardModifiers &modifiers,
+ int& code,
+ QEvent::Type &type,
+ bool statefulTranslation)
+{
+ XKeyEvent xkeyevent = event->xkey;
+ int keycode = event->xkey.keycode;
+ // save the modifier state, we will use the keystate uint later by passing
+ // it to translateButtonState
+ uint keystate = event->xkey.state;
+
+ type = (event->type == XKeyPress) ? QEvent::KeyPress : QEvent::KeyRelease;
+
+ static int directionKeyEvent = 0;
+ static unsigned int lastWinId = 0;
+
+ // translate pending direction change
+ if (statefulTranslation && qt_use_rtl_extensions && type == QEvent::KeyRelease) {
+ if (directionKeyEvent == Qt::Key_Direction_R || directionKeyEvent == Qt::Key_Direction_L) {
+ type = QEvent::KeyPress;
+ code = directionKeyEvent;
+ text = QString();
+ directionKeyEvent = 0;
+ lastWinId = 0;
+ return true;
+ } else {
+ directionKeyEvent = 0;
+ lastWinId = 0;
+ }
+ }
+
+ // some XmbLookupString implementations don't return buffer overflow correctly,
+ // so we increase the input buffer to allow for long strings...
+ // 256 chars * 2 bytes + 1 null-term == 513 bytes
+ QByteArray chars;
+ chars.resize(513);
+
+ count = XLookupString(&xkeyevent, chars.data(), chars.size(), &keysym, 0);
+ if (count && !keycode) {
+ extern int qt_ximComposingKeycode; // from qapplication_x11.cpp
+ keycode = qt_ximComposingKeycode;
+ qt_ximComposingKeycode = 0;
+ }
+
+ // translate the keysym + xmodifiers to Qt::Key_* + Qt::KeyboardModifiers
+ text = translateKeySym(keysym, keystate, code, modifiers, chars, count);
+
+ // Watch for keypresses and if its a key belonging to the Ctrl-Shift
+ // direction-changing accel, remember it.
+ // We keep track of those keys instead of using the event's state
+ // (to figure out whether the Ctrl modifier is held while Shift is pressed,
+ // or Shift is held while Ctrl is pressed) since the 'state' doesn't tell
+ // us whether the modifier held is Left or Right.
+ if (statefulTranslation && qt_use_rtl_extensions && type == QEvent::KeyPress) {
+ if (keysym == XK_Control_L || keysym == XK_Control_R
+ || keysym == XK_Shift_L || keysym == XK_Shift_R) {
+ if (!directionKeyEvent) {
+ directionKeyEvent = keysym;
+ // This code exists in order to check that
+ // the event is occurred in the same widget.
+ lastWinId = keyWidget->internalWinId();
+ }
+ } else {
+ // this can no longer be a direction-changing accel.
+ // if any other key was pressed.
+ directionKeyEvent = Qt::Key_Space;
+ }
+
+ if (directionKeyEvent && lastWinId == keyWidget->internalWinId()) {
+ if ((keysym == XK_Shift_L && directionKeyEvent == XK_Control_L)
+ || (keysym == XK_Control_L && directionKeyEvent == XK_Shift_L)) {
+ directionKeyEvent = Qt::Key_Direction_L;
+ } else if ((keysym == XK_Shift_R && directionKeyEvent == XK_Control_R)
+ || (keysym == XK_Control_R && directionKeyEvent == XK_Shift_R)) {
+ directionKeyEvent = Qt::Key_Direction_R;
+ }
+ } else if (directionKeyEvent == Qt::Key_Direction_L
+ || directionKeyEvent == Qt::Key_Direction_R) {
+ directionKeyEvent = Qt::Key_Space; // invalid
+ }
+ }
+
+ return true;
+}
+
+
+struct qt_auto_repeat_data
+{
+ // match the window and keycode with timestamp delta of 10 ms
+ Window window;
+ KeyCode keycode;
+ Time timestamp;
+
+ // queue scanner state
+ bool release;
+ bool error;
+};
+
+#if defined(Q_C_CALLBACKS)
+extern "C" {
+#endif
+
+static Bool qt_keypress_scanner(Display *, XEvent *event, XPointer arg)
+{
+ if (event->type != XKeyPress && event->type != XKeyRelease)
+ return false;
+
+ qt_auto_repeat_data *data = (qt_auto_repeat_data *) arg;
+ if (data->error)
+ return false;
+
+ if (event->xkey.window != data->window ||
+ event->xkey.keycode != data->keycode) {
+ // deal breakers: key events in a different window or an event
+ // with a different key code
+ data->error = true;
+ return false;
+ }
+
+ if (event->type == XKeyPress) {
+ data->error = (! data->release || event->xkey.time - data->timestamp > 10);
+ return (! data->error);
+ }
+
+ // must be XKeyRelease event
+ if (data->release) {
+ // found a second release
+ data->error = true;
+ return false;
+ }
+
+ // found a single release
+ data->release = true;
+ data->timestamp = event->xkey.time;
+
+ return false;
+}
+
+static Bool qt_keyrelease_scanner(Display *, XEvent *event, XPointer arg)
+{
+ const qt_auto_repeat_data *data = (const qt_auto_repeat_data *) arg;
+ return (event->type == XKeyRelease &&
+ event->xkey.window == data->window &&
+ event->xkey.keycode == data->keycode);
+}
+
+#if defined(Q_C_CALLBACKS)
+}
+#endif
+
+bool QKeyMapperPrivate::translateKeyEvent(QWidget *keyWidget, const XEvent *event, bool grab)
+{
+ int code = -1;
+ int count = 0;
+ Qt::KeyboardModifiers modifiers;
+
+ if (qt_sm_blockUserInput) // block user interaction during session management
+ return true;
+
+ Display *dpy = X11->display;
+
+ if (!keyWidget->isEnabled())
+ return true;
+
+ QEvent::Type type;
+ bool autor = false;
+ QString text;
+
+ KeySym keysym = 0;
+ translateKeyEventInternal(keyWidget, event, keysym, count, text, modifiers, code, type);
+
+ // was this the last auto-repeater?
+ qt_auto_repeat_data auto_repeat_data;
+ auto_repeat_data.window = event->xkey.window;
+ auto_repeat_data.keycode = event->xkey.keycode;
+ auto_repeat_data.timestamp = event->xkey.time;
+
+ static uint curr_autorep = 0;
+ if (event->type == XKeyPress) {
+ if (curr_autorep == event->xkey.keycode) {
+ autor = true;
+ curr_autorep = 0;
+ }
+ } else {
+ // look ahead for auto-repeat
+ XEvent nextpress;
+
+ auto_repeat_data.release = true;
+ auto_repeat_data.error = false;
+ if (XCheckIfEvent(dpy, &nextpress, &qt_keypress_scanner,
+ (XPointer) &auto_repeat_data)) {
+ autor = true;
+
+ // Put it back... we COULD send the event now and not need
+ // the static curr_autorep variable.
+ XPutBackEvent(dpy,&nextpress);
+ }
+ curr_autorep = autor ? event->xkey.keycode : 0;
+ }
+
+#if defined QT3_SUPPORT && !defined(QT_NO_SHORTCUT)
+ // process accelerators before doing key compression
+ if (type == QEvent::KeyPress && !grab
+ && QApplicationPrivate::instance()->use_compat()) {
+ // send accel events if the keyboard is not grabbed
+ QKeyEventEx a(type, code, modifiers, text, autor, qMax(qMax(count,1), int(text.length())),
+ event->xkey.keycode, keysym, event->xkey.state);
+ if (QApplicationPrivate::instance()->qt_tryAccelEvent(keyWidget, &a))
+ return true;
+ }
+#endif
+
+#ifndef QT_NO_IM
+ QInputContext *qic = keyWidget->inputContext();
+#endif
+
+ // compress keys
+ if (!text.isEmpty() && keyWidget->testAttribute(Qt::WA_KeyCompression) &&
+#ifndef QT_NO_IM
+ // Ordinary input methods require discrete key events to work
+ // properly, so key compression has to be disabled when input
+ // context exists.
+ //
+ // And further consideration, some complex input method
+ // require all key press/release events discretely even if
+ // the input method awares of key compression and compressed
+ // keys are ordinary alphabets. For example, the uim project
+ // is planning to implement "combinational shift" feature for
+ // a Japanese input method, uim-skk. It will work as follows.
+ //
+ // 1. press "r"
+ // 2. press "u"
+ // 3. release both "r" and "u" in arbitrary order
+ // 4. above key sequence generates "Ru"
+ //
+ // Of course further consideration about other participants
+ // such as key repeat mechanism is required to implement such
+ // feature.
+ !qic &&
+#endif // QT_NO_IM
+ // do not compress keys if the key event we just got above matches
+ // one of the key ranges used to compute stopCompression
+ !((code >= Qt::Key_Escape && code <= Qt::Key_SysReq)
+ || (code >= Qt::Key_Home && code <= Qt::Key_PageDown)
+ || (code >= Qt::Key_Super_L && code <= Qt::Key_Direction_R)
+ || (code == 0)
+ || (text.length() == 1 && text.unicode()->unicode() == '\n'))) {
+ // the widget wants key compression so it gets it
+
+ // sync the event queue, this makes key compress work better
+ XSync(dpy, false);
+
+ for (;;) {
+ XEvent evRelease;
+ XEvent evPress;
+ if (!XCheckTypedWindowEvent(dpy,event->xkey.window,
+ XKeyRelease,&evRelease))
+ break;
+ if (!XCheckTypedWindowEvent(dpy,event->xkey.window,
+ XKeyPress,&evPress)) {
+ XPutBackEvent(dpy, &evRelease);
+ break;
+ }
+ QString textIntern;
+ int codeIntern = -1;
+ int countIntern = 0;
+ Qt::KeyboardModifiers modifiersIntern;
+ QEvent::Type t;
+ KeySym keySymIntern;
+ translateKeyEventInternal(keyWidget, &evPress, keySymIntern, countIntern, textIntern,
+ modifiersIntern, codeIntern, t);
+ // use stopCompression to stop key compression for the following
+ // key event ranges:
+ bool stopCompression =
+ // 1) misc keys
+ (codeIntern >= Qt::Key_Escape && codeIntern <= Qt::Key_SysReq)
+ // 2) cursor movement
+ || (codeIntern >= Qt::Key_Home && codeIntern <= Qt::Key_PageDown)
+ // 3) extra keys
+ || (codeIntern >= Qt::Key_Super_L && codeIntern <= Qt::Key_Direction_R)
+ // 4) something that a) doesn't translate to text or b) translates
+ // to newline text
+ || (codeIntern == 0)
+ || (textIntern.length() == 1 && textIntern.unicode()->unicode() == '\n')
+ || (codeIntern == Qt::Key_unknown);
+
+ if (modifiersIntern == modifiers && !textIntern.isEmpty() && !stopCompression) {
+ text += textIntern;
+ count += countIntern;
+ } else {
+ XPutBackEvent(dpy, &evPress);
+ XPutBackEvent(dpy, &evRelease);
+ break;
+ }
+ }
+ }
+
+ // autorepeat compression makes sense for all widgets (Windows
+ // does it automatically ....)
+ if (event->type == XKeyPress && text.length() <= 1
+#ifndef QT_NO_IM
+ // input methods need discrete key events
+ && !qic
+#endif// QT_NO_IM
+ ) {
+ XEvent dummy;
+
+ for (;;) {
+ auto_repeat_data.release = false;
+ auto_repeat_data.error = false;
+ if (! XCheckIfEvent(dpy, &dummy, &qt_keypress_scanner,
+ (XPointer) &auto_repeat_data))
+ break;
+ if (! XCheckIfEvent(dpy, &dummy, &qt_keyrelease_scanner,
+ (XPointer) &auto_repeat_data))
+ break;
+
+ count++;
+ if (!text.isEmpty())
+ text += text[0];
+ }
+ }
+
+ return QKeyMapper::sendKeyEvent(keyWidget, grab, type, code, modifiers, text, autor,
+ qMax(qMax(count,1), int(text.length())),
+ event->xkey.keycode, keysym, event->xkey.state);
+}
+
+bool QKeyMapper::sendKeyEvent(QWidget *keyWidget, bool grab,
+ QEvent::Type type, int code, Qt::KeyboardModifiers modifiers,
+ const QString &text, bool autorepeat, int count,
+ quint32 nativeScanCode, quint32 nativeVirtualKey, quint32 nativeModifiers,
+ bool *)
+{
+ // try the menukey first
+ if (type == QEvent::KeyPress && code == Qt::Key_Menu) {
+ QVariant v = keyWidget->inputMethodQuery(Qt::ImMicroFocus);
+ QPoint globalPos;
+ QPoint pos;
+ if (v.isNull()) {
+ globalPos = QCursor::pos();
+ pos = keyWidget->mapFromGlobal(globalPos);
+ } else {
+ pos = v.toRect().center();
+ globalPos = keyWidget->mapToGlobal(pos);
+ }
+ QContextMenuEvent e(QContextMenuEvent::Keyboard, pos, globalPos);
+ qt_sendSpontaneousEvent(keyWidget, &e);
+ if(e.isAccepted())
+ return true;
+ }
+
+ Q_UNUSED(grab);
+ QKeyEventEx e(type, code, modifiers, text, autorepeat, qMax(qMax(count,1), int(text.length())),
+ nativeScanCode, nativeVirtualKey, nativeModifiers);
+ return qt_sendSpontaneousEvent(keyWidget, &e);
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/x11/qkeymapper_x11_p.cpp b/src/widgets/platforms/x11/qkeymapper_x11_p.cpp
new file mode 100644
index 0000000000..80352ee797
--- /dev/null
+++ b/src/widgets/platforms/x11/qkeymapper_x11_p.cpp
@@ -0,0 +1,489 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// This file is auto-generated, do not edit!
+// (Generated using util/xkbdatagen)
+
+static struct {
+ const char *layout;
+ const char *variant; // 0 means any variant
+ Qt::LayoutDirection direction;
+ QLocale::Language language;
+ QLocale::Country country;
+} xkbLayoutData[] = {
+ // name = us, description = U.S. English
+ { "us", "", Qt::LeftToRight, QLocale::English, QLocale::UnitedStates },
+ // name = us:intl, description = U.S. English
+ { "us", "intl", Qt::LeftToRight, QLocale::English, QLocale::UnitedStates },
+ // name = us:alt-intl, description = U.S. English
+ { "us", "alt-intl", Qt::LeftToRight, QLocale::English, QLocale::UnitedStates },
+ // name = us:dvorak, description = U.S. English
+ { "us", "dvorak", Qt::LeftToRight, QLocale::English, QLocale::UnitedStates },
+ // name = us:rus, description = U.S. English
+ { "us", "rus", Qt::LeftToRight, QLocale::Russian, QLocale::UnitedStates },
+ // name = ara, description = Arabic
+ { "ara", "", Qt::RightToLeft, QLocale::Arabic, QLocale::UnitedArabEmirates },
+ // name = ara:azerty, description = Arabic
+ { "ara", "azerty", Qt::RightToLeft, QLocale::Arabic, QLocale::UnitedArabEmirates },
+ // name = ara:azerty_digits, description = Arabic
+ { "ara", "azerty_digits", Qt::RightToLeft, QLocale::Arabic, QLocale::UnitedArabEmirates },
+ // name = ara:digits, description = Arabic
+ { "ara", "digits", Qt::RightToLeft, QLocale::Arabic, QLocale::UnitedArabEmirates },
+ // name = ara:qwerty, description = Arabic
+ { "ara", "qwerty", Qt::RightToLeft, QLocale::Arabic, QLocale::UnitedArabEmirates },
+ // name = ara:qwerty_digits, description = Arabic
+ { "ara", "qwerty_digits", Qt::RightToLeft, QLocale::Arabic, QLocale::UnitedArabEmirates },
+ // name = al, description = Albania
+ { "al", "", Qt::LeftToRight, QLocale::Albanian, QLocale::Albania },
+ // name = am, description = Armenia
+ { "am", "", Qt::LeftToRight, QLocale::Armenian, QLocale::Armenia },
+ // name = am:phonetic, description = Armenia
+ { "am", "phonetic", Qt::LeftToRight, QLocale::Armenian, QLocale::Armenia },
+ // name = az, description = Azerbaijan
+ { "az", "", Qt::LeftToRight, QLocale::Azerbaijani, QLocale::Azerbaijan },
+ // name = az:cyrillic, description = Azerbaijan
+ { "az", "cyrillic", Qt::LeftToRight, QLocale::Azerbaijani, QLocale::Azerbaijan },
+ // name = by, description = Belarus
+ { "by", "", Qt::LeftToRight, QLocale::Byelorussian, QLocale::Belarus },
+ // name = by:winkeys, description = Belarus
+ { "by", "winkeys", Qt::LeftToRight, QLocale::Byelorussian, QLocale::Belarus },
+ // name = be, description = Belgium
+ { "be", "", Qt::LeftToRight, QLocale::Dutch, QLocale::Belgium },
+ // name = be:iso-alternate, description = Belgium
+ { "be", "iso-alternate", Qt::LeftToRight, QLocale::Dutch, QLocale::Belgium },
+ // name = be:nodeadkeys, description = Belgium
+ { "be", "nodeadkeys", Qt::LeftToRight, QLocale::Dutch, QLocale::Belgium },
+ // name = be:sundeadkeys, description = Belgium
+ { "be", "sundeadkeys", Qt::LeftToRight, QLocale::Dutch, QLocale::Belgium },
+ // name = bd, description = Bangladesh
+ { "bd", "", Qt::LeftToRight, QLocale::Bengali, QLocale::Bangladesh },
+ // name = bd:probhat, description = Bangladesh
+ { "bd", "probhat", Qt::LeftToRight, QLocale::Bengali, QLocale::Bangladesh },
+ // name = in, description = India
+ { "in", "", Qt::LeftToRight, QLocale::Hindi, QLocale::India },
+ // name = in:ben, description = India
+ { "in", "ben", Qt::LeftToRight, QLocale::Bengali, QLocale::India },
+ // name = in:ben_probhat, description = India
+ { "in", "ben_probhat", Qt::LeftToRight, QLocale::Bengali, QLocale::India },
+ // name = in:guj, description = India
+ { "in", "guj", Qt::LeftToRight, QLocale::Gujarati, QLocale::India },
+ // name = in:guru, description = India
+ { "in", "guru", Qt::LeftToRight, QLocale::Punjabi, QLocale::India },
+ // name = in:kan, description = India
+ { "in", "kan", Qt::LeftToRight, QLocale::Kannada, QLocale::India },
+ // name = in:mal, description = India
+ { "in", "mal", Qt::LeftToRight, QLocale::Malayalam, QLocale::India },
+ // name = in:ori, description = India
+ { "in", "ori", Qt::LeftToRight, QLocale::Oriya, QLocale::India },
+ // name = in:tam_unicode, description = India
+ { "in", "tam_unicode", Qt::LeftToRight, QLocale::Tamil, QLocale::India },
+ // name = in:tam_TAB, description = India
+ { "in", "tam_TAB", Qt::LeftToRight, QLocale::Tamil, QLocale::India },
+ // name = in:tam_TSCII, description = India
+ { "in", "tam_TSCII", Qt::LeftToRight, QLocale::Tamil, QLocale::India },
+ // name = in:tam, description = India
+ { "in", "tam", Qt::LeftToRight, QLocale::Tamil, QLocale::India },
+ // name = in:tel, description = India
+ { "in", "tel", Qt::LeftToRight, QLocale::Telugu, QLocale::India },
+ // name = in:urd, description = India
+ { "in", "urd", Qt::RightToLeft, QLocale::Urdu, QLocale::India },
+ // name = ba, description = Bosnia and Herzegovina
+ { "ba", "", Qt::LeftToRight, QLocale::Bosnian, QLocale::BosniaAndHerzegowina },
+ // name = br, description = Brazil
+ { "br", "", Qt::LeftToRight, QLocale::Portuguese, QLocale::Brazil },
+ // name = br:nodeadkeys, description = Brazil
+ { "br", "nodeadkeys", Qt::LeftToRight, QLocale::Portuguese, QLocale::Brazil },
+ // name = bg, description = Bulgaria
+ { "bg", "", Qt::LeftToRight, QLocale::Bulgarian, QLocale::Bulgaria },
+ // name = bg:phonetic, description = Bulgaria
+ { "bg", "phonetic", Qt::LeftToRight, QLocale::Bulgarian, QLocale::Bulgaria },
+ // name = mm, description = Myanmar
+ { "mm", "", Qt::LeftToRight, QLocale::Burmese, QLocale::Myanmar },
+ // name = ca, description = Canada
+ { "ca", "", Qt::LeftToRight, QLocale::English, QLocale::Canada },
+ // name = ca:fr-dvorak, description = Canada
+ { "ca", "fr-dvorak", Qt::LeftToRight, QLocale::French, QLocale::Canada },
+ // name = ca:fr-legacy, description = Canada
+ { "ca", "fr-legacy", Qt::LeftToRight, QLocale::French, QLocale::Canada },
+ // name = ca:multi, description = Canada
+ { "ca", "multi", Qt::LeftToRight, QLocale::English, QLocale::Canada },
+ // name = ca:multi-2gr, description = Canada
+ { "ca", "multi-2gr", Qt::LeftToRight, QLocale::English, QLocale::Canada },
+ // name = ca:ike, description = Canada
+ { "ca", "ike", Qt::LeftToRight, QLocale::Inuktitut, QLocale::Canada },
+ // name = hr, description = Croatia
+ { "hr", "", Qt::LeftToRight, QLocale::Croatian, QLocale::Croatia },
+ // name = hr:us, description = Croatia
+ { "hr", "us", Qt::LeftToRight, QLocale::Croatian, QLocale::Croatia },
+ // name = cz, description = Czechia
+ { "cz", "", Qt::LeftToRight, QLocale::Czech, QLocale::CzechRepublic },
+ // name = cz:bksl, description = Czechia
+ { "cz", "bksl", Qt::LeftToRight, QLocale::Czech, QLocale::CzechRepublic },
+ // name = cz:qwerty, description = Czechia
+ { "cz", "qwerty", Qt::LeftToRight, QLocale::Czech, QLocale::CzechRepublic },
+ // name = cz:qwerty_bksl, description = Czechia
+ { "cz", "qwerty_bksl", Qt::LeftToRight, QLocale::Czech, QLocale::CzechRepublic },
+ // name = dk, description = Denmark
+ { "dk", "", Qt::LeftToRight, QLocale::Danish, QLocale::Denmark },
+ // name = dk:nodeadkeys, description = Denmark
+ { "dk", "nodeadkeys", Qt::LeftToRight, QLocale::Danish, QLocale::Denmark },
+ // name = nl, description = Netherlands
+ { "nl", "", Qt::LeftToRight, QLocale::Dutch, QLocale::Netherlands },
+ // name = bt, description = Bhutan
+ { "bt", "", Qt::LeftToRight, QLocale::Bhutani, QLocale::Bhutan },
+ // name = ee, description = Estonia
+ { "ee", "", Qt::LeftToRight, QLocale::Estonian, QLocale::Estonia },
+ // name = ee:nodeadkeys, description = Estonia
+ { "ee", "nodeadkeys", Qt::LeftToRight, QLocale::Estonian, QLocale::Estonia },
+ // name = ir, description = Iran
+ { "ir", "", Qt::RightToLeft, QLocale::Persian, QLocale::Iran },
+ // name = fo, description = Faroe Islands
+ { "fo", "", Qt::LeftToRight, QLocale::Faroese, QLocale::FaroeIslands },
+ // name = fo:nodeadkeys, description = Faroe Islands
+ { "fo", "nodeadkeys", Qt::LeftToRight, QLocale::Faroese, QLocale::FaroeIslands },
+ // name = fi, description = Finland
+ { "fi", "", Qt::LeftToRight, QLocale::Finnish, QLocale::Finland },
+ // name = fi:nodeadkeys, description = Finland
+ { "fi", "nodeadkeys", Qt::LeftToRight, QLocale::Finnish, QLocale::Finland },
+ // name = fi:smi, description = Finland
+ { "fi", "smi", Qt::LeftToRight, QLocale::Finnish, QLocale::Finland },
+ // name = fr, description = France
+ { "fr", "", Qt::LeftToRight, QLocale::French, QLocale::France },
+ // name = fr:nodeadkeys, description = France
+ { "fr", "nodeadkeys", Qt::LeftToRight, QLocale::French, QLocale::France },
+ // name = fr:sundeadkeys, description = France
+ { "fr", "sundeadkeys", Qt::LeftToRight, QLocale::French, QLocale::France },
+ // name = fr:latin9, description = France
+ { "fr", "latin9", Qt::LeftToRight, QLocale::French, QLocale::France },
+ // name = fr:latin9_nodeadkeys, description = France
+ { "fr", "latin9_nodeadkeys", Qt::LeftToRight, QLocale::French, QLocale::France },
+ // name = fr:latin9_sundeadkeys, description = France
+ { "fr", "latin9_sundeadkeys", Qt::LeftToRight, QLocale::French, QLocale::France },
+ // name = fr:dvorak, description = France
+ { "fr", "dvorak", Qt::LeftToRight, QLocale::French, QLocale::France },
+ // name = ge, description = Georgia
+ { "ge", "", Qt::LeftToRight, QLocale::Georgian, QLocale::Georgia },
+ // name = ge:ru, description = Georgia
+ { "ge", "ru", Qt::LeftToRight, QLocale::Russian, QLocale::Georgia },
+ // name = de, description = Germany
+ { "de", "", Qt::LeftToRight, QLocale::German, QLocale::Germany },
+ // name = de:deadacute, description = Germany
+ { "de", "deadacute", Qt::LeftToRight, QLocale::German, QLocale::Germany },
+ // name = de:deadgraveacute, description = Germany
+ { "de", "deadgraveacute", Qt::LeftToRight, QLocale::German, QLocale::Germany },
+ // name = de:nodeadkeys, description = Germany
+ { "de", "nodeadkeys", Qt::LeftToRight, QLocale::German, QLocale::Germany },
+ // name = de:ro, description = Germany
+ { "de", "ro", Qt::LeftToRight, QLocale::Romanian, QLocale::Germany },
+ // name = de:ro_nodeadkeys, description = Germany
+ { "de", "ro_nodeadkeys", Qt::LeftToRight, QLocale::Romanian, QLocale::Germany },
+ // name = de:dvorak, description = Germany
+ { "de", "dvorak", Qt::LeftToRight, QLocale::German, QLocale::Germany },
+ // name = gr, description = Greece
+ { "gr", "", Qt::LeftToRight, QLocale::Greek, QLocale::Greece },
+ // name = gr:extended, description = Greece
+ { "gr", "extended", Qt::LeftToRight, QLocale::Greek, QLocale::Greece },
+ // name = gr:nodeadkeys, description = Greece
+ { "gr", "nodeadkeys", Qt::LeftToRight, QLocale::Greek, QLocale::Greece },
+ // name = gr:polytonic, description = Greece
+ { "gr", "polytonic", Qt::LeftToRight, QLocale::Greek, QLocale::Greece },
+ // name = hu, description = Hungary
+ { "hu", "", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:standard, description = Hungary
+ { "hu", "standard", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:nodeadkeys, description = Hungary
+ { "hu", "nodeadkeys", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:qwerty, description = Hungary
+ { "hu", "qwerty", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:101_qwertz_comma_dead, description = Hungary
+ { "hu", "101_qwertz_comma_dead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:101_qwertz_comma_nodead, description = Hungary
+ { "hu", "101_qwertz_comma_nodead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:101_qwertz_dot_dead, description = Hungary
+ { "hu", "101_qwertz_dot_dead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:101_qwertz_dot_nodead, description = Hungary
+ { "hu", "101_qwertz_dot_nodead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:101_qwerty_comma_dead, description = Hungary
+ { "hu", "101_qwerty_comma_dead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:101_qwerty_comma_nodead, description = Hungary
+ { "hu", "101_qwerty_comma_nodead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:101_qwerty_dot_dead, description = Hungary
+ { "hu", "101_qwerty_dot_dead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:101_qwerty_dot_nodead, description = Hungary
+ { "hu", "101_qwerty_dot_nodead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:102_qwertz_comma_dead, description = Hungary
+ { "hu", "102_qwertz_comma_dead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:102_qwertz_comma_nodead, description = Hungary
+ { "hu", "102_qwertz_comma_nodead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:102_qwertz_dot_dead, description = Hungary
+ { "hu", "102_qwertz_dot_dead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:102_qwertz_dot_nodead, description = Hungary
+ { "hu", "102_qwertz_dot_nodead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:102_qwerty_comma_dead, description = Hungary
+ { "hu", "102_qwerty_comma_dead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:102_qwerty_comma_nodead, description = Hungary
+ { "hu", "102_qwerty_comma_nodead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:102_qwerty_dot_dead, description = Hungary
+ { "hu", "102_qwerty_dot_dead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = hu:102_qwerty_dot_nodead, description = Hungary
+ { "hu", "102_qwerty_dot_nodead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary },
+ // name = is, description = Iceland
+ { "is", "", Qt::LeftToRight, QLocale::Icelandic, QLocale::Iceland },
+ // name = is:Sundeadkeys, description = Iceland
+ { "is", "Sundeadkeys", Qt::LeftToRight, QLocale::Icelandic, QLocale::Iceland },
+ // name = is:nodeadkeys, description = Iceland
+ { "is", "nodeadkeys", Qt::LeftToRight, QLocale::Icelandic, QLocale::Iceland },
+ // name = il, description = Israel
+ { "il", "", Qt::RightToLeft, QLocale::Hebrew, QLocale::Israel },
+ // name = il:lyx, description = Israel
+ { "il", "lyx", Qt::RightToLeft, QLocale::Hebrew, QLocale::Israel },
+ // name = il:si1452, description = Israel
+ { "il", "si1452", Qt::RightToLeft, QLocale::Hebrew, QLocale::Israel },
+ // name = il:phonetic, description = Israel
+ { "il", "phonetic", Qt::RightToLeft, QLocale::Hebrew, QLocale::Israel },
+ // name = it, description = Italy
+ { "it", "", Qt::LeftToRight, QLocale::Italian, QLocale::Italy },
+ // name = it:nodeadkeys, description = Italy
+ { "it", "nodeadkeys", Qt::LeftToRight, QLocale::Italian, QLocale::Italy },
+ // name = jp, description = Japan
+ { "jp", "", Qt::LeftToRight, QLocale::Japanese, QLocale::Japan },
+ // name = kg, description = Kyrgyzstan
+ { "kg", "", Qt::LeftToRight, QLocale::Kirghiz, QLocale::Kyrgyzstan },
+ // name = la, description = Laos
+ { "la", "", Qt::LeftToRight, QLocale::Laothian, QLocale::Lao },
+ // name = latam, description = Latin American
+ { "latam", "", Qt::LeftToRight, QLocale::Spanish, QLocale::Mexico },
+ // name = latam:nodeadkeys, description = Latin American
+ { "latam", "nodeadkeys", Qt::LeftToRight, QLocale::Spanish, QLocale::Mexico },
+ // name = latam:sundeadkeys, description = Latin American
+ { "latam", "sundeadkeys", Qt::LeftToRight, QLocale::Spanish, QLocale::Mexico },
+ // name = lt, description = Lithuania
+ { "lt", "", Qt::LeftToRight, QLocale::Lithuanian, QLocale::Lithuania },
+ // name = lt:std, description = Lithuania
+ { "lt", "std", Qt::LeftToRight, QLocale::Lithuanian, QLocale::Lithuania },
+ // name = lt:us, description = Lithuania
+ { "lt", "us", Qt::LeftToRight, QLocale::Lithuanian, QLocale::Lithuania },
+ // name = lv, description = Latvia
+ { "lv", "", Qt::LeftToRight, QLocale::Latvian, QLocale::Latvia },
+ // name = lv:apostrophe, description = Latvia
+ { "lv", "apostrophe", Qt::LeftToRight, QLocale::Latvian, QLocale::Latvia },
+ // name = lv:tilde, description = Latvia
+ { "lv", "tilde", Qt::LeftToRight, QLocale::Latvian, QLocale::Latvia },
+ // name = lv:fkey, description = Latvia
+ { "lv", "fkey", Qt::LeftToRight, QLocale::Latvian, QLocale::Latvia },
+ // name = mao, description = Maori
+ { "mao", "", Qt::LeftToRight, QLocale::Maori, QLocale::NewZealand },
+ // name = mkd, description = Macedonian
+ { "mkd", "", Qt::LeftToRight, QLocale::Macedonian, QLocale::Macedonia },
+ // name = mkd:nodeadkeys, description = Macedonian
+ { "mkd", "nodeadkeys", Qt::LeftToRight, QLocale::Macedonian, QLocale::Macedonia },
+ // name = mt, description = Malta
+ { "mt", "", Qt::LeftToRight, QLocale::Maltese, QLocale::Malta },
+ // name = mt:us, description = Malta
+ { "mt", "us", Qt::LeftToRight, QLocale::Maltese, QLocale::Malta },
+ // name = mn, description = Mongolia
+ { "mn", "", Qt::LeftToRight, QLocale::Mongolian, QLocale::Mongolia },
+ // name = no, description = Norway
+ { "no", "", Qt::LeftToRight, QLocale::Norwegian, QLocale::Norway },
+ // name = no:nodeadkeys, description = Norway
+ { "no", "nodeadkeys", Qt::LeftToRight, QLocale::Norwegian, QLocale::Norway },
+ // name = no:dvorak, description = Norway
+ { "no", "dvorak", Qt::LeftToRight, QLocale::Norwegian, QLocale::Norway },
+ // name = no:smi, description = Norway
+ { "no", "smi", Qt::LeftToRight, QLocale::Norwegian, QLocale::Norway },
+ // name = no:smi_nodeadkeys, description = Norway
+ { "no", "smi_nodeadkeys", Qt::LeftToRight, QLocale::Norwegian, QLocale::Norway },
+ // name = pl, description = Poland
+ { "pl", "", Qt::LeftToRight, QLocale::Polish, QLocale::Poland },
+ // name = pl:qwertz, description = Poland
+ { "pl", "qwertz", Qt::LeftToRight, QLocale::Polish, QLocale::Poland },
+ // name = pl:dvorak, description = Poland
+ { "pl", "dvorak", Qt::LeftToRight, QLocale::Polish, QLocale::Poland },
+ // name = pl:dvorak_quotes, description = Poland
+ { "pl", "dvorak_quotes", Qt::LeftToRight, QLocale::Polish, QLocale::Poland },
+ // name = pl:dvorak_altquotes, description = Poland
+ { "pl", "dvorak_altquotes", Qt::LeftToRight, QLocale::Polish, QLocale::Poland },
+ // name = pt, description = Portugal
+ { "pt", "", Qt::LeftToRight, QLocale::Portuguese, QLocale::Portugal },
+ // name = pt:nodeadkeys, description = Portugal
+ { "pt", "nodeadkeys", Qt::LeftToRight, QLocale::Portuguese, QLocale::Portugal },
+ // name = pt:sundeadkeys, description = Portugal
+ { "pt", "sundeadkeys", Qt::LeftToRight, QLocale::Portuguese, QLocale::Portugal },
+ // name = ro, description = Romania
+ { "ro", "", Qt::LeftToRight, QLocale::Romanian, QLocale::Romania },
+ // name = ro:us, description = Romania
+ { "ro", "us", Qt::LeftToRight, QLocale::English, QLocale::Romania },
+ // name = ro:de, description = Romania
+ { "ro", "de", Qt::LeftToRight, QLocale::German, QLocale::Romania },
+ // name = ru, description = Russia
+ { "ru", "", Qt::LeftToRight, QLocale::Russian, QLocale::RussianFederation },
+ // name = ru:phonetic, description = Russia
+ { "ru", "phonetic", Qt::LeftToRight, QLocale::Russian, QLocale::RussianFederation },
+ // name = ru:typewriter, description = Russia
+ { "ru", "typewriter", Qt::LeftToRight, QLocale::Russian, QLocale::RussianFederation },
+ // name = ru:winkeys, description = Russia
+ { "ru", "winkeys", Qt::LeftToRight, QLocale::Russian, QLocale::RussianFederation },
+ // name = srp, description = Serbian
+ { "srp", "", Qt::LeftToRight, QLocale::Serbian, QLocale::SerbiaAndMontenegro },
+ // name = srp:yz, description = Serbian
+ { "srp", "yz", Qt::LeftToRight, QLocale::Serbian, QLocale::SerbiaAndMontenegro },
+ // name = srp:latin, description = Serbian
+ { "srp", "latin", Qt::LeftToRight, QLocale::Serbian, QLocale::SerbiaAndMontenegro },
+ // name = srp:latinunicode, description = Serbian
+ { "srp", "latinunicode", Qt::LeftToRight, QLocale::Serbian, QLocale::SerbiaAndMontenegro },
+ // name = srp:latinyz, description = Serbian
+ { "srp", "latinyz", Qt::LeftToRight, QLocale::Serbian, QLocale::SerbiaAndMontenegro },
+ // name = srp:latinunicodeyz, description = Serbian
+ { "srp", "latinunicodeyz", Qt::LeftToRight, QLocale::Serbian, QLocale::SerbiaAndMontenegro },
+ // name = srp:alternatequotes, description = Serbian
+ { "srp", "alternatequotes", Qt::LeftToRight, QLocale::Serbian, QLocale::SerbiaAndMontenegro },
+ // name = srp:latinalternatequotes, description = Serbian
+ { "srp", "latinalternatequotes", Qt::LeftToRight, QLocale::Serbian, QLocale::SerbiaAndMontenegro },
+ // name = si, description = Slovenia
+ { "si", "", Qt::LeftToRight, QLocale::Slovenian, QLocale::Slovenia },
+ // name = sk, description = Slovakia
+ { "sk", "", Qt::LeftToRight, QLocale::Slovak, QLocale::Slovakia },
+ // name = sk:bksl, description = Slovakia
+ { "sk", "bksl", Qt::LeftToRight, QLocale::Slovak, QLocale::Slovakia },
+ // name = sk:qwerty, description = Slovakia
+ { "sk", "qwerty", Qt::LeftToRight, QLocale::Slovak, QLocale::Slovakia },
+ // name = sk:qwerty_bksl, description = Slovakia
+ { "sk", "qwerty_bksl", Qt::LeftToRight, QLocale::Slovak, QLocale::Slovakia },
+ // name = es, description = Spain
+ { "es", "", Qt::LeftToRight, QLocale::Spanish, QLocale::Spain },
+ // name = es:nodeadkeys, description = Spain
+ { "es", "nodeadkeys", Qt::LeftToRight, QLocale::Spanish, QLocale::Spain },
+ // name = es:sundeadkeys, description = Spain
+ { "es", "sundeadkeys", Qt::LeftToRight, QLocale::Spanish, QLocale::Spain },
+ // name = es:dvorak, description = Spain
+ { "es", "dvorak", Qt::LeftToRight, QLocale::Spanish, QLocale::Spain },
+ // name = se, description = Sweden
+ { "se", "", Qt::LeftToRight, QLocale::Swedish, QLocale::Sweden },
+ // name = se:nodeadkeys, description = Sweden
+ { "se", "nodeadkeys", Qt::LeftToRight, QLocale::Swedish, QLocale::Sweden },
+ // name = se:dvorak, description = Sweden
+ { "se", "dvorak", Qt::LeftToRight, QLocale::Swedish, QLocale::Sweden },
+ // name = se:rus, description = Sweden
+ { "se", "rus", Qt::LeftToRight, QLocale::Russian, QLocale::Sweden },
+ // name = se:rus_nodeadkeys, description = Sweden
+ { "se", "rus_nodeadkeys", Qt::LeftToRight, QLocale::Russian, QLocale::Sweden },
+ // name = se:smi, description = Sweden
+ { "se", "smi", Qt::LeftToRight, QLocale::Swedish, QLocale::Sweden },
+ // name = ch, description = Switzerland
+ { "ch", "", Qt::LeftToRight, QLocale::German, QLocale::Switzerland },
+ // name = ch:de_nodeadkeys, description = Switzerland
+ { "ch", "de_nodeadkeys", Qt::LeftToRight, QLocale::German, QLocale::Switzerland },
+ // name = ch:de_sundeadkeys, description = Switzerland
+ { "ch", "de_sundeadkeys", Qt::LeftToRight, QLocale::German, QLocale::Switzerland },
+ // name = ch:fr, description = Switzerland
+ { "ch", "fr", Qt::LeftToRight, QLocale::French, QLocale::Switzerland },
+ // name = ch:fr_nodeadkeys, description = Switzerland
+ { "ch", "fr_nodeadkeys", Qt::LeftToRight, QLocale::French, QLocale::Switzerland },
+ // name = ch:fr_sundeadkeys, description = Switzerland
+ { "ch", "fr_sundeadkeys", Qt::LeftToRight, QLocale::French, QLocale::Switzerland },
+ // name = sy, description = Syria
+ { "sy", "", Qt::RightToLeft, QLocale::Syriac, QLocale::SyrianArabRepublic },
+ // name = sy:syc, description = Syria
+ { "sy", "syc", Qt::RightToLeft, QLocale::Syriac, QLocale::SyrianArabRepublic },
+ // name = sy:syc_phonetic, description = Syria
+ { "sy", "syc_phonetic", Qt::RightToLeft, QLocale::Syriac, QLocale::SyrianArabRepublic },
+ // name = tj, description = Tajikistan
+ { "tj", "", Qt::LeftToRight, QLocale::Tajik, QLocale::Tajikistan },
+ // name = lk, description = Sri Lanka
+ { "lk", "", Qt::LeftToRight, QLocale::Singhalese, QLocale::SriLanka },
+ // name = lk:tam_unicode, description = Sri Lanka
+ { "lk", "tam_unicode", Qt::LeftToRight, QLocale::Tamil, QLocale::SriLanka },
+ // name = lk:tam_TAB, description = Sri Lanka
+ { "lk", "tam_TAB", Qt::LeftToRight, QLocale::Tamil, QLocale::SriLanka },
+ // name = lk:tam_TSCII, description = Sri Lanka
+ { "lk", "tam_TSCII", Qt::LeftToRight, QLocale::Tamil, QLocale::SriLanka },
+ // name = lk:sin_phonetic, description = Sri Lanka
+ { "lk", "sin_phonetic", Qt::LeftToRight, QLocale::Singhalese, QLocale::SriLanka },
+ // name = th, description = Thailand
+ { "th", "", Qt::LeftToRight, QLocale::Thai, QLocale::Thailand },
+ // name = th:tis, description = Thailand
+ { "th", "tis", Qt::LeftToRight, QLocale::Thai, QLocale::Thailand },
+ // name = th:pat, description = Thailand
+ { "th", "pat", Qt::LeftToRight, QLocale::Thai, QLocale::Thailand },
+ // name = tr, description = Turkish
+ { "tr", "", Qt::LeftToRight, QLocale::Turkish, QLocale::Turkey },
+ // name = tr:f, description = Turkish
+ { "tr", "f", Qt::LeftToRight, QLocale::Turkish, QLocale::Turkey },
+ // name = tr:alt, description = Turkish
+ { "tr", "alt", Qt::LeftToRight, QLocale::Turkish, QLocale::Turkey },
+ // name = ua, description = Ukraine
+ { "ua", "", Qt::LeftToRight, QLocale::Ukrainian, QLocale::Ukraine },
+ // name = ua:phonetic, description = Ukraine
+ { "ua", "phonetic", Qt::LeftToRight, QLocale::Ukrainian, QLocale::Ukraine },
+ // name = ua:typewriter, description = Ukraine
+ { "ua", "typewriter", Qt::LeftToRight, QLocale::Ukrainian, QLocale::Ukraine },
+ // name = ua:winkeys, description = Ukraine
+ { "ua", "winkeys", Qt::LeftToRight, QLocale::Ukrainian, QLocale::Ukraine },
+ // name = ua:rstu, description = Ukraine
+ { "ua", "rstu", Qt::LeftToRight, QLocale::Ukrainian, QLocale::Ukraine },
+ // name = ua:rstu_ru, description = Ukraine
+ { "ua", "rstu_ru", Qt::LeftToRight, QLocale::Ukrainian, QLocale::Ukraine },
+ // name = gb, description = United Kingdom
+ { "gb", "", Qt::LeftToRight, QLocale::English, QLocale::UnitedKingdom },
+ // name = gb:intl, description = United Kingdom
+ { "gb", "intl", Qt::LeftToRight, QLocale::English, QLocale::UnitedKingdom },
+ // name = gb:dvorak, description = United Kingdom
+ { "gb", "dvorak", Qt::LeftToRight, QLocale::English, QLocale::UnitedKingdom },
+ // name = uz, description = Uzbekistan
+ { "uz", "", Qt::LeftToRight, QLocale::Uzbek, QLocale::Uzbekistan },
+ // name = vn, description = Vietnam
+ { "vn", "", Qt::LeftToRight, QLocale::Vietnamese, QLocale::VietNam },
+ // name = nec_vndr/jp, description = PC-98xx Series
+ { "nec_vndr/jp", "", Qt::LeftToRight, QLocale::Japanese, QLocale::Japan },
+ // name = ie, description = Ireland
+ { "ie", "", Qt::LeftToRight, QLocale::Irish, QLocale::Ireland },
+ // name = ie:CloGaelach, description = Ireland
+ { "ie", "CloGaelach", Qt::LeftToRight, QLocale::Gaelic, QLocale::Ireland },
+ // name = ie:UnicodeExpert, description = Ireland
+ { "ie", "UnicodeExpert", Qt::LeftToRight, QLocale::Irish, QLocale::Ireland },
+ // name = ie:ogam, description = Ireland
+ { "ie", "ogam", Qt::LeftToRight, QLocale::Gaelic, QLocale::Ireland },
+ // name = ie:ogam_is434, description = Ireland
+ { "ie", "ogam_is434", Qt::LeftToRight, QLocale::Gaelic, QLocale::Ireland },
+ // name = pk, description = Pakistan
+ { "pk", "", Qt::RightToLeft, QLocale::Urdu, QLocale::Pakistan },
+ { 0, 0, Qt::LeftToRight, QLocale::C, QLocale::AnyCountry }
+};
diff --git a/src/widgets/platforms/x11/qmotifdnd_x11.cpp b/src/widgets/platforms/x11/qmotifdnd_x11.cpp
new file mode 100644
index 0000000000..77f6ce5827
--- /dev/null
+++ b/src/widgets/platforms/x11/qmotifdnd_x11.cpp
@@ -0,0 +1,1031 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/* The following copyright notice pertains to the code as contributed
+to Qt, not to Nokia's modifications. It is replicated
+in doc/dnd.doc, where the documentation system can see it. */
+
+/* Copyright 1996 Daniel Dardailler.
+
+ Permission to use, copy, modify, distribute, and sell this software
+ for any purpose is hereby granted without fee, provided that the above
+ copyright notice appear in all copies and that both that copyright
+ notice and this permission notice appear in supporting documentation,
+ and that the name of Daniel Dardailler not be used in advertising or
+ publicity pertaining to distribution of the software without specific,
+ written prior permission. Daniel Dardailler makes no representations
+ about the suitability of this software for any purpose. It is
+ provided "as is" without express or implied warranty.
+
+ Modifications Copyright 1999 Matt Koss, under the same license as
+ above.
+************************************************************/
+
+/***********************************************************/
+/* Motif Drag&Drop Dynamic Protocol messaging API code */
+/* Only requires Xlib layer - not MT safe */
+/* Author: Daniel Dardailler, daniel@x.org */
+/* Adapted by: Matt Koss, koss@napri.sk */
+/* Further adaptions by: Nokia Corporation and/or its subsidiary(-ies) */
+/***********************************************************/
+
+#include "qplatformdefs.h"
+
+#include "qapplication.h"
+
+#ifndef QT_NO_DRAGANDDROP
+
+#include "qdebug.h"
+#include "qtextcodec.h"
+#include "qwidget.h"
+#include "qevent.h"
+#include "qt_x11_p.h"
+#include "qx11info_x11.h"
+#include "qiodevice.h"
+#include "qdnd_p.h"
+
+#include <stdlib.h>
+
+QT_BEGIN_NAMESPACE
+
+static Window sourceWindow = XNone;
+static QWidget *dropWidget = 0;
+static Qt::DropAction lastAcceptedAction = Qt::IgnoreAction;
+
+static Atom Dnd_selection = 0;
+static Time Dnd_selection_time;
+
+static Atom * src_targets ;
+static ushort num_src_targets ;
+
+// Motif definitions
+#define DndVersion 1
+#define DndRevision 0
+#define DndIncludeVersion (DndVersion * 10 + DndRevision)
+
+/* The following values are used in the DndData structure */
+
+/* protocol style */
+#define DND_DRAG_NONE 0
+#define DND_DRAG_DROP_ONLY 1
+#define DND_DRAG_DYNAMIC 5
+
+/* message type */
+#define DND_TOP_LEVEL_ENTER 0
+#define DND_TOP_LEVEL_LEAVE 1
+#define DND_DRAG_MOTION 2
+#define DND_DROP_SITE_ENTER 3
+#define DND_DROP_SITE_LEAVE 4
+#define DND_DROP_START 5
+#define DND_OPERATION_CHANGED 8
+
+/* operation(s) */
+#define DND_NOOP 0L
+#define DND_MOVE (1L << 0)
+#define DND_COPY (1L << 1)
+#define DND_LINK (1L << 2)
+
+static Qt::DropActions DndOperationsToQtDropActions(uchar op)
+{
+ Qt::DropActions actions = Qt::IgnoreAction;
+ if (op | DND_MOVE)
+ actions |= Qt::MoveAction;
+ if (op | DND_COPY)
+ actions |= Qt::CopyAction;
+ if (op | DND_LINK)
+ actions |= Qt::LinkAction;
+ return actions;
+}
+
+static uchar QtDropActionToDndOperation(Qt::DropAction action)
+{
+ switch (action & Qt::ActionMask) {
+ case Qt::CopyAction:
+ default:
+ return DND_COPY;
+ case Qt::MoveAction:
+ return DND_MOVE;
+ case Qt::LinkAction:
+ return DND_LINK;
+ }
+}
+
+
+/* status */
+#define DND_NO_DROP_SITE 1
+#define DND_INVALID_DROP_SITE 2
+#define DND_VALID_DROP_SITE 3
+
+/* completion */
+#define DND_DROP 0
+#define DND_DROP_HELP 1
+#define DND_DROP_CANCEL 2
+
+#define BYTE unsigned char
+#define CARD32 unsigned int
+#define CARD16 unsigned short
+#define INT16 signed short
+
+/* Client side structure used in the API */
+typedef struct {
+ unsigned char reason; /* message type: DND_TOP_LEVEL_ENTER, etc */
+ Time time ;
+ unsigned char operation;
+ unsigned char operations;
+ unsigned char status;
+ unsigned char completion;
+ short x ;
+ short y ;
+ Window src_window ;
+ Atom property ;
+} DndData ;
+
+
+typedef struct _DndSrcProp {
+ BYTE byte_order ;
+ BYTE protocol_version ;
+ CARD16 target_index ;
+ CARD32 selection ;
+} DndSrcProp ;
+
+typedef struct _DndReceiverProp {
+ BYTE byte_order ;
+ BYTE protocol_version ;
+ BYTE protocol_style ;
+ BYTE pad1;
+ CARD32 proxy_window;
+ CARD16 num_drop_sites ;
+ CARD16 pad2;
+ CARD32 total_size;
+} DndReceiverProp ;
+
+/* need to use some union hack since window and property are in
+ different order depending on the message ... */
+typedef struct _DndTop {
+ CARD32 src_window;
+ CARD32 property;
+} DndTop ;
+
+typedef struct _DndPot {
+ INT16 x;
+ INT16 y;
+ CARD32 property;
+ CARD32 src_window;
+} DndPot ;
+
+typedef struct _DndMessage {
+ BYTE reason;
+ BYTE byte_order;
+ CARD16 flags;
+ CARD32 time;
+ union {
+ DndTop top ;
+ DndPot pot ;
+ } data ;
+} DndMessage ;
+
+typedef struct {
+ BYTE byte_order;
+ BYTE protocol_version;
+ CARD16 num_target_lists;
+ CARD32 data_size;
+ /* then come series of CARD16,CARD32,CARD32,CARD32... */
+} DndTargets;
+
+
+/* protocol version */
+#define DND_PROTOCOL_VERSION 0
+
+
+#define DND_EVENT_TYPE_MASK ((BYTE)0x80)
+#define DND_EVENT_TYPE_SHIFT 7
+#define DND_CLEAR_EVENT_TYPE ((BYTE)0x7F)
+
+/* message_type is data[0] of the client_message
+ this return 1 (receiver bit up) or 0 (initiator) */
+#define DND_GET_EVENT_TYPE(message_type) \
+((char) (((message_type) & DND_EVENT_TYPE_MASK) >> DND_EVENT_TYPE_SHIFT))
+
+/* event_type can be 0 (initiator) or 1 (receiver) */
+#define DND_SET_EVENT_TYPE(event_type) \
+(((BYTE)(event_type) << DND_EVENT_TYPE_SHIFT) & DND_EVENT_TYPE_MASK)
+
+
+#define DND_OPERATION_MASK ((CARD16) 0x000F)
+#define DND_OPERATION_SHIFT 0
+#define DND_STATUS_MASK ((CARD16) 0x00F0)
+#define DND_STATUS_SHIFT 4
+#define DND_OPERATIONS_MASK ((CARD16) 0x0F00)
+#define DND_OPERATIONS_SHIFT 8
+#define DND_COMPLETION_MASK ((CARD16) 0xF000)
+#define DND_COMPLETION_SHIFT 12
+
+#define DND_GET_OPERATION(flags) \
+((unsigned char) \
+(((flags) & DND_OPERATION_MASK) >> DND_OPERATION_SHIFT))
+
+#define DND_SET_OPERATION(operation) \
+(((CARD16)(operation) << DND_OPERATION_SHIFT)\
+& DND_OPERATION_MASK)
+
+#define DND_GET_STATUS(flags) \
+((unsigned char) \
+(((flags) & DND_STATUS_MASK) >> DND_STATUS_SHIFT))
+
+#define DND_SET_STATUS(status) \
+(((CARD16)(status) << DND_STATUS_SHIFT)\
+& DND_STATUS_MASK)
+
+#define DND_GET_OPERATIONS(flags) \
+((unsigned char) \
+(((flags) & DND_OPERATIONS_MASK) >> DND_OPERATIONS_SHIFT))
+
+#define DND_SET_OPERATIONS(operation) \
+(((CARD16)(operation) << DND_OPERATIONS_SHIFT)\
+& DND_OPERATIONS_MASK)
+
+#define DND_GET_COMPLETION(flags) \
+((unsigned char) \
+(((flags) & DND_COMPLETION_MASK) >> DND_COMPLETION_SHIFT))
+
+#define DND_SET_COMPLETION(completion) \
+(((CARD16)(completion) << DND_COMPLETION_SHIFT)\
+& DND_COMPLETION_MASK)
+
+
+#define SWAP4BYTES(l) {\
+struct { unsigned t :32;} bit32;\
+char n, *tp = (char *) &bit32;\
+bit32.t = l;\
+n = tp[0]; tp[0] = tp[3]; tp[3] = n;\
+n = tp[1]; tp[1] = tp[2]; tp[2] = n;\
+l = bit32.t;\
+}
+
+#define SWAP2BYTES(s) {\
+struct { unsigned t :16; } bit16;\
+char n, *tp = (char *) &bit16;\
+bit16.t = s;\
+n = tp[0]; tp[0] = tp[1]; tp[1] = n;\
+s = bit16.t;\
+}
+
+
+/** Private extern functions */
+
+static unsigned char DndByteOrder ();
+
+
+/***** Targets/Index stuff */
+
+typedef struct {
+ int num_targets;
+ Atom *targets;
+} DndTargetsTableEntryRec, * DndTargetsTableEntry;
+
+typedef struct {
+ int num_entries;
+ DndTargetsTableEntry entries;
+} DndTargetsTableRec, * DndTargetsTable;
+
+
+static ushort _DndIndexToTargets(Display * display,
+ int index,
+ Atom ** targets);
+
+extern void qt_x11_intern_atom(const char *, Atom *);
+
+/////////////////////////////////////////////////////////////////
+
+static unsigned char DndByteOrder ()
+{
+ static unsigned char byte_order = 0;
+
+ if (!byte_order) {
+ unsigned int endian = 1;
+ byte_order = (*((char *)&endian))?'l':'B';
+ }
+ return byte_order ;
+}
+
+
+
+static void DndReadSourceProperty(Display * dpy,
+ Window window, Atom dnd_selection,
+ Atom ** targets, unsigned short * num_targets)
+{
+ unsigned char *retval = 0;
+ Atom type ;
+ int format ;
+ unsigned long bytesafter, lengthRtn;
+
+ if ((XGetWindowProperty (dpy, window, dnd_selection, 0L, 100000L,
+ False, ATOM(_MOTIF_DRAG_INITIATOR_INFO), &type,
+ &format, &lengthRtn, &bytesafter,
+ &retval) != Success)
+ || (type == XNone)) {
+ *num_targets = 0;
+ return ;
+ }
+
+ DndSrcProp * src_prop = (DndSrcProp *)retval;
+
+ if (src_prop->byte_order != DndByteOrder()) {
+ SWAP2BYTES(src_prop->target_index);
+ SWAP4BYTES(src_prop->selection);
+ }
+
+ *num_targets = _DndIndexToTargets(dpy, src_prop->target_index, targets);
+
+ XFree((char*)src_prop);
+}
+
+
+/* Position the _MOTIF_DRAG_RECEIVER_INFO property on the dropsite window.
+ Called by the receiver of the drop to indicate the
+ supported protocol style : dynamic, drop_only or none */
+static void DndWriteReceiverProperty(Display * dpy, Window window,
+ unsigned char protocol_style)
+{
+ DndReceiverProp receiver_prop;
+
+ // squelch potential valgrind errors about uninitialized reads
+ memset(&receiver_prop, 0, sizeof(receiver_prop));
+
+ receiver_prop.byte_order = DndByteOrder() ;
+ receiver_prop.protocol_version = DND_PROTOCOL_VERSION;
+ receiver_prop.protocol_style = protocol_style ;
+ receiver_prop.proxy_window = XNone ;
+ receiver_prop.num_drop_sites = 0 ;
+ receiver_prop.total_size = sizeof(DndReceiverProp);
+
+ /* write the buffer to the property */
+ XChangeProperty (dpy, window, ATOM(_MOTIF_DRAG_RECEIVER_INFO), ATOM(_MOTIF_DRAG_RECEIVER_INFO),
+ 8, PropModeReplace,
+ (unsigned char *)&receiver_prop,
+ sizeof(DndReceiverProp));
+}
+
+
+/* protocol style equiv (preregister stuff really) */
+#define DND_DRAG_DROP_ONLY_EQUIV 3
+#define DND_DRAG_DYNAMIC_EQUIV1 2
+#define DND_DRAG_DYNAMIC_EQUIV2 4
+
+
+/* Produce a client message to be sent by the caller */
+static void DndFillClientMessage(Display * dpy, Window window,
+ XClientMessageEvent *cm,
+ DndData * dnd_data,
+ char receiver)
+{
+ DndMessage * dnd_message = (DndMessage*)&cm->data.b[0] ;
+
+ cm->display = dpy;
+ cm->type = ClientMessage;
+ cm->serial = LastKnownRequestProcessed(dpy);
+ cm->send_event = True;
+ cm->window = window;
+ cm->format = 8;
+ cm->message_type = ATOM(_MOTIF_DRAG_AND_DROP_MESSAGE);
+
+ dnd_message->reason = dnd_data->reason | DND_SET_EVENT_TYPE(receiver);
+
+ dnd_message->byte_order = DndByteOrder();
+
+ /* we're filling in flags with more stuff that necessary,
+ depending on the reason, but it doesn't matter */
+ dnd_message->flags = 0 ;
+ dnd_message->flags |= DND_SET_STATUS(dnd_data->status) ;
+ dnd_message->flags |= DND_SET_OPERATION(dnd_data->operation) ;
+ dnd_message->flags |= DND_SET_OPERATIONS(dnd_data->operations) ;
+ dnd_message->flags |= DND_SET_COMPLETION(dnd_data->completion) ;
+
+ dnd_message->time = dnd_data->time ;
+
+ switch(dnd_data->reason) {
+ case DND_DROP_SITE_LEAVE: break ;
+ case DND_TOP_LEVEL_ENTER:
+ case DND_TOP_LEVEL_LEAVE:
+ dnd_message->data.top.src_window = dnd_data->src_window ;
+ dnd_message->data.top.property = dnd_data->property ;
+ break ; /* cannot fall through since the byte layout is different in
+ both set of messages, see top and pot union stuff */
+
+ case DND_DRAG_MOTION:
+ case DND_OPERATION_CHANGED:
+ case DND_DROP_SITE_ENTER:
+ case DND_DROP_START:
+ dnd_message->data.pot.x = dnd_data->x ; /* mouse position */
+ dnd_message->data.pot.y = dnd_data->y ;
+ dnd_message->data.pot.src_window = dnd_data->src_window ;
+ dnd_message->data.pot.property = dnd_data->property ;
+ break ;
+ default:
+ break ;
+ }
+
+}
+
+static Bool DndParseClientMessage(XClientMessageEvent *cm, DndData * dnd_data,
+ char * receiver)
+{
+ DndMessage * dnd_message = (DndMessage*)&cm->data.b[0] ;
+
+ if (cm->message_type != ATOM(_MOTIF_DRAG_AND_DROP_MESSAGE)) {
+ return False ;
+ }
+
+ if (dnd_message->byte_order != DndByteOrder()) {
+ SWAP2BYTES(dnd_message->flags);
+ SWAP4BYTES(dnd_message->time);
+ } /* do the rest in the switch */
+
+ dnd_data->reason = dnd_message->reason ;
+ if (DND_GET_EVENT_TYPE(dnd_data->reason))
+ *receiver = 1 ;
+ else
+ *receiver = 0 ;
+ dnd_data->reason &= DND_CLEAR_EVENT_TYPE ;
+
+ dnd_data->time = dnd_message->time ;
+
+ /* we're reading in more stuff that necessary. but who cares */
+ dnd_data->status = DND_GET_STATUS(dnd_message->flags) ;
+ dnd_data->operation = DND_GET_OPERATION(dnd_message->flags) ;
+ dnd_data->operations = DND_GET_OPERATIONS(dnd_message->flags) ;
+ dnd_data->completion = DND_GET_COMPLETION(dnd_message->flags) ;
+
+ switch(dnd_data->reason) {
+ case DND_TOP_LEVEL_ENTER:
+ case DND_TOP_LEVEL_LEAVE:
+ if (dnd_message->byte_order != DndByteOrder()) {
+ SWAP4BYTES(dnd_message->data.top.src_window);
+ SWAP4BYTES(dnd_message->data.top.property);
+ }
+ dnd_data->src_window = dnd_message->data.top.src_window ;
+ dnd_data->property = dnd_message->data.top.property ;
+ break ; /* cannot fall through, see above comment in write msg */
+
+ case DND_DRAG_MOTION:
+ case DND_OPERATION_CHANGED:
+ case DND_DROP_SITE_ENTER:
+ case DND_DROP_START:
+ if (dnd_message->byte_order != DndByteOrder()) {
+ SWAP2BYTES(dnd_message->data.pot.x);
+ SWAP2BYTES(dnd_message->data.pot.y);
+ SWAP4BYTES(dnd_message->data.pot.property);
+ SWAP4BYTES(dnd_message->data.pot.src_window);
+ }
+ dnd_data->x = dnd_message->data.pot.x ;
+ dnd_data->y = dnd_message->data.pot.y ;
+ dnd_data->property = dnd_message->data.pot.property ;
+ dnd_data->src_window = dnd_message->data.pot.src_window ;
+ break ;
+
+ case DND_DROP_SITE_LEAVE:
+ break;
+ default:
+ break ;
+ }
+
+ return True ;
+}
+
+
+static Window MotifWindow(Display *display)
+{
+ Atom type;
+ int format;
+ unsigned long size;
+ unsigned long bytes_after;
+ unsigned char *property = 0;
+ Window motif_window ;
+
+ /* this version does no caching, so it's slow: round trip each time */
+
+ if ((XGetWindowProperty (display, RootWindow(display, 0),
+ ATOM(_MOTIF_DRAG_WINDOW),
+ 0L, 100000L, False, AnyPropertyType,
+ &type, &format, &size, &bytes_after,
+ &property) == Success) &&
+ (type != XNone)) {
+ motif_window = *(Window *)property;
+ } else {
+ XSetWindowAttributes sAttributes;
+
+ /* really, this should be done on a separate connection,
+ with XSetCloseDownMode (RetainPermanent), so that
+ others don't have to recreate it; hopefully, some real
+ Motif application will be around to do it */
+
+ sAttributes.override_redirect = True;
+ sAttributes.event_mask = PropertyChangeMask;
+ motif_window = XCreateWindow (display,
+ RootWindow (display, 0),
+ -170, -560, 1, 1, 0, 0,
+ InputOnly, CopyFromParent,
+ (CWOverrideRedirect |CWEventMask),
+ &sAttributes);
+ XMapWindow (display, motif_window);
+ }
+
+ if (property) {
+ XFree ((char *)property);
+ }
+
+ return (motif_window);
+}
+
+
+static DndTargetsTable TargetsTable(Display *display)
+{
+ Atom type;
+ int format;
+ unsigned long size;
+ unsigned long bytes_after;
+ Window motif_window = MotifWindow(display) ;
+ unsigned char *retval;
+ DndTargetsTable targets_table ;
+ int i,j ;
+ char * target_data ;
+
+ /* this version does no caching, so it's slow: round trip each time */
+ /* ideally, register for property notify on this target_list
+ atom and update when necessary only */
+
+ if ((XGetWindowProperty (display, motif_window,
+ ATOM(_MOTIF_DRAG_TARGETS), 0L, 100000L,
+ False, ATOM(_MOTIF_DRAG_TARGETS),
+ &type, &format, &size, &bytes_after,
+ &retval) != Success) ||
+ type == XNone) {
+ qWarning("QMotifDND: Cannot get property on Motif window");
+ return 0;
+ }
+
+ DndTargets * target_prop = (DndTargets *)retval;
+
+ if (target_prop->protocol_version != DND_PROTOCOL_VERSION) {
+ qWarning("QMotifDND: Protocol mismatch");
+ }
+
+ if (target_prop->byte_order != DndByteOrder()) {
+ /* need to swap num_target_lists and size */
+ SWAP2BYTES(target_prop->num_target_lists);
+ SWAP4BYTES(target_prop->data_size);
+ }
+
+ /* now parse DndTarget prop data in a TargetsTable */
+
+ targets_table = (DndTargetsTable)malloc(sizeof(DndTargetsTableRec));
+ targets_table->num_entries = target_prop->num_target_lists ;
+ targets_table->entries = (DndTargetsTableEntry)
+ malloc(sizeof(DndTargetsTableEntryRec) * target_prop->num_target_lists);
+
+ target_data = (char*)target_prop + sizeof(*target_prop) ;
+
+ for (i = 0 ; i < targets_table->num_entries; i++) {
+ CARD16 num_targets ;
+ CARD32 atom ;
+
+ memcpy(&num_targets, target_data, 2);
+ target_data += 2;
+
+ /* potential swap needed here */
+ if (target_prop->byte_order != DndByteOrder())
+ SWAP2BYTES(num_targets);
+
+ targets_table->entries[i].num_targets = num_targets ;
+ targets_table->entries[i].targets = (Atom *)
+ malloc(sizeof(Atom) * targets_table->entries[i].num_targets);
+
+
+ for (j = 0; j < num_targets; j++) {
+ memcpy(&atom, target_data, 4);
+ target_data += 4;
+
+ /* another potential swap needed here */
+ if (target_prop->byte_order != DndByteOrder())
+ SWAP4BYTES(atom);
+
+ targets_table->entries[i].targets[j] = (Atom) atom ;
+ }
+ }
+
+ if (target_prop) {
+ XFree((char *)target_prop);
+ }
+
+ return targets_table ;
+}
+
+
+static ushort _DndIndexToTargets(Display * display,
+ int index,
+ Atom ** targets)
+{
+ DndTargetsTable targets_table;
+ int i ;
+
+ /* again, slow: no caching here, alloc/free each time */
+
+ if (!(targets_table = TargetsTable (display)) ||
+ (index >= targets_table->num_entries)) {
+ if (targets_table)
+ XFree((char*)targets_table);
+ return 0;
+ }
+
+ /* transfer the correct target list index */
+ *targets = (Atom*)malloc(sizeof(Atom)*targets_table->
+ entries[index].num_targets);
+ memcpy((char*)*targets,
+ (char*)targets_table->entries[index].targets,
+ sizeof(Atom)*targets_table->entries[index].num_targets);
+
+ /* free the target table and its guts */
+ for (i=0 ; i < targets_table->num_entries; i++)
+ XFree((char*)targets_table->entries[i].targets);
+
+ int tmp = targets_table->entries[index].num_targets;
+ XFree((char*)targets_table);
+
+ return tmp; // targets_table->entries[index].num_targets;
+}
+
+
+QByteArray QX11Data::motifdndFormat(int n)
+{
+ if (!motifdnd_active)
+ return 0; // should not happen
+
+ if (n >= num_src_targets)
+ return 0;
+
+ Atom target = src_targets[n];
+
+ if (target == XA_STRING)
+ return "text/plain;charset=ISO-8859-1";
+ if (target == ATOM(UTF8_STRING))
+ return "text/plain;charset=UTF-8";
+ if (target == ATOM(COMPOUND_TEXT))
+ return QByteArray("text/plain;charset=") + QTextCodec::codecForLocale()->name();
+ if (target == ATOM(TEXT))
+ return "text/plain";
+
+ return ("x-motif-dnd/" + X11->xdndAtomToString(target));
+}
+
+
+QVariant QX11Data::motifdndObtainData(const char *mimeType)
+{
+ QByteArray result;
+
+ if (Dnd_selection == 0 || !dropWidget)
+ return result;
+
+ // try to convert the selection to the requested property
+ // qDebug("trying to convert to '%s'", mimeType);
+
+ int n=0;
+ QByteArray f;
+ do {
+ f = motifdndFormat(n);
+ if (f.isEmpty())
+ return result;
+ n++;
+ } while(qstricmp(mimeType, f.data()));
+
+ Atom conversion_type = XNone;
+ if (f == "text/plain;charset=ISO-8859-1") {
+ conversion_type = XA_STRING;
+ } else if (f == "text/plain;charset=UTF-8") {
+ conversion_type = ATOM(UTF8_STRING);
+ } else if (f == (QByteArray("text/plain;charset=") + QTextCodec::codecForLocale()->name())) {
+ conversion_type = ATOM(COMPOUND_TEXT);
+ } else if (f == "text/plain") {
+ conversion_type = ATOM(TEXT);
+ } else if (f.startsWith("x-motif-dnd/")) {
+ // strip off the "x-motif-dnd/" prefix
+ conversion_type = X11->xdndStringToAtom(f.remove(0, 12));
+ }
+
+ if (XGetSelectionOwner(X11->display, Dnd_selection) == XNone) {
+ return result; // should never happen?
+ }
+
+ QWidget* tw = dropWidget;
+ if ((dropWidget->windowType() == Qt::Desktop)) {
+ tw = new QWidget;
+ }
+
+ // convert selection to the appropriate type
+ XConvertSelection (X11->display, Dnd_selection, conversion_type,
+ Dnd_selection, tw->internalWinId(), Dnd_selection_time);
+
+ XFlush(X11->display);
+
+ XEvent xevent;
+ bool got=X11->clipboardWaitForEvent(tw->internalWinId(), SelectionNotify, &xevent, 5000);
+ if (got) {
+ Atom type;
+
+ if (X11->clipboardReadProperty(tw->internalWinId(), Dnd_selection, true, &result, 0, &type, 0)) {
+ }
+ }
+
+ // we have to convert selection in order to indicate success to the initiator
+ XConvertSelection (X11->display, Dnd_selection, ATOM(XmTRANSFER_SUCCESS),
+ Dnd_selection, tw->internalWinId(), Dnd_selection_time);
+
+ // wait again for SelectionNotify event
+ X11->clipboardWaitForEvent(tw->internalWinId(), SelectionNotify, &xevent, 5000);
+
+ if ((dropWidget->windowType() == Qt::Desktop)) {
+ delete tw;
+ }
+
+ return result;
+}
+
+
+void QX11Data::motifdndEnable(QWidget *widget, bool)
+{
+ DndWriteReceiverProperty(display, widget->internalWinId(), DND_DRAG_DYNAMIC);
+}
+
+
+void QX11Data::motifdndHandle(QWidget *widget, const XEvent * xe, bool /* passive */)
+{
+ XEvent event = *xe;
+ XClientMessageEvent cm ;
+ DndData dnd_data ;
+ char receiver ;
+
+ if (!(DndParseClientMessage ((XClientMessageEvent*)&event,
+ &dnd_data, &receiver))) {
+ return;
+ }
+
+ switch (dnd_data.reason) {
+
+ case DND_DRAG_MOTION:
+ {
+ QPoint p = widget->mapFromGlobal(QPoint(dnd_data.x, dnd_data.y));
+ QWidget *c = widget->childAt(p);
+
+ if (!c || !c->acceptDrops()) {
+ // not over a drop site
+ if (dropWidget) {
+ QDragLeaveEvent dragLeaveEvent;
+ QApplication::sendEvent(dropWidget, &dragLeaveEvent);
+
+ dropWidget = 0;
+ lastAcceptedAction = Qt::IgnoreAction;
+
+ dnd_data.reason = DND_DROP_SITE_LEAVE;
+ dnd_data.time = X11->time;
+ DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, receiver);
+ XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm) ;
+ } else {
+ dnd_data.reason = DND_DRAG_MOTION;
+ dnd_data.status = DND_NO_DROP_SITE;
+ dnd_data.time = X11->time;
+ dnd_data.operation = DND_NOOP;
+ dnd_data.operations = DND_NOOP;
+ DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, receiver);
+ XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm) ;
+ }
+ } else {
+ Q_ASSERT(c != 0);
+ p = c->mapFrom(widget, p);
+
+ if (dropWidget != c) {
+ if (dropWidget) {
+ QDragLeaveEvent le;
+ QApplication::sendEvent(dropWidget, &le);
+ }
+
+ dropWidget = c;
+ lastAcceptedAction = Qt::IgnoreAction;
+
+ const Qt::DropActions possibleActions =
+ DndOperationsToQtDropActions(dnd_data.operations);
+ QDragEnterEvent de(p, possibleActions, QDragManager::self()->dropData,
+ QApplication::mouseButtons(), QApplication::keyboardModifiers());
+ QApplication::sendEvent(dropWidget, &de);
+
+ dnd_data.reason = DND_DROP_SITE_ENTER;
+ dnd_data.time = X11->time;
+ if (de.isAccepted()) {
+ lastAcceptedAction = de.dropAction();
+
+ dnd_data.status = DND_VALID_DROP_SITE;
+ dnd_data.operation = QtDropActionToDndOperation(lastAcceptedAction);
+ } else {
+ dnd_data.status = DND_INVALID_DROP_SITE;
+ dnd_data.operation = DND_NOOP;
+ dnd_data.operations = DND_NOOP;
+ }
+ DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, receiver);
+ XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm);
+ } else {
+ const Qt::DropActions possibleActions =
+ DndOperationsToQtDropActions(dnd_data.operations);
+ QDragMoveEvent me(p, possibleActions, QDragManager::self()->dropData,
+ QApplication::mouseButtons(), QApplication::keyboardModifiers());
+ if (lastAcceptedAction != Qt::IgnoreAction) {
+ me.setDropAction(lastAcceptedAction);
+ me.accept();
+ }
+ QApplication::sendEvent(dropWidget, &me);
+
+ dnd_data.reason = DND_DRAG_MOTION;
+ dnd_data.time = X11->time;
+
+ if (me.isAccepted()) {
+ lastAcceptedAction = me.dropAction();
+
+ dnd_data.status = DND_VALID_DROP_SITE;
+ dnd_data.operation = QtDropActionToDndOperation(lastAcceptedAction);
+ } else {
+ dnd_data.status = DND_INVALID_DROP_SITE;
+ dnd_data.operation = DND_NOOP;
+ dnd_data.operations = DND_NOOP;
+ }
+
+ DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, receiver);
+ XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm);
+ }
+ }
+
+ break;
+ }
+
+ case DND_TOP_LEVEL_ENTER:
+ {
+ /* get the size of our drop site for later use */
+
+ motifdnd_active = true;
+ sourceWindow = dnd_data.src_window;
+
+ /* no answer needed, just read source property */
+ DndReadSourceProperty (event.xclient.display,
+ sourceWindow,
+ dnd_data.property,
+ &src_targets, &num_src_targets);
+
+ break;
+ }
+
+ case DND_TOP_LEVEL_LEAVE:
+ {
+ XEvent nextEvent;
+ if (XCheckTypedWindowEvent(X11->display, widget->winId(), ClientMessage, &nextEvent)) {
+ // we just want to check, not eat (should use XPeekIfEvent)
+ XPutBackEvent(X11->display, &nextEvent);
+
+ if (DndParseClientMessage (&nextEvent.xclient, &dnd_data, &receiver)
+ && dnd_data.reason == DND_DROP_START) {
+ // expecting drop next, keeping DnD alive
+ break;
+ }
+ }
+
+ // not expecting drop, need to send drag leave events and such here
+ if (dropWidget) {
+ QDragLeaveEvent le;
+ QApplication::sendEvent(dropWidget, &le);
+ }
+
+ sourceWindow = XNone;
+ dropWidget = 0;
+ lastAcceptedAction = Qt::IgnoreAction;
+
+ motifdnd_active = false;
+
+ break;
+ }
+
+ case DND_OPERATION_CHANGED:
+ // ### need to echo
+ break;
+
+ case DND_DROP_START:
+ {
+ Q_ASSERT(motifdnd_active);
+ Q_ASSERT(sourceWindow == dnd_data.src_window);
+
+ if (!dropWidget || lastAcceptedAction == Qt::IgnoreAction) {
+ // echo DROP_START
+ dnd_data.reason = DND_DROP_START;
+ dnd_data.status = DND_NO_DROP_SITE;
+ dnd_data.operation = DND_NOOP;
+ dnd_data.operations = DND_NOOP;
+ DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, 0);
+ XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm);
+
+ // we have to convert selection in order to indicate failure to the initiator
+ XConvertSelection (X11->display, dnd_data.property, ATOM(XmTRANSFER_FAILURE),
+ dnd_data.property, dnd_data.src_window, dnd_data.time);
+
+ if (dropWidget) {
+ QDragLeaveEvent e;
+ QApplication::sendEvent(dropWidget, &e);
+ }
+
+ motifdnd_active = false;
+ sourceWindow = XNone;
+ dropWidget = 0;
+ lastAcceptedAction = Qt::IgnoreAction;
+
+ return;
+ }
+
+ // store selection and its time
+ Dnd_selection = dnd_data.property;
+ Dnd_selection_time = dnd_data.time;
+
+ QPoint p(dnd_data.x, dnd_data.y);
+ QDropEvent de(dropWidget->mapFromGlobal(p), Qt::CopyAction, QDragManager::self()->dropData,
+ QApplication::mouseButtons(), QApplication::keyboardModifiers());
+ if (lastAcceptedAction != Qt::IgnoreAction) {
+ de.setDropAction(lastAcceptedAction);
+ de.accept();
+ }
+ QApplication::sendEvent(dropWidget, &de);
+
+ // reset
+ Dnd_selection = XNone;
+ Dnd_selection_time = 0;
+
+ // echo DROP_START depending on the result of the dropEvent
+ if (de.isAccepted()) {
+ dnd_data.reason = DND_DROP_START;
+ dnd_data.status = DND_VALID_DROP_SITE;
+ dnd_data.operation = QtDropActionToDndOperation(de.dropAction());
+ } else {
+ dnd_data.reason = DND_DROP_START;
+ dnd_data.status = DND_NO_DROP_SITE;
+ dnd_data.operation = DND_NOOP;
+ dnd_data.operations = DND_NOOP;
+ }
+ DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, 0);
+ XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm);
+
+ sourceWindow = XNone;
+ dropWidget = 0;
+ lastAcceptedAction = Qt::IgnoreAction;
+
+ motifdnd_active = false;
+
+ break;
+ }
+
+ default:
+ break;
+ } // end of switch (dnd_data.reason)
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_DRAGANDDROP
diff --git a/src/widgets/platforms/x11/qpaintdevice_x11.cpp b/src/widgets/platforms/x11/qpaintdevice_x11.cpp
new file mode 100644
index 0000000000..d7ecb06885
--- /dev/null
+++ b/src/widgets/platforms/x11/qpaintdevice_x11.cpp
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpaintdevice.h"
+#include "qpainter.h"
+#include "qwidget.h"
+#include "qbitmap.h"
+#include "qapplication.h"
+#include <private/qt_x11_p.h>
+#include "qx11info_x11.h"
+
+QT_BEGIN_NAMESPACE
+
+/*! \internal
+
+ Returns the X11 Drawable of the paint device. 0 is returned if it
+ can't be obtained.
+*/
+
+Drawable Q_WIDGETS_EXPORT qt_x11Handle(const QPaintDevice *pd)
+{
+ if (!pd) return 0;
+ if (pd->devType() == QInternal::Widget)
+ return static_cast<const QWidget *>(pd)->handle();
+ else if (pd->devType() == QInternal::Pixmap)
+ return static_cast<const QPixmap *>(pd)->handle();
+ return 0;
+}
+
+/*!
+ \relates QPaintDevice
+
+ Returns the QX11Info structure for the \a pd paint device. 0 is
+ returned if it can't be obtained.
+*/
+const Q_WIDGETS_EXPORT QX11Info *qt_x11Info(const QPaintDevice *pd)
+{
+ if (!pd) return 0;
+ if (pd->devType() == QInternal::Widget)
+ return &static_cast<const QWidget *>(pd)->x11Info();
+ else if (pd->devType() == QInternal::Pixmap)
+ return &static_cast<const QPixmap *>(pd)->x11Info();
+ return 0;
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/x11/qpaintengine_x11.cpp b/src/widgets/platforms/x11/qpaintengine_x11.cpp
new file mode 100644
index 0000000000..3cb3e3caf7
--- /dev/null
+++ b/src/widgets/platforms/x11/qpaintengine_x11.cpp
@@ -0,0 +1,2499 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+
+#include "private/qpixmap_x11_p.h"
+
+#include "qapplication.h"
+#include "qdebug.h"
+#include "qfont.h"
+#include "qwidget.h"
+#include "qbitmap.h"
+#include "qpixmapcache.h"
+#include "qtextcodec.h"
+#include "qcoreevent.h"
+#include "qiodevice.h"
+#include <qmath.h>
+
+#include "qpainter_p.h"
+#include <qtextlayout.h>
+#include <qvarlengtharray.h>
+#include <private/qfont_p.h>
+#include <private/qtextengine_p.h>
+#include <private/qpaintengine_x11_p.h>
+#include <private/qfontengine_x11_p.h>
+#include <private/qwidget_p.h>
+#include <private/qpainterpath_p.h>
+
+#include "qpen.h"
+#include "qcolor.h"
+#include "qcolormap.h"
+
+#include <private/qpaintengine_p.h>
+#include "qpaintengine_x11_p.h"
+
+#include <private/qt_x11_p.h>
+#include <private/qnumeric_p.h>
+#include <limits.h>
+
+#ifndef QT_NO_XRENDER
+#include <private/qtessellator_p.h>
+#endif
+
+#include <private/qhexstring_p.h>
+
+QT_BEGIN_NAMESPACE
+
+extern Drawable qt_x11Handle(const QPaintDevice *pd);
+extern const QX11Info *qt_x11Info(const QPaintDevice *pd);
+extern QPixmap qt_pixmapForBrush(int brushStyle, bool invert); //in qbrush.cpp
+extern QPixmap qt_toX11Pixmap(const QPixmap &pixmap);
+
+// use the same rounding as in qrasterizer.cpp (6 bit fixed point)
+static const qreal aliasedCoordinateDelta = 0.5 - 0.015625;
+
+#undef X11 // defined in qt_x11_p.h
+/*!
+ Returns the X11 specific pen GC for the painter \a p. Note that
+ QPainter::begin() must be called before this function returns a
+ valid GC.
+*/
+Q_WIDGETS_EXPORT GC qt_x11_get_pen_gc(QPainter *p)
+{
+ if (p && p->paintEngine()
+ && p->paintEngine()->isActive()
+ && p->paintEngine()->type() == QPaintEngine::X11) {
+ return static_cast<QX11PaintEngine *>(p->paintEngine())->d_func()->gc;
+ }
+ return 0;
+}
+
+/*!
+ Returns the X11 specific brush GC for the painter \a p. Note that
+ QPainter::begin() must be called before this function returns a
+ valid GC.
+*/
+Q_WIDGETS_EXPORT GC qt_x11_get_brush_gc(QPainter *p)
+{
+ if (p && p->paintEngine()
+ && p->paintEngine()->isActive()
+ && p->paintEngine()->type() == QPaintEngine::X11) {
+ return static_cast<QX11PaintEngine *>(p->paintEngine())->d_func()->gc_brush;
+ }
+ return 0;
+}
+#define X11 qt_x11Data
+
+#ifndef QT_NO_XRENDER
+static const int compositionModeToRenderOp[QPainter::CompositionMode_Xor + 1] = {
+ PictOpOver, //CompositionMode_SourceOver,
+ PictOpOverReverse, //CompositionMode_DestinationOver,
+ PictOpClear, //CompositionMode_Clear,
+ PictOpSrc, //CompositionMode_Source,
+ PictOpDst, //CompositionMode_Destination,
+ PictOpIn, //CompositionMode_SourceIn,
+ PictOpInReverse, //CompositionMode_DestinationIn,
+ PictOpOut, //CompositionMode_SourceOut,
+ PictOpOutReverse, //CompositionMode_DestinationOut,
+ PictOpAtop, //CompositionMode_SourceAtop,
+ PictOpAtopReverse, //CompositionMode_DestinationAtop,
+ PictOpXor //CompositionMode_Xor
+};
+
+static inline int qpainterOpToXrender(QPainter::CompositionMode mode)
+{
+ Q_ASSERT(mode <= QPainter::CompositionMode_Xor);
+ return compositionModeToRenderOp[mode];
+}
+#endif
+
+// hack, so we don't have to make QRegion::clipRectangles() public or include
+// X11 headers in qregion.h
+Q_WIDGETS_EXPORT void *qt_getClipRects(const QRegion &r, int &num)
+{
+ return r.clipRectangles(num);
+}
+
+static inline void x11SetClipRegion(Display *dpy, GC gc, GC gc2,
+#ifndef QT_NO_XRENDER
+ Picture picture,
+#else
+ Qt::HANDLE picture,
+#endif
+ const QRegion &r)
+{
+ int num;
+ XRectangle *rects = (XRectangle *)qt_getClipRects(r, num);
+
+ if (gc)
+ XSetClipRectangles( dpy, gc, 0, 0, rects, num, YXBanded );
+ if (gc2)
+ XSetClipRectangles( dpy, gc2, 0, 0, rects, num, YXBanded );
+
+#ifndef QT_NO_XRENDER
+ if (picture)
+ XRenderSetPictureClipRectangles(dpy, picture, 0, 0, rects, num);
+#else
+ Q_UNUSED(picture);
+#endif // QT_NO_XRENDER
+}
+
+
+static inline void x11ClearClipRegion(Display *dpy, GC gc, GC gc2,
+#ifndef QT_NO_XRENDER
+ Picture picture
+#else
+ Qt::HANDLE picture
+#endif
+ )
+{
+ if (gc)
+ XSetClipMask(dpy, gc, XNone);
+ if (gc2)
+ XSetClipMask(dpy, gc2, XNone);
+
+#ifndef QT_NO_XRENDER
+ if (picture) {
+ XRenderPictureAttributes attrs;
+ attrs.clip_mask = XNone;
+ XRenderChangePicture (dpy, picture, CPClipMask, &attrs);
+ }
+#else
+ Q_UNUSED(picture);
+#endif // QT_NO_XRENDER
+}
+
+
+#define DITHER_SIZE 16
+static const uchar base_dither_matrix[DITHER_SIZE][DITHER_SIZE] = {
+ { 0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255 },
+ { 128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127 },
+ { 32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223 },
+ { 160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95 },
+ { 8,200, 56,248, 4,196, 52,244, 11,203, 59,251, 7,199, 55,247 },
+ { 136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119 },
+ { 40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215 },
+ { 168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87 },
+ { 2,194, 50,242, 14,206, 62,254, 1,193, 49,241, 13,205, 61,253 },
+ { 130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125 },
+ { 34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221 },
+ { 162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93 },
+ { 10,202, 58,250, 6,198, 54,246, 9,201, 57,249, 5,197, 53,245 },
+ { 138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117 },
+ { 42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213 },
+ { 170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85 }
+};
+
+static QPixmap qt_patternForAlpha(uchar alpha, int screen)
+{
+ QPixmap pm;
+ QString key = QLatin1Literal("$qt-alpha-brush$")
+ % HexString<uchar>(alpha)
+ % HexString<int>(screen);
+
+ if (!QPixmapCache::find(key, pm)) {
+ // #### why not use a mono image here????
+ QImage pattern(DITHER_SIZE, DITHER_SIZE, QImage::Format_ARGB32);
+ pattern.fill(0xffffffff);
+ for (int y = 0; y < DITHER_SIZE; ++y) {
+ for (int x = 0; x < DITHER_SIZE; ++x) {
+ if (base_dither_matrix[x][y] <= alpha)
+ pattern.setPixel(x, y, 0x00000000);
+ }
+ }
+ pm = QBitmap::fromImage(pattern);
+ pm.x11SetScreen(screen);
+ QPixmapCache::insert(key, pm);
+ }
+ return pm;
+}
+
+#if !defined(QT_NO_XRENDER)
+
+class QXRenderTessellator : public QTessellator
+{
+public:
+ QXRenderTessellator() : traps(0), allocated(0), size(0) {}
+ ~QXRenderTessellator() { free(traps); }
+ XTrapezoid *traps;
+ int allocated;
+ int size;
+ void addTrap(const Trapezoid &trap);
+ QRect tessellate(const QPointF *points, int nPoints, bool winding) {
+ size = 0;
+ setWinding(winding);
+ return QTessellator::tessellate(points, nPoints).toRect();
+ }
+ void done() {
+ if (allocated > 64) {
+ free(traps);
+ traps = 0;
+ allocated = 0;
+ }
+ }
+};
+
+void QXRenderTessellator::addTrap(const Trapezoid &trap)
+{
+ if (size == allocated) {
+ allocated = qMax(2*allocated, 64);
+ traps = q_check_ptr((XTrapezoid *)realloc(traps, allocated * sizeof(XTrapezoid)));
+ }
+ traps[size].top = Q27Dot5ToXFixed(trap.top);
+ traps[size].bottom = Q27Dot5ToXFixed(trap.bottom);
+ traps[size].left.p1.x = Q27Dot5ToXFixed(trap.topLeft->x);
+ traps[size].left.p1.y = Q27Dot5ToXFixed(trap.topLeft->y);
+ traps[size].left.p2.x = Q27Dot5ToXFixed(trap.bottomLeft->x);
+ traps[size].left.p2.y = Q27Dot5ToXFixed(trap.bottomLeft->y);
+ traps[size].right.p1.x = Q27Dot5ToXFixed(trap.topRight->x);
+ traps[size].right.p1.y = Q27Dot5ToXFixed(trap.topRight->y);
+ traps[size].right.p2.x = Q27Dot5ToXFixed(trap.bottomRight->x);
+ traps[size].right.p2.y = Q27Dot5ToXFixed(trap.bottomRight->y);
+ ++size;
+}
+
+#endif // !defined(QT_NO_XRENDER)
+
+
+#ifndef QT_NO_XRENDER
+static Picture getPatternFill(int screen, const QBrush &b)
+{
+ if (!X11->use_xrender)
+ return XNone;
+
+ XRenderColor color = X11->preMultiply(b.color());
+ XRenderColor bg_color;
+
+ bg_color = X11->preMultiply(QColor(0, 0, 0, 0));
+
+ for (int i = 0; i < X11->pattern_fill_count; ++i) {
+ if (X11->pattern_fills[i].screen == screen
+ && X11->pattern_fills[i].opaque == false
+ && X11->pattern_fills[i].style == b.style()
+ && X11->pattern_fills[i].color.alpha == color.alpha
+ && X11->pattern_fills[i].color.red == color.red
+ && X11->pattern_fills[i].color.green == color.green
+ && X11->pattern_fills[i].color.blue == color.blue
+ && X11->pattern_fills[i].bg_color.alpha == bg_color.alpha
+ && X11->pattern_fills[i].bg_color.red == bg_color.red
+ && X11->pattern_fills[i].bg_color.green == bg_color.green
+ && X11->pattern_fills[i].bg_color.blue == bg_color.blue)
+ return X11->pattern_fills[i].picture;
+ }
+ // none found, replace one
+ int i = qrand() % 16;
+
+ if (X11->pattern_fills[i].screen != screen && X11->pattern_fills[i].picture) {
+ XRenderFreePicture (X11->display, X11->pattern_fills[i].picture);
+ X11->pattern_fills[i].picture = 0;
+ }
+
+ if (!X11->pattern_fills[i].picture) {
+ Pixmap pixmap = XCreatePixmap (X11->display, RootWindow (X11->display, screen), 8, 8, 32);
+ XRenderPictureAttributes attrs;
+ attrs.repeat = True;
+ X11->pattern_fills[i].picture = XRenderCreatePicture (X11->display, pixmap,
+ XRenderFindStandardFormat(X11->display, PictStandardARGB32),
+ CPRepeat, &attrs);
+ XFreePixmap (X11->display, pixmap);
+ }
+
+ X11->pattern_fills[i].screen = screen;
+ X11->pattern_fills[i].color = color;
+ X11->pattern_fills[i].bg_color = bg_color;
+ X11->pattern_fills[i].opaque = false;
+ X11->pattern_fills[i].style = b.style();
+
+ XRenderFillRectangle(X11->display, PictOpSrc, X11->pattern_fills[i].picture, &bg_color, 0, 0, 8, 8);
+
+ QPixmap pattern(qt_pixmapForBrush(b.style(), true));
+ XRenderPictureAttributes attrs;
+ attrs.repeat = true;
+ XRenderChangePicture(X11->display, pattern.x11PictureHandle(), CPRepeat, &attrs);
+
+ Picture fill_fg = X11->getSolidFill(screen, b.color());
+ XRenderComposite(X11->display, PictOpOver, fill_fg, pattern.x11PictureHandle(),
+ X11->pattern_fills[i].picture,
+ 0, 0, 0, 0, 0, 0, 8, 8);
+
+ return X11->pattern_fills[i].picture;
+}
+
+static void qt_render_bitmap(Display *dpy, int scrn, Picture src, Picture dst,
+ int sx, int sy, int x, int y, int sw, int sh,
+ const QPen &pen)
+{
+ Picture fill_fg = X11->getSolidFill(scrn, pen.color());
+ XRenderComposite(dpy, PictOpOver,
+ fill_fg, src, dst, sx, sy, sx, sy, x, y, sw, sh);
+}
+#endif
+
+void QX11PaintEnginePrivate::init()
+{
+ dpy = 0;
+ scrn = 0;
+ hd = 0;
+ picture = 0;
+ xinfo = 0;
+#ifndef QT_NO_XRENDER
+ current_brush = 0;
+ composition_mode = PictOpOver;
+ tessellator = new QXRenderTessellator;
+#endif
+}
+
+void QX11PaintEnginePrivate::setupAdaptedOrigin(const QPoint &p)
+{
+ if (adapted_pen_origin)
+ XSetTSOrigin(dpy, gc, p.x(), p.y());
+ if (adapted_brush_origin)
+ XSetTSOrigin(dpy, gc_brush, p.x(), p.y());
+}
+
+void QX11PaintEnginePrivate::resetAdaptedOrigin()
+{
+ if (adapted_pen_origin)
+ XSetTSOrigin(dpy, gc, 0, 0);
+ if (adapted_brush_origin)
+ XSetTSOrigin(dpy, gc_brush, 0, 0);
+}
+
+void QX11PaintEnginePrivate::clipPolygon_dev(const QPolygonF &poly, QPolygonF *clipped_poly)
+{
+ int clipped_count = 0;
+ qt_float_point *clipped_points = 0;
+ polygonClipper.clipPolygon((qt_float_point *) poly.data(), poly.size(),
+ &clipped_points, &clipped_count);
+ clipped_poly->resize(clipped_count);
+ for (int i=0; i<clipped_count; ++i)
+ (*clipped_poly)[i] = *((QPointF *)(&clipped_points[i]));
+}
+
+void QX11PaintEnginePrivate::systemStateChanged()
+{
+ Q_Q(QX11PaintEngine);
+ QPainter *painter = q->state ? static_cast<QPainterState *>(q->state)->painter : 0;
+ if (painter && painter->hasClipping()) {
+ if (q->testDirty(QPaintEngine::DirtyTransform))
+ q->updateMatrix(q->state->transform());
+ QPolygonF clip_poly_dev(matrix.map(painter->clipPath().toFillPolygon()));
+ QPolygonF clipped_poly_dev;
+ clipPolygon_dev(clip_poly_dev, &clipped_poly_dev);
+ q->updateClipRegion_dev(QRegion(clipped_poly_dev.toPolygon()), Qt::ReplaceClip);
+ } else {
+ q->updateClipRegion_dev(QRegion(), Qt::NoClip);
+ }
+}
+
+static QPaintEngine::PaintEngineFeatures qt_decide_features()
+{
+ QPaintEngine::PaintEngineFeatures features =
+ QPaintEngine::PrimitiveTransform
+ | QPaintEngine::PatternBrush
+ | QPaintEngine::AlphaBlend
+ | QPaintEngine::PainterPaths
+ | QPaintEngine::RasterOpModes;
+
+ if (X11->use_xrender) {
+ features |= QPaintEngine::Antialiasing;
+ features |= QPaintEngine::PorterDuff;
+ features |= QPaintEngine::MaskedBrush;
+#if 0
+ if (X11->xrender_version > 10) {
+ features |= QPaintEngine::LinearGradientFill;
+ // ###
+ }
+#endif
+ }
+
+ return features;
+}
+
+/*
+ * QX11PaintEngine members
+ */
+
+QX11PaintEngine::QX11PaintEngine()
+ : QPaintEngine(*(new QX11PaintEnginePrivate), qt_decide_features())
+{
+ d_func()->init();
+}
+
+QX11PaintEngine::QX11PaintEngine(QX11PaintEnginePrivate &dptr)
+ : QPaintEngine(dptr, qt_decide_features())
+{
+ d_func()->init();
+}
+
+QX11PaintEngine::~QX11PaintEngine()
+{
+#ifndef QT_NO_XRENDER
+ Q_D(QX11PaintEngine);
+ delete d->tessellator;
+#endif
+}
+
+bool QX11PaintEngine::begin(QPaintDevice *pdev)
+{
+ Q_D(QX11PaintEngine);
+ d->xinfo = qt_x11Info(pdev);
+ QWidget *w = d->pdev->devType() == QInternal::Widget ? static_cast<QWidget *>(d->pdev) : 0;
+ const bool isAlienWidget = w && !w->internalWinId() && w->testAttribute(Qt::WA_WState_Created);
+#ifndef QT_NO_XRENDER
+ if (w) {
+ if (isAlienWidget)
+ d->picture = (::Picture)w->nativeParentWidget()->x11PictureHandle();
+ else
+ d->picture = (::Picture)w->x11PictureHandle();
+ } else if (pdev->devType() == QInternal::Pixmap) {
+ const QPixmap *pm = static_cast<const QPixmap *>(pdev);
+ QX11PlatformPixmap *data = static_cast<QX11PlatformPixmap*>(pm->data.data());
+ if (X11->use_xrender && data->depth() != 32 && data->x11_mask)
+ data->convertToARGB32();
+ d->picture = (::Picture)static_cast<const QPixmap *>(pdev)->x11PictureHandle();
+ }
+#else
+ d->picture = 0;
+#endif
+ d->hd = !isAlienWidget ? qt_x11Handle(pdev) : qt_x11Handle(w->nativeParentWidget());
+
+ Q_ASSERT(d->xinfo != 0);
+ d->dpy = d->xinfo->display(); // get display variable
+ d->scrn = d->xinfo->screen(); // get screen variable
+
+ d->crgn = QRegion();
+ d->gc = XCreateGC(d->dpy, d->hd, 0, 0);
+ d->gc_brush = XCreateGC(d->dpy, d->hd, 0, 0);
+ d->has_alpha_brush = false;
+ d->has_alpha_pen = false;
+ d->has_clipping = false;
+ d->has_complex_xform = false;
+ d->has_scaling_xform = false;
+ d->has_non_scaling_xform = true;
+ d->xform_scale = 1;
+ d->has_custom_pen = false;
+ d->matrix = QTransform();
+ d->pdev_depth = d->pdev->depth();
+ d->render_hints = 0;
+ d->txop = QTransform::TxNone;
+ d->use_path_fallback = false;
+#if !defined(QT_NO_XRENDER)
+ d->composition_mode = PictOpOver;
+#endif
+ d->xlibMaxLinePoints = 32762; // a safe number used to avoid, call to XMaxRequestSize(d->dpy) - 3;
+ d->opacity = 1;
+
+ // Set up the polygon clipper. Note: This will only work in
+ // polyline mode as long as we have a buffer zone, since a
+ // polyline may be clipped into several non-connected polylines.
+ const int BUFFERZONE = 1000;
+ QRect devClipRect(-BUFFERZONE, -BUFFERZONE,
+ pdev->width() + 2*BUFFERZONE, pdev->height() + 2*BUFFERZONE);
+ d->polygonClipper.setBoundingRect(devClipRect);
+
+ if (isAlienWidget) {
+ // Set system clip for alien widgets painting outside the paint event.
+ // This is not a problem with native windows since the windowing system
+ // will handle the clip.
+ QWidgetPrivate *wd = w->d_func();
+ QRegion widgetClip(wd->clipRect());
+ wd->clipToEffectiveMask(widgetClip);
+ wd->subtractOpaqueSiblings(widgetClip);
+ widgetClip.translate(w->mapTo(w->nativeParentWidget(), QPoint()));
+ setSystemClip(widgetClip);
+ }
+
+ QPixmap::x11SetDefaultScreen(d->xinfo->screen());
+
+ if (w && w->testAttribute(Qt::WA_PaintUnclipped)) { // paint direct on device
+ updatePen(QPen(Qt::black));
+ updateBrush(QBrush(Qt::white), QPoint());
+ XSetSubwindowMode(d->dpy, d->gc, IncludeInferiors);
+ XSetSubwindowMode(d->dpy, d->gc_brush, IncludeInferiors);
+#ifndef QT_NO_XRENDER
+ XRenderPictureAttributes attrs;
+ attrs.subwindow_mode = IncludeInferiors;
+ XRenderChangePicture(d->dpy, d->picture, CPSubwindowMode, &attrs);
+#endif
+ }
+
+ setDirty(QPaintEngine::DirtyClipRegion);
+ setDirty(QPaintEngine::DirtyPen);
+ setDirty(QPaintEngine::DirtyBrush);
+ setDirty(QPaintEngine::DirtyBackground);
+
+ return true;
+}
+
+bool QX11PaintEngine::end()
+{
+ Q_D(QX11PaintEngine);
+
+#if !defined(QT_NO_XRENDER)
+ if (d->picture) {
+ // reset clipping/subwindow mode on our render picture
+ XRenderPictureAttributes attrs;
+ attrs.subwindow_mode = ClipByChildren;
+ attrs.clip_mask = XNone;
+ XRenderChangePicture(d->dpy, d->picture, CPClipMask|CPSubwindowMode, &attrs);
+ }
+#endif
+
+ if (d->gc_brush && d->pdev->painters < 2) {
+ XFreeGC(d->dpy, d->gc_brush);
+ d->gc_brush = 0;
+ }
+
+ if (d->gc && d->pdev->painters < 2) {
+ XFreeGC(d->dpy, d->gc);
+ d->gc = 0;
+ }
+
+ // Restore system clip for alien widgets painting outside the paint event.
+ if (d->pdev->devType() == QInternal::Widget && !static_cast<QWidget *>(d->pdev)->internalWinId())
+ setSystemClip(QRegion());
+
+ return true;
+}
+
+static bool clipLine(QLineF *line, const QRect &rect)
+{
+ qreal x1 = line->x1();
+ qreal x2 = line->x2();
+ qreal y1 = line->y1();
+ qreal y2 = line->y2();
+
+ qreal left = rect.x();
+ qreal right = rect.x() + rect.width() - 1;
+ qreal top = rect.y();
+ qreal bottom = rect.y() + rect.height() - 1;
+
+ enum { Left, Right, Top, Bottom };
+ // clip the lines, after cohen-sutherland, see e.g. http://www.nondot.org/~sabre/graphpro/line6.html
+ int p1 = ((x1 < left) << Left)
+ | ((x1 > right) << Right)
+ | ((y1 < top) << Top)
+ | ((y1 > bottom) << Bottom);
+ int p2 = ((x2 < left) << Left)
+ | ((x2 > right) << Right)
+ | ((y2 < top) << Top)
+ | ((y2 > bottom) << Bottom);
+
+ if (p1 & p2)
+ // completely outside
+ return false;
+
+ if (p1 | p2) {
+ qreal dx = x2 - x1;
+ qreal dy = y2 - y1;
+
+ // clip x coordinates
+ if (x1 < left) {
+ y1 += dy/dx * (left - x1);
+ x1 = left;
+ } else if (x1 > right) {
+ y1 -= dy/dx * (x1 - right);
+ x1 = right;
+ }
+ if (x2 < left) {
+ y2 += dy/dx * (left - x2);
+ x2 = left;
+ } else if (x2 > right) {
+ y2 -= dy/dx * (x2 - right);
+ x2 = right;
+ }
+ p1 = ((y1 < top) << Top)
+ | ((y1 > bottom) << Bottom);
+ p2 = ((y2 < top) << Top)
+ | ((y2 > bottom) << Bottom);
+ if (p1 & p2)
+ return false;
+ // clip y coordinates
+ if (y1 < top) {
+ x1 += dx/dy * (top - y1);
+ y1 = top;
+ } else if (y1 > bottom) {
+ x1 -= dx/dy * (y1 - bottom);
+ y1 = bottom;
+ }
+ if (y2 < top) {
+ x2 += dx/dy * (top - y2);
+ y2 = top;
+ } else if (y2 > bottom) {
+ x2 -= dx/dy * (y2 - bottom);
+ y2 = bottom;
+ }
+ *line = QLineF(QPointF(x1, y1), QPointF(x2, y2));
+ }
+ return true;
+}
+
+void QX11PaintEngine::drawLines(const QLine *lines, int lineCount)
+{
+ Q_ASSERT(lines);
+ Q_ASSERT(lineCount);
+ Q_D(QX11PaintEngine);
+ if (d->has_alpha_brush
+ || d->has_alpha_pen
+ || d->has_custom_pen
+ || (d->cpen.widthF() > 0 && d->has_complex_xform
+ && !d->has_non_scaling_xform)
+ || (d->render_hints & QPainter::Antialiasing)) {
+ for (int i = 0; i < lineCount; ++i) {
+ QPainterPath path(lines[i].p1());
+ path.lineTo(lines[i].p2());
+ drawPath(path);
+ }
+ return;
+ }
+
+ if (d->has_pen) {
+ for (int i = 0; i < lineCount; ++i) {
+ QLineF linef;
+ if (d->txop == QTransform::TxNone) {
+ linef = lines[i];
+ } else {
+ linef = d->matrix.map(QLineF(lines[i]));
+ }
+ if (clipLine(&linef, d->polygonClipper.boundingRect())) {
+ int x1 = qRound(linef.x1() + aliasedCoordinateDelta);
+ int y1 = qRound(linef.y1() + aliasedCoordinateDelta);
+ int x2 = qRound(linef.x2() + aliasedCoordinateDelta);
+ int y2 = qRound(linef.y2() + aliasedCoordinateDelta);
+
+ XDrawLine(d->dpy, d->hd, d->gc, x1, y1, x2, y2);
+ }
+ }
+ }
+}
+
+void QX11PaintEngine::drawLines(const QLineF *lines, int lineCount)
+{
+ Q_ASSERT(lines);
+ Q_ASSERT(lineCount);
+ Q_D(QX11PaintEngine);
+ if (d->has_alpha_brush
+ || d->has_alpha_pen
+ || d->has_custom_pen
+ || (d->cpen.widthF() > 0 && d->has_complex_xform
+ && !d->has_non_scaling_xform)
+ || (d->render_hints & QPainter::Antialiasing)) {
+ for (int i = 0; i < lineCount; ++i) {
+ QPainterPath path(lines[i].p1());
+ path.lineTo(lines[i].p2());
+ drawPath(path);
+ }
+ return;
+ }
+
+ if (d->has_pen) {
+ for (int i = 0; i < lineCount; ++i) {
+ QLineF linef = d->matrix.map(lines[i]);
+ if (clipLine(&linef, d->polygonClipper.boundingRect())) {
+ int x1 = qRound(linef.x1() + aliasedCoordinateDelta);
+ int y1 = qRound(linef.y1() + aliasedCoordinateDelta);
+ int x2 = qRound(linef.x2() + aliasedCoordinateDelta);
+ int y2 = qRound(linef.y2() + aliasedCoordinateDelta);
+
+ XDrawLine(d->dpy, d->hd, d->gc, x1, y1, x2, y2);
+ }
+ }
+ }
+}
+
+static inline QLine clipStraightLine(const QRect &clip, const QLine &l)
+{
+ if (l.p1().x() == l.p2().x()) {
+ int x = qBound(clip.left(), l.p1().x(), clip.right());
+ int y1 = qBound(clip.top(), l.p1().y(), clip.bottom());
+ int y2 = qBound(clip.top(), l.p2().y(), clip.bottom());
+
+ return QLine(x, y1, x, y2);
+ } else {
+ Q_ASSERT(l.p1().y() == l.p2().y());
+
+ int x1 = qBound(clip.left(), l.p1().x(), clip.right());
+ int x2 = qBound(clip.left(), l.p2().x(), clip.right());
+ int y = qBound(clip.top(), l.p1().y(), clip.bottom());
+
+ return QLine(x1, y, x2, y);
+ }
+}
+
+void QX11PaintEngine::drawRects(const QRectF *rects, int rectCount)
+{
+ Q_D(QX11PaintEngine);
+ Q_ASSERT(rects);
+ Q_ASSERT(rectCount);
+
+ if (rectCount != 1
+ || d->has_pen
+ || d->has_alpha_brush
+ || d->has_complex_xform
+ || d->has_custom_pen
+ || d->cbrush.style() != Qt::SolidPattern)
+ {
+ QPaintEngine::drawRects(rects, rectCount);
+ return;
+ }
+
+ QPoint alignedOffset;
+ if (d->txop == QTransform::TxTranslate) {
+ QPointF offset(d->matrix.dx(), d->matrix.dy());
+ alignedOffset = offset.toPoint();
+ if (offset != QPointF(alignedOffset)) {
+ QPaintEngine::drawRects(rects, rectCount);
+ return;
+ }
+ }
+
+ const QRectF& r = rects[0];
+ QRect alignedRect = r.toAlignedRect();
+ if (r != QRectF(alignedRect)) {
+ QPaintEngine::drawRects(rects, rectCount);
+ return;
+ }
+ alignedRect.translate(alignedOffset);
+
+ QRect clip(d->polygonClipper.boundingRect());
+ alignedRect = alignedRect.intersected(clip);
+ if (alignedRect.isEmpty())
+ return;
+
+ // simple-case:
+ // the rectangle is pixel-aligned
+ // the fill brush is just a solid non-alpha color
+ // the painter transform is only integer translation
+ // ignore: antialiasing and just XFillRectangles directly
+ XRectangle xrect;
+ xrect.x = short(alignedRect.x());
+ xrect.y = short(alignedRect.y());
+ xrect.width = ushort(alignedRect.width());
+ xrect.height = ushort(alignedRect.height());
+ XFillRectangles(d->dpy, d->hd, d->gc_brush, &xrect, 1);
+}
+
+void QX11PaintEngine::drawRects(const QRect *rects, int rectCount)
+{
+ Q_D(QX11PaintEngine);
+ Q_ASSERT(rects);
+ Q_ASSERT(rectCount);
+
+ if (d->has_alpha_pen
+ || d->has_complex_xform
+ || d->has_custom_pen
+ || (d->render_hints & QPainter::Antialiasing))
+ {
+ for (int i = 0; i < rectCount; ++i) {
+ QPainterPath path;
+ path.addRect(rects[i]);
+ drawPath(path);
+ }
+ return;
+ }
+
+ QRect clip(d->polygonClipper.boundingRect());
+ QPoint offset(qRound(d->matrix.dx()), qRound(d->matrix.dy()));
+#if !defined(QT_NO_XRENDER)
+ ::Picture pict = d->picture;
+
+ if (X11->use_xrender && pict && d->has_brush && d->pdev_depth != 1
+ && (d->has_texture || d->has_alpha_brush))
+ {
+ XRenderColor xc;
+ if (!d->has_texture && !d->has_pattern)
+ xc = X11->preMultiply(d->cbrush.color());
+
+ for (int i = 0; i < rectCount; ++i) {
+ QRect r(rects[i]);
+ if (d->txop == QTransform::TxTranslate)
+ r.translate(offset);
+
+ if (r.width() == 0 || r.height() == 0) {
+ if (d->has_pen) {
+ const QLine l = clipStraightLine(clip, QLine(r.left(), r.top(), r.left() + r.width(), r.top() + r.height()));
+ XDrawLine(d->dpy, d->hd, d->gc, l.p1().x(), l.p1().y(), l.p2().x(), l.p2().y());
+ }
+ continue;
+ }
+
+ r = r.intersected(clip);
+ if (r.isEmpty())
+ continue;
+ if (d->has_texture || d->has_pattern) {
+ XRenderComposite(d->dpy, d->composition_mode, d->current_brush, 0, pict,
+ qRound(r.x() - d->bg_origin.x()), qRound(r.y() - d->bg_origin.y()),
+ 0, 0, r.x(), r.y(), r.width(), r.height());
+ } else {
+ XRenderFillRectangle(d->dpy, d->composition_mode, pict, &xc, r.x(), r.y(), r.width(), r.height());
+ }
+ if (d->has_pen)
+ XDrawRectangle(d->dpy, d->hd, d->gc, r.x(), r.y(), r.width(), r.height());
+ }
+ } else
+#endif // !QT_NO_XRENDER
+ {
+ if (d->has_brush && d->has_pen) {
+ for (int i = 0; i < rectCount; ++i) {
+ QRect r(rects[i]);
+ if (d->txop == QTransform::TxTranslate)
+ r.translate(offset);
+
+ if (r.width() == 0 || r.height() == 0) {
+ const QLine l = clipStraightLine(clip, QLine(r.left(), r.top(), r.left() + r.width(), r.top() + r.height()));
+ XDrawLine(d->dpy, d->hd, d->gc, l.p1().x(), l.p1().y(), l.p2().x(), l.p2().y());
+ continue;
+ }
+
+ r = r.intersected(clip);
+ if (r.isEmpty())
+ continue;
+ d->setupAdaptedOrigin(r.topLeft());
+ XFillRectangle(d->dpy, d->hd, d->gc_brush, r.x(), r.y(), r.width(), r.height());
+ XDrawRectangle(d->dpy, d->hd, d->gc, r.x(), r.y(), r.width(), r.height());
+ }
+ d->resetAdaptedOrigin();
+ } else {
+ QVarLengthArray<XRectangle> xrects(rectCount);
+ int numClipped = rectCount;
+ for (int i = 0; i < rectCount; ++i) {
+ QRect r(rects[i]);
+ if (d->txop == QTransform::TxTranslate)
+ r.translate(offset);
+
+ if (r.width() == 0 || r.height() == 0) {
+ --numClipped;
+ if (d->has_pen) {
+ const QLine l = clipStraightLine(clip, QLine(r.left(), r.top(), r.left() + r.width(), r.top() + r.height()));
+ XDrawLine(d->dpy, d->hd, d->gc, l.p1().x(), l.p1().y(), l.p2().x(), l.p2().y());
+ }
+ continue;
+ }
+
+ r = r.intersected(clip);
+ if (r.isEmpty()) {
+ --numClipped;
+ continue;
+ }
+ xrects[i].x = short(r.x());
+ xrects[i].y = short(r.y());
+ xrects[i].width = ushort(r.width());
+ xrects[i].height = ushort(r.height());
+ }
+ if (numClipped) {
+ d->setupAdaptedOrigin(rects[0].topLeft());
+ if (d->has_brush)
+ XFillRectangles(d->dpy, d->hd, d->gc_brush, xrects.data(), numClipped);
+ else if (d->has_pen)
+ XDrawRectangles(d->dpy, d->hd, d->gc, xrects.data(), numClipped);
+ d->resetAdaptedOrigin();
+ }
+ }
+ }
+}
+
+static inline void setCapStyle(int cap_style, GC gc)
+{
+ ulong mask = GCCapStyle;
+ XGCValues vals;
+ vals.cap_style = cap_style;
+ XChangeGC(X11->display, gc, mask, &vals);
+}
+
+void QX11PaintEngine::drawPoints(const QPoint *points, int pointCount)
+{
+ Q_ASSERT(points);
+ Q_ASSERT(pointCount);
+ Q_D(QX11PaintEngine);
+
+ if (!d->has_pen)
+ return;
+
+ // use the same test here as in drawPath to ensure that we don't use the path fallback
+ // and end up in XDrawLines for pens with width <= 1
+ if (d->cpen.widthF() > 1.0f
+ || (X11->use_xrender && (d->has_alpha_pen || (d->render_hints & QPainter::Antialiasing)))
+ || (!d->cpen.isCosmetic() && d->txop > QTransform::TxTranslate))
+ {
+ Qt::PenCapStyle capStyle = d->cpen.capStyle();
+ if (capStyle == Qt::FlatCap) {
+ setCapStyle(CapProjecting, d->gc);
+ d->cpen.setCapStyle(Qt::SquareCap);
+ }
+ const QPoint *end = points + pointCount;
+ while (points < end) {
+ QPainterPath path;
+ path.moveTo(*points);
+ path.lineTo(points->x()+.005, points->y());
+ drawPath(path);
+ ++points;
+ }
+
+ if (capStyle == Qt::FlatCap) {
+ setCapStyle(CapButt, d->gc);
+ d->cpen.setCapStyle(capStyle);
+ }
+ return;
+ }
+
+ static const int BUF_SIZE = 1024;
+ XPoint xPoints[BUF_SIZE];
+ int i = 0, j = 0;
+ while (i < pointCount) {
+ while (i < pointCount && j < BUF_SIZE) {
+ const QPoint &xformed = d->matrix.map(points[i]);
+ int x = xformed.x();
+ int y = xformed.y();
+ if (x >= SHRT_MIN && y >= SHRT_MIN && x < SHRT_MAX && y < SHRT_MAX) {
+ xPoints[j].x = x;
+ xPoints[j].y = y;
+ ++j;
+ }
+ ++i;
+ }
+ if (j)
+ XDrawPoints(d->dpy, d->hd, d->gc, xPoints, j, CoordModeOrigin);
+
+ j = 0;
+ }
+}
+
+void QX11PaintEngine::drawPoints(const QPointF *points, int pointCount)
+{
+ Q_ASSERT(points);
+ Q_ASSERT(pointCount);
+ Q_D(QX11PaintEngine);
+
+ if (!d->has_pen)
+ return;
+
+ // use the same test here as in drawPath to ensure that we don't use the path fallback
+ // and end up in XDrawLines for pens with width <= 1
+ if (d->cpen.widthF() > 1.0f
+ || (X11->use_xrender && (d->has_alpha_pen || (d->render_hints & QPainter::Antialiasing)))
+ || (!d->cpen.isCosmetic() && d->txop > QTransform::TxTranslate))
+ {
+ Qt::PenCapStyle capStyle = d->cpen.capStyle();
+ if (capStyle == Qt::FlatCap) {
+ setCapStyle(CapProjecting, d->gc);
+ d->cpen.setCapStyle(Qt::SquareCap);
+ }
+
+ const QPointF *end = points + pointCount;
+ while (points < end) {
+ QPainterPath path;
+ path.moveTo(*points);
+ path.lineTo(points->x() + 0.005, points->y());
+ drawPath(path);
+ ++points;
+ }
+ if (capStyle == Qt::FlatCap) {
+ setCapStyle(CapButt, d->gc);
+ d->cpen.setCapStyle(capStyle);
+ }
+ return;
+ }
+
+ static const int BUF_SIZE = 1024;
+ XPoint xPoints[BUF_SIZE];
+ int i = 0, j = 0;
+ while (i < pointCount) {
+ while (i < pointCount && j < BUF_SIZE) {
+ const QPointF &xformed = d->matrix.map(points[i]);
+ int x = qFloor(xformed.x());
+ int y = qFloor(xformed.y());
+
+ if (x >= SHRT_MIN && y >= SHRT_MIN && x < SHRT_MAX && y < SHRT_MAX) {
+ xPoints[j].x = x;
+ xPoints[j].y = y;
+ ++j;
+ }
+ ++i;
+ }
+ if (j)
+ XDrawPoints(d->dpy, d->hd, d->gc, xPoints, j, CoordModeOrigin);
+
+ j = 0;
+ }
+}
+
+QPainter::RenderHints QX11PaintEngine::supportedRenderHints() const
+{
+#if !defined(QT_NO_XRENDER)
+ if (X11->use_xrender)
+ return QPainter::Antialiasing;
+#endif
+ return QFlag(0);
+}
+
+void QX11PaintEngine::updateState(const QPaintEngineState &state)
+{
+ Q_D(QX11PaintEngine);
+ QPaintEngine::DirtyFlags flags = state.state();
+
+
+ if (flags & DirtyOpacity) {
+ d->opacity = state.opacity();
+ // Force update pen/brush as to get proper alpha colors propagated
+ flags |= DirtyPen;
+ flags |= DirtyBrush;
+ }
+
+ if (flags & DirtyTransform) updateMatrix(state.transform());
+ if (flags & DirtyPen) updatePen(state.pen());
+ if (flags & (DirtyBrush | DirtyBrushOrigin)) updateBrush(state.brush(), state.brushOrigin());
+ if (flags & DirtyFont) updateFont(state.font());
+
+ if (state.state() & DirtyClipEnabled) {
+ if (state.isClipEnabled()) {
+ QPolygonF clip_poly_dev(d->matrix.map(painter()->clipPath().toFillPolygon()));
+ QPolygonF clipped_poly_dev;
+ d->clipPolygon_dev(clip_poly_dev, &clipped_poly_dev);
+ updateClipRegion_dev(QRegion(clipped_poly_dev.toPolygon()), Qt::ReplaceClip);
+ } else {
+ updateClipRegion_dev(QRegion(), Qt::NoClip);
+ }
+ }
+
+ if (flags & DirtyClipPath) {
+ QPolygonF clip_poly_dev(d->matrix.map(state.clipPath().toFillPolygon()));
+ QPolygonF clipped_poly_dev;
+ d->clipPolygon_dev(clip_poly_dev, &clipped_poly_dev);
+ updateClipRegion_dev(QRegion(clipped_poly_dev.toPolygon(), state.clipPath().fillRule()),
+ state.clipOperation());
+ } else if (flags & DirtyClipRegion) {
+ extern QPainterPath qt_regionToPath(const QRegion &region);
+ QPainterPath clip_path = qt_regionToPath(state.clipRegion());
+ QPolygonF clip_poly_dev(d->matrix.map(clip_path.toFillPolygon()));
+ QPolygonF clipped_poly_dev;
+ d->clipPolygon_dev(clip_poly_dev, &clipped_poly_dev);
+ updateClipRegion_dev(QRegion(clipped_poly_dev.toPolygon()), state.clipOperation());
+ }
+ if (flags & DirtyHints) updateRenderHints(state.renderHints());
+ if (flags & DirtyCompositionMode) {
+ int function = GXcopy;
+ if (state.compositionMode() >= QPainter::RasterOp_SourceOrDestination) {
+ switch (state.compositionMode()) {
+ case QPainter::RasterOp_SourceOrDestination:
+ function = GXor;
+ break;
+ case QPainter::RasterOp_SourceAndDestination:
+ function = GXand;
+ break;
+ case QPainter::RasterOp_SourceXorDestination:
+ function = GXxor;
+ break;
+ case QPainter::RasterOp_NotSourceAndNotDestination:
+ function = GXnor;
+ break;
+ case QPainter::RasterOp_NotSourceOrNotDestination:
+ function = GXnand;
+ break;
+ case QPainter::RasterOp_NotSourceXorDestination:
+ function = GXequiv;
+ break;
+ case QPainter::RasterOp_NotSource:
+ function = GXcopyInverted;
+ break;
+ case QPainter::RasterOp_SourceAndNotDestination:
+ function = GXandReverse;
+ break;
+ case QPainter::RasterOp_NotSourceAndDestination:
+ function = GXandInverted;
+ break;
+ default:
+ function = GXcopy;
+ }
+ }
+#if !defined(QT_NO_XRENDER)
+ else {
+ d->composition_mode =
+ qpainterOpToXrender(state.compositionMode());
+ }
+#endif
+ XSetFunction(X11->display, d->gc, function);
+ XSetFunction(X11->display, d->gc_brush, function);
+ }
+ d->decidePathFallback();
+ d->decideCoordAdjust();
+}
+
+void QX11PaintEngine::updateRenderHints(QPainter::RenderHints hints)
+{
+ Q_D(QX11PaintEngine);
+ d->render_hints = hints;
+
+#if !defined(QT_NO_XRENDER)
+ if (X11->use_xrender && d->picture) {
+ XRenderPictureAttributes attrs;
+ attrs.poly_edge = (hints & QPainter::Antialiasing) ? PolyEdgeSmooth : PolyEdgeSharp;
+ XRenderChangePicture(d->dpy, d->picture, CPPolyEdge, &attrs);
+ }
+#endif
+}
+
+void QX11PaintEngine::updatePen(const QPen &pen)
+{
+ Q_D(QX11PaintEngine);
+ d->cpen = pen;
+ int cp = CapButt;
+ int jn = JoinMiter;
+ int ps = pen.style();
+
+ if (d->opacity < 1.0) {
+ QColor c = d->cpen.color();
+ c.setAlpha(qRound(c.alpha()*d->opacity));
+ d->cpen.setColor(c);
+ }
+
+ d->has_pen = (ps != Qt::NoPen);
+ d->has_alpha_pen = (pen.color().alpha() != 255);
+
+ switch (pen.capStyle()) {
+ case Qt::SquareCap:
+ cp = CapProjecting;
+ break;
+ case Qt::RoundCap:
+ cp = CapRound;
+ break;
+ case Qt::FlatCap:
+ default:
+ cp = CapButt;
+ break;
+ }
+ switch (pen.joinStyle()) {
+ case Qt::BevelJoin:
+ jn = JoinBevel;
+ break;
+ case Qt::RoundJoin:
+ jn = JoinRound;
+ break;
+ case Qt::MiterJoin:
+ default:
+ jn = JoinMiter;
+ break;
+ }
+
+ d->adapted_pen_origin = false;
+
+ char dashes[10]; // custom pen dashes
+ int dash_len = 0; // length of dash list
+ int xStyle = LineSolid;
+
+ /*
+ We are emulating Windows here. Windows treats cpen.width() == 1
+ (or 0) as a very special case. The fudge variable unifies this
+ case with the general case.
+ */
+ qreal pen_width = pen.widthF();
+ int scale = qRound(pen_width < 1 ? 1 : pen_width);
+ int space = (pen_width < 1 && pen_width > 0 ? 1 : (2 * scale));
+ int dot = 1 * scale;
+ int dash = 4 * scale;
+
+ d->has_custom_pen = false;
+
+ switch (ps) {
+ case Qt::NoPen:
+ case Qt::SolidLine:
+ xStyle = LineSolid;
+ break;
+ case Qt::DashLine:
+ dashes[0] = dash;
+ dashes[1] = space;
+ dash_len = 2;
+ xStyle = LineOnOffDash;
+ break;
+ case Qt::DotLine:
+ dashes[0] = dot;
+ dashes[1] = space;
+ dash_len = 2;
+ xStyle = LineOnOffDash;
+ break;
+ case Qt::DashDotLine:
+ dashes[0] = dash;
+ dashes[1] = space;
+ dashes[2] = dot;
+ dashes[3] = space;
+ dash_len = 4;
+ xStyle = LineOnOffDash;
+ break;
+ case Qt::DashDotDotLine:
+ dashes[0] = dash;
+ dashes[1] = space;
+ dashes[2] = dot;
+ dashes[3] = space;
+ dashes[4] = dot;
+ dashes[5] = space;
+ dash_len = 6;
+ xStyle = LineOnOffDash;
+ break;
+ case Qt::CustomDashLine:
+ d->has_custom_pen = true;
+ break;
+ }
+
+ ulong mask = GCForeground | GCBackground | GCGraphicsExposures | GCLineWidth
+ | GCCapStyle | GCJoinStyle | GCLineStyle;
+ XGCValues vals;
+ vals.graphics_exposures = false;
+ if (d->pdev_depth == 1) {
+ vals.foreground = qGray(pen.color().rgb()) > 127 ? 0 : 1;
+ vals.background = qGray(QColor(Qt::transparent).rgb()) > 127 ? 0 : 1;
+ } else if (d->pdev->devType() == QInternal::Pixmap && d->pdev_depth == 32
+ && X11->use_xrender) {
+ vals.foreground = pen.color().rgba();
+ vals.background = QColor(Qt::transparent).rgba();
+ } else {
+ QColormap cmap = QColormap::instance(d->scrn);
+ vals.foreground = cmap.pixel(pen.color());
+ vals.background = cmap.pixel(QColor(Qt::transparent));
+ }
+
+
+ vals.line_width = qRound(pen.widthF());
+ vals.cap_style = cp;
+ vals.join_style = jn;
+ vals.line_style = xStyle;
+
+ XChangeGC(d->dpy, d->gc, mask, &vals);
+
+ if (dash_len) { // make dash list
+ XSetDashes(d->dpy, d->gc, 0, dashes, dash_len);
+ }
+
+ if (!d->has_clipping) { // if clipping is set the paintevent clip region is merged with the clip region
+ QRegion sysClip = systemClip();
+ if (!sysClip.isEmpty())
+ x11SetClipRegion(d->dpy, d->gc, 0, d->picture, sysClip);
+ else
+ x11ClearClipRegion(d->dpy, d->gc, 0, d->picture);
+ }
+}
+
+void QX11PaintEngine::updateBrush(const QBrush &brush, const QPointF &origin)
+{
+ Q_D(QX11PaintEngine);
+ d->cbrush = brush;
+ d->bg_origin = origin;
+ d->adapted_brush_origin = false;
+#if !defined(QT_NO_XRENDER)
+ d->current_brush = 0;
+#endif
+ if (d->opacity < 1.0) {
+ QColor c = d->cbrush.color();
+ c.setAlpha(qRound(c.alpha()*d->opacity));
+ d->cbrush.setColor(c);
+ }
+
+ int s = FillSolid;
+ int bs = d->cbrush.style();
+ d->has_brush = (bs != Qt::NoBrush);
+ d->has_pattern = bs >= Qt::Dense1Pattern && bs <= Qt::DiagCrossPattern;
+ d->has_texture = bs == Qt::TexturePattern;
+ d->has_alpha_brush = brush.color().alpha() != 255;
+ d->has_alpha_texture = d->has_texture && d->cbrush.texture().hasAlphaChannel();
+
+ ulong mask = GCForeground | GCBackground | GCGraphicsExposures
+ | GCLineStyle | GCCapStyle | GCJoinStyle | GCFillStyle;
+ XGCValues vals;
+ vals.graphics_exposures = false;
+ if (d->pdev_depth == 1) {
+ vals.foreground = qGray(d->cbrush.color().rgb()) > 127 ? 0 : 1;
+ vals.background = qGray(QColor(Qt::transparent).rgb()) > 127 ? 0 : 1;
+ } else if (X11->use_xrender && d->pdev->devType() == QInternal::Pixmap
+ && d->pdev_depth == 32) {
+ vals.foreground = d->cbrush.color().rgba();
+ vals.background = QColor(Qt::transparent).rgba();
+ } else {
+ QColormap cmap = QColormap::instance(d->scrn);
+ vals.foreground = cmap.pixel(d->cbrush.color());
+ vals.background = cmap.pixel(QColor(Qt::transparent));
+
+ if (!X11->use_xrender && d->has_brush && !d->has_pattern && !brush.isOpaque()) {
+ QPixmap pattern = qt_patternForAlpha(brush.color().alpha(), d->scrn);
+ mask |= GCStipple;
+ vals.stipple = pattern.handle();
+ s = FillStippled;
+ d->adapted_brush_origin = true;
+ }
+ }
+ vals.cap_style = CapButt;
+ vals.join_style = JoinMiter;
+ vals.line_style = LineSolid;
+
+ if (d->has_pattern || d->has_texture) {
+ if (bs == Qt::TexturePattern) {
+ d->brush_pm = qt_toX11Pixmap(d->cbrush.texture());
+#if !defined(QT_NO_XRENDER)
+ if (X11->use_xrender) {
+ XRenderPictureAttributes attrs;
+ attrs.repeat = true;
+ XRenderChangePicture(d->dpy, d->brush_pm.x11PictureHandle(), CPRepeat, &attrs);
+ QX11PlatformPixmap *data = static_cast<QX11PlatformPixmap*>(d->brush_pm.data.data());
+ if (data->mask_picture)
+ XRenderChangePicture(d->dpy, data->mask_picture, CPRepeat, &attrs);
+ }
+#endif
+ } else {
+ d->brush_pm = qt_toX11Pixmap(qt_pixmapForBrush(bs, true));
+ }
+ d->brush_pm.x11SetScreen(d->scrn);
+ if (d->brush_pm.depth() == 1) {
+ mask |= GCStipple;
+ vals.stipple = d->brush_pm.handle();
+ s = FillStippled;
+#if !defined(QT_NO_XRENDER)
+ if (X11->use_xrender) {
+ d->bitmap_texture = QPixmap(d->brush_pm.size());
+ d->bitmap_texture.fill(Qt::transparent);
+ d->bitmap_texture = qt_toX11Pixmap(d->bitmap_texture);
+ d->bitmap_texture.x11SetScreen(d->scrn);
+
+ ::Picture src = X11->getSolidFill(d->scrn, d->cbrush.color());
+ XRenderComposite(d->dpy, PictOpSrc, src, d->brush_pm.x11PictureHandle(),
+ d->bitmap_texture.x11PictureHandle(),
+ 0, 0, d->brush_pm.width(), d->brush_pm.height(),
+ 0, 0, d->brush_pm.width(), d->brush_pm.height());
+
+ XRenderPictureAttributes attrs;
+ attrs.repeat = true;
+ XRenderChangePicture(d->dpy, d->bitmap_texture.x11PictureHandle(), CPRepeat, &attrs);
+
+ d->current_brush = d->bitmap_texture.x11PictureHandle();
+ }
+#endif
+ } else {
+ mask |= GCTile;
+#ifndef QT_NO_XRENDER
+ if (d->pdev_depth == 32 && d->brush_pm.depth() != 32) {
+ d->brush_pm.detach();
+ QX11PlatformPixmap *brushData = static_cast<QX11PlatformPixmap*>(d->brush_pm.data.data());
+ brushData->convertToARGB32();
+ }
+#endif
+ vals.tile = (d->brush_pm.depth() == d->pdev_depth
+ ? d->brush_pm.handle()
+ : static_cast<QX11PlatformPixmap*>(d->brush_pm.data.data())->x11ConvertToDefaultDepth());
+ s = FillTiled;
+#if !defined(QT_NO_XRENDER)
+ d->current_brush = d->cbrush.texture().x11PictureHandle();
+#endif
+ }
+
+ mask |= GCTileStipXOrigin | GCTileStipYOrigin;
+ vals.ts_x_origin = qRound(origin.x());
+ vals.ts_y_origin = qRound(origin.y());
+ }
+#if !defined(QT_NO_XRENDER)
+ else if (d->has_alpha_brush) {
+ d->current_brush = X11->getSolidFill(d->scrn, d->cbrush.color());
+ }
+#endif
+
+ vals.fill_style = s;
+ XChangeGC(d->dpy, d->gc_brush, mask, &vals);
+ if (!d->has_clipping) {
+ QRegion sysClip = systemClip();
+ if (!sysClip.isEmpty())
+ x11SetClipRegion(d->dpy, d->gc_brush, 0, d->picture, sysClip);
+ else
+ x11ClearClipRegion(d->dpy, d->gc_brush, 0, d->picture);
+ }
+}
+
+void QX11PaintEngine::drawEllipse(const QRectF &rect)
+{
+ QRect aligned = rect.toAlignedRect();
+ if (aligned == rect)
+ drawEllipse(aligned);
+ else
+ QPaintEngine::drawEllipse(rect);
+}
+
+void QX11PaintEngine::drawEllipse(const QRect &rect)
+{
+ if (rect.isEmpty()) {
+ drawRects(&rect, 1);
+ return;
+ }
+
+ Q_D(QX11PaintEngine);
+ QRect devclip(SHRT_MIN, SHRT_MIN, SHRT_MAX*2 - 1, SHRT_MAX*2 - 1);
+ QRect r(rect);
+ if (d->txop < QTransform::TxRotate) {
+ r = d->matrix.mapRect(rect);
+ } else if (d->txop == QTransform::TxRotate && rect.width() == rect.height()) {
+ QPainterPath path;
+ path.addEllipse(rect);
+ r = d->matrix.map(path).boundingRect().toRect();
+ }
+
+ if (d->has_alpha_brush || d->has_alpha_pen || d->has_custom_pen || (d->render_hints & QPainter::Antialiasing)
+ || d->has_alpha_texture || devclip.intersected(r) != r
+ || (d->has_complex_xform
+ && !(d->has_non_scaling_xform && rect.width() == rect.height())))
+ {
+ QPainterPath path;
+ path.addEllipse(rect);
+ drawPath(path);
+ return;
+ }
+
+ int x = r.x();
+ int y = r.y();
+ int w = r.width();
+ int h = r.height();
+ if (w < 1 || h < 1)
+ return;
+ if (w == 1 && h == 1) {
+ XDrawPoint(d->dpy, d->hd, d->has_pen ? d->gc : d->gc_brush, x, y);
+ return;
+ }
+ d->setupAdaptedOrigin(rect.topLeft());
+ if (d->has_brush) { // draw filled ellipse
+ XFillArc(d->dpy, d->hd, d->gc_brush, x, y, w, h, 0, 360*64);
+ if (!d->has_pen) // make smoother outline
+ XDrawArc(d->dpy, d->hd, d->gc_brush, x, y, w-1, h-1, 0, 360*64);
+ }
+ if (d->has_pen) // draw outline
+ XDrawArc(d->dpy, d->hd, d->gc, x, y, w, h, 0, 360*64);
+ d->resetAdaptedOrigin();
+}
+
+
+
+void QX11PaintEnginePrivate::fillPolygon_translated(const QPointF *polygonPoints, int pointCount,
+ QX11PaintEnginePrivate::GCMode gcMode,
+ QPaintEngine::PolygonDrawMode mode)
+{
+
+ QVarLengthArray<QPointF> translated_points(pointCount);
+ QPointF offset(matrix.dx(), matrix.dy());
+
+ qreal offs = adjust_coords ? aliasedCoordinateDelta : 0.0;
+ if (!X11->use_xrender || !(render_hints & QPainter::Antialiasing))
+ offset += QPointF(aliasedCoordinateDelta, aliasedCoordinateDelta);
+
+ for (int i = 0; i < pointCount; ++i) {
+ translated_points[i] = polygonPoints[i] + offset;
+
+ translated_points[i].rx() = qRound(translated_points[i].x()) + offs;
+ translated_points[i].ry() = qRound(translated_points[i].y()) + offs;
+ }
+
+ fillPolygon_dev(translated_points.data(), pointCount, gcMode, mode);
+}
+
+#ifndef QT_NO_XRENDER
+static void qt_XRenderCompositeTrapezoids(Display *dpy,
+ int op,
+ Picture src,
+ Picture dst,
+ _Xconst XRenderPictFormat *maskFormat,
+ int xSrc,
+ int ySrc,
+ const XTrapezoid *traps, int size)
+{
+ const int MAX_TRAPS = 50000;
+ while (size) {
+ int to_draw = size;
+ if (to_draw > MAX_TRAPS)
+ to_draw = MAX_TRAPS;
+ XRenderCompositeTrapezoids(dpy, op, src, dst,
+ maskFormat,
+ xSrc, ySrc,
+ traps, to_draw);
+ size -= to_draw;
+ traps += to_draw;
+ }
+}
+#endif
+
+void QX11PaintEnginePrivate::fillPolygon_dev(const QPointF *polygonPoints, int pointCount,
+ QX11PaintEnginePrivate::GCMode gcMode,
+ QPaintEngine::PolygonDrawMode mode)
+{
+ Q_Q(QX11PaintEngine);
+
+ int clippedCount = 0;
+ qt_float_point *clippedPoints = 0;
+
+#ifndef QT_NO_XRENDER
+ //can change if we switch to pen if gcMode != BrushGC
+ bool has_fill_texture = has_texture;
+ bool has_fill_pattern = has_pattern;
+ ::Picture src;
+#endif
+ QBrush fill;
+ GC fill_gc;
+ if (gcMode == BrushGC) {
+ fill = cbrush;
+ fill_gc = gc_brush;
+#ifndef QT_NO_XRENDER
+ if (current_brush)
+ src = current_brush;
+ else
+ src = X11->getSolidFill(scrn, fill.color());
+#endif
+ } else {
+ fill = QBrush(cpen.brush());
+ fill_gc = gc;
+#ifndef QT_NO_XRENDER
+ //we use the pens brush
+ has_fill_texture = (fill.style() == Qt::TexturePattern);
+ has_fill_pattern = (fill.style() >= Qt::Dense1Pattern && fill.style() <= Qt::DiagCrossPattern);
+ if (has_fill_texture)
+ src = fill.texture().x11PictureHandle();
+ else if (has_fill_pattern)
+ src = getPatternFill(scrn, fill);
+ else
+ src = X11->getSolidFill(scrn, fill.color());
+#endif
+ }
+
+ polygonClipper.clipPolygon((qt_float_point *) polygonPoints, pointCount,
+ &clippedPoints, &clippedCount);
+
+#ifndef QT_NO_XRENDER
+ bool solid_fill = fill.color().alpha() == 255;
+ if (has_fill_texture && fill.texture().depth() == 1 && solid_fill) {
+ has_fill_texture = false;
+ has_fill_pattern = true;
+ }
+
+ bool antialias = render_hints & QPainter::Antialiasing;
+
+ if (X11->use_xrender
+ && picture
+ && !has_fill_pattern
+ && (clippedCount > 0)
+ && (fill.style() != Qt::NoBrush)
+ && ((has_fill_texture && fill.texture().hasAlpha()) || antialias || !solid_fill || has_alpha_pen != has_alpha_brush))
+ {
+ if (tessellator->size > 0) {
+ XRenderPictureAttributes attrs;
+ attrs.poly_edge = antialias ? PolyEdgeSmooth : PolyEdgeSharp;
+ XRenderChangePicture(dpy, picture, CPPolyEdge, &attrs);
+ int x_offset = int(XFixedToDouble(tessellator->traps[0].left.p1.x) - bg_origin.x());
+ int y_offset = int(XFixedToDouble(tessellator->traps[0].left.p1.y) - bg_origin.y());
+ qt_XRenderCompositeTrapezoids(dpy, composition_mode, src, picture,
+ antialias
+ ? XRenderFindStandardFormat(dpy, PictStandardA8)
+ : XRenderFindStandardFormat(dpy, PictStandardA1),
+ x_offset, y_offset,
+ tessellator->traps, tessellator->size);
+ tessellator->done();
+ }
+ } else
+#endif
+ if (fill.style() != Qt::NoBrush) {
+ if (clippedCount > 200000) {
+ QPolygon poly;
+ for (int i = 0; i < clippedCount; ++i)
+ poly << QPoint(qFloor(clippedPoints[i].x), qFloor(clippedPoints[i].y));
+
+ const QRect bounds = poly.boundingRect();
+ const QRect aligned = bounds
+ & QRect(QPoint(), QSize(pdev->width(), pdev->height()));
+
+ QImage img(aligned.size(), QImage::Format_ARGB32_Premultiplied);
+ img.fill(0);
+
+ QPainter painter(&img);
+ painter.translate(-aligned.x(), -aligned.y());
+ painter.setPen(Qt::NoPen);
+ painter.setBrush(fill);
+ if (gcMode == BrushGC)
+ painter.setBrushOrigin(q->painter()->brushOrigin());
+ painter.drawPolygon(poly);
+ painter.end();
+
+ q->drawImage(aligned, img, img.rect(), Qt::AutoColor);
+ } else if (clippedCount > 0) {
+ QVarLengthArray<XPoint> xpoints(clippedCount);
+ for (int i = 0; i < clippedCount; ++i) {
+ xpoints[i].x = qFloor(clippedPoints[i].x);
+ xpoints[i].y = qFloor(clippedPoints[i].y);
+ }
+ if (mode == QPaintEngine::WindingMode)
+ XSetFillRule(dpy, fill_gc, WindingRule);
+ setupAdaptedOrigin(QPoint(xpoints[0].x, xpoints[0].y));
+ XFillPolygon(dpy, hd, fill_gc,
+ xpoints.data(), clippedCount,
+ mode == QPaintEngine::ConvexMode ? Convex : Complex, CoordModeOrigin);
+ resetAdaptedOrigin();
+ if (mode == QPaintEngine::WindingMode)
+ XSetFillRule(dpy, fill_gc, EvenOddRule);
+ }
+ }
+}
+
+void QX11PaintEnginePrivate::strokePolygon_translated(const QPointF *polygonPoints, int pointCount, bool close)
+{
+ QVarLengthArray<QPointF> translated_points(pointCount);
+ QPointF offset(matrix.dx(), matrix.dy());
+ for (int i = 0; i < pointCount; ++i)
+ translated_points[i] = polygonPoints[i] + offset;
+ strokePolygon_dev(translated_points.data(), pointCount, close);
+}
+
+void QX11PaintEnginePrivate::strokePolygon_dev(const QPointF *polygonPoints, int pointCount, bool close)
+{
+ int clippedCount = 0;
+ qt_float_point *clippedPoints = 0;
+ polygonClipper.clipPolygon((qt_float_point *) polygonPoints, pointCount,
+ &clippedPoints, &clippedCount, close);
+
+ if (clippedCount > 0) {
+ QVarLengthArray<XPoint> xpoints(clippedCount);
+ for (int i = 0; i < clippedCount; ++i) {
+ xpoints[i].x = qRound(clippedPoints[i].x + aliasedCoordinateDelta);
+ xpoints[i].y = qRound(clippedPoints[i].y + aliasedCoordinateDelta);
+ }
+ uint numberPoints = qMin(clippedCount, xlibMaxLinePoints);
+ XPoint *pts = xpoints.data();
+ XDrawLines(dpy, hd, gc, pts, numberPoints, CoordModeOrigin);
+ pts += numberPoints;
+ clippedCount -= numberPoints;
+ numberPoints = qMin(clippedCount, xlibMaxLinePoints-1);
+ while (clippedCount) {
+ XDrawLines(dpy, hd, gc, pts-1, numberPoints+1, CoordModeOrigin);
+ pts += numberPoints;
+ clippedCount -= numberPoints;
+ numberPoints = qMin(clippedCount, xlibMaxLinePoints-1);
+ }
+ }
+}
+
+void QX11PaintEngine::drawPolygon(const QPointF *polygonPoints, int pointCount, PolygonDrawMode mode)
+{
+ Q_D(QX11PaintEngine);
+ if (d->use_path_fallback) {
+ QPainterPath path(polygonPoints[0]);
+ for (int i = 1; i < pointCount; ++i)
+ path.lineTo(polygonPoints[i]);
+ if (mode == PolylineMode) {
+ QBrush oldBrush = d->cbrush;
+ d->cbrush = QBrush(Qt::NoBrush);
+ path.setFillRule(Qt::WindingFill);
+ drawPath(path);
+ d->cbrush = oldBrush;
+ } else {
+ path.setFillRule(mode == OddEvenMode ? Qt::OddEvenFill : Qt::WindingFill);
+ path.closeSubpath();
+ drawPath(path);
+ }
+ return;
+ }
+ if (mode != PolylineMode && d->has_brush)
+ d->fillPolygon_translated(polygonPoints, pointCount, QX11PaintEnginePrivate::BrushGC, mode);
+
+ if (d->has_pen)
+ d->strokePolygon_translated(polygonPoints, pointCount, mode != PolylineMode);
+}
+
+
+void QX11PaintEnginePrivate::fillPath(const QPainterPath &path, QX11PaintEnginePrivate::GCMode gc_mode, bool transform)
+{
+ qreal offs = adjust_coords ? aliasedCoordinateDelta : 0.0;
+
+ QPainterPath clippedPath;
+ QPainterPath clipPath;
+ clipPath.addRect(polygonClipper.boundingRect());
+
+ if (transform)
+ clippedPath = (path*matrix).intersected(clipPath);
+ else
+ clippedPath = path.intersected(clipPath);
+
+ QList<QPolygonF> polys = clippedPath.toFillPolygons();
+ for (int i = 0; i < polys.size(); ++i) {
+ QVarLengthArray<QPointF> translated_points(polys.at(i).size());
+
+ for (int j = 0; j < polys.at(i).size(); ++j) {
+ translated_points[j] = polys.at(i).at(j);
+ if (!X11->use_xrender || !(render_hints & QPainter::Antialiasing)) {
+ translated_points[j].rx() = qRound(translated_points[j].rx() + aliasedCoordinateDelta) + offs;
+ translated_points[j].ry() = qRound(translated_points[j].ry() + aliasedCoordinateDelta) + offs;
+ }
+ }
+
+ fillPolygon_dev(translated_points.data(), polys.at(i).size(), gc_mode,
+ path.fillRule() == Qt::OddEvenFill ? QPaintEngine::OddEvenMode : QPaintEngine::WindingMode);
+ }
+}
+
+void QX11PaintEngine::drawPath(const QPainterPath &path)
+{
+ Q_D(QX11PaintEngine);
+ if (path.isEmpty())
+ return;
+
+ if (d->has_brush)
+ d->fillPath(path, QX11PaintEnginePrivate::BrushGC, true);
+ if (d->has_pen
+ && ((X11->use_xrender && (d->has_alpha_pen || (d->render_hints & QPainter::Antialiasing)))
+ || (!d->cpen.isCosmetic() && d->txop > QTransform::TxTranslate
+ && !d->has_non_scaling_xform)
+ || (d->cpen.style() == Qt::CustomDashLine))) {
+ QPainterPathStroker stroker;
+ if (d->cpen.style() == Qt::CustomDashLine) {
+ stroker.setDashPattern(d->cpen.dashPattern());
+ stroker.setDashOffset(d->cpen.dashOffset());
+ } else {
+ stroker.setDashPattern(d->cpen.style());
+ }
+ stroker.setCapStyle(d->cpen.capStyle());
+ stroker.setJoinStyle(d->cpen.joinStyle());
+ QPainterPath stroke;
+ qreal width = d->cpen.widthF();
+ QPolygonF poly;
+ QRectF deviceRect(0, 0, d->pdev->width(), d->pdev->height());
+ // necessary to get aliased alphablended primitives to be drawn correctly
+ if (d->cpen.isCosmetic() || d->has_scaling_xform) {
+ if (d->cpen.isCosmetic())
+ stroker.setWidth(width == 0 ? 1 : width);
+ else
+ stroker.setWidth(width * d->xform_scale);
+ stroker.d_ptr->stroker.setClipRect(deviceRect);
+ stroke = stroker.createStroke(path * d->matrix);
+ if (stroke.isEmpty())
+ return;
+ stroke.setFillRule(Qt::WindingFill);
+ d->fillPath(stroke, QX11PaintEnginePrivate::PenGC, false);
+ } else {
+ stroker.setWidth(width);
+ stroker.d_ptr->stroker.setClipRect(d->matrix.inverted().mapRect(deviceRect));
+ stroke = stroker.createStroke(path);
+ if (stroke.isEmpty())
+ return;
+ stroke.setFillRule(Qt::WindingFill);
+ d->fillPath(stroke, QX11PaintEnginePrivate::PenGC, true);
+ }
+ } else if (d->has_pen) {
+ // if we have a cosmetic pen - use XDrawLine() for speed
+ QList<QPolygonF> polys = path.toSubpathPolygons(d->matrix);
+ for (int i = 0; i < polys.size(); ++i)
+ d->strokePolygon_dev(polys.at(i).data(), polys.at(i).size(), false);
+ }
+}
+
+Q_WIDGETS_EXPORT void qt_x11_drawImage(const QRect &rect, const QPoint &pos, const QImage &image,
+ Drawable hd, GC gc, Display *dpy, Visual *visual, int depth)
+{
+ Q_ASSERT(image.format() == QImage::Format_RGB32);
+ Q_ASSERT(image.depth() == 32);
+
+ XImage *xi;
+ // Note: this code assumes either RGB or BGR, 8 bpc server layouts
+ const uint red_mask = (uint) visual->red_mask;
+ bool bgr_layout = (red_mask == 0xff);
+
+ const int w = rect.width();
+ const int h = rect.height();
+
+ QImage im;
+ int image_byte_order = ImageByteOrder(X11->display);
+ if ((QSysInfo::ByteOrder == QSysInfo::BigEndian && ((image_byte_order == LSBFirst) || bgr_layout))
+ || (image_byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::LittleEndian)
+ || (image_byte_order == LSBFirst && bgr_layout))
+ {
+ im = image.copy(rect);
+ const int iw = im.bytesPerLine() / 4;
+ uint *data = (uint *)im.bits();
+ for (int i=0; i < h; i++) {
+ uint *p = data;
+ uint *end = p + w;
+ if (bgr_layout && image_byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
+ while (p < end) {
+ *p = ((*p << 8) & 0xffffff00) | ((*p >> 24) & 0x000000ff);
+ p++;
+ }
+ } else if ((image_byte_order == LSBFirst && QSysInfo::ByteOrder == QSysInfo::BigEndian)
+ || (image_byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::LittleEndian)) {
+ while (p < end) {
+ *p = ((*p << 24) & 0xff000000) | ((*p << 8) & 0x00ff0000)
+ | ((*p >> 8) & 0x0000ff00) | ((*p >> 24) & 0x000000ff);
+ p++;
+ }
+ } else if ((image_byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::BigEndian)
+ || (image_byte_order == LSBFirst && bgr_layout))
+ {
+ while (p < end) {
+ *p = ((*p << 16) & 0x00ff0000) | ((*p >> 16) & 0x000000ff)
+ | ((*p ) & 0xff00ff00);
+ p++;
+ }
+ }
+ data += iw;
+ }
+ xi = XCreateImage(dpy, visual, depth, ZPixmap,
+ 0, (char *) im.bits(), w, h, 32, im.bytesPerLine());
+ } else {
+ xi = XCreateImage(dpy, visual, depth, ZPixmap,
+ 0, (char *) image.scanLine(rect.y())+rect.x()*sizeof(uint), w, h, 32, image.bytesPerLine());
+ }
+ XPutImage(dpy, hd, gc, xi, 0, 0, pos.x(), pos.y(), w, h);
+ xi->data = 0; // QImage owns these bits
+ XDestroyImage(xi);
+}
+
+void QX11PaintEngine::drawImage(const QRectF &r, const QImage &image, const QRectF &sr, Qt::ImageConversionFlags flags)
+{
+ Q_D(QX11PaintEngine);
+
+ if (image.format() == QImage::Format_RGB32
+ && d->pdev_depth >= 24 && image.depth() == 32
+ && r.size() == sr.size())
+ {
+ int sx = qRound(sr.x());
+ int sy = qRound(sr.y());
+ int x = qRound(r.x());
+ int y = qRound(r.y());
+ int w = qRound(r.width());
+ int h = qRound(r.height());
+
+ qt_x11_drawImage(QRect(sx, sy, w, h), QPoint(x, y), image, d->hd, d->gc, d->dpy,
+ (Visual *)d->xinfo->visual(), d->pdev_depth);
+ } else {
+ QPaintEngine::drawImage(r, image, sr, flags);
+ }
+}
+
+void QX11PaintEngine::drawPixmap(const QRectF &r, const QPixmap &px, const QRectF &_sr)
+{
+ Q_D(QX11PaintEngine);
+ QRectF sr = _sr;
+ int x = qRound(r.x());
+ int y = qRound(r.y());
+ int sx = qRound(sr.x());
+ int sy = qRound(sr.y());
+ int sw = qRound(sr.width());
+ int sh = qRound(sr.height());
+
+ QPixmap pixmap = qt_toX11Pixmap(px);
+ if(pixmap.isNull())
+ return;
+
+ if ((d->xinfo && d->xinfo->screen() != pixmap.x11Info().screen())
+ || (pixmap.x11Info().screen() != DefaultScreen(X11->display))) {
+ QPixmap* p = const_cast<QPixmap *>(&pixmap);
+ p->x11SetScreen(d->xinfo ? d->xinfo->screen() : DefaultScreen(X11->display));
+ }
+
+ QPixmap::x11SetDefaultScreen(pixmap.x11Info().screen());
+
+#ifndef QT_NO_XRENDER
+ ::Picture src_pict = static_cast<QX11PlatformPixmap*>(pixmap.data.data())->picture;
+ if (src_pict && d->picture) {
+ const int pDepth = pixmap.depth();
+ if (pDepth == 1 && (d->has_alpha_pen)) {
+ qt_render_bitmap(d->dpy, d->scrn, src_pict, d->picture,
+ sx, sy, x, y, sw, sh, d->cpen);
+ return;
+ } else if (pDepth != 1 && (pDepth == 32 || pDepth != d->pdev_depth)) {
+ XRenderComposite(d->dpy, d->composition_mode,
+ src_pict, 0, d->picture, sx, sy, 0, 0, x, y, sw, sh);
+ return;
+ }
+ }
+#endif
+
+ bool mono_src = pixmap.depth() == 1;
+ bool mono_dst = d->pdev_depth == 1;
+ bool restore_clip = false;
+
+ if (static_cast<QX11PlatformPixmap*>(pixmap.data.data())->x11_mask) { // pixmap has a mask
+ QBitmap comb(sw, sh);
+ GC cgc = XCreateGC(d->dpy, comb.handle(), 0, 0);
+ XSetForeground(d->dpy, cgc, 0);
+ XFillRectangle(d->dpy, comb.handle(), cgc, 0, 0, sw, sh);
+ XSetBackground(d->dpy, cgc, 0);
+ XSetForeground(d->dpy, cgc, 1);
+ if (!d->crgn.isEmpty()) {
+ int num;
+ XRectangle *rects = (XRectangle *)qt_getClipRects(d->crgn, num);
+ XSetClipRectangles(d->dpy, cgc, -x, -y, rects, num, Unsorted);
+ } else if (d->has_clipping) {
+ XSetClipRectangles(d->dpy, cgc, 0, 0, 0, 0, Unsorted);
+ }
+ XSetFillStyle(d->dpy, cgc, FillOpaqueStippled);
+ XSetTSOrigin(d->dpy, cgc, -sx, -sy);
+ XSetStipple(d->dpy, cgc,
+ static_cast<QX11PlatformPixmap*>(pixmap.data.data())->x11_mask);
+ XFillRectangle(d->dpy, comb.handle(), cgc, 0, 0, sw, sh);
+ XFreeGC(d->dpy, cgc);
+
+ XSetClipOrigin(d->dpy, d->gc, x, y);
+ XSetClipMask(d->dpy, d->gc, comb.handle());
+ restore_clip = true;
+ }
+
+ if (mono_src) {
+ if (!d->crgn.isEmpty()) {
+ Pixmap comb = XCreatePixmap(d->dpy, d->hd, sw, sh, 1);
+ GC cgc = XCreateGC(d->dpy, comb, 0, 0);
+ XSetForeground(d->dpy, cgc, 0);
+ XFillRectangle(d->dpy, comb, cgc, 0, 0, sw, sh);
+ int num;
+ XRectangle *rects = (XRectangle *)qt_getClipRects(d->crgn, num);
+ XSetClipRectangles(d->dpy, cgc, -x, -y, rects, num, Unsorted);
+ XCopyArea(d->dpy, pixmap.handle(), comb, cgc, sx, sy, sw, sh, 0, 0);
+ XFreeGC(d->dpy, cgc);
+
+ XSetClipMask(d->dpy, d->gc, comb);
+ XSetClipOrigin(d->dpy, d->gc, x, y);
+ XFreePixmap(d->dpy, comb);
+ } else {
+ XSetClipMask(d->dpy, d->gc, pixmap.handle());
+ XSetClipOrigin(d->dpy, d->gc, x - sx, y - sy);
+ }
+
+ if (mono_dst) {
+ XSetForeground(d->dpy, d->gc, qGray(d->cpen.color().rgb()) > 127 ? 0 : 1);
+ } else {
+ QColormap cmap = QColormap::instance(d->scrn);
+ XSetForeground(d->dpy, d->gc, cmap.pixel(d->cpen.color()));
+ }
+ XFillRectangle(d->dpy, d->hd, d->gc, x, y, sw, sh);
+ restore_clip = true;
+ } else if (mono_dst && !mono_src) {
+ QBitmap bitmap(pixmap);
+ XCopyArea(d->dpy, bitmap.handle(), d->hd, d->gc, sx, sy, sw, sh, x, y);
+ } else {
+ XCopyArea(d->dpy, pixmap.handle(), d->hd, d->gc, sx, sy, sw, sh, x, y);
+ }
+
+ if (d->pdev->devType() == QInternal::Pixmap) {
+ const QPixmap *px = static_cast<const QPixmap*>(d->pdev);
+ Pixmap src_mask = static_cast<QX11PlatformPixmap*>(pixmap.data.data())->x11_mask;
+ Pixmap dst_mask = static_cast<QX11PlatformPixmap*>(px->data.data())->x11_mask;
+ if (dst_mask) {
+ GC cgc = XCreateGC(d->dpy, dst_mask, 0, 0);
+ if (src_mask) { // copy src mask into dst mask
+ XCopyArea(d->dpy, src_mask, dst_mask, cgc, sx, sy, sw, sh, x, y);
+ } else { // no src mask, but make sure the area copied is opaque in dest
+ XSetBackground(d->dpy, cgc, 0);
+ XSetForeground(d->dpy, cgc, 1);
+ XFillRectangle(d->dpy, dst_mask, cgc, x, y, sw, sh);
+ }
+ XFreeGC(d->dpy, cgc);
+ }
+ }
+
+ if (restore_clip) {
+ XSetClipOrigin(d->dpy, d->gc, 0, 0);
+ int num;
+ XRectangle *rects = (XRectangle *)qt_getClipRects(d->crgn, num);
+ if (num == 0)
+ XSetClipMask(d->dpy, d->gc, XNone);
+ else
+ XSetClipRectangles(d->dpy, d->gc, 0, 0, rects, num, Unsorted);
+ }
+}
+
+void QX11PaintEngine::updateMatrix(const QTransform &mtx)
+{
+ Q_D(QX11PaintEngine);
+ d->txop = mtx.type();
+ d->matrix = mtx;
+
+ d->has_complex_xform = (d->txop > QTransform::TxTranslate);
+
+ extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale);
+ bool scaling = qt_scaleForTransform(d->matrix, &d->xform_scale);
+ d->has_scaling_xform = scaling && d->xform_scale != 1.0;
+ d->has_non_scaling_xform = scaling && d->xform_scale == 1.0;
+}
+
+/*
+ NB! the clip region is expected to be in dev coordinates
+*/
+void QX11PaintEngine::updateClipRegion_dev(const QRegion &clipRegion, Qt::ClipOperation op)
+{
+ Q_D(QX11PaintEngine);
+ QRegion sysClip = systemClip();
+ if (op == Qt::NoClip) {
+ d->has_clipping = false;
+ d->crgn = sysClip;
+ if (!sysClip.isEmpty()) {
+ x11SetClipRegion(d->dpy, d->gc, d->gc_brush, d->picture, sysClip);
+ } else {
+ x11ClearClipRegion(d->dpy, d->gc, d->gc_brush, d->picture);
+ }
+ return;
+ }
+
+ switch (op) {
+ case Qt::IntersectClip:
+ if (d->has_clipping) {
+ d->crgn &= clipRegion;
+ break;
+ }
+ // fall through
+ case Qt::ReplaceClip:
+ if (!sysClip.isEmpty())
+ d->crgn = clipRegion.intersected(sysClip);
+ else
+ d->crgn = clipRegion;
+ break;
+ default:
+ break;
+ }
+ d->has_clipping = true;
+ x11SetClipRegion(d->dpy, d->gc, d->gc_brush, d->picture, d->crgn);
+}
+
+void QX11PaintEngine::updateFont(const QFont &)
+{
+}
+
+Qt::HANDLE QX11PaintEngine::handle() const
+{
+ Q_D(const QX11PaintEngine);
+ Q_ASSERT(isActive());
+ Q_ASSERT(d->hd);
+ return d->hd;
+}
+
+extern void qt_draw_tile(QPaintEngine *, qreal, qreal, qreal, qreal, const QPixmap &,
+ qreal, qreal);
+
+void QX11PaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &p)
+{
+ int x = qRound(r.x());
+ int y = qRound(r.y());
+ int w = qRound(r.width());
+ int h = qRound(r.height());
+ int sx = qRound(p.x());
+ int sy = qRound(p.y());
+
+ bool mono_src = pixmap.depth() == 1;
+ Q_D(QX11PaintEngine);
+
+ if ((d->xinfo && d->xinfo->screen() != pixmap.x11Info().screen())
+ || (pixmap.x11Info().screen() != DefaultScreen(X11->display))) {
+ QPixmap* p = const_cast<QPixmap *>(&pixmap);
+ p->x11SetScreen(d->xinfo ? d->xinfo->screen() : DefaultScreen(X11->display));
+ }
+
+ QPixmap::x11SetDefaultScreen(pixmap.x11Info().screen());
+
+#ifndef QT_NO_XRENDER
+ if (X11->use_xrender && d->picture && pixmap.x11PictureHandle()) {
+#if 0
+ // ### Qt 5: enable this
+ XRenderPictureAttributes attrs;
+ attrs.repeat = true;
+ XRenderChangePicture(d->dpy, pixmap.x11PictureHandle(), CPRepeat, &attrs);
+
+ if (mono_src) {
+ qt_render_bitmap(d->dpy, d->scrn, pixmap.x11PictureHandle(), d->picture,
+ sx, sy, x, y, w, h, d->cpen);
+ } else {
+ XRenderComposite(d->dpy, d->composition_mode,
+ pixmap.x11PictureHandle(), XNone, d->picture,
+ sx, sy, 0, 0, x, y, w, h);
+ }
+#else
+ const int numTiles = (w / pixmap.width()) * (h / pixmap.height());
+ if (numTiles < 100) {
+ // this is essentially qt_draw_tile(), inlined for
+ // the XRenderComposite call
+ int yPos, xPos, drawH, drawW, yOff, xOff;
+ yPos = y;
+ yOff = sy;
+ while(yPos < y + h) {
+ drawH = pixmap.height() - yOff; // Cropping first row
+ if (yPos + drawH > y + h) // Cropping last row
+ drawH = y + h - yPos;
+ xPos = x;
+ xOff = sx;
+ while(xPos < x + w) {
+ drawW = pixmap.width() - xOff; // Cropping first column
+ if (xPos + drawW > x + w) // Cropping last column
+ drawW = x + w - xPos;
+ if (mono_src) {
+ qt_render_bitmap(d->dpy, d->scrn, pixmap.x11PictureHandle(), d->picture,
+ xOff, yOff, xPos, yPos, drawW, drawH, d->cpen);
+ } else {
+ XRenderComposite(d->dpy, d->composition_mode,
+ pixmap.x11PictureHandle(), XNone, d->picture,
+ xOff, yOff, 0, 0, xPos, yPos, drawW, drawH);
+ }
+ xPos += drawW;
+ xOff = 0;
+ }
+ yPos += drawH;
+ yOff = 0;
+ }
+ } else {
+ w = qMin(w, d->pdev->width() - x);
+ h = qMin(h, d->pdev->height() - y);
+ if (w <= 0 || h <= 0)
+ return;
+
+ const int pw = w + sx;
+ const int ph = h + sy;
+ QPixmap pm(pw, ph);
+ if (pixmap.hasAlpha() || mono_src)
+ pm.fill(Qt::transparent);
+
+ const int mode = pixmap.hasAlpha() ? PictOpOver : PictOpSrc;
+ const ::Picture pmPicture = pm.x11PictureHandle();
+
+ // first tile
+ XRenderComposite(d->dpy, mode,
+ pixmap.x11PictureHandle(), XNone, pmPicture,
+ 0, 0, 0, 0, 0, 0, qMin(pw, pixmap.width()), qMin(ph, pixmap.height()));
+
+ // first row of tiles
+ int xPos = pixmap.width();
+ const int sh = qMin(ph, pixmap.height());
+ while (xPos < pw) {
+ const int sw = qMin(xPos, pw - xPos);
+ XRenderComposite(d->dpy, mode,
+ pmPicture, XNone, pmPicture,
+ 0, 0, 0, 0, xPos, 0, sw, sh);
+ xPos *= 2;
+ }
+
+ // remaining rows
+ int yPos = pixmap.height();
+ const int sw = pw;
+ while (yPos < ph) {
+ const int sh = qMin(yPos, ph - yPos);
+ XRenderComposite(d->dpy, mode,
+ pmPicture, XNone, pmPicture,
+ 0, 0, 0, 0, 0, yPos, sw, sh);
+ yPos *= 2;
+ }
+
+ // composite
+ if (mono_src)
+ qt_render_bitmap(d->dpy, d->scrn, pmPicture, d->picture,
+ sx, sy, x, y, w, h, d->cpen);
+ else
+ XRenderComposite(d->dpy, d->composition_mode,
+ pmPicture, XNone, d->picture,
+ sx, sy, 0, 0, x, y, w, h);
+ }
+#endif
+ } else
+#endif // !QT_NO_XRENDER
+ if (pixmap.depth() > 1 && !static_cast<QX11PlatformPixmap*>(pixmap.data.data())->x11_mask) {
+ XSetTile(d->dpy, d->gc, pixmap.handle());
+ XSetFillStyle(d->dpy, d->gc, FillTiled);
+ XSetTSOrigin(d->dpy, d->gc, x-sx, y-sy);
+ XFillRectangle(d->dpy, d->hd, d->gc, x, y, w, h);
+ XSetTSOrigin(d->dpy, d->gc, 0, 0);
+ XSetFillStyle(d->dpy, d->gc, FillSolid);
+ } else {
+ qt_draw_tile(this, x, y, w, h, pixmap, sx, sy);
+ }
+}
+
+void QX11PaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
+{
+ const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
+
+ switch(ti.fontEngine->type()) {
+ case QFontEngine::TestFontEngine:
+ case QFontEngine::Box:
+ d_func()->drawBoxTextItem(p, ti);
+ break;
+ case QFontEngine::XLFD:
+ drawXLFD(p, ti);
+ break;
+#ifndef QT_NO_FONTCONFIG
+ case QFontEngine::Freetype:
+ drawFreetype(p, ti);
+ break;
+#endif
+ default:
+ Q_ASSERT(false);
+ }
+}
+
+void QX11PaintEngine::drawXLFD(const QPointF &p, const QTextItemInt &ti)
+{
+ Q_D(QX11PaintEngine);
+
+ if (d->txop > QTransform::TxTranslate) {
+ // XServer or font don't support server side transformations, need to do it by hand
+ QPaintEngine::drawTextItem(p, ti);
+ return;
+ }
+
+ if (!ti.glyphs.numGlyphs)
+ return;
+
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> glyphs;
+ QTransform matrix = d->matrix;
+ matrix.translate(p.x(), p.y());
+ ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
+ if (glyphs.size() == 0)
+ return;
+
+ QFontEngineXLFD *xlfd = static_cast<QFontEngineXLFD *>(ti.fontEngine);
+ Qt::HANDLE font_id = xlfd->fontStruct()->fid;
+
+ XSetFont(d->dpy, d->gc, font_id);
+
+ const QFixed offs = QFixed::fromReal(aliasedCoordinateDelta);
+ for (int i = 0; i < glyphs.size(); i++) {
+ int xp = qRound(positions[i].x + offs);
+ int yp = qRound(positions[i].y + offs);
+ if (xp < SHRT_MAX && xp > SHRT_MIN && yp > SHRT_MIN && yp < SHRT_MAX) {
+ XChar2b ch;
+ ch.byte1 = glyphs[i] >> 8;
+ ch.byte2 = glyphs[i] & 0xff;
+ XDrawString16(d->dpy, d->hd, d->gc, xp, yp, &ch, 1);
+ }
+ }
+}
+
+#ifndef QT_NO_FONTCONFIG
+static QPainterPath path_for_glyphs(const QVarLengthArray<glyph_t> &glyphs,
+ const QVarLengthArray<QFixedPoint> &positions,
+ const QFontEngineFT *ft)
+{
+ QPainterPath path;
+ path.setFillRule(Qt::WindingFill);
+ ft->lockFace();
+ int i = 0;
+ while (i < glyphs.size()) {
+ QFontEngineFT::Glyph *glyph = ft->loadGlyph(glyphs[i], QFontEngineFT::Format_Mono);
+ // #### fix case where we don't get a glyph
+ if (!glyph)
+ break;
+
+ Q_ASSERT(glyph->format == QFontEngineFT::Format_Mono);
+ int n = 0;
+ int h = glyph->height;
+ int xp = qRound(positions[i].x);
+ int yp = qRound(positions[i].y);
+
+ xp += glyph->x;
+ yp += -glyph->y + glyph->height;
+ int pitch = ((glyph->width + 31) & ~31) >> 3;
+
+ uchar *src = glyph->data;
+ while (h--) {
+ for (int x = 0; x < glyph->width; ++x) {
+ bool set = src[x >> 3] & (0x80 >> (x & 7));
+ if (set) {
+ QRect r(xp + x, yp - h, 1, 1);
+ while (x < glyph->width-1 && src[(x+1) >> 3] & (0x80 >> ((x+1) & 7))) {
+ ++x;
+ r.setRight(r.right()+1);
+ }
+
+ path.addRect(r);
+ ++n;
+ }
+ }
+ src += pitch;
+ }
+ ++i;
+ }
+ ft->unlockFace();
+ return path;
+}
+
+void QX11PaintEngine::drawFreetype(const QPointF &p, const QTextItemInt &ti)
+{
+ Q_D(QX11PaintEngine);
+ if (!ti.glyphs.numGlyphs)
+ return;
+
+ QFontEngineX11FT *ft = static_cast<QFontEngineX11FT *>(ti.fontEngine);
+
+ if (!d->cpen.isSolid()) {
+ QPaintEngine::drawTextItem(p, ti);
+ return;
+ }
+
+ const bool xrenderPath = (X11->use_xrender
+ && !(d->pdev->devType() == QInternal::Pixmap
+ && static_cast<const QPixmap *>(d->pdev)->data->pixelType() == QPlatformPixmap::BitmapType));
+
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> glyphs;
+ QTransform matrix;
+
+ if (xrenderPath)
+ matrix = d->matrix;
+ matrix.translate(p.x(), p.y());
+ ft->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
+ if (glyphs.count() == 0)
+ return;
+
+#ifndef QT_NO_XRENDER
+ QFontEngineFT::QGlyphSet *set = ft->defaultGlyphs();
+ if (d->txop >= QTransform::TxScale && xrenderPath)
+ set = ft->loadTransformedGlyphSet(d->matrix);
+
+ if (!set || set->outline_drawing
+ || !ft->loadGlyphs(set, glyphs.constData(), glyphs.size(), positions.constData(), QFontEngineFT::Format_Render))
+ {
+ QPaintEngine::drawTextItem(p, ti);
+ return;
+ }
+
+ if (xrenderPath) {
+ GlyphSet glyphSet = set->id;
+ const QColor &pen = d->cpen.color();
+ ::Picture src = X11->getSolidFill(d->scrn, pen);
+ XRenderPictFormat *maskFormat = 0;
+ if (ft->xglyph_format != PictStandardA1)
+ maskFormat = XRenderFindStandardFormat(X11->display, ft->xglyph_format);
+
+ enum { t_min = SHRT_MIN, t_max = SHRT_MAX };
+
+ int i = 0;
+ for (; i < glyphs.size()
+ && (positions[i].x < t_min || positions[i].x > t_max
+ || positions[i].y < t_min || positions[i].y > t_max);
+ ++i)
+ ;
+
+ if (i >= glyphs.size())
+ return;
+ ++i;
+
+ QFixed xp = positions[i - 1].x;
+ QFixed yp = positions[i - 1].y;
+ QFixed offs = QFixed::fromReal(aliasedCoordinateDelta);
+
+ XGlyphElt32 elt;
+ elt.glyphset = glyphSet;
+ elt.chars = &glyphs[i - 1];
+ elt.nchars = 1;
+ elt.xOff = qRound(xp + offs);
+ elt.yOff = qRound(yp + offs);
+ for (; i < glyphs.size(); ++i) {
+ if (positions[i].x < t_min || positions[i].x > t_max
+ || positions[i].y < t_min || positions[i].y > t_max) {
+ break;
+ }
+ QFontEngineFT::Glyph *g = ft->cachedGlyph(glyphs[i - 1]);
+ if (g
+ && positions[i].x == xp + g->advance
+ && positions[i].y == yp
+ && elt.nchars < 253 // don't draw more than 253 characters as some X servers
+ // hang with it
+ ) {
+ elt.nchars++;
+ xp += g->advance;
+ } else {
+ xp = positions[i].x;
+ yp = positions[i].y;
+
+ XRenderCompositeText32(X11->display, PictOpOver, src, d->picture,
+ maskFormat, 0, 0, 0, 0,
+ &elt, 1);
+ elt.chars = &glyphs[i];
+ elt.nchars = 1;
+ elt.xOff = qRound(xp + offs);
+ elt.yOff = qRound(yp + offs);
+ }
+ }
+ XRenderCompositeText32(X11->display, PictOpOver, src, d->picture,
+ maskFormat, 0, 0, 0, 0,
+ &elt, 1);
+
+ return;
+
+ }
+#endif
+
+ QPainterPath path = path_for_glyphs(glyphs, positions, ft);
+ if (path.elementCount() <= 1)
+ return;
+ Q_ASSERT((path.elementCount() % 5) == 0);
+ if (d->txop >= QTransform::TxScale) {
+ painter()->save();
+ painter()->setBrush(d->cpen.brush());
+ painter()->setPen(Qt::NoPen);
+ painter()->drawPath(path);
+ painter()->restore();
+ return;
+ }
+
+ const int rectcount = 256;
+ XRectangle rects[rectcount];
+ int num_rects = 0;
+
+ QPoint delta(qRound(d->matrix.dx()), qRound(d->matrix.dy()));
+ QRect clip(d->polygonClipper.boundingRect());
+ for (int i=0; i < path.elementCount(); i+=5) {
+ int x = qRound(path.elementAt(i).x);
+ int y = qRound(path.elementAt(i).y);
+ int w = qRound(path.elementAt(i+1).x) - x;
+ int h = qRound(path.elementAt(i+2).y) - y;
+
+ QRect rect = QRect(x + delta.x(), y + delta.y(), w, h);
+ rect = rect.intersected(clip);
+ if (rect.isEmpty())
+ continue;
+
+ rects[num_rects].x = short(rect.x());
+ rects[num_rects].y = short(rect.y());
+ rects[num_rects].width = ushort(rect.width());
+ rects[num_rects].height = ushort(rect.height());
+ ++num_rects;
+ if (num_rects == rectcount) {
+ XFillRectangles(d->dpy, d->hd, d->gc, rects, num_rects);
+ num_rects = 0;
+ }
+ }
+ if (num_rects > 0)
+ XFillRectangles(d->dpy, d->hd, d->gc, rects, num_rects);
+
+}
+#endif // !QT_NO_XRENDER
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/x11/qpaintengine_x11_p.h b/src/widgets/platforms/x11/qpaintengine_x11_p.h
new file mode 100644
index 0000000000..2ff339bbe1
--- /dev/null
+++ b/src/widgets/platforms/x11/qpaintengine_x11_p.h
@@ -0,0 +1,246 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTENGINE_X11_P_H
+#define QPAINTENGINE_X11_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtGui/qpaintengine.h"
+#include "QtGui/qregion.h"
+#include "QtGui/qpen.h"
+#include "QtCore/qpoint.h"
+#include "private/qpaintengine_p.h"
+#include "private/qpainter_p.h"
+#include "private/qpolygonclipper_p.h"
+
+typedef unsigned long Picture;
+
+QT_BEGIN_NAMESPACE
+
+class QX11PaintEnginePrivate;
+class QFontEngineFT;
+class QXRenderTessellator;
+
+struct qt_float_point
+{
+ qreal x, y;
+};
+
+class QX11PaintEngine : public QPaintEngine
+{
+ Q_DECLARE_PRIVATE(QX11PaintEngine)
+public:
+ QX11PaintEngine();
+ ~QX11PaintEngine();
+
+ bool begin(QPaintDevice *pdev);
+ bool end();
+
+ void updateState(const QPaintEngineState &state);
+
+ void updatePen(const QPen &pen);
+ void updateBrush(const QBrush &brush, const QPointF &pt);
+ void updateRenderHints(QPainter::RenderHints hints);
+ void updateFont(const QFont &font);
+ void updateMatrix(const QTransform &matrix);
+ void updateClipRegion_dev(const QRegion &region, Qt::ClipOperation op);
+
+ void drawLines(const QLine *lines, int lineCount);
+ void drawLines(const QLineF *lines, int lineCount);
+
+ void drawRects(const QRect *rects, int rectCount);
+ void drawRects(const QRectF *rects, int rectCount);
+
+ void drawPoints(const QPoint *points, int pointCount);
+ void drawPoints(const QPointF *points, int pointCount);
+
+ void drawEllipse(const QRect &r);
+ void drawEllipse(const QRectF &r);
+
+ virtual void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode);
+ inline void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
+ { QPaintEngine::drawPolygon(points, pointCount, mode); }
+
+ void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
+ void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s);
+ void drawPath(const QPainterPath &path);
+ void drawTextItem(const QPointF &p, const QTextItem &textItem);
+ void drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
+ Qt::ImageConversionFlags flags = Qt::AutoColor);
+
+ virtual Qt::HANDLE handle() const;
+ inline Type type() const { return QPaintEngine::X11; }
+
+ QPainter::RenderHints supportedRenderHints() const;
+
+protected:
+ QX11PaintEngine(QX11PaintEnginePrivate &dptr);
+
+ void drawXLFD(const QPointF &p, const QTextItemInt &si);
+#ifndef QT_NO_FONTCONFIG
+ void drawFreetype(const QPointF &p, const QTextItemInt &si);
+#endif
+
+ friend class QPixmap;
+ friend class QFontEngineBox;
+ friend Q_WIDGETS_EXPORT GC qt_x11_get_pen_gc(QPainter *);
+ friend Q_WIDGETS_EXPORT GC qt_x11_get_brush_gc(QPainter *);
+
+private:
+ Q_DISABLE_COPY(QX11PaintEngine)
+};
+
+class QX11PaintEnginePrivate : public QPaintEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QX11PaintEngine)
+public:
+ QX11PaintEnginePrivate()
+ {
+ scrn = -1;
+ hd = 0;
+ picture = 0;
+ gc = gc_brush = 0;
+ dpy = 0;
+ xinfo = 0;
+ txop = QTransform::TxNone;
+ has_clipping = false;
+ render_hints = 0;
+ xform_scale = 1;
+#ifndef QT_NO_XRENDER
+ tessellator = 0;
+#endif
+ }
+ enum GCMode {
+ PenGC,
+ BrushGC
+ };
+
+ void init();
+ void fillPolygon_translated(const QPointF *points, int pointCount, GCMode gcMode,
+ QPaintEngine::PolygonDrawMode mode);
+ void fillPolygon_dev(const QPointF *points, int pointCount, GCMode gcMode,
+ QPaintEngine::PolygonDrawMode mode);
+ void fillPath(const QPainterPath &path, GCMode gcmode, bool transform);
+ void strokePolygon_dev(const QPointF *points, int pointCount, bool close);
+ void strokePolygon_translated(const QPointF *points, int pointCount, bool close);
+ void setupAdaptedOrigin(const QPoint &p);
+ void resetAdaptedOrigin();
+ void decidePathFallback() {
+ use_path_fallback = has_alpha_brush
+ || has_alpha_pen
+ || has_custom_pen
+ || has_complex_xform
+ || (render_hints & QPainter::Antialiasing);
+ }
+ void decideCoordAdjust() {
+ adjust_coords = !(render_hints & QPainter::Antialiasing)
+ && (has_alpha_pen
+ || (has_alpha_brush && has_pen && !has_alpha_pen)
+ || (cpen.style() > Qt::SolidLine));
+ }
+ void clipPolygon_dev(const QPolygonF &poly, QPolygonF *clipped_poly);
+ void systemStateChanged();
+
+ Display *dpy;
+ int scrn;
+ int pdev_depth;
+ Qt::HANDLE hd;
+ QPixmap brush_pm;
+#if !defined (QT_NO_XRENDER)
+ Qt::HANDLE picture;
+ Qt::HANDLE current_brush;
+ QPixmap bitmap_texture;
+ int composition_mode;
+#else
+ Qt::HANDLE picture;
+#endif
+ GC gc;
+ GC gc_brush;
+
+ QPen cpen;
+ QBrush cbrush;
+ QRegion crgn;
+ QTransform matrix;
+ qreal opacity;
+
+ uint has_complex_xform : 1;
+ uint has_scaling_xform : 1;
+ uint has_non_scaling_xform : 1;
+ uint has_custom_pen : 1;
+ uint use_path_fallback : 1;
+ uint adjust_coords : 1;
+ uint has_clipping : 1;
+ uint adapted_brush_origin : 1;
+ uint adapted_pen_origin : 1;
+ uint has_pen : 1;
+ uint has_brush : 1;
+ uint has_texture : 1;
+ uint has_alpha_texture : 1;
+ uint has_pattern : 1;
+ uint has_alpha_pen : 1;
+ uint has_alpha_brush : 1;
+ uint render_hints;
+
+ const QX11Info *xinfo;
+ QPointF bg_origin;
+ QTransform::TransformationType txop;
+ qreal xform_scale;
+ QPolygonClipper<qt_float_point, qt_float_point, float> polygonClipper;
+
+ int xlibMaxLinePoints;
+#ifndef QT_NO_XRENDER
+ QXRenderTessellator *tessellator;
+#endif
+};
+
+QT_END_NAMESPACE
+
+#endif // QPAINTENGINE_X11_P_H
diff --git a/src/widgets/platforms/x11/qpixmap_x11.cpp b/src/widgets/platforms/x11/qpixmap_x11.cpp
new file mode 100644
index 0000000000..500d1e3ddf
--- /dev/null
+++ b/src/widgets/platforms/x11/qpixmap_x11.cpp
@@ -0,0 +1,2419 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// Uncomment the next line to enable the MIT Shared Memory extension
+//
+// WARNING: This has some problems:
+//
+// 1. Consumes a 800x600 pixmap
+// 2. Qt does not handle the ShmCompletion message, so you will
+// get strange effects if you xForm() repeatedly.
+//
+// #define QT_MITSHM
+
+#if defined(Q_OS_WIN32) && defined(QT_MITSHM)
+#undef QT_MITSHM
+#endif
+
+#include "qplatformdefs.h"
+
+#include "qdebug.h"
+#include "qiodevice.h"
+#include "qpixmap_x11_p.h"
+#include "qbitmap.h"
+#include "qcolormap.h"
+#include "qimage.h"
+#include "qmatrix.h"
+#include "qapplication.h"
+#include <private/qpaintengine_x11_p.h>
+#include <private/qt_x11_p.h>
+#include "qx11info_x11.h"
+#include <private/qdrawhelper_p.h>
+#include <private/qimage_p.h>
+#include <private/qimagepixmapcleanuphooks_p.h>
+
+#include <stdlib.h>
+
+#if defined(Q_CC_MIPS)
+# define for if(0){}else for
+#endif
+
+QT_BEGIN_NAMESPACE
+
+QPixmap qt_toX11Pixmap(const QImage &image)
+{
+ QPlatformPixmap *data =
+ new QX11PlatformPixmap(image.depth() == 1
+ ? QPlatformPixmap::BitmapType
+ : QPlatformPixmap::PixmapType);
+
+ data->fromImage(image, Qt::AutoColor);
+
+ return QPixmap(data);
+}
+
+QPixmap qt_toX11Pixmap(const QPixmap &pixmap)
+{
+ if (pixmap.isNull())
+ return QPixmap();
+
+ if (QPixmap(pixmap).data_ptr()->classId() == QPlatformPixmap::X11Class)
+ return pixmap;
+
+ return qt_toX11Pixmap(pixmap.toImage());
+}
+
+// For thread-safety:
+// image->data does not belong to X11, so we must free it ourselves.
+
+inline static void qSafeXDestroyImage(XImage *x)
+{
+ if (x->data) {
+ free(x->data);
+ x->data = 0;
+ }
+ XDestroyImage(x);
+}
+
+QBitmap QX11PlatformPixmap::mask_to_bitmap(int screen) const
+{
+ if (!x11_mask)
+ return QBitmap();
+ QPixmap::x11SetDefaultScreen(screen);
+ QBitmap bm(w, h);
+ GC gc = XCreateGC(X11->display, bm.handle(), 0, 0);
+ XCopyArea(X11->display, x11_mask, bm.handle(), gc, 0, 0,
+ bm.data->width(), bm.data->height(), 0, 0);
+ XFreeGC(X11->display, gc);
+ return bm;
+}
+
+Qt::HANDLE QX11PlatformPixmap::bitmap_to_mask(const QBitmap &bitmap, int screen)
+{
+ if (bitmap.isNull())
+ return 0;
+ QBitmap bm = bitmap;
+ bm.x11SetScreen(screen);
+
+ Pixmap mask = XCreatePixmap(X11->display, RootWindow(X11->display, screen),
+ bm.data->width(), bm.data->height(), 1);
+ GC gc = XCreateGC(X11->display, mask, 0, 0);
+ XCopyArea(X11->display, bm.handle(), mask, gc, 0, 0,
+ bm.data->width(), bm.data->height(), 0, 0);
+ XFreeGC(X11->display, gc);
+ return mask;
+}
+
+
+/*****************************************************************************
+ MIT Shared Memory Extension support: makes xForm noticeably (~20%) faster.
+ *****************************************************************************/
+
+#if defined(QT_MITSHM)
+
+static bool xshminit = false;
+static XShmSegmentInfo xshminfo;
+static XImage *xshmimg = 0;
+static Pixmap xshmpm = 0;
+
+static void qt_cleanup_mitshm()
+{
+ if (xshmimg == 0)
+ return;
+ Display *dpy = QX11Info::appDisplay();
+ if (xshmpm) {
+ XFreePixmap(dpy, xshmpm);
+ xshmpm = 0;
+ }
+ XShmDetach(dpy, &xshminfo); xshmimg->data = 0;
+ qSafeXDestroyImage(xshmimg); xshmimg = 0;
+ shmdt(xshminfo.shmaddr);
+ shmctl(xshminfo.shmid, IPC_RMID, 0);
+}
+
+static bool qt_create_mitshm_buffer(const QPaintDevice* dev, int w, int h)
+{
+ static int major, minor;
+ static Bool pixmaps_ok;
+ Display *dpy = dev->data->xinfo->display();
+ int dd = dev->x11Depth();
+ Visual *vis = (Visual*)dev->x11Visual();
+
+ if (xshminit) {
+ qt_cleanup_mitshm();
+ } else {
+ if (!XShmQueryVersion(dpy, &major, &minor, &pixmaps_ok))
+ return false; // MIT Shm not supported
+ qAddPostRoutine(qt_cleanup_mitshm);
+ xshminit = true;
+ }
+
+ xshmimg = XShmCreateImage(dpy, vis, dd, ZPixmap, 0, &xshminfo, w, h);
+ if (!xshmimg)
+ return false;
+
+ bool ok;
+ xshminfo.shmid = shmget(IPC_PRIVATE,
+ xshmimg->bytes_per_line * xshmimg->height,
+ IPC_CREAT | 0777);
+ ok = xshminfo.shmid != -1;
+ if (ok) {
+ xshmimg->data = (char*)shmat(xshminfo.shmid, 0, 0);
+ xshminfo.shmaddr = xshmimg->data;
+ ok = (xshminfo.shmaddr != (char*)-1);
+ }
+ xshminfo.readOnly = false;
+ if (ok)
+ ok = XShmAttach(dpy, &xshminfo);
+ if (!ok) {
+ qSafeXDestroyImage(xshmimg);
+ xshmimg = 0;
+ if (xshminfo.shmaddr)
+ shmdt(xshminfo.shmaddr);
+ if (xshminfo.shmid != -1)
+ shmctl(xshminfo.shmid, IPC_RMID, 0);
+ return false;
+ }
+ if (pixmaps_ok)
+ xshmpm = XShmCreatePixmap(dpy, DefaultRootWindow(dpy), xshmimg->data,
+ &xshminfo, w, h, dd);
+
+ return true;
+}
+
+#else
+
+// If extern, need a dummy.
+//
+// static bool qt_create_mitshm_buffer(QPaintDevice*, int, int)
+// {
+// return false;
+// }
+
+#endif // QT_MITSHM
+
+
+/*****************************************************************************
+ Internal functions
+ *****************************************************************************/
+
+extern const uchar *qt_get_bitflip_array(); // defined in qimage.cpp
+
+// Returns position of highest bit set or -1 if none
+static int highest_bit(uint v)
+{
+ int i;
+ uint b = (uint)1 << 31;
+ for (i=31; ((b & v) == 0) && i>=0; i--)
+ b >>= 1;
+ return i;
+}
+
+// Returns position of lowest set bit in 'v' as an integer (0-31), or -1
+static int lowest_bit(uint v)
+{
+ int i;
+ ulong lb;
+ lb = 1;
+ for (i=0; ((v & lb) == 0) && i<32; i++, lb<<=1) {}
+ return i==32 ? -1 : i;
+}
+
+// Counts the number of bits set in 'v'
+static uint n_bits(uint v)
+{
+ int i = 0;
+ while (v) {
+ v = v & (v - 1);
+ i++;
+ }
+ return i;
+}
+
+static uint *red_scale_table = 0;
+static uint *green_scale_table = 0;
+static uint *blue_scale_table = 0;
+
+static void cleanup_scale_tables()
+{
+ delete[] red_scale_table;
+ delete[] green_scale_table;
+ delete[] blue_scale_table;
+}
+
+/*
+ Could do smart bitshifting, but the "obvious" algorithm only works for
+ nBits >= 4. This is more robust.
+*/
+static void build_scale_table(uint **table, uint nBits)
+{
+ if (nBits > 7) {
+ qWarning("build_scale_table: internal error, nBits = %i", nBits);
+ return;
+ }
+ if (!*table) {
+ static bool firstTable = true;
+ if (firstTable) {
+ qAddPostRoutine(cleanup_scale_tables);
+ firstTable = false;
+ }
+ *table = new uint[256];
+ }
+ int maxVal = (1 << nBits) - 1;
+ int valShift = 8 - nBits;
+ int i;
+ for(i = 0 ; i < maxVal + 1 ; i++)
+ (*table)[i << valShift] = i*255/maxVal;
+}
+
+static int defaultScreen = -1;
+
+/*****************************************************************************
+ QPixmap member functions
+ *****************************************************************************/
+
+QBasicAtomicInt qt_pixmap_serial = Q_BASIC_ATOMIC_INITIALIZER(0);
+int Q_WIDGETS_EXPORT qt_x11_preferred_pixmap_depth = 0;
+
+QX11PlatformPixmap::QX11PlatformPixmap(PixelType type)
+ : QPlatformPixmap(type, X11Class), gl_surface(0), hd(0),
+ flags(Uninitialized), x11_mask(0), picture(0), mask_picture(0), hd2(0),
+ share_mode(QPixmap::ImplicitlyShared), pengine(0)
+{
+}
+
+QPlatformPixmap *QX11PlatformPixmap::createCompatiblePlatformPixmap() const
+{
+ return new QX11PlatformPixmap(pixelType());
+}
+
+void QX11PlatformPixmap::resize(int width, int height)
+{
+ setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1));
+
+ w = width;
+ h = height;
+ is_null = (w <= 0 || h <= 0);
+
+ if (defaultScreen >= 0 && defaultScreen != xinfo.screen()) {
+ QX11InfoData* xd = xinfo.getX11Data(true);
+ xd->screen = defaultScreen;
+ xd->depth = QX11Info::appDepth(xd->screen);
+ xd->cells = QX11Info::appCells(xd->screen);
+ xd->colormap = QX11Info::appColormap(xd->screen);
+ xd->defaultColormap = QX11Info::appDefaultColormap(xd->screen);
+ xd->visual = (Visual *)QX11Info::appVisual(xd->screen);
+ xd->defaultVisual = QX11Info::appDefaultVisual(xd->screen);
+ xinfo.setX11Data(xd);
+ }
+
+ int dd = xinfo.depth();
+
+ if (qt_x11_preferred_pixmap_depth)
+ dd = qt_x11_preferred_pixmap_depth;
+
+ bool make_null = w <= 0 || h <= 0; // create null pixmap
+ d = (pixelType() == BitmapType ? 1 : dd);
+ if (make_null || d == 0) {
+ w = 0;
+ h = 0;
+ is_null = true;
+ hd = 0;
+ picture = 0;
+ d = 0;
+ if (!make_null)
+ qWarning("QPixmap: Invalid pixmap parameters");
+ return;
+ }
+ hd = (Qt::HANDLE)XCreatePixmap(X11->display,
+ RootWindow(X11->display, xinfo.screen()),
+ w, h, d);
+#ifndef QT_NO_XRENDER
+ if (X11->use_xrender) {
+ XRenderPictFormat *format = d == 1
+ ? XRenderFindStandardFormat(X11->display, PictStandardA1)
+ : XRenderFindVisualFormat(X11->display, (Visual *)xinfo.visual());
+ picture = XRenderCreatePicture(X11->display, hd, format, 0, 0);
+ }
+#endif // QT_NO_XRENDER
+}
+
+struct QX11AlphaDetector
+{
+ bool hasAlpha() const {
+ if (checked)
+ return has;
+ // Will implicitly also check format and return quickly for opaque types...
+ checked = true;
+ has = image->isNull() ? false : const_cast<QImage *>(image)->data_ptr()->checkForAlphaPixels();
+ return has;
+ }
+
+ bool hasXRenderAndAlpha() const {
+ if (!X11->use_xrender)
+ return false;
+ return hasAlpha();
+ }
+
+ QX11AlphaDetector(const QImage *i, Qt::ImageConversionFlags flags)
+ : image(i), checked(false), has(false)
+ {
+ if (flags & Qt::NoOpaqueDetection) {
+ checked = true;
+ has = image->hasAlphaChannel();
+ }
+ }
+
+ const QImage *image;
+ mutable bool checked;
+ mutable bool has;
+};
+
+void QX11PlatformPixmap::fromImage(const QImage &img,
+ Qt::ImageConversionFlags flags)
+{
+ setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1));
+
+ w = img.width();
+ h = img.height();
+ d = img.depth();
+ is_null = (w <= 0 || h <= 0);
+
+ if (is_null) {
+ w = h = 0;
+ return;
+ }
+
+ if (defaultScreen >= 0 && defaultScreen != xinfo.screen()) {
+ QX11InfoData* xd = xinfo.getX11Data(true);
+ xd->screen = defaultScreen;
+ xd->depth = QX11Info::appDepth(xd->screen);
+ xd->cells = QX11Info::appCells(xd->screen);
+ xd->colormap = QX11Info::appColormap(xd->screen);
+ xd->defaultColormap = QX11Info::appDefaultColormap(xd->screen);
+ xd->visual = (Visual *)QX11Info::appVisual(xd->screen);
+ xd->defaultVisual = QX11Info::appDefaultVisual(xd->screen);
+ xinfo.setX11Data(xd);
+ }
+
+ if (pixelType() == BitmapType) {
+ bitmapFromImage(img);
+ return;
+ }
+
+ if (uint(w) >= 32768 || uint(h) >= 32768) {
+ w = h = 0;
+ is_null = true;
+ return;
+ }
+
+ QX11AlphaDetector alphaCheck(&img, flags);
+ int dd = alphaCheck.hasXRenderAndAlpha() ? 32 : xinfo.depth();
+
+ if (qt_x11_preferred_pixmap_depth)
+ dd = qt_x11_preferred_pixmap_depth;
+
+ QImage image = img;
+
+ // must be monochrome
+ if (dd == 1 || (flags & Qt::ColorMode_Mask) == Qt::MonoOnly) {
+ if (d != 1) {
+ // dither
+ image = image.convertToFormat(QImage::Format_MonoLSB, flags);
+ d = 1;
+ }
+ } else { // can be both
+ bool conv8 = false;
+ if (d > 8 && dd <= 8) { // convert to 8 bit
+ if ((flags & Qt::DitherMode_Mask) == Qt::AutoDither)
+ flags = (flags & ~Qt::DitherMode_Mask)
+ | Qt::PreferDither;
+ conv8 = true;
+ } else if ((flags & Qt::ColorMode_Mask) == Qt::ColorOnly) {
+ conv8 = (d == 1); // native depth wanted
+ } else if (d == 1) {
+ if (image.colorCount() == 2) {
+ QRgb c0 = image.color(0); // Auto: convert to best
+ QRgb c1 = image.color(1);
+ conv8 = qMin(c0,c1) != qRgb(0,0,0) || qMax(c0,c1) != qRgb(255,255,255);
+ } else {
+ // eg. 1-color monochrome images (they do exist).
+ conv8 = true;
+ }
+ }
+ if (conv8) {
+ image = image.convertToFormat(QImage::Format_Indexed8, flags);
+ d = 8;
+ }
+ }
+
+ if (d == 1 || d == 16 || d == 24) {
+ image = image.convertToFormat(QImage::Format_RGB32, flags);
+ fromImage(image, Qt::AutoColor);
+ return;
+ }
+
+ Display *dpy = X11->display;
+ Visual *visual = (Visual *)xinfo.visual();
+ XImage *xi = 0;
+ bool trucol = (visual->c_class >= TrueColor);
+ int nbytes = image.byteCount();
+ uchar *newbits= 0;
+
+#ifndef QT_NO_XRENDER
+ if (alphaCheck.hasXRenderAndAlpha()) {
+ const QImage &cimage = image;
+
+ d = 32;
+
+ if (QX11Info::appDepth() != d) {
+ if (xinfo.x11data) {
+ xinfo.x11data->depth = d;
+ } else {
+ QX11InfoData *xd = xinfo.getX11Data(true);
+ xd->screen = QX11Info::appScreen();
+ xd->depth = d;
+ xd->cells = QX11Info::appCells();
+ xd->colormap = QX11Info::appColormap();
+ xd->defaultColormap = QX11Info::appDefaultColormap();
+ xd->visual = (Visual *)QX11Info::appVisual();
+ xd->defaultVisual = QX11Info::appDefaultVisual();
+ xinfo.setX11Data(xd);
+ }
+ }
+
+ hd = (Qt::HANDLE)XCreatePixmap(dpy, RootWindow(dpy, xinfo.screen()),
+ w, h, d);
+ picture = XRenderCreatePicture(X11->display, hd,
+ XRenderFindStandardFormat(X11->display, PictStandardARGB32), 0, 0);
+
+ xi = XCreateImage(dpy, visual, d, ZPixmap, 0, 0, w, h, 32, 0);
+ Q_CHECK_PTR(xi);
+ newbits = (uchar *)malloc(xi->bytes_per_line*h);
+ Q_CHECK_PTR(newbits);
+ xi->data = (char *)newbits;
+
+ switch(cimage.format()) {
+ case QImage::Format_Indexed8: {
+ QVector<QRgb> colorTable = cimage.colorTable();
+ uint *xidata = (uint *)xi->data;
+ for (int y = 0; y < h; ++y) {
+ const uchar *p = cimage.scanLine(y);
+ for (int x = 0; x < w; ++x) {
+ const QRgb rgb = colorTable[p[x]];
+ const int a = qAlpha(rgb);
+ if (a == 0xff)
+ *xidata = rgb;
+ else
+ // RENDER expects premultiplied alpha
+ *xidata = qRgba(qt_div_255(qRed(rgb) * a),
+ qt_div_255(qGreen(rgb) * a),
+ qt_div_255(qBlue(rgb) * a),
+ a);
+ ++xidata;
+ }
+ }
+ }
+ break;
+ case QImage::Format_RGB32: {
+ uint *xidata = (uint *)xi->data;
+ for (int y = 0; y < h; ++y) {
+ const QRgb *p = (const QRgb *) cimage.scanLine(y);
+ for (int x = 0; x < w; ++x)
+ *xidata++ = p[x] | 0xff000000;
+ }
+ }
+ break;
+ case QImage::Format_ARGB32: {
+ uint *xidata = (uint *)xi->data;
+ for (int y = 0; y < h; ++y) {
+ const QRgb *p = (const QRgb *) cimage.scanLine(y);
+ for (int x = 0; x < w; ++x) {
+ const QRgb rgb = p[x];
+ const int a = qAlpha(rgb);
+ if (a == 0xff)
+ *xidata = rgb;
+ else
+ // RENDER expects premultiplied alpha
+ *xidata = qRgba(qt_div_255(qRed(rgb) * a),
+ qt_div_255(qGreen(rgb) * a),
+ qt_div_255(qBlue(rgb) * a),
+ a);
+ ++xidata;
+ }
+ }
+
+ }
+ break;
+ case QImage::Format_ARGB32_Premultiplied: {
+ uint *xidata = (uint *)xi->data;
+ for (int y = 0; y < h; ++y) {
+ const QRgb *p = (const QRgb *) cimage.scanLine(y);
+ memcpy(xidata, p, w*sizeof(QRgb));
+ xidata += w;
+ }
+ }
+ break;
+ default:
+ Q_ASSERT(false);
+ }
+
+ if ((xi->byte_order == MSBFirst) != (QSysInfo::ByteOrder == QSysInfo::BigEndian)) {
+ uint *xidata = (uint *)xi->data;
+ uint *xiend = xidata + w*h;
+ while (xidata < xiend) {
+ *xidata = (*xidata >> 24)
+ | ((*xidata >> 8) & 0xff00)
+ | ((*xidata << 8) & 0xff0000)
+ | (*xidata << 24);
+ ++xidata;
+ }
+ }
+
+ GC gc = XCreateGC(dpy, hd, 0, 0);
+ XPutImage(dpy, hd, gc, xi, 0, 0, 0, 0, w, h);
+ XFreeGC(dpy, gc);
+
+ qSafeXDestroyImage(xi);
+
+ return;
+ }
+#endif // QT_NO_XRENDER
+
+ if (trucol) { // truecolor display
+ if (image.format() == QImage::Format_ARGB32_Premultiplied)
+ image = image.convertToFormat(QImage::Format_ARGB32);
+
+ const QImage &cimage = image;
+ QRgb pix[256]; // pixel translation table
+ const bool d8 = (d == 8);
+ const uint red_mask = (uint)visual->red_mask;
+ const uint green_mask = (uint)visual->green_mask;
+ const uint blue_mask = (uint)visual->blue_mask;
+ const int red_shift = highest_bit(red_mask) - 7;
+ const int green_shift = highest_bit(green_mask) - 7;
+ const int blue_shift = highest_bit(blue_mask) - 7;
+ const uint rbits = highest_bit(red_mask) - lowest_bit(red_mask) + 1;
+ const uint gbits = highest_bit(green_mask) - lowest_bit(green_mask) + 1;
+ const uint bbits = highest_bit(blue_mask) - lowest_bit(blue_mask) + 1;
+
+ if (d8) { // setup pixel translation
+ QVector<QRgb> ctable = cimage.colorTable();
+ for (int i=0; i < cimage.colorCount(); i++) {
+ int r = qRed (ctable[i]);
+ int g = qGreen(ctable[i]);
+ int b = qBlue (ctable[i]);
+ r = red_shift > 0 ? r << red_shift : r >> -red_shift;
+ g = green_shift > 0 ? g << green_shift : g >> -green_shift;
+ b = blue_shift > 0 ? b << blue_shift : b >> -blue_shift;
+ pix[i] = (b & blue_mask) | (g & green_mask) | (r & red_mask)
+ | ~(blue_mask | green_mask | red_mask);
+ }
+ }
+
+ xi = XCreateImage(dpy, visual, dd, ZPixmap, 0, 0, w, h, 32, 0);
+ Q_CHECK_PTR(xi);
+ newbits = (uchar *)malloc(xi->bytes_per_line*h);
+ Q_CHECK_PTR(newbits);
+ if (!newbits) // no memory
+ return;
+ int bppc = xi->bits_per_pixel;
+
+ bool contig_bits = n_bits(red_mask) == rbits &&
+ n_bits(green_mask) == gbits &&
+ n_bits(blue_mask) == bbits;
+ bool dither_tc =
+ // Want it?
+ (flags & Qt::Dither_Mask) != Qt::ThresholdDither &&
+ (flags & Qt::DitherMode_Mask) != Qt::AvoidDither &&
+ // Need it?
+ bppc < 24 && !d8 &&
+ // Can do it? (Contiguous bits?)
+ contig_bits;
+
+ static bool init=false;
+ static int D[16][16];
+ if (dither_tc && !init) {
+ // I also contributed this code to XV - WWA.
+ /*
+ The dither matrix, D, is obtained with this formula:
+
+ D2 = [0 2]
+ [3 1]
+
+
+ D2*n = [4*Dn 4*Dn+2*Un]
+ [4*Dn+3*Un 4*Dn+1*Un]
+ */
+ int n,i,j;
+ init=1;
+
+ /* Set D2 */
+ D[0][0]=0;
+ D[1][0]=2;
+ D[0][1]=3;
+ D[1][1]=1;
+
+ /* Expand using recursive definition given above */
+ for (n=2; n<16; n*=2) {
+ for (i=0; i<n; i++) {
+ for (j=0; j<n; j++) {
+ D[i][j]*=4;
+ D[i+n][j]=D[i][j]+2;
+ D[i][j+n]=D[i][j]+3;
+ D[i+n][j+n]=D[i][j]+1;
+ }
+ }
+ }
+ init=true;
+ }
+
+ enum { BPP8,
+ BPP16_565, BPP16_555,
+ BPP16_MSB, BPP16_LSB,
+ BPP24_888,
+ BPP24_MSB, BPP24_LSB,
+ BPP32_8888,
+ BPP32_MSB, BPP32_LSB
+ } mode = BPP8;
+
+ bool same_msb_lsb = (xi->byte_order == MSBFirst) == (QSysInfo::ByteOrder == QSysInfo::BigEndian);
+
+ if(bppc == 8) // 8 bit
+ mode = BPP8;
+ else if(bppc == 16) { // 16 bit MSB/LSB
+ if(red_shift == 8 && green_shift == 3 && blue_shift == -3 && !d8 && same_msb_lsb)
+ mode = BPP16_565;
+ else if(red_shift == 7 && green_shift == 2 && blue_shift == -3 && !d8 && same_msb_lsb)
+ mode = BPP16_555;
+ else
+ mode = (xi->byte_order == LSBFirst) ? BPP16_LSB : BPP16_MSB;
+ } else if(bppc == 24) { // 24 bit MSB/LSB
+ if (red_shift == 16 && green_shift == 8 && blue_shift == 0 && !d8 && same_msb_lsb)
+ mode = BPP24_888;
+ else
+ mode = (xi->byte_order == LSBFirst) ? BPP24_LSB : BPP24_MSB;
+ } else if(bppc == 32) { // 32 bit MSB/LSB
+ if(red_shift == 16 && green_shift == 8 && blue_shift == 0 && !d8 && same_msb_lsb)
+ mode = BPP32_8888;
+ else
+ mode = (xi->byte_order == LSBFirst) ? BPP32_LSB : BPP32_MSB;
+ } else
+ qFatal("Logic error 3");
+
+#define GET_PIXEL \
+ uint pixel; \
+ if (d8) pixel = pix[*src++]; \
+ else { \
+ int r = qRed (*p); \
+ int g = qGreen(*p); \
+ int b = qBlue (*p++); \
+ r = red_shift > 0 \
+ ? r << red_shift : r >> -red_shift; \
+ g = green_shift > 0 \
+ ? g << green_shift : g >> -green_shift; \
+ b = blue_shift > 0 \
+ ? b << blue_shift : b >> -blue_shift; \
+ pixel = (r & red_mask)|(g & green_mask) | (b & blue_mask) \
+ | ~(blue_mask | green_mask | red_mask); \
+ }
+
+#define GET_PIXEL_DITHER_TC \
+ int r = qRed (*p); \
+ int g = qGreen(*p); \
+ int b = qBlue (*p++); \
+ const int thres = D[x%16][y%16]; \
+ if (r <= (255-(1<<(8-rbits))) && ((r<<rbits) & 255) \
+ > thres) \
+ r += (1<<(8-rbits)); \
+ if (g <= (255-(1<<(8-gbits))) && ((g<<gbits) & 255) \
+ > thres) \
+ g += (1<<(8-gbits)); \
+ if (b <= (255-(1<<(8-bbits))) && ((b<<bbits) & 255) \
+ > thres) \
+ b += (1<<(8-bbits)); \
+ r = red_shift > 0 \
+ ? r << red_shift : r >> -red_shift; \
+ g = green_shift > 0 \
+ ? g << green_shift : g >> -green_shift; \
+ b = blue_shift > 0 \
+ ? b << blue_shift : b >> -blue_shift; \
+ uint pixel = (r & red_mask)|(g & green_mask) | (b & blue_mask);
+
+// again, optimized case
+// can't be optimized that much :(
+#define GET_PIXEL_DITHER_TC_OPT(red_shift,green_shift,blue_shift,red_mask,green_mask,blue_mask, \
+ rbits,gbits,bbits) \
+ const int thres = D[x%16][y%16]; \
+ int r = qRed (*p); \
+ if (r <= (255-(1<<(8-rbits))) && ((r<<rbits) & 255) \
+ > thres) \
+ r += (1<<(8-rbits)); \
+ int g = qGreen(*p); \
+ if (g <= (255-(1<<(8-gbits))) && ((g<<gbits) & 255) \
+ > thres) \
+ g += (1<<(8-gbits)); \
+ int b = qBlue (*p++); \
+ if (b <= (255-(1<<(8-bbits))) && ((b<<bbits) & 255) \
+ > thres) \
+ b += (1<<(8-bbits)); \
+ uint pixel = ((r red_shift) & red_mask) \
+ | ((g green_shift) & green_mask) \
+ | ((b blue_shift) & blue_mask);
+
+#define CYCLE(body) \
+ for (int y=0; y<h; y++) { \
+ const uchar* src = cimage.scanLine(y); \
+ uchar* dst = newbits + xi->bytes_per_line*y; \
+ const QRgb* p = (const QRgb *)src; \
+ body \
+ }
+
+ if (dither_tc) {
+ switch (mode) {
+ case BPP16_565:
+ CYCLE(
+ quint16* dst16 = (quint16*)dst;
+ for (int x=0; x<w; x++) {
+ GET_PIXEL_DITHER_TC_OPT(<<8,<<3,>>3,0xf800,0x7e0,0x1f,5,6,5)
+ *dst16++ = pixel;
+ }
+ )
+ break;
+ case BPP16_555:
+ CYCLE(
+ quint16* dst16 = (quint16*)dst;
+ for (int x=0; x<w; x++) {
+ GET_PIXEL_DITHER_TC_OPT(<<7,<<2,>>3,0x7c00,0x3e0,0x1f,5,5,5)
+ *dst16++ = pixel;
+ }
+ )
+ break;
+ case BPP16_MSB: // 16 bit MSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL_DITHER_TC
+ *dst++ = (pixel >> 8);
+ *dst++ = pixel;
+ }
+ )
+ break;
+ case BPP16_LSB: // 16 bit LSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL_DITHER_TC
+ *dst++ = pixel;
+ *dst++ = pixel >> 8;
+ }
+ )
+ break;
+ default:
+ qFatal("Logic error");
+ }
+ } else {
+ switch (mode) {
+ case BPP8: // 8 bit
+ CYCLE(
+ Q_UNUSED(p);
+ for (int x=0; x<w; x++)
+ *dst++ = pix[*src++];
+ )
+ break;
+ case BPP16_565:
+ CYCLE(
+ quint16* dst16 = (quint16*)dst;
+ for (int x = 0; x < w; x++) {
+ *dst16++ = ((*p >> 8) & 0xf800)
+ | ((*p >> 5) & 0x7e0)
+ | ((*p >> 3) & 0x1f);
+ ++p;
+ }
+ )
+ break;
+ case BPP16_555:
+ CYCLE(
+ quint16* dst16 = (quint16*)dst;
+ for (int x=0; x<w; x++) {
+ *dst16++ = ((*p >> 9) & 0x7c00)
+ | ((*p >> 6) & 0x3e0)
+ | ((*p >> 3) & 0x1f);
+ ++p;
+ }
+ )
+ break;
+ case BPP16_MSB: // 16 bit MSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL
+ *dst++ = (pixel >> 8);
+ *dst++ = pixel;
+ }
+ )
+ break;
+ case BPP16_LSB: // 16 bit LSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL
+ *dst++ = pixel;
+ *dst++ = pixel >> 8;
+ }
+ )
+ break;
+ case BPP24_888: // 24 bit MSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ *dst++ = qRed (*p);
+ *dst++ = qGreen(*p);
+ *dst++ = qBlue (*p++);
+ }
+ )
+ break;
+ case BPP24_MSB: // 24 bit MSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL
+ *dst++ = pixel >> 16;
+ *dst++ = pixel >> 8;
+ *dst++ = pixel;
+ }
+ )
+ break;
+ case BPP24_LSB: // 24 bit LSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL
+ *dst++ = pixel;
+ *dst++ = pixel >> 8;
+ *dst++ = pixel >> 16;
+ }
+ )
+ break;
+ case BPP32_8888:
+ CYCLE(
+ memcpy(dst, p, w * 4);
+ )
+ break;
+ case BPP32_MSB: // 32 bit MSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL
+ *dst++ = pixel >> 24;
+ *dst++ = pixel >> 16;
+ *dst++ = pixel >> 8;
+ *dst++ = pixel;
+ }
+ )
+ break;
+ case BPP32_LSB: // 32 bit LSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL
+ *dst++ = pixel;
+ *dst++ = pixel >> 8;
+ *dst++ = pixel >> 16;
+ *dst++ = pixel >> 24;
+ }
+ )
+ break;
+ default:
+ qFatal("Logic error 2");
+ }
+ }
+ xi->data = (char *)newbits;
+ }
+
+ if (d == 8 && !trucol) { // 8 bit pixmap
+ int pop[256]; // pixel popularity
+
+ if (image.colorCount() == 0)
+ image.setColorCount(1);
+
+ const QImage &cimage = image;
+ memset(pop, 0, sizeof(int)*256); // reset popularity array
+ for (int i = 0; i < h; i++) { // for each scanline...
+ const uchar* p = cimage.scanLine(i);
+ const uchar *end = p + w;
+ while (p < end) // compute popularity
+ pop[*p++]++;
+ }
+
+ newbits = (uchar *)malloc(nbytes); // copy image into newbits
+ Q_CHECK_PTR(newbits);
+ if (!newbits) // no memory
+ return;
+ uchar* p = newbits;
+ memcpy(p, cimage.bits(), nbytes); // copy image data into newbits
+
+ /*
+ * The code below picks the most important colors. It is based on the
+ * diversity algorithm, implemented in XV 3.10. XV is (C) by John Bradley.
+ */
+
+ struct PIX { // pixel sort element
+ uchar r,g,b,n; // color + pad
+ int use; // popularity
+ int index; // index in colormap
+ int mindist;
+ };
+ int ncols = 0;
+ for (int i=0; i< cimage.colorCount(); i++) { // compute number of colors
+ if (pop[i] > 0)
+ ncols++;
+ }
+ for (int i = cimage.colorCount(); i < 256; i++) // ignore out-of-range pixels
+ pop[i] = 0;
+
+ // works since we make sure above to have at least
+ // one color in the image
+ if (ncols == 0)
+ ncols = 1;
+
+ PIX pixarr[256]; // pixel array
+ PIX pixarr_sorted[256]; // pixel array (sorted)
+ memset(pixarr, 0, ncols*sizeof(PIX));
+ PIX *px = &pixarr[0];
+ int maxpop = 0;
+ int maxpix = 0;
+ uint j = 0;
+ QVector<QRgb> ctable = cimage.colorTable();
+ for (int i = 0; i < 256; i++) { // init pixel array
+ if (pop[i] > 0) {
+ px->r = qRed (ctable[i]);
+ px->g = qGreen(ctable[i]);
+ px->b = qBlue (ctable[i]);
+ px->n = 0;
+ px->use = pop[i];
+ if (pop[i] > maxpop) { // select most popular entry
+ maxpop = pop[i];
+ maxpix = j;
+ }
+ px->index = i;
+ px->mindist = 1000000;
+ px++;
+ j++;
+ }
+ }
+ pixarr_sorted[0] = pixarr[maxpix];
+ pixarr[maxpix].use = 0;
+
+ for (int i = 1; i < ncols; i++) { // sort pixels
+ int minpix = -1, mindist = -1;
+ px = &pixarr_sorted[i-1];
+ int r = px->r;
+ int g = px->g;
+ int b = px->b;
+ int dist;
+ if ((i & 1) || i<10) { // sort on max distance
+ for (int j=0; j<ncols; j++) {
+ px = &pixarr[j];
+ if (px->use) {
+ dist = (px->r - r)*(px->r - r) +
+ (px->g - g)*(px->g - g) +
+ (px->b - b)*(px->b - b);
+ if (px->mindist > dist)
+ px->mindist = dist;
+ if (px->mindist > mindist) {
+ mindist = px->mindist;
+ minpix = j;
+ }
+ }
+ }
+ } else { // sort on max popularity
+ for (int j=0; j<ncols; j++) {
+ px = &pixarr[j];
+ if (px->use) {
+ dist = (px->r - r)*(px->r - r) +
+ (px->g - g)*(px->g - g) +
+ (px->b - b)*(px->b - b);
+ if (px->mindist > dist)
+ px->mindist = dist;
+ if (px->use > mindist) {
+ mindist = px->use;
+ minpix = j;
+ }
+ }
+ }
+ }
+ pixarr_sorted[i] = pixarr[minpix];
+ pixarr[minpix].use = 0;
+ }
+
+ QColormap cmap = QColormap::instance(xinfo.screen());
+ uint pix[256]; // pixel translation table
+ px = &pixarr_sorted[0];
+ for (int i = 0; i < ncols; i++) { // allocate colors
+ QColor c(px->r, px->g, px->b);
+ pix[px->index] = cmap.pixel(c);
+ px++;
+ }
+
+ p = newbits;
+ for (int i = 0; i < nbytes; i++) { // translate pixels
+ *p = pix[*p];
+ p++;
+ }
+ }
+
+ if (!xi) { // X image not created
+ xi = XCreateImage(dpy, visual, dd, ZPixmap, 0, 0, w, h, 32, 0);
+ if (xi->bits_per_pixel == 16) { // convert 8 bpp ==> 16 bpp
+ ushort *p2;
+ int p2inc = xi->bytes_per_line/sizeof(ushort);
+ ushort *newerbits = (ushort *)malloc(xi->bytes_per_line * h);
+ Q_CHECK_PTR(newerbits);
+ if (!newerbits) // no memory
+ return;
+ uchar* p = newbits;
+ for (int y = 0; y < h; y++) { // OOPS: Do right byte order!!
+ p2 = newerbits + p2inc*y;
+ for (int x = 0; x < w; x++)
+ *p2++ = *p++;
+ }
+ free(newbits);
+ newbits = (uchar *)newerbits;
+ } else if (xi->bits_per_pixel != 8) {
+ qWarning("QPixmap::fromImage: Display not supported "
+ "(bpp=%d)", xi->bits_per_pixel);
+ }
+ xi->data = (char *)newbits;
+ }
+
+ hd = (Qt::HANDLE)XCreatePixmap(X11->display,
+ RootWindow(X11->display, xinfo.screen()),
+ w, h, dd);
+
+ GC gc = XCreateGC(dpy, hd, 0, 0);
+ XPutImage(dpy, hd, gc, xi, 0, 0, 0, 0, w, h);
+ XFreeGC(dpy, gc);
+
+ qSafeXDestroyImage(xi);
+ d = dd;
+
+#ifndef QT_NO_XRENDER
+ if (X11->use_xrender) {
+ XRenderPictFormat *format = d == 1
+ ? XRenderFindStandardFormat(X11->display, PictStandardA1)
+ : XRenderFindVisualFormat(X11->display, (Visual *)xinfo.visual());
+ picture = XRenderCreatePicture(X11->display, hd, format, 0, 0);
+ }
+#endif
+
+ if (alphaCheck.hasAlpha()) {
+ QBitmap m = QBitmap::fromImage(image.createAlphaMask(flags));
+ setMask(m);
+ }
+}
+
+Qt::HANDLE QX11PlatformPixmap::createBitmapFromImage(const QImage &image)
+{
+ QImage img = image.convertToFormat(QImage::Format_MonoLSB);
+ const QRgb c0 = QColor(Qt::black).rgb();
+ const QRgb c1 = QColor(Qt::white).rgb();
+ if (img.color(0) == c0 && img.color(1) == c1) {
+ img.invertPixels();
+ img.setColor(0, c1);
+ img.setColor(1, c0);
+ }
+
+ char *bits;
+ uchar *tmp_bits;
+ int w = img.width();
+ int h = img.height();
+ int bpl = (w + 7) / 8;
+ int ibpl = img.bytesPerLine();
+ if (bpl != ibpl) {
+ tmp_bits = new uchar[bpl*h];
+ bits = (char *)tmp_bits;
+ uchar *p, *b;
+ int y;
+ b = tmp_bits;
+ p = img.scanLine(0);
+ for (y = 0; y < h; y++) {
+ memcpy(b, p, bpl);
+ b += bpl;
+ p += ibpl;
+ }
+ } else {
+ bits = (char *)img.bits();
+ tmp_bits = 0;
+ }
+ Qt::HANDLE hd = (Qt::HANDLE)XCreateBitmapFromData(X11->display,
+ QX11Info::appRootWindow(),
+ bits, w, h);
+ if (tmp_bits) // Avoid purify complaint
+ delete [] tmp_bits;
+ return hd;
+}
+
+void QX11PlatformPixmap::bitmapFromImage(const QImage &image)
+{
+ w = image.width();
+ h = image.height();
+ d = 1;
+ is_null = (w <= 0 || h <= 0);
+ hd = createBitmapFromImage(image);
+#ifndef QT_NO_XRENDER
+ if (X11->use_xrender)
+ picture = XRenderCreatePicture(X11->display, hd,
+ XRenderFindStandardFormat(X11->display, PictStandardA1), 0, 0);
+#endif // QT_NO_XRENDER
+}
+
+void QX11PlatformPixmap::fill(const QColor &fillColor)
+{
+ if (fillColor.alpha() != 255) {
+#ifndef QT_NO_XRENDER
+ if (X11->use_xrender) {
+ if (!picture || d != 32)
+ convertToARGB32(/*preserveContents = */false);
+
+ ::Picture src = X11->getSolidFill(xinfo.screen(), fillColor);
+ XRenderComposite(X11->display, PictOpSrc, src, 0, picture,
+ 0, 0, width(), height(),
+ 0, 0, width(), height());
+ } else
+#endif
+ {
+ QImage im(width(), height(), QImage::Format_ARGB32_Premultiplied);
+ im.fill(PREMUL(fillColor.rgba()));
+ release();
+ fromImage(im, Qt::AutoColor | Qt::OrderedAlphaDither);
+ }
+ return;
+ }
+
+ GC gc = XCreateGC(X11->display, hd, 0, 0);
+ if (depth() == 1) {
+ XSetForeground(X11->display, gc, qGray(fillColor.rgb()) > 127 ? 0 : 1);
+ } else if (X11->use_xrender && d >= 24) {
+ XSetForeground(X11->display, gc, fillColor.rgba());
+ } else {
+ XSetForeground(X11->display, gc,
+ QColormap::instance(xinfo.screen()).pixel(fillColor));
+ }
+ XFillRectangle(X11->display, hd, gc, 0, 0, width(), height());
+ XFreeGC(X11->display, gc);
+}
+
+QX11PlatformPixmap::~QX11PlatformPixmap()
+{
+ // Cleanup hooks have to be called before the handles are freed
+ if (is_cached) {
+ QImagePixmapCleanupHooks::executePlatformPixmapDestructionHooks(this);
+ is_cached = false;
+ }
+
+ release();
+}
+
+void QX11PlatformPixmap::release()
+{
+ delete pengine;
+ pengine = 0;
+
+ if (!X11) {
+ // At this point, the X server will already have freed our resources,
+ // so there is nothing to do.
+ return;
+ }
+
+ if (x11_mask) {
+#ifndef QT_NO_XRENDER
+ if (mask_picture)
+ XRenderFreePicture(X11->display, mask_picture);
+ mask_picture = 0;
+#endif
+ XFreePixmap(X11->display, x11_mask);
+ x11_mask = 0;
+ }
+
+ if (hd) {
+#ifndef QT_NO_XRENDER
+ if (picture) {
+ XRenderFreePicture(X11->display, picture);
+ picture = 0;
+ }
+#endif // QT_NO_XRENDER
+
+ if (hd2) {
+ XFreePixmap(xinfo.display(), hd2);
+ hd2 = 0;
+ }
+ if (!(flags & Readonly))
+ XFreePixmap(xinfo.display(), hd);
+ hd = 0;
+ }
+}
+
+QPixmap QX11PlatformPixmap::alphaChannel() const
+{
+ if (!hasAlphaChannel()) {
+ QPixmap pm(w, h);
+ pm.fill(Qt::white);
+ return pm;
+ }
+ QImage im(toImage());
+ return QPixmap::fromImage(im.alphaChannel(), Qt::OrderedDither);
+}
+
+void QX11PlatformPixmap::setAlphaChannel(const QPixmap &alpha)
+{
+ QImage image(toImage());
+ image.setAlphaChannel(alpha.toImage());
+ release();
+ fromImage(image, Qt::OrderedDither | Qt::OrderedAlphaDither);
+}
+
+
+QBitmap QX11PlatformPixmap::mask() const
+{
+ QBitmap mask;
+#ifndef QT_NO_XRENDER
+ if (picture && d == 32) {
+ // #### slow - there must be a better way..
+ mask = QBitmap::fromImage(toImage().createAlphaMask());
+ } else
+#endif
+ if (d == 1) {
+ QX11PlatformPixmap *that = const_cast<QX11PlatformPixmap*>(this);
+ mask = QPixmap(that);
+ } else {
+ mask = mask_to_bitmap(xinfo.screen());
+ }
+ return mask;
+}
+
+/*!
+ Sets a mask bitmap.
+
+ The \a newmask bitmap defines the clip mask for this pixmap. Every
+ pixel in \a newmask corresponds to a pixel in this pixmap. Pixel
+ value 1 means opaque and pixel value 0 means transparent. The mask
+ must have the same size as this pixmap.
+
+ \warning Setting the mask on a pixmap will cause any alpha channel
+ data to be cleared. For example:
+ \snippet doc/src/snippets/image/image.cpp 2
+ Now, alpha and alphacopy are visually different.
+
+ Setting a null mask resets the mask.
+
+ The effect of this function is undefined when the pixmap is being
+ painted on.
+
+ \sa mask(), {QPixmap#Pixmap Transformations}{Pixmap
+ Transformations}, QBitmap
+*/
+void QX11PlatformPixmap::setMask(const QBitmap &newmask)
+{
+ if (newmask.isNull()) { // clear mask
+#ifndef QT_NO_XRENDER
+ if (picture && d == 32) {
+ QX11PlatformPixmap newData(pixelType());
+ newData.resize(w, h);
+ newData.fill(Qt::black);
+ XRenderComposite(X11->display, PictOpOver,
+ picture, 0, newData.picture,
+ 0, 0, 0, 0, 0, 0, w, h);
+ release();
+ *this = newData;
+ // the new QX11PlatformPixmap object isn't referenced yet, so
+ // ref it
+ ref.ref();
+
+ // the below is to make sure the QX11PlatformPixmap destructor
+ // doesn't delete our newly created render picture
+ newData.hd = 0;
+ newData.x11_mask = 0;
+ newData.picture = 0;
+ newData.mask_picture = 0;
+ newData.hd2 = 0;
+ } else
+#endif
+ if (x11_mask) {
+#ifndef QT_NO_XRENDER
+ if (picture) {
+ XRenderPictureAttributes attrs;
+ attrs.alpha_map = 0;
+ XRenderChangePicture(X11->display, picture, CPAlphaMap,
+ &attrs);
+ }
+ if (mask_picture)
+ XRenderFreePicture(X11->display, mask_picture);
+ mask_picture = 0;
+#endif
+ XFreePixmap(X11->display, x11_mask);
+ x11_mask = 0;
+ }
+ return;
+ }
+
+#ifndef QT_NO_XRENDER
+ if (picture && d == 32) {
+ XRenderComposite(X11->display, PictOpSrc,
+ picture, newmask.x11PictureHandle(),
+ picture, 0, 0, 0, 0, 0, 0, w, h);
+ } else
+#endif
+ if (depth() == 1) {
+ XGCValues vals;
+ vals.function = GXand;
+ GC gc = XCreateGC(X11->display, hd, GCFunction, &vals);
+ XCopyArea(X11->display, newmask.handle(), hd, gc, 0, 0,
+ width(), height(), 0, 0);
+ XFreeGC(X11->display, gc);
+ } else {
+ // ##### should or the masks together
+ if (x11_mask) {
+ XFreePixmap(X11->display, x11_mask);
+#ifndef QT_NO_XRENDER
+ if (mask_picture)
+ XRenderFreePicture(X11->display, mask_picture);
+#endif
+ }
+ x11_mask = QX11PlatformPixmap::bitmap_to_mask(newmask, xinfo.screen());
+#ifndef QT_NO_XRENDER
+ if (picture) {
+ mask_picture = XRenderCreatePicture(X11->display, x11_mask,
+ XRenderFindStandardFormat(X11->display, PictStandardA1), 0, 0);
+ XRenderPictureAttributes attrs;
+ attrs.alpha_map = mask_picture;
+ XRenderChangePicture(X11->display, picture, CPAlphaMap, &attrs);
+ }
+#endif
+ }
+}
+
+int QX11PlatformPixmap::metric(QPaintDevice::PaintDeviceMetric metric) const
+{
+ switch (metric) {
+ case QPaintDevice::PdmWidth:
+ return w;
+ case QPaintDevice::PdmHeight:
+ return h;
+ case QPaintDevice::PdmNumColors:
+ return 1 << d;
+ case QPaintDevice::PdmDepth:
+ return d;
+ case QPaintDevice::PdmWidthMM: {
+ const int screen = xinfo.screen();
+ const int mm = DisplayWidthMM(X11->display, screen) * w
+ / DisplayWidth(X11->display, screen);
+ return mm;
+ }
+ case QPaintDevice::PdmHeightMM: {
+ const int screen = xinfo.screen();
+ const int mm = (DisplayHeightMM(X11->display, screen) * h)
+ / DisplayHeight(X11->display, screen);
+ return mm;
+ }
+ case QPaintDevice::PdmDpiX:
+ case QPaintDevice::PdmPhysicalDpiX:
+ return QX11Info::appDpiX(xinfo.screen());
+ case QPaintDevice::PdmDpiY:
+ case QPaintDevice::PdmPhysicalDpiY:
+ return QX11Info::appDpiY(xinfo.screen());
+ default:
+ qWarning("QX11PlatformPixmap::metric(): Invalid metric");
+ return 0;
+ }
+}
+
+struct QXImageWrapper
+{
+ XImage *xi;
+};
+
+bool QX11PlatformPixmap::canTakeQImageFromXImage(const QXImageWrapper &xiWrapper) const
+{
+ XImage *xi = xiWrapper.xi;
+
+ // ARGB32_Premultiplied
+ if (picture && depth() == 32)
+ return true;
+
+ Visual *visual = (Visual *)xinfo.visual();
+
+ // RGB32
+ if (depth() == 24 && xi->bits_per_pixel == 32 && visual->red_mask == 0xff0000
+ && visual->green_mask == 0xff00 && visual->blue_mask == 0xff)
+ return true;
+
+ // RGB16
+ if (depth() == 16 && xi->bits_per_pixel == 16 && visual->red_mask == 0xf800
+ && visual->green_mask == 0x7e0 && visual->blue_mask == 0x1f)
+ return true;
+
+ return false;
+}
+
+QImage QX11PlatformPixmap::takeQImageFromXImage(const QXImageWrapper &xiWrapper) const
+{
+ XImage *xi = xiWrapper.xi;
+
+ QImage::Format format = QImage::Format_ARGB32_Premultiplied;
+ if (depth() == 24)
+ format = QImage::Format_RGB32;
+ else if (depth() == 16)
+ format = QImage::Format_RGB16;
+
+ QImage image((uchar *)xi->data, xi->width, xi->height, xi->bytes_per_line, format);
+ // take ownership
+ image.data_ptr()->own_data = true;
+ xi->data = 0;
+
+ // we may have to swap the byte order
+ if ((QSysInfo::ByteOrder == QSysInfo::LittleEndian && xi->byte_order == MSBFirst)
+ || (QSysInfo::ByteOrder == QSysInfo::BigEndian && xi->byte_order == LSBFirst))
+ {
+ for (int i=0; i < image.height(); i++) {
+ if (depth() == 16) {
+ ushort *p = (ushort*)image.scanLine(i);
+ ushort *end = p + image.width();
+ while (p < end) {
+ *p = ((*p << 8) & 0xff00) | ((*p >> 8) & 0x00ff);
+ p++;
+ }
+ } else {
+ uint *p = (uint*)image.scanLine(i);
+ uint *end = p + image.width();
+ while (p < end) {
+ *p = ((*p << 24) & 0xff000000) | ((*p << 8) & 0x00ff0000)
+ | ((*p >> 8) & 0x0000ff00) | ((*p >> 24) & 0x000000ff);
+ p++;
+ }
+ }
+ }
+ }
+
+ // fix-up alpha channel
+ if (format == QImage::Format_RGB32) {
+ QRgb *p = (QRgb *)image.bits();
+ for (int y = 0; y < xi->height; ++y) {
+ for (int x = 0; x < xi->width; ++x)
+ p[x] |= 0xff000000;
+ p += xi->bytes_per_line / 4;
+ }
+ }
+
+ XDestroyImage(xi);
+ return image;
+}
+
+QImage QX11PlatformPixmap::toImage(const QRect &rect) const
+{
+ QXImageWrapper xiWrapper;
+ xiWrapper.xi = XGetImage(X11->display, hd, rect.x(), rect.y(), rect.width(), rect.height(),
+ AllPlanes, (depth() == 1) ? XYPixmap : ZPixmap);
+
+ Q_CHECK_PTR(xiWrapper.xi);
+ if (!xiWrapper.xi)
+ return QImage();
+
+ if (!x11_mask && canTakeQImageFromXImage(xiWrapper))
+ return takeQImageFromXImage(xiWrapper);
+
+ QImage image = toImage(xiWrapper, rect);
+ qSafeXDestroyImage(xiWrapper.xi);
+ return image;
+}
+
+/*!
+ Converts the pixmap to a QImage. Returns a null image if the
+ conversion fails.
+
+ If the pixmap has 1-bit depth, the returned image will also be 1
+ bit deep. If the pixmap has 2- to 8-bit depth, the returned image
+ has 8-bit depth. If the pixmap has greater than 8-bit depth, the
+ returned image has 32-bit depth.
+
+ Note that for the moment, alpha masks on monochrome images are
+ ignored.
+
+ \sa fromImage(), {QImage#Image Formats}{Image Formats}
+*/
+
+QImage QX11PlatformPixmap::toImage() const
+{
+ return toImage(QRect(0, 0, w, h));
+}
+
+QImage QX11PlatformPixmap::toImage(const QXImageWrapper &xiWrapper, const QRect &rect) const
+{
+ XImage *xi = xiWrapper.xi;
+
+ int d = depth();
+ Visual *visual = (Visual *)xinfo.visual();
+ bool trucol = (visual->c_class >= TrueColor) && d > 1;
+
+ QImage::Format format = QImage::Format_Mono;
+ if (d > 1 && d <= 8) {
+ d = 8;
+ format = QImage::Format_Indexed8;
+ }
+ // we could run into the situation where d == 8 AND trucol is true, which can
+ // cause problems when converting to and from images. in this case, always treat
+ // the depth as 32...
+ if (d > 8 || trucol) {
+ d = 32;
+ format = QImage::Format_RGB32;
+ }
+
+ if (d == 1 && xi->bitmap_bit_order == LSBFirst)
+ format = QImage::Format_MonoLSB;
+ if (x11_mask && format == QImage::Format_RGB32)
+ format = QImage::Format_ARGB32;
+
+ QImage image(xi->width, xi->height, format);
+ if (image.isNull()) // could not create image
+ return image;
+
+ QImage alpha;
+ if (x11_mask) {
+ if (rect.contains(QRect(0, 0, w, h)))
+ alpha = mask().toImage();
+ else
+ alpha = mask().toImage().copy(rect);
+ }
+ bool ale = alpha.format() == QImage::Format_MonoLSB;
+
+ if (trucol) { // truecolor
+ const uint red_mask = (uint)visual->red_mask;
+ const uint green_mask = (uint)visual->green_mask;
+ const uint blue_mask = (uint)visual->blue_mask;
+ const int red_shift = highest_bit(red_mask) - 7;
+ const int green_shift = highest_bit(green_mask) - 7;
+ const int blue_shift = highest_bit(blue_mask) - 7;
+
+ const uint red_bits = n_bits(red_mask);
+ const uint green_bits = n_bits(green_mask);
+ const uint blue_bits = n_bits(blue_mask);
+
+ static uint red_table_bits = 0;
+ static uint green_table_bits = 0;
+ static uint blue_table_bits = 0;
+
+ if (red_bits < 8 && red_table_bits != red_bits) {
+ build_scale_table(&red_scale_table, red_bits);
+ red_table_bits = red_bits;
+ }
+ if (blue_bits < 8 && blue_table_bits != blue_bits) {
+ build_scale_table(&blue_scale_table, blue_bits);
+ blue_table_bits = blue_bits;
+ }
+ if (green_bits < 8 && green_table_bits != green_bits) {
+ build_scale_table(&green_scale_table, green_bits);
+ green_table_bits = green_bits;
+ }
+
+ int r, g, b;
+
+ QRgb *dst;
+ uchar *src;
+ uint pixel;
+ int bppc = xi->bits_per_pixel;
+
+ if (bppc > 8 && xi->byte_order == LSBFirst)
+ bppc++;
+
+ for (int y = 0; y < xi->height; ++y) {
+ uchar* asrc = x11_mask ? alpha.scanLine(y) : 0;
+ dst = (QRgb *)image.scanLine(y);
+ src = (uchar *)xi->data + xi->bytes_per_line*y;
+ for (int x = 0; x < xi->width; x++) {
+ switch (bppc) {
+ case 8:
+ pixel = *src++;
+ break;
+ case 16: // 16 bit MSB
+ pixel = src[1] | (uint)src[0] << 8;
+ src += 2;
+ break;
+ case 17: // 16 bit LSB
+ pixel = src[0] | (uint)src[1] << 8;
+ src += 2;
+ break;
+ case 24: // 24 bit MSB
+ pixel = src[2] | (uint)src[1] << 8 | (uint)src[0] << 16;
+ src += 3;
+ break;
+ case 25: // 24 bit LSB
+ pixel = src[0] | (uint)src[1] << 8 | (uint)src[2] << 16;
+ src += 3;
+ break;
+ case 32: // 32 bit MSB
+ pixel = src[3] | (uint)src[2] << 8 | (uint)src[1] << 16 | (uint)src[0] << 24;
+ src += 4;
+ break;
+ case 33: // 32 bit LSB
+ pixel = src[0] | (uint)src[1] << 8 | (uint)src[2] << 16 | (uint)src[3] << 24;
+ src += 4;
+ break;
+ default: // should not really happen
+ x = xi->width; // leave loop
+ y = xi->height;
+ pixel = 0; // eliminate compiler warning
+ qWarning("QPixmap::convertToImage: Invalid depth %d", bppc);
+ }
+ if (red_shift > 0)
+ r = (pixel & red_mask) >> red_shift;
+ else
+ r = (pixel & red_mask) << -red_shift;
+ if (green_shift > 0)
+ g = (pixel & green_mask) >> green_shift;
+ else
+ g = (pixel & green_mask) << -green_shift;
+ if (blue_shift > 0)
+ b = (pixel & blue_mask) >> blue_shift;
+ else
+ b = (pixel & blue_mask) << -blue_shift;
+
+ if (red_bits < 8)
+ r = red_scale_table[r];
+ if (green_bits < 8)
+ g = green_scale_table[g];
+ if (blue_bits < 8)
+ b = blue_scale_table[b];
+
+ if (x11_mask) {
+ if (ale) {
+ *dst++ = (asrc[x >> 3] & (1 << (x & 7))) ? qRgba(r, g, b, 0xff) : 0;
+ } else {
+ *dst++ = (asrc[x >> 3] & (0x80 >> (x & 7))) ? qRgba(r, g, b, 0xff) : 0;
+ }
+ } else {
+ *dst++ = qRgb(r, g, b);
+ }
+ }
+ }
+ } else if (xi->bits_per_pixel == d) { // compatible depth
+ char *xidata = xi->data; // copy each scanline
+ int bpl = qMin(image.bytesPerLine(),xi->bytes_per_line);
+ for (int y=0; y<xi->height; y++) {
+ memcpy(image.scanLine(y), xidata, bpl);
+ xidata += xi->bytes_per_line;
+ }
+ } else {
+ /* Typically 2 or 4 bits display depth */
+ qWarning("QPixmap::convertToImage: Display not supported (bpp=%d)",
+ xi->bits_per_pixel);
+ return QImage();
+ }
+
+ if (d == 1) { // bitmap
+ image.setColorCount(2);
+ image.setColor(0, qRgb(255,255,255));
+ image.setColor(1, qRgb(0,0,0));
+ } else if (!trucol) { // pixmap with colormap
+ register uchar *p;
+ uchar *end;
+ uchar use[256]; // pixel-in-use table
+ uchar pix[256]; // pixel translation table
+ int ncols, bpl;
+ memset(use, 0, 256);
+ memset(pix, 0, 256);
+ bpl = image.bytesPerLine();
+
+ if (x11_mask) { // which pixels are used?
+ for (int i = 0; i < xi->height; i++) {
+ uchar* asrc = alpha.scanLine(i);
+ p = image.scanLine(i);
+ if (ale) {
+ for (int x = 0; x < xi->width; x++) {
+ if (asrc[x >> 3] & (1 << (x & 7)))
+ use[*p] = 1;
+ ++p;
+ }
+ } else {
+ for (int x = 0; x < xi->width; x++) {
+ if (asrc[x >> 3] & (0x80 >> (x & 7)))
+ use[*p] = 1;
+ ++p;
+ }
+ }
+ }
+ } else {
+ for (int i = 0; i < xi->height; i++) {
+ p = image.scanLine(i);
+ end = p + bpl;
+ while (p < end)
+ use[*p++] = 1;
+ }
+ }
+ ncols = 0;
+ for (int i = 0; i < 256; i++) { // build translation table
+ if (use[i])
+ pix[i] = ncols++;
+ }
+ for (int i = 0; i < xi->height; i++) { // translate pixels
+ p = image.scanLine(i);
+ end = p + bpl;
+ while (p < end) {
+ *p = pix[*p];
+ p++;
+ }
+ }
+ if (x11_mask) {
+ int trans;
+ if (ncols < 256) {
+ trans = ncols++;
+ image.setColorCount(ncols); // create color table
+ image.setColor(trans, 0x00000000);
+ } else {
+ image.setColorCount(ncols); // create color table
+ // oh dear... no spare "transparent" pixel.
+ // use first pixel in image (as good as any).
+ trans = image.scanLine(0)[0];
+ }
+ for (int i = 0; i < xi->height; i++) {
+ uchar* asrc = alpha.scanLine(i);
+ p = image.scanLine(i);
+ if (ale) {
+ for (int x = 0; x < xi->width; x++) {
+ if (!(asrc[x >> 3] & (1 << (x & 7))))
+ *p = trans;
+ ++p;
+ }
+ } else {
+ for (int x = 0; x < xi->width; x++) {
+ if (!(asrc[x >> 3] & (1 << (7 -(x & 7)))))
+ *p = trans;
+ ++p;
+ }
+ }
+ }
+ } else {
+ image.setColorCount(ncols); // create color table
+ }
+ QVector<QColor> colors = QColormap::instance(xinfo.screen()).colormap();
+ int j = 0;
+ for (int i=0; i<colors.size(); i++) { // translate pixels
+ if (use[i])
+ image.setColor(j++, 0xff000000 | colors.at(i).rgb());
+ }
+ }
+
+ return image;
+}
+
+/*!
+ Returns a copy of the pixmap that is transformed using the given
+ transformation \a matrix and transformation \a mode. The original
+ pixmap is not changed.
+
+ The transformation \a matrix is internally adjusted to compensate
+ for unwanted translation; i.e. the pixmap produced is the smallest
+ pixmap that contains all the transformed points of the original
+ pixmap. Use the trueMatrix() function to retrieve the actual
+ matrix used for transforming the pixmap.
+
+ This function is slow because it involves transformation to a
+ QImage, non-trivial computations and a transformation back to a
+ QPixmap.
+
+ \sa trueMatrix(), {QPixmap#Pixmap Transformations}{Pixmap
+ Transformations}
+*/
+QPixmap QX11PlatformPixmap::transformed(const QTransform &transform,
+ Qt::TransformationMode mode ) const
+{
+ if (mode == Qt::SmoothTransformation || transform.type() >= QTransform::TxProject) {
+ QImage image = toImage();
+ return QPixmap::fromImage(image.transformed(transform, mode));
+ }
+
+ uint w = 0;
+ uint h = 0; // size of target pixmap
+ uint ws, hs; // size of source pixmap
+ uchar *dptr; // data in target pixmap
+ uint dbpl, dbytes; // bytes per line/bytes total
+ uchar *sptr; // data in original pixmap
+ int sbpl; // bytes per line in original
+ int bpp; // bits per pixel
+ bool depth1 = depth() == 1;
+ Display *dpy = X11->display;
+
+ ws = width();
+ hs = height();
+
+ QTransform mat(transform.m11(), transform.m12(), transform.m13(),
+ transform.m21(), transform.m22(), transform.m23(),
+ 0., 0., 1);
+ bool complex_xform = false;
+ qreal scaledWidth;
+ qreal scaledHeight;
+
+ if (mat.type() <= QTransform::TxScale) {
+ scaledHeight = qAbs(mat.m22()) * hs + 0.9999;
+ scaledWidth = qAbs(mat.m11()) * ws + 0.9999;
+ h = qAbs(int(scaledHeight));
+ w = qAbs(int(scaledWidth));
+ } else { // rotation or shearing
+ QPolygonF a(QRectF(0, 0, ws, hs));
+ a = mat.map(a);
+ QRect r = a.boundingRect().toAlignedRect();
+ w = r.width();
+ h = r.height();
+ scaledWidth = w;
+ scaledHeight = h;
+ complex_xform = true;
+ }
+ mat = QPixmap::trueMatrix(mat, ws, hs); // true matrix
+
+ bool invertible;
+ mat = mat.inverted(&invertible); // invert matrix
+
+ if (h == 0 || w == 0 || !invertible
+ || qAbs(scaledWidth) >= 32768 || qAbs(scaledHeight) >= 32768 )
+ // error, return null pixmap
+ return QPixmap();
+
+#if defined(QT_MITSHM)
+ static bool try_once = true;
+ if (try_once) {
+ try_once = false;
+ if (!xshminit)
+ qt_create_mitshm_buffer(this, 800, 600);
+ }
+
+ bool use_mitshm = xshmimg && !depth1 &&
+ xshmimg->width >= w && xshmimg->height >= h;
+#endif
+ XImage *xi = XGetImage(X11->display, handle(), 0, 0, ws, hs, AllPlanes,
+ depth1 ? XYPixmap : ZPixmap);
+
+ if (!xi)
+ return QPixmap();
+
+ sbpl = xi->bytes_per_line;
+ sptr = (uchar *)xi->data;
+ bpp = xi->bits_per_pixel;
+
+ if (depth1)
+ dbpl = (w+7)/8;
+ else
+ dbpl = ((w*bpp+31)/32)*4;
+ dbytes = dbpl*h;
+
+#if defined(QT_MITSHM)
+ if (use_mitshm) {
+ dptr = (uchar *)xshmimg->data;
+ uchar fillbyte = bpp == 8 ? white.pixel() : 0xff;
+ for (int y=0; y<h; y++)
+ memset(dptr + y*xshmimg->bytes_per_line, fillbyte, dbpl);
+ } else {
+#endif
+ dptr = (uchar *)malloc(dbytes); // create buffer for bits
+ Q_CHECK_PTR(dptr);
+ if (depth1) // fill with zeros
+ memset(dptr, 0, dbytes);
+ else if (bpp == 8) // fill with background color
+ memset(dptr, WhitePixel(X11->display, xinfo.screen()), dbytes);
+ else
+ memset(dptr, 0, dbytes);
+#if defined(QT_MITSHM)
+ }
+#endif
+
+ // #define QT_DEBUG_XIMAGE
+#if defined(QT_DEBUG_XIMAGE)
+ qDebug("----IMAGE--INFO--------------");
+ qDebug("width............. %d", xi->width);
+ qDebug("height............ %d", xi->height);
+ qDebug("xoffset........... %d", xi->xoffset);
+ qDebug("format............ %d", xi->format);
+ qDebug("byte order........ %d", xi->byte_order);
+ qDebug("bitmap unit....... %d", xi->bitmap_unit);
+ qDebug("bitmap bit order.. %d", xi->bitmap_bit_order);
+ qDebug("depth............. %d", xi->depth);
+ qDebug("bytes per line.... %d", xi->bytes_per_line);
+ qDebug("bits per pixel.... %d", xi->bits_per_pixel);
+#endif
+
+ int type;
+ if (xi->bitmap_bit_order == MSBFirst)
+ type = QT_XFORM_TYPE_MSBFIRST;
+ else
+ type = QT_XFORM_TYPE_LSBFIRST;
+ int xbpl, p_inc;
+ if (depth1) {
+ xbpl = (w+7)/8;
+ p_inc = dbpl - xbpl;
+ } else {
+ xbpl = (w*bpp)/8;
+ p_inc = dbpl - xbpl;
+#if defined(QT_MITSHM)
+ if (use_mitshm)
+ p_inc = xshmimg->bytes_per_line - xbpl;
+#endif
+ }
+
+ if (!qt_xForm_helper(mat, xi->xoffset, type, bpp, dptr, xbpl, p_inc, h, sptr, sbpl, ws, hs)){
+ qWarning("QPixmap::transform: display not supported (bpp=%d)",bpp);
+ QPixmap pm;
+ return pm;
+ }
+
+ qSafeXDestroyImage(xi);
+
+ if (depth1) { // mono bitmap
+ QBitmap bm = QBitmap::fromData(QSize(w, h), dptr,
+ BitmapBitOrder(X11->display) == MSBFirst
+ ? QImage::Format_Mono
+ : QImage::Format_MonoLSB);
+ free(dptr);
+ return bm;
+ } else { // color pixmap
+ QX11PlatformPixmap *x11Data = new QX11PlatformPixmap(QPlatformPixmap::PixmapType);
+ QPixmap pm(x11Data);
+ x11Data->flags &= ~QX11PlatformPixmap::Uninitialized;
+ x11Data->xinfo = xinfo;
+ x11Data->d = d;
+ x11Data->w = w;
+ x11Data->h = h;
+ x11Data->is_null = (w <= 0 || h <= 0);
+ x11Data->hd = (Qt::HANDLE)XCreatePixmap(X11->display,
+ RootWindow(X11->display, xinfo.screen()),
+ w, h, d);
+ x11Data->setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1));
+
+#ifndef QT_NO_XRENDER
+ if (X11->use_xrender) {
+ XRenderPictFormat *format = x11Data->d == 32
+ ? XRenderFindStandardFormat(X11->display, PictStandardARGB32)
+ : XRenderFindVisualFormat(X11->display, (Visual *) x11Data->xinfo.visual());
+ x11Data->picture = XRenderCreatePicture(X11->display, x11Data->hd, format, 0, 0);
+ }
+#endif // QT_NO_XRENDER
+
+ GC gc = XCreateGC(X11->display, x11Data->hd, 0, 0);
+#if defined(QT_MITSHM)
+ if (use_mitshm) {
+ XCopyArea(dpy, xshmpm, x11Data->hd, gc, 0, 0, w, h, 0, 0);
+ } else
+#endif
+ {
+ xi = XCreateImage(dpy, (Visual*)x11Data->xinfo.visual(),
+ x11Data->d,
+ ZPixmap, 0, (char *)dptr, w, h, 32, 0);
+ XPutImage(dpy, pm.handle(), gc, xi, 0, 0, 0, 0, w, h);
+ qSafeXDestroyImage(xi);
+ }
+ XFreeGC(X11->display, gc);
+
+ if (x11_mask) { // xform mask, too
+ pm.setMask(mask_to_bitmap(xinfo.screen()).transformed(transform));
+ } else if (d != 32 && complex_xform) { // need a mask!
+ QBitmap mask(ws, hs);
+ mask.fill(Qt::color1);
+ pm.setMask(mask.transformed(transform));
+ }
+ return pm;
+ }
+}
+
+int QPixmap::x11SetDefaultScreen(int screen)
+{
+ int old = defaultScreen;
+ defaultScreen = screen;
+ return old;
+}
+
+void QPixmap::x11SetScreen(int screen)
+{
+ if (paintingActive()) {
+ qWarning("QPixmap::x11SetScreen(): Cannot change screens during painting");
+ return;
+ }
+
+ if (isNull())
+ return;
+
+ if (data->classId() != QPlatformPixmap::X11Class)
+ return;
+
+ if (screen < 0)
+ screen = QX11Info::appScreen();
+
+ QX11PlatformPixmap *x11Data = static_cast<QX11PlatformPixmap*>(data.data());
+ if (screen == x11Data->xinfo.screen())
+ return; // nothing to do
+
+ if (isNull()) {
+ QX11InfoData* xd = x11Data->xinfo.getX11Data(true);
+ xd->screen = screen;
+ xd->depth = QX11Info::appDepth(screen);
+ xd->cells = QX11Info::appCells(screen);
+ xd->colormap = QX11Info::appColormap(screen);
+ xd->defaultColormap = QX11Info::appDefaultColormap(screen);
+ xd->visual = (Visual *)QX11Info::appVisual(screen);
+ xd->defaultVisual = QX11Info::appDefaultVisual(screen);
+ x11Data->xinfo.setX11Data(xd);
+ return;
+ }
+#if 0
+ qDebug("QPixmap::x11SetScreen for %p from %d to %d. Size is %d/%d", x11Data, x11Data->xinfo.screen(), screen, width(), height());
+#endif
+
+ x11SetDefaultScreen(screen);
+ *this = qt_toX11Pixmap(toImage());
+}
+
+QPixmap QPixmap::grabWindow(WId window, int x, int y, int w, int h)
+{
+ if (w == 0 || h == 0)
+ return QPixmap();
+
+ Display *dpy = X11->display;
+ XWindowAttributes window_attr;
+ if (!XGetWindowAttributes(dpy, window, &window_attr))
+ return QPixmap();
+
+ if (w < 0)
+ w = window_attr.width - x;
+ if (h < 0)
+ h = window_attr.height - y;
+
+ // determine the screen
+ int scr;
+ for (scr = 0; scr < ScreenCount(dpy); ++scr) {
+ if (window_attr.root == RootWindow(dpy, scr)) // found it
+ break;
+ }
+ if (scr >= ScreenCount(dpy)) // sanity check
+ return QPixmap();
+
+
+ // get the depth of the root window
+ XWindowAttributes root_attr;
+ if (!XGetWindowAttributes(dpy, window_attr.root, &root_attr))
+ return QPixmap();
+
+ if (window_attr.depth == root_attr.depth) {
+ // if the depth of the specified window and the root window are the
+ // same, grab pixels from the root window (so that we get the any
+ // overlapping windows and window manager frames)
+
+ // map x and y to the root window
+ WId unused;
+ if (!XTranslateCoordinates(dpy, window, window_attr.root, x, y,
+ &x, &y, &unused))
+ return QPixmap();
+
+ window = window_attr.root;
+ window_attr = root_attr;
+ }
+
+ QX11PlatformPixmap *data = new QX11PlatformPixmap(QPlatformPixmap::PixmapType);
+
+ void qt_x11_getX11InfoForWindow(QX11Info * xinfo, const XWindowAttributes &a);
+ qt_x11_getX11InfoForWindow(&data->xinfo,window_attr);
+
+ data->resize(w, h);
+
+ QPixmap pm(data);
+
+ data->flags &= ~QX11PlatformPixmap::Uninitialized;
+ pm.x11SetScreen(scr);
+
+ GC gc = XCreateGC(dpy, pm.handle(), 0, 0);
+ XSetSubwindowMode(dpy, gc, IncludeInferiors);
+ XCopyArea(dpy, window, pm.handle(), gc, x, y, w, h, 0, 0);
+ XFreeGC(dpy, gc);
+
+ return pm;
+}
+
+bool QX11PlatformPixmap::hasAlphaChannel() const
+{
+ return d == 32;
+}
+
+const QX11Info &QPixmap::x11Info() const
+{
+ if (data && data->classId() == QPlatformPixmap::X11Class)
+ return static_cast<QX11PlatformPixmap*>(data.data())->xinfo;
+ else {
+ static QX11Info nullX11Info;
+ return nullX11Info;
+ }
+}
+
+#if !defined(QT_NO_XRENDER)
+static XRenderPictFormat *qt_renderformat_for_depth(const QX11Info &xinfo, int depth)
+{
+ if (depth == 1)
+ return XRenderFindStandardFormat(X11->display, PictStandardA1);
+ else if (depth == 32)
+ return XRenderFindStandardFormat(X11->display, PictStandardARGB32);
+ else
+ return XRenderFindVisualFormat(X11->display, (Visual *)xinfo.visual());
+}
+#endif
+
+QPaintEngine* QX11PlatformPixmap::paintEngine() const
+{
+ QX11PlatformPixmap *that = const_cast<QX11PlatformPixmap*>(this);
+
+ if ((flags & Readonly) && share_mode == QPixmap::ImplicitlyShared) {
+ // if someone wants to draw onto us, copy the shared contents
+ // and turn it into a fully fledged QPixmap
+ ::Pixmap hd_copy = XCreatePixmap(X11->display, RootWindow(X11->display, xinfo.screen()),
+ w, h, d);
+#if !defined(QT_NO_XRENDER)
+ XRenderPictFormat *format = qt_renderformat_for_depth(xinfo, d);
+ ::Picture picture_copy = XRenderCreatePicture(X11->display, hd_copy, format, 0, 0);
+
+ if (picture && d == 32) {
+ XRenderComposite(X11->display, PictOpSrc, picture, 0, picture_copy,
+ 0, 0, 0, 0, 0, 0, w, h);
+ XRenderFreePicture(X11->display, picture);
+ that->picture = picture_copy;
+ } else
+#endif
+ {
+ GC gc = XCreateGC(X11->display, hd_copy, 0, 0);
+ XCopyArea(X11->display, hd, hd_copy, gc, 0, 0, w, h, 0, 0);
+ XFreeGC(X11->display, gc);
+ }
+ that->hd = hd_copy;
+ that->flags &= ~QX11PlatformPixmap::Readonly;
+ }
+
+ if (!that->pengine)
+ that->pengine = new QX11PaintEngine;
+ return that->pengine;
+}
+
+Qt::HANDLE QPixmap::x11PictureHandle() const
+{
+#ifndef QT_NO_XRENDER
+ if (data && data->classId() == QPlatformPixmap::X11Class)
+ return static_cast<const QX11PlatformPixmap*>(data.data())->picture;
+ else
+ return 0;
+#else
+ return 0;
+#endif // QT_NO_XRENDER
+}
+
+Qt::HANDLE QX11PlatformPixmap::x11ConvertToDefaultDepth()
+{
+#ifndef QT_NO_XRENDER
+ if (d == QX11Info::appDepth() || !X11->use_xrender)
+ return hd;
+ if (!hd2) {
+ hd2 = XCreatePixmap(xinfo.display(), hd, w, h, QX11Info::appDepth());
+ XRenderPictFormat *format = XRenderFindVisualFormat(xinfo.display(),
+ (Visual*) xinfo.visual());
+ Picture pic = XRenderCreatePicture(xinfo.display(), hd2, format, 0, 0);
+ XRenderComposite(xinfo.display(), PictOpSrc, picture,
+ XNone, pic, 0, 0, 0, 0, 0, 0, w, h);
+ XRenderFreePicture(xinfo.display(), pic);
+ }
+ return hd2;
+#else
+ return hd;
+#endif
+}
+
+void QX11PlatformPixmap::copy(const QPlatformPixmap *data, const QRect &rect)
+{
+ if (data->pixelType() == BitmapType) {
+ fromImage(data->toImage().copy(rect), Qt::AutoColor);
+ return;
+ }
+
+ const QX11PlatformPixmap *x11Data = static_cast<const QX11PlatformPixmap*>(data);
+
+ setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1));
+
+ flags &= ~Uninitialized;
+ xinfo = x11Data->xinfo;
+ d = x11Data->d;
+ w = rect.width();
+ h = rect.height();
+ is_null = (w <= 0 || h <= 0);
+ hd = (Qt::HANDLE)XCreatePixmap(X11->display,
+ RootWindow(X11->display, x11Data->xinfo.screen()),
+ w, h, d);
+#ifndef QT_NO_XRENDER
+ if (X11->use_xrender) {
+ XRenderPictFormat *format = d == 32
+ ? XRenderFindStandardFormat(X11->display, PictStandardARGB32)
+ : XRenderFindVisualFormat(X11->display, (Visual *)xinfo.visual());
+ picture = XRenderCreatePicture(X11->display, hd, format, 0, 0);
+ }
+#endif // QT_NO_XRENDER
+ if (x11Data->x11_mask) {
+ x11_mask = XCreatePixmap(X11->display, hd, w, h, 1);
+#ifndef QT_NO_XRENDER
+ if (X11->use_xrender) {
+ mask_picture = XRenderCreatePicture(X11->display, x11_mask,
+ XRenderFindStandardFormat(X11->display, PictStandardA1), 0, 0);
+ XRenderPictureAttributes attrs;
+ attrs.alpha_map = x11Data->mask_picture;
+ XRenderChangePicture(X11->display, x11Data->picture, CPAlphaMap, &attrs);
+ }
+#endif
+ }
+
+#if !defined(QT_NO_XRENDER)
+ if (x11Data->picture && x11Data->d == 32) {
+ XRenderComposite(X11->display, PictOpSrc,
+ x11Data->picture, 0, picture,
+ rect.x(), rect.y(), 0, 0, 0, 0, w, h);
+ } else
+#endif
+ {
+ GC gc = XCreateGC(X11->display, hd, 0, 0);
+ XCopyArea(X11->display, x11Data->hd, hd, gc,
+ rect.x(), rect.y(), w, h, 0, 0);
+ if (x11Data->x11_mask) {
+ GC monogc = XCreateGC(X11->display, x11_mask, 0, 0);
+ XCopyArea(X11->display, x11Data->x11_mask, x11_mask, monogc,
+ rect.x(), rect.y(), w, h, 0, 0);
+ XFreeGC(X11->display, monogc);
+ }
+ XFreeGC(X11->display, gc);
+ }
+}
+
+bool QX11PlatformPixmap::scroll(int dx, int dy, const QRect &rect)
+{
+ GC gc = XCreateGC(X11->display, hd, 0, 0);
+ XCopyArea(X11->display, hd, hd, gc,
+ rect.left(), rect.top(), rect.width(), rect.height(),
+ rect.left() + dx, rect.top() + dy);
+ XFreeGC(X11->display, gc);
+ return true;
+}
+
+#if !defined(QT_NO_XRENDER)
+void QX11PlatformPixmap::convertToARGB32(bool preserveContents)
+{
+ if (!X11->use_xrender)
+ return;
+
+ // Q_ASSERT(count == 1);
+ if ((flags & Readonly) && share_mode == QPixmap::ExplicitlyShared)
+ return;
+
+ Pixmap pm = XCreatePixmap(X11->display, RootWindow(X11->display, xinfo.screen()),
+ w, h, 32);
+ Picture p = XRenderCreatePicture(X11->display, pm,
+ XRenderFindStandardFormat(X11->display, PictStandardARGB32), 0, 0);
+ if (picture) {
+ if (preserveContents)
+ XRenderComposite(X11->display, PictOpSrc, picture, 0, p, 0, 0, 0, 0, 0, 0, w, h);
+ if (!(flags & Readonly))
+ XRenderFreePicture(X11->display, picture);
+ }
+ if (hd && !(flags & Readonly))
+ XFreePixmap(X11->display, hd);
+ if (x11_mask) {
+ XFreePixmap(X11->display, x11_mask);
+ if (mask_picture)
+ XRenderFreePicture(X11->display, mask_picture);
+ x11_mask = 0;
+ mask_picture = 0;
+ }
+ hd = pm;
+ picture = p;
+ d = 32;
+}
+#endif
+
+QPixmap QPixmap::fromX11Pixmap(Qt::HANDLE pixmap, QPixmap::ShareMode mode)
+{
+ Window root;
+ int x;
+ int y;
+ uint width;
+ uint height;
+ uint border_width;
+ uint depth;
+ XWindowAttributes win_attribs;
+ int num_screens = ScreenCount(X11->display);
+ int screen = 0;
+
+ XGetGeometry(X11->display, pixmap, &root, &x, &y, &width, &height, &border_width, &depth);
+ XGetWindowAttributes(X11->display, root, &win_attribs);
+
+ for (; screen < num_screens; ++screen) {
+ if (win_attribs.screen == ScreenOfDisplay(X11->display, screen))
+ break;
+ }
+
+ QX11PlatformPixmap *data = new QX11PlatformPixmap(depth == 1 ? QPlatformPixmap::BitmapType : QPlatformPixmap::PixmapType);
+ data->setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1));
+ data->flags = QX11PlatformPixmap::Readonly;
+ data->share_mode = mode;
+ data->w = width;
+ data->h = height;
+ data->is_null = (width <= 0 || height <= 0);
+ data->d = depth;
+ data->hd = pixmap;
+
+ if (defaultScreen >= 0 && defaultScreen != screen) {
+ QX11InfoData* xd = data->xinfo.getX11Data(true);
+ xd->screen = defaultScreen;
+ xd->depth = QX11Info::appDepth(xd->screen);
+ xd->cells = QX11Info::appCells(xd->screen);
+ xd->colormap = QX11Info::appColormap(xd->screen);
+ xd->defaultColormap = QX11Info::appDefaultColormap(xd->screen);
+ xd->visual = (Visual *)QX11Info::appVisual(xd->screen);
+ xd->defaultVisual = QX11Info::appDefaultVisual(xd->screen);
+ data->xinfo.setX11Data(xd);
+ }
+
+#ifndef QT_NO_XRENDER
+ if (X11->use_xrender) {
+ XRenderPictFormat *format = qt_renderformat_for_depth(data->xinfo, depth);
+ data->picture = XRenderCreatePicture(X11->display, data->hd, format, 0, 0);
+ }
+#endif // QT_NO_XRENDER
+
+ return QPixmap(data);
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/x11/qpixmap_x11_p.h b/src/widgets/platforms/x11/qpixmap_x11_p.h
new file mode 100644
index 0000000000..fce32cbe3e
--- /dev/null
+++ b/src/widgets/platforms/x11/qpixmap_x11_p.h
@@ -0,0 +1,156 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPIXMAPDATA_X11_P_H
+#define QPIXMAPDATA_X11_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/qplatformpixmap_qpa.h>
+#include <QtGui/qplatformpixmapfactory_p.h>
+
+#include "QtGui/qx11info_x11.h"
+
+QT_BEGIN_NAMESPACE
+
+class QX11PaintEngine;
+
+struct QXImageWrapper;
+
+class Q_WIDGETS_EXPORT QX11PlatformPixmap : public QPlatformPixmap
+{
+public:
+ QX11PlatformPixmap(PixelType type);
+// QX11PlatformPixmap(PixelType type, int width, int height);
+// QX11PlatformPixmap(PixelType type, const QImage &image,
+// Qt::ImageConversionFlags flags);
+ ~QX11PlatformPixmap();
+
+ QPlatformPixmap *createCompatiblePlatformPixmap() const;
+
+ void resize(int width, int height);
+ void fromImage(const QImage &image, Qt::ImageConversionFlags flags);
+ void copy(const QPlatformPixmap *data, const QRect &rect);
+ bool scroll(int dx, int dy, const QRect &rect);
+
+ void fill(const QColor &color);
+ QBitmap mask() const;
+ void setMask(const QBitmap &mask);
+ bool hasAlphaChannel() const;
+ void setAlphaChannel(const QPixmap &alphaChannel);
+ QPixmap alphaChannel() const;
+ QPixmap transformed(const QTransform &transform,
+ Qt::TransformationMode mode) const;
+ QImage toImage() const;
+ QImage toImage(const QRect &rect) const;
+ QPaintEngine* paintEngine() const;
+
+ Qt::HANDLE handle() const { return hd; }
+ Qt::HANDLE x11ConvertToDefaultDepth();
+
+ static Qt::HANDLE createBitmapFromImage(const QImage &image);
+
+ void* gl_surface;
+#ifndef QT_NO_XRENDER
+ void convertToARGB32(bool preserveContents = true);
+#endif
+
+protected:
+ int metric(QPaintDevice::PaintDeviceMetric metric) const;
+
+private:
+ friend class QPixmap;
+ friend class QBitmap;
+ friend class QX11PaintEngine;
+ friend class QX11WindowSurface;
+ friend class QRasterWindowSurface;
+ friend class QGLContextPrivate; // Needs to access xinfo, gl_surface & flags
+ friend class QEglContext; // Needs gl_surface
+ friend class QGLContext; // Needs gl_surface
+ friend class QX11GLPlatformPixmap; // Needs gl_surface
+ friend class QMeeGoLivePlatformPixmap; // Needs gl_surface and flags
+ friend bool qt_createEGLSurfaceForPixmap(QPlatformPixmap*, bool); // Needs gl_surface
+
+ void release();
+
+ QImage toImage(const QXImageWrapper &xi, const QRect &rect) const;
+
+ QBitmap mask_to_bitmap(int screen) const;
+ static Qt::HANDLE bitmap_to_mask(const QBitmap &, int screen);
+ void bitmapFromImage(const QImage &image);
+
+ bool canTakeQImageFromXImage(const QXImageWrapper &xi) const;
+ QImage takeQImageFromXImage(const QXImageWrapper &xi) const;
+
+ Qt::HANDLE hd;
+
+ enum Flag {
+ NoFlags = 0x0,
+ Uninitialized = 0x1,
+ Readonly = 0x2,
+ InvertedWhenBoundToTexture = 0x4,
+ GlSurfaceCreatedWithAlpha = 0x8
+ };
+ uint flags;
+
+ QX11Info xinfo;
+ Qt::HANDLE x11_mask;
+ Qt::HANDLE picture;
+ Qt::HANDLE mask_picture;
+ Qt::HANDLE hd2; // sorted in the default display depth
+ QPixmap::ShareMode share_mode;
+
+ QX11PaintEngine *pengine;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPIXMAPDATA_X11_P_H
+
diff --git a/src/widgets/platforms/x11/qregion_x11.cpp b/src/widgets/platforms/x11/qregion_x11.cpp
new file mode 100644
index 0000000000..a96ec6dc96
--- /dev/null
+++ b/src/widgets/platforms/x11/qregion_x11.cpp
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qt_x11_p.h>
+
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+QRegion::QRegionData QRegion::shared_empty = {Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0, 0};
+
+void QRegion::updateX11Region() const
+{
+ d->rgn = XCreateRegion();
+ if (!d->qt_rgn)
+ return;
+
+ int n = d->qt_rgn->numRects;
+ const QRect *rect = (n == 1 ? &d->qt_rgn->extents : d->qt_rgn->rects.constData());
+ while (n--) {
+ XRectangle r;
+ r.x = qMax(SHRT_MIN, rect->x());
+ r.y = qMax(SHRT_MIN, rect->y());
+ r.width = qMin((int)USHRT_MAX, rect->width());
+ r.height = qMin((int)USHRT_MAX, rect->height());
+ XUnionRectWithRegion(&r, d->rgn, d->rgn);
+ ++rect;
+ }
+}
+
+void *QRegion::clipRectangles(int &num) const
+{
+ if (!d->xrectangles && !(d == &shared_empty || d->qt_rgn->numRects == 0)) {
+ XRectangle *r = static_cast<XRectangle*>(malloc(d->qt_rgn->numRects * sizeof(XRectangle)));
+ d->xrectangles = r;
+ int n = d->qt_rgn->numRects;
+ const QRect *rect = (n == 1 ? &d->qt_rgn->extents : d->qt_rgn->rects.constData());
+ while (n--) {
+ r->x = qMax(SHRT_MIN, rect->x());
+ r->y = qMax(SHRT_MIN, rect->y());
+ r->width = qMin((int)USHRT_MAX, rect->width());
+ r->height = qMin((int)USHRT_MAX, rect->height());
+ ++r;
+ ++rect;
+ }
+ }
+ if (d == &shared_empty || d->qt_rgn->numRects == 0)
+ num = 0;
+ else
+ num = d->qt_rgn->numRects;
+ return d->xrectangles;
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/x11/qsound_x11.cpp b/src/widgets/platforms/x11/qsound_x11.cpp
new file mode 100644
index 0000000000..c83c26a027
--- /dev/null
+++ b/src/widgets/platforms/x11/qsound_x11.cpp
@@ -0,0 +1,296 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsound.h"
+
+#ifndef QT_NO_SOUND
+
+#include "qhash.h"
+#include "qsocketnotifier.h"
+#include "qapplication.h"
+#include "qsound_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_NAS
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <audio/audiolib.h>
+#include <audio/soundlib.h>
+QT_END_INCLUDE_NAMESPACE
+
+static AuServer *nas=0;
+
+static AuBool eventPred(AuServer *, AuEvent *e, AuPointer p)
+{
+ if (e && (e->type == AuEventTypeElementNotify)) {
+ if (e->auelementnotify.flow == *((AuFlowID *)p))
+ return true;
+ }
+ return false;
+}
+
+class QAuBucketNAS : public QAuBucket {
+public:
+ QAuBucketNAS(AuBucketID b, AuFlowID f = 0) : id(b), flow(f), stopped(true), numplaying(0) { }
+ ~QAuBucketNAS()
+ {
+ if (nas) {
+ AuSync(nas, false);
+ AuDestroyBucket(nas, id, NULL);
+
+ AuEvent ev;
+ while (AuScanEvents(nas, AuEventsQueuedAfterFlush, true, eventPred, &flow, &ev))
+ ;
+ }
+ }
+
+ AuBucketID id;
+ AuFlowID flow;
+ bool stopped;
+ int numplaying;
+};
+
+class QAuServerNAS : public QAuServer {
+ Q_OBJECT
+
+ QSocketNotifier* sn;
+
+public:
+ QAuServerNAS(QObject* parent);
+ ~QAuServerNAS();
+
+ void init(QSound*);
+ void play(const QString& filename);
+ void play(QSound*);
+ void stop(QSound*);
+ bool okay();
+ void setDone(QSound*);
+
+public slots:
+ void dataReceived();
+ void soundDestroyed(QObject *o);
+
+private:
+ QAuBucketNAS* bucket(QSound* s)
+ {
+ return (QAuBucketNAS*)QAuServer::bucket(s);
+ }
+};
+
+QAuServerNAS::QAuServerNAS(QObject* parent) :
+ QAuServer(parent)
+{
+ setObjectName(QLatin1String("Network Audio System"));
+ nas = AuOpenServer(NULL, 0, NULL, 0, NULL, NULL);
+ if (nas) {
+ AuSetCloseDownMode(nas, AuCloseDownDestroy, NULL);
+ // Ask Qt for async messages...
+ sn=new QSocketNotifier(AuServerConnectionNumber(nas),
+ QSocketNotifier::Read);
+ QObject::connect(sn, SIGNAL(activated(int)),
+ this, SLOT(dataReceived()));
+ } else {
+ sn = 0;
+ }
+}
+
+QAuServerNAS::~QAuServerNAS()
+{
+ if (nas)
+ AuCloseServer(nas);
+ delete sn;
+ nas = 0;
+}
+
+typedef QHash<void*,QAuServerNAS*> AuServerHash;
+static AuServerHash *inprogress=0;
+
+void QAuServerNAS::soundDestroyed(QObject *o)
+{
+ if (inprogress) {
+ QSound *so = static_cast<QSound *>(o);
+ while (inprogress->remove(so))
+ ; // Loop while remove returns true
+ }
+}
+
+void QAuServerNAS::play(const QString& filename)
+{
+ if (nas) {
+ int iv=100;
+ AuFixedPoint volume=AuFixedPointFromFraction(iv,100);
+ AuSoundPlayFromFile(nas, filename.toLocal8Bit().constData(), AuNone, volume,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ AuFlush(nas);
+ dataReceived();
+ AuFlush(nas);
+ qApp->flush();
+ }
+}
+
+static void callback(AuServer*, AuEventHandlerRec*, AuEvent* e, AuPointer p)
+{
+ if (inprogress->contains(p) && e) {
+ if (e->type==AuEventTypeElementNotify &&
+ e->auelementnotify.kind==AuElementNotifyKindState) {
+ if (e->auelementnotify.cur_state == AuStateStop) {
+ AuServerHash::Iterator it = inprogress->find(p);
+ if (it != inprogress->end())
+ (*it)->setDone((QSound*)p);
+ }
+ }
+ }
+}
+
+void QAuServerNAS::setDone(QSound* s)
+{
+ if (nas) {
+ decLoop(s);
+ if (s->loopsRemaining() && !bucket(s)->stopped) {
+ bucket(s)->stopped = true;
+ play(s);
+ } else {
+ if (--(bucket(s)->numplaying) == 0)
+ bucket(s)->stopped = true;
+ inprogress->remove(s);
+ }
+ }
+}
+
+void QAuServerNAS::play(QSound* s)
+{
+ if (nas) {
+ ++(bucket(s)->numplaying);
+ if (!bucket(s)->stopped) {
+ stop(s);
+ }
+
+ bucket(s)->stopped = false;
+ if (!inprogress)
+ inprogress = new AuServerHash;
+ inprogress->insert(s,this);
+ int iv=100;
+ AuFixedPoint volume=AuFixedPointFromFraction(iv,100);
+ QAuBucketNAS *b = bucket(s);
+ AuSoundPlayFromBucket(nas, b->id, AuNone, volume,
+ callback, s, 0, &b->flow, NULL, NULL, NULL);
+ AuFlush(nas);
+ dataReceived();
+ AuFlush(nas);
+ qApp->flush();
+ }
+}
+
+void QAuServerNAS::stop(QSound* s)
+{
+ if (nas && !bucket(s)->stopped) {
+ bucket(s)->stopped = true;
+ AuStopFlow(nas, bucket(s)->flow, NULL);
+ AuFlush(nas);
+ dataReceived();
+ AuFlush(nas);
+ qApp->flush();
+ }
+}
+
+void QAuServerNAS::init(QSound* s)
+{
+ connect(s, SIGNAL(destroyed(QObject*)),
+ this, SLOT(soundDestroyed(QObject*)));
+
+ if (nas) {
+ AuBucketID b_id =
+ AuSoundCreateBucketFromFile(nas, s->fileName().toLocal8Bit().constData(),
+ 0 /*AuAccessAllMasks*/, NULL, NULL);
+ setBucket(s, new QAuBucketNAS(b_id));
+ }
+}
+
+bool QAuServerNAS::okay()
+{
+ return !!nas;
+}
+
+void QAuServerNAS::dataReceived()
+{
+ AuHandleEvents(nas);
+}
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include "qsound_x11.moc"
+QT_END_INCLUDE_NAMESPACE
+
+#endif
+
+
+class QAuServerNull : public QAuServer
+{
+public:
+ QAuServerNull(QObject* parent);
+
+ void play(const QString&) { }
+ void play(QSound*s) { while(decLoop(s) > 0) /* nothing */ ; }
+ void stop(QSound*) { }
+ bool okay() { return false; }
+};
+
+QAuServerNull::QAuServerNull(QObject* parent)
+ : QAuServer(parent)
+{
+}
+
+
+QAuServer* qt_new_audio_server()
+{
+#ifndef QT_NO_NAS
+ QAuServer* s = new QAuServerNAS(qApp);
+ if (s->okay())
+ return s;
+ else
+ delete s;
+#endif
+ return new QAuServerNull(qApp);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SOUND
diff --git a/src/widgets/platforms/x11/qt_x11_p.h b/src/widgets/platforms/x11/qt_x11_p.h
new file mode 100644
index 0000000000..ea808fb8a2
--- /dev/null
+++ b/src/widgets/platforms/x11/qt_x11_p.h
@@ -0,0 +1,757 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT_X11_P_H
+#define QT_X11_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtGui/qwindowdefs.h"
+#include "QtCore/qlist.h"
+#include "QtCore/qvariant.h"
+
+// the following is necessary to work around breakage in many versions
+// of XFree86's Xlib.h still in use
+// ### which versions?
+#if defined(_XLIB_H_) // crude hack, but...
+#error "cannot include <X11/Xlib.h> before this file"
+#endif
+#define XRegisterIMInstantiateCallback qt_XRegisterIMInstantiateCallback
+#define XUnregisterIMInstantiateCallback qt_XUnregisterIMInstantiateCallback
+#define XSetIMValues qt_XSetIMValues
+#include <X11/Xlib.h>
+#undef XRegisterIMInstantiateCallback
+#undef XUnregisterIMInstantiateCallback
+#undef XSetIMValues
+
+#include <X11/Xutil.h>
+#include <X11/Xos.h>
+#ifdef index
+# undef index
+#endif
+#ifdef rindex
+# undef rindex
+#endif
+#ifdef Q_OS_VXWORS
+# ifdef open
+# undef open
+# endif
+# ifdef getpid
+# undef getpid
+# endif
+#endif // Q_OS_VXWORKS
+#include <X11/Xatom.h>
+
+//#define QT_NO_SHAPE
+#ifdef QT_NO_SHAPE
+# define XShapeCombineRegion(a,b,c,d,e,f,g)
+# define XShapeCombineMask(a,b,c,d,e,f,g)
+#else
+# include <X11/extensions/shape.h>
+#endif // QT_NO_SHAPE
+
+
+#if !defined (QT_NO_TABLET)
+# include <X11/extensions/XInput.h>
+#if defined (Q_OS_IRIX)
+# include <X11/extensions/SGIMisc.h>
+# include <wacom.h>
+#endif
+#endif // QT_NO_TABLET
+
+
+// #define QT_NO_XINERAMA
+#ifndef QT_NO_XINERAMA
+# if 0 // ### Xsun, but how to detect it?
+// Xinerama is only supported in Solaris 7 with patches 107648/108376 and
+// Solaris 8 or above which introduce the X11R6.4 Xserver.
+// To switch the Xinerama functionality on, you need to add the "+xinerama"
+// argument to the Xsun start line.
+// At least Solaris 7 and 8 are missing Xinerama system headers and function
+// declarations (bug 4284701).
+// The Xinerama API is not documented. In theory it could change but it
+// probably won't because Sun are using it in at least dtlogin (bug 4221829).
+extern "C" Bool XPanoramiXQueryExtension(
+ Display*,
+ int*,
+ int*
+);
+extern "C" Status XPanoramiXQueryVersion(
+ Display*,
+ int*,
+ int*
+);
+extern "C" Status XPanoramiXGetState(
+ Display*,
+ Drawable,
+ XPanoramiXInfo*
+);
+extern "C" Status XPanoramiXGetScreenCount(
+ Display *,
+ Drawable,
+ XPanoramiXInfo*
+);
+extern "C" Status XPanoramiXGetScreenSize(
+ Display*,
+ Drawable,
+ int,
+ XPanoramiXInfo*
+);
+# else // XFree86
+// XFree86 does not C++ify Xinerama (at least up to XFree86 4.0.3).
+extern "C" {
+# include <X11/extensions/Xinerama.h>
+}
+# endif
+#endif // QT_NO_XINERAMA
+
+// #define QT_NO_XRANDR
+#ifndef QT_NO_XRANDR
+# include <X11/extensions/Xrandr.h>
+#endif // QT_NO_XRANDR
+
+// #define QT_NO_XRENDER
+#ifndef QT_NO_XRENDER
+# include <X11/extensions/Xrender.h>
+#endif // QT_NO_XRENDER
+
+#ifndef QT_NO_XSYNC
+extern "C" {
+# include "X11/extensions/sync.h"
+}
+#endif
+
+// #define QT_NO_XKB
+#ifndef QT_NO_XKB
+# include <X11/XKBlib.h>
+#endif // QT_NO_XKB
+
+
+#if !defined(XlibSpecificationRelease)
+# define X11R4
+typedef char *XPointer;
+#else
+# undef X11R4
+#endif
+
+// #define QT_NO_XIM
+#if defined(X11R4)
+// X11R4 does not have XIM
+#define QT_NO_XIM
+#elif defined(Q_OS_OSF) && (XlibSpecificationRelease < 6)
+// broken in Xlib up to OSF/1 3.2
+#define QT_NO_XIM
+#elif defined(Q_OS_AIX)
+// broken in Xlib up to what version of AIX?
+#define QT_NO_XIM
+#elif defined(QT_NO_DEBUG) && defined(Q_OS_IRIX)
+// XmbLookupString broken on IRIX
+// XCreateIC broken when compiling -64 on IRIX 6.5.2
+#define QT_NO_XIM
+#elif defined(Q_OS_HPUX) && defined(__LP64__)
+// XCreateIC broken when compiling 64-bit ELF on HP-UX 11.0
+#define QT_NO_XIM
+#elif defined(Q_OS_SCO)
+// ### suggested by user...
+// ### #define QT_NO_XIM
+#endif // QT_NO_XIM
+
+#ifndef QT_NO_XFIXES
+typedef Bool (*PtrXFixesQueryExtension)(Display *, int *, int *);
+typedef Status (*PtrXFixesQueryVersion)(Display *, int *, int *);
+typedef void (*PtrXFixesSetCursorName)(Display *dpy, Cursor cursor, const char *name);
+typedef void (*PtrXFixesSelectSelectionInput)(Display *dpy, Window win, Atom selection, unsigned long eventMask);
+#endif // QT_NO_XFIXES
+
+#ifndef QT_NO_XCURSOR
+#include <X11/Xcursor/Xcursor.h>
+typedef Cursor (*PtrXcursorLibraryLoadCursor)(Display *, const char *);
+#endif // QT_NO_XCURSOR
+
+#ifndef QT_NO_XINERAMA
+typedef Bool (*PtrXineramaQueryExtension)(Display *dpy, int *event_base, int *error_base);
+typedef Bool (*PtrXineramaIsActive)(Display *dpy);
+typedef XineramaScreenInfo *(*PtrXineramaQueryScreens)(Display *dpy, int *number);
+#endif // QT_NO_XINERAMA
+
+#ifndef QT_NO_XRANDR
+typedef void (*PtrXRRSelectInput)(Display *, Window, int);
+typedef int (*PtrXRRUpdateConfiguration)(XEvent *);
+typedef int (*PtrXRRRootToScreen)(Display *, Window);
+typedef Bool (*PtrXRRQueryExtension)(Display *, int *, int *);
+typedef XRRScreenSize *(*PtrXRRSizes)(Display *, int, int *);
+#endif // QT_NO_XRANDR
+
+#ifndef QT_NO_XINPUT
+typedef int (*PtrXCloseDevice)(Display *, XDevice *);
+typedef XDeviceInfo* (*PtrXListInputDevices)(Display *, int *);
+typedef XDevice* (*PtrXOpenDevice)(Display *, XID);
+typedef void (*PtrXFreeDeviceList)(XDeviceInfo *);
+typedef int (*PtrXSelectExtensionEvent)(Display *, Window, XEventClass *, int);
+#endif // QT_NO_XINPUT
+
+/*
+ * Solaris patch 108652-47 and higher fixes crases in
+ * XRegisterIMInstantiateCallback, but the function doesn't seem to
+ * work.
+ *
+ * Instead, we disabled R6 input, and open the input method
+ * immediately at application start.
+ */
+#if !defined(QT_NO_XIM) && (XlibSpecificationRelease >= 6) && \
+ !defined(Q_OS_SOLARIS)
+#define USE_X11R6_XIM
+
+//######### XFree86 has wrong declarations for XRegisterIMInstantiateCallback
+//######### and XUnregisterIMInstantiateCallback in at least version 3.3.2.
+//######### Many old X11R6 header files lack XSetIMValues.
+//######### Therefore, we have to declare these functions ourselves.
+
+extern "C" Bool XRegisterIMInstantiateCallback(
+ Display*,
+ struct _XrmHashBucketRec*,
+ char*,
+ char*,
+ XIMProc, //XFree86 has XIDProc, which has to be wrong
+ XPointer
+);
+
+extern "C" Bool XUnregisterIMInstantiateCallback(
+ Display*,
+ struct _XrmHashBucketRec*,
+ char*,
+ char*,
+ XIMProc, //XFree86 has XIDProc, which has to be wrong
+ XPointer
+);
+
+extern "C" char *XSetIMValues(XIM /* im */, ...);
+
+#endif
+
+#ifndef QT_NO_FONTCONFIG
+#include <fontconfig/fontconfig.h>
+#endif
+
+#ifndef QT_NO_XIM
+// some platforms (eg. Solaris 2.51) don't have these defines in Xlib.h
+#ifndef XNResetState
+#define XNResetState "resetState"
+#endif
+#ifndef XIMPreserveState
+#define XIMPreserveState (1L<<1)
+#endif
+#endif
+
+
+#ifndef X11R4
+# include <X11/Xlocale.h>
+#endif // X11R4
+
+
+#ifndef QT_NO_MITSHM
+# include <X11/extensions/XShm.h>
+#endif // QT_NO_MITSHM
+
+QT_BEGIN_NAMESPACE
+
+class QWidget;
+
+struct QX11InfoData {
+ uint ref;
+ int screen;
+ int dpiX;
+ int dpiY;
+ int depth;
+ int cells;
+ Colormap colormap;
+ Visual *visual;
+ bool defaultColormap;
+ bool defaultVisual;
+ int subpixel;
+};
+
+class QDrag;
+struct QXdndDropTransaction
+{
+ Time timestamp;
+ Window target;
+ Window proxy_target;
+ QWidget *targetWidget;
+ QWidget *embedding_widget;
+ QDrag *object;
+};
+
+class QMimeData;
+
+struct QX11Data;
+extern Q_WIDGETS_EXPORT QX11Data *qt_x11Data;
+
+enum DesktopEnvironment {
+ DE_UNKNOWN,
+ DE_KDE,
+ DE_GNOME,
+ DE_CDE,
+ DE_MEEGO_COMPOSITOR,
+ DE_4DWM
+};
+
+struct QX11Data
+{
+ static Qt::KeyboardModifiers translateModifiers(int s);
+
+ Window findClientWindow(Window, Atom, bool);
+
+ // from qclipboard_x11.cpp
+ bool clipboardWaitForEvent(Window win, int type, XEvent *event, int timeout);
+ bool clipboardReadProperty(Window win, Atom property, bool deleteProperty,
+ QByteArray *buffer, int *size, Atom *type, int *format);
+ QByteArray clipboardReadIncrementalProperty(Window win, Atom property, int nbytes, bool nullterm);
+
+ // from qdnd_x11.cpp
+ bool dndEnable(QWidget* w, bool on);
+ static void xdndSetup();
+ void xdndHandleEnter(QWidget *, const XEvent *, bool);
+ void xdndHandlePosition(QWidget *, const XEvent *, bool);
+ void xdndHandleStatus(QWidget *, const XEvent *, bool);
+ void xdndHandleLeave(QWidget *, const XEvent *, bool);
+ void xdndHandleDrop(QWidget *, const XEvent *, bool);
+ void xdndHandleFinished(QWidget *, const XEvent *, bool);
+ void xdndHandleSelectionRequest(const XSelectionRequestEvent *);
+ static bool xdndHandleBadwindow();
+ QByteArray xdndAtomToString(Atom a);
+ Atom xdndStringToAtom(const char *);
+
+ QString xdndMimeAtomToString(Atom a);
+ Atom xdndMimeStringToAtom(const QString &mimeType);
+ QStringList xdndMimeFormatsForAtom(Atom a);
+ bool xdndMimeDataForAtom(Atom a, QMimeData *mimeData, QByteArray *data, Atom *atomFormat, int *dataFormat);
+ QList<Atom> xdndMimeAtomsForFormat(const QString &format);
+ QVariant xdndMimeConvertToFormat(Atom a, const QByteArray &data, const QString &format, QVariant::Type requestedType, const QByteArray &encoding);
+ Atom xdndMimeAtomForFormat(const QString &format, QVariant::Type requestedType, const QList<Atom> &atoms, QByteArray *requestedEncoding);
+
+ QList<QXdndDropTransaction> dndDropTransactions;
+
+ // from qmotifdnd_x11.cpp
+ void motifdndHandle(QWidget *, const XEvent *, bool);
+ void motifdndEnable(QWidget *, bool);
+ QVariant motifdndObtainData(const char *format);
+ QByteArray motifdndFormat(int n);
+ bool motifdnd_active;
+
+ Display *display;
+ char *displayName;
+ bool foreignDisplay;
+ // current focus model
+ enum {
+ FM_Unknown = -1,
+ FM_Other = 0,
+ FM_PointerRoot = 1
+ };
+ int focus_model;
+
+ // true if Qt is compiled w/ RANDR support and RANDR is supported on the connected Display
+ bool use_xrandr;
+ int xrandr_major;
+ int xrandr_eventbase;
+ int xrandr_errorbase;
+
+ // true if Qt is compiled w/ RENDER support and RENDER is supported on the connected Display
+ bool use_xrender;
+ int xrender_major;
+ int xrender_version;
+
+ // true if Qt is compiled w/ XFIXES support and XFIXES is supported on the connected Display
+ bool use_xfixes;
+ int xfixes_major;
+ int xfixes_eventbase;
+ int xfixes_errorbase;
+
+#ifndef QT_NO_XFIXES
+ PtrXFixesQueryExtension ptrXFixesQueryExtension;
+ PtrXFixesQueryVersion ptrXFixesQueryVersion;
+ PtrXFixesSetCursorName ptrXFixesSetCursorName;
+ PtrXFixesSelectSelectionInput ptrXFixesSelectSelectionInput;
+#endif
+
+#ifndef QT_NO_XINPUT
+ PtrXCloseDevice ptrXCloseDevice;
+ PtrXListInputDevices ptrXListInputDevices;
+ PtrXOpenDevice ptrXOpenDevice;
+ PtrXFreeDeviceList ptrXFreeDeviceList;
+ PtrXSelectExtensionEvent ptrXSelectExtensionEvent;
+#endif // QT_NO_XINPUT
+
+
+ // true if Qt is compiled w/ MIT-SHM support and MIT-SHM is supported on the connected Display
+ bool use_mitshm;
+ bool use_mitshm_pixmaps;
+ int mitshm_major;
+
+ // true if Qt is compiled w/ Tablet support and we have a tablet.
+ bool use_xinput;
+ int xinput_major;
+ int xinput_eventbase;
+ int xinput_errorbase;
+
+ // for XKEYBOARD support
+ bool use_xkb;
+ int xkb_major;
+ int xkb_eventbase;
+ int xkb_errorbase;
+
+ QList<QWidget *> deferred_map;
+ struct ScrollInProgress {
+ long id;
+ QWidget* scrolled_widget;
+ int dx, dy;
+ };
+ long sip_serial;
+ QList<ScrollInProgress> sip_list;
+
+ // window managers list of supported "stuff"
+ Atom *net_supported_list;
+ // list of virtual root windows
+ Window *net_virtual_root_list;
+ // client leader window
+ Window wm_client_leader;
+
+ QX11InfoData *screens;
+ Visual **argbVisuals;
+ Colormap *argbColormaps;
+ int screenCount;
+ int defaultScreen;
+
+ Time time;
+ Time userTime;
+
+ QString default_im;
+
+ // starts to ignore bad window errors from X
+ static inline void ignoreBadwindow() {
+ qt_x11Data->ignore_badwindow = true;
+ qt_x11Data->seen_badwindow = false;
+ }
+
+ // ends ignoring bad window errors and returns whether an error had happened.
+ static inline bool badwindow() {
+ qt_x11Data->ignore_badwindow = false;
+ return qt_x11Data->seen_badwindow;
+ }
+
+ bool ignore_badwindow;
+ bool seen_badwindow;
+
+ // options
+ int visual_class;
+ int visual_id;
+ int color_count;
+ bool custom_cmap;
+
+ // outside visual/colormap
+ Visual *visual;
+ Colormap colormap;
+
+#ifndef QT_NO_XRENDER
+ enum { solid_fill_count = 16 };
+ struct SolidFills {
+ XRenderColor color;
+ int screen;
+ Picture picture;
+ } solid_fills[solid_fill_count];
+ enum { pattern_fill_count = 16 };
+ struct PatternFills {
+ XRenderColor color;
+ XRenderColor bg_color;
+ int screen;
+ int style;
+ bool opaque;
+ Picture picture;
+ } pattern_fills[pattern_fill_count];
+ Picture getSolidFill(int screen, const QColor &c);
+ XRenderColor preMultiply(const QColor &c);
+#endif
+
+ bool has_fontconfig;
+ qreal fc_scale;
+ bool fc_antialias;
+ int fc_hint_style;
+
+ char *startupId;
+
+ DesktopEnvironment desktopEnvironment : 8;
+ uint desktopVersion : 8; /* Used only for KDE */
+
+ /* Warning: if you modify this list, modify the names of atoms in qapplication_x11.cpp as well! */
+ enum X11Atom {
+ // window-manager <-> client protocols
+ WM_PROTOCOLS,
+ WM_DELETE_WINDOW,
+ WM_TAKE_FOCUS,
+ _NET_WM_PING,
+ _NET_WM_CONTEXT_HELP,
+ _NET_WM_SYNC_REQUEST,
+ _NET_WM_SYNC_REQUEST_COUNTER,
+
+ // ICCCM window state
+ WM_STATE,
+ WM_CHANGE_STATE,
+
+ // Session management
+ WM_CLIENT_LEADER,
+ WM_WINDOW_ROLE,
+ SM_CLIENT_ID,
+
+ // Clipboard
+ CLIPBOARD,
+ INCR,
+ TARGETS,
+ MULTIPLE,
+ TIMESTAMP,
+ SAVE_TARGETS,
+ CLIP_TEMPORARY,
+ _QT_SELECTION,
+ _QT_CLIPBOARD_SENTINEL,
+ _QT_SELECTION_SENTINEL,
+ CLIPBOARD_MANAGER,
+
+ RESOURCE_MANAGER,
+
+ _XSETROOT_ID,
+
+ _QT_SCROLL_DONE,
+ _QT_INPUT_ENCODING,
+
+ _MOTIF_WM_HINTS,
+
+ DTWM_IS_RUNNING,
+ ENLIGHTENMENT_DESKTOP,
+ _DT_SAVE_MODE,
+ _SGI_DESKS_MANAGER,
+
+ // EWMH (aka NETWM)
+ _NET_SUPPORTED,
+ _NET_VIRTUAL_ROOTS,
+ _NET_WORKAREA,
+
+ _NET_MOVERESIZE_WINDOW,
+ _NET_WM_MOVERESIZE,
+
+ _NET_WM_NAME,
+ _NET_WM_ICON_NAME,
+ _NET_WM_ICON,
+
+ _NET_WM_PID,
+
+ _NET_WM_WINDOW_OPACITY,
+
+ _NET_WM_STATE,
+ _NET_WM_STATE_ABOVE,
+ _NET_WM_STATE_BELOW,
+ _NET_WM_STATE_FULLSCREEN,
+ _NET_WM_STATE_MAXIMIZED_HORZ,
+ _NET_WM_STATE_MAXIMIZED_VERT,
+ _NET_WM_STATE_MODAL,
+ _NET_WM_STATE_STAYS_ON_TOP,
+ _NET_WM_STATE_DEMANDS_ATTENTION,
+
+ _NET_WM_USER_TIME,
+ _NET_WM_USER_TIME_WINDOW,
+ _NET_WM_FULL_PLACEMENT,
+
+ _NET_WM_WINDOW_TYPE,
+ _NET_WM_WINDOW_TYPE_DESKTOP,
+ _NET_WM_WINDOW_TYPE_DOCK,
+ _NET_WM_WINDOW_TYPE_TOOLBAR,
+ _NET_WM_WINDOW_TYPE_MENU,
+ _NET_WM_WINDOW_TYPE_UTILITY,
+ _NET_WM_WINDOW_TYPE_SPLASH,
+ _NET_WM_WINDOW_TYPE_DIALOG,
+ _NET_WM_WINDOW_TYPE_DROPDOWN_MENU,
+ _NET_WM_WINDOW_TYPE_POPUP_MENU,
+ _NET_WM_WINDOW_TYPE_TOOLTIP,
+ _NET_WM_WINDOW_TYPE_NOTIFICATION,
+ _NET_WM_WINDOW_TYPE_COMBO,
+ _NET_WM_WINDOW_TYPE_DND,
+ _NET_WM_WINDOW_TYPE_NORMAL,
+ _KDE_NET_WM_WINDOW_TYPE_OVERRIDE,
+
+ _KDE_NET_WM_FRAME_STRUT,
+
+ _NET_STARTUP_INFO,
+ _NET_STARTUP_INFO_BEGIN,
+
+ _NET_SUPPORTING_WM_CHECK,
+
+ _NET_WM_CM_S0,
+
+ _NET_SYSTEM_TRAY_VISUAL,
+
+ _NET_ACTIVE_WINDOW,
+
+ // Property formats
+ COMPOUND_TEXT,
+ TEXT,
+ UTF8_STRING,
+
+ // Xdnd
+ XdndEnter,
+ XdndPosition,
+ XdndStatus,
+ XdndLeave,
+ XdndDrop,
+ XdndFinished,
+ XdndTypelist,
+ XdndActionList,
+
+ XdndSelection,
+
+ XdndAware,
+ XdndProxy,
+
+ XdndActionCopy,
+ XdndActionLink,
+ XdndActionMove,
+ XdndActionPrivate,
+
+ // Motif DND
+ _MOTIF_DRAG_AND_DROP_MESSAGE,
+ _MOTIF_DRAG_INITIATOR_INFO,
+ _MOTIF_DRAG_RECEIVER_INFO,
+ _MOTIF_DRAG_WINDOW,
+ _MOTIF_DRAG_TARGETS,
+
+ XmTRANSFER_SUCCESS,
+ XmTRANSFER_FAILURE,
+
+ // Xkb
+ _XKB_RULES_NAMES,
+
+ // XEMBED
+ _XEMBED,
+ _XEMBED_INFO,
+
+ XWacomStylus,
+ XWacomCursor,
+ XWacomEraser,
+
+ XTabletStylus,
+ XTabletEraser,
+
+ NPredefinedAtoms,
+
+ _QT_SETTINGS_TIMESTAMP = NPredefinedAtoms,
+ NAtoms
+ };
+ Atom atoms[NAtoms];
+
+ bool isSupportedByWM(Atom atom);
+
+ bool compositingManagerRunning;
+
+#ifndef QT_NO_XCURSOR
+ PtrXcursorLibraryLoadCursor ptrXcursorLibraryLoadCursor;
+#endif // QT_NO_XCURSOR
+
+#ifndef QT_NO_XINERAMA
+ PtrXineramaQueryExtension ptrXineramaQueryExtension;
+ PtrXineramaIsActive ptrXineramaIsActive;
+ PtrXineramaQueryScreens ptrXineramaQueryScreens;
+#endif // QT_NO_XINERAMA
+
+#ifndef QT_NO_XRANDR
+ PtrXRRSelectInput ptrXRRSelectInput;
+ PtrXRRUpdateConfiguration ptrXRRUpdateConfiguration;
+ PtrXRRRootToScreen ptrXRRRootToScreen;
+ PtrXRRQueryExtension ptrXRRQueryExtension;
+ PtrXRRSizes ptrXRRSizes;
+#endif // QT_NO_XRANDR
+};
+
+extern QX11Data *qt_x11Data;
+#define ATOM(x) qt_x11Data->atoms[QX11Data::x]
+#define X11 qt_x11Data
+
+// rename a couple of X defines to get rid of name clashes
+// resolve the conflict between X11's FocusIn and QEvent::FocusIn
+enum {
+ XFocusOut = FocusOut,
+ XFocusIn = FocusIn,
+ XKeyPress = KeyPress,
+ XKeyRelease = KeyRelease,
+ XNone = None,
+ XRevertToParent = RevertToParent,
+ XGrayScale = GrayScale,
+ XCursorShape = CursorShape
+};
+#undef FocusOut
+#undef FocusIn
+#undef KeyPress
+#undef KeyRelease
+#undef None
+#undef RevertToParent
+#undef GrayScale
+#undef CursorShape
+
+#ifdef FontChange
+#undef FontChange
+#endif
+
+Q_DECLARE_TYPEINFO(XPoint, Q_PRIMITIVE_TYPE);
+Q_DECLARE_TYPEINFO(XRectangle, Q_PRIMITIVE_TYPE);
+Q_DECLARE_TYPEINFO(XChar2b, Q_PRIMITIVE_TYPE);
+#ifndef QT_NO_XRENDER
+Q_DECLARE_TYPEINFO(XGlyphElt32, Q_PRIMITIVE_TYPE);
+#endif
+
+
+QT_END_NAMESPACE
+
+#endif // QT_X11_P_H
diff --git a/src/widgets/platforms/x11/qwidget_x11.cpp b/src/widgets/platforms/x11/qwidget_x11.cpp
new file mode 100644
index 0000000000..5bb59b26c9
--- /dev/null
+++ b/src/widgets/platforms/x11/qwidget_x11.cpp
@@ -0,0 +1,3149 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qevent.h"
+#include "qwidget.h"
+#include "qdesktopwidget.h"
+#include "qapplication.h"
+#include "qapplication_p.h"
+#include "qnamespace.h"
+#include "qpainter.h"
+#include "qbitmap.h"
+#include "qlayout.h"
+#include "qtextcodec.h"
+#include "qelapsedtimer.h"
+#include "qcursor.h"
+#include "qstack.h"
+#include "qcolormap.h"
+#include "qdebug.h"
+#include "qmenu.h"
+#include "private/qmenu_p.h"
+#include "private/qbackingstore_p.h"
+#include "private/qwindowsurface_x11_p.h"
+
+//extern bool qt_sendSpontaneousEvent(QObject *, QEvent *); //qapplication_x11.cpp
+
+#include <private/qpixmap_x11_p.h>
+#include <private/qpaintengine_x11_p.h>
+#include "qt_x11_p.h"
+#include "qx11info_x11.h"
+
+#include <stdlib.h>
+
+//#define ALIEN_DEBUG
+
+// defined in qapplication_x11.cpp
+//bool qt_wstate_iconified(WId);
+//void qt_updated_rootinfo();
+
+
+#if !defined(QT_NO_IM)
+#include "qinputcontext.h"
+#include "qinputcontextfactory.h"
+#endif
+
+#include "qwidget_p.h"
+
+#define XCOORD_MAX 16383
+#define WRECT_MAX 8191
+
+QT_BEGIN_NAMESPACE
+
+extern bool qt_nograb();
+
+QWidget *QWidgetPrivate::mouseGrabber = 0;
+QWidget *QWidgetPrivate::keyboardGrabber = 0;
+
+void qt_net_remove_user_time(QWidget *tlw);
+void qt_net_update_user_time(QWidget *tlw, unsigned long timestamp);
+
+int qt_x11_create_desktop_on_screen = -1;
+
+extern void qt_net_update_user_time(QWidget *tlw, unsigned long timestamp);
+
+// MWM support
+struct QtMWMHints {
+ ulong flags, functions, decorations;
+ long input_mode;
+ ulong status;
+};
+
+enum {
+ MWM_HINTS_FUNCTIONS = (1L << 0),
+
+ MWM_FUNC_ALL = (1L << 0),
+ MWM_FUNC_RESIZE = (1L << 1),
+ MWM_FUNC_MOVE = (1L << 2),
+ MWM_FUNC_MINIMIZE = (1L << 3),
+ MWM_FUNC_MAXIMIZE = (1L << 4),
+ MWM_FUNC_CLOSE = (1L << 5),
+
+ MWM_HINTS_DECORATIONS = (1L << 1),
+
+ MWM_DECOR_ALL = (1L << 0),
+ MWM_DECOR_BORDER = (1L << 1),
+ MWM_DECOR_RESIZEH = (1L << 2),
+ MWM_DECOR_TITLE = (1L << 3),
+ MWM_DECOR_MENU = (1L << 4),
+ MWM_DECOR_MINIMIZE = (1L << 5),
+ MWM_DECOR_MAXIMIZE = (1L << 6),
+
+ MWM_HINTS_INPUT_MODE = (1L << 2),
+
+ MWM_INPUT_MODELESS = 0L,
+ MWM_INPUT_PRIMARY_APPLICATION_MODAL = 1L,
+ MWM_INPUT_FULL_APPLICATION_MODAL = 3L
+};
+
+
+static QtMWMHints getMWMHints(Display *display, Window window)
+{
+ QtMWMHints mwmhints;
+
+ Atom type;
+ int format;
+ ulong nitems, bytesLeft;
+ uchar *data = 0;
+ if ((XGetWindowProperty(display, window, ATOM(_MOTIF_WM_HINTS), 0, 5, false,
+ ATOM(_MOTIF_WM_HINTS), &type, &format, &nitems, &bytesLeft,
+ &data) == Success)
+ && (type == ATOM(_MOTIF_WM_HINTS)
+ && format == 32
+ && nitems >= 5)) {
+ mwmhints = *(reinterpret_cast<QtMWMHints *>(data));
+ } else {
+ mwmhints.flags = 0L;
+ mwmhints.functions = MWM_FUNC_ALL;
+ mwmhints.decorations = MWM_DECOR_ALL;
+ mwmhints.input_mode = 0L;
+ mwmhints.status = 0L;
+ }
+
+ if (data)
+ XFree(data);
+
+ return mwmhints;
+}
+
+static void SetMWMHints(Display *display, Window window, const QtMWMHints &mwmhints)
+{
+ if (mwmhints.flags != 0l) {
+ XChangeProperty(display, window, ATOM(_MOTIF_WM_HINTS), ATOM(_MOTIF_WM_HINTS), 32,
+ PropModeReplace, (unsigned char *) &mwmhints, 5);
+ } else {
+ XDeleteProperty(display, window, ATOM(_MOTIF_WM_HINTS));
+ }
+}
+
+// Returns true if we should set WM_TRANSIENT_FOR on \a w
+static inline bool isTransient(const QWidget *w)
+{
+ return ((w->windowType() == Qt::Dialog
+ || w->windowType() == Qt::Sheet
+ || w->windowType() == Qt::Tool
+ || w->windowType() == Qt::SplashScreen
+ || w->windowType() == Qt::ToolTip
+ || w->windowType() == Qt::Drawer
+ || w->windowType() == Qt::Popup)
+ && !w->testAttribute(Qt::WA_X11BypassTransientForHint));
+}
+
+static void do_size_hints(QWidget* widget, QWExtra *x);
+
+/*****************************************************************************
+ QWidget member functions
+ *****************************************************************************/
+
+const uint stdWidgetEventMask = // X event mask
+ (uint)(
+ KeyPressMask | KeyReleaseMask |
+ ButtonPressMask | ButtonReleaseMask |
+ KeymapStateMask |
+ ButtonMotionMask | PointerMotionMask |
+ EnterWindowMask | LeaveWindowMask |
+ FocusChangeMask |
+ ExposureMask |
+ PropertyChangeMask |
+ StructureNotifyMask
+ );
+
+const uint stdDesktopEventMask = // X event mask
+ (uint)(
+ KeymapStateMask |
+ EnterWindowMask | LeaveWindowMask |
+ PropertyChangeMask
+ );
+
+
+/*
+ The qt_ functions below are implemented in qwidgetcreate_x11.cpp.
+*/
+
+Window qt_XCreateWindow(const QWidget *creator,
+ Display *display, Window parent,
+ int x, int y, uint w, uint h,
+ int borderwidth, int depth,
+ uint windowclass, Visual *visual,
+ ulong valuemask, XSetWindowAttributes *attributes);
+Window qt_XCreateSimpleWindow(const QWidget *creator,
+ Display *display, Window parent,
+ int x, int y, uint w, uint h, int borderwidth,
+ ulong border, ulong background);
+void qt_XDestroyWindow(const QWidget *destroyer,
+ Display *display, Window window);
+
+
+static void qt_insert_sip(QWidget* scrolled_widget, int dx, int dy)
+{
+ if (!scrolled_widget->isWindow() && !scrolled_widget->internalWinId())
+ return;
+ QX11Data::ScrollInProgress sip = { X11->sip_serial++, scrolled_widget, dx, dy };
+ X11->sip_list.append(sip);
+
+ XClientMessageEvent client_message;
+ client_message.type = ClientMessage;
+ client_message.window = scrolled_widget->internalWinId();
+ client_message.format = 32;
+ client_message.message_type = ATOM(_QT_SCROLL_DONE);
+ client_message.data.l[0] = sip.id;
+
+ XSendEvent(X11->display, scrolled_widget->internalWinId(), False, NoEventMask,
+ (XEvent*)&client_message);
+}
+
+static int qt_sip_count(QWidget* scrolled_widget)
+{
+ int sips=0;
+
+ for (int i = 0; i < X11->sip_list.size(); ++i) {
+ const QX11Data::ScrollInProgress &sip = X11->sip_list.at(i);
+ if (sip.scrolled_widget == scrolled_widget)
+ sips++;
+ }
+
+ return sips;
+}
+
+static void create_wm_client_leader()
+{
+ if (X11->wm_client_leader) return;
+
+ X11->wm_client_leader =
+ XCreateSimpleWindow(X11->display,
+ QX11Info::appRootWindow(),
+ 0, 0, 1, 1, 0, 0, 0);
+
+ // set client leader property to itself
+ XChangeProperty(X11->display,
+ X11->wm_client_leader, ATOM(WM_CLIENT_LEADER),
+ XA_WINDOW, 32, PropModeReplace,
+ (unsigned char *)&X11->wm_client_leader, 1);
+
+#ifndef QT_NO_SESSIONMANAGER
+ // If we are session managed, inform the window manager about it
+ QByteArray session = qApp->sessionId().toLatin1();
+ if (!session.isEmpty()) {
+ XChangeProperty(X11->display,
+ X11->wm_client_leader, ATOM(SM_CLIENT_ID),
+ XA_STRING, 8, PropModeReplace,
+ (unsigned char *)session.data(), session.size());
+ }
+#endif
+}
+
+/*!
+ \internal
+ Update the X11 cursor of the widget w.
+ \a force is true if this function is called from dispatchEnterLeave, it means that the
+ mouse is actually directly under this widget.
+ */
+void qt_x11_enforce_cursor(QWidget * w, bool force)
+{
+ if (!w->testAttribute(Qt::WA_WState_Created))
+ return;
+
+ static QPointer<QWidget> lastUnderMouse = 0;
+ if (force) {
+ lastUnderMouse = w;
+ } else if (lastUnderMouse && lastUnderMouse->effectiveWinId() == w->effectiveWinId()) {
+ w = lastUnderMouse;
+ } else if (!w->internalWinId()) {
+ return; //the mouse is not under this widget, and it's not native, so don't change it
+ }
+
+ while (!w->internalWinId() && w->parentWidget() && !w->isWindow() && !w->testAttribute(Qt::WA_SetCursor))
+ w = w->parentWidget();
+
+ QWidget *nativeParent = w;
+ if (!w->internalWinId())
+ nativeParent = w->nativeParentWidget();
+ // This does the same as effectiveWinId(), but since it is possible
+ // to not have a native parent widget due to a special hack in
+ // qwidget for reparenting widgets to a different X11 screen,
+ // added additional check to make sure native parent widget exists.
+ if (!nativeParent || !nativeParent->internalWinId())
+ return;
+ WId winid = nativeParent->internalWinId();
+
+ if (w->isWindow() || w->testAttribute(Qt::WA_SetCursor)) {
+#ifndef QT_NO_CURSOR
+ QCursor *oc = QApplication::overrideCursor();
+ if (oc) {
+ XDefineCursor(X11->display, winid, oc->handle());
+ } else if (w->isEnabled()) {
+ XDefineCursor(X11->display, winid, w->cursor().handle());
+ } else {
+ // enforce the windows behavior of clearing the cursor on
+ // disabled widgets
+ XDefineCursor(X11->display, winid, XNone);
+ }
+#endif
+ } else {
+ XDefineCursor(X11->display, winid, XNone);
+ }
+}
+
+Q_WIDGETS_EXPORT void qt_x11_enforce_cursor(QWidget * w)
+{
+ qt_x11_enforce_cursor(w, false);
+}
+
+void qt_x11_wait_for_window_manager(QWidget *w, bool sendPostedEvents)
+{
+ if (!w || (!w->isWindow() && !w->internalWinId()))
+ return;
+ QApplication::flush();
+ XEvent ev;
+ QElapsedTimer t;
+ t.start();
+ static const int maximumWaitTime = 2000;
+ if (!w->testAttribute(Qt::WA_WState_Created))
+ return;
+
+ WId winid = w->internalWinId();
+
+ // first deliver events that are already in the local queue
+ if (sendPostedEvents)
+ QApplication::sendPostedEvents();
+
+ // the normal sequence is:
+ // ... ConfigureNotify ... ReparentNotify ... MapNotify ... Expose
+ // with X11BypassWindowManagerHint:
+ // ConfigureNotify ... MapNotify ... Expose
+
+ enum State {
+ Initial, Mapped
+ } state = Initial;
+
+ do {
+ if (XEventsQueued(X11->display, QueuedAlready)) {
+ XNextEvent(X11->display, &ev);
+ qApp->x11ProcessEvent(&ev);
+
+ switch (state) {
+ case Initial:
+ if (ev.type == MapNotify && ev.xany.window == winid)
+ state = Mapped;
+ break;
+ case Mapped:
+ if (ev.type == Expose && ev.xany.window == winid)
+ return;
+ break;
+ }
+ } else {
+ if (!XEventsQueued(X11->display, QueuedAfterFlush))
+ qApp->syncX(); // non-busy wait
+ }
+ if (t.elapsed() > maximumWaitTime)
+ return;
+ } while(1);
+}
+
+Q_WIDGETS_EXPORT void qt_x11_wait_for_window_manager(QWidget *w)
+{
+ qt_x11_wait_for_window_manager(w, true);
+}
+
+void qt_change_net_wm_state(const QWidget* w, bool set, Atom one, Atom two = 0)
+{
+ if (!w->isVisible()) // not managed by the window manager
+ return;
+
+ XEvent e;
+ e.xclient.type = ClientMessage;
+ e.xclient.message_type = ATOM(_NET_WM_STATE);
+ e.xclient.display = X11->display;
+ e.xclient.window = w->internalWinId();
+ e.xclient.format = 32;
+ e.xclient.data.l[0] = set ? 1 : 0;
+ e.xclient.data.l[1] = one;
+ e.xclient.data.l[2] = two;
+ e.xclient.data.l[3] = 0;
+ e.xclient.data.l[4] = 0;
+ XSendEvent(X11->display, RootWindow(X11->display, w->x11Info().screen()),
+ false, (SubstructureNotifyMask | SubstructureRedirectMask), &e);
+}
+
+struct QX11WindowAttributes {
+ const XWindowAttributes *att;
+};
+
+void qt_x11_getX11InfoForWindow(QX11Info * xinfo, const XWindowAttributes &a)
+{
+ QX11WindowAttributes att;
+ att.att = &a;
+ qt_x11_getX11InfoForWindow(xinfo,att);
+}
+
+
+static QVector<Atom> getNetWmState(QWidget *w)
+{
+ QVector<Atom> returnValue;
+
+ // Don't read anything, just get the size of the property data
+ Atom actualType;
+ int actualFormat;
+ ulong propertyLength;
+ ulong bytesLeft;
+ uchar *propertyData = 0;
+ if (XGetWindowProperty(X11->display, w->internalWinId(), ATOM(_NET_WM_STATE), 0, 0,
+ False, XA_ATOM, &actualType, &actualFormat,
+ &propertyLength, &bytesLeft, &propertyData) == Success
+ && actualType == XA_ATOM && actualFormat == 32) {
+ returnValue.resize(bytesLeft / 4);
+ XFree((char*) propertyData);
+ propertyData = 0;
+
+ // fetch all data
+ if (XGetWindowProperty(X11->display, w->internalWinId(), ATOM(_NET_WM_STATE), 0,
+ returnValue.size(), False, XA_ATOM, &actualType, &actualFormat,
+ &propertyLength, &bytesLeft, &propertyData) != Success) {
+ returnValue.clear();
+ } else if (propertyLength != (ulong)returnValue.size()) {
+ returnValue.resize(propertyLength);
+ }
+
+ // put it into netWmState
+ if (!returnValue.isEmpty()) {
+ memcpy(returnValue.data(), propertyData, returnValue.size() * sizeof(Atom));
+ }
+ if (propertyData)
+ XFree((char*) propertyData);
+ }
+
+ return returnValue;
+}
+
+void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyOldWindow)
+{
+ Q_Q(QWidget);
+ Qt::WindowType type = q->windowType();
+ Qt::WindowFlags &flags = data.window_flags;
+ QWidget *parentWidget = q->parentWidget();
+
+ if (type == Qt::ToolTip)
+ flags |= Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint;
+ if (type == Qt::Popup)
+ flags |= Qt::X11BypassWindowManagerHint;
+
+ bool topLevel = (flags & Qt::Window);
+ bool popup = (type == Qt::Popup);
+ bool desktop = (type == Qt::Desktop);
+ bool tool = (type == Qt::Tool || type == Qt::SplashScreen
+ || type == Qt::ToolTip || type == Qt::Drawer);
+
+#ifdef ALIEN_DEBUG
+ qDebug() << "QWidgetPrivate::create_sys START:" << q << "topLevel?" << topLevel << "WId:"
+ << window << "initializeWindow:" << initializeWindow << "destroyOldWindow" << destroyOldWindow;
+#endif
+ if (topLevel) {
+ if (parentWidget) { // if our parent stays on top, so must we
+ QWidget *ptl = parentWidget->window();
+ if(ptl && (ptl->windowFlags() & Qt::WindowStaysOnTopHint))
+ flags |= Qt::WindowStaysOnTopHint;
+ }
+
+ if (type == Qt::SplashScreen) {
+ if (X11->isSupportedByWM(ATOM(_NET_WM_WINDOW_TYPE_SPLASH))) {
+ flags &= ~Qt::X11BypassWindowManagerHint;
+ } else {
+ flags |= Qt::X11BypassWindowManagerHint | Qt::FramelessWindowHint;
+ }
+ }
+ // All these buttons depend on the system menu, so we enable it
+ if (flags & (Qt::WindowMinimizeButtonHint
+ | Qt::WindowMaximizeButtonHint
+ | Qt::WindowContextHelpButtonHint))
+ flags |= Qt::WindowSystemMenuHint;
+ }
+
+
+ Window parentw, destroyw = 0;
+ WId id = 0;
+
+ // always initialize
+ if (!window)
+ initializeWindow = true;
+
+ QX11Info *parentXinfo = parentWidget ? &parentWidget->d_func()->xinfo : 0;
+
+ if (desktop &&
+ qt_x11_create_desktop_on_screen >= 0 &&
+ qt_x11_create_desktop_on_screen != xinfo.screen()) {
+ // desktop on a certain screen other than the default requested
+ QX11InfoData *xd = &X11->screens[qt_x11_create_desktop_on_screen];
+ xinfo.setX11Data(xd);
+ } else if (parentXinfo && (parentXinfo->screen() != xinfo.screen()
+ || (parentXinfo->visual() != xinfo.visual()
+ && !q->inherits("QGLWidget"))))
+ {
+ // QGLWidgets have to be excluded here as they have a
+ // specially crafted QX11Info structure which can't be swapped
+ // out with the parent widgets QX11Info. The parent visual,
+ // for instance, might not even be GL capable.
+ xinfo = *parentXinfo;
+ }
+
+ //get display, screen number, root window and desktop geometry for
+ //the current screen
+ Display *dpy = X11->display;
+ int scr = xinfo.screen();
+ Window root_win = RootWindow(dpy, scr);
+ int sw = DisplayWidth(dpy,scr);
+ int sh = DisplayHeight(dpy,scr);
+
+ if (desktop) { // desktop widget
+ popup = false; // force these flags off
+ data.crect.setRect(0, 0, sw, sh);
+ } else if (topLevel && !q->testAttribute(Qt::WA_Resized)) {
+ QDesktopWidget *desktopWidget = qApp->desktop();
+ if (desktopWidget->isVirtualDesktop()) {
+ QRect r = desktopWidget->screenGeometry();
+ sw = r.width();
+ sh = r.height();
+ }
+
+ int width = sw / 2;
+ int height = 4 * sh / 10;
+ if (extra) {
+ width = qMax(qMin(width, extra->maxw), extra->minw);
+ height = qMax(qMin(height, extra->maxh), extra->minh);
+ }
+ data.crect.setSize(QSize(width, height));
+ }
+
+ parentw = topLevel ? root_win : parentWidget->effectiveWinId();
+
+ XSetWindowAttributes wsa;
+
+ if (window) { // override the old window
+ if (destroyOldWindow) {
+ if (topLevel)
+ X11->dndEnable(q, false);
+ destroyw = data.winid;
+ }
+ id = window;
+ setWinId(window);
+ XWindowAttributes a;
+ XGetWindowAttributes(dpy, window, &a);
+ data.crect.setRect(a.x, a.y, a.width, a.height);
+
+ if (a.map_state == IsUnmapped)
+ q->setAttribute(Qt::WA_WState_Visible, false);
+ else
+ q->setAttribute(Qt::WA_WState_Visible);
+
+ qt_x11_getX11InfoForWindow(&xinfo,a);
+
+ } else if (desktop) { // desktop widget
+#ifdef QWIDGET_EXTRA_DEBUG
+ qDebug() << "create desktop";
+#endif
+ id = (WId)parentw; // id = root window
+// QWidget *otherDesktop = find(id); // is there another desktop?
+// if (otherDesktop && otherDesktop->testWFlags(Qt::WPaintDesktop)) {
+// otherDesktop->d->setWinId(0); // remove id from widget mapper
+// d->setWinId(id); // make sure otherDesktop is
+// otherDesktop->d->setWinId(id); // found first
+// } else {
+ setWinId(id);
+// }
+ } else if (topLevel || q->testAttribute(Qt::WA_NativeWindow) || paintOnScreen()) {
+#ifdef QWIDGET_EXTRA_DEBUG
+ static int topLevels = 0;
+ static int children = 0;
+ if (parentw == root_win)
+ qDebug() << "create toplevel" << ++topLevels;
+ else
+ qDebug() << "create child" << ++children;
+#endif
+ QRect safeRect = data.crect; //##### must handle huge sizes as well.... i.e. wrect
+ if (safeRect.width() < 1|| safeRect.height() < 1) {
+ if (topLevel) {
+ // top-levels must be at least 1x1
+ safeRect.setSize(safeRect.size().expandedTo(QSize(1, 1)));
+ } else {
+ // create it way off screen, and rely on
+ // setWSGeometry() to do the right thing with it later
+ safeRect = QRect(-1000,-1000,1,1);
+ }
+ }
+#ifndef QT_NO_XRENDER
+ int screen = xinfo.screen();
+ if (topLevel && X11->use_xrender
+ && xinfo.depth() != 32 && X11->argbVisuals[screen]
+ && q->testAttribute(Qt::WA_TranslucentBackground))
+ {
+ QX11InfoData *xd = xinfo.getX11Data(true);
+
+ xd->screen = screen;
+ xd->visual = X11->argbVisuals[screen];
+ xd->colormap = X11->argbColormaps[screen];
+ xd->depth = 32;
+ xd->defaultVisual = false;
+ xd->defaultColormap = false;
+ xd->cells = xd->visual->map_entries;
+ xinfo.setX11Data(xd);
+ }
+#endif
+ if (xinfo.defaultVisual() && xinfo.defaultColormap()) {
+ id = (WId)qt_XCreateSimpleWindow(q, dpy, parentw,
+ safeRect.left(), safeRect.top(),
+ safeRect.width(), safeRect.height(),
+ 0,
+ BlackPixel(dpy, xinfo.screen()),
+ WhitePixel(dpy, xinfo.screen()));
+ } else {
+ wsa.background_pixel = WhitePixel(dpy, xinfo.screen());
+ wsa.border_pixel = BlackPixel(dpy, xinfo.screen());
+ wsa.colormap = xinfo.colormap();
+ id = (WId)qt_XCreateWindow(q, dpy, parentw,
+ safeRect.left(), safeRect.top(),
+ safeRect.width(), safeRect.height(),
+ 0, xinfo.depth(), InputOutput,
+ (Visual *) xinfo.visual(),
+ CWBackPixel|CWBorderPixel|CWColormap,
+ &wsa);
+ }
+
+ setWinId(id); // set widget id/handle + hd
+ }
+
+#ifndef QT_NO_XRENDER
+ if (picture) {
+ XRenderFreePicture(X11->display, picture);
+ picture = 0;
+ }
+
+ if (X11->use_xrender && !desktop && q->internalWinId()) {
+ XRenderPictFormat *format = XRenderFindVisualFormat(dpy, (Visual *) xinfo.visual());
+ if (format)
+ picture = XRenderCreatePicture(dpy, id, format, 0, 0);
+ }
+#endif // QT_NO_XRENDER
+
+ QtMWMHints mwmhints;
+ mwmhints.flags = 0L;
+ mwmhints.functions = 0L;
+ mwmhints.decorations = 0;
+ mwmhints.input_mode = 0L;
+ mwmhints.status = 0L;
+
+ if (topLevel) {
+ ulong wsa_mask = 0;
+ if (type != Qt::SplashScreen) { // && customize) {
+ mwmhints.flags |= MWM_HINTS_DECORATIONS;
+
+ bool customize = flags & Qt::CustomizeWindowHint;
+ if (!(flags & Qt::FramelessWindowHint) && !(customize && !(flags & Qt::WindowTitleHint))) {
+ mwmhints.decorations |= MWM_DECOR_BORDER;
+ mwmhints.decorations |= MWM_DECOR_RESIZEH;
+
+ if (flags & Qt::WindowTitleHint)
+ mwmhints.decorations |= MWM_DECOR_TITLE;
+
+ if (flags & Qt::WindowSystemMenuHint)
+ mwmhints.decorations |= MWM_DECOR_MENU;
+
+ if (flags & Qt::WindowMinimizeButtonHint) {
+ mwmhints.decorations |= MWM_DECOR_MINIMIZE;
+ mwmhints.functions |= MWM_FUNC_MINIMIZE;
+ }
+
+ if (flags & Qt::WindowMaximizeButtonHint) {
+ mwmhints.decorations |= MWM_DECOR_MAXIMIZE;
+ mwmhints.functions |= MWM_FUNC_MAXIMIZE;
+ }
+
+ if (flags & Qt::WindowCloseButtonHint)
+ mwmhints.functions |= MWM_FUNC_CLOSE;
+ }
+ } else {
+ // if type == Qt::SplashScreen
+ mwmhints.decorations = MWM_DECOR_ALL;
+ }
+
+ if (tool) {
+ wsa.save_under = True;
+ wsa_mask |= CWSaveUnder;
+ }
+
+ if (flags & Qt::X11BypassWindowManagerHint) {
+ wsa.override_redirect = True;
+ wsa_mask |= CWOverrideRedirect;
+ }
+
+ if (wsa_mask && initializeWindow) {
+ Q_ASSERT(id);
+ XChangeWindowAttributes(dpy, id, wsa_mask, &wsa);
+ }
+
+ if (mwmhints.functions != 0) {
+ mwmhints.flags |= MWM_HINTS_FUNCTIONS;
+ mwmhints.functions |= MWM_FUNC_MOVE | MWM_FUNC_RESIZE;
+ } else {
+ mwmhints.functions = MWM_FUNC_ALL;
+ }
+
+ if (!(flags & Qt::FramelessWindowHint)
+ && flags & Qt::CustomizeWindowHint
+ && flags & Qt::WindowTitleHint
+ && !(flags &
+ (Qt::WindowMinimizeButtonHint
+ | Qt::WindowMaximizeButtonHint
+ | Qt::WindowCloseButtonHint))) {
+ // a special case - only the titlebar without any button
+ mwmhints.flags = MWM_HINTS_FUNCTIONS;
+ mwmhints.functions = MWM_FUNC_MOVE | MWM_FUNC_RESIZE;
+ mwmhints.decorations = 0;
+ }
+ }
+
+ if (!initializeWindow) {
+ // do no initialization
+ } else if (popup) { // popup widget
+ // set EWMH window types
+ setNetWmWindowTypes();
+
+ wsa.override_redirect = True;
+ wsa.save_under = True;
+ Q_ASSERT(id);
+ XChangeWindowAttributes(dpy, id, CWOverrideRedirect | CWSaveUnder,
+ &wsa);
+ } else if (topLevel && !desktop) { // top-level widget
+ if (!X11->wm_client_leader)
+ create_wm_client_leader();
+
+ // note: WM_TRANSIENT_FOR is set in QWidgetPrivate::show_sys()
+
+ XSizeHints size_hints;
+ size_hints.flags = USSize | PSize | PWinGravity;
+ size_hints.x = data.crect.left();
+ size_hints.y = data.crect.top();
+ size_hints.width = data.crect.width();
+ size_hints.height = data.crect.height();
+ size_hints.win_gravity =
+ QApplication::isRightToLeft() ? NorthEastGravity : NorthWestGravity;
+
+ XWMHints wm_hints; // window manager hints
+ memset(&wm_hints, 0, sizeof(wm_hints)); // make valgrind happy
+ wm_hints.flags = InputHint | StateHint | WindowGroupHint;
+ wm_hints.input = q->testAttribute(Qt::WA_X11DoNotAcceptFocus) ? False : True;
+ wm_hints.initial_state = NormalState;
+ wm_hints.window_group = X11->wm_client_leader;
+
+ XClassHint class_hint;
+ QByteArray appName = qAppName().toLatin1();
+ class_hint.res_name = appName.data(); // application name
+ class_hint.res_class = const_cast<char *>(QX11Info::appClass()); // application class
+
+ XSetWMProperties(dpy, id, 0, 0,
+ qApp->d_func()->argv, qApp->d_func()->argc,
+ &size_hints, &wm_hints, &class_hint);
+
+ XResizeWindow(dpy, id,
+ qBound(1, data.crect.width(), XCOORD_MAX),
+ qBound(1, data.crect.height(), XCOORD_MAX));
+ XStoreName(dpy, id, appName.data());
+ Atom protocols[5];
+ int n = 0;
+ protocols[n++] = ATOM(WM_DELETE_WINDOW); // support del window protocol
+ protocols[n++] = ATOM(WM_TAKE_FOCUS); // support take focus window protocol
+ protocols[n++] = ATOM(_NET_WM_PING); // support _NET_WM_PING protocol
+#ifndef QT_NO_XSYNC
+ protocols[n++] = ATOM(_NET_WM_SYNC_REQUEST); // support _NET_WM_SYNC_REQUEST protocol
+#endif // QT_NO_XSYNC
+ if (flags & Qt::WindowContextHelpButtonHint)
+ protocols[n++] = ATOM(_NET_WM_CONTEXT_HELP);
+ XSetWMProtocols(dpy, id, protocols, n);
+
+ // set mwm hints
+ SetMWMHints(dpy, id, mwmhints);
+
+ // set EWMH window types
+ setNetWmWindowTypes();
+
+ // set _NET_WM_PID
+ long curr_pid = getpid();
+ XChangeProperty(dpy, id, ATOM(_NET_WM_PID), XA_CARDINAL, 32, PropModeReplace,
+ (unsigned char *) &curr_pid, 1);
+
+ // when we create a toplevel widget, the frame strut should be dirty
+ data.fstrut_dirty = 1;
+
+ // declare the widget's window role
+ if (QTLWExtra *topData = maybeTopData()) {
+ if (!topData->role.isEmpty()) {
+ QByteArray windowRole = topData->role.toUtf8();
+ XChangeProperty(dpy, id,
+ ATOM(WM_WINDOW_ROLE), XA_STRING, 8, PropModeReplace,
+ (unsigned char *)windowRole.constData(), windowRole.length());
+ }
+ }
+
+ // set client leader property
+ XChangeProperty(dpy, id, ATOM(WM_CLIENT_LEADER),
+ XA_WINDOW, 32, PropModeReplace,
+ (unsigned char *)&X11->wm_client_leader, 1);
+ } else {
+ // non-toplevel widgets don't have a frame, so no need to
+ // update the strut
+ data.fstrut_dirty = 0;
+ }
+
+ if (initializeWindow && q->internalWinId()) {
+ // don't erase when resizing
+ wsa.bit_gravity = QApplication::isRightToLeft() ? NorthEastGravity : NorthWestGravity;
+ Q_ASSERT(id);
+ XChangeWindowAttributes(dpy, id, CWBitGravity, &wsa);
+ }
+
+ // set X11 event mask
+ if (desktop) {
+// QWidget* main_desktop = find(id);
+// if (main_desktop->testWFlags(Qt::WPaintDesktop))
+// XSelectInput(dpy, id, stdDesktopEventMask | ExposureMask);
+// else
+ XSelectInput(dpy, id, stdDesktopEventMask);
+ } else if (q->internalWinId()) {
+ XSelectInput(dpy, id, stdWidgetEventMask);
+#if !defined (QT_NO_TABLET)
+ QTabletDeviceDataList *tablet_list = qt_tablet_devices();
+ if (X11->ptrXSelectExtensionEvent) {
+ for (int i = 0; i < tablet_list->size(); ++i) {
+ QTabletDeviceData tablet = tablet_list->at(i);
+ X11->ptrXSelectExtensionEvent(dpy, id, reinterpret_cast<XEventClass*>(tablet.eventList),
+ tablet.eventCount);
+ }
+ }
+#endif
+ }
+
+ if (desktop) {
+ q->setAttribute(Qt::WA_WState_Visible);
+ } else if (topLevel) { // set X cursor
+ if (initializeWindow) {
+ qt_x11_enforce_cursor(q);
+
+ if (QTLWExtra *topData = maybeTopData())
+ if (!topData->caption.isEmpty())
+ setWindowTitle_helper(topData->caption);
+
+ //always enable dnd: it's not worth the effort to maintain the state
+ // NOTE: this always creates topData()
+ X11->dndEnable(q, true);
+
+ if (maybeTopData() && maybeTopData()->opacity != 255)
+ q->setWindowOpacity(maybeTopData()->opacity/255.);
+
+ }
+ } else if (q->internalWinId()) {
+ qt_x11_enforce_cursor(q);
+ if (QWidget *p = q->parentWidget()) // reset the cursor on the native parent
+ qt_x11_enforce_cursor(p);
+ }
+
+ if (extra && !extra->mask.isEmpty() && q->internalWinId())
+ XShapeCombineRegion(X11->display, q->internalWinId(), ShapeBounding, 0, 0,
+ extra->mask.handle(), ShapeSet);
+
+ if (q->hasFocus() && q->testAttribute(Qt::WA_InputMethodEnabled)) {
+ QInputContext *inputContext = q->inputContext();
+ if (inputContext)
+ inputContext->setFocusWidget(q);
+ }
+
+ if (destroyw) {
+ qt_XDestroyWindow(q, dpy, destroyw);
+ if (QTLWExtra *topData = maybeTopData()) {
+#ifndef QT_NO_XSYNC
+ if (topData->syncUpdateCounter)
+ XSyncDestroyCounter(dpy, topData->syncUpdateCounter);
+#endif
+ // we destroyed our old window - reset the top-level state
+ createTLSysExtra();
+ }
+ }
+
+ // newly created windows are positioned at the window system's
+ // (0,0) position. If the parent uses wrect mapping to expand the
+ // coordinate system, we must also adjust this widget's window
+ // system position
+ if (!topLevel && !parentWidget->data->wrect.topLeft().isNull())
+ setWSGeometry();
+ else if (topLevel && (data.crect.width() == 0 || data.crect.height() == 0))
+ q->setAttribute(Qt::WA_OutsideWSRange, true);
+
+ if (!topLevel && q->testAttribute(Qt::WA_NativeWindow) && q->testAttribute(Qt::WA_Mapped)) {
+ Q_ASSERT(q->internalWinId());
+ XMapWindow(X11->display, q->internalWinId());
+ // Ensure that mapped alien widgets are flushed immediately when re-created as native widgets.
+ if (QWindowSurface *surface = q->windowSurface())
+ surface->flush(q, q->rect(), q->mapTo(surface->window(), QPoint()));
+ }
+
+#ifdef ALIEN_DEBUG
+ qDebug() << "QWidgetPrivate::create_sys END:" << q;
+#endif
+}
+
+static void qt_x11_recreateWidget(QWidget *widget)
+{
+ if (widget->inherits("QGLWidget")) {
+ // We send QGLWidgets a ParentChange event which causes them to
+ // recreate their GL context, which in turn causes them to choose
+ // their visual again. Now that WA_TranslucentBackground is set,
+ // QGLContext::chooseVisual will select an ARGB visual.
+
+ // QGLWidget expects a ParentAboutToChange to be sent first
+ QEvent aboutToChangeEvent(QEvent::ParentAboutToChange);
+ QApplication::sendEvent(widget, &aboutToChangeEvent);
+
+ QEvent parentChangeEvent(QEvent::ParentChange);
+ QApplication::sendEvent(widget, &parentChangeEvent);
+ } else {
+ // For regular widgets, reparent them with their parent which
+ // also triggers a recreation of the native window
+ QPoint pos = widget->pos();
+ bool visible = widget->isVisible();
+ if (visible)
+ widget->hide();
+
+ widget->setParent(widget->parentWidget(), widget->windowFlags());
+ widget->move(pos);
+ if (visible)
+ widget->show();
+ }
+}
+
+static void qt_x11_recreateNativeWidgetsRecursive(QWidget *widget)
+{
+ if (widget->internalWinId())
+ qt_x11_recreateWidget(widget);
+
+ const QObjectList &children = widget->children();
+ for (int i = 0; i < children.size(); ++i) {
+ QWidget *child = qobject_cast<QWidget*>(children.at(i));
+ if (child)
+ qt_x11_recreateNativeWidgetsRecursive(child);
+ }
+}
+
+void QWidgetPrivate::x11UpdateIsOpaque()
+{
+#ifndef QT_NO_XRENDER
+ Q_Q(QWidget);
+ if (!q->testAttribute(Qt::WA_WState_Created) || !q->testAttribute(Qt::WA_TranslucentBackground))
+ return;
+
+ bool topLevel = (data.window_flags & Qt::Window);
+ int screen = xinfo.screen();
+ if (topLevel && X11->use_xrender
+ && X11->argbVisuals[screen] && xinfo.depth() != 32)
+ {
+ qt_x11_recreateNativeWidgetsRecursive(q);
+ }
+#endif
+}
+
+/*
+ Returns true if the background is inherited; otherwise returns
+ false.
+
+ Mainly used in the paintOnScreen case.
+*/
+bool QWidgetPrivate::isBackgroundInherited() const
+{
+ Q_Q(const QWidget);
+
+ // windows do not inherit their background
+ if (q->isWindow() || q->windowType() == Qt::SubWindow)
+ return false;
+
+ if (q->testAttribute(Qt::WA_NoSystemBackground) || q->testAttribute(Qt::WA_OpaquePaintEvent))
+ return false;
+
+ const QPalette &pal = q->palette();
+ QPalette::ColorRole bg = q->backgroundRole();
+ QBrush brush = pal.brush(bg);
+
+ // non opaque brushes leaves us no choice, we must inherit
+ if (!q->autoFillBackground() || !brush.isOpaque())
+ return true;
+
+ if (brush.style() == Qt::SolidPattern) {
+ // the background is just a solid color. If there is no
+ // propagated contents, then we claim as performance
+ // optimization that it was not inheritet. This is the normal
+ // case in standard Windows or Motif style.
+ const QWidget *w = q->parentWidget();
+ if (!w->d_func()->isBackgroundInherited())
+ return false;
+ }
+
+ return true;
+}
+
+void QWidget::destroy(bool destroyWindow, bool destroySubWindows)
+{
+ Q_D(QWidget);
+ d->aboutToDestroy();
+ if (!isWindow() && parentWidget())
+ parentWidget()->d_func()->invalidateBuffer(d->effectiveRectFor(geometry()));
+ d->deactivateWidgetCleanup();
+ if (testAttribute(Qt::WA_WState_Created)) {
+ setAttribute(Qt::WA_WState_Created, false);
+ QObjectList childList = children();
+ for (int i = 0; i < childList.size(); ++i) { // destroy all widget children
+ register QObject *obj = childList.at(i);
+ if (obj->isWidgetType())
+ static_cast<QWidget*>(obj)->destroy(destroySubWindows,
+ destroySubWindows);
+ }
+ if (QWidgetPrivate::mouseGrabber == this)
+ releaseMouse();
+ if (QWidgetPrivate::keyboardGrabber == this)
+ releaseKeyboard();
+ if (isWindow())
+ X11->deferred_map.removeAll(this);
+ if (isModal()) {
+ // just be sure we leave modal
+ QApplicationPrivate::leaveModal(this);
+ }
+ else if ((windowType() == Qt::Popup))
+ qApp->d_func()->closePopup(this);
+
+#ifndef QT_NO_XRENDER
+ if (d->picture) {
+ if (destroyWindow)
+ XRenderFreePicture(X11->display, d->picture);
+ d->picture = 0;
+ }
+#endif // QT_NO_XRENDER
+
+ // delete the _NET_WM_USER_TIME_WINDOW
+ qt_net_remove_user_time(this);
+
+ if ((windowType() == Qt::Desktop)) {
+ if (acceptDrops())
+ X11->dndEnable(this, false);
+ } else {
+ if (isWindow())
+ X11->dndEnable(this, false);
+ if (destroyWindow)
+ qt_XDestroyWindow(this, X11->display, data->winid);
+ }
+ QT_TRY {
+ d->setWinId(0);
+ } QT_CATCH (const std::bad_alloc &) {
+ // swallow - destructors must not throw
+ }
+
+ extern void qPRCleanup(QWidget *widget); // from qapplication_x11.cpp
+ if (testAttribute(Qt::WA_WState_Reparented))
+ qPRCleanup(this);
+
+ if(d->ic) {
+ delete d->ic;
+ } else {
+ // release previous focus information participating with
+ // preedit preservation of qic
+ QInputContext *qic = QApplicationPrivate::inputContext;
+ if (qic)
+ qic->widgetDestroyed(this);
+ }
+ }
+}
+
+void QWidgetPrivate::setParent_sys(QWidget *parent, Qt::WindowFlags f)
+{
+ Q_Q(QWidget);
+#ifdef ALIEN_DEBUG
+ qDebug() << "QWidgetPrivate::setParent_sys START" << q << "parent:" << parent;
+#endif
+ QX11Info old_xinfo = xinfo;
+ if (parent && parent->windowType() == Qt::Desktop) {
+ // make sure the widget is created on the same screen as the
+ // programmer specified desktop widget
+ xinfo = parent->d_func()->xinfo;
+ parent = 0;
+ }
+
+ QTLWExtra *topData = maybeTopData();
+ bool wasCreated = q->testAttribute(Qt::WA_WState_Created);
+ if (q->isVisible() && q->parentWidget() && parent != q->parentWidget())
+ q->parentWidget()->d_func()->invalidateBuffer(effectiveRectFor(q->geometry()));
+ extern void qPRCreate(const QWidget *, Window);
+#ifndef QT_NO_CURSOR
+ QCursor oldcurs;
+#endif
+
+ // dnd unregister (we will register again below)
+ if (q->testAttribute(Qt::WA_DropSiteRegistered))
+ q->setAttribute(Qt::WA_DropSiteRegistered, false);
+
+ // if we are a top then remove our dnd prop for now
+ // it will get rest later
+ if (q->isWindow() && wasCreated)
+ X11->dndEnable(q, false);
+
+ if (topData)
+ qt_net_remove_user_time(q);
+
+// QWidget *oldparent = q->parentWidget();
+ WId old_winid = wasCreated ? data.winid : 0;
+ if ((q->windowType() == Qt::Desktop))
+ old_winid = 0;
+ setWinId(0);
+
+#ifndef QT_NO_XRENDER
+ if (picture) {
+ XRenderFreePicture(X11->display, picture);
+ picture = 0;
+ }
+#endif
+
+ // hide and reparent our own window away. Otherwise we might get
+ // destroyed when emitting the child remove event below. See QWorkspace.
+ if (wasCreated && old_winid) {
+ XUnmapWindow(X11->display, old_winid);
+ if (!old_xinfo.screen() != xinfo.screen())
+ XReparentWindow(X11->display, old_winid, RootWindow(X11->display, xinfo.screen()), 0, 0);
+ }
+ if (topData) {
+ topData->parentWinId = 0;
+ // zero the frame strut and mark it dirty
+ topData->frameStrut.setCoords(0, 0, 0, 0);
+
+ // reparenting from top-level, make sure show() works again
+ topData->waitingForMapNotify = 0;
+ topData->validWMState = 0;
+ }
+ data.fstrut_dirty = (!parent || (f & Qt::Window)); // toplevels get a dirty framestrut
+
+ QObjectPrivate::setParent_helper(parent);
+ bool explicitlyHidden = q->testAttribute(Qt::WA_WState_Hidden) && q->testAttribute(Qt::WA_WState_ExplicitShowHide);
+
+ data.window_flags = f;
+ q->setAttribute(Qt::WA_WState_Created, false);
+ q->setAttribute(Qt::WA_WState_Visible, false);
+ q->setAttribute(Qt::WA_WState_Hidden, false);
+ adjustFlags(data.window_flags, q);
+ // keep compatibility with previous versions, we need to preserve the created state
+ // (but we recreate the winId for the widget being reparented, again for compatibility)
+ if (wasCreated)
+ createWinId();
+ if (q->isWindow() || (!parent || parent->isVisible()) || explicitlyHidden)
+ q->setAttribute(Qt::WA_WState_Hidden);
+ q->setAttribute(Qt::WA_WState_ExplicitShowHide, explicitlyHidden);
+
+ if (wasCreated) {
+ QObjectList chlist = q->children();
+ for (int i = 0; i < chlist.size(); ++i) { // reparent children
+ QObject *obj = chlist.at(i);
+ if (obj->isWidgetType()) {
+ QWidget *w = (QWidget *)obj;
+ if (!w->testAttribute(Qt::WA_WState_Created))
+ continue;
+ if (xinfo.screen() != w->d_func()->xinfo.screen()) {
+ // ### force setParent() to not shortcut out (because
+ // ### we're setting the parent to the current parent)
+ // ### setParent will add child back to the list
+ // ### of children so we need to make sure the
+ // ### widget won't be added twice.
+ w->d_func()->parent = 0;
+ this->children.removeOne(w);
+ w->setParent(q);
+ } else if (!w->isWindow()) {
+ w->d_func()->invalidateBuffer(w->rect());
+ if (w->internalWinId()) {
+ if (w->testAttribute(Qt::WA_NativeWindow)) {
+ QWidget *nativeParentWidget = w->nativeParentWidget();
+ // Qt::WA_NativeWindow ensures that we always have a nativeParentWidget
+ Q_ASSERT(nativeParentWidget != 0);
+ QPoint p = w->mapTo(nativeParentWidget, QPoint());
+ XReparentWindow(X11->display,
+ w->internalWinId(),
+ nativeParentWidget->internalWinId(),
+ p.x(), p.y());
+ } else {
+ w->d_func()->setParent_sys(q, w->data->window_flags);
+ }
+ }
+ } else if (isTransient(w)) {
+ /*
+ when reparenting toplevel windows with toplevel-transient children,
+ we need to make sure that the window manager gets the updated
+ WM_TRANSIENT_FOR information... unfortunately, some window managers
+ don't handle changing WM_TRANSIENT_FOR before the toplevel window is
+ visible, so we unmap and remap all toplevel-transient children *after*
+ the toplevel parent has been mapped. thankfully, this is easy in Qt :)
+
+ note that the WM_TRANSIENT_FOR hint is actually updated in
+ QWidgetPrivate::show_sys()
+ */
+ if (w->internalWinId())
+ XUnmapWindow(X11->display, w->internalWinId());
+ QApplication::postEvent(w, new QEvent(QEvent::ShowWindowRequest));
+ }
+ }
+ }
+ qPRCreate(q, old_winid);
+ updateSystemBackground();
+
+ if (old_winid) {
+ Window *cmwret;
+ int count;
+ if (XGetWMColormapWindows(X11->display, old_winid, &cmwret, &count)) {
+ Window *cmw;
+ int cmw_size = sizeof(Window)*count;
+ cmw = new Window[count];
+ memcpy((char *)cmw, (char *)cmwret, cmw_size);
+ XFree((char *)cmwret);
+ int i;
+ for (i=0; i<count; i++) {
+ if (cmw[i] == old_winid) {
+ cmw[i] = q->internalWinId();
+ break;
+ }
+ }
+ int top_count;
+ if (XGetWMColormapWindows(X11->display, q->window()->internalWinId(),
+ &cmwret, &top_count))
+ {
+ Window *merged_cmw = new Window[count + top_count];
+ memcpy((char *)merged_cmw, (char *)cmw, cmw_size);
+ memcpy((char *)merged_cmw + cmw_size, (char *)cmwret, sizeof(Window)*top_count);
+ delete [] cmw;
+ XFree((char *)cmwret);
+ cmw = merged_cmw;
+ count += top_count;
+ }
+
+ XSetWMColormapWindows(X11->display, q->window()->internalWinId(), cmw, count);
+ delete [] cmw;
+ }
+
+ qt_XDestroyWindow(q, X11->display, old_winid);
+ }
+ }
+
+ // check if we need to register our dropsite
+ if (q->testAttribute(Qt::WA_AcceptDrops)
+ || (!q->isWindow() && q->parentWidget() && q->parentWidget()->testAttribute(Qt::WA_DropSiteRegistered))) {
+ q->setAttribute(Qt::WA_DropSiteRegistered, true);
+ }
+#if !defined(QT_NO_IM)
+ ic = 0;
+#endif
+ invalidateBuffer(q->rect());
+#ifdef ALIEN_DEBUG
+ qDebug() << "QWidgetPrivate::setParent_sys END" << q;
+#endif
+}
+
+QPoint QWidgetPrivate::mapToGlobal(const QPoint &pos) const
+{
+ Q_Q(const QWidget);
+ if (!q->testAttribute(Qt::WA_WState_Created) || !q->internalWinId()) {
+ QPoint p = pos + q->data->crect.topLeft();
+ //cannot trust that !isWindow() implies parentWidget() before create
+ return (q->isWindow() || !q->parentWidget()) ? p : q->parentWidget()->d_func()->mapToGlobal(p);
+ }
+ int x, y;
+ Window child;
+ QPoint p = mapToWS(pos);
+ XTranslateCoordinates(X11->display, q->internalWinId(),
+ QApplication::desktop()->screen(xinfo.screen())->internalWinId(),
+ p.x(), p.y(), &x, &y, &child);
+ return QPoint(x, y);
+}
+
+QPoint QWidgetPrivate::mapFromGlobal(const QPoint &pos) const
+{
+ Q_Q(const QWidget);
+ if (!q->testAttribute(Qt::WA_WState_Created) || !q->internalWinId()) {
+ //cannot trust that !isWindow() implies parentWidget() before create
+ QPoint p = (q->isWindow() || !q->parentWidget()) ? pos : q->parentWidget()->d_func()->mapFromGlobal(pos);
+ return p - q->data->crect.topLeft();
+ }
+ int x, y;
+ Window child;
+ XTranslateCoordinates(X11->display,
+ QApplication::desktop()->screen(xinfo.screen())->internalWinId(),
+ q->internalWinId(), pos.x(), pos.y(), &x, &y, &child);
+ return mapFromWS(QPoint(x, y));
+}
+
+QPoint QWidget::mapToGlobal(const QPoint &pos) const
+{
+ Q_D(const QWidget);
+ QPoint offset = data->crect.topLeft();
+ const QWidget *w = this;
+ const QWidget *p = w->parentWidget();
+ while (!w->isWindow() && p) {
+ w = p;
+ p = p->parentWidget();
+ offset += w->data->crect.topLeft();
+ }
+
+ const QWidgetPrivate *wd = w->d_func();
+ QTLWExtra *tlw = wd->topData();
+ if (!tlw->embedded)
+ return pos + offset;
+
+ return d->mapToGlobal(pos);
+}
+
+QPoint QWidget::mapFromGlobal(const QPoint &pos) const
+{
+ Q_D(const QWidget);
+ QPoint offset = data->crect.topLeft();
+ const QWidget *w = this;
+ const QWidget *p = w->parentWidget();
+ while (!w->isWindow() && p) {
+ w = p;
+ p = p->parentWidget();
+ offset += w->data->crect.topLeft();
+ }
+
+ const QWidgetPrivate *wd = w->d_func();
+ QTLWExtra *tlw = wd->topData();
+ if (!tlw->embedded)
+ return pos - offset;
+
+ return d->mapFromGlobal(pos);
+}
+
+void QWidgetPrivate::updateSystemBackground()
+{
+ Q_Q(QWidget);
+ if (!q->testAttribute(Qt::WA_WState_Created) || !q->internalWinId())
+ return;
+ QBrush brush = q->palette().brush(QPalette::Active, q->backgroundRole());
+ Qt::WindowType type = q->windowType();
+ if (brush.style() == Qt::NoBrush
+ || q->testAttribute(Qt::WA_NoSystemBackground)
+ || q->testAttribute(Qt::WA_UpdatesDisabled)
+ || type == Qt::Popup || type == Qt::ToolTip) {
+ if (QX11Info::isCompositingManagerRunning()
+ && q->testAttribute(Qt::WA_TranslucentBackground)
+ && !(q->parent()))
+ XSetWindowBackground(X11->display, q->internalWinId(),
+ QColormap::instance(xinfo.screen()).pixel(Qt::transparent));
+ else
+ XSetWindowBackgroundPixmap(X11->display, q->internalWinId(), XNone);
+ }
+ else if (brush.style() == Qt::SolidPattern && brush.isOpaque())
+ XSetWindowBackground(X11->display, q->internalWinId(),
+ QColormap::instance(xinfo.screen()).pixel(brush.color()));
+ else if (isBackgroundInherited())
+ XSetWindowBackgroundPixmap(X11->display, q->internalWinId(), ParentRelative);
+ else if (brush.style() == Qt::TexturePattern) {
+ extern QPixmap qt_toX11Pixmap(const QPixmap &pixmap); // qpixmap_x11.cpp
+ XSetWindowBackgroundPixmap(X11->display, q->internalWinId(),
+ static_cast<QX11PlatformPixmap*>(qt_toX11Pixmap(brush.texture()).data.data())->x11ConvertToDefaultDepth());
+ } else
+ XSetWindowBackground(X11->display, q->internalWinId(),
+ QColormap::instance(xinfo.screen()).pixel(brush.color()));
+}
+
+#ifndef QT_NO_CURSOR
+void QWidgetPrivate::setCursor_sys(const QCursor &)
+{
+ Q_Q(QWidget);
+ qt_x11_enforce_cursor(q);
+ XFlush(X11->display);
+}
+
+void QWidgetPrivate::unsetCursor_sys()
+{
+ Q_Q(QWidget);
+ qt_x11_enforce_cursor(q);
+ XFlush(X11->display);
+}
+#endif
+
+static XTextProperty*
+qstring_to_xtp(const QString& s)
+{
+ static XTextProperty tp = { 0, 0, 0, 0 };
+ static bool free_prop = true; // we can't free tp.value in case it references
+ // the data of the static QCString below.
+ if (tp.value) {
+ if (free_prop)
+ XFree(tp.value);
+ tp.value = 0;
+ free_prop = true;
+ }
+
+ static const QTextCodec* mapper = QTextCodec::codecForLocale();
+ int errCode = 0;
+ if (mapper) {
+ QByteArray mapped = mapper->fromUnicode(s);
+ char* tl[2];
+ tl[0] = mapped.data();
+ tl[1] = 0;
+ errCode = XmbTextListToTextProperty(X11->display, tl, 1, XStdICCTextStyle, &tp);
+#if defined(QT_DEBUG)
+ if (errCode < 0)
+ qDebug("qstring_to_xtp result code %d", errCode);
+#endif
+ }
+ if (!mapper || errCode < 0) {
+ static QByteArray qcs;
+ qcs = s.toAscii();
+ tp.value = (uchar*)qcs.data();
+ tp.encoding = XA_STRING;
+ tp.format = 8;
+ tp.nitems = qcs.length();
+ free_prop = false;
+ }
+
+ // ### If we knew WM could understand unicode, we could use
+ // ### a much simpler, cheaper encoding...
+ /*
+ tp.value = (XChar2b*)s.unicode();
+ tp.encoding = XA_UNICODE; // wish
+ tp.format = 16;
+ tp.nitems = s.length();
+ */
+
+ return &tp;
+}
+
+void QWidgetPrivate::setWindowTitle_sys(const QString &caption)
+{
+ Q_Q(QWidget);
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ if (!q->internalWinId())
+ return;
+ XSetWMName(X11->display, q->internalWinId(), qstring_to_xtp(caption));
+
+ QByteArray net_wm_name = caption.toUtf8();
+ XChangeProperty(X11->display, q->internalWinId(), ATOM(_NET_WM_NAME), ATOM(UTF8_STRING), 8,
+ PropModeReplace, (unsigned char *)net_wm_name.data(), net_wm_name.size());
+}
+
+void QWidgetPrivate::setWindowIcon_sys(bool forceReset)
+{
+ Q_Q(QWidget);
+ if (!q->testAttribute(Qt::WA_WState_Created))
+ return;
+ QTLWExtra *topData = this->topData();
+ if (topData->iconPixmap && !forceReset)
+ // already been set
+ return;
+
+ // preparing images to set the _NET_WM_ICON property
+ QIcon icon = q->windowIcon();
+ QVector<long> icon_data;
+ Qt::HANDLE pixmap_handle = 0;
+ if (!icon.isNull()) {
+ QList<QSize> availableSizes = icon.availableSizes();
+ if(availableSizes.isEmpty()) {
+ // try to use default sizes since the icon can be a scalable image like svg.
+ availableSizes.push_back(QSize(16,16));
+ availableSizes.push_back(QSize(32,32));
+ availableSizes.push_back(QSize(64,64));
+ availableSizes.push_back(QSize(128,128));
+ }
+ for(int i = 0; i < availableSizes.size(); ++i) {
+ QSize size = availableSizes.at(i);
+ QPixmap pixmap = icon.pixmap(size);
+ if (!pixmap.isNull()) {
+ QImage image = pixmap.toImage().convertToFormat(QImage::Format_ARGB32);
+ int pos = icon_data.size();
+ icon_data.resize(pos + 2 + image.width()*image.height());
+ icon_data[pos++] = image.width();
+ icon_data[pos++] = image.height();
+ if (sizeof(long) == sizeof(quint32)) {
+ memcpy(icon_data.data() + pos, image.scanLine(0), image.byteCount());
+ } else {
+ for (int y = 0; y < image.height(); ++y) {
+ uint *scanLine = reinterpret_cast<uint *>(image.scanLine(y));
+ for (int x = 0; x < image.width(); ++x)
+ icon_data[pos + y*image.width() + x] = scanLine[x];
+ }
+ }
+ }
+ }
+ if (!icon_data.isEmpty()) {
+ extern QPixmap qt_toX11Pixmap(const QPixmap &pixmap);
+ /*
+ if the app is running on an unknown desktop, or it is not
+ using the default visual, convert the icon to 1bpp as stated
+ in the ICCCM section 4.1.2.4; otherwise, create the icon pixmap
+ in the default depth (even though this violates the ICCCM)
+ */
+ if (X11->desktopEnvironment == DE_UNKNOWN
+ || !QX11Info::appDefaultVisual(xinfo.screen())
+ || !QX11Info::appDefaultColormap(xinfo.screen())) {
+ // unknown DE or non-default visual/colormap, use 1bpp bitmap
+ if (!forceReset || !topData->iconPixmap)
+ topData->iconPixmap = new QPixmap(qt_toX11Pixmap(QBitmap(icon.pixmap(QSize(64,64)))));
+ pixmap_handle = topData->iconPixmap->handle();
+ } else {
+ // default depth, use a normal pixmap (even though this
+ // violates the ICCCM), since this works on all DEs known to Qt
+ if (!forceReset || !topData->iconPixmap)
+ topData->iconPixmap = new QPixmap(qt_toX11Pixmap(icon.pixmap(QSize(64,64))));
+ pixmap_handle = static_cast<QX11PlatformPixmap*>(topData->iconPixmap->data.data())->x11ConvertToDefaultDepth();
+ }
+ }
+ }
+
+ if (!q->internalWinId())
+ return;
+
+ if (!icon_data.isEmpty()) {
+ XChangeProperty(X11->display, q->internalWinId(), ATOM(_NET_WM_ICON), XA_CARDINAL, 32,
+ PropModeReplace, (unsigned char *) icon_data.data(),
+ icon_data.size());
+ } else {
+ XDeleteProperty(X11->display, q->internalWinId(), ATOM(_NET_WM_ICON));
+ }
+
+ XWMHints *h = XGetWMHints(X11->display, q->internalWinId());
+ XWMHints wm_hints;
+ if (!h) {
+ memset(&wm_hints, 0, sizeof(wm_hints)); // make valgrind happy
+ h = &wm_hints;
+ }
+
+ if (pixmap_handle) {
+ h->icon_pixmap = pixmap_handle;
+ h->flags |= IconPixmapHint;
+ } else {
+ h->icon_pixmap = 0;
+ h->flags &= ~(IconPixmapHint | IconMaskHint);
+ }
+
+ XSetWMHints(X11->display, q->internalWinId(), h);
+ if (h != &wm_hints)
+ XFree((char *)h);
+}
+
+void QWidgetPrivate::setWindowIconText_sys(const QString &iconText)
+{
+ Q_Q(QWidget);
+ if (!q->internalWinId())
+ return;
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ XSetWMIconName(X11->display, q->internalWinId(), qstring_to_xtp(iconText));
+
+ QByteArray icon_name = iconText.toUtf8();
+ XChangeProperty(X11->display, q->internalWinId(), ATOM(_NET_WM_ICON_NAME), ATOM(UTF8_STRING), 8,
+ PropModeReplace, (unsigned char *) icon_name.constData(), icon_name.size());
+}
+
+
+void QWidget::grabMouse()
+{
+ if (isVisible() && !qt_nograb()) {
+ if (QWidgetPrivate::mouseGrabber && QWidgetPrivate::mouseGrabber != this)
+ QWidgetPrivate::mouseGrabber->releaseMouse();
+ Q_ASSERT(testAttribute(Qt::WA_WState_Created));
+#ifndef QT_NO_DEBUG
+ int status =
+#endif
+ XGrabPointer(X11->display, effectiveWinId(), False,
+ (uint)(ButtonPressMask | ButtonReleaseMask |
+ PointerMotionMask | EnterWindowMask |
+ LeaveWindowMask),
+ GrabModeAsync, GrabModeAsync,
+ XNone, XNone, X11->time);
+#ifndef QT_NO_DEBUG
+ if (status) {
+ const char *s =
+ status == GrabNotViewable ? "\"GrabNotViewable\"" :
+ status == AlreadyGrabbed ? "\"AlreadyGrabbed\"" :
+ status == GrabFrozen ? "\"GrabFrozen\"" :
+ status == GrabInvalidTime ? "\"GrabInvalidTime\"" :
+ "<?>";
+ qWarning("QWidget::grabMouse: Failed with %s", s);
+ }
+#endif
+ QWidgetPrivate::mouseGrabber = this;
+ }
+}
+
+
+#ifndef QT_NO_CURSOR
+void QWidget::grabMouse(const QCursor &cursor)
+{
+ if (!qt_nograb()) {
+ if (QWidgetPrivate::mouseGrabber && QWidgetPrivate::mouseGrabber != this)
+ QWidgetPrivate::mouseGrabber->releaseMouse();
+ Q_ASSERT(testAttribute(Qt::WA_WState_Created));
+#ifndef QT_NO_DEBUG
+ int status =
+#endif
+ XGrabPointer(X11->display, effectiveWinId(), False,
+ (uint)(ButtonPressMask | ButtonReleaseMask |
+ PointerMotionMask | EnterWindowMask | LeaveWindowMask),
+ GrabModeAsync, GrabModeAsync,
+ XNone, cursor.handle(), X11->time);
+#ifndef QT_NO_DEBUG
+ if (status) {
+ const char *s =
+ status == GrabNotViewable ? "\"GrabNotViewable\"" :
+ status == AlreadyGrabbed ? "\"AlreadyGrabbed\"" :
+ status == GrabFrozen ? "\"GrabFrozen\"" :
+ status == GrabInvalidTime ? "\"GrabInvalidTime\"" :
+ "<?>";
+ qWarning("QWidget::grabMouse: Failed with %s", s);
+ }
+#endif
+ QWidgetPrivate::mouseGrabber = this;
+ }
+}
+#endif
+
+
+void QWidget::releaseMouse()
+{
+ if (!qt_nograb() && QWidgetPrivate::mouseGrabber == this) {
+ XUngrabPointer(X11->display, X11->time);
+ XFlush(X11->display);
+ QWidgetPrivate::mouseGrabber = 0;
+ }
+}
+
+
+void QWidget::grabKeyboard()
+{
+ if (!qt_nograb()) {
+ if (QWidgetPrivate::keyboardGrabber && QWidgetPrivate::keyboardGrabber != this)
+ QWidgetPrivate::keyboardGrabber->releaseKeyboard();
+ XGrabKeyboard(X11->display, effectiveWinId(), False, GrabModeAsync, GrabModeAsync,
+ X11->time);
+ QWidgetPrivate::keyboardGrabber = this;
+ }
+}
+
+
+void QWidget::releaseKeyboard()
+{
+ if (!qt_nograb() && QWidgetPrivate::keyboardGrabber == this) {
+ XUngrabKeyboard(X11->display, X11->time);
+ QWidgetPrivate::keyboardGrabber = 0;
+ }
+}
+
+
+QWidget *QWidget::mouseGrabber()
+{
+ return QWidgetPrivate::mouseGrabber;
+}
+
+
+QWidget *QWidget::keyboardGrabber()
+{
+ return QWidgetPrivate::keyboardGrabber;
+}
+
+void QWidget::activateWindow()
+{
+ QWidget *tlw = window();
+ if (tlw->isVisible() && !tlw->d_func()->topData()->embedded && !X11->deferred_map.contains(tlw)) {
+ if (X11->userTime == 0)
+ X11->userTime = X11->time;
+ qt_net_update_user_time(tlw, X11->userTime);
+
+ if (X11->isSupportedByWM(ATOM(_NET_ACTIVE_WINDOW))
+ && !(tlw->windowFlags() & Qt::X11BypassWindowManagerHint)) {
+ XEvent e;
+ e.xclient.type = ClientMessage;
+ e.xclient.message_type = ATOM(_NET_ACTIVE_WINDOW);
+ e.xclient.display = X11->display;
+ e.xclient.window = tlw->internalWinId();
+ e.xclient.format = 32;
+ e.xclient.data.l[0] = 1; // 1 == application
+ e.xclient.data.l[1] = X11->userTime;
+ if (QWidget *aw = QApplication::activeWindow())
+ e.xclient.data.l[2] = aw->internalWinId();
+ else
+ e.xclient.data.l[2] = XNone;
+ e.xclient.data.l[3] = 0;
+ e.xclient.data.l[4] = 0;
+ XSendEvent(X11->display, RootWindow(X11->display, tlw->x11Info().screen()),
+ false, SubstructureNotifyMask | SubstructureRedirectMask, &e);
+ } else {
+ if (!qt_widget_private(tlw)->topData()->waitingForMapNotify)
+ XSetInputFocus(X11->display, tlw->internalWinId(), XRevertToParent, X11->time);
+ }
+ }
+}
+
+void QWidget::setWindowState(Qt::WindowStates newstate)
+{
+ Q_D(QWidget);
+ bool needShow = false;
+ Qt::WindowStates oldstate = windowState();
+ if (oldstate == newstate)
+ return;
+ if (isWindow()) {
+ // Ensure the initial size is valid, since we store it as normalGeometry below.
+ if (!testAttribute(Qt::WA_Resized) && !isVisible())
+ adjustSize();
+
+ QTLWExtra *top = d->topData();
+
+ if ((oldstate & Qt::WindowMaximized) != (newstate & Qt::WindowMaximized)) {
+ if (X11->isSupportedByWM(ATOM(_NET_WM_STATE_MAXIMIZED_HORZ))
+ && X11->isSupportedByWM(ATOM(_NET_WM_STATE_MAXIMIZED_VERT))) {
+ if ((newstate & Qt::WindowMaximized) && !(oldstate & Qt::WindowFullScreen))
+ top->normalGeometry = geometry();
+ qt_change_net_wm_state(this, (newstate & Qt::WindowMaximized),
+ ATOM(_NET_WM_STATE_MAXIMIZED_HORZ),
+ ATOM(_NET_WM_STATE_MAXIMIZED_VERT));
+ } else if (! (newstate & Qt::WindowFullScreen)) {
+ if (newstate & Qt::WindowMaximized) {
+ // save original geometry
+ const QRect normalGeometry = geometry();
+
+ if (isVisible()) {
+ data->fstrut_dirty = true;
+ const QRect maxRect = QApplication::desktop()->availableGeometry(this);
+ const QRect r = top->normalGeometry;
+ const QRect fs = d->frameStrut();
+ setGeometry(maxRect.x() + fs.left(),
+ maxRect.y() + fs.top(),
+ maxRect.width() - fs.left() - fs.right(),
+ maxRect.height() - fs.top() - fs.bottom());
+ top->normalGeometry = r;
+ }
+
+ if (top->normalGeometry.width() < 0)
+ top->normalGeometry = normalGeometry;
+ } else {
+ // restore original geometry
+ setGeometry(top->normalGeometry);
+ }
+ }
+ }
+
+ if ((oldstate & Qt::WindowFullScreen) != (newstate & Qt::WindowFullScreen)) {
+ if (X11->isSupportedByWM(ATOM(_NET_WM_STATE_FULLSCREEN))) {
+ if (newstate & Qt::WindowFullScreen) {
+ top->normalGeometry = geometry();
+ top->fullScreenOffset = d->frameStrut().topLeft();
+ }
+ qt_change_net_wm_state(this, (newstate & Qt::WindowFullScreen),
+ ATOM(_NET_WM_STATE_FULLSCREEN));
+ } else {
+ needShow = isVisible();
+
+ if (newstate & Qt::WindowFullScreen) {
+ data->fstrut_dirty = true;
+ const QRect normalGeometry = geometry();
+ const QPoint fullScreenOffset = d->frameStrut().topLeft();
+
+ top->savedFlags = windowFlags();
+ setParent(0, Qt::Window | Qt::FramelessWindowHint);
+ const QRect r = top->normalGeometry;
+ setGeometry(qApp->desktop()->screenGeometry(this));
+ top->normalGeometry = r;
+
+ if (top->normalGeometry.width() < 0) {
+ top->normalGeometry = normalGeometry;
+ top->fullScreenOffset = fullScreenOffset;
+ }
+ } else {
+ setParent(0, top->savedFlags);
+
+ if (newstate & Qt::WindowMaximized) {
+ // from fullscreen to maximized
+ data->fstrut_dirty = true;
+ const QRect maxRect = QApplication::desktop()->availableGeometry(this);
+ const QRect r = top->normalGeometry;
+ const QRect fs = d->frameStrut();
+ setGeometry(maxRect.x() + fs.left(),
+ maxRect.y() + fs.top(),
+ maxRect.width() - fs.left() - fs.right(),
+ maxRect.height() - fs.top() - fs.bottom());
+ top->normalGeometry = r;
+ } else {
+ // restore original geometry
+ setGeometry(top->normalGeometry.adjusted(-top->fullScreenOffset.x(),
+ -top->fullScreenOffset.y(),
+ -top->fullScreenOffset.x(),
+ -top->fullScreenOffset.y()));
+ }
+ }
+ }
+ }
+
+ createWinId();
+ Q_ASSERT(testAttribute(Qt::WA_WState_Created));
+ if ((oldstate & Qt::WindowMinimized) != (newstate & Qt::WindowMinimized)) {
+ if (isVisible()) {
+ if (newstate & Qt::WindowMinimized) {
+ XEvent e;
+ e.xclient.type = ClientMessage;
+ e.xclient.message_type = ATOM(WM_CHANGE_STATE);
+ e.xclient.display = X11->display;
+ e.xclient.window = data->winid;
+ e.xclient.format = 32;
+ e.xclient.data.l[0] = IconicState;
+ e.xclient.data.l[1] = 0;
+ e.xclient.data.l[2] = 0;
+ e.xclient.data.l[3] = 0;
+ e.xclient.data.l[4] = 0;
+ XSendEvent(X11->display,
+ RootWindow(X11->display,d->xinfo.screen()),
+ False, (SubstructureNotifyMask|SubstructureRedirectMask), &e);
+ } else {
+ setAttribute(Qt::WA_Mapped);
+ XMapWindow(X11->display, effectiveWinId());
+ }
+ }
+
+ needShow = false;
+ }
+ }
+
+ data->window_state = newstate;
+
+ if (needShow)
+ show();
+
+ if (newstate & Qt::WindowActive)
+ activateWindow();
+
+ QWindowStateChangeEvent e(oldstate);
+ QApplication::sendEvent(this, &e);
+}
+
+/*!
+ \internal
+ Platform-specific part of QWidget::show().
+*/
+
+void QWidgetPrivate::show_sys()
+{
+ Q_Q(QWidget);
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+
+ if (q->testAttribute(Qt::WA_DontShowOnScreen)) {
+ invalidateBuffer(q->rect());
+ q->setAttribute(Qt::WA_Mapped);
+ if (QTLWExtra *tlwExtra = maybeTopData())
+ tlwExtra->waitingForMapNotify = 0;
+ return;
+ }
+
+ if (q->isWindow()) {
+ XWMHints *h = XGetWMHints(X11->display, q->internalWinId());
+ XWMHints wm_hints;
+ bool got_hints = h != 0;
+ if (!got_hints) {
+ memset(&wm_hints, 0, sizeof(wm_hints)); // make valgrind happy
+ h = &wm_hints;
+ }
+ h->initial_state = q->isMinimized() ? IconicState : NormalState;
+ h->flags |= StateHint;
+ XSetWMHints(X11->display, q->internalWinId(), h);
+ if (got_hints)
+ XFree((char *)h);
+
+ // update WM_NORMAL_HINTS
+ do_size_hints(q, extra);
+
+ // udpate WM_TRANSIENT_FOR
+ if (isTransient(q)) {
+ QWidget *p = q->parentWidget();
+
+#ifndef QT_NO_MENU
+ // hackish ... try to find the main window related to this QMenu
+ if (qobject_cast<QMenu *>(q)) {
+ p = static_cast<QMenuPrivate*>(this)->causedPopup.widget;
+ if (!p)
+ p = q->parentWidget();
+ if (!p)
+ p = QApplication::widgetAt(q->pos());
+ if (!p)
+ p = qApp->activeWindow();
+ }
+#endif
+ if (p)
+ p = p->window();
+ if (p) {
+ // transient for window
+ XSetTransientForHint(X11->display, q->internalWinId(), p->internalWinId());
+ } else {
+ // transient for group
+ XSetTransientForHint(X11->display, q->internalWinId(), X11->wm_client_leader);
+ }
+ }
+
+ // update _MOTIF_WM_HINTS
+ QtMWMHints mwmhints = GetMWMHints(X11->display, q->internalWinId());
+
+ if (data.window_modality != Qt::NonModal) {
+ switch (data.window_modality) {
+ case Qt::WindowModal:
+ mwmhints.input_mode = MWM_INPUT_PRIMARY_APPLICATION_MODAL;
+ break;
+ case Qt::ApplicationModal:
+ default:
+ mwmhints.input_mode = MWM_INPUT_FULL_APPLICATION_MODAL;
+ break;
+ }
+ mwmhints.flags |= MWM_HINTS_INPUT_MODE;
+ } else {
+ mwmhints.input_mode = MWM_INPUT_MODELESS;
+ mwmhints.flags &= ~MWM_HINTS_INPUT_MODE;
+ }
+
+ if (q->minimumSize() == q->maximumSize()) {
+ // fixed size, remove the resize handle (since mwm/dtwm
+ // isn't smart enough to do it itself)
+ mwmhints.flags |= MWM_HINTS_FUNCTIONS;
+ if (mwmhints.functions == MWM_FUNC_ALL) {
+ mwmhints.functions = MWM_FUNC_MOVE;
+ } else {
+ mwmhints.functions &= ~MWM_FUNC_RESIZE;
+ }
+
+ if (mwmhints.decorations == MWM_DECOR_ALL) {
+ mwmhints.flags |= MWM_HINTS_DECORATIONS;
+ mwmhints.decorations = (MWM_DECOR_BORDER
+ | MWM_DECOR_TITLE
+ | MWM_DECOR_MENU);
+ } else {
+ mwmhints.decorations &= ~MWM_DECOR_RESIZEH;
+ }
+
+ if (q->windowFlags() & Qt::WindowMinimizeButtonHint) {
+ mwmhints.flags |= MWM_HINTS_DECORATIONS;
+ mwmhints.decorations |= MWM_DECOR_MINIMIZE;
+ mwmhints.functions |= MWM_FUNC_MINIMIZE;
+ }
+ if (q->windowFlags() & Qt::WindowMaximizeButtonHint) {
+ mwmhints.flags |= MWM_HINTS_DECORATIONS;
+ mwmhints.decorations |= MWM_DECOR_MAXIMIZE;
+ mwmhints.functions |= MWM_FUNC_MAXIMIZE;
+ }
+ if (q->windowFlags() & Qt::WindowCloseButtonHint)
+ mwmhints.functions |= MWM_FUNC_CLOSE;
+ }
+
+ SetMWMHints(X11->display, q->internalWinId(), mwmhints);
+
+ // update _NET_WM_STATE
+ QVector<Atom> netWmState = getNetWmState(q);
+
+ Qt::WindowFlags flags = q->windowFlags();
+ if (flags & Qt::WindowStaysOnTopHint) {
+ if (flags & Qt::WindowStaysOnBottomHint)
+ qWarning() << "QWidget: Incompatible window flags: the window can't be on top and on bottom at the same time";
+ if (!netWmState.contains(ATOM(_NET_WM_STATE_ABOVE)))
+ netWmState.append(ATOM(_NET_WM_STATE_ABOVE));
+ if (!netWmState.contains(ATOM(_NET_WM_STATE_STAYS_ON_TOP)))
+ netWmState.append(ATOM(_NET_WM_STATE_STAYS_ON_TOP));
+ } else if (flags & Qt::WindowStaysOnBottomHint) {
+ if (!netWmState.contains(ATOM(_NET_WM_STATE_BELOW)))
+ netWmState.append(ATOM(_NET_WM_STATE_BELOW));
+ }
+ if (q->isFullScreen()) {
+ if (!netWmState.contains(ATOM(_NET_WM_STATE_FULLSCREEN)))
+ netWmState.append(ATOM(_NET_WM_STATE_FULLSCREEN));
+ }
+ if (q->isMaximized()) {
+ if (!netWmState.contains(ATOM(_NET_WM_STATE_MAXIMIZED_HORZ)))
+ netWmState.append(ATOM(_NET_WM_STATE_MAXIMIZED_HORZ));
+ if (!netWmState.contains(ATOM(_NET_WM_STATE_MAXIMIZED_VERT)))
+ netWmState.append(ATOM(_NET_WM_STATE_MAXIMIZED_VERT));
+ }
+ if (data.window_modality != Qt::NonModal) {
+ if (!netWmState.contains(ATOM(_NET_WM_STATE_MODAL)))
+ netWmState.append(ATOM(_NET_WM_STATE_MODAL));
+ }
+
+ if (!netWmState.isEmpty()) {
+ XChangeProperty(X11->display, q->internalWinId(),
+ ATOM(_NET_WM_STATE), XA_ATOM, 32, PropModeReplace,
+ (unsigned char *) netWmState.data(), netWmState.size());
+ } else {
+ XDeleteProperty(X11->display, q->internalWinId(), ATOM(_NET_WM_STATE));
+ }
+
+ // set _NET_WM_USER_TIME
+ Time userTime = X11->userTime;
+ bool setUserTime = false;
+ if (q->testAttribute(Qt::WA_ShowWithoutActivating)) {
+ userTime = 0;
+ setUserTime = true;
+ } else if (userTime != CurrentTime) {
+ setUserTime = true;
+ }
+ if (setUserTime)
+ qt_net_update_user_time(q, userTime);
+
+#ifndef QT_NO_XSYNC
+ if (!topData()->syncUpdateCounter) {
+ XSyncValue value;
+ XSyncIntToValue(&value, 0);
+ topData()->syncUpdateCounter = XSyncCreateCounter(X11->display, value);
+
+ XChangeProperty(X11->display, q->internalWinId(),
+ ATOM(_NET_WM_SYNC_REQUEST_COUNTER),
+ XA_CARDINAL,
+ 32, PropModeReplace,
+ (uchar *) &topData()->syncUpdateCounter, 1);
+
+ topData()->newCounterValueHi = 0;
+ topData()->newCounterValueLo = 0;
+ }
+#endif
+
+ if (!topData()->embedded
+ && (topData()->validWMState || topData()->waitingForMapNotify)
+ && !q->isMinimized()) {
+ X11->deferred_map.append(q);
+ return;
+ }
+
+ if (q->isMaximized() && !q->isFullScreen()
+ && !(X11->isSupportedByWM(ATOM(_NET_WM_STATE_MAXIMIZED_HORZ))
+ && X11->isSupportedByWM(ATOM(_NET_WM_STATE_MAXIMIZED_VERT)))) {
+ XMapWindow(X11->display, q->internalWinId());
+ data.fstrut_dirty = true;
+ qt_x11_wait_for_window_manager(q);
+
+ // if the wm was not smart enough to adjust our size, do that manually
+ QRect maxRect = QApplication::desktop()->availableGeometry(q);
+
+ QTLWExtra *top = topData();
+ QRect normalRect = top->normalGeometry;
+ const QRect fs = frameStrut();
+
+ q->setGeometry(maxRect.x() + fs.left(),
+ maxRect.y() + fs.top(),
+ maxRect.width() - fs.left() - fs.right(),
+ maxRect.height() - fs.top() - fs.bottom());
+
+ // restore the original normalGeometry
+ top->normalGeometry = normalRect;
+ // internalSetGeometry() clears the maximized flag... make sure we set it back
+ data.window_state = data.window_state | Qt::WindowMaximized;
+ q->setAttribute(Qt::WA_Mapped);
+ return;
+ }
+
+ if (q->isFullScreen() && !X11->isSupportedByWM(ATOM(_NET_WM_STATE_FULLSCREEN))) {
+ XMapWindow(X11->display, q->internalWinId());
+ qt_x11_wait_for_window_manager(q);
+ q->setAttribute(Qt::WA_Mapped);
+ return;
+ }
+ }
+
+ invalidateBuffer(q->rect());
+
+ if (q->testAttribute(Qt::WA_OutsideWSRange))
+ return;
+ q->setAttribute(Qt::WA_Mapped);
+ if (q->isWindow())
+ topData()->waitingForMapNotify = 1;
+
+ if (!q->isWindow()
+ && (!q->autoFillBackground()
+ || q->palette().brush(q->backgroundRole()).style() == Qt::LinearGradientPattern)) {
+ if (q->internalWinId()) {
+ XSetWindowBackgroundPixmap(X11->display, q->internalWinId(), XNone);
+ XMapWindow(X11->display, q->internalWinId());
+ updateSystemBackground();
+ }
+ return;
+ }
+
+ if (q->internalWinId())
+ XMapWindow(X11->display, q->internalWinId());
+
+ // Freedesktop.org Startup Notification
+ if (X11->startupId && q->isWindow()) {
+ QByteArray message("remove: ID=");
+ message.append(X11->startupId);
+ sendStartupMessage(message.constData());
+ X11->startupId = 0;
+ }
+}
+
+/*!
+ \internal
+ Platform-specific part of QWidget::show().
+*/
+
+void QWidgetPrivate::sendStartupMessage(const char *message) const
+{
+ Q_Q(const QWidget);
+
+ if (!message)
+ return;
+
+ XEvent xevent;
+ xevent.xclient.type = ClientMessage;
+ xevent.xclient.message_type = ATOM(_NET_STARTUP_INFO_BEGIN);
+ xevent.xclient.display = X11->display;
+ xevent.xclient.window = q->internalWinId();
+ xevent.xclient.format = 8;
+
+ Window rootWindow = RootWindow(X11->display, DefaultScreen(X11->display));
+ uint sent = 0;
+ uint length = strlen(message) + 1;
+ do {
+ if (sent == 20)
+ xevent.xclient.message_type = ATOM(_NET_STARTUP_INFO);
+
+ for (uint i = 0; i < 20 && i + sent <= length; i++)
+ xevent.xclient.data.b[i] = message[i + sent++];
+
+ XSendEvent(X11->display, rootWindow, false, PropertyChangeMask, &xevent);
+ } while (sent <= length);
+}
+
+void QWidgetPrivate::setNetWmWindowTypes()
+{
+ Q_Q(QWidget);
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+
+ if (!q->isWindow()) {
+ if (q->internalWinId())
+ XDeleteProperty(X11->display, q->internalWinId(), ATOM(_NET_WM_WINDOW_TYPE));
+ return;
+ }
+
+ QVector<long> windowTypes;
+
+ // manual selection 1 (these are never set by Qt and take precedence)
+ if (q->testAttribute(Qt::WA_X11NetWmWindowTypeDesktop))
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_DESKTOP));
+ if (q->testAttribute(Qt::WA_X11NetWmWindowTypeDock))
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_DOCK));
+ if (q->testAttribute(Qt::WA_X11NetWmWindowTypeNotification))
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_NOTIFICATION));
+
+ // manual selection 2 (Qt uses these during auto selection);
+ if (q->testAttribute(Qt::WA_X11NetWmWindowTypeUtility))
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_UTILITY));
+ if (q->testAttribute(Qt::WA_X11NetWmWindowTypeSplash))
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_SPLASH));
+ if (q->testAttribute(Qt::WA_X11NetWmWindowTypeDialog))
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_DIALOG));
+ if (q->testAttribute(Qt::WA_X11NetWmWindowTypeToolTip))
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_TOOLTIP));
+
+ // manual selection 3 (these can be set by Qt, but don't have a
+ // corresponding Qt::WindowType). note that order of the *MENU
+ // atoms is important
+ if (q->testAttribute(Qt::WA_X11NetWmWindowTypeMenu))
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_MENU));
+ if (q->testAttribute(Qt::WA_X11NetWmWindowTypeDropDownMenu))
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_DROPDOWN_MENU));
+ if (q->testAttribute(Qt::WA_X11NetWmWindowTypePopupMenu))
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_POPUP_MENU));
+ if (q->testAttribute(Qt::WA_X11NetWmWindowTypeToolBar))
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_TOOLBAR));
+ if (q->testAttribute(Qt::WA_X11NetWmWindowTypeCombo))
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_COMBO));
+ if (q->testAttribute(Qt::WA_X11NetWmWindowTypeDND))
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_DND));
+
+ // automatic selection
+ switch (q->windowType()) {
+ case Qt::Dialog:
+ case Qt::Sheet:
+ // dialog netwm type
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_DIALOG));
+ break;
+
+ case Qt::Tool:
+ case Qt::Drawer:
+ // utility netwm type
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_UTILITY));
+ break;
+
+ case Qt::ToolTip:
+ // tooltip netwm type
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_TOOLTIP));
+ break;
+
+ case Qt::SplashScreen:
+ // splash netwm type
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_SPLASH));
+ break;
+
+ default:
+ break;
+ }
+
+ if (q->windowFlags() & Qt::FramelessWindowHint) {
+ // override netwm type - quick and easy for KDE noborder
+ windowTypes.append(ATOM(_KDE_NET_WM_WINDOW_TYPE_OVERRIDE));
+ }
+
+ // normal netwm type - default
+ windowTypes.append(ATOM(_NET_WM_WINDOW_TYPE_NORMAL));
+
+ if (!windowTypes.isEmpty()) {
+ XChangeProperty(X11->display, q->winId(), ATOM(_NET_WM_WINDOW_TYPE), XA_ATOM, 32,
+ PropModeReplace, (unsigned char *) windowTypes.constData(),
+ windowTypes.count());
+ } else {
+ XDeleteProperty(X11->display, q->winId(), ATOM(_NET_WM_WINDOW_TYPE));
+ }
+}
+
+/*!
+ \internal
+ Platform-specific part of QWidget::hide().
+*/
+
+void QWidgetPrivate::hide_sys()
+{
+ Q_Q(QWidget);
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ deactivateWidgetCleanup();
+ if (q->isWindow()) {
+ X11->deferred_map.removeAll(q);
+ if (q->internalWinId()) // in nsplugin, may be 0
+ XWithdrawWindow(X11->display, q->internalWinId(), xinfo.screen());
+ XFlush(X11->display);
+ } else {
+ invalidateBuffer(q->rect());
+ if (q->internalWinId()) // in nsplugin, may be 0
+ XUnmapWindow(X11->display, q->internalWinId());
+ }
+ q->setAttribute(Qt::WA_Mapped, false);
+}
+
+void QWidgetPrivate::setFocus_sys()
+{
+
+}
+
+
+void QWidgetPrivate::raise_sys()
+{
+ Q_Q(QWidget);
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ if (q->internalWinId())
+ XRaiseWindow(X11->display, q->internalWinId());
+}
+
+void QWidgetPrivate::lower_sys()
+{
+ Q_Q(QWidget);
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ if (q->internalWinId())
+ XLowerWindow(X11->display, q->internalWinId());
+ if(!q->isWindow())
+ invalidateBuffer(q->rect());
+}
+
+void QWidgetPrivate::stackUnder_sys(QWidget* w)
+{
+ Q_Q(QWidget);
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ if (q->internalWinId() && w->internalWinId()) {
+ Window stack[2];
+ stack[0] = w->internalWinId();;
+ stack[1] = q->internalWinId();
+ XRestackWindows(X11->display, stack, 2);
+ }
+ if(!q->isWindow() || !w->internalWinId())
+ invalidateBuffer(q->rect());
+}
+
+
+static void do_size_hints(QWidget* widget, QWExtra *x)
+{
+ Q_ASSERT(widget->testAttribute(Qt::WA_WState_Created));
+ XSizeHints s;
+ s.flags = 0;
+ if (x) {
+ QRect g = widget->geometry();
+ s.x = g.x();
+ s.y = g.y();
+ s.width = g.width();
+ s.height = g.height();
+ if (x->minw > 0 || x->minh > 0) {
+ // add minimum size hints
+ s.flags |= PMinSize;
+ s.min_width = qMin(XCOORD_MAX, x->minw);
+ s.min_height = qMin(XCOORD_MAX, x->minh);
+ }
+ if (x->maxw < QWIDGETSIZE_MAX || x->maxh < QWIDGETSIZE_MAX) {
+ // add maximum size hints
+ s.flags |= PMaxSize;
+ s.max_width = qMin(XCOORD_MAX, x->maxw);
+ s.max_height = qMin(XCOORD_MAX, x->maxh);
+ }
+ if (x->topextra &&
+ (x->topextra->incw > 0 || x->topextra->inch > 0)) {
+ // add resize increment hints
+ s.flags |= PResizeInc | PBaseSize;
+ s.width_inc = x->topextra->incw;
+ s.height_inc = x->topextra->inch;
+ s.base_width = x->topextra->basew;
+ s.base_height = x->topextra->baseh;
+ }
+ }
+ if (widget->testAttribute(Qt::WA_Moved)) {
+ // user (i.e. command-line) specified position
+ s.flags |= USPosition;
+ s.flags |= PPosition;
+ }
+ if (widget->testAttribute(Qt::WA_Resized)) {
+ // user (i.e. command-line) specified size
+ s.flags |= USSize;
+ s.flags |= PSize;
+ }
+ s.flags |= PWinGravity;
+ if (widget->testAttribute(Qt::WA_Moved) && x && x->topextra && !x->topextra->posFromMove) {
+ // position came from setGeometry(), tell the WM that we don't
+ // want our window gravity-shifted
+ s.win_gravity = StaticGravity;
+ } else {
+ // position came from move()
+ s.x = widget->x();
+ s.y = widget->y();
+ s.win_gravity = QApplication::isRightToLeft() ? NorthEastGravity : NorthWestGravity;
+ }
+ if (widget->internalWinId())
+ XSetWMNormalHints(X11->display, widget->internalWinId(), &s);
+}
+
+
+/*
+ Helper function for non-toplevel widgets. Helps to map Qt's 32bit
+ coordinate system to X11's 16bit coordinate system.
+
+ Sets the geometry of the widget to data.crect, but clipped to sizes
+ that X can handle. Unmaps widgets that are completely outside the
+ valid range.
+
+ Maintains data.wrect, which is the geometry of the X widget,
+ measured in this widget's coordinate system.
+
+ if the parent is not clipped, parentWRect is empty, otherwise
+ parentWRect is the geometry of the parent's X rect, measured in
+ parent's coord sys
+ */
+void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &)
+{
+ Q_Q(QWidget);
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+
+ /*
+ There are up to four different coordinate systems here:
+ Qt coordinate system for this widget.
+ X coordinate system for this widget (relative to wrect).
+ Qt coordinate system for parent
+ X coordinate system for parent (relative to parent's wrect).
+ */
+ Display *dpy = xinfo.display();
+ QRect validRange(-XCOORD_MAX,-XCOORD_MAX, 2*XCOORD_MAX, 2*XCOORD_MAX);
+ QRect wrectRange(-WRECT_MAX,-WRECT_MAX, 2*WRECT_MAX, 2*WRECT_MAX);
+ QRect wrect;
+ //xrect is the X geometry of my X widget. (starts out in parent's Qt coord sys, and ends up in parent's X coord sys)
+ QRect xrect = data.crect;
+
+ const QWidget *const parent = q->parentWidget();
+ QRect parentWRect = parent->data->wrect;
+
+ if (parentWRect.isValid()) {
+ // parent is clipped, and we have to clip to the same limit as parent
+ if (!parentWRect.contains(xrect)) {
+ xrect &= parentWRect;
+ wrect = xrect;
+ //translate from parent's to my Qt coord sys
+ wrect.translate(-data.crect.topLeft());
+ }
+ //translate from parent's Qt coords to parent's X coords
+ xrect.translate(-parentWRect.topLeft());
+
+ } else {
+ // parent is not clipped, we may or may not have to clip
+
+ if (data.wrect.isValid() && QRect(QPoint(),data.crect.size()).contains(data.wrect)) {
+ // This is where the main optimization is: we are already
+ // clipped, and if our clip is still valid, we can just
+ // move our window, and do not need to move or clip
+ // children
+
+ QRect vrect = xrect & parent->rect();
+ vrect.translate(-data.crect.topLeft()); //the part of me that's visible through parent, in my Qt coords
+ if (data.wrect.contains(vrect)) {
+ xrect = data.wrect;
+ xrect.translate(data.crect.topLeft());
+ if (data.winid)
+ XMoveWindow(dpy, data.winid, xrect.x(), xrect.y());
+ return;
+ }
+ }
+
+ if (!validRange.contains(xrect)) {
+ // we are too big, and must clip
+ xrect &=wrectRange;
+ wrect = xrect;
+ wrect.translate(-data.crect.topLeft());
+ //parent's X coord system is equal to parent's Qt coord
+ //sys, so we don't need to map xrect.
+ }
+
+ }
+
+ // unmap if we are outside the valid window system coord system
+ bool outsideRange = !xrect.isValid();
+ bool mapWindow = false;
+ if (q->testAttribute(Qt::WA_OutsideWSRange) != outsideRange) {
+ q->setAttribute(Qt::WA_OutsideWSRange, outsideRange);
+ if (outsideRange) {
+ if (data.winid)
+ XUnmapWindow(dpy, data.winid);
+ q->setAttribute(Qt::WA_Mapped, false);
+ } else if (!q->isHidden()) {
+ mapWindow = true;
+ }
+ }
+
+ if (outsideRange)
+ return;
+
+ bool jump = (data.wrect != wrect);
+ data.wrect = wrect;
+
+
+ // and now recursively for all children...
+ // ### can be optimized
+ for (int i = 0; i < children.size(); ++i) {
+ QObject *object = children.at(i);
+ if (object->isWidgetType()) {
+ QWidget *w = static_cast<QWidget *>(object);
+ if (!w->isWindow() && w->testAttribute(Qt::WA_WState_Created))
+ w->d_func()->setWSGeometry(jump);
+ }
+ }
+
+ if (data.winid) {
+ // move ourselves to the new position and map (if necessary) after
+ // the movement. Rationale: moving unmapped windows is much faster
+ // than moving mapped windows
+ if (jump) //avoid flicker when jumping
+ XSetWindowBackgroundPixmap(dpy, data.winid, XNone);
+ if (!parent->internalWinId())
+ xrect.translate(parent->mapTo(q->nativeParentWidget(), QPoint(0, 0)));
+ XMoveResizeWindow(dpy, data.winid, xrect.x(), xrect.y(), xrect.width(), xrect.height());
+ }
+
+ //to avoid flicker, we have to show children after the helper widget has moved
+ if (jump) {
+ for (int i = 0; i < children.size(); ++i) {
+ QObject *object = children.at(i);
+ if (object->isWidgetType()) {
+ QWidget *w = static_cast<QWidget *>(object);
+ if (!w->testAttribute(Qt::WA_OutsideWSRange) && !w->testAttribute(Qt::WA_Mapped) && !w->isHidden()) {
+ w->setAttribute(Qt::WA_Mapped);
+ if (w->internalWinId())
+ XMapWindow(dpy, w->data->winid);
+ }
+ }
+ }
+ }
+
+
+ if (jump && data.winid)
+ XClearArea(dpy, data.winid, 0, 0, wrect.width(), wrect.height(), True);
+
+ if (mapWindow && !dontShow) {
+ q->setAttribute(Qt::WA_Mapped);
+ if (data.winid)
+ XMapWindow(dpy, data.winid);
+ }
+}
+
+void QWidgetPrivate::setGeometry_sys(int x, int y, int w, int h, bool isMove)
+{
+ Q_Q(QWidget);
+ Q_ASSERT(q->testAttribute(Qt::WA_WState_Created));
+ Display *dpy = X11->display;
+
+ if ((q->windowType() == Qt::Desktop))
+ return;
+ if (q->isWindow()) {
+ if (!X11->isSupportedByWM(ATOM(_NET_WM_STATE_MAXIMIZED_VERT))
+ && !X11->isSupportedByWM(ATOM(_NET_WM_STATE_MAXIMIZED_HORZ)))
+ data.window_state &= ~Qt::WindowMaximized;
+ if (!X11->isSupportedByWM(ATOM(_NET_WM_STATE_FULLSCREEN)))
+ data.window_state &= ~Qt::WindowFullScreen;
+ if (QTLWExtra *topData = maybeTopData())
+ topData->normalGeometry = QRect(0,0,-1,-1);
+ } else {
+ uint s = data.window_state;
+ s &= ~(Qt::WindowMaximized | Qt::WindowFullScreen);
+ data.window_state = s;
+ }
+ if (extra) { // any size restrictions?
+ w = qMin(w,extra->maxw);
+ h = qMin(h,extra->maxh);
+ w = qMax(w,extra->minw);
+ h = qMax(h,extra->minh);
+ }
+ QPoint oldPos(q->pos());
+ QSize oldSize(q->size());
+ QRect oldGeom(data.crect);
+ QRect r(x, y, w, h);
+
+ // We only care about stuff that changes the geometry, or may
+ // cause the window manager to change its state
+ if (!q->isWindow() && oldGeom == r)
+ return;
+
+ data.crect = r;
+ bool isResize = q->size() != oldSize;
+
+ if (q->isWindow()) {
+ if (w == 0 || h == 0) {
+ q->setAttribute(Qt::WA_OutsideWSRange, true);
+ if (q->isVisible() && q->testAttribute(Qt::WA_Mapped))
+ hide_sys();
+ } else if (q->isVisible() && q->testAttribute(Qt::WA_OutsideWSRange)) {
+ q->setAttribute(Qt::WA_OutsideWSRange, false);
+
+ // put the window in its place and show it
+ if (data.winid)
+ XMoveResizeWindow(dpy, data.winid, x, y, w, h);
+ topData()->posFromMove = false; // force StaticGravity
+ do_size_hints(q, extra);
+ show_sys();
+ } else {
+ q->setAttribute(Qt::WA_OutsideWSRange, false);
+ if (!q->isVisible())
+ do_size_hints(q, extra);
+ if (isMove) {
+ if ((data.window_flags & Qt::X11BypassWindowManagerHint) == Qt::X11BypassWindowManagerHint
+ // work around 4Dwm's incompliance with ICCCM 4.1.5
+ || X11->desktopEnvironment == DE_4DWM) {
+ if (data.winid)
+ XMoveResizeWindow(dpy, data.winid, x, y, w, h);
+ } else if (q->isVisible()
+ && topData()->validWMState
+ && X11->isSupportedByWM(ATOM(_NET_MOVERESIZE_WINDOW))) {
+ XEvent e;
+ e.xclient.type = ClientMessage;
+ e.xclient.message_type = ATOM(_NET_MOVERESIZE_WINDOW);
+ e.xclient.display = X11->display;
+ e.xclient.window = q->internalWinId();
+ e.xclient.format = 32;
+ e.xclient.data.l[0] = StaticGravity | 1<<8 | 1<<9 | 1<<10 | 1<<11 | 1<<12;
+ e.xclient.data.l[1] = x;
+ e.xclient.data.l[2] = y;
+ e.xclient.data.l[3] = w;
+ e.xclient.data.l[4] = h;
+ XSendEvent(X11->display, RootWindow(X11->display, q->x11Info().screen()),
+ false, (SubstructureNotifyMask | SubstructureRedirectMask), &e);
+ } else if (data.winid) {
+ // pos() is right according to ICCCM 4.1.5
+ XMoveResizeWindow(dpy, data.winid, q->pos().x(), q->pos().y(), w, h);
+ }
+ } else if (isResize && data.winid) {
+ if (!q->isVisible()
+ && topData()->validWMState
+ && !q->testAttribute(Qt::WA_PendingMoveEvent)) {
+ /*
+ even though we've not visible, we could be in a
+ race w/ the window manager, and it may ignore
+ our ConfigureRequest. setting posFromMove to
+ false makes sure that doDeferredMap() in
+ qapplication_x11.cpp keeps the window in the
+ right place
+ */
+ topData()->posFromMove = false;
+ }
+ XResizeWindow(dpy, data.winid, w, h);
+ }
+ }
+ if (isResize && !q->testAttribute(Qt::WA_DontShowOnScreen)) // set config pending only on resize, see qapplication_x11.cpp, translateConfigEvent()
+ q->setAttribute(Qt::WA_WState_ConfigPending);
+
+ } else {
+ QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData();
+ const bool inTopLevelResize = tlwExtra ? tlwExtra->inTopLevelResize : false;
+ const bool disableInTopLevelResize = inTopLevelResize && q->internalWinId();
+ if (disableInTopLevelResize) {
+ // Top-level resize optimization does not work for native child widgets;
+ // disable it for this particular widget.
+ tlwExtra->inTopLevelResize = false;
+ }
+
+ if (!isResize && (!inTopLevelResize || disableInTopLevelResize) && q->isVisible()) {
+ moveRect(QRect(oldPos, oldSize), x - oldPos.x(), y - oldPos.y());
+ }
+ if (q->testAttribute(Qt::WA_WState_Created))
+ setWSGeometry();
+
+ if (isResize && (!inTopLevelResize || disableInTopLevelResize) && q->isVisible())
+ invalidateBuffer_resizeHelper(oldPos, oldSize);
+
+ if (disableInTopLevelResize)
+ tlwExtra->inTopLevelResize = true;
+ }
+
+ if (q->isVisible()) {
+ if (isMove && q->pos() != oldPos) {
+ if (X11->desktopEnvironment != DE_4DWM) {
+ // pos() is right according to ICCCM 4.1.5
+ QMoveEvent e(q->pos(), oldPos);
+ QApplication::sendEvent(q, &e);
+ } else {
+ // work around 4Dwm's incompliance with ICCCM 4.1.5
+ QMoveEvent e(data.crect.topLeft(), oldGeom.topLeft());
+ QApplication::sendEvent(q, &e);
+ }
+ }
+ if (isResize) {
+ static bool slowResize = qgetenv("QT_SLOW_TOPLEVEL_RESIZE").toInt();
+ // If we have a backing store with static contents, we have to disable the top-level
+ // resize optimization in order to get invalidated regions for resized widgets.
+ // The optimization discards all invalidateBuffer() calls since we're going to
+ // repaint everything anyways, but that's not the case with static contents.
+ const bool setTopLevelResize = !slowResize && q->isWindow() && extra && extra->topextra
+ && !extra->topextra->inTopLevelResize
+ && (!extra->topextra->backingStore
+ || !extra->topextra->backingStore->hasStaticContents());
+ if (setTopLevelResize)
+ extra->topextra->inTopLevelResize = true;
+ QResizeEvent e(q->size(), oldSize);
+ QApplication::sendEvent(q, &e);
+ if (setTopLevelResize)
+ extra->topextra->inTopLevelResize = false;
+ }
+ } else {
+ if (isMove && q->pos() != oldPos)
+ q->setAttribute(Qt::WA_PendingMoveEvent, true);
+ if (isResize)
+ q->setAttribute(Qt::WA_PendingResizeEvent, true);
+ }
+}
+
+void QWidgetPrivate::setConstraints_sys()
+{
+ Q_Q(QWidget);
+#ifdef ALIEN_DEBUG
+ qDebug() << "QWidgetPrivate::setConstraints_sys START" << q;
+#endif
+ if (q->testAttribute(Qt::WA_WState_Created))
+ do_size_hints(q, extra);
+#ifdef ALIEN_DEBUG
+ qDebug() << "QWidgetPrivate::setConstraints_sys END" << q;
+#endif
+}
+
+void QWidgetPrivate::scroll_sys(int dx, int dy)
+{
+ Q_Q(QWidget);
+
+ scrollChildren(dx, dy);
+ if (!paintOnScreen()) {
+ scrollRect(q->rect(), dx, dy);
+ } else {
+ scroll_sys(dx, dy, QRect());
+ }
+}
+
+void QWidgetPrivate::scroll_sys(int dx, int dy, const QRect &r)
+{
+ Q_Q(QWidget);
+
+ if (!paintOnScreen()) {
+ scrollRect(r, dx, dy);
+ return;
+ }
+ bool valid_rect = r.isValid();
+ bool just_update = qAbs(dx) > q->width() || qAbs(dy) > q->height();
+ QRect sr = valid_rect ? r : clipRect();
+ if (just_update)
+ q->update();
+ else if (!valid_rect)
+ dirty.translate(dx, dy);
+
+ int x1, y1, x2, y2, w = sr.width(), h = sr.height();
+ if (dx > 0) {
+ x1 = sr.x();
+ x2 = x1+dx;
+ w -= dx;
+ } else {
+ x2 = sr.x();
+ x1 = x2-dx;
+ w += dx;
+ }
+ if (dy > 0) {
+ y1 = sr.y();
+ y2 = y1+dy;
+ h -= dy;
+ } else {
+ y2 = sr.y();
+ y1 = y2-dy;
+ h += dy;
+ }
+
+ if (dx == 0 && dy == 0)
+ return;
+
+ Display *dpy = X11->display;
+ // Want expose events
+ if (w > 0 && h > 0 && !just_update && q->internalWinId()) {
+ GC gc = XCreateGC(dpy, q->internalWinId(), 0, 0);
+ XSetGraphicsExposures(dpy, gc, True);
+ XCopyArea(dpy, q->internalWinId(), q->internalWinId(), gc, x1, y1, w, h, x2, y2);
+ XFreeGC(dpy, gc);
+ }
+
+ if (!valid_rect && !children.isEmpty()) { // scroll children
+ QPoint pd(dx, dy);
+ for (int i = 0; i < children.size(); ++i) { // move all children
+ register QObject *object = children.at(i);
+ if (object->isWidgetType()) {
+ QWidget *w = static_cast<QWidget *>(object);
+ if (!w->isWindow())
+ w->move(w->pos() + pd);
+ }
+ }
+ }
+
+ if (just_update)
+ return;
+
+ // Don't let the server be bogged-down with repaint events
+ bool repaint_immediately = (qt_sip_count(q) < 3 && !q->testAttribute(Qt::WA_WState_InPaintEvent));
+
+ if (dx) {
+ int x = x2 == sr.x() ? sr.x()+w : sr.x();
+ if (repaint_immediately)
+ q->repaint(x, sr.y(), qAbs(dx), sr.height());
+ else if (q->internalWinId())
+ XClearArea(dpy, data.winid, x, sr.y(), qAbs(dx), sr.height(), True);
+ }
+ if (dy) {
+ int y = y2 == sr.y() ? sr.y()+h : sr.y();
+ if (repaint_immediately)
+ q->repaint(sr.x(), y, sr.width(), qAbs(dy));
+ else if (q->internalWinId())
+ XClearArea(dpy, data.winid, sr.x(), y, sr.width(), qAbs(dy), True);
+ }
+
+ qt_insert_sip(q, dx, dy); // #### ignores r
+}
+
+int QWidget::metric(PaintDeviceMetric m) const
+{
+ Q_D(const QWidget);
+ int val;
+ if (m == PdmWidth) {
+ val = data->crect.width();
+ } else if (m == PdmHeight) {
+ val = data->crect.height();
+ } else {
+ Display *dpy = X11->display;
+ int scr = d->xinfo.screen();
+ switch (m) {
+ case PdmDpiX:
+ case PdmPhysicalDpiX:
+ if (d->extra && d->extra->customDpiX)
+ val = d->extra->customDpiX;
+ else if (d->parent)
+ val = static_cast<QWidget *>(d->parent)->metric(m);
+ else
+ val = QX11Info::appDpiX(scr);
+ break;
+ case PdmDpiY:
+ case PdmPhysicalDpiY:
+ if (d->extra && d->extra->customDpiY)
+ val = d->extra->customDpiY;
+ else if (d->parent)
+ val = static_cast<QWidget *>(d->parent)->metric(m);
+ else
+ val = QX11Info::appDpiY(scr);
+ break;
+ case PdmWidthMM:
+ val = (DisplayWidthMM(dpy,scr)*data->crect.width())/
+ DisplayWidth(dpy,scr);
+ break;
+ case PdmHeightMM:
+ val = (DisplayHeightMM(dpy,scr)*data->crect.height())/
+ DisplayHeight(dpy,scr);
+ break;
+ case PdmNumColors:
+ val = d->xinfo.cells();
+ break;
+ case PdmDepth:
+ val = d->xinfo.depth();
+ break;
+ default:
+ val = 0;
+ qWarning("QWidget::metric: Invalid metric command");
+ }
+ }
+ return val;
+}
+
+void QWidgetPrivate::createSysExtra()
+{
+ extra->compress_events = true;
+ extra->xDndProxy = 0;
+}
+
+void QWidgetPrivate::deleteSysExtra()
+{
+}
+
+void QWidgetPrivate::createTLSysExtra()
+{
+ extra->topextra->spont_unmapped = 0;
+ extra->topextra->dnd = 0;
+ extra->topextra->validWMState = 0;
+ extra->topextra->waitingForMapNotify = 0;
+ extra->topextra->parentWinId = 0;
+ extra->topextra->userTimeWindow = 0;
+#ifndef QT_NO_XSYNC
+ extra->topextra->syncUpdateCounter = 0;
+ extra->topextra->syncRequestTimestamp = 0;
+ extra->topextra->newCounterValueHi = 0;
+ extra->topextra->newCounterValueLo = 0;
+#endif
+}
+
+void QWidgetPrivate::deleteTLSysExtra()
+{
+ // don't destroy input context here. it will be destroyed in
+ // QWidget::destroy() destroyInputContext();
+}
+
+void QWidgetPrivate::registerDropSite(bool on)
+{
+ Q_UNUSED(on);
+}
+
+void QWidgetPrivate::setMask_sys(const QRegion &region)
+{
+ Q_Q(QWidget);
+ if (!q->internalWinId())
+ return;
+
+ if (region.isEmpty()) {
+ XShapeCombineMask(X11->display, q->internalWinId(), ShapeBounding, 0, 0,
+ XNone, ShapeSet);
+ } else {
+ XShapeCombineRegion(X11->display, q->internalWinId(), ShapeBounding, 0, 0,
+ region.handle(), ShapeSet);
+ }
+}
+
+/*!
+ \internal
+
+ Computes the frame rectangle when needed. This is an internal function, you
+ should never call this.
+*/
+
+void QWidgetPrivate::updateFrameStrut()
+{
+ Q_Q(QWidget);
+
+ QTLWExtra *top = topData();
+ if (!top->validWMState) {
+ return;
+ }
+ if (!q->isWindow() && !q->internalWinId()) {
+ data.fstrut_dirty = false;
+ return;
+ }
+
+ Atom type_ret;
+ Window l = q->effectiveWinId(), w = l, p, r; // target window, its parent, root
+ Window *c;
+ int i_unused;
+ unsigned int nc;
+ unsigned char *data_ret;
+ unsigned long l_unused;
+
+ while (XQueryTree(X11->display, w, &r, &p, &c, &nc)) {
+ if (c && nc > 0)
+ XFree(c);
+
+ if (! p) {
+ qWarning("QWidget::updateFrameStrut: No parent");
+ return;
+ }
+
+ // if the parent window is the root window, an Enlightenment virtual root or
+ // a NET WM virtual root window, stop here
+ data_ret = 0;
+ if (p == r ||
+ (XGetWindowProperty(X11->display, p,
+ ATOM(ENLIGHTENMENT_DESKTOP), 0, 1, False, XA_CARDINAL,
+ &type_ret, &i_unused, &l_unused, &l_unused,
+ &data_ret) == Success &&
+ type_ret == XA_CARDINAL)) {
+ if (data_ret)
+ XFree(data_ret);
+
+ break;
+ } else if (X11->isSupportedByWM(ATOM(_NET_VIRTUAL_ROOTS)) && X11->net_virtual_root_list) {
+ int i = 0;
+ while (X11->net_virtual_root_list[i] != 0) {
+ if (X11->net_virtual_root_list[i++] == p)
+ break;
+ }
+ }
+
+ l = w;
+ w = p;
+ }
+
+ // we have our window
+ int transx, transy;
+ XWindowAttributes wattr;
+ if (XTranslateCoordinates(X11->display, l, w,
+ 0, 0, &transx, &transy, &p) &&
+ XGetWindowAttributes(X11->display, w, &wattr)) {
+ top->frameStrut.setCoords(transx,
+ transy,
+ wattr.width - data.crect.width() - transx,
+ wattr.height - data.crect.height() - transy);
+
+ // add the border_width for the window managers frame... some window managers
+ // do not use a border_width of zero for their frames, and if we the left and
+ // top strut, we ensure that pos() is absolutely correct. frameGeometry()
+ // will still be incorrect though... perhaps i should have foffset as well, to
+ // indicate the frame offset (equal to the border_width on X).
+ // - Brad
+ top->frameStrut.adjust(wattr.border_width,
+ wattr.border_width,
+ wattr.border_width,
+ wattr.border_width);
+ }
+
+ data.fstrut_dirty = false;
+}
+
+void QWidgetPrivate::setWindowOpacity_sys(qreal opacity)
+{
+ Q_Q(QWidget);
+ ulong value = ulong(opacity * 0xffffffff);
+ XChangeProperty(QX11Info::display(), q->internalWinId(), ATOM(_NET_WM_WINDOW_OPACITY), XA_CARDINAL,
+ 32, PropModeReplace, (uchar*)&value, 1);
+}
+
+const QX11Info &QWidget::x11Info() const
+{
+ Q_D(const QWidget);
+ return d->xinfo;
+}
+
+void QWidgetPrivate::setWindowRole()
+{
+ Q_Q(QWidget);
+ if (!q->internalWinId())
+ return;
+ QByteArray windowRole = topData()->role.toUtf8();
+ XChangeProperty(X11->display, q->internalWinId(),
+ ATOM(WM_WINDOW_ROLE), XA_STRING, 8, PropModeReplace,
+ (unsigned char *)windowRole.constData(), windowRole.length());
+}
+
+Q_GLOBAL_STATIC(QX11PaintEngine, qt_widget_paintengine)
+QPaintEngine *QWidget::paintEngine() const
+{
+ Q_D(const QWidget);
+ if (qt_widget_paintengine()->isActive()) {
+ if (d->extraPaintEngine)
+ return d->extraPaintEngine;
+ QWidget *self = const_cast<QWidget *>(this);
+ self->d_func()->extraPaintEngine = new QX11PaintEngine();
+ return d->extraPaintEngine;
+ }
+ return qt_widget_paintengine();
+}
+
+QWindowSurface *QWidgetPrivate::createDefaultWindowSurface_sys()
+{
+ return new QX11WindowSurface(q_func());
+}
+
+Qt::HANDLE QWidget::x11PictureHandle() const
+{
+#ifndef QT_NO_XRENDER
+ Q_D(const QWidget);
+ if (!internalWinId() && testAttribute(Qt::WA_WState_Created))
+ (void)winId(); // enforce native window
+ return d->picture;
+#else
+ return 0;
+#endif // QT_NO_XRENDER
+}
+
+#ifndef QT_NO_XRENDER
+XRenderColor QX11Data::preMultiply(const QColor &c)
+{
+ XRenderColor color;
+ const uint A = c.alpha(),
+ R = c.red(),
+ G = c.green(),
+ B = c.blue();
+ color.alpha = (A | A << 8);
+ color.red = (R | R << 8) * color.alpha / 0x10000;
+ color.green = (G | G << 8) * color.alpha / 0x10000;
+ color.blue = (B | B << 8) * color.alpha / 0x10000;
+ return color;
+}
+Picture QX11Data::getSolidFill(int screen, const QColor &c)
+{
+ if (!X11->use_xrender)
+ return XNone;
+
+ XRenderColor color = preMultiply(c);
+ for (int i = 0; i < X11->solid_fill_count; ++i) {
+ if (X11->solid_fills[i].screen == screen
+ && X11->solid_fills[i].color.alpha == color.alpha
+ && X11->solid_fills[i].color.red == color.red
+ && X11->solid_fills[i].color.green == color.green
+ && X11->solid_fills[i].color.blue == color.blue)
+ return X11->solid_fills[i].picture;
+ }
+ // none found, replace one
+ int i = qrand() % 16;
+
+ if (X11->solid_fills[i].screen != screen && X11->solid_fills[i].picture) {
+ XRenderFreePicture (X11->display, X11->solid_fills[i].picture);
+ X11->solid_fills[i].picture = 0;
+ }
+
+ if (!X11->solid_fills[i].picture) {
+ Pixmap pixmap = XCreatePixmap (X11->display, RootWindow (X11->display, screen), 1, 1, 32);
+ XRenderPictureAttributes attrs;
+ attrs.repeat = True;
+ X11->solid_fills[i].picture = XRenderCreatePicture (X11->display, pixmap,
+ XRenderFindStandardFormat(X11->display, PictStandardARGB32),
+ CPRepeat, &attrs);
+ XFreePixmap (X11->display, pixmap);
+ }
+
+ X11->solid_fills[i].color = color;
+ X11->solid_fills[i].screen = screen;
+ XRenderFillRectangle (X11->display, PictOpSrc, X11->solid_fills[i].picture, &color, 0, 0, 1, 1);
+ return X11->solid_fills[i].picture;
+}
+#endif
+
+void QWidgetPrivate::setModal_sys()
+{
+}
+
+void qt_x11_getX11InfoForWindow(QX11Info * xinfo, const QX11WindowAttributes &att)
+{
+ QX11InfoData* xd = xinfo->getX11Data(true);
+ const XWindowAttributes &a = *(att.att);
+ // find which screen the window is on...
+ xd->screen = QX11Info::appScreen(); // by default, use the default :)
+ int i;
+ for (i = 0; i < ScreenCount(X11->display); i++) {
+ if (RootWindow(X11->display, i) == a.root) {
+ xd->screen = i;
+ break;
+ }
+ }
+
+ xd->depth = a.depth;
+ xd->cells = DisplayCells(X11->display, xd->screen);
+ xd->visual = a.visual;
+ xd->defaultVisual = (XVisualIDFromVisual((Visual *) a.visual) ==
+ XVisualIDFromVisual((Visual *) QX11Info::appVisual(xinfo->screen())));
+ xd->colormap = a.colormap;
+ xd->defaultColormap = (a.colormap == QX11Info::appColormap(xinfo->screen()));
+ xinfo->setX11Data(xd);
+}
+
+void QWidgetPrivate::updateX11AcceptFocus()
+{
+ Q_Q(QWidget);
+ if (!q->isWindow() || !q->internalWinId())
+ return;
+
+ XWMHints *h = XGetWMHints(X11->display, q->internalWinId());
+ XWMHints wm_hints;
+ if (!h) {
+ memset(&wm_hints, 0, sizeof(wm_hints)); // make valgrind happy
+ h = &wm_hints;
+ }
+ h->flags |= InputHint;
+ h->input = q->testAttribute(Qt::WA_X11DoNotAcceptFocus) ? False : True;
+
+ XSetWMHints(X11->display, q->internalWinId(), h);
+ if (h != &wm_hints)
+ XFree((char *)h);
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/x11/qwidgetcreate_x11.cpp b/src/widgets/platforms/x11/qwidgetcreate_x11.cpp
new file mode 100644
index 0000000000..0a0656dd2b
--- /dev/null
+++ b/src/widgets/platforms/x11/qwidgetcreate_x11.cpp
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwidget.h"
+#include "qt_x11_p.h"
+
+/*
+ Internal Qt functions to create X windows. We have put them in
+ separate functions to allow the programmer to reimplement them by
+ custom versions.
+*/
+
+QT_BEGIN_NAMESPACE
+
+Window qt_XCreateWindow(const QWidget *, Display *display, Window parent,
+ int x, int y, uint w, uint h,
+ int borderwidth, int depth,
+ uint windowclass, Visual *visual,
+ ulong valuemask, XSetWindowAttributes *attributes)
+{
+ return XCreateWindow(display, parent, x, y, w, h, borderwidth, depth,
+ windowclass, visual, valuemask, attributes);
+}
+
+
+Window qt_XCreateSimpleWindow(const QWidget *, Display *display, Window parent,
+ int x, int y, uint w, uint h, int borderwidth,
+ ulong border, ulong background)
+{
+ return XCreateSimpleWindow(display, parent, x, y, w, h, borderwidth,
+ border, background);
+}
+
+
+void qt_XDestroyWindow(const QWidget *, Display *display, Window window)
+{
+ if (window)
+ XDestroyWindow(display, window);
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/x11/qx11embed_x11.cpp b/src/widgets/platforms/x11/qx11embed_x11.cpp
new file mode 100644
index 0000000000..1fa658d0df
--- /dev/null
+++ b/src/widgets/platforms/x11/qx11embed_x11.cpp
@@ -0,0 +1,1808 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+#include "qx11embed_x11.h"
+#include <qapplication.h>
+#include <qevent.h>
+#include <qpainter.h>
+#include <qlayout.h>
+#include <qstyle.h>
+#include <qstyleoption.h>
+#include <qelapsedtimer.h>
+#include <qpointer.h>
+#include <qdebug.h>
+#include <qx11info_x11.h>
+#include <private/qt_x11_p.h>
+#include <private/qwidget_p.h>
+
+#define XK_MISCELLANY
+#define XK_LATIN1
+#define None 0
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+#include <X11/keysymdef.h>
+#include <X11/X.h>
+
+#ifndef XK_ISO_Left_Tab
+#define XK_ISO_Left_Tab 0xFE20
+#endif
+
+//#define QX11EMBED_DEBUG
+#ifdef QX11EMBED_DEBUG
+#include <qdebug.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QX11EmbedWidget
+ \ingroup advanced
+
+ \brief The QX11EmbedWidget class provides an XEmbed client widget.
+
+ XEmbed is an X11 protocol that supports the embedding of a widget
+ from one application into another application.
+
+ An XEmbed \e{client widget} is a window that is embedded into a
+ \e container. A container is the graphical location that embeds
+ (or \e swallows) an external application.
+
+ QX11EmbedWidget is a widget used for writing XEmbed applets or
+ plugins. When it has been embedded and the container receives tab
+ focus, focus is passed on to the widget. When the widget reaches
+ the end of its focus chain, focus is passed back to the
+ container. Window activation, accelerator support, modality and
+ drag and drop (XDND) are also handled.
+
+ The widget and container can both initiate the embedding. If the
+ widget is the initiator, the X11 window ID of the container that
+ it wants to embed itself into must be passed to embedInto().
+
+ If the container initiates the embedding, the window ID of the
+ embedded widget must be known. The container calls embed(),
+ passing the window ID.
+
+ This example shows an application that embeds a QX11EmbedWidget
+ subclass into the window whose ID is passed as a command-line
+ argument:
+
+ \snippet doc/src/snippets/qx11embedwidget/main.cpp 0
+
+ The problem of obtaining the window IDs is often solved by the
+ container invoking the application that provides the widget as a
+ separate process (as a panel invokes a docked applet), passing
+ its window ID to the new process as a command-line argument. The
+ new process can then call embedInto() with the container's window
+ ID, as shown in the example code above. Similarly, the new
+ process can report its window ID to the container through IPC, in
+ which case the container can embed the widget.
+
+ When the widget has been embedded, it emits the signal
+ embedded(). If it is closed by the container, the widget emits
+ containerClosed(). If an error occurs when embedding, error() is
+ emitted.
+
+ There are XEmbed widgets available for KDE and GTK+. The GTK+
+ equivalent of QX11EmbedWidget is GtkPlug. The corresponding KDE 3
+ widget is called QXEmbed.
+
+ \sa QX11EmbedContainer, {XEmbed Specification}
+*/
+
+/*!
+ \class QX11EmbedContainer
+ \ingroup advanced
+
+ \brief The QX11EmbedContainer class provides an XEmbed container
+ widget.
+
+ XEmbed is an X11 protocol that supports the embedding of a widget
+ from one application into another application.
+
+ An XEmbed \e container is the graphical location that embeds an
+ external \e {client widget}. A client widget is a window that is
+ embedded into a container.
+
+ When a widget has been embedded and the container receives tab
+ focus, focus is passed on to the widget. When the widget reaches
+ the end of its focus chain, focus is passed back to the
+ container. Window activation, accelerator support, modality and
+ drag and drop (XDND) are also handled.
+
+ QX11EmbedContainer is commonly used for writing panels or
+ toolbars that hold applets, or for \e swallowing X11
+ applications. When writing a panel application, one container
+ widget is created on the toolbar, and it can then either swallow
+ another widget using embed(), or allow an XEmbed widget to be
+ embedded into itself. The container's X11 window ID, which is
+ retrieved with winId(), must then be known to the client widget.
+ After embedding, the client's window ID can be retrieved with
+ clientWinId().
+
+ In the following example, a container widget is created as the
+ main widget. It then invokes an application called "playmovie",
+ passing its window ID as a command line argument. The "playmovie"
+ program is an XEmbed client widget. The widget embeds itself into
+ the container using the container's window ID.
+
+ \snippet doc/src/snippets/qx11embedcontainer/main.cpp 0
+
+ When the client widget is embedded, the container emits the
+ signal clientIsEmbedded(). The signal clientClosed() is emitted
+ when a widget is closed.
+
+ It is possible for QX11EmbedContainer to embed XEmbed widgets
+ from toolkits other than Qt, such as GTK+. Arbitrary (non-XEmbed)
+ X11 widgets can also be embedded, but the XEmbed-specific
+ features such as window activation and focus handling are then
+ lost.
+
+ The GTK+ equivalent of QX11EmbedContainer is GtkSocket. The
+ corresponding KDE 3 widget is called QXEmbed.
+
+ \sa QX11EmbedWidget, {XEmbed Specification}
+*/
+
+/*! \fn void QX11EmbedWidget::embedded()
+
+ This signal is emitted by the widget that has been embedded by an
+ XEmbed container.
+*/
+
+/*! \fn void QX11EmbedWidget::containerClosed()
+
+ This signal is emitted by the client widget when the container
+ closes the widget. This can happen if the container itself
+ closes, or if the widget is rejected.
+
+ The container can reject a widget for any reason, but the most
+ common cause of a rejection is when an attempt is made to embed a
+ widget into a container that already has an embedded widget.
+*/
+
+/*! \fn void QX11EmbedContainer::clientIsEmbedded()
+
+ This signal is emitted by the container when a client widget has
+ been embedded.
+*/
+
+/*! \fn void QX11EmbedContainer::clientClosed()
+
+ This signal is emitted by the container when the client widget
+ closes.
+*/
+
+/*!
+ \fn void QX11EmbedWidget::error(QX11EmbedWidget::Error error)
+
+ This signal is emitted if an error occurred as a result of
+ embedding into or communicating with a container. The specified
+ \a error describes the problem that occurred.
+
+ \sa QX11EmbedWidget::Error
+*/
+
+/*!
+ \fn QX11EmbedContainer::Error QX11EmbedContainer::error() const
+
+ Returns the last error that occurred.
+*/
+
+/*! \fn void QX11EmbedContainer::error(QX11EmbedContainer::Error error)
+
+ This signal is emitted if an error occurred when embedding or
+ communicating with a client. The specified \a error describes the
+ problem that occurred.
+
+ \sa QX11EmbedContainer::Error
+*/
+
+/*!
+ \enum QX11EmbedWidget::Error
+
+ \value Unknown An unrecognized error occurred.
+
+ \value InvalidWindowID The X11 window ID of the container was
+ invalid. This error is usually triggered by passing an invalid
+ window ID to embedInto().
+
+ \omitvalue Internal
+*/
+
+/*!
+ \enum QX11EmbedContainer::Error
+
+ \value Unknown An unrecognized error occurred.
+
+ \value InvalidWindowID The X11 window ID of the container was
+ invalid. This error is usually triggered by passing an invalid
+ window ID to embed().
+
+ \omitvalue Internal
+*/
+
+const int XButtonPress = ButtonPress;
+const int XButtonRelease = ButtonRelease;
+#undef ButtonPress
+#undef ButtonRelease
+
+// This is a hack to move topData() out from QWidgetPrivate to public. We
+// need to to inspect window()'s embedded state.
+class QHackWidget : public QWidget
+{
+ Q_DECLARE_PRIVATE(QWidget)
+public:
+ QTLWExtra* topData() { return d_func()->topData(); }
+};
+
+static unsigned int XEMBED_VERSION = 0;
+
+enum QX11EmbedMessageType {
+ XEMBED_EMBEDDED_NOTIFY = 0,
+ XEMBED_WINDOW_ACTIVATE = 1,
+ XEMBED_WINDOW_DEACTIVATE = 2,
+ XEMBED_REQUEST_FOCUS = 3,
+ XEMBED_FOCUS_IN = 4,
+ XEMBED_FOCUS_OUT = 5,
+ XEMBED_FOCUS_NEXT = 6,
+ XEMBED_FOCUS_PREV = 7,
+ XEMBED_MODALITY_ON = 10,
+ XEMBED_MODALITY_OFF = 11,
+ XEMBED_REGISTER_ACCELERATOR = 12,
+ XEMBED_UNREGISTER_ACCELERATOR = 13,
+ XEMBED_ACTIVATE_ACCELERATOR = 14
+};
+
+enum QX11EmbedFocusInDetail {
+ XEMBED_FOCUS_CURRENT = 0,
+ XEMBED_FOCUS_FIRST = 1,
+ XEMBED_FOCUS_LAST = 2
+};
+
+enum QX11EmbedFocusInFlags {
+ XEMBED_FOCUS_OTHER = (0 << 0),
+ XEMBED_FOCUS_WRAPAROUND = (1 << 0)
+};
+
+enum QX11EmbedInfoFlags {
+ XEMBED_MAPPED = (1 << 0)
+};
+
+enum QX11EmbedAccelModifiers {
+ XEMBED_MODIFIER_SHIFT = (1 << 0),
+ XEMBED_MODIFIER_CONTROL = (1 << 1),
+ XEMBED_MODIFIER_ALT = (1 << 2),
+ XEMBED_MODIFIER_SUPER = (1 << 3),
+ XEMBED_MODIFIER_HYPER = (1 << 4)
+};
+
+enum QX11EmbedAccelFlags {
+ XEMBED_ACCELERATOR_OVERLOADED = (1 << 0)
+};
+
+// Silence the default X11 error handler.
+static int x11ErrorHandler(Display *, XErrorEvent *)
+{
+ return 0;
+}
+
+// Returns the X11 timestamp. Maintained mainly by qapplication
+// internals, but also updated by the XEmbed widgets.
+static Time x11Time()
+{
+ return qt_x11Data->time;
+}
+
+// Gives the version and flags of the supported XEmbed protocol.
+static unsigned int XEmbedVersion()
+{
+ return 0;
+}
+
+// Sends an XEmbed message.
+static void sendXEmbedMessage(WId window, Display *display, long message,
+ long detail = 0, long data1 = 0, long data2 = 0)
+{
+ XClientMessageEvent c;
+ memset(&c, 0, sizeof(c));
+ c.type = ClientMessage;
+ c.message_type = ATOM(_XEMBED);
+ c.format = 32;
+ c.display = display;
+ c.window = window;
+
+ c.data.l[0] = x11Time();
+ c.data.l[1] = message;
+ c.data.l[2] = detail;
+ c.data.l[3] = data1;
+ c.data.l[4] = data2;
+
+ XSendEvent(display, window, false, NoEventMask, (XEvent *) &c);
+}
+
+// From qapplication_x11.cpp
+static XKeyEvent lastKeyEvent;
+
+static QCoreApplication::EventFilter oldX11EventFilter;
+
+// The purpose of this global x11 filter is for one to capture the key
+// events in their original state, but most importantly this is the
+// only way to get the WM_TAKE_FOCUS message from WM_PROTOCOLS.
+static bool x11EventFilter(void *message, long *result)
+{
+ XEvent *event = reinterpret_cast<XEvent *>(message);
+ if (event->type == XKeyPress || event->type == XKeyRelease)
+ lastKeyEvent = event->xkey;
+
+ if (oldX11EventFilter && oldX11EventFilter != &x11EventFilter)
+ return oldX11EventFilter(message, result);
+ else
+ return false;
+}
+
+//
+struct functorData
+{
+ Window id, rootWindow;
+ bool clearedWmState;
+ bool reparentedToRoot;
+};
+
+static Bool functor(Display *display, XEvent *event, XPointer arg)
+{
+ functorData *data = (functorData *) arg;
+
+ if (!data->reparentedToRoot && event->type == ReparentNotify
+ && event->xreparent.window == data->id
+ && event->xreparent.parent == data->rootWindow) {
+ data->reparentedToRoot = true;
+ return true;
+ }
+
+ if (!data->clearedWmState
+ && event->type == PropertyNotify
+ && event->xproperty.window == data->id
+ && event->xproperty.atom == ATOM(WM_STATE)) {
+ if (event->xproperty.state == PropertyDelete) {
+ data->clearedWmState = true;
+ return true;
+ }
+
+ Atom ret;
+ int format, status;
+ unsigned char *retval;
+ unsigned long nitems, after;
+ status = XGetWindowProperty(display, data->id, ATOM(WM_STATE), 0, 2, False, ATOM(WM_STATE),
+ &ret, &format, &nitems, &after, &retval );
+ if (status == Success && ret == ATOM(WM_STATE) && format == 32 && nitems > 0) {
+ long state = *(long *)retval;
+ XFree(retval);
+ if (state == WithdrawnState) {
+ data->clearedWmState = true;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+class QX11EmbedWidgetPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QX11EmbedWidget)
+public:
+ inline QX11EmbedWidgetPrivate()
+ {
+ lastError = QX11EmbedWidget::Unknown;
+ container = 0;
+ }
+
+ void setEmbedded();
+
+ void emitError(QX11EmbedWidget::Error error) {
+ Q_Q(QX11EmbedWidget);
+
+ lastError = error;
+ emit q->error(error);
+ }
+
+ enum FocusWidgets {
+ FirstFocusWidget,
+ LastFocusWidget
+ };
+
+ int focusOriginator;
+ QWidget *getFocusWidget(FocusWidgets fw);
+ void checkActivateWindow(QObject *o);
+ QX11EmbedWidget *xEmbedWidget(QObject *o) const;
+ void clearFocus();
+
+ WId container;
+ QPointer<QWidget> currentFocus;
+
+ QX11EmbedWidget::Error lastError;
+
+};
+
+/*!
+ Constructs a QX11EmbedWidget object with the given \a parent.
+*/
+QX11EmbedWidget::QX11EmbedWidget(QWidget *parent)
+ : QWidget(*new QX11EmbedWidgetPrivate, parent, 0)
+{
+ XSetErrorHandler(x11ErrorHandler);
+
+ setAttribute(Qt::WA_NativeWindow);
+ setAttribute(Qt::WA_DontCreateNativeAncestors);
+ createWinId();
+ XSelectInput(x11Info().display(), internalWinId(),
+ KeyPressMask | KeyReleaseMask | ButtonPressMask
+ | ButtonReleaseMask
+ | KeymapStateMask | ButtonMotionMask | PointerMotionMask
+ | FocusChangeMask
+ | ExposureMask | StructureNotifyMask
+ | SubstructureNotifyMask | PropertyChangeMask);
+
+ long data[] = {XEMBED_VERSION, XEMBED_MAPPED};
+ XChangeProperty(x11Info().display(), internalWinId(), ATOM(_XEMBED_INFO),
+ ATOM(_XEMBED_INFO), 32, PropModeReplace,
+ (unsigned char*) data, 2);
+
+ setFocusPolicy(Qt::StrongFocus);
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ QApplication::instance()->installEventFilter(this);
+
+#ifdef QX11EMBED_DEBUG
+ qDebug() << "QX11EmbedWidget::QX11EmbedWidget: constructed client"
+ << (void *)this << "with winId" << winId();
+#endif
+}
+
+/*!
+ Destructs the QX11EmbedWidget object. If the widget is embedded
+ when deleted, it is hidden and then detached from its container,
+ so that the container is free to embed a new widget.
+*/
+QX11EmbedWidget::~QX11EmbedWidget()
+{
+ Q_D(QX11EmbedWidget);
+ if (d->container) {
+#ifdef QX11EMBED_DEBUG
+ qDebug() << "QX11EmbedWidget::~QX11EmbedWidget: unmapping"
+ << (void *)this << "with winId" << winId()
+ << "from container with winId" << d->container;
+#endif
+ XUnmapWindow(x11Info().display(), internalWinId());
+ XReparentWindow(x11Info().display(), internalWinId(), x11Info().appRootWindow(x11Info().screen()), 0, 0);
+ }
+
+#ifdef QX11EMBED_DEBUG
+ qDebug() << "QX11EmbedWidget::~QX11EmbedWidget: destructed client"
+ << (void *)this << "with winId" << winId();
+#endif
+}
+
+/*!
+ Returns the type of error that occurred last. This is the same error code
+ that is emitted by the error() signal.
+
+ \sa Error
+*/
+QX11EmbedWidget::Error QX11EmbedWidget::error() const
+{
+ return d_func()->lastError;
+}
+
+/*!
+ When this function is called, the widget embeds itself into the
+ container whose window ID is \a id.
+
+ If \a id is \e not the window ID of a container this function will
+ behave unpredictably.
+*/
+void QX11EmbedWidget::embedInto(WId id)
+{
+ Q_D(QX11EmbedWidget);
+#ifdef QX11EMBED_DEBUG
+ qDebug() << "QX11EmbedWidget::embedInto: embedding client"
+ << (void *)this << "with winId" << winId() << "into container"
+ << id;
+#endif
+
+ d->container = id;
+ switch (XReparentWindow(x11Info().display(), internalWinId(), d->container, 0, 0)) {
+ case BadWindow:
+ d->emitError(InvalidWindowID);
+ break;
+ case BadMatch:
+ d->emitError(Internal);
+ break;
+ case Success:
+ default:
+ break;
+ }
+ QTLWExtra* x = d->extra ? d->extra->topextra : 0;
+ if (x)
+ x->frameStrut.setCoords(0, 0, 0, 0);
+ d->data.fstrut_dirty = false;
+}
+
+/*! \internal
+
+ Gets the first or last child widget that can get focus.
+*/
+QWidget *QX11EmbedWidgetPrivate::getFocusWidget(FocusWidgets fw)
+{
+ Q_Q(QX11EmbedWidget);
+ QWidget *tlw = q;
+ QWidget *w = tlw->nextInFocusChain();
+
+ QWidget *last = tlw;
+
+ extern bool qt_tab_all_widgets;
+ uint focus_flag = qt_tab_all_widgets ? Qt::TabFocus : Qt::StrongFocus;
+
+ while (w != tlw)
+ {
+ if (((w->focusPolicy() & focus_flag) == focus_flag)
+ && w->isVisibleTo(q) && w->isEnabled())
+ {
+ last = w;
+ if (fw == FirstFocusWidget)
+ break;
+ }
+ w = w->nextInFocusChain();
+ }
+
+ return last;
+}
+
+/*! \internal
+
+ Returns the xembed widget that the widget is a child of
+*/
+QX11EmbedWidget *QX11EmbedWidgetPrivate::xEmbedWidget(QObject *o) const
+{
+ QX11EmbedWidget *xec = 0;
+
+ // Check the widget itself, then its parents, and find the first
+ // QX11EmbedWidget.
+ do {
+ if ((xec = qobject_cast<QX11EmbedWidget *>(o)))
+ return xec;
+ } while ((o = o->parent()));
+ return 0;
+}
+
+/*! \internal
+
+ Checks the active window.
+*/
+void QX11EmbedWidgetPrivate::checkActivateWindow(QObject *o)
+{
+ Q_Q(QX11EmbedWidget);
+ QX11EmbedWidget *xec = xEmbedWidget(o);
+
+ // check if we are in the right xembed client
+ if (q != xec)
+ return;
+
+ QWidget *w = qobject_cast<QWidget *>(o);
+
+ // if it is no active window, then don't do the change
+ if (!(w && qApp->activeWindow()))
+ return;
+
+ // if it already is the active window, don't do anything
+ if (w->window() != qApp->activeWindow())
+ {
+ qApp->setActiveWindow(w->window());
+ currentFocus = w;
+
+ sendXEmbedMessage(xec->containerWinId(), q->x11Info().display(), XEMBED_REQUEST_FOCUS);
+ }
+}
+
+/*! \internal
+
+ Clears the focus
+*/
+void QX11EmbedWidgetPrivate::clearFocus()
+{
+ Q_Q(QX11EmbedWidget);
+ // Setting focus on the client itself removes Qt's
+ // logical focus rectangle. We can't just do a
+ // clearFocus here, because when we send the synthetic
+ // FocusIn to ourselves on activation, Qt will set
+ // focus on focusWidget() again. This way, we "hide"
+ // focus rather than clearing it.
+
+ if (!q->window()->hasFocus())
+ q->window()->setFocus(Qt::OtherFocusReason);
+
+ currentFocus = 0;
+}
+
+/*! \internal
+
+ Sets the embedded flag on the client.
+*/
+void QX11EmbedWidgetPrivate::setEmbedded()
+{
+ Q_Q(QX11EmbedWidget);
+ ((QHackWidget *)q->window())->topData()->embedded = 1;
+}
+
+/*! \internal
+
+ Handles WindowActivate and FocusIn events for the client.
+*/
+bool QX11EmbedWidget::eventFilter(QObject *o, QEvent *event)
+{
+ Q_D(QX11EmbedWidget);
+ switch (event->type()) {
+ case QEvent::FocusIn:
+ switch (((QFocusEvent *)event)->reason()) {
+ case Qt::MouseFocusReason:
+ // If the user clicks into one of the client widget's
+ // children and we didn't have focus already, we request
+ // focus from our container.
+ if (d->xEmbedWidget(o) == this) {
+ if (d->currentFocus.isNull())
+ sendXEmbedMessage(d->container, x11Info().display(), XEMBED_REQUEST_FOCUS);
+
+ d->currentFocus = qobject_cast<QWidget *>(o);
+ }
+ break;
+ case Qt::TabFocusReason:
+ // If the xembed client receives a focus event because of
+ // a Tab, then we are at the end of our focus chain and we
+ // ask the container to move to its next focus widget.
+ if (o == this) {
+ d->clearFocus();
+ sendXEmbedMessage(d->container, x11Info().display(), XEMBED_FOCUS_NEXT);
+ return true;
+ } else {
+ // We're listening on events from qApp, so in order
+ // for us to know who to set focus on if we receive an
+ // activation event, we note the widget that got the
+ // focusin last.
+ if (d->xEmbedWidget(o) == this)
+ d->currentFocus = qobject_cast<QWidget *>(o);
+ }
+ break;
+ case Qt::BacktabFocusReason:
+ // If the window receives a focus event because of
+ // a Backtab, then we are at the start of our focus chain
+ // and we ask the container to move to its previous focus
+ // widget.
+ if (o == this) {
+ // See comment for Tab.
+ // If we receive a XEMBED_FOCUS_IN
+ // XEMBED_FOCUS_CURRENT, we will set focus in
+ // currentFocus. To avoid that in this case, we reset
+ // currentFocus.
+ d->clearFocus();
+ sendXEmbedMessage(d->container, x11Info().display(), XEMBED_FOCUS_PREV);
+ return true;
+ } else {
+ if (d->xEmbedWidget(o) == this)
+ d->currentFocus = qobject_cast<QWidget *>(o);
+ }
+ break;
+ case Qt::ActiveWindowFocusReason:
+ if (isActiveWindow()) {
+ if (!d->currentFocus.isNull()) {
+ if (!d->currentFocus->hasFocus())
+ d->currentFocus->setFocus(Qt::OtherFocusReason);
+ } else {
+ d->clearFocus();
+ return true;
+ }
+ }
+
+ break;
+ case Qt::PopupFocusReason:
+ case Qt::ShortcutFocusReason:
+ case Qt::OtherFocusReason:
+ // If focus is received to any child widget because of any
+ // other reason, remember the widget so that we can give
+ // it focus again if we're activated.
+ if (d->xEmbedWidget(o) == this) {
+ d->currentFocus = qobject_cast<QWidget *>(o);
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case QEvent::MouseButtonPress:
+ // If we get a mouse button press event inside a embedded widget
+ // make sure this is the active window in qapp.
+ d->checkActivateWindow(o);
+ break;
+ default:
+ break;
+ }
+
+ return QWidget::eventFilter(o, event);
+}
+
+/*! \internal
+
+ Handles some notification events and client messages. Client side
+ XEmbed message receiving is also handled here.
+*/
+bool QX11EmbedWidget::x11Event(XEvent *event)
+{
+ Q_D(QX11EmbedWidget);
+ switch (event->type) {
+ case DestroyNotify:
+#ifdef QX11EMBED_DEBUG
+ qDebug() << "QX11EmbedWidget::x11Event: client"
+ << (void *)this << "with winId" << winId()
+ << "received a DestroyNotify";
+#endif
+ // If the container window is destroyed, we signal this to the user.
+ d->container = 0;
+ emit containerClosed();
+ break;
+ case ReparentNotify:
+#ifdef QX11EMBED_DEBUG
+ qDebug() << "QX11EmbedWidget::x11Event: client"
+ << (void *)this << "with winId" << winId()
+ << "received a ReparentNotify to"
+ << ((event->xreparent.parent == x11Info().appRootWindow(x11Info().screen()))
+ ? QString::fromLatin1("root") : QString::number(event->xreparent.parent));
+#endif
+ // If the container shuts down, we will be reparented to the
+ // root window. We must also consider the case that we may be
+ // reparented from one container to another.
+ if (event->xreparent.parent == x11Info().appRootWindow(x11Info().screen())) {
+ if (((QHackWidget *)this)->topData()->embedded) {
+ d->container = 0;
+ emit containerClosed();
+ }
+ return true;
+ } else {
+ d->container = event->xreparent.parent;
+ }
+ break;
+ case UnmapNotify:
+ // Mapping and unmapping are handled by changes to the
+ // _XEMBED_INFO property. Any default map/unmap requests are
+ // ignored.
+ return true;
+ case PropertyNotify:
+ // The container sends us map/unmap messages through the
+ // _XEMBED_INFO property. We adhere to the XEMBED_MAPPED bit in
+ // data2.
+ if (event->xproperty.atom == ATOM(_XEMBED_INFO)) {
+ Atom actual_type_return;
+ int actual_format_return;
+ unsigned long nitems_return;
+ unsigned long bytes_after_return;
+ unsigned char *prop_return = 0;
+ if (XGetWindowProperty(x11Info().display(), internalWinId(), ATOM(_XEMBED_INFO), 0, 2,
+ false, ATOM(_XEMBED_INFO), &actual_type_return,
+ &actual_format_return, &nitems_return,
+ &bytes_after_return, &prop_return) == Success) {
+ if (nitems_return > 1) {
+ if (((long * )prop_return)[1] & XEMBED_MAPPED) {
+ XMapWindow(x11Info().display(), internalWinId());
+ } else {
+ XUnmapWindow(x11Info().display(), internalWinId());
+ }
+ }
+ if (prop_return)
+ XFree(prop_return);
+ }
+ }
+
+ break;
+ case ClientMessage:
+ // XEMBED messages have message_type _XEMBED
+ if (event->xclient.message_type == ATOM(_XEMBED)) {
+ // Discard XEMBED messages not to ourselves. (### dead code?)
+ if (event->xclient.window != internalWinId())
+ break;
+
+ // Update qt_x_time if necessary
+ Time msgtime = (Time) event->xclient.data.l[0];
+ if (msgtime > X11->time)
+ X11->time = msgtime;
+
+ switch (event->xclient.data.l[1]) {
+ case XEMBED_WINDOW_ACTIVATE: {
+ // When we receive an XEMBED_WINDOW_ACTIVATE, we simply send
+ // ourselves a WindowActivate event. Real activation happens
+ // when receive XEMBED_FOCUS_IN.
+ if (!isActiveWindow()) {
+ QEvent ev(QEvent::WindowActivate);
+ QApplication::sendEvent(this, &ev);
+ }
+ }
+ break;
+ case XEMBED_WINDOW_DEACTIVATE: {
+ // When we receive an XEMBED_WINDOW_DEACTIVATE, we simply send
+ // ourselves a WindowDeactivate event. Real activation happens
+ // when receive XEMBED_FOCUS_IN.
+ if (isActiveWindow()) {
+ if (!qApp->activePopupWidget())
+ QApplication::setActiveWindow(0);
+ } else {
+ QEvent ev(QEvent::WindowDeactivate);
+ QApplication::sendEvent(this, &ev);
+ }
+ }
+ break;
+ case XEMBED_EMBEDDED_NOTIFY: {
+#ifdef QX11EMBED_DEBUG
+ qDebug() << "QX11EmbedWidget::x11Event: client"
+ << (void *)this << "with winId" << winId()
+ << "received an XEMBED EMBEDDED NOTIFY message";
+#endif
+ // In this message's l[2] we have the max version
+ // supported by both the client and the
+ // container. QX11EmbedWidget does not use this field.
+
+ // We have been embedded, so we set our
+ // client's embedded flag.
+ d->setEmbedded();
+ emit embedded();
+ }
+ break;
+ case XEMBED_FOCUS_IN:
+ // don't set the focus if a modal dialog is open
+ if (qApp->activeModalWidget())
+ break;
+
+ // in case we embed more than one topLevel window inside the same
+ // host window.
+ if (window() != qApp->activeWindow())
+ qApp->setActiveWindow(this);
+
+ switch (event->xclient.data.l[2]) {
+ case XEMBED_FOCUS_CURRENT:
+ // The container sends us this message if it wants
+ // us to focus on the widget that last had focus.
+ // This is the reply when XEMBED_REQUEST_FOCUS is
+ // sent to the container.
+ if (!d->currentFocus.isNull()) {
+ if (!d->currentFocus->hasFocus())
+ d->currentFocus->setFocus(Qt::OtherFocusReason);
+ } else {
+ // No widget currently has focus. We set focus
+ // on the first widget next to the
+ // client widget. Since the setFocus will not work
+ // if the window is disabled, set the currentFocus
+ // directly so that it's set on window activate.
+ d->currentFocus = d->getFocusWidget(QX11EmbedWidgetPrivate::FirstFocusWidget);
+ d->currentFocus->setFocus(Qt::OtherFocusReason);
+ }
+ break;
+ case XEMBED_FOCUS_FIRST:
+ // The container sends this message when it wants
+ // us to focus on the first widget in our focus
+ // chain (typically because of a tab).
+ d->currentFocus = d->getFocusWidget(QX11EmbedWidgetPrivate::FirstFocusWidget);
+ d->currentFocus->setFocus(Qt::TabFocusReason);
+ break;
+ case XEMBED_FOCUS_LAST:
+ // The container sends this message when it wants
+ // us to focus on the last widget in our focus
+ // chain (typically because of a backtab).
+ d->currentFocus = d->getFocusWidget(QX11EmbedWidgetPrivate::LastFocusWidget);
+ d->currentFocus->setFocus(Qt::BacktabFocusReason);
+ break;
+ default:
+ // Ignore any other XEMBED_FOCUS_IN details.
+ break;
+ }
+ break;
+ case XEMBED_FOCUS_OUT:
+ // The container sends us this message when it wants us
+ // to lose focus and forget about the widget that last
+ // had focus. Typically sent by the container when it
+ // loses focus because of mouse or tab activity. We do
+ // then not want to set focus on anything if we're
+ // activated.
+ if (isActiveWindow())
+ d->clearFocus();
+
+ break;
+ default:
+ // Ignore any other XEMBED messages.
+ break;
+ };
+ } else {
+ // Non-XEMBED client messages are not interesting.
+ }
+
+ break;
+ default:
+ // Ignore all other x11 events.
+ break;
+ }
+
+ // Allow default handling.
+ return QWidget::x11Event(event);
+}
+
+/*!
+ \reimp
+*/
+bool QX11EmbedWidget::event(QEvent *event)
+{
+ if (event->type() == QEvent::ParentChange) {
+ XSelectInput(x11Info().display(), internalWinId(),
+ KeyPressMask | KeyReleaseMask | ButtonPressMask
+ | ButtonReleaseMask
+ | KeymapStateMask | ButtonMotionMask | PointerMotionMask
+ | FocusChangeMask
+ | ExposureMask | StructureNotifyMask
+ | SubstructureNotifyMask | PropertyChangeMask);
+ }
+ return QWidget::event(event);
+}
+
+/*!
+ \reimp
+*/
+void QX11EmbedWidget::resizeEvent(QResizeEvent *event)
+{
+ if (layout())
+ layout()->update();
+ QWidget::resizeEvent(event);
+}
+
+/*!
+ If the widget is embedded, returns the window ID of the
+ container; otherwize returns 0.
+*/
+WId QX11EmbedWidget::containerWinId() const
+{
+ Q_D(const QX11EmbedWidget);
+ return d->container;
+}
+
+class QX11EmbedContainerPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QX11EmbedContainer)
+public:
+ inline QX11EmbedContainerPrivate()
+ {
+ lastError = QX11EmbedContainer::Unknown;
+ client = 0;
+ focusProxy = 0;
+ clientIsXEmbed = false;
+ xgrab = false;
+ }
+
+ bool isEmbedded() const;
+ void moveInputToProxy();
+
+ void acceptClient(WId window);
+ void rejectClient(WId window);
+
+ void checkGrab();
+
+ WId topLevelParentWinId() const;
+
+ void emitError(QX11EmbedContainer::Error error) {
+ Q_Q(QX11EmbedContainer);
+ lastError = error;
+ emit q->error(error);
+ }
+
+ WId client;
+ QWidget *focusProxy;
+ bool clientIsXEmbed;
+ bool xgrab;
+ QRect clientOriginalRect;
+ QSize wmMinimumSizeHint;
+
+ QX11EmbedContainer::Error lastError;
+
+ static QX11EmbedContainer *activeContainer;
+};
+
+QX11EmbedContainer *QX11EmbedContainerPrivate::activeContainer = 0;
+
+/*!
+ Creates a QX11EmbedContainer object with the given \a parent.
+*/
+QX11EmbedContainer::QX11EmbedContainer(QWidget *parent)
+ : QWidget(*new QX11EmbedContainerPrivate, parent, 0)
+{
+ Q_D(QX11EmbedContainer);
+ XSetErrorHandler(x11ErrorHandler);
+
+ setAttribute(Qt::WA_NativeWindow);
+ setAttribute(Qt::WA_DontCreateNativeAncestors);
+ createWinId();
+
+ setFocusPolicy(Qt::StrongFocus);
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ // ### PORT setKeyCompression(false);
+ setAcceptDrops(true);
+ setEnabled(false);
+
+ // Everybody gets a focus proxy, but only one toplevel container's
+ // focus proxy is actually in use.
+ d->focusProxy = new QWidget(this);
+ d->focusProxy->setAttribute(Qt::WA_NativeWindow);
+ d->focusProxy->setAttribute(Qt::WA_DontCreateNativeAncestors);
+ d->focusProxy->createWinId();
+ d->focusProxy->setGeometry(-1, -1, 1, 1);
+
+ // We need events from the window (activation status) and
+ // from qApp (keypress/release).
+ qApp->installEventFilter(this);
+
+ // Install X11 event filter.
+ if (!oldX11EventFilter)
+ oldX11EventFilter = QCoreApplication::instance()->setEventFilter(x11EventFilter);
+
+ XSelectInput(x11Info().display(), internalWinId(),
+ KeyPressMask | KeyReleaseMask
+ | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask
+ | KeymapStateMask
+ | PointerMotionMask
+ | EnterWindowMask | LeaveWindowMask
+ | FocusChangeMask
+ | ExposureMask
+ | StructureNotifyMask
+ | SubstructureNotifyMask);
+
+ // Make sure our new event mask takes effect as soon as possible.
+ XFlush(x11Info().display());
+
+ // Move input to our focusProxy if this widget is active, and not
+ // shaded by a modal dialog (in which case isActiveWindow() would
+ // still return true, but where we must not move input focus).
+ if (qApp->activeWindow() == window() && !d->isEmbedded())
+ d->moveInputToProxy();
+
+#ifdef QX11EMBED_DEBUG
+ qDebug() << "QX11EmbedContainer::QX11EmbedContainer: constructed container"
+ << (void *)this << "with winId" << winId();
+#endif
+}
+
+/*!
+ Destructs a QX11EmbedContainer.
+*/
+QX11EmbedContainer::~QX11EmbedContainer()
+{
+ Q_D(QX11EmbedContainer);
+ if (d->client) {
+ XUnmapWindow(x11Info().display(), d->client);
+ XReparentWindow(x11Info().display(), d->client, x11Info().appRootWindow(x11Info().screen()), 0, 0);
+ }
+
+ if (d->xgrab)
+ XUngrabButton(x11Info().display(), AnyButton, AnyModifier, internalWinId());
+}
+
+
+QX11EmbedContainer::Error QX11EmbedContainer::error() const {
+ return d_func()->lastError;
+}
+
+
+/*! \reimp
+*/
+void QX11EmbedContainer::paintEvent(QPaintEvent *)
+{
+}
+
+/*! \internal
+
+ Returns whether or not the windows' embedded flag is set.
+*/
+bool QX11EmbedContainerPrivate::isEmbedded() const
+{
+ Q_Q(const QX11EmbedContainer);
+ return ((QHackWidget *)q->window())->topData()->embedded == 1;
+}
+
+/*! \internal
+
+ Returns the parentWinId of the window.
+*/
+WId QX11EmbedContainerPrivate::topLevelParentWinId() const
+{
+ Q_Q(const QX11EmbedContainer);
+ return ((QHackWidget *)q->window())->topData()->parentWinId;
+}
+
+/*!
+ If the container has an embedded widget, this function returns
+ the X11 window ID of the client; otherwise it returns 0.
+*/
+WId QX11EmbedContainer::clientWinId() const
+{
+ Q_D(const QX11EmbedContainer);
+ return d->client;
+}
+
+/*!
+ Instructs the container to embed the X11 window with window ID \a
+ id. The client widget will then move on top of the container
+ window and be resized to fit into the container.
+
+ The \a id should be the ID of a window controlled by an XEmbed
+ enabled application, but this is not mandatory. If \a id does not
+ belong to an XEmbed client widget, then focus handling,
+ activation, accelerators and other features will not work
+ properly.
+*/
+void QX11EmbedContainer::embedClient(WId id)
+{
+ Q_D(QX11EmbedContainer);
+
+ if (id == 0) {
+ d->emitError(InvalidWindowID);
+ return;
+ }
+
+ // Walk up the tree of parent windows to prevent embedding of ancestors.
+ WId thisId = internalWinId();
+ Window rootReturn;
+ Window parentReturn;
+ Window *childrenReturn = 0;
+ unsigned int nchildrenReturn;
+ do {
+ if (XQueryTree(x11Info().display(), thisId, &rootReturn,
+ &parentReturn, &childrenReturn, &nchildrenReturn) == 0) {
+ d->emitError(InvalidWindowID);
+ return;
+ }
+ if (childrenReturn) {
+ XFree(childrenReturn);
+ childrenReturn = 0;
+ }
+
+ thisId = parentReturn;
+ if (id == thisId) {
+ d->emitError(InvalidWindowID);
+ return;
+ }
+ } while (thisId != rootReturn);
+
+ // watch for property notify events (see below)
+ XGrabServer(x11Info().display());
+ XWindowAttributes attrib;
+ if (!XGetWindowAttributes(x11Info().display(), id, &attrib)) {
+ XUngrabServer(x11Info().display());
+ d->emitError(InvalidWindowID);
+ return;
+ }
+ XSelectInput(x11Info().display(), id, attrib.your_event_mask | PropertyChangeMask | StructureNotifyMask);
+ XUngrabServer(x11Info().display());
+
+ // Put the window into WithdrawnState
+ XUnmapWindow(x11Info().display(), id);
+ XSync(x11Info().display(), False); // make sure the window is hidden
+
+ /*
+ Wait for notification from the window manager that the window is
+ in withdrawn state. According to the ICCCM section 4.1.3.1,
+ we should wait for the WM_STATE property to either be deleted or
+ set to WithdrawnState.
+
+ For safety, we will not wait more than 500 ms, so that we can
+ preemptively workaround buggy window managers.
+ */
+ QElapsedTimer t;
+ t.start();
+
+ functorData data;
+ data.id = id;
+ data.rootWindow = attrib.root;
+ data.clearedWmState = false;
+ data.reparentedToRoot = false;
+
+ do {
+ if (t.elapsed() > 500) // time-out after 500 ms
+ break;
+
+ XEvent event;
+ if (!XCheckIfEvent(x11Info().display(), &event, functor, (XPointer) &data)) {
+ XSync(x11Info().display(), False);
+ usleep(50000);
+ continue;
+ }
+
+ qApp->x11ProcessEvent(&event);
+ } while (!data.clearedWmState || !data.reparentedToRoot);
+
+ // restore the event mask
+ XSelectInput(x11Info().display(), id, attrib.your_event_mask);
+
+ switch (XReparentWindow(x11Info().display(), id, internalWinId(), 0, 0)) {
+ case BadWindow:
+ case BadMatch:
+ d->emitError(InvalidWindowID);
+ break;
+ default:
+ break;
+ }
+}
+
+/*! \internal
+
+ Handles key, activation and focus events for the container.
+*/
+bool QX11EmbedContainer::eventFilter(QObject *o, QEvent *event)
+{
+ Q_D(QX11EmbedContainer);
+ switch (event->type()) {
+ case QEvent::KeyPress:
+ // Forward any keypresses to our client.
+ if (o == this && d->client) {
+ lastKeyEvent.window = d->client;
+ XSendEvent(x11Info().display(), d->client, false, KeyPressMask, (XEvent *) &lastKeyEvent);
+ return true;
+ }
+ break;
+ case QEvent::KeyRelease:
+ // Forward any keyreleases to our client.
+ if (o == this && d->client) {
+ lastKeyEvent.window = d->client;
+ XSendEvent(x11Info().display(), d->client, false, KeyReleaseMask, (XEvent *) &lastKeyEvent);
+ return true;
+ }
+ break;
+
+ case QEvent::WindowActivate:
+ // When our container window is activated, we pass the
+ // activation message on to our client. Note that X input
+ // focus is set to our focus proxy. We want to intercept all
+ // keypresses.
+ if (o == window() && d->client) {
+ if (d->clientIsXEmbed) {
+ sendXEmbedMessage(d->client, x11Info().display(), XEMBED_WINDOW_ACTIVATE);
+ } else {
+ d->checkGrab();
+ if (hasFocus())
+ XSetInputFocus(x11Info().display(), d->client, XRevertToParent, x11Time());
+ }
+ if (!d->isEmbedded())
+ d->moveInputToProxy();
+ }
+ break;
+ case QEvent::WindowDeactivate:
+ // When our container window is deactivated, we pass the
+ // deactivation message to our client.
+ if (o == window() && d->client) {
+ if (d->clientIsXEmbed)
+ sendXEmbedMessage(d->client, x11Info().display(), XEMBED_WINDOW_DEACTIVATE);
+ else
+ d->checkGrab();
+ }
+ break;
+ case QEvent::FocusIn:
+ // When receiving FocusIn events generated by Tab or Backtab,
+ // we pass focus on to our client. Any mouse activity is sent
+ // directly to the client, and it will ask us for focus with
+ // XEMBED_REQUEST_FOCUS.
+ if (o == this && d->client) {
+ if (!d->isEmbedded())
+ d->activeContainer = this;
+
+ if (d->clientIsXEmbed) {
+ if (!d->isEmbedded())
+ d->moveInputToProxy();
+
+ QFocusEvent *fe = (QFocusEvent *)event;
+ switch (fe->reason()) {
+ case Qt::TabFocusReason:
+ sendXEmbedMessage(d->client, x11Info().display(), XEMBED_FOCUS_IN, XEMBED_FOCUS_FIRST);
+ break;
+ case Qt::BacktabFocusReason:
+ sendXEmbedMessage(d->client, x11Info().display(), XEMBED_FOCUS_IN, XEMBED_FOCUS_LAST);
+ break;
+ default:
+ sendXEmbedMessage(d->client, x11Info().display(), XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT);
+ break;
+ }
+ } else {
+ d->checkGrab();
+ XSetInputFocus(x11Info().display(), d->client, XRevertToParent, x11Time());
+ }
+ }
+
+ break;
+ case QEvent::FocusOut: {
+ // When receiving a FocusOut, we ask our client to remove its
+ // focus.
+ if (o == this && d->client) {
+ if (!d->isEmbedded()) {
+ d->activeContainer = 0;
+ if (isActiveWindow())
+ d->moveInputToProxy();
+ }
+
+ if (d->clientIsXEmbed) {
+ QFocusEvent *fe = (QFocusEvent *)event;
+ if (o == this && d->client && fe->reason() != Qt::ActiveWindowFocusReason)
+ sendXEmbedMessage(d->client, x11Info().display(), XEMBED_FOCUS_OUT);
+ } else {
+ d->checkGrab();
+ }
+ }
+ }
+ break;
+
+ case QEvent::Close: {
+ if (o == this && d->client) {
+ // Unmap the client and reparent it to the root window.
+ // Wait until the messages have been processed. Then ask
+ // the window manager to delete the window.
+ XUnmapWindow(x11Info().display(), d->client);
+ XReparentWindow(x11Info().display(), d->client, x11Info().appRootWindow(x11Info().screen()), 0, 0);
+ XSync(x11Info().display(), false);
+
+ XEvent ev;
+ memset(&ev, 0, sizeof(ev));
+ ev.xclient.type = ClientMessage;
+ ev.xclient.window = d->client;
+ ev.xclient.message_type = ATOM(WM_PROTOCOLS);
+ ev.xclient.format = 32;
+ ev.xclient.data.s[0] = ATOM(WM_DELETE_WINDOW);
+ XSendEvent(x11Info().display(), d->client, false, NoEventMask, &ev);
+
+ XFlush(x11Info().display());
+ d->client = 0;
+ d->clientIsXEmbed = false;
+ d->wmMinimumSizeHint = QSize();
+ updateGeometry();
+ setEnabled(false);
+ update();
+
+ emit clientClosed();
+ }
+ }
+ default:
+ break;
+ }
+
+ return QWidget::eventFilter(o, event);
+}
+
+/*! \internal
+
+ Handles X11 events for the container.
+*/
+bool QX11EmbedContainer::x11Event(XEvent *event)
+{
+ Q_D(QX11EmbedContainer);
+
+ switch (event->type) {
+ case CreateNotify:
+ // The client created an embedded window.
+ if (d->client)
+ d->rejectClient(event->xcreatewindow.window);
+ else
+ d->acceptClient(event->xcreatewindow.window);
+ break;
+ case DestroyNotify:
+ if (event->xdestroywindow.window == d->client) {
+ // The client died.
+ d->client = 0;
+ d->clientIsXEmbed = false;
+ d->wmMinimumSizeHint = QSize();
+ updateGeometry();
+ update();
+ setEnabled(false);
+ emit clientClosed();
+ }
+ break;
+ case ReparentNotify:
+ // The client sends us this if it reparents itself out of our
+ // widget.
+ if (event->xreparent.window == d->client && event->xreparent.parent != internalWinId()) {
+ d->client = 0;
+ d->clientIsXEmbed = false;
+ d->wmMinimumSizeHint = QSize();
+ updateGeometry();
+ update();
+ setEnabled(false);
+ emit clientClosed();
+ } else if (event->xreparent.parent == internalWinId()) {
+ // The client reparented itself into this window.
+ if (d->client)
+ d->rejectClient(event->xreparent.window);
+ else
+ d->acceptClient(event->xreparent.window);
+ }
+ break;
+ case ClientMessage: {
+ if (event->xclient.message_type == ATOM(_XEMBED)) {
+ // Ignore XEMBED messages not to ourselves
+ if (event->xclient.window != internalWinId())
+ break;
+
+ // Receiving an XEmbed message means the client
+ // is an XEmbed client.
+ d->clientIsXEmbed = true;
+
+ Time msgtime = (Time) event->xclient.data.l[0];
+ if (msgtime > X11->time)
+ X11->time = msgtime;
+
+ switch (event->xclient.data.l[1]) {
+ case XEMBED_REQUEST_FOCUS: {
+ // This typically happens when the client gets focus
+ // because of a mouse click.
+ if (!hasFocus())
+ setFocus(Qt::OtherFocusReason);
+
+ // The message is passed along to the topmost container
+ // that eventually responds with a XEMBED_FOCUS_IN
+ // message. The focus in message is passed all the way
+ // back until it reaches the original focus
+ // requestor. In the end, not only the original client
+ // has focus, but also all its ancestor containers.
+ if (d->isEmbedded()) {
+ // If our window's embedded flag is set, then
+ // that suggests that we are part of a client. The
+ // parentWinId will then point to an container to whom
+ // we must pass this message.
+ sendXEmbedMessage(d->topLevelParentWinId(), x11Info().display(), XEMBED_REQUEST_FOCUS);
+ } else {
+ // Our window's embedded flag is not set,
+ // so we are the topmost container. We respond to
+ // the focus request message with a focus in
+ // message. This message will pass on from client
+ // to container to client until it reaches the
+ // originator of the XEMBED_REQUEST_FOCUS message.
+ sendXEmbedMessage(d->client, x11Info().display(), XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT);
+ }
+
+ break;
+ }
+ case XEMBED_FOCUS_NEXT:
+ // Client sends this event when it received a tab
+ // forward and was at the end of its focus chain. If
+ // we are the only widget in the focus chain, we send
+ // ourselves a FocusIn event.
+ if (d->focus_next != this) {
+ focusNextPrevChild(true);
+ } else {
+ QFocusEvent event(QEvent::FocusIn, Qt::TabFocusReason);
+ qApp->sendEvent(this, &event);
+ }
+
+ break;
+ case XEMBED_FOCUS_PREV:
+ // Client sends this event when it received a backtab
+ // and was at the start of its focus chain. If we are
+ // the only widget in the focus chain, we send
+ // ourselves a FocusIn event.
+ if (d->focus_next != this) {
+ focusNextPrevChild(false);
+ } else {
+ QFocusEvent event(QEvent::FocusIn, Qt::BacktabFocusReason);
+ qApp->sendEvent(this, &event);
+ }
+
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ break;
+ case XButtonPress:
+ if (!d->clientIsXEmbed) {
+ setFocus(Qt::MouseFocusReason);
+ XAllowEvents(x11Info().display(), ReplayPointer, CurrentTime);
+ return true;
+ }
+ break;
+ case XButtonRelease:
+ if (!d->clientIsXEmbed)
+ XAllowEvents(x11Info().display(), SyncPointer, CurrentTime);
+ break;
+ default:
+ break;
+ }
+
+ return QWidget::x11Event(event);
+}
+
+/*! \internal
+
+ Whenever the container is resized, we need to resize our client.
+*/
+void QX11EmbedContainer::resizeEvent(QResizeEvent *)
+{
+ Q_D(QX11EmbedContainer);
+ if (d->client)
+ XResizeWindow(x11Info().display(), d->client, width(), height());
+}
+
+/*! \internal
+
+ We use the QShowEvent to signal to our client that we want it to
+ map itself. We do this by changing its window property
+ XEMBED_INFO. The client will get an X11 PropertyNotify.
+*/
+void QX11EmbedContainer::showEvent(QShowEvent *)
+{
+ Q_D(QX11EmbedContainer);
+ if (d->client) {
+ long data[] = {XEMBED_VERSION, XEMBED_MAPPED};
+ XChangeProperty(x11Info().display(), d->client, ATOM(_XEMBED_INFO), ATOM(_XEMBED_INFO), 32,
+ PropModeReplace, (unsigned char *) data, 2);
+ }
+}
+
+/*! \internal
+
+ We use the QHideEvent to signal to our client that we want it to
+ unmap itself. We do this by changing its window property
+ XEMBED_INFO. The client will get an X11 PropertyNotify.
+*/
+void QX11EmbedContainer::hideEvent(QHideEvent *)
+{
+ Q_D(QX11EmbedContainer);
+ if (d->client) {
+ long data[] = {XEMBED_VERSION, XEMBED_MAPPED};
+ XChangeProperty(x11Info().display(), d->client, ATOM(_XEMBED_INFO), ATOM(_XEMBED_INFO), 32,
+ PropModeReplace, (unsigned char *) data, 2);
+ }
+}
+
+/*!
+ \reimp
+*/
+bool QX11EmbedContainer::event(QEvent *event)
+{
+ if (event->type() == QEvent::ParentChange) {
+ XSelectInput(x11Info().display(), internalWinId(),
+ KeyPressMask | KeyReleaseMask
+ | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask
+ | KeymapStateMask
+ | PointerMotionMask
+ | EnterWindowMask | LeaveWindowMask
+ | FocusChangeMask
+ | ExposureMask
+ | StructureNotifyMask
+ | SubstructureNotifyMask);
+ }
+ return QWidget::event(event);
+}
+
+/*! \internal
+
+ Rejects a client window by reparenting it to the root window. The
+ client will receive a reparentnotify, and will most likely assume
+ that the container has shut down. The XEmbed protocol does not
+ define any way to reject a client window, but this is a clean way
+ to do it.
+*/
+void QX11EmbedContainerPrivate::rejectClient(WId window)
+{
+ Q_Q(QX11EmbedContainer);
+ q->setEnabled(false);
+ XRemoveFromSaveSet(q->x11Info().display(), client);
+ XReparentWindow(q->x11Info().display(), window, q->x11Info().appRootWindow(q->x11Info().screen()), 0, 0);
+}
+
+/*! \internal
+
+ Accepts a client by mapping it, resizing it and optionally
+ activating and giving it logical focusing through XEMBED messages.
+*/
+void QX11EmbedContainerPrivate::acceptClient(WId window)
+{
+ Q_Q(QX11EmbedContainer);
+ client = window;
+ q->setEnabled(true);
+
+ // This tells Qt that we wish to forward DnD messages to
+ // our client.
+ if (!extra)
+ createExtra();
+ extraData()->xDndProxy = client;
+
+ unsigned int version = XEmbedVersion();
+
+ Atom actual_type_return;
+ int actual_format_return;
+ unsigned long nitems_return = 0;
+ unsigned long bytes_after_return;
+ unsigned char *prop_return = 0;
+ unsigned int clientversion = 0;
+
+ // Add this client to our saveset, so if we crash, the client window
+ // doesn't get destroyed. This is useful for containers that restart
+ // automatically after a crash, because it can simply reembed its clients
+ // without having to restart them (KDE panel).
+ XAddToSaveSet(q->x11Info().display(), client);
+
+ // XEmbed clients have an _XEMBED_INFO property in which we can
+ // fetch the version
+ if (XGetWindowProperty(q->x11Info().display(), client, ATOM(_XEMBED_INFO), 0, 2, false,
+ ATOM(_XEMBED_INFO), &actual_type_return, &actual_format_return,
+ &nitems_return, &bytes_after_return, &prop_return) == Success) {
+
+ if (actual_type_return != None && actual_format_return != 0) {
+ // Clients with the _XEMBED_INFO property are XEMBED clients.
+ clientIsXEmbed = true;
+
+ long *p = (long *)prop_return;
+ if (nitems_return >= 2)
+ clientversion = (unsigned int)p[0];
+ }
+
+ XFree(prop_return);
+ }
+
+ // Store client window's original size and placement.
+ Window root;
+ int x_return, y_return;
+ unsigned int width_return, height_return, border_width_return, depth_return;
+ XGetGeometry(q->x11Info().display(), client, &root, &x_return, &y_return,
+ &width_return, &height_return, &border_width_return, &depth_return);
+ clientOriginalRect.setCoords(x_return, y_return,
+ x_return + width_return - 1,
+ y_return + height_return - 1);
+
+ // Ask the client for its minimum size.
+ XSizeHints size;
+ long msize;
+ if (XGetWMNormalHints(q->x11Info().display(), client, &size, &msize) && (size.flags & PMinSize)) {
+ wmMinimumSizeHint = QSize(size.min_width, size.min_height);
+ q->updateGeometry();
+ }
+
+ // The container should set the data2 field to the lowest of its
+ // supported version number and that of the client (from
+ // _XEMBED_INFO property).
+ unsigned int minversion = version > clientversion ? clientversion : version;
+ sendXEmbedMessage(client, q->x11Info().display(), XEMBED_EMBEDDED_NOTIFY, q->internalWinId(), minversion);
+ XMapWindow(q->x11Info().display(), client);
+
+ // Resize it, but no smaller than its minimum size hint.
+ XResizeWindow(q->x11Info().display(),
+ client,
+ qMax(q->width(), wmMinimumSizeHint.width()),
+ qMax(q->height(), wmMinimumSizeHint.height()));
+ q->update();
+
+ // Not mentioned in the protocol is that if the container
+ // is already active, the client must be activated to work
+ // properly.
+ if (q->window()->isActiveWindow())
+ sendXEmbedMessage(client, q->x11Info().display(), XEMBED_WINDOW_ACTIVATE);
+
+ // Also, if the container already has focus, then it must
+ // send a focus in message to its new client; otherwise we ask
+ // it to remove focus.
+ if (q->focusWidget() == q && q->hasFocus())
+ sendXEmbedMessage(client, q->x11Info().display(), XEMBED_FOCUS_IN, XEMBED_FOCUS_FIRST);
+ else
+ sendXEmbedMessage(client, q->x11Info().display(), XEMBED_FOCUS_OUT);
+
+ if (!clientIsXEmbed) {
+ checkGrab();
+ if (q->hasFocus()) {
+ XSetInputFocus(q->x11Info().display(), client, XRevertToParent, x11Time());
+ }
+ } else {
+ if (!isEmbedded())
+ moveInputToProxy();
+ }
+
+ emit q->clientIsEmbedded();
+}
+
+/*! \internal
+
+ Moves X11 keyboard input focus to the focusProxy, unless the focus
+ is there already. When X11 keyboard input focus is on the
+ focusProxy, which is a child of the container and a sibling of the
+ client, X11 keypresses and keyreleases will always go to the proxy
+ and not to the client.
+*/
+void QX11EmbedContainerPrivate::moveInputToProxy()
+{
+ Q_Q(QX11EmbedContainer);
+ // Following Owen Taylor's advice from the XEmbed specification to
+ // always use CurrentTime when no explicit user action is involved.
+ XSetInputFocus(q->x11Info().display(), focusProxy->internalWinId(), XRevertToParent, CurrentTime);
+}
+
+/*! \internal
+
+ Ask the window manager to give us a default minimum size.
+*/
+QSize QX11EmbedContainer::minimumSizeHint() const
+{
+ Q_D(const QX11EmbedContainer);
+ if (!d->client || !d->wmMinimumSizeHint.isValid())
+ return QWidget::minimumSizeHint();
+ return d->wmMinimumSizeHint;
+}
+
+/*! \internal
+
+*/
+void QX11EmbedContainerPrivate::checkGrab()
+{
+ Q_Q(QX11EmbedContainer);
+ if (!clientIsXEmbed && q->isActiveWindow() && !q->hasFocus()) {
+ if (!xgrab) {
+ XGrabButton(q->x11Info().display(), AnyButton, AnyModifier, q->internalWinId(),
+ true, ButtonPressMask, GrabModeSync, GrabModeAsync,
+ None, None);
+ }
+ xgrab = true;
+ } else {
+ if (xgrab)
+ XUngrabButton(q->x11Info().display(), AnyButton, AnyModifier, q->internalWinId());
+ xgrab = false;
+ }
+}
+
+/*!
+ Detaches the client from the embedder. The client will appear as a
+ standalone window on the desktop.
+*/
+void QX11EmbedContainer::discardClient()
+{
+ Q_D(QX11EmbedContainer);
+ if (d->client) {
+ XResizeWindow(x11Info().display(), d->client, d->clientOriginalRect.width(),
+ d->clientOriginalRect.height());
+
+ d->rejectClient(d->client);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/x11/qx11embed_x11.h b/src/widgets/platforms/x11/qx11embed_x11.h
new file mode 100644
index 0000000000..fcf94b60a2
--- /dev/null
+++ b/src/widgets/platforms/x11/qx11embed_x11.h
@@ -0,0 +1,132 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QX11EMBED_X11_H
+#define QX11EMBED_X11_H
+
+#include <QtWidgets/qwidget.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QX11EmbedWidgetPrivate;
+class Q_WIDGETS_EXPORT QX11EmbedWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ QX11EmbedWidget(QWidget *parent = 0);
+ ~QX11EmbedWidget();
+
+ void embedInto(WId id);
+ WId containerWinId() const;
+
+ enum Error {
+ Unknown,
+ Internal,
+ InvalidWindowID
+ };
+ Error error() const;
+
+Q_SIGNALS:
+ void embedded();
+ void containerClosed();
+ void error(QX11EmbedWidget::Error error);
+
+protected:
+ bool x11Event(XEvent *);
+ bool eventFilter(QObject *, QEvent *);
+ bool event(QEvent *);
+ void resizeEvent(QResizeEvent *);
+
+private:
+ Q_DECLARE_PRIVATE(QX11EmbedWidget)
+ Q_DISABLE_COPY(QX11EmbedWidget)
+};
+
+class QX11EmbedContainerPrivate;
+class Q_WIDGETS_EXPORT QX11EmbedContainer : public QWidget
+{
+ Q_OBJECT
+public:
+ QX11EmbedContainer(QWidget *parent = 0);
+ ~QX11EmbedContainer();
+
+ void embedClient(WId id);
+ void discardClient();
+
+ WId clientWinId() const;
+
+ QSize minimumSizeHint() const;
+
+ enum Error {
+ Unknown,
+ Internal,
+ InvalidWindowID
+ };
+ Error error() const;
+
+Q_SIGNALS:
+ void clientIsEmbedded();
+ void clientClosed();
+ void error(QX11EmbedContainer::Error);
+
+protected:
+ bool x11Event(XEvent *);
+ bool eventFilter(QObject *, QEvent *);
+ void paintEvent(QPaintEvent *e);
+ void resizeEvent(QResizeEvent *);
+ void showEvent(QShowEvent *);
+ void hideEvent(QHideEvent *);
+ bool event(QEvent *);
+
+private:
+ Q_DECLARE_PRIVATE(QX11EmbedContainer)
+ Q_DISABLE_COPY(QX11EmbedContainer)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QX11EMBED_X11_H
diff --git a/src/widgets/platforms/x11/qx11info_x11.cpp b/src/widgets/platforms/x11/qx11info_x11.cpp
new file mode 100644
index 0000000000..f359b610e1
--- /dev/null
+++ b/src/widgets/platforms/x11/qx11info_x11.cpp
@@ -0,0 +1,543 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwidget.h"
+#include "qpixmap.h"
+#include "qx11info_x11.h"
+#include "qt_x11_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QX11Info
+ \brief The QX11Info class provides information about the X display
+ configuration.
+
+ \ingroup shared
+
+ The class provides two APIs: a set of non-static functions that
+ provide information about a specific widget or pixmap, and a set
+ of static functions that provide the default information for the
+ application.
+
+ \warning This class is only available on X11. For querying
+ per-screen information in a portable way, use QDesktopWidget.
+
+ \sa QWidget::x11Info(), QPixmap::x11Info(), QDesktopWidget
+*/
+
+/*!
+ Constructs an empty QX11Info object.
+*/
+QX11Info::QX11Info()
+ : x11data(0)
+{
+}
+
+/*!
+ Constructs a copy of \a other.
+*/
+QX11Info::QX11Info(const QX11Info &other)
+{
+ x11data = other.x11data;
+ if (x11data)
+ ++x11data->ref;
+}
+
+/*!
+ Assigns \a other to this object and returns a reference to this
+ object.
+*/
+QX11Info &QX11Info::operator=(const QX11Info &other)
+{
+ if (other.x11data)
+ ++other.x11data->ref;
+ if (x11data && !--x11data->ref)
+ delete x11data;
+ x11data = other.x11data;
+ return *this;
+}
+
+/*!
+ Destroys the QX11Info object.
+*/
+QX11Info::~QX11Info()
+{
+ if (x11data && !--x11data->ref)
+ delete x11data;
+}
+
+/*!
+ \internal
+ Makes a shallow copy of the X11-specific data of \a fromDevice, if it is not
+ null. Otherwise this function sets it to null.
+*/
+
+void QX11Info::copyX11Data(const QPaintDevice *fromDevice)
+{
+ QX11InfoData *xd = 0;
+ if (fromDevice) {
+ if (fromDevice->devType() == QInternal::Widget)
+ xd = static_cast<const QWidget *>(fromDevice)->x11Info().x11data;
+ else if (fromDevice->devType() == QInternal::Pixmap)
+ xd = static_cast<const QPixmap *>(fromDevice)->x11Info().x11data;
+ }
+ setX11Data(xd);
+}
+
+/*!
+ \internal
+ Makes a deep copy of the X11-specific data of \a fromDevice, if it is not
+ null. Otherwise this function sets it to null.
+*/
+
+void QX11Info::cloneX11Data(const QPaintDevice *fromDevice)
+{
+ QX11InfoData *d = 0;
+ if (fromDevice) {
+ QX11InfoData *xd;
+ if (fromDevice->devType() == QInternal::Widget) {
+ xd = static_cast<const QWidget *>(fromDevice)->x11Info().x11data;
+ } else {
+ Q_ASSERT(fromDevice->devType() == QInternal::Pixmap);
+ xd = static_cast<const QPixmap *>(fromDevice)->x11Info().x11data;
+ }
+ d = new QX11InfoData(*xd);
+ d->ref = 0;
+ }
+ setX11Data(d);
+}
+
+/*!
+ \internal
+ Makes a shallow copy of the X11-specific data \a d and assigns it to this
+ class. This function increments the reference code of \a d.
+*/
+
+void QX11Info::setX11Data(const QX11InfoData* d)
+{
+ if (x11data && !--x11data->ref)
+ delete x11data;
+ x11data = (QX11InfoData *)d;
+ if (x11data)
+ ++x11data->ref;
+}
+
+
+/*!
+ \internal
+ If \a def is false, returns a deep copy of the x11Data, or 0 if x11Data is 0.
+ If \a def is true, makes a QX11Data struct filled with the default
+ values.
+
+ In either case the caller is responsible for deleting the returned
+ struct. But notice that the struct is a shared class, so other
+ classes might also have a reference to it. The reference count of
+ the returned QX11Data* is 0.
+*/
+
+QX11InfoData* QX11Info::getX11Data(bool def) const
+{
+ QX11InfoData* res = 0;
+ if (def) {
+ res = new QX11InfoData;
+ res->ref = 0;
+ res->screen = appScreen();
+ res->depth = appDepth();
+ res->cells = appCells();
+ res->colormap = colormap();
+ res->defaultColormap = appDefaultColormap();
+ res->visual = (Visual*) appVisual();
+ res->defaultVisual = appDefaultVisual();
+ } else if (x11data) {
+ res = new QX11InfoData;
+ *res = *x11data;
+ res->ref = 0;
+ }
+ return res;
+}
+
+/*!
+ Returns the horizontal resolution of the given \a screen in terms of the
+ number of dots per inch.
+
+ The \a screen argument is an X screen number. Be aware that if
+ the user's system uses Xinerama (as opposed to traditional X11
+ multiscreen), there is only one X screen. Use QDesktopWidget to
+ query for information about Xinerama screens.
+
+ \sa setAppDpiX(), appDpiY()
+*/
+int QX11Info::appDpiX(int screen)
+{
+ if (!X11)
+ return 75;
+ if (screen < 0)
+ screen = X11->defaultScreen;
+ if (screen > X11->screenCount)
+ return 0;
+ return X11->screens[screen].dpiX;
+}
+
+/*!
+ Sets the horizontal resolution of the given \a screen to the number of
+ dots per inch specified by \a xdpi.
+
+ The \a screen argument is an X screen number. Be aware that if
+ the user's system uses Xinerama (as opposed to traditional X11
+ multiscreen), there is only one X screen. Use QDesktopWidget to
+ query for information about Xinerama screens.
+
+ \sa appDpiX(), setAppDpiY()
+*/
+
+void QX11Info::setAppDpiX(int screen, int xdpi)
+{
+ if (!X11)
+ return;
+ if (screen < 0)
+ screen = X11->defaultScreen;
+ if (screen > X11->screenCount)
+ return;
+ X11->screens[screen].dpiX = xdpi;
+}
+
+/*!
+ Returns the vertical resolution of the given \a screen in terms of the
+ number of dots per inch.
+
+ The \a screen argument is an X screen number. Be aware that if
+ the user's system uses Xinerama (as opposed to traditional X11
+ multiscreen), there is only one X screen. Use QDesktopWidget to
+ query for information about Xinerama screens.
+
+ \sa setAppDpiY(), appDpiX()
+*/
+
+int QX11Info::appDpiY(int screen)
+{
+ if (!X11)
+ return 75;
+ if (screen < 0)
+ screen = X11->defaultScreen;
+ if (screen > X11->screenCount)
+ return 0;
+ return X11->screens[screen].dpiY;
+}
+
+/*!
+ Sets the vertical resolution of the given \a screen to the number of
+ dots per inch specified by \a ydpi.
+
+ The \a screen argument is an X screen number. Be aware that if
+ the user's system uses Xinerama (as opposed to traditional X11
+ multiscreen), there is only one X screen. Use QDesktopWidget to
+ query for information about Xinerama screens.
+
+ \sa appDpiY(), setAppDpiX()
+*/
+void QX11Info::setAppDpiY(int screen, int ydpi)
+{
+ if (!X11)
+ return;
+ if (screen < 0)
+ screen = X11->defaultScreen;
+ if (screen > X11->screenCount)
+ return;
+ X11->screens[screen].dpiY = ydpi;
+}
+
+/*!
+ Returns the X11 time.
+
+ \sa setAppTime(), appUserTime()
+*/
+unsigned long QX11Info::appTime()
+{
+ return X11 ? X11->time : 0;
+}
+
+/*!
+ Sets the X11 time to the value specified by \a time.
+
+ \sa appTime(), setAppUserTime()
+*/
+void QX11Info::setAppTime(unsigned long time)
+{
+ if (X11) {
+ X11->time = time;
+ }
+}
+
+/*!
+ Returns the X11 user time.
+
+ \sa setAppUserTime(), appTime()
+*/
+unsigned long QX11Info::appUserTime()
+{
+ return X11 ? X11->userTime : 0;
+}
+
+/*!
+ Sets the X11 user time as specified by \a time.
+
+ \sa appUserTime(), setAppTime()
+*/
+void QX11Info::setAppUserTime(unsigned long time)
+{
+ if (X11) {
+ X11->userTime = time;
+ }
+}
+
+
+/*!
+ \fn const char *QX11Info::appClass()
+
+ Returns the X11 application class.
+
+ \sa display()
+*/
+
+/*!
+ Returns the default display for the application.
+
+ \sa appScreen()
+*/
+
+Display *QX11Info::display()
+{
+ return X11 ? X11->display : 0;
+}
+
+/*!
+ Returns the number of the screen where the application is being
+ displayed.
+
+ \sa display(), screen()
+*/
+int QX11Info::appScreen()
+{
+ return X11 ? X11->defaultScreen : 0;
+}
+
+/*!
+ Returns a handle for the application's color map on the given \a screen.
+
+ The \a screen argument is an X screen number. Be aware that if
+ the user's system uses Xinerama (as opposed to traditional X11
+ multiscreen), there is only one X screen. Use QDesktopWidget to
+ query for information about Xinerama screens.
+
+ \sa colormap(), defaultColormap()
+*/
+Qt::HANDLE QX11Info::appColormap(int screen)
+{
+ return X11 ? X11->screens[screen == -1 ? X11->defaultScreen : screen].colormap : 0;
+}
+
+/*!
+ Returns the current visual used by the application on the given
+ \a screen.
+
+ The \a screen argument is an X screen number. Be aware that if
+ the user's system uses Xinerama (as opposed to traditional X11
+ multiscreen), there is only one X screen. Use QDesktopWidget to
+ query for information about Xinerama screens.
+
+ \sa visual(), defaultVisual()
+*/
+
+void *QX11Info::appVisual(int screen)
+{
+ return X11 ? X11->screens[screen == -1 ? X11->defaultScreen : screen].visual : 0;
+}
+
+/*!
+ Returns a handle for the applications root window on the given \a screen.
+
+ The \a screen argument is an X screen number. Be aware that if
+ the user's system uses Xinerama (as opposed to traditional X11
+ multiscreen), there is only one X screen. Use QDesktopWidget to
+ query for information about Xinerama screens.
+
+ \sa QApplication::desktop()
+*/
+Qt::HANDLE QX11Info::appRootWindow(int screen)
+{
+ return X11 ? RootWindow(X11->display, screen == -1 ? X11->defaultScreen : screen) : 0;
+}
+
+/*!
+ Returns the color depth (bits per pixel) used by the application on the
+ given \a screen.
+
+ The \a screen argument is an X screen number. Be aware that if
+ the user's system uses Xinerama (as opposed to traditional X11
+ multiscreen), there is only one X screen. Use QDesktopWidget to
+ query for information about Xinerama screens.
+
+ \sa depth()
+*/
+
+int QX11Info::appDepth(int screen)
+{
+ return X11 ? X11->screens[screen == -1 ? X11->defaultScreen : screen].depth : 32;
+}
+
+/*!
+ Returns the number of cells used by the application on the given \a screen.
+
+ The \a screen argument is an X screen number. Be aware that if
+ the user's system uses Xinerama (as opposed to traditional X11
+ multiscreen), there is only one X screen. Use QDesktopWidget to
+ query for information about Xinerama screens.
+
+ \sa cells()
+*/
+
+int QX11Info::appCells(int screen)
+{ return X11 ? X11->screens[screen == -1 ? X11->defaultScreen : screen].cells : 0; }
+
+/*!
+ Returns true if the application has a default color map on the given
+ \a screen; otherwise returns false.
+
+ The \a screen argument is an X screen number. Be aware that if
+ the user's system uses Xinerama (as opposed to traditional X11
+ multiscreen), there is only one X screen. Use QDesktopWidget to
+ query for information about Xinerama screens.
+*/
+bool QX11Info::appDefaultColormap(int screen)
+{ return X11 ? X11->screens[screen == -1 ? X11->defaultScreen : screen].defaultColormap : true; }
+
+/*!
+ Returns true if the application has a default visual on the given \a screen;
+ otherwise returns false.
+
+ The \a screen argument is an X screen number. Be aware that if
+ the user's system uses Xinerama (as opposed to traditional X11
+ multiscreen), there is only one X screen. Use QDesktopWidget to
+ query for information about Xinerama screens.
+*/
+bool QX11Info::appDefaultVisual(int screen)
+{ return X11 ? X11->screens[screen == -1 ? X11->defaultScreen : screen].defaultVisual : true; }
+
+/*!
+ Returns the number of the screen currently in use.
+
+ The return value is an X screen number. Be aware that if the
+ user's system uses Xinerama (as opposed to traditional X11
+ multiscreen), there is only one X screen. Use QDesktopWidget to
+ query for information about Xinerama screens.
+
+ \sa appScreen()
+*/
+int QX11Info::screen() const
+{ return x11data ? x11data->screen : QX11Info::appScreen(); }
+
+/*!
+ Returns the color depth (bits per pixel) of the X display.
+
+ \sa appDepth()
+*/
+
+int QX11Info::depth() const
+{ return x11data ? x11data->depth : QX11Info::appDepth(); }
+
+/*!
+ Returns the number of cells.
+
+ \sa appCells()
+*/
+
+int QX11Info::cells() const
+{ return x11data ? x11data->cells : QX11Info::appCells(); }
+
+/*!
+ Returns a handle for the color map.
+
+ \sa defaultColormap()
+*/
+
+Qt::HANDLE QX11Info::colormap() const
+{ return x11data ? x11data->colormap : QX11Info::appColormap(); }
+
+/*!
+ Returns true if there is a default color map; otherwise returns false.
+
+ \sa colormap()
+*/
+
+bool QX11Info::defaultColormap() const
+{ return x11data ? x11data->defaultColormap : QX11Info::appDefaultColormap(); }
+
+/*!
+ Returns the current visual.
+
+ \sa appVisual(), defaultVisual()
+*/
+
+void *QX11Info::visual() const
+{ return x11data ? x11data->visual : QX11Info::appVisual(); }
+
+/*!
+ Returns true if there is a default visual; otherwise returns false.
+
+ \sa visual(), appVisual()
+*/
+
+bool QX11Info::defaultVisual() const
+{ return x11data ? x11data->defaultVisual : QX11Info::appDefaultVisual(); }
+
+
+/*!
+ \since 4.4
+
+ Returns true if there is a compositing manager running.
+*/
+bool QX11Info::isCompositingManagerRunning()
+{
+ return X11 ? X11->compositingManagerRunning : false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/platforms/x11/qx11info_x11.h b/src/widgets/platforms/x11/qx11info_x11.h
new file mode 100644
index 0000000000..60cd0e9e92
--- /dev/null
+++ b/src/widgets/platforms/x11/qx11info_x11.h
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QX11INFO_X11_H
+#define QX11INFO_X11_H
+
+#include <QtCore/qnamespace.h>
+
+typedef struct _XDisplay Display;
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+struct QX11InfoData;
+class QX11Info;
+class QPaintDevice;
+class QApplicationPrivate;
+class QX11InfoPrivate;
+struct QX11WindowAttributes;
+
+void qt_x11_getX11InfoForWindow(QX11Info * xinfo, const QX11WindowAttributes &a);
+class Q_WIDGETS_EXPORT QX11Info
+{
+public:
+ QX11Info();
+ ~QX11Info();
+ QX11Info(const QX11Info &other);
+ QX11Info &operator=(const QX11Info &other);
+
+ static Display *display();
+ static const char *appClass();
+ int screen() const;
+ int depth() const;
+ int cells() const;
+ Qt::HANDLE colormap() const;
+ bool defaultColormap() const;
+ void *visual() const;
+ bool defaultVisual() const;
+
+ static int appScreen();
+ static int appDepth(int screen = -1);
+ static int appCells(int screen = -1);
+ static Qt::HANDLE appColormap(int screen = -1);
+ static void *appVisual(int screen = -1);
+ static Qt::HANDLE appRootWindow(int screen = -1);
+ static bool appDefaultColormap(int screen = -1);
+ static bool appDefaultVisual(int screen = -1);
+ static int appDpiX(int screen = -1);
+ static int appDpiY(int screen = -1);
+ static void setAppDpiX(int screen, int dpi);
+ static void setAppDpiY(int screen, int dpi);
+ static unsigned long appTime();
+ static unsigned long appUserTime();
+ static void setAppTime(unsigned long time);
+ static void setAppUserTime(unsigned long time);
+ static bool isCompositingManagerRunning();
+
+protected:
+ void copyX11Data(const QPaintDevice *);
+ void cloneX11Data(const QPaintDevice *);
+ void setX11Data(const QX11InfoData *);
+ QX11InfoData* getX11Data(bool def = false) const;
+
+ QX11InfoData *x11data;
+
+ friend class QX11PaintEngine;
+ friend class QPixmap;
+ friend class QX11PlatformPixmap;
+ friend class QWidget;
+ friend class QWidgetPrivate;
+ friend class QGLWidget;
+ friend void qt_init(QApplicationPrivate *priv, int, Display *display, Qt::HANDLE visual,
+ Qt::HANDLE colormap);
+ friend void qt_cleanup();
+ friend void qt_x11_getX11InfoForWindow(QX11Info * xinfo, const QX11WindowAttributes &a);
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QX11INFO_X11_H
diff --git a/src/widgets/platforms/x11/qximinputcontext_p.h b/src/widgets/platforms/x11/qximinputcontext_p.h
new file mode 100644
index 0000000000..47c6f78ff9
--- /dev/null
+++ b/src/widgets/platforms/x11/qximinputcontext_p.h
@@ -0,0 +1,142 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** Definition of QXIMInputContext class
+**
+** Copyright (C) 2003-2004 immodule for Qt Project. All rights reserved.
+**
+** This file is written to contribute to Nokia Corporation and/or its subsidiary(-ies) under their own
+** license. You may use this file under your Qt license. Following
+** description is copied from their original file headers. Contact
+** immodule-qt@freedesktop.org if any conditions of this licensing are
+** not clear to you.
+**
+****************************************************************************/
+
+#ifndef QXIMINPUTCONTEXT_P_H
+#define QXIMINPUTCONTEXT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#if !defined(Q_NO_IM)
+
+#include "QtCore/qglobal.h"
+#include "QtWidgets/qinputcontext.h"
+#include "QtGui/qfont.h"
+#include "QtCore/qhash.h"
+#ifdef Q_WS_X11
+#include "QtCore/qlist.h"
+#include "QtCore/qbitarray.h"
+#include "QtGui/qwindowdefs.h"
+#include "private/qt_x11_p.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QKeyEvent;
+class QWidget;
+class QFont;
+class QString;
+
+class QXIMInputContext : public QInputContext
+{
+ Q_OBJECT
+public:
+ struct ICData {
+ XIC ic;
+ XFontSet fontset;
+ QWidget *widget;
+ QString text;
+ QBitArray selectedChars;
+ bool composing;
+ bool preeditEmpty;
+ void clear();
+ };
+
+ QXIMInputContext();
+ ~QXIMInputContext();
+
+ QString identifierName();
+ QString language();
+
+ void reset();
+
+ void mouseHandler( int x, QMouseEvent *event);
+ bool isComposing() const;
+
+ void setFocusWidget( QWidget *w );
+ void widgetDestroyed(QWidget *w);
+
+ void create_xim();
+ void close_xim();
+
+ void update();
+
+ ICData *icData() const;
+protected:
+ bool x11FilterEvent( QWidget *keywidget, XEvent *event );
+
+private:
+ static XIMStyle xim_style;
+
+ QString _language;
+ XIM xim;
+ QHash<WId, ICData *> ximData;
+
+ ICData *createICData(QWidget *w);
+};
+
+QT_END_NAMESPACE
+
+#endif // Q_NO_IM
+
+#endif // QXIMINPUTCONTEXT_P_H
diff --git a/src/widgets/platforms/x11/qximinputcontext_x11.cpp b/src/widgets/platforms/x11/qximinputcontext_x11.cpp
new file mode 100644
index 0000000000..de1212c556
--- /dev/null
+++ b/src/widgets/platforms/x11/qximinputcontext_x11.cpp
@@ -0,0 +1,885 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** Implementation of QXIMInputContext class
+**
+** Copyright (C) 2003-2004 immodule for Qt Project. All rights reserved.
+**
+** This file is written to contribute to Nokia Corporation and/or its subsidiary(-ies) under their own
+** license. You may use this file under your Qt license. Following
+** description is copied from their original file headers. Contact
+** immodule-qt@freedesktop.org if any conditions of this licensing are
+** not clear to you.
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+#include "qdebug.h"
+#include "qximinputcontext_p.h"
+
+#if !defined(QT_NO_IM)
+
+QT_BEGIN_NAMESPACE
+
+#if !defined(QT_NO_XIM)
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include "qplatformdefs.h"
+
+#include "qapplication.h"
+#include "qwidget.h"
+#include "qstring.h"
+#include "qlist.h"
+#include "qtextcodec.h"
+#include "qevent.h"
+#include "qtextformat.h"
+
+#include "qx11info_x11.h"
+
+#include <stdlib.h>
+#include <limits.h>
+QT_END_INCLUDE_NAMESPACE
+
+// #define QT_XIM_DEBUG
+#ifdef QT_XIM_DEBUG
+#define XIM_DEBUG qDebug
+#else
+#define XIM_DEBUG if (0) qDebug
+#endif
+
+// from qapplication_x11.cpp
+// #### move to X11 struct
+extern XIMStyle qt_xim_preferred_style;
+extern char *qt_ximServer;
+extern int qt_ximComposingKeycode;
+extern QTextCodec * qt_input_mapper;
+
+XIMStyle QXIMInputContext::xim_style = 0;
+// moved from qapplication_x11.cpp
+static const XIMStyle xim_default_style = XIMPreeditCallbacks | XIMStatusNothing;
+
+
+extern "C" {
+#ifdef USE_X11R6_XIM
+ static void xim_create_callback(XIM /*im*/,
+ XPointer client_data,
+ XPointer /*call_data*/)
+ {
+ QXIMInputContext *qic = reinterpret_cast<QXIMInputContext *>(client_data);
+ // qDebug("xim_create_callback");
+ qic->create_xim();
+ }
+
+ static void xim_destroy_callback(XIM /*im*/,
+ XPointer client_data,
+ XPointer /*call_data*/)
+ {
+ QXIMInputContext *qic = reinterpret_cast<QXIMInputContext *>(client_data);
+ // qDebug("xim_destroy_callback");
+ qic->close_xim();
+ XRegisterIMInstantiateCallback(X11->display, 0, 0, 0,
+ (XIMProc) xim_create_callback, reinterpret_cast<char *>(qic));
+ }
+#endif // USE_X11R6_XIM
+
+ static int xic_start_callback(XIC, XPointer client_data, XPointer) {
+ QXIMInputContext *qic = (QXIMInputContext *) client_data;
+ if (!qic) {
+ XIM_DEBUG("xic_start_callback: no qic");
+ return 0;
+ }
+ QXIMInputContext::ICData *data = qic->icData();
+ if (!data) {
+ XIM_DEBUG("xic_start_callback: no ic data");
+ return 0;
+ }
+ XIM_DEBUG("xic_start_callback");
+
+ data->clear();
+ data->composing = true;
+
+ return 0;
+ }
+
+ static int xic_draw_callback(XIC, XPointer client_data, XPointer call_data) {
+ QXIMInputContext *qic = (QXIMInputContext *) client_data;
+ if (!qic) {
+ XIM_DEBUG("xic_draw_callback: no qic");
+ return 0;
+ }
+ QXIMInputContext::ICData *data = qic->icData();
+ if (!data) {
+ XIM_DEBUG("xic_draw_callback: no ic data");
+ return 0;
+ }
+ XIM_DEBUG("xic_draw_callback");
+
+
+ if(!data->composing) {
+ data->clear();
+ data->composing = true;
+ }
+
+ XIMPreeditDrawCallbackStruct *drawstruct = (XIMPreeditDrawCallbackStruct *) call_data;
+ XIMText *text = (XIMText *) drawstruct->text;
+ int cursor = drawstruct->caret, sellen = 0, selstart = 0;
+
+ if (!drawstruct->caret && !drawstruct->chg_first && !drawstruct->chg_length && !text) {
+ if(data->text.isEmpty()) {
+ XIM_DEBUG("compose emptied");
+ // if the composition string has been emptied, we need
+ // to send an InputMethodEnd event
+ QInputMethodEvent e;
+ qic->sendEvent(e);
+ data->clear();
+
+ // if the commit string has coming after here, InputMethodStart
+ // will be sent dynamically
+ }
+ return 0;
+ }
+
+ if (text) {
+ char *str = 0;
+ if (text->encoding_is_wchar) {
+ int l = wcstombs(NULL, text->string.wide_char, text->length);
+ if (l != -1) {
+ str = new char[l + 1];
+ wcstombs(str, text->string.wide_char, l);
+ str[l] = 0;
+ }
+ } else
+ str = text->string.multi_byte;
+
+ if (!str)
+ return 0;
+
+ QString s = QString::fromLocal8Bit(str);
+
+ if (text->encoding_is_wchar)
+ delete [] str;
+
+ if (drawstruct->chg_length < 0)
+ data->text.replace(drawstruct->chg_first, INT_MAX, s);
+ else
+ data->text.replace(drawstruct->chg_first, drawstruct->chg_length, s);
+
+ if (data->selectedChars.size() < data->text.length()) {
+ // expand the selectedChars array if the compose string is longer
+ int from = data->selectedChars.size();
+ data->selectedChars.resize(data->text.length());
+ for (int x = from; x < data->selectedChars.size(); ++x)
+ data->selectedChars.clearBit(x);
+ }
+
+ // determine if the changed chars are selected based on text->feedback
+ for (int x = 0; x < text->length; ++x)
+ data->selectedChars.setBit(x + drawstruct->chg_first,
+ (text->feedback ? (text->feedback[x] & XIMReverse) : 0));
+
+ // figure out where the selection starts, and how long it is
+ bool started = false;
+ for (int x = 0; x < qMin(data->selectedChars.size(), data->text.length()); ++x) {
+ if (started) {
+ if (data->selectedChars.testBit(x)) ++sellen;
+ else break;
+ } else {
+ if (data->selectedChars.testBit(x)) {
+ selstart = x;
+ started = true;
+ sellen = 1;
+ }
+ }
+ }
+ } else {
+ if (drawstruct->chg_length == 0)
+ drawstruct->chg_length = -1;
+
+ data->text.remove(drawstruct->chg_first, drawstruct->chg_length);
+ bool qt_compose_emptied = data->text.isEmpty();
+ if (qt_compose_emptied) {
+ XIM_DEBUG("compose emptied 2 text=%s", data->text.toUtf8().constData());
+ // if the composition string has been emptied, we need
+ // to send an InputMethodEnd event
+ QInputMethodEvent e;
+ qic->sendEvent(e);
+ data->clear();
+ // if the commit string has coming after here, InputMethodStart
+ // will be sent dynamically
+ return 0;
+ }
+ }
+
+ XIM_DEBUG("sending compose: '%s', cursor=%d, sellen=%d",
+ data->text.toUtf8().constData(), cursor, sellen);
+ QList<QInputMethodEvent::Attribute> attrs;
+ if (selstart > 0)
+ attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, selstart,
+ qic->standardFormat(QInputContext::PreeditFormat));
+ if (sellen)
+ attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, selstart, sellen,
+ qic->standardFormat(QInputContext::SelectionFormat));
+ if (selstart + sellen < data->text.length())
+ attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
+ selstart + sellen, data->text.length() - selstart - sellen,
+ qic->standardFormat(QInputContext::PreeditFormat));
+ attrs << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, cursor, sellen ? 0 : 1, QVariant());
+ QInputMethodEvent e(data->text, attrs);
+ data->preeditEmpty = data->text.isEmpty();
+ qic->sendEvent(e);
+
+ return 0;
+ }
+
+ static int xic_done_callback(XIC, XPointer client_data, XPointer) {
+ QXIMInputContext *qic = (QXIMInputContext *) client_data;
+ if (!qic)
+ return 0;
+
+ XIM_DEBUG("xic_done_callback");
+ // Don't send InputMethodEnd here. QXIMInputContext::x11FilterEvent()
+ // handles InputMethodEnd with commit string.
+ return 0;
+ }
+}
+
+void QXIMInputContext::ICData::clear()
+{
+ text = QString();
+ selectedChars.clear();
+ composing = false;
+ preeditEmpty = true;
+}
+
+QXIMInputContext::ICData *QXIMInputContext::icData() const
+{
+ if (QWidget *w = focusWidget())
+ return ximData.value(w->effectiveWinId());
+ return 0;
+}
+/* The cache here is needed, as X11 leaks a few kb for every
+ XFreeFontSet call, so we avoid creating and deletion of fontsets as
+ much as possible
+*/
+static XFontSet fontsetCache[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+static int fontsetRefCount = 0;
+
+static const char * const fontsetnames[] = {
+ "-*-fixed-medium-r-*-*-16-*,-*-*-medium-r-*-*-16-*",
+ "-*-fixed-medium-i-*-*-16-*,-*-*-medium-i-*-*-16-*",
+ "-*-fixed-bold-r-*-*-16-*,-*-*-bold-r-*-*-16-*",
+ "-*-fixed-bold-i-*-*-16-*,-*-*-bold-i-*-*-16-*",
+ "-*-fixed-medium-r-*-*-24-*,-*-*-medium-r-*-*-24-*",
+ "-*-fixed-medium-i-*-*-24-*,-*-*-medium-i-*-*-24-*",
+ "-*-fixed-bold-r-*-*-24-*,-*-*-bold-r-*-*-24-*",
+ "-*-fixed-bold-i-*-*-24-*,-*-*-bold-i-*-*-24-*"
+};
+
+static XFontSet getFontSet(const QFont &f)
+{
+ int i = 0;
+ if (f.italic())
+ i |= 1;
+ if (f.bold())
+ i |= 2;
+
+ if (f.pointSize() > 20)
+ i += 4;
+
+ if (!fontsetCache[i]) {
+ Display* dpy = X11->display;
+ int missCount;
+ char** missList;
+ fontsetCache[i] = XCreateFontSet(dpy, fontsetnames[i], &missList, &missCount, 0);
+ if(missCount > 0)
+ XFreeStringList(missList);
+ if (!fontsetCache[i]) {
+ fontsetCache[i] = XCreateFontSet(dpy, "-*-fixed-*-*-*-*-16-*", &missList, &missCount, 0);
+ if(missCount > 0)
+ XFreeStringList(missList);
+ if (!fontsetCache[i])
+ fontsetCache[i] = (XFontSet)-1;
+ }
+ }
+ return (fontsetCache[i] == (XFontSet)-1) ? 0 : fontsetCache[i];
+}
+
+extern bool qt_use_rtl_extensions; // from qapplication_x11.cpp
+#ifndef QT_NO_XKB
+extern QLocale q_getKeyboardLocale(const QByteArray &layoutName, const QByteArray &variantName);
+#endif
+
+QXIMInputContext::QXIMInputContext()
+{
+ if (!qt_xim_preferred_style) // no configured input style, use the default
+ qt_xim_preferred_style = xim_default_style;
+
+ xim = 0;
+ QByteArray ximServerName(qt_ximServer);
+ if (qt_ximServer)
+ ximServerName.prepend("@im=");
+ else
+ ximServerName = "";
+
+ if (!XSupportsLocale())
+#ifndef QT_NO_DEBUG
+ qWarning("Qt: Locale not supported on X server")
+#endif
+ ;
+#ifdef USE_X11R6_XIM
+ else if (XSetLocaleModifiers (ximServerName.constData()) == 0)
+ qWarning("Qt: Cannot set locale modifiers: %s", ximServerName.constData());
+ else
+ XRegisterIMInstantiateCallback(X11->display, 0, 0, 0,
+ (XIMProc) xim_create_callback, reinterpret_cast<char *>(this));
+#else // !USE_X11R6_XIM
+ else if (XSetLocaleModifiers ("") == 0)
+ qWarning("Qt: Cannot set locale modifiers");
+ else
+ QXIMInputContext::create_xim();
+#endif // USE_X11R6_XIM
+
+#ifndef QT_NO_XKB
+ if (X11->use_xkb) {
+ QByteArray layoutName;
+ QByteArray variantName;
+
+ Atom type = XNone;
+ int format = 0;
+ ulong nitems = 0;
+ ulong bytesAfter = 0;
+ uchar *data = 0;
+ if (XGetWindowProperty(X11->display, RootWindow(X11->display, 0), ATOM(_XKB_RULES_NAMES), 0, 1024,
+ false, XA_STRING, &type, &format, &nitems, &bytesAfter, &data) == Success
+ && type == XA_STRING && format == 8 && nitems > 2) {
+
+ char *names[5] = { 0, 0, 0, 0, 0 };
+ char *p = reinterpret_cast<char *>(data), *end = p + nitems;
+ int i = 0;
+ do {
+ names[i++] = p;
+ p += qstrlen(p) + 1;
+ } while (p < end);
+
+ QList<QByteArray> layoutNames = QByteArray::fromRawData(names[2], qstrlen(names[2])).split(',');
+ QList<QByteArray> variantNames = QByteArray::fromRawData(names[3], qstrlen(names[3])).split(',');
+ for (int i = 0; i < qMin(layoutNames.count(), variantNames.count()); ++i ) {
+ QByteArray variantName = variantNames.at(i);
+ const int dashPos = variantName.indexOf("-");
+ if (dashPos >= 0)
+ variantName.truncate(dashPos);
+ QLocale keyboardInputLocale = q_getKeyboardLocale(layoutNames.at(i), variantName);
+ if (keyboardInputLocale.textDirection() == Qt::RightToLeft)
+ qt_use_rtl_extensions = true;
+ }
+ }
+
+ if (data)
+ XFree(data);
+ }
+#endif // QT_NO_XKB
+
+}
+
+
+/*!\internal
+ Creates the application input method.
+*/
+void QXIMInputContext::create_xim()
+{
+ ++fontsetRefCount;
+#ifndef QT_NO_XIM
+ xim = XOpenIM(X11->display, 0, 0, 0);
+ if (xim) {
+
+#ifdef USE_X11R6_XIM
+ XIMCallback destroy;
+ destroy.callback = (XIMProc) xim_destroy_callback;
+ destroy.client_data = XPointer(this);
+ if (XSetIMValues(xim, XNDestroyCallback, &destroy, (char *) 0) != 0)
+ qWarning("Xlib doesn't support destroy callback");
+#endif // USE_X11R6_XIM
+
+ XIMStyles *styles = 0;
+ XGetIMValues(xim, XNQueryInputStyle, &styles, (char *) 0, (char *) 0);
+ if (styles) {
+ int i;
+ for (i = 0; !xim_style && i < styles->count_styles; i++) {
+ if (styles->supported_styles[i] == qt_xim_preferred_style) {
+ xim_style = qt_xim_preferred_style;
+ break;
+ }
+ }
+ // if the preferred input style couldn't be found, look for
+ // Nothing
+ for (i = 0; !xim_style && i < styles->count_styles; i++) {
+ if (styles->supported_styles[i] == (XIMPreeditNothing | XIMStatusNothing)) {
+ xim_style = XIMPreeditNothing | XIMStatusNothing;
+ break;
+ }
+ }
+ // ... and failing that, None.
+ for (i = 0; !xim_style && i < styles->count_styles; i++) {
+ if (styles->supported_styles[i] == (XIMPreeditNone |
+ XIMStatusNone)) {
+ xim_style = XIMPreeditNone | XIMStatusNone;
+ break;
+ }
+ }
+
+ // qDebug("QApplication: using im style %lx", xim_style);
+ XFree((char *)styles);
+ }
+
+ if (xim_style) {
+
+#ifdef USE_X11R6_XIM
+ XUnregisterIMInstantiateCallback(X11->display, 0, 0, 0,
+ (XIMProc) xim_create_callback, reinterpret_cast<char *>(this));
+#endif // USE_X11R6_XIM
+
+ if (QWidget *focusWidget = QApplication::focusWidget()) {
+ // reinitialize input context after the input method
+ // server (like SCIM) has been launched without
+ // requiring the user to manually switch focus.
+ if (focusWidget->testAttribute(Qt::WA_InputMethodEnabled)
+ && focusWidget->testAttribute(Qt::WA_WState_Created)
+ && focusWidget->isEnabled())
+ setFocusWidget(focusWidget);
+ }
+ // following code fragment is not required for immodule
+ // version of XIM
+#if 0
+ QWidgetList list = qApp->topLevelWidgets();
+ for (int i = 0; i < list.size(); ++i) {
+ QWidget *w = list.at(i);
+ w->d->createTLSysExtra();
+ }
+#endif
+ } else {
+ // Give up
+ qWarning("No supported input style found."
+ " See InputMethod documentation.");
+ close_xim();
+ }
+ }
+#endif // QT_NO_XIM
+}
+
+/*!\internal
+ Closes the application input method.
+*/
+void QXIMInputContext::close_xim()
+{
+ for(QHash<WId, ICData *>::const_iterator i = ximData.constBegin(),
+ e = ximData.constEnd(); i != e; ++i) {
+ ICData *data = i.value();
+ if (data->ic)
+ XDestroyIC(data->ic);
+ delete data;
+ }
+ ximData.clear();
+
+ if ( --fontsetRefCount == 0 ) {
+ Display *dpy = X11->display;
+ for ( int i = 0; i < 8; i++ ) {
+ if ( fontsetCache[i] && fontsetCache[i] != (XFontSet)-1 ) {
+ XFreeFontSet(dpy, fontsetCache[i]);
+ fontsetCache[i] = 0;
+ }
+ }
+ }
+
+ setFocusWidget(0);
+ xim = 0;
+}
+
+
+
+QXIMInputContext::~QXIMInputContext()
+{
+ XIM old_xim = xim; // close_xim clears xim pointer.
+ close_xim();
+ if (old_xim)
+ XCloseIM(old_xim);
+}
+
+
+QString QXIMInputContext::identifierName()
+{
+ // the name should be "xim" rather than "XIM" to be consistent
+ // with corresponding immodule of GTK+
+ return QLatin1String("xim");
+}
+
+
+QString QXIMInputContext::language()
+{
+ QString language;
+ if (xim) {
+ QByteArray locale(XLocaleOfIM(xim));
+
+ if (locale.startsWith("zh")) {
+ // Chinese language should be formed as "zh_CN", "zh_TW", "zh_HK"
+ language = QLatin1String(locale.left(5));
+ } else {
+ // other languages should be two-letter ISO 639 language code
+ language = QLatin1String(locale.left(2));
+ }
+ }
+ return language;
+}
+
+void QXIMInputContext::reset()
+{
+ QWidget *w = focusWidget();
+ if (!w)
+ return;
+
+ ICData *data = ximData.value(w->effectiveWinId());
+ if (!data)
+ return;
+
+ if (data->ic) {
+ char *mb = XmbResetIC(data->ic);
+ QInputMethodEvent e;
+ if (mb) {
+ e.setCommitString(QString::fromLocal8Bit(mb));
+ XFree(mb);
+ data->preeditEmpty = false; // force sending an event
+ }
+ if (!data->preeditEmpty) {
+ sendEvent(e);
+ update();
+ }
+ }
+ data->clear();
+}
+
+void QXIMInputContext::widgetDestroyed(QWidget *w)
+{
+ QInputContext::widgetDestroyed(w);
+ ICData *data = ximData.take(w->effectiveWinId());
+ if (!data)
+ return;
+
+ data->clear();
+ if (data->ic)
+ XDestroyIC(data->ic);
+ delete data;
+}
+
+void QXIMInputContext::mouseHandler(int pos, QMouseEvent *e)
+{
+ if(e->type() != QEvent::MouseButtonPress)
+ return;
+
+ XIM_DEBUG("QXIMInputContext::mouseHandler pos=%d", pos);
+ if (QWidget *w = focusWidget()) {
+ ICData *data = ximData.value(w->effectiveWinId());
+ if (!data)
+ return;
+ if (pos < 0 || pos > data->text.length())
+ reset();
+ // ##### handle mouse position
+ }
+}
+
+bool QXIMInputContext::isComposing() const
+{
+ QWidget *w = focusWidget();
+ if (!w)
+ return false;
+
+ ICData *data = ximData.value(w->effectiveWinId());
+ if (!data)
+ return false;
+ return data->composing;
+}
+
+void QXIMInputContext::setFocusWidget(QWidget *w)
+{
+ if (!xim)
+ return;
+ QWidget *oldFocus = focusWidget();
+ if (oldFocus == w)
+ return;
+
+ if (language() != QLatin1String("ja"))
+ reset();
+
+ if (oldFocus) {
+ ICData *data = ximData.value(oldFocus->effectiveWinId());
+ if (data && data->ic)
+ XUnsetICFocus(data->ic);
+ }
+
+ QInputContext::setFocusWidget(w);
+
+ if (!w || w->inputMethodHints() & (Qt::ImhExclusiveInputMask | Qt::ImhHiddenText))
+ return;
+
+ ICData *data = ximData.value(w->effectiveWinId());
+ if (!data)
+ data = createICData(w);
+
+ if (data->ic)
+ XSetICFocus(data->ic);
+
+ update();
+}
+
+
+bool QXIMInputContext::x11FilterEvent(QWidget *keywidget, XEvent *event)
+{
+ int xkey_keycode = event->xkey.keycode;
+ if (!keywidget->testAttribute(Qt::WA_WState_Created))
+ return false;
+ if (XFilterEvent(event, keywidget->effectiveWinId())) {
+ qt_ximComposingKeycode = xkey_keycode; // ### not documented in xlib
+
+ update();
+
+ return true;
+ }
+ if (event->type != XKeyPress || event->xkey.keycode != 0)
+ return false;
+
+ QWidget *w = focusWidget();
+ if (keywidget != w)
+ return false;
+ ICData *data = ximData.value(w->effectiveWinId());
+ if (!data)
+ return false;
+
+ // input method has sent us a commit string
+ QByteArray string;
+ string.resize(513);
+ KeySym key; // unused
+ Status status; // unused
+ QString text;
+ int count = XmbLookupString(data->ic, &event->xkey, string.data(), string.size(),
+ &key, &status);
+
+ if (status == XBufferOverflow) {
+ string.resize(count + 1);
+ count = XmbLookupString(data->ic, &event->xkey, string.data(), string.size(),
+ &key, &status);
+ }
+ if (count > 0) {
+ // XmbLookupString() gave us some text, convert it to unicode
+ text = qt_input_mapper->toUnicode(string.constData() , count);
+ if (text.isEmpty()) {
+ // codec couldn't convert to unicode? this can happen when running in the
+ // C locale (or with no LANG set). try converting from latin-1
+ text = QString::fromLatin1(string.constData(), count);
+ }
+ }
+
+#if 0
+ if (!(xim_style & XIMPreeditCallbacks) || !isComposing()) {
+ // ############### send a regular key event here!
+ ;
+ }
+#endif
+
+ QInputMethodEvent e;
+ e.setCommitString(text);
+ sendEvent(e);
+ data->clear();
+
+ update();
+
+ return true;
+}
+
+
+QXIMInputContext::ICData *QXIMInputContext::createICData(QWidget *w)
+{
+ ICData *data = new ICData;
+ data->widget = w;
+ data->preeditEmpty = true;
+
+ XVaNestedList preedit_attr = 0;
+ XIMCallback startcallback, drawcallback, donecallback;
+
+ QFont font = w->font();
+ data->fontset = getFontSet(font);
+
+ if (xim_style & XIMPreeditArea) {
+ XRectangle rect;
+ rect.x = 0;
+ rect.y = 0;
+ rect.width = w->width();
+ rect.height = w->height();
+
+ preedit_attr = XVaCreateNestedList(0,
+ XNArea, &rect,
+ XNFontSet, data->fontset,
+ (char *) 0);
+ } else if (xim_style & XIMPreeditPosition) {
+ XPoint spot;
+ spot.x = 1;
+ spot.y = 1;
+
+ preedit_attr = XVaCreateNestedList(0,
+ XNSpotLocation, &spot,
+ XNFontSet, data->fontset,
+ (char *) 0);
+ } else if (xim_style & XIMPreeditCallbacks) {
+ startcallback.client_data = (XPointer) this;
+ startcallback.callback = (XIMProc) xic_start_callback;
+ drawcallback.client_data = (XPointer) this;
+ drawcallback.callback = (XIMProc)xic_draw_callback;
+ donecallback.client_data = (XPointer) this;
+ donecallback.callback = (XIMProc) xic_done_callback;
+
+ preedit_attr = XVaCreateNestedList(0,
+ XNPreeditStartCallback, &startcallback,
+ XNPreeditDrawCallback, &drawcallback,
+ XNPreeditDoneCallback, &donecallback,
+ (char *) 0);
+ }
+
+ if (preedit_attr) {
+ data->ic = XCreateIC(xim,
+ XNInputStyle, xim_style,
+ XNClientWindow, w->effectiveWinId(),
+ XNPreeditAttributes, preedit_attr,
+ (char *) 0);
+ XFree(preedit_attr);
+ } else {
+ data->ic = XCreateIC(xim,
+ XNInputStyle, xim_style,
+ XNClientWindow, w->effectiveWinId(),
+ (char *) 0);
+ }
+
+ if (data->ic) {
+ // when resetting the input context, preserve the input state
+ (void) XSetICValues(data->ic, XNResetState, XIMPreserveState, (char *) 0);
+ } else {
+ qWarning("Failed to create XIC");
+ }
+
+ ximData[w->effectiveWinId()] = data;
+ return data;
+}
+
+void QXIMInputContext::update()
+{
+ QWidget *w = focusWidget();
+ if (!w)
+ return;
+
+ ICData *data = ximData.value(w->effectiveWinId());
+ if (!data || !data->ic)
+ return;
+
+ QRect r = w->inputMethodQuery(Qt::ImMicroFocus).toRect();
+ QPoint p;
+ if (w->nativeParentWidget())
+ p = w->mapTo(w->nativeParentWidget(), QPoint((r.left() + r.right() + 1)/2, r.bottom()));
+ else
+ p = QPoint((r.left() + r.right() + 1)/2, r.bottom());
+ XPoint spot;
+ spot.x = p.x();
+ spot.y = p.y();
+
+ r = w->rect();
+ XRectangle area;
+ area.x = r.x();
+ area.y = r.y();
+ area.width = r.width();
+ area.height = r.height();
+
+ XFontSet fontset = getFontSet(qvariant_cast<QFont>(w->inputMethodQuery(Qt::ImFont)));
+ if (data->fontset == fontset)
+ fontset = 0;
+ else
+ data->fontset = fontset;
+
+ XVaNestedList preedit_attr;
+ if (fontset)
+ preedit_attr = XVaCreateNestedList(0,
+ XNSpotLocation, &spot,
+ XNArea, &area,
+ XNFontSet, fontset,
+ (char *) 0);
+ else
+ preedit_attr = XVaCreateNestedList(0,
+ XNSpotLocation, &spot,
+ XNArea, &area,
+ (char *) 0);
+
+ XSetICValues(data->ic, XNPreeditAttributes, preedit_attr, (char *) 0);
+ XFree(preedit_attr);
+}
+
+
+#else
+/*
+ When QT_NO_XIM is defined, we provide a dummy implementation for
+ this class. The reason for this is that the header file is moc'ed
+ regardless of QT_NO_XIM. The best would be to remove the file
+ completely from the pri file is QT_NO_XIM was defined, or for moc
+ to understand this preprocessor directive. Since the header does
+ not declare this class when QT_NO_XIM is defined, this is dead
+ code.
+*/
+bool QXIMInputContext::isComposing() const { return false; }
+QString QXIMInputContext::identifierName() { return QString(); }
+void QXIMInputContext::mouseHandler(int, QMouseEvent *) {}
+void QXIMInputContext::setFocusWidget(QWidget *) {}
+void QXIMInputContext::reset() {}
+void QXIMInputContext::update() {}
+QXIMInputContext::~QXIMInputContext() {}
+void QXIMInputContext::widgetDestroyed(QWidget *) {}
+QString QXIMInputContext::language() { return QString(); }
+bool QXIMInputContext::x11FilterEvent(QWidget *, XEvent *) { return true; }
+
+#endif //QT_NO_XIM
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_IM