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   chartdialogs.cc
21   This file contains code for the dialog boxes in the StarPlot Chart menu.
22 */
23 
24 #include "starplot.h"
25 #include <gtk/gtk.h>
26 using std::string;
27 
28 /* automagnitude(): Tries to estimate a good dim magnitude cutoff for a given
29  *  chart radius. */
automagnitude(double radius)30 double automagnitude(double radius)
31 {
32   if (radius <= 10) return 25;
33   else if (radius <   12.5) return 25 - (radius -   10) * 4.4;
34   else if (radius <   15) return 14   - (radius - 12.5) * 1.0;
35   else if (radius <   20) return 11.5 - (radius -   15) * 0.8;
36   else if (radius <   30) return  7.5 - (radius -   20) * 0.26;
37   else if (radius <   40) return  4.9 - (radius -   30) * 0.14;
38   else if (radius <   50) return  3.5 - (radius -   40) * 0.10;
39   else if (radius <   75) return  2.5 - (radius -   50) * 0.068;
40   else if (radius <  150) return  0.8 - (radius -   75) * 0.0173;
41   else if (radius <  200) return -0.5 - (radius -  150) * 0.002;
42   else if (radius <  600) return -0.6 - (radius -  200) * 0.005;
43   else if (radius < 1000) return -2.6 - (radius -  600) * 0.0025;
44   else if (radius < 2000) return -3.6 - (radius - 1000) * 0.001;
45   else if (radius < 4000) return -4.6 - (radius - 2000) * 0.00045;
46   else if (radius <10000) return -5.5 - (radius - 4000) * 0.000133;
47   else if (radius <12500) return -6.3;
48   else return -6.4;
49 }
50 
51 /* my_gtk_position_dialog(): Sets up a box containing entry boxes for
52  *  RA and Declination */
my_gtk_position_dialog(GtkWidget ** entrybox,GtkWidget * insertionbox,SolidAngle posn,bool celestial_coords)53 void my_gtk_position_dialog( GtkWidget **entrybox, GtkWidget *insertionbox,
54 			     SolidAngle posn, bool celestial_coords )
55 {
56   GtkWidget *table, *labels[8];
57   string labeltext[8], boxentries[6];
58 
59   labeltext[5] = DEGREE_UTF8;
60   labeltext[6] = "'";
61   labeltext[7] = "\"";
62 
63   if (celestial_coords) {
64     labeltext[1] =
65       /* TRANSLATORS: This is the abbreviation for
66          "hours of right ascension". */
67       _("h");
68     labeltext[2] =
69       /* TRANSLATORS: This is the abbreviation for
70          "minutes of right ascension". */
71       _("m");
72     labeltext[3] =
73       /* TRANSLATORS: This is the abbreviation for
74          "seconds of right ascension". */
75       _("s");
76     labeltext[0] = _("Right Ascension:"); labeltext[4] = _("Declination:");
77   }
78   else {
79     labeltext[1] = DEGREE_UTF8; labeltext[2] = "'"; labeltext[3] = "\"";
80     labeltext[0] = _("Galactic Longitude:");
81     labeltext[4] = _("Galactic Latitude:");
82   }
83 
84   StringList rastrings=starstrings::ra_to_strs(posn.getPhi(),celestial_coords);
85   StringList decstrings = starstrings::dec_to_strs(posn.getTheta());
86   for (unsigned int i = 0; i < 3; i++) boxentries[i] = rastrings[i];
87   for (unsigned int i = 0; i < 3; i++) boxentries[i + 3] = decstrings[i];
88 
89   for (int i = 0; i < 6; i++) {
90     starstrings::stripspace(boxentries[i]);
91     entrybox[i] = gtk_entry_new_with_max_length (10);
92     gtk_entry_set_text (GTK_ENTRY (entrybox[i]), boxentries[i].c_str());
93     gtk_widget_set_usize (entrybox[i], 40, 20);
94     gtk_widget_show (entrybox[i]);
95   }
96   for (int i = 0; i < 8; i++) {
97     labels[i] = gtk_label_new (labeltext[i].c_str());
98     gtk_misc_set_alignment (GTK_MISC (labels[i]), 0.0F, 0.0F);
99     gtk_widget_show (labels[i]);
100   }
101 
102   table = gtk_table_new (2, 7, false);
103   gtk_widget_show (table);
104   for (int i = 0; i < 2; i++) {
105     gtk_table_attach (GTK_TABLE (table), labels[i*4], 0, 1, i, i + 1,
106 		      (GtkAttachOptions)GTK_FILL, (GtkAttachOptions)GTK_FILL,
107 		      5, 5);
108     for (int j = 0; j < 3; j++) {
109       gtk_table_attach (GTK_TABLE (table), entrybox[i*3 + j],
110 			j*2 + 1, j*2 + 2, i, i + 1,
111 			(GtkAttachOptions)0, (GtkAttachOptions)0, 5, 5);
112       gtk_table_attach (GTK_TABLE (table), labels[i*4 + 1 + j],
113 			j*2 + 2, j*2 + 3, i, i + 1,
114 			(GtkAttachOptions)0, (GtkAttachOptions)0, 5, 5);
115     }
116   }
117   gtk_box_pack_start (GTK_BOX (insertionbox), table, false, false, 0);
118 
119   return;
120 }
121 
122 
123 /* Handler for the Chart->Define Chart menu item */
124 
125 /* The top-level Chart Define window pointer */
126 GtkWidget * definedialog;
127 
128 /* Callback functions */
129 
130 /* chart_define_callback(): Set the chart parameters to what was specified
131  * in the dialog box and update the chart. */
chart_define_callback(GtkWidget * widget,gpointer chart_define_data)132 void chart_define_callback(GtkWidget *widget, gpointer chart_define_data)
133 {
134   GtkWidget **data = (GtkWidget **)chart_define_data;
135   double oldchartradius = globals::chart_rules.ChartRadius;
136   double newchartradius = starmath::atof(
137 		          gtk_entry_get_text (GTK_ENTRY (data[8])))
138 		  / distance_conversion[globals::chart_rules.ChartUnits[1]];
139 
140   if (newchartradius <= 0) {
141     my_gtk_error (GTK_WINDOW (definedialog),
142 	  starstrings::ssprintf(
143 _("You have entered a negative or\n\
144 zero value (%s %s) for the chart radius!\n\
145 Ignoring the change in this parameter."),
146 	    gtk_entry_get_text (GTK_ENTRY (data[8])),
147 	    distance_name[globals::chart_rules.ChartUnits[1]]).c_str());
148     newchartradius = oldchartradius;
149   }
150 
151   globals::chart_rules.ChartLocation =
152     SolidAngle( starstrings::strs_to_ra(
153 			    gtk_entry_get_text (GTK_ENTRY (data[0])),
154 			    gtk_entry_get_text (GTK_ENTRY (data[1])),
155 			    gtk_entry_get_text (GTK_ENTRY (data[2])),
156 			    globals::chart_rules.CelestialCoords),
157 		starstrings::strs_to_dec(
158 			     gtk_entry_get_text (GTK_ENTRY (data[3])),
159 			     gtk_entry_get_text (GTK_ENTRY (data[4])),
160 			     gtk_entry_get_text (GTK_ENTRY (data[5]))
161 			   )
162 		).toCartesian(starmath::atof(
163 				gtk_entry_get_text(GTK_ENTRY(data[6])))
164 		  / distance_conversion[globals::chart_rules.ChartUnits[1]]);
165 
166   globals::chart_rules.ChartRadius = newchartradius;
167 
168   /* Even if the magnitude autoset feature is on, the user may have set
169    *  the dim magnitude limit manually.  We should discard the user's
170    *  changes only if the new chart radius differs from the old radius.
171    *  In this test, we compare the ratio of new and old radii to 1.0 in order
172    *  to get around floating point imprecision. */
173 
174   if (globals::chart_rules.ChartAutosetDimmestMagnitude
175       && (oldchartradius == 0.0
176 	  || fabs(newchartradius / oldchartradius - 1.0) > 0.01))
177     globals::chart_rules.ChartDimmestMagnitude =
178       automagnitude(globals::chart_rules.ChartRadius);
179 
180   redraw_all(LOCATION_CHANGE);
181   return;
182 }
183 
184 /* chart_define_defaults(): Restore the values handled by this dialog box
185  * to their default settings and update the chart. */
chart_define_defaults(GtkWidget * widget,gpointer dummy)186 void chart_define_defaults(GtkWidget *widget, gpointer dummy /* not used */)
187 {
188   double oldchartradius = globals::chart_rules.ChartRadius;
189   double newchartradius = defaults::chart_rules.ChartRadius;
190   RESET_GLOBAL(chart_rules.ChartLocation);
191   RESET_GLOBAL(chart_rules.ChartRadius);
192 
193   /* Even if the magnitude autoset feature is on, the user may have set
194    *  the dim magnitude limit manually.  We should discard the user's
195    *  changes only if the new chart radius differs from the old radius.
196    *  In this test, we compare the ratio of new and old radii to 1.0 in order
197    *  to get around floating point imprecision. */
198 
199   if (globals::chart_rules.ChartAutosetDimmestMagnitude
200       && (oldchartradius == 0.0
201 	  || fabs(newchartradius / oldchartradius - 1.0) > 0.01))
202     globals::chart_rules.ChartDimmestMagnitude =
203       automagnitude(globals::chart_rules.ChartRadius);
204 
205   redraw_all(LOCATION_CHANGE);
206   return;
207 }
208 
209 /* chart_define_search(): Look up the star in the star name entry box, and if
210  *  found, insert its coordinates into all the other entry boxes.  Note: this
211  *  function does not do anything else, it is still up to the user to hit
212  *  OK to set these coordinates! */
chart_define_search(GtkWidget * widget,gpointer chart_define_data)213 void chart_define_search(GtkWidget *widget, gpointer chart_define_data)
214 {
215   GtkWidget **data = (GtkWidget **)chart_define_data;
216   string starname = gtk_entry_get_text (GTK_ENTRY (data[7]));
217   StarArray searchresults;
218 
219   if (starstrings::isempty(starname)) return;
220 
221   StringList ra, dec;
222 
223   if (starname == string("[") + _("Chart Center") + string("]")) {
224     SolidAngle s = globals::chart_rules.ChartLocation.toSpherical();
225     //if (globals::chart_rules.CelestialCoords == GALACTIC)
226       //s = s.toGalactic();
227 
228     ra = starstrings::ra_to_strs(s.getPhi(),
229 				 globals::chart_rules.CelestialCoords);
230     dec = starstrings::dec_to_strs(s.getTheta());
231 
232     gtk_entry_set_text (GTK_ENTRY (data[6]), starstrings::ftoa(
233 	globals::chart_rules.ChartLocation.magnitude()
234 	* distance_conversion[globals::chart_rules.ChartUnits[1]], 8).c_str());
235   }
236   else {
237 
238   // Try several different kinds of searches, order of most to least specific
239   searchresults.Search(starname, globals::chart_rules.ChartFileNames,
240 		       globals::chart_rules,
241 		       /* not case-sensitive */ false,
242 		       /* exact match        */ true,
243 		       /* exit on match      */ true);
244 
245   // If the search string was quoted, an exact match was forced, so skip
246   // the remaining redundant search attempts
247   unsigned size = starname.size();
248   if (size > 2 && starname[0] == '"' && starname[size - 1] == '"')
249     /* nothing */;
250   else {
251 
252   if (! searchresults.size())
253     searchresults.Search(starname, globals::chart_rules.ChartFileNames,
254 			 globals::chart_rules,
255 			 /* case-sensitive  */ true,
256 			 /* substring match */ false,
257 			 /* exit on match   */ true);
258   if (! searchresults.size())
259     searchresults.Search(starname, globals::chart_rules.ChartFileNames,
260 			 globals::chart_rules,
261 			 /* not case-sensitive */ false,
262 			 /* substring match    */ false,
263 			 /* exit on match      */ true);
264   } // end "else"
265 
266   // if we don't find anything, leave a message in the star name entry
267   //  box and return
268   if (! searchresults.size()) {
269     my_gtk_error (GTK_WINDOW (definedialog),
270 	starstrings::ssprintf(_("Sorry, star \"%s\" not found."),
271 	  starname.c_str()).c_str());
272     return;
273   }
274 
275   // otherwise, put the name which matched into the star name entry box,
276   //  and put the star coordinates into the coordinate entry boxes.
277 
278   StringList infolist = searchresults[0].GetInfo(globals::chart_rules,
279 						 false /*no dms punctuation*/,
280 						 SUBFIELD_DELIMITER);
281   // where is the star name which matches the search string in the
282   //  list of star characteristics?
283   unsigned int nameplace = starmath::atoi(infolist[0]);
284   unsigned int infolistplace = nameplace ? nameplace + 9 : 1;
285   gtk_entry_set_text (GTK_ENTRY (data[7]), infolist[infolistplace].c_str());
286 
287   ra = StringList(infolist[2], SUBFIELD_DELIMITER);
288   dec = StringList(infolist[3], SUBFIELD_DELIMITER);
289 
290   if (ra[0] == string(_("N/A")))
291     ra = dec = StringList("00,00,00", ',');
292 
293   gtk_entry_set_text (GTK_ENTRY (data[6]), starstrings::ftoa(
294       searchresults[0].GetStarDistance()
295       * distance_conversion[globals::chart_rules.ChartUnits[1]], 8).c_str());
296   } // end "else"
297 
298   for (unsigned int i = 0; i < 3; i++) {
299     gtk_entry_set_text (GTK_ENTRY (data[i]), ra[i].c_str());
300     gtk_entry_set_text (GTK_ENTRY (data[i + 3]), dec[i].c_str());
301   }
302 
303   return;
304 }
305 
306 /* my_gtk_star_coordinates(): Create a dialog that permits entering a
307  * star name and clicking "Search" to enter coordinates, or entering
308  * coordinates by hand.  Dialog is packed into an existing vbox passed
309  * in as an argument.
310  *
311  * entryboxes must be an array of or pointer to at least 8 GtkWidget *'s
312  * and is assumed to have the following layout:
313  *
314  * entryboxes[0] through [5] - controlled by my_gtk_position_dialog()
315  *                             gives the two angular coordinates
316  * entryboxes[6]             - distance of coordinate from Sun
317  * entryboxes[7]             - entry box for star name
318  *
319  * */
my_gtk_star_coordinates(GtkWidget ** entryboxes,GtkWidget * main_vbox)320 void my_gtk_star_coordinates(GtkWidget ** entryboxes, GtkWidget * main_vbox)
321 {
322   GtkWidget * lookup_frame, * lookup_hbox, * search;
323   GtkWidget * coord_frame, * coord_vbox;
324   GtkWidget * distance_hbox, * distance_label;
325   string distance_text;
326 
327   // First element: a frame containing an hbox containing the star search
328   //  entry box
329   lookup_frame = gtk_frame_new (
330       _("Enter a star name and press \"Search\""));
331   gtk_box_pack_start (GTK_BOX (main_vbox), lookup_frame, false, false, 0);
332   gtk_widget_show (lookup_frame);
333 
334   lookup_hbox = gtk_hbox_new (false, 5);
335   gtk_container_add (GTK_CONTAINER (lookup_frame), lookup_hbox);
336   gtk_container_set_border_width (GTK_CONTAINER (lookup_hbox), 5);
337   gtk_widget_show (lookup_hbox);
338 
339   entryboxes[7] = gtk_entry_new();
340   gtk_box_pack_start (GTK_BOX (lookup_hbox), entryboxes[7], true, true, 5);
341   gtk_widget_show (entryboxes[7]);
342 
343   search = gtk_button_new_with_mnemonic (_("_Search"));
344   gtk_box_pack_start (GTK_BOX (lookup_hbox), search, false, false, 5);
345   gtk_widget_set_usize (search, defaults::program_button_width,
346 			defaults::program_button_height);
347   gtk_widget_show (search);
348 
349   // Second element: a frame containing a vbox containing the coordinate
350   //  entry boxes
351   coord_frame = gtk_frame_new (
352       _("Or, enter coordinates manually"));
353   gtk_box_pack_start (GTK_BOX (main_vbox), coord_frame, false, false, 0);
354   gtk_widget_show (coord_frame);
355 
356   coord_vbox = gtk_vbox_new (false, 5);
357   gtk_container_add (GTK_CONTAINER (coord_frame), coord_vbox);
358   gtk_container_set_border_width (GTK_CONTAINER (coord_vbox), 5);
359   gtk_widget_show (coord_vbox);
360 
361   // fills in entryboxes[0] through [5]
362   my_gtk_position_dialog (entryboxes, coord_vbox,
363 			  globals::chart_rules.ChartLocation.toSpherical(),
364 			  globals::chart_rules.CelestialCoords);
365 
366   distance_hbox = gtk_hbox_new (false, 5);
367   gtk_box_pack_start (GTK_BOX (coord_vbox), distance_hbox, false, false, 5);
368   gtk_widget_show (distance_hbox);
369 
370   distance_label = gtk_label_new ((string(
371       _("Distance of position from Sun")) + " ("
372       + distance_name[globals::chart_rules.ChartUnits[1]] + "):").c_str());
373   gtk_box_pack_start (GTK_BOX (distance_hbox), distance_label, false, false, 5);
374   gtk_widget_show (distance_label);
375 
376   distance_text =
377     starstrings::ftoa(globals::chart_rules.ChartLocation.magnitude()
378 	* distance_conversion[globals::chart_rules.ChartUnits[1]], 8);
379   entryboxes[6] = gtk_entry_new_with_max_length (14);
380   gtk_box_pack_start (GTK_BOX(distance_hbox), entryboxes[6], false, false, 5);
381   gtk_entry_set_text (GTK_ENTRY (entryboxes[6]), distance_text.c_str());
382   gtk_widget_set_usize (entryboxes[6], 80, 20);
383   gtk_widget_show (entryboxes[6]);
384 
385   // Connect the buttons to signals
386   g_signal_connect (G_OBJECT (search), "clicked",
387 		      G_CALLBACK (chart_define_search),
388 		      entryboxes);
389   g_signal_connect (G_OBJECT (entryboxes[7]), "activate",
390 		      G_CALLBACK (chart_define_search),
391 		      entryboxes);
392 }
393 
394 /* This function has lots of GTK gui boilerplate because it is the most
395  *  complicated of the dialog boxes.  Sorry about that. */
chart_define()396 void chart_define()
397 {
398   GtkWidget *main_vbox, *query;
399   GtkWidget *chartrad_hbox, *chartrad_label, *chartrad_entry;
400   GtkWidget *OK, *defaults, *cancel;
401   static GtkWidget *chart_define_data[9];
402   string chartrad_text;
403 
404   // The window
405   definedialog = gtk_window_new (GTK_WINDOW_TOPLEVEL);
406   gtk_window_set_resizable (GTK_WINDOW (definedialog), false);
407   gtk_window_set_title (GTK_WINDOW (definedialog), _("StarPlot: Define Chart"));
408   gtk_window_set_modal (GTK_WINDOW (definedialog), true);
409   g_signal_connect (G_OBJECT (definedialog), "destroy",
410 		      G_CALLBACK (gtk_widget_destroy), NULL);
411 
412   // Pack it vertically
413   main_vbox = gtk_vbox_new (false, 5);
414   gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 10);
415   gtk_container_add (GTK_CONTAINER (definedialog), main_vbox);
416   gtk_widget_show (main_vbox);
417 
418   // Query text
419   query = gtk_label_new (
420       _("Where should the chart origin be located?"));
421   gtk_box_pack_start (GTK_BOX (main_vbox), query, false, false, 0);
422   gtk_misc_set_alignment (GTK_MISC (query), 0.0F, 0.0F);
423   gtk_widget_show (query);
424 
425   // Drop in the two elements of the star search frame
426   // (fills in chart_define_data[0] through [7])
427   my_gtk_star_coordinates(chart_define_data, main_vbox);
428 
429   // Third element: an hbox containing the Chart Radius entry box
430   chartrad_hbox = gtk_hbox_new (false, 5);
431   gtk_box_pack_start (GTK_BOX (main_vbox), chartrad_hbox, false, false, 10);
432   gtk_widget_show (chartrad_hbox);
433 
434   chartrad_label = gtk_label_new ((string(
435       _("Radius of chart")) + " ("
436       + distance_name[globals::chart_rules.ChartUnits[1]] + "):").c_str());
437   gtk_box_pack_start (GTK_BOX(chartrad_hbox), chartrad_label, false, false, 5);
438   gtk_misc_set_alignment (GTK_MISC (chartrad_label), 0.0F, 0.0F);
439   gtk_widget_show (chartrad_label);
440 
441   chartrad_text = starstrings::ftoa(globals::chart_rules.ChartRadius
442       * distance_conversion[globals::chart_rules.ChartUnits[1]], 8);
443   chartrad_entry = gtk_entry_new_with_max_length (14);
444   gtk_box_pack_start(GTK_BOX(chartrad_hbox), chartrad_entry, false, false, 10);
445   gtk_entry_set_text (GTK_ENTRY (chartrad_entry), chartrad_text.c_str());
446   gtk_widget_set_usize (chartrad_entry, 80, 20);
447   gtk_widget_show (chartrad_entry);
448   chart_define_data[8] = chartrad_entry;
449 
450   // Last element: the OK/Defaults/Cancel buttons
451   my_gtk_button_bar(&OK, &defaults, &cancel, main_vbox);
452 
453   // Connect the buttons to signals
454   g_signal_connect (G_OBJECT (OK), "clicked",
455 		      G_CALLBACK (chart_define_callback),
456 		      chart_define_data);
457   g_signal_connect (G_OBJECT (defaults), "clicked",
458 		      G_CALLBACK (chart_define_defaults), NULL);
459   my_gtk_buttons_connect_destroy(OK, defaults, cancel, definedialog);
460 
461   // Finally, set the window policy and show it
462   gtk_window_set_focus (GTK_WINDOW (definedialog), OK);
463   gtk_widget_show (definedialog);
464 
465   return;
466 }
467 
468 
469 /* Handler for the Chart->Orientation menu item */
470 
471 /* Callback functions */
472 
473 /* chart_orient_callback(): Set the chart parameters to what was specified
474  * in the dialog box and update the chart. */
chart_orient_callback(GtkWidget * widget,gpointer chart_orient_data)475 void chart_orient_callback(GtkWidget *widget, gpointer chart_orient_data)
476 {
477   GtkWidget **data = (GtkWidget **)chart_orient_data;
478 
479   globals::chart_rules.ChartOrientation =
480     SolidAngle( starstrings::strs_to_ra(
481 			    gtk_entry_get_text (GTK_ENTRY (data[0])),
482 			    gtk_entry_get_text (GTK_ENTRY (data[1])),
483 			    gtk_entry_get_text (GTK_ENTRY (data[2])),
484 			    globals::chart_rules.CelestialCoords),
485 		starstrings::strs_to_dec(
486 			     gtk_entry_get_text (GTK_ENTRY (data[3])),
487 			     gtk_entry_get_text (GTK_ENTRY (data[4])),
488 			     gtk_entry_get_text (GTK_ENTRY (data[5]))
489 			   )
490 		); // <sarcasm>what is this, LISP?</sarcasm>
491 
492   redraw_all(ORIENTATION_CHANGE);
493   return;
494 }
495 
496 /* chart_orient_defaults(): Restore the values handled by this dialog box
497  * to their default settings and update the chart. */
chart_orient_defaults(GtkWidget * widget,gpointer data)498 void chart_orient_defaults(GtkWidget *widget, gpointer data)
499 {
500   RESET_GLOBAL(chart_rules.ChartOrientation);
501   redraw_all(ORIENTATION_CHANGE);
502   return;
503 }
504 
chart_orient()505 void chart_orient()
506 {
507   GtkWidget *orientdialog, *vbox, *entrybox[6], *explanation;
508   GtkWidget *OK, *defaults, *cancel;
509   static GtkWidget *chart_orient_data[6];
510 
511   // The window
512   orientdialog = gtk_window_new (GTK_WINDOW_TOPLEVEL);
513   gtk_window_set_resizable (GTK_WINDOW (orientdialog), false);
514   gtk_window_set_title (GTK_WINDOW (orientdialog), _("StarPlot: Orientation"));
515   gtk_window_set_modal (GTK_WINDOW (orientdialog), true);
516   g_signal_connect (G_OBJECT (orientdialog), "destroy",
517 		      G_CALLBACK (gtk_widget_destroy), NULL);
518 
519   // Pack it vertically
520   vbox = gtk_vbox_new (false, 10);
521   gtk_container_set_border_width (GTK_CONTAINER (vbox), 10);
522   gtk_container_add (GTK_CONTAINER (orientdialog), vbox);
523   gtk_widget_show (vbox);
524 
525   explanation =
526     gtk_label_new (_("Set the orientation of the front of the chart:"));
527   gtk_misc_set_alignment (GTK_MISC (explanation), 0.0F, 0.0F);
528   gtk_box_pack_start (GTK_BOX (vbox), explanation, false, false, 0);
529   gtk_widget_show (explanation);
530 
531   // The entry boxes
532   my_gtk_position_dialog (entrybox, vbox, globals::chart_rules.ChartOrientation,
533 		          globals::chart_rules.CelestialCoords);
534   for (int i = 0; i < 6; i++)
535     chart_orient_data[i] = entrybox[i];
536 
537   // set up the buttons
538   my_gtk_button_bar (&OK, &defaults, &cancel, vbox);
539   g_signal_connect (G_OBJECT (OK), "clicked",
540 		      G_CALLBACK (chart_orient_callback),
541 		      chart_orient_data);
542   g_signal_connect (G_OBJECT (defaults), "clicked",
543 		      G_CALLBACK (chart_orient_defaults), NULL);
544   my_gtk_buttons_connect_destroy (OK, defaults, cancel, orientdialog);
545 
546   gtk_window_set_focus (GTK_WINDOW (orientdialog), OK);
547   gtk_widget_show (orientdialog);
548 
549   return;
550 }
551 
552 
553 /* Handler for the Chart->Star Filter menu item */
554 
555 /* Callback functions */
556 
557 /* chart_filter_callback(): Set the chart parameters to what was specified
558  * in the dialog box and update the chart. */
chart_filter_callback(GtkWidget * widget,gpointer chart_filter_data)559 void chart_filter_callback(GtkWidget *widget, gpointer chart_filter_data)
560 {
561   double origdimmag = globals::chart_rules.ChartDimmestMagnitude;
562   bool origautoset = globals::chart_rules.ChartAutosetDimmestMagnitude;
563   GtkWidget **data = (GtkWidget **)chart_filter_data;
564 
565   for (int i = 0; i < 10; i++)
566     globals::chart_rules.StarClasses[i] = GTK_TOGGLE_BUTTON (data[i])->active;
567 
568   globals::chart_rules.ChartDimmestMagnitude =
569     starmath::atof(gtk_entry_get_text (GTK_ENTRY (data[11])));
570   globals::chart_rules.ChartBrightestMagnitude =
571     starmath::atof(gtk_entry_get_text (GTK_ENTRY (data[10])));
572   globals::chart_rules.ChartAutosetDimmestMagnitude =
573     GTK_TOGGLE_BUTTON (data[12])->active;
574   globals::chart_rules.ChartHideCompanions =
575     GTK_TOGGLE_BUTTON (data[13])->active;
576 
577   /* If the user has just turned on the magnitude autoset feature, we should
578    *  autoset the dim magnitude limit, UNLESS the user has also just manually
579    *  edited the dim magnitude limit. */
580   if (!origautoset && globals::chart_rules.ChartAutosetDimmestMagnitude
581       && fabs(origdimmag - globals::chart_rules.ChartDimmestMagnitude) < 0.1)
582     globals::chart_rules.ChartDimmestMagnitude =
583       automagnitude(globals::chart_rules.ChartRadius);
584 
585   redraw_all(FILTER_CHANGE);
586   return;
587 }
588 
589 /* chart_filter_defaults(): Restore the values handled by this dialog box
590  * to their default settings and update the chart. */
chart_filter_defaults(GtkWidget * widget,gpointer data)591 void chart_filter_defaults(GtkWidget *widget, gpointer data)
592 {
593   for (int i = 0; i < 10; i++)
594     RESET_GLOBAL(chart_rules.StarClasses[i]);
595   RESET_GLOBAL(chart_rules.ChartBrightestMagnitude);
596   RESET_GLOBAL(chart_rules.ChartDimmestMagnitude);
597   RESET_GLOBAL(chart_rules.ChartHideCompanions);
598   RESET_GLOBAL(chart_rules.ChartAutosetDimmestMagnitude);
599 
600   if (globals::chart_rules.ChartAutosetDimmestMagnitude)
601     globals::chart_rules.ChartDimmestMagnitude =
602       automagnitude(globals::chart_rules.ChartRadius);
603 
604   redraw_all(FILTER_CHANGE);
605   return;
606 }
607 
608 /* chart_filter(): This function creates the dialog box for turning stars on
609  *  or off by spectral class and magnitude. */
610 
chart_filter()611 void chart_filter()
612 {
613   GtkWidget *filterdialog, *mainbox, *classframe, *magframe;
614   GtkWidget *classtable, *classboxes[10], *clabel;
615   GtkWidget *magtable, *magentrybox[2], *maglabel[2], *magautoset;
616   GtkWidget *hidecomps;
617   GtkWidget *OK, *defaults, *cancel;
618   static GtkWidget *chart_filter_data[14];
619   string maglimits[2];
620 
621   // The window
622   filterdialog = gtk_window_new (GTK_WINDOW_TOPLEVEL);
623   gtk_window_set_resizable (GTK_WINDOW (filterdialog), false);
624   gtk_window_set_title (GTK_WINDOW (filterdialog), _("StarPlot: Star Filter"));
625   gtk_window_set_modal (GTK_WINDOW (filterdialog), true);
626   g_signal_connect (G_OBJECT (filterdialog), "destroy",
627 		      G_CALLBACK (gtk_widget_destroy), NULL);
628 
629   // Pack it vertically
630   mainbox = gtk_vbox_new (false, 10);
631   gtk_container_set_border_width (GTK_CONTAINER (mainbox), 10);
632   gtk_container_add (GTK_CONTAINER (filterdialog), mainbox);
633   gtk_widget_show (mainbox);
634 
635   // Frames in the window
636   classframe = gtk_frame_new (
637       _("By Spectral Class"));
638   magframe = gtk_frame_new (_("By Magnitude"));
639   gtk_box_pack_start (GTK_BOX (mainbox), classframe, true, false, 0);
640   gtk_box_pack_start (GTK_BOX (mainbox), magframe, true, false, 0);
641   gtk_widget_show (classframe);
642   gtk_widget_show (magframe);
643 
644   // Create the spectral class checkboxes
645   classtable = gtk_table_new (5, 4, false);
646   gtk_container_add (GTK_CONTAINER (classframe), classtable);
647   gtk_widget_show (classtable);
648 
649   clabel = gtk_label_new (_("Indicate which stellar classes to include:"));
650   gtk_widget_show (clabel);
651   gtk_misc_set_alignment (GTK_MISC (clabel), 0.0F, 0.0F);
652   gtk_table_attach (GTK_TABLE (classtable), clabel, 0, 4, 0, 1,
653 		    (GtkAttachOptions)(GTK_FILL | GTK_EXPAND),
654 		    (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), 5, 5);
655 
656   classboxes[0] =
657     gtk_check_button_new_with_mnemonic (
658 	/* TRANSLATORS: "Wolf-Rayet" is a proper name for a type of star. */
659 	_("Class _O / Wolf-Rayet"));
660   classboxes[1] = gtk_check_button_new_with_mnemonic (_("Class _B"));
661   classboxes[2] = gtk_check_button_new_with_mnemonic (_("Class _A"));
662   classboxes[3] = gtk_check_button_new_with_mnemonic (_("Class _F"));
663   classboxes[4] = gtk_check_button_new_with_mnemonic (_("Class _G"));
664   classboxes[5] = gtk_check_button_new_with_mnemonic (_("Class _K"));
665   classboxes[6] = gtk_check_button_new_with_mnemonic (_("Class _M"));
666   classboxes[7] = gtk_check_button_new_with_mnemonic (_("_Dwarf stars"));
667   classboxes[9] =
668     gtk_check_button_new_with_mnemonic (_("Show _Non-Stellar Objects"));
669   classboxes[8] =
670     gtk_check_button_new_with_mnemonic (_("Show _Unclassified Objects"));
671 
672   for (int i = 0; i < 10; i++) {
673     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (classboxes[i]),
674 				  globals::chart_rules.StarClasses[i]);
675     gtk_widget_show (classboxes[i]);
676     chart_filter_data[i] = classboxes[i];
677   }
678 
679   for (int i = 0; i < 4; i++) {
680     gtk_table_attach_defaults (GTK_TABLE (classtable), classboxes[2*i],
681 			       i, i + 1, 1, 2);
682     gtk_table_attach_defaults (GTK_TABLE (classtable), classboxes[2*i + 1],
683 			       i, i + 1, 2, 3);
684   }
685   gtk_table_attach_defaults (GTK_TABLE (classtable), classboxes[9], 0,2,3,4);
686   gtk_table_attach_defaults (GTK_TABLE (classtable), classboxes[8], 0,2,4,5);
687 
688   // Create the magnitude text boxes
689   maglimits[0]=starstrings::ftoa(globals::chart_rules.ChartBrightestMagnitude);
690   maglimits[1]=starstrings::ftoa(globals::chart_rules.ChartDimmestMagnitude);
691   maglabel[0] = gtk_label_new (_("Smallest (brightest) allowed magnitude: "));
692   maglabel[1] = gtk_label_new (_("Largest (dimmest) allowed magnitude: "));
693 
694   magtable = gtk_table_new (3, 2, false);
695   gtk_container_add (GTK_CONTAINER (magframe), magtable);
696   gtk_widget_show (magtable);
697 
698   for (int i = 0; i < 2; i++) {
699     gtk_widget_show (maglabel[i]);
700     gtk_misc_set_alignment (GTK_MISC (maglabel[i]), 0.0F, 0.0F);
701 
702     magentrybox[i] = gtk_entry_new_with_max_length (6);
703     gtk_entry_set_text (GTK_ENTRY (magentrybox[i]), maglimits[i].c_str());
704     gtk_widget_set_usize (magentrybox[i], 50, 20);
705     gtk_widget_show (magentrybox[i]);
706 
707     gtk_table_attach (GTK_TABLE (magtable), maglabel[i], 0, 1, i, i + 1,
708 		      (GtkAttachOptions)GTK_FILL, (GtkAttachOptions)GTK_FILL,
709 		      5, 5);
710     gtk_table_attach (GTK_TABLE (magtable), magentrybox[i], 1, 2, i, i + 1,
711 		      (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 5);
712 
713     chart_filter_data[10 + i] = magentrybox[i];
714   }
715 
716   magautoset = gtk_check_button_new_with_mnemonic
717     (_("Automatically set dim magnitude limit\nbased on _chart radius"));
718   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (magautoset),
719     globals::chart_rules.ChartAutosetDimmestMagnitude);
720   gtk_table_attach (GTK_TABLE (magtable), magautoset, 0, 2, 2, 3,
721 		    (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 5);
722   gtk_widget_show (magautoset);
723   chart_filter_data[12] = magautoset;
724 
725   hidecomps = gtk_check_button_new_with_mnemonic
726     (_("_Hide nearby companion stars"));
727   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (hidecomps),
728     globals::chart_rules.ChartHideCompanions);
729   gtk_box_pack_start (GTK_BOX (mainbox), hidecomps, true, false, 0);
730   gtk_widget_show (hidecomps);
731   chart_filter_data[13] = hidecomps;
732 
733   // set up the buttons
734   my_gtk_button_bar (&OK, &defaults, &cancel, mainbox);
735   g_signal_connect (G_OBJECT (OK), "clicked",
736 		      G_CALLBACK (chart_filter_callback),
737 		      chart_filter_data);
738   g_signal_connect (G_OBJECT (defaults), "clicked",
739 		      G_CALLBACK (chart_filter_defaults), NULL);
740   my_gtk_buttons_connect_destroy (OK, defaults, cancel, filterdialog);
741 
742   gtk_window_set_focus (GTK_WINDOW (filterdialog), OK);
743   gtk_widget_show (filterdialog);
744 
745   return;
746 }
747