1 | /* Tree View/List Store |
2 | * |
3 | * The GtkListStore is used to store data in list form, to be used |
4 | * later on by a GtkTreeView to display it. This demo builds a |
5 | * simple GtkListStore and displays it. |
6 | */ |
7 | |
8 | #include <gtk/gtk.h> |
9 | |
10 | static GtkWidget *window = NULL; |
11 | static GtkTreeModel *model = NULL; |
12 | static guint timeout = 0; |
13 | |
14 | typedef struct |
15 | { |
16 | const gboolean fixed; |
17 | const guint number; |
18 | const char *severity; |
19 | const char *description; |
20 | } Bug; |
21 | |
22 | enum |
23 | { |
24 | COLUMN_FIXED, |
25 | COLUMN_NUMBER, |
26 | COLUMN_SEVERITY, |
27 | COLUMN_DESCRIPTION, |
28 | COLUMN_PULSE, |
29 | COLUMN_ICON, |
30 | COLUMN_ACTIVE, |
31 | COLUMN_SENSITIVE, |
32 | NUM_COLUMNS |
33 | }; |
34 | |
35 | static Bug bugs[] = |
36 | { |
37 | { FALSE, 60482, "Normal" , "scrollable notebooks and hidden tabs" }, |
38 | { FALSE, 60620, "Critical" , "gdk_surface_clear_area (gdksurface-win32.c) is not thread-safe" }, |
39 | { FALSE, 50214, "Major" , "Xft support does not clean up correctly" }, |
40 | { TRUE, 52877, "Major" , "GtkFileSelection needs a refresh method. " }, |
41 | { FALSE, 56070, "Normal" , "Can't click button after setting in sensitive" }, |
42 | { TRUE, 56355, "Normal" , "GtkLabel - Not all changes propagate correctly" }, |
43 | { FALSE, 50055, "Normal" , "Rework width/height computations for TreeView" }, |
44 | { FALSE, 58278, "Normal" , "gtk_dialog_set_response_sensitive () doesn't work" }, |
45 | { FALSE, 55767, "Normal" , "Getters for all setters" }, |
46 | { FALSE, 56925, "Normal" , "Gtkcalender size" }, |
47 | { FALSE, 56221, "Normal" , "Selectable label needs right-click copy menu" }, |
48 | { TRUE, 50939, "Normal" , "Add shift clicking to GtkTextView" }, |
49 | { FALSE, 6112, "Enhancement" ,"netscape-like collapsible toolbars" }, |
50 | { FALSE, 1, "Normal" , "First bug :=)" }, |
51 | }; |
52 | |
53 | static gboolean |
54 | spinner_timeout (gpointer data) |
55 | { |
56 | GtkTreeIter iter; |
57 | guint pulse; |
58 | |
59 | if (model == NULL) |
60 | return G_SOURCE_REMOVE; |
61 | |
62 | gtk_tree_model_get_iter_first (tree_model: model, iter: &iter); |
63 | gtk_tree_model_get (tree_model: model, iter: &iter, |
64 | COLUMN_PULSE, &pulse, |
65 | -1); |
66 | if (pulse == G_MAXUINT) |
67 | pulse = 0; |
68 | else |
69 | pulse++; |
70 | |
71 | gtk_list_store_set (GTK_LIST_STORE (model), |
72 | iter: &iter, |
73 | COLUMN_PULSE, pulse, |
74 | COLUMN_ACTIVE, TRUE, |
75 | -1); |
76 | |
77 | return G_SOURCE_CONTINUE; |
78 | } |
79 | |
80 | static GtkTreeModel * |
81 | create_model (void) |
82 | { |
83 | int i = 0; |
84 | GtkListStore *store; |
85 | GtkTreeIter iter; |
86 | |
87 | /* create list store */ |
88 | store = gtk_list_store_new (n_columns: NUM_COLUMNS, |
89 | G_TYPE_BOOLEAN, |
90 | G_TYPE_UINT, |
91 | G_TYPE_STRING, |
92 | G_TYPE_STRING, |
93 | G_TYPE_UINT, |
94 | G_TYPE_STRING, |
95 | G_TYPE_BOOLEAN, |
96 | G_TYPE_BOOLEAN); |
97 | |
98 | /* add data to the list store */ |
99 | for (i = 0; i < G_N_ELEMENTS (bugs); i++) |
100 | { |
101 | const char *icon_name; |
102 | gboolean sensitive; |
103 | |
104 | if (i == 1 || i == 3) |
105 | icon_name = "battery-caution-charging-symbolic" ; |
106 | else |
107 | icon_name = NULL; |
108 | if (i == 3) |
109 | sensitive = FALSE; |
110 | else |
111 | sensitive = TRUE; |
112 | gtk_list_store_append (list_store: store, iter: &iter); |
113 | gtk_list_store_set (list_store: store, iter: &iter, |
114 | COLUMN_FIXED, bugs[i].fixed, |
115 | COLUMN_NUMBER, bugs[i].number, |
116 | COLUMN_SEVERITY, bugs[i].severity, |
117 | COLUMN_DESCRIPTION, bugs[i].description, |
118 | COLUMN_PULSE, 0, |
119 | COLUMN_ICON, icon_name, |
120 | COLUMN_ACTIVE, FALSE, |
121 | COLUMN_SENSITIVE, sensitive, |
122 | -1); |
123 | } |
124 | |
125 | return GTK_TREE_MODEL (store); |
126 | } |
127 | |
128 | static void |
129 | fixed_toggled (GtkCellRendererToggle *cell, |
130 | char *path_str, |
131 | gpointer data) |
132 | { |
133 | GtkTreeModel *tree_model = (GtkTreeModel *)data; |
134 | GtkTreeIter iter; |
135 | GtkTreePath *path = gtk_tree_path_new_from_string (path: path_str); |
136 | gboolean fixed; |
137 | |
138 | /* get toggled iter */ |
139 | gtk_tree_model_get_iter (tree_model, iter: &iter, path); |
140 | gtk_tree_model_get (tree_model, iter: &iter, COLUMN_FIXED, &fixed, -1); |
141 | |
142 | /* do something with the value */ |
143 | fixed ^= 1; |
144 | |
145 | /* set new value */ |
146 | gtk_list_store_set (GTK_LIST_STORE (tree_model), iter: &iter, COLUMN_FIXED, fixed, -1); |
147 | |
148 | /* clean up */ |
149 | gtk_tree_path_free (path); |
150 | } |
151 | |
152 | static void |
153 | add_columns (GtkTreeView *treeview) |
154 | { |
155 | GtkCellRenderer *renderer; |
156 | GtkTreeViewColumn *column; |
157 | |
158 | /* column for fixed toggles */ |
159 | renderer = gtk_cell_renderer_toggle_new (); |
160 | g_signal_connect (renderer, "toggled" , |
161 | G_CALLBACK (fixed_toggled), model); |
162 | |
163 | column = gtk_tree_view_column_new_with_attributes (title: "Fixed?" , |
164 | cell: renderer, |
165 | "active" , COLUMN_FIXED, |
166 | NULL); |
167 | |
168 | /* set this column to a fixed sizing (of 50 pixels) */ |
169 | gtk_tree_view_column_set_sizing (GTK_TREE_VIEW_COLUMN (column), |
170 | type: GTK_TREE_VIEW_COLUMN_FIXED); |
171 | gtk_tree_view_column_set_fixed_width (GTK_TREE_VIEW_COLUMN (column), fixed_width: 50); |
172 | gtk_tree_view_append_column (tree_view: treeview, column); |
173 | |
174 | /* column for bug numbers */ |
175 | renderer = gtk_cell_renderer_text_new (); |
176 | column = gtk_tree_view_column_new_with_attributes (title: "Bug number" , |
177 | cell: renderer, |
178 | "text" , |
179 | COLUMN_NUMBER, |
180 | NULL); |
181 | gtk_tree_view_column_set_sort_column_id (tree_column: column, sort_column_id: COLUMN_NUMBER); |
182 | gtk_tree_view_append_column (tree_view: treeview, column); |
183 | |
184 | /* column for severities */ |
185 | renderer = gtk_cell_renderer_text_new (); |
186 | column = gtk_tree_view_column_new_with_attributes (title: "Severity" , |
187 | cell: renderer, |
188 | "text" , |
189 | COLUMN_SEVERITY, |
190 | NULL); |
191 | gtk_tree_view_column_set_sort_column_id (tree_column: column, sort_column_id: COLUMN_SEVERITY); |
192 | gtk_tree_view_append_column (tree_view: treeview, column); |
193 | |
194 | /* column for description */ |
195 | renderer = gtk_cell_renderer_text_new (); |
196 | column = gtk_tree_view_column_new_with_attributes (title: "Description" , |
197 | cell: renderer, |
198 | "text" , |
199 | COLUMN_DESCRIPTION, |
200 | NULL); |
201 | gtk_tree_view_column_set_sort_column_id (tree_column: column, sort_column_id: COLUMN_DESCRIPTION); |
202 | gtk_tree_view_append_column (tree_view: treeview, column); |
203 | |
204 | /* column for spinner */ |
205 | renderer = gtk_cell_renderer_spinner_new (); |
206 | column = gtk_tree_view_column_new_with_attributes (title: "Spinning" , |
207 | cell: renderer, |
208 | "pulse" , |
209 | COLUMN_PULSE, |
210 | "active" , |
211 | COLUMN_ACTIVE, |
212 | NULL); |
213 | gtk_tree_view_column_set_sort_column_id (tree_column: column, sort_column_id: COLUMN_PULSE); |
214 | gtk_tree_view_append_column (tree_view: treeview, column); |
215 | |
216 | /* column for symbolic icon */ |
217 | renderer = gtk_cell_renderer_pixbuf_new (); |
218 | column = gtk_tree_view_column_new_with_attributes (title: "Symbolic icon" , |
219 | cell: renderer, |
220 | "icon-name" , |
221 | COLUMN_ICON, |
222 | "sensitive" , |
223 | COLUMN_SENSITIVE, |
224 | NULL); |
225 | gtk_tree_view_column_set_sort_column_id (tree_column: column, sort_column_id: COLUMN_ICON); |
226 | gtk_tree_view_append_column (tree_view: treeview, column); |
227 | } |
228 | |
229 | static gboolean |
230 | window_closed (GtkWidget *widget, |
231 | GdkEvent *event, |
232 | gpointer user_data) |
233 | { |
234 | model = NULL; |
235 | window = NULL; |
236 | if (timeout != 0) |
237 | { |
238 | g_source_remove (tag: timeout); |
239 | timeout = 0; |
240 | } |
241 | return FALSE; |
242 | } |
243 | |
244 | GtkWidget * |
245 | do_list_store (GtkWidget *do_widget) |
246 | { |
247 | if (!window) |
248 | { |
249 | GtkWidget *vbox; |
250 | GtkWidget *label; |
251 | GtkWidget *sw; |
252 | GtkWidget *treeview; |
253 | |
254 | /* create window, etc */ |
255 | window = gtk_window_new (); |
256 | gtk_window_set_display (GTK_WINDOW (window), |
257 | display: gtk_widget_get_display (widget: do_widget)); |
258 | gtk_window_set_title (GTK_WINDOW (window), title: "List Store" ); |
259 | g_object_add_weak_pointer (G_OBJECT (window), weak_pointer_location: (gpointer *)&window); |
260 | |
261 | vbox = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 8); |
262 | gtk_widget_set_margin_start (widget: vbox, margin: 8); |
263 | gtk_widget_set_margin_end (widget: vbox, margin: 8); |
264 | gtk_widget_set_margin_top (widget: vbox, margin: 8); |
265 | gtk_widget_set_margin_bottom (widget: vbox, margin: 8); |
266 | gtk_window_set_child (GTK_WINDOW (window), child: vbox); |
267 | |
268 | label = gtk_label_new (str: "This is the bug list (note: not based on real data, it would be nice to have a nice ODBC interface to bugzilla or so, though)." ); |
269 | gtk_box_append (GTK_BOX (vbox), child: label); |
270 | |
271 | sw = gtk_scrolled_window_new (); |
272 | gtk_scrolled_window_set_has_frame (GTK_SCROLLED_WINDOW (sw), TRUE); |
273 | gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), |
274 | hscrollbar_policy: GTK_POLICY_NEVER, |
275 | vscrollbar_policy: GTK_POLICY_AUTOMATIC); |
276 | gtk_box_append (GTK_BOX (vbox), child: sw); |
277 | |
278 | /* create tree model */ |
279 | model = create_model (); |
280 | |
281 | /* create tree view */ |
282 | treeview = gtk_tree_view_new_with_model (model); |
283 | gtk_widget_set_vexpand (widget: treeview, TRUE); |
284 | gtk_tree_view_set_search_column (GTK_TREE_VIEW (treeview), |
285 | column: COLUMN_DESCRIPTION); |
286 | |
287 | g_object_unref (object: model); |
288 | |
289 | gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), child: treeview); |
290 | |
291 | /* add columns to the tree view */ |
292 | add_columns (GTK_TREE_VIEW (treeview)); |
293 | |
294 | /* finish & show */ |
295 | gtk_window_set_default_size (GTK_WINDOW (window), width: 280, height: 250); |
296 | g_signal_connect (window, "destroy" , G_CALLBACK (window_closed), NULL); |
297 | } |
298 | |
299 | if (!gtk_widget_get_visible (widget: window)) |
300 | { |
301 | gtk_widget_show (widget: window); |
302 | if (timeout == 0) { |
303 | /* FIXME this should use the animation-duration instead */ |
304 | timeout = g_timeout_add (interval: 80, function: spinner_timeout, NULL); |
305 | } |
306 | } |
307 | else |
308 | { |
309 | gtk_window_destroy (GTK_WINDOW (window)); |
310 | window = NULL; |
311 | if (timeout != 0) |
312 | { |
313 | g_source_remove (tag: timeout); |
314 | timeout = 0; |
315 | } |
316 | } |
317 | |
318 | return window; |
319 | } |
320 | |