lexer: Reimplement for better testability and internationalization.
[pspp-builds.git] / src / ui / gui / psppire-syntax-window.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2008, 2009, 2010  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/gtkbox.h>
21 #include "executor.h"
22 #include "helper.h"
23
24 #include <language/lexer/lexer.h>
25 #include <libpspp/message.h>
26 #include <stdlib.h>
27
28 #include "help-menu.h"
29 #include "psppire.h"
30 #include "psppire-data-window.h"
31 #include "psppire-window-register.h"
32 #include "psppire-syntax-window.h"
33
34 #include "xalloc.h"
35
36 #include <gettext.h>
37 #define _(msgid) gettext (msgid)
38 #define N_(msgid) msgid
39
40 static void psppire_syntax_window_base_finalize (PsppireSyntaxWindowClass *, gpointer);
41 static void psppire_syntax_window_base_init     (PsppireSyntaxWindowClass *class);
42 static void psppire_syntax_window_class_init    (PsppireSyntaxWindowClass *class);
43 static void psppire_syntax_window_init          (PsppireSyntaxWindow      *syntax_editor);
44
45
46 static void psppire_syntax_window_iface_init (PsppireWindowIface *iface);
47
48
49 GType
50 psppire_syntax_window_get_type (void)
51 {
52   static GType psppire_syntax_window_type = 0;
53
54   if (!psppire_syntax_window_type)
55     {
56       static const GTypeInfo psppire_syntax_window_info =
57       {
58         sizeof (PsppireSyntaxWindowClass),
59         (GBaseInitFunc) psppire_syntax_window_base_init,
60         (GBaseFinalizeFunc) psppire_syntax_window_base_finalize,
61         (GClassInitFunc)psppire_syntax_window_class_init,
62         (GClassFinalizeFunc) NULL,
63         NULL,
64         sizeof (PsppireSyntaxWindow),
65         0,
66         (GInstanceInitFunc) psppire_syntax_window_init,
67       };
68
69       static const GInterfaceInfo window_interface_info =
70         {
71           (GInterfaceInitFunc) psppire_syntax_window_iface_init,
72           NULL,
73           NULL
74         };
75
76       psppire_syntax_window_type =
77         g_type_register_static (PSPPIRE_TYPE_WINDOW, "PsppireSyntaxWindow",
78                                 &psppire_syntax_window_info, 0);
79
80       g_type_add_interface_static (psppire_syntax_window_type,
81                                    PSPPIRE_TYPE_WINDOW_MODEL,
82                                    &window_interface_info);
83     }
84
85   return psppire_syntax_window_type;
86 }
87
88 static GObjectClass *parent_class ;
89
90 static void
91 psppire_syntax_window_finalize (GObject *object)
92 {
93   if (G_OBJECT_CLASS (parent_class)->finalize)
94     (*G_OBJECT_CLASS (parent_class)->finalize) (object);
95 }
96
97
98 static void
99 psppire_syntax_window_dispose (GObject *obj)
100 {
101   PsppireSyntaxWindow *sw = (PsppireSyntaxWindow *)obj;
102
103   GtkClipboard *clip_selection;
104   GtkClipboard *clip_primary;
105
106   if (sw->dispose_has_run)
107     return;
108
109   clip_selection = gtk_widget_get_clipboard (GTK_WIDGET (sw), GDK_SELECTION_CLIPBOARD);
110   clip_primary =   gtk_widget_get_clipboard (GTK_WIDGET (sw), GDK_SELECTION_PRIMARY);
111
112   g_signal_handler_disconnect (clip_primary, sw->sel_handler);
113
114   g_signal_handler_disconnect (clip_selection, sw->ps_handler);
115
116   /* Make sure dispose does not run twice. */
117   sw->dispose_has_run = TRUE;
118
119   /* Chain up to the parent class */
120   G_OBJECT_CLASS (parent_class)->dispose (obj);
121 }
122
123
124
125 static void
126 psppire_syntax_window_class_init (PsppireSyntaxWindowClass *class)
127 {
128   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
129
130   parent_class = g_type_class_peek_parent (class);
131
132   gobject_class->dispose = psppire_syntax_window_dispose;
133 }
134
135
136 static void
137 psppire_syntax_window_base_init (PsppireSyntaxWindowClass *class)
138 {
139   GObjectClass *object_class = G_OBJECT_CLASS (class);
140
141   object_class->finalize = psppire_syntax_window_finalize;
142 }
143
144
145
146 static void
147 psppire_syntax_window_base_finalize (PsppireSyntaxWindowClass *class,
148                                      gpointer class_data)
149 {
150 }
151
152
153 static void
154 editor_execute_syntax (const PsppireSyntaxWindow *sw, GtkTextIter start,
155                        GtkTextIter stop)
156 {
157   PsppireWindow *win = PSPPIRE_WINDOW (sw);
158   struct lex_reader *reader;
159   gchar *text;
160
161   text = gtk_text_buffer_get_text (sw->buffer, &start, &stop, FALSE);
162   reader = lex_reader_for_string (text);
163   g_free (text);
164
165   lex_reader_set_file_name (reader, psppire_window_get_filename (win));
166
167   execute_syntax (reader);
168 }
169
170
171 \f
172
173 /* Delete the currently selected text */
174 static void
175 on_edit_delete (PsppireSyntaxWindow *sw)
176 {
177   GtkTextIter begin, end;
178   
179   if ( gtk_text_buffer_get_selection_bounds (sw->buffer, &begin, &end) )
180     gtk_text_buffer_delete (sw->buffer, &begin, &end);
181 }
182
183
184
185
186 /* The syntax editor's clipboard deals only with text */
187 enum {
188   SELECT_FMT_NULL,
189   SELECT_FMT_TEXT,
190 };
191
192
193 static void
194 selection_changed (PsppireSyntaxWindow *sw)
195 {
196   gboolean sel = gtk_text_buffer_get_has_selection (sw->buffer);
197
198   gtk_action_set_sensitive (sw->edit_copy, sel);
199   gtk_action_set_sensitive (sw->edit_cut, sel);
200   gtk_action_set_sensitive (sw->edit_delete, sel);
201 }
202
203 /* The callback which runs when something request clipboard data */
204 static void
205 clipboard_get_cb (GtkClipboard     *clipboard,
206                   GtkSelectionData *selection_data,
207                   guint             info,
208                   gpointer          data)
209 {
210   PsppireSyntaxWindow *sw = data;
211   g_assert (info == SELECT_FMT_TEXT);
212
213   gtk_selection_data_set (selection_data, selection_data->target,
214                           8,
215                           (const guchar *) sw->cliptext, strlen (sw->cliptext));
216
217 }
218
219 static void
220 clipboard_clear_cb (GtkClipboard *clipboard,
221                     gpointer data)
222 {
223   PsppireSyntaxWindow *sw = data;
224   g_free (sw->cliptext);
225   sw->cliptext = NULL;
226 }
227
228
229 static const GtkTargetEntry targets[] = {
230   { "UTF8_STRING",   0, SELECT_FMT_TEXT },
231   { "STRING",        0, SELECT_FMT_TEXT },
232   { "TEXT",          0, SELECT_FMT_TEXT },
233   { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
234   { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
235   { "text/plain",    0, SELECT_FMT_TEXT },
236 };
237
238
239 /*
240   Store a clip containing the currently selected text.
241   Returns true iff something was set.
242   As a side effect, begin and end will be set to indicate
243   the limits of the selected text.
244 */
245 static gboolean
246 set_clip (PsppireSyntaxWindow *sw, GtkTextIter *begin, GtkTextIter *end)
247 {
248   GtkClipboard *clipboard ;
249
250   if ( ! gtk_text_buffer_get_selection_bounds (sw->buffer, begin, end) )
251     return FALSE;
252
253   g_free (sw->cliptext);
254   sw->cliptext = gtk_text_buffer_get_text  (sw->buffer, begin, end, FALSE);
255
256   clipboard =
257     gtk_widget_get_clipboard (GTK_WIDGET (sw), GDK_SELECTION_CLIPBOARD);
258
259   if (!gtk_clipboard_set_with_owner (clipboard, targets,
260                                      G_N_ELEMENTS (targets),
261                                      clipboard_get_cb, clipboard_clear_cb,
262                                      G_OBJECT (sw)))
263     clipboard_clear_cb (clipboard, sw);
264
265   return TRUE;
266 }
267
268 static void
269 on_edit_cut (PsppireSyntaxWindow *sw)
270 {
271   GtkTextIter begin, end;
272   
273   if ( set_clip (sw, &begin, &end))
274     gtk_text_buffer_delete (sw->buffer, &begin, &end);
275 }
276
277 static void
278 on_edit_copy (PsppireSyntaxWindow *sw)
279 {
280   GtkTextIter begin, end;
281
282   set_clip (sw, &begin, &end);
283 }
284
285
286 /* A callback for when the clipboard contents have been received */
287 static void
288 contents_received_callback (GtkClipboard *clipboard,
289                             GtkSelectionData *sd,
290                             gpointer data)
291 {
292   gchar *c;
293   PsppireSyntaxWindow *syntax_window = data;
294
295   if ( sd->length < 0 )
296     return;
297
298   if ( sd->type != gdk_atom_intern ("UTF8_STRING", FALSE))
299     return;
300
301   c = (gchar *) sd->data;
302
303   gtk_text_buffer_insert_at_cursor (syntax_window->buffer,
304                                     (gchar *) sd->data,
305                                     sd->length);
306
307 }
308
309 static void
310 on_edit_paste (PsppireSyntaxWindow *sw)
311 {
312   GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (sw));
313   GtkClipboard *clipboard =
314     gtk_clipboard_get_for_display (display, GDK_SELECTION_CLIPBOARD);
315
316   gtk_clipboard_request_contents (clipboard,
317                                   gdk_atom_intern ("UTF8_STRING", TRUE),
318                                   contents_received_callback,
319                                   sw);
320 }
321
322
323 /* Check to see if CLIP holds a target which we know how to paste,
324    and set the sensitivity of the Paste action accordingly.
325  */
326 static void
327 set_paste_sensitivity (GtkClipboard *clip, GdkEventOwnerChange *event, gpointer data)
328 {
329   gint i;
330   gboolean compatible_target = FALSE;
331   PsppireSyntaxWindow *sw = PSPPIRE_SYNTAX_WINDOW (data);
332
333   for (i = 0 ; i < sizeof (targets) / sizeof (targets[0]) ; ++i)
334     {
335       GdkAtom atom = gdk_atom_intern (targets[i].target, TRUE);
336       if ( gtk_clipboard_wait_is_target_available (clip, atom))
337         {
338           compatible_target = TRUE;
339           break;
340         }
341     }
342
343   gtk_action_set_sensitive (sw->edit_paste, compatible_target);
344 }
345
346
347 \f
348
349 /* Parse and execute all the text in the buffer */
350 static void
351 on_run_all (GtkMenuItem *menuitem, gpointer user_data)
352 {
353   GtkTextIter begin, end;
354   PsppireSyntaxWindow *se = PSPPIRE_SYNTAX_WINDOW (user_data);
355
356   gtk_text_buffer_get_iter_at_offset (se->buffer, &begin, 0);
357   gtk_text_buffer_get_iter_at_offset (se->buffer, &end, -1);
358
359   editor_execute_syntax (se, begin, end);
360 }
361
362 /* Parse and execute the currently selected text */
363 static void
364 on_run_selection (GtkMenuItem *menuitem, gpointer user_data)
365 {
366   GtkTextIter begin, end;
367   PsppireSyntaxWindow *se = PSPPIRE_SYNTAX_WINDOW (user_data);
368
369   if ( gtk_text_buffer_get_selection_bounds (se->buffer, &begin, &end) )
370     editor_execute_syntax (se, begin, end);
371 }
372
373
374 /* Parse and execute the from the current line, to the end of the
375    buffer */
376 static void
377 on_run_to_end (GtkMenuItem *menuitem, gpointer user_data)
378 {
379   GtkTextIter begin, end;
380   GtkTextIter here;
381   gint line;
382
383   PsppireSyntaxWindow *se = PSPPIRE_SYNTAX_WINDOW (user_data);
384
385   /* Get the current line */
386   gtk_text_buffer_get_iter_at_mark (se->buffer,
387                                     &here,
388                                     gtk_text_buffer_get_insert (se->buffer)
389                                     );
390
391   line = gtk_text_iter_get_line (&here) ;
392
393   /* Now set begin and end to the start of this line, and end of buffer
394      respectively */
395   gtk_text_buffer_get_iter_at_line (se->buffer, &begin, line);
396   gtk_text_buffer_get_iter_at_line (se->buffer, &end, -1);
397
398   editor_execute_syntax (se, begin, end);
399 }
400
401
402
403 /* Parse and execute the current line */
404 static void
405 on_run_current_line (GtkMenuItem *menuitem, gpointer user_data)
406 {
407   GtkTextIter begin, end;
408   GtkTextIter here;
409   gint line;
410
411   PsppireSyntaxWindow *se = PSPPIRE_SYNTAX_WINDOW (user_data);
412
413   /* Get the current line */
414   gtk_text_buffer_get_iter_at_mark (se->buffer,
415                                     &here,
416                                     gtk_text_buffer_get_insert (se->buffer)
417                                     );
418
419   line = gtk_text_iter_get_line (&here) ;
420
421   /* Now set begin and end to the start of this line, and start of
422      following line respectively */
423   gtk_text_buffer_get_iter_at_line (se->buffer, &begin, line);
424   gtk_text_buffer_get_iter_at_line (se->buffer, &end, line + 1);
425
426   editor_execute_syntax (se, begin, end);
427 }
428
429
430
431 /* Append ".sps" to FILENAME if necessary.
432    The returned result must be freed when no longer required.
433  */
434 static gchar *
435 append_suffix (const gchar *filename)
436 {
437   if ( ! g_str_has_suffix (filename, ".sps" ) &&
438        ! g_str_has_suffix (filename, ".SPS" ) )
439     {
440       return g_strdup_printf ("%s.sps", filename);
441     }
442
443   return xstrdup (filename);
444 }
445
446 /*
447   Save BUFFER to the file called FILENAME.
448   FILENAME must be encoded in Glib filename encoding.
449   If successful, clears the buffer's modified flag.
450 */
451 static gboolean
452 save_editor_to_file (PsppireSyntaxWindow *se,
453                      const gchar *filename,
454                      GError **err)
455 {
456   GtkTextBuffer *buffer = se->buffer;
457   gboolean result ;
458   GtkTextIter start, stop;
459   gchar *text;
460
461   gchar *suffixedname;
462   g_assert (filename);
463
464   suffixedname = append_suffix (filename);
465
466   gtk_text_buffer_get_iter_at_line (buffer, &start, 0);
467   gtk_text_buffer_get_iter_at_offset (buffer, &stop, -1);
468
469   text = gtk_text_buffer_get_text (buffer, &start, &stop, FALSE);
470
471   result =  g_file_set_contents (suffixedname, text, -1, err);
472
473   g_free (suffixedname);
474
475   if ( result )
476     {
477       char *fn = g_filename_display_name (filename);
478       gchar *msg = g_strdup_printf (_("Saved file `%s'"), fn);
479       g_free (fn);
480       gtk_statusbar_push (GTK_STATUSBAR (se->sb), se->text_context, msg);
481       gtk_text_buffer_set_modified (buffer, FALSE);
482       g_free (msg);
483     }
484
485   return result;
486 }
487
488
489 /* Callback for the File->SaveAs menuitem */
490 static void
491 syntax_save_as (PsppireWindow *se)
492 {
493   GtkFileFilter *filter;
494   gint response;
495
496   GtkWidget *dialog =
497     gtk_file_chooser_dialog_new (_("Save Syntax"),
498                                  GTK_WINDOW (se),
499                                  GTK_FILE_CHOOSER_ACTION_SAVE,
500                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
501                                  GTK_STOCK_SAVE,   GTK_RESPONSE_ACCEPT,
502                                  NULL);
503
504   filter = gtk_file_filter_new ();
505   gtk_file_filter_set_name (filter, _("Syntax Files (*.sps) "));
506   gtk_file_filter_add_pattern (filter, "*.sps");
507   gtk_file_filter_add_pattern (filter, "*.SPS");
508   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
509
510   filter = gtk_file_filter_new ();
511   gtk_file_filter_set_name (filter, _("All Files"));
512   gtk_file_filter_add_pattern (filter, "*");
513   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
514
515   gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
516                                                   TRUE);
517   response = gtk_dialog_run (GTK_DIALOG (dialog));
518
519   if ( response == GTK_RESPONSE_ACCEPT )
520     {
521       GError *err = NULL;
522       char *filename =
523         gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog) );
524
525       if ( ! save_editor_to_file (PSPPIRE_SYNTAX_WINDOW (se), filename, &err) )
526         {
527           msg ( ME, "%s", err->message );
528           g_error_free (err);
529         }
530
531       free (filename);
532     }
533
534   gtk_widget_destroy (dialog);
535 }
536
537
538 /* Callback for the File->Save menuitem */
539 static void
540 syntax_save (PsppireWindow *se)
541 {
542   const gchar *filename = psppire_window_get_filename (se);
543
544   if ( filename == NULL )
545     syntax_save_as (se);
546   else
547     {
548       GError *err = NULL;
549       save_editor_to_file (PSPPIRE_SYNTAX_WINDOW (se), filename, &err);
550       if ( err )
551         {
552           msg (ME, "%s", err->message);
553           g_error_free (err);
554         }
555     }
556 }
557
558
559 /* Callback for the File->Quit menuitem */
560 static gboolean
561 on_quit (GtkMenuItem *menuitem, gpointer    user_data)
562 {
563   psppire_quit ();
564
565   return FALSE;
566 }
567
568
569 void
570 create_syntax_window (void)
571 {
572   GtkWidget *w = psppire_syntax_window_new ();
573   gtk_widget_show (w);
574 }
575
576 void
577 open_syntax_window (const char *file_name)
578 {
579   GtkWidget *se = psppire_syntax_window_new ();
580
581   if ( psppire_window_load (PSPPIRE_WINDOW (se), file_name) )
582     gtk_widget_show (se);
583   else
584     gtk_widget_destroy (se);
585 }
586
587 static void
588 on_text_changed (GtkTextBuffer *buffer, PsppireSyntaxWindow *window)
589 {
590   gtk_statusbar_pop (GTK_STATUSBAR (window->sb), window->text_context);
591 }
592
593 static void
594 on_modified_changed (GtkTextBuffer *buffer, PsppireWindow *window)
595 {
596   if (gtk_text_buffer_get_modified (buffer))
597     psppire_window_set_unsaved (window);
598 }
599
600 static void
601 psppire_syntax_window_init (PsppireSyntaxWindow *window)
602 {
603   GtkBuilder *xml = builder_new ("syntax-editor.ui");
604   GtkWidget *box = gtk_vbox_new (FALSE, 0);
605
606   GtkWidget *menubar = get_widget_assert (xml, "menubar");
607   GtkWidget *sw = get_widget_assert (xml, "scrolledwindow8");
608
609
610   GtkWidget *text_view = get_widget_assert (xml, "syntax_text_view");
611
612   GtkClipboard *clip_selection = gtk_widget_get_clipboard (GTK_WIDGET (window), GDK_SELECTION_CLIPBOARD);
613   GtkClipboard *clip_primary =   gtk_widget_get_clipboard (GTK_WIDGET (window), GDK_SELECTION_PRIMARY);
614
615   window->cliptext = NULL;
616   window->dispose_has_run = FALSE;
617
618   window->edit_delete = get_action_assert (xml, "edit_delete");
619   window->edit_copy = get_action_assert (xml, "edit_copy");
620   window->edit_cut = get_action_assert (xml, "edit_cut");
621   window->edit_paste = get_action_assert (xml, "edit_paste");
622
623   window->buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
624
625   window->sb = get_widget_assert (xml, "statusbar2");
626   window->text_context = gtk_statusbar_get_context_id (GTK_STATUSBAR (window->sb), "Text Context");
627
628   g_signal_connect (window->buffer, "changed", 
629                     G_CALLBACK (on_text_changed), window);
630
631   g_signal_connect (window->buffer, "modified-changed", 
632                     G_CALLBACK (on_modified_changed), window);
633
634   window->sel_handler = g_signal_connect_swapped (clip_primary, "owner-change", 
635                                                    G_CALLBACK (selection_changed), window);
636
637   window->ps_handler = g_signal_connect (clip_selection, "owner-change", 
638                                           G_CALLBACK (set_paste_sensitivity), window);
639
640   connect_help (xml);
641
642   gtk_container_add (GTK_CONTAINER (window), box);
643
644   g_object_ref (menubar);
645
646   g_object_ref (sw);
647
648   g_object_ref (window->sb);
649
650   gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
651   gtk_box_pack_start (GTK_BOX (box), sw, TRUE, TRUE, 0);
652   gtk_box_pack_start (GTK_BOX (box), window->sb, FALSE, TRUE, 0);
653
654   gtk_widget_show_all (box);
655
656   g_signal_connect_swapped (get_action_assert (xml,"file_new_syntax"), "activate", G_CALLBACK (create_syntax_window), NULL);
657
658 #if 0
659   g_signal_connect (get_action_assert (xml,"file_new_data"),
660                     "activate",
661                     G_CALLBACK (create_data_window),
662                     window);
663 #endif
664
665   g_signal_connect_swapped (get_action_assert (xml, "file_save"),
666                     "activate",
667                     G_CALLBACK (syntax_save),
668                     window);
669
670   g_signal_connect_swapped (get_action_assert (xml, "file_save_as"),
671                     "activate",
672                     G_CALLBACK (syntax_save_as),
673                     window);
674
675   g_signal_connect (get_action_assert (xml,"file_quit"),
676                     "activate",
677                     G_CALLBACK (on_quit),
678                     window);
679
680   g_signal_connect_swapped (window->edit_delete,
681                     "activate",
682                     G_CALLBACK (on_edit_delete),
683                     window);
684
685   g_signal_connect_swapped (window->edit_copy,
686                     "activate",
687                     G_CALLBACK (on_edit_copy),
688                     window);
689
690   g_signal_connect_swapped (window->edit_cut,
691                     "activate",
692                     G_CALLBACK (on_edit_cut),
693                     window);
694
695   g_signal_connect_swapped (window->edit_paste,
696                     "activate",
697                     G_CALLBACK (on_edit_paste),
698                     window);
699
700   g_signal_connect (get_action_assert (xml,"run_all"),
701                     "activate",
702                     G_CALLBACK (on_run_all),
703                     window);
704
705   g_signal_connect (get_action_assert (xml,"run_selection"),
706                     "activate",
707                     G_CALLBACK (on_run_selection),
708                     window);
709
710   g_signal_connect (get_action_assert (xml,"run_current_line"),
711                     "activate",
712                     G_CALLBACK (on_run_current_line),
713                     window);
714
715   g_signal_connect (get_action_assert (xml,"run_to_end"),
716                     "activate",
717                     G_CALLBACK (on_run_to_end),
718                     window);
719
720   g_signal_connect (get_action_assert (xml,"windows_minimise_all"),
721                     "activate",
722                     G_CALLBACK (psppire_window_minimise_all), NULL);
723
724
725   {
726   GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (xml, "uimanager1", GTK_TYPE_UI_MANAGER));
727
728   merge_help_menu (uim);
729
730   PSPPIRE_WINDOW (window)->menu =
731     GTK_MENU_SHELL (gtk_ui_manager_get_widget (uim,"/ui/menubar/windows/windows_minimise_all")->parent);
732   }
733
734   g_object_unref (xml);
735 }
736
737
738
739
740
741 GtkWidget*
742 psppire_syntax_window_new (void)
743 {
744   return GTK_WIDGET (g_object_new (psppire_syntax_window_get_type (),
745                                    /* TRANSLATORS: This will form a filename.  Please avoid whitespace. */
746                                    "filename", _("Syntax"),
747                                    "description", _("Syntax Editor"),
748                                    NULL));
749 }
750
751 static void
752 error_dialog (GtkWindow *w, const gchar *filename,  GError *err)
753 {
754   gchar *fn = g_filename_display_basename (filename);
755
756   GtkWidget *dialog =
757     gtk_message_dialog_new (w,
758                             GTK_DIALOG_DESTROY_WITH_PARENT,
759                             GTK_MESSAGE_ERROR,
760                             GTK_BUTTONS_CLOSE,
761                             _("Cannot load syntax file `%s'"),
762                             fn);
763
764   g_free (fn);
765
766   g_object_set (dialog, "icon-name", "psppicon", NULL);
767
768   gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
769                                             "%s", err->message);
770
771   gtk_dialog_run (GTK_DIALOG (dialog));
772
773   gtk_widget_destroy (dialog);
774 }
775
776 /*
777   Loads the buffer from the file called FILENAME
778 */
779 gboolean
780 syntax_load (PsppireWindow *window, const gchar *filename)
781 {
782   GError *err = NULL;
783   gchar *text_locale = NULL;
784   gchar *text_utf8 = NULL;
785   gsize len_locale = -1;
786   gsize len_utf8 = -1;
787   GtkTextIter iter;
788   PsppireSyntaxWindow *sw = PSPPIRE_SYNTAX_WINDOW (window);
789
790   /* FIXME: What if it's a very big file ? */
791   if ( ! g_file_get_contents (filename, &text_locale, &len_locale, &err) )
792     {
793       error_dialog (GTK_WINDOW (window), filename, err);
794       g_clear_error (&err);
795       return FALSE;
796     }
797
798   text_utf8 = g_locale_to_utf8 (text_locale, len_locale, NULL, &len_utf8, &err);
799
800   free (text_locale);
801
802   if ( text_utf8 == NULL )
803     {
804       error_dialog (GTK_WINDOW (window), filename, err);
805       g_clear_error (&err);
806       return FALSE;
807     }
808
809   gtk_text_buffer_get_iter_at_line (sw->buffer, &iter, 0);
810
811   gtk_text_buffer_insert (sw->buffer, &iter, text_utf8, len_utf8);
812
813   gtk_text_buffer_set_modified (sw->buffer, FALSE);
814
815   free (text_utf8);
816
817   return TRUE;
818 }
819
820 \f
821
822 static void
823 psppire_syntax_window_iface_init (PsppireWindowIface *iface)
824 {
825   iface->save = syntax_save;
826   iface->load = syntax_load;
827 }
828