aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/datetime-prefs-locations.c168
-rw-r--r--src/datetime-prefs-locations.h2
-rw-r--r--src/datetime-prefs.c84
-rw-r--r--src/datetime-service.c17
-rw-r--r--src/indicator-datetime.c16
-rw-r--r--src/settings-shared.h1
-rw-r--r--src/timezone-completion.c214
-rw-r--r--src/utils.c2
8 files changed, 390 insertions, 114 deletions
diff --git a/src/datetime-prefs-locations.c b/src/datetime-prefs-locations.c
index 0c9a93c..2862022 100644
--- a/src/datetime-prefs-locations.c
+++ b/src/datetime-prefs-locations.c
@@ -35,6 +35,15 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#define DATETIME_DIALOG_UI_FILE PKGDATADIR "/datetime-dialog.ui"
+#define COL_NAME 0
+#define COL_TIME 1
+#define COL_ZONE 2
+#define COL_VISIBLE_NAME 3
+#define COL_ICON 4
+
+static gboolean update_times (GtkWidget * dlg);
+static void save_when_idle (GtkWidget * dlg);
+
static void
handle_add (GtkWidget * button, GtkTreeView * tree)
{
@@ -83,37 +92,45 @@ handle_edit (GtkCellRendererText * renderer, gchar * path, gchar * new_text,
{
GtkTreeIter iter;
+ // Manual user edits are always wrong (unless they are undoing a previous
+ // edit), so we set the error icon here if needed. Common way to get to
+ // this code path is to lose entry focus.
if (gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (store), &iter, path)) {
- gtk_list_store_set (store, &iter, 0, new_text, -1);
+ const gchar * name;
+ gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, COL_NAME, &name, -1);
+ gboolean correct = g_strcmp0 (name, new_text) == 0;
+
+ gtk_list_store_set (store, &iter,
+ COL_VISIBLE_NAME, new_text,
+ COL_ICON, correct ? NULL : GTK_STOCK_DIALOG_ERROR,
+ -1);
}
}
static gboolean
timezone_selected (GtkEntryCompletion * widget, GtkTreeModel * model,
- GtkTreeIter * iter, gpointer user_data)
+ GtkTreeIter * iter, GtkWidget * dlg)
{
- GValue zone_value = {0}, name_value = {0};
const gchar * zone, * name;
- gtk_tree_model_get_value (model, iter, TIMEZONE_COMPLETION_ZONE, &zone_value);
- zone = g_value_get_string (&zone_value);
-
- gtk_tree_model_get_value (model, iter, TIMEZONE_COMPLETION_NAME, &name_value);
- name = g_value_get_string (&name_value);
+ gtk_tree_model_get (model, iter,
+ TIMEZONE_COMPLETION_ZONE, &zone,
+ TIMEZONE_COMPLETION_NAME, &name,
+ -1);
if (zone == NULL || zone[0] == 0) {
- GValue lon_value = {0}, lat_value = {0};
const gchar * strlon, * strlat;
gdouble lon = 0.0, lat = 0.0;
- gtk_tree_model_get_value (model, iter, TIMEZONE_COMPLETION_LONGITUDE, &lon_value);
- strlon = g_value_get_string (&lon_value);
+ gtk_tree_model_get (model, iter,
+ TIMEZONE_COMPLETION_LONGITUDE, &strlon,
+ TIMEZONE_COMPLETION_LATITUDE, &strlat,
+ -1);
+
if (strlon != NULL && strlon[0] != 0) {
lon = strtod(strlon, NULL);
}
- gtk_tree_model_get_value (model, iter, TIMEZONE_COMPLETION_LATITUDE, &lat_value);
- strlat = g_value_get_string (&lat_value);
if (strlat != NULL && strlat[0] != 0) {
lat = strtod(strlat, NULL);
}
@@ -125,11 +142,14 @@ timezone_selected (GtkEntryCompletion * widget, GtkTreeModel * model,
GtkListStore * store = GTK_LIST_STORE (g_object_get_data (G_OBJECT (widget), "store"));
GtkTreeIter * store_iter = (GtkTreeIter *)g_object_get_data (G_OBJECT (widget), "store_iter");
if (store != NULL && store_iter != NULL) {
- gtk_list_store_set (store, store_iter, 0, name, 2, zone, -1);
+ gtk_list_store_set (store, store_iter,
+ COL_VISIBLE_NAME, name,
+ COL_ICON, NULL,
+ COL_NAME, name,
+ COL_ZONE, zone, -1);
}
- g_value_unset (&name_value);
- g_value_unset (&zone_value);
+ update_times (dlg);
return FALSE; // Do normal action too
}
@@ -140,7 +160,6 @@ handle_edit_started (GtkCellRendererText * renderer, GtkCellEditable * editable,
{
if (GTK_IS_ENTRY (editable)) {
GtkEntry *entry = GTK_ENTRY (editable);
- gtk_entry_set_completion (entry, GTK_ENTRY_COMPLETION (completion));
timezone_completion_watch_entry (completion, entry);
GtkListStore * store = GTK_LIST_STORE (g_object_get_data (G_OBJECT (completion), "store"));
@@ -152,9 +171,10 @@ handle_edit_started (GtkCellRendererText * renderer, GtkCellEditable * editable,
}
static gboolean
-update_times (TimezoneCompletion * completion)
+update_times (GtkWidget * dlg)
{
/* For each entry, check zone in column 2 and set column 1 to it's time */
+ TimezoneCompletion * completion = TIMEZONE_COMPLETION (g_object_get_data (G_OBJECT (dlg), "completion"));
GtkListStore * store = GTK_LIST_STORE (g_object_get_data (G_OBJECT (completion), "store"));
GObject * cell = G_OBJECT (g_object_get_data (G_OBJECT (completion), "name-cell"));
@@ -164,16 +184,16 @@ update_times (TimezoneCompletion * completion)
return TRUE;
}
+ g_signal_handlers_block_by_func (store, save_when_idle, dlg);
+
GDateTime * now = g_date_time_new_now_local ();
GtkTreeIter iter;
if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter)) {
do {
- GValue zone_value = {0};
const gchar * strzone;
- gtk_tree_model_get_value (GTK_TREE_MODEL (store), &iter, 2, &zone_value);
- strzone = g_value_get_string (&zone_value);
+ gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, COL_ZONE, &strzone, -1);
if (strzone != NULL && strzone[0] != 0) {
GTimeZone * tz = g_time_zone_new (strzone);
@@ -181,19 +201,20 @@ update_times (TimezoneCompletion * completion)
gchar * format = generate_format_string_at_time (now_tz);
gchar * time_str = g_date_time_format (now_tz, format);
- gtk_list_store_set (store, &iter, 1, time_str, -1);
+ gtk_list_store_set (store, &iter, COL_TIME, time_str, -1);
g_free (time_str);
g_free (format);
g_date_time_unref (now_tz);
g_time_zone_unref (tz);
}
-
- g_value_unset (&zone_value);
} while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter));
}
g_date_time_unref (now);
+
+ g_signal_handlers_unblock_by_func (store, save_when_idle, dlg);
+
return TRUE;
}
@@ -211,7 +232,11 @@ fill_from_settings (GObject * store, GSettings * conf)
split_settings_location (*striter, &zone, &name);
gtk_list_store_append (GTK_LIST_STORE (store), &iter);
- gtk_list_store_set (GTK_LIST_STORE (store), &iter, 0, name, 2, zone, -1);
+ gtk_list_store_set (GTK_LIST_STORE (store), &iter,
+ COL_VISIBLE_NAME, name,
+ COL_ICON, NULL,
+ COL_NAME, name,
+ COL_ZONE, zone, -1);
g_free (zone);
g_free (name);
@@ -221,45 +246,78 @@ fill_from_settings (GObject * store, GSettings * conf)
}
static void
-dialog_closed (GtkWidget * dlg, GObject * store)
+save_to_settings (GObject * store, GSettings * conf)
{
- /* Cleanup a tad */
- guint time_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (dlg), "time-id"));
- g_source_remove (time_id);
-
- /* Now save to settings */
+ gboolean empty = TRUE;
GVariantBuilder builder;
g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
GtkTreeIter iter;
if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter)) {
do {
- GValue zone_value = {0}, name_value = {0};
const gchar * strzone, * strname;
- gtk_tree_model_get_value (GTK_TREE_MODEL (store), &iter, 0, &name_value);
- gtk_tree_model_get_value (GTK_TREE_MODEL (store), &iter, 2, &zone_value);
-
- strzone = g_value_get_string (&zone_value);
- strname = g_value_get_string (&name_value);
+ gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
+ COL_NAME, &strname,
+ COL_ZONE, &strzone,
+ -1);
if (strzone != NULL && strzone[0] != 0 && strname != NULL && strname[0] != 0) {
gchar * settings_string = g_strdup_printf("%s %s", strzone, strname);
g_variant_builder_add (&builder, "s", settings_string);
g_free (settings_string);
+ empty = FALSE;
}
-
- g_value_unset (&zone_value);
- g_value_unset (&name_value);
} while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter));
}
- GVariant * locations = g_variant_builder_end (&builder);
+ if (empty) {
+ /* Empty list */
+ g_variant_builder_clear (&builder);
+ g_settings_set_strv (conf, SETTINGS_LOCATIONS_S, NULL);
+ }
+ else {
+ GVariant * locations = g_variant_builder_end (&builder);
+ g_settings_set_strv (conf, SETTINGS_LOCATIONS_S, g_variant_get_strv (locations, NULL));
+ g_variant_unref (locations);
+ }
+}
+static gboolean
+save_now (GtkWidget *dlg)
+{
GSettings * conf = G_SETTINGS (g_object_get_data (G_OBJECT (dlg), "conf"));
- g_settings_set_strv (conf, SETTINGS_LOCATIONS_S, g_variant_get_strv (locations, NULL));
+ GObject * completion = G_OBJECT (g_object_get_data (G_OBJECT (dlg), "completion"));
+ GObject * store = G_OBJECT (g_object_get_data (completion, "store"));
+
+ save_to_settings (store, conf);
- g_variant_unref (locations);
+ g_object_set_data (G_OBJECT (dlg), "save-id", GINT_TO_POINTER(0));
+
+ return FALSE;
+}
+
+static void
+save_when_idle (GtkWidget *dlg)
+{
+ guint save_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (dlg), "save-id"));
+
+ if (save_id == 0) {
+ save_id = g_idle_add ((GSourceFunc)save_now, dlg);
+ g_object_set_data (G_OBJECT (dlg), "save-id", GINT_TO_POINTER(save_id));
+ }
+}
+
+static void
+dialog_closed (GtkWidget * dlg, GObject * store)
+{
+ /* Cleanup a tad */
+ guint time_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (dlg), "time-id"));
+ g_source_remove (time_id);
+
+ guint save_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (dlg), "save-id"));
+ if (save_id > 0)
+ g_source_remove (save_id);
}
static void
@@ -270,7 +328,7 @@ selection_changed (GtkTreeSelection * selection, GtkWidget * remove_button)
}
GtkWidget *
-datetime_setup_locations_dialog (GtkWindow * parent, CcTimezoneMap * map)
+datetime_setup_locations_dialog (CcTimezoneMap * map)
{
GError * error = NULL;
GtkBuilder * builder = gtk_builder_new ();
@@ -296,7 +354,7 @@ datetime_setup_locations_dialog (GtkWindow * parent, CcTimezoneMap * map)
TimezoneCompletion * completion = timezone_completion_new ();
g_object_set_data (G_OBJECT (completion), "tzmap", map);
g_object_set_data (G_OBJECT (completion), "store", store);
- g_signal_connect (completion, "match-selected", G_CALLBACK (timezone_selected), NULL);
+ g_signal_connect (completion, "match-selected", G_CALLBACK (timezone_selected), dlg);
GtkCellRenderer * cell = gtk_cell_renderer_text_new ();
g_object_set (cell, "editable", TRUE, NULL);
@@ -304,15 +362,19 @@ datetime_setup_locations_dialog (GtkWindow * parent, CcTimezoneMap * map)
g_signal_connect (cell, "edited", G_CALLBACK (handle_edit), store);
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tree), -1,
_("Location"), cell,
- "text", 0, NULL);
+ "text", COL_VISIBLE_NAME, NULL);
GtkTreeViewColumn * loc_col = gtk_tree_view_get_column (GTK_TREE_VIEW (tree), 0);
gtk_tree_view_column_set_expand (loc_col, TRUE);
g_object_set_data (G_OBJECT (completion), "name-cell", cell);
+ cell = gtk_cell_renderer_pixbuf_new ();
+ gtk_tree_view_column_pack_start (loc_col, cell, FALSE);
+ gtk_tree_view_column_add_attribute (loc_col, cell, "icon-name", COL_ICON);
+
cell = gtk_cell_renderer_text_new ();
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tree), -1,
_("Time"), cell,
- "text", 1, NULL);
+ "text", COL_TIME, NULL);
GtkTreeSelection * selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree));
gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
@@ -323,16 +385,18 @@ datetime_setup_locations_dialog (GtkWindow * parent, CcTimezoneMap * map)
g_signal_connect (WIG ("removeButton"), "clicked", G_CALLBACK (handle_remove), tree);
fill_from_settings (store, conf);
-
- guint time_id = g_timeout_add_seconds (2, (GSourceFunc)update_times, completion);
- update_times (completion);
+ g_signal_connect_swapped (store, "row-deleted", G_CALLBACK (save_when_idle), dlg);
+ g_signal_connect_swapped (store, "row-inserted", G_CALLBACK (save_when_idle), dlg);
+ g_signal_connect_swapped (store, "row-changed", G_CALLBACK (save_when_idle), dlg);
+ g_signal_connect_swapped (store, "rows-reordered", G_CALLBACK (save_when_idle), dlg);
g_object_set_data_full (G_OBJECT (dlg), "conf", g_object_ref (conf), g_object_unref);
g_object_set_data_full (G_OBJECT (dlg), "completion", completion, g_object_unref);
- g_object_set_data (G_OBJECT (dlg), "time-id", GINT_TO_POINTER(time_id));
g_signal_connect (dlg, "destroy", G_CALLBACK (dialog_closed), store);
- gtk_window_set_transient_for (GTK_WINDOW (dlg), parent);
+ guint time_id = g_timeout_add_seconds (2, (GSourceFunc)update_times, dlg);
+ g_object_set_data (G_OBJECT (dlg), "time-id", GINT_TO_POINTER(time_id));
+ update_times (dlg);
#undef WIG
diff --git a/src/datetime-prefs-locations.h b/src/datetime-prefs-locations.h
index 1760567..e312894 100644
--- a/src/datetime-prefs-locations.h
+++ b/src/datetime-prefs-locations.h
@@ -28,7 +28,7 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
G_BEGIN_DECLS
-GtkWidget * datetime_setup_locations_dialog (GtkWindow * parent, CcTimezoneMap * map);
+GtkWidget * datetime_setup_locations_dialog (CcTimezoneMap * map);
G_END_DECLS
diff --git a/src/datetime-prefs.c b/src/datetime-prefs.c
index fbc88f2..b1335ca 100644
--- a/src/datetime-prefs.c
+++ b/src/datetime-prefs.c
@@ -53,6 +53,7 @@ GtkWidget * date_spin = NULL;
guint save_time_id = 0;
gboolean user_edited_time = FALSE;
gboolean changing_time = FALSE;
+GtkWidget * loc_dlg = NULL;
/* Turns the boolean property into a string gsettings */
static GVariant *
@@ -183,10 +184,30 @@ ntp_query_answered (GObject *object, GAsyncResult *res, gpointer user_data)
static void
sync_entry (const gchar * location)
{
- gchar * name;
- split_settings_location (location, NULL, &name);
- gtk_entry_set_text (GTK_ENTRY (tz_entry), name);
- g_free (name);
+ gchar * new_zone, * new_name;
+ gchar * old_zone, * old_name;
+
+ split_settings_location (location, &new_zone, &new_name);
+
+ GSettings * conf = g_settings_new (SETTINGS_INTERFACE);
+ gchar * tz_name = g_settings_get_string (conf, SETTINGS_TIMEZONE_NAME_S);
+ split_settings_location (tz_name, &old_zone, &old_name);
+ g_free (tz_name);
+ g_object_unref (conf);
+
+ // new_name is always just a sanitized version of a timezone.
+ // old_name is potentially a saved "pretty" version of a timezone name from
+ // geonames. So we prefer to use it if available and the zones match.
+
+ if (g_strcmp0 (old_zone, new_zone) == 0)
+ gtk_entry_set_text (GTK_ENTRY (tz_entry), old_name);
+ else
+ gtk_entry_set_text (GTK_ENTRY (tz_entry), new_name);
+
+ g_free (new_zone);
+ g_free (old_zone);
+ g_free (new_name);
+ g_free (old_name);
}
static void
@@ -495,46 +516,65 @@ setup_time_spinners (GtkWidget * time, GtkWidget * date)
}
static void
+hide_locations ()
+{
+ if (loc_dlg != NULL)
+ gtk_widget_destroy (loc_dlg);
+}
+
+static void
show_locations (GtkWidget * button, GtkWidget * dlg)
{
- GtkWidget * locationsDlg = datetime_setup_locations_dialog (GTK_WINDOW (dlg), tzmap);
- gtk_widget_show_all (locationsDlg);
+ if (loc_dlg == NULL) {
+ loc_dlg = datetime_setup_locations_dialog (tzmap);
+ gtk_window_set_transient_for (GTK_WINDOW (loc_dlg), GTK_WINDOW (dlg));
+ g_signal_connect (loc_dlg, "destroy", G_CALLBACK (gtk_widget_destroyed), &loc_dlg);
+ g_signal_connect (dlg, "focus-in-event", G_CALLBACK (hide_locations), NULL);
+ gtk_widget_show_all (loc_dlg);
+ }
+ else {
+ gtk_window_present_with_time (GTK_WINDOW (loc_dlg), gtk_get_current_event_time ());
+ }
}
static gboolean
timezone_selected (GtkEntryCompletion * widget, GtkTreeModel * model,
GtkTreeIter * iter, gpointer user_data)
{
- GValue value = {0};
- const gchar * strval;
+ const gchar * name, * zone;
- gtk_tree_model_get_value (model, iter, TIMEZONE_COMPLETION_ZONE, &value);
- strval = g_value_get_string (&value);
+ gtk_tree_model_get (model, iter,
+ TIMEZONE_COMPLETION_NAME, &name,
+ TIMEZONE_COMPLETION_ZONE, &zone,
+ -1);
- if (strval != NULL && strval[0] != 0) {
- cc_timezone_map_set_timezone (tzmap, strval);
- }
- else {
- GValue lon_value = {0}, lat_value = {0};
+ if (zone == NULL || zone[0] == 0) {
const gchar * strlon, * strlat;
gdouble lon = 0.0, lat = 0.0;
- gtk_tree_model_get_value (model, iter, TIMEZONE_COMPLETION_LONGITUDE, &lon_value);
- strlon = g_value_get_string (&lon_value);
+ gtk_tree_model_get (model, iter,
+ TIMEZONE_COMPLETION_LONGITUDE, &strlon,
+ TIMEZONE_COMPLETION_LATITUDE, &strlat,
+ -1);
+
if (strlon != NULL && strlon[0] != 0) {
lon = strtod(strlon, NULL);
}
- gtk_tree_model_get_value (model, iter, TIMEZONE_COMPLETION_LATITUDE, &lat_value);
- strlat = g_value_get_string (&lat_value);
if (strlat != NULL && strlat[0] != 0) {
lat = strtod(strlat, NULL);
}
- cc_timezone_map_set_coords (tzmap, lon, lat);
+ zone = cc_timezone_map_get_timezone_at_coords (tzmap, lon, lat);
}
- g_value_unset (&value);
+ GSettings * conf = g_settings_new (SETTINGS_INTERFACE);
+ gchar * tz_name = g_strdup_printf ("%s %s", zone, name);
+ g_settings_set_string (conf, SETTINGS_TIMEZONE_NAME_S, tz_name);
+ g_free (tz_name);
+ g_object_unref (conf);
+
+ cc_timezone_map_set_timezone (tzmap, zone);
return FALSE; // Do normal action too
}
@@ -604,8 +644,6 @@ create_dialog (void)
/* And completion entry */
TimezoneCompletion * completion = timezone_completion_new ();
- gtk_entry_set_completion (GTK_ENTRY (WIG ("timezoneEntry")),
- GTK_ENTRY_COMPLETION (completion));
timezone_completion_watch_entry (completion, GTK_ENTRY (WIG ("timezoneEntry")));
g_signal_connect (completion, "match-selected", G_CALLBACK (timezone_selected), NULL);
diff --git a/src/datetime-service.c b/src/datetime-service.c
index 65df77e..905128b 100644
--- a/src/datetime-service.c
+++ b/src/datetime-service.c
@@ -364,12 +364,13 @@ static gboolean
check_for_calendar (gpointer user_data)
{
g_return_val_if_fail (calendar != NULL, FALSE);
-
+ // Always enable the calendar even if it does nothing
+ dbusmenu_menuitem_property_set_bool(calendar, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE);
+ dbusmenu_menuitem_property_set_bool(calendar, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE);
+
gchar *evo = g_find_program_in_path("evolution");
if (evo != NULL) {
g_debug("Found the calendar application: %s", evo);
- dbusmenu_menuitem_property_set_bool(calendar, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE);
- dbusmenu_menuitem_property_set_bool(calendar, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE);
dbusmenu_menuitem_property_set_bool(date, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE);
g_signal_connect (G_OBJECT(date), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
@@ -437,11 +438,11 @@ update_timezone_menu_items(gpointer user_data) {
gboolean show = g_settings_get_boolean (conf, SETTINGS_SHOW_LOCATIONS_S);
- if (len > 0) {
- dbusmenu_menuitem_property_set_bool (locations_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, show);
- dbusmenu_menuitem_property_set_bool (current_location, DBUSMENU_MENUITEM_PROP_VISIBLE, show);
- dbusmenu_menuitem_property_set_bool (current_location, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE);
- } else {
+ dbusmenu_menuitem_property_set_bool (locations_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, show);
+ dbusmenu_menuitem_property_set_bool (current_location, DBUSMENU_MENUITEM_PROP_VISIBLE, show);
+ dbusmenu_menuitem_property_set_bool (current_location, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE);
+
+ if (len == 0) {
g_debug("No locations configured (Empty List)");
return FALSE;
}
diff --git a/src/indicator-datetime.c b/src/indicator-datetime.c
index 1cdcd3f..aed498d 100644
--- a/src/indicator-datetime.c
+++ b/src/indicator-datetime.c
@@ -525,7 +525,7 @@ set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec
}
break;
}
- case PROP_SHOW_SECONDS:
+ case PROP_SHOW_SECONDS: {
if (g_value_get_boolean(value) != self->priv->show_seconds) {
self->priv->show_seconds = !self->priv->show_seconds;
if (self->priv->time_mode != SETTINGS_TIME_CUSTOM) {
@@ -534,7 +534,8 @@ set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec
}
}
break;
- case PROP_SHOW_DAY:
+ }
+ case PROP_SHOW_DAY: {
if (g_value_get_boolean(value) != self->priv->show_day) {
self->priv->show_day = !self->priv->show_day;
if (self->priv->time_mode != SETTINGS_TIME_CUSTOM) {
@@ -542,7 +543,8 @@ set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec
}
}
break;
- case PROP_SHOW_DATE:
+ }
+ case PROP_SHOW_DATE: {
if (g_value_get_boolean(value) != self->priv->show_date) {
self->priv->show_date = !self->priv->show_date;
if (self->priv->time_mode != SETTINGS_TIME_CUSTOM) {
@@ -550,6 +552,7 @@ set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec
}
}
break;
+ }
case PROP_CUSTOM_TIME_FORMAT: {
const gchar * newstr = g_value_get_string(value);
if (g_strcmp0(newstr, self->priv->custom_string) != 0) {
@@ -585,11 +588,12 @@ set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec
gtk_widget_set_visible (GTK_WIDGET (self->priv->ido_calendar), self->priv->show_calendar);
}
break;
- }
- default:
+ }
+ default: {
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
return;
}
+ }
if (!update) {
return;
@@ -813,7 +817,7 @@ setup_timer (IndicatorDatetime * self, GDateTime * datetime)
if (self->priv->show_seconds ||
(self->priv->time_mode == SETTINGS_TIME_CUSTOM && self->priv->custom_show_seconds)) {
- self->priv->timer = g_timeout_add_full(G_PRIORITY_HIGH, 865, timer_func, self, NULL);
+ self->priv->timer = g_timeout_add_full(G_PRIORITY_HIGH, 999, timer_func, self, NULL);
} else {
if (datetime == NULL) {
datetime = g_date_time_new_now_local();
diff --git a/src/settings-shared.h b/src/settings-shared.h
index cdbb063..1ad4799 100644
--- a/src/settings-shared.h
+++ b/src/settings-shared.h
@@ -34,6 +34,7 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#define SETTINGS_SHOW_EVENTS_S "show-events"
#define SETTINGS_SHOW_LOCATIONS_S "show-locations"
#define SETTINGS_LOCATIONS_S "locations"
+#define SETTINGS_TIMEZONE_NAME_S "timezone-name"
enum {
SETTINGS_TIME_LOCALE = 0,
diff --git a/src/timezone-completion.c b/src/timezone-completion.c
index aaf6bdc..ad04d0c 100644
--- a/src/timezone-completion.c
+++ b/src/timezone-completion.c
@@ -24,6 +24,7 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#include <json-glib/json-glib.h>
#include <gdk/gdk.h>
+#include <gdk/gdkkeysyms.h>
#include <glib/gi18n.h>
#include "timezone-completion.h"
#include "tz.h"
@@ -41,6 +42,7 @@ struct _TimezoneCompletionPrivate
GtkEntry * entry;
guint queued_request;
guint changed_id;
+ guint keypress_id;
GCancellable * cancel;
gchar * request_text;
GHashTable * request_table;
@@ -79,12 +81,48 @@ save_and_use_model (TimezoneCompletion * completion, GtkTreeModel * model)
gtk_entry_completion_set_match_func (GTK_ENTRY_COMPLETION (completion), match_func, NULL, NULL);
gtk_entry_completion_set_model (GTK_ENTRY_COMPLETION (completion), model);
- gtk_entry_completion_complete (GTK_ENTRY_COMPLETION (completion));
- /* By this time, the changed signal has come and gone. We didn't give a
- model to use, so no popup appeared for user. Poke the entry again to show
- popup in 300ms. */
- g_signal_emit_by_name (priv->entry, "changed");
+ if (priv->entry != NULL) {
+ gtk_entry_completion_complete (GTK_ENTRY_COMPLETION (completion));
+
+ /* By this time, the changed signal has come and gone. We didn't give a
+ model to use, so no popup appeared for user. Poke the entry again to show
+ popup in 300ms. */
+ g_signal_emit_by_name (priv->entry, "changed");
+ }
+}
+
+static gint
+sort_zone (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b,
+ gpointer user_data)
+{
+ /* Anything that has text as a prefix goes first, in mostly sorted order.
+ Then everything else goes after, in mostly sorted order. */
+ const gchar *casefolded_text = (const gchar *)user_data;
+
+ const gchar *namea = NULL, *nameb = NULL;
+ gtk_tree_model_get (model, a, TIMEZONE_COMPLETION_NAME, &namea, -1);
+ gtk_tree_model_get (model, b, TIMEZONE_COMPLETION_NAME, &nameb, -1);
+
+ gchar *casefolded_namea = NULL, *casefolded_nameb = NULL;
+ casefolded_namea = g_utf8_casefold (namea, -1);
+ casefolded_nameb = g_utf8_casefold (nameb, -1);
+
+ gboolean amatches = FALSE, bmatches = FALSE;
+ amatches = strncmp (casefolded_text, casefolded_namea, strlen(casefolded_text)) == 0;
+ bmatches = strncmp (casefolded_text, casefolded_nameb, strlen(casefolded_text)) == 0;
+
+ gint rv;
+ if (amatches && !bmatches)
+ rv = -1;
+ else if (bmatches && !amatches)
+ rv = 1;
+ else
+ rv = g_utf8_collate (casefolded_namea, casefolded_nameb);
+
+ g_free (casefolded_namea);
+ g_free (casefolded_nameb);
+ return rv;
}
static void
@@ -99,14 +137,15 @@ json_parse_ready (GObject *object, GAsyncResult *res, gpointer user_data)
json_parser_load_from_stream_finish (JSON_PARSER (object), res, &error);
- if (priv->cancel && (error == NULL || error->code != G_IO_ERROR_CANCELLED)) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) && priv->cancel) {
g_cancellable_reset (priv->cancel);
}
if (error != NULL) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ save_and_use_model (completion, priv->initial_model);
g_warning ("Could not parse geoname JSON data: %s", error->message);
g_error_free (error);
- save_and_use_model (completion, priv->initial_model);
return;
}
@@ -179,6 +218,13 @@ json_parse_ready (GObject *object, GAsyncResult *res, gpointer user_data)
TIMEZONE_COMPLETION_LONGITUDE, longitude,
TIMEZONE_COMPLETION_LATITUDE, latitude,
-1);
+ gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (store),
+ TIMEZONE_COMPLETION_NAME, sort_zone,
+ g_utf8_casefold(priv->request_text, -1),
+ g_free);
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
+ TIMEZONE_COMPLETION_NAME,
+ GTK_SORT_ASCENDING);
}
prev_name = name;
@@ -189,6 +235,20 @@ json_parse_ready (GObject *object, GAsyncResult *res, gpointer user_data)
json_reader_end_element (reader);
}
+ if (strlen (priv->request_text) < 4) {
+ gchar * lower_text = g_ascii_strdown (priv->request_text, -1);
+ if (g_strcmp0 (lower_text, "ut") == 0 ||
+ g_strcmp0 (lower_text, "utc") == 0) {
+ GtkTreeIter iter;
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ TIMEZONE_COMPLETION_ZONE, "UTC",
+ TIMEZONE_COMPLETION_NAME, "UTC",
+ -1);
+ }
+ g_free (lower_text);
+ }
+
save_and_use_model (completion, GTK_TREE_MODEL (store));
g_object_unref (G_OBJECT (reader));
}
@@ -203,14 +263,15 @@ geonames_data_ready (GObject *object, GAsyncResult *res, gpointer user_data)
stream = g_file_read_finish (G_FILE (object), res, &error);
- if (priv->cancel && (error == NULL || error->code != G_IO_ERROR_CANCELLED)) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) && priv->cancel) {
g_cancellable_reset (priv->cancel);
}
if (error != NULL) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ save_and_use_model (completion, priv->initial_model);
g_warning ("Could not connect to geoname lookup server: %s", error->message);
g_error_free (error);
- save_and_use_model (completion, priv->initial_model);
return;
}
@@ -257,6 +318,7 @@ entry_changed (GtkEntry * entry, TimezoneCompletion * completion)
if (priv->queued_request) {
g_source_remove (priv->queued_request);
+ priv->queued_request = 0;
}
/* See if we've already got this one */
@@ -272,6 +334,94 @@ entry_changed (GtkEntry * entry, TimezoneCompletion * completion)
gtk_entry_completion_complete (GTK_ENTRY_COMPLETION (completion));
}
+static GtkWidget *
+get_descendent (GtkWidget * parent, GType type)
+{
+ if (g_type_is_a (G_OBJECT_TYPE (parent), type))
+ return parent;
+
+ if (GTK_IS_CONTAINER (parent)) {
+ GList * children = gtk_container_get_children (GTK_CONTAINER (parent));
+ GList * iter;
+ for (iter = children; iter; iter = iter->next) {
+ GtkWidget * found = get_descendent (GTK_WIDGET (iter->data), type);
+ if (found) {
+ g_list_free (children);
+ return found;
+ }
+ }
+ g_list_free (children);
+ }
+
+ return NULL;
+}
+
+/**
+ * The popup window and its GtkTreeView are private to our parent completion
+ * object. We can't get access to discover if there is a highlighted item or
+ * even if the window is showing right now. So this is a super hack to find
+ * it by looking through our toplevel's window group and finding a window with
+ * a GtkTreeView that points at our model. There should be only one ever, so
+ * we'll use the first one we find.
+ */
+static GtkTreeView *
+find_popup_treeview (GtkWidget * widget, GtkTreeModel * model)
+{
+ GtkWidget * toplevel = gtk_widget_get_toplevel (widget);
+ if (!GTK_IS_WINDOW (toplevel))
+ return NULL;
+
+ GtkWindowGroup * group = gtk_window_get_group (GTK_WINDOW (toplevel));
+ GList * windows = gtk_window_group_list_windows (group);
+ GList * iter;
+ for (iter = windows; iter; iter = iter->next) {
+ if (iter->data == toplevel)
+ continue; // Skip our own window, we don't have it
+ GtkWidget * view = get_descendent (GTK_WIDGET (iter->data), GTK_TYPE_TREE_VIEW);
+ if (view != NULL) {
+ GtkTreeModel * tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
+ if (GTK_IS_TREE_MODEL_FILTER (tree_model))
+ tree_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (tree_model));
+ if (tree_model == model) {
+ g_list_free (windows);
+ return GTK_TREE_VIEW (view);
+ }
+ }
+ }
+ g_list_free (windows);
+
+ return NULL;
+}
+
+static gboolean
+entry_keypress (GtkEntry * entry, GdkEventKey *event, TimezoneCompletion * completion)
+{
+ if (event->keyval == GDK_ISO_Enter ||
+ event->keyval == GDK_KP_Enter ||
+ event->keyval == GDK_Return) {
+ /* Make sure that user has a selection to choose, otherwise ignore */
+ GtkTreeModel * model = gtk_entry_completion_get_model (GTK_ENTRY_COMPLETION (completion));
+ GtkTreeView * view = find_popup_treeview (GTK_WIDGET (entry), model);
+ if (view == NULL) {
+ // Just beep if popup hasn't appeared yet.
+ gtk_widget_error_bell (GTK_WIDGET (entry));
+ return TRUE;
+ }
+
+ GtkTreeSelection * sel = gtk_tree_view_get_selection (view);
+ GtkTreeModel * sel_model = NULL;
+ if (!gtk_tree_selection_get_selected (sel, &sel_model, NULL)) {
+ // No selection, we should help them out and select first item in list
+ GtkTreeIter iter;
+ if (gtk_tree_model_get_iter_first (sel_model, &iter))
+ gtk_tree_selection_select_iter (sel, &iter);
+ // And fall through to normal handler code
+ }
+ }
+
+ return FALSE;
+}
+
void
timezone_completion_watch_entry (TimezoneCompletion * completion, GtkEntry * entry)
{
@@ -282,15 +432,22 @@ timezone_completion_watch_entry (TimezoneCompletion * completion, GtkEntry * ent
priv->queued_request = 0;
}
if (priv->entry) {
- g_source_remove (priv->changed_id);
+ g_signal_handler_disconnect (priv->entry, priv->changed_id);
+ g_signal_handler_disconnect (priv->entry, priv->keypress_id);
g_object_remove_weak_pointer (G_OBJECT (priv->entry), (gpointer *)&priv->entry);
+ gtk_entry_set_completion (priv->entry, NULL);
}
guint id = g_signal_connect (entry, "changed", G_CALLBACK (entry_changed), completion);
priv->changed_id = id;
+ id = g_signal_connect (entry, "key-press-event", G_CALLBACK (entry_keypress), completion);
+ priv->keypress_id = id;
+
priv->entry = entry;
g_object_add_weak_pointer (G_OBJECT (entry), (gpointer *)&priv->entry);
+
+ gtk_entry_set_completion (entry, GTK_ENTRY_COMPLETION (completion));
}
static GtkListStore *
@@ -332,6 +489,13 @@ get_initial_model (void)
g_free (name);
}
+ GtkTreeIter iter;
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ TIMEZONE_COMPLETION_ZONE, "UTC",
+ TIMEZONE_COMPLETION_NAME, "UTC",
+ -1);
+
tz_db_free (db);
return store;
}
@@ -340,29 +504,24 @@ static void
data_func (GtkCellLayout *cell_layout, GtkCellRenderer *cell,
GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer user_data)
{
- GValue name_val = {0}, admin1_val = {0}, country_val = {0};
const gchar * name, * admin1, * country;
- gtk_tree_model_get_value (GTK_TREE_MODEL (tree_model), iter, TIMEZONE_COMPLETION_NAME, &name_val);
- gtk_tree_model_get_value (GTK_TREE_MODEL (tree_model), iter, TIMEZONE_COMPLETION_ADMIN1, &admin1_val);
- gtk_tree_model_get_value (GTK_TREE_MODEL (tree_model), iter, TIMEZONE_COMPLETION_COUNTRY, &country_val);
-
- name = g_value_get_string (&name_val);
- admin1 = g_value_get_string (&admin1_val);
- country = g_value_get_string (&country_val);
+ gtk_tree_model_get (GTK_TREE_MODEL (tree_model), iter,
+ TIMEZONE_COMPLETION_NAME, &name,
+ TIMEZONE_COMPLETION_ADMIN1, &admin1,
+ TIMEZONE_COMPLETION_COUNTRY, &country,
+ -1);
gchar * user_name;
- if (admin1 == NULL || admin1[0] == 0) {
+ if (country == NULL || country[0] == 0) {
+ user_name = g_strdup (name);
+ } else if (admin1 == NULL || admin1[0] == 0) {
user_name = g_strdup_printf ("%s <small>(%s)</small>", name, country);
} else {
user_name = g_strdup_printf ("%s <small>(%s, %s)</small>", name, admin1, country);
}
g_object_set (G_OBJECT (cell), "markup", user_name, NULL);
-
- g_value_unset (&name_val);
- g_value_unset (&admin1_val);
- g_value_unset (&country_val);
}
static void
@@ -410,10 +569,17 @@ timezone_completion_dispose (GObject * object)
TimezoneCompletionPrivate * priv = TIMEZONE_COMPLETION_GET_PRIVATE (completion);
if (priv->changed_id) {
- g_source_remove (priv->changed_id);
+ if (priv->entry)
+ g_signal_handler_disconnect (priv->entry, priv->changed_id);
priv->changed_id = 0;
}
+ if (priv->keypress_id) {
+ if (priv->entry)
+ g_signal_handler_disconnect (priv->entry, priv->keypress_id);
+ priv->keypress_id = 0;
+ }
+
if (priv->entry != NULL) {
g_object_remove_weak_pointer (G_OBJECT (priv->entry), (gpointer *)&priv->entry);
}
diff --git a/src/utils.c b/src/utils.c
index 537495b..d8851aa 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -233,6 +233,8 @@ generate_format_string_at_time (GDateTime * time)
g_date_time_unref(future_bound);
}
+ g_date_time_unref (now);
+
return generate_format_string_full(show_day, show_date);
}