]> Pileus Git - ~andy/gtk/blob - gdk/quartz/GdkQuartzNSWindow.c
Merge branch 'master' into broadway2
[~andy/gtk] / gdk / quartz / GdkQuartzNSWindow.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 "GdkQuartzNSWindow.h"
22 #include "gdkquartzwindow.h"
23 #include "gdkdnd-quartz.h"
24 #include "gdkprivate-quartz.h"
25
26 @implementation GdkQuartzNSWindow
27
28 -(BOOL)windowShouldClose:(id)sender
29 {
30   GdkWindow *window = [[self contentView] gdkWindow];
31   GdkEvent *event;
32
33   event = gdk_event_new (GDK_DELETE);
34
35   event->any.window = g_object_ref (window);
36   event->any.send_event = FALSE;
37
38   _gdk_event_queue_append (gdk_display_get_default (), event);
39
40   return NO;
41 }
42
43 -(void)windowWillMiniaturize:(NSNotification *)aNotification
44 {
45   GdkWindow *window = [[self contentView] gdkWindow];
46
47   _gdk_quartz_window_detach_from_parent (window);
48 }
49
50 -(void)windowDidMiniaturize:(NSNotification *)aNotification
51 {
52   GdkWindow *window = [[self contentView] gdkWindow];
53
54   gdk_synthesize_window_state (window, 0, 
55                                GDK_WINDOW_STATE_ICONIFIED);
56 }
57
58 -(void)windowDidDeminiaturize:(NSNotification *)aNotification
59 {
60   GdkWindow *window = [[self contentView] gdkWindow];
61
62   _gdk_quartz_window_attach_to_parent (window);
63
64   gdk_synthesize_window_state (window, GDK_WINDOW_STATE_ICONIFIED, 0);
65 }
66
67 -(void)windowDidBecomeKey:(NSNotification *)aNotification
68 {
69   GdkWindow *window = [[self contentView] gdkWindow];
70
71   _gdk_quartz_events_update_focus_window (window, TRUE);
72 }
73
74 -(void)windowDidResignKey:(NSNotification *)aNotification
75 {
76   GdkWindow *window = [[self contentView] gdkWindow];
77
78   _gdk_quartz_events_update_focus_window (window, FALSE);
79 }
80
81 -(void)windowDidBecomeMain:(NSNotification *)aNotification
82 {
83   GdkWindow *window = [[self contentView] gdkWindow];
84
85   if (![self isVisible])
86     {
87       /* Note: This is a hack needed because for unknown reasons, hidden
88        * windows get shown when clicking the dock icon when the application
89        * is not already active.
90        */
91       [self orderOut:nil];
92       return;
93     }
94
95   _gdk_quartz_window_did_become_main (window);
96 }
97
98 -(void)windowDidResignMain:(NSNotification *)aNotification
99 {
100   GdkWindow *window;
101
102   window = [[self contentView] gdkWindow];
103   _gdk_quartz_window_did_resign_main (window);
104 }
105
106 /* Used in combination with NSLeftMouseUp in sendEvent to keep track
107  * of when the window is being moved with the mouse.
108  */
109 -(void)windowWillMove:(NSNotification *)aNotification
110 {
111   inMove = YES;
112 }
113
114 -(void)sendEvent:(NSEvent *)event
115 {
116   switch ([event type])
117     {
118     case NSLeftMouseUp:
119       inManualMove = NO;
120       inManualResize = NO;
121       inMove = NO;
122       break;
123
124     case NSLeftMouseDragged:
125       if ([self trackManualMove] || [self trackManualResize])
126         return;
127       break;
128
129     default:
130       break;
131     }
132
133   [super sendEvent:event];
134 }
135
136 -(BOOL)isInMove
137 {
138   return inMove;
139 }
140
141 -(void)windowDidMove:(NSNotification *)aNotification
142 {
143   GdkWindow *window = [[self contentView] gdkWindow];
144   GdkEvent *event;
145
146   _gdk_quartz_window_update_position (window);
147
148   /* Synthesize a configure event */
149   event = gdk_event_new (GDK_CONFIGURE);
150   event->configure.window = g_object_ref (window);
151   event->configure.x = window->x;
152   event->configure.y = window->y;
153   event->configure.width = window->width;
154   event->configure.height = window->height;
155
156   _gdk_event_queue_append (gdk_display_get_default (), event);
157 }
158
159 -(void)windowDidResize:(NSNotification *)aNotification
160 {
161   NSRect content_rect = [self contentRectForFrameRect:[self frame]];
162   GdkWindow *window = [[self contentView] gdkWindow];
163   GdkEvent *event;
164
165   window->width = content_rect.size.width;
166   window->height = content_rect.size.height;
167
168   [[self contentView] setFrame:NSMakeRect (0, 0, window->width, window->height)];
169
170   _gdk_window_update_size (window);
171
172   /* Synthesize a configure event */
173   event = gdk_event_new (GDK_CONFIGURE);
174   event->configure.window = g_object_ref (window);
175   event->configure.x = window->x;
176   event->configure.y = window->y;
177   event->configure.width = window->width;
178   event->configure.height = window->height;
179
180   _gdk_event_queue_append (gdk_display_get_default (), event);
181 }
182
183 -(id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag screen:(NSScreen *)screen
184 {
185   self = [super initWithContentRect:contentRect
186                           styleMask:styleMask
187                             backing:backingType
188                               defer:flag
189                              screen:screen];
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   GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (window->impl);
202
203   switch (impl->type_hint)
204     {
205     case GDK_WINDOW_TYPE_HINT_NORMAL:
206     case GDK_WINDOW_TYPE_HINT_DIALOG:
207       return YES;
208       
209     case GDK_WINDOW_TYPE_HINT_MENU:
210     case GDK_WINDOW_TYPE_HINT_TOOLBAR:
211     case GDK_WINDOW_TYPE_HINT_SPLASHSCREEN:
212     case GDK_WINDOW_TYPE_HINT_UTILITY:
213     case GDK_WINDOW_TYPE_HINT_DOCK:
214     case GDK_WINDOW_TYPE_HINT_DESKTOP:
215     case GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU:
216     case GDK_WINDOW_TYPE_HINT_POPUP_MENU:
217     case GDK_WINDOW_TYPE_HINT_TOOLTIP:
218     case GDK_WINDOW_TYPE_HINT_NOTIFICATION:
219     case GDK_WINDOW_TYPE_HINT_COMBO:
220     case GDK_WINDOW_TYPE_HINT_DND:
221       return NO;
222     }
223   
224   return YES;
225 }
226
227 -(BOOL)canBecomeKeyWindow
228 {
229   GdkWindow *window = [[self contentView] gdkWindow];
230   GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (window->impl);
231
232   if (!window->accept_focus)
233     return NO;
234
235   /* Popup windows should not be able to get focused in the window
236    * manager sense, it's only handled through grabs.
237    */
238   if (window->window_type == GDK_WINDOW_TEMP)
239     return NO;
240
241   switch (impl->type_hint)
242     {
243     case GDK_WINDOW_TYPE_HINT_NORMAL:
244     case GDK_WINDOW_TYPE_HINT_DIALOG:
245     case GDK_WINDOW_TYPE_HINT_MENU:
246     case GDK_WINDOW_TYPE_HINT_TOOLBAR:
247     case GDK_WINDOW_TYPE_HINT_UTILITY:
248     case GDK_WINDOW_TYPE_HINT_DOCK:
249     case GDK_WINDOW_TYPE_HINT_DESKTOP:
250     case GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU:
251     case GDK_WINDOW_TYPE_HINT_POPUP_MENU:
252     case GDK_WINDOW_TYPE_HINT_COMBO:
253       return YES;
254       
255     case GDK_WINDOW_TYPE_HINT_SPLASHSCREEN:
256     case GDK_WINDOW_TYPE_HINT_TOOLTIP:
257     case GDK_WINDOW_TYPE_HINT_NOTIFICATION:
258     case GDK_WINDOW_TYPE_HINT_DND:
259       return NO;
260     }
261   
262   return YES;
263 }
264
265 - (void)showAndMakeKey:(BOOL)makeKey
266 {
267   GdkWindow *window = [[self contentView] gdkWindow];
268   GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (window->impl);
269
270   inShowOrHide = YES;
271
272   if (makeKey)
273     [impl->toplevel makeKeyAndOrderFront:impl->toplevel];
274   else
275     [impl->toplevel orderFront:nil];
276
277   inShowOrHide = NO;
278 }
279
280 - (void)hide
281 {
282   GdkWindow *window = [[self contentView] gdkWindow];
283   GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (window->impl);
284
285   inShowOrHide = YES;
286   [impl->toplevel orderOut:nil];
287   inShowOrHide = NO;
288 }
289
290 - (BOOL)trackManualMove
291 {
292   NSPoint currentLocation;
293   NSPoint newOrigin;
294   NSRect screenFrame = [[NSScreen mainScreen] visibleFrame];
295   NSRect windowFrame = [self frame];
296
297   if (!inManualMove)
298     return NO;
299
300   currentLocation = [self convertBaseToScreen:[self mouseLocationOutsideOfEventStream]];
301   newOrigin.x = currentLocation.x - initialMoveLocation.x;
302   newOrigin.y = currentLocation.y - initialMoveLocation.y;
303
304   /* Clamp vertical position to below the menu bar. */
305   if (newOrigin.y + windowFrame.size.height > screenFrame.origin.y + screenFrame.size.height)
306     newOrigin.y = screenFrame.origin.y + screenFrame.size.height - windowFrame.size.height;
307
308   [self setFrameOrigin:newOrigin];
309
310   return YES;
311 }
312
313 -(void)beginManualMove
314 {
315   NSRect frame = [self frame];
316
317   if (inMove || inManualMove || inManualResize)
318     return;
319
320   inManualMove = YES;
321
322   initialMoveLocation = [self convertBaseToScreen:[self mouseLocationOutsideOfEventStream]];
323   initialMoveLocation.x -= frame.origin.x;
324   initialMoveLocation.y -= frame.origin.y;
325 }
326
327 - (BOOL)trackManualResize
328 {
329   NSPoint currentLocation;
330   NSRect newFrame;
331   float dx, dy;
332   NSSize min_size;
333
334   if (!inManualResize || inTrackManualResize)
335     return NO;
336
337   inTrackManualResize = YES;
338
339   currentLocation = [self convertBaseToScreen:[self mouseLocationOutsideOfEventStream]];
340   currentLocation.x -= initialResizeFrame.origin.x;
341   currentLocation.y -= initialResizeFrame.origin.y;
342
343   dx = currentLocation.x - initialResizeLocation.x;
344   dy = -(currentLocation.y - initialResizeLocation.y);
345
346   newFrame = initialResizeFrame;
347   newFrame.size.width = initialResizeFrame.size.width + dx;
348   newFrame.size.height = initialResizeFrame.size.height + dy;
349
350   min_size = [self contentMinSize];
351   if (newFrame.size.width < min_size.width)
352     newFrame.size.width = min_size.width;
353   if (newFrame.size.height < min_size.height)
354     newFrame.size.height = min_size.height;
355
356   /* We could also apply aspect ratio:
357      newFrame.size.height = newFrame.size.width / [self aspectRatio].width * [self aspectRatio].height;
358   */
359
360   dy = newFrame.size.height - initialResizeFrame.size.height;
361
362   newFrame.origin.x = initialResizeFrame.origin.x;
363   newFrame.origin.y = initialResizeFrame.origin.y - dy;
364
365   [self setFrame:newFrame display:YES];
366
367   /* Let the resizing be handled by GTK+. */
368   if (g_main_context_pending (NULL))
369     g_main_context_iteration (NULL, FALSE);
370
371   inTrackManualResize = NO;
372
373   return YES;
374 }
375
376 -(void)beginManualResize
377 {
378   if (inMove || inManualMove || inManualResize)
379     return;
380
381   inManualResize = YES;
382
383   initialResizeFrame = [self frame];
384   initialResizeLocation = [self convertBaseToScreen:[self mouseLocationOutsideOfEventStream]];
385   initialResizeLocation.x -= initialResizeFrame.origin.x;
386   initialResizeLocation.y -= initialResizeFrame.origin.y;
387 }
388
389
390
391 static GdkDragContext *current_context = NULL;
392
393 static GdkDragAction
394 drag_operation_to_drag_action (NSDragOperation operation)
395 {
396   GdkDragAction result = 0;
397
398   /* GDK and Quartz drag operations do not map 1:1.
399    * This mapping represents about the best that we
400    * can come up.
401    *
402    * Note that NSDragOperationPrivate and GDK_ACTION_PRIVATE
403    * have almost opposite meanings: the GDK one means that the
404    * destination is solely responsible for the action; the Quartz
405    * one means that the source and destination will agree
406    * privately on the action. NSOperationGeneric is close in meaning
407    * to GDK_ACTION_PRIVATE but there is a problem: it will be
408    * sent for any ordinary drag, and likely not understood
409    * by any intra-widget drag (since the source & dest are the
410    * same).
411    */
412
413   if (operation & NSDragOperationGeneric)
414     result |= GDK_ACTION_MOVE;
415   if (operation & NSDragOperationCopy)
416     result |= GDK_ACTION_COPY;
417   if (operation & NSDragOperationMove)
418     result |= GDK_ACTION_MOVE;
419   if (operation & NSDragOperationLink)
420     result |= GDK_ACTION_LINK;
421
422   return result;
423 }
424
425 static NSDragOperation
426 drag_action_to_drag_operation (GdkDragAction action)
427 {
428   NSDragOperation result = 0;
429
430   if (action & GDK_ACTION_COPY)
431     result |= NSDragOperationCopy;
432   if (action & GDK_ACTION_LINK)
433     result |= NSDragOperationLink;
434   if (action & GDK_ACTION_MOVE)
435     result |= NSDragOperationMove;
436
437   return result;
438 }
439
440 static void
441 update_context_from_dragging_info (id <NSDraggingInfo> sender)
442 {
443   g_assert (current_context != NULL);
444
445   GDK_QUARTZ_DRAG_CONTEXT (current_context)->dragging_info = sender;
446   current_context->suggested_action = drag_operation_to_drag_action ([sender draggingSourceOperationMask]);
447   current_context->actions = current_context->suggested_action;
448 }
449
450 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
451 {
452   GdkDeviceManager *device_manager;
453   GdkEvent *event;
454   GdkWindow *window;
455
456   if (current_context)
457     g_object_unref (current_context);
458   
459   current_context = g_object_new (GDK_TYPE_QUARTZ_DRAG_CONTEXT, NULL);
460   update_context_from_dragging_info (sender);
461
462   window = [[self contentView] gdkWindow];
463
464   device_manager = gdk_display_get_device_manager (gdk_display_get_default ());
465   gdk_drag_context_set_device (current_context,
466                                gdk_device_manager_get_client_pointer (device_manager));
467
468   event = gdk_event_new (GDK_DRAG_ENTER);
469   event->dnd.window = g_object_ref (window);
470   event->dnd.send_event = FALSE;
471   event->dnd.context = g_object_ref (current_context);
472   event->dnd.time = GDK_CURRENT_TIME;
473
474   gdk_event_set_device (event, gdk_drag_context_get_device (current_context));
475
476   _gdk_event_emit (event);
477
478   gdk_event_free (event);
479
480   return NSDragOperationNone;
481 }
482
483 - (void)draggingEnded:(id <NSDraggingInfo>)sender
484 {
485   /* leave a note for the source about what action was taken */
486   if (_gdk_quartz_drag_source_context && current_context)
487    _gdk_quartz_drag_source_context->action = current_context->action;
488
489   if (current_context)
490     g_object_unref (current_context);
491   current_context = NULL;
492 }
493
494 - (void)draggingExited:(id <NSDraggingInfo>)sender
495 {
496   GdkEvent *event;
497   
498   event = gdk_event_new (GDK_DRAG_LEAVE);
499   event->dnd.window = g_object_ref ([[self contentView] gdkWindow]);
500   event->dnd.send_event = FALSE;
501   event->dnd.context = g_object_ref (current_context);
502   event->dnd.time = GDK_CURRENT_TIME;
503
504   gdk_event_set_device (event, gdk_drag_context_get_device (current_context));
505
506   _gdk_event_emit (event);
507
508   gdk_event_free (event);
509   
510   g_object_unref (current_context);
511   current_context = NULL;
512 }
513
514 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
515 {
516   NSPoint point = [sender draggingLocation];
517   NSPoint screen_point = [self convertBaseToScreen:point];
518   GdkEvent *event;
519   int gx, gy;
520
521   update_context_from_dragging_info (sender);
522   _gdk_quartz_window_nspoint_to_gdk_xy (screen_point, &gx, &gy);
523
524   event = gdk_event_new (GDK_DRAG_MOTION);
525   event->dnd.window = g_object_ref ([[self contentView] gdkWindow]);
526   event->dnd.send_event = FALSE;
527   event->dnd.context = g_object_ref (current_context);
528   event->dnd.time = GDK_CURRENT_TIME;
529   event->dnd.x_root = gx;
530   event->dnd.y_root = gy;
531
532   gdk_event_set_device (event, gdk_drag_context_get_device (current_context));
533
534   _gdk_event_emit (event);
535
536   gdk_event_free (event);
537
538   return drag_action_to_drag_operation (current_context->action);
539 }
540
541 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
542 {
543   NSPoint point = [sender draggingLocation];
544   NSPoint screen_point = [self convertBaseToScreen:point];
545   GdkEvent *event;
546   int gy, gx;
547
548   update_context_from_dragging_info (sender);
549   _gdk_quartz_window_nspoint_to_gdk_xy (screen_point, &gx, &gy);
550
551   event = gdk_event_new (GDK_DROP_START);
552   event->dnd.window = g_object_ref ([[self contentView] gdkWindow]);
553   event->dnd.send_event = FALSE;
554   event->dnd.context = g_object_ref (current_context);
555   event->dnd.time = GDK_CURRENT_TIME;
556   event->dnd.x_root = gx;
557   event->dnd.y_root = gy;
558
559   gdk_event_set_device (event, gdk_drag_context_get_device (current_context));
560
561   _gdk_event_emit (event);
562
563   gdk_event_free (event);
564
565   g_object_unref (current_context);
566   current_context = NULL;
567
568   return YES;
569 }
570
571 - (BOOL)wantsPeriodicDraggingUpdates
572 {
573   return NO;
574 }
575
576 - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
577 {
578   GdkEvent *event;
579
580   g_assert (_gdk_quartz_drag_source_context != NULL);
581
582   event = gdk_event_new (GDK_DROP_FINISHED);
583   event->dnd.window = g_object_ref ([[self contentView] gdkWindow]);
584   event->dnd.send_event = FALSE;
585   event->dnd.context = g_object_ref (_gdk_quartz_drag_source_context);
586
587   gdk_event_set_device (event,
588                         gdk_drag_context_get_device (_gdk_quartz_drag_source_context));
589
590   _gdk_event_emit (event);
591
592   gdk_event_free (event);
593
594   g_object_unref (_gdk_quartz_drag_source_context);
595   _gdk_quartz_drag_source_context = NULL;
596 }
597
598 @end