Back to index

indicator-datetime  12.10.0
indicator-datetime.c
Go to the documentation of this file.
00001 /*
00002 An indicator to time and date related information in the menubar.
00003 
00004 Copyright 2010 Canonical Ltd.
00005 
00006 Authors:
00007     Ted Gould <ted@canonical.com>
00008 
00009 This program is free software: you can redistribute it and/or modify it 
00010 under the terms of the GNU General Public License version 3, as published 
00011 by the Free Software Foundation.
00012 
00013 This program is distributed in the hope that it will be useful, but 
00014 WITHOUT ANY WARRANTY; without even the implied warranties of 
00015 MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR 
00016 PURPOSE.  See the GNU General Public License for more details.
00017 
00018 You should have received a copy of the GNU General Public License along 
00019 with this program.  If not, see <http://www.gnu.org/licenses/>.
00020 */
00021 
00022 #ifdef HAVE_CONFIG_H
00023 #include "config.h"
00024 #endif
00025 
00026 #include <locale.h>
00027 #include <langinfo.h>
00028 #include <string.h>
00029 #include <time.h>
00030 
00031 /* GStuff */
00032 #include <glib.h>
00033 #include <glib/gprintf.h>
00034 #include <glib-object.h>
00035 #include <glib/gi18n-lib.h>
00036 #include <gio/gio.h>
00037 
00038 /* Indicator Stuff */
00039 #include <libindicator/indicator.h>
00040 #include <libindicator/indicator-object.h>
00041 #include <libindicator/indicator-service-manager.h>
00042 
00043 /* DBusMenu */
00044 #include <libdbusmenu-gtk/menu.h>
00045 #include <libido/libido.h>
00046 #include <libdbusmenu-gtk/menuitem.h>
00047 
00048 #include "utils.h"
00049 #include "dbus-shared.h"
00050 #include "settings-shared.h"
00051 
00052 
00053 #define INDICATOR_DATETIME_TYPE            (indicator_datetime_get_type ())
00054 #define INDICATOR_DATETIME(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), INDICATOR_DATETIME_TYPE, IndicatorDatetime))
00055 #define INDICATOR_DATETIME_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), INDICATOR_DATETIME_TYPE, IndicatorDatetimeClass))
00056 #define IS_INDICATOR_DATETIME(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), INDICATOR_DATETIME_TYPE))
00057 #define IS_INDICATOR_DATETIME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), INDICATOR_DATETIME_TYPE))
00058 #define INDICATOR_DATETIME_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), INDICATOR_DATETIME_TYPE, IndicatorDatetimeClass))
00059 
00060 typedef struct _IndicatorDatetime         IndicatorDatetime;
00061 typedef struct _IndicatorDatetimeClass    IndicatorDatetimeClass;
00062 typedef struct _IndicatorDatetimePrivate  IndicatorDatetimePrivate;
00063 
00064 struct _IndicatorDatetimeClass {
00065        IndicatorObjectClass parent_class;
00066 };
00067 
00068 struct _IndicatorDatetime {
00069        IndicatorObject parent;
00070        IndicatorDatetimePrivate * priv;
00071 };
00072 
00073 struct _IndicatorDatetimePrivate {
00074        GtkLabel * label;
00075        guint timer;
00076 
00077        gchar * time_string;
00078 
00079        gboolean show_clock;
00080        gint time_mode;
00081        gboolean show_seconds;
00082        gboolean show_date;
00083        gboolean show_day;
00084        gchar * custom_string;
00085        gboolean custom_show_seconds;
00086 
00087        gboolean show_week_numbers;
00088        gboolean show_calendar;
00089        gint week_start;
00090        
00091        guint idle_measure;
00092        gint  max_width;
00093 
00094        IndicatorServiceManager * sm;
00095        DbusmenuGtkMenu * menu;
00096 
00097        GCancellable * service_proxy_cancel;
00098        GDBusProxy * service_proxy;
00099        IdoCalendarMenuItem *ido_calendar;
00100 
00101        GList * timezone_items;
00102 
00103        GSettings * settings;
00104 
00105        GtkSizeGroup * indicator_right_group;
00106 };
00107 
00108 /* Enum for the properties so that they can be quickly
00109    found and looked up. */
00110 enum {
00111        PROP_0,
00112        PROP_SHOW_CLOCK,
00113        PROP_TIME_FORMAT,
00114        PROP_SHOW_SECONDS,
00115        PROP_SHOW_DAY,
00116        PROP_SHOW_DATE,
00117        PROP_CUSTOM_TIME_FORMAT,
00118        PROP_SHOW_WEEK_NUMBERS,
00119        PROP_SHOW_CALENDAR
00120 };
00121 
00122 typedef struct _indicator_item_t indicator_item_t;
00123 struct _indicator_item_t {
00124        IndicatorDatetime * self;
00125        DbusmenuMenuitem * mi;
00126        GtkWidget * gmi;
00127        GtkWidget * icon;
00128        GtkWidget * label;
00129        GtkWidget * right;
00130 };
00131 
00132 #define PROP_SHOW_CLOCK_S               "show-clock"
00133 #define PROP_TIME_FORMAT_S              "time-format"
00134 #define PROP_SHOW_SECONDS_S             "show-seconds"
00135 #define PROP_SHOW_DAY_S                 "show-day"
00136 #define PROP_SHOW_DATE_S                "show-date"
00137 #define PROP_CUSTOM_TIME_FORMAT_S       "custom-time-format"
00138 #define PROP_SHOW_WEEK_NUMBERS_S        "show-week-numbers"
00139 #define PROP_SHOW_CALENDAR_S            "show-calendar"
00140 
00141 enum {
00142        STRFTIME_MASK_NONE    = 0,      /* Hours or minutes as we always test those */
00143        STRFTIME_MASK_SECONDS = 1 << 0, /* Seconds count */
00144        STRFTIME_MASK_AMPM    = 1 << 1, /* AM/PM counts */
00145        STRFTIME_MASK_WEEK    = 1 << 2, /* Day of the week maters (Sat, Sun, etc.) */
00146        STRFTIME_MASK_DAY     = 1 << 3, /* Day of the month counts (Feb 1st) */
00147        STRFTIME_MASK_MONTH   = 1 << 4, /* Which month matters */
00148        STRFTIME_MASK_YEAR    = 1 << 5, /* Which year matters */
00149        /* Last entry, combines all previous */
00150        STRFTIME_MASK_ALL     = (STRFTIME_MASK_SECONDS | STRFTIME_MASK_AMPM | STRFTIME_MASK_WEEK | STRFTIME_MASK_DAY | STRFTIME_MASK_MONTH | STRFTIME_MASK_YEAR)
00151 };
00152 
00153 GType indicator_datetime_get_type (void) G_GNUC_CONST;
00154 
00155 static void indicator_datetime_class_init (IndicatorDatetimeClass *klass);
00156 static void indicator_datetime_init       (IndicatorDatetime *self);
00157 static void set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec);
00158 static void get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec);
00159 static void indicator_datetime_dispose    (GObject *object);
00160 static void indicator_datetime_finalize   (GObject *object);
00161 static GtkLabel * get_label               (IndicatorObject * io);
00162 static GtkMenu *  get_menu                (IndicatorObject * io);
00163 static const gchar * get_accessible_desc  (IndicatorObject * io);
00164 static const gchar * get_name_hint        (IndicatorObject * io);
00165 static gboolean bind_enum_get             (GValue * value, GVariant * variant, gpointer user_data);
00166 static gchar * generate_format_string_now (IndicatorDatetime * self);
00167 static void update_label                  (IndicatorDatetime * io, GDateTime ** datetime);
00168 static void guess_label_size              (IndicatorDatetime * self);
00169 static void setup_timer                   (IndicatorDatetime * self, GDateTime * datetime);
00170 static void update_time                   (IndicatorDatetime * self);
00171 static void receive_signal                (GDBusProxy * proxy, gchar * sender_name, gchar * signal_name, GVariant * parameters, gpointer user_data);
00172 static void service_proxy_cb (GObject * object, GAsyncResult * res, gpointer user_data);
00173 static gint generate_strftime_bitmask     (const char *time_str);
00174 static void timezone_update_labels        (indicator_item_t * mi_data);
00175 static gboolean new_calendar_item         (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient   * client, gpointer user_data);
00176 static gboolean new_appointment_item      (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client, gpointer user_data);
00177 static gboolean new_timezone_item         (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient   * client, gpointer user_data);
00178 
00179 /* Indicator Module Config */
00180 INDICATOR_SET_VERSION
00181 INDICATOR_SET_TYPE(INDICATOR_DATETIME_TYPE)
00182 
00183 G_DEFINE_TYPE (IndicatorDatetime, indicator_datetime, INDICATOR_OBJECT_TYPE);
00184 
00185 static void
00186 indicator_datetime_class_init (IndicatorDatetimeClass *klass)
00187 {
00188        GObjectClass *object_class = G_OBJECT_CLASS (klass);
00189 
00190        g_type_class_add_private (klass, sizeof (IndicatorDatetimePrivate));
00191 
00192        object_class->dispose = indicator_datetime_dispose;
00193        object_class->finalize = indicator_datetime_finalize;
00194 
00195        object_class->set_property = set_property;
00196        object_class->get_property = get_property;
00197 
00198        IndicatorObjectClass * io_class = INDICATOR_OBJECT_CLASS(klass);
00199 
00200        io_class->get_label = get_label;
00201        io_class->get_menu  = get_menu;
00202        io_class->get_accessible_desc = get_accessible_desc;
00203        io_class->get_name_hint = get_name_hint;
00204 
00205        g_object_class_install_property (object_class,
00206                                         PROP_SHOW_CLOCK,
00207                                         g_param_spec_boolean(PROP_SHOW_CLOCK_S,
00208                                                              "Whether to show the clock in the menu bar.",
00209                                                              "Shows indicator-datetime in the shell's menu bar.",
00210                                                              TRUE, /* default */
00211                                                              G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
00212        g_object_class_install_property (object_class,
00213                                         PROP_TIME_FORMAT,
00214                                         g_param_spec_int(PROP_TIME_FORMAT_S,
00215                                                          "A choice of which format should be used on the panel",
00216                                                          "Chooses between letting the locale choose the time, 12-hour time, 24-time or using the custom string passed to g_date_time_format().",
00217                                                          SETTINGS_TIME_LOCALE, /* min */
00218                                                          SETTINGS_TIME_CUSTOM, /* max */
00219                                                          SETTINGS_TIME_LOCALE, /* default */
00220                                                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
00221        g_object_class_install_property (object_class,
00222                                         PROP_SHOW_SECONDS,
00223                                         g_param_spec_boolean(PROP_SHOW_SECONDS_S,
00224                                                              "Whether to show seconds in the indicator.",
00225                                                              "Shows seconds along with the time in the indicator.  Also effects refresh interval.",
00226                                                              FALSE, /* default */
00227                                                              G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
00228        g_object_class_install_property (object_class,
00229                                         PROP_SHOW_DAY,
00230                                         g_param_spec_boolean(PROP_SHOW_DAY_S,
00231                                                              "Whether to show the day of the week in the indicator.",
00232                                                              "Shows the day of the week along with the time in the indicator.",
00233                                                              FALSE, /* default */
00234                                                              G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
00235        g_object_class_install_property (object_class,
00236                                         PROP_SHOW_DATE,
00237                                         g_param_spec_boolean(PROP_SHOW_DATE_S,
00238                                                              "Whether to show the day and month in the indicator.",
00239                                                              "Shows the day and month along with the time in the indicator.",
00240                                                              FALSE, /* default */
00241                                                              G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
00242        g_object_class_install_property (object_class,
00243                                         PROP_CUSTOM_TIME_FORMAT,
00244                                         g_param_spec_string(PROP_CUSTOM_TIME_FORMAT_S,
00245                                                             "The format that is used to show the time on the panel.",
00246                                                             "A format string in the form used to pass to g_date_time_format() to make a string for displaying on the panel.",
00247                                                             DEFAULT_TIME_FORMAT,
00248                                                             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
00249 
00250        g_object_class_install_property (object_class,
00251                                         PROP_SHOW_WEEK_NUMBERS,
00252                                         g_param_spec_boolean(PROP_SHOW_WEEK_NUMBERS_S,
00253                                                              "Whether to show the week numbers in the calendar.",
00254                                                              "Shows the week numbers in the monthly calendar in indicator-datetime's menu.",
00255                                                              FALSE, /* default */
00256                                                              G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
00257        g_object_class_install_property (object_class,
00258                                         PROP_SHOW_CALENDAR,
00259                                         g_param_spec_boolean(PROP_SHOW_CALENDAR_S,
00260                                                              "Whether to show the calendar.",
00261                                                              "Shows the monthly calendar in indicator-datetime's menu.",
00262                                                              TRUE, /* default */
00263                                                              G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
00264        return;
00265 }
00266 
00267 static void
00268 menu_visible_notfy_cb(GtkWidget * menu, G_GNUC_UNUSED GParamSpec *pspec, gpointer user_data)
00269 {
00270        IndicatorDatetime * self = INDICATOR_DATETIME(user_data);
00271        GDateTime *datetime;
00272        gint y, m, d;
00273 
00274        g_debug("notify visible signal received");
00275 
00276        datetime = g_date_time_new_now_local ();
00277        g_date_time_get_ymd (datetime, &y, &m, &d);
00278        g_date_time_unref (datetime);
00279 
00280        // Set the calendar to todays date
00281        ido_calendar_menu_item_set_date (self->priv->ido_calendar, y, m-1, d);
00282 
00283        // Make sure the day-selected signal is sent so the menu updates - may duplicate
00284        /*GVariant *variant = g_variant_new_uint32((guint)curtime);
00285        guint timestamp = (guint)time(NULL);
00286        dbusmenu_menuitem_handle_event(DBUSMENU_MENUITEM(self->priv->ido_calendar), "day-selected", variant, timestamp);*/
00287 }
00288 
00289 static void
00290 indicator_datetime_init (IndicatorDatetime *self)
00291 {
00292        self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
00293                                                   INDICATOR_DATETIME_TYPE,
00294                                                   IndicatorDatetimePrivate);
00295 
00296        self->priv->label = NULL;
00297        self->priv->timer = 0;
00298 
00299        self->priv->idle_measure = 0;
00300        self->priv->max_width = 0;
00301 
00302        self->priv->show_clock = TRUE;
00303        self->priv->time_mode = SETTINGS_TIME_LOCALE;
00304        self->priv->show_seconds = FALSE;
00305        self->priv->show_date = FALSE;
00306        self->priv->show_day = FALSE;
00307        self->priv->custom_string = g_strdup(DEFAULT_TIME_FORMAT);
00308        self->priv->custom_show_seconds = FALSE;
00309 
00310        self->priv->time_string = generate_format_string_now(self);
00311 
00312        self->priv->service_proxy = NULL;
00313 
00314        self->priv->sm = NULL;
00315        self->priv->menu = NULL;
00316 
00317        self->priv->settings = g_settings_new(SETTINGS_INTERFACE);
00318        if (self->priv->settings != NULL) {
00319               g_settings_bind(self->priv->settings,
00320                               SETTINGS_SHOW_CLOCK_S,
00321                               self,
00322                               PROP_SHOW_CLOCK_S,
00323                               G_SETTINGS_BIND_GET);
00324               g_settings_bind_with_mapping(self->priv->settings,
00325                               SETTINGS_TIME_FORMAT_S,
00326                               self,
00327                               PROP_TIME_FORMAT_S,
00328                               G_SETTINGS_BIND_GET,
00329                               bind_enum_get,
00330                               NULL, NULL, NULL); /* set mapping, userdata and destroy func */
00331               g_settings_bind(self->priv->settings,
00332                               SETTINGS_SHOW_SECONDS_S,
00333                               self,
00334                               PROP_SHOW_SECONDS_S,
00335                               G_SETTINGS_BIND_GET);
00336               g_settings_bind(self->priv->settings,
00337                               SETTINGS_SHOW_DAY_S,
00338                               self,
00339                               PROP_SHOW_DAY_S,
00340                               G_SETTINGS_BIND_GET);
00341               g_settings_bind(self->priv->settings,
00342                               SETTINGS_SHOW_DATE_S,
00343                               self,
00344                               PROP_SHOW_DATE_S,
00345                               G_SETTINGS_BIND_GET);
00346               g_settings_bind(self->priv->settings,
00347                               SETTINGS_CUSTOM_TIME_FORMAT_S,
00348                               self,
00349                               PROP_CUSTOM_TIME_FORMAT_S,
00350                               G_SETTINGS_BIND_GET);
00351               g_settings_bind(self->priv->settings,
00352                               SETTINGS_SHOW_WEEK_NUMBERS_S,
00353                               self,
00354                               PROP_SHOW_WEEK_NUMBERS_S,
00355                               G_SETTINGS_BIND_GET);
00356               g_settings_bind(self->priv->settings,
00357                               SETTINGS_SHOW_CALENDAR_S,
00358                               self,
00359                               PROP_SHOW_CALENDAR_S,
00360                               G_SETTINGS_BIND_GET);
00361        } else {
00362               g_warning("Unable to get settings for '" SETTINGS_INTERFACE "'");
00363        }
00364 
00365        self->priv->sm = indicator_service_manager_new_version(SERVICE_NAME, SERVICE_VERSION);
00366        self->priv->indicator_right_group = GTK_SIZE_GROUP(gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL));
00367 
00368        self->priv->menu = dbusmenu_gtkmenu_new(SERVICE_NAME, MENU_OBJ);
00369 
00370        g_signal_connect(self->priv->menu, "notify::visible", G_CALLBACK(menu_visible_notfy_cb), self);
00371        
00372        DbusmenuGtkClient *client = dbusmenu_gtkmenu_get_client(self->priv->menu);
00373        dbusmenu_client_add_type_handler_full(DBUSMENU_CLIENT(client), DBUSMENU_CALENDAR_MENUITEM_TYPE, new_calendar_item, self, NULL);
00374        dbusmenu_client_add_type_handler_full(DBUSMENU_CLIENT(client), APPOINTMENT_MENUITEM_TYPE, new_appointment_item, self, NULL);
00375        dbusmenu_client_add_type_handler_full(DBUSMENU_CLIENT(client), TIMEZONE_MENUITEM_TYPE, new_timezone_item, self, NULL);
00376 
00377        self->priv->service_proxy_cancel = g_cancellable_new();
00378 
00379        g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION,
00380                                 G_DBUS_PROXY_FLAGS_NONE,
00381                                 NULL,
00382                                 SERVICE_NAME,
00383                                 SERVICE_OBJ,
00384                                 SERVICE_IFACE,
00385                                 self->priv->service_proxy_cancel,
00386                                 service_proxy_cb,
00387                                   self);
00388 
00389        return;
00390 }
00391 
00392 /* Callback from trying to create the proxy for the serivce, this
00393    could include starting the service.  Sometime it'll fail and
00394    we'll try to start that dang service again! */
00395 static void
00396 service_proxy_cb (GObject * object, GAsyncResult * res, gpointer user_data)
00397 {
00398        GError * error = NULL;
00399 
00400        IndicatorDatetime * self = INDICATOR_DATETIME(user_data);
00401        g_return_if_fail(self != NULL);
00402        IndicatorDatetimePrivate * priv = self->priv;
00403 
00404        GDBusProxy * proxy = g_dbus_proxy_new_for_bus_finish(res, &error);
00405 
00406        g_clear_object (&priv->service_proxy_cancel);
00407 
00408        if (error != NULL) {
00409               g_warning("Could not grab DBus proxy for %s: %s", SERVICE_NAME, error->message);
00410               g_error_free(error);
00411               return;
00412        }
00413 
00414        /* Okay, we're good to grab the proxy at this point, we're
00415        sure that it's ours. */
00416        priv->service_proxy = proxy;
00417 
00418        g_signal_connect(proxy, "g-signal", G_CALLBACK(receive_signal), self);
00419 
00420        return;
00421 }
00422 
00423 static void
00424 indicator_datetime_dispose (GObject *object)
00425 {
00426        IndicatorDatetime * self = INDICATOR_DATETIME(object);
00427        IndicatorDatetimePrivate * priv = self->priv;
00428 
00429        if (priv->timer != 0) {
00430               g_source_remove(priv->timer);
00431               priv->timer = 0;
00432        }
00433 
00434        if (priv->idle_measure != 0) {
00435               g_source_remove(priv->idle_measure);
00436               priv->idle_measure = 0;
00437        }
00438 
00439        g_clear_object (&priv->label);
00440        g_clear_object (&priv->menu);
00441        g_clear_object (&priv->sm);
00442        g_clear_object (&priv->settings);
00443        g_clear_object (&priv->service_proxy);
00444        g_clear_object (&priv->indicator_right_group);
00445        g_clear_object (&priv->ido_calendar);
00446        g_clear_object (&priv->service_proxy_cancel);
00447 
00448        G_OBJECT_CLASS (indicator_datetime_parent_class)->dispose (object);
00449        return;
00450 }
00451 
00452 static void
00453 indicator_datetime_finalize (GObject *object)
00454 {
00455        IndicatorDatetime * self = INDICATOR_DATETIME(object);
00456 
00457        if (self->priv->time_string != NULL) {
00458               g_free(self->priv->time_string);
00459               self->priv->time_string = NULL;
00460        }
00461 
00462        if (self->priv->custom_string != NULL) {
00463               g_free(self->priv->custom_string);
00464               self->priv->custom_string = NULL;
00465        }
00466 
00467        G_OBJECT_CLASS (indicator_datetime_parent_class)->finalize (object);
00468        return;
00469 }
00470 
00471 /* Turns a string GVariant into an int value */
00472 static gboolean
00473 bind_enum_get (GValue * value, GVariant * variant, gpointer user_data)
00474 {
00475        const gchar * str = g_variant_get_string(variant, NULL);
00476        gint output = 0;
00477 
00478        if (g_strcmp0(str, "locale-default") == 0) {
00479               output = SETTINGS_TIME_LOCALE;
00480        } else if (g_strcmp0(str, "12-hour") == 0) {
00481               output = SETTINGS_TIME_12_HOUR;
00482        } else if (g_strcmp0(str, "24-hour") == 0) {
00483               output = SETTINGS_TIME_24_HOUR;
00484        } else if (g_strcmp0(str, "custom") == 0) {
00485               output = SETTINGS_TIME_CUSTOM;
00486        } else {
00487               return FALSE;
00488        }
00489 
00490        g_value_set_int(value, output);
00491        return TRUE;
00492 }
00493 
00494 static void
00495 timezone_update_all_labels (IndicatorDatetime * self)
00496 {
00497        IndicatorDatetimePrivate *priv = self->priv;
00498 
00499        g_list_foreach(priv->timezone_items, (GFunc)timezone_update_labels, NULL);
00500 }
00501 
00502 /* Sets a property on the object */
00503 static void
00504 set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec)
00505 {
00506        IndicatorDatetime * self = INDICATOR_DATETIME(object);
00507        gboolean update = FALSE;
00508 
00509        switch(prop_id) {
00510        case PROP_SHOW_CLOCK: {
00511               if (g_value_get_boolean(value) != self->priv->show_clock) {
00512                      self->priv->show_clock = g_value_get_boolean(value);
00513                      if (self->priv->label != NULL) {
00514                             gtk_widget_set_visible (GTK_WIDGET (self->priv->label), self->priv->show_clock);
00515                      }
00516               }
00517               break;
00518        }
00519        case PROP_TIME_FORMAT: {
00520               gint newval = g_value_get_int(value);
00521               if (newval != self->priv->time_mode) {
00522                      update = TRUE;
00523                      self->priv->time_mode = newval;
00524                      setup_timer(self, NULL);                  
00525               }
00526               break;
00527        }
00528        case PROP_SHOW_SECONDS: {
00529               if (g_value_get_boolean(value) != self->priv->show_seconds) {
00530                      self->priv->show_seconds = !self->priv->show_seconds;
00531                      if (self->priv->time_mode != SETTINGS_TIME_CUSTOM) {
00532                             update = TRUE;
00533                             setup_timer(self, NULL);
00534                      }
00535               }
00536               break;
00537        }
00538        case PROP_SHOW_DAY: {
00539               if (g_value_get_boolean(value) != self->priv->show_day) {
00540                      self->priv->show_day = !self->priv->show_day;
00541                      if (self->priv->time_mode != SETTINGS_TIME_CUSTOM) {
00542                             update = TRUE;
00543                      }
00544               }
00545               break;
00546        }
00547        case PROP_SHOW_DATE: {
00548               if (g_value_get_boolean(value) != self->priv->show_date) {
00549                      self->priv->show_date = !self->priv->show_date;
00550                      if (self->priv->time_mode != SETTINGS_TIME_CUSTOM) {
00551                             update = TRUE;
00552                      }
00553               }
00554               break;
00555        }
00556        case PROP_CUSTOM_TIME_FORMAT: {
00557               const gchar * newstr = g_value_get_string(value);
00558               if (g_strcmp0(newstr, self->priv->custom_string) != 0) {
00559                      if (self->priv->custom_string != NULL) {
00560                             g_free(self->priv->custom_string);
00561                             self->priv->custom_string = NULL;
00562                      }
00563                      self->priv->custom_string = g_strdup(newstr);
00564                      gint time_mask = generate_strftime_bitmask(newstr);
00565                      self->priv->custom_show_seconds = (time_mask & STRFTIME_MASK_SECONDS);
00566                      if (self->priv->time_mode == SETTINGS_TIME_CUSTOM) {
00567                             update = TRUE;
00568                             setup_timer(self, NULL);
00569                      }
00570               }
00571               break;
00572        }
00573        case PROP_SHOW_WEEK_NUMBERS: {
00574               if (g_value_get_boolean(value) != self->priv->show_week_numbers) {
00575                      GtkCalendarDisplayOptions flags = ido_calendar_menu_item_get_display_options (self->priv->ido_calendar);
00576                      if (g_value_get_boolean(value) == TRUE)
00577                             flags |= GTK_CALENDAR_SHOW_WEEK_NUMBERS;
00578                      else
00579                             flags &= ~GTK_CALENDAR_SHOW_WEEK_NUMBERS;
00580                      ido_calendar_menu_item_set_display_options (self->priv->ido_calendar, flags);
00581                      self->priv->show_week_numbers = g_value_get_boolean(value);
00582               }
00583               break;
00584        }
00585        case PROP_SHOW_CALENDAR: {
00586               if (g_value_get_boolean(value) != self->priv->show_calendar) {
00587                      self->priv->show_calendar = g_value_get_boolean(value);
00588                      if (self->priv->ido_calendar != NULL) {
00589                             gtk_widget_set_visible (GTK_WIDGET (self->priv->ido_calendar), self->priv->show_calendar);
00590                      }
00591               }
00592               break;
00593        } 
00594        default: {
00595               G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
00596               return;
00597        }
00598        }
00599 
00600        if (!update) {
00601               return;
00602        }
00603 
00604        /* Get the new format string */
00605        gchar * newformat = generate_format_string_now(self);
00606 
00607        /* check to ensure the format really changed */
00608        if (g_strcmp0(self->priv->time_string, newformat) == 0) {
00609               g_free(newformat);
00610               return;
00611        }
00612 
00613        /* Okay now process the change */
00614        if (self->priv->time_string != NULL) {
00615               g_free(self->priv->time_string);
00616               self->priv->time_string = NULL;
00617        }
00618        self->priv->time_string = newformat;
00619 
00620        /* And update everything */
00621        update_label(self, NULL);
00622        timezone_update_all_labels(self);
00623        guess_label_size(self);
00624 
00625        return;
00626 }
00627 
00628 /* Gets a property from the object */
00629 static void
00630 get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec)
00631 {
00632        IndicatorDatetime * self = INDICATOR_DATETIME(object);
00633 
00634        switch(prop_id) {
00635        case PROP_SHOW_CLOCK:
00636               g_value_set_boolean(value, self->priv->show_clock);
00637               break;
00638        case PROP_TIME_FORMAT:
00639               g_value_set_int(value, self->priv->time_mode);
00640               break;
00641        case PROP_SHOW_SECONDS:
00642               g_value_set_boolean(value, self->priv->show_seconds);
00643               break;
00644        case PROP_SHOW_DAY:
00645               g_value_set_boolean(value, self->priv->show_day);
00646               break;
00647        case PROP_SHOW_DATE:
00648               g_value_set_boolean(value, self->priv->show_date);
00649               break;
00650        case PROP_CUSTOM_TIME_FORMAT:
00651               g_value_set_string(value, self->priv->custom_string);
00652               break;
00653        case PROP_SHOW_WEEK_NUMBERS:
00654               g_value_set_boolean(value, self->priv->show_week_numbers);
00655               break;
00656        case PROP_SHOW_CALENDAR:
00657               g_value_set_boolean(value, self->priv->show_calendar);
00658               break;
00659        default:
00660               G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
00661               return;
00662        }
00663 
00664        return;
00665 }
00666 
00667 /* Looks at the size of the label, if it grew beyond what we
00668    thought was the max, make sure it doesn't shrink again. */
00669 static gboolean
00670 idle_measure (gpointer data)
00671 {
00672        IndicatorDatetime * self = INDICATOR_DATETIME(data);
00673        self->priv->idle_measure = 0;
00674 
00675        GtkAllocation allocation;
00676        gtk_widget_get_allocation(GTK_WIDGET(self->priv->label), &allocation);
00677 
00678        if (allocation.width > self->priv->max_width) {
00679               if (self->priv->max_width != 0) {
00680                      g_warning("Guessed wrong.  We thought the max would be %d but we're now at %d", self->priv->max_width, allocation.width);
00681               }
00682               self->priv->max_width = allocation.width;
00683               gtk_widget_set_size_request(GTK_WIDGET(self->priv->label), self->priv->max_width, -1);
00684        }
00685 
00686        return FALSE;
00687 }
00688 
00689 /* Updates the accessible description */
00690 static void
00691 update_accessible_description (IndicatorDatetime * io)
00692 {
00693        GList * entries = indicator_object_get_entries(INDICATOR_OBJECT(io));
00694        IndicatorObjectEntry * entry = (IndicatorObjectEntry *)entries->data;
00695 
00696        entry->accessible_desc = get_accessible_desc(INDICATOR_OBJECT(io));
00697 
00698        g_signal_emit(G_OBJECT(io),
00699                      INDICATOR_OBJECT_SIGNAL_ACCESSIBLE_DESC_UPDATE_ID,
00700                      0,
00701                      entry,
00702                      TRUE);
00703 
00704        g_list_free(entries);
00705 
00706        return;
00707 }
00708 
00709 /* Updates the label to be the current time. */
00710 static void
00711 set_label_to_time_in_zone (IndicatorDatetime * self, GtkLabel * label,
00712                            GTimeZone * tz, const gchar * format,
00713                            GDateTime ** datetime)
00714 {
00715        gboolean unref_tz = FALSE;
00716        if (tz == NULL) {
00717               gchar * zone = read_timezone ();
00718               if (zone == NULL)
00719                      return;
00720               tz = g_time_zone_new(zone);
00721               unref_tz = TRUE;
00722               g_free (zone);
00723        }
00724 
00725        GDateTime * datetime_now;
00726        datetime_now = g_date_time_new_now(tz);
00727 
00728        gchar * timestr;
00729        if (format == NULL) {
00730               gchar * format_for_time = generate_format_string_at_time(datetime_now);
00731               timestr = g_date_time_format(datetime_now, format_for_time);
00732               g_free(format_for_time);
00733        }
00734        else {
00735               timestr = g_date_time_format(datetime_now, format);
00736               if (timestr == NULL) {
00737                      g_warning ("The custom date format is not valid, check the\n"
00738                                 "g_date_time_format() documentation for the supported\n"
00739                                 "format specifiers ");
00740                      timestr = g_strdup ("Date format not supported");
00741               }
00742        }
00743 
00744        gboolean use_markup = FALSE;
00745        if (pango_parse_markup(timestr, -1, 0, NULL, NULL, NULL, NULL))
00746               use_markup = TRUE;
00747 
00748        if (use_markup)
00749               gtk_label_set_markup(label, timestr);
00750        else
00751               gtk_label_set_text(label, timestr);
00752 
00753        g_free(timestr);
00754 
00755        if (datetime)
00756               *datetime = datetime_now;
00757        else
00758               g_date_time_unref(datetime_now);
00759 
00760        if (unref_tz)
00761               g_time_zone_unref(tz);
00762 
00763        return;
00764 }
00765 
00766 /* Updates the label to be the current time. */
00767 static void
00768 update_label (IndicatorDatetime * io, GDateTime ** datetime)
00769 {
00770        IndicatorDatetime * self = INDICATOR_DATETIME(io);
00771 
00772        if (self->priv->label == NULL) return;
00773 
00774        set_label_to_time_in_zone(self, self->priv->label, NULL, self->priv->time_string, datetime);
00775 
00776        if (self->priv->idle_measure == 0) {
00777               self->priv->idle_measure = g_idle_add(idle_measure, io);
00778        }
00779 
00780        update_accessible_description(io);
00781 
00782        return;
00783 }
00784 
00785 /* Update the time right now.  Usually the result of a timezone switch. */
00786 static void
00787 update_time (IndicatorDatetime * self)
00788 {
00789        GDateTime * dt = NULL;
00790        update_label(self, &dt);
00791        timezone_update_all_labels(self);
00792        if (dt != NULL) {
00793               setup_timer(self, dt);
00794               g_date_time_unref(dt);
00795   }
00796        return;
00797 }
00798 
00799 /* Receives all signals from the service, routed to the appropriate functions */
00800 static void
00801 receive_signal (GDBusProxy * proxy, gchar * sender_name, gchar * signal_name,
00802                 GVariant * parameters, gpointer user_data)
00803 {
00804        IndicatorDatetime * self = INDICATOR_DATETIME(user_data);
00805 
00806        if (g_strcmp0(signal_name, "UpdateTime") == 0) {
00807               update_time(self);
00808        }
00809 
00810        return;
00811 }
00812 
00813 /* Runs every minute and updates the time */
00814 gboolean
00815 timer_func (gpointer user_data)
00816 {
00817        IndicatorDatetime * self = INDICATOR_DATETIME(user_data);
00818        self->priv->timer = 0;
00819        GDateTime * dt = NULL;
00820        update_label(self, &dt);
00821        timezone_update_all_labels(self);
00822        if (dt != NULL) {
00823               setup_timer(self, dt);
00824               g_date_time_unref(dt);
00825   }
00826        return FALSE;
00827 }
00828 
00829 /* Configure the timer to run the next time through */
00830 static void
00831 setup_timer (IndicatorDatetime * self, GDateTime * datetime)
00832 {
00833        gboolean unref = FALSE;
00834 
00835        if (self->priv->timer != 0) {
00836               g_source_remove(self->priv->timer);
00837               self->priv->timer = 0;
00838        }
00839        
00840        if (self->priv->show_seconds ||
00841               (self->priv->time_mode == SETTINGS_TIME_CUSTOM && self->priv->custom_show_seconds)) {
00842               self->priv->timer = g_timeout_add_full(G_PRIORITY_HIGH, 999, timer_func, self, NULL);
00843        } else {
00844               if (datetime == NULL) {
00845                      datetime = g_date_time_new_now_local();
00846                      unref = TRUE;
00847               }
00848 
00849               /* Plus 2 so we're just after the minute, don't want to be early. */
00850               gint seconds = (gint)g_date_time_get_seconds(datetime);
00851               self->priv->timer = g_timeout_add_seconds(60 - seconds + 2, timer_func, self);
00852 
00853               if (unref) {
00854                      g_date_time_unref(datetime);
00855               }
00856        }
00857 
00858        return;
00859 }
00860 
00861 /* Does a quick meausre of how big the string is in
00862    pixels with a Pango layout */
00863 static gint
00864 measure_string (GtkStyle * style, PangoContext * context, const gchar * string)
00865 {
00866        PangoLayout * layout = pango_layout_new(context);
00867 
00868        if (pango_parse_markup(string, -1, 0, NULL, NULL, NULL, NULL))
00869               pango_layout_set_markup(layout, string, -1);
00870        else
00871               pango_layout_set_text(layout, string, -1);
00872 
00873        pango_layout_set_font_description(layout, style->font_desc);
00874 
00875        gint width;
00876        pango_layout_get_pixel_size(layout, &width, NULL);
00877        g_object_unref(layout);
00878        return width;
00879 }
00880 
00881 /* Format for the table of strftime() modifiers to what
00882    we need to check when determining the length */
00883 typedef struct _strftime_type_t strftime_type_t;
00884 struct _strftime_type_t {
00885        char character;
00886        gint mask;
00887 };
00888 
00889 /* A table taken from the man page of strftime to what the different
00890    characters can effect.  These are worst case in that we need to
00891    test the length based on all these things to ensure that we have
00892    a reasonable string lenght measurement. */
00893 const static strftime_type_t strftime_type[] = {
00894        {'a', STRFTIME_MASK_WEEK},
00895        {'A', STRFTIME_MASK_WEEK},
00896        {'b', STRFTIME_MASK_MONTH},
00897        {'B', STRFTIME_MASK_MONTH},
00898        {'c', STRFTIME_MASK_ALL}, /* We don't know, so we have to assume all */
00899        {'C', STRFTIME_MASK_YEAR},
00900        {'d', STRFTIME_MASK_MONTH},
00901        {'D', STRFTIME_MASK_MONTH | STRFTIME_MASK_YEAR | STRFTIME_MASK_DAY},
00902        {'e', STRFTIME_MASK_DAY},
00903        {'F', STRFTIME_MASK_MONTH | STRFTIME_MASK_YEAR | STRFTIME_MASK_DAY},
00904        {'G', STRFTIME_MASK_YEAR},
00905        {'g', STRFTIME_MASK_YEAR},
00906        {'h', STRFTIME_MASK_MONTH},
00907        {'j', STRFTIME_MASK_DAY},
00908        {'m', STRFTIME_MASK_MONTH},
00909        {'p', STRFTIME_MASK_AMPM},
00910        {'P', STRFTIME_MASK_AMPM},
00911        {'r', STRFTIME_MASK_AMPM},
00912        {'s', STRFTIME_MASK_SECONDS},
00913        {'S', STRFTIME_MASK_SECONDS},
00914        {'T', STRFTIME_MASK_SECONDS},
00915        {'u', STRFTIME_MASK_WEEK},
00916        {'U', STRFTIME_MASK_DAY | STRFTIME_MASK_MONTH},
00917        {'V', STRFTIME_MASK_DAY | STRFTIME_MASK_MONTH},
00918        {'w', STRFTIME_MASK_DAY},
00919        {'W', STRFTIME_MASK_DAY | STRFTIME_MASK_MONTH},
00920        {'x', STRFTIME_MASK_YEAR | STRFTIME_MASK_MONTH | STRFTIME_MASK_DAY | STRFTIME_MASK_WEEK},
00921        {'X', STRFTIME_MASK_SECONDS},
00922        {'y', STRFTIME_MASK_YEAR},
00923        {'Y', STRFTIME_MASK_YEAR},
00924        /* Last one */
00925        {0, 0}
00926 };
00927 
00928 #define FAT_NUMBER 8
00929 
00930 /* Looks through the characters in the format string to
00931    ensure that we can figure out which of the things we
00932    need to check in determining the length. */
00933 static gint
00934 generate_strftime_bitmask (const char *time_str)
00935 {
00936        gint retval = 0;
00937        glong strlength = g_utf8_strlen(time_str, -1);
00938        gint i;
00939        g_debug("Evaluating bitmask for '%s'", time_str);
00940 
00941        for (i = 0; i < strlength; i++) {
00942               if (time_str[i] == '%' && i + 1 < strlength) {
00943                      gchar evalchar = time_str[i + 1];
00944 
00945                      /* If we're using alternate formats we need to skip those characters */
00946                      if (evalchar == 'E' || evalchar == 'O') {
00947                             if (i + 2 < strlength) {
00948                                    evalchar = time_str[i + 2];
00949                             } else {
00950                                    continue;
00951                             }
00952                      }
00953 
00954                      /* Let's look at that character in the table */
00955                      int j;
00956                      for (j = 0; strftime_type[j].character != 0; j++) {
00957                             if (strftime_type[j].character == evalchar) {
00958                                    retval |= strftime_type[j].mask;
00959                                    break;
00960                             }
00961                      }
00962               }
00963        }
00964 
00965        return retval;
00966 }
00967 
00968 /* Build an array up of all the time values that we want to check
00969    for length to ensure we're in a good place */
00970 static void
00971 build_timeval_array (GArray * timevals, gint mask)
00972 {
00973        struct tm mytm = {0};
00974 
00975        /* Sun 12/28/8888 00:00 */
00976        mytm.tm_hour = 0;
00977        mytm.tm_mday = 28;
00978        mytm.tm_mon = 11;
00979        mytm.tm_year = 8888 - 1900;
00980        mytm.tm_wday = 0;
00981        mytm.tm_yday = 363;
00982        g_array_append_val(timevals, mytm);
00983 
00984        if (mask & STRFTIME_MASK_AMPM) {
00985               /* Sun 12/28/8888 12:00 */
00986               mytm.tm_hour = 12;
00987               g_array_append_val(timevals, mytm);
00988        }
00989 
00990        /* NOTE: Ignoring year 8888 should handle it */
00991 
00992        if (mask & STRFTIME_MASK_MONTH) {
00993               gint oldlen = timevals->len;
00994               gint i, j;
00995               for (i = 0; i < oldlen; i++) {
00996                      for (j = 0; j < 11; j++) {
00997                             struct tm localval = g_array_index(timevals, struct tm, i);
00998                             localval.tm_mon = j;
00999                             /* Not sure if I need to adjust yday & wday, hope not */
01000                             g_array_append_val(timevals, localval);
01001                      }
01002               }
01003        }
01004 
01005        /* Doing these together as it seems like just slightly more
01006           coverage on the numerical days, but worth it. */
01007        if (mask & (STRFTIME_MASK_WEEK | STRFTIME_MASK_DAY)) {
01008               gint oldlen = timevals->len;
01009               gint i, j;
01010               for (i = 0; i < oldlen; i++) {
01011                      for (j = 22; j < 28; j++) {
01012                             struct tm localval = g_array_index(timevals, struct tm, i);
01013 
01014                             gint diff = 28 - j;
01015 
01016                             localval.tm_mday = j;
01017                             localval.tm_wday = localval.tm_wday - diff;
01018                             if (localval.tm_wday < 0) {
01019                                    localval.tm_wday += 7;
01020                             }
01021                             localval.tm_yday = localval.tm_yday - diff;
01022 
01023                             g_array_append_val(timevals, localval);
01024                      }
01025               }
01026        }
01027 
01028        return;
01029 }
01030 
01031 /* Try to get a good guess at what a maximum width of the entire
01032    string would be. */
01033 static void
01034 guess_label_size (IndicatorDatetime * self)
01035 {
01036        /* This is during startup. */
01037        if (self->priv->label == NULL) return;
01038 
01039        GtkStyle * style = gtk_widget_get_style(GTK_WIDGET(self->priv->label));
01040        PangoContext * context = gtk_widget_get_pango_context(GTK_WIDGET(self->priv->label));
01041        gint * max_width = &(self->priv->max_width);
01042        gint posibilitymask = generate_strftime_bitmask(self->priv->time_string);
01043 
01044        /* Reset max width */
01045        *max_width = 0;
01046 
01047        /* Build the array of possibilities that we want to test */
01048        GArray * timevals = g_array_new(FALSE, TRUE, sizeof(struct tm));
01049        build_timeval_array(timevals, posibilitymask);
01050 
01051        g_debug("Checking against %d possible times", timevals->len);
01052        gint check_time;
01053        for (check_time = 0; check_time < timevals->len; check_time++) {
01054               struct tm * timeval = &g_array_index(timevals, struct tm, check_time);
01055               GDateTime * dt = g_date_time_new_local(timeval->tm_year, timeval->tm_mon, timeval->tm_mday, timeval->tm_hour, timeval->tm_min, timeval->tm_sec);
01056               gchar * timestr = g_date_time_format(dt, self->priv->time_string);
01057 
01058               gint length = measure_string(style, context, timestr);
01059 
01060               g_free(timestr);
01061               g_date_time_unref(dt);
01062 
01063               if (length > *max_width) {
01064                      *max_width = length;
01065               }
01066        }
01067 
01068        g_array_free(timevals, TRUE);
01069 
01070        gtk_widget_set_size_request(GTK_WIDGET(self->priv->label), self->priv->max_width, -1);
01071        g_debug("Guessing max time width: %d", self->priv->max_width);
01072 
01073        return;
01074 }
01075 
01076 /* React to the style changing, which could mean an font
01077    update. */
01078 static void
01079 style_changed (GtkWidget * widget, GtkStyle * oldstyle, gpointer data)
01080 {
01081        g_debug("New style for time label");
01082        IndicatorDatetime * self = INDICATOR_DATETIME(data);
01083        guess_label_size(self);
01084        update_label(self, NULL);
01085        timezone_update_all_labels(self);
01086        return;
01087 }
01088 
01089 /* Respond to changes in the screen to update the text gravity */
01090 static void
01091 update_text_gravity (GtkWidget *widget, GdkScreen *previous_screen, gpointer data)
01092 {
01093        IndicatorDatetime * self = INDICATOR_DATETIME(data);
01094        if (self->priv->label == NULL) return;
01095 
01096        PangoLayout  *layout;
01097        PangoContext *context;
01098 
01099        layout = gtk_label_get_layout (GTK_LABEL(self->priv->label));
01100        context = pango_layout_get_context(layout);
01101        pango_context_set_base_gravity(context, PANGO_GRAVITY_AUTO);
01102 }
01103 
01104 static gchar *
01105 generate_format_string_now (IndicatorDatetime * self)
01106 {
01107        if (self->priv->time_mode == SETTINGS_TIME_CUSTOM) {
01108               return g_strdup(self->priv->custom_string);
01109        }
01110        else {
01111               return generate_format_string_full(self->priv->show_day,
01112                                                  self->priv->show_date);
01113        }
01114 }
01115 
01116 static void
01117 timezone_update_labels (indicator_item_t * mi_data)
01118 {
01119        const gchar * zone = dbusmenu_menuitem_property_get(mi_data->mi, TIMEZONE_MENUITEM_PROP_ZONE);
01120        const gchar * name = dbusmenu_menuitem_property_get(mi_data->mi, TIMEZONE_MENUITEM_PROP_NAME);
01121 
01122        gtk_label_set_text(GTK_LABEL(mi_data->label), name);
01123 
01124        /* Show current time in that zone on the right */
01125        GTimeZone * tz = g_time_zone_new(zone);
01126        set_label_to_time_in_zone(mi_data->self, GTK_LABEL(mi_data->right), tz, NULL, NULL);
01127        g_time_zone_unref(tz);
01128 }
01129 
01130 /* Whenever we have a property change on a DbusmenuMenuitem
01131    we need to be responsive to that. */
01132 static void
01133 indicator_prop_change_cb (DbusmenuMenuitem * mi, gchar * prop, GVariant *value, indicator_item_t * mi_data)
01134 {
01135        if (!g_strcmp0(prop, APPOINTMENT_MENUITEM_PROP_LABEL)) {
01136               /* Set the main label */
01137               gtk_label_set_text(GTK_LABEL(mi_data->label), g_variant_get_string(value, NULL));
01138        } else if (!g_strcmp0(prop, APPOINTMENT_MENUITEM_PROP_RIGHT)) {
01139               /* Set the right label */
01140               gtk_label_set_text(GTK_LABEL(mi_data->right), g_variant_get_string(value, NULL));
01141        } else if (!g_strcmp0(prop, APPOINTMENT_MENUITEM_PROP_ICON)) {
01142               /* We don't use the value here, which is probably less efficient, 
01143                  but it's easier to use the easy function.  And since th value
01144                  is already cached, shouldn't be a big deal really.  */
01145               GdkPixbuf * pixbuf = dbusmenu_menuitem_property_get_image(mi, APPOINTMENT_MENUITEM_PROP_ICON);
01146               if (pixbuf != NULL) {
01147                      /* If we've got a pixbuf we need to make sure it's of a reasonable
01148                         size to fit in the menu.  If not, rescale it. */
01149                      GdkPixbuf * resized_pixbuf;
01150                      gint width, height;
01151                      gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height);
01152                      if (gdk_pixbuf_get_width(pixbuf) > width ||
01153                                    gdk_pixbuf_get_height(pixbuf) > height) {
01154                             g_debug("Resizing icon from %dx%d to %dx%d", gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf), width, height);
01155                             resized_pixbuf = gdk_pixbuf_scale_simple(pixbuf,
01156                                                                      width,
01157                                                                      height,
01158                                                                      GDK_INTERP_BILINEAR);
01159                      } else {
01160                             g_debug("Happy with icon sized %dx%d", gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf));
01161                             resized_pixbuf = pixbuf;
01162                      }
01163                      gtk_image_set_from_pixbuf(GTK_IMAGE(mi_data->icon), resized_pixbuf);
01164                      /* The other pixbuf should be free'd by the dbusmenu. */
01165                      if (resized_pixbuf != pixbuf) {
01166                             g_object_unref(resized_pixbuf);
01167                      }
01168               }
01169        } else if (!g_strcmp0(prop, TIMEZONE_MENUITEM_PROP_ZONE)) {
01170               timezone_update_labels(mi_data);
01171        } else if (!g_strcmp0(prop, TIMEZONE_MENUITEM_PROP_NAME)) {
01172               timezone_update_labels(mi_data);
01173        } else if (!g_strcmp0(prop, TIMEZONE_MENUITEM_PROP_RADIO)) {
01174               gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mi_data->gmi), g_variant_get_boolean(value));
01175        }
01176        return;
01177 }
01178 // Properties for marking and unmarking the calendar
01179 static void
01180 calendar_prop_change_cb (DbusmenuMenuitem * mi, gchar * prop, GVariant *value, IdoCalendarMenuItem * mi_data)
01181 {
01182        g_debug("Changing calendar property: %s", prop);
01183        if (!g_strcmp0(prop, CALENDAR_MENUITEM_PROP_MARKS)) {
01184               ido_calendar_menu_item_clear_marks (IDO_CALENDAR_MENU_ITEM (mi_data));
01185 
01186               if (value != NULL) {
01187                      GVariantIter *iter;
01188                      gint day;
01189 
01190                      g_debug("\tMarks: %s", g_variant_print(value, FALSE));
01191 
01192                      g_variant_get (value, "ai", &iter);
01193                      while (g_variant_iter_loop (iter, "i", &day)) {
01194                             ido_calendar_menu_item_mark_day (IDO_CALENDAR_MENU_ITEM (mi_data), day);
01195                      }
01196                      g_variant_iter_free (iter);
01197               } else {
01198                      g_debug("\tMarks: <cleared>");
01199               }
01200        }
01201        return;
01202 }
01203 
01204 /* We have a small little menuitem type that handles all
01205    of the fun stuff for indicators.  Mostly this is the
01206    shifting over and putting the icon in with some right
01207    side text that'll be determined by the service.  
01208    Copied verbatim from an old revision (including comments) of indicator-messages   
01209 */
01210 static gboolean
01211 new_appointment_item (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client, gpointer user_data)
01212 {
01213        g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE);
01214        g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE);
01215        g_return_val_if_fail(IS_INDICATOR_DATETIME(user_data), FALSE);
01216        /* Note: not checking parent, it's reasonable for it to be NULL */
01217        IndicatorDatetime * self = INDICATOR_DATETIME(user_data);
01218 
01219        indicator_item_t * mi_data = g_new0(indicator_item_t, 1);
01220 
01221        mi_data->gmi = gtk_menu_item_new();
01222 
01223        GtkWidget * hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
01224 
01225        /* Icon, probably someone's face or avatar on an IM */
01226        mi_data->icon = gtk_image_new();
01227        GdkPixbuf * pixbuf = dbusmenu_menuitem_property_get_image(newitem, APPOINTMENT_MENUITEM_PROP_ICON);
01228 
01229        if (pixbuf != NULL) {
01230               /* If we've got a pixbuf we need to make sure it's of a reasonable
01231                  size to fit in the menu.  If not, rescale it. */
01232               GdkPixbuf * resized_pixbuf;
01233               gint width, height;
01234               gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height);
01235               if (gdk_pixbuf_get_width(pixbuf) > width ||
01236                       gdk_pixbuf_get_height(pixbuf) > height) {
01237                      g_debug("Resizing icon from %dx%d to %dx%d", gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf), width, height);
01238                      resized_pixbuf = gdk_pixbuf_scale_simple(pixbuf,
01239                                                               width,
01240                                                               height,
01241                                                               GDK_INTERP_BILINEAR);
01242               } else {
01243                      g_debug("Happy with icon sized %dx%d", gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf));
01244                      resized_pixbuf = pixbuf;
01245               }
01246   
01247               gtk_image_set_from_pixbuf(GTK_IMAGE(mi_data->icon), resized_pixbuf);
01248 
01249               /* The other pixbuf should be free'd by the dbusmenu. */
01250               if (resized_pixbuf != pixbuf) {
01251                      g_object_unref(resized_pixbuf);
01252               }
01253        }
01254        gtk_misc_set_alignment(GTK_MISC(mi_data->icon), 0.0, 0.5);
01255        gtk_box_pack_start(GTK_BOX(hbox), mi_data->icon, FALSE, FALSE, 0);
01256        gtk_widget_show(mi_data->icon);
01257 
01258        /* Label, probably a username, chat room or mailbox name */
01259        mi_data->label = gtk_label_new(dbusmenu_menuitem_property_get(newitem, APPOINTMENT_MENUITEM_PROP_LABEL));
01260        gtk_misc_set_alignment(GTK_MISC(mi_data->label), 0.0, 0.5);
01261        
01262        GtkStyle * style = gtk_widget_get_style(GTK_WIDGET(mi_data->label));
01263        PangoContext * context = gtk_widget_get_pango_context(GTK_WIDGET(mi_data->label));
01264        gint length = measure_string(style, context, "MMMMMMMMMMMMMMM"); // 15 char wide string max
01265        gtk_widget_set_size_request(GTK_WIDGET(mi_data->label), length, -1); // Set the min size in pixels
01266        
01267        gtk_label_set_ellipsize(GTK_LABEL(mi_data->label), PANGO_ELLIPSIZE_END);
01268        gtk_box_pack_start(GTK_BOX(hbox), mi_data->label, TRUE, TRUE, 0);
01269        gtk_widget_show(mi_data->label);
01270 
01271        /* Usually either the time or the count on the individual
01272           item. */
01273        mi_data->right = gtk_label_new(dbusmenu_menuitem_property_get(newitem, APPOINTMENT_MENUITEM_PROP_RIGHT));
01274        gtk_size_group_add_widget(self->priv->indicator_right_group, mi_data->right);
01275        gtk_misc_set_alignment(GTK_MISC(mi_data->right), 1.0, 0.5);
01276        gtk_box_pack_start(GTK_BOX(hbox), mi_data->right, FALSE, FALSE, 0);
01277        gtk_widget_show(mi_data->right);
01278 
01279        gtk_container_add(GTK_CONTAINER(mi_data->gmi), hbox);
01280        gtk_widget_show(hbox);
01281 
01282        dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), newitem, GTK_MENU_ITEM(mi_data->gmi), parent);
01283 
01284        g_signal_connect(G_OBJECT(newitem), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(indicator_prop_change_cb), mi_data);
01285        return TRUE;
01286 }
01287 
01288 static void
01289 month_changed_cb (IdoCalendarMenuItem *ido, 
01290                   gpointer        user_data) 
01291 {
01292        guint d,m,y;
01293        DbusmenuMenuitem * item = DBUSMENU_MENUITEM (user_data);
01294        ido_calendar_menu_item_get_date(ido, &y, &m, &d);
01295        struct tm date = {0};
01296        date.tm_mday = d;
01297        date.tm_mon = m;
01298        date.tm_year = y - 1900;
01299        guint selecteddate = (guint)mktime(&date);
01300        g_debug("Got month changed signal: %s", asctime(&date));
01301        GVariant *variant = g_variant_new_uint32(selecteddate);
01302        guint timestamp = (guint)time(NULL);
01303        dbusmenu_menuitem_handle_event(DBUSMENU_MENUITEM(item), "month-changed", variant, timestamp);
01304 }
01305        
01306 static void
01307 day_selected_cb (IdoCalendarMenuItem *ido,
01308                  gpointer        user_data) 
01309 {
01310        guint d,m,y;
01311        DbusmenuMenuitem * item = DBUSMENU_MENUITEM (user_data);
01312        ido_calendar_menu_item_get_date(ido, &y, &m, &d);
01313        struct tm date = {0};
01314        date.tm_mday = d;
01315        date.tm_mon = m;
01316        date.tm_year = y - 1900;
01317        guint selecteddate = (guint)mktime(&date);
01318        g_debug("Got day selected signal: %s", asctime(&date));
01319        GVariant *variant = g_variant_new_uint32(selecteddate);
01320        guint timestamp = (guint)time(NULL);
01321        dbusmenu_menuitem_handle_event(DBUSMENU_MENUITEM(item), "day-selected", variant, timestamp);
01322 }
01323 
01324 static void
01325 day_selected_double_click_cb (IdoCalendarMenuItem *ido,
01326                               gpointer        user_data) 
01327 {
01328        guint d,m,y;
01329        DbusmenuMenuitem * item = DBUSMENU_MENUITEM (user_data);
01330        ido_calendar_menu_item_get_date(ido, &y, &m, &d);
01331        struct tm date = {0};
01332        date.tm_mday = d;
01333        date.tm_mon = m;
01334        date.tm_year = y - 1900;
01335        guint selecteddate = (guint)mktime(&date);
01336        g_debug("Got day selected double click signal: %s", asctime(&date));
01337        GVariant *variant = g_variant_new_uint32(selecteddate);
01338        guint timestamp = (guint)time(NULL);
01339        dbusmenu_menuitem_handle_event(DBUSMENU_MENUITEM(item), "day-selected-double-click", variant, timestamp);
01340 }
01341 
01342 static gboolean
01343 new_calendar_item (DbusmenuMenuitem * newitem,
01344                                DbusmenuMenuitem * parent,
01345                                DbusmenuClient   * client,
01346                                gpointer           user_data)
01347 {
01348        g_debug("New calendar item");
01349        g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE);
01350        g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE);
01351        g_return_val_if_fail(IS_INDICATOR_DATETIME(user_data), FALSE);
01352        /* Note: not checking parent, it's reasonable for it to be NULL */
01353 
01354        IndicatorDatetime *self = INDICATOR_DATETIME(user_data);
01355 
01356        IdoCalendarMenuItem *ido = IDO_CALENDAR_MENU_ITEM (ido_calendar_menu_item_new ());
01357        self->priv->ido_calendar = ido;
01358        
01359        GtkCalendarDisplayOptions flags = ido_calendar_menu_item_get_display_options (self->priv->ido_calendar);
01360        if (self->priv->show_week_numbers == TRUE)
01361               flags |= GTK_CALENDAR_SHOW_WEEK_NUMBERS;
01362        else
01363               flags &= ~GTK_CALENDAR_SHOW_WEEK_NUMBERS;
01364        ido_calendar_menu_item_set_display_options (self->priv->ido_calendar, flags);
01365 
01366        gtk_widget_set_visible (GTK_WIDGET (self->priv->ido_calendar), self->priv->show_calendar);
01367 
01368        dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), newitem, GTK_MENU_ITEM(ido), parent);
01369 
01370        g_signal_connect_after(ido, "month-changed", G_CALLBACK(month_changed_cb), (gpointer)newitem);
01371        g_signal_connect_after(ido, "day-selected", G_CALLBACK(day_selected_cb), (gpointer)newitem);
01372        g_signal_connect_after(ido, "day-selected-double-click", G_CALLBACK(day_selected_double_click_cb), (gpointer)newitem);
01373 
01374        g_signal_connect(G_OBJECT(newitem), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(calendar_prop_change_cb), ido);
01375 
01376        /* Run the current values through prop changed */
01377        GVariant * propval = NULL;
01378 
01379        propval = dbusmenu_menuitem_property_get_variant(newitem, CALENDAR_MENUITEM_PROP_MARKS);
01380        if (propval != NULL) {
01381               calendar_prop_change_cb(newitem, CALENDAR_MENUITEM_PROP_MARKS, propval, ido);
01382        }
01383 
01384        return TRUE;
01385 }
01386 
01387 static void
01388 timezone_toggled_cb (GtkCheckMenuItem *checkmenuitem, DbusmenuMenuitem * dbusitem)
01389 {
01390        /* Make sure that the displayed radio-active setting is always 
01391           consistent with the dbus menuitem */
01392        gtk_check_menu_item_set_active(checkmenuitem,
01393               dbusmenu_menuitem_property_get_bool(dbusitem, TIMEZONE_MENUITEM_PROP_RADIO));
01394 }
01395 
01396 static void
01397 timezone_destroyed_cb (indicator_item_t * mi_data, DbusmenuMenuitem * dbusitem)
01398 {
01399        IndicatorDatetime *self = INDICATOR_DATETIME (mi_data->self);
01400        IndicatorDatetimePrivate *priv = self->priv;
01401 
01402        priv->timezone_items = g_list_remove(priv->timezone_items, mi_data);
01403        g_signal_handlers_disconnect_by_func(G_OBJECT(mi_data->gmi), G_CALLBACK(timezone_toggled_cb), dbusitem);
01404        g_free(mi_data);
01405 }
01406 
01407 static gboolean
01408 new_timezone_item(DbusmenuMenuitem * newitem,
01409                                DbusmenuMenuitem * parent,
01410                                DbusmenuClient   * client,
01411                                gpointer           user_data)
01412 {
01413        g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE);
01414        g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE);
01415        g_return_val_if_fail(IS_INDICATOR_DATETIME(user_data), FALSE);
01416        /* Note: not checking parent, it's reasonable for it to be NULL */
01417 
01418        IndicatorDatetime * self = INDICATOR_DATETIME(user_data);
01419        IndicatorDatetimePrivate *priv = self->priv;
01420 
01421        // Menu item with a radio button and a right aligned time
01422        indicator_item_t * mi_data = g_new0(indicator_item_t, 1);
01423 
01424        priv->timezone_items = g_list_prepend(priv->timezone_items, mi_data);
01425 
01426        mi_data->self = self;
01427        mi_data->mi = newitem;
01428        mi_data->gmi = gtk_check_menu_item_new();
01429 
01430        gtk_check_menu_item_set_draw_as_radio(GTK_CHECK_MENU_ITEM(mi_data->gmi), TRUE);
01431        gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mi_data->gmi),
01432               dbusmenu_menuitem_property_get_bool(newitem, TIMEZONE_MENUITEM_PROP_RADIO));
01433 
01434        GtkWidget * hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
01435 
01436        /* Label, probably a username, chat room or mailbox name */
01437        mi_data->label = gtk_label_new("");
01438        gtk_misc_set_alignment(GTK_MISC(mi_data->label), 0.0, 0.5);
01439        gtk_box_pack_start(GTK_BOX(hbox), mi_data->label, TRUE, TRUE, 0);
01440        gtk_widget_show(mi_data->label);
01441 
01442        /* Usually either the time or the count on the individual
01443           item. */
01444        mi_data->right = gtk_label_new("");
01445        gtk_size_group_add_widget(self->priv->indicator_right_group, mi_data->right);
01446        gtk_misc_set_alignment(GTK_MISC(mi_data->right), 1.0, 0.5);
01447        gtk_box_pack_start(GTK_BOX(hbox), mi_data->right, FALSE, FALSE, 0);
01448        gtk_widget_show(mi_data->right);
01449 
01450        timezone_update_labels(mi_data);
01451 
01452        gtk_container_add(GTK_CONTAINER(mi_data->gmi), hbox);
01453        gtk_widget_show(hbox);
01454 
01455        dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), newitem, GTK_MENU_ITEM(mi_data->gmi), parent);
01456 
01457        g_signal_connect(G_OBJECT(mi_data->gmi), "toggled", G_CALLBACK(timezone_toggled_cb), newitem);
01458        g_signal_connect(G_OBJECT(newitem), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(indicator_prop_change_cb), mi_data);
01459        g_object_weak_ref(G_OBJECT(newitem), (GWeakNotify)timezone_destroyed_cb, mi_data);
01460 
01461        return TRUE;
01462 }
01463 
01464 /* Grabs the label.  Creates it if it doesn't
01465    exist already */
01466 static GtkLabel *
01467 get_label (IndicatorObject * io)
01468 {
01469        IndicatorDatetime * self = INDICATOR_DATETIME(io);
01470 
01471        /* If there's not a label, we'll build ourselves one */
01472        if (self->priv->label == NULL) {
01473               self->priv->label = GTK_LABEL(gtk_label_new("Time"));
01474               gtk_label_set_justify (GTK_LABEL(self->priv->label), GTK_JUSTIFY_CENTER);
01475               g_object_ref(G_OBJECT(self->priv->label));
01476               g_signal_connect(G_OBJECT(self->priv->label), "style-set", G_CALLBACK(style_changed), self);
01477               g_signal_connect(G_OBJECT(self->priv->label), "screen-changed", G_CALLBACK(update_text_gravity), self);
01478               guess_label_size(self);
01479               update_label(self, NULL);
01480               gtk_widget_set_visible(GTK_WIDGET (self->priv->label), self->priv->show_clock);
01481        }
01482 
01483        if (self->priv->timer == 0) {
01484               setup_timer(self, NULL);
01485        }
01486 
01487        return self->priv->label;
01488 }
01489 
01490 static GtkMenu *
01491 get_menu (IndicatorObject * io)
01492 {
01493        IndicatorDatetime * self = INDICATOR_DATETIME(io);
01494 
01495        return GTK_MENU(self->priv->menu);
01496 }
01497 
01498 static const gchar *
01499 get_accessible_desc (IndicatorObject * io)
01500 {
01501        IndicatorDatetime * self = INDICATOR_DATETIME(io);
01502        const gchar * name;
01503 
01504        if (self->priv->label != NULL) {
01505               name = gtk_label_get_text(self->priv->label);
01506               return name;
01507        }
01508        return NULL;
01509 }
01510 
01511 static const gchar *
01512 get_name_hint (IndicatorObject * io)
01513 {
01514        return PACKAGE_NAME;
01515 }