d2d1: Add D2D1Crop.
[wine.git] / dlls / winemac.drv / cocoa_status_item.m
blob1ffdeb8048d487ce682657d616129ed23755caa9
1 /*
2  * MACDRV Cocoa status item class
3  *
4  * Copyright 2011, 2012, 2013 Ken Thomases for CodeWeavers Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
21 #import <Cocoa/Cocoa.h>
22 #include "macdrv_cocoa.h"
23 #import "cocoa_app.h"
24 #import "cocoa_event.h"
27 @interface WineStatusItem : NSView
29     NSStatusItem* item;
30     WineEventQueue* queue;
31     NSTrackingArea* trackingArea;
32     NSImage* image;
35 @property (retain, nonatomic) NSStatusItem* item;
36 @property (assign, nonatomic) WineEventQueue* queue;
37 @property (retain, nonatomic) NSImage* image;
39 @end
42 @implementation WineStatusItem
44 @synthesize item, queue, image;
46     - (id) initWithEventQueue:(WineEventQueue*)inQueue
47     {
48         NSStatusBar* statusBar = [NSStatusBar systemStatusBar];
49         CGFloat thickness = [statusBar thickness];
51         self = [super initWithFrame:NSMakeRect(0, 0, thickness, thickness)];
52         if (self)
53         {
54             item = [[statusBar statusItemWithLength:NSSquareStatusItemLength] retain];
55             // This is a retain cycle which is broken in -removeFromStatusBar.
56             [item setView:self];
58             queue = inQueue;
60             trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds]
61                                                         options:NSTrackingMouseMoved | NSTrackingActiveAlways | NSTrackingInVisibleRect
62                                                           owner:self
63                                                        userInfo:nil];
64             [self addTrackingArea:trackingArea];
65         }
66         return self;
67     }
69     - (void) dealloc
70     {
71         if (item)
72         {
73             NSStatusBar* statusBar = [NSStatusBar systemStatusBar];
74             [statusBar removeStatusItem:item];
75             [item release];
76         }
77         [image release];
78         [trackingArea release];
79         [super dealloc];
80     }
82     - (void) setImage:(NSImage*)inImage
83     {
84         if (image != inImage)
85         {
86             [image release];
87             image = [inImage retain];
88             [self setNeedsDisplay:YES];
89         }
90     }
92     - (void) removeFromStatusBar
93     {
94         if (item)
95         {
96             NSStatusBar* statusBar = [NSStatusBar systemStatusBar];
97             [statusBar removeStatusItem:item];
98             [item setView:nil];
100             [queue discardEventsPassingTest:^BOOL (macdrv_event* event){
101                 return ((event->type == STATUS_ITEM_MOUSE_BUTTON && event->status_item_mouse_button.item == (macdrv_status_item)self) ||
102                         (event->type == STATUS_ITEM_MOUSE_MOVE && event->status_item_mouse_move.item == (macdrv_status_item)self));
103             }];
105             self.item = nil;
106         }
107     }
109     - (void) postMouseButtonEvent:(NSEvent*)nsevent;
110     {
111         macdrv_event* event;
112         NSUInteger typeMask = NSEventMaskFromType([nsevent type]);
113         CGPoint point = CGEventGetLocation([nsevent CGEvent]);
115         point = cgpoint_win_from_mac(point);
117         event = macdrv_create_event(STATUS_ITEM_MOUSE_BUTTON, nil);
118         event->status_item_mouse_button.item = (macdrv_status_item)self;
119         event->status_item_mouse_button.button = [nsevent buttonNumber];
120         event->status_item_mouse_button.down = (typeMask & (NSEventMaskLeftMouseDown |
121                                                             NSEventMaskRightMouseDown |
122                                                             NSEventMaskOtherMouseDown)) != 0;
123         event->status_item_mouse_button.count = [nsevent clickCount];
124         event->status_item_mouse_button.x = floor(point.x);
125         event->status_item_mouse_button.y = floor(point.y);
126         [queue postEvent:event];
127         macdrv_release_event(event);
128     }
131     /*
132      * ---------- NSView methods ----------
133      */
134     - (void) drawRect:(NSRect)rect
135     {
136         [item drawStatusBarBackgroundInRect:[self bounds] withHighlight:NO];
138         if (image)
139         {
140             NSSize imageSize = [image size];
141             NSRect bounds = [self bounds];
142             NSPoint imageOrigin = NSMakePoint(NSMidX(bounds) - imageSize.width / 2,
143                                               NSMidY(bounds) - imageSize.height / 2);
145             imageOrigin = [self convertPointToBase:imageOrigin];
146             imageOrigin.x = floor(imageOrigin.x);
147             imageOrigin.y = floor(imageOrigin.y);
148             imageOrigin = [self convertPointFromBase:imageOrigin];
150             [image drawAtPoint:imageOrigin
151                       fromRect:NSZeroRect
152                      operation:NSCompositingOperationSourceOver
153                       fraction:1];
154         }
155     }
158     /*
159      * ---------- NSResponder methods ----------
160      */
161     - (void) mouseDown:(NSEvent*)event
162     {
163         [self postMouseButtonEvent:event];
164     }
166     - (void) mouseDragged:(NSEvent*)event
167     {
168         [self mouseMoved:event];
169     }
171     - (void) mouseMoved:(NSEvent*)nsevent
172     {
173         macdrv_event* event;
174         CGPoint point = CGEventGetLocation([nsevent CGEvent]);
176         point = cgpoint_win_from_mac(point);
178         event = macdrv_create_event(STATUS_ITEM_MOUSE_MOVE, nil);
179         event->status_item_mouse_move.item = (macdrv_status_item)self;
180         event->status_item_mouse_move.x = floor(point.x);
181         event->status_item_mouse_move.y = floor(point.y);
182         [queue postEvent:event];
183         macdrv_release_event(event);
184     }
186     - (void) mouseUp:(NSEvent*)event
187     {
188         [self postMouseButtonEvent:event];
189     }
191     - (void) otherMouseDown:(NSEvent*)event
192     {
193         [self postMouseButtonEvent:event];
194     }
196     - (void) otherMouseDragged:(NSEvent*)event
197     {
198         [self mouseMoved:event];
199     }
201     - (void) otherMouseUp:(NSEvent*)event
202     {
203         [self postMouseButtonEvent:event];
204     }
206     - (void) rightMouseDown:(NSEvent*)event
207     {
208         [self postMouseButtonEvent:event];
209     }
211     - (void) rightMouseDragged:(NSEvent*)event
212     {
213         [self mouseMoved:event];
214     }
216     - (void) rightMouseUp:(NSEvent*)event
217     {
218         [self postMouseButtonEvent:event];
219     }
221 @end
224 /***********************************************************************
225  *              macdrv_create_status_item
227  * Creates a new status item in the status bar.
228  */
229 macdrv_status_item macdrv_create_status_item(macdrv_event_queue q)
231     WineEventQueue* queue = (WineEventQueue*)q;
232     __block WineStatusItem* statusItem;
234     OnMainThread(^{
235         statusItem = [[WineStatusItem alloc] initWithEventQueue:queue];
236     });
238     return (macdrv_status_item)statusItem;
241 /***********************************************************************
242  *              macdrv_destroy_status_item
244  * Removes a status item previously returned by
245  * macdrv_create_status_item() from the status bar and destroys it.
246  */
247 void macdrv_destroy_status_item(macdrv_status_item s)
249     WineStatusItem* statusItem = (WineStatusItem*)s;
251     OnMainThreadAsync(^{
252         [statusItem removeFromStatusBar];
253         [statusItem release];
254     });
257 /***********************************************************************
258  *              macdrv_set_status_item_image
260  * Sets the image for a status item.  If cgimage is NULL, clears the
261  * image of the status item (leaving it a blank spot on the menu bar).
262  */
263 void macdrv_set_status_item_image(macdrv_status_item s, CGImageRef cgimage)
265     WineStatusItem* statusItem = (WineStatusItem*)s;
267     CGImageRetain(cgimage);
269     OnMainThreadAsync(^{
270         NSImage* image = nil;
271         if (cgimage)
272         {
273             NSSize size;
274             CGFloat maxSize = [[NSStatusBar systemStatusBar] thickness];
275             BOOL changed = FALSE;
277             image = [[NSImage alloc] initWithCGImage:cgimage size:NSZeroSize];
278             CGImageRelease(cgimage);
279             size = [image size];
280             while (size.width > maxSize || size.height > maxSize)
281             {
282                 size.width /= 2.0;
283                 size.height /= 2.0;
284                 changed = TRUE;
285             }
286             if (changed)
287                 [image setSize:size];
288         }
289         statusItem.image = image;
290         [image release];
291     });
294 /***********************************************************************
295  *              macdrv_set_status_item_tooltip
297  * Sets the tooltip string for a status item.  If cftip is NULL, clears
298  * the tooltip string for the status item.
299  */
300 void macdrv_set_status_item_tooltip(macdrv_status_item s, CFStringRef cftip)
302     WineStatusItem* statusItem = (WineStatusItem*)s;
303     NSString* tip = (NSString*)cftip;
305     if (![tip length]) tip = nil;
306     OnMainThreadAsync(^{
307         [statusItem setToolTip:tip];
308     });