To: vim-dev@vim.org Subject: Patch 6.2.211 (extra) Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 8bit ------------ Patch 6.2.211 (extra) Problem: Code for handling file dropped on Vim is duplicated. Solution: Move the common code to gui_handle_drop(). Add code to drop the files in the window under the cursor. Support drag&drop on the Macintosh. (Taro Muraoka) When dropping a directory name edit that directory (using the explorer plugin) Fix that changing directory with Shift pressed didn't work for relative path names. Files: src/fileio.c, src/gui.c, src/gui_gtk_x11.c, src/gui_mac.c, src/gui_w48.c, src/proto/fileio.pro, src/proto/gui.pro *** ../vim-6.2.210/src/fileio.c Sun Jan 18 21:31:56 2004 --- src/fileio.c Wed Jan 21 14:49:33 2004 *************** *** 4834,4839 **** --- 4834,4840 ---- } /* + * Shorten filenames for all buffers. * When "force" is TRUE: Use full path from now on for files currently being * edited, both for file name and swap file name. Try to shorten the file * names a bit, if safe to do so. *************** *** 4879,4884 **** --- 4880,4919 ---- #endif } + #if (defined(FEAT_DND) && defined(FEAT_GUI_GTK)) \ + || defined(FEAT_GUI_MSWIN) \ + || defined(FEAT_GUI_MAC) \ + || defined(PROTO) + /* + * Shorten all filenames in "fnames[count]" by current directory. + */ + void + shorten_filenames(fnames, count) + char_u **fnames; + int count; + { + int i; + char_u dirname[MAXPATHL]; + char_u *p; + + if (fnames == NULL || count < 1) + return; + mch_dirname(dirname, sizeof(dirname)); + for (i = 0; i < count; ++i) + { + if ((p = shorten_fname(fnames[i], dirname)) != NULL) + { + /* shorten_fname() returns pointer in given "fnames[i]". If free + * "fnames[i]" first, "p" becomes invalid. So we need to copy + * "p" first then free fnames[i]. */ + p = vim_strsave(p); + vim_free(fnames[i]); + fnames[i] = p; + } + } + } + #endif + /* * add extention to file name - change path/fo.o.h to path/fo.o.h.ext or * fo_o_h.ext for MSDOS or when shortname option set. *** ../vim-6.2.210/src/gui.c Thu Jan 8 20:54:45 2004 --- src/gui.c Thu Jan 22 16:53:48 2004 *************** *** 4481,4483 **** --- 4481,4615 ---- } #endif + + #if (defined(FEAT_DND) && defined(FEAT_GUI_GTK)) \ + || defined(FEAT_GUI_MSWIN) \ + || defined(FEAT_GUI_MAC) \ + || defined(PROTO) + + #ifdef FEAT_WINDOWS + static void gui_wingoto_xy __ARGS((int x, int y)); + + /* + * Jump to the window at specified point (x, y). + */ + static void + gui_wingoto_xy(x, y) + int x; + int y; + { + int row = Y_2_ROW(y); + int col = X_2_COL(x); + win_T *wp; + + if (row >= 0 && col >= 0) + { + wp = mouse_find_win(&row, &col); + if (wp != NULL && wp != curwin) + win_goto(wp); + } + } + #endif + + /* + * Process file drop. Mouse cursor position, key modifiers, name of files + * and count of files are given. Argument "fnames[count]" has full pathnames + * of dropped files, they will be freed in this function, and caller can't use + * fnames after call this function. + */ + /*ARGSUSED*/ + void + gui_handle_drop(x, y, modifiers, fnames, count) + int x; + int y; + int_u modifiers; + char_u **fnames; + int count; + { + int i; + char_u *p; + + /* + * When the cursor is at the command line, add the file names to the + * command line, don't edit the files. + */ + if (State & CMDLINE) + { + shorten_filenames(fnames, count); + for (i = 0; i < count; ++i) + { + if (fnames[i] != NULL) + { + if (i > 0) + add_to_input_buf((char_u*)" ", 1); + + /* We don't know what command is used thus we can't be sure + * about which characters need to be escaped. Only escape the + * most common ones. */ + # ifdef BACKSLASH_IN_FILENAME + p = vim_strsave_escaped(fnames[i], (char_u *)" \t\"|"); + # else + p = vim_strsave_escaped(fnames[i], (char_u *)"\\ \t\"|"); + # endif + if (p != NULL) + add_to_input_buf(p, (int)STRLEN(p)); + vim_free(p); + vim_free(fnames[i]); + } + } + vim_free(fnames); + } + else + { + /* Go to the window under mouse cursor, then shorten given "fnames" by + * current window, because a window can have local current dir. */ + # ifdef FEAT_WINDOWS + gui_wingoto_xy(x, y); + # endif + shorten_filenames(fnames, count); + + /* If Shift held down, remember the first item. */ + if ((modifiers & MOUSE_SHIFT) != 0) + p = vim_strsave(fnames[0]); + else + p = NULL; + + /* Handle the drop, :edit or :split to get to the file. This also + * frees fnames[]. Skip this if there is only one item it's a + * directory and Shift is held down. */ + if (count == 1 && (modifiers & MOUSE_SHIFT) != 0 + && mch_isdir(fnames[0])) + { + vim_free(fnames[0]); + vim_free(fnames); + } + else + handle_drop(count, fnames, (modifiers & MOUSE_CTRL) != 0); + + /* If Shift held down, change to first file's directory. If the first + * item is a directory, change to that directory (and let the explorer + * plugin show the contents). */ + if (p != NULL) + { + if (mch_isdir(p)) + { + if (mch_chdir((char *)p) == 0) + shorten_fnames(TRUE); + } + else if (vim_chdirfile(p) == OK) + shorten_fnames(TRUE); + vim_free(p); + } + + /* Update the screen display */ + update_screen(NOT_VALID); + # ifdef FEAT_MENU + gui_update_menus(0); + # endif + setcursor(); + out_flush(); + gui_update_cursor(FALSE, FALSE); + gui_mch_flush(); + } + } + #endif *** ../vim-6.2.210/src/gui_gtk_x11.c Sun Jan 11 12:45:02 2004 --- src/gui_gtk_x11.c Wed Jan 21 16:07:00 2004 *************** *** 1823,1981 **** * Drag aNd Drop support handlers. */ ! static void ! drag_handle_uri_list(GdkDragContext *context, ! GtkSelectionData *data, ! guint time_, ! GdkModifierType state) { ! char_u **fnames; ! int redo_dirs = FALSE; ! int i; ! int n; ! char_u *start; ! char_u *copy; ! char_u *names; ! int nfiles = 0; ! int url = FALSE; ! names = data->data; ! copy = alloc((unsigned)(data->length + 1)); ! if (copy == NULL) ! return; ! /* ! * Count how many items there may be and separate them with a NUL. ! * Apparently the items are separated with \r\n. This is not documented, ! * thus be careful not to go past the end. Also allow separation with NUL ! * characters. ! */ ! start = copy; ! for (i = 0; i < data->length; ++i) { ! if (names[i] == NUL || names[i] == '\n' || names[i] == '\r') { ! if (start > copy && start[-1] != NUL) { ! ++nfiles; ! *start++ = NUL; } } ! else if (names[i] == '%' && i + 2 < data->length ! && hexhex2nr(names + i + 1) > 0) { ! *start++ = hexhex2nr(names + i + 1); i += 2; } else ! *start++ = names[i]; } ! if (start > copy && start[-1] != NUL) { ! *start = NUL; /* last item didn't have \r or \n */ ! ++nfiles; } ! fnames = (char_u **)alloc((unsigned)(nfiles * sizeof(char_u *))); ! if (fnames == NULL) ! { ! vim_free(copy); ! return; ! } ! url = FALSE; /* Set when a non-file URL was found. */ ! start = copy; ! for (n = 0; n < nfiles; ++n) { ! if (STRNCMP(start, "file://localhost", 16) == 0) ! start += 16; ! else { ! if (STRNCMP(start, "file:", 5) != 0) ! url = TRUE; ! else ! { ! start += 5; ! while (start[0] == '/' && start[1] == '/') ! ++start; ! } } ! fnames[n] = vim_strsave(start); ! start += STRLEN(start) + 1; } ! /* accept */ ! gtk_drag_finish(context, TRUE, FALSE, time_); ! /* Special handling when all items are real files. */ ! if (url == FALSE) ! { ! if (nfiles == 1) ! { ! if (fnames[0] != NULL && mch_isdir(fnames[0])) ! { ! /* Handle dropping a directory on Vim. */ ! if (mch_chdir((char *)fnames[0]) == 0) ! { ! vim_free(fnames[0]); ! fnames[0] = NULL; ! redo_dirs = TRUE; ! } ! } ! } ! else ! { ! /* Ignore any directories */ ! for (i = 0; i < nfiles; ++i) ! { ! if (fnames[i] != NULL && mch_isdir(fnames[i])) ! { ! vim_free(fnames[i]); ! fnames[i] = NULL; ! } ! } ! } ! if (state & GDK_SHIFT_MASK) ! { ! /* Shift held down, change to first file's directory */ ! if (fnames[0] != NULL && vim_chdirfile(fnames[0]) == OK) ! redo_dirs = TRUE; ! } ! else ! { ! char_u dirname[MAXPATHL]; ! char_u *s; ! /* Shorten dropped file names. */ ! if (mch_dirname(dirname, MAXPATHL) == OK) ! for (i = 0; i < nfiles; ++i) ! if (fnames[i] != NULL) ! { ! s = shorten_fname(fnames[i], dirname); ! if (s != NULL && (s = vim_strsave(s)) != NULL) ! { ! vim_free(fnames[i]); ! fnames[i] = s; ! } ! } ! } ! } ! vim_free(copy); ! /* Handle the drop, :edit or :split to get to the file */ ! handle_drop(nfiles, fnames, (state & GDK_CONTROL_MASK) != 0); ! if (redo_dirs) ! shorten_fnames(TRUE); ! /* Update the screen display */ ! update_screen(NOT_VALID); ! # ifdef FEAT_MENU ! gui_update_menus(0); ! # endif ! setcursor(); ! out_flush(); ! gui_update_cursor(FALSE, FALSE); ! gui_mch_flush(); } static void --- 1823,1940 ---- * Drag aNd Drop support handlers. */ ! /* ! * Count how many items there may be and separate them with a NUL. ! * Apparently the items are separated with \r\n. This is not documented, ! * thus be careful not to go past the end. Also allow separation with ! * NUL characters. ! */ ! static int ! count_and_decode_uri_list(char_u *out, char_u *raw, int len) { ! int i; ! char_u *p = out; ! int count = 0; ! for (i = 0; i < len; ++i) { ! if (raw[i] == NUL || raw[i] == '\n' || raw[i] == '\r') { ! if (p > out && p[-1] != NUL) { ! ++count; ! *p++ = NUL; } } ! else if (raw[i] == '%' && i + 2 < len && hexhex2nr(raw + i + 1) > 0) { ! *p++ = hexhex2nr(raw + i + 1); i += 2; } else ! *p++ = raw[i]; } ! if (p > out && p[-1] != NUL) { ! *p = NUL; /* last item didn't have \r or \n */ ! ++count; } + return count; + } ! /* ! * Parse NUL separated "src" strings. Make it an array "outlist" form. On ! * this process, URI which protocol is not "file:" are removed. Return ! * length of array (less than "max"). ! */ ! static int ! filter_uri_list(char_u **outlist, int max, char_u *src) ! { ! int i, j; ! ! for (i = j = 0; i < max; ++i) { ! outlist[i] = NULL; ! if (STRNCMP(src, "file:", 5) == 0) { ! src += 5; ! if (STRNCMP(src, "//localhost", 11) == 0) ! src += 11; ! while (src[0] == '/' && src[1] == '/') ! ++src; ! outlist[j++] = vim_strsave(src); } ! src += STRLEN(src) + 1; } + return j; + } ! static char_u ** ! parse_uri_list(int *count, char_u *data, int len) ! { ! int n = 0; ! char_u *tmp = NULL; ! char_u **array = NULL;; ! ! if (data != NULL && len > 0 && (tmp = (char_u *)alloc(len + 1)) != NULL) ! { ! n = count_and_decode_uri_list(tmp, data, len); ! if (n > 0 && (array = (char_u **)alloc(n * sizeof(char_u *))) != NULL) ! n = filter_uri_list(array, n, tmp); ! } ! vim_free(tmp); ! *count = n; ! return array; ! } ! static void ! drag_handle_uri_list(GdkDragContext *context, ! GtkSelectionData *data, ! guint time_, ! GdkModifierType state, ! gint x, ! gint y) ! { ! char_u **fnames; ! int nfiles = 0; ! fnames = parse_uri_list(&nfiles, data->data, data->length); ! if (fnames != NULL && nfiles > 0) ! { ! int_u modifiers = 0; ! gtk_drag_finish(context, TRUE, FALSE, time_); /* accept */ ! if (state & GDK_SHIFT_MASK) ! modifiers |= MOUSE_SHIFT; ! if (state & GDK_CONTROL_MASK) ! modifiers |= MOUSE_CTRL; ! if (state & GDK_MOD1_MASK) ! modifiers |= MOUSE_ALT; ! gui_handle_drop(x, y, modifiers, fnames, nfiles); ! } } static void *************** *** 2071,2077 **** /* Not sure about the role of "text/plain" here... */ if (info == (guint)TARGET_TEXT_URI_LIST) ! drag_handle_uri_list(context, data, time_, state); else drag_handle_text(context, data, time_, state); --- 2030,2036 ---- /* Not sure about the role of "text/plain" here... */ if (info == (guint)TARGET_TEXT_URI_LIST) ! drag_handle_uri_list(context, data, time_, state, x, y); else drag_handle_text(context, data, time_, state); *** ../vim-6.2.210/src/gui_mac.c Sun Jan 25 20:42:15 2004 --- src/gui_mac.c Thu Jan 22 10:44:31 2004 *************** *** 2653,2658 **** --- 2653,2712 ---- } #endif + static OSErr + receiveHandler(WindowRef theWindow, void* handlerRefCon, DragRef theDrag) + { + int x, y; + int_u modifiers; + char_u **fnames = NULL; + int count; + int i, j; + + /* Get drop position, modifiers and count of items */ + { + Point point; + SInt16 mouseUpModifiers; + UInt16 countItem; + + GetDragMouse(theDrag, &point, NULL); + GlobalToLocal(&point); + x = point.h; + y = point.v; + GetDragModifiers(theDrag, NULL, NULL, &mouseUpModifiers); + modifiers = EventModifiers2VimMouseModifiers(mouseUpModifiers); + CountDragItems(theDrag, &countItem); + count = countItem; + } + + fnames = (char_u **)alloc(count * sizeof(char_u *)); + if (fnames == NULL) + return dragNotAcceptedErr; + + /* Get file names dropped */ + for (i = j = 0; i < count; ++i) + { + DragItemRef item; + OSErr err; + Size size; + FlavorType type = flavorTypeHFS; + HFSFlavor hfsFlavor; + + fnames[i] = NULL; + GetDragItemReferenceNumber(theDrag, i + 1, &item); + err = GetFlavorDataSize(theDrag, item, type, &size); + if (err != noErr || size > sizeof(hfsFlavor)) + continue; + err = GetFlavorData(theDrag, item, type, &hfsFlavor, &size, 0); + if (err != noErr) + continue; + fnames[j++] = FullPathFromFSSpec_save(hfsFlavor.fileSpec); + } + count = j; + + gui_handle_drop(x, y, modifiers, fnames, count); + return noErr; + } + /* * Initialise the GUI. Create all the windows, set up all the call-backs * etc. *************** *** 2721,2726 **** --- 2775,2782 ---- gui.VimWindow = NewCWindow(nil, &windRect, "\pgVim on Macintosh", true, documentProc, (WindowPtr) -1L, false, 0); + InstallReceiveHandler((DragReceiveHandlerUPP)receiveHandler, + gui.VimWindow, NULL); #ifdef USE_CARBONIZED SetPortWindowPort ( gui.VimWindow ); #else *** ../vim-6.2.210/src/gui_w48.c Sun Jan 25 20:24:03 2004 --- src/gui_w48.c Wed Jan 21 16:11:49 2004 *************** *** 2841,2924 **** char szFile[BUFPATHLEN]; UINT cFiles = DragQueryFile(hDrop, DRAGQVAL, szFile, BUFPATHLEN); UINT i; - char_u *fname; char_u **fnames; char_u redo_dirs = FALSE; /* TRACE("_OnDropFiles: %d files dropped\n", cFiles); */ # ifdef FEAT_VISUAL reset_VIsual(); # endif fnames = (char_u **)alloc(cFiles * sizeof(char_u *)); ! for (i = 0; i < cFiles; ++i) ! { ! DragQueryFile(hDrop, i, szFile, BUFPATHLEN); ! ! mch_dirname(IObuff, IOSIZE); ! fname = shorten_fname(szFile, IObuff); ! if (fname == NULL) ! fname = szFile; ! fnames[i] = vim_strsave(fname); ! } ! DragFinish(hDrop); ! ! /* ! * When the cursor is at the command line, add the file names to the ! * command line, don't edit the files. ! */ ! if (State & CMDLINE) ! { for (i = 0; i < cFiles; ++i) { ! if (fnames[i] != NULL) ! { ! if (i > 0) ! add_to_input_buf(" ", 1); ! add_to_input_buf(fnames[i], (int)STRLEN(fnames[i])); ! } ! } ! } ! else ! { ! /* ! * Handle dropping a directory on Vim. ! * Change to that directory and don't open any file. ! */ ! if (cFiles == 1 && mch_isdir(fnames[0])) ! { ! if (mch_chdir(fnames[0]) == 0) ! { ! msg_str((char_u *)":cd %s", fnames[0]); ! vim_free(fnames[0]); ! fnames[0] = NULL; ! redo_dirs = TRUE; ! } } ! if (fnames[0] != NULL) ! { ! /* If Shift held down, change to first file's directory */ ! if (GetKeyState(VK_SHIFT) & 0x8000) ! if (vim_chdirfile(fnames[0]) == OK) ! redo_dirs = TRUE; ! ! /* Handle the drop, :edit or :split to get to the file */ ! handle_drop(cFiles, fnames, ! ((GetKeyState(VK_CONTROL) & 0x8000) != 0)); ! } ! if (redo_dirs) ! shorten_fnames(TRUE); ! /* Update the screen display */ ! update_screen(NOT_VALID); ! setcursor(); ! out_flush(); } - s_need_activate = TRUE; #endif } --- 2841,2885 ---- char szFile[BUFPATHLEN]; UINT cFiles = DragQueryFile(hDrop, DRAGQVAL, szFile, BUFPATHLEN); UINT i; char_u **fnames; char_u redo_dirs = FALSE; + POINT pt; + int_u modifiers = 0; /* TRACE("_OnDropFiles: %d files dropped\n", cFiles); */ + /* Obtain dropped position */ + DragQueryPoint(hDrop, &pt); + MapWindowPoints(s_hwnd, s_textArea, &pt, 1); + # ifdef FEAT_VISUAL reset_VIsual(); # endif fnames = (char_u **)alloc(cFiles * sizeof(char_u *)); ! if (fnames != NULL) for (i = 0; i < cFiles; ++i) { ! DragQueryFile(hDrop, i, szFile, BUFPATHLEN); ! fnames[i] = vim_strsave(szFile); } ! DragFinish(hDrop); ! ! if (fnames != NULL) ! { ! if ((GetKeyState(VK_SHIFT) & 0x8000) != 0) ! modifiers |= MOUSE_SHIFT; ! if ((GetKeyState(VK_CONTROL) & 0x8000) != 0) ! modifiers |= MOUSE_CTRL; ! if ((GetKeyState(VK_MENU) & 0x8000) != 0) ! modifiers |= MOUSE_ALT; ! gui_handle_drop(pt.x, pt.y, modifiers, fnames, cFiles); ! s_need_activate = TRUE; } #endif } *** ../vim-6.2.210/src/proto/fileio.pro Sun Jun 1 12:26:09 2003 --- src/proto/fileio.pro Wed Jan 21 15:32:29 2004 *************** *** 5,10 **** --- 5,11 ---- int buf_write __ARGS((buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_T end, exarg_T *eap, int append, int forceit, int reset_changed, int filtering)); char_u *shorten_fname __ARGS((char_u *full_path, char_u *dir_name)); void shorten_fnames __ARGS((int force)); + void shorten_filenames __ARGS((char_u **fnames, int count)); char_u *modname __ARGS((char_u *fname, char_u *ext, int prepend_dot)); char_u *buf_modname __ARGS((int shortname, char_u *fname, char_u *ext, int prepend_dot)); int vim_fgets __ARGS((char_u *buf, int size, FILE *fp)); *** ../vim-6.2.210/src/proto/gui.pro Sun Jun 1 12:26:24 2003 --- src/proto/gui.pro Wed Jan 21 15:32:24 2004 *************** *** 57,60 **** --- 57,61 ---- void gui_update_screen __ARGS((void)); char_u *get_find_dialog_text __ARGS((char_u *arg, int *wwordp, int *mcasep)); int gui_do_findrepl __ARGS((int flags, char_u *find_text, char_u *repl_text, int down)); + void gui_handle_drop __ARGS((int x, int y, int_u modifiers, char_u **fnames, int count)); /* vim: set ft=c : */ *** ../vim-6.2.210/src/version.c Sun Jan 25 20:42:15 2004 --- src/version.c Sun Jan 25 20:44:30 2004 *************** *** 639,640 **** --- 639,642 ---- { /* Add new patch number below this line */ + /**/ + 211, /**/ -- NEIL INNES PLAYED: THE FIRST SELF-DESTRUCTIVE MONK, ROBIN'S LEAST FAVORITE MINSTREL, THE PAGE CRUSHED BY A RABBIT, THE OWNER OF A DUCK "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// Sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ Project leader for A-A-P -- http://www.A-A-P.org /// \\\ Help AIDS victims, buy here: http://ICCF-Holland.org/click1.html ///