]> Pileus Git - grits/blob - src/gis-viewer.c
GisViewer: Improve add/remove functions
[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 gpointer gis_viewer_add(GisViewer *self, GisObject *object,
275                 gint level, gboolean sort)
276 {
277         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
278         if (!klass->add)
279                 g_warning("GisViewer: add - Unimplemented");
280         return klass->add(self, object, level, sort);
281 }
282
283 GisObject *gis_viewer_remove(GisViewer *self, gpointer ref)
284 {
285         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
286         if (!klass->remove)
287                 g_warning("GisViewer: remove - Unimplemented");
288         return klass->remove(self, ref);
289 }
290
291 /****************
292  * GObject code *
293  ****************/
294 G_DEFINE_ABSTRACT_TYPE(GisViewer, gis_viewer, GTK_TYPE_DRAWING_AREA);
295 static void gis_viewer_init(GisViewer *self)
296 {
297         g_debug("GisViewer: init");
298         /* Default values */
299         self->time = g_strdup("");
300         self->location[0] = 40;
301         self->location[1] = -100;
302         self->location[2] = 1.5*EARTH_R;
303         self->rotation[0] = 0;
304         self->rotation[1] = 0;
305         self->rotation[2] = 0;
306
307         g_signal_connect(self, "key-press-event",    G_CALLBACK(on_key_press),    NULL);
308         g_signal_connect(self, "button-press-event", G_CALLBACK(on_button_press), NULL);
309
310         g_signal_connect(self, "location-changed",   G_CALLBACK(on_view_changed), NULL);
311         g_signal_connect(self, "rotation-changed",   G_CALLBACK(on_view_changed), NULL);
312 }
313 static void gis_viewer_finalize(GObject *gobject)
314 {
315         g_debug("GisViewer: finalize");
316         GisViewer *self = GIS_VIEWER(gobject);
317         g_free(self->time);
318         G_OBJECT_CLASS(gis_viewer_parent_class)->finalize(gobject);
319 }
320 static void gis_viewer_class_init(GisViewerClass *klass)
321 {
322         g_debug("GisViewer: class_init");
323         GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
324         gobject_class->finalize     = gis_viewer_finalize;
325         signals[SIG_TIME_CHANGED] = g_signal_new(
326                         "time-changed",
327                         G_TYPE_FROM_CLASS(gobject_class),
328                         G_SIGNAL_RUN_LAST,
329                         0,
330                         NULL,
331                         NULL,
332                         g_cclosure_marshal_VOID__STRING,
333                         G_TYPE_NONE,
334                         1,
335                         G_TYPE_STRING);
336         signals[SIG_LOCATION_CHANGED] = g_signal_new(
337                         "location-changed",
338                         G_TYPE_FROM_CLASS(gobject_class),
339                         G_SIGNAL_RUN_LAST,
340                         0,
341                         NULL,
342                         NULL,
343                         gis_cclosure_marshal_VOID__DOUBLE_DOUBLE_DOUBLE,
344                         G_TYPE_NONE,
345                         3,
346                         G_TYPE_DOUBLE,
347                         G_TYPE_DOUBLE,
348                         G_TYPE_DOUBLE);
349         signals[SIG_ROTATION_CHANGED] = g_signal_new(
350                         "rotation-changed",
351                         G_TYPE_FROM_CLASS(gobject_class),
352                         G_SIGNAL_RUN_LAST,
353                         0,
354                         NULL,
355                         NULL,
356                         gis_cclosure_marshal_VOID__DOUBLE_DOUBLE_DOUBLE,
357                         G_TYPE_NONE,
358                         3,
359                         G_TYPE_DOUBLE,
360                         G_TYPE_DOUBLE,
361                         G_TYPE_DOUBLE);
362         signals[SIG_REFRESH] = g_signal_new(
363                         "refresh",
364                         G_TYPE_FROM_CLASS(gobject_class),
365                         G_SIGNAL_RUN_LAST,
366                         0,
367                         NULL,
368                         NULL,
369                         g_cclosure_marshal_VOID__VOID,
370                         G_TYPE_NONE,
371                         0);
372         signals[SIG_OFFLINE] = g_signal_new(
373                         "offline",
374                         G_TYPE_FROM_CLASS(gobject_class),
375                         G_SIGNAL_RUN_LAST,
376                         0,
377                         NULL,
378                         NULL,
379                         g_cclosure_marshal_VOID__BOOLEAN,
380                         G_TYPE_NONE,
381                         1,
382                         G_TYPE_BOOLEAN);
383 }