1 /*
2  *  Celestia GTK+ Front-End
3  *  Copyright (C) 2005 Pat Suwalski <pat@suwalski.net>
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  $Id: dialog-star.cpp,v 1.3 2006-09-05 04:57:51 suwalski Exp $
11  */
12 
13 #include <gtk/gtk.h>
14 
15 #include <celengine/body.h>
16 #include <celengine/selection.h>
17 #include <celengine/simulation.h>
18 #include <celengine/star.h>
19 #include <celengine/starbrowser.h>
20 #include <celengine/stardb.h>
21 #include <celengine/univcoord.h>
22 #include <celmath/vecmath.h>
23 #include <celutil/utf8.h>
24 
25 #include "dialog-star.h"
26 #include "actions.h"
27 #include "common.h"
28 
29 
30 /* Declarations: Callbacks */
31 static void listStarSelect(GtkTreeSelection* sel, AppData* app);
32 static void radioClicked(GtkButton*, gpointer choice);
33 static void refreshBrowser(GtkWidget*, sbData* sb);
34 static void listStarEntryChange(GtkEntry *entry, GdkEventFocus *event, sbData* sb);
35 static void listStarSliderChange(GtkRange *range, sbData* sb);
36 static void starDestroy(GtkWidget* w, gint, sbData* sb);
37 
38 /* Declarations: Helpers */
39 static void addStars(sbData* sb);
40 
41 
42 /* ENTRY: Navigation -> Star Browser... */
dialogStarBrowser(AppData * app)43 void dialogStarBrowser(AppData* app)
44 {
45 	sbData* sb = g_new0(sbData, 1);
46 	sb->app = app;
47 	sb->numListStars = 100;
48 
49 	GtkWidget *browser = gtk_dialog_new_with_buttons("Star System Browser",
50 	                                                 GTK_WINDOW(app->mainWindow),
51 	                                                 GTK_DIALOG_DESTROY_WITH_PARENT,
52 	                                                 GTK_STOCK_OK, GTK_RESPONSE_OK,
53 	                                                 NULL);
54 	app->simulation->setSelection(Selection((Star *) NULL));
55 
56 	/* Star System Browser */
57 	GtkWidget *mainbox = gtk_vbox_new(FALSE, CELSPACING);
58 	gtk_container_set_border_width(GTK_CONTAINER(mainbox), CELSPACING);
59 	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(browser)->vbox), mainbox, TRUE, TRUE, 0);
60 
61 	GtkWidget *scrolled_win = gtk_scrolled_window_new (NULL, NULL);
62 
63 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrolled_win),
64 	                               GTK_POLICY_AUTOMATIC,
65 	                               GTK_POLICY_ALWAYS);
66 	gtk_box_pack_start(GTK_BOX(mainbox), scrolled_win, TRUE, TRUE, 0);
67 
68 	/* Create listbox list */
69 	sb->starListStore = gtk_list_store_new(6,
70 	                                       G_TYPE_STRING,
71 	                                       G_TYPE_STRING,
72 	                                       G_TYPE_STRING,
73 	                                       G_TYPE_STRING,
74 	                                       G_TYPE_STRING,
75 	                                       G_TYPE_POINTER);
76 	GtkWidget *starList = gtk_tree_view_new_with_model(GTK_TREE_MODEL(sb->starListStore));
77 
78 	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(starList), TRUE);
79 	gtk_container_add(GTK_CONTAINER(scrolled_win), starList);
80 
81 	GtkCellRenderer *renderer;
82 	GtkTreeViewColumn *column;
83 
84 	/* Add the columns */
85 	for (int i=0; i<5; i++) {
86 		renderer = gtk_cell_renderer_text_new();
87 		column = gtk_tree_view_column_new_with_attributes (sbTitles[i], renderer, "text", i, NULL);
88 		if (i > 0 && i < 4) {
89 			/* Right align */
90 			gtk_tree_view_column_set_alignment(column, 1.0);
91 			g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
92 		}
93 		gtk_tree_view_append_column(GTK_TREE_VIEW(starList), column);
94 	}
95 
96 	/* Initialize the star browser */
97 	sb->browser.setSimulation(sb->app->simulation);
98 
99 	/* Set up callback for when a star is selected */
100 	GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(starList));
101 	g_signal_connect(selection, "changed", G_CALLBACK(listStarSelect), app);
102 
103 	/* From now on, it's the bottom-of-the-window controls */
104 	GtkWidget *frame = gtk_frame_new("Star Search Criteria");
105 	gtk_box_pack_start(GTK_BOX(mainbox), frame, FALSE, FALSE, 0);
106 
107 	GtkWidget *hbox = gtk_hbox_new(FALSE, CELSPACING);
108 	gtk_container_set_border_width(GTK_CONTAINER(hbox), CELSPACING);
109 	gtk_container_add(GTK_CONTAINER(frame), hbox);
110 
111 	/* List viewing preference settings */
112 	GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
113 	GtkWidget *hbox2 = gtk_hbox_new(FALSE, CELSPACING);
114 	GtkWidget *label = gtk_label_new("Maximum Stars Displayed in List");
115 	gtk_box_pack_start(GTK_BOX(hbox2), label, TRUE, FALSE, 0);
116 	sb->entry = gtk_entry_new_with_max_length(3);
117 	gtk_entry_set_width_chars(GTK_ENTRY(sb->entry), 5);
118 	gtk_box_pack_start(GTK_BOX(hbox2), sb->entry, TRUE, FALSE, 0);
119 	gtk_box_pack_start(GTK_BOX(vbox), hbox2, TRUE, FALSE, 0);
120 	sb->scale = gtk_hscale_new_with_range(MINLISTSTARS, MAXLISTSTARS, 1);
121 	gtk_scale_set_draw_value(GTK_SCALE(sb->scale), FALSE);
122 	gtk_range_set_update_policy(GTK_RANGE(sb->scale), GTK_UPDATE_DISCONTINUOUS);
123 	g_signal_connect(sb->scale, "value-changed", G_CALLBACK(listStarSliderChange), sb);
124 	g_signal_connect(sb->entry, "focus-out-event", G_CALLBACK(listStarEntryChange), sb);
125 	gtk_box_pack_start(GTK_BOX(vbox), sb->scale, TRUE, FALSE, 0);
126 	gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, FALSE, 0);
127 
128 	/* Set initial Star Value */
129 	gtk_range_set_value(GTK_RANGE(sb->scale), sb->numListStars);
130 	if (sb->numListStars == MINLISTSTARS)
131 	{
132 		/* Force update manually (scale won't trigger event) */
133 		listStarEntryChange(GTK_ENTRY(sb->entry), NULL, sb);
134 		refreshBrowser(NULL, sb);
135 	}
136 
137 	/* Radio Buttons */
138 	vbox = gtk_vbox_new(TRUE, 0);
139 	makeRadioItems(sbRadioLabels, vbox, GTK_SIGNAL_FUNC(radioClicked), NULL, sb);
140 	gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
141 
142 	/* Common Buttons */
143 	hbox = gtk_hbox_new(TRUE, CELSPACING);
144 	if (buttonMake(hbox, "Center", (GtkSignalFunc)actionCenterSelection, app))
145 		return;
146 	if (buttonMake(hbox, "Go To", (GtkSignalFunc)actionGotoSelection, app))
147 		return;
148 	if (buttonMake(hbox, "Refresh", (GtkSignalFunc)refreshBrowser, sb))
149 		return;
150 	gtk_box_pack_start(GTK_BOX(mainbox), hbox, FALSE, FALSE, 0);
151 
152 	g_signal_connect(browser, "response", G_CALLBACK(starDestroy), browser);
153 
154 	gtk_widget_set_usize(browser, 500, 400); /* Absolute Size, urghhh */
155 	gtk_widget_show_all(browser);
156 }
157 
158 
159 /* CALLBACK: When Star is selected in Star Browser */
listStarSelect(GtkTreeSelection * sel,AppData * app)160 static void listStarSelect(GtkTreeSelection* sel, AppData* app)
161 {
162 	GValue value = { 0, 0 }; /* Initialize GValue to 0 */
163 	GtkTreeIter iter;
164 	GtkTreeModel* model;
165 
166 	/* IF prevents selection while list is being updated */
167 	if (!gtk_tree_selection_get_selected(sel, &model, &iter))
168 		return;
169 
170 	gtk_tree_model_get_value(model, &iter, 5, &value);
171 	Star *selStar = (Star *)g_value_get_pointer(&value);
172 	g_value_unset(&value);
173 
174 	if (selStar)
175 		app->simulation->setSelection(Selection(selStar));
176 }
177 
178 
179 /* CALLBACK: Refresh button is pressed. */
refreshBrowser(GtkWidget *,sbData * sb)180 static void refreshBrowser(GtkWidget*, sbData* sb)
181 {
182 	addStars(sb);
183 }
184 
185 
186 /* CALLBACK: One of the RadioButtons is pressed */
radioClicked(GtkButton * r,gpointer choice)187 static void radioClicked(GtkButton* r, gpointer choice)
188 {
189 	sbData* sb = (sbData*)g_object_get_data(G_OBJECT(r), "data");
190 	gint selection = GPOINTER_TO_INT(choice);
191 
192 	sb->browser.setPredicate(selection);
193 	refreshBrowser(NULL, sb);
194 }
195 
196 
197 /* CALLBACK: Maximum stars EntryBox changed. */
listStarEntryChange(GtkEntry * entry,GdkEventFocus * event,sbData * sb)198 static void listStarEntryChange(GtkEntry *entry, GdkEventFocus *event, sbData* sb)
199 {
200 	/* If not called by the slider, but rather by user.
201 	   Prevents infinite recursion. */
202 	if (event != NULL)
203 	{
204 		sb->numListStars = atoi(gtk_entry_get_text(entry));
205 
206 		/* Sanity Check */
207 		if (sb->numListStars < MINLISTSTARS)
208 		{
209 			sb->numListStars = MINLISTSTARS;
210 			/* Call self to set text (NULL event = no recursion) */
211 			listStarEntryChange(entry, NULL, sb);
212 		}
213 		if (sb->numListStars > MAXLISTSTARS)
214 		{
215 			sb->numListStars = MAXLISTSTARS;
216 			/* Call self to set text */
217 			listStarEntryChange(entry, NULL, sb);
218 		}
219 
220 		gtk_range_set_value(GTK_RANGE(sb->scale), (gdouble)sb->numListStars);
221 	}
222 
223 	/* Update value of this box */
224 	char stars[4];
225 	sprintf(stars, "%d", sb->numListStars);
226 	gtk_entry_set_text(entry, stars);
227 }
228 
229 
230 /* CALLBACK: Maximum stars RangeSlider changed. */
listStarSliderChange(GtkRange * range,sbData * sb)231 static void listStarSliderChange(GtkRange *range, sbData* sb)
232 {
233 	/* Update the value of the text entry box */
234 	sb->numListStars = (int)gtk_range_get_value(GTK_RANGE(range));
235 	listStarEntryChange(GTK_ENTRY(sb->entry), NULL, sb);
236 
237 	/* Refresh the browser listbox */
238 	refreshBrowser(NULL, sb);
239 }
240 
241 
242 /* CALLBACK: Destroy Window */
starDestroy(GtkWidget * w,gint,sbData *)243 static void starDestroy(GtkWidget* w, gint, sbData*)
244 {
245 	gtk_widget_destroy(GTK_WIDGET(w));
246 
247 	/* Cannot do this, as the program crashes because of the StarBrowser:
248 	 * g_free(sb); */
249 }
250 
251 
252 /* HELPER: Clear and Add stars to the starListStore */
addStars(sbData * sb)253 static void addStars(sbData* sb)
254 {
255 	const char *values[5];
256 	GtkTreeIter iter;
257 
258 	StarDatabase* stardb;
259 	vector<const Star*> *stars;
260 	unsigned int currentLength;
261 	UniversalCoord ucPos;
262 
263 	/* Load the catalogs and set data */
264 	stardb = sb->app->simulation->getUniverse()->getStarCatalog();
265 	sb->browser.refresh();
266 	stars = sb->browser.listStars(sb->numListStars);
267 	currentLength = (*stars).size();
268 	sb->app->simulation->setSelection(Selection((Star *)(*stars)[0]));
269 	ucPos = sb->app->simulation->getObserver().getPosition();
270 
271 	gtk_list_store_clear(sb->starListStore);
272 
273 	for (unsigned int i = 0; i < currentLength; i++)
274 	{
275 		char buf[20];
276 		const Star *star=(*stars)[i];
277 		values[0] = g_strdup(ReplaceGreekLetterAbbr((stardb->getStarName(*star))).c_str());
278 
279 		Point3f pStar = star->getPosition();
280 		Vec3d v(pStar.x * 1e6 - (float)ucPos.x,
281 		        pStar.y * 1e6 - (float)ucPos.y,
282 		        pStar.z * 1e6 - (float)ucPos.z);
283 		float d = v.length() * 1e-6;
284 
285 		sprintf(buf, " %.3f ", d);
286 		values[1] = g_strdup(buf);
287 
288 		Vec3f r = star->getPosition() - ucPos;
289 		sprintf(buf, " %.2f ", astro::absToAppMag(star->getAbsoluteMagnitude(), d));
290 		values[2] = g_strdup(buf);
291 
292 		sprintf(buf, " %.2f ", star->getAbsoluteMagnitude());
293 		values[3] = g_strdup(buf);
294 
295 		gtk_list_store_append(sb->starListStore, &iter);
296 		gtk_list_store_set(sb->starListStore, &iter,
297 		                   0, values[0],
298 		                   1, values[1],
299 		                   2, values[2],
300 		                   3, values[3],
301 		                   4, star->getSpectralType(),
302 		                   5, (gpointer)star, -1);
303 	}
304 
305 	delete stars;
306 }
307