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 ¶ms,
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