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.
30 /* Need Windows XP for SHGetFolderPathAndSubDirW */
31 #define _WIN32_WINNT 0x0501
32 /* Needed for SHGFP_TYPE */
33 #define _WIN32_IE 0x0500
42 #include "filetypes.h"
60 #include <glib/gstdio.h>
61 #include <gdk/gdkwin32.h>
64 /* The timer handle used to refresh windows below modal native dialogs. If
65 * ever more than one dialog can be shown at a time, this needs to be changed
66 * to be for specific dialogs. */
67 static UINT_PTR dialog_timer
= 0;
70 G_INLINE_FUNC
void win32_dialog_reset_timer(HWND hwnd
)
72 if (G_UNLIKELY(dialog_timer
!= 0))
74 KillTimer(hwnd
, dialog_timer
);
81 win32_dialog_update_main_window(HWND hwnd
, UINT uMsg
, UINT_PTR idEvent
, DWORD dwTime
)
85 /* Pump the main window loop a bit, but not enough to lock-up.
86 * The typical `while(gtk_events_pending()) gtk_main_iteration();`
87 * loop causes the entire operating system to lock-up. */
88 for (i
= 0; i
< 4 && gtk_events_pending(); i
++)
91 /* Cancel any pending timers since we just did an update */
92 win32_dialog_reset_timer(hwnd
);
96 G_INLINE_FUNC UINT_PTR
win32_dialog_queue_main_window_redraw(HWND dlg
, UINT msg
,
97 WPARAM wParam
, LPARAM lParam
, gboolean postpone
)
101 /* Messages that likely mean the window below a dialog needs to be re-drawn. */
102 case WM_WINDOWPOSCHANGED
:
105 case WM_THEMECHANGED
:
108 win32_dialog_reset_timer(dlg
);
109 dialog_timer
= SetTimer(dlg
, 0, 33 /* around 30fps */, win32_dialog_update_main_window
);
112 win32_dialog_update_main_window(dlg
, msg
, wParam
, lParam
);
115 return 0; /* always let the default proc handle it */
119 /* This function is called for OPENFILENAME lpfnHook function and it establishes
120 * a timer that is reset each time which will update the main window loop eventually. */
121 UINT_PTR CALLBACK
win32_dialog_explorer_hook_proc(HWND dlg
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
123 return win32_dialog_queue_main_window_redraw(dlg
, msg
, wParam
, lParam
, TRUE
);
127 /* This function is called for old-school win32 dialogs that accept a proper
128 * lpfnHook function for all messages, it doesn't use a timer. */
129 UINT_PTR CALLBACK
win32_dialog_hook_proc(HWND dlg
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
131 return win32_dialog_queue_main_window_redraw(dlg
, msg
, wParam
, lParam
, FALSE
);
135 static wchar_t *get_file_filters(void)
139 static wchar_t title
[4096];
140 GString
*str
= g_string_sized_new(100);
141 GString
*all_patterns
= g_string_sized_new(100);
145 /* create meta file filter "All files" */
146 g_string_append_printf(str
, "%s\t*\t", _("All files"));
147 /* create meta file filter "All Source" (skip GEANY_FILETYPES_NONE) */
148 for (i
= GEANY_FILETYPES_NONE
+ 1; i
< filetypes_array
->len
; i
++)
150 for (j
= 0; filetypes
[i
]->pattern
[j
] != NULL
; j
++)
152 g_string_append(all_patterns
, filetypes
[i
]->pattern
[j
]);
153 g_string_append_c(all_patterns
, ';');
156 g_string_append_printf(str
, "%s\t%s\t", _("All Source"), all_patterns
->str
);
157 g_string_free(all_patterns
, TRUE
);
158 /* add 'usual' filetypes */
159 foreach_slist(node
, filetypes_by_title
)
161 GeanyFiletype
*ft
= node
->data
;
163 if (G_UNLIKELY(ft
->id
== GEANY_FILETYPES_NONE
))
165 tmp
= g_strjoinv(";", ft
->pattern
);
166 g_string_append_printf(str
, "%s\t%s\t", ft
->title
, tmp
);
169 g_string_append_c(str
, '\t'); /* the final \0 byte to mark the end of the string */
171 g_string_free(str
, FALSE
);
173 /* replace all "\t"s by \0 */
174 len
= strlen(string
);
175 g_strdelimit(string
, "\t", '\0');
176 g_assert(string
[len
- 1] == 0x0);
177 MultiByteToWideChar(CP_UTF8
, 0, string
, len
, title
, G_N_ELEMENTS(title
));
184 static wchar_t *get_file_filter_all_files(void)
187 static wchar_t title
[4096];
190 /* create meta file filter "All files" */
191 filter
= g_strdup_printf("%s\t*\t", _("All files"));
193 len
= strlen(filter
);
194 g_strdelimit(filter
, "\t", '\0');
195 g_assert(filter
[len
- 1] == 0x0);
196 MultiByteToWideChar(CP_UTF8
, 0, filter
, len
, title
, G_N_ELEMENTS(title
));
203 static wchar_t *get_filters(gboolean project_files
)
207 static wchar_t title
[1024];
211 string
= g_strconcat(_("Geany project files"), "\t", "*." GEANY_PROJECT_EXT
, "\t",
212 _("All files"), "\t", "*", "\t", NULL
);
216 string
= g_strconcat(_("Executables"), "\t", "*.exe;*.bat;*.cmd", "\t",
217 _("All files"), "\t", "*", "\t", NULL
);
220 /* replace all "\t"s by \0 */
221 len
= strlen(string
);
222 g_strdelimit(string
, "\t", '\0');
223 g_assert(string
[len
- 1] == 0x0);
224 MultiByteToWideChar(CP_UTF8
, 0, string
, len
, title
, G_N_ELEMENTS(title
));
231 /* Converts the given UTF-8 filename or directory name into something usable for Windows and
232 * returns the directory part of the given filename. */
233 static wchar_t *get_dir_for_path(const gchar
*utf8_filename
)
235 static wchar_t w_dir
[MAX_PATH
];
238 if (g_file_test(utf8_filename
, G_FILE_TEST_IS_DIR
))
239 result
= (gchar
*) utf8_filename
;
241 result
= g_path_get_dirname(utf8_filename
);
243 MultiByteToWideChar(CP_UTF8
, 0, result
, -1, w_dir
, G_N_ELEMENTS(w_dir
));
245 if (result
!= utf8_filename
)
252 /* Callback function for setting the initial directory of the folder open dialog. This could also
253 * be done with BROWSEINFO.pidlRoot and SHParseDisplayName but SHParseDisplayName is not available
254 * on systems below Windows XP. So, we go the hard way by creating a callback which will set up the
255 * folder when the dialog is initialised. Yeah, I like Windows. */
256 INT CALLBACK
BrowseCallbackProc(HWND hwnd
, UINT uMsg
, LPARAM lp
, LPARAM pData
)
258 win32_dialog_hook_proc(hwnd
, uMsg
, lp
, pData
);
261 case BFFM_INITIALIZED
:
263 SendMessageW(hwnd
, BFFM_SETSELECTIONW
, TRUE
, pData
);
266 case BFFM_SELCHANGED
:
268 /* set the status window to the currently selected path. */
269 static wchar_t szDir
[MAX_PATH
];
270 if (SHGetPathFromIDListW((LPITEMIDLIST
) lp
, szDir
))
272 SendMessageW(hwnd
, BFFM_SETSTATUSTEXTW
, 0, (LPARAM
) szDir
);
281 /* Shows a folder selection dialog.
282 * initial_dir is expected in UTF-8
283 * The selected folder name is returned. */
284 gchar
*win32_show_folder_dialog(GtkWidget
*parent
, const gchar
*title
, const gchar
*initial_dir
)
288 gchar
*result
= NULL
;
289 wchar_t fname
[MAX_PATH
];
290 wchar_t w_title
[512];
292 MultiByteToWideChar(CP_UTF8
, 0, title
, -1, w_title
, G_N_ELEMENTS(w_title
));
295 parent
= main_widgets
.window
;
297 memset(&bi
, 0, sizeof bi
);
298 bi
.hwndOwner
= GDK_WINDOW_HWND(gtk_widget_get_window(parent
));
300 bi
.lpszTitle
= w_title
;
301 bi
.lpfn
= BrowseCallbackProc
;
302 bi
.lParam
= (LPARAM
) get_dir_for_path(initial_dir
);
303 bi
.ulFlags
= BIF_DONTGOBELOWDOMAIN
| BIF_RETURNONLYFSDIRS
| BIF_STATUSTEXT
| BIF_USENEWUI
;
305 pidl
= SHBrowseForFolderW(&bi
);
307 /* convert the strange Windows folder list item something into an usual path string ;-) */
310 if (SHGetPathFromIDListW(pidl
, fname
))
312 result
= g_malloc0(MAX_PATH
* 2);
313 WideCharToMultiByte(CP_UTF8
, 0, fname
, -1, result
, MAX_PATH
* 2, NULL
, NULL
);
321 /* Shows a file open dialog.
322 * If allow_new_file is set, the file to be opened doesn't have to exist.
323 * initial_dir is expected in UTF-8
324 * The selected file name is returned.
325 * If project_file_filter is set, the file open dialog will have a file filter for Geany project
326 * files, a filter for executables otherwise. */
327 gchar
*win32_show_project_open_dialog(GtkWidget
*parent
, const gchar
*title
,
328 const gchar
*initial_dir
, gboolean allow_new_file
,
329 gboolean project_file_filter
)
333 wchar_t fname
[MAX_PATH
];
334 wchar_t w_title
[512];
339 MultiByteToWideChar(CP_UTF8
, 0, title
, -1, w_title
, G_N_ELEMENTS(w_title
));
342 parent
= main_widgets
.window
;
344 /* initialise file dialog info struct */
345 memset(&of
, 0, sizeof of
);
346 of
.lStructSize
= sizeof of
;
347 of
.hwndOwner
= GDK_WINDOW_HWND(gtk_widget_get_window(parent
));
348 of
.lpstrFilter
= get_filters(project_file_filter
);
350 of
.lpstrCustomFilter
= NULL
;
352 of
.lpstrFile
= fname
;
353 of
.lpstrInitialDir
= get_dir_for_path(initial_dir
);
355 of
.lpstrFileTitle
= NULL
;
356 of
.lpstrTitle
= w_title
;
357 of
.lpstrDefExt
= L
"";
358 of
.Flags
= OFN_PATHMUSTEXIST
| OFN_EXPLORER
| OFN_HIDEREADONLY
|
359 OFN_ENABLEHOOK
| OFN_ENABLESIZING
;
360 of
.lpfnHook
= win32_dialog_explorer_hook_proc
;
361 if (! allow_new_file
)
362 of
.Flags
|= OFN_FILEMUSTEXIST
;
364 retval
= GetOpenFileNameW(&of
);
368 if (CommDlgExtendedError())
371 error
= g_strdup_printf("File dialog box error (%x)", (int)CommDlgExtendedError());
372 win32_message_dialog(NULL
, GTK_MESSAGE_ERROR
, error
);
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
);
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
)
393 wchar_t fname
[MAX_PATH
];
394 wchar_t w_dir
[MAX_PATH
];
395 wchar_t w_title
[512];
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 of
.lStructSize
= sizeof of
;
407 of
.hwndOwner
= GDK_WINDOW_HWND(gtk_widget_get_window(GTK_WIDGET(parent
)));
408 of
.lpstrFilter
= get_file_filters();
410 of
.lpstrCustomFilter
= NULL
;
411 of
.nFilterIndex
= GEANY_FILETYPES_NONE
+ 1;
412 of
.lpstrFile
= fname
;
413 of
.lpstrInitialDir
= (initial_dir
!= NULL
) ? w_dir
: NULL
;
415 of
.lpstrFileTitle
= NULL
;
416 of
.lpstrTitle
= w_title
;
417 of
.lpstrDefExt
= L
"";
418 of
.Flags
= OFN_ALLOWMULTISELECT
| OFN_FILEMUSTEXIST
| OFN_EXPLORER
|
419 OFN_ENABLEHOOK
| OFN_ENABLESIZING
;
420 of
.lpfnHook
= win32_dialog_explorer_hook_proc
;
422 retval
= GetOpenFileNameW(&of
);
426 if (CommDlgExtendedError())
429 g_snprintf(error
, sizeof error
, "File dialog box error (%x)", (int)CommDlgExtendedError());
430 win32_message_dialog(NULL
, GTK_MESSAGE_ERROR
, error
);
435 x
= of
.nFileOffset
- 1;
436 if (x
!= wcslen(fname
))
437 { /* open a single file */
438 WideCharToMultiByte(CP_UTF8
, 0, fname
, -1, tmp
, sizeof(tmp
), NULL
, NULL
);
439 document_open_file(tmp
, of
.Flags
& OFN_READONLY
, NULL
, NULL
);
442 { /* open multiple files */
443 gchar file_name
[MAX_PATH
];
444 gchar dir_name
[MAX_PATH
];
446 WideCharToMultiByte(CP_UTF8
, 0, fname
, of
.nFileOffset
,
447 dir_name
, sizeof(dir_name
), NULL
, NULL
);
455 WideCharToMultiByte(CP_UTF8
, 0, fname
+ x
+ 1, -1,
456 tmp
, sizeof(tmp
), NULL
, NULL
);
457 g_snprintf(file_name
, 511, "%s\\%s", dir_name
, tmp
);
459 /* convert the resulting filename into UTF-8 */
460 document_open_file(file_name
, of
.Flags
& OFN_READONLY
, NULL
, NULL
);
465 return (retval
!= 0);
469 gchar
*win32_show_document_save_as_dialog(GtkWindow
*parent
, const gchar
*title
,
475 wchar_t w_file
[MAX_PATH
];
476 wchar_t w_title
[512];
481 /* Convert the name of the file for of.lpstrFile */
482 n
= MultiByteToWideChar(CP_UTF8
, 0, DOC_FILENAME(doc
), -1, w_file
, G_N_ELEMENTS(w_file
));
484 /* If creating a new file name, convert and append the extension if any */
485 if (! doc
->file_name
&& doc
->file_type
&& doc
->file_type
->extension
&&
486 n
+ 1 < (int)G_N_ELEMENTS(w_file
))
488 w_file
[n
- 1] = L
'.';
489 MultiByteToWideChar(CP_UTF8
, 0, doc
->file_type
->extension
, -1, &w_file
[n
],
490 G_N_ELEMENTS(w_file
) - n
- 1);
493 MultiByteToWideChar(CP_UTF8
, 0, title
, -1, w_title
, G_N_ELEMENTS(w_title
));
495 /* initialise file dialog info struct */
496 memset(&of
, 0, sizeof of
);
497 of
.lStructSize
= sizeof of
;
498 of
.hwndOwner
= GDK_WINDOW_HWND(gtk_widget_get_window(GTK_WIDGET(parent
)));
500 of
.lpstrFilter
= get_file_filter_all_files();
501 of
.lpstrCustomFilter
= NULL
;
504 of
.lpstrFile
= w_file
;
506 of
.lpstrFileTitle
= NULL
;
507 of
.lpstrTitle
= w_title
;
508 of
.lpstrDefExt
= L
"";
509 of
.Flags
= OFN_OVERWRITEPROMPT
| OFN_EXPLORER
| OFN_ENABLEHOOK
| OFN_ENABLESIZING
;
510 of
.lpfnHook
= win32_dialog_explorer_hook_proc
;
511 retval
= GetSaveFileNameW(&of
);
515 if (CommDlgExtendedError())
517 gchar
*error
= g_strdup_printf(
518 "File dialog box error (%x)", (gint
) CommDlgExtendedError());
519 win32_message_dialog(NULL
, GTK_MESSAGE_ERROR
, error
);
525 WideCharToMultiByte(CP_UTF8
, 0, w_file
, -1, tmp
, sizeof(tmp
), NULL
, NULL
);
527 return g_strdup(tmp
);
531 /* initial_dir can be NULL to use the current working directory.
532 * Returns: the selected filename */
533 gchar
*win32_show_file_dialog(GtkWindow
*parent
, const gchar
*title
, const gchar
*initial_file
)
538 wchar_t w_file
[MAX_PATH
];
539 wchar_t w_title
[512];
543 if (initial_file
!= NULL
)
544 MultiByteToWideChar(CP_UTF8
, 0, initial_file
, -1, w_file
, G_N_ELEMENTS(w_file
));
546 MultiByteToWideChar(CP_UTF8
, 0, title
, -1, w_title
, G_N_ELEMENTS(w_title
));
548 /* initialise file dialog info struct */
549 memset(&of
, 0, sizeof of
);
550 of
.lStructSize
= sizeof of
;
551 of
.hwndOwner
= GDK_WINDOW_HWND(gtk_widget_get_window(GTK_WIDGET(parent
)));
553 of
.lpstrFile
= w_file
;
555 of
.lpstrFileTitle
= NULL
;
556 of
.lpstrTitle
= w_title
;
557 of
.lpstrDefExt
= L
"";
558 of
.Flags
= OFN_FILEMUSTEXIST
| OFN_EXPLORER
| OFN_ENABLEHOOK
| OFN_ENABLESIZING
;
559 of
.lpfnHook
= win32_dialog_explorer_hook_proc
;
560 retval
= GetOpenFileNameW(&of
);
564 if (CommDlgExtendedError())
566 gchar
*error
= g_strdup_printf(
567 "File dialog box error (%x)", (gint
) CommDlgExtendedError());
568 win32_message_dialog(NULL
, GTK_MESSAGE_ERROR
, error
);
574 WideCharToMultiByte(CP_UTF8
, 0, w_file
, -1, tmp
, sizeof(tmp
), NULL
, NULL
);
576 return g_strdup(tmp
);
580 void win32_show_font_dialog(void)
584 LOGFONT lf
; /* logical font structure */
586 memset(&lf
, 0, sizeof lf
);
587 /* TODO: init lf members */
589 memset(&cf
, 0, sizeof cf
);
590 cf
.lStructSize
= sizeof cf
;
591 cf
.hwndOwner
= GDK_WINDOW_HWND(gtk_widget_get_window(main_widgets
.window
));
593 /* support CF_APPLY? */
594 cf
.Flags
= CF_NOSCRIPTSEL
| CF_FORCEFONTEXIST
| CF_INITTOLOGFONTSTRUCT
| CF_SCREENFONTS
| CF_ENABLEHOOK
;
595 cf
.lpfnHook
= win32_dialog_hook_proc
;
597 retval
= ChooseFont(&cf
);
601 gchar
*editorfont
= g_strdup_printf("%s %d", lf
.lfFaceName
, (cf
.iPointSize
/ 10));
602 ui_set_editor_font(editorfont
);
608 void win32_show_color_dialog(const gchar
*colour
)
611 static COLORREF acr_cust_clr
[16];
612 static DWORD rgb_current
;
613 gchar
*hex
= g_malloc0(12);
614 GeanyDocument
*doc
= document_get_current();
616 /* Initialize CHOOSECOLOR */
617 memset(&cc
, 0, sizeof cc
);
618 cc
.lStructSize
= sizeof(cc
);
619 cc
.hwndOwner
= GDK_WINDOW_HWND(gtk_widget_get_window(main_widgets
.window
));
620 cc
.lpCustColors
= (LPDWORD
) acr_cust_clr
;
621 cc
.rgbResult
= (colour
!= NULL
) ? utils_parse_color_to_bgr(colour
) : 0;
622 cc
.Flags
= CC_FULLOPEN
| CC_RGBINIT
| CC_ENABLEHOOK
;
623 cc
.lpfnHook
= win32_dialog_hook_proc
;
625 if (ChooseColor(&cc
))
627 rgb_current
= cc
.rgbResult
;
628 g_snprintf(hex
, 11, "#%02X%02X%02X",
629 (guint
) (utils_scale_round(GetRValue(rgb_current
), 255)),
630 (guint
) (utils_scale_round(GetGValue(rgb_current
), 255)),
631 (guint
) (utils_scale_round(GetBValue(rgb_current
), 255)));
633 editor_insert_color(doc
->editor
, hex
);
639 void win32_show_pref_file_dialog(GtkEntry
*item
)
643 wchar_t fname
[MAX_PATH
];
645 gchar
**field
, *filename
;
649 /* cut the options from the command line */
650 field
= g_strsplit(gtk_entry_get_text(GTK_ENTRY(item
)), " ", 2);
653 filename
= g_find_program_in_path(field
[0]);
654 if (filename
!= NULL
&& g_file_test(filename
, G_FILE_TEST_EXISTS
))
656 MultiByteToWideChar(CP_UTF8
, 0, filename
, -1, fname
, G_N_ELEMENTS(fname
));
661 /* initialize file dialog info struct */
662 memset(&of
, 0, sizeof of
);
663 of
.lStructSize
= sizeof of
;
664 of
.hwndOwner
= GDK_WINDOW_HWND(gtk_widget_get_window(ui_widgets
.prefs_dialog
));
666 of
.lpstrFilter
= get_filters(FALSE
);
667 of
.lpstrCustomFilter
= NULL
;
670 of
.lpstrFile
= fname
;
672 of
.lpstrFileTitle
= NULL
;
673 /*of.lpstrInitialDir = g_get_home_dir();*/
674 of
.lpstrInitialDir
= NULL
;
675 of
.lpstrTitle
= NULL
;
676 of
.lpstrDefExt
= L
"exe";
677 of
.Flags
= OFN_HIDEREADONLY
| OFN_FILEMUSTEXIST
| OFN_EXPLORER
|
678 OFN_ENABLEHOOK
| OFN_ENABLESIZING
;
679 of
.lpfnHook
= win32_dialog_explorer_hook_proc
;
680 retval
= GetOpenFileNameW(&of
);
684 if (CommDlgExtendedError())
687 g_snprintf(error
, sizeof error
, "File dialog box error (%x)", (int)CommDlgExtendedError());
688 win32_message_dialog(NULL
, GTK_MESSAGE_ERROR
, error
);
694 len
= WideCharToMultiByte(CP_UTF8
, 0, fname
, -1, tmp
, sizeof(tmp
), NULL
, NULL
);
695 if ((of
.nFileOffset
- 1) != len
)
697 if (g_strv_length(field
) > 1)
698 /* add the command line args of the old command */
699 /** TODO this fails badly when the old command contained spaces, we need quoting here */
700 filename
= g_strconcat(tmp
, " ", field
[1], NULL
);
703 filename
= g_strdup(tmp
);
705 gtk_entry_set_text(GTK_ENTRY(item
), filename
);
712 /* Creates a native Windows message box of the given type and returns always TRUE
713 * or FALSE representing th pressed Yes or No button.
714 * If type is not GTK_MESSAGE_QUESTION, it returns always TRUE. */
715 gboolean
win32_message_dialog(GtkWidget
*parent
, GtkMessageType type
, const gchar
*msg
)
721 HWND parent_hwnd
= NULL
;
722 static wchar_t w_msg
[512];
723 static wchar_t w_title
[512];
727 case GTK_MESSAGE_ERROR
:
729 t
= MB_OK
| MB_ICONERROR
;
733 case GTK_MESSAGE_QUESTION
:
735 t
= MB_YESNO
| MB_ICONQUESTION
;
736 title
= _("Question");
739 case GTK_MESSAGE_WARNING
:
741 t
= MB_OK
| MB_ICONWARNING
;
742 title
= _("Warning");
747 t
= MB_OK
| MB_ICONINFORMATION
;
748 title
= _("Information");
753 /* convert the Unicode chars to wide chars */
754 /** TODO test if LANG == C then possibly skip conversion => g_win32_getlocale() */
755 MultiByteToWideChar(CP_UTF8
, 0, msg
, -1, w_msg
, G_N_ELEMENTS(w_msg
));
756 MultiByteToWideChar(CP_UTF8
, 0, title
, -1, w_title
, G_N_ELEMENTS(w_title
));
759 parent_hwnd
= GDK_WINDOW_HWND(gtk_widget_get_window(parent
));
760 else if (main_widgets
.window
!= NULL
)
761 parent_hwnd
= GDK_WINDOW_HWND(gtk_widget_get_window(main_widgets
.window
));
763 /* display the message box */
764 rc
= MessageBoxW(parent_hwnd
, w_msg
, w_title
, t
);
766 if (type
== GTK_MESSAGE_QUESTION
&& rc
!= IDYES
)
773 /* Little wrapper for _waccess(), returns errno or 0 if there was no error */
774 gint
win32_check_write_permission(const gchar
*dir
)
776 static wchar_t w_dir
[MAX_PATH
];
777 MultiByteToWideChar(CP_UTF8
, 0, dir
, -1, w_dir
, G_N_ELEMENTS(w_dir
));
778 if (_waccess(w_dir
, R_OK
| W_OK
) != 0)
785 /* Just a simple wrapper function to open a browser window */
786 void win32_open_browser(const gchar
*uri
)
789 if (strncmp(uri
, "file://", 7) == 0)
792 if (strchr(uri
, ':') != NULL
)
798 ret
= (gint
) ShellExecute(NULL
, "open", uri
, NULL
, NULL
, SW_SHOWNORMAL
);
801 gchar
*err
= g_win32_error_message(GetLastError());
802 ui_set_statusbar(TRUE
, _("Failed to open URI \"%s\": %s"), uri
, err
);
803 g_warning("ShellExecute failed opening \"%s\" (code %d): %s", uri
, ret
, err
);
809 static FILE *open_std_handle(DWORD handle
, const char *mode
)
815 lStdHandle
= GetStdHandle(handle
);
816 if (lStdHandle
== INVALID_HANDLE_VALUE
)
818 gchar
*err
= g_win32_error_message(GetLastError());
819 g_warning("GetStdHandle(%ld) failed: %s", (long)handle
, err
);
823 hConHandle
= _open_osfhandle((intptr_t)lStdHandle
, _O_TEXT
);
824 if (hConHandle
== -1)
826 gchar
*err
= g_win32_error_message(GetLastError());
827 g_warning("_open_osfhandle(handle(%ld), _O_TEXT) failed: %s", (long)handle
, err
);
831 fp
= _fdopen(hConHandle
, mode
);
834 gchar
*err
= g_win32_error_message(GetLastError());
835 g_warning("_fdopen(%d, \"%s\") failed: %s", hConHandle
, mode
, err
);
839 if (setvbuf(fp
, NULL
, _IONBF
, 0) != 0)
841 gchar
*err
= g_win32_error_message(GetLastError());
842 g_warning("setvbuf(%p, NULL, _IONBF, 0) failed: %s", fp
, err
);
852 static void debug_setup_console(void)
854 static const WORD MAX_CONSOLE_LINES
= 500;
855 CONSOLE_SCREEN_BUFFER_INFO coninfo
;
858 /* allocate a console for this app */
861 /* set the screen buffer to be big enough to let us scroll text */
862 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE
), &coninfo
);
863 coninfo
.dwSize
.Y
= MAX_CONSOLE_LINES
;
864 SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE
), coninfo
.dwSize
);
866 /* redirect unbuffered STDOUT to the console */
867 fp
= open_std_handle(STD_OUTPUT_HANDLE
, "w");
871 /* redirect unbuffered STDERR to the console */
872 fp
= open_std_handle(STD_ERROR_HANDLE
, "w");
876 /* redirect unbuffered STDIN to the console */
877 fp
= open_std_handle(STD_INPUT_HANDLE
, "r");
883 void win32_init_debug_code(void)
887 /* create a console window to get log messages on Windows,
888 * especially useful when generating tags files */
889 debug_setup_console();
894 gchar
*win32_expand_environment_variables(const gchar
*str
)
896 gchar expCmdline
[32768]; /* 32768 is the limit for ExpandEnvironmentStrings() */
898 if (ExpandEnvironmentStrings((LPCTSTR
) str
, (LPTSTR
) expCmdline
, sizeof(expCmdline
)) != 0)
899 return g_strdup(expCmdline
);
901 return g_strdup(str
);
905 /* From GDK (they got it from MS Knowledge Base article Q130698) */
906 static gboolean
resolve_link(HWND hWnd
, wchar_t *link
, gchar
**lpszPath
)
908 WIN32_FILE_ATTRIBUTE_DATA wfad
;
910 IShellLinkW
*pslW
= NULL
;
911 IPersistFile
*ppf
= NULL
;
915 /* Check if the file is empty first because IShellLink::Resolve for some reason succeeds
916 * with an empty file and returns an empty "link target". (#524151) */
917 if (!GetFileAttributesExW(link
, GetFileExInfoStandard
, &wfad
) ||
918 (wfad
.nFileSizeHigh
== 0 && wfad
.nFileSizeLow
== 0))
923 /* Assume failure to start with: */
928 hres
= CoCreateInstance(
929 &CLSID_ShellLink
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IShellLinkW
, &pslWV
);
933 /* The IShellLink interface supports the IPersistFile interface.
934 * Get an interface pointer to it. */
935 pslW
= (IShellLinkW
*) pslWV
;
936 hres
= pslW
->lpVtbl
->QueryInterface(pslW
, &IID_IPersistFile
, &ppfV
);
942 ppf
= (IPersistFile
*) ppfV
;
943 hres
= ppf
->lpVtbl
->Load(ppf
, link
, STGM_READ
);
948 /* Resolve the link by calling the Resolve() interface function. */
949 hres
= pslW
->lpVtbl
->Resolve(pslW
, hWnd
, SLR_ANY_MATCH
| SLR_NO_UI
);
954 wchar_t wtarget
[MAX_PATH
];
956 hres
= pslW
->lpVtbl
->GetPath(pslW
, wtarget
, MAX_PATH
, NULL
, 0);
958 *lpszPath
= g_utf16_to_utf8(wtarget
, -1, NULL
, NULL
, NULL
);
962 ppf
->lpVtbl
->Release(ppf
);
965 pslW
->lpVtbl
->Release(pslW
);
967 return SUCCEEDED(hres
);
971 /* Checks whether file_name is a Windows shortcut. file_name is expected in UTF-8 encoding.
972 * If file_name is a Windows shortcut, it returns the target in UTF-8 encoding.
973 * If it is not a shortcut, it returns a newly allocated copy of file_name. */
974 gchar
*win32_get_shortcut_target(const gchar
*file_name
)
977 wchar_t *wfilename
= g_utf8_to_utf16(file_name
, -1, NULL
, NULL
, NULL
);
980 if (main_widgets
.window
!= NULL
)
982 GdkWindow
*window
= gtk_widget_get_window(main_widgets
.window
);
984 hWnd
= GDK_WINDOW_HWND(window
);
987 resolve_link(hWnd
, wfilename
, &path
);
991 return g_strdup(file_name
);
997 void win32_set_working_directory(const gchar
*dir
)
999 SetCurrentDirectory(dir
);
1003 gchar
*win32_get_installation_dir(void)
1005 return g_win32_get_package_installation_directory_of_module(NULL
);
1009 gchar
*win32_get_user_config_dir(void)
1012 wchar_t path
[MAX_PATH
];
1014 hr
= SHGetFolderPathAndSubDirW(NULL
, CSIDL_APPDATA
| CSIDL_FLAG_CREATE
, NULL
, SHGFP_TYPE_CURRENT
, L
"geany", path
);
1017 // GLib always uses UTF-8 for filename encoding on Windows
1018 int u8_size
= WideCharToMultiByte(CP_UTF8
, 0, path
, -1, NULL
, 0, NULL
, NULL
);
1021 gchar
*u8_path
= g_malloc0(u8_size
+ 1);
1022 if (u8_path
!= NULL
&&
1023 WideCharToMultiByte(CP_UTF8
, 0, path
, -1, u8_path
, u8_size
, NULL
, NULL
))
1031 g_warning("Failed to retrieve Windows config dir, falling back to default");
1032 return g_build_filename(g_get_user_config_dir(), "geany", NULL
);
1036 /* Retrieve the console codepage
1037 * In case GetConsoleOutputCP() returns 0 (i.e. the application doesn't have an own console window
1038 * fallback to GetOEMCP(). */
1039 guint
win32_get_console_codepage(void)
1041 guint codepage
= GetConsoleOutputCP();
1043 codepage
= GetOEMCP();