/** * Navit, a modular navigation system. * Copyright (C) 2005-2009 Navit Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program 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 this program; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #define _USE_MATH_DEFINES 1 #include "config.h" #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include #include #include #include #include #include "debug.h" #include "navit.h" #include "callback.h" #include "gui.h" #include "item.h" #include "xmlconfig.h" #include "projection.h" #include "map.h" #include "mapset.h" #include "main.h" #include "coord.h" #include "point.h" #include "transform.h" #include "traffic.h" #include "param.h" #include "menu.h" #include "graphics.h" #include "popup.h" #include "data_window.h" #include "route.h" #include "navigation.h" #include "speech.h" #include "track.h" #include "vehicle.h" #include "layout.h" #include "log.h" #include "attr.h" #include "event.h" #include "file.h" #include "profile.h" #include "command.h" #include "navit_nls.h" #include "map.h" #include "util.h" #include "messages.h" #include "vehicleprofile.h" #include "sunriset.h" #include "bookmarks.h" #ifdef HAVE_API_WIN32_BASE #include #include "util.h" #endif #ifdef HAVE_API_WIN32_CE #include "libc.h" #endif /* define string for bookmark handling */ #define TEXTFILE_COMMENT_NAVI_STOPPED "# navigation stopped\n" /** * @defgroup navit The navit core instance * @brief navit is the object containing most global data structures. * * Among others: * - a set of maps * - one or more vehicles * - a graphics object for rendering the map * - a gui object for displaying the user interface * - a route object * - a navigation object * @{ */ //! The vehicle used for navigation. struct navit_vehicle { int follow; /*! Limit of the follow counter. See navit_add_vehicle */ int follow_curr; /*! Deprecated : follow counter itself. When it reaches 'update' counts, map is recentered*/ struct coord coord; int dir; int speed; struct coord last; /*< Position of the last update of this vehicle */ struct vehicle *vehicle; struct attr callback; int animate_cursor; }; struct navit { NAVIT_OBJECT struct attr self; GList *mapsets; GList *layouts; struct gui *gui; char *default_layout_name; /*!< The default layout indicated by the config file (if any) */ struct layout *layout_current; /*!< The current layout theme used to display the map */ struct graphics *gra; struct action *action; struct transformation *trans, *trans_cursor; struct compass *compass; struct route *route; struct navigation *navigation; struct speech *speech; struct tracking *tracking; int ready; struct window *win; struct displaylist *displaylist; int tracking_flag; int orientation; int recentdest_count; int osd_configuration; GList *vehicles; GList *windows_items; struct navit_vehicle *vehicle; struct callback_list *attr_cbl; struct callback *nav_speech_cb, *roadbook_callback, *popup_callback, *route_cb, *progress_cb; struct datawindow *roadbook_window; struct map *former_destination; struct point pressed, last, current; int button_pressed,moved,popped,zoomed; int center_timeout; int autozoom_secs; int autozoom_min; int autozoom_max; int autozoom_active; int autozoom_paused; struct event_timeout *button_timeout, *motion_timeout; struct callback *motion_timeout_callback; int ignore_button; int ignore_graphics_events; struct log *textfile_debug_log; struct pcoord destination; int destination_valid; int blocked; /**< Whether draw operations are currently blocked. This can be a combination of the following flags: 1: draw operations are blocked 2: draw operations are pending, requiring a redraw once draw operations are unblocked */ int w,h; int drag_bitmap; int use_mousewheel; struct messagelist *messages; struct callback *resize_callback,*button_callback,*motion_callback,*predraw_callback; struct vehicleprofile *vehicleprofile; GList *vehicleprofiles; int pitch; int follow_cursor; int prevTs; int graphics_flags; int zoom_min, zoom_max; int radius; struct bookmarks *bookmarks; int flags; /* 1=No graphics ok */ /* 2=No gui ok */ int border; int imperial; int waypoints_flag; struct coord_geo center; int auto_switch; /*auto switching between day/night layout enabled ?*/ int tunnel_nightlayout; /* switch to nightlayout if we are in a tunnel? */ char* layout_before_tunnel; int sunrise_degrees; }; struct gui *main_loop_gui; struct attr_iter { void *iter; union { GList *list; struct mapset_handle *mapset_handle; } u; }; static void navit_vehicle_update_position(struct navit *this_, struct navit_vehicle *nv); static void navit_vehicle_draw(struct navit *this_, struct navit_vehicle *nv, struct point *pnt); static int navit_add_vehicle(struct navit *this_, struct vehicle *v); static int navit_set_attr_do(struct navit *this_, struct attr *attr, int init); static int navit_get_cursor_pnt(struct navit *this_, struct point *p, int keep_orientation, int *dir); static void navit_set_cursors(struct navit *this_); static int navit_cmd_zoom_to_route(struct navit *this, char *function, struct attr **in, struct attr ***out); static int navit_cmd_set_center_cursor(struct navit *this_, char *function, struct attr **in, struct attr ***out); static int navit_cmd_announcer_toggle(struct navit *this_, char *function, struct attr **in, struct attr ***out); static void navit_set_vehicle(struct navit *this_, struct navit_vehicle *nv); static int navit_set_vehicleprofile(struct navit *this_, struct vehicleprofile *vp); static int navit_cmd_switch_layout_day_night(struct navit *this_, char *function, struct attr **in, struct attr ***out); struct object_func navit_func; struct navit *global_navit; void navit_add_mapset(struct navit *this_, struct mapset *ms) { this_->mapsets = g_list_append(this_->mapsets, ms); } /** * @brief Get the current mapset * * @param this_ The navit instance * * @return A pointer to the current mapset */ struct mapset * navit_get_mapset(struct navit *this_) { if(this_->mapsets) { return this_->mapsets->data; } else { dbg(lvl_error,"No mapsets enabled! Is it on purpose? Navit can't draw a map. Please check your navit.xml"); } return NULL; } /** * @brief Get the search result map (and create it if it does not exist) * * @param this_ The navit instance * * @return A pointer to the map named "search_results" or NULL if there wasa failure */ struct map *navit_get_search_results_map(struct navit *this_) { struct mapset *ms; struct map *map; ms=navit_get_mapset(this_); if(!ms) return NULL; map=mapset_get_map_by_name(ms, "search_results"); if(!map) { struct attr *attrs[10], attrmap; enum attr_type types[]= {attr_position_longitude,attr_position_latitude,attr_label,attr_none}; int i; attrs[0]=g_new0(struct attr,1); attrs[0]->type=attr_type; attrs[0]->u.str="csv"; attrs[1]=g_new0(struct attr,1); attrs[1]->type=attr_name; attrs[1]->u.str="search_results"; attrs[2]=g_new0(struct attr,1); attrs[2]->type=attr_charset; attrs[2]->u.str="utf-8"; attrs[3]=g_new0(struct attr,1); attrs[3]->type=attr_item_type; attrs[3]->u.num=type_found_item; attrs[4]=g_new0(struct attr,1); attrs[4]->type=attr_attr_types; attrs[4]->u.attr_types=types; attrs[5]=NULL; attrmap.type=attr_map; map=attrmap.u.map=map_new(NULL,attrs); if(map) mapset_add_attr(ms,&attrmap); for(i=0; attrs[i]; i++) g_free(attrs[i]); } return map; } /** * @brief Populate a map containing one or more search result points * * These search results will be displayed as an overlay on the top of the geographic map. * * @warning Each call to this function will replace currently displayed results, it will not add to them * * @param this_ The navit instance * @param search_results A GList storing {@code struct lcoord} elements to display on the result map * If this argument in NULL, all existing results will be removed from the map * @param[in,out] coord_rect An optional rectangular zone that will be extended to contain all result points * or NULL if no zone needs to be computed * @return The number of results actually added to the map */ int navit_populate_search_results_map(struct navit *this_, GList *search_results, struct coord_rect *r) { struct map *map; struct map_rect *mr; struct item *item; GList *curr_result = search_results; int count; char *name_label; map = navit_get_search_results_map(this_); if(!map) return 0; mr = map_rect_new(map, NULL); if(!mr) return 0; /* Clean the map */ while((item = map_rect_get_item(mr))!=NULL) { item_type_set(item,type_none); } if(!search_results) { map_rect_destroy(mr); dbg(lvl_warning,"NULL result table - only map clean up is done."); return 0; } /* Populate the map with search results*/ for(curr_result = search_results, count=0; curr_result; curr_result=g_list_next(curr_result)) { struct lcoord *point = curr_result->data; struct item* it; if(point->label==NULL) continue; dbg(lvl_info,"%s",point->label); it=map_rect_create_item(mr,type_found_item); if(it) { struct attr a; item_coord_set(it, &(point->c), 1, change_mode_modify); a.type=attr_label; name_label = g_strdup(point->label); square_shape_str(name_label); a.u.str=name_label; item_attr_set(it, &a, change_mode_modify); if (r) { if(!count++) r->lu=r->rl=point->c; else coord_rect_extend(r,&(point->c)); } } } map_rect_destroy(mr); return count; } struct tracking * navit_get_tracking(struct navit *this_) { return this_->tracking; } /** * @brief Get the user data directory. * @param[in] create - create the directory if it does not exist * * @return char * to the data directory string. * * returns the directory used to store user data files (center.txt, * destination.txt, bookmark.txt, ...) * */ char* navit_get_user_data_directory(int create) { char *dir; dir = getenv("NAVIT_USER_DATADIR"); if (create && !file_exists(dir)) { dbg(lvl_debug,"creating dir %s", dir); if (file_mkdir(dir,1)) { dbg(lvl_error,"failed creating dir %s", dir); return NULL; } } return dir; } void navit_draw_async(struct navit *this_, int async) { if (this_->blocked) { this_->blocked |= 2; return; } transform_setup_source_rect(this_->trans); graphics_draw(this_->gra, this_->displaylist, this_->mapsets->data, this_->trans, this_->layout_current, async, NULL, this_->graphics_flags|1); } void navit_draw(struct navit *this_) { if (this_->ready == 3) navit_draw_async(this_, 0); } int navit_get_ready(struct navit *this_) { return this_->ready; } void navit_draw_displaylist(struct navit *this_) { if (this_->ready == 3) graphics_displaylist_draw(this_->gra, this_->displaylist, this_->trans, this_->layout_current, this_->graphics_flags|1); } static void navit_map_progress(struct navit *this_) { struct map *map; struct mapset *ms; struct mapset_handle *msh; struct attr attr; struct point p; if (this_->ready != 3) return; p.x=10; p.y=32; ms=this_->mapsets->data; msh=mapset_open(ms); while (msh && (map=mapset_next(msh, 0))) { if (map_get_attr(map, attr_progress, &attr, NULL)) { char *str=g_strdup_printf("%s ",attr.u.str); graphics_draw_mode(this_->gra, draw_mode_begin); graphics_draw_text_std(this_->gra, 16, str, &p); g_free(str); p.y+=32; graphics_draw_mode(this_->gra, draw_mode_end); } } mapset_close(msh); } static void navit_redraw_route(struct navit *this_, struct route *route, struct attr *attr) { int updated; if (attr->type != attr_route_status) return; updated=attr->u.num; if (this_->ready != 3) return; if (updated != route_status_path_done_new) return; if (this_->vehicle) { if (this_->vehicle->follow_curr == 1) return; if (this_->vehicle->follow_curr <= this_->vehicle->follow) this_->vehicle->follow_curr=this_->vehicle->follow; } navit_draw(this_); } void navit_handle_resize(struct navit *this_, int w, int h) { struct map_selection sel; int callback=(this_->ready == 1); this_->ready |= 2; memset(&sel, 0, sizeof(sel)); int firstcall = 0; struct attr attr; /* Fix for #1135: Check if pitch was set while w and h were 0. In this case set pitch * again so transformation value is set correctly */ if(this_->w==0 && this_->h==0) { firstcall=1; } this_->w=w; this_->h=h; /* Fix for #1135: Now w and h are set initially, we can set pitch value again * */ if(firstcall) { attr.type=attr_pitch; attr.u.num=this_->pitch; navit_set_attr(this_, &attr); // Set pitch again } sel.u.p_rect.rl.x=w; sel.u.p_rect.rl.y=h; transform_set_screen_selection(this_->trans, &sel); graphics_init(this_->gra); graphics_set_rect(this_->gra, &sel.u.p_rect); if (callback) callback_list_call_attr_1(this_->attr_cbl, attr_graphics_ready, this_); if (this_->ready == 3) { /* About to resize. Cancel drawing whatever it is */ graphics_draw_cancel(this_->gra, this_->displaylist); /* draw again even if we did not cancel anything */ navit_draw_async(this_, 1); } } static void navit_resize(void *data, int w, int h) { struct navit *this=data; if (!this->ignore_graphics_events) navit_handle_resize(this, w, h); } int navit_get_width(struct navit *this_) { return this_->w; } int navit_get_height(struct navit *this_) { return this_->h; } static void navit_popup(void *data) { struct navit *this_=data; popup(this_, 1, &this_->pressed); this_->button_timeout=NULL; this_->popped=1; } /** * @brief Sets a flag indicating that the current button event should be ignored by subsequent handlers. * * Calling this function will set the {@code ignore_button} member to {@code true} and return its previous state. * The default handler, {@link navit_handle_button(navit *, int, int, point *, callback *)} calls this function * just before the actual event handling core and aborts if the result is {@code true}. In order to prevent * multiple handlers from firing on a single event, custom button click handlers should implement the same logic * for events they wish to handle. * * If a handler wishes to pass down an event to other handlers, it must abort without calling this function. * * @param this_ The navit instance * @return {@code true} if the caller should ignore the button event, {@code false} if it should handle it */ int navit_ignore_button(struct navit *this_) { if (this_->ignore_button) return 1; this_->ignore_button=1; return 0; } void navit_ignore_graphics_events(struct navit *this_, int ignore) { this_->ignore_graphics_events=ignore; } static int navit_restrict_to_range(int value, int min, int max) { if (value>max) { value = max; } if (valuex = navit_restrict_to_range(new_center->x, WORLD_BOUNDINGBOX_MIN_X, WORLD_BOUNDINGBOX_MAX_X); new_center->y = navit_restrict_to_range(new_center->y, WORLD_BOUNDINGBOX_MIN_Y, WORLD_BOUNDINGBOX_MAX_Y); } /** * @brief Change map center position by translating from "old" to "new". */ static void update_transformation(struct transformation *tr, struct point *old, struct point *new) { /* Code for rotation was removed in rev. 5252; see Trac #1078. */ struct coord coord_old,coord_new; struct coord center_new,*center_old; if (!transform_reverse(tr, old, &coord_old)) return; if (!transform_reverse(tr, new, &coord_new)) return; center_old=transform_get_center(tr); center_new.x=center_old->x+coord_old.x-coord_new.x; center_new.y=center_old->y+coord_old.y-coord_new.y; navit_restrict_map_center_to_world_boundingbox(tr, ¢er_new); dbg(lvl_debug,"change center from 0x%x,0x%x to 0x%x,0x%x", center_old->x, center_old->y, center_new.x, center_new.y); transform_set_center(tr, ¢er_new); } void navit_set_timeout(struct navit *this_) { struct attr follow; follow.type=attr_follow; follow.u.num=this_->center_timeout; navit_set_attr(this_, &follow); } int navit_handle_button(struct navit *this_, int pressed, int button, struct point *p, struct callback *popup_callback) { int border=16; dbg(lvl_debug,"button %d %s (ignore: %d)",button,pressed?"pressed":"released",this_->ignore_button); callback_list_call_attr_4(this_->attr_cbl, attr_button, this_, GINT_TO_POINTER(pressed), GINT_TO_POINTER(button), p); if (this_->ignore_button) { this_->ignore_button=0; return 0; } if (pressed) { this_->pressed=*p; this_->last=*p; this_->zoomed=0; if (button == 1) { this_->button_pressed=1; this_->moved=0; this_->popped=0; if (popup_callback) this_->button_timeout=event_add_timeout(500, 0, popup_callback); } if (button == 2) navit_set_center_screen(this_, p, 1); if (button == 3) popup(this_, button, p); if (button == 4 && this_->use_mousewheel) { this_->zoomed = 1; navit_zoom_in(this_, 2, p); } if (button == 5 && this_->use_mousewheel) { this_->zoomed = 1; navit_zoom_out(this_, 2, p); } } else { this_->button_pressed=0; if (this_->button_timeout) { event_remove_timeout(this_->button_timeout); this_->button_timeout=NULL; if (! this_->moved && ! transform_within_border(this_->trans, p, border)) { navit_set_center_screen(this_, p, !this_->zoomed); } } if (this_->motion_timeout) { event_remove_timeout(this_->motion_timeout); this_->motion_timeout=NULL; } if (this_->moved) { dbg(lvl_debug, "mouse drag (%d, %d)->(%d, %d)", this_->pressed.x, this_->pressed.y, p->x, p->y); update_transformation(this_->trans, &this_->pressed, p); graphics_draw_drag(this_->gra, NULL); transform_copy(this_->trans, this_->trans_cursor); graphics_overlay_disable(this_->gra, 0); if (!this_->zoomed) navit_set_timeout(this_); navit_draw(this_); } else return 1; } return 0; } static void navit_button(void *data, int pressed, int button, struct point *p) { struct navit *this=data; dbg(lvl_debug,"enter %d %d ignore %d",pressed,button,this->ignore_graphics_events); if (!this->ignore_graphics_events) { if (! this->popup_callback) this->popup_callback=callback_new_1(callback_cast(navit_popup), this); navit_handle_button(this, pressed, button, p, this->popup_callback); } } static void navit_motion_timeout(struct navit *this_) { int dx, dy; if (this_->drag_bitmap) { struct point point; point.x=(this_->current.x-this_->pressed.x); point.y=(this_->current.y-this_->pressed.y); if (graphics_draw_drag(this_->gra, &point)) { graphics_overlay_disable(this_->gra, 1); graphics_draw_mode(this_->gra, draw_mode_end); this_->moved=1; this_->motion_timeout=NULL; return; } } dx=(this_->current.x-this_->last.x); dy=(this_->current.y-this_->last.y); if (dx || dy) { struct transformation *tr; this_->last=this_->current; graphics_overlay_disable(this_->gra, 1); tr=transform_dup(this_->trans); update_transformation(tr, &this_->pressed, &this_->current); graphics_draw_cancel(this_->gra, this_->displaylist); graphics_displaylist_draw(this_->gra, this_->displaylist, tr, this_->layout_current, this_->graphics_flags|512); transform_destroy(tr); this_->moved=1; } this_->motion_timeout=NULL; return; } void navit_handle_motion(struct navit *this_, struct point *p) { int dx, dy; if (this_->button_pressed && !this_->popped) { dx=(p->x-this_->pressed.x); dy=(p->y-this_->pressed.y); if (dx < -8 || dx > 8 || dy < -8 || dy > 8) { this_->moved=1; if (this_->button_timeout) { event_remove_timeout(this_->button_timeout); this_->button_timeout=NULL; } this_->current=*p; if (! this_->motion_timeout_callback) this_->motion_timeout_callback=callback_new_1(callback_cast(navit_motion_timeout), this_); if (! this_->motion_timeout) this_->motion_timeout=event_add_timeout(this_->drag_bitmap?10:100, 0, this_->motion_timeout_callback); } } } static void navit_motion(void *data, struct point *p) { struct navit *this=data; if (!this->ignore_graphics_events) navit_handle_motion(this, p); } static void navit_predraw(struct navit *this_) { GList *l; struct navit_vehicle *nv; transform_copy(this_->trans, this_->trans_cursor); l=this_->vehicles; while (l) { nv=l->data; navit_vehicle_draw(this_, nv, NULL); l=g_list_next(l); } } static void navit_scale(struct navit *this_, long scale, struct point *p, int draw) { struct coord c1, c2, *center; if (scale < this_->zoom_min) scale=this_->zoom_min; if (scale > this_->zoom_max) scale=this_->zoom_max; if (p) transform_reverse(this_->trans, p, &c1); transform_set_scale(this_->trans, scale); if (p) { transform_reverse(this_->trans, p, &c2); center = transform_center(this_->trans); center->x += c1.x - c2.x; center->y += c1.y - c2.y; } if (draw) navit_draw(this_); } /** * @brief Automatically adjusts zoom level * * This function automatically adjusts the current * zoom level according to the current speed. * * @param this_ The navit struct * @param center The "immovable" point - i.e. the vehicles position if we're centering on the vehicle * @param speed The vehicles speed in meters per second * @param dir The direction into which the vehicle moves */ static void navit_autozoom(struct navit *this_, struct coord *center, int speed, int draw) { struct point pc; int distance,w,h; double new_scale; long scale; if (! this_->autozoom_active) { return; } if(this_->autozoom_paused) { this_->autozoom_paused--; return; } distance = speed * this_->autozoom_secs; transform_get_size(this_->trans, &w, &h); transform_point(this_->trans, transform_get_projection(this_->trans), center, &pc); scale = transform_get_scale(this_->trans); /* We make sure that the point we want to see is within a certain range * around the vehicle. The radius of this circle is the size of the * screen. This doesn't necessarily mean the point is visible because of * perspective etc. Quite rough, but should be enough. */ if (w > h) { new_scale = (double)distance / h * 16; } else { new_scale = (double)distance / w * 16; } if (abs((int)new_scale - (int)scale) < 2) { return; // Smoothing } if (new_scale > this_->autozoom_max) new_scale=this_->autozoom_max; if (new_scale < this_->autozoom_min) new_scale=this_->autozoom_min; if (new_scale != scale) navit_scale(this_, (long)new_scale, &pc, 0); } /** * Change the current zoom level, zooming closer to the ground * * @param navit The navit instance * @param factor The zoom factor, usually 2 * @param p The invariant point (if set to NULL, default to center) * @returns nothing */ void navit_zoom_in(struct navit *this_, int factor, struct point *p) { long scale=transform_get_scale(this_->trans)/factor; if(this_->autozoom_active) { this_->autozoom_paused = 10; } if (scale < 1) scale=1; navit_scale(this_, scale, p, 1); } /** * Change the current zoom level * * @param navit The navit instance * @param factor The zoom factor, usually 2 * @param p The invariant point (if set to NULL, default to center) * @returns nothing */ void navit_zoom_out(struct navit *this_, int factor, struct point *p) { long scale=transform_get_scale(this_->trans)*factor; if(this_->autozoom_active) { this_->autozoom_paused = 10; } navit_scale(this_, scale, p, 1); } void navit_zoom_in_cursor(struct navit *this_, int factor) { struct point p; if (this_->vehicle && this_->vehicle->follow_curr <= 1 && navit_get_cursor_pnt(this_, &p, 0, NULL)) { navit_zoom_in(this_, factor, &p); this_->vehicle->follow_curr=this_->vehicle->follow; } else navit_zoom_in(this_, factor, NULL); } void navit_zoom_out_cursor(struct navit *this_, int factor) { struct point p; if (this_->vehicle && this_->vehicle->follow_curr <= 1 && navit_get_cursor_pnt(this_, &p, 0, NULL)) { navit_zoom_out(this_, 2, &p); this_->vehicle->follow_curr=this_->vehicle->follow; } else navit_zoom_out(this_, 2, NULL); } static int navit_cmd_zoom_in(struct navit *this_, char *cmd, struct attr **in, struct attr ***out) { navit_zoom_in_cursor(this_, 2); return 0; } static int navit_cmd_zoom_out(struct navit *this_, char *cmd, struct attr **in, struct attr ***out) { navit_zoom_out_cursor(this_, 2); return 0; } static int navit_cmd_say(struct navit *this, char *function, struct attr **in, struct attr ***out) { if (in && in[0] && ATTR_IS_STRING(in[0]->type) && in[0]->u.str) navit_say(this, in[0]->u.str); return 0; } static GHashTable *cmd_int_var_hash = NULL; static GHashTable *cmd_attr_var_hash = NULL; /** * Store key value pair for the command system (for int typed values) * * @param navit The navit instance * @param function unused (needed to match command function signature) * @param in input attributes in[0] is the key string, in[1] is the integer value to store * @param out output attributes, unused * @returns 0 */ static int navit_cmd_set_int_var(struct navit *this, char *function, struct attr **in, struct attr ***out) { char*key; struct attr*val; if(!cmd_int_var_hash) { cmd_int_var_hash = g_hash_table_new(g_str_hash, g_str_equal); } if ( (in && in[0] && ATTR_IS_STRING(in[0]->type) && in[0]->u.str) && (in && in[1] && ATTR_IS_NUMERIC(in[1]->type))) { val = g_new(struct attr,1); attr_dup_content(in[1],val); key = g_strdup(in[0]->u.str); g_hash_table_insert(cmd_int_var_hash, key, val); } return 0; } /** * Store key value pair for the command system (for attr typed values, can be used as opaque handles) * * @param navit The navit instance * @param function unused (needed to match command function signature) * @param in input attributes in[0] is the key string, in[1] is the attr* value to store * @param out output attributes, unused * @returns 0 */ static int navit_cmd_set_attr_var(struct navit *this, char *function, struct attr **in, struct attr ***out) { char*key; struct attr*val; if(!cmd_attr_var_hash) { cmd_attr_var_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)attr_free); } if ( (in && in[0] && ATTR_IS_STRING(in[0]->type) && in[0]->u.str) && (in && in[1] )) { val = attr_dup(in[1]); key = g_strdup(in[0]->u.str); g_hash_table_insert(cmd_attr_var_hash, key, val); } else { dbg(lvl_warning, "Wrong parameters for set_attr_var() command function"); } return 0; } /** * command to toggle the active state of a named layer of the current layout * * @param navit The navit instance * @param function unused (needed to match command function signature) * @param in input attribute in[0] is the name of the layer * @param out output unused * @param valid unused * @returns 0 */ static int navit_cmd_toggle_layer(struct navit *this, char *function, struct attr **in, struct attr ***out) { if (in && in[0] && ATTR_IS_STRING(in[0]->type) && in[0]->u.str) { if(this->layout_current && this->layout_current->layers) { GList* layers = this->layout_current->layers; while (layers) { struct layer*l=layers->data; if(l && !strcmp(l->name,in[0]->u.str) ) { l->active ^= 1; navit_draw(this); return 0; } layers=g_list_next(layers); } } } return 0; } /** * adds an item with the current coordinate of the vehicle to a named map * * @param navit The navit instance * @param function unused (needed to match command function signature) * @param in input attribute in[0] is the name of the map * @param out output attribute, 0 on error or the id of the created item on success * @returns 0 */ static int navit_cmd_map_add_curr_pos(struct navit *this, char *function, struct attr **in, struct attr ***out) { struct attr **list = g_new0(struct attr *,2); struct attr*val = g_new0(struct attr,1); struct mapset* ms; struct map_selection sel; const int selection_range = 10; enum item_type item_type; struct item *it; struct map* curr_map = NULL; struct coord curr_coord; struct map_rect *mr; //return invalid item on error val->type = attr_none; val->u.item = NULL; list[0] = val; list[1] = NULL; *out = list; if ( in && in[0] && ATTR_IS_STRING(in[0]->type) && in[0]->u.str && //map name in[1] && ATTR_IS_STRING(in[1]->type) && in[1]->u.str //item type ) { if(!(ms=navit_get_mapset(this))) { dbg(lvl_error, "Command function map_add_curr_pos(): there is no active mapset"); return 0; } if((item_type = item_from_name(in[1]->u.str))==type_none) { dbg(lvl_error, "Command function map_add_curr_pos(): unknown item type"); return 0; } curr_map = mapset_get_map_by_name(ms, in[0]->u.str); //no map with the given name found if( ! curr_map) { dbg(lvl_error, "Command function map_add_curr_pos(): map not found"); return 0; } if(this->vehicle && this->vehicle->vehicle ) { struct attr pos_attr; if(vehicle_get_attr(this->vehicle->vehicle,attr_position_coord_geo,&pos_attr,NULL)) { transform_from_geo(projection_mg, pos_attr.u.coord_geo, &curr_coord); } else { dbg(lvl_error, "Command function map_add_curr_pos(): vehicle position is not accessible"); return 0; } } else { dbg(lvl_error, "Command function map_add_curr_pos(): no vehicle"); return 0; } sel.next=NULL; sel.order=18; sel.range.min=type_none; sel.range.max=type_tec_common; sel.u.c_rect.lu.x=curr_coord.x-selection_range; sel.u.c_rect.lu.y=curr_coord.y+selection_range; sel.u.c_rect.rl.x=curr_coord.x+selection_range; sel.u.c_rect.rl.y=curr_coord.y-selection_range; mr = map_rect_new(curr_map, &sel); if(mr) { it = map_rect_create_item( mr, item_type); if (it) { struct attr attr; attr.type=attr_type_item_begin; attr.u.item=it; attr_dup_content(&attr,val); item_coord_set(it,&curr_coord, 1, change_mode_modify); } } map_rect_destroy(mr); } return 0; } /** * sets an attribute (name value pair) of a map item specified by map name and item id * * @param navit The navit instance * @param function unused (needed to match command function signature) * @param in input attribute in[0] - name of the map ; in[1] - item ; in[2] - attr name ; in[3] - attr value * @param out output attribute, 0 on error, 1 on success * @returns 0 */ static int navit_cmd_map_item_set_attr(struct navit *this, char *function, struct attr **in, struct attr ***out) { if ( in && in[0] && ATTR_IS_STRING(in[0]->type) && in[0]->u.str &&//map name in[1] && ATTR_IS_ITEM(in[1]->type) && in[2]->u.item &&//item in[2] && ATTR_IS_STRING(in[2]->type) && in[2]->u.str && //attr_type str in[3] && ATTR_IS_STRING(in[3]->type) && in[3]->u.str //attr_value str ) { struct attr attr_to_set; struct map* curr_map = NULL; struct mapset *ms; struct item *it; struct map_rect *mr; if(ATTR_IS_STRING(attr_from_name(in[2]->u.str))) { attr_to_set.u.str = in[3]->u.str; attr_to_set.type = attr_from_name(in[2]->u.str); } else if(ATTR_IS_INT(attr_from_name(in[2]->u.str))) { attr_to_set.u.num = atoi(in[3]->u.str); attr_to_set.type = attr_from_name(in[2]->u.str); } else if(ATTR_IS_DOUBLE(attr_from_name(in[2]->u.str))) { double* val = g_new0(double,1); *val = atof(in[3]->u.str); attr_to_set.u.numd = val; attr_to_set.type = attr_from_name(in[2]->u.str); } ms = navit_get_mapset(this); curr_map = mapset_get_map_by_name(ms, in[0]->u.str); if( ! curr_map) { return 0; } mr=map_rect_new(curr_map,NULL); it=in[1]->u.item; it=map_rect_get_item_byid(mr,it->id_hi,it->id_lo); if(it) { item_attr_set(it, &attr_to_set, change_mode_modify); } map_rect_destroy(mr); } else { dbg(lvl_debug,"Error in command function item_set_attr()"); dbg(lvl_debug,"Command function item_set_attr(): map cond: %d",(in[0] && ATTR_IS_STRING(in[0]->type) && in[0]->u.str)?1:0); dbg(lvl_debug,"Command function item_set_attr(): item cond: %d",(in[1] && ATTR_IS_ITEM(in[1]->type))?1:0); dbg(lvl_debug,"Command function item_set_attr(): attr type cond: %d",(in[2] && ATTR_IS_STRING(in[2]->type) && in[2]->u.str)?1:0); dbg(lvl_debug,"Command function item_set_attr(): attr val cond: %d",(in[3] && ATTR_IS_STRING(in[3]->type) && in[3]->u.str)?1:0); } return 0; } /** * Get attr variable given a key string for the command system (for opaque usage) * * @param navit The navit instance * @param function unused (needed to match command function signature) * @param in input attribute in[0] is the key string * @param out output attribute, the attr for the given key string if exists or NULL * @returns 0 */ static int navit_cmd_get_attr_var(struct navit *this, char *function, struct attr **in, struct attr ***out) { struct attr **list = g_new0(struct attr *,2); list[1] = NULL; *out = list; if(!cmd_attr_var_hash) { struct attr*val = g_new0(struct attr,1); val->type = attr_type_item_begin; val->u.item = NULL; list[0] = val; return 0; } if (in && in[0] && ATTR_IS_STRING(in[0]->type) && in[0]->u.str) { struct attr*ret = g_hash_table_lookup(cmd_attr_var_hash, in[0]->u.str); if(ret) { list[0] = attr_dup(ret); } else { struct attr*val = g_new0(struct attr,1); val->type = attr_type_int_begin; val->u.item = NULL; list[0] = val; } } return 0; } /** * Get value given a key string for the command system * * @param navit The navit instance * @param function unused (needed to match command function signature) * @param in input attribute in[0] is the key string * @param out output attribute, the value for the given key string if exists or 0 * @returns 0 */ static int navit_cmd_get_int_var(struct navit *this, char *function, struct attr **in, struct attr ***out) { struct attr **list = g_new0(struct attr *,2); list[1] = NULL; *out = list; if(!cmd_int_var_hash) { struct attr*val = g_new0(struct attr,1); val->type = attr_type_int_begin; val->u.num = 0; list[0] = val; return 0; } if (in && in[0] && ATTR_IS_STRING(in[0]->type) && in[0]->u.str) { struct attr*ret = g_hash_table_lookup(cmd_int_var_hash, in[0]->u.str); if(ret) { list[0] = attr_dup(ret); } else { struct attr*val = g_new0(struct attr,1); val->type = attr_type_int_begin; val->u.num = 0; list[0] = val; } } return 0; } GList *cmd_int_var_stack = NULL; /** * Push an integer to the stack for the command system * * @param navit The navit instance * @param function unused (needed to match command function signature) * @param in input attribute in[0] is the integer attibute to push * @param out output attributes, unused * @returns 0 */ static int navit_cmd_push_int(struct navit *this, char *function, struct attr **in, struct attr ***out) { if (in && in[0] && ATTR_IS_NUMERIC(in[0]->type)) { struct attr*val = g_new(struct attr,1); attr_dup_content(in[0],val); cmd_int_var_stack = g_list_prepend(cmd_int_var_stack, val); } return 0; } /** * Pop an integer from the command system's integer stack * * @param navit The navit instance * @param function unused (needed to match command function signature) * @param in input attributes unused * @param out output attribute, the value popped if stack isn't empty or 0 * @returns 0 */ static int navit_cmd_pop_int(struct navit *this, char *function, struct attr **in, struct attr ***out) { struct attr **list = g_new0(struct attr *,2); if(!cmd_int_var_stack) { struct attr*val = g_new0(struct attr,1); val->type = attr_type_int_begin; val->u.num = 0; list[0] = val; } else { list[0] = cmd_int_var_stack->data; cmd_int_var_stack = g_list_remove_link(cmd_int_var_stack,cmd_int_var_stack); } list[1] = NULL; *out = list; return 0; } /** * Get current size of command system's integer stack * * @param navit The navit instance * @param function unused (needed to match command function signature) * @param in input attributes unused * @param out output attribute, the size of stack * @returns 0 */ static int navit_cmd_int_stack_size(struct navit *this, char *function, struct attr **in, struct attr ***out) { struct attr **list; struct attr *attr = g_new0(struct attr,1); attr->type = attr_type_int_begin; if(!cmd_int_var_stack) { attr->u.num = 0; } else { attr->u.num = g_list_length(cmd_int_var_stack); } list = g_new0(struct attr *,2); list[0] = attr; list[1] = NULL; *out = list; cmd_int_var_stack = g_list_remove_link(cmd_int_var_stack,cmd_int_var_stack); return 0; } static struct attr ** navit_get_coord(struct navit *this, struct attr **in, struct pcoord *pc) { if (!in) return NULL; if (!in[0]) return NULL; pc->pro = transform_get_projection(this->trans); if (ATTR_IS_STRING(in[0]->type)) { struct coord c; coord_parse(in[0]->u.str, pc->pro, &c); pc->x=c.x; pc->y=c.y; in++; } else if (ATTR_IS_COORD(in[0]->type)) { pc->x=in[0]->u.coord->x; pc->y=in[0]->u.coord->y; in++; } else if (ATTR_IS_PCOORD(in[0]->type)) { *pc=*in[0]->u.pcoord; in++; } else if (in[1] && in[2] && ATTR_IS_INT(in[0]->type) && ATTR_IS_INT(in[1]->type) && ATTR_IS_INT(in[2]->type)) { pc->pro=in[0]->u.num; pc->x=in[1]->u.num; pc->y=in[2]->u.num; in+=3; } else if (in[1] && ATTR_IS_INT(in[0]->type) && ATTR_IS_INT(in[1]->type)) { pc->x=in[0]->u.num; pc->y=in[1]->u.num; in+=2; } else return NULL; return in; } static int navit_cmd_set_destination(struct navit *this, char *function, struct attr **in, struct attr ***out) { struct pcoord pc; char *description=NULL; in=navit_get_coord(this, in, &pc); if (!in) return 0; if (in[0] && ATTR_IS_STRING(in[0]->type)) description=in[0]->u.str; navit_set_destination(this, &pc, description, 1); return 0; } static int navit_cmd_route_remove_next_waypoint(struct navit *this, char *function, struct attr **in, struct attr ***out) { navit_remove_waypoint(this); return 0; } static int navit_cmd_route_remove_last_waypoint(struct navit *this, char *function, struct attr **in, struct attr ***out) { navit_remove_nth_waypoint(this, navit_get_destination_count(this)-1); return 0; } static int navit_cmd_set_center(struct navit *this, char *function, struct attr **in, struct attr ***out) { struct pcoord pc; int set_timeout=0; in=navit_get_coord(this, in, &pc); if (!in) return 0; if(in[0] && ATTR_IS_INT(in[0]->type)) set_timeout=in[0]->u.num!=0; navit_set_center(this, &pc, set_timeout); return 0; } static int navit_cmd_set_position(struct navit *this, char *function, struct attr **in, struct attr ***out) { struct pcoord pc; in=navit_get_coord(this, in, &pc); if (!in) return 0; navit_set_position(this, &pc); return 0; } static int navit_cmd_fmt_coordinates(struct navit *this, char *function, struct attr **in, struct attr ***out) { struct attr attr; attr.type=attr_type_string_begin; attr.u.str="Fix me"; if (out) { *out=attr_generic_add_attr(*out, &attr); } return 0; } /** * Join several string attributes into one * * @param navit The navit instance * @param function unused (needed to match command function signature) * @param in input attributes in[0] - separator, in[1..] - attributes to join * @param out output attribute joined attribute as string * @returns 0 */ static int navit_cmd_strjoin(struct navit *this, char *function, struct attr **in, struct attr ***out) { struct attr attr; gchar *ret, *sep; int i; attr.type=attr_type_string_begin; attr.u.str=NULL; if(in[0] && in[1]) { sep=attr_to_text(in[0],NULL,1); ret=attr_to_text(in[1],NULL,1); for(i=2; in[i]; i++) { gchar *in_i=attr_to_text(in[i],NULL,1); gchar *r=g_strjoin(sep,ret,in_i,NULL); g_free(in_i); g_free(ret); ret=r; } g_free(sep); attr.u.str=ret; if(out) { *out=attr_generic_add_attr(*out, &attr); } g_free(ret); } return 0; } /** * Call external program * * @param navit The navit instance * @param function unused (needed to match command function signature) * @param in input attributes in[0] - name of executable, in[1..] - parameters * @param out output attribute unused * @returns 0 */ static int navit_cmd_spawn(struct navit *this, char *function, struct attr **in, struct attr ***out) { int i,j, nparms, nvalid; char ** argv=NULL; struct spawn_process_info *pi; nparms=0; nvalid=0; if(in) { while(in[nparms]) { if (in[nparms]->type!=attr_none) nvalid++; nparms++; } } if(nvalid>0) { argv=g_new(char*,nvalid+1); for(i=0,j=0; in[i]; i++) { if(in[i]->type!=attr_none ) { argv[j++]=attr_to_text(in[i],NULL,1); } else { dbg(lvl_debug,"Parameter #%i is attr_none - skipping",i); } } argv[j]=NULL; pi=spawn_process(argv); // spawn_process() testing suite - uncomment following code to test. //sleep(3); // example of non-blocking wait //int st=spawn_process_check_status(pi,0);dbg(lvl_debug,"status %i",st); // example of blocking wait //st=spawn_process_check_status(pi,1);dbg(lvl_debug,"status %i",st); // example of wait after process is finished and status is // already tested //st=spawn_process_check_status(pi,1);dbg(lvl_debug,"status %i",st); // example of wait after process is finished and status is // already tested - unblocked //st=spawn_process_check_status(pi,0);dbg(lvl_debug,"status %i",st); // End testing suite spawn_process_info_free(pi); for(i=0; argv[i]; i++) g_free(argv[i]); g_free(argv); } return 0; } static struct command_table commands[] = { {"zoom_in",command_cast(navit_cmd_zoom_in)}, {"zoom_out",command_cast(navit_cmd_zoom_out)}, {"zoom_to_route",command_cast(navit_cmd_zoom_to_route)}, {"say",command_cast(navit_cmd_say)}, {"set_center",command_cast(navit_cmd_set_center)}, {"set_center_cursor",command_cast(navit_cmd_set_center_cursor)}, {"set_destination",command_cast(navit_cmd_set_destination)}, {"set_position",command_cast(navit_cmd_set_position)}, {"route_remove_next_waypoint",command_cast(navit_cmd_route_remove_next_waypoint)}, {"route_remove_last_waypoint",command_cast(navit_cmd_route_remove_last_waypoint)}, {"set_position",command_cast(navit_cmd_set_position)}, {"announcer_toggle",command_cast(navit_cmd_announcer_toggle)}, {"fmt_coordinates",command_cast(navit_cmd_fmt_coordinates)}, {"set_int_var",command_cast(navit_cmd_set_int_var)}, {"get_int_var",command_cast(navit_cmd_get_int_var)}, {"push_int",command_cast(navit_cmd_push_int)}, {"pop_int",command_cast(navit_cmd_pop_int)}, {"int_stack_size",command_cast(navit_cmd_int_stack_size)}, {"toggle_layer",command_cast(navit_cmd_toggle_layer)}, {"strjoin",command_cast(navit_cmd_strjoin)}, {"spawn",command_cast(navit_cmd_spawn)}, {"map_add_curr_pos",command_cast(navit_cmd_map_add_curr_pos)}, {"map_item_set_attr",command_cast(navit_cmd_map_item_set_attr)}, {"set_attr_var",command_cast(navit_cmd_set_attr_var)}, {"get_attr_var",command_cast(navit_cmd_get_attr_var)}, {"switch_layout_day_night",command_cast(navit_cmd_switch_layout_day_night)}, }; void navit_command_add_table(struct navit*this_, struct command_table *commands, int count) { command_add_table(this_->attr_cbl, commands, count, this_); } struct navit * navit_new(struct attr *parent, struct attr **attrs) { struct navit *this_=g_new0(struct navit, 1); struct pcoord center; struct coord co; struct coord_geo g; enum projection pro=projection_mg; int zoom = 256; g.lat=53.13; g.lng=11.70; this_->func=&navit_func; navit_object_ref((struct navit_object *)this_); this_->attrs=attr_list_dup(attrs); this_->self.type=attr_navit; this_->self.u.navit=this_; this_->attr_cbl=callback_list_new(); this_->orientation=-1; this_->tracking_flag=1; this_->recentdest_count=10; this_->osd_configuration=-1; this_->default_layout_name=NULL; this_->center_timeout = 10; this_->use_mousewheel = 1; this_->autozoom_secs = 10; this_->autozoom_min = 7; this_->autozoom_active = 0; this_->autozoom_paused = 0; this_->zoom_min = 1; this_->zoom_max = 2097152; this_->autozoom_max = this_->zoom_max; this_->follow_cursor = 1; this_->radius = 30; this_->border = 16; this_->auto_switch = TRUE; this_->tunnel_nightlayout = FALSE; this_->layout_before_tunnel = ""; this_->sunrise_degrees = -5; transform_from_geo(pro, &g, &co); center.x=co.x; center.y=co.y; center.pro = pro; this_->trans = transform_new(¢er, zoom, (this_->orientation != -1) ? this_->orientation : 0); this_->trans_cursor = transform_new(¢er, zoom, (this_->orientation != -1) ? this_->orientation : 0); this_->bookmarks=bookmarks_new(&this_->self, NULL, this_->trans); this_->prevTs=0; for (; *attrs; attrs++) { navit_set_attr_do(this_, *attrs, 1); } this_->displaylist=graphics_displaylist_new(); command_add_table(this_->attr_cbl, commands, sizeof(commands)/sizeof(struct command_table), this_); this_->messages = messagelist_new(attrs); dbg(lvl_debug,"return %p",this_); return this_; } static int navit_set_gui(struct navit *this_, struct gui *gui) { if (this_->gui) return 0; this_->gui=gui; if (gui_has_main_loop(this_->gui)) { if (! main_loop_gui) { main_loop_gui=this_->gui; } else { dbg(lvl_error,"gui with main loop already active, ignoring this instance"); return 0; } } return 1; } void navit_add_message(struct navit *this_, const char *message) { message_new(this_->messages, message); } struct message *navit_get_messages(struct navit *this_) { return message_get(this_->messages); } static int navit_set_graphics(struct navit *this_, struct graphics *gra) { if (this_->gra) return 0; this_->gra=gra; this_->resize_callback=callback_new_attr_1(callback_cast(navit_resize), attr_resize, this_); graphics_add_callback(gra, this_->resize_callback); this_->button_callback=callback_new_attr_1(callback_cast(navit_button), attr_button, this_); graphics_add_callback(gra, this_->button_callback); this_->motion_callback=callback_new_attr_1(callback_cast(navit_motion), attr_motion, this_); graphics_add_callback(gra, this_->motion_callback); this_->predraw_callback=callback_new_attr_1(callback_cast(navit_predraw), attr_predraw, this_); graphics_add_callback(gra, this_->predraw_callback); return 1; } struct graphics * navit_get_graphics(struct navit *this_) { return this_->gra; } struct vehicleprofile * navit_get_vehicleprofile(struct navit *this_) { return this_->vehicleprofile; } GList *navit_get_vehicleprofiles(struct navit *this_) { return this_->vehicleprofiles; } static void navit_projection_set(struct navit *this_, enum projection pro, int draw) { struct coord_geo g; struct coord *c; c=transform_center(this_->trans); transform_to_geo(transform_get_projection(this_->trans), c, &g); transform_set_projection(this_->trans, pro); transform_from_geo(pro, &g, c); if (draw) navit_draw(this_); } static void navit_mark_navigation_stopped(char *former_destination_file) { FILE *f; f=fopen(former_destination_file, "a"); if (f) { fprintf(f,"%s", TEXTFILE_COMMENT_NAVI_STOPPED); fclose(f); } else { dbg(lvl_error, "Error setting mark in destination file %s: %s", former_destination_file, strerror(errno)); } } /** * Start or add a given set of coordinates for route computing * * @param navit The navit instance * @param c The coordinate to start routing to * @param description A label which allows the user to later identify this destination in the former destinations selection * @param async Set to 1 to do route calculation asynchronously * @return nothing */ void navit_set_destination(struct navit *this_, struct pcoord *c, const char *description, int async) { char *destination_file; destination_file = bookmarks_get_destination_file(TRUE); if (c) { this_->destination=*c; this_->destination_valid=1; dbg(lvl_debug, "c=(%i,%i)", c->x,c->y); bookmarks_append_destinations(this_->former_destination, destination_file, c, 1, type_former_destination, description, this_->recentdest_count); } else { this_->destination_valid=0; bookmarks_append_destinations(this_->former_destination, destination_file, NULL, 0, type_former_destination, NULL, this_->recentdest_count); navit_mark_navigation_stopped(destination_file); } g_free(destination_file); if (this_->route) { struct attr attr; int dstcount; struct pcoord *pc; navit_get_attr(this_, attr_waypoints_flag, &attr, NULL); if (this_->waypoints_flag==0 || route_get_destination_count(this_->route)==0) { route_set_destination(this_->route, c, async); } else { route_append_destination(this_->route, c, async); } dstcount=route_get_destination_count(this_->route); if(dstcount>0) { destination_file = bookmarks_get_destination_file(TRUE); pc=g_new(struct pcoord,dstcount); route_get_destinations(this_->route,pc,dstcount); bookmarks_append_destinations(this_->former_destination, destination_file, pc, dstcount, type_former_itinerary, description, this_->recentdest_count); g_free(pc); g_free(destination_file); } } callback_list_call_attr_0(this_->attr_cbl, attr_destination); if (this_->route && this_->ready == 3 && !(this_->flags & 4)) navit_draw(this_); } /** * Add destination description to the recent dest file. Doesn't start routing. * * @param navit The navit instance * @param c The coordinate to start routing to * @param description A label which allows the user to later identify this destination in the former destinations selection * @returns nothing */ void navit_add_destination_description(struct navit *this_, struct pcoord *c, const char *description) { char *destination_file; if (c) { destination_file = bookmarks_get_destination_file(TRUE); bookmarks_append_destinations(this_->former_destination, destination_file, c, 1, type_former_destination, description, this_->recentdest_count); g_free(destination_file); } } /** * Start the route computing to a given set of coordinates including waypoints * * @param this_ The navit instance * @param c The coordinate to start routing to * @param description A label which allows the user to later identify this destination in the former destinations selection * @param async If routing should be done asynchronously * @returns nothing */ void navit_set_destinations(struct navit *this_, struct pcoord *c, int count, const char *description, int async) { char *destination_file; if (c && count) { this_->destination=c[count-1]; this_->destination_valid=1; destination_file = bookmarks_get_destination_file(TRUE); bookmarks_append_destinations(this_->former_destination, destination_file, c, count, type_former_itinerary, description, this_->recentdest_count); g_free(destination_file); } else this_->destination_valid=0; if (this_->route) route_set_destinations(this_->route, c, count, async); callback_list_call_attr_0(this_->attr_cbl, attr_destination); if (this_->route && this_->ready == 3) navit_draw(this_); } /** * @brief Retrieves destinations from the route * * Prior to calling this method, you may want to retrieve the number of destinations by calling * {@link navit_get_destination_count(struct navit *)} and assigning a buffer of sufficient capacity. * * If the return value equals `count`, the buffer was either just large enough or too small to hold the * entire list of destinations; there is no way to tell from the result which is the case. * * If the Navit instance does not have a route, the result is 0. * * @param this_ The Navit instance * @param pc Pointer to an array of projected coordinates which will receive the destination coordinates * @param count Capacity of `pc` * @return The number of destinations stored in `pc`, never greater than `count` */ int navit_get_destinations(struct navit *this_, struct pcoord *pc, int count) { if(!this_->route) return 0; return route_get_destinations(this_->route, pc, count); } /** * @brief Get the destinations count for the route * * @param this The Navit instance * @return destination count for the route, or 0 if the Navit instance has no route */ int navit_get_destination_count(struct navit *this_) { if(!this_->route) return 0; return route_get_destination_count(this_->route); } char* navit_get_destination_description(struct navit *this_, int n) { if(!this_->route) return NULL; return route_get_destination_description(this_->route, n); } void navit_remove_nth_waypoint(struct navit *this_, int n) { if(!this_->route) return; if (route_get_destination_count(this_->route)>1) { route_remove_nth_waypoint(this_->route, n); } else { navit_set_destination(this_, NULL, NULL, 0); } } void navit_remove_waypoint(struct navit *this_) { if(!this_->route) return; if (route_get_destination_count(this_->route)>1) { route_remove_waypoint(this_->route); } else { navit_set_destination(this_, NULL, NULL, 0); } } /** * @brief Checks if a route is calculated * * This function checks if a route is calculated. * * @param this_ The navit struct whose route should be checked. * @return True if the route is set, false otherwise. */ int navit_check_route(struct navit *this_) { if (this_->route) { return route_get_path_set(this_->route); } return 0; } static int navit_former_destinations_active(struct navit *this_) { char *destination_file_name = bookmarks_get_destination_file(FALSE); FILE *destination_file; int active=0; char lastline[100]; destination_file=fopen(destination_file_name,"r"); if (destination_file) { while(fgets(lastline, sizeof(lastline), destination_file)); fclose(destination_file); /*forcefully terminate the string, there is no proper fgets error handling.*/ lastline[sizeof(lastline) -1] = 0; if (strcmp(lastline, TEXTFILE_COMMENT_NAVI_STOPPED)) { active=1; } } g_free(destination_file_name); return active; } struct map* read_former_destinations_from_file() { struct attr type, data, no_warn, flags, *attrs[5]; char *destination_file = bookmarks_get_destination_file(FALSE); struct map *m; type.type=attr_type; type.u.str="textfile"; data.type=attr_data; data.u.str=destination_file; no_warn.type=attr_no_warning_if_map_file_missing; no_warn.u.num=1; flags.type=attr_flags; flags.u.num=1; attrs[0]=&type; attrs[1]=&data; attrs[2]=&flags; attrs[3]=&no_warn; attrs[4]=NULL; m=map_new(NULL, attrs); g_free(destination_file); return m; } static void navit_add_former_destinations_from_file(struct navit *this_) { struct item *item; int i,valid=0,count=0,maxcount=1; struct coord *c=g_new(struct coord, maxcount); struct pcoord *pc; struct map_rect *mr; this_->former_destination=read_former_destinations_from_file(); if (!this_->route || !navit_former_destinations_active(this_) || !this_->vehicle) return; mr=map_rect_new(this_->former_destination, NULL); while ((item=map_rect_get_item(mr))) { if (item->type == type_former_itinerary || item->type == type_former_itinerary_part) { count=item_coord_get(item, c, maxcount); while(count==maxcount) { maxcount*=2; c=g_realloc(c, sizeof(struct coord)*maxcount); count+=item_coord_get(item, &c[count], maxcount-count); } if(count) valid=1; } } map_rect_destroy(mr); if (valid && count > 0) { pc=g_new(struct pcoord, count); for (i = 0 ; i < count ; i++) { pc[i].pro=map_projection(this_->former_destination); pc[i].x=c[i].x; pc[i].y=c[i].y; } if (count == 1) route_set_destination(this_->route, &pc[0], 1); else route_set_destinations(this_->route, pc, count, 1); this_->destination=pc[count-1]; this_->destination_valid=1; g_free(pc); } g_free(c); } void navit_textfile_debug_log(struct navit *this_, const char *fmt, ...) { va_list ap; char *str1,*str2; va_start(ap, fmt); if (this_->textfile_debug_log && this_->vehicle) { str1=g_strdup_vprintf(fmt, ap); str2=g_strdup_printf("0x%x 0x%x%s%s\n", this_->vehicle->coord.x, this_->vehicle->coord.y, strlen(str1) ? " " : "", str1); log_write(this_->textfile_debug_log, str2, strlen(str2), 0); g_free(str2); g_free(str1); } va_end(ap); } void navit_textfile_debug_log_at(struct navit *this_, struct pcoord *pc, const char *fmt, ...) { va_list ap; char *str1,*str2; va_start(ap, fmt); if (this_->textfile_debug_log && this_->vehicle) { str1=g_strdup_vprintf(fmt, ap); str2=g_strdup_printf("0x%x 0x%x%s%s\n", pc->x, pc->y, strlen(str1) ? " " : "", str1); log_write(this_->textfile_debug_log, str2, strlen(str2), 0); g_free(str2); g_free(str1); } va_end(ap); } void navit_say(struct navit *this_, const char *text) { struct attr attr; if(this_->speech) { if (!speech_get_attr(this_->speech, attr_active, &attr, NULL)) attr.u.num = 1; dbg(lvl_debug, "this_.speech->active %ld", attr.u.num); if(attr.u.num) speech_say(this_->speech, text); } } /** * @brief Toggles the navigation announcer for navit * @param this_ The navit object */ static int navit_cmd_announcer_toggle(struct navit *this_, char *function, struct attr **in, struct attr ***out) { struct attr attr, speechattr; // search for the speech attribute if(!navit_get_attr(this_, attr_speech, &speechattr, NULL)) return 0; // find out if the corresponding attribute attr_active has been set if(speech_get_attr(speechattr.u.speech, attr_active, &attr, NULL)) { // flip it then... attr.u.num = !attr.u.num; } else { // otherwise disable it because voice is enabled by default attr.type = attr_active; attr.u.num = 0; } // apply the new state if(!speech_set_attr(speechattr.u.speech, &attr)) return 0; // announce that the speech attribute has changed callback_list_call_attr_1(this_->attr_cbl, attr_speech, this_); return 0; } void navit_speak(struct navit *this_) { struct navigation *nav=this_->navigation; struct map *map=NULL; struct map_rect *mr=NULL; struct item *item; struct attr attr; if (!speech_get_attr(this_->speech, attr_active, &attr, NULL)) attr.u.num = 1; dbg(lvl_debug, "this_.speech->active %ld", attr.u.num); if(!attr.u.num) return; if (nav) map=navigation_get_map(nav); if (map) mr=map_rect_new(map, NULL); if (mr) { while ((item=map_rect_get_item(mr)) && (item->type == type_nav_position || item->type == type_nav_none)); if (item && item_attr_get(item, attr_navigation_speech, &attr)) { if (*attr.u.str != '\0') { speech_say(this_->speech, attr.u.str); navit_add_message(this_, attr.u.str); } navit_textfile_debug_log(this_, "type=announcement label=\"%s\"", attr.u.str); } map_rect_destroy(mr); } } static void navit_window_roadbook_update(struct navit *this_) { struct navigation *nav=this_->navigation; struct map *map=NULL; struct map_rect *mr=NULL; struct item *item; struct attr attr; struct param_list param[5]; int secs; /* Respect the Imperial attribute as we enlighten the user. */ int imperial = FALSE; /* default to using metric measures. */ if (navit_get_attr(this_, attr_imperial, &attr, NULL)) imperial=attr.u.num; dbg(lvl_debug,"enter"); datawindow_mode(this_->roadbook_window, 1); if (nav) map=navigation_get_map(nav); if (map) mr=map_rect_new(map, NULL); dbg(lvl_debug,"nav=%p map=%p mr=%p", nav, map, mr); if (mr) { dbg(lvl_debug,"while loop"); while ((item=map_rect_get_item(mr))) { dbg(lvl_debug,"item=%p", item); attr.u.str=NULL; if (item->type != type_nav_position) { item_attr_get(item, attr_navigation_long, &attr); if (attr.u.str == NULL) { continue; } dbg(lvl_info, "Command='%s'", attr.u.str); param[0].value=g_strdup(attr.u.str); } else param[0].value=_("Position"); param[0].name=_("Command"); /* Distance to the next maneuver. */ item_attr_get(item, attr_length, &attr); dbg(lvl_info, "Length=%ld in meters", attr.u.num); param[1].name=_("Length"); if ( attr.u.num >= 2000 ) { param[1].value=g_strdup_printf("%5.1f %s", imperial == TRUE ? (float)attr.u.num * METERS_TO_MILES : (float)attr.u.num / 1000, imperial == TRUE ? _("mi") : _("km") ); } else { param[1].value=g_strdup_printf("%7.0f %s", imperial == TRUE ? (attr.u.num * FEET_PER_METER) : attr.u.num, imperial == TRUE ? _("feet") : _("m") ); } /* Time to next maneuver. */ item_attr_get(item, attr_time, &attr); dbg(lvl_info, "Time=%ld", attr.u.num); secs=attr.u.num/10; param[2].name=_("Time"); if ( secs >= 3600 ) { param[2].value=g_strdup_printf("%d:%02d:%02d",secs / 60, ( secs / 60 ) % 60, secs % 60); } else { param[2].value=g_strdup_printf("%d:%02d",secs / 60, secs % 60); } /* Distance from next maneuver to destination. */ item_attr_get(item, attr_destination_length, &attr); dbg(lvl_info, "Destlength=%ld in meters.", attr.u.num); param[3].name=_("Destination Length"); if ( attr.u.num >= 2000 ) { param[3].value=g_strdup_printf("%5.1f %s", imperial == TRUE ? (float)attr.u.num * METERS_TO_MILES : (float)attr.u.num / 1000, imperial == TRUE ? _("mi") : _("km") ); } else { param[3].value=g_strdup_printf("%7.0f %s", imperial == TRUE ? (attr.u.num * FEET_PER_METER) : attr.u.num, imperial == TRUE ? _("feet") : _("m") ); } /* Time from next maneuver to destination. */ item_attr_get(item, attr_destination_time, &attr); dbg(lvl_info, "Desttime=%ld", attr.u.num); secs=attr.u.num/10; param[4].name=_("Destination Time"); if ( secs >= 3600 ) { param[4].value=g_strdup_printf("%d:%02d:%02d",secs / 3600, (secs / 60 ) % 60, secs % 60); } else { param[4].value=g_strdup_printf("%d:%02d",secs / 60, secs % 60); } datawindow_add(this_->roadbook_window, param, 5); } map_rect_destroy(mr); } datawindow_mode(this_->roadbook_window, 0); } void navit_window_roadbook_destroy(struct navit *this_) { dbg(lvl_debug, "enter"); navigation_unregister_callback(this_->navigation, attr_navigation_long, this_->roadbook_callback); callback_destroy(this_->roadbook_callback); this_->roadbook_window=NULL; this_->roadbook_callback=NULL; } void navit_window_roadbook_new(struct navit *this_) { if (!this_->gui || this_->roadbook_callback || this_->roadbook_window) { return; } this_->roadbook_callback=callback_new_1(callback_cast(navit_window_roadbook_update), this_); navigation_register_callback(this_->navigation, attr_navigation_long, this_->roadbook_callback); this_->roadbook_window=gui_datawindow_new(this_->gui, _("Roadbook"), NULL, callback_new_1(callback_cast(navit_window_roadbook_destroy), this_)); navit_window_roadbook_update(this_); } int navit_init(struct navit *this_) { struct mapset *ms; struct map *map; int callback; char *center_file; struct attr_iter *iter; struct attr *attr; struct traffic * traffic; this_->w=0; this_->h=0; dbg(lvl_info,"enter gui %p graphics %p",this_->gui,this_->gra); if (!this_->gui && !(this_->flags & 2)) { dbg(lvl_error,"FATAL: No GUI available."); exit(1); } if (!this_->gra && !(this_->flags & 1)) { dbg(lvl_error,"FATAL: No graphics subsystem available."); exit(1); } dbg(lvl_info,"Connecting gui to graphics"); if (this_->gui && this_->gra && gui_set_graphics(this_->gui, this_->gra)) { struct attr attr_type_gui, attr_type_graphics; gui_get_attr(this_->gui, attr_type, &attr_type_gui, NULL); graphics_get_attr(this_->gra, attr_type, &attr_type_graphics, NULL); dbg(lvl_error,"FATAL: Failed to connect graphics '%s' to gui '%s'", attr_type_graphics.u.str, attr_type_gui.u.str); dbg(lvl_error,"Please see http://wiki.navit-project.org/index.php/Failed_to_connect_graphics_to_gui " "for explanations and solutions\n"); exit(1); } if (this_->speech && this_->navigation) { struct attr speech; speech.type=attr_speech; speech.u.speech=this_->speech; navigation_set_attr(this_->navigation, &speech); } dbg(lvl_info,"Initializing graphics"); dbg(lvl_info,"Setting Vehicle"); navit_set_vehicle(this_, this_->vehicle); dbg(lvl_info,"Adding dynamic maps to mapset %p",this_->mapsets); if (this_->mapsets) { struct mapset_handle *msh; ms=this_->mapsets->data; this_->progress_cb=callback_new_attr_1(callback_cast(navit_map_progress), attr_progress, this_); msh=mapset_open(ms); while (msh && (map=mapset_next(msh, 0))) { //pass new callback instance for each map in the mapset to make map callback list destruction work correctly struct callback *pcb = callback_new_attr_1(callback_cast(navit_map_progress), attr_progress, this_); map_add_callback(map, pcb); } mapset_close(msh); if (this_->route) { if ((map=route_get_map(this_->route))) { struct attr map_a; map_a.type=attr_map; map_a.u.map=map; mapset_add_attr(ms, &map_a); } if ((map=route_get_graph_map(this_->route))) { struct attr map_a,active; map_a.type=attr_map; map_a.u.map=map; active.type=attr_active; active.u.num=0; mapset_add_attr(ms, &map_a); map_set_attr(map, &active); } route_set_mapset(this_->route, ms); route_set_projection(this_->route, transform_get_projection(this_->trans)); } if (this_->tracking) { tracking_set_mapset(this_->tracking, ms); if (this_->route) tracking_set_route(this_->tracking, this_->route); } attr = g_new0(struct attr, 1); iter = navit_attr_iter_new(NULL); map = NULL; while (navit_get_attr(this_, attr_traffic, attr, iter)) { traffic = (struct traffic *) attr->u.navit_object; traffic_set_mapset(traffic, ms); if (this_->route) traffic_set_route(traffic, this_->route); /* add the first map found */ if (!map && (map = traffic_get_map(traffic))) { struct attr map_a; map_a.type = attr_map; map_a.u.map = map; mapset_add_attr(ms, &map_a); } } navit_attr_iter_destroy(iter); g_free(attr); if (this_->navigation) { if ((map=navigation_get_map(this_->navigation))) { struct attr map_a,active; map_a.type=attr_map; map_a.u.map=map; active.type=attr_active; active.u.num=0; mapset_add_attr(ms, &map_a); map_set_attr(map, &active); } } if (this_->tracking) { if ((map=tracking_get_map(this_->tracking))) { struct attr map_a,active; map_a.type=attr_map; map_a.u.map=map; active.type=attr_active; active.u.num=0; mapset_add_attr(ms, &map_a); map_set_attr(map, &active); } } navit_add_former_destinations_from_file(this_); } else { dbg(lvl_error, "FATAL: No mapset available. Please add a (valid) mapset to your configuration."); exit(1); } if (this_->route) { struct attr callback; this_->route_cb=callback_new_attr_1(callback_cast(navit_redraw_route), attr_route_status, this_); callback.type=attr_callback; callback.u.callback=this_->route_cb; route_add_attr(this_->route, &callback); } if (this_->navigation) { if (this_->speech) { this_->nav_speech_cb=callback_new_1(callback_cast(navit_speak), this_); navigation_register_callback(this_->navigation, attr_navigation_speech, this_->nav_speech_cb); } if (this_->route) navigation_set_route(this_->navigation, this_->route); } dbg(lvl_info,"Setting Center"); center_file = bookmarks_get_center_file(FALSE); bookmarks_set_center_from_file(this_->bookmarks, center_file); g_free(center_file); global_navit=this_; messagelist_init(this_->messages); navit_set_cursors(this_); callback_list_call_attr_1(this_->attr_cbl, attr_navit, this_); callback=(this_->ready == 2); this_->ready|=1; dbg(lvl_info,"ready=%d",this_->ready); if (this_->ready == 3) navit_draw_async(this_, 1); if (callback) callback_list_call_attr_1(this_->attr_cbl, attr_graphics_ready, this_); return 0; } void navit_zoom_to_rect(struct navit *this_, struct coord_rect *r) { struct coord c; int w,h,scale=16; c.x=(r->rl.x+r->lu.x)/2; c.y=(r->rl.y+r->lu.y)/2; transform_set_center(this_->trans, &c); transform_get_size(this_->trans, &w, &h); dbg(lvl_debug,"center 0x%x,0x%x w %d h %d",c.x,c.y,w,h); dbg(lvl_debug,"%x,%x-%x,%x", r->lu.x,r->lu.y,r->rl.x,r->rl.y); while (scale < 1<<20) { struct point p1,p2; transform_set_scale(this_->trans, scale); transform_setup_source_rect(this_->trans); transform_point(this_->trans, transform_get_projection(this_->trans), &r->lu, &p1); transform_point(this_->trans, transform_get_projection(this_->trans), &r->rl, &p2); dbg(lvl_debug,"%d,%d-%d,%d",p1.x,p1.y,p2.x,p2.y); if (p1.x < 0 || p2.x < 0 || p1.x > w || p2.x > w || p1.y < 0 || p2.y < 0 || p1.y > h || p2.y > h) scale*=2; else break; } dbg(lvl_debug,"scale=%d (0x%x) of %d (0x%x)",scale,scale,1<<20,1<<20); if (this_->ready == 3) navit_draw_async(this_,0); } void navit_zoom_to_route(struct navit *this_, int orientation) { struct map *map; struct map_rect *mr=NULL; struct item *item; struct coord c; struct coord_rect r; int count=0; if (! this_->route) return; dbg(lvl_debug,"enter"); map=route_get_map(this_->route); dbg(lvl_debug,"map=%p",map); if (map) mr=map_rect_new(map, NULL); dbg(lvl_debug,"mr=%p",mr); if (mr) { while ((item=map_rect_get_item(mr))) { dbg(lvl_debug,"item=%s", item_to_name(item->type)); while (item_coord_get(item, &c, 1)) { dbg(lvl_debug,"coord"); if (!count) r.lu=r.rl=c; else coord_rect_extend(&r, &c); count++; } } map_rect_destroy(mr); } if (! count) return; if (orientation != -1) transform_set_yaw(this_->trans, orientation); navit_zoom_to_rect(this_, &r); } static int navit_cmd_zoom_to_route(struct navit *this, char *function, struct attr **in, struct attr ***out) { navit_zoom_to_route(this, 0); return 0; } /** * Change the current zoom level * * @param navit The navit instance * @param center The point where to center the map, including its projection * @returns nothing */ void navit_set_center(struct navit *this_, struct pcoord *center, int set_timeout) { struct coord *c=transform_center(this_->trans); struct coord c1,c2; enum projection pro = transform_get_projection(this_->trans); if (pro != center->pro) { c1.x = center->x; c1.y = center->y; transform_from_to(&c1, center->pro, &c2, pro); } else { c2.x = center->x; c2.y = center->y; } *c=c2; if (set_timeout) navit_set_timeout(this_); if (this_->ready == 3) navit_draw(this_); } static void navit_set_center_coord_screen(struct navit *this_, struct coord *c, struct point *p, int set_timeout) { int width, height; struct point po; transform_set_center(this_->trans, c); transform_get_size(this_->trans, &width, &height); po.x=width/2; po.y=height/2; update_transformation(this_->trans, &po, p); if (set_timeout) navit_set_timeout(this_); } /** * Links all vehicles to a cursor depending on the current profile. * * @param this_ A navit instance * @author Ralph Sennhauser (10/2009) */ static void navit_set_cursors(struct navit *this_) { struct attr name; struct navit_vehicle *nv; struct cursor *c; GList *v; v=g_list_first(this_->vehicles); // GList of navit_vehicles while (v) { nv=v->data; if (vehicle_get_attr(nv->vehicle, attr_cursorname, &name, NULL)) { if (!strcmp(name.u.str,"none")) c=NULL; else c=layout_get_cursor(this_->layout_current, name.u.str); } else c=layout_get_cursor(this_->layout_current, "default"); vehicle_set_cursor(nv->vehicle, c, 0); v=g_list_next(v); } return; } /** * @brief Calculates the position of the cursor on the screen. * * This method considers padding if supported by the graphics plugin. In that case, the inner rectangle * (i.e. screen size minus padding) will be used to center the cursor and to determine cursor offset (as * specified in `this_->radius`). * * @param this_ The navit object * @param p Receives the screen coordinates for the cursor * @param keep_orientation Whether to maintain the current map orientation. If false, the map will be * rotated so that the bearing of the vehicle is up. * @param dir Receives the new map orientation as requested by `screen_orientation` (can be `NULL`) * * @return Always 1 */ static int navit_get_cursor_pnt(struct navit *this_, struct point *p, int keep_orientation, int *dir) { int width, height; struct navit_vehicle *nv=this_->vehicle; struct padding *padding = NULL; float offset=this_->radius; // Cursor offset from the center of the screen (percent). #if 0 /* Better improve track.c to get that issue resolved or make it configurable with being off the default, the jumping back to the center is a bit annoying */ float min_offset = 0.; // Percent offset at min_offset_speed. float max_offset = 30.; // Percent offset at max_offset_speed. int min_offset_speed = 2; // Speed in km/h int max_offset_speed = 50; // Speed in km/h // Calculate cursor offset from the center of the screen, upon speed. if (nv->speed <= min_offset_speed) { offset = min_offset; } else if (nv->speed > max_offset_speed) { offset = max_offset; } else { offset = (max_offset - min_offset) / (max_offset_speed - min_offset_speed) * (nv->speed - min_offset_speed); } #endif if (this_->gra) { padding = graphics_get_data(this_->gra, "padding"); } else dbg(lvl_warning, "cannot get padding: this->gra is NULL"); transform_get_size(this_->trans, &width, &height); dbg(lvl_debug, "width=%d height=%d", width, height); if (padding) { width -= (padding->left + padding->right); height -= (padding->top + padding->bottom); dbg(lvl_debug, "corrected for padding: width=%d height=%d", width, height); } if (this_->orientation == -1 || keep_orientation) { p->x=50*width/100; p->y=(50 + offset)*height/100; if (dir) *dir=keep_orientation?this_->orientation:nv->dir; } else { int mdir; if (this_->tracking && this_->tracking_flag) { mdir = tracking_get_angle(this_->tracking) - this_->orientation; } else { mdir=nv->dir-this_->orientation; } p->x=(50 - offset*sin(M_PI*mdir/180.))*width/100; p->y=(50 + offset*cos(M_PI*mdir/180.))*height/100; if (dir) *dir=this_->orientation; } if (padding) { p->x += padding->left; p->y += padding->top; } dbg(lvl_debug, "x=%d y=%d, offset=%f", p->x, p->y, offset); return 1; } /** * @brief Recalculates the map view so that the vehicle cursor is visible * * This function recalculates the parameters which control the visible map area, zoom and orientation. The * caller is responsible for redrawing the map after the function returns. * * If the vehicle supplies a {@code position_valid} attribute and it is {@code attr_position_valid_invalid}, * the map position is not changed. * * @param this_ The navit object * @param autozoom Whether to set zoom based on current speed. If false, current zoom will be maintained. * @param keep_orientation Whether to maintain the current map orientation. If false, the map will be rotated * so that the bearing of the vehicle is up. */ void navit_set_center_cursor(struct navit *this_, int autozoom, int keep_orientation) { int dir; struct point pn; struct navit_vehicle *nv=this_->vehicle; struct attr attr; if (!nv || !nv->vehicle) { return; } if (vehicle_get_attr(nv->vehicle, attr_position_valid, &attr, NULL) && (attr.u.num == attr_position_valid_invalid)) return; navit_get_cursor_pnt(this_, &pn, keep_orientation, &dir); transform_set_yaw(this_->trans, dir); navit_set_center_coord_screen(this_, &nv->coord, &pn, 0); if (autozoom) navit_autozoom(this_, &nv->coord, nv->speed, 0); } /** * @brief Recenters the map so that the vehicle cursor is visible * * This function first calls {@code navit_set_center_cursor()} to recalculate the map display, then * triggers a redraw of the map. * *@param this_ The navit object */ static void navit_set_center_cursor_draw(struct navit *this_) { navit_set_center_cursor(this_,1,0); if (this_->ready == 3) navit_draw_async(this_, 1); } /** * @brief Recenters the map so that the vehicle cursor is visible * * This is the callback function for the {@code set_center_cursor()} command. It is just a wrapper around * {@code navit_set_center_cursor_draw()}. * *@param this_ The navit object */ static int navit_cmd_set_center_cursor(struct navit *this_, char *function, struct attr **in, struct attr ***out) { navit_set_center_cursor_draw(this_); return 0; } void navit_set_center_screen(struct navit *this_, struct point *p, int set_timeout) { struct coord c; struct pcoord pc; transform_reverse(this_->trans, p, &c); pc.x = c.x; pc.y = c.y; pc.pro = transform_get_projection(this_->trans); navit_set_center(this_, &pc, set_timeout); } static int navit_set_attr_do(struct navit *this_, struct attr *attr, int init) { int dir=0, orient_old=0, attr_updated=0; struct coord co; long zoom; GList *l; struct navit_vehicle *nv; struct layout *lay; struct attr active; active.type=attr_active; active.u.num=0; dbg(lvl_debug, "enter, this_=%p, attr=%p (%s), init=%d", this_, attr, attr_to_name(attr->type), init); switch (attr->type) { case attr_autozoom: attr_updated=(this_->autozoom_secs != attr->u.num); this_->autozoom_secs = attr->u.num; break; case attr_autozoom_active: attr_updated=(this_->autozoom_active != attr->u.num); this_->autozoom_active = attr->u.num; break; case attr_center: transform_from_geo(transform_get_projection(this_->trans), attr->u.coord_geo, &co); dbg(lvl_debug,"0x%x,0x%x",co.x,co.y); transform_set_center(this_->trans, &co); break; case attr_drag_bitmap: attr_updated=(this_->drag_bitmap != !!attr->u.num); this_->drag_bitmap=!!attr->u.num; break; case attr_flags: attr_updated=(this_->flags != attr->u.num); this_->flags=attr->u.num; break; case attr_flags_graphics: attr_updated=(this_->graphics_flags != attr->u.num); this_->graphics_flags=attr->u.num; break; case attr_follow: if (!this_->vehicle) return 0; attr_updated=(this_->vehicle->follow_curr != attr->u.num); this_->vehicle->follow_curr = attr->u.num; break; case attr_default_layout: if(!attr->u.str) return 0; if(this_->default_layout_name) /* There is already a default layout, ignore this new value */ g_free(this_->default_layout_name); /* Drop the previous layout name, use the this one instead */ this_->default_layout_name=g_strdup(attr->u.str); attr_updated=1; break; case attr_layout: if(!attr->u.layout) return 0; dbg(lvl_debug,"setting attr_layout to %s", attr->u.layout->name); if(this_->layout_current!=attr->u.layout) { navit_update_current_layout(this_, attr->u.layout); graphics_font_destroy_all(this_->gra); navit_set_cursors(this_); if (this_->ready == 3) navit_draw(this_); attr_updated=1; } break; case attr_layout_name: if(!attr->u.str) return 0; dbg(lvl_debug,"setting attr_layout_name to %s", attr->u.str); l=this_->layouts; while (l) { lay=l->data; if (!strcmp(lay->name,attr->u.str)) { struct attr attr; attr.type=attr_layout; attr.u.layout=lay; return navit_set_attr_do(this_, &attr, init); } l=g_list_next(l); } return 0; case attr_map_border: if (this_->border != attr->u.num) { this_->border=attr->u.num; attr_updated=1; } break; case attr_orientation: orient_old=this_->orientation; this_->orientation=attr->u.num; if (!init) { if (this_->orientation != -1) { dir = this_->orientation; } else { if (this_->vehicle) { dir = this_->vehicle->dir; } } transform_set_yaw(this_->trans, dir); if (orient_old != this_->orientation) { #if 0 if (this_->ready == 3) navit_draw(this_); #endif attr_updated=1; } } break; case attr_osd_configuration: dbg(lvl_debug,"setting osd_configuration to %ld (was %d)", attr->u.num, this_->osd_configuration); attr_updated=(this_->osd_configuration != attr->u.num); this_->osd_configuration=attr->u.num; break; case attr_pitch: attr_updated=(this_->pitch != attr->u.num); this_->pitch=attr->u.num; transform_set_pitch(this_->trans, round(this_->pitch*sqrt(240*320)/sqrt( this_->w*this_->h))); // Pitch corrected for window resolution if (!init && attr_updated && this_->ready == 3) navit_draw(this_); break; case attr_projection: if(this_->trans && transform_get_projection(this_->trans) != attr->u.projection) { navit_projection_set(this_, attr->u.projection, !init); attr_updated=1; } break; case attr_radius: attr_updated=(this_->radius != attr->u.num); this_->radius=attr->u.num; break; case attr_recent_dest: attr_updated=(this_->recentdest_count != attr->u.num); this_->recentdest_count=attr->u.num; break; case attr_speech: if(this_->speech && this_->speech != attr->u.speech) { attr_updated=1; this_->speech = attr->u.speech; } break; case attr_timeout: attr_updated=(this_->center_timeout != attr->u.num); this_->center_timeout = attr->u.num; break; case attr_tracking: attr_updated=(this_->tracking_flag != !!attr->u.num); this_->tracking_flag=!!attr->u.num; break; case attr_transformation: this_->trans=attr->u.transformation; break; case attr_use_mousewheel: attr_updated=(this_->use_mousewheel != !!attr->u.num); this_->use_mousewheel=!!attr->u.num; break; case attr_vehicle: if (!attr->u.vehicle) { if (this_->vehicle) { vehicle_set_attr(this_->vehicle->vehicle, &active); navit_set_vehicle(this_, NULL); attr_updated=1; } break; } l=this_->vehicles; while(l) { nv=l->data; if (nv->vehicle == attr->u.vehicle) { if (!this_->vehicle || this_->vehicle->vehicle != attr->u.vehicle) { if (this_->vehicle) vehicle_set_attr(this_->vehicle->vehicle, &active); active.u.num=1; vehicle_set_attr(nv->vehicle, &active); attr_updated=1; } navit_set_vehicle(this_, nv); } l=g_list_next(l); } break; case attr_vehicleprofile: attr_updated=navit_set_vehicleprofile(this_, attr->u.vehicleprofile); break; case attr_zoom: zoom=transform_get_scale(this_->trans); attr_updated=(zoom != attr->u.num); transform_set_scale(this_->trans, attr->u.num); if (attr_updated && !init) navit_draw(this_); break; case attr_zoom_min: attr_updated=(attr->u.num != this_->zoom_min); this_->zoom_min=attr->u.num; break; case attr_zoom_max: attr_updated=(attr->u.num != this_->zoom_max); this_->zoom_max=attr->u.num; break; case attr_message: navit_add_message(this_, attr->u.str); break; case attr_follow_cursor: attr_updated=(this_->follow_cursor != !!attr->u.num); this_->follow_cursor=!!attr->u.num; break; case attr_imperial: attr_updated=(this_->imperial != attr->u.num); this_->imperial=attr->u.num; break; case attr_waypoints_flag: attr_updated=(this_->waypoints_flag != !!attr->u.num); this_->waypoints_flag=!!attr->u.num; break; case attr_tunnel_nightlayout: attr_updated = (this_->tunnel_nightlayout != !!attr->u.num); this_->tunnel_nightlayout = !!attr->u.num; break; case attr_layout_daynightauto: attr_updated = (this_->auto_switch != !!attr->u.num); this_->auto_switch = !!attr->u.num; break; case attr_sunrise_degrees: attr_updated = (this_->sunrise_degrees != attr->u.num); this_->sunrise_degrees = attr->u.num; break; default: dbg(lvl_debug, "calling generic setter method for attribute type %s", attr_to_name(attr->type)) return navit_object_set_attr((struct navit_object *) this_, attr); } if (attr_updated && !init) { callback_list_call_attr_2(this_->attr_cbl, attr->type, this_, attr); if (attr->type == attr_osd_configuration) graphics_draw_mode(this_->gra, draw_mode_end); } return 1; } int navit_set_attr(struct navit *this_, struct attr *attr) { return navit_set_attr_do(this_, attr, 0); } int navit_get_attr(struct navit *this_, enum attr_type type, struct attr *attr, struct attr_iter *iter) { struct message *msg; struct coord *c; int len,offset; int ret=1; switch (type) { case attr_message: msg = navit_get_messages(this_); if (!msg) { return 0; } len = 0; while (msg) { len += strlen(msg->text) + 1; msg = msg->next; } attr->u.str = g_malloc(len + 1); msg = navit_get_messages(this_); offset = 0; while (msg) { g_stpcpy((attr->u.str + offset), msg->text); offset += strlen(msg->text); attr->u.str[offset] = '\n'; offset++; msg = msg->next; } attr->u.str[len] = '\0'; break; case attr_imperial: attr->u.num=this_->imperial; break; case attr_bookmark_map: attr->u.map=bookmarks_get_map(this_->bookmarks); break; case attr_bookmarks: attr->u.bookmarks=this_->bookmarks; break; case attr_callback_list: attr->u.callback_list=this_->attr_cbl; break; case attr_center: c=transform_get_center(this_->trans); transform_to_geo(transform_get_projection(this_->trans), c, &this_->center); attr->u.coord_geo=&this_->center; break; case attr_destination: if (! this_->destination_valid) return 0; attr->u.pcoord=&this_->destination; break; case attr_displaylist: attr->u.displaylist=this_->displaylist; return (attr->u.displaylist != NULL); case attr_follow: if (!this_->vehicle) return 0; attr->u.num=this_->vehicle->follow_curr; break; case attr_former_destination_map: attr->u.map=this_->former_destination; break; case attr_graphics: attr->u.graphics=this_->gra; ret=(attr->u.graphics != NULL); break; case attr_gui: attr->u.gui=this_->gui; ret=(attr->u.gui != NULL); break; case attr_layer: ret=attr_generic_get_attr(this_->attrs, NULL, type, attr, iter?(struct attr_iter *)&iter->iter:NULL); break; case attr_layout: if (iter) { if (iter->u.list) { iter->u.list=g_list_next(iter->u.list); } else { iter->u.list=this_->layouts; } if (!iter->u.list) return 0; attr->u.layout=(struct layout *)iter->u.list->data; } else { attr->u.layout=this_->layout_current; } break; case attr_map: if (iter && this_->mapsets) { if (!iter->u.mapset_handle) { iter->u.mapset_handle=mapset_open((struct mapset *)this_->mapsets->data); } attr->u.map=mapset_next(iter->u.mapset_handle, 0); if(!attr->u.map) { mapset_close(iter->u.mapset_handle); return 0; } } else { return 0; } break; case attr_mapset: attr->u.mapset=this_->mapsets->data; ret=(attr->u.mapset != NULL); break; case attr_navigation: attr->u.navigation=this_->navigation; break; case attr_orientation: attr->u.num=this_->orientation; break; case attr_osd: ret=attr_generic_get_attr(this_->attrs, NULL, type, attr, iter?(struct attr_iter *)&iter->iter:NULL); break; case attr_osd_configuration: attr->u.num=this_->osd_configuration; break; case attr_pitch: attr->u.num=round(transform_get_pitch(this_->trans)*sqrt(this_->w*this_->h)/sqrt( 240*320)); // Pitch corrected for window resolution break; case attr_projection: if(this_->trans) { attr->u.num=transform_get_projection(this_->trans); } else { return 0; } break; case attr_route: attr->u.route=this_->route; break; case attr_speech: if(this_->speech) { attr->u.speech=this_->speech; } else { return 0; } break; case attr_timeout: attr->u.num=this_->center_timeout; break; case attr_tracking: attr->u.num=this_->tracking_flag; break; case attr_trackingo: attr->u.tracking=this_->tracking; break; case attr_transformation: attr->u.transformation=this_->trans; break; case attr_vehicle: if(iter) { if(iter->u.list) { iter->u.list=g_list_next(iter->u.list); } else { iter->u.list=this_->vehicles; } if(!iter->u.list) return 0; attr->u.vehicle=((struct navit_vehicle*)iter->u.list->data)->vehicle; } else { if(this_->vehicle) { attr->u.vehicle=this_->vehicle->vehicle; } else { return 0; } } break; case attr_vehicleprofile: if (iter) { if(iter->u.list) { iter->u.list=g_list_next(iter->u.list); } else { iter->u.list=this_->vehicleprofiles; } if(!iter->u.list) return 0; attr->u.vehicleprofile=iter->u.list->data; } else { attr->u.vehicleprofile=this_->vehicleprofile; } break; case attr_zoom: attr->u.num=transform_get_scale(this_->trans); break; case attr_autozoom_active: attr->u.num=this_->autozoom_active; break; case attr_follow_cursor: attr->u.num=this_->follow_cursor; break; case attr_waypoints_flag: attr->u.num=this_->waypoints_flag; break; case attr_tunnel_nightlayout: attr->u.num=this_->tunnel_nightlayout; break; case attr_layout_daynightauto: attr->u.num=this_->auto_switch; break; case attr_sunrise_degrees: attr->u.num=this_->sunrise_degrees; break; default: dbg(lvl_debug, "calling generic getter method for attribute type %s", attr_to_name(type)) return navit_object_get_attr((struct navit_object *) this_, type, attr, iter); } attr->type=type; return ret; } /** * @brief Select the default layout by name * * @param this_ The navit instance * @param name The new default layout's name * * @return The first layout match (if any), or NULL if there was no match */ struct layout *navit_get_layout_by_name(struct navit *this_, const char *layout_name) { struct attr_iter *iter; struct attr layout_attr; struct layout *result = NULL; if (!layout_name) return NULL; iter=navit_attr_iter_new(NULL); while (navit_get_attr(this_, attr_layout, &layout_attr, iter)) { if (strcmp(layout_attr.u.layout->name, layout_name) == 0) { result = layout_attr.u.layout; } } navit_attr_iter_destroy(iter); return result; } /** * @brief Set the current layout * * @param this_ The navit instance * @param layout The layout to set as default (if NULL, we will set the current layout according to the default name stored in this_->default_layout_name * * @note If argument @p layout is NULL and the default layout name in the config file does not exist or has not been provided in the config file, the default layout is unchanged */ void navit_update_current_layout(struct navit *this_, struct layout *layout) { struct layout *default_layout = NULL; if (layout) { this_->layout_current=layout; } else { if (this_->default_layout_name) { /* If a default layout name was provided */ default_layout=navit_get_layout_by_name(this_, this_->default_layout_name); if (default_layout) { dbg(lvl_debug, "Found the config-specified default layout '%s'", this_->default_layout_name); this_->layout_current=default_layout; return; } else { dbg(lvl_warning, "No definition exists in config for specified default layout '%s'", this_->default_layout_name); } } } } static int navit_add_log(struct navit *this_, struct log *log) { struct attr type_attr; if (!log_get_attr(log, attr_type, &type_attr, NULL)) return 0; if (!strcmp(type_attr.u.str, "textfile_debug")) { char *header = "type=track_tracked\n"; if (this_->textfile_debug_log) return 0; log_set_header(log, header, strlen(header)); this_->textfile_debug_log=log; return 1; } return 0; } static int navit_add_layout(struct navit *this_, struct layout *layout) { struct attr active; int is_default=0; int is_active=0; this_->layouts = g_list_append(this_->layouts, layout); /** check if we want to immediately activate this layout. * Unfortunately we have concurring conditions about when to activate * a layout: * - A layout could bear the "active" property * - A layout's name could match this_->default_layout_name * This cannot be fully resolved, as we cannot predict the future, so * lets set the last parsed layout active, which either matches default_layout_name or * bears the "active" tag, or is the first layout ever parsed. */ if((layout->name != NULL) && (this_->default_layout_name != NULL)) { if (strcmp(layout->name, this_->default_layout_name) == 0) is_default = 1; } layout_get_attr(layout, attr_active, &active, NULL); if(active.u.num) is_active = 1; dbg(lvl_debug, "add layout '%s' is_default %d, is_active %d", layout->name, is_default, is_active); if(is_default || is_active || !this_->layout_current) { this_->layout_current=layout; return 1; } return 0; } int navit_add_attr(struct navit *this_, struct attr *attr) { int ret=1; switch (attr->type) { case attr_callback: navit_add_callback(this_, attr->u.callback); break; case attr_log: ret=navit_add_log(this_, attr->u.log); break; case attr_gui: ret=navit_set_gui(this_, attr->u.gui); break; case attr_graphics: ret=navit_set_graphics(this_, attr->u.graphics); break; case attr_layout: navit_add_layout(this_, attr->u.layout); break; case attr_route: this_->route=attr->u.route; break; case attr_mapset: this_->mapsets = g_list_append(this_->mapsets, attr->u.mapset); break; case attr_navigation: this_->navigation=attr->u.navigation; break; case attr_osd: break; case attr_recent_dest: this_->recentdest_count = attr->u.num; break; case attr_speech: this_->speech=attr->u.speech; break; case attr_trackingo: this_->tracking=attr->u.tracking; break; case attr_vehicle: ret=navit_add_vehicle(this_, attr->u.vehicle); break; case attr_vehicleprofile: this_->vehicleprofiles=g_list_append(this_->vehicleprofiles, attr->u.vehicleprofile); break; case attr_autozoom_min: this_->autozoom_min = attr->u.num; break; case attr_autozoom_max: this_->autozoom_max = attr->u.num; break; case attr_layer: case attr_script: case attr_traffic: break; default: return 0; } if (ret) this_->attrs=attr_generic_add_attr(this_->attrs, attr); callback_list_call_attr_2(this_->attr_cbl, attr->type, this_, attr); return ret; } int navit_remove_attr(struct navit *this_, struct attr *attr) { int ret=1; switch (attr->type) { case attr_callback: navit_remove_callback(this_, attr->u.callback); break; case attr_vehicle: case attr_osd: this_->attrs=attr_generic_remove_attr(this_->attrs, attr); return 1; default: return 0; } return ret; } struct attr_iter * navit_attr_iter_new(void * unused) { return g_new0(struct attr_iter, 1); } void navit_attr_iter_destroy(struct attr_iter *iter) { g_free(iter); } void navit_add_callback(struct navit *this_, struct callback *cb) { callback_list_add(this_->attr_cbl, cb); } void navit_remove_callback(struct navit *this_, struct callback *cb) { callback_list_remove(this_->attr_cbl, cb); } static int coord_not_set(struct coord c) { return !(c.x || c.y); } /** * Toggle the cursor update : refresh the map each time the cursor has moved (instead of only when it reaches a border) * * @param this_ The navit instance * @param nv vehicle to draw * @param pnt Screen coordinates of the vehicle. If NULL, position stored in nv is used. * @returns nothing */ static void navit_vehicle_draw(struct navit *this_, struct navit_vehicle *nv, struct point *pnt) { struct point cursor_pnt; enum projection pro; if (this_->blocked||coord_not_set(nv->coord)) return; if (pnt) cursor_pnt=*pnt; else { pro=transform_get_projection(this_->trans_cursor); if (!pro) return; transform_point(this_->trans_cursor, pro, &nv->coord, &cursor_pnt); } vehicle_draw(nv->vehicle, this_->gra, &cursor_pnt, nv->dir-transform_get_yaw(this_->trans_cursor), nv->speed); } /** * @brief Called when the position of a vehicle changes. * * This function is called when the position of any configured vehicle changes and triggers all actions * that need to happen in response, such as: *
    *
  • Switching between day and night layout (based on the new position timestamp)
  • *
  • Updating position, bearing and speed of {@code nv} with the data of the active vehicle * (which may be different from the vehicle reporting the update)
  • *
  • Invoking callbacks for {@code navit}'s {@code attr_position} and {@code attr_position_coord_geo} * attributes
  • *
  • Triggering an update of the vehicle's position on the map and, if needed, an update of the * visible map area ad orientation
  • *
  • Logging a new track point, if enabled
  • *
  • Updating the position on the route
  • *
  • Stopping navigation if the destination has been reached
  • *
