source: trunk/src/gui/kernel/qcocoasharedwindowmethods_mac_p.h

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

  • Property svn:eol-style set to native
File size: 17.3 KB
Line 
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 QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42/****************************************************************************
43 NB: This is not a header file, dispite the file name suffix. This file is
44 included directly into the source code of qcocoawindow_mac.mm and
45 qcocoapanel_mac.mm to avoid manually doing copy and paste of the exact
46 same code needed at both places. This solution makes it more difficult
47 to e.g fix a bug in qcocoawindow_mac.mm, but forget to do the same in
48 qcocoapanel_mac.mm.
49 The reason we need to do copy and paste in the first place, rather than
50 resolve to method overriding, is that QCocoaPanel needs to inherit from
51 NSPanel, while QCocoaWindow needs to inherit NSWindow rather than NSPanel).
52****************************************************************************/
53
54// WARNING: Don't include any header files from within this file. Put them
55// directly into qcocoawindow_mac_p.h and qcocoapanel_mac_p.h
56
57QT_BEGIN_NAMESPACE
58extern Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum); // qcocoaview.mm
59extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp
60extern const QStringList& qEnabledDraggedTypes(); // qmime_mac.cpp
61extern void qt_event_request_window_change(QWidget *); // qapplication_mac.mm
62extern void qt_mac_send_posted_gl_updates(QWidget *widget); // qapplication_mac.mm
63
64Q_GLOBAL_STATIC(QPointer<QWidget>, currentDragTarget);
65
66QT_END_NAMESPACE
67
68- (id)initWithContentRect:(NSRect)contentRect
69 styleMask:(NSUInteger)windowStyle
70 backing:(NSBackingStoreType)bufferingType
71 defer:(BOOL)deferCreation
72{
73 self = [super initWithContentRect:contentRect styleMask:windowStyle
74 backing:bufferingType defer:deferCreation];
75 if (self) {
76 currentCustomDragTypes = 0;
77 }
78 return self;
79}
80
81- (void)dealloc
82{
83 delete currentCustomDragTypes;
84 [super dealloc];
85}
86
87- (BOOL)canBecomeKeyWindow
88{
89 QWidget *widget = [self QT_MANGLE_NAMESPACE(qt_qwidget)];
90 if (!widget)
91 return NO; // This should happen only for qt_root_win
92
93 bool isToolTip = (widget->windowType() == Qt::ToolTip);
94 bool isPopup = (widget->windowType() == Qt::Popup);
95 return !(isPopup || isToolTip);
96}
97
98- (BOOL)canBecomeMainWindow
99{
100 QWidget *widget = [self QT_MANGLE_NAMESPACE(qt_qwidget)];
101 if (!widget)
102 return NO; // This should happen only for qt_root_win
103
104 bool isToolTip = (widget->windowType() == Qt::ToolTip);
105 bool isPopup = (widget->windowType() == Qt::Popup);
106 bool isTool = (widget->windowType() == Qt::Tool);
107 return !(isPopup || isToolTip || isTool);
108}
109
110- (void)becomeMainWindow
111{
112 [super becomeMainWindow];
113 // Cocoa sometimes tell a hidden window to become the
114 // main window (and as such, show it). This can e.g
115 // happend when the application gets activated. If
116 // this is the case, we tell it to hide again:
117 if (![self isVisible])
118 [self orderOut:self];
119}
120
121- (void)toggleToolbarShown:(id)sender
122{
123 macSendToolbarChangeEvent([self QT_MANGLE_NAMESPACE(qt_qwidget)]);
124 [super toggleToolbarShown:sender];
125}
126
127- (void)flagsChanged:(NSEvent *)theEvent
128{
129 qt_dispatchModifiersChanged(theEvent, [self QT_MANGLE_NAMESPACE(qt_qwidget)]);
130 [super flagsChanged:theEvent];
131}
132
133
134- (void)tabletProximity:(NSEvent *)tabletEvent
135{
136 qt_dispatchTabletProximityEvent(tabletEvent);
137}
138
139- (void)qtDispatcherToQAction:(id)sender
140{
141 // If this window is modal, the menu bar will be modally shaddowed.
142 // In that case, since the window will be in the first responder chain,
143 // we can still catch the trigger here and forward it to the menu bar.
144 // This is needed as a single modal dialog on Qt should be able to access
145 // the application menu (e.g. quit).
146 [[NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)] qtDispatcherToQAction:sender];
147}
148
149- (void)terminate:(id)sender
150{
151 // This function is called from the quit item in the menubar when this window
152 // is in the first responder chain (see also qtDispatcherToQAction above)
153 [NSApp terminate:sender];
154}
155
156- (void)sendEvent:(NSEvent *)event
157{
158 if ([event type] == NSApplicationDefined) {
159 switch ([event subtype]) {
160 case QtCocoaEventSubTypePostMessage:
161 [NSApp qt_sendPostedMessage:event];
162 return;
163 default:
164 break;
165 }
166 return;
167 }
168
169 QWidget *widget = [[QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate] qt_qwidgetForWindow:self];
170 // Cocoa can hold onto the window after we've disavowed its knowledge. So,
171 // if we get sent an event afterwards just have it go through the super's
172 // version and don't do any stuff with Qt.
173 if (!widget) {
174 [super sendEvent:event];
175 return;
176 }
177
178 [self retain];
179 QT_MANGLE_NAMESPACE(QCocoaView) *view = static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(qt_mac_nativeview_for(widget));
180 Qt::MouseButton mouseButton = cocoaButton2QtButton([event buttonNumber]);
181
182 bool handled = false;
183 // sometimes need to redirect mouse events to the popup.
184 QWidget *popup = qAppInstance()->activePopupWidget();
185 if (popup && popup != widget) {
186 switch([event type])
187 {
188 case NSLeftMouseDown:
189 if (!qt_button_down)
190 qt_button_down = widget;
191 handled = qt_mac_handleMouseEvent(view, event, QEvent::MouseButtonPress, mouseButton);
192 // Don't call super here. This prevents us from getting the mouseUp event,
193 // which we need to send even if the mouseDown event was not accepted.
194 // (this is standard Qt behavior.)
195 break;
196 case NSRightMouseDown:
197 case NSOtherMouseDown:
198 if (!qt_button_down)
199 qt_button_down = widget;
200 handled = qt_mac_handleMouseEvent(view, event, QEvent::MouseButtonPress, mouseButton);
201 break;
202 case NSLeftMouseUp:
203 case NSRightMouseUp:
204 case NSOtherMouseUp:
205 handled = qt_mac_handleMouseEvent(view, event, QEvent::MouseButtonRelease, mouseButton);
206 qt_button_down = 0;
207 break;
208 case NSMouseMoved:
209 handled = qt_mac_handleMouseEvent(view, event, QEvent::MouseMove, Qt::NoButton);
210 break;
211 case NSLeftMouseDragged:
212 case NSRightMouseDragged:
213 case NSOtherMouseDragged:
214 [QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent]->view = view;
215 [QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent]->theEvent = event;
216 handled = qt_mac_handleMouseEvent(view, event, QEvent::MouseMove, mouseButton);
217 break;
218 default:
219 [super sendEvent:event];
220 break;
221 }
222 } else {
223 [super sendEvent:event];
224 }
225
226 if (!handled)
227 qt_mac_dispatchNCMouseMessage(self, event, [self QT_MANGLE_NAMESPACE(qt_qwidget)], leftButtonIsRightButton);
228
229 [self release];
230}
231
232- (void)setInitialFirstResponder:(NSView *)view
233{
234 // This method is called the first time the window is placed on screen and
235 // is the earliest point in time we can connect OpenGL contexts to NSViews.
236 QWidget *qwidget = [[QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate] qt_qwidgetForWindow:self];
237 if (qwidget) {
238 qt_event_request_window_change(qwidget);
239 qt_mac_send_posted_gl_updates(qwidget);
240 }
241
242 [super setInitialFirstResponder:view];
243}
244
245- (BOOL)makeFirstResponder:(NSResponder *)responder
246{
247 // For some reason Cocoa wants to flip the first responder
248 // when Qt doesn't want to, sorry, but "No" :-)
249 if (responder == nil && qApp->focusWidget())
250 return NO;
251 return [super makeFirstResponder:responder];
252}
253
254+ (Class)frameViewClassForStyleMask:(NSUInteger)styleMask
255{
256 if (styleMask & QtMacCustomizeWindow)
257 return [QT_MANGLE_NAMESPACE(QCocoaWindowCustomThemeFrame) class];
258 return [super frameViewClassForStyleMask:styleMask];
259}
260
261-(void)registerDragTypes
262{
263 // Calling registerForDraggedTypes below is slow, so only do
264 // it once for each window, or when the custom types change.
265 QMacCocoaAutoReleasePool pool;
266 const QStringList& customTypes = qEnabledDraggedTypes();
267 if (currentCustomDragTypes == 0 || *currentCustomDragTypes != customTypes) {
268 if (currentCustomDragTypes == 0)
269 currentCustomDragTypes = new QStringList();
270 *currentCustomDragTypes = customTypes;
271 const NSString* mimeTypeGeneric = @"com.trolltech.qt.MimeTypeName";
272 NSMutableArray *supportedTypes = [NSMutableArray arrayWithObjects:NSColorPboardType,
273 NSFilenamesPboardType, NSStringPboardType,
274 NSFilenamesPboardType, NSPostScriptPboardType, NSTIFFPboardType,
275 NSRTFPboardType, NSTabularTextPboardType, NSFontPboardType,
276 NSRulerPboardType, NSFileContentsPboardType, NSColorPboardType,
277 NSRTFDPboardType, NSHTMLPboardType, NSPICTPboardType,
278 NSURLPboardType, NSPDFPboardType, NSVCardPboardType,
279 NSFilesPromisePboardType, NSInkTextPboardType,
280 NSMultipleTextSelectionPboardType, mimeTypeGeneric, nil];
281 // Add custom types supported by the application.
282 for (int i = 0; i < customTypes.size(); i++) {
283 [supportedTypes addObject:reinterpret_cast<const NSString *>(QCFString::toCFStringRef(customTypes[i]))];
284 }
285 [self registerForDraggedTypes:supportedTypes];
286 }
287}
288
289- (QWidget *)dragTargetHitTest:(id <NSDraggingInfo>)sender
290{
291 // Do a hittest to find the NSView under the
292 // mouse, and return the corresponding QWidget:
293 NSPoint windowPoint = [sender draggingLocation];
294 NSView *candidateView = [[self contentView] hitTest:windowPoint];
295 if (![candidateView isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaView) class]])
296 return 0;
297 return [static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(candidateView) qt_qwidget];
298}
299
300- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
301{
302 // The user dragged something into the window. Send a draggingEntered message
303 // to the QWidget under the mouse. As the drag moves over the window, and over
304 // different widgets, we will handle enter and leave events from within
305 // draggingUpdated below. The reason why we handle this ourselves rather than
306 // subscribing for drag events directly in QCocoaView is that calling
307 // registerForDraggedTypes on the views will severly degrade initialization time
308 // for an application that uses a lot of drag subscribing widgets.
309
310 QWidget *target = [self dragTargetHitTest:sender];
311 if (!target)
312 return [super draggingEntered:sender];
313 if (target->testAttribute(Qt::WA_DropSiteRegistered) == false)
314 return NSDragOperationNone;
315
316 *currentDragTarget() = target;
317 return [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) draggingEntered:sender];
318 }
319
320- (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender
321{
322 QWidget *target = [self dragTargetHitTest:sender];
323 if (!target)
324 return [super draggingUpdated:sender];
325
326 if (target == *currentDragTarget()) {
327 // The drag continues to move over the widget that we have sendt
328 // a draggingEntered message to. So just update the view:
329 return [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) draggingUpdated:sender];
330 } else {
331 // The widget under the mouse has changed.
332 // So we need to fake enter/leave events:
333 if (*currentDragTarget())
334 [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) draggingExited:sender];
335 if (target->testAttribute(Qt::WA_DropSiteRegistered) == false) {
336 *currentDragTarget() = 0;
337 return NSDragOperationNone;
338 }
339 *currentDragTarget() = target;
340 return [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) draggingEntered:sender];
341 }
342}
343
344- (void)draggingExited:(id < NSDraggingInfo >)sender
345{
346 QWidget *target = [self dragTargetHitTest:sender];
347 if (!target)
348 return [super draggingExited:sender];
349
350 if (*currentDragTarget()) {
351 [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) draggingExited:sender];
352 *currentDragTarget() = 0;
353 }
354}
355
356- (BOOL)performDragOperation:(id < NSDraggingInfo >)sender
357{
358 QWidget *target = [self dragTargetHitTest:sender];
359 if (!target)
360 return [super performDragOperation:sender];
361
362 BOOL dropResult = NO;
363 if (*currentDragTarget()) {
364 dropResult = [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) performDragOperation:sender];
365 *currentDragTarget() = 0;
366 }
367 return dropResult;
368}
369
370- (void)displayIfNeeded
371{
372
373 QWidget *qwidget = [[QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate] qt_qwidgetForWindow:self];
374 if (qwidget == 0) {
375 [super displayIfNeeded];
376 return;
377 }
378
379 if (QApplicationPrivate::graphicsSystem() != 0) {
380 if (QWidgetBackingStore *bs = qt_widget_private(qwidget)->maybeBackingStore())
381 bs->sync(qwidget, qwidget->rect());
382 }
383 [super displayIfNeeded];
384}
385
386// This is a hack and it should be removed once we find the real cause for
387// the painting problems.
388// We have a static variable that signals if we have been called before or not.
389static bool firstDrawingInvocation = true;
390
391// The method below exists only as a workaround to draw/not draw the baseline
392// in the title bar. This is to support unifiedToolbar look.
393
394// This method is very special. To begin with, it is a
395// method that will get called only if we enable documentMode.
396// Furthermore, it won't get called as a normal method, we swap
397// this method with the normal implementation of drawRect in
398// _NSThemeFrame. When this method is active, its mission is to
399// first call the original drawRect implementation so the widget
400// gets proper painting. After that, it needs to detect if there
401// is a toolbar or not, in order to decide how to handle the unified
402// look. The distinction is important since the presence and
403// visibility of a toolbar change the way we enter into unified mode.
404// When there is a toolbar and that toolbar is visible, the problem
405// is as simple as to tell the toolbar not to draw its baseline.
406// However when there is not toolbar or the toolbar is not visible,
407// we need to draw a line on top of the baseline, because the baseline
408// in that case will belong to the title. For this case we need to draw
409// a line on top of the baseline.
410// As usual, there is a special case. When we first are called, we might
411// need to repaint ourselves one more time. We only need that if we
412// didn't get the activation, i.e. when we are launched via the command
413// line. And this only if the toolbar is visible from the beginning,
414// so we have a special flag that signals if we need to repaint or not.
415- (void)drawRectSpecial:(NSRect)rect
416{
417 // Call the original drawing method.
418 [self drawRectOriginal:rect];
419 NSWindow *window = [self window];
420 NSToolbar *toolbar = [window toolbar];
421 if(!toolbar) {
422 // There is no toolbar, we have to draw a line on top of the line drawn by Cocoa.
423 macDrawRectOnTop((void *)window);
424 } else {
425 if([toolbar isVisible]) {
426 // We tell Cocoa to avoid drawing the line at the end.
427 if(firstDrawingInvocation) {
428 firstDrawingInvocation = false;
429 macSyncDrawingOnFirstInvocation((void *)window);
430 } else
431 [toolbar setShowsBaselineSeparator:NO];
432 } else {
433 // There is a toolbar but it is not visible so
434 // we have to draw a line on top of the line drawn by Cocoa.
435 macDrawRectOnTop((void *)window);
436 }
437 }
438}
Note: See TracBrowser for help on using the repository browser.