1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the plugins of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qcocoahelpers.h"
44 #include "qcocoaautoreleasepool.h"
48 #include <qplatformscreen_qpa.h>
49 #include <private/qguiapplication_p.h>
54 // Conversion Functions
57 QStringList qt_mac_NSArrayToQStringList(void *nsarray)
60 NSArray *array = static_cast<NSArray *>(nsarray);
61 for (NSUInteger i=0; i<[array count]; ++i)
62 result << QCFString::toQString([array objectAtIndex:i]);
66 void *qt_mac_QStringListToNSMutableArrayVoid(const QStringList &list)
68 NSMutableArray *result = [NSMutableArray arrayWithCapacity:list.size()];
69 for (int i=0; i<list.size(); ++i){
70 [result addObject:reinterpret_cast<const NSString *>(QCFString::toCFStringRef(list[i]))];
75 static void drawImageReleaseData (void *info, const void *, size_t)
77 delete static_cast<QImage *>(info);
80 CGImageRef qt_mac_image_to_cgimage(const QImage &img)
83 if (img.depth() != 32)
84 image = new QImage(img.convertToFormat(QImage::Format_ARGB32_Premultiplied));
86 image = new QImage(img);
88 uint cgflags = kCGImageAlphaNone;
89 switch (image->format()) {
90 case QImage::Format_ARGB32_Premultiplied:
91 cgflags = kCGImageAlphaPremultipliedFirst;
93 case QImage::Format_ARGB32:
94 cgflags = kCGImageAlphaFirst;
96 case QImage::Format_RGB32:
97 cgflags = kCGImageAlphaNoneSkipFirst;
101 cgflags |= kCGBitmapByteOrder32Host;
102 QCFType<CGDataProviderRef> dataProvider = CGDataProviderCreateWithData(image,
103 static_cast<const QImage *>(image)->bits(),
105 drawImageReleaseData);
107 return CGImageCreate(image->width(), image->height(), 8, 32,
108 image->bytesPerLine(),
109 qt_mac_genericColorSpace(),
110 cgflags, dataProvider, 0, false, kCGRenderingIntentDefault);
114 NSImage *qt_mac_cgimage_to_nsimage(CGImageRef image)
116 QCocoaAutoReleasePool pool;
117 NSImage *newImage = 0;
118 NSRect imageRect = NSMakeRect(0.0, 0.0, CGImageGetWidth(image), CGImageGetHeight(image));
119 newImage = [[NSImage alloc] initWithSize:imageRect.size];
120 [newImage lockFocus];
122 CGContextRef imageContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
123 CGContextDrawImage(imageContext, *(CGRect*)&imageRect, image);
125 [newImage unlockFocus];
129 NSImage *qt_mac_create_nsimage(const QPixmap &pm)
131 QImage image = pm.toImage();
132 return qt_mac_cgimage_to_nsimage(qt_mac_image_to_cgimage(image));
135 HIMutableShapeRef qt_mac_QRegionToHIMutableShape(const QRegion ®ion)
137 HIMutableShapeRef shape = HIShapeCreateMutable();
138 QVector<QRect> rects = region.rects();
139 if (!rects.isEmpty()) {
140 int n = rects.count();
141 const QRect *qt_r = rects.constData();
143 CGRect cgRect = CGRectMake(qt_r->x(), qt_r->y(), qt_r->width(), qt_r->height());
144 HIShapeUnionWithRect(shape, &cgRect);
151 NSSize qt_mac_toNSSize(const QSize &qtSize)
153 return NSMakeSize(qtSize.width(), qtSize.height());
156 QColor qt_mac_toQColor(const NSColor *color)
159 NSString *colorSpace = [color colorSpaceName];
160 if (colorSpace == NSDeviceCMYKColorSpace) {
161 CGFloat cyan, magenta, yellow, black, alpha;
162 [color getCyan:&cyan magenta:&magenta yellow:&yellow black:&black alpha:&alpha];
163 qtColor.setCmykF(cyan, magenta, yellow, black, alpha);
166 tmpColor = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace];
167 CGFloat red, green, blue, alpha;
168 [tmpColor getRed:&red green:&green blue:&blue alpha:&alpha];
169 qtColor.setRgbF(red, green, blue, alpha);
175 // Use this method to keep all the information in the TextSegment. As long as it is ordered
176 // we are in OK shape, and we can influence that ourselves.
183 bool operator==(const KeyPair &entry, QChar qchar)
185 return entry.cocoaKey == qchar;
188 bool operator<(const KeyPair &entry, QChar qchar)
190 return entry.cocoaKey < qchar;
193 bool operator<(QChar qchar, const KeyPair &entry)
195 return qchar < entry.cocoaKey;
198 bool operator<(const Qt::Key &key, const KeyPair &entry)
200 return key < entry.qtKey;
203 bool operator<(const KeyPair &entry, const Qt::Key &key)
205 return entry.qtKey < key;
208 static bool qtKey2CocoaKeySortLessThan(const KeyPair &entry1, const KeyPair &entry2)
210 return entry1.qtKey < entry2.qtKey;
213 static const int NumEntries = 59;
214 static const KeyPair entries[NumEntries] = {
215 { NSEnterCharacter, Qt::Key_Enter },
216 { NSBackspaceCharacter, Qt::Key_Backspace },
217 { NSTabCharacter, Qt::Key_Tab },
218 { NSNewlineCharacter, Qt::Key_Return },
219 { NSCarriageReturnCharacter, Qt::Key_Return },
220 { NSBackTabCharacter, Qt::Key_Backtab },
221 { kEscapeCharCode, Qt::Key_Escape },
222 // Cocoa sends us delete when pressing backspace!
223 // (NB when we reverse this list in qtKey2CocoaKey, there
224 // will be two indices of Qt::Key_Backspace. But is seems to work
225 // ok for menu shortcuts (which uses that function):
226 { NSDeleteCharacter, Qt::Key_Backspace },
227 { NSUpArrowFunctionKey, Qt::Key_Up },
228 { NSDownArrowFunctionKey, Qt::Key_Down },
229 { NSLeftArrowFunctionKey, Qt::Key_Left },
230 { NSRightArrowFunctionKey, Qt::Key_Right },
231 { NSF1FunctionKey, Qt::Key_F1 },
232 { NSF2FunctionKey, Qt::Key_F2 },
233 { NSF3FunctionKey, Qt::Key_F3 },
234 { NSF4FunctionKey, Qt::Key_F4 },
235 { NSF5FunctionKey, Qt::Key_F5 },
236 { NSF6FunctionKey, Qt::Key_F6 },
237 { NSF7FunctionKey, Qt::Key_F7 },
238 { NSF8FunctionKey, Qt::Key_F8 },
239 { NSF9FunctionKey, Qt::Key_F8 },
240 { NSF10FunctionKey, Qt::Key_F10 },
241 { NSF11FunctionKey, Qt::Key_F11 },
242 { NSF12FunctionKey, Qt::Key_F12 },
243 { NSF13FunctionKey, Qt::Key_F13 },
244 { NSF14FunctionKey, Qt::Key_F14 },
245 { NSF15FunctionKey, Qt::Key_F15 },
246 { NSF16FunctionKey, Qt::Key_F16 },
247 { NSF17FunctionKey, Qt::Key_F17 },
248 { NSF18FunctionKey, Qt::Key_F18 },
249 { NSF19FunctionKey, Qt::Key_F19 },
250 { NSF20FunctionKey, Qt::Key_F20 },
251 { NSF21FunctionKey, Qt::Key_F21 },
252 { NSF22FunctionKey, Qt::Key_F22 },
253 { NSF23FunctionKey, Qt::Key_F23 },
254 { NSF24FunctionKey, Qt::Key_F24 },
255 { NSF25FunctionKey, Qt::Key_F25 },
256 { NSF26FunctionKey, Qt::Key_F26 },
257 { NSF27FunctionKey, Qt::Key_F27 },
258 { NSF28FunctionKey, Qt::Key_F28 },
259 { NSF29FunctionKey, Qt::Key_F29 },
260 { NSF30FunctionKey, Qt::Key_F30 },
261 { NSF31FunctionKey, Qt::Key_F31 },
262 { NSF32FunctionKey, Qt::Key_F32 },
263 { NSF33FunctionKey, Qt::Key_F33 },
264 { NSF34FunctionKey, Qt::Key_F34 },
265 { NSF35FunctionKey, Qt::Key_F35 },
266 { NSInsertFunctionKey, Qt::Key_Insert },
267 { NSDeleteFunctionKey, Qt::Key_Delete },
268 { NSHomeFunctionKey, Qt::Key_Home },
269 { NSEndFunctionKey, Qt::Key_End },
270 { NSPageUpFunctionKey, Qt::Key_PageUp },
271 { NSPageDownFunctionKey, Qt::Key_PageDown },
272 { NSPrintScreenFunctionKey, Qt::Key_Print },
273 { NSScrollLockFunctionKey, Qt::Key_ScrollLock },
274 { NSPauseFunctionKey, Qt::Key_Pause },
275 { NSSysReqFunctionKey, Qt::Key_SysReq },
276 { NSMenuFunctionKey, Qt::Key_Menu },
277 { NSHelpFunctionKey, Qt::Key_Help },
279 static const KeyPair * const end = entries + NumEntries;
281 QChar qt_mac_qtKey2CocoaKey(Qt::Key key)
283 // The first time this function is called, create a reverse
284 // lookup table sorted on Qt Key rather than Cocoa key:
285 static QVector<KeyPair> rev_entries(NumEntries);
286 static bool mustInit = true;
289 for (int i=0; i<NumEntries; ++i)
290 rev_entries[i] = entries[i];
291 qSort(rev_entries.begin(), rev_entries.end(), qtKey2CocoaKeySortLessThan);
293 const QVector<KeyPair>::iterator i
294 = qBinaryFind(rev_entries.begin(), rev_entries.end(), key);
295 if (i == rev_entries.end())
300 Qt::Key qt_mac_cocoaKey2QtKey(QChar keyCode)
302 const KeyPair *i = qBinaryFind(entries, end, keyCode);
304 return Qt::Key(keyCode.toUpper().unicode());
308 struct dndenum_mapper
310 NSDragOperation mac_code;
311 Qt::DropAction qt_code;
315 static dndenum_mapper dnd_enums[] = {
316 { NSDragOperationLink, Qt::LinkAction, true },
317 { NSDragOperationMove, Qt::MoveAction, true },
318 { NSDragOperationCopy, Qt::CopyAction, true },
319 { NSDragOperationGeneric, Qt::CopyAction, false },
320 { NSDragOperationEvery, Qt::ActionMask, false },
321 { NSDragOperationNone, Qt::IgnoreAction, false }
324 NSDragOperation qt_mac_mapDropAction(Qt::DropAction action)
326 for (int i=0; dnd_enums[i].qt_code; i++) {
327 if (dnd_enums[i].Qt2Mac && (action & dnd_enums[i].qt_code)) {
328 return dnd_enums[i].mac_code;
331 return NSDragOperationNone;
334 NSDragOperation qt_mac_mapDropActions(Qt::DropActions actions)
336 NSDragOperation nsActions = NSDragOperationNone;
337 for (int i=0; dnd_enums[i].qt_code; i++) {
338 if (dnd_enums[i].Qt2Mac && (actions & dnd_enums[i].qt_code))
339 nsActions |= dnd_enums[i].mac_code;
344 Qt::DropAction qt_mac_mapNSDragOperation(NSDragOperation nsActions)
346 Qt::DropAction action = Qt::IgnoreAction;
347 for (int i=0; dnd_enums[i].mac_code; i++) {
348 if (nsActions & dnd_enums[i].mac_code)
349 return dnd_enums[i].qt_code;
354 Qt::DropActions qt_mac_mapNSDragOperations(NSDragOperation nsActions)
356 Qt::DropActions actions = Qt::IgnoreAction;
357 for (int i=0; dnd_enums[i].mac_code; i++) {
358 if (nsActions & dnd_enums[i].mac_code)
359 actions |= dnd_enums[i].qt_code;
370 // Changes the process type for this process to kProcessTransformToForegroundApplication,
371 // unless either LSUIElement or LSBackgroundOnly is set in the Info.plist.
372 void qt_mac_transformProccessToForegroundApplication()
374 ProcessSerialNumber psn;
375 if (GetCurrentProcess(&psn) == noErr) {
376 bool forceTransform = true;
377 CFTypeRef value = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(),
378 CFSTR("LSUIElement"));
380 CFTypeID valueType = CFGetTypeID(value);
381 // Officially it's supposed to be a string, a boolean makes sense, so we'll check.
382 // A number less so, but OK.
383 if (valueType == CFStringGetTypeID())
384 forceTransform = !(QCFString::toQString(static_cast<CFStringRef>(value)).toInt());
385 else if (valueType == CFBooleanGetTypeID())
386 forceTransform = !CFBooleanGetValue(static_cast<CFBooleanRef>(value));
387 else if (valueType == CFNumberGetTypeID()) {
389 CFNumberGetValue(static_cast<CFNumberRef>(value), kCFNumberIntType, &valueAsInt);
390 forceTransform = !valueAsInt;
394 if (forceTransform) {
395 value = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(),
396 CFSTR("LSBackgroundOnly"));
398 CFTypeID valueType = CFGetTypeID(value);
399 if (valueType == CFBooleanGetTypeID())
400 forceTransform = !CFBooleanGetValue(static_cast<CFBooleanRef>(value));
401 else if (valueType == CFStringGetTypeID())
402 forceTransform = !(QCFString::toQString(static_cast<CFStringRef>(value)).toInt());
403 else if (valueType == CFNumberGetTypeID()) {
405 CFNumberGetValue(static_cast<CFNumberRef>(value), kCFNumberIntType, &valueAsInt);
406 forceTransform = !valueAsInt;
411 if (forceTransform) {
412 TransformProcessType(&psn, kProcessTransformToForegroundApplication);
417 QString qt_mac_removeMnemonics(const QString &original)
419 QString returnText(original.size(), 0);
422 int l = original.length();
424 if (original.at(currPos) == QLatin1Char('&')
425 && (l == 1 || original.at(currPos + 1) != QLatin1Char('&'))) {
431 returnText[finalDest] = original.at(currPos);
436 returnText.truncate(finalDest);
441 CGColorSpaceRef m_genericColorSpace = 0;
442 QHash<CGDirectDisplayID, CGColorSpaceRef> m_displayColorSpaceHash;
443 bool m_postRoutineRegistered = false;
445 CGColorSpaceRef qt_mac_genericColorSpace()
448 if (!m_genericColorSpace) {
449 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
450 if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
451 m_genericColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
455 m_genericColorSpace = CGColorSpaceCreateDeviceRGB();
457 if (!m_postRoutineRegistered) {
458 m_postRoutineRegistered = true;
459 qAddPostRoutine(QCoreGraphicsPaintEngine::cleanUpMacColorSpaces);
462 return m_genericColorSpace;
464 // Just return the main display colorspace for the moment.
465 return qt_mac_displayColorSpace(0);
470 Ideally, we should pass the widget in here, and use CGGetDisplaysWithRect() etc.
471 to support multiple displays correctly.
473 CGColorSpaceRef qt_mac_displayColorSpace(const QWidget *widget)
475 CGColorSpaceRef colorSpace;
477 CGDirectDisplayID displayID;
478 CMProfileRef displayProfile = 0;
480 displayID = CGMainDisplayID();
482 displayID = CGMainDisplayID();
484 ### get correct display
485 const QRect &qrect = widget->window()->geometry();
486 CGRect rect = CGRectMake(qrect.x(), qrect.y(), qrect.width(), qrect.height());
487 CGDisplayCount throwAway;
488 CGDisplayErr dErr = CGGetDisplaysWithRect(rect, 1, &displayID, &throwAway);
489 if (dErr != kCGErrorSuccess)
490 return macDisplayColorSpace(0); // fall back on main display
493 if ((colorSpace = m_displayColorSpaceHash.value(displayID)))
496 CMError err = CMGetProfileByAVID((CMDisplayIDType)displayID, &displayProfile);
498 colorSpace = CGColorSpaceCreateWithPlatformColorSpace(displayProfile);
500 return qt_mac_displayColorSpace(0); // fall back on main display
504 colorSpace = CGColorSpaceCreateDeviceRGB();
506 m_displayColorSpaceHash.insert(displayID, colorSpace);
507 CMCloseProfile(displayProfile);
508 if (!m_postRoutineRegistered) {
509 m_postRoutineRegistered = true;
510 void qt_mac_cleanUpMacColorSpaces();
511 qAddPostRoutine(qt_mac_cleanUpMacColorSpaces);
516 void qt_mac_cleanUpMacColorSpaces()
518 if (m_genericColorSpace) {
519 CFRelease(m_genericColorSpace);
520 m_genericColorSpace = 0;
522 QHash<CGDirectDisplayID, CGColorSpaceRef>::const_iterator it = m_displayColorSpaceHash.constBegin();
523 while (it != m_displayColorSpaceHash.constEnd()) {
525 CFRelease(it.value());
528 m_displayColorSpaceHash.clear();
531 QString qt_mac_applicationName()
534 CFTypeRef string = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(), CFSTR("CFBundleName"));
536 appName = QCFString::toQString(static_cast<CFStringRef>(string));
538 if (appName.isEmpty()) {
539 QString arg0 = QGuiApplicationPrivate::instance()->appName();
540 if (arg0.contains("/")) {
541 QStringList parts = arg0.split("/");
542 appName = parts.at(parts.count() - 1);
550 NSRect qt_mac_flipRect(const QRect &rect, QWindow *window)
552 QPlatformScreen *onScreen = QPlatformScreen::platformScreenForWindow(window);
553 int flippedY = onScreen->geometry().height() - rect.y() - rect.height();
554 return NSMakeRect(rect.x(), flippedY, rect.width(), rect.height());
557 OSStatus qt_mac_drawCGImage(CGContextRef inContext, const CGRect *inBounds, CGImageRef inImage)
559 // Verbatim copy if HIViewDrawCGImage (as shown on Carbon-Dev)
560 OSStatus err = noErr;
562 require_action(inContext != NULL, InvalidContext, err = paramErr);
563 require_action(inBounds != NULL, InvalidBounds, err = paramErr);
564 require_action(inImage != NULL, InvalidImage, err = paramErr);
566 CGContextSaveGState( inContext );
567 CGContextTranslateCTM (inContext, 0, inBounds->origin.y + CGRectGetMaxY(*inBounds));
568 CGContextScaleCTM(inContext, 1, -1);
570 CGContextDrawImage(inContext, *inBounds, inImage);
572 CGContextRestoreGState(inContext);
579 CGFloat qt_mac_get_scalefactor()
581 return [[NSScreen mainScreen] userSpaceScaleFactor];