Index: display.c =================================================================== --- display.c (revision 3721) +++ display.c (working copy) @@ -96,6 +96,12 @@ GtkStatusbar *statusbar; guint context_id; + if (is_integrated_ui ()) + { + integrated_ui_show_diagram_modified_status (ddisp); + return; + } + if (diagram_is_modified(ddisp->diagram)) { statusbar = GTK_STATUSBAR (ddisp->modified_status); Index: makefile.msc =================================================================== --- makefile.msc (revision 3721) +++ makefile.msc (working copy) @@ -48,6 +48,7 @@ disp_callbacks.obj \ display.obj \ dynamic_refresh.obj \ + exit_dialog.obj \ export_png.obj \ filedlg.obj \ grid.obj \ Index: app_procs.c =================================================================== --- app_procs.c (revision 3721) +++ app_procs.c (working copy) @@ -95,6 +95,7 @@ #include "persistence.h" #include "sheets.h" #include "utils.h" +#include "exit_dialog.h" #if defined(HAVE_LIBPNG) && defined(HAVE_LIBART) extern DiaExportFilter png_export_filter; @@ -1017,9 +1018,60 @@ } if (diagram_modified_exists()) { + if (is_integrated_ui ()) + { + GtkWidget *dialog; + int result; + exit_dialog_item_array_t *items = NULL; + GList * list; + Diagram * diagram; + + dialog = exit_dialog_make (GTK_WINDOW (interface_get_toolbox_shell ()), + _("Exiting Dia")); + + list = dia_open_diagrams(); + while (list) + { + diagram = list->data; + + if (diagram_is_modified (diagram)) + { + const gchar * name = diagram_get_name (diagram); + const gchar * path = diagram->filename; + exit_dialog_add_item (dialog, name, path, diagram); + } + + list = g_slist_next (list); + } + + result = exit_dialog_run (dialog, &items); + + gtk_widget_destroy (dialog); + + if (result == EXIT_DIALOG_EXIT_CANCEL) + { + return FALSE; + } + else if (result == EXIT_DIALOG_EXIT_SAVE_SELECTED) + { + int i; + for (i = 0 ; i < items->array_size ; i++) + { + gchar *filename; + + diagram = items->array[i].data; + filename = g_filename_from_utf8 (diagram->filename, -1, NULL, NULL, NULL); + diagram_update_extents (diagram); + diagram_save (diagram, filename); + g_free (filename); + } + exit_dialog_free_items (items); + } + } + else + { GtkWidget *dialog; GtkWidget *button; - dialog = gtk_message_dialog_new( NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, @@ -1047,6 +1099,7 @@ return FALSE; } gtk_widget_destroy(dialog); + } } prefs_save(); Index: exit_dialog.c =================================================================== --- exit_dialog.c (revision 0) +++ exit_dialog.c (revision 0) @@ -0,0 +1,435 @@ +/* + * exit_dialog.h: Dialog to allow the user to choose which data to + * save on exit or to cancel exit. + * + * Copyright (C) 2007 Patrick Hallinan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* TODO: Non-modal api */ + +#include + +#include "exit_dialog.h" + +#include "intl.h" + +#include + +#include + +#define EXIT_DIALOG_ITEM_DATA _("EXIT_DIALOG_ITEM_DATA") + +enum { + CHECK_COL, + NAME_COL, + PATH_COL, + DATA_COL, /* To store optional data (not shown in listview) */ + NUM_COL +}; + +typedef struct +{ + GSList * item_list; + GtkTreeView * treeview; + +} exit_dialog_item_data_t; + +static void selected_state_set_all (GtkTreeView * treeview, + gboolean state); + +static gint get_selected_items (GtkWidget * dialog, + exit_dialog_item_array_t ** items); + +/* Event Handlers */ +static void select_all_clicked (GtkButton * button, + gpointer data); + +static void select_none_clicked (GtkButton * button, + gpointer data); + +static void toggle_check_button (GtkCellRendererToggle * renderer, + gchar * path, + GtkTreeView * treeview); + +/* A dialog to allow a user to select which unsaved files to save + * (if any) or to abort exiting + * + * @param parent_window This is needed for modal behavior. + * @param title Text display on the dialog's title bar. + * @return The dialog. + */ +GtkWidget * +exit_dialog_make (GtkWindow * parent_window, + gchar * title) +{ + GtkWidget * dialog = gtk_dialog_new_with_buttons (title, parent_window, + GTK_DIALOG_MODAL, + _("Do Not Exit"), + EXIT_DIALOG_EXIT_CANCEL, + _("Exit Without Save"), + EXIT_DIALOG_EXIT_NO_SAVE, + _("Save Selected"), + EXIT_DIALOG_EXIT_SAVE_SELECTED, + NULL); + + GtkBox * vbox = GTK_BOX (GTK_DIALOG(dialog)->vbox); + + GtkWidget * label = gtk_label_new (_("The following are not saved:")); + + GSList * list; + GtkWidget * scrolled; + GtkWidget * button; + GtkWidget * hbox; + + GtkWidget * treeview; + GtkListStore * model; + GtkCellRenderer * renderer; + GtkTreeViewColumn * column; + + exit_dialog_item_data_t * data; + + gtk_box_pack_start (vbox, label, FALSE, FALSE, 0); + + gtk_widget_show (label); + + /* Scrolled window for displaying things which need saving */ + scrolled = gtk_scrolled_window_new (NULL, NULL); + gtk_box_pack_start (vbox, scrolled, TRUE, TRUE, 0); + gtk_widget_show (scrolled); + + model = gtk_list_store_new (NUM_COL, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER); + + treeview = gtk_tree_view_new_with_model ( GTK_TREE_MODEL(model)); + + renderer = gtk_cell_renderer_toggle_new (); + + column = gtk_tree_view_column_new_with_attributes (_("Save"), renderer, + _("active"), CHECK_COL, + NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); + + g_signal_connect (G_OBJECT (renderer), "toggled", + G_CALLBACK (toggle_check_button), + treeview); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Name"), renderer, + "text", NAME_COL, + NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Path"), renderer, + "text", PATH_COL, + NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); + + + gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled), GTK_WIDGET (treeview)); + + + hbox = gtk_hbox_new (FALSE, 3); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + button = gtk_button_new_with_label (_("Select All")); + gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 3); + gtk_widget_show (button); + + g_signal_connect (G_OBJECT (button), "clicked", + G_CALLBACK (select_all_clicked), + treeview); + + button = gtk_button_new_with_label (_("Select None")); + gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 3); + gtk_widget_show (button); + + g_signal_connect (G_OBJECT (button), "clicked", + G_CALLBACK (select_none_clicked), + treeview); + + g_object_unref (model); + gtk_widget_show (GTK_WIDGET (treeview)); + + list = g_slist_alloc (); + + data = g_malloc (sizeof (exit_dialog_item_data_t)); + data->item_list = list; + data->treeview = GTK_TREE_VIEW (treeview); + + gtk_widget_show_all (GTK_WIDGET(vbox)); + + g_object_set_data (G_OBJECT (dialog), EXIT_DIALOG_ITEM_DATA, data); + + return dialog; +} + +/** + * Add name and path of a file that needs to be saved + * @param dialog Exit dialog created with exit_dialog_make() + * @param name User identifiable name of the thing which needs saving. + * @param path File system path of the thing which needs saving. + * @param optional_data Optional data to be returned with selected file info. + */ +void +exit_dialog_add_item (GtkWidget * dialog, + const gchar * name, + const gchar * path, + const gpointer optional_data) +{ + GtkTreeIter iter; + GtkListStore * model; + + exit_dialog_item_data_t * data = + g_object_get_data (G_OBJECT (dialog), EXIT_DIALOG_ITEM_DATA); + + model = GTK_LIST_STORE (gtk_tree_view_get_model (data->treeview)); + + gtk_list_store_append (model, &iter); + + gtk_list_store_set (model, &iter, + CHECK_COL, 1, + NAME_COL, name, + PATH_COL, path, + DATA_COL, optional_data, + -1); +} + +gint +exit_dialog_run (GtkWidget * dialog, + exit_dialog_item_array_t ** items) +{ + gint result; + gint count; + + while (TRUE) + { + result = gtk_dialog_run ( GTK_DIALOG(dialog)); + + switch (result) + { + case EXIT_DIALOG_EXIT_CANCEL: + case EXIT_DIALOG_EXIT_NO_SAVE: + *items = NULL; + return result; + } + + count = get_selected_items (dialog, items); + + if (count == 0) + { + const gchar * msg = _("Nothing selected for saving. Would you like to try again?"); + + GtkWidget * msg_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog), + GTK_DIALOG_MODAL, + GTK_MESSAGE_WARNING, + GTK_BUTTONS_YES_NO, + msg); + + gint yes_or_no = gtk_dialog_run (GTK_DIALOG (msg_dialog)); + + gtk_widget_destroy (msg_dialog); + + if (yes_or_no == GTK_RESPONSE_NO) + { + return EXIT_DIALOG_EXIT_NO_SAVE; + } + } + else + { + return EXIT_DIALOG_EXIT_SAVE_SELECTED; + } + } +} + + +/** + * Gets the list of items selected for saving by the user. + * @param dialog Exit dialog. + * @param items Structure to hold the selected items. Set to NULL if not items selected. + * @return The number of selected items. + */ +gint +get_selected_items (GtkWidget * dialog, + exit_dialog_item_array_t ** items) +{ + exit_dialog_item_data_t * data; + GtkTreeIter iter; + GtkListStore * model; + gboolean valid; + GSList * list = NULL; + GSList * list_iter; + gint selected_count; + gint i; + + data = g_object_get_data (G_OBJECT (dialog), EXIT_DIALOG_ITEM_DATA); + + model = GTK_LIST_STORE (gtk_tree_view_get_model (data->treeview)); + + /* Get the first iter in the list */ + valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter); + + /* Get the selected items */ + while (valid) + { + const char * name; + const char * path; + gpointer data; + gboolean is_selected; + + gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, + CHECK_COL, &is_selected, + NAME_COL, &name, + PATH_COL, &path, + DATA_COL, &data, + -1); + + if (is_selected) + { + exit_dialog_item_t * item = g_new (exit_dialog_item_t,1); + item->name = name; + item->path = path; + item->data = data; + list = g_slist_prepend (list, item); + } + + valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &iter); + } + + selected_count = g_slist_length (list); + + if (selected_count > 0) + { + *items = g_new (exit_dialog_item_array_t,1); + (*items)->array_size = selected_count; + (*items)->array = g_new (exit_dialog_item_t, (*items)->array_size); + + list_iter = list; + for(i = 0 ; i < selected_count ; i++) + { + exit_dialog_item_t * item = list_iter->data; + (*items)->array[i].name = item->name; + (*items)->array[i].path = item->path; + (*items)->array[i].data = item->data; + list_iter = g_slist_next(list_iter); + } + + g_slist_free (list); + } + else + { + *items = NULL; + } + + return selected_count; +} + +/** + * Free memory allocated for the exit_dialog_item_array_t. This + * will not free any memory not allocated by the dialog itself + * @param items Item array struct returned by exit_dialog_run() + */ +void exit_dialog_free_items (exit_dialog_item_array_t * items) +{ + if (items) + { + int i; + for (i = 0 ; i < items->array_size ; i++) + { + /* Cast is needed to remove warning because of const decl */ + g_free ( (char*)items->array[i].name); + g_free ( (char*)items->array[i].path); + } + + g_free (items); + } +} + +/** + * Handler for the check box (button) in the exit dialogs treeview. + * This is needed to cause the check box to change state when the + * user clicks it + */ +static void toggle_check_button (GtkCellRendererToggle * renderer, + gchar * path, + GtkTreeView * treeview) +{ + GtkTreeModel * model; + GtkTreeIter iter; + gboolean value; + + model = gtk_tree_view_get_model (treeview); + if (gtk_tree_model_get_iter_from_string (model, &iter, path)) + { + gtk_tree_model_get (model, &iter, CHECK_COL, &value, -1); + gtk_list_store_set (GTK_LIST_STORE (model), &iter, CHECK_COL, !value, -1); + } +} + +/** + * Sets the state of the save checkbox in the treeview to the + * state specified by the caller. + * @param treeview The treeview in the exit dialog box. + * @param state Set to TRUE to select all, FALSE to de-select all. + */ +static void selected_state_set_all (GtkTreeView * treeview, + gboolean state) +{ + GtkTreeIter iter; + GtkListStore * model; + gboolean valid; + + model = GTK_LIST_STORE (gtk_tree_view_get_model (treeview)); + + /* Get the first iter in the list */ + valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter); + + while (valid) + { + gtk_list_store_set (model, &iter, + CHECK_COL, state, + -1); + + valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &iter); + } +} + +/** + * Handler for Select All button. Causes all checkboxes in the + * exit dialog box treeview to be checked. + * @param button The Select all button. + * @param data The treeview + */ +static void select_all_clicked (GtkButton * button, + gpointer data) +{ + selected_state_set_all (data, TRUE); +} + +/** + * Handler for Select All button. Causes all checkboxes in the + * exit dialog box treeview to be un-checked. + * @param button The Select all button. + * @param data The treeview + */ +static void select_none_clicked (GtkButton * button, + gpointer data) +{ + selected_state_set_all (data, FALSE); +} + + Index: exit_dialog.h =================================================================== --- exit_dialog.h (revision 0) +++ exit_dialog.h (revision 0) @@ -0,0 +1,70 @@ +/* + * exit_dialog.h: Dialog to allow the user to choose which data to + * save on exit or to cancel exit. + * + * Copyright (C) 2007 Patrick Hallinan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef EXIT_DIALOG_H +#define EXIT_DIALOG_H + +#include +#include + +G_BEGIN_DECLS + +enum { + EXIT_DIALOG_EXIT_NO_SAVE, + EXIT_DIALOG_EXIT_SAVE_SELECTED, + EXIT_DIALOG_EXIT_CANCEL +}; + +GtkWidget * +exit_dialog_make (GtkWindow * parent_window, + gchar * title); + +void +exit_dialog_add_item (GtkWidget * dialog, + const gchar * name, + const gchar * filepath, + const gpointer optional_data); + +typedef struct +{ + const gchar * name; + const gchar * path; + gpointer data; + +} exit_dialog_item_t; + +typedef struct +{ + size_t array_size; + exit_dialog_item_t * array; + +} exit_dialog_item_array_t; + +gint +exit_dialog_run (GtkWidget * dialog, + exit_dialog_item_array_t ** items_to_save); + +void exit_dialog_free_items (exit_dialog_item_array_t *); + +G_END_DECLS + +#endif + Index: Makefile.am =================================================================== --- Makefile.am (revision 3721) +++ Makefile.am (working copy) @@ -90,6 +90,8 @@ oafinfo_DATA = $(dia_oafinfo) dia_core_files = \ + exit_dialog.h \ + exit_dialog.c \ diagram.c \ diagram.h \ preferences.c \ Index: interface.c =================================================================== --- interface.c (revision 3721) +++ interface.c (working copy) @@ -438,6 +438,41 @@ } /** + * Change diagram notebook label to show that the diagram has + * been modified (or that it hasn't been modified). + * + * @param ddisp The display which needs the label updated. + */ +void integrated_ui_show_diagram_modified_status (DDisplay *ddisp) +{ + GtkLabel * label = g_object_get_data (G_OBJECT (ddisp->container), "tab-label"); + const gchar * name; + const gchar * sep; + + sep = g_strrstr (ddisp->diagram->filename,G_DIR_SEPARATOR_S); + + if (sep) + { + name = sep + 1; /* IS THIS PORTABLE??? */ + } + else + { + name = ddisp->diagram->filename; + } + + if (diagram_is_modified (ddisp->diagram)) + { + const gchar * text = g_strdup_printf ("*%s",name); + gtk_label_set_text (label, text); + g_free (text); + } + else + { + gtk_label_set_text (label,name); + } +} + +/** * @param ddisp The diagram display object that a window is created for * @param title */ Index: interface.h =================================================================== --- interface.h (revision 3721) +++ interface.h (working copy) @@ -53,12 +53,10 @@ void integrated_ui_main_statusbar_show (void); void integrated_ui_main_statusbar_hide (void); -/* -void synchronize_ui_to_active_display (DDisplay *ddisp); -*/ - int is_integrated_ui (void); +void integrated_ui_show_diagram_modified_status (DDisplay *ddisp); + void create_display_shell(DDisplay *ddisp, int width, int height, char *title, int use_mbar, int top_level_window); Index: makefile.mingw =================================================================== --- makefile.mingw (revision 3721) +++ makefile.mingw (working copy) @@ -43,6 +43,7 @@ disp_callbacks.o \ display.o \ dynamic_refresh.o \ + exit_dialog.o \ export_png.o \ filedlg.o \ grid.o \ Index: disp_callbacks.c =================================================================== --- disp_callbacks.c (revision 3721) +++ disp_callbacks.c (working copy) @@ -527,7 +527,11 @@ ddisp->canvas->allocation.height); ddisplay_update_scrollbars(ddisp); } - display_set_active(ddisp); + /* TODO: Find out if display_set_active is needed for the GDK_CONFIGURE event */ + /* This is prevented for the integrated UI because it causes the diagram + showing in the diagram notebook to change on a resize event */ + if (is_integrated_ui () == 0) + display_set_active(ddisp); break; case GDK_FOCUS_CHANGE: {