1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /*
4 * GThumb
5 *
6 * Copyright (C) 2006-2008 Free Software Foundation, Inc.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include <config.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <time.h>
26 #include <glib/gi18n.h>
27 #include <gtk/gtk.h>
28 #include "dom.h"
29 #include "glib-utils.h"
30 #include "gth-filterbar.h"
31 #include "gth-main.h"
32 #include "gth-user-dir.h"
33
34 enum {
35 ITEM_TYPE_NONE,
36 ITEM_TYPE_SEPARATOR,
37 ITEM_TYPE_FILTER,
38 ITEM_TYPE_PERSONALIZE
39 };
40
41 enum {
42 ICON_COLUMN,
43 NAME_COLUMN,
44 TYPE_COLUMN,
45 FILTER_COLUMN,
46 N_COLUMNS
47 };
48
49 enum {
50 CHANGED,
51 PERSONALIZE,
52 CLOSE_BUTTON_CLICKED,
53 LAST_SIGNAL
54 };
55
56 struct _GthFilterbarPrivate {
57 GtkListStore *model;
58 GtkWidget *test_combo_box;
59 GthTest *test;
60 GtkWidget *control_box;
61 GtkWidget *control;
62 GtkWidget *extra_area;
63 GtkTreeIter current_iter;
64 gulong filters_changed_id;
65 gulong test_changed_id;
66 };
67
68
69 static guint gth_filterbar_signals[LAST_SIGNAL] = { 0 };
70
71
G_DEFINE_TYPE_WITH_CODE(GthFilterbar,gth_filterbar,GTK_TYPE_BOX,G_ADD_PRIVATE (GthFilterbar))72 G_DEFINE_TYPE_WITH_CODE (GthFilterbar,
73 gth_filterbar,
74 GTK_TYPE_BOX,
75 G_ADD_PRIVATE (GthFilterbar))
76
77
78 static void
79 gth_filterbar_finalize (GObject *object)
80 {
81 GthFilterbar *filterbar;
82
83 filterbar = GTH_FILTERBAR (object);
84
85 g_signal_handler_disconnect (gth_main_get_default_monitor (), filterbar->priv->filters_changed_id);
86 if (filterbar->priv->test != NULL) {
87 g_signal_handler_disconnect (filterbar->priv->test, filterbar->priv->test_changed_id);
88 g_object_unref (filterbar->priv->test);
89 }
90
91 G_OBJECT_CLASS (gth_filterbar_parent_class)->finalize (object);
92 }
93
94
95 static void
gth_filterbar_class_init(GthFilterbarClass * class)96 gth_filterbar_class_init (GthFilterbarClass *class)
97 {
98 GObjectClass *object_class;
99
100 object_class = (GObjectClass*) class;
101 object_class->finalize = gth_filterbar_finalize;
102
103 gth_filterbar_signals[CHANGED] =
104 g_signal_new ("changed",
105 G_TYPE_FROM_CLASS (class),
106 G_SIGNAL_RUN_LAST,
107 G_STRUCT_OFFSET (GthFilterbarClass, changed),
108 NULL, NULL,
109 g_cclosure_marshal_VOID__VOID,
110 G_TYPE_NONE,
111 0);
112 gth_filterbar_signals[PERSONALIZE] =
113 g_signal_new ("personalize",
114 G_TYPE_FROM_CLASS (class),
115 G_SIGNAL_RUN_LAST,
116 G_STRUCT_OFFSET (GthFilterbarClass, personalize),
117 NULL, NULL,
118 g_cclosure_marshal_VOID__VOID,
119 G_TYPE_NONE,
120 0);
121 gth_filterbar_signals[CLOSE_BUTTON_CLICKED] =
122 g_signal_new ("close_button_clicked",
123 G_TYPE_FROM_CLASS (class),
124 G_SIGNAL_RUN_LAST,
125 G_STRUCT_OFFSET (GthFilterbarClass, close_button_clicked),
126 NULL, NULL,
127 g_cclosure_marshal_VOID__VOID,
128 G_TYPE_NONE,
129 0);
130 }
131
132
133 static void
gth_filterbar_init(GthFilterbar * filterbar)134 gth_filterbar_init (GthFilterbar *filterbar)
135 {
136 filterbar->priv = gth_filterbar_get_instance_private (filterbar);
137 filterbar->priv->model = NULL;
138 filterbar->priv->test_combo_box = NULL;
139 filterbar->priv->test = NULL;
140 filterbar->priv->control_box = NULL;
141 filterbar->priv->control = NULL;
142 filterbar->priv->extra_area = NULL;
143 filterbar->priv->filters_changed_id = 0;
144 filterbar->priv->test_changed_id = 0;
145
146 gtk_orientable_set_orientation (GTK_ORIENTABLE (filterbar), GTK_ORIENTATION_HORIZONTAL);
147 }
148
149
150 static void
gth_filterbar_changed(GthFilterbar * filterbar)151 gth_filterbar_changed (GthFilterbar *filterbar)
152 {
153 g_signal_emit (filterbar, gth_filterbar_signals[CHANGED], 0);
154 }
155
156
157 static void
_gth_filterbar_set_test_control(GthFilterbar * filterbar,GtkWidget * control)158 _gth_filterbar_set_test_control (GthFilterbar *filterbar,
159 GtkWidget *control)
160 {
161 if (filterbar->priv->control != NULL) {
162 gtk_container_remove (GTK_CONTAINER (filterbar->priv->control_box),
163 filterbar->priv->control);
164 filterbar->priv->control = NULL;
165 }
166
167 gth_filterbar_changed (filterbar);
168
169 if (control == NULL)
170 return;
171
172 filterbar->priv->control = control;
173 gtk_widget_show (control);
174 gtk_container_add (GTK_CONTAINER (filterbar->priv->control_box),
175 filterbar->priv->control);
176 }
177
178
179 static void
test_changed_cb(GthTest * test,GthFilterbar * filterbar)180 test_changed_cb (GthTest *test,
181 GthFilterbar *filterbar)
182 {
183 gth_filterbar_changed (filterbar);
184 }
185
186
187 static void
_gth_filterbar_set_test(GthFilterbar * filterbar,GthTest * test)188 _gth_filterbar_set_test (GthFilterbar *filterbar,
189 GthTest *test)
190 {
191 GthTest *old_test;
192
193 old_test = filterbar->priv->test;
194 if (old_test != NULL) {
195 if (filterbar->priv->test_changed_id != 0)
196 g_signal_handler_disconnect (old_test, filterbar->priv->test_changed_id);
197 filterbar->priv->test = NULL;
198 }
199
200 if (test != NULL) {
201 filterbar->priv->test = g_object_ref (test);
202 filterbar->priv->test_changed_id = g_signal_connect (test,
203 "changed",
204 G_CALLBACK (test_changed_cb),
205 filterbar);
206 _gth_filterbar_set_test_control (filterbar, gth_test_create_control (filterbar->priv->test));
207 gth_test_focus_control (filterbar->priv->test);
208 }
209 else
210 _gth_filterbar_set_test_control (filterbar, NULL);
211
212 _g_object_unref (old_test);
213 }
214
215
216 static void
test_combo_box_changed_cb(GtkComboBox * scope_combo_box,GthFilterbar * filterbar)217 test_combo_box_changed_cb (GtkComboBox *scope_combo_box,
218 GthFilterbar *filterbar)
219 {
220 GtkTreeIter iter;
221 int item_type = ITEM_TYPE_NONE;
222 GthTest *test;
223
224 if (! gtk_combo_box_get_active_iter (scope_combo_box, &iter))
225 return;
226
227 gtk_tree_model_get (GTK_TREE_MODEL (filterbar->priv->model),
228 &iter,
229 TYPE_COLUMN, &item_type,
230 FILTER_COLUMN, &test,
231 -1);
232
233 switch (item_type) {
234 case ITEM_TYPE_FILTER:
235 _gth_filterbar_set_test (filterbar, test);
236 filterbar->priv->current_iter = iter;
237 break;
238 case ITEM_TYPE_PERSONALIZE:
239 g_signal_emit (filterbar, gth_filterbar_signals[PERSONALIZE], 0);
240 g_signal_handlers_block_by_func (filterbar->priv->test_combo_box, test_combo_box_changed_cb, filterbar);
241 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (filterbar->priv->test_combo_box), &filterbar->priv->current_iter);
242 g_signal_handlers_unblock_by_func (filterbar->priv->test_combo_box, test_combo_box_changed_cb, filterbar);
243 break;
244 default:
245 break;
246 }
247
248 if (test != NULL)
249 g_object_unref (test);
250 }
251
252
253 static gboolean
test_combo_box_row_separator_func(GtkTreeModel * model,GtkTreeIter * iter,gpointer data)254 test_combo_box_row_separator_func (GtkTreeModel *model,
255 GtkTreeIter *iter,
256 gpointer data)
257 {
258 int item_type = ITEM_TYPE_NONE;
259
260 gtk_tree_model_get (model, iter, TYPE_COLUMN, &item_type, -1);
261
262 return (item_type == ITEM_TYPE_SEPARATOR);
263 }
264
265
266 static void
update_filter_list(GthFilterbar * filterbar,const char * current_filter)267 update_filter_list (GthFilterbar *filterbar,
268 const char *current_filter)
269 {
270 gboolean no_filter_selected = TRUE;
271 GList *filters, *scan;
272 GtkTreeIter iter;
273
274 gtk_list_store_clear (filterbar->priv->model);
275
276 gtk_list_store_append (filterbar->priv->model, &iter);
277 gtk_list_store_set (filterbar->priv->model, &iter,
278 TYPE_COLUMN, ITEM_TYPE_FILTER,
279 FILTER_COLUMN, NULL,
280 NAME_COLUMN, _("All"),
281 -1);
282
283 filters = gth_main_get_all_filters ();
284 for (scan = filters; scan; scan = scan->next) {
285 GthTest *test = scan->data;
286
287 if (! gth_test_is_visible (test))
288 continue;
289
290 gtk_list_store_append (filterbar->priv->model, &iter);
291 gtk_list_store_set (filterbar->priv->model, &iter,
292 TYPE_COLUMN, ITEM_TYPE_FILTER,
293 FILTER_COLUMN, test,
294 NAME_COLUMN, gth_test_get_display_name (test),
295 -1);
296
297 if (g_strcmp0 (current_filter, gth_test_get_id (test)) == 0) {
298 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (filterbar->priv->test_combo_box), &iter);
299 filterbar->priv->current_iter = iter;
300 _gth_filterbar_set_test (GTH_FILTERBAR (filterbar), test);
301 no_filter_selected = FALSE;
302 }
303 }
304 _g_object_list_unref (filters);
305
306 gtk_list_store_append (filterbar->priv->model, &iter);
307 gtk_list_store_set (filterbar->priv->model, &iter,
308 TYPE_COLUMN, ITEM_TYPE_SEPARATOR,
309 -1);
310
311 gtk_list_store_append (filterbar->priv->model, &iter);
312 gtk_list_store_set (filterbar->priv->model, &iter,
313 TYPE_COLUMN, ITEM_TYPE_PERSONALIZE,
314 NAME_COLUMN, _("Personalize…"),
315 -1);
316
317 if (no_filter_selected) {
318 GtkTreeIter iter;
319
320 gtk_tree_model_get_iter_first (GTK_TREE_MODEL (filterbar->priv->model), &iter);
321 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (filterbar->priv->test_combo_box), &iter);
322 filterbar->priv->current_iter = iter;
323 }
324 }
325
326
327 static void
filters_changed_cb(GthMonitor * monitor,GthFilterbar * filterbar)328 filters_changed_cb (GthMonitor *monitor,
329 GthFilterbar *filterbar)
330 {
331 GthTest *current_filter;
332
333 gtk_tree_model_get (GTK_TREE_MODEL (filterbar->priv->model),
334 &filterbar->priv->current_iter,
335 FILTER_COLUMN, ¤t_filter,
336 -1);
337
338 update_filter_list (filterbar, current_filter != NULL ? gth_test_get_id (current_filter) : NULL);
339
340 if (current_filter != NULL)
341 g_object_unref (current_filter);
342 }
343
344
345 static void
gth_filterbar_construct(GthFilterbar * filterbar,const char * selected_filter)346 gth_filterbar_construct (GthFilterbar *filterbar,
347 const char *selected_filter)
348 {
349 GtkCellRenderer *renderer;
350 GtkWidget *label;
351
352 gtk_box_set_spacing (GTK_BOX (filterbar), 6);
353 gtk_container_set_border_width (GTK_CONTAINER (filterbar), 4);
354
355 /* filter combo box */
356
357 filterbar->priv->model = gtk_list_store_new (N_COLUMNS,
358 GDK_TYPE_PIXBUF,
359 G_TYPE_STRING,
360 G_TYPE_INT,
361 G_TYPE_OBJECT);
362 filterbar->priv->test_combo_box = gtk_combo_box_new_with_model (GTK_TREE_MODEL (filterbar->priv->model));
363 g_object_unref (filterbar->priv->model);
364 gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (filterbar->priv->test_combo_box),
365 test_combo_box_row_separator_func,
366 filterbar,
367 NULL);
368
369 /* icon renderer */
370
371 renderer = gtk_cell_renderer_pixbuf_new ();
372 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (filterbar->priv->test_combo_box),
373 renderer,
374 FALSE);
375 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (filterbar->priv->test_combo_box),
376 renderer,
377 "pixbuf", ICON_COLUMN,
378 NULL);
379
380 /* name renderer */
381
382 renderer = gtk_cell_renderer_text_new ();
383 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (filterbar->priv->test_combo_box),
384 renderer,
385 TRUE);
386 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (filterbar->priv->test_combo_box),
387 renderer,
388 "text", NAME_COLUMN,
389 NULL);
390
391 /**/
392
393 update_filter_list (filterbar, selected_filter);
394
395 g_signal_connect (G_OBJECT (filterbar->priv->test_combo_box),
396 "changed",
397 G_CALLBACK (test_combo_box_changed_cb),
398 filterbar);
399
400 gtk_widget_show (filterbar->priv->test_combo_box);
401
402 /* test control box */
403
404 filterbar->priv->control_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
405 gtk_widget_show (filterbar->priv->control_box);
406
407 /* extra widgets container */
408
409 filterbar->priv->extra_area = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
410 gtk_widget_show (filterbar->priv->extra_area);
411
412 /* view label */
413
414 label = gtk_label_new_with_mnemonic (_("S_how:"));
415 gtk_label_set_mnemonic_widget (GTK_LABEL (label), filterbar->priv->test_combo_box);
416 gtk_widget_show (label);
417
418 /**/
419
420 filterbar->priv->filters_changed_id =
421 g_signal_connect (gth_main_get_default_monitor (),
422 "filters-changed",
423 G_CALLBACK (filters_changed_cb),
424 filterbar);
425
426 gtk_box_pack_start (GTK_BOX (filterbar), label, FALSE, FALSE, 0);
427 gtk_box_pack_start (GTK_BOX (filterbar), filterbar->priv->test_combo_box, FALSE, FALSE, 0);
428 gtk_box_pack_start (GTK_BOX (filterbar), filterbar->priv->control_box, FALSE, FALSE, 0);
429 gtk_box_pack_end (GTK_BOX (filterbar), filterbar->priv->extra_area, FALSE, FALSE, 0);
430 }
431
432
433 GtkWidget*
gth_filterbar_new(const char * selected_filter)434 gth_filterbar_new (const char *selected_filter)
435 {
436 GtkWidget *widget;
437
438 widget = GTK_WIDGET (g_object_new (GTH_TYPE_FILTERBAR, NULL));
439 gth_filterbar_construct (GTH_FILTERBAR (widget), selected_filter);
440
441 return widget;
442 }
443
444
445 GthTest *
gth_filterbar_get_test(GthFilterbar * filterbar)446 gth_filterbar_get_test (GthFilterbar *filterbar)
447 {
448 if (filterbar->priv->test != NULL)
449 return g_object_ref (filterbar->priv->test);
450 else
451 return NULL;
452 }
453
454
455 void
gth_filterbar_save_filter(GthFilterbar * filterbar,const char * filename)456 gth_filterbar_save_filter (GthFilterbar *filterbar,
457 const char *filename)
458 {
459 char *filter_description;
460 gsize len;
461 GFile *filter_file;
462
463 if (filterbar->priv->test != NULL) {
464 DomDocument *doc;
465
466 doc = dom_document_new ();
467 dom_element_append_child (DOM_ELEMENT (doc), dom_domizable_create_element (DOM_DOMIZABLE (filterbar->priv->test), doc));
468 filter_description = dom_document_dump (doc, &len);
469
470 g_object_unref (doc);
471 }
472 else {
473 filter_description = g_strdup ("");
474 len = 0;
475 }
476 filter_file = gth_user_dir_get_file_for_write (GTH_DIR_CONFIG, GTHUMB_DIR, filename, NULL);
477 _g_file_write (filter_file, FALSE, 0, filter_description, len, NULL, NULL);
478
479 g_object_unref (filter_file);
480 g_free (filter_description);
481 }
482
483
484 static gboolean
find_test_by_id(GthFilterbar * filterbar,const char * id,GthTest ** test,GtkTreeIter * iter)485 find_test_by_id (GthFilterbar *filterbar,
486 const char *id,
487 GthTest **test,
488 GtkTreeIter *iter)
489 {
490 g_return_val_if_fail (test != NULL, FALSE);
491 g_return_val_if_fail (iter != NULL, FALSE);
492
493 if (! gtk_tree_model_get_iter_first(GTK_TREE_MODEL (filterbar->priv->model), iter))
494 return FALSE;
495
496 do {
497 int item_type = ITEM_TYPE_NONE;
498
499 gtk_tree_model_get (GTK_TREE_MODEL (filterbar->priv->model),
500 iter,
501 TYPE_COLUMN, &item_type,
502 FILTER_COLUMN, test,
503 -1);
504
505 if ((item_type == ITEM_TYPE_FILTER) && (*test != NULL) && (g_strcmp0 (gth_test_get_id (*test), id) == 0))
506 return TRUE;
507 }
508 while (gtk_tree_model_iter_next (GTK_TREE_MODEL (filterbar->priv->model), iter));
509
510 return FALSE;
511 }
512
513
514 void
gth_filterbar_load_filter(GthFilterbar * filterbar,const char * filename)515 gth_filterbar_load_filter (GthFilterbar *filterbar,
516 const char *filename)
517 {
518 GFile *filter_file;
519 char *buffer;
520 gsize len;
521 DomDocument *doc;
522
523 filter_file = gth_user_dir_get_file_for_write (GTH_DIR_CONFIG, GTHUMB_DIR, filename, NULL);
524 if (! _g_file_load_in_buffer (filter_file, (void **) &buffer, &len, NULL, NULL)) {
525 g_object_unref (filter_file);
526 return;
527 }
528
529 if (buffer == NULL) {
530 g_object_unref (filter_file);
531 return;
532 }
533
534 doc = dom_document_new ();
535 if (dom_document_load (doc, buffer, len, NULL)) {
536 DomElement *node = DOM_ELEMENT (doc)->first_child;
537
538 if (node != NULL) {
539 GthTest *test;
540 GtkTreeIter iter;
541
542 if (find_test_by_id (filterbar,
543 dom_element_get_attribute (node, "id"),
544 &test,
545 &iter))
546 {
547 dom_domizable_load_from_element (DOM_DOMIZABLE (test), node);
548
549 g_signal_handlers_block_by_func (filterbar->priv->test_combo_box, test_combo_box_changed_cb, filterbar);
550 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (filterbar->priv->test_combo_box), &iter);
551 g_signal_handlers_unblock_by_func (filterbar->priv->test_combo_box, test_combo_box_changed_cb, filterbar);
552
553 filterbar->priv->current_iter = iter;
554 _gth_filterbar_set_test (GTH_FILTERBAR (filterbar), test);
555 }
556 }
557 }
558
559 g_object_unref (doc);
560 g_free (buffer);
561 g_object_unref (filter_file);
562 }
563
564
565 GtkWidget *
gth_filterbar_get_extra_area(GthFilterbar * filterbar)566 gth_filterbar_get_extra_area (GthFilterbar *filterbar)
567 {
568 return filterbar->priv->extra_area;
569 }
570