quit on window close
[belltower] / belltower.c
1 /*
2  * belltower
3  * an app to find belltowers under Maemo 5
4  *
5  * Copyright (c) 2009 Thomas Thurman <tthurman@gnome.org>
6  * Released under the GPL
7  */
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <glib.h>
12 #include <hildon/hildon.h>
13 #include <gtk/gtk.h>
14 #include <location/location-gps-device.h>
15 #include <dbus/dbus-glib.h>
16
17 #define MAX_FIELDS 50
18
19 #define EM_DASH "\xE2\x80\x94"
20
21 GtkWidget *window;
22
23 typedef enum {
24   FieldPrimaryKey,
25   FieldNationalGrid,
26   FieldAccRef,
27   FieldSNLat,
28   FieldSNLong,
29   FieldPostcode,
30   FieldTowerBase,
31   FieldCounty,
32   FieldCountry,
33   FieldDiocese,
34   FieldPlace,
35   FieldPlace2,
36   FieldPlaceCL,
37   FieldDedication,
38   FieldBells,
39   FieldWt,
40   FieldApp,
41   FieldNote,
42   FieldHz,
43   FieldDetails,
44   FieldGF,
45   FieldToilet,
46   FieldUR,
47   FieldPDNo,
48   FieldPracticeNight,
49   FieldPSt,
50   FieldPrXF,
51   FieldOvhaulYr,
52   FieldContractor,
53   FieldExtraInfo,
54   FieldWebPage,
55   FieldUpdated,
56   FieldAffiliations,
57   FieldAltName,
58   FieldLat,
59   FieldLong,
60   FieldSimulator
61 } field;
62
63 typedef struct {
64   int serial;
65
66   /* the raw data */
67
68   char* fields[MAX_FIELDS];
69   int n_fields;
70 } tower;
71
72 static void
73 show_message (char *message)
74 {
75   HildonNote* note = HILDON_NOTE
76     (hildon_note_new_information (GTK_WINDOW (window),
77                                   message?message:
78                                   "Some message was supposed to be here."));
79   gtk_dialog_run (GTK_DIALOG (note));
80   gtk_widget_destroy (GTK_WIDGET (note));
81 }
82
83 static void
84 call_dbus (DBusBusType type,
85            char *name,
86            char *path,
87            char *interface,
88            char *method,
89            char *parameter)
90 {
91   DBusGConnection *connection;
92   GError *error = NULL;
93
94   DBusGProxy *proxy;
95
96   connection = dbus_g_bus_get (type,
97                                &error);
98   if (connection == NULL)
99     {
100       show_message (error->message);
101       g_error_free (error);
102       return;
103     }
104
105   proxy = dbus_g_proxy_new_for_name (connection, name, path, interface);
106
107   error = NULL;
108   if (!dbus_g_proxy_call (proxy, method, &error,
109                           G_TYPE_STRING, parameter,
110                           G_TYPE_INVALID,
111                           G_TYPE_INVALID))
112     {
113       show_message (error->message);
114       g_error_free (error);
115     }
116 }
117
118 static void
119 show_browser (gchar *url)
120 {
121   call_dbus (DBUS_BUS_SESSION,
122              "com.nokia.osso_browser",
123              "/com/nokia/osso_browser/request",
124              "com.nokia.osso_browser",
125              "load_url",
126              url);
127 }
128
129 typedef gboolean (*ParseDoveCallback)(tower *details, gpointer data);
130 typedef void (*ButtonCallback)(void);
131
132 GtkWidget *tower_window, *buttons, *tower_table;
133 HildonAppMenu *menu;
134
135 static void
136 add_table_field (char *name,
137                  char *value)
138 {
139   int row;
140   GtkLabel *label;
141   gchar *str;
142
143   g_object_get(tower_table, "n-rows", &row);
144
145   row++;
146
147   gtk_table_resize (GTK_TABLE (tower_table), row, 2);
148
149   label = GTK_LABEL (gtk_label_new (NULL));
150   str = g_strdup_printf("<b>%s</b>", name);
151   gtk_label_set_markup (label, str);
152   g_free (str);
153   gtk_label_set_justify (label, GTK_JUSTIFY_RIGHT);
154   gtk_table_attach_defaults (GTK_TABLE (tower_table),
155                              GTK_WIDGET (label),
156                              0, 1, row, row+1);
157
158   label = GTK_LABEL (gtk_label_new (value));
159   gtk_label_set_justify (label, GTK_JUSTIFY_LEFT);
160   gtk_table_attach_defaults (GTK_TABLE (tower_table),
161                              GTK_WIDGET (label),
162                              1, 2, row, row+1);
163 }
164
165 static void
166 add_button (char *label,
167             ButtonCallback callback)
168 {
169   GtkWidget *button;
170
171   button = gtk_button_new_with_label (label);
172   g_signal_connect (button, "clicked", G_CALLBACK (callback), NULL);
173   hildon_app_menu_append (menu, GTK_BUTTON (button));
174   button = hildon_button_new_with_text (HILDON_SIZE_AUTO_WIDTH | HILDON_SIZE_FINGER_HEIGHT,
175                                         HILDON_BUTTON_ARRANGEMENT_VERTICAL,
176                                         label, NULL);
177   g_signal_connect (button, "clicked", G_CALLBACK (callback), NULL);
178   gtk_box_pack_end (GTK_BOX (buttons), button, FALSE, FALSE, 0);
179 }
180
181
182 static void
183 bookmark_toggled (GtkButton *button,
184                   gpointer dummy)
185 {
186   show_message ("Bookmarks are not yet implemented.");
187 }
188
189 char *tower_website = NULL;
190 char *tower_map = NULL;
191 char *tower_directions = NULL;
192 char *peals_list = NULL;
193
194 static void
195 show_tower_website (void)
196 {
197   show_browser (tower_website);
198 }
199
200 static void
201 show_tower_map (void)
202 {
203   show_browser (tower_map);
204 }
205
206 static void
207 show_peals_list (void)
208 {
209   show_browser (peals_list);
210 }
211
212 static gboolean
213 get_areas_cb (tower *details,
214               gpointer data)
215 {
216   GHashTable *hash = (GHashTable *)data;
217
218   /*
219   if (details->serial==0)
220     return TRUE; /* header row */
221
222   if (!g_hash_table_lookup_extended (hash,
223                                     details->fields[FieldCounty],
224                                      NULL, NULL))
225     {
226       char *display_format;
227
228       if (strcmp (details->fields[FieldCounty], "")==0)
229         {
230           display_format = g_strdup (details->fields[FieldCountry]);
231         }
232       else
233         {
234           display_format = g_strdup_printf ("%s " EM_DASH " %s",
235                                             details->fields[FieldCountry],
236                                             details->fields[FieldCounty]);
237         }
238
239       g_hash_table_insert (hash,
240                            g_strdup(details->fields[FieldCounty]),
241                            display_format);
242     }
243
244   return TRUE;
245 }
246
247 static gboolean
248 single_tower_cb (tower *details,
249                  gpointer data)
250 {
251
252   GtkWidget *hbox, *button;
253   gchar *str;
254   gint tenor_weight;
255   gchar *primary_key = (gchar*) data;
256
257   if (strcmp(details->fields[FieldPrimaryKey], primary_key)!=0)
258     {
259       /* not this one; keep going */
260       return TRUE;
261     }
262
263   tower_window = hildon_stackable_window_new ();
264
265   if (g_str_has_prefix (details->fields[FieldDedication],
266                         "S "))
267     {
268       /* FIXME: This needs to be cleverer, because we can have
269        * e.g. "S Peter and S Paul".
270        * May have to use regexps.
271        * Reallocation in general even when unchanged is okay,
272        * because it's the common case (most towers are S Something)
273        */
274       
275       /* FIXME: Since we're passing this in as markup,
276        * we need to escape the strings.
277        */
278
279       str = g_strdup_printf("S<sup>t</sup> %s, %s",
280                               details->fields[FieldDedication]+2,
281                               details->fields[FieldPlace]);
282
283     }
284   else
285     {
286       str = g_strdup_printf("%s, %s",
287                               details->fields[FieldDedication],
288                               details->fields[FieldPlace]);
289     }
290
291   hildon_window_set_markup (HILDON_WINDOW (tower_window),
292                             str);
293   g_free (str);
294
295   hbox = gtk_hbox_new (FALSE, 0);
296   tower_table = gtk_table_new (0, 2, FALSE);
297   buttons = gtk_vbox_new (TRUE, 0);
298   menu = HILDON_APP_MENU (hildon_app_menu_new ());
299
300   add_table_field ("Postcode", details->fields[FieldPostcode]);
301   add_table_field ("County", details->fields[FieldCounty]);
302   add_table_field ("Country", details->fields[FieldCountry]);
303   add_table_field ("Diocese", details->fields[FieldDiocese]);
304   add_table_field ("Practice night", details->fields[FieldPracticeNight]);
305   add_table_field ("Bells", details->fields[FieldBells]);
306
307   tenor_weight = atoi (details->fields[FieldWt]);
308   str = g_strdup_printf("%dcwt %dqr %dlb in %s",
309                         tenor_weight/112,
310                         (tenor_weight % 112)/28,
311                         tenor_weight % 28,
312                         details->fields[FieldNote]
313                         );
314   add_table_field ("Tenor", str);
315   g_free (str);
316
317   add_button ("Tower website", show_tower_website);
318   add_button ("Peals", show_peals_list);
319   add_button ("Map", show_tower_map);
320   add_button ("Directions", NULL);
321
322   /* don't use a toggle button: it looks stupid */
323   button = hildon_button_new_with_text (HILDON_SIZE_AUTO_WIDTH | HILDON_SIZE_FINGER_HEIGHT,
324                                         HILDON_BUTTON_ARRANGEMENT_VERTICAL,
325                                         "Bookmark", NULL);
326   g_signal_connect (button, "clicked", G_CALLBACK (bookmark_toggled), NULL);
327   gtk_box_pack_start (GTK_BOX (buttons), button, FALSE, FALSE, 0);
328
329   gtk_widget_show_all (GTK_WIDGET (menu));
330   hildon_window_set_app_menu (HILDON_WINDOW (tower_window), menu);
331
332   gtk_box_pack_end (GTK_BOX (hbox), buttons, TRUE, TRUE, 0);
333   gtk_box_pack_end (GTK_BOX (hbox), tower_table, TRUE, TRUE, 0);
334
335   gtk_container_add (GTK_CONTAINER (tower_window), hbox);
336
337   g_free (tower_website);
338   tower_website = g_strdup_printf ("http://%s", details->fields[FieldWebPage]);
339   g_free (peals_list);
340   peals_list = g_strdup_printf ("http://www.pealbase.ismysite.co.uk/felstead/tbid.php?tid=%s",
341        details->fields[FieldTowerBase]);
342   g_free (tower_map);
343   tower_map = g_strdup_printf ("http://maps.google.com/maps?q=%s,%s",
344         details->fields[FieldLat],
345         details->fields[FieldLong]);
346   gtk_widget_show_all (GTK_WIDGET (tower_window));
347
348   return FALSE;
349 }
350
351 static void
352 parse_dove (ParseDoveCallback callback,
353             gpointer data)
354 {
355   FILE *dove = fopen("/usr/share/belltower/dove.txt", "r");
356   char tower_rec[4096];
357   tower result;
358   char *i;
359   gboolean seen_newline;
360
361   if (!dove)
362     {
363       show_message ("Cannot open Dove database!");
364       exit (255);
365     }
366
367   result.serial = 0;
368
369   while (fgets (tower_rec, sizeof (tower_rec), dove))
370     {
371       seen_newline = FALSE;
372       result.fields[0] = tower_rec;
373       result.n_fields = 0;
374       for (i=tower_rec; *i; i++) {
375         if (*i=='\n')
376           {
377             seen_newline = TRUE;
378           }
379         if (*i=='\\' || *i=='\n')
380           {
381             *i = 0;
382             result.n_fields++;
383             result.fields[result.n_fields] = i+1;
384           }
385       }
386
387       if (!seen_newline)
388         {
389           /* keep it simple, stupid */
390           show_message ("Line too long, cannot continue.");
391           exit (255);
392         }
393
394       if (strcmp (result.fields[FieldCountry], "")==0)
395         {
396           result.fields[FieldCountry] = "England";
397         }
398
399       if (!callback (&result, data))
400         {
401           fclose (dove);
402           return;
403         }
404     }
405
406   fclose (dove);
407 }
408
409 static void
410 nearby_towers (void)
411 {
412   char buffer[4096];
413   LocationGPSDevice *device;
414   device = g_object_new (LOCATION_TYPE_GPS_DEVICE, NULL);
415
416   sprintf(buffer, "%f %f %x",
417       device->fix->latitude,
418       device->fix->longitude,
419       device->fix->fields);
420   show_message (buffer);
421
422   if (device->fix->fields & LOCATION_GPS_DEVICE_LATLONG_SET)
423     {
424       show_message ("I know where you are!");
425     }
426   else
427     {
428       show_message ("I don't know where you are!");
429     }
430
431   g_object_unref (device);
432 }
433
434 static void
435 show_tower (char *primary_key)
436 {
437   parse_dove (single_tower_cb,
438               primary_key);
439 }
440
441 static void
442 put_areas_into_list (gpointer key,
443                      gpointer value,
444                      gpointer data)
445 {
446   GtkTreeIter iter;
447   GtkListStore *list_store = (GtkListStore*) data;
448   gtk_list_store_append (list_store, &iter);
449   gtk_list_store_set (list_store, &iter,
450                       0, value, 
451                       -1);
452 }
453
454 static void
455 towers_by_area (void)
456 {
457   GtkWidget *dialog = gtk_dialog_new ();
458   GtkWidget *vbox = GTK_DIALOG(dialog)->vbox;
459   GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
460   GtkWidget *treeview = hildon_gtk_tree_view_new (HILDON_UI_MODE_NORMAL);
461   GtkWidget *pan = hildon_pannable_area_new ();
462   GtkListStore *list_store = gtk_list_store_new(1, G_TYPE_STRING);
463
464   GHashTable *hash = g_hash_table_new_full (g_str_hash,
465                                             g_str_equal,
466                                             g_free,
467                                             g_free);
468   /*
469   g_free (hash);
470   */
471
472   parse_dove (get_areas_cb,
473               hash);
474
475   g_hash_table_foreach (hash,
476                         put_areas_into_list,
477                         list_store);      
478
479   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (list_store),
480                                         0,
481                                         GTK_SORT_ASCENDING);
482
483   g_object_set (GTK_OBJECT(renderer), "yalign", 0.5, NULL);
484   g_object_set (GTK_OBJECT(renderer), "xpad", 24, NULL);
485
486   gtk_window_set_title (GTK_WINDOW (dialog), "Towers by area");
487
488   gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
489                                                0, "",
490                                                renderer,
491                                                "text", 0,
492                                                NULL);
493
494   gtk_tree_view_set_model (GTK_TREE_VIEW (treeview),
495                            GTK_TREE_MODEL (list_store));
496   g_object_unref (list_store);
497
498   hildon_pannable_area_add_with_viewport (HILDON_PANNABLE_AREA (pan),
499                                           treeview);
500
501   gtk_widget_set_size_request (treeview, 480, 800);
502   hildon_pannable_area_set_size_request_policy (HILDON_PANNABLE_AREA (pan),
503                                                 HILDON_SIZE_REQUEST_CHILDREN);
504   gtk_container_add(GTK_CONTAINER (vbox), pan);
505
506   gtk_widget_show_all (GTK_WIDGET (dialog));
507
508   gtk_dialog_run (GTK_DIALOG (dialog));
509   gtk_widget_destroy (GTK_WIDGET (dialog));
510 }
511
512 static void
513 show_bookmarks (void)
514 {
515   /* nothing */
516 }
517
518 static void
519 tower_search (void)
520 {
521   /* nothing */
522 }
523
524 static void
525 recent_towers (void)
526 {
527   show_tower ("NORTON  HE");
528 }
529
530 int
531 main(int argc, char **argv)
532 {
533   GtkWidget *bell, *button, *hbox;
534   GdkPixbuf *bell_picture;
535
536   gtk_init (&argc, &argv);
537   g_set_application_name ("Belltower");
538
539   window = hildon_stackable_window_new ();
540   gtk_window_set_title (GTK_WINDOW (window), "Belltower");
541   g_signal_connect (G_OBJECT (window), "delete_event", G_CALLBACK (gtk_main_quit), NULL);
542
543   bell_picture = gdk_pixbuf_new_from_file ("/usr/share/belltower/bells1.jpg", NULL);
544
545   buttons = gtk_vbox_new (TRUE, 0);
546   menu = HILDON_APP_MENU (hildon_app_menu_new ());
547
548   add_button ("Nearby", nearby_towers);
549   add_button ("Recent", recent_towers);
550   add_button ("Bookmarks", show_bookmarks);
551   add_button ("By area", towers_by_area);
552   add_button ("Search", tower_search);
553
554   /* extra buttons for the app menu */
555   button = gtk_button_new_with_label ("Credits");
556   hildon_app_menu_append (menu, GTK_BUTTON (button));
557   hildon_app_menu_append (menu, GTK_BUTTON (button));
558
559   gtk_widget_show_all (GTK_WIDGET (menu));
560   hildon_window_set_app_menu (HILDON_WINDOW (window), menu);
561
562   hbox = gtk_hbox_new (FALSE, 0);
563   gtk_box_pack_end (GTK_BOX (hbox), buttons, TRUE, TRUE, 0);
564   gtk_box_pack_end (GTK_BOX (hbox),
565                     gtk_image_new_from_pixbuf (bell_picture),
566                     TRUE, TRUE, 0);
567
568   gtk_container_add (GTK_CONTAINER (window), hbox);
569   gtk_widget_show_all (GTK_WIDGET (window));
570
571   gtk_main ();
572
573   return EXIT_SUCCESS;
574 }