3 * an app to find belltowers under Maemo 5
5 * Copyright (c) 2009 Thomas Thurman <tthurman@gnome.org>
6 * Released under the GPL
13 #include <hildon/hildon.h>
15 #include <location/location-gps-device.h>
16 #include <location/location-distance-utils.h>
17 #include <dbus/dbus-glib.h>
23 LocationGPSDevice *device;
26 /** stop scanning the database */
28 /** ignore this one */
30 /** add this one to the list */
36 We should really do this by looking at the header row of the table.
37 They might decide to put in new columns some day.
84 char* fields[MAX_FIELDS];
89 * we're going to pretend you're in Helsinki
90 * until I get the GPS working
92 gboolean gps_working = FALSE;
93 double current_lat = 60.161790;
94 double current_long = 23.924902;
97 show_message (char *message)
99 HildonNote* note = HILDON_NOTE
100 (hildon_note_new_information (GTK_WINDOW (window),
102 "Some message was supposed to be here."));
103 gtk_dialog_run (GTK_DIALOG (note));
104 gtk_widget_destroy (GTK_WIDGET (note));
108 distance_to_tower (tower *details)
114 const double km_to_miles = 1.609344;
116 tower_lat = strtod(details->fields[FieldLat], &endptr);
117 if (*endptr) return g_strdup ("unknown");
118 tower_long = strtod(details->fields[FieldLong], &endptr);
119 if (*endptr) return g_strdup ("unknown");
121 km_distance = location_distance_between (current_lat,
126 return g_strdup_printf("%dmi", (int) (km_distance / km_to_miles));
130 call_dbus (DBusBusType type,
137 DBusGConnection *connection;
138 GError *error = NULL;
142 connection = dbus_g_bus_get (type,
144 if (connection == NULL)
146 show_message (error->message);
147 g_error_free (error);
151 proxy = dbus_g_proxy_new_for_name (connection, name, path, interface);
154 if (!dbus_g_proxy_call (proxy, method, &error,
155 G_TYPE_STRING, parameter,
159 show_message (error->message);
160 g_error_free (error);
165 show_browser (gchar *url)
167 call_dbus (DBUS_BUS_SESSION,
168 "com.nokia.osso_browser",
169 "/com/nokia/osso_browser/request",
170 "com.nokia.osso_browser",
175 typedef FilterResult (*ParseDoveCallback)(tower *details, gpointer data);
176 typedef void (*ButtonCallback)(void);
178 GtkWidget *tower_window, *buttons, *tower_table;
182 add_table_field (char *name,
189 g_object_get(tower_table, "n-rows", &row);
193 gtk_table_resize (GTK_TABLE (tower_table), row, 2);
195 label = GTK_LABEL (gtk_label_new (NULL));
196 str = g_strdup_printf("<b>%s</b>", name);
197 gtk_label_set_markup (label, str);
199 gtk_label_set_justify (label, GTK_JUSTIFY_RIGHT);
200 gtk_table_attach_defaults (GTK_TABLE (tower_table),
204 label = GTK_LABEL (gtk_label_new (value));
205 gtk_label_set_justify (label, GTK_JUSTIFY_LEFT);
206 gtk_table_attach_defaults (GTK_TABLE (tower_table),
212 add_button (char *label,
213 ButtonCallback callback)
217 button = gtk_button_new_with_label (label);
218 g_signal_connect (button, "clicked", G_CALLBACK (callback), NULL);
219 hildon_app_menu_append (menu, GTK_BUTTON (button));
220 button = hildon_button_new_with_text (HILDON_SIZE_AUTO_WIDTH | HILDON_SIZE_FINGER_HEIGHT,
221 HILDON_BUTTON_ARRANGEMENT_VERTICAL,
223 g_signal_connect (button, "clicked", G_CALLBACK (callback), NULL);
224 gtk_box_pack_end (GTK_BOX (buttons), button, FALSE, FALSE, 0);
229 bookmark_toggled (GtkButton *button,
232 show_message ("Bookmarks are not yet implemented.");
235 char *tower_website = NULL;
236 char *tower_map = NULL;
237 char *tower_directions = NULL;
238 char *peals_list = NULL;
241 show_tower_website (void)
243 show_browser (tower_website);
247 show_tower_map (void)
249 show_browser (tower_map);
253 show_peals_list (void)
255 show_browser (peals_list);
259 get_countries_cb (tower *details,
262 GHashTable *hash = (GHashTable *)data;
264 if (details->serial==0)
265 return TRUE; /* header row */
267 if (!g_hash_table_lookup_extended (hash,
268 details->fields[FieldCountry],
271 g_hash_table_insert (hash,
272 g_strdup(details->fields[FieldCountry]),
273 g_strdup (details->fields[FieldCountry]));
276 return FILTER_IGNORE;
287 } country_and_county;
290 get_counties_cb (tower *details,
293 country_cb_data *d = (country_cb_data *)data;
295 if (details->serial==0)
296 return FILTER_IGNORE; /* header row */
298 if (strcmp(details->fields[FieldCountry], d->country_name)!=0)
299 return FILTER_IGNORE; /* wrong country */
301 if (!g_hash_table_lookup_extended (d->hash,
302 details->fields[FieldCounty],
305 g_hash_table_insert (d->hash,
306 g_strdup(details->fields[FieldCounty]),
307 g_strdup (details->fields[FieldCounty]));
310 return FILTER_IGNORE;
314 get_towers_by_county_cb (tower *details,
317 country_and_county *cac = (country_and_county *) data;
319 if ((!cac->county || strcmp (cac->county, details->fields[FieldCounty])==0) &&
320 (!cac->country || strcmp (cac->country, details->fields[FieldCountry])==0))
322 return FILTER_ACCEPT;
326 return FILTER_IGNORE;
331 get_towers_by_search_cb (tower *details,
334 char *s = (char *) data;
336 if (strcasestr(details->fields[FieldCountry], s) ||
337 strcasestr(details->fields[FieldCounty], s) ||
338 strcasestr(details->fields[FieldDedication], s) ||
339 strcasestr(details->fields[FieldPlace], s))
341 return FILTER_ACCEPT;
345 return FILTER_IGNORE;
350 single_tower_cb (tower *details,
354 GtkWidget *hbox, *button;
357 gchar *primary_key = (gchar*) data;
360 if (strcmp(details->fields[FieldPrimaryKey], primary_key)!=0)
362 /* not this one; keep going */
363 return FILTER_IGNORE;
366 tower_window = hildon_stackable_window_new ();
368 if (g_str_has_prefix (details->fields[FieldDedication],
371 /* FIXME: This needs to be cleverer, because we can have
372 * e.g. "S Peter and S Paul".
373 * May have to use regexps.
374 * Reallocation in general even when unchanged is okay,
375 * because it's the common case (most towers are S Something)
378 /* FIXME: Since we're passing this in as markup,
379 * we need to escape the strings.
382 str = g_strdup_printf("S<sup>t</sup> %s, %s",
383 details->fields[FieldDedication]+2,
384 details->fields[FieldPlace]);
389 str = g_strdup_printf("%s, %s",
390 details->fields[FieldDedication],
391 details->fields[FieldPlace]);
394 hildon_window_set_markup (HILDON_WINDOW (tower_window),
398 hbox = gtk_hbox_new (FALSE, 0);
399 tower_table = gtk_table_new (0, 2, FALSE);
400 buttons = gtk_vbox_new (TRUE, 0);
401 menu = HILDON_APP_MENU (hildon_app_menu_new ());
403 miles = distance_to_tower(details);
405 add_table_field ("Distance", miles);
406 add_table_field ("Postcode", details->fields[FieldPostcode]);
407 add_table_field ("County", details->fields[FieldCounty]);
408 add_table_field ("Country", details->fields[FieldCountry]);
409 add_table_field ("Diocese", details->fields[FieldDiocese]);
410 add_table_field ("Practice night", details->fields[FieldPracticeNight]);
411 add_table_field ("Bells", details->fields[FieldBells]);
415 tenor_weight = atoi (details->fields[FieldWt]);
416 str = g_strdup_printf("%dcwt %dqr %dlb in %s",
418 (tenor_weight % 112)/28,
420 details->fields[FieldNote]
422 add_table_field ("Tenor", str);
425 add_button ("Tower website", show_tower_website);
426 add_button ("Peals", show_peals_list);
427 add_button ("Map", show_tower_map);
428 add_button ("Directions", NULL);
430 /* don't use a toggle button: it looks stupid */
431 button = hildon_button_new_with_text (HILDON_SIZE_AUTO_WIDTH | HILDON_SIZE_FINGER_HEIGHT,
432 HILDON_BUTTON_ARRANGEMENT_VERTICAL,
434 g_signal_connect (button, "clicked", G_CALLBACK (bookmark_toggled), NULL);
435 gtk_box_pack_start (GTK_BOX (buttons), button, FALSE, FALSE, 0);
437 gtk_widget_show_all (GTK_WIDGET (menu));
438 hildon_window_set_app_menu (HILDON_WINDOW (tower_window), menu);
440 gtk_box_pack_end (GTK_BOX (hbox), buttons, TRUE, TRUE, 0);
441 gtk_box_pack_end (GTK_BOX (hbox), tower_table, TRUE, TRUE, 0);
443 gtk_container_add (GTK_CONTAINER (tower_window), hbox);
445 g_free (tower_website);
446 tower_website = g_strdup_printf ("http://%s", details->fields[FieldWebPage]);
448 peals_list = g_strdup_printf ("http://www.pealbase.ismysite.co.uk/felstead/tbid.php?tid=%s",
449 details->fields[FieldTowerBase]);
451 tower_map = g_strdup_printf ("http://maps.google.com/maps?q=%s,%s",
452 details->fields[FieldLat],
453 details->fields[FieldLong]);
454 gtk_widget_show_all (GTK_WIDGET (tower_window));
460 * A tower that was accepted by a filter.
469 found_tower_new (tower *basis)
471 FoundTower* result = g_new (FoundTower, 1);
473 result->sortkey = g_strdup (basis->fields[FieldPrimaryKey]);
474 result->primarykey = g_strdup (basis->fields[FieldPrimaryKey]);
478 gchar *distance = distance_to_tower (basis);
479 result->displayname = g_strdup_printf ("%s, %s (%s, %s) (%s)",
480 basis->fields[FieldDedication],
481 basis->fields[FieldPlace],
482 basis->fields[FieldBells],
483 basis->fields[FieldPracticeNight],
489 result->displayname = g_strdup_printf ("%s, %s (%s, %s)",
490 basis->fields[FieldDedication],
491 basis->fields[FieldPlace],
492 basis->fields[FieldBells],
493 basis->fields[FieldPracticeNight]);
500 found_tower_free (FoundTower *tower)
502 g_free (tower->sortkey);
503 g_free (tower->primarykey);
504 g_free (tower->displayname);
509 parse_dove (ParseDoveCallback callback,
510 GSList **filter_results,
513 FILE *dove = fopen("/usr/share/belltower/dove.txt", "r");
514 char tower_rec[4096];
517 gboolean seen_newline;
521 show_message ("Cannot open Dove database!");
527 while (fgets (tower_rec, sizeof (tower_rec), dove))
529 seen_newline = FALSE;
530 result.fields[0] = tower_rec;
532 for (i=tower_rec; *i; i++) {
537 if (*i=='\\' || *i=='\n')
541 result.fields[result.n_fields] = i+1;
547 /* keep it simple, stupid */
548 show_message ("Line too long, cannot continue.");
552 if (strcmp (result.fields[FieldCountry], "")==0)
554 result.fields[FieldCountry] = "England";
557 switch (callback (&result, data))
570 *filter_results = g_slist_append (*filter_results,
571 found_tower_new (&result));
586 show_message ("I don't know where you are!");
592 show_tower (char *primary_key)
594 parse_dove (single_tower_cb, NULL, primary_key);
598 show_towers_from_list (GSList *list)
608 hildon_banner_show_information(window,
616 /* only one; don't bother showing the list */
617 FoundTower* found = (FoundTower*) list->data;
619 hildon_banner_show_information(window,
622 show_tower (found->primarykey);
624 /* FIXME: and free the list */
628 dialog = hildon_picker_dialog_new (GTK_WINDOW (window));
629 selector = hildon_touch_selector_new_text ();
631 for (cursor=list; cursor; cursor=cursor->next)
633 FoundTower* found = (FoundTower*) cursor->data;
634 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
638 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (dialog),
639 HILDON_TOUCH_SELECTOR (selector));
641 gtk_widget_show_all (GTK_WIDGET (dialog));
643 if (gtk_dialog_run (GTK_DIALOG (dialog))==GTK_RESPONSE_OK)
645 GList *rows = hildon_touch_selector_get_selected_rows (HILDON_TOUCH_SELECTOR (selector),
647 GtkTreePath *path = (GtkTreePath*) rows->data;
648 gint *indices = gtk_tree_path_get_indices (path);
653 gtk_widget_destroy (GTK_WIDGET (dialog));
657 FoundTower *found = (FoundTower *) g_slist_nth_data (list, result);
658 show_tower (found->primarykey);
661 /* FIXME: and free the list */
664 static gint strcmp_f (gconstpointer a,
667 return strcmp ((char*)a, (char*)b);
671 put_areas_into_list (gpointer key,
675 GSList **list = (GSList **)data;
676 *list = g_slist_insert_sorted (*list,
682 towers_by_subarea (gchar *area)
684 GtkWidget *dialog = hildon_picker_dialog_new (GTK_WINDOW (window));
685 GtkWidget *selector = hildon_touch_selector_new_text ();
686 GHashTable *hash = g_hash_table_new_full (g_str_hash,
690 GSList *list=NULL, *cursor;
691 gchar *title = g_strdup_printf ("Areas of %s", area);
692 country_cb_data d = { hash, area };
693 country_and_county cac = { area, NULL };
695 gtk_window_set_title (GTK_WINDOW (dialog), title);
698 parse_dove (get_counties_cb, NULL, &d);
700 g_hash_table_foreach (hash,
704 for (cursor=list; cursor; cursor=cursor->next)
706 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
710 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (dialog),
711 HILDON_TOUCH_SELECTOR (selector));
713 gtk_widget_show_all (GTK_WIDGET (dialog));
715 if (gtk_dialog_run (GTK_DIALOG (dialog))==GTK_RESPONSE_OK)
717 GSList *matches = NULL;
718 cac.county = strdup (hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector)));
720 parse_dove (get_towers_by_county_cb,
725 show_towers_from_list (matches);
727 g_hash_table_unref (hash);
728 gtk_widget_destroy (GTK_WIDGET (dialog));
732 towers_by_area (void)
734 GtkWidget *dialog = hildon_picker_dialog_new (GTK_WINDOW (window));
735 GtkWidget *selector = hildon_touch_selector_new_text ();
736 GHashTable *hash = g_hash_table_new_full (g_str_hash,
740 GSList *list = NULL, *cursor;
741 gchar *result = NULL;
743 gtk_window_set_title (GTK_WINDOW (dialog), "Areas of the world");
745 parse_dove (get_countries_cb, NULL, hash);
747 g_hash_table_foreach (hash,
751 for (cursor=list; cursor; cursor=cursor->next)
753 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
757 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (dialog),
758 HILDON_TOUCH_SELECTOR (selector));
760 gtk_widget_show_all (GTK_WIDGET (dialog));
762 if (gtk_dialog_run (GTK_DIALOG (dialog))==GTK_RESPONSE_OK)
764 result = g_strdup (hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector)));
766 g_hash_table_unref (hash);
767 gtk_widget_destroy (GTK_WIDGET (dialog));
771 towers_by_subarea (result);
777 show_bookmarks (void)
784 GtkWidget *terms = gtk_dialog_new_with_buttons ("What are you looking for?",
786 GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
790 GtkWidget *entry = gtk_entry_new ();
791 GSList *matches = NULL;
793 gtk_box_pack_end (GTK_BOX (GTK_DIALOG (terms)->vbox),
794 entry, TRUE, TRUE, 0);
796 gtk_widget_show_all (GTK_WIDGET (terms));
798 if (gtk_dialog_run (GTK_DIALOG (terms))==GTK_RESPONSE_OK)
800 GSList *matches = NULL;
802 parse_dove (get_towers_by_search_cb,
804 (char*) gtk_entry_get_text (GTK_ENTRY (entry)));
806 show_towers_from_list (matches);
809 gtk_widget_destroy (GTK_WIDGET (terms));
820 gps_working = (device->fix->fields & LOCATION_GPS_DEVICE_LATLONG_SET)!=0;
824 current_lat = device->fix->latitude;
825 current_long = device->fix->longitude;
830 main(int argc, char **argv)
832 GtkWidget *bell, *button, *hbox;
833 GdkPixbuf *bell_picture;
835 gtk_init (&argc, &argv);
836 g_set_application_name ("Belltower");
838 device = g_object_new (LOCATION_TYPE_GPS_DEVICE, NULL);
840 /* FIXME and call that when the location changes, too */
842 window = hildon_stackable_window_new ();
843 gtk_window_set_title (GTK_WINDOW (window), "Belltower");
844 g_signal_connect (G_OBJECT (window), "delete_event", G_CALLBACK (gtk_main_quit), NULL);
846 bell_picture = gdk_pixbuf_new_from_file ("/usr/share/belltower/bells1.jpg", NULL);
848 buttons = gtk_vbox_new (TRUE, 0);
849 menu = HILDON_APP_MENU (hildon_app_menu_new ());
851 add_button ("Nearby", nearby_towers);
852 add_button ("Recent", recent_towers);
853 add_button ("Bookmarks", show_bookmarks);
854 add_button ("By area", towers_by_area);
855 add_button ("Search", tower_search);
857 /* extra buttons for the app menu */
858 button = gtk_button_new_with_label ("Credits");
859 hildon_app_menu_append (menu, GTK_BUTTON (button));
860 hildon_app_menu_append (menu, GTK_BUTTON (button));
862 gtk_widget_show_all (GTK_WIDGET (menu));
863 hildon_window_set_app_menu (HILDON_WINDOW (window), menu);
865 hbox = gtk_hbox_new (FALSE, 0);
866 gtk_box_pack_end (GTK_BOX (hbox), buttons, TRUE, TRUE, 0);
867 gtk_box_pack_end (GTK_BOX (hbox),
868 gtk_image_new_from_pixbuf (bell_picture),
871 gtk_container_add (GTK_CONTAINER (window), hbox);
872 gtk_widget_show_all (GTK_WIDGET (window));