/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* timezone-menu.c - Timezone-selecting menu
*
* Copyright 2008, Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* .
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#define MATEWEATHER_I_KNOW_THIS_IS_UNSTABLE
#include "timezone-menu.h"
#include "weather-priv.h"
#include
/**
* SECTION:timezone-menu
* @Title: MateWeatherTimezoneMenu
*
* A #GtkComboBox subclass for choosing a #MateWeatherTimezone
*/
G_DEFINE_TYPE (MateWeatherTimezoneMenu, mateweather_timezone_menu, GTK_TYPE_COMBO_BOX)
enum {
PROP_0,
PROP_TOP,
PROP_TZID,
LAST_PROP
};
static void set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec);
static void get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec);
static void changed (GtkComboBox *combo);
static GtkTreeModel *mateweather_timezone_model_new (MateWeatherLocation *top);
static gboolean row_separator_func (GtkTreeModel *model, GtkTreeIter *iter,
gpointer data);
static void is_sensitive (GtkCellLayout *cell_layout, GtkCellRenderer *cell,
GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data);
static void
mateweather_timezone_menu_init (MateWeatherTimezoneMenu *menu)
{
GtkCellRenderer *renderer;
gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (menu),
row_separator_func, NULL, NULL);
renderer = gtk_cell_renderer_text_new ();
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (menu), renderer, TRUE);
gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (menu), renderer,
"markup", 0,
NULL);
gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (menu),
renderer, is_sensitive, NULL, NULL);
}
static void
finalize (GObject *object)
{
MateWeatherTimezoneMenu *menu = MATEWEATHER_TIMEZONE_MENU (object);
if (menu->zone)
mateweather_timezone_unref (menu->zone);
G_OBJECT_CLASS (mateweather_timezone_menu_parent_class)->finalize (object);
}
static void
mateweather_timezone_menu_class_init (MateWeatherTimezoneMenuClass *timezone_menu_class)
{
GObjectClass *object_class = G_OBJECT_CLASS (timezone_menu_class);
GtkComboBoxClass *combo_class = GTK_COMBO_BOX_CLASS (timezone_menu_class);
object_class->finalize = finalize;
object_class->set_property = set_property;
object_class->get_property = get_property;
combo_class->changed = changed;
/* properties */
g_object_class_install_property (
object_class, PROP_TOP,
g_param_spec_pointer ("top",
"Top Location",
"The MateWeatherLocation whose children will be used to fill in the menu",
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (
object_class, PROP_TZID,
g_param_spec_string ("tzid",
"TZID",
"The selected TZID",
NULL,
G_PARAM_READWRITE));
}
static void
set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
GtkTreeModel *model;
switch (prop_id) {
case PROP_TOP:
model = mateweather_timezone_model_new (g_value_get_pointer (value));
gtk_combo_box_set_model (GTK_COMBO_BOX (object), model);
g_object_unref (model);
gtk_combo_box_set_active (GTK_COMBO_BOX (object), 0);
break;
case PROP_TZID:
mateweather_timezone_menu_set_tzid (MATEWEATHER_TIMEZONE_MENU (object),
g_value_get_string (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
MateWeatherTimezoneMenu *menu = MATEWEATHER_TIMEZONE_MENU (object);
switch (prop_id) {
case PROP_TZID:
g_value_set_string (value, mateweather_timezone_menu_get_tzid (menu));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
enum {
MATEWEATHER_TIMEZONE_MENU_NAME,
MATEWEATHER_TIMEZONE_MENU_ZONE
};
static void
changed (GtkComboBox *combo)
{
MateWeatherTimezoneMenu *menu = MATEWEATHER_TIMEZONE_MENU (combo);
GtkTreeIter iter;
if (menu->zone)
mateweather_timezone_unref (menu->zone);
gtk_combo_box_get_active_iter (combo, &iter);
gtk_tree_model_get (gtk_combo_box_get_model (combo), &iter,
MATEWEATHER_TIMEZONE_MENU_ZONE, &menu->zone,
-1);
if (menu->zone)
mateweather_timezone_ref (menu->zone);
g_object_notify (G_OBJECT (combo), "tzid");
}
static void
append_offset (GString *desc, int offset)
{
int hours, minutes;
hours = offset / 60;
minutes = (offset > 0) ? offset % 60 : -offset % 60;
if (minutes)
g_string_append_printf (desc, "GMT%+d:%02d", hours, minutes);
else if (hours)
g_string_append_printf (desc, "GMT%+d", hours);
else
g_string_append (desc, "GMT");
}
static char *
get_offset (MateWeatherTimezone *zone)
{
GString *desc;
desc = g_string_new (NULL);
append_offset (desc, mateweather_timezone_get_offset (zone));
if (mateweather_timezone_has_dst (zone)) {
g_string_append (desc, " / ");
append_offset (desc, mateweather_timezone_get_dst_offset (zone));
}
return g_string_free (desc, FALSE);
}
static void
insert_location (GtkTreeStore *store, MateWeatherTimezone *zone, const char *loc_name, GtkTreeIter *parent)
{
GtkTreeIter iter;
char *name, *offset;
offset = get_offset (zone);
name = g_strdup_printf ("%s (%s)",
loc_name ? loc_name : mateweather_timezone_get_name (zone),
offset);
gtk_tree_store_append (store, &iter, parent);
gtk_tree_store_set (store, &iter,
MATEWEATHER_TIMEZONE_MENU_NAME, name,
MATEWEATHER_TIMEZONE_MENU_ZONE, mateweather_timezone_ref (zone),
-1);
g_free (name);
g_free (offset);
}
static void
insert_locations (GtkTreeStore *store, MateWeatherLocation *loc)
{
int i;
if (mateweather_location_get_level (loc) < MATEWEATHER_LOCATION_COUNTRY) {
MateWeatherLocation **children;
children = mateweather_location_get_children (loc);
for (i = 0; children[i]; i++)
insert_locations (store, children[i]);
mateweather_location_free_children (loc, children);
} else {
MateWeatherTimezone **zones;
GtkTreeIter iter;
zones = mateweather_location_get_timezones (loc);
if (zones[1]) {
gtk_tree_store_append (store, &iter, NULL);
gtk_tree_store_set (store, &iter,
MATEWEATHER_TIMEZONE_MENU_NAME, mateweather_location_get_name (loc),
-1);
for (i = 0; zones[i]; i++) {
insert_location (store, zones[i], NULL, &iter);
}
} else if (zones[0]) {
insert_location (store, zones[0], mateweather_location_get_name (loc), NULL);
}
mateweather_location_free_timezones (loc, zones);
}
}
static GtkTreeModel *
mateweather_timezone_model_new (MateWeatherLocation *top)
{
GtkTreeStore *store;
GtkTreeModel *model;
GtkTreeIter iter;
char *unknown;
MateWeatherTimezone *utc;
store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_POINTER);
model = GTK_TREE_MODEL (store);
unknown = g_markup_printf_escaped ("%s", C_("timezone", "Unknown"));
gtk_tree_store_append (store, &iter, NULL);
gtk_tree_store_set (store, &iter,
MATEWEATHER_TIMEZONE_MENU_NAME, unknown,
MATEWEATHER_TIMEZONE_MENU_ZONE, NULL,
-1);
utc = mateweather_timezone_get_utc ();
if (utc) {
insert_location (store, utc, NULL, NULL);
mateweather_timezone_unref (utc);
}
gtk_tree_store_append (store, &iter, NULL);
g_free (unknown);
insert_locations (store, top);
return model;
}
static gboolean
row_separator_func (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
{
char *name;
gtk_tree_model_get (model, iter,
MATEWEATHER_TIMEZONE_MENU_NAME, &name,
-1);
if (name) {
g_free (name);
return FALSE;
} else
return TRUE;
}
static void
is_sensitive (GtkCellLayout *cell_layout, GtkCellRenderer *cell,
GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
{
gboolean sensitive;
sensitive = !gtk_tree_model_iter_has_child (tree_model, iter);
g_object_set (cell, "sensitive", sensitive, NULL);
}
/**
* mateweather_timezone_menu_new:
* @top: the top-level location for the menu.
*
* Creates a new #MateWeatherTimezoneMenu.
*
* @top will normally be a location returned from
* mateweather_location_new_world(), but you can create a menu that
* contains the timezones from a smaller set of locations if you want.
*
* Return value: the new #MateWeatherTimezoneMenu
**/
GtkWidget *
mateweather_timezone_menu_new (MateWeatherLocation *top)
{
return g_object_new (MATEWEATHER_TYPE_TIMEZONE_MENU,
"top", top,
NULL);
}
typedef struct {
GtkComboBox *combo;
const char *tzid;
} SetTimezoneData;
static gboolean
check_tzid (GtkTreeModel *model, GtkTreePath *path,
GtkTreeIter *iter, gpointer data)
{
SetTimezoneData *tzd = data;
MateWeatherTimezone *zone;
gtk_tree_model_get (model, iter,
MATEWEATHER_TIMEZONE_MENU_ZONE, &zone,
-1);
if (!zone)
return FALSE;
if (!strcmp (mateweather_timezone_get_tzid (zone), tzd->tzid)) {
gtk_combo_box_set_active_iter (tzd->combo, iter);
return TRUE;
} else
return FALSE;
}
/**
* mateweather_timezone_menu_set_tzid:
* @menu: a #MateWeatherTimezoneMenu
* @tzid: (allow-none): a tzdata id (eg, "America/New_York")
*
* Sets @menu to the given @tzid. If @tzid is %NULL, sets @menu to
* "Unknown".
**/
void
mateweather_timezone_menu_set_tzid (MateWeatherTimezoneMenu *menu,
const char *tzid)
{
SetTimezoneData tzd;
g_return_if_fail (MATEWEATHER_IS_TIMEZONE_MENU (menu));
if (!tzid) {
gtk_combo_box_set_active (GTK_COMBO_BOX (menu), 0);
return;
}
tzd.combo = GTK_COMBO_BOX (menu);
tzd.tzid = tzid;
gtk_tree_model_foreach (gtk_combo_box_get_model (tzd.combo),
check_tzid, &tzd);
}
/**
* mateweather_timezone_menu_get_tzid:
* @menu: a #MateWeatherTimezoneMenu
*
* Gets @menu's timezone id.
*
* Return value: (allow-none): @menu's tzid, or %NULL if no timezone
* is selected.
**/
const char *
mateweather_timezone_menu_get_tzid (MateWeatherTimezoneMenu *menu)
{
g_return_val_if_fail (MATEWEATHER_IS_TIMEZONE_MENU (menu), NULL);
if (!menu->zone)
return NULL;
return mateweather_timezone_get_tzid (menu->zone);
}