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