usp10: Handle an offset of the first glyph.
[wine] / dlls / winemac.drv / cocoa_window.m
1 /*
2  * MACDRV Cocoa window code
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  */
20
21 #import "cocoa_window.h"
22
23 #include "macdrv_cocoa.h"
24 #import "cocoa_app.h"
25
26
27 static NSUInteger style_mask_for_features(const struct macdrv_window_features* wf)
28 {
29     NSUInteger style_mask;
30
31     if (wf->title_bar)
32     {
33         style_mask = NSTitledWindowMask;
34         if (wf->close_button) style_mask |= NSClosableWindowMask;
35         if (wf->minimize_button) style_mask |= NSMiniaturizableWindowMask;
36         if (wf->resizable) style_mask |= NSResizableWindowMask;
37         if (wf->utility) style_mask |= NSUtilityWindowMask;
38     }
39     else style_mask = NSBorderlessWindowMask;
40
41     return style_mask;
42 }
43
44
45 static BOOL frame_intersects_screens(NSRect frame, NSArray* screens)
46 {
47     NSScreen* screen;
48     for (screen in screens)
49     {
50         if (NSIntersectsRect(frame, [screen frame]))
51             return TRUE;
52     }
53     return FALSE;
54 }
55
56
57 @interface WineContentView : NSView
58 @end
59
60
61 @interface WineWindow ()
62
63     + (void) flipRect:(NSRect*)rect;
64
65 @end
66
67
68 @implementation WineContentView
69
70     - (BOOL) isFlipped
71     {
72         return YES;
73     }
74
75 @end
76
77
78 @implementation WineWindow
79
80     + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
81                                  windowFrame:(NSRect)window_frame
82     {
83         WineWindow* window;
84         WineContentView* contentView;
85
86         [self flipRect:&window_frame];
87
88         window = [[[self alloc] initWithContentRect:window_frame
89                                           styleMask:style_mask_for_features(wf)
90                                             backing:NSBackingStoreBuffered
91                                               defer:YES] autorelease];
92
93         /* Standardize windows to eliminate differences between titled and
94            borderless windows and between NSWindow and NSPanel. */
95         [window setHidesOnDeactivate:NO];
96         [window setReleasedWhenClosed:NO];
97
98         [window disableCursorRects];
99         [window setShowsResizeIndicator:NO];
100         [window setHasShadow:wf->shadow];
101         [window setDelegate:window];
102
103         contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
104         if (!contentView)
105             return nil;
106         [contentView setAutoresizesSubviews:NO];
107
108         [window setContentView:contentView];
109
110         return window;
111     }
112
113     + (void) flipRect:(NSRect*)rect
114     {
115         rect->origin.y = NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]) - NSMaxY(*rect);
116     }
117
118     - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
119     {
120         [self setStyleMask:style_mask_for_features(wf)];
121         [self setHasShadow:wf->shadow];
122     }
123
124     /* Returns whether or not the window was ordered in, which depends on if
125        its frame intersects any screen. */
126     - (BOOL) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next
127     {
128         BOOL on_screen = frame_intersects_screens([self frame], [NSScreen screens]);
129         if (on_screen)
130         {
131             [NSApp transformProcessToForeground];
132
133             if (prev)
134                 [self orderWindow:NSWindowBelow relativeTo:[prev windowNumber]];
135             else
136                 [self orderWindow:NSWindowAbove relativeTo:[next windowNumber]];
137         }
138
139         return on_screen;
140     }
141
142     - (BOOL) setFrameIfOnScreen:(NSRect)contentRect
143     {
144         NSArray* screens = [NSScreen screens];
145         BOOL on_screen = [self isVisible];
146         NSRect frame, oldFrame;
147
148         if (![screens count]) return on_screen;
149
150         /* Origin is (left, top) in a top-down space.  Need to convert it to
151            (left, bottom) in a bottom-up space. */
152         [[self class] flipRect:&contentRect];
153
154         if (on_screen)
155         {
156             on_screen = frame_intersects_screens(contentRect, screens);
157             if (!on_screen)
158                 [self orderOut:nil];
159         }
160
161         oldFrame = [self frame];
162         frame = [self frameRectForContentRect:contentRect];
163         if (!NSEqualRects(frame, oldFrame))
164         {
165             if (NSEqualSizes(frame.size, oldFrame.size))
166                 [self setFrameOrigin:frame.origin];
167             else
168                 [self setFrame:frame display:YES];
169         }
170
171         return on_screen;
172     }
173
174
175     /*
176      * ---------- NSWindow method overrides ----------
177      */
178     - (BOOL) canBecomeKeyWindow
179     {
180         return YES;
181     }
182
183     - (BOOL) canBecomeMainWindow
184     {
185         return [self canBecomeKeyWindow];
186     }
187
188
189     /*
190      * ---------- NSWindowDelegate methods ----------
191      */
192     - (BOOL)windowShouldClose:(id)sender
193     {
194         return NO;
195     }
196
197 @end
198
199
200 /***********************************************************************
201  *              macdrv_create_cocoa_window
202  *
203  * Create a Cocoa window with the given content frame and features (e.g.
204  * title bar, close box, etc.).
205  */
206 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
207         CGRect frame)
208 {
209     __block WineWindow* window;
210
211     OnMainThread(^{
212         window = [[WineWindow createWindowWithFeatures:wf
213                                            windowFrame:NSRectFromCGRect(frame)] retain];
214     });
215
216     return (macdrv_window)window;
217 }
218
219 /***********************************************************************
220  *              macdrv_destroy_cocoa_window
221  *
222  * Destroy a Cocoa window.
223  */
224 void macdrv_destroy_cocoa_window(macdrv_window w)
225 {
226     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
227     WineWindow* window = (WineWindow*)w;
228
229     [window close];
230     [window release];
231
232     [pool release];
233 }
234
235 /***********************************************************************
236  *              macdrv_set_cocoa_window_features
237  *
238  * Update a Cocoa window's features.
239  */
240 void macdrv_set_cocoa_window_features(macdrv_window w,
241         const struct macdrv_window_features* wf)
242 {
243     WineWindow* window = (WineWindow*)w;
244
245     OnMainThread(^{
246         [window setWindowFeatures:wf];
247     });
248 }
249
250 /***********************************************************************
251  *              macdrv_set_cocoa_window_title
252  *
253  * Set a Cocoa window's title.
254  */
255 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
256         size_t length)
257 {
258     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
259     WineWindow* window = (WineWindow*)w;
260     NSString* titleString;
261
262     if (title)
263         titleString = [NSString stringWithCharacters:title length:length];
264     else
265         titleString = @"";
266     OnMainThreadAsync(^{
267         [window setTitle:titleString];
268     });
269
270     [pool release];
271 }
272
273 /***********************************************************************
274  *              macdrv_order_cocoa_window
275  *
276  * Reorder a Cocoa window relative to other windows.  If prev is
277  * non-NULL, it is ordered below that window.  Else, if next is non-NULL,
278  * it is ordered above that window.  Otherwise, it is ordered to the
279  * front.
280  *
281  * Returns true if the window has actually been ordered onto the screen
282  * (i.e. if its frame intersects with a screen).  Otherwise, false.
283  */
284 int macdrv_order_cocoa_window(macdrv_window w, macdrv_window prev,
285         macdrv_window next)
286 {
287     WineWindow* window = (WineWindow*)w;
288     __block BOOL on_screen;
289
290     OnMainThread(^{
291         on_screen = [window orderBelow:(WineWindow*)prev
292                                orAbove:(WineWindow*)next];
293     });
294
295     return on_screen;
296 }
297
298 /***********************************************************************
299  *              macdrv_hide_cocoa_window
300  *
301  * Hides a Cocoa window.
302  */
303 void macdrv_hide_cocoa_window(macdrv_window w)
304 {
305     WineWindow* window = (WineWindow*)w;
306
307     OnMainThread(^{
308         [window orderOut:nil];
309     });
310 }
311
312 /***********************************************************************
313  *              macdrv_set_cocoa_window_frame
314  *
315  * Move a Cocoa window.  If the window has been moved out of the bounds
316  * of the desktop, it is ordered out.  (This routine won't ever order a
317  * window in, though.)
318  *
319  * Returns true if the window is on screen; false otherwise.
320  */
321 int macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
322 {
323     WineWindow* window = (WineWindow*)w;
324     __block BOOL on_screen;
325
326     OnMainThread(^{
327         on_screen = [window setFrameIfOnScreen:NSRectFromCGRect(*new_frame)];
328     });
329
330     return on_screen;
331 }