[gimp] Bug 706309 - Add a “Save As” button to the “Quit GIMP” dialog



commit 0089a017f0b4b522a9e8f9fb0be8155e0a78212d
Author: Michael Natterer <mitch gimp org>
Date:   Sat Oct 1 20:55:13 2016 +0200

    Bug 706309 - Add a “Save As” button to the “Quit GIMP” dialog
    
    Add GimpCellRendererButton and use it to add a "Save" icon to each row
    of dirty images. Click invokes the "edit-save" action, shift-click
    invokes "edit-save-as". Also add a tooltip for the icon button.
    
    Involves minor changes to GimpContainerTreeView to allow
    GimpCellRendererButton to be added, and to allow external
    "query-tooltip" handlers to run.

 app/dialogs/quit-dialog.c            |  173 ++++++++++++++++++++++++++++++----
 app/widgets/Makefile.am              |    2 +
 app/widgets/gimpcellrendererbutton.c |  143 ++++++++++++++++++++++++++++
 app/widgets/gimpcellrendererbutton.h |   59 ++++++++++++
 app/widgets/gimpcontainertreeview.c  |   32 +++++--
 app/widgets/widgets-types.h          |    1 +
 6 files changed, 384 insertions(+), 26 deletions(-)
---
diff --git a/app/dialogs/quit-dialog.c b/app/dialogs/quit-dialog.c
index df56262..d5ed14a 100644
--- a/app/dialogs/quit-dialog.c
+++ b/app/dialogs/quit-dialog.c
@@ -37,7 +37,9 @@
 #include "display/gimpdisplay.h"
 #include "display/gimpdisplay-foreach.h"
 #include "display/gimpdisplayshell.h"
+#include "display/gimpimagewindow.h"
 
+#include "widgets/gimpcellrendererbutton.h"
 #include "widgets/gimpcontainertreestore.h"
 #include "widgets/gimpcontainertreeview.h"
 #include "widgets/gimpcontainerview.h"
@@ -45,6 +47,7 @@
 #include "widgets/gimphelp-ids.h"
 #include "widgets/gimpmessagebox.h"
 #include "widgets/gimpmessagedialog.h"
+#include "widgets/gimpuimanager.h"
 #include "widgets/gimpviewrenderer.h"
 #include "widgets/gimpwidgets-utils.h"
 
@@ -57,20 +60,22 @@ typedef struct _QuitDialog QuitDialog;
 
 struct _QuitDialog
 {
-  Gimp            *gimp;
-  GimpContainer   *images;
-  GimpContext     *context;
+  Gimp                  *gimp;
+  GimpContainer         *images;
+  GimpContext           *context;
 
-  gboolean         do_quit;
+  gboolean               do_quit;
 
-  GtkWidget       *dialog;
-  GtkWidget       *ok_button;
-  GimpMessageBox  *box;
-  GtkWidget       *lost_label;
-  GtkWidget       *hint_label;
-
-  guint            accel_key;
-  GdkModifierType  accel_mods;
+  GtkWidget             *dialog;
+  GimpContainerTreeView *tree_view;
+  GtkTreeViewColumn     *save_column;
+  GtkWidget             *ok_button;
+  GimpMessageBox        *box;
+  GtkWidget             *lost_label;
+  GtkWidget             *hint_label;
+
+  guint                  accel_key;
+  GdkModifierType        accel_mods;
 };
 
 
@@ -98,7 +103,16 @@ static void        quit_close_all_dialog_name_cell_func    (GtkTreeViewColumn *t
                                                             GtkTreeModel      *tree_model,
                                                             GtkTreeIter       *iter,
                                                             gpointer           data);
-
+static void        quit_close_all_dialog_save_clicked      (GtkCellRenderer   *cell,
+                                                            const gchar       *path,
+                                                            GdkModifierType    state,
+                                                            QuitDialog        *dialog);
+static gboolean    quit_close_all_dialog_query_tooltip     (GtkWidget         *widget,
+                                                            gint               x,
+                                                            gint               y,
+                                                            gboolean           keyboard_tip,
+                                                            GtkTooltip        *tooltip,
+                                                            QuitDialog        *dialog);
 
 /*  public functions  */
 
