c65162379c4c9b2b2c04a4706210c45db04363b5
[pspp] / src / ui / gui / psppire-dialog-action.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2012, 2016  Free Software Foundation
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17
18 #include <config.h>
19
20 #include "psppire-dialog-action.h"
21 #include "psppire-dialog.h"
22 #include "executor.h"
23 #include "helper.h"
24 #include "psppire-data-window.h"
25 #include "builder-wrapper.h"
26
27 static void psppire_dialog_action_init            (PsppireDialogAction      *act);
28 static void psppire_dialog_action_class_init      (PsppireDialogActionClass *class);
29
30 static GObjectClass * parent_class = NULL;
31
32
33 static const gchar *
34 __get_name (GAction *act)
35 {
36   return G_OBJECT_TYPE_NAME (act);
37 }
38
39 static const GVariantType *
40 __get_state_type (GAction *act)
41 {
42   return NULL;
43 }
44
45
46 static GVariant *
47 __get_state (GAction *act)
48 {
49   return NULL;
50 }
51
52
53 static const GVariantType *
54 __get_parameter_type (GAction *act)
55 {
56   return PSPPIRE_DIALOG_ACTION (act)->parameter_type;
57 }
58
59 static gboolean
60 __get_enabled (GAction *act)
61 {
62   return TRUE;
63 }
64
65 static void psppire_dialog_action_activate (PsppireDialogAction *act, GVariant *parameter);
66
67 void
68 psppire_dialog_action_activate_null (PsppireDialogAction *act)
69 {
70   psppire_dialog_action_activate (act, NULL);
71 }
72
73
74 static void
75 __activate (GAction *action, GVariant *parameter)
76 {
77   psppire_dialog_action_activate (PSPPIRE_DIALOG_ACTION (action), parameter);
78 }
79
80
81 static void
82 action_model_init (GActionInterface *iface)
83 {
84   iface->get_name = __get_name;
85   iface->get_state_type = __get_state_type;
86   iface->get_state = __get_state;
87   iface->get_parameter_type = __get_parameter_type;
88   iface->get_enabled = __get_enabled;
89   iface->activate = __activate;
90 }
91
92
93 GType
94 psppire_dialog_action_get_type (void)
95 {
96   static GType de_type = 0;
97
98   if (!de_type)
99     {
100       static const GTypeInfo de_info =
101       {
102         sizeof (PsppireDialogActionClass),
103         NULL, /* base_init */
104         NULL, /* base_finalize */
105         (GClassInitFunc) psppire_dialog_action_class_init,
106         NULL, /* class_finalize */
107         NULL, /* class_data */
108         sizeof (PsppireDialogAction),
109         0,
110         (GInstanceInitFunc) psppire_dialog_action_init,
111       };
112
113
114       static const GInterfaceInfo ga_info = {
115         (GInterfaceInitFunc) action_model_init,
116         NULL,
117         NULL
118       };
119
120
121       de_type = g_type_register_static (G_TYPE_OBJECT, "PsppireDialogAction",
122                                         &de_info, G_TYPE_FLAG_ABSTRACT);
123
124       g_type_add_interface_static (de_type, G_TYPE_ACTION, &ga_info);
125     }
126
127   return de_type;
128 }
129
130
131 /* Properties */
132 enum
133 {
134   PROP_0,
135   PROP_TOPLEVEL,
136   PROP_NAME,
137   PROP_ENABLED,
138   PROP_STATE,
139   PROP_STATE_TYPE,
140   PROP_PARAMETER_TYPE
141 };
142
143 static void
144 psppire_dialog_action_set_property (GObject         *object,
145                                guint            prop_id,
146                                const GValue    *value,
147                                GParamSpec      *pspec)
148 {
149   PsppireDialogAction *act = PSPPIRE_DIALOG_ACTION (object);
150
151   switch (prop_id)
152     {
153     case PROP_TOPLEVEL:
154       {
155         GObject *p = g_value_get_object (value);
156         act->toplevel = GTK_WIDGET (p);
157       }
158       break;
159     default:
160       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
161       break;
162     };
163 }
164
165
166 static void
167 psppire_dialog_action_get_property (GObject    *object,
168                                guint            prop_id,
169                                GValue          *value,
170                                GParamSpec      *pspec)
171 {
172   PsppireDialogAction *dialog_action = PSPPIRE_DIALOG_ACTION (object);
173
174   switch (prop_id)
175     {
176     case PROP_TOPLEVEL:
177       g_value_take_object (value, dialog_action->toplevel);
178       break;
179     default:
180       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
181       break;
182     };
183 }
184
185
186
187 static void
188 on_destroy_dataset (GObject *w)
189 {
190   GHashTable *t = g_object_get_data (w, "thing-table");
191   GSList *dl = g_object_get_data (w, "widget-list");
192
193   g_slist_free_full (dl, (GDestroyNotify) gtk_widget_destroy);
194   g_hash_table_unref (t);
195 }
196
197 /* Each toplevel widget - that is the data window, which generally has a 1-1 association
198    with a dataset - has an associated GHashTable.
199
200    This GHashTable is keyed by the address of a PsppireDialogAction, and its values
201    are user determined pointers (typically a GtkBuilder*).
202
203    This is useful for storing the state of dialogs so they can persist between invocations.
204 */
205 GHashTable *
206 psppire_dialog_action_get_hash_table (PsppireDialogAction *act)
207 {
208   GHashTable *t = g_object_get_data (G_OBJECT (act->toplevel), "thing-table");
209   if (t == NULL)
210     {
211       t = g_hash_table_new_full (g_direct_hash, g_direct_equal, 0, g_object_unref);
212       g_object_set_data (G_OBJECT (act->toplevel), "thing-table", t);
213       g_object_set_data (G_OBJECT (act->toplevel), "widget-list", NULL);
214       g_signal_connect (act->toplevel, "destroy", G_CALLBACK (on_destroy_dataset), NULL);
215     }
216
217   return t;
218 }
219
220 GtkBuilder *
221 psppire_dialog_action_get_xml (PsppireDialogAction *da)
222 {
223   GHashTable *thing = psppire_dialog_action_get_hash_table (da);
224   GtkBuilder *xml = g_hash_table_lookup (thing, da);
225   return xml;
226 }
227
228
229 static void
230 psppire_dialog_action_activate (PsppireDialogAction *act, GVariant *parameter)
231 {
232   gint response;
233
234   PsppireDialogActionClass *class = PSPPIRE_DIALOG_ACTION_GET_CLASS (act);
235
236   act->dict = PSPPIRE_DATA_WINDOW(act->toplevel)->dict;
237
238   GSList *wl = g_object_get_data (G_OBJECT (act->toplevel), "widget-list");
239   wl = g_slist_prepend (wl, act->dialog);
240   g_object_set_data (G_OBJECT (act->toplevel), "widget-list", wl);
241
242   if (class->initial_activate)
243     {
244       GHashTable *thing = psppire_dialog_action_get_hash_table (act);
245       GtkBuilder *xml = g_hash_table_lookup (thing, act);
246       if (xml == NULL)
247         {
248           xml = class->initial_activate (act, parameter);
249           g_hash_table_insert (thing, act, xml);
250         }
251     }
252
253   if (class->activate)
254     {
255       GHashTable *thing = psppire_dialog_action_get_hash_table (act);
256       GtkBuilder *xml = g_hash_table_lookup (thing, act);
257       if (xml != NULL)
258         class->activate (act, parameter);
259     }
260
261   gtk_window_set_transient_for (GTK_WINDOW (act->dialog),
262                                 GTK_WINDOW (act->toplevel));
263
264   if (act->source)
265     {
266       g_object_set (act->source, "model", act->dict, NULL);
267       gtk_widget_grab_focus (act->source);
268     }
269
270   if (!act->activated)
271     psppire_dialog_reload (PSPPIRE_DIALOG (act->dialog));
272
273   act->activated = TRUE;
274
275   response = psppire_dialog_run (PSPPIRE_DIALOG (act->dialog));
276
277   if (class->generate_syntax)
278     {
279       switch (response)
280         {
281         case GTK_RESPONSE_OK:
282           g_free (execute_syntax_string (PSPPIRE_DATA_WINDOW (act->toplevel),
283                                          class->generate_syntax (act)));
284           break;
285         case PSPPIRE_RESPONSE_PASTE:
286           g_free (paste_syntax_to_window (class->generate_syntax (act)));
287           break;
288         default:
289           break;
290         }
291     }
292 }
293
294 static void
295 psppire_dialog_action_class_init (PsppireDialogActionClass *class)
296 {
297   GObjectClass *object_class = G_OBJECT_CLASS (class);
298
299   parent_class = g_type_class_peek_parent (class);
300
301   GParamSpec *toplevel_spec =
302     g_param_spec_object ("top-level",
303                          "Top Level",
304                          "The top level widget to which this dialog action belongs",
305                          GTK_TYPE_WINDOW,
306                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
307
308   object_class->set_property = psppire_dialog_action_set_property;
309   object_class->get_property = psppire_dialog_action_get_property;
310
311   class->generate_syntax = NULL;
312
313   g_object_class_install_property (object_class,
314                                    PROP_TOPLEVEL,
315                                    toplevel_spec);
316
317   g_object_class_override_property (object_class, PROP_NAME, "name");
318   g_object_class_override_property (object_class, PROP_ENABLED, "enabled");
319   g_object_class_override_property (object_class, PROP_STATE, "state");
320   g_object_class_override_property (object_class, PROP_STATE_TYPE, "state-type");
321   g_object_class_override_property (object_class, PROP_PARAMETER_TYPE, "parameter-type");
322 }
323
324
325 static void
326 psppire_dialog_action_init (PsppireDialogAction *act)
327 {
328   act->toplevel = NULL;
329   act->dict = NULL;
330   act->activated = FALSE;
331   act->parameter_type = NULL;
332 }
333
334 void
335 psppire_dialog_action_set_valid_predicate (PsppireDialogAction *act,
336                                            ContentsAreValid dialog_state_valid)
337 {
338   psppire_dialog_set_valid_predicate (PSPPIRE_DIALOG (act->dialog),
339                                       dialog_state_valid, act);
340 }
341
342 void
343 psppire_dialog_action_set_refresh (PsppireDialogAction *pda,
344                                    PsppireDialogActionRefresh refresh)
345 {
346   g_signal_connect_swapped (pda->dialog, "refresh", G_CALLBACK (refresh),  pda);
347 }