1 // StarPlot - A program for interactively viewing 3D maps of stellar positions.
2 // Copyright (C) 2000 Kevin B. McCarty
3 //
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License
6 // as published by the Free Software Foundation; either version 2
7 // of the License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18
19 /*
20 filedialogs.cc
21 This file contains code for the dialog boxes in the StarPlot File menu,
22 and also code for reading to / writing from the .starplotrc settings file.
23 */
24
25 #include <gtk/gtk.h>
26 #include "starplot.h"
27 #include <cstring>
28 #include <cstdlib> // for getenv()
29
30 using std::endl;
31 using std::string;
32
33 static bool read_params(const string & filename);
34 static bool write_params(const string & filename, const string & header);
35 static void my_gtk_file_select (void (*)(GtkWidget *, GtkFileSelection *),
36 const string &, const string &);
37
starplot_quit()38 void starplot_quit()
39 {
40 delete globals::program_viewer;
41 delete globals::chart_stararray;
42 write_rc_file();
43 gtk_main_quit();
44 return;
45 }
46
47
48 /* File selection dialogues */
49 /* These cover selection of data / parameter files to load / save, and also
50 * the selection of the help browser program. */
51
52 /* This opens a new file */
file_ok_sel(GtkWidget * w,GtkFileSelection * fs)53 static void file_ok_sel(GtkWidget *w, GtkFileSelection *fs)
54 {
55 globals::chart_rules.ChartFileNames = StringList(1,
56 gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
57 redraw_all(FILE_CHANGE);
58 }
59
60 /* This merges the selected file with the other(s) already open */
merge_ok_sel(GtkWidget * w,GtkFileSelection * fs)61 static void merge_ok_sel(GtkWidget *w, GtkFileSelection *fs)
62 {
63 string filename = gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs));
64
65 // make sure the file we want to merge isn't already open
66 // (not foolproof due to the possibility of symlinks, etc., but better
67 // than nothing)
68 iterate (StringList, globals::chart_rules.ChartFileNames, name_ptr)
69 if (*name_ptr == filename) return;
70
71 globals::chart_rules.ChartFileNames.push_back(filename);
72 redraw_all(FILE_CHANGE);
73 }
74
75 /* This opens a new set of chart parameters */
param_open_sel(GtkWidget * w,GtkFileSelection * fs)76 static void param_open_sel(GtkWidget *w, GtkFileSelection *fs)
77 {
78 string filename = gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs));
79 if (read_params(filename))
80 redraw_all(FILE_CHANGE);
81 }
82
83 /* This saves the current chart parameters to a file */
param_save_sel(GtkWidget * w,GtkFileSelection * fs)84 static void param_save_sel(GtkWidget *w, GtkFileSelection *fs)
85 {
86 string filename = gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs));
87
88 if (!write_params(filename, string("# ") + _("StarPlot Parameter File\n")))
89 my_gtk_error (0, starstrings::ssprintf(
90 _("Sorry, couldn't create file \"%s\"."), filename.c_str()).c_str());
91 }
92
93 /* This saves the current chart as a PNG image file */
file_save_sel(GtkWidget * w,GtkFileSelection * fs)94 static void file_save_sel(GtkWidget *w, GtkFileSelection *fs)
95 {
96 string filename = gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs));
97 unsigned len = filename.size();
98
99 if (len <= 4 || filename.substr(len - 4) != ".png")
100 filename += ".png";
101
102 GError *error = NULL;
103 GdkPixbuf *buffer =
104 gdk_pixbuf_get_from_drawable (NULL, globals::program_pixmap,
105 gdk_colormap_get_system(), 0, 0, 0, 0, -1, -1);
106
107 if (!buffer)
108 my_gtk_error (0,
109 _("Sorry, an error occurred\ncreating the buffer."));
110
111 else if (!gdk_pixbuf_save (buffer, filename.c_str(), "png", &error, NULL))
112 my_gtk_error (0, starstrings::ssprintf(
113 _("Sorry, couldn't create file \"%s\"."), filename.c_str()).c_str());
114 }
115
116 #ifndef BROWSER
117 /* This selects a help file browser */
help_ok_sel(GtkWidget * w,GtkFileSelection * fs)118 static void help_ok_sel(GtkWidget *w, GtkFileSelection *fs)
119 {
120 globals::program_help_browser =
121 gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs));
122 }
123
124 /* This selects a help file browser and also opens the help documentation */
help_open_sel(GtkWidget * w,GtkFileSelection * fs)125 static void help_open_sel(GtkWidget *w, GtkFileSelection *fs)
126 {
127 help_ok_sel(w, fs);
128 helpdocs();
129 }
130 #endif
131
132 /* We need several functions for the menu generator, plus one for the case
133 * where a help browser must be selected before opening help docs,
134 * but they all call the same interface, just choose the correct file
135 * selection function */
get_local_dir()136 static std::string get_local_dir()
137 {
138 using namespace std; // is getenv() in std or :: ?
139 string str = "~";
140
141 const char * result = getenv("PWD");
142 if (result && strlen(result)) str = result;
143 if (str[str.size() - 1] != '/') str += '/';
144 return str;
145 }
146
file_open()147 void file_open()
148 {
149 my_gtk_file_select(file_ok_sel,
150 _("StarPlot: Select data file to open"), DATADIR "/");
151 }
file_merge()152 void file_merge()
153 {
154 my_gtk_file_select(merge_ok_sel,
155 _("StarPlot: Select data file to merge"), DATADIR "/");
156 }
params_open()157 void params_open()
158 {
159 my_gtk_file_select(param_open_sel,
160 _("StarPlot: Select parameter file to use"), get_local_dir().c_str());
161 }
params_save()162 void params_save()
163 {
164 my_gtk_file_select(param_save_sel,
165 _("StarPlot: Save chart parameters as:"), get_local_dir().c_str());
166 }
file_save()167 void file_save()
168 {
169 my_gtk_file_select(file_save_sel,
170 _("StarPlot: Save chart as PNG image"), get_local_dir().c_str());
171 }
172
173 #ifndef BROWSER
help_select()174 void help_select()
175 {
176 string file;
177 if (starstrings::isempty(globals::program_help_browser))
178 file = "/usr/bin/";
179 else
180 file = globals::program_help_browser;
181 my_gtk_file_select(help_ok_sel,
182 _("StarPlot: Select help browser program"), file);
183 }
184 #endif
185
help_select_and_open()186 void help_select_and_open()
187 {
188 #ifndef BROWSER
189 string file;
190 if (starstrings::isempty(globals::program_help_browser))
191 file = "/usr/bin/";
192 else
193 file = globals::program_help_browser;
194 my_gtk_file_select(help_open_sel,
195 _("StarPlot: Select help browser program"), file);
196 #endif
197 }
198
199 /* The interface which creates the dialog */
my_gtk_file_select(void (* select_function)(GtkWidget *,GtkFileSelection *),const string & title,const string & startfile)200 void my_gtk_file_select
201 (void (*select_function)(GtkWidget *, GtkFileSelection *), const string &title,
202 const string &startfile)
203 {
204 GtkWidget *filew;
205
206 filew = gtk_file_selection_new (title.c_str());
207 gtk_window_set_modal (GTK_WINDOW (filew), true);
208 g_signal_connect (G_OBJECT (filew), "destroy",
209 G_CALLBACK (gtk_widget_destroy),
210 GTK_WIDGET (filew));
211
212 /* Connect the ok_button to the appropriate file selection function */
213 g_signal_connect (G_OBJECT (GTK_FILE_SELECTION (filew)->ok_button),
214 "clicked", G_CALLBACK (select_function), filew);
215 g_signal_connect_swapped (G_OBJECT (GTK_FILE_SELECTION
216 (filew)->ok_button),
217 "clicked", G_CALLBACK (gtk_widget_destroy),
218 G_OBJECT (filew));
219
220 /* Connect the cancel_button to destroy the widget */
221 g_signal_connect_swapped (G_OBJECT (GTK_FILE_SELECTION
222 (filew)->cancel_button),
223 "clicked", G_CALLBACK (gtk_widget_destroy),
224 G_OBJECT (filew));
225
226 /* Set the default filename */
227 gtk_file_selection_set_filename (GTK_FILE_SELECTION(filew), startfile.c_str());
228
229 gtk_widget_show(filew);
230 }
231
232
233
234 // read_params() : Read a StarPlot parameters file. Note that this function
235 // will have problems if any of the filenames stored in ~/.starplotrc contain
236 // trailing spaces or any of these characters: # = ( ) [ ] { }
237 // I think we can live with this limitation.
238
read_params(const string & filename)239 bool read_params(const string &filename)
240 {
241 std::ifstream paramstream;
242 paramstream.open(filename.c_str());
243 if (!paramstream.good()) return false;
244
245 int files = 0;
246 char line[1000];
247 while (paramstream.getline(line, 1000)) {
248 // delete comments and munch all kinds of parentheses (which are used
249 // only to make the RC file entries look nicer)
250 for (unsigned int i = 0; i < strlen(line); i++) {
251 if (line[i] == '#') line[i] = 0;
252 if (line[i] == '(' || line[i] == ')' || line[i] == '[' || line[i] == ']'
253 || line[i] == '{' || line[i] == '}')
254 line[i] = ' ';
255 }
256 StringList assign(line, '=');
257 if (assign.size() < 2) continue;
258 assign.stripspace();
259
260 if (assign[0] == string("DataFile")) {
261 if (files == 0)
262 globals::chart_rules.ChartFileNames = StringList(1, assign[1]);
263 else
264 globals::chart_rules.ChartFileNames.push_back(assign[1]);
265 files++;
266 }
267 else if (assign[0] == string("Location")) {
268 StringList coords(assign[1], ',');
269 if (coords.size() != 3) continue;
270 starstrings::stripspace(coords[2]);
271 globals::chart_rules.ChartLocation = Vector3(starmath::atof(coords[0]),
272 starmath::atof(coords[1]),
273 starmath::atof(coords[2]));
274 }
275 else if (assign[0] == string("Orientation")) {
276 StringList orient(assign[1], ',');
277 if (orient.size() != 2) continue;
278 orient.stripspace();
279 globals::chart_rules.ChartOrientation =
280 SolidAngle(starmath::atof(orient[0]),
281 starmath::atof(orient[1]));
282 }
283 else if (assign[0] == string("Radius"))
284 globals::chart_rules.ChartRadius = starmath::atof(assign[1]);
285 else if (assign[0] == string("MagLimits")) {
286 StringList maglim(assign[1], ',');
287 if (maglim.size() != 2) continue;
288 maglim.stripspace();
289 globals::chart_rules.ChartBrightestMagnitude = starmath::atof(maglim[0]);
290 globals::chart_rules.ChartDimmestMagnitude = starmath::atof(maglim[1]);
291 }
292 else if (assign[0] == string("HRMagLimits")) {
293 StringList maglim(assign[1], ',');
294 if (maglim.size() != 2) continue;
295 maglim.stripspace();
296 globals::hr_mag_bright = starmath::atof(maglim[0]);
297 globals::hr_mag_dim = starmath::atof(maglim[1]);
298 }
299 else if (assign[0] == string("AutosetDimMag"))
300 globals::chart_rules.ChartAutosetDimmestMagnitude = static_cast<bool>
301 (starmath::atoi(assign[1]));
302 else if (assign[0] == string("HideCompanions"))
303 globals::chart_rules.ChartHideCompanions = static_cast<bool>
304 (starmath::atoi(assign[1]));
305 else if (assign[0] == string("SpecClassDisplay")) {
306 StringList classes(assign[1], ',');
307 if (classes.size() != 10) continue;
308 classes.stripspace();
309 for (unsigned int i = 0; i < 10; i++)
310 globals::chart_rules.StarClasses[i] = static_cast<bool>
311 (starmath::atoi(classes[i]));
312 }
313 else if (assign[0] == string("ChartUnits")) {
314 distance_unit units[4];
315 StringList unit_strs(assign[1], ',');
316 unit_strs.stripspace();
317 unsigned int i;
318 for (i = 0; i < unit_strs.size(); i++) {
319 if (i == 4) break;
320 units[i] = starstrings::str_to_unit(unit_strs[i]);
321 }
322 // fill any unfilled entries caused by truncated list in rc file
323 if (!i) { units[0] = DIST_LY; i = 1; }
324 if (i < 4)
325 for (unsigned int j = i; j < 4; j++)
326 units[j] = units[i - 1];
327 set_distance_units(units, false);
328 }
329 else if (assign[0] == string("CelestialCoords"))
330 set_coord_item(static_cast<bool>(starmath::atoi(assign[1])), false);
331 else if (assign[0] == string("StarDiameters"))
332 set_diameter_item(static_cast<star_diameter_t>(starmath::atoi(assign[1])),
333 false);
334 else if (assign[0] == string("StarLabels"))
335 set_label_item(static_cast<star_label_t>(starmath::atoi(assign[1])),
336 false);
337 else if (assign[0] == string("StarBars"))
338 set_toggle_item(&globals::chart_rules.StarBars,
339 static_cast<bool>(starmath::atoi(assign[1])),
340 TOGGLE_BARS, false);
341 else if (assign[0] == string("ChartGrid"))
342 set_toggle_item(&globals::chart_rules.ChartGrid,
343 static_cast<bool>(starmath::atoi(assign[1])),
344 TOGGLE_GRID, false);
345 else if (assign[0] == string("ChartLegend"))
346 set_toggle_item(&globals::chart_rules.ChartLegend,
347 static_cast<bool>(starmath::atoi(assign[1])),
348 TOGGLE_LEGEND, false);
349 #ifndef BROWSER
350 else if (assign[0] == string("HelpBrowser"))
351 globals::program_help_browser = assign[1];
352 #endif
353 else if (assign[0] == string("FontDescription"))
354 globals::display_fontname = assign[1];
355 }
356
357 return true;
358 }
359
360
361 /* write_params() : Save the current StarPlot parameters to a file. */
362
write_params(const string & filename,const string & header)363 bool write_params(const string & filename, const string & header)
364 {
365 std::ofstream paramstream;
366 paramstream.open(filename.c_str());
367 if (!paramstream.good()) return false;
368
369 paramstream << header << endl;
370
371 iterate (StringList, globals::chart_rules.ChartFileNames, name_ptr)
372 paramstream << "DataFile = " << *name_ptr << endl << endl;
373
374 std::streamsize oldprec = paramstream.precision();
375 paramstream.precision(12);
376 paramstream << "# " << _("(X, Y, Z) of chart center, in light-years") << endl
377 << "Location = ( "
378 << globals::chart_rules.ChartLocation.getX() << ", "
379 << globals::chart_rules.ChartLocation.getY() << ", "
380 << globals::chart_rules.ChartLocation.getZ() << " )" << endl;
381 paramstream << "Radius = "
382 << globals::chart_rules.ChartRadius << " # " << _("light-years")
383 << endl << endl;
384
385 paramstream << "# " <<
386 /* TRANSLATORS: RA stands for "Right Ascension"; Dec for "Declination" */
387 _("(RA, Dec) of orientation vector, in radians")
388 << endl
389 << "Orientation = ( "
390 << globals::chart_rules.ChartOrientation.getPhi()
391 << ", " << globals::chart_rules.ChartOrientation.getTheta()
392 << " )" << endl << endl;
393 paramstream.precision(oldprec);
394
395 paramstream << "MagLimits = [ "
396 << globals::chart_rules.ChartBrightestMagnitude << ", "
397 << globals::chart_rules.ChartDimmestMagnitude << " ]" << endl;
398 paramstream << "HRMagLimits = [ "
399 << globals::hr_mag_bright << ", " << globals::hr_mag_dim << " ]"
400 << endl;
401 paramstream << "AutosetDimMag = "
402 << globals::chart_rules.ChartAutosetDimmestMagnitude
403 << endl;
404 paramstream << "HideCompanions = "
405 << globals::chart_rules.ChartHideCompanions << endl << endl;
406
407 paramstream << "# SpecClassDisplay: { O, B, A, F, G, K, M, "
408 /* TRANSLATORS: "dwarf" refers to a class of star. */
409 << _("dwarf") << ", " << _("unclassified")
410 << ", " << _("non-stellar") << " }"
411 << endl;
412 paramstream << "# " << _("1 if displayed, 0 if not displayed") << endl;
413 paramstream << "SpecClassDisplay = { ";
414 for (unsigned int i = 0; i < 9; i ++)
415 paramstream << globals::chart_rules.StarClasses[i] << ", ";
416 paramstream << globals::chart_rules.StarClasses[9] << " }" << endl << endl;
417
418 paramstream << "CelestialCoords = "
419 << globals::chart_rules.CelestialCoords << endl;
420 paramstream << "StarDiameters = "
421 << globals::chart_rules.StarDiameters << endl;
422 paramstream << "StarLabels = "
423 << globals::chart_rules.StarLabels << endl;
424 paramstream << "StarBars = " << globals::chart_rules.StarBars << endl;
425 paramstream << "ChartGrid = " << globals::chart_rules.ChartGrid << endl;
426 paramstream << "ChartLegend = " << globals::chart_rules.ChartLegend << endl;
427
428 paramstream << endl << "# ChartUnits: { "
429 << _("long-range") << ", " << _("standard") << ", "
430 << _("short-range") << ", " << _("very short-range") << " }"
431 << endl;
432 paramstream << "# " << _("Allowed units:") << " ";
433 for (unsigned int i = 0; i < N_DIST - 1; i++)
434 paramstream << distance_cname[i] << ", ";
435 paramstream << distance_cname[N_DIST - 1] << endl;
436 paramstream << "ChartUnits = { ";
437 for (unsigned int i = 0; i < 3; i++)
438 paramstream << distance_cname[globals::chart_rules.ChartUnits[i]] << ", ";
439 paramstream << distance_cname[globals::chart_rules.ChartUnits[3]] << " }"
440 << endl << endl;;
441
442 paramstream << "FontDescription = " << globals::display_fontname << endl;
443 if (! starstrings::isempty(globals::program_help_browser))
444 paramstream << "HelpBrowser = " << globals::program_help_browser << endl;
445
446 paramstream << endl;
447 return true;
448 }
449
450
451
452 // read_rc_file() : Called when the program begins.
453
read_rc_file()454 bool read_rc_file()
455 {
456 string homedir = std::getenv("HOME");
457 return read_params(homedir + "/.starplotrc");
458 }
459
460
461 // write_rc_file() : Called as the program is exiting.
462
write_rc_file()463 bool write_rc_file()
464 {
465 string homedir = std::getenv("HOME");
466 string header = string("# ") + _("StarPlot user settings file") + "\n\n" +
467 /* TRANSLATORS: Prefix each line of this text with a '#' character. */ _(
468 "# Read by StarPlot when starting and rewritten automatically on\n\
469 # program exit. If you edit this file, note that distances are in\n\
470 # light-years and angles are in radians.\n\n");
471 return write_params(homedir + "/.starplotrc", header);
472 }
473
474