Add support to start 'Git Bash' with UNC paths
[git-cheetah/kirill.git] / explorer / menu.c
blob31b451f4d45ef090e87644456ec90a875e8969ab
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 struct windows_menu_data *windows_menu = platform;
59 if (windows_menu->last < windows_menu->first + next_active_item)
60 return FALSE;
62 InsertMenu(windows_menu->menu, windows_menu->index,
63 MF_STRING | MF_BYPOSITION,
64 windows_menu->first + next_active_item,
65 item->string);
67 if (item->flags & MI_CHECKED)
68 CheckMenuItem(windows_menu->menu, windows_menu->index,
69 MF_BYPOSITION | MF_CHECKED);
71 windows_menu->index++;
72 return TRUE;
75 void *start_submenu(struct git_data *this_, const struct menu_item *item,
76 void *platform)
78 struct windows_menu_data *parent_menu = platform;
79 struct windows_menu_data *submenu =
80 xmalloc(sizeof(struct windows_menu_data));
81 submenu->menu = CreateMenu();
82 InsertMenu(parent_menu->menu, parent_menu->index,
83 MF_POPUP | MF_BYPOSITION, (UINT_PTR)(submenu->menu),
84 item->string);
85 parent_menu->index++;
87 submenu->index = 0;
88 submenu->first = parent_menu->first;
89 submenu->last = parent_menu->last;
91 return submenu;
94 void end_submenu(void *parent, void *submenu)
96 free(submenu);
100 * These are the functions for handling the context menu.
103 inline STDMETHODIMP query_context_menu(void *p, HMENU menu,
104 UINT index, UINT first_command,
105 UINT last_command, UINT flags)
107 struct git_menu *this_menu = p;
108 struct git_data *this_ = this_menu->git_data;
109 struct windows_menu_data windows_menu =
110 { menu, index, first_command, last_command };
112 if (flags & CMF_DEFAULTONLY)
113 return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 0);
115 build_cheetah_menu(this_, &windows_menu);
117 return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL,
118 next_active_item);
122 * Perform a couple of transformations, such that a directory
123 * C:\Program Files\Bunch of stuff\in\A dir
124 * becomes
125 * /C/Program\ Files/Bunch\ of\ stuff/in/A\ dir
127 * Assumes path is initially a correctly formed Windows-style path.
128 * Returns a new string.
130 static char *convert_directory_format(const char *path)
132 int i;
133 int size_incr = 0;
134 char *converted;
135 char *dst;
137 /* Figure out how much extra space we need to escape spaces */
138 for (i = 0; i < MAX_PATH && path[i] != '\0'; ++i)
139 if (path[i] == ' ')
140 size_incr++;
142 converted = (char *)calloc(size_incr + i + 1, sizeof(char));
143 dst = converted;
145 /* Transform:
146 * " " -> "\ "
147 * "\" -> "/"
149 for (i = 0; i < MAX_PATH && path[i] != '\0'; ++i)
151 switch (path[i])
153 case ' ':
154 *(dst++) = '\\';
155 *(dst++) = ' ';
156 break;
157 case '\\':
158 *(dst++) = '/';
159 break;
160 default:
161 *(dst++) = path[i];
162 break;
165 *dst = '\0';
167 /* X: -> /X */
168 converted[1] = converted[0];
169 converted[0] = '/';
171 return converted;
174 static void free_platform_argv(void *data)
176 free(data);
179 #ifndef _WIN64
180 #define SYSTEMDIR "system32"
181 #else
182 #define SYSTEMDIR "syswow64"
183 #endif
185 __attribute__((unused))
186 static const char *get_cmd(void)
188 static struct strbuf buf = STRBUF_INIT;
190 if (!buf.len)
191 strbuf_addf(&buf, "%s\\" SYSTEMDIR "\\cmd.exe",
192 getenv("WINDIR"));
194 return buf.buf;
197 static void *create_bash_argv(char *wd)
199 /* start is required because exec_program does not create a window */
200 static const char *bash_argv[] = { NULL, "/c", "start",
201 "sh", "-c", NULL, NULL };
202 static const char *command = "cd %s && sh -l -i";
203 void *argv = xmalloc(sizeof(bash_argv));
204 struct strbuf shell_cmd = STRBUF_INIT;
205 char *converted = convert_directory_format(wd);
207 /* strbuf_addf allocates only 64 bytes, so we have to grow it manually */
208 strbuf_grow(&shell_cmd, strlen(converted) + strlen(command) + 1);
209 strbuf_addf(&shell_cmd, command, converted);
210 free(converted);
212 bash_argv[0] = get_cmd();
213 bash_argv[5] = shell_cmd.buf;
215 memcpy(argv, bash_argv, sizeof(bash_argv));
217 /* start the cmd on a system drive, so it does not fail on UNC */
218 strcpy(wd, getenv("SYSTEMDRIVE"));
220 return argv;
223 static void free_bash_argv(void *data)
225 void **argv = (void **)data;
226 free(argv[5]);
227 free(data);
230 const char **menu_get_platform_argv(menu_commands cmd, void *data,
231 free_func_t *free_argv, void **argv_data)
233 char *wd = data;
234 const char **argv;
235 const char *history_argv[] = { "sh", "--login", "-i",
236 "/bin/gitk", "HEAD", "--", NULL, NULL };
237 *free_argv = NULL;
238 *argv_data = NULL;
240 switch(cmd)
242 case MENU_HISTORY:
243 history_argv[6] = wd;
245 argv = xmalloc(sizeof(history_argv));
246 memcpy(argv, history_argv, sizeof(history_argv));
247 *free_argv = free_platform_argv;
249 break;
251 case MENU_BASH:
253 argv = create_bash_argv(wd);
254 *free_argv = free_bash_argv;
256 break;
258 default:
259 return NULL;
262 *argv_data = argv;
264 return argv;
267 inline STDMETHODIMP invoke_command(void *p,
268 LPCMINVOKECOMMANDINFO info)
270 struct git_menu *this_menu = p;
271 struct git_data *this_ = this_menu->git_data;
272 UINT id = LOWORD(info->lpVerb);
274 if (HIWORD(info->lpVerb))
275 return E_INVALIDARG;
277 handle_menu_item(this_, id);
278 return S_OK;
281 inline STDMETHODIMP get_command_string(void *p, UINT id,
282 UINT flags, UINT *reserved,
283 LPSTR name, UINT size)
285 const char *text;
287 if (!(flags & GCS_HELPTEXT))
288 return E_INVALIDARG;
290 text = get_menu_item_text(id);
291 if (!text)
292 return E_INVALIDARG;
294 if (flags & GCS_UNICODE) {
295 size_t len = strlen(text) + 1;
296 LPWSTR tw = xmalloc(len * sizeof(wchar_t));
297 /* need to convert terminating NULL as well */
298 mbstowcs(tw, text, len);
299 lstrcpynW((LPWSTR)name, tw, size);
300 free(tw);
301 } else
302 lstrcpynA(name, text, size);
304 return S_OK;
307 DEFINE_STANDARD_METHODS(git_menu)
309 struct git_menu_virtual_table git_menu_virtual_table = {
310 query_interface_git_menu,
311 add_ref_git_menu,
312 release_git_menu,
313 query_context_menu,
314 invoke_command,
315 get_command_string