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