1/* Lists/Weather
2 *
3 * This demo shows a few of the rarer features of GtkListView and
4 * how they can be used to display weather information.
5 *
6 * The hourly weather info uses a horizontal listview. This is easy
7 * to achieve because GtkListView implements the GtkOrientable interface.
8 * To make the items in the list stand out more, the listview uses
9 * separators.
10 *
11 * A GtkNoSelectionModel is used to make sure no item in the list can be
12 * selected. All other interactions with the items is still possible.
13 *
14 * The dataset used here has 70 000 items.
15 */
16
17#include <gtk/gtk.h>
18
19#define GTK_TYPE_WEATHER_INFO (gtk_weather_info_get_type ())
20
21G_DECLARE_FINAL_TYPE (GtkWeatherInfo, gtk_weather_info, GTK, WEATHER_INFO, GObject)
22
23typedef enum {
24 GTK_WEATHER_CLEAR,
25 GTK_WEATHER_FEW_CLOUDS,
26 GTK_WEATHER_FOG,
27 GTK_WEATHER_OVERCAST,
28 GTK_WEATHER_SCATTERED_SHOWERS,
29 GTK_WEATHER_SHOWERS,
30 GTK_WEATHER_SNOW,
31 GTK_WEATHER_STORM
32} GtkWeatherType;
33
34struct _GtkWeatherInfo
35{
36 GObject parent_instance;
37
38 gint64 timestamp;
39 int temperature;
40 GtkWeatherType weather_type;
41};
42
43struct _GtkWeatherInfoClass
44{
45 GObjectClass parent_class;
46};
47
48G_DEFINE_TYPE (GtkWeatherInfo, gtk_weather_info, G_TYPE_OBJECT)
49
50static void
51gtk_weather_info_class_init (GtkWeatherInfoClass *klass)
52{
53}
54
55static void
56gtk_weather_info_init (GtkWeatherInfo *self)
57{
58}
59
60static GtkWeatherInfo *
61gtk_weather_info_new (GDateTime *timestamp,
62 GtkWeatherInfo *copy_from)
63{
64 GtkWeatherInfo *result;
65
66 result = g_object_new (GTK_TYPE_WEATHER_INFO, NULL);
67
68 result->timestamp = g_date_time_to_unix (datetime: timestamp);
69 if (copy_from)
70 {
71 result->temperature = copy_from->temperature;
72 result->weather_type = copy_from->weather_type;
73 }
74
75 return result;
76}
77
78static GDateTime *
79parse_timestamp (const char *string,
80 GTimeZone *_tz)
81{
82 char *with_seconds;
83 GDateTime *result;
84
85 with_seconds = g_strconcat (string1: string, ":00", NULL);
86 result = g_date_time_new_from_iso8601 (text: with_seconds, default_tz: _tz);
87 g_free (mem: with_seconds);
88
89 return result;
90}
91
92static GtkWeatherType
93parse_weather_type (const char *clouds,
94 const char *precip,
95 GtkWeatherType fallback)
96{
97 if (strstr (haystack: precip, needle: "SN"))
98 return GTK_WEATHER_SNOW;
99
100 if (strstr (haystack: precip, needle: "TS"))
101 return GTK_WEATHER_STORM;
102
103 if (strstr (haystack: precip, needle: "DZ"))
104 return GTK_WEATHER_SCATTERED_SHOWERS;
105
106 if (strstr (haystack: precip, needle: "SH") || strstr (haystack: precip, needle: "RA"))
107 return GTK_WEATHER_SHOWERS;
108
109 if (strstr (haystack: precip, needle: "FG"))
110 return GTK_WEATHER_FOG;
111
112 if (g_str_equal (v1: clouds, v2: "M") ||
113 g_str_equal (v1: clouds, v2: ""))
114 return fallback;
115
116 if (strstr (haystack: clouds, needle: "OVC") ||
117 strstr (haystack: clouds, needle: "BKN"))
118 return GTK_WEATHER_OVERCAST;
119
120 if (strstr (haystack: clouds, needle: "BKN") ||
121 strstr (haystack: clouds, needle: "SCT"))
122 return GTK_WEATHER_FEW_CLOUDS;
123
124 if (strstr (haystack: clouds, needle: "VV"))
125 return GTK_WEATHER_FOG;
126
127 return GTK_WEATHER_CLEAR;
128}
129
130static double
131parse_temperature (const char *s,
132 double fallback)
133{
134 char *endptr;
135 double d;
136
137 d = g_ascii_strtod (nptr: s, endptr: &endptr);
138 if (*endptr != '\0')
139 return fallback;
140
141 return d;
142}
143
144static GListModel *
145create_weather_model (void)
146{
147 GListStore *store;
148 GTimeZone *utc;
149 GDateTime *timestamp;
150 GtkWeatherInfo *info;
151 GBytes *data;
152 char **lines;
153 guint i;
154
155 store = g_list_store_new (GTK_TYPE_WEATHER_INFO);
156 data = g_resources_lookup_data (path: "/listview_weather/listview_weather.txt", lookup_flags: 0, NULL);
157 lines = g_strsplit (string: g_bytes_get_data (bytes: data, NULL), delimiter: "\n", max_tokens: 0);
158
159 utc = g_time_zone_new_utc ();
160 timestamp = g_date_time_new (tz: utc, year: 2011, month: 1, day: 1, hour: 0, minute: 0, seconds: 0);
161 info = gtk_weather_info_new (timestamp, NULL);
162 g_list_store_append (store, item: info);
163 g_object_unref (object: info);
164
165 for (i = 0; lines[i] != NULL && *lines[i]; i++)
166 {
167 char **fields;
168 GDateTime *date;
169
170 fields = g_strsplit (string: lines[i], delimiter: ",", max_tokens: 0);
171 date = parse_timestamp (string: fields[0], tz: utc);
172 while (g_date_time_difference (end: date, begin: timestamp) > 30 * G_TIME_SPAN_MINUTE)
173 {
174 GDateTime *new_timestamp = g_date_time_add_hours (datetime: timestamp, hours: 1);
175 g_date_time_unref (datetime: timestamp);
176 timestamp = new_timestamp;
177 info = gtk_weather_info_new (timestamp, copy_from: info);
178 g_list_store_append (store, item: info);
179 g_object_unref (object: info);
180 }
181
182 info->temperature = parse_temperature (s: fields[1], fallback: info->temperature);
183 info->weather_type = parse_weather_type (clouds: fields[2], precip: fields[3], fallback: info->weather_type);
184 g_date_time_unref (datetime: date);
185 g_strfreev (str_array: fields);
186 }
187
188 g_date_time_unref (datetime: timestamp);
189 g_strfreev (str_array: lines);
190 g_bytes_unref (bytes: data);
191 g_time_zone_unref (tz: utc);
192
193 return G_LIST_MODEL (ptr: store);
194}
195
196static void
197setup_widget (GtkSignalListItemFactory *factory,
198 GtkListItem *list_item)
199{
200 GtkWidget *box, *child;
201
202 box = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 0);
203 gtk_list_item_set_child (self: list_item, child: box);
204
205 child = gtk_label_new (NULL);
206 gtk_label_set_width_chars (GTK_LABEL (child), n_chars: 5);
207 gtk_box_append (GTK_BOX (box), child);
208
209 child = gtk_image_new ();
210 gtk_image_set_icon_size (GTK_IMAGE (child), icon_size: GTK_ICON_SIZE_LARGE);
211 gtk_box_append (GTK_BOX (box), child);
212
213 child = gtk_label_new (NULL);
214 gtk_widget_set_vexpand (widget: child, TRUE);
215 gtk_widget_set_valign (widget: child, align: GTK_ALIGN_END);
216 gtk_label_set_width_chars (GTK_LABEL (child), n_chars: 4);
217 gtk_box_append (GTK_BOX (box), child);
218}
219
220static void
221bind_widget (GtkSignalListItemFactory *factory,
222 GtkListItem *list_item)
223{
224 GtkWidget *box, *child;
225 GtkWeatherInfo *info;
226 GDateTime *timestamp;
227 char *s;
228
229 box = gtk_list_item_get_child (self: list_item);
230 info = gtk_list_item_get_item (self: list_item);
231
232 child = gtk_widget_get_first_child (widget: box);
233 timestamp = g_date_time_new_from_unix_utc (t: info->timestamp);
234 s = g_date_time_format (datetime: timestamp, format: "%R");
235 gtk_label_set_text (GTK_LABEL (child), str: s);
236 g_free (mem: s);
237 g_date_time_unref (datetime: timestamp);
238
239 child = gtk_widget_get_next_sibling (widget: child);
240 switch (info->weather_type)
241 {
242 case GTK_WEATHER_CLEAR:
243 gtk_image_set_from_icon_name (GTK_IMAGE (child), icon_name: "weather-clear-symbolic");
244 break;
245 case GTK_WEATHER_FEW_CLOUDS:
246 gtk_image_set_from_icon_name (GTK_IMAGE (child), icon_name: "weather-few-clouds-symbolic");
247 break;
248 case GTK_WEATHER_FOG:
249 gtk_image_set_from_icon_name (GTK_IMAGE (child), icon_name: "weather-fog-symbolic");
250 break;
251 case GTK_WEATHER_OVERCAST:
252 gtk_image_set_from_icon_name (GTK_IMAGE (child), icon_name: "weather-overcast-symbolic");
253 break;
254 case GTK_WEATHER_SCATTERED_SHOWERS:
255 gtk_image_set_from_icon_name (GTK_IMAGE (child), icon_name: "weather-showers-scattered-symbolic");
256 break;
257 case GTK_WEATHER_SHOWERS:
258 gtk_image_set_from_icon_name (GTK_IMAGE (child), icon_name: "weather-showers-symbolic");
259 break;
260 case GTK_WEATHER_SNOW:
261 gtk_image_set_from_icon_name (GTK_IMAGE (child), icon_name: "weather-snow-symbolic");
262 break;
263 case GTK_WEATHER_STORM:
264 gtk_image_set_from_icon_name (GTK_IMAGE (child), icon_name: "weather-storm-symbolic");
265 break;
266 default:
267 gtk_image_clear (GTK_IMAGE (child));
268 break;
269 }
270
271
272 child = gtk_widget_get_next_sibling (widget: child);
273 s = g_strdup_printf (format: "%d°", info->temperature);
274 gtk_label_set_text (GTK_LABEL (child), str: s);
275 g_free (mem: s);
276}
277
278static GtkWidget *window = NULL;
279
280GtkWidget *
281create_weather_view (void)
282{
283 GtkWidget *listview;
284 GtkSelectionModel *model;
285 GtkListItemFactory *factory;
286
287 factory = gtk_signal_list_item_factory_new ();
288 g_signal_connect (factory, "setup", G_CALLBACK (setup_widget), NULL);
289 g_signal_connect (factory, "bind", G_CALLBACK (bind_widget), NULL);
290 model = GTK_SELECTION_MODEL (ptr: gtk_no_selection_new (model: create_weather_model ()));
291 listview = gtk_list_view_new (model, factory);
292 gtk_orientable_set_orientation (GTK_ORIENTABLE (listview), orientation: GTK_ORIENTATION_HORIZONTAL);
293 gtk_list_view_set_show_separators (GTK_LIST_VIEW (listview), TRUE);
294
295 return listview;
296}
297
298GtkWidget *
299do_listview_weather (GtkWidget *do_widget)
300{
301 if (window == NULL)
302 {
303 GtkWidget *listview, *sw;
304
305 window = gtk_window_new ();
306 gtk_window_set_default_size (GTK_WINDOW (window), width: 600, height: 400);
307 gtk_window_set_title (GTK_WINDOW (window), title: "Weather");
308 gtk_window_set_display (GTK_WINDOW (window),
309 display: gtk_widget_get_display (widget: do_widget));
310 gtk_window_set_title (GTK_WINDOW (window), title: "Weather");
311 g_object_add_weak_pointer (G_OBJECT (window), weak_pointer_location: (gpointer *) &window);
312
313 sw = gtk_scrolled_window_new ();
314 gtk_window_set_child (GTK_WINDOW (window), child: sw);
315 listview = create_weather_view ();
316 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), child: listview);
317 }
318
319 if (!gtk_widget_get_visible (widget: window))
320 gtk_widget_show (widget: window);
321 else
322 gtk_window_destroy (GTK_WINDOW (window));
323
324 return window;
325}
326

source code of gtk/demos/gtk-demo/listview_weather.c