summaryrefslogtreecommitdiffstats
path: root/app/widgets/gimpfileprocview.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:30:19 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:30:19 +0000
commit5c1676dfe6d2f3c837a5e074117b45613fd29a72 (patch)
treecbffb45144febf451e54061db2b21395faf94bfe /app/widgets/gimpfileprocview.c
parentInitial commit. (diff)
downloadgimp-upstream/2.10.34.tar.xz
gimp-upstream/2.10.34.zip
Adding upstream version 2.10.34.upstream/2.10.34upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'app/widgets/gimpfileprocview.c')
-rw-r--r--app/widgets/gimpfileprocview.c476
1 files changed, 476 insertions, 0 deletions
diff --git a/app/widgets/gimpfileprocview.c b/app/widgets/gimpfileprocview.c
new file mode 100644
index 0000000..ce50f84
--- /dev/null
+++ b/app/widgets/gimpfileprocview.c
@@ -0,0 +1,476 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpfileprocview.c
+ * Copyright (C) 2004 Sven Neumann <sven@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 <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include <gegl.h>
+
+#include "widgets-types.h"
+
+#include "core/gimp.h"
+#include "core/gimpmarshal.h"
+
+#include "plug-in/gimppluginprocedure.h"
+
+#include "gimpfileprocview.h"
+
+#include "gimp-intl.h"
+
+/* an arbitrary limit to keep the file dialog from becoming too wide */
+#define MAX_EXTENSIONS 4
+
+enum
+{
+ COLUMN_PROC,
+ COLUMN_LABEL,
+ COLUMN_EXTENSIONS,
+ COLUMN_HELP_ID,
+ COLUMN_FILTER,
+ N_COLUMNS
+};
+
+enum
+{
+ CHANGED,
+ LAST_SIGNAL
+};
+
+
+static void gimp_file_proc_view_finalize (GObject *object);
+
+static void gimp_file_proc_view_selection_changed (GtkTreeSelection *selection,
+ GimpFileProcView *view);
+
+static GtkFileFilter * gimp_file_proc_view_process_procedure (GimpPlugInProcedure *file_proc,
+ GtkFileFilter *all);
+static gchar * gimp_file_proc_view_pattern_from_extension (const gchar *extension);
+
+
+G_DEFINE_TYPE (GimpFileProcView, gimp_file_proc_view, GTK_TYPE_TREE_VIEW)
+
+#define parent_class gimp_file_proc_view_parent_class
+
+static guint view_signals[LAST_SIGNAL] = { 0 };
+
+
+static void
+gimp_file_proc_view_class_init (GimpFileProcViewClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gimp_file_proc_view_finalize;
+
+ klass->changed = NULL;
+
+ view_signals[CHANGED] = g_signal_new ("changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GimpFileProcViewClass,
+ changed),
+ NULL, NULL,
+ gimp_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+static void
+gimp_file_proc_view_init (GimpFileProcView *view)
+{
+}
+
+static void
+gimp_file_proc_view_finalize (GObject *object)
+{
+ GimpFileProcView *view = GIMP_FILE_PROC_VIEW (object);
+
+ if (view->meta_extensions)
+ {
+ g_list_free_full (view->meta_extensions, (GDestroyNotify) g_free);
+ view->meta_extensions = NULL;
+ }
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+GtkWidget *
+gimp_file_proc_view_new (Gimp *gimp,
+ GSList *procedures,
+ const gchar *automatic,
+ const gchar *automatic_help_id)
+{
+ GtkFileFilter *all_filter;
+ GtkTreeView *view;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *cell;
+ GtkListStore *store;
+ GSList *list;
+ GtkTreeIter iter;
+
+ g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
+
+ store = gtk_list_store_new (N_COLUMNS,
+ GIMP_TYPE_PLUG_IN_PROCEDURE, /* COLUMN_PROC */
+ G_TYPE_STRING, /* COLUMN_LABEL */
+ G_TYPE_STRING, /* COLUMN_EXTENSIONS */
+ G_TYPE_STRING, /* COLUMN_HELP_ID */
+ GTK_TYPE_FILE_FILTER); /* COLUMN_FILTER */
+
+ view = g_object_new (GIMP_TYPE_FILE_PROC_VIEW,
+ "model", store,
+ "rules-hint", TRUE,
+ NULL);
+
+ g_object_unref (store);
+
+ all_filter = gtk_file_filter_new ();
+
+ for (list = procedures; list; list = g_slist_next (list))
+ {
+ GimpPlugInProcedure *proc = list->data;
+
+ if (! proc->prefixes_list) /* skip URL loaders */
+ {
+ const gchar *label = gimp_procedure_get_label (GIMP_PROCEDURE (proc));
+ const gchar *help_id = gimp_procedure_get_help_id (GIMP_PROCEDURE (proc));
+ GSList *list2;
+
+ if (label)
+ {
+ GtkFileFilter *filter;
+
+ filter = gimp_file_proc_view_process_procedure (proc, all_filter);
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ COLUMN_PROC, proc,
+ COLUMN_LABEL, label,
+ COLUMN_EXTENSIONS, proc->extensions,
+ COLUMN_HELP_ID, help_id,
+ COLUMN_FILTER, filter,
+ -1);
+ g_object_unref (filter);
+ }
+
+ for (list2 = proc->extensions_list;
+ list2;
+ list2 = g_slist_next (list2))
+ {
+ GimpFileProcView *proc_view = GIMP_FILE_PROC_VIEW (view);
+ const gchar *ext = list2->data;
+ const gchar *dot = strchr (ext, '.');
+
+ if (dot && dot != ext)
+ proc_view->meta_extensions =
+ g_list_append (proc_view->meta_extensions,
+ g_strdup (dot + 1));
+ }
+ }
+ }
+
+ if (automatic)
+ {
+ gtk_list_store_prepend (store, &iter);
+ gtk_list_store_set (store, &iter,
+ COLUMN_PROC, NULL,
+ COLUMN_LABEL, automatic,
+ COLUMN_HELP_ID, automatic_help_id,
+ COLUMN_FILTER, all_filter,
+ -1);
+ }
+
+ column = gtk_tree_view_column_new ();
+ gtk_tree_view_column_set_title (column, _("File Type"));
+ gtk_tree_view_column_set_expand (column, TRUE);
+
+ cell = gtk_cell_renderer_text_new ();
+ gtk_tree_view_column_pack_start (column, cell, TRUE);
+ gtk_tree_view_column_set_attributes (column, cell,
+ "text", COLUMN_LABEL,
+ NULL);
+
+ gtk_tree_view_append_column (view, column);
+
+ column = gtk_tree_view_column_new ();
+ gtk_tree_view_column_set_title (column, _("Extensions"));
+
+ cell = gtk_cell_renderer_text_new ();
+ gtk_tree_view_column_pack_start (column, cell, TRUE);
+ gtk_tree_view_column_set_attributes (column, cell,
+ "text", COLUMN_EXTENSIONS,
+ NULL);
+
+ gtk_tree_view_append_column (view, column);
+
+ g_signal_connect (gtk_tree_view_get_selection (view), "changed",
+ G_CALLBACK (gimp_file_proc_view_selection_changed),
+ view);
+
+ return GTK_WIDGET (view);
+}
+
+GimpPlugInProcedure *
+gimp_file_proc_view_get_proc (GimpFileProcView *view,
+ gchar **label,
+ GtkFileFilter **filter)
+{
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GimpPlugInProcedure *proc;
+ GtkTreeIter iter;
+ gboolean has_selection;
+
+ g_return_val_if_fail (GIMP_IS_FILE_PROC_VIEW (view), NULL);
+
+ if (label) *label = NULL;
+ if (filter) *filter = NULL;
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+
+ has_selection = gtk_tree_selection_get_selected (selection, &model, &iter);
+
+ /* if there's no selected item, we return the "automatic" procedure, which,
+ * if exists, is the first item.
+ */
+ if (! has_selection)
+ {
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
+
+ if (! gtk_tree_model_get_iter_first (model, &iter))
+ return NULL;
+ }
+
+ gtk_tree_model_get (model, &iter,
+ COLUMN_PROC, &proc,
+ -1);
+
+ if (proc)
+ {
+ g_object_unref (proc);
+
+ /* there's no selected item, and no "automatic" procedure. return NULL.
+ */
+ if (! has_selection)
+ return NULL;
+ }
+
+ if (label)
+ {
+ gtk_tree_model_get (model, &iter,
+ COLUMN_LABEL, label,
+ -1);
+ }
+
+ if (filter)
+ {
+ gtk_tree_model_get (model, &iter,
+ COLUMN_FILTER, filter,
+ -1);
+ }
+
+ return proc;
+}
+
+gboolean
+gimp_file_proc_view_set_proc (GimpFileProcView *view,
+ GimpPlugInProcedure *proc)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gboolean iter_valid;
+
+ g_return_val_if_fail (GIMP_IS_FILE_PROC_VIEW (view), FALSE);
+
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
+
+ for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
+ iter_valid;
+ iter_valid = gtk_tree_model_iter_next (model, &iter))
+ {
+ GimpPlugInProcedure *this;
+
+ gtk_tree_model_get (model, &iter,
+ COLUMN_PROC, &this,
+ -1);
+
+ if (this)
+ g_object_unref (this);
+
+ if (this == proc)
+ break;
+ }
+
+ if (iter_valid)
+ {
+ GtkTreeSelection *selection;
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+
+ gtk_tree_selection_select_iter (selection, &iter);
+ }
+
+ return iter_valid;
+}
+
+gchar *
+gimp_file_proc_view_get_help_id (GimpFileProcView *view)
+{
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+
+ g_return_val_if_fail (GIMP_IS_FILE_PROC_VIEW (view), NULL);
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter))
+ {
+ gchar *help_id;
+
+ gtk_tree_model_get (model, &iter,
+ COLUMN_HELP_ID, &help_id,
+ -1);
+
+ return help_id;
+ }
+
+ return NULL;
+}
+
+static void
+gimp_file_proc_view_selection_changed (GtkTreeSelection *selection,
+ GimpFileProcView *view)
+{
+ g_signal_emit (view, view_signals[CHANGED], 0);
+}
+
+/**
+ * gimp_file_proc_view_process_procedure:
+ * @file_proc:
+ * @all:
+ *
+ * Creates a #GtkFileFilter of @file_proc and adds the extensions to
+ * the @all filter.
+ * The returned #GtkFileFilter has a normal ref and must be unreffed
+ * when used.
+ **/
+static GtkFileFilter *
+gimp_file_proc_view_process_procedure (GimpPlugInProcedure *file_proc,
+ GtkFileFilter *all)
+{
+ GtkFileFilter *filter;
+ GString *str;
+ GSList *list;
+ gint i;
+
+ if (! file_proc->extensions_list)
+ return NULL;
+
+ filter = gtk_file_filter_new ();
+ str = g_string_new (gimp_procedure_get_label (GIMP_PROCEDURE (file_proc)));
+
+ /* Take ownership directly so we don't have to mess with a floating
+ * ref
+ */
+ g_object_ref_sink (filter);
+
+ for (list = file_proc->mime_types_list; list; list = g_slist_next (list))
+ {
+ const gchar *mime_type = list->data;
+
+ gtk_file_filter_add_mime_type (filter, mime_type);
+ gtk_file_filter_add_mime_type (all, mime_type);
+ }
+
+ for (list = file_proc->extensions_list, i = 0;
+ list;
+ list = g_slist_next (list), i++)
+ {
+ const gchar *extension = list->data;
+ gchar *pattern;
+
+ pattern = gimp_file_proc_view_pattern_from_extension (extension);
+ gtk_file_filter_add_pattern (filter, pattern);
+ gtk_file_filter_add_pattern (all, pattern);
+ g_free (pattern);
+
+ if (i == 0)
+ {
+ g_string_append (str, " (");
+ }
+ else if (i <= MAX_EXTENSIONS)
+ {
+ g_string_append (str, ", ");
+ }
+
+ if (i < MAX_EXTENSIONS)
+ {
+ g_string_append (str, "*.");
+ g_string_append (str, extension);
+ }
+ else if (i == MAX_EXTENSIONS)
+ {
+ g_string_append (str, "...");
+ }
+
+ if (! list->next)
+ {
+ g_string_append (str, ")");
+ }
+ }
+
+ gtk_file_filter_set_name (filter, str->str);
+ g_string_free (str, TRUE);
+
+ return filter;
+}
+
+static gchar *
+gimp_file_proc_view_pattern_from_extension (const gchar *extension)
+{
+ gchar *pattern;
+ gchar *p;
+ gint len, i;
+
+ g_return_val_if_fail (extension != NULL, NULL);
+
+ /* This function assumes that file extensions are 7bit ASCII. It
+ * could certainly be rewritten to handle UTF-8 if this assumption
+ * turns out to be incorrect.
+ */
+
+ len = strlen (extension);
+
+ pattern = g_new (gchar, 4 + 4 * len);
+
+ strcpy (pattern, "*.");
+
+ for (i = 0, p = pattern + 2; i < len; i++, p+= 4)
+ {
+ p[0] = '[';
+ p[1] = g_ascii_tolower (extension[i]);
+ p[2] = g_ascii_toupper (extension[i]);
+ p[3] = ']';
+ }
+
+ *p = '\0';
+
+ return pattern;
+}