diff options
author | lains <lains@caramail.com> | 2019-03-05 16:26:59 +0100 |
---|---|---|
committer | jkoan <jkoan@users.noreply.github.com> | 2019-03-05 16:26:59 +0100 |
commit | cc781eec9b8825d1ffbbb4798e2b86ac2d08be62 (patch) | |
tree | 2a15abeec06946b26b6fc768211e420bdae307df | |
parent | 5352c402c7aeef45206d99577217f20a283fb8b1 (diff) | |
download | navit-cc781eec9b8825d1ffbbb4798e2b86ac2d08be62.tar.gz |
Refactor:GUI/GTK:Adding pointer and label when displaying POI on map (#729)
* Moving get_search_results_map from internal gui to generic navit functions
* Moving square_shape_str() and populate_search_results_map() in generic files (not making them gui_internal-specific)
* Adding GList cleanup after adding search results to a map, setting result names properly in GTK POI
* Using common navit_populate_search_results_map() in internal GUI (factorizing code)
* Adding doxygen comments
* Reverting added empty lines
* Applying expected style (indentation)
* Adding dedicated code to free payload for GList (instead of using unavailable g_list_free_full())
* Avoiding for loop initial declarations
* Remove obsolete FIXME comment
* Minor tweaking doxygen comments and variables
* Removing highlighted points on result map when starting routing
-rw-r--r-- | navit/coord.h | 12 | ||||
-rw-r--r-- | navit/gui/gtk/destination.c | 50 | ||||
-rw-r--r-- | navit/gui/gtk/gui_gtk_poi.c | 71 | ||||
-rw-r--r-- | navit/gui/gtk/gui_gtk_window.c | 1 | ||||
-rw-r--r-- | navit/gui/internal/gui_internal.c | 200 | ||||
-rw-r--r-- | navit/navit.c | 132 | ||||
-rw-r--r-- | navit/navit.h | 2 | ||||
-rw-r--r-- | navit/util.c | 64 | ||||
-rw-r--r-- | navit/util.h | 3 |
9 files changed, 336 insertions, 199 deletions
diff --git a/navit/coord.h b/navit/coord.h index cff549e95..8387462a2 100644 --- a/navit/coord.h +++ b/navit/coord.h @@ -36,6 +36,18 @@ struct coord { int y; /*!< Y-Value */ }; +/** + * @brief An integer mercator coordinate packed with a text label + * + * This structure holds information about an item (coordinates & label) on a map. This can be + * used in a list as group of points to display as search results on a map. + * It is used to structure input data for function navit_populate_search_results_map() + */ +struct lcoord { + struct coord c; /*!< The coordinates for this item */ + char *label; /*!< A label to associate to this item */ +}; + /*! A integer mercator coordinate carrying its projection */ struct pcoord { enum projection pro; diff --git a/navit/gui/gtk/destination.c b/navit/gui/gtk/destination.c index 5f52e042b..6a0fbb5b7 100644 --- a/navit/gui/gtk/destination.c +++ b/navit/gui/gtk/destination.c @@ -50,22 +50,6 @@ static struct search_param { GtkTreeModel *liststore2; } search_param; -static void button_map(GtkWidget *widget, struct search_param *search) { - GtkTreePath *path; - GtkTreeViewColumn *focus_column; - struct pcoord *c=NULL; - GtkTreeIter iter; - - gtk_tree_view_get_cursor(GTK_TREE_VIEW(search->treeview), &path, &focus_column); - if(!path) - return; - if(!gtk_tree_model_get_iter(search->liststore2, &iter, path)) - return; - gtk_tree_model_get (GTK_TREE_MODEL (search->liststore2), &iter, COL_COUNT, &c, -1); - if (c) { - navit_set_center(search->nav, c, 1); - } -} static char *description(struct search_param *search, GtkTreeIter *iter) { char *desc,*car,*postal,*town,*street; @@ -89,6 +73,39 @@ static char *description(struct search_param *search, GtkTreeIter *iter) { return desc; } +static void button_map(GtkWidget *widget, struct search_param *search) { + GtkTreePath *path; + GtkTreeViewColumn *focus_column; + struct pcoord *point=NULL; /* A pointer on the geographical position of the selected map point */ + GtkTreeIter iter; + char *label; + GList* p; + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(search->treeview), &path, &focus_column); + if(!path) + return; + if(!gtk_tree_model_get_iter(search->liststore2, &iter, path)) + return; + gtk_tree_model_get (GTK_TREE_MODEL (search->liststore2), &iter, COL_COUNT, &point, -1); + if (point) { + GList* list = NULL; + struct lcoord *result = g_new0(struct lcoord, 1); + result->c.x=point->x; + result->c.y=point->y; + result->label=description(search, &iter); /* Allocated here, freed by invoking g_free() below when parsing the GList */ + list = g_list_prepend(list, result); + navit_populate_search_results_map(search->nav, list, NULL); + /* Parse the GList starting at list and free all payloads before freeing the list itself */ + for(p=list; p; p=g_list_next(p)) { + if (((struct lcoord *)(p->data))->label) + g_free(((struct lcoord *)(p->data))->label); + } + g_list_free(list); + + navit_set_center(search->nav, point, 1); + } +} + static void button_destination(GtkWidget *widget, struct search_param *search) { struct pcoord *c=NULL; GtkTreeIter iter; @@ -99,6 +116,7 @@ static void button_destination(GtkWidget *widget, struct search_param *search) { gtk_tree_model_get (GTK_TREE_MODEL (search->liststore2), &iter, COL_COUNT, &c, -1); if (c) { desc=description(search, &iter); + navit_populate_search_results_map(search->nav, NULL, NULL); /* Remove any highlighted point on the map */ navit_set_destination(search->nav, c, desc, 1); g_free(desc); } diff --git a/navit/gui/gtk/gui_gtk_poi.c b/navit/gui/gtk/gui_gtk_poi.c index 503bc321f..265f1f28e 100644 --- a/navit/gui/gtk/gui_gtk_poi.c +++ b/navit/gui/gtk/gui_gtk_poi.c @@ -37,6 +37,9 @@ #include "navigation.h" /* for FEET_PER_METER and other conversion factors. */ +/** + * @brief Context passed around POI search function + */ static struct gtk_poi_search { GtkWidget *entry_distance; GtkWidget *label_distance; @@ -51,6 +54,12 @@ static struct gtk_poi_search { struct navit *nav; } gtk_poi_search; +/** + * @brief Get a pixbuf representing an icon for the catalog + * + * @param name The name of the icon to use (eg: "pharmacy.png" + * @return A pixbuf containing this icon of NULL if the icon could not be loaded + */ static GdkPixbuf *geticon(const char *name) { GdkPixbuf *icon=NULL; GError *error=NULL; @@ -217,7 +226,14 @@ static void treeview_poi_reload(GtkWidget *widget, struct gtk_poi_search *search gtk_tree_view_set_model(GTK_TREE_VIEW (search->treeview_poi), model_poi(search)); } -/** Set the selected POI as destination. */ +/** + * @brief Callback invoked when 'Destination' is clicked in a POI contextual window + * + * Set the selected POI as destination + * + * @param widget The widget that has been clicked + * @param search A pointer to private data containing the POI search context + */ static void button_destination_clicked(GtkWidget *widget, struct gtk_poi_search *search) { GtkTreePath *path; GtkTreeViewColumn *focus_column; @@ -241,6 +257,7 @@ static void button_destination_clicked(GtkWidget *widget, struct gtk_poi_search gtk_tree_model_get(GTK_TREE_MODEL(search->store_poi_sorted), &iter, 3, &lat, -1); gtk_tree_model_get(GTK_TREE_MODEL(search->store_poi_sorted), &iter, 4, &lon, -1); sprintf(buffer, _("POI %s. %s"), category, label); + navit_populate_search_results_map(search->nav, NULL, NULL); /* Remove any highlighted point on the map */ struct pcoord dest; dest.x=lat; @@ -250,28 +267,58 @@ static void button_destination_clicked(GtkWidget *widget, struct gtk_poi_search dbg(lvl_debug,_("Set destination to %ld, %ld "),lat,lon); } -/* Show the POI's position in the map. */ +/** + * @brief Callback invoked when 'Map' is clicked in a POI contextual window + * + * Show the POI's position in the map + * + * @param widget The widget that has been clicked + * @param search A pointer to private data containing the POI search context + */ static void button_map_clicked(GtkWidget *widget, struct gtk_poi_search *search) { GtkTreePath *path; GtkTreeViewColumn *focus_column; GtkTreeIter iter; long int lat,lon; + char *label; + GList* p; gtk_tree_view_get_cursor(GTK_TREE_VIEW(search->treeview_poi), &path, &focus_column); if(!path) return; if(!gtk_tree_model_get_iter(GTK_TREE_MODEL(search->store_poi_sorted), &iter, path)) return; + gtk_tree_model_get(GTK_TREE_MODEL(search->store_poi_sorted), &iter, 2, &label, -1); gtk_tree_model_get(GTK_TREE_MODEL(search->store_poi_sorted), &iter, 3, &lat, -1); gtk_tree_model_get(GTK_TREE_MODEL(search->store_poi_sorted), &iter, 4, &lon, -1); - struct pcoord dest; - dest.x=lat; - dest.y=lon; - dest.pro=1; - navit_set_center(search->nav, &dest,1); + struct pcoord point; /* The geographical position of the selected POI point */ + point.x=lat; + point.y=lon; + point.pro=1; + GList* list = NULL; + struct lcoord *result = g_new0(struct lcoord, 1); + result->c.x=lat; + result->c.y=lon; + result->label=g_strdup(label); + list = g_list_prepend(list, result); + navit_populate_search_results_map(search->nav, list, NULL); + /* Parse the GList starting at list and free all payloads before freeing the list itself */ + for(p=list; p; p=g_list_next(p)) { + if (((struct lcoord *)(p->data))->label) + g_free(((struct lcoord *)(p->data))->label); + } + g_list_free(list); + navit_set_center(search->nav, &point,1); dbg(lvl_debug,_("Set map to %ld, %ld "),lat,lon); } -/** Set POI as the first "visit before". */ +/** + * @brief Callback invoked when 'Visit before' is clicked in a POI contextual window + * + * Set POI as a waypoint to visit before an existing destination + * + * @param widget The widget that has been clicked + * @param search A pointer to private data containing the POI search context + */ static void button_visit_clicked(GtkWidget *widget, struct gtk_poi_search *search) { GtkTreePath *path; GtkTreeViewColumn *focus_column; @@ -284,6 +331,7 @@ static void button_visit_clicked(GtkWidget *widget, struct gtk_poi_search *searc gtk_tree_model_get(GTK_TREE_MODEL(search->store_poi_sorted), &iter, 3, &lat, -1); gtk_tree_model_get(GTK_TREE_MODEL(search->store_poi_sorted), &iter, 4, &lon, -1); dbg(lvl_debug,_("Set next visit to %ld, %ld "),lat,lon); + navit_populate_search_results_map(search->nav, NULL, NULL); /* Remove any highlighted point on the map */ struct pcoord dest; dest.x=lat; @@ -292,7 +340,11 @@ static void button_visit_clicked(GtkWidget *widget, struct gtk_poi_search *searc popup_set_visitbefore(search->nav,&dest,0); } -/** Create UI and connect objects to functions. */ +/** + * @brief Create the POI search UI window and connect objects to functions + * + * @param nav The navit instance + */ void gtk_gui_poi(struct navit *nav) { GtkWidget *window2,*vbox, *keyboard, *table; GtkWidget *label_category, *label_poi; @@ -302,6 +354,7 @@ void gtk_gui_poi(struct navit *nav) { struct gtk_poi_search *search=>k_poi_search; search->nav=nav; + navit_populate_search_results_map(search->nav, NULL, NULL); /* Remove any highlighted point on the map */ window2 = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(window2),_("POI search")); gtk_window_set_wmclass (GTK_WINDOW (window2), "navit", "Navit"); diff --git a/navit/gui/gtk/gui_gtk_window.c b/navit/gui/gtk/gui_gtk_window.c index 782add46f..1e57004d0 100644 --- a/navit/gui/gtk/gui_gtk_window.c +++ b/navit/gui/gtk/gui_gtk_window.c @@ -323,6 +323,7 @@ static void gui_gtk_action_activate(GtkAction *action, struct action_cb_data *da if(data->attr.type == attr_destination) { char * label; g_object_get(G_OBJECT(action), "label", &label,NULL); + navit_populate_search_results_map(data->gui->nav, NULL, NULL); /* Remove any highlighted point on the map */ navit_set_destination(data->gui->nav, data->attr.u.pcoord, label, 1); g_free(label); } diff --git a/navit/gui/internal/gui_internal.c b/navit/gui/internal/gui_internal.c index afae9a9f1..726a03d17 100644 --- a/navit/gui/internal/gui_internal.c +++ b/navit/gui/internal/gui_internal.c @@ -895,125 +895,6 @@ static void gui_internal_cmd_view_in_browser(struct gui_priv *this, struct widge } /** - * @brief Get the search result map (and create it if it does not exist) - * - * @param this The GUI context - * - * @return A pointer to the map named "search_results" or NULL if there wasa failure - */ -static struct map *get_search_results_map(struct gui_priv *this) { - - struct mapset *ms; - struct map *map; - - ms=navit_get_mapset(this->nav); - - 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 Optimizes the format of a string, adding carriage returns so that when displayed, the result text zone is roughly as wide as high - * - * @param[in,out] s The string to proces (will be modified by this function, but length will be unchanged) - */ -static void square_shape_str(char *s) { - char *c; - char *last_break; - unsigned int max_cols = 0; - unsigned int cur_cols = 0; - unsigned int max_rows = 0; - unsigned int surface; - unsigned int target_cols; - - if (!s) - return; - for (c=s; *c!='\0'; c++) { - if (*c==' ') { - if (max_cols < cur_cols) - max_cols = cur_cols; - cur_cols = 0; - max_rows++; - } else - cur_cols++; - } - if (max_cols < cur_cols) - max_cols = cur_cols; - if (cur_cols) /* If last line does not end with CR, add it to line numbers anyway */ - max_rows++; - /* Give twice more room for rows (hence the factor 2 below) - * This will render as a rectangular shape, taking more horizontal space than vertical */ - surface = max_rows * 2 * max_cols; - target_cols = uint_sqrt(surface); - - if (target_cols < max_cols) - target_cols = max_cols; - - target_cols = target_cols + target_cols/10; /* Allow 10% extra on columns */ - dbg(lvl_debug, "square_shape_str(): analyzing input text=\"%s\". max_rows=%u, max_cols=%u, surface=%u, target_cols=%u", - s, max_rows, max_cols, max_rows * 2 * max_cols, target_cols); - - cur_cols = 0; - last_break = NULL; - for (c=s; *c!='\0'; c++) { - if (*c==' ') { - if (cur_cols>=target_cols) { /* This line is too long, break at the previous non alnum character */ - if (last_break) { - *last_break = - '\n'; /* Replace the previous non alnum character with a line break, this creates a new line and prevents the previous line from being too long */ - cur_cols = c-last_break; - } - } - last_break = c; /* Record this position as a candidate to insert a line break */ - } - cur_cols++; - } - if (cur_cols>=target_cols && last_break) { - *last_break = - '\n'; /* Replace the previous non alnum character with a line break, this creates a new line and prevents the previous line from being too long */ - } - - dbg(lvl_debug, "square_shape_str(): output text=\"%s\"", s); -} - -/** * @brief Create a map rect highlighting one of multiple points provided in argument @data and displayed using * the style type_found_item (name for each point will also be displayed aside) * @@ -1024,28 +905,10 @@ static void square_shape_str(char *s) { */ static void gui_internal_prepare_search_results_map(struct gui_priv *this, struct widget *table, struct coord_rect *r) { struct widget *w; - struct map *map; - struct map_rect *mr; - struct item *item; - GList *l; + GList *l; /* Cursor in the list of widgets */ + GList* list = NULL; /* List we will create to store the points to add to the result map */ struct attr a; - int count; - char *name_label; - - map = get_search_results_map(this); - if(!map) - return; - - - mr = map_rect_new(map, NULL); - - if(!mr) - return; - - /* Clean the map */ - while((item = map_rect_get_item(mr))!=NULL) { - item_type_set(item,type_none); - } + GList* p; this->results_map_population=0; @@ -1053,43 +916,33 @@ static void gui_internal_prepare_search_results_map(struct gui_priv *this, struc for(w=table; w && w->type!=widget_table; w=w->parent); if(!w) { - map_rect_destroy(mr); dbg(lvl_warning,"Can't find the results table - only map clean up is done."); - return; - } - - /* Populate the map with search results*/ - for(l=w->children, count=0; l; l=g_list_next(l)) { - struct widget *wr=l->data; - if(wr->type==widget_table_row) { - struct widget *wi=wr->children->data; - struct item* it; - if(wi->name==NULL) - continue; - dbg(lvl_info,"%s",wi->name); - it=map_rect_create_item(mr,type_found_item); - if(it) { - struct coord c; - struct attr a; - c.x=wi->c.x; - c.y=wi->c.y; - item_coord_set(it, &c, 1, change_mode_modify); - a.type=attr_label; - name_label = g_strdup(wi->name); - 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=c; - else - coord_rect_extend(r,&c); - } + } else { + /* Create a GList containing all search results */ + for(l=w->children; l; l=g_list_next(l)) { + struct widget *wr=l->data; + if(wr->type==widget_table_row) { + struct widget *wi=wr->children->data; + if(wi->name==NULL) + continue; + struct lcoord *result = g_new0(struct lcoord, 1); + result->c.x=wi->c.x; + result->c.y=wi->c.y; + result->label=g_strdup(wi->name); + list = g_list_prepend(list, result); } } } - map_rect_destroy(mr); - if(!count) + this->results_map_population=navit_populate_search_results_map(this->nav, list, r); + /* Parse the GList starting at list and free all payloads before freeing the list itself */ + if (list) { + for(p=list; p; p=g_list_next(p)) { + if (((struct lcoord *)(p->data))->label) + g_free(((struct lcoord *)(p->data))->label); + } + } + g_list_free(list); + if(!this->results_map_population) return; a.type=attr_orientation; a.u.num=0; @@ -1098,7 +951,6 @@ static void gui_internal_prepare_search_results_map(struct gui_priv *this, struc navit_zoom_to_rect(this->nav,r); gui_internal_prune_menu(this, NULL); } - this->results_map_population=count; } /** diff --git a/navit/navit.c b/navit/navit.c index 90935786d..1d1b8e664 100644 --- a/navit/navit.c +++ b/navit/navit.c @@ -213,6 +213,13 @@ 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) { @@ -223,6 +230,131 @@ navit_get_mapset(struct navit *this_) { 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; diff --git a/navit/navit.h b/navit/navit.h index 7125c1878..f543a7baf 100644 --- a/navit/navit.h +++ b/navit/navit.h @@ -54,6 +54,8 @@ struct command_table; struct item; void navit_add_mapset(struct navit *this_, struct mapset *ms); struct mapset *navit_get_mapset(struct navit *this_); +struct map *navit_get_search_results_map(struct navit *this_); +int navit_populate_search_results_map(struct navit *navit, GList *search_results, struct coord_rect *r); struct tracking *navit_get_tracking(struct navit *this_); char *navit_get_user_data_directory(int create); void navit_draw_async(struct navit *this_, int async); diff --git a/navit/util.c b/navit/util.c index 9398e316c..944dc2a21 100644 --- a/navit/util.c +++ b/navit/util.c @@ -529,6 +529,70 @@ char * newSysString(const char *toconvert) { #endif #endif +/** + * @brief Optimizes the format of a string, adding carriage returns so that when displayed, the result text zone is roughly as wide as high + * + * @param[in,out] s The string to proces (will be modified by this function, but length will be unchanged) + */ +void square_shape_str(char *s) { + char *c; + char *last_break; + unsigned int max_cols = 0; + unsigned int cur_cols = 0; + unsigned int max_rows = 0; + unsigned int surface; + unsigned int target_cols; + + if (!s) + return; + for (c=s; *c!='\0'; c++) { + if (*c==' ') { + if (max_cols < cur_cols) + max_cols = cur_cols; + cur_cols = 0; + max_rows++; + } else + cur_cols++; + } + if (max_cols < cur_cols) + max_cols = cur_cols; + if (cur_cols) /* If last line does not end with CR, add it to line numbers anyway */ + max_rows++; + /* Give twice more room for rows (hence the factor 2 below) + * This will render as a rectangular shape, taking more horizontal space than vertical */ + surface = max_rows * 2 * max_cols; + target_cols = uint_sqrt(surface); + + if (target_cols < max_cols) + target_cols = max_cols; + + target_cols = target_cols + target_cols/10; /* Allow 10% extra on columns */ + dbg(lvl_debug, "square_shape_str(): analyzing input text=\"%s\". max_rows=%u, max_cols=%u, surface=%u, target_cols=%u", + s, max_rows, max_cols, max_rows * 2 * max_cols, target_cols); + + cur_cols = 0; + last_break = NULL; + for (c=s; *c!='\0'; c++) { + if (*c==' ') { + if (cur_cols>=target_cols) { /* This line is too long, break at the previous non alnum character */ + if (last_break) { + *last_break = + '\n'; /* Replace the previous non alnum character with a line break, this creates a new line and prevents the previous line from being too long */ + cur_cols = c-last_break; + } + } + last_break = c; /* Record this position as a candidate to insert a line break */ + } + cur_cols++; + } + if (cur_cols>=target_cols && last_break) { + *last_break = + '\n'; /* Replace the previous non alnum character with a line break, this creates a new line and prevents the previous line from being too long */ + } + + dbg(lvl_debug, "square_shape_str(): output text=\"%s\"", s); +} + #if defined(_MSC_VER) || (!defined(HAVE_GETTIMEOFDAY) && defined(HAVE_API_WIN32_BASE)) /** * Impements a simple incomplete version of gettimeofday. Only usefull for messuring diff --git a/navit/util.h b/navit/util.h index 131173ff6..685adc080 100644 --- a/navit/util.h +++ b/navit/util.h @@ -41,6 +41,9 @@ wchar_t* newSysString(const char *toconvert); char * newSysString(const char *toconvert); #endif #endif + +void square_shape_str(char *s); + unsigned int iso8601_to_secs(char *iso8601); time_t mkgmtime(struct tm * pt); time_t iso8601_to_time(char * iso8601); |