]> Pileus Git - ~andy/gtk/blob - gdk/quartz/GdkQuartzNSWindow.c
x11: Clean up xsettings header
[~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     {
118       double time = ((double)[event timestamp]) * 1000.0;
119
120       _gdk_quartz_events_break_all_grabs (time);
121       inManualMove = NO;
122       inManualResize = NO;
123       inMove = NO;
124       break;
125     }
126
127     case NSLeftMouseDragged:
128       if ([self trackManualMove] || [self trackManualResize])
129         return;
130       break;
131
132     default:
133       break;
134     }
135
136   [super sendEvent:event];
137 }
138
139 -(BOOL)isInMove
140 {
141   return inMove;
142 }
143
144 -(void)checkSendEnterNotify
145 {
146   GdkWindow *window = [[self contentView] gdkWindow];
147   GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (window->impl);
148
149   /* When a new window has been created, and the mouse
150    * is in the window area, we will not receive an NSMouseEntered
151    * event.  Therefore, we synthesize an enter notify event manually.
152    */
153   if (!initialPositionKnown)
154     {
155       initialPositionKnown = YES;
156
157       if (NSPointInRect ([NSEvent mouseLocation], [self frame]))
158         {
159           NSEvent *event;
160
161           event = [NSEvent enterExitEventWithType: NSMouseEntered
162                                          location: [self mouseLocationOutsideOfEventStream]
163                                     modifierFlags: 0
164                                         timestamp: [[NSApp currentEvent] timestamp]
165                                      windowNumber: [impl->toplevel windowNumber]
166                                           context: NULL
167                                       eventNumber: 0
168                                    trackingNumber: [impl->view trackingRect]
169                                          userData: nil];
170
171           [NSApp postEvent:event atStart:NO];
172         }
173     }
174 }
175
176 -(void)windowDidMove:(NSNotification *)aNotification
177 {
178   GdkWindow *window = [[self contentView] gdkWindow];
179   GdkEvent *event;
180
181   _gdk_quartz_window_update_position (window);
182
183   /* Synthesize a configure event */
184   event = gdk_event_new (GDK_CONFIGURE);
185   event->configure.window = g_object_ref (window);
186   event->configure.x = window->x;
187   event->configure.y = window->y;
188   event->configure.width = window->width;
189   event->configure.height = window->height;
190
191   _gdk_event_queue_append (gdk_display_get_default (), event);
192
193   [self checkSendEnterNotify];
194 }
195
196 -(void)windowDidResize:(NSNotification *)aNotification
197 {
198   NSRect content_rect = [self contentRectForFrameRect:[self frame]];
199   GdkWindow *window = [[self contentView] gdkWindow];
200   GdkEvent *event;
201
202   window->width = content_rect.size.width;
203   window->height = content_rect.size.height;
204
205   /* Certain resize operations (e.g. going fullscreen), also move the
206    * origin of the window.
207    */
208   _gdk_quartz_window_update_position (window);
209
210   [[self contentView] setFrame:NSMakeRect (0, 0, window->width, window->height)];
211
212   _gdk_window_update_size (window);
213
214   /* Synthesize a configure event */
215   event = gdk_event_new (GDK_CONFIGURE);
216   event->configure.window = g_object_ref (window);
217   event->configure.x = window->x;
218   event->configure.y = window->y;
219   event->configure.width = window->width;
220   event->configure.height = window->height;
221
222   _gdk_event_queue_append (gdk_display_get_default (), event);
223
224   [self checkSendEnterNotify];
225 }
226
227 -(id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag screen:(NSScreen *)screen
228 {
229   self = [super initWithContentRect:contentRect
230                           styleMask:styleMask
231                             backing:backingType
232                               defer:flag
233                              screen:screen];
234
235   [self setAcceptsMouseMovedEvents:YES];
236   [self setDelegate:self];
237   [self setReleasedWhenClosed:YES];
238
239   return self;
240 }
241
242 -(BOOL)canBecomeMainWindow
243 {
244   GdkWindow *window = [[self contentView] gdkWindow];
245   GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (window->impl);
246
247   switch (impl->type_hint)
248     {
249     case GDK_WINDOW_TYPE_HINT_NORMAL:
250     case GDK_WINDOW_TYPE_HINT_DIALOG:
251       return YES;
252       
253     case GDK_WINDOW_TYPE_HINT_MENU:
254     case GDK_WINDOW_TYPE_HINT_TOOLBAR:
255     case GDK_WINDOW_TYPE_HINT_SPLASHSCREEN:
256     case GDK_WINDOW_TYPE_HINT_UTILITY:
257     case GDK_WINDOW_TYPE_HINT_DOCK:
258     case GDK_WINDOW_TYPE_HINT_DESKTOP:
259     case GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU:
260     case GDK_WINDOW_TYPE_HINT_POPUP_MENU:
261     case GDK_WINDOW_TYPE_HINT_TOOLTIP:
262     case GDK_WINDOW_TYPE_HINT_NOTIFICATION:
263     case GDK_WINDOW_TYPE_HINT_COMBO:
264     case GDK_WINDOW_TYPE_HINT_DND:
265       return NO;
266     }
267   
268   return YES;
269 }
270
271 -(BOOL)canBecomeKeyWindow
272 {
273   GdkWindow *window = [[self contentView] gdkWindow];
274   GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (window->impl);
275
276   if (!window->accept_focus)
277     return NO;
278
279   /* Popup windows should not be able to get focused in the window
280    * manager sense, it's only handled through grabs.
281    */
282   if (window->window_type == GDK_WINDOW_TEMP)
283     return NO;
284
285   switch (impl->type_hint)
286     {
287     case GDK_WINDOW_TYPE_HINT_NORMAL:
288     case GDK_WINDOW_TYPE_HINT_DIALOG:
289     case GDK_WINDOW_TYPE_HINT_MENU:
290     case GDK_WINDOW_TYPE_HINT_TOOLBAR:
291     case GDK_WINDOW_TYPE_HINT_UTILITY:
292     case GDK_WINDOW_TYPE_HINT_DOCK:
293     case GDK_WINDOW_TYPE_HINT_DESKTOP:
294     case GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU:
295     case GDK_WINDOW_TYPE_HINT_POPUP_MENU:
296     case GDK_WINDOW_TYPE_HINT_COMBO:
297       return YES;
298       
299     case GDK_WINDOW_TYPE_HINT_SPLASHSCREEN:
300     case GDK_WINDOW_TYPE_HINT_TOOLTIP:
301     case GDK_WINDOW_TYPE_HINT_NOTIFICATION:
302     case GDK_WINDOW_TYPE_HINT_DND:
303       return NO;
304     }
305   
306   return YES;
307 }
308
309 - (void)showAndMakeKey:(BOOL)makeKey
310 {
311   GdkWindow *window = [[self contentView] gdkWindow];
312   GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (window->impl);
313
314   inShowOrHide = YES;
315
316   if (makeKey)
317     [impl->toplevel makeKeyAndOrderFront:impl->toplevel];
318   else
319     [impl->toplevel orderFront:nil];
320
321   inShowOrHide = NO;
322
323   [self checkSendEnterNotify];
324 }
325
326 - (void)hide
327 {
328   GdkWindow *window = [[self contentView] gdkWindow];
329   GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (window->impl);
330
331   inShowOrHide = YES;
332   [impl->toplevel orderOut:nil];
333   inShowOrHide = NO;
334
335   initialPositionKnown = NO;
336 }
337
338 - (BOOL)trackManualMove
339 {
340   NSPoint currentLocation;
341   NSPoint newOrigin;
342   NSRect screenFrame = [[NSScreen mainScreen] visibleFrame];
343   NSRect windowFrame = [self frame];
344
345   if (!inManualMove)
346     return NO;
347
348   currentLocation = [self convertBaseToScreen:[self mouseLocationOutsideOfEventStream]];
349   newOrigin.x = currentLocation.x - initialMoveLocation.x;
350   newOrigin.y = currentLocation.y - initialMoveLocation.y;
351
352   /* Clamp vertical position to below the menu bar. */
353   if (newOrigin.y + windowFrame.size.height > screenFrame.origin.y + screenFrame.size.height)
354     newOrigin.y = screenFrame.origin.y + screenFrame.size.height - windowFrame.size.height;
355
356   [self setFrameOrigin:newOrigin];
357
358   return YES;
359 }
360
361 -(BOOL)isInManualResize
362 {
363   return inManualResize;
364 }
365
366 -(void)beginManualMove
367 {
368   NSRect frame = [self frame];
369
370   if (inMove || inManualMove || inManualResize)
371     return;
372
373   inManualMove = YES;
374
375   initialMoveLocation = [self convertBaseToScreen:[self mouseLocationOutsideOfEventStream]];
376   initialMoveLocation.x -= frame.origin.x;
377   initialMoveLocation.y -= frame.origin.y;
378 }
379
380 - (BOOL)trackManualResize
381 {
382   NSPoint currentLocation;
383   NSRect newFrame;
384   float dx, dy;
385   NSSize min_size;
386
387   if (!inManualResize || inTrackManualResize)
388     return NO;
389
390   inTrackManualResize = YES;
391
392   currentLocation = [self convertBaseToScreen:[self mouseLocationOutsideOfEventStream]];
393   currentLocation.x -= initialResizeFrame.origin.x;
394   currentLocation.y -= initialResizeFrame.origin.y;
395
396   dx = currentLocation.x - initialResizeLocation.x;
397   dy = -(currentLocation.y - initialResizeLocation.y);
398
399   newFrame = initialResizeFrame;
400   newFrame.size.width = initialResizeFrame.size.width + dx;
401   newFrame.size.height = initialResizeFrame.size.height + dy;
402
403   min_size = [self contentMinSize];
404   if (newFrame.size.width < min_size.width)
405     newFrame.size.width = min_size.width;
406   if (newFrame.size.height < min_size.height)
407     newFrame.size.height = min_size.height;
408
409   /* We could also apply aspect ratio:
410      newFrame.size.height = newFrame.size.width / [self aspectRatio].width * [self aspectRatio].height;
411   */
412
413   dy = newFrame.size.height - initialResizeFrame.size.height;
414
415   newFrame.origin.x = initialResizeFrame.origin.x;
416   newFrame.origin.y = initialResizeFrame.origin.y - dy;
417
418   [self setFrame:newFrame display:YES];
419
420   /* Let the resizing be handled by GTK+. */
421   if (g_main_context_pending (NULL))
422     g_main_context_iteration (NULL, FALSE);
423
424   inTrackManualResize = NO;
425
426   return YES;
427 }
428
429 -(void)beginManualResize
430 {
431   if (inMove || inManualMove || inManualResize)
432     return;
433
434   inManualResize = YES;
435
436   initialResizeFrame = [self frame];
437   initialResizeLocation = [self convertBaseToScreen:[self mouseLocationOutsideOfEventStream]];
438   initialResizeLocation.x -= initialResizeFrame.origin.x;
439   initialResizeLocation.y -= initialResizeFrame.origin.y;
440 }
441
442
443
444 static GdkDragContext *current_context = NULL;
445
446 static GdkDragAction
447 drag_operation_to_drag_action (NSDragOperation operation)
448 {
449   GdkDragAction result = 0;
450
451   /* GDK and Quartz drag operations do not map 1:1.
452    * This mapping represents about the best that we
453    * can come up.
454    *
455    * Note that NSDragOperationPrivate and GDK_ACTION_PRIVATE
456    * have almost opposite meanings: the GDK one means that the
457    * destination is solely responsible for the action; the Quartz
458    * one means that the source and destination will agree
459    * privately on the action. NSOperationGeneric is close in meaning
460    * to GDK_ACTION_PRIVATE but there is a problem: it will be
461    * sent for any ordinary drag, and likely not understood
462    * by any intra-widget drag (since the source & dest are the
463    * same).
464    */
465
466   if (operation & NSDragOperationGeneric)
467     result |= GDK_ACTION_MOVE;
468   if (operation & NSDragOperationCopy)
469     result |= GDK_ACTION_COPY;
470   if (operation & NSDragOperationMove)
471     result |= GDK_ACTION_MOVE;
472   if (operation & NSDragOperationLink)
473     result |= GDK_ACTION_LINK;
474
475   return result;
476 }
477
478 static NSDragOperation
479 drag_action_to_drag_operation (GdkDragAction action)
480 {
481   NSDragOperation result = 0;
482
483   if (action & GDK_ACTION_COPY)
484     result |= NSDragOperationCopy;
485   if (action & GDK_ACTION_LINK)
486     result |= NSDragOperationLink;
487   if (action & GDK_ACTION_MOVE)
488     result |= NSDragOperationMove;
489
490   return result;
491 }
492
493 static void
494 update_context_from_dragging_info (id <NSDraggingInfo> sender)
495 {
496   g_assert (current_context != NULL);
497
498   GDK_QUARTZ_DRAG_CONTEXT (current_context)->dragging_info = sender;
499   current_context->suggested_action = drag_operation_to_drag_action ([sender draggingSourceOperationMask]);
500   current_context->actions = current_context->suggested_action;
501 }
502
503 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
504 {
505   GdkDeviceManager *device_manager;
506   GdkEvent *event;
507   GdkWindow *window;
508
509   if (current_context)
510     g_object_unref (current_context);
511   
512   current_context = g_object_new (GDK_TYPE_QUARTZ_DRAG_CONTEXT, NULL);
513   update_context_from_dragging_info (sender);
514
515   window = [[self contentView] gdkWindow];
516
517   device_manager = gdk_display_get_device_manager (gdk_display_get_default ());
518   gdk_drag_context_set_device (current_context,
519                                gdk_device_manager_get_client_pointer (device_manager));
520
521   event = gdk_event_new (GDK_DRAG_ENTER);
522   event->dnd.window = g_object_ref (window);
523   event->dnd.send_event = FALSE;
524   event->dnd.context = g_object_ref (current_context);
525   event->dnd.time = GDK_CURRENT_TIME;
526
527   gdk_event_set_device (event, gdk_drag_context_get_device (current_context));
528
529   _gdk_event_emit (event);
530
531   gdk_event_free (event);
532
533   return NSDragOperationNone;
534 }
535
536 - (void)draggingEnded:(id <NSDraggingInfo>)sender
537 {
538   /* leave a note for the source about what action was taken */
539   if (_gdk_quartz_drag_source_context && current_context)
540    _gdk_quartz_drag_source_context->action = current_context->action;
541
542   if (current_context)
543     g_object_unref (current_context);
544   current_context = NULL;
545 }
546
547 - (void)draggingExited:(id <NSDraggingInfo>)sender
548 {
549   GdkEvent *event;
550   
551   event = gdk_event_new (GDK_DRAG_LEAVE);
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
557   gdk_event_set_device (event, gdk_drag_context_get_device (current_context));
558
559   _gdk_event_emit (event);
560
561   gdk_event_free (event);
562   
563   g_object_unref (current_context);
564   current_context = NULL;
565 }
566
567 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
568 {
569   NSPoint point = [sender draggingLocation];
570   NSPoint screen_point = [self convertBaseToScreen:point];
571   GdkEvent *event;
572   int gx, gy;
573
574   update_context_from_dragging_info (sender);
575   _gdk_quartz_window_nspoint_to_gdk_xy (screen_point, &gx, &gy);
576
577   event = gdk_event_new (GDK_DRAG_MOTION);
578   event->dnd.window = g_object_ref ([[self contentView] gdkWindow]);
579   event->dnd.send_event = FALSE;
580   event->dnd.context = g_object_ref (current_context);
581   event->dnd.time = GDK_CURRENT_TIME;
582   event->dnd.x_root = gx;
583   event->dnd.y_root = gy;
584
585   gdk_event_set_device (event, gdk_drag_context_get_device (current_context));
586
587   _gdk_event_emit (event);
588
589   gdk_event_free (event);
590
591   return drag_action_to_drag_operation (current_context->action);
592 }
593
594 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
595 {
596   NSPoint point = [sender draggingLocation];
597   NSPoint screen_point = [self convertBaseToScreen:point];
598   GdkEvent *event;
599   int gy, gx;
600
601   update_context_from_dragging_info (sender);
602   _gdk_quartz_window_nspoint_to_gdk_xy (screen_point, &gx, &gy);
603
604   event = gdk_event_new (GDK_DROP_START);
605   event->dnd.window = g_object_ref ([[self contentView] gdkWindow]);
606   event->dnd.send_event = FALSE;
607   event->dnd.context = g_object_ref (current_context);
608   event->dnd.time = GDK_CURRENT_TIME;
609   event->dnd.x_root = gx;
610   event->dnd.y_root = gy;
611
612   gdk_event_set_device (event, gdk_drag_context_get_device (current_context));
613
614   _gdk_event_emit (event);
615
616   gdk_event_free (event);
617
618   g_object_unref (current_context);
619   current_context = NULL;
620
621   return YES;
622 }
623
624 - (BOOL)wantsPeriodicDraggingUpdates
625 {
626   return NO;
627 }
628
629 - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
630 {
631   GdkEvent *event;
632
633   g_assert (_gdk_quartz_drag_source_context != NULL);
634
635   event = gdk_event_new (GDK_DROP_FINISHED);
636   event->dnd.window = g_object_ref ([[self contentView] gdkWindow]);
637   event->dnd.send_event = FALSE;
638   event->dnd.context = g_object_ref (_gdk_quartz_drag_source_context);
639
640   gdk_event_set_device (event,
641                         gdk_drag_context_get_device (_gdk_quartz_drag_source_context));
642
643   _gdk_event_emit (event);
644
645   gdk_event_free (event);
646
647   g_object_unref (_gdk_quartz_drag_source_context);
648   _gdk_quartz_drag_source_context = NULL;
649 }
650
651 @end