Merge commit 'HEAD'; commit 'master/master'
[pspp] / src / ui / gui / psppire-window.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2008  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 #include <config.h>
18
19 #include <gtk/gtksignal.h>
20 #include <gtk/gtkwindow.h>
21 #include <gtk/gtkcheckmenuitem.h>
22
23 #include <stdlib.h>
24
25 #include <gettext.h>
26 #define _(msgid) gettext (msgid)
27 #define N_(msgid) msgid
28
29 #include "psppire-window.h"
30 #include "psppire-window-register.h"
31
32 static void psppire_window_base_finalize (PsppireWindowClass *, gpointer);
33 static void psppire_window_base_init     (PsppireWindowClass *class);
34 static void psppire_window_class_init    (PsppireWindowClass *class);
35 static void psppire_window_init          (PsppireWindow      *window);
36
37
38 static PsppireWindowClass *the_class;
39 static GObjectClass *parent_class;
40
41 GType
42 psppire_window_get_type (void)
43 {
44   static GType psppire_window_type = 0;
45
46   if (!psppire_window_type)
47     {
48       static const GTypeInfo psppire_window_info =
49       {
50         sizeof (PsppireWindowClass),
51         (GBaseInitFunc) psppire_window_base_init,
52         (GBaseFinalizeFunc) psppire_window_base_finalize,
53         (GClassInitFunc) psppire_window_class_init,
54         (GClassFinalizeFunc) NULL,
55         NULL,
56         sizeof (PsppireWindow),
57         0,
58         (GInstanceInitFunc) psppire_window_init,
59       };
60
61       psppire_window_type =
62         g_type_register_static (GTK_TYPE_WINDOW, "PsppireWindow",
63                                 &psppire_window_info, 0);
64     }
65
66   return psppire_window_type;
67 }
68
69
70 /* Properties */
71 enum
72 {
73   PROP_0,
74   PROP_FILENAME,
75   PROP_USAGE
76 };
77
78
79 gchar *
80 uniquify (const gchar *str, int *x)
81 {
82   return g_strdup_printf ("%s%d", str, (*x)++);
83 }
84
85
86
87 static void
88 psppire_window_set_property (GObject         *object,
89                              guint            prop_id,
90                              const GValue    *value,
91                              GParamSpec      *pspec)
92 {
93   PsppireWindow *window = PSPPIRE_WINDOW (object);
94
95   switch (prop_id)
96     {
97     case PROP_USAGE:
98       window->usage = g_value_get_enum (value);
99       break;
100     case PROP_FILENAME:
101       {
102         PsppireWindowRegister *reg = psppire_window_register_new ();
103         gchar mdash[6] = {0,0,0,0,0,0};
104         gchar *basename, *title;
105         const gchar *name = g_value_get_string (value);
106         int x = 0;
107         gchar *candidate_name ;
108         GValue def = {0};
109         g_value_init (&def, pspec->value_type);
110
111         if ( NULL == name)
112           {
113             g_param_value_set_default (pspec, &def);
114             name = g_value_get_string (&def);
115           }
116         
117         candidate_name = strdup (name);
118
119         while ( psppire_window_register_lookup (reg, candidate_name))
120           {
121             free (candidate_name);
122             candidate_name = uniquify (name, &x);
123           }
124
125         basename = g_path_get_basename (candidate_name);
126         g_unichar_to_utf8 (0x2014, mdash);
127
128         g_value_unset (&def);
129
130         switch (window->usage)
131           {
132           case PSPPIRE_WINDOW_USAGE_SYNTAX:
133             title = g_strdup_printf ( _("%s %s PSPPIRE Syntax Editor"),
134                                       basename, mdash);
135             break;
136           case PSPPIRE_WINDOW_USAGE_OUTPUT:
137             title = g_strdup_printf ( _("%s %s PSPPIRE Output"),
138                                       basename, mdash);
139           case PSPPIRE_WINDOW_USAGE_DATA:
140             title = g_strdup_printf ( _("%s %s PSPPIRE Data Editor"),
141                                       basename, mdash);
142             break;
143           default:
144             g_assert_not_reached ();
145             break;
146           }
147
148         gtk_window_set_title (GTK_WINDOW (window), title);
149
150         if ( window->name)
151           psppire_window_register_remove (reg, window->name);
152
153         free (window->name);
154         window->name = candidate_name;
155
156         psppire_window_register_insert (reg, window, window->name);
157
158         free (basename);
159         free (title);
160       }
161       break;
162     default:
163       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
164       break;
165     };
166 }
167
168
169 static void
170 psppire_window_get_property (GObject         *object,
171                              guint            prop_id,
172                              GValue          *value,
173                              GParamSpec      *pspec)
174 {
175   PsppireWindow *window = PSPPIRE_WINDOW (object);
176
177   switch (prop_id)
178     {
179     case PROP_USAGE:
180       g_value_set_enum (value, window->usage);
181       break;
182     case PROP_FILENAME:
183       g_value_set_string (value, window->name);
184       break;
185     default:
186       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
187       break;
188     };
189 }
190
191
192
193 static void
194 psppire_window_finalize (GObject *object)
195 {
196   PsppireWindow *window = PSPPIRE_WINDOW (object);
197   
198   PsppireWindowRegister *reg = psppire_window_register_new ();
199
200   psppire_window_register_remove (reg, window->name);
201   free (window->name);
202
203   g_signal_handler_disconnect (psppire_window_register_new (),
204                                window->remove_handler);
205
206   g_signal_handler_disconnect (psppire_window_register_new (),
207                                window->insert_handler);
208
209   g_hash_table_destroy (window->menuitem_table);
210
211   if (G_OBJECT_CLASS (parent_class)->finalize)
212     G_OBJECT_CLASS (parent_class)->finalize (object);
213 }
214
215
216 static void
217 psppire_window_class_init (PsppireWindowClass *class)
218 {
219   GObjectClass *object_class = G_OBJECT_CLASS (class);
220
221   GParamSpec *use_class_spec =
222     g_param_spec_enum ("usage",
223                        "Usage",
224                        "What the window is used for",
225                        G_TYPE_PSPPIRE_WINDOW_USAGE,
226                        PSPPIRE_WINDOW_USAGE_SYNTAX /* default value */,
227                        G_PARAM_CONSTRUCT_ONLY |G_PARAM_READABLE | G_PARAM_WRITABLE);
228
229
230   GParamSpec *filename_spec =
231     g_param_spec_string ("filename",
232                        "File name",
233                        "The name of the file associated with this window, if any",
234                          "Untitled",
235                          G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT
236                          );
237
238
239   object_class->set_property = psppire_window_set_property;
240   object_class->get_property = psppire_window_get_property;
241
242   g_object_class_install_property (object_class,
243                                    PROP_FILENAME,
244                                    filename_spec);
245
246   g_object_class_install_property (object_class,
247                                    PROP_USAGE,
248                                    use_class_spec);
249
250
251   the_class = class;
252   parent_class = g_type_class_peek_parent (class);
253 }
254
255
256 static void
257 psppire_window_base_init (PsppireWindowClass *class)
258 {
259   GObjectClass *object_class = G_OBJECT_CLASS (class);
260
261   object_class->finalize = psppire_window_finalize;
262 }
263
264
265
266 static void
267 psppire_window_base_finalize (PsppireWindowClass *class,
268                                 gpointer class_data)
269 {
270 }
271
272 static void
273 menu_toggled (GtkCheckMenuItem *mi, gpointer data)
274 {
275   /* Prohibit changes to the state */
276   mi->active = !mi->active;
277 }
278
279
280 /* Look up the window associated with this menuitem and present it to the user */
281 static void
282 menu_activate (GtkMenuItem *mi, gpointer data)
283 {
284   const gchar *key = data;
285
286   PsppireWindowRegister *reg = psppire_window_register_new ();
287
288   PsppireWindow *window = psppire_window_register_lookup (reg, key);
289
290   gtk_window_present (GTK_WINDOW (window));
291 }
292
293 static void
294 insert_menuitem_into_menu (PsppireWindow *window, gpointer key)
295 {
296   GtkWidget *item = gtk_check_menu_item_new_with_label (key);
297
298   g_signal_connect (item, "toggled", G_CALLBACK (menu_toggled), NULL);
299   g_signal_connect (item, "activate", G_CALLBACK (menu_activate), key);
300
301   gtk_widget_show (item);
302   
303   gtk_menu_shell_append (window->menu, item);
304
305   /* Set the state without emitting a signal */
306   GTK_CHECK_MENU_ITEM (item)->active =
307    (psppire_window_register_lookup (psppire_window_register_new (), key) == window);
308
309   g_hash_table_insert (window->menuitem_table, key, item);
310 }
311
312 static void
313 insert_item (gpointer key, gpointer value, gpointer data)
314 {
315   PsppireWindow *window = PSPPIRE_WINDOW (data);
316
317   if ( NULL != g_hash_table_lookup (window->menuitem_table, key))
318     return;
319
320   insert_menuitem_into_menu (window, key);
321 }
322
323 /* Insert a new item into the window menu */
324 static void
325 insert_menuitem (GObject *reg, const gchar *key, gpointer data)
326 {
327   PsppireWindow *window = PSPPIRE_WINDOW (data);
328   
329   insert_menuitem_into_menu (window, (gpointer) key);
330 }
331
332
333 static void
334 remove_menuitem (PsppireWindowRegister *reg, const gchar *key, gpointer data)
335 {
336   PsppireWindow *window = PSPPIRE_WINDOW (data);
337   GtkWidget *item ;
338
339   item = g_hash_table_lookup (window->menuitem_table, key);
340
341   g_hash_table_remove (window->menuitem_table, key);
342
343   if (GTK_IS_CONTAINER (window->menu))
344     gtk_container_remove (GTK_CONTAINER (window->menu), item);
345 }
346
347 static void
348 insert_existing_items (PsppireWindow *window)
349 {
350   psppire_window_register_foreach (psppire_window_register_new (), insert_item, window);
351 }
352
353 static void
354 psppire_window_init (PsppireWindow *window)
355 {
356   window->name = NULL;
357   window->menu = NULL;
358
359   window->menuitem_table  = g_hash_table_new (g_str_hash, g_str_equal);
360
361
362   g_signal_connect (window,  "realize", G_CALLBACK (insert_existing_items), NULL);
363
364   window->insert_handler = g_signal_connect (psppire_window_register_new (),
365                                              "inserted",
366                                              G_CALLBACK (insert_menuitem),
367                                              window);
368
369   window->remove_handler = g_signal_connect (psppire_window_register_new (),
370                                              "removed",
371                                              G_CALLBACK (remove_menuitem),
372                                              window);
373 }
374
375
376 GtkWidget*
377 psppire_window_new (PsppireWindowUsage usage)
378 {
379   return GTK_WIDGET (g_object_new (psppire_window_get_type (),
380                                    "type", GTK_WINDOW_TOPLEVEL,
381                                    "usage", usage,
382                                    NULL));
383 }
384
385
386 const gchar *
387 psppire_window_get_filename (PsppireWindow *w)
388 {
389   const gchar *name = NULL;
390   g_object_get (w, "filename", &name, NULL);
391   return name;
392 }
393
394
395 void
396 psppire_window_set_filename (PsppireWindow *w, const gchar *filename)
397 {
398   g_object_set (w, "filename", filename, NULL);
399 }
400
401 \f
402
403 GType
404 psppire_window_usage_get_type (void)
405 {
406   static GType etype = 0;
407   if (etype == 0)
408     {
409       static const GEnumValue values[] = {
410         { PSPPIRE_WINDOW_USAGE_SYNTAX, "PSPPIRE_WINDOW_USAGE_SYNTAX",
411           "Syntax" },
412
413         { PSPPIRE_WINDOW_USAGE_OUTPUT, "PSPPIRE_WINDOW_USAGE_OUTPUT",
414           "Output" },
415
416         { PSPPIRE_WINDOW_USAGE_DATA,   "PSPPIRE_WINDOW_USAGE_DATA",
417           "Data" },
418
419         { 0, NULL, NULL }
420       };
421
422       etype = g_enum_register_static (g_intern_static_string ("PsppireWindowUsage"),
423                                       values);
424     }
425
426   return etype;
427 }
428
429
430
431 static void
432 minimise_window (gpointer key, gpointer value, gpointer data)
433 {
434   gtk_window_iconify (GTK_WINDOW (value));
435 }
436
437
438 void
439 psppire_window_minimise_all (void)
440 {
441   PsppireWindowRegister *reg = psppire_window_register_new ();
442
443   g_hash_table_foreach (reg->name_table, minimise_window, NULL);
444 }