Mercurial > vim
annotate src/GvimExt/gvimext.cpp @ 3346:e3cbb11f3555 v7.3.440
updated for version 7.3.440
Problem: Vim does not support UTF8_STRING for the X selection.
Solution: Add UTF8_STRING atom support. (Alex Efros) Use it only when
'encoding' is set to Unicode.
author | Bram Moolenaar <bram@vim.org> |
---|---|
date | Sun, 12 Feb 2012 01:35:10 +0100 |
parents | 37a54a85413e |
children | 04736b4030ec |
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 // avoid that Vim tries to expand wildcards in the file names | |
84 strcat(name, " --literal"); | |
85 } | |
86 } | |
87 | |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
88 static void |
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
89 getGvimNameW(wchar_t *nameW) |
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
90 { |
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
91 char *name; |
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
92 |
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
93 name = (char *)malloc(BUFSIZE); |
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
94 getGvimName(name, 0); |
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
95 mbstowcs(nameW, name, BUFSIZE); |
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
96 free(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 |
10 | 99 // |
415 | 100 // Get the Vim runtime directory into buf[BUFSIZE]. |
10 | 101 // The result is empty when it failed. |
102 // When it works, the path ends in a slash or backslash. | |
103 // | |
104 static void | |
105 getRuntimeDir(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 | |
835 | 117 for (idx = (int)strlen(buf) - 1; idx >= 0; idx--) |
10 | 118 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 | |
139 static char *null_libintl_gettext(const char *); | |
140 static char *null_libintl_textdomain(const char *); | |
141 static char *null_libintl_bindtextdomain(const char *, const char *); | |
142 static int dyn_libintl_init(char *dir); | |
143 static void dyn_libintl_end(void); | |
144 | |
3008 | 145 static wchar_t *oldenv = NULL; |
10 | 146 static HINSTANCE hLibintlDLL = 0; |
147 static char *(*dyn_libintl_gettext)(const char *) = null_libintl_gettext; | |
148 static char *(*dyn_libintl_textdomain)(const char *) = null_libintl_textdomain; | |
149 static 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 | |
158 dyn_libintl_init(char *dir) | |
159 { | |
160 int i; | |
161 static struct | |
162 { | |
163 char *name; | |
164 FARPROC *ptr; | |
165 } libintl_entry[] = | |
166 { | |
1366 | 167 {(char *)"gettext", (FARPROC*)&dyn_libintl_gettext}, |
168 {(char *)"textdomain", (FARPROC*)&dyn_libintl_textdomain}, | |
169 {(char *)"bindtextdomain", (FARPROC*)&dyn_libintl_bindtextdomain}, | |
10 | 170 {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 | |
203 dyn_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 * | |
214 null_libintl_gettext(const char *msgid) | |
215 { | |
216 return (char *)msgid; | |
217 } | |
218 | |
219 static char * | |
1686 | 220 null_libintl_bindtextdomain(const char * /* domainname */, const char * /* dirname */) |
10 | 221 { |
222 return NULL; | |
223 } | |
224 | |
225 static char * | |
1686 | 226 null_libintl_textdomain(const char* /* domainname */) |
10 | 227 { |
228 return NULL; | |
229 } | |
230 | |
231 // | |
232 // Setup for translating strings. | |
233 // | |
234 static void | |
235 dyn_gettext_load(void) | |
236 { | |
415 | 237 char szBuff[BUFSIZE]; |
238 char szLang[BUFSIZE]; | |
10 | 239 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 { | |
415 | 250 len = BUFSIZE; |
10 | 251 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, | |
415 | 268 (LPTSTR)szBuff, BUFSIZE); |
10 | 269 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 { | |
835 | 294 len = (DWORD)strlen(szBuff); |
10 | 295 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 | |
306 dyn_gettext_free(void) | |
307 { | |
308 dyn_libintl_end(); | |
309 } | |
310 #endif // FEAT_GETTEXT | |
311 | |
312 // | |
313 // Global variables | |
314 // | |
315 UINT g_cRefThisDll = 0; // Reference count of this DLL. | |
316 HINSTANCE g_hmodThisDll = NULL; // Handle to this DLL itself. | |
317 | |
318 | |
319 //--------------------------------------------------------------------------- | |
320 // DllMain | |
321 //--------------------------------------------------------------------------- | |
322 extern "C" int APIENTRY | |
1686 | 323 DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /* lpReserved */) |
10 | 324 { |
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 | |
340 inc_cRefThisDLL() | |
341 { | |
342 #ifdef FEAT_GETTEXT | |
3008 | 343 if (g_cRefThisDll == 0) { |
10 | 344 dyn_gettext_load(); |
3008 | 345 oldenv = GetEnvironmentStringsW(); |
346 } | |
10 | 347 #endif |
348 InterlockedIncrement((LPLONG)&g_cRefThisDll); | |
349 } | |
350 | |
351 static void | |
352 dec_cRefThisDLL() | |
353 { | |
354 #ifdef FEAT_GETTEXT | |
3008 | 355 if (InterlockedDecrement((LPLONG)&g_cRefThisDll) == 0) { |
10 | 356 dyn_gettext_free(); |
3008 | 357 if (oldenv != NULL) { |
358 FreeEnvironmentStringsW(oldenv); | |
359 oldenv = NULL; | |
360 } | |
361 } | |
10 | 362 #else |
363 InterlockedDecrement((LPLONG)&g_cRefThisDll); | |
364 #endif | |
365 } | |
366 | |
367 //--------------------------------------------------------------------------- | |
368 // DllCanUnloadNow | |
369 //--------------------------------------------------------------------------- | |
370 | |
371 STDAPI DllCanUnloadNow(void) | |
372 { | |
373 return (g_cRefThisDll == 0 ? S_OK : S_FALSE); | |
374 } | |
375 | |
376 STDAPI 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 | |
390 CShellExtClassFactory::CShellExtClassFactory() | |
391 { | |
392 m_cRef = 0L; | |
393 | |
394 inc_cRefThisDLL(); | |
395 } | |
396 | |
397 CShellExtClassFactory::~CShellExtClassFactory() | |
398 { | |
399 dec_cRefThisDLL(); | |
400 } | |
401 | |
402 STDMETHODIMP 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 | |
421 STDMETHODIMP_(ULONG) CShellExtClassFactory::AddRef() | |
422 { | |
423 return InterlockedIncrement((LPLONG)&m_cRef); | |
424 } | |
425 | |
426 STDMETHODIMP_(ULONG) CShellExtClassFactory::Release() | |
427 { | |
428 if (InterlockedDecrement((LPLONG)&m_cRef)) | |
429 return m_cRef; | |
430 | |
431 delete this; | |
432 | |
433 return 0L; | |
434 } | |
435 | |
436 STDMETHODIMP 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 | |
1686 | 460 STDMETHODIMP CShellExtClassFactory::LockServer(BOOL /* fLock */) |
10 | 461 { |
462 return NOERROR; | |
463 } | |
464 | |
465 // *********************** CShellExt ************************* | |
466 CShellExt::CShellExt() | |
467 { | |
468 m_cRef = 0L; | |
469 m_pDataObj = NULL; | |
470 | |
471 inc_cRefThisDLL(); | |
472 } | |
473 | |
474 CShellExt::~CShellExt() | |
475 { | |
476 if (m_pDataObj) | |
477 m_pDataObj->Release(); | |
478 | |
479 dec_cRefThisDLL(); | |
480 } | |
481 | |
482 STDMETHODIMP 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 | |
505 STDMETHODIMP_(ULONG) CShellExt::AddRef() | |
506 { | |
507 return InterlockedIncrement((LPLONG)&m_cRef); | |
508 } | |
509 | |
510 STDMETHODIMP_(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 | |
530 // pDataObj - Spefifies the set of items selected in that folder. | |
531 // 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 | |
1686 | 542 STDMETHODIMP CShellExt::Initialize(LPCITEMIDLIST /* pIDFolder */, |
10 | 543 LPDATAOBJECT pDataObj, |
1686 | 544 HKEY /* hRegKey */) |
10 | 545 { |
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 | |
581 STDMETHODIMP CShellExt::QueryContextMenu(HMENU hMenu, | |
582 UINT indexMenu, | |
583 UINT idCmdFirst, | |
1686 | 584 UINT /* idCmdLast */, |
585 UINT /* uFlags */) | |
10 | 586 { |
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; | |
2972 | 597 |
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); | |
10 | 614 |
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++, | |
23 | 636 _("Diff with Vim")); |
10 | 637 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 { | |
415 | 656 char title[BUFSIZE]; |
657 char temp[BUFSIZE]; | |
10 | 658 |
659 // Obtain window title, continue if can not | |
415 | 660 if (GetWindowText(m_hWnd[i], title, BUFSIZE - 1) == 0) |
10 | 661 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 | |
415 | 671 strncpy(temp, _("Edit with existing Vim - "), BUFSIZE - 1); |
1953 | 672 temp[BUFSIZE - 1] = '\0'; |
673 strncat(temp, title, BUFSIZE - 1 - strlen(temp)); | |
674 temp[BUFSIZE - 1] = '\0'; | |
10 | 675 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 | |
702 STDMETHODIMP 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 | |
1686 | 757 STDMETHODIMP CShellExt::PushToWindow(HWND /* hParent */, |
758 LPCSTR /* pszWorkingDir */, | |
759 LPCSTR /* pszCmd */, | |
760 LPCSTR /* pszParam */, | |
761 int /* iShowCmd */, | |
10 | 762 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 | |
1686 | 779 STDMETHODIMP CShellExt::GetCommandString(UINT_PTR /* idCmd */, |
10 | 780 UINT uFlags, |
1686 | 781 UINT FAR * /* reserved */, |
10 | 782 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 | |
791 BOOL CALLBACK CShellExt::EnumWindowsProc(HWND hWnd, LPARAM lParam) | |
792 { | |
415 | 793 char temp[BUFSIZE]; |
10 | 794 |
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 | |
823 static 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 | |
830 PlatformId(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 * | |
848 searchpath(char *name) | |
849 { | |
415 | 850 static char widename[2 * BUFSIZE]; |
851 static char location[2 * BUFSIZE + 2]; | |
10 | 852 |
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, | |
415 | 859 (LPWSTR)widename, BUFSIZE); |
10 | 860 if (FindExecutableW((LPCWSTR)widename, (LPCWSTR)"", |
861 (LPWSTR)location) > (HINSTANCE)32) | |
862 { | |
863 WideCharToMultiByte(CP_ACP, 0, (LPWSTR)location, -1, | |
415 | 864 (LPSTR)widename, 2 * BUFSIZE, NULL, NULL); |
10 | 865 return widename; |
866 } | |
867 } | |
868 else | |
869 { | |
870 if (FindExecutableA((LPCTSTR)name, (LPCTSTR)"", | |
871 (LPTSTR)location) > (HINSTANCE)32) | |
872 return location; | |
873 } | |
1366 | 874 return (char *)""; |
10 | 875 } |
876 # endif | |
877 #endif | |
878 | |
879 STDMETHODIMP CShellExt::InvokeGvim(HWND hParent, | |
1686 | 880 LPCSTR /* pszWorkingDir */, |
881 LPCSTR /* pszCmd */, | |
882 LPCSTR /* pszParam */, | |
883 int /* iShowCmd */) | |
10 | 884 { |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
885 wchar_t m_szFileUserClickedOn[BUFSIZE]; |
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
886 wchar_t cmdStrW[BUFSIZE]; |
10 | 887 UINT i; |
888 | |
889 for (i = 0; i < cbFiles; i++) | |
890 { | |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
891 DragQueryFileW((HDROP)medium.hGlobal, |
10 | 892 i, |
893 m_szFileUserClickedOn, | |
894 sizeof(m_szFileUserClickedOn)); | |
895 | |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
896 getGvimNameW(cmdStrW); |
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
897 wcscat(cmdStrW, L" \""); |
10 | 898 |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
899 if ((wcslen(cmdStrW) + wcslen(m_szFileUserClickedOn) + 2) < BUFSIZE) |
10 | 900 { |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
901 wcscat(cmdStrW, m_szFileUserClickedOn); |
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
902 wcscat(cmdStrW, L"\""); |
10 | 903 |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
904 STARTUPINFOW si; |
10 | 905 PROCESS_INFORMATION pi; |
906 | |
907 ZeroMemory(&si, sizeof(si)); | |
908 si.cb = sizeof(si); | |
909 | |
910 // Start the child process. | |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
911 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
|
912 cmdStrW, // Command line. |
10 | 913 NULL, // Process handle not inheritable. |
914 NULL, // Thread handle not inheritable. | |
915 FALSE, // Set handle inheritance to FALSE. | |
3008 | 916 oldenv == NULL ? 0 : CREATE_UNICODE_ENVIRONMENT, |
917 oldenv, // Use unmodified environment block. | |
10 | 918 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 | |
949 STDMETHODIMP CShellExt::InvokeSingleGvim(HWND hParent, | |
1686 | 950 LPCSTR /* pszWorkingDir */, |
951 LPCSTR /* pszCmd */, | |
952 LPCSTR /* pszParam */, | |
953 int /* iShowCmd */, | |
10 | 954 int useDiff) |
955 { | |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
956 wchar_t m_szFileUserClickedOn[BUFSIZE]; |
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
957 wchar_t *cmdStrW; |
10 | 958 size_t cmdlen; |
959 size_t len; | |
960 UINT i; | |
961 | |
415 | 962 cmdlen = BUFSIZE; |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
963 cmdStrW = (wchar_t *) malloc(cmdlen * sizeof(wchar_t)); |
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
964 getGvimNameW(cmdStrW); |
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
965 |
10 | 966 if (useDiff) |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
967 wcscat(cmdStrW, L" -d"); |
10 | 968 for (i = 0; i < cbFiles; i++) |
969 { | |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
970 DragQueryFileW((HDROP)medium.hGlobal, |
10 | 971 i, |
972 m_szFileUserClickedOn, | |
973 sizeof(m_szFileUserClickedOn)); | |
974 | |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
975 len = wcslen(cmdStrW) + wcslen(m_szFileUserClickedOn) + 4; |
10 | 976 if (len > cmdlen) |
977 { | |
415 | 978 cmdlen = len + BUFSIZE; |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
979 cmdStrW = (wchar_t *)realloc(cmdStrW, cmdlen * sizeof(wchar_t)); |
10 | 980 } |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
981 wcscat(cmdStrW, L" \""); |
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
982 wcscat(cmdStrW, m_szFileUserClickedOn); |
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
983 wcscat(cmdStrW, L"\""); |
10 | 984 } |
985 | |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
986 STARTUPINFOW si; |
10 | 987 PROCESS_INFORMATION pi; |
988 | |
989 ZeroMemory(&si, sizeof(si)); | |
990 si.cb = sizeof(si); | |
991 | |
992 // Start the child process. | |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
993 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
|
994 cmdStrW, // Command line. |
10 | 995 NULL, // Process handle not inheritable. |
996 NULL, // Thread handle not inheritable. | |
997 FALSE, // Set handle inheritance to FALSE. | |
3008 | 998 oldenv == NULL ? 0 : CREATE_UNICODE_ENVIRONMENT, |
999 oldenv, // Use unmodified environment block. | |
10 | 1000 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 | |
2295
b9bc9c5df131
Support wide file names in gvimext. (Szabolcs Horvat)
Bram Moolenaar <bram@vim.org>
parents:
1953
diff
changeset
|
1017 free(cmdStrW); |
10 | 1018 |
1019 return NOERROR; | |
1020 } |