Contents of /trunk/src/misc.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 185 - (show annotations)
Sat Nov 14 16:55:33 2009 UTC (14 years, 6 months ago) by harbaum
File MIME type: text/plain
File size: 14491 byte(s)
All icons redone
1 /*
2 * Copyright (C) 2008 Till Harbaum <till@harbaum.org>.
3 *
4 * This file is part of GPXView.
5 *
6 * GPXView is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * GPXView is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with GPXView. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <math.h>
21 #include <string.h>
22 #include <ctype.h>
23
24 #include <glib.h>
25 #include <glib/gstdio.h>
26
27 #include "gpxview.h"
28
29 char strlastchr(char *str) {
30 return str[strlen(str)]-1;
31 }
32
33 /* make sure the entire path "dir" exists and create it if not */
34 int checkdir(char *dir) {
35 struct stat filestat;
36 char *p = dir, tmp;
37
38 /* don't try to create root dir */
39 if(p[0] == '/') p++;
40
41 do {
42 while(*p && *p != '/') p++;
43 tmp = *p;
44 *p = 0;
45
46 int err = stat(dir, &filestat);
47 if(err) {
48 if(mkdir(dir, S_IRWXU) != 0) {
49 perror("mkdir()");
50 *p++ = tmp;
51 return -1;
52 }
53 } else {
54 if(!filestat.st_mode & S_IFDIR) {
55 printf("File %s exists and is _no_ directory\n", dir);
56 *p++ = tmp;
57 return -1;
58 }
59 }
60
61 *p++ = tmp;
62 } while(tmp && strchr(p, '/'));
63
64 return 0;
65 }
66
67 void pos_lat_str(char *str, int len, float latitude) {
68 char *c = _("N");
69 float integral, fractional;
70
71 if(isnan(latitude))
72 str[0] = 0;
73 else {
74 if(latitude < 0) { latitude = fabs(latitude); c = _("S"); }
75 fractional = modff(latitude, &integral);
76
77 snprintf(str, len, "%s %02d° %06.3f'", c, (int)integral, fractional*60.0);
78 }
79 }
80
81 GtkWidget *pos_lat(float latitude, int size, int strikethrough) {
82 char str[32];
83
84 pos_lat_str(str, sizeof(str), latitude);
85 return gtk_label_attrib(str, size, strikethrough);
86 }
87
88 void pos_lon_str(char *str, int len, float longitude) {
89 char *c = _("E");
90 float integral, fractional;
91
92 if(isnan(longitude))
93 str[0] = 0;
94 else {
95 if(longitude < 0) { longitude = fabs(longitude); c = _("W"); }
96 fractional = modff(longitude, &integral);
97
98 snprintf(str, len, "%s %03d° %06.3f'", c, (int)integral, fractional*60.0);
99 }
100 }
101
102 GtkWidget *pos_lon(float longitude, int size, int strikethrough) {
103 char str[32];
104
105 pos_lon_str(str, sizeof(str), longitude);
106 return gtk_label_attrib(str, size, strikethrough);
107 }
108
109 float pos_parse_lat(char *str) {
110 int integral_int;
111 float fractional;
112 char c;
113
114 if(sscanf(str, "%c %d° %f'", &c, &integral_int, &fractional) == 3) {
115 c = toupper(c);
116
117 if(c != 'S' && c != 'N')
118 return NAN;
119
120 /* prevent -0.0 */
121 if(!integral_int && (fractional == 0.0))
122 return 0.0;
123
124 return ((c == 'S')?-1:+1) * (integral_int + fractional/60.0);
125 }
126
127 return NAN;
128 }
129
130 float pos_parse_lon(char *str) {
131 int integral_int;
132 float fractional;
133 char c;
134
135 if(sscanf(str, "%c %d° %f'", &c, &integral_int, &fractional) == 3) {
136 c = toupper(c);
137
138 /* O is german "Ost" for "East" */
139 if(c != 'E' && c != 'W' && c != 'O')
140 return NAN;
141
142 /* prevent -0.0 */
143 if(!integral_int && (fractional == 0.0))
144 return 0.0;
145
146 return ((c == 'W')?-1:+1) * (integral_int + fractional/60.0);
147 }
148
149 return NAN;
150 }
151
152 const char *pos_get_bearing_str(pos_t from, pos_t to) {
153 static const char *bear_str[]={
154 "N", "NE", "E", "SE", "S", "SW", "W", "NW", "" };
155
156 float bearing = gpx_pos_get_bearing(from, to);
157 if(!isnan(bearing)) {
158 int idx = (bearing+22.5)/45.0;
159 /* make sure we stay in icon bounds */
160 while(idx < 0) idx += 8;
161 while(idx > 7) idx -= 8;
162 return _(bear_str[idx]);
163 }
164
165 return bear_str[8]; // empty string
166 }
167
168 /* the maemo font size is quite huge, so we adjust some fonts */
169 /* differently on maemo and non-maemo. Basically "BIG" does nothing */
170 /* on maemo and "SMALL" only does something on maemo */
171 #ifdef USE_MAEMO
172 #define MARKUP_SMALL "<span size='small'>%s</span>"
173 GtkWidget *gtk_label_small(char *str) {
174 GtkWidget *label = gtk_label_new("");
175 char *markup = g_markup_printf_escaped(MARKUP_SMALL, str);
176 gtk_label_set_markup(GTK_LABEL(label), markup);
177 g_free(markup);
178 return label;
179 }
180 #else
181 #define MARKUP_BIG "<span size='x-large'>%s</span>"
182 GtkWidget *gtk_label_big(char *str) {
183 GtkWidget *label = gtk_label_new("");
184 char *markup = g_markup_printf_escaped(MARKUP_BIG, str);
185 gtk_label_set_markup(GTK_LABEL(label), markup);
186 g_free(markup);
187 return label;
188 }
189 #endif
190
191 void gtk_label_attrib_set(GtkWidget *label,
192 char *str, int size, int strikethrough) {
193 char format[80];
194
195 snprintf(format, sizeof(format), "<span%s%s%s>%%s</span>",
196 #ifdef USE_MAEMO
197 (size==SIZE_SMALL)?" size='small'":"",
198 #else
199 (size==SIZE_BIG)?" size='x-large'":"",
200 #endif
201 strikethrough?" strikethrough='yes'":"",
202 (strikethrough==STRIKETHROUGH_RED)?" strikethrough_color='red'":"");
203
204 char *markup = g_markup_printf_escaped(format, str);
205 // printf("markup = %s\n", markup);
206 gtk_label_set_markup(GTK_LABEL(label), markup);
207 g_free(markup);
208 }
209
210 GtkWidget *gtk_label_attrib(char *str, int size, int strikethrough) {
211 GtkWidget *label = gtk_label_new("");
212 gtk_label_attrib_set(label, str, size, strikethrough);
213 return label;
214 }
215
216 GtkWidget *gtk_button_attrib(char *str, int size, int strikethrough) {
217 GtkWidget *button = gtk_button_new_with_label("");
218 gtk_label_attrib_set(gtk_bin_get_child(GTK_BIN(button)),
219 str, size, strikethrough);
220 return button;
221 }
222
223 void textbox_disable(GtkWidget *widget) {
224 gtk_editable_set_editable(GTK_EDITABLE(widget), FALSE);
225 gtk_widget_set_sensitive(widget, FALSE);
226 }
227
228 void textbox_enable(GtkWidget *widget) {
229 gtk_widget_set_sensitive(widget, TRUE);
230 gtk_editable_set_editable(GTK_EDITABLE(widget), TRUE);
231 }
232
233 pos_t *get_pos(appdata_t *appdata) {
234 pos_t *pos = &appdata->home;
235
236 if(appdata->active_location) {
237 int i = appdata->active_location-1;
238 location_t *loc = appdata->location;
239 while(i--) loc = loc->next;
240 pos = &loc->pos;
241 }
242
243 if(appdata->use_gps) {
244 pos = gps_get_pos(appdata);
245
246 if(!pos) pos = &appdata->gps; /* use saved position */
247 else appdata->gps = *pos; /* save position */
248 }
249 return pos;
250 }
251
252 void distance_str(char *str, int len, float dist, gboolean imperial) {
253 if(isnan(dist))
254 snprintf(str, len, "---");
255 else if(imperial) {
256 /* 1 mil = 1760 yd = 5280 ft ... */
257 if(dist<0.018) snprintf(str, len, "%.1f ft", dist*5280.0);
258 else if(dist<0.055) snprintf(str, len, "%.1f yd", dist*1760.0);
259 else if(dist<0.55) snprintf(str, len, "%.0f yd", dist*1760.0);
260 else if(dist<10.0) snprintf(str, len, "%.2f mi", dist);
261 else if(dist<100.0) snprintf(str, len, "%.1f mi", dist);
262 else snprintf(str, len, "%.0f mi", dist);
263 } else {
264 if(dist<0.01) snprintf(str, len, "%.2f m", dist*1000.0);
265 else if(dist<0.1) snprintf(str, len, "%.1f m", dist*1000.0);
266 else if(dist<1.0) snprintf(str, len, "%.0f m", dist*1000.0);
267 else if(dist<100.0) snprintf(str, len, "%.1f km", dist);
268 else snprintf(str, len, "%.0f km", dist);
269 }
270 }
271
272 /* return distance in miles or kilometers */
273 float distance_parse(char *str, gboolean imperial) {
274 char unit[4];
275 float val = NAN;
276
277 if(sscanf(str, "%f %3s", &val, unit) == 2) {
278 gboolean fimp = FALSE;
279
280 if(strcasecmp(unit, "ft") == 0) { fimp = TRUE; val /= 5280.0; }
281 else if(strcasecmp(unit, "yd") == 0) { fimp = TRUE; val /= 1760.0; }
282 else if(strcasecmp(unit, "mi") == 0) { fimp = TRUE; }
283 else if(strcasecmp(unit, "m") == 0) { fimp = FALSE; val /= 1000.0; }
284 else if(strcasecmp(unit, "km") == 0) { fimp = FALSE; }
285 else val = NAN;
286
287 /* found imperial and metric requested? convert miles into kilometers */
288 if(fimp & !imperial) val *= 1.609344;
289
290 /* found metric and imperial requested? convert kilometers into miles */
291 if(!fimp & imperial) val /= 1.609344;
292 }
293 return val;
294 }
295
296 static gboolean mark(GtkWidget *widget, gboolean valid) {
297 gtk_widget_set_state(widget, valid?GTK_STATE_NORMAL:TAG_STATE);
298 return valid;
299 }
300
301 static void callback_modified_lat(GtkWidget *widget, gpointer data ) {
302 float i = pos_parse_lat((char*)gtk_entry_get_text(GTK_ENTRY(widget)));
303 mark(widget, !isnan(i));
304 }
305
306 /* a entry that is colored red when being "active" */
307 GtkWidget *lat_entry_new(float lat) {
308 GdkColor color;
309 GtkWidget *widget = gtk_entry_new();
310 gdk_color_parse("#ff0000", &color);
311 gtk_widget_modify_text(widget, TAG_STATE, &color);
312
313 char str[32];
314 pos_lat_str(str, sizeof(str), lat);
315 gtk_entry_set_text(GTK_ENTRY(widget), str);
316
317 g_signal_connect(G_OBJECT(widget), "changed",
318 G_CALLBACK(callback_modified_lat), NULL);
319
320 return widget;
321 }
322
323 static void callback_modified_lon(GtkWidget *widget, gpointer data ) {
324 float i = pos_parse_lon((char*)gtk_entry_get_text(GTK_ENTRY(widget)));
325 mark(widget, !isnan(i));
326 }
327
328 /* a entry that is colored red when filled with invalid coordinate */
329 GtkWidget *lon_entry_new(float lon) {
330 GdkColor color;
331 GtkWidget *widget = gtk_entry_new();
332 gdk_color_parse("#ff0000", &color);
333 gtk_widget_modify_text(widget, TAG_STATE, &color);
334
335 char str[32];
336 pos_lon_str(str, sizeof(str), lon);
337 gtk_entry_set_text(GTK_ENTRY(widget), str);
338
339 g_signal_connect(G_OBJECT(widget), "changed",
340 G_CALLBACK(callback_modified_lon), NULL);
341
342 return widget;
343 }
344
345
346 float lat_get(GtkWidget *widget) {
347 char *p = (char*)gtk_entry_get_text(GTK_ENTRY(widget));
348 return pos_parse_lat(p);
349 }
350
351 float lon_get(GtkWidget *widget) {
352 char *p = (char*)gtk_entry_get_text(GTK_ENTRY(widget));
353 return pos_parse_lon(p);
354 }
355
356 static void callback_modified_dist(GtkWidget *widget, gpointer data ) {
357 /* don't care for metric/imperial since we only want to know if this */
358 /* is parseable at all */
359 float i = distance_parse((char*)gtk_entry_get_text(GTK_ENTRY(widget)), FALSE);
360 mark(widget, !isnan(i));
361 }
362
363 /* a entry that is colored red when filled with invalid distance */
364 GtkWidget *dist_entry_new(float dist, gboolean mil) {
365 GdkColor color;
366 GtkWidget *widget = gtk_entry_new();
367 gdk_color_parse("#ff0000", &color);
368 gtk_widget_modify_text(widget, TAG_STATE, &color);
369
370 char str[32];
371 distance_str(str, sizeof(str), dist, mil);
372 gtk_entry_set_text(GTK_ENTRY(widget), str);
373
374 g_signal_connect(G_OBJECT(widget), "changed",
375 G_CALLBACK(callback_modified_dist), NULL);
376
377 return widget;
378 }
379
380 float dist_get(GtkWidget *widget, gboolean mil) {
381 char *p = (char*)gtk_entry_get_text(GTK_ENTRY(widget));
382 return distance_parse(p, mil);
383 }
384
385 #ifndef USE_MAEMO
386 #ifdef ENABLE_BROWSER_INTERFACE
387 #include <libgnome/gnome-url.h>
388
389 int browser_url(appdata_t *appdata, char *url) {
390 /* taken from gnome-open, part of libgnome */
391 GError *err = NULL;
392 gnome_url_show(url, &err);
393 return 0;
394 }
395 #endif
396 #endif
397
398 /* recursively remove an entire file system */
399 void rmdir_recursive(char *path) {
400 GDir *dir = g_dir_open(path, 0, NULL);
401 if(dir) {
402 const char *name = g_dir_read_name(dir);
403 while(name) {
404 char *fullname = g_strdup_printf("%s/%s", path, name);
405 // printf("deleting %s\n", fullname);
406
407 if(g_file_test(fullname, G_FILE_TEST_IS_DIR))
408 rmdir_recursive(fullname);
409 else if(g_file_test(fullname, G_FILE_TEST_IS_REGULAR))
410 g_remove(fullname);
411
412 g_free(fullname);
413 name = g_dir_read_name(dir);
414 }
415
416 g_dir_close(dir);
417 }
418 g_rmdir(path);
419 }
420
421 #ifdef ENABLE_BROWSER_INTERFACE
422 static void on_link_clicked(GtkButton *button, gpointer data) {
423 appdata_t *appdata = (appdata_t*)data;
424 char *url = g_object_get_data(G_OBJECT(button), "url");
425 if(url) browser_url(appdata, url);
426 }
427 #endif
428
429 /* a button containing a weblink */
430 GtkWidget *link_button_attrib(appdata_t *appdata, char *str, char *url,
431 int size, int strikethrough) {
432
433 #ifdef ENABLE_BROWSER_INTERFACE
434 if(url) {
435 GtkWidget *button = gtk_button_attrib(str, size, strikethrough);
436 g_object_set_data(G_OBJECT(button), "url", url);
437 gtk_signal_connect(GTK_OBJECT(button), "clicked",
438 (GtkSignalFunc)on_link_clicked, appdata);
439
440 return button;
441 }
442 #endif
443 return gtk_label_attrib(str, size, strikethrough);
444 }
445
446 #ifdef ENABLE_BROWSER_INTERFACE
447 static void on_link_id_clicked(GtkButton *button, gpointer data) {
448 appdata_t *appdata = (appdata_t*)data;
449
450 unsigned int id = (unsigned int)g_object_get_data(G_OBJECT(button), "id");
451 char *type = g_object_get_data(G_OBJECT(button), "type");
452
453 char *url = g_strdup_printf("http://www.geocaching.com/%s?id=%u",
454 type, id);
455
456 if(url) {
457 browser_url(appdata, url);
458 g_free(url);
459 }
460 }
461 #endif
462
463 GtkWidget *link_button_by_id(appdata_t *appdata, char *str,
464 const char *type, int id) {
465
466 #ifdef ENABLE_BROWSER_INTERFACE
467 if(id) {
468 GtkWidget *ref = gtk_button_new_with_label(str);
469 #if defined(USE_MAEMO) && (MAEMO_VERSION_MAJOR == 5)
470 // hildon_gtk_widget_set_theme_size(ref,
471 // (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH));
472 #endif
473 g_object_set_data(G_OBJECT(ref), "id", (gpointer)id);
474 g_object_set_data(G_OBJECT(ref), "type", (gpointer)type);
475 gtk_signal_connect(GTK_OBJECT(ref), "clicked",
476 GTK_SIGNAL_FUNC(on_link_id_clicked), appdata);
477
478 return ref;
479 }
480 #endif
481 return gtk_label_new(str);
482 }
483
484
485 GtkWidget *link_icon_button_by_id(appdata_t *appdata, GtkWidget *icon,
486 const char *type, int id) {
487
488 #ifdef ENABLE_BROWSER_INTERFACE
489 if(id) {
490 GtkWidget *ref = gtk_button_new();
491 gtk_button_set_image(GTK_BUTTON(ref), icon);
492
493 #if defined(USE_MAEMO) && (MAEMO_VERSION_MAJOR == 5)
494 // hildon_gtk_widget_set_theme_size(ref,
495 // (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH));
496 #endif
497 g_object_set_data(G_OBJECT(ref), "id", (gpointer)id);
498 g_object_set_data(G_OBJECT(ref), "type", (gpointer)type);
499 gtk_signal_connect(GTK_OBJECT(ref), "clicked",
500 GTK_SIGNAL_FUNC(on_link_id_clicked), appdata);
501
502 return ref;
503 }
504 #endif
505 return icon;
506 }
507
508 /* left aligned, word wrapped multiline widget */
509 GtkWidget *simple_text_widget(char *text) {
510 GtkWidget *label = gtk_label_new(text);
511
512 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
513 gtk_label_set_line_wrap_mode(GTK_LABEL(label), PANGO_WRAP_WORD);
514 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
515
516 return label;
517 }