c++: Fix handling of the `final` contextual keyword
[geany-mirror.git] / src / win32.c
blob1f28e67886e59e70de74711c8c71ce02c6270afd
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 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
30 #include "win32.h"
32 #ifdef G_OS_WIN32
34 #include "dialogs.h"
35 #include "document.h"
36 #include "editor.h"
37 #include "filetypes.h"
38 #include "project.h"
39 #include "support.h"
40 #include "ui_utils.h"
41 #include "utils.h"
43 #include <ctype.h>
44 #include <fcntl.h>
45 #include <io.h>
46 #include <math.h>
47 #include <stdlib.h>
48 #include <string.h>
50 #define VC_EXTRALEAN
51 #define WIN32_LEAN_AND_MEAN
52 #include <windows.h>
53 #include <commdlg.h>
54 #include <shellapi.h>
55 #include <shlobj.h>
57 #include <glib/gstdio.h>
58 #include <gdk/gdkwin32.h>
61 /* The timer handle used to refresh windows below modal native dialogs. If
62 * ever more than one dialog can be shown at a time, this needs to be changed
63 * to be for specific dialogs. */
64 static UINT_PTR dialog_timer = 0;
67 G_INLINE_FUNC void win32_dialog_reset_timer(HWND hwnd)
69 if (G_UNLIKELY(dialog_timer != 0))
71 KillTimer(hwnd, dialog_timer);
72 dialog_timer = 0;
77 VOID CALLBACK
78 win32_dialog_update_main_window(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
80 gint i;
82 /* Pump the main window loop a bit, but not enough to lock-up.
83 * The typical `while(gtk_events_pending()) gtk_main_iteration();`
84 * loop causes the entire operating system to lock-up. */
85 for (i = 0; i < 4 && gtk_events_pending(); i++)
86 gtk_main_iteration();
88 /* Cancel any pending timers since we just did an update */
89 win32_dialog_reset_timer(hwnd);
93 G_INLINE_FUNC UINT_PTR win32_dialog_queue_main_window_redraw(HWND dlg, UINT msg,
94 WPARAM wParam, LPARAM lParam, gboolean postpone)
96 switch (msg)
98 /* Messages that likely mean the window below a dialog needs to be re-drawn. */
99 case WM_WINDOWPOSCHANGED:
100 case WM_MOVE:
101 case WM_SIZE:
102 case WM_THEMECHANGED:
103 if (postpone)
105 win32_dialog_reset_timer(dlg);
106 dialog_timer = SetTimer(dlg, 0, 33 /* around 30fps */, win32_dialog_update_main_window);
108 else
109 win32_dialog_update_main_window(dlg, msg, wParam, lParam);
110 break;
112 return 0; /* always let the default proc handle it */
116 /* This function is called for OPENFILENAME lpfnHook function and it establishes
117 * a timer that is reset each time which will update the main window loop eventually. */
118 UINT_PTR CALLBACK win32_dialog_explorer_hook_proc(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam)
120 return win32_dialog_queue_main_window_redraw(dlg, msg, wParam, lParam, TRUE);
124 /* This function is called for old-school win32 dialogs that accept a proper
125 * lpfnHook function for all messages, it doesn't use a timer. */
126 UINT_PTR CALLBACK win32_dialog_hook_proc(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam)
128 return win32_dialog_queue_main_window_redraw(dlg, msg, wParam, lParam, FALSE);
132 static wchar_t *get_file_filters(void)
134 gchar *string;
135 guint i, j, len;
136 static wchar_t title[4096];
137 GString *str = g_string_sized_new(100);
138 GString *all_patterns = g_string_sized_new(100);
139 GSList *node;
140 gchar *tmp;
142 /* create meta file filter "All files" */
143 g_string_append_printf(str, "%s\t*\t", _("All files"));
144 /* create meta file filter "All Source" (skip GEANY_FILETYPES_NONE) */
145 for (i = GEANY_FILETYPES_NONE + 1; i < filetypes_array->len; i++)
147 for (j = 0; filetypes[i]->pattern[j] != NULL; j++)
149 g_string_append(all_patterns, filetypes[i]->pattern[j]);
150 g_string_append_c(all_patterns, ';');
153 g_string_append_printf(str, "%s\t%s\t", _("All Source"), all_patterns->str);
154 g_string_free(all_patterns, TRUE);
155 /* add 'usual' filetypes */
156 foreach_slist(node, filetypes_by_title)
158 GeanyFiletype *ft = node->data;
160 if (G_UNLIKELY(ft->id == GEANY_FILETYPES_NONE))
161 continue;
162 tmp = g_strjoinv(";", ft->pattern);
163 g_string_append_printf(str, "%s\t%s\t", ft->title, tmp);
164 g_free(tmp);
166 g_string_append_c(str, '\t'); /* the final \0 byte to mark the end of the string */
167 string = str->str;
168 g_string_free(str, FALSE);
170 /* replace all "\t"s by \0 */
171 len = strlen(string);
172 g_strdelimit(string, "\t", '\0');
173 g_assert(string[len - 1] == 0x0);
174 MultiByteToWideChar(CP_UTF8, 0, string, len, title, G_N_ELEMENTS(title));
175 g_free(string);
177 return title;
181 static wchar_t *get_file_filter_all_files(void)
183 guint len;
184 static wchar_t title[4096];
185 gchar *filter;
187 /* create meta file filter "All files" */
188 filter = g_strdup_printf("%s\t*\t", _("All files"));
190 len = strlen(filter);
191 g_strdelimit(filter, "\t", '\0');
192 g_assert(filter[len - 1] == 0x0);
193 MultiByteToWideChar(CP_UTF8, 0, filter, len, title, G_N_ELEMENTS(title));
194 g_free(filter);
196 return title;
200 static wchar_t *get_filters(gboolean project_files)
202 gchar *string;
203 gint len;
204 static wchar_t title[1024];
206 if (project_files)
208 string = g_strconcat(_("Geany project files"), "\t", "*." GEANY_PROJECT_EXT, "\t",
209 _("All files"), "\t", "*", "\t", NULL);
211 else
213 string = g_strconcat(_("Executables"), "\t", "*.exe;*.bat;*.cmd", "\t",
214 _("All files"), "\t", "*", "\t", NULL);
217 /* replace all "\t"s by \0 */
218 len = strlen(string);
219 g_strdelimit(string, "\t", '\0');
220 g_assert(string[len - 1] == 0x0);
221 MultiByteToWideChar(CP_UTF8, 0, string, len, title, G_N_ELEMENTS(title));
222 g_free(string);
224 return title;
228 /* Converts the given UTF-8 filename or directory name into something usable for Windows and
229 * returns the directory part of the given filename. */
230 static wchar_t *get_dir_for_path(const gchar *utf8_filename)
232 static wchar_t w_dir[MAX_PATH];
233 gchar *result;
235 if (g_file_test(utf8_filename, G_FILE_TEST_IS_DIR))
236 result = (gchar*) utf8_filename;
237 else
238 result = g_path_get_dirname(utf8_filename);
240 MultiByteToWideChar(CP_UTF8, 0, result, -1, w_dir, G_N_ELEMENTS(w_dir));
242 if (result != utf8_filename)
243 g_free(result);
245 return w_dir;
249 /* Callback function for setting the initial directory of the folder open dialog. This could also
250 * be done with BROWSEINFO.pidlRoot and SHParseDisplayName but SHParseDisplayName is not available
251 * on systems below Windows XP. So, we go the hard way by creating a callback which will set up the
252 * folder when the dialog is initialised. Yeah, I like Windows. */
253 INT CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData)
255 win32_dialog_hook_proc(hwnd, uMsg, lp, pData);
256 switch (uMsg)
258 case BFFM_INITIALIZED:
260 SendMessageW(hwnd, BFFM_SETSELECTIONW, TRUE, pData);
261 break;
263 case BFFM_SELCHANGED:
265 /* set the status window to the currently selected path. */
266 static wchar_t szDir[MAX_PATH];
267 if (SHGetPathFromIDListW((LPITEMIDLIST) lp, szDir))
269 SendMessageW(hwnd, BFFM_SETSTATUSTEXTW, 0, (LPARAM) szDir);
271 break;
274 return 0;
278 /* Shows a folder selection dialog.
279 * initial_dir is expected in UTF-8
280 * The selected folder name is returned. */
281 gchar *win32_show_folder_dialog(GtkWidget *parent, const gchar *title, const gchar *initial_dir)
283 BROWSEINFOW bi;
284 LPCITEMIDLIST pidl;
285 gchar *result = NULL;
286 wchar_t fname[MAX_PATH];
287 wchar_t w_title[512];
289 MultiByteToWideChar(CP_UTF8, 0, title, -1, w_title, G_N_ELEMENTS(w_title));
291 if (parent == NULL)
292 parent = main_widgets.window;
294 memset(&bi, 0, sizeof bi);
295 bi.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(parent));
296 bi.pidlRoot = NULL;
297 bi.lpszTitle = w_title;
298 bi.lpfn = BrowseCallbackProc;
299 bi.lParam = (LPARAM) get_dir_for_path(initial_dir);
300 bi.ulFlags = BIF_DONTGOBELOWDOMAIN | BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT;
302 pidl = SHBrowseForFolderW(&bi);
304 /* convert the strange Windows folder list item something into an usual path string ;-) */
305 if (pidl != 0)
307 if (SHGetPathFromIDListW(pidl, fname))
309 result = g_malloc0(MAX_PATH * 2);
310 WideCharToMultiByte(CP_UTF8, 0, fname, -1, result, MAX_PATH * 2, NULL, NULL);
312 /* SHBrowseForFolder() probably leaks memory here, but how to free the allocated memory? */
314 return result;
318 /* Shows a file open dialog.
319 * If allow_new_file is set, the file to be opened doesn't have to exist.
320 * initial_dir is expected in UTF-8
321 * The selected file name is returned.
322 * If project_file_filter is set, the file open dialog will have a file filter for Geany project
323 * files, a filter for executables otherwise. */
324 gchar *win32_show_project_open_dialog(GtkWidget *parent, const gchar *title,
325 const gchar *initial_dir, gboolean allow_new_file,
326 gboolean project_file_filter)
328 OPENFILENAMEW of;
329 gint retval;
330 wchar_t fname[MAX_PATH];
331 wchar_t w_title[512];
332 gchar *filename;
334 fname[0] = '\0';
336 MultiByteToWideChar(CP_UTF8, 0, title, -1, w_title, G_N_ELEMENTS(w_title));
338 if (parent == NULL)
339 parent = main_widgets.window;
341 /* initialise file dialog info struct */
342 memset(&of, 0, sizeof of);
343 #ifdef OPENFILENAME_SIZE_VERSION_400
344 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
345 #else
346 of.lStructSize = sizeof of;
347 #endif
348 of.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(parent));
349 of.lpstrFilter = get_filters(project_file_filter);
351 of.lpstrCustomFilter = NULL;
352 of.nFilterIndex = 0;
353 of.lpstrFile = fname;
354 of.lpstrInitialDir = get_dir_for_path(initial_dir);
355 of.nMaxFile = 2048;
356 of.lpstrFileTitle = NULL;
357 of.lpstrTitle = w_title;
358 of.lpstrDefExt = L"";
359 of.Flags = OFN_PATHMUSTEXIST | OFN_EXPLORER | OFN_HIDEREADONLY | OFN_ENABLEHOOK;
360 of.lpfnHook = win32_dialog_explorer_hook_proc;
361 if (! allow_new_file)
362 of.Flags |= OFN_FILEMUSTEXIST;
364 retval = GetOpenFileNameW(&of);
366 if (! retval)
368 if (CommDlgExtendedError())
370 gchar *error;
371 error = g_strdup_printf("File dialog box error (%x)", (int)CommDlgExtendedError());
372 win32_message_dialog(NULL, GTK_MESSAGE_ERROR, error);
373 g_free(error);
375 return NULL;
377 /* convert the resulting filename into UTF-8 (from whatever encoding it has at this moment) */
378 filename = g_malloc0(MAX_PATH * 2);
379 WideCharToMultiByte(CP_UTF8, 0, fname, -1, filename, MAX_PATH * 2, NULL, NULL);
381 return filename;
385 /* initial_dir can be NULL to use the current working directory.
386 * Returns: TRUE if the dialog was not cancelled. */
387 gboolean win32_show_document_open_dialog(GtkWindow *parent, const gchar *title, const gchar *initial_dir)
389 OPENFILENAMEW of;
390 gint retval;
391 guint x;
392 gchar tmp[MAX_PATH];
393 wchar_t fname[MAX_PATH];
394 wchar_t w_dir[MAX_PATH];
395 wchar_t w_title[512];
397 fname[0] = '\0';
399 if (initial_dir != NULL)
400 MultiByteToWideChar(CP_UTF8, 0, initial_dir, -1, w_dir, G_N_ELEMENTS(w_dir));
402 MultiByteToWideChar(CP_UTF8, 0, title, -1, w_title, G_N_ELEMENTS(w_title));
404 /* initialise file dialog info struct */
405 memset(&of, 0, sizeof of);
406 #ifdef OPENFILENAME_SIZE_VERSION_400
407 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
408 #else
409 of.lStructSize = sizeof of;
410 #endif
411 of.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(GTK_WIDGET(parent)));
412 of.lpstrFilter = get_file_filters();
414 of.lpstrCustomFilter = NULL;
415 of.nFilterIndex = GEANY_FILETYPES_NONE + 1;
416 of.lpstrFile = fname;
417 of.lpstrInitialDir = (initial_dir != NULL) ? w_dir : NULL;
418 of.nMaxFile = 2048;
419 of.lpstrFileTitle = NULL;
420 of.lpstrTitle = w_title;
421 of.lpstrDefExt = L"";
422 of.Flags = OFN_ALLOWMULTISELECT | OFN_FILEMUSTEXIST | OFN_EXPLORER | OFN_ENABLEHOOK;
423 of.lpfnHook = win32_dialog_explorer_hook_proc;
425 retval = GetOpenFileNameW(&of);
427 if (!retval)
429 if (CommDlgExtendedError())
431 gchar error[100];
432 g_snprintf(error, sizeof error, "File dialog box error (%x)", (int)CommDlgExtendedError());
433 win32_message_dialog(NULL, GTK_MESSAGE_ERROR, error);
435 return FALSE;
438 x = of.nFileOffset - 1;
439 if (x != wcslen(fname))
440 { /* open a single file */
441 WideCharToMultiByte(CP_UTF8, 0, fname, -1, tmp, sizeof(tmp), NULL, NULL);
442 document_open_file(tmp, of.Flags & OFN_READONLY, NULL, NULL);
444 else
445 { /* open multiple files */
446 gchar file_name[MAX_PATH];
447 gchar dir_name[MAX_PATH];
449 WideCharToMultiByte(CP_UTF8, 0, fname, of.nFileOffset,
450 dir_name, sizeof(dir_name), NULL, NULL);
451 for (; ;)
453 if (! fname[x])
455 if (! fname[x + 1])
456 break;
458 WideCharToMultiByte(CP_UTF8, 0, fname + x + 1, -1,
459 tmp, sizeof(tmp), NULL, NULL);
460 g_snprintf(file_name, 511, "%s\\%s", dir_name, tmp);
462 /* convert the resulting filename into UTF-8 */
463 document_open_file(file_name, of.Flags & OFN_READONLY, NULL, NULL);
465 x++;
468 return (retval != 0);
472 gchar *win32_show_document_save_as_dialog(GtkWindow *parent, const gchar *title,
473 GeanyDocument *doc)
475 OPENFILENAMEW of;
476 gint retval;
477 gchar tmp[MAX_PATH];
478 wchar_t w_file[MAX_PATH];
479 wchar_t w_title[512];
480 int n;
482 w_file[0] = '\0';
484 /* Convert the name of the file for of.lpstrFile */
485 n = MultiByteToWideChar(CP_UTF8, 0, DOC_FILENAME(doc), -1, w_file, G_N_ELEMENTS(w_file));
487 /* If creating a new file name, convert and append the extension if any */
488 if (! doc->file_name && doc->file_type && doc->file_type->extension &&
489 n + 1 < (int)G_N_ELEMENTS(w_file))
491 w_file[n - 1] = L'.';
492 MultiByteToWideChar(CP_UTF8, 0, doc->file_type->extension, -1, &w_file[n],
493 G_N_ELEMENTS(w_file) - n - 1);
496 MultiByteToWideChar(CP_UTF8, 0, title, -1, w_title, G_N_ELEMENTS(w_title));
498 /* initialise file dialog info struct */
499 memset(&of, 0, sizeof of);
500 #ifdef OPENFILENAME_SIZE_VERSION_400
501 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
502 #else
503 of.lStructSize = sizeof of;
504 #endif
505 of.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(GTK_WIDGET(parent)));
507 of.lpstrFilter = get_file_filter_all_files();
508 of.lpstrCustomFilter = NULL;
509 of.nFilterIndex = 0;
511 of.lpstrFile = w_file;
512 of.nMaxFile = 2048;
513 of.lpstrFileTitle = NULL;
514 of.lpstrTitle = w_title;
515 of.lpstrDefExt = L"";
516 of.Flags = OFN_OVERWRITEPROMPT | OFN_EXPLORER | OFN_ENABLEHOOK;
517 of.lpfnHook = win32_dialog_explorer_hook_proc;
518 retval = GetSaveFileNameW(&of);
520 if (! retval)
522 if (CommDlgExtendedError())
524 gchar *error = g_strdup_printf(
525 "File dialog box error (%x)", (gint) CommDlgExtendedError());
526 win32_message_dialog(NULL, GTK_MESSAGE_ERROR, error);
527 g_free(error);
529 return NULL;
532 WideCharToMultiByte(CP_UTF8, 0, w_file, -1, tmp, sizeof(tmp), NULL, NULL);
534 return g_strdup(tmp);
538 /* initial_dir can be NULL to use the current working directory.
539 * Returns: the selected filename */
540 gchar *win32_show_file_dialog(GtkWindow *parent, const gchar *title, const gchar *initial_file)
542 OPENFILENAMEW of;
543 gint retval;
544 gchar tmp[MAX_PATH];
545 wchar_t w_file[MAX_PATH];
546 wchar_t w_title[512];
548 w_file[0] = '\0';
550 if (initial_file != NULL)
551 MultiByteToWideChar(CP_UTF8, 0, initial_file, -1, w_file, G_N_ELEMENTS(w_file));
553 MultiByteToWideChar(CP_UTF8, 0, title, -1, w_title, G_N_ELEMENTS(w_title));
555 /* initialise file dialog info struct */
556 memset(&of, 0, sizeof of);
557 #ifdef OPENFILENAME_SIZE_VERSION_400
558 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
559 #else
560 of.lStructSize = sizeof of;
561 #endif
562 of.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(GTK_WIDGET(parent)));
564 of.lpstrFile = w_file;
565 of.nMaxFile = 2048;
566 of.lpstrFileTitle = NULL;
567 of.lpstrTitle = w_title;
568 of.lpstrDefExt = L"";
569 of.Flags = OFN_FILEMUSTEXIST | OFN_EXPLORER | OFN_ENABLEHOOK;
570 of.lpfnHook = win32_dialog_explorer_hook_proc;
571 retval = GetOpenFileNameW(&of);
573 if (! retval)
575 if (CommDlgExtendedError())
577 gchar *error = g_strdup_printf(
578 "File dialog box error (%x)", (gint) CommDlgExtendedError());
579 win32_message_dialog(NULL, GTK_MESSAGE_ERROR, error);
580 g_free(error);
582 return NULL;
585 WideCharToMultiByte(CP_UTF8, 0, w_file, -1, tmp, sizeof(tmp), NULL, NULL);
587 return g_strdup(tmp);
591 void win32_show_font_dialog(void)
593 gint retval;
594 CHOOSEFONT cf;
595 LOGFONT lf; /* logical font structure */
597 memset(&lf, 0, sizeof lf);
598 /* TODO: init lf members */
600 memset(&cf, 0, sizeof cf);
601 cf.lStructSize = sizeof cf;
602 cf.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(main_widgets.window));
603 cf.lpLogFont = &lf;
604 /* support CF_APPLY? */
605 cf.Flags = CF_NOSCRIPTSEL | CF_FORCEFONTEXIST | CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS | CF_ENABLEHOOK;
606 cf.lpfnHook = win32_dialog_hook_proc;
608 retval = ChooseFont(&cf);
610 if (retval)
612 gchar *editorfont = g_strdup_printf("%s %d", lf.lfFaceName, (cf.iPointSize / 10));
613 ui_set_editor_font(editorfont);
614 g_free(editorfont);
619 void win32_show_color_dialog(const gchar *colour)
621 CHOOSECOLOR cc;
622 static COLORREF acr_cust_clr[16];
623 static DWORD rgb_current;
624 gchar *hex = g_malloc0(12);
625 GeanyDocument *doc = document_get_current();
627 /* Initialize CHOOSECOLOR */
628 memset(&cc, 0, sizeof cc);
629 cc.lStructSize = sizeof(cc);
630 cc.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(main_widgets.window));
631 cc.lpCustColors = (LPDWORD) acr_cust_clr;
632 cc.rgbResult = (colour != NULL) ? utils_parse_color_to_bgr(colour) : 0;
633 cc.Flags = CC_FULLOPEN | CC_RGBINIT | CC_ENABLEHOOK;
634 cc.lpfnHook = win32_dialog_hook_proc;
636 if (ChooseColor(&cc))
638 rgb_current = cc.rgbResult;
639 g_snprintf(hex, 11, "#%02X%02X%02X",
640 (guint) (utils_scale_round(GetRValue(rgb_current), 255)),
641 (guint) (utils_scale_round(GetGValue(rgb_current), 255)),
642 (guint) (utils_scale_round(GetBValue(rgb_current), 255)));
644 editor_insert_color(doc->editor, hex);
646 g_free(hex);
650 void win32_show_pref_file_dialog(GtkEntry *item)
652 OPENFILENAMEW of;
653 gint retval, len;
654 wchar_t fname[MAX_PATH];
655 gchar tmp[MAX_PATH];
656 gchar **field, *filename;
658 fname[0] = '\0';
660 /* cut the options from the command line */
661 field = g_strsplit(gtk_entry_get_text(GTK_ENTRY(item)), " ", 2);
662 if (field[0])
664 filename = g_find_program_in_path(field[0]);
665 if (filename != NULL && g_file_test(filename, G_FILE_TEST_EXISTS))
667 MultiByteToWideChar(CP_UTF8, 0, filename, -1, fname, G_N_ELEMENTS(fname));
668 g_free(filename);
672 /* initialize file dialog info struct */
673 memset(&of, 0, sizeof of);
674 #ifdef OPENFILENAME_SIZE_VERSION_400
675 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
676 #else
677 of.lStructSize = sizeof of;
678 #endif
679 of.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(ui_widgets.prefs_dialog));
681 of.lpstrFilter = get_filters(FALSE);
682 of.lpstrCustomFilter = NULL;
683 of.nFilterIndex = 1;
685 of.lpstrFile = fname;
686 of.nMaxFile = 2048;
687 of.lpstrFileTitle = NULL;
688 /*of.lpstrInitialDir = g_get_home_dir();*/
689 of.lpstrInitialDir = NULL;
690 of.lpstrTitle = NULL;
691 of.lpstrDefExt = L"exe";
692 of.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_EXPLORER | OFN_ENABLEHOOK;
693 of.lpfnHook = win32_dialog_explorer_hook_proc;
694 retval = GetOpenFileNameW(&of);
696 if (!retval)
698 if (CommDlgExtendedError())
700 gchar error[100];
701 g_snprintf(error, sizeof error, "File dialog box error (%x)", (int)CommDlgExtendedError());
702 win32_message_dialog(NULL, GTK_MESSAGE_ERROR, error);
704 g_strfreev(field);
705 return;
708 len = WideCharToMultiByte(CP_UTF8, 0, fname, -1, tmp, sizeof(tmp), NULL, NULL);
709 if ((of.nFileOffset - 1) != len)
711 if (g_strv_length(field) > 1)
712 /* add the command line args of the old command */
713 /** TODO this fails badly when the old command contained spaces, we need quoting here */
714 filename = g_strconcat(tmp, " ", field[1], NULL);
715 else
717 filename = g_strdup(tmp);
719 gtk_entry_set_text(GTK_ENTRY(item), filename);
720 g_free(filename);
722 g_strfreev(field);
726 /* Creates a native Windows message box of the given type and returns always TRUE
727 * or FALSE representing th pressed Yes or No button.
728 * If type is not GTK_MESSAGE_QUESTION, it returns always TRUE. */
729 gboolean win32_message_dialog(GtkWidget *parent, GtkMessageType type, const gchar *msg)
731 gboolean ret = TRUE;
732 gint rc;
733 guint t;
734 const gchar *title;
735 HWND parent_hwnd = NULL;
736 static wchar_t w_msg[512];
737 static wchar_t w_title[512];
739 switch (type)
741 case GTK_MESSAGE_ERROR:
743 t = MB_OK | MB_ICONERROR;
744 title = _("Error");
745 break;
747 case GTK_MESSAGE_QUESTION:
749 t = MB_YESNO | MB_ICONQUESTION;
750 title = _("Question");
751 break;
753 case GTK_MESSAGE_WARNING:
755 t = MB_OK | MB_ICONWARNING;
756 title = _("Warning");
757 break;
759 default:
761 t = MB_OK | MB_ICONINFORMATION;
762 title = _("Information");
763 break;
767 /* convert the Unicode chars to wide chars */
768 /** TODO test if LANG == C then possibly skip conversion => g_win32_getlocale() */
769 MultiByteToWideChar(CP_UTF8, 0, msg, -1, w_msg, G_N_ELEMENTS(w_msg));
770 MultiByteToWideChar(CP_UTF8, 0, title, -1, w_title, G_N_ELEMENTS(w_title));
772 if (parent != NULL)
773 parent_hwnd = GDK_WINDOW_HWND(gtk_widget_get_window(parent));
774 else if (main_widgets.window != NULL)
775 parent_hwnd = GDK_WINDOW_HWND(gtk_widget_get_window(main_widgets.window));
777 /* display the message box */
778 rc = MessageBoxW(parent_hwnd, w_msg, w_title, t);
780 if (type == GTK_MESSAGE_QUESTION && rc != IDYES)
781 ret = FALSE;
783 return ret;
787 /* Little wrapper for _waccess(), returns errno or 0 if there was no error */
788 gint win32_check_write_permission(const gchar *dir)
790 static wchar_t w_dir[MAX_PATH];
791 MultiByteToWideChar(CP_UTF8, 0, dir, -1, w_dir, G_N_ELEMENTS(w_dir));
792 if (_waccess(w_dir, R_OK | W_OK) != 0)
793 return errno;
794 else
795 return 0;
799 /* Just a simple wrapper function to open a browser window */
800 void win32_open_browser(const gchar *uri)
802 if (strncmp(uri, "file://", 7) == 0)
804 uri += 7;
805 if (strchr(uri, ':') != NULL)
807 while (*uri == '/')
808 uri++;
811 ShellExecute(NULL, "open", uri, NULL, NULL, SW_SHOWNORMAL);
815 static FILE *open_std_handle(DWORD handle, const char *mode)
817 HANDLE lStdHandle;
818 int hConHandle;
819 FILE *fp;
821 lStdHandle = GetStdHandle(handle);
822 if (lStdHandle == INVALID_HANDLE_VALUE)
824 gchar *err = g_win32_error_message(GetLastError());
825 g_warning("GetStdHandle(%ld) failed: %s", (long)handle, err);
826 g_free(err);
827 return NULL;
829 hConHandle = _open_osfhandle((long)lStdHandle, _O_TEXT);
830 if (hConHandle == -1)
832 gchar *err = g_win32_error_message(GetLastError());
833 g_warning("_open_osfhandle(%ld, _O_TEXT) failed: %s", (long)lStdHandle, err);
834 g_free(err);
835 return NULL;
837 fp = _fdopen(hConHandle, mode);
838 if (! fp)
840 gchar *err = g_win32_error_message(GetLastError());
841 g_warning("_fdopen(%d, \"%s\") failed: %s", hConHandle, mode, err);
842 g_free(err);
843 return NULL;
845 if (setvbuf(fp, NULL, _IONBF, 0) != 0)
847 gchar *err = g_win32_error_message(GetLastError());
848 g_warning("setvbuf(%p, NULL, _IONBF, 0) failed: %s", fp, err);
849 g_free(err);
850 fclose(fp);
851 return NULL;
854 return fp;
858 static void debug_setup_console(void)
860 static const WORD MAX_CONSOLE_LINES = 500;
861 CONSOLE_SCREEN_BUFFER_INFO coninfo;
862 FILE *fp;
864 /* allocate a console for this app */
865 AllocConsole();
867 /* set the screen buffer to be big enough to let us scroll text */
868 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo);
869 coninfo.dwSize.Y = MAX_CONSOLE_LINES;
870 SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize);
872 /* redirect unbuffered STDOUT to the console */
873 fp = open_std_handle(STD_OUTPUT_HANDLE, "w");
874 if (fp)
875 *stdout = *fp;
877 /* redirect unbuffered STDERR to the console */
878 fp = open_std_handle(STD_ERROR_HANDLE, "w");
879 if (fp)
880 *stderr = *fp;
882 /* redirect unbuffered STDIN to the console */
883 fp = open_std_handle(STD_INPUT_HANDLE, "r");
884 if (fp)
885 *stdin = *fp;
889 void win32_init_debug_code(void)
891 if (app->debug_mode)
893 /* create a console window to get log messages on Windows,
894 * especially useful when generating tags files */
895 debug_setup_console();
900 gchar *win32_expand_environment_variables(const gchar *str)
902 gchar expCmdline[32768]; /* 32768 is the limit for ExpandEnvironmentStrings() */
904 if (ExpandEnvironmentStrings((LPCTSTR) str, (LPTSTR) expCmdline, sizeof(expCmdline)) != 0)
905 return g_strdup(expCmdline);
906 else
907 return g_strdup(str);
911 /* From GDK (they got it from MS Knowledge Base article Q130698) */
912 static gboolean resolve_link(HWND hWnd, wchar_t *link, gchar **lpszPath)
914 WIN32_FILE_ATTRIBUTE_DATA wfad;
915 HRESULT hres;
916 IShellLinkW *pslW = NULL;
917 IPersistFile *ppf = NULL;
918 LPVOID pslWV = NULL;
919 LPVOID ppfV = NULL;
921 /* Check if the file is empty first because IShellLink::Resolve for some reason succeeds
922 * with an empty file and returns an empty "link target". (#524151) */
923 if (!GetFileAttributesExW(link, GetFileExInfoStandard, &wfad) ||
924 (wfad.nFileSizeHigh == 0 && wfad.nFileSizeLow == 0))
926 return FALSE;
929 /* Assume failure to start with: */
930 *lpszPath = 0;
932 CoInitialize(NULL);
934 hres = CoCreateInstance(
935 &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkW, &pslWV);
937 if (SUCCEEDED(hres))
939 /* The IShellLink interface supports the IPersistFile interface.
940 * Get an interface pointer to it. */
941 pslW = (IShellLinkW*) pslWV;
942 hres = pslW->lpVtbl->QueryInterface(pslW, &IID_IPersistFile, &ppfV);
945 if (SUCCEEDED(hres))
947 /* Load the file. */
948 ppf = (IPersistFile*) ppfV;
949 hres = ppf->lpVtbl->Load(ppf, link, STGM_READ);
952 if (SUCCEEDED(hres))
954 /* Resolve the link by calling the Resolve() interface function. */
955 hres = pslW->lpVtbl->Resolve(pslW, hWnd, SLR_ANY_MATCH | SLR_NO_UI);
958 if (SUCCEEDED(hres))
960 wchar_t wtarget[MAX_PATH];
962 hres = pslW->lpVtbl->GetPath(pslW, wtarget, MAX_PATH, NULL, 0);
963 if (SUCCEEDED(hres))
964 *lpszPath = g_utf16_to_utf8(wtarget, -1, NULL, NULL, NULL);
967 if (ppf)
968 ppf->lpVtbl->Release(ppf);
970 if (pslW)
971 pslW->lpVtbl->Release(pslW);
973 return SUCCEEDED(hres);
977 /* Checks whether file_name is a Windows shortcut. file_name is expected in UTF-8 encoding.
978 * If file_name is a Windows shortcut, it returns the target in UTF-8 encoding.
979 * If it is not a shortcut, it returns a newly allocated copy of file_name. */
980 gchar *win32_get_shortcut_target(const gchar *file_name)
982 gchar *path = NULL;
983 wchar_t *wfilename = g_utf8_to_utf16(file_name, -1, NULL, NULL, NULL);
984 HWND hWnd = NULL;
986 if (main_widgets.window != NULL)
988 GdkWindow *window = gtk_widget_get_window(main_widgets.window);
989 if (window != NULL)
990 hWnd = GDK_WINDOW_HWND(window);
993 resolve_link(hWnd, wfilename, &path);
994 g_free(wfilename);
996 if (path == NULL)
997 return g_strdup(file_name);
998 else
999 return path;
1003 void win32_set_working_directory(const gchar *dir)
1005 SetCurrentDirectory(dir);
1009 gchar *win32_get_installation_dir(void)
1011 return g_win32_get_package_installation_directory_of_module(NULL);
1015 gchar *win32_get_user_config_dir(void)
1017 HRESULT hr;
1018 wchar_t path[MAX_PATH];
1020 hr = SHGetFolderPathAndSubDirW(NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, L"geany", path);
1021 if (SUCCEEDED(hr))
1023 // GLib always uses UTF-8 for filename encoding on Windows
1024 int u8_size = WideCharToMultiByte(CP_UTF8, 0, path, -1, NULL, 0, NULL, NULL);
1025 if (u8_size > 0)
1027 gchar *u8_path = g_malloc0(u8_size + 1);
1028 if (u8_path != NULL &&
1029 WideCharToMultiByte(CP_UTF8, 0, path, -1, u8_path, u8_size, NULL, NULL))
1031 return u8_path;
1036 // glib fallback
1037 g_warning("Failed to retrieve Windows config dir, falling back to default");
1038 return g_build_filename(g_get_user_config_dir(), "geany", NULL);
1041 #endif