1
/* $Id: VBoxUtils-darwin.cpp 35424 2011-01-07 13:05:41Z vboxsync $ */
3
* Qt GUI - Utility Classes and Functions specific to Darwin.
7
* Copyright (C) 2006-2010 Oracle Corporation
9
* This file is part of VirtualBox Open Source Edition (OSE), as
10
* available from http://www.virtualbox.org. This file is free software;
11
* you can redistribute it and/or modify it under the terms of the GNU
12
* General Public License (GPL) as published by the Free Software
13
* Foundation, in version 2 as it comes in the "COPYING" file of the
14
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18
#include "VBoxUtils-darwin.h"
19
#include "VBoxCocoaHelper.h"
20
#include "UICocoaApplication.h"
24
#include <QMainWindow>
25
#include <QApplication>
30
#include <QContextMenuEvent>
32
#include <Carbon/Carbon.h>
34
#if QT_VERSION < 0x040400
35
extern void qt_mac_set_menubar_icons(bool b);
36
#endif /* QT_VERSION < 0x040400 */
38
NativeNSViewRef darwinToNativeView(QWidget *pWidget)
41
return reinterpret_cast<NativeNSViewRef>(pWidget->winId());
45
NativeNSWindowRef darwinToNativeWindow(QWidget *pWidget)
48
return ::darwinToNativeWindowImpl(::darwinToNativeView(pWidget));
52
NativeNSWindowRef darwinToNativeWindow(NativeNSViewRef aView)
54
return ::darwinToNativeWindowImpl(aView);
57
NativeNSViewRef darwinToNativeView(NativeNSWindowRef aWindow)
59
return ::darwinToNativeViewImpl(aWindow);
62
void darwinSetShowsToolbarButton(QToolBar *aToolBar, bool fEnabled)
64
QWidget *parent = aToolBar->parentWidget();
66
::darwinSetShowsToolbarButtonImpl(::darwinToNativeWindow(parent), fEnabled);
69
void darwinLabelWindow(QWidget *pWidget, QPixmap *pPixmap, bool fCenter)
71
::darwinLabelWindow(::darwinToNativeWindow(pWidget), ::darwinToNSImageRef(pPixmap), fCenter);
74
void darwinSetHidesAllTitleButtons(QWidget *pWidget)
76
#ifdef QT_MAC_USE_COCOA
77
/* Currently only necessary in the Cocoa version */
78
::darwinSetHidesAllTitleButtonsImpl(::darwinToNativeWindow(pWidget));
79
#else /* QT_MAC_USE_COCOA */
81
#endif /* !QT_MAC_USE_COCOA */
84
void darwinSetShowsWindowTransparent(QWidget *pWidget, bool fEnabled)
86
::darwinSetShowsWindowTransparentImpl(::darwinToNativeWindow(pWidget), fEnabled);
89
void darwinWindowAnimateResize(QWidget *pWidget, const QRect &aTarget)
91
::darwinWindowAnimateResizeImpl(::darwinToNativeWindow(pWidget), aTarget.x(), aTarget.y(), aTarget.width(), aTarget.height());
94
void darwinWindowAnimateResizeNew(QWidget *pWidget, int h, bool fAnimate)
96
::darwinWindowAnimateResizeNewImpl(::darwinToNativeWindow(pWidget), h, fAnimate);
99
void darwinTest(QWidget *pWidget1, QWidget *pWidget2, int h)
101
::darwinTest(::darwinToNativeView(pWidget1), ::darwinToNativeView(pWidget2), h);
104
void darwinWindowInvalidateShape(QWidget *pWidget)
106
#ifdef QT_MAC_USE_COCOA
107
/* Here a simple update is enough! */
109
#else /* QT_MAC_USE_COCOA */
110
::darwinWindowInvalidateShapeImpl(::darwinToNativeWindow(pWidget));
111
#endif /* QT_MAC_USE_COCOA */
114
void darwinWindowInvalidateShadow(QWidget *pWidget)
116
#ifdef QT_MAC_USE_COCOA
117
::darwinWindowInvalidateShadowImpl(::darwinToNativeWindow(pWidget));
118
#else /* QT_MAC_USE_COCOA */
120
#endif /* QT_MAC_USE_COCOA */
123
void darwinSetShowsResizeIndicator(QWidget *pWidget, bool fEnabled)
125
::darwinSetShowsResizeIndicatorImpl(::darwinToNativeWindow(pWidget), fEnabled);
128
bool darwinIsWindowMaximized(QWidget *pWidget)
130
#ifdef QT_MAC_USE_COCOA
131
/* Currently only necessary in the Cocoa version */
132
return ::darwinIsWindowMaximized(::darwinToNativeWindow(pWidget));
133
#else /* QT_MAC_USE_COCOA */
136
#endif /* !QT_MAC_USE_COCOA */
139
void darwinMinaturizeWindow(QWidget *pWidget)
141
return ::darwinMinaturizeWindow(::darwinToNativeWindow(pWidget));
144
bool darwinOpenFile(const QString& strFile)
146
return ::darwinOpenFile(darwinToNativeString(strFile.toUtf8().constData()));
149
QString darwinSystemLanguage(void)
151
/* Get the locales supported by our bundle */
152
CFArrayRef supportedLocales = ::CFBundleCopyBundleLocalizations(::CFBundleGetMainBundle());
153
/* Check them against the languages currently selected by the user */
154
CFArrayRef preferredLocales = ::CFBundleCopyPreferredLocalizationsFromArray(supportedLocales);
155
/* Get the one which is on top */
156
CFStringRef localeId = (CFStringRef)::CFArrayGetValueAtIndex(preferredLocales, 0);
157
/* Convert them to a C-string */
159
::CFStringGetCString(localeId, localeName, sizeof(localeName), kCFStringEncodingUTF8);
161
::CFRelease(supportedLocales);
162
::CFRelease(preferredLocales);
163
QString id(localeName);
164
/* Check for some misbehavior */
166
id.toLower() == "english")
171
void darwinDisableIconsInMenus(void)
173
/* No icons in the menu of a mac application. */
174
#if QT_VERSION < 0x040400
175
qt_mac_set_menubar_icons(false);
176
#else /* QT_VERSION < 0x040400 */
177
/* Available since Qt 4.4 only */
178
QApplication::instance()->setAttribute(Qt::AA_DontShowIconsInMenus, true);
179
#endif /* QT_VERSION >= 0x040400 */
182
int darwinWindowToolBarHeight(QWidget *pWidget)
184
#ifndef QT_MAC_USE_COCOA
185
return ::darwinWindowToolBarHeight(::darwinToNativeWindow(pWidget));
186
#else /* QT_MAC_USE_COCOA */
189
#endif /* QT_MAC_USE_COCOA */
192
bool darwinIsToolbarVisible(QToolBar *pToolBar)
194
bool fResult = false;
195
QWidget *pParent = pToolBar->parentWidget();
197
fResult = ::darwinIsToolbarVisible(::darwinToNativeWindow(pParent));
202
bool darwinSetFrontMostProcess()
204
ProcessSerialNumber psn = { 0, kCurrentProcess };
205
return ::SetFrontProcess(&psn) == 0;
208
uint64_t darwinGetCurrentProcessId()
210
uint64_t processId = 0;
211
ProcessSerialNumber psn = { 0, kCurrentProcess };
212
if (::GetCurrentProcess(&psn) == 0)
213
processId = RT_MAKE_U64(psn.lowLongOfPSN, psn.highLongOfPSN);
217
CGContextRef darwinToCGContextRef(QWidget *pWidget)
219
return static_cast<CGContext *>(pWidget->macCGHandle());
222
/* Proxy icon creation */
223
QPixmap darwinCreateDragPixmap(const QPixmap& aPixmap, const QString &aText)
225
QFontMetrics fm(qApp->font());
226
QRect tbRect = fm.boundingRect(aText);
227
const int h = qMax(aPixmap.height(), fm.ascent() + 1);
229
QPixmap dragPixmap(aPixmap.width() + tbRect.width() + m, h);
230
dragPixmap.fill(Qt::transparent);
231
QPainter painter(&dragPixmap);
232
painter.drawPixmap(0, qAbs(h - aPixmap.height()) / 2.0, aPixmap);
233
painter.setPen(Qt::white);
234
painter.drawText(QRect(aPixmap.width() + m, 1, tbRect.width(), h - 1), Qt::AlignLeft | Qt::AlignVCenter, aText);
235
painter.setPen(Qt::black);
236
painter.drawText(QRect(aPixmap.width() + m, 0, tbRect.width(), h - 1), Qt::AlignLeft | Qt::AlignVCenter, aText);
242
* Callback for deleting the QImage object when CGImageCreate is done
243
* with it (which is probably not until the returned CFGImageRef is released).
245
* @param info Pointer to the QImage.
247
static void darwinDataProviderReleaseQImage(void *info, const void *, size_t)
249
QImage *qimg = (QImage *)info;
254
* Converts a QPixmap to a CGImage.
256
* @returns CGImageRef for the new image. (Remember to release it when finished with it.)
257
* @param aPixmap Pointer to the QPixmap instance to convert.
259
CGImageRef darwinToCGImageRef(const QImage *pImage)
261
QImage *imageCopy = new QImage(*pImage);
262
/** @todo this code assumes 32-bit image input, the lazy bird convert image to 32-bit method is anything but optimal... */
263
if (imageCopy->format() != QImage::Format_ARGB32)
264
*imageCopy = imageCopy->convertToFormat(QImage::Format_ARGB32);
265
Assert(!imageCopy->isNull());
267
CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
268
CGDataProviderRef dp = CGDataProviderCreateWithData(imageCopy, pImage->bits(), pImage->numBytes(), darwinDataProviderReleaseQImage);
270
CGBitmapInfo bmpInfo = kCGImageAlphaFirst | kCGBitmapByteOrder32Host;
271
CGImageRef ir = CGImageCreate(imageCopy->width(), imageCopy->height(), 8, 32, imageCopy->bytesPerLine(), cs,
272
bmpInfo, dp, 0 /*decode */, 0 /* shouldInterpolate */,
273
kCGRenderingIntentDefault);
274
CGColorSpaceRelease(cs);
275
CGDataProviderRelease(dp);
282
* Converts a QPixmap to a CGImage.
284
* @returns CGImageRef for the new image. (Remember to release it when finished with it.)
285
* @param aPixmap Pointer to the QPixmap instance to convert.
287
CGImageRef darwinToCGImageRef(const QPixmap *pPixmap)
289
/* It seems Qt releases the memory to an returned CGImageRef when the
290
* associated QPixmap is destroyed. This shouldn't happen as long a
291
* CGImageRef has a retrain count. As a workaround we make a real copy. */
292
int bitmapBytesPerRow = pPixmap->width() * 4;
293
int bitmapByteCount = (bitmapBytesPerRow * pPixmap->height());
294
/* Create a memory block for the temporary image. It is initialized by zero
295
* which means black & zero alpha. */
296
void *pBitmapData = RTMemAllocZ(bitmapByteCount);
297
CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
298
/* Create a context to paint on */
299
CGContextRef ctx = CGBitmapContextCreate(pBitmapData,
305
kCGImageAlphaPremultipliedFirst);
306
/* Get the CGImageRef from Qt */
307
CGImageRef qtPixmap = pPixmap->toMacCGImageRef();
308
/* Draw the image from Qt & convert the context back to a new CGImageRef. */
309
CGContextDrawImage(ctx, CGRectMake(0, 0, pPixmap->width(), pPixmap->height()), qtPixmap);
310
CGImageRef newImage = CGBitmapContextCreateImage(ctx);
311
/* Now release all used resources */
312
CGImageRelease(qtPixmap);
313
CGContextRelease(ctx);
314
CGColorSpaceRelease(cs);
315
RTMemFree(pBitmapData);
317
/* Return the new CGImageRef */
322
* Loads an image using Qt and converts it to a CGImage.
324
* @returns CGImageRef for the new image. (Remember to release it when finished with it.)
325
* @param aSource The source name.
327
CGImageRef darwinToCGImageRef(const char *pczSource)
329
QPixmap qpm(QString(":/") + pczSource);
330
Assert(!qpm.isNull());
331
return ::darwinToCGImageRef(&qpm);
334
void darwinRegisterForUnifiedToolbarContextMenuEvents(QMainWindow *pWindow)
336
UICocoaApplication::instance()->registerForNativeEvents(RT_BIT_32(3) /* NSRightMouseDown */, ::darwinUnifiedToolbarEvents, pWindow);
339
void darwinUnregisterForUnifiedToolbarContextMenuEvents(QMainWindow *pWindow)
341
UICocoaApplication::instance()->unregisterForNativeEvents(RT_BIT_32(3) /* NSRightMouseDown */, ::darwinUnifiedToolbarEvents, pWindow);
344
void darwinCreateContextMenuEvent(void *pvUser, int x, int y)
346
QWidget *pWin = static_cast<QWidget*>(pvUser);
348
QPoint local = pWin->mapFromGlobal(global);
349
qApp->postEvent(pWin, new QContextMenuEvent(QContextMenuEvent::Mouse, local, global));
352
QString darwinResolveAlias(const QString &strFile)
360
if ((err = FSPathMakeRef((const UInt8*)strFile.toUtf8().constData(), &fileRef, &fDir)) != noErr)
362
Boolean fAlias = FALSE;
363
if ((err = FSIsAliasFile(&fileRef, &fAlias, &fDir)) != noErr)
367
if ((err = FSResolveAliasFile(&fileRef, TRUE, &fAlias, &fDir)) != noErr)
370
if ((err = FSRefMakePath(&fileRef, (UInt8*)pszPath, 1024)) != noErr)
372
strTarget = QString::fromUtf8(pszPath);
381
/********************************************************************************
383
* Old carbon stuff. Have to converted soon!
385
********************************************************************************/
387
/* Event debugging stuff. Borrowed from Knuts Qt patch. */
390
# define MY_CASE(a) case a: return #a
391
const char * DarwinDebugEventName(UInt32 ekind)
396
MY_CASE(kEventWindowUpdate );
397
MY_CASE(kEventWindowDrawContent );
399
MY_CASE(kEventWindowActivated );
400
MY_CASE(kEventWindowDeactivated );
401
MY_CASE(kEventWindowHandleActivate );
402
MY_CASE(kEventWindowHandleDeactivate );
403
MY_CASE(kEventWindowGetClickActivation );
404
MY_CASE(kEventWindowGetClickModality );
405
MY_CASE(kEventWindowShowing );
406
MY_CASE(kEventWindowHiding );
407
MY_CASE(kEventWindowShown );
408
MY_CASE(kEventWindowHidden );
409
MY_CASE(kEventWindowCollapsing );
410
MY_CASE(kEventWindowCollapsed );
411
MY_CASE(kEventWindowExpanding );
412
MY_CASE(kEventWindowExpanded );
413
MY_CASE(kEventWindowZoomed );
414
MY_CASE(kEventWindowBoundsChanging );
415
MY_CASE(kEventWindowBoundsChanged );
416
MY_CASE(kEventWindowResizeStarted );
417
MY_CASE(kEventWindowResizeCompleted );
418
MY_CASE(kEventWindowDragStarted );
419
MY_CASE(kEventWindowDragCompleted );
420
MY_CASE(kEventWindowClosed );
421
MY_CASE(kEventWindowTransitionStarted );
422
MY_CASE(kEventWindowTransitionCompleted );
424
MY_CASE(kEventWindowClickDragRgn );
425
MY_CASE(kEventWindowClickResizeRgn );
426
MY_CASE(kEventWindowClickCollapseRgn );
427
MY_CASE(kEventWindowClickCloseRgn );
428
MY_CASE(kEventWindowClickZoomRgn );
429
MY_CASE(kEventWindowClickContentRgn );
430
MY_CASE(kEventWindowClickProxyIconRgn );
431
MY_CASE(kEventWindowClickToolbarButtonRgn );
432
MY_CASE(kEventWindowClickStructureRgn );
434
MY_CASE(kEventWindowCursorChange );
435
MY_CASE(kEventWindowCollapse );
436
MY_CASE(kEventWindowCollapseAll );
437
MY_CASE(kEventWindowExpand );
438
MY_CASE(kEventWindowExpandAll );
439
MY_CASE(kEventWindowClose );
440
MY_CASE(kEventWindowCloseAll );
441
MY_CASE(kEventWindowZoom );
442
MY_CASE(kEventWindowZoomAll );
443
MY_CASE(kEventWindowContextualMenuSelect );
444
MY_CASE(kEventWindowPathSelect );
445
MY_CASE(kEventWindowGetIdealSize );
446
MY_CASE(kEventWindowGetMinimumSize );
447
MY_CASE(kEventWindowGetMaximumSize );
448
MY_CASE(kEventWindowConstrain );
450
MY_CASE(kEventWindowHandleContentClick );
452
MY_CASE(kEventWindowGetDockTileMenu );
453
MY_CASE(kEventWindowProxyBeginDrag );
454
MY_CASE(kEventWindowProxyEndDrag );
455
MY_CASE(kEventWindowToolbarSwitchMode );
456
MY_CASE(kEventWindowFocusAcquired );
457
MY_CASE(kEventWindowFocusRelinquish );
458
MY_CASE(kEventWindowFocusContent );
459
MY_CASE(kEventWindowFocusToolbar );
460
MY_CASE(kEventWindowFocusDrawer );
461
MY_CASE(kEventWindowSheetOpening );
462
MY_CASE(kEventWindowSheetOpened );
463
MY_CASE(kEventWindowSheetClosing );
464
MY_CASE(kEventWindowSheetClosed );
465
MY_CASE(kEventWindowDrawerOpening );
466
MY_CASE(kEventWindowDrawerOpened );
467
MY_CASE(kEventWindowDrawerClosing );
468
MY_CASE(kEventWindowDrawerClosed );
469
MY_CASE(kEventWindowDrawFrame );
470
MY_CASE(kEventWindowDrawPart );
471
MY_CASE(kEventWindowGetRegion );
472
MY_CASE(kEventWindowHitTest );
473
MY_CASE(kEventWindowInit );
474
MY_CASE(kEventWindowDispose );
475
MY_CASE(kEventWindowDragHilite );
476
MY_CASE(kEventWindowModified );
477
MY_CASE(kEventWindowSetupProxyDragImage );
478
MY_CASE(kEventWindowStateChanged );
479
MY_CASE(kEventWindowMeasureTitle );
480
MY_CASE(kEventWindowDrawGrowBox );
481
MY_CASE(kEventWindowGetGrowImageRegion );
482
MY_CASE(kEventWindowPaint );
484
static char s_sz[64];
485
sprintf(s_sz, "kind=%u", (uint)ekind);
490
/* Convert a class into the 4 char code defined in
491
* 'Developer/Headers/CFMCarbon/CarbonEvents.h' to
492
* identify the event. */
493
const char * darwinDebugClassName(UInt32 eclass)
495
char *pclass = (char*)&eclass;
496
static char s_sz[11];
497
sprintf(s_sz, "class=%c%c%c%c", pclass[3],
504
void darwinDebugPrintEvent(const char *psz, EventRef evtRef)
508
UInt32 ekind = GetEventKind(evtRef), eclass = GetEventClass(evtRef);
509
if (eclass == kEventClassWindow)
514
case kEventWindowDrawContent:
515
case kEventWindowUpdate:
517
case kEventWindowBoundsChanged:
521
WindowRef wid = NULL;
522
GetEventParameter(evtRef, kEventParamDirectObject, typeWindowRef, NULL, sizeof(WindowRef), NULL, &wid);
523
QWidget *widget = QWidget::find((WId)wid);
524
printf("%d %s: (%s) %#x win=%p wid=%p (%s)\n", (int)time(NULL), psz, darwinDebugClassName(eclass), (uint)ekind, wid, widget, DarwinDebugEventName(ekind));
529
else if (eclass == kEventClassCommand)
531
WindowRef wid = NULL;
532
GetEventParameter(evtRef, kEventParamDirectObject, typeWindowRef, NULL, sizeof(WindowRef), NULL, &wid);
533
QWidget *widget = QWidget::find((WId)wid);
534
const char *name = "Unknown";
537
case kEventCommandProcess:
538
name = "kEventCommandProcess";
540
case kEventCommandUpdateStatus:
541
name = "kEventCommandUpdateStatus";
544
printf("%d %s: (%s) %#x win=%p wid=%p (%s)\n", (int)time(NULL), psz, darwinDebugClassName(eclass), (uint)ekind, wid, widget, name);
546
else if (eclass == kEventClassKeyboard)
548
printf("%d %s: %#x(%s) %#x (kEventClassKeyboard)", (int)time(NULL), psz, (uint)eclass, darwinDebugClassName(eclass), (uint)ekind);
551
::GetEventParameter(evtRef, kEventParamKeyCode, typeUInt32, NULL,
552
sizeof(keyCode), NULL, &keyCode);
553
printf(" keyCode=%d (%#x) ", (int)keyCode, (unsigned)keyCode);
555
char macCharCodes[8] = {0,0,0,0, 0,0,0,0};
556
::GetEventParameter(evtRef, kEventParamKeyCode, typeChar, NULL,
557
sizeof(macCharCodes), NULL, &macCharCodes[0]);
558
printf(" macCharCodes={");
559
for (unsigned i =0; i < 8 && macCharCodes[i]; i++)
560
printf( i == 0 ? "%02x" : ",%02x", macCharCodes[i]);
563
UInt32 modifierMask = 0;
564
::GetEventParameter(evtRef, kEventParamKeyModifiers, typeUInt32, NULL,
565
sizeof(modifierMask), NULL, &modifierMask);
566
printf(" modifierMask=%08x", (unsigned)modifierMask);
568
UniChar keyUnicodes[8] = {0,0,0,0, 0,0,0,0};
569
::GetEventParameter(evtRef, kEventParamKeyUnicodes, typeUnicodeText, NULL,
570
sizeof(keyUnicodes), NULL, &keyUnicodes[0]);
571
printf(" keyUnicodes={");
572
for (unsigned i =0; i < 8 && keyUnicodes[i]; i++)
573
printf( i == 0 ? "%02x" : ",%02x", keyUnicodes[i]);
576
UInt32 keyboardType = 0;
577
::GetEventParameter(evtRef, kEventParamKeyboardType, typeUInt32, NULL,
578
sizeof(keyboardType), NULL, &keyboardType);
579
printf(" keyboardType=%08x", (unsigned)keyboardType);
581
EventHotKeyID evtHotKeyId = {0,0};
582
::GetEventParameter(evtRef, typeEventHotKeyID, typeEventHotKeyID, NULL,
583
sizeof(evtHotKeyId), NULL, &evtHotKeyId);
584
printf(" evtHotKeyId={signature=%08x, .id=%08x}", (unsigned)evtHotKeyId.signature, (unsigned)evtHotKeyId.id);
588
printf("%d %s: %#x(%s) %#x\n", (int)time(NULL), psz, (uint)eclass, darwinDebugClassName(eclass), (uint)ekind);