Here is an experimental patch for a new feature for gvim on MS Windows. I'm glad to hear any feedback about the code or the feature itself. I've tried to follow the conventions in the vim source code. I call the feature "ClipboardWatch".

The new feature triggers an event "ClipboardChanged" whenever the clipboard is changed (in any application). The Windows editor "NoteTab" implements this (as "pasteboard") and it is, at times, very handy.

The feature is activated by ":set clipboardwatch" or ":set cbw".

To my .vimrc, I've added
 if has("clipboardwatch")
   au! ClipboardChanged * exec "normal \"*gp$"
 endif
so that anything copied to the clipboard is pasted into the current buffer.


Notes--
 At http://kenhirsch.net/vim
 I have a downloadable zip file that includes these files:
   gvim.exe
   doc/autocmd.txt
   doc/eval.txt
   doc/map.txt
   doc/options.txt
   doc/tags
   doc/various.txt
 If you unzip directly into your Vim folder, usually
    "C:\Program Files\Vim\vim70",
you should be good to go. Some zip programs may not honor the full path, so you would have to copy the txt files to the vim70\doc directory.

 (This gvim.exe was compiled with
    nmake -f Make_mvc.mak GUI=yes DEFINES=-DFEAT_CBWATCH
  using Visual C++ Express 2005)


I implemented the function 'ui_clipboard_changed()' in ui.c by copying what seemed relevant from 'ui_focus_change()'. Not knowing much about vim internals, it may need fixing, but I haven't seen any problems yet.


I could implement a direct paste in addition to (or instead of) the event. This might be more convenient in most cases, if less flexible.


 I am using
   au! ClipboardChanged * exec "normal \"*gp$"

I added the $ at the end because, when the cursor is at the end of the file and you "*gp with complete lines, the cursor ends up at the beginning of the line. This might be considered a bug with "normal gp".


