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