Cocoa: Implement propagateSizeHints.
[profile/ivi/qtbase.git] / src / plugins / platforms / cocoa / qcocoahelpers.mm
1 /****************************************************************************
2  **
3  ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4  ** All rights reserved.
5  ** Contact: Nokia Corporation (qt-info@nokia.com)
6  **
7  ** This file is part of the plugins of the Qt Toolkit.
8  **
9  ** $QT_BEGIN_LICENSE:LGPL$
10  ** GNU Lesser General Public License Usage
11  ** This file may be used under the terms of the GNU Lesser General Public
12  ** License version 2.1 as published by the Free Software Foundation and
13  ** appearing in the file LICENSE.LGPL included in the packaging of this
14  ** file. Please review the following information to ensure the GNU Lesser
15  ** General Public License version 2.1 requirements will be met:
16  ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17  **
18  ** In addition, as a special exception, Nokia gives you certain additional
19  ** rights. These rights are described in the Nokia Qt LGPL Exception
20  ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21  **
22  ** GNU General Public License Usage
23  ** Alternatively, this file may be used under the terms of the GNU General
24  ** Public License version 3.0 as published by the Free Software Foundation
25  ** and appearing in the file LICENSE.GPL included in the packaging of this
26  ** file. Please review the following information to ensure the GNU General
27  ** Public License version 3.0 requirements will be met:
28  ** http://www.gnu.org/copyleft/gpl.html.
29  **
30  ** Other Usage
31  ** Alternatively, this file may be used in accordance with the terms and
32  ** conditions contained in a signed written agreement between you and Nokia.
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
49 //
50 // Conversion Functions
51 //
52
53 QStringList qt_mac_NSArrayToQStringList(void *nsarray)
54 {
55     QStringList result;
56     NSArray *array = static_cast<NSArray *>(nsarray);
57     for (NSUInteger i=0; i<[array count]; ++i)
58         result << qt_mac_NSStringToQString([array objectAtIndex:i]);
59     return result;
60 }
61
62 void *qt_mac_QStringListToNSMutableArrayVoid(const QStringList &list)
63 {
64     NSMutableArray *result = [NSMutableArray arrayWithCapacity:list.size()];
65     for (int i=0; i<list.size(); ++i){
66         [result addObject:reinterpret_cast<const NSString *>(QCFString::toCFStringRef(list[i]))];
67     }
68     return result;
69 }
70
71 static void drawImageReleaseData (void *info, const void *, size_t)
72 {
73     delete static_cast<QImage *>(info);
74 }
75
76 CGImageRef qt_mac_image_to_cgimage(const QImage &img)
77 {
78     QImage *image;
79     if (img.depth() != 32)
80         image = new QImage(img.convertToFormat(QImage::Format_ARGB32_Premultiplied));
81     else
82         image = new QImage(img);
83
84     uint cgflags = kCGImageAlphaNone;
85     switch (image->format()) {
86     case QImage::Format_ARGB32_Premultiplied:
87         cgflags = kCGImageAlphaPremultipliedFirst;
88         break;
89     case QImage::Format_ARGB32:
90         cgflags = kCGImageAlphaFirst;
91         break;
92     case QImage::Format_RGB32:
93         cgflags = kCGImageAlphaNoneSkipFirst;
94     default:
95         break;
96     }
97     cgflags |= kCGBitmapByteOrder32Host;
98     QCFType<CGDataProviderRef> dataProvider = CGDataProviderCreateWithData(image,
99                                                           static_cast<const QImage *>(image)->bits(),
100                                                           image->byteCount(),
101                                                           drawImageReleaseData);
102
103     return CGImageCreate(image->width(), image->height(), 8, 32,
104                                         image->bytesPerLine(),
105                                         qt_mac_genericColorSpace(),
106                                         cgflags, dataProvider, 0, false, kCGRenderingIntentDefault);
107
108 }
109
110 NSImage *qt_mac_cgimage_to_nsimage(CGImageRef image)
111 {
112     QCocoaAutoReleasePool pool;
113     NSImage *newImage = 0;
114     NSRect imageRect = NSMakeRect(0.0, 0.0, CGImageGetWidth(image), CGImageGetHeight(image));
115     newImage = [[NSImage alloc] initWithSize:imageRect.size];
116     [newImage lockFocus];
117     {
118         CGContextRef imageContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
119         CGContextDrawImage(imageContext, *(CGRect*)&imageRect, image);
120     }
121     [newImage unlockFocus];
122     return newImage;
123 }
124
125 NSImage *qt_mac_create_nsimage(const QPixmap &pm)
126 {
127     QImage image = pm.toImage();
128     return qt_mac_cgimage_to_nsimage(qt_mac_image_to_cgimage(image));
129 }
130
131 NSSize qt_mac_toNSSize(const QSize &qtSize)
132 {
133     return NSMakeSize(qtSize.width(), qtSize.height());
134 }
135
136 // Use this method to keep all the information in the TextSegment. As long as it is ordered
137 // we are in OK shape, and we can influence that ourselves.
138 struct KeyPair
139 {
140     QChar cocoaKey;
141     Qt::Key qtKey;
142 };
143
144 bool operator==(const KeyPair &entry, QChar qchar)
145 {
146     return entry.cocoaKey == qchar;
147 }
148
149 bool operator<(const KeyPair &entry, QChar qchar)
150 {
151     return entry.cocoaKey < qchar;
152 }
153
154 bool operator<(QChar qchar, const KeyPair &entry)
155 {
156     return qchar < entry.cocoaKey;
157 }
158
159 bool operator<(const Qt::Key &key, const KeyPair &entry)
160 {
161     return key < entry.qtKey;
162 }
163
164 bool operator<(const KeyPair &entry, const Qt::Key &key)
165 {
166     return entry.qtKey < key;
167 }
168
169 static bool qtKey2CocoaKeySortLessThan(const KeyPair &entry1, const KeyPair &entry2)
170 {
171     return entry1.qtKey < entry2.qtKey;
172 }
173
174 static const int NumEntries = 59;
175 static const KeyPair entries[NumEntries] = {
176     { NSEnterCharacter, Qt::Key_Enter },
177     { NSBackspaceCharacter, Qt::Key_Backspace },
178     { NSTabCharacter, Qt::Key_Tab },
179     { NSNewlineCharacter, Qt::Key_Return },
180     { NSCarriageReturnCharacter, Qt::Key_Return },
181     { NSBackTabCharacter, Qt::Key_Backtab },
182     { kEscapeCharCode, Qt::Key_Escape },
183     // Cocoa sends us delete when pressing backspace!
184     // (NB when we reverse this list in qtKey2CocoaKey, there
185     // will be two indices of Qt::Key_Backspace. But is seems to work
186     // ok for menu shortcuts (which uses that function):
187     { NSDeleteCharacter, Qt::Key_Backspace },
188     { NSUpArrowFunctionKey, Qt::Key_Up },
189     { NSDownArrowFunctionKey, Qt::Key_Down },
190     { NSLeftArrowFunctionKey, Qt::Key_Left },
191     { NSRightArrowFunctionKey, Qt::Key_Right },
192     { NSF1FunctionKey, Qt::Key_F1 },
193     { NSF2FunctionKey, Qt::Key_F2 },
194     { NSF3FunctionKey, Qt::Key_F3 },
195     { NSF4FunctionKey, Qt::Key_F4 },
196     { NSF5FunctionKey, Qt::Key_F5 },
197     { NSF6FunctionKey, Qt::Key_F6 },
198     { NSF7FunctionKey, Qt::Key_F7 },
199     { NSF8FunctionKey, Qt::Key_F8 },
200     { NSF9FunctionKey, Qt::Key_F8 },
201     { NSF10FunctionKey, Qt::Key_F10 },
202     { NSF11FunctionKey, Qt::Key_F11 },
203     { NSF12FunctionKey, Qt::Key_F12 },
204     { NSF13FunctionKey, Qt::Key_F13 },
205     { NSF14FunctionKey, Qt::Key_F14 },
206     { NSF15FunctionKey, Qt::Key_F15 },
207     { NSF16FunctionKey, Qt::Key_F16 },
208     { NSF17FunctionKey, Qt::Key_F17 },
209     { NSF18FunctionKey, Qt::Key_F18 },
210     { NSF19FunctionKey, Qt::Key_F19 },
211     { NSF20FunctionKey, Qt::Key_F20 },
212     { NSF21FunctionKey, Qt::Key_F21 },
213     { NSF22FunctionKey, Qt::Key_F22 },
214     { NSF23FunctionKey, Qt::Key_F23 },
215     { NSF24FunctionKey, Qt::Key_F24 },
216     { NSF25FunctionKey, Qt::Key_F25 },
217     { NSF26FunctionKey, Qt::Key_F26 },
218     { NSF27FunctionKey, Qt::Key_F27 },
219     { NSF28FunctionKey, Qt::Key_F28 },
220     { NSF29FunctionKey, Qt::Key_F29 },
221     { NSF30FunctionKey, Qt::Key_F30 },
222     { NSF31FunctionKey, Qt::Key_F31 },
223     { NSF32FunctionKey, Qt::Key_F32 },
224     { NSF33FunctionKey, Qt::Key_F33 },
225     { NSF34FunctionKey, Qt::Key_F34 },
226     { NSF35FunctionKey, Qt::Key_F35 },
227     { NSInsertFunctionKey, Qt::Key_Insert },
228     { NSDeleteFunctionKey, Qt::Key_Delete },
229     { NSHomeFunctionKey, Qt::Key_Home },
230     { NSEndFunctionKey, Qt::Key_End },
231     { NSPageUpFunctionKey, Qt::Key_PageUp },
232     { NSPageDownFunctionKey, Qt::Key_PageDown },
233     { NSPrintScreenFunctionKey, Qt::Key_Print },
234     { NSScrollLockFunctionKey, Qt::Key_ScrollLock },
235     { NSPauseFunctionKey, Qt::Key_Pause },
236     { NSSysReqFunctionKey, Qt::Key_SysReq },
237     { NSMenuFunctionKey, Qt::Key_Menu },
238     { NSHelpFunctionKey, Qt::Key_Help },
239 };
240 static const KeyPair * const end = entries + NumEntries;
241
242 QChar qt_mac_qtKey2CocoaKey(Qt::Key key)
243 {
244     // The first time this function is called, create a reverse
245     // lookup table sorted on Qt Key rather than Cocoa key:
246     static QVector<KeyPair> rev_entries(NumEntries);
247     static bool mustInit = true;
248     if (mustInit){
249         mustInit = false;
250         for (int i=0; i<NumEntries; ++i)
251             rev_entries[i] = entries[i];
252         qSort(rev_entries.begin(), rev_entries.end(), qtKey2CocoaKeySortLessThan);
253     }
254     const QVector<KeyPair>::iterator i
255             = qBinaryFind(rev_entries.begin(), rev_entries.end(), key);
256     if (i == rev_entries.end())
257         return QChar();
258     return i->cocoaKey;
259 }
260
261 Qt::Key qt_mac_cocoaKey2QtKey(QChar keyCode)
262 {
263     const KeyPair *i = qBinaryFind(entries, end, keyCode);
264     if (i == end)
265         return Qt::Key(keyCode.unicode());
266     return i->qtKey;
267 }
268
269 //
270 // Misc
271 //
272
273 // Changes the process type for this process to kProcessTransformToForegroundApplication,
274 // unless either LSUIElement or LSBackgroundOnly is set in the Info.plist.
275 void qt_mac_transformProccessToForegroundApplication()
276 {
277     ProcessSerialNumber psn;
278     if (GetCurrentProcess(&psn) == noErr) {
279         bool forceTransform = true;
280         CFTypeRef value = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(),
281                                                                CFSTR("LSUIElement"));
282         if (value) {
283             CFTypeID valueType = CFGetTypeID(value);
284             // Officially it's supposed to be a string, a boolean makes sense, so we'll check.
285             // A number less so, but OK.
286             if (valueType == CFStringGetTypeID())
287                 forceTransform = !(QCFString::toQString(static_cast<CFStringRef>(value)).toInt());
288             else if (valueType == CFBooleanGetTypeID())
289                 forceTransform = !CFBooleanGetValue(static_cast<CFBooleanRef>(value));
290             else if (valueType == CFNumberGetTypeID()) {
291                 int valueAsInt;
292                 CFNumberGetValue(static_cast<CFNumberRef>(value), kCFNumberIntType, &valueAsInt);
293                 forceTransform = !valueAsInt;
294             }
295         }
296
297         if (forceTransform) {
298             value = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(),
299                                                          CFSTR("LSBackgroundOnly"));
300             if (value) {
301                 CFTypeID valueType = CFGetTypeID(value);
302                 if (valueType == CFBooleanGetTypeID())
303                     forceTransform = !CFBooleanGetValue(static_cast<CFBooleanRef>(value));
304                 else if (valueType == CFStringGetTypeID())
305                     forceTransform = !(QCFString::toQString(static_cast<CFStringRef>(value)).toInt());
306                 else if (valueType == CFNumberGetTypeID()) {
307                     int valueAsInt;
308                     CFNumberGetValue(static_cast<CFNumberRef>(value), kCFNumberIntType, &valueAsInt);
309                     forceTransform = !valueAsInt;
310                 }
311             }
312         }
313
314         if (forceTransform) {
315             TransformProcessType(&psn, kProcessTransformToForegroundApplication);
316         }
317     }
318 }
319
320 QString qt_mac_removeMnemonics(const QString &original)
321 {
322     QString returnText(original.size(), 0);
323     int finalDest = 0;
324     int currPos = 0;
325     int l = original.length();
326     while (l) {
327         if (original.at(currPos) == QLatin1Char('&')
328             && (l == 1 || original.at(currPos + 1) != QLatin1Char('&'))) {
329             ++currPos;
330             --l;
331             if (l == 0)
332                 break;
333         }
334         returnText[finalDest] = original.at(currPos);
335         ++currPos;
336         ++finalDest;
337         --l;
338     }
339     returnText.truncate(finalDest);
340     return returnText;
341 }
342
343
344 CGColorSpaceRef m_genericColorSpace = 0;
345 QHash<CGDirectDisplayID, CGColorSpaceRef> m_displayColorSpaceHash;
346 bool m_postRoutineRegistered = false;
347
348 CGColorSpaceRef qt_mac_genericColorSpace()
349 {
350 #if 0
351     if (!m_genericColorSpace) {
352 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
353         if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
354             m_genericColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
355         } else
356 #endif
357         {
358             m_genericColorSpace = CGColorSpaceCreateDeviceRGB();
359         }
360         if (!m_postRoutineRegistered) {
361             m_postRoutineRegistered = true;
362             qAddPostRoutine(QCoreGraphicsPaintEngine::cleanUpMacColorSpaces);
363         }
364     }
365     return m_genericColorSpace;
366 #else
367     // Just return the main display colorspace for the moment.
368     return qt_mac_displayColorSpace(0);
369 #endif
370 }
371
372 /*
373     Ideally, we should pass the widget in here, and use CGGetDisplaysWithRect() etc.
374     to support multiple displays correctly.
375 */
376 CGColorSpaceRef qt_mac_displayColorSpace(const QWidget *widget)
377 {
378     CGColorSpaceRef colorSpace;
379
380     CGDirectDisplayID displayID;
381     CMProfileRef displayProfile = 0;
382     if (widget == 0) {
383         displayID = CGMainDisplayID();
384     } else {
385         displayID = CGMainDisplayID();
386         /*
387         ### get correct display
388         const QRect &qrect = widget->window()->geometry();
389         CGRect rect = CGRectMake(qrect.x(), qrect.y(), qrect.width(), qrect.height());
390         CGDisplayCount throwAway;
391         CGDisplayErr dErr = CGGetDisplaysWithRect(rect, 1, &displayID, &throwAway);
392         if (dErr != kCGErrorSuccess)
393             return macDisplayColorSpace(0); // fall back on main display
394         */
395     }
396     if ((colorSpace = m_displayColorSpaceHash.value(displayID)))
397         return colorSpace;
398
399     CMError err = CMGetProfileByAVID((CMDisplayIDType)displayID, &displayProfile);
400     if (err == noErr) {
401         colorSpace = CGColorSpaceCreateWithPlatformColorSpace(displayProfile);
402     } else if (widget) {
403         return qt_mac_displayColorSpace(0); // fall back on main display
404     }
405
406     if (colorSpace == 0)
407         colorSpace = CGColorSpaceCreateDeviceRGB();
408
409     m_displayColorSpaceHash.insert(displayID, colorSpace);
410     CMCloseProfile(displayProfile);
411     if (!m_postRoutineRegistered) {
412         m_postRoutineRegistered = true;
413         void qt_mac_cleanUpMacColorSpaces();
414         qAddPostRoutine(qt_mac_cleanUpMacColorSpaces);
415     }
416     return colorSpace;
417 }
418
419 void qt_mac_cleanUpMacColorSpaces()
420 {
421     if (m_genericColorSpace) {
422         CFRelease(m_genericColorSpace);
423         m_genericColorSpace = 0;
424     }
425     QHash<CGDirectDisplayID, CGColorSpaceRef>::const_iterator it = m_displayColorSpaceHash.constBegin();
426     while (it != m_displayColorSpaceHash.constEnd()) {
427         if (it.value())
428             CFRelease(it.value());
429         ++it;
430     }
431     m_displayColorSpaceHash.clear();
432 }
433
434 QString qt_mac_applicationName()
435 {
436     QString appName;
437     CFTypeRef string = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(), CFSTR("CFBundleName"));
438     if (string)
439         appName = QCFString::toQString(static_cast<CFStringRef>(string));
440
441     if (appName.isEmpty()) {
442         QString arg0 = qApp->arguments().at(0);
443         if (arg0.contains("/")) {
444             QStringList parts = arg0.split("/");
445             appName = parts.at(parts.count() - 1);
446         } else {
447             appName = arg0;
448         }
449     }
450     return appName;
451 }
452