src/gui/kernel/qcocoasharedwindowmethods_mac_p.h
changeset 30 5dc02b23752f
parent 18 2f34d5167611
child 33 3e2da88830cd
equal deleted inserted replaced
29:b72c6db6890b 30:5dc02b23752f
    55 // directly into qcocoawindow_mac_p.h and qcocoapanel_mac_p.h
    55 // directly into qcocoawindow_mac_p.h and qcocoapanel_mac_p.h
    56 
    56 
    57 QT_BEGIN_NAMESPACE
    57 QT_BEGIN_NAMESPACE
    58 extern Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum); // qcocoaview.mm
    58 extern Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum); // qcocoaview.mm
    59 extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp
    59 extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp
       
    60 extern const QStringList& qEnabledDraggedTypes(); // qmime_mac.cpp
       
    61 
       
    62 Q_GLOBAL_STATIC(QPointer<QWidget>, currentDragTarget);
       
    63 
    60 QT_END_NAMESPACE
    64 QT_END_NAMESPACE
       
    65 
       
    66 - (id)initWithContentRect:(NSRect)contentRect
       
    67     styleMask:(NSUInteger)windowStyle
       
    68     backing:(NSBackingStoreType)bufferingType
       
    69     defer:(BOOL)deferCreation
       
    70 {
       
    71     self = [super initWithContentRect:contentRect styleMask:windowStyle
       
    72         backing:bufferingType defer:deferCreation];
       
    73     if (self) {
       
    74         currentCustomDragTypes = 0;
       
    75     }
       
    76     return self;
       
    77 }
       
    78 
       
    79 - (void)dealloc
       
    80 {
       
    81     delete currentCustomDragTypes;
       
    82     [super dealloc];
       
    83 }
    61 
    84 
    62 - (BOOL)canBecomeKeyWindow
    85 - (BOOL)canBecomeKeyWindow
    63 {
    86 {
    64     QWidget *widget = [self QT_MANGLE_NAMESPACE(qt_qwidget)];
    87     QWidget *widget = [self QT_MANGLE_NAMESPACE(qt_qwidget)];
    65 
    88 
    66     bool isToolTip = (widget->windowType() == Qt::ToolTip);
    89     bool isToolTip = (widget->windowType() == Qt::ToolTip);
    67     bool isPopup = (widget->windowType() == Qt::Popup);
    90     bool isPopup = (widget->windowType() == Qt::Popup);
    68     return !(isPopup || isToolTip);
    91     return !(isPopup || isToolTip);
    69 }
    92 }
    70 
    93 
       
    94 - (BOOL)canBecomeMainWindow
       
    95 {
       
    96     QWidget *widget = [self QT_MANGLE_NAMESPACE(qt_qwidget)];
       
    97 
       
    98     bool isToolTip = (widget->windowType() == Qt::ToolTip);
       
    99     bool isPopup = (widget->windowType() == Qt::Popup);
       
   100     bool isTool = (widget->windowType() == Qt::Tool);
       
   101     return !(isPopup || isToolTip || isTool);
       
   102 }
       
   103 
       
   104 - (void)becomeMainWindow
       
   105 {
       
   106     [super becomeMainWindow];
       
   107     // Cocoa sometimes tell a hidden window to become the
       
   108     // main window (and as such, show it). This can e.g
       
   109     // happend when the application gets activated. If
       
   110     // this is the case, we tell it to hide again:
       
   111     if (![self isVisible])
       
   112         [self orderOut:self];
       
   113 }
       
   114 
    71 - (void)toggleToolbarShown:(id)sender
   115 - (void)toggleToolbarShown:(id)sender
    72 {
   116 {
    73     macSendToolbarChangeEvent([self QT_MANGLE_NAMESPACE(qt_qwidget)]);
   117     macSendToolbarChangeEvent([self QT_MANGLE_NAMESPACE(qt_qwidget)]);
    74     [super toggleToolbarShown:sender];
   118     [super toggleToolbarShown:sender];
    75 }
   119 }
    76 
   120 
    77 /*
       
    78     The methods keyDown, keyUp, and flagsChanged... These really shouldn't ever
       
    79     get hit. We automatically say we can be first responder if we are a window.
       
    80     So, the handling should get handled by the view. This is here more as a
       
    81     last resort (i.e., this is code that can potentially be removed).
       
    82  */
       
    83 - (void)keyDown:(NSEvent *)theEvent
       
    84 {
       
    85     bool keyOK = qt_dispatchKeyEvent(theEvent, [self QT_MANGLE_NAMESPACE(qt_qwidget)]);
       
    86     if (!keyOK)
       
    87         [super keyDown:theEvent];
       
    88 }
       
    89 
       
    90 - (void)keyUp:(NSEvent *)theEvent
       
    91 {
       
    92     bool keyOK = qt_dispatchKeyEvent(theEvent, [self QT_MANGLE_NAMESPACE(qt_qwidget)]);
       
    93     if (!keyOK)
       
    94         [super keyUp:theEvent];
       
    95 }
       
    96 
       
    97 - (void)flagsChanged:(NSEvent *)theEvent
   121 - (void)flagsChanged:(NSEvent *)theEvent
    98 {
   122 {
    99     qt_dispatchModifiersChanged(theEvent, [self QT_MANGLE_NAMESPACE(qt_qwidget)]);
   123     qt_dispatchModifiersChanged(theEvent, [self QT_MANGLE_NAMESPACE(qt_qwidget)]);
   100     [super flagsChanged:theEvent];
   124     [super flagsChanged:theEvent];
   101 }
   125 }
   104 - (void)tabletProximity:(NSEvent *)tabletEvent
   128 - (void)tabletProximity:(NSEvent *)tabletEvent
   105 {
   129 {
   106     qt_dispatchTabletProximityEvent(tabletEvent);
   130     qt_dispatchTabletProximityEvent(tabletEvent);
   107 }
   131 }
   108 
   132 
       
   133 - (void)qtDispatcherToQAction:(id)sender
       
   134 {
       
   135     // If this window is modal, the menu bar will be modally shaddowed.
       
   136     // In that case, since the window will be in the first responder chain,
       
   137     // we can still catch the trigger here and forward it to the menu bar.
       
   138     // This is needed as a single modal dialog on Qt should be able to access
       
   139     // the application menu (e.g. quit).
       
   140     [[NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)] qtDispatcherToQAction:sender];
       
   141 }
       
   142 
       
   143 - (void)terminate:(id)sender
       
   144 {
       
   145     // This function is called from the quit item in the menubar when this window
       
   146     // is in the first responder chain (see also qtDispatcherToQAction above)
       
   147     [NSApp terminate:sender];
       
   148 }
       
   149 
   109 - (void)sendEvent:(NSEvent *)event
   150 - (void)sendEvent:(NSEvent *)event
   110 {
   151 {
       
   152     if ([event type] == NSApplicationDefined) {
       
   153         switch ([event subtype]) {
       
   154             case QtCocoaEventSubTypePostMessage:
       
   155                 [NSApp qt_sendPostedMessage:event];
       
   156                 return;
       
   157             default:
       
   158                 break;
       
   159         }
       
   160         return;
       
   161     }
       
   162 
   111     QWidget *widget = [[QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate] qt_qwidgetForWindow:self];
   163     QWidget *widget = [[QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate] qt_qwidgetForWindow:self];
   112 
       
   113     // Cocoa can hold onto the window after we've disavowed its knowledge. So,
   164     // Cocoa can hold onto the window after we've disavowed its knowledge. So,
   114     // if we get sent an event afterwards just have it go through the super's
   165     // if we get sent an event afterwards just have it go through the super's
   115     // version and don't do any stuff with Qt.
   166     // version and don't do any stuff with Qt.
   116     if (!widget) {
   167     if (!widget) {
   117         [super sendEvent:event];
   168         [super sendEvent:event];
   131         case NSLeftMouseDown:
   182         case NSLeftMouseDown:
   132             if (!qt_button_down)
   183             if (!qt_button_down)
   133                 qt_button_down = widget;
   184                 qt_button_down = widget;
   134             handled = qt_mac_handleMouseEvent(view, event, QEvent::MouseButtonPress, mouseButton);
   185             handled = qt_mac_handleMouseEvent(view, event, QEvent::MouseButtonPress, mouseButton);
   135             // Don't call super here. This prevents us from getting the mouseUp event,
   186             // Don't call super here. This prevents us from getting the mouseUp event,
   136             // which we need to send even if the mouseDown event was not accepted. 
   187             // which we need to send even if the mouseDown event was not accepted.
   137             // (this is standard Qt behavior.)
   188             // (this is standard Qt behavior.)
   138             break;
   189             break;
   139         case NSRightMouseDown:
   190         case NSRightMouseDown:
   140         case NSOtherMouseDown:
   191         case NSOtherMouseDown:
   141             if (!qt_button_down)
   192             if (!qt_button_down)
   186     if (styleMask & QtMacCustomizeWindow)
   237     if (styleMask & QtMacCustomizeWindow)
   187         return [QT_MANGLE_NAMESPACE(QCocoaWindowCustomThemeFrame) class];
   238         return [QT_MANGLE_NAMESPACE(QCocoaWindowCustomThemeFrame) class];
   188     return [super frameViewClassForStyleMask:styleMask];
   239     return [super frameViewClassForStyleMask:styleMask];
   189 }
   240 }
   190 
   241 
       
   242 -(void)registerDragTypes
       
   243 {
       
   244     // Calling registerForDraggedTypes below is slow, so only do
       
   245     // it once for each window, or when the custom types change.
       
   246     QMacCocoaAutoReleasePool pool;
       
   247     const QStringList& customTypes = qEnabledDraggedTypes();
       
   248     if (currentCustomDragTypes == 0 || *currentCustomDragTypes != customTypes) {
       
   249         if (currentCustomDragTypes == 0)
       
   250             currentCustomDragTypes = new QStringList();
       
   251         *currentCustomDragTypes = customTypes;
       
   252         const NSString* mimeTypeGeneric = @"com.trolltech.qt.MimeTypeName";
       
   253         NSMutableArray *supportedTypes = [NSMutableArray arrayWithObjects:NSColorPboardType,
       
   254                        NSFilenamesPboardType, NSStringPboardType,
       
   255                        NSFilenamesPboardType, NSPostScriptPboardType, NSTIFFPboardType,
       
   256                        NSRTFPboardType, NSTabularTextPboardType, NSFontPboardType,
       
   257                        NSRulerPboardType, NSFileContentsPboardType, NSColorPboardType,
       
   258                        NSRTFDPboardType, NSHTMLPboardType, NSPICTPboardType,
       
   259                        NSURLPboardType, NSPDFPboardType, NSVCardPboardType,
       
   260                        NSFilesPromisePboardType, NSInkTextPboardType,
       
   261                        NSMultipleTextSelectionPboardType, mimeTypeGeneric, nil];
       
   262         // Add custom types supported by the application.
       
   263         for (int i = 0; i < customTypes.size(); i++) {
       
   264            [supportedTypes addObject:reinterpret_cast<const NSString *>(QCFString::toCFStringRef(customTypes[i]))];
       
   265         }
       
   266         [self registerForDraggedTypes:supportedTypes];
       
   267     }
       
   268 }
       
   269 
       
   270 - (QWidget *)dragTargetHitTest:(id <NSDraggingInfo>)sender
       
   271 {
       
   272     // Do a hittest to find the NSView under the
       
   273     // mouse, and return the corresponding QWidget:
       
   274     NSPoint windowPoint = [sender draggingLocation];
       
   275     NSView *candidateView = [[self contentView] hitTest:windowPoint];
       
   276     if (![candidateView isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaView) class]])
       
   277         return 0;
       
   278     return [static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(candidateView) qt_qwidget];
       
   279 }
       
   280 
       
   281 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
       
   282 {
       
   283     // The user dragged something into the window. Send a draggingEntered message
       
   284     // to the QWidget under the mouse. As the drag moves over the window, and over
       
   285     // different widgets, we will handle enter and leave events from within
       
   286     // draggingUpdated below. The reason why we handle this ourselves rather than
       
   287     // subscribing for drag events directly in QCocoaView is that calling
       
   288     // registerForDraggedTypes on the views will severly degrade initialization time
       
   289     // for an application that uses a lot of drag subscribing widgets.
       
   290 
       
   291     QWidget *target = [self dragTargetHitTest:sender];
       
   292     if (!target)
       
   293         return [super draggingEntered:sender];
       
   294     if (target->testAttribute(Qt::WA_DropSiteRegistered) == false)
       
   295         return NSDragOperationNone;
       
   296 
       
   297     *currentDragTarget() = target;
       
   298     return [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) draggingEntered:sender];
       
   299  }
       
   300 
       
   301 - (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender
       
   302 {
       
   303     QWidget *target = [self dragTargetHitTest:sender];
       
   304     if (!target)
       
   305         return [super draggingUpdated:sender];
       
   306 
       
   307     if (target == *currentDragTarget()) {
       
   308         // The drag continues to move over the widget that we have sendt
       
   309         // a draggingEntered message to. So just update the view:
       
   310         return [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) draggingUpdated:sender];
       
   311     } else {
       
   312         // The widget under the mouse has changed.
       
   313         // So we need to fake enter/leave events:
       
   314         if (*currentDragTarget())
       
   315             [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) draggingExited:sender];
       
   316         if (target->testAttribute(Qt::WA_DropSiteRegistered) == false) {
       
   317             *currentDragTarget() = 0;
       
   318             return NSDragOperationNone;
       
   319         }
       
   320         *currentDragTarget() = target;
       
   321         return [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) draggingEntered:sender];
       
   322     }
       
   323 }
       
   324 
       
   325 - (void)draggingExited:(id < NSDraggingInfo >)sender
       
   326 {
       
   327     QWidget *target = [self dragTargetHitTest:sender];
       
   328     if (!target)
       
   329         return [super draggingExited:sender];
       
   330 
       
   331     if (*currentDragTarget()) {
       
   332         [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) draggingExited:sender];
       
   333         *currentDragTarget() = 0;
       
   334     }
       
   335 }
       
   336 
       
   337 - (BOOL)performDragOperation:(id < NSDraggingInfo >)sender
       
   338 {
       
   339     QWidget *target = [self dragTargetHitTest:sender];
       
   340     if (!target)
       
   341         return [super performDragOperation:sender];
       
   342 
       
   343     BOOL dropResult = NO;
       
   344     if (*currentDragTarget()) {
       
   345         dropResult = [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) performDragOperation:sender];
       
   346         *currentDragTarget() = 0;
       
   347     }
       
   348     return dropResult;
       
   349 }
       
   350 
   191 - (void)displayIfNeeded
   351 - (void)displayIfNeeded
   192 {
   352 {
   193 
   353 
   194     QWidget *qwidget = [[QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate] qt_qwidgetForWindow:self];
   354     QWidget *qwidget = [[QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate] qt_qwidgetForWindow:self];
   195     if (qwidget == 0) {
   355     if (qwidget == 0) {
   202             bs->sync(qwidget, qwidget->rect());
   362             bs->sync(qwidget, qwidget->rect());
   203     }
   363     }
   204     [super displayIfNeeded];
   364     [super displayIfNeeded];
   205 }
   365 }
   206 
   366 
   207 
   367 // This is a hack and it should be removed once we find the real cause for
       
   368 // the painting problems.
       
   369 // We have a static variable that signals if we have been called before or not.
       
   370 static bool firstDrawingInvocation = true;
       
   371 
       
   372 // The method below exists only as a workaround to draw/not draw the baseline
       
   373 // in the title bar. This is to support unifiedToolbar look.
       
   374 
       
   375 // This method is very special. To begin with, it is a
       
   376 // method that will get called only if we enable documentMode.
       
   377 // Furthermore, it won't get called as a normal method, we swap
       
   378 // this method with the normal implementation of drawRect in
       
   379 // _NSThemeFrame. When this method is active, its mission is to
       
   380 // first call the original drawRect implementation so the widget
       
   381 // gets proper painting. After that, it needs to detect if there
       
   382 // is a toolbar or not, in order to decide how to handle the unified
       
   383 // look. The distinction is important since the presence and
       
   384 // visibility of a toolbar change the way we enter into unified mode.
       
   385 // When there is a toolbar and that toolbar is visible, the problem
       
   386 // is as simple as to tell the toolbar not to draw its baseline.
       
   387 // However when there is not toolbar or the toolbar is not visible,
       
   388 // we need to draw a line on top of the baseline, because the baseline
       
   389 // in that case will belong to the title. For this case we need to draw
       
   390 // a line on top of the baseline.
       
   391 // As usual, there is a special case. When we first are called, we might
       
   392 // need to repaint ourselves one more time. We only need that if we
       
   393 // didn't get the activation, i.e. when we are launched via the command
       
   394 // line. And this only if the toolbar is visible from the beginning,
       
   395 // so we have a special flag that signals if we need to repaint or not.
       
   396 - (void)drawRectSpecial:(NSRect)rect
       
   397 {
       
   398     // Call the original drawing method.
       
   399     [self drawRectOriginal:rect];
       
   400     NSWindow *window = [self window];
       
   401     NSToolbar *toolbar = [window toolbar];
       
   402     if(!toolbar) {
       
   403         // There is no toolbar, we have to draw a line on top of the line drawn by Cocoa.
       
   404         macDrawRectOnTop((void *)window);
       
   405     } else {
       
   406         if([toolbar isVisible]) {
       
   407             // We tell Cocoa to avoid drawing the line at the end.
       
   408             if(firstDrawingInvocation) {
       
   409                 firstDrawingInvocation = false;
       
   410                 macSyncDrawingOnFirstInvocation((void *)window);
       
   411             } else
       
   412                 [toolbar setShowsBaselineSeparator:NO];
       
   413         } else {
       
   414             // There is a toolbar but it is not visible so
       
   415             // we have to draw a line on top of the line drawn by Cocoa.
       
   416             macDrawRectOnTop((void *)window);
       
   417         }
       
   418     }
       
   419 }