3 * Copyright (C) 2005-2007 Imendio AB
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.
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.
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.
21 #import "GdkQuartzNSWindow.h"
22 #include "gdkquartzwindow.h"
23 #include "gdkdnd-quartz.h"
24 #include "gdkprivate-quartz.h"
26 @implementation GdkQuartzNSWindow
28 -(BOOL)windowShouldClose:(id)sender
30 GdkWindow *window = [[self contentView] gdkWindow];
33 event = gdk_event_new (GDK_DELETE);
35 event->any.window = g_object_ref (window);
36 event->any.send_event = FALSE;
38 _gdk_event_queue_append (gdk_display_get_default (), event);
43 -(void)windowWillMiniaturize:(NSNotification *)aNotification
45 GdkWindow *window = [[self contentView] gdkWindow];
47 _gdk_quartz_window_detach_from_parent (window);
50 -(void)windowDidMiniaturize:(NSNotification *)aNotification
52 GdkWindow *window = [[self contentView] gdkWindow];
54 gdk_synthesize_window_state (window, 0,
55 GDK_WINDOW_STATE_ICONIFIED);
58 -(void)windowDidDeminiaturize:(NSNotification *)aNotification
60 GdkWindow *window = [[self contentView] gdkWindow];
62 _gdk_quartz_window_attach_to_parent (window);
64 gdk_synthesize_window_state (window, GDK_WINDOW_STATE_ICONIFIED, 0);
67 -(void)windowDidBecomeKey:(NSNotification *)aNotification
69 GdkWindow *window = [[self contentView] gdkWindow];
71 _gdk_quartz_events_update_focus_window (window, TRUE);
74 -(void)windowDidResignKey:(NSNotification *)aNotification
76 GdkWindow *window = [[self contentView] gdkWindow];
78 _gdk_quartz_events_update_focus_window (window, FALSE);
81 -(void)windowDidBecomeMain:(NSNotification *)aNotification
83 GdkWindow *window = [[self contentView] gdkWindow];
85 if (![self isVisible])
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.
95 _gdk_quartz_window_did_become_main (window);
98 -(void)windowDidResignMain:(NSNotification *)aNotification
102 window = [[self contentView] gdkWindow];
103 _gdk_quartz_window_did_resign_main (window);
106 /* Used in combination with NSLeftMouseUp in sendEvent to keep track
107 * of when the window is being moved with the mouse.
109 -(void)windowWillMove:(NSNotification *)aNotification
114 -(void)sendEvent:(NSEvent *)event
116 switch ([event type])
124 case NSLeftMouseDragged:
125 if ([self trackManualMove] || [self trackManualResize])
133 [super sendEvent:event];
141 -(void)windowDidMove:(NSNotification *)aNotification
143 GdkWindow *window = [[self contentView] gdkWindow];
146 _gdk_quartz_window_update_position (window);
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;
156 _gdk_event_queue_append (gdk_display_get_default (), event);
159 -(void)windowDidResize:(NSNotification *)aNotification
161 NSRect content_rect = [self contentRectForFrameRect:[self frame]];
162 GdkWindow *window = [[self contentView] gdkWindow];
165 window->width = content_rect.size.width;
166 window->height = content_rect.size.height;
168 [[self contentView] setFrame:NSMakeRect (0, 0, window->width, window->height)];
170 _gdk_window_update_size (window);
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;
180 _gdk_event_queue_append (gdk_display_get_default (), event);
183 -(id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag screen:(NSScreen *)screen
185 self = [super initWithContentRect:contentRect
191 [self setAcceptsMouseMovedEvents:YES];
192 [self setDelegate:self];
193 [self setReleasedWhenClosed:YES];
198 -(BOOL)canBecomeMainWindow
200 GdkWindow *window = [[self contentView] gdkWindow];
201 GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (window->impl);
203 switch (impl->type_hint)
205 case GDK_WINDOW_TYPE_HINT_NORMAL:
206 case GDK_WINDOW_TYPE_HINT_DIALOG:
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:
227 -(BOOL)canBecomeKeyWindow
229 GdkWindow *window = [[self contentView] gdkWindow];
230 GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (window->impl);
232 if (!window->accept_focus)
235 /* Popup windows should not be able to get focused in the window
236 * manager sense, it's only handled through grabs.
238 if (window->window_type == GDK_WINDOW_TEMP)
241 switch (impl->type_hint)
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:
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:
265 - (void)showAndMakeKey:(BOOL)makeKey
267 GdkWindow *window = [[self contentView] gdkWindow];
268 GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (window->impl);
273 [impl->toplevel makeKeyAndOrderFront:impl->toplevel];
275 [impl->toplevel orderFront:nil];
282 GdkWindow *window = [[self contentView] gdkWindow];
283 GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (window->impl);
286 [impl->toplevel orderOut:nil];
290 - (BOOL)trackManualMove
292 NSPoint currentLocation;
294 NSRect screenFrame = [[NSScreen mainScreen] visibleFrame];
295 NSRect windowFrame = [self frame];
300 currentLocation = [self convertBaseToScreen:[self mouseLocationOutsideOfEventStream]];
301 newOrigin.x = currentLocation.x - initialMoveLocation.x;
302 newOrigin.y = currentLocation.y - initialMoveLocation.y;
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;
308 [self setFrameOrigin:newOrigin];
313 -(void)beginManualMove
315 NSRect frame = [self frame];
317 if (inMove || inManualMove || inManualResize)
322 initialMoveLocation = [self convertBaseToScreen:[self mouseLocationOutsideOfEventStream]];
323 initialMoveLocation.x -= frame.origin.x;
324 initialMoveLocation.y -= frame.origin.y;
327 - (BOOL)trackManualResize
329 NSPoint currentLocation;
334 if (!inManualResize || inTrackManualResize)
337 inTrackManualResize = YES;
339 currentLocation = [self convertBaseToScreen:[self mouseLocationOutsideOfEventStream]];
340 currentLocation.x -= initialResizeFrame.origin.x;
341 currentLocation.y -= initialResizeFrame.origin.y;
343 dx = currentLocation.x - initialResizeLocation.x;
344 dy = -(currentLocation.y - initialResizeLocation.y);
346 newFrame = initialResizeFrame;
347 newFrame.size.width = initialResizeFrame.size.width + dx;
348 newFrame.size.height = initialResizeFrame.size.height + dy;
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;
356 /* We could also apply aspect ratio:
357 newFrame.size.height = newFrame.size.width / [self aspectRatio].width * [self aspectRatio].height;
360 dy = newFrame.size.height - initialResizeFrame.size.height;
362 newFrame.origin.x = initialResizeFrame.origin.x;
363 newFrame.origin.y = initialResizeFrame.origin.y - dy;
365 [self setFrame:newFrame display:YES];
367 /* Let the resizing be handled by GTK+. */
368 if (g_main_context_pending (NULL))
369 g_main_context_iteration (NULL, FALSE);
371 inTrackManualResize = NO;
376 -(void)beginManualResize
378 if (inMove || inManualMove || inManualResize)
381 inManualResize = YES;
383 initialResizeFrame = [self frame];
384 initialResizeLocation = [self convertBaseToScreen:[self mouseLocationOutsideOfEventStream]];
385 initialResizeLocation.x -= initialResizeFrame.origin.x;
386 initialResizeLocation.y -= initialResizeFrame.origin.y;
391 static GdkDragContext *current_context = NULL;
394 drag_operation_to_drag_action (NSDragOperation operation)
396 GdkDragAction result = 0;
398 /* GDK and Quartz drag operations do not map 1:1.
399 * This mapping represents about the best that we
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
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;
425 static NSDragOperation
426 drag_action_to_drag_operation (GdkDragAction action)
428 NSDragOperation result = 0;
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;
441 update_context_from_dragging_info (id <NSDraggingInfo> sender)
443 g_assert (current_context != NULL);
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;
450 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
452 GdkDeviceManager *device_manager;
457 g_object_unref (current_context);
459 current_context = g_object_new (GDK_TYPE_QUARTZ_DRAG_CONTEXT, NULL);
460 update_context_from_dragging_info (sender);
462 window = [[self contentView] gdkWindow];
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));
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;
474 gdk_event_set_device (event, gdk_drag_context_get_device (current_context));
476 _gdk_event_emit (event);
478 gdk_event_free (event);
480 return NSDragOperationNone;
483 - (void)draggingEnded:(id <NSDraggingInfo>)sender
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;
490 g_object_unref (current_context);
491 current_context = NULL;
494 - (void)draggingExited:(id <NSDraggingInfo>)sender
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;
504 gdk_event_set_device (event, gdk_drag_context_get_device (current_context));
506 _gdk_event_emit (event);
508 gdk_event_free (event);
510 g_object_unref (current_context);
511 current_context = NULL;
514 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
516 NSPoint point = [sender draggingLocation];
517 NSPoint screen_point = [self convertBaseToScreen:point];
521 update_context_from_dragging_info (sender);
522 _gdk_quartz_window_nspoint_to_gdk_xy (screen_point, &gx, &gy);
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;
532 gdk_event_set_device (event, gdk_drag_context_get_device (current_context));
534 _gdk_event_emit (event);
536 gdk_event_free (event);
538 return drag_action_to_drag_operation (current_context->action);
541 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
543 NSPoint point = [sender draggingLocation];
544 NSPoint screen_point = [self convertBaseToScreen:point];
548 update_context_from_dragging_info (sender);
549 _gdk_quartz_window_nspoint_to_gdk_xy (screen_point, &gx, &gy);
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;
559 gdk_event_set_device (event, gdk_drag_context_get_device (current_context));
561 _gdk_event_emit (event);
563 gdk_event_free (event);
565 g_object_unref (current_context);
566 current_context = NULL;
571 - (BOOL)wantsPeriodicDraggingUpdates
576 - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
580 g_assert (_gdk_quartz_drag_source_context != NULL);
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);
587 gdk_event_set_device (event,
588 gdk_drag_context_get_device (_gdk_quartz_drag_source_context));
590 _gdk_event_emit (event);
592 gdk_event_free (event);
594 g_object_unref (_gdk_quartz_drag_source_context);
595 _gdk_quartz_drag_source_context = NULL;