gui: Add missing $(DESTDIR) to install rule.
[pspp-builds.git] / src / ui / gui / psppire-window.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2009, 2010, 2011  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 "psppire-window.h"
20
21 #include <gtk/gtk.h>
22
23 #include <stdlib.h>
24 #include <xalloc.h>
25
26 #include <gettext.h>
27 #define _(msgid) gettext (msgid)
28 #define N_(msgid) msgid
29
30 #include "data/any-reader.h"
31 #include "data/dataset.h"
32
33 #include "helper.h"
34 #include "psppire-conf.h"
35 #include "psppire-data-window.h"
36 #include "psppire-encoding-selector.h"
37 #include "psppire-syntax-window.h"
38 #include "psppire-window-register.h"
39 #include "psppire.h"
40
41 static void psppire_window_base_init     (PsppireWindowClass *class);
42 static void psppire_window_class_init    (PsppireWindowClass *class);
43 static void psppire_window_init          (PsppireWindow      *window);
44
45
46 static GObjectClass *parent_class;
47
48 GType
49 psppire_window_get_type (void)
50 {
51   static GType psppire_window_type = 0;
52
53   if (!psppire_window_type)
54     {
55       static const GTypeInfo psppire_window_info =
56       {
57         sizeof (PsppireWindowClass),
58         (GBaseInitFunc) psppire_window_base_init,
59         (GBaseFinalizeFunc) NULL,
60         (GClassInitFunc) psppire_window_class_init,
61         (GClassFinalizeFunc) NULL,
62         NULL,
63         sizeof (PsppireWindow),
64         0,
65         (GInstanceInitFunc) psppire_window_init,
66       };
67
68       psppire_window_type =
69         g_type_register_static (GTK_TYPE_WINDOW, "PsppireWindow",
70                                 &psppire_window_info, G_TYPE_FLAG_ABSTRACT);
71     }
72
73   return psppire_window_type;
74 }
75
76
77 /* Properties */
78 enum
79 {
80   PROP_0,
81   PROP_FILENAME,
82   PROP_DESCRIPTION,
83   PROP_ID
84 };
85
86
87 static void
88 psppire_window_set_title (PsppireWindow *window)
89 {
90   GString *title = g_string_sized_new (80);
91
92   if (window->dirty)
93     g_string_append_c (title, '*');
94
95   if (window->basename || window->id)
96     {
97       if (window->basename)
98         g_string_append_printf (title, "%s ", window->basename);
99
100       if (window->id != '\0')
101         g_string_append_printf (title, "[%s] ", window->id);
102
103       g_string_append_unichar (title, 0x2014); /* em dash */
104       g_string_append_c (title, ' '); /* em dash */
105     }
106
107   g_string_append_printf (title, "PSPPIRE %s", window->description);
108
109   gtk_window_set_title (GTK_WINDOW (window), title->str);
110
111   g_string_free (title, TRUE);
112 }
113
114 static void
115 psppire_window_update_list_name (PsppireWindow *window)
116 {
117   PsppireWindowRegister *reg = psppire_window_register_new ();
118   GString *candidate = g_string_sized_new (80);
119   int n;
120
121   n = 1;
122   do
123     {
124       /* Compose a name. */
125       g_string_truncate (candidate, 0);
126       if (window->filename)
127         {
128           gchar *display_filename = g_filename_display_name (window->filename);
129           g_string_append (candidate, display_filename);
130           g_free (display_filename);
131
132           if (window->id)
133             g_string_append_printf (candidate, " [%s]", window->id);
134         }
135       else if (window->id)
136         g_string_append_printf (candidate, "[%s]", window->id);
137       else
138         g_string_append (candidate, window->description);
139
140       if (n++ > 1)
141         g_string_append_printf (candidate, " #%d", n);
142
143       if (window->list_name && !strcmp (candidate->str, window->list_name))
144         {
145           /* Keep the existing name. */
146           g_string_free (candidate, TRUE);
147           return;
148         }
149     }
150   while (psppire_window_register_lookup (reg, candidate->str));
151
152   if (window->list_name)
153     psppire_window_register_remove (reg, window->list_name);
154
155   g_free (window->list_name);
156   window->list_name = g_string_free (candidate, FALSE);
157
158   psppire_window_register_insert (reg, window, window->list_name);
159 }
160
161 static void
162 psppire_window_name_changed (PsppireWindow *window)
163 {
164   psppire_window_set_title (window);
165   psppire_window_update_list_name (window);
166 }
167
168 static void
169 psppire_window_set_property (GObject         *object,
170                              guint            prop_id,
171                              const GValue    *value,
172                              GParamSpec      *pspec)
173 {
174   PsppireWindow *window = PSPPIRE_WINDOW (object);
175
176   switch (prop_id)
177     {
178     case PROP_DESCRIPTION:
179       g_free (window->description);
180       window->description = g_value_dup_string (value);
181       psppire_window_set_title (window);
182       break;
183     case PROP_FILENAME:
184       g_free (window->filename);
185       window->filename = g_value_dup_string (value);
186       g_free (window->basename);
187       window->basename = (window->filename
188                           ? g_filename_display_basename (window->filename)
189                           : NULL);
190       psppire_window_name_changed (window);
191       break;
192       break;
193     case PROP_ID:
194       g_free (window->id);
195       window->id = g_value_dup_string (value);
196       psppire_window_name_changed (window);
197       break;
198     default:
199       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
200       break;
201     };
202 }
203
204
205 static void
206 psppire_window_get_property (GObject         *object,
207                              guint            prop_id,
208                              GValue          *value,
209                              GParamSpec      *pspec)
210 {
211   PsppireWindow *window = PSPPIRE_WINDOW (object);
212
213   switch (prop_id)
214     {
215     case PROP_FILENAME:
216       g_value_set_string (value, window->filename);
217       break;
218     case PROP_DESCRIPTION:
219       g_value_set_string (value, window->description);
220       break;
221     case PROP_ID:
222       g_value_set_string (value, window->id);
223       break;
224     default:
225       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
226       break;
227     };
228 }
229
230
231 static void
232 on_realize (GtkWindow *window, gpointer data)
233 {
234   PsppireConf *conf = psppire_conf_new ();
235
236   const gchar *base = G_OBJECT_TYPE_NAME (window);
237
238   psppire_conf_set_window_geometry (conf, base, window);
239 }
240
241
242 static void
243 psppire_window_finalize (GObject *object)
244 {
245   PsppireWindow *window = PSPPIRE_WINDOW (object);
246
247   PsppireWindowRegister *reg = psppire_window_register_new ();
248
249   psppire_window_register_remove (reg, window->list_name);
250   g_free (window->filename);
251   g_free (window->basename);
252   g_free (window->id);
253   g_free (window->description);
254   g_free (window->list_name);
255
256   g_signal_handler_disconnect (psppire_window_register_new (),
257                                window->remove_handler);
258
259   g_signal_handler_disconnect (psppire_window_register_new (),
260                                window->insert_handler);
261
262   g_hash_table_destroy (window->menuitem_table);
263
264   if (G_OBJECT_CLASS (parent_class)->finalize)
265     G_OBJECT_CLASS (parent_class)->finalize (object);
266 }
267
268 static void
269 psppire_window_class_init (PsppireWindowClass *class)
270 {
271   GObjectClass *object_class = G_OBJECT_CLASS (class);
272
273   GParamSpec *description_spec =
274     null_if_empty_param ("description",
275                        "Description",
276                        "A string describing the usage of the window",
277                          NULL, /*Should be overridden by derived classes */
278                        G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
279
280   GParamSpec *filename_spec =
281     null_if_empty_param ("filename",
282                        "File name",
283                        "The name of the file associated with this window, if any",
284                          NULL,
285                          G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
286
287   GParamSpec *id_spec =
288     null_if_empty_param ("id",
289                          "Identifier",
290                          "The PSPP language identifier for the data associated "
291                          "with this window (e.g. dataset name)",
292                          NULL,
293                          G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
294
295   object_class->set_property = psppire_window_set_property;
296   object_class->get_property = psppire_window_get_property;
297
298   g_object_class_install_property (object_class,
299                                    PROP_DESCRIPTION,
300                                    description_spec);
301
302   g_object_class_install_property (object_class,
303                                    PROP_FILENAME,
304                                    filename_spec);
305
306   g_object_class_install_property (object_class,
307                                    PROP_ID,
308                                    id_spec);
309
310   parent_class = g_type_class_peek_parent (class);
311 }
312
313
314 static void
315 psppire_window_base_init (PsppireWindowClass *class)
316 {
317   GObjectClass *object_class = G_OBJECT_CLASS (class);
318
319   object_class->finalize = psppire_window_finalize;
320 }
321
322
323
324 static void
325 menu_toggled (GtkCheckMenuItem *mi, gpointer data)
326 {
327   /* Prohibit changes to the state */
328   mi->active = !mi->active;
329 }
330
331
332 /* Look up the window associated with this menuitem and present it to the user */
333 static void
334 menu_activate (GtkMenuItem *mi, gpointer data)
335 {
336   const gchar *key = data;
337
338   PsppireWindowRegister *reg = psppire_window_register_new ();
339
340   PsppireWindow *window = psppire_window_register_lookup (reg, key);
341
342   gtk_window_present (GTK_WINDOW (window));
343 }
344
345 static void
346 insert_menuitem_into_menu (PsppireWindow *window, gpointer key)
347 {
348   gchar *filename;
349   GtkWidget *item;
350
351   /* Add a separator before adding the first real item.  If we add a separator
352      at any other time, sometimes GtkUIManager removes it. */
353   if (g_hash_table_size (window->menuitem_table) == 0)
354     {
355       GtkWidget *separator = gtk_separator_menu_item_new ();
356       gtk_widget_show (separator);
357       gtk_menu_shell_append (window->menu, separator);
358     }
359
360   filename = g_filename_display_name (key);
361   item = gtk_check_menu_item_new_with_label (filename);
362   g_free (filename);
363
364   g_signal_connect (item, "toggled", G_CALLBACK (menu_toggled), NULL);
365   g_signal_connect (item, "activate", G_CALLBACK (menu_activate), key);
366
367   gtk_widget_show (item);
368
369   gtk_menu_shell_append (window->menu, item);
370
371   /* Set the state without emitting a signal */
372   GTK_CHECK_MENU_ITEM (item)->active =
373    (psppire_window_register_lookup (psppire_window_register_new (), key) == window);
374
375   g_hash_table_insert (window->menuitem_table, key, item);
376 }
377
378 static void
379 insert_item (gpointer key, gpointer value, gpointer data)
380 {
381   PsppireWindow *window = PSPPIRE_WINDOW (data);
382
383   if ( NULL != g_hash_table_lookup (window->menuitem_table, key))
384     return;
385
386   insert_menuitem_into_menu (window, key);
387 }
388
389 /* Insert a new item into the window menu */
390 static void
391 insert_menuitem (GObject *reg, const gchar *key, gpointer data)
392 {
393   PsppireWindow *window = PSPPIRE_WINDOW (data);
394   
395   insert_menuitem_into_menu (window, (gpointer) key);
396 }
397
398
399 static void
400 remove_menuitem (PsppireWindowRegister *reg, const gchar *key, gpointer data)
401 {
402   PsppireWindow *window = PSPPIRE_WINDOW (data);
403   GtkWidget *item ;
404
405   item = g_hash_table_lookup (window->menuitem_table, key);
406
407   g_hash_table_remove (window->menuitem_table, key);
408
409   if (GTK_IS_CONTAINER (window->menu))
410     gtk_container_remove (GTK_CONTAINER (window->menu), item);
411 }
412
413 static void
414 insert_existing_items (PsppireWindow *window)
415 {
416   psppire_window_register_foreach (psppire_window_register_new (), insert_item, window);
417 }
418
419
420 static gboolean
421 on_delete (PsppireWindow *w, GdkEvent *event, gpointer user_data)
422 {
423   PsppireWindowRegister *reg = psppire_window_register_new ();
424
425   const gchar *base = G_OBJECT_TYPE_NAME (w);
426
427   PsppireConf *conf = psppire_conf_new ();
428
429   psppire_conf_save_window_geometry (conf, base, GTK_WINDOW (w));
430
431
432   if ( w->dirty )
433     {
434       gint response = psppire_window_query_save (w);
435
436       switch (response)
437         {
438         default:
439         case GTK_RESPONSE_CANCEL:
440           return TRUE;
441           break;
442         case GTK_RESPONSE_APPLY:
443           psppire_window_save (w);
444           if (w->dirty)
445             {
446               /* Save failed, or user exited Save As dialog with Cancel. */
447               return TRUE;
448             }
449           break;
450         case GTK_RESPONSE_REJECT:
451           break;
452         }
453     }
454
455   if ( 1 == psppire_window_register_n_items (reg))
456     gtk_main_quit ();
457
458   return FALSE;
459 }
460
461
462 static void
463 psppire_window_init (PsppireWindow *window)
464 {
465   window->menu = NULL;
466   window->filename = NULL;
467   window->basename = NULL;
468   window->id = NULL;
469   window->description = NULL;
470   window->list_name = NULL;
471
472   window->menuitem_table  = g_hash_table_new (g_str_hash, g_str_equal);
473
474
475   g_signal_connect (window,  "realize", G_CALLBACK (insert_existing_items), NULL);
476
477   window->insert_handler = g_signal_connect (psppire_window_register_new (),
478                                              "inserted",
479                                              G_CALLBACK (insert_menuitem),
480                                              window);
481
482   window->remove_handler = g_signal_connect (psppire_window_register_new (),
483                                              "removed",
484                                              G_CALLBACK (remove_menuitem),
485                                              window);
486
487   window->dirty = FALSE;
488
489   g_signal_connect_swapped (window, "delete-event", G_CALLBACK (on_delete), window);
490
491   g_object_set (window, "icon-name", "psppicon", NULL);
492
493   g_signal_connect (window, "realize",
494                     G_CALLBACK (on_realize), window);
495 }
496
497 /*
498    Ask the user if the buffer should be saved.
499    Return the response.
500 */
501 gint
502 psppire_window_query_save (PsppireWindow *se)
503 {
504   gint response;
505   GtkWidget *dialog;
506   GtkWidget *cancel_button;
507
508   gchar *description;
509
510   GTimeVal time;
511
512   g_get_current_time (&time);
513
514   if (se->filename)
515     description = g_filename_display_basename (se->filename);
516   else if (se->id)
517     description = g_strdup (se->id);
518   else
519     description = g_strdup (se->description);
520   dialog =
521     gtk_message_dialog_new (GTK_WINDOW (se),
522                             GTK_DIALOG_MODAL,
523                             GTK_MESSAGE_WARNING,
524                             GTK_BUTTONS_NONE,
525                             _("Save the changes to `%s' before closing?"),
526                             description);
527   g_free (description);
528
529   g_object_set (dialog, "icon-name", "psppicon", NULL);
530
531   gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
532                                             _("If you don't save, changes from the last %ld seconds will be permanently lost."),
533                                             time.tv_sec - se->savetime.tv_sec);
534
535   gtk_dialog_add_button  (GTK_DIALOG (dialog),
536                           _("Close _without saving"),
537                           GTK_RESPONSE_REJECT);
538
539   cancel_button = gtk_dialog_add_button  (GTK_DIALOG (dialog),
540                                           GTK_STOCK_CANCEL,
541                                           GTK_RESPONSE_CANCEL);
542
543   gtk_dialog_add_button  (GTK_DIALOG (dialog),
544                           GTK_STOCK_SAVE,
545                           GTK_RESPONSE_APPLY);
546
547   gtk_widget_grab_focus (cancel_button);
548
549   response = gtk_dialog_run (GTK_DIALOG (dialog));
550
551   gtk_widget_destroy (dialog);
552
553   return response;
554 }
555
556
557 /* The return value is encoded in the glib filename encoding. */
558 const gchar *
559 psppire_window_get_filename (PsppireWindow *w)
560 {
561   return w->filename;
562 }
563
564
565 /* FILENAME must be encoded in the glib filename encoding. */
566 void
567 psppire_window_set_filename (PsppireWindow *w, const gchar *filename)
568 {
569   g_object_set (w, "filename", filename, NULL);
570 }
571
572 void
573 psppire_window_set_unsaved (PsppireWindow *w)
574 {
575   if ( w->dirty == FALSE)
576     g_get_current_time (&w->savetime);
577
578   w->dirty = TRUE;
579
580   psppire_window_set_title (w);
581 }
582
583 gboolean
584 psppire_window_get_unsaved (PsppireWindow *w)
585 {
586   return w->dirty;
587 }
588
589
590 \f
591
592
593 static void
594 minimise_window (gpointer key, gpointer value, gpointer data)
595 {
596   gtk_window_iconify (GTK_WINDOW (value));
597 }
598
599
600 void
601 psppire_window_minimise_all (void)
602 {
603   PsppireWindowRegister *reg = psppire_window_register_new ();
604
605   g_hash_table_foreach (reg->name_table, minimise_window, NULL);
606 }
607
608
609 \f
610
611 GType
612 psppire_window_model_get_type (void)
613 {
614   static GType window_model_type = 0;
615
616   if (! window_model_type)
617     {
618       static const GTypeInfo window_model_info =
619       {
620         sizeof (PsppireWindowIface), /* class_size */
621         NULL,           /* base_init */
622         NULL,           /* base_finalize */
623         NULL,
624         NULL,           /* class_finalize */
625         NULL,           /* class_data */
626         0,
627         0,              /* n_preallocs */
628         NULL
629       };
630
631       window_model_type =
632         g_type_register_static (G_TYPE_INTERFACE, "PsppireWindowModel",
633                                 &window_model_info, 0);
634
635       g_type_interface_add_prerequisite (window_model_type, G_TYPE_OBJECT);
636     }
637
638   return window_model_type;
639 }
640
641
642 void
643 psppire_window_save (PsppireWindow *w)
644 {
645   PsppireWindowIface *i = PSPPIRE_WINDOW_MODEL_GET_IFACE (w);
646
647   g_assert (i);
648   g_return_if_fail (i->save);
649
650   if (w->filename == NULL)
651     psppire_window_save_as (w);
652   else
653     {
654       i->save (w);
655       w->dirty = FALSE;
656       psppire_window_set_title (w);
657     }
658 }
659
660 void
661 psppire_window_save_as (PsppireWindow *w)
662 {
663   PsppireWindowIface *i = PSPPIRE_WINDOW_MODEL_GET_IFACE (w);
664   gchar *old_filename;
665
666   g_assert (i);
667   g_return_if_fail (i->pick_filename);
668
669   old_filename = w->filename;
670   w->filename = NULL;
671
672   i->pick_filename (w);
673   if (w->filename == NULL)
674     w->filename = old_filename;
675   else
676     {
677       g_free (old_filename);
678       psppire_window_save (w);
679     }
680 }
681
682 static void delete_recent (const char *file_name);
683
684 gboolean
685 psppire_window_load (PsppireWindow *w, const gchar *file)
686 {
687   gboolean ok;
688   PsppireWindowIface *i = PSPPIRE_WINDOW_MODEL_GET_IFACE (w);
689
690   g_assert (PSPPIRE_IS_WINDOW_MODEL (w));
691
692   g_assert (i);
693
694   g_return_val_if_fail (i->load, FALSE);
695
696   ok = i->load (w, file);
697
698   if ( ok )
699     {
700       psppire_window_set_filename (w, file);
701       w->dirty = FALSE;
702     }
703   else
704     delete_recent (file);
705
706   return ok;
707 }
708
709 GtkWidget *
710 psppire_window_file_chooser_dialog (PsppireWindow *toplevel)
711 {
712   GtkFileFilter *filter = gtk_file_filter_new ();
713   GtkWidget *dialog =
714     gtk_file_chooser_dialog_new (_("Open"),
715                                  GTK_WINDOW (toplevel),
716                                  GTK_FILE_CHOOSER_ACTION_OPEN,
717                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
718                                  GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
719                                  NULL);
720
721   g_object_set (dialog, "local-only", FALSE, NULL);
722
723   gtk_file_filter_set_name (filter, _("Data and Syntax Files"));
724   gtk_file_filter_add_pattern (filter, "*.sav");
725   gtk_file_filter_add_pattern (filter, "*.SAV");
726   gtk_file_filter_add_pattern (filter, "*.por");
727   gtk_file_filter_add_pattern (filter, "*.POR");
728   gtk_file_filter_add_pattern (filter, "*.sps");
729   gtk_file_filter_add_pattern (filter, "*.SPS");
730   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
731
732   filter = gtk_file_filter_new ();
733   gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
734   gtk_file_filter_add_pattern (filter, "*.sav");
735   gtk_file_filter_add_pattern (filter, "*.SAV");
736   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
737
738   filter = gtk_file_filter_new ();
739   gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
740   gtk_file_filter_add_pattern (filter, "*.por");
741   gtk_file_filter_add_pattern (filter, "*.POR");
742   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
743
744   filter = gtk_file_filter_new ();
745   gtk_file_filter_set_name (filter, _("Syntax Files (*.sps) "));
746   gtk_file_filter_add_pattern (filter, "*.sps");
747   gtk_file_filter_add_pattern (filter, "*.SPS");
748   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
749
750   filter = gtk_file_filter_new ();
751   gtk_file_filter_set_name (filter, _("All Files"));
752   gtk_file_filter_add_pattern (filter, "*");
753   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
754
755   if (toplevel->filename)
756     {
757       const gchar *filename = toplevel->filename;
758       gchar *dir_name;
759
760       if ( ! g_path_is_absolute (filename))
761         {
762           gchar *path =
763             g_build_filename (g_get_current_dir (), filename, NULL);
764           dir_name = g_path_get_dirname (path);
765           g_free (path);
766         }
767       else
768         {
769           dir_name = g_path_get_dirname (filename);
770         }
771       gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog),
772                                            dir_name);
773       free (dir_name);
774     }
775
776   gtk_file_chooser_set_extra_widget (
777     GTK_FILE_CHOOSER (dialog), psppire_encoding_selector_new ("Auto", true));
778
779   return dialog;
780 }
781
782 /* Callback for the file_open action.
783    Prompts for a filename and opens it */
784 void
785 psppire_window_open (PsppireWindow *de)
786 {
787   GtkWidget *dialog = psppire_window_file_chooser_dialog (de);
788
789   switch (gtk_dialog_run (GTK_DIALOG (dialog)))
790     {
791     case GTK_RESPONSE_ACCEPT:
792       {
793         gchar *name =
794           gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
795
796         gchar *sysname = convert_glib_filename_to_system_filename (name, NULL);
797
798         gchar *encoding = psppire_encoding_selector_get_encoding (
799           gtk_file_chooser_get_extra_widget (GTK_FILE_CHOOSER (dialog)));
800
801         if (any_reader_may_open (sysname))
802           open_data_window (de, name);
803         else
804           open_syntax_window (name, encoding);
805
806         g_free (encoding);
807         g_free (sysname);
808         g_free (name);
809       }
810       break;
811     default:
812       break;
813     }
814
815   gtk_widget_destroy (dialog);
816 }
817
818
819 /* Puts FILE_NAME (encoded in the glib file name encoding) into the recent list
820    with associated MIME_TYPE.  If it's already in the list, it moves it to the
821    top. */
822 void
823 add_most_recent (const char *file_name, const char *mime_type)
824 {
825   gchar *uri = g_filename_to_uri  (file_name, NULL, NULL);
826
827   if ( uri )
828     {
829       GtkRecentData recent_data;
830
831       recent_data.display_name = NULL;
832       recent_data.description = NULL;
833       recent_data.mime_type = CONST_CAST (gchar *, mime_type);
834       recent_data.app_name = CONST_CAST (gchar *, g_get_application_name ());
835       recent_data.app_exec = g_strjoin (" ", g_get_prgname (), "%u", NULL);
836       recent_data.groups = NULL;
837       recent_data.is_private = FALSE;
838
839       gtk_recent_manager_add_full (gtk_recent_manager_get_default (),
840                                    uri, &recent_data);
841
842       g_free (recent_data.app_exec);
843     }
844
845   g_free (uri);
846 }
847
848
849
850 /*
851    If FILE_NAME exists in the recent list, then  delete it.
852  */
853 static void
854 delete_recent (const char *file_name)
855 {
856   gchar *uri = g_filename_to_uri  (file_name, NULL, NULL);
857
858   if ( uri )
859     gtk_recent_manager_remove_item (gtk_recent_manager_get_default (), uri, NULL);
860
861   g_free (uri);
862 }
863