/*
* gedit-close-confirmation-dialog.c
* This file is part of gedit
*
* Copyright (C) 2004-2005 GNOME Foundation
* Copyright (C) 2015 Sébastien Wilmet
*
* 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, see .
*/
#include "config.h"
#include "gedit-close-confirmation-dialog.h"
#include
#include
#include
#include
#include
#include
/* Mode */
enum
{
SINGLE_DOC_MODE,
MULTIPLE_DOCS_MODE
};
#define GET_MODE(dlg) (((dlg->unsaved_documents != NULL) && \
(dlg->unsaved_documents->next == NULL)) ? \
SINGLE_DOC_MODE : MULTIPLE_DOCS_MODE)
#define GEDIT_SAVE_DOCUMENT_KEY "gedit-save-document"
struct _GeditCloseConfirmationDialog
{
GtkMessageDialog parent_instance;
GList *unsaved_documents;
GList *selected_documents;
GtkWidget *list_box;
};
enum
{
PROP_0,
PROP_UNSAVED_DOCUMENTS,
LAST_PROP
};
static GParamSpec *properties[LAST_PROP];
G_DEFINE_TYPE (GeditCloseConfirmationDialog,
gedit_close_confirmation_dialog,
GTK_TYPE_MESSAGE_DIALOG)
static void set_unsaved_document (GeditCloseConfirmationDialog *dlg,
const GList *list);
static GList *
get_selected_docs (GtkWidget *list_box)
{
GList *rows;
GList *l;
GList *ret = NULL;
rows = gtk_container_get_children (GTK_CONTAINER (list_box));
for (l = rows; l != NULL; l = l->next)
{
GtkWidget *row = l->data;
GtkWidget *check_button;
check_button = gtk_bin_get_child (GTK_BIN (row));
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check_button)))
{
GeditDocument *doc;
doc = g_object_get_data (G_OBJECT (row), GEDIT_SAVE_DOCUMENT_KEY);
g_return_val_if_fail (doc != NULL, NULL);
ret = g_list_prepend (ret, doc);
}
}
g_list_free (rows);
return g_list_reverse (ret);
}
/* Since we connect in the constructor we are sure this handler will be called
* before the user ones.
*/
static void
response_cb (GeditCloseConfirmationDialog *dlg,
gint response_id,
gpointer data)
{
g_return_if_fail (GEDIT_IS_CLOSE_CONFIRMATION_DIALOG (dlg));
if (dlg->selected_documents != NULL)
{
g_list_free (dlg->selected_documents);
dlg->selected_documents = NULL;
}
if (response_id == GTK_RESPONSE_YES)
{
if (GET_MODE (dlg) == SINGLE_DOC_MODE)
{
dlg->selected_documents = g_list_copy (dlg->unsaved_documents);
}
else
{
dlg->selected_documents = get_selected_docs (dlg->list_box);
}
}
}
static void
gedit_close_confirmation_dialog_init (GeditCloseConfirmationDialog *dlg)
{
gtk_window_set_title (GTK_WINDOW (dlg), "");
gtk_window_set_modal (GTK_WINDOW (dlg), TRUE);
gtk_window_set_destroy_with_parent (GTK_WINDOW (dlg), TRUE);
g_signal_connect (dlg,
"response",
G_CALLBACK (response_cb),
NULL);
}
static void
gedit_close_confirmation_dialog_finalize (GObject *object)
{
GeditCloseConfirmationDialog *dlg = GEDIT_CLOSE_CONFIRMATION_DIALOG (object);
g_list_free (dlg->unsaved_documents);
g_list_free (dlg->selected_documents);
/* Call the parent's destructor */
G_OBJECT_CLASS (gedit_close_confirmation_dialog_parent_class)->finalize (object);
}
static void
gedit_close_confirmation_dialog_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GeditCloseConfirmationDialog *dlg;
dlg = GEDIT_CLOSE_CONFIRMATION_DIALOG (object);
switch (prop_id)
{
case PROP_UNSAVED_DOCUMENTS:
set_unsaved_document (dlg, g_value_get_pointer (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gedit_close_confirmation_dialog_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GeditCloseConfirmationDialog *dlg = GEDIT_CLOSE_CONFIRMATION_DIALOG (object);
switch (prop_id)
{
case PROP_UNSAVED_DOCUMENTS:
g_value_set_pointer (value, dlg->unsaved_documents);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gedit_close_confirmation_dialog_class_init (GeditCloseConfirmationDialogClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->set_property = gedit_close_confirmation_dialog_set_property;
gobject_class->get_property = gedit_close_confirmation_dialog_get_property;
gobject_class->finalize = gedit_close_confirmation_dialog_finalize;
properties[PROP_UNSAVED_DOCUMENTS] =
g_param_spec_pointer ("unsaved-documents",
"Unsaved Documents",
"List of Unsaved Documents",
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, LAST_PROP, properties);
}
GList *
gedit_close_confirmation_dialog_get_selected_documents (GeditCloseConfirmationDialog *dlg)
{
g_return_val_if_fail (GEDIT_IS_CLOSE_CONFIRMATION_DIALOG (dlg), NULL);
return g_list_copy (dlg->selected_documents);
}
GtkWidget *
gedit_close_confirmation_dialog_new (GtkWindow *parent,
GList *unsaved_documents)
{
GtkWidget *dlg;
g_return_val_if_fail (unsaved_documents != NULL, NULL);
dlg = g_object_new (GEDIT_TYPE_CLOSE_CONFIRMATION_DIALOG,
"unsaved-documents", unsaved_documents,
"message-type", GTK_MESSAGE_QUESTION,
NULL);
if (parent != NULL)
{
gtk_window_group_add_window (gedit_window_get_group (GEDIT_WINDOW (parent)),
GTK_WINDOW (dlg));
gtk_window_set_transient_for (GTK_WINDOW (dlg), parent);
}
return dlg;
}
GtkWidget *
gedit_close_confirmation_dialog_new_single (GtkWindow *parent,
GeditDocument *doc)
{
GtkWidget *dlg;
GList *unsaved_documents;
g_return_val_if_fail (doc != NULL, NULL);
unsaved_documents = g_list_prepend (NULL, doc);
dlg = gedit_close_confirmation_dialog_new (parent, unsaved_documents);
g_list_free (unsaved_documents);
return dlg;
}
static void
add_buttons (GeditCloseConfirmationDialog *dlg)
{
GtkWidget *close_button;
gboolean save_as = FALSE;
close_button = gtk_dialog_add_button (GTK_DIALOG (dlg),
_("Close _without Saving"),
GTK_RESPONSE_NO);
gtk_style_context_add_class (gtk_widget_get_style_context (close_button),
"destructive-action");
gtk_dialog_add_button (GTK_DIALOG (dlg), _("_Cancel"), GTK_RESPONSE_CANCEL);
if (GET_MODE (dlg) == SINGLE_DOC_MODE)
{
GeditDocument *doc;
GtkSourceFile *file;
doc = GEDIT_DOCUMENT (dlg->unsaved_documents->data);
file = gedit_document_get_file (doc);
if (gtk_source_file_is_readonly (file) ||
_gedit_document_is_untitled (doc))
{
save_as = TRUE;
}
}
gtk_dialog_add_button (GTK_DIALOG (dlg),
save_as ? _("_Save As…") : _("_Save"),
GTK_RESPONSE_YES);
gtk_dialog_set_default_response (GTK_DIALOG (dlg),
GTK_RESPONSE_YES);
}
static gchar *
get_text_secondary_label (GeditDocument *doc)
{
glong seconds;
gchar *secondary_msg;
seconds = MAX (1, _gedit_document_get_seconds_since_last_save_or_load (doc));
if (seconds < 55)
{
secondary_msg = g_strdup_printf (
ngettext ("If you don’t save, changes from the last %ld second "
"will be permanently lost.",
"If you don’t save, changes from the last %ld seconds "
"will be permanently lost.",
seconds),
seconds);
}
else if (seconds < 75) /* 55 <= seconds < 75 */
{
secondary_msg = g_strdup (_("If you don’t save, changes from the last minute "
"will be permanently lost."));
}
else if (seconds < 110) /* 75 <= seconds < 110 */
{
secondary_msg = g_strdup_printf (
ngettext ("If you don’t save, changes from the last minute and %ld "
"second will be permanently lost.",
"If you don’t save, changes from the last minute and %ld "
"seconds will be permanently lost.",
seconds - 60 ),
seconds - 60);
}
else if (seconds < 3600)
{
secondary_msg = g_strdup_printf (
ngettext ("If you don’t save, changes from the last %ld minute "
"will be permanently lost.",
"If you don’t save, changes from the last %ld minutes "
"will be permanently lost.",
seconds / 60),
seconds / 60);
}
else if (seconds < 7200)
{
gint minutes;
seconds -= 3600;
minutes = seconds / 60;
if (minutes < 5)
{
secondary_msg = g_strdup (_("If you don’t save, changes from the last hour "
"will be permanently lost."));
}
else
{
secondary_msg = g_strdup_printf (
ngettext ("If you don’t save, changes from the last hour and %d "
"minute will be permanently lost.",
"If you don’t save, changes from the last hour and %d "
"minutes will be permanently lost.",
minutes),
minutes);
}
}
else
{
gint hours;
hours = seconds / 3600;
secondary_msg = g_strdup_printf (
ngettext ("If you don’t save, changes from the last %d hour "
"will be permanently lost.",
"If you don’t save, changes from the last %d hours "
"will be permanently lost.",
hours),
hours);
}
return secondary_msg;
}
static void
build_single_doc_dialog (GeditCloseConfirmationDialog *dlg)
{
GeditDocument *doc;
gchar *doc_name;
gchar *str;
gchar *markup_str;
g_return_if_fail (dlg->unsaved_documents->data != NULL);
doc = GEDIT_DOCUMENT (dlg->unsaved_documents->data);
add_buttons (dlg);
/* Primary message */
doc_name = tepl_file_get_short_name (tepl_buffer_get_file (TEPL_BUFFER (doc)));
str = g_markup_printf_escaped (_("Save changes to document “%s” before closing?"), doc_name);
g_free (doc_name);
markup_str = g_strconcat ("", str, "", NULL);
g_free (str);
gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (dlg), markup_str);
g_free (markup_str);
/* Secondary message */
str = get_text_secondary_label (doc);
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dlg), "%s", str);
g_free (str);
}
static GtkWidget *
create_list_box (GeditCloseConfirmationDialog *dlg)
{
GtkWidget *list_box;
GList *l;
list_box = gtk_list_box_new ();
for (l = dlg->unsaved_documents; l != NULL; l = l->next)
{
GeditDocument *doc = l->data;
gchar *name;
GtkWidget *check_button;
GtkWidget *row;
name = tepl_file_get_short_name (tepl_buffer_get_file (TEPL_BUFFER (doc)));
check_button = gtk_check_button_new_with_label (name);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button), TRUE);
gtk_widget_set_halign (check_button, GTK_ALIGN_START);
g_free (name);
row = gtk_list_box_row_new ();
gtk_container_add (GTK_CONTAINER (row), check_button);
gtk_widget_show_all (row);
g_object_set_data_full (G_OBJECT (row),
GEDIT_SAVE_DOCUMENT_KEY,
g_object_ref (doc),
(GDestroyNotify) g_object_unref);
gtk_list_box_insert (GTK_LIST_BOX (list_box), row, -1);
}
return list_box;
}
static void
build_multiple_docs_dialog (GeditCloseConfirmationDialog *dlg)
{
GtkWidget *content_area;
GtkWidget *vbox;
GtkWidget *select_label;
GtkWidget *scrolledwindow;
GtkWidget *secondary_label;
gchar *str;
gchar *markup_str;
add_buttons (dlg);
gtk_window_set_resizable (GTK_WINDOW (dlg), TRUE);
/* Primary message */
str = g_strdup_printf (
ngettext ("There is %d document with unsaved changes. "
"Save changes before closing?",
"There are %d documents with unsaved changes. "
"Save changes before closing?",
g_list_length (dlg->unsaved_documents)),
g_list_length (dlg->unsaved_documents));
markup_str = g_strconcat ("", str, "", NULL);
g_free (str);
gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (dlg), markup_str);
g_free (markup_str);
/* List of unsaved documents */
content_area = gtk_dialog_get_content_area (GTK_DIALOG (dlg));
gtk_box_set_spacing (GTK_BOX (content_area), 10);
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 8);
gtk_widget_set_margin_start (vbox, 30);
gtk_widget_set_margin_end (vbox, 30);
gtk_widget_set_margin_bottom (vbox, 12);
gtk_box_pack_start (GTK_BOX (content_area), vbox, TRUE, TRUE, 0);
select_label = gtk_label_new_with_mnemonic (_("S_elect the documents you want to save:"));
gtk_box_pack_start (GTK_BOX (vbox), select_label, FALSE, FALSE, 0);
gtk_label_set_line_wrap (GTK_LABEL (select_label), TRUE);
gtk_label_set_max_width_chars (GTK_LABEL (select_label), 72);
gtk_widget_set_halign (select_label, GTK_ALIGN_START);
scrolledwindow = gtk_scrolled_window_new (NULL, NULL);
gtk_box_pack_start (GTK_BOX (vbox), scrolledwindow, TRUE, TRUE, 0);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow),
GTK_SHADOW_IN);
gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (scrolledwindow), 90);
dlg->list_box = create_list_box (dlg);
gtk_container_add (GTK_CONTAINER (scrolledwindow), dlg->list_box);
/* Secondary label */
secondary_label = gtk_label_new (_("If you don’t save, "
"all your changes will be permanently lost."));
gtk_box_pack_start (GTK_BOX (vbox), secondary_label, FALSE, FALSE, 0);
gtk_label_set_line_wrap (GTK_LABEL (secondary_label), TRUE);
gtk_widget_set_halign (secondary_label, GTK_ALIGN_CENTER);
gtk_widget_set_valign (secondary_label, GTK_ALIGN_START);
gtk_label_set_selectable (GTK_LABEL (secondary_label), TRUE);
gtk_label_set_max_width_chars (GTK_LABEL (secondary_label), 72);
gtk_label_set_mnemonic_widget (GTK_LABEL (select_label), dlg->list_box);
gtk_widget_show_all (vbox);
}
static void
set_unsaved_document (GeditCloseConfirmationDialog *dlg,
const GList *list)
{
g_return_if_fail (list != NULL);
g_return_if_fail (dlg->unsaved_documents == NULL);
dlg->unsaved_documents = g_list_copy ((GList *)list);
if (GET_MODE (dlg) == SINGLE_DOC_MODE)
{
build_single_doc_dialog (dlg);
}
else
{
build_multiple_docs_dialog (dlg);
}
}
const GList *
gedit_close_confirmation_dialog_get_unsaved_documents (GeditCloseConfirmationDialog *dlg)
{
g_return_val_if_fail (GEDIT_IS_CLOSE_CONFIRMATION_DIALOG (dlg), NULL);
return dlg->unsaved_documents;
}
/* ex:set ts=8 noet: */