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 */
};