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;
440 vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
445 vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
449 window_cb_key_release(GtkWidget* widget, GdkEventKey *event)
451 CustomKey custom_key;
452 printf("%s()\n", __PRETTY_FUNCTION__);
454 custom_key = get_custom_key_from_keyval(event->keyval);
456 return FALSE; /* Not our event. */
458 switch(_action[custom_key])
460 case CUSTOM_ACTION_ZOOM_IN:
461 case CUSTOM_ACTION_ZOOM_OUT:
462 if(_key_zoom_timeout_sid)
464 g_source_remove(_key_zoom_timeout_sid);
465 _key_zoom_timeout_sid = 0;
466 _key_zoom_timeout_sid =g_timeout_add_full(G_PRIORITY_HIGH_IDLE,
467 500, (GSourceFunc)key_zoom_timeout, NULL, NULL);
469 _key_zoom_is_down = FALSE;
472 case CUSTOM_ACTION_PAN_NORTH:
473 case CUSTOM_ACTION_PAN_SOUTH:
474 case CUSTOM_ACTION_PAN_EAST:
475 case CUSTOM_ACTION_PAN_WEST:
476 case CUSTOM_ACTION_PAN_UP:
477 case CUSTOM_ACTION_PAN_DOWN:
478 case CUSTOM_ACTION_PAN_LEFT:
479 case CUSTOM_ACTION_PAN_RIGHT:
480 if(_key_pan_timeout_sid)
482 g_source_remove(_key_pan_timeout_sid);
483 _key_pan_timeout_sid = g_timeout_add_full(G_PRIORITY_HIGH_IDLE,
484 500, (GSourceFunc)key_pan_timeout, NULL, NULL);
486 _key_pan_is_down = FALSE;
487 _key_pan_incr_devx = 0;
488 _key_pan_incr_devy = 0;
494 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
498 map_cb_motion_notify(GtkWidget *widget, GdkEventMotion *event)
501 GdkModifierType state;
502 printf("%s()\n", __PRETTY_FUNCTION__);
508 gdk_window_get_pointer(event->window, &x, &y, &state);
513 state = event->state;
516 if(_mouse_is_dragging)
518 /* Already in the process of dragging. Set the offset. */
519 _map_offset_devx = x - _cmenu_position_x;
520 _map_offset_devy = y - _cmenu_position_y;
521 MACRO_QUEUE_DRAW_AREA();
525 gint diffx = x - _cmenu_position_x - _map_offset_devx;
526 gint diffy = y - _cmenu_position_y - _map_offset_devy;
527 if(diffx * diffx + diffy + diffy > 100)
529 _mouse_is_dragging = TRUE;
533 /* Return FALSE to allow context menu to work. */
534 vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
539 map_cb_button_press(GtkWidget *widget, GdkEventButton *event)
541 printf("%s()\n", __PRETTY_FUNCTION__);
545 _cmenu_position_x = event->x + 0.5 - _map_offset_devx;
546 _cmenu_position_y = event->y + 0.5 - _map_offset_devy;
547 g_mutex_lock(_mouse_mutex);
548 _mouse_is_down = TRUE;
551 /* Return FALSE to allow context menu to work. */
552 vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
557 map_cb_button_release(GtkWidget *widget, GdkEventButton *event)
559 printf("%s()\n", __PRETTY_FUNCTION__);
563 /* We didn't know about it anyway. Return FALSE. */
564 vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
568 _mouse_is_down = FALSE;
571 if(event->button != 1)
573 screen2unit((gint)(event->x + 0.5), (gint)(event->y + 0.5),
574 _pos.unitx, _pos.unity);
575 unit2latlon(_pos.unitx, _pos.unity, _gps.lat, _gps.lon);
576 /* Test unit-to-lat/lon conversion. */
577 latlon2unit(_gps.lat, _gps.lon, _pos.unitx, _pos.unity);
578 _gps.speed = ((gint)(_gps.speed + 5) % 60);
579 _gps.heading = ((gint)(_gps.heading + 5) % 360);
580 if(track_add(time(NULL), FALSE))
582 /* Move mark to new location. */
583 map_refresh_mark(FALSE);
592 if(_mouse_is_dragging)
596 _mouse_is_dragging = FALSE;
598 screen2unit(_view_halfwidth_pixels, _view_halfheight_pixels,
599 clkpt.unitx, clkpt.unity);
604 gint mvpt_unitx, mvpt_unity;
605 gfloat new_offset_unitx, new_offset_unity;
606 /* If auto-center is enabled (meaning that panning will cause the
607 * view the rotate according to the heading), rotate the new
608 * unitx/unity around the mvpt, by any changes in _gps.heading
609 * since the last time _map_rotate_angle was updated. The end
610 * result is that the point the user is moving stays in the same
611 * relative point, even if the heading has since changed. */
612 gdk_pixbuf_rotate_matrix_fill_for_rotation(matrix,
613 deg2rad(_gps.heading - _next_map_rotate_angle));
614 buf2unit(_cmenu_position_x, _cmenu_position_y,
615 mvpt_unitx, mvpt_unity);
616 gdk_pixbuf_rotate_vector(&new_offset_unitx, &new_offset_unity,
618 (gint)(clkpt.unitx - mvpt_unitx),
619 (gint)(clkpt.unity -mvpt_unity));
620 clkpt.unitx = mvpt_unitx + new_offset_unitx;
621 clkpt.unity = mvpt_unity + new_offset_unity;
625 gtk_check_menu_item_set_active(
626 GTK_CHECK_MENU_ITEM(_menu_view_ac_none_item), TRUE);
628 map_center_unit(clkpt);
632 gboolean selected_point = FALSE;
634 screen2unit(event->x, event->y, clkpt.unitx, clkpt.unity);
636 if(_show_poi && (_poi_zoom > _zoom))
639 selected_point = select_poi(
640 clkpt.unitx, clkpt.unity, &poi, TRUE);
644 banner = g_strdup_printf("%s (%s)", poi.label, poi.clabel);
645 MACRO_BANNER_SHOW_INFO(_window, banner);
652 if(!selected_point && (_show_paths & ROUTES_MASK)
653 && _route.whead <= _route.wtail)
655 WayPoint *way = find_nearest_waypoint(
656 clkpt.unitx, clkpt.unity);
659 selected_point = TRUE;
660 MACRO_BANNER_SHOW_INFO(_window, way->desc);
666 gtk_check_menu_item_set_active(
667 GTK_CHECK_MENU_ITEM(_menu_view_ac_none_item), TRUE);
668 map_center_unit_full(clkpt, _next_zoom,
669 _center_mode > 0 && _center_rotate
670 ? _gps.heading : _next_map_rotate_angle);
674 g_mutex_unlock(_mouse_mutex);
676 /* Return FALSE to avoid context menu (if it hasn't popped up already). */
677 vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
684 printf("%s():\n", __PRETTY_FUNCTION__);
686 g_signal_connect(G_OBJECT(_window), "key_press_event",
687 G_CALLBACK(window_cb_key_press), NULL);
689 g_signal_connect(G_OBJECT(_window), "key_release_event",
690 G_CALLBACK(window_cb_key_release), NULL);
692 g_signal_connect(G_OBJECT(_map_widget), "motion_notify_event",
693 G_CALLBACK(map_cb_motion_notify), NULL);
695 g_signal_connect(G_OBJECT(_map_widget), "button_press_event",
696 G_CALLBACK(map_cb_button_press), NULL);
698 g_signal_connect(G_OBJECT(_map_widget), "button_release_event",
699 G_CALLBACK(map_cb_button_release), NULL);
701 gtk_widget_add_events(_map_widget,
703 | GDK_POINTER_MOTION_MASK
704 | GDK_POINTER_MOTION_HINT_MASK
705 | GDK_BUTTON_PRESS_MASK
706 | GDK_BUTTON_RELEASE_MASK);
708 vprintf("%s(): return\n", __PRETTY_FUNCTION__);