1 | #include <gtk/gtk.h> |
2 | |
3 | enum |
4 | { |
5 | PROP_LABEL = 1, |
6 | PROP_ID, |
7 | LAST_PROPERTY |
8 | }; |
9 | |
10 | static GParamSpec *properties[LAST_PROPERTY] = { NULL, }; |
11 | |
12 | typedef struct |
13 | { |
14 | GObject parent; |
15 | |
16 | char *label; |
17 | int id; |
18 | } MyObject; |
19 | |
20 | typedef struct |
21 | { |
22 | GObjectClass parent_class; |
23 | } MyObjectClass; |
24 | |
25 | static GType my_object_get_type (void); |
26 | G_DEFINE_TYPE (MyObject, my_object, G_TYPE_OBJECT) |
27 | |
28 | static void |
29 | my_object_init (MyObject *obj) |
30 | { |
31 | } |
32 | |
33 | static void |
34 | my_object_get_property (GObject *object, |
35 | guint property_id, |
36 | GValue *value, |
37 | GParamSpec *pspec) |
38 | { |
39 | MyObject *obj = (MyObject *)object; |
40 | |
41 | switch (property_id) |
42 | { |
43 | case PROP_LABEL: |
44 | g_value_set_string (value, v_string: obj->label); |
45 | break; |
46 | case PROP_ID: |
47 | g_value_set_int (value, v_int: obj->id); |
48 | break; |
49 | default: |
50 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); |
51 | break; |
52 | } |
53 | } |
54 | |
55 | static void |
56 | my_object_set_property (GObject *object, |
57 | guint property_id, |
58 | const GValue *value, |
59 | GParamSpec *pspec) |
60 | { |
61 | MyObject *obj = (MyObject *)object; |
62 | |
63 | switch (property_id) |
64 | { |
65 | case PROP_LABEL: |
66 | g_free (mem: obj->label); |
67 | obj->label = g_value_dup_string (value); |
68 | break; |
69 | case PROP_ID: |
70 | obj->id = g_value_get_int (value); |
71 | break; |
72 | default: |
73 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); |
74 | break; |
75 | } |
76 | } |
77 | |
78 | static void |
79 | my_object_finalize (GObject *obj) |
80 | { |
81 | MyObject *object = (MyObject *)obj; |
82 | |
83 | g_free (mem: object->label); |
84 | |
85 | G_OBJECT_CLASS (my_object_parent_class)->finalize (obj); |
86 | } |
87 | |
88 | static void |
89 | my_object_class_init (MyObjectClass *class) |
90 | { |
91 | GObjectClass *object_class = G_OBJECT_CLASS (class); |
92 | |
93 | object_class->get_property = my_object_get_property; |
94 | object_class->set_property = my_object_set_property; |
95 | object_class->finalize = my_object_finalize; |
96 | |
97 | properties[PROP_LABEL] = g_param_spec_string (name: "label" , nick: "label" , blurb: "label" , |
98 | NULL, flags: G_PARAM_READWRITE); |
99 | properties[PROP_ID] = g_param_spec_int (name: "id" , nick: "id" , blurb: "id" , |
100 | minimum: 0, G_MAXINT, default_value: 0, flags: G_PARAM_READWRITE); |
101 | |
102 | g_object_class_install_properties (oclass: object_class, n_pspecs: LAST_PROPERTY, pspecs: properties); |
103 | } |
104 | |
105 | static GtkWidget * |
106 | create_widget (gpointer item, |
107 | gpointer user_data) |
108 | { |
109 | MyObject *obj = (MyObject *)item; |
110 | GtkWidget *label; |
111 | |
112 | label = gtk_label_new (str: "" ); |
113 | g_object_bind_property (source: obj, source_property: "label" , target: label, target_property: "label" , flags: G_BINDING_SYNC_CREATE); |
114 | |
115 | return label; |
116 | } |
117 | |
118 | static int |
119 | compare_items (gconstpointer a, gconstpointer b, gpointer data) |
120 | { |
121 | int id_a, id_b; |
122 | |
123 | g_object_get (object: (gpointer)a, first_property_name: "id" , &id_a, NULL); |
124 | g_object_get (object: (gpointer)b, first_property_name: "id" , &id_b, NULL); |
125 | |
126 | return id_a - id_b; |
127 | } |
128 | |
129 | static void |
130 | add_some (GtkButton *button, GListStore *store) |
131 | { |
132 | int n, i; |
133 | guint n_items; |
134 | GObject *obj; |
135 | char *label; |
136 | |
137 | for (n = 0; n < 50; n++) |
138 | { |
139 | n_items = g_list_model_get_n_items (list: G_LIST_MODEL (ptr: store)); |
140 | i = g_random_int_range (begin: 0, MAX (2 * n_items, 1)); |
141 | label = g_strdup_printf (format: "Added %d" , i); |
142 | obj = g_object_new (object_type: my_object_get_type (), |
143 | first_property_name: "id" , i, |
144 | "label" , label, |
145 | NULL); |
146 | g_list_store_insert_sorted (store, item: obj, compare_func: compare_items, NULL); |
147 | g_object_unref (object: obj); |
148 | g_free (mem: label); |
149 | } |
150 | } |
151 | |
152 | static void |
153 | remove_some (GtkButton *button, GListStore *store) |
154 | { |
155 | int n, i; |
156 | guint n_items; |
157 | |
158 | for (n = 0; n < 50; n++) |
159 | { |
160 | n_items = g_list_model_get_n_items (list: G_LIST_MODEL (ptr: store)); |
161 | if (n_items == 0) |
162 | return; |
163 | i = g_random_int_range (begin: 0, end: n_items); |
164 | g_list_store_remove (store, position: i); |
165 | } |
166 | } |
167 | |
168 | int |
169 | main (int argc, char *argv[]) |
170 | { |
171 | GtkWidget *window, *grid, *sw, *box, *button; |
172 | GListStore *store; |
173 | int i; |
174 | |
175 | gtk_init (); |
176 | |
177 | store = g_list_store_new (item_type: my_object_get_type ()); |
178 | for (i = 0; i < 100; i++) |
179 | { |
180 | MyObject *obj; |
181 | char *label; |
182 | |
183 | label = g_strdup_printf (format: "item %d" , i); |
184 | obj = g_object_new (object_type: my_object_get_type (), |
185 | first_property_name: "id" , i, |
186 | "label" , label, |
187 | NULL); |
188 | g_list_store_append (store, item: obj); |
189 | g_free (mem: label); |
190 | g_object_unref (object: obj); |
191 | } |
192 | |
193 | window = gtk_window_new (); |
194 | grid = gtk_grid_new (); |
195 | gtk_window_set_child (GTK_WINDOW (window), child: grid); |
196 | sw = gtk_scrolled_window_new (); |
197 | gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), |
198 | hscrollbar_policy: GTK_POLICY_AUTOMATIC, |
199 | vscrollbar_policy: GTK_POLICY_AUTOMATIC); |
200 | gtk_widget_set_hexpand (widget: sw, TRUE); |
201 | gtk_widget_set_vexpand (widget: sw, TRUE); |
202 | gtk_grid_attach (GTK_GRID (grid), child: sw, column: 0, row: 0, width: 1, height: 1); |
203 | |
204 | box = gtk_list_box_new (); |
205 | gtk_list_box_bind_model (GTK_LIST_BOX (box), model: G_LIST_MODEL (ptr: store), create_widget_func: create_widget, NULL, NULL); |
206 | gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), child: box); |
207 | |
208 | sw = gtk_scrolled_window_new (); |
209 | gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), |
210 | hscrollbar_policy: GTK_POLICY_AUTOMATIC, |
211 | vscrollbar_policy: GTK_POLICY_AUTOMATIC); |
212 | gtk_widget_set_hexpand (widget: sw, TRUE); |
213 | gtk_widget_set_vexpand (widget: sw, TRUE); |
214 | gtk_grid_attach (GTK_GRID (grid), child: sw, column: 1, row: 0, width: 1, height: 1); |
215 | |
216 | box = gtk_flow_box_new (); |
217 | gtk_flow_box_bind_model (GTK_FLOW_BOX (box), model: G_LIST_MODEL (ptr: store), create_widget_func: create_widget, NULL, NULL); |
218 | gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), child: box); |
219 | |
220 | button = gtk_button_new_with_label (label: "Add some" ); |
221 | g_signal_connect (button, "clicked" , G_CALLBACK (add_some), store); |
222 | gtk_grid_attach (GTK_GRID (grid), child: button, column: 0, row: 1, width: 1, height: 1); |
223 | |
224 | button = gtk_button_new_with_label (label: "Remove some" ); |
225 | g_signal_connect (button, "clicked" , G_CALLBACK (remove_some), store); |
226 | gtk_grid_attach (GTK_GRID (grid), child: button, column: 0, row: 2, width: 1, height: 1); |
227 | |
228 | gtk_widget_show (widget: window); |
229 | |
230 | while (TRUE) |
231 | g_main_context_iteration (NULL, TRUE); |
232 | |
233 | return 0; |
234 | } |
235 | |