Fixed buglet managing entries in the recent files lists.
[pspp-builds.git] / src / ui / gui / syntax-editor.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2006 Free Software Foundation, Inc.
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 #include <stdlib.h>
19 #include <gettext.h>
20 #define _(msgid) gettext (msgid)
21 #define N_(msgid) msgid
22
23 #include <glade/glade.h>
24 #include <gtk/gtk.h>
25 #include <libpspp/message.h>
26 #include <libpspp/getl.h>
27 #include "helper.h"
28 #include "data-editor.h"
29 #include "about.h"
30
31 #include "window-manager.h"
32
33 #include <data/dictionary.h>
34 #include <language/lexer/lexer.h>
35 #include <language/command.h>
36 #include <data/procedure.h>
37 #include "syntax-editor.h"
38 #include "syntax-editor-source.h"
39
40 extern struct source_stream *the_source_stream ;
41 extern struct dataset *the_dataset;
42
43 static gboolean save_editor_to_file (struct syntax_editor *se,
44                                      const gchar *filename,
45                                      GError **err);
46
47 /* Append ".sps" to FILENAME if necessary.
48    The returned result must be freed when no longer required.
49  */
50 static gchar *
51 append_suffix (const gchar *filename)
52 {
53   if ( ! g_str_has_suffix (filename, ".sps" ) &&
54        ! g_str_has_suffix (filename, ".SPS" ) )
55     {
56       return g_strdup_printf ("%s.sps", filename);
57     }
58
59   return strdup (filename);
60 }
61
62 /* If the buffer's modified flag is set, then save it, and close the window.
63    Otherwise just close the window.
64 */
65 static void
66 save_if_modified (struct syntax_editor *se)
67 {
68   struct editor_window *e = (struct editor_window *) se;
69   if ( TRUE == gtk_text_buffer_get_modified (se->buffer))
70     {
71       gint response;
72       GtkWidget *dialog =
73         gtk_message_dialog_new (GTK_WINDOW (e->window),
74                                 GTK_DIALOG_MODAL,
75                                 GTK_MESSAGE_QUESTION,
76                                 GTK_BUTTONS_NONE,
77                                 _("Save contents of syntax editor to %s?"),
78                                 e->name
79                                 );
80
81       gtk_dialog_add_button  (GTK_DIALOG (dialog),
82                               GTK_STOCK_YES,
83                               GTK_RESPONSE_ACCEPT);
84       gtk_dialog_add_button  (GTK_DIALOG (dialog),
85                               GTK_STOCK_NO,
86                               GTK_RESPONSE_REJECT);
87       gtk_dialog_add_button  (GTK_DIALOG (dialog),
88                               GTK_STOCK_CANCEL,
89                               GTK_RESPONSE_CANCEL);
90
91
92       response = gtk_dialog_run (GTK_DIALOG (dialog));
93
94       gtk_widget_destroy (dialog);
95
96       if ( response == GTK_RESPONSE_ACCEPT )
97         {
98           GError *err = NULL;
99
100           if ( ! save_editor_to_file (se, e->name, &err) )
101             {
102               msg (ME, err->message);
103               g_error_free (err);
104             }
105         }
106
107       if ( response == GTK_RESPONSE_CANCEL )
108         return ;
109     }
110
111   gtk_widget_destroy (GTK_WIDGET (e->window));
112 }
113
114 /* Callback for the File->SaveAs menuitem */
115 static void
116 on_syntax_save_as (GtkMenuItem *menuitem, gpointer user_data)
117 {
118   GtkFileFilter *filter;
119   gint response;
120   struct syntax_editor *se = user_data;
121   struct editor_window *e = user_data;
122
123   GtkWidget *dialog =
124     gtk_file_chooser_dialog_new (_("Save Syntax"),
125                                  GTK_WINDOW (e->window),
126                                  GTK_FILE_CHOOSER_ACTION_SAVE,
127                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
128                                  GTK_STOCK_SAVE,   GTK_RESPONSE_ACCEPT,
129                                  NULL);
130
131   filter = gtk_file_filter_new ();
132   gtk_file_filter_set_name (filter, _("Syntax Files (*.sps) "));
133   gtk_file_filter_add_pattern (filter, "*.sps");
134   gtk_file_filter_add_pattern (filter, "*.SPS");
135   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
136
137   filter = gtk_file_filter_new ();
138   gtk_file_filter_set_name (filter, _("All Files"));
139   gtk_file_filter_add_pattern (filter, "*");
140   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
141
142   gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
143                                                   TRUE);
144   response = gtk_dialog_run (GTK_DIALOG (dialog));
145
146   if ( response == GTK_RESPONSE_ACCEPT )
147     {
148       GError *err = NULL;
149       char *filename =
150         gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog) );
151
152       if ( save_editor_to_file (se, filename, &err) )
153         {
154           g_free (e->name);
155           e->name = g_strdup (filename);
156         }
157       else
158         {
159           msg ( ME, err->message );
160           g_error_free (err);
161         }
162
163       free (filename);
164     }
165
166   gtk_widget_destroy ( dialog );
167 }
168
169 /* Callback for the File->Save menuitem */
170 static void
171 on_syntax_save (GtkMenuItem *menuitem, gpointer user_data)
172 {
173   struct syntax_editor *se = user_data;
174   struct editor_window *e = user_data;
175
176   if ( e->name == NULL )
177     on_syntax_save_as (menuitem, user_data);
178   else
179     {
180       GError *err = NULL;
181       save_editor_to_file (se, e->name, &err);
182       if ( err )
183         {
184           msg (ME, err->message);
185           g_error_free (err);
186         }
187     }
188 }
189
190
191 /* Callback for the "delete" action (clicking the x on the top right
192    hand corner of the window) */
193 static gboolean
194 on_delete (GtkWidget *w, GdkEvent *event, gpointer user_data)
195 {
196   struct syntax_editor *se = user_data;
197   save_if_modified (se);
198   return TRUE;
199 }
200
201
202 /* Callback for the File->Quit menuitem */
203 static gboolean
204 on_quit (GtkMenuItem *menuitem, gpointer    user_data)
205 {
206   struct syntax_editor *se = user_data;
207   save_if_modified (se);
208   return FALSE;
209 }
210
211 static void
212 editor_execute_syntax (const struct syntax_editor *se, GtkTextIter start,
213                 GtkTextIter stop)
214 {
215   execute_syntax (create_syntax_editor_source (se, start, stop));
216 }
217
218 /* Parse and execute all the text in the buffer */
219 static void
220 on_run_all (GtkMenuItem *menuitem, gpointer user_data)
221 {
222   GtkTextIter begin, end;
223   struct syntax_editor *se = user_data;
224
225   gtk_text_buffer_get_iter_at_offset (se->buffer, &begin, 0);
226   gtk_text_buffer_get_iter_at_offset (se->buffer, &end, -1);
227
228   editor_execute_syntax (se, begin, end);
229 }
230
231 /* Parse and execute the currently selected text */
232 static void
233 on_run_selection (GtkMenuItem *menuitem, gpointer user_data)
234 {
235   GtkTextIter begin, end;
236   struct syntax_editor *se = user_data;
237
238   if ( gtk_text_buffer_get_selection_bounds (se->buffer, &begin, &end) )
239     editor_execute_syntax (se, begin, end);
240 }
241
242
243 /* Parse and execute the current line */
244 static void
245 on_run_current_line (GtkMenuItem *menuitem, gpointer user_data)
246 {
247   GtkTextIter begin, end;
248   GtkTextIter here;
249   gint line;
250
251   struct syntax_editor *se = user_data;
252
253   /* Get the current line */
254   gtk_text_buffer_get_iter_at_mark (se->buffer,
255                                     &here,
256                                     gtk_text_buffer_get_insert (se->buffer)
257                                     );
258
259   line = gtk_text_iter_get_line (&here) ;
260
261   /* Now set begin and end to the start of this line, and start of
262      following line respectively */
263   gtk_text_buffer_get_iter_at_line (se->buffer, &begin, line);
264   gtk_text_buffer_get_iter_at_line (se->buffer, &end, line + 1);
265
266   editor_execute_syntax (se, begin, end);
267 }
268
269
270
271 /* Parse and execute the from the current line, to the end of the
272    buffer */
273 static void
274 on_run_to_end (GtkMenuItem *menuitem, gpointer user_data)
275 {
276   GtkTextIter begin, end;
277   GtkTextIter here;
278   gint line;
279
280   struct syntax_editor *se = user_data;
281
282   /* Get the current line */
283   gtk_text_buffer_get_iter_at_mark (se->buffer,
284                                     &here,
285                                     gtk_text_buffer_get_insert (se->buffer)
286                                     );
287
288   line = gtk_text_iter_get_line (&here) ;
289
290   /* Now set begin and end to the start of this line, and end of buffer
291      respectively */
292   gtk_text_buffer_get_iter_at_line (se->buffer, &begin, line);
293   gtk_text_buffer_get_iter_at_line (se->buffer, &end, -1);
294
295   editor_execute_syntax (se, begin, end);
296 }
297
298
299
300
301 /*
302   Create a new syntax editor with NAME.
303   If NAME is NULL, a name will be automatically assigned
304 */
305 struct syntax_editor *
306 new_syntax_editor (void)
307 {
308   GladeXML *xml = XML_NEW ("syntax-editor.glade");
309
310   GtkWidget *text_view;
311   struct syntax_editor *se ;
312   struct editor_window *e;
313
314   connect_help (xml);
315
316   se = g_malloc (sizeof (*se));
317
318   e = (struct editor_window *)se;
319
320   e->window = GTK_WINDOW (get_widget_assert (xml, "syntax_editor"));
321   text_view = get_widget_assert (xml, "syntax_text_view");
322   se->buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
323   se->lexer = lex_create (the_source_stream);
324
325   g_signal_connect (get_widget_assert (xml,"file_new_syntax"),
326                     "activate",
327                     G_CALLBACK (new_syntax_window),
328                     e->window);
329
330   g_signal_connect (get_widget_assert (xml,"file_open_syntax"),
331                     "activate",
332                     G_CALLBACK (open_syntax_window),
333                     e->window);
334
335   g_signal_connect (get_widget_assert (xml,"file_new_data"),
336                     "activate",
337                     G_CALLBACK (new_data_window),
338                     e->window);
339
340   g_signal_connect (get_widget_assert (xml,"help_about"),
341                     "activate",
342                     G_CALLBACK (about_new),
343                     e->window);
344
345   g_signal_connect (get_widget_assert (xml,"help_reference"),
346                     "activate",
347                     G_CALLBACK (reference_manual),
348                     NULL);
349
350
351   g_signal_connect (get_widget_assert (xml, "file_save"),
352                     "activate",
353                     G_CALLBACK (on_syntax_save),
354                     se);
355
356   g_signal_connect (get_widget_assert (xml, "file_save_as"),
357                     "activate",
358                     G_CALLBACK (on_syntax_save_as),
359                     se);
360
361
362   g_signal_connect (get_widget_assert (xml,"file_quit"),
363                     "activate",
364                     G_CALLBACK (on_quit),
365                     se);
366
367
368   g_signal_connect (get_widget_assert (xml,"run_all"),
369                     "activate",
370                     G_CALLBACK (on_run_all),
371                     se);
372
373
374   g_signal_connect (get_widget_assert (xml,"run_selection"),
375                     "activate",
376                     G_CALLBACK (on_run_selection),
377                     se);
378
379   g_signal_connect (get_widget_assert (xml,"run_current_line"),
380                     "activate",
381                     G_CALLBACK (on_run_current_line),
382                     se);
383
384
385   g_signal_connect (get_widget_assert (xml,"run_to_end"),
386                     "activate",
387                     G_CALLBACK (on_run_to_end),
388                     se);
389
390
391   g_signal_connect (get_widget_assert (xml,"windows_minimise_all"),
392                     "activate",
393                     G_CALLBACK (minimise_all_windows),
394                     NULL);
395
396
397
398   g_object_unref (xml);
399
400   g_signal_connect (e->window, "delete-event",
401                     G_CALLBACK (on_delete), se);
402
403
404
405   return se;
406 }
407
408 /*
409    Callback for the File->New->Syntax menuitem
410 */
411 void
412 new_syntax_window (GtkMenuItem     *menuitem,
413                    gpointer         user_data)
414 {
415   window_create (WINDOW_SYNTAX, NULL);
416 }
417
418
419 /*
420   Save BUFFER to the file called FILENAME.
421   If successful, clears the buffer's modified flag
422 */
423 static gboolean
424 save_editor_to_file (struct syntax_editor *se,
425                      const gchar *filename,
426                      GError **err)
427 {
428   GtkTextBuffer *buffer = se->buffer;
429   gboolean result ;
430   GtkTextIter start, stop;
431   gchar *text;
432
433   gchar *suffixedname;
434   gchar *glibfilename;
435   g_assert (filename);
436
437   suffixedname = append_suffix (filename);
438
439   glibfilename = g_filename_from_utf8 (suffixedname, -1, 0, 0, err);
440
441   g_free ( suffixedname);
442
443   if ( ! glibfilename )
444     return FALSE;
445
446   gtk_text_buffer_get_iter_at_line (buffer, &start, 0);
447   gtk_text_buffer_get_iter_at_offset (buffer, &stop, -1);
448
449   text = gtk_text_buffer_get_text (buffer, &start, &stop, FALSE);
450
451   result =  g_file_set_contents (glibfilename, text, -1, err);
452
453   if ( result )
454     {
455       window_set_name_from_filename ((struct editor_window *) se, filename);
456       gtk_text_buffer_set_modified (buffer, FALSE);
457     }
458
459   return result;
460 }
461
462
463 /*
464   Loads the buffer from the file called FILENAME
465 */
466 gboolean
467 load_editor_from_file (struct syntax_editor *se,
468                        const gchar *filename,
469                        GError **err)
470 {
471   GtkTextBuffer *buffer = se->buffer;
472   gchar *text;
473   GtkTextIter iter;
474
475   gchar *glibfilename = g_filename_from_utf8 (filename, -1, 0, 0, err);
476
477   if ( ! glibfilename )
478     return FALSE;
479
480   /* FIXME: What if it's a very big file ? */
481   if ( ! g_file_get_contents (glibfilename, &text, NULL, err) )
482     {
483       g_free (glibfilename);
484       return FALSE;
485     }
486   g_free (glibfilename);
487
488   gtk_text_buffer_get_iter_at_line (buffer, &iter, 0);
489
490   gtk_text_buffer_insert (buffer, &iter, text, -1);
491
492
493   window_set_name_from_filename ((struct editor_window *)se, filename);
494   gtk_text_buffer_set_modified (buffer, FALSE);
495
496   return TRUE;
497 }
498
499
500 /* Callback for the File->Open->Syntax menuitem */
501 void
502 open_syntax_window (GtkMenuItem *menuitem, gpointer parent)
503 {
504   GtkFileFilter *filter;
505   gint response;
506
507   GtkWidget *dialog =
508     gtk_file_chooser_dialog_new (_("Open Syntax"),
509                                  GTK_WINDOW (parent),
510                                  GTK_FILE_CHOOSER_ACTION_OPEN,
511                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
512                                  GTK_STOCK_OPEN,   GTK_RESPONSE_ACCEPT,
513                                  NULL);
514
515   filter = gtk_file_filter_new ();
516   gtk_file_filter_set_name (filter, _("Syntax Files (*.sps) "));
517   gtk_file_filter_add_pattern (filter, "*.sps");
518   gtk_file_filter_add_pattern (filter, "*.SPS");
519   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
520
521   filter = gtk_file_filter_new ();
522   gtk_file_filter_set_name (filter, _("All Files"));
523   gtk_file_filter_add_pattern (filter, "*");
524   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
525
526   response = gtk_dialog_run (GTK_DIALOG (dialog));
527
528   if (response == GTK_RESPONSE_ACCEPT)
529     {
530       const char *file_name = gtk_file_chooser_get_filename
531         (GTK_FILE_CHOOSER (dialog));
532
533       struct syntax_editor *se = (struct syntax_editor *)
534         window_create (WINDOW_SYNTAX, file_name);
535
536       if ( load_editor_from_file (se, file_name, NULL) )
537 #if RECENT_LISTS_AVAILABLE
538       {
539         GtkRecentManager *manager = gtk_recent_manager_get_default();
540         gchar *uri = g_filename_to_uri (file_name, NULL, NULL);
541
542         gtk_recent_manager_remove_item (manager, uri, NULL);
543         if ( ! gtk_recent_manager_add_item (manager, uri))
544           g_warning ("Could not add item %s to recent list\n",uri);
545
546         g_free (uri);
547       }
548 #else
549       ;
550 #endif
551
552     }
553
554   gtk_widget_destroy (dialog);
555 }
556