/* XGDragView - Drag and Drop code for X11 backends. Copyright (C) 1998,1999,2001 Free Software Foundation, Inc. Created by: Wim Oudshoorn Date: Nov 2001 Written by: Adam Fedor Date: Nov 1998 This file is part of the GNU Objective C User Interface Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include "x11/XGServer.h" #include "x11/XGServerWindow.h" #include "x11/XGDragView.h" /* Size of the dragged window */ #define DWZ 48 #define XDPY [XGServer currentXDisplay] #define SLIDE_TIME_STEP .02 /* in seconds */ #define SLIDE_NR_OF_STEPS 20 #define dragWindev [XGServer _windowWithTag: [_window windowNumber]] #define XX(P) (P.x) #define XY(P) (DisplayHeight(XDPY, dragWindev->screen) - P.y) @interface XGRawWindow : NSWindow @end @interface NSCursor (BackendPrivate) - (void *)_cid; - (void) _setCid: (void *)val; @end // --- DRAG AND DROP SUPPORT (XDND) ----------------------------------- /* * This will get initialized when we declare a window that will * accept dragging or if we start a dragging ourself. Up to than * even the dragging messages are not defined. */ static DndClass dnd; static BOOL xDndInitialized = NO; void GSEnsureDndIsInitialized (void) { if (xDndInitialized == NO) { xDndInitialized = YES; xdnd_init (&dnd, XDPY); } } DndClass xdnd (void) { return dnd; // FIX ME rename with private desig } Atom GSActionForDragOperation(unsigned int op) { Atom xaction; if (op == NSDragOperationAll) xaction = dnd.XdndActionAsk; else if (op & NSDragOperationCopy) xaction = dnd.XdndActionCopy; else if (op & NSDragOperationLink) xaction = dnd.XdndActionLink; else if (op & NSDragOperationGeneric) xaction = dnd.XdndActionCopy; else if (op & NSDragOperationPrivate) xaction = dnd.XdndActionPrivate; else if (op & NSDragOperationMove) xaction = dnd.XdndActionMove; else xaction = None; return xaction; } NSDragOperation GSDragOperationForAction(Atom xaction) { NSDragOperation action; if (xaction == dnd.XdndActionCopy) action = NSDragOperationCopy; else if (xaction == dnd.XdndActionMove) action = NSDragOperationMove; else if (xaction == dnd.XdndActionLink) action = NSDragOperationLink; else if (xaction == dnd.XdndActionAsk) action = NSDragOperationAll; else if (xaction == dnd.XdndActionPrivate) action = NSDragOperationPrivate; else action = NSDragOperationNone; return action; } // The result of this function must be freed by the caller static inline Atom * mimeTypeForPasteboardType(Display *xDisplay, NSZone *zone, NSArray *types) { Atom *typelist; int count = [types count]; int i; typelist = NSZoneMalloc(zone, (count+1) * sizeof(Atom)); for (i = 0; i < count; i++) { NSString *mime; mime = [types objectAtIndex: i]; mime = [NSPasteboard mimeTypeForPasteboardType: mime]; typelist[i] = XInternAtom(xDisplay, [mime cString], False); } typelist[count] = 0; return typelist; } @implementation XGDragView static XGDragView *sharedDragView = nil; + (id) sharedDragView { if (sharedDragView == nil) { GSEnsureDndIsInitialized (); sharedDragView = [XGDragView new]; } return sharedDragView; } + (Class) windowClass { return [XGRawWindow class]; } /* * External drag operation */ - (void) setupDragInfoFromXEvent: (XEvent *)xEvent { // Start a dragging session from another application dragSource = nil; destExternal = YES; operationMask = NSDragOperationAll; ASSIGN(dragPasteboard, [NSPasteboard pasteboardWithName: NSDragPboard]); } - (void) updateDragInfoFromEvent: (NSEvent*)event { // Store the drag info, so that we can send status messages as response destWindow = [event window]; dragPoint = [event locationInWindow]; dragSequence = [event timestamp]; dragMask = [event data2]; } - (void) resetDragInfo { DESTROY(dragPasteboard); } /* * Local drag operation */ - (void) dragImage: (NSImage*)anImage at: (NSPoint)screenLocation offset: (NSSize)initialOffset event: (NSEvent*)event pasteboard: (NSPasteboard*)pboard source: (id)sourceObject slideBack: (BOOL)slideFlag { typelist = mimeTypeForPasteboardType (XDPY, [self zone], [pboard types]); [super dragImage: anImage at: screenLocation offset: initialOffset event: event pasteboard: pboard source: sourceObject slideBack: slideFlag]; NSZoneFree([self zone], typelist); typelist = NULL; } - (void) postDragEvent: (NSEvent *)theEvent { if (destExternal) { gswindow_device_t *window; window = [XGServer _windowWithTag: [theEvent windowNumber]]; if ([theEvent subtype] == GSAppKitDraggingStatus) { NSDragOperation action = [theEvent data2]; Atom xaction; xaction = GSActionForDragOperation(action); xdnd_send_status(&dnd, [theEvent data1], window->ident, (action != NSDragOperationNone), 0, 0, 0, 0, 0, xaction); } else if ([theEvent subtype] == GSAppKitDraggingFinished) { xdnd_send_finished(&dnd, [theEvent data1], window->ident, 0); } } else { [super postDragEvent: theEvent]; } } - (void) sendExternalEvent: (GSAppKitSubtype)subtype action: (NSDragOperation)action position: (NSPoint)eventLocation timestamp: (NSTimeInterval)time toWindow: (int)dWindowNumber { switch (subtype) { case GSAppKitDraggingDrop: if (targetWindowRef == dragWindev->root) { // FIXME There is an xdnd extension for root drop } xdnd_send_drop(&dnd, dWindowNumber, dragWindev->ident, CurrentTime); break; case GSAppKitDraggingUpdate: xdnd_send_position(&dnd, dWindowNumber, dragWindev->ident, GSActionForDragOperation(dragMask & operationMask), XX(newPosition), XY(newPosition), CurrentTime); break; case GSAppKitDraggingEnter: // FIXME: The first two lines need only be called once for every drag operation. // They should be moved to a different method. xdnd_set_selection_owner(&dnd, dragWindev->ident, typelist[0]); xdnd_set_type_list(&dnd, dragWindev->ident, typelist); xdnd_send_enter(&dnd, dWindowNumber, dragWindev->ident, typelist); xdnd_send_position(&dnd, dWindowNumber, dragWindev->ident, GSActionForDragOperation (dragMask & operationMask), XX(dragPosition), XY(dragPosition), CurrentTime); break; case GSAppKitDraggingExit: xdnd_send_leave(&dnd, dWindowNumber, dragWindev->ident); break; default: break; } } - (NSWindow*) windowAcceptingDnDunder: (NSPoint)p windowRef: (int*)mouseWindowRef { gswindow_device_t *dwindev; *mouseWindowRef = [self _xWindowAcceptingDnDunderX: XX(p) Y: XY(p)]; dwindev = [XGServer _windowForXWindow: *mouseWindowRef]; if (dwindev != 0) { return GSWindowWithNumber(dwindev->number); } else { return nil; } } /* Search all descendents of parent and return X window containing screen coordinates (x,y) that accepts drag and drop. -1 if we can only find the X window that we are dragging None if there is no X window that accepts drag and drop. */ - (Window) _xWindowAcceptingDnDDescendentOf: (Window) parent underX: (int) x Y: (int) y { Window *children; unsigned int nchildren; Window result = None; Window ignore, child2, root; Display *display = XDPY; XWindowAttributes attr; int ret_x, ret_y; if (parent == dragWindev->ident) return -1; XQueryTree(display, parent, &root, &ignore, &children, &nchildren); while (nchildren-- > 0) { Window child = children [nchildren]; if (XGetWindowAttributes (display, child, &attr) && attr.map_state == IsViewable && XTranslateCoordinates (display, root, child, x, y, &ret_x, &ret_y, &child2) && ret_x >= 0 && ret_x < attr.width && ret_y >= 0 && ret_y < attr.height) { result = [self _xWindowAcceptingDnDDescendentOf: child underX: x Y: y]; if (result != (Window)-1) break; } } if (children) { XFree (children); } if (result == (Window) None) { if (xdnd_is_dnd_aware (&dnd, parent, &dnd.dragging_version, typelist)) { result = parent; } } return result; } /* Return window under the mouse that accepts drag and drop */ - (Window) _xWindowAcceptingDnDunderX: (int) x Y: (int) y { Window result; result = [self _xWindowAcceptingDnDDescendentOf: dragWindev->root underX: x Y: y]; if (result == (Window)-1) return None; else return result; } @end @interface XGServer (DragAndDrop) - (void) _resetDragTypesForWindow: (NSWindow *)win; @end @implementation XGServer (DragAndDrop) - (void) _resetDragTypesForWindow: (NSWindow *)win { int winNum; Atom *typelist; gswindow_device_t *window; NSCountedSet *drag_set = [self dragTypesForWindow: win]; winNum = [win windowNumber]; window = [isa _windowWithTag: winNum]; GSEnsureDndIsInitialized (); typelist = mimeTypeForPasteboardType(XDPY, [self zone], [drag_set allObjects]); NSDebugLLog(@"NSDragging", @"Set types on %x to %@", window->ident, drag_set); xdnd_set_dnd_aware(&dnd, window->ident, typelist); NSZoneFree([self zone], typelist); } - (BOOL) addDragTypes: (NSArray*)types toWindow: (NSWindow *)win { BOOL did_add; int winNum; did_add = [super addDragTypes: types toWindow: win]; /* Check if window device exists */ winNum = [win windowNumber]; if (winNum > 0 && did_add == YES) { [self _resetDragTypesForWindow: win]; } return did_add; } - (BOOL) removeDragTypes: (NSArray*)types fromWindow: (NSWindow *)win { BOOL did_change; int winNum; did_change = [super removeDragTypes: types fromWindow: win]; /* Check if window device exists. */ winNum = [win windowNumber]; if (winNum > 0 && did_change == YES) { [self _resetDragTypesForWindow: win]; } return did_change; } @end @implementation XGRawWindow - (BOOL) canBecomeMainWindow { return NO; } - (BOOL) canBecomeKeyWindow { return NO; } - (void) _initDefaults { [super _initDefaults]; [self setReleasedWhenClosed: NO]; [self setExcludedFromWindowsMenu: YES]; } - (void) orderWindow: (NSWindowOrderingMode)place relativeTo: (int)otherWin { XSetWindowAttributes winattrs; unsigned long valuemask; gswindow_device_t *window; [super orderWindow: place relativeTo: otherWin]; window = [XGServer _windowWithTag: _windowNum]; valuemask = (CWSaveUnder|CWOverrideRedirect); winattrs.save_under = True; /* Temporarily make this False? we don't handle it correctly (fedor) */ winattrs.override_redirect = False; XChangeWindowAttributes (XDPY, window->ident, valuemask, &winattrs); [self setLevel: NSPopUpMenuWindowLevel]; } @end