From 64f34f1f59232e3986eeec6b599a8035713ef8db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrico=20Tr=C3=B6ger?= Date: Mon, 19 Feb 2007 18:58:32 +0000 Subject: [PATCH] Added Windows dialogs for Project new and Project open actions. Fixed some mem leaks in the Windows code. git-svn-id: https://geany.svn.sourceforge.net/svnroot/geany/trunk@1307 ea778897-0a13-0410-b9d1-a72fbfd435f5 --- ChangeLog | 3 + src/project.c | 41 +++++++--- src/win32.c | 210 +++++++++++++++++++++++++++++++++++++++++++------- src/win32.h | 9 +++ 4 files changed, 225 insertions(+), 38 deletions(-) diff --git a/ChangeLog b/ChangeLog index 388372ae..aba1906b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,9 @@ 2007-02-19 Enrico Tröger * src/search.c: Fixed compiler warning. + * src/project.c, src/win32.c, src/win32.h: + Added Windows dialogs for Project new and Project open actions. + Fixed some mem leaks in the Windows code. 2007-02-19 Nick Treleaven diff --git a/src/project.c b/src/project.c index 9ea5e215..39a9862d 100644 --- a/src/project.c +++ b/src/project.c @@ -55,7 +55,9 @@ static void on_properties_dialog_response(GtkDialog *dialog, gint response, PropertyDialogElements *e); static void on_file_open_button_clicked(GtkButton *button, GtkWidget *entry); static void on_folder_open_button_clicked(GtkButton *button, GtkWidget *entry); +#ifndef G_OS_WIN32 static void on_open_dialog_response(GtkDialog *dialog, gint response, gpointer user_data); +#endif static gboolean close_open_project(); static gboolean load_config(const gchar *filename); static gboolean write_config(); @@ -80,15 +82,23 @@ void project_new() void project_open() { -#ifndef G_OS_WIN32 + gchar *dir = g_strconcat(GEANY_HOME_DIR, G_DIR_SEPARATOR_S, PROJECT_DIR, NULL); +#ifdef G_OS_WIN32 + gchar *file; +#else GtkWidget *dialog; GtkFileFilter *filter; - gchar *dir; #endif if (! close_open_project()) return; #ifdef G_OS_WIN32 - win32_show_file_dialog(TRUE); + file = win32_show_project_open_dialog(_("Open project"), dir, FALSE); + if (file != NULL) + { + load_config(file); + g_free(file); + g_free(dir); + } #else dialog = gtk_file_chooser_dialog_new(_("Open project"), GTK_WINDOW(app->window), @@ -115,7 +125,6 @@ void project_open() gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter); - dir = g_strconcat(GEANY_HOME_DIR, G_DIR_SEPARATOR_S, PROJECT_DIR, NULL); gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), dir); g_free(dir); @@ -462,9 +471,10 @@ static void on_properties_dialog_response(GtkDialog *dialog, gint response, } +#ifndef G_OS_WIN32 static void run_dialog(GtkWidget *dialog, GtkWidget *entry) { - // set filename + // set filename in the file chooser dialog gchar *locale_filename = utils_get_locale_from_utf8(gtk_entry_get_text(GTK_ENTRY(entry))); if (g_path_is_absolute(locale_filename)) @@ -486,13 +496,19 @@ static void run_dialog(GtkWidget *dialog, GtkWidget *entry) } gtk_widget_destroy(dialog); } +#endif static void on_file_open_button_clicked(GtkButton *button, GtkWidget *entry) { #ifdef G_OS_WIN32 - /// TODO write me - //win32_show_project_file_dialog(item); + gchar *path = win32_show_project_open_dialog(_("Choose project filename"), + gtk_entry_get_text(GTK_ENTRY(entry)), TRUE); + if (path != NULL) + { + gtk_entry_set_text(GTK_ENTRY(entry), path); + g_free(path); + } #else GtkWidget *dialog; @@ -514,8 +530,13 @@ static void on_file_open_button_clicked(GtkButton *button, GtkWidget *entry) static void on_folder_open_button_clicked(GtkButton *button, GtkWidget *entry) { #ifdef G_OS_WIN32 - /// TODO write me - //win32_show_project_folder_dialog(item); + gchar *path = win32_show_project_folder_dialog(_("Choose project base path"), + gtk_entry_get_text(GTK_ENTRY(entry))); + if (path != NULL) + { + gtk_entry_set_text(GTK_ENTRY(entry), path); + g_free(path); + } #else GtkWidget *dialog; @@ -579,6 +600,7 @@ static void on_entries_changed(GtkEditable *editable, PropertyDialogElements *e) } +#ifndef G_OS_WIN32 static void on_open_dialog_response(GtkDialog *dialog, gint response, gpointer user_data) { if (response == GTK_RESPONSE_ACCEPT) @@ -601,6 +623,7 @@ static void on_open_dialog_response(GtkDialog *dialog, gint response, gpointer u else gtk_widget_destroy(GTK_WIDGET(dialog)); } +#endif /* Reads the given filename and creates a new project with the data found in the file. diff --git a/src/win32.c b/src/win32.c index ef77ab61..ccd7388f 100644 --- a/src/win32.c +++ b/src/win32.c @@ -30,12 +30,15 @@ #include #include +#include #include #include #include #include +#include + #include "win32.h" #include "document.h" @@ -46,33 +49,27 @@ #include "dialogs.h" -static gchar *filters; -static gchar *exe_filters; - -static gchar *win32_get_filters(gboolean exe) +static gchar *win32_get_file_filters() { - gchar *string = ""; + gchar *string; gint i, len; - if (exe) + GString *str = g_string_sized_new(100); + gchar *tmp; + + for (i = 0; i < GEANY_MAX_FILE_TYPES; i++) { - string = g_strconcat(string, - _("Executables"), "\t", "*.exe;*.bat;*.cmd", "\t", - filetypes[GEANY_FILETYPES_ALL]->title, "\t", - g_strjoinv(";", filetypes[GEANY_FILETYPES_ALL]->pattern), "\t", NULL); - } - else - { - for(i = 0; i < GEANY_MAX_FILE_TYPES; i++) + if (filetypes[i]) { - if (filetypes[i]) - { - string = g_strconcat(string, filetypes[i]->title, "\t", g_strjoinv(";", filetypes[i]->pattern), "\t", NULL); - } + tmp = g_strjoinv(";", filetypes[i]->pattern); + g_string_append_printf(str, "%s\t%s\t", filetypes[i]->title, tmp); + g_free(tmp); } } - string = g_strconcat(string, "\t", NULL); + g_string_append_c(str, '\t'); // the final \0 byte to mark the end of the string + string = str->str; + g_string_free(str, FALSE); // replace all "\t"s by \0 len = strlen(string); @@ -84,6 +81,159 @@ static gchar *win32_get_filters(gboolean exe) } +static gchar *win32_get_filters(gboolean exe) +{ + gchar *string; + gint i, len; + + if (exe) + { + string = g_strconcat(_("Executables"), "\t", "*.exe;*.bat;*.cmd", "\t", + filetypes[GEANY_FILETYPES_ALL]->title, "\t", + filetypes[GEANY_FILETYPES_ALL]->pattern[0], "\t", NULL); + } + else + { + string = g_strconcat(_("Geany project files"), "\t", "*." GEANY_PROJECT_EXT, "\t", + filetypes[GEANY_FILETYPES_ALL]->title, "\t", + filetypes[GEANY_FILETYPES_ALL]->pattern[0], "\t", NULL); + } + + // replace all "\t"s by \0 + len = strlen(string); + for(i = 0; i < len; i++) + { + if (string[i] == '\t') string[i] = '\0'; + } + return string; +} + + +/* Returns the directory part of the given filename. */ +static gchar *get_dir(const gchar *filename) +{ + if (! g_file_test(filename, G_FILE_TEST_IS_DIR)) + return g_path_get_dirname(filename); + else + return g_strdup(filename); +} + + +/* Callback function for setting the initial directory of the folder open dialog. This could also + * be done with BROWSEINFO.pidlRoot and SHParseDisplayName but SHParseDisplayName is not available + * on systems below Windows XP. So, we go the hard way by creating a callback which will set up the + * folder when the dialog is initialised. Yeah, I like Windows. */ +INT CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData) +{ + switch(uMsg) + { + case BFFM_INITIALIZED: + SendMessage(hwnd, BFFM_SETSELECTION, TRUE, (LPARAM) pData); + break; + + case BFFM_SELCHANGED: + { + // set the status window to the currently selected path. + static TCHAR szDir[MAX_PATH]; + if (SHGetPathFromIDList((LPITEMIDLIST) lp, szDir)) + { + SendMessage(hwnd, BFFM_SETSTATUSTEXT, 0, (LPARAM) szDir); + } + break; + } + } + return 0; +} + + +/* Shows a folder selection dialog. + * The selected folder name is returned. */ +gchar *win32_show_project_folder_dialog(const gchar *title, const gchar *initial_dir) +{ + BROWSEINFO bi; + LPCITEMIDLIST pidl; + gchar *fname = g_malloc(MAX_PATH); + gchar *dir = get_dir(initial_dir); + + memset(&bi, 0, sizeof bi); + bi.hwndOwner = GDK_WINDOW_HWND(app->window->window); + bi.pidlRoot = NULL; + bi.lpszTitle = title; + bi.lpfn = BrowseCallbackProc; + bi.lParam = (LPARAM) dir; + bi.ulFlags = BIF_DONTGOBELOWDOMAIN | BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT; + + pidl = SHBrowseForFolder(&bi); + g_free(dir); + + // convert the strange Windows folder list item something into an usual path string ;-) + if (pidl != NULL && SHGetPathFromIDList(pidl, fname)) + return fname; + else + { + g_free(fname); + return NULL; + } +} + + +/* Shows a file open dialog. + * If allow_new_file is set, the file to be opened doesn't have to exist. + * The selected file name is returned. */ +gchar *win32_show_project_open_dialog(const gchar *title, const gchar *initial_dir, gboolean allow_new_file) +{ + OPENFILENAME of; + gint retval; + gchar *fname = g_malloc(2048); + gchar *filters = win32_get_filters(FALSE); + gchar *dir = get_dir(initial_dir); + + fname[0] = '\0'; + + /* initialise file dialog info struct */ + memset(&of, 0, sizeof of); +#ifdef OPENFILENAME_SIZE_VERSION_400 + of.lStructSize = OPENFILENAME_SIZE_VERSION_400; +#else + of.lStructSize = sizeof of; +#endif + of.hwndOwner = GDK_WINDOW_HWND(app->window->window); + of.lpstrFilter = filters; + + of.lpstrCustomFilter = NULL; + of.nFilterIndex = 0; + of.lpstrFile = fname; + of.lpstrInitialDir = dir; + of.nMaxFile = 2048; + of.lpstrFileTitle = NULL; + of.lpstrTitle = title; + of.lpstrDefExt = ""; + of.Flags = OFN_PATHMUSTEXIST | OFN_EXPLORER | OFN_HIDEREADONLY; + if (! allow_new_file) + of.Flags |= OFN_FILEMUSTEXIST; + + retval = GetOpenFileName(&of); + + g_free(dir); + g_free(filters); + + if (! retval) + { + if (CommDlgExtendedError()) + { + gchar *error; + error = g_strdup_printf("File dialog box error (%x)", (int)CommDlgExtendedError()); + win32_message_dialog(GTK_MESSAGE_ERROR, error); + g_free(error); + } + g_free(fname); + return NULL; + } + + return fname; +} + + // return TRUE if the dialog was not cancelled. gboolean win32_show_file_dialog(gboolean file_open) { @@ -91,11 +241,10 @@ gboolean win32_show_file_dialog(gboolean file_open) gint retval; gchar *fname = g_malloc(2048); gchar *current_dir = utils_get_current_file_dir(); + gchar *filters = win32_get_file_filters(); fname[0] = '\0'; - if (! filters) filters = win32_get_filters(FALSE); - /* initialize file dialog info struct */ memset(&of, 0, sizeof of); #ifdef OPENFILENAME_SIZE_VERSION_400 @@ -103,7 +252,7 @@ gboolean win32_show_file_dialog(gboolean file_open) #else of.lStructSize = sizeof of; #endif - of.hwndOwner = NULL; + of.hwndOwner = GDK_WINDOW_HWND(app->window->window); of.lpstrFilter = filters; of.lpstrCustomFilter = NULL; @@ -126,6 +275,7 @@ gboolean win32_show_file_dialog(gboolean file_open) } g_free(current_dir); + g_free(filters); if (!retval) { @@ -183,7 +333,7 @@ void win32_show_font_dialog(void) memset(&cf, 0, sizeof cf); cf.lStructSize = sizeof cf; - cf.hwndOwner = NULL; + cf.hwndOwner = GDK_WINDOW_HWND(app->window->window); cf.lpLogFont = &lf; cf.Flags = CF_APPLY | CF_NOSCRIPTSEL | CF_FORCEFONTEXIST | CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS; @@ -220,7 +370,7 @@ void win32_show_color_dialog(const gchar *colour) // Initialize CHOOSECOLOR memset(&cc, 0, sizeof cc); cc.lStructSize = sizeof(cc); - cc.hwndOwner = NULL; + cc.hwndOwner = GDK_WINDOW_HWND(app->window->window); cc.lpCustColors = (LPDWORD) acr_cust_clr; cc.rgbResult = (colour != NULL) ? utils_strtod(colour, NULL, colour[0] == '#') : 0; cc.Flags = CC_FULLOPEN | CC_RGBINIT; @@ -245,9 +395,9 @@ void win32_show_pref_file_dialog(GtkEntry *item) gint retval; gchar *fname = g_malloc(512); gchar **field, *filename, *tmp; + gchar *filters = win32_get_filters(TRUE); fname[0] = '\0'; - if (! exe_filters) exe_filters = win32_get_filters(TRUE); // cut the options from the command line field = g_strsplit(gtk_entry_get_text(GTK_ENTRY(item)), " ", 2); @@ -265,9 +415,9 @@ void win32_show_pref_file_dialog(GtkEntry *item) #else of.lStructSize = sizeof of; #endif - of.hwndOwner = NULL; + of.hwndOwner = GDK_WINDOW_HWND(app->window->window); - of.lpstrFilter = exe_filters; + of.lpstrFilter = filters; of.lpstrCustomFilter = NULL; of.nFilterIndex = 1; @@ -281,6 +431,8 @@ void win32_show_pref_file_dialog(GtkEntry *item) of.Flags = OFN_ALLOWMULTISELECT | OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_EXPLORER; retval = GetOpenFileName(&of); + g_free(filters); + if (!retval) { if (CommDlgExtendedError()) @@ -360,7 +512,7 @@ gboolean win32_message_dialog(GtkMessageType type, const gchar *msg) MultiByteToWideChar(CP_UTF8, 0, title, -1, w_title, sizeof(w_title)/sizeof(w_title[0])); // display the message box - rc = MessageBoxW(NULL, w_msg, w_title, t); + rc = MessageBoxW(GDK_WINDOW_HWND(app->window->window), w_msg, w_title, t); if (type == GTK_MESSAGE_QUESTION && rc != IDYES) ret = FALSE; @@ -380,7 +532,7 @@ gint win32_message_dialog_unsaved(const gchar *msg) MultiByteToWideChar(CP_UTF8, 0, msg, -1, w_msg, sizeof(w_msg)/sizeof(w_msg[0])); MultiByteToWideChar(CP_UTF8, 0, _("Question"), -1, w_title, sizeof(w_title)/sizeof(w_title[0])); - ret = MessageBoxW(NULL, w_msg, w_title, MB_YESNOCANCEL | MB_ICONQUESTION); + ret = MessageBoxW(GDK_WINDOW_HWND(app->window->window), w_msg, w_title, MB_YESNOCANCEL | MB_ICONQUESTION); switch(ret) { case IDYES: ret = GTK_RESPONSE_YES; break; diff --git a/src/win32.h b/src/win32.h index 5b1e825f..3246c2fe 100644 --- a/src/win32.h +++ b/src/win32.h @@ -44,4 +44,13 @@ gint win32_message_dialog_unsaved(const gchar *msg); /* Just a simple wrapper function to open a browser window */ void win32_open_browser(const gchar *uri); +/* Shows a file open dialog. + * If allow_new_file is set, the file to be opened doesn't have to exist. + * The selected file name is returned. */ +gchar *win32_show_project_open_dialog(const gchar *title, const gchar *initial_dir, gboolean allow_new_file); + +/* Shows a folder selection dialog. + * The selected folder name is returned. */ +gchar *win32_show_project_folder_dialog(const gchar *title, const gchar *initial_dir); + #endif