1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /*
3  * Copyright (C) 2012 Red Hat
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, see <http://www.gnu.org/licenses/>.
17  *
18  * Written by:
19  *     Jasper St. Pierre <jstpierre@mecheye.net>
20  */
21 
22 #include "config.h"
23 
24 #include <glib-object.h>
25 
26 #include "gis-page.h"
27 
28 struct _GisPagePrivate
29 {
30   char *title;
31 
32   gboolean applying;
33   GCancellable *apply_cancel;
34   GisPageApplyCallback apply_cb;
35   gpointer apply_data;
36 
37   guint complete : 1;
38   guint skippable : 1;
39   guint needs_accept : 1;
40   guint has_forward : 1;
41   guint padding : 5;
42 };
43 typedef struct _GisPagePrivate GisPagePrivate;
44 
45 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GisPage, gis_page, GTK_TYPE_BIN);
46 
47 enum
48 {
49   PROP_0,
50   PROP_DRIVER,
51   PROP_TITLE,
52   PROP_COMPLETE,
53   PROP_SKIPPABLE,
54   PROP_NEEDS_ACCEPT,
55   PROP_APPLYING,
56   PROP_SMALL_SCREEN,
57   PROP_HAS_FORWARD,
58   PROP_LAST,
59 };
60 
61 static GParamSpec *obj_props[PROP_LAST];
62 
63 static void
gis_page_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)64 gis_page_get_property (GObject    *object,
65                        guint       prop_id,
66                        GValue     *value,
67                        GParamSpec *pspec)
68 {
69   GisPage *page = GIS_PAGE (object);
70   GisPagePrivate *priv = gis_page_get_instance_private (page);
71   switch (prop_id)
72     {
73     case PROP_DRIVER:
74       g_value_set_object (value, page->driver);
75       break;
76     case PROP_TITLE:
77       g_value_set_string (value, priv->title);
78       break;
79     case PROP_COMPLETE:
80       g_value_set_boolean (value, priv->complete);
81       break;
82     case PROP_SKIPPABLE:
83       g_value_set_boolean (value, priv->skippable);
84       break;
85     case PROP_NEEDS_ACCEPT:
86       g_value_set_boolean (value, priv->needs_accept);
87       break;
88     case PROP_HAS_FORWARD:
89       g_value_set_boolean (value, priv->has_forward);
90       break;
91     case PROP_APPLYING:
92       g_value_set_boolean (value, gis_page_get_applying (page));
93       break;
94     case PROP_SMALL_SCREEN:
95       if (page->driver)
96         g_object_get_property (G_OBJECT (page->driver), "small-screen", value);
97       else
98         g_value_set_boolean (value, FALSE);
99       break;
100     default:
101       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
102       break;
103     }
104 }
105 
106 static void
small_screen_changed(GisPage * page)107 small_screen_changed (GisPage *page)
108 {
109   g_object_notify_by_pspec (G_OBJECT (page), obj_props[PROP_SMALL_SCREEN]);
110 }
111 
112 static void
gis_page_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)113 gis_page_set_property (GObject      *object,
114                        guint         prop_id,
115                        const GValue *value,
116                        GParamSpec   *pspec)
117 {
118   GisPage *page = GIS_PAGE (object);
119   GisPagePrivate *priv = gis_page_get_instance_private (page);
120   switch (prop_id)
121     {
122     case PROP_DRIVER:
123       page->driver = g_value_dup_object (value);
124       g_signal_connect_swapped (page->driver, "notify::small-screen",
125                                 G_CALLBACK (small_screen_changed), page);
126       small_screen_changed (page);
127       break;
128     case PROP_TITLE:
129       gis_page_set_title (page, (char *) g_value_get_string (value));
130       break;
131     case PROP_SKIPPABLE:
132       priv->skippable = g_value_get_boolean (value);
133       break;
134     case PROP_NEEDS_ACCEPT:
135       priv->needs_accept = g_value_get_boolean (value);
136       break;
137     case PROP_HAS_FORWARD:
138       priv->has_forward = g_value_get_boolean (value);
139       break;
140     case PROP_COMPLETE:
141       priv->complete = g_value_get_boolean (value);
142       break;
143     default:
144       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
145       break;
146     }
147 }
148 
149 static void
gis_page_finalize(GObject * object)150 gis_page_finalize (GObject *object)
151 {
152   GisPage *page = GIS_PAGE (object);
153   GisPagePrivate *priv = gis_page_get_instance_private (page);
154 
155   g_free (priv->title);
156   g_assert (!priv->applying);
157   g_assert (priv->apply_cb == NULL);
158   g_assert (priv->apply_cancel == NULL);
159 
160   G_OBJECT_CLASS (gis_page_parent_class)->finalize (object);
161 }
162 
163 static void
gis_page_dispose(GObject * object)164 gis_page_dispose (GObject *object)
165 {
166   GisPage *page = GIS_PAGE (object);
167   GisPagePrivate *priv = gis_page_get_instance_private (page);
168 
169   if (priv->apply_cancel)
170     g_cancellable_cancel (priv->apply_cancel);
171 
172   if (page->driver)
173     g_signal_handlers_disconnect_by_func (page->driver, small_screen_changed, page);
174   g_clear_object (&page->driver);
175 
176   G_OBJECT_CLASS (gis_page_parent_class)->dispose (object);
177 }
178 
179 static void
gis_page_constructed(GObject * object)180 gis_page_constructed (GObject *object)
181 {
182   GisPage *page = GIS_PAGE (object);
183 
184   gis_page_locale_changed (page);
185 
186   G_OBJECT_CLASS (gis_page_parent_class)->constructed (object);
187 
188 }
189 
190 static gboolean
gis_page_real_apply(GisPage * page,GCancellable * cancellable)191 gis_page_real_apply (GisPage      *page,
192                      GCancellable *cancellable)
193 {
194   return FALSE;
195 }
196 
197 static void
gis_page_class_init(GisPageClass * klass)198 gis_page_class_init (GisPageClass *klass)
199 {
200   GObjectClass *object_class = G_OBJECT_CLASS (klass);
201 
202   object_class->constructed = gis_page_constructed;
203   object_class->dispose = gis_page_dispose;
204   object_class->finalize = gis_page_finalize;
205   object_class->get_property = gis_page_get_property;
206   object_class->set_property = gis_page_set_property;
207 
208   klass->apply = gis_page_real_apply;
209 
210   obj_props[PROP_DRIVER] =
211     g_param_spec_object ("driver", "", "", GIS_TYPE_DRIVER,
212                          G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
213   obj_props[PROP_TITLE] =
214     g_param_spec_string ("title", "", "", "",
215                          G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
216   obj_props[PROP_COMPLETE] =
217     g_param_spec_boolean ("complete", "", "", FALSE,
218                           G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
219   obj_props[PROP_SKIPPABLE] =
220     g_param_spec_boolean ("skippable", "", "", FALSE,
221                           G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
222   obj_props[PROP_NEEDS_ACCEPT] =
223     g_param_spec_boolean ("needs-accept", "", "", FALSE,
224                           G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
225   obj_props[PROP_HAS_FORWARD] =
226     g_param_spec_boolean ("has-forward", "", "", FALSE,
227                           G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
228   obj_props[PROP_APPLYING] =
229     g_param_spec_boolean ("applying", "", "", FALSE,
230                           G_PARAM_STATIC_STRINGS | G_PARAM_READABLE);
231   obj_props[PROP_SMALL_SCREEN] =
232     g_param_spec_boolean ("small-screen", "", "", FALSE,
233                           G_PARAM_STATIC_STRINGS | G_PARAM_READABLE);
234 
235   g_object_class_install_properties (object_class, PROP_LAST, obj_props);
236 }
237 
238 static void
gis_page_init(GisPage * page)239 gis_page_init (GisPage *page)
240 {
241   gtk_widget_set_margin_start (GTK_WIDGET (page), 12);
242   gtk_widget_set_margin_top (GTK_WIDGET (page), 12);
243   gtk_widget_set_margin_bottom (GTK_WIDGET (page), 12);
244   gtk_widget_set_margin_end (GTK_WIDGET (page), 12);
245 }
246 
247 char *
gis_page_get_title(GisPage * page)248 gis_page_get_title (GisPage *page)
249 {
250   GisPagePrivate *priv = gis_page_get_instance_private (page);
251   if (priv->title != NULL)
252     return priv->title;
253   else
254     return "";
255 }
256 
257 void
gis_page_set_title(GisPage * page,char * title)258 gis_page_set_title (GisPage *page, char *title)
259 {
260   GisPagePrivate *priv = gis_page_get_instance_private (page);
261   g_clear_pointer (&priv->title, g_free);
262   priv->title = g_strdup (title);
263   g_object_notify_by_pspec (G_OBJECT (page), obj_props[PROP_TITLE]);
264 }
265 
266 gboolean
gis_page_get_complete(GisPage * page)267 gis_page_get_complete (GisPage *page)
268 {
269   GisPagePrivate *priv = gis_page_get_instance_private (page);
270   return priv->complete;
271 }
272 
273 void
gis_page_set_complete(GisPage * page,gboolean complete)274 gis_page_set_complete (GisPage *page, gboolean complete)
275 {
276   GisPagePrivate *priv = gis_page_get_instance_private (page);
277   priv->complete = complete;
278   g_object_notify_by_pspec (G_OBJECT (page), obj_props[PROP_COMPLETE]);
279 }
280 
281 gboolean
gis_page_get_skippable(GisPage * page)282 gis_page_get_skippable (GisPage *page)
283 {
284   GisPagePrivate *priv = gis_page_get_instance_private (page);
285   return priv->skippable;
286 }
287 
288 void
gis_page_set_skippable(GisPage * page,gboolean skippable)289 gis_page_set_skippable (GisPage *page, gboolean skippable)
290 {
291   GisPagePrivate *priv = gis_page_get_instance_private (page);
292   priv->skippable = skippable;
293   g_object_notify_by_pspec (G_OBJECT (page), obj_props[PROP_SKIPPABLE]);
294 }
295 
296 gboolean
gis_page_get_needs_accept(GisPage * page)297 gis_page_get_needs_accept (GisPage *page)
298 {
299   GisPagePrivate *priv = gis_page_get_instance_private (page);
300   return priv->needs_accept;
301 }
302 
303 void
gis_page_set_needs_accept(GisPage * page,gboolean needs_accept)304 gis_page_set_needs_accept (GisPage *page, gboolean needs_accept)
305 {
306   GisPagePrivate *priv = gis_page_get_instance_private (page);
307   priv->needs_accept = needs_accept;
308   g_object_notify_by_pspec (G_OBJECT (page), obj_props[PROP_NEEDS_ACCEPT]);
309 }
310 
311 gboolean
gis_page_get_has_forward(GisPage * page)312 gis_page_get_has_forward (GisPage *page)
313 {
314   GisPagePrivate *priv = gis_page_get_instance_private (page);
315   return priv->has_forward;
316 }
317 
318 void
gis_page_set_has_forward(GisPage * page,gboolean has_forward)319 gis_page_set_has_forward (GisPage *page, gboolean has_forward)
320 {
321   GisPagePrivate *priv = gis_page_get_instance_private (page);
322   if (priv->has_forward != has_forward)
323     {
324       priv->has_forward = has_forward;
325       g_object_notify_by_pspec (G_OBJECT (page), obj_props[PROP_HAS_FORWARD]);
326     }
327 }
328 
329 void
gis_page_locale_changed(GisPage * page)330 gis_page_locale_changed (GisPage *page)
331 {
332   if (GIS_PAGE_GET_CLASS (page)->locale_changed)
333     return GIS_PAGE_GET_CLASS (page)->locale_changed (page);
334 }
335 
336 void
gis_page_apply_begin(GisPage * page,GisPageApplyCallback callback,gpointer user_data)337 gis_page_apply_begin (GisPage                *page,
338                       GisPageApplyCallback callback,
339                       gpointer                user_data)
340 {
341   GisPageClass *klass;
342   GisPagePrivate *priv = gis_page_get_instance_private (page);
343 
344   g_return_if_fail (GIS_IS_PAGE (page));
345   g_return_if_fail (priv->applying == FALSE);
346 
347   klass = GIS_PAGE_GET_CLASS (page);
348 
349   priv->apply_cb = callback;
350   priv->apply_data = user_data;
351   priv->apply_cancel = g_cancellable_new ();
352   priv->applying = TRUE;
353 
354   if (!klass->apply (page, priv->apply_cancel))
355     {
356       /* Shortcut case where we don't want apply, to avoid flicker */
357       gis_page_apply_complete (page, TRUE);
358     }
359 
360   g_object_notify_by_pspec (G_OBJECT (page), obj_props[PROP_APPLYING]);
361 }
362 
363 void
gis_page_apply_complete(GisPage * page,gboolean valid)364 gis_page_apply_complete (GisPage *page,
365                          gboolean valid)
366 {
367   GisPageApplyCallback callback;
368   gpointer user_data;
369   GisPagePrivate *priv = gis_page_get_instance_private (page);
370 
371   g_return_if_fail (GIS_IS_PAGE (page));
372   g_return_if_fail (priv->applying == TRUE);
373 
374   callback = priv->apply_cb;
375   priv->apply_cb = NULL;
376   user_data = priv->apply_data;
377   priv->apply_data = NULL;
378 
379   g_clear_object (&priv->apply_cancel);
380   priv->applying = FALSE;
381   g_object_notify_by_pspec (G_OBJECT (page), obj_props[PROP_APPLYING]);
382 
383   if (callback)
384     (callback) (page, valid, user_data);
385 }
386 
387 gboolean
gis_page_get_applying(GisPage * page)388 gis_page_get_applying (GisPage *page)
389 {
390   GisPagePrivate *priv = gis_page_get_instance_private (page);
391   return priv->applying;
392 }
393 
394 void
gis_page_apply_cancel(GisPage * page)395 gis_page_apply_cancel (GisPage *page)
396 {
397   GisPagePrivate *priv = gis_page_get_instance_private (page);
398   g_cancellable_cancel (priv->apply_cancel);
399 }
400 
401 gboolean
gis_page_save_data(GisPage * page,GError ** error)402 gis_page_save_data (GisPage  *page,
403                     GError  **error)
404 {
405   if (GIS_PAGE_GET_CLASS (page)->save_data == NULL)
406     {
407       /* Not implemented, which presumably means the page has nothing to save. */
408       return TRUE;
409     }
410 
411   return GIS_PAGE_GET_CLASS (page)->save_data (page, error);
412 }
413 
414 void
gis_page_shown(GisPage * page)415 gis_page_shown (GisPage *page)
416 {
417   if (GIS_PAGE_GET_CLASS (page)->shown)
418     GIS_PAGE_GET_CLASS (page)->shown (page);
419 }
420 
421 void
gis_page_skip(GisPage * page)422 gis_page_skip (GisPage *page)
423 {
424   if (GIS_PAGE_GET_CLASS (page)->skip)
425     GIS_PAGE_GET_CLASS (page)->skip (page);
426 }
427