Merge pull request #70 from gergelypolonkai/city-chooser

Implement country/city chooser
This commit is contained in:
Gergely Polonkai 2014-09-01 00:07:06 +02:00
commit de36a9fbe6
7 changed files with 485 additions and 27 deletions

View File

@ -2,6 +2,6 @@ BEGIN {
FS="\t"
}
{
if ($7 != "P" || $8 != "PPL" || $15 < 1000) next
if ($7 != "P" || ($8 != "PPL" && $8 != "PPLC") || $15 < 1000) next
print $9 FS $2 FS $5 FS $6 FS $16 FS $18
}

View File

@ -16,25 +16,34 @@ while (<TIMEZONES>) {
}
close(TIMEZONES);
my $xml_file = IO::File->new('>geodata.xml');
my $writer = XML::Writer->new(OUTPUT => $xml_file, NEWLINES => 0);
$writer->xmlDecl('utf-8');
$writer->startTag('geodata');
$writer->startTag('countries');
open(COUNTRIES, 'countryInfo.txt') or die("Cannot open countryInfo.txt: $!\n");
while (<COUNTRIES>) {
my ($country_code, $iso3, $iso_numeric, $fips, $name, $capital, $area, $population, $continent, $tld, $currency_code, $currency_name, $phone, $postal_code_format, $postal_code_regex, $languages, $geonameid, $neighbours, $equivalent_fips_code) = split(/\t/, $_);
next if ($country_code !~ /^[A-Z]{2}$/);
$writer->emptyTag('c',
'n' => $name,
'c' => $country_code,
);
if ($country_code =~ /^[A-Z]{2}$/) {
$countries{$country_code} = $name;
}
}
close(COUNTRIES);
$writer->endTag('countries');
$writer->startTag('places');
open(GEONAMES, "cities.txt") or die("Cannot open cities.txt: $!\n");
my $xml_file = IO::File->new('>geodata.xml');
my $writer = XML::Writer->new(OUTPUT => $xml_file, NEWLINES => 0);
$writer->xmlDecl('utf-8');
$writer->startTag('geodata');
while (<GEONAMES>) {
chomp($_);
my ($country_code, $name, $latitude, $longitude, $elevation, $timezone) = split(/\t/, $_);
@ -62,6 +71,7 @@ while (<GEONAMES>) {
print $., "\n" if ($. % 19083 == 0);
}
$writer->endTag('places');
$writer->endTag('geodata');
$writer->end();
$xml_file->close();

View File

@ -857,7 +857,6 @@ ag_db_get_chart_data_by_id(AgDb *db, guint row_id, GError **err)
{
AgDbSave *save_data;
const GValue *value;
GdaValueAttribute attributes;
gchar *query,
*columns;
guint i;
@ -918,18 +917,16 @@ ag_db_get_chart_data_by_id(AgDb *db, guint row_id, GError **err)
/* country */
value = gda_data_model_get_value_at(result, COLUMN_COUNTRY, 0, NULL);
attributes = gda_data_model_get_attributes_at(result, COLUMN_COUNTRY, 0);
if (attributes | GDA_VALUE_ATTR_IS_NULL) {
if (GDA_VALUE_HOLDS_NULL(value)) {
save_data->country = NULL;
} else {
save_data->country = g_strdup(g_value_get_string(value));
}
value = gda_data_model_get_value_at(result, COLUMN_CITY, 0, NULL);
attributes = gda_data_model_get_attributes_at(result, COLUMN_CITY, 0);
if (attributes | GDA_VALUE_ATTR_IS_NULL) {
if (GDA_VALUE_HOLDS_NULL(value)) {
save_data->city = NULL;
} else {
save_data->city = g_strdup(g_value_get_string(value));
@ -952,9 +949,8 @@ ag_db_get_chart_data_by_id(AgDb *db, guint row_id, GError **err)
save_data->latitude = g_value_get_double(value);
value = gda_data_model_get_value_at(result, COLUMN_ALTITUDE, 0, NULL);
attributes = gda_data_model_get_attributes_at(result, COLUMN_ALTITUDE, 0);
if (attributes | GDA_VALUE_ATTR_IS_NULL) {
if (GDA_VALUE_HOLDS_NULL(value)) {
save_data->altitude = DEFAULT_ALTITUDE;
} else {
save_data->altitude = g_value_get_double(value);
@ -1010,9 +1006,8 @@ ag_db_get_chart_data_by_id(AgDb *db, guint row_id, GError **err)
save_data->house_system = g_strdup(g_value_get_string(value));
value = gda_data_model_get_value_at(result, COLUMN_NOTE, 0, NULL);
attributes = gda_data_model_get_attributes_at(result, 15, 0);
if (attributes | GDA_VALUE_ATTR_IS_NULL) {
if (GDA_VALUE_HOLDS_NULL(value)) {
save_data->note = NULL;
} else {
save_data->note = g_strdup(g_value_get_string(value));

View File

@ -24,6 +24,8 @@ struct _AgWindowPrivate {
GtkWidget *selection_toolbar;
GtkWidget *stack;
GtkWidget *name;
GtkWidget *country;
GtkWidget *city;
GtkWidget *north_lat;
GtkWidget *south_lat;
GtkWidget *latitude;
@ -55,6 +57,16 @@ struct _AgWindowPrivate {
GtkListStore *house_system_model;
GtkListStore *db_chart_data;
AgDbSave *saved_data;
GtkEntryCompletion *country_comp;
GtkEntryCompletion *city_comp;
gchar *selected_country;
gchar *selected_city;
};
struct cc_search {
const gchar *target;
GtkTreeIter *ret_iter;
gchar *ret_code;
};
G_DEFINE_QUARK(ag_window_error_quark, ag_window_error);
@ -64,7 +76,7 @@ G_DEFINE_TYPE_WITH_PRIVATE(AgWindow, ag_window, GTK_TYPE_APPLICATION_WINDOW);
static void
ag_window_gear_menu_action(GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
gpointer user_data)
{
GVariant *state;
@ -522,6 +534,11 @@ ag_window_update_from_chart(AgWindow *window)
);
gtk_entry_set_text(GTK_ENTRY(priv->name), ag_chart_get_name(priv->chart));
gtk_entry_set_text(
GTK_ENTRY(priv->country),
ag_chart_get_country(priv->chart)
);
gtk_entry_set_text(GTK_ENTRY(priv->city), ag_chart_get_city(priv->chart));
if (ag_chart_get_note(priv->chart)) {
// TODO: maybe setting length to -1 here is not that good of an idea…
@ -586,9 +603,8 @@ ag_window_recalculate_chart(AgWindow *window, gboolean set_everything)
edit_data->db_id = db_id;
edit_data->name = g_strdup(gtk_entry_get_text(GTK_ENTRY(priv->name)));
// TODO: This will cause problems with imported charts…
edit_data->country = NULL;
edit_data->city = NULL;
edit_data->country = g_strdup(gtk_entry_get_text(GTK_ENTRY(priv->country)));
edit_data->city = g_strdup(gtk_entry_get_text(GTK_ENTRY(priv->city)));
edit_data->longitude = gtk_spin_button_get_value(
GTK_SPIN_BUTTON(priv->longitude)
);
@ -605,7 +621,7 @@ ag_window_recalculate_chart(AgWindow *window, gboolean set_everything)
edit_data->latitude = - edit_data->latitude;
}
// TODO: So as this…
// TODO: This will cause problems with imported charts
edit_data->altitude = DEFAULT_ALTITUDE;
edit_data->year = gtk_spin_button_get_value_as_int(
GTK_SPIN_BUTTON(priv->year)
@ -1122,8 +1138,8 @@ ag_window_new_chart_action(GSimpleAction *action,
/* Empty edit tab values */
gtk_entry_set_text(GTK_ENTRY(priv->name), "");
//gtk_entry_set_text(GTK_ENTRY(priv->country), "");
//gtk_entry_set_text(GTK_ENTRY(priv->city), "");
gtk_entry_set_text(GTK_ENTRY(priv->country), "");
gtk_entry_set_text(GTK_ENTRY(priv->city), "");
gtk_spin_button_set_value(GTK_SPIN_BUTTON(priv->year), (gdouble)1);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(priv->month), (gdouble)1);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(priv->day), (gdouble)1);
@ -1477,6 +1493,49 @@ ag_window_list_selection_changed_cb(GdMainView *view, AgWindow *window)
// Here it is possible to set button sensitivity later
}
static gboolean
ag_window_city_matches(GtkEntryCompletion *city_comp,
const gchar *key,
GtkTreeIter *iter,
AgWindow *window)
{
AgWindowPrivate *priv = ag_window_get_instance_private(window);
gchar *ccode,
*name,
*normalized_name,
*case_normalized_name;
gboolean ret = FALSE;
gtk_tree_model_get(
gtk_entry_completion_get_model(city_comp), iter,
AG_CITY_NAME, &name,
AG_CITY_COUNTRY, &ccode,
-1
);
if (
(priv->selected_country == NULL)
|| (strcmp(priv->selected_country, ccode) == 0)
) {
normalized_name = g_utf8_normalize(name, -1, G_NORMALIZE_ALL);
if (normalized_name) {
case_normalized_name = g_utf8_casefold(normalized_name, -1);
if (strncmp(key, case_normalized_name, strlen(key)) == 0) {
ret = TRUE;
}
g_free(case_normalized_name);
g_free(normalized_name);
}
}
g_free(name);
g_free(ccode);
return ret;
}
static void
ag_window_init(AgWindow *window)
{
@ -1504,6 +1563,21 @@ ag_window_init(AgWindow *window)
window
);
gtk_entry_completion_set_model(priv->country_comp, country_list);
gtk_entry_completion_set_text_column(priv->country_comp, AG_COUNTRY_NAME);
gtk_entry_set_completion(GTK_ENTRY(priv->country), priv->country_comp);
gtk_entry_completion_set_model(priv->city_comp, city_list);
gtk_entry_completion_set_text_column(priv->city_comp, AG_CITY_NAME);
gtk_entry_completion_set_minimum_key_length(priv->city_comp, 3);
gtk_entry_set_completion(GTK_ENTRY(priv->city), priv->city_comp);
gtk_entry_completion_set_match_func(
priv->city_comp,
(GtkEntryCompletionMatchFunc)ag_window_city_matches,
window,
NULL
);
house_system_list = gswe_all_house_systems();
g_list_foreach(house_system_list, (GFunc)ag_window_add_house_system, priv);
g_list_free(house_system_list);
@ -1596,6 +1670,188 @@ ag_window_name_changed_cb(GtkEntry *name_entry, AgWindow *window)
gtk_header_bar_set_subtitle(GTK_HEADER_BAR(priv->header_bar), name);
}
static gboolean
ag_window_find_country(GtkTreeModel *model,
GtkTreePath *path,
GtkTreeIter *iter,
struct cc_search *search)
{
gchar *name,
*ccode;
gboolean found = FALSE;
gtk_tree_model_get(
model, iter,
AG_COUNTRY_NAME, &name,
AG_COUNTRY_CODE, &ccode,
-1
);
if (g_utf8_collate(search->target, name) == 0) {
found = TRUE;
search->ret_iter = gtk_tree_iter_copy(iter);
search->ret_code = ccode;
} else {
g_free(ccode);
}
return found;
}
/**
* ag_window_country_changed_callback:
* @country: the #GtkSearchEntry for country search
* @window: the window in which the event happens
*
* This function is called whenever the text in the country search entry is
* changed.
*/
static void
ag_window_country_changed_callback(GtkSearchEntry *country, AgWindow *window)
{
struct cc_search search;
AgWindowPrivate *priv = ag_window_get_instance_private(window);
search.target = gtk_entry_get_text(GTK_ENTRY(country));
search.ret_iter = NULL;
gtk_tree_model_foreach(
country_list,
(GtkTreeModelForeachFunc)ag_window_find_country,
&search
);
g_free(priv->selected_country);
if (search.ret_iter != NULL) {
g_debug("Country (entry-changed): %s", search.ret_code);
gtk_tree_iter_free(search.ret_iter);
priv->selected_country = search.ret_code;
} else {
priv->selected_country = NULL;
}
}
static gboolean
ag_window_find_city(GtkTreeModel *model,
GtkTreePath *path,
GtkTreeIter *iter,
struct cc_search *search)
{
gchar *name,
*ccode;
gboolean found = FALSE;
gtk_tree_model_get(
model, iter,
AG_CITY_NAME, &name,
AG_CITY_COUNTRY, &ccode,
-1
);
if (g_utf8_collate(search->target, name) == 0) {
found = TRUE;
search->ret_iter = gtk_tree_iter_copy(iter);
search->ret_code = ccode;
} else {
g_free(ccode);
}
return found;
}
static void
ag_window_city_changed_callback(GtkSearchEntry *city, AgWindow *window)
{
struct cc_search search;
AgWindowPrivate *priv = ag_window_get_instance_private(window);
search.target = gtk_entry_get_text(GTK_ENTRY(city));
search.ret_iter = NULL;
gtk_tree_model_foreach(
city_list,
(GtkTreeModelForeachFunc)ag_window_find_city,
&search
);
g_free(priv->selected_city);
if (search.ret_iter != NULL) {
gdouble longitude,
latitude,
altitude;
gchar *name,
*ccode;
gtk_tree_model_get(
city_list, search.ret_iter,
AG_CITY_COUNTRY, &ccode,
AG_CITY_NAME, &name,
AG_CITY_LAT, &latitude,
AG_CITY_LONG, &longitude,
AG_CITY_ALT, &altitude,
-1
);
if (
(priv->selected_country != NULL)
&& (strcmp(priv->selected_country, ccode) != 0)
) {
return;
}
if (latitude < 0.0) {
gtk_toggle_button_set_active(
GTK_TOGGLE_BUTTON(priv->south_lat),
TRUE
);
gtk_spin_button_set_value(
GTK_SPIN_BUTTON(priv->latitude),
-latitude
);
} else {
gtk_toggle_button_set_active(
GTK_TOGGLE_BUTTON(priv->north_lat),
TRUE
);
gtk_spin_button_set_value(
GTK_SPIN_BUTTON(priv->latitude),
latitude
);
}
if (longitude < 0.0) {
gtk_toggle_button_set_active(
GTK_TOGGLE_BUTTON(priv->west_long),
TRUE
);
gtk_spin_button_set_value(
GTK_SPIN_BUTTON(priv->longitude),
-longitude
);
} else {
gtk_toggle_button_set_active(
GTK_TOGGLE_BUTTON(priv->east_long),
TRUE
);
gtk_spin_button_set_value(
GTK_SPIN_BUTTON(priv->longitude),
longitude
);
}
// TODO: implement setting altitude maybe? Is that really necessary?
g_debug("City (entry-changed): %s (%s); %.6f, %.6f, %.6f", name, search.ret_code, longitude, latitude, altitude);
g_free(name);
gtk_tree_iter_free(search.ret_iter);
priv->selected_city = search.ret_code;
} else {
priv->selected_city = NULL;
}
}
static void
ag_window_class_init(AgWindowClass *klass)
{
@ -1634,6 +1890,22 @@ ag_window_class_init(AgWindowClass *klass)
tab_edit
);
gtk_widget_class_bind_template_child_private(widget_class, AgWindow, name);
gtk_widget_class_bind_template_child_private(
widget_class,
AgWindow,
country
);
gtk_widget_class_bind_template_child_private(
widget_class,
AgWindow,
country_comp
);
gtk_widget_class_bind_template_child_private(widget_class, AgWindow, city);
gtk_widget_class_bind_template_child_private(
widget_class,
AgWindow,
city_comp
);
gtk_widget_class_bind_template_child_private(widget_class, AgWindow, year);
gtk_widget_class_bind_template_child_private(widget_class, AgWindow, month);
gtk_widget_class_bind_template_child_private(widget_class, AgWindow, day);
@ -1732,6 +2004,14 @@ ag_window_class_init(AgWindowClass *klass)
widget_class,
ag_window_name_changed_cb
);
gtk_widget_class_bind_template_callback(
widget_class,
ag_window_country_changed_callback
);
gtk_widget_class_bind_template_callback(
widget_class,
ag_window_city_changed_callback
);
}
gboolean

View File

@ -7,6 +7,7 @@
#include <libxslt/xslt.h>
#include <libxslt/transform.h>
#include <libexslt/exslt.h>
#include <libxml/xmlreader.h>
#include <swe-glib.h>
@ -15,10 +16,16 @@
#include "ag-app.h"
#include "ag-window.h"
#ifndef LIBXML_READER_ENABLED
#error "You need to have libxml2 with XmlReader enabled"
#endif
GtkBuilder *builder;
GtkFileFilter *filter_all = NULL;
GtkFileFilter *filter_chart = NULL;
GtkFileFilter *filter_hor = NULL;
GtkTreeModel *country_list = NULL;
GtkTreeModel *city_list = NULL;
GHashTable *xinclude_positions;
const char *moonStateName[] = {
@ -186,9 +193,9 @@ main(int argc, char *argv[])
{
gint status;
AgApp *app;
GError *err = NULL;
xmlTextReaderPtr reader;
AstrognomeOptions options;
GError *err = NULL;
GOptionEntry option_entries[] = {
{
"new-window", 'n',
@ -279,6 +286,145 @@ main(int argc, char *argv[])
return EXIT_SUCCESS;
}
country_list = GTK_TREE_MODEL(gtk_list_store_new(
AG_COUNTRY_COLCOUNT,
G_TYPE_STRING,
G_TYPE_STRING
));
city_list = GTK_TREE_MODEL(gtk_list_store_new(
AG_CITY_COLCOUNT,
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_DOUBLE,
G_TYPE_DOUBLE,
G_TYPE_DOUBLE,
G_TYPE_DOUBLE,
G_TYPE_DOUBLE
));
reader = xmlReaderForFile(PKGDATADIR "/geodata.xml", NULL, 0);
if (reader != NULL) {
int ret;
while ((ret = xmlTextReaderRead(reader)) == 1) {
gchar *name;
GtkTreeIter iter;
name = (gchar *)xmlTextReaderConstName(reader);
if (strcmp(name, "p") == 0) {
gchar *aname,
*acode,
*alat,
*alon,
*aalt,
*atzo,
*atzd,
*endptr;
gdouble lat,
lon,
alt,
tzo,
tzd;
aname = (gchar *)xmlTextReaderGetAttribute(
reader,
BAD_CAST "n"
);
acode = (gchar *)xmlTextReaderGetAttribute(
reader,
BAD_CAST "c"
);
alat = (gchar *)xmlTextReaderGetAttribute(
reader,
BAD_CAST "lat"
);
alon = (gchar *)xmlTextReaderGetAttribute(
reader,
BAD_CAST "lon"
);
aalt = (gchar *)xmlTextReaderGetAttribute(
reader,
BAD_CAST "alt"
);
atzo = (gchar *)xmlTextReaderGetAttribute(
reader,
BAD_CAST "tzo"
);
atzd = (gchar *)xmlTextReaderGetAttribute(
reader,
BAD_CAST "tzd"
);
lat = g_ascii_strtod(alat, &endptr);
lon = g_ascii_strtod(alon, &endptr);
if (*aalt == '\0') {
alt = DEFAULT_ALTITUDE;
} else {
alt = g_ascii_strtod(aalt, &endptr);
}
tzo = g_ascii_strtod(atzo, &endptr);
tzd = g_ascii_strtod(atzd, &endptr);
gtk_list_store_append(GTK_LIST_STORE(city_list), &iter);
gtk_list_store_set(
GTK_LIST_STORE(city_list), &iter,
AG_CITY_COUNTRY, acode,
AG_CITY_NAME, aname,
AG_CITY_LAT, lat,
AG_CITY_LONG, lon,
AG_CITY_ALT, alt,
AG_CITY_TZO, tzo,
AG_CITY_TZD, tzd,
-1
);
g_free(aname);
g_free(acode);
g_free(alat);
g_free(alon);
g_free(aalt);
g_free(atzo);
g_free(atzd);
} else if (strcmp(name, "c") == 0) {
gchar *aname,
*acode;
aname = (gchar *)xmlTextReaderGetAttribute(
reader,
BAD_CAST "n"
);
acode = (gchar *)xmlTextReaderGetAttribute(
reader,
BAD_CAST "c"
);
gtk_list_store_append(GTK_LIST_STORE(country_list), &iter);
gtk_list_store_set(
GTK_LIST_STORE(country_list), &iter,
AG_COUNTRY_CODE, acode,
AG_COUNTRY_NAME, aname,
-1
);
g_free(aname);
g_free(acode);
}
}
xmlFreeTextReader(reader);
if (ret != 0) {
g_error("Parse error in geodata.xml!");
}
} else {
g_error("Unable to open geodata.xml!");
}
status = g_application_run(G_APPLICATION(app), argc, argv);
g_hash_table_destroy(xinclude_positions);

View File

@ -12,6 +12,25 @@ typedef struct {
extern GtkFileFilter *filter_all;
extern GtkFileFilter *filter_chart;
extern GtkFileFilter *filter_hor;
extern GtkTreeModel *country_list;
extern GtkTreeModel *city_list;
enum {
AG_COUNTRY_CODE,
AG_COUNTRY_NAME,
AG_COUNTRY_COLCOUNT
};
enum {
AG_CITY_COUNTRY,
AG_CITY_NAME,
AG_CITY_LAT,
AG_CITY_LONG,
AG_CITY_ALT,
AG_CITY_TZO,
AG_CITY_TZD,
AG_CITY_COLCOUNT
};
const gchar *ag_house_system_id_to_nick(GsweHouseSystem house_system);
GsweHouseSystem ag_house_system_nick_to_id(const gchar *nick);

View File

@ -136,6 +136,12 @@
<column type="guint"/>
</columns>
</object>
<object class="GtkEntryCompletion" id="country_comp">
<property name="inline_completion">True</property>
</object>
<object class="GtkEntryCompletion" id="city_comp">
<property name="inline_completion">True</property>
</object>
<template class="AgWindow" parent="GtkApplicationWindow">
<property name="can_focus">False</property>
<property name="has_focus">False</property>
@ -394,10 +400,11 @@
<property name="primary_icon_name">edit-find-symbolic</property>
<property name="primary_icon_activatable">False</property>
<property name="primary_icon_sensitive">False</property>
<signal name="search-changed" handler="ag_window_country_changed_callback" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
<property name="top_attach">1</property>
<property name="width">3</property>
</packing>
</child>
@ -419,10 +426,11 @@
<property name="primary_icon_name">edit-find-symbolic</property>
<property name="primary_icon_activatable">False</property>
<property name="primary_icon_sensitive">False</property>
<signal name="search-changed" handler="ag_window_city_changed_callback" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
<property name="top_attach">2</property>
<property name="width">3</property>
</packing>
</child>