1 /*
2 * Copyright (c) 2016 Red Hat, Inc.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
12 * License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 *
18 */
19
20 #include "config.h"
21 #include "gtkstackcombo.h"
22 #include "gtkbox.h"
23 #include "gtkstack.h"
24 #include "gtkcomboboxtext.h"
25 #include "gtkprivate.h"
26 #include "gtkintl.h"
27
28 struct _GtkStackCombo
29 {
30 GtkBox box;
31
32 GtkComboBox *combo;
33 GtkStack *stack;
34 GBinding *binding;
35 };
36
37 struct _GtkStackComboClass {
38 GtkBoxClass parent_class;
39 };
40
41 enum {
42 PROP_0,
43 PROP_STACK
44 };
45
G_DEFINE_TYPE(GtkStackCombo,gtk_stack_combo,GTK_TYPE_BOX)46 G_DEFINE_TYPE (GtkStackCombo, gtk_stack_combo, GTK_TYPE_BOX)
47
48 static void
49 gtk_stack_combo_init (GtkStackCombo *self)
50 {
51 self->stack = NULL;
52 self->combo = GTK_COMBO_BOX (gtk_combo_box_text_new ());
53 gtk_widget_show (GTK_WIDGET (self->combo));
54 gtk_box_pack_start (GTK_BOX (self), GTK_WIDGET (self->combo), FALSE, FALSE, 0);
55 }
56
57 static void gtk_stack_combo_set_stack (GtkStackCombo *self,
58 GtkStack *stack);
59
60 static void
rebuild_combo(GtkStackCombo * self)61 rebuild_combo (GtkStackCombo *self)
62 {
63 gtk_stack_combo_set_stack (self, self->stack);
64 }
65
66 static void
on_child_visible_changed(GtkStackCombo * self)67 on_child_visible_changed (GtkStackCombo *self)
68 {
69 rebuild_combo (self);
70 }
71
72 static void
add_child(GtkWidget * widget,GtkStackCombo * self)73 add_child (GtkWidget *widget,
74 GtkStackCombo *self)
75 {
76 g_signal_handlers_disconnect_by_func (widget, G_CALLBACK (on_child_visible_changed), self);
77 g_signal_connect_swapped (widget, "notify::visible", G_CALLBACK (on_child_visible_changed), self);
78
79 if (gtk_widget_get_visible (widget))
80 {
81 char *name, *title;
82
83 gtk_container_child_get (GTK_CONTAINER (self->stack), widget,
84 "name", &name,
85 "title", &title,
86 NULL);
87
88 gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (self->combo), name, title);
89
90 g_free (name);
91 g_free (title);
92 }
93 }
94
95 static void
populate_combo(GtkStackCombo * self)96 populate_combo (GtkStackCombo *self)
97 {
98 gtk_container_foreach (GTK_CONTAINER (self->stack), (GtkCallback)add_child, self);
99 }
100
101 static void
clear_combo(GtkStackCombo * self)102 clear_combo (GtkStackCombo *self)
103 {
104 gtk_combo_box_text_remove_all (GTK_COMBO_BOX_TEXT (self->combo));
105 }
106
107 static void
on_stack_child_added(GtkContainer * container,GtkWidget * widget,GtkStackCombo * self)108 on_stack_child_added (GtkContainer *container,
109 GtkWidget *widget,
110 GtkStackCombo *self)
111 {
112 rebuild_combo (self);
113 }
114
115 static void
on_stack_child_removed(GtkContainer * container,GtkWidget * widget,GtkStackCombo * self)116 on_stack_child_removed (GtkContainer *container,
117 GtkWidget *widget,
118 GtkStackCombo *self)
119 {
120 g_signal_handlers_disconnect_by_func (widget, G_CALLBACK (on_child_visible_changed), self);
121 rebuild_combo (self);
122 }
123
124 static void
disconnect_stack_signals(GtkStackCombo * self)125 disconnect_stack_signals (GtkStackCombo *self)
126 {
127 g_binding_unbind (self->binding);
128 self->binding = NULL;
129 g_signal_handlers_disconnect_by_func (self->stack, on_stack_child_added, self);
130 g_signal_handlers_disconnect_by_func (self->stack, on_stack_child_removed, self);
131 g_signal_handlers_disconnect_by_func (self->stack, disconnect_stack_signals, self);
132 }
133
134 static void
connect_stack_signals(GtkStackCombo * self)135 connect_stack_signals (GtkStackCombo *self)
136 {
137 g_signal_connect_after (self->stack, "add", G_CALLBACK (on_stack_child_added), self);
138 g_signal_connect_after (self->stack, "remove", G_CALLBACK (on_stack_child_removed), self);
139 g_signal_connect_swapped (self->stack, "destroy", G_CALLBACK (disconnect_stack_signals), self);
140 self->binding = g_object_bind_property (self->stack, "visible-child-name", self->combo, "active-id", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
141 }
142
143 static void
gtk_stack_combo_set_stack(GtkStackCombo * self,GtkStack * stack)144 gtk_stack_combo_set_stack (GtkStackCombo *self,
145 GtkStack *stack)
146 {
147 if (stack)
148 g_object_ref (stack);
149
150 if (self->stack)
151 {
152 disconnect_stack_signals (self);
153 clear_combo (self);
154 g_clear_object (&self->stack);
155 }
156
157 if (stack)
158 {
159 self->stack = stack;
160 populate_combo (self);
161 connect_stack_signals (self);
162 }
163 }
164
165 static void
gtk_stack_combo_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)166 gtk_stack_combo_get_property (GObject *object,
167 guint prop_id,
168 GValue *value,
169 GParamSpec *pspec)
170 {
171 GtkStackCombo *self = GTK_STACK_COMBO (object);
172
173 switch (prop_id)
174 {
175 case PROP_STACK:
176 g_value_set_object (value, self->stack);
177 break;
178
179 default:
180 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
181 break;
182 }
183 }
184
185 static void
gtk_stack_combo_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)186 gtk_stack_combo_set_property (GObject *object,
187 guint prop_id,
188 const GValue *value,
189 GParamSpec *pspec)
190 {
191 GtkStackCombo *self = GTK_STACK_COMBO (object);
192
193 switch (prop_id)
194 {
195 case PROP_STACK:
196 if (self->stack != g_value_get_object (value))
197 {
198 gtk_stack_combo_set_stack (self, g_value_get_object (value));
199 g_object_notify (G_OBJECT (self), "stack");
200 }
201 break;
202
203 default:
204 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
205 break;
206 }
207 }
208
209 static void
gtk_stack_combo_dispose(GObject * object)210 gtk_stack_combo_dispose (GObject *object)
211 {
212 GtkStackCombo *self = GTK_STACK_COMBO (object);
213
214 gtk_stack_combo_set_stack (self, NULL);
215
216 G_OBJECT_CLASS (gtk_stack_combo_parent_class)->dispose (object);
217 }
218
219 static void
gtk_stack_combo_class_init(GtkStackComboClass * class)220 gtk_stack_combo_class_init (GtkStackComboClass *class)
221 {
222 GObjectClass *object_class = G_OBJECT_CLASS (class);
223 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
224
225 object_class->get_property = gtk_stack_combo_get_property;
226 object_class->set_property = gtk_stack_combo_set_property;
227 object_class->dispose = gtk_stack_combo_dispose;
228
229 g_object_class_install_property (object_class,
230 PROP_STACK,
231 g_param_spec_object ("stack",
232 P_("Stack"),
233 P_("Stack"),
234 GTK_TYPE_STACK,
235 GTK_PARAM_READWRITE |
236 G_PARAM_CONSTRUCT));
237
238 gtk_widget_class_set_css_name (widget_class, "stackcombo");
239 }
240