#include <hildon/hildon.h>
#include <gtk/gtk.h>
#include <location/location-gps-device.h>
+#include <location/location-distance-utils.h>
#include <dbus/dbus-glib.h>
#define MAX_FIELDS 50
+#define MAX_RECENT 5
+#define CONFIG_GENERAL_GROUP "General"
+#define CONFIG_DISTANCES_KEY "Distances"
+#define CONFIG_TOWERSORT_KEY "Towersort"
+#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
+
+char *distances_settings[] = {
+ CONFIG_DISTANCES_KEY,
+ "Distances are measured in...",
+ "Miles",
+ "Kilometres",
+ NULL
+};
+
+char *towersort_settings[] = {
+ CONFIG_TOWERSORT_KEY,
+ "Towers are sorted by...",
+ "Name of town",
+ "Distance from you",
+ "Days until practice night",
+ "Weight of tenor",
+ NULL
+};
+
+char **settings[] = {
+ distances_settings,
+ towersort_settings
+};
+
+gint settings_value[G_N_ELEMENTS (settings)] = { 0, };
+
+typedef enum {
+ SETTINGS_DISTANCES,
+ SETTINGS_TOWERSORT
+} Settings;
+
+typedef enum {
+ DISTANCES_MILES,
+ DISTANCES_KILOMETRES
+} DistancesSetting;
+
+typedef enum {
+ TOWERSORT_TOWN,
+ TOWERSORT_DISTANCE,
+ TOWERSORT_PRACTICE,
+ TOWERSORT_WEIGHT
+} TowersortSetting;
GtkWidget *window;
+LocationGPSDevice *device;
+GKeyFile *static_content;
+GKeyFile *config;
+
+typedef enum {
+ /** stop scanning the database */
+ FILTER_STOP,
+ /** ignore this one */
+ FILTER_IGNORE,
+ /** add this one to the list */
+ FILTER_ACCEPT
+} FilterResult;
/*
FIXME:
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)
{
gtk_widget_destroy (GTK_WIDGET (note));
}
+/**
+ * Loads the content of the static and dynamic data files.
+ * Possibly puts up a warning if we can't load the static file.
+ */
+static void
+load_config (void)
+{
+ gint i;
+
+ static_content = g_key_file_new ();
+
+ if (!g_key_file_load_from_file (static_content,
+ "/usr/share/belltower/static.ini",
+ G_KEY_FILE_NONE,
+ NULL))
+ {
+ show_message ("Could not load static content. Attempting to continue.");
+ }
+
+ config = g_key_file_new ();
+ /* it doesn't matter if this fails */
+ g_key_file_load_from_file (config,
+ CONFIG_FILENAME,
+ G_KEY_FILE_KEEP_COMMENTS,
+ NULL);
+
+ for (i=0; i<G_N_ELEMENTS (settings); i++)
+ {
+ gchar *value = g_key_file_get_string (config,
+ CONFIG_GENERAL_GROUP,
+ settings[i][0],
+ NULL);
+
+ settings_value[i] = 0;
+
+ if (value)
+ {
+ gint j=0;
+ char **cursor = settings[i]+2;
+
+ while (*cursor)
+ {
+ if (strcmp (value, *cursor)==0)
+ {
+ settings_value[i] = j;
+ break;
+ }
+ cursor++;
+ j++;
+ }
+ }
+ }
+}
+
+/**
+ * Saves the dynamic data file to disk.
+ * Puts up a message if there was any error.
+ */
+static void
+save_config (void)
+{
+ gchar *data;
+
+ g_mkdir_with_parents (CONFIG_DIRECTORY, 0700);
+
+ data = g_key_file_to_data (config, NULL, NULL);
+
+ if (!g_file_set_contents (CONFIG_FILENAME,
+ data,
+ -1,
+ NULL))
+ {
+ show_message ("Could not write config file.");
+ }
+
+ g_free (data);
+}
+
+static gint
+distance_to_tower (tower *details)
+{
+ char *endptr;
+ double tower_lat;
+ double tower_long;
+ double km_distance;
+ const double km_to_miles = 1.609344;
+
+ tower_lat = strtod(details->fields[FieldLat], &endptr);
+ if (*endptr) return -1;
+ tower_long = strtod(details->fields[FieldLong], &endptr);
+ if (*endptr) return -1;
+
+ km_distance = location_distance_between (device->fix->latitude,
+ device->fix->longitude,
+ tower_lat,
+ tower_long);
+ if (settings_value[SETTINGS_DISTANCES]==DISTANCES_KILOMETRES)
+ return (int) km_distance;
+ else
+ return (int) (km_distance / km_to_miles);
+}
+
+static gchar*
+distance_to_tower_str (tower *details)
+{
+ int distance = distance_to_tower (details);
+
+ if (distance==-1)
+ {
+ return g_strdup ("unknown");
+ }
+ else if (settings_value[SETTINGS_DISTANCES]==DISTANCES_KILOMETRES)
+ {
+ return g_strdup_printf("%dkm", (int) distance);
+ }
+ else
+ {
+ return g_strdup_printf("%dmi", (int) distance);
+ }
+}
+
static void
call_dbus (DBusBusType type,
char *name,
url);
}
-typedef gboolean (*ParseDoveCallback)(tower *details, gpointer data);
+typedef FilterResult (*ParseDoveCallback)(tower *details, gpointer data);
typedef void (*ButtonCallback)(void);
GtkWidget *tower_window, *buttons, *tower_table;
}
+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)
}
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);
}
-static gboolean
+static FilterResult
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 TRUE;
+ return FILTER_IGNORE;
}
typedef struct {
gchar *country_name;
} country_cb_data;
-static gboolean
+typedef struct {
+ char *country;
+ char *county;
+} country_and_county;
+
+static FilterResult
get_counties_cb (tower *details,
gpointer data)
{
country_cb_data *d = (country_cb_data *)data;
if (details->serial==0)
- return TRUE; /* header row */
+ return FILTER_IGNORE; /* header row */
if (strcmp(details->fields[FieldCountry], d->country_name)!=0)
- return TRUE; /* wrong country */
+ return FILTER_IGNORE; /* wrong country */
if (!g_hash_table_lookup_extended (d->hash,
details->fields[FieldCounty],
g_strdup (details->fields[FieldCounty]));
}
- return TRUE;
+ return FILTER_IGNORE;
+}
+
+static FilterResult
+get_nearby_towers_cb (tower *details,
+ gpointer distance)
+{
+ if (details->serial==0)
+ return FILTER_IGNORE; /* header row */
+
+ if (distance_to_tower (details) <= GPOINTER_TO_INT (distance))
+ {
+ return FILTER_ACCEPT;
+ }
+ else
+ {
+ return FILTER_IGNORE;
+ }
+}
+
+static FilterResult
+get_towers_by_county_cb (tower *details,
+ gpointer data)
+{
+ country_and_county *cac = (country_and_county *) data;
+
+ if ((!cac->county || strcmp (cac->county, details->fields[FieldCounty])==0) &&
+ (!cac->country || strcmp (cac->country, details->fields[FieldCountry])==0))
+ {
+ return FILTER_ACCEPT;
+ }
+ else
+ {
+ return FILTER_IGNORE;
+ }
+}
+
+static FilterResult
+get_towers_by_search_cb (tower *details,
+ gpointer data)
+{
+ char *s = (char *) data;
+
+ if (strcasestr(details->fields[FieldCountry], s) ||
+ strcasestr(details->fields[FieldCounty], s) ||
+ strcasestr(details->fields[FieldDedication], s) ||
+ strcasestr(details->fields[FieldPlace], s))
+ {
+ return FILTER_ACCEPT;
+ }
+ else
+ {
+ return FILTER_IGNORE;
+ }
+}
+
+/**
+ * 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;
+ }
}
-static gboolean
+/**
+ * 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<count; i++)
+ {
+ gint date = g_key_file_get_integer (config,
+ CONFIG_RECENT_GROUP,
+ towers[i],
+ NULL);
+
+ if (date==0)
+ continue;
+
+ if (oldest_date==0 ||
+ date < oldest_date)
+ {
+ oldest_tower = towers[i];
+ oldest_date = date;
+ }
+ }
+
+ if (oldest_tower)
+ {
+ g_key_file_remove_key (config,
+ CONFIG_RECENT_GROUP,
+ oldest_tower,
+ NULL);
+ count --;
+ }
+ g_strfreev (towers);
+ }
+ while (count > MAX_RECENT);
+}
+
+static FilterResult
single_tower_cb (tower *details,
gpointer data)
{
gchar *str;
gint tenor_weight;
gchar *primary_key = (gchar*) data;
+ gchar *distance;
if (strcmp(details->fields[FieldPrimaryKey], primary_key)!=0)
{
/* not this one; keep going */
- return TRUE;
+ return FILTER_IGNORE;
}
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("S<sup>t</sup> %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);
buttons = gtk_vbox_new (TRUE, 0);
menu = HILDON_APP_MENU (hildon_app_menu_new ());
+ distance = distance_to_tower_str(details);
+
+ add_table_field ("Distance", distance);
add_table_field ("Postcode", details->fields[FieldPostcode]);
add_table_field ("County", details->fields[FieldCounty]);
add_table_field ("Country", details->fields[FieldCountry]);
add_table_field ("Practice night", details->fields[FieldPracticeNight]);
add_table_field ("Bells", details->fields[FieldBells]);
+ g_free (distance);
+
tenor_weight = atoi (details->fields[FieldWt]);
str = g_strdup_printf("%dcwt %dqr %dlb in %s",
tenor_weight/112,
add_table_field ("Tenor", str);
g_free (str);
- add_button ("Tower website", show_tower_website);
+ if (strcmp(details->fields[FieldWebPage], "")!=0)
+ {
+ add_button ("Tower website", show_tower_website);
+ }
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);
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 FALSE;
+ return FILTER_STOP;
}
+/**
+ * A tower that was accepted by a filter.
+ */
+typedef struct {
+ char *sortkey;
+ char *primarykey;
+ char *displayname;
+} FoundTower;
+
+static FoundTower *
+found_tower_new (tower *basis)
+{
+ FoundTower* result = g_new (FoundTower, 1);
+
+ switch (settings_value[SETTINGS_TOWERSORT])
+ {
+ case TOWERSORT_DISTANCE:
+ result->sortkey = g_strdup_printf ("%5d %s",
+ distance_to_tower (basis),
+ basis->fields[FieldPlace]);
+ break;
+ case TOWERSORT_PRACTICE:
+ result->sortkey = g_strdup ("FIXME");
+ break;
+ case TOWERSORT_WEIGHT:
+ result->sortkey = g_strdup_printf ("%10s", basis->fields[FieldWt]);
+ break;
+ case TOWERSORT_TOWN:
+ default:
+ result->sortkey = g_strdup (basis->fields[FieldPlace]);
+ }
+
+ result->primarykey = g_strdup (basis->fields[FieldPrimaryKey]);
+
+ if (device->fix->fields & LOCATION_GPS_DEVICE_LATLONG_SET)
+ {
+ gchar *distance = distance_to_tower_str (basis);
+ result->displayname = g_strdup_printf ("%s, %s (%s, %s) (%s)",
+ basis->fields[FieldDedication],
+ basis->fields[FieldPlace],
+ basis->fields[FieldBells],
+ basis->fields[FieldPracticeNight],
+ distance);
+ g_free (distance);
+ }
+ else
+ {
+ result->displayname = g_strdup_printf ("%s, %s (%s, %s)",
+ basis->fields[FieldDedication],
+ basis->fields[FieldPlace],
+ basis->fields[FieldBells],
+ basis->fields[FieldPracticeNight]);
+ }
+
+ return result;
+}
+
+static void
+found_tower_free (FoundTower *tower)
+{
+ g_free (tower->sortkey);
+ g_free (tower->primarykey);
+ g_free (tower->displayname);
+ 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,
- 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)
{
result.fields[FieldCountry] = "England";
}
- if (!callback (&result, data))
+ switch (callback (&result, data))
{
+ case FILTER_IGNORE:
+ /* nothing */
+ break;
+
+ case FILTER_STOP:
fclose (dove);
return;
+
+ case FILTER_ACCEPT:
+ 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
-nearby_towers (void)
+show_tower (char *primary_key)
{
- char buffer[4096];
- LocationGPSDevice *device;
- device = g_object_new (LOCATION_TYPE_GPS_DEVICE, NULL);
+ parse_dove (single_tower_cb, primary_key, NULL);
+}
- sprintf(buffer, "%f %f %x",
- device->fix->latitude,
- device->fix->longitude,
- device->fix->fields);
- show_message (buffer);
+static void
+free_tower_list (GSList *list)
+{
+ GSList *cursor = list;
- if (device->fix->fields & LOCATION_GPS_DEVICE_LATLONG_SET)
- {
- show_message ("I know where you are!");
- }
- else
+ while (cursor)
{
- show_message ("I don't know where you are!");
+ found_tower_free ((FoundTower*) cursor->data);
+ cursor = cursor->next;
}
- g_object_unref (device);
+ 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_tower (char *primary_key)
+show_towers_from_list (GSList *list,
+ gchar *list_name)
{
- parse_dove (single_tower_cb,
- primary_key);
+ GtkWidget *dialog;
+ GtkWidget *selector;
+ gint result = -1;
+ GSList *cursor;
+
+ if (!list)
+ {
+ hildon_banner_show_information(window,
+ NULL,
+ "No towers found.");
+ return;
+ }
+
+ if (!list->next)
+ {
+ /* only one; don't bother showing the list */
+ FoundTower* found = (FoundTower*) list->data;
+
+ hildon_banner_show_information(window,
+ NULL,
+ "One tower found.");
+ show_tower (found->primarykey);
+
+ 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)
+ {
+ FoundTower* found = (FoundTower*) cursor->data;
+ hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
+ found->displayname);
+ }
+
+ hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (dialog),
+ HILDON_TOUCH_SELECTOR (selector));
+
+ gtk_widget_show_all (GTK_WIDGET (dialog));
+
+ if (gtk_dialog_run (GTK_DIALOG (dialog))==GTK_RESPONSE_OK)
+ {
+ GList *rows = hildon_touch_selector_get_selected_rows (HILDON_TOUCH_SELECTOR (selector),
+ 0);
+ GtkTreePath *path = (GtkTreePath*) rows->data;
+ gint *indices = gtk_tree_path_get_indices (path);
+
+ result = *indices;
+ }
+
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+
+ if (result!=-1)
+ {
+ FoundTower *found = (FoundTower *) g_slist_nth_data (list, result);
+ show_tower (found->primarykey);
+ }
+
+ free_tower_list (list);
}
static gint strcmp_f (gconstpointer a,
}
static void
+nearby_towers (void)
+{
+ if (!(device->fix->fields & LOCATION_GPS_DEVICE_LATLONG_SET))
+ {
+ show_message ("I don't know where you are!");
+ return;
+ }
+
+ parse_dove (get_nearby_towers_cb,
+ settings_value[SETTINGS_DISTANCES]==DISTANCES_KILOMETRES?
+ GINT_TO_POINTER (80) :
+ GINT_TO_POINTER (50),
+ settings_value[SETTINGS_DISTANCES]==DISTANCES_KILOMETRES?
+ "Towers within eighty kilometres of you":
+ "Towers within fifty miles of you");
+}
+
+static void
towers_by_subarea (gchar *area)
{
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);
-
GSList *list=NULL, *cursor;
-
gchar *title = g_strdup_printf ("Areas of %s", area);
-
country_cb_data d = { hash, area };
+ country_and_county cac = { area, NULL };
gtk_window_set_title (GTK_WINDOW (dialog), title);
g_free (title);
- parse_dove (get_counties_cb,
- &d);
+ parse_dove (get_counties_cb, &d, NULL);
g_hash_table_foreach (hash,
put_areas_into_list,
if (gtk_dialog_run (GTK_DIALOG (dialog))==GTK_RESPONSE_OK)
{
- show_message (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,
+ &cac,
+ title);
+ g_free (cac.county);
+ 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,
- 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);
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));
{
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
+towers_by_area (void)
+{
+ towers_by_area_with_many (TRUE);
+}
+
static void
show_bookmarks (void)
{
- /* nothing */
+ parse_dove (get_group_of_towers_cb,
+ CONFIG_BOOKMARK_GROUP,
+ "Bookmarks");
}
static void
tower_search (void)
{
- /* nothing */
+ GtkWidget *terms = gtk_dialog_new_with_buttons ("What are you looking for?",
+ GTK_WINDOW (window),
+ GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
+ "Search",
+ GTK_RESPONSE_OK,
+ NULL);
+ GtkWidget *entry = gtk_entry_new ();
+
+ gtk_box_pack_end (GTK_BOX (GTK_DIALOG (terms)->vbox),
+ entry, TRUE, TRUE, 0);
+
+ gtk_widget_show_all (GTK_WIDGET (terms));
+
+ if (gtk_dialog_run (GTK_DIALOG (terms))==GTK_RESPONSE_OK)
+ {
+ parse_dove (get_towers_by_search_cb,
+ (char*) gtk_entry_get_text (GTK_ENTRY (entry)),
+ "Search results");
+ }
+
+ gtk_widget_destroy (GTK_WIDGET (terms));
}
static void
recent_towers (void)
{
- show_tower ("NORTON HE");
+ parse_dove (get_group_of_towers_cb,
+ CONFIG_RECENT_GROUP,
+ "Towers you have recently viewed");
+}
+
+/**
+ * Displays a web page.
+ * (Perhaps this should be merged with show_browser().)
+ *
+ * \param url The URL.
+ */
+static void
+show_web_page (GtkButton *dummy,
+ gpointer url)
+{
+ show_browser (url);
+}
+
+/**
+ * Shows the credits.
+ *
+ * \param source If non-null, we were called from a button press,
+ * so always show the credits. If null, we were called
+ * automatically on startup, so show the credits if
+ * they haven't already been seen.
+ */
+static void
+show_credits (GtkButton *source,
+ gpointer dummy)
+{
+ gboolean from_button = (source!=NULL);
+ GtkWidget *dialog, *label, *button;
+
+ if (!from_button &&
+ g_key_file_get_boolean (config,
+ CONFIG_GENERAL_GROUP,
+ CONFIG_SEEN_CREDITS_KEY,
+ NULL))
+ {
+ return;
+ }
+
+
+ dialog = gtk_dialog_new_with_buttons ("Credits",
+ GTK_WINDOW (window),
+ GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
+ NULL
+ );
+
+ button = hildon_button_new_with_text (HILDON_SIZE_AUTO_WIDTH | HILDON_SIZE_FINGER_HEIGHT,
+ HILDON_BUTTON_ARRANGEMENT_VERTICAL,
+ "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),
+ "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,
+ "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),
+ button,
+ TRUE, TRUE, 0);
+
+ button = hildon_button_new_with_text (HILDON_SIZE_AUTO_WIDTH | HILDON_SIZE_FINGER_HEIGHT,
+ HILDON_BUTTON_ARRANGEMENT_VERTICAL,
+ "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),
+ button,
+ TRUE, TRUE, 0);
+
+ gtk_widget_show_all (GTK_WIDGET (dialog));
+
+ g_key_file_set_boolean (config,
+ CONFIG_GENERAL_GROUP,
+ CONFIG_SEEN_CREDITS_KEY,
+ TRUE);
+ save_config ();
+}
+
+static void
+settings_dialogue (GtkButton *source,
+ gpointer dummy)
+{
+ GtkWidget *dialog, *button;
+ GtkWidget *selector[G_N_ELEMENTS (settings)];
+ gint i;
+
+ dialog = gtk_dialog_new_with_buttons ("Settings",
+ GTK_WINDOW (window),
+ GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
+ NULL
+ );
+
+ for (i=0; i<G_N_ELEMENTS (settings); i++)
+ {
+ char **cursor = settings[i]+2;
+ selector[i] = hildon_touch_selector_new_text ();
+
+ while (*cursor)
+ {
+ hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector[i]), *cursor);
+ cursor++;
+ }
+
+ hildon_touch_selector_set_active (HILDON_TOUCH_SELECTOR (selector[i]), 0, settings_value[i]);
+
+ button = hildon_picker_button_new (HILDON_SIZE_AUTO, HILDON_BUTTON_ARRANGEMENT_VERTICAL);
+ hildon_button_set_title (HILDON_BUTTON (button), settings[i][1]);
+ hildon_picker_button_set_selector (HILDON_PICKER_BUTTON (button),
+ HILDON_TOUCH_SELECTOR (selector[i]));
+ gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
+ button,
+ TRUE, TRUE, 0);
+ }
+
+ gtk_widget_show_all (GTK_WIDGET (dialog));
+ gtk_dialog_run (GTK_DIALOG (dialog));
+
+ for (i=0; i<G_N_ELEMENTS (settings); i++)
+ {
+ GList *rows = hildon_touch_selector_get_selected_rows (HILDON_TOUCH_SELECTOR (selector[i]),
+ 0);
+ GtkTreePath *path = (GtkTreePath*) rows->data;
+ gint *indices = gtk_tree_path_get_indices (path);
+
+ g_key_file_set_string (config,
+ CONFIG_GENERAL_GROUP,
+ settings[i][0],
+ hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector[i])));
+
+ settings_value[i] = *indices;
+ }
+ save_config ();
+
+ gtk_widget_destroy (GTK_WIDGET (dialog));
}
int
gtk_init (&argc, &argv);
g_set_application_name ("Belltower");
+ device = g_object_new (LOCATION_TYPE_GPS_DEVICE, NULL);
+
window = hildon_stackable_window_new ();
gtk_window_set_title (GTK_WINDOW (window), "Belltower");
g_signal_connect (G_OBJECT (window), "delete_event", G_CALLBACK (gtk_main_quit), NULL);
add_button ("Bookmarks", show_bookmarks);
add_button ("By area", towers_by_area);
add_button ("Search", tower_search);
+ add_button ("(temp) settings", settings_dialogue);
/* extra buttons for the app menu */
button = gtk_button_new_with_label ("Credits");
- hildon_app_menu_append (menu, GTK_BUTTON (button));
+ g_signal_connect (button, "clicked", G_CALLBACK (show_credits), NULL);
hildon_app_menu_append (menu, GTK_BUTTON (button));
gtk_widget_show_all (GTK_WIDGET (menu));
gtk_container_add (GTK_CONTAINER (window), hbox);
gtk_widget_show_all (GTK_WIDGET (window));
+ load_config ();
+ show_credits (NULL, NULL);
+
gtk_main ();
return EXIT_SUCCESS;