X-Git-Url: http://git.maemo.org/git/?a=blobdiff_plain;f=belltower.c;h=802d5d336ae6d710dc6eada8812dc8cf6376c6f6;hb=e462081c257a7f62557413f9b4d2189dc8ec51e7;hp=78ed46ff5ad779f3da3e89c474658fa0fa17c375;hpb=681226867ad13987655af6da3edcf83a89a54a84;p=belltower diff --git a/belltower.c b/belltower.c index 78ed46f..802d5d3 100644 --- a/belltower.c +++ b/belltower.c @@ -17,11 +17,21 @@ #include #define MAX_FIELDS 50 +#define MAX_RECENT 5 #define CONFIG_GENERAL_GROUP "General" +#define CONFIG_BOOKMARK_GROUP "Bookmarks" +#define CONFIG_RECENT_GROUP "Recent" #define CONFIG_SEEN_CREDITS_KEY "seen_credits" #define CONFIG_DIRECTORY "/home/user/.config/belltower" #define CONFIG_FILENAME CONFIG_DIRECTORY "/belltower.ini" +/** + * Somewhat arbitrary minimum number of belltowers in + * one country for the country to be considered to have + * "many" belltowers. + */ +#define MANY_BELLTOWERS 10 + GtkWidget *window; LocationGPSDevice *device; GKeyFile *static_content; @@ -90,6 +100,9 @@ typedef struct { int n_fields; } tower; +static void show_towers_from_list (GSList *list, gchar *list_name); +static void free_tower_list (GSList *list); + static void show_message (char *message) { @@ -286,17 +299,54 @@ add_button (char *label, } +char *tower_displayed = NULL; +char *tower_website = NULL; +char *tower_map = NULL; +char *tower_directions = NULL; +char *peals_list = NULL; + +#define BUTTON_BOOKMARKED_YES "Remove from bookmarks" +#define BUTTON_BOOKMARKED_NO "Bookmark" + static void bookmark_toggled (GtkButton *button, gpointer dummy) { - show_message ("Bookmarks are not yet implemented."); -} + if (g_key_file_get_boolean (config, + CONFIG_BOOKMARK_GROUP, + tower_displayed, + NULL)) + { -char *tower_website = NULL; -char *tower_map = NULL; -char *tower_directions = NULL; -char *peals_list = NULL; + /* it's bookmarked; remove the bookmark */ + + if (!g_key_file_remove_key (config, + CONFIG_BOOKMARK_GROUP, + tower_displayed, + NULL)) + { + show_message ("Could not remove bookmark."); + return; + } + + save_config (); + gtk_button_set_label (button, + BUTTON_BOOKMARKED_NO); + } + else + { + /* it's not bookmarked; add a bookmark */ + + g_key_file_set_boolean (config, + CONFIG_BOOKMARK_GROUP, + tower_displayed, + TRUE); + + save_config (); + gtk_button_set_label (button, + BUTTON_BOOKMARKED_YES); + } +} static void show_tower_website (void) @@ -311,6 +361,19 @@ show_tower_map (void) } static void +show_tower_directions (void) +{ + if (tower_directions) + { + show_browser (tower_directions); + } + else + { + show_message ("I don't know where you are!"); + } +} + +static void show_peals_list (void) { show_browser (peals_list); @@ -321,17 +384,24 @@ get_countries_cb (tower *details, gpointer data) { GHashTable *hash = (GHashTable *)data; + gpointer value; if (details->serial==0) return TRUE; /* header row */ if (!g_hash_table_lookup_extended (hash, - details->fields[FieldCountry], - NULL, NULL)) + details->fields[FieldCountry], + NULL, &value)) { g_hash_table_insert (hash, g_strdup(details->fields[FieldCountry]), - g_strdup (details->fields[FieldCountry])); + GINT_TO_POINTER (0)); + } + else + { + g_hash_table_replace (hash, + g_strdup(details->fields[FieldCountry]), + GINT_TO_POINTER (GPOINTER_TO_INT (value)+1)); } return FILTER_IGNORE; @@ -424,6 +494,92 @@ get_towers_by_search_cb (tower *details, } } +/** + * A filter which accepts towers based on whether they + * appear in a particular group in the config file. + * + * \param details the candidate tower + * \param data pointer to a char* which names the group + */ +static FilterResult +get_group_of_towers_cb (tower *details, + gpointer data) +{ + if (g_key_file_has_key (config, + (char*) data, + details->fields[FieldPrimaryKey], + NULL)) + { + return FILTER_ACCEPT; + } + else + { + return FILTER_IGNORE; + } +} + +/** + * Removes the oldest entry from the [Recent] group in the config + * file until there are only five entries left. Does not save + * the file; you have to do that. + */ +static void +remove_old_recent_entries (void) +{ + gint count; + + do + { + gchar **towers; + gint oldest_date = 0; + gchar *oldest_tower = NULL; + gint i; + + /* It is a bit inefficient to do this every + * time we go around the loop. However, it + * makes the code far simpler, and we almost + * never go around more than once. + */ + towers = g_key_file_get_keys (config, + CONFIG_RECENT_GROUP, + &count, + NULL); + + if (count <= MAX_RECENT) + /* everything's fine */ + return; + + for (i=0; i MAX_RECENT); +} + static FilterResult single_tower_cb (tower *details, gpointer data) @@ -443,31 +599,9 @@ single_tower_cb (tower *details, tower_window = hildon_stackable_window_new (); - if (g_str_has_prefix (details->fields[FieldDedication], - "S ")) - { - /* FIXME: This needs to be cleverer, because we can have - * e.g. "S Peter and S Paul". - * May have to use regexps. - * Reallocation in general even when unchanged is okay, - * because it's the common case (most towers are S Something) - */ - - /* FIXME: Since we're passing this in as markup, - * we need to escape the strings. - */ - - str = g_strdup_printf("St %s, %s", - details->fields[FieldDedication]+2, - details->fields[FieldPlace]); - - } - else - { - str = g_strdup_printf("%s, %s", - details->fields[FieldDedication], - details->fields[FieldPlace]); - } + str = g_strdup_printf("%s, %s", + details->fields[FieldDedication], + details->fields[FieldPlace]); hildon_window_set_markup (HILDON_WINDOW (tower_window), str); @@ -506,12 +640,17 @@ single_tower_cb (tower *details, } add_button ("Peals", show_peals_list); add_button ("Map", show_tower_map); - add_button ("Directions", NULL); + add_button ("Directions", show_tower_directions); /* don't use a toggle button: it looks stupid */ button = hildon_button_new_with_text (HILDON_SIZE_AUTO_WIDTH | HILDON_SIZE_FINGER_HEIGHT, HILDON_BUTTON_ARRANGEMENT_VERTICAL, - "Bookmark", NULL); + g_key_file_get_boolean (config, + CONFIG_BOOKMARK_GROUP, + details->fields[FieldPrimaryKey], + NULL)? + BUTTON_BOOKMARKED_YES: BUTTON_BOOKMARKED_NO, + NULL); g_signal_connect (button, "clicked", G_CALLBACK (bookmark_toggled), NULL); gtk_box_pack_start (GTK_BOX (buttons), button, FALSE, FALSE, 0); @@ -532,6 +671,25 @@ single_tower_cb (tower *details, tower_map = g_strdup_printf ("http://maps.google.com/maps?q=%s,%s", details->fields[FieldLat], details->fields[FieldLong]); + g_free (tower_directions); + if (device->fix->fields & LOCATION_GPS_DEVICE_LATLONG_SET) + { + tower_directions = g_strdup_printf ("http://maps.google.com/maps?q=%f,%f+to+%s,%s", + device->fix->latitude, + device->fix->longitude, + details->fields[FieldLat], + details->fields[FieldLong]); + } + g_free (tower_displayed); + tower_displayed = g_strdup (details->fields[FieldPrimaryKey]); + + g_key_file_set_integer (config, + CONFIG_RECENT_GROUP, + tower_displayed, + time (NULL)); + remove_old_recent_entries (); + save_config (); + gtk_widget_show_all (GTK_WIDGET (tower_window)); return FILTER_STOP; @@ -586,16 +744,28 @@ found_tower_free (FoundTower *tower) g_free (tower); } +/** + * Calls a given function once for each tower in the world. + * (The first call, however, is a header row.) + * + * \param callback The function to call. + * \param data Arbitrary data to pass to the callback. + * \param dialogue_title If non-NULL, a list will be displayed + * with the results. This is the title + * used for that dialogue. (The dialogue + * will automatically free filter_results.) + */ static void parse_dove (ParseDoveCallback callback, - GSList **filter_results, - gpointer data) + gpointer data, + gchar *dialogue_title) { FILE *dove = fopen("/usr/share/belltower/dove.txt", "r"); char tower_rec[4096]; tower result; char *i; gboolean seen_newline; + GSList *filter_results = NULL; if (!dove) { @@ -646,33 +816,63 @@ parse_dove (ParseDoveCallback callback, return; case FILTER_ACCEPT: - if (filter_results) - { - *filter_results = g_slist_append (*filter_results, - found_tower_new (&result)); - } + filter_results = g_slist_append (filter_results, + found_tower_new (&result)); } result.serial++; } fclose (dove); + + if (dialogue_title) + { + show_towers_from_list (filter_results, + dialogue_title); + } + else + { + free_tower_list (filter_results); + } } static void show_tower (char *primary_key) { - parse_dove (single_tower_cb, NULL, primary_key); + parse_dove (single_tower_cb, primary_key, NULL); } static void -show_towers_from_list (GSList *list) +free_tower_list (GSList *list) +{ + GSList *cursor = list; + + while (cursor) + { + found_tower_free ((FoundTower*) cursor->data); + cursor = cursor->next; + } + + g_slist_free (list); +} + +/** + * Displays a list of towers for the user to choose from. + * When one is chosen, we go to the display page for that tower. + * If there are none, this will tell the user there were none. + * If there is only one, we go straight to its display page. + * + * \param list a GSList of FoundTower objects. + * \param list_name the title for the dialogue. + */ +static void +show_towers_from_list (GSList *list, + gchar *list_name) { GtkWidget *dialog; GtkWidget *selector; gint result = -1; GSList *cursor; - gchar foo[2048]; if (!list) { @@ -692,12 +892,13 @@ show_towers_from_list (GSList *list) "One tower found."); show_tower (found->primarykey); - /* FIXME: and free the list */ + free_tower_list (list); return; } dialog = hildon_picker_dialog_new (GTK_WINDOW (window)); selector = hildon_touch_selector_new_text (); + gtk_window_set_title (GTK_WINDOW (dialog), list_name); for (cursor=list; cursor; cursor=cursor->next) { @@ -729,7 +930,7 @@ show_towers_from_list (GSList *list) show_tower (found->primarykey); } - /* FIXME: and free the list */ + free_tower_list (list); } static gint strcmp_f (gconstpointer a, @@ -752,8 +953,6 @@ put_areas_into_list (gpointer key, static void nearby_towers (void) { - GSList *matches = NULL; - if (!(device->fix->fields & LOCATION_GPS_DEVICE_LATLONG_SET)) { show_message ("I don't know where you are!"); @@ -761,10 +960,8 @@ nearby_towers (void) } parse_dove (get_nearby_towers_cb, - &matches, - NULL); - - show_towers_from_list (matches); + NULL, + "Towers within fifty miles of you"); } static void @@ -784,7 +981,7 @@ towers_by_subarea (gchar *area) gtk_window_set_title (GTK_WINDOW (dialog), title); g_free (title); - parse_dove (get_counties_cb, NULL, &d); + parse_dove (get_counties_cb, &d, NULL); g_hash_table_foreach (hash, put_areas_into_list, @@ -803,37 +1000,96 @@ towers_by_subarea (gchar *area) if (gtk_dialog_run (GTK_DIALOG (dialog))==GTK_RESPONSE_OK) { - GSList *matches = NULL; - cac.county = strdup (hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector))); + gchar *title; + cac.county = g_strdup (hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector))); + title = g_strdup_printf ("Towers in %s", + cac.county); parse_dove (get_towers_by_county_cb, - &matches, - &cac); + &cac, + title); g_free (cac.county); - - show_towers_from_list (matches); + g_free (title); } g_hash_table_unref (hash); gtk_widget_destroy (GTK_WIDGET (dialog)); } +/** + * Maps a hash table from country names to counts of belltowers to a + * newly-created hash table mapping country names to display + * names, containing only those countries which have many + * (or few) belltowers. + * + * \param source the source table + * \param want_many true if you want countries with many belltowers; + * false if you want countries with few. + */ +static GHashTable* +get_countries_with_many (GHashTable *source, + gboolean want_many) +{ + GHashTable *result = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + NULL); + GList *countries = g_hash_table_get_keys (source); + GList *cursor = countries; + + while (cursor) + { + gboolean has_many = + GPOINTER_TO_INT (g_hash_table_lookup (source, + cursor->data)) >= MANY_BELLTOWERS; + + if (has_many == want_many) + { + g_hash_table_insert (result, + g_strdup (cursor->data), + g_strdup (cursor->data)); + } + + cursor = cursor->next; + } + + g_list_free (countries); + return result; +} + +#define COUNTRIES_WITH_MANY "Countries with many belltowers" +#define COUNTRIES_WITH_FEW "Countries with few belltowers" + +/** + * Displays a list of areas of the world with many (or few) + * belltowers. If you ask for the areas with many, it include + * a link to the areas with few. + * + * \param countries_with_many True to list countries with many; + * false to list countries with few. + */ static void -towers_by_area (void) +towers_by_area_with_many (gboolean countries_with_many) { GtkWidget *dialog = hildon_picker_dialog_new (GTK_WINDOW (window)); GtkWidget *selector = hildon_touch_selector_new_text (); - GHashTable *hash = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - g_free); + GHashTable *countries_to_counts = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + NULL); + GHashTable *country_names; GSList *list = NULL, *cursor; gchar *result = NULL; - gtk_window_set_title (GTK_WINDOW (dialog), "Areas of the world"); + gtk_window_set_title (GTK_WINDOW (dialog), + countries_with_many? + COUNTRIES_WITH_MANY : COUNTRIES_WITH_FEW); - parse_dove (get_countries_cb, NULL, hash); + parse_dove (get_countries_cb, countries_to_counts, NULL); - g_hash_table_foreach (hash, + country_names = get_countries_with_many (countries_to_counts, + countries_with_many); + + g_hash_table_foreach (country_names, put_areas_into_list, &list); @@ -843,6 +1099,12 @@ towers_by_area (void) cursor->data); } + if (countries_with_many) + { + hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector), + COUNTRIES_WITH_FEW); + } + hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (dialog), HILDON_TOUCH_SELECTOR (selector)); @@ -852,23 +1114,55 @@ towers_by_area (void) { result = g_strdup (hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector))); } - g_hash_table_unref (hash); + + g_hash_table_unref (countries_to_counts); + g_hash_table_unref (country_names); gtk_widget_destroy (GTK_WIDGET (dialog)); if (result) { - towers_by_subarea (result); + if (countries_with_many) + { + /* these countries have many towers, so + * show the sub-areas + */ + if (strcmp (result, COUNTRIES_WITH_FEW)==0) + towers_by_area_with_many (FALSE); + else + towers_by_subarea (result); + } + else + { + country_and_county cac = { result, NULL }; + gchar *title = g_strdup_printf ("Belltowers in %s", + result); + + parse_dove (get_towers_by_county_cb, + &cac, + title); + + g_free (title); + } + g_free (result); } } +/** + * Shows all the towers in areas with many towers. + */ static void -show_bookmarks (void) +towers_by_area (void) { - /* well, there are currently no bookmarks, - because you can't set them. */ + towers_by_area_with_many (TRUE); +} - show_towers_from_list (NULL); +static void +show_bookmarks (void) +{ + parse_dove (get_group_of_towers_cb, + CONFIG_BOOKMARK_GROUP, + "Bookmarks"); } static void @@ -881,7 +1175,6 @@ tower_search (void) GTK_RESPONSE_OK, NULL); GtkWidget *entry = gtk_entry_new (); - GSList *matches = NULL; gtk_box_pack_end (GTK_BOX (GTK_DIALOG (terms)->vbox), entry, TRUE, TRUE, 0); @@ -890,13 +1183,9 @@ tower_search (void) if (gtk_dialog_run (GTK_DIALOG (terms))==GTK_RESPONSE_OK) { - GSList *matches = NULL; - parse_dove (get_towers_by_search_cb, - &matches, - (char*) gtk_entry_get_text (GTK_ENTRY (entry))); - - show_towers_from_list (matches); + (char*) gtk_entry_get_text (GTK_ENTRY (entry)), + "Search results"); } gtk_widget_destroy (GTK_WIDGET (terms)); @@ -905,7 +1194,9 @@ tower_search (void) static void recent_towers (void) { - show_message ("This is not yet implemented."); + parse_dove (get_group_of_towers_cb, + CONFIG_RECENT_GROUP, + "Towers you have recently viewed"); } /** @@ -954,18 +1245,28 @@ show_credits (GtkButton *source, button = hildon_button_new_with_text (HILDON_SIZE_AUTO_WIDTH | HILDON_SIZE_FINGER_HEIGHT, HILDON_BUTTON_ARRANGEMENT_VERTICAL, - "View the GNU General Public Licence", - "This program is provided under the GPL, with no warranty."); + "Welcome to Belltower. The program is \xc2\xa9 2009 Thomas Thurman.", + "View the program's home page."); + g_signal_connect (button, "clicked", G_CALLBACK (show_web_page), + "http://belltower.garage.maemo.org"); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), + button, + TRUE, TRUE, 0); + + button = hildon_button_new_with_text (HILDON_SIZE_AUTO_WIDTH | HILDON_SIZE_FINGER_HEIGHT, + HILDON_BUTTON_ARRANGEMENT_VERTICAL, + "This program is provided under the GNU GPL, with no warranty.", + "View the GNU General Public Licence."); g_signal_connect (button, "clicked", G_CALLBACK (show_web_page), - "www.gnu.org/copyleft/gpl.html"); + "http://www.gnu.org/copyleft/gpl.html"); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), button, TRUE, TRUE, 0); button = hildon_button_new_with_text (HILDON_SIZE_AUTO_WIDTH | HILDON_SIZE_FINGER_HEIGHT, HILDON_BUTTON_ARRANGEMENT_VERTICAL, - "View Dove's Guide for Church Bell Ringers", - "The source of this program's data."); + "The data comes from Dove's Guide for Church Bell Ringers.", + "View Dove's Guide."); g_signal_connect (button, "clicked", G_CALLBACK (show_web_page), "http://dove.cccbr.org.uk"); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), @@ -974,8 +1275,8 @@ show_credits (GtkButton *source, button = hildon_button_new_with_text (HILDON_SIZE_AUTO_WIDTH | HILDON_SIZE_FINGER_HEIGHT, HILDON_BUTTON_ARRANGEMENT_VERTICAL, - "View belfry photograph", - "Image \xc2\xa9 Amanda Slater, cc-by-sa."); + "The belfry image is \xc2\xa9 Amanda Slater, cc-by-sa.", + "View the original photograph."); g_signal_connect (button, "clicked", G_CALLBACK (show_web_page), "http://www.flickr.com/photos/pikerslanefarm/3398769335/"); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),