2 * Copyright (C) 2006, 2007 John Costigan.
4 * POI and GPS-Info code originally written by Cezary Jackiewicz.
6 * Default map data provided by http://www.openstreetmap.org/
8 * This file is part of Maemo Mapper.
10 * Maemo Mapper is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 3 of the License, or
13 * (at your option) any later version.
15 * Maemo Mapper is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with Maemo Mapper. If not, see <http://www.gnu.org/licenses/>.
31 #include <gdk/gdkkeysyms.h>
34 # include <hildon/hildon-defines.h>
35 # include <hildon/hildon-banner.h>
37 # include <hildon-widgets/hildon-defines.h>
38 # include <hildon-widgets/hildon-banner.h>
46 #include "gdk-pixbuf-rotate.h"
54 static gint _key_zoom_is_down = FALSE;
55 static gint _key_zoom_new = -1;
56 static gint _key_zoom_timeout_sid = 0;
57 static gint _key_pan_is_down = FALSE;
58 static gfloat _key_pan_incr_devx = 0;
59 static gfloat _key_pan_incr_devy = 0;
60 static gint _key_pan_timeout_sid = 0;
63 key_pan_timeout(CustomAction action)
65 printf("%s()\n", __PRETTY_FUNCTION__);
68 /* The pan key is still down. Continue panning. */
69 _map_offset_devx += -_key_pan_incr_devx;
70 _map_offset_devy += -_key_pan_incr_devy;
71 MACRO_QUEUE_DRAW_AREA();
72 vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
77 gfloat panx_adj, pany_adj;
78 /* Time is up for further action - execute the pan. */
79 /* Adjust for rotate angle. */
80 gdk_pixbuf_rotate_vector(&panx_adj, &pany_adj, _map_reverse_matrix,
81 _map_offset_devx, _map_offset_devy);
82 map_pan(-pixel2unit((gint)(panx_adj + 0.5f)),
83 -pixel2unit((gint)(pany_adj + 0.5f)));
84 _key_pan_timeout_sid = 0;
85 vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
91 key_zoom_timeout(CustomAction action)
93 printf("%s()\n", __PRETTY_FUNCTION__);
96 /* The zoom key is still down. Continue zooming. */
97 if(action == CUSTOM_ACTION_ZOOM_IN)
99 /* We're currently zooming in (_zoom is decreasing). */
100 gint test = _key_zoom_new - _curr_repo->view_zoom_steps;
102 /* We can zoom some more. Hurray! */
103 _key_zoom_new = test;
107 /* We're currently zooming out (_zoom is increasing). */
108 gint test = _key_zoom_new + _curr_repo->view_zoom_steps;
110 /* We can zoom some more. Hurray! */
111 _key_zoom_new = test;
113 /* Tell them where they're about to zoom. */
116 snprintf(buffer, sizeof(buffer),
117 "%s %d", _("Zoom to Level"), _key_zoom_new);
118 MACRO_BANNER_SHOW_INFO(_window, buffer);
120 vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
125 /* Time is up for further action - execute. */
126 if(_key_zoom_new != _zoom)
127 map_set_zoom(_key_zoom_new);
128 _key_pan_timeout_sid = 0;
130 vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
137 get_custom_key_from_keyval(gint keyval)
139 CustomKey custom_key;
140 printf("%s(%d)\n", __PRETTY_FUNCTION__, keyval);
144 case HILDON_HARDKEY_UP:
145 custom_key = CUSTOM_KEY_UP;
147 case HILDON_HARDKEY_DOWN:
148 custom_key = CUSTOM_KEY_DOWN;
150 case HILDON_HARDKEY_LEFT:
151 custom_key = CUSTOM_KEY_LEFT;
153 case HILDON_HARDKEY_RIGHT:
154 custom_key = CUSTOM_KEY_RIGHT;
156 case HILDON_HARDKEY_SELECT:
157 custom_key = CUSTOM_KEY_SELECT;
159 case HILDON_HARDKEY_INCREASE:
160 custom_key = CUSTOM_KEY_INCREASE;
162 case HILDON_HARDKEY_DECREASE:
163 custom_key = CUSTOM_KEY_DECREASE;
165 case HILDON_HARDKEY_FULLSCREEN:
166 custom_key = CUSTOM_KEY_FULLSCREEN;
168 case HILDON_HARDKEY_ESC:
169 custom_key = CUSTOM_KEY_ESC;
175 vprintf("%s(): return %d\n", __PRETTY_FUNCTION__, custom_key);
180 window_cb_key_press(GtkWidget* widget, GdkEventKey *event)
182 CustomKey custom_key;
183 printf("%s()\n", __PRETTY_FUNCTION__);
185 custom_key = get_custom_key_from_keyval(event->keyval);
187 return FALSE; /* Not our event. */
189 switch(_action[custom_key])
191 case CUSTOM_ACTION_PAN_NORTH:
192 case CUSTOM_ACTION_PAN_SOUTH:
193 case CUSTOM_ACTION_PAN_EAST:
194 case CUSTOM_ACTION_PAN_WEST:
195 case CUSTOM_ACTION_PAN_UP:
196 case CUSTOM_ACTION_PAN_DOWN:
197 case CUSTOM_ACTION_PAN_LEFT:
198 case CUSTOM_ACTION_PAN_RIGHT:
199 if(!_key_pan_is_down)
201 _key_pan_is_down = TRUE;
203 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(
204 _menu_view_ac_none_item), FALSE);
205 if(_key_pan_timeout_sid)
207 g_source_remove(_key_pan_timeout_sid);
208 _key_pan_timeout_sid = 0;
210 /* Figure out new pan. */
211 switch(_action[custom_key])
213 case CUSTOM_ACTION_PAN_UP:
214 case CUSTOM_ACTION_PAN_NORTH:
215 _key_pan_incr_devy = -PAN_PIXELS;
217 case CUSTOM_ACTION_PAN_SOUTH:
218 case CUSTOM_ACTION_PAN_DOWN:
219 _key_pan_incr_devy = PAN_PIXELS;
221 case CUSTOM_ACTION_PAN_EAST:
222 case CUSTOM_ACTION_PAN_RIGHT:
223 _key_pan_incr_devx = PAN_PIXELS;
225 case CUSTOM_ACTION_PAN_WEST:
226 case CUSTOM_ACTION_PAN_LEFT:
227 _key_pan_incr_devx = -PAN_PIXELS;
230 g_printerr("Invalid action in key_pan_timeout(): %d\n",
231 _action[custom_key]);
233 switch(_action[custom_key])
235 case CUSTOM_ACTION_PAN_NORTH:
236 case CUSTOM_ACTION_PAN_SOUTH:
237 case CUSTOM_ACTION_PAN_EAST:
238 case CUSTOM_ACTION_PAN_WEST:
239 /* Adjust for rotate angle. */
240 gdk_pixbuf_rotate_vector(&_key_pan_incr_devx,
241 &_key_pan_incr_devy, _map_rotate_matrix,
242 _key_pan_incr_devx, _key_pan_incr_devy);
246 key_pan_timeout(_action[custom_key]);
247 _key_pan_timeout_sid = g_timeout_add_full(G_PRIORITY_HIGH_IDLE,
248 250, (GSourceFunc)key_pan_timeout,
249 (gpointer)(_action[custom_key]), NULL);
253 case CUSTOM_ACTION_RESET_VIEW_ANGLE:
254 map_rotate(-_next_map_rotate_angle);
257 case CUSTOM_ACTION_ROTATE_CLOCKWISE:
259 map_rotate(-ROTATE_DEGREES);
263 case CUSTOM_ACTION_ROTATE_COUNTERCLOCKWISE:
265 map_rotate(ROTATE_DEGREES);
269 case CUSTOM_ACTION_TOGGLE_AUTOROTATE:
270 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(
271 _menu_view_rotate_auto_item), !_center_rotate);
274 case CUSTOM_ACTION_TOGGLE_AUTOCENTER:
278 case CENTER_WAS_LEAD:
279 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(
280 _menu_view_ac_lead_item), TRUE);
283 case CENTER_WAS_LATLON:
284 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(
285 _menu_view_ac_latlon_item), TRUE);
290 case CUSTOM_ACTION_ZOOM_IN:
291 case CUSTOM_ACTION_ZOOM_OUT:
292 if(!_key_zoom_is_down)
294 _key_zoom_is_down = TRUE;
295 if(_key_zoom_timeout_sid)
297 g_source_remove(_key_zoom_timeout_sid);
298 _key_zoom_timeout_sid = 0;
300 if(_key_zoom_new == -1)
301 _key_zoom_new = _next_zoom;
302 _key_zoom_new = _key_zoom_new
303 + (_action[custom_key] == CUSTOM_ACTION_ZOOM_IN
304 ? -_curr_repo->view_zoom_steps
305 : _curr_repo->view_zoom_steps);
306 BOUND(_key_zoom_new, 0, MAX_ZOOM);
309 snprintf(buffer, sizeof(buffer),"%s %d",
310 _("Zoom to Level"), _key_zoom_new);
311 MACRO_BANNER_SHOW_INFO(_window, buffer);
313 _key_zoom_timeout_sid =g_timeout_add_full(G_PRIORITY_HIGH_IDLE,
314 500, (GSourceFunc)key_zoom_timeout,
315 (gpointer)(_action[custom_key]), NULL);
319 case CUSTOM_ACTION_TOGGLE_FULLSCREEN:
320 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(
321 _menu_view_fullscreen_item), !_fullscreen);
324 case CUSTOM_ACTION_TOGGLE_TRACKING:
325 gtk_check_menu_item_set_active(
326 GTK_CHECK_MENU_ITEM(_menu_track_enable_tracking_item),
330 case CUSTOM_ACTION_TOGGLE_TRACKS:
334 /* Nothing shown, nothing saved; just set both. */
335 _show_paths = TRACKS_MASK | ROUTES_MASK;
337 case TRACKS_MASK << 16:
338 case ROUTES_MASK << 16:
339 case (ROUTES_MASK | TRACKS_MASK) << 16:
340 /* Something was saved and nothing changed since.
342 _show_paths = _show_paths >> 16;
345 /* There is no history, or they changed something
346 * since the last historical change. Save and
348 _show_paths = _show_paths << 16;
350 gtk_check_menu_item_set_active(
351 GTK_CHECK_MENU_ITEM(_menu_view_show_routes_item),
352 _show_paths & ROUTES_MASK);
354 gtk_check_menu_item_set_active(
355 GTK_CHECK_MENU_ITEM(_menu_view_show_tracks_item),
356 _show_paths & TRACKS_MASK);
358 case CUSTOM_ACTION_TOGGLE_SCALE:
359 gtk_check_menu_item_set_active(
360 GTK_CHECK_MENU_ITEM(_menu_view_show_scale_item),
364 case CUSTOM_ACTION_TOGGLE_POI:
365 gtk_check_menu_item_set_active(
366 GTK_CHECK_MENU_ITEM(_menu_view_show_poi_item),
369 case CUSTOM_ACTION_CHANGE_REPO: {
370 GList *curr = g_list_find(_repo_list, _curr_repo);
374 /* Loop until we reach a next-able repo, or until we get
375 * back to the current repo. */
376 while((curr = (curr->next ? curr->next : _repo_list))
377 && !((RepoData*)curr->data)->nextable
378 && curr->data != _curr_repo) { }
380 if(curr->data != _curr_repo)
382 repo_set_curr(curr->data);
383 gtk_check_menu_item_set_active(
384 GTK_CHECK_MENU_ITEM(_curr_repo->menu_item),
390 _("There are no other next-able repositories."));
395 case CUSTOM_ACTION_RESET_BLUETOOTH:
399 case CUSTOM_ACTION_ROUTE_DISTNEXT:
400 route_show_distance_to_next();
403 case CUSTOM_ACTION_ROUTE_DISTLAST:
404 route_show_distance_to_last();
407 case CUSTOM_ACTION_TRACK_BREAK:
408 track_insert_break(TRUE);
411 case CUSTOM_ACTION_TRACK_CLEAR:
415 case CUSTOM_ACTION_TRACK_DISTLAST:
416 track_show_distance_from_last();
419 case CUSTOM_ACTION_TRACK_DISTFIRST:
420 track_show_distance_from_first();
423 case CUSTOM_ACTION_TOGGLE_GPS:
424 gtk_check_menu_item_set_active(
425 GTK_CHECK_MENU_ITEM(_menu_gps_enable_item),
429 case CUSTOM_ACTION_TOGGLE_GPSINFO:
430 gtk_check_menu_item_set_active(
431 GTK_CHECK_MENU_ITEM(_menu_gps_show_info_item),
435 case CUSTOM_ACTION_TOGGLE_SPEEDLIMIT:
436 _speed_limit_on ^= 1;
439 case CUSTOM_ACTION_TOGGLE_LAYERS:
440 maps_toggle_visible_layers ();
444 vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
449 vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
453 window_cb_key_release(GtkWidget* widget, GdkEventKey *event)
455 CustomKey custom_key;
456 printf("%s()\n", __PRETTY_FUNCTION__);
458 custom_key = get_custom_key_from_keyval(event->keyval);
460 return FALSE; /* Not our event. */
462 switch(_action[custom_key])
464 case CUSTOM_ACTION_ZOOM_IN:
465 case CUSTOM_ACTION_ZOOM_OUT:
466 if(_key_zoom_timeout_sid)
468 g_source_remove(_key_zoom_timeout_sid);
469 _key_zoom_timeout_sid = 0;
470 _key_zoom_timeout_sid =g_timeout_add_full(G_PRIORITY_HIGH_IDLE,
471 500, (GSourceFunc)key_zoom_timeout, NULL, NULL);
473 _key_zoom_is_down = FALSE;
476 case CUSTOM_ACTION_PAN_NORTH:
477 case CUSTOM_ACTION_PAN_SOUTH:
478 case CUSTOM_ACTION_PAN_EAST:
479 case CUSTOM_ACTION_PAN_WEST:
480 case CUSTOM_ACTION_PAN_UP:
481 case CUSTOM_ACTION_PAN_DOWN:
482 case CUSTOM_ACTION_PAN_LEFT:
483 case CUSTOM_ACTION_PAN_RIGHT:
484 if(_key_pan_timeout_sid)
486 g_source_remove(_key_pan_timeout_sid);
487 _key_pan_timeout_sid = g_timeout_add_full(G_PRIORITY_HIGH_IDLE,
488 500, (GSourceFunc)key_pan_timeout, NULL, NULL);
490 _key_pan_is_down = FALSE;
491 _key_pan_incr_devx = 0;
492 _key_pan_incr_devy = 0;
498 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
502 map_cb_motion_notify(GtkWidget *widget, GdkEventMotion *event)
505 GdkModifierType state;
506 printf("%s()\n", __PRETTY_FUNCTION__);
512 gdk_window_get_pointer(event->window, &x, &y, &state);
517 state = event->state;
520 if(_mouse_is_dragging)
522 /* Already in the process of dragging. Set the offset. */
523 _map_offset_devx = x - _cmenu_position_x;
524 _map_offset_devy = y - _cmenu_position_y;
525 MACRO_QUEUE_DRAW_AREA();
529 gint diffx = x - _cmenu_position_x - _map_offset_devx;
530 gint diffy = y - _cmenu_position_y - _map_offset_devy;
531 if(diffx * diffx + diffy + diffy > 100)
533 _mouse_is_dragging = TRUE;
537 /* Return FALSE to allow context menu to work. */
538 vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
543 map_cb_button_press(GtkWidget *widget, GdkEventButton *event)
545 printf("%s()\n", __PRETTY_FUNCTION__);
549 _cmenu_position_x = event->x + 0.5 - _map_offset_devx;
550 _cmenu_position_y = event->y + 0.5 - _map_offset_devy;
551 g_mutex_lock(_mouse_mutex);
552 _mouse_is_down = TRUE;
555 /* Return FALSE to allow context menu to work. */
556 vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
561 map_cb_button_release(GtkWidget *widget, GdkEventButton *event)
563 printf("%s()\n", __PRETTY_FUNCTION__);
567 /* We didn't know about it anyway. Return FALSE. */
568 vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
572 _mouse_is_down = FALSE;
574 if(event->button == 3) /* right-click */
576 gtk_menu_popup(_map_cmenu, NULL, NULL, NULL, NULL,
577 event->button, event->time);
581 if(event->button == 2) /* middle-click */
583 screen2unit((gint)(event->x + 0.5), (gint)(event->y + 0.5),
584 _pos.unitx, _pos.unity);
585 unit2latlon(_pos.unitx, _pos.unity, _gps.lat, _gps.lon);
586 /* Test unit-to-lat/lon conversion. */
587 latlon2unit(_gps.lat, _gps.lon, _pos.unitx, _pos.unity);
588 _gps.speed = ((gint)(_gps.speed + 5) % 60);
589 _gps.heading = ((gint)(_gps.heading + 5) % 360);
590 if(track_add(time(NULL), FALSE))
592 /* Move mark to new location. */
593 map_refresh_mark(FALSE);
602 if(_mouse_is_dragging)
606 _mouse_is_dragging = FALSE;
608 screen2unit(_view_halfwidth_pixels, _view_halfheight_pixels,
609 clkpt.unitx, clkpt.unity);
614 gint mvpt_unitx, mvpt_unity;
615 gfloat new_offset_unitx, new_offset_unity;
616 /* If auto-center is enabled (meaning that panning will cause the
617 * view the rotate according to the heading), rotate the new
618 * unitx/unity around the mvpt, by any changes in _gps.heading
619 * since the last time _map_rotate_angle was updated. The end
620 * result is that the point the user is moving stays in the same
621 * relative point, even if the heading has since changed. */
622 gdk_pixbuf_rotate_matrix_fill_for_rotation(matrix,
623 deg2rad(_gps.heading - _next_map_rotate_angle));
624 buf2unit(_cmenu_position_x, _cmenu_position_y,
625 mvpt_unitx, mvpt_unity);
626 gdk_pixbuf_rotate_vector(&new_offset_unitx, &new_offset_unity,
628 (gint)(clkpt.unitx - mvpt_unitx),
629 (gint)(clkpt.unity -mvpt_unity));
630 clkpt.unitx = mvpt_unitx + new_offset_unitx;
631 clkpt.unity = mvpt_unity + new_offset_unity;
635 gtk_check_menu_item_set_active(
636 GTK_CHECK_MENU_ITEM(_menu_view_ac_none_item), TRUE);
638 map_center_unit(clkpt);
642 gboolean selected_point = FALSE;
644 screen2unit(event->x, event->y, clkpt.unitx, clkpt.unity);
647 gboolean aprs_found = FALSE;
649 if(_aprs_enable && (_poi_zoom > _zoom) )
652 selected_point = select_aprs(
653 clkpt.unitx, clkpt.unity, TRUE);
655 aprs_found = selected_point;
658 if(!aprs_found && _show_poi && (_poi_zoom > _zoom))
660 if(_show_poi && (_poi_zoom > _zoom))
661 #endif // INCLUDE_APRS
664 selected_point = select_poi(
665 clkpt.unitx, clkpt.unity, &poi, TRUE);
669 banner = g_strdup_printf("%s (%s)", poi.label, poi.clabel);
670 MACRO_BANNER_SHOW_INFO(_window, banner);
677 if(!selected_point && (_show_paths & ROUTES_MASK)
678 && _route.whead <= _route.wtail)
680 WayPoint *way = find_nearest_waypoint(
681 clkpt.unitx, clkpt.unity);
684 selected_point = TRUE;
685 MACRO_BANNER_SHOW_INFO(_window, way->desc);
691 gtk_check_menu_item_set_active(
692 GTK_CHECK_MENU_ITEM(_menu_view_ac_none_item), TRUE);
693 map_center_unit_full(clkpt, _next_zoom,
694 _center_mode > 0 && _center_rotate
695 ? _gps.heading : _next_map_rotate_angle);
699 g_mutex_unlock(_mouse_mutex);
701 /* Return FALSE to avoid context menu (if it hasn't popped up already). */
702 vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
709 printf("%s():\n", __PRETTY_FUNCTION__);
711 g_signal_connect(G_OBJECT(_window), "key_press_event",
712 G_CALLBACK(window_cb_key_press), NULL);
714 g_signal_connect(G_OBJECT(_window), "key_release_event",
715 G_CALLBACK(window_cb_key_release), NULL);
717 g_signal_connect(G_OBJECT(_map_widget), "motion_notify_event",
718 G_CALLBACK(map_cb_motion_notify), NULL);
720 g_signal_connect(G_OBJECT(_map_widget), "button_press_event",
721 G_CALLBACK(map_cb_button_press), NULL);
723 g_signal_connect(G_OBJECT(_map_widget), "button_release_event",
724 G_CALLBACK(map_cb_button_release), NULL);
726 gtk_widget_add_events(_map_widget,
728 | GDK_POINTER_MOTION_MASK
729 | GDK_POINTER_MOTION_HINT_MASK
730 | GDK_BUTTON_PRESS_MASK
731 | GDK_BUTTON_RELEASE_MASK);
733 vprintf("%s(): return\n", __PRETTY_FUNCTION__);