blob: 70448b8d699cd301cb7303b43588a8c1edb37087 [file] [log] [blame]
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00001/* vi:set ts=8 sts=4 sw=4:
2 *
3 * VIM - Vi IMproved gvimext by Tianmiao Hu
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 */
8
9/*
10 * gvimext is a DLL which is used for the "Edit with Vim" context menu
11 * extension. It implements a MS defined interface with the Shell.
12 *
13 * If you have any questions or any suggestions concerning gvimext, please
14 * contact Tianmiao Hu: tianmiao@acm.org.
15 */
16
17#include "gvimext.h"
18
19#ifdef __BORLANDC__
20# include <dir.h>
21# ifndef _strnicmp
22# define _strnicmp(a, b, c) strnicmp((a), (b), (c))
23# endif
24#else
25static char *searchpath(char *name);
26#endif
27
28// Always get an error while putting the following stuff to the
29// gvimext.h file as class protected variables, give up and
30// declare them as global stuff
31FORMATETC fmte = {CF_HDROP,
32 (DVTARGETDEVICE FAR *)NULL,
33 DVASPECT_CONTENT,
34 -1,
35 TYMED_HGLOBAL
36 };
37STGMEDIUM medium;
38HRESULT hres = 0;
39UINT cbFiles = 0;
40
Bram Moolenaare759a7a2005-07-12 22:50:18 +000041/* The buffers size used to be MAX_PATH (256 bytes), but that's not always
42 * enough */
43#define BUFSIZE 1100
44
Bram Moolenaarf4b8e572004-06-24 15:53:16 +000045//
46// Get the name of the Gvim executable to use, with the path.
47// When "runtime" is non-zero, we were called to find the runtime directory.
Bram Moolenaare759a7a2005-07-12 22:50:18 +000048// Returns the path in name[BUFSIZE]. It's empty when it fails.
Bram Moolenaarf4b8e572004-06-24 15:53:16 +000049//
50 static void
51getGvimName(char *name, int runtime)
52{
53 HKEY keyhandle;
54 DWORD hlen;
55
56 // Get the location of gvim from the registry.
57 name[0] = 0;
58 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Vim\\Gvim", 0,
59 KEY_READ, &keyhandle) == ERROR_SUCCESS)
60 {
Bram Moolenaare759a7a2005-07-12 22:50:18 +000061 hlen = BUFSIZE;
Bram Moolenaarf4b8e572004-06-24 15:53:16 +000062 if (RegQueryValueEx(keyhandle, "path", 0, NULL, (BYTE *)name, &hlen)
63 != ERROR_SUCCESS)
64 name[0] = 0;
65 else
66 name[hlen] = 0;
67 RegCloseKey(keyhandle);
68 }
69
70 // Registry didn't work, use the search path.
71 if (name[0] == 0)
Bram Moolenaar7baa45d2007-08-18 15:00:42 +000072 strcpy(name, searchpath((char *)"gvim.exe"));
Bram Moolenaarf4b8e572004-06-24 15:53:16 +000073
74 if (!runtime)
75 {
76 // Only when looking for the executable, not the runtime dir, we can
77 // search for the batch file or a name without a path.
78 if (name[0] == 0)
Bram Moolenaar7baa45d2007-08-18 15:00:42 +000079 strcpy(name, searchpath((char *)"gvim.bat"));
Bram Moolenaarf4b8e572004-06-24 15:53:16 +000080 if (name[0] == 0)
81 strcpy(name, "gvim"); // finds gvim.bat or gvim.exe
82
83 // avoid that Vim tries to expand wildcards in the file names
84 strcat(name, " --literal");
85 }
86}
87
Bram Moolenaar7e6d3bd2010-07-10 19:22:44 +020088 static void
89getGvimNameW(wchar_t *nameW)
90{
91 char *name;
92
93 name = (char *)malloc(BUFSIZE);
94 getGvimName(name, 0);
95 mbstowcs(nameW, name, BUFSIZE);
96 free(name);
97}
98
Bram Moolenaarf4b8e572004-06-24 15:53:16 +000099//
Bram Moolenaare759a7a2005-07-12 22:50:18 +0000100// Get the Vim runtime directory into buf[BUFSIZE].
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000101// The result is empty when it failed.
102// When it works, the path ends in a slash or backslash.
103//
104 static void
105getRuntimeDir(char *buf)
106{
107 int idx;
108
109 getGvimName(buf, 1);
110 if (buf[0] != 0)
111 {
112 // When no path found, use the search path to expand it.
113 if (strchr(buf, '/') == NULL && strchr(buf, '\\') == NULL)
114 strcpy(buf, searchpath(buf));
115
116 // remove "gvim.exe" from the end
Bram Moolenaara93fa7e2006-04-17 22:14:47 +0000117 for (idx = (int)strlen(buf) - 1; idx >= 0; idx--)
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000118 if (buf[idx] == '\\' || buf[idx] == '/')
119 {
120 buf[idx + 1] = 0;
121 break;
122 }
123 }
124}
125
126//
127// GETTEXT: translated messages and menu entries
128//
129#ifndef FEAT_GETTEXT
130# define _(x) x
131#else
132# define _(x) (*dyn_libintl_gettext)(x)
133# define VIMPACKAGE "vim"
134# ifndef GETTEXT_DLL
135# define GETTEXT_DLL "libintl.dll"
136# endif
137
138// Dummy functions
139static char *null_libintl_gettext(const char *);
140static char *null_libintl_textdomain(const char *);
141static char *null_libintl_bindtextdomain(const char *, const char *);
142static int dyn_libintl_init(char *dir);
143static void dyn_libintl_end(void);
144
Bram Moolenaarcf839732011-08-10 16:31:23 +0200145static wchar_t *oldenv = NULL;
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000146static HINSTANCE hLibintlDLL = 0;
147static char *(*dyn_libintl_gettext)(const char *) = null_libintl_gettext;
148static char *(*dyn_libintl_textdomain)(const char *) = null_libintl_textdomain;
149static char *(*dyn_libintl_bindtextdomain)(const char *, const char *)
150 = null_libintl_bindtextdomain;
151
152//
153// Attempt to load libintl.dll. If it doesn't work, use dummy functions.
154// "dir" is the directory where the libintl.dll might be.
155// Return 1 for success, 0 for failure.
156//
157 static int
158dyn_libintl_init(char *dir)
159{
160 int i;
161 static struct
162 {
163 char *name;
164 FARPROC *ptr;
165 } libintl_entry[] =
166 {
Bram Moolenaar7baa45d2007-08-18 15:00:42 +0000167 {(char *)"gettext", (FARPROC*)&dyn_libintl_gettext},
168 {(char *)"textdomain", (FARPROC*)&dyn_libintl_textdomain},
169 {(char *)"bindtextdomain", (FARPROC*)&dyn_libintl_bindtextdomain},
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000170 {NULL, NULL}
171 };
172
173 // No need to initialize twice.
174 if (hLibintlDLL)
175 return 1;
176
177 // Load gettext library, first try the Vim runtime directory, then search
178 // the path.
179 strcat(dir, GETTEXT_DLL);
180 hLibintlDLL = LoadLibrary(dir);
181 if (!hLibintlDLL)
182 {
183 hLibintlDLL = LoadLibrary(GETTEXT_DLL);
184 if (!hLibintlDLL)
185 return 0;
186 }
187
188 // Get the addresses of the functions we need.
189 for (i = 0; libintl_entry[i].name != NULL
190 && libintl_entry[i].ptr != NULL; ++i)
191 {
192 if ((*libintl_entry[i].ptr = GetProcAddress(hLibintlDLL,
193 libintl_entry[i].name)) == NULL)
194 {
195 dyn_libintl_end();
196 return 0;
197 }
198 }
199 return 1;
200}
201
202 static void
203dyn_libintl_end(void)
204{
205 if (hLibintlDLL)
206 FreeLibrary(hLibintlDLL);
207 hLibintlDLL = NULL;
208 dyn_libintl_gettext = null_libintl_gettext;
209 dyn_libintl_textdomain = null_libintl_textdomain;
210 dyn_libintl_bindtextdomain = null_libintl_bindtextdomain;
211}
212
213 static char *
214null_libintl_gettext(const char *msgid)
215{
216 return (char *)msgid;
217}
218
219 static char *
Bram Moolenaare6a91fd2008-07-24 18:51:11 +0000220null_libintl_bindtextdomain(const char * /* domainname */, const char * /* dirname */)
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000221{
222 return NULL;
223}
224
225 static char *
Bram Moolenaare6a91fd2008-07-24 18:51:11 +0000226null_libintl_textdomain(const char* /* domainname */)
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000227{
228 return NULL;
229}
230
231//
232// Setup for translating strings.
233//
234 static void
235dyn_gettext_load(void)
236{
Bram Moolenaare759a7a2005-07-12 22:50:18 +0000237 char szBuff[BUFSIZE];
238 char szLang[BUFSIZE];
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000239 DWORD len;
240 HKEY keyhandle;
241 int gotlang = 0;
242
243 strcpy(szLang, "LANG=");
244
245 // First try getting the language from the registry, this can be
246 // used to overrule the system language.
247 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Vim\\Gvim", 0,
248 KEY_READ, &keyhandle) == ERROR_SUCCESS)
249 {
Bram Moolenaare759a7a2005-07-12 22:50:18 +0000250 len = BUFSIZE;
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000251 if (RegQueryValueEx(keyhandle, "lang", 0, NULL, (BYTE*)szBuff, &len)
252 == ERROR_SUCCESS)
253 {
254 szBuff[len] = 0;
255 strcat(szLang, szBuff);
256 gotlang = 1;
257 }
258 RegCloseKey(keyhandle);
259 }
260
261 if (!gotlang && getenv("LANG") == NULL)
262 {
263 // Get the language from the system.
264 // Could use LOCALE_SISO639LANGNAME, but it's not in Win95.
265 // LOCALE_SABBREVLANGNAME gives us three letters, like "enu", we use
266 // only the first two.
267 len = GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SABBREVLANGNAME,
Bram Moolenaare759a7a2005-07-12 22:50:18 +0000268 (LPTSTR)szBuff, BUFSIZE);
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000269 if (len >= 2 && _strnicmp(szBuff, "en", 2) != 0)
270 {
271 // There are a few exceptions (probably more)
272 if (_strnicmp(szBuff, "cht", 3) == 0
273 || _strnicmp(szBuff, "zht", 3) == 0)
274 strcpy(szBuff, "zh_TW");
275 else if (_strnicmp(szBuff, "chs", 3) == 0
276 || _strnicmp(szBuff, "zhc", 3) == 0)
277 strcpy(szBuff, "zh_CN");
278 else if (_strnicmp(szBuff, "jp", 2) == 0)
279 strcpy(szBuff, "ja");
280 else
281 szBuff[2] = 0; // truncate to two-letter code
282 strcat(szLang, szBuff);
283 gotlang = 1;
284 }
285 }
286 if (gotlang)
287 putenv(szLang);
288
289 // Try to locate the runtime files. The path is used to find libintl.dll
290 // and the vim.mo files.
291 getRuntimeDir(szBuff);
292 if (szBuff[0] != 0)
293 {
Bram Moolenaara93fa7e2006-04-17 22:14:47 +0000294 len = (DWORD)strlen(szBuff);
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000295 if (dyn_libintl_init(szBuff))
296 {
297 strcpy(szBuff + len, "lang");
298
299 (*dyn_libintl_bindtextdomain)(VIMPACKAGE, szBuff);
300 (*dyn_libintl_textdomain)(VIMPACKAGE);
301 }
302 }
303}
304
305 static void
306dyn_gettext_free(void)
307{
308 dyn_libintl_end();
309}
310#endif // FEAT_GETTEXT
311
312//
313// Global variables
314//
315UINT g_cRefThisDll = 0; // Reference count of this DLL.
316HINSTANCE g_hmodThisDll = NULL; // Handle to this DLL itself.
317
318
319//---------------------------------------------------------------------------
320// DllMain
321//---------------------------------------------------------------------------
322extern "C" int APIENTRY
Bram Moolenaare6a91fd2008-07-24 18:51:11 +0000323DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /* lpReserved */)
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000324{
325 switch (dwReason)
326 {
327 case DLL_PROCESS_ATTACH:
328 // Extension DLL one-time initialization
329 g_hmodThisDll = hInstance;
330 break;
331
332 case DLL_PROCESS_DETACH:
333 break;
334 }
335
336 return 1; // ok
337}
338
339 static void
340inc_cRefThisDLL()
341{
342#ifdef FEAT_GETTEXT
Bram Moolenaarcf839732011-08-10 16:31:23 +0200343 if (g_cRefThisDll == 0) {
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000344 dyn_gettext_load();
Bram Moolenaarcf839732011-08-10 16:31:23 +0200345 oldenv = GetEnvironmentStringsW();
346 }
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000347#endif
348 InterlockedIncrement((LPLONG)&g_cRefThisDll);
349}
350
351 static void
352dec_cRefThisDLL()
353{
354#ifdef FEAT_GETTEXT
Bram Moolenaarcf839732011-08-10 16:31:23 +0200355 if (InterlockedDecrement((LPLONG)&g_cRefThisDll) == 0) {
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000356 dyn_gettext_free();
Bram Moolenaarcf839732011-08-10 16:31:23 +0200357 if (oldenv != NULL) {
358 FreeEnvironmentStringsW(oldenv);
359 oldenv = NULL;
360 }
361 }
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000362#else
363 InterlockedDecrement((LPLONG)&g_cRefThisDll);
364#endif
365}
366
367//---------------------------------------------------------------------------
368// DllCanUnloadNow
369//---------------------------------------------------------------------------
370
371STDAPI DllCanUnloadNow(void)
372{
373 return (g_cRefThisDll == 0 ? S_OK : S_FALSE);
374}
375
376STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppvOut)
377{
378 *ppvOut = NULL;
379
380 if (IsEqualIID(rclsid, CLSID_ShellExtension))
381 {
382 CShellExtClassFactory *pcf = new CShellExtClassFactory;
383
384 return pcf->QueryInterface(riid, ppvOut);
385 }
386
387 return CLASS_E_CLASSNOTAVAILABLE;
388}
389
390CShellExtClassFactory::CShellExtClassFactory()
391{
392 m_cRef = 0L;
393
394 inc_cRefThisDLL();
395}
396
397CShellExtClassFactory::~CShellExtClassFactory()
398{
399 dec_cRefThisDLL();
400}
401
402STDMETHODIMP CShellExtClassFactory::QueryInterface(REFIID riid,
403 LPVOID FAR *ppv)
404{
405 *ppv = NULL;
406
407 // Any interface on this object is the object pointer
408
409 if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IClassFactory))
410 {
411 *ppv = (LPCLASSFACTORY)this;
412
413 AddRef();
414
415 return NOERROR;
416 }
417
418 return E_NOINTERFACE;
419}
420
421STDMETHODIMP_(ULONG) CShellExtClassFactory::AddRef()
422{
423 return InterlockedIncrement((LPLONG)&m_cRef);
424}
425
426STDMETHODIMP_(ULONG) CShellExtClassFactory::Release()
427{
428 if (InterlockedDecrement((LPLONG)&m_cRef))
429 return m_cRef;
430
431 delete this;
432
433 return 0L;
434}
435
436STDMETHODIMP CShellExtClassFactory::CreateInstance(LPUNKNOWN pUnkOuter,
437 REFIID riid,
438 LPVOID *ppvObj)
439{
440 *ppvObj = NULL;
441
442 // Shell extensions typically don't support aggregation (inheritance)
443
444 if (pUnkOuter)
445 return CLASS_E_NOAGGREGATION;
446
447 // Create the main shell extension object. The shell will then call
448 // QueryInterface with IID_IShellExtInit--this is how shell extensions are
449 // initialized.
450
451 LPCSHELLEXT pShellExt = new CShellExt(); //Create the CShellExt object
452
453 if (NULL == pShellExt)
454 return E_OUTOFMEMORY;
455
456 return pShellExt->QueryInterface(riid, ppvObj);
457}
458
459
Bram Moolenaare6a91fd2008-07-24 18:51:11 +0000460STDMETHODIMP CShellExtClassFactory::LockServer(BOOL /* fLock */)
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000461{
462 return NOERROR;
463}
464
465// *********************** CShellExt *************************
466CShellExt::CShellExt()
467{
468 m_cRef = 0L;
469 m_pDataObj = NULL;
470
471 inc_cRefThisDLL();
472}
473
474CShellExt::~CShellExt()
475{
476 if (m_pDataObj)
477 m_pDataObj->Release();
478
479 dec_cRefThisDLL();
480}
481
482STDMETHODIMP CShellExt::QueryInterface(REFIID riid, LPVOID FAR *ppv)
483{
484 *ppv = NULL;
485
486 if (IsEqualIID(riid, IID_IShellExtInit) || IsEqualIID(riid, IID_IUnknown))
487 {
488 *ppv = (LPSHELLEXTINIT)this;
489 }
490 else if (IsEqualIID(riid, IID_IContextMenu))
491 {
492 *ppv = (LPCONTEXTMENU)this;
493 }
494
495 if (*ppv)
496 {
497 AddRef();
498
499 return NOERROR;
500 }
501
502 return E_NOINTERFACE;
503}
504
505STDMETHODIMP_(ULONG) CShellExt::AddRef()
506{
507 return InterlockedIncrement((LPLONG)&m_cRef);
508}
509
510STDMETHODIMP_(ULONG) CShellExt::Release()
511{
512
513 if (InterlockedDecrement((LPLONG)&m_cRef))
514 return m_cRef;
515
516 delete this;
517
518 return 0L;
519}
520
521
522//
523// FUNCTION: CShellExt::Initialize(LPCITEMIDLIST, LPDATAOBJECT, HKEY)
524//
525// PURPOSE: Called by the shell when initializing a context menu or property
526// sheet extension.
527//
528// PARAMETERS:
529// pIDFolder - Specifies the parent folder
Bram Moolenaar84a05ac2013-05-06 04:24:17 +0200530// pDataObj - Specifies the set of items selected in that folder.
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000531// hRegKey - Specifies the type of the focused item in the selection.
532//
533// RETURN VALUE:
534//
535// NOERROR in all cases.
536//
537// COMMENTS: Note that at the time this function is called, we don't know
538// (or care) what type of shell extension is being initialized.
539// It could be a context menu or a property sheet.
540//
541
Bram Moolenaare6a91fd2008-07-24 18:51:11 +0000542STDMETHODIMP CShellExt::Initialize(LPCITEMIDLIST /* pIDFolder */,
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000543 LPDATAOBJECT pDataObj,
Bram Moolenaare6a91fd2008-07-24 18:51:11 +0000544 HKEY /* hRegKey */)
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000545{
546 // Initialize can be called more than once
547 if (m_pDataObj)
548 m_pDataObj->Release();
549
550 // duplicate the object pointer and registry handle
551
552 if (pDataObj)
553 {
554 m_pDataObj = pDataObj;
555 pDataObj->AddRef();
556 }
557
558 return NOERROR;
559}
560
561
562//
563// FUNCTION: CShellExt::QueryContextMenu(HMENU, UINT, UINT, UINT, UINT)
564//
565// PURPOSE: Called by the shell just before the context menu is displayed.
566// This is where you add your specific menu items.
567//
568// PARAMETERS:
569// hMenu - Handle to the context menu
570// indexMenu - Index of where to begin inserting menu items
571// idCmdFirst - Lowest value for new menu ID's
572// idCmtLast - Highest value for new menu ID's
573// uFlags - Specifies the context of the menu event
574//
575// RETURN VALUE:
576//
577//
578// COMMENTS:
579//
580
581STDMETHODIMP CShellExt::QueryContextMenu(HMENU hMenu,
582 UINT indexMenu,
583 UINT idCmdFirst,
Bram Moolenaare6a91fd2008-07-24 18:51:11 +0000584 UINT /* idCmdLast */,
585 UINT /* uFlags */)
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000586{
587 UINT idCmd = idCmdFirst;
588
589 hres = m_pDataObj->GetData(&fmte, &medium);
590 if (medium.hGlobal)
591 cbFiles = DragQueryFile((HDROP)medium.hGlobal, (UINT)-1, 0, 0);
592
593 // InsertMenu(hMenu, indexMenu++, MF_SEPARATOR|MF_BYPOSITION, 0, NULL);
594
595 // Initialize m_cntOfHWnd to 0
596 m_cntOfHWnd = 0;
Bram Moolenaarce35c882011-07-20 17:27:25 +0200597
598 HKEY keyhandle;
599 bool showExisting = true;
600
601 // Check whether "Edit with existing Vim" entries are disabled.
602 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Vim\\Gvim", 0,
603 KEY_READ, &keyhandle) == ERROR_SUCCESS)
604 {
605 if (RegQueryValueEx(keyhandle, "DisableEditWithExisting", 0, NULL,
606 NULL, NULL) == ERROR_SUCCESS)
607 showExisting = false;
608 RegCloseKey(keyhandle);
609 }
610
611 // Retrieve all the vim instances, unless disabled.
612 if (showExisting)
613 EnumWindows(EnumWindowsProc, (LPARAM)this);
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000614
615 if (cbFiles > 1)
616 {
617 InsertMenu(hMenu,
618 indexMenu++,
619 MF_STRING|MF_BYPOSITION,
620 idCmd++,
621 _("Edit with &multiple Vims"));
622
623 InsertMenu(hMenu,
624 indexMenu++,
625 MF_STRING|MF_BYPOSITION,
626 idCmd++,
627 _("Edit with single &Vim"));
628
629 if (cbFiles <= 4)
630 {
631 // Can edit up to 4 files in diff mode
632 InsertMenu(hMenu,
633 indexMenu++,
634 MF_STRING|MF_BYPOSITION,
635 idCmd++,
Bram Moolenaar15d0a8c2004-09-06 17:44:46 +0000636 _("Diff with Vim"));
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000637 m_edit_existing_off = 3;
638 }
639 else
640 m_edit_existing_off = 2;
641
642 }
643 else
644 {
645 InsertMenu(hMenu,
646 indexMenu++,
647 MF_STRING|MF_BYPOSITION,
648 idCmd++,
649 _("Edit with &Vim"));
650 m_edit_existing_off = 1;
651 }
652
653 // Now display all the vim instances
654 for (int i = 0; i < m_cntOfHWnd; i++)
655 {
Bram Moolenaare759a7a2005-07-12 22:50:18 +0000656 char title[BUFSIZE];
657 char temp[BUFSIZE];
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000658
659 // Obtain window title, continue if can not
Bram Moolenaare759a7a2005-07-12 22:50:18 +0000660 if (GetWindowText(m_hWnd[i], title, BUFSIZE - 1) == 0)
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000661 continue;
662 // Truncate the title before the path, keep the file name
663 char *pos = strchr(title, '(');
664 if (pos != NULL)
665 {
666 if (pos > title && pos[-1] == ' ')
667 --pos;
668 *pos = 0;
669 }
670 // Now concatenate
Bram Moolenaare759a7a2005-07-12 22:50:18 +0000671 strncpy(temp, _("Edit with existing Vim - "), BUFSIZE - 1);
Bram Moolenaardc7e00e2009-09-11 11:26:56 +0000672 temp[BUFSIZE - 1] = '\0';
673 strncat(temp, title, BUFSIZE - 1 - strlen(temp));
674 temp[BUFSIZE - 1] = '\0';
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000675 InsertMenu(hMenu,
676 indexMenu++,
677 MF_STRING|MF_BYPOSITION,
678 idCmd++,
679 temp);
680 }
681 // InsertMenu(hMenu, indexMenu++, MF_SEPARATOR|MF_BYPOSITION, 0, NULL);
682
683 // Must return number of menu items we added.
684 return ResultFromShort(idCmd-idCmdFirst);
685}
686
687//
688// FUNCTION: CShellExt::InvokeCommand(LPCMINVOKECOMMANDINFO)
689//
690// PURPOSE: Called by the shell after the user has selected on of the
691// menu items that was added in QueryContextMenu().
692//
693// PARAMETERS:
694// lpcmi - Pointer to an CMINVOKECOMMANDINFO structure
695//
696// RETURN VALUE:
697//
698//
699// COMMENTS:
700//
701
702STDMETHODIMP CShellExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
703{
704 HRESULT hr = E_INVALIDARG;
705
706 // If HIWORD(lpcmi->lpVerb) then we have been called programmatically
707 // and lpVerb is a command that should be invoked. Otherwise, the shell
708 // has called us, and LOWORD(lpcmi->lpVerb) is the menu ID the user has
709 // selected. Actually, it's (menu ID - idCmdFirst) from QueryContextMenu().
710 if (!HIWORD(lpcmi->lpVerb))
711 {
712 UINT idCmd = LOWORD(lpcmi->lpVerb);
713
714 if (idCmd >= m_edit_existing_off)
715 {
716 // Existing with vim instance
717 hr = PushToWindow(lpcmi->hwnd,
718 lpcmi->lpDirectory,
719 lpcmi->lpVerb,
720 lpcmi->lpParameters,
721 lpcmi->nShow,
722 idCmd - m_edit_existing_off);
723 }
724 else
725 {
726 switch (idCmd)
727 {
728 case 0:
729 hr = InvokeGvim(lpcmi->hwnd,
730 lpcmi->lpDirectory,
731 lpcmi->lpVerb,
732 lpcmi->lpParameters,
733 lpcmi->nShow);
734 break;
735 case 1:
736 hr = InvokeSingleGvim(lpcmi->hwnd,
737 lpcmi->lpDirectory,
738 lpcmi->lpVerb,
739 lpcmi->lpParameters,
740 lpcmi->nShow,
741 0);
742 break;
743 case 2:
744 hr = InvokeSingleGvim(lpcmi->hwnd,
745 lpcmi->lpDirectory,
746 lpcmi->lpVerb,
747 lpcmi->lpParameters,
748 lpcmi->nShow,
749 1);
750 break;
751 }
752 }
753 }
754 return hr;
755}
756
Bram Moolenaare6a91fd2008-07-24 18:51:11 +0000757STDMETHODIMP CShellExt::PushToWindow(HWND /* hParent */,
758 LPCSTR /* pszWorkingDir */,
759 LPCSTR /* pszCmd */,
760 LPCSTR /* pszParam */,
761 int /* iShowCmd */,
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000762 int idHWnd)
763{
764 HWND hWnd = m_hWnd[idHWnd];
765
766 // Show and bring vim instance to foreground
767 if (IsIconic(hWnd) != 0)
768 ShowWindow(hWnd, SW_RESTORE);
769 else
770 ShowWindow(hWnd, SW_SHOW);
771 SetForegroundWindow(hWnd);
772
773 // Post the selected files to the vim instance
774 PostMessage(hWnd, WM_DROPFILES, (WPARAM)medium.hGlobal, 0);
775
776 return NOERROR;
777}
778
Bram Moolenaare6a91fd2008-07-24 18:51:11 +0000779STDMETHODIMP CShellExt::GetCommandString(UINT_PTR /* idCmd */,
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000780 UINT uFlags,
Bram Moolenaare6a91fd2008-07-24 18:51:11 +0000781 UINT FAR * /* reserved */,
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000782 LPSTR pszName,
783 UINT cchMax)
784{
785 if (uFlags == GCS_HELPTEXT && cchMax > 35)
786 lstrcpy(pszName, _("Edits the selected file(s) with Vim"));
787
788 return NOERROR;
789}
790
791BOOL CALLBACK CShellExt::EnumWindowsProc(HWND hWnd, LPARAM lParam)
792{
Bram Moolenaare759a7a2005-07-12 22:50:18 +0000793 char temp[BUFSIZE];
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000794
795 // First do a bunch of check
796 // No invisible window
797 if (!IsWindowVisible(hWnd)) return TRUE;
798 // No child window ???
799 // if (GetParent(hWnd)) return TRUE;
800 // Class name should be Vim, if failed to get class name, return
801 if (GetClassName(hWnd, temp, sizeof(temp)) == 0)
802 return TRUE;
803 // Compare class name to that of vim, if not, return
804 if (_strnicmp(temp, "vim", sizeof("vim")) != 0)
805 return TRUE;
806 // First check if the number of vim instance exceeds MAX_HWND
807 CShellExt *cs = (CShellExt*) lParam;
808 if (cs->m_cntOfHWnd >= MAX_HWND) return TRUE;
809 // Now we get the vim window, put it into some kind of array
810 cs->m_hWnd[cs->m_cntOfHWnd] = hWnd;
811 cs->m_cntOfHWnd ++;
812
813 return TRUE; // continue enumeration (otherwise this would be false)
814}
815
816#ifdef WIN32
817// This symbol is not defined in older versions of the SDK or Visual C++.
818
819#ifndef VER_PLATFORM_WIN32_WINDOWS
820# define VER_PLATFORM_WIN32_WINDOWS 1
821#endif
822
823static DWORD g_PlatformId;
824
825//
826// Set g_PlatformId to VER_PLATFORM_WIN32_NT (NT) or
827// VER_PLATFORM_WIN32_WINDOWS (Win95).
828//
829 static void
830PlatformId(void)
831{
832 static int done = FALSE;
833
834 if (!done)
835 {
836 OSVERSIONINFO ovi;
837
838 ovi.dwOSVersionInfoSize = sizeof(ovi);
839 GetVersionEx(&ovi);
840
841 g_PlatformId = ovi.dwPlatformId;
842 done = TRUE;
843 }
844}
845
846# ifndef __BORLANDC__
847 static char *
848searchpath(char *name)
849{
Bram Moolenaare759a7a2005-07-12 22:50:18 +0000850 static char widename[2 * BUFSIZE];
851 static char location[2 * BUFSIZE + 2];
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000852
853 // There appears to be a bug in FindExecutableA() on Windows NT.
854 // Use FindExecutableW() instead...
855 PlatformId();
856 if (g_PlatformId == VER_PLATFORM_WIN32_NT)
857 {
858 MultiByteToWideChar(CP_ACP, 0, (LPCTSTR)name, -1,
Bram Moolenaare759a7a2005-07-12 22:50:18 +0000859 (LPWSTR)widename, BUFSIZE);
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000860 if (FindExecutableW((LPCWSTR)widename, (LPCWSTR)"",
861 (LPWSTR)location) > (HINSTANCE)32)
862 {
863 WideCharToMultiByte(CP_ACP, 0, (LPWSTR)location, -1,
Bram Moolenaare759a7a2005-07-12 22:50:18 +0000864 (LPSTR)widename, 2 * BUFSIZE, NULL, NULL);
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000865 return widename;
866 }
867 }
868 else
869 {
870 if (FindExecutableA((LPCTSTR)name, (LPCTSTR)"",
871 (LPTSTR)location) > (HINSTANCE)32)
872 return location;
873 }
Bram Moolenaar7baa45d2007-08-18 15:00:42 +0000874 return (char *)"";
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000875}
876# endif
877#endif
878
879STDMETHODIMP CShellExt::InvokeGvim(HWND hParent,
Bram Moolenaare6a91fd2008-07-24 18:51:11 +0000880 LPCSTR /* pszWorkingDir */,
881 LPCSTR /* pszCmd */,
882 LPCSTR /* pszParam */,
883 int /* iShowCmd */)
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000884{
Bram Moolenaar7e6d3bd2010-07-10 19:22:44 +0200885 wchar_t m_szFileUserClickedOn[BUFSIZE];
886 wchar_t cmdStrW[BUFSIZE];
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000887 UINT i;
888
889 for (i = 0; i < cbFiles; i++)
890 {
Bram Moolenaar7e6d3bd2010-07-10 19:22:44 +0200891 DragQueryFileW((HDROP)medium.hGlobal,
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000892 i,
893 m_szFileUserClickedOn,
894 sizeof(m_szFileUserClickedOn));
895
Bram Moolenaar7e6d3bd2010-07-10 19:22:44 +0200896 getGvimNameW(cmdStrW);
897 wcscat(cmdStrW, L" \"");
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000898
Bram Moolenaar7e6d3bd2010-07-10 19:22:44 +0200899 if ((wcslen(cmdStrW) + wcslen(m_szFileUserClickedOn) + 2) < BUFSIZE)
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000900 {
Bram Moolenaar7e6d3bd2010-07-10 19:22:44 +0200901 wcscat(cmdStrW, m_szFileUserClickedOn);
902 wcscat(cmdStrW, L"\"");
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000903
Bram Moolenaar7e6d3bd2010-07-10 19:22:44 +0200904 STARTUPINFOW si;
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000905 PROCESS_INFORMATION pi;
906
907 ZeroMemory(&si, sizeof(si));
908 si.cb = sizeof(si);
909
910 // Start the child process.
Bram Moolenaar7e6d3bd2010-07-10 19:22:44 +0200911 if (!CreateProcessW(NULL, // No module name (use command line).
912 cmdStrW, // Command line.
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000913 NULL, // Process handle not inheritable.
914 NULL, // Thread handle not inheritable.
915 FALSE, // Set handle inheritance to FALSE.
Bram Moolenaarcf839732011-08-10 16:31:23 +0200916 oldenv == NULL ? 0 : CREATE_UNICODE_ENVIRONMENT,
917 oldenv, // Use unmodified environment block.
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000918 NULL, // Use parent's starting directory.
919 &si, // Pointer to STARTUPINFO structure.
920 &pi) // Pointer to PROCESS_INFORMATION structure.
921 )
922 {
923 MessageBox(
924 hParent,
925 _("Error creating process: Check if gvim is in your path!"),
926 _("gvimext.dll error"),
927 MB_OK);
928 }
929 else
930 {
931 CloseHandle( pi.hProcess );
932 CloseHandle( pi.hThread );
933 }
934 }
935 else
936 {
937 MessageBox(
938 hParent,
939 _("Path length too long!"),
940 _("gvimext.dll error"),
941 MB_OK);
942 }
943 }
944
945 return NOERROR;
946}
947
948
949STDMETHODIMP CShellExt::InvokeSingleGvim(HWND hParent,
Bram Moolenaare6a91fd2008-07-24 18:51:11 +0000950 LPCSTR /* pszWorkingDir */,
951 LPCSTR /* pszCmd */,
952 LPCSTR /* pszParam */,
953 int /* iShowCmd */,
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000954 int useDiff)
955{
Bram Moolenaar7e6d3bd2010-07-10 19:22:44 +0200956 wchar_t m_szFileUserClickedOn[BUFSIZE];
957 wchar_t *cmdStrW;
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000958 size_t cmdlen;
959 size_t len;
960 UINT i;
961
Bram Moolenaare759a7a2005-07-12 22:50:18 +0000962 cmdlen = BUFSIZE;
Bram Moolenaar7e6d3bd2010-07-10 19:22:44 +0200963 cmdStrW = (wchar_t *) malloc(cmdlen * sizeof(wchar_t));
964 getGvimNameW(cmdStrW);
965
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000966 if (useDiff)
Bram Moolenaar7e6d3bd2010-07-10 19:22:44 +0200967 wcscat(cmdStrW, L" -d");
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000968 for (i = 0; i < cbFiles; i++)
969 {
Bram Moolenaar7e6d3bd2010-07-10 19:22:44 +0200970 DragQueryFileW((HDROP)medium.hGlobal,
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000971 i,
972 m_szFileUserClickedOn,
973 sizeof(m_szFileUserClickedOn));
974
Bram Moolenaar7e6d3bd2010-07-10 19:22:44 +0200975 len = wcslen(cmdStrW) + wcslen(m_szFileUserClickedOn) + 4;
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000976 if (len > cmdlen)
977 {
Bram Moolenaare759a7a2005-07-12 22:50:18 +0000978 cmdlen = len + BUFSIZE;
Bram Moolenaar7e6d3bd2010-07-10 19:22:44 +0200979 cmdStrW = (wchar_t *)realloc(cmdStrW, cmdlen * sizeof(wchar_t));
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000980 }
Bram Moolenaar7e6d3bd2010-07-10 19:22:44 +0200981 wcscat(cmdStrW, L" \"");
982 wcscat(cmdStrW, m_szFileUserClickedOn);
983 wcscat(cmdStrW, L"\"");
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000984 }
985
Bram Moolenaar7e6d3bd2010-07-10 19:22:44 +0200986 STARTUPINFOW si;
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000987 PROCESS_INFORMATION pi;
988
989 ZeroMemory(&si, sizeof(si));
990 si.cb = sizeof(si);
991
992 // Start the child process.
Bram Moolenaar7e6d3bd2010-07-10 19:22:44 +0200993 if (!CreateProcessW(NULL, // No module name (use command line).
994 cmdStrW, // Command line.
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000995 NULL, // Process handle not inheritable.
996 NULL, // Thread handle not inheritable.
997 FALSE, // Set handle inheritance to FALSE.
Bram Moolenaarcf839732011-08-10 16:31:23 +0200998 oldenv == NULL ? 0 : CREATE_UNICODE_ENVIRONMENT,
999 oldenv, // Use unmodified environment block.
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00001000 NULL, // Use parent's starting directory.
1001 &si, // Pointer to STARTUPINFO structure.
1002 &pi) // Pointer to PROCESS_INFORMATION structure.
1003 )
1004 {
1005 MessageBox(
1006 hParent,
1007 _("Error creating process: Check if gvim is in your path!"),
1008 _("gvimext.dll error"),
1009 MB_OK);
1010 }
1011 else
1012 {
1013 CloseHandle(pi.hProcess);
1014 CloseHandle(pi.hThread);
1015 }
1016
Bram Moolenaar7e6d3bd2010-07-10 19:22:44 +02001017 free(cmdStrW);
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00001018
1019 return NOERROR;
1020}