Back to index

indicator-datetime  12.10.0
datetime-prefs.c
Go to the documentation of this file.
00001 /* -*- Mode: C; coding: utf-8; indent-tabs-mode: nil; tab-width: 2 -*-
00002 
00003 A dialog for setting time and date preferences.
00004 
00005 Copyright 2011 Canonical Ltd.
00006 
00007 Authors:
00008     Ted Gould <ted@canonical.com>
00009     Michael Terry <michael.terry@canonical.com>
00010 
00011 This program is free software: you can redistribute it and/or modify it 
00012 under the terms of the GNU General Public License version 3, as published 
00013 by the Free Software Foundation.
00014 
00015 This program is distributed in the hope that it will be useful, but 
00016 WITHOUT ANY WARRANTY; without even the implied warranties of 
00017 MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR 
00018 PURPOSE.  See the GNU General Public License for more details.
00019 
00020 You should have received a copy of the GNU General Public License along 
00021 with this program.  If not, see <http://www.gnu.org/licenses/>.
00022 */
00023 
00024 #ifdef HAVE_CONFIG_H
00025 #include "config.h"
00026 #endif
00027 
00028 #include <stdlib.h>
00029 #include <libintl.h>
00030 #include <locale.h>
00031 #include <langinfo.h>
00032 #include <glib/gi18n-lib.h>
00033 #include <gdk/gdkkeysyms.h>
00034 #include <gtk/gtk.h>
00035 #include <polkit/polkit.h>
00036 #include <libgnome-control-center/cc-panel.h>
00037 #include <timezonemap/cc-timezone-map.h>
00038 #include <timezonemap/timezone-completion.h>
00039 
00040 #include "dbus-shared.h"
00041 #include "settings-shared.h"
00042 #include "utils.h"
00043 #include "datetime-prefs-locations.h"
00044 
00045 #define DATETIME_DIALOG_UI_FILE PKGDATADIR "/datetime-dialog.ui"
00046 
00047 #define INDICATOR_DATETIME_TYPE_PANEL indicator_datetime_panel_get_type()
00048 
00049 typedef struct _IndicatorDatetimePanel IndicatorDatetimePanel;
00050 typedef struct _IndicatorDatetimePanelPrivate IndicatorDatetimePanelPrivate;
00051 typedef struct _IndicatorDatetimePanelClass IndicatorDatetimePanelClass;
00052 
00053 struct _IndicatorDatetimePanel
00054 {
00055   CcPanel parent;
00056   IndicatorDatetimePanelPrivate * priv;
00057 };
00058 
00059 struct _IndicatorDatetimePanelPrivate
00060 {
00061   GtkBuilder *         builder;
00062   GDBusProxy *         proxy;
00063   GtkWidget *          auto_radio;
00064   GtkWidget *          tz_entry;
00065   CcTimezoneMap *      tzmap;
00066   GtkWidget *          time_spin;
00067   GtkWidget *          date_spin;
00068   guint                save_time_id;
00069   gboolean             user_edited_time;
00070   gboolean             changing_time;
00071   GtkWidget *          loc_dlg;
00072   CcTimezoneCompletion * completion;
00073   GCancellable         * tz_query_cancel;
00074   GCancellable         * ntp_query_cancel;
00075 };
00076 
00077 struct _IndicatorDatetimePanelClass
00078 {
00079   CcPanelClass parent_class;
00080 };
00081 
00082 G_DEFINE_DYNAMIC_TYPE (IndicatorDatetimePanel, indicator_datetime_panel, CC_TYPE_PANEL)
00083 
00084 /* Turns the boolean property into a string gsettings */
00085 static GVariant *
00086 bind_hours_set (const GValue * value, const GVariantType * type, gpointer user_data)
00087 {
00088   const gchar * output = NULL;
00089   gboolean is_12hour_button = (gboolean)GPOINTER_TO_INT(user_data);
00090 
00091   if (g_value_get_boolean(value)) {
00092     /* Only do anything if we're setting active = true */
00093     output = is_12hour_button ? "12-hour" : "24-hour";
00094   } else {
00095     return NULL;
00096   }
00097 
00098   return g_variant_new_string (output);
00099 }
00100 
00101 /* Turns a string gsettings into a boolean property */
00102 static gboolean
00103 bind_hours_get (GValue * value, GVariant * variant, gpointer user_data)
00104 {
00105   const gchar * str = g_variant_get_string(variant, NULL);
00106   gboolean output = FALSE;
00107   gboolean is_12hour_button = (gboolean)GPOINTER_TO_INT(user_data);
00108 
00109   if (g_strcmp0(str, "locale-default") == 0) {
00110     output = (is_12hour_button == is_locale_12h ());
00111   } else if (g_strcmp0(str, "12-hour") == 0) {
00112     output = is_12hour_button;
00113   } else if (g_strcmp0(str, "24-hour") == 0) {
00114     output = !is_12hour_button;
00115   } else {
00116     return FALSE;
00117   }
00118 
00119   g_value_set_boolean (value, output);
00120   return TRUE;
00121 }
00122 
00123 static void
00124 widget_dependency_cb (GtkWidget * parent, GParamSpec *pspec, GtkWidget * dependent)
00125 {
00126   gboolean active, sensitive;
00127   g_object_get (G_OBJECT (parent),
00128                 "active", &active,
00129                 "sensitive", &sensitive, NULL);
00130   gtk_widget_set_sensitive (dependent, active && sensitive);
00131 }
00132 
00133 static void
00134 add_widget_dependency (GtkWidget * parent, GtkWidget * dependent)
00135 {
00136   g_signal_connect (parent, "notify::active", G_CALLBACK(widget_dependency_cb),
00137                     dependent);
00138   g_signal_connect (parent, "notify::sensitive", G_CALLBACK(widget_dependency_cb),
00139                     dependent);
00140   widget_dependency_cb (parent, NULL, dependent);
00141 }
00142 
00143 static void
00144 polkit_dependency_cb (GPermission * permission, GParamSpec *pspec, GtkWidget * dependent)
00145 {
00146   gboolean allowed = FALSE;
00147 
00148   g_object_get (G_OBJECT (permission),
00149                 "allowed", &allowed, NULL);
00150 
00151   gtk_widget_set_sensitive (dependent, allowed);
00152 }
00153 
00154 static void
00155 add_polkit_dependency_helper (GtkWidget * parent, GParamSpec *pspec, GtkWidget * dependent)
00156 {
00157   GtkLockButton * button = GTK_LOCK_BUTTON (parent);
00158   GPermission * permission = gtk_lock_button_get_permission (button);
00159   g_signal_connect (permission, "notify::allowed",
00160                     G_CALLBACK(polkit_dependency_cb), dependent);
00161   polkit_dependency_cb (permission, NULL, dependent);
00162 }
00163 
00164 static void
00165 add_polkit_dependency (GtkWidget * parent, GtkWidget * dependent)
00166 {
00167   /* polkit async hasn't finished at this point, so wait for permission to come in */
00168   g_signal_connect (parent, "notify::permission", G_CALLBACK(add_polkit_dependency_helper),
00169                     dependent);
00170   gtk_widget_set_sensitive (dependent, FALSE);
00171 }
00172 
00173 static void
00174 polkit_perm_ready (GObject *source_object, GAsyncResult *res, gpointer user_data)
00175 {
00176   GError * error = NULL;
00177   GPermission * permission = polkit_permission_new_finish (res, &error);
00178 
00179   if (error != NULL) {
00180     g_warning ("Could not get permission object: %s", error->message);
00181     g_error_free (error);
00182     return;
00183   }
00184 
00185   GtkLockButton * button = GTK_LOCK_BUTTON (user_data);
00186   gtk_lock_button_set_permission (button, permission);
00187 }
00188 
00189 static void
00190 dbus_set_answered (GObject *object, GAsyncResult *res, gpointer command)
00191 {
00192   GError * error = NULL;
00193   GVariant * answers = g_dbus_proxy_call_finish (G_DBUS_PROXY (object), res, &error);
00194 
00195   if (error != NULL) {
00196     g_warning("Could not set '%s' for SettingsDaemon: %s", (gchar *)command, error->message);
00197     g_error_free(error);
00198     return;
00199   }
00200 
00201   g_variant_unref (answers);
00202 }
00203 
00204 static void
00205 toggle_ntp (GtkWidget * radio, GParamSpec * pspec, IndicatorDatetimePanel * self)
00206 {
00207   gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (radio));
00208 
00209   g_dbus_proxy_call (self->priv->proxy, "SetUsingNtp", g_variant_new ("(b)", active),
00210                      G_DBUS_CALL_FLAGS_NONE, -1, NULL, dbus_set_answered, "using_ntp");
00211 }
00212 
00213 static void
00214 ntp_query_answered (GObject *object, GAsyncResult *res, IndicatorDatetimePanel * self)
00215 {
00216   GError * error = NULL;
00217   GVariant * answers = g_dbus_proxy_call_finish (G_DBUS_PROXY (object), res, &error);
00218 
00219   g_clear_object (&self->priv->ntp_query_cancel);
00220 
00221   if (error != NULL) {
00222     g_warning("Could not query DBus proxy for SettingsDaemon: %s", error->message);
00223     g_error_free(error);
00224     return;
00225   }
00226 
00227   gboolean can_use_ntp, is_using_ntp;
00228   g_variant_get (answers, "(bb)", &can_use_ntp, &is_using_ntp);
00229 
00230   gtk_widget_set_sensitive (GTK_WIDGET (self->priv->auto_radio), can_use_ntp);
00231   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->priv->auto_radio), is_using_ntp);
00232 
00233   g_signal_connect (self->priv->auto_radio, "notify::active", G_CALLBACK (toggle_ntp), self);
00234 
00235   g_variant_unref (answers);
00236 }
00237 
00238 static void
00239 sync_entry (IndicatorDatetimePanel * self, const gchar * location)
00240 {
00241   gchar * name = get_current_zone_name (location);
00242   gtk_entry_set_text (GTK_ENTRY (self->priv->tz_entry), name);
00243   g_free (name);
00244 
00245   gtk_entry_set_icon_from_stock (GTK_ENTRY (self->priv->tz_entry),
00246                                  GTK_ENTRY_ICON_SECONDARY, NULL);
00247 }
00248 
00249 static void
00250 tz_changed (CcTimezoneMap * map, CcTimezoneLocation * location, IndicatorDatetimePanel * self)
00251 {
00252   if (location == NULL)
00253     return;
00254 
00255   gchar * zone;
00256   g_object_get (location, "zone", &zone, NULL);
00257 
00258   g_dbus_proxy_call (self->priv->proxy, "SetTimezone", g_variant_new ("(s)", zone),
00259                      G_DBUS_CALL_FLAGS_NONE, -1, NULL, dbus_set_answered, "timezone");
00260 
00261   sync_entry (self, zone);
00262 
00263   g_free (zone);
00264 }
00265 
00266 static void
00267 tz_query_answered (GObject *object, GAsyncResult *res, IndicatorDatetimePanel * self)
00268 {
00269   GError * error = NULL;
00270   GVariant * answers = g_dbus_proxy_call_finish (G_DBUS_PROXY (object), res, &error);
00271 
00272   g_clear_object (&self->priv->tz_query_cancel);
00273 
00274   if (error != NULL) {
00275     g_warning("Could not query DBus proxy for SettingsDaemon: %s", error->message);
00276     g_error_free(error);
00277     return;
00278   }
00279 
00280   const gchar * timezone;
00281   g_variant_get (answers, "(&s)", &timezone);
00282 
00283   cc_timezone_map_set_timezone (self->priv->tzmap, timezone);
00284 
00285   sync_entry (self, timezone);
00286   g_signal_connect (self->priv->tzmap, "location-changed", G_CALLBACK (tz_changed), self);
00287 
00288   g_variant_unref (answers);
00289 }
00290 
00291 static void
00292 proxy_ready (GObject *object, GAsyncResult *res, IndicatorDatetimePanel * self)
00293 {
00294   GError * error = NULL;
00295   IndicatorDatetimePanelPrivate * priv = self->priv;
00296 
00297   self->priv->proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
00298 
00299   if (error != NULL) {
00300     g_critical("Could not grab DBus proxy for SettingsDaemon: %s", error->message);
00301     g_error_free(error);
00302     return;
00303   }
00304 
00305   /* And now, do initial proxy configuration */
00306   if (priv->ntp_query_cancel == NULL) {
00307     priv->ntp_query_cancel = g_cancellable_new();
00308     g_dbus_proxy_call (priv->proxy, "GetUsingNtp", NULL, G_DBUS_CALL_FLAGS_NONE, -1,
00309                        priv->ntp_query_cancel, (GAsyncReadyCallback)ntp_query_answered, self);
00310   }
00311   if (priv->tz_query_cancel == NULL); {
00312     priv->tz_query_cancel = g_cancellable_new();
00313     g_dbus_proxy_call (priv->proxy, "GetTimezone", NULL, G_DBUS_CALL_FLAGS_NONE, -1,
00314                        priv->tz_query_cancel, (GAsyncReadyCallback)tz_query_answered, self);
00315   }
00316 }
00317 
00318 static void
00319 service_name_owner_changed (GDBusProxy * proxy, GParamSpec *pspec, gpointer user_data)
00320 {
00321   GtkWidget * widget = GTK_WIDGET (user_data);
00322   gchar * owner = g_dbus_proxy_get_name_owner (proxy);
00323 
00324   gtk_widget_set_sensitive (widget, (owner != NULL));
00325 
00326   g_free (owner);
00327 }
00328 
00329 static void
00330 service_proxy_ready (GObject *object, GAsyncResult *res, gpointer user_data)
00331 {
00332   GError * error = NULL;
00333 
00334   GDBusProxy * proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
00335 
00336   if (error != NULL) {
00337     g_critical("Could not grab DBus proxy for indicator-datetime-service: %s", error->message);
00338     g_error_free(error);
00339     return;
00340   }
00341 
00342   /* And now, do initial proxy configuration */
00343   g_signal_connect (proxy, "notify::g-name-owner", G_CALLBACK (service_name_owner_changed), user_data);
00344   service_name_owner_changed (proxy, NULL, user_data);
00345 }
00346 
00347 static gboolean
00348 are_spinners_focused (IndicatorDatetimePanel * self)
00349 {
00350   // save_time_id means that we were in focus and haven't finished our save
00351   // yet, so act like we are still focused.
00352   return self->priv->save_time_id ||
00353          gtk_widget_has_focus (self->priv->time_spin) ||
00354          gtk_widget_has_focus (self->priv->date_spin);
00355 }
00356 
00357 static gboolean
00358 save_time (IndicatorDatetimePanel * self)
00359 {
00360   if (self->priv->user_edited_time) {
00361     gdouble current_value = gtk_spin_button_get_value (GTK_SPIN_BUTTON (self->priv->date_spin));
00362     g_dbus_proxy_call (self->priv->proxy, "SetTime", g_variant_new ("(x)", (guint64)current_value),
00363                        G_DBUS_CALL_FLAGS_NONE, -1, NULL, dbus_set_answered, "time");
00364   }
00365   self->priv->user_edited_time = FALSE;
00366   self->priv->save_time_id = 0;
00367   return FALSE;
00368 }
00369 
00370 static gboolean
00371 spin_focus_in (IndicatorDatetimePanel * self)
00372 {
00373   if (self->priv->save_time_id > 0) {
00374     g_source_remove (self->priv->save_time_id);
00375     self->priv->save_time_id = 0;
00376   }
00377   return FALSE;
00378 }
00379 
00380 static gboolean
00381 spin_focus_out (IndicatorDatetimePanel * self)
00382 {
00383   /* We want to only save when both spinners are unfocused.  But it's difficult
00384      to tell who is about to get focus during a focus-out.  So we set an idle
00385      callback to save the time if we don't focus in to another spinner by that
00386      time. */
00387   if (self->priv->save_time_id == 0) {
00388     self->priv->save_time_id = g_idle_add ((GSourceFunc)save_time, self);
00389   }
00390   return FALSE;
00391 }
00392 
00393 static int
00394 input_time_text (GtkWidget * spinner, gdouble * value, IndicatorDatetimePanel * self)
00395 {
00396   gboolean is_time = (gboolean)GPOINTER_TO_INT (g_object_get_data (G_OBJECT (spinner), "is-time"));
00397   const gchar * text = gtk_entry_get_text (GTK_ENTRY (spinner));
00398 
00399   gdouble current_value = gtk_spin_button_get_value (GTK_SPIN_BUTTON (spinner));
00400   *value = current_value;
00401 
00402   GDateTime * now = g_date_time_new_from_unix_local (current_value);
00403   gint year, month, day, hour, minute, second;
00404   year = g_date_time_get_year (now);
00405   month = g_date_time_get_month (now);
00406   day = g_date_time_get_day_of_month (now);
00407   hour = g_date_time_get_hour (now);
00408   minute = g_date_time_get_minute (now);
00409   second = g_date_time_get_second (now);
00410   g_date_time_unref (now);
00411 
00412   /* Parse this string as if it were in the output format */
00413   gint scanned = 0;
00414   gboolean passed = TRUE, skip = FALSE;
00415   if (is_time) {
00416     gint hour_in, minute_in, second_in;
00417 
00418     if (is_locale_12h ()) { // TODO: make this look-at/watch gsettings?
00419       char ampm[51];
00420 
00421       /* coverity[secure_coding] */
00422       scanned = sscanf (text, "%u:%u:%u %50s", &hour_in, &minute_in, &second_in, ampm);
00423       passed = (scanned == 4);
00424 
00425       if (passed) {
00426         const char *pm_str = nl_langinfo (PM_STR);
00427         if (g_ascii_strcasecmp (pm_str, ampm) == 0) {
00428           hour_in += 12;
00429         }
00430       }
00431     } else {
00432       /* coverity[secure_coding] */
00433       scanned = sscanf (text, "%u:%u:%u", &hour_in, &minute_in, &second_in);
00434       passed = (scanned == 3);
00435     }
00436 
00437     if (passed && (hour_in > 23 || minute_in > 59 || second_in > 59)) {
00438       passed = FALSE;
00439     }
00440     if (passed && hour == hour_in && minute == minute_in && second == second_in) {
00441       skip = TRUE; // no change
00442     } else {
00443       hour = hour_in;
00444       minute = minute_in;
00445       second = second_in;
00446     }
00447   }
00448   else {
00449     gint year_in, month_in, day_in;
00450 
00451     /* coverity[secure_coding] */
00452     scanned = sscanf (text, "%u-%u-%u", &year_in, &month_in, &day_in);
00453 
00454     if (scanned != 3 || year_in < 1 || year_in > 9999 ||
00455         month_in < 1 || month_in > 12 || day_in < 1 || day_in > 31) {
00456       passed = FALSE;
00457     }
00458     if (passed && year == year_in && month == month_in && day == day_in) {
00459       skip = TRUE; // no change
00460     } else {
00461       year = year_in;
00462       month = month_in;
00463       day = day_in;
00464     }
00465   }
00466 
00467   if (!passed) {
00468     g_warning ("Could not understand %s", text);
00469     return TRUE;
00470   }
00471 
00472   if (skip) {
00473     return TRUE;
00474   }
00475 
00476   gboolean prev_changing = self->priv->changing_time;
00477   self->priv->changing_time = TRUE;
00478   GDateTime * new_time = g_date_time_new_local (year, month, day, hour, minute, second);
00479   *value = g_date_time_to_unix (new_time);
00480   self->priv->user_edited_time = TRUE;
00481   g_date_time_unref (new_time);
00482   self->priv->changing_time = prev_changing;
00483 
00484   return TRUE;
00485 }
00486 
00487 static gboolean
00488 format_time_text (GtkWidget * spinner, gpointer user_data)
00489 {
00490   gboolean is_time = (gboolean)GPOINTER_TO_INT (g_object_get_data (G_OBJECT (spinner), "is-time"));
00491 
00492   const gchar * format;
00493   if (is_time) {
00494     if (is_locale_12h ()) { // TODO: make this look-at/watch gsettings?
00495       format = "%I:%M:%S %p";
00496     } else {
00497       format = "%H:%M:%S";
00498     }
00499   }
00500   else {
00501     format = "%x";
00502   }
00503 
00504   GDateTime * datetime = g_date_time_new_from_unix_local (gtk_spin_button_get_value (GTK_SPIN_BUTTON (spinner)));
00505   gchar * formatted = g_date_time_format (datetime, format);
00506   gtk_entry_set_text (GTK_ENTRY (spinner), formatted);
00507   g_date_time_unref (datetime);
00508 
00509   return TRUE;
00510 }
00511 
00512 static void
00513 spin_copy_value (GtkSpinButton * spinner, IndicatorDatetimePanel * self)
00514 {
00515   GtkSpinButton * other = NULL;
00516   if (GTK_WIDGET (spinner) == self->priv->date_spin)
00517     other = GTK_SPIN_BUTTON (self->priv->time_spin);
00518   else
00519     other = GTK_SPIN_BUTTON (self->priv->date_spin);
00520 
00521   if (gtk_spin_button_get_value (spinner) != gtk_spin_button_get_value (other)) {
00522     gtk_spin_button_set_value (other, gtk_spin_button_get_value (spinner));
00523   }
00524   if (!self->priv->changing_time) { /* Means user pressed spin buttons */
00525     self->priv->user_edited_time = TRUE;
00526   }
00527 }
00528 
00529 static gboolean
00530 update_spinners (IndicatorDatetimePanel * self)
00531 {
00532   /* Add datetime object to spinner, which will hold the real time value, rather
00533      then using the value of the spinner itself.  And don't update while user is
00534      editing. */
00535   if (!are_spinners_focused (self)) {
00536     gboolean prev_changing = self->priv->changing_time;
00537     self->priv->changing_time = TRUE;
00538     GDateTime * now = g_date_time_new_now_local ();
00539     gtk_spin_button_set_value (GTK_SPIN_BUTTON (self->priv->time_spin),
00540                                (gdouble)g_date_time_to_unix (now));
00541     /* will be copied to other spin button */
00542     g_date_time_unref (now);
00543     self->priv->changing_time = prev_changing;
00544   }
00545   return TRUE;
00546 }
00547 
00548 static void
00549 setup_time_spinners (IndicatorDatetimePanel * self, GtkWidget * time, GtkWidget * date)
00550 {
00551   g_signal_connect (time, "input", G_CALLBACK (input_time_text), self);
00552   g_signal_connect (date, "input", G_CALLBACK (input_time_text), self);
00553 
00554   g_signal_connect (time, "output", G_CALLBACK (format_time_text), date);
00555   g_signal_connect (date, "output", G_CALLBACK (format_time_text), time);
00556 
00557   g_signal_connect_swapped (time, "focus-in-event", G_CALLBACK (spin_focus_in), self);
00558   g_signal_connect_swapped (date, "focus-in-event", G_CALLBACK (spin_focus_in), self);
00559 
00560   g_signal_connect_swapped (time, "focus-out-event", G_CALLBACK (spin_focus_out), self);
00561   g_signal_connect_swapped (date, "focus-out-event", G_CALLBACK (spin_focus_out), self);
00562 
00563   g_signal_connect (time, "value-changed", G_CALLBACK (spin_copy_value), self);
00564   g_signal_connect (date, "value-changed", G_CALLBACK (spin_copy_value), self);
00565 
00566   g_object_set_data (G_OBJECT (time), "is-time", GINT_TO_POINTER (TRUE));
00567   g_object_set_data (G_OBJECT (date), "is-time", GINT_TO_POINTER (FALSE));
00568 
00569   self->priv->time_spin = time;
00570   self->priv->date_spin = date;
00571 
00572   /* 2 seconds is what the indicator itself uses */
00573   guint time_id = g_timeout_add_seconds (2, (GSourceFunc)update_spinners, self);
00574   g_signal_connect_swapped (self->priv->time_spin, "destroy",
00575                             G_CALLBACK (g_source_remove), GINT_TO_POINTER (time_id));
00576   update_spinners (self);
00577 }
00578 
00579 static void
00580 show_locations (IndicatorDatetimePanel * self)
00581 {
00582   if (self->priv->loc_dlg == NULL) {
00583     self->priv->loc_dlg = datetime_setup_locations_dialog (self->priv->tzmap);
00584     GtkWidget * dlg = gtk_widget_get_toplevel (GTK_WIDGET (self));
00585     gtk_window_set_type_hint (GTK_WINDOW(self->priv->loc_dlg), GDK_WINDOW_TYPE_HINT_DIALOG);
00586     gtk_window_set_transient_for (GTK_WINDOW (self->priv->loc_dlg), GTK_WINDOW (dlg));
00587     g_signal_connect (self->priv->loc_dlg, "destroy", G_CALLBACK (gtk_widget_destroyed), &self->priv->loc_dlg);
00588     gtk_widget_show_all (self->priv->loc_dlg);
00589   }
00590   else {
00591     gtk_window_present_with_time (GTK_WINDOW (self->priv->loc_dlg), gtk_get_current_event_time ());
00592   }
00593 }
00594 
00595 static gboolean
00596 timezone_selected (GtkEntryCompletion * widget, GtkTreeModel * model,
00597                    GtkTreeIter * iter, IndicatorDatetimePanel * self)
00598 {
00599   const gchar * name, * zone;
00600 
00601   gtk_tree_model_get (model, iter,
00602                       CC_TIMEZONE_COMPLETION_NAME, &name,
00603                       CC_TIMEZONE_COMPLETION_ZONE, &zone,
00604                       -1);
00605 
00606   if (zone == NULL || zone[0] == 0) {
00607     const gchar * strlon, * strlat;
00608     gdouble lon = 0.0, lat = 0.0;
00609 
00610     gtk_tree_model_get (model, iter,
00611                         CC_TIMEZONE_COMPLETION_LONGITUDE, &strlon,
00612                         CC_TIMEZONE_COMPLETION_LATITUDE, &strlat,
00613                         -1);
00614 
00615     if (strlon != NULL && strlon[0] != 0) {
00616       lon = g_ascii_strtod(strlon, NULL);
00617     }
00618 
00619     if (strlat != NULL && strlat[0] != 0) {
00620       lat = g_ascii_strtod(strlat, NULL);
00621     }
00622 
00623     zone = cc_timezone_map_get_timezone_at_coords (self->priv->tzmap, lon, lat);
00624   }
00625 
00626   GSettings * conf = g_settings_new (SETTINGS_INTERFACE);
00627   gchar * tz_name = g_strdup_printf ("%s %s", zone, name);
00628   g_settings_set_string (conf, SETTINGS_TIMEZONE_NAME_S, tz_name);
00629   g_free (tz_name);
00630   g_object_unref (conf);
00631 
00632   cc_timezone_map_set_timezone (self->priv->tzmap, zone);
00633 
00634   return FALSE; // Do normal action too
00635 }
00636 
00637 static gboolean
00638 entry_focus_out (GtkEntry * entry, GdkEventFocus * event, IndicatorDatetimePanel * self)
00639 {
00640   // If the name left in the entry doesn't match the current timezone name,
00641   // show an error icon.  It's always an error for the user to manually type in
00642   // a timezone.
00643   CcTimezoneLocation * location = cc_timezone_map_get_location (self->priv->tzmap);
00644   if (location == NULL)
00645     return FALSE;
00646 
00647   gchar * zone;
00648   g_object_get (location, "zone", &zone, NULL);
00649 
00650   gchar * name = get_current_zone_name (zone);
00651   gboolean correct = (g_strcmp0 (gtk_entry_get_text (entry), name) == 0);
00652   g_free (name);
00653   g_free (zone);
00654 
00655   gtk_entry_set_icon_from_stock (entry, GTK_ENTRY_ICON_SECONDARY,
00656                                  correct ? NULL : GTK_STOCK_DIALOG_ERROR);
00657   gtk_entry_set_icon_tooltip_text (entry, GTK_ENTRY_ICON_SECONDARY,
00658                                    _("You need to choose a location to change the time zone."));
00659   gtk_entry_set_icon_activatable (entry, GTK_ENTRY_ICON_SECONDARY, FALSE);
00660   return FALSE;
00661 }
00662 
00663 static void
00664 indicator_datetime_panel_init (IndicatorDatetimePanel * self)
00665 {
00666   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
00667                                             INDICATOR_DATETIME_TYPE_PANEL,
00668                                             IndicatorDatetimePanelPrivate);
00669 
00670   GError * error = NULL;
00671 
00672   self->priv->builder = gtk_builder_new ();
00673   gtk_builder_set_translation_domain (self->priv->builder, GETTEXT_PACKAGE);
00674   gtk_builder_add_from_file (self->priv->builder, DATETIME_DIALOG_UI_FILE, &error);
00675   if (error != NULL) {
00676     /* We have to abort, we can't continue without the ui file */
00677     g_error ("Could not load ui file %s: %s", DATETIME_DIALOG_UI_FILE, error->message);
00678     g_error_free (error);
00679     return;
00680   }
00681 
00682   GSettings * conf = g_settings_new (SETTINGS_INTERFACE);
00683 
00684 #define WIG(name) GTK_WIDGET (gtk_builder_get_object (self->priv->builder, name))
00685 
00686   /* Add policykit button */
00687   GtkWidget * polkit_button = gtk_lock_button_new (NULL);
00688   g_object_set (G_OBJECT (polkit_button),
00689                 "text-unlock", _("Unlock to change these settings"),
00690                 "text-lock", _("Lock to prevent further changes"),
00691                 NULL);
00692   GtkWidget * alignment = gtk_alignment_new (0.0, 0.5, 0.0, 0.0);
00693   gtk_container_add (GTK_CONTAINER (alignment), polkit_button);
00694   gtk_box_pack_start (GTK_BOX (WIG ("timeDateBox")), alignment, FALSE, TRUE, 0);
00695 
00696   const gchar * polkit_name = "org.gnome.settingsdaemon.datetimemechanism.configure";
00697   polkit_permission_new (polkit_name, NULL, NULL, polkit_perm_ready, polkit_button);
00698 
00699   /* Add map */
00700   self->priv->tzmap = cc_timezone_map_new ();
00701   gtk_container_add (GTK_CONTAINER (WIG ("mapBox")), GTK_WIDGET (self->priv->tzmap));
00702   /* Fufill the CC by Attribution license requirements for the Geonames lookup */
00703   cc_timezone_map_set_watermark (self->priv->tzmap, "Geonames.org");
00704 
00705   /* And completion entry */
00706   self->priv->completion = cc_timezone_completion_new ();
00707   cc_timezone_completion_watch_entry (self->priv->completion, GTK_ENTRY (WIG ("timezoneEntry")));
00708   g_signal_connect (self->priv->completion, "match-selected", G_CALLBACK (timezone_selected), self);
00709   g_signal_connect (WIG ("timezoneEntry"), "focus-out-event", G_CALLBACK (entry_focus_out), self);
00710 
00711   /* Set up settings bindings */
00712   g_settings_bind (conf, SETTINGS_SHOW_CLOCK_S, WIG ("showClockCheck"),
00713                    "active", G_SETTINGS_BIND_DEFAULT);
00714   g_settings_bind (conf, SETTINGS_SHOW_DAY_S, WIG ("showWeekdayCheck"),
00715                    "active", G_SETTINGS_BIND_DEFAULT);
00716   g_settings_bind (conf, SETTINGS_SHOW_DATE_S, WIG ("showDateTimeCheck"),
00717                    "active", G_SETTINGS_BIND_DEFAULT);
00718   g_settings_bind (conf, SETTINGS_SHOW_SECONDS_S, WIG ("showSecondsCheck"),
00719                    "active", G_SETTINGS_BIND_DEFAULT);
00720   g_settings_bind_with_mapping (conf, SETTINGS_TIME_FORMAT_S,
00721                                 WIG ("show12HourRadio"), "active",
00722                                 G_SETTINGS_BIND_DEFAULT,
00723                                 bind_hours_get, bind_hours_set,
00724                                 GINT_TO_POINTER(TRUE), NULL);
00725   g_settings_bind_with_mapping (conf, SETTINGS_TIME_FORMAT_S,
00726                                 WIG ("show24HourRadio"), "active",
00727                                 G_SETTINGS_BIND_DEFAULT,
00728                                 bind_hours_get, bind_hours_set,
00729                                 GINT_TO_POINTER(FALSE), NULL);
00730   g_settings_bind (conf, SETTINGS_SHOW_CALENDAR_S, WIG ("showCalendarCheck"),
00731                    "active", G_SETTINGS_BIND_DEFAULT);
00732   g_settings_bind (conf, SETTINGS_SHOW_WEEK_NUMBERS_S, WIG ("includeWeekNumbersCheck"),
00733                    "active", G_SETTINGS_BIND_DEFAULT);
00734   g_settings_bind (conf, SETTINGS_SHOW_EVENTS_S, WIG ("showEventsCheck"),
00735                    "active", G_SETTINGS_BIND_DEFAULT);
00736   g_settings_bind (conf, SETTINGS_SHOW_DETECTED_S, WIG ("showDetectedCheck"),
00737                    "active", G_SETTINGS_BIND_DEFAULT);
00738   g_settings_bind (conf, SETTINGS_SHOW_LOCATIONS_S, WIG ("showLocationsCheck"),
00739                    "active", G_SETTINGS_BIND_DEFAULT);
00740 
00741   /* Set up sensitivities */
00742   add_widget_dependency (WIG ("showCalendarCheck"), WIG ("calendarOptions"));
00743   add_widget_dependency (WIG ("showClockCheck"), WIG ("clockOptions"));
00744   add_widget_dependency (WIG ("showLocationsCheck"), WIG ("locationsButton"));
00745   add_widget_dependency (WIG ("manualTimeRadio"), WIG ("manualOptions"));
00746   add_polkit_dependency (polkit_button, WIG ("timeDateOptions"));
00747 
00748   /* Hacky proxy test for whether evolution-data-server is installed */
00749   gchar * evo_path = g_find_program_in_path ("evolution");
00750   gtk_widget_set_sensitive (WIG ("showEventsCheck"), (evo_path != NULL));
00751   g_free (evo_path);
00752 
00753   setup_time_spinners (self, WIG ("timeSpinner"), WIG ("dateSpinner"));
00754 
00755   GtkWidget * panel = WIG ("timeDatePanel");
00756   self->priv->auto_radio = WIG ("automaticTimeRadio");
00757   self->priv->tz_entry = WIG ("timezoneEntry");
00758 
00759   g_signal_connect_swapped (WIG ("locationsButton"), "clicked", G_CALLBACK (show_locations), self);
00760 
00761   /* Grab proxy for settings daemon */
00762   g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL,
00763                             "org.gnome.SettingsDaemon.DateTimeMechanism",
00764                             "/",                            
00765                             "org.gnome.SettingsDaemon.DateTimeMechanism",
00766                             NULL, (GAsyncReadyCallback)proxy_ready, self);
00767 
00768   /* Grab proxy for datetime service, to see if it's running.  It would
00769      actually be more ideal to see if the indicator module itself is running,
00770      but that doesn't yet claim a name on the bus.  Presumably the service
00771      would have been started by any such indicator, so this will at least tell
00772      us if there *was* a datetime module run this session. */
00773   g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, NULL,
00774                             SERVICE_NAME, SERVICE_OBJ, SERVICE_IFACE,
00775                             NULL, (GAsyncReadyCallback)service_proxy_ready,
00776                             WIG ("showClockCheck"));
00777 
00778 #undef WIG
00779 
00780   g_object_unref (conf);
00781 
00782   gtk_widget_show_all (panel);
00783   gtk_container_add (GTK_CONTAINER (self), panel);
00784 }
00785 
00786 static void
00787 indicator_datetime_panel_dispose (GObject * object)
00788 {
00789   IndicatorDatetimePanel * self = (IndicatorDatetimePanel *) object;
00790   IndicatorDatetimePanelPrivate * priv = self->priv;
00791 
00792   g_clear_object (&priv->builder);
00793   g_clear_object (&priv->proxy);
00794 
00795   if (priv->tz_query_cancel != NULL) {
00796     g_cancellable_cancel (priv->tz_query_cancel);
00797     g_clear_object (&priv->tz_query_cancel);
00798   }
00799 
00800   if (priv->ntp_query_cancel != NULL) {
00801     g_cancellable_cancel (priv->ntp_query_cancel);
00802     g_clear_object (&priv->ntp_query_cancel);
00803   }
00804 
00805   if (priv->loc_dlg) {
00806     gtk_widget_destroy (priv->loc_dlg);
00807     priv->loc_dlg = NULL;
00808   }
00809 
00810   if (priv->save_time_id) {
00811     g_source_remove (priv->save_time_id);
00812     priv->save_time_id = 0;
00813   }
00814 
00815   if (priv->completion) {
00816     cc_timezone_completion_watch_entry (priv->completion, NULL);
00817     g_clear_object (&priv->completion);
00818   }
00819 
00820   if (priv->tz_entry) {
00821     gtk_widget_destroy (priv->tz_entry);
00822     priv->tz_entry = NULL;
00823   }
00824 
00825   if (priv->time_spin) {
00826     gtk_widget_destroy (priv->time_spin);
00827     priv->time_spin = NULL;
00828   }
00829 
00830   if (priv->date_spin) {
00831     gtk_widget_destroy (priv->date_spin);
00832     priv->date_spin = NULL;
00833   }
00834 
00835   G_OBJECT_CLASS (indicator_datetime_panel_parent_class)->dispose (object);
00836 }
00837 
00838 static void
00839 indicator_datetime_panel_class_finalize (IndicatorDatetimePanelClass *klass)
00840 {
00841 }
00842 
00843 static void
00844 indicator_datetime_panel_class_init (IndicatorDatetimePanelClass *klass)
00845 {
00846   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
00847 
00848   g_type_class_add_private (klass, sizeof (IndicatorDatetimePanelPrivate));
00849 
00850   gobject_class->dispose = indicator_datetime_panel_dispose;
00851 }
00852 
00853 void
00854 g_io_module_load (GIOModule *module)
00855 {
00856   bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
00857   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
00858 
00859   indicator_datetime_panel_register_type (G_TYPE_MODULE (module));
00860   g_io_extension_point_implement (CC_SHELL_PANEL_EXTENSION_POINT,
00861                                   INDICATOR_DATETIME_TYPE_PANEL,
00862                                   "indicator-datetime", 0);
00863 }
00864 
00865 void
00866 g_io_module_unload (GIOModule *module)
00867 {
00868 }