winemac: Add support for mouse-move and right- and middle-click events on systray icons in the Mac status bar.

This commit is contained in:
Ken Thomases 2013-09-05 22:23:52 -05:00 committed by Alexandre Julliard
parent ee771fddaa
commit 7ed00f6d97
5 changed files with 186 additions and 45 deletions

View File

@ -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 @@ - (void) dealloc
[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
{
[self postClickedEventWithCount:1];
[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];
}
}
- (void) doubleClicked:(id)sender
/*
* ---------- NSResponder methods ----------
*/
- (void) mouseDown:(NSEvent*)event
{
[self postClickedEventWithCount:2];
[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 mouseMoved:event];
}
- (void) rightMouseUp:(NSEvent*)event
{
[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];
});
}

View File

@ -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);

View File

@ -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;
/**************************************************************************

View File

@ -176,7 +176,8 @@ extern int macdrv_set_display_mode(const struct macdrv_display* display,
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 @@ extern int macdrv_set_display_mode(const struct macdrv_display* display,
} 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;

View File

@ -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;