1 | /* Tree View/Tree Store |
2 | * |
3 | * The GtkTreeStore is used to store data in tree form, to be |
4 | * used later on by a GtkTreeView to display it. This demo builds |
5 | * a simple GtkTreeStore and displays it. If you're new to the |
6 | * GtkTreeView widgets and associates, look into the GtkListStore |
7 | * example first. |
8 | * |
9 | */ |
10 | |
11 | #include <gtk/gtk.h> |
12 | |
13 | /* TreeItem structure */ |
14 | typedef struct _TreeItem TreeItem; |
15 | struct _TreeItem |
16 | { |
17 | const char *label; |
18 | gboolean alex; |
19 | gboolean havoc; |
20 | gboolean tim; |
21 | gboolean owen; |
22 | gboolean dave; |
23 | gboolean world_holiday; /* shared by the European hackers */ |
24 | TreeItem *children; |
25 | }; |
26 | |
27 | /* columns */ |
28 | enum |
29 | { |
30 | HOLIDAY_NAME_COLUMN = 0, |
31 | ALEX_COLUMN, |
32 | HAVOC_COLUMN, |
33 | TIM_COLUMN, |
34 | OWEN_COLUMN, |
35 | DAVE_COLUMN, |
36 | |
37 | VISIBLE_COLUMN, |
38 | WORLD_COLUMN, |
39 | NUM_COLUMNS |
40 | }; |
41 | |
42 | /* tree data */ |
43 | static TreeItem january[] = |
44 | { |
45 | {"New Years Day" , TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, NULL }, |
46 | {"Presidential Inauguration" , FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, NULL }, |
47 | {"Martin Luther King Jr. day" , FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, NULL }, |
48 | { NULL } |
49 | }; |
50 | |
51 | static TreeItem february[] = |
52 | { |
53 | { "Presidents' Day" , FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, NULL }, |
54 | { "Groundhog Day" , FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, |
55 | { "Valentine's Day" , FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, NULL }, |
56 | { NULL } |
57 | }; |
58 | |
59 | static TreeItem march[] = |
60 | { |
61 | { "National Tree Planting Day" , FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, |
62 | { "St Patrick's Day" , FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, NULL }, |
63 | { NULL } |
64 | }; |
65 | static TreeItem april[] = |
66 | { |
67 | { "April Fools' Day" , FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, NULL }, |
68 | { "Army Day" , FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, |
69 | { "Earth Day" , FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, NULL }, |
70 | { "Administrative Professionals' Day" , FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, |
71 | { NULL } |
72 | }; |
73 | |
74 | static TreeItem may[] = |
75 | { |
76 | { "Nurses' Day" , FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, |
77 | { "National Day of Prayer" , FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, |
78 | { "Mothers' Day" , FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, NULL }, |
79 | { "Armed Forces Day" , FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, |
80 | { "Memorial Day" , TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, NULL }, |
81 | { NULL } |
82 | }; |
83 | |
84 | static TreeItem june[] = |
85 | { |
86 | { "June Fathers' Day" , FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, NULL }, |
87 | { "Juneteenth (Liberation Day)" , FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, |
88 | { "Flag Day" , FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, NULL }, |
89 | { NULL } |
90 | }; |
91 | |
92 | static TreeItem july[] = |
93 | { |
94 | { "Parents' Day" , FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, NULL }, |
95 | { "Independence Day" , FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, NULL }, |
96 | { NULL } |
97 | }; |
98 | |
99 | static TreeItem august[] = |
100 | { |
101 | { "Air Force Day" , FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, |
102 | { "Coast Guard Day" , FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, |
103 | { "Friendship Day" , FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, |
104 | { NULL } |
105 | }; |
106 | |
107 | static TreeItem september[] = |
108 | { |
109 | { "Grandparents' Day" , FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, NULL }, |
110 | { "Citizenship Day or Constitution Day" , FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, |
111 | { "Labor Day" , TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, NULL }, |
112 | { NULL } |
113 | }; |
114 | |
115 | static TreeItem october[] = |
116 | { |
117 | { "National Children's Day" , FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, |
118 | { "Bosses' Day" , FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, |
119 | { "Sweetest Day" , FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, |
120 | { "Mother-in-Law's Day" , FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, |
121 | { "Navy Day" , FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, |
122 | { "Columbus Day" , FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, NULL }, |
123 | { "Halloween" , FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, NULL }, |
124 | { NULL } |
125 | }; |
126 | |
127 | static TreeItem november[] = |
128 | { |
129 | { "Marine Corps Day" , FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, |
130 | { "Veterans' Day" , TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, NULL }, |
131 | { "Thanksgiving" , FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, NULL }, |
132 | { NULL } |
133 | }; |
134 | |
135 | static TreeItem december[] = |
136 | { |
137 | { "Pearl Harbor Remembrance Day" , FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, |
138 | { "Christmas" , TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, NULL }, |
139 | { "Kwanzaa" , FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, |
140 | { NULL } |
141 | }; |
142 | |
143 | |
144 | static TreeItem toplevel[] = |
145 | { |
146 | {"January" , FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, january}, |
147 | {"February" , FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, february}, |
148 | {"March" , FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, march}, |
149 | {"April" , FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, april}, |
150 | {"May" , FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, may}, |
151 | {"June" , FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, june}, |
152 | {"July" , FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, july}, |
153 | {"August" , FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, august}, |
154 | {"September" , FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, september}, |
155 | {"October" , FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, october}, |
156 | {"November" , FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, november}, |
157 | {"December" , FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, december}, |
158 | {NULL} |
159 | }; |
160 | |
161 | |
162 | static GtkTreeModel * |
163 | create_model (void) |
164 | { |
165 | GtkTreeStore *model; |
166 | GtkTreeIter iter; |
167 | TreeItem *month = toplevel; |
168 | |
169 | /* create tree store */ |
170 | model = gtk_tree_store_new (n_columns: NUM_COLUMNS, |
171 | G_TYPE_STRING, |
172 | G_TYPE_BOOLEAN, |
173 | G_TYPE_BOOLEAN, |
174 | G_TYPE_BOOLEAN, |
175 | G_TYPE_BOOLEAN, |
176 | G_TYPE_BOOLEAN, |
177 | G_TYPE_BOOLEAN, |
178 | G_TYPE_BOOLEAN); |
179 | |
180 | /* add data to the tree store */ |
181 | while (month->label) |
182 | { |
183 | TreeItem *holiday = month->children; |
184 | |
185 | gtk_tree_store_append (tree_store: model, iter: &iter, NULL); |
186 | gtk_tree_store_set (tree_store: model, iter: &iter, |
187 | HOLIDAY_NAME_COLUMN, month->label, |
188 | ALEX_COLUMN, FALSE, |
189 | HAVOC_COLUMN, FALSE, |
190 | TIM_COLUMN, FALSE, |
191 | OWEN_COLUMN, FALSE, |
192 | DAVE_COLUMN, FALSE, |
193 | VISIBLE_COLUMN, FALSE, |
194 | WORLD_COLUMN, FALSE, |
195 | -1); |
196 | |
197 | /* add children */ |
198 | while (holiday->label) |
199 | { |
200 | GtkTreeIter child_iter; |
201 | |
202 | gtk_tree_store_append (tree_store: model, iter: &child_iter, parent: &iter); |
203 | gtk_tree_store_set (tree_store: model, iter: &child_iter, |
204 | HOLIDAY_NAME_COLUMN, holiday->label, |
205 | ALEX_COLUMN, holiday->alex, |
206 | HAVOC_COLUMN, holiday->havoc, |
207 | TIM_COLUMN, holiday->tim, |
208 | OWEN_COLUMN, holiday->owen, |
209 | DAVE_COLUMN, holiday->dave, |
210 | VISIBLE_COLUMN, TRUE, |
211 | WORLD_COLUMN, holiday->world_holiday, |
212 | -1); |
213 | |
214 | holiday++; |
215 | } |
216 | |
217 | month++; |
218 | } |
219 | |
220 | return GTK_TREE_MODEL (model); |
221 | } |
222 | |
223 | static void |
224 | item_toggled (GtkCellRendererToggle *cell, |
225 | char *path_str, |
226 | gpointer data) |
227 | { |
228 | GtkTreeModel *model = (GtkTreeModel *)data; |
229 | GtkTreePath *path = gtk_tree_path_new_from_string (path: path_str); |
230 | GtkTreeIter iter; |
231 | gboolean toggle_item; |
232 | |
233 | int *column; |
234 | |
235 | column = g_object_get_data (G_OBJECT (cell), key: "column" ); |
236 | |
237 | /* get toggled iter */ |
238 | gtk_tree_model_get_iter (tree_model: model, iter: &iter, path); |
239 | gtk_tree_model_get (tree_model: model, iter: &iter, column, &toggle_item, -1); |
240 | |
241 | /* do something with the value */ |
242 | toggle_item ^= 1; |
243 | |
244 | /* set new value */ |
245 | gtk_tree_store_set (GTK_TREE_STORE (model), iter: &iter, column, |
246 | toggle_item, -1); |
247 | |
248 | /* clean up */ |
249 | gtk_tree_path_free (path); |
250 | } |
251 | |
252 | static void |
253 | add_columns (GtkTreeView *treeview) |
254 | { |
255 | int col_offset; |
256 | GtkCellRenderer *renderer; |
257 | GtkTreeViewColumn *column; |
258 | GtkTreeModel *model = gtk_tree_view_get_model (tree_view: treeview); |
259 | |
260 | /* column for holiday names */ |
261 | renderer = gtk_cell_renderer_text_new (); |
262 | g_object_set (object: renderer, first_property_name: "xalign" , 0.0, NULL); |
263 | |
264 | col_offset = gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview), |
265 | position: -1, title: "Holiday" , |
266 | cell: renderer, "text" , |
267 | HOLIDAY_NAME_COLUMN, |
268 | NULL); |
269 | column = gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), n: col_offset - 1); |
270 | gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE); |
271 | |
272 | /* alex column */ |
273 | renderer = gtk_cell_renderer_toggle_new (); |
274 | g_object_set (object: renderer, first_property_name: "xalign" , 0.0, NULL); |
275 | g_object_set_data (G_OBJECT (renderer), key: "column" , data: (int *)ALEX_COLUMN); |
276 | |
277 | g_signal_connect (renderer, "toggled" , G_CALLBACK (item_toggled), model); |
278 | |
279 | col_offset = gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview), |
280 | position: -1, title: "Alex" , |
281 | cell: renderer, |
282 | "active" , |
283 | ALEX_COLUMN, |
284 | "visible" , |
285 | VISIBLE_COLUMN, |
286 | "activatable" , |
287 | WORLD_COLUMN, NULL); |
288 | |
289 | column = gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), n: col_offset - 1); |
290 | gtk_tree_view_column_set_sizing (GTK_TREE_VIEW_COLUMN (column), |
291 | type: GTK_TREE_VIEW_COLUMN_FIXED); |
292 | gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE); |
293 | |
294 | /* havoc column */ |
295 | renderer = gtk_cell_renderer_toggle_new (); |
296 | g_object_set (object: renderer, first_property_name: "xalign" , 0.0, NULL); |
297 | g_object_set_data (G_OBJECT (renderer), key: "column" , data: (int *)HAVOC_COLUMN); |
298 | |
299 | g_signal_connect (renderer, "toggled" , G_CALLBACK (item_toggled), model); |
300 | |
301 | col_offset = gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview), |
302 | position: -1, title: "Havoc" , |
303 | cell: renderer, |
304 | "active" , |
305 | HAVOC_COLUMN, |
306 | "visible" , |
307 | VISIBLE_COLUMN, |
308 | NULL); |
309 | |
310 | column = gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), n: col_offset - 1); |
311 | gtk_tree_view_column_set_sizing (GTK_TREE_VIEW_COLUMN (column), |
312 | type: GTK_TREE_VIEW_COLUMN_FIXED); |
313 | gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE); |
314 | |
315 | /* tim column */ |
316 | renderer = gtk_cell_renderer_toggle_new (); |
317 | g_object_set (object: renderer, first_property_name: "xalign" , 0.0, NULL); |
318 | g_object_set_data (G_OBJECT (renderer), key: "column" , data: (int *)TIM_COLUMN); |
319 | |
320 | g_signal_connect (renderer, "toggled" , G_CALLBACK (item_toggled), model); |
321 | |
322 | col_offset = gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview), |
323 | position: -1, title: "Tim" , |
324 | cell: renderer, |
325 | "active" , |
326 | TIM_COLUMN, |
327 | "visible" , |
328 | VISIBLE_COLUMN, |
329 | "activatable" , |
330 | WORLD_COLUMN, NULL); |
331 | |
332 | column = gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), n: col_offset - 1); |
333 | gtk_tree_view_column_set_sizing (GTK_TREE_VIEW_COLUMN (column), |
334 | type: GTK_TREE_VIEW_COLUMN_FIXED); |
335 | gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE); |
336 | |
337 | /* owen column */ |
338 | renderer = gtk_cell_renderer_toggle_new (); |
339 | g_object_set (object: renderer, first_property_name: "xalign" , 0.0, NULL); |
340 | g_object_set_data (G_OBJECT (renderer), key: "column" , data: (int *)OWEN_COLUMN); |
341 | |
342 | g_signal_connect (renderer, "toggled" , G_CALLBACK (item_toggled), model); |
343 | |
344 | col_offset = gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview), |
345 | position: -1, title: "Owen" , |
346 | cell: renderer, |
347 | "active" , |
348 | OWEN_COLUMN, |
349 | "visible" , |
350 | VISIBLE_COLUMN, |
351 | NULL); |
352 | |
353 | column = gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), n: col_offset - 1); |
354 | gtk_tree_view_column_set_sizing (GTK_TREE_VIEW_COLUMN (column), |
355 | type: GTK_TREE_VIEW_COLUMN_FIXED); |
356 | gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE); |
357 | |
358 | /* dave column */ |
359 | renderer = gtk_cell_renderer_toggle_new (); |
360 | g_object_set (object: renderer, first_property_name: "xalign" , 0.0, NULL); |
361 | g_object_set_data (G_OBJECT (renderer), key: "column" , data: (int *)DAVE_COLUMN); |
362 | |
363 | g_signal_connect (renderer, "toggled" , G_CALLBACK (item_toggled), model); |
364 | |
365 | col_offset = gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview), |
366 | position: -1, title: "Dave" , |
367 | cell: renderer, |
368 | "active" , |
369 | DAVE_COLUMN, |
370 | "visible" , |
371 | VISIBLE_COLUMN, |
372 | NULL); |
373 | |
374 | column = gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), n: col_offset - 1); |
375 | gtk_tree_view_column_set_sizing (GTK_TREE_VIEW_COLUMN (column), |
376 | type: GTK_TREE_VIEW_COLUMN_FIXED); |
377 | gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE); |
378 | } |
379 | |
380 | GtkWidget * |
381 | do_tree_store (GtkWidget *do_widget) |
382 | { |
383 | static GtkWidget *window = NULL; |
384 | |
385 | if (!window) |
386 | { |
387 | GtkWidget *vbox; |
388 | GtkWidget *sw; |
389 | GtkWidget *treeview; |
390 | GtkTreeModel *model; |
391 | |
392 | /* create window, etc */ |
393 | window = gtk_window_new (); |
394 | gtk_window_set_display (GTK_WINDOW (window), |
395 | display: gtk_widget_get_display (widget: do_widget)); |
396 | gtk_window_set_title (GTK_WINDOW (window), title: "Tree Store" ); |
397 | g_object_add_weak_pointer (G_OBJECT (window), weak_pointer_location: (gpointer *)&window); |
398 | |
399 | vbox = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 8); |
400 | gtk_widget_set_margin_start (widget: vbox, margin: 8); |
401 | gtk_widget_set_margin_end (widget: vbox, margin: 8); |
402 | gtk_widget_set_margin_top (widget: vbox, margin: 8); |
403 | gtk_widget_set_margin_bottom (widget: vbox, margin: 8); |
404 | gtk_window_set_child (GTK_WINDOW (window), child: vbox); |
405 | |
406 | gtk_box_append (GTK_BOX (vbox), |
407 | child: gtk_label_new (str: "Jonathan's Holiday Card Planning Sheet" )); |
408 | |
409 | sw = gtk_scrolled_window_new (); |
410 | gtk_scrolled_window_set_has_frame (GTK_SCROLLED_WINDOW (sw), TRUE); |
411 | gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), |
412 | hscrollbar_policy: GTK_POLICY_AUTOMATIC, |
413 | vscrollbar_policy: GTK_POLICY_AUTOMATIC); |
414 | gtk_box_append (GTK_BOX (vbox), child: sw); |
415 | |
416 | /* create model */ |
417 | model = create_model (); |
418 | |
419 | /* create tree view */ |
420 | treeview = gtk_tree_view_new_with_model (model); |
421 | gtk_widget_set_vexpand (widget: treeview, TRUE); |
422 | g_object_unref (object: model); |
423 | gtk_tree_selection_set_mode (selection: gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)), |
424 | type: GTK_SELECTION_MULTIPLE); |
425 | |
426 | add_columns (GTK_TREE_VIEW (treeview)); |
427 | |
428 | gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), child: treeview); |
429 | |
430 | /* expand all rows after the treeview widget has been realized */ |
431 | g_signal_connect (treeview, "realize" , |
432 | G_CALLBACK (gtk_tree_view_expand_all), NULL); |
433 | gtk_window_set_default_size (GTK_WINDOW (window), width: 650, height: 400); |
434 | } |
435 | |
436 | if (!gtk_widget_get_visible (widget: window)) |
437 | gtk_widget_show (widget: window); |
438 | else |
439 | gtk_window_destroy (GTK_WINDOW (window)); |
440 | |
441 | return window; |
442 | } |
443 | |