]> Pileus Git - ~andy/gtk/blob - gdk/quartz/GdkQuartzWindow.c
[broadway] Add initial keyboard event support
[~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   GdkWindow *window = [[self contentView] gdkWindow];
143   GdkWindowObject *private = (GdkWindowObject *)window;
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 = private->x;
152   event->configure.y = private->y;
153   event->configure.width = private->width;
154   event->configure.height = private->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   GdkWindowObject *private = (GdkWindowObject *)window;
164   GdkEvent *event;
165
166   private->width = content_rect.size.width;
167   private->height = content_rect.size.height;
168
169   [[self contentView] setFrame:NSMakeRect (0, 0, private->width, private->height)];
170
171   _gdk_window_update_size (window);
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:(NSUInteger)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag screen:(NSScreen *)screen
185 {
186   self = [super initWithContentRect:contentRect
187                           styleMask:styleMask
188                             backing:backingType
189                               defer:flag
190                              screen:screen];
191
192   [self setAcceptsMouseMovedEvents:YES];
193   [self setDelegate:self];
194   [self setReleasedWhenClosed:YES];
195
196   return self;
197 }
198
199 -(BOOL)canBecomeMainWindow
200 {
201   GdkWindow *window = [[self contentView] gdkWindow];
202   GdkWindowObject *private = (GdkWindowObject *)window;
203   GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
204
205   switch (impl->type_hint)
206     {
207     case GDK_WINDOW_TYPE_HINT_NORMAL:
208     case GDK_WINDOW_TYPE_HINT_DIALOG:
209       return YES;
210       
211     case GDK_WINDOW_TYPE_HINT_MENU:
212     case GDK_WINDOW_TYPE_HINT_TOOLBAR:
213     case GDK_WINDOW_TYPE_HINT_SPLASHSCREEN:
214     case GDK_WINDOW_TYPE_HINT_UTILITY:
215     case GDK_WINDOW_TYPE_HINT_DOCK:
216     case GDK_WINDOW_TYPE_HINT_DESKTOP:
217     case GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU:
218     case GDK_WINDOW_TYPE_HINT_POPUP_MENU:
219     case GDK_WINDOW_TYPE_HINT_TOOLTIP:
220     case GDK_WINDOW_TYPE_HINT_NOTIFICATION:
221     case GDK_WINDOW_TYPE_HINT_COMBO:
222     case GDK_WINDOW_TYPE_HINT_DND:
223       return NO;
224     }
225   
226   return YES;
227 }
228
229 -(BOOL)canBecomeKeyWindow
230 {
231   GdkWindow *window = [[self contentView] gdkWindow];
232   GdkWindowObject *private = (GdkWindowObject *)window;
233   GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
234
235   if (!private->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 (private->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   GdkWindowObject *private = (GdkWindowObject *)window;
272   GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
273
274   inShowOrHide = YES;
275
276   if (makeKey)
277     [impl->toplevel makeKeyAndOrderFront:impl->toplevel];
278   else
279     [impl->toplevel orderFront:nil];
280
281   inShowOrHide = NO;
282 }
283
284 - (void)hide
285 {
286   GdkWindow *window = [[self contentView] gdkWindow];
287   GdkWindowObject *private = (GdkWindowObject *)window;
288   GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
289
290   inShowOrHide = YES;
291   [impl->toplevel orderOut:nil];
292   inShowOrHide = NO;
293 }
294
295 - (BOOL)trackManualMove
296 {
297   NSPoint currentLocation;
298   NSPoint newOrigin;
299   NSRect screenFrame = [[NSScreen mainScreen] visibleFrame];
300   NSRect windowFrame = [self frame];
301
302   if (!inManualMove)
303     return NO;
304
305   currentLocation = [self convertBaseToScreen:[self mouseLocationOutsideOfEventStream]];
306   newOrigin.x = currentLocation.x - initialMoveLocation.x;
307   newOrigin.y = currentLocation.y - initialMoveLocation.y;
308
309   /* Clamp vertical position to below the menu bar. */
310   if (newOrigin.y + windowFrame.size.height > screenFrame.origin.y + screenFrame.size.height)
311     newOrigin.y = screenFrame.origin.y + screenFrame.size.height - windowFrame.size.height;
312
313   [self setFrameOrigin:newOrigin];
314
315   return YES;
316 }
317
318 -(void)beginManualMove
319 {
320   NSRect frame = [self frame];
321
322   if (inMove || inManualMove || inManualResize)
323     return;
324
325   inManualMove = YES;
326
327   initialMoveLocation = [self convertBaseToScreen:[self mouseLocationOutsideOfEventStream]];
328   initialMoveLocation.x -= frame.origin.x;
329   initialMoveLocation.y -= frame.origin.y;
330 }
331
332 - (BOOL)trackManualResize
333 {
334   NSPoint currentLocation;
335   NSRect newFrame;
336   float dx, dy;
337   NSSize min_size;
338
339   if (!inManualResize || inTrackManualResize)
340     return NO;
341
342   inTrackManualResize = YES;
343
344   currentLocation = [self convertBaseToScreen:[self mouseLocationOutsideOfEventStream]];
345   currentLocation.x -= initialResizeFrame.origin.x;
346   currentLocation.y -= initialResizeFrame.origin.y;
347
348   dx = currentLocation.x - initialResizeLocation.x;
349   dy = -(currentLocation.y - initialResizeLocation.y);
350
351   newFrame = initialResizeFrame;
352   newFrame.size.width = initialResizeFrame.size.width + dx;
353   newFrame.size.height = initialResizeFrame.size.height + dy;
354
355   min_size = [self contentMinSize];
356   if (newFrame.size.width < min_size.width)
357     newFrame.size.width = min_size.width;
358   if (newFrame.size.height < min_size.height)
359     newFrame.size.height = min_size.height;
360
361   /* We could also apply aspect ratio:
362      newFrame.size.height = newFrame.size.width / [self aspectRatio].width * [self aspectRatio].height;
363   */
364
365   dy = newFrame.size.height - initialResizeFrame.size.height;
366
367   newFrame.origin.x = initialResizeFrame.origin.x;
368   newFrame.origin.y = initialResizeFrame.origin.y - dy;
369
370   [self setFrame:newFrame display:YES];
371
372   /* Let the resizing be handled by GTK+. */
373   if (g_main_context_pending (NULL))
374     g_main_context_iteration (NULL, FALSE);
375
376   inTrackManualResize = NO;
377
378   return YES;
379 }
380
381 -(void)beginManualResize
382 {
383   if (inMove || inManualMove || inManualResize)
384     return;
385
386   inManualResize = YES;
387
388   initialResizeFrame = [self frame];
389   initialResizeLocation = [self convertBaseToScreen:[self mouseLocationOutsideOfEventStream]];
390   initialResizeLocation.x -= initialResizeFrame.origin.x;
391   initialResizeLocation.y -= initialResizeFrame.origin.y;
392 }
393
394
395
396 static GdkDragContext *current_context = NULL;
397
398 static GdkDragAction
399 drag_operation_to_drag_action (NSDragOperation operation)
400 {
401   GdkDragAction result = 0;
402
403   /* GDK and Quartz drag operations do not map 1:1.
404    * This mapping represents about the best that we
405    * can come up.
406    *
407    * Note that NSDragOperationPrivate and GDK_ACTION_PRIVATE
408    * have almost opposite meanings: the GDK one means that the
409    * destination is solely responsible for the action; the Quartz
410    * one means that the source and destination will agree
411    * privately on the action. NSOperationGeneric is close in meaning
412    * to GDK_ACTION_PRIVATE but there is a problem: it will be
413    * sent for any ordinary drag, and likely not understood
414    * by any intra-widget drag (since the source & dest are the
415    * same).
416    */
417
418   if (operation & NSDragOperationGeneric)
419     result |= GDK_ACTION_MOVE;
420   if (operation & NSDragOperationCopy)
421     result |= GDK_ACTION_COPY;
422   if (operation & NSDragOperationMove)
423     result |= GDK_ACTION_MOVE;
424   if (operation & NSDragOperationLink)
425     result |= GDK_ACTION_LINK;
426
427   return result;
428 }
429
430 static NSDragOperation
431 drag_action_to_drag_operation (GdkDragAction action)
432 {
433   NSDragOperation result = 0;
434
435   if (action & GDK_ACTION_COPY)
436     result |= NSDragOperationCopy;
437   if (action & GDK_ACTION_LINK)
438     result |= NSDragOperationLink;
439   if (action & GDK_ACTION_MOVE)
440     result |= NSDragOperationMove;
441
442   return result;
443 }
444
445 static void
446 update_context_from_dragging_info (id <NSDraggingInfo> sender)
447 {
448   g_assert (current_context != NULL);
449
450   GDK_DRAG_CONTEXT_PRIVATE (current_context)->dragging_info = sender;
451   current_context->suggested_action = drag_operation_to_drag_action ([sender draggingSourceOperationMask]);
452   current_context->actions = current_context->suggested_action;
453 }
454
455 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
456 {
457   GdkDeviceManager *device_manager;
458   GdkEvent *event;
459   GdkWindow *window;
460
461   if (current_context)
462     g_object_unref (current_context);
463   
464   current_context = gdk_drag_context_new ();
465   update_context_from_dragging_info (sender);
466
467   window = [[self contentView] gdkWindow];
468
469   device_manager = gdk_display_get_device_manager (gdk_display_get_default ());
470   gdk_drag_context_set_device (current_context,
471                                gdk_device_manager_get_client_pointer (device_manager));
472
473   event = gdk_event_new (GDK_DRAG_ENTER);
474   event->dnd.window = g_object_ref (window);
475   event->dnd.send_event = FALSE;
476   event->dnd.context = g_object_ref (current_context);
477   event->dnd.time = GDK_CURRENT_TIME;
478
479   gdk_event_set_device (event, gdk_drag_context_get_device (current_context));
480
481   (*_gdk_event_func) (event, _gdk_event_data);
482
483   gdk_event_free (event);
484
485   return NSDragOperationNone;
486 }
487
488 - (void)draggingEnded:(id <NSDraggingInfo>)sender
489 {
490   /* leave a note for the source about what action was taken */
491   if (_gdk_quartz_drag_source_context && current_context)
492    _gdk_quartz_drag_source_context->action = current_context->action;
493
494   if (current_context)
495     g_object_unref (current_context);
496   current_context = NULL;
497 }
498
499 - (void)draggingExited:(id <NSDraggingInfo>)sender
500 {
501   GdkEvent *event;
502   
503   event = gdk_event_new (GDK_DRAG_LEAVE);
504   event->dnd.window = g_object_ref ([[self contentView] gdkWindow]);
505   event->dnd.send_event = FALSE;
506   event->dnd.context = g_object_ref (current_context);
507   event->dnd.time = GDK_CURRENT_TIME;
508
509   gdk_event_set_device (event, gdk_drag_context_get_device (current_context));
510
511   (*_gdk_event_func) (event, _gdk_event_data);
512
513   gdk_event_free (event);
514   
515   g_object_unref (current_context);
516   current_context = NULL;
517 }
518
519 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
520 {
521   NSPoint point = [sender draggingLocation];
522   NSPoint screen_point = [self convertBaseToScreen:point];
523   GdkEvent *event;
524   int gx, gy;
525
526   update_context_from_dragging_info (sender);
527   _gdk_quartz_window_nspoint_to_gdk_xy (screen_point, &gx, &gy);
528
529   event = gdk_event_new (GDK_DRAG_MOTION);
530   event->dnd.window = g_object_ref ([[self contentView] gdkWindow]);
531   event->dnd.send_event = FALSE;
532   event->dnd.context = g_object_ref (current_context);
533   event->dnd.time = GDK_CURRENT_TIME;
534   event->dnd.x_root = gx;
535   event->dnd.y_root = gy;
536
537   gdk_event_set_device (event, gdk_drag_context_get_device (current_context));
538
539   (*_gdk_event_func) (event, _gdk_event_data);
540
541   gdk_event_free (event);
542
543   return drag_action_to_drag_operation (current_context->action);
544 }
545
546 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
547 {
548   NSPoint point = [sender draggingLocation];
549   NSPoint screen_point = [self convertBaseToScreen:point];
550   GdkEvent *event;
551   int gy, gx;
552
553   update_context_from_dragging_info (sender);
554   _gdk_quartz_window_nspoint_to_gdk_xy (screen_point, &gx, &gy);
555
556   event = gdk_event_new (GDK_DROP_START);
557   event->dnd.window = g_object_ref ([[self contentView] gdkWindow]);
558   event->dnd.send_event = FALSE;
559   event->dnd.context = g_object_ref (current_context);
560   event->dnd.time = GDK_CURRENT_TIME;
561   event->dnd.x_root = gx;
562   event->dnd.y_root = gy;
563
564   gdk_event_set_device (event, gdk_drag_context_get_device (current_context));
565
566   (*_gdk_event_func) (event, _gdk_event_data);
567
568   gdk_event_free (event);
569
570   g_object_unref (current_context);
571   current_context = NULL;
572
573   return YES;
574 }
575
576 - (BOOL)wantsPeriodicDraggingUpdates
577 {
578   return NO;
579 }
580
581 - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
582 {
583   GdkEvent *event;
584
585   g_assert (_gdk_quartz_drag_source_context != NULL);
586
587   event = gdk_event_new (GDK_DROP_FINISHED);
588   event->dnd.window = g_object_ref ([[self contentView] gdkWindow]);
589   event->dnd.send_event = FALSE;
590   event->dnd.context = g_object_ref (_gdk_quartz_drag_source_context);
591
592   gdk_event_set_device (event,
593                         gdk_drag_context_get_device (_gdk_quartz_drag_source_context));
594
595   (*_gdk_event_func) (event, _gdk_event_data);
596
597   gdk_event_free (event);
598
599   g_object_unref (_gdk_quartz_drag_source_context);
600   _gdk_quartz_drag_source_context = NULL;
601 }
602
603 @end