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