wine-wine/dlls/winemac.drv/cocoa_status_item.m

308 lines
9.0 KiB
Objective-C

/*
* MACDRV Cocoa status item class
*
* Copyright 2011, 2012, 2013 Ken Thomases for CodeWeavers Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#import <Cocoa/Cocoa.h>
#include "macdrv_cocoa.h"
#import "cocoa_app.h"
#import "cocoa_event.h"
@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, image;
- (id) initWithEventQueue:(WineEventQueue*)inQueue
{
NSStatusBar* statusBar = [NSStatusBar systemStatusBar];
CGFloat thickness = [statusBar thickness];
self = [super initWithFrame:NSMakeRect(0, 0, thickness, thickness)];
if (self)
{
item = [[statusBar statusItemWithLength:NSSquareStatusItemLength] retain];
// 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;
}
- (void) dealloc
{
if (item)
{
NSStatusBar* statusBar = [NSStatusBar systemStatusBar];
[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];
[queue discardEventsPassingTest:^BOOL (macdrv_event* event){
return ((event->type == STATUS_ITEM_MOUSE_BUTTON && event->status_item_mouse_button.item == (macdrv_status_item)self) ||
(event->type == STATUS_ITEM_MOUSE_MOVE && event->status_item_mouse_move.item == (macdrv_status_item)self));
}];
self.item = nil;
}
}
- (void) postMouseButtonEvent:(NSEvent*)nsevent;
{
macdrv_event* event;
NSUInteger typeMask = NSEventMaskFromType([nsevent type]);
CGPoint point = CGEventGetLocation([nsevent CGEvent]);
point = cgpoint_win_from_mac(point);
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];
event->status_item_mouse_button.x = floor(point.x);
event->status_item_mouse_button.y = floor(point.y);
[queue postEvent:event];
macdrv_release_event(event);
}
/*
* ---------- 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;
CGPoint point = CGEventGetLocation([nsevent CGEvent]);
point = cgpoint_win_from_mac(point);
event = macdrv_create_event(STATUS_ITEM_MOUSE_MOVE, nil);
event->status_item_mouse_move.item = (macdrv_status_item)self;
event->status_item_mouse_move.x = floor(point.x);
event->status_item_mouse_move.y = floor(point.y);
[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
/***********************************************************************
* macdrv_create_status_item
*
* Creates a new status item in the status bar.
*/
macdrv_status_item macdrv_create_status_item(macdrv_event_queue q)
{
WineEventQueue* queue = (WineEventQueue*)q;
__block WineStatusItem* statusItem;
OnMainThread(^{
statusItem = [[WineStatusItem alloc] initWithEventQueue:queue];
});
return (macdrv_status_item)statusItem;
}
/***********************************************************************
* macdrv_destroy_status_item
*
* Removes a status item previously returned by
* macdrv_create_status_item() from the status bar and destroys it.
*/
void macdrv_destroy_status_item(macdrv_status_item s)
{
WineStatusItem* statusItem = (WineStatusItem*)s;
OnMainThreadAsync(^{
[statusItem removeFromStatusBar];
[statusItem release];
});
}
/***********************************************************************
* macdrv_set_status_item_image
*
* Sets the image for a status item. If cgimage is NULL, clears the
* image of the status item (leaving it a blank spot on the menu bar).
*/
void macdrv_set_status_item_image(macdrv_status_item s, CGImageRef cgimage)
{
WineStatusItem* statusItem = (WineStatusItem*)s;
CGImageRetain(cgimage);
OnMainThreadAsync(^{
NSImage* image = nil;
if (cgimage)
{
NSSize size;
CGFloat maxSize = [[NSStatusBar systemStatusBar] thickness];
BOOL changed = FALSE;
image = [[NSImage alloc] initWithCGImage:cgimage size:NSZeroSize];
CGImageRelease(cgimage);
size = [image size];
while (size.width > maxSize || size.height > maxSize)
{
size.width /= 2.0;
size.height /= 2.0;
changed = TRUE;
}
if (changed)
[image setSize:size];
}
statusItem.image = image;
[image release];
});
}
/***********************************************************************
* macdrv_set_status_item_tooltip
*
* Sets the tooltip string for a status item. If cftip is NULL, clears
* the tooltip string for the status item.
*/
void macdrv_set_status_item_tooltip(macdrv_status_item s, CFStringRef cftip)
{
WineStatusItem* statusItem = (WineStatusItem*)s;
NSString* tip = (NSString*)cftip;
if (![tip length]) tip = nil;
OnMainThreadAsync(^{
[statusItem setToolTip:tip];
});
}