[dia] app: use a common DiaExitDialog when closing diagrams
- From: Zander <zbrown src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [dia] app: use a common DiaExitDialog when closing diagrams
- Date: Thu, 23 Apr 2020 11:46:03 +0000 (UTC)
commit 5cb87e7d8b2a054f91f944255c1b4bb85d8982b6
Author: Zander Brown <zbrown gnome org>
Date: Thu Apr 23 12:08:32 2020 +0100
app: use a common DiaExitDialog when closing diagrams
Rework exit_dialog as a GObject wrapping GtkMessageDialog for a UX which
matches the HIG
Nicely handles 1 diagram vs multiple diagrams
Use DiaExitDialog for closing classic windows or tabs as well as
integrated windows
Fix a leak or two
app/app_procs.c | 58 ++---
app/display.c | 144 ++++++------
app/exit_dialog.c | 660 ++++++++++++++++++++++++------------------------------
app/exit_dialog.h | 66 +++---
app/meson.build | 2 +
5 files changed, 421 insertions(+), 509 deletions(-)
---
diff --git a/app/app_procs.c b/app/app_procs.c
index 117c2ec4..cc66d71e 100644
--- a/app/app_procs.c
+++ b/app/app_procs.c
@@ -889,56 +889,60 @@ app_exit (void)
if (diagram_modified_exists()) {
if (is_integrated_ui ()) {
- GtkWidget *dialog;
- int result;
- exit_dialog_item_array_t *items = NULL;
- GList * diagrams;
- Diagram * diagram;
+ DiaExitDialog *dialog;
+ int result;
+ GPtrArray *items = NULL;
+ GList *diagrams;
+ Diagram *diagram;
- dialog = exit_dialog_make (GTK_WINDOW (interface_get_toolbox_shell ()),
- _("Exiting Dia"));
+ dialog = dia_exit_dialog_new (GTK_WINDOW (interface_get_toolbox_shell ()));
diagrams = dia_open_diagrams ();
while (diagrams) {
diagram = diagrams->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);
+ const char *name = diagram_get_name (diagram);
+ const char *path = diagram->filename;
+ dia_exit_dialog_add_item (dialog, name, path, diagram);
}
diagrams = g_list_next (diagrams);
}
- result = exit_dialog_run (dialog, &items);
+ result = dia_exit_dialog_run (dialog, &items);
- gtk_widget_destroy (dialog);
+ g_clear_object (&dialog);
- if (result == EXIT_DIALOG_EXIT_CANCEL) {
+ if (result == DIA_EXIT_DIALOG_CANCEL) {
return FALSE;
- } else if (result == EXIT_DIALOG_EXIT_SAVE_SELECTED) {
- DiaContext *ctx = dia_context_new(_("Save"));
- 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);
+ } else if (result == DIA_EXIT_DIALOG_SAVE) {
+ DiaContext *ctx = dia_context_new (_("Save"));
+
+ for (int i = 0; i < items->len; i++) {
+ DiaExitDialogItem *item = g_ptr_array_index (items, i);
+ char *filename;
+
+ filename = g_filename_from_utf8 (item->data->filename, -1, NULL, NULL, NULL);
+ diagram_update_extents (item->data);
dia_context_set_filename (ctx, filename);
- if (!diagram_save (diagram, filename, ctx)) {
- exit_dialog_free_items (items);
+
+ if (!diagram_save (item->data, filename, ctx)) {
dia_context_release (ctx);
+
+ g_clear_pointer (&filename, g_free);
+ g_clear_pointer (&items, g_ptr_array_unref);
+
return FALSE;
} else {
dia_context_reset (ctx);
}
+
g_clear_pointer (&filename, g_free);
}
+
dia_context_release (ctx);
- exit_dialog_free_items (items);
- } else if (result == EXIT_DIALOG_EXIT_NO_SAVE) {
+ } else if (result == DIA_EXIT_DIALOG_QUIT) {
diagrams = dia_open_diagrams ();
while (diagrams) {
diagram = diagrams->data;
@@ -949,6 +953,8 @@ app_exit (void)
diagrams = g_list_next (diagrams);
}
}
+
+ g_clear_pointer (&items, g_ptr_array_unref);
} else {
GtkWidget *dialog;
GtkWidget *button;
diff --git a/app/display.c b/app/display.c
index c94c1fc9..8564fc0e 100644
--- a/app/display.c
+++ b/app/display.c
@@ -47,6 +47,8 @@
#include "recent_files.h"
#include "filedlg.h"
#include "dia-layer.h"
+#include "exit_dialog.h"
+
static GdkCursor *current_cursor = NULL;
@@ -1167,98 +1169,82 @@ ddisp_destroy (DDisplay *ddisp)
}
-static void
-are_you_sure_close_dialog_respond (GtkWidget *widget, /* the dialog */
- gint response_id,
- gpointer user_data) /* the display */
-{
- DDisplay *ddisp = (DDisplay *)user_data;
- gboolean close_ddisp = TRUE;
-
- switch (response_id) {
- case GTK_RESPONSE_YES :
- /* save changes */
- if (ddisp->diagram->unsaved) {
- /* we have to open the file dlg, close this one first */
- gtk_widget_destroy(widget);
- if (file_save_as(ddisp->diagram, ddisp))
- ddisp_destroy (ddisp);
- /* no way back */
- return;
- } else {
- DiaContext *ctx = dia_context_new (_("Save"));
- if (!diagram_save(ddisp->diagram, ddisp->diagram->filename, ctx))
- close_ddisp = FALSE;
- dia_context_release (ctx);
- }
- if (close_ddisp) /* saving succeeded */
- recent_file_history_add(ddisp->diagram->filename);
-
- /* fall through */
- case GTK_RESPONSE_NO :
- if (close_ddisp)
- ddisp_destroy (ddisp);
- /* fall through */
- case GTK_RESPONSE_CANCEL :
- case GTK_RESPONSE_NONE :
- case GTK_RESPONSE_DELETE_EVENT : /* closing via window manager */
- gtk_widget_destroy(widget);
- break;
- default :
- g_assert_not_reached();
- }
-}
-
void
ddisplay_close (DDisplay *ddisp)
{
Diagram *dia;
- GtkWidget *dialog, *button;
- gchar *fname;
+ DiaExitDialog *dialog;
+ DiaExitDialogResult res;
+ GPtrArray *items = NULL;
+ char *fname;
+ char *path;
+ GFile *file;
+ gboolean close_ddisp = TRUE;
- g_return_if_fail(ddisp != NULL);
+ g_return_if_fail (ddisp != NULL);
dia = ddisp->diagram;
- if ( (g_slist_length(dia->displays) > 1) ||
- (!diagram_is_modified(dia)) ) {
- ddisp_destroy(ddisp);
+ if ((g_slist_length (dia->displays) > 1) || (!diagram_is_modified (dia))) {
+ ddisp_destroy (ddisp);
return;
}
- fname = dia->filename;
- if (!fname)
- fname = _("<unnamed>");
-
- dialog = gtk_message_dialog_new(GTK_WINDOW (ddisp->shell),
- GTK_DIALOG_MODAL,
- GTK_MESSAGE_QUESTION,
- GTK_BUTTONS_NONE, /* no standard buttons */
- _("Closing diagram without saving"));
- gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
- _("The diagram '%s'\n"
- "has not been saved. Save changes now?"), fname);
- gtk_window_set_title (GTK_WINDOW(dialog), _("Close Diagram"));
-
- button = gtk_button_new_with_mnemonic (_("_Cancel"));
- gtk_dialog_add_action_widget (GTK_DIALOG(dialog), button, GTK_RESPONSE_CANCEL);
-
- button = gtk_button_new_with_mnemonic (_("_Discard Changes"));
- gtk_dialog_add_action_widget (GTK_DIALOG(dialog), button, GTK_RESPONSE_NO);
-
- /* button = gtk_button_new_with_label (_("Save and Close")); */
- button = gtk_button_new_with_mnemonic (_("_Save"));
- gtk_dialog_add_action_widget (GTK_DIALOG(dialog), button, GTK_RESPONSE_YES);
- gtk_widget_set_can_default (GTK_WIDGET (button), TRUE);
- gtk_dialog_set_default_response (GTK_DIALOG(dialog), GTK_RESPONSE_YES);
-
- g_signal_connect (G_OBJECT (dialog), "response",
- G_CALLBACK(are_you_sure_close_dialog_respond),
- ddisp);
-
- gtk_widget_show_all(dialog);
+ file = dia_diagram_get_file (dia);
+
+ if (file) {
+ fname = g_file_get_basename (file);
+ path = g_file_get_path (file);
+ } else {
+ fname = g_strdup (_("Unsaved Diagram"));
+ path = NULL;
+ }
+
+ dialog = dia_exit_dialog_new (GTK_WINDOW (ddisp->shell));
+ dia_exit_dialog_add_item (dialog, fname, path, dia);
+
+ res = dia_exit_dialog_run (dialog, &items);
+
+ g_clear_object (&dialog);
+ g_clear_pointer (&items, g_ptr_array_unref);
+
+ g_clear_pointer (&fname, g_free);
+ g_clear_pointer (&path, g_free);
+
+ switch (res) {
+ case DIA_EXIT_DIALOG_SAVE:
+ /* save changes */
+ if (ddisp->diagram->unsaved) {
+ if (file_save_as (ddisp->diagram, ddisp)) {
+ ddisp_destroy (ddisp);
+ }
+ /* no way back */
+ return;
+ } else {
+ DiaContext *ctx = dia_context_new (_("Save"));
+ if (!diagram_save (ddisp->diagram, ddisp->diagram->filename, ctx)) {
+ close_ddisp = FALSE;
+ }
+ dia_context_release (ctx);
+ }
+
+ if (close_ddisp) {
+ /* saving succeeded */
+ recent_file_history_add(ddisp->diagram->filename);
+ }
+
+ case DIA_EXIT_DIALOG_QUIT:
+ if (close_ddisp) {
+ ddisp_destroy (ddisp);
+ }
+ case DIA_EXIT_DIALOG_CANCEL:
+ break;
+ default:
+ g_return_if_reached ();
+ }
}
+
void
display_update_menu_state(DDisplay *ddisp)
{
diff --git a/app/exit_dialog.c b/app/exit_dialog.c
index 16222d8d..7e3b6747 100644
--- a/app/exit_dialog.c
+++ b/app/exit_dialog.c
@@ -19,460 +19,374 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-/* TODO: Non-modal api */
#include <config.h>
+#include <gtk/gtk.h>
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
#include "exit_dialog.h"
-#include "intl.h"
-#include <gtk/gtk.h>
+typedef struct _DiaExitDialogPrivate DiaExitDialogPrivate;
+struct _DiaExitDialogPrivate {
+ GtkWidget *dialog;
-#include <glib.h>
+ GtkWidget *file_box;
+ GtkWidget *file_list;
+ GtkListStore *file_store;
+};
-#define EXIT_DIALOG_TREEVIEW "EXIT_DIALOG_TREEVIEW"
enum {
- CHECK_COL,
- NAME_COL,
- PATH_COL,
- DATA_COL, /* To store optional data (not shown in listview) */
+ COL_SAVE,
+ COL_NAME,
+ COL_PATH,
+ COL_DIAGRAM,
NUM_COL
};
-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 exit_dialog_destroy (GtkWidget * exit_dialog,
- gpointer data);
+G_DEFINE_TYPE_WITH_PRIVATE (DiaExitDialog, dia_exit_dialog, G_TYPE_OBJECT)
-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)
+static void
+dia_exit_dialog_finalize (GObject *object)
{
- 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_get_content_area (GTK_DIALOG(dialog)));
-
- GtkWidget * label = gtk_label_new (_("The following are not saved:"));
-
- GtkWidget * scrolled;
- GtkWidget * button;
- GtkWidget * hbox;
-
- GtkWidget * treeview;
- GtkListStore * model;
- GtkCellRenderer * renderer;
- GtkTreeViewColumn * column;
-
- GdkGeometry geometry = { 0 };
-
- 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);
+ DiaExitDialog *self = DIA_EXIT_DIALOG (object);
+ DiaExitDialogPrivate *priv = dia_exit_dialog_get_instance_private (self);
+ g_signal_handlers_disconnect_by_data (priv->dialog, self);
+ gtk_widget_destroy (priv->dialog);
- gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled), GTK_WIDGET (treeview));
+ g_clear_object (&priv->file_store);
-
- 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_clear_object (&model);
- gtk_widget_show (GTK_WIDGET (treeview));
-
- gtk_widget_show_all (GTK_WIDGET(vbox));
-
- g_object_set_data (G_OBJECT (dialog), EXIT_DIALOG_TREEVIEW, treeview);
-
- g_signal_connect (G_OBJECT (dialog), "destroy",
- G_CALLBACK (exit_dialog_destroy),
- treeview);
-
- /* golden ratio */
- geometry.min_aspect = 0.618;
- geometry.max_aspect = 1.618;
- geometry.win_gravity = GDK_GRAVITY_CENTER;
- gtk_window_set_geometry_hints (GTK_WINDOW (dialog), GTK_WIDGET (vbox), &geometry,
- GDK_HINT_ASPECT|GDK_HINT_WIN_GRAVITY);
- return dialog;
+ G_OBJECT_CLASS (dia_exit_dialog_parent_class)->finalize (object);
}
-/**
- * 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)
+
+static void
+dia_exit_dialog_class_init (DiaExitDialogClass *klass)
{
- GtkTreeView * treeview;
- GtkTreeIter iter;
- GtkListStore * model;
- const gchar * name_copy = g_strdup (name);
- const gchar * path_copy = g_strdup (path);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
- treeview = g_object_get_data (G_OBJECT (dialog), EXIT_DIALOG_TREEVIEW);
+ object_class->finalize = dia_exit_dialog_finalize;
+}
- model = GTK_LIST_STORE (gtk_tree_view_get_model (treeview));
- gtk_list_store_append (model, &iter);
+static void
+clear_item (gpointer data)
+{
+ DiaExitDialogItem *item = data;
- gtk_list_store_set (model, &iter,
- CHECK_COL, 1,
- NAME_COL, name_copy,
- PATH_COL, path_copy,
- DATA_COL, optional_data,
- -1);
-}
+ g_clear_pointer (&item->name, g_free);
+ g_clear_pointer (&item->path, g_free);
+ g_clear_object (&item->data);
-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;
- case EXIT_DIALOG_EXIT_SAVE_SELECTED :
- break;
- default : /* e.g. if closed by window manager button */
- return EXIT_DIALOG_EXIT_CANCEL;
- }
-
- count = get_selected_items (dialog, items);
-
- if (count == 0)
- {
- GtkWidget * msg_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog),
- GTK_DIALOG_MODAL,
- GTK_MESSAGE_WARNING,
- GTK_BUTTONS_YES_NO,
- _("Nothing selected for saving. Would you like
to try again?"));
-
- 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;
- }
- }
+ g_free (data);
}
/**
* 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.
+ * @self: the #DiaExitDialog
+ *
+ * Returns: The selected items.
+ *
+ * Since: 0.98
*/
-int
-get_selected_items (GtkWidget *dialog,
- exit_dialog_item_array_t **items)
+static GPtrArray *
+get_selected_items (DiaExitDialog *self)
{
- GtkTreeView *treeview;
- GtkTreeIter iter;
- GtkListStore *model;
- gboolean valid;
- GSList *list = NULL;
- GSList *list_iter;
- int selected_count;
- int i;
-
- treeview = g_object_get_data (G_OBJECT (dialog), EXIT_DIALOG_TREEVIEW);
+ DiaExitDialogPrivate *priv = dia_exit_dialog_get_instance_private (self);
+ GtkTreeIter iter;
+ gboolean valid;
+ GPtrArray *selected;
- model = GTK_LIST_STORE (gtk_tree_view_get_model (treeview));
+ selected = g_ptr_array_new_with_free_func (clear_item);
/* Get the first iter in the list */
- valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter);
+ valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->file_store),
+ &iter);
/* Get the selected items */
while (valid) {
- char *name;
- 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,
+ DiaExitDialogItem *item = g_new (DiaExitDialogItem, 1);
+ gboolean is_selected;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (priv->file_store), &iter,
+ COL_SAVE, &is_selected,
+ COL_NAME, &item->name,
+ COL_PATH, &item->path,
+ COL_DIAGRAM, &item->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);
+ g_ptr_array_add (selected, item);
+ } else {
+ clear_item (item);
}
- valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &iter);
+ valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->file_store),
+ &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;
- g_assert(list_iter!=NULL); /* can't be if g_slist_length works */
- 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;
+ return selected;
}
-/**
- * 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)
+static void
+save_sensitive (DiaExitDialog *self)
{
- if (items) {
- for (int i = 0 ; i < items->array_size ; i++) {
- /* Cast is needed to remove warning because of const decl */
- g_clear_pointer (&items->array[i].name, g_free);
- g_clear_pointer (&items->array[i].path, g_free);
- }
+ DiaExitDialogPrivate *priv = dia_exit_dialog_get_instance_private (self);
+ GPtrArray *selected = get_selected_items (self);
- g_clear_pointer (&items, g_free);
- }
+ gtk_dialog_set_response_sensitive (GTK_DIALOG (priv->dialog),
+ DIA_EXIT_DIALOG_SAVE,
+ selected->len > 0);
+
+ g_clear_pointer (&selected, g_ptr_array_unref);
}
-/**
+/*
* 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)
+static void
+toggle_check_button (GtkCellRendererToggle *renderer,
+ char *path,
+ DiaExitDialog *self)
{
- 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);
- }
+ DiaExitDialogPrivate *priv = dia_exit_dialog_get_instance_private (self);
+ GtkTreeIter iter;
+ gboolean value;
+
+ if (gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (priv->file_store),
+ &iter,
+ path)) {
+ gtk_tree_model_get (GTK_TREE_MODEL (priv->file_store), &iter,
+ COL_SAVE, &value,
+ -1);
+ gtk_list_store_set (priv->file_store, &iter, COL_SAVE, !value, -1);
+
+ save_sensitive (self);
+ }
}
-/*
- * Signal handler for "destroy" event to free memory allocated for the exit_dialog.
- *
- * @param exit_dialog
- * @param data Exit dialog's treeview
- */
static void
-exit_dialog_destroy (GtkWidget *exit_dialog,
- gpointer data)
+dia_exit_dialog_init (DiaExitDialog *self)
{
- GtkTreeView *treeview;
- GtkTreeIter iter;
- GtkTreeModel *model;
- gboolean valid;
-
- treeview = g_object_get_data (G_OBJECT (exit_dialog), EXIT_DIALOG_TREEVIEW);
-
- model = GTK_TREE_MODEL (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) {
- char *name = NULL;
- char *path = NULL;
-
- gtk_tree_model_get (model, &iter,
- NAME_COL, &name,
- PATH_COL, &path,
- -1);
-
- g_clear_pointer (&name, g_free);
- g_clear_pointer (&path, g_free);
-
- valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &iter);
- }
+ DiaExitDialogPrivate *priv = dia_exit_dialog_get_instance_private (self);
+
+ GtkWidget *label;
+
+ GtkWidget *scrolled;
+ GtkWidget *button;
+
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ priv->dialog = gtk_message_dialog_new (NULL,
+ GTK_DIALOG_MODAL,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_NONE,
+ NULL);
+
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (priv->dialog),
+ _("If you don’t save, all your changes will be permanently
lost."));
+
+ button = gtk_dialog_add_button (GTK_DIALOG (priv->dialog),
+ _("Close _without Saving"),
+ DIA_EXIT_DIALOG_QUIT);
+
+ /* "use" it for gtk2 */
+ gtk_widget_get_name (button);
+ /*
+ GTK3:
+ gtk_style_context_add_class (gtk_widget_get_style_context (button),
+ "destructive-action");
+ */
+
+ gtk_dialog_add_button (GTK_DIALOG (priv->dialog),
+ _("_Cancel"),
+ DIA_EXIT_DIALOG_CANCEL);
+
+ gtk_dialog_add_button (GTK_DIALOG (priv->dialog),
+ _("_Save"),
+ DIA_EXIT_DIALOG_SAVE);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (priv->dialog),
+ DIA_EXIT_DIALOG_SAVE);
+
+
+ priv->file_box = gtk_vbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (priv->dialog))),
+ priv->file_box,
+ FALSE,
+ FALSE,
+ 0);
+ gtk_widget_show (priv->file_box);
+
+
+ /* Scrolled window for displaying things which need saving */
+ scrolled = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
+ GTK_SHADOW_IN);
+ /* gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (scrolled),
+ 90); */
+ g_object_set (scrolled, "height-request", 90, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
+ GTK_POLICY_NEVER,
+ GTK_POLICY_AUTOMATIC);
+ gtk_widget_show (scrolled);
+
+ priv->file_store = gtk_list_store_new (NUM_COL,
+ G_TYPE_BOOLEAN,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ DIA_TYPE_DIAGRAM);
+
+ priv->file_list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (priv->file_store));
+ gtk_tree_view_set_tooltip_column (GTK_TREE_VIEW (priv->file_list),
+ COL_PATH);
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->file_list), FALSE);
+ gtk_widget_show (priv->file_list);
+
+ renderer = gtk_cell_renderer_toggle_new ();
+ gtk_cell_renderer_toggle_set_activatable (GTK_CELL_RENDERER_TOGGLE (renderer),
+ TRUE);
+ column = gtk_tree_view_column_new_with_attributes (_("Save"), renderer,
+ "active", COL_SAVE,
+ NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (priv->file_list), column);
+
+ g_signal_connect (G_OBJECT (renderer), "toggled",
+ G_CALLBACK (toggle_check_button),
+ self);
+
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes (_("Name"), renderer,
+ "text", COL_NAME,
+ NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (priv->file_list), column);
+
+
+ gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled),
+ priv->file_list);
+
+ label = g_object_new (GTK_TYPE_LABEL,
+ "label", _("S_elect the diagrams you want to save:"),
+ "use-underline", TRUE,
+ "mnemonic-widget", priv->file_list,
+ "xalign", 0.0,
+ "visible", TRUE,
+ NULL);
+
+
+ gtk_box_pack_start (GTK_BOX (priv->file_box), label, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (priv->file_box), scrolled, FALSE, FALSE, 0);
+
+ g_signal_connect_swapped (G_OBJECT (priv->dialog), "destroy",
+ G_CALLBACK (g_object_unref),
+ self);
}
/**
- * 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.
+ * dia_exit_dialog_new:
+ * @parent: This is needed for modal behavior.
+ *
+ * A dialog to allow a user to select which unsaved files to save
+ * (if any) or to abort exiting
+ *
+ * Since: 0.98
*/
-static void selected_state_set_all (GtkTreeView * treeview,
- gboolean state)
+DiaExitDialog *
+dia_exit_dialog_new (GtkWindow *parent)
{
- GtkTreeIter iter;
- GtkListStore * model;
- gboolean valid;
+ DiaExitDialog *self = g_object_new (DIA_TYPE_EXIT_DIALOG, NULL);
+ DiaExitDialogPrivate *priv = dia_exit_dialog_get_instance_private (self);
- model = GTK_LIST_STORE (gtk_tree_view_get_model (treeview));
+ gtk_window_set_transient_for (GTK_WINDOW (priv->dialog), parent);
- /* 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);
- }
+ return self;
}
+
/**
- * 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
+ * dia_exit_dialog_add_item:
+ * @self: the #DiaExitDialog
+ * @name: User identifiable name of the thing which needs saving.
+ * @path: File system path of the thing which needs saving.
+ * @diagram: The unsaved #Diagram
+ *
+ * Add name and path of a file that needs to be saved
+ *
+ * Since: 0.98
*/
-static void select_all_clicked (GtkButton * button,
- gpointer data)
+void
+dia_exit_dialog_add_item (DiaExitDialog *self,
+ const char *name,
+ const char *filepath,
+ Diagram *diagram)
{
- selected_state_set_all (data, TRUE);
+ DiaExitDialogPrivate *priv = dia_exit_dialog_get_instance_private (self);
+ GtkTreeIter iter;
+ char *title = NULL;
+ int n;
+
+ gtk_list_store_append (priv->file_store, &iter);
+ gtk_list_store_set (priv->file_store, &iter,
+ COL_SAVE, 1,
+ COL_NAME, name,
+ COL_PATH, filepath,
+ COL_DIAGRAM, diagram,
+ -1);
+
+ save_sensitive (self);
+
+ n = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (priv->file_store), NULL);
+
+ if (n == 1) {
+ title = g_markup_printf_escaped ("Save changes to diagram “%s” before closing?",
+ name);
+ gtk_widget_hide (priv->file_box);
+ } else {
+ title = g_markup_printf_escaped (ngettext ("There is %d diagram with unsaved changes. "
+ "Save changes before closing?",
+ "There are %d diagrams with unsaved changes. "
+ "Save changes before closing?",
+ n), n);
+ gtk_widget_show (priv->file_box);
+ }
+
+ g_object_set (priv->dialog, "text", title, NULL);
+
+ g_clear_pointer (&title, g_free);
}
-/**
- * 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)
+
+DiaExitDialogResult
+dia_exit_dialog_run (DiaExitDialog *self,
+ GPtrArray **items)
{
- selected_state_set_all (data, FALSE);
-}
+ DiaExitDialogPrivate *priv = dia_exit_dialog_get_instance_private (self);
+ int result;
+
+ result = gtk_dialog_run (GTK_DIALOG (priv->dialog));
+ *items = NULL;
+ if (result == DIA_EXIT_DIALOG_SAVE) {
+ *items = get_selected_items (self);
+ } else if (result != DIA_EXIT_DIALOG_QUIT &&
+ result != DIA_EXIT_DIALOG_CANCEL) {
+ result = DIA_EXIT_DIALOG_CANCEL;
+ }
+
+ return result;
+}
diff --git a/app/exit_dialog.h b/app/exit_dialog.h
index 88e489e6..3307f638 100644
--- a/app/exit_dialog.h
+++ b/app/exit_dialog.h
@@ -19,51 +19,55 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#ifndef EXIT_DIALOG_H
-#define EXIT_DIALOG_H
+#pragma once
#include <gtk/gtk.h>
-G_BEGIN_DECLS
+#include "diagram.h"
-enum {
- EXIT_DIALOG_EXIT_NO_SAVE,
- EXIT_DIALOG_EXIT_SAVE_SELECTED,
- EXIT_DIALOG_EXIT_CANCEL
-};
-GtkWidget *
-exit_dialog_make (GtkWindow * parent_window,
- gchar * title);
+G_BEGIN_DECLS
-void
-exit_dialog_add_item (GtkWidget *dialog,
- const char *name,
- const char *filepath,
- const gpointer optional_data);
+
+/**
+ * DiaExitDialogResult:
+ * @DIA_EXIT_DIALOG_SAVE: The selectd diagrams should be saved, then quit
+ * @DIA_EXIT_DIALOG_CANCEL: Don't quit, don't save
+ * @DIA_EXIT_DIALOG_QUIT: Close anyway, loosing changes
+ *
+ * Since: 0.98
+ */
+typedef enum /*< enum,prefix=DIA >*/
+{
+ DIA_EXIT_DIALOG_SAVE, /*< nick=save >*/
+ DIA_EXIT_DIALOG_CANCEL, /*< nick=cancel >*/
+ DIA_EXIT_DIALOG_QUIT, /*< nick=quit >*/
+} DiaExitDialogResult;
typedef struct {
- char *name;
- char *path;
- gpointer data;
-} exit_dialog_item_t;
+ char *name;
+ char *path;
+ Diagram *data;
+} DiaExitDialogItem;
-typedef struct
-{
- size_t array_size;
- exit_dialog_item_t * array;
+struct _DiaExitDialog {
+ GObject parent;
+};
-} exit_dialog_item_array_t;
-gint
-exit_dialog_run (GtkWidget * dialog,
- exit_dialog_item_array_t ** items_to_save);
+#define DIA_TYPE_EXIT_DIALOG dia_exit_dialog_get_type ()
+G_DECLARE_FINAL_TYPE (DiaExitDialog, dia_exit_dialog, DIA, EXIT_DIALOG, GObject)
-void exit_dialog_free_items (exit_dialog_item_array_t *);
-G_END_DECLS
+DiaExitDialog *dia_exit_dialog_new (GtkWindow *parent);
+void dia_exit_dialog_add_item (DiaExitDialog *self,
+ const char *name,
+ const char *filepath,
+ Diagram *diagram);
+DiaExitDialogResult dia_exit_dialog_run (DiaExitDialog *self,
+ GPtrArray **items);
-#endif
+G_END_DECLS
diff --git a/app/meson.build b/app/meson.build
index 565335d3..7b3b63a0 100644
--- a/app/meson.build
+++ b/app/meson.build
@@ -79,6 +79,8 @@ dia_sources = [
'textedit_tool.c',
]
+sources += gnome.mkenums_simple('dia-app-enums', sources: 'exit_dialog.h')
+
#TODO: this is workaround for openbsd where libraries are in /usr/lib/local
# Potential parent issues: https://github.com/mesonbuild/meson/issues/3570
# https://github.com/mesonbuild/meson/issues/4468
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]