Save encoding in session as text
[geany-mirror.git] / src / win32.c
blobe3cf1aff4a88bab9b3fde90abd94ce3b70861d55
1 /*
2 * win32.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2005-2012 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
5 * Copyright 2006-2012 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 * Special functions for the win32 platform, to provide native dialogs.
26 #include "geany.h"
28 #ifdef G_OS_WIN32
30 #define VC_EXTRALEAN
31 #define WIN32_LEAN_AND_MEAN
32 #include <windows.h>
33 #include <commdlg.h>
34 #include <shlobj.h>
35 #include <io.h>
36 #include <fcntl.h>
38 #include <string.h>
39 #include <ctype.h>
40 #include <math.h>
41 #include <stdlib.h>
43 #include <gdk/gdkwin32.h>
45 #include "win32.h"
47 #include "document.h"
48 #include "support.h"
49 #include "utils.h"
50 #include "ui_utils.h"
51 #include "sciwrappers.h"
52 #include "dialogs.h"
53 #include "filetypes.h"
54 #include "project.h"
55 #include "editor.h"
57 #define BUFSIZE 4096
58 #define CMDSIZE 32768
60 struct _geany_win32_spawn
62 HANDLE hChildStdinRd;
63 HANDLE hChildStdinWr;
64 HANDLE hChildStdoutRd;
65 HANDLE hChildStdoutWr;
66 HANDLE hChildStderrRd;
67 HANDLE hChildStderrWr;
68 HANDLE hInputFile;
69 HANDLE hStdout;
70 HANDLE hStderr;
71 HANDLE processId;
72 DWORD dwExitCode;
74 typedef struct _geany_win32_spawn geany_win32_spawn;
76 static gboolean GetContentFromHandle(HANDLE hFile, gchar **content, GError **error);
77 static HANDLE GetTempFileHandle(GError **error);
78 static gboolean CreateChildProcess(geany_win32_spawn *gw_spawn, TCHAR *szCmdline,
79 const TCHAR *dir, GError **error);
80 static VOID ReadFromPipe(HANDLE hRead, HANDLE hWrite, HANDLE hFile, GError **error);
83 static wchar_t *get_file_filters(void)
85 gchar *string;
86 guint i, j, len;
87 static wchar_t title[4096];
88 GString *str = g_string_sized_new(100);
89 GString *all_patterns = g_string_sized_new(100);
90 GSList *node;
91 gchar *tmp;
93 /* create meta file filter "All files" */
94 g_string_append_printf(str, "%s\t*\t", _("All files"));
95 /* create meta file filter "All Source" (skip GEANY_FILETYPES_NONE) */
96 for (i = GEANY_FILETYPES_NONE + 1; i < filetypes_array->len; i++)
98 for (j = 0; filetypes[i]->pattern[j] != NULL; j++)
100 g_string_append(all_patterns, filetypes[i]->pattern[j]);
101 g_string_append_c(all_patterns, ';');
104 g_string_append_printf(str, "%s\t%s\t", _("All Source"), all_patterns->str);
105 g_string_free(all_patterns, TRUE);
106 /* add 'usual' filetypes */
107 foreach_slist(node, filetypes_by_title)
109 GeanyFiletype *ft = node->data;
111 if (G_UNLIKELY(ft->id == GEANY_FILETYPES_NONE))
112 continue;
113 tmp = g_strjoinv(";", ft->pattern);
114 g_string_append_printf(str, "%s\t%s\t", ft->title, tmp);
115 g_free(tmp);
117 g_string_append_c(str, '\t'); /* the final \0 byte to mark the end of the string */
118 string = str->str;
119 g_string_free(str, FALSE);
121 /* replace all "\t"s by \0 */
122 len = strlen(string);
123 g_strdelimit(string, "\t", '\0');
124 g_assert(string[len - 1] == 0x0);
125 MultiByteToWideChar(CP_UTF8, 0, string, len, title, sizeof(title));
126 g_free(string);
128 return title;
132 static wchar_t *get_file_filter_all_files(void)
134 guint len;
135 static wchar_t title[4096];
136 gchar *filter;
138 /* create meta file filter "All files" */
139 filter = g_strdup_printf("%s\t*\t", _("All files"));
141 len = strlen(filter);
142 g_strdelimit(filter, "\t", '\0');
143 g_assert(filter[len - 1] == 0x0);
144 MultiByteToWideChar(CP_UTF8, 0, filter, len, title, sizeof(title));
145 g_free(filter);
147 return title;
151 static wchar_t *get_filters(gboolean project_files)
153 gchar *string;
154 gint len;
155 static wchar_t title[1024];
157 if (project_files)
159 string = g_strconcat(_("Geany project files"), "\t", "*." GEANY_PROJECT_EXT, "\t",
160 _("All files"), "\t", "*", "\t", NULL);
162 else
164 string = g_strconcat(_("Executables"), "\t", "*.exe;*.bat;*.cmd", "\t",
165 _("All files"), "\t", "*", "\t", NULL);
168 /* replace all "\t"s by \0 */
169 len = strlen(string);
170 g_strdelimit(string, "\t", '\0');
171 g_assert(string[len - 1] == 0x0);
172 MultiByteToWideChar(CP_UTF8, 0, string, len, title, sizeof(title));
173 g_free(string);
175 return title;
179 /* Converts the given UTF-8 filename or directory name into something usable for Windows and
180 * returns the directory part of the given filename. */
181 static wchar_t *get_dir_for_path(const gchar *utf8_filename)
183 static wchar_t w_dir[MAX_PATH];
184 gchar *result;
186 if (g_file_test(utf8_filename, G_FILE_TEST_IS_DIR))
187 result = (gchar*) utf8_filename;
188 else
189 result = g_path_get_dirname(utf8_filename);
191 MultiByteToWideChar(CP_UTF8, 0, result, -1, w_dir, sizeof(w_dir));
193 if (result != utf8_filename)
194 g_free(result);
196 return w_dir;
200 /* Callback function for setting the initial directory of the folder open dialog. This could also
201 * be done with BROWSEINFO.pidlRoot and SHParseDisplayName but SHParseDisplayName is not available
202 * on systems below Windows XP. So, we go the hard way by creating a callback which will set up the
203 * folder when the dialog is initialised. Yeah, I like Windows. */
204 INT CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData)
206 switch (uMsg)
208 case BFFM_INITIALIZED:
210 SendMessageW(hwnd, BFFM_SETSELECTIONW, TRUE, pData);
211 break;
213 case BFFM_SELCHANGED:
215 /* set the status window to the currently selected path. */
216 static wchar_t szDir[MAX_PATH];
217 if (SHGetPathFromIDListW((LPITEMIDLIST) lp, szDir))
219 SendMessageW(hwnd, BFFM_SETSTATUSTEXTW, 0, (LPARAM) szDir);
221 break;
224 return 0;
228 /* Shows a folder selection dialog.
229 * initial_dir is expected in UTF-8
230 * The selected folder name is returned. */
231 gchar *win32_show_folder_dialog(GtkWidget *parent, const gchar *title, const gchar *initial_dir)
233 BROWSEINFOW bi;
234 LPCITEMIDLIST pidl;
235 gchar *result = NULL;
236 wchar_t fname[MAX_PATH];
237 wchar_t w_title[512];
239 MultiByteToWideChar(CP_UTF8, 0, title, -1, w_title, sizeof(w_title));
241 if (parent == NULL)
242 parent = main_widgets.window;
244 memset(&bi, 0, sizeof bi);
245 bi.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(parent));
246 bi.pidlRoot = NULL;
247 bi.lpszTitle = w_title;
248 bi.lpfn = BrowseCallbackProc;
249 bi.lParam = (LPARAM) get_dir_for_path(initial_dir);
250 bi.ulFlags = BIF_DONTGOBELOWDOMAIN | BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT;
252 pidl = SHBrowseForFolderW(&bi);
254 /* convert the strange Windows folder list item something into an usual path string ;-) */
255 if (pidl != 0)
257 if (SHGetPathFromIDListW(pidl, fname))
259 result = g_malloc0(MAX_PATH * 2);
260 WideCharToMultiByte(CP_UTF8, 0, fname, -1, result, MAX_PATH * 2, NULL, NULL);
262 /* SHBrowseForFolder() probably leaks memory here, but how to free the allocated memory? */
264 return result;
268 /* Shows a file open dialog.
269 * If allow_new_file is set, the file to be opened doesn't have to exist.
270 * initial_dir is expected in UTF-8
271 * The selected file name is returned.
272 * If project_file_filter is set, the file open dialog will have a file filter for Geany project
273 * files, a filter for executables otherwise. */
274 gchar *win32_show_project_open_dialog(GtkWidget *parent, const gchar *title,
275 const gchar *initial_dir, gboolean allow_new_file,
276 gboolean project_file_filter)
278 OPENFILENAMEW of;
279 gint retval;
280 wchar_t fname[MAX_PATH];
281 wchar_t w_title[512];
282 gchar *filename;
284 fname[0] = '\0';
286 MultiByteToWideChar(CP_UTF8, 0, title, -1, w_title, sizeof(w_title));
288 if (parent == NULL)
289 parent = main_widgets.window;
291 /* initialise file dialog info struct */
292 memset(&of, 0, sizeof of);
293 #ifdef OPENFILENAME_SIZE_VERSION_400
294 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
295 #else
296 of.lStructSize = sizeof of;
297 #endif
298 of.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(parent));
299 of.lpstrFilter = get_filters(project_file_filter);
301 of.lpstrCustomFilter = NULL;
302 of.nFilterIndex = 0;
303 of.lpstrFile = fname;
304 of.lpstrInitialDir = get_dir_for_path(initial_dir);
305 of.nMaxFile = 2048;
306 of.lpstrFileTitle = NULL;
307 of.lpstrTitle = w_title;
308 of.lpstrDefExt = L"";
309 of.Flags = OFN_PATHMUSTEXIST | OFN_EXPLORER | OFN_HIDEREADONLY;
310 if (! allow_new_file)
311 of.Flags |= OFN_FILEMUSTEXIST;
313 retval = GetOpenFileNameW(&of);
315 if (! retval)
317 if (CommDlgExtendedError())
319 gchar *error;
320 error = g_strdup_printf("File dialog box error (%x)", (int)CommDlgExtendedError());
321 win32_message_dialog(NULL, GTK_MESSAGE_ERROR, error);
322 g_free(error);
324 return NULL;
326 /* convert the resulting filename into UTF-8 (from whatever encoding it has at this moment) */
327 filename = g_malloc0(MAX_PATH * 2);
328 WideCharToMultiByte(CP_UTF8, 0, fname, -1, filename, MAX_PATH * 2, NULL, NULL);
330 return filename;
334 /* initial_dir can be NULL to use the current working directory.
335 * Returns: TRUE if the dialog was not cancelled. */
336 gboolean win32_show_document_open_dialog(GtkWindow *parent, const gchar *title, const gchar *initial_dir)
338 OPENFILENAMEW of;
339 gint retval;
340 guint x;
341 gchar tmp[MAX_PATH];
342 wchar_t fname[MAX_PATH];
343 wchar_t w_dir[MAX_PATH];
344 wchar_t w_title[512];
346 fname[0] = '\0';
348 if (initial_dir != NULL)
349 MultiByteToWideChar(CP_UTF8, 0, initial_dir, -1, w_dir, sizeof(w_dir));
351 MultiByteToWideChar(CP_UTF8, 0, title, -1, w_title, sizeof(w_title));
353 /* initialise file dialog info struct */
354 memset(&of, 0, sizeof of);
355 #ifdef OPENFILENAME_SIZE_VERSION_400
356 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
357 #else
358 of.lStructSize = sizeof of;
359 #endif
360 of.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(GTK_WIDGET(parent)));
361 of.lpstrFilter = get_file_filters();
363 of.lpstrCustomFilter = NULL;
364 of.nFilterIndex = GEANY_FILETYPES_NONE + 1;
365 of.lpstrFile = fname;
366 of.lpstrInitialDir = (initial_dir != NULL) ? w_dir : NULL;
367 of.nMaxFile = 2048;
368 of.lpstrFileTitle = NULL;
369 of.lpstrTitle = w_title;
370 of.lpstrDefExt = L"";
371 of.Flags = OFN_ALLOWMULTISELECT | OFN_FILEMUSTEXIST | OFN_EXPLORER;
373 retval = GetOpenFileNameW(&of);
375 if (!retval)
377 if (CommDlgExtendedError())
379 gchar error[100];
380 g_snprintf(error, sizeof error, "File dialog box error (%x)", (int)CommDlgExtendedError());
381 win32_message_dialog(NULL, GTK_MESSAGE_ERROR, error);
383 return FALSE;
386 x = of.nFileOffset - 1;
387 if (x != wcslen(fname))
388 { /* open a single file */
389 WideCharToMultiByte(CP_UTF8, 0, fname, -1, tmp, sizeof(tmp), NULL, NULL);
390 document_open_file(tmp, of.Flags & OFN_READONLY, NULL, NULL);
392 else
393 { /* open multiple files */
394 gchar file_name[MAX_PATH];
395 gchar dir_name[MAX_PATH];
397 WideCharToMultiByte(CP_UTF8, 0, fname, of.nFileOffset,
398 dir_name, sizeof(dir_name), NULL, NULL);
399 for (; ;)
401 if (! fname[x])
403 if (! fname[x + 1])
404 break;
406 WideCharToMultiByte(CP_UTF8, 0, fname + x + 1, -1,
407 tmp, sizeof(tmp), NULL, NULL);
408 g_snprintf(file_name, 511, "%s\\%s", dir_name, tmp);
410 /* convert the resulting filename into UTF-8 */
411 document_open_file(file_name, of.Flags & OFN_READONLY, NULL, NULL);
413 x++;
416 return (retval != 0);
420 gchar *win32_show_document_save_as_dialog(GtkWindow *parent, const gchar *title,
421 const gchar *initial_file)
423 OPENFILENAMEW of;
424 gint retval;
425 gchar tmp[MAX_PATH];
426 wchar_t w_file[MAX_PATH];
427 wchar_t w_title[512];
429 w_file[0] = '\0';
431 if (initial_file != NULL)
432 MultiByteToWideChar(CP_UTF8, 0, initial_file, -1, w_file, sizeof(w_file));
434 MultiByteToWideChar(CP_UTF8, 0, title, -1, w_title, sizeof(w_title));
436 /* initialise file dialog info struct */
437 memset(&of, 0, sizeof of);
438 #ifdef OPENFILENAME_SIZE_VERSION_400
439 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
440 #else
441 of.lStructSize = sizeof of;
442 #endif
443 of.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(GTK_WIDGET(parent)));
445 of.lpstrFilter = get_file_filter_all_files();
446 of.lpstrCustomFilter = NULL;
447 of.nFilterIndex = 0;
449 of.lpstrFile = w_file;
450 of.nMaxFile = 2048;
451 of.lpstrFileTitle = NULL;
452 of.lpstrTitle = w_title;
453 of.lpstrDefExt = L"";
454 of.Flags = OFN_FILEMUSTEXIST | OFN_EXPLORER;
455 retval = GetSaveFileNameW(&of);
457 if (! retval)
459 if (CommDlgExtendedError())
461 gchar *error = g_strdup_printf(
462 "File dialog box error (%x)", (gint) CommDlgExtendedError());
463 win32_message_dialog(NULL, GTK_MESSAGE_ERROR, error);
464 g_free(error);
466 return NULL;
469 WideCharToMultiByte(CP_UTF8, 0, w_file, -1, tmp, sizeof(tmp), NULL, NULL);
471 return g_strdup(tmp);
475 /* initial_dir can be NULL to use the current working directory.
476 * Returns: the selected filename */
477 gchar *win32_show_file_dialog(GtkWindow *parent, const gchar *title, const gchar *initial_file)
479 OPENFILENAMEW of;
480 gint retval;
481 gchar tmp[MAX_PATH];
482 wchar_t w_file[MAX_PATH];
483 wchar_t w_title[512];
485 w_file[0] = '\0';
487 if (initial_file != NULL)
488 MultiByteToWideChar(CP_UTF8, 0, initial_file, -1, w_file, sizeof(w_file));
490 MultiByteToWideChar(CP_UTF8, 0, title, -1, w_title, sizeof(w_title));
492 /* initialise file dialog info struct */
493 memset(&of, 0, sizeof of);
494 #ifdef OPENFILENAME_SIZE_VERSION_400
495 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
496 #else
497 of.lStructSize = sizeof of;
498 #endif
499 of.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(GTK_WIDGET(parent)));
501 of.lpstrFile = w_file;
502 of.nMaxFile = 2048;
503 of.lpstrFileTitle = NULL;
504 of.lpstrTitle = w_title;
505 of.lpstrDefExt = L"";
506 of.Flags = OFN_FILEMUSTEXIST | OFN_EXPLORER;
507 retval = GetOpenFileNameW(&of);
509 if (! retval)
511 if (CommDlgExtendedError())
513 gchar *error = g_strdup_printf(
514 "File dialog box error (%x)", (gint) CommDlgExtendedError());
515 win32_message_dialog(NULL, GTK_MESSAGE_ERROR, error);
516 g_free(error);
518 return NULL;
521 WideCharToMultiByte(CP_UTF8, 0, w_file, -1, tmp, sizeof(tmp), NULL, NULL);
523 return g_strdup(tmp);
527 void win32_show_font_dialog(void)
529 gint retval;
530 CHOOSEFONT cf;
531 LOGFONT lf; /* logical font structure */
533 memset(&lf, 0, sizeof lf);
534 /* TODO: init lf members */
536 memset(&cf, 0, sizeof cf);
537 cf.lStructSize = sizeof cf;
538 cf.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(main_widgets.window));
539 cf.lpLogFont = &lf;
540 /* support CF_APPLY? */
541 cf.Flags = CF_NOSCRIPTSEL | CF_FORCEFONTEXIST | CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS;
543 retval = ChooseFont(&cf);
545 if (retval)
547 gchar *editorfont = g_strdup_printf("%s %d", lf.lfFaceName, (cf.iPointSize / 10));
548 ui_set_editor_font(editorfont);
549 g_free(editorfont);
554 void win32_show_color_dialog(const gchar *colour)
556 CHOOSECOLOR cc;
557 static COLORREF acr_cust_clr[16];
558 static DWORD rgb_current;
559 gchar *hex = g_malloc0(12);
560 GeanyDocument *doc = document_get_current();
562 /* Initialize CHOOSECOLOR */
563 memset(&cc, 0, sizeof cc);
564 cc.lStructSize = sizeof(cc);
565 cc.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(main_widgets.window));
566 cc.lpCustColors = (LPDWORD) acr_cust_clr;
567 cc.rgbResult = (colour != NULL) ? utils_strtod(colour, NULL, colour[0] == '#') : 0;
568 cc.Flags = CC_FULLOPEN | CC_RGBINIT;
570 if (ChooseColor(&cc))
572 rgb_current = cc.rgbResult;
573 g_snprintf(hex, 11, "#%02X%02X%02X",
574 (guint) (utils_scale_round(GetRValue(rgb_current), 255)),
575 (guint) (utils_scale_round(GetGValue(rgb_current), 255)),
576 (guint) (utils_scale_round(GetBValue(rgb_current), 255)));
578 editor_insert_color(doc->editor, hex);
580 g_free(hex);
584 void win32_show_pref_file_dialog(GtkEntry *item)
586 OPENFILENAMEW of;
587 gint retval, len;
588 wchar_t fname[MAX_PATH];
589 gchar tmp[MAX_PATH];
590 gchar **field, *filename;
592 fname[0] = '\0';
594 /* cut the options from the command line */
595 field = g_strsplit(gtk_entry_get_text(GTK_ENTRY(item)), " ", 2);
596 if (field[0])
598 filename = g_find_program_in_path(field[0]);
599 if (filename != NULL && g_file_test(filename, G_FILE_TEST_EXISTS))
601 MultiByteToWideChar(CP_UTF8, 0, filename, -1, fname, sizeof(fname));
602 g_free(filename);
606 /* initialize file dialog info struct */
607 memset(&of, 0, sizeof of);
608 #ifdef OPENFILENAME_SIZE_VERSION_400
609 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
610 #else
611 of.lStructSize = sizeof of;
612 #endif
613 of.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(ui_widgets.prefs_dialog));
615 of.lpstrFilter = get_filters(FALSE);
616 of.lpstrCustomFilter = NULL;
617 of.nFilterIndex = 1;
619 of.lpstrFile = fname;
620 of.nMaxFile = 2048;
621 of.lpstrFileTitle = NULL;
622 /*of.lpstrInitialDir = g_get_home_dir();*/
623 of.lpstrInitialDir = NULL;
624 of.lpstrTitle = NULL;
625 of.lpstrDefExt = L"exe";
626 of.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_EXPLORER;
627 retval = GetOpenFileNameW(&of);
629 if (!retval)
631 if (CommDlgExtendedError())
633 gchar error[100];
634 g_snprintf(error, sizeof error, "File dialog box error (%x)", (int)CommDlgExtendedError());
635 win32_message_dialog(NULL, GTK_MESSAGE_ERROR, error);
637 g_strfreev(field);
638 return;
641 len = WideCharToMultiByte(CP_UTF8, 0, fname, -1, tmp, sizeof(tmp), NULL, NULL);
642 if ((of.nFileOffset - 1) != len)
644 if (g_strv_length(field) > 1)
645 /* add the command line args of the old command */
646 /** TODO this fails badly when the old command contained spaces, we need quoting here */
647 filename = g_strconcat(tmp, " ", field[1], NULL);
648 else
650 filename = g_strdup(tmp);
652 gtk_entry_set_text(GTK_ENTRY(item), filename);
653 g_free(filename);
655 g_strfreev(field);
659 /* Creates a native Windows message box of the given type and returns always TRUE
660 * or FALSE representing th pressed Yes or No button.
661 * If type is not GTK_MESSAGE_QUESTION, it returns always TRUE. */
662 gboolean win32_message_dialog(GtkWidget *parent, GtkMessageType type, const gchar *msg)
664 gboolean ret = TRUE;
665 gint rc;
666 guint t;
667 const gchar *title;
668 HWND parent_hwnd = NULL;
669 static wchar_t w_msg[512];
670 static wchar_t w_title[512];
672 switch (type)
674 case GTK_MESSAGE_ERROR:
676 t = MB_OK | MB_ICONERROR;
677 title = _("Error");
678 break;
680 case GTK_MESSAGE_QUESTION:
682 t = MB_YESNO | MB_ICONQUESTION;
683 title = _("Question");
684 break;
686 case GTK_MESSAGE_WARNING:
688 t = MB_OK | MB_ICONWARNING;
689 title = _("Warning");
690 break;
692 default:
694 t = MB_OK | MB_ICONINFORMATION;
695 title = _("Information");
696 break;
700 /* convert the Unicode chars to wide chars */
701 /** TODO test if LANG == C then possibly skip conversion => g_win32_getlocale() */
702 MultiByteToWideChar(CP_UTF8, 0, msg, -1, w_msg, G_N_ELEMENTS(w_msg));
703 MultiByteToWideChar(CP_UTF8, 0, title, -1, w_title, G_N_ELEMENTS(w_title));
705 if (parent != NULL)
706 parent_hwnd = GDK_WINDOW_HWND(gtk_widget_get_window(parent));
707 else if (main_widgets.window != NULL)
708 parent_hwnd = GDK_WINDOW_HWND(gtk_widget_get_window(main_widgets.window));
710 /* display the message box */
711 rc = MessageBoxW(parent_hwnd, w_msg, w_title, t);
713 if (type == GTK_MESSAGE_QUESTION && rc != IDYES)
714 ret = FALSE;
716 return ret;
720 /* Little wrapper for _waccess(), returns errno or 0 if there was no error */
721 gint win32_check_write_permission(const gchar *dir)
723 static wchar_t w_dir[MAX_PATH];
724 MultiByteToWideChar(CP_UTF8, 0, dir, -1, w_dir, sizeof w_dir);
725 if (_waccess(w_dir, R_OK | W_OK) != 0)
726 return errno;
727 else
728 return 0;
732 /* Just a simple wrapper function to open a browser window */
733 void win32_open_browser(const gchar *uri)
735 if (strncmp(uri, "file://", 7) == 0)
737 uri += 7;
738 if (strchr(uri, ':') != NULL)
740 while (*uri == '/')
741 uri++;
744 ShellExecute(NULL, "open", uri, NULL, NULL, SW_SHOWNORMAL);
748 /* Returns TRUE if the command, which child_pid refers to, returned with a successful exit code,
749 * otherwise FALSE. */
750 gboolean win32_get_exit_status(GPid child_pid)
752 DWORD exit_code;
753 GetExitCodeProcess(child_pid, &exit_code);
755 return (exit_code == 0);
759 static void debug_setup_console()
761 static const WORD MAX_CONSOLE_LINES = 500;
762 gint hConHandle;
763 glong lStdHandle;
764 CONSOLE_SCREEN_BUFFER_INFO coninfo;
765 FILE *fp;
767 /* allocate a console for this app */
768 AllocConsole();
770 /* set the screen buffer to be big enough to let us scroll text */
771 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo);
772 coninfo.dwSize.Y = MAX_CONSOLE_LINES;
773 SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize);
775 /* redirect unbuffered STDOUT to the console */
776 lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE);
777 hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
778 fp = _fdopen(hConHandle, "w");
779 *stdout = *fp;
780 setvbuf(stdout, NULL, _IONBF, 0);
782 /* redirect unbuffered STDERR to the console */
783 lStdHandle = (long)GetStdHandle(STD_ERROR_HANDLE);
784 hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
785 fp = _fdopen(hConHandle, "w");
786 *stderr = *fp;
787 setvbuf(stderr, NULL, _IONBF, 0);
789 /* redirect unbuffered STDIN to the console */
790 lStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE);
791 hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
792 fp = _fdopen( hConHandle, "r" );
793 *stdin = *fp;
794 setvbuf(stdin, NULL, _IONBF, 0);
798 void win32_init_debug_code(void)
800 if (app->debug_mode)
802 /* create a console window to get log messages on Windows,
803 * especially useful when generating tags files */
804 debug_setup_console();
805 /* Enable GLib process spawn debug mode when Geany was started with the debug flag */
806 g_setenv("G_SPAWN_WIN32_DEBUG", "1", FALSE);
811 gchar *win32_get_hostname(void)
813 gchar hostname[100];
814 DWORD size = sizeof(hostname);
816 if (GetComputerName(hostname, &size))
817 return g_strdup(hostname);
818 else
819 return g_strdup("localhost");
823 /* Process spawning implementation for Windows, by Pierre Joye.
824 * Don't call this function directly, use utils_spawn_[a]sync() instead. */
825 gboolean win32_spawn(const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags,
826 gchar **std_out, gchar **std_err, gint *exit_status, GError **error)
828 TCHAR buffer[CMDSIZE]=TEXT("");
829 TCHAR cmdline[CMDSIZE] = TEXT("");
830 TCHAR* lpPart[CMDSIZE]={NULL};
831 DWORD retval = 0;
832 gint argc = 0, i;
833 gint cmdpos = 0;
835 SECURITY_ATTRIBUTES saAttr;
836 BOOL fSuccess;
837 geany_win32_spawn gw_spawn;
839 /* Temp file */
840 HANDLE hStdoutTempFile = NULL;
841 HANDLE hStderrTempFile = NULL;
843 gchar *stdout_content = NULL;
844 gchar *stderr_content = NULL;
846 while (argv[argc])
848 ++argc;
850 g_return_val_if_fail (std_out == NULL ||
851 !(flags & G_SPAWN_STDOUT_TO_DEV_NULL), FALSE);
852 g_return_val_if_fail (std_err == NULL ||
853 !(flags & G_SPAWN_STDERR_TO_DEV_NULL), FALSE);
855 if (flags & G_SPAWN_SEARCH_PATH)
857 retval = SearchPath(NULL, argv[0], ".exe", sizeof(buffer), buffer, lpPart);
858 if (retval > 0)
859 g_snprintf(cmdline, sizeof(cmdline), "\"%s\"", buffer);
860 else
861 g_strlcpy(cmdline, argv[0], sizeof(cmdline));
862 cmdpos = 1;
865 for (i = cmdpos; i < argc; i++)
867 g_snprintf(cmdline, sizeof(cmdline), "%s %s", cmdline, argv[i]);
868 /*MessageBox(NULL, cmdline, cmdline, MB_OK);*/
871 if (std_err != NULL)
873 hStderrTempFile = GetTempFileHandle(error);
874 if (hStderrTempFile == INVALID_HANDLE_VALUE)
876 gchar *msg = g_win32_error_message(GetLastError());
877 geany_debug("win32_spawn: Second CreateFile failed (%d)", (gint) GetLastError());
878 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR, "%s", msg);
879 g_free(msg);
880 return FALSE;
884 if (std_out != NULL)
886 hStdoutTempFile = GetTempFileHandle(error);
887 if (hStdoutTempFile == INVALID_HANDLE_VALUE)
889 gchar *msg = g_win32_error_message(GetLastError());
890 geany_debug("win32_spawn: Second CreateFile failed (%d)", (gint) GetLastError());
891 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR, "%s", msg);
892 g_free(msg);
893 return FALSE;
897 /* Set the bInheritHandle flag so pipe handles are inherited. */
898 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
899 saAttr.bInheritHandle = TRUE;
900 saAttr.lpSecurityDescriptor = NULL;
902 /* Get the handle to the current STDOUT and STDERR. */
903 gw_spawn.hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
904 gw_spawn.hStderr = GetStdHandle(STD_ERROR_HANDLE);
905 gw_spawn.dwExitCode = 0;
907 /* Create a pipe for the child process's STDOUT. */
908 if (! CreatePipe(&(gw_spawn.hChildStdoutRd), &(gw_spawn.hChildStdoutWr), &saAttr, 0))
910 gchar *msg = g_win32_error_message(GetLastError());
911 geany_debug("win32_spawn: Stdout pipe creation failed (%d)", (gint) GetLastError());
912 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR_PIPE, "%s", msg);
913 g_free(msg);
914 return FALSE;
917 /* Ensure that the read handle to the child process's pipe for STDOUT is not inherited.*/
918 SetHandleInformation(gw_spawn.hChildStdoutRd, HANDLE_FLAG_INHERIT, 0);
920 /* Create a pipe for the child process's STDERR. */
921 if (! CreatePipe(&(gw_spawn.hChildStderrRd), &(gw_spawn.hChildStderrWr), &saAttr, 0))
923 gchar *msg = g_win32_error_message(GetLastError());
924 geany_debug("win32_spawn: Stderr pipe creation failed");
925 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR_PIPE, "%s", msg);
926 g_free(msg);
927 return FALSE;
930 /* Ensure that the read handle to the child process's pipe for STDOUT is not inherited.*/
931 SetHandleInformation(gw_spawn.hChildStderrRd, HANDLE_FLAG_INHERIT, 0);
933 /* Create a pipe for the child process's STDIN. */
934 if (! CreatePipe(&(gw_spawn.hChildStdinRd), &(gw_spawn.hChildStdinWr), &saAttr, 0))
936 gchar *msg = g_win32_error_message(GetLastError());
937 geany_debug("win32_spawn: Stdin pipe creation failed");
938 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR_PIPE, "%s", msg);
939 g_free(msg);
940 return FALSE;
943 /* Ensure that the write handle to the child process's pipe for STDIN is not inherited. */
944 SetHandleInformation(gw_spawn.hChildStdinWr, HANDLE_FLAG_INHERIT, 0);
946 /* Now create the child process. */
947 fSuccess = CreateChildProcess(&gw_spawn, cmdline, dir, error);
948 if (exit_status)
950 *exit_status = gw_spawn.dwExitCode;
953 if (! fSuccess)
955 geany_debug("win32_spawn: Create process failed");
956 return FALSE;
959 /* Read from pipe that is the standard output for child process. */
960 if (std_out != NULL)
962 ReadFromPipe(gw_spawn.hChildStdoutRd, gw_spawn.hChildStdoutWr, hStdoutTempFile, error);
963 if (! GetContentFromHandle(hStdoutTempFile, &stdout_content, error))
965 return FALSE;
967 *std_out = stdout_content;
970 if (std_err != NULL)
972 ReadFromPipe(gw_spawn.hChildStderrRd, gw_spawn.hChildStderrWr, hStderrTempFile, error);
973 if (! GetContentFromHandle(hStderrTempFile, &stderr_content, error))
975 return FALSE;
977 *std_err = stderr_content;
979 return TRUE;
983 static gboolean GetContentFromHandle(HANDLE hFile, gchar **content, GError **error)
985 DWORD filesize;
986 gchar * buffer;
987 DWORD dwRead;
989 filesize = GetFileSize(hFile, NULL);
990 if (filesize < 1)
992 *content = NULL;
993 return TRUE;
996 buffer = g_malloc(filesize + 1);
997 if (! buffer)
999 gchar *msg = g_win32_error_message(GetLastError());
1000 geany_debug("GetContentFromHandle: Alloc failed");
1001 g_set_error(error, G_SPAWN_ERROR, G_SPAWN_ERROR, "%s", msg);
1002 g_free(msg);
1003 return FALSE;
1006 SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
1007 if (! ReadFile(hFile, buffer, filesize, &dwRead, NULL) || dwRead == 0)
1009 gchar *msg = g_win32_error_message(GetLastError());
1010 geany_debug("GetContentFromHandle: Cannot read tempfile");
1011 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR_FAILED, "%s", msg);
1012 g_free(msg);
1013 return FALSE;
1016 if (! CloseHandle(hFile))
1018 gchar *msg = g_win32_error_message(GetLastError());
1019 geany_debug("GetContentFromHandle: CloseHandle failed (%d)", (gint) GetLastError());
1020 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR_FAILED, "%s", msg);
1021 g_free(msg);
1022 g_free(buffer);
1023 *content = NULL;
1024 return FALSE;
1026 buffer[filesize] = '\0';
1027 *content = buffer;
1028 return TRUE;
1032 gchar *win32_expand_environment_variables(const gchar *str)
1034 gchar expCmdline[32768]; /* 32768 is the limit for ExpandEnvironmentStrings() */
1036 if (ExpandEnvironmentStrings((LPCTSTR) str, (LPTSTR) expCmdline, sizeof(expCmdline)) != 0)
1037 return g_strdup(expCmdline);
1038 else
1039 return g_strdup(str);
1043 static gboolean CreateChildProcess(geany_win32_spawn *gw_spawn, TCHAR *szCmdline,
1044 const TCHAR *dir, GError **error)
1046 PROCESS_INFORMATION piProcInfo;
1047 STARTUPINFOW siStartInfo;
1048 BOOL bFuncRetn = FALSE;
1049 gchar *expandedCmdline;
1050 wchar_t w_commandline[CMDSIZE];
1051 wchar_t w_dir[MAX_PATH];
1053 /* Set up members of the PROCESS_INFORMATION structure. */
1054 ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
1056 /* Set up members of the STARTUPINFO structure.*/
1057 ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
1059 siStartInfo.cb = sizeof(STARTUPINFO);
1060 siStartInfo.hStdError = gw_spawn->hChildStderrWr;
1061 siStartInfo.hStdOutput = gw_spawn->hChildStdoutWr;
1062 siStartInfo.hStdInput = gw_spawn->hChildStdinRd;
1063 siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
1065 /* Expand environment variables like %blah%. */
1066 expandedCmdline = win32_expand_environment_variables(szCmdline);
1068 MultiByteToWideChar(CP_UTF8, 0, expandedCmdline, -1, w_commandline, sizeof(w_commandline));
1069 MultiByteToWideChar(CP_UTF8, 0, dir, -1, w_dir, sizeof(w_dir));
1071 /* Create the child process. */
1072 bFuncRetn = CreateProcessW(NULL,
1073 w_commandline, /* command line */
1074 NULL, /* process security attributes */
1075 NULL, /* primary thread security attributes */
1076 TRUE, /* handles are inherited */
1077 CREATE_NO_WINDOW, /* creation flags */
1078 NULL, /* use parent's environment */
1079 w_dir, /* use parent's current directory */
1080 &siStartInfo, /* STARTUPINFO pointer */
1081 &piProcInfo); /* receives PROCESS_INFORMATION */
1083 g_free(expandedCmdline);
1085 if (bFuncRetn == 0)
1087 gchar *msg = g_win32_error_message(GetLastError());
1088 geany_debug("CreateChildProcess: CreateProcess failed (%s)", msg);
1089 g_set_error(error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, "%s", msg);
1090 g_free(msg);
1091 return FALSE;
1093 else
1095 gint i;
1096 gsize ms = 30*1000;
1098 /* FIXME: this seems to timeout when there are many lines
1099 * to read - maybe because the child's pipe is full */
1100 for (i = 0; i < 2 &&
1101 WaitForSingleObject(piProcInfo.hProcess, ms) == WAIT_TIMEOUT; i++)
1103 ui_set_statusbar(FALSE, _("Process timed out after %.02f s!"), ms / 1000.0F);
1104 geany_debug("CreateChildProcess: timed out");
1105 TerminateProcess(piProcInfo.hProcess, WAIT_TIMEOUT); /* NOTE: This will not kill grandkids. */
1108 if (!GetExitCodeProcess(piProcInfo.hProcess, &gw_spawn->dwExitCode))
1110 gchar *msg = g_win32_error_message(GetLastError());
1111 geany_debug("GetExitCodeProcess failed: %s", msg);
1112 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR_FAILED, "%s", msg);
1113 g_free(msg);
1115 CloseHandle(piProcInfo.hProcess);
1116 CloseHandle(piProcInfo.hThread);
1117 return bFuncRetn;
1119 return FALSE;
1123 static VOID ReadFromPipe(HANDLE hRead, HANDLE hWrite, HANDLE hFile, GError **error)
1125 DWORD dwRead, dwWritten;
1126 CHAR chBuf[BUFSIZE];
1128 /* Close the write end of the pipe before reading from the
1129 read end of the pipe. */
1130 if (! CloseHandle(hWrite))
1132 gchar *msg = g_win32_error_message(GetLastError());
1133 geany_debug("ReadFromPipe: Closing handle failed");
1134 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR_PIPE, "%s", msg);
1135 g_free(msg);
1136 return;
1139 /* Read output from the child process, and write to parent's STDOUT. */
1140 for (;;)
1142 if (! ReadFile(hRead, chBuf, BUFSIZE, &dwRead, NULL) || dwRead == 0)
1143 break;
1145 if (! WriteFile(hFile, chBuf, dwRead, &dwWritten, NULL))
1146 break;
1151 static HANDLE GetTempFileHandle(GError **error)
1153 /* Temp file */
1154 DWORD dwBufSize = BUFSIZE;
1155 UINT uRetVal;
1156 TCHAR szTempName[BUFSIZE];
1157 TCHAR lpPathBuffer[BUFSIZE];
1158 DWORD dwRetVal;
1159 HANDLE hTempFile;
1161 /* Get the temp path. */
1162 dwRetVal = GetTempPath(dwBufSize, /* length of the buffer*/
1163 lpPathBuffer); /* buffer for path */
1165 if (dwRetVal > dwBufSize || (dwRetVal == 0))
1167 gchar *msg = g_win32_error_message(GetLastError());
1168 geany_debug("GetTempFileHandle: GetTempPath failed (%d)", (gint) GetLastError());
1169 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR, "%s", msg);
1170 g_free(msg);
1171 return NULL;
1174 /* Create a temporary file for STDOUT. */
1175 uRetVal = GetTempFileName(lpPathBuffer, /* directory for tmp files */
1176 TEXT("GEANY_VCDIFF_"), /* temp file name prefix */
1177 0, /* create unique name */
1178 szTempName); /* buffer for name */
1179 if (uRetVal == 0)
1181 gchar *msg = g_win32_error_message(GetLastError());
1182 geany_debug("GetTempFileName failed (%d)", (gint) GetLastError());
1183 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR, "%s", msg);
1184 g_free(msg);
1185 return NULL;
1188 hTempFile = CreateFile((LPTSTR) szTempName, /* file name */
1189 GENERIC_READ | GENERIC_WRITE, /* open r-w */
1190 0, /* do not share */
1191 NULL, /* default security */
1192 CREATE_ALWAYS, /* overwrite existing */
1193 FILE_ATTRIBUTE_NORMAL,/* normal file */
1194 NULL); /* no template */
1196 if (hTempFile == INVALID_HANDLE_VALUE)
1198 gchar *msg = g_win32_error_message(GetLastError());
1199 geany_debug("GetTempFileHandle: Second CreateFile failed (%d)", (gint) GetLastError());
1200 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR, "%s", msg);
1201 g_free(msg);
1202 return NULL;
1204 return hTempFile;
1208 /* From GDK (they got it from MS Knowledge Base article Q130698) */
1209 static gboolean resolve_link(HWND hWnd, wchar_t *link, gchar **lpszPath)
1211 WIN32_FILE_ATTRIBUTE_DATA wfad;
1212 HRESULT hres;
1213 IShellLinkW *pslW = NULL;
1214 IPersistFile *ppf = NULL;
1215 LPVOID pslWV = NULL;
1216 LPVOID ppfV = NULL;
1218 /* Check if the file is empty first because IShellLink::Resolve for some reason succeeds
1219 * with an empty file and returns an empty "link target". (#524151) */
1220 if (!GetFileAttributesExW(link, GetFileExInfoStandard, &wfad) ||
1221 (wfad.nFileSizeHigh == 0 && wfad.nFileSizeLow == 0))
1223 return FALSE;
1226 /* Assume failure to start with: */
1227 *lpszPath = 0;
1229 CoInitialize(NULL);
1231 hres = CoCreateInstance(
1232 &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkW, &pslWV);
1234 if (SUCCEEDED(hres))
1236 /* The IShellLink interface supports the IPersistFile interface.
1237 * Get an interface pointer to it. */
1238 pslW = (IShellLinkW*) pslWV;
1239 hres = pslW->lpVtbl->QueryInterface(pslW, &IID_IPersistFile, &ppfV);
1242 if (SUCCEEDED(hres))
1244 /* Load the file. */
1245 ppf = (IPersistFile*) ppfV;
1246 hres = ppf->lpVtbl->Load(ppf, link, STGM_READ);
1249 if (SUCCEEDED(hres))
1251 /* Resolve the link by calling the Resolve() interface function. */
1252 hres = pslW->lpVtbl->Resolve(pslW, hWnd, SLR_ANY_MATCH | SLR_NO_UI);
1255 if (SUCCEEDED(hres))
1257 wchar_t wtarget[MAX_PATH];
1259 hres = pslW->lpVtbl->GetPath(pslW, wtarget, MAX_PATH, NULL, 0);
1260 if (SUCCEEDED(hres))
1261 *lpszPath = g_utf16_to_utf8(wtarget, -1, NULL, NULL, NULL);
1264 if (ppf)
1265 ppf->lpVtbl->Release(ppf);
1267 if (pslW)
1268 pslW->lpVtbl->Release(pslW);
1270 return SUCCEEDED(hres);
1274 /* Checks whether file_name is a Windows shortcut. file_name is expected in UTF-8 encoding.
1275 * If file_name is a Windows shortcut, it returns the target in UTF-8 encoding.
1276 * If it is not a shortcut, it returns a newly allocated copy of file_name. */
1277 gchar *win32_get_shortcut_target(const gchar *file_name)
1279 gchar *path = NULL;
1280 wchar_t *wfilename = g_utf8_to_utf16(file_name, -1, NULL, NULL, NULL);
1282 resolve_link(GDK_WINDOW_HWND(gtk_widget_get_window(main_widgets.window)), wfilename, &path);
1283 g_free(wfilename);
1285 if (path == NULL)
1286 return g_strdup(file_name);
1287 else
1288 return path;
1292 void win32_set_working_directory(const gchar *dir)
1294 SetCurrentDirectory(dir);
1298 gchar *win32_get_installation_dir(void)
1300 return g_win32_get_package_installation_directory_of_module(NULL);
1304 #endif