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, see <http://www.gnu.org/licenses/>.
19 #import "GdkQuartzNSWindow.h"
20 #include "gdkquartzwindow.h"
21 #include "gdkdnd-quartz.h"
22 #include "gdkprivate-quartz.h"
24 @implementation GdkQuartzNSWindow
26 -(BOOL)windowShouldClose:(id)sender
28 GdkWindow *window = [[self contentView] gdkWindow];
31 event = gdk_event_new (GDK_DELETE);
33 event->any.window = g_object_ref (window);
34 event->any.send_event = FALSE;
36 _gdk_event_queue_append (gdk_display_get_default (), event);
41 -(void)windowWillMiniaturize:(NSNotification *)aNotification
43 GdkWindow *window = [[self contentView] gdkWindow];
45 _gdk_quartz_window_detach_from_parent (window);
48 -(void)windowDidMiniaturize:(NSNotification *)aNotification
50 GdkWindow *window = [[self contentView] gdkWindow];
52 gdk_synthesize_window_state (window, 0,
53 GDK_WINDOW_STATE_ICONIFIED);
56 -(void)windowDidDeminiaturize:(NSNotification *)aNotification
58 GdkWindow *window = [[self contentView] gdkWindow];
60 _gdk_quartz_window_attach_to_parent (window);
62 gdk_synthesize_window_state (window, GDK_WINDOW_STATE_ICONIFIED, 0);
65 -(void)windowDidBecomeKey:(NSNotification *)aNotification
67 GdkWindow *window = [[self contentView] gdkWindow];
69 _gdk_quartz_events_update_focus_window (window, TRUE);
72 -(void)windowDidResignKey:(NSNotification *)aNotification
74 GdkWindow *window = [[self contentView] gdkWindow];
76 _gdk_quartz_events_update_focus_window (window, FALSE);
79 -(void)windowDidBecomeMain:(NSNotification *)aNotification
81 GdkWindow *window = [[self contentView] gdkWindow];
83 if (![self isVisible])
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.
93 _gdk_quartz_window_did_become_main (window);
96 -(void)windowDidResignMain:(NSNotification *)aNotification
100 window = [[self contentView] gdkWindow];
101 _gdk_quartz_window_did_resign_main (window);
104 /* Used in combination with NSLeftMouseUp in sendEvent to keep track
105 * of when the window is being moved with the mouse.
107 -(void)windowWillMove:(NSNotification *)aNotification
112 -(void)sendEvent:(NSEvent *)event
114 switch ([event type])
118 double time = ((double)[event timestamp]) * 1000.0;
120 _gdk_quartz_events_break_all_grabs (time);
127 case NSLeftMouseDragged:
128 if ([self trackManualMove] || [self trackManualResize])
136 [super sendEvent:event];
144 -(void)checkSendEnterNotify
146 GdkWindow *window = [[self contentView] gdkWindow];
147 GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (window->impl);
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.
153 if (!initialPositionKnown)
155 initialPositionKnown = YES;
157 if (NSPointInRect ([NSEvent mouseLocation], [self frame]))
161 event = [NSEvent enterExitEventWithType: NSMouseEntered
162 location: [self mouseLocationOutsideOfEventStream]
164 timestamp: [[NSApp currentEvent] timestamp]
165 windowNumber: [impl->toplevel windowNumber]
168 trackingNumber: [impl->view trackingRect]
171 [NSApp postEvent:event atStart:NO];
176 -(void)windowDidMove:(NSNotification *)aNotification
178 GdkWindow *window = [[self contentView] gdkWindow];
181 _gdk_quartz_window_update_position (window);
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;
191 _gdk_event_queue_append (gdk_display_get_default (), event);
193 [self checkSendEnterNotify];
196 -(void)windowDidResize:(NSNotification *)aNotification
198 NSRect content_rect = [self contentRectForFrameRect:[self frame]];
199 GdkWindow *window = [[self contentView] gdkWindow];
202 window->width = content_rect.size.width;
203 window->height = content_rect.size.height;
205 /* Certain resize operations (e.g. going fullscreen), also move the
206 * origin of the window.
208 _gdk_quartz_window_update_position (window);
210 [[self contentView] setFrame:NSMakeRect (0, 0, window->width, window->height)];
212 _gdk_window_update_size (window);
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;
222 _gdk_event_queue_append (gdk_display_get_default (), event);
224 [self checkSendEnterNotify];
227 -(id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag screen:(NSScreen *)screen
229 self = [super initWithContentRect:contentRect
235 [self setAcceptsMouseMovedEvents:YES];
236 [self setDelegate:self];
237 [self setReleasedWhenClosed:YES];
242 -(BOOL)canBecomeMainWindow
244 GdkWindow *window = [[self contentView] gdkWindow];
245 GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (window->impl);
247 switch (impl->type_hint)
249 case GDK_WINDOW_TYPE_HINT_NORMAL:
250 case GDK_WINDOW_TYPE_HINT_DIALOG:
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:
271 -(BOOL)canBecomeKeyWindow
273 GdkWindow *window = [[self contentView] gdkWindow];
274 GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (window->impl);
276 if (!window->accept_focus)
279 /* Popup windows should not be able to get focused in the window
280 * manager sense, it's only handled through grabs.
282 if (window->window_type == GDK_WINDOW_TEMP)
285 switch (impl->type_hint)
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:
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:
309 - (void)showAndMakeKey:(BOOL)makeKey
311 GdkWindow *window = [[self contentView] gdkWindow];
312 GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (window->impl);
317 [impl->toplevel makeKeyAndOrderFront:impl->toplevel];
319 [impl->toplevel orderFront:nil];
323 [self checkSendEnterNotify];
328 GdkWindow *window = [[self contentView] gdkWindow];
329 GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (window->impl);
332 [impl->toplevel orderOut:nil];
335 initialPositionKnown = NO;
338 - (BOOL)trackManualMove
340 NSPoint currentLocation;
342 NSRect screenFrame = [[NSScreen mainScreen] visibleFrame];
343 NSRect windowFrame = [self frame];
348 currentLocation = [self convertBaseToScreen:[self mouseLocationOutsideOfEventStream]];
349 newOrigin.x = currentLocation.x - initialMoveLocation.x;
350 newOrigin.y = currentLocation.y - initialMoveLocation.y;
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;
356 [self setFrameOrigin:newOrigin];
361 -(BOOL)isInManualResize
363 return inManualResize;
366 -(void)beginManualMove
368 NSRect frame = [self frame];
370 if (inMove || inManualMove || inManualResize)
375 initialMoveLocation = [self convertBaseToScreen:[self mouseLocationOutsideOfEventStream]];
376 initialMoveLocation.x -= frame.origin.x;
377 initialMoveLocation.y -= frame.origin.y;
380 - (BOOL)trackManualResize
382 NSPoint currentLocation;
387 if (!inManualResize || inTrackManualResize)
390 inTrackManualResize = YES;
392 currentLocation = [self convertBaseToScreen:[self mouseLocationOutsideOfEventStream]];
393 currentLocation.x -= initialResizeFrame.origin.x;
394 currentLocation.y -= initialResizeFrame.origin.y;
396 dx = currentLocation.x - initialResizeLocation.x;
397 dy = -(currentLocation.y - initialResizeLocation.y);
399 newFrame = initialResizeFrame;
400 newFrame.size.width = initialResizeFrame.size.width + dx;
401 newFrame.size.height = initialResizeFrame.size.height + dy;
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;
409 /* We could also apply aspect ratio:
410 newFrame.size.height = newFrame.size.width / [self aspectRatio].width * [self aspectRatio].height;
413 dy = newFrame.size.height - initialResizeFrame.size.height;
415 newFrame.origin.x = initialResizeFrame.origin.x;
416 newFrame.origin.y = initialResizeFrame.origin.y - dy;
418 [self setFrame:newFrame display:YES];
420 /* Let the resizing be handled by GTK+. */
421 if (g_main_context_pending (NULL))
422 g_main_context_iteration (NULL, FALSE);
424 inTrackManualResize = NO;
429 -(void)beginManualResize
431 if (inMove || inManualMove || inManualResize)
434 inManualResize = YES;
436 initialResizeFrame = [self frame];
437 initialResizeLocation = [self convertBaseToScreen:[self mouseLocationOutsideOfEventStream]];
438 initialResizeLocation.x -= initialResizeFrame.origin.x;
439 initialResizeLocation.y -= initialResizeFrame.origin.y;
444 static GdkDragContext *current_context = NULL;
447 drag_operation_to_drag_action (NSDragOperation operation)
449 GdkDragAction result = 0;
451 /* GDK and Quartz drag operations do not map 1:1.
452 * This mapping represents about the best that we
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
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;
478 static NSDragOperation
479 drag_action_to_drag_operation (GdkDragAction action)
481 NSDragOperation result = 0;
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;
494 update_context_from_dragging_info (id <NSDraggingInfo> sender)
496 g_assert (current_context != NULL);
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;
503 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
505 GdkDeviceManager *device_manager;
510 g_object_unref (current_context);
512 current_context = g_object_new (GDK_TYPE_QUARTZ_DRAG_CONTEXT, NULL);
513 update_context_from_dragging_info (sender);
515 window = [[self contentView] gdkWindow];
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));
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;
527 gdk_event_set_device (event, gdk_drag_context_get_device (current_context));
529 _gdk_event_emit (event);
531 gdk_event_free (event);
533 return NSDragOperationNone;
536 - (void)draggingEnded:(id <NSDraggingInfo>)sender
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;
543 g_object_unref (current_context);
544 current_context = NULL;
547 - (void)draggingExited:(id <NSDraggingInfo>)sender
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;
557 gdk_event_set_device (event, gdk_drag_context_get_device (current_context));
559 _gdk_event_emit (event);
561 gdk_event_free (event);
563 g_object_unref (current_context);
564 current_context = NULL;
567 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
569 NSPoint point = [sender draggingLocation];
570 NSPoint screen_point = [self convertBaseToScreen:point];
574 update_context_from_dragging_info (sender);
575 _gdk_quartz_window_nspoint_to_gdk_xy (screen_point, &gx, &gy);
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;
585 gdk_event_set_device (event, gdk_drag_context_get_device (current_context));
587 _gdk_event_emit (event);
589 gdk_event_free (event);
591 return drag_action_to_drag_operation (current_context->action);
594 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
596 NSPoint point = [sender draggingLocation];
597 NSPoint screen_point = [self convertBaseToScreen:point];
601 update_context_from_dragging_info (sender);
602 _gdk_quartz_window_nspoint_to_gdk_xy (screen_point, &gx, &gy);
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;
612 gdk_event_set_device (event, gdk_drag_context_get_device (current_context));
614 _gdk_event_emit (event);
616 gdk_event_free (event);
618 g_object_unref (current_context);
619 current_context = NULL;
624 - (BOOL)wantsPeriodicDraggingUpdates
629 - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
633 g_assert (_gdk_quartz_drag_source_context != NULL);
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);
640 gdk_event_set_device (event,
641 gdk_drag_context_get_device (_gdk_quartz_drag_source_context));
643 _gdk_event_emit (event);
645 gdk_event_free (event);
647 g_object_unref (_gdk_quartz_drag_source_context);
648 _gdk_quartz_drag_source_context = NULL;