2019-10-07 02:46:11 +00:00
|
|
|
|
/* Lists/Weather
|
|
|
|
|
*
|
|
|
|
|
* This demo shows a few of the rarer features of GtkListView and
|
|
|
|
|
* how they can be used to display weather information.
|
|
|
|
|
*
|
|
|
|
|
* The hourly weather info uses a horizontal listview. This is easy
|
|
|
|
|
* to achieve because GtkListView implements the GtkOrientable interface.
|
|
|
|
|
* To make the items in the list stand out more, the listview uses
|
|
|
|
|
* separators.
|
|
|
|
|
*
|
|
|
|
|
* A GtkNoSelectionModel is used to make sure no item in the list can be
|
|
|
|
|
* selected. All other interactions with the items is still possible.
|
2020-05-29 17:25:36 +00:00
|
|
|
|
*
|
2020-06-07 02:05:52 +00:00
|
|
|
|
* The dataset used here has 70 000 items.
|
2019-10-07 02:46:11 +00:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
|
|
|
|
|
|
#define GTK_TYPE_WEATHER_INFO (gtk_weather_info_get_type ())
|
|
|
|
|
|
|
|
|
|
G_DECLARE_FINAL_TYPE (GtkWeatherInfo, gtk_weather_info, GTK, WEATHER_INFO, GObject)
|
|
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
|
GTK_WEATHER_CLEAR,
|
|
|
|
|
GTK_WEATHER_FEW_CLOUDS,
|
|
|
|
|
GTK_WEATHER_FOG,
|
|
|
|
|
GTK_WEATHER_OVERCAST,
|
|
|
|
|
GTK_WEATHER_SCATTERED_SHOWERS,
|
|
|
|
|
GTK_WEATHER_SHOWERS,
|
|
|
|
|
GTK_WEATHER_SNOW,
|
|
|
|
|
GTK_WEATHER_STORM
|
|
|
|
|
} GtkWeatherType;
|
|
|
|
|
|
|
|
|
|
struct _GtkWeatherInfo
|
|
|
|
|
{
|
|
|
|
|
GObject parent_instance;
|
|
|
|
|
|
|
|
|
|
gint64 timestamp;
|
|
|
|
|
int temperature;
|
|
|
|
|
GtkWeatherType weather_type;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct _GtkWeatherInfoClass
|
|
|
|
|
{
|
|
|
|
|
GObjectClass parent_class;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
G_DEFINE_TYPE (GtkWeatherInfo, gtk_weather_info, G_TYPE_OBJECT)
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
gtk_weather_info_class_init (GtkWeatherInfoClass *klass)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
gtk_weather_info_init (GtkWeatherInfo *self)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GtkWeatherInfo *
|
|
|
|
|
gtk_weather_info_new (GDateTime *timestamp,
|
|
|
|
|
GtkWeatherInfo *copy_from)
|
|
|
|
|
{
|
|
|
|
|
GtkWeatherInfo *result;
|
|
|
|
|
|
|
|
|
|
result = g_object_new (GTK_TYPE_WEATHER_INFO, NULL);
|
|
|
|
|
|
|
|
|
|
result->timestamp = g_date_time_to_unix (timestamp);
|
|
|
|
|
if (copy_from)
|
|
|
|
|
{
|
|
|
|
|
result->temperature = copy_from->temperature;
|
|
|
|
|
result->weather_type = copy_from->weather_type;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GDateTime *
|
|
|
|
|
parse_timestamp (const char *string,
|
2020-06-22 09:04:18 +00:00
|
|
|
|
GTimeZone *_tz)
|
2019-10-07 02:46:11 +00:00
|
|
|
|
{
|
|
|
|
|
char *with_seconds;
|
|
|
|
|
GDateTime *result;
|
|
|
|
|
|
|
|
|
|
with_seconds = g_strconcat (string, ":00", NULL);
|
2020-06-22 09:04:18 +00:00
|
|
|
|
result = g_date_time_new_from_iso8601 (with_seconds, _tz);
|
2019-10-07 02:46:11 +00:00
|
|
|
|
g_free (with_seconds);
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GtkWeatherType
|
|
|
|
|
parse_weather_type (const char *clouds,
|
|
|
|
|
const char *precip,
|
|
|
|
|
GtkWeatherType fallback)
|
|
|
|
|
{
|
|
|
|
|
if (strstr (precip, "SN"))
|
|
|
|
|
return GTK_WEATHER_SNOW;
|
|
|
|
|
|
|
|
|
|
if (strstr (precip, "TS"))
|
|
|
|
|
return GTK_WEATHER_STORM;
|
|
|
|
|
|
|
|
|
|
if (strstr (precip, "DZ"))
|
|
|
|
|
return GTK_WEATHER_SCATTERED_SHOWERS;
|
|
|
|
|
|
|
|
|
|
if (strstr (precip, "SH") || strstr (precip, "RA"))
|
|
|
|
|
return GTK_WEATHER_SHOWERS;
|
|
|
|
|
|
|
|
|
|
if (strstr (precip, "FG"))
|
|
|
|
|
return GTK_WEATHER_FOG;
|
|
|
|
|
|
|
|
|
|
if (g_str_equal (clouds, "M") ||
|
|
|
|
|
g_str_equal (clouds, ""))
|
|
|
|
|
return fallback;
|
|
|
|
|
|
|
|
|
|
if (strstr (clouds, "OVC") ||
|
|
|
|
|
strstr (clouds, "BKN"))
|
|
|
|
|
return GTK_WEATHER_OVERCAST;
|
|
|
|
|
|
|
|
|
|
if (strstr (clouds, "BKN") ||
|
|
|
|
|
strstr (clouds, "SCT"))
|
|
|
|
|
return GTK_WEATHER_FEW_CLOUDS;
|
|
|
|
|
|
|
|
|
|
if (strstr (clouds, "VV"))
|
|
|
|
|
return GTK_WEATHER_FOG;
|
|
|
|
|
|
|
|
|
|
return GTK_WEATHER_CLEAR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static double
|
|
|
|
|
parse_temperature (const char *s,
|
|
|
|
|
double fallback)
|
|
|
|
|
{
|
|
|
|
|
char *endptr;
|
|
|
|
|
double d;
|
|
|
|
|
|
|
|
|
|
d = g_ascii_strtod (s, &endptr);
|
|
|
|
|
if (*endptr != '\0')
|
|
|
|
|
return fallback;
|
|
|
|
|
|
|
|
|
|
return d;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GListModel *
|
|
|
|
|
create_weather_model (void)
|
|
|
|
|
{
|
|
|
|
|
GListStore *store;
|
|
|
|
|
GTimeZone *utc;
|
|
|
|
|
GDateTime *timestamp;
|
|
|
|
|
GtkWeatherInfo *info;
|
|
|
|
|
GBytes *data;
|
|
|
|
|
char **lines;
|
|
|
|
|
guint i;
|
|
|
|
|
|
|
|
|
|
store = g_list_store_new (GTK_TYPE_WEATHER_INFO);
|
|
|
|
|
data = g_resources_lookup_data ("/listview_weather/listview_weather.txt", 0, NULL);
|
|
|
|
|
lines = g_strsplit (g_bytes_get_data (data, NULL), "\n", 0);
|
|
|
|
|
|
|
|
|
|
utc = g_time_zone_new_utc ();
|
|
|
|
|
timestamp = g_date_time_new (utc, 2011, 1, 1, 0, 0, 0);
|
|
|
|
|
info = gtk_weather_info_new (timestamp, NULL);
|
|
|
|
|
g_list_store_append (store, info);
|
2020-06-20 16:18:17 +00:00
|
|
|
|
g_object_unref (info);
|
2019-10-07 02:46:11 +00:00
|
|
|
|
|
|
|
|
|
for (i = 0; lines[i] != NULL && *lines[i]; i++)
|
|
|
|
|
{
|
|
|
|
|
char **fields;
|
|
|
|
|
GDateTime *date;
|
|
|
|
|
|
|
|
|
|
fields = g_strsplit (lines[i], ",", 0);
|
|
|
|
|
date = parse_timestamp (fields[0], utc);
|
|
|
|
|
while (g_date_time_difference (date, timestamp) > 30 * G_TIME_SPAN_MINUTE)
|
|
|
|
|
{
|
|
|
|
|
GDateTime *new_timestamp = g_date_time_add_hours (timestamp, 1);
|
|
|
|
|
g_date_time_unref (timestamp);
|
|
|
|
|
timestamp = new_timestamp;
|
|
|
|
|
info = gtk_weather_info_new (timestamp, info);
|
|
|
|
|
g_list_store_append (store, info);
|
2020-06-20 16:18:17 +00:00
|
|
|
|
g_object_unref (info);
|
2019-10-07 02:46:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
info->temperature = parse_temperature (fields[1], info->temperature);
|
|
|
|
|
info->weather_type = parse_weather_type (fields[2], fields[3], info->weather_type);
|
|
|
|
|
g_date_time_unref (date);
|
|
|
|
|
g_strfreev (fields);
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-20 16:18:17 +00:00
|
|
|
|
g_date_time_unref (timestamp);
|
2019-10-07 02:46:11 +00:00
|
|
|
|
g_strfreev (lines);
|
|
|
|
|
g_bytes_unref (data);
|
|
|
|
|
g_time_zone_unref (utc);
|
|
|
|
|
|
|
|
|
|
return G_LIST_MODEL (store);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2020-06-29 20:37:14 +00:00
|
|
|
|
setup_widget (GtkSignalListItemFactory *factory,
|
|
|
|
|
GtkListItem *list_item)
|
2019-10-07 02:46:11 +00:00
|
|
|
|
{
|
|
|
|
|
GtkWidget *box, *child;
|
|
|
|
|
|
|
|
|
|
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
|
|
|
|
gtk_list_item_set_child (list_item, box);
|
|
|
|
|
|
|
|
|
|
child = gtk_label_new (NULL);
|
|
|
|
|
gtk_label_set_width_chars (GTK_LABEL (child), 5);
|
|
|
|
|
gtk_box_append (GTK_BOX (box), child);
|
|
|
|
|
|
|
|
|
|
child = gtk_image_new ();
|
|
|
|
|
gtk_image_set_icon_size (GTK_IMAGE (child), GTK_ICON_SIZE_LARGE);
|
|
|
|
|
gtk_box_append (GTK_BOX (box), child);
|
|
|
|
|
|
|
|
|
|
child = gtk_label_new (NULL);
|
|
|
|
|
gtk_widget_set_vexpand (child, TRUE);
|
|
|
|
|
gtk_widget_set_valign (child, GTK_ALIGN_END);
|
|
|
|
|
gtk_label_set_width_chars (GTK_LABEL (child), 4);
|
|
|
|
|
gtk_box_append (GTK_BOX (box), child);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2020-06-29 20:37:14 +00:00
|
|
|
|
bind_widget (GtkSignalListItemFactory *factory,
|
|
|
|
|
GtkListItem *list_item)
|
2019-10-07 02:46:11 +00:00
|
|
|
|
{
|
|
|
|
|
GtkWidget *box, *child;
|
|
|
|
|
GtkWeatherInfo *info;
|
|
|
|
|
GDateTime *timestamp;
|
|
|
|
|
char *s;
|
|
|
|
|
|
|
|
|
|
box = gtk_list_item_get_child (list_item);
|
|
|
|
|
info = gtk_list_item_get_item (list_item);
|
|
|
|
|
|
|
|
|
|
child = gtk_widget_get_first_child (box);
|
|
|
|
|
timestamp = g_date_time_new_from_unix_utc (info->timestamp);
|
|
|
|
|
s = g_date_time_format (timestamp, "%R");
|
|
|
|
|
gtk_label_set_text (GTK_LABEL (child), s);
|
|
|
|
|
g_free (s);
|
|
|
|
|
g_date_time_unref (timestamp);
|
|
|
|
|
|
|
|
|
|
child = gtk_widget_get_next_sibling (child);
|
|
|
|
|
switch (info->weather_type)
|
|
|
|
|
{
|
|
|
|
|
case GTK_WEATHER_CLEAR:
|
|
|
|
|
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-clear-symbolic");
|
|
|
|
|
break;
|
|
|
|
|
case GTK_WEATHER_FEW_CLOUDS:
|
|
|
|
|
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-few-clouds-symbolic");
|
|
|
|
|
break;
|
|
|
|
|
case GTK_WEATHER_FOG:
|
|
|
|
|
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-fog-symbolic");
|
|
|
|
|
break;
|
|
|
|
|
case GTK_WEATHER_OVERCAST:
|
|
|
|
|
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-overcast-symbolic");
|
|
|
|
|
break;
|
|
|
|
|
case GTK_WEATHER_SCATTERED_SHOWERS:
|
|
|
|
|
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-showers-scattered-symbolic");
|
|
|
|
|
break;
|
|
|
|
|
case GTK_WEATHER_SHOWERS:
|
|
|
|
|
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-showers-symbolic");
|
|
|
|
|
break;
|
|
|
|
|
case GTK_WEATHER_SNOW:
|
|
|
|
|
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-snow-symbolic");
|
|
|
|
|
break;
|
|
|
|
|
case GTK_WEATHER_STORM:
|
|
|
|
|
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-storm-symbolic");
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
gtk_image_clear (GTK_IMAGE (child));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
child = gtk_widget_get_next_sibling (child);
|
|
|
|
|
s = g_strdup_printf ("%d°", info->temperature);
|
|
|
|
|
gtk_label_set_text (GTK_LABEL (child), s);
|
|
|
|
|
g_free (s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GtkWidget *window = NULL;
|
|
|
|
|
|
2019-12-22 17:27:30 +00:00
|
|
|
|
GtkWidget *
|
|
|
|
|
create_weather_view (void)
|
|
|
|
|
{
|
|
|
|
|
GtkWidget *listview;
|
2020-07-26 20:55:43 +00:00
|
|
|
|
GListModel *selection;
|
2020-06-29 20:37:14 +00:00
|
|
|
|
GtkListItemFactory *factory;
|
2019-12-22 17:27:30 +00:00
|
|
|
|
|
2020-06-29 20:37:14 +00:00
|
|
|
|
factory = gtk_signal_list_item_factory_new ();
|
|
|
|
|
g_signal_connect (factory, "setup", G_CALLBACK (setup_widget), NULL);
|
|
|
|
|
g_signal_connect (factory, "bind", G_CALLBACK (bind_widget), NULL);
|
|
|
|
|
listview = gtk_list_view_new_with_factory (factory);
|
2019-12-22 17:27:30 +00:00
|
|
|
|
gtk_orientable_set_orientation (GTK_ORIENTABLE (listview), GTK_ORIENTATION_HORIZONTAL);
|
|
|
|
|
gtk_list_view_set_show_separators (GTK_LIST_VIEW (listview), TRUE);
|
2020-07-26 20:55:43 +00:00
|
|
|
|
selection = G_LIST_MODEL (gtk_no_selection_new (create_weather_model ()));
|
2019-12-22 17:27:30 +00:00
|
|
|
|
gtk_list_view_set_model (GTK_LIST_VIEW (listview), selection);
|
|
|
|
|
g_object_unref (selection);
|
|
|
|
|
|
|
|
|
|
return listview;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-07 02:46:11 +00:00
|
|
|
|
GtkWidget *
|
|
|
|
|
do_listview_weather (GtkWidget *do_widget)
|
|
|
|
|
{
|
|
|
|
|
if (window == NULL)
|
|
|
|
|
{
|
2020-06-04 17:28:21 +00:00
|
|
|
|
GtkWidget *listview, *sw;
|
2019-10-07 02:46:11 +00:00
|
|
|
|
|
|
|
|
|
window = gtk_window_new ();
|
2020-05-29 17:25:36 +00:00
|
|
|
|
gtk_window_set_default_size (GTK_WINDOW (window), 600, 400);
|
|
|
|
|
gtk_window_set_title (GTK_WINDOW (window), "Weather");
|
2019-10-07 02:46:11 +00:00
|
|
|
|
gtk_window_set_display (GTK_WINDOW (window),
|
|
|
|
|
gtk_widget_get_display (do_widget));
|
|
|
|
|
gtk_window_set_title (GTK_WINDOW (window), "Weather");
|
|
|
|
|
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *) &window);
|
|
|
|
|
|
2020-06-24 15:25:09 +00:00
|
|
|
|
sw = gtk_scrolled_window_new ();
|
2019-10-07 02:46:11 +00:00
|
|
|
|
gtk_window_set_child (GTK_WINDOW (window), sw);
|
2019-12-22 17:27:30 +00:00
|
|
|
|
listview = create_weather_view ();
|
2019-10-07 02:46:11 +00:00
|
|
|
|
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), listview);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!gtk_widget_get_visible (window))
|
|
|
|
|
gtk_widget_show (window);
|
|
|
|
|
else
|
|
|
|
|
gtk_window_destroy (GTK_WINDOW (window));
|
|
|
|
|
|
|
|
|
|
return window;
|
|
|
|
|
}
|