diff options
Diffstat (limited to 'applets/clock/clock.c')
-rw-r--r-- | applets/clock/clock.c | 3745 |
1 files changed, 3745 insertions, 0 deletions
diff --git a/applets/clock/clock.c b/applets/clock/clock.c new file mode 100644 index 00000000..b4846009 --- /dev/null +++ b/applets/clock/clock.c @@ -0,0 +1,3745 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ +/* + * clock.c: the MATE clock applet + * + * Copyright (C) 1997-2003 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Authors: + * Miguel de Icaza + * Frederico Mena + * Stuart Parmenter + * Alexander Larsson + * George Lebl + * Gediminas Paulauskas + * Mark McLoughlin + */ + +#include "config.h" + +#include <stdio.h> +#include <sys/stat.h> +#include <unistd.h> +#include <dirent.h> +#include <string.h> +#include <time.h> +#include <sys/time.h> +#include <math.h> +#include <locale.h> + +#include <mate-panel-applet.h> +#include <mate-panel-applet-mateconf.h> + +#include <glib/gi18n.h> + +#include <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> +#include <gdk/gdkx.h> +#include <mateconf/mateconf-client.h> + +#include <libmateweather/mateweather-prefs.h> +#include <libmateweather/mateweather-xml.h> +#include <libmateweather/location-entry.h> +#include <libmateweather/timezone-menu.h> + +#ifdef HAVE_LIBECAL +#include <libedataserverui/e-passwords.h> +#endif + +#include "clock.h" + +#include "calendar-window.h" +#include "clock-location.h" +#include "clock-location-tile.h" +#include "clock-map.h" +#include "clock-utils.h" +#include "set-timezone.h" +#include "system-timezone.h" + +#define INTERNETSECOND (864) +#define INTERNETBEAT (86400) + +#define NEVER_SENSITIVE "never_sensitive" + +#define N_MATECONF_PREFS 11 /* Keep this in sync with the number of keys below! */ +#define KEY_FORMAT "format" +#define KEY_SHOW_SECONDS "show_seconds" +#define KEY_SHOW_DATE "show_date" +#define KEY_SHOW_WEATHER "show_weather" +#define KEY_SHOW_TEMPERATURE "show_temperature" +#define KEY_CUSTOM_FORMAT "custom_format" +#define KEY_SHOW_WEEK "show_week_numbers" +#define KEY_CITIES "cities" +#define KEY_TEMPERATURE_UNIT "temperature_unit" +#define KEY_SPEED_UNIT "speed_unit" + +static MateConfEnumStringPair format_type_enum_map [] = { + { CLOCK_FORMAT_12, "12-hour" }, + { CLOCK_FORMAT_24, "24-hour" }, + { CLOCK_FORMAT_UNIX, "unix" }, + { CLOCK_FORMAT_INTERNET, "internet" }, + { CLOCK_FORMAT_CUSTOM, "custom" }, + { 0, NULL } +}; + +enum { + COL_CITY_NAME = 0, + COL_CITY_TZ, + COL_CITY_LOC, + COL_CITY_LAST +}; + +typedef struct _ClockData ClockData; + +struct _ClockData { + /* widgets */ + GtkWidget *applet; + + GtkWidget *panel_button; /* main toggle button for the whole clock */ + + GtkWidget *main_obox; /* orientable box inside panel_button */ + GtkWidget *weather_obox; /* orientable box for the weather widgets */ + + GtkWidget *clockw; /* main label for the date/time display */ + + GtkWidget *panel_weather_icon; + GtkWidget *panel_temperature_label; + + GtkWidget *props; + GtkWidget *calendar_popup; + + GtkWidget *clock_vbox; + GtkSizeGroup *clock_group; + + GtkBuilder *builder; + + /* Preferences dialog */ + GtkWidget *prefs_window; + GtkTreeView *prefs_locations; + + GtkWidget *prefs_location_add_button; + GtkWidget *prefs_location_edit_button; + GtkWidget *prefs_location_remove_button; + + MateWeatherLocationEntry *location_entry; + MateWeatherTimezoneMenu *zone_combo; + + GtkWidget *time_settings_button; + GtkWidget *calendar; + GtkWidget *hours_spin; + GtkWidget *minutes_spin; + GtkWidget *seconds_spin; + GtkWidget *set_time_button; + + GtkListStore *cities_store; + GtkWidget *cities_section; + GtkWidget *map_section; + GtkWidget *map_widget; + + /* Window to set the time */ + GtkWidget *set_time_window; + GtkWidget *current_time_label; + + /* preferences */ + ClockFormat format; + char *custom_format; + gboolean showseconds; + gboolean showdate; + gboolean showweek; + gboolean show_weather; + gboolean show_temperature; + + gboolean use_temperature_default; + gboolean use_speed_default; + TempUnit temperature_unit; + SpeedUnit speed_unit; + + /* Locations */ + GList *locations; + GList *location_tiles; + + /* runtime data */ + time_t current_time; + char *timeformat; + guint timeout; + MatePanelAppletOrient orient; + int size; + GtkAllocation old_allocation; + + SystemTimezone *systz; + + int fixed_width; + int fixed_height; + + GtkWidget *showseconds_check; + GtkWidget *showdate_check; + GtkWidget *custom_hbox; + GtkWidget *custom_label; + GtkWidget *custom_entry; + gboolean custom_format_shown; + + gboolean can_handle_format_12; + + guint listeners [N_MATECONF_PREFS]; +}; + +/* Used to count the number of clock instances. It's there to know when we + * should free resources that are shared. */ +static int clock_numbers = 0; + +static void update_clock (ClockData * cd); +static void update_tooltip (ClockData * cd); +static void update_panel_weather (ClockData *cd); +static int clock_timeout_callback (gpointer data); +static float get_itime (time_t current_time); + +static void set_atk_name_description (GtkWidget *widget, + const char *name, + const char *desc); +static void verb_display_properties_dialog (GtkAction *action, + ClockData *cd); + +static void display_properties_dialog (ClockData *cd, + gboolean start_in_locations_page); +static void display_help_dialog (GtkAction *action, + ClockData *cd); +static void display_about_dialog (GtkAction *action, + ClockData *cd); +static void position_calendar_popup (ClockData *cd); +static void update_orient (ClockData *cd); +static void applet_change_orient (MatePanelApplet *applet, + MatePanelAppletOrient orient, + ClockData *cd); + +static void edit_hide (GtkWidget *unused, ClockData *cd); +static gboolean edit_delete (GtkWidget *unused, GdkEvent *event, ClockData *cd); +static void save_cities_store (ClockData *cd); + +/* ClockBox, an instantiable GtkBox */ + +typedef GtkBox ClockBox; +typedef GtkBoxClass ClockBoxClass; + +static GType clock_box_get_type (void); + +G_DEFINE_TYPE (ClockBox, clock_box, GTK_TYPE_BOX) + +static void +clock_box_init (ClockBox *box) +{ +} + +static void +clock_box_class_init (ClockBoxClass *klass) +{ +} + +/* Clock */ + +static inline GtkWidget * +_clock_get_widget (ClockData *cd, + const char *name) +{ + return GTK_WIDGET (gtk_builder_get_object (cd->builder, name)); +} + +static void +unfix_size (ClockData *cd) +{ + cd->fixed_width = -1; + cd->fixed_height = -1; + gtk_widget_queue_resize (cd->panel_button); +} + +static int +calculate_minimum_width (GtkWidget *widget, + const gchar *text) +{ + PangoContext *context; + PangoLayout *layout; + int width, height; + int focus_width = 0; + int focus_pad = 0; + + context = gtk_widget_get_pango_context (widget); + + layout = pango_layout_new (context); + pango_layout_set_alignment (layout, PANGO_ALIGN_LEFT); + pango_layout_set_text (layout, text, -1); + pango_layout_get_pixel_size (layout, &width, &height); + g_object_unref (G_OBJECT (layout)); + layout = NULL; + + gtk_widget_style_get (widget, + "focus-line-width", &focus_width, + "focus-padding", &focus_pad, + NULL); + + width += 2 * (focus_width + focus_pad + gtk_widget_get_style (widget)->xthickness); + + return width; +} + +static void +clock_set_timeout (ClockData *cd, + time_t now) +{ + int timeouttime; + + if (cd->format == CLOCK_FORMAT_INTERNET) { + int itime_ms; + + itime_ms = ((unsigned int) (get_itime (now) * 1000)); + + if (!cd->showseconds) + timeouttime = (999 - itime_ms % 1000) * 86.4 + 1; + else { + struct timeval tv; + gettimeofday (&tv, NULL); + itime_ms += (tv.tv_usec * 86.4) / 1000; + timeouttime = ((999 - itime_ms % 1000) * 86.4) / 100 + 1; + } + } else { + struct timeval tv; + + gettimeofday (&tv, NULL); + timeouttime = (G_USEC_PER_SEC - tv.tv_usec)/1000+1; + + /* timeout of one minute if we don't care about the seconds */ + if (cd->format != CLOCK_FORMAT_UNIX && + !cd->showseconds && + (!cd->set_time_window || !gtk_widget_get_visible (cd->set_time_window))) + timeouttime += 1000 * (59 - now % 60); + } + + cd->timeout = g_timeout_add (timeouttime, + clock_timeout_callback, + cd); +} + +static int +clock_timeout_callback (gpointer data) +{ + ClockData *cd = data; + time_t new_time; + + time (&new_time); + + if (!cd->showseconds && + (!cd->set_time_window || !gtk_widget_get_visible (cd->set_time_window)) && + cd->format != CLOCK_FORMAT_UNIX && + cd->format != CLOCK_FORMAT_CUSTOM) { + if (cd->format == CLOCK_FORMAT_INTERNET && + (unsigned int)get_itime (new_time) != + (unsigned int)get_itime (cd->current_time)) { + update_clock (cd); + } else if ((cd->format == CLOCK_FORMAT_12 || + cd->format == CLOCK_FORMAT_24) && + new_time / 60 != cd->current_time / 60) { + update_clock (cd); + } + } else { + update_clock (cd); + } + + clock_set_timeout (cd, new_time); + + return FALSE; +} + +static float +get_itime (time_t current_time) +{ + struct tm *tm; + float itime; + time_t bmt; + + /* BMT (Biel Mean Time) is GMT+1 */ + bmt = current_time + 3600; + tm = gmtime (&bmt); + itime = (tm->tm_hour*3600.0 + tm->tm_min*60.0 + tm->tm_sec)/86.4; + + return itime; +} + +/* adapted from panel-toplevel.c */ +static int +calculate_minimum_height (GtkWidget *widget, + MatePanelAppletOrient orientation) +{ + GtkStyle *style; + PangoContext *context; + PangoFontMetrics *metrics; + int focus_width = 0; + int focus_pad = 0; + int ascent; + int descent; + int thickness; + + style = gtk_widget_get_style (widget); + context = gtk_widget_get_pango_context (widget); + metrics = pango_context_get_metrics (context, + style->font_desc, + pango_context_get_language (context)); + + ascent = pango_font_metrics_get_ascent (metrics); + descent = pango_font_metrics_get_descent (metrics); + + pango_font_metrics_unref (metrics); + + gtk_widget_style_get (widget, + "focus-line-width", &focus_width, + "focus-padding", &focus_pad, + NULL); + + if (orientation == MATE_PANEL_APPLET_ORIENT_UP + || orientation == MATE_PANEL_APPLET_ORIENT_DOWN) { + thickness = style->ythickness; + } else { + thickness = style->xthickness; + } + + return PANGO_PIXELS (ascent + descent) + 2 * (focus_width + focus_pad + thickness); +} + +static gboolean +use_two_line_format (ClockData *cd) +{ + if (cd->size >= 2 * calculate_minimum_height (cd->panel_button, cd->orient)) + return TRUE; + + return FALSE; +} + +static char * +get_updated_timeformat (ClockData *cd) +{ + /* Show date in another line if panel is vertical, or + * horizontal but large enough to hold two lines of text + */ + char *result; + const char *time_format; + const char *date_format; + char *clock_format; + + if (cd->format == CLOCK_FORMAT_12) + /* Translators: This is a strftime format string. + * It is used to display the time in 12-hours format (eg, like + * in the US: 8:10 am). The %p expands to am/pm. */ + time_format = cd->showseconds ? _("%l:%M:%S %p") : _("%l:%M %p"); + else + /* Translators: This is a strftime format string. + * It is used to display the time in 24-hours format (eg, like + * in France: 20:10). */ + time_format = cd->showseconds ? _("%H:%M:%S") : _("%H:%M"); + + if (!cd->showdate) + clock_format = g_strdup (time_format); + + else { + /* Translators: This is a strftime format string. + * It is used to display the date. Replace %e with %d if, when + * the day of the month as a decimal number is a single digit, + * it should begin with a 0 in your locale (e.g. "May 01" + * instead of "May 1"). */ + date_format = _("%a %b %e"); + + if (use_two_line_format (cd)) + /* translators: reverse the order of these arguments + * if the time should come before the + * date on a clock in your locale. + */ + clock_format = g_strdup_printf (_("%1$s\n%2$s"), + date_format, + time_format); + else + /* translators: reverse the order of these arguments + * if the time should come before the + * date on a clock in your locale. + */ + clock_format = g_strdup_printf (_("%1$s, %2$s"), + date_format, + time_format); + } + + result = g_locale_from_utf8 (clock_format, -1, NULL, NULL, NULL); + g_free (clock_format); + + /* let's be paranoid */ + if (!result) + result = g_strdup ("???"); + + return result; +} + +static void +update_timeformat (ClockData *cd) +{ + g_free (cd->timeformat); + cd->timeformat = get_updated_timeformat (cd); +} + +/* sets accessible name and description for the widget */ +static void +set_atk_name_description (GtkWidget *widget, + const char *name, + const char *desc) +{ + AtkObject *obj; + obj = gtk_widget_get_accessible (widget); + + /* return if gail is not loaded */ + if (!GTK_IS_ACCESSIBLE (obj)) + return; + + if (desc != NULL) + atk_object_set_description (obj, desc); + if (name != NULL) + atk_object_set_name (obj, name); +} + +static void +update_location_tiles (ClockData *cd) +{ + GList *l; + + for (l = cd->location_tiles; l; l = l->next) { + ClockLocationTile *tile; + + tile = CLOCK_LOCATION_TILE (l->data); + clock_location_tile_refresh (tile, FALSE); + } +} + +static char * +format_time (ClockData *cd) +{ + struct tm *tm; + char hour[256]; + char *utf8; + + utf8 = NULL; + + tm = localtime (&cd->current_time); + + if (cd->format == CLOCK_FORMAT_UNIX) { + if (use_two_line_format (cd)) { + utf8 = g_strdup_printf ("%lu\n%05lu", + (unsigned long)(cd->current_time / 100000L), + (unsigned long)(cd->current_time % 100000L)); + } else { + utf8 = g_strdup_printf ("%lu", + (unsigned long)cd->current_time); + } + } else if (cd->format == CLOCK_FORMAT_INTERNET) { + float itime = get_itime (cd->current_time); + if (cd->showseconds) + utf8 = g_strdup_printf ("@%3.2f", itime); + else + utf8 = g_strdup_printf ("@%3d", (unsigned int) itime); + } else if (cd->format == CLOCK_FORMAT_CUSTOM) { + char *timeformat = g_locale_from_utf8 (cd->custom_format, -1, + NULL, NULL, NULL); + if (!timeformat) + strcpy (hour, "???"); + else if (strftime (hour, sizeof (hour), timeformat, tm) <= 0) + strcpy (hour, "???"); + g_free (timeformat); + + utf8 = g_locale_to_utf8 (hour, -1, NULL, NULL, NULL); + } else { + if (strftime (hour, sizeof (hour), cd->timeformat, tm) <= 0) + strcpy (hour, "???"); + + utf8 = g_locale_to_utf8 (hour, -1, NULL, NULL, NULL); + } + + if (!utf8) + utf8 = g_strdup (hour); + + return utf8; +} + +static gchar * +format_time_24 (ClockData *cd) +{ + struct tm *tm; + gchar buf[128]; + + tm = localtime (&cd->current_time); + strftime (buf, sizeof (buf) - 1, "%k:%M:%S", tm); + return g_locale_to_utf8 (buf, -1, NULL, NULL, NULL); +} + +static void +update_clock (ClockData * cd) +{ + gboolean use_markup; + char *utf8; + + use_markup = FALSE; + + time (&cd->current_time); + utf8 = format_time (cd); + + use_markup = FALSE; + if (pango_parse_markup (utf8, -1, 0, NULL, NULL, NULL, NULL)) + use_markup = TRUE; + + if (use_markup) + gtk_label_set_markup (GTK_LABEL (cd->clockw), utf8); + else + gtk_label_set_text (GTK_LABEL (cd->clockw), utf8); + + g_free (utf8); + + update_orient (cd); + gtk_widget_queue_resize (cd->panel_button); + + update_tooltip (cd); + update_location_tiles (cd); + + if (cd->map_widget && cd->calendar_popup && gtk_widget_get_visible (cd->calendar_popup)) + clock_map_update_time (CLOCK_MAP (cd->map_widget)); + + if (cd->current_time_label && + gtk_widget_get_visible (cd->current_time_label)) { + utf8 = format_time_24 (cd); + gtk_label_set_text (GTK_LABEL (cd->current_time_label), utf8); + g_free (utf8); + } +} + +static void +update_tooltip (ClockData * cd) +{ + if (!cd->showdate) { + struct tm *tm; + char date[256]; + char *utf8, *loc; + char *zone; + time_t now_t; + struct tm now; + char *tip; + + tm = localtime (&cd->current_time); + + utf8 = NULL; + + /* Show date in tooltip. */ + /* Translators: This is a strftime format string. + * It is used to display a date. Please leave "%%s" as it is: + * it will be used to insert the timezone name later. */ + loc = g_locale_from_utf8 (_("%A %B %d (%%s)"), -1, NULL, NULL, NULL); + if (!loc) + strcpy (date, "???"); + else if (strftime (date, sizeof (date), loc, tm) <= 0) + strcpy (date, "???"); + g_free (loc); + + utf8 = g_locale_to_utf8 (date, -1, NULL, NULL, NULL); + + /* Add the timezone name */ + + tzset (); + time (&now_t); + localtime_r (&now_t, &now); + + if (now.tm_isdst > 0) { + zone = tzname[1]; + } else { + zone = tzname[0]; + } + + tip = g_strdup_printf (utf8, zone); + + gtk_widget_set_tooltip_text (cd->panel_button, tip); + g_free (utf8); + g_free (tip); + } else { +#ifdef HAVE_LIBECAL + if (cd->calendar_popup) + gtk_widget_set_tooltip_text (cd->panel_button, + _("Click to hide your appointments and tasks")); + else + gtk_widget_set_tooltip_text (cd->panel_button, + _("Click to view your appointments and tasks")); +#else + if (cd->calendar_popup) + gtk_widget_set_tooltip_text (cd->panel_button, + _("Click to hide month calendar")); + else + gtk_widget_set_tooltip_text (cd->panel_button, + _("Click to view month calendar")); +#endif + } +} + +static void +refresh_clock (ClockData *cd) +{ + unfix_size (cd); + update_clock (cd); +} + +static void +refresh_clock_timeout(ClockData *cd) +{ + unfix_size (cd); + + update_timeformat (cd); + + if (cd->timeout) + g_source_remove (cd->timeout); + + update_clock (cd); + + clock_set_timeout (cd, cd->current_time); +} + +/** + * This is like refresh_clock_timeout(), except that we only care about whether + * the time actually changed. We don't care about the format. + */ +static void +refresh_click_timeout_time_only (ClockData *cd) +{ + if (cd->timeout) + g_source_remove (cd->timeout); + clock_timeout_callback (cd); +} + +static void +free_locations (ClockData *cd) +{ + GList *l; + + for (l = cd->locations; l; l = l->next) + g_object_unref (l->data); + + g_list_free (cd->locations); + cd->locations = NULL; +} + +static void +destroy_clock (GtkWidget * widget, ClockData *cd) +{ + MateConfClient *client; + int i; + + client = mateconf_client_get_default (); + + for (i = 0; i < N_MATECONF_PREFS; i++) + mateconf_client_notify_remove ( + client, cd->listeners [i]); + + g_object_unref (G_OBJECT (client)); + + if (cd->timeout) + g_source_remove (cd->timeout); + cd->timeout = 0; + + if (cd->props) + gtk_widget_destroy (cd->props); + cd->props = NULL; + + if (cd->calendar_popup) + gtk_widget_destroy (cd->calendar_popup); + cd->calendar_popup = NULL; + + g_free (cd->timeformat); + g_free (cd->custom_format); + + free_locations (cd); + + g_list_free (cd->location_tiles); + cd->location_tiles = NULL; + + if (cd->systz) { + g_object_unref (cd->systz); + cd->systz = NULL; + } + + if (cd->cities_store) { + g_object_unref (cd->cities_store); + cd->cities_store = NULL; + } + + if (cd->builder) { + g_object_unref (cd->builder); + cd->builder = NULL; + } + + g_free (cd); + +#ifdef HAVE_LIBECAL + if (clock_numbers > 0) { + e_passwords_shutdown (); + clock_numbers--; + } +#endif +} + +static gboolean +close_on_escape (GtkWidget *widget, + GdkEventKey *event, + GtkToggleButton *toggle_button) +{ + if (event->keyval == GDK_Escape) { + gtk_toggle_button_set_active (toggle_button, FALSE); + return TRUE; + } + + return FALSE; +} + +static gboolean +delete_event (GtkWidget *widget, + GdkEvent *event, + GtkToggleButton *toggle_button) +{ + gtk_toggle_button_set_active (toggle_button, FALSE); + return TRUE; +} + +static void +edit_locations_cb (CalendarWindow *calwin, gpointer data) +{ + ClockData *cd; + + cd = data; + + display_properties_dialog (cd, TRUE); +} + +static GtkWidget * +create_calendar (ClockData *cd) +{ + GtkWidget *window; + char *prefs_dir; + + prefs_dir = mate_panel_applet_get_preferences_key (MATE_PANEL_APPLET (cd->applet)); + window = calendar_window_new (&cd->current_time, + prefs_dir, + cd->orient == MATE_PANEL_APPLET_ORIENT_UP); + g_free (prefs_dir); + + calendar_window_set_show_weeks (CALENDAR_WINDOW (window), + cd->showweek); + calendar_window_set_time_format (CALENDAR_WINDOW (window), + cd->format); + + gtk_window_set_screen (GTK_WINDOW (window), + gtk_widget_get_screen (cd->applet)); + + g_signal_connect (window, "edit-locations", + G_CALLBACK (edit_locations_cb), cd); + + g_signal_connect (window, "delete_event", + G_CALLBACK (delete_event), cd->panel_button); + g_signal_connect (window, "key_press_event", + G_CALLBACK (close_on_escape), cd->panel_button); + + return window; +} + +static void +position_calendar_popup (ClockData *cd) +{ + GtkRequisition req; + GtkAllocation allocation; + GdkScreen *screen; + GdkRectangle monitor; + GdkGravity gravity = GDK_GRAVITY_NORTH_WEST; + int button_w, button_h; + int x, y; + int w, h; + int i, n; + gboolean found_monitor = FALSE; + + /* Get root origin of the toggle button, and position above that. */ + gdk_window_get_origin (gtk_widget_get_window (cd->panel_button), + &x, &y); + + gtk_window_get_size (GTK_WINDOW (cd->calendar_popup), &w, &h); + gtk_widget_size_request (cd->calendar_popup, &req); + w = req.width; + h = req.height; + + gtk_widget_get_allocation (cd->panel_button, &allocation); + button_w = allocation.width; + button_h = allocation.height; + + screen = gtk_window_get_screen (GTK_WINDOW (cd->calendar_popup)); + + n = gdk_screen_get_n_monitors (screen); + for (i = 0; i < n; i++) { + gdk_screen_get_monitor_geometry (screen, i, &monitor); + if (x >= monitor.x && x <= monitor.x + monitor.width && + y >= monitor.y && y <= monitor.y + monitor.height) { + found_monitor = TRUE; + break; + } + } + + if (!found_monitor) { + /* eek, we should be on one of those xinerama + monitors */ + monitor.x = 0; + monitor.y = 0; + monitor.width = gdk_screen_get_width (screen); + monitor.height = gdk_screen_get_height (screen); + } + + /* Based on panel orientation, position the popup. + * Ignore window gravity since the window is undecorated. + * The orientations are all named backward from what + * I expected. + */ + switch (cd->orient) { + case MATE_PANEL_APPLET_ORIENT_RIGHT: + x += button_w; + if ((y + h) > monitor.y + monitor.height) + y -= (y + h) - (monitor.y + monitor.height); + + if ((y + h) > (monitor.height / 2)) + gravity = GDK_GRAVITY_SOUTH_WEST; + else + gravity = GDK_GRAVITY_NORTH_WEST; + + break; + case MATE_PANEL_APPLET_ORIENT_LEFT: + x -= w; + if ((y + h) > monitor.y + monitor.height) + y -= (y + h) - (monitor.y + monitor.height); + + if ((y + h) > (monitor.height / 2)) + gravity = GDK_GRAVITY_SOUTH_EAST; + else + gravity = GDK_GRAVITY_NORTH_EAST; + + break; + case MATE_PANEL_APPLET_ORIENT_DOWN: + y += button_h; + if ((x + w) > monitor.x + monitor.width) + x -= (x + w) - (monitor.x + monitor.width); + + gravity = GDK_GRAVITY_NORTH_WEST; + + break; + case MATE_PANEL_APPLET_ORIENT_UP: + y -= h; + if ((x + w) > monitor.x + monitor.width) + x -= (x + w) - (monitor.x + monitor.width); + + gravity = GDK_GRAVITY_SOUTH_WEST; + + break; + } + + gtk_window_move (GTK_WINDOW (cd->calendar_popup), x, y); + gtk_window_set_gravity (GTK_WINDOW (cd->calendar_popup), gravity); +} + +static void +add_to_group (GtkWidget *child, gpointer data) +{ + GtkSizeGroup *group = data; + + gtk_size_group_add_widget (group, child); +} + +static void +create_clock_window (ClockData *cd) +{ + GtkWidget *locations_box; + + locations_box = calendar_window_get_locations_box (CALENDAR_WINDOW (cd->calendar_popup)); + gtk_widget_show (locations_box); + + cd->clock_vbox = gtk_vbox_new (FALSE, 6); + gtk_container_add (GTK_CONTAINER (locations_box), cd->clock_vbox); + + cd->clock_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + gtk_size_group_set_ignore_hidden (cd->clock_group, FALSE); + + gtk_container_foreach (GTK_CONTAINER (locations_box), + (GtkCallback) add_to_group, + cd->clock_group); +} + +static gint +sort_locations_by_name (gconstpointer a, gconstpointer b) +{ + ClockLocation *loc_a = (ClockLocation *) a; + ClockLocation *loc_b = (ClockLocation *) b; + + const char *name_a = clock_location_get_display_name (loc_a); + const char *name_b = clock_location_get_display_name (loc_b); + + return strcmp (name_a, name_b); +} + +static void +create_cities_store (ClockData *cd) +{ + GtkTreeIter iter; + GList *cities = cd->locations; + GList *list = NULL; + + if (cd->cities_store) { + g_object_unref (G_OBJECT (cd->cities_store)); + cd->cities_store = NULL; + } + + /* City name, Timezone name, Coordinates in lat/long */ + cd->cities_store = g_object_ref (gtk_list_store_new (COL_CITY_LAST, + G_TYPE_STRING, /* COL_CITY_NAME */ + G_TYPE_STRING, /* COL_CITY_TZ */ + CLOCK_LOCATION_TYPE)); /* COL_CITY_LOC */ + + list = g_list_copy (cities); + list = g_list_sort (list, sort_locations_by_name); + + while (list) { + ClockLocation *loc = CLOCK_LOCATION (list->data); + + gtk_list_store_append (cd->cities_store, &iter); + gtk_list_store_set (cd->cities_store, &iter, + COL_CITY_NAME, clock_location_get_display_name (loc), + /* FIXME: translate the timezone */ + COL_CITY_TZ, clock_location_get_timezone (loc), + COL_CITY_LOC, loc, + -1); + + list = list->next; + } + + + if (cd->prefs_window) { + GtkWidget *widget = _clock_get_widget (cd, "cities_list"); + gtk_tree_view_set_model (GTK_TREE_VIEW (widget), + GTK_TREE_MODEL (cd->cities_store)); + } +} + +static gint +sort_locations_by_time (gconstpointer a, gconstpointer b) +{ + ClockLocation *loc_a = (ClockLocation *) a; + ClockLocation *loc_b = (ClockLocation *) b; + + struct tm tm_a; + struct tm tm_b; + gint ret; + + clock_location_localtime (loc_a, &tm_a); + clock_location_localtime (loc_b, &tm_b); + + ret = (tm_a.tm_year == tm_b.tm_year) ? 0 : 1; + if (ret) { + return (tm_a.tm_year < tm_b.tm_year) ? -1 : 1; + } + + ret = (tm_a.tm_mon == tm_b.tm_mon) ? 0 : 1; + if (ret) { + return (tm_a.tm_mon < tm_b.tm_mon) ? -1 : 1; + } + + ret = (tm_a.tm_mday == tm_b.tm_mday) ? 0 : 1; + if (ret) { + return (tm_a.tm_mday < tm_b.tm_mday) ? -1 : 1; + } + + ret = (tm_a.tm_hour == tm_b.tm_hour) ? 0 : 1; + if (ret) { + return (tm_a.tm_hour < tm_b.tm_hour) ? -1 : 1; + } + + ret = (tm_a.tm_min == tm_b.tm_min) ? 0 : 1; + if (ret) { + return (tm_a.tm_min < tm_b.tm_min) ? -1 : 1; + } + + ret = (tm_a.tm_sec == tm_b.tm_sec) ? 0 : 1; + if (ret) { + return (tm_a.tm_sec < tm_b.tm_sec) ? -1 : 1; + } + + return ret; +} + +static void +location_tile_pressed_cb (ClockLocationTile *tile, gpointer data) +{ + ClockData *cd = data; + ClockLocation *loc; + + loc = clock_location_tile_get_location (tile); + + clock_map_blink_location (CLOCK_MAP (cd->map_widget), loc); + + g_object_unref (loc); +} + +static ClockFormat +location_tile_need_clock_format_cb(ClockLocationTile *tile, gpointer data) +{ + ClockData *cd = data; + + return cd->format; +} + +static void +create_cities_section (ClockData *cd) +{ + GList *node; + ClockLocationTile *city; + GList *cities; + + if (cd->cities_section) { + gtk_widget_destroy (cd->cities_section); + cd->cities_section = NULL; + } + + g_list_free (cd->location_tiles); + cd->location_tiles = NULL; + + cd->cities_section = gtk_vbox_new (FALSE, 6); + gtk_container_set_border_width (GTK_CONTAINER (cd->cities_section), 0); + + cities = cd->locations; + if (g_list_length (cities) == 0) { + /* if the list is empty, don't bother showing the + cities section */ + gtk_widget_hide (cd->cities_section); + return; + } + + /* Copy the existing list, so we can sort it nondestructively */ + node = g_list_copy (cities); + node = g_list_sort (node, sort_locations_by_time); + node = g_list_reverse (node); + + while (node) { + ClockLocation *loc = node->data; + + city = clock_location_tile_new (loc, CLOCK_FACE_SMALL); + g_signal_connect (city, "tile-pressed", + G_CALLBACK (location_tile_pressed_cb), cd); + g_signal_connect (city, "need-clock-format", + G_CALLBACK (location_tile_need_clock_format_cb), cd); + + gtk_box_pack_start (GTK_BOX (cd->cities_section), + GTK_WIDGET (city), + FALSE, FALSE, 0); + + cd->location_tiles = g_list_prepend (cd->location_tiles, city); + + clock_location_tile_refresh (city, TRUE); + + node = g_list_next (node); + } + + g_list_free (node); + + gtk_box_pack_end (GTK_BOX (cd->clock_vbox), + cd->cities_section, FALSE, FALSE, 0); + + gtk_widget_show_all (cd->cities_section); +} + +static GList * +map_need_locations_cb (ClockMap *map, gpointer data) +{ + ClockData *cd = data; + + return cd->locations; +} + +static void +create_map_section (ClockData *cd) +{ + ClockMap *map; + + if (cd->map_widget) { + gtk_widget_destroy (GTK_WIDGET (cd->map_section)); + cd->map_widget = NULL; + } + + map = clock_map_new (); + g_signal_connect (map, "need-locations", + G_CALLBACK (map_need_locations_cb), cd); + + cd->map_section = gtk_alignment_new (0, 0, 1, 1); + cd->map_widget = GTK_WIDGET (map); + + gtk_container_add (GTK_CONTAINER (cd->map_section), cd->map_widget); + + gtk_alignment_set_padding (GTK_ALIGNMENT (cd->map_section), 1, 1, 1, 1); + + gtk_box_pack_start (GTK_BOX (cd->clock_vbox), cd->map_section, FALSE, FALSE, 0); + gtk_widget_show (cd->map_widget); + gtk_widget_show (cd->map_section); +} + +static void +update_calendar_popup (ClockData *cd) +{ + if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (cd->panel_button))) { + if (cd->calendar_popup) { + gtk_widget_destroy (cd->calendar_popup); + cd->calendar_popup = NULL; + cd->cities_section = NULL; + cd->map_section = NULL; + cd->map_widget = NULL; + cd->clock_vbox = NULL; + + g_list_free (cd->location_tiles); + cd->location_tiles = NULL; + } + update_tooltip (cd); + return; + } + + if (!cd->calendar_popup) { + cd->calendar_popup = create_calendar (cd); + g_object_add_weak_pointer (G_OBJECT (cd->calendar_popup), + (gpointer *) &cd->calendar_popup); + update_tooltip (cd); + + create_clock_window (cd); + create_cities_store (cd); + create_cities_section (cd); + create_map_section (cd); + } + + if (cd->calendar_popup && gtk_widget_get_realized (cd->panel_button)) { + calendar_window_refresh (CALENDAR_WINDOW (cd->calendar_popup)); + position_calendar_popup (cd); + gtk_window_present (GTK_WINDOW (cd->calendar_popup)); + } +} + +static void +toggle_calendar (GtkWidget *button, + ClockData *cd) +{ + /* if time is wrong, the user might try to fix it by clicking on the + * clock */ + refresh_click_timeout_time_only (cd); + update_calendar_popup (cd); +} + +static gboolean +do_not_eat_button_press (GtkWidget *widget, + GdkEventButton *event) +{ + if (event->button != 1) + g_signal_stop_emission_by_name (widget, "button_press_event"); + + return FALSE; +} + +/* Don't request smaller size then the last one we did, this avoids + jumping when proportional fonts are used. We must take care to + call "unfix_size" whenever options are changed or such where + we'd want to forget the fixed size */ +static void +clock_size_request (GtkWidget *clock, GtkRequisition *req, gpointer data) +{ + ClockData *cd = data; + + if (req->width > cd->fixed_width) + cd->fixed_width = req->width; + if (req->height > cd->fixed_height) + cd->fixed_height = req->height; + req->width = cd->fixed_width; + req->height = cd->fixed_height; +} + +static void +clock_update_text_gravity (GtkWidget *label) +{ + PangoLayout *layout; + PangoContext *context; + + layout = gtk_label_get_layout (GTK_LABEL (label)); + context = pango_layout_get_context (layout); + pango_context_set_base_gravity (context, PANGO_GRAVITY_AUTO); +} + +static inline void +force_no_focus_padding (GtkWidget *widget) +{ + static gboolean first_time = TRUE; + + if (first_time) { + gtk_rc_parse_string ("\n" + " style \"clock-applet-button-style\"\n" + " {\n" + " GtkWidget::focus-line-width=0\n" + " GtkWidget::focus-padding=0\n" + " }\n" + "\n" + " widget \"*.clock-applet-button\" style \"clock-applet-button-style\"\n" + "\n"); + first_time = FALSE; + } + + gtk_widget_set_name (widget, "clock-applet-button"); +} + +static GtkWidget * +create_main_clock_button (void) +{ + GtkWidget *button; + + button = gtk_toggle_button_new (); + gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE); + + force_no_focus_padding (button); + + return button; +} + +static GtkWidget * +create_main_clock_label (ClockData *cd) +{ + GtkWidget *label; + + label = gtk_label_new (NULL); + g_signal_connect (label, "size_request", + G_CALLBACK (clock_size_request), + cd); + g_signal_connect_swapped (label, "style_set", + G_CALLBACK (unfix_size), + cd); + gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER); + clock_update_text_gravity (label); + g_signal_connect (label, "screen-changed", + G_CALLBACK (clock_update_text_gravity), + NULL); + + return label; +} + +static gboolean +weather_tooltip (GtkWidget *widget, + gint x, + gint y, + gboolean keyboard_mode, + GtkTooltip *tooltip, + ClockData *cd) +{ + GList *locations, *l; + WeatherInfo *info; + + locations = cd->locations; + + for (l = locations; l; l = l->next) { + ClockLocation *location = l->data; + if (clock_location_is_current (location)) { + info = clock_location_get_weather_info (location); + if (!info || !weather_info_is_valid (info)) + continue; + + weather_info_setup_tooltip (info, location, tooltip, cd->format); + + return TRUE; + } + } + + return FALSE; +} + +static void +create_clock_widget (ClockData *cd) +{ +#ifdef HAVE_LIBECAL + clock_numbers++; + e_passwords_init (); +#endif + + /* Main toggle button */ + cd->panel_button = create_main_clock_button (); + g_signal_connect (cd->panel_button, "button_press_event", + G_CALLBACK (do_not_eat_button_press), NULL); + g_signal_connect (cd->panel_button, "toggled", + G_CALLBACK (toggle_calendar), cd); + g_signal_connect (G_OBJECT (cd->panel_button), "destroy", + G_CALLBACK (destroy_clock), + cd); + gtk_widget_show (cd->panel_button); + + /* Main orientable box */ + cd->main_obox = g_object_new (clock_box_get_type (), NULL); + gtk_box_set_spacing (GTK_BOX (cd->main_obox), 12); /* spacing between weather and time */ + gtk_container_add (GTK_CONTAINER (cd->panel_button), cd->main_obox); + gtk_widget_show (cd->main_obox); + + /* Weather orientable box */ + cd->weather_obox = g_object_new (clock_box_get_type (), NULL); + gtk_box_set_spacing (GTK_BOX (cd->weather_obox), 2); /* spacing between weather icon and temperature */ + gtk_box_pack_start (GTK_BOX (cd->main_obox), cd->weather_obox, FALSE, FALSE, 0); + gtk_widget_set_has_tooltip (cd->weather_obox, TRUE); + g_signal_connect (cd->weather_obox, "query-tooltip", + G_CALLBACK (weather_tooltip), cd); + + /* Weather widgets */ + cd->panel_weather_icon = gtk_image_new (); + gtk_box_pack_start (GTK_BOX (cd->weather_obox), cd->panel_weather_icon, FALSE, FALSE, 0); + + cd->panel_temperature_label = gtk_label_new (NULL); + gtk_box_pack_start (GTK_BOX (cd->weather_obox), cd->panel_temperature_label, FALSE, FALSE, 0); + + /* Main label for time display */ + cd->clockw = create_main_clock_label (cd); + gtk_box_pack_start (GTK_BOX (cd->main_obox), cd->clockw, FALSE, FALSE, 0); + gtk_widget_show (cd->clockw); + + /* Done! */ + + set_atk_name_description (GTK_WIDGET (cd->applet), NULL, + _("Computer Clock")); + + gtk_container_add (GTK_CONTAINER (cd->applet), cd->panel_button); + gtk_container_set_border_width (GTK_CONTAINER (cd->applet), 0); + + cd->props = NULL; + cd->orient = -1; + cd->size = mate_panel_applet_get_size (MATE_PANEL_APPLET (cd->applet)); + + update_panel_weather (cd); + + /* Refresh the clock so that it paints its first state */ + refresh_clock_timeout (cd); + applet_change_orient (MATE_PANEL_APPLET (cd->applet), + mate_panel_applet_get_orient (MATE_PANEL_APPLET (cd->applet)), + cd); +} + +static void +update_orient (ClockData *cd) +{ + const gchar *text; + int min_width; + GtkAllocation allocation; + gdouble new_angle; + gdouble angle; + + text = gtk_label_get_text (GTK_LABEL (cd->clockw)); + min_width = calculate_minimum_width (cd->panel_button, text); + gtk_widget_get_allocation (cd->panel_button, &allocation); + + if (cd->orient == MATE_PANEL_APPLET_ORIENT_LEFT && + min_width > allocation.width) + new_angle = 270; + else if (cd->orient == MATE_PANEL_APPLET_ORIENT_RIGHT && + min_width > allocation.width) + new_angle = 90; + else + new_angle = 0; + + angle = gtk_label_get_angle (GTK_LABEL (cd->clockw)); + if (angle != new_angle) { + unfix_size (cd); + gtk_label_set_angle (GTK_LABEL (cd->clockw), new_angle); + gtk_label_set_angle (GTK_LABEL (cd->panel_temperature_label), new_angle); + } +} + +/* this is when the panel orientation changes */ +static void +applet_change_orient (MatePanelApplet *applet, + MatePanelAppletOrient orient, + ClockData *cd) +{ + GtkOrientation o; + + if (orient == cd->orient) + return; + + cd->orient = orient; + + switch (cd->orient) { + case MATE_PANEL_APPLET_ORIENT_RIGHT: + o = GTK_ORIENTATION_VERTICAL; + break; + case MATE_PANEL_APPLET_ORIENT_LEFT: + o = GTK_ORIENTATION_VERTICAL; + break; + case MATE_PANEL_APPLET_ORIENT_DOWN: + o = GTK_ORIENTATION_HORIZONTAL; + break; + case MATE_PANEL_APPLET_ORIENT_UP: + o = GTK_ORIENTATION_HORIZONTAL; + break; + default: + g_assert_not_reached (); + return; + } + + gtk_orientable_set_orientation (GTK_ORIENTABLE (cd->main_obox), o); + gtk_orientable_set_orientation (GTK_ORIENTABLE (cd->weather_obox), o); + + unfix_size (cd); + update_clock (cd); + update_calendar_popup (cd); +} + +/* this is when the panel size changes */ +static void +panel_button_change_pixel_size (GtkWidget *widget, + GtkAllocation *allocation, + ClockData *cd) +{ + int new_size; + + if (cd->old_allocation.width == allocation->width && + cd->old_allocation.height == allocation->height) + return; + + cd->old_allocation.width = allocation->width; + cd->old_allocation.height = allocation->height; + + if (cd->orient == MATE_PANEL_APPLET_ORIENT_LEFT || + cd->orient == MATE_PANEL_APPLET_ORIENT_RIGHT) + new_size = allocation->width; + else + new_size = allocation->height; + + cd->size = new_size; + + unfix_size (cd); + update_timeformat (cd); + update_clock (cd); +} + +static void +copy_time (GtkAction *action, + ClockData *cd) +{ + char string[256]; + char *utf8; + + if (cd->format == CLOCK_FORMAT_UNIX) { + g_snprintf (string, sizeof(string), "%lu", + (unsigned long)cd->current_time); + } else if (cd->format == CLOCK_FORMAT_INTERNET) { + float itime = get_itime (cd->current_time); + if (cd->showseconds) + g_snprintf (string, sizeof (string), "@%3.2f", itime); + else + g_snprintf (string, sizeof (string), "@%3d", + (unsigned int) itime); + } else { + struct tm *tm; + char *format; + + if (cd->format == CLOCK_FORMAT_CUSTOM) { + format = g_locale_from_utf8 (cd->custom_format, -1, + NULL, NULL, NULL); + } else if (cd->format == CLOCK_FORMAT_12) { + if (cd->showseconds) + /* Translators: This is a strftime format + * string. + * It is used to display the time in 12-hours + * format with a leading 0 if needed (eg, like + * in the US: 08:10 am). The %p expands to + * am/pm. */ + format = g_locale_from_utf8 (_("%I:%M:%S %p"), -1, NULL, NULL, NULL); + else + /* Translators: This is a strftime format + * string. + * It is used to display the time in 12-hours + * format with a leading 0 if needed (eg, like + * in the US: 08:10 am). The %p expands to + * am/pm. */ + format = g_locale_from_utf8 (_("%I:%M %p"), -1, NULL, NULL, NULL); + } else { + if (cd->showseconds) + /* Translators: This is a strftime format + * string. + * It is used to display the time in 24-hours + * format (eg, like in France: 20:10). */ + format = g_locale_from_utf8 (_("%H:%M:%S"), -1, NULL, NULL, NULL); + else + /* Translators: This is a strftime format + * string. + * It is used to display the time in 24-hours + * format (eg, like in France: 20:10). */ + format = g_locale_from_utf8 (_("%H:%M"), -1, NULL, NULL, NULL); + } + + tm = localtime (&cd->current_time); + + if (!format) + strcpy (string, "???"); + else if (strftime (string, sizeof (string), format, tm) <= 0) + strcpy (string, "???"); + g_free (format); + } + + utf8 = g_locale_to_utf8 (string, -1, NULL, NULL, NULL); + gtk_clipboard_set_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY), + utf8, -1); + gtk_clipboard_set_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD), + utf8, -1); + g_free (utf8); +} + +static void +copy_date (GtkAction *action, + ClockData *cd) +{ + struct tm *tm; + char string[256]; + char *utf8, *loc; + + tm = localtime (&cd->current_time); + + /* Translators: This is a strftime format string. + * It is used to display a date in the full format (so that people can + * copy and paste it elsewhere). */ + loc = g_locale_from_utf8 (_("%A, %B %d %Y"), -1, NULL, NULL, NULL); + if (!loc) + strcpy (string, "???"); + else if (strftime (string, sizeof (string), loc, tm) <= 0) + strcpy (string, "???"); + g_free (loc); + + utf8 = g_locale_to_utf8 (string, -1, NULL, NULL, NULL); + gtk_clipboard_set_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY), + utf8, -1); + gtk_clipboard_set_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD), + utf8, -1); + g_free (utf8); +} + +static void +update_set_time_button (ClockData *cd) +{ + gint can_set; + + /* this returns more than just a boolean; check the documentation of + * the dbus method for more information */ + can_set = can_set_system_time (); + + if (cd->time_settings_button) + gtk_widget_set_sensitive (cd->time_settings_button, can_set); + + if (cd->set_time_button) { + gtk_widget_set_sensitive (cd->set_time_button, can_set != 0); + gtk_button_set_label (GTK_BUTTON (cd->set_time_button), + can_set == 1 ? + _("Set System Time...") : + _("Set System Time")); + } +} + +static void +set_time_callback (ClockData *cd, GError *error) +{ + GtkWidget *window; + GtkWidget *dialog; + + if (error) { + dialog = gtk_message_dialog_new (NULL, + 0, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + _("Failed to set the system time")); + + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", error->message); + g_signal_connect (dialog, "response", + G_CALLBACK (gtk_widget_destroy), NULL); + gtk_window_present (GTK_WINDOW (dialog)); + + g_error_free (error); + } + else + update_set_time_button (cd); + + window = _clock_get_widget (cd, "set-time-window"); + gtk_widget_hide (window); +} + +static void +set_time (GtkWidget *widget, ClockData *cd) +{ + struct tm t; + time_t tim; + guint year, month, day; + + time (&tim); + /* sets t.isdst -- we could set it to -1 to have mktime() guess the + * right value , but we don't know if this works with all libc */ + localtime_r (&tim, &t); + + t.tm_sec = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (cd->seconds_spin)); + t.tm_min = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (cd->minutes_spin)); + t.tm_hour = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (cd->hours_spin)); + gtk_calendar_get_date (GTK_CALENDAR (cd->calendar), &year, &month, &day); + t.tm_year = year - 1900; + t.tm_mon = month; + t.tm_mday = day; + + tim = mktime (&t); + + set_system_time_async (tim, (GFunc)set_time_callback, cd, NULL); +} + +static void +cancel_time_settings (GtkWidget *button, ClockData *cd) +{ + gtk_widget_hide (cd->set_time_window); + + refresh_click_timeout_time_only (cd); +} + +static gboolean +delete_time_settings (GtkWidget *widget, GdkEvent *event, gpointer data) +{ + cancel_time_settings (widget, data); + + return TRUE; +} + +static void +fill_time_settings_window (ClockData *cd) +{ + time_t now_t; + struct tm now; + + /* Fill the time settings */ + tzset (); + time (&now_t); + localtime_r (&now_t, &now); + + gtk_spin_button_set_value (GTK_SPIN_BUTTON (cd->seconds_spin), now.tm_sec); + gtk_spin_button_set_value (GTK_SPIN_BUTTON (cd->minutes_spin), now.tm_min); + gtk_spin_button_set_value (GTK_SPIN_BUTTON (cd->hours_spin), now.tm_hour); + + gtk_calendar_select_month (GTK_CALENDAR (cd->calendar), now.tm_mon, + now.tm_year + 1900); + gtk_calendar_select_day (GTK_CALENDAR (cd->calendar), now.tm_mday); +} + +static void +wrap_cb (GtkSpinButton *spin, ClockData *cd) +{ + gdouble value; + gdouble min, max; + GtkSpinType direction; + + value = gtk_spin_button_get_value (spin); + gtk_spin_button_get_range (spin, &min, &max); + + if (value == min) + direction = GTK_SPIN_STEP_FORWARD; + else + direction = GTK_SPIN_STEP_BACKWARD; + + if (spin == (GtkSpinButton *) cd->seconds_spin) + gtk_spin_button_spin (GTK_SPIN_BUTTON (cd->minutes_spin), direction, 1.0); + else if (spin == (GtkSpinButton *) cd->minutes_spin) + gtk_spin_button_spin (GTK_SPIN_BUTTON (cd->hours_spin), direction, 1.0); + else { + guint year, month, day; + GDate *date; + + gtk_calendar_get_date (GTK_CALENDAR (cd->calendar), &year, &month, &day); + + date = g_date_new_dmy (day, month + 1, year); + + if (direction == GTK_SPIN_STEP_FORWARD) + g_date_add_days (date, 1); + else + g_date_subtract_days (date, 1); + + year = g_date_get_year (date); + month = g_date_get_month (date) - 1; + day = g_date_get_day (date); + + gtk_calendar_select_month (GTK_CALENDAR (cd->calendar), month, year); + gtk_calendar_select_day (GTK_CALENDAR (cd->calendar), day); + + g_date_free (date); + } +} + +static gboolean +output_cb (GtkSpinButton *spin, + gpointer data) +{ + GtkAdjustment *adj; + gchar *text; + int value; + + adj = gtk_spin_button_get_adjustment (spin); + value = (int) gtk_adjustment_get_value (adj); + text = g_strdup_printf ("%02d", value); + gtk_entry_set_text (GTK_ENTRY (spin), text); + g_free (text); + + return TRUE; +} + +static void +ensure_time_settings_window_is_created (ClockData *cd) +{ + GtkWidget *cancel_button; + + if (cd->set_time_window) + return; + + cd->set_time_window = _clock_get_widget (cd, "set-time-window"); + g_signal_connect (cd->set_time_window, "delete_event", + G_CALLBACK (delete_time_settings), cd); + + cd->calendar = _clock_get_widget (cd, "calendar"); + cd->hours_spin = _clock_get_widget (cd, "hours_spin"); + cd->minutes_spin = _clock_get_widget (cd, "minutes_spin"); + cd->seconds_spin = _clock_get_widget (cd, "seconds_spin"); + + gtk_entry_set_width_chars (GTK_ENTRY (cd->hours_spin), 2); + gtk_entry_set_width_chars (GTK_ENTRY (cd->minutes_spin), 2); + gtk_entry_set_width_chars (GTK_ENTRY (cd->seconds_spin), 2); + gtk_entry_set_alignment (GTK_ENTRY (cd->hours_spin), 1.0); + gtk_entry_set_alignment (GTK_ENTRY (cd->minutes_spin), 1.0); + gtk_entry_set_alignment (GTK_ENTRY (cd->seconds_spin), 1.0); + g_signal_connect (cd->seconds_spin, "wrapped", G_CALLBACK (wrap_cb), cd); + g_signal_connect (cd->minutes_spin, "wrapped", G_CALLBACK (wrap_cb), cd); + g_signal_connect (cd->hours_spin, "wrapped", G_CALLBACK (wrap_cb), cd); + + g_signal_connect (cd->minutes_spin, "output", G_CALLBACK (output_cb), cd); + g_signal_connect (cd->seconds_spin, "output", G_CALLBACK (output_cb), cd); + + cd->set_time_button = _clock_get_widget (cd, "set-time-button"); + g_signal_connect (cd->set_time_button, "clicked", G_CALLBACK (set_time), cd); + + cancel_button = _clock_get_widget (cd, "cancel-set-time-button"); + g_signal_connect (cancel_button, "clicked", G_CALLBACK (cancel_time_settings), cd); + + cd->current_time_label = _clock_get_widget (cd, "current_time_label"); +} + +static void +run_time_settings (GtkWidget *unused, ClockData *cd) +{ + ensure_time_settings_window_is_created (cd); + fill_time_settings_window (cd); + + update_set_time_button (cd); + + gtk_window_present (GTK_WINDOW (cd->set_time_window)); + + refresh_click_timeout_time_only (cd); +} + +static void +config_date (GtkAction *action, + ClockData *cd) +{ + run_time_settings (NULL, cd); +} + +/* current timestamp */ +static const GtkActionEntry clock_menu_actions [] = { + { "ClockPreferences", GTK_STOCK_PROPERTIES, N_("_Preferences"), + NULL, NULL, + G_CALLBACK (verb_display_properties_dialog) }, + { "ClockHelp", GTK_STOCK_HELP, N_("_Help"), + NULL, NULL, + G_CALLBACK (display_help_dialog) }, + { "ClockAbout", GTK_STOCK_ABOUT, N_("_About"), + NULL, NULL, + G_CALLBACK (display_about_dialog) }, + { "ClockCopyTime", GTK_STOCK_COPY, N_("Copy _Time"), + NULL, NULL, + G_CALLBACK (copy_time) }, + { "ClockCopyDate", GTK_STOCK_COPY, N_("Copy _Date"), + NULL, NULL, + G_CALLBACK (copy_date) }, + { "ClockConfig", GTK_STOCK_PREFERENCES, N_("Ad_just Date & Time"), + NULL, NULL, + G_CALLBACK (config_date) } +}; + +static void +format_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + ClockData *clock) +{ + const char *value; + int new_format; + + if (!entry->value || entry->value->type != MATECONF_VALUE_STRING) + return; + + value = mateconf_value_get_string (entry->value); + if (!mateconf_string_to_enum (format_type_enum_map, value, &new_format)) + return; + + if (!clock->can_handle_format_12 && new_format == CLOCK_FORMAT_12) + new_format = CLOCK_FORMAT_24; + + if (new_format == clock->format) + return; + + clock->format = new_format; + refresh_clock_timeout (clock); + + if (clock->calendar_popup != NULL) { + calendar_window_set_time_format (CALENDAR_WINDOW (clock->calendar_popup), clock->format); + position_calendar_popup (clock); + } + +} + +static void +show_seconds_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + ClockData *clock) +{ + gboolean value; + + if (!entry->value || entry->value->type != MATECONF_VALUE_BOOL) + return; + + value = mateconf_value_get_bool (entry->value); + + clock->showseconds = (value != 0); + refresh_clock_timeout (clock); +} + +static void +show_date_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + ClockData *clock) +{ + gboolean value; + + if (!entry->value || entry->value->type != MATECONF_VALUE_BOOL) + return; + + value = mateconf_value_get_bool (entry->value); + + clock->showdate = (value != 0); + update_timeformat (clock); + refresh_clock (clock); +} + +static void +update_panel_weather (ClockData *cd) +{ + if (cd->show_weather) + gtk_widget_show (cd->panel_weather_icon); + else + gtk_widget_hide (cd->panel_weather_icon); + + if (cd->show_temperature) + gtk_widget_show (cd->panel_temperature_label); + else + gtk_widget_hide (cd->panel_temperature_label); + + if ((cd->show_weather || cd->show_temperature) && + g_list_length (cd->locations) > 0) + gtk_widget_show (cd->weather_obox); + else + gtk_widget_hide (cd->weather_obox); + + gtk_widget_queue_resize (cd->applet); +} + +static void +update_weather_bool_value_and_toggle_from_mateconf (ClockData *cd, MateConfEntry *entry, + gboolean *value_loc, const char *widget_name) +{ + GtkWidget *widget; + gboolean value; + + if (!entry->value || entry->value->type != MATECONF_VALUE_BOOL) + return; + + value = mateconf_value_get_bool (entry->value); + + *value_loc = (value != 0); + + widget = _clock_get_widget (cd, widget_name); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), + *value_loc); + + update_panel_weather (cd); +} + +static void +show_weather_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + ClockData *cd) +{ + update_weather_bool_value_and_toggle_from_mateconf (cd, entry, &cd->show_weather, "weather_check"); +} + +static void +show_temperature_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + ClockData *cd) +{ + update_weather_bool_value_and_toggle_from_mateconf (cd, entry, &cd->show_temperature, "temperature_check"); +} + +static void +location_weather_updated_cb (ClockLocation *location, + WeatherInfo *info, + gpointer data) +{ + ClockData *cd = data; + const gchar *icon_name; + const gchar *temp; + GtkIconTheme *theme; + GdkPixbuf *pixbuf; + + if (!info || !weather_info_is_valid (info)) + return; + + if (!clock_location_is_current (location)) + return; + + icon_name = weather_info_get_icon_name (info); + /* FIXME: mmh, screen please? Also, don't hardcode to 16 */ + theme = gtk_icon_theme_get_default (); + pixbuf = gtk_icon_theme_load_icon (theme, icon_name, 16, + GTK_ICON_LOOKUP_GENERIC_FALLBACK, NULL); + + temp = weather_info_get_temp_summary (info); + + gtk_image_set_from_pixbuf (GTK_IMAGE (cd->panel_weather_icon), pixbuf); + gtk_label_set_text (GTK_LABEL (cd->panel_temperature_label), temp); +} + +static void +location_set_current_cb (ClockLocation *loc, + gpointer data) +{ + ClockData *cd = data; + WeatherInfo *info; + + info = clock_location_get_weather_info (loc); + location_weather_updated_cb (loc, info, cd); + + if (cd->map_widget) + clock_map_refresh (CLOCK_MAP (cd->map_widget)); + update_location_tiles (cd); + save_cities_store (cd); +} + +static void +locations_changed (ClockData *cd) +{ + GList *l; + ClockLocation *loc; + glong id; + + if (!cd->locations) { + if (cd->weather_obox) + gtk_widget_hide (cd->weather_obox); + if (cd->panel_weather_icon) + gtk_image_set_from_pixbuf (GTK_IMAGE (cd->panel_weather_icon), + NULL); + if (cd->panel_temperature_label) + gtk_label_set_text (GTK_LABEL (cd->panel_temperature_label), + ""); + } else { + if (cd->weather_obox) + gtk_widget_show (cd->weather_obox); + } + + for (l = cd->locations; l; l = l->next) { + loc = l->data; + + id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (loc), "weather-updated")); + if (id == 0) { + id = g_signal_connect (loc, "weather-updated", + G_CALLBACK (location_weather_updated_cb), cd); + g_object_set_data (G_OBJECT (loc), "weather-updated", GINT_TO_POINTER (id)); + g_signal_connect (loc, "set-current", + G_CALLBACK (location_set_current_cb), cd); + } + } + + if (cd->map_widget) + clock_map_refresh (CLOCK_MAP (cd->map_widget)); + + if (cd->clock_vbox) + create_cities_section (cd); +} + + +static void +set_locations (ClockData *cd, GList *locations) +{ + free_locations (cd); + cd->locations = locations; + locations_changed (cd); +} + +typedef struct { + GList *cities; + ClockData *cd; +} LocationParserData; + +/* Parser for our serialized locations in mateconf */ +static void +location_start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error) +{ + ClockLocation *loc; + LocationParserData *data = user_data; + ClockData *cd = data->cd; + WeatherPrefs prefs; + const gchar *att_name; + + gchar *name = NULL; + gchar *city = NULL; + gchar *timezone = NULL; + gfloat latitude = 0.0; + gfloat longitude = 0.0; + gchar *code = NULL; + gboolean current = FALSE; + + int index = 0; + + prefs.temperature_unit = cd->temperature_unit; + prefs.speed_unit = cd->speed_unit; + + if (strcmp (element_name, "location") != 0) { + return; + } + + setlocale (LC_NUMERIC, "POSIX"); + + for (att_name = attribute_names[index]; att_name != NULL; + att_name = attribute_names[++index]) { + if (strcmp (att_name, "name") == 0) { + name = (gchar *)attribute_values[index]; + } else if (strcmp (att_name, "city") == 0) { + city = (gchar *)attribute_values[index]; + } else if (strcmp (att_name, "timezone") == 0) { + timezone = (gchar *)attribute_values[index]; + } else if (strcmp (att_name, "latitude") == 0) { + sscanf (attribute_values[index], "%f", &latitude); + } else if (strcmp (att_name, "longitude") == 0) { + sscanf (attribute_values[index], "%f", &longitude); + } else if (strcmp (att_name, "code") == 0) { + code = (gchar *)attribute_values[index]; + } + else if (strcmp (att_name, "current") == 0) { + if (strcmp (attribute_values[index], "true") == 0) { + current = TRUE; + } + } + } + + setlocale (LC_NUMERIC, ""); + + if ((!name && !city) || !timezone) { + return; + } + + /* migration from the old configuration, when name == city */ + if (!city) + city = name; + + loc = clock_location_find_and_ref (cd->locations, name, city, + timezone, latitude, longitude, code); + if (!loc) + loc = clock_location_new (name, city, timezone, + latitude, longitude, code, &prefs); + + if (current && clock_location_is_current_timezone (loc)) + clock_location_make_current (loc, GDK_WINDOW_XWINDOW (gtk_widget_get_window (cd->applet)), + NULL, NULL, NULL); + + data->cities = g_list_append (data->cities, loc); +} + +static GMarkupParser location_parser = { + location_start_element, NULL, NULL, NULL, NULL +}; + +static void +cities_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + ClockData *cd) +{ + LocationParserData data; + + GSList *cur = NULL; + + GMarkupParseContext *context; + + data.cities = NULL; + data.cd = cd; + + if (!entry->value || entry->value->type != MATECONF_VALUE_LIST) + return; + + context = g_markup_parse_context_new (&location_parser, 0, &data, NULL); + + cur = mateconf_value_get_list (entry->value); + + while (cur) { + const char *str = mateconf_value_get_string (cur->data); + g_markup_parse_context_parse (context, str, strlen (str), NULL); + + cur = cur->next; + } + + g_markup_parse_context_free (context); + + set_locations (cd, data.cities); + create_cities_store (cd); +} + +static void +update_temperature_combo (ClockData *cd) +{ + GtkWidget *widget; + int active_index; + + widget = _clock_get_widget (cd, "temperature_combo"); + + if (cd->use_temperature_default) + active_index = 0; + else + active_index = cd->temperature_unit - 1; + + gtk_combo_box_set_active (GTK_COMBO_BOX (widget), active_index); +} + +static void +update_weather_locations (ClockData *cd) +{ + GList *locations, *l; + WeatherPrefs prefs = { + FORECAST_STATE, + FALSE, + NULL, + TEMP_UNIT_CENTIGRADE, + SPEED_UNIT_MS, + PRESSURE_UNIT_MB, + DISTANCE_UNIT_KM + }; + + prefs.temperature_unit = cd->temperature_unit; + prefs.speed_unit = cd->speed_unit; + + locations = cd->locations; + + for (l = locations; l; l = l->next) { + clock_location_set_weather_prefs (l->data, &prefs); + } +} + +static void +clock_migrate_to_26 (ClockData *clock) +{ + gboolean unixtime; + gboolean internettime; + int hourformat; + + internettime = mate_panel_applet_mateconf_get_bool (MATE_PANEL_APPLET (clock->applet), + "internet_time", + NULL); + unixtime = mate_panel_applet_mateconf_get_bool (MATE_PANEL_APPLET (clock->applet), + "unix_time", + NULL); + hourformat = mate_panel_applet_mateconf_get_int (MATE_PANEL_APPLET (clock->applet), + "hour_format", + NULL); + + if (unixtime) + clock->format = CLOCK_FORMAT_UNIX; + else if (internettime) + clock->format = CLOCK_FORMAT_INTERNET; + else if (hourformat == 12) + clock->format = CLOCK_FORMAT_12; + else if (hourformat == 24) + clock->format = CLOCK_FORMAT_24; + + /* It's still possible that we have none of the old keys, in which case + * we're not migrating from 2.6, but the config is simply wrong. So + * don't set the format key in this case. */ + if (clock->format != CLOCK_FORMAT_INVALID) + mate_panel_applet_mateconf_set_string (MATE_PANEL_APPLET (clock->applet), + KEY_FORMAT, + mateconf_enum_to_string (format_type_enum_map, + clock->format), + NULL); +} + +static void +clock_timezone_changed (SystemTimezone *systz, + const char *new_tz, + ClockData *cd) +{ + /* This will refresh the current location */ + save_cities_store (cd); + + refresh_click_timeout_time_only (cd); +} + +static void +parse_and_set_temperature_string (const char *str, ClockData *cd) +{ + gint value = 0; + gboolean use_default = FALSE; + + value = mateweather_prefs_parse_temperature (str, &use_default); + + cd->use_temperature_default = use_default; + cd->temperature_unit = value; +} + +static void +parse_and_set_speed_string (const char *str, ClockData *cd) +{ + gint value = 0; + gboolean use_default = FALSE; + + value = mateweather_prefs_parse_speed (str, &use_default); + + cd->use_speed_default = use_default; + cd->speed_unit = value; +} + +static void +temperature_unit_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + ClockData *cd) +{ + const gchar *value; + + if (!entry->value || entry->value->type != MATECONF_VALUE_STRING) + return; + + value = mateconf_value_get_string (entry->value); + parse_and_set_temperature_string (value, cd); + update_temperature_combo (cd); + update_weather_locations (cd); +} + +static void +update_speed_combo (ClockData *cd) +{ + GtkWidget *widget; + int active_index; + + widget = _clock_get_widget (cd, "wind_speed_combo"); + + if (cd->use_speed_default) + active_index = 0; + else + active_index = cd->speed_unit - 1; + + gtk_combo_box_set_active (GTK_COMBO_BOX (widget), active_index); +} + +static void +speed_unit_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + ClockData *cd) +{ + const gchar *value; + + if (!entry->value || entry->value->type != MATECONF_VALUE_STRING) + return; + + value = mateconf_value_get_string (entry->value); + parse_and_set_speed_string (value, cd); + update_speed_combo (cd); + update_weather_locations (cd); +} + +static void +custom_format_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + ClockData *clock) +{ + const char *value; + + if (!entry->value || entry->value->type != MATECONF_VALUE_STRING) + return; + + value = mateconf_value_get_string (entry->value); + + g_free (clock->custom_format); + clock->custom_format = g_strdup (value); + + if (clock->format == CLOCK_FORMAT_CUSTOM) + refresh_clock (clock); +} + +static void +show_week_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + ClockData *clock) +{ + gboolean value; + + if (!entry->value || entry->value->type != MATECONF_VALUE_BOOL) + return; + + value = mateconf_value_get_bool (entry->value); + + if (clock->showweek == (value != 0)) + return; + + clock->showweek = (value != 0); + + if (clock->calendar_popup != NULL) { + calendar_window_set_show_weeks (CALENDAR_WINDOW (clock->calendar_popup), clock->showweek); + position_calendar_popup (clock); + } +} + +static guint +setup_mateconf_preference (ClockData *cd, MateConfClient *client, const char *key_name, MateConfClientNotifyFunc callback) +{ + char *key; + guint id; + + key = mate_panel_applet_mateconf_get_full_key (MATE_PANEL_APPLET (cd->applet), + key_name); + id = mateconf_client_notify_add (client, key, + callback, + cd, NULL, NULL); + g_free (key); + + return id; +} + +static void +setup_mateconf (ClockData *cd) +{ + struct { + const char *key_name; + MateConfClientNotifyFunc callback; + } prefs[] = { + { KEY_FORMAT, (MateConfClientNotifyFunc) format_changed }, + { KEY_SHOW_SECONDS, (MateConfClientNotifyFunc) show_seconds_changed }, + { KEY_SHOW_DATE, (MateConfClientNotifyFunc) show_date_changed }, + { KEY_SHOW_WEATHER, (MateConfClientNotifyFunc) show_weather_changed }, + { KEY_SHOW_TEMPERATURE, (MateConfClientNotifyFunc) show_temperature_changed }, + { KEY_CUSTOM_FORMAT, (MateConfClientNotifyFunc) custom_format_changed }, + { KEY_SHOW_WEEK, (MateConfClientNotifyFunc) show_week_changed }, + { KEY_CITIES, (MateConfClientNotifyFunc) cities_changed }, + { KEY_TEMPERATURE_UNIT, (MateConfClientNotifyFunc) temperature_unit_changed }, + { KEY_SPEED_UNIT, (MateConfClientNotifyFunc) speed_unit_changed }, + }; + + MateConfClient *client; + int i; + + client = mateconf_client_get_default (); + + for (i = 0; i < G_N_ELEMENTS (prefs); i++) + cd->listeners[i] = setup_mateconf_preference (cd, client, prefs[i].key_name, prefs[i].callback); + + g_object_unref (G_OBJECT (client)); +} + +static GList * +parse_mateconf_cities (ClockData *cd, GSList *values) +{ + GSList *cur = values; + LocationParserData data; + GMarkupParseContext *context; + + data.cities = NULL; + data.cd = cd; + + context = + g_markup_parse_context_new (&location_parser, 0, &data, NULL); + + while (cur) { + const char *str = (char *)cur->data; + g_markup_parse_context_parse (context, str, strlen(str), NULL); + + cur = cur->next; + } + + g_markup_parse_context_free (context); + + return data.cities; +} + +static void +load_mateconf_settings (ClockData *cd) +{ + MatePanelApplet *applet; + int format; + char *format_str; + char *value; + GError *error; + GSList *values = NULL; + GList *cities = NULL; + + applet = MATE_PANEL_APPLET (cd->applet); + + cd->format = CLOCK_FORMAT_INVALID; + + format_str = mate_panel_applet_mateconf_get_string (applet, KEY_FORMAT, NULL); + if (format_str && + mateconf_string_to_enum (format_type_enum_map, format_str, &format)) + cd->format = format; + else + clock_migrate_to_26 (cd); + + g_free (format_str); + + if (cd->format == CLOCK_FORMAT_INVALID) + cd->format = clock_locale_format (); + + cd->custom_format = mate_panel_applet_mateconf_get_string (applet, KEY_CUSTOM_FORMAT, NULL); + cd->showseconds = mate_panel_applet_mateconf_get_bool (applet, KEY_SHOW_SECONDS, NULL); + + error = NULL; + cd->showdate = mate_panel_applet_mateconf_get_bool (applet, KEY_SHOW_DATE, &error); + if (error) { + g_error_free (error); + /* if on a small screen don't show date by default */ + if (gdk_screen_width () <= 800) + cd->showdate = FALSE; + else + cd->showdate = TRUE; + } + + cd->show_weather = mate_panel_applet_mateconf_get_bool (applet, KEY_SHOW_WEATHER, NULL); + cd->show_temperature = mate_panel_applet_mateconf_get_bool (applet, KEY_SHOW_TEMPERATURE, NULL); + cd->showweek = mate_panel_applet_mateconf_get_bool (applet, KEY_SHOW_WEEK, NULL); + cd->timeformat = NULL; + + cd->can_handle_format_12 = (clock_locale_format () == CLOCK_FORMAT_12); + if (!cd->can_handle_format_12 && cd->format == CLOCK_FORMAT_12) + cd->format = CLOCK_FORMAT_24; + + value = mate_panel_applet_mateconf_get_string (applet, KEY_TEMPERATURE_UNIT, NULL); + parse_and_set_temperature_string (value, cd); + g_free (value); + + value = mate_panel_applet_mateconf_get_string (applet, KEY_SPEED_UNIT, NULL); + parse_and_set_speed_string (value, cd); + g_free (value); + + values = mate_panel_applet_mateconf_get_list (MATE_PANEL_APPLET (cd->applet), KEY_CITIES, + MATECONF_VALUE_STRING, NULL); + + if (g_slist_length (values) == 0) { + cities = NULL; + } else { + cities = parse_mateconf_cities (cd, values); + } + + set_locations (cd, cities); +} + +static gboolean +fill_clock_applet (MatePanelApplet *applet) +{ + ClockData *cd; + GtkActionGroup *action_group; + GtkAction *action; + gchar *ui_path; + char *filename; + GError *error; + + mate_panel_applet_add_preferences (applet, CLOCK_SCHEMA_DIR, NULL); + mate_panel_applet_set_flags (applet, MATE_PANEL_APPLET_EXPAND_MINOR); + + cd = g_new0 (ClockData, 1); + cd->fixed_width = -1; + cd->fixed_height = -1; + + cd->applet = GTK_WIDGET (applet); + + setup_mateconf (cd); + load_mateconf_settings (cd); + + cd->builder = gtk_builder_new (); + gtk_builder_set_translation_domain (cd->builder, GETTEXT_PACKAGE); + filename = g_build_filename (BUILDERDIR, "clock.ui", NULL); + + error = NULL; + gtk_builder_add_from_file (cd->builder, filename, &error); + if (error) { + g_warning ("Error loading \"%s\": %s", + filename, error->message); + g_error_free (error); + } + + g_free (filename); + + create_clock_widget (cd); + +#ifndef CLOCK_INPROCESS + gtk_window_set_default_icon_name (CLOCK_ICON); +#endif + gtk_widget_show (cd->applet); + + /* FIXME: Update this comment. */ + /* we have to bind change_orient before we do applet_widget_add + since we need to get an initial change_orient signal to set our + initial oriantation, and we get that during the _add call */ + g_signal_connect (G_OBJECT (cd->applet), + "change_orient", + G_CALLBACK (applet_change_orient), + cd); + + g_signal_connect (G_OBJECT (cd->panel_button), + "size_allocate", + G_CALLBACK (panel_button_change_pixel_size), + cd); + + mate_panel_applet_set_background_widget (MATE_PANEL_APPLET (cd->applet), + GTK_WIDGET (cd->applet)); + + action_group = gtk_action_group_new ("ClockApplet Menu Actions"); + gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE); + gtk_action_group_add_actions (action_group, + clock_menu_actions, + G_N_ELEMENTS (clock_menu_actions), + cd); + ui_path = g_build_filename (CLOCK_MENU_UI_DIR, "clock-menu.xml", NULL); + mate_panel_applet_setup_menu_from_file (MATE_PANEL_APPLET (cd->applet), + ui_path, action_group); + g_free (ui_path); + + if (mate_panel_applet_get_locked_down (MATE_PANEL_APPLET (cd->applet))) { + action = gtk_action_group_get_action (action_group, "ClockPreferences"); + gtk_action_set_visible (action, FALSE); + + action = gtk_action_group_get_action (action_group, "ClockConfig"); + gtk_action_set_visible (action, FALSE); + } + + cd->systz = system_timezone_new (); + g_signal_connect (cd->systz, "changed", + G_CALLBACK (clock_timezone_changed), cd); + + action = gtk_action_group_get_action (action_group, "ClockConfig"); + gtk_action_set_visible (action, can_set_system_time ()); + g_object_unref (action_group); + + return TRUE; +} + +/* FIXME old clock applet */ +#if 0 +static void +setup_writability_sensitivity (ClockData *clock, GtkWidget *w, GtkWidget *label, const char *key) +{ + /* FMQ: was used from old preferences dialog; fix this up */ + char *fullkey; + MateConfClient *client; + + client = mateconf_client_get_default (); + + fullkey = mate_panel_applet_mateconf_get_full_key + (MATE_PANEL_APPLET (clock->applet), key); + + if ( ! mateconf_client_key_is_writable (client, fullkey, NULL)) { + g_object_set_data (G_OBJECT (w), NEVER_SENSITIVE, + GINT_TO_POINTER (1)); + gtk_widget_set_sensitive (w, FALSE); + if (label != NULL) { + g_object_set_data (G_OBJECT (label), NEVER_SENSITIVE, + GINT_TO_POINTER (1)); + gtk_widget_set_sensitive (label, FALSE); + } + } + + g_free (fullkey); + + g_object_unref (G_OBJECT (client)); +} + +static void +update_properties_for_format (ClockData *cd, + GtkComboBox *combo, + ClockFormat format) +{ + + /* show the custom format things the first time we actually + * have a custom format set in MateConf, but after that don't + * unshow it if the format changes + */ + if (!cd->custom_format_shown && + (cd->format == CLOCK_FORMAT_CUSTOM || + (cd->custom_format && cd->custom_format [0]))) { + gtk_widget_show (cd->custom_hbox); + gtk_widget_show (cd->custom_label); + gtk_widget_show (cd->custom_entry); + + gtk_combo_box_append_text (combo, _("Custom format")); + + cd->custom_format_shown = TRUE; + } + + /* Some combinations of options do not make sense */ + switch (format) { + case CLOCK_FORMAT_12: + case CLOCK_FORMAT_24: + gtk_widget_set_sensitive (cd->showseconds_check, TRUE); + gtk_widget_set_sensitive (cd->showdate_check, TRUE); + gtk_widget_set_sensitive (cd->custom_entry, FALSE); + gtk_widget_set_sensitive (cd->custom_label, FALSE); + break; + case CLOCK_FORMAT_UNIX: + gtk_widget_set_sensitive (cd->showseconds_check, FALSE); + gtk_widget_set_sensitive (cd->showdate_check, FALSE); + gtk_widget_set_sensitive (cd->custom_entry, FALSE); + gtk_widget_set_sensitive (cd->custom_label, FALSE); + break; + case CLOCK_FORMAT_INTERNET: + gtk_widget_set_sensitive (cd->showseconds_check, TRUE); + gtk_widget_set_sensitive (cd->showdate_check, FALSE); + gtk_widget_set_sensitive (cd->custom_entry, FALSE); + gtk_widget_set_sensitive (cd->custom_label, FALSE); + break; + case CLOCK_FORMAT_CUSTOM: + gtk_widget_set_sensitive (cd->showseconds_check, FALSE); + gtk_widget_set_sensitive (cd->showdate_check, FALSE); + gtk_widget_set_sensitive (cd->custom_entry, TRUE); + gtk_widget_set_sensitive (cd->custom_label, TRUE); + break; + default: + g_assert_not_reached (); + break; + } +} + +static void +set_format_cb (GtkComboBox *combo, + ClockData *cd) +{ + /* FMQ: was used from old preferences dialog; fix this up */ + ClockFormat format; + + /* valid values begin from 1 */ + if (cd->can_handle_format_12) + format = gtk_combo_box_get_active (combo) + 1; + else + format = gtk_combo_box_get_active (combo) + 2; + + update_properties_for_format (cd, combo, format); + + if (cd->format != format) + mate_panel_applet_mateconf_set_string (MATE_PANEL_APPLET (cd->applet), + KEY_FORMAT, + mateconf_enum_to_string (format_type_enum_map, format), + NULL); +} +#endif + +static void +set_show_seconds_cb (GtkWidget *w, + ClockData *clock) +{ + mate_panel_applet_mateconf_set_bool (MATE_PANEL_APPLET (clock->applet), + KEY_SHOW_SECONDS, + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w)), + NULL); +} + +static void +set_show_date_cb (GtkWidget *w, + ClockData *clock) +{ + mate_panel_applet_mateconf_set_bool (MATE_PANEL_APPLET (clock->applet), + KEY_SHOW_DATE, + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w)), + NULL); +} + +static void +set_show_weather_cb (GtkWidget *w, + ClockData *clock) +{ + mate_panel_applet_mateconf_set_bool (MATE_PANEL_APPLET (clock->applet), + KEY_SHOW_WEATHER, + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w)), + NULL); +} + +static void +set_show_temperature_cb (GtkWidget *w, + ClockData *clock) +{ + mate_panel_applet_mateconf_set_bool (MATE_PANEL_APPLET (clock->applet), + KEY_SHOW_TEMPERATURE, + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w)), + NULL); +} + +#if 0 +static void +set_show_zones_cb (GtkWidget *w, + ClockData *clock) +{ + mate_panel_applet_mateconf_set_bool (MATE_PANEL_APPLET (clock->applet), + KEY_SHOW_ZONES, + GTK_TOGGLE_BUTTON (w)->active, + NULL); +} +#endif + +/* FIXME old clock applet */ +#if 0 +static void +set_custom_format_cb (GtkEntry *entry, + ClockData *cd) +{ + /* FMQ: was used from old preferences dialog; fix this up */ + const char *custom_format; + + custom_format = gtk_entry_get_text (entry); + mate_panel_applet_mateconf_set_string (MATE_PANEL_APPLET (cd->applet), + KEY_CUSTOM_FORMAT, custom_format, NULL); +} +#endif + +static void +prefs_locations_changed (GtkTreeSelection *selection, ClockData *cd) +{ + gint n; + + n = gtk_tree_selection_count_selected_rows (selection); + gtk_widget_set_sensitive (cd->prefs_location_edit_button, n > 0); + gtk_widget_set_sensitive (cd->prefs_location_remove_button, n > 0); +} + +static gchar * +loc_to_string (ClockLocation *loc) +{ + const gchar *name, *city; + gfloat latitude, longitude; + gchar *ret; + + name = clock_location_get_name (loc); + city = clock_location_get_city (loc); + clock_location_get_coords (loc, &latitude, &longitude); + + setlocale (LC_NUMERIC, "POSIX"); + + ret = g_markup_printf_escaped + ("<location name=\"%s\" city=\"%s\" timezone=\"%s\" latitude=\"%f\" longitude=\"%f\" code=\"%s\" current=\"%s\"/>", + name ? name : "", + city ? city : "", + clock_location_get_timezone (loc), + latitude, longitude, + clock_location_get_weather_code (loc), + clock_location_is_current (loc) ? "true" : "false"); + + setlocale (LC_NUMERIC, ""); + + return ret; +} + +static void +save_cities_store (ClockData *cd) +{ + ClockLocation *loc; + GList *node = cd->locations; + + GSList *root = NULL; + GSList *list = NULL; + + while (node) { + loc = CLOCK_LOCATION (node->data); + list = g_slist_prepend (list, loc_to_string (loc)); + node = node->next; + } + + list = g_slist_reverse (list); + mate_panel_applet_mateconf_set_list (MATE_PANEL_APPLET (cd->applet), + KEY_CITIES, MATECONF_VALUE_STRING, list, NULL); + + root = list; + + while (list) { + g_free (list->data); + list = g_slist_next (list); + } + + g_slist_free (root); +} + +static void +run_prefs_edit_save (GtkButton *button, ClockData *cd) +{ + GtkWidget *edit_window = _clock_get_widget (cd, "edit-location-window"); + + ClockLocation *loc = g_object_get_data (G_OBJECT (edit_window), "clock-location"); + + GtkWidget *lat_entry = _clock_get_widget (cd, "edit-location-latitude-entry"); + GtkWidget *lon_entry = _clock_get_widget (cd, "edit-location-longitude-entry"); + GtkWidget *lat_combo = _clock_get_widget (cd, "edit-location-latitude-combo"); + GtkWidget *lon_combo = _clock_get_widget (cd, "edit-location-longitude-combo"); + + const gchar *timezone, *weather_code; + gchar *city, *name; + + MateWeatherLocation *gloc; + gfloat lat = 0; + gfloat lon = 0; + + timezone = mateweather_timezone_menu_get_tzid (cd->zone_combo); + if (!timezone) { + edit_hide (NULL, cd); + return; + } + + city = NULL; + weather_code = NULL; + name = NULL; + + gloc = mateweather_location_entry_get_location (cd->location_entry); + if (gloc) { + city = mateweather_location_get_city_name (gloc); + weather_code = mateweather_location_get_code (gloc); + } + + if (mateweather_location_entry_has_custom_text (cd->location_entry)) { + name = gtk_editable_get_chars (GTK_EDITABLE (cd->location_entry), 0, -1); + } + + sscanf (gtk_entry_get_text (GTK_ENTRY (lat_entry)), "%f", &lat); + sscanf (gtk_entry_get_text (GTK_ENTRY (lon_entry)), "%f", &lon); + + if (gtk_combo_box_get_active (GTK_COMBO_BOX (lat_combo)) != 0) { + lat = -lat; + } + + if (gtk_combo_box_get_active (GTK_COMBO_BOX (lon_combo)) != 0) { + lon = -lon; + } + + if (loc) { + clock_location_set_timezone (loc, timezone); + clock_location_set_name (loc, name); + clock_location_set_city (loc, city); + clock_location_set_coords (loc, lat, lon); + clock_location_set_weather_code (loc, weather_code); + } else { + WeatherPrefs prefs; + + prefs.temperature_unit = cd->temperature_unit; + prefs.speed_unit = cd->speed_unit; + + loc = clock_location_new (name, city, timezone, lat, lon, weather_code, &prefs); + /* has the side-effect of setting the current location if + * there's none and this one can be considered as a current one + */ + clock_location_is_current (loc); + + cd->locations = g_list_append (cd->locations, loc); + } + g_free (name); + g_free (city); + + /* This will update everything related to locations to take into + * account the new location (via the mateconf notification) */ + save_cities_store (cd); + + edit_hide (edit_window, cd); +} + +static void +update_coords_helper (gfloat value, GtkWidget *entry, GtkWidget *combo) +{ + gchar *tmp; + + tmp = g_strdup_printf ("%f", fabsf (value)); + gtk_entry_set_text (GTK_ENTRY (entry), tmp); + g_free (tmp); + + if (value > 0) { + gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0); + } else { + gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 1); + } +} + +static void +update_coords (ClockData *cd, gboolean valid, gfloat lat, gfloat lon) +{ + GtkWidget *lat_entry = _clock_get_widget (cd, "edit-location-latitude-entry"); + GtkWidget *lon_entry = _clock_get_widget (cd, "edit-location-longitude-entry"); + GtkWidget *lat_combo = _clock_get_widget (cd, "edit-location-latitude-combo"); + GtkWidget *lon_combo = _clock_get_widget (cd, "edit-location-longitude-combo"); + + if (!valid) { + gtk_entry_set_text (GTK_ENTRY (lat_entry), ""); + gtk_entry_set_text (GTK_ENTRY (lon_entry), ""); + gtk_combo_box_set_active (GTK_COMBO_BOX (lat_combo), -1); + gtk_combo_box_set_active (GTK_COMBO_BOX (lon_combo), -1); + + return; + } + + update_coords_helper (lat, lat_entry, lat_combo); + update_coords_helper (lon, lon_entry, lon_combo); +} + +static void +fill_timezone_combo_from_location (ClockData *cd, ClockLocation *loc) +{ + if (loc != NULL) { + mateweather_timezone_menu_set_tzid (cd->zone_combo, + clock_location_get_timezone (loc)); + } else { + mateweather_timezone_menu_set_tzid (cd->zone_combo, NULL); + } +} + +static void +location_update_ok_sensitivity (ClockData *cd) +{ + GtkWidget *ok_button; + const gchar *timezone; + gchar *name; + + ok_button = _clock_get_widget (cd, "edit-location-ok-button"); + + timezone = mateweather_timezone_menu_get_tzid (cd->zone_combo); + name = gtk_editable_get_chars (GTK_EDITABLE (cd->location_entry), 0, -1); + + if (timezone && name && name[0] != '\0') { + gtk_widget_set_sensitive (ok_button, TRUE); + } else { + gtk_widget_set_sensitive (ok_button, FALSE); + } + + g_free (name); +} + +static void +location_changed (GObject *object, GParamSpec *param, ClockData *cd) +{ + MateWeatherLocationEntry *entry = MATEWEATHER_LOCATION_ENTRY (object); + MateWeatherLocation *gloc; + MateWeatherTimezone *zone; + gboolean latlon_valid; + double latitude = 0.0, longitude = 0.0; + + gloc = mateweather_location_entry_get_location (entry); + + latlon_valid = gloc && mateweather_location_has_coords (gloc); + if (latlon_valid) + mateweather_location_get_coords (gloc, &latitude, &longitude); + update_coords (cd, latlon_valid, latitude, longitude); + + zone = gloc ? mateweather_location_get_timezone (gloc) : NULL; + if (zone) + mateweather_timezone_menu_set_tzid (cd->zone_combo, mateweather_timezone_get_tzid (zone)); + else + mateweather_timezone_menu_set_tzid (cd->zone_combo, NULL); + + if (gloc) + mateweather_location_unref (gloc); +} + +static void +location_name_changed (GObject *object, ClockData *cd) +{ + location_update_ok_sensitivity (cd); +} + +static void +location_timezone_changed (GObject *object, GParamSpec *param, ClockData *cd) +{ + location_update_ok_sensitivity (cd); +} + +static void +edit_clear (ClockData *cd) +{ + GtkWidget *lat_entry = _clock_get_widget (cd, "edit-location-latitude-entry"); + GtkWidget *lon_entry = _clock_get_widget (cd, "edit-location-longitude-entry"); + GtkWidget *lat_combo = _clock_get_widget (cd, "edit-location-latitude-combo"); + GtkWidget *lon_combo = _clock_get_widget (cd, "edit-location-longitude-combo"); + + /* clear out the old data */ + mateweather_location_entry_set_location (cd->location_entry, NULL); + mateweather_timezone_menu_set_tzid (cd->zone_combo, NULL); + + gtk_entry_set_text (GTK_ENTRY (lat_entry), ""); + gtk_entry_set_text (GTK_ENTRY (lon_entry), ""); + + gtk_combo_box_set_active (GTK_COMBO_BOX (lat_combo), -1); + gtk_combo_box_set_active (GTK_COMBO_BOX (lon_combo), -1); +} + +static void +edit_hide (GtkWidget *unused, ClockData *cd) +{ + GtkWidget *edit_window = _clock_get_widget (cd, "edit-location-window"); + + gtk_widget_hide (edit_window); + edit_clear (cd); +} + +static gboolean +edit_delete (GtkWidget *unused, GdkEvent *event, ClockData *cd) +{ + edit_hide (unused, cd); + + return TRUE; +} + +static gboolean +edit_hide_event (GtkWidget *widget, GdkEvent *event, ClockData *cd) +{ + edit_hide (widget, cd); + + return TRUE; +} + +static void +prefs_hide (GtkWidget *widget, ClockData *cd) +{ + GtkWidget *tree; + + edit_hide (widget, cd); + + gtk_widget_hide (cd->prefs_window); + + tree = _clock_get_widget (cd, "cities_list"); + + gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree))); + + refresh_click_timeout_time_only (cd); +} + +static gboolean +prefs_hide_event (GtkWidget *widget, GdkEvent *event, ClockData *cd) +{ + prefs_hide (widget, cd); + + return TRUE; +} + +static void +prefs_help (GtkWidget *widget, ClockData *cd) +{ + clock_utils_display_help (cd->prefs_window, + "clock", "clock-settings"); +} + +static void +remove_tree_row (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) +{ + ClockData *cd = data; + ClockLocation *loc = NULL; + + gtk_tree_model_get (model, iter, COL_CITY_LOC, &loc, -1); + cd->locations = g_list_remove (cd->locations, loc); + g_object_unref (loc); + + /* This will update everything related to locations to take into + * account the removed location (via the mateconf notification) */ + save_cities_store (cd); +} + +static void +run_prefs_locations_remove (GtkButton *button, ClockData *cd) +{ + GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (cd->prefs_locations)); + + gtk_tree_selection_selected_foreach (sel, remove_tree_row, cd); +} + +static void +run_prefs_locations_add (GtkButton *button, ClockData *cd) +{ + GtkWidget *edit_window = _clock_get_widget (cd, "edit-location-window"); + + fill_timezone_combo_from_location (cd, NULL); + + g_object_set_data (G_OBJECT (edit_window), "clock-location", NULL); + gtk_window_set_title (GTK_WINDOW (edit_window), _("Choose Location")); + gtk_window_set_transient_for (GTK_WINDOW (edit_window), GTK_WINDOW (cd->prefs_window)); + + if (g_object_get_data (G_OBJECT (edit_window), "delete-handler") == NULL) { + g_object_set_data (G_OBJECT (edit_window), "delete-handler", + GINT_TO_POINTER (g_signal_connect (edit_window, "delete_event", G_CALLBACK (edit_delete), cd))); + } + + location_update_ok_sensitivity (cd); + + gtk_widget_grab_focus (GTK_WIDGET (cd->location_entry)); + gtk_editable_set_position (GTK_EDITABLE (cd->location_entry), -1); + + gtk_window_present_with_time (GTK_WINDOW (edit_window), gtk_get_current_event_time ()); +} + +static void +edit_tree_row (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) +{ + ClockData *cd = data; + ClockLocation *loc; + const char *name; + gchar *tmp; + gfloat lat, lon; + + /* fill the dialog with this location's data, show it */ + GtkWidget *edit_window = _clock_get_widget (cd, "edit-location-window"); + + GtkWidget *lat_entry = _clock_get_widget (cd, "edit-location-latitude-entry"); + + GtkWidget *lon_entry = _clock_get_widget (cd, "edit-location-longitude-entry"); + + GtkWidget *lat_combo = _clock_get_widget (cd, "edit-location-latitude-combo"); + + GtkWidget *lon_combo = _clock_get_widget (cd, "edit-location-longitude-combo"); + + edit_clear (cd); + + gtk_tree_model_get (model, iter, COL_CITY_LOC, &loc, -1); + + mateweather_location_entry_set_city (cd->location_entry, + clock_location_get_city (loc), + clock_location_get_weather_code (loc)); + name = clock_location_get_name (loc); + if (name && name[0]) { + gtk_entry_set_text (GTK_ENTRY (cd->location_entry), name); + } + + clock_location_get_coords (loc, &lat, &lon); + + fill_timezone_combo_from_location (cd, loc); + + tmp = g_strdup_printf ("%f", fabsf(lat)); + gtk_entry_set_text (GTK_ENTRY (lat_entry), tmp); + g_free (tmp); + + if (lat > 0) { + gtk_combo_box_set_active (GTK_COMBO_BOX (lat_combo), 0); + } else { + gtk_combo_box_set_active (GTK_COMBO_BOX (lat_combo), 1); + } + + tmp = g_strdup_printf ("%f", fabsf(lon)); + gtk_entry_set_text (GTK_ENTRY (lon_entry), tmp); + g_free (tmp); + + if (lon > 0) { + gtk_combo_box_set_active (GTK_COMBO_BOX (lon_combo), 0); + } else { + gtk_combo_box_set_active (GTK_COMBO_BOX (lon_combo), 1); + } + + location_update_ok_sensitivity (cd); + + g_object_set_data (G_OBJECT (edit_window), "clock-location", loc); + + gtk_widget_grab_focus (GTK_WIDGET (cd->location_entry)); + gtk_editable_set_position (GTK_EDITABLE (cd->location_entry), -1); + + gtk_window_set_title (GTK_WINDOW (edit_window), _("Edit Location")); + gtk_window_present (GTK_WINDOW (edit_window)); +} + +static void +run_prefs_locations_edit (GtkButton *unused, ClockData *cd) +{ + GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (cd->prefs_locations)); + + gtk_tree_selection_selected_foreach (sel, edit_tree_row, cd); +} + +static void +set_12hr_format_radio_cb (GtkWidget *widget, ClockData *cd) +{ + const gchar *val; + ClockFormat format; + + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) + format = CLOCK_FORMAT_12; + else + format = CLOCK_FORMAT_24; + + val = mateconf_enum_to_string (format_type_enum_map, format); + + mate_panel_applet_mateconf_set_string (MATE_PANEL_APPLET (cd->applet), + KEY_FORMAT, val, NULL); +} + +static void +temperature_combo_changed (GtkComboBox *combo, ClockData *cd) +{ + int value; + int old_value; + const gchar *str; + + value = gtk_combo_box_get_active (combo) + 1; + + if (cd->use_temperature_default) + old_value = TEMP_UNIT_DEFAULT; + else + old_value = cd->temperature_unit; + + if (value == old_value) + return; + + str = mateweather_prefs_temp_enum_to_string (value); + + mate_panel_applet_mateconf_set_string (MATE_PANEL_APPLET (cd->applet), + KEY_TEMPERATURE_UNIT, str, NULL); +} + +static void +speed_combo_changed (GtkComboBox *combo, ClockData *cd) +{ + int value; + int old_value; + const gchar *str; + + value = gtk_combo_box_get_active (combo) + 1; + + if (cd->use_speed_default) + old_value = SPEED_UNIT_DEFAULT; + else + old_value = cd->speed_unit; + + if (value == old_value) + return; + + str = mateweather_prefs_speed_enum_to_string (value); + + mate_panel_applet_mateconf_set_string (MATE_PANEL_APPLET (cd->applet), + KEY_SPEED_UNIT, str, NULL); +} + +static void +fill_prefs_window (ClockData *cd) +{ + static const int temperatures[] = { + TEMP_UNIT_DEFAULT, + TEMP_UNIT_KELVIN, + TEMP_UNIT_CENTIGRADE, + TEMP_UNIT_FAHRENHEIT, + -1 + }; + + static const int speeds[] = { + SPEED_UNIT_DEFAULT, + SPEED_UNIT_MS, + SPEED_UNIT_KPH, + SPEED_UNIT_MPH, + SPEED_UNIT_KNOTS, + SPEED_UNIT_BFT, + -1 + }; + + GtkWidget *radio_12hr; + GtkWidget *radio_24hr; + GtkWidget *widget; + GtkCellRenderer *renderer; + GtkTreeViewColumn *col; + GtkListStore *store; + int i; + + /* Set the 12 hour / 24 hour widget */ + radio_12hr = _clock_get_widget (cd, "12hr_radio"); + radio_24hr = _clock_get_widget (cd, "24hr_radio"); + + if (cd->format == CLOCK_FORMAT_12) + widget = radio_12hr; + else + widget = radio_24hr; + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE); + + g_signal_connect (radio_12hr, "toggled", + G_CALLBACK (set_12hr_format_radio_cb), cd); + + /* Set the "Show Date" checkbox */ + widget = _clock_get_widget (cd, "date_check"); + g_signal_connect (widget, "toggled", + G_CALLBACK (set_show_date_cb), cd); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), cd->showdate); + + /* Set the "Show Seconds" checkbox */ + widget = _clock_get_widget (cd, "seconds_check"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), cd->showseconds); + g_signal_connect (widget, "toggled", + G_CALLBACK (set_show_seconds_cb), cd); + + /* Set the "Show weather" checkbox */ + widget = _clock_get_widget (cd, "weather_check"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), cd->show_weather); + g_signal_connect (widget, "toggled", + G_CALLBACK (set_show_weather_cb), cd); + + /* Set the "Show temperature" checkbox */ + widget = _clock_get_widget (cd, "temperature_check"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), cd->show_temperature); + g_signal_connect (widget, "toggled", + G_CALLBACK (set_show_temperature_cb), cd); + + /* Fill the Cities list */ + widget = _clock_get_widget (cd, "cities_list"); + + renderer = gtk_cell_renderer_text_new (); + col = gtk_tree_view_column_new_with_attributes (_("City Name"), renderer, "text", COL_CITY_NAME, NULL); + gtk_tree_view_insert_column (GTK_TREE_VIEW (widget), col, -1); + + renderer = gtk_cell_renderer_text_new (); + col = gtk_tree_view_column_new_with_attributes (_("City Time Zone"), renderer, "text", COL_CITY_TZ, NULL); + gtk_tree_view_insert_column (GTK_TREE_VIEW (widget), col, -1); + + if (cd->cities_store == NULL) + create_cities_store (cd); + + gtk_tree_view_set_model (GTK_TREE_VIEW (widget), + GTK_TREE_MODEL (cd->cities_store)); + + /* Temperature combo */ + widget = _clock_get_widget (cd, "temperature_combo"); + store = gtk_list_store_new (1, G_TYPE_STRING); + gtk_combo_box_set_model (GTK_COMBO_BOX (widget), GTK_TREE_MODEL (store)); + renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (widget), renderer, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (widget), renderer, "text", 0, NULL); + + for (i = 0; temperatures[i] != -1; i++) + gtk_combo_box_append_text (GTK_COMBO_BOX (widget), + mateweather_prefs_get_temp_display_name (temperatures[i])); + + update_temperature_combo (cd); + g_signal_connect (widget, "changed", + G_CALLBACK (temperature_combo_changed), cd); + + /* Wind speed combo */ + widget = _clock_get_widget (cd, "wind_speed_combo"); + store = gtk_list_store_new (1, G_TYPE_STRING); + gtk_combo_box_set_model (GTK_COMBO_BOX (widget), GTK_TREE_MODEL (store)); + renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (widget), renderer, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (widget), renderer, "text", 0, NULL); + + for (i = 0; speeds[i] != -1; i++) + gtk_combo_box_append_text (GTK_COMBO_BOX (widget), + mateweather_prefs_get_speed_display_name (speeds[i])); + + update_speed_combo (cd); + g_signal_connect (widget, "changed", + G_CALLBACK (speed_combo_changed), cd); +} + +static void +ensure_prefs_window_is_created (ClockData *cd) +{ + GtkWidget *edit_window; + GtkWidget *prefs_close_button; + GtkWidget *prefs_help_button; + GtkWidget *clock_options; + GtkWidget *edit_cancel_button; + GtkWidget *edit_ok_button; + GtkWidget *location_box; + GtkWidget *zone_box; + GtkWidget *location_name_label; + GtkWidget *timezone_label; + GtkTreeSelection *selection; + MateWeatherLocation *world; + + if (cd->prefs_window) + return; + + cd->prefs_window = _clock_get_widget (cd, "prefs-window"); + + gtk_window_set_icon_name (GTK_WINDOW (cd->prefs_window), CLOCK_ICON); + + prefs_close_button = _clock_get_widget (cd, "prefs-close-button"); + prefs_help_button = _clock_get_widget (cd, "prefs-help-button"); + clock_options = _clock_get_widget (cd, "clock-options"); + cd->prefs_locations = GTK_TREE_VIEW (_clock_get_widget (cd, "cities_list")); + location_name_label = _clock_get_widget (cd, "location-name-label"); + timezone_label = _clock_get_widget (cd, "timezone-label"); + + + if (!clock_locale_supports_am_pm ()) + gtk_widget_hide (clock_options); + + selection = gtk_tree_view_get_selection (cd->prefs_locations); + g_signal_connect (G_OBJECT (selection), "changed", + G_CALLBACK (prefs_locations_changed), cd); + + g_signal_connect (G_OBJECT (cd->prefs_window), "delete_event", + G_CALLBACK (prefs_hide_event), cd); + + g_signal_connect (G_OBJECT (prefs_close_button), "clicked", + G_CALLBACK (prefs_hide), cd); + + g_signal_connect (G_OBJECT (prefs_help_button), "clicked", + G_CALLBACK (prefs_help), cd); + + cd->prefs_location_remove_button = _clock_get_widget (cd, "prefs-locations-remove-button"); + + g_signal_connect (G_OBJECT (cd->prefs_location_remove_button), "clicked", + G_CALLBACK (run_prefs_locations_remove), cd); + + cd->prefs_location_add_button = _clock_get_widget (cd, "prefs-locations-add-button"); + + g_signal_connect (G_OBJECT (cd->prefs_location_add_button), "clicked", + G_CALLBACK (run_prefs_locations_add), cd); + + cd->prefs_location_edit_button = _clock_get_widget (cd, "prefs-locations-edit-button"); + + g_signal_connect (G_OBJECT (cd->prefs_location_edit_button), "clicked", + G_CALLBACK (run_prefs_locations_edit), cd); + + edit_window = _clock_get_widget (cd, "edit-location-window"); + + gtk_window_set_transient_for (GTK_WINDOW (edit_window), + GTK_WINDOW (cd->prefs_window)); + + g_signal_connect (G_OBJECT (edit_window), "delete_event", + G_CALLBACK (edit_hide_event), cd); + + edit_cancel_button = _clock_get_widget (cd, "edit-location-cancel-button"); + + edit_ok_button = _clock_get_widget (cd, "edit-location-ok-button"); + + world = mateweather_location_new_world (FALSE); + + location_box = _clock_get_widget (cd, "edit-location-name-box"); + cd->location_entry = MATEWEATHER_LOCATION_ENTRY (mateweather_location_entry_new (world)); + gtk_widget_show (GTK_WIDGET (cd->location_entry)); + gtk_container_add (GTK_CONTAINER (location_box), GTK_WIDGET (cd->location_entry)); + gtk_label_set_mnemonic_widget (GTK_LABEL (location_name_label), + GTK_WIDGET (cd->location_entry)); + + g_signal_connect (G_OBJECT (cd->location_entry), "notify::location", + G_CALLBACK (location_changed), cd); + g_signal_connect (G_OBJECT (cd->location_entry), "changed", + G_CALLBACK (location_name_changed), cd); + + zone_box = _clock_get_widget (cd, "edit-location-timezone-box"); + cd->zone_combo = MATEWEATHER_TIMEZONE_MENU (mateweather_timezone_menu_new (world)); + gtk_widget_show (GTK_WIDGET (cd->zone_combo)); + gtk_container_add (GTK_CONTAINER (zone_box), GTK_WIDGET (cd->zone_combo)); + gtk_label_set_mnemonic_widget (GTK_LABEL (timezone_label), + GTK_WIDGET (cd->zone_combo)); + + g_signal_connect (G_OBJECT (cd->zone_combo), "notify::tzid", + G_CALLBACK (location_timezone_changed), cd); + + mateweather_location_unref (world); + + g_signal_connect (G_OBJECT (edit_cancel_button), "clicked", + G_CALLBACK (edit_hide), cd); + + g_signal_connect (G_OBJECT (edit_ok_button), "clicked", + G_CALLBACK (run_prefs_edit_save), cd); + + /* Set up the time setting section */ + + cd->time_settings_button = _clock_get_widget (cd, "time-settings-button"); + g_signal_connect (cd->time_settings_button, "clicked", + G_CALLBACK (run_time_settings), cd); + + /* fill it with the current preferences */ + fill_prefs_window (cd); +} + +static void +display_properties_dialog (ClockData *cd, gboolean start_in_locations_page) +{ + ensure_prefs_window_is_created (cd); + + if (start_in_locations_page) { + GtkWidget *notebook = _clock_get_widget (cd, "notebook"); + gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), 1); + } + + update_set_time_button (cd); + + gtk_window_set_screen (GTK_WINDOW (cd->prefs_window), + gtk_widget_get_screen (cd->applet)); + gtk_window_present (GTK_WINDOW (cd->prefs_window)); + + refresh_click_timeout_time_only (cd); + + /* FMQ: cd->props was the old preferences window; remove references to it */ + /* FMQ: connect to the Help button by hand; look at properties_response_cb() for the help code */ +#if 0 + /* FMQ: check the code below; replace the proper parts */ + GtkWidget *hbox; + GtkWidget *vbox; + GtkWidget *combo; + GtkWidget *label; + + gtk_combo_box_append_text (GTK_COMBO_BOX (combo), _("24 hour")); + gtk_combo_box_append_text (GTK_COMBO_BOX (combo), _("UNIX time")); + gtk_combo_box_append_text (GTK_COMBO_BOX (combo), _("Internet time")); + + gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, FALSE, 0); + gtk_widget_show (combo); + + cd->custom_hbox = gtk_hbox_new (FALSE, 12); + gtk_box_pack_start (GTK_BOX (vbox), cd->custom_hbox, TRUE, TRUE, 0); + + cd->custom_label = gtk_label_new_with_mnemonic (_("Custom _format:")); + gtk_label_set_use_markup (GTK_LABEL (cd->custom_label), TRUE); + gtk_label_set_justify (GTK_LABEL (cd->custom_label), + GTK_JUSTIFY_LEFT); + gtk_misc_set_alignment (GTK_MISC (cd->custom_label), 0, 0.5); + gtk_box_pack_start (GTK_BOX (cd->custom_hbox), + cd->custom_label, + FALSE, FALSE, 0); + + cd->custom_entry = gtk_entry_new (); + gtk_box_pack_start (GTK_BOX (cd->custom_hbox), + cd->custom_entry, + FALSE, FALSE, 0); + gtk_entry_set_text (GTK_ENTRY (cd->custom_entry), + cd->custom_format); + g_signal_connect (cd->custom_entry, "changed", + G_CALLBACK (set_custom_format_cb), + cd); + + g_signal_connect (cd->props, "destroy", + G_CALLBACK (gtk_widget_destroyed), + &cd->props); + g_signal_connect (cd->props, "response", + G_CALLBACK (properties_response_cb), + cd); + + cd->custom_format_shown = FALSE; + update_properties_for_format (cd, GTK_COMBO_BOX (combo), cd->format); + + /* valid values begin from 1 */ + if (cd->can_handle_format_12) + gtk_combo_box_set_active (GTK_COMBO_BOX (combo), + cd->format - 1); + else + gtk_combo_box_set_active (GTK_COMBO_BOX (combo), + cd->format - 2); + + g_signal_connect (combo, "changed", + G_CALLBACK (set_format_cb), cd); + + /* Now set up the sensitivity based on mateconf key writability */ + setup_writability_sensitivity (cd, combo, label, KEY_FORMAT); + setup_writability_sensitivity (cd, cd->custom_entry, cd->custom_label, + KEY_CUSTOM_FORMAT); + setup_writability_sensitivity (cd, cd->showseconds_check, NULL, KEY_SHOW_SECONDS); + setup_writability_sensitivity (cd, cd->showdate_check, NULL, KEY_SHOW_DATE); + + gtk_widget_show (cd->props); +#endif +} + +static void +verb_display_properties_dialog (GtkAction *action, + ClockData *cd) +{ + display_properties_dialog (cd, FALSE); +} + +static void +display_help_dialog (GtkAction *action, + ClockData *cd) +{ + clock_utils_display_help (cd->applet, "clock", NULL); +} + +static void display_about_dialog(GtkAction* action, ClockData* cd) +{ + static const gchar* authors[] = { + "George Lebl <[email protected]>", + "Gediminas Paulauskas <[email protected]>", + NULL + }; + + static const char* documenters[] = { + "Dan Mueth <[email protected]>", + NULL + }; + + char copyright[] = \ + "Copyright \xc2\xa9 1998-2004 Free Software Foundation, Inc."; + + gtk_show_about_dialog(NULL, + "program-name", _("Clock"), + "authors", authors, + "comments", _("The Clock displays the current time and date"), + "copyright", copyright, + "documenters", documenters, + "logo-icon-name", CLOCK_ICON, + "translator-credits", _("translator-credits"), + "version", VERSION, + "website", "http://matsusoft.com.ar/projects/mate/", + NULL); +} + +static gboolean +clock_factory (MatePanelApplet *applet, + const char *iid, + gpointer data) +{ + gboolean retval = FALSE; + + if (!strcmp (iid, "ClockApplet")) + retval = fill_clock_applet (applet); + + return retval; +} + +#ifdef CLOCK_INPROCESS +MATE_PANEL_APPLET_IN_PROCESS_FACTORY ("ClockAppletFactory", + PANEL_TYPE_APPLET, + "ClockApplet", + clock_factory, + NULL) +#else +MATE_PANEL_APPLET_OUT_PROCESS_FACTORY ("ClockAppletFactory", + PANEL_TYPE_APPLET, + "ClockApplet", + clock_factory, + NULL) +#endif |