diff options
Diffstat (limited to 'src/GvimExt')
-rw-r--r-- | src/GvimExt/GvimExt.reg | 20 | ||||
-rw-r--r-- | src/GvimExt/Make_ming.mak | 83 | ||||
-rw-r--r-- | src/GvimExt/Make_mvc.mak | 99 | ||||
-rw-r--r-- | src/GvimExt/Makefile | 4 | ||||
-rw-r--r-- | src/GvimExt/README.txt | 94 | ||||
-rw-r--r-- | src/GvimExt/gvimext.cpp | 1050 | ||||
-rw-r--r-- | src/GvimExt/gvimext.def | 8 | ||||
-rw-r--r-- | src/GvimExt/gvimext.h | 167 | ||||
-rw-r--r-- | src/GvimExt/gvimext.inf | 22 | ||||
-rw-r--r-- | src/GvimExt/gvimext.rc | 109 | ||||
-rw-r--r-- | src/GvimExt/gvimext_ming.def | 10 | ||||
-rw-r--r-- | src/GvimExt/gvimext_ming.rc | 45 | ||||
-rw-r--r-- | src/GvimExt/resource.h | 15 | ||||
-rw-r--r-- | src/GvimExt/uninst.bat | 1 |
14 files changed, 1727 insertions, 0 deletions
diff --git a/src/GvimExt/GvimExt.reg b/src/GvimExt/GvimExt.reg new file mode 100644 index 0000000..0abeb4e --- /dev/null +++ b/src/GvimExt/GvimExt.reg @@ -0,0 +1,20 @@ +REGEDIT4 + +[HKEY_CLASSES_ROOT\CLSID\{51EEE242-AD87-11d3-9C1E-0090278BBD99}] + @="Vim Shell Extension" +[HKEY_CLASSES_ROOT\CLSID\{51EEE242-AD87-11d3-9C1E-0090278BBD99}\InProcServer32] + @="gvimext.dll" + "ThreadingModel"="Apartment" + +[HKEY_CLASSES_ROOT\*\shellex\ContextMenuHandlers\gvim] + @="{51EEE242-AD87-11d3-9C1E-0090278BBD99}" + +[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved] + "{51EEE242-AD87-11d3-9C1E-0090278BBD99}"="Vim Shell Extension" + +[HKEY_LOCAL_MACHINE\Software\Vim\Gvim] + "path"="gvim.exe" + +[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall\Vim 9.1] + "DisplayName"="Vim 9.1: Edit with Vim popup menu entry" + "UninstallString"="uninstall.exe" diff --git a/src/GvimExt/Make_ming.mak b/src/GvimExt/Make_ming.mak new file mode 100644 index 0000000..85017d8 --- /dev/null +++ b/src/GvimExt/Make_ming.mak @@ -0,0 +1,83 @@ +# Project: gvimext +# Generates gvimext.dll with gcc. +# To be used with MingW and Cygwin. +# +# Originally, the DLL base address was fixed: -Wl,--image-base=0x1C000000 +# Now it is allocated dynamically by the linker by evaluating all DLLs +# already loaded in memory. The binary image contains as well information +# for automatic pseudo-rebasing, if needed by the system. ALV 2004-02-29 + +# If cross-compiling set this to yes, else set it to no +CROSS = no +#CROSS = yes +# For the old MinGW 2.95 (the one you get e.g. with debian woody) +# set the following variable to yes and check if the executables are +# really named that way. +# If you have a newer MinGW or you are using cygwin set it to no and +# check also the executables +MINGWOLD = no + +# Link against the shared versions of libgcc/libstdc++ by default. Set +# STATIC_STDCPLUS to "yes" to link against static versions instead. +STATIC_STDCPLUS=no +#STATIC_STDCPLUS=yes + +# Note: -static-libstdc++ is not available until gcc 4.5.x. +LDFLAGS += -shared +ifeq (yes, $(STATIC_STDCPLUS)) +LDFLAGS += -static-libgcc -static-libstdc++ +endif + +ifeq ($(CROSS),yes) +DEL = rm +ifeq ($(MINGWOLD),yes) +CXXFLAGS := -O2 -fvtable-thunks +else +CXXFLAGS := -O2 +endif +else +CXXFLAGS := -O2 +ifneq (sh.exe, $(SHELL)) +DEL = rm +else +DEL = del +endif +endif +# Set the default $(WINVER) to make it work with Windows 7. +ifndef WINVER +WINVER = 0x0601 +endif +CXX := $(CROSS_COMPILE)g++ +WINDRES := $(CROSS_COMPILE)windres +# this used to have --preprocessor, but it's no longer supported +WINDRES_FLAGS = +LIBS := -luuid -lgdi32 +RES := gvimext.res +ifeq ($(findstring clang++,$(CXX)),) +DEFFILE = gvimext_ming.def +endif +OBJ := gvimext.o + +DLL := gvimext.dll + +.PHONY: all all-before all-after clean clean-custom + +all: all-before $(DLL) all-after + +$(DLL): $(OBJ) $(RES) $(DEFFILE) + $(CXX) $(LDFLAGS) $(CXXFLAGS) -s -o $@ \ + -Wl,--enable-auto-image-base \ + -Wl,--enable-auto-import \ + -Wl,--whole-archive \ + $^ \ + -Wl,--no-whole-archive \ + $(LIBS) + +gvimext.o: gvimext.cpp + $(CXX) $(CXXFLAGS) -DFEAT_GETTEXT -DWINVER=$(WINVER) -D_WIN32_WINNT=$(WINVER) -c $? -o $@ + +$(RES): gvimext_ming.rc + $(WINDRES) $(WINDRES_FLAGS) --input-format=rc --output-format=coff -DMING $? -o $@ + +clean: clean-custom + -$(DEL) $(OBJ) $(RES) $(DLL) diff --git a/src/GvimExt/Make_mvc.mak b/src/GvimExt/Make_mvc.mak new file mode 100644 index 0000000..7068d8f --- /dev/null +++ b/src/GvimExt/Make_mvc.mak @@ -0,0 +1,99 @@ +# Makefile for GvimExt, using MSVC +# Options: +# DEBUG=yes Build debug version (for VC7 and maybe later) +# CPUARG= /arch:IA32/AVX/etc, call from main makefile to set +# automatically from CPUNR +# + +TARGETOS = WINNT + +!ifndef APPVER +APPVER = 6.01 +!endif +# Set the default $(WINVER) to make it work with Windows 7. +!ifndef WINVER +WINVER = 0x0601 +!endif + +!if "$(DEBUG)" != "yes" +NODEBUG = 1 +!endif + +!ifdef PROCESSOR_ARCHITECTURE +# On Windows NT +! ifndef CPU +CPU = i386 +! if !defined(PLATFORM) && defined(TARGET_CPU) +PLATFORM = $(TARGET_CPU) +! endif +! ifdef PLATFORM +! if ("$(PLATFORM)" == "x64") || ("$(PLATFORM)" == "X64") +CPU = AMD64 +! elseif ("$(PLATFORM)" == "arm64") || ("$(PLATFORM)" == "ARM64") +CPU = ARM64 +! elseif ("$(PLATFORM)" != "x86") && ("$(PLATFORM)" != "X86") +! error *** ERROR Unknown target platform "$(PLATFORM)". Make aborted. +! endif +! endif +! endif +!else +CPU = i386 +!endif + +!ifdef SDK_INCLUDE_DIR +! include $(SDK_INCLUDE_DIR)\Win32.mak +!elseif "$(USE_WIN32MAK)"=="yes" +! include <Win32.mak> +!else +cc = cl +link = link +rc = rc +cflags = -nologo -c +lflags = -incremental:no -nologo +rcflags = /r +olelibsdll = ole32.lib uuid.lib oleaut32.lib user32.lib gdi32.lib advapi32.lib +!endif + +# include CPUARG +cflags = $(cflags) $(CPUARG) + +# set WINVER and _WIN32_WINNT +cflags = $(cflags) -DWINVER=$(WINVER) -D_WIN32_WINNT=$(WINVER) + +!if "$(CL)" == "/D_USING_V110_SDK71_" +rcflags = $(rcflags) /D_USING_V110_SDK71_ +!endif + +SUBSYSTEM = console +!if "$(SUBSYSTEM_VER)" != "" +SUBSYSTEM = $(SUBSYSTEM),$(SUBSYSTEM_VER) +!endif + +!if "$(CPU)" == "AMD64" || "$(CPU)" == "ARM64" +OFFSET = 0x11C000000 +!else +OFFSET = 0x1C000000 +!endif + +all: gvimext.dll + +gvimext.dll: gvimext.obj \ + gvimext.res + $(link) $(lflags) -dll -def:gvimext.def -base:$(OFFSET) -out:$*.dll $** $(olelibsdll) shell32.lib comctl32.lib -subsystem:$(SUBSYSTEM) + if exist $*.dll.manifest mt -nologo -manifest $*.dll.manifest -outputresource:$*.dll;2 + +gvimext.obj: gvimext.h + +.cpp.obj: + $(cc) $(cflags) -DFEAT_GETTEXT $(cvarsmt) $*.cpp + +gvimext.res: gvimext.rc + $(rc) /nologo $(rcflags) $(rcvars) gvimext.rc + +clean: + - if exist gvimext.dll del gvimext.dll + - if exist gvimext.lib del gvimext.lib + - if exist gvimext.exp del gvimext.exp + - if exist gvimext.obj del gvimext.obj + - if exist gvimext.res del gvimext.res + - if exist gvimext.dll.manifest del gvimext.dll.manifest diff --git a/src/GvimExt/Makefile b/src/GvimExt/Makefile new file mode 100644 index 0000000..18b91ec --- /dev/null +++ b/src/GvimExt/Makefile @@ -0,0 +1,4 @@ +!message This makefile is deprecated. Use Make_mvc.mak instead. +!message + +!include Make_mvc.mak diff --git a/src/GvimExt/README.txt b/src/GvimExt/README.txt new file mode 100644 index 0000000..a29200e --- /dev/null +++ b/src/GvimExt/README.txt @@ -0,0 +1,94 @@ +README.txt for the gvimext DLL. + +Written by Tianmiao Hu. Edited by Bram Moolenaar. + + +INSTALLATION + +To install the "Edit with Vim" popup menu entry, it is recommended to use the +"install.exe" program. It will ask you a few questions and install the needed +registry entries. + +In special situations you might want to make changes by hand. Check these +items: +- The gvimext.dll, gvim.exe and uninstall.exe either need to be in the search + path, or you have to set the full path in the registry entries. You could + move the gvimext.dll to the "windows\system" or "windows\system32" + directory, where the other DLL files are. +- You can find the names of the used registry entries in the file + "GvimExt.reg". You can edit this file to add the paths. To install the + registry entries, right-click the gvimext.reg file and choose the "merge" + menu option. +- The registry key [HKEY_LOCAL_MACHINE\Software\Vim\Gvim] is used by the + gvimext.dll. The value "path" specifies the location of "gvim.exe". If + gvim.exe is in the search path, the path can be omitted. The value "lang" + can be used to set the language, for example "de" for German. If "lang" is + omitted, the language set for Windows will be used. + +It is the preferred method to keep gvim.exe with the runtime files, so that +Vim will find them (also the translated menu items are there). + + +UNINSTALLATION + +To uninstall the "Edit with Vim" popup menu entry, it is recommended to use +the "uninstal.exe" program. + +In special situations you might want to uninstall by hand: +- Open the registry by running regedit.exe. +- Delete all the keys listed in GvimExt.reg, except this one: + [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved] + For this key, only delete one value: + "{51EEE242-AD87-11d3-9C1E-0090278BBD99}"="Vim Shell Extension" +- Delete the gvimext.dll, if you want. You might need to reboot the machine + in order to remove this file. A quick way is to log off and re-login. + +Another method is by using the uninst.bat script: + uninst gvimext.inf +This batch file will remove all the registry keys from the system. Then you +can remove the gvimext.dll file. +Note: In order for this batch file to work, you must have two system files: +rundll32.exe and setupapi.dll. I believe you will have rundll32.exe in your +system. I know windows nt 4.0 with the service pack 4 has setupapi.dll. My +windows 95 has setupapi.dll. I find that the internet explorer 4.0 comes with +the setupapi.dll in file Ie4_5.cab. + +If you do encounter problems running this script, then probably you need to +modify the uninst.bat to suit to your system. Basically, you must find out +where are the locations for your rundll32.exe and setupapi.dll files. In +windows nt, both files are under c:\winnt\system32 directory. In my windows 95 +system, I got setupapi.dll at c:\windows\system and rundll32.exe at +c:\windows. So you might want to try something like: + rundll32.exe c:\windows\system\setupapi.dll,InstallHinfSection DefaultUninstall 128 %1 +where %1 can be substituted by gvimext.inf + + +THE SOURCE CODE + +I have provided the source code here in hope that gvim users around world can +further enhance this little dll. I believe the only thing you need to change +is gvimext.cpp file. The important two functions you need to look at are +QueryContextMenu and InvokeCommand. You can modify right-click menus in the +QueryContextMenu function and invoke gvim in the InvokeCommand function. Note +the selected files can be accessed from the DragQueryFile function. I am not +familiar with the invoking options for gvim. I believe there are some +improvements that can be made on that side. + +I use MS Visual C++ 6.0's nmake to make the gvimext.dll. I don't have a +chance to try earlier versions of MSVC. The files that are required for build +are: + gvimext.cpp + gvimext.h + gvimext.def + gvimext.rc + resource.h + Makefile + +To compile the DLL from the command line: + vcvars32 + nmake -f Makefile + +If you did something interesting to this dll, please let me know +@ tianmiao@acm.org. + +Happy vimming!!! diff --git a/src/GvimExt/gvimext.cpp b/src/GvimExt/gvimext.cpp new file mode 100644 index 0000000..e58b142 --- /dev/null +++ b/src/GvimExt/gvimext.cpp @@ -0,0 +1,1050 @@ +/* vi:set ts=8 sts=4 sw=4: + * + * VIM - Vi IMproved gvimext by Tianmiao Hu + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + */ + +/* + * gvimext is a DLL which is used for the "Edit with Vim" context menu + * extension. It implements a MS defined interface with the Shell. + * + * If you have any questions or any suggestions concerning gvimext, please + * contact Tianmiao Hu: tianmiao@acm.org. + */ + +#include "gvimext.h" + +static char *searchpath(char *name); + +// Always get an error while putting the following stuff to the +// gvimext.h file as class protected variables, give up and +// declare them as global stuff +FORMATETC fmte = {CF_HDROP, + (DVTARGETDEVICE FAR *)NULL, + DVASPECT_CONTENT, + -1, + TYMED_HGLOBAL + }; +STGMEDIUM medium; +HRESULT hres = 0; +UINT cbFiles = 0; + +/* The buffers size used to be MAX_PATH (260 bytes), but that's not always + * enough */ +#define BUFSIZE 1100 + +// The "Edit with Vim" shell extension provides these choices when +// a new instance of Gvim is selected: +// - use tabpages +// - enable diff mode +// - none of the above +#define EDIT_WITH_VIM_USE_TABPAGES (2) +#define EDIT_WITH_VIM_IN_DIFF_MODE (1) +#define EDIT_WITH_VIM_NO_OPTIONS (0) + +// +// Get the name of the Gvim executable to use, with the path. +// When "runtime" is non-zero, we were called to find the runtime directory. +// Returns the path in name[BUFSIZE]. It's empty when it fails. +// + static void +getGvimName(char *name, int runtime) +{ + HKEY keyhandle; + DWORD hlen; + + // Get the location of gvim from the registry. + name[0] = 0; + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Vim\\Gvim", 0, + KEY_READ, &keyhandle) == ERROR_SUCCESS) + { + hlen = BUFSIZE; + if (RegQueryValueEx(keyhandle, "path", 0, NULL, (BYTE *)name, &hlen) + != ERROR_SUCCESS) + name[0] = 0; + else + name[hlen] = 0; + RegCloseKey(keyhandle); + } + + // Registry didn't work, use the search path. + if (name[0] == 0) + strcpy(name, searchpath((char *)"gvim.exe")); + + if (!runtime) + { + // Only when looking for the executable, not the runtime dir, we can + // search for the batch file or a name without a path. + if (name[0] == 0) + strcpy(name, searchpath((char *)"gvim.bat")); + if (name[0] == 0) + strcpy(name, "gvim"); // finds gvim.bat or gvim.exe + } +} + + static void +getGvimInvocation(char *name, int runtime) +{ + getGvimName(name, runtime); + // avoid that Vim tries to expand wildcards in the file names + strcat(name, " --literal"); +} + + static void +getGvimInvocationW(wchar_t *nameW) +{ + char *name; + + name = (char *)malloc(BUFSIZE); + getGvimInvocation(name, 0); + mbstowcs(nameW, name, BUFSIZE); + free(name); +} + +// +// Get the Vim runtime directory into buf[BUFSIZE]. +// The result is empty when it failed. +// When it works, the path ends in a slash or backslash. +// + static void +getRuntimeDir(char *buf) +{ + int idx; + + getGvimName(buf, 1); + if (buf[0] != 0) + { + // When no path found, use the search path to expand it. + if (strchr(buf, '/') == NULL && strchr(buf, '\\') == NULL) + strcpy(buf, searchpath(buf)); + + // remove "gvim.exe" from the end + for (idx = (int)strlen(buf) - 1; idx >= 0; idx--) + if (buf[idx] == '\\' || buf[idx] == '/') + { + buf[idx + 1] = 0; + break; + } + } +} + + WCHAR * +utf8_to_utf16(const char *s) +{ + int size = MultiByteToWideChar(CP_UTF8, 0, s, -1, NULL, 0); + WCHAR *buf = (WCHAR *)malloc(size * sizeof(WCHAR)); + MultiByteToWideChar(CP_UTF8, 0, s, -1, buf, size); + return buf; +} + + HBITMAP +IconToBitmap(HICON hIcon, HBRUSH hBackground, int width, int height) +{ + HDC hDC = GetDC(NULL); + HDC hMemDC = CreateCompatibleDC(hDC); + HBITMAP hMemBmp = CreateCompatibleBitmap(hDC, width, height); + HBITMAP hResultBmp = NULL; + HGDIOBJ hOrgBMP = SelectObject(hMemDC, hMemBmp); + + DrawIconEx(hMemDC, 0, 0, hIcon, width, height, 0, hBackground, DI_NORMAL); + + hResultBmp = hMemBmp; + hMemBmp = NULL; + + SelectObject(hMemDC, hOrgBMP); + DeleteDC(hMemDC); + ReleaseDC(NULL, hDC); + DestroyIcon(hIcon); + return hResultBmp; +} + +// +// GETTEXT: translated messages and menu entries +// +#ifndef FEAT_GETTEXT +# define _(x) x +# define W_impl(x) _wcsdup(L##x) +# define W(x) W_impl(x) +# define set_gettext_codeset() NULL +# define restore_gettext_codeset(x) +#else +# define _(x) (*dyn_libintl_gettext)(x) +# define W(x) utf8_to_utf16(x) +# define VIMPACKAGE "vim" +# ifndef GETTEXT_DLL +# define GETTEXT_DLL "libintl.dll" +# define GETTEXT_DLL_ALT "libintl-8.dll" +# endif + +// Dummy functions +static char *null_libintl_gettext(const char *); +static char *null_libintl_textdomain(const char *); +static char *null_libintl_bindtextdomain(const char *, const char *); +static char *null_libintl_bind_textdomain_codeset(const char *, const char *); +static int dyn_libintl_init(char *dir); +static void dyn_libintl_end(void); + +static HINSTANCE hLibintlDLL = 0; +static char *(*dyn_libintl_gettext)(const char *) = null_libintl_gettext; +static char *(*dyn_libintl_textdomain)(const char *) = null_libintl_textdomain; +static char *(*dyn_libintl_bindtextdomain)(const char *, const char *) + = null_libintl_bindtextdomain; +static char *(*dyn_libintl_bind_textdomain_codeset)(const char *, const char *) + = null_libintl_bind_textdomain_codeset; + +// +// Attempt to load libintl.dll. If it doesn't work, use dummy functions. +// "dir" is the directory where the libintl.dll might be. +// Return 1 for success, 0 for failure. +// + static int +dyn_libintl_init(char *dir) +{ + int i; + static struct + { + char *name; + FARPROC *ptr; + } libintl_entry[] = + { + {(char *)"gettext", (FARPROC*)&dyn_libintl_gettext}, + {(char *)"textdomain", (FARPROC*)&dyn_libintl_textdomain}, + {(char *)"bindtextdomain", (FARPROC*)&dyn_libintl_bindtextdomain}, + {(char *)"bind_textdomain_codeset", (FARPROC*)&dyn_libintl_bind_textdomain_codeset}, + {NULL, NULL} + }; + DWORD len, len2; + LPWSTR buf = NULL; + LPWSTR buf2 = NULL; + + // No need to initialize twice. + if (hLibintlDLL) + return 1; + + // Load gettext library from $VIMRUNTIME\GvimExt{64,32} directory. + // Add the directory to $PATH temporarily. + len = GetEnvironmentVariableW(L"PATH", NULL, 0); + len2 = MAX_PATH + 1 + len; + buf = (LPWSTR)malloc(len * sizeof(WCHAR)); + buf2 = (LPWSTR)malloc(len2 * sizeof(WCHAR)); + if (buf != NULL && buf2 != NULL) + { + GetEnvironmentVariableW(L"PATH", buf, len); +# ifdef _WIN64 + _snwprintf(buf2, len2, L"%S\\GvimExt64;%s", dir, buf); +# else + _snwprintf(buf2, len2, L"%S\\GvimExt32;%s", dir, buf); +# endif + SetEnvironmentVariableW(L"PATH", buf2); + hLibintlDLL = LoadLibrary(GETTEXT_DLL); +# ifdef GETTEXT_DLL_ALT + if (!hLibintlDLL) + hLibintlDLL = LoadLibrary(GETTEXT_DLL_ALT); +# endif + SetEnvironmentVariableW(L"PATH", buf); + } + free(buf); + free(buf2); + if (!hLibintlDLL) + return 0; + + // Get the addresses of the functions we need. + for (i = 0; libintl_entry[i].name != NULL + && libintl_entry[i].ptr != NULL; ++i) + { + if ((*libintl_entry[i].ptr = GetProcAddress(hLibintlDLL, + libintl_entry[i].name)) == NULL) + { + dyn_libintl_end(); + return 0; + } + } + return 1; +} + + static void +dyn_libintl_end(void) +{ + if (hLibintlDLL) + FreeLibrary(hLibintlDLL); + hLibintlDLL = NULL; + dyn_libintl_gettext = null_libintl_gettext; + dyn_libintl_textdomain = null_libintl_textdomain; + dyn_libintl_bindtextdomain = null_libintl_bindtextdomain; + dyn_libintl_bind_textdomain_codeset = null_libintl_bind_textdomain_codeset; +} + + static char * +null_libintl_gettext(const char *msgid) +{ + return (char *)msgid; +} + + static char * +null_libintl_textdomain(const char * /* domainname */) +{ + return NULL; +} + + static char * +null_libintl_bindtextdomain(const char * /* domainname */, const char * /* dirname */) +{ + return NULL; +} + + static char * +null_libintl_bind_textdomain_codeset(const char * /* domainname */, const char * /* codeset */) +{ + return NULL; +} + +// +// Setup for translating strings. +// + static void +dyn_gettext_load(void) +{ + char szBuff[BUFSIZE]; + DWORD len; + + // Try to locate the runtime files. The path is used to find libintl.dll + // and the vim.mo files. + getRuntimeDir(szBuff); + if (szBuff[0] != 0) + { + len = (DWORD)strlen(szBuff); + if (dyn_libintl_init(szBuff)) + { + strcpy(szBuff + len, "lang"); + + (*dyn_libintl_bindtextdomain)(VIMPACKAGE, szBuff); + (*dyn_libintl_textdomain)(VIMPACKAGE); + } + } +} + + static void +dyn_gettext_free(void) +{ + dyn_libintl_end(); +} + +// +// Use UTF-8 for gettext. Returns previous codeset. +// + static char * +set_gettext_codeset(void) +{ + char *prev = dyn_libintl_bind_textdomain_codeset(VIMPACKAGE, NULL); + prev = _strdup((prev != NULL) ? prev : "char"); + dyn_libintl_bind_textdomain_codeset(VIMPACKAGE, "utf-8"); + + return prev; +} + +// +// Restore previous codeset for gettext. +// + static void +restore_gettext_codeset(char *prev) +{ + dyn_libintl_bind_textdomain_codeset(VIMPACKAGE, prev); + free(prev); +} +#endif // FEAT_GETTEXT + +// +// Global variables +// +UINT g_cRefThisDll = 0; // Reference count of this DLL. +HINSTANCE g_hmodThisDll = NULL; // Handle to this DLL itself. + + +//--------------------------------------------------------------------------- +// DllMain +//--------------------------------------------------------------------------- +extern "C" int APIENTRY +DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /* lpReserved */) +{ + switch (dwReason) + { + case DLL_PROCESS_ATTACH: + // Extension DLL one-time initialization + g_hmodThisDll = hInstance; + break; + + case DLL_PROCESS_DETACH: + break; + } + + return 1; // ok +} + + static void +inc_cRefThisDLL() +{ +#ifdef FEAT_GETTEXT + if (g_cRefThisDll == 0) + dyn_gettext_load(); +#endif + InterlockedIncrement((LPLONG)&g_cRefThisDll); +} + + static void +dec_cRefThisDLL() +{ +#ifdef FEAT_GETTEXT + if (InterlockedDecrement((LPLONG)&g_cRefThisDll) == 0) + dyn_gettext_free(); +#else + InterlockedDecrement((LPLONG)&g_cRefThisDll); +#endif +} + +//--------------------------------------------------------------------------- +// DllCanUnloadNow +//--------------------------------------------------------------------------- + +STDAPI DllCanUnloadNow(void) +{ + return (g_cRefThisDll == 0 ? S_OK : S_FALSE); +} + +STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppvOut) +{ + *ppvOut = NULL; + + if (IsEqualIID(rclsid, CLSID_ShellExtension)) + { + CShellExtClassFactory *pcf = new CShellExtClassFactory; + + return pcf->QueryInterface(riid, ppvOut); + } + + return CLASS_E_CLASSNOTAVAILABLE; +} + +CShellExtClassFactory::CShellExtClassFactory() +{ + m_cRef = 0L; + + inc_cRefThisDLL(); +} + +CShellExtClassFactory::~CShellExtClassFactory() +{ + dec_cRefThisDLL(); +} + +STDMETHODIMP CShellExtClassFactory::QueryInterface(REFIID riid, + LPVOID FAR *ppv) +{ + *ppv = NULL; + + // any interface on this object is the object pointer + + if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IClassFactory)) + { + *ppv = (LPCLASSFACTORY)this; + + AddRef(); + + return NOERROR; + } + + return E_NOINTERFACE; +} + +STDMETHODIMP_(ULONG) CShellExtClassFactory::AddRef() +{ + return InterlockedIncrement((LPLONG)&m_cRef); +} + +STDMETHODIMP_(ULONG) CShellExtClassFactory::Release() +{ + if (InterlockedDecrement((LPLONG)&m_cRef)) + return m_cRef; + + delete this; + + return 0L; +} + +STDMETHODIMP CShellExtClassFactory::CreateInstance(LPUNKNOWN pUnkOuter, + REFIID riid, + LPVOID *ppvObj) +{ + *ppvObj = NULL; + + // Shell extensions typically don't support aggregation (inheritance) + + if (pUnkOuter) + return CLASS_E_NOAGGREGATION; + + // Create the main shell extension object. The shell will then call + // QueryInterface with IID_IShellExtInit--this is how shell extensions are + // initialized. + + LPCSHELLEXT pShellExt = new CShellExt(); // create the CShellExt object + + if (NULL == pShellExt) + return E_OUTOFMEMORY; + + return pShellExt->QueryInterface(riid, ppvObj); +} + + +STDMETHODIMP CShellExtClassFactory::LockServer(BOOL /* fLock */) +{ + return NOERROR; +} + +// *********************** CShellExt ************************* +CShellExt::CShellExt() +{ + m_cRef = 0L; + m_pDataObj = NULL; + + inc_cRefThisDLL(); + + LoadMenuIcon(); +} + +CShellExt::~CShellExt() +{ + if (m_pDataObj) + m_pDataObj->Release(); + + dec_cRefThisDLL(); + + if (m_hVimIconBitmap) + DeleteObject(m_hVimIconBitmap); +} + +STDMETHODIMP CShellExt::QueryInterface(REFIID riid, LPVOID FAR *ppv) +{ + *ppv = NULL; + + if (IsEqualIID(riid, IID_IShellExtInit) || IsEqualIID(riid, IID_IUnknown)) + { + *ppv = (LPSHELLEXTINIT)this; + } + else if (IsEqualIID(riid, IID_IContextMenu)) + { + *ppv = (LPCONTEXTMENU)this; + } + + if (*ppv) + { + AddRef(); + + return NOERROR; + } + + return E_NOINTERFACE; +} + +STDMETHODIMP_(ULONG) CShellExt::AddRef() +{ + return InterlockedIncrement((LPLONG)&m_cRef); +} + +STDMETHODIMP_(ULONG) CShellExt::Release() +{ + + if (InterlockedDecrement((LPLONG)&m_cRef)) + return m_cRef; + + delete this; + + return 0L; +} + + +// +// FUNCTION: CShellExt::Initialize(LPCITEMIDLIST, LPDATAOBJECT, HKEY) +// +// PURPOSE: Called by the shell when initializing a context menu or property +// sheet extension. +// +// PARAMETERS: +// pIDFolder - Specifies the parent folder +// pDataObj - Specifies the set of items selected in that folder. +// hRegKey - Specifies the type of the focused item in the selection. +// +// RETURN VALUE: +// +// NOERROR in all cases. +// +// COMMENTS: Note that at the time this function is called, we don't know +// (or care) what type of shell extension is being initialized. +// It could be a context menu or a property sheet. +// + +STDMETHODIMP CShellExt::Initialize(LPCITEMIDLIST /* pIDFolder */, + LPDATAOBJECT pDataObj, + HKEY /* hRegKey */) +{ + // Initialize can be called more than once + if (m_pDataObj) + m_pDataObj->Release(); + + // duplicate the object pointer and registry handle + + if (pDataObj) + { + m_pDataObj = pDataObj; + pDataObj->AddRef(); + } + + return NOERROR; +} + + +// +// FUNCTION: CShellExt::QueryContextMenu(HMENU, UINT, UINT, UINT, UINT) +// +// PURPOSE: Called by the shell just before the context menu is displayed. +// This is where you add your specific menu items. +// +// PARAMETERS: +// hMenu - Handle to the context menu +// indexMenu - Index of where to begin inserting menu items +// idCmdFirst - Lowest value for new menu ID's +// idCmtLast - Highest value for new menu ID's +// uFlags - Specifies the context of the menu event +// +// RETURN VALUE: +// +// +// COMMENTS: +// + +STDMETHODIMP CShellExt::QueryContextMenu(HMENU hMenu, + UINT indexMenu, + UINT idCmdFirst, + UINT /* idCmdLast */, + UINT /* uFlags */) +{ + UINT idCmd = idCmdFirst; + + hres = m_pDataObj->GetData(&fmte, &medium); + if (medium.hGlobal) + cbFiles = DragQueryFileW((HDROP)medium.hGlobal, (UINT)-1, 0, 0); + + // InsertMenu(hMenu, indexMenu++, MF_SEPARATOR|MF_BYPOSITION, 0, NULL); + + // Initialize m_cntOfHWnd to 0 + m_cntOfHWnd = 0; + + HKEY keyhandle; + bool showExisting = true; + bool showIcons = true; + + // Check whether "Edit with existing Vim" entries are disabled. + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Vim\\Gvim", 0, + KEY_READ, &keyhandle) == ERROR_SUCCESS) + { + if (RegQueryValueEx(keyhandle, "DisableEditWithExisting", 0, NULL, + NULL, NULL) == ERROR_SUCCESS) + showExisting = false; + if (RegQueryValueEx(keyhandle, "DisableContextMenuIcons", 0, NULL, + NULL, NULL) == ERROR_SUCCESS) + showIcons = false; + RegCloseKey(keyhandle); + } + + // Use UTF-8 for gettext. + char *prev = set_gettext_codeset(); + + // Retrieve all the vim instances, unless disabled. + if (showExisting) + EnumWindows(EnumWindowsProc, (LPARAM)this); + + MENUITEMINFOW mii = { sizeof(MENUITEMINFOW) }; + mii.fMask = MIIM_STRING | MIIM_ID; + if (showIcons) + { + mii.fMask |= MIIM_BITMAP; + mii.hbmpItem = m_hVimIconBitmap; + } + + if (cbFiles > 1) + { + mii.wID = idCmd++; + mii.dwTypeData = W(_("Edit with Vim using &tabpages")); + mii.cch = wcslen(mii.dwTypeData); + InsertMenuItemW(hMenu, indexMenu++, TRUE, &mii); + free(mii.dwTypeData); + + mii.wID = idCmd++; + mii.dwTypeData = W(_("Edit with single &Vim")); + mii.cch = wcslen(mii.dwTypeData); + InsertMenuItemW(hMenu, indexMenu++, TRUE, &mii); + free(mii.dwTypeData); + + if (cbFiles <= 4) + { + // Can edit up to 4 files in diff mode + mii.wID = idCmd++; + mii.dwTypeData = W(_("Diff with Vim")); + mii.cch = wcslen(mii.dwTypeData); + InsertMenuItemW(hMenu, indexMenu++, TRUE, &mii); + free(mii.dwTypeData); + m_edit_existing_off = 3; + } + else + m_edit_existing_off = 2; + + } + else + { + mii.wID = idCmd++; + mii.dwTypeData = W(_("Edit with &Vim")); + mii.cch = wcslen(mii.dwTypeData); + InsertMenuItemW(hMenu, indexMenu++, TRUE, &mii); + free(mii.dwTypeData); + m_edit_existing_off = 1; + } + + HMENU hSubMenu = NULL; + if (m_cntOfHWnd > 1) + { + hSubMenu = CreatePopupMenu(); + mii.fMask |= MIIM_SUBMENU; + mii.wID = idCmd; + mii.dwTypeData = W(_("Edit with existing Vim")); + mii.cch = wcslen(mii.dwTypeData); + mii.hSubMenu = hSubMenu; + InsertMenuItemW(hMenu, indexMenu++, TRUE, &mii); + free(mii.dwTypeData); + mii.fMask = mii.fMask & ~MIIM_SUBMENU; + mii.hSubMenu = NULL; + } + // Now display all the vim instances + for (int i = 0; i < m_cntOfHWnd; i++) + { + WCHAR title[BUFSIZE]; + WCHAR temp[BUFSIZE]; + int index; + HMENU hmenu; + + // Obtain window title, continue if can not + if (GetWindowTextW(m_hWnd[i], title, BUFSIZE - 1) == 0) + continue; + // Truncate the title before the path, keep the file name + WCHAR *pos = wcschr(title, L'('); + if (pos != NULL) + { + if (pos > title && pos[-1] == L' ') + --pos; + *pos = 0; + } + // Now concatenate + if (m_cntOfHWnd > 1) + temp[0] = L'\0'; + else + { + WCHAR *s = W(_("Edit with existing Vim - ")); + wcsncpy(temp, s, BUFSIZE - 1); + temp[BUFSIZE - 1] = L'\0'; + free(s); + } + wcsncat(temp, title, BUFSIZE - 1 - wcslen(temp)); + temp[BUFSIZE - 1] = L'\0'; + + mii.wID = idCmd++; + mii.dwTypeData = temp; + mii.cch = wcslen(mii.dwTypeData); + if (m_cntOfHWnd > 1) + { + hmenu = hSubMenu; + index = i; + } + else + { + hmenu = hMenu; + index = indexMenu++; + } + InsertMenuItemW(hmenu, index, TRUE, &mii); + } + // InsertMenu(hMenu, indexMenu++, MF_SEPARATOR|MF_BYPOSITION, 0, NULL); + + // Restore previous codeset. + restore_gettext_codeset(prev); + + // Must return number of menu items we added. + return ResultFromShort(idCmd-idCmdFirst); +} + +// +// FUNCTION: CShellExt::InvokeCommand(LPCMINVOKECOMMANDINFO) +// +// PURPOSE: Called by the shell after the user has selected on of the +// menu items that was added in QueryContextMenu(). +// +// PARAMETERS: +// lpcmi - Pointer to an CMINVOKECOMMANDINFO structure +// +// RETURN VALUE: +// +// +// COMMENTS: +// + +STDMETHODIMP CShellExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi) +{ + HRESULT hr = E_INVALIDARG; + int gvimExtraOptions; + + // If HIWORD(lpcmi->lpVerb) then we have been called programmatically + // and lpVerb is a command that should be invoked. Otherwise, the shell + // has called us, and LOWORD(lpcmi->lpVerb) is the menu ID the user has + // selected. Actually, it's (menu ID - idCmdFirst) from QueryContextMenu(). + if (!HIWORD(lpcmi->lpVerb)) + { + UINT idCmd = LOWORD(lpcmi->lpVerb); + + if (idCmd >= m_edit_existing_off) + { + // Existing with vim instance + hr = PushToWindow(lpcmi->hwnd, + lpcmi->lpDirectory, + lpcmi->lpVerb, + lpcmi->lpParameters, + lpcmi->nShow, + idCmd - m_edit_existing_off); + } + else + { + switch (idCmd) + { + case 0: + gvimExtraOptions = EDIT_WITH_VIM_USE_TABPAGES; + break; + case 1: + gvimExtraOptions = EDIT_WITH_VIM_NO_OPTIONS; + break; + case 2: + gvimExtraOptions = EDIT_WITH_VIM_IN_DIFF_MODE; + break; + default: + // If execution reaches this point we likely have an + // inconsistency between the code that setup the menus + // and this code that determines what the user + // selected. This should be detected and fixed during + // development. + return E_FAIL; + } + + LPCMINVOKECOMMANDINFOEX lpcmiex = (LPCMINVOKECOMMANDINFOEX)lpcmi; + LPCWSTR currentDirectory = lpcmi->cbSize == sizeof(CMINVOKECOMMANDINFOEX) ? lpcmiex->lpDirectoryW : NULL; + + hr = InvokeSingleGvim(lpcmi->hwnd, + currentDirectory, + lpcmi->lpVerb, + lpcmi->lpParameters, + lpcmi->nShow, + gvimExtraOptions); + } + } + return hr; +} + +STDMETHODIMP CShellExt::PushToWindow(HWND /* hParent */, + LPCSTR /* pszWorkingDir */, + LPCSTR /* pszCmd */, + LPCSTR /* pszParam */, + int /* iShowCmd */, + int idHWnd) +{ + HWND hWnd = m_hWnd[idHWnd]; + + // Show and bring vim instance to foreground + if (IsIconic(hWnd) != 0) + ShowWindow(hWnd, SW_RESTORE); + else + ShowWindow(hWnd, SW_SHOW); + SetForegroundWindow(hWnd); + + // Post the selected files to the vim instance + PostMessage(hWnd, WM_DROPFILES, (WPARAM)medium.hGlobal, 0); + + return NOERROR; +} + +STDMETHODIMP CShellExt::GetCommandString(UINT_PTR /* idCmd */, + UINT uFlags, + UINT FAR * /* reserved */, + LPSTR pszName, + UINT cchMax) +{ + // Use UTF-8 for gettext. + char *prev = set_gettext_codeset(); + + WCHAR *s = W(_("Edits the selected file(s) with Vim")); + if (uFlags == GCS_HELPTEXTW && cchMax > wcslen(s)) + wcscpy((WCHAR *)pszName, s); + free(s); + + // Restore previous codeset. + restore_gettext_codeset(prev); + + return NOERROR; +} + +BOOL CALLBACK CShellExt::EnumWindowsProc(HWND hWnd, LPARAM lParam) +{ + char temp[BUFSIZE]; + + // First do a bunch of check + // No invisible window + if (!IsWindowVisible(hWnd)) + return TRUE; + // No child window ??? + // if (GetParent(hWnd)) return TRUE; + // Class name should be Vim, if failed to get class name, return + if (GetClassName(hWnd, temp, sizeof(temp)) == 0) + return TRUE; + // Compare class name to that of vim, if not, return + if (_strnicmp(temp, "vim", sizeof("vim")) != 0) + return TRUE; + // First check if the number of vim instance exceeds MAX_HWND + CShellExt *cs = (CShellExt*) lParam; + if (cs->m_cntOfHWnd >= MAX_HWND) + return FALSE; // stop enumeration + // Now we get the vim window, put it into some kind of array + cs->m_hWnd[cs->m_cntOfHWnd] = hWnd; + cs->m_cntOfHWnd ++; + + return TRUE; // continue enumeration (otherwise this would be false) +} + +BOOL CShellExt::LoadMenuIcon() +{ + char vimExeFile[BUFSIZE]; + getGvimName(vimExeFile, 1); + if (vimExeFile[0] == '\0') + return FALSE; + HICON hVimIcon; + if (ExtractIconEx(vimExeFile, 0, NULL, &hVimIcon, 1) == 0) + return FALSE; + m_hVimIconBitmap = IconToBitmap(hVimIcon, + GetSysColorBrush(COLOR_MENU), + GetSystemMetrics(SM_CXSMICON), + GetSystemMetrics(SM_CYSMICON)); + return TRUE; +} + + static char * +searchpath(char *name) +{ + static char widename[2 * BUFSIZE]; + static char location[2 * BUFSIZE + 2]; + + // There appears to be a bug in FindExecutableA() on Windows NT. + // Use FindExecutableW() instead... + MultiByteToWideChar(CP_ACP, 0, (LPCSTR)name, -1, + (LPWSTR)widename, BUFSIZE); + if (FindExecutableW((LPCWSTR)widename, (LPCWSTR)"", + (LPWSTR)location) > (HINSTANCE)32) + { + WideCharToMultiByte(CP_ACP, 0, (LPWSTR)location, -1, + (LPSTR)widename, 2 * BUFSIZE, NULL, NULL); + return widename; + } + return (char *)""; +} + + +STDMETHODIMP CShellExt::InvokeSingleGvim(HWND hParent, + LPCWSTR workingDir, + LPCSTR /* pszCmd */, + LPCSTR /* pszParam */, + int /* iShowCmd */, + int gvimExtraOptions) +{ + wchar_t m_szFileUserClickedOn[BUFSIZE]; + wchar_t *cmdStrW; + size_t cmdlen; + size_t len; + UINT i; + + cmdlen = BUFSIZE; + cmdStrW = (wchar_t *) malloc(cmdlen * sizeof(wchar_t)); + if (cmdStrW == NULL) + return E_FAIL; + getGvimInvocationW(cmdStrW); + + if (gvimExtraOptions == EDIT_WITH_VIM_IN_DIFF_MODE) + wcscat(cmdStrW, L" -d"); + else if (gvimExtraOptions == EDIT_WITH_VIM_USE_TABPAGES) + wcscat(cmdStrW, L" -p"); + for (i = 0; i < cbFiles; i++) + { + DragQueryFileW((HDROP)medium.hGlobal, + i, + m_szFileUserClickedOn, + sizeof(m_szFileUserClickedOn)); + + len = wcslen(cmdStrW) + wcslen(m_szFileUserClickedOn) + 4; + if (len > cmdlen) + { + cmdlen = len + BUFSIZE; + wchar_t *cmdStrW_new = (wchar_t *)realloc(cmdStrW, cmdlen * sizeof(wchar_t)); + if (cmdStrW_new == NULL) + { + free(cmdStrW); + return E_FAIL; + } + cmdStrW = cmdStrW_new; + } + wcscat(cmdStrW, L" \""); + wcscat(cmdStrW, m_szFileUserClickedOn); + wcscat(cmdStrW, L"\""); + } + + STARTUPINFOW si; + PROCESS_INFORMATION pi; + + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + + // Start the child process. + if (!CreateProcessW(NULL, // No module name (use command line). + cmdStrW, // Command line. + NULL, // Process handle not inheritable. + NULL, // Thread handle not inheritable. + FALSE, // Set handle inheritance to FALSE. + 0, // No creation flags. + NULL, // Use parent's environment block. + workingDir, // Use parent's starting directory. + &si, // Pointer to STARTUPINFO structure. + &pi) // Pointer to PROCESS_INFORMATION structure. + ) + { + // Use UTF-8 for gettext. + char *prev = set_gettext_codeset(); + + WCHAR *msg = W(_("Error creating process: Check if gvim is in your path!")); + WCHAR *title = W(_("gvimext.dll error")); + + MessageBoxW(hParent, msg, title, MB_OK); + + free(msg); + free(title); + + // Restore previous codeset. + restore_gettext_codeset(prev); + } + else + { + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } + free(cmdStrW); + + return NOERROR; +} diff --git a/src/GvimExt/gvimext.def b/src/GvimExt/gvimext.def new file mode 100644 index 0000000..e6d66f4 --- /dev/null +++ b/src/GvimExt/gvimext.def @@ -0,0 +1,8 @@ +;gvimdef.def : Declares the module parameters for the DLL. + +LIBRARY gvimext +; DESCRIPTION 'Vim Shell Extension' + +EXPORTS + DllCanUnloadNow private + DllGetClassObject private diff --git a/src/GvimExt/gvimext.h b/src/GvimExt/gvimext.h new file mode 100644 index 0000000..e7bcfb8 --- /dev/null +++ b/src/GvimExt/gvimext.h @@ -0,0 +1,167 @@ +/* vi:set ts=8 sts=4 sw=4: + * + * VIM - Vi IMproved gvimext by Tianmiao Hu + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + */ + +/* + * If you have any questions or any suggestions concerning gvimext, please + * contact Tianmiao Hu: tianmiao@acm.org. + */ + +#if !defined(AFX_STDAFX_H__3389658B_AD83_11D3_9C1E_0090278BBD99__INCLUDED_) +#define AFX_STDAFX_H__3389658B_AD83_11D3_9C1E_0090278BBD99__INCLUDED_ + +#if defined(_MSC_VER) && _MSC_VER > 1000 +#pragma once +#endif + +// Insert your headers here +// #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers + +//-------------------------------------------------------------- +// common user interface routines +// +// +//-------------------------------------------------------------- + +#ifndef STRICT +# define STRICT +#endif + +#define INC_OLE2 // MS-Windows, get ole2 from windows.h + +/* Visual Studio 2005 has 'deprecated' many of the standard CRT functions */ +#if defined(_MSC_VER) && _MSC_VER >= 1400 +# define _CRT_SECURE_NO_DEPRECATE +# define _CRT_NONSTDC_NO_DEPRECATE +#endif + +#include <windows.h> +#include <windowsx.h> +#include <shlobj.h> +#include <wchar.h> + +#define ResultFromShort(i) ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS, 0, (USHORT)(i))) + +// Initialize GUIDs (should be done only and at-least once per DLL/EXE) +// +#pragma data_seg(".text") +#define INITGUID +#include <initguid.h> + +// +// The class ID of this Shell extension class. +// +// class id: {51EEE242-AD87-11d3-9C1E-0090278BBD99} +// +// +// NOTE!!! If you use this shell extension as a starting point, +// you MUST change the GUID below. Simply run UUIDGEN.EXE +// to generate a new GUID. +// + +// {51EEE242-AD87-11d3-9C1E-0090278BBD99} +// static const GUID <<name>> = +// { 0x51eee242, 0xad87, 0x11d3, { 0x9c, 0x1e, 0x0, 0x90, 0x27, 0x8b, 0xbd, 0x99 } }; +// +// + +// {51EEE242-AD87-11d3-9C1E-0090278BBD99} +// IMPLEMENT_OLECREATE(<<class>>, <<external_name>>, +// 0x51eee242, 0xad87, 0x11d3, 0x9c, 0x1e, 0x0, 0x90, 0x27, 0x8b, 0xbd, 0x99); +// + +// {51EEE242-AD87-11d3-9C1E-0090278BBD99} -- this is the registry format +DEFINE_GUID(CLSID_ShellExtension, 0x51eee242, 0xad87, 0x11d3, 0x9c, 0x1e, 0x0, 0x90, 0x27, 0x8b, 0xbd, 0x99); + +// this class factory object creates context menu handlers for windows 32 shell +class CShellExtClassFactory : public IClassFactory +{ +protected: + ULONG m_cRef; + +public: + CShellExtClassFactory(); + ~CShellExtClassFactory(); + + //IUnknown members + STDMETHODIMP QueryInterface(REFIID, LPVOID FAR *); + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + + //IClassFactory members + STDMETHODIMP CreateInstance(LPUNKNOWN, REFIID, LPVOID FAR *); + STDMETHODIMP LockServer(BOOL); +}; +typedef CShellExtClassFactory *LPCSHELLEXTCLASSFACTORY; +#define MAX_HWND 100 + +// this is the actual OLE Shell context menu handler +class CShellExt : public IContextMenu, + IShellExtInit +{ +private: + BOOL LoadMenuIcon(); + +protected: + ULONG m_cRef; + LPDATAOBJECT m_pDataObj; + UINT m_edit_existing_off; + HBITMAP m_hVimIconBitmap; + + // For some reason, this callback must be static + static BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam); + + STDMETHODIMP PushToWindow(HWND hParent, + LPCSTR pszWorkingDir, + LPCSTR pszCmd, + LPCSTR pszParam, + int iShowCmd, + int idHWnd); + + STDMETHODIMP InvokeSingleGvim(HWND hParent, + LPCWSTR workingDir, + LPCSTR pszCmd, + LPCSTR pszParam, + int iShowCmd, + int gvimExtraOptions); + +public: + int m_cntOfHWnd; + HWND m_hWnd[MAX_HWND]; + CShellExt(); + ~CShellExt(); + + //IUnknown members + STDMETHODIMP QueryInterface(REFIID, LPVOID FAR *); + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + + //IShell members + STDMETHODIMP QueryContextMenu(HMENU hMenu, + UINT indexMenu, + UINT idCmdFirst, + UINT idCmdLast, + UINT uFlags); + + STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi); + + STDMETHODIMP GetCommandString(UINT_PTR idCmd, + UINT uFlags, + UINT FAR *reserved, + LPSTR pszName, + UINT cchMax); + + //IShellExtInit methods + STDMETHODIMP Initialize(LPCITEMIDLIST pIDFolder, + LPDATAOBJECT pDataObj, + HKEY hKeyID); +}; + +typedef CShellExt *LPCSHELLEXT; +#pragma data_seg() + +#endif diff --git a/src/GvimExt/gvimext.inf b/src/GvimExt/gvimext.inf new file mode 100644 index 0000000..8b45bb1 --- /dev/null +++ b/src/GvimExt/gvimext.inf @@ -0,0 +1,22 @@ +[Version] +Signature="$Windows NT$"" +Provider="Tianmiao" + +[Manufacture] + +[DefaultInstall] +AddReg=ThisDll.Add.Reg + +[DefaultUninstall] +DelReg=ThisDLL.Add.Reg + +[ThisDll.Add.Reg] +HKCR,CLSID\{51EEE242-AD87-11d3-9C1E-0090278BBD99} +HKCR,CLSID\{51EEE242-AD87-11d3-9C1E-0090278BBD99}\InProcServer32 +HKCR,*\shellex\ContextMenuHandlers\gvim +HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved",{51EEE242-AD87-11d3-9C1E-0090278BBD99} +HKLM,Software\Vim\Gvim +HKLM,"Software\Microsoft\Windows\CurrentVersion\Uninstall\Vim 6.0" + +[Strings] +ThisDll="gvimext.dll" diff --git a/src/GvimExt/gvimext.rc b/src/GvimExt/gvimext.rc new file mode 100644 index 0000000..10476da --- /dev/null +++ b/src/GvimExt/gvimext.rc @@ -0,0 +1,109 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winresrc.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x3L +#else + FILEFLAGS 0x2L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", "Developed using COM architecture!\0" + VALUE "CompanyName", "Tianmiao Hu's Developer Studio\0" + VALUE "FileDescription", "A small project for the context menu of gvim!\0" + VALUE "FileVersion", "1, 0, 0, 1\0" + VALUE "InternalName", "gvim right-click menu extension\0" + VALUE "LegalCopyright", "Copyright © 1999 Tianmiao Hu\0" + VALUE "LegalTrademarks", "Tianmiao Hu's Gvim Context Menu Extension\0" + VALUE "OriginalFilename", "gvimext.dll\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "Tianmiao Hu's gvimext context menu extension\0" + VALUE "ProductVersion", "1, 0, 0, 1\0" + VALUE "SpecialBuild", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // !_MAC + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/src/GvimExt/gvimext_ming.def b/src/GvimExt/gvimext_ming.def new file mode 100644 index 0000000..b96e1d3 --- /dev/null +++ b/src/GvimExt/gvimext_ming.def @@ -0,0 +1,10 @@ +;gvimdef_ming.def : Declares the module parameters for the DLL. +;The mingw environment doesn't know anything about private declarations +;Hence this is the same file as gvimext.def with private removed + +LIBRARY gvimext +; DESCRIPTION 'Vim Shell Extension build with MinGW' + +EXPORTS + DllCanUnloadNow = DllCanUnloadNow@0 + DllGetClassObject = DllGetClassObject@12 diff --git a/src/GvimExt/gvimext_ming.rc b/src/GvimExt/gvimext_ming.rc new file mode 100644 index 0000000..6c69854 --- /dev/null +++ b/src/GvimExt/gvimext_ming.rc @@ -0,0 +1,45 @@ +#include <windows.h> +#define xstr(x) str(x) +#define str(x) #x +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x3L +#else + FILEFLAGS 0x2L +#endif + FILEOS 0x4L + FILETYPE VFT_DLL + FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "040904b0" + { + VALUE "Comments", "Developed using COM architecture!\0" + VALUE "CompanyName", "Tianmiao Hu's Developer Studio\0" + VALUE "FileDescription", "A small project for the context menu of gvim!\0" + VALUE "FileVersion", "1, 0, 0, 1\0" + VALUE "InternalName", "gvim right-click menu extension\0" + VALUE "LegalCopyright", "Copyright © 1999 Tianmiao Hu\0" + VALUE "LegalTrademarks", "Tianmiao Hu's Gvim Context Menu Extension\0" + VALUE "OriginalFilename", "gvimext.dll\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "Tianmiao Hu's gvimext context menu extension\0" + VALUE "ProductVersion", "1, 0, 0, 1\0" + +#if defined(__GNUC__) + VALUE "SpecialBuild", "Build With " "MingW " xstr(__GNUC__) "." xstr(__GNUC_MINOR__) "." xstr(__GNUC_PATCHLEVEL__) " on " __DATE__ " " __TIME__ "\0" +#else + VALUE "SpecialBuild", "Unknown compiler\0" + +#endif + } + } + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x409, 1200 + } +} diff --git a/src/GvimExt/resource.h b/src/GvimExt/resource.h new file mode 100644 index 0000000..8ddc823 --- /dev/null +++ b/src/GvimExt/resource.h @@ -0,0 +1,15 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by gvimext.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/src/GvimExt/uninst.bat b/src/GvimExt/uninst.bat new file mode 100644 index 0000000..71d14f6 --- /dev/null +++ b/src/GvimExt/uninst.bat @@ -0,0 +1 @@ +rundll32.exe setupapi,InstallHinfSection DefaultUninstall 128 %1 |