VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/platform/darwin/VBoxUtils-darwin-cocoa.mm@ 98103

Last change on this file since 98103 was 98103, checked in by vboxsync, 17 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 26.8 KB
Line 
1/* $Id: VBoxUtils-darwin-cocoa.mm 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - Declarations of utility classes and functions for handling Darwin Cocoa specific tasks.
4 */
5
6/*
7 * Copyright (C) 2009-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#include "VBoxUtils-darwin.h"
29#include "VBoxCocoaHelper.h"
30
31#include <QMenu>
32
33#include <iprt/assert.h>
34
35#import <AppKit/NSEvent.h>
36#import <AppKit/NSColor.h>
37#import <AppKit/NSFont.h>
38#import <AppKit/NSScreen.h>
39#import <AppKit/NSScroller.h>
40#import <AppKit/NSWindow.h>
41#import <AppKit/NSImageView.h>
42
43#import <objc/objc-class.h>
44
45/* For the keyboard stuff */
46#include <Carbon/Carbon.h>
47#include "DarwinKeyboard.h"
48
49/** Easy way of dynamical call for 10.7 AppKit functionality we do not yet support. */
50#define NSWindowCollectionBehaviorFullScreenPrimary (1 << 7)
51#define NSFullScreenWindowMask (1 << 14)
52
53NativeNSWindowRef darwinToNativeWindowImpl(NativeNSViewRef pView)
54{
55 NativeNSWindowRef window = NULL;
56 if (pView)
57 window = [pView window];
58
59 return window;
60}
61
62NativeNSViewRef darwinToNativeViewImpl(NativeNSWindowRef pWindow)
63{
64 NativeNSViewRef view = NULL;
65 if (pWindow)
66 view = [pWindow contentView];
67
68 return view;
69}
70
71NativeNSButtonRef darwinNativeButtonOfWindowImpl(NativeNSWindowRef pWindow, StandardWindowButtonType enmButtonType)
72{
73 /* Return corresponding button: */
74 switch (enmButtonType)
75 {
76 case StandardWindowButtonType_Close: return [pWindow standardWindowButton:NSWindowCloseButton];
77 case StandardWindowButtonType_Miniaturize: return [pWindow standardWindowButton:NSWindowMiniaturizeButton];
78 case StandardWindowButtonType_Zoom: return [pWindow standardWindowButton:NSWindowZoomButton];
79 case StandardWindowButtonType_Toolbar: return [pWindow standardWindowButton:NSWindowToolbarButton];
80 case StandardWindowButtonType_DocumentIcon: return [pWindow standardWindowButton:NSWindowDocumentIconButton];
81 case StandardWindowButtonType_DocumentVersions: /*return [pWindow standardWindowButton:NSWindowDocumentVersionsButton];*/ break;
82 case StandardWindowButtonType_FullScreen: /*return [pWindow standardWindowButton:NSWindowFullScreenButton];*/ break;
83 }
84 /* Return Nul by default: */
85 return Nil;
86}
87
88NativeNSImageRef darwinToNSImageRef(const CGImageRef pImage)
89{
90 /* Create a bitmap rep from the image. */
91 NSBitmapImageRep *bitmapRep = [[[NSBitmapImageRep alloc] initWithCGImage:pImage] autorelease];
92 /* Create an NSImage and add the bitmap rep to it */
93 NSImage *image = [[NSImage alloc] init];
94 [image addRepresentation:bitmapRep];
95 return image;
96}
97
98NativeNSImageRef darwinToNSImageRef(const QImage *pImage)
99{
100 /* Create CGImage on the basis of passed QImage: */
101 CGImageRef pCGImage = ::darwinToCGImageRef(pImage);
102 NativeNSImageRef pNSImage = ::darwinToNSImageRef(pCGImage);
103 CGImageRelease(pCGImage);
104 /* Apply device pixel ratio: */
105 double dScaleFactor = pImage->devicePixelRatio();
106 NSSize imageSize = { (CGFloat)pImage->width() / dScaleFactor,
107 (CGFloat)pImage->height() / dScaleFactor };
108 [pNSImage setSize:imageSize];
109 /* Return result: */
110 return pNSImage;
111}
112
113NativeNSImageRef darwinToNSImageRef(const QPixmap *pPixmap)
114{
115 CGImageRef pCGImage = ::darwinToCGImageRef(pPixmap);
116 NativeNSImageRef pNSImage = ::darwinToNSImageRef(pCGImage);
117 CGImageRelease(pCGImage);
118 return pNSImage;
119}
120
121NativeNSImageRef darwinToNSImageRef(const char *pczSource)
122{
123 CGImageRef pCGImage = ::darwinToCGImageRef(pczSource);
124 NativeNSImageRef pNSImage = ::darwinToNSImageRef(pCGImage);
125 CGImageRelease(pCGImage);
126 return pNSImage;
127}
128
129NativeNSStringRef darwinToNativeString(const char* pcszString)
130{
131 return [NSString stringWithUTF8String: pcszString];
132}
133
134QString darwinFromNativeString(NativeNSStringRef pString)
135{
136 return [pString cStringUsingEncoding :NSASCIIStringEncoding];
137}
138
139void darwinSetShowsToolbarButtonImpl(NativeNSWindowRef pWindow, bool fEnabled)
140{
141 [pWindow setShowsToolbarButton:fEnabled];
142}
143
144void darwinLabelWindow(NativeNSWindowRef pWindow, NativeNSImageRef pImage, double dDpr)
145{
146 /* Get the parent view of the close button. */
147 NSView *wv = [[pWindow standardWindowButton:NSWindowCloseButton] superview];
148 if (wv)
149 {
150 /* We have to calculate the size of the title bar for the center case. */
151 NSSize s = [pImage size];
152 NSSize s1 = [wv frame].size;
153 /* Correctly position the label. */
154 NSImageView *iv = [[NSImageView alloc] initWithFrame:NSMakeRect(s1.width - s.width / dDpr,
155 s1.height - s.height / dDpr - 1,
156 s.width / dDpr, s.height / dDpr)];
157 /* Configure the NSImageView for auto moving. */
158 [iv setImage:pImage];
159 [iv setAutoresizesSubviews:true];
160 [iv setAutoresizingMask:NSViewMinXMargin | NSViewMinYMargin];
161 /* Add it to the parent of the close button. */
162 [wv addSubview:iv positioned:NSWindowBelow relativeTo:nil];
163 }
164}
165
166void darwinSetShowsResizeIndicatorImpl(NativeNSWindowRef pWindow, bool fEnabled)
167{
168 [pWindow setShowsResizeIndicator:fEnabled];
169}
170
171void darwinSetHidesAllTitleButtonsImpl(NativeNSWindowRef pWindow)
172{
173 /* Remove all title buttons by changing the style mask. This method is
174 available from 10.6 on only. */
175 if ([pWindow respondsToSelector: @selector(setStyleMask:)])
176 [pWindow performSelector: @selector(setStyleMask:) withObject: (id)NSTitledWindowMask];
177 else
178 {
179 /* On pre 10.6 disable all the buttons currently displayed. Don't use
180 setHidden cause this remove the buttons, but didn't release the
181 place used for the buttons. */
182 NSButton *pButton = [pWindow standardWindowButton:NSWindowCloseButton];
183 if (pButton != Nil)
184 [pButton setEnabled: NO];
185 pButton = [pWindow standardWindowButton:NSWindowMiniaturizeButton];
186 if (pButton != Nil)
187 [pButton setEnabled: NO];
188 pButton = [pWindow standardWindowButton:NSWindowZoomButton];
189 if (pButton != Nil)
190 [pButton setEnabled: NO];
191 pButton = [pWindow standardWindowButton:NSWindowDocumentIconButton];
192 if (pButton != Nil)
193 [pButton setEnabled: NO];
194 }
195}
196
197void darwinSetShowsWindowTransparentImpl(NativeNSWindowRef pWindow, bool fEnabled)
198{
199 if (fEnabled)
200 {
201 [pWindow setOpaque:NO];
202 [pWindow setBackgroundColor:[NSColor clearColor]];
203 [pWindow setHasShadow:NO];
204 }
205 else
206 {
207 [pWindow setOpaque:YES];
208 [pWindow setBackgroundColor:[NSColor windowBackgroundColor]];
209 [pWindow setHasShadow:YES];
210 }
211}
212
213void darwinSetWindowHasShadow(NativeNSWindowRef pWindow, bool fEnabled)
214{
215 if (fEnabled)
216 [pWindow setHasShadow :YES];
217 else
218 [pWindow setHasShadow :NO];
219}
220
221void darwinMinaturizeWindow(NativeNSWindowRef pWindow)
222{
223 RT_NOREF(pWindow);
224// [[NSApplication sharedApplication] miniaturizeAll];
225// printf("bla\n");
226// [pWindow miniaturize:pWindow];
227// [[NSApplication sharedApplication] deactivate];
228// [pWindow performMiniaturize:nil];
229}
230
231void darwinEnableFullscreenSupport(NativeNSWindowRef pWindow)
232{
233 [pWindow setCollectionBehavior :NSWindowCollectionBehaviorFullScreenPrimary];
234}
235
236void darwinEnableTransienceSupport(NativeNSWindowRef pWindow)
237{
238 [pWindow setCollectionBehavior :NSWindowCollectionBehaviorTransient];
239}
240
241void darwinToggleFullscreenMode(NativeNSWindowRef pWindow)
242{
243 /* Toggle native fullscreen mode for passed pWindow. This method is available since 10.7 only. */
244 if ([pWindow respondsToSelector: @selector(toggleFullScreen:)])
245 [pWindow performSelector: @selector(toggleFullScreen:) withObject: (id)nil];
246}
247
248void darwinToggleWindowZoom(NativeNSWindowRef pWindow)
249{
250 /* Toggle native window zoom for passed pWindow. This method is available since 10.0. */
251 if ([pWindow respondsToSelector: @selector(zoom:)])
252 [pWindow performSelector: @selector(zoom:)];
253}
254
255bool darwinIsInFullscreenMode(NativeNSWindowRef pWindow)
256{
257 /* Check whether passed pWindow is in native fullscreen mode. */
258 return [pWindow styleMask] & NSFullScreenWindowMask;
259}
260
261bool darwinIsOnActiveSpace(NativeNSWindowRef pWindow)
262{
263 /* Check whether passed pWindow is on active space. */
264 return [pWindow isOnActiveSpace];
265}
266
267bool darwinScreensHaveSeparateSpaces()
268{
269 /* Check whether screens have separate spaces.
270 * This method is available since 10.9 only. */
271 if ([NSScreen respondsToSelector: @selector(screensHaveSeparateSpaces)])
272 return [NSScreen performSelector: @selector(screensHaveSeparateSpaces)];
273 else
274 return false;
275}
276
277bool darwinIsScrollerStyleOverlay()
278{
279 /* Check whether scrollers by default have legacy style.
280 * This method is available since 10.7 only. */
281 if ([NSScroller respondsToSelector: @selector(preferredScrollerStyle)])
282 {
283 const int enmType = (int)(intptr_t)[NSScroller performSelector: @selector(preferredScrollerStyle)];
284 return enmType == NSScrollerStyleOverlay;
285 }
286 else
287 return false;
288}
289
290/**
291 * Calls the + (void)setMouseCoalescingEnabled:(BOOL)flag class method.
292 *
293 * @param fEnabled Whether to enable or disable coalescing.
294 */
295void darwinSetMouseCoalescingEnabled(bool fEnabled)
296{
297 [NSEvent setMouseCoalescingEnabled:fEnabled];
298}
299
300void darwinWindowAnimateResizeImpl(NativeNSWindowRef pWindow, int x, int y, int width, int height)
301{
302 RT_NOREF(x, y, width);
303
304 /* It seems that Qt doesn't return the height of the window with the
305 * toolbar height included. So add this size manually. Could easily be that
306 * the Trolls fix this in the final release. */
307 NSToolbar *toolbar = [pWindow toolbar];
308 NSRect windowFrame = [pWindow frame];
309 int toolbarHeight = 0;
310 if(toolbar && [toolbar isVisible])
311 toolbarHeight = NSHeight(windowFrame) - NSHeight([[pWindow contentView] frame]);
312 int h = height + toolbarHeight;
313 int h1 = h - NSHeight(windowFrame);
314 windowFrame.size.height = h;
315 windowFrame.origin.y -= h1;
316
317 [pWindow setFrame:windowFrame display:YES animate: YES];
318}
319
320void darwinWindowAnimateResizeNewImpl(NativeNSWindowRef pWindow, int height, bool fAnimate)
321{
322 /* It seems that Qt doesn't return the height of the window with the
323 * toolbar height included. So add this size manually. Could easily be that
324 * the Trolls fix this in the final release. */
325 NSToolbar *toolbar = [pWindow toolbar];
326 NSRect windowFrame = [pWindow frame];
327 int toolbarHeight = 0;
328 if(toolbar && [toolbar isVisible])
329 toolbarHeight = NSHeight(windowFrame) - NSHeight([[pWindow contentView] frame]);
330 int h = height + toolbarHeight;
331 int h1 = h - NSHeight(windowFrame);
332 windowFrame.size.height = h;
333 windowFrame.origin.y -= h1;
334
335 [pWindow setFrame:windowFrame display:YES animate: fAnimate ? YES : NO];
336}
337
338void darwinTest(NativeNSViewRef pViewOld, NativeNSViewRef pViewNew, int h)
339{
340 NSMutableDictionary *pDicts[3] = { nil, nil, nil };
341 int c = 0;
342
343 /* Scaling necessary? */
344 if (h != -1)
345 {
346 NSWindow *pWindow = [(pViewOld ? pViewOld : pViewNew) window];
347 NSToolbar *toolbar = [pWindow toolbar];
348 NSRect windowFrame = [pWindow frame];
349 /* Dictionary containing all animation parameters. */
350 pDicts[c] = [NSMutableDictionary dictionaryWithCapacity:2];
351 /* Specify the animation target. */
352 [pDicts[c] setObject:pWindow forKey:NSViewAnimationTargetKey];
353 /* Scaling effect. */
354 [pDicts[c] setObject:[NSValue valueWithRect:windowFrame] forKey:NSViewAnimationStartFrameKey];
355 int toolbarHeight = 0;
356 if(toolbar && [toolbar isVisible])
357 toolbarHeight = NSHeight(windowFrame) - NSHeight([[pWindow contentView] frame]);
358 int h1 = h + toolbarHeight;
359 int h2 = h1 - NSHeight(windowFrame);
360 windowFrame.size.height = h1;
361 windowFrame.origin.y -= h2;
362 [pDicts[c] setObject:[NSValue valueWithRect:windowFrame] forKey:NSViewAnimationEndFrameKey];
363 ++c;
364 }
365 /* Fade out effect. */
366 if (pViewOld)
367 {
368 /* Dictionary containing all animation parameters. */
369 pDicts[c] = [NSMutableDictionary dictionaryWithCapacity:2];
370 /* Specify the animation target. */
371 [pDicts[c] setObject:pViewOld forKey:NSViewAnimationTargetKey];
372 /* Fade out effect. */
373 [pDicts[c] setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
374 ++c;
375 }
376 /* Fade in effect. */
377 if (pViewNew)
378 {
379 /* Dictionary containing all animation parameters. */
380 pDicts[c] = [NSMutableDictionary dictionaryWithCapacity:2];
381 /* Specify the animation target. */
382 [pDicts[c] setObject:pViewNew forKey:NSViewAnimationTargetKey];
383 /* Fade in effect. */
384 [pDicts[c] setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
385 ++c;
386 }
387 /* Create our animation object. */
388 NSViewAnimation *pAni = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:pDicts count:c]];
389 [pAni setDuration:.15];
390 [pAni setAnimationCurve:NSAnimationEaseIn];
391 [pAni setAnimationBlockingMode:NSAnimationBlocking];
392// [pAni setAnimationBlockingMode:NSAnimationNonblockingThreaded];
393
394 /* Run the animation. */
395 [pAni startAnimation];
396 /* Cleanup */
397 [pAni release];
398}
399
400void darwinWindowInvalidateShadowImpl(NativeNSWindowRef pWindow)
401{
402 [pWindow invalidateShadow];
403}
404
405int darwinWindowToolBarHeight(NativeNSWindowRef pWindow)
406{
407 NSToolbar *toolbar = [pWindow toolbar];
408 NSRect windowFrame = [pWindow frame];
409 int toolbarHeight = 0;
410 int theight = (NSHeight([NSWindow contentRectForFrameRect:[pWindow frame] styleMask:[pWindow styleMask]]) - NSHeight([[pWindow contentView] frame]));
411 /* toolbar height: */
412 if(toolbar && [toolbar isVisible])
413 /* title bar height: */
414 toolbarHeight = NSHeight(windowFrame) - NSHeight([[pWindow contentView] frame]) - theight;
415
416 return toolbarHeight;
417}
418
419int darwinWindowTitleHeight(NativeNSWindowRef pWindow)
420{
421 NSView *pSuperview = [[pWindow standardWindowButton:NSWindowCloseButton] superview];
422 NSSize sz = [pSuperview frame].size;
423 return sz.height;
424}
425
426bool darwinIsToolbarVisible(NativeNSWindowRef pWindow)
427{
428 NSToolbar *pToolbar = [pWindow toolbar];
429
430 return [pToolbar isVisible] == YES;
431}
432
433bool darwinIsWindowMaximized(NativeNSWindowRef pWindow)
434{
435 /* Mac OS X API NSWindow isZoomed returns true even for almost maximized windows,
436 * So implementing this by ourseleves by comparing visible screen-frame & window-frame: */
437 NSRect windowFrame = [pWindow frame];
438 NSRect screenFrame = [[NSScreen mainScreen] visibleFrame];
439
440 return (windowFrame.origin.x == screenFrame.origin.x) &&
441 (windowFrame.origin.y == screenFrame.origin.y) &&
442 (windowFrame.size.width == screenFrame.size.width) &&
443 (windowFrame.size.height == screenFrame.size.height);
444}
445
446bool darwinOpenFile(NativeNSStringRef pstrFile)
447{
448 return [[NSWorkspace sharedWorkspace] openFile:pstrFile];
449}
450
451float darwinSmallFontSize()
452{
453 float size = [NSFont systemFontSizeForControlSize: NSSmallControlSize];
454
455 return size;
456}
457
458bool darwinMouseGrabEvents(const void *pvCocoaEvent, const void *pvCarbonEvent, void *pvUser)
459{
460 NSEvent *pEvent = (NSEvent*)pvCocoaEvent;
461 NSEventType EvtType = [pEvent type];
462 NSWindow *pWin = ::darwinToNativeWindow((QWidget*)pvUser);
463 if ( pWin == [pEvent window]
464 && ( EvtType == NSLeftMouseDown
465 || EvtType == NSLeftMouseUp
466 || EvtType == NSRightMouseDown
467 || EvtType == NSRightMouseUp
468 || EvtType == NSOtherMouseDown
469 || EvtType == NSOtherMouseUp
470 || EvtType == NSLeftMouseDragged
471 || EvtType == NSRightMouseDragged
472 || EvtType == NSOtherMouseDragged
473 || EvtType == NSMouseMoved
474 || EvtType == NSScrollWheel))
475 {
476 /* When the mouse position is not associated to the mouse cursor, the x
477 and y values are reported as delta values. */
478 float x = [pEvent deltaX];
479 float y = [pEvent deltaY];
480 if (EvtType == NSScrollWheel)
481 {
482 /* In the scroll wheel case we have to do some magic, cause a
483 normal scroll wheel on a mouse behaves different to a trackpad.
484 The following is used within Qt. We use the same to get a
485 similar behavior. */
486 if ([pEvent respondsToSelector:@selector(deviceDeltaX:)])
487 x = (float)(intptr_t)[pEvent performSelector:@selector(deviceDeltaX)] * 2;
488 else
489 x = qBound(-120, (int)(x * 10000), 120);
490 if ([pEvent respondsToSelector:@selector(deviceDeltaY:)])
491 y = (float)(intptr_t)[pEvent performSelector:@selector(deviceDeltaY)] * 2;
492 else
493 y = qBound(-120, (int)(y * 10000), 120);
494 }
495 /* Get the buttons which where pressed when this event occurs. We have
496 to use Carbon here, cause the Cocoa method [NSEvent pressedMouseButtons]
497 is >= 10.6. */
498 uint32 buttonMask = 0;
499 GetEventParameter((EventRef)pvCarbonEvent, kEventParamMouseChord, typeUInt32, 0,
500 sizeof(buttonMask), 0, &buttonMask);
501 /* Produce a Qt event out of our info. */
502 ::darwinSendMouseGrabEvents((QWidget*)pvUser, EvtType, [pEvent buttonNumber], buttonMask, x, y);
503 return true;
504 }
505 return false;
506}
507
508/* Cocoa event handler which checks if the user right clicked at the unified
509 toolbar or the title area. */
510bool darwinUnifiedToolbarEvents(const void *pvCocoaEvent, const void *pvCarbonEvent, void *pvUser)
511{
512 RT_NOREF(pvCarbonEvent);
513
514 NSEvent *pEvent = (NSEvent*)pvCocoaEvent;
515 NSEventType EvtType = [pEvent type];
516 NSWindow *pWin = ::darwinToNativeWindow((QWidget*)pvUser);
517 /* First check for the right event type and that we are processing events
518 from the window which was registered by the user. */
519 if ( EvtType == NSRightMouseDown
520 && pWin == [pEvent window])
521 {
522 /* Get the mouse position of the event (screen coordinates) */
523 NSPoint point = [NSEvent mouseLocation];
524 /* Get the frame rectangle of the window (screen coordinates) */
525 NSRect winFrame = [pWin frame];
526 /* Calculate the height of the title and the toolbar */
527 int i = NSHeight(winFrame) - NSHeight([[pWin contentView] frame]);
528 /* Based on that height create a rectangle of the unified toolbar + title */
529 winFrame.origin.y += winFrame.size.height - i;
530 winFrame.size.height = i;
531 /* Check if the mouse press event was on the unified toolbar or title */
532 if (NSMouseInRect(point, winFrame, NO))
533 /* Create a Qt context menu event, with flipped screen coordinates */
534 ::darwinCreateContextMenuEvent(pvUser, point.x, NSHeight([[pWin screen] frame]) - point.y);
535 }
536 return false;
537}
538
539/**
540 * Check for some default application key combinations a Mac user expect, like
541 * CMD+Q or CMD+H.
542 *
543 * @returns true if such a key combo was hit, false otherwise.
544 * @param pEvent The Cocoa event.
545 */
546bool darwinIsApplicationCommand(ConstNativeNSEventRef pEvent)
547{
548 NSEventType eEvtType = [pEvent type];
549 bool fGlobalHotkey = false;
550//
551// if ( (eEvtType == NSKeyDown || eEvtType == NSKeyUp)
552// && [[NSApp mainMenu] performKeyEquivalent:pEvent])
553// return true;
554// return false;
555// && [[[NSApp mainMenu] delegate] menuHasKeyEquivalent:[NSApp mainMenu] forEvent:pEvent target:b action:a])
556
557 switch (eEvtType)
558 {
559 case NSKeyDown:
560 case NSKeyUp:
561 {
562 NSUInteger fEvtMask = [pEvent modifierFlags];
563 unsigned short KeyCode = [pEvent keyCode];
564 if ( ((fEvtMask & (NX_NONCOALSESCEDMASK | NX_COMMANDMASK | NX_DEVICELCMDKEYMASK)) == (NX_NONCOALSESCEDMASK | NX_COMMANDMASK | NX_DEVICELCMDKEYMASK)) /* L+CMD */
565 || ((fEvtMask & (NX_NONCOALSESCEDMASK | NX_COMMANDMASK | NX_DEVICERCMDKEYMASK)) == (NX_NONCOALSESCEDMASK | NX_COMMANDMASK | NX_DEVICERCMDKEYMASK))) /* R+CMD */
566 {
567 if ( KeyCode == 0x0c /* CMD+Q (Quit) */
568 || KeyCode == 0x04) /* CMD+H (Hide) */
569 fGlobalHotkey = true;
570 }
571 else if ( ((fEvtMask & (NX_NONCOALSESCEDMASK | NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_COMMANDMASK | NX_DEVICELCMDKEYMASK)) == (NX_NONCOALSESCEDMASK | NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_COMMANDMASK | NX_DEVICELCMDKEYMASK)) /* L+ALT+CMD */
572 || ((fEvtMask & (NX_NONCOALSESCEDMASK | NX_ALTERNATEMASK | NX_DEVICERCMDKEYMASK | NX_COMMANDMASK | NX_DEVICERCMDKEYMASK)) == (NX_NONCOALSESCEDMASK | NX_ALTERNATEMASK | NX_DEVICERCMDKEYMASK | NX_COMMANDMASK | NX_DEVICERCMDKEYMASK))) /* R+ALT+CMD */
573 {
574 if (KeyCode == 0x04) /* ALT+CMD+H (Hide-Others) */
575 fGlobalHotkey = true;
576 }
577 break;
578 }
579 default: break;
580 }
581 return fGlobalHotkey;
582}
583
584void darwinRetranslateAppMenu()
585{
586 /* This is purely Qt internal. If the Trolls change something here, it will
587 not work anymore, but at least it will not be a burning man. */
588 if ([NSApp respondsToSelector:@selector(qt_qcocoamenuLoader)])
589 {
590 id loader = [NSApp performSelector:@selector(qt_qcocoamenuLoader)];
591 if ([loader respondsToSelector:@selector(qtTranslateApplicationMenu)])
592 [loader performSelector:@selector(qtTranslateApplicationMenu)];
593 }
594}
595
596/* Our resize proxy singleton. This class has two major tasks. First it is used
597 to proxy the windowWillResize selector of the Qt delegate. As this is class
598 global and therewith done for all Qt window instances, we have to track the
599 windows we are interested in. This is the second task. */
600@interface UIResizeProxy: NSObject
601{
602 NSMutableArray *m_pArray;
603 bool m_fInit;
604}
605+(UIResizeProxy*)sharedResizeProxy;
606-(void)addWindow:(NSWindow*)pWindow;
607-(void)removeWindow:(NSWindow*)pWindow;
608-(BOOL)containsWindow:(NSWindow*)pWindow;
609@end
610
611static UIResizeProxy *gSharedResizeProxy = nil;
612
613@implementation UIResizeProxy
614+(UIResizeProxy*)sharedResizeProxy
615{
616 if (gSharedResizeProxy == nil)
617 gSharedResizeProxy = [[super allocWithZone:NULL] init];
618 return gSharedResizeProxy;
619}
620-(id)init
621{
622 self = [super init];
623
624 m_fInit = false;
625
626 return self;
627}
628- (void)addWindow:(NSWindow*)pWindow
629{
630 if (!m_fInit)
631 {
632 /* Create an array which contains the registered windows. */
633 m_pArray = [[NSMutableArray alloc] init];
634 /* Swizzle the windowWillResize method. This means replacing the
635 original method with our own one and reroute the original one to
636 another name. */
637 Class oriClass = [[pWindow delegate] class];
638 Class myClass = [UIResizeProxy class];
639 SEL oriSel = @selector(windowWillResize:toSize:);
640 SEL qtSel = @selector(qtWindowWillResize:toSize:);
641 Method m1 = class_getInstanceMethod(oriClass, oriSel);
642 Method m2 = class_getInstanceMethod(myClass, oriSel);
643 Method m3 = class_getInstanceMethod(myClass, qtSel);
644 /* Overwrite the original implementation with our own one. old contains
645 the old implementation. */
646 IMP old = method_setImplementation(m1, method_getImplementation(m2));
647 /* Add a new method to our class with the old implementation. */
648 class_addMethod(oriClass, qtSel, old, method_getTypeEncoding(m3));
649 m_fInit = true;
650 }
651 [m_pArray addObject:pWindow];
652}
653- (void)removeWindow:(NSWindow*)pWindow
654{
655 [m_pArray removeObject:pWindow];
656}
657- (BOOL)containsWindow:(NSWindow*)pWindow
658{
659 return [m_pArray containsObject:pWindow];
660}
661- (NSSize)qtWindowWillResize:(NSWindow *)pWindow toSize:(NSSize)newFrameSize
662{
663 RT_NOREF(pWindow);
664 /* This is a fake implementation. It will be replaced by the original Qt
665 method. */
666 return newFrameSize;
667}
668- (NSSize)windowWillResize:(NSWindow *)pWindow toSize:(NSSize)newFrameSize
669{
670 /* Call the original implementation for newFrameSize. */
671 NSSize qtSize = [self qtWindowWillResize:pWindow toSize:newFrameSize];
672 /* Check if we are responsible for this window. */
673 if (![[UIResizeProxy sharedResizeProxy] containsWindow:pWindow])
674 return qtSize;
675 /* The static modifier method in NSEvent is >= 10.6. It allows us to check
676 the shift modifier state during the resize. If it is not available the
677 user have to press shift before he start to resize. */
678 if ([NSEvent respondsToSelector:@selector(modifierFlags)])
679 {
680 if (((int)(intptr_t)[NSEvent performSelector:@selector(modifierFlags)] & NSShiftKeyMask) == NSShiftKeyMask)
681 return qtSize;
682 }
683 else
684 {
685 /* Shift key pressed when this resize event was initiated? */
686 if (([pWindow resizeFlags] & NSShiftKeyMask) == NSShiftKeyMask)
687 return qtSize;
688 }
689 /* The default case is to calculate the aspect radio of the old size and
690 used it for the new size. */
691 NSSize s = [pWindow frame].size;
692 double a = (double)s.width / s.height;
693 NSSize newSize = NSMakeSize(newFrameSize.width, newFrameSize.width / a);
694 /* We have to make sure the new rectangle meets the minimum requirements. */
695 NSSize testSize = [self qtWindowWillResize:pWindow toSize:newSize];
696 if ( testSize.width > newSize.width
697 || testSize.height > newSize.height)
698 {
699 double w1 = testSize.width / newSize.width;
700 double h1 = testSize.height / newSize.height;
701 if ( w1 > 1
702 && w1 > h1)
703 {
704 newSize.width = testSize.width;
705 newSize.height = testSize.width * a;
706 }else if (h1 > 1)
707 {
708 newSize.width = testSize.height * a;
709 newSize.height = testSize.height;
710 }
711 }
712 return newSize;
713}
714@end
715
716void darwinInstallResizeDelegate(NativeNSWindowRef pWindow)
717{
718 [[UIResizeProxy sharedResizeProxy] addWindow:pWindow];
719}
720
721void darwinUninstallResizeDelegate(NativeNSWindowRef pWindow)
722{
723 [[UIResizeProxy sharedResizeProxy] removeWindow:pWindow];
724}
725
726void *darwinCocoaToCarbonEvent(void *pvCocoaEvent)
727{
728 NSEvent *pEvent = (NSEvent*)pvCocoaEvent;
729 return (void*)[pEvent eventRef];
730}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use