From 7ed00f6d975718ab32cf30a7fe6c6c27c8f69364 Mon Sep 17 00:00:00 2001 From: Ken Thomases Date: Thu, 5 Sep 2013 22:23:52 -0500 Subject: [PATCH] winemac: Add support for mouse-move and right- and middle-click events on systray icons in the Mac status bar. --- dlls/winemac.drv/cocoa_status_item.m | 136 ++++++++++++++++++++++++++++++----- dlls/winemac.drv/event.c | 13 ++-- dlls/winemac.drv/macdrv.h | 3 +- dlls/winemac.drv/macdrv_cocoa.h | 10 ++- dlls/winemac.drv/systray.c | 69 ++++++++++++------ 5 files changed, 186 insertions(+), 45 deletions(-) diff --git a/dlls/winemac.drv/cocoa_status_item.m b/dlls/winemac.drv/cocoa_status_item.m index 5b4ee947584..f0a5863cb8e 100644 --- a/dlls/winemac.drv/cocoa_status_item.m +++ b/dlls/winemac.drv/cocoa_status_item.m @@ -24,34 +24,44 @@ #import "cocoa_event.h" -@interface WineStatusItem : NSObject +@interface WineStatusItem : NSView { NSStatusItem* item; WineEventQueue* queue; + NSTrackingArea* trackingArea; + NSImage* image; } @property (retain, nonatomic) NSStatusItem* item; @property (assign, nonatomic) WineEventQueue* queue; +@property (retain, nonatomic) NSImage* image; @end @implementation WineStatusItem -@synthesize item, queue; +@synthesize item, queue, image; - (id) initWithEventQueue:(WineEventQueue*)inQueue { - self = [super init]; + NSStatusBar* statusBar = [NSStatusBar systemStatusBar]; + CGFloat thickness = [statusBar thickness]; + + self = [super initWithFrame:NSMakeRect(0, 0, thickness, thickness)]; if (self) { - NSStatusBar* statusBar = [NSStatusBar systemStatusBar]; item = [[statusBar statusItemWithLength:NSSquareStatusItemLength] retain]; - [item setTarget:self]; - [item setAction:@selector(clicked:)]; - [item setDoubleAction:@selector(doubleClicked:)]; + // This is a retain cycle which is broken in -removeFromStatusBar. + [item setView:self]; queue = inQueue; + + trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] + options:NSTrackingMouseMoved | NSTrackingActiveAlways | NSTrackingInVisibleRect + owner:self + userInfo:nil]; + [self addTrackingArea:trackingArea]; } return self; } @@ -64,37 +74,129 @@ [statusBar removeStatusItem:item]; [item release]; } + [image release]; + [trackingArea release]; [super dealloc]; } + - (void) setImage:(NSImage*)inImage + { + if (image != inImage) + { + [image release]; + image = [inImage retain]; + [self setNeedsDisplay:YES]; + } + } + - (void) removeFromStatusBar { if (item) { NSStatusBar* statusBar = [NSStatusBar systemStatusBar]; [statusBar removeStatusItem:item]; + [item setView:nil]; self.item = nil; } } - - (void) postClickedEventWithCount:(int)count + - (void) postMouseButtonEvent:(NSEvent*)nsevent; { macdrv_event* event; - event = macdrv_create_event(STATUS_ITEM_CLICKED, nil); - event->status_item_clicked.item = (macdrv_status_item)self; - event->status_item_clicked.count = count; + NSUInteger typeMask = NSEventMaskFromType([nsevent type]); + + event = macdrv_create_event(STATUS_ITEM_MOUSE_BUTTON, nil); + event->status_item_mouse_button.item = (macdrv_status_item)self; + event->status_item_mouse_button.button = [nsevent buttonNumber]; + event->status_item_mouse_button.down = (typeMask & (NSLeftMouseDownMask | NSRightMouseDownMask | NSOtherMouseDownMask)) != 0; + event->status_item_mouse_button.count = [nsevent clickCount]; [queue postEvent:event]; macdrv_release_event(event); } - - (void) clicked:(id)sender + + /* + * ---------- NSView methods ---------- + */ + - (void) drawRect:(NSRect)rect + { + [item drawStatusBarBackgroundInRect:[self bounds] withHighlight:NO]; + + if (image) + { + NSSize imageSize = [image size]; + NSRect bounds = [self bounds]; + NSPoint imageOrigin = NSMakePoint(NSMidX(bounds) - imageSize.width / 2, + NSMidY(bounds) - imageSize.height / 2); + + imageOrigin = [self convertPointToBase:imageOrigin]; + imageOrigin.x = floor(imageOrigin.x); + imageOrigin.y = floor(imageOrigin.y); + imageOrigin = [self convertPointFromBase:imageOrigin]; + + [image drawAtPoint:imageOrigin + fromRect:NSZeroRect + operation:NSCompositeSourceOver + fraction:1]; + } + } + + + /* + * ---------- NSResponder methods ---------- + */ + - (void) mouseDown:(NSEvent*)event + { + [self postMouseButtonEvent:event]; + } + + - (void) mouseDragged:(NSEvent*)event + { + [self mouseMoved:event]; + } + + - (void) mouseMoved:(NSEvent*)nsevent + { + macdrv_event* event; + event = macdrv_create_event(STATUS_ITEM_MOUSE_MOVE, nil); + event->status_item_mouse_move.item = (macdrv_status_item)self; + [queue postEvent:event]; + macdrv_release_event(event); + } + + - (void) mouseUp:(NSEvent*)event + { + [self postMouseButtonEvent:event]; + } + + - (void) otherMouseDown:(NSEvent*)event + { + [self postMouseButtonEvent:event]; + } + + - (void) otherMouseDragged:(NSEvent*)event + { + [self mouseMoved:event]; + } + + - (void) otherMouseUp:(NSEvent*)event + { + [self postMouseButtonEvent:event]; + } + + - (void) rightMouseDown:(NSEvent*)event + { + [self postMouseButtonEvent:event]; + } + + - (void) rightMouseDragged:(NSEvent*)event { - [self postClickedEventWithCount:1]; + [self mouseMoved:event]; } - - (void) doubleClicked:(id)sender + - (void) rightMouseUp:(NSEvent*)event { - [self postClickedEventWithCount:2]; + [self postMouseButtonEvent:event]; } @end @@ -165,7 +267,7 @@ void macdrv_set_status_item_image(macdrv_status_item s, CGImageRef cgimage) if (changed) [image setSize:size]; } - [statusItem.item setImage:image]; + statusItem.image = image; [image release]; }); } @@ -183,6 +285,6 @@ void macdrv_set_status_item_tooltip(macdrv_status_item s, CFStringRef cftip) if (![tip length]) tip = nil; OnMainThreadAsync(^{ - [statusItem.item setToolTip:tip]; + [statusItem setToolTip:tip]; }); } diff --git a/dlls/winemac.drv/event.c b/dlls/winemac.drv/event.c index 5eb823e167c..8a8acf36b53 100644 --- a/dlls/winemac.drv/event.c +++ b/dlls/winemac.drv/event.c @@ -45,7 +45,8 @@ static const char *dbgstr_event(int type) "MOUSE_SCROLL", "QUERY_EVENT", "RELEASE_CAPTURE", - "STATUS_ITEM_CLICKED", + "STATUS_ITEM_MOUSE_BUTTON", + "STATUS_ITEM_MOUSE_MOVE", "WINDOW_CLOSE_REQUESTED", "WINDOW_DID_MINIMIZE", "WINDOW_DID_UNMINIMIZE", @@ -93,7 +94,8 @@ static macdrv_event_mask get_event_mask(DWORD mask) event_mask |= event_mask_for_type(APP_QUIT_REQUESTED); event_mask |= event_mask_for_type(DISPLAYS_CHANGED); event_mask |= event_mask_for_type(IM_SET_TEXT); - event_mask |= event_mask_for_type(STATUS_ITEM_CLICKED); + event_mask |= event_mask_for_type(STATUS_ITEM_MOUSE_BUTTON); + event_mask |= event_mask_for_type(STATUS_ITEM_MOUSE_MOVE); event_mask |= event_mask_for_type(WINDOW_CLOSE_REQUESTED); event_mask |= event_mask_for_type(WINDOW_DID_MINIMIZE); event_mask |= event_mask_for_type(WINDOW_DID_UNMINIMIZE); @@ -207,8 +209,11 @@ void macdrv_handle_event(const macdrv_event *event) case RELEASE_CAPTURE: macdrv_release_capture(hwnd, event); break; - case STATUS_ITEM_CLICKED: - macdrv_status_item_clicked(event); + case STATUS_ITEM_MOUSE_BUTTON: + macdrv_status_item_mouse_button(event); + break; + case STATUS_ITEM_MOUSE_MOVE: + macdrv_status_item_mouse_move(event); break; case WINDOW_CLOSE_REQUESTED: macdrv_window_close_requested(hwnd); diff --git a/dlls/winemac.drv/macdrv.h b/dlls/winemac.drv/macdrv.h index 9bf1b7a18df..0e7d364be71 100644 --- a/dlls/winemac.drv/macdrv.h +++ b/dlls/winemac.drv/macdrv.h @@ -195,7 +195,8 @@ extern CGImageRef create_cgimage_from_icon_bitmaps(HDC hdc, HANDLE icon, HBITMAP extern CGImageRef create_cgimage_from_icon(HANDLE icon, int width, int height) DECLSPEC_HIDDEN; extern CFArrayRef create_app_icon_images(void) DECLSPEC_HIDDEN; -extern void macdrv_status_item_clicked(const macdrv_event *event) DECLSPEC_HIDDEN; +extern void macdrv_status_item_mouse_button(const macdrv_event *event) DECLSPEC_HIDDEN; +extern void macdrv_status_item_mouse_move(const macdrv_event *event) DECLSPEC_HIDDEN; /************************************************************************** diff --git a/dlls/winemac.drv/macdrv_cocoa.h b/dlls/winemac.drv/macdrv_cocoa.h index 40173bae8fd..1f68b615244 100644 --- a/dlls/winemac.drv/macdrv_cocoa.h +++ b/dlls/winemac.drv/macdrv_cocoa.h @@ -176,7 +176,8 @@ enum { MOUSE_SCROLL, QUERY_EVENT, RELEASE_CAPTURE, - STATUS_ITEM_CLICKED, + STATUS_ITEM_MOUSE_BUTTON, + STATUS_ITEM_MOUSE_MOVE, WINDOW_CLOSE_REQUESTED, WINDOW_DID_MINIMIZE, WINDOW_DID_UNMINIMIZE, @@ -248,8 +249,13 @@ typedef struct macdrv_event { } query_event; struct { macdrv_status_item item; + int button; + int down; int count; - } status_item_clicked; + } status_item_mouse_button; + struct { + macdrv_status_item item; + } status_item_mouse_move; struct { CGRect frame; } window_frame_changed; diff --git a/dlls/winemac.drv/systray.c b/dlls/winemac.drv/systray.c index dc0f99b7c5b..6bae0eda1d1 100644 --- a/dlls/winemac.drv/systray.c +++ b/dlls/winemac.drv/systray.c @@ -308,22 +308,38 @@ int CDECL wine_notify_icon(DWORD msg, NOTIFYICONDATAW *data) /*********************************************************************** - * macdrv_status_item_clicked + * macdrv_status_item_mouse_button * - * Handle STATUS_ITEM_CLICKED events. + * Handle STATUS_ITEM_MOUSE_BUTTON events. */ -void macdrv_status_item_clicked(const macdrv_event *event) +void macdrv_status_item_mouse_button(const macdrv_event *event) { struct tray_icon *icon; - TRACE("item %p count %d\n", event->status_item_clicked.item, - event->status_item_clicked.count); + TRACE("item %p button %d down %d count %d\n", event->status_item_mouse_button.item, + event->status_item_mouse_button.button, event->status_item_mouse_button.down, + event->status_item_mouse_button.count); LIST_FOR_EACH_ENTRY(icon, &icon_list, struct tray_icon, entry) { - if (icon->status_item == event->status_item_clicked.item) + if (icon->status_item == event->status_item_mouse_button.item) { - UINT down; + UINT msg; + + switch (event->status_item_mouse_button.button) + { + case 0: msg = WM_LBUTTONDOWN; break; + case 1: msg = WM_RBUTTONDOWN; break; + case 2: msg = WM_MBUTTONDOWN; break; + default: + TRACE("ignoring button beyond the third\n"); + return; + } + + if (!event->status_item_mouse_button.down) + msg += WM_LBUTTONUP - WM_LBUTTONDOWN; + else if (event->status_item_mouse_button.count % 2 == 0) + msg += WM_LBUTTONDBLCLK - WM_LBUTTONDOWN; if (!SendMessageW(icon->owner, WM_MACDRV_ACTIVATE_ON_FOLLOWING_FOCUS, 0, 0) && GetLastError() == ERROR_INVALID_WINDOW_HANDLE) @@ -333,18 +349,8 @@ void macdrv_status_item_clicked(const macdrv_event *event) return; } - if (event->status_item_clicked.count == 1) - { - down = WM_LBUTTONDOWN; - TRACE("posting WM_LBUTTONDOWN to hwnd %p id 0x%x\n", icon->owner, icon->id); - } - else - { - down = WM_LBUTTONDBLCLK; - TRACE("posting WM_LBUTTONDBLCLK to hwnd %p id 0x%x\n", icon->owner, icon->id); - } - - if (!PostMessageW(icon->owner, icon->callback_message, icon->id, down) && + TRACE("posting msg 0x%04x to hwnd %p id 0x%x\n", msg, icon->owner, icon->id); + if (!PostMessageW(icon->owner, icon->callback_message, icon->id, msg) && GetLastError() == ERROR_INVALID_WINDOW_HANDLE) { WARN("window %p was destroyed, removing icon 0x%x\n", icon->owner, icon->id); @@ -352,12 +358,33 @@ void macdrv_status_item_clicked(const macdrv_event *event) return; } - TRACE("posting WM_LBUTTONUP to hwnd %p id 0x%x\n", icon->owner, icon->id); - if (!PostMessageW(icon->owner, icon->callback_message, icon->id, WM_LBUTTONUP) && + break; + } + } +} + + +/*********************************************************************** + * macdrv_status_item_mouse_move + * + * Handle STATUS_ITEM_MOUSE_MOVE events. + */ +void macdrv_status_item_mouse_move(const macdrv_event *event) +{ + struct tray_icon *icon; + + TRACE("item %p\n", event->status_item_mouse_move.item); + + LIST_FOR_EACH_ENTRY(icon, &icon_list, struct tray_icon, entry) + { + if (icon->status_item == event->status_item_mouse_move.item) + { + if (!PostMessageW(icon->owner, icon->callback_message, icon->id, WM_MOUSEMOVE) && GetLastError() == ERROR_INVALID_WINDOW_HANDLE) { WARN("window %p was destroyed, removing icon 0x%x\n", icon->owner, icon->id); delete_icon(icon); + return; } break; -- 2.11.4.GIT