1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the plugins of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
42 #include "qcocoahelpers.h"
44 #include "qcocoaautoreleasepool.h"
50 // Conversion Functions
53 QStringList qt_mac_NSArrayToQStringList(void *nsarray)
56 NSArray *array = static_cast<NSArray *>(nsarray);
57 for (NSUInteger i=0; i<[array count]; ++i)
58 result << qt_mac_NSStringToQString([array objectAtIndex:i]);
62 void *qt_mac_QStringListToNSMutableArrayVoid(const QStringList &list)
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]))];
71 static void drawImageReleaseData (void *info, const void *, size_t)
73 delete static_cast<QImage *>(info);
76 CGImageRef qt_mac_image_to_cgimage(const QImage &img)
79 if (img.depth() != 32)
80 image = new QImage(img.convertToFormat(QImage::Format_ARGB32_Premultiplied));
82 image = new QImage(img);
84 uint cgflags = kCGImageAlphaNone;
85 switch (image->format()) {
86 case QImage::Format_ARGB32_Premultiplied:
87 cgflags = kCGImageAlphaPremultipliedFirst;
89 case QImage::Format_ARGB32:
90 cgflags = kCGImageAlphaFirst;
92 case QImage::Format_RGB32:
93 cgflags = kCGImageAlphaNoneSkipFirst;
97 cgflags |= kCGBitmapByteOrder32Host;
98 QCFType<CGDataProviderRef> dataProvider = CGDataProviderCreateWithData(image,
99 static_cast<const QImage *>(image)->bits(),
101 drawImageReleaseData);
103 return CGImageCreate(image->width(), image->height(), 8, 32,
104 image->bytesPerLine(),
105 qt_mac_genericColorSpace(),
106 cgflags, dataProvider, 0, false, kCGRenderingIntentDefault);
110 NSImage *qt_mac_cgimage_to_nsimage(CGImageRef image)
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];
118 CGContextRef imageContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
119 CGContextDrawImage(imageContext, *(CGRect*)&imageRect, image);
121 [newImage unlockFocus];
125 NSImage *qt_mac_create_nsimage(const QPixmap &pm)
127 QImage image = pm.toImage();
128 return qt_mac_cgimage_to_nsimage(qt_mac_image_to_cgimage(image));
131 NSSize qt_mac_toNSSize(const QSize &qtSize)
133 return NSMakeSize(qtSize.width(), qtSize.height());
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.
144 bool operator==(const KeyPair &entry, QChar qchar)
146 return entry.cocoaKey == qchar;
149 bool operator<(const KeyPair &entry, QChar qchar)
151 return entry.cocoaKey < qchar;
154 bool operator<(QChar qchar, const KeyPair &entry)
156 return qchar < entry.cocoaKey;
159 bool operator<(const Qt::Key &key, const KeyPair &entry)
161 return key < entry.qtKey;
164 bool operator<(const KeyPair &entry, const Qt::Key &key)
166 return entry.qtKey < key;
169 static bool qtKey2CocoaKeySortLessThan(const KeyPair &entry1, const KeyPair &entry2)
171 return entry1.qtKey < entry2.qtKey;
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 },
240 static const KeyPair * const end = entries + NumEntries;
242 QChar qt_mac_qtKey2CocoaKey(Qt::Key key)
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;
250 for (int i=0; i<NumEntries; ++i)
251 rev_entries[i] = entries[i];
252 qSort(rev_entries.begin(), rev_entries.end(), qtKey2CocoaKeySortLessThan);
254 const QVector<KeyPair>::iterator i
255 = qBinaryFind(rev_entries.begin(), rev_entries.end(), key);
256 if (i == rev_entries.end())
261 Qt::Key qt_mac_cocoaKey2QtKey(QChar keyCode)
263 const KeyPair *i = qBinaryFind(entries, end, keyCode);
265 return Qt::Key(keyCode.unicode());
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()
277 ProcessSerialNumber psn;
278 if (GetCurrentProcess(&psn) == noErr) {
279 bool forceTransform = true;
280 CFTypeRef value = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(),
281 CFSTR("LSUIElement"));
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()) {
292 CFNumberGetValue(static_cast<CFNumberRef>(value), kCFNumberIntType, &valueAsInt);
293 forceTransform = !valueAsInt;
297 if (forceTransform) {
298 value = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(),
299 CFSTR("LSBackgroundOnly"));
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()) {
308 CFNumberGetValue(static_cast<CFNumberRef>(value), kCFNumberIntType, &valueAsInt);
309 forceTransform = !valueAsInt;
314 if (forceTransform) {
315 TransformProcessType(&psn, kProcessTransformToForegroundApplication);
320 QString qt_mac_removeMnemonics(const QString &original)
322 QString returnText(original.size(), 0);
325 int l = original.length();
327 if (original.at(currPos) == QLatin1Char('&')
328 && (l == 1 || original.at(currPos + 1) != QLatin1Char('&'))) {
334 returnText[finalDest] = original.at(currPos);
339 returnText.truncate(finalDest);
344 CGColorSpaceRef m_genericColorSpace = 0;
345 QHash<CGDirectDisplayID, CGColorSpaceRef> m_displayColorSpaceHash;
346 bool m_postRoutineRegistered = false;
348 CGColorSpaceRef qt_mac_genericColorSpace()
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);
358 m_genericColorSpace = CGColorSpaceCreateDeviceRGB();
360 if (!m_postRoutineRegistered) {
361 m_postRoutineRegistered = true;
362 qAddPostRoutine(QCoreGraphicsPaintEngine::cleanUpMacColorSpaces);
365 return m_genericColorSpace;
367 // Just return the main display colorspace for the moment.
368 return qt_mac_displayColorSpace(0);
373 Ideally, we should pass the widget in here, and use CGGetDisplaysWithRect() etc.
374 to support multiple displays correctly.
376 CGColorSpaceRef qt_mac_displayColorSpace(const QWidget *widget)
378 CGColorSpaceRef colorSpace;
380 CGDirectDisplayID displayID;
381 CMProfileRef displayProfile = 0;
383 displayID = CGMainDisplayID();
385 displayID = CGMainDisplayID();
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
396 if ((colorSpace = m_displayColorSpaceHash.value(displayID)))
399 CMError err = CMGetProfileByAVID((CMDisplayIDType)displayID, &displayProfile);
401 colorSpace = CGColorSpaceCreateWithPlatformColorSpace(displayProfile);
403 return qt_mac_displayColorSpace(0); // fall back on main display
407 colorSpace = CGColorSpaceCreateDeviceRGB();
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);
419 void qt_mac_cleanUpMacColorSpaces()
421 if (m_genericColorSpace) {
422 CFRelease(m_genericColorSpace);
423 m_genericColorSpace = 0;
425 QHash<CGDirectDisplayID, CGColorSpaceRef>::const_iterator it = m_displayColorSpaceHash.constBegin();
426 while (it != m_displayColorSpaceHash.constEnd()) {
428 CFRelease(it.value());
431 m_displayColorSpaceHash.clear();
434 QString qt_mac_applicationName()
437 CFTypeRef string = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(), CFSTR("CFBundleName"));
439 appName = QCFString::toQString(static_cast<CFStringRef>(string));
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);