geany  1.38
About: Geany is a text editor (using GTK2) with basic features of an integrated development environment (syntax highlighting, code folding, symbol name auto-completion, ...). F: office T: editor programming GTK+ IDE
  Fossies Dox: geany-1.38.tar.bz2  ("unofficial" and yet experimental doxygen-generated source code documentation)  

win32.c
Go to the documentation of this file.
1/*
2 * win32.c - this file is part of Geany, a fast and lightweight IDE
3 *
4 * Copyright 2005 The Geany contributors
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21/*
22 * Special functions for the win32 platform, to provide native dialogs.
23 */
24
25#ifdef HAVE_CONFIG_H
26# include "config.h"
27#endif
28
29/* Need Windows XP for SHGetFolderPathAndSubDirW */
30#define _WIN32_WINNT 0x0501
31/* Needed for SHGFP_TYPE */
32#define _WIN32_IE 0x0500
33
34#include "win32.h"
35
36#ifdef G_OS_WIN32
37
38#include "dialogs.h"
39#include "document.h"
40#include "editor.h"
41#include "filetypes.h"
42#include "project.h"
43#include "support.h"
44#include "ui_utils.h"
45#include "utils.h"
46
47#include <ctype.h>
48#include <fcntl.h>
49#include <io.h>
50#include <math.h>
51#include <stdlib.h>
52#include <string.h>
53
54#include <windows.h>
55#include <commdlg.h>
56#include <shellapi.h>
57#include <shlobj.h>
58
59#include <glib/gstdio.h>
60#include <gdk/gdkwin32.h>
61
62
63/* The timer handle used to refresh windows below modal native dialogs. If
64 * ever more than one dialog can be shown at a time, this needs to be changed
65 * to be for specific dialogs. */
66static UINT_PTR dialog_timer = 0;
67
68
69G_INLINE_FUNC void win32_dialog_reset_timer(HWND hwnd)
70{
71 if (G_UNLIKELY(dialog_timer != 0))
72 {
73 KillTimer(hwnd, dialog_timer);
74 dialog_timer = 0;
75 }
76}
77
78
79VOID CALLBACK
80win32_dialog_update_main_window(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
81{
82 gint i;
83
84 /* Pump the main window loop a bit, but not enough to lock-up.
85 * The typical `while(gtk_events_pending()) gtk_main_iteration();`
86 * loop causes the entire operating system to lock-up. */
87 for (i = 0; i < 4 && gtk_events_pending(); i++)
88 gtk_main_iteration();
89
90 /* Cancel any pending timers since we just did an update */
91 win32_dialog_reset_timer(hwnd);
92}
93
94
95G_INLINE_FUNC UINT_PTR win32_dialog_queue_main_window_redraw(HWND dlg, UINT msg,
96 WPARAM wParam, LPARAM lParam, gboolean postpone)
97{
98 switch (msg)
99 {
100 /* Messages that likely mean the window below a dialog needs to be re-drawn. */
101 case WM_WINDOWPOSCHANGED:
102 case WM_MOVE:
103 case WM_SIZE:
104 case WM_THEMECHANGED:
105 if (postpone)
106 {
107 win32_dialog_reset_timer(dlg);
108 dialog_timer = SetTimer(dlg, 0, 33 /* around 30fps */, win32_dialog_update_main_window);
109 }
110 else
111 win32_dialog_update_main_window(dlg, msg, wParam, lParam);
112 break;
113 }
114 return 0; /* always let the default proc handle it */
115}
116
117
118/* This function is called for OPENFILENAME lpfnHook function and it establishes
119 * a timer that is reset each time which will update the main window loop eventually. */
120UINT_PTR CALLBACK win32_dialog_explorer_hook_proc(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam)
121{
122 return win32_dialog_queue_main_window_redraw(dlg, msg, wParam, lParam, TRUE);
123}
124
125
126/* This function is called for old-school win32 dialogs that accept a proper
127 * lpfnHook function for all messages, it doesn't use a timer. */
128UINT_PTR CALLBACK win32_dialog_hook_proc(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam)
129{
130 return win32_dialog_queue_main_window_redraw(dlg, msg, wParam, lParam, FALSE);
131}
132
133
134static wchar_t *get_file_filters(void)
135{
136 gchar *string;
137 guint i, j, len;
138 static wchar_t title[4096];
139 GString *str = g_string_sized_new(100);
140 GString *all_patterns = g_string_sized_new(100);
141 GSList *node;
142 gchar *tmp;
143
144 /* create meta file filter "All files" */
145 g_string_append_printf(str, "%s\t*\t", _("All files"));
146 /* create meta file filter "All Source" (skip GEANY_FILETYPES_NONE) */
147 for (i = GEANY_FILETYPES_NONE + 1; i < filetypes_array->len; i++)
148 {
149 for (j = 0; filetypes[i]->pattern[j] != NULL; j++)
150 {
151 g_string_append(all_patterns, filetypes[i]->pattern[j]);
152 g_string_append_c(all_patterns, ';');
153 }
154 }
155 g_string_append_printf(str, "%s\t%s\t", _("All Source"), all_patterns->str);
156 g_string_free(all_patterns, TRUE);
157 /* add 'usual' filetypes */
159 {
160 GeanyFiletype *ft = node->data;
161
162 if (G_UNLIKELY(ft->id == GEANY_FILETYPES_NONE))
163 continue;
164 tmp = g_strjoinv(";", ft->pattern);
165 g_string_append_printf(str, "%s\t%s\t", ft->title, tmp);
166 g_free(tmp);
167 }
168 g_string_append_c(str, '\t'); /* the final \0 byte to mark the end of the string */
169 string = str->str;
170 g_string_free(str, FALSE);
171
172 /* replace all "\t"s by \0 */
173 len = strlen(string);
174 g_strdelimit(string, "\t", '\0');
175 g_assert(string[len - 1] == 0x0);
176 MultiByteToWideChar(CP_UTF8, 0, string, len, title, G_N_ELEMENTS(title));
177 g_free(string);
178
179 return title;
180}
181
182
183static wchar_t *get_file_filter_all_files(void)
184{
185 guint len;
186 static wchar_t title[4096];
187 gchar *filter;
188
189 /* create meta file filter "All files" */
190 filter = g_strdup_printf("%s\t*\t", _("All files"));
191
192 len = strlen(filter);
193 g_strdelimit(filter, "\t", '\0');
194 g_assert(filter[len - 1] == 0x0);
195 MultiByteToWideChar(CP_UTF8, 0, filter, len, title, G_N_ELEMENTS(title));
196 g_free(filter);
197
198 return title;
199}
200
201
202static wchar_t *get_filters(gboolean project_files)
203{
204 gchar *string;
205 gint len;
206 static wchar_t title[1024];
207
208 if (project_files)
209 {
210 string = g_strconcat(_("Geany project files"), "\t", "*." GEANY_PROJECT_EXT, "\t",
211 _("All files"), "\t", "*", "\t", NULL);
212 }
213 else
214 {
215 string = g_strconcat(_("Executables"), "\t", "*.exe;*.bat;*.cmd", "\t",
216 _("All files"), "\t", "*", "\t", NULL);
217 }
218
219 /* replace all "\t"s by \0 */
220 len = strlen(string);
221 g_strdelimit(string, "\t", '\0');
222 g_assert(string[len - 1] == 0x0);
223 MultiByteToWideChar(CP_UTF8, 0, string, len, title, G_N_ELEMENTS(title));
224 g_free(string);
225
226 return title;
227}
228
229
230/* Converts the given UTF-8 filename or directory name into something usable for Windows and
231 * returns the directory part of the given filename. */
232static wchar_t *get_dir_for_path(const gchar *utf8_filename)
233{
234 static wchar_t w_dir[MAX_PATH];
235 gchar *result;
236
237 if (g_file_test(utf8_filename, G_FILE_TEST_IS_DIR))
238 result = (gchar*) utf8_filename;
239 else
240 result = g_path_get_dirname(utf8_filename);
241
242 MultiByteToWideChar(CP_UTF8, 0, result, -1, w_dir, G_N_ELEMENTS(w_dir));
243
244 if (result != utf8_filename)
245 g_free(result);
246
247 return w_dir;
248}
249
250
251/* Callback function for setting the initial directory of the folder open dialog. This could also
252 * be done with BROWSEINFO.pidlRoot and SHParseDisplayName but SHParseDisplayName is not available
253 * on systems below Windows XP. So, we go the hard way by creating a callback which will set up the
254 * folder when the dialog is initialised. Yeah, I like Windows. */
255INT CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData)
256{
257 win32_dialog_hook_proc(hwnd, uMsg, lp, pData);
258 switch (uMsg)
259 {
260 case BFFM_INITIALIZED:
261 {
262 SendMessageW(hwnd, BFFM_SETSELECTIONW, TRUE, pData);
263 break;
264 }
265 case BFFM_SELCHANGED:
266 {
267 /* set the status window to the currently selected path. */
268 static wchar_t szDir[MAX_PATH];
269 if (SHGetPathFromIDListW((LPITEMIDLIST) lp, szDir))
270 {
271 SendMessageW(hwnd, BFFM_SETSTATUSTEXTW, 0, (LPARAM) szDir);
272 }
273 break;
274 }
275 }
276 return 0;
277}
278
279
280/* Shows a folder selection dialog.
281 * initial_dir is expected in UTF-8
282 * The selected folder name is returned. */
283gchar *win32_show_folder_dialog(GtkWidget *parent, const gchar *title, const gchar *initial_dir)
284{
285 BROWSEINFOW bi;
286 LPITEMIDLIST pidl;
287 gchar *result = NULL;
288 wchar_t fname[MAX_PATH];
289 wchar_t w_title[512];
290
291 MultiByteToWideChar(CP_UTF8, 0, title, -1, w_title, G_N_ELEMENTS(w_title));
292
293 if (parent == NULL)
294 parent = main_widgets.window;
295
296 memset(&bi, 0, sizeof bi);
297 bi.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(parent));
298 bi.pidlRoot = NULL;
299 bi.lpszTitle = w_title;
300 bi.lpfn = BrowseCallbackProc;
301 bi.lParam = (LPARAM) get_dir_for_path(initial_dir);
302 bi.ulFlags = BIF_DONTGOBELOWDOMAIN | BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT | BIF_USENEWUI;
303
304 pidl = SHBrowseForFolderW(&bi);
305
306 /* convert the strange Windows folder list item something into an usual path string ;-) */
307 if (pidl != NULL)
308 {
309 if (SHGetPathFromIDListW(pidl, fname))
310 {
311 result = g_malloc0(MAX_PATH * 2);
312 WideCharToMultiByte(CP_UTF8, 0, fname, -1, result, MAX_PATH * 2, NULL, NULL);
313 }
314 CoTaskMemFree(pidl);
315 }
316 return result;
317}
318
319
320/* Shows a file open dialog.
321 * If allow_new_file is set, the file to be opened doesn't have to exist.
322 * initial_dir is expected in UTF-8
323 * The selected file name is returned.
324 * If project_file_filter is set, the file open dialog will have a file filter for Geany project
325 * files, a filter for executables otherwise. */
326gchar *win32_show_project_open_dialog(GtkWidget *parent, const gchar *title,
327 const gchar *initial_dir, gboolean allow_new_file,
328 gboolean project_file_filter)
329{
330 OPENFILENAMEW of;
331 gint retval;
332 wchar_t fname[MAX_PATH];
333 wchar_t w_title[512];
334 gchar *filename;
335
336 fname[0] = '\0';
337
338 MultiByteToWideChar(CP_UTF8, 0, title, -1, w_title, G_N_ELEMENTS(w_title));
339
340 if (parent == NULL)
341 parent = main_widgets.window;
342
343 /* initialise file dialog info struct */
344 memset(&of, 0, sizeof of);
345 of.lStructSize = sizeof of;
346 of.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(parent));
347 of.lpstrFilter = get_filters(project_file_filter);
348
349 of.lpstrCustomFilter = NULL;
350 of.nFilterIndex = 0;
351 of.lpstrFile = fname;
352 of.lpstrInitialDir = get_dir_for_path(initial_dir);
353 of.nMaxFile = 2048;
354 of.lpstrFileTitle = NULL;
355 of.lpstrTitle = w_title;
356 of.lpstrDefExt = L"";
357 of.Flags = OFN_PATHMUSTEXIST | OFN_EXPLORER | OFN_HIDEREADONLY |
358 OFN_ENABLEHOOK | OFN_ENABLESIZING;
359 of.lpfnHook = win32_dialog_explorer_hook_proc;
360 if (! allow_new_file)
361 of.Flags |= OFN_FILEMUSTEXIST;
362
363 retval = GetOpenFileNameW(&of);
364
365 if (! retval)
366 {
367 if (CommDlgExtendedError())
368 {
369 gchar *error;
370 error = g_strdup_printf("File dialog box error (%x)", (int)CommDlgExtendedError());
371 win32_message_dialog(NULL, GTK_MESSAGE_ERROR, error);
372 g_free(error);
373 }
374 return NULL;
375 }
376 /* convert the resulting filename into UTF-8 (from whatever encoding it has at this moment) */
377 filename = g_malloc0(MAX_PATH * 2);
378 WideCharToMultiByte(CP_UTF8, 0, fname, -1, filename, MAX_PATH * 2, NULL, NULL);
379
380 return filename;
381}
382
383
384/* initial_dir can be NULL to use the current working directory.
385 * Returns: TRUE if the dialog was not cancelled. */
386gboolean win32_show_document_open_dialog(GtkWindow *parent, const gchar *title, const gchar *initial_dir)
387{
388 OPENFILENAMEW of;
389 gint retval;
390 guint x;
391 gchar tmp[MAX_PATH];
392 wchar_t fname[MAX_PATH];
393 wchar_t w_dir[MAX_PATH];
394 wchar_t w_title[512];
395
396 fname[0] = '\0';
397
398 if (initial_dir != NULL)
399 MultiByteToWideChar(CP_UTF8, 0, initial_dir, -1, w_dir, G_N_ELEMENTS(w_dir));
400
401 MultiByteToWideChar(CP_UTF8, 0, title, -1, w_title, G_N_ELEMENTS(w_title));
402
403 /* initialise file dialog info struct */
404 memset(&of, 0, sizeof of);
405 of.lStructSize = sizeof of;
406 of.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(GTK_WIDGET(parent)));
407 of.lpstrFilter = get_file_filters();
408
409 of.lpstrCustomFilter = NULL;
410 of.nFilterIndex = GEANY_FILETYPES_NONE + 1;
411 of.lpstrFile = fname;
412 of.lpstrInitialDir = (initial_dir != NULL) ? w_dir : NULL;
413 of.nMaxFile = 2048;
414 of.lpstrFileTitle = NULL;
415 of.lpstrTitle = w_title;
416 of.lpstrDefExt = L"";
417 of.Flags = OFN_ALLOWMULTISELECT | OFN_FILEMUSTEXIST | OFN_EXPLORER |
418 OFN_ENABLEHOOK | OFN_ENABLESIZING;
419 of.lpfnHook = win32_dialog_explorer_hook_proc;
420
421 retval = GetOpenFileNameW(&of);
422
423 if (!retval)
424 {
425 if (CommDlgExtendedError())
426 {
427 gchar error[100];
428 g_snprintf(error, sizeof error, "File dialog box error (%x)", (int)CommDlgExtendedError());
429 win32_message_dialog(NULL, GTK_MESSAGE_ERROR, error);
430 }
431 return FALSE;
432 }
433
434 x = of.nFileOffset - 1;
435 if (x != wcslen(fname))
436 { /* open a single file */
437 WideCharToMultiByte(CP_UTF8, 0, fname, -1, tmp, sizeof(tmp), NULL, NULL);
438 document_open_file(tmp, of.Flags & OFN_READONLY, NULL, NULL);
439 }
440 else
441 { /* open multiple files */
442 gchar file_name[MAX_PATH];
443 gchar dir_name[MAX_PATH];
444
445 WideCharToMultiByte(CP_UTF8, 0, fname, of.nFileOffset,
446 dir_name, sizeof(dir_name), NULL, NULL);
447 for (; ;)
448 {
449 if (! fname[x])
450 {
451 if (! fname[x + 1])
452 break;
453
454 WideCharToMultiByte(CP_UTF8, 0, fname + x + 1, -1,
455 tmp, sizeof(tmp), NULL, NULL);
456 g_snprintf(file_name, 511, "%s\\%s", dir_name, tmp);
457
458 /* convert the resulting filename into UTF-8 */
459 document_open_file(file_name, of.Flags & OFN_READONLY, NULL, NULL);
460 }
461 x++;
462 }
463 }
464 return (retval != 0);
465}
466
467
468gchar *win32_show_document_save_as_dialog(GtkWindow *parent, const gchar *title,
469 GeanyDocument *doc)
470{
471 OPENFILENAMEW of;
472 gint retval;
473 gchar tmp[MAX_PATH];
474 wchar_t w_file[MAX_PATH];
475 wchar_t w_title[512];
476 int n;
477
478 w_file[0] = '\0';
479
480 /* Convert the name of the file for of.lpstrFile */
481 n = MultiByteToWideChar(CP_UTF8, 0, DOC_FILENAME(doc), -1, w_file, G_N_ELEMENTS(w_file));
482
483 /* If creating a new file name, convert and append the extension if any */
484 if (! doc->file_name && doc->file_type && doc->file_type->extension &&
485 n + 1 < (int)G_N_ELEMENTS(w_file))
486 {
487 w_file[n - 1] = L'.';
488 MultiByteToWideChar(CP_UTF8, 0, doc->file_type->extension, -1, &w_file[n],
489 G_N_ELEMENTS(w_file) - n - 1);
490 }
491
492 MultiByteToWideChar(CP_UTF8, 0, title, -1, w_title, G_N_ELEMENTS(w_title));
493
494 /* initialise file dialog info struct */
495 memset(&of, 0, sizeof of);
496 of.lStructSize = sizeof of;
497 of.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(GTK_WIDGET(parent)));
498
499 of.lpstrFilter = get_file_filter_all_files();
500 of.lpstrCustomFilter = NULL;
501 of.nFilterIndex = 0;
502
503 of.lpstrFile = w_file;
504 of.nMaxFile = 2048;
505 of.lpstrFileTitle = NULL;
506 of.lpstrTitle = w_title;
507 of.lpstrDefExt = L"";
508 of.Flags = OFN_OVERWRITEPROMPT | OFN_EXPLORER | OFN_ENABLEHOOK | OFN_ENABLESIZING;
509 of.lpfnHook = win32_dialog_explorer_hook_proc;
510 retval = GetSaveFileNameW(&of);
511
512 if (! retval)
513 {
514 if (CommDlgExtendedError())
515 {
516 gchar *error = g_strdup_printf(
517 "File dialog box error (%x)", (gint) CommDlgExtendedError());
518 win32_message_dialog(NULL, GTK_MESSAGE_ERROR, error);
519 g_free(error);
520 }
521 return NULL;
522 }
523
524 WideCharToMultiByte(CP_UTF8, 0, w_file, -1, tmp, sizeof(tmp), NULL, NULL);
525
526 return g_strdup(tmp);
527}
528
529
530/* initial_dir can be NULL to use the current working directory.
531 * Returns: the selected filename */
532gchar *win32_show_file_dialog(GtkWindow *parent, const gchar *title, const gchar *initial_file)
533{
534 OPENFILENAMEW of;
535 gint retval;
536 gchar tmp[MAX_PATH];
537 wchar_t w_file[MAX_PATH];
538 wchar_t w_title[512];
539
540 w_file[0] = '\0';
541
542 if (initial_file != NULL)
543 MultiByteToWideChar(CP_UTF8, 0, initial_file, -1, w_file, G_N_ELEMENTS(w_file));
544
545 MultiByteToWideChar(CP_UTF8, 0, title, -1, w_title, G_N_ELEMENTS(w_title));
546
547 /* initialise file dialog info struct */
548 memset(&of, 0, sizeof of);
549 of.lStructSize = sizeof of;
550 of.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(GTK_WIDGET(parent)));
551
552 of.lpstrFile = w_file;
553 of.nMaxFile = 2048;
554 of.lpstrFileTitle = NULL;
555 of.lpstrTitle = w_title;
556 of.lpstrDefExt = L"";
557 of.Flags = OFN_FILEMUSTEXIST | OFN_EXPLORER | OFN_ENABLEHOOK | OFN_ENABLESIZING;
558 of.lpfnHook = win32_dialog_explorer_hook_proc;
559 retval = GetOpenFileNameW(&of);
560
561 if (! retval)
562 {
563 if (CommDlgExtendedError())
564 {
565 gchar *error = g_strdup_printf(
566 "File dialog box error (%x)", (gint) CommDlgExtendedError());
567 win32_message_dialog(NULL, GTK_MESSAGE_ERROR, error);
568 g_free(error);
569 }
570 return NULL;
571 }
572
573 WideCharToMultiByte(CP_UTF8, 0, w_file, -1, tmp, sizeof(tmp), NULL, NULL);
574
575 return g_strdup(tmp);
576}
577
578
579void win32_show_font_dialog(void)
580{
581 gint retval;
582 CHOOSEFONT cf;
583 LOGFONT lf; /* logical font structure */
584
585 memset(&lf, 0, sizeof lf);
586 /* TODO: init lf members */
587
588 memset(&cf, 0, sizeof cf);
589 cf.lStructSize = sizeof cf;
590 cf.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(main_widgets.window));
591 cf.lpLogFont = &lf;
592 /* support CF_APPLY? */
593 cf.Flags = CF_NOSCRIPTSEL | CF_FORCEFONTEXIST | CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS | CF_ENABLEHOOK;
594 cf.lpfnHook = win32_dialog_hook_proc;
595
596 retval = ChooseFont(&cf);
597
598 if (retval)
599 {
600 gchar *editorfont = g_strdup_printf("%s %d", lf.lfFaceName, (cf.iPointSize / 10));
601 ui_set_editor_font(editorfont);
602 g_free(editorfont);
603 }
604}
605
606
607void win32_show_color_dialog(const gchar *colour)
608{
609 CHOOSECOLOR cc;
610 static COLORREF acr_cust_clr[16];
611 static DWORD rgb_current;
612 gchar *hex = g_malloc0(12);
614
615 /* Initialize CHOOSECOLOR */
616 memset(&cc, 0, sizeof cc);
617 cc.lStructSize = sizeof(cc);
618 cc.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(main_widgets.window));
619 cc.lpCustColors = (LPDWORD) acr_cust_clr;
620 cc.rgbResult = (colour != NULL) ? utils_parse_color_to_bgr(colour) : 0;
621 cc.Flags = CC_FULLOPEN | CC_RGBINIT | CC_ENABLEHOOK;
622 cc.lpfnHook = win32_dialog_hook_proc;
623
624 if (ChooseColor(&cc))
625 {
626 rgb_current = cc.rgbResult;
627 g_snprintf(hex, 11, "#%02X%02X%02X",
628 GetRValue(rgb_current), GetGValue(rgb_current), GetBValue(rgb_current));
629 editor_insert_color(doc->editor, hex);
630 }
631 g_free(hex);
632}
633
634
635void win32_show_pref_file_dialog(GtkEntry *item)
636{
637 OPENFILENAMEW of;
638 gint retval, len;
639 wchar_t fname[MAX_PATH];
640 gchar tmp[MAX_PATH];
641 gchar **field, *filename;
642
643 fname[0] = '\0';
644
645 /* cut the options from the command line */
646 field = g_strsplit(gtk_entry_get_text(GTK_ENTRY(item)), " ", 2);
647 if (field[0])
648 {
649 filename = g_find_program_in_path(field[0]);
650 if (filename != NULL && g_file_test(filename, G_FILE_TEST_EXISTS))
651 {
652 MultiByteToWideChar(CP_UTF8, 0, filename, -1, fname, G_N_ELEMENTS(fname));
653 g_free(filename);
654 }
655 }
656
657 /* initialize file dialog info struct */
658 memset(&of, 0, sizeof of);
659 of.lStructSize = sizeof of;
660 of.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(ui_widgets.prefs_dialog));
661
662 of.lpstrFilter = get_filters(FALSE);
663 of.lpstrCustomFilter = NULL;
664 of.nFilterIndex = 1;
665
666 of.lpstrFile = fname;
667 of.nMaxFile = 2048;
668 of.lpstrFileTitle = NULL;
669 /*of.lpstrInitialDir = g_get_home_dir();*/
670 of.lpstrInitialDir = NULL;
671 of.lpstrTitle = NULL;
672 of.lpstrDefExt = L"exe";
673 of.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_EXPLORER |
674 OFN_ENABLEHOOK | OFN_ENABLESIZING;
675 of.lpfnHook = win32_dialog_explorer_hook_proc;
676 retval = GetOpenFileNameW(&of);
677
678 if (!retval)
679 {
680 if (CommDlgExtendedError())
681 {
682 gchar error[100];
683 g_snprintf(error, sizeof error, "File dialog box error (%x)", (int)CommDlgExtendedError());
684 win32_message_dialog(NULL, GTK_MESSAGE_ERROR, error);
685 }
686 g_strfreev(field);
687 return;
688 }
689
690 len = WideCharToMultiByte(CP_UTF8, 0, fname, -1, tmp, sizeof(tmp), NULL, NULL);
691 if ((of.nFileOffset - 1) != len)
692 {
693 if (g_strv_length(field) > 1)
694 /* add the command line args of the old command */
695 /** TODO this fails badly when the old command contained spaces, we need quoting here */
696 filename = g_strconcat(tmp, " ", field[1], NULL);
697 else
698 {
699 filename = g_strdup(tmp);
700 }
701 gtk_entry_set_text(GTK_ENTRY(item), filename);
702 g_free(filename);
703 }
704 g_strfreev(field);
705}
706
707
708/* Creates a native Windows message box of the given type and returns always TRUE
709 * or FALSE representing th pressed Yes or No button.
710 * If type is not GTK_MESSAGE_QUESTION, it returns always TRUE. */
711gboolean win32_message_dialog(GtkWidget *parent, GtkMessageType type, const gchar *msg)
712{
713 gboolean ret = TRUE;
714 gint rc;
715 guint t;
716 const gchar *title;
717 HWND parent_hwnd = NULL;
718 static wchar_t w_msg[512];
719 static wchar_t w_title[512];
720
721 switch (type)
722 {
723 case GTK_MESSAGE_ERROR:
724 {
725 t = MB_OK | MB_ICONERROR;
726 title = _("Error");
727 break;
728 }
729 case GTK_MESSAGE_QUESTION:
730 {
731 t = MB_YESNO | MB_ICONQUESTION;
732 title = _("Question");
733 break;
734 }
735 case GTK_MESSAGE_WARNING:
736 {
737 t = MB_OK | MB_ICONWARNING;
738 title = _("Warning");
739 break;
740 }
741 default:
742 {
743 t = MB_OK | MB_ICONINFORMATION;
744 title = _("Information");
745 break;
746 }
747 }
748
749 /* convert the Unicode chars to wide chars */
750 /** TODO test if LANG == C then possibly skip conversion => g_win32_getlocale() */
751 MultiByteToWideChar(CP_UTF8, 0, msg, -1, w_msg, G_N_ELEMENTS(w_msg));
752 MultiByteToWideChar(CP_UTF8, 0, title, -1, w_title, G_N_ELEMENTS(w_title));
753
754 if (parent != NULL)
755 parent_hwnd = GDK_WINDOW_HWND(gtk_widget_get_window(parent));
756 else if (main_widgets.window != NULL)
757 parent_hwnd = GDK_WINDOW_HWND(gtk_widget_get_window(main_widgets.window));
758
759 /* display the message box */
760 rc = MessageBoxW(parent_hwnd, w_msg, w_title, t);
761
762 if (type == GTK_MESSAGE_QUESTION && rc != IDYES)
763 ret = FALSE;
764
765 return ret;
766}
767
768
769/* Little wrapper for _waccess(), returns errno or 0 if there was no error */
770gint win32_check_write_permission(const gchar *dir)
771{
772 static wchar_t w_dir[MAX_PATH];
773 MultiByteToWideChar(CP_UTF8, 0, dir, -1, w_dir, G_N_ELEMENTS(w_dir));
774 if (_waccess(w_dir, R_OK | W_OK) != 0)
775 return errno;
776 else
777 return 0;
778}
779
780
781/* Just a simple wrapper function to open a browser window */
782void win32_open_browser(const gchar *uri)
783{
784 gint ret;
785 if (strncmp(uri, "file://", 7) == 0)
786 {
787 uri += 7;
788 if (strchr(uri, ':') != NULL)
789 {
790 while (*uri == '/')
791 uri++;
792 }
793 }
794 ret = (gint) ShellExecute(NULL, "open", uri, NULL, NULL, SW_SHOWNORMAL);
795 if (ret <= 32)
796 {
797 gchar *err = g_win32_error_message(GetLastError());
798 ui_set_statusbar(TRUE, _("Failed to open URI \"%s\": %s"), uri, err);
799 g_warning("ShellExecute failed opening \"%s\" (code %d): %s", uri, ret, err);
800 g_free(err);
801 }
802}
803
804
805static FILE *open_std_handle(DWORD handle, const char *mode)
806{
807 HANDLE lStdHandle;
808 int hConHandle;
809 FILE *fp;
810
811 lStdHandle = GetStdHandle(handle);
812 if (lStdHandle == INVALID_HANDLE_VALUE)
813 {
814 gchar *err = g_win32_error_message(GetLastError());
815 g_warning("GetStdHandle(%ld) failed: %s", (long)handle, err);
816 g_free(err);
817 return NULL;
818 }
819 hConHandle = _open_osfhandle((intptr_t)lStdHandle, _O_TEXT);
820 if (hConHandle == -1)
821 {
822 gchar *err = g_win32_error_message(GetLastError());
823 g_warning("_open_osfhandle(handle(%ld), _O_TEXT) failed: %s", (long)handle, err);
824 g_free(err);
825 return NULL;
826 }
827 fp = _fdopen(hConHandle, mode);
828 if (! fp)
829 {
830 gchar *err = g_win32_error_message(GetLastError());
831 g_warning("_fdopen(%d, \"%s\") failed: %s", hConHandle, mode, err);
832 g_free(err);
833 return NULL;
834 }
835 if (setvbuf(fp, NULL, _IONBF, 0) != 0)
836 {
837 gchar *err = g_win32_error_message(GetLastError());
838 g_warning("setvbuf(%p, NULL, _IONBF, 0) failed: %s", fp, err);
839 g_free(err);
840 fclose(fp);
841 return NULL;
842 }
843
844 return fp;
845}
846
847
848static void debug_setup_console(void)
849{
850 static const WORD MAX_CONSOLE_LINES = 500;
851 CONSOLE_SCREEN_BUFFER_INFO coninfo;
852 FILE *fp;
853
854 /* allocate a console for this app */
855 AllocConsole();
856
857 /* set the screen buffer to be big enough to let us scroll text */
858 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo);
859 coninfo.dwSize.Y = MAX_CONSOLE_LINES;
860 SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize);
861
862 /* redirect unbuffered STDOUT to the console */
863 fp = open_std_handle(STD_OUTPUT_HANDLE, "w");
864 if (fp)
865 *stdout = *fp;
866
867 /* redirect unbuffered STDERR to the console */
868 fp = open_std_handle(STD_ERROR_HANDLE, "w");
869 if (fp)
870 *stderr = *fp;
871
872 /* redirect unbuffered STDIN to the console */
873 fp = open_std_handle(STD_INPUT_HANDLE, "r");
874 if (fp)
875 *stdin = *fp;
876}
877
878
879void win32_init_debug_code(void)
880{
881 if (app->debug_mode)
882 {
883 /* create a console window to get log messages on Windows,
884 * especially useful when generating tags files */
885 debug_setup_console();
886 }
887}
888
889
890/* expands environment placeholders in @str. input and output is in UTF-8 */
891gchar *win32_expand_environment_variables(const gchar *str)
892{
893 wchar_t *cmdline = g_utf8_to_utf16(str, -1, NULL, NULL, NULL);
894 wchar_t expCmdline[32768]; /* 32768 is the limit for ExpandEnvironmentStrings() */
895 gchar *expanded = NULL;
896
897 if (cmdline && ExpandEnvironmentStringsW(cmdline, expCmdline, sizeof(expCmdline)) != 0)
898 expanded = g_utf16_to_utf8(expCmdline, -1, NULL, NULL, NULL);
899
900 g_free(cmdline);
901
902 return expanded ? expanded : g_strdup(str);
903}
904
905
906/* From GDK (they got it from MS Knowledge Base article Q130698) */
907static gboolean resolve_link(HWND hWnd, wchar_t *link, gchar **lpszPath)
908{
909 WIN32_FILE_ATTRIBUTE_DATA wfad;
910 HRESULT hres;
911 IShellLinkW *pslW = NULL;
912 IPersistFile *ppf = NULL;
913 LPVOID pslWV = NULL;
914 LPVOID ppfV = NULL;
915
916 /* Check if the file is empty first because IShellLink::Resolve for some reason succeeds
917 * with an empty file and returns an empty "link target". (#524151) */
918 if (!GetFileAttributesExW(link, GetFileExInfoStandard, &wfad) ||
919 (wfad.nFileSizeHigh == 0 && wfad.nFileSizeLow == 0))
920 {
921 return FALSE;
922 }
923
924 /* Assume failure to start with: */
925 *lpszPath = 0;
926
927 CoInitialize(NULL);
928
929 hres = CoCreateInstance(
930 &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkW, &pslWV);
931
932 if (SUCCEEDED(hres))
933 {
934 /* The IShellLink interface supports the IPersistFile interface.
935 * Get an interface pointer to it. */
936 pslW = (IShellLinkW*) pslWV;
937 hres = pslW->lpVtbl->QueryInterface(pslW, &IID_IPersistFile, &ppfV);
938 }
939
940 if (SUCCEEDED(hres))
941 {
942 /* Load the file. */
943 ppf = (IPersistFile*) ppfV;
944 hres = ppf->lpVtbl->Load(ppf, link, STGM_READ);
945 }
946
947 if (SUCCEEDED(hres))
948 {
949 /* Resolve the link by calling the Resolve() interface function. */
950 hres = pslW->lpVtbl->Resolve(pslW, hWnd, SLR_ANY_MATCH | SLR_NO_UI);
951 }
952
953 if (SUCCEEDED(hres))
954 {
955 wchar_t wtarget[MAX_PATH];
956
957 hres = pslW->lpVtbl->GetPath(pslW, wtarget, MAX_PATH, NULL, 0);
958 if (SUCCEEDED(hres))
959 *lpszPath = g_utf16_to_utf8(wtarget, -1, NULL, NULL, NULL);
960 }
961
962 if (ppf)
963 ppf->lpVtbl->Release(ppf);
964
965 if (pslW)
966 pslW->lpVtbl->Release(pslW);
967
968 return SUCCEEDED(hres);
969}
970
971
972/* Checks whether file_name is a Windows shortcut. file_name is expected in UTF-8 encoding.
973 * If file_name is a Windows shortcut, it returns the target in UTF-8 encoding.
974 * If it is not a shortcut, it returns a newly allocated copy of file_name. */
975gchar *win32_get_shortcut_target(const gchar *file_name)
976{
977 gchar *path = NULL;
978 wchar_t *wfilename = g_utf8_to_utf16(file_name, -1, NULL, NULL, NULL);
979 HWND hWnd = NULL;
980
981 if (main_widgets.window != NULL)
982 {
983 GdkWindow *window = gtk_widget_get_window(main_widgets.window);
984 if (window != NULL)
985 hWnd = GDK_WINDOW_HWND(window);
986 }
987
988 resolve_link(hWnd, wfilename, &path);
989 g_free(wfilename);
990
991 if (path == NULL)
992 return g_strdup(file_name);
993 else
994 return path;
995}
996
997
998void win32_set_working_directory(const gchar *dir)
999{
1000 SetCurrentDirectory(dir);
1001}
1002
1003
1004gchar *win32_get_installation_dir(void)
1005{
1006 return g_win32_get_package_installation_directory_of_module(NULL);
1007}
1008
1009
1010gchar *win32_get_user_config_dir(void)
1011{
1012 HRESULT hr;
1013 wchar_t path[MAX_PATH];
1014
1015 hr = SHGetFolderPathAndSubDirW(NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, L"geany", path);
1016 if (SUCCEEDED(hr))
1017 {
1018 // GLib always uses UTF-8 for filename encoding on Windows
1019 int u8_size = WideCharToMultiByte(CP_UTF8, 0, path, -1, NULL, 0, NULL, NULL);
1020 if (u8_size > 0)
1021 {
1022 gchar *u8_path = g_malloc0(u8_size + 1);
1023 if (u8_path != NULL &&
1024 WideCharToMultiByte(CP_UTF8, 0, path, -1, u8_path, u8_size, NULL, NULL))
1025 {
1026 return u8_path;
1027 }
1028 }
1029 }
1030
1031 // glib fallback
1032 g_warning("Failed to retrieve Windows config dir, falling back to default");
1033 return g_build_filename(g_get_user_config_dir(), "geany", NULL);
1034}
1035
1036#endif
File related dialogs, miscellaneous dialogs, font dialog.
GeanyDocument * document_get_current(void)
Finds the current document.
Definition: document.c:371
GeanyDocument * document_open_file(const gchar *locale_filename, gboolean readonly, GeanyFiletype *ft, const gchar *forced_enc)
Opens a document specified by locale_filename.
Definition: document.c:908
Document related actions: new, save, open, etc.
#define DOC_FILENAME(doc)
Returns the filename of the document passed or GEANY_STRING_UNTITLED (e.g.
Definition: document.h:170
void editor_insert_color(GeanyEditor *editor, const gchar *colour)
Definition: editor.c:4238
Editor-related functions for GeanyEditor.
#define field(x)
void error(const errorSelection selection, const char *const format,...)
Definition: error.c:53
static gchar ** filter
Definition: filebrowser.c:89
Filetype detection, file extensions and filetype menu items.
@ GEANY_FILETYPES_NONE
Definition: filetypes.h:46
#define filetypes
Wraps GeanyData::filetypes_array so it can be used with C array syntax.
Definition: filetypes.h:178
int errno
GeanyApp * app
Definition: libmain.c:86
Project Management.
#define GEANY_PROJECT_EXT
Definition: project.h:30
#define NULL
Definition: rbtree.h:150
GSList * filetypes_by_title
Definition: filetypes.c:59
GPtrArray * filetypes_array
Definition: filetypes.c:57
const gchar filename[]
Definition: stash-example.c:4
gboolean debug_mode
TRUE if debug messages should be printed.
Definition: app.h:39
Structure for representing an open tab with all its properties.
Definition: document.h:81
gchar * file_name
The UTF-8 encoded file name.
Definition: document.h:92
GeanyFiletype * file_type
The filetype for this document, it's only a reference to one of the elements of the global filetypes ...
Definition: document.h:101
GeanyEditor * editor
The editor associated with the document.
Definition: document.h:98
Represents a filetype.
Definition: filetypes.h:144
GeanyFiletypeID id
Index in filetypes.
Definition: filetypes.h:145
gchar * title
Shown in the file open dialog, such as "C source file".
Definition: filetypes.h:154
gchar * extension
Default file extension for new files, or NULL.
Definition: filetypes.h:155
gchar ** pattern
Array of filename-matching wildcard strings.
Definition: filetypes.h:156
GtkWidget * window
Main window.
Definition: ui_utils.h:80
Defines internationalization macros.
#define _(String)
Definition: support.h:42
void ui_set_editor_font(const gchar *font_name)
Definition: ui_utils.c:413
GeanyMainWidgets main_widgets
Definition: ui_utils.c:72
void ui_set_statusbar(gboolean log, const gchar *format,...)
Displays text on the statusbar.
Definition: ui_utils.c:168
UIWidgets ui_widgets
Definition: ui_utils.c:75
User Interface general utility functions.
gint utils_parse_color_to_bgr(const gchar *spec)
Definition: utils.c:1005
General utility functions, non-GTK related.
#define foreach_slist(node, list)
Iterates all the nodes in list.
Definition: utils.h:121