]> Pileus Git - ~andy/gtk/blob - gdk/quartz/GdkQuartzWindow.c
Remove the old workarond for broken tracking rects
[~andy/gtk] / gdk / quartz / GdkQuartzWindow.c
1 /* GdkQuartzWindow.m
2  *
3  * Copyright (C) 2005-2007 Imendio AB
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #import "GdkQuartzWindow.h"
22 #include "gdkwindow-quartz.h"
23 #include "gdkprivate-quartz.h"
24
25 @implementation GdkQuartzWindow
26
27 -(BOOL)windowShouldClose:(id)sender
28 {
29   GdkWindow *window = [[self contentView] gdkWindow];
30   GdkEvent *event;
31
32   event = gdk_event_new (GDK_DELETE);
33
34   event->any.window = g_object_ref (window);
35   event->any.send_event = FALSE;
36
37   _gdk_event_queue_append (gdk_display_get_default (), event);
38
39   return NO;
40 }
41
42 -(void)windowWillMiniaturize:(NSNotification *)aNotification
43 {
44   GdkWindow *window = [[self contentView] gdkWindow];
45
46   _gdk_quartz_window_detach_from_parent (window);
47 }
48
49 -(void)windowDidMiniaturize:(NSNotification *)aNotification
50 {
51   GdkWindow *window = [[self contentView] gdkWindow];
52
53   gdk_synthesize_window_state (window, 0, 
54                                GDK_WINDOW_STATE_ICONIFIED);
55 }
56
57 -(void)windowDidDeminiaturize:(NSNotification *)aNotification
58 {
59   GdkWindow *window = [[self contentView] gdkWindow];
60
61   _gdk_quartz_window_attach_to_parent (window);
62
63   gdk_synthesize_window_state (window, GDK_WINDOW_STATE_ICONIFIED, 0);
64 }
65
66 -(void)windowDidBecomeKey:(NSNotification *)aNotification
67 {
68   GdkWindow *window = [[self contentView] gdkWindow];
69
70   _gdk_quartz_events_update_focus_window (window, TRUE);
71 }
72
73 -(void)windowDidResignKey:(NSNotification *)aNotification
74 {
75   GdkWindow *window = [[self contentView] gdkWindow];
76
77   _gdk_quartz_events_update_focus_window (window, FALSE);
78 }
79
80 -(void)windowDidBecomeMain:(NSNotification *)aNotification
81 {
82   GdkWindow *window = [[self contentView] gdkWindow];
83
84   if (![self isVisible])
85     {
86       /* Note: This is a hack needed because for unknown reasons, hidden
87        * windows get shown when clicking the dock icon when the application
88        * is not already active.
89        */
90       [self orderOut:nil];
91       return;
92     }
93
94   _gdk_quartz_window_did_become_main (window);
95 }
96
97 -(void)windowDidResignMain:(NSNotification *)aNotification
98 {
99   GdkWindow *window;
100
101   window = [[self contentView] gdkWindow];
102   _gdk_quartz_window_did_resign_main (window);
103 }
104
105 /* Used in combination with NSLeftMouseUp in sendEvent to keep track
106  * of when the window is being moved with the mouse.
107  */
108 -(void)windowWillMove:(NSNotification *)aNotification
109 {
110   inMove = YES;
111 }
112
113 -(void)sendEvent:(NSEvent *)event
114 {
115   switch ([event type])
116     {
117     case NSLeftMouseUp:
118       inManualMove = NO;
119       inManualResize = NO;
120       inMove = NO;
121       break;
122
123     case NSLeftMouseDragged:
124       if ([self trackManualMove] || [self trackManualResize])
125         return;
126       break;
127
128     default:
129       break;
130     }
131
132   [super sendEvent:event];
133 }
134
135 -(BOOL)isInMove
136 {
137   return inMove;
138 }
139
140 -(void)windowDidMove:(NSNotification *)aNotification
141 {
142   NSRect content_rect = [self contentRectForFrameRect:[self frame]];
143   GdkWindow *window = [[self contentView] gdkWindow];
144   GdkWindowObject *private = (GdkWindowObject *)window;
145   GdkEvent *event;
146
147   private->x = content_rect.origin.x;
148   private->y = _gdk_quartz_window_get_inverted_screen_y (content_rect.origin.y + content_rect.size.height);
149
150   /* Synthesize a configure event */
151   event = gdk_event_new (GDK_CONFIGURE);
152   event->configure.window = g_object_ref (window);
153   event->configure.x = private->x;
154   event->configure.y = private->y;
155   event->configure.width = private->width;
156   event->configure.height = private->height;
157
158   _gdk_event_queue_append (gdk_display_get_default (), event);
159 }
160
161 -(void)windowDidResize:(NSNotification *)aNotification
162 {
163   NSRect content_rect = [self contentRectForFrameRect:[self frame]];
164   GdkWindow *window = [[self contentView] gdkWindow];
165   GdkWindowObject *private = (GdkWindowObject *)window;
166   GdkEvent *event;
167
168   private->width = content_rect.size.width;
169   private->height = content_rect.size.height;
170
171   [[self contentView] setFrame:NSMakeRect (0, 0, private->width, private->height)];
172
173   /* Synthesize a configure event */
174   event = gdk_event_new (GDK_CONFIGURE);
175   event->configure.window = g_object_ref (window);
176   event->configure.x = private->x;
177   event->configure.y = private->y;
178   event->configure.width = private->width;
179   event->configure.height = private->height;
180
181   _gdk_event_queue_append (gdk_display_get_default (), event);
182 }
183
184 -(id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag
185 {
186   self = [super initWithContentRect:contentRect
187                           styleMask:styleMask
188                             backing:backingType
189                               defer:flag];
190
191   [self setAcceptsMouseMovedEvents:YES];
192   [self setDelegate:self];
193   [self setReleasedWhenClosed:YES];
194
195   return self;
196 }
197
198 -(BOOL)canBecomeMainWindow
199 {
200   GdkWindow *window = [[self contentView] gdkWindow];
201   GdkWindowObject *private = (GdkWindowObject *)window;
202   GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
203
204   switch (impl->type_hint)
205     {
206     case GDK_WINDOW_TYPE_HINT_NORMAL:
207     case GDK_WINDOW_TYPE_HINT_DIALOG:
208       return YES;
209       
210     case GDK_WINDOW_TYPE_HINT_MENU:
211     case GDK_WINDOW_TYPE_HINT_TOOLBAR:
212     case GDK_WINDOW_TYPE_HINT_SPLASHSCREEN:
213     case GDK_WINDOW_TYPE_HINT_UTILITY:
214     case GDK_WINDOW_TYPE_HINT_DOCK:
215     case GDK_WINDOW_TYPE_HINT_DESKTOP:
216     case GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU:
217     case GDK_WINDOW_TYPE_HINT_POPUP_MENU:
218     case GDK_WINDOW_TYPE_HINT_TOOLTIP:
219     case GDK_WINDOW_TYPE_HINT_NOTIFICATION:
220     case GDK_WINDOW_TYPE_HINT_COMBO:
221     case GDK_WINDOW_TYPE_HINT_DND:
222       return NO;
223     }
224   
225   return YES;
226 }
227
228 -(BOOL)canBecomeKeyWindow
229 {
230   GdkWindow *window = [[self contentView] gdkWindow];
231   GdkWindowObject *private = (GdkWindowObject *)window;
232   GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
233
234   if (!private->accept_focus)
235     return NO;
236
237   /* Popup windows should not be able to get focused in the window
238    * manager sense, it's only handled through grabs.
239    */
240   if (private->window_type == GDK_WINDOW_TEMP)
241     return NO;
242
243   switch (impl->type_hint)
244     {
245     case GDK_WINDOW_TYPE_HINT_NORMAL:
246     case GDK_WINDOW_TYPE_HINT_DIALOG:
247     case GDK_WINDOW_TYPE_HINT_MENU:
248     case GDK_WINDOW_TYPE_HINT_TOOLBAR:
249     case GDK_WINDOW_TYPE_HINT_UTILITY:
250     case GDK_WINDOW_TYPE_HINT_DOCK:
251     case GDK_WINDOW_TYPE_HINT_DESKTOP:
252     case GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU:
253     case GDK_WINDOW_TYPE_HINT_POPUP_MENU:
254     case GDK_WINDOW_TYPE_HINT_COMBO:
255       return YES;
256       
257     case GDK_WINDOW_TYPE_HINT_SPLASHSCREEN:
258     case GDK_WINDOW_TYPE_HINT_TOOLTIP:
259     case GDK_WINDOW_TYPE_HINT_NOTIFICATION:
260     case GDK_WINDOW_TYPE_HINT_DND:
261       return NO;
262     }
263   
264   return YES;
265 }
266
267 - (void)showAndMakeKey:(BOOL)makeKey
268 {
269   GdkWindow *window = [[self contentView] gdkWindow];
270   GdkWindowObject *private = (GdkWindowObject *)window;
271   GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
272
273   inShowOrHide = YES;
274
275   if (makeKey)
276     [impl->toplevel makeKeyAndOrderFront:impl->toplevel];
277   else
278     [impl->toplevel orderFront:nil];
279
280   inShowOrHide = NO;
281 }
282
283 - (void)hide
284 {
285   GdkWindow *window = [[self contentView] gdkWindow];
286   GdkWindowObject *private = (GdkWindowObject *)window;
287   GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
288
289   inShowOrHide = YES;
290   [impl->toplevel orderOut:nil];
291   inShowOrHide = NO;
292 }
293
294 - (BOOL)trackManualMove
295 {
296   NSPoint currentLocation;
297   NSPoint newOrigin;
298   NSRect screenFrame = [[NSScreen mainScreen] visibleFrame];
299   NSRect windowFrame = [self frame];
300
301   if (!inManualMove)
302     return NO;
303
304   currentLocation = [self convertBaseToScreen:[self mouseLocationOutsideOfEventStream]];
305   newOrigin.x = currentLocation.x - initialMoveLocation.x;
306   newOrigin.y = currentLocation.y - initialMoveLocation.y;
307
308   /* Clamp vertical position to below the menu bar. */
309   if (newOrigin.y + windowFrame.size.height > screenFrame.origin.y + screenFrame.size.height)
310     newOrigin.y = screenFrame.origin.y + screenFrame.size.height - windowFrame.size.height;
311
312   [self setFrameOrigin:newOrigin];
313
314   return YES;
315 }
316
317 -(void)beginManualMove
318 {
319   NSRect frame = [self frame];
320
321   if (inMove || inManualMove || inManualResize)
322     return;
323
324   inManualMove = YES;
325
326   initialMoveLocation = [self convertBaseToScreen:[self mouseLocationOutsideOfEventStream]];
327   initialMoveLocation.x -= frame.origin.x;
328   initialMoveLocation.y -= frame.origin.y;
329 }
330
331 - (BOOL)trackManualResize
332 {
333   NSPoint currentLocation;
334   NSRect newFrame;
335   float dx, dy;
336   NSSize min_size;
337
338   if (!inManualResize || inTrackManualResize)
339     return NO;
340
341   inTrackManualResize = YES;
342
343   currentLocation = [self convertBaseToScreen:[self mouseLocationOutsideOfEventStream]];
344   currentLocation.x -= initialResizeFrame.origin.x;
345   currentLocation.y -= initialResizeFrame.origin.y;
346
347   dx = currentLocation.x - initialResizeLocation.x;
348   dy = -(currentLocation.y - initialResizeLocation.y);
349
350   newFrame = initialResizeFrame;
351   newFrame.size.width = initialResizeFrame.size.width + dx;
352   newFrame.size.height = initialResizeFrame.size.height + dy;
353
354   min_size = [self contentMinSize];
355   if (newFrame.size.width < min_size.width)
356     newFrame.size.width = min_size.width;
357   if (newFrame.size.height < min_size.height)
358     newFrame.size.height = min_size.height;
359
360   /* We could also apply aspect ratio:
361      newFrame.size.height = newFrame.size.width / [self aspectRatio].width * [self aspectRatio].height;
362   */
363
364   dy = newFrame.size.height - initialResizeFrame.size.height;
365
366   newFrame.origin.x = initialResizeFrame.origin.x;
367   newFrame.origin.y = initialResizeFrame.origin.y - dy;
368
369   [self setFrame:newFrame display:YES];
370
371   /* Let the resizing be handled by GTK+. */
372   if (g_main_context_pending (NULL))
373     g_main_context_iteration (NULL, FALSE);
374
375   inTrackManualResize = NO;
376
377   return YES;
378 }
379
380 -(void)beginManualResize
381 {
382   if (inMove || inManualMove || inManualResize)
383     return;
384
385   inManualResize = YES;
386
387   initialResizeFrame = [self frame];
388   initialResizeLocation = [self convertBaseToScreen:[self mouseLocationOutsideOfEventStream]];
389   initialResizeLocation.x -= initialResizeFrame.origin.x;
390   initialResizeLocation.y -= initialResizeFrame.origin.y;
391 }
392
393
394
395 static GdkDragContext *current_context = NULL;
396
397 static GdkDragAction
398 drag_operation_to_drag_action (NSDragOperation operation)
399 {
400   GdkDragAction result = 0;
401
402   if (operation & NSDragOperationGeneric)
403     result |= GDK_ACTION_COPY;
404
405   return result;
406 }
407
408 static NSDragOperation
409 drag_action_to_drag_operation (GdkDragAction action)
410 {
411   NSDragOperation result = 0;
412
413   if (action & GDK_ACTION_COPY)
414     result |= NSDragOperationCopy;
415
416   return result;
417 }
418
419 static void
420 update_context_from_dragging_info (id <NSDraggingInfo> sender)
421 {
422   g_assert (current_context != NULL);
423
424   GDK_DRAG_CONTEXT_PRIVATE (current_context)->dragging_info = sender;
425   current_context->suggested_action = drag_operation_to_drag_action ([sender draggingSourceOperationMask]);
426 }
427
428 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
429 {
430   GdkEvent event;
431
432   if (current_context)
433     g_object_unref (current_context);
434   
435   current_context = gdk_drag_context_new ();
436   update_context_from_dragging_info (sender);
437
438   event.dnd.type = GDK_DRAG_ENTER;
439   event.dnd.window = g_object_ref ([[self contentView] gdkWindow]);
440   event.dnd.send_event = FALSE;
441   event.dnd.context = current_context;
442   event.dnd.time = GDK_CURRENT_TIME;
443
444   (*_gdk_event_func) (&event, _gdk_event_data);
445
446   return NSDragOperationNone;
447 }
448
449 - (void)draggingEnded:(id <NSDraggingInfo>)sender
450 {
451   if (current_context)
452     g_object_unref (current_context);
453   current_context = NULL;
454 }
455
456 - (void)draggingExited:(id <NSDraggingInfo>)sender
457 {
458   GdkEvent event;
459   
460   event.dnd.type = GDK_DRAG_LEAVE;
461   event.dnd.window = g_object_ref ([[self contentView] gdkWindow]);
462   event.dnd.send_event = FALSE;
463   event.dnd.context = current_context;
464   event.dnd.time = GDK_CURRENT_TIME;
465
466   (*_gdk_event_func) (&event, _gdk_event_data);
467   
468   g_object_unref (current_context);
469   current_context = NULL;
470 }
471
472 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
473 {
474   NSPoint point = [sender draggingLocation];
475   NSPoint screen_point = [self convertBaseToScreen:point];
476   GdkEvent event;
477
478   update_context_from_dragging_info (sender);
479
480   event.dnd.type = GDK_DRAG_MOTION;
481   event.dnd.window = g_object_ref ([[self contentView] gdkWindow]);
482   event.dnd.send_event = FALSE;
483   event.dnd.context = current_context;
484   event.dnd.time = GDK_CURRENT_TIME;
485   event.dnd.x_root = screen_point.x;
486   event.dnd.y_root = _gdk_quartz_window_get_inverted_screen_y (screen_point.y);
487
488   (*_gdk_event_func) (&event, _gdk_event_data);
489
490   g_object_unref (event.dnd.window);
491
492   return drag_action_to_drag_operation (current_context->action);
493 }
494
495 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
496 {
497   NSPoint point = [sender draggingLocation];
498   NSPoint screen_point = [self convertBaseToScreen:point];
499   GdkEvent event;
500
501   update_context_from_dragging_info (sender);
502
503   event.dnd.type = GDK_DROP_START;
504   event.dnd.window = g_object_ref ([[self contentView] gdkWindow]);
505   event.dnd.send_event = FALSE;
506   event.dnd.context = current_context;
507   event.dnd.time = GDK_CURRENT_TIME;
508   event.dnd.x_root = screen_point.x;
509   event.dnd.y_root = _gdk_quartz_window_get_inverted_screen_y (screen_point.y);
510
511   (*_gdk_event_func) (&event, _gdk_event_data);
512
513   g_object_unref (event.dnd.window);
514
515   g_object_unref (current_context);
516   current_context = NULL;
517
518   return YES;
519 }
520
521 - (BOOL)wantsPeriodicDraggingUpdates
522 {
523   return NO;
524 }
525
526 - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
527 {
528   GdkEvent event;
529
530   g_assert (_gdk_quartz_drag_source_context != NULL);
531
532   event.dnd.type = GDK_DROP_FINISHED;
533   event.dnd.window = g_object_ref ([[self contentView] gdkWindow]);
534   event.dnd.send_event = FALSE;
535   event.dnd.context = _gdk_quartz_drag_source_context;
536
537   (*_gdk_event_func) (&event, _gdk_event_data);
538
539   g_object_unref (event.dnd.window);
540
541   g_object_unref (_gdk_quartz_drag_source_context);
542   _gdk_quartz_drag_source_context = NULL;
543 }
544
545 @end