1 /* App.c - functions for processing the app. struct
2 *
3 * Copyright (C) 2001, 2010 Patrice St-Gelais
4 * patrstg@users.sourceforge.net
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
20
21 #include <gtk/gtk.h>
22 #include <gdk/gdkrgb.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <math.h>
27 #include <unistd.h>
28
29 #include "../icons/create.xpm"
30 #include "../icons/open.xpm"
31 #include "../icons/save.xpm"
32 #include "../icons/save_as.xpm"
33 #include "../icons/save_copy_as.xpm"
34 // #include "../icons/print.xpm"
35 #include "../icons/close.xpm"
36 #include "../icons/quit.xpm"
37 #include "../icons/undo.xpm"
38 #include "../icons/redo.xpm"
39 // #include "../icons/copy.xpm"
40 // #include "../icons/cut.xpm"
41 // #include "../icons/paste.xpm"
42 // #include "../icons/paste_special.xpm"
43 #include "../icons/stats.xpm"
44 #include "../icons/settings.xpm"
45 #include "../icons/help.xpm"
46 #include "../icons/about.xpm"
47
48 #include "app.h"
49 #include "doc.h"
50
51 gint ok_create_callb(GtkWidget *, gpointer);
52 gint cancel_create_callb(GtkWidget *,gpointer);
53
54 #define NBCOMMANDS 16
55 command_item_struct commands[NBCOMMANDS] = {
56 { "File", "New", "Create a new document", 'N', (gchar **) create_xpm, GDK_LEFT_PTR, create_callb, NULL, NULL,FALSE },
57 { "File", "Open", "Open a file",'O', (gchar **) open_xpm, GDK_LEFT_PTR, open_callb,NULL, NULL,FALSE },
58 { "File", "Save", "Save the current document", 'S', (gchar **) save_xpm, GDK_LEFT_PTR, save_callb,NULL, NULL,FALSE },
59 { "File", "Save as...", "Save under a new name", 0, (gchar **) save_as_xpm, GDK_LEFT_PTR, save_as_callb,NULL, NULL, FALSE },
60 { "File", "Copy as...", "Copy under a new name", 0, (gchar **) save_copy_as_xpm, GDK_LEFT_PTR, save_copy_as_callb,NULL, NULL, FALSE },
61 // "File", "Print", PRINT_TOOLTIP, 'P', (gchar **) print_xpm, GDK_LEFT_PTR, print_callb,NULL, NULL, FALSE },
62 { "File", NULL, NULL, 0, NULL, 0, NULL,NULL, NULL, FALSE }, // Separator
63 { "File", "Close", "Close the current document", 'W', (gchar **) close_xpm, GDK_LEFT_PTR, close_callb,NULL, NULL, FALSE },
64 { "File", "Quit", "Quit application", 'Q', (gchar **) quit_xpm, GDK_LEFT_PTR, quit_callb,NULL, NULL, FALSE },
65 { "Edit", "Undo", "Undo",'Z', (gchar **) undo_xpm, GDK_LEFT_PTR, undo_callb,NULL, NULL, FALSE },
66 { "Edit", "Redo", "Redo", 'Y', (gchar **) redo_xpm, GDK_LEFT_PTR, redo_callb,NULL, NULL, FALSE },
67 // { "Edit", NULL, NULL, 0, NULL, 0, NULL,NULL, NULL, FALSE }, // Separator
68 // { "Edit", "Copy", "Copy", 'C', (gchar **) copy_xpm, GDK_LEFT_PTR, copy_callb,NULL, NULL, FALSE },
69 // { "Edit", "Cut", "Cut", 'X', (gchar **) cut_xpm, GDK_LEFT_PTR, cut_callb,NULL, NULL, FALSE },
70 // { "Edit", "Paste", "Paste", 'V',(gchar **) paste_xpm, GDK_LEFT_PTR, paste_callb,NULL, NULL, FALSE },
71 // { "Edit", "Paste special", "Paste special (merge...)", 'T', (gchar **) paste_special_xpm, GDK_LEFT_PTR, paste_special_callb,NULL, NULL, FALSE },
72 { "Tools", NULL, NULL, 0, NULL, 0, NULL,NULL, NULL, FALSE }, // Separator
73 { "Tools", "Statistics", "Information and statistics about the current document", 0, (gchar **) stats_xpm, GDK_LEFT_PTR, stats_callb,NULL, NULL, FALSE },
74 { "Tools", NULL, NULL, 0, NULL, 0, NULL, NULL, NULL, FALSE }, // Separator
75 { "Tools", "Options", "Options at the application launch", 0, (gchar **) settings_xpm, GDK_LEFT_PTR, options_callb,NULL, NULL, FALSE },
76 { "?", "Guide", "User guide", 'H', (gchar **) help_xpm, GDK_LEFT_PTR, help_callb, NULL, NULL, FALSE },
77 { "?", "About...", "Application and author", 0, (gchar **) about_xpm, GDK_LEFT_PTR, about_callb, NULL, NULL, FALSE }
78 };
79
80
81 // Global variables coming from the RC file or defined in globals.h
82 gint DEF_PAD,MAIN_BAR_X, MAIN_BAR_Y, MAX_HISTORY;
83 gint TOOLS_X, TOOLS_Y, CREATION_X, CREATION_Y, DISPLAY_DOC_OFFSET;
84 guint DEFAULT_SEED;
85 gchar *DEF_DIR=NULL, *DOC_READER, *DOC_DIR;
86 gboolean INTEGRATED_INTERFACE, MENU_IN_DOC_WINDOW, ICONS_IN_DOC_WINDOW;
87
process_options(option_file_type * options)88 void process_options(option_file_type *options) {
89 // Process some generic defaults from the RC file
90 // DEFAULT_PAD, MAXIMUM_HISTORY... are #defined in globals.h
91 // DEF_PAD, MAX_HISTORY are declared "extern" in globals.h as well
92 gchar *buf, *current_dir, *home;
93 gboolean invalid;
94
95 DEF_PAD = DEFAULT_PAD;
96 if ( (buf = get_option(options,"interface","pad"))) {
97 if (is_integer(buf)) {
98 DEF_PAD = (gint) atol(buf);
99 invalid = FALSE;
100 }
101 else
102 invalid = TRUE;
103 }
104 if ((!buf) || invalid) {
105 buf = (gchar *) x_malloc(5, "gchar (buf - interface:pad)");
106 sprintf(buf,"%-4d", DEF_PAD);
107 put_option(options,"interface","pad",buf);
108 }
109
110 if ((buf = get_option(options,"files","def_dir")) && strlen(buf))
111 DEF_DIR = buf;
112 else
113 if (!DEF_DIR) {
114 // DEF_DIR is supposed to be initialized by main.c
115 // If not, it defaults to $HOME
116 DEF_DIR = getenv("HOME");
117 put_option(options,"files","def_dir",DEF_DIR);
118 }
119 // DEF_DIR should have a full path.
120 // If not (like in Geomorph version prior to 0.50),
121 // we try to build it
122 if (DEF_DIR[0] != FILESEP) {
123 // First try to test for a subdirectory
124 // in the current one, then in the home directory
125 current_dir = (gchar *) get_current_dir_name();
126 // current_dir has no FILESEP at the end
127 buf = (gchar *) x_malloc(strlen(current_dir)+2+strlen(DEF_DIR), "gchar (buf - DEF_DIR)");
128 sprintf(buf,"%s%c%s",current_dir,FILESEP,DEF_DIR);
129 // printf("CURRENT_DIR: %s; BUF: %s\n",current_dir,buf);
130 if (directory_exists(buf))
131 DEF_DIR = buf;
132 else {
133 // We try with the home directory
134 x_free(buf);
135 home = getenv("HOME");
136 buf = (gchar *) x_malloc(strlen(home)+2+strlen(DEF_DIR), "gchar (buf - DEF_DIR)");
137 sprintf(buf,"%s%c%s", home, FILESEP, DEF_DIR);
138 if (directory_exists(buf))
139 DEF_DIR = buf;
140 else {
141 DEF_DIR = home;
142 x_free(buf);
143 }
144 // printf("DEF_DIR après BUF: %s\n",DEF_DIR);
145 }
146 }
147
148 MAX_HISTORY = MAXIMUM_HISTORY;
149 if ((buf = get_option(options,"application","max_history"))) {
150 if (is_integer(buf)) {
151 MAX_HISTORY = atoi(buf);
152 invalid = FALSE;
153 }
154 else
155 invalid = TRUE;
156 }
157 if ((!buf) || invalid) {
158 buf = (gchar *) x_malloc(5, "gchar (buf - put_option)");
159 sprintf(buf,"%-4d",MAX_HISTORY);
160 put_option(options,"application","max_history",buf);
161 }
162
163 DEFAULT_SEED = rand();
164 if ((buf = get_option(options,"application","default_seed"))) {
165 // On some systems, is_integer does not recognizes Hex numbers (0x01b5...)
166 // if (is_integer(buf))
167 DEFAULT_SEED = (guint) strtoul(buf,NULL,0);
168 }
169
170 CREATION_X = CREATION_WINDOW_X;
171 if ((buf = get_option(options,"interface","creation_window_x"))) {
172 if (is_integer(buf))
173 CREATION_X = atol(buf);
174 }
175
176 CREATION_Y = CREATION_WINDOW_Y;
177 if ((buf = get_option(options,"interface","creation_window_y"))) {
178 if (is_integer(buf))
179 CREATION_Y = atol(buf);
180 }
181
182 TOOLS_X = TOOLS_WINDOW_X;
183 if ((buf = get_option(options,"interface","tools_window_x"))) {
184 if (is_integer(buf))
185 TOOLS_X = atol(buf);
186 }
187
188 TOOLS_Y = TOOLS_WINDOW_Y;
189 if ((buf = get_option(options,"interface","tools_window_y"))) {
190 if (is_integer(buf))
191 TOOLS_Y = atol(buf);
192 }
193
194 MAIN_BAR_X = MAIN_MENU_BAR_X;
195 if ((buf = get_option(options,"interface","main_bar_x"))) {
196 if (is_integer(buf))
197 MAIN_BAR_X = atol(buf);
198 }
199
200 MAIN_BAR_Y = MAIN_MENU_BAR_Y;
201 if ((buf = get_option(options,"interface","main_bar_y"))) {
202 if (is_integer(buf))
203 MAIN_BAR_Y = atol(buf);
204 }
205
206 DISPLAY_DOC_OFFSET = DEFAULT_DOC_OFFSET;
207 if ((buf = get_option(options,"interface","display_doc_offset"))) {
208 if (is_integer(buf))
209 DISPLAY_DOC_OFFSET = atol(buf);
210 }
211
212 if ((buf = get_option(options,"files","doc_dir")) && strlen(buf))
213 DOC_DIR = buf;
214 else {
215 DOC_DIR = DOCUMENT_DIR;
216 put_option(options,"files","doc_dir",DOC_DIR);
217 }
218
219 if ((buf = get_option(options,"files","doc_reader")) && strlen(buf))
220 DOC_READER = buf;
221 else {
222 DOC_READER = DEFAULT_DOC_READER;
223 put_option(options,"files","doc_reader",DOC_READER);
224 }
225
226 }
227
process_all_options(option_file_type * options)228 void process_all_options(option_file_type *options) {
229 if (options) {
230 process_options(options);
231 process_specific_options(options); // Function given by thisappinit.h
232 }
233 }
234
check_integrated_interface(option_file_type * options)235 void check_integrated_interface (option_file_type *options) {
236
237 gchar *buf;
238 // The INTEGRATED option cannot be executed in process_options,
239 // because it shouldn't be reinitialized when geomorphrc
240 // is modified without restarting the application
241 if ((buf = get_option(options,"interface","interface_style"))){
242 if (!strcmp(buf,"Integrated"))
243 INTEGRATED_INTERFACE = TRUE;
244 else
245 INTEGRATED_INTERFACE = FALSE;
246 }
247 else {
248 INTEGRATED_INTERFACE = DEF_INTEGRATED_INTERFACE;
249 if (INTEGRATED_INTERFACE)
250 put_option (options, "interface","interface_style","Integrated");
251 else
252 put_option (options,"interface","interface_style","Gimp style");
253 }
254
255 // Menu mandatory with integrated interface
256 if ((buf = get_option(options,"interface","menu_in_doc_window"))){
257 if (!strcmp(buf,"TRUE"))
258 MENU_IN_DOC_WINDOW = TRUE;
259 else
260 MENU_IN_DOC_WINDOW = INTEGRATED_INTERFACE;
261 }
262 else {
263 MENU_IN_DOC_WINDOW = INTEGRATED_INTERFACE;
264 if (MENU_IN_DOC_WINDOW)
265 put_option (options,"interface","menu_in_doc_window","TRUE");
266 else
267 put_option (options,"interface","menu_in_doc_window","FALSE");
268 }
269
270 // When the ICONS in the main menu option is not specified,
271 // it defaults to TRUE for the integrated interface
272 if ((buf = get_option(options,"interface","icons_in_doc_window"))){
273 if (!strcmp(buf,"TRUE"))
274 ICONS_IN_DOC_WINDOW = TRUE;
275 else
276 ICONS_IN_DOC_WINDOW = INTEGRATED_INTERFACE;
277 }
278 else {
279 ICONS_IN_DOC_WINDOW = INTEGRATED_INTERFACE;
280 if (ICONS_IN_DOC_WINDOW)
281 put_option (options,"interface", "icons_in_doc_window", "TRUE");
282 else
283 put_option (options,"interface", "icons_in_doc_window", "FALSE");
284 }
285 }
286
287
app_new(gchar * title,option_file_type * current_opt,option_file_type * allowed_opt,gchar * options_file)288 app_struct *app_new( gchar *title,
289 option_file_type *current_opt,
290 option_file_type *allowed_opt,
291 gchar *options_file) {
292 // Initializes application
293
294 app_struct *app;
295 app = (app_struct *) x_malloc(sizeof(app_struct), "app_struct");
296
297 app->main_bar_pos_x = MAIN_BAR_X*gdk_screen_width()/100;
298 app->main_bar_pos_y = MAIN_BAR_Y*gdk_screen_height()/100;
299
300 app->default_dir = (gchar *) x_malloc(strlen(HF_DIR)+1, "gchar (HF_DIR)");
301 strcpy(app->default_dir, HF_DIR);
302 app->file_on_cmdline = NULL;
303 app->title = title;
304 app->docs = doc_swap_new();
305 app->new_doc_count = 0;
306
307 app->rc_options_file = options_file;
308 app->allowed_options = allowed_opt;
309 app->current_options = current_opt;
310 app->options_dialog = NULL;
311 app->stack = stack_struct_new (NULL);
312
313 // printf("APP: %p; APP->STACK: %p\n",app, app->stack);
314
315 check_integrated_interface (allowed_opt);
316
317 app->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
318
319 gtk_widget_realize(GTK_WIDGET(app->window));
320
321 gtk_widget_set_uposition(GTK_WIDGET(app->window),
322 app->main_bar_pos_x, app->main_bar_pos_y);
323
324 app->types = instantiate_types(app->window);
325
326 if (!INTEGRATED_INTERFACE)
327 gtk_window_set_resizable(GTK_WINDOW(app->window),FALSE);
328
329 gtk_signal_connect (GTK_OBJECT (app->window), "delete_event",
330 GTK_SIGNAL_FUNC(app_menu_delete),
331 (gpointer) app);
332 gtk_window_set_title (GTK_WINDOW (app->window), app->title);
333 gtk_container_border_width (GTK_CONTAINER(app->window),DEF_PAD);
334
335 app->accel_group = gtk_accel_group_new();
336 gtk_window_add_accel_group (GTK_WINDOW(app->window), app->accel_group);
337
338 app->tooltips = gtk_tooltips_new();
339
340 if ((!INTEGRATED_INTERFACE) || (!MENU_IN_DOC_WINDOW))
341 app->menu = menu_new(NBCOMMANDS,commands, app->accel_group, (gpointer) app);
342 else
343 app->menu = NULL;
344
345 if ((!INTEGRATED_INTERFACE) || (!ICONS_IN_DOC_WINDOW))
346 app->toolbar = standard_toolbar_new(NBCOMMANDS,
347 commands,
348 app->tooltips,
349 app->window,
350 (gpointer) app,
351 GTK_ORIENTATION_HORIZONTAL,
352 GTK_TOOLBAR_ICONS,
353 FALSE);
354 else
355 app->toolbar = NULL;
356
357 if (!INTEGRATED_INTERFACE) {
358 creation_container_new(app->types);
359 tools_container_new(app->types);
360 // We need the accelerators for the tools window,
361 // but not for the creation window, which is modal
362 if (GTK_IS_WINDOW(app->types->tools_container))
363 gtk_window_add_accel_group (GTK_WINDOW(app->types->tools_container), app->accel_group);
364 }
365 return(app);
366 }
367
app_show(app_struct * app)368 void app_show(app_struct *app) {
369
370 GtkWidget *mainbox;
371
372 // Eventually shows doc list
373 // Eventually shows version list (undo / redo)
374
375 if ((!INTEGRATED_INTERFACE) || ((!MENU_IN_DOC_WINDOW) && (!ICONS_IN_DOC_WINDOW))) {
376 mainbox = gtk_vbox_new(FALSE,DEF_PAD);
377
378 if (GTK_IS_WIDGET(app->menu->bar))
379 gtk_box_pack_start(GTK_BOX(mainbox), app->menu->bar, TRUE, TRUE, 0);
380 if (GTK_IS_WIDGET(app->toolbar))
381 gtk_box_pack_start(GTK_BOX(mainbox), app->toolbar, TRUE, TRUE, 0);
382
383 gtk_widget_show(mainbox);
384
385 gtk_container_add (GTK_CONTAINER (app->window), mainbox);
386
387 gtk_widget_show (app->window);
388
389 }
390 if (INTEGRATED_INTERFACE) {
391 create_callb(NULL,(gpointer) app);
392 }
393
394 return;
395 }
396
app_quit(GtkWidget * wdg,gpointer data)397 gint app_quit(GtkWidget* wdg, gpointer data) {
398 GList *node;
399 GtkWidget *window;
400 gint answer, modcount, newmodcount;
401 gboolean discard=TRUE;
402 doc_wrapper *dw=NULL, *dwtmp;
403 app_struct *app;
404 app = (app_struct *) data;
405
406 modcount = count_modified_documents (app->docs);
407
408 // We try to avoid inconsistencies...
409 if (app->docs->current_doc) {
410 commit_or_reset (app->stack);
411 }
412
413 while (modcount) {
414 if (modcount==1) {
415 for (node = app->docs->doc_list; node; node = node->next) {
416 dwtmp = (doc_wrapper *) node->data;
417 if (dwtmp->if_modified) {
418 dw = dwtmp;
419 break;
420 }
421 }
422 if (!dw) {
423 my_msg("One document is marked as unsaved, but I'm not able to find it\nPlease save it with the menu bar", WARNING);
424 return TRUE;
425 }
426 answer = doc_save_question(dw);
427 if (answer==CANCEL_YESNO)
428 return TRUE; // Quitting cancelled!
429 else
430 // We continue the process (quitting + freeing docs)
431 break;
432 }
433 else {
434 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
435 modal_dialog_with_titles_window_provided (window,
436 save_all_dialog_new (window, app->docs),
437 _("Exiting Geomorph"),
438 (gpointer) gboolean_set_true,
439 "Discard all and quit",
440 (gpointer) gboolean_set_false,
441 "Return", &discard, GTK_WIN_POS_CENTER, TRUE);
442 if (discard) {
443 // If there are less unsaved documents than before,
444 // the user is still saving them, so we re-display
445 // the dialog.
446 // Otherwise, we continue the quitting process
447 newmodcount = count_modified_documents (app->docs);
448 if (newmodcount==modcount)
449 break;
450 else {
451 modcount = newmodcount;
452 continue;
453 }
454 }
455 else // !discard
456 return TRUE; // Quitting cancelled!
457 }
458 }
459
460 gtk_main_quit();
461
462 // 2005-02: The "save" and the "destroy" parts are separated,
463 // otherwise some events stay connected to freed structures
464 for (node = app->docs->doc_list; node; node = node->next) {
465 doc_wrapper_free((doc_wrapper *) node->data, FALSE);
466 }
467
468 // xalloc_print_all ();
469
470 return TRUE;
471 }
472
app_menu_delete(GtkWidget * wdg,GdkEvent * event,gpointer data)473 gint app_menu_delete (GtkWidget *wdg, GdkEvent *event, gpointer data) {
474 return app_quit (wdg, data);
475 }
476
app_free(app_struct * app)477 void app_free(app_struct *app) {
478 if (app->types)
479 types_wrapper_free(app->types);
480 // At this point, documents (app->docs->doc_list) are supposed to be freed
481 if (app->docs)
482 doc_swap_free(app->docs);
483 if (app)
484 x_free(app);
485 }
486
487 /*************************** CALLBACKS FOR TOOLBAR AND MENUS *************************/
488
swap_top_doc(GtkWidget * wdg,GdkEvent * event,gpointer data)489 gboolean swap_top_doc(GtkWidget *wdg, GdkEvent *event, gpointer data)
490 {
491 // Initializes the current document pointer when the user clicks on the window
492 GList *node;
493 app_struct *app;
494 doc_wrapper *doc;
495 app = (app_struct *) data;
496 // Do not execute the creation operation in another window!!
497 if (app->docs->current_doc->creation_mode)
498 return TRUE;
499 // printf("SWAP_TOP_DOC1: %s\n", app->docs->current_doc->filename);
500 // Find the document related to the window "wdg"
501 node = g_list_first(app->docs->doc_list);
502 while (node) {
503 doc = (doc_wrapper *) node->data;
504 if (wdg == doc->window)
505 break;
506 node = node->next;
507 }
508 if (!node)
509 return TRUE; // not found, keep current value
510 if (doc == app->docs->current_doc) {
511 // printf("SWAP THE SAME... %s\n",doc->filename);
512 if (doc->type->display)
513 (*doc->type->display) (doc->data);
514 return TRUE; // no change
515 }
516 // We commit the last changes before focusing on the new document
517 // (we suppose they were done on the last document displayed)
518 commit_or_reset (app->stack);
519 // The default is to place on top the tools window and the main window
520 // We check the windows... under some circumstances (quitting...),
521 // the gdk window of the current document is NULL
522 if (app->window && app->window->window)
523 gdk_window_raise(app->window->window);
524 // This last test should not be necessary, this is not a document, but...
525 if ((!INTEGRATED_INTERFACE) && app->types->tools_container && app->types->tools_container->window)
526 gdk_window_raise(app->types->tools_container->window);
527 // printf("SWAP_TOP_DOC2: %s\n", app->docs->current_doc->filename);
528 doc_make_current(app->docs, doc);
529 return TRUE; // stop the process
530 }
531
ok_create_callb(GtkWidget * wdg,gpointer data)532 gint ok_create_callb(GtkWidget *wdg, gpointer data) {
533 // Comitting the document creation
534 app_struct *app;
535 app = (app_struct*) data;
536 // Add current doc to app->docs->doc_list
537 app->docs->doc_list = g_list_append(app->docs->doc_list, app->docs->current_doc);
538 // printf("OK_CREATE_CALLB: doc_list: %d; doc_list->prev: %d; doc_list->next: %d\n", app->docs->doc_list, app->docs->doc_list->prev, app->docs->doc_list->next);
539 // Connect document window "focus_in_event" to current_doc/last_doc initialization
540 gtk_signal_connect (GTK_OBJECT(app->docs->current_doc->window),
541 "focus-in-event", GTK_SIGNAL_FUNC(swap_top_doc),
542 (gpointer) app);
543 // We probably need another signal, when the top most window is minimized,
544 // for activating the window under it
545 // gtk_signal_connect (GTK_OBJECT(app->docs->current_doc->window),
546 // "expose-event", GTK_SIGNAL_FUNC(swap_top_doc),
547 // (gpointer) app);
548
549 // Attach the global menu accelerators
550 gtk_window_add_accel_group (GTK_WINDOW(app->docs->current_doc->window), app->accel_group);
551
552 app->new_doc_count++;
553 app->docs->current_doc->creation_mode = FALSE;
554 app->docs->current_doc->if_modified = TRUE;
555
556 // We do some mandatory processes for committing the creation
557 if (app->docs->current_doc->type->commit_creation) {
558 (*app->docs->current_doc->type->commit_creation)
559 ((gpointer) app->docs->current_doc_data);
560 }
561 // Display the default tools dialog for the current document type
562 // All commands should be connected to (gpointer) app->current_doc_data
563
564 /* NB: tools_dialog created in the file type choice callack (in thisappinit.c)
565 or when opening a new file (open_callb here), with create_type_dialogs */
566 if (INTEGRATED_INTERFACE) {
567 gtk_widget_show(app->docs->current_doc->tools_container);
568 if (GTK_IS_WIDGET(app->docs->current_doc->creation_container))
569 // No creation container when opening a file
570 gtk_widget_hide(app->docs->current_doc->creation_container);
571 }
572 else {
573 if (app->docs->last_doc) // last_doc NULL => creation of the 1st document
574 if (app->docs->last_doc->type->tools_dialog ==
575 app->docs->current_doc->type->tools_dialog)
576 // If type of new doc. = type of last one, same controls, do nothing!
577 return;
578 if (app->docs->last_doc)
579 if (app->docs->last_doc->type->tools_dialog) {
580 // Changing type - replace tools dialog
581 gtk_widget_ref(GTK_WIDGET(app->docs->last_doc->type->tools_dialog));
582 gtk_container_remove(GTK_CONTAINER(app->types->tools_container),
583 app->docs->last_doc->type->tools_dialog);
584 }
585 gtk_widget_show(GTK_WIDGET(app->docs->current_doc->type->tools_dialog));
586 if (!GTK_WIDGET(app->docs->current_doc->type->tools_dialog)->parent) {
587 gtk_container_add(GTK_CONTAINER(app->types->tools_container),
588 app->docs->current_doc->type->tools_dialog);
589 gtk_object_sink(GTK_OBJECT(app->docs->current_doc->type->tools_dialog));
590 }
591 gtk_widget_show(app->types->tools_container);
592 } // end else (==!INTEGRATED_INTERFACE)
593 return FALSE;
594 }
595
cancel_create(app_struct * app)596 void cancel_create (app_struct *app) {
597 // Destroying the window is supposed to call doc_wrapper_free
598 // 2005-02: Seg faults when closing with gtk_widget_destroy (GTK+??)
599 // so we simply hides the window (a memory leak...)
600 // gtk_widget_destroy(GTK_WIDGET(app->docs->current_doc->window));
601 gtk_widget_hide (GTK_WIDGET(app->docs->current_doc->window));
602 doc_wrapper_free(app->docs->current_doc, TRUE);
603 // printf("Current_doc destroyed in cancel_create_callb!\n");
604 app->docs->current_doc = app->docs->last_doc;
605 if (app->docs->current_doc)
606 app->docs->current_doc_data = app->docs->current_doc->data;
607 else
608 app->docs->current_doc_data = NULL;
609 }
610
cancel_create_callb(GtkWidget * wdg,gpointer data)611 gint cancel_create_callb(GtkWidget *wdg, gpointer data) {
612 // Destroy newly created document structure
613 // Restore app->current_doc
614
615 app_struct *app;
616 app = (app_struct *)data;
617 cancel_create (app);
618 if (INTEGRATED_INTERFACE && (!count_documents(app->docs)))
619 gtk_main_quit();
620 return FALSE;
621 }
622
create_callb(GtkWidget * wdg,gpointer data)623 void create_callb(GtkWidget *wdg, gpointer data) {
624
625 // Creates a new document with one of the allowed types for the current application
626 // Dialog is modal and varies with application type
627
628 GtkWidget *hbox, *button, *vbox, *app_toolbar=NULL, *app_menu=NULL;
629 app_struct *app;
630 app = (app_struct *)data;
631
632 // First we commit the changes to the current document, if there are any
633
634 commit_or_reset (app->stack);
635
636 app->docs->last_doc = app->docs->current_doc;
637 // Create a document window for the current doc
638 // Fill it with the display / controls for the current document type
639 app->docs->current_doc =
640 doc_wrapper_new("*", app->types->default_type->def_dir, app->types->default_type);
641 app->docs->current_doc->fname_tochoose = TRUE;
642
643 if (INTEGRATED_INTERFACE) {
644
645 // We pack the creation dialog with OK/CANCEL buttons here
646 app->docs->current_doc->creation_container = creation_container_new(app->types);
647 app->docs->current_doc->tools_container = tools_container_new(app->types);
648 // Commit_data && current_doc_data must not
649 // contain the last document pointer
650 set_commit_data (app->stack, NULL);
651 app->docs->current_doc_data = NULL;
652 create_type_dialogs(app->types,
653 app->docs->current_doc->type,
654 (gpointer) &app->docs->current_doc_data,
655 app->stack,
656 app->docs,
657 app->docs->current_doc);
658
659 hbox = gtk_hbox_new(TRUE,0);
660 gtk_widget_show(GTK_WIDGET(hbox));
661
662 button = gtk_button_new_with_label(_("OK"));
663 gtk_widget_show(GTK_WIDGET(button));
664 gtk_container_set_border_width(GTK_CONTAINER(button),DEF_PAD);
665 gtk_signal_connect(GTK_OBJECT(button), "clicked",
666 GTK_SIGNAL_FUNC(ok_create_callb), (gpointer) app);
667 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, DEF_PAD);
668
669 button = gtk_button_new_with_label(_("Cancel"));
670 gtk_widget_show(GTK_WIDGET(button));
671 gtk_container_set_border_width(GTK_CONTAINER(button),DEF_PAD);
672 gtk_signal_connect(GTK_OBJECT(button), "clicked",
673 GTK_SIGNAL_FUNC(cancel_create_callb), (gpointer) app);
674 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, DEF_PAD);
675
676 vbox = gtk_vbox_new(FALSE,0);
677 gtk_widget_show(GTK_WIDGET(vbox));
678 gtk_box_pack_start(GTK_BOX(vbox), app->docs->current_doc->creation_dialog, TRUE, TRUE, DEF_PAD);
679 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, DEF_PAD);
680 gtk_container_add(GTK_CONTAINER(app->docs->current_doc->creation_container),vbox);
681
682 // We don't forget the tools dialog, for later use
683 gtk_container_add(GTK_CONTAINER(app->docs->current_doc->tools_container), app->docs->current_doc->tools_dialog);
684 gtk_widget_hide(app->docs->current_doc->tools_container);
685 }
686 else { // Gimp style interface
687 if (!app->types->type_choice_dialog) {
688
689 // Current document and interface callbacks
690 // are linked in the following process
691
692 // We create a modal window, containing a hbox which packs:
693 // 1st widget from the top: file type choice toolbar
694 // option widgets, in the middle
695 // OK / Cancel button, at the bottom
696 // The interface is created once, so we keep the widgets embedded in their structs
697
698 // Creates the type choice dialog if inexistent
699
700 type_choice_dialog_new(app->types, (gpointer) app,
701 (gpointer) &app->docs->current_doc_data,
702 app->stack,
703 app->docs);
704
705 modal_dialog_showhide(app->types->creation_container,
706 app->types->type_choice_dialog,
707 _("New document"),
708 ok_create_callb,
709 cancel_create_callb,
710 (gpointer) app, 0, TRUE);
711 }
712
713 else { // Reactivates the modal dialog
714
715 type_choice_dialog_show(app->types);
716 gtk_widget_show(GTK_WIDGET(app->types->creation_container));
717 // ... set_uposition deprecated in GTK2, but I still have to find an alternative...
718 gtk_widget_set_uposition(GTK_WIDGET(app->types->creation_container),app->types->win_pos_x, app->types->win_pos_y);
719 gtk_grab_add(GTK_WIDGET(app->types->creation_container));
720 }
721 } // Emd Gimp style interface
722 if (MENU_IN_DOC_WINDOW)
723 app_menu = (menu_new(NBCOMMANDS,commands, app->accel_group, (gpointer) app))->bar;
724 if (ICONS_IN_DOC_WINDOW)
725 app_toolbar = standard_toolbar_new(NBCOMMANDS,
726 commands,
727 app->tooltips,
728 app->window,
729 (gpointer) app,
730 GTK_ORIENTATION_HORIZONTAL,
731 GTK_TOOLBAR_ICONS,
732 FALSE);
733
734 // Empty window + document creation
735 // printf("app->docs->doc_list dans CREATE_CALLB: %d\n",app->docs->doc_list);
736 doc_new(app->docs, app->types, NULL, app->new_doc_count, &app->stack->commit_data, app_menu, app_toolbar);
737
738 // Execute some defaults, if any
739 // 2008-01: Migrated to the type dialog level -doctype.c
740 // if (app->docs->current_doc->type->defaults)
741 // (*app->docs->current_doc->type->defaults)
742 // ((gpointer) &app->docs->current_doc_data);
743 }
744
open_callb(GtkWidget * wdg,gpointer data)745 void open_callb(GtkWidget *wdg, gpointer data) {
746 // Opens an existing file, loading it in a new document
747 // Dialog is modal and varies with application type
748 app_struct *app;
749 doc_wrapper *dw;
750 gchar *path_n_file;
751 GtkWidget *app_toolbar=NULL, *app_menu=NULL;
752 app = (app_struct *)data;
753
754 // If wdg is NULL, we are trying to open a file given on the command line
755 // doc_read calls doc_prepare_to_load
756 // doc_new (a few lines ahead) "fills up" the structure
757 // prepared by doc_prepare_to_load
758 if (wdg) { // Interactive creation
759
760 // In this case we commit the changes to the current
761 // document, if there are any
762
763 commit_or_reset (app->stack);
764 dw = doc_read(&path_n_file, app->types, app->docs, app->default_dir);
765 }
766 else { // File name given on the command line
767 path_n_file = app->file_on_cmdline;
768 dw = doc_prepare_to_load(path_n_file, app->types);
769 }
770 if (!dw)
771 return;
772
773 if (app->docs->current_doc)
774 if (app->docs->current_doc->creation_mode && INTEGRATED_INTERFACE)
775 cancel_create (app);
776
777 app->docs->last_doc = app->docs->current_doc;
778 app->docs->current_doc = dw;
779 // current_doc_data may contain a pointer to another document
780 app->docs->current_doc_data = NULL;
781 /************** CHECK: creating a creation dialog not required here **********/
782 // Create the dialogs needed for managing
783 // this particular document type, if needed
784 create_type_dialogs(app->types,
785 dw->type,
786 (gpointer) &app->docs->current_doc_data,
787 app->stack,
788 app->docs,
789 dw);
790
791 // Empty window + document creation
792 // Load the data from path_n_file into doc_wrapper struct
793 if (INTEGRATED_INTERFACE) {
794 app->docs->current_doc->tools_container = tools_container_new(app->types);
795 gtk_container_add(GTK_CONTAINER(app->docs->current_doc->tools_container), app->docs->current_doc->tools_dialog);
796 }
797
798 if (MENU_IN_DOC_WINDOW)
799 app_menu = (menu_new(NBCOMMANDS,commands, app->accel_group, (gpointer) app))->bar;
800 if (ICONS_IN_DOC_WINDOW)
801 app_toolbar = standard_toolbar_new(NBCOMMANDS,
802 commands,
803 app->tooltips,
804 app->window,
805 (gpointer) app,
806 GTK_ORIENTATION_HORIZONTAL,
807 GTK_TOOLBAR_ICONS,
808 FALSE);
809
810 if (!doc_new(app->docs, app->types, path_n_file, app->new_doc_count, &app->stack->commit_data, app_menu, app_toolbar))
811 cancel_create (app);
812 else {
813 ok_create_callb(NULL,data);
814 app->default_dir = app->docs->current_doc->dir;
815 if (!app->docs->current_doc->fname_tochoose)
816 app->docs->current_doc->if_modified = FALSE;
817 // If it's the first document, we have to re-position the window after building the dialogs
818 place_document_window(app->docs, app->types, app->types->tools_container);
819 }
820 }
821
save_callb(GtkWidget * wdg,gpointer data)822 void save_callb(GtkWidget *wdg, gpointer data) {
823 // Saves current document
824 app_struct *app;
825 app = (app_struct *)data;
826 if (app && app->docs && app->docs->doc_list)
827 if (g_list_length(app->docs->doc_list))
828 if (app->docs->current_doc) {
829 commit_or_reset (app->stack);
830 doc_save(app->docs->current_doc);
831 }
832 }
833
save_as_callb(GtkWidget * wdg,gpointer data)834 void save_as_callb(GtkWidget *wdg, gpointer data) {
835 // Saves top document under a new name
836 // Change file name in current doc of app
837 // Then apply save_callb
838 app_struct *app;
839 app = (app_struct *)data;
840 if (app && app->docs && app->docs->doc_list)
841 if (g_list_length(app->docs->doc_list))
842 if (app->docs->current_doc) {
843 commit_or_reset (app->stack);
844 app->docs->current_doc->fname_tochoose = TRUE;
845 doc_save(app->docs->current_doc);
846 }
847 }
848
save_copy_as_callb(GtkWidget * wdg,gpointer data)849 void save_copy_as_callb(GtkWidget *wdg,gpointer data) {
850 // Saves top document under a new name,
851 // after copying it in a new window
852 // Choose new file name
853 // Returns if cancelled
854 // Create new doc_wrapper
855
856 // Simple implementation (probably with overhead...):
857 // Save under a new name, reopen the original document,
858 // swap on the top the old renamed document
859
860 app_struct *app;
861 gchar *dname, *fname;
862 doc_wrapper *doc;
863 app = (app_struct *)data;
864 if (!app->docs)
865 return;
866 if (!app->docs->current_doc)
867 return;
868 doc = app->docs->current_doc;
869
870 if (doc->fname_tochoose) {
871 my_msg(_("You'll be asked to save the document before cloning it."),INFO);
872 }
873 // Any modification should be saved, whatever happens
874 commit_or_reset (app->stack);
875 doc_save(app->docs->current_doc);
876
877 if (app->file_on_cmdline)
878 x_free(app->file_on_cmdline);
879 app->file_on_cmdline = concat_dname_fname(doc->dir, doc->filename);
880
881 app->docs->current_doc->fname_tochoose = TRUE;
882 if (!doc_save(app->docs->current_doc)) {
883 app->docs->current_doc->fname_tochoose = FALSE;
884 return;
885 }
886
887 fname = doc->filename; // new name
888 dname = doc->dir;
889 open_callb(NULL,data); // Open the filename given by app->file_on_cmdline (old name)
890 // Swap the file and dir names (some window focusing problems, otherwise)
891 doc->filename = app->docs->current_doc->filename; // old name
892 doc->dir = app->docs->current_doc->dir;
893 app->docs->current_doc->filename = fname;
894 app->docs->current_doc->dir = dname;
895 // Old name for original doc
896 gtk_window_set_title(GTK_WINDOW(doc->window), g_filename_to_utf8(app->file_on_cmdline,-1, NULL, NULL, NULL));
897 // New name for newly opened doc
898 gtk_window_set_title(GTK_WINDOW(app->docs->current_doc->window),
899 g_filename_to_utf8(concat_dname_fname(dname, fname), -1, NULL, NULL, NULL));
900 }
901
print_callb(GtkWidget * wdg,gpointer app)902 void print_callb(GtkWidget *wdg, gpointer app) {
903 my_msg("PRINT_CALLB",WARNING);
904 }
905
close_callb(GtkWidget * wdg,gpointer callb_data)906 void close_callb(GtkWidget *wdg, gpointer callb_data) {
907 // Closes current document (which should be the top one!)
908 GtkWidget *wintemp;
909 app_struct *app;
910 app = (app_struct *) callb_data;
911 if (app && app->docs && app->docs->doc_list)
912 if (g_list_length(app->docs->doc_list))
913 if (app->docs->current_doc) {
914 commit_or_reset (app->stack);
915 wintemp = app->docs->current_doc->window;
916 if (doc_close(wintemp, NULL, (gpointer) app->docs))
917 return;
918 gtk_widget_hide(wintemp);
919 // Some problems here with destroy - "expose event" left on ghost data!!
920 // gtk_widget_destroy(app->docs->current_doc->window);
921 }
922 }
923
quit_callb(GtkWidget * wdg,gpointer callb_data)924 void quit_callb(GtkWidget *wdg, gpointer callb_data) {
925 // Quit the application, after closing all opened documents
926 // Scan documents wrappers
927
928 app_struct *app;
929 app = (app_struct *) callb_data;
930 if (app)
931 commit_or_reset (app->stack);
932
933 // Check for document needing saving
934 // If so: ask to save (modal), then save with the save function for this type
935 // If not: destroy document window
936 app_quit(wdg, callb_data);
937 }
938
undo_callb(GtkWidget * wdg,gpointer callb_data)939 void undo_callb(GtkWidget *wdg, gpointer callb_data) {
940 app_struct *app;
941 app = (app_struct *) callb_data;
942 if (app && app->docs && app->docs->current_doc && app->docs->current_doc->history)
943 if (g_list_length(app->docs->current_doc->history)) {
944 doc_undo(app->docs);
945 }
946 }
947
redo_callb(GtkWidget * wdg,gpointer callb_data)948 void redo_callb(GtkWidget *wdg, gpointer callb_data) {
949 app_struct *app;
950 app = (app_struct *) callb_data;
951 if (app && app->docs && app->docs->current_doc && app->docs->current_doc->history)
952 if (g_list_length(app->docs->current_doc->history)) {
953 doc_redo(app->docs);
954 }
955 }
956
copy_callb(GtkWidget * wdg,gpointer app)957 void copy_callb(GtkWidget *wdg, gpointer app) {
958 my_msg("COPY_CALLB",WARNING);
959 }
cut_callb(GtkWidget * wdg,gpointer app)960 void cut_callb(GtkWidget *wdg, gpointer app) {
961 my_msg("CUT_CALLB",WARNING);
962 }
paste_callb(GtkWidget * wdg,gpointer app)963 void paste_callb(GtkWidget *wdg, gpointer app) {
964 my_msg("PASTE_CALLB",WARNING);
965 }
paste_special_callb(GtkWidget * wdg,gpointer app)966 void paste_special_callb(GtkWidget *wdg, gpointer app) {
967 my_msg("PASTE_SPECIAL_CALLB",WARNING);
968 }
stats_callb(GtkWidget * wdg,gpointer data)969 void stats_callb(GtkWidget *wdg, gpointer data) {
970 app_struct *app;
971 app = (app_struct *) data;
972 if (app && app->docs && app->docs->doc_list)
973 if (g_list_length(app->docs->doc_list))
974 if (app->docs->current_doc) {
975 doc_stats(app->docs->current_doc);
976 }
977 }
options_callb(GtkWidget * wdg,gpointer data)978 void options_callb(GtkWidget *wdg, gpointer data) {
979 app_struct *app;
980 app = (app_struct *) data;
981 if (!app->options_dialog) {
982 app->options_dialog = options_dialog_new(
983 &app->rc_options_file,
984 &app->current_options,
985 app->allowed_options,
986 process_all_options);
987 }
988 else {
989 gtk_widget_show(GTK_WIDGET(app->options_dialog));
990 gtk_grab_add(GTK_WIDGET(app->options_dialog));
991 }
992 }
993
help_callb(GtkWidget * wdg,gpointer app)994 void help_callb(GtkWidget *wdg, gpointer app) {
995 static char *buf[3] = {NULL, NULL, NULL};
996 buf[0] = DOC_READER;
997 buf[1] = DOC_DIR;
998 execution(buf[0],buf);
999 }
about_callb(GtkWidget * wdg,gpointer app)1000 void about_callb(GtkWidget *wdg, gpointer app) {
1001 GtkWidget *content=NULL, *lbl;
1002 gchar *fname = NULL,*utf8_msg=NULL;
1003 gsize br,bw;
1004 GError *ger;
1005 static gchar *f1=NULL;
1006 static gchar *f2 = "splash.jpg";
1007 static gchar *msg = "\nGEOMORPH 0.6\nhttp://geomorph.sourceforge.net\n\n(c) Patrice St-Gelais 2003-2009 (GPL)\npatrstg@users.sourceforge.net\n\nGerman translation by\nSimon Donike (2005-2008) and Tim Schuermann (2004)";
1008 if (!f1) {
1009 f1 = x_malloc(2+strlen("/usr/local/share/geomorph/")+strlen(VERSION)+strlen(f2), "Splash image");
1010 strcpy(f1,"/usr/local/share/geomorph/");
1011 strcat(f1,VERSION);
1012 strcat(f1,"/");
1013 strcat(f1,f2);
1014 }
1015 if (!utf8_msg) {
1016 utf8_msg = g_locale_to_utf8(msg,-1,&br,&bw,&ger);
1017 }
1018 if (!utf8_msg)
1019 utf8_msg = msg;
1020 if (filexists(f1))
1021 fname = f1;
1022 else
1023 if (filexists(f2))
1024 fname = f2;
1025 if (fname) {
1026 content = gtk_vbox_new(FALSE,0);
1027 gtk_container_add(GTK_CONTAINER(content),gtk_image_new_from_file(fname));
1028 lbl = gtk_label_new(utf8_msg);
1029 gtk_label_set_justify(GTK_LABEL(lbl),GTK_JUSTIFY_CENTER);
1030 gtk_container_add(GTK_CONTAINER(content),lbl);
1031 gtk_widget_show_all(content);
1032 }
1033 if (content)
1034 modal_dialog (content, " ", NULL, NULL, NULL, GTK_WIN_POS_CENTER, TRUE);
1035 else
1036 my_msg(utf8_msg,INFO);
1037 }
1038