Merge remote-tracking branch 'origin/master' into sheet
[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
26 static void psppire_dialog_action_init            (PsppireDialogAction      *act);
27 static void psppire_dialog_action_class_init      (PsppireDialogActionClass *class);
28
29 static GObjectClass * parent_class = NULL;
30
31
32 static const gchar *
33 __get_name (GAction *act)
34 {
35   return G_OBJECT_TYPE_NAME (act);
36 }
37
38 static const GVariantType *
39 __get_state_type (GAction *act)
40 {
41   return NULL;
42 }
43
44
45 static GVariant *
46 __get_state (GAction *act)
47 {
48   return NULL;
49 }
50
51
52 static const GVariantType *
53 __get_parameter_type (GAction *act)
54 {
55   return PSPPIRE_DIALOG_ACTION (act)->parameter_type;
56 }
57
58 static gboolean
59 __get_enabled (GAction *act)
60 {
61   return TRUE;
62 }
63
64 static void psppire_dialog_action_activate (PsppireDialogAction *act, GVariant *parameter);
65
66 void
67 psppire_dialog_action_activate_null (PsppireDialogAction *act)
68 {
69   psppire_dialog_action_activate (act, NULL);
70 }
71
72
73 static void
74 __activate (GAction *action, GVariant *parameter)
75 {
76   psppire_dialog_action_activate (PSPPIRE_DIALOG_ACTION (action), parameter);
77 }
78
79
80 static void
81 action_model_init (GActionInterface *iface)
82 {
83   iface->get_name = __get_name;
84   iface->get_state_type = __get_state_type;
85   iface->get_state = __get_state;
86   iface->get_parameter_type = __get_parameter_type;
87   iface->get_enabled = __get_enabled;
88   iface->activate = __activate;
89 }
90
91
92 GType
93 psppire_dialog_action_get_type (void)
94 {
95   static GType de_type = 0;
96
97   if (!de_type)
98     {
99       static const GTypeInfo de_info =
100       {
101         sizeof (PsppireDialogActionClass),
102         NULL, /* base_init */
103         NULL, /* base_finalize */
104         (GClassInitFunc) psppire_dialog_action_class_init,
105         NULL, /* class_finalize */
106         NULL, /* class_data */
107         sizeof (PsppireDialogAction),
108         0,
109         (GInstanceInitFunc) psppire_dialog_action_init,
110       };
111
112
113       static const GInterfaceInfo ga_info = {
114         (GInterfaceInitFunc) action_model_init,
115         NULL,
116         NULL
117       };
118
119
120       de_type = g_type_register_static (G_TYPE_OBJECT, "PsppireDialogAction",
121                                         &de_info, G_TYPE_FLAG_ABSTRACT);
122
123       g_type_add_interface_static (de_type, G_TYPE_ACTION, &ga_info);
124     }
125
126   return de_type;
127 }
128
129
130 /* Properties */
131 enum
132 {
133   PROP_0,
134   PROP_TOPLEVEL,
135   PROP_NAME,
136   PROP_ENABLED,
137   PROP_STATE,
138   PROP_STATE_TYPE,
139   PROP_PARAMETER_TYPE
140 };
141
142 static void
143 psppire_dialog_action_set_property (GObject         *object,
144                                guint            prop_id,
145                                const GValue    *value,
146                                GParamSpec      *pspec)
147 {
148   PsppireDialogAction *act = PSPPIRE_DIALOG_ACTION (object);
149
150   switch (prop_id)
151     {
152     case PROP_TOPLEVEL:
153       {
154         GObject *p = g_value_get_object (value);
155         act->toplevel = GTK_WIDGET (p);
156       }
157       break;
158     default:
159       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
160       break;
161     };
162 }
163
164
165 static void
166 psppire_dialog_action_get_property (GObject    *object,
167                                guint            prop_id,
168                                GValue          *value,
169                                GParamSpec      *pspec)
170 {
171   PsppireDialogAction *dialog_action = PSPPIRE_DIALOG_ACTION (object);
172
173   switch (prop_id)
174     {
175     case PROP_TOPLEVEL:
176       g_value_take_object (value, dialog_action->toplevel);
177       break;
178     default:
179       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
180       break;
181     };
182 }
183
184
185
186 static void
187 on_destroy_dataset (GObject *w)
188 {
189   GHashTable *t = g_object_get_data (w, "thing-table");
190   GSList *dl = g_object_get_data (w, "widget-list");
191   
192   g_slist_free_full (dl, (GDestroyNotify) gtk_widget_destroy);
193   g_hash_table_unref (t);
194 }
195
196 /* Each toplevel widget - that is the data window, which generally has a 1-1 association
197    with a dataset - has an associated GHashTable.
198    
199    This GHashTable is keyed by the address of a PsppireDialogAction, and its values
200    are user determined pointers (typically a GtkBuilder*).
201
202    This is useful for storing the state of dialogs so they can persist between invocations.
203 */
204 GHashTable *
205 psppire_dialog_action_get_hash_table (PsppireDialogAction *act)
206 {
207   GHashTable *t = g_object_get_data (G_OBJECT (act->toplevel), "thing-table");
208   if (t == NULL)
209     {
210       t = g_hash_table_new_full (g_direct_hash, g_direct_equal, 0, g_object_unref);
211       g_object_set_data (G_OBJECT (act->toplevel), "thing-table", t);
212       g_object_set_data (G_OBJECT (act->toplevel), "widget-list", NULL);
213       g_signal_connect (act->toplevel, "destroy", G_CALLBACK (on_destroy_dataset), NULL);
214     }
215
216   return t;
217 }
218
219
220 static void
221 psppire_dialog_action_activate (PsppireDialogAction *act, GVariant *parameter)
222 {
223   gint response;
224
225   PsppireDialogActionClass *class = PSPPIRE_DIALOG_ACTION_GET_CLASS (act);
226
227   act->dict = PSPPIRE_DATA_WINDOW(act->toplevel)->dict;
228
229   GSList *wl = g_object_get_data (G_OBJECT (act->toplevel), "widget-list");
230   wl = g_slist_prepend (wl, act->dialog);
231   g_object_set_data (G_OBJECT (act->toplevel), "widget-list", wl);
232
233   if (class->activate)
234     class->activate (act, parameter);
235
236   gtk_window_set_transient_for (GTK_WINDOW (act->dialog),
237                                 GTK_WINDOW (act->toplevel));
238
239   if (act->source)
240     {
241       g_object_set (act->source, "model", act->dict, NULL);
242       gtk_widget_grab_focus (act->source);
243     }
244
245   if (!act->activated)
246     psppire_dialog_reload (PSPPIRE_DIALOG (act->dialog));
247
248   act->activated = TRUE;
249
250   response = psppire_dialog_run (PSPPIRE_DIALOG (act->dialog));
251
252   if ( class->generate_syntax )
253     {
254       switch (response)
255         {
256         case GTK_RESPONSE_OK:
257           g_free (execute_syntax_string (PSPPIRE_DATA_WINDOW (act->toplevel),
258                                          class->generate_syntax (act)));
259           break;
260         case PSPPIRE_RESPONSE_PASTE:
261           g_free (paste_syntax_to_window (class->generate_syntax (act)));
262           break;
263         default:
264           break;
265         }
266     }
267 }
268
269 static void
270 psppire_dialog_action_class_init (PsppireDialogActionClass *class)
271 {
272   GObjectClass *object_class = G_OBJECT_CLASS (class);
273
274   parent_class = g_type_class_peek_parent (class);
275
276   GParamSpec *toplevel_spec =
277     g_param_spec_object ("top-level",
278                          "Top Level",
279                          "The top level widget to which this dialog action belongs",
280                          GTK_TYPE_WINDOW,
281                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
282
283   object_class->set_property = psppire_dialog_action_set_property;
284   object_class->get_property = psppire_dialog_action_get_property;
285
286   class->generate_syntax = NULL;
287
288   class->activate = psppire_dialog_action_activate;
289
290   g_object_class_install_property (object_class,
291                                    PROP_TOPLEVEL,
292                                    toplevel_spec);
293
294   g_object_class_override_property (object_class, PROP_NAME, "name");
295   g_object_class_override_property (object_class, PROP_ENABLED, "enabled");
296   g_object_class_override_property (object_class, PROP_STATE, "state");
297   g_object_class_override_property (object_class, PROP_STATE_TYPE, "state-type");
298   g_object_class_override_property (object_class, PROP_PARAMETER_TYPE, "parameter-type");
299 }
300
301
302 static void
303 psppire_dialog_action_init (PsppireDialogAction *act)
304 {
305   act->toplevel = NULL;
306   act->dict = NULL;
307   act->activated = FALSE;
308   act->parameter_type = NULL;
309 }
310
311 void
312 psppire_dialog_action_set_valid_predicate (PsppireDialogAction *act, 
313                                            ContentsAreValid dialog_state_valid)
314 {
315   psppire_dialog_set_valid_predicate (PSPPIRE_DIALOG (act->dialog),
316                                       dialog_state_valid, act);
317 }
318
319 void
320 psppire_dialog_action_set_refresh (PsppireDialogAction *pda, 
321                                    PsppireDialogActionRefresh refresh)
322 {
323   g_signal_connect_swapped (pda->dialog, "refresh", G_CALLBACK (refresh),  pda);
324 }
325
326
327 void 
328 psppire_dialog_action_set_activation (gpointer class, activation activate)
329 {
330   PSPPIRE_DIALOG_ACTION_CLASS (class)->activate = (void (*)(PsppireDialogAction *, GVariant *)) activate;
331 }
332