1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * gimpdataeditor.c
5 * Copyright (C) 2002-2004 Michael Natterer <mitch@gimp.org>
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <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 #include <gdk/gdkkeysyms.h>
28
29 #include "libgimpwidgets/gimpwidgets.h"
30
31 #include "widgets-types.h"
32
33 #include "core/gimp.h"
34 #include "core/gimpcontainer.h"
35 #include "core/gimpcontext.h"
36 #include "core/gimpdata.h"
37 #include "core/gimpdatafactory.h"
38
39 #include "gimpdataeditor.h"
40 #include "gimpdocked.h"
41 #include "gimpmenufactory.h"
42 #include "gimpsessioninfo-aux.h"
43 #include "gimpuimanager.h"
44
45 #include "gimp-intl.h"
46
47
48 #define DEFAULT_MINIMAL_HEIGHT 96
49
50
51 enum
52 {
53 PROP_0,
54 PROP_DATA_FACTORY,
55 PROP_CONTEXT,
56 PROP_DATA
57 };
58
59
60 static void gimp_data_editor_docked_iface_init (GimpDockedInterface *iface);
61
62 static void gimp_data_editor_constructed (GObject *object);
63 static void gimp_data_editor_dispose (GObject *object);
64 static void gimp_data_editor_set_property (GObject *object,
65 guint property_id,
66 const GValue *value,
67 GParamSpec *pspec);
68 static void gimp_data_editor_get_property (GObject *object,
69 guint property_id,
70 GValue *value,
71 GParamSpec *pspec);
72
73 static void gimp_data_editor_style_set (GtkWidget *widget,
74 GtkStyle *prev_style);
75
76 static void gimp_data_editor_set_context (GimpDocked *docked,
77 GimpContext *context);
78 static void gimp_data_editor_set_aux_info (GimpDocked *docked,
79 GList *aux_info);
80 static GList * gimp_data_editor_get_aux_info (GimpDocked *docked);
81 static gchar * gimp_data_editor_get_title (GimpDocked *docked);
82
83 static void gimp_data_editor_real_set_data (GimpDataEditor *editor,
84 GimpData *data);
85
86 static void gimp_data_editor_data_changed (GimpContext *context,
87 GimpData *data,
88 GimpDataEditor *editor);
89 static gboolean gimp_data_editor_name_key_press (GtkWidget *widget,
90 GdkEventKey *kevent,
91 GimpDataEditor *editor);
92 static void gimp_data_editor_name_activate (GtkWidget *widget,
93 GimpDataEditor *editor);
94 static gboolean gimp_data_editor_name_focus_out (GtkWidget *widget,
95 GdkEvent *event,
96 GimpDataEditor *editor);
97
98 static void gimp_data_editor_data_name_changed (GimpObject *object,
99 GimpDataEditor *editor);
100
101 static void gimp_data_editor_save_dirty (GimpDataEditor *editor);
102
103
104 G_DEFINE_TYPE_WITH_CODE (GimpDataEditor, gimp_data_editor, GIMP_TYPE_EDITOR,
105 G_IMPLEMENT_INTERFACE (GIMP_TYPE_DOCKED,
106 gimp_data_editor_docked_iface_init))
107
108 #define parent_class gimp_data_editor_parent_class
109
110 static GimpDockedInterface *parent_docked_iface = NULL;
111
112
113 static void
gimp_data_editor_class_init(GimpDataEditorClass * klass)114 gimp_data_editor_class_init (GimpDataEditorClass *klass)
115 {
116 GObjectClass *object_class = G_OBJECT_CLASS (klass);
117 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
118
119 object_class->constructed = gimp_data_editor_constructed;
120 object_class->dispose = gimp_data_editor_dispose;
121 object_class->set_property = gimp_data_editor_set_property;
122 object_class->get_property = gimp_data_editor_get_property;
123
124 widget_class->style_set = gimp_data_editor_style_set;
125
126 klass->set_data = gimp_data_editor_real_set_data;
127
128 g_object_class_install_property (object_class, PROP_DATA_FACTORY,
129 g_param_spec_object ("data-factory",
130 NULL, NULL,
131 GIMP_TYPE_DATA_FACTORY,
132 GIMP_PARAM_READWRITE |
133 G_PARAM_CONSTRUCT_ONLY));
134
135 g_object_class_install_property (object_class, PROP_CONTEXT,
136 g_param_spec_object ("context",
137 NULL, NULL,
138 GIMP_TYPE_CONTEXT,
139 GIMP_PARAM_READWRITE |
140 G_PARAM_CONSTRUCT_ONLY));
141
142 g_object_class_install_property (object_class, PROP_DATA,
143 g_param_spec_object ("data",
144 NULL, NULL,
145 GIMP_TYPE_DATA,
146 GIMP_PARAM_READWRITE));
147
148 gtk_widget_class_install_style_property (widget_class,
149 g_param_spec_int ("minimal-height",
150 NULL, NULL,
151 32,
152 G_MAXINT,
153 DEFAULT_MINIMAL_HEIGHT,
154 GIMP_PARAM_READABLE));
155 }
156
157 static void
gimp_data_editor_docked_iface_init(GimpDockedInterface * iface)158 gimp_data_editor_docked_iface_init (GimpDockedInterface *iface)
159 {
160 parent_docked_iface = g_type_interface_peek_parent (iface);
161
162 if (! parent_docked_iface)
163 parent_docked_iface = g_type_default_interface_peek (GIMP_TYPE_DOCKED);
164
165 iface->set_context = gimp_data_editor_set_context;
166 iface->set_aux_info = gimp_data_editor_set_aux_info;
167 iface->get_aux_info = gimp_data_editor_get_aux_info;
168 iface->get_title = gimp_data_editor_get_title;
169 }
170
171 static void
gimp_data_editor_init(GimpDataEditor * editor)172 gimp_data_editor_init (GimpDataEditor *editor)
173 {
174 editor->data_factory = NULL;
175 editor->context = NULL;
176 editor->data = NULL;
177 editor->data_editable = FALSE;
178
179 editor->name_entry = gtk_entry_new ();
180 gtk_box_pack_start (GTK_BOX (editor), editor->name_entry, FALSE, FALSE, 0);
181 gtk_widget_show (editor->name_entry);
182
183 gtk_editable_set_editable (GTK_EDITABLE (editor->name_entry), FALSE);
184
185 g_signal_connect (editor->name_entry, "key-press-event",
186 G_CALLBACK (gimp_data_editor_name_key_press),
187 editor);
188 g_signal_connect (editor->name_entry, "activate",
189 G_CALLBACK (gimp_data_editor_name_activate),
190 editor);
191 g_signal_connect (editor->name_entry, "focus-out-event",
192 G_CALLBACK (gimp_data_editor_name_focus_out),
193 editor);
194 }
195
196 static void
gimp_data_editor_constructed(GObject * object)197 gimp_data_editor_constructed (GObject *object)
198 {
199 GimpDataEditor *editor = GIMP_DATA_EDITOR (object);
200
201 G_OBJECT_CLASS (parent_class)->constructed (object);
202
203 gimp_assert (GIMP_IS_DATA_FACTORY (editor->data_factory));
204 gimp_assert (GIMP_IS_CONTEXT (editor->context));
205
206 gimp_data_editor_set_edit_active (editor, TRUE);
207 }
208
209 static void
gimp_data_editor_dispose(GObject * object)210 gimp_data_editor_dispose (GObject *object)
211 {
212 GimpDataEditor *editor = GIMP_DATA_EDITOR (object);
213
214 if (editor->data)
215 {
216 /* Save dirty data before we clear out */
217 gimp_data_editor_save_dirty (editor);
218 gimp_data_editor_set_data (editor, NULL);
219 }
220
221 if (editor->context)
222 gimp_docked_set_context (GIMP_DOCKED (editor), NULL);
223
224 G_OBJECT_CLASS (parent_class)->dispose (object);
225 }
226
227 static void
gimp_data_editor_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)228 gimp_data_editor_set_property (GObject *object,
229 guint property_id,
230 const GValue *value,
231 GParamSpec *pspec)
232 {
233 GimpDataEditor *editor = GIMP_DATA_EDITOR (object);
234
235 switch (property_id)
236 {
237 case PROP_DATA_FACTORY:
238 editor->data_factory = g_value_get_object (value);
239 break;
240 case PROP_CONTEXT:
241 gimp_docked_set_context (GIMP_DOCKED (object),
242 g_value_get_object (value));
243 break;
244 case PROP_DATA:
245 gimp_data_editor_set_data (editor, g_value_get_object (value));
246 break;
247 default:
248 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
249 break;
250 }
251 }
252
253 static void
gimp_data_editor_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)254 gimp_data_editor_get_property (GObject *object,
255 guint property_id,
256 GValue *value,
257 GParamSpec *pspec)
258 {
259 GimpDataEditor *editor = GIMP_DATA_EDITOR (object);
260
261 switch (property_id)
262 {
263 case PROP_DATA_FACTORY:
264 g_value_set_object (value, editor->data_factory);
265 break;
266 case PROP_CONTEXT:
267 g_value_set_object (value, editor->context);
268 break;
269 case PROP_DATA:
270 g_value_set_object (value, editor->data);
271 break;
272 default:
273 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
274 break;
275 }
276 }
277
278 static void
gimp_data_editor_style_set(GtkWidget * widget,GtkStyle * prev_style)279 gimp_data_editor_style_set (GtkWidget *widget,
280 GtkStyle *prev_style)
281 {
282 GimpDataEditor *editor = GIMP_DATA_EDITOR (widget);
283 gint minimal_height;
284
285 GTK_WIDGET_CLASS (parent_class)->style_set (widget, prev_style);
286
287 gtk_widget_style_get (widget,
288 "minimal-height", &minimal_height,
289 NULL);
290
291 if (editor->view)
292 gtk_widget_set_size_request (editor->view, -1, minimal_height);
293 }
294
295 static void
gimp_data_editor_set_context(GimpDocked * docked,GimpContext * context)296 gimp_data_editor_set_context (GimpDocked *docked,
297 GimpContext *context)
298 {
299 GimpDataEditor *editor = GIMP_DATA_EDITOR (docked);
300
301 if (context == editor->context)
302 return;
303
304 if (parent_docked_iface->set_context)
305 parent_docked_iface->set_context (docked, context);
306
307 if (editor->context)
308 {
309 g_signal_handlers_disconnect_by_func (editor->context,
310 gimp_data_editor_data_changed,
311 editor);
312
313 g_object_unref (editor->context);
314 }
315
316 editor->context = context;
317
318 if (editor->context)
319 {
320 GType data_type;
321 GimpData *data;
322
323 g_object_ref (editor->context);
324
325 data_type = gimp_data_factory_get_data_type (editor->data_factory);
326 data = GIMP_DATA (gimp_context_get_by_type (editor->context, data_type));
327
328 g_signal_connect (editor->context,
329 gimp_context_type_to_signal_name (data_type),
330 G_CALLBACK (gimp_data_editor_data_changed),
331 editor);
332
333 gimp_data_editor_data_changed (editor->context, data, editor);
334 }
335 }
336
337 #define AUX_INFO_EDIT_ACTIVE "edit-active"
338 #define AUX_INFO_CURRENT_DATA "current-data"
339
340 static void
gimp_data_editor_set_aux_info(GimpDocked * docked,GList * aux_info)341 gimp_data_editor_set_aux_info (GimpDocked *docked,
342 GList *aux_info)
343 {
344 GimpDataEditor *editor = GIMP_DATA_EDITOR (docked);
345 GList *list;
346
347 parent_docked_iface->set_aux_info (docked, aux_info);
348
349 for (list = aux_info; list; list = g_list_next (list))
350 {
351 GimpSessionInfoAux *aux = list->data;
352
353 if (! strcmp (aux->name, AUX_INFO_EDIT_ACTIVE))
354 {
355 gboolean edit_active;
356
357 edit_active = ! g_ascii_strcasecmp (aux->value, "true");
358
359 gimp_data_editor_set_edit_active (editor, edit_active);
360 }
361 else if (! strcmp (aux->name, AUX_INFO_CURRENT_DATA))
362 {
363 if (! editor->edit_active)
364 {
365 GimpData *data;
366
367 data = (GimpData *)
368 gimp_container_get_child_by_name (gimp_data_factory_get_container (editor->data_factory),
369 aux->value);
370
371 if (data)
372 gimp_data_editor_set_data (editor, data);
373 }
374 }
375 }
376 }
377
378 static GList *
gimp_data_editor_get_aux_info(GimpDocked * docked)379 gimp_data_editor_get_aux_info (GimpDocked *docked)
380 {
381 GimpDataEditor *editor = GIMP_DATA_EDITOR (docked);
382 GList *aux_info;
383 GimpSessionInfoAux *aux;
384
385 aux_info = parent_docked_iface->get_aux_info (docked);
386
387 aux = gimp_session_info_aux_new (AUX_INFO_EDIT_ACTIVE,
388 editor->edit_active ? "true" : "false");
389 aux_info = g_list_append (aux_info, aux);
390
391 if (editor->data)
392 {
393 const gchar *value;
394
395 value = gimp_object_get_name (editor->data);
396
397 aux = gimp_session_info_aux_new (AUX_INFO_CURRENT_DATA, value);
398 aux_info = g_list_append (aux_info, aux);
399 }
400
401 return aux_info;
402 }
403
404 static gchar *
gimp_data_editor_get_title(GimpDocked * docked)405 gimp_data_editor_get_title (GimpDocked *docked)
406 {
407 GimpDataEditor *editor = GIMP_DATA_EDITOR (docked);
408 GimpDataEditorClass *editor_class = GIMP_DATA_EDITOR_GET_CLASS (editor);
409
410 if (editor->data_editable)
411 return g_strdup (editor_class->title);
412 else
413 return g_strdup_printf (_("%s (read only)"), editor_class->title);
414 }
415
416 static void
gimp_data_editor_real_set_data(GimpDataEditor * editor,GimpData * data)417 gimp_data_editor_real_set_data (GimpDataEditor *editor,
418 GimpData *data)
419 {
420 gboolean editable;
421
422 if (editor->data)
423 {
424 gimp_data_editor_save_dirty (editor);
425
426 g_signal_handlers_disconnect_by_func (editor->data,
427 gimp_data_editor_data_name_changed,
428 editor);
429
430 g_object_unref (editor->data);
431 }
432
433 editor->data = data;
434
435 if (editor->data)
436 {
437 g_object_ref (editor->data);
438
439 g_signal_connect (editor->data, "name-changed",
440 G_CALLBACK (gimp_data_editor_data_name_changed),
441 editor);
442
443 gtk_entry_set_text (GTK_ENTRY (editor->name_entry),
444 gimp_object_get_name (editor->data));
445 }
446 else
447 {
448 gtk_entry_set_text (GTK_ENTRY (editor->name_entry), "");
449 }
450
451 gtk_editable_set_editable (
452 GTK_EDITABLE (editor->name_entry),
453 editor->data &&
454 gimp_viewable_is_name_editable (GIMP_VIEWABLE (editor->data)));
455
456 editable = (editor->data && gimp_data_is_writable (editor->data));
457
458 if (editor->data_editable != editable)
459 {
460 editor->data_editable = editable;
461
462 gimp_docked_title_changed (GIMP_DOCKED (editor));
463 }
464 }
465
466 void
gimp_data_editor_set_data(GimpDataEditor * editor,GimpData * data)467 gimp_data_editor_set_data (GimpDataEditor *editor,
468 GimpData *data)
469 {
470 g_return_if_fail (GIMP_IS_DATA_EDITOR (editor));
471 g_return_if_fail (data == NULL || GIMP_IS_DATA (data));
472 g_return_if_fail (data == NULL ||
473 g_type_is_a (G_TYPE_FROM_INSTANCE (data),
474 gimp_data_factory_get_data_type (editor->data_factory)));
475
476 if (editor->data != data)
477 {
478 GIMP_DATA_EDITOR_GET_CLASS (editor)->set_data (editor, data);
479
480 g_object_notify (G_OBJECT (editor), "data");
481
482 if (gimp_editor_get_ui_manager (GIMP_EDITOR (editor)))
483 gimp_ui_manager_update (gimp_editor_get_ui_manager (GIMP_EDITOR (editor)),
484 gimp_editor_get_popup_data (GIMP_EDITOR (editor)));
485 }
486 }
487
488 GimpData *
gimp_data_editor_get_data(GimpDataEditor * editor)489 gimp_data_editor_get_data (GimpDataEditor *editor)
490 {
491 g_return_val_if_fail (GIMP_IS_DATA_EDITOR (editor), NULL);
492
493 return editor->data;
494 }
495
496 void
gimp_data_editor_set_edit_active(GimpDataEditor * editor,gboolean edit_active)497 gimp_data_editor_set_edit_active (GimpDataEditor *editor,
498 gboolean edit_active)
499 {
500 g_return_if_fail (GIMP_IS_DATA_EDITOR (editor));
501
502 if (editor->edit_active != edit_active)
503 {
504 editor->edit_active = edit_active;
505
506 if (editor->edit_active && editor->context)
507 {
508 GType data_type;
509 GimpData *data;
510
511 data_type = gimp_data_factory_get_data_type (editor->data_factory);
512 data = GIMP_DATA (gimp_context_get_by_type (editor->context,
513 data_type));
514
515 gimp_data_editor_set_data (editor, data);
516 }
517 }
518 }
519
520 gboolean
gimp_data_editor_get_edit_active(GimpDataEditor * editor)521 gimp_data_editor_get_edit_active (GimpDataEditor *editor)
522 {
523 g_return_val_if_fail (GIMP_IS_DATA_EDITOR (editor), FALSE);
524
525 return editor->edit_active;
526 }
527
528
529 /* private functions */
530
531 static void
gimp_data_editor_data_changed(GimpContext * context,GimpData * data,GimpDataEditor * editor)532 gimp_data_editor_data_changed (GimpContext *context,
533 GimpData *data,
534 GimpDataEditor *editor)
535 {
536 if (editor->edit_active)
537 gimp_data_editor_set_data (editor, data);
538 }
539
540 static gboolean
gimp_data_editor_name_key_press(GtkWidget * widget,GdkEventKey * kevent,GimpDataEditor * editor)541 gimp_data_editor_name_key_press (GtkWidget *widget,
542 GdkEventKey *kevent,
543 GimpDataEditor *editor)
544 {
545 if (kevent->keyval == GDK_KEY_Escape)
546 {
547 gtk_entry_set_text (GTK_ENTRY (editor->name_entry),
548 gimp_object_get_name (editor->data));
549 return TRUE;
550 }
551
552 return FALSE;
553 }
554
555 static void
gimp_data_editor_name_activate(GtkWidget * widget,GimpDataEditor * editor)556 gimp_data_editor_name_activate (GtkWidget *widget,
557 GimpDataEditor *editor)
558 {
559 if (editor->data)
560 {
561 gchar *new_name;
562
563 new_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (widget)));
564 new_name = g_strstrip (new_name);
565
566 if (strlen (new_name) &&
567 g_strcmp0 (new_name, gimp_object_get_name (editor->data)))
568 {
569 gimp_object_take_name (GIMP_OBJECT (editor->data), new_name);
570 }
571 else
572 {
573 gtk_entry_set_text (GTK_ENTRY (widget),
574 gimp_object_get_name (editor->data));
575 g_free (new_name);
576 }
577 }
578 }
579
580 static gboolean
gimp_data_editor_name_focus_out(GtkWidget * widget,GdkEvent * event,GimpDataEditor * editor)581 gimp_data_editor_name_focus_out (GtkWidget *widget,
582 GdkEvent *event,
583 GimpDataEditor *editor)
584 {
585 gimp_data_editor_name_activate (widget, editor);
586
587 return FALSE;
588 }
589
590 static void
gimp_data_editor_data_name_changed(GimpObject * object,GimpDataEditor * editor)591 gimp_data_editor_data_name_changed (GimpObject *object,
592 GimpDataEditor *editor)
593 {
594 gtk_entry_set_text (GTK_ENTRY (editor->name_entry),
595 gimp_object_get_name (object));
596 }
597
598 static void
gimp_data_editor_save_dirty(GimpDataEditor * editor)599 gimp_data_editor_save_dirty (GimpDataEditor *editor)
600 {
601 GimpData *data = editor->data;
602
603 if (data &&
604 gimp_data_is_dirty (data) &&
605 gimp_data_is_writable (data))
606 {
607 GError *error = NULL;
608
609 if (! gimp_data_factory_data_save_single (editor->data_factory, data,
610 &error))
611 {
612 gimp_message_literal (gimp_data_factory_get_gimp (editor->data_factory),
613 G_OBJECT (editor),
614 GIMP_MESSAGE_ERROR,
615 error->message);
616 g_clear_error (&error);
617 }
618 }
619 }
620