+static void _gis_viewer_emit_offline(GisViewer *viewer)
+{
+ g_signal_emit(viewer, signals[SIG_OFFLINE], 0,
+ viewer->offline);
+}
+
+/*************
+ * Callbacks *
+ *************/
+static gboolean on_key_press(GisViewer *viewer, GdkEventKey *event, gpointer _)
+{
+ g_debug("GisViewer: on_key_press - key=%x, state=%x, plus=%x",
+ event->keyval, event->state, GDK_plus);
+
+ double lat, lon, elev, pan;
+ gis_viewer_get_location(viewer, &lat, &lon, &elev);
+ pan = MIN(elev/(EARTH_R/2), 30);
+ gdk_threads_leave();
+ switch (event->keyval) {
+ case GDK_Left: case GDK_h: gis_viewer_pan(viewer, 0, -pan, 0); break;
+ case GDK_Down: case GDK_j: gis_viewer_pan(viewer, -pan, 0, 0); break;
+ case GDK_Up: case GDK_k: gis_viewer_pan(viewer, pan, 0, 0); break;
+ case GDK_Right: case GDK_l: gis_viewer_pan(viewer, 0, pan, 0); break;
+ case GDK_minus: case GDK_o: gis_viewer_zoom(viewer, 10./9); break;
+ case GDK_plus: case GDK_i: gis_viewer_zoom(viewer, 9./10); break;
+ case GDK_H: gis_viewer_rotate(viewer, 0, 0, -2); break;
+ case GDK_J: gis_viewer_rotate(viewer, 2, 0, 0); break;
+ case GDK_K: gis_viewer_rotate(viewer, -2, 0, 0); break;
+ case GDK_L: gis_viewer_rotate(viewer, 0, 0, 2); break;
+ }
+ return FALSE;
+}
+
+static gboolean on_scroll(GisViewer *viewer, GdkEventScroll *event, gpointer _)
+{
+ switch (event->direction) {
+ case GDK_SCROLL_DOWN: gis_viewer_zoom(viewer, 10./9); break;
+ case GDK_SCROLL_UP: gis_viewer_zoom(viewer, 9./10); break;
+ default: break;
+ }
+ return FALSE;
+}
+
+enum {
+ GIS_DRAG_NONE,
+ GIS_DRAG_PAN,
+ GIS_DRAG_ZOOM,
+ GIS_DRAG_TILT,
+};
+
+static gboolean on_button_press(GisViewer *viewer, GdkEventButton *event, gpointer _)