Merge branch 'msys2'
[git-cheetah.git] / explorer / menu.c
blobf13882d648e4594c52b72653085bf3ff705e5439
1 #include "../common/cache.h"
3 #include <shlobj.h>
4 #include <tchar.h>
5 #include "../common/menuengine.h"
6 #include "../common/cheetahmenu.h"
7 #include "menu.h"
8 #include "ext.h"
9 #include "../common/debug.h"
10 #include "../common/systeminfo.h"
11 #include "../common/exec.h"
13 #define LONGEST_MENU_ITEM 40
16 * Windows-specific Cheetah menu functions
18 struct windows_menu_data {
19 HMENU menu;
20 UINT index;
21 UINT first;
22 UINT last;
25 void reset_platform(void *platform)
27 /* On Windows, we don't do anything to reset the menu */
31 * menu_item_builder to build a Windows-specific menu separator
33 * Always returns FALSE so the menu engine does not track this item
35 BOOL build_separator(struct git_data *data, const struct menu_item *item,
36 void *platform)
38 struct windows_menu_data *windows_menu = platform;
39 InsertMenu(windows_menu->menu, windows_menu->index,
40 MF_SEPARATOR | MF_BYPOSITION, 0, "");
41 windows_menu->index++;
43 return FALSE;
47 * menu_item_builder to build a simple menu item
49 * Explorer's context menu are limited in the number of comands
50 * that they can use, so build_item would:
51 * - do nothing if that limit is reached and return FALSE to
52 * instruct the menu engine to not track this item
53 * - create item and return TRUE, so the item can be handled later
55 BOOL build_item(struct git_data *data, const struct menu_item *item,
56 void *platform)
58 MENUITEMINFO mii;
60 struct windows_menu_data *windows_menu = platform;
61 if (windows_menu->last < windows_menu->first + next_active_item)
62 return FALSE;
64 memset(&mii, 0, sizeof(MENUITEMINFO));
65 mii.cbSize = sizeof(MENUITEMINFO);
66 mii.fMask = MIIM_STRING | MIIM_ID | MIIM_DATA;
67 mii.wID = windows_menu->first + next_active_item;
68 mii.dwItemData = (ULONG_PTR) hInst;
69 mii.dwTypeData = item->string;
70 InsertMenuItem(windows_menu->menu, windows_menu->index, TRUE, &mii);
72 if (item->flags & MI_CHECKED)
73 CheckMenuItem(windows_menu->menu, windows_menu->index,
74 MF_BYPOSITION | MF_CHECKED);
76 windows_menu->index++;
77 return TRUE;
80 void *start_submenu(struct git_data *this_, const struct menu_item *item,
81 void *platform)
83 struct windows_menu_data *parent_menu = platform;
84 struct windows_menu_data *submenu =
85 xmalloc(sizeof(struct windows_menu_data));
86 MENUITEMINFO mii;
88 submenu->menu = CreateMenu();
90 memset(&mii, 0, sizeof(MENUITEMINFO));
91 mii.cbSize = sizeof(MENUITEMINFO);
92 mii.fMask = MIIM_STRING | MIIM_DATA | MIIM_SUBMENU;
93 mii.dwItemData = (ULONG_PTR) hInst;
94 mii.dwTypeData = item->string;
95 mii.hSubMenu = submenu->menu;
96 InsertMenuItem(parent_menu->menu, parent_menu->index, TRUE, &mii);
98 parent_menu->index++;
100 submenu->index = 0;
101 submenu->first = parent_menu->first;
102 submenu->last = parent_menu->last;
104 return submenu;
107 void end_submenu(void *parent, void *submenu)
109 free(submenu);
112 BOOL menu_exists(HMENU menu)
114 int count, i;
115 MENUITEMINFO mii;
116 memset(&mii, 0, sizeof(MENUITEMINFO));
117 mii.cbSize = sizeof(MENUITEMINFO);
118 mii.fMask = MIIM_DATA;
119 mii.dwTypeData = NULL;
121 count = GetMenuItemCount(menu);
122 for (i = 0; i < count; ++i)
124 GetMenuItemInfo(menu, i, TRUE, &mii);
125 if (mii.dwItemData == (ULONG_PTR) hInst)
127 return TRUE;
131 return FALSE;
135 * These are the functions for handling the context menu.
138 STDMETHODIMP query_context_menu(void *p, HMENU menu,
139 UINT index, UINT first_command,
140 UINT last_command, UINT flags)
142 struct git_menu *this_menu = p;
143 struct git_data *this_ = this_menu->git_data;
144 struct windows_menu_data windows_menu =
145 { menu, index, first_command, last_command };
147 if (flags & CMF_DEFAULTONLY)
148 return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 0);
150 if (menu_exists(menu))
151 return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 0);
153 build_cheetah_menu(this_, &windows_menu);
155 return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL,
156 next_active_item);
160 * Perform a couple of transformations, such that a directory
161 * C:\Program Files\Bunch of stuff\in\A dir
162 * becomes
163 * /C/Program\ Files/Bunch\ of\ stuff/in/A\ dir
165 * Assumes path is initially a correctly formed Windows-style path.
166 * Returns a new string.
168 static char *convert_directory_format(const char *path)
170 /* assuming that each character has to be escaped,
171 allocate twice as much memory */
172 char *converted = (char *)calloc(2 * strlen(path) + 1, sizeof(char));
173 char *dst = converted;
175 /* Transform:
176 * "\" -> "/"
177 * chars, special to bash, are escaped with "\"
179 for (; *path; path++)
181 switch (*path)
183 case ' ':
184 case '(':
185 case ')':
186 case ';':
187 case '&':
188 case '`':
189 case '$':
190 case '\'':
191 *(dst++) = '\\';
192 *(dst++) = *path;
193 break;
194 case '\\':
195 *(dst++) = '/';
196 break;
197 default:
198 *(dst++) = *path;
199 break;
202 *dst = '\0';
204 /* X: -> /X */
205 converted[1] = converted[0];
206 converted[0] = '/';
208 return converted;
211 static void free_platform_argv(void *data)
213 free(data);
216 #ifndef _WIN64
217 #define SYSTEMDIR "system32"
218 #else
219 #define SYSTEMDIR "syswow64"
220 #endif
222 static const char *get_cmd(void)
224 static struct strbuf buf = STRBUF_INIT;
226 if (!buf.len)
227 strbuf_addf(&buf, "%s\\" SYSTEMDIR "\\cmd.exe",
228 getenv("WINDIR"));
230 return buf.buf;
233 static void *create_bash_argv(char *wd)
235 /* start is required because exec_program does not create a window */
236 static const char *bash_argv[] = { NULL, "/c", "start",
237 "sh", "-c", NULL, NULL };
238 static const char *command = "cd %s && sh -l -i";
239 void *argv = xmalloc(sizeof(bash_argv));
240 struct strbuf shell_cmd = STRBUF_INIT;
241 char *converted = convert_directory_format(wd);
243 /* strbuf_addf allocates only 64 bytes, so we have to grow it manually */
244 strbuf_grow(&shell_cmd, strlen(converted) + strlen(command) + 1);
245 strbuf_addf(&shell_cmd, command, converted);
246 free(converted);
248 bash_argv[0] = get_cmd();
249 bash_argv[5] = shell_cmd.buf;
251 memcpy(argv, bash_argv, sizeof(bash_argv));
253 /* start the cmd on a system drive, so it does not fail on UNC */
254 strcpy(wd, getenv("SYSTEMDRIVE"));
256 return argv;
259 static void free_bash_argv(void *data)
261 void **argv = (void **)data;
262 free(argv[5]);
263 free(data);
266 const char **menu_get_platform_argv(menu_commands cmd, void *data,
267 free_func_t *free_argv, void **argv_data)
269 char *wd = data;
270 const char **argv;
271 const char *history_argv[] = { "sh", "--login", "-i",
272 "/bin/gitk", "HEAD", "--", NULL, NULL };
273 *free_argv = NULL;
274 *argv_data = NULL;
276 switch(cmd)
278 case MENU_HISTORY:
279 history_argv[6] = wd;
281 argv = xmalloc(sizeof(history_argv));
282 memcpy(argv, history_argv, sizeof(history_argv));
283 *free_argv = free_platform_argv;
285 break;
287 case MENU_BASH:
289 argv = create_bash_argv(wd);
290 *free_argv = free_bash_argv;
292 break;
294 default:
295 return NULL;
298 *argv_data = argv;
300 return argv;
303 STDMETHODIMP invoke_command(void *p,
304 LPCMINVOKECOMMANDINFO info)
306 struct git_menu *this_menu = p;
307 struct git_data *this_ = this_menu->git_data;
308 UINT id = LOWORD(info->lpVerb);
310 if (HIWORD(info->lpVerb))
311 return E_INVALIDARG;
313 handle_menu_item(this_, id);
314 return S_OK;
317 STDMETHODIMP get_command_string(void *p, UINT id,
318 UINT flags, UINT *reserved,
319 LPSTR name, UINT size)
321 const char *text;
323 if (!(flags & GCS_HELPTEXT))
324 return E_INVALIDARG;
326 text = get_menu_item_text(id);
327 if (!text)
328 return E_INVALIDARG;
330 if (flags & GCS_UNICODE) {
331 size_t len = strlen(text) + 1;
332 LPWSTR tw = xmalloc(len * sizeof(wchar_t));
333 /* need to convert terminating NULL as well */
334 mbstowcs(tw, text, len);
335 lstrcpynW((LPWSTR)name, tw, size);
336 free(tw);
337 } else
338 lstrcpynA(name, text, size);
340 return S_OK;
343 DEFINE_STANDARD_METHODS(git_menu)
345 struct git_menu_virtual_table git_menu_virtual_table = {
346 query_interface_git_menu,
347 add_ref_git_menu,
348 release_git_menu,
349 query_context_menu,
350 invoke_command,
351 get_command_string