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