ui: callbacks - added callbacks
[nova.git] / src / sky / sky.c
blobaf79c429d7f03ff90b5e0e040b19d45dd05caf2e
1 /*
2 * Copyright (C) 2008 Liam Girdwood
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
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.
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,
17 * Boston, MA 02111-1307, USA.
20 #include <math.h>
21 #include <time.h>
22 #include <gtk/gtk.h>
23 #include <gdk/gdkkeysyms.h>
24 #include <libnova/utility.h>
25 #include <libnova/transform.h>
26 #include <libnova/julian_day.h>
27 #include <libnova/ln_types.h>
28 #include <libastrodb/astrodb.h>
29 #include "sky.h"
30 #include "render.h"
31 #include "star.h"
32 #include "tile.h"
33 #include "db.h"
34 #include "legend.h"
35 #include "constellation.h"
36 #include "grid.h"
37 #include "solar_system.h"
38 #include "glade.h"
40 #define NOVA_SKY_GET_PRIVATE(obj) \
41 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), NOVA_TYPE_SKY, Sky))
43 G_DEFINE_TYPE (GtkSky, virtual_sky, GTK_TYPE_DRAWING_AREA);
45 #define SKY_MOVE_SIZE 15
46 #define SKY_UPDATE_MAG_DIFF 1
47 #define SKY_MAG_SCALER 8
48 #define SKY_ZOOM_OUT 1.1
49 #define SKY_ZOOM_IN 0.9
50 #define SKY_MAX_RA 360.0
52 static gboolean virtual_sky_expose (GtkWidget *sky_widget, GdkEventExpose *event);
53 static gboolean virtual_sky_update (gpointer data);
54 static GdkCursor *sky_cursor_arrow;
55 static GdkCursor *sky_cursor_ptr;
57 static inline void sky_get_posn(Sky *sky, struct render_coord *coord)
59 sky->projection.trans->proj_to_sky_equ(&sky->projection, coord);
62 static inline struct astrodb_object*
63 virtual_sky_get_object (Sky *sky, struct render_coord* rc)
65 return tile_get_object_at(sky->tile, rc);
68 static gboolean sky_complete_scroll_render(gpointer data)
70 GtkWidget *widget = data;
71 Sky *sky = NOVA_SKY_GET_PRIVATE(widget);
73 /* redraw still pending ? */
74 if (sky->redraw_pending)
75 return TRUE;
77 sky->robject.type = RT_FULL;
78 gtk_widget_queue_draw(widget);
79 return FALSE;
82 static void sky_move_abs_posn(Sky *sky, gfloat ra, gfloat dec)
84 sky->projection.centre_ra = ra;
85 sky->projection.centre_dec = dec;
86 projection_check_bounds(&sky->projection);
89 static void sky_zoom(Sky *sky, gfloat zoom)
91 sky->projection.fov *= zoom;
92 projection_check_bounds(&sky->projection);
93 legend_update_fov(sky->legend_widget, sky->projection.fov,
94 sky->projection.clip_mag_faint);
97 static gboolean sky_key_press_event(GtkWidget *widget, GdkEventKey *event,
98 gpointer user_data)
100 Sky *sky = NOVA_SKY_GET_PRIVATE(widget);
102 switch (event->keyval) {
103 case GDK_Right:
104 projection_move_rel_pixels(&sky->projection, -SKY_MOVE_SIZE, 0);
105 break;
106 case GDK_Left:
107 projection_move_rel_pixels(&sky->projection, SKY_MOVE_SIZE, 0);
108 break;
109 case GDK_Down:
110 projection_move_rel_pixels(&sky->projection, 0, -SKY_MOVE_SIZE);
111 break;
112 case GDK_Up:
113 projection_move_rel_pixels(&sky->projection, 0, SKY_MOVE_SIZE);
114 break;
115 case GDK_minus:
116 sky_zoom(sky, SKY_ZOOM_OUT);
117 break;
118 case GDK_equal:
119 sky_zoom(sky, SKY_ZOOM_IN);
120 break;
121 default:
122 return TRUE;
125 sky->robject.type = RT_FAST;
126 gtk_widget_queue_draw(widget);
127 sky->is_modified = 1;
128 return TRUE;
131 static gboolean sky_key_release_event(GtkWidget *widget, GdkEventKey *event,
132 gpointer user_data)
134 Sky *sky = NOVA_SKY_GET_PRIVATE(widget);
136 sky->robject.type = RT_FULL;
137 gtk_widget_queue_draw(widget);
138 return TRUE;
141 static gboolean sky_enter_notify_event(GtkWidget *widget,
142 GdkEventCrossing *event,
143 gpointer user_data)
145 gtk_widget_grab_focus(widget);
146 gdk_window_set_cursor(event->window, sky_cursor_ptr);
147 return TRUE;
150 static gboolean sky_leave_notify_event(GtkWidget *widget,
151 GdkEventCrossing *event,
152 gpointer user_data)
154 gdk_window_set_cursor(event->window, sky_cursor_arrow);
155 return TRUE;
158 static gboolean sky_button_press_event(GtkWidget *widget,
159 GdkEventButton *event,
160 gpointer user_data)
162 Sky *sky = NOVA_SKY_GET_PRIVATE(widget);
163 GdkModifierType state;
165 switch (event->button) {
166 case 1:
167 gdk_window_get_pointer (event->window, &sky->pointer_x,
168 &sky->pointer_y, &state);
169 sky->robject.type = RT_FAST;
170 sky->pointer_drag = 1;
171 break;
174 return TRUE;
177 static gboolean sky_button_release_event(GtkWidget *widget,
178 GdkEventButton *event,
179 gpointer user_data)
181 Sky *sky = NOVA_SKY_GET_PRIVATE(widget);
183 switch (event->button) {
184 case 1:
185 sky->robject.type = RT_FULL;
186 sky->pointer_drag = 0;
187 gtk_widget_queue_draw(widget);
188 break;
191 return TRUE;
194 static gboolean sky_scroll_event(GtkWidget *widget,
195 GdkEventScroll *event,
196 gpointer user_data)
198 Sky *sky = NOVA_SKY_GET_PRIVATE(widget);
200 switch (event->direction) {
201 case GDK_SCROLL_UP:
202 sky_zoom(sky, SKY_ZOOM_IN);
203 break;
204 case GDK_SCROLL_DOWN:
205 sky_zoom(sky, SKY_ZOOM_OUT);
206 break;
207 case GDK_SCROLL_LEFT:
208 case GDK_SCROLL_RIGHT:
209 break;
212 sky->robject.type = RT_FAST;
213 sky->redraw_pending = 1;
214 sky->is_modified = 1;
215 gtk_widget_queue_draw(widget);
217 /* add timeout to draw detail when scroll completes */
218 g_timeout_add(20, sky_complete_scroll_render, widget);
219 return TRUE;
222 static gboolean sky_motion_notify_event(GtkWidget *widget,
223 GdkEventMotion *event,
224 gpointer user_data)
226 GdkModifierType state;
227 struct render_coord rc;
228 struct ln_equ_posn pos;
229 struct astrodb_object *object;
230 gint x,y;
231 Sky *sky = NOVA_SKY_GET_PRIVATE(widget);
233 sky->robject.coord[0].posn = &pos;
234 gdk_window_get_pointer(event->window, &x, &y, &state);
236 /* drag the sky to pointer position */
237 if (sky->pointer_drag) {
238 projection_move_rel_pixels(&sky->projection,
239 x - sky->pointer_x,
240 y - sky->pointer_y);
241 sky->pointer_x = x;
242 sky->pointer_y = y;
243 gtk_widget_queue_draw(widget);
244 sky->is_modified = 1;
247 /* update the legend position */
248 if (event->is_hint) {
249 sky->robject.coord[0].x = rc.x = x;
250 sky->robject.coord[0].y = rc.y = y;
251 sky_get_posn(sky, &sky->robject.coord[0]);
252 legend_update_pointer(sky->legend_widget,
253 sky->robject.coord[0].posn->ra,
254 sky->robject.coord[0].posn->dec);
256 /* update or clear legend object */
257 object = tile_get_object_at(sky->tile, &rc);
258 legend_set_object(sky->legend_widget, object);
261 return TRUE;
264 void on_sky_markers_activate(GtkMenuItem *menuitem, gpointer user_data)
266 GtkWidget *dlg, *sky_widget = (GtkWidget *)user_data;
267 Sky *sky = NOVA_SKY_GET_PRIVATE(sky_widget);
268 gint result = 0;
270 dlg = ui_get_dialog("SkyMarkersDialog");
271 if (dlg == NULL)
272 return;
274 ui_markers_dlg_init(dlg, &sky->marker_settings);
276 do {
277 result = gtk_dialog_run(GTK_DIALOG(dlg));
278 if (result == GTK_RESPONSE_OK || result == GTK_RESPONSE_APPLY)
279 gtk_widget_queue_draw(sky_widget);
280 } while (result == GTK_RESPONSE_APPLY || result == GTK_RESPONSE_HELP);
282 //app_bar_sync(GTK_WIDGET(menuitem));
283 gtk_widget_destroy(dlg);
286 static int render_stars(gpointer data, gpointer udata)
288 Sky *sky = udata;
289 struct astrodb_slist *objects = data;
290 struct star_object *star;
292 while (objects) {
293 /* get object --> transform --> render_object */
294 star = (struct star_object*)objects->data;
296 sky->robject.object = star;
297 sky->robject.coord[0].posn = &star->dobject.posn;
298 sky->projection.trans->sky_to_proj_equ(&sky->projection,
299 &sky->robject.coord[0]);
301 if (projection_is_visible0(&sky->projection, &sky->robject)) {
303 star_object_render(&sky->robject);
305 if (sky->robject.type == RT_FULL)
306 tile_add_object(sky->tile, &sky->robject);
308 objects = astrodb_slist_next(objects);
310 return 0;
313 static void render_default(Sky *sky)
315 gfloat mag_bright, mag_faint, mag_index;
316 struct astrodb_slist *objects = NULL;
318 /* are we only rendering bright objects */
319 if (sky->robject.type == RT_FAST)
320 mag_faint = sky->projection.clip_mag_faint -
321 SKY_UPDATE_MAG_DIFF;
322 else
323 mag_faint = sky->projection.clip_mag_faint;
325 mag_bright = sky->projection.clip_mag_bright;
327 /* render_object in magnitude bands from
328 * brightest --> faintest_magnitude */
329 for (mag_index = mag_bright; mag_index < mag_faint; mag_index++) {
331 /* clip the catalog in mag bands */
332 astrodb_table_unclip(sky->default_star_table);
333 astrodb_table_clip_on_fov(sky->default_star_table,
334 sky->projection.centre_az,
335 sky->projection.centre_alt,
336 sky->projection.fov,
337 mag_index, mag_index -1);
339 if (astrodb_table_get_objects(sky->default_star_table,
340 &objects, ADB_SMEM) == 0)
341 continue;
343 astrodb_slist_foreach(objects, render_stars, sky);
344 astrodb_table_put_objects(objects);
345 objects = NULL;
350 * Render the virtual sky
352 * There are 2 different render_object types:-
354 * RT_FULL --> render_object everthing that is visible
355 * RT_FAST --> render_object bright items that are visible
357 static void vsky_render(Sky *sky)
359 /* render_object every item that is visible */
360 if (sky->marker_settings.show_const_figures)
361 constellation_render_lines(sky);
363 if (sky->marker_settings.show_grid)
364 grid_render(sky);
366 if (sky->marker_settings.show_const_bounds)
367 constellation_render_bounds(sky);
368 if (sky->marker_settings.show_const_names)
369 constellation_render_names(sky);
371 /* render_object every item that is visible */
372 if (sky->solar_system_settings.planets.show_object)
373 planets_render(sky);
374 if (sky->solar_system_settings.solar.show_object)
375 solar_render(sky);
376 if (sky->solar_system_settings.lunar.show_object)
377 lunar_render(sky);
379 render_default(sky);
383 static void virtual_sky_class_init (GtkSkyClass *class)
385 GObjectClass *obj_class;
386 GtkWidgetClass *widget_class;
388 obj_class = G_OBJECT_CLASS (class);
389 widget_class = GTK_WIDGET_CLASS (class);
391 widget_class->expose_event = virtual_sky_expose;
393 g_type_class_add_private (obj_class, sizeof(Sky));
396 static void virtual_sky_init (GtkSky *gtk_sky)
398 Sky *sky = NOVA_SKY_GET_PRIVATE(gtk_sky);
400 sky->default_star_table = db_get_default_table("star");
401 projection_set_flip(&sky->projection, PF_NONE);
402 sky->projection.centre_ra = 0;
403 sky->projection.centre_dec = 0;
404 sky->projection.fov = 180;
405 sky->projection.clip_mag_faint = 3;
406 sky->projection.clip_mag_bright = -2;
407 sky->projection.coords = PC_ALT_AZ;
408 sky->tile = tile_init();
410 sky->marker_settings.show_const_figures = 1;
411 sky->marker_settings.show_grid = 1;
412 sky->marker_settings.show_grid_labels = 1;
413 sky->solar_system_settings.planets.show_object = 1;
414 sky->solar_system_settings.planets.show_label = 1;
415 sky->solar_system_settings.lunar.show_label = 1;
416 sky->solar_system_settings.lunar.show_object = 1;
417 sky->solar_system_settings.solar.show_label = 1;
418 sky->solar_system_settings.solar.show_object = 1;
420 sky->robject.type = RT_FULL;
422 // TODO: get observer location and JD 1st run
423 sky->observer.JD = ln_get_julian_from_sys();
424 sky->observer.posn.lng = 3.0;
425 sky->observer.posn.lat = 56.0;
426 projection_set_observer(&sky->projection, &sky->observer);
428 virtual_sky_update (gtk_sky);
430 /* update the vsky once a second */
431 //g_timeout_add(1000, virtual_sky_update, gtk_sky);
434 static void sky_draw(GtkWidget *sky_widget, cairo_t *cr)
436 Sky *sky = NOVA_SKY_GET_PRIVATE(sky_widget);
438 tile_set_array_size(sky->tile, sky_widget->allocation.width,
439 sky_widget->allocation.height);
440 tile_reset_array(sky->tile);
442 projection_fov_bounds(&sky->projection, &sky->observer);
443 sky->robject.context.pixels_per_degree =
444 sky->projection.pixels_per_degree;
445 sky->robject.context.JD = sky->observer.JD;
446 sky->robject.context.faintest_magnitude =
447 sky->projection.clip_mag_faint;
449 sky->robject.cr = cr;
450 vsky_render(sky);
451 cairo_restore(cr);
452 sky->redraw_pending = sky->is_modified = 0;
455 static gboolean virtual_sky_expose (GtkWidget *sky_widget,
456 GdkEventExpose *event)
458 cairo_t *cr;
459 Sky *sky = NOVA_SKY_GET_PRIVATE(sky_widget);
461 /* the sky is modified if we have been resized */
462 if (sky->projection.sky_width != sky_widget->allocation.width ||
463 sky->projection.sky_height != sky_widget->allocation.height)
464 sky->is_modified = 1;
466 /* save sky dimensions */
467 sky->projection.sky_width = sky_widget->allocation.width;
468 sky->projection.sky_height = sky_widget->allocation.height;
470 /* get a cairo_t */
471 cr = gdk_cairo_create(sky_widget->window);
473 /* draw night sky background */
474 cairo_set_source_rgb(cr, 0, 0, 0);
475 cairo_paint(cr);
477 /* render_object sky objects */
478 sky_draw(sky_widget, cr);
479 cairo_destroy(cr);
481 return FALSE;
484 static void virtual_sky_redraw_canvas (GtkSky *gtk_sky)
486 GtkWidget *widget;
487 GdkRegion *region;
489 widget = GTK_WIDGET(gtk_sky);
491 if (!widget->window)
492 return;
494 region = gdk_drawable_get_clip_region(widget->window);
495 /* redraw the cairo canvas completely by exposing it */
496 gdk_window_invalidate_region(widget->window, region, TRUE);
497 gdk_window_process_updates(widget->window, TRUE);
499 gdk_region_destroy(region);
502 static gboolean virtual_sky_update (gpointer data)
504 GtkSky *gtk_sky = NOVA_SKY(data);
505 Sky *sky = NOVA_SKY_GET_PRIVATE(gtk_sky);
506 time_t timet;
508 /* update the time */
509 time(&timet);
510 localtime_r(&timet, &sky->time);
512 virtual_sky_redraw_canvas(gtk_sky);
514 return TRUE; /* keep running this event */
517 GtkWidget * virtual_sky_new (void)
519 return g_object_new(NOVA_TYPE_SKY, NULL);
523 void sky_connect_signals(GtkWidget *gtk_sky)
525 g_signal_connect(gtk_sky, "key_press_event",
526 G_CALLBACK (sky_key_press_event), NULL);
527 g_signal_connect(gtk_sky, "key_release_event",
528 G_CALLBACK (sky_key_release_event), NULL);
529 g_signal_connect(gtk_sky, "button_press_event",
530 G_CALLBACK (sky_button_press_event), NULL);
531 g_signal_connect(gtk_sky, "button_release_event",
532 G_CALLBACK (sky_button_release_event), NULL);
533 g_signal_connect(gtk_sky, "motion_notify_event",
534 G_CALLBACK (sky_motion_notify_event), NULL);
535 g_signal_connect(gtk_sky, "enter_notify_event",
536 G_CALLBACK (sky_enter_notify_event), NULL);
537 g_signal_connect(gtk_sky, "leave_notify_event",
538 G_CALLBACK (sky_leave_notify_event), NULL);
539 g_signal_connect(gtk_sky, "scroll_event",
540 G_CALLBACK (sky_scroll_event), NULL);
542 g_object_set(gtk_sky, "can-default", TRUE, "can-focus", TRUE, NULL);
543 gtk_widget_add_events (gtk_sky, GDK_POINTER_MOTION_MASK |
544 GDK_POINTER_MOTION_HINT_MASK |
545 GDK_BUTTON_PRESS_MASK |
546 GDK_BUTTON_RELEASE_MASK |
547 GDK_KEY_PRESS_MASK |
548 GDK_KEY_RELEASE_MASK |
549 GDK_ENTER_NOTIFY_MASK |
550 GDK_LEAVE_NOTIFY_MASK);
552 sky_cursor_arrow = gdk_cursor_new(GDK_LEFT_PTR);
553 sky_cursor_ptr = gdk_cursor_new(GDK_CROSSHAIR);
556 void sky_set_legend(GtkWidget *sky_widget, GtkWidget *legend_widget)
558 Sky *sky = NOVA_SKY_GET_PRIVATE(sky_widget);
560 sky->legend_widget = legend_widget;
561 legend_set_observer(sky->legend_widget, &sky->observer);