Save window contents on closing.
[pspp-builds.git] / src / ui / gui / psppire-syntax-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/gtkbox.h>
21 #include "helper.h"
22
23 #include <libpspp/message.h>
24 #include <stdlib.h>
25
26 #include "psppire.h"
27 #include "psppire-syntax-window.h"
28
29 #include "psppire-data-window.h"
30 #include "psppire-window-register.h"
31 #include "about.h"
32 #include "psppire-syntax-window.h"
33 #include "syntax-editor-source.h"
34 #include <language/lexer/lexer.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_class_init (PsppireSyntaxWindowClass *class)
100 {
101   parent_class = g_type_class_peek_parent (class);
102 }
103
104
105 static void
106 psppire_syntax_window_base_init (PsppireSyntaxWindowClass *class)
107 {
108   GObjectClass *object_class = G_OBJECT_CLASS (class);
109
110   object_class->finalize = psppire_syntax_window_finalize;
111 }
112
113
114
115 static void
116 psppire_syntax_window_base_finalize (PsppireSyntaxWindowClass *class,
117                                      gpointer class_data)
118 {
119 }
120
121
122 static void
123 editor_execute_syntax (const PsppireSyntaxWindow *sw, GtkTextIter start,
124                        GtkTextIter stop)
125 {
126   PsppireWindow *win = PSPPIRE_WINDOW (sw);
127   const gchar *name = psppire_window_get_filename (win);
128   execute_syntax (create_syntax_editor_source (sw->buffer, start, stop, name));
129 }
130
131
132 /* Parse and execute all the text in the buffer */
133 static void
134 on_run_all (GtkMenuItem *menuitem, gpointer user_data)
135 {
136   GtkTextIter begin, end;
137   PsppireSyntaxWindow *se = PSPPIRE_SYNTAX_WINDOW (user_data);
138
139   gtk_text_buffer_get_iter_at_offset (se->buffer, &begin, 0);
140   gtk_text_buffer_get_iter_at_offset (se->buffer, &end, -1);
141
142   editor_execute_syntax (se, begin, end);
143 }
144
145 /* Parse and execute the currently selected text */
146 static void
147 on_run_selection (GtkMenuItem *menuitem, gpointer user_data)
148 {
149   GtkTextIter begin, end;
150   PsppireSyntaxWindow *se = PSPPIRE_SYNTAX_WINDOW (user_data);
151
152   if ( gtk_text_buffer_get_selection_bounds (se->buffer, &begin, &end) )
153     editor_execute_syntax (se, begin, end);
154 }
155
156
157 /* Parse and execute the from the current line, to the end of the
158    buffer */
159 static void
160 on_run_to_end (GtkMenuItem *menuitem, gpointer user_data)
161 {
162   GtkTextIter begin, end;
163   GtkTextIter here;
164   gint line;
165
166   PsppireSyntaxWindow *se = PSPPIRE_SYNTAX_WINDOW (user_data);
167
168   /* Get the current line */
169   gtk_text_buffer_get_iter_at_mark (se->buffer,
170                                     &here,
171                                     gtk_text_buffer_get_insert (se->buffer)
172                                     );
173
174   line = gtk_text_iter_get_line (&here) ;
175
176   /* Now set begin and end to the start of this line, and end of buffer
177      respectively */
178   gtk_text_buffer_get_iter_at_line (se->buffer, &begin, line);
179   gtk_text_buffer_get_iter_at_line (se->buffer, &end, -1);
180
181   editor_execute_syntax (se, begin, end);
182 }
183
184
185
186 /* Parse and execute the current line */
187 static void
188 on_run_current_line (GtkMenuItem *menuitem, gpointer user_data)
189 {
190   GtkTextIter begin, end;
191   GtkTextIter here;
192   gint line;
193
194   PsppireSyntaxWindow *se = PSPPIRE_SYNTAX_WINDOW (user_data);
195
196   /* Get the current line */
197   gtk_text_buffer_get_iter_at_mark (se->buffer,
198                                     &here,
199                                     gtk_text_buffer_get_insert (se->buffer)
200                                     );
201
202   line = gtk_text_iter_get_line (&here) ;
203
204   /* Now set begin and end to the start of this line, and start of
205      following line respectively */
206   gtk_text_buffer_get_iter_at_line (se->buffer, &begin, line);
207   gtk_text_buffer_get_iter_at_line (se->buffer, &end, line + 1);
208
209   editor_execute_syntax (se, begin, end);
210 }
211
212
213
214 /* Append ".sps" to FILENAME if necessary.
215    The returned result must be freed when no longer required.
216  */
217 static gchar *
218 append_suffix (const gchar *filename)
219 {
220   if ( ! g_str_has_suffix (filename, ".sps" ) &&
221        ! g_str_has_suffix (filename, ".SPS" ) )
222     {
223       return g_strdup_printf ("%s.sps", filename);
224     }
225
226   return strdup (filename);
227 }
228
229 /*
230   Save BUFFER to the file called FILENAME.
231   If successful, clears the buffer's modified flag
232 */
233 static gboolean
234 save_editor_to_file (PsppireSyntaxWindow *se,
235                      const gchar *filename,
236                      GError **err)
237 {
238   GtkTextBuffer *buffer = se->buffer;
239   gboolean result ;
240   GtkTextIter start, stop;
241   gchar *text;
242
243   gchar *suffixedname;
244   gchar *glibfilename;
245   g_assert (filename);
246
247   suffixedname = append_suffix (filename);
248
249   glibfilename = g_filename_from_utf8 (suffixedname, -1, 0, 0, err);
250
251   g_free ( suffixedname);
252
253   if ( ! glibfilename )
254     return FALSE;
255
256   gtk_text_buffer_get_iter_at_line (buffer, &start, 0);
257   gtk_text_buffer_get_iter_at_offset (buffer, &stop, -1);
258
259   text = gtk_text_buffer_get_text (buffer, &start, &stop, FALSE);
260
261   result =  g_file_set_contents (glibfilename, text, -1, err);
262
263   if ( result )
264     {
265       gchar *msg = g_strdup_printf (_("Saved file \"%s\""), filename);
266       gtk_statusbar_push (GTK_STATUSBAR (se->sb), se->text_context, msg);
267       gtk_text_buffer_set_modified (buffer, FALSE);
268       g_free (msg);
269     }
270
271   return result;
272 }
273
274
275 /* Callback for the File->SaveAs menuitem */
276 static void
277 syntax_save_as (PsppireWindow *se)
278 {
279   GtkFileFilter *filter;
280   gint response;
281
282   GtkWidget *dialog =
283     gtk_file_chooser_dialog_new (_("Save Syntax"),
284                                  GTK_WINDOW (se),
285                                  GTK_FILE_CHOOSER_ACTION_SAVE,
286                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
287                                  GTK_STOCK_SAVE,   GTK_RESPONSE_ACCEPT,
288                                  NULL);
289
290   filter = gtk_file_filter_new ();
291   gtk_file_filter_set_name (filter, _("Syntax Files (*.sps) "));
292   gtk_file_filter_add_pattern (filter, "*.sps");
293   gtk_file_filter_add_pattern (filter, "*.SPS");
294   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
295
296   filter = gtk_file_filter_new ();
297   gtk_file_filter_set_name (filter, _("All Files"));
298   gtk_file_filter_add_pattern (filter, "*");
299   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
300
301   gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
302                                                   TRUE);
303   response = gtk_dialog_run (GTK_DIALOG (dialog));
304
305   if ( response == GTK_RESPONSE_ACCEPT )
306     {
307       GError *err = NULL;
308       char *filename =
309         gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog) );
310
311       if ( ! save_editor_to_file (PSPPIRE_SYNTAX_WINDOW (se), filename, &err) )
312         {
313           msg ( ME, err->message );
314           g_error_free (err);
315         }
316
317       free (filename);
318     }
319
320   gtk_widget_destroy (dialog);
321 }
322
323
324 /* Callback for the File->Save menuitem */
325 static void
326 syntax_save (PsppireWindow *se)
327 {
328   const gchar *filename = psppire_window_get_filename (se);
329
330   if ( filename == NULL )
331     syntax_save_as (se);
332   else
333     {
334       GError *err = NULL;
335       save_editor_to_file (PSPPIRE_SYNTAX_WINDOW (se), filename, &err);
336       if ( err )
337         {
338           msg (ME, err->message);
339           g_error_free (err);
340         }
341     }
342 }
343
344
345 /* Callback for the File->Quit menuitem */
346 static gboolean
347 on_quit (GtkMenuItem *menuitem, gpointer    user_data)
348 {
349   psppire_quit ();
350
351   return FALSE;
352 }
353
354
355 void
356 create_syntax_window (void)
357 {
358   GtkWidget *w = psppire_syntax_window_new ();
359   gtk_widget_show (w);
360 }
361
362 /* Callback for the File->Open->Syntax menuitem */
363 void
364 open_syntax_window (GtkMenuItem *menuitem, gpointer parent)
365 {
366   GtkFileFilter *filter;
367   gint response;
368
369   GtkWidget *dialog =
370     gtk_file_chooser_dialog_new (_("Open Syntax"),
371                                  GTK_WINDOW (parent),
372                                  GTK_FILE_CHOOSER_ACTION_OPEN,
373                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
374                                  GTK_STOCK_OPEN,   GTK_RESPONSE_ACCEPT,
375                                  NULL);
376
377   filter = gtk_file_filter_new ();
378   gtk_file_filter_set_name (filter, _("Syntax Files (*.sps) "));
379   gtk_file_filter_add_pattern (filter, "*.sps");
380   gtk_file_filter_add_pattern (filter, "*.SPS");
381   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
382
383   filter = gtk_file_filter_new ();
384   gtk_file_filter_set_name (filter, _("All Files"));
385   gtk_file_filter_add_pattern (filter, "*");
386   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
387
388   response = gtk_dialog_run (GTK_DIALOG (dialog));
389
390   if (response == GTK_RESPONSE_ACCEPT)
391     {
392       const char *file_name = gtk_file_chooser_get_filename
393         (GTK_FILE_CHOOSER (dialog));
394
395       GtkWidget *se = psppire_syntax_window_new ();
396
397       if ( psppire_syntax_window_load_from_file (PSPPIRE_SYNTAX_WINDOW (se), file_name, NULL) )
398 #if RECENT_LISTS_AVAILABLE
399       {
400         GtkRecentManager *manager = gtk_recent_manager_get_default();
401         gchar *uri = g_filename_to_uri (file_name, NULL, NULL);
402
403         gtk_recent_manager_remove_item (manager, uri, NULL);
404         if ( ! gtk_recent_manager_add_item (manager, uri))
405           g_warning ("Could not add item %s to recent list\n",uri);
406
407         g_free (uri);
408       }
409 #else
410       ;
411 #endif
412       gtk_widget_show (se);
413     }
414
415   gtk_widget_destroy (dialog);
416 }
417
418
419 static void
420 on_text_changed (GtkTextBuffer *buffer, PsppireSyntaxWindow *window)
421 {
422   gtk_statusbar_pop (GTK_STATUSBAR (window->sb), window->text_context);
423 }
424
425 static void
426 on_modified_changed (GtkTextBuffer *buffer, PsppireWindow *window)
427 {
428   psppire_window_set_unsaved (window, gtk_text_buffer_get_modified (buffer));
429 }
430
431
432 extern struct source_stream *the_source_stream ;
433
434 static void
435 psppire_syntax_window_init (PsppireSyntaxWindow *window)
436 {
437   GtkBuilder *xml = builder_new ("syntax-editor.ui");
438   GtkWidget *box = gtk_vbox_new (FALSE, 0);
439
440   GtkWidget *menubar = get_widget_assert (xml, "menubar2");
441   GtkWidget *sw = get_widget_assert (xml, "scrolledwindow8");
442
443
444   GtkWidget *text_view = get_widget_assert (xml, "syntax_text_view");
445   window->buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
446   window->lexer = lex_create (the_source_stream);
447
448   window->sb = get_widget_assert (xml, "statusbar2");
449   window->text_context = gtk_statusbar_get_context_id (GTK_STATUSBAR (window->sb), "Text Context");
450
451   g_signal_connect (window->buffer, "changed", G_CALLBACK (on_text_changed), window);
452
453   g_signal_connect (window->buffer, "modified-changed",
454                     G_CALLBACK (on_modified_changed), window);
455
456   connect_help (xml);
457
458   gtk_container_add (GTK_CONTAINER (window), box);
459
460   g_object_ref (menubar);
461
462   g_object_ref (sw);
463
464   g_object_ref (window->sb);
465
466
467   gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
468   gtk_box_pack_start (GTK_BOX (box), sw, TRUE, TRUE, 0);
469   gtk_box_pack_start (GTK_BOX (box), window->sb, FALSE, TRUE, 0);
470
471   gtk_widget_show_all (box);
472
473   g_signal_connect (get_object_assert (xml,"file_new_syntax"),
474                     "activate",
475                     G_CALLBACK (create_syntax_window),
476                     NULL);
477
478   g_signal_connect (get_object_assert (xml,"file_open_syntax"),
479                     "activate",
480                     G_CALLBACK (open_syntax_window),
481                     window);
482
483 #if 0
484   g_signal_connect (get_object_assert (xml,"file_new_data"),
485                     "activate",
486                     G_CALLBACK (create_data_window),
487                     window);
488 #endif
489
490   g_signal_connect (get_object_assert (xml,"help_about"),
491                     "activate",
492                     G_CALLBACK (about_new),
493                     window);
494
495   g_signal_connect (get_object_assert (xml,"help_reference"),
496                     "activate",
497                     G_CALLBACK (reference_manual),
498                     NULL);
499
500   g_signal_connect_swapped (get_object_assert (xml, "file_save"),
501                     "activate",
502                     G_CALLBACK (syntax_save),
503                     window);
504
505   g_signal_connect_swapped (get_object_assert (xml, "file_save_as"),
506                     "activate",
507                     G_CALLBACK (syntax_save_as),
508                     window);
509
510   g_signal_connect (get_object_assert (xml,"file_quit"),
511                     "activate",
512                     G_CALLBACK (on_quit),
513                     window);
514
515   g_signal_connect (get_object_assert (xml,"run_all"),
516                     "activate",
517                     G_CALLBACK (on_run_all),
518                     window);
519
520
521   g_signal_connect (get_object_assert (xml,"run_selection"),
522                     "activate",
523                     G_CALLBACK (on_run_selection),
524                     window);
525
526   g_signal_connect (get_object_assert (xml,"run_current_line"),
527                     "activate",
528                     G_CALLBACK (on_run_current_line),
529                     window);
530
531   g_signal_connect (get_object_assert (xml,"run_to_end"),
532                     "activate",
533                     G_CALLBACK (on_run_to_end),
534                     window);
535
536   g_signal_connect (get_object_assert (xml,"windows_minimise_all"),
537                     "activate",
538                     G_CALLBACK (psppire_window_minimise_all), NULL);
539
540
541   {
542   GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (xml, "uimanager1"));
543
544   PSPPIRE_WINDOW (window)->menu =
545     GTK_MENU_SHELL (gtk_ui_manager_get_widget (uim,"/ui/menubar2/windows/windows_minimise_all")->parent);
546   }
547
548   g_object_unref (xml);
549 }
550
551
552 GtkWidget*
553 psppire_syntax_window_new (void)
554 {
555   return GTK_WIDGET (g_object_new (psppire_syntax_window_get_type (),
556                                    "filename", "Syntax",
557                                    "description", _("Syntax Editor"),
558                                    NULL));
559 }
560
561
562 /*
563   Loads the buffer from the file called FILENAME
564 */
565 gboolean
566 psppire_syntax_window_load_from_file (PsppireSyntaxWindow *se,
567                                       const gchar *filename,
568                                       GError **err)
569 {
570   gchar *text;
571   GtkTextIter iter;
572
573   gchar *glibfilename = g_filename_from_utf8 (filename, -1, 0, 0, err);
574
575   if ( ! glibfilename )
576     return FALSE;
577
578   /* FIXME: What if it's a very big file ? */
579   if ( ! g_file_get_contents (glibfilename, &text, NULL, err) )
580     {
581       g_free (glibfilename);
582       return FALSE;
583     }
584   g_free (glibfilename);
585
586   gtk_text_buffer_get_iter_at_line (se->buffer, &iter, 0);
587
588   gtk_text_buffer_insert (se->buffer, &iter, text, -1);
589
590   psppire_window_set_filename (PSPPIRE_WINDOW (se), filename);
591
592   gtk_text_buffer_set_modified (se->buffer, FALSE);
593
594   return TRUE;
595 }
596
597 \f
598
599 static void
600 psppire_syntax_window_iface_init (PsppireWindowIface *iface)
601 {
602   iface->save = syntax_save;
603 }