1 /* LIBGIMP - The GIMP Library
2  * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
3  *
4  * gimpprocbrowserdialog.c
5  *
6  * This library is free software: you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 3 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library.  If not, see
18  * <https://www.gnu.org/licenses/>.
19  */
20 
21 #include "config.h"
22 
23 #include <string.h>
24 
25 #include <gegl.h>
26 #include <gtk/gtk.h>
27 
28 #include "libgimpwidgets/gimpwidgets.h"
29 
30 #include "gimp.h"
31 
32 #include "gimpuitypes.h"
33 #include "gimpprocbrowserdialog.h"
34 #include "gimpprocview.h"
35 
36 #include "libgimp-intl.h"
37 
38 
39 /**
40  * SECTION: gimpprocbrowserdialog
41  * @title: GimpProcBrowserDialog
42  * @short_description: The dialog for the procedure and plugin browsers.
43  *
44  * The dialog for the procedure and plugin browsers.
45  **/
46 
47 
48 #define DBL_LIST_WIDTH 250
49 #define DBL_WIDTH      (DBL_LIST_WIDTH + 400)
50 #define DBL_HEIGHT     250
51 
52 
53 enum
54 {
55   SELECTION_CHANGED,
56   ROW_ACTIVATED,
57   LAST_SIGNAL
58 };
59 
60 typedef enum
61 {
62   SEARCH_TYPE_ALL,
63   SEARCH_TYPE_NAME,
64   SEARCH_TYPE_BLURB,
65   SEARCH_TYPE_HELP,
66   SEARCH_TYPE_AUTHOR,
67   SEARCH_TYPE_COPYRIGHT,
68   SEARCH_TYPE_DATE,
69   SEARCH_TYPE_PROC_TYPE
70 } SearchType;
71 
72 enum
73 {
74   COLUMN_PROC_NAME,
75   N_COLUMNS
76 };
77 
78 
79 static void       browser_selection_changed (GtkTreeSelection      *sel,
80                                              GimpProcBrowserDialog *dialog);
81 static void       browser_row_activated     (GtkTreeView           *treeview,
82                                              GtkTreePath           *path,
83                                              GtkTreeViewColumn     *column,
84                                              GimpProcBrowserDialog *dialog);
85 static void       browser_show_procedure    (GimpProcBrowserDialog *dialog,
86                                              const gchar           *proc_name);
87 static void       browser_search            (GimpBrowser           *browser,
88                                              const gchar           *query_text,
89                                              gint                   search_type,
90                                              GimpProcBrowserDialog *dialog);
91 
92 
93 G_DEFINE_TYPE (GimpProcBrowserDialog, gimp_proc_browser_dialog,
94                GIMP_TYPE_DIALOG)
95 
96 #define parent_class gimp_proc_browser_dialog_parent_class
97 
98 static guint dialog_signals[LAST_SIGNAL] = { 0, };
99 
100 
101 static void
gimp_proc_browser_dialog_class_init(GimpProcBrowserDialogClass * klass)102 gimp_proc_browser_dialog_class_init (GimpProcBrowserDialogClass *klass)
103 {
104   /**
105    * GimpProcBrowserDialog::selection-changed:
106    * @dialog: the object that received the signal
107    *
108    * Emitted when the selection in the contained #GtkTreeView changes.
109    */
110   dialog_signals[SELECTION_CHANGED] =
111     g_signal_new ("selection-changed",
112                   G_TYPE_FROM_CLASS (klass),
113                   G_SIGNAL_RUN_LAST,
114                   G_STRUCT_OFFSET (GimpProcBrowserDialogClass,
115                                    selection_changed),
116                   NULL, NULL,
117                   g_cclosure_marshal_VOID__VOID,
118                   G_TYPE_NONE, 0);
119 
120   /**
121    * GimpProcBrowserDialog::row-activated:
122    * @dialog: the object that received the signal
123    *
124    * Emitted when one of the rows in the contained #GtkTreeView is activated.
125    */
126   dialog_signals[ROW_ACTIVATED] =
127     g_signal_new ("row-activated",
128                   G_TYPE_FROM_CLASS (klass),
129                   G_SIGNAL_RUN_LAST,
130                   G_STRUCT_OFFSET (GimpProcBrowserDialogClass,
131                                    row_activated),
132                   NULL, NULL,
133                   g_cclosure_marshal_VOID__VOID,
134                   G_TYPE_NONE, 0);
135 
136   klass->selection_changed = NULL;
137   klass->row_activated     = NULL;
138 }
139 
140 static void
gimp_proc_browser_dialog_init(GimpProcBrowserDialog * dialog)141 gimp_proc_browser_dialog_init (GimpProcBrowserDialog *dialog)
142 {
143   GtkWidget        *scrolled_window;
144   GtkCellRenderer  *renderer;
145   GtkTreeSelection *selection;
146   GtkWidget        *parent;
147 
148   dialog->browser = gimp_browser_new ();
149   gimp_browser_add_search_types (GIMP_BROWSER (dialog->browser),
150                                  _("by name"),        SEARCH_TYPE_NAME,
151                                  _("by description"), SEARCH_TYPE_BLURB,
152                                  _("by help"),        SEARCH_TYPE_HELP,
153                                  _("by author"),      SEARCH_TYPE_AUTHOR,
154                                  _("by copyright"),   SEARCH_TYPE_COPYRIGHT,
155                                  _("by date"),        SEARCH_TYPE_DATE,
156                                  _("by type"),        SEARCH_TYPE_PROC_TYPE,
157                                  NULL);
158   gtk_container_set_border_width (GTK_CONTAINER (dialog->browser), 12);
159   gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
160                       dialog->browser, TRUE, TRUE, 0);
161   gtk_widget_show (dialog->browser);
162 
163   g_signal_connect (dialog->browser, "search",
164                     G_CALLBACK (browser_search),
165                     dialog);
166 
167   /* list : list in a scrolled_win */
168 
169   scrolled_window = gtk_scrolled_window_new (NULL, NULL);
170   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
171                                        GTK_SHADOW_IN);
172   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
173                                   GTK_POLICY_AUTOMATIC,
174                                   GTK_POLICY_ALWAYS);
175   gtk_box_pack_start (GTK_BOX (GIMP_BROWSER (dialog->browser)->left_vbox),
176                       scrolled_window, TRUE, TRUE, 0);
177   gtk_widget_show (scrolled_window);
178 
179   dialog->tree_view = gtk_tree_view_new ();
180 
181   renderer = gtk_cell_renderer_text_new ();
182   gtk_cell_renderer_text_set_fixed_height_from_font
183     (GTK_CELL_RENDERER_TEXT (renderer), 1);
184 
185   gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (dialog->tree_view),
186                                                -1, NULL,
187                                                renderer,
188                                                "text", 0,
189                                                NULL);
190   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (dialog->tree_view), FALSE);
191 
192   g_signal_connect (dialog->tree_view, "row_activated",
193                     G_CALLBACK (browser_row_activated),
194                     dialog);
195 
196   gtk_widget_set_size_request (dialog->tree_view, DBL_LIST_WIDTH, DBL_HEIGHT);
197   gtk_container_add (GTK_CONTAINER (scrolled_window), dialog->tree_view);
198   gtk_widget_show (dialog->tree_view);
199 
200   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->tree_view));
201 
202   g_signal_connect (selection, "changed",
203                     G_CALLBACK (browser_selection_changed),
204                     dialog);
205 
206   parent = gtk_widget_get_parent (GIMP_BROWSER (dialog->browser)->right_vbox);
207   parent = gtk_widget_get_parent (parent);
208 
209   gtk_widget_set_size_request (parent, DBL_WIDTH - DBL_LIST_WIDTH, -1);
210 }
211 
212 
213 /*  public functions  */
214 
215 /**
216  * gimp_proc_browser_dialog_new:
217  * @title:     The dialog's title.
218  * @role:      The dialog's role, see gtk_window_set_role().
219  * @help_func: The function which will be called if the user presses "F1".
220  * @help_id:   The help_id which will be passed to @help_func.
221  * @...:       A %NULL-terminated list destribing the action_area buttons.
222  *
223  * Create a new #GimpProcBrowserDialog.
224  *
225  * Return Value: a newly created #GimpProcBrowserDialog.
226  *
227  * Since: 2.4
228  **/
229 GtkWidget *
gimp_proc_browser_dialog_new(const gchar * title,const gchar * role,GimpHelpFunc help_func,const gchar * help_id,...)230 gimp_proc_browser_dialog_new (const gchar  *title,
231                               const gchar  *role,
232                               GimpHelpFunc  help_func,
233                               const gchar  *help_id,
234                               ...)
235 {
236   GimpProcBrowserDialog *dialog;
237   va_list                args;
238 
239   va_start (args, help_id);
240 
241   dialog = g_object_new (GIMP_TYPE_PROC_BROWSER_DIALOG,
242                          "title",     title,
243                          "role",      role,
244                          "help-func", help_func,
245                          "help-id",   help_id,
246                          NULL);
247 
248   gimp_dialog_add_buttons_valist (GIMP_DIALOG (dialog), args);
249 
250   va_end (args);
251 
252   /* first search (all procedures) */
253   browser_search (GIMP_BROWSER (dialog->browser), "", SEARCH_TYPE_ALL, dialog);
254 
255   return GTK_WIDGET (dialog);
256 }
257 
258 /**
259  * gimp_proc_browser_dialog_get_selected:
260  * @dialog: a #GimpProcBrowserDialog
261  *
262  * Retrieves the name of the currently selected procedure.
263  *
264  * Return Value: The name of the selected procedure of %NULL if no
265  *               procedure is selected.
266  *
267  * Since: 2.4
268  **/
269 gchar *
gimp_proc_browser_dialog_get_selected(GimpProcBrowserDialog * dialog)270 gimp_proc_browser_dialog_get_selected (GimpProcBrowserDialog *dialog)
271 {
272   GtkTreeSelection *sel;
273   GtkTreeIter       iter;
274 
275   g_return_val_if_fail (GIMP_IS_PROC_BROWSER_DIALOG (dialog), NULL);
276 
277   sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->tree_view));
278 
279   if (gtk_tree_selection_get_selected (sel, NULL, &iter))
280     {
281       gchar *proc_name;
282 
283       gtk_tree_model_get (GTK_TREE_MODEL (dialog->store), &iter,
284                           COLUMN_PROC_NAME, &proc_name,
285                           -1);
286 
287       return proc_name;
288     }
289 
290   return NULL;
291 }
292 
293 
294 /*  private functions  */
295 
296 static void
browser_selection_changed(GtkTreeSelection * sel,GimpProcBrowserDialog * dialog)297 browser_selection_changed (GtkTreeSelection      *sel,
298                            GimpProcBrowserDialog *dialog)
299 {
300   GtkTreeIter iter;
301 
302   if (gtk_tree_selection_get_selected (sel, NULL, &iter))
303     {
304       gchar *proc_name;
305 
306       gtk_tree_model_get (GTK_TREE_MODEL (dialog->store), &iter,
307                           COLUMN_PROC_NAME, &proc_name,
308                           -1);
309       browser_show_procedure (dialog, proc_name);
310       g_free (proc_name);
311     }
312 
313   g_signal_emit (dialog, dialog_signals[SELECTION_CHANGED], 0);
314 }
315 
316 static void
browser_row_activated(GtkTreeView * treeview,GtkTreePath * path,GtkTreeViewColumn * column,GimpProcBrowserDialog * dialog)317 browser_row_activated (GtkTreeView           *treeview,
318                        GtkTreePath           *path,
319                        GtkTreeViewColumn     *column,
320                        GimpProcBrowserDialog *dialog)
321 {
322   g_signal_emit (dialog, dialog_signals[ROW_ACTIVATED], 0);
323 }
324 
325 static void
browser_show_procedure(GimpProcBrowserDialog * dialog,const gchar * proc_name)326 browser_show_procedure (GimpProcBrowserDialog *dialog,
327                         const gchar           *proc_name)
328 {
329   gchar           *proc_blurb;
330   gchar           *proc_help;
331   gchar           *proc_author;
332   gchar           *proc_copyright;
333   gchar           *proc_date;
334   GimpPDBProcType  proc_type;
335   gint             n_params;
336   gint             n_return_vals;
337   GimpParamDef    *params;
338   GimpParamDef    *return_vals;
339 
340   gimp_procedural_db_proc_info (proc_name,
341                                 &proc_blurb,
342                                 &proc_help,
343                                 &proc_author,
344                                 &proc_copyright,
345                                 &proc_date,
346                                 &proc_type,
347                                 &n_params,
348                                 &n_return_vals,
349                                 &params,
350                                 &return_vals);
351 
352   gimp_browser_set_widget (GIMP_BROWSER (dialog->browser),
353                            gimp_proc_view_new (proc_name,
354                                                NULL,
355                                                proc_blurb,
356                                                proc_help,
357                                                proc_author,
358                                                proc_copyright,
359                                                proc_date,
360                                                proc_type,
361                                                n_params,
362                                                n_return_vals,
363                                                params,
364                                                return_vals));
365 
366   g_free (proc_blurb);
367   g_free (proc_help);
368   g_free (proc_author);
369   g_free (proc_copyright);
370   g_free (proc_date);
371 
372   gimp_destroy_paramdefs (params,      n_params);
373   gimp_destroy_paramdefs (return_vals, n_return_vals);
374 }
375 
376 static void
browser_search(GimpBrowser * browser,const gchar * query_text,gint search_type,GimpProcBrowserDialog * dialog)377 browser_search (GimpBrowser           *browser,
378                 const gchar           *query_text,
379                 gint                   search_type,
380                 GimpProcBrowserDialog *dialog)
381 {
382   gchar  **proc_list;
383   gint     num_procs;
384   gchar   *str;
385   GRegex  *regex;
386 
387   /*  first check if the query is a valid regex  */
388   regex = g_regex_new (query_text, 0, 0, NULL);
389 
390   if (! regex)
391     {
392       gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->tree_view), NULL);
393       dialog->store = NULL;
394 
395       gimp_browser_show_message (browser, _("No matches"));
396 
397       gtk_label_set_text (GTK_LABEL (browser->count_label),
398                           _("Search term invalid or incomplete"));
399       return;
400     }
401 
402   g_regex_unref (regex);
403 
404   switch (search_type)
405     {
406     case SEARCH_TYPE_ALL:
407       gimp_browser_show_message (browser, _("Searching"));
408 
409       gimp_procedural_db_query (".*", ".*", ".*", ".*", ".*", ".*", ".*",
410                                 &num_procs, &proc_list);
411       break;
412 
413     case SEARCH_TYPE_NAME:
414       {
415         GString     *query = g_string_new ("");
416         const gchar *q     = query_text;
417 
418         gimp_browser_show_message (browser, _("Searching by name"));
419 
420         while (*q)
421           {
422             if ((*q == '_') || (*q == '-'))
423               g_string_append (query, "-");
424             else
425               g_string_append_c (query, *q);
426 
427             q++;
428           }
429 
430         gimp_procedural_db_query (query->str,
431                                   ".*", ".*", ".*", ".*", ".*", ".*",
432                                   &num_procs, &proc_list);
433 
434         g_string_free (query, TRUE);
435       }
436       break;
437 
438     case SEARCH_TYPE_BLURB:
439       gimp_browser_show_message (browser, _("Searching by description"));
440 
441       gimp_procedural_db_query (".*", query_text, ".*", ".*", ".*", ".*", ".*",
442                                 &num_procs, &proc_list);
443       break;
444 
445     case SEARCH_TYPE_HELP:
446       gimp_browser_show_message (browser, _("Searching by help"));
447 
448       gimp_procedural_db_query (".*", ".*", query_text, ".*", ".*", ".*", ".*",
449                                 &num_procs, &proc_list);
450       break;
451 
452     case SEARCH_TYPE_AUTHOR:
453       gimp_browser_show_message (browser, _("Searching by author"));
454 
455       gimp_procedural_db_query (".*", ".*", ".*", query_text, ".*", ".*", ".*",
456                                 &num_procs, &proc_list);
457       break;
458 
459     case SEARCH_TYPE_COPYRIGHT:
460       gimp_browser_show_message (browser, _("Searching by copyright"));
461 
462       gimp_procedural_db_query (".*", ".*", ".*", ".*", query_text, ".*", ".*",
463                                 &num_procs, &proc_list);
464       break;
465 
466     case SEARCH_TYPE_DATE:
467       gimp_browser_show_message (browser, _("Searching by date"));
468 
469       gimp_procedural_db_query (".*", ".*", ".*", ".*", ".*", query_text, ".*",
470                                 &num_procs, &proc_list);
471       break;
472 
473     case SEARCH_TYPE_PROC_TYPE:
474       gimp_browser_show_message (browser, _("Searching by type"));
475 
476       gimp_procedural_db_query (".*", ".*", ".*", ".*", ".*", ".*", query_text,
477                                 &num_procs, &proc_list);
478       break;
479     }
480 
481   if (! query_text || strlen (query_text) == 0)
482     {
483       str = g_strdup_printf (dngettext (GETTEXT_PACKAGE "-libgimp",
484                                         "%d procedure",
485                                         "%d procedures",
486                                         num_procs), num_procs);
487     }
488   else
489     {
490       switch (num_procs)
491         {
492         case 0:
493           str = g_strdup (_("No matches for your query"));
494           break;
495         default:
496           str = g_strdup_printf (dngettext (GETTEXT_PACKAGE "-libgimp",
497                                             "%d procedure matches your query",
498                                             "%d procedures match your query",
499                                             num_procs), num_procs);
500           break;
501         }
502     }
503 
504   gtk_label_set_text (GTK_LABEL (browser->count_label), str);
505   g_free (str);
506 
507   if (num_procs > 0)
508     {
509       GtkTreeSelection *selection;
510       GtkTreeIter       iter;
511       gint              i;
512 
513       dialog->store = gtk_list_store_new (N_COLUMNS,
514                                           G_TYPE_STRING);
515       gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->tree_view),
516                                GTK_TREE_MODEL (dialog->store));
517       g_object_unref (dialog->store);
518 
519       for (i = 0; i < num_procs; i++)
520         {
521           gtk_list_store_append (dialog->store, &iter);
522           gtk_list_store_set (dialog->store, &iter,
523                               COLUMN_PROC_NAME, proc_list[i],
524                               -1);
525         }
526 
527       g_strfreev (proc_list);
528 
529       gtk_tree_view_columns_autosize (GTK_TREE_VIEW (dialog->tree_view));
530 
531       gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (dialog->store),
532                                             COLUMN_PROC_NAME,
533                                             GTK_SORT_ASCENDING);
534 
535       gtk_tree_model_get_iter_first (GTK_TREE_MODEL (dialog->store), &iter);
536       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->tree_view));
537       gtk_tree_selection_select_iter (selection, &iter);
538     }
539   else
540     {
541       gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->tree_view), NULL);
542       dialog->store = NULL;
543 
544       gimp_browser_show_message (browser, _("No matches"));
545     }
546 }
547