For X-Windows and MacOS, there doesn't seem to be any way for an application to get informed immediately (synchronously) of clipboard changes. X-windows seems to only notify the previous clipboard owner. I didn't see anything in the Mac API documentation that seemed right, but I'm not at all familiar with Mac programming, so I might have missed something. However, there's an open-source Mac utility called Jumpcut (http://jumpcut.sourceforge.net/) which attempts to provide similar functionality for Mac applications. It looks like it polls the clipboard once a second to see if it's changed. This leads to two conclusions:
   1. Polling the clipboard is the only way to do it on the Mac.
   2. Hey, that works! It could be done on X-Windows, too.

 The patch is relative to vim-7.0.220

 These files are patched:
   runtime/doc/autocmd.txt
   runtime/doc/eval.txt
   runtime/doc/map.txt
   runtime/doc/options.txt
   runtime/doc/tags
   runtime/doc/various.txt
   src/eval.c
   src/fileio.c
   src/gui_w32.c
   src/gui_w48.c
   src/option.c
   src/option.h
   src/os_mswin.c
   src/proto/gui_w32.pro
   src/proto/ui.pro
   src/ui.c
   src/vim.h



Ken Hirsch
Chapel Hill, NC
USA

--- vim-current/runtime/doc/autocmd.txt 2007-03-12 16:07:18.264790400 -0400
+++ vim-changed/runtime/doc/autocmd.txt 2007-03-22 22:25:59.000000000 -0400
@@ -312,6 +312,7 @@
 |MenuPopup|            just before showing the popup menu
 
 |User|                 to be used in combination with ":doautocmd"
+|ClipboardChanged|     system clipboard changed.
 
 
 The alphabetical list of autocommand events:           *autocmd-events-abc*
@@ -434,6 +435,11 @@
                                                        *BufWritePost*
 BufWritePost                   After writing the whole buffer to a file
                                (should undo the commands for BufWritePre).
+
+                                                       *ClipboardChanged*
+ClipboardChanged               When sytem clipboard changes (if
+                               *'ClipboardWatch'* is set)
+
                                                        *CmdwinEnter*
 CmdwinEnter                    After entering the command-line window.
                                Useful for setting options specifically for
--- vim-current/runtime/doc/eval.txt    2007-03-12 16:07:19.766950400 -0400
+++ vim-changed/runtime/doc/eval.txt    2007-03-22 21:05:15.000000000 -0400
@@ -5156,6 +5156,7 @@
 cindent                        Compiled with 'cindent' support.
 clientserver           Compiled with remote invocation support |clientserver|.
 clipboard              Compiled with 'clipboard' support.
+clipboardwatch         Compiled with 'clipboardwatch' support.
 cmdline_compl          Compiled with |cmdline-completion| support.
 cmdline_hist           Compiled with |cmdline-history| support.
 cmdline_info           Compiled with 'showcmd' and 'ruler' support.
--- vim-current/runtime/doc/options.txt 2007-03-12 16:06:58.706667200 -0400
+++ vim-changed/runtime/doc/options.txt 2007-03-26 11:04:31.176324800 -0400
@@ -1436,6 +1436,23 @@
                        The rest of the option value will be used for
                        {pattern}, this must be the last entry.
 
+                       *'clipboardwatch'* *'cbw'* *'noclipboardwatch'*
+                       *'nocbw'*
+'clipboardwatch' 'cbw' boolean (default off)
+                       global
+                       {not in Vi}
+                       {only available when compiled with the |+clipboardwatch|
+                       feature; currently only on MS-Windows}
+
+       When on, any change of the system clipboard triggers the
+       |ClipboardChanged| event. For example, this code in your
+       |vimrc| file:
+               if has("clipboardwatch")
+                 au! ClipboardChanged * exec "normal \"*gp$"
+               endif
+       will cause every clipboard to be pasted into the current
+       buffer when 'cbw' is set.
+
                                                *'cmdheight'* *'ch'*
 'cmdheight' 'ch'       number  (default 1)
                        global
--- vim-current/runtime/doc/tags        2006-05-07 09:33:26.000000000 -0400
+++ vim-changed/runtime/doc/tags        2007-03-23 00:19:41.000000000 -0400
@@ -89,6 +89,7 @@
 'buftype'      options.txt     /*'buftype'*
 'casemap'      options.txt     /*'casemap'*
 'cb'   options.txt     /*'cb'*
+'cbw'  options.txt     /*'cbw'*
 'ccv'  options.txt     /*'ccv'*
 'cd'   options.txt     /*'cd'*
 'cdpath'       options.txt     /*'cdpath'*
@@ -108,6 +109,7 @@
 'cinw' options.txt     /*'cinw'*
 'cinwords'     options.txt     /*'cinwords'*
 'clipboard'    options.txt     /*'clipboard'*
+'clipboardwatch'       options.txt     /*'clipboardwatch'*
 'cmdheight'    options.txt     /*'cmdheight'*
 'cmdwinheight' options.txt     /*'cmdwinheight'*
 'cmp'  options.txt     /*'cmp'*
@@ -1067,6 +1069,7 @@
 +cindent       various.txt     /*+cindent*
 +clientserver  various.txt     /*+clientserver*
 +clipboard     various.txt     /*+clipboard*
++clipboardwatch        various.txt     /*+clipboardwatch*
 +cmd   editing.txt     /*+cmd*
 +cmdline_compl various.txt     /*+cmdline_compl*
 +cmdline_hist  various.txt     /*+cmdline_hist*
@@ -4700,6 +4703,7 @@
 client-server  remote.txt      /*client-server*
 clientserver   remote.txt      /*clientserver*
 clipboard      gui.txt /*clipboard*
+ClipboardChanged       autocmd.txt     /*ClipboardChanged*
 cmdarg-variable        eval.txt        /*cmdarg-variable*
 cmdbang-variable       eval.txt        /*cmdbang-variable*
 cmdline-arguments      vi_diff.txt     /*cmdline-arguments*
--- vim-current/runtime/doc/various.txt 2006-05-07 08:16:46.000000000 -0400
+++ vim-changed/runtime/doc/various.txt 2007-03-22 21:36:50.000000000 -0400
@@ -282,6 +282,7 @@
 N  *+cindent*          |'cindent'|, C indenting
 N  *+clientserver*     Unix and Win32: Remote invocation |clientserver|
    *+clipboard*                |clipboard| support
+   *+clipboardwatch*   |clipboardwatch| support
 N  *+cmdline_compl*    command line completion |cmdline-completion|
 N  *+cmdline_hist*     command line history |cmdline-history|
 N  *+cmdline_info*     |'showcmd'| and |'ruler'|
--- vim-current/src/eval.c      2007-03-26 15:31:34.957390400 -0400
+++ vim-changed/src/eval.c      2007-03-26 15:41:42.080390400 -0400
@@ -10691,6 +10691,10 @@
 #ifdef FEAT_CLIPBOARD
        "clipboard",
 #endif
+#ifdef FEAT_CBWATCH
+       "cbw",
+       "clipboardwatch",
+#endif
 #ifdef FEAT_CMDL_COMPL
        "cmdline_compl",
 #endif
--- vim-current/src/fileio.c    2007-03-23 15:22:27.669132800 -0400
+++ vim-changed/src/fileio.c    2007-03-23 15:23:51.000000000 -0400
@@ -6977,6 +6977,7 @@
     {"BufWritePost",   EVENT_BUFWRITEPOST},
     {"BufWritePre",    EVENT_BUFWRITEPRE},
     {"BufWriteCmd",    EVENT_BUFWRITECMD},
+    {"ClipboardChanged",EVENT_CLIPBOARDCHANGED},
     {"CmdwinEnter",    EVENT_CMDWINENTER},
     {"CmdwinLeave",    EVENT_CMDWINLEAVE},
     {"ColorScheme",    EVENT_COLORSCHEME},
--- vim-current/src/gui_w32.c   2007-03-12 16:06:08.985171200 -0400
+++ vim-changed/src/gui_w32.c   2007-03-23 15:23:51.000000000 -0400
@@ -160,6 +160,22 @@
 #  define HANDLE_WM_DEADCHAR(hwnd, wParam, lParam, fn) \
     ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L)
 # endif
+# ifndef HANDLE_WM_CHANGECBCHAIN
+#  define HANDLE_WM_CHANGECBCHAIN(hwnd, wParam, lParam, fn) \
+    ((fn)((hwnd), (HWND)(wParam), (HWND)(lParam)), 0L)
+# endif
+# ifndef FORWARD_WM_CHANGECBCHAIN
+#  define FORWARD_WM_CHANGECBCHAIN(hwnd, hwndRemove, hwndNext, fn) \
+    (void)(fn)((hwnd), WM_CHANGECBCHAIN, (WPARAM)(HWND)(hwndRemove), 
(LPARAM)(HWND)(hwndNext))
+# endif
+# ifndef HANDLE_WM_DRAWCLIPBOARD
+#  define HANDLE_WM_DRAWCLIPBOARD(hwnd, wParam, lParam, fn) \
+    ((fn)(hwnd), 0L)
+# endif
+# ifndef FORWARD_WM_DRAWCLIPBOARD
+#  define FORWARD_WM_DRAWCLIPBOARD(hwnd, fn) \
+    (void)(fn)((hwnd), WM_DRAWCLIPBOARD, 0L, 0L)
+# endif
 #endif /* __MINGW32__ */
 
 
@@ -354,6 +370,11 @@
 # define pImmSetConversionStatus  ImmSetConversionStatus
 #endif
 
+#ifdef FEAT_CBWATCH
+static void _OnDrawClipboard(HWND hwnd);
+static void _OnChangeCBChain(HWND hwnd, HWND hwndRemove, HWND hwndNext);
+# endif
+
 #ifndef ETO_IGNORELANGUAGE
 # define ETO_IGNORELANGUAGE  0x1000
 #endif
@@ -784,6 +805,11 @@
        HANDLE_MSG(hwnd, WM_WINDOWPOSCHANGED, _OnWindowPosChanged);
 #endif
 
+#ifdef FEAT_CBWATCH
+       HANDLE_MSG(hwnd, WM_DRAWCLIPBOARD,      _OnDrawClipboard);
+       HANDLE_MSG(hwnd, WM_CHANGECBCHAIN,      _OnChangeCBChain);
+#endif
+
 #ifdef FEAT_GUI_TABLINE
        case WM_RBUTTONUP:
        {
@@ -1076,6 +1102,7 @@
        break;
 #endif
 
+
     default:
        if (uMsg == msh_msgmousewheel && msh_msgmousewheel != 0)
        {   /* handle MSH_MOUSEWHEEL messages for Intellimouse */
@@ -4732,3 +4759,68 @@
     SetPixel(s_hdc, x+2, y, gui.currFgColor);
 }
 #endif
+
+#ifdef FEAT_CBWATCH
+static HWND hwndPasteBoard;
+static HWND hwndNextViewer;
+static BOOL pb_firstpaste;
+
+
+    static void
+_OnChangeCBChain(
+    HWND hwnd,
+    HWND hwndRemove,
+    HWND hwndNext)
+{
+    if (hwndRemove == hwndNextViewer) 
+        hwndNextViewer =  hwndNext; 
+    else if (hwndNextViewer != NULL) 
+       FORWARD_WM_CHANGECBCHAIN(hwnd, hwndRemove, hwndNext, SendMessage);
+}
+    static void
+_OnDrawClipboard(
+    HWND hwnd)
+{
+
+    // If not first paste and clipboard not from current window
+    HWND owner;
+    owner = GetClipboardOwner();
+    if (!pb_firstpaste && hwnd != GetClipboardOwner())
+       ui_clipboard_changed();
+    if (hwndNextViewer)
+        FORWARD_WM_DRAWCLIPBOARD(hwndNextViewer, SendMessage);
+}
+
+    static void
+destroy_cbwatch_window(
+    HWND hwnd)
+{
+    if (hwnd == hwndPasteBoard)
+    {
+         ChangeClipboardChain(hwndPasteBoard, hwndNextViewer); 
+         hwndPasteBoard = NULL;
+         hwndNextViewer = NULL;
+    }
+}
+
+    void
+mch_set_ClipboardWatch(
+   int option)
+{
+    if (hwndPasteBoard && ((hwndPasteBoard != s_hwnd) || !option))
+         destroy_cbwatch_window(hwndPasteBoard); 
+
+    if (option)
+    {
+       if (s_hwnd && s_hwnd != hwndPasteBoard)
+       {
+           // Set pb_firstpaste so that initial state of clipboard
+           // is not pasted.
+           pb_firstpaste  = TRUE;
+           hwndNextViewer = SetClipboardViewer(s_hwnd);
+           pb_firstpaste  = FALSE;
+           hwndPasteBoard = s_hwnd;
+       }
+    }
+}
+#endif /* FEAT_CBWATCH */
--- vim-current/src/gui_w48.c   2007-03-12 16:07:13.027259200 -0400
+++ vim-changed/src/gui_w48.c   2007-03-23 15:23:51.000000000 -0400
@@ -320,6 +320,9 @@
 static LRESULT _OnImeNotify(HWND hWnd, DWORD dwCommand, DWORD dwData);
 #endif
 
+#ifdef FEAT_CBWATCH
+static void destroy_cbwatch_window(HWND hwnd);
+#endif
 #ifdef DEBUG_PRINT_ERROR
 /*
  * Print out the last Windows error message
@@ -2626,6 +2629,9 @@
 #ifdef WIN16_3DLOOK
     Ctl3dUnregister(s_hinst);
 #endif
+#ifdef FEAT_CBWATCH
+    destroy_cbwatch_window(hwnd);
+#endif
     if (!destroying)
        _OnClose(hwnd);
 }
--- vim-current/src/option.c    2007-03-23 15:22:30.102632000 -0400
+++ vim-changed/src/option.c    2007-03-23 15:23:51.000000000 -0400
@@ -741,6 +741,15 @@
                            {(char_u *)"", (char_u *)0L}
 #endif
                            },
+    {"clipboardwatch", "cbw",   P_BOOL|P_VI_DEF,
+#ifdef FEAT_CBWATCH
+                           (char_u *)&p_cbwatch, PV_NONE,
+                           {(char_u *)FALSE, (char_u *)0L},
+#else
+                           (char_u *)NULL, PV_NONE,
+                           {(char_u *)0L, (char_u *)0L}
+#endif
+                           },
     {"cmdheight",   "ch",   P_NUM|P_VI_DEF|P_RALL,
                            (char_u *)&p_ch, PV_NONE,
                            {(char_u *)1L, (char_u *)0L}},
@@ -7383,6 +7392,10 @@
        }
     }
 #endif
+#ifdef FEAT_CBWATCH
+    else if ((int *)varp == &p_cbwatch)
+       mch_set_ClipboardWatch(p_cbwatch);
+#endif
 
 #ifdef FEAT_FKMAP
     else if ((int *)varp == &p_altkeymap)
--- vim-current/src/option.h    2007-03-12 16:06:59.207387200 -0400
+++ vim-changed/src/option.h    2007-03-23 15:23:51.000000000 -0400
@@ -373,6 +373,9 @@
 #ifdef FEAT_CLIPBOARD
 EXTERN char_u  *p_cb;          /* 'clipboard' */
 #endif
+#ifdef FEAT_CBWATCH
+EXTERN int     p_cbwatch;      /* 'clipboardwatch' */
+#endif
 EXTERN long    p_ch;           /* 'cmdheight' */
 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
 EXTERN int     p_confirm;      /* 'confirm' */
--- vim-current/src/os_mswin.c  2007-03-12 16:07:21.619614400 -0400
+++ vim-changed/src/os_mswin.c  2007-03-23 15:23:52.000000000 -0400
@@ -1540,7 +1540,10 @@
      * because then we can't paste back into the same window for some
      * reason - webb.
      */
-    if (OpenClipboard(NULL))
+    // KH: I don't think above comment is true (anymore?)
+    // and it is good to do this for FEAT_CBWATCH.  Could
+    // be made conditional, though.
+    if (OpenClipboard(GetActiveWindow()))
     {
        if (EmptyClipboard())
        {
--- vim-current/src/proto/gui_w32.pro   2006-05-07 09:09:27.000000000 -0400
+++ vim-changed/src/proto/gui_w32.pro   2007-03-23 15:23:52.000000000 -0400
@@ -93,4 +93,7 @@
 extern BalloonEval *gui_mch_create_beval_area __ARGS((void *target, char_u 
*mesg, void (*mesgCB)(BalloonEval *, int), void *clientData));
 extern void gui_mch_destroy_beval_area __ARGS((BalloonEval *beval));
 extern void netbeans_draw_multisign_indicator __ARGS((int row));
+#ifdef FEAT_CBWATCH
+extern void mch_set_ClipboardWatch __ARGS((int option));
+#endif
 /* vim: set ft=c : */
--- vim-current/src/proto/ui.pro        2006-05-07 09:09:10.000000000 -0400
+++ vim-changed/src/proto/ui.pro        2007-03-23 15:23:52.000000000 -0400
@@ -57,5 +57,6 @@
 extern int get_fpos_of_mouse __ARGS((pos_T *mpos));
 extern int vcol2col __ARGS((win_T *wp, linenr_T lnum, int vcol));
 extern void ui_focus_change __ARGS((int in_focus));
+extern void ui_clipboard_changed __ARGS((void));
 extern void im_save_status __ARGS((long *psave));
 /* vim: set ft=c : */
--- vim-current/src/ui.c        2007-03-23 15:22:32.736419200 -0400
+++ vim-changed/src/ui.c        2007-03-23 15:23:52.000000000 -0400
@@ -3081,6 +3081,54 @@
 }
 #endif
 
+#ifdef FEAT_CBWATCH
+    void
+ui_clipboard_changed()
+{
+    static time_t      last_time = (time_t)0;
+    int                        need_redraw = FALSE;
+
+
+#ifdef FEAT_AUTOCMD
+    need_redraw |= 
+       apply_autocmds(EVENT_CLIPBOARDCHANGED, NULL, NULL, TRUE, curbuf);
+#endif
+
+    if (need_redraw)
+    {
+       /* Something was executed, make sure the cursor is put back where it
+        * belongs. */
+       need_wait_return = FALSE;
+
+       if (State & CMDLINE)
+           redrawcmdline();
+       else if (State == HITRETURN || State == SETWSIZE || State == ASKMORE
+               || State == EXTERNCMD || State == CONFIRM || exmode_active)
+           repeat_message();
+       else if ((State & NORMAL) || (State & INSERT))
+       {
+           if (must_redraw != 0)
+               update_screen(0);
+           setcursor();
+       }
+       cursor_on();        /* redrawing may have switched it off */
+       out_flush();
+# ifdef FEAT_GUI
+       if (gui.in_use)
+       {
+           gui_update_cursor(FALSE, TRUE);
+           gui_update_scrollbars(FALSE);
+       }
+# endif
+    }
+#ifdef FEAT_TITLE
+    /* File may have been changed from 'readonly' to 'noreadonly' */
+    if (need_maketitle)
+       maketitle();
+#endif
+}
+#endif 
+
 #if defined(USE_IM_CONTROL) || defined(PROTO)
 /*
  * Save current Input Method status to specified place.
--- vim-current/src/vim.h       2007-03-23 15:22:28.640529600 -0400
+++ vim-changed/src/vim.h       2007-03-23 15:23:52.000000000 -0400
@@ -1164,6 +1164,7 @@
     EVENT_TABENTER,            /* after entering a tab page */
     EVENT_SHELLCMDPOST,                /* after ":!cmd" */
     EVENT_SHELLFILTERPOST,     /* after ":1,2!cmd", ":w !cmd", ":r !cmd". */
+    EVENT_CLIPBOARDCHANGED,    /* clipboard changed (clipboardwatch) */
     NUM_EVENTS                 /* MUST be the last one */
 };
 

Reply via email to