* * @param this_ The navit object * @param nv The {@code navit_vehicle} which reported a new position */ static void navit_vehicle_update_position(struct navit *this_, struct navit_vehicle *nv) { struct attr attr_valid, attr_dir, attr_speed, attr_pos; struct pcoord cursor_pc; struct point cursor_pnt, *pnt=&cursor_pnt; struct tracking *tracking=NULL; struct pcoord *pc; enum projection pro=transform_get_projection(this_->trans_cursor); int count; int (*get_attr)(void *, enum attr_type, struct attr *, struct attr_iter *); void *attr_object; char *destination_file; char *description; profile(0,NULL); if (this_->ready == 3) navit_layout_switch(this_); if (this_->vehicle == nv && this_->tracking_flag) tracking=this_->tracking; if (tracking) { tracking_update(tracking, nv->vehicle, this_->vehicleprofile, pro); attr_object=tracking; get_attr=(int (*)(void *, enum attr_type, struct attr *, struct attr_iter *))tracking_get_attr; } else { attr_object=nv->vehicle; get_attr=(int (*)(void *, enum attr_type, struct attr *, struct attr_iter *))vehicle_get_attr; } if (get_attr(attr_object, attr_position_valid, &attr_valid, NULL)) if (!attr_valid.u.num != attr_position_valid_invalid) return; if (! get_attr(attr_object, attr_position_direction, &attr_dir, NULL) || ! get_attr(attr_object, attr_position_speed, &attr_speed, NULL) || ! get_attr(attr_object, attr_position_coord_geo, &attr_pos, NULL)) { profile(0,"return 2\n"); return; } nv->dir=*attr_dir.u.numd; nv->speed=*attr_speed.u.numd; transform_from_geo(pro, attr_pos.u.coord_geo, &nv->coord); if (nv != this_->vehicle) { if (this_->ready == 3) navit_vehicle_draw(this_, nv, NULL); profile(0,"return 3\n"); return; } cursor_pc.x = nv->coord.x; cursor_pc.y = nv->coord.y; cursor_pc.pro = pro; if (this_->route) { if (tracking) route_set_position_from_tracking(this_->route, tracking, pro); else route_set_position(this_->route, &cursor_pc); } callback_list_call_attr_0(this_->attr_cbl, attr_position); navit_textfile_debug_log(this_, "type=trackpoint_tracked"); if (this_->ready == 3) { if (this_->gui && nv->speed > 2) navit_disable_suspend(); transform_point(this_->trans_cursor, pro, &nv->coord, &cursor_pnt); if (this_->button_pressed != 1 && this_->follow_cursor && nv->follow_curr <= nv->follow && (nv->follow_curr == 1 || !transform_within_border(this_->trans_cursor, &cursor_pnt, this_->border))) navit_set_center_cursor_draw(this_); else navit_vehicle_draw(this_, nv, pnt); if (nv->follow_curr > 1) nv->follow_curr--; else nv->follow_curr=nv->follow; } callback_list_call_attr_2(this_->attr_cbl, attr_position_coord_geo, this_, nv->vehicle); /* Finally, if we reached our destination, stop navigation. */ if (this_->route) { switch(route_destination_reached(this_->route)) { case 1: description=route_get_destination_description(this_->route, 0); route_remove_waypoint(this_->route); count=route_get_destination_count(this_->route); pc=g_alloca(sizeof(*pc)*count); route_get_destinations(this_->route, pc, count); destination_file = bookmarks_get_destination_file(TRUE); bookmarks_append_destinations(this_->former_destination, destination_file, pc, count, type_former_itinerary_part, description, this_->recentdest_count); g_free(destination_file); g_free(description); break; case 2: destination_file = bookmarks_get_destination_file(TRUE); bookmarks_append_destinations(this_->former_destination, destination_file, NULL, 0, type_former_itinerary_part, NULL, this_->recentdest_count); navit_set_destination(this_, NULL, NULL, 0); g_free(destination_file); break; } } profile(0,"return 5\n"); } /** * @brief Called when a status attribute of a vehicle changes. * * This function is called when the {@code position_fix_type}, {@code position_sats_used} or {@code position_hdop} * attribute of any configured vehicle changes. * * The function checks if {@code nv} refers to the active vehicle and if {@code type} is one of the above types. * If this is the case, it invokes the callback functions for {@code navit}'s respective attributes. * * Future actions that need to happen when one of these three attribute changes for any vehicle should be * implemented here. * * @param this_ The navit object * @param nv The {@code navit_vehicle} which reported a new status attribute * @param type The type of attribute with has changed */ static void navit_vehicle_update_status(struct navit *this_, struct navit_vehicle *nv, enum attr_type type) { if (this_->vehicle != nv) return; switch(type) { case attr_position_fix_type: case attr_position_sats_used: case attr_position_hdop: callback_list_call_attr_2(this_->attr_cbl, type, this_, nv->vehicle); break; default: return; } } /** * Set the position of the vehicle * * @param navit The navit instance * @param c The coordinate to set as position * @returns nothing */ void navit_set_position(struct navit *this_, struct pcoord *c) { if (this_->route) { route_set_position(this_->route, c); callback_list_call_attr_0(this_->attr_cbl, attr_position); } if (this_->ready == 3) navit_draw(this_); } static int navit_set_vehicleprofile(struct navit *this_, struct vehicleprofile *vp) { if (this_->vehicleprofile == vp) return 0; this_->vehicleprofile=vp; if (this_->route) route_set_profile(this_->route, this_->vehicleprofile); return 1; } int navit_set_vehicleprofile_name(struct navit *this_, char *name) { struct attr attr; GList *l; l=this_->vehicleprofiles; while (l) { if (vehicleprofile_get_attr(l->data, attr_name, &attr, NULL)) { if (!strcmp(attr.u.str, name)) { navit_set_vehicleprofile(this_, l->data); return 1; } } l=g_list_next(l); } return 0; } static void navit_set_vehicle(struct navit *this_, struct navit_vehicle *nv) { struct attr attr; this_->vehicle=nv; if (nv && vehicle_get_attr(nv->vehicle, attr_profilename, &attr, NULL)) { if (navit_set_vehicleprofile_name(this_, attr.u.str)) return; } if (!this_->vehicleprofile) { // When deactivating vehicle, keep the last profile if any if (!navit_set_vehicleprofile_name(this_,"car")) { /* We do not have a fallback "car" profile * so lets set any profile */ GList *l; l=this_->vehicleprofiles; if (l) { this_->vehicleprofile=l->data; if (this_->route) route_set_profile(this_->route, this_->vehicleprofile); } } } else { if (this_->route) route_set_profile(this_->route, this_->vehicleprofile); } } /** * @brief Registers a new vehicle. * * @param this_ The navit instance * @param v The vehicle to register * @return True for success */ static int navit_add_vehicle(struct navit *this_, struct vehicle *v) { struct navit_vehicle *nv=g_new0(struct navit_vehicle, 1); struct attr follow, active, animate; nv->vehicle=v; nv->follow=0; nv->last.x = 0; nv->last.y = 0; nv->animate_cursor=0; if ((vehicle_get_attr(v, attr_follow, &follow, NULL))) nv->follow=follow.u.num; nv->follow_curr=nv->follow; this_->vehicles=g_list_append(this_->vehicles, nv); if ((vehicle_get_attr(v, attr_active, &active, NULL)) && active.u.num) navit_set_vehicle(this_, nv); if ((vehicle_get_attr(v, attr_animate, &animate, NULL))) nv->animate_cursor=animate.u.num; nv->callback.type=attr_callback; nv->callback.u.callback=callback_new_attr_2(callback_cast(navit_vehicle_update_position), attr_position_coord_geo, this_, nv); vehicle_add_attr(nv->vehicle, &nv->callback); nv->callback.u.callback=callback_new_attr_3(callback_cast(navit_vehicle_update_status), attr_position_fix_type, this_, nv, attr_position_fix_type); vehicle_add_attr(nv->vehicle, &nv->callback); nv->callback.u.callback=callback_new_attr_3(callback_cast(navit_vehicle_update_status), attr_position_sats_used, this_, nv, attr_position_sats_used); vehicle_add_attr(nv->vehicle, &nv->callback); nv->callback.u.callback=callback_new_attr_3(callback_cast(navit_vehicle_update_status), attr_position_hdop, this_, nv, attr_position_hdop); vehicle_add_attr(nv->vehicle, &nv->callback); vehicle_set_attr(nv->vehicle, &this_->self); return 1; } struct gui * navit_get_gui(struct navit *this_) { return this_->gui; } struct transformation * navit_get_trans(struct navit *this_) { return this_->trans; } struct route * navit_get_route(struct navit *this_) { return this_->route; } struct navigation * navit_get_navigation(struct navit *this_) { return this_->navigation; } struct displaylist * navit_get_displaylist(struct navit *this_) { return this_->displaylist; } void navit_layout_switch(struct navit *n) { int currTs=0; struct attr iso8601_attr,geo_attr,valid_attr,layout_attr; double trise,tset; struct layout *l; int year, month, day; int after_sunrise = FALSE; int after_sunset = FALSE; int tunnel = tracking_get_current_tunnel(n->tracking); if (navit_get_attr(n,attr_layout,&layout_attr,NULL)!=1) { return; //No layout - nothing to switch } if (!n->vehicle) return; l=layout_attr.u.layout; if (l->dayname || l->nightname) { //Ok, we know that we have profile to switch //Check that we aren't calculating too fast if (vehicle_get_attr(n->vehicle->vehicle, attr_position_time_iso8601,&iso8601_attr,NULL)==1) { currTs=iso8601_to_secs(iso8601_attr.u.str); dbg(lvl_debug,"currTs: %02u:%02u",currTs%86400/3600,((currTs%86400)%3600)/60); } dbg(lvl_debug,"prevTs: %02u:%02u",n->prevTs%86400/3600,((n->prevTs%86400)%3600)/60); if (n->auto_switch == FALSE) return; if (n->tunnel_nightlayout) { if (tunnel) { // store the current layout name if(!strcmp(n->layout_before_tunnel, "")) n->layout_before_tunnel = n->layout_current->name; // We are in a tunnel and if we have a nightlayout -> switch to nightlayout if (l->nightname) { navit_set_layout_by_name(n, l->nightname); dbg(lvl_debug, "tunnel -> nightlayout"); } return; } else { if (l->dayname) { if (!strcmp(l->dayname, n->layout_before_tunnel)) { // restore previous layout navit_set_layout_by_name(n, l->dayname); dbg(lvl_debug, "tunnel end -> daylayout"); } // We were in nightlayout before the tunnel, keep it n->layout_before_tunnel=""; } } } if (currTs-(n->prevTs)<60) { //We've have to wait a little return; } if (sscanf(iso8601_attr.u.str,"%d-%02d-%02dT",&year,&month,&day) != 3) return; if (vehicle_get_attr(n->vehicle->vehicle, attr_position_valid, &valid_attr,NULL) && valid_attr.u.num==attr_position_valid_invalid) { return; //No valid fix yet } if (vehicle_get_attr(n->vehicle->vehicle, attr_position_coord_geo,&geo_attr,NULL)!=1) { //No position - no sun return; } //We calculate sunrise anyway, cause it is needed both for day and for night if (__sunriset__(year,month,day,geo_attr.u.coord_geo->lng,geo_attr.u.coord_geo->lat,n->sunrise_degrees,1,&trise, &tset)!=0) { dbg(lvl_debug,"near the pole sun never rises/sets, so we should never switch profiles"); dbg(lvl_debug,"trise: %02u:%02u",HOURS(trise),MINUTES(trise)); dbg(lvl_debug,"tset: %02u:%02u",HOURS(tset),MINUTES(tset)); n->prevTs=currTs; return; } dbg(lvl_debug,"trise: %02u:%02u",HOURS(trise),MINUTES(trise)); dbg(lvl_debug,"tset: %02u:%02u",HOURS(tset),MINUTES(tset)); dbg(lvl_debug,"dayname = %s, name =%s ",l->dayname, l->name); dbg(lvl_debug,"nightname = %s, name = %s ",l->nightname, l->name); // We want any times to be in [0;1439]. if(trise<0) trise+=24; if(tset>=24) tset-=24; int trmin=HOURS(trise)*60+MINUTES(trise); // rise time in minutes int tsmin=HOURS(tset)*60+MINUTES(tset); // set time in minutes int tcur=(currTs%86400)/60; // minutes elapsed today if (trmin<=tcur) { // use <= to have defined values for after_sunrise for any value of tcur after_sunrise = TRUE; } if (tsmin>trmin) { // set > rise if (trmin<=tcur) { after_sunrise = TRUE; } if (tsmin<=tcur || trmin>tcur) { after_sunset = TRUE; } } else { // rise > set if((tcur<=tsmin) || (tcur>=trmin)) { after_sunrise = TRUE; } if(tcur>tsmin && tcurdayname) { navit_set_layout_by_name(n,l->dayname); dbg(lvl_debug,"layout set to day"); } else if (after_sunset && l->nightname) { navit_set_layout_by_name(n,l->nightname); dbg(lvl_debug,"layout set to night"); } n->prevTs=currTs; } } /** * @brief this command is used to change the layout and enable/disable the automatic layout switcher * * @param this_ The navit instance * @param function unused * @param in input attributes in[0], a string, see usage below * @param out output attribute unused * * * usage : * manual : disable autoswitcher * auto : enable autoswitcher * manual_toggle : disable autoswitcher and toggle between day / night layout * manual_day : disable autoswitcher and set to day layout * manual_night : disable autoswitcher and set to night layout * * todo : make it return the state of the autoswitcher and * the version of the active layout (day/night/undefined) */ static int navit_cmd_switch_layout_day_night(struct navit *this_, char *function, struct attr **in, struct attr *** out) { if (!(in && in[0] && ATTR_IS_STRING(in[0]->type))) { return 0; } dbg(lvl_debug," called with mode =%s",in[0]->u.str); if (!this_->layout_current) return 0; if (!this_->vehicle) return 0; if (!strcmp(in[0]->u.str,"manual")) { this_->auto_switch = FALSE; } else if (!strcmp(in[0]->u.str,"auto")) { this_->auto_switch = TRUE; this_->prevTs = 0; navit_layout_switch(this_); } else if (!strcmp(in[0]->u.str,"manual_toggle")) { if (this_->layout_current->dayname) { navit_set_layout_by_name(this_,this_->layout_current->dayname); this_->auto_switch = FALSE; dbg(lvl_debug,"toggeled layout to = %s",this_->layout_current->name); } else if (this_->layout_current->nightname) { navit_set_layout_by_name(this_,this_->layout_current->nightname); this_->auto_switch = FALSE; dbg(lvl_debug,"toggeled layout to = %s",this_->layout_current->name); } } else if (!strcmp(in[0]->u.str,"manual_day") && this_->layout_current->dayname) { navit_set_layout_by_name(this_,this_->layout_current->dayname); this_->auto_switch = FALSE; dbg(lvl_debug,"switched layout to = %s",this_->layout_current->name); } else if (!strcmp(in[0]->u.str,"manual_night") && this_->layout_current->nightname) { navit_set_layout_by_name(this_,this_->layout_current->nightname); this_->auto_switch = FALSE; dbg(lvl_debug,"switched layout to = %s",this_->layout_current->name); } dbg(lvl_debug,"auto = %i",this_->auto_switch); return 0; } int navit_set_vehicle_by_name(struct navit *n,const char *name) { struct vehicle *v; struct attr_iter *iter; struct attr vehicle_attr, name_attr; iter=navit_attr_iter_new(NULL); while (navit_get_attr(n,attr_vehicle,&vehicle_attr,iter)) { v=vehicle_attr.u.vehicle; vehicle_get_attr(v,attr_name,&name_attr,NULL); if (name_attr.type==attr_name) { if (!strcmp(name,name_attr.u.str)) { navit_set_attr(n,&vehicle_attr); navit_attr_iter_destroy(iter); return 1; } } } navit_attr_iter_destroy(iter); return 0; } int navit_set_layout_by_name(struct navit *n,const char *name) { struct layout *l; struct attr_iter iter; struct attr layout_attr; iter.u.list=0x00; if (navit_get_attr(n,attr_layout,&layout_attr,&iter)!=1) { return 0; //No layouts - nothing to do } if (iter.u.list==NULL) { return 0; } iter.u.list=g_list_first(iter.u.list); while(iter.u.list) { l=(struct layout*)iter.u.list->data; if (!strcmp(name,l->name)) { layout_attr.u.layout=l; layout_attr.type=attr_layout; navit_set_attr(n,&layout_attr); iter.u.list=g_list_first(iter.u.list); return 1; } iter.u.list=g_list_next(iter.u.list); } iter.u.list=g_list_first(iter.u.list); return 0; } void navit_disable_suspend() { gui_disable_suspend(global_navit->gui); callback_list_call_attr_0(global_navit->attr_cbl,attr_unsuspend); } /** * @brief Blocks or unblocks redraw operations. * * The {@code block} parameter specifies the operation to carry out: * * {@code block > 0} cancels all draw operations in progress and blocks future operations. It sets flag 1 of the * {@code blocked} member. If draw operations in progress were canceled, flag 2 is also set. * * {@code block = 0} unblocks redraw operations, resetting {@code blocked} to 0. If flag 2 was previously set, * indicating that draw operations had been previously canceled, a redraw is triggered. * * {@code block < 0} unblocks redraw operations and forces a redraw. As above, {@code blocked} is reset to 0. * * @param this_ The navit instance * @param block The operation to perform, see description * * @return {@code true} if a redraw operation was triggered, {@code false} if not */ int navit_block(struct navit *this_, int block) { if (block > 0) { this_->blocked |= 1; if (graphics_draw_cancel(this_->gra, this_->displaylist)) this_->blocked |= 2; return 0; } if ((this_->blocked & 2) || block < 0) { this_->blocked=0; navit_draw(this_); return 1; } this_->blocked=0; return 0; } /** * @brief Returns whether redraw operations are currently blocked. */ int navit_get_blocked(struct navit *this_) { return this_->blocked; } void navit_destroy(struct navit *this_) { dbg(lvl_debug,"enter %p",this_); GList *mapsets; struct map * map; struct attr attr; graphics_draw_cancel(this_->gra, this_->displaylist); mapsets = this_->mapsets; while (mapsets) { GList *maps = NULL; struct mapset_handle *msh; msh = mapset_open(mapsets->data); while (msh && (map = mapset_next(msh, 0))) { /* Add traffic map (identified by the `attr_traffic` attribute) to list of maps to remove */ if (map_get_attr(map, attr_traffic, &attr, NULL)) maps = g_list_append(maps, map); } mapset_close(msh); /* Remove traffic maps, if any */ while (maps) { attr.type = attr_map; attr.u.map = maps->data; mapset_remove_attr(mapsets->data, &attr); attr_free_content(&attr); maps = g_list_next(maps); } if (maps) g_list_free(maps); mapsets = g_list_next(mapsets); } callback_list_call_attr_1(this_->attr_cbl, attr_destroy, this_); attr_list_free(this_->attrs); if(cmd_int_var_hash) { g_hash_table_destroy(cmd_int_var_hash); cmd_int_var_hash=NULL; } if(cmd_attr_var_hash) { g_hash_table_destroy(cmd_attr_var_hash); cmd_attr_var_hash=NULL; } if(cmd_int_var_stack) { g_list_foreach(cmd_int_var_stack, (GFunc)attr_free_g, NULL); g_list_free(cmd_int_var_stack); cmd_int_var_stack=NULL; } if (this_->bookmarks) { char *center_file = bookmarks_get_center_file(TRUE); bookmarks_write_center_to_file(this_->bookmarks, center_file); g_free(center_file); bookmarks_destroy(this_->bookmarks); } callback_destroy(this_->nav_speech_cb); callback_destroy(this_->roadbook_callback); callback_destroy(this_->popup_callback); callback_destroy(this_->motion_timeout_callback); callback_destroy(this_->progress_cb); if(this_->gra) { graphics_remove_callback(this_->gra, this_->resize_callback); graphics_remove_callback(this_->gra, this_->button_callback); graphics_remove_callback(this_->gra, this_->motion_callback); graphics_remove_callback(this_->gra, this_->predraw_callback); } callback_destroy(this_->resize_callback); callback_destroy(this_->motion_callback); callback_destroy(this_->predraw_callback); callback_destroy(this_->route_cb); if (this_->route) route_destroy(this_->route); map_destroy(this_->former_destination); graphics_displaylist_destroy(this_->displaylist); graphics_free(this_->gra); g_free(this_); } struct object_func navit_func = { attr_navit, (object_func_new)navit_new, (object_func_get_attr)navit_get_attr, (object_func_iter_new)navit_attr_iter_new, (object_func_iter_destroy)navit_attr_iter_destroy, (object_func_set_attr)navit_set_attr, (object_func_add_attr)navit_add_attr, (object_func_remove_attr)navit_remove_attr, (object_func_init)navit_init, (object_func_destroy)navit_destroy, (object_func_dup)NULL, (object_func_ref)navit_object_ref, (object_func_unref)navit_object_unref, }; /** @} */