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   infodialogs.cc
21   This file contains code for the dialog boxes in the StarPlot Stars menu,
22   and for the informational popup window.
23 */
24 
25 #include <gtk/gtk.h>
26 #include "starplot.h"
27 #include <cstring>
28 using std::string;
29 
30 // ---------------------------------------------------------------------------
31 // This function displays an error message popup.
my_gtk_error(GtkWindow * parent_win,const char * error_msg)32 void my_gtk_error (GtkWindow *parent_win, const char * error_msg)
33 {
34   GtkWidget * dialog = gtk_message_dialog_new (parent_win,
35       GTK_DIALOG_DESTROY_WITH_PARENT,
36       GTK_MESSAGE_ERROR,
37       GTK_BUTTONS_CLOSE,
38       error_msg);
39   gtk_dialog_run (GTK_DIALOG (dialog));
40   gtk_widget_destroy (dialog);
41 }
42 
43 // ---------------------------------------------------------------------------
44 /* This function displays a popup window of information about the star: */
45 
my_gtk_star_popup(Star & s)46 void my_gtk_star_popup(Star &s)
47 {
48   GtkWidget *popup, *OK, *topbox, *expander, *separator,
49 	    *namebox, *altname, *label[5];
50   StringList starinfo = s.GetInfo(globals::chart_rules);
51   string Name, RA, Dec, Distance, Class, Mag;
52 
53   Name = starinfo[1];
54   starstrings::addgreek(Name);
55   if (globals::chart_rules.StarLabels == NUMBER_LABEL)
56     Name = starinfo[0] + ". " + Name;
57   if (globals::chart_rules.CelestialCoords) {
58     RA = string(_("R.A.")) + ": " + starinfo[2];
59     Dec = string(_("Dec.")) + ": " + starinfo[3];
60   }
61   else {
62     RA = string(_("Long.")) + ": " + starinfo[2];
63     Dec = string(_("Lat.")) + ": " + starinfo[3];
64   }
65   Distance = string(_("Distance")) + ": " + starstrings::distance_to_str(
66       s.GetStarDistance(), globals::chart_rules.ChartUnits[1]);
67   Class = string(_("Spectral Class")) + ": " + starinfo[5];
68   Mag = string(_("Magnitude")) + ": " + starstrings::ftoa(s.GetStarMagnitude());
69 
70   popup = gtk_dialog_new ();
71   gtk_window_set_resizable (GTK_WINDOW (popup), false);
72   gtk_window_set_title (GTK_WINDOW (popup), _("StarPlot: Star Info"));
73   gtk_window_set_position (GTK_WINDOW (popup), GTK_WIN_POS_MOUSE);
74 
75   topbox = gtk_vbox_new (false, 0);
76   gtk_container_set_border_width (GTK_CONTAINER (topbox), 10);
77   gtk_widget_show (topbox);
78   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (popup)->vbox), topbox,
79 		      true, true, 0);
80 
81 #if HAVE_GTK_EXPANDER_NEW
82   // name
83   expander = gtk_expander_new ((string("<big><b>") + Name.c_str()
84       + "</b></big>").c_str());
85   gtk_expander_set_use_markup (GTK_EXPANDER (expander), true);
86   gtk_expander_set_expanded (GTK_EXPANDER (expander), false);
87 
88   // alternate names
89   namebox = gtk_vbox_new (true, 0);
90   gtk_widget_show (namebox);
91   gtk_container_add (GTK_CONTAINER (expander), namebox);
92 
93   for (unsigned int i = 10; i < starinfo.size(); i++) {
94     starstrings::addgreek(starinfo[i]);
95     altname = gtk_label_new (starinfo[i].c_str());
96     gtk_label_set_markup (GTK_LABEL (altname),
97 	(string("<b>") + starinfo[i] + string("</b>")).c_str());
98     gtk_misc_set_alignment(GTK_MISC (altname), 0.0F, 0.0F);
99     gtk_widget_show (altname);
100     gtk_box_pack_start (GTK_BOX (namebox), altname, true, true, 0);
101   }
102 #else // no GtkExpander widget, just show best-known name
103   expander = gtk_label_new (Name.c_str());
104 #endif
105   gtk_widget_show (expander);
106   gtk_box_pack_start (GTK_BOX (topbox), expander, false, false, 0);
107 
108   separator = gtk_hseparator_new ();
109   gtk_widget_show (separator);
110   gtk_box_pack_start (GTK_BOX (topbox), separator, false, false, 5);
111 
112   // other info
113   label[0] = gtk_label_new (RA.c_str());
114   label[1] = gtk_label_new (Dec.c_str());
115   label[2] = gtk_label_new (Distance.c_str());
116   label[3] = gtk_label_new (Class.c_str());
117   label[4] = gtk_label_new (Mag.c_str());
118 
119   for (unsigned int i = 0; i < 5; i++) {
120     gtk_box_pack_start (GTK_BOX (topbox), label[i], false, false, 0);
121     gtk_misc_set_alignment(GTK_MISC (label[i]), 0.0F, 0.0F);
122     gtk_widget_show (label[i]);
123   }
124 
125   my_gtk_popup_button (&OK, GTK_DIALOG (popup)->action_area, popup);
126   gtk_window_set_focus (GTK_WINDOW (popup), OK);
127   gtk_widget_show (popup);
128 }
129 
130 // ---------------------------------------------------------------------------
131 /* Begin auxiliary types and functions for my_gtk_list_setup () */
132 
133 enum elisttype { CHARTLIST, NUMBERED_CHARTLIST, SEARCHLIST };
134 enum ecolumntype { COL_INDEX, COL_NAME, COL_ALTNAME, COL_RA, COL_DEC,
135 		   COL_DISTANCE, COL_SPCLASS, COL_MAGNITUDE, NUM_COLS };
136 
137 // Print doubles with at most 4 significant figures.  Modified from here:
138 // http://mail.gnome.org/archives/gtk-list/2003-April/msg00326.html
139 void
print_double_to_text(GtkTreeViewColumn * column,GtkCellRenderer * cell,GtkTreeModel * treemodel,GtkTreeIter * iter,void * data)140 print_double_to_text(GtkTreeViewColumn *column,
141 		     GtkCellRenderer *cell,
142 		     GtkTreeModel *treemodel,
143 		     GtkTreeIter *iter,
144 		     void * data)
145 {
146   GtkCellRendererText *cell_text = (GtkCellRendererText *)cell;
147   double d;
148 
149   /* Free the previous (default) text of the column's renderer. */
150   g_free (cell_text->text);
151   /* Get the double value from the model. */
152   gtk_tree_model_get (treemodel, iter, (long)data, &d, -1);
153   /* Now we can format the value ourselves. */
154   cell_text->text = g_strdup (starstrings::ftoa(d, 4).c_str());
155 }
156 
157 // Sort function for latitude coordinate
158 // (in order to get strings starting with '-' before those starting
159 // with '+')
160 int
signed_coord_sort_func(GtkTreeModel * model,GtkTreeIter * item1,GtkTreeIter * item2,void * data)161 signed_coord_sort_func(GtkTreeModel * model,
162 		       GtkTreeIter * item1, GtkTreeIter * item2,
163 		       void * data)
164 {
165   char * s1, * s2;
166   int result;
167   gtk_tree_model_get (model, item1, COL_DEC, &s1, -1);
168   gtk_tree_model_get (model, item2, COL_DEC, &s2, -1);
169 
170   // N.B. we expect strings that look like "[+ -]NNh NNm NN.NNs"
171   char c1 = s1[0], c2 = s2[0];
172   result = std::strcmp(s1, s2);
173 
174   if (c1 == '-' && c2 == '-')
175     result *= -1;
176   else if (c1 == '-')
177     result = -1;
178   else if (c2 == '-')
179     result = +1;
180 
181   std::free (s1);
182   std::free (s2);
183   return result;
184 }
185 
186 // Sort function for spectral class
187 int
spec_class_sort_func(GtkTreeModel * model,GtkTreeIter * item1,GtkTreeIter * item2,void * data)188 spec_class_sort_func(GtkTreeModel * model,
189 		     GtkTreeIter * item1, GtkTreeIter * item2,
190 		     void * data)
191 {
192   char * s1, * s2;
193   int M1, M2;
194   double m1, m2, MK1, MK2;
195   int result;
196 
197   gtk_tree_model_get (model, item1, COL_SPCLASS, &s1, -1);
198   gtk_tree_model_get (model, item2, COL_SPCLASS, &s2, -1);
199 
200   SpecClass *c1 = new SpecClass(string(s1));
201   SpecClass *c2 = new SpecClass(string(s2));
202   std::free (s1);
203   std::free (s2);
204 
205   c1->initialize();
206   c2->initialize();
207   M1 = SpecClass::class_to_int(c1->classmajor());
208   M2 = SpecClass::class_to_int(c2->classmajor());
209   if (M1 < 0) M1 = 999;
210   if (M2 < 0) M2 = 999;
211   result = M1 - M2;
212   if (result) goto done;
213 
214   m1 = c1->classminor();
215   m2 = c2->classminor();
216   result = static_cast<int>(100 * (m1 - m2));
217   if (result) goto done;
218 
219   MK1 = c1->MKtype();
220   MK2 = c2->MKtype();
221   result = static_cast<int>(100 * (MK1 - MK2));
222 
223  done:
224   delete c1;
225   delete c2;
226   return result;
227 }
228 
229 /* Here is a function which sets up the treeview for starinfo() and
230  *  starsearch().  "box" is the box into which the treeview should
231  *  be packed. */
232 
my_gtk_list_setup(GtkWidget * box,GtkTreeView ** treeviewptr,enum elisttype listtype)233 void my_gtk_list_setup (GtkWidget *box, GtkTreeView **treeviewptr,
234 			enum elisttype listtype)
235 {
236   GtkWidget *scrolled_window;
237   GtkTreeView *treeview;
238   GtkListStore *store;
239 
240   // set up a scroll window to hold the chart
241   scrolled_window = gtk_scrolled_window_new (NULL, NULL);
242   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
243 				  GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
244   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
245 				       GTK_SHADOW_IN);
246   gtk_box_pack_start (GTK_BOX (box), scrolled_window, true, true, 0);
247   gtk_widget_show (scrolled_window);
248 
249   // set up the data model
250   store = gtk_list_store_new (NUM_COLS,	      // number of possible columns
251 			      G_TYPE_UINT,    // star index number
252 			      G_TYPE_STRING,  // star name
253 			      G_TYPE_STRING,  // alternate name
254 			      G_TYPE_STRING,  // right ascension
255 			      G_TYPE_STRING,  // declination
256 			      G_TYPE_DOUBLE,  // distance
257 			      G_TYPE_STRING,  // spectral class
258 			      G_TYPE_DOUBLE); // absolute magnitude
259 
260   // set up the data viewer
261   *treeviewptr = (GtkTreeView *)gtk_tree_view_new_with_model (
262       GTK_TREE_MODEL (store));
263   treeview = *treeviewptr;
264   gtk_tree_view_set_rules_hint (treeview, true);
265   gtk_widget_set_usize (GTK_WIDGET (treeview), 600, 300);
266   gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (treeview));
267 
268   // render the data viewer columns
269   GtkTreeViewColumn *column;
270   GtkCellRenderer *renderer;
271 
272 #define COL_SETUP(Text, Index) do { \
273   renderer = gtk_cell_renderer_text_new (); \
274   column = gtk_tree_view_column_new_with_attributes (Text, renderer, \
275       "text", Index, NULL); \
276   gtk_tree_view_column_set_sort_column_id (column, Index); \
277   gtk_tree_view_append_column (treeview, column); } while (0)
278 
279   if (listtype == NUMBERED_CHARTLIST)
280     COL_SETUP (_("Label"), COL_INDEX);
281   COL_SETUP   (_("Star"),  COL_NAME);
282   if (listtype == SEARCHLIST)
283     COL_SETUP (_("Also known as"), COL_ALTNAME);
284   if (globals::chart_rules.CelestialCoords) {
285     COL_SETUP (_("Right Ascension"), COL_RA);
286     COL_SETUP (_("Declination"), COL_DEC);
287   }
288   else {
289     COL_SETUP (_("Gal. Longitude"), COL_RA);
290     COL_SETUP (_("Gal. Latitude"), COL_DEC);
291   }
292   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (store),
293       COL_DEC, signed_coord_sort_func, NULL, NULL);
294   COL_SETUP   ((string(_("Distance")) + " ("
295 		+ distance_name[globals::chart_rules.ChartUnits[1]]
296 		+ ")").c_str(),
297 	       COL_DISTANCE);
298   gtk_tree_view_column_set_cell_data_func (column, renderer,
299       print_double_to_text, (void *)COL_DISTANCE, 0);
300   COL_SETUP   (_("Class"),     COL_SPCLASS);
301   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (store),
302       COL_SPCLASS, spec_class_sort_func, NULL, NULL);
303   COL_SETUP   (_("Magnitude"), COL_MAGNITUDE);
304   gtk_tree_view_column_set_cell_data_func (column, renderer,
305       print_double_to_text, (void *)COL_MAGNITUDE, 0);
306 #undef COL_SETUP
307 
308   g_object_unref (store);
309   return;
310 }
311 
312 
313 /* Callback to destroy info window dialog */
314 
315 namespace globals {
316   GtkTreeView *program_treeview = NULL;
317   static GtkWidget *program_info_window = NULL;
318 }
319 
info_quit()320 void info_quit()
321 {
322   gtk_widget_destroy(globals::program_info_window);
323   globals::program_info_window = NULL;
324   globals::program_treeview = NULL;
325   return;
326 }
327 
328 /* Function to update the chart information when necessary */
329 
update_info(GtkTreeView * treeview)330 void update_info(GtkTreeView *treeview)
331 {
332   int namecol = (globals::chart_rules.StarLabels == NUMBER_LABEL);
333   GtkTreeViewColumn *column = gtk_tree_view_get_column (treeview, 0);
334   const char *col_label = gtk_tree_view_column_get_title (column);
335 
336   // add or remove the "Label" column as necessary
337   if ((!namecol) && strcmp(_("Label"), col_label) == 0) {
338     gtk_tree_view_remove_column (treeview, column);
339   }
340   else if (namecol && strcmp(_("Label"), col_label)) {
341     GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
342     column = gtk_tree_view_column_new_with_attributes (_("Label"), renderer,
343         "text", COL_INDEX, NULL);
344     gtk_tree_view_column_set_sort_column_id (column, COL_INDEX);
345     gtk_tree_view_insert_column (treeview, column, 0);
346   }
347 
348   // re-label column headers as necessary
349   column = gtk_tree_view_get_column (treeview, namecol + 1);
350   gtk_tree_view_column_set_title (column,
351       globals::chart_rules.CelestialCoords ?
352       _("Right Ascension") : _("Gal. Longitude"));
353   column = gtk_tree_view_get_column (treeview, namecol + 2);
354   gtk_tree_view_column_set_title (column,
355       globals::chart_rules.CelestialCoords ?
356       _("Declination") : _("Gal. Latitude"));
357   column = gtk_tree_view_get_column (treeview, namecol + 3);
358   gtk_tree_view_column_set_title (column, (string(_("Distance")) + " ("
359 	+ distance_name[globals::chart_rules.ChartUnits[1]] + ")").c_str());
360 
361   // update the data
362   GtkListStore *store = (GtkListStore *)gtk_tree_view_get_model (treeview);
363   GtkTreeIter iter;
364   gtk_list_store_clear (store);
365 
366   citerate (StarArray, *globals::chart_stararray, star_ptr) {
367     StringList infolist = (*star_ptr).GetInfo(globals::chart_rules);
368     starstrings::addgreek(infolist[1]);
369     gtk_list_store_append (store, &iter);
370     gtk_list_store_set (store, &iter,
371 			COL_INDEX, starmath::atoi(infolist[0]),
372 			COL_NAME,  infolist[1].c_str(),
373 			COL_RA,    infolist[2].c_str(),
374 			COL_DEC,   infolist[3].c_str(),
375 			COL_DISTANCE, (*star_ptr).GetStarDistance()
376 		    * distance_conversion[globals::chart_rules.ChartUnits[1]],
377 			COL_SPCLASS, infolist[5].c_str(),
378 			COL_MAGNITUDE, (*star_ptr).GetStarMagnitude(),
379 			-1 /* to mark end of args */);
380   }
381 
382   return;
383 }
384 
385 /* This function displays information about the stars shown on the chart. */
386 
starinfo()387 void starinfo()
388 {
389   GtkWidget *vbox, *OK;
390   enum elisttype listtype = (globals::chart_rules.StarLabels == NUMBER_LABEL) ?
391 			    NUMBERED_CHARTLIST : CHARTLIST;
392 
393   // If the info window is already open, do nothing but put it in front
394   if (globals::program_info_window) {
395     gdk_window_raise(globals::program_info_window->window);
396     return;
397   }
398 
399   // make a window for it
400   globals::program_info_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
401   gtk_window_set_title (GTK_WINDOW (globals::program_info_window),
402 			_("StarPlot: Chart Data"));
403   g_signal_connect (G_OBJECT (globals::program_info_window), "destroy",
404 		      G_CALLBACK (info_quit), NULL);
405 
406   // make a vbox
407   vbox = gtk_vbox_new (false, 5);
408   gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
409   gtk_container_add (GTK_CONTAINER (globals::program_info_window), vbox);
410   gtk_widget_show (vbox);
411 
412   // set up the column list and put star information into it
413   my_gtk_list_setup (vbox, &globals::program_treeview, listtype);
414   update_info(globals::program_treeview);
415 
416   // the button to make the window go away
417   my_gtk_popup_button(&OK, vbox, globals::program_info_window);
418   gtk_window_set_focus (GTK_WINDOW (globals::program_info_window), OK);
419   gtk_widget_show_all (globals::program_info_window);
420   return;
421 }
422 
423 
424 /* These functions allow the user to look up star names containing a string.
425  *  (admittedly, doing a `grep' of the star data files would work just as
426  *  well, but it wouldn't be all nice and GUI...) */
427 
428 GtkWidget *searchwindow = NULL;
429 
430 struct sSearchData {
431   GtkWidget   *casesensitive;
432   GtkWidget   *entrybox;
433   GtkTreeView *sview;
434 };
435 
search_callback(GtkWidget * widget,gpointer search_data)436 void search_callback(GtkWidget *widget, gpointer search_data)
437 {
438   sSearchData   data = * (sSearchData *)search_data;
439   GtkTreeView  *sview = data.sview;
440   GtkListStore *store = (GtkListStore *)gtk_tree_view_get_model (sview);
441   GtkTreeIter   iter;
442 
443   string searchstring = gtk_entry_get_text(GTK_ENTRY (data.entrybox));
444   bool casesensitive =
445     (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data.casesensitive))
446      == true);
447   StarArray searchresults;
448   StringList information;
449 
450   if (starstrings::isempty(searchstring)) return;
451 
452   gtk_list_store_clear (store);
453   searchresults.Search(searchstring, globals::chart_rules.ChartFileNames,
454 		       globals::chart_rules, casesensitive);
455 
456   // error checking
457   if (searchresults.size() >= MAX_STARS) {
458     string errormsg = starstrings::ssprintf(
459 	_("Showing only the first %d results."), MAX_STARS);
460     my_gtk_error (GTK_WINDOW (searchwindow), errormsg.c_str());
461   }
462   else if (!searchresults.size()) {
463     my_gtk_error (GTK_WINDOW (searchwindow), starstrings::ssprintf(
464 	_("Sorry, no stars matching\npattern \"%s\" were found."),
465 	searchstring.c_str()).c_str());
466     return;
467   }
468 
469   citerate (StarArray, searchresults, result_ptr) {
470     StringList infolist = (*result_ptr).GetInfo(globals::chart_rules);
471 
472     // where is the star name which matches the search string in the
473     //  list of star characteristics?
474     unsigned int nameplace = starmath::atoi(infolist[0]);
475     unsigned int infolistplace = nameplace ? nameplace + 9 : 1;
476     information.clear();
477     information.push_back(infolist[infolistplace]);
478 
479     // if this is not the best-known star name, put that in the "aka" column;
480     //  else put the second-best-known star name in that column
481     information.push_back(nameplace ? infolist[1] : infolist[10]);
482     starstrings::addgreek(information[0]);
483     starstrings::addgreek(information[1]);
484 
485     iterate_between (StringList, infolist, j, 2, 7)
486       information.push_back(*j);
487 
488     gtk_list_store_append (store, &iter);
489     gtk_list_store_set (store, &iter,
490 			COL_NAME,     information[0].c_str(),
491 			COL_ALTNAME,  information[1].c_str(),
492 			COL_RA,	      information[2].c_str(),
493 			COL_DEC,      information[3].c_str(),
494 			COL_DISTANCE, (*result_ptr).GetStarDistance()
495 		    * distance_conversion[globals::chart_rules.ChartUnits[1]],
496 			COL_SPCLASS,  information[5].c_str(),
497 			COL_MAGNITUDE,(*result_ptr).GetStarMagnitude(),
498 			-1 /* to mark end of args */);
499   }
500   return;
501 }
502 
503 /* Callback to destroy search window dialog */
504 
search_quit()505 void search_quit()
506 {
507   gtk_widget_destroy(searchwindow);
508   searchwindow = NULL;
509   return;
510 }
511 
512 /* create the search window dialog */
513 
starsearch()514 void starsearch()
515 {
516   GtkWidget *vbox, *OK;
517   GtkWidget *hbox, *casesensitive, *entrybox, *searchbutton;
518   GtkTreeView *sview;
519   static sSearchData search_data;
520 
521   // If the search window is already open, do nothing but put it in front
522   if (searchwindow) {
523     gdk_window_raise(searchwindow->window);
524     return;
525   }
526 
527   // make a window for it
528   searchwindow = gtk_window_new (GTK_WINDOW_TOPLEVEL);
529   gtk_window_set_title (GTK_WINDOW (searchwindow), _("StarPlot: Search"));
530   g_signal_connect (G_OBJECT (searchwindow), "destroy",
531 		      G_CALLBACK (search_quit), NULL);
532 
533   // make a vbox
534   vbox = gtk_vbox_new (false, 5);
535   gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
536   gtk_container_add (GTK_CONTAINER (searchwindow), vbox);
537   gtk_widget_show (vbox);
538 
539   // set up the search input field in an hbox
540   casesensitive = gtk_check_button_new_with_label (_("Case sensitive"));
541   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (casesensitive), false);
542   gtk_widget_show (casesensitive);
543 
544   entrybox = gtk_entry_new();
545   gtk_widget_show (entrybox);
546 
547   searchbutton = gtk_button_new_with_mnemonic (_("_Search"));
548   gtk_widget_set_usize (searchbutton,
549 			defaults::program_button_width,
550 			defaults::program_button_height);
551   gtk_widget_show (searchbutton);
552 
553   hbox = gtk_hbox_new (false, 5);
554   gtk_box_pack_start (GTK_BOX (hbox), casesensitive, false, false, 0);
555   gtk_box_pack_start (GTK_BOX (hbox), entrybox, true, true, 0);
556   gtk_box_pack_start (GTK_BOX (hbox), searchbutton, false, false, 0);
557   gtk_box_pack_start (GTK_BOX (vbox), hbox, false, false, 0);
558   gtk_widget_show (hbox);
559 
560   // set up the column list
561   my_gtk_list_setup (vbox, &sview, SEARCHLIST);
562 
563   // set up the search button
564   search_data.casesensitive = casesensitive;
565   search_data.entrybox = entrybox;
566   search_data.sview = sview;
567   g_signal_connect (G_OBJECT (searchbutton), "clicked",
568 		      G_CALLBACK (search_callback),
569 		      &search_data);
570   g_signal_connect (G_OBJECT (entrybox), "activate",
571 		      G_CALLBACK (search_callback),
572 		      &search_data);
573 
574   // the button to make the window go away
575   my_gtk_popup_button (&OK, vbox, searchwindow);
576   gtk_window_set_focus (GTK_WINDOW (searchwindow), OK);
577   gtk_widget_show_all (searchwindow);
578   return;
579 }
580 
581 // ---------------------------------------------------------------------------
582 /* This function displays a dialog allowing to calculate the distance
583  * between two stars (or arbitrary coordinates): */
584 
585 /* Callback function that actually calculates the distance: */
calculate_star_distance(GtkWidget * widget,gpointer star_data)586 static void calculate_star_distance(GtkWidget *widget, gpointer star_data)
587 {
588   GtkWidget ** coords = (GtkWidget **)star_data;
589 
590   int mult[2] = { 1, -1 };
591   Vector3 result;
592 
593   for (unsigned i = 0; i < 2; i++) {
594     Vector3 v = SolidAngle( starstrings::strs_to_ra(
595 			      gtk_entry_get_text (GTK_ENTRY (coords[8*i + 0])),
596 			      gtk_entry_get_text (GTK_ENTRY (coords[8*i + 1])),
597 			      gtk_entry_get_text (GTK_ENTRY (coords[8*i + 2])),
598 			      globals::chart_rules.CelestialCoords),
599 			    starstrings::strs_to_dec(
600 			      gtk_entry_get_text (GTK_ENTRY (coords[8*i + 3])),
601 			      gtk_entry_get_text (GTK_ENTRY (coords[8*i + 4])),
602 			      gtk_entry_get_text (GTK_ENTRY (coords[8*i + 5]))
603 			    )
604 			  ).toCartesian(starmath::atof(
605 			      gtk_entry_get_text(GTK_ENTRY (coords[8*i + 6])))
606 		    / distance_conversion[globals::chart_rules.ChartUnits[1]]);
607     result += mult[i] * v;
608   }
609 
610   string dist = starstrings::distance_to_str(result.magnitude(),
611 					     globals::chart_rules.ChartUnits);
612   gtk_entry_set_text (GTK_ENTRY (coords[2*8]), dist.c_str());
613 }
614 
stardistance(void)615 void stardistance(void)
616 {
617   GtkWidget *window, *main_hbox, *main_vbox, *title;
618   GtkWidget *star_frame[2], *star_vbox[2];
619   GtkWidget *OK, *calculate, *results_hbox, *results_label;
620   static GtkWidget * star_coords[2*8 + 1];
621 
622   // The window
623   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
624   gtk_window_set_resizable (GTK_WINDOW (window), false);
625   gtk_window_set_title (GTK_WINDOW (window), _("StarPlot: Calculate Distance"));
626   gtk_window_set_modal (GTK_WINDOW (window), true);
627   g_signal_connect (G_OBJECT (window), "destroy",
628                       G_CALLBACK (gtk_widget_destroy), NULL);
629 
630   // Pack it vertically
631   main_vbox = gtk_vbox_new (false, 5);
632   gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 10);
633   gtk_container_add (GTK_CONTAINER (window), main_vbox);
634   gtk_widget_show (main_vbox);
635 
636   // Add explanatory title
637   title = gtk_label_new (_(
638 "Define the two positions between which to calculate the distance.\n\
639 (The default for the first position is the center of the current chart.)"));
640   gtk_box_pack_start (GTK_BOX (main_vbox), title, false, false, 0);
641   gtk_misc_set_alignment (GTK_MISC (title), 0.5F, 0.0F);
642   gtk_widget_show (title);
643 
644   // Set up star frames horizontally
645   main_hbox = gtk_hbox_new (true, 20);
646   gtk_container_set_border_width (GTK_CONTAINER (main_hbox), 10);
647   gtk_box_pack_start (GTK_BOX (main_vbox), main_hbox, false, false, 0);
648   gtk_widget_show (main_hbox);
649 
650   // Set up star frames
651   for (unsigned i = 0; i < 2; i++) {
652     string s = starstrings::ssprintf(_("Coordinates of position %d:"), i+1);
653     star_frame[i] = gtk_frame_new (s.c_str());
654     gtk_box_pack_start (GTK_BOX (main_hbox), star_frame[i], false, false, 0);
655     gtk_frame_set_label_align (GTK_FRAME (star_frame[i]), 0.5F, 0.0F);
656     gtk_frame_set_shadow_type (GTK_FRAME (star_frame[i]), GTK_SHADOW_NONE);
657     gtk_widget_show (star_frame[i]);
658 
659     star_vbox[i] = gtk_vbox_new (false, 5);
660     gtk_container_add (GTK_CONTAINER (star_frame[i]), star_vbox[i]);
661     gtk_container_set_border_width (GTK_CONTAINER (star_vbox[i]), 5);
662     gtk_widget_show (star_vbox[i]);
663 
664     my_gtk_star_coordinates(&star_coords[8*i], star_vbox[i]);
665   }
666 
667   // Set the default for the second position to be the origin.
668   for (unsigned i = 0; i < 6; i++)
669     gtk_entry_set_text (GTK_ENTRY (star_coords[8 + i]), "00");
670   gtk_entry_set_text (GTK_ENTRY (star_coords[8+6]), "0");
671 
672   gtk_entry_set_text (GTK_ENTRY (star_coords[0+7]),
673       (string("[") + _("Chart Center") + string("]")).c_str());
674   gtk_entry_set_text (GTK_ENTRY (star_coords[8+7]), "Sun");
675 
676   // Add calculate button and results field
677   results_hbox = gtk_hbox_new (false, 20);
678   gtk_container_set_border_width (GTK_CONTAINER (results_hbox), 10);
679   gtk_box_pack_start (GTK_BOX (main_vbox), results_hbox, false, false, 0);
680   gtk_widget_show (results_hbox);
681 
682   GtkWidget * spacer = gtk_label_new ("");
683   gtk_box_pack_start (GTK_BOX (results_hbox), spacer, true, false, 0);
684   gtk_widget_show (spacer);
685 
686   calculate = gtk_button_new_with_mnemonic (_("_Calculate distance"));
687   gtk_box_pack_start (GTK_BOX (results_hbox), calculate, false, false, 10);
688   gtk_widget_show (calculate);
689 
690   results_label = gtk_label_new (_("Distance between positions is:"));
691   gtk_box_pack_start (GTK_BOX (results_hbox), results_label, false, false, 0);
692   gtk_misc_set_alignment (GTK_MISC (results_label), 1.0F, 0.5F);
693   gtk_widget_show (results_label);
694 
695   star_coords[2*8] = gtk_entry_new();
696   gtk_box_pack_start (GTK_BOX (results_hbox), star_coords[2*8], false,false, 0);
697   gtk_entry_set_editable (GTK_ENTRY (star_coords[2*8]), false);
698   gtk_widget_show (star_coords[2*8]);
699 
700   spacer = gtk_label_new ("");
701   gtk_box_pack_start (GTK_BOX (results_hbox), spacer, true, false, 0);
702   gtk_widget_show (spacer);
703 
704   g_signal_connect (G_OBJECT (calculate), "clicked",
705                       G_CALLBACK (calculate_star_distance), star_coords);
706 
707   my_gtk_popup_button(&OK, main_vbox, window);
708 
709   // Finally, set the window policy and show it
710   gtk_window_set_focus (GTK_WINDOW (window), OK);
711   gtk_widget_show (window);
712 
713   return;
714 }
715