summaryrefslogtreecommitdiffstats
path: root/src/widgets/platforms/mac
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@nokia.com>2011-05-07 00:02:01 +0200
committerLars Knoll <lars.knoll@nokia.com>2011-05-07 00:02:01 +0200
commitf67b8df3ebdba2d398b9cce686b7c644adffff08 (patch)
tree062dd469f7cf8daa01a32d3e7b767b8fbdb7573a /src/widgets/platforms/mac
parent32ce4fe9e6a94e77828e976776cf08da85254ff2 (diff)
library split
Diffstat (limited to 'src/widgets/platforms/mac')
-rw-r--r--src/widgets/platforms/mac/qapplication_mac.mm3134
-rw-r--r--src/widgets/platforms/mac/qclipboard_mac.cpp634
-rw-r--r--src/widgets/platforms/mac/qcocoaapplication_mac.mm222
-rw-r--r--src/widgets/platforms/mac/qcocoaapplication_mac_p.h117
-rw-r--r--src/widgets/platforms/mac/qcocoaapplicationdelegate_mac.mm354
-rw-r--r--src/widgets/platforms/mac/qcocoaapplicationdelegate_mac_p.h128
-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/qcocoamenuloader_mac.mm264
-rw-r--r--src/widgets/platforms/mac/qcocoamenuloader_mac_p.h95
-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.cpp165
-rw-r--r--src/widgets/platforms/mac/qfontdatabase_mac.cpp466
-rw-r--r--src/widgets/platforms/mac/qfontengine_coretext.mm880
-rw-r--r--src/widgets/platforms/mac/qfontengine_coretext_p.h144
-rw-r--r--src/widgets/platforms/mac/qfontengine_mac.mm1236
-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.h180
-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/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.cpp1751
-rw-r--r--src/widgets/platforms/mac/qpaintengine_mac_p.h254
-rw-r--r--src/widgets/platforms/mac/qpixmap_mac.cpp1195
-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.mm1824
-rw-r--r--src/widgets/platforms/mac/qt_cocoa_helpers_mac_p.h340
-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.mm5420
61 files changed, 32377 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..f607a72e92
--- /dev/null
+++ b/src/widgets/platforms/mac/qapplication_mac.mm
@@ -0,0 +1,3134 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** 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_mac_app_fullscreen = false;
+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.
+
+ \sa macEventFilter(void *nsevent)
+*/
+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..4a8bc56e41
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "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/qcocoaapplication_mac.mm b/src/widgets/platforms/mac/qcocoaapplication_mac.mm
new file mode 100644
index 0000000000..872f31dec7
--- /dev/null
+++ b/src/widgets/platforms/mac/qcocoaapplication_mac.mm
@@ -0,0 +1,222 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** 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>
+#ifdef QT_MAC_USE_COCOA
+#include <private/qcocoaapplication_mac_p.h>
+#include <private/qcocoaapplicationdelegate_mac_p.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+#include <private/qcocoaintrospection_p.h>
+
+QT_USE_NAMESPACE
+
+@implementation NSApplication (QT_MANGLE_NAMESPACE(QApplicationIntegration))
+
+- (void)QT_MANGLE_NAMESPACE(qt_setDockMenu):(NSMenu *)newMenu
+{
+ [[QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) sharedDelegate] setDockMenu:newMenu];
+}
+
+- (QApplicationPrivate *)QT_MANGLE_NAMESPACE(qt_qappPrivate)
+{
+ return [[QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) sharedDelegate] qAppPrivate];
+}
+
+- (QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *)QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)
+{
+ return [[QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) sharedDelegate] menuLoader];
+}
+
+- (int)QT_MANGLE_NAMESPACE(qt_validModesForFontPanel):(NSFontPanel *)fontPanel
+{
+ Q_UNUSED(fontPanel);
+ // only display those things that QFont can handle
+ return NSFontPanelFaceModeMask
+ | NSFontPanelSizeModeMask
+ | NSFontPanelCollectionModeMask
+ | NSFontPanelUnderlineEffectModeMask
+ | NSFontPanelStrikethroughEffectModeMask;
+}
+
+- (void)qt_sendPostedMessage:(NSEvent *)event
+{
+ // WARNING: data1 and data2 is truncated to from 64-bit to 32-bit on OS 10.5!
+ // That is why we need to split the address in two parts:
+ quint64 lower = [event data1];
+ quint64 upper = [event data2];
+ QCocoaPostMessageArgs *args = reinterpret_cast<QCocoaPostMessageArgs *>(lower | (upper << 32));
+ // Special case for convenience: if the argument is an NSNumber, we unbox it directly.
+ // Use NSValue instead if this behaviour is unwanted.
+ id a1 = ([args->arg1 isKindOfClass:[NSNumber class]]) ? (id)[args->arg1 intValue] : args->arg1;
+ id a2 = ([args->arg2 isKindOfClass:[NSNumber class]]) ? (id)[args->arg2 intValue] : args->arg2;
+ switch (args->argCount) {
+ case 0:
+ [args->target performSelector:args->selector];
+ break;
+ case 1:
+ [args->target performSelector:args->selector withObject:a1];
+ break;
+ case 3:
+ [args->target performSelector:args->selector withObject:a1 withObject:a2];
+ break;
+ }
+
+ delete args;
+}
+
+- (BOOL)qt_filterEvent:(NSEvent *)event
+{
+ if (qApp->macEventFilter(0, reinterpret_cast<EventRef>(event)))
+ return true;
+
+ if ([event type] == NSApplicationDefined) {
+ switch ([event subtype]) {
+ case QtCocoaEventSubTypePostMessage:
+ [NSApp qt_sendPostedMessage:event];
+ return true;
+ default:
+ break;
+ }
+ }
+ return false;
+}
+
+@end
+
+@implementation QNSApplication
+
+- (void)qt_sendEvent_original:(NSEvent *)event
+{
+ Q_UNUSED(event);
+ // This method will only be used as a signature
+ // template for the method we add into NSApplication
+ // containing the original [NSApplication sendEvent:] implementation
+}
+
+- (void)qt_sendEvent_replacement:(NSEvent *)event
+{
+ // This method (or its implementation to be precise) will
+ // be called instead of sendEvent if redirection occurs.
+ // 'self' will then be an instance of NSApplication
+ // (and not QNSApplication)
+ if (![NSApp qt_filterEvent:event])
+ [self qt_sendEvent_original:event];
+}
+
+- (void)sendEvent:(NSEvent *)event
+{
+ // This method will be called if
+ // no redirection occurs
+ if (![NSApp qt_filterEvent:event])
+ [super sendEvent:event];
+}
+
+- (void)qtDispatcherToQAction:(id)sender
+{
+ // Forward actions sendt from the menu bar (e.g. quit) to the menu loader.
+ // Having this method here means that we are the last stop in the responder
+ // chain, and that we are able to handle menu actions even when no window is
+ // visible on screen. Note: If Qt is used as a plugin, Qt will not use a
+ // native menu bar. Hence, we will also not need to do any redirection etc. as
+ // we do with sendEvent.
+ [[NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)] qtDispatcherToQAction:sender];
+}
+
+@end
+
+QT_BEGIN_NAMESPACE
+
+void qt_redirectNSApplicationSendEvent()
+{
+ if ([NSApp isMemberOfClass:[QNSApplication class]]) {
+ // No need to change implementation since Qt
+ // already controls a subclass of NSApplication
+ return;
+ }
+
+ // Change the implementation of [NSApplication sendEvent] to the
+ // implementation of qt_sendEvent_replacement found in QNSApplication.
+ // And keep the old implementation that gets overwritten inside a new
+ // method 'qt_sendEvent_original' that we add to NSApplication
+ qt_cocoa_change_implementation(
+ [NSApplication class],
+ @selector(sendEvent:),
+ [QNSApplication class],
+ @selector(qt_sendEvent_replacement:),
+ @selector(qt_sendEvent_original:));
+ }
+
+QT_END_NAMESPACE
+#endif
diff --git a/src/widgets/platforms/mac/qcocoaapplication_mac_p.h b/src/widgets/platforms/mac/qcocoaapplication_mac_p.h
new file mode 100644
index 0000000000..0c3f5e442d
--- /dev/null
+++ b/src/widgets/platforms/mac/qcocoaapplication_mac_p.h
@@ -0,0 +1,117 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+//
+// 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.
+//
+
+/*
+ Cocoa Application Categories
+*/
+#include "qmacdefines_mac.h"
+#ifdef QT_MAC_USE_COCOA
+#import <AppKit/AppKit.h>
+QT_FORWARD_DECLARE_CLASS(QApplicationPrivate)
+@class QT_MANGLE_NAMESPACE(QCocoaMenuLoader);
+
+@interface NSApplication (QT_MANGLE_NAMESPACE(QApplicationIntegration))
+- (void)QT_MANGLE_NAMESPACE(qt_setDockMenu):(NSMenu *)newMenu;
+- (QApplicationPrivate *)QT_MANGLE_NAMESPACE(qt_qappPrivate);
+- (QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *)QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader);
+- (int)QT_MANGLE_NAMESPACE(qt_validModesForFontPanel):(NSFontPanel *)fontPanel;
+
+- (void)qt_sendPostedMessage:(NSEvent *)event;
+- (BOOL)qt_filterEvent:(NSEvent *)event;
+@end
+
+@interface QNSApplication : NSApplication {
+}
+@end
+
+QT_BEGIN_NAMESPACE
+
+void qt_redirectNSApplicationSendEvent();
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/widgets/platforms/mac/qcocoaapplicationdelegate_mac.mm b/src/widgets/platforms/mac/qcocoaapplicationdelegate_mac.mm
new file mode 100644
index 0000000000..77cd8902c3
--- /dev/null
+++ b/src/widgets/platforms/mac/qcocoaapplicationdelegate_mac.mm
@@ -0,0 +1,354 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+ **
+ ** 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 "qmacdefines_mac.h"
+#ifdef QT_MAC_USE_COCOA
+
+#import <private/qcocoaapplicationdelegate_mac_p.h>
+#import <private/qcocoamenuloader_mac_p.h>
+#import <private/qcocoaapplication_mac_p.h>
+#include <private/qapplication_p.h>
+#include <private/qt_mac_p.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+#include <private/qdesktopwidget_mac_p.h>
+#include <qevent.h>
+#include <qurl.h>
+#include <qapplication.h>
+
+QT_BEGIN_NAMESPACE
+extern void onApplicationChangedActivation(bool); // qapplication_mac.mm
+extern void qt_release_apple_event_handler(); //qapplication_mac.mm
+extern QPointer<QWidget> qt_last_mouse_receiver; // qapplication_mac.cpp
+extern QPointer<QWidget> qt_last_native_mouse_receiver; // qt_cocoa_helpers_mac.mm
+extern QPointer<QWidget> qt_button_down; // qapplication_mac.cpp
+
+QT_END_NAMESPACE
+
+QT_FORWARD_DECLARE_CLASS(QDesktopWidgetImplementation)
+QT_USE_NAMESPACE
+
+static QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) *sharedCocoaApplicationDelegate = nil;
+
+static void cleanupCocoaApplicationDelegate()
+{
+ [sharedCocoaApplicationDelegate release];
+}
+
+@implementation QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate)
+
+- (id)init
+{
+ self = [super init];
+ if (self)
+ inLaunch = true;
+ return self;
+}
+
+- (void)dealloc
+{
+ sharedCocoaApplicationDelegate = nil;
+ [dockMenu release];
+ [qtMenuLoader release];
+ if (reflectionDelegate) {
+ [NSApp setDelegate:reflectionDelegate];
+ [reflectionDelegate release];
+ }
+ [super dealloc];
+}
+
++ (id)allocWithZone:(NSZone *)zone
+{
+ @synchronized(self) {
+ if (sharedCocoaApplicationDelegate == nil) {
+ sharedCocoaApplicationDelegate = [super allocWithZone:zone];
+ return sharedCocoaApplicationDelegate;
+ qAddPostRoutine(cleanupCocoaApplicationDelegate);
+ }
+ }
+ return nil;
+}
+
++ (QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate)*)sharedDelegate
+{
+ @synchronized(self) {
+ if (sharedCocoaApplicationDelegate == nil)
+ [[self alloc] init];
+ }
+ return [[sharedCocoaApplicationDelegate retain] autorelease];
+}
+
+- (void)setDockMenu:(NSMenu*)newMenu
+{
+ [newMenu retain];
+ [dockMenu release];
+ dockMenu = newMenu;
+}
+
+- (NSMenu *)applicationDockMenu
+{
+ return [[dockMenu retain] autorelease];
+}
+
+- (QApplicationPrivate *)qAppPrivate
+{
+ return qtPrivate;
+}
+
+- (void)setQtPrivate:(QApplicationPrivate *)value
+{
+ qtPrivate = value;
+}
+
+- (void)setMenuLoader:(QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *)menuLoader
+{
+ [menuLoader retain];
+ [qtMenuLoader release];
+ qtMenuLoader = menuLoader;
+}
+
+- (QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *)menuLoader
+{
+ return [[qtMenuLoader retain] autorelease];
+}
+
+// This function will only be called when NSApp is actually running. Before
+// that, the kAEQuitApplication Apple event will be sent to
+// QApplicationPrivate::globalAppleEventProcessor in qapplication_mac.mm
+- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
+{
+ Q_UNUSED(sender);
+ // The reflection delegate gets precedence
+ if (reflectionDelegate
+ && [reflectionDelegate respondsToSelector:@selector(applicationShouldTerminate:)]) {
+ return [reflectionDelegate applicationShouldTerminate:sender];
+ }
+
+ if (qtPrivate->canQuit()) {
+ if (!startedQuit) {
+ startedQuit = true;
+ qAppInstance()->quit();
+ startedQuit = false;
+ }
+ }
+
+ if (qtPrivate->threadData->eventLoops.size() == 0) {
+ // INVARIANT: No event loop is executing. This probably
+ // means that Qt is used as a plugin, or as a part of a native
+ // Cocoa application. In any case it should be fine to
+ // terminate now:
+ return NSTerminateNow;
+ }
+
+ return NSTerminateCancel;
+}
+
+- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
+{
+ Q_UNUSED(aNotification);
+ inLaunch = false;
+ qt_release_apple_event_handler();
+}
+
+- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames
+{
+ for (NSString *fileName in filenames) {
+ QString qtFileName = qt_mac_NSStringToQString(fileName);
+ if (inLaunch) {
+ // We need to be careful because Cocoa will be nice enough to take
+ // command line arguments and send them to us as events. Given the history
+ // of Qt Applications, this will result in behavior people don't want, as
+ // they might be doing the opening themselves with the command line parsing.
+ if (qApp->arguments().contains(qtFileName))
+ continue;
+ }
+ QFileOpenEvent foe(qtFileName);
+ qt_sendSpontaneousEvent(qAppInstance(), &foe);
+ }
+
+ if (reflectionDelegate &&
+ [reflectionDelegate respondsToSelector:@selector(application:openFiles:)])
+ [reflectionDelegate application:sender openFiles:filenames];
+}
+
+- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
+{
+ // If we have a reflection delegate, that will get to call the shots.
+ if (reflectionDelegate
+ && [reflectionDelegate respondsToSelector:
+ @selector(applicationShouldTerminateAfterLastWindowClosed:)])
+ return [reflectionDelegate applicationShouldTerminateAfterLastWindowClosed:sender];
+ return NO; // Someday qApp->quitOnLastWindowClosed(); when QApp and NSApp work closer together.
+}
+
+
+- (void)applicationDidBecomeActive:(NSNotification *)notification
+{
+ if (reflectionDelegate
+ && [reflectionDelegate respondsToSelector:@selector(applicationDidBecomeActive:)])
+ [reflectionDelegate applicationDidBecomeActive:notification];
+
+ onApplicationChangedActivation(true);
+
+ if (!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, 0);
+ qt_last_mouse_receiver = widgetUnderMouse;
+ qt_last_native_mouse_receiver = widgetUnderMouse ?
+ (widgetUnderMouse->internalWinId() ? widgetUnderMouse : widgetUnderMouse->nativeParentWidget()) : 0;
+ }
+}
+
+- (void)applicationDidResignActive:(NSNotification *)notification
+{
+ if (reflectionDelegate
+ && [reflectionDelegate respondsToSelector:@selector(applicationDidResignActive:)])
+ [reflectionDelegate applicationDidResignActive:notification];
+
+ onApplicationChangedActivation(false);
+
+ if (!QWidget::mouseGrabber())
+ QApplicationPrivate::dispatchEnterLeave(0, qt_last_mouse_receiver);
+ qt_last_mouse_receiver = 0;
+ qt_last_native_mouse_receiver = 0;
+ qt_button_down = 0;
+}
+
+- (void)applicationDidChangeScreenParameters:(NSNotification *)notification
+{
+ Q_UNUSED(notification);
+ QDesktopWidgetImplementation::instance()->onResize();
+}
+
+- (void)setReflectionDelegate:(NSObject <NSApplicationDelegate> *)oldDelegate
+{
+ [oldDelegate retain];
+ [reflectionDelegate release];
+ reflectionDelegate = oldDelegate;
+}
+
+- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
+{
+ NSMethodSignature *result = [super methodSignatureForSelector:aSelector];
+ if (!result && reflectionDelegate) {
+ result = [reflectionDelegate methodSignatureForSelector:aSelector];
+ }
+ return result;
+}
+
+- (BOOL)respondsToSelector:(SEL)aSelector
+{
+ BOOL result = [super respondsToSelector:aSelector];
+ if (!result && reflectionDelegate)
+ result = [reflectionDelegate respondsToSelector:aSelector];
+ return result;
+}
+
+- (void)forwardInvocation:(NSInvocation *)invocation
+{
+ SEL invocationSelector = [invocation selector];
+ if (reflectionDelegate && [reflectionDelegate respondsToSelector:invocationSelector])
+ [invocation invokeWithTarget:reflectionDelegate];
+ else
+ [self doesNotRecognizeSelector:invocationSelector];
+}
+
+- (void)getUrl:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
+{
+ Q_UNUSED(replyEvent);
+
+ NSString *urlString = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
+ QUrl url(qt_mac_NSStringToQString(urlString));
+ QFileOpenEvent qtEvent(url);
+ qt_sendSpontaneousEvent(qAppInstance(), &qtEvent);
+}
+
+- (void)appleEventQuit:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
+{
+ Q_UNUSED(event);
+ Q_UNUSED(replyEvent);
+ [NSApp terminate:self];
+}
+
+- (void)qtDispatcherToQAction:(id)sender
+{
+ [[NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)] qtDispatcherToQAction:sender];
+}
+
+@end
+#endif
diff --git a/src/widgets/platforms/mac/qcocoaapplicationdelegate_mac_p.h b/src/widgets/platforms/mac/qcocoaapplicationdelegate_mac_p.h
new file mode 100644
index 0000000000..714c046f48
--- /dev/null
+++ b/src/widgets/platforms/mac/qcocoaapplicationdelegate_mac_p.h
@@ -0,0 +1,128 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+/****************************************************************************
+ **
+ ** 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.
+ **
+ ****************************************************************************/
+
+//
+// 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 "qmacdefines_mac.h"
+#ifdef QT_MAC_USE_COCOA
+#import <Cocoa/Cocoa.h>
+
+QT_FORWARD_DECLARE_CLASS(QApplicationPrivate);
+
+@class QT_MANGLE_NAMESPACE(QCocoaMenuLoader);
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5
+
+@protocol NSApplicationDelegate <NSObject>
+- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
+- (void)applicationDidFinishLaunching:(NSNotification *)aNotification;
+- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames;
+- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender;
+- (void)applicationDidBecomeActive:(NSNotification *)notification;
+- (void)applicationDidResignActive:(NSNotification *)notification;
+@end
+
+#endif
+
+@interface QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) : NSObject <NSApplicationDelegate> {
+ bool startedQuit;
+ QApplicationPrivate *qtPrivate;
+ NSMenu *dockMenu;
+ QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *qtMenuLoader;
+ NSObject <NSApplicationDelegate> *reflectionDelegate;
+ bool inLaunch;
+}
++ (QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate)*)sharedDelegate;
+- (void)setDockMenu:(NSMenu *)newMenu;
+- (void)setQtPrivate:(QApplicationPrivate *)value;
+- (QApplicationPrivate *)qAppPrivate;
+- (void)setMenuLoader:(QT_MANGLE_NAMESPACE(QCocoaMenuLoader)*)menuLoader;
+- (QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *)menuLoader;
+- (void)setReflectionDelegate:(NSObject <NSApplicationDelegate> *)oldDelegate;
+- (void)getUrl:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent;
+@end
+#endif
diff --git a/src/widgets/platforms/mac/qcocoaintrospection_mac.mm b/src/widgets/platforms/mac/qcocoaintrospection_mac.mm
new file mode 100644
index 0000000000..70c893aeec
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** 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..1c7d6ac13c
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** 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/qcocoamenuloader_mac.mm b/src/widgets/platforms/mac/qcocoamenuloader_mac.mm
new file mode 100644
index 0000000000..71ff011069
--- /dev/null
+++ b/src/widgets/platforms/mac/qcocoamenuloader_mac.mm
@@ -0,0 +1,264 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmacdefines_mac.h"
+#ifdef QT_MAC_USE_COCOA
+#include <qaction.h>
+#include <qcoreapplication.h>
+#include <private/qcocoamenuloader_mac_p.h>
+#include <private/qapplication_p.h>
+#include <private/qt_mac_p.h>
+#include <private/qmenubar_p.h>
+#include <qmenubar.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+
+QT_FORWARD_DECLARE_CLASS(QCFString)
+QT_FORWARD_DECLARE_CLASS(QString)
+
+#ifndef QT_NO_TRANSLATION
+ QT_BEGIN_NAMESPACE
+ extern QString qt_mac_applicationmenu_string(int type);
+ QT_END_NAMESPACE
+#endif
+
+QT_USE_NAMESPACE
+
+@implementation QT_MANGLE_NAMESPACE(QCocoaMenuLoader)
+
+- (void)awakeFromNib
+{
+ servicesItem = [[appMenu itemWithTitle:@"Services"] retain];
+ hideAllOthersItem = [[appMenu itemWithTitle:@"Hide Others"] retain];
+ showAllItem = [[appMenu itemWithTitle:@"Show All"] retain];
+
+ // Get the names in the nib to match the app name set by Qt.
+ const NSString *appName = reinterpret_cast<const NSString*>(QCFString::toCFStringRef(qAppName()));
+ [quitItem setTitle:[[quitItem title] stringByReplacingOccurrencesOfString:@"NewApplication"
+ withString:const_cast<NSString *>(appName)]];
+ [hideItem setTitle:[[hideItem title] stringByReplacingOccurrencesOfString:@"NewApplication"
+ withString:const_cast<NSString *>(appName)]];
+ [aboutItem setTitle:[[aboutItem title] stringByReplacingOccurrencesOfString:@"NewApplication"
+ withString:const_cast<NSString *>(appName)]];
+ [appName release];
+ // Disable the items that don't do anything. If someone associates a QAction with them
+ // They should get synced back in.
+ [preferencesItem setEnabled:NO];
+ [preferencesItem setHidden:YES];
+ [aboutItem setEnabled:NO];
+ [aboutItem setHidden:YES];
+}
+
+- (void)ensureAppMenuInMenu:(NSMenu *)menu
+{
+ // The application menu is the menu in the menu bar that contains the
+ // 'Quit' item. When changing menu bar (e.g when switching between
+ // windows with different menu bars), we never recreate this menu, but
+ // instead pull it out the current menu bar and place into the new one:
+ NSMenu *mainMenu = [NSApp mainMenu];
+ if ([NSApp mainMenu] == menu)
+ return; // nothing to do (menu is the current menu bar)!
+
+#ifndef QT_NAMESPACE
+ Q_ASSERT(mainMenu);
+#endif
+ // Grab the app menu out of the current menu.
+ int numItems = [mainMenu numberOfItems];
+ NSMenuItem *oldAppMenuItem = 0;
+ for (int i = 0; i < numItems; ++i) {
+ NSMenuItem *item = [mainMenu itemAtIndex:i];
+ if ([item submenu] == appMenu) {
+ oldAppMenuItem = item;
+ [oldAppMenuItem retain];
+ [mainMenu removeItemAtIndex:i];
+ break;
+ }
+ }
+
+ if (oldAppMenuItem) {
+ [oldAppMenuItem setSubmenu:nil];
+ [oldAppMenuItem release];
+ NSMenuItem *appMenuItem = [[NSMenuItem alloc] initWithTitle:@"Apple"
+ action:nil keyEquivalent:@""];
+ [appMenuItem setSubmenu:appMenu];
+ [menu insertItem:appMenuItem atIndex:0];
+ }
+}
+
+- (void)removeActionsFromAppMenu
+{
+ for (NSMenuItem *item in [appMenu itemArray])
+ [item setTag:nil];
+}
+
+- (void)dealloc
+{
+ [servicesItem release];
+ [hideAllOthersItem release];
+ [showAllItem release];
+
+ [lastAppSpecificItem release];
+ [theMenu release];
+ [appMenu release];
+ [super dealloc];
+}
+
+- (NSMenu *)menu
+{
+ return [[theMenu retain] autorelease];
+}
+
+- (NSMenu *)applicationMenu
+{
+ return [[appMenu retain] autorelease];
+}
+
+- (NSMenuItem *)quitMenuItem
+{
+ return [[quitItem retain] autorelease];
+}
+
+- (NSMenuItem *)preferencesMenuItem
+{
+ return [[preferencesItem retain] autorelease];
+}
+
+- (NSMenuItem *)aboutMenuItem
+{
+ return [[aboutItem retain] autorelease];
+}
+
+- (NSMenuItem *)aboutQtMenuItem
+{
+ return [[aboutQtItem retain] autorelease];
+}
+
+- (NSMenuItem *)hideMenuItem
+{
+ return [[hideItem retain] autorelease];
+}
+
+- (NSMenuItem *)appSpecificMenuItem
+{
+ // Create an App-Specific menu item, insert it into the menu and return
+ // it as an autorelease item.
+ NSMenuItem *item = [[NSMenuItem alloc] init];
+
+ NSInteger location;
+ if (lastAppSpecificItem == nil) {
+ location = [appMenu indexOfItem:aboutQtItem];
+ } else {
+ location = [appMenu indexOfItem:lastAppSpecificItem];
+ [lastAppSpecificItem release];
+ }
+ lastAppSpecificItem = item; // Keep track of this for later (i.e., don't release it)
+ [appMenu insertItem:item atIndex:location + 1];
+
+ return [[item retain] autorelease];
+}
+
+- (BOOL) acceptsFirstResponder
+{
+ return YES;
+}
+
+- (void)terminate:(id)sender
+{
+ [NSApp terminate:sender];
+}
+
+- (void)orderFrontStandardAboutPanel:(id)sender
+{
+ [NSApp orderFrontStandardAboutPanel:sender];
+}
+
+- (void)hideOtherApplications:(id)sender
+{
+ [NSApp hideOtherApplications:sender];
+}
+
+- (void)unhideAllApplications:(id)sender
+{
+ [NSApp unhideAllApplications:sender];
+}
+
+- (void)hide:(id)sender
+{
+ [NSApp hide:sender];
+}
+
+- (void)qtUpdateMenubar
+{
+ QMenuBarPrivate::macUpdateMenuBarImmediatly();
+}
+
+- (void)qtTranslateApplicationMenu
+{
+#ifndef QT_NO_TRANSLATION
+ [servicesItem setTitle: qt_mac_QStringToNSString(qt_mac_applicationmenu_string(0))];
+ [hideItem setTitle: qt_mac_QStringToNSString(qt_mac_applicationmenu_string(1).arg(qAppName()))];
+ [hideAllOthersItem setTitle: qt_mac_QStringToNSString(qt_mac_applicationmenu_string(2))];
+ [showAllItem setTitle: qt_mac_QStringToNSString(qt_mac_applicationmenu_string(3))];
+ [preferencesItem setTitle: qt_mac_QStringToNSString(qt_mac_applicationmenu_string(4))];
+ [quitItem setTitle: qt_mac_QStringToNSString(qt_mac_applicationmenu_string(5).arg(qAppName()))];
+ [aboutItem setTitle: qt_mac_QStringToNSString(qt_mac_applicationmenu_string(6).arg(qAppName()))];
+#endif
+}
+
+- (IBAction)qtDispatcherToQAction:(id)sender
+{
+ QScopedLoopLevelCounter loopLevelCounter(QApplicationPrivate::instance()->threadData);
+ NSMenuItem *item = static_cast<NSMenuItem *>(sender);
+ if (QAction *action = reinterpret_cast<QAction *>([item tag])) {
+ action->trigger();
+ } else if (item == quitItem) {
+ // We got here because someone was once the quitItem, but it has been
+ // abandoned (e.g., the menubar was deleted). In the meantime, just do
+ // normal QApplication::quit().
+ qApp->quit();
+ }
+}
+
+ - (void)orderFrontCharacterPalette:(id)sender
+ {
+ [NSApp orderFrontCharacterPalette:sender];
+ }
+@end
+#endif // QT_MAC_USE_COCOA
diff --git a/src/widgets/platforms/mac/qcocoamenuloader_mac_p.h b/src/widgets/platforms/mac/qcocoamenuloader_mac_p.h
new file mode 100644
index 0000000000..cfcc7e00c6
--- /dev/null
+++ b/src/widgets/platforms/mac/qcocoamenuloader_mac_p.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOCOAMENULOADER_P_H
+#define QCOCOAMENULOADER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header
+// file may change from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qmacdefines_mac.h"
+#ifdef QT_MAC_USE_COCOA
+#import <Cocoa/Cocoa.h>
+
+@interface QT_MANGLE_NAMESPACE(QCocoaMenuLoader) : NSResponder
+{
+ IBOutlet NSMenu *theMenu;
+ IBOutlet NSMenu *appMenu;
+ IBOutlet NSMenuItem *quitItem;
+ IBOutlet NSMenuItem *preferencesItem;
+ IBOutlet NSMenuItem *aboutItem;
+ IBOutlet NSMenuItem *aboutQtItem;
+ IBOutlet NSMenuItem *hideItem;
+ NSMenuItem *lastAppSpecificItem;
+ NSMenuItem *servicesItem;
+ NSMenuItem *hideAllOthersItem;
+ NSMenuItem *showAllItem;
+}
+- (void)ensureAppMenuInMenu:(NSMenu *)menu;
+- (void)removeActionsFromAppMenu;
+- (NSMenu *)applicationMenu;
+- (NSMenu *)menu;
+- (NSMenuItem *)quitMenuItem;
+- (NSMenuItem *)preferencesMenuItem;
+- (NSMenuItem *)aboutMenuItem;
+- (NSMenuItem *)aboutQtMenuItem;
+- (NSMenuItem *)hideMenuItem;
+- (NSMenuItem *)appSpecificMenuItem;
+- (IBAction)terminate:(id)sender;
+- (IBAction)orderFrontStandardAboutPanel:(id)sender;
+- (IBAction)hideOtherApplications:(id)sender;
+- (IBAction)unhideAllApplications:(id)sender;
+- (IBAction)hide:(id)sender;
+- (IBAction)qtDispatcherToQAction:(id)sender;
+- (void)qtUpdateMenubar;
+- (void)orderFrontCharacterPalette:(id)sender;
+@end
+
+#endif // QT_MAC_USE_COCOA
+#endif // QCOCOAMENULOADER_P_H
diff --git a/src/widgets/platforms/mac/qcocoapanel_mac.mm b/src/widgets/platforms/mac/qcocoapanel_mac.mm
new file mode 100644
index 0000000000..67a12e25f8
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#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..542615903e
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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..ee1115bd4e
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+ 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..e885d1552c
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#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..cc79b6705b
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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_GUI_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..6e5023aaca
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "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..d567cab244
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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..b761934c01
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "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..09b40875f6
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of 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..1faf068a12
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#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..638ce2df9a
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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..28589f41b8
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "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..0afa3ee4f0
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <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..0b529c9843
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** 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..ac638ea7d8
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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..3af2ba007c
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "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..677a7368b4
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** 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..12fcafbb01
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** 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)
+ {
+ 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..daf68c03ea
--- /dev/null
+++ b/src/widgets/platforms/mac/qfont_mac.cpp
@@ -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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qfont.h"
+#include "qfont_p.h"
+#include "qfontengine_p.h"
+#include "qfontengine_mac_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
+{
+#if 0
+ QFontEngine *fe = d->engineForScript(QUnicodeTables::Common);
+ if (fe && fe->type() == QFontEngine::Mac)
+ return (Qt::HANDLE)static_cast<QFontEngineMacMulti*>(fe)->fontFamilyRef();
+#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..5ba236b5f7
--- /dev/null
+++ b/src/widgets/platforms/mac/qfontdatabase_mac.cpp
@@ -0,0 +1,466 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <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);
+ 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;
+ 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, 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, 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;
+}
+
+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 = qRound(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
+ }
+
+ //find the font
+ 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();
+
+#if defined(QT_MAC_USE_COCOA)
+ QCFString fontName = NULL, familyName = NULL;
+#else
+ ATSFontFamilyRef familyRef = 0;
+ ATSFontRef fontRef = 0;
+#endif
+
+ 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);
+ familyName = CTFontCopyFamilyName(ctFont);
+ goto FamilyFound;
+ }
+#else
+ familyRef = ATSFontFamilyFindFromName(QCFString(db->families[k]->name), kATSOptionFlagsDefault);
+ if (familyRef) {
+ fontRef = ATSFontFindFromName(QCFString(db->families[k]->name), kATSOptionFlagsDefault);
+ goto FamilyFound;
+ }
+#endif
+ }
+ }
+ }
+FamilyFound:
+ //fill in the engine's font definition
+ QFontDef fontDef = d->request; //copy..
+ if(fontDef.pointSize < 0)
+ fontDef.pointSize = qt_mac_pointsize(fontDef, d->dpi);
+ else
+ fontDef.pixelSize = qt_mac_pixelsize(fontDef, d->dpi);
+
+#ifdef QT_MAC_USE_COCOA
+ fontDef.family = familyName;
+ QFontEngine *engine = new QCoreTextFontEngineMulti(fontName, fontDef, d->kerning);
+#else
+ QCFString actualName;
+ if (ATSFontFamilyGetName(familyRef, kATSOptionFlagsDefault, &actualName) == noErr)
+ fontDef.family = actualName;
+ QFontEngine *engine = new QFontEngineMacMulti(familyRef, fontRef, fontDef, d->kerning);
+#endif
+ d->engineData->engine = engine;
+ engine->ref.ref(); //a ref for the engineData->engine
+ 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..d4df2183ed
--- /dev/null
+++ b/src/widgets/platforms/mac/qfontengine_coretext.mm
@@ -0,0 +1,880 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "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 = 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(CGFontRef cgFontRef, const QFontDef &fontDef, bool kerning)
+ : QFontEngineMulti(0)
+{
+ this->fontDef = fontDef;
+
+ transform = CGAffineTransformIdentity;
+ if (fontDef.stretch != 100) {
+ transform = CGAffineTransformMakeScale(float(fontDef.stretch) / float(100), 1);
+ }
+
+ ctfont = CTFontCreateWithGraphicsFont(cgFontRef, fontDef.pixelSize, &transform, NULL);
+ 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);
+ 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
+
+static inline CGAffineTransform transformFromFontDef(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 = transformFromFontDef(fontDef);
+ ctfont = font;
+ CFRetain(ctfont);
+ cgFont = CTFontCopyGraphicsFont(font, NULL);
+ init();
+}
+
+QCoreTextFontEngine::QCoreTextFontEngine(CGFontRef font, const QFontDef &def)
+{
+ fontDef = def;
+ transform = transformFromFontDef(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;
+
+ 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();
+
+ ctMaxCharWidth = ctMinLeftBearing = ctMinRightBearing = 0;
+ QByteArray hheaTable = getSfntTable(MAKE_TAG('h', 'h', 'e', 'a'));
+ if (hheaTable.size() >= 16) {
+ quint16 width = qFromBigEndian<quint16>(reinterpret_cast<const uchar *>(hheaTable.constData() + 10));
+ ctMaxCharWidth = width * fontDef.pixelSize / emSize;
+ qint16 bearing = qFromBigEndian<qint16>(reinterpret_cast<const uchar *>(hheaTable.constData() + 12));
+ ctMinLeftBearing = bearing * fontDef.pixelSize / emSize;
+ bearing = qFromBigEndian<qint16>(reinterpret_cast<const uchar *>(hheaTable.constData() + 14));
+ ctMinRightBearing = bearing * fontDef.pixelSize / emSize;
+ }
+}
+
+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 (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
+ ? qRound(ctMaxCharWidth) : ctMaxCharWidth;
+}
+
+qreal QCoreTextFontEngine::minLeftBearing() const
+{
+ return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
+ ? qRound(ctMinLeftBearing) : ctMinLeftBearing;
+}
+
+qreal QCoreTextFontEngine::minRightBearing() const
+{
+ return (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
+ ? qRound(ctMinRightBearing) : ctMinLeftBearing;
+}
+
+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.toReal() + subPixelPosition.toReal();
+ qreal pos_y = im.height() + br.y.toReal() - 1;
+ 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, fontDef);
+}
+
+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..bb80a9b2f3
--- /dev/null
+++ b/src/widgets/platforms/mac/qfontengine_coretext_p.h
@@ -0,0 +1,144 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef 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)
+
+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;
+ qreal ctMaxCharWidth;
+ qreal ctMinLeftBearing;
+ qreal ctMinRightBearing;
+ friend class QCoreTextFontEngineMulti;
+};
+
+class QCoreTextFontEngineMulti : public QFontEngineMulti
+{
+public:
+ QCoreTextFontEngineMulti(const QCFString &name, const QFontDef &fontDef, bool kerning);
+ QCoreTextFontEngineMulti(CGFontRef cgFontRef, 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"; }
+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;
+};
+
+#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..9f094ad7d1
--- /dev/null
+++ b/src/widgets/platforms/mac/qfontengine_mac.mm
@@ -0,0 +1,1236 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "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].unicode() >= 0xd800 && str[i].unicode() < 0xdc00
+ && str[i+1].unicode() >= 0xdc00 && str[i+1].unicode() < 0xe000);
+ }
+#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.unicode() >= 0xd800 && current.unicode() < 0xdc00
+ && next.unicode() >= 0xdc00 && next.unicode() < 0xe000) {
+ 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)
+{
+ unsigned int uc = str[i].unicode();
+ if (uc >= 0xd800 && uc < 0xdc00 && i < len-1) {
+ uint low = str[i+1].unicode();
+ if (low >= 0xdc00 && low < 0xe000) {
+ uc = (uc - 0xd800)*0x400 + (low - 0xdc00) + 0x10000;
+ ++i;
+ }
+ }
+ return uc;
+}
+
+// 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..292ea98d9a
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef 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..d3bbf89711
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <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_GUI_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..d6ccb93593
--- /dev/null
+++ b/src/widgets/platforms/mac/qmacdefines_mac.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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** 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
+#ifdef QT_MAC_USE_COCOA
+ 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
+#else // Carbon
+ 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;
+ typedef struct OpaqueWindowPtr * WindowRef;
+ typedef struct OpaqueControlRef * HIViewRef;
+ typedef WindowRef OSWindowRef;
+ typedef HIViewRef OSViewRef;
+ typedef MenuRef OSMenuRef;
+ typedef EventRef OSEventRef;
+#endif // QT_MAC_USE_COCOA
+
+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..6a4f0bb445
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "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..465f6a2ac8
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef 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/qmime_mac.cpp b/src/widgets/platforms/mac/qmime_mac.cpp
new file mode 100644
index 0000000000..d6f6222c23
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "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_GUI_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..d9e845a01c
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <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..16be930d0a
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of 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..6ec3f64efa
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of 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..2cb4916c06
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of 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..4eb5332194
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of 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..245408a0b0
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "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_GUI_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_GUI_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 QPixmapData at some point
+ if (pm->data->classId() == QPixmapData::MacClass) {
+ const QMacPixmapData *pmData = static_cast<const QMacPixmapData*>(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() == QPixmapData::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..c6d061dea8
--- /dev/null
+++ b/src/widgets/platforms/mac/qpaintengine_mac.cpp
@@ -0,0 +1,1751 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <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
+}
+
+/*
+ Ideally, we should pass the widget in here, and use CGGetDisplaysWithRect() etc.
+ to support multiple displays correctly.
+*/
+CGColorSpaceRef QCoreGraphicsPaintEngine::macDisplayColorSpace(const QWidget *widget)
+{
+ CGColorSpaceRef colorSpace;
+
+ CGDirectDisplayID displayID;
+ CMProfileRef displayProfile = 0;
+ if (widget == 0) {
+ displayID = CGMainDisplayID();
+ } else {
+ const QRect &qrect = widget->window()->geometry();
+ CGRect rect = CGRectMake(qrect.x(), qrect.y(), qrect.width(), qrect.height());
+ CGDisplayCount throwAway;
+ CGDisplayErr dErr = CGGetDisplaysWithRect(rect, 1, &displayID, &throwAway);
+ if (dErr != kCGErrorSuccess)
+ return macDisplayColorSpace(0); // fall back on main display
+ }
+ if ((colorSpace = m_displayColorSpaceHash.value(displayID)))
+ return colorSpace;
+
+ CMError err = CMGetProfileByAVID((CMDisplayIDType)displayID, &displayProfile);
+ if (err == noErr) {
+ colorSpace = CGColorSpaceCreateWithPlatformColorSpace(displayProfile);
+ } else if (widget) {
+ return macDisplayColorSpace(0); // fall back on main display
+ }
+
+ if (colorSpace == 0)
+ colorSpace = CGColorSpaceCreateDeviceRGB();
+
+ m_displayColorSpaceHash.insert(displayID, colorSpace);
+ CMCloseProfile(displayProfile);
+ if (!m_postRoutineRegistered) {
+ m_postRoutineRegistered = true;
+ qAddPostRoutine(QCoreGraphicsPaintEngine::cleanUpMacColorSpaces);
+ }
+ return colorSpace;
+}
+
+void QCoreGraphicsPaintEngine::cleanUpMacColorSpaces()
+{
+ if (m_genericColorSpace) {
+ CFRelease(m_genericColorSpace);
+ m_genericColorSpace = 0;
+ }
+ QHash<CGDirectDisplayID, CGColorSpaceRef>::const_iterator it = m_displayColorSpaceHash.constBegin();
+ while (it != m_displayColorSpaceHash.constEnd()) {
+ if (it.value())
+ CFRelease(it.value());
+ ++it;
+ }
+ m_displayColorSpaceHash.clear();
+}
+
+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);
+ } else if(op == Qt::UniteClip) {
+ d->current.clip = d->current.clip.united(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;
+ else if(op == Qt::UniteClip)
+ d->current.clip = d->current.clip.united(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();
+}
+
+static void drawImageReleaseData (void *info, const void *, size_t)
+{
+ delete static_cast<QImage *>(info);
+}
+
+CGImageRef qt_mac_createCGImageFromQImage(const QImage &img, const QImage **imagePtr = 0)
+{
+ QImage *image;
+ if (img.depth() != 32)
+ image = new QImage(img.convertToFormat(QImage::Format_ARGB32_Premultiplied));
+ else
+ image = new QImage(img);
+
+ uint cgflags = kCGImageAlphaNone;
+ switch (image->format()) {
+ case QImage::Format_ARGB32_Premultiplied:
+ cgflags = kCGImageAlphaPremultipliedFirst;
+ break;
+ case QImage::Format_ARGB32:
+ cgflags = kCGImageAlphaFirst;
+ break;
+ case QImage::Format_RGB32:
+ cgflags = kCGImageAlphaNoneSkipFirst;
+ default:
+ break;
+ }
+#if defined(kCGBitmapByteOrder32Host) //only needed because CGImage.h added symbols in the minor version
+ cgflags |= kCGBitmapByteOrder32Host;
+#endif
+ QCFType<CGDataProviderRef> dataProvider = CGDataProviderCreateWithData(image,
+ static_cast<const QImage *>(image)->bits(),
+ image->byteCount(),
+ drawImageReleaseData);
+ if (imagePtr)
+ *imagePtr = image;
+ return CGImageCreate(image->width(), image->height(), 8, 32,
+ image->bytesPerLine(),
+ QCoreGraphicsPaintEngine::macGenericColorSpace(),
+ cgflags, dataProvider, 0, false, kCGRenderingIntentDefault);
+
+}
+
+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();
+ shading = CGShadingCreateRadial(colorspace, CGPointMake(focal.x(), focal.y()),
+ 0.0, 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..3e71d6ca6b
--- /dev/null
+++ b/src/widgets/platforms/mac/qpaintengine_mac_p.h
@@ -0,0 +1,254 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef 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); }
+
+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..72e2aa6e04
--- /dev/null
+++ b/src/widgets/platforms/mac/qpixmap_mac.cpp
@@ -0,0 +1,1195 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "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 <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_GUI_EXPORT quint32 *qt_mac_pixmap_get_base(const QPixmap *pix)
+{
+ return static_cast<QMacPixmapData*>(pix->data.data())->pixels;
+}
+
+Q_GUI_EXPORT int qt_mac_pixmap_get_bytes_per_line(const QPixmap *pix)
+{
+ return static_cast<QMacPixmapData*>(pix->data.data())->bytesPerRow;
+}
+
+void qt_mac_cgimage_data_free(void *info, const void *memoryToFree, size_t)
+{
+ QMacPixmapData *pmdata = static_cast<QMacPixmapData *>(info);
+ if (!pmdata) {
+ free(const_cast<void *>(memoryToFree));
+ } else {
+ if (QMacPixmapData::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;
+ }
+}
+
+CGImageRef qt_mac_image_to_cgimage(const QImage &image)
+{
+ int bitsPerColor = 8;
+ int bitsPerPixel = 32;
+ if (image.depth() == 1) {
+ bitsPerColor = 1;
+ bitsPerPixel = 1;
+ }
+ QCFType<CGDataProviderRef> provider =
+ CGDataProviderCreateWithData(0, image.bits(), image.bytesPerLine() * image.height(),
+ 0);
+
+ uint cgflags = kCGImageAlphaPremultipliedFirst;
+#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version
+ cgflags |= kCGBitmapByteOrder32Host;
+#endif
+
+ CGImageRef cgImage = CGImageCreate(image.width(), image.height(), bitsPerColor, bitsPerPixel,
+ image.bytesPerLine(),
+ QCoreGraphicsPaintEngine::macGenericColorSpace(),
+ cgflags, provider,
+ 0,
+ 0,
+ kCGRenderingIntentDefault);
+
+ return cgImage;
+}
+
+/*****************************************************************************
+ 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<QMacPixmapData*> QMacPixmapData::validDataPointers;
+
+QMacPixmapData::QMacPixmapData(PixelType type)
+ : QPixmapData(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)
+{
+}
+
+QPixmapData *QMacPixmapData::createCompatiblePixmapData() const
+{
+ return new QMacPixmapData(pixelType());
+}
+
+#define BEST_BYTE_ALIGNMENT 16
+#define COMPTUE_BEST_BYTES_PER_ROW(bpr) \
+ (((bpr) + (BEST_BYTE_ALIGNMENT - 1)) & ~(BEST_BYTE_ALIGNMENT - 1))
+
+void QMacPixmapData::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 QMacPixmapData::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 QMacPixmapData::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 QMacPixmapData::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 QMacPixmapData::alphaChannel() const
+{
+ if (!has_alpha)
+ return QPixmap();
+
+ QMacPixmapData *alpha = new QMacPixmapData(PixmapType);
+ alpha->resize(w, h);
+ macGetAlphaChannel(alpha, false);
+ return QPixmap(alpha);
+}
+
+void QMacPixmapData::setAlphaChannel(const QPixmap &alpha)
+{
+ has_mask = true;
+ QMacPixmapData *alphaData = static_cast<QMacPixmapData*>(alpha.data.data());
+ macSetAlphaChannel(alphaData, false);
+}
+
+QBitmap QMacPixmapData::mask() const
+{
+ if (!has_mask && !has_alpha)
+ return QBitmap();
+
+ QMacPixmapData *mask = new QMacPixmapData(BitmapType);
+ mask->resize(w, h);
+ macGetAlphaChannel(mask, true);
+ return QPixmap(mask);
+}
+
+void QMacPixmapData::setMask(const QBitmap &mask)
+{
+ if (mask.isNull()) {
+ QMacPixmapData 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;
+ QMacPixmapData *maskData = static_cast<QMacPixmapData*>(mask.data.data());
+ macSetAlphaChannel(maskData, true);
+}
+
+int QMacPixmapData::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;
+}
+
+QMacPixmapData::~QMacPixmapData()
+{
+ 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 QMacPixmapData::macSetAlphaChannel(const QMacPixmapData *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 QMacPixmapData::macGetAlphaChannel(QMacPixmapData *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 QMacPixmapData::macSetHasAlpha(bool b)
+{
+ has_alpha = b;
+ macReleaseCGImageRef();
+}
+
+void QMacPixmapData::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 QMacPixmapData::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 QMacPixmapData::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 QMacPixmapData::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
+ QMacPixmapData *pm = new QMacPixmapData(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 QMacPixmapData::transformed not properly implemented
+ qWarning("QMacPixmapData::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("QMacPixmapData::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() == QPixmapData::MacClass) {
+ QMacPixmapData *d = static_cast<QMacPixmapData *>(data.data());
+ if (!d->cg_data)
+ d->macCreateCGImageRef();
+ CGImageRef ret = d->cg_data;
+ CGImageRetain(ret);
+ return ret;
+ } else if (data->classId() == QPixmapData::RasterClass) {
+ return qt_mac_image_to_cgimage(static_cast<QRasterPixmapData *>(data.data())->image);
+ }
+ return 0;
+}
+
+bool QMacPixmapData::hasAlphaChannel() const
+{
+ return has_alpha;
+}
+
+CGImageRef qt_mac_create_imagemask(const QPixmap &pixmap, const QRectF &sr)
+{
+ QMacPixmapData *px = static_cast<QMacPixmapData*>(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* QMacPixmapData::paintEngine() const
+{
+ if (!pengine) {
+ QMacPixmapData *that = const_cast<QMacPixmapData*>(this);
+ that->pengine = new QCoreGraphicsPaintEngine();
+ }
+ return pengine;
+}
+
+void QMacPixmapData::copy(const QPixmapData *data, const QRect &rect)
+{
+ if (data->pixelType() == BitmapType) {
+ QBitmap::fromImage(toImage().copy(rect));
+ return;
+ }
+
+ const QMacPixmapData *macData = static_cast<const QMacPixmapData*>(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 QMacPixmapData::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..307e38aceb
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef 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/private/qpixmapdata_p.h>
+#include <QtGui/private/qpixmapdatafactory_p.h>
+#include <QtGui/private/qt_mac_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QMacPixmapData : public QPixmapData
+{
+public:
+ QMacPixmapData(PixelType type);
+ ~QMacPixmapData();
+
+ QPixmapData *createCompatiblePixmapData() const;
+
+ void resize(int width, int height);
+ void fromImage(const QImage &image, Qt::ImageConversionFlags flags);
+ void copy(const QPixmapData *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(QMacPixmapData *, bool asMask) const;
+ void macSetAlphaChannel(const QMacPixmapData *, 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<QMacPixmapData*> 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..1dce30301f
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <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..511705d26f
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef 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..b24ab70267
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "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..1ed4185a5d
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <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,
+ int 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..50fd783df4
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <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_GUI_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_GUI_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_GUI_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..5a9af135b0
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <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..32123ee682
--- /dev/null
+++ b/src/widgets/platforms/mac/qt_cocoa_helpers_mac.mm
@@ -0,0 +1,1824 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** 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 * /*NSImage */qt_mac_create_nsimage(const QPixmap &pm)
+{
+ QMacCocoaAutoReleasePool pool;
+ if(QCFType<CGImageRef> image = pm.toMacCGImageRef()) {
+ NSImage *newImage = 0;
+ NSRect imageRect = NSMakeRect(0.0, 0.0, CGImageGetWidth(image), CGImageGetHeight(image));
+ newImage = [[NSImage alloc] initWithSize:imageRect.size];
+ [newImage lockFocus];
+ {
+ CGContextRef imageContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
+ CGContextDrawImage(imageContext, *(CGRect*)&imageRect, image);
+ }
+ [newImage unlockFocus];
+ return newImage;
+ }
+ return 0;
+}
+
+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);
+}
+
+// Use this method to keep all the information in the TextSegment. As long as it is ordered
+// we are in OK shape, and we can influence that ourselves.
+struct KeyPair
+{
+ QChar cocoaKey;
+ Qt::Key qtKey;
+};
+
+bool operator==(const KeyPair &entry, QChar qchar)
+{
+ return entry.cocoaKey == qchar;
+}
+
+bool operator<(const KeyPair &entry, QChar qchar)
+{
+ return entry.cocoaKey < qchar;
+}
+
+bool operator<(QChar qchar, const KeyPair &entry)
+{
+ return qchar < entry.cocoaKey;
+}
+
+bool operator<(const Qt::Key &key, const KeyPair &entry)
+{
+ return key < entry.qtKey;
+}
+
+bool operator<(const KeyPair &entry, const Qt::Key &key)
+{
+ return entry.qtKey < key;
+}
+
+static bool qtKey2CocoaKeySortLessThan(const KeyPair &entry1, const KeyPair &entry2)
+{
+ return entry1.qtKey < entry2.qtKey;
+}
+
+static const int NumEntries = 59;
+static const KeyPair entries[NumEntries] = {
+ { NSEnterCharacter, Qt::Key_Enter },
+ { NSBackspaceCharacter, Qt::Key_Backspace },
+ { NSTabCharacter, Qt::Key_Tab },
+ { NSNewlineCharacter, Qt::Key_Return },
+ { NSCarriageReturnCharacter, Qt::Key_Return },
+ { NSBackTabCharacter, Qt::Key_Backtab },
+ { kEscapeCharCode, Qt::Key_Escape },
+ // Cocoa sends us delete when pressing backspace!
+ // (NB when we reverse this list in qtKey2CocoaKey, there
+ // will be two indices of Qt::Key_Backspace. But is seems to work
+ // ok for menu shortcuts (which uses that function):
+ { NSDeleteCharacter, Qt::Key_Backspace },
+ { NSUpArrowFunctionKey, Qt::Key_Up },
+ { NSDownArrowFunctionKey, Qt::Key_Down },
+ { NSLeftArrowFunctionKey, Qt::Key_Left },
+ { NSRightArrowFunctionKey, Qt::Key_Right },
+ { NSF1FunctionKey, Qt::Key_F1 },
+ { NSF2FunctionKey, Qt::Key_F2 },
+ { NSF3FunctionKey, Qt::Key_F3 },
+ { NSF4FunctionKey, Qt::Key_F4 },
+ { NSF5FunctionKey, Qt::Key_F5 },
+ { NSF6FunctionKey, Qt::Key_F6 },
+ { NSF7FunctionKey, Qt::Key_F7 },
+ { NSF8FunctionKey, Qt::Key_F8 },
+ { NSF9FunctionKey, Qt::Key_F8 },
+ { NSF10FunctionKey, Qt::Key_F10 },
+ { NSF11FunctionKey, Qt::Key_F11 },
+ { NSF12FunctionKey, Qt::Key_F12 },
+ { NSF13FunctionKey, Qt::Key_F13 },
+ { NSF14FunctionKey, Qt::Key_F14 },
+ { NSF15FunctionKey, Qt::Key_F15 },
+ { NSF16FunctionKey, Qt::Key_F16 },
+ { NSF17FunctionKey, Qt::Key_F17 },
+ { NSF18FunctionKey, Qt::Key_F18 },
+ { NSF19FunctionKey, Qt::Key_F19 },
+ { NSF20FunctionKey, Qt::Key_F20 },
+ { NSF21FunctionKey, Qt::Key_F21 },
+ { NSF22FunctionKey, Qt::Key_F22 },
+ { NSF23FunctionKey, Qt::Key_F23 },
+ { NSF24FunctionKey, Qt::Key_F24 },
+ { NSF25FunctionKey, Qt::Key_F25 },
+ { NSF26FunctionKey, Qt::Key_F26 },
+ { NSF27FunctionKey, Qt::Key_F27 },
+ { NSF28FunctionKey, Qt::Key_F28 },
+ { NSF29FunctionKey, Qt::Key_F29 },
+ { NSF30FunctionKey, Qt::Key_F30 },
+ { NSF31FunctionKey, Qt::Key_F31 },
+ { NSF32FunctionKey, Qt::Key_F32 },
+ { NSF33FunctionKey, Qt::Key_F33 },
+ { NSF34FunctionKey, Qt::Key_F34 },
+ { NSF35FunctionKey, Qt::Key_F35 },
+ { NSInsertFunctionKey, Qt::Key_Insert },
+ { NSDeleteFunctionKey, Qt::Key_Delete },
+ { NSHomeFunctionKey, Qt::Key_Home },
+ { NSEndFunctionKey, Qt::Key_End },
+ { NSPageUpFunctionKey, Qt::Key_PageUp },
+ { NSPageDownFunctionKey, Qt::Key_PageDown },
+ { NSPrintScreenFunctionKey, Qt::Key_Print },
+ { NSScrollLockFunctionKey, Qt::Key_ScrollLock },
+ { NSPauseFunctionKey, Qt::Key_Pause },
+ { NSSysReqFunctionKey, Qt::Key_SysReq },
+ { NSMenuFunctionKey, Qt::Key_Menu },
+ { NSHelpFunctionKey, Qt::Key_Help },
+};
+static const KeyPair * const end = entries + NumEntries;
+
+QChar qtKey2CocoaKey(Qt::Key key)
+{
+ // The first time this function is called, create a reverse
+ // looup table sorted on Qt Key rather than Cocoa key:
+ static QVector<KeyPair> rev_entries(NumEntries);
+ static bool mustInit = true;
+ if (mustInit){
+ mustInit = false;
+ for (int i=0; i<NumEntries; ++i)
+ rev_entries[i] = entries[i];
+ qSort(rev_entries.begin(), rev_entries.end(), qtKey2CocoaKeySortLessThan);
+ }
+ const QVector<KeyPair>::iterator i
+ = qBinaryFind(rev_entries.begin(), rev_entries.end(), key);
+ if (i == rev_entries.end())
+ return QChar();
+ return i->cocoaKey;
+}
+
+#ifdef QT_MAC_USE_COCOA
+static Qt::Key cocoaKey2QtKey(QChar keyCode)
+{
+ const KeyPair *i = qBinaryFind(entries, end, keyCode);
+ if (i == end)
+ return Qt::Key(keyCode.unicode());
+ return i->qtKey;
+}
+
+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;
+}
+
+NSString *qt_mac_removePrivateUnicode(NSString* string)
+{
+ int len = [string length];
+ if (len) {
+ QVarLengthArray <unichar, 10> characters(len);
+ bool changed = false;
+ for (int i = 0; i<len; i++) {
+ characters[i] = [string characterAtIndex:i];
+ // check if they belong to key codes in private unicode range
+ // currently we need to handle only the NSDeleteFunctionKey
+ if (characters[i] == NSDeleteFunctionKey) {
+ characters[i] = NSDeleteCharacter;
+ changed = true;
+ }
+ }
+ if (changed)
+ return [NSString stringWithCharacters:characters.data() length:len];
+ }
+ return string;
+}
+
+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
+ }
+}
+
+#ifdef QT_MAC_USE_COCOA
+void qt_mac_menu_collapseSeparators(void */*NSMenu **/ theMenu, bool collapse)
+{
+ QMacCocoaAutoReleasePool pool;
+ OSMenuRef menu = static_cast<OSMenuRef>(theMenu);
+ if (collapse) {
+ bool previousIsSeparator = true; // setting to true kills all the separators placed at the top.
+ NSMenuItem *previousItem = nil;
+
+ NSArray *itemArray = [menu itemArray];
+ for (unsigned int i = 0; i < [itemArray count]; ++i) {
+ NSMenuItem *item = reinterpret_cast<NSMenuItem *>([itemArray objectAtIndex:i]);
+ if ([item isSeparatorItem]) {
+ [item setHidden:previousIsSeparator];
+ }
+
+ if (![item isHidden]) {
+ previousItem = item;
+ previousIsSeparator = ([previousItem isSeparatorItem]);
+ }
+ }
+
+ // We now need to check the final item since we don't want any separators at the end of the list.
+ if (previousItem && previousIsSeparator)
+ [previousItem setHidden:YES];
+ } else {
+ NSArray *itemArray = [menu itemArray];
+ for (unsigned int i = 0; i < [itemArray count]; ++i) {
+ NSMenuItem *item = reinterpret_cast<NSMenuItem *>([itemArray objectAtIndex:i]);
+ if (QAction *action = reinterpret_cast<QAction *>([item tag]))
+ [item setHidden:!action->isVisible()];
+ }
+ }
+}
+
+class CocoaPostMessageAfterEventLoopExitHelp : public QObject
+{
+ id target;
+ SEL selector;
+ int argCount;
+ id arg1;
+ id arg2;
+public:
+ CocoaPostMessageAfterEventLoopExitHelp(id target, SEL selector, int argCount, id arg1, id arg2)
+ : target(target), selector(selector), argCount(argCount), arg1(arg1), arg2(arg2){
+ deleteLater();
+ }
+
+ ~CocoaPostMessageAfterEventLoopExitHelp()
+ {
+ qt_cocoaPostMessage(target, selector, argCount, arg1, arg2);
+ }
+};
+
+void qt_cocoaPostMessage(id target, SEL selector, int argCount, id arg1, id arg2)
+{
+ // WARNING: data1 and data2 is truncated to from 64-bit to 32-bit on OS 10.5!
+ // That is why we need to split the address in two parts:
+ QCocoaPostMessageArgs *args = new QCocoaPostMessageArgs(target, selector, argCount, arg1, arg2);
+ quint32 lower = quintptr(args);
+ quint32 upper = quintptr(args) >> 32;
+ NSEvent *e = [NSEvent otherEventWithType:NSApplicationDefined
+ location:NSZeroPoint modifierFlags:0 timestamp:0 windowNumber:0
+ context:nil subtype:QtCocoaEventSubTypePostMessage data1:lower data2:upper];
+ [NSApp postEvent:e atStart:NO];
+}
+
+void qt_cocoaPostMessageAfterEventLoopExit(id target, SEL selector, int argCount, id arg1, id arg2)
+{
+ if (QApplicationPrivate::instance()->threadData->eventLoops.size() <= 1)
+ qt_cocoaPostMessage(target, selector, argCount, arg1, arg2);
+ else
+ new CocoaPostMessageAfterEventLoopExitHelp(target, selector, argCount, arg1, arg2);
+}
+
+#endif
+
+QMacCocoaAutoReleasePool::QMacCocoaAutoReleasePool()
+{
+#ifndef QT_MAC_USE_COCOA
+ NSApplicationLoad();
+#endif
+ pool = (void*)[[NSAutoreleasePool alloc] init];
+}
+
+QMacCocoaAutoReleasePool::~QMacCocoaAutoReleasePool()
+{
+ [(NSAutoreleasePool*)pool release];
+}
+
+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..a49753ae2f
--- /dev/null
+++ b/src/widgets/platforms/mac/qt_cocoa_helpers_mac_p.h
@@ -0,0 +1,340 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** 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 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
+
+QT_BEGIN_NAMESPACE
+
+enum {
+ QtCocoaEventSubTypeWakeup = SHRT_MAX,
+ QtCocoaEventSubTypePostMessage = SHRT_MAX-1
+};
+
+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);
+#ifdef QT_MAC_USE_COCOA
+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);
+void qt_mac_menu_collapseSeparators(void * /*NSMenu */ menu, bool collapse);
+#endif
+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);
+
+#if QT_MAC_USE_COCOA && __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]; }
+
+#ifdef QT_MAC_USE_COCOA
+class QCocoaPostMessageArgs {
+public:
+ id target;
+ SEL selector;
+ int argCount;
+ id arg1;
+ id arg2;
+ QCocoaPostMessageArgs(id target, SEL selector, int argCount=0, id arg1=0, id arg2=0)
+ : target(target), selector(selector), argCount(argCount), arg1(arg1), arg2(arg2)
+ {
+ [target retain];
+ [arg1 retain];
+ [arg2 retain];
+ }
+
+ ~QCocoaPostMessageArgs()
+ {
+ [arg2 release];
+ [arg1 release];
+ [target release];
+ }
+};
+void qt_cocoaPostMessage(id target, SEL selector, int argCount=0, id arg1=0, id arg2=0);
+void qt_cocoaPostMessageAfterEventLoopExit(id target, SEL selector, int argCount=0, id arg1=0, id arg2=0);
+#endif
+
+#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();
+
+#ifdef QT_MAC_USE_COCOA
+void qt_mac_display(QWidget *widget);
+void qt_mac_setNeedsDisplay(QWidget *widget);
+void qt_mac_setNeedsDisplayInRect(QWidget *widget, QRegion region);
+#endif // QT_MAC_USE_COCOA
+
+
+// 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..046bcf6a54
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <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..b2bb804ff0
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef 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_GUI_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_GUI_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..2c6e579b45
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "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..354f05ba10
--- /dev/null
+++ b/src/widgets/platforms/mac/qwidget_mac.mm
@@ -0,0 +1,5420 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** 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_repaintParentUnderAlienWidget(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->repaint(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_repaintParentUnderAlienWidget(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. Repaint q instead:
+ q->repaint();
+ }
+#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_repaintParentUnderAlienWidget(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();
+ 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();
+ // Apply size restrictions, applicable for Windows & Widgets.
+ if (QWExtra *extra = extraData()) {
+ w = qMin(w, extra->maxw);
+ h = qMin(h, extra->maxh);
+ w = qMax(w, extra->minw);
+ h = qMax(h, extra->minh);
+ }
+ 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;
+
+ QCocoaView *view = static_cast<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