/* * Copyright (C) 2006, 2007 John Costigan. * * POI and GPS-Info code originally written by Cezary Jackiewicz. * * Default map data provided by http://www.openstreetmap.org/ * * This file is part of Maemo Mapper. * * Maemo Mapper is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Maemo Mapper is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Maemo Mapper. If not, see . */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #define _GNU_SOURCE #include #include #ifndef LEGACY # include # include #else # include # include #endif #include "types.h" #include "data.h" #include "defines.h" #include "display.h" #include "gdk-pixbuf-rotate.h" #include "gps.h" #include "input.h" #include "maps.h" #include "path.h" #include "poi.h" #include "util.h" static gint _key_zoom_is_down = FALSE; static gint _key_zoom_new = -1; static gint _key_zoom_timeout_sid = 0; static gint _key_pan_is_down = FALSE; static gfloat _key_pan_incr_devx = 0; static gfloat _key_pan_incr_devy = 0; static gint _key_pan_timeout_sid = 0; static gboolean key_pan_timeout(CustomAction action) { printf("%s()\n", __PRETTY_FUNCTION__); if(_key_pan_is_down) { /* The pan key is still down. Continue panning. */ _map_offset_devx += -_key_pan_incr_devx; _map_offset_devy += -_key_pan_incr_devy; MACRO_QUEUE_DRAW_AREA(); vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } else { gfloat panx_adj, pany_adj; /* Time is up for further action - execute the pan. */ /* Adjust for rotate angle. */ gdk_pixbuf_rotate_vector(&panx_adj, &pany_adj, _map_reverse_matrix, _map_offset_devx, _map_offset_devy); map_pan(-pixel2unit((gint)(panx_adj + 0.5f)), -pixel2unit((gint)(pany_adj + 0.5f))); _key_pan_timeout_sid = 0; vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__); return FALSE; } } static gboolean key_zoom_timeout(CustomAction action) { printf("%s()\n", __PRETTY_FUNCTION__); if(_key_zoom_is_down) { /* The zoom key is still down. Continue zooming. */ if(action == CUSTOM_ACTION_ZOOM_IN) { /* We're currently zooming in (_zoom is decreasing). */ gint test = _key_zoom_new - _curr_repo->view_zoom_steps; if(test >= 0) /* We can zoom some more. Hurray! */ _key_zoom_new = test; } else { /* We're currently zooming out (_zoom is increasing). */ gint test = _key_zoom_new + _curr_repo->view_zoom_steps; if(test <= MAX_ZOOM) /* We can zoom some more. Hurray! */ _key_zoom_new = test; } /* Tell them where they're about to zoom. */ { gchar buffer[32]; snprintf(buffer, sizeof(buffer), "%s %d", _("Zoom to Level"), _key_zoom_new); MACRO_BANNER_SHOW_INFO(_window, buffer); } vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } else { /* Time is up for further action - execute. */ if(_key_zoom_new != _zoom) map_set_zoom(_key_zoom_new); _key_pan_timeout_sid = 0; _key_zoom_new = -1; vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__); return FALSE; } } static CustomKey get_custom_key_from_keyval(gint keyval) { CustomKey custom_key; printf("%s(%d)\n", __PRETTY_FUNCTION__, keyval); switch(keyval) { case HILDON_HARDKEY_UP: custom_key = CUSTOM_KEY_UP; break; case HILDON_HARDKEY_DOWN: custom_key = CUSTOM_KEY_DOWN; break; case HILDON_HARDKEY_LEFT: custom_key = CUSTOM_KEY_LEFT; break; case HILDON_HARDKEY_RIGHT: custom_key = CUSTOM_KEY_RIGHT; break; case HILDON_HARDKEY_SELECT: custom_key = CUSTOM_KEY_SELECT; break; case HILDON_HARDKEY_INCREASE: custom_key = CUSTOM_KEY_INCREASE; break; case HILDON_HARDKEY_DECREASE: custom_key = CUSTOM_KEY_DECREASE; break; case HILDON_HARDKEY_FULLSCREEN: custom_key = CUSTOM_KEY_FULLSCREEN; break; case HILDON_HARDKEY_ESC: custom_key = CUSTOM_KEY_ESC; break; default: custom_key = -1; } vprintf("%s(): return %d\n", __PRETTY_FUNCTION__, custom_key); return custom_key; } gboolean window_cb_key_press(GtkWidget* widget, GdkEventKey *event) { CustomKey custom_key; printf("%s()\n", __PRETTY_FUNCTION__); custom_key = get_custom_key_from_keyval(event->keyval); if(custom_key == -1) return FALSE; /* Not our event. */ switch(_action[custom_key]) { case CUSTOM_ACTION_PAN_NORTH: case CUSTOM_ACTION_PAN_SOUTH: case CUSTOM_ACTION_PAN_EAST: case CUSTOM_ACTION_PAN_WEST: case CUSTOM_ACTION_PAN_UP: case CUSTOM_ACTION_PAN_DOWN: case CUSTOM_ACTION_PAN_LEFT: case CUSTOM_ACTION_PAN_RIGHT: if(!_key_pan_is_down) { _key_pan_is_down = TRUE; if(_center_mode > 0) gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM( _menu_view_ac_none_item), FALSE); if(_key_pan_timeout_sid) { g_source_remove(_key_pan_timeout_sid); _key_pan_timeout_sid = 0; } /* Figure out new pan. */ switch(_action[custom_key]) { case CUSTOM_ACTION_PAN_UP: case CUSTOM_ACTION_PAN_NORTH: _key_pan_incr_devy = -PAN_PIXELS; break; case CUSTOM_ACTION_PAN_SOUTH: case CUSTOM_ACTION_PAN_DOWN: _key_pan_incr_devy = PAN_PIXELS; break; case CUSTOM_ACTION_PAN_EAST: case CUSTOM_ACTION_PAN_RIGHT: _key_pan_incr_devx = PAN_PIXELS; break; case CUSTOM_ACTION_PAN_WEST: case CUSTOM_ACTION_PAN_LEFT: _key_pan_incr_devx = -PAN_PIXELS; break; default: g_printerr("Invalid action in key_pan_timeout(): %d\n", _action[custom_key]); } switch(_action[custom_key]) { case CUSTOM_ACTION_PAN_NORTH: case CUSTOM_ACTION_PAN_SOUTH: case CUSTOM_ACTION_PAN_EAST: case CUSTOM_ACTION_PAN_WEST: /* Adjust for rotate angle. */ gdk_pixbuf_rotate_vector(&_key_pan_incr_devx, &_key_pan_incr_devy, _map_rotate_matrix, _key_pan_incr_devx, _key_pan_incr_devy); default: ; } key_pan_timeout(_action[custom_key]); _key_pan_timeout_sid = g_timeout_add_full(G_PRIORITY_HIGH_IDLE, 250, (GSourceFunc)key_pan_timeout, (gpointer)(_action[custom_key]), NULL); } break; case CUSTOM_ACTION_RESET_VIEW_ANGLE: map_rotate(-_next_map_rotate_angle); break; case CUSTOM_ACTION_ROTATE_CLOCKWISE: { map_rotate(-ROTATE_DEGREES); break; } case CUSTOM_ACTION_ROTATE_COUNTERCLOCKWISE: { map_rotate(ROTATE_DEGREES); break; } case CUSTOM_ACTION_TOGGLE_AUTOROTATE: gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM( _menu_view_rotate_auto_item), !_center_rotate); break; case CUSTOM_ACTION_TOGGLE_AUTOCENTER: switch(_center_mode) { case CENTER_LATLON: case CENTER_WAS_LEAD: gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM( _menu_view_ac_lead_item), TRUE); break; case CENTER_LEAD: case CENTER_WAS_LATLON: gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM( _menu_view_ac_latlon_item), TRUE); break; } break; case CUSTOM_ACTION_ZOOM_IN: case CUSTOM_ACTION_ZOOM_OUT: if(!_key_zoom_is_down) { _key_zoom_is_down = TRUE; if(_key_zoom_timeout_sid) { g_source_remove(_key_zoom_timeout_sid); _key_zoom_timeout_sid = 0; } if(_key_zoom_new == -1) _key_zoom_new = _next_zoom; _key_zoom_new = _key_zoom_new + (_action[custom_key] == CUSTOM_ACTION_ZOOM_IN ? -_curr_repo->view_zoom_steps : _curr_repo->view_zoom_steps); BOUND(_key_zoom_new, 0, MAX_ZOOM); { gchar buffer[80]; snprintf(buffer, sizeof(buffer),"%s %d", _("Zoom to Level"), _key_zoom_new); MACRO_BANNER_SHOW_INFO(_window, buffer); } _key_zoom_timeout_sid =g_timeout_add_full(G_PRIORITY_HIGH_IDLE, 500, (GSourceFunc)key_zoom_timeout, (gpointer)(_action[custom_key]), NULL); } break; case CUSTOM_ACTION_TOGGLE_FULLSCREEN: gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM( _menu_view_fullscreen_item), !_fullscreen); break; case CUSTOM_ACTION_TOGGLE_TRACKING: gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(_menu_track_enable_tracking_item), !_enable_tracking); break; case CUSTOM_ACTION_TOGGLE_TRACKS: switch(_show_paths) { case 0: /* Nothing shown, nothing saved; just set both. */ _show_paths = TRACKS_MASK | ROUTES_MASK; break; case TRACKS_MASK << 16: case ROUTES_MASK << 16: case (ROUTES_MASK | TRACKS_MASK) << 16: /* Something was saved and nothing changed since. * Restore saved. */ _show_paths = _show_paths >> 16; break; default: /* There is no history, or they changed something * since the last historical change. Save and * clear. */ _show_paths = _show_paths << 16; } gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(_menu_view_show_routes_item), _show_paths & ROUTES_MASK); gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(_menu_view_show_tracks_item), _show_paths & TRACKS_MASK); case CUSTOM_ACTION_TOGGLE_SCALE: gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(_menu_view_show_scale_item), !_show_scale); break; case CUSTOM_ACTION_TOGGLE_POI: gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(_menu_view_show_poi_item), !_show_poi); break; case CUSTOM_ACTION_CHANGE_REPO: { GList *curr = g_list_find(_repo_list, _curr_repo); if(!curr) break; /* Loop until we reach a next-able repo, or until we get * back to the current repo. */ while((curr = (curr->next ? curr->next : _repo_list)) && !((RepoData*)curr->data)->nextable && curr->data != _curr_repo) { } if(curr->data != _curr_repo) { repo_set_curr(curr->data); gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(_curr_repo->menu_item), TRUE); } else { popup_error(_window, _("There are no other next-able repositories.")); } break; } case CUSTOM_ACTION_RESET_BLUETOOTH: reset_bluetooth(); break; case CUSTOM_ACTION_ROUTE_DISTNEXT: route_show_distance_to_next(); break; case CUSTOM_ACTION_ROUTE_DISTLAST: route_show_distance_to_last(); break; case CUSTOM_ACTION_TRACK_BREAK: track_insert_break(TRUE); break; case CUSTOM_ACTION_TRACK_CLEAR: track_clear(); break; case CUSTOM_ACTION_TRACK_DISTLAST: track_show_distance_from_last(); break; case CUSTOM_ACTION_TRACK_DISTFIRST: track_show_distance_from_first(); break; case CUSTOM_ACTION_TOGGLE_GPS: gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(_menu_gps_enable_item), !_enable_gps); break; case CUSTOM_ACTION_TOGGLE_GPSINFO: gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(_menu_gps_show_info_item), !_gps_info); break; case CUSTOM_ACTION_TOGGLE_SPEEDLIMIT: _speed_limit_on ^= 1; break; case CUSTOM_ACTION_TOGGLE_LAYERS: maps_toggle_visible_layers (); break; default: vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__); return FALSE; } return TRUE; vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); } gboolean window_cb_key_release(GtkWidget* widget, GdkEventKey *event) { CustomKey custom_key; printf("%s()\n", __PRETTY_FUNCTION__); custom_key = get_custom_key_from_keyval(event->keyval); if(custom_key == -1) return FALSE; /* Not our event. */ switch(_action[custom_key]) { case CUSTOM_ACTION_ZOOM_IN: case CUSTOM_ACTION_ZOOM_OUT: if(_key_zoom_timeout_sid) { g_source_remove(_key_zoom_timeout_sid); _key_zoom_timeout_sid = 0; _key_zoom_timeout_sid =g_timeout_add_full(G_PRIORITY_HIGH_IDLE, 500, (GSourceFunc)key_zoom_timeout, NULL, NULL); } _key_zoom_is_down = FALSE; return TRUE; case CUSTOM_ACTION_PAN_NORTH: case CUSTOM_ACTION_PAN_SOUTH: case CUSTOM_ACTION_PAN_EAST: case CUSTOM_ACTION_PAN_WEST: case CUSTOM_ACTION_PAN_UP: case CUSTOM_ACTION_PAN_DOWN: case CUSTOM_ACTION_PAN_LEFT: case CUSTOM_ACTION_PAN_RIGHT: if(_key_pan_timeout_sid) { g_source_remove(_key_pan_timeout_sid); _key_pan_timeout_sid = g_timeout_add_full(G_PRIORITY_HIGH_IDLE, 500, (GSourceFunc)key_pan_timeout, NULL, NULL); } _key_pan_is_down = FALSE; _key_pan_incr_devx = 0; _key_pan_incr_devy = 0; return TRUE; default: return FALSE; } vprintf("%s(): return\n", __PRETTY_FUNCTION__); } gboolean map_cb_motion_notify(GtkWidget *widget, GdkEventMotion *event) { gint x, y; GdkModifierType state; printf("%s()\n", __PRETTY_FUNCTION__); if(!_mouse_is_down) return FALSE; if(event->is_hint) gdk_window_get_pointer(event->window, &x, &y, &state); else { x = event->x; y = event->y; state = event->state; } if(_mouse_is_dragging) { /* Already in the process of dragging. Set the offset. */ _map_offset_devx = x - _cmenu_position_x; _map_offset_devy = y - _cmenu_position_y; MACRO_QUEUE_DRAW_AREA(); } else { gint diffx = x - _cmenu_position_x - _map_offset_devx; gint diffy = y - _cmenu_position_y - _map_offset_devy; if(diffx * diffx + diffy + diffy > 100) { _mouse_is_dragging = TRUE; } } /* Return FALSE to allow context menu to work. */ vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__); return TRUE; } gboolean map_cb_button_press(GtkWidget *widget, GdkEventButton *event) { printf("%s()\n", __PRETTY_FUNCTION__); if(!_mouse_is_down) { _cmenu_position_x = event->x + 0.5 - _map_offset_devx; _cmenu_position_y = event->y + 0.5 - _map_offset_devy; g_mutex_lock(_mouse_mutex); _mouse_is_down = TRUE; } /* Return FALSE to allow context menu to work. */ vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__); return FALSE; } gboolean map_cb_button_release(GtkWidget *widget, GdkEventButton *event) { printf("%s()\n", __PRETTY_FUNCTION__); if(!_mouse_is_down) { /* We didn't know about it anyway. Return FALSE. */ vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__); return FALSE; } _mouse_is_down = FALSE; if(event->button == 3) /* right-click */ { gtk_menu_popup(_map_cmenu, NULL, NULL, NULL, NULL, event->button, event->time); } else #ifdef DEBUG if(event->button == 2) /* middle-click */ { screen2unit((gint)(event->x + 0.5), (gint)(event->y + 0.5), _pos.unitx, _pos.unity); unit2latlon(_pos.unitx, _pos.unity, _gps.lat, _gps.lon); /* Test unit-to-lat/lon conversion. */ latlon2unit(_gps.lat, _gps.lon, _pos.unitx, _pos.unity); _gps.speed = ((gint)(_gps.speed + 5) % 60); _gps.heading = ((gint)(_gps.heading + 5) % 360); if(track_add(time(NULL), FALSE)) { /* Move mark to new location. */ map_refresh_mark(FALSE); } else { map_move_mark(); } } else #endif if(_mouse_is_dragging) { Point clkpt; _mouse_is_dragging = FALSE; screen2unit(_view_halfwidth_pixels, _view_halfheight_pixels, clkpt.unitx, clkpt.unity); if(_center_mode > 0) { gfloat matrix[4]; gint mvpt_unitx, mvpt_unity; gfloat new_offset_unitx, new_offset_unity; /* If auto-center is enabled (meaning that panning will cause the * view the rotate according to the heading), rotate the new * unitx/unity around the mvpt, by any changes in _gps.heading * since the last time _map_rotate_angle was updated. The end * result is that the point the user is moving stays in the same * relative point, even if the heading has since changed. */ gdk_pixbuf_rotate_matrix_fill_for_rotation(matrix, deg2rad(_gps.heading - _next_map_rotate_angle)); buf2unit(_cmenu_position_x, _cmenu_position_y, mvpt_unitx, mvpt_unity); gdk_pixbuf_rotate_vector(&new_offset_unitx, &new_offset_unity, matrix, (gint)(clkpt.unitx - mvpt_unitx), (gint)(clkpt.unity -mvpt_unity)); clkpt.unitx = mvpt_unitx + new_offset_unitx; clkpt.unity = mvpt_unity + new_offset_unity; } if(_center_mode > 0) gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(_menu_view_ac_none_item), TRUE); map_center_unit(clkpt); } else { gboolean selected_point = FALSE; Point clkpt; screen2unit(event->x, event->y, clkpt.unitx, clkpt.unity); #ifdef INCLUDE_APRS gboolean aprs_found = FALSE; if(_aprs_enable && (_poi_zoom > _zoom) ) { AprsDisplayData poi; selected_point = select_aprs( clkpt.unitx, clkpt.unity, TRUE); aprs_found = selected_point; } if(!aprs_found && _show_poi && (_poi_zoom > _zoom)) #else if(_show_poi && (_poi_zoom > _zoom)) #endif // INCLUDE_APRS { PoiInfo poi; selected_point = select_poi( clkpt.unitx, clkpt.unity, &poi, TRUE); if(selected_point) { gchar *banner; banner = g_strdup_printf("%s (%s)", poi.label, poi.clabel); MACRO_BANNER_SHOW_INFO(_window, banner); g_free(banner); g_free(poi.label); g_free(poi.desc); g_free(poi.clabel); } } if(!selected_point && (_show_paths & ROUTES_MASK) && _route.whead <= _route.wtail) { WayPoint *way = find_nearest_waypoint( clkpt.unitx, clkpt.unity); if(way) { selected_point = TRUE; MACRO_BANNER_SHOW_INFO(_window, way->desc); } } if(!selected_point) { if(_center_mode > 0) gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(_menu_view_ac_none_item), TRUE); map_center_unit_full(clkpt, _next_zoom, _center_mode > 0 && _center_rotate ? _gps.heading : _next_map_rotate_angle); } } g_mutex_unlock(_mouse_mutex); /* Return FALSE to avoid context menu (if it hasn't popped up already). */ vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__); return FALSE; } void input_init() { printf("%s():\n", __PRETTY_FUNCTION__); g_signal_connect(G_OBJECT(_window), "key_press_event", G_CALLBACK(window_cb_key_press), NULL); g_signal_connect(G_OBJECT(_window), "key_release_event", G_CALLBACK(window_cb_key_release), NULL); g_signal_connect(G_OBJECT(_map_widget), "motion_notify_event", G_CALLBACK(map_cb_motion_notify), NULL); g_signal_connect(G_OBJECT(_map_widget), "button_press_event", G_CALLBACK(map_cb_button_press), NULL); g_signal_connect(G_OBJECT(_map_widget), "button_release_event", G_CALLBACK(map_cb_button_release), NULL); gtk_widget_add_events(_map_widget, GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); vprintf("%s(): return\n", __PRETTY_FUNCTION__); }