1 /*
2 Copyright (C) 2002/2003 Kai Sterker <kai.sterker@gmail.com>
3 Part of the Adonthell Project <http://adonthell.nongnu.org>
4
5 Dlgedit is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 Dlgedit is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with Dlgedit. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /**
20 * @file gui_dlgedit.cc
21 *
22 * @author Kai Sterker
23 * @brief The Dialogue Editor's main window
24 */
25
26 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
29
30 #ifdef MAC_INTEGRATION
31 #include <ige-mac-integration.h>
32 #endif
33
34 #ifdef WIN32
35 #define WIN32_LEAN_AND_MEAN
36 #include <windows.h>
37 #endif
38
39 #define MIME_TYPE "application/x-adonthell-dlg"
40
41 #include <algorithm>
42 #include <gdk-pixbuf/gdk-pixbuf.h>
43 #include <gdk/gdkkeysyms.h>
44 #include <glib/gstdio.h>
45 #include <sys/stat.h>
46 #include <unistd.h>
47 #include <cstring>
48 #include "gettext.h"
49 #include "cfg_data.h"
50 #include "dlg_cmdline.h"
51 #include "dlg_compiler.h"
52 #include "gui_code.h"
53 #include "gui_settings.h"
54 #include "gui_resources.h"
55 #include "gui_dlgedit.h"
56 #include "gui_dlgedit_events.h"
57
58 /**
59 * Point parser to another file to load sub-dialogue.
60 * Defined in loadlg.l
61 */
62 extern void parser_switch_input ();
63
64 /**
65 * Return to previous input file after loading sub-dialogue - or
66 * after failing to do so.
67 * Defined in loadlg.l
68 */
69 extern void parser_restore_input ();
70
71 /**
72 * Global pointer to the main window
73 */
74 GuiDlgedit *GuiDlgedit::window = NULL;
75
76 /**
77 * Strings describing the various program states
78 */
79 const char *GuiDlgedit::progState[NUM_MODES] =
80 { " IDLE", " SELECTED", " HIGHLIGHTED", " DRAGGED", " PREVIEW" };
81
82 // Create the main window
GuiDlgedit()83 GuiDlgedit::GuiDlgedit ()
84 {
85 GtkWidget *vbox;
86 GtkWidget *hbox;
87 GtkWidget *menu;
88 GtkWidget *submenu;
89 GtkWidget *menuitem;
90 GtkWidget *hpaned, *vpaned;
91
92 window = this;
93 number = 0;
94
95 // recent file management
96 std::string cmdline = "-g" + DlgCmdline::datadir + " -p" + DlgCmdline::project;
97 RecentFiles = new GuiRecentFiles ("dlgedit", cmdline);
98 RecentFiles->setListener(this);
99
100 // Statusbar for displaying help and error messages
101 GtkWidget *status_help = gtk_statusbar_new ();
102 // gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR(status_help), FALSE);
103 message = new GuiMessages (status_help);
104
105 // Main Window
106 wnd = gtk_window_new (GTK_WINDOW_TOPLEVEL);
107 gtk_window_set_default_size(GTK_WINDOW(wnd), 900, 700);
108 g_signal_connect (G_OBJECT (wnd), "delete_event", G_CALLBACK (on_widget_destroy), NULL);
109
110 // Menu Accelerators
111 GtkAccelGroup *accel_group = gtk_accel_group_new ();
112
113 // Main Windows Menu
114 menu = gtk_menu_bar_new ();
115
116 // Attach Menubar
117 vbox = gtk_vbox_new (FALSE, 0);
118 gtk_container_add (GTK_CONTAINER (wnd), vbox);
119 gtk_widget_show (vbox);
120
121 gtk_box_pack_start (GTK_BOX (vbox), menu, FALSE, FALSE, 2);
122
123 // File Menu
124 submenu = gtk_menu_new ();
125
126 // New
127 menuitem =gtk_image_menu_item_new_from_stock ("gtk-new", accel_group);
128 gtk_container_add (GTK_CONTAINER (submenu), menuitem);
129 g_object_set_data (G_OBJECT (menuitem), "help-id", GINT_TO_POINTER (1));
130 g_signal_connect (G_OBJECT (menuitem), "enter-notify-event", G_CALLBACK (on_display_help), message);
131 g_signal_connect (G_OBJECT (menuitem), "leave-notify-event", G_CALLBACK (on_clear_help), message);
132 g_signal_connect (G_OBJECT (menuitem), "activate", G_CALLBACK (on_file_new_activate), (gpointer) this);
133 gtk_widget_show (menuitem);
134
135 // Open
136 menuitem = gtk_image_menu_item_new_from_stock ("gtk-open", accel_group);
137 gtk_container_add (GTK_CONTAINER (submenu), menuitem);
138 g_object_set_data (G_OBJECT (menuitem), "help-id", GINT_TO_POINTER (2));
139 g_signal_connect (G_OBJECT (menuitem), "enter-notify-event", G_CALLBACK (on_display_help), message);
140 g_signal_connect (G_OBJECT (menuitem), "leave-notify-event", G_CALLBACK (on_clear_help), message);
141 g_signal_connect (G_OBJECT (menuitem), "activate", G_CALLBACK (on_file_load_activate), (gpointer) this);
142 gtk_widget_show (menuitem);
143
144 // Open Previous >
145 menuitem = gtk_menu_item_new_with_label ("Open Previous");
146 gtk_container_add (GTK_CONTAINER (submenu), menuitem);
147 g_object_set_data (G_OBJECT (menuitem), "help-id", GINT_TO_POINTER (2));
148 g_signal_connect (G_OBJECT (menuitem), "enter-notify-event", G_CALLBACK (on_display_help), message);
149 g_signal_connect (G_OBJECT (menuitem), "leave-notify-event", G_CALLBACK (on_clear_help), message);
150 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), RecentFiles->recentFileMenu());
151
152 // Save
153 menuitem = gtk_image_menu_item_new_from_stock ("gtk-save", accel_group);
154 gtk_container_add (GTK_CONTAINER (submenu), menuitem);
155 g_object_set_data (G_OBJECT (menuitem), "help-id", GINT_TO_POINTER (3));
156 g_signal_connect (G_OBJECT (menuitem), "enter-notify-event", G_CALLBACK (on_display_help), message);
157 g_signal_connect (G_OBJECT (menuitem), "leave-notify-event", G_CALLBACK (on_clear_help), message);
158 g_signal_connect (G_OBJECT (menuitem), "activate", G_CALLBACK (on_file_save_activate), (gpointer) this);
159 menuItem[SAVE] = menuitem;
160
161 // Save As
162 menuitem = gtk_image_menu_item_new_from_stock ("gtk-save-as", accel_group);
163 gtk_container_add (GTK_CONTAINER (submenu), menuitem);
164 g_object_set_data (G_OBJECT (menuitem), "help-id", GINT_TO_POINTER (4));
165 g_signal_connect (G_OBJECT (menuitem), "enter-notify-event", G_CALLBACK (on_display_help), message);
166 g_signal_connect (G_OBJECT (menuitem), "leave-notify-event", G_CALLBACK (on_clear_help), message);
167 g_signal_connect (G_OBJECT (menuitem), "activate", G_CALLBACK (on_file_save_as_activate), (gpointer) this);
168 menuItem[SAVE_AS] = menuitem;
169
170 // Seperator
171 menuitem = gtk_menu_item_new ();
172 gtk_menu_shell_append (GTK_MENU_SHELL (submenu), menuitem);
173 gtk_widget_set_sensitive (menuitem, FALSE);
174
175 // Revert to Saved
176 menuitem = gtk_image_menu_item_new_from_stock ("gtk-revert-to-saved", accel_group);
177 gtk_container_add (GTK_CONTAINER (submenu), menuitem);
178 g_object_set_data (G_OBJECT (menuitem), "help-id", GINT_TO_POINTER (7));
179 g_signal_connect (G_OBJECT (menuitem), "enter-notify-event", G_CALLBACK (on_display_help), message);
180 g_signal_connect (G_OBJECT (menuitem), "leave-notify-event", G_CALLBACK (on_clear_help), message);
181 g_signal_connect (G_OBJECT (menuitem), "activate", G_CALLBACK (on_file_revert_activate), (gpointer) NULL);
182 menuItem[REVERT] = menuitem;
183
184 // Close
185 menuitem = gtk_image_menu_item_new_from_stock ("gtk-close", accel_group);
186 gtk_container_add (GTK_CONTAINER (submenu), menuitem);
187 g_object_set_data (G_OBJECT (menuitem), "help-id", GINT_TO_POINTER (5));
188 g_signal_connect (G_OBJECT (menuitem), "enter-notify-event", G_CALLBACK (on_display_help), message);
189 g_signal_connect (G_OBJECT (menuitem), "leave-notify-event", G_CALLBACK (on_clear_help), message);
190 g_signal_connect (G_OBJECT (menuitem), "activate", G_CALLBACK (on_file_close_activate), (gpointer) NULL);
191 menuItem[CLOSE] = menuitem;
192
193 // Seperator
194 #ifndef MAC_INTEGRATION
195 menuitem = gtk_menu_item_new ();
196 gtk_menu_shell_append (GTK_MENU_SHELL (submenu), menuitem);
197 gtk_widget_set_sensitive (menuitem, FALSE);
198 #endif
199
200 // Quit
201 GtkWidget *quit_item = gtk_image_menu_item_new_from_stock ("gtk-quit", accel_group);
202 gtk_menu_shell_append (GTK_MENU_SHELL (submenu), quit_item);
203 g_object_set_data (G_OBJECT (quit_item), "help-id", GINT_TO_POINTER (6));
204 g_signal_connect (G_OBJECT (quit_item), "enter-notify-event", G_CALLBACK (on_display_help), message);
205 g_signal_connect (G_OBJECT (quit_item), "leave-notify-event", G_CALLBACK (on_clear_help), message);
206 g_signal_connect (G_OBJECT (quit_item), "activate", G_CALLBACK (on_widget_destroy), (gpointer) NULL);
207
208 // Attach File Menu
209 menuitem = gtk_menu_item_new_with_mnemonic ("_File");
210 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu);
211 gtk_container_add (GTK_CONTAINER (menu), menuitem);
212
213 // Dialogue Menu
214 submenu = gtk_menu_new ();
215
216 // Settings
217 menuitem = gtk_image_menu_item_new_with_mnemonic ("_Settings ...");
218 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menuitem), gtk_image_new_from_stock ("gtk-preferences", GTK_ICON_SIZE_MENU));
219 gtk_container_add (GTK_CONTAINER (submenu), menuitem);
220 gtk_widget_add_accelerator (menuitem, "activate", accel_group, GDK_KEY_t, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
221 g_object_set_data (G_OBJECT (menuitem), "help-id", GINT_TO_POINTER (10));
222 g_signal_connect (G_OBJECT (menuitem), "enter-notify-event", G_CALLBACK (on_display_help), message);
223 g_signal_connect (G_OBJECT (menuitem), "leave-notify-event", G_CALLBACK (on_clear_help), message);
224 g_signal_connect (G_OBJECT (menuitem), "activate", G_CALLBACK (on_dialogue_player_activate), (gpointer) NULL);
225 menuItem[SETTINGS] = menuitem;
226
227 // Custom Functions
228 menuitem = gtk_image_menu_item_new_with_mnemonic ("_Python Code ...");
229 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menuitem), gtk_image_new_from_stock ("gtk-justify-fill", GTK_ICON_SIZE_MENU));
230 gtk_container_add (GTK_CONTAINER (submenu), menuitem);
231 gtk_widget_add_accelerator (menuitem, "activate", accel_group, GDK_KEY_p, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
232 g_object_set_data (G_OBJECT (menuitem), "help-id", GINT_TO_POINTER (11));
233 g_signal_connect (G_OBJECT (menuitem), "enter-notify-event", G_CALLBACK (on_display_help), message);
234 g_signal_connect (G_OBJECT (menuitem), "leave-notify-event", G_CALLBACK (on_clear_help), message);
235 g_signal_connect (G_OBJECT (menuitem), "activate", G_CALLBACK (on_dialogue_functions_activate), (gpointer) this);
236 menuItem[FUNCTIONS] = menuitem;
237
238 // Seperator
239 menuitem = gtk_menu_item_new ();
240 gtk_menu_shell_append (GTK_MENU_SHELL (submenu), menuitem);
241 gtk_widget_set_sensitive (menuitem, FALSE);
242
243 // Compile
244 menuitem = gtk_image_menu_item_new_with_mnemonic ("_Compile");
245 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menuitem), gtk_image_new_from_stock ("gtk-convert", GTK_ICON_SIZE_MENU));
246 gtk_container_add (GTK_CONTAINER (submenu), menuitem);
247 gtk_widget_add_accelerator (menuitem, "activate", accel_group, GDK_KEY_c, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
248 g_object_set_data (G_OBJECT (menuitem), "help-id", GINT_TO_POINTER (12));
249 g_signal_connect (G_OBJECT (menuitem), "enter-notify-event", G_CALLBACK (on_display_help), message);
250 g_signal_connect (G_OBJECT (menuitem), "leave-notify-event", G_CALLBACK (on_clear_help), message);
251 g_signal_connect (G_OBJECT (menuitem), "activate", G_CALLBACK (on_dialogue_compile_activate), (gpointer) NULL);
252 menuItem[COMPILE] = menuitem;
253
254 // Preview i18n
255 #ifdef ENABLE_NLS
256 menuitem = gtk_menu_item_new_with_label ("Preview L10n");
257 gtk_container_add (GTK_CONTAINER (submenu), menuitem);
258 gtk_widget_add_accelerator (menuitem, "activate", accel_group, GDK_KEY_v, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
259 g_object_set_data (G_OBJECT (menuitem), "help-id", GINT_TO_POINTER (13));
260 g_signal_connect (G_OBJECT (menuitem), "enter-notify-event", G_CALLBACK (on_display_help), message);
261 g_signal_connect (G_OBJECT (menuitem), "leave-notify-event", G_CALLBACK (on_clear_help), message);
262 g_signal_connect (G_OBJECT (menuitem), "activate", G_CALLBACK (on_dialogue_preview_activate), (gpointer) this);
263 gtk_widget_show (menuitem);
264 menuItem[PREVIEW] = menuitem;
265 #endif
266
267 // Run
268 menuitem = gtk_image_menu_item_new_with_mnemonic ("_Execute ...");
269 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menuitem), gtk_image_new_from_stock ("gtk-execute", GTK_ICON_SIZE_MENU));
270 gtk_container_add (GTK_CONTAINER (submenu), menuitem);
271 gtk_widget_add_accelerator (menuitem, "activate", accel_group, GDK_KEY_e, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
272 g_object_set_data (G_OBJECT (menuitem), "help-id", GINT_TO_POINTER (14));
273 g_signal_connect (G_OBJECT (menuitem), "enter-notify-event", G_CALLBACK (on_display_help), message);
274 g_signal_connect (G_OBJECT (menuitem), "leave-notify-event", G_CALLBACK (on_clear_help), message);
275 g_signal_connect (G_OBJECT (menuitem), "activate", G_CALLBACK (on_dialogue_run_activate), (gpointer) NULL);
276 gtk_widget_show (menuitem);
277 menuItem[RUN] = menuitem;
278
279 // Attach Dialogue Menu
280 menuitem = gtk_menu_item_new_with_mnemonic ("_Dialogue");
281 gtk_widget_show (menuitem);
282 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu);
283 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
284
285 // Window Menu
286 windowMenu = gtk_menu_new ();
287
288 // Attach Window Menu
289 menuitem = gtk_menu_item_new_with_mnemonic ("_Windows");
290 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), windowMenu);
291 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
292
293 gtk_widget_show_all (menu);
294
295 #ifdef MAC_INTEGRATION
296 // Mac OSX-Style menu
297 gtk_widget_hide (menu);
298
299 mainMenu = menu;
300
301 ige_mac_menu_set_menu_bar (GTK_MENU_SHELL (menu));
302 ige_mac_menu_set_quit_menu_item (GTK_MENU_ITEM (quit_item));
303
304 // IgeMacMenuGroup *group = ige_mac_menu_add_app_menu_group ();
305 // ige_mac_menu_add_app_menu_item (group, GTK_MENU_ITEM (quit_item), NULL);
306 #endif
307
308 vpaned = gtk_vpaned_new ();
309 gtk_box_pack_start (GTK_BOX (vbox), vpaned, TRUE, TRUE, 2);
310 gtk_widget_show (vpaned);
311
312 hpaned = gtk_hpaned_new ();
313 gtk_paned_add1 (GTK_PANED (vpaned), hpaned);
314 gtk_widget_show (hpaned);
315
316 // Accelerators
317 gtk_window_add_accel_group (GTK_WINDOW (wnd), accel_group);
318 gtk_widget_realize (wnd);
319
320 // Tree
321 tree_ = new GuiTree (hpaned);
322
323 // Drawing Area
324 graph_ = new GuiGraph (hpaned);
325
326 // List
327 list_ = new GuiList (vpaned);
328
329 // Status bars
330 hbox = gtk_hbox_new (FALSE, 0);
331 g_object_ref (hbox);
332 g_object_set_data_full (G_OBJECT (wnd), "hbox", hbox, (GDestroyNotify) g_object_unref);
333 gtk_widget_show (hbox);
334 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
335 gtk_widget_set_size_request (hbox, -1, 20);
336
337 // help message
338 g_object_ref (status_help);
339 g_object_set_data_full (G_OBJECT (wnd), "status_help", status_help, (GDestroyNotify) g_object_unref);
340 gtk_widget_show (status_help);
341 gtk_box_pack_start (GTK_BOX (hbox), status_help, TRUE, TRUE, 0);
342 gtk_widget_set_size_request (status_help, -1, 20);
343
344 // program mode
345 status_mode = gtk_statusbar_new ();
346 g_object_ref (status_mode);
347 g_object_set_data_full (G_OBJECT (wnd), "status_mode", status_mode, (GDestroyNotify) g_object_unref);
348 gtk_widget_show (status_mode);
349 gtk_box_pack_start (GTK_BOX (hbox), status_mode, FALSE, TRUE, 0);
350 gtk_widget_set_size_request (status_mode, 150, -1);
351
352 // Display MainWindow
353 gtk_widget_show (wnd);
354
355 // set the programm mode
356 setMode (IDLE);
357
358 // Display welcome message
359 message->display (1000);
360
361 // get the current working directory
362 directory_ = g_get_current_dir ();
363
364 // init the resources we will need
365 GuiResources::init (graph_->drawingArea ());
366
367 clear ();
368 }
369
370 // dtor
~GuiDlgedit()371 GuiDlgedit::~GuiDlgedit ()
372 {
373 // cleanup if in preview mode
374 if (mode_ == L10N_PREVIEW)
375 {
376 unlink ("/tmp/locale/xy/LC_MESSAGES/preview.mo");
377 rmdir ("/tmp/locale/xy/LC_MESSAGES");
378 rmdir ("/tmp/locale/xy");
379 rmdir ("/tmp/locale/");
380 }
381 }
382
383 // starts a new dialogue
newDialogue()384 void GuiDlgedit::newDialogue ()
385 {
386 // the new dialogue
387 DlgModule *module = initDialogue ("untitled");
388
389 // Display the dialogue
390 showDialogue (module);
391 }
392
393 // open recent file
OnRecentFileActivated(const std::string & file)394 void GuiDlgedit::OnRecentFileActivated (const std::string & file)
395 {
396 loadDialogue (file);
397 }
398
399 // load a new dialogue
loadDialogue(const std::string & f)400 void GuiDlgedit::loadDialogue (const std::string &f)
401 {
402 // make sure that file has an absolute path
403 std::string file = ((f[0] == '/' || f[1] == ':') ? f : directory_ + std::string ("/") + f);
404 gchar *fname = g_path_get_basename (file.c_str ());
405
406 // test if we have a valid dialogue
407 if (!checkDialogue (file))
408 {
409 message->display (-2, fname);
410 g_free(fname);
411 return;
412 }
413
414 // remember the current directory for later use
415 directory_ = g_dirname (file.c_str ());
416
417 // get the name to use for the dialogue
418 std::string filename (fname);
419
420 // remove file extension
421 unsigned long pos = filename.rfind (FILE_EXT);
422 if (pos != filename.npos) filename.erase (pos);
423
424 // the new dialogue
425 DlgModule *module = initDialogue (filename);
426
427 // try to load from file
428 if (!module->load ())
429 {
430 message->display (-3, filename.c_str ());
431 closeDialogue ();
432 }
433
434 // Display the dialogue
435 else
436 {
437 // update list of previously opened files
438 RecentFiles->registerFile(file, MIME_TYPE);
439
440 message->display (200);
441 showDialogue (module, true);
442 }
443
444 g_free(fname);
445 }
446
447 // load a sub-dialogue
loadSubdialogue(const std::string & file)448 DlgModule* GuiDlgedit::loadSubdialogue (const std::string &file)
449 {
450 // test if we have a valid dialogue
451 if (!checkDialogue (file)) return NULL;
452
453 // remember the current directory for later use
454 directory_ = g_dirname (file.c_str ());
455
456 // get the name to use for the dialogue
457 gchar *fname = g_path_get_basename (file.c_str ());
458 std::string filename (fname);
459 g_free (fname);
460
461 // remove file extension
462 unsigned long pos = filename.rfind (FILE_EXT);
463 if (pos != filename.npos) filename.erase (pos);
464
465 // the sub-dialogue
466 DlgModule *module = new DlgModule (directory_, filename, "", "");
467
468 // parser needs to read from sub-dialogue source file
469 parser_switch_input ();
470
471 // try to load from file
472 if (!module->load ())
473 {
474 delete module;
475 parser_restore_input ();
476 return NULL;
477 }
478
479 // return the sub-dialogue
480 return module;
481 }
482
483 // revert Dialogue to state on disk
revertDialogue()484 void GuiDlgedit::revertDialogue ()
485 {
486 DlgModule *module = graph_->dialogue ();
487 if (module == NULL) return;
488
489 // check whether dialogue (still) exists on disk
490 if (!checkDialogue (module->fullName ()))
491 {
492 gtk_widget_set_sensitive (menuItem[REVERT], FALSE);
493 message->display (-2, module->name ().c_str ());
494 return;
495 }
496
497 // cleanup
498 module->clear ();
499
500 // reload
501 if (!module->load ())
502 {
503 message->display (-3, module->name ().c_str ());
504 closeDialogue ();
505 return;
506 }
507
508 // redisplay
509 graph_->detachModule ();
510 tree_->updateModule (module);
511 graph_->attachModule (module);
512 }
513
514 // save a dialogue
saveDialogue(const std::string & file)515 void GuiDlgedit::saveDialogue (const std::string &file)
516 {
517 DlgModule *module = graph_->dialogue ();
518 if (module == NULL) return;
519
520 // remember the current directory for later use
521 directory_ = g_dirname (file.c_str ());
522
523 // get the filename
524 gchar *fname = g_path_get_basename (file.c_str ());
525 std::string filename (fname);
526 g_free (fname);
527
528 // remove file extension
529 unsigned long pos = filename.rfind (FILE_EXT);
530 if (pos != filename.npos) filename.erase (pos);
531
532 // try to save file
533 if (!module->save (directory_, filename))
534 message->display (-4, filename.c_str ());
535 else
536 {
537 message->display (201);
538
539 // update list of previously opened files
540 RecentFiles->registerFile(file, MIME_TYPE);
541
542 // update 'Revert to Saved' menu item
543 gtk_widget_set_sensitive (menuItem[REVERT], TRUE);
544
545 // update the dialogue's name in case it has changed
546 tree_->setName (module);
547 initMenu ();
548 }
549 }
550
551 // close the dialogue being displayed
closeDialogue()552 void GuiDlgedit::closeDialogue ()
553 {
554 DlgModule *module = graph_->dialogue ();
555 if (module == NULL) return;
556
557 // check whether dialogue has been saved
558 // if (module->hasChanged ())
559
560 // remove the dialogue from the list of open dialogues
561 dialogues_.erase (remove (dialogues_.begin (), dialogues_.end (), module), dialogues_.end ());
562
563 // detach module
564 graph_->detachModule ();
565
566 // if another dialogue is open, display that one
567 if (dialogues_.size () > 0) showDialogue (dialogues_.front ());
568 // otherwise just clear the GUI
569 else clear ();
570
571 // rebuild the 'windows' menu
572 initMenu ();
573
574 // delete the dialogue
575 delete module;
576 }
577
578 // display a certain dialogue
showDialogue(DlgModule * module,bool center)579 void GuiDlgedit::showDialogue (DlgModule *module, bool center)
580 {
581 // remove the current module from the view
582 graph_->detachModule ();
583
584 // update the tree view
585 // NOTE that this method does some magic: it will select and attach
586 // the sub-dialogue of 'module' that has been viewed before. In that
587 // case, 'module' must not be attached, as it is the toplevel dialogue.
588 tree_->addModule (module);
589
590 // attach the dialogue to the view
591 // In case of a newly created or (re)loaded 'module', none of it's
592 // sub-dialogues will have been in the view. Therefore, the call
593 // above can't attach anything. Therefore we can attach 'module'.
594 if (!graph_->getAttached ()) graph_->attachModule (module, center);
595
596 // update the custom code entry if neccessary
597 if (GuiCode::dialog != NULL)
598 GuiCode::dialog->display (module->entry (), module->shortName ());
599
600 // update the settings if neccessary
601 if (GuiSettings::dialog != NULL)
602 GuiSettings::dialog->display (module->entry (), module->shortName ());
603
604 // update 'Revert to Saved' menu item
605 if (!checkDialogue (module->fullName ()))
606 gtk_widget_set_sensitive (menuItem[REVERT], FALSE);
607 else
608 gtk_widget_set_sensitive (menuItem[REVERT], TRUE);
609
610 // update the window title
611 initTitle ();
612 }
613
614 // compile a dialogue
compileDialogue()615 void GuiDlgedit::compileDialogue ()
616 {
617 DlgModule *module = graph_->dialogue ();
618 if (module == NULL) return;
619
620 // init the compiler
621 DlgCompiler compiler (module);
622
623 // compile
624 compiler.run ();
625
626 // enable the 'run' menuitem after successful compilation
627 gtk_widget_set_sensitive (menuItem[RUN], TRUE);
628
629 // report success
630 message->display (212);
631 }
632
633 // edit the genral dialogu settings
settings()634 void GuiDlgedit::settings ()
635 {
636 DlgModule *module = graph_->dialogue ();
637 if (module == NULL) return;
638
639 // if the dialog isn't already open ...
640 if (GuiSettings::dialog == NULL)
641 GuiSettings::dialog = new GuiSettings ();
642
643 // otherwise just show it
644 std::string project = module->entry ()->project ();
645 GuiSettings::dialog->display (module->entry (), module->shortName ());
646 }
647
648 // edit custom code of current module
customCode()649 void GuiDlgedit::customCode ()
650 {
651 DlgModule *module = graph_->dialogue ();
652 if (module == NULL) return;
653
654 // if the dialog isn't already open ...
655 if (GuiCode::dialog == NULL)
656 GuiCode::dialog = new GuiCode ();
657
658 // otherwise just show it
659 GuiCode::dialog->display (module->entry (), module->shortName ());
660 }
661
662 // preview the translated dialogue
previewTranslation(const std::string & catalogue)663 void GuiDlgedit::previewTranslation (const std::string &catalogue)
664 {
665 DlgModule *module = graph_->dialogue ();
666 if (module == NULL) return;
667
668 // check if we have a proper catalogue
669 if (strncmp (catalogue.substr (catalogue.length ()-3).c_str (), ".mo", 3))
670 {
671 gchar *cname = g_path_get_basename (catalogue.c_str ());
672 message->display (-130, cname);
673 g_free (cname);
674 return;
675 }
676
677 // see if the file exists at all
678 FILE *exists = g_fopen (catalogue.c_str (), "rb");
679
680 if (!exists)
681 {
682 gchar *cname = g_path_get_basename (catalogue.c_str ());
683 message->display (-2, cname);
684 g_free (cname);
685 return;
686 }
687 // if it does, check magic number of catalogue file
688 else
689 {
690 unsigned int magic;
691
692 // read magic number
693 fread (&magic, 4, 1, exists);
694 fclose (exists);
695
696 if (magic != 0x950412de)
697 {
698 gchar *cname = g_path_get_basename (catalogue.c_str ());
699 message->display (-130, cname);
700 g_free (cname);
701 return;
702 }
703 }
704
705 // create temporary locale directory
706 #ifndef WIN32
707 if (mkdir ("/tmp/locale/", 0750) ||
708 mkdir ("/tmp/locale/xy", 0750) ||
709 mkdir ("/tmp/locale/xy/LC_MESSAGES", 0750))
710 #else
711 if (mkdir ("tmp/locale/") ||
712 mkdir ("tmp/locale/xy") ||
713 mkdir ("tmp/locale/xy/LC_MESSAGES"))
714 #endif
715 {
716 message->display (-131);
717 return;
718 }
719
720 #ifndef WIN32
721 // create a symlink to the given catalogue
722 symlink (catalogue.c_str (), "/tmp/locale/xy/LC_MESSAGES/preview.mo");
723
724 // set the language to use
725 setenv ("LANGUAGE", "xy", 1);
726 #else
727 // create a symlink to the given catalogue
728 // VISTA only: CreateSymbolicLink ()
729
730 // set the language to use
731 SetEnvironmentVariable ("LANGUAGE", "xy");
732 #endif
733
734 #ifdef ENABLE_NLS
735 setlocale(LC_MESSAGES, "xy");
736
737 {
738 // tell gettext that the language has changed
739 extern int _nl_msg_cat_cntr;
740 ++_nl_msg_cat_cntr;
741 }
742 #endif
743
744 // open the catalogue
745 bindtextdomain ("preview", "/tmp/locale");
746 textdomain ("preview");
747
748 // deselect selected node, if any
749 DlgNode *node = module->deselectNode ();
750
751 // update menuitem
752 gtk_label_set_text (GTK_LABEL (gtk_bin_get_child (GTK_BIN(menuItem[PREVIEW]))), "Exit Preview mode");
753
754 // set program mode
755 setMode (L10N_PREVIEW);
756 message->display (130);
757
758 // reselect node with proper translation
759 if (node != NULL) module->selectNode (node);
760 }
761
762 // stop preview mode
exitPreview()763 void GuiDlgedit::exitPreview ()
764 {
765 DlgModule *module = graph_->dialogue ();
766
767 // clear the program mode
768 setMode (NUM_MODES);
769
770 // restore the program mode
771 if (module != NULL)
772 {
773 setMode (module->state ());
774
775 // deselect selected node, if any
776 DlgNode *node = module->deselectNode ();
777
778 // reselect node without translation
779 if (node != NULL) module->selectNode (node);
780 }
781
782 // update menuitem
783 gtk_label_set_text (GTK_LABEL (gtk_bin_get_child (GTK_BIN(menuItem[PREVIEW]))), "Preview Translation");
784
785 // cleanup
786 unlink ("/tmp/locale/xy/LC_MESSAGES/preview.mo");
787 rmdir ("/tmp/locale/xy/LC_MESSAGES");
788 rmdir ("/tmp/locale/xy");
789 rmdir ("/tmp/locale/");
790
791 // clear the statusbar
792 message->clear ();
793 }
794
setChanged()795 void GuiDlgedit::setChanged ()
796 {
797 // update tree
798 tree_->setChanged (graph_->dialogue ());
799
800 // update title bar
801 initTitle ();
802 }
803
updateProject()804 void GuiDlgedit::updateProject ()
805 {
806 // update tree
807 tree_->updateProject (graph_->dialogue ());
808 }
809
checkDialogue(const std::string & file)810 bool GuiDlgedit::checkDialogue (const std::string &file)
811 {
812 // first, open the file
813 FILE *test = g_fopen (file.c_str (), "rb");
814
815 if (!test)
816 return false;
817
818 // check if it's a regular file
819 struct stat statbuf;
820 fstat (fileno (test), &statbuf);
821 if (!S_ISREG (statbuf.st_mode))
822 {
823 fclose (test);
824 return false;
825 }
826
827 loadlgin = test;
828 return true;
829 }
830
initDialogue(std::string name)831 DlgModule *GuiDlgedit::initDialogue (std::string name)
832 {
833 // serial number
834 std::string serial = g_strdup_printf ("-%i", ++number);
835
836 // the new dialogue
837 DlgModule *dlg = new DlgModule (directory_, name, serial, "New Dialogue");
838
839 // set project if dlgedit started with '-j' option
840 dlg->entry()->setProject (DlgCmdline::project);
841
842 // insert into the list of open dialogues
843 dialogues_.push_back (dlg);
844
845 // rebuild the 'Window' menu
846 initMenu ();
847
848 // activate all dialogue related menu-items
849 gtk_widget_set_sensitive (menuItem[SAVE], TRUE);
850 gtk_widget_set_sensitive (menuItem[SAVE_AS], TRUE);
851 gtk_widget_set_sensitive (menuItem[CLOSE], TRUE);
852 gtk_widget_set_sensitive (menuItem[SETTINGS], TRUE);
853 gtk_widget_set_sensitive (menuItem[FUNCTIONS], TRUE);
854 gtk_widget_set_sensitive (menuItem[COMPILE], TRUE);
855 #ifdef ENABLE_NLS
856 gtk_widget_set_sensitive (menuItem[PREVIEW], TRUE);
857 #endif
858
859 return dlg;
860 }
861
862 // sets the window title
initTitle()863 void GuiDlgedit::initTitle ()
864 {
865 gchar *title = (char*) "Adonthell Dialogue Editor v" _VERSION_;
866 DlgModule *module = graph_->dialogue ();
867
868 if (module != NULL)
869 {
870 if (module->changed ())
871 title = g_strjoin (NULL, title, " - [",
872 module->shortName ().c_str (), " (modified)]", NULL);
873 else
874 title = g_strjoin (NULL, title, " - [",
875 module->shortName ().c_str (), "]", NULL);
876 }
877 gtk_window_set_title (GTK_WINDOW (wnd), title);
878 }
879
880 // init the 'windows' menu
initMenu()881 void GuiDlgedit::initMenu ()
882 {
883 // first, remove everything from the menu
884 gtk_container_foreach (GTK_CONTAINER (windowMenu), (GtkCallback)gtk_widget_destroy, NULL);
885
886 // Now rebuild the menu if any dialogues remain
887 if (dialogues_.size () == 0) return;
888
889 int position = 0;
890 std::vector<DlgModule*>::iterator i;
891 // GtkAccelGroup *accel_group = gtk_accel_group_get_default ();
892 GtkWidget *menuitem;
893
894 for (i = dialogues_.begin (); i != dialogues_.end (); i++, position++)
895 {
896 menuitem = gtk_menu_item_new_with_label ((*i)->shortName ().c_str ());
897 gtk_container_add (GTK_CONTAINER (windowMenu), menuitem);
898 g_signal_connect (G_OBJECT (menuitem), "activate", G_CALLBACK (on_window_activate), (gpointer) *i);
899 g_object_set_data (G_OBJECT (menuitem), "help-id", GINT_TO_POINTER (20));
900 g_signal_connect (G_OBJECT (menuitem), "enter-notify-event", G_CALLBACK (on_display_help), message);
901 g_signal_connect (G_OBJECT (menuitem), "leave-notify-event", G_CALLBACK (on_clear_help), message);
902 gtk_widget_show (menuitem);
903
904 // Menu Accelerators
905 // if (position < 9)
906 // gtk_widget_add_accelerator (menuitem, "activate", accel_group,
907 // GDK_1 + position, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
908 }
909
910 #ifdef MAC_INTEGRATION
911 ige_mac_menu_sync(GTK_MENU_SHELL(mainMenu));
912 #endif
913 }
914
clear()915 void GuiDlgedit::clear ()
916 {
917 // update the window title
918 initTitle ();
919
920 // make the various menu-items insensitive
921 gtk_widget_set_sensitive (menuItem[SAVE], FALSE);
922 gtk_widget_set_sensitive (menuItem[SAVE_AS], FALSE);
923 gtk_widget_set_sensitive (menuItem[REVERT], FALSE);
924 gtk_widget_set_sensitive (menuItem[CLOSE], FALSE);
925 gtk_widget_set_sensitive (menuItem[SETTINGS], FALSE);
926 gtk_widget_set_sensitive (menuItem[FUNCTIONS], FALSE);
927 gtk_widget_set_sensitive (menuItem[COMPILE], FALSE);
928 #ifdef ENABLE_NLS
929 gtk_widget_set_sensitive (menuItem[PREVIEW], FALSE);
930 #endif
931 gtk_widget_set_sensitive (menuItem[RUN], FALSE);
932
933 // empty the graph and list widget
934 graph_->clear ();
935 list_->clear ();
936 }
937
setMode(mode_type mode)938 void GuiDlgedit::setMode (mode_type mode)
939 {
940 const char *text;
941
942 // get the string representing the current program state
943 if (mode < IDLE || mode >= NUM_MODES)
944 {
945 text = " INVALID MODE";
946 mode_ = IDLE;
947 }
948 else
949 {
950 // ignore everything else as long as we are in preview mode
951 if (mode_ == L10N_PREVIEW) return;
952
953 text = progState[mode];
954 mode_ = mode;
955 }
956
957 // some context id the statusbar needs for some reason
958 int id = gtk_statusbar_get_context_id (GTK_STATUSBAR (status_mode), "Mode");
959
960 // remove the previous message
961 gtk_statusbar_pop (GTK_STATUSBAR (status_mode), id);
962
963 // add the new status
964 gtk_statusbar_push (GTK_STATUSBAR (status_mode), id, text);
965 }
966
967 // get the full path/name/extension of a dialogue
filename()968 std::string GuiDlgedit::filename ()
969 {
970 DlgModule *module = graph_->dialogue ();
971
972 return module->fullName ();
973 }
974