📄 dnd.c
字号:
/* dnd.c - drag and drop support Copyright (C) 1996-2000 Paul Sheer This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. *//* The cursor bitmaps and the cursor initialisation routines are from the DND package which can be found with the OffiX package at sunsite.unc.edu. These are Copyright (C) 1996 C閟ar Crusius. I also took a look at his code to get a better idea how get my Dnd working the same, and small parts of the code are modifications from there. */#include <config.h>#ifdef HAVE_DND#include <my_string.h>#include <stdlib.h>#include <stdio.h>#include "pool.h"#include <sys/types.h>#include <sys/stat.h>#ifdef HAVE_UNISTD_H#include <unistd.h>#endif#include <X11/Xlib.h>#include <X11/Xutil.h>#include <X11/Xresource.h>#include <X11/Xatom.h>#include <X11/cursorfont.h>#include "cursor/file.xbm"#include "cursor/file_mask.xbm"#include "cursor/files.xbm"#include "cursor/files_mask.xbm"#include "cursor/dir.xbm"#include "cursor/dir_mask.xbm"#include "cursor/text.xbm"#include "cursor/text_mask.xbm"#include "cursor/grey.xbm"#include "cursor/grey_mask.xbm"#include "cursor/link.xbm"#include "cursor/link_mask.xbm"#include "cursor/app.xbm"#include "cursor/app_mask.xbm"#include "cursor/url.xbm"#include "cursor/url_mask.xbm"#include "cursor/mime.xbm"#include "cursor/mime_mask.xbm"#include "app_glob.c"#include "coolwidget.h"#include "mad.h"/* #define DND_DEBUG *//* what dnd version for sending drops *//* (recieving drops we support both versions simultaneously) */int option_dnd_version = 1;Atom DndProtocol, DndSelection, DndAcknowledge;Atom OldDndProtocol, OldDndSelection;typedef struct { int Width, Height; unsigned char *ImageData, *MaskData; int HotSpotX, HotSpotY; Pixmap ImagePixmap, MaskPixmap; Cursor CursorID;} CursorData;static CursorData DndCursor[] ={ {0, 0, NULL, NULL, 0, 0, 0}, {grey_width, grey_height, grey_bits, grey_mask_bits, grey_x_hot, grey_y_hot}, {file_width, file_height, file_bits, file_mask_bits, file_x_hot, file_y_hot}, {files_width, files_height, files_bits, files_mask_bits, files_x_hot, files_y_hot}, {text_width, text_height, text_bits, text_mask_bits, text_x_hot, text_y_hot}, {dir_width, dir_height, dir_bits, dir_mask_bits, dir_x_hot, dir_y_hot}, {link_width, link_height, link_bits, link_mask_bits, link_x_hot, link_y_hot}, {app_width, app_height, app_bits, app_mask_bits, app_x_hot, app_y_hot}, {url_width, url_height, url_bits, url_mask_bits, url_x_hot, url_y_hot}, {mime_width, mime_height, mime_bits, mime_mask_bits, mime_x_hot, mime_y_hot}};static int num_cursors = sizeof (DndCursor) / sizeof (CursorData);static char dnd_directory[MAX_PATH_LEN] = "";/* return the prepending directory (see CDndFileList() below) */char *CDndDirectory (void){ return dnd_directory;}char *striptrailing (char *s, int c){ int i; i = strlen (s) - 1; while (i >= 0) { if (s[i] == c) { s[i--] = 0; continue; } break; } return s;}/* Sets the directory, must be a null terminated complete path. Strips trailing slashes. */void CSetDndDirectory (char *d){ if (!d) return; strcpy (dnd_directory, d); striptrailing (dnd_directory, '/'); if (*dnd_directory) return; *dnd_directory = '/';}/* Takes a newline separate list of files, returns a null separate list of complete path names by prepending dnd_directory to each file name. returns 0 if no files in list. result must always be free'd. returns l as the total length of data. returns num_files as the number of files in the list. Alters t */char *CDndFileList (char *t, int *l, int *num_files){ char *p, *q, *r, *result; int i; int path_len, len;/* strip leading newlines */ while (*t == '\n') t++;/* strip trailing newlines */ striptrailing (t, '\n'); if (!*t) return 0;/* count files */ for (i = 1, p = t; *p; p++) if (*p == '\n') i++; *num_files = i; len = (unsigned long) p - (unsigned long) t; path_len = strlen (dnd_directory); result = CMalloc ((path_len + 2) * i + len + 2); r = result; p = t; for (;;) { q = strchr (p, '\n'); if (!q) q = t + len; *q = 0; strcpy (r, dnd_directory); r += path_len; *r++ = '/'; strcpy (r, p); r += (unsigned long) q - (unsigned long) p; *r++ = 0; if ((unsigned long) q == (unsigned long) t + len) break; p = ++q; } *r = 0; *l = (unsigned long) r - (unsigned long) result; return result;}/* Dnd initialisation */void initialise_drag_n_drop (void){ int screen, i; Colormap colormap; Window root; XColor Black, White; DndAcknowledge = XInternAtom (CDisplay, "_DND_ACKNOWLEDGE", False); DndProtocol = XInternAtom (CDisplay, "_DND_PROTOCOL", False); DndSelection = XInternAtom (CDisplay, "_DND_SELECTION", False); OldDndProtocol = XInternAtom (CDisplay, "DndProtocol", False); OldDndSelection = XInternAtom (CDisplay, "DndSelection", False); screen = DefaultScreen (CDisplay); colormap = DefaultColormap (CDisplay, screen); root = CRoot; Black.pixel = BlackPixel (CDisplay, screen); White.pixel = WhitePixel (CDisplay, screen); XQueryColor (CDisplay, colormap, &Black); XQueryColor (CDisplay, colormap, &White); for (i = 1; i < num_cursors; i++) { DndCursor[i].ImagePixmap = XCreateBitmapFromData (CDisplay, root, (char *) DndCursor[i].ImageData, DndCursor[i].Width, DndCursor[i].Height); DndCursor[i].MaskPixmap = XCreateBitmapFromData (CDisplay, root, (char *) DndCursor[i].MaskData, DndCursor[i].Width, DndCursor[i].Height); DndCursor[i].CursorID = XCreatePixmapCursor (CDisplay, DndCursor[i].ImagePixmap, DndCursor[i].MaskPixmap, &Black, &White, DndCursor[i].HotSpotX, DndCursor[i].HotSpotY); } DndCursor[0].CursorID = XCreateFontCursor (CDisplay, XC_question_arrow);}static void change_file_to_directory (int *data_type, char *data){ struct stat stats; if (*data_type != DndFile) return; if (lstat (data, &stats)) return; if (S_ISDIR (stats.st_mode)) *data_type = DndDir;}/* This does a drag. This is copied somewhat from the dnd Xt code by C閟ar Crusius so it should behave almost exactly the way his does. From is the sending window. data_type is a dnd type in coolwidget.h, data is the actual data, and length is its length. pointer state is the state of the pointer. I set the pointer state to that *during* the drag, not before. See eh_editor in editwidget.c for details. The routines doesn't do anything until the mouse has moved more than five pixels. If the user releases the button before dragging, the button release event is sent back to the calling window. */void CDrag (Window from, int data_type, unsigned char *data, int length, unsigned long pointer_state){ XEvent e; long x, y; float x_mouse, y_mouse; Window root; Window target, dispatch; change_file_to_directory (&data_type, (char *) data);#ifdef DND_DEBUG/* NLS ? */ printf ("Drag start\n");#endif/* first wait until the mouse moves more than ten pixels */ do { XNextEvent (CDisplay, &e); if (e.type == ButtonRelease) { XSendEvent (CDisplay, e.xany.window, 0, ButtonReleaseMask, &e); return; } } while (e.type != MotionNotify); x_mouse = (float) e.xmotion.x_root; y_mouse = (float) e.xmotion.y_root; for (;;) { XNextEvent (CDisplay, &e); if (e.type == MotionNotify) if (my_sqrt ((x_mouse - e.xmotion.x_root) * (x_mouse - e.xmotion.x_root) + (y_mouse - e.xmotion.y_root) * (y_mouse - e.xmotion.y_root)) > 5.0) break; if (e.type == ButtonRelease) { XSendEvent (CDisplay, e.xany.window, 0, ButtonReleaseMask, &e); return; } }/* the mouse has been dragged a little, so this is a drag proper */ root = CRoot; XChangeProperty (CDisplay, root, option_dnd_version ? DndSelection : OldDndSelection, XA_STRING, 8, PropModeReplace, data, length); XGrabPointer (CDisplay, root, False, ButtonMotionMask | ButtonPressMask | ButtonReleaseMask, GrabModeSync, GrabModeAsync, root, DndCursor[data_type].CursorID, CurrentTime); do { XAllowEvents (CDisplay, SyncPointer, CurrentTime); XNextEvent (CDisplay, &e); } while (e.type != ButtonRelease); XUngrabPointer (CDisplay, CurrentTime); if (!e.xbutton.subwindow) {#ifdef DND_DEBUG/* NLS ? */ printf ("Pointer on root window, Drag end\n");#endif return; } target = my_XmuClientWindow (CDisplay, e.xbutton.subwindow); if (target == e.xbutton.subwindow) dispatch = target; else dispatch = PointerWindow; x = e.xbutton.x_root; y = e.xbutton.y_root; e.xclient.type = ClientMessage; e.xclient.display = CDisplay; e.xclient.message_type = option_dnd_version ? DndProtocol : OldDndProtocol; e.xclient.format = 32; e.xclient.window = target; e.xclient.data.l[0] = data_type; e.xclient.data.l[1] = pointer_state; e.xclient.data.l[2] = from; e.xclient.data.l[3] = x + y * 65536L; e.xclient.data.l[4] = option_dnd_version; /* Send the drop message */ XSendEvent (CDisplay, dispatch, True, NoEventMask, &e);#ifdef DND_DEBUG/* NLS ? */ printf ("Drop send to window %ld from %ld, Drag end\n", target, from);#endif}void this_pool_insert (void *p, int c){ unsigned char ch; ch = c; pool_write ((POOL *) p, &ch, 1);}void paste_prop (void *data, void (*insert) (void *, int), Window win, unsigned prop, int delete);/* Returns data_type on success, DndNotDnd if not a Dnd drop. Unneeded values can be passed as null. */int CGetDrop (XEvent * xe, unsigned char **data, unsigned long *size, int *x, int *y){ POOL *p; if (xe->type != ClientMessage) {#ifdef DND_DEBUG/* NLS ? */ printf ("None ClientMessage event to window %ld\n", xe->xany.window);#endif return DndNotDnd; }#ifdef DND_DEBUG/* NLS ? */ printf ("ClientMessage event to window %ld\n", xe->xany.window);#endif if ((xe->xclient.message_type == DndProtocol && xe->xclient.data.l[4] == 1) || (xe->xclient.message_type == OldDndProtocol && xe->xclient.data.l[4] == 0)) { /* continue */ } else { return DndNotDnd; }#ifdef DND_DEBUG/* NLS ? */ printf ("Drop recieved at window %ld from window %ld\n", xe->xclient.window, xe->xclient.data.l[2]);#endif p = pool_init (); paste_prop (p, this_pool_insert, CRoot, xe->xclient.data.l[4] ? DndSelection : OldDndSelection, False); if (size) *size = pool_length (p); *data = pool_break (p); if (x) *x = xe->xclient.data.l[3] & 0xFFFF; if (y) *y = xe->xclient.data.l[3] >> 16; return xe->xclient.data.l[0];}/* Sends an acknowledge event that the drop was recieved. This is my extension to the protocol, but cooledit doesn't depend on an acknowledge event being recieved, so everything will work as usual when communicating with apps that don't support this. xe is the drop event that you recived and is not be modified. */void CDropAcknowledge (XEvent * xe){ XEvent e; e.xclient.type = ClientMessage; e.xclient.display = CDisplay; e.xclient.message_type = DndAcknowledge; e.xclient.format = 32; e.xclient.window = xe->xclient.data.l[2]; /* to window that send the drop */ e.xclient.data.l[0] = xe->xclient.data.l[0]; /* data type */ e.xclient.data.l[1] = xe->xclient.data.l[1]; /* pointer state of the pointer when the drop was send */ e.xclient.data.l[2] = xe->xclient.window; /* from this window */ e.xclient.data.l[3] = 0; /* not used */ e.xclient.data.l[4] = 1; /* same dnd version, since this extension can be ignored, there is no need to have a new version */ XSendEvent (CDisplay, e.xclient.window, True, NoEventMask, &e);#ifdef DND_DEBUG/* NLS ? */ printf ("acknowledge send to window %ld from %ld, return\n", xe->xclient.data.l[2], xe->xclient.window);#endif}/* Checks if xe is an acknowledge event. Returns data_type if so and the state that the pointer was in when the drag was initiated. Returns DndNotDnd if not an acknowledge event. */int CIsDropAcknowledge (XEvent * xe, unsigned int *state){ if (xe->type != ClientMessage) return DndNotDnd; if (xe->xclient.message_type != DndAcknowledge || xe->xclient.data.l[4] != 1) return DndNotDnd; if (state) *state = xe->xclient.data.l[1]; return xe->xclient.data.l[0];}#endif /* HAVE_DND */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -