Port QMacPrintEngine and QCoreGraphicsPaintEngine from Qt 4 to Qt 5
[profile/ivi/qtbase.git] / src / plugins / platforms / cocoa / qcocoahelpers.mm
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
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.
16 **
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.
20 **
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.
28 **
29 ** Other Usage
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.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qcocoahelpers.h"
43
44 #include "qcocoaautoreleasepool.h"
45
46 #include <QtCore>
47 #include <QtGui>
48 #include <qplatformscreen_qpa.h>
49 #include <private/qguiapplication_p.h>
50
51 QT_BEGIN_NAMESPACE
52
53 //
54 // Conversion Functions
55 //
56
57 QStringList qt_mac_NSArrayToQStringList(void *nsarray)
58 {
59     QStringList result;
60     NSArray *array = static_cast<NSArray *>(nsarray);
61     for (NSUInteger i=0; i<[array count]; ++i)
62         result << QCFString::toQString([array objectAtIndex:i]);
63     return result;
64 }
65
66 void *qt_mac_QStringListToNSMutableArrayVoid(const QStringList &list)
67 {
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]))];
71     }
72     return result;
73 }
74
75 static void drawImageReleaseData (void *info, const void *, size_t)
76 {
77     delete static_cast<QImage *>(info);
78 }
79
80 CGImageRef qt_mac_image_to_cgimage(const QImage &img)
81 {
82     QImage *image;
83     if (img.depth() != 32)
84         image = new QImage(img.convertToFormat(QImage::Format_ARGB32_Premultiplied));
85     else
86         image = new QImage(img);
87
88     uint cgflags = kCGImageAlphaNone;
89     switch (image->format()) {
90     case QImage::Format_ARGB32_Premultiplied:
91         cgflags = kCGImageAlphaPremultipliedFirst;
92         break;
93     case QImage::Format_ARGB32:
94         cgflags = kCGImageAlphaFirst;
95         break;
96     case QImage::Format_RGB32:
97         cgflags = kCGImageAlphaNoneSkipFirst;
98     default:
99         break;
100     }
101     cgflags |= kCGBitmapByteOrder32Host;
102     QCFType<CGDataProviderRef> dataProvider = CGDataProviderCreateWithData(image,
103                                                           static_cast<const QImage *>(image)->bits(),
104                                                           image->byteCount(),
105                                                           drawImageReleaseData);
106
107     return CGImageCreate(image->width(), image->height(), 8, 32,
108                                         image->bytesPerLine(),
109                                         qt_mac_genericColorSpace(),
110                                         cgflags, dataProvider, 0, false, kCGRenderingIntentDefault);
111
112 }
113
114 NSImage *qt_mac_cgimage_to_nsimage(CGImageRef image)
115 {
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];
121     {
122         CGContextRef imageContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
123         CGContextDrawImage(imageContext, *(CGRect*)&imageRect, image);
124     }
125     [newImage unlockFocus];
126     return newImage;
127 }
128
129 NSImage *qt_mac_create_nsimage(const QPixmap &pm)
130 {
131     QImage image = pm.toImage();
132     return qt_mac_cgimage_to_nsimage(qt_mac_image_to_cgimage(image));
133 }
134
135 HIMutableShapeRef qt_mac_QRegionToHIMutableShape(const QRegion &region)
136 {
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();
142         while (n--) {
143             CGRect cgRect = CGRectMake(qt_r->x(), qt_r->y(), qt_r->width(), qt_r->height());
144             HIShapeUnionWithRect(shape, &cgRect);
145             ++qt_r;
146         }
147     }
148     return shape;
149 }
150
151 NSSize qt_mac_toNSSize(const QSize &qtSize)
152 {
153     return NSMakeSize(qtSize.width(), qtSize.height());
154 }
155
156 QColor qt_mac_toQColor(const NSColor *color)
157 {
158     QColor qtColor;
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);
164     } else {
165         NSColor *tmpColor;
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);
170     }
171     return qtColor;
172 }
173
174
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.
177 struct KeyPair
178 {
179     QChar cocoaKey;
180     Qt::Key qtKey;
181 };
182
183 bool operator==(const KeyPair &entry, QChar qchar)
184 {
185     return entry.cocoaKey == qchar;
186 }
187
188 bool operator<(const KeyPair &entry, QChar qchar)
189 {
190     return entry.cocoaKey < qchar;
191 }
192
193 bool operator<(QChar qchar, const KeyPair &entry)
194 {
195     return qchar < entry.cocoaKey;
196 }
197
198 bool operator<(const Qt::Key &key, const KeyPair &entry)
199 {
200     return key < entry.qtKey;
201 }
202
203 bool operator<(const KeyPair &entry, const Qt::Key &key)
204 {
205     return entry.qtKey < key;
206 }
207
208 static bool qtKey2CocoaKeySortLessThan(const KeyPair &entry1, const KeyPair &entry2)
209 {
210     return entry1.qtKey < entry2.qtKey;
211 }
212
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 },
278 };
279 static const KeyPair * const end = entries + NumEntries;
280
281 QChar qt_mac_qtKey2CocoaKey(Qt::Key key)
282 {
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;
287     if (mustInit){
288         mustInit = false;
289         for (int i=0; i<NumEntries; ++i)
290             rev_entries[i] = entries[i];
291         qSort(rev_entries.begin(), rev_entries.end(), qtKey2CocoaKeySortLessThan);
292     }
293     const QVector<KeyPair>::iterator i
294             = qBinaryFind(rev_entries.begin(), rev_entries.end(), key);
295     if (i == rev_entries.end())
296         return QChar();
297     return i->cocoaKey;
298 }
299
300 Qt::Key qt_mac_cocoaKey2QtKey(QChar keyCode)
301 {
302     const KeyPair *i = qBinaryFind(entries, end, keyCode);
303     if (i == end)
304         return Qt::Key(keyCode.toUpper().unicode());
305     return i->qtKey;
306 }
307
308 struct dndenum_mapper
309 {
310     NSDragOperation mac_code;
311     Qt::DropAction qt_code;
312     bool Qt2Mac;
313 };
314
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 }
322 };
323
324 NSDragOperation qt_mac_mapDropAction(Qt::DropAction action)
325 {
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;
329         }
330     }
331     return NSDragOperationNone;
332 }
333
334 NSDragOperation qt_mac_mapDropActions(Qt::DropActions actions)
335 {
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;
340     }
341     return nsActions;
342 }
343
344 Qt::DropAction qt_mac_mapNSDragOperation(NSDragOperation nsActions)
345 {
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;
350     }
351     return action;
352 }
353
354 Qt::DropActions qt_mac_mapNSDragOperations(NSDragOperation nsActions)
355 {
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;
360     }
361     return actions;
362 }
363
364
365
366 //
367 // Misc
368 //
369
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()
373 {
374     ProcessSerialNumber psn;
375     if (GetCurrentProcess(&psn) == noErr) {
376         bool forceTransform = true;
377         CFTypeRef value = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(),
378                                                                CFSTR("LSUIElement"));
379         if (value) {
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()) {
388                 int valueAsInt;
389                 CFNumberGetValue(static_cast<CFNumberRef>(value), kCFNumberIntType, &valueAsInt);
390                 forceTransform = !valueAsInt;
391             }
392         }
393
394         if (forceTransform) {
395             value = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(),
396                                                          CFSTR("LSBackgroundOnly"));
397             if (value) {
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()) {
404                     int valueAsInt;
405                     CFNumberGetValue(static_cast<CFNumberRef>(value), kCFNumberIntType, &valueAsInt);
406                     forceTransform = !valueAsInt;
407                 }
408             }
409         }
410
411         if (forceTransform) {
412             TransformProcessType(&psn, kProcessTransformToForegroundApplication);
413         }
414     }
415 }
416
417 QString qt_mac_removeMnemonics(const QString &original)
418 {
419     QString returnText(original.size(), 0);
420     int finalDest = 0;
421     int currPos = 0;
422     int l = original.length();
423     while (l) {
424         if (original.at(currPos) == QLatin1Char('&')
425             && (l == 1 || original.at(currPos + 1) != QLatin1Char('&'))) {
426             ++currPos;
427             --l;
428             if (l == 0)
429                 break;
430         }
431         returnText[finalDest] = original.at(currPos);
432         ++currPos;
433         ++finalDest;
434         --l;
435     }
436     returnText.truncate(finalDest);
437     return returnText;
438 }
439
440
441 CGColorSpaceRef m_genericColorSpace = 0;
442 QHash<CGDirectDisplayID, CGColorSpaceRef> m_displayColorSpaceHash;
443 bool m_postRoutineRegistered = false;
444
445 CGColorSpaceRef qt_mac_genericColorSpace()
446 {
447 #if 0
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);
452         } else
453 #endif
454         {
455             m_genericColorSpace = CGColorSpaceCreateDeviceRGB();
456         }
457         if (!m_postRoutineRegistered) {
458             m_postRoutineRegistered = true;
459             qAddPostRoutine(QCoreGraphicsPaintEngine::cleanUpMacColorSpaces);
460         }
461     }
462     return m_genericColorSpace;
463 #else
464     // Just return the main display colorspace for the moment.
465     return qt_mac_displayColorSpace(0);
466 #endif
467 }
468
469 /*
470     Ideally, we should pass the widget in here, and use CGGetDisplaysWithRect() etc.
471     to support multiple displays correctly.
472 */
473 CGColorSpaceRef qt_mac_displayColorSpace(const QWidget *widget)
474 {
475     CGColorSpaceRef colorSpace;
476
477     CGDirectDisplayID displayID;
478     CMProfileRef displayProfile = 0;
479     if (widget == 0) {
480         displayID = CGMainDisplayID();
481     } else {
482         displayID = CGMainDisplayID();
483         /*
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
491         */
492     }
493     if ((colorSpace = m_displayColorSpaceHash.value(displayID)))
494         return colorSpace;
495
496     CMError err = CMGetProfileByAVID((CMDisplayIDType)displayID, &displayProfile);
497     if (err == noErr) {
498         colorSpace = CGColorSpaceCreateWithPlatformColorSpace(displayProfile);
499     } else if (widget) {
500         return qt_mac_displayColorSpace(0); // fall back on main display
501     }
502
503     if (colorSpace == 0)
504         colorSpace = CGColorSpaceCreateDeviceRGB();
505
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);
512     }
513     return colorSpace;
514 }
515
516 void qt_mac_cleanUpMacColorSpaces()
517 {
518     if (m_genericColorSpace) {
519         CFRelease(m_genericColorSpace);
520         m_genericColorSpace = 0;
521     }
522     QHash<CGDirectDisplayID, CGColorSpaceRef>::const_iterator it = m_displayColorSpaceHash.constBegin();
523     while (it != m_displayColorSpaceHash.constEnd()) {
524         if (it.value())
525             CFRelease(it.value());
526         ++it;
527     }
528     m_displayColorSpaceHash.clear();
529 }
530
531 QString qt_mac_applicationName()
532 {
533     QString appName;
534     CFTypeRef string = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(), CFSTR("CFBundleName"));
535     if (string)
536         appName = QCFString::toQString(static_cast<CFStringRef>(string));
537
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);
543         } else {
544             appName = arg0;
545         }
546     }
547     return appName;
548 }
549
550 NSRect qt_mac_flipRect(const QRect &rect, QWindow *window)
551 {
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());
555 }
556
557 OSStatus qt_mac_drawCGImage(CGContextRef inContext, const CGRect *inBounds, CGImageRef inImage)
558 {
559     // Verbatim copy if HIViewDrawCGImage (as shown on Carbon-Dev)
560     OSStatus err = noErr;
561
562     require_action(inContext != NULL, InvalidContext, err = paramErr);
563     require_action(inBounds != NULL, InvalidBounds, err = paramErr);
564     require_action(inImage != NULL, InvalidImage, err = paramErr);
565
566     CGContextSaveGState( inContext );
567     CGContextTranslateCTM (inContext, 0, inBounds->origin.y + CGRectGetMaxY(*inBounds));
568     CGContextScaleCTM(inContext, 1, -1);
569
570     CGContextDrawImage(inContext, *inBounds, inImage);
571
572     CGContextRestoreGState(inContext);
573 InvalidImage:
574 InvalidBounds:
575 InvalidContext:
576         return err;
577 }
578
579 CGFloat qt_mac_get_scalefactor()
580 {
581     return [[NSScreen mainScreen] userSpaceScaleFactor];
582 }
583
584 QT_END_NAMESPACE