]> Pileus Git - grits/blob - src/gis-viewer.c
Move around a lot of preferences management
[grits] / src / gis-viewer.c
1 /*
2  * Copyright (C) 2009-2010 Andy Spencer <andy753421@gmail.com>
3  *
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 3 of the License, or
7  * (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, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include <config.h>
19 #include <gtk/gtk.h>
20 #include <gdk/gdkkeysyms.h>
21
22 #include "gis-marshal.h"
23 #include "gis-viewer.h"
24
25 #include "gis-util.h"
26
27
28 /* Constants */
29 enum {
30         SIG_TIME_CHANGED,
31         SIG_LOCATION_CHANGED,
32         SIG_ROTATION_CHANGED,
33         SIG_REFRESH,
34         SIG_OFFLINE,
35         NUM_SIGNALS,
36 };
37 static guint signals[NUM_SIGNALS];
38
39
40 /***********
41  * Helpers *
42  ***********/
43 /* Misc helpers */
44 static void _gis_viewer_fix_location(GisViewer *self)
45 {
46         while (self->location[0] <  -90) self->location[0] += 180;
47         while (self->location[0] >   90) self->location[0] -= 180;
48         while (self->location[1] < -180) self->location[1] += 360;
49         while (self->location[1] >  180) self->location[1] -= 360;
50         self->location[2] = ABS(self->location[2]);
51 }
52
53 /* Signal helpers */
54 static void _gis_viewer_emit_location_changed(GisViewer *self)
55 {
56         g_signal_emit(self, signals[SIG_LOCATION_CHANGED], 0,
57                         self->location[0],
58                         self->location[1],
59                         self->location[2]);
60 }
61 static void _gis_viewer_emit_rotation_changed(GisViewer *self)
62 {
63         g_signal_emit(self, signals[SIG_ROTATION_CHANGED], 0,
64                         self->rotation[0],
65                         self->rotation[1],
66                         self->rotation[2]);
67 }
68 static void _gis_viewer_emit_time_changed(GisViewer *self)
69 {
70         g_signal_emit(self, signals[SIG_TIME_CHANGED], 0,
71                         self->time);
72 }
73 static void _gis_viewer_emit_refresh(GisViewer *self)
74 {
75         g_signal_emit(self, signals[SIG_REFRESH], 0);
76 }
77 static void _gis_viewer_emit_offline(GisViewer *self)
78 {
79         g_signal_emit(self, signals[SIG_OFFLINE], 0,
80                         self->offline);
81 }
82
83 /*************
84  * Callbacks *
85  *************/
86 static gboolean on_button_press(GisViewer *self, GdkEventButton *event, gpointer _)
87 {
88         g_debug("GisViewer: on_button_press - Grabbing focus");
89         gtk_widget_grab_focus(GTK_WIDGET(self));
90         return TRUE;
91 }
92 static gboolean on_key_press(GisViewer *self, GdkEventKey *event, gpointer _)
93 {
94         g_debug("GisViewer: on_key_press - key=%x, state=%x, plus=%x",
95                         event->keyval, event->state, GDK_plus);
96
97         double lat, lon, elev, pan;
98         gis_viewer_get_location(self, &lat, &lon, &elev);
99         pan = MIN(elev/(EARTH_R/2), 30);
100         guint kv = event->keyval;
101         gdk_threads_leave();
102         if      (kv == GDK_Left  || kv == GDK_h) gis_viewer_pan(self,  0,  -pan, 0);
103         else if (kv == GDK_Down  || kv == GDK_j) gis_viewer_pan(self, -pan, 0,   0);
104         else if (kv == GDK_Up    || kv == GDK_k) gis_viewer_pan(self,  pan, 0,   0);
105         else if (kv == GDK_Right || kv == GDK_l) gis_viewer_pan(self,  0,   pan, 0);
106         else if (kv == GDK_minus || kv == GDK_o) gis_viewer_zoom(self, 10./9);
107         else if (kv == GDK_plus  || kv == GDK_i) gis_viewer_zoom(self, 9./10);
108         else if (kv == GDK_H) gis_viewer_rotate(self,  0, 0, -2);
109         else if (kv == GDK_J) gis_viewer_rotate(self,  2, 0,  0);
110         else if (kv == GDK_K) gis_viewer_rotate(self, -2, 0,  0);
111         else if (kv == GDK_L) gis_viewer_rotate(self,  0, 0,  2);
112         return FALSE;
113 }
114 static void on_view_changed(GisViewer *self,
115                 gdouble _1, gdouble _2, gdouble _3)
116 {
117         gtk_widget_queue_draw(GTK_WIDGET(self));
118 }
119
120 /***********
121  * Methods *
122  ***********/
123 void gis_viewer_setup(GisViewer *self, GisPlugins *plugins, GisPrefs *prefs)
124 {
125         self->plugins = plugins;
126         self->prefs   = prefs;
127         self->offline = gis_prefs_get_boolean(prefs, "gis/offline", NULL);
128 }
129
130 void gis_viewer_set_time(GisViewer *self, const char *time)
131 {
132         g_assert(GIS_IS_VIEWER(self));
133         g_debug("GisViewer: set_time - time=%s", time);
134         g_free(self->time);
135         self->time = g_strdup(time);
136         _gis_viewer_emit_time_changed(self);
137 }
138
139 gchar *gis_viewer_get_time(GisViewer *self)
140 {
141         g_assert(GIS_IS_VIEWER(self));
142         g_debug("GisViewer: get_time");
143         return self->time;
144 }
145
146 void gis_viewer_set_location(GisViewer *self, gdouble lat, gdouble lon, gdouble elev)
147 {
148         g_assert(GIS_IS_VIEWER(self));
149         g_debug("GisViewer: set_location");
150         self->location[0] = lat;
151         self->location[1] = lon;
152         self->location[2] = elev;
153         _gis_viewer_fix_location(self);
154         _gis_viewer_emit_location_changed(self);
155 }
156
157 void gis_viewer_get_location(GisViewer *self, gdouble *lat, gdouble *lon, gdouble *elev)
158 {
159         g_assert(GIS_IS_VIEWER(self));
160         //g_debug("GisViewer: get_location");
161         *lat  = self->location[0];
162         *lon  = self->location[1];
163         *elev = self->location[2];
164 }
165
166 void gis_viewer_pan(GisViewer *self, gdouble lat, gdouble lon, gdouble elev)
167 {
168         g_assert(GIS_IS_VIEWER(self));
169         g_debug("GisViewer: pan - lat=%8.3f, lon=%8.3f, elev=%8.3f", lat, lon, elev);
170         self->location[0] += lat;
171         self->location[1] += lon;
172         self->location[2] += elev;
173         _gis_viewer_fix_location(self);
174         _gis_viewer_emit_location_changed(self);
175 }
176
177 void gis_viewer_zoom(GisViewer *self, gdouble scale)
178 {
179         g_assert(GIS_IS_VIEWER(self));
180         g_debug("GisViewer: zoom");
181         self->location[2] *= scale;
182         _gis_viewer_emit_location_changed(self);
183 }
184
185 void gis_viewer_set_rotation(GisViewer *self, gdouble x, gdouble y, gdouble z)
186 {
187         g_assert(GIS_IS_VIEWER(self));
188         g_debug("GisViewer: set_rotation");
189         self->rotation[0] = x;
190         self->rotation[1] = y;
191         self->rotation[2] = z;
192         _gis_viewer_emit_rotation_changed(self);
193 }
194
195 void gis_viewer_get_rotation(GisViewer *self, gdouble *x, gdouble *y, gdouble *z)
196 {
197         g_assert(GIS_IS_VIEWER(self));
198         g_debug("GisViewer: get_rotation");
199         *x = self->rotation[0];
200         *y = self->rotation[1];
201         *z = self->rotation[2];
202 }
203
204 void gis_viewer_rotate(GisViewer *self, gdouble x, gdouble y, gdouble z)
205 {
206         g_assert(GIS_IS_VIEWER(self));
207         g_debug("GisViewer: rotate - x=%.0f, y=%.0f, z=%.0f", x, y, z);
208         self->rotation[0] += x;
209         self->rotation[1] += y;
210         self->rotation[2] += z;
211         _gis_viewer_emit_rotation_changed(self);
212 }
213
214 void gis_viewer_refresh(GisViewer *self)
215 {
216         g_debug("GisViewer: refresh");
217         _gis_viewer_emit_refresh(self);
218 }
219
220 void gis_viewer_set_offline(GisViewer *self, gboolean offline)
221 {
222         g_assert(GIS_IS_VIEWER(self));
223         g_debug("GisViewer: set_offline - %d", offline);
224         gis_prefs_set_boolean(self->prefs, "gis/offline", offline);
225         self->offline = offline;
226         _gis_viewer_emit_offline(self);
227 }
228
229 gboolean gis_viewer_get_offline(GisViewer *self)
230 {
231         g_assert(GIS_IS_VIEWER(self));
232         g_debug("GisViewer: get_offline - %d", self->offline);
233         return self->offline;
234 }
235
236 /* To be implemented by subclasses */
237 void gis_viewer_center_position(GisViewer *self,
238                 gdouble lat, gdouble lon, gdouble elev)
239 {
240         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
241         if (!klass->center_position)
242                 g_warning("GisViewer: center_position - Unimplemented");
243         klass->center_position(self, lat, lon, elev);
244 }
245
246 void gis_viewer_project(GisViewer *self,
247                 gdouble lat, gdouble lon, gdouble elev,
248                 gdouble *px, gdouble *py, gdouble *pz)
249 {
250         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
251         if (!klass->project)
252                 g_warning("GisViewer: project - Unimplemented");
253         klass->project(self, lat, lon, elev, px, py, pz);
254 }
255
256 void gis_viewer_clear_height_func(GisViewer *self)
257 {
258         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
259         if (!klass->clear_height_func)
260                 g_warning("GisViewer: clear_height_func - Unimplemented");
261         klass->clear_height_func(self);
262 }
263
264 void gis_viewer_set_height_func(GisViewer *self, GisTile *tile,
265                 GisHeightFunc height_func, gpointer user_data,
266                 gboolean update)
267 {
268         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
269         if (!klass->set_height_func)
270                 g_warning("GisViewer: set_height_func - Unimplemented");
271         klass->set_height_func(self, tile, height_func, user_data, update);
272 }
273
274 void gis_viewer_render_tile(GisViewer *self, GisTile *tile)
275 {
276         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
277         if (!klass->render_tile)
278                 g_warning("GisViewer: render_tile - Unimplemented");
279         klass->render_tile(self, tile);
280 }
281
282 void gis_viewer_render_tiles(GisViewer *self, GisTile *root)
283 {
284         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
285         if (!klass->render_tiles)
286                 g_warning("GisViewer: render_tiles - Unimplemented");
287         klass->render_tiles(self, root);
288 }
289
290 void gis_viewer_begin(GisViewer *self)
291 {
292         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
293         if (!klass->begin)
294                 g_warning("GisViewer: begin - Unimplemented");
295         klass->begin(self);
296 }
297
298 void gis_viewer_end(GisViewer *self)
299 {
300         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
301         if (!klass->end)
302                 g_warning("GisViewer: end - Unimplemented");
303         klass->end(self);
304 }
305
306 gpointer gis_viewer_add(GisViewer *self, GisObject *object,
307                 gint level, gboolean sort)
308 {
309         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
310         if (!klass->add)
311                 g_warning("GisViewer: add - Unimplemented");
312         return klass->add(self, object, level, sort);
313 }
314
315 void gis_viewer_remove(GisViewer *self, gpointer ref)
316 {
317         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
318         if (!klass->remove)
319                 g_warning("GisViewer: remove - Unimplemented");
320         klass->remove(self, ref);
321 }
322
323 /****************
324  * GObject code *
325  ****************/
326 G_DEFINE_ABSTRACT_TYPE(GisViewer, gis_viewer, GTK_TYPE_DRAWING_AREA);
327 static void gis_viewer_init(GisViewer *self)
328 {
329         g_debug("GisViewer: init");
330         /* Default values */
331         self->time = g_strdup("");
332         self->location[0] = 40;
333         self->location[1] = -100;
334         self->location[2] = 1.5*EARTH_R;
335         self->rotation[0] = 0;
336         self->rotation[1] = 0;
337         self->rotation[2] = 0;
338
339         g_signal_connect(self, "key-press-event",    G_CALLBACK(on_key_press),    NULL);
340         g_signal_connect(self, "button-press-event", G_CALLBACK(on_button_press), NULL);
341
342         g_signal_connect(self, "location-changed",   G_CALLBACK(on_view_changed), NULL);
343         g_signal_connect(self, "rotation-changed",   G_CALLBACK(on_view_changed), NULL);
344 }
345 static void gis_viewer_finalize(GObject *gobject)
346 {
347         g_debug("GisViewer: finalize");
348         GisViewer *self = GIS_VIEWER(gobject);
349         g_free(self->time);
350         G_OBJECT_CLASS(gis_viewer_parent_class)->finalize(gobject);
351 }
352 static void gis_viewer_class_init(GisViewerClass *klass)
353 {
354         g_debug("GisViewer: class_init");
355         GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
356         gobject_class->finalize     = gis_viewer_finalize;
357         signals[SIG_TIME_CHANGED] = g_signal_new(
358                         "time-changed",
359                         G_TYPE_FROM_CLASS(gobject_class),
360                         G_SIGNAL_RUN_LAST,
361                         0,
362                         NULL,
363                         NULL,
364                         g_cclosure_marshal_VOID__STRING,
365                         G_TYPE_NONE,
366                         1,
367                         G_TYPE_STRING);
368         signals[SIG_LOCATION_CHANGED] = g_signal_new(
369                         "location-changed",
370                         G_TYPE_FROM_CLASS(gobject_class),
371                         G_SIGNAL_RUN_LAST,
372                         0,
373                         NULL,
374                         NULL,
375                         gis_cclosure_marshal_VOID__DOUBLE_DOUBLE_DOUBLE,
376                         G_TYPE_NONE,
377                         3,
378                         G_TYPE_DOUBLE,
379                         G_TYPE_DOUBLE,
380                         G_TYPE_DOUBLE);
381         signals[SIG_ROTATION_CHANGED] = g_signal_new(
382                         "rotation-changed",
383                         G_TYPE_FROM_CLASS(gobject_class),
384                         G_SIGNAL_RUN_LAST,
385                         0,
386                         NULL,
387                         NULL,
388                         gis_cclosure_marshal_VOID__DOUBLE_DOUBLE_DOUBLE,
389                         G_TYPE_NONE,
390                         3,
391                         G_TYPE_DOUBLE,
392                         G_TYPE_DOUBLE,
393                         G_TYPE_DOUBLE);
394         signals[SIG_REFRESH] = g_signal_new(
395                         "refresh",
396                         G_TYPE_FROM_CLASS(gobject_class),
397                         G_SIGNAL_RUN_LAST,
398                         0,
399                         NULL,
400                         NULL,
401                         g_cclosure_marshal_VOID__VOID,
402                         G_TYPE_NONE,
403                         0);
404         signals[SIG_OFFLINE] = g_signal_new(
405                         "offline",
406                         G_TYPE_FROM_CLASS(gobject_class),
407                         G_SIGNAL_RUN_LAST,
408                         0,
409                         NULL,
410                         NULL,
411                         g_cclosure_marshal_VOID__BOOLEAN,
412                         G_TYPE_NONE,
413                         1,
414                         G_TYPE_BOOLEAN);
415 }