@@ -121,6 +135,7 @@ quit_close_all_dialog_new (Gimp     *gimp,
   QuitDialog            *dialog;
   GtkWidget             *view;
   GimpContainerTreeView *tree_view;
+  GtkTreeViewColumn     *column;
   GtkCellRenderer       *renderer;
   GtkWidget             *dnd_widget;
   GtkAccelGroup         *accel_group;
@@ -187,15 +202,35 @@ quit_close_all_dialog_new (Gimp     *gimp,
 
   view = gimp_container_tree_view_new (dialog->images, dialog->context,
                                        view_size, 1);
-  tree_view = GIMP_CONTAINER_TREE_VIEW (view);
+  gimp_container_box_set_size_request (GIMP_CONTAINER_BOX (view),
+                                       -1,
+                                       rows * (view_size + 2));
+
+  dialog->tree_view = tree_view = GIMP_CONTAINER_TREE_VIEW (view);
+
+  gtk_tree_view_column_set_expand (tree_view->main_column, TRUE);
+
   renderer = gimp_container_tree_view_get_name_cell (tree_view);
   gtk_tree_view_column_set_cell_data_func (tree_view->main_column,
                                            renderer,
                                            quit_close_all_dialog_name_cell_func,
                                            NULL, NULL);
-  gimp_container_box_set_size_request (GIMP_CONTAINER_BOX (view),
-                                       -1,
-                                       rows * (view_size + 2));
+
+  dialog->save_column = column = gtk_tree_view_column_new ();
+  renderer = gimp_cell_renderer_button_new ();
+  g_object_set (renderer,
+                "icon-name", "document-save",
+                NULL);
+  gtk_tree_view_column_pack_end (column, renderer, FALSE);
+  gtk_tree_view_column_set_attributes (column, renderer, NULL);
+
+  gtk_tree_view_append_column (tree_view->view, column);
+  gimp_container_tree_view_add_toggle_cell (tree_view, renderer);
+
+  g_signal_connect (renderer, "clicked",
+                    G_CALLBACK (quit_close_all_dialog_save_clicked),
+                    dialog);
+
   gtk_box_pack_start (GTK_BOX (dialog->box), view, TRUE, TRUE, 0);
   gtk_widget_show (view);
 
@@ -208,6 +243,10 @@ quit_close_all_dialog_new (Gimp     *gimp,
                            (GimpDndDragViewableFunc) gimp_dnd_get_drag_data,
                            NULL);
 
+  g_signal_connect (tree_view->view, "query-tooltip",
+                    G_CALLBACK (quit_close_all_dialog_query_tooltip),
+                    dialog);
+
   if (do_quit)
     dialog->lost_label = gtk_label_new (_("If you quit GIMP now, "
                                           "these changes will be lost."));
@@ -436,3 +475,103 @@ quit_close_all_dialog_name_cell_func (GtkTreeViewColumn *tree_column,
   g_object_unref (renderer);
   g_free (name);
 }
+
+static void
+quit_close_all_dialog_save_clicked (GtkCellRenderer *cell,
+                                    const gchar     *path_str,
+                                    GdkModifierType  state,
+                                    QuitDialog      *dialog)
+{
+  GtkTreePath *path = gtk_tree_path_new_from_string (path_str);
+  GtkTreeIter  iter;
+
+  if (gtk_tree_model_get_iter (dialog->tree_view->model, &iter, path))
+    {
+      GimpViewRenderer *renderer;
+      GimpImage        *image;
+      GList            *list;
+
+      gtk_tree_model_get (dialog->tree_view->model, &iter,
+                          GIMP_CONTAINER_TREE_STORE_COLUMN_RENDERER, &renderer,
+                          -1);
+
+      image = GIMP_IMAGE (renderer->viewable);
+      g_object_unref (renderer);
+
+      for (list = gimp_get_display_iter (dialog->gimp);
+           list;
+           list = g_list_next (list))
+        {
+          GimpDisplay *display = list->data;
+
+          if (gimp_display_get_image (display) == image)
+            {
+              GimpDisplayShell *shell  = gimp_display_get_shell (display);
+              GimpImageWindow  *window = gimp_display_shell_get_window (shell);
+
+              if (window)
+                {
+                  GimpUIManager *manager;
+
+                  manager = gimp_image_window_get_ui_manager (window);
+
+                  gimp_display_shell_present (shell);
+
+                  if (state & GDK_SHIFT_MASK)
+                    {
+                      gimp_ui_manager_activate_action (manager, "file",
+                                                       "file-save-as");
+                    }
+                  else
+                    {
+                      gimp_ui_manager_activate_action (manager, "file",
+                                                       "file-save");
+                    }
+                }
+
+              break;
+            }
+        }
+    }
+}
+
+static gboolean
+quit_close_all_dialog_query_tooltip (GtkWidget  *widget,
+                                     gint        x,
+                                     gint        y,
+                                     gboolean    keyboard_tip,
+                                     GtkTooltip *tooltip,
+                                     QuitDialog *dialog)
+{
+  GtkTreePath *path;
+  gboolean     show_tip = FALSE;
+
+  if (gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (widget), &x, &y,
+                                         keyboard_tip,
+                                         NULL, &path, NULL))
+    {
+      GtkTreeViewColumn *column = NULL;
+
+      gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget), x, y,
+                                     NULL, &column, NULL, NULL);
+
+      if (column == dialog->save_column)
+        {
+          gchar *tip = g_strconcat (_("Save this image"), "\n<b>",
+                                    gimp_get_mod_string (GDK_SHIFT_MASK),
+                                    "</b>  ", _("Save as"),
+                                    NULL);
+
+          gtk_tooltip_set_markup (tooltip, tip);
+          gtk_tree_view_set_tooltip_row (GTK_TREE_VIEW (widget), tooltip, path);
+
+          g_free (tip);
+
+          show_tip = TRUE;
+        }
+
+      gtk_tree_path_free (path);
+    }
+
+  return show_tip;
+}
diff --git a/app/widgets/Makefile.am b/app/widgets/Makefile.am
index 3fc7dde..c0eb8e8 100644
--- a/app/widgets/Makefile.am
+++ b/app/widgets/Makefile.am
@@ -43,6 +43,8 @@ libappwidgets_a_sources = \
        gimpbufferview.h                \
        gimpcairo-wilber.c              \
        gimpcairo-wilber.h              \
+       gimpcellrendererbutton.c        \
+       gimpcellrendererbutton.h        \
        gimpcellrendererdashes.c        \
        gimpcellrendererdashes.h        \
        gimpcellrendererviewable.c      \
diff --git a/app/widgets/gimpcellrendererbutton.c b/app/widgets/gimpcellrendererbutton.c
new file mode 100644
index 0000000..f9589c0
--- /dev/null
+++ b/app/widgets/gimpcellrendererbutton.c
@@ -0,0 +1,143 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpcellrendererbutton.c
+ * Copyright (C) 2016 Michael Natterer <mitch gimp org>
+ *
+ * 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 3 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <gtk/gtk.h>
+
+#include "widgets-types.h"
+
+#include "core/gimpmarshal.h"
+
+#include "gimpcellrendererbutton.h"
+
+
+enum
+{
+  CLICKED,
+  LAST_SIGNAL
+};
+
+
+static gboolean gimp_cell_renderer_button_activate (GtkCellRenderer     *cell,
+                                                    GdkEvent            *event,
+                                                    GtkWidget           *widget,
+                                                    const gchar         *path,
+                                                    GdkRectangle        *background_area,
+                                                    GdkRectangle        *cell_area,
+                                                    GtkCellRendererState flags);
+
+
+G_DEFINE_TYPE (GimpCellRendererButton, gimp_cell_renderer_button,
+               GTK_TYPE_CELL_RENDERER_PIXBUF)
+
+#define parent_class gimp_cell_renderer_button_parent_class
+
+static guint button_cell_signals[LAST_SIGNAL] = { 0 };
+
+
+static void
+gimp_cell_renderer_button_class_init (GimpCellRendererButtonClass *klass)
+{
+  GObjectClass         *object_class = G_OBJECT_CLASS (klass);
+  GtkCellRendererClass *cell_class   = GTK_CELL_RENDERER_CLASS (klass);
+
+  /**
+   * GimpCellRendererButton::clicked:
+   * @cell:
+   * @path:
+   * @state:
+   *
+   * Called on a button cell when it is clicked.
+   **/
+  button_cell_signals[CLICKED] =
+    g_signal_new ("clicked",
+                  G_OBJECT_CLASS_TYPE (object_class),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (GimpCellRendererButtonClass, clicked),
+                  NULL, NULL,
+                  gimp_marshal_VOID__STRING_FLAGS,
+                  G_TYPE_NONE, 2,
+                  G_TYPE_STRING,
+                  GDK_TYPE_MODIFIER_TYPE);
+
+  cell_class->activate = gimp_cell_renderer_button_activate;
+
+  klass->clicked       = NULL;
+}
+
+static void
+gimp_cell_renderer_button_init (GimpCellRendererButton *cell_button)
+{
+  g_object_set (cell_button,
+                "mode",         GTK_CELL_RENDERER_MODE_ACTIVATABLE,
+                "xpad",         2,
+                "ypad",         2,
+                "follow-state", TRUE,
+                "stock-size",   GTK_ICON_SIZE_BUTTON,
+                NULL);
+}
+
+static gboolean
+gimp_cell_renderer_button_activate (GtkCellRenderer      *cell,
+                                    GdkEvent             *event,
+                                    GtkWidget            *widget,
+                                    const gchar          *path,
+                                    GdkRectangle         *background_area,
+                                    GdkRectangle         *cell_area,
+                                    GtkCellRendererState  flags)
+{
+  GimpCellRendererButton *cell_button = GIMP_CELL_RENDERER_BUTTON (cell);
+  GdkModifierType         state       = 0;
+
+  if (event && ((GdkEventAny *) event)->type == GDK_BUTTON_PRESS)
+    state = ((GdkEventButton *) event)->state;
+
+  if (! event ||
+      (((GdkEventAny *) event)->type == GDK_BUTTON_PRESS &&
+       ((GdkEventButton *) event)->button == 1))
+    {
+      gimp_cell_renderer_button_clicked (cell_button, path, state);
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+
+/*  public functions  */
+
+GtkCellRenderer *
+gimp_cell_renderer_button_new (void)
+{
+  return g_object_new (GIMP_TYPE_CELL_RENDERER_BUTTON, NULL);
+}
+
+void
+gimp_cell_renderer_button_clicked (GimpCellRendererButton *cell,
+                                   const gchar            *path,
+                                   GdkModifierType         state)
+{
+  g_return_if_fail (GIMP_IS_CELL_RENDERER_BUTTON (cell));
+  g_return_if_fail (path != NULL);
+
+  g_signal_emit (cell, button_cell_signals[CLICKED], 0, path, state);
+}
diff --git a/app/widgets/gimpcellrendererbutton.h b/app/widgets/gimpcellrendererbutton.h
new file mode 100644
index 0000000..9ce430f
--- /dev/null
+++ b/app/widgets/gimpcellrendererbutton.h
@@ -0,0 +1,59 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpcellrendererbutton.h
+ * Copyright (C) 2016 Michael Natterer <mitch gimp org>
+ *
+ * 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 3 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_CELL_RENDERER_BUTTON_H__
+#define __GIMP_CELL_RENDERER_BUTTON_H__
+
+
+#define GIMP_TYPE_CELL_RENDERER_BUTTON            (gimp_cell_renderer_button_get_type ())
+#define GIMP_CELL_RENDERER_BUTTON(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
GIMP_TYPE_CELL_RENDERER_BUTTON, GimpCellRendererButton))
+#define GIMP_CELL_RENDERER_BUTTON_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), 
GIMP_TYPE_CELL_RENDERER_BUTTON, GimpCellRendererButtonClass))
+#define GIMP_IS_CELL_RENDERER_BUTTON(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
GIMP_TYPE_CELL_RENDERER_BUTTON))
+#define GIMP_IS_CELL_RENDERER_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), 
GIMP_TYPE_CELL_RENDERER_BUTTON))
+#define GIMP_CELL_RENDERER_BUTTON_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), 
GIMP_TYPE_CELL_RENDERER_BUTTON, GimpCellRendererButtonClass))
+
+
+typedef struct _GimpCellRendererButtonClass GimpCellRendererButtonClass;
+
+struct _GimpCellRendererButton
+{
+  GtkCellRendererPixbuf  parent_instance;
+};
+
+struct _GimpCellRendererButtonClass
+{
+  GtkCellRendererPixbufClass  parent_class;
+
+  void (* clicked) (GimpCellRendererButton *cell,
+                    const gchar            *path,
+                    GdkModifierType         state);
+};
+
+
+GType             gimp_cell_renderer_button_get_type (void) G_GNUC_CONST;
+
+GtkCellRenderer * gimp_cell_renderer_button_new      (void);
+
+void              gimp_cell_renderer_button_clicked  (GimpCellRendererButton *cell,
+                                                      const gchar            *path,
+                                                      GdkModifierType         state);
+
+
+#endif /* __GIMP_CELL_RENDERER_BUTTON_H__ */
diff --git a/app/widgets/gimpcontainertreeview.c b/app/widgets/gimpcontainertreeview.c
index f56fa4f..5901460 100644
--- a/app/widgets/gimpcontainertreeview.c
+++ b/app/widgets/gimpcontainertreeview.c
@@ -35,6 +35,7 @@
 #include "core/gimpmarshal.h"
 #include "core/gimpviewable.h"
 
+#include "gimpcellrendererbutton.h"
 #include "gimpcellrendererviewable.h"
 #include "gimpcontainertreestore.h"
 #include "gimpcontainertreeview.h"
@@ -321,9 +322,12 @@ gimp_container_tree_view_constructed (GObject *object)
                     G_CALLBACK (gimp_container_tree_view_drag_data_received),
                     tree_view);
 
-  g_signal_connect (tree_view->view, "query-tooltip",
-                    G_CALLBACK (gimp_container_tree_view_tooltip),
-                    tree_view);
+  /* connect_after so external code can connect to "query-tooltip" too
+   * and override the default tip
+   */
+  g_signal_connect_after (tree_view->view, "query-tooltip",
+                          G_CALLBACK (gimp_container_tree_view_tooltip),
+                          tree_view);
 }
 
 static void
@@ -520,7 +524,8 @@ gimp_container_tree_view_add_toggle_cell (GimpContainerTreeView *tree_view,
                                           GtkCellRenderer       *cell)
 {
   g_return_if_fail (GIMP_IS_CONTAINER_TREE_VIEW (tree_view));
-  g_return_if_fail (GIMP_IS_CELL_RENDERER_TOGGLE (cell));
+  g_return_if_fail (GIMP_IS_CELL_RENDERER_TOGGLE (cell) ||
+                    GIMP_IS_CELL_RENDERER_BUTTON (cell));
 
   tree_view->priv->toggle_cells = g_list_prepend (tree_view->priv->toggle_cells,
                                                   cell);
@@ -1043,7 +1048,7 @@ gimp_container_tree_view_button_press (GtkWidget             *widget,
                                      &path, &column, NULL, NULL))
     {
       GimpViewRenderer         *renderer;
-      GimpCellRendererToggle   *toggled_cell = NULL;
+      GtkCellRenderer          *toggled_cell = NULL;
       GimpCellRendererViewable *clicked_cell = NULL;
       GtkCellRenderer          *edit_cell    = NULL;
       GdkRectangle              column_area;
@@ -1098,7 +1103,7 @@ gimp_container_tree_view_button_press (GtkWidget             *widget,
           g_list_free (cells);
         }
 
-      toggled_cell = (GimpCellRendererToggle *)
+      toggled_cell =
         gimp_container_tree_view_find_click_cell (widget,
                                                   tree_view->priv->toggle_cells,
                                                   column, &column_area,
@@ -1181,9 +1186,18 @@ gimp_container_tree_view_button_press (GtkWidget             *widget,
 
                       if (toggled_cell)
                         {
-                          gimp_cell_renderer_toggle_clicked (toggled_cell,
-                                                             path_str,
-                                                             bevent->state);
+                          if (GIMP_IS_CELL_RENDERER_TOGGLE (toggled_cell))
+                            {
+                              gimp_cell_renderer_toggle_clicked (GIMP_CELL_RENDERER_TOGGLE (toggled_cell),
+                                                                 path_str,
+                                                                 bevent->state);
+                            }
+                          else if (GIMP_IS_CELL_RENDERER_BUTTON (toggled_cell))
+                            {
+                              gimp_cell_renderer_button_clicked (GIMP_CELL_RENDERER_BUTTON (toggled_cell),
+                                                                 path_str,
+                                                                 bevent->state);
+                            }
                         }
                       else if (clicked_cell)
                         {
diff --git a/app/widgets/widgets-types.h b/app/widgets/widgets-types.h
index d617088..8989e1f 100644
--- a/app/widgets/widgets-types.h
+++ b/app/widgets/widgets-types.h
@@ -240,6 +240,7 @@ typedef struct _GimpViewRendererVectors      GimpViewRendererVectors;
 
 /*  cell renderers  */
 
+typedef struct _GimpCellRendererButton       GimpCellRendererButton;
 typedef struct _GimpCellRendererDashes       GimpCellRendererDashes;
 typedef struct _GimpCellRendererViewable     GimpCellRendererViewable;
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]