Mercurial > vim
annotate src/GvimExt/gvimext.cpp @ 7215:a22e3bf151ca
Added tag v7.4.916 for changeset e036defe034e0d1291351d324f8edede8c410369
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Tue, 10 Nov 2015 19:15:05 +0100 |
parents | 81cb471657e0 |
children | 1e48ffa2d697 |
rev | line source |
---|---|
10 | 1 /* 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 | |
25 static 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 | |
31 FORMATETC fmte = {CF_HDROP, | |
32 (DVTARGETDEVICE FAR *)NULL, | |
33 DVASPECT_CONTENT, | |
34 -1, | |
35 TYMED_HGLOBAL | |
36 }; | |
37 STGMEDIUM medium; | |
38 HRESULT hres = 0; | |
39 UINT cbFiles = 0; | |
40 | |
415 | 41 /* The buffers size used to be MAX_PATH (256 bytes), but that's not always |
42 * enough */ | |
43 #define BUFSIZE 1100 | |
44 | |
10 | 45 // |
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. | |
415 | 48 // Returns the path in name[BUFSIZE]. It's empty when it fails. |
10 | 49 // |
50 static void | |
51 getGvimName(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 { | |
415 | 61 hlen = BUFSIZE; |
10 | 62 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) | |
1366 | 72 strcpy(name, searchpath((char *)"gvim.exe")); |
10 | 73 |
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) | |
1366 | 79 strcpy(name, searchpath((char *)"gvim.bat")); |
10 | 80 if (name[0] == 0) |
81 strcpy(name, "gvim"); // finds gvim.bat or gvim.exe | |
82 } | |
83 } | |
84 | |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
85 static void |
6805 | 86 getGvimInvocation(char *name, int runtime) |
87 { | |
88 getGvimName(name, runtime); | |
89 // avoid that Vim tries to expand wildcards in the file names | |
90 strcat(name, " --literal"); | |
91 } | |
92 | |
93 static void | |
94 getGvimInvocationW(wchar_t *nameW) | |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
95 { |
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
96 char *name; |
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
97 |
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
98 name = (char *)malloc(BUFSIZE); |
6805 | 99 getGvimInvocation(name, 0); |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
100 mbstowcs(nameW, name, BUFSIZE); |
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
101 free(name); |
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
102 } |
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
103 |
10 | 104 // |
415 | 105 // Get the Vim runtime directory into buf[BUFSIZE]. |
10 | 106 // The result is empty when it failed. |
107 // When it works, the path ends in a slash or backslash. | |
108 // | |
109 static void | |
110 getRuntimeDir(char *buf) | |
111 { | |
112 int idx; | |
113 | |
114 getGvimName(buf, 1); | |
115 if (buf[0] != 0) | |
116 { | |
117 // When no path found, use the search path to expand it. | |
118 if (strchr(buf, '/') == NULL && strchr(buf, '\\') == NULL) | |
119 strcpy(buf, searchpath(buf)); | |
120 | |
121 // remove "gvim.exe" from the end | |
835 | 122 for (idx = (int)strlen(buf) - 1; idx >= 0; idx--) |
10 | 123 if (buf[idx] == '\\' || buf[idx] == '/') |
124 { | |
125 buf[idx + 1] = 0; | |
126 break; | |
127 } | |
128 } | |
129 } | |
130 | |
6805 | 131 HBITMAP IconToBitmap(HICON hIcon, HBRUSH hBackground, int width, int height) |
132 { | |
133 HDC hDC = GetDC(NULL); | |
134 HDC hMemDC = CreateCompatibleDC(hDC); | |
135 HBITMAP hMemBmp = CreateCompatibleBitmap(hDC, width, height); | |
136 HBITMAP hResultBmp = NULL; | |
137 HGDIOBJ hOrgBMP = SelectObject(hMemDC, hMemBmp); | |
138 | |
139 DrawIconEx(hMemDC, 0, 0, hIcon, width, height, 0, hBackground, DI_NORMAL); | |
140 | |
141 hResultBmp = hMemBmp; | |
142 hMemBmp = NULL; | |
143 | |
144 SelectObject(hMemDC, hOrgBMP); | |
145 DeleteDC(hMemDC); | |
146 ReleaseDC(NULL, hDC); | |
147 DestroyIcon(hIcon); | |
148 return hResultBmp; | |
149 } | |
150 | |
10 | 151 // |
152 // GETTEXT: translated messages and menu entries | |
153 // | |
154 #ifndef FEAT_GETTEXT | |
155 # define _(x) x | |
156 #else | |
157 # define _(x) (*dyn_libintl_gettext)(x) | |
158 # define VIMPACKAGE "vim" | |
159 # ifndef GETTEXT_DLL | |
160 # define GETTEXT_DLL "libintl.dll" | |
161 # endif | |
162 | |
163 // Dummy functions | |
164 static char *null_libintl_gettext(const char *); | |
165 static char *null_libintl_textdomain(const char *); | |
166 static char *null_libintl_bindtextdomain(const char *, const char *); | |
167 static int dyn_libintl_init(char *dir); | |
168 static void dyn_libintl_end(void); | |
169 | |
3008 | 170 static wchar_t *oldenv = NULL; |
10 | 171 static HINSTANCE hLibintlDLL = 0; |
172 static char *(*dyn_libintl_gettext)(const char *) = null_libintl_gettext; | |
173 static char *(*dyn_libintl_textdomain)(const char *) = null_libintl_textdomain; | |
174 static char *(*dyn_libintl_bindtextdomain)(const char *, const char *) | |
175 = null_libintl_bindtextdomain; | |
176 | |
177 // | |
178 // Attempt to load libintl.dll. If it doesn't work, use dummy functions. | |
179 // "dir" is the directory where the libintl.dll might be. | |
180 // Return 1 for success, 0 for failure. | |
181 // | |
182 static int | |
183 dyn_libintl_init(char *dir) | |
184 { | |
185 int i; | |
186 static struct | |
187 { | |
188 char *name; | |
189 FARPROC *ptr; | |
190 } libintl_entry[] = | |
191 { | |
1366 | 192 {(char *)"gettext", (FARPROC*)&dyn_libintl_gettext}, |
193 {(char *)"textdomain", (FARPROC*)&dyn_libintl_textdomain}, | |
194 {(char *)"bindtextdomain", (FARPROC*)&dyn_libintl_bindtextdomain}, | |
10 | 195 {NULL, NULL} |
196 }; | |
197 | |
198 // No need to initialize twice. | |
199 if (hLibintlDLL) | |
200 return 1; | |
201 | |
202 // Load gettext library, first try the Vim runtime directory, then search | |
203 // the path. | |
204 strcat(dir, GETTEXT_DLL); | |
205 hLibintlDLL = LoadLibrary(dir); | |
206 if (!hLibintlDLL) | |
207 { | |
208 hLibintlDLL = LoadLibrary(GETTEXT_DLL); | |
209 if (!hLibintlDLL) | |
210 return 0; | |
211 } | |
212 | |
213 // Get the addresses of the functions we need. | |
214 for (i = 0; libintl_entry[i].name != NULL | |
215 && libintl_entry[i].ptr != NULL; ++i) | |
216 { | |
217 if ((*libintl_entry[i].ptr = GetProcAddress(hLibintlDLL, | |
218 libintl_entry[i].name)) == NULL) | |
219 { | |
220 dyn_libintl_end(); | |
221 return 0; | |
222 } | |
223 } | |
224 return 1; | |
225 } | |
226 | |
227 static void | |
228 dyn_libintl_end(void) | |
229 { | |
230 if (hLibintlDLL) | |
231 FreeLibrary(hLibintlDLL); | |
232 hLibintlDLL = NULL; | |
233 dyn_libintl_gettext = null_libintl_gettext; | |
234 dyn_libintl_textdomain = null_libintl_textdomain; | |
235 dyn_libintl_bindtextdomain = null_libintl_bindtextdomain; | |
236 } | |
237 | |
238 static char * | |
239 null_libintl_gettext(const char *msgid) | |
240 { | |
241 return (char *)msgid; | |
242 } | |
243 | |
244 static char * | |
1686 | 245 null_libintl_bindtextdomain(const char * /* domainname */, const char * /* dirname */) |
10 | 246 { |
247 return NULL; | |
248 } | |
249 | |
250 static char * | |
1686 | 251 null_libintl_textdomain(const char* /* domainname */) |
10 | 252 { |
253 return NULL; | |
254 } | |
255 | |
256 // | |
257 // Setup for translating strings. | |
258 // | |
259 static void | |
260 dyn_gettext_load(void) | |
261 { | |
415 | 262 char szBuff[BUFSIZE]; |
263 char szLang[BUFSIZE]; | |
10 | 264 DWORD len; |
265 HKEY keyhandle; | |
266 int gotlang = 0; | |
267 | |
268 strcpy(szLang, "LANG="); | |
269 | |
270 // First try getting the language from the registry, this can be | |
271 // used to overrule the system language. | |
272 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Vim\\Gvim", 0, | |
273 KEY_READ, &keyhandle) == ERROR_SUCCESS) | |
274 { | |
415 | 275 len = BUFSIZE; |
10 | 276 if (RegQueryValueEx(keyhandle, "lang", 0, NULL, (BYTE*)szBuff, &len) |
277 == ERROR_SUCCESS) | |
278 { | |
279 szBuff[len] = 0; | |
280 strcat(szLang, szBuff); | |
281 gotlang = 1; | |
282 } | |
283 RegCloseKey(keyhandle); | |
284 } | |
285 | |
286 if (!gotlang && getenv("LANG") == NULL) | |
287 { | |
288 // Get the language from the system. | |
289 // Could use LOCALE_SISO639LANGNAME, but it's not in Win95. | |
290 // LOCALE_SABBREVLANGNAME gives us three letters, like "enu", we use | |
291 // only the first two. | |
292 len = GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SABBREVLANGNAME, | |
415 | 293 (LPTSTR)szBuff, BUFSIZE); |
10 | 294 if (len >= 2 && _strnicmp(szBuff, "en", 2) != 0) |
295 { | |
296 // There are a few exceptions (probably more) | |
297 if (_strnicmp(szBuff, "cht", 3) == 0 | |
298 || _strnicmp(szBuff, "zht", 3) == 0) | |
299 strcpy(szBuff, "zh_TW"); | |
300 else if (_strnicmp(szBuff, "chs", 3) == 0 | |
301 || _strnicmp(szBuff, "zhc", 3) == 0) | |
302 strcpy(szBuff, "zh_CN"); | |
303 else if (_strnicmp(szBuff, "jp", 2) == 0) | |
304 strcpy(szBuff, "ja"); | |
305 else | |
306 szBuff[2] = 0; // truncate to two-letter code | |
307 strcat(szLang, szBuff); | |
308 gotlang = 1; | |
309 } | |
310 } | |
311 if (gotlang) | |
312 putenv(szLang); | |
313 | |
314 // Try to locate the runtime files. The path is used to find libintl.dll | |
315 // and the vim.mo files. | |
316 getRuntimeDir(szBuff); | |
317 if (szBuff[0] != 0) | |
318 { | |
835 | 319 len = (DWORD)strlen(szBuff); |
10 | 320 if (dyn_libintl_init(szBuff)) |
321 { | |
322 strcpy(szBuff + len, "lang"); | |
323 | |
324 (*dyn_libintl_bindtextdomain)(VIMPACKAGE, szBuff); | |
325 (*dyn_libintl_textdomain)(VIMPACKAGE); | |
326 } | |
327 } | |
328 } | |
329 | |
330 static void | |
331 dyn_gettext_free(void) | |
332 { | |
333 dyn_libintl_end(); | |
334 } | |
335 #endif // FEAT_GETTEXT | |
336 | |
337 // | |
338 // Global variables | |
339 // | |
340 UINT g_cRefThisDll = 0; // Reference count of this DLL. | |
341 HINSTANCE g_hmodThisDll = NULL; // Handle to this DLL itself. | |
342 | |
343 | |
344 //--------------------------------------------------------------------------- | |
345 // DllMain | |
346 //--------------------------------------------------------------------------- | |
347 extern "C" int APIENTRY | |
1686 | 348 DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /* lpReserved */) |
10 | 349 { |
350 switch (dwReason) | |
351 { | |
352 case DLL_PROCESS_ATTACH: | |
353 // Extension DLL one-time initialization | |
354 g_hmodThisDll = hInstance; | |
355 break; | |
356 | |
357 case DLL_PROCESS_DETACH: | |
358 break; | |
359 } | |
360 | |
361 return 1; // ok | |
362 } | |
363 | |
364 static void | |
365 inc_cRefThisDLL() | |
366 { | |
367 #ifdef FEAT_GETTEXT | |
3008 | 368 if (g_cRefThisDll == 0) { |
10 | 369 dyn_gettext_load(); |
3008 | 370 oldenv = GetEnvironmentStringsW(); |
371 } | |
10 | 372 #endif |
373 InterlockedIncrement((LPLONG)&g_cRefThisDll); | |
374 } | |
375 | |
376 static void | |
377 dec_cRefThisDLL() | |
378 { | |
379 #ifdef FEAT_GETTEXT | |
3008 | 380 if (InterlockedDecrement((LPLONG)&g_cRefThisDll) == 0) { |
10 | 381 dyn_gettext_free(); |
3008 | 382 if (oldenv != NULL) { |
383 FreeEnvironmentStringsW(oldenv); | |
384 oldenv = NULL; | |
385 } | |
386 } | |
10 | 387 #else |
388 InterlockedDecrement((LPLONG)&g_cRefThisDll); | |
389 #endif | |
390 } | |
391 | |
392 //--------------------------------------------------------------------------- | |
393 // DllCanUnloadNow | |
394 //--------------------------------------------------------------------------- | |
395 | |
396 STDAPI DllCanUnloadNow(void) | |
397 { | |
398 return (g_cRefThisDll == 0 ? S_OK : S_FALSE); | |
399 } | |
400 | |
401 STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppvOut) | |
402 { | |
403 *ppvOut = NULL; | |
404 | |
405 if (IsEqualIID(rclsid, CLSID_ShellExtension)) | |
406 { | |
407 CShellExtClassFactory *pcf = new CShellExtClassFactory; | |
408 | |
409 return pcf->QueryInterface(riid, ppvOut); | |
410 } | |
411 | |
412 return CLASS_E_CLASSNOTAVAILABLE; | |
413 } | |
414 | |
415 CShellExtClassFactory::CShellExtClassFactory() | |
416 { | |
417 m_cRef = 0L; | |
418 | |
419 inc_cRefThisDLL(); | |
420 } | |
421 | |
422 CShellExtClassFactory::~CShellExtClassFactory() | |
423 { | |
424 dec_cRefThisDLL(); | |
425 } | |
426 | |
427 STDMETHODIMP CShellExtClassFactory::QueryInterface(REFIID riid, | |
428 LPVOID FAR *ppv) | |
429 { | |
430 *ppv = NULL; | |
431 | |
6805 | 432 // any interface on this object is the object pointer |
10 | 433 |
434 if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IClassFactory)) | |
435 { | |
436 *ppv = (LPCLASSFACTORY)this; | |
437 | |
438 AddRef(); | |
439 | |
440 return NOERROR; | |
441 } | |
442 | |
443 return E_NOINTERFACE; | |
444 } | |
445 | |
446 STDMETHODIMP_(ULONG) CShellExtClassFactory::AddRef() | |
447 { | |
448 return InterlockedIncrement((LPLONG)&m_cRef); | |
449 } | |
450 | |
451 STDMETHODIMP_(ULONG) CShellExtClassFactory::Release() | |
452 { | |
453 if (InterlockedDecrement((LPLONG)&m_cRef)) | |
454 return m_cRef; | |
455 | |
456 delete this; | |
457 | |
458 return 0L; | |
459 } | |
460 | |
461 STDMETHODIMP CShellExtClassFactory::CreateInstance(LPUNKNOWN pUnkOuter, | |
462 REFIID riid, | |
463 LPVOID *ppvObj) | |
464 { | |
465 *ppvObj = NULL; | |
466 | |
467 // Shell extensions typically don't support aggregation (inheritance) | |
468 | |
469 if (pUnkOuter) | |
470 return CLASS_E_NOAGGREGATION; | |
471 | |
472 // Create the main shell extension object. The shell will then call | |
473 // QueryInterface with IID_IShellExtInit--this is how shell extensions are | |
474 // initialized. | |
475 | |
6805 | 476 LPCSHELLEXT pShellExt = new CShellExt(); // create the CShellExt object |
10 | 477 |
478 if (NULL == pShellExt) | |
479 return E_OUTOFMEMORY; | |
480 | |
481 return pShellExt->QueryInterface(riid, ppvObj); | |
482 } | |
483 | |
484 | |
1686 | 485 STDMETHODIMP CShellExtClassFactory::LockServer(BOOL /* fLock */) |
10 | 486 { |
487 return NOERROR; | |
488 } | |
489 | |
490 // *********************** CShellExt ************************* | |
491 CShellExt::CShellExt() | |
492 { | |
493 m_cRef = 0L; | |
494 m_pDataObj = NULL; | |
495 | |
496 inc_cRefThisDLL(); | |
6805 | 497 |
498 LoadMenuIcon(); | |
10 | 499 } |
500 | |
501 CShellExt::~CShellExt() | |
502 { | |
503 if (m_pDataObj) | |
504 m_pDataObj->Release(); | |
505 | |
506 dec_cRefThisDLL(); | |
6805 | 507 |
508 if (m_hVimIconBitmap) | |
509 DeleteObject(m_hVimIconBitmap); | |
10 | 510 } |
511 | |
512 STDMETHODIMP CShellExt::QueryInterface(REFIID riid, LPVOID FAR *ppv) | |
513 { | |
514 *ppv = NULL; | |
515 | |
516 if (IsEqualIID(riid, IID_IShellExtInit) || IsEqualIID(riid, IID_IUnknown)) | |
517 { | |
518 *ppv = (LPSHELLEXTINIT)this; | |
519 } | |
520 else if (IsEqualIID(riid, IID_IContextMenu)) | |
521 { | |
522 *ppv = (LPCONTEXTMENU)this; | |
523 } | |
524 | |
525 if (*ppv) | |
526 { | |
527 AddRef(); | |
528 | |
529 return NOERROR; | |
530 } | |
531 | |
532 return E_NOINTERFACE; | |
533 } | |
534 | |
535 STDMETHODIMP_(ULONG) CShellExt::AddRef() | |
536 { | |
537 return InterlockedIncrement((LPLONG)&m_cRef); | |
538 } | |
539 | |
540 STDMETHODIMP_(ULONG) CShellExt::Release() | |
541 { | |
542 | |
543 if (InterlockedDecrement((LPLONG)&m_cRef)) | |
544 return m_cRef; | |
545 | |
546 delete this; | |
547 | |
548 return 0L; | |
549 } | |
550 | |
551 | |
552 // | |
553 // FUNCTION: CShellExt::Initialize(LPCITEMIDLIST, LPDATAOBJECT, HKEY) | |
554 // | |
555 // PURPOSE: Called by the shell when initializing a context menu or property | |
556 // sheet extension. | |
557 // | |
558 // PARAMETERS: | |
559 // pIDFolder - Specifies the parent folder | |
4352 | 560 // pDataObj - Specifies the set of items selected in that folder. |
10 | 561 // hRegKey - Specifies the type of the focused item in the selection. |
562 // | |
563 // RETURN VALUE: | |
564 // | |
565 // NOERROR in all cases. | |
566 // | |
567 // COMMENTS: Note that at the time this function is called, we don't know | |
568 // (or care) what type of shell extension is being initialized. | |
569 // It could be a context menu or a property sheet. | |
570 // | |
571 | |
1686 | 572 STDMETHODIMP CShellExt::Initialize(LPCITEMIDLIST /* pIDFolder */, |
10 | 573 LPDATAOBJECT pDataObj, |
1686 | 574 HKEY /* hRegKey */) |
10 | 575 { |
576 // Initialize can be called more than once | |
577 if (m_pDataObj) | |
578 m_pDataObj->Release(); | |
579 | |
580 // duplicate the object pointer and registry handle | |
581 | |
582 if (pDataObj) | |
583 { | |
584 m_pDataObj = pDataObj; | |
585 pDataObj->AddRef(); | |
586 } | |
587 | |
588 return NOERROR; | |
589 } | |
590 | |
591 | |
592 // | |
593 // FUNCTION: CShellExt::QueryContextMenu(HMENU, UINT, UINT, UINT, UINT) | |
594 // | |
595 // PURPOSE: Called by the shell just before the context menu is displayed. | |
596 // This is where you add your specific menu items. | |
597 // | |
598 // PARAMETERS: | |
599 // hMenu - Handle to the context menu | |
600 // indexMenu - Index of where to begin inserting menu items | |
601 // idCmdFirst - Lowest value for new menu ID's | |
602 // idCmtLast - Highest value for new menu ID's | |
603 // uFlags - Specifies the context of the menu event | |
604 // | |
605 // RETURN VALUE: | |
606 // | |
607 // | |
608 // COMMENTS: | |
609 // | |
610 | |
611 STDMETHODIMP CShellExt::QueryContextMenu(HMENU hMenu, | |
612 UINT indexMenu, | |
613 UINT idCmdFirst, | |
1686 | 614 UINT /* idCmdLast */, |
615 UINT /* uFlags */) | |
10 | 616 { |
617 UINT idCmd = idCmdFirst; | |
618 | |
619 hres = m_pDataObj->GetData(&fmte, &medium); | |
620 if (medium.hGlobal) | |
621 cbFiles = DragQueryFile((HDROP)medium.hGlobal, (UINT)-1, 0, 0); | |
622 | |
623 // InsertMenu(hMenu, indexMenu++, MF_SEPARATOR|MF_BYPOSITION, 0, NULL); | |
624 | |
625 // Initialize m_cntOfHWnd to 0 | |
626 m_cntOfHWnd = 0; | |
2972 | 627 |
628 HKEY keyhandle; | |
629 bool showExisting = true; | |
6805 | 630 bool showIcons = true; |
2972 | 631 |
632 // Check whether "Edit with existing Vim" entries are disabled. | |
633 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Vim\\Gvim", 0, | |
634 KEY_READ, &keyhandle) == ERROR_SUCCESS) | |
635 { | |
636 if (RegQueryValueEx(keyhandle, "DisableEditWithExisting", 0, NULL, | |
637 NULL, NULL) == ERROR_SUCCESS) | |
638 showExisting = false; | |
6805 | 639 if (RegQueryValueEx(keyhandle, "DisableContextMenuIcons", 0, NULL, |
640 NULL, NULL) == ERROR_SUCCESS) | |
641 showIcons = false; | |
2972 | 642 RegCloseKey(keyhandle); |
643 } | |
644 | |
645 // Retrieve all the vim instances, unless disabled. | |
646 if (showExisting) | |
647 EnumWindows(EnumWindowsProc, (LPARAM)this); | |
10 | 648 |
6805 | 649 MENUITEMINFO mii = { sizeof(MENUITEMINFO) }; |
650 mii.fMask = MIIM_STRING | MIIM_ID; | |
651 if (showIcons) | |
652 { | |
653 mii.fMask |= MIIM_BITMAP; | |
654 mii.hbmpItem = m_hVimIconBitmap; | |
655 } | |
656 | |
10 | 657 if (cbFiles > 1) |
658 { | |
6805 | 659 mii.wID = idCmd++; |
660 mii.dwTypeData = _("Edit with &multiple Vims"); | |
661 mii.cch = lstrlen(mii.dwTypeData); | |
662 InsertMenuItem(hMenu, indexMenu++, TRUE, &mii); | |
10 | 663 |
6805 | 664 mii.wID = idCmd++; |
665 mii.dwTypeData = _("Edit with single &Vim"); | |
666 mii.cch = lstrlen(mii.dwTypeData); | |
667 InsertMenuItem(hMenu, indexMenu++, TRUE, &mii); | |
10 | 668 |
669 if (cbFiles <= 4) | |
670 { | |
671 // Can edit up to 4 files in diff mode | |
6805 | 672 mii.wID = idCmd++; |
673 mii.dwTypeData = _("Diff with Vim"); | |
674 mii.cch = lstrlen(mii.dwTypeData); | |
675 InsertMenuItem(hMenu, indexMenu++, TRUE, &mii); | |
10 | 676 m_edit_existing_off = 3; |
677 } | |
678 else | |
679 m_edit_existing_off = 2; | |
680 | |
681 } | |
682 else | |
683 { | |
6805 | 684 mii.wID = idCmd++; |
685 mii.dwTypeData = _("Edit with &Vim"); | |
686 mii.cch = lstrlen(mii.dwTypeData); | |
687 InsertMenuItem(hMenu, indexMenu++, TRUE, &mii); | |
10 | 688 m_edit_existing_off = 1; |
689 } | |
690 | |
691 // Now display all the vim instances | |
692 for (int i = 0; i < m_cntOfHWnd; i++) | |
693 { | |
415 | 694 char title[BUFSIZE]; |
695 char temp[BUFSIZE]; | |
10 | 696 |
697 // Obtain window title, continue if can not | |
415 | 698 if (GetWindowText(m_hWnd[i], title, BUFSIZE - 1) == 0) |
10 | 699 continue; |
700 // Truncate the title before the path, keep the file name | |
701 char *pos = strchr(title, '('); | |
702 if (pos != NULL) | |
703 { | |
704 if (pos > title && pos[-1] == ' ') | |
705 --pos; | |
706 *pos = 0; | |
707 } | |
708 // Now concatenate | |
415 | 709 strncpy(temp, _("Edit with existing Vim - "), BUFSIZE - 1); |
1953 | 710 temp[BUFSIZE - 1] = '\0'; |
711 strncat(temp, title, BUFSIZE - 1 - strlen(temp)); | |
712 temp[BUFSIZE - 1] = '\0'; | |
6805 | 713 |
714 mii.wID = idCmd++; | |
715 mii.dwTypeData = temp; | |
716 mii.cch = lstrlen(mii.dwTypeData); | |
717 InsertMenuItem(hMenu, indexMenu++, TRUE, &mii); | |
10 | 718 } |
719 // InsertMenu(hMenu, indexMenu++, MF_SEPARATOR|MF_BYPOSITION, 0, NULL); | |
720 | |
721 // Must return number of menu items we added. | |
722 return ResultFromShort(idCmd-idCmdFirst); | |
723 } | |
724 | |
725 // | |
726 // FUNCTION: CShellExt::InvokeCommand(LPCMINVOKECOMMANDINFO) | |
727 // | |
728 // PURPOSE: Called by the shell after the user has selected on of the | |
729 // menu items that was added in QueryContextMenu(). | |
730 // | |
731 // PARAMETERS: | |
732 // lpcmi - Pointer to an CMINVOKECOMMANDINFO structure | |
733 // | |
734 // RETURN VALUE: | |
735 // | |
736 // | |
737 // COMMENTS: | |
738 // | |
739 | |
740 STDMETHODIMP CShellExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi) | |
741 { | |
742 HRESULT hr = E_INVALIDARG; | |
743 | |
744 // If HIWORD(lpcmi->lpVerb) then we have been called programmatically | |
745 // and lpVerb is a command that should be invoked. Otherwise, the shell | |
746 // has called us, and LOWORD(lpcmi->lpVerb) is the menu ID the user has | |
747 // selected. Actually, it's (menu ID - idCmdFirst) from QueryContextMenu(). | |
748 if (!HIWORD(lpcmi->lpVerb)) | |
749 { | |
750 UINT idCmd = LOWORD(lpcmi->lpVerb); | |
751 | |
752 if (idCmd >= m_edit_existing_off) | |
753 { | |
754 // Existing with vim instance | |
755 hr = PushToWindow(lpcmi->hwnd, | |
756 lpcmi->lpDirectory, | |
757 lpcmi->lpVerb, | |
758 lpcmi->lpParameters, | |
759 lpcmi->nShow, | |
760 idCmd - m_edit_existing_off); | |
761 } | |
762 else | |
763 { | |
764 switch (idCmd) | |
765 { | |
766 case 0: | |
767 hr = InvokeGvim(lpcmi->hwnd, | |
768 lpcmi->lpDirectory, | |
769 lpcmi->lpVerb, | |
770 lpcmi->lpParameters, | |
771 lpcmi->nShow); | |
772 break; | |
773 case 1: | |
774 hr = InvokeSingleGvim(lpcmi->hwnd, | |
775 lpcmi->lpDirectory, | |
776 lpcmi->lpVerb, | |
777 lpcmi->lpParameters, | |
778 lpcmi->nShow, | |
779 0); | |
780 break; | |
781 case 2: | |
782 hr = InvokeSingleGvim(lpcmi->hwnd, | |
783 lpcmi->lpDirectory, | |
784 lpcmi->lpVerb, | |
785 lpcmi->lpParameters, | |
786 lpcmi->nShow, | |
787 1); | |
788 break; | |
789 } | |
790 } | |
791 } | |
792 return hr; | |
793 } | |
794 | |
1686 | 795 STDMETHODIMP CShellExt::PushToWindow(HWND /* hParent */, |
796 LPCSTR /* pszWorkingDir */, | |
797 LPCSTR /* pszCmd */, | |
798 LPCSTR /* pszParam */, | |
799 int /* iShowCmd */, | |
10 | 800 int idHWnd) |
801 { | |
802 HWND hWnd = m_hWnd[idHWnd]; | |
803 | |
804 // Show and bring vim instance to foreground | |
805 if (IsIconic(hWnd) != 0) | |
806 ShowWindow(hWnd, SW_RESTORE); | |
807 else | |
808 ShowWindow(hWnd, SW_SHOW); | |
809 SetForegroundWindow(hWnd); | |
810 | |
811 // Post the selected files to the vim instance | |
812 PostMessage(hWnd, WM_DROPFILES, (WPARAM)medium.hGlobal, 0); | |
813 | |
814 return NOERROR; | |
815 } | |
816 | |
1686 | 817 STDMETHODIMP CShellExt::GetCommandString(UINT_PTR /* idCmd */, |
10 | 818 UINT uFlags, |
1686 | 819 UINT FAR * /* reserved */, |
10 | 820 LPSTR pszName, |
821 UINT cchMax) | |
822 { | |
823 if (uFlags == GCS_HELPTEXT && cchMax > 35) | |
824 lstrcpy(pszName, _("Edits the selected file(s) with Vim")); | |
825 | |
826 return NOERROR; | |
827 } | |
828 | |
829 BOOL CALLBACK CShellExt::EnumWindowsProc(HWND hWnd, LPARAM lParam) | |
830 { | |
415 | 831 char temp[BUFSIZE]; |
10 | 832 |
833 // First do a bunch of check | |
834 // No invisible window | |
835 if (!IsWindowVisible(hWnd)) return TRUE; | |
836 // No child window ??? | |
837 // if (GetParent(hWnd)) return TRUE; | |
838 // Class name should be Vim, if failed to get class name, return | |
839 if (GetClassName(hWnd, temp, sizeof(temp)) == 0) | |
840 return TRUE; | |
841 // Compare class name to that of vim, if not, return | |
842 if (_strnicmp(temp, "vim", sizeof("vim")) != 0) | |
843 return TRUE; | |
844 // First check if the number of vim instance exceeds MAX_HWND | |
845 CShellExt *cs = (CShellExt*) lParam; | |
846 if (cs->m_cntOfHWnd >= MAX_HWND) return TRUE; | |
847 // Now we get the vim window, put it into some kind of array | |
848 cs->m_hWnd[cs->m_cntOfHWnd] = hWnd; | |
849 cs->m_cntOfHWnd ++; | |
850 | |
851 return TRUE; // continue enumeration (otherwise this would be false) | |
852 } | |
853 | |
6805 | 854 BOOL CShellExt::LoadMenuIcon() |
855 { | |
856 char vimExeFile[BUFSIZE]; | |
857 getGvimName(vimExeFile, 1); | |
858 if (vimExeFile[0] == '\0') | |
859 return FALSE; | |
860 HICON hVimIcon; | |
861 if (ExtractIconEx(vimExeFile, 0, NULL, &hVimIcon, 1) == 0) | |
862 return FALSE; | |
863 m_hVimIconBitmap = IconToBitmap(hVimIcon, | |
864 GetSysColorBrush(COLOR_MENU), | |
865 GetSystemMetrics(SM_CXSMICON), | |
866 GetSystemMetrics(SM_CYSMICON)); | |
867 return TRUE; | |
868 } | |
869 | |
10 | 870 #ifdef WIN32 |
871 // This symbol is not defined in older versions of the SDK or Visual C++. | |
872 | |
873 #ifndef VER_PLATFORM_WIN32_WINDOWS | |
874 # define VER_PLATFORM_WIN32_WINDOWS 1 | |
875 #endif | |
876 | |
877 static DWORD g_PlatformId; | |
878 | |
879 // | |
880 // Set g_PlatformId to VER_PLATFORM_WIN32_NT (NT) or | |
881 // VER_PLATFORM_WIN32_WINDOWS (Win95). | |
882 // | |
883 static void | |
884 PlatformId(void) | |
885 { | |
886 static int done = FALSE; | |
887 | |
888 if (!done) | |
889 { | |
890 OSVERSIONINFO ovi; | |
891 | |
892 ovi.dwOSVersionInfoSize = sizeof(ovi); | |
893 GetVersionEx(&ovi); | |
894 | |
895 g_PlatformId = ovi.dwPlatformId; | |
896 done = TRUE; | |
897 } | |
898 } | |
899 | |
900 # ifndef __BORLANDC__ | |
901 static char * | |
902 searchpath(char *name) | |
903 { | |
415 | 904 static char widename[2 * BUFSIZE]; |
905 static char location[2 * BUFSIZE + 2]; | |
10 | 906 |
907 // There appears to be a bug in FindExecutableA() on Windows NT. | |
908 // Use FindExecutableW() instead... | |
909 PlatformId(); | |
910 if (g_PlatformId == VER_PLATFORM_WIN32_NT) | |
911 { | |
912 MultiByteToWideChar(CP_ACP, 0, (LPCTSTR)name, -1, | |
415 | 913 (LPWSTR)widename, BUFSIZE); |
10 | 914 if (FindExecutableW((LPCWSTR)widename, (LPCWSTR)"", |
915 (LPWSTR)location) > (HINSTANCE)32) | |
916 { | |
917 WideCharToMultiByte(CP_ACP, 0, (LPWSTR)location, -1, | |
415 | 918 (LPSTR)widename, 2 * BUFSIZE, NULL, NULL); |
10 | 919 return widename; |
920 } | |
921 } | |
922 else | |
923 { | |
924 if (FindExecutableA((LPCTSTR)name, (LPCTSTR)"", | |
925 (LPTSTR)location) > (HINSTANCE)32) | |
926 return location; | |
927 } | |
1366 | 928 return (char *)""; |
10 | 929 } |
930 # endif | |
931 #endif | |
932 | |
933 STDMETHODIMP CShellExt::InvokeGvim(HWND hParent, | |
1686 | 934 LPCSTR /* pszWorkingDir */, |
935 LPCSTR /* pszCmd */, | |
936 LPCSTR /* pszParam */, | |
937 int /* iShowCmd */) | |
10 | 938 { |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
939 wchar_t m_szFileUserClickedOn[BUFSIZE]; |
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
940 wchar_t cmdStrW[BUFSIZE]; |
10 | 941 UINT i; |
942 | |
943 for (i = 0; i < cbFiles; i++) | |
944 { | |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
945 DragQueryFileW((HDROP)medium.hGlobal, |
10 | 946 i, |
947 m_szFileUserClickedOn, | |
948 sizeof(m_szFileUserClickedOn)); | |
949 | |
6805 | 950 getGvimInvocationW(cmdStrW); |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
951 wcscat(cmdStrW, L" \""); |
10 | 952 |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
953 if ((wcslen(cmdStrW) + wcslen(m_szFileUserClickedOn) + 2) < BUFSIZE) |
10 | 954 { |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
955 wcscat(cmdStrW, m_szFileUserClickedOn); |
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
956 wcscat(cmdStrW, L"\""); |
10 | 957 |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
958 STARTUPINFOW si; |
10 | 959 PROCESS_INFORMATION pi; |
960 | |
961 ZeroMemory(&si, sizeof(si)); | |
962 si.cb = sizeof(si); | |
963 | |
964 // Start the child process. | |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
965 if (!CreateProcessW(NULL, // No module name (use command line). |
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
966 cmdStrW, // Command line. |
10 | 967 NULL, // Process handle not inheritable. |
968 NULL, // Thread handle not inheritable. | |
969 FALSE, // Set handle inheritance to FALSE. | |
3008 | 970 oldenv == NULL ? 0 : CREATE_UNICODE_ENVIRONMENT, |
971 oldenv, // Use unmodified environment block. | |
10 | 972 NULL, // Use parent's starting directory. |
973 &si, // Pointer to STARTUPINFO structure. | |
974 &pi) // Pointer to PROCESS_INFORMATION structure. | |
975 ) | |
976 { | |
977 MessageBox( | |
978 hParent, | |
979 _("Error creating process: Check if gvim is in your path!"), | |
980 _("gvimext.dll error"), | |
981 MB_OK); | |
982 } | |
983 else | |
984 { | |
985 CloseHandle( pi.hProcess ); | |
986 CloseHandle( pi.hThread ); | |
987 } | |
988 } | |
989 else | |
990 { | |
991 MessageBox( | |
992 hParent, | |
993 _("Path length too long!"), | |
994 _("gvimext.dll error"), | |
995 MB_OK); | |
996 } | |
997 } | |
998 | |
999 return NOERROR; | |
1000 } | |
1001 | |
1002 | |
1003 STDMETHODIMP CShellExt::InvokeSingleGvim(HWND hParent, | |
1686 | 1004 LPCSTR /* pszWorkingDir */, |
1005 LPCSTR /* pszCmd */, | |
1006 LPCSTR /* pszParam */, | |
1007 int /* iShowCmd */, | |
10 | 1008 int useDiff) |
1009 { | |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
1010 wchar_t m_szFileUserClickedOn[BUFSIZE]; |
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
1011 wchar_t *cmdStrW; |
10 | 1012 size_t cmdlen; |
1013 size_t len; | |
1014 UINT i; | |
1015 | |
415 | 1016 cmdlen = BUFSIZE; |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
1017 cmdStrW = (wchar_t *) malloc(cmdlen * sizeof(wchar_t)); |
6805 | 1018 getGvimInvocationW(cmdStrW); |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
1019 |
10 | 1020 if (useDiff) |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
1021 wcscat(cmdStrW, L" -d"); |
10 | 1022 for (i = 0; i < cbFiles; i++) |
1023 { | |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
1024 DragQueryFileW((HDROP)medium.hGlobal, |
10 | 1025 i, |
1026 m_szFileUserClickedOn, | |
1027 sizeof(m_szFileUserClickedOn)); | |
1028 | |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
1029 len = wcslen(cmdStrW) + wcslen(m_szFileUserClickedOn) + 4; |
10 | 1030 if (len > cmdlen) |
1031 { | |
415 | 1032 cmdlen = len + BUFSIZE; |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
1033 cmdStrW = (wchar_t *)realloc(cmdStrW, cmdlen * sizeof(wchar_t)); |
10 | 1034 } |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
1035 wcscat(cmdStrW, L" \""); |
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
1036 wcscat(cmdStrW, m_szFileUserClickedOn); |
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
1037 wcscat(cmdStrW, L"\""); |
10 | 1038 } |
1039 | |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
1040 STARTUPINFOW si; |
10 | 1041 PROCESS_INFORMATION pi; |
1042 | |
1043 ZeroMemory(&si, sizeof(si)); | |
1044 si.cb = sizeof(si); | |
1045 | |
1046 // Start the child process. | |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
1047 if (!CreateProcessW(NULL, // No module name (use command line). |
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
1048 cmdStrW, // Command line. |
10 | 1049 NULL, // Process handle not inheritable. |
1050 NULL, // Thread handle not inheritable. | |
1051 FALSE, // Set handle inheritance to FALSE. | |
3008 | 1052 oldenv == NULL ? 0 : CREATE_UNICODE_ENVIRONMENT, |
1053 oldenv, // Use unmodified environment block. | |
10 | 1054 NULL, // Use parent's starting directory. |
1055 &si, // Pointer to STARTUPINFO structure. | |
1056 &pi) // Pointer to PROCESS_INFORMATION structure. | |
1057 ) | |
1058 { | |
1059 MessageBox( | |
1060 hParent, | |
1061 _("Error creating process: Check if gvim is in your path!"), | |
1062 _("gvimext.dll error"), | |
1063 MB_OK); | |
1064 } | |
1065 else | |
1066 { | |
1067 CloseHandle(pi.hProcess); | |
1068 CloseHandle(pi.hThread); | |
1069 } | |
1070 | |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
1071 free(cmdStrW); |
10 | 1072 |
1073 return NOERROR; | |
1074 } |