Contents of /trunk/src/cache.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 214 - (show annotations)
Thu Nov 26 10:05:23 2009 UTC (14 years, 5 months ago) by harbaum
File MIME type: text/plain
File size: 31145 byte(s)
Unified coo tool
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 "gpxview.h"
21 #include <math.h>
22 #include <string.h>
23
24 static GtkWidget *cache_description(appdata_t *appdata, cache_t *cache) {
25 return html_view(appdata, cache->long_description,
26 cache->long_is_html?HTML_HTML:HTML_PLAIN_TEXT, TRUE, cache, NULL);
27 }
28
29 #ifndef USE_MAEMO // maemos touchscreen doesn't support tooltips
30 static const char *cache_type_tip[] = {
31 "Traditional Cache", "Multicache", "Mystery/Unknown Cache",
32 "Virtual Cache", "Webcam Cache", "Event Cache",
33 "Letterbox Hybrid", "Earthcache", "Wherigo Cache",
34 "Mega-Event Cache", "Cache-In-Trash-Out Cache"
35 };
36
37 static const char *cache_size_tip[] = {
38 "Regular Container", "Small Container", "Micro",
39 "Other Container", "Container not chosen", "Large Container",
40 "Virtual Container"
41 };
42 #endif
43
44 static const char *cache_size_name[] = {
45 "Regular", "Small", "Micro", "Other", "Not chosen", "Large", "Virtual"
46 };
47
48 void bearing_fill_hbox(GtkWidget *hbox,
49 appdata_t *appdata, pos_t refpos, pos_t pos) {
50 char str[32];
51
52 if(!isnan(pos.lat) && !isnan(pos.lon)) {
53 gtk_box_pack_start(GTK_BOX(hbox), gtk_image_new_from_pixbuf(
54 icon_bearing(refpos, pos)),TRUE,FALSE,0);
55
56 if(!isnan(refpos.lat) && !isnan(refpos.lon)) {
57 gtk_box_pack_start_defaults(GTK_BOX(hbox),
58 GTK_LABEL_SMALL((char*)pos_get_bearing_str(refpos, pos)));
59 gtk_box_pack_start_defaults(GTK_BOX(hbox), GTK_LABEL_SMALL(" "));
60 snprintf(str, sizeof(str), _("%.1f°"),
61 gpx_pos_get_bearing(refpos, pos));
62 gtk_box_pack_start_defaults(GTK_BOX(hbox), GTK_LABEL_SMALL(str));
63 gpx_pos_get_distance_str(str, sizeof(str),
64 refpos, pos, appdata->imperial);
65 gtk_box_pack_start_defaults(GTK_BOX(hbox), GTK_LABEL_SMALL(" "));
66 gtk_box_pack_start(GTK_BOX(hbox),
67 gtk_label_attrib(str, SIZE_SMALL, STRIKETHROUGH_NONE),TRUE,FALSE,0);
68 } else
69 gtk_box_pack_start(GTK_BOX(hbox),
70 gtk_label_attrib(_("(no position)"),
71 SIZE_SMALL, STRIKETHROUGH_NONE),TRUE,FALSE,0);
72 } else
73 gtk_box_pack_start(GTK_BOX(hbox),
74 gtk_label_attrib(_("(invalid position in notes)"),
75 SIZE_SMALL, STRIKETHROUGH_NONE),TRUE,FALSE,0);
76 }
77
78 /* this function sets everything related to the coordinate. used to */
79 /* cope with the user setting a new "note coordinate" */
80 void overview_coordinate_update(cache_context_t *context) {
81 if(!context->notes.modified)
82 return;
83
84 /* update position labels */
85 int strike = notes_get_override(context)?STRIKETHROUGH:STRIKETHROUGH_NONE;
86 char str[32];
87 pos_lat_str(str, sizeof(str), context->cache->pos.lat);
88 gtk_label_attrib_set(context->pos_lat_label, str, SIZE_BIG, strike);
89 pos_lon_str(str, sizeof(str), context->cache->pos.lon);
90 gtk_label_attrib_set(context->pos_lon_label, str, SIZE_BIG, strike);
91
92 /* remove enire hbox and build a new one */
93 gtk_container_foreach(GTK_CONTAINER(context->bearing_hbox),
94 (GtkCallback)gtk_widget_destroy, NULL);
95
96 /* update distance/etc */
97 if(!isnan(context->cache->pos.lat) &&
98 !isnan(context->cache->pos.lon))
99 bearing_fill_hbox(context->bearing_hbox, context->appdata,
100 *get_pos(context->appdata), notes_get_pos(context));
101
102 gtk_widget_show_all(context->bearing_hbox);
103 }
104
105 static void gcvote_set(cache_context_t *context, vote_t *vote) {
106 if(!vote) return;
107
108 if(context->quality) {
109 gtk_widget_destroy(context->quality);
110 context->quality = NULL;
111 }
112
113 if(context->votes) {
114 gtk_widget_destroy(context->votes);
115 context->votes = NULL;
116 }
117
118 /* update/draw the voting */
119 #ifndef USE_MAEMO
120 GtkTooltips *tips = gtk_tooltips_new();
121 #endif
122
123 char *votes_str = g_strdup_printf(_("Quality (%d %s):"), vote->votes,
124 (vote->votes == 1)?_("vote"):_("votes"));
125 context->votes = GTK_LABEL_SMALL(votes_str);
126 gtk_box_pack_start(GTK_BOX(context->votebox),
127 context->votes, FALSE, FALSE, 0);
128 g_free(votes_str);
129 context->quality = icon_get_widget(ICON_STARS, (int)(vote->quality*2-2));
130 gtk_box_pack_start(GTK_BOX(context->votebox), context->quality,
131 FALSE, FALSE, 0);
132
133 #ifndef USE_MAEMO
134 char *str = g_strdup_printf(_("Quality: %d"), vote->quality);
135 gtk_tooltips_set_tip(tips, context->votebox, str, NULL);
136 g_free(str);
137 #endif
138
139 gtk_widget_show_all(context->votebox);
140
141 g_free(vote);
142 }
143
144 static void gcvote_callback(vote_t *vote, gpointer data) {
145 cache_context_t *context = (cache_context_t*)data;
146
147 /* no vote returned: request failed, just cleanup */
148 if(!vote) {
149 printf("gcvote: callback for failed request\n");
150
151 gcvote_request_free(context->gcvote_request);
152 context->gcvote_request = NULL;
153 return;
154 }
155
156 printf("gcvote: callback is being called with a %d/%d\n",
157 vote->quality, vote->votes);
158
159 gcvote_set(context, vote);
160
161 gcvote_save(context->appdata, context->cache, &context->gcvote_request->mem);
162
163 gcvote_request_free(context->gcvote_request);
164 context->gcvote_request = NULL;
165 }
166
167 static void pack_vcentered(GtkWidget *vbox, GtkWidget *child) {
168 GtkWidget *align = gtk_alignment_new(0.5, 0.5, 0.0, 0.0);
169 gtk_container_add(GTK_CONTAINER(align), child);
170 gtk_box_pack_start_defaults(GTK_BOX(vbox), align);
171 }
172
173 #ifdef FREMANTLE
174 #define ICON_SIZE ICON_CACHE_TYPE_2X
175 #else
176 #define ICON_SIZE ICON_CACHE_TYPE_1_5X
177 #endif
178
179 static GtkWidget *cache_overview(cache_context_t *context) {
180 GtkWidget *vbox, *ivbox;
181 GtkWidget *colbox, *tip;
182 char str[64];
183 #ifndef USE_MAEMO
184 GtkTooltips *tips = gtk_tooltips_new();
185 #endif
186 appdata_t *appdata = context->appdata;
187 cache_t *cache = context->cache;
188
189 vbox = gtk_vbox_new(FALSE, 4);
190
191 /* hbox containing the four columns of cache details */
192 GtkWidget *ihbox = gtk_hbox_new(FALSE, 0);
193
194 /* vbox containing leftmost column (icon and container) */
195 colbox = gtk_vbox_new(FALSE, 0);
196
197 if(cache->type != CACHE_TYPE_UNKNOWN) {
198 gtk_box_pack_start_defaults(GTK_BOX(colbox),
199 tip = icon_get_widget(ICON_SIZE, cache->type));
200
201 #ifndef USE_MAEMO
202 gtk_tooltips_set_tip(tips, tip, _(cache_type_tip[cache->type]), NULL);
203 #endif
204 }
205
206 /* ------------ box containing container info ------------ */
207 if(cache->container != CACHE_CONT_UNKNOWN) {
208 ivbox = gtk_vbox_new(FALSE, 0);
209 sprintf(str, _("Size: %s"), _(cache_size_name[cache->container]));
210 gtk_box_pack_start(GTK_BOX(ivbox), GTK_LABEL_SMALL(str),
211 FALSE, FALSE, 0);
212 gtk_box_pack_start(GTK_BOX(ivbox),
213 icon_get_widget(ICON_CACHE_SIZE, cache->container),
214 FALSE, FALSE, 0);
215
216 pack_vcentered(colbox, ivbox);
217 #ifndef USE_MAEMO
218 gtk_tooltips_set_tip(tips, ivbox, _(cache_size_tip[cache->container]), NULL);
219 #endif
220 }
221
222 gtk_box_pack_start_defaults(GTK_BOX(ihbox), colbox);
223 colbox = gtk_vbox_new(FALSE, 0);
224
225 /* ----------------------- id ---------------------------- */
226 if(cache->id) {
227 int strike = cache->archived?STRIKETHROUGH_RED:
228 (!cache->available?STRIKETHROUGH:STRIKETHROUGH_NONE);
229 GtkWidget *lbl = link_button_attrib(context->appdata,
230 cache->id, context->cache->url,
231 SIZE_BIG, strike);
232 pack_vcentered(colbox, lbl);
233 }
234
235 /* --------------- box containing owner info ------------- */
236 if(cache->owner) {
237 gtk_box_pack_start_defaults(GTK_BOX(colbox), GTK_LABEL_SMALL(_("by")));
238
239 static const char *owner_type = "profile/";
240 pack_vcentered(colbox,
241 link_button_by_id(appdata, cache->owner->name,
242 owner_type, cache->owner->id));
243 }
244
245 gtk_box_pack_start_defaults(GTK_BOX(ihbox), colbox);
246
247 /* ----------- vbox containing all ratings ---------- */
248 colbox = gtk_vbox_new(FALSE, 0);
249
250 /* ----------- box containing difficulty rating ---------- */
251 if(cache->difficulty != 0) {
252 ivbox = gtk_vbox_new(FALSE, 0);
253 gtk_box_pack_start(GTK_BOX(ivbox),
254 GTK_LABEL_SMALL(_("Difficulty:")),
255 FALSE, FALSE, 0);
256 gtk_box_pack_start(GTK_BOX(ivbox),
257 icon_get_widget(ICON_STARS, (int)(cache->difficulty*2-2)),
258 FALSE, FALSE, 0);
259
260 pack_vcentered(colbox, ivbox);
261 #ifndef USE_MAEMO
262 sprintf(str, _("Difficulty: %.1f"), cache->difficulty);
263 gtk_tooltips_set_tip(tips, ivbox, str, NULL);
264 #endif
265 }
266
267 /* ------------ box containing terrain rating ------------ */
268 if(cache->terrain != 0) {
269 ivbox = gtk_vbox_new(FALSE, 0);
270 gtk_box_pack_start(GTK_BOX(ivbox), GTK_LABEL_SMALL(_("Terrain:")),
271 FALSE, FALSE, 0);
272 gtk_box_pack_start(GTK_BOX(ivbox),
273 icon_get_widget(ICON_STARS, (int)(cache->terrain*2-2)),
274 FALSE, FALSE, 0);
275 pack_vcentered(colbox, ivbox);
276 #ifndef USE_MAEMO
277 sprintf(str, _("Terrain: %.1f"), cache->terrain);
278 gtk_tooltips_set_tip(tips, ivbox, str, NULL);
279 #endif
280 }
281
282 /* --------------------- GCVote ------------------------ */
283 if(!appdata->disable_gcvote) {
284 vote_t *vote = gcvote_restore(appdata, cache);
285
286 context->gcvote_request =
287 gcvote_request(appdata, gcvote_callback, cache->url, context);
288
289 context->votebox = gtk_vbox_new(FALSE, 0);
290 pack_vcentered(colbox, context->votebox);
291
292 /* fill with vote if present on disk (will also free vote) */
293 if(vote)
294 gcvote_set(context, vote);
295 }
296
297 gtk_box_pack_start_defaults(GTK_BOX(ihbox), colbox);
298
299 /* ----------------------------------------------------- */
300
301
302 /* ----------------- the two coordinates ----------------- */
303 /* ----------------- and the heading/distance ------------ */
304 colbox = gtk_vbox_new(FALSE, 0);
305
306 pos_t *refpos = get_pos(appdata);
307
308 ivbox = gtk_vbox_new(FALSE, 0);
309 int strike = (cache->notes && cache->notes->override)?
310 STRIKETHROUGH:STRIKETHROUGH_NONE;
311
312 /* the original coordinate is being displayed */
313 gtk_box_pack_start(GTK_BOX(ivbox),
314 context->pos_lat_label = pos_lat(cache->pos.lat, SIZE_BIG, strike), FALSE, FALSE, 0);
315 gtk_box_pack_start(GTK_BOX(ivbox),
316 context->pos_lon_label = pos_lon(cache->pos.lon, SIZE_BIG, strike), FALSE, FALSE, 0);
317
318 pack_vcentered(colbox, ivbox);
319
320 /* but calculations may be done with respect to the overriden one */
321 if(!isnan(cache->pos.lat) && !isnan(cache->pos.lon)) {
322 context->bearing_hbox = gtk_hbox_new(FALSE, 0);
323 bearing_fill_hbox(context->bearing_hbox, appdata, *refpos,
324 gpx_cache_pos(cache));
325 pack_vcentered(colbox, context->bearing_hbox);
326 }
327
328 gtk_box_pack_start_defaults(GTK_BOX(ihbox), colbox);
329
330 /* ----------------------------------------------------- */
331
332 gtk_box_pack_start(GTK_BOX(vbox), ihbox, FALSE, FALSE, 0);
333 gtk_box_pack_start(GTK_BOX(vbox), gtk_hseparator_new(), FALSE, FALSE, 0);
334
335 /* ----------------------------------------------------- */
336
337 if(cache->short_description)
338 gtk_box_pack_start_defaults(GTK_BOX(vbox),
339 html_view(appdata, cache->short_description,
340 cache->short_is_html?HTML_HTML:HTML_PLAIN_TEXT, TRUE, cache, NULL));
341
342 return vbox;
343 }
344
345 /* slow but short, we don't need performance here ... */
346 static void rot13(char *t) {
347 int braces = 0;
348
349 while(*t) {
350 if(!braces) {
351 if(*t == '[')
352 braces++;
353 else if(((*t >= 'a') && (*t < 'n')) ||
354 ((*t >= 'A') && (*t < 'N'))) *t += 13;
355 else if(((*t >= 'n') && (*t <= 'z')) ||
356 ((*t >= 'N') && (*t <= 'Z'))) *t -= 13;
357 } else {
358 if(braces > 0 && *t == ']')
359 braces--;
360 }
361
362 t++;
363 }
364 }
365
366 static void on_decrypt(GtkWidget *widget, gpointer data) {
367 /* data is a link to the textview */
368 g_assert(GTK_IS_TEXT_VIEW(data));
369
370 GtkTextIter start, end;
371 GtkTextBuffer *buffer = gtk_text_view_get_buffer((GtkTextView*)data);
372
373 gtk_text_buffer_get_start_iter(buffer, &start);
374 gtk_text_buffer_get_end_iter(buffer, &end);
375 char *text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
376
377 rot13(text);
378 gtk_text_buffer_set_text(buffer, text, -1);
379
380 free(text);
381 }
382
383 static GtkWidget *cache_hint(appdata_t *appdata, cache_t *cache) {
384 /* encrypting/decrypting html is nothing we want to do */
385 if(cache->hint_is_html)
386 return html_view(appdata, cache->hint, HTML_HTML, TRUE, NULL, NULL);
387
388 /* we can now be sure that we are talking about pain text */
389 GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
390
391 char *hint = strdup(cache->hint);
392 rot13(hint);
393 GtkWidget *view =
394 html_view(appdata, hint, HTML_PLAIN_TEXT, TRUE, NULL, NULL);
395 gtk_box_pack_start_defaults(GTK_BOX(vbox), view);
396 free(hint);
397
398 GtkWidget *button = gtk_button_new_with_label(_("Encrypt/Decrypt"));
399 #if defined(USE_MAEMO) && (MAEMO_VERSION_MAJOR == 5)
400 hildon_gtk_widget_set_theme_size(button,
401 (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH));
402 #endif
403 gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
404 gtk_signal_connect(GTK_OBJECT(button), "clicked",
405 GTK_SIGNAL_FUNC(on_decrypt), gtk_bin_get_child(GTK_BIN(view)));
406
407 return vbox;
408 }
409
410 static GtkWidget *cache_wpts(appdata_t *appdata, wpt_t *wpt) {
411 pos_t *refpos = NULL;
412
413 #ifndef USE_PANNABLE_AREA
414 GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
415 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
416 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
417 #else
418 GtkWidget *pannable_area = hildon_pannable_area_new();
419 #endif
420
421 GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
422
423 /* four rows per waypoint */
424 GtkWidget *table = gtk_table_new(4*gpx_number_of_waypoints(wpt)-1,4, FALSE);
425
426 refpos = get_pos(appdata);
427
428 int wpt_row=0;
429 while(wpt) {
430 GtkWidget *ihbox, *tip;
431 char str[32];
432
433 /* ----------------------- icon/id ---------------------------- */
434 ihbox = gtk_hbox_new(FALSE, 0);
435
436 if(wpt->sym != WPT_SYM_UNKNOWN) {
437 gtk_box_pack_start(GTK_BOX(ihbox),
438 tip = icon_get_widget(ICON_WPT, wpt->sym), 1,0,0);
439 }
440
441 if(wpt->id)
442 gtk_box_pack_start(GTK_BOX(ihbox), GTK_LABEL_BIG(wpt->id), 1,0,0);
443
444 gtk_table_attach_defaults(GTK_TABLE(table), ihbox, 0,1,wpt_row, wpt_row+1);
445
446 /* ----------------- the two coordinates ----------------- */
447 /* ----------------- and the heading/distance ------------ */
448 gtk_table_attach_defaults(GTK_TABLE(table),
449 pos_lat(wpt->pos.lat, SIZE_BIG, STRIKETHROUGH_NONE),
450 1,2, wpt_row, wpt_row+1);
451 gtk_table_attach_defaults(GTK_TABLE(table),
452 pos_lon(wpt->pos.lon, SIZE_BIG, STRIKETHROUGH_NONE),
453 2,3, wpt_row, wpt_row+1);
454
455 if(refpos && !isnan(refpos->lat) && !isnan(refpos->lon)) {
456 ihbox = gtk_hbox_new(FALSE, 0);
457 gtk_box_pack_start(GTK_BOX(ihbox), gtk_image_new_from_pixbuf(
458 icon_bearing(*refpos, wpt->pos)),1,0,0);
459 snprintf(str, sizeof(str), _("%.1f°"),
460 gpx_pos_get_bearing(*refpos, wpt->pos));
461 gtk_box_pack_start_defaults(GTK_BOX(ihbox), GTK_LABEL_SMALL(str));
462
463 gpx_pos_get_distance_str(str, sizeof(str),
464 *refpos, wpt->pos, appdata->imperial);
465 gtk_box_pack_start(GTK_BOX(ihbox), GTK_LABEL_SMALL(str),1,0,0);
466
467 gtk_table_attach_defaults(GTK_TABLE(table), ihbox, 3,4,
468 wpt_row+0, wpt_row+1);
469 }
470
471 /* ------------------ description ------------------------- */
472 if(wpt->desc)
473 gtk_table_attach_defaults(GTK_TABLE(table),
474 simple_text_widget(wpt->desc), 0,4,
475 wpt_row+1, wpt_row+2);
476
477 /* ------------------ comment ------------------------- */
478 if(wpt->cmt)
479 gtk_table_attach_defaults(GTK_TABLE(table),
480 simple_text_widget(wpt->cmt), 0,4,
481 wpt_row+2, wpt_row+3);
482
483 /* --------------------- seperator -------------------------*/
484 if(wpt->next) {
485 gtk_table_set_row_spacing(GTK_TABLE(table), wpt_row+2, 8);
486 gtk_table_attach_defaults(GTK_TABLE(table), gtk_hseparator_new(), 0,4,
487 wpt_row+3, wpt_row+4);
488 gtk_table_set_row_spacing(GTK_TABLE(table), wpt_row+3, 8);
489 }
490
491 wpt_row+=4;
492 wpt = wpt->next;
493 }
494
495 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
496
497 #ifndef USE_PANNABLE_AREA
498 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
499 vbox);
500 return scrolled_window;
501 #else
502 hildon_pannable_area_add_with_viewport(HILDON_PANNABLE_AREA(pannable_area),
503 vbox);
504 return pannable_area;
505 #endif
506 }
507
508 static GtkWidget *cache_tbs(appdata_t *appdata, tb_t *tb) {
509 pos_t *refpos = NULL;
510
511 #ifndef USE_PANNABLE_AREA
512 GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
513 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
514 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
515 #else
516 GtkWidget *pannable_area = hildon_pannable_area_new();
517 #endif
518
519 GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
520
521 /* four rows per waypoint */
522 GtkWidget *table = gtk_table_new(2*gpx_number_of_tbs(tb)-1,3, FALSE);
523
524 refpos = get_pos(appdata);
525
526 int tb_row=0;
527 while(tb) {
528 static const char *tb_type = "track/details.aspx";
529
530 /* --------------------- icon/ref/name -------------------------*/
531 GtkWidget *icon = NULL;
532 if((strcasestr(tb->name, "coin") != 0) ||
533 (strcasestr(tb->name, "muenze") != 0) ||
534 (strcasestr(tb->name, "münze") != 0))
535 icon = icon_get_widget(ICON_TB, 1); /* coin icon */
536 else
537 icon = icon_get_widget(ICON_TB, 0); /* tb icon */
538
539 gtk_table_attach_defaults(GTK_TABLE(table), icon,
540 0, 1, tb_row+0, tb_row+1);
541
542 if(tb->ref) {
543 GtkWidget *ref = link_button_by_id(appdata, tb->ref, tb_type, tb->id);
544 gtk_table_attach_defaults(GTK_TABLE(table), ref,
545 1, 2, tb_row+0, tb_row+1);
546 }
547
548 if(tb->name)
549 gtk_table_attach_defaults(GTK_TABLE(table), GTK_LABEL_BIG(tb->name),
550 2, 3, tb_row+0, tb_row+1);
551
552 /* --------------------- seperator -------------------------*/
553 if(tb->next)
554 gtk_table_attach_defaults(GTK_TABLE(table), gtk_hseparator_new(), 0, 3,
555 tb_row+1, tb_row+2);
556 tb_row+=2;
557 tb = tb->next;
558 }
559
560 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
561
562 #ifndef USE_PANNABLE_AREA
563 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
564 vbox);
565 return scrolled_window;
566 #else
567 hildon_pannable_area_add_with_viewport(HILDON_PANNABLE_AREA(pannable_area),
568 vbox);
569 return pannable_area;
570 #endif
571 }
572
573 #ifdef ENABLE_BROWSER_INTERFACE
574 static void on_gclink_clicked(GtkButton *button, gpointer data) {
575 cache_context_t *context = (cache_context_t*)data;
576 char *url = g_strdup_printf("http://www.geocaching.com/seek/log.aspx?wp=%s", context->cache->id);
577 browser_url(context->appdata, url);
578 g_free(url);
579 }
580 #endif
581
582 static GtkWidget *cache_logs(appdata_t *appdata, cache_context_t *context,
583 log_t *log, int is_html) {
584 #ifndef USE_PANNABLE_AREA
585 /* put this inside a scrolled view */
586 GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
587 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
588 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
589 #else
590 GtkWidget *pannable_area = hildon_pannable_area_new();
591 #endif
592
593 #ifdef ENABLE_BROWSER_INTERFACE
594 gboolean gc_link = strncmp(context->cache->id, "GC", 2) == 0;
595 #else
596 #define gc_link (FALSE)
597 #endif
598
599 GtkWidget *vbox = gtk_vbox_new(FALSE, 6);
600
601 #ifdef ENABLE_BROWSER_INTERFACE
602 if(gc_link) {
603 GtkWidget *but =
604 gtk_button_new_with_label(_("Post a new log entry for this geocache"));
605 #if 0
606 gtk_button_set_image(GTK_BUTTON(but),
607 gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_BUTTON));
608 gtk_button_set_image_position(GTK_BUTTON(but), GTK_POS_LEFT);
609 #endif
610 #if defined(USE_MAEMO) && (MAEMO_VERSION_MAJOR == 5)
611 hildon_gtk_widget_set_theme_size(but,
612 (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH));
613 #endif
614 gtk_signal_connect(GTK_OBJECT(but), "clicked",
615 GTK_SIGNAL_FUNC(on_gclink_clicked), context);
616
617 gtk_box_pack_start(GTK_BOX(vbox), but, FALSE, FALSE, 0);
618 }
619 #endif
620
621 int logs = gpx_number_of_logs(log);
622 GtkWidget *table = gtk_table_new(2*logs-1, 2,FALSE);
623 int log_cnt = 0;
624
625 gtk_table_set_col_spacing(GTK_TABLE(table), 0, 8);
626
627 /* add all logs to the vbox */
628 while(log) {
629 GtkWidget *ivbox = gtk_vbox_new(FALSE, 2);
630 GtkWidget *ihbox = gtk_hbox_new(FALSE, 2);
631
632 static const char *finder_type = "profile/";
633 GtkWidget *finder = link_button_by_id(appdata, log->finder->name,
634 finder_type, log->finder->id);
635
636 /* if the finder is a button make sure it's the right size and */
637 /* does not exceed the size limits */
638 if(GTK_WIDGET_TYPE(finder) == GTK_TYPE_BUTTON) {
639 #if defined(USE_MAEMO) && (MAEMO_VERSION_MAJOR == 5)
640 hildon_gtk_widget_set_theme_size(finder,
641 (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH));
642 #endif
643
644 gtk_label_set_ellipsize(GTK_LABEL(gtk_bin_get_child(GTK_BIN(finder))),
645 PANGO_ELLIPSIZE_END);
646 } else
647 gtk_label_set_ellipsize(GTK_LABEL(finder), PANGO_ELLIPSIZE_END);
648
649 gtk_box_pack_start(GTK_BOX(ivbox), finder, FALSE, FALSE, 0);
650
651 gtk_box_pack_start_defaults(GTK_BOX(ihbox),
652 icon_get_widget(ICON_LOG, log->type));
653
654 char date_str[32];
655 if(log->day && log->month && log->year) {
656 GDate *date = g_date_new_dmy(log->day, log->month, log->year);
657 g_date_strftime(date_str, sizeof(date_str), "%x", date);
658 g_date_free(date);
659 } else
660 strcpy(date_str, "---");
661
662 gtk_box_pack_start_defaults(GTK_BOX(ihbox), gtk_label_new(date_str));
663
664 gtk_box_pack_start(GTK_BOX(ivbox), ihbox, FALSE, FALSE, 0);
665
666 gtk_table_attach(GTK_TABLE(table), ivbox, 0, 1,
667 2*log_cnt, 2*log_cnt+1, 0, GTK_EXPAND | GTK_FILL, 0, 0);
668
669 if(log->text) {
670 gtk_table_attach_defaults(GTK_TABLE(table),
671 html_view(appdata, log->text,
672 is_html?HTML_HTML:HTML_CUSTOM_MARKUP, FALSE, NULL, NULL),
673 1, 2, 2*log_cnt, 2*log_cnt+1);
674 }
675
676 if(log_cnt < logs-1) {
677 gtk_table_set_row_spacing(GTK_TABLE(table), 2*log_cnt, 8);
678
679 gtk_table_attach_defaults(GTK_TABLE(table), gtk_hseparator_new(),
680 0, 2, 2*log_cnt+1, 2*log_cnt+2);
681
682 gtk_table_set_row_spacing(GTK_TABLE(table), 2*log_cnt+1, 8);
683 }
684
685 log = log->next;
686 log_cnt++;
687 }
688
689 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
690
691 #ifndef USE_PANNABLE_AREA
692 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
693 vbox);
694 return scrolled_window;
695 #else
696 hildon_pannable_area_add_with_viewport(HILDON_PANNABLE_AREA(pannable_area),
697 vbox);
698 return pannable_area;
699 #endif
700 }
701
702 #ifdef USE_MAEMO
703 /* this routine is called once a second as long as the "goto" tab is visible */
704 static gboolean screensaver_update(gpointer data) {
705 appdata_t *appdata = (appdata_t*)data;
706
707 if(appdata->goto_disable_screensaver)
708 if (osso_display_blanking_pause(appdata->osso_context) != OSSO_OK)
709 fprintf(stderr, "error with display blank\n");
710
711 return TRUE; // fire again
712 }
713 #endif
714
715 static void on_notebook_page_change(GtkNotebook *notebook,
716 GtkNotebookPage *page,
717 guint page_num,
718 gpointer user_data) {
719
720 cache_context_t *context = (cache_context_t*)user_data;
721 GtkWidget *w = gtk_notebook_get_nth_page(notebook, page_num);
722 const char *name = gtk_notebook_get_tab_label_text(notebook, w);
723
724 #ifdef USE_MAEMO
725 if(context->handler_id)
726 gtk_timeout_remove(context->handler_id);
727 #endif
728
729 /* this is a workaround, around some bug in the gtktextwidget or so ... */
730 /* i tried to get info on this and everybody agreed that this is a bug */
731 /* in gtk but noone had a fix ready. so i came up with this. */
732 /* seems to work ... */
733 if(strcasecmp(name, _("Logs")) == 0) {
734 gtk_widget_queue_resize(w);
735 } else if(strcasecmp(name, _("Goto")) == 0) {
736 #ifdef USE_MAEMO
737 context->handler_id = gtk_timeout_add(1000, screensaver_update,
738 context->appdata);
739 #endif
740
741 goto_coordinate_update(context);
742 }
743
744 if(strcasecmp(name, _("Main")) == 0) {
745 /* the notes page may have changed its "override" setting, thus the */
746 /* striked out coordinate may need update */
747 overview_coordinate_update(context);
748 }
749 }
750
751 static void on_notebook_destroy(GtkWidget *widget, gpointer user_data ) {
752 cache_context_t *context = (cache_context_t*)user_data;
753
754 printf("destroying notebook\n");
755
756 /* cancel a pending gcvote request */
757 if(context->gcvote_request) {
758 gcvote_request_free(context->gcvote_request);
759 context->gcvote_request = NULL;
760 }
761
762 notes_destroy_event(NULL, context);
763 goto_destroy_event(NULL, context);
764
765 #ifdef USE_MAEMO
766 if(context->handler_id)
767 gtk_timeout_remove(context->handler_id);
768 #endif
769
770 free(user_data);
771 }
772
773 #if defined(USE_MAEMO) && (MAEMO_VERSION_MAJOR >= 5)
774 #define CUSTOM_NOTEBOOK
775 #endif
776
777 static GtkWidget *notebook_new(void) {
778 #ifdef CUSTOM_NOTEBOOK
779 GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
780
781 GtkWidget *notebook = gtk_notebook_new();
782
783 /* prevents user from accidentially touching the breadcrumb trail */
784 /* (looks ugly on fremantle as it isn't themed) */
785 // gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_BOTTOM);
786
787 /* solution for fremantle: we use a row of ordinary buttons instead */
788 /* of regular tabs */
789
790 /* hide the regular tabs */
791 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE);
792
793 gtk_box_pack_start_defaults(GTK_BOX(vbox), notebook);
794
795 /* store a reference to the notebook in the vbox */
796 g_object_set_data(G_OBJECT(vbox), "notebook", (gpointer)notebook);
797
798 /* create a hbox for the buttons */
799 GtkWidget *hbox = gtk_hbox_new(TRUE, 0);
800 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
801 g_object_set_data(G_OBJECT(vbox), "hbox", (gpointer)hbox);
802
803 return vbox;
804 #else
805 return gtk_notebook_new();
806 #endif
807 }
808
809 #ifdef CUSTOM_NOTEBOOK
810 static void on_notebook_button_clicked(GtkWidget *button, gpointer data) {
811 GtkNotebook *nb =
812 GTK_NOTEBOOK(g_object_get_data(G_OBJECT(data), "notebook"));
813
814 gint page = (gint)g_object_get_data(G_OBJECT(button), "page");
815 gtk_notebook_set_current_page(nb, page);
816 }
817 #endif
818
819 static void notebook_append_page(GtkWidget *notebook,
820 GtkWidget *page, char *label) {
821 #ifdef CUSTOM_NOTEBOOK
822 GtkNotebook *nb =
823 GTK_NOTEBOOK(g_object_get_data(G_OBJECT(notebook), "notebook"));
824
825 gint page_num = gtk_notebook_append_page(nb, page, gtk_label_new(label));
826 GtkWidget *button = NULL;
827
828 /* select button for page 0 by default */
829 if(!page_num) {
830 button = gtk_radio_button_new_with_label(NULL, label);
831 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
832 g_object_set_data(G_OBJECT(notebook), "group_master", (gpointer)button);
833 } else {
834 GtkWidget *master = g_object_get_data(G_OBJECT(notebook), "group_master");
835 button = gtk_radio_button_new_with_label_from_widget(
836 GTK_RADIO_BUTTON(master), label);
837 }
838
839 gtk_toggle_button_set_mode(GTK_TOGGLE_BUTTON(button), FALSE);
840 g_object_set_data(G_OBJECT(button), "page", (gpointer)page_num);
841
842 gtk_signal_connect(GTK_OBJECT(button), "clicked",
843 GTK_SIGNAL_FUNC(on_notebook_button_clicked), notebook);
844
845 #if defined(USE_MAEMO) && (MAEMO_VERSION_MAJOR == 5)
846 hildon_gtk_widget_set_theme_size(button,
847 (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH));
848 #endif
849
850 gtk_box_pack_start_defaults(
851 GTK_BOX(g_object_get_data(G_OBJECT(notebook), "hbox")),
852 button);
853
854 #else
855 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, gtk_label_new(label));
856 #endif
857 }
858
859 static GObject *notebook_object(GtkWidget *notebook) {
860 #ifdef CUSTOM_NOTEBOOK
861 return G_OBJECT(g_object_get_data(G_OBJECT(notebook), "notebook"));
862 #else
863 return G_OBJECT(notebook);
864 #endif
865 }
866
867 GtkWidget *cache_view(appdata_t *appdata, cache_t *cache) {
868 GtkWidget *notebook;
869
870 cache_context_t *cache_context = malloc(sizeof(cache_context_t));
871 memset(cache_context, 0, sizeof(cache_context_t));
872 cache_context->appdata = appdata;
873 cache_context->cache = cache;
874
875 #ifdef USE_MAEMO
876 #define TAB_DESC _("Desc.")
877 #define TAB_WPTS _("Wpts")
878 #else
879 #define TAB_DESC _("Description")
880 #define TAB_WPTS _("Waypoints")
881 #endif
882
883 notebook = notebook_new();
884
885 notebook_append_page(notebook,
886 cache_overview(cache_context), _("Main"));
887
888 if(cache->long_description)
889 notebook_append_page(notebook,
890 cache_description(appdata, cache), TAB_DESC);
891
892 if(cache->hint)
893 notebook_append_page(notebook,
894 cache_hint(appdata, cache), _("Hint"));
895
896 if(cache->log)
897 notebook_append_page(notebook,
898 cache_logs(appdata, cache_context, cache->log, cache->logs_are_html),
899 _("Logs"));
900
901 if(cache->wpt)
902 notebook_append_page(notebook,
903 cache_wpts(appdata, cache->wpt), TAB_WPTS);
904
905 if(cache->tb)
906 notebook_append_page(notebook,
907 cache_tbs(appdata, cache->tb), _("TBs"));
908
909 notebook_append_page(notebook,
910 cache_notes(cache_context), _("Notes"));
911
912 notebook_append_page(notebook,
913 goto_cache(cache_context), _("Goto"));
914
915 g_signal_connect(notebook_object(notebook), "switch-page",
916 G_CALLBACK(on_notebook_page_change), cache_context);
917
918 g_signal_connect(notebook_object(notebook), "destroy",
919 G_CALLBACK(on_notebook_destroy), cache_context);
920
921 return notebook;
922 }
923
924 #ifndef USE_MAEMO
925 void cache_dialog(appdata_t *appdata, cache_t *cache) {
926 GtkWidget *dialog = gtk_dialog_new_with_buttons(cache->name,
927 GTK_WINDOW(appdata->window),
928 GTK_DIALOG_NO_SEPARATOR | GTK_DIALOG_MODAL |
929 GTK_DIALOG_DESTROY_WITH_PARENT,
930 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
931 NULL);
932
933 gtk_window_set_default_size(GTK_WINDOW(dialog), DIALOG_WIDTH, DIALOG_HEIGHT);
934
935 /* create cache visualization widget */
936 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox),
937 cache_view(appdata, cache));
938
939 gtk_widget_show_all(dialog);
940 gtk_dialog_run(GTK_DIALOG(dialog));
941 gtk_widget_destroy(dialog);
942 }
943
944 #else
945 #ifdef USE_STACKABLE_WINDOW
946 static void on_cache_destroy (GtkWidget *widget, appdata_t *appdata) {
947 appdata->cur_cache = NULL;
948
949 /* restore cur_view */
950 appdata->cur_view = g_object_get_data(G_OBJECT(widget), "cur_view");
951 }
952
953 void cache_dialog(appdata_t *appdata, cache_t *cache) {
954 GtkWidget *window = hildon_stackable_window_new();
955
956 /* store last "cur_view" in window */
957 g_object_set_data(G_OBJECT(window), "cur_view", appdata->cur_view);
958
959 appdata->cur_cache = cache;
960 char *title = g_strdup_printf("%s - GPXView", cache->name);
961 gtk_window_set_title(GTK_WINDOW(window), title);
962 g_free(title);
963
964 /* create cache visualization widget */
965 appdata->cur_view = cache_view(appdata, cache);
966 gtk_container_add(GTK_CONTAINER(window), appdata->cur_view);
967
968 hildon_window_set_app_menu(HILDON_WINDOW(window),
969 menu_create(appdata, MENU_CACHE));
970
971 g_signal_connect(G_OBJECT(window), "destroy",
972 G_CALLBACK(on_cache_destroy), appdata);
973
974 gtk_widget_show_all(window);
975 }
976 #endif // USE_STACKABLE_WINDOW
977
978 #endif // USE_MAEMO