1 /*
2  *  Celestia GTK+ Front-End
3  *  Copyright (C) 2005 Pat Suwalski <pat@suwalski.net>
4  *
5  *  This program 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  *  $Id: actions.cpp,v 1.17 2008-01-25 01:05:14 suwalski Exp $
11  */
12 
13 #ifdef HAVE_CONFIG_H
14 #include <config.h>
15 #endif /* HAVE_CONFIG_H */
16 
17 #include <cstring>
18 #include <fstream>
19 #include <gtk/gtk.h>
20 
21 #ifdef GNOME
22 #include <gconf/gconf-client.h>
23 #endif /* GNOME */
24 
25 #include <celengine/body.h>
26 #include <celengine/gl.h>
27 #include <celengine/glext.h>
28 #include <celengine/simulation.h>
29 #include <celengine/cmdparser.h>
30 #include <celengine/render.h>
31 #include <celestia/celestiacore.h>
32 #include <celestia/imagecapture.h>
33 #include <celestia/url.h>
34 #include <celutil/filetype.h>
35 #ifdef THEORA
36 #include <celestia/oggtheoracapture.h>
37 #endif
38 
39 #include "actions.h"
40 #include "common.h"
41 #include "dialog-eclipse.h"
42 #include "dialog-goto.h"
43 #include "dialog-options.h"
44 #include "dialog-solar.h"
45 #include "dialog-star.h"
46 #include "dialog-time.h"
47 #include "dialog-tour.h"
48 
49 #ifdef GNOME
50 #include "settings-gconf.h"
51 #else
52 #include "settings-file.h"
53 #endif /* GNOME */
54 
55 
56 /* Declarations: Action Helpers */
57 static void openScript(const char* filename, AppData* app);
58 static void captureImage(const char* filename, AppData* app);
59 #ifdef THEORA
60 static void captureMovie(const char* filename, int aspect, float fps, float quality, AppData* app);
61 #endif
62 static void textInfoDialog(const char *txt, const char *title, AppData* app);
63 static void setRenderFlag(AppData* a, int flag, gboolean state);
64 static void setOrbitMask(AppData* a, int mask, gboolean state);
65 static void setLabelMode(AppData* a, int mode, gboolean state);
66 
67 
68 /* File -> Copy URL */
actionCopyURL(GtkAction *,AppData * app)69 void actionCopyURL(GtkAction*, AppData* app)
70 {
71 	GtkClipboard* cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
72 	CelestiaState appState;
73 	appState.captureState(app->core);
74 	Url url(appState, Url::CurrentVersion);
75 	gtk_clipboard_set_text(cb, url.getAsString().c_str(), -1);
76 }
77 
78 
79 /* File -> Open URL */
actionOpenURL(GtkAction *,AppData * app)80 void actionOpenURL(GtkAction*, AppData* app)
81 {
82 	GtkWidget* dialog = gtk_dialog_new_with_buttons("Enter cel:// URL",
83 	                                                GTK_WINDOW(app->mainWindow),
84 	                                                GTK_DIALOG_MODAL,
85 	                                                GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
86 	                                                GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
87 	                                                NULL);
88 
89 	/* Create a new entry box with default text, all selected */
90 	GtkWidget* entry = gtk_entry_new();
91 	gtk_entry_set_width_chars(GTK_ENTRY(entry), 80);
92 	gtk_entry_set_text(GTK_ENTRY(entry), "cel://");
93 	gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
94 
95 	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), entry, TRUE, TRUE, CELSPACING);
96 	gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
97 	gtk_widget_show_all(dialog);
98 
99 	if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
100 		app->core->goToUrl(gtk_entry_get_text(GTK_ENTRY(entry)));
101 
102 	gtk_widget_destroy(dialog);
103 }
104 
105 
106 /* File -> Open Script... */
actionOpenScript(GtkAction *,AppData * app)107 void actionOpenScript(GtkAction*, AppData* app)
108 {
109 	GtkWidget* fs = gtk_file_chooser_dialog_new("Open Script.",
110 	                                            GTK_WINDOW(app->mainWindow),
111 	                                            GTK_FILE_CHOOSER_ACTION_OPEN,
112 	                                            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
113 	                                            GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
114 	                                            NULL);
115 
116 	#if GTK_CHECK_VERSION(2, 7, 0)
117 	gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(fs), TRUE);
118 	#endif /* GTK_CHECK_VERSION */
119 
120 	gtk_dialog_set_default_response(GTK_DIALOG(fs), GTK_RESPONSE_ACCEPT);
121 	gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(fs), g_get_home_dir());
122 
123 	GtkFileFilter* filter = gtk_file_filter_new();
124 	gtk_file_filter_set_name(filter, "Celestia Scripts");
125 
126 	gtk_file_filter_add_pattern(filter, "*.cel");
127 
128 	#ifdef CELX
129 	gtk_file_filter_add_pattern(filter, "*.celx");
130 	gtk_file_filter_add_pattern(filter, "*.clx");
131 	#endif /* CELX */
132 
133 	gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(fs), filter);
134 
135 	if (gtk_dialog_run(GTK_DIALOG(fs)) == GTK_RESPONSE_ACCEPT)
136 	{
137 		char* filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
138 		openScript(filename, app);
139 		g_free(filename);
140 	}
141 
142 	gtk_widget_destroy(fs);
143 }
144 
145 
146 /* File -> Capture Image... */
actionCaptureImage(GtkAction *,AppData * app)147 void actionCaptureImage(GtkAction*, AppData* app)
148 {
149 	GtkWidget* fs = gtk_file_chooser_dialog_new("Save Image to File",
150 	                                            GTK_WINDOW(app->mainWindow),
151 	                                            GTK_FILE_CHOOSER_ACTION_SAVE,
152 	                                            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
153 	                                            GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
154 	                                            NULL);
155 
156 	GtkFileFilter* filter = gtk_file_filter_new();
157 	gtk_file_filter_set_name(filter, "PNG and JPEG Images");
158 	gtk_file_filter_add_pattern(filter, "*.jpeg");
159 	gtk_file_filter_add_pattern(filter, "*.jpg");
160 	gtk_file_filter_add_pattern(filter, "*.png");
161 	gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(fs), filter);
162 
163 	#if GTK_CHECK_VERSION(2, 7, 0)
164 	gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(fs), TRUE);
165 	#endif /* GTK_CHECK_VERSION */
166 
167 	gtk_dialog_set_default_response(GTK_DIALOG(fs), GTK_RESPONSE_ACCEPT);
168 	gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(fs), g_get_home_dir());
169 
170 	if (gtk_dialog_run(GTK_DIALOG(fs)) == GTK_RESPONSE_ACCEPT)
171 	{
172 		char* filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
173 		gtk_widget_destroy(fs);
174 		for (int i=0; i < 10 && gtk_events_pending ();i++)
175 			gtk_main_iteration ();
176 		captureImage(filename, app);
177 		g_free(filename);
178 	}
179 	else
180 	{
181 		gtk_widget_destroy(fs);
182 	}
183 }
184 
185 /* File -> Capture Movie... */
actionCaptureMovie(GtkAction *,AppData * app)186 void actionCaptureMovie(GtkAction*, AppData* app)
187 {
188 #ifdef THEORA
189 	// TODO: The menu item should be disable so that the user doesn't even
190 	// have the opportunity to record two movies simultaneously; the only
191 	// thing missing to make this happen is notification when recording
192 	// is complete.
193 	if (app->core->isCaptureActive())
194 	{
195 		GtkWidget* errBox = gtk_message_dialog_new(GTK_WINDOW(app->mainWindow),
196 							   GTK_DIALOG_DESTROY_WITH_PARENT,
197 							   GTK_MESSAGE_ERROR,
198 							   GTK_BUTTONS_OK,
199 							   "Stop current movie capture before starting another one.");
200 		gtk_dialog_run(GTK_DIALOG(errBox));
201 		gtk_widget_destroy(errBox);
202 		return;
203 	}
204 
205 	GtkWidget* fs = gtk_file_chooser_dialog_new("Save Ogg Theora Movie to File",
206 	                                            GTK_WINDOW(app->mainWindow),
207 	                                            GTK_FILE_CHOOSER_ACTION_SAVE,
208 	                                            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
209 	                                            GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
210 	                                            NULL);
211 
212 	GtkFileFilter* filter = gtk_file_filter_new();
213 	gtk_file_filter_set_name(filter, "Ogg Files");
214 	gtk_file_filter_add_pattern(filter, "*.ogg");
215 	gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(fs), filter);
216 
217 	#if GTK_CHECK_VERSION(2, 7, 0)
218 	gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(fs), TRUE);
219 	#endif /* GTK_CHECK_VERSION */
220 
221 	gtk_dialog_set_default_response(GTK_DIALOG(fs), GTK_RESPONSE_ACCEPT);
222 	gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(fs), g_get_home_dir());
223 
224 	GtkWidget* hbox = gtk_hbox_new(FALSE, CELSPACING);
225 	gtk_container_set_border_width(GTK_CONTAINER(hbox), CELSPACING);
226 
227 	GtkWidget* rlabel = gtk_label_new("Aspect Ratio:");
228 	gtk_box_pack_start(GTK_BOX(hbox), rlabel, TRUE, TRUE, 0);
229 
230 	GtkWidget* aspectmenubox = gtk_combo_box_new_text();
231 	gtk_combo_box_append_text(GTK_COMBO_BOX(aspectmenubox), "1:1");
232 	gtk_combo_box_append_text(GTK_COMBO_BOX(aspectmenubox), "4:3");
233 	gtk_combo_box_append_text(GTK_COMBO_BOX(aspectmenubox), "16:9");
234 	gtk_combo_box_append_text(GTK_COMBO_BOX(aspectmenubox), "Display");
235 	gtk_combo_box_set_active(GTK_COMBO_BOX(aspectmenubox), 0);
236 	gtk_box_pack_start(GTK_BOX(hbox), aspectmenubox, FALSE, FALSE, 0);
237 
238 	GtkWidget* flabel = gtk_label_new("Frame Rate:");
239 	gtk_box_pack_start(GTK_BOX(hbox), flabel, TRUE, TRUE, 0);
240 
241 	GtkWidget* fpsspin = gtk_spin_button_new_with_range(5.0, 30.0, 0.01);
242 	gtk_box_pack_start(GTK_BOX(hbox), fpsspin, TRUE, TRUE, 0);
243 	gtk_spin_button_set_value(GTK_SPIN_BUTTON(fpsspin), 12.0);
244 	gtk_spin_button_set_increments(GTK_SPIN_BUTTON(fpsspin), 0.01, 1.0);
245 
246 	GtkWidget* qlabel = gtk_label_new("Video Quality:");
247 	gtk_box_pack_start(GTK_BOX(hbox), qlabel, TRUE, TRUE, 0);
248 
249 	GtkWidget* qspin = gtk_spin_button_new_with_range(0.0, 10.0, 1.0);
250 	gtk_box_pack_start(GTK_BOX(hbox), qspin, TRUE, TRUE, 0);
251 	gtk_spin_button_set_value(GTK_SPIN_BUTTON(qspin), 10.0);
252 
253 	gtk_widget_show_all(hbox);
254 	gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(fs), hbox);
255 
256 	if (gtk_dialog_run(GTK_DIALOG(fs)) == GTK_RESPONSE_ACCEPT)
257 	{
258 		char* filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
259 		int aspect = gtk_combo_box_get_active(GTK_COMBO_BOX(aspectmenubox));
260 		double fps = gtk_spin_button_get_value(GTK_SPIN_BUTTON(fpsspin));
261 		double quality = gtk_spin_button_get_value(GTK_SPIN_BUTTON(qspin));
262 
263 		gtk_widget_destroy(fs);
264 		for (int i=0; i < 10 && gtk_events_pending ();i++)
265 			gtk_main_iteration ();
266 		captureMovie(filename, aspect, fps, quality,  app);
267 		g_free(filename);
268 	}
269 	else
270 	{
271 		gtk_widget_destroy(fs);
272 	}
273 #else
274 	GtkWidget* errBox = gtk_message_dialog_new(GTK_WINDOW(app->mainWindow),
275 						   GTK_DIALOG_DESTROY_WITH_PARENT,
276 						   GTK_MESSAGE_ERROR,
277 						   GTK_BUTTONS_OK,
278 						   "Movie support was not included. To use re-build with --enable-theora.");
279 	gtk_dialog_run(GTK_DIALOG(errBox));
280 	gtk_widget_destroy(errBox);
281 #endif
282 }
283 
284 
actionQuit(GtkAction *,AppData * app)285 void actionQuit(GtkAction*, AppData* app)
286 {
287 	#ifdef GNOME
288 	saveSettingsGConf(app);
289 	#else
290 	saveSettingsFile(app);
291 	#endif /* GNOME */
292 
293 	gtk_main_quit();
294 }
295 
296 
actionSelectSol(GtkAction *,AppData * app)297 void actionSelectSol(GtkAction*, AppData* app)
298 {
299 	app->core->charEntered('H');
300 }
301 
302 
actionTourGuide(GtkAction *,AppData * app)303 void actionTourGuide(GtkAction*, AppData* app)
304 {
305 	dialogTourGuide(app);
306 }
307 
308 
actionSearchObject(GtkAction *,AppData * app)309 void actionSearchObject(GtkAction*, AppData* app)
310 {
311 	GtkWidget* dialog = gtk_dialog_new_with_buttons("Select Object",
312 	                                                GTK_WINDOW(app->mainWindow),
313 	                                                GTK_DIALOG_DESTROY_WITH_PARENT,
314 	                                                GTK_STOCK_OK, GTK_RESPONSE_OK,
315 	                                                GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
316 	                                                NULL);
317 
318 	GtkWidget* box = gtk_hbox_new(FALSE, CELSPACING);
319 	gtk_container_set_border_width(GTK_CONTAINER(box), CELSPACING);
320 	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), box, TRUE, TRUE, 0);
321 
322 	GtkWidget* label = gtk_label_new("Object name");
323 	gtk_box_pack_start(GTK_BOX(box), label, TRUE, TRUE, 0);
324 
325 	GtkWidget* entry = gtk_entry_new();
326 	gtk_box_pack_start(GTK_BOX(box), entry, TRUE, TRUE, 0);
327 
328 	gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
329 	gtk_widget_show_all(GTK_WIDGET(dialog));
330 
331 	if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
332 	{
333 		const gchar* name = gtk_entry_get_text(GTK_ENTRY(entry));
334 		if (name != NULL)
335 		{
336 			Selection sel = app->simulation->findObject(name);
337 			if (!sel.empty())
338 				app->simulation->setSelection(sel);
339 		}
340 	}
341 
342 	gtk_widget_destroy(GTK_WIDGET(dialog));
343 }
344 
345 
actionGotoObject(GtkAction *,AppData * app)346 void actionGotoObject(GtkAction*, AppData* app)
347 {
348 	dialogGotoObject(app);
349 }
350 
351 
actionCenterSelection(GtkAction *,AppData * app)352 void actionCenterSelection(GtkAction*, AppData* app)
353 {
354 	app->core->charEntered('c');
355 }
356 
357 
actionGotoSelection(GtkAction *,AppData * app)358 void actionGotoSelection(GtkAction*, AppData* app)
359 {
360 	app->core->charEntered('G');
361 }
362 
363 
actionFollowSelection(GtkAction *,AppData * app)364 void actionFollowSelection(GtkAction*, AppData* app)
365 {
366 	app->core->charEntered('F');
367 }
368 
369 
actionSyncSelection(GtkAction *,AppData * app)370 void actionSyncSelection(GtkAction*, AppData* app)
371 {
372 	app->core->charEntered('Y');
373 }
374 
375 
actionTrackSelection(GtkAction *,AppData * app)376 void actionTrackSelection(GtkAction*, AppData* app)
377 {
378 	app->core->charEntered('T');
379 }
380 
381 
actionSystemBrowser(GtkAction *,AppData * app)382 void actionSystemBrowser(GtkAction*, AppData* app)
383 {
384 	dialogSolarBrowser(app);
385 }
386 
387 
actionStarBrowser(GtkAction *,AppData * app)388 void actionStarBrowser(GtkAction*, AppData* app)
389 {
390 	dialogStarBrowser(app);
391 }
392 
393 
actionEclipseFinder(GtkAction *,AppData * app)394 void actionEclipseFinder(GtkAction*, AppData* app)
395 {
396 	dialogEclipseFinder(app);
397 }
398 
399 
actionTimeFaster(GtkAction *,AppData * app)400 void actionTimeFaster(GtkAction*, AppData* app)
401 {
402 	app->core->charEntered('L');
403 }
404 
405 
actionTimeSlower(GtkAction *,AppData * app)406 void actionTimeSlower(GtkAction*, AppData* app)
407 {
408 	app->core->charEntered('K');
409 }
410 
411 
actionTimeFreeze(GtkAction *,AppData * app)412 void actionTimeFreeze(GtkAction*, AppData* app)
413 {
414 	app->core->charEntered(' ');
415 }
416 
417 
actionTimeReal(GtkAction *,AppData * app)418 void actionTimeReal(GtkAction*, AppData* app)
419 {
420 	app->core->charEntered('\\');
421 }
422 
423 
actionTimeReverse(GtkAction *,AppData * app)424 void actionTimeReverse(GtkAction*, AppData* app)
425 {
426 	app->core->charEntered('J');
427 }
428 
429 
actionTimeSet(GtkAction *,AppData * app)430 void actionTimeSet(GtkAction*, AppData* app)
431 {
432 	dialogSetTime(app);
433 }
434 
435 
actionTimeLocal(GtkAction * action,AppData * app)436 void actionTimeLocal(GtkAction* action, AppData* app)
437 {
438 	app->showLocalTime = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
439 	updateTimeZone(app, app->showLocalTime);
440 
441 	#ifdef GNOME
442 	gconf_client_set_bool(app->client, "/apps/celestia/showLocalTime", app->showLocalTime, NULL);
443 	#endif /* GNOME */
444 }
445 
446 
actionViewerSize(GtkAction *,AppData * app)447 void actionViewerSize(GtkAction*, AppData* app)
448 {
449 	GtkWidget* dialog;
450 	int newX, currentX, currentY, winX, winY, screenX, i = 1, position = -1;
451 	char res[32];
452 
453 	screenX = gdk_screen_get_width(gdk_screen_get_default());
454 	currentX = app->glArea->allocation.width;
455 	currentY = app->glArea->allocation.height;
456 
457 	dialog = gtk_dialog_new_with_buttons("Set Viewer Size...",
458 	                                     GTK_WINDOW(app->mainWindow),
459 	                                     GTK_DIALOG_DESTROY_WITH_PARENT,
460 	                                     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
461 	                                     GTK_STOCK_OK, GTK_RESPONSE_OK,
462 	                                     NULL);
463 
464 	GtkWidget* vbox = gtk_vbox_new(FALSE, CELSPACING);
465 	gtk_container_set_border_width(GTK_CONTAINER(vbox), CELSPACING);
466 
467 	GtkWidget* label = gtk_label_new("Dimensions for Main Window:");
468 	gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
469 
470 	GtkWidget* menubox = gtk_combo_box_new_text();
471 	gtk_box_pack_start(GTK_BOX(vbox), menubox, FALSE, FALSE, 0);
472 
473 	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), vbox, TRUE, TRUE, 0);
474 
475 	while (resolutions[i] != -1)
476 	{
477 		if (position == -1 && resolutions[i-1] < currentX && resolutions[i] >= currentX)
478 		{
479 			sprintf(res, "%d x %d (current)", currentX, currentY);
480 			position = i - 1;
481 		}
482 		else if (resolutions[i] < screenX)
483 		{
484 			sprintf(res, "%d x %d", resolutions[i], int(0.75 * resolutions[i]));
485 			i++;
486 		}
487 		else
488 			break;
489 
490 		gtk_combo_box_append_text(GTK_COMBO_BOX(menubox), res);
491 	}
492 
493 	gtk_combo_box_set_active(GTK_COMBO_BOX(menubox), position);
494 
495 	gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
496 	gtk_widget_show_all(dialog);
497 
498 	if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
499 	{
500 		int active = gtk_combo_box_get_active(GTK_COMBO_BOX(menubox));
501 
502 		if (active > -1 && active != position)
503 		{
504 			/* Adjust for default entry */
505 			if (active > position) active--;
506 
507 			newX = resolutions[active + 1];
508 			gtk_window_get_size(GTK_WINDOW(app->mainWindow), &winX, &winY);
509 
510 			/* Resizing takes into account border, titlebar, and menubar
511 			   sizes. Without them only an allocation can be requested */
512 			gtk_window_resize(GTK_WINDOW(app->mainWindow), newX + winX - currentX, int(0.75 * newX) + winY - currentY);
513 		}
514 	}
515 
516 	gtk_widget_destroy(GTK_WIDGET(dialog));
517 }
518 
519 
actionFullScreen(GtkAction * action,AppData * app)520 void actionFullScreen(GtkAction* action, AppData* app)
521 {
522 	int positionX, positionY;
523 	app->fullScreen = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
524 
525 	if (app->fullScreen)
526 	{
527 		/* Save size/position, so original numbers are available for prefs */
528 		g_object_set_data(G_OBJECT(app->mainWindow), "sizeX", GINT_TO_POINTER(app->glArea->allocation.width));
529 		g_object_set_data(G_OBJECT(app->mainWindow), "sizeY", GINT_TO_POINTER(app->glArea->allocation.height));
530 		gtk_window_get_position(GTK_WINDOW(app->mainWindow), &positionX, &positionY);
531 		g_object_set_data(G_OBJECT(app->mainWindow), "positionX", GINT_TO_POINTER(positionX));
532 		g_object_set_data(G_OBJECT(app->mainWindow), "positionY", GINT_TO_POINTER(positionY));
533 
534 		gtk_window_fullscreen(GTK_WINDOW(app->mainWindow));
535 	}
536 	else
537 		gtk_window_unfullscreen(GTK_WINDOW(app->mainWindow));
538 
539 	/* Enable/Disable the Viewer Size action */
540 	gtk_action_set_sensitive(gtk_action_group_get_action(app->agMain, "ViewerSize"), !app->fullScreen);
541 
542 	#ifdef GNOME
543 	gconf_client_set_bool(app->client, "/apps/celestia/fullScreen", app->fullScreen, NULL);
544 	#endif
545 }
546 
547 
actionViewOptions(GtkAction *,AppData * app)548 void actionViewOptions(GtkAction*, AppData* app)
549 {
550 	dialogViewOptions(app);
551 }
552 
553 
actionStarsMore(GtkAction *,AppData * app)554 void actionStarsMore(GtkAction*, AppData* app)
555 {
556 	app->core->charEntered(']');
557 
558 	#ifdef GNOME
559 	gconf_client_set_float(app->client, "/apps/celestia/visualMagnitude", app->simulation->getFaintestVisible(), NULL);
560 	#endif
561 }
562 
563 
actionStarsFewer(GtkAction *,AppData * app)564 void actionStarsFewer(GtkAction*, AppData* app)
565 {
566 	app->core->charEntered('[');
567 
568 	#ifdef GNOME
569 	gconf_client_set_float(app->client, "/apps/celestia/visualMagnitude", app->simulation->getFaintestVisible(), NULL);
570 	#endif
571 }
572 
573 
actionVideoSync(GtkToggleAction * action,AppData * app)574 void actionVideoSync(GtkToggleAction* action, AppData* app)
575 {
576 	app->renderer->setVideoSync(gtk_toggle_action_get_active(action));
577 
578 	#ifdef GNOME
579 	gconf_client_set_bool(app->client, "/apps/celestia/videoSync", app->renderer->getVideoSync(), NULL);
580 	#endif
581 }
582 
583 
actionMenuBarVisible(GtkToggleAction * action,AppData * app)584 void actionMenuBarVisible(GtkToggleAction* action, AppData* app)
585 {
586 	g_object_set(G_OBJECT(app->mainMenu), "visible", gtk_toggle_action_get_active(action), NULL);
587 }
588 
589 
actionMultiSplitH(GtkAction *,AppData * app)590 void actionMultiSplitH(GtkAction*, AppData* app)
591 {
592 	app->core->splitView(View::HorizontalSplit);
593 }
594 
595 
actionMultiSplitV(GtkAction *,AppData * app)596 void actionMultiSplitV(GtkAction*, AppData* app)
597 {
598 	app->core->splitView(View::VerticalSplit);
599 }
600 
601 
actionMultiCycle(GtkAction *,AppData * app)602 void actionMultiCycle(GtkAction*, AppData* app)
603 {
604 	/* Pass a Tab character */
605 	app->core->charEntered('\011');
606 }
607 
608 
actionMultiDelete(GtkAction *,AppData * app)609 void actionMultiDelete(GtkAction*, AppData* app)
610 {
611 	app->core->deleteView();
612 }
613 
614 
actionMultiSingle(GtkAction *,AppData * app)615 void actionMultiSingle(GtkAction*, AppData* app)
616 {
617 	app->core->singleView();
618 }
619 
620 
actionMultiShowFrames(GtkToggleAction * action,AppData * app)621 void actionMultiShowFrames(GtkToggleAction* action, AppData* app)
622 {
623 	app->core->setFramesVisible(gtk_toggle_action_get_active(action));
624 }
625 
626 
actionMultiShowActive(GtkToggleAction * action,AppData * app)627 void actionMultiShowActive(GtkToggleAction* action, AppData* app)
628 {
629 	app->core->setActiveFrameVisible(gtk_toggle_action_get_active(action));
630 }
631 
632 
actionMultiSyncTime(GtkToggleAction * action,AppData * app)633 void actionMultiSyncTime(GtkToggleAction* action, AppData* app)
634 {
635 	app->simulation->setSyncTime(gtk_toggle_action_get_active(action));
636 }
637 
638 
actionRunDemo(GtkAction *,AppData * app)639 void actionRunDemo(GtkAction*, AppData* app)
640 {
641 	app->core->charEntered('D');
642 }
643 
644 
actionHelpControls(GtkAction *,AppData * app)645 void actionHelpControls(GtkAction*, AppData* app)
646 {
647 	char *txt = readFromFile("controls.txt");
648 	textInfoDialog(txt, "Mouse and Keyboard Controls", app);
649 	g_free(txt);
650 }
651 
652 
actionHelpOpenGL(GtkAction *,AppData * app)653 void actionHelpOpenGL(GtkAction*, AppData* app)
654 {
655 	/* Code grabbed from winmain.cpp */
656 	char* vendor = (char*) glGetString(GL_VENDOR);
657 	char* render = (char*) glGetString(GL_RENDERER);
658 	char* version = (char*) glGetString(GL_VERSION);
659 	char* ext = (char*) glGetString(GL_EXTENSIONS);
660 
661 	string s;
662 	s = "Vendor: ";
663 	if (vendor != NULL)
664 		s += vendor;
665 	s += "\n";
666 
667 	s += "Renderer: ";
668 	if (render != NULL)
669 		s += render;
670 	s += "\n";
671 
672 	s += "Version: ";
673 	if (version != NULL)
674 		s += version;
675 	s += "\n";
676 
677 	char buf[100];
678 	GLint simTextures = 1;
679 	if (ExtensionSupported("GL_ARB_multitexture"))
680 		glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &simTextures);
681 	sprintf(buf, "Max simultaneous textures: %d\n", simTextures);
682 	s += buf;
683 
684 	GLint maxTextureSize = 0;
685 	glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
686 	sprintf(buf, "Max texture size: %d\n\n", maxTextureSize);
687 	s += buf;
688 
689 	s += "Supported Extensions:\n    ";
690 	if (ext != NULL)
691 	{
692 		string extString(ext);
693 		unsigned int pos = extString.find(' ', 0);
694 		while (pos != (unsigned int)string::npos)
695 		{
696 			extString.replace(pos, 1, "\n    ");
697 			pos = extString.find(' ', pos + 5);
698 		}
699 		s += extString;
700 	}
701 
702 	textInfoDialog(s.c_str(), "Open GL Info", app);
703 }
704 
705 
actionHelpAbout(GtkAction *,AppData * app)706 void actionHelpAbout(GtkAction*, AppData* app)
707 {
708 	const gchar* authors[] = {
709 		"Chris Laurel <claurel@shatters.net>",
710 		"Clint Weisbrod <cweisbrod@cogeco.ca>",
711 		"Fridger Schrempp <fridger.schrempp@desy.de>",
712 		"Bob Ippolito <bob@redivi.com>",
713 		"Christophe Teyssier <chris@teyssier.org>",
714 		"Hank Ramsey <hramsey@users.sourceforce.net>",
715 		"Grant Hutchison <grantcelestia@xemaps.com>",
716 		"Pat Suwalski <pat@suwalski.net>",
717 		"Toti <>",
718 		"Da-Woon Jung <dirkpitt2050@users.sf.net>",
719 		"Vincent Giangiulio <vince.gian@free.fr>",
720 		NULL
721     };
722 
723 	GdkPixbuf *logo = gdk_pixbuf_new_from_file ("celestia-logo.png", NULL);
724 
725 	gtk_show_about_dialog(GTK_WINDOW(app->mainWindow),
726 	                     "name", "Celestia",
727 	                     "version", VERSION,
728 	                     "copyright", "Copyright \xc2\xa9 2001-2011 Celestia Development Team",
729 	                     "comments", FRONTEND " Front-End",
730 	                     "website", "http://celestia.sf.net",
731 	                     "authors", authors,
732 	                     "license", readFromFile("COPYING"),
733 	                     "logo", logo,
734 	                     NULL);
735 	gdk_pixbuf_unref(logo);
736 }
737 
738 
actionVerbosity(GtkRadioAction * action,GtkRadioAction *,AppData * app)739 void actionVerbosity(GtkRadioAction* action, GtkRadioAction*, AppData* app)
740 {
741 	int value = gtk_radio_action_get_current_value(action);
742 	app->core->setHudDetail(value);
743 
744 	#ifdef GNOME
745 	gconf_client_set_int(app->client, "/apps/celestia/verbosity", value, NULL);
746 	#endif
747 }
748 
749 
actionStarStyle(GtkRadioAction * action,GtkRadioAction *,AppData * app)750 void actionStarStyle(GtkRadioAction* action, GtkRadioAction*, AppData* app)
751 {
752 	int value = gtk_radio_action_get_current_value(action);
753 	app->renderer->setStarStyle((Renderer::StarStyle)value);
754 
755 	#ifdef GNOME
756 	gconf_client_set_int(app->client, "/apps/celestia/starStyle", value, NULL);
757 	#endif
758 }
759 
760 
actionAmbientLight(GtkRadioAction * action,GtkRadioAction *,AppData * app)761 void actionAmbientLight(GtkRadioAction* action, GtkRadioAction*, AppData* app)
762 {
763 	float value = amLevels[gtk_radio_action_get_current_value(action)];
764 	app->renderer->setAmbientLightLevel(value);
765 
766 	#ifdef GNOME
767 	gconf_client_set_float(app->client, "/apps/celestia/ambientLight", value, NULL);
768 	#endif
769 }
770 
771 /* Render-Flag Actions */
actionRenderAA(GtkToggleAction * action,AppData * app)772 void actionRenderAA(GtkToggleAction* action, AppData* app)
773 {
774 	setRenderFlag(app, Renderer::ShowSmoothLines, gtk_toggle_action_get_active(action));
775 }
776 
777 
actionRenderAtmospheres(GtkToggleAction * action,AppData * app)778 void actionRenderAtmospheres(GtkToggleAction* action, AppData* app)
779 {
780 	setRenderFlag(app, Renderer::ShowAtmospheres, gtk_toggle_action_get_active(action));
781 }
782 
783 
actionRenderAutoMagnitude(GtkToggleAction * action,AppData * app)784 void actionRenderAutoMagnitude(GtkToggleAction* action, AppData* app)
785 {
786 	setRenderFlag(app, Renderer::ShowAutoMag, gtk_toggle_action_get_active(action));
787 }
788 
789 
actionRenderCelestialGrid(GtkToggleAction * action,AppData * app)790 void actionRenderCelestialGrid(GtkToggleAction* action, AppData* app)
791 {
792 	setRenderFlag(app, Renderer::ShowCelestialSphere, gtk_toggle_action_get_active(action));
793 }
794 
795 
actionRenderClouds(GtkToggleAction * action,AppData * app)796 void actionRenderClouds(GtkToggleAction* action, AppData* app)
797 {
798 	setRenderFlag(app, Renderer::ShowCloudMaps, gtk_toggle_action_get_active(action));
799 }
800 
801 
actionRenderCometTails(GtkToggleAction * action,AppData * app)802 void actionRenderCometTails(GtkToggleAction* action, AppData* app)
803 {
804 	setRenderFlag(app, Renderer::ShowCometTails, gtk_toggle_action_get_active(action));
805 }
806 
807 
actionRenderConstellationBoundaries(GtkToggleAction * action,AppData * app)808 void actionRenderConstellationBoundaries(GtkToggleAction* action, AppData* app)
809 {
810 	setRenderFlag(app, Renderer::ShowBoundaries, gtk_toggle_action_get_active(action));
811 }
812 
813 
actionRenderConstellations(GtkToggleAction * action,AppData * app)814 void actionRenderConstellations(GtkToggleAction* action, AppData* app)
815 {
816 	setRenderFlag(app, Renderer::ShowDiagrams, gtk_toggle_action_get_active(action));
817 }
818 
819 
actionRenderEclipticGrid(GtkToggleAction * action,AppData * app)820 void actionRenderEclipticGrid(GtkToggleAction* action, AppData* app)
821 {
822 	setRenderFlag(app, Renderer::ShowEclipticGrid, gtk_toggle_action_get_active(action));
823 }
824 
825 
actionRenderEclipseShadows(GtkToggleAction * action,AppData * app)826 void actionRenderEclipseShadows(GtkToggleAction* action, AppData* app)
827 {
828 	setRenderFlag(app, Renderer::ShowEclipseShadows, gtk_toggle_action_get_active(action));
829 }
830 
831 
actionRenderGalacticGrid(GtkToggleAction * action,AppData * app)832 void actionRenderGalacticGrid(GtkToggleAction* action, AppData* app)
833 {
834 	setRenderFlag(app, Renderer::ShowGalacticGrid, gtk_toggle_action_get_active(action));
835 }
836 
837 
actionRenderGalaxies(GtkToggleAction * action,AppData * app)838 void actionRenderGalaxies(GtkToggleAction* action, AppData* app)
839 {
840 	setRenderFlag(app, Renderer::ShowGalaxies, gtk_toggle_action_get_active(action));
841 }
842 
843 
actionRenderGlobulars(GtkToggleAction * action,AppData * app)844 void actionRenderGlobulars(GtkToggleAction* action, AppData* app)
845 {
846 	setRenderFlag(app, Renderer::ShowGlobulars, gtk_toggle_action_get_active(action));
847 }
848 
849 
actionRenderHorizontalGrid(GtkToggleAction * action,AppData * app)850 void actionRenderHorizontalGrid(GtkToggleAction* action, AppData* app)
851 {
852 	setRenderFlag(app, Renderer::ShowHorizonGrid, gtk_toggle_action_get_active(action));
853 }
854 
855 
actionRenderMarkers(GtkToggleAction * action,AppData * app)856 void actionRenderMarkers(GtkToggleAction* action, AppData* app)
857 {
858 	setRenderFlag(app, Renderer::ShowMarkers, gtk_toggle_action_get_active(action));
859 }
860 
861 
actionRenderNebulae(GtkToggleAction * action,AppData * app)862 void actionRenderNebulae(GtkToggleAction* action, AppData* app)
863 {
864 	setRenderFlag(app, Renderer::ShowNebulae, gtk_toggle_action_get_active(action));
865 }
866 
867 
actionRenderNightLights(GtkToggleAction * action,AppData * app)868 void actionRenderNightLights(GtkToggleAction* action, AppData* app)
869 {
870 	setRenderFlag(app, Renderer::ShowNightMaps, gtk_toggle_action_get_active(action));
871 }
872 
873 
actionRenderOpenClusters(GtkToggleAction * action,AppData * app)874 void actionRenderOpenClusters(GtkToggleAction* action, AppData* app)
875 {
876 	setRenderFlag(app, Renderer::ShowOpenClusters, gtk_toggle_action_get_active(action));
877 }
878 
879 
actionRenderOrbits(GtkToggleAction * action,AppData * app)880 void actionRenderOrbits(GtkToggleAction* action, AppData* app)
881 {
882 	setRenderFlag(app, Renderer::ShowOrbits, gtk_toggle_action_get_active(action));
883 }
884 
885 
actionRenderPlanets(GtkToggleAction * action,AppData * app)886 void actionRenderPlanets(GtkToggleAction* action, AppData* app)
887 {
888 	setRenderFlag(app, Renderer::ShowPlanets, gtk_toggle_action_get_active(action));
889 }
890 
891 
actionRenderRingShadows(GtkToggleAction * action,AppData * app)892 void actionRenderRingShadows(GtkToggleAction* action, AppData* app)
893 {
894 	setRenderFlag(app, Renderer::ShowRingShadows, gtk_toggle_action_get_active(action));
895 }
896 
897 
actionRenderStars(GtkToggleAction * action,AppData * app)898 void actionRenderStars(GtkToggleAction* action, AppData* app)
899 {
900 	setRenderFlag(app, Renderer::ShowStars, gtk_toggle_action_get_active(action));
901 }
902 
903 
actionOrbitAsteroids(GtkToggleAction * action,AppData * app)904 void actionOrbitAsteroids(GtkToggleAction* action, AppData* app)
905 {
906 	setOrbitMask(app, Body::Asteroid, gtk_toggle_action_get_active(action));
907 }
908 
909 
actionOrbitComets(GtkToggleAction * action,AppData * app)910 void actionOrbitComets(GtkToggleAction* action, AppData* app)
911 {
912 	setOrbitMask(app, Body::Comet, gtk_toggle_action_get_active(action));
913 }
914 
915 
actionOrbitMoons(GtkToggleAction * action,AppData * app)916 void actionOrbitMoons(GtkToggleAction* action, AppData* app)
917 {
918 	setOrbitMask(app, Body::Moon, gtk_toggle_action_get_active(action));
919 }
920 
921 
actionOrbitPlanets(GtkToggleAction * action,AppData * app)922 void actionOrbitPlanets(GtkToggleAction* action, AppData* app)
923 {
924 	setOrbitMask(app, Body::Planet, gtk_toggle_action_get_active(action));
925 }
926 
927 
actionOrbitSpacecraft(GtkToggleAction * action,AppData * app)928 void actionOrbitSpacecraft(GtkToggleAction* action, AppData* app)
929 {
930 	setOrbitMask(app, Body::Spacecraft, gtk_toggle_action_get_active(action));
931 }
932 
933 
actionLabelAsteroids(GtkToggleAction * action,AppData * app)934 void actionLabelAsteroids(GtkToggleAction* action, AppData* app)
935 {
936 	setLabelMode(app, Renderer::AsteroidLabels, gtk_toggle_action_get_active(action));
937 }
938 
939 
actionLabelComets(GtkToggleAction * action,AppData * app)940 void actionLabelComets(GtkToggleAction* action, AppData* app)
941 {
942 	setLabelMode(app, Renderer::CometLabels, gtk_toggle_action_get_active(action));
943 }
944 
945 
actionLabelConstellations(GtkToggleAction * action,AppData * app)946 void actionLabelConstellations(GtkToggleAction* action, AppData* app)
947 {
948 	setLabelMode(app, Renderer::ConstellationLabels, gtk_toggle_action_get_active(action));
949 }
950 
951 
actionLabelGalaxies(GtkToggleAction * action,AppData * app)952 void actionLabelGalaxies(GtkToggleAction* action, AppData* app)
953 {
954 	setLabelMode(app, Renderer::GalaxyLabels, gtk_toggle_action_get_active(action));
955 }
956 
957 
actionLabelGlobulars(GtkToggleAction * action,AppData * app)958 void actionLabelGlobulars(GtkToggleAction* action, AppData* app)
959 {
960 	setLabelMode(app, Renderer::GlobularLabels, gtk_toggle_action_get_active(action));
961 }
962 
963 
actionLabelLocations(GtkToggleAction * action,AppData * app)964 void actionLabelLocations(GtkToggleAction* action, AppData* app)
965 {
966 	setLabelMode(app, Renderer::LocationLabels, gtk_toggle_action_get_active(action));
967 }
968 
969 
actionLabelMoons(GtkToggleAction * action,AppData * app)970 void actionLabelMoons(GtkToggleAction* action, AppData* app)
971 {
972 	setLabelMode(app, Renderer::MoonLabels, gtk_toggle_action_get_active(action));
973 }
974 
975 
actionLabelNebulae(GtkToggleAction * action,AppData * app)976 void actionLabelNebulae(GtkToggleAction* action, AppData* app)
977 {
978 	setLabelMode(app, Renderer::NebulaLabels, gtk_toggle_action_get_active(action));
979 }
980 
981 
actionLabelOpenClusters(GtkToggleAction * action,AppData * app)982 void actionLabelOpenClusters(GtkToggleAction* action, AppData* app)
983 {
984 	setLabelMode(app, Renderer::OpenClusterLabels, gtk_toggle_action_get_active(action));
985 }
986 
987 
actionLabelPlanets(GtkToggleAction * action,AppData * app)988 void actionLabelPlanets(GtkToggleAction* action, AppData* app)
989 {
990 	setLabelMode(app, Renderer::PlanetLabels, gtk_toggle_action_get_active(action));
991 }
992 
993 
actionLabelSpacecraft(GtkToggleAction * action,AppData * app)994 void actionLabelSpacecraft(GtkToggleAction* action, AppData* app)
995 {
996 	setLabelMode(app, Renderer::SpacecraftLabels, gtk_toggle_action_get_active(action));
997 }
998 
999 
actionLabelStars(GtkToggleAction * action,AppData * app)1000 void actionLabelStars(GtkToggleAction* action, AppData* app)
1001 {
1002 	setLabelMode(app, Renderer::StarLabels, gtk_toggle_action_get_active(action));
1003 }
1004 
1005 
1006 /* Script opening helper called by actionOpenScript() */
openScript(const char * filename,AppData * app)1007 static void openScript(const char* filename, AppData* app)
1008 {
1009 	/* Modified From Win32 HandleOpenScript */
1010 	if (filename)
1011 	{
1012 		/* If you got here, a path and file has been specified.
1013 		 * filename contains full path to specified file. */
1014 		ContentType type = DetermineFileType(filename);
1015 
1016 		if (type == Content_CelestiaScript)
1017 		{
1018 			app->core->runScript(filename);
1019 		}
1020 		else if (type == Content_CelestiaLegacyScript)
1021 		{
1022 			ifstream scriptfile(filename);
1023 			if (!scriptfile.good())
1024 			{
1025 				GtkWidget* errBox = gtk_message_dialog_new(GTK_WINDOW(app->mainWindow),
1026 				                                           GTK_DIALOG_DESTROY_WITH_PARENT,
1027 				                                           GTK_MESSAGE_ERROR,
1028 				                                           GTK_BUTTONS_OK,
1029 				                                           "Error opening script file.");
1030 				gtk_dialog_run(GTK_DIALOG(errBox));
1031 				gtk_widget_destroy(errBox);
1032 			}
1033 			else
1034 			{
1035 				CommandParser parser(scriptfile);
1036 				CommandSequence* script = parser.parse();
1037 				if (script == NULL)
1038 				{
1039 					const vector<string>* errors = parser.getErrors();
1040 					const char* errorMsg = "";
1041 					if (errors->size() > 0)
1042 						errorMsg = (*errors)[0].c_str();
1043 					GtkWidget* errBox = gtk_message_dialog_new(GTK_WINDOW(app->mainWindow),
1044 					                                           GTK_DIALOG_DESTROY_WITH_PARENT,
1045 					                                           GTK_MESSAGE_ERROR,
1046 					                                           GTK_BUTTONS_OK, "%s",
1047 					                                           errorMsg);
1048 					gtk_dialog_run(GTK_DIALOG(errBox));
1049 					gtk_widget_destroy(errBox);
1050 				}
1051 				else
1052 				{
1053 					/* Cancel any running script */
1054 					app->core->cancelScript();
1055 					app->core->runScript(script);
1056 				}
1057 			}
1058 		}
1059 		else
1060 		{
1061 			GtkWidget* errBox = gtk_message_dialog_new(GTK_WINDOW(app->mainWindow),
1062 			                                           GTK_DIALOG_DESTROY_WITH_PARENT,
1063 			                                           GTK_MESSAGE_ERROR,
1064 			                                           GTK_BUTTONS_OK,
1065 			                                           "Bad File Type. Use *.(cel|celx|clx).");
1066 			gtk_dialog_run(GTK_DIALOG(errBox));
1067 			gtk_widget_destroy(errBox);
1068 		}
1069 	}
1070 }
1071 
1072 
1073 /* Image capturing helper called by actionCaptureImage() */
captureImage(const char * filename,AppData * app)1074 static void captureImage(const char* filename, AppData* app)
1075 {
1076 	/* Get the dimensions of the current viewport */
1077 	int viewport[4];
1078 	glGetIntegerv(GL_VIEWPORT, viewport);
1079 
1080 	bool success = false;
1081 	ContentType type = DetermineFileType(filename);
1082 	if (type == Content_Unknown)
1083 	{
1084 		GtkWidget* errBox = gtk_message_dialog_new(GTK_WINDOW(app->mainWindow),
1085 		                                           GTK_DIALOG_DESTROY_WITH_PARENT,
1086 		                                           GTK_MESSAGE_ERROR,
1087 		                                           GTK_BUTTONS_OK,
1088 		                                           "Unable to determine image file type from name, please use a name ending in '.jpg' or '.png'.");
1089 		gtk_dialog_run(GTK_DIALOG(errBox));
1090 		gtk_widget_destroy(errBox);
1091 		return;
1092 	}
1093 	else if (type == Content_JPEG)
1094 	{
1095 		success = CaptureGLBufferToJPEG(filename,
1096 		                                viewport[0], viewport[1],
1097 		                                viewport[2], viewport[3]);
1098 	}
1099 	else if (type == Content_PNG)
1100 	{
1101 		success = CaptureGLBufferToPNG(filename,
1102 		                               viewport[0], viewport[1],
1103 		                               viewport[2], viewport[3]);
1104 	}
1105 	else
1106 	{
1107 		GtkWidget* errBox = gtk_message_dialog_new(GTK_WINDOW(app->mainWindow),
1108 		                                           GTK_DIALOG_DESTROY_WITH_PARENT,
1109 		                                           GTK_MESSAGE_ERROR,
1110 		                                           GTK_BUTTONS_OK,
1111 		                                           "Currently screen capturing to only JPEG or PNG files is supported.");
1112 		gtk_dialog_run(GTK_DIALOG(errBox));
1113 		gtk_widget_destroy(errBox);
1114 		return;
1115 	}
1116 
1117 	if (!success)
1118 	{
1119 		GtkWidget* errBox = gtk_message_dialog_new(GTK_WINDOW(app->mainWindow),
1120 		                                           GTK_DIALOG_DESTROY_WITH_PARENT,
1121 		                                           GTK_MESSAGE_ERROR,
1122 		                                           GTK_BUTTONS_OK,
1123 		                                           "Error writing captured image.");
1124 		gtk_dialog_run(GTK_DIALOG(errBox));
1125 		gtk_widget_destroy(errBox);
1126 	}
1127 }
1128 
1129 /* Image capturing helper called by actionCaptureImage() */
1130 #ifdef THEORA
captureMovie(const char * filename,int aspect,float fps,float quality,AppData * app)1131 static void captureMovie(const char* filename, int aspect, float fps, float quality, AppData* app)
1132 {
1133 	/* Get the dimensions of the current viewport */
1134 	int viewport[4];
1135 	glGetIntegerv(GL_VIEWPORT, viewport);
1136 
1137 	MovieCapture* movieCapture = new OggTheoraCapture();
1138 	switch (aspect)
1139 	{
1140 	case 0:
1141 		movieCapture->setAspectRatio(1, 1);
1142 		break;
1143 	case 1:
1144 		movieCapture->setAspectRatio(4, 3);
1145 		break;
1146 	case 2:
1147 		movieCapture->setAspectRatio(16, 9);
1148 		break;
1149 	default:
1150 		movieCapture->setAspectRatio(viewport[2], viewport[3]);
1151 		break;
1152 	}
1153 	movieCapture->setQuality(quality);
1154 
1155 	bool success = movieCapture->start(filename, viewport[2], viewport[3], fps);
1156 	if (success)
1157 		app->core->initMovieCapture(movieCapture);
1158 	else
1159 	{
1160 		delete movieCapture;
1161 
1162 		GtkWidget* errBox = gtk_message_dialog_new(GTK_WINDOW(app->mainWindow),
1163 		                                           GTK_DIALOG_DESTROY_WITH_PARENT,
1164 		                                           GTK_MESSAGE_ERROR,
1165 		                                           GTK_BUTTONS_OK,
1166 		                                           "Error initializing movie capture.");
1167 		gtk_dialog_run(GTK_DIALOG(errBox));
1168 		gtk_widget_destroy(errBox);
1169 	}
1170 }
1171 #endif
1172 
1173 /* Runs a dialog that displays text; should be replaced at some point with
1174    a more elegant solution. */
textInfoDialog(const char * txt,const char * title,AppData * app)1175 static void textInfoDialog(const char *txt, const char *title, AppData* app)
1176 {
1177 	/* I would use a gnome_message_box dialog for this, except they don't seem
1178 	 * to notice that the texts are so big that they create huge windows, and
1179 	 * would work better with a scrolled window. Deon */
1180 	GtkWidget* dialog = gtk_dialog_new_with_buttons(title,
1181 	                                                GTK_WINDOW(app->mainWindow),
1182 	                                                GTK_DIALOG_DESTROY_WITH_PARENT,
1183 	                                                GTK_STOCK_OK, GTK_RESPONSE_OK,
1184 	                                                NULL);
1185 
1186 	GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1187 	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), scrolled_window, TRUE, TRUE, 0);
1188 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrolled_window),
1189 	                               GTK_POLICY_AUTOMATIC,
1190 	                               GTK_POLICY_AUTOMATIC);
1191 	gtk_widget_show(scrolled_window);
1192 
1193 	GtkWidget *text = gtk_label_new(txt);
1194 	gtk_widget_modify_font(text, pango_font_description_from_string("mono"));
1195 	gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window), GTK_WIDGET(text));
1196 	gtk_widget_show(text);
1197 
1198 	gtk_window_set_default_size(GTK_WINDOW(dialog), 600, 400); /* Absolute Size, urghhh */
1199 	gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
1200 
1201 	gtk_dialog_run(GTK_DIALOG(dialog));
1202 	gtk_widget_destroy(dialog);
1203 }
1204 
1205 
1206 /* Calculates and sets the render-flag int */
setRenderFlag(AppData * a,int flag,gboolean state)1207 static void setRenderFlag(AppData* a, int flag, gboolean state)
1208 {
1209 	int rf = (a->renderer->getRenderFlags() & ~flag) | (state ? flag : 0);
1210 	a->renderer->setRenderFlags(rf);
1211 
1212 	#ifdef GNOME
1213 	/* Update GConf */
1214 	gcSetRenderFlag(flag, state, a->client);
1215 	#endif /* GNOME */
1216 }
1217 
1218 
1219 /* Calculates and sets the orbit-mask int */
setOrbitMask(AppData * a,int mask,gboolean state)1220 static void setOrbitMask(AppData* a, int mask, gboolean state)
1221 {
1222 	int om = (a->renderer->getOrbitMask() & ~mask) | (state ? mask : 0);
1223 	a->renderer->setOrbitMask(om);
1224 
1225 	#ifdef GNOME
1226 	/* Update GConf */
1227 	gcSetOrbitMask(mask, state, a->client);
1228 	#endif /* GNOME */
1229 }
1230 
1231 
1232 /* Calculates and sets the label-mode int */
setLabelMode(AppData * a,int mode,gboolean state)1233 static void setLabelMode(AppData* a, int mode, gboolean state)
1234 {
1235 	int lm = (a->renderer->getLabelMode() & ~mode) | (state ? mode : 0);
1236 	a->renderer->setLabelMode(lm);
1237 
1238 	#ifdef GNOME
1239 	/* Update GConf */
1240 	gcSetLabelMode(mode, state, a->client);
1241 	#endif /* GNOME */
1242 }
1243 
1244 
1245 /* Synchronizes the Label Actions with the state of the core */
resyncLabelActions(AppData * app)1246 void resyncLabelActions(AppData* app)
1247 {
1248 	GtkAction* action;
1249 	const char* actionName;
1250 
1251 	/* Simply for readability */
1252 	int f = app->renderer->getLabelMode();
1253 
1254 	for (int i = Renderer::StarLabels; i <= Renderer::GlobularLabels; i *= 2)
1255 	{
1256 		switch (i)
1257 		{
1258 			case Renderer::StarLabels: actionName = "LabelStars"; break;
1259 			case Renderer::PlanetLabels: actionName = "LabelPlanets"; break;
1260 			case Renderer::MoonLabels: actionName = "LabelMoons"; break;
1261 			case Renderer::ConstellationLabels: actionName = "LabelConstellations"; break;
1262 			case Renderer::GalaxyLabels: actionName = "LabelGalaxies"; break;
1263 			case Renderer::AsteroidLabels: actionName = "LabelAsteroids"; break;
1264 			case Renderer::SpacecraftLabels: actionName = "LabelSpacecraft"; break;
1265 			case Renderer::LocationLabels: actionName = "LabelLocations"; break;
1266 			case Renderer::CometLabels: actionName = "LabelComets"; break;
1267 			case Renderer::NebulaLabels: actionName = "LabelNebulae"; break;
1268 			case Renderer::OpenClusterLabels: actionName = "LabelOpenClusters"; break;
1269 			case Renderer::GlobularLabels: actionName = "LabelGlobulars"; break;
1270 			case Renderer::I18nConstellationLabels: /* Not used yet */
1271 			default: actionName = NULL;
1272 		}
1273 
1274 		if (actionName != NULL)
1275 		{
1276 			/* Get the action */
1277 			action = gtk_action_group_get_action(app->agLabel, actionName);
1278 
1279 			/* The current i anded with the labelMode gives state of flag */
1280 			gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), (i & f));
1281 		}
1282 	}
1283 }
1284 
1285 
1286 /* Synchronizes the Render Actions with the state of the core */
resyncRenderActions(AppData * app)1287 void resyncRenderActions(AppData* app)
1288 {
1289 	GtkAction* action;
1290 	const char* actionName;
1291 
1292 	/* Simply for readability */
1293 	int rf = app->renderer->getRenderFlags();
1294 
1295 	/* Unlike the other interfaces, which go through each menu item and set
1296 	 * the corresponding renderFlag, we go the other way and set the menu
1297 	 * based on the renderFlag. Last one is ShowEcliptic. */
1298 
1299 	for (int i = Renderer::ShowStars; i <= Renderer::ShowEcliptic; i *= 2)
1300 	{
1301 		switch (i)
1302 		{
1303 			case Renderer::ShowStars: actionName = "RenderStars"; break;
1304 			case Renderer::ShowPlanets: actionName = "RenderPlanets"; break;
1305 			case Renderer::ShowGalaxies: actionName = "RenderGalaxies"; break;
1306 			case Renderer::ShowDiagrams: actionName = "RenderConstellations"; break;
1307 			case Renderer::ShowCloudMaps: actionName = "RenderClouds"; break;
1308 			case Renderer::ShowOrbits: actionName = "RenderOrbits"; break;
1309 			case Renderer::ShowCelestialSphere: actionName = "RenderCelestialGrid"; break;
1310 			case Renderer::ShowNightMaps: actionName = "RenderNightLights"; break;
1311 			case Renderer::ShowAtmospheres: actionName = "RenderAtmospheres"; break;
1312 			case Renderer::ShowSmoothLines: actionName = "RenderAA"; break;
1313 			case Renderer::ShowEclipseShadows: actionName = "RenderEclipseShadows"; break;
1314 			case Renderer::ShowStarsAsPoints: actionName = NULL; break; /* Deprecated */
1315 			case Renderer::ShowRingShadows: actionName = "RenderRingShadows"; break;
1316 			case Renderer::ShowBoundaries: actionName = "RenderConstellationBoundaries"; break;
1317 			case Renderer::ShowAutoMag: actionName = "RenderAutoMagnitude"; break;
1318 			case Renderer::ShowCometTails: actionName = "RenderCometTails"; break;
1319 			case Renderer::ShowMarkers: actionName = "RenderMarkers"; break;
1320 			case Renderer::ShowPartialTrajectories: actionName = NULL; break; /* Not useful yet */
1321 			case Renderer::ShowNebulae: actionName = "RenderNebulae"; break;
1322 			case Renderer::ShowOpenClusters: actionName = "RenderOpenClusters"; break;
1323 			case Renderer::ShowGlobulars: actionName = "RenderGlobulars"; break;
1324 			case Renderer::ShowCloudShadows: actionName = NULL; break; /* Not implemented yet */
1325 			case Renderer::ShowGalacticGrid: actionName = "RenderGalacticGrid"; break;
1326 			case Renderer::ShowEclipticGrid: actionName = "RenderEclipticGrid"; break;
1327 			case Renderer::ShowHorizonGrid: actionName = "RenderHorizontalGrid"; break;
1328 			case Renderer::ShowEcliptic: actionName = NULL; break; /* Not implemented yet */
1329 			default: actionName = NULL;
1330 		}
1331 
1332 		if (actionName != NULL)
1333 		{
1334 			/* Get the action */
1335 			action = gtk_action_group_get_action(app->agRender, actionName);
1336 
1337 			/* The current i anded with the renderFlags gives state of flag */
1338 			gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), (i & rf));
1339 		}
1340 	}
1341 }
1342 
1343 
1344 /* Synchronizes the Orbit Actions with the state of the core */
resyncOrbitActions(AppData * app)1345 void resyncOrbitActions(AppData* app)
1346 {
1347 	GtkAction* action;
1348 	const char* actionName;
1349 
1350 	/* Simply for readability */
1351 	int om = app->renderer->getOrbitMask();
1352 
1353 	for (int i = Body::Planet; i <= Body::Spacecraft; i *= 2)
1354 	{
1355 		switch (i)
1356 		{
1357 			case Body::Planet: actionName = "OrbitPlanets"; break;
1358 			case Body::Moon: actionName = "OrbitMoons"; break;
1359 			case Body::Asteroid: actionName = "OrbitAsteroids"; break;
1360 			case Body::Comet: actionName = "OrbitComets"; break;
1361 			case Body::Spacecraft: actionName = "OrbitSpacecraft"; break;
1362 			default: actionName = NULL;
1363 		}
1364 
1365 		if (actionName != NULL)
1366 		{
1367 			/* Get the action */
1368 			action = gtk_action_group_get_action(app->agOrbit, actionName);
1369 
1370 			/* The current i anded with the orbitMask gives state of flag */
1371 			gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), (i & om));
1372 		}
1373 	}
1374 }
1375 
1376 
1377 /* Synchronizes the Verbosity Actions with the state of the core */
resyncVerbosityActions(AppData * app)1378 void resyncVerbosityActions(AppData* app)
1379 {
1380 	GtkAction* action;
1381 	const char* actionName;
1382 
1383 	switch (app->core->getHudDetail())
1384 	{
1385 		case 0: actionName = "HudNone"; break;
1386 		case 1: actionName = "HudTerse"; break;
1387 		case 2: actionName = "HudVerbose"; break;
1388 		default: return;
1389 	}
1390 
1391 	/* Get the action, set the widget */
1392 	action = gtk_action_group_get_action(app->agVerbosity, actionName);
1393 	gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE);
1394 }
1395 
1396 
1397 /* Synchronizes the TimeZone Action with the state of the core */
resyncTimeZoneAction(AppData * app)1398 void resyncTimeZoneAction(AppData* app)
1399 {
1400 	/* Get the action, set the widget */
1401 	GtkAction* action = gtk_action_group_get_action(app->agMain, "TimeLocal");
1402 	gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), app->showLocalTime);
1403 }
1404 
1405 
1406 /* Synchronizes the Ambient Light Actions with the state of the core */
resyncAmbientActions(AppData * app)1407 void resyncAmbientActions(AppData* app)
1408 {
1409 	GtkAction* action;
1410 
1411 	float ambient = app->renderer->getAmbientLightLevel();
1412 
1413 	/* Try to be smart about being close to the value of a float */
1414 	if (ambient > amLevels[0] && ambient < (amLevels[1] / 2.0))
1415 		action = gtk_action_group_get_action(app->agAmbient, "AmbientNone");
1416 
1417 	else if (ambient < amLevels[1] + ((amLevels[2] - amLevels[1]) / 2.0))
1418 		action = gtk_action_group_get_action(app->agAmbient, "AmbientLow");
1419 
1420 	else
1421 		action = gtk_action_group_get_action(app->agAmbient, "AmbientMedium");
1422 
1423 	gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE);
1424 
1425 	#ifdef GNOME
1426 	/* The action callback only occurs when one of the None/Low/Medium barriers
1427 	 * is surpassed, so an update is forced. */
1428 	gconf_client_set_float(app->client, "/apps/celestia/ambientLight", ambient, NULL);
1429 	#endif
1430 }
1431 
1432 
1433 /* Synchronizes the Verbosity Actions with the state of the core */
resyncStarStyleActions(AppData * app)1434 void resyncStarStyleActions(AppData* app)
1435 {
1436 	GtkAction* action;
1437 	const char* actionName;
1438 
1439 	switch (app->renderer->getStarStyle())
1440 	{
1441 		case Renderer::FuzzyPointStars: actionName = "StarsFuzzy"; break;
1442 		case Renderer::PointStars: actionName = "StarsPoints"; break;
1443 		case Renderer::ScaledDiscStars: actionName = "StarsDiscs"; break;
1444 		default: return;
1445 	}
1446 
1447 	/* Get the action, set the widget */
1448 	action = gtk_action_group_get_action(app->agStarStyle, actionName);
1449 	gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE);
1450 }
1451 
1452 
1453 /* Placeholder for when galaxy brightness is added as an action */
resyncGalaxyGainActions(AppData * app)1454 void resyncGalaxyGainActions(AppData* app)
1455 {
1456 	float gain = Galaxy::getLightGain();
1457 
1458 	#ifdef GNOME
1459 	gconf_client_set_float(app->client, "/apps/celestia/galaxyLightGain", gain, NULL);
1460 	#endif
1461 }
1462 
1463 
1464 /* Synchronizes the Texture Resolution with the state of the core */
resyncTextureResolutionActions(AppData * app)1465 void resyncTextureResolutionActions(AppData* app)
1466 {
1467 	int resolution = app->renderer->getResolution();
1468 
1469 	#ifdef GNOME
1470 	gconf_client_set_int(app->client, "/apps/celestia/textureResolution", resolution, NULL);
1471 	#endif /* GNOME */
1472 }
1473