1 | /* testtreecolumns.c |
2 | * Copyright (C) 2001 Red Hat, Inc |
3 | * Author: Jonathan Blandford |
4 | * |
5 | * This library is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU Library General Public |
7 | * License as published by the Free Software Foundation; either |
8 | * version 2 of the License, or (at your option) any later version. |
9 | * |
10 | * This library is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * Library General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU Library General Public |
16 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
17 | */ |
18 | |
19 | #include "config.h" |
20 | #include <gtk/gtk.h> |
21 | |
22 | /* |
23 | * README README README README README README README README README README |
24 | * README README README README README README README README README README |
25 | * README README README README README README README README README README |
26 | * README README README README README README README README README README |
27 | * README README README README README README README README README README |
28 | * README README README README README README README README README README |
29 | * README README README README README README README README README README |
30 | * README README README README README README README README README README |
31 | * README README README README README README README README README README |
32 | * README README README README README README README README README README |
33 | * README README README README README README README README README README |
34 | * README README README README README README README README README README |
35 | * README README README README README README README README README README |
36 | * |
37 | * DO NOT!!! I REPEAT DO NOT! EVER LOOK AT THIS CODE AS AN EXAMPLE OF WHAT YOUR |
38 | * CODE SHOULD LOOK LIKE. |
39 | * |
40 | * IT IS VERY CONFUSING, AND IS MEANT TO TEST A LOT OF CODE IN THE TREE. WHILE |
41 | * IT IS ACTUALLY CORRECT CODE, IT IS NOT USEFUL. |
42 | */ |
43 | |
44 | GtkWidget *left_tree_view; |
45 | GtkWidget *top_right_tree_view; |
46 | GtkWidget *bottom_right_tree_view; |
47 | GtkTreeModel *left_tree_model; |
48 | GtkTreeModel *top_right_tree_model; |
49 | GtkTreeModel *bottom_right_tree_model; |
50 | GtkWidget *sample_tree_view_top; |
51 | GtkWidget *sample_tree_view_bottom; |
52 | |
53 | #define column_data "my_column_data" |
54 | |
55 | static void move_row (GtkTreeModel *src, |
56 | GtkTreeIter *src_iter, |
57 | GtkTreeModel *dest, |
58 | GtkTreeIter *dest_iter); |
59 | |
60 | /* Kids, don't try this at home. */ |
61 | |
62 | /* Small GtkTreeModel to model columns */ |
63 | typedef struct _ViewColumnModel ViewColumnModel; |
64 | typedef struct _ViewColumnModelClass ViewColumnModelClass; |
65 | |
66 | struct _ViewColumnModel |
67 | { |
68 | GtkListStore parent; |
69 | GtkTreeView *view; |
70 | GList *columns; |
71 | int stamp; |
72 | }; |
73 | |
74 | struct _ViewColumnModelClass |
75 | { |
76 | GtkListStoreClass parent_class; |
77 | }; |
78 | |
79 | static void view_column_model_tree_model_init (GtkTreeModelIface *iface); |
80 | static void view_column_model_drag_source_init (GtkTreeDragSourceIface *iface); |
81 | static void view_column_model_drag_dest_init (GtkTreeDragDestIface *iface); |
82 | |
83 | |
84 | static GType view_column_model_get_type (void); |
85 | G_DEFINE_TYPE_WITH_CODE (ViewColumnModel, view_column_model, GTK_TYPE_LIST_STORE, |
86 | G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, view_column_model_tree_model_init) |
87 | G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE, view_column_model_drag_source_init) |
88 | G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_DEST, view_column_model_drag_dest_init)) |
89 | |
90 | |
91 | |
92 | static void view_column_model_init (ViewColumnModel *model) |
93 | { |
94 | model->stamp = g_random_int (); |
95 | } |
96 | |
97 | static int |
98 | view_column_model_get_n_columns (GtkTreeModel *tree_model) |
99 | { |
100 | return 2; |
101 | } |
102 | |
103 | static GType |
104 | view_column_model_get_column_type (GtkTreeModel *tree_model, |
105 | int index) |
106 | { |
107 | switch (index) |
108 | { |
109 | case 0: |
110 | return G_TYPE_STRING; |
111 | case 1: |
112 | return GTK_TYPE_TREE_VIEW_COLUMN; |
113 | default: |
114 | return G_TYPE_INVALID; |
115 | } |
116 | } |
117 | |
118 | static gboolean |
119 | view_column_model_get_iter (GtkTreeModel *tree_model, |
120 | GtkTreeIter *iter, |
121 | GtkTreePath *path) |
122 | |
123 | { |
124 | ViewColumnModel *view_model = (ViewColumnModel *)tree_model; |
125 | GList *list; |
126 | int i; |
127 | |
128 | g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE); |
129 | |
130 | i = gtk_tree_path_get_indices (path)[0]; |
131 | list = g_list_nth (list: view_model->columns, n: i); |
132 | |
133 | if (list == NULL) |
134 | return FALSE; |
135 | |
136 | iter->stamp = view_model->stamp; |
137 | iter->user_data = list; |
138 | |
139 | return TRUE; |
140 | } |
141 | |
142 | static GtkTreePath * |
143 | view_column_model_get_path (GtkTreeModel *tree_model, |
144 | GtkTreeIter *iter) |
145 | { |
146 | ViewColumnModel *view_model = (ViewColumnModel *)tree_model; |
147 | GtkTreePath *retval; |
148 | GList *list; |
149 | int i = 0; |
150 | |
151 | g_return_val_if_fail (iter->stamp == view_model->stamp, NULL); |
152 | |
153 | for (list = view_model->columns; list; list = list->next) |
154 | { |
155 | if (list == (GList *)iter->user_data) |
156 | break; |
157 | i++; |
158 | } |
159 | if (list == NULL) |
160 | return NULL; |
161 | |
162 | retval = gtk_tree_path_new (); |
163 | gtk_tree_path_append_index (path: retval, index_: i); |
164 | return retval; |
165 | } |
166 | |
167 | static void |
168 | view_column_model_get_value (GtkTreeModel *tree_model, |
169 | GtkTreeIter *iter, |
170 | int column, |
171 | GValue *value) |
172 | { |
173 | #ifndef G_DISABLE_CHECKS |
174 | ViewColumnModel *view_model = (ViewColumnModel *)tree_model; |
175 | |
176 | g_return_if_fail (column < 2); |
177 | g_return_if_fail (view_model->stamp == iter->stamp); |
178 | g_return_if_fail (iter->user_data != NULL); |
179 | #endif |
180 | |
181 | if (column == 0) |
182 | { |
183 | g_value_init (value, G_TYPE_STRING); |
184 | g_value_set_string (value, v_string: gtk_tree_view_column_get_title (GTK_TREE_VIEW_COLUMN (((GList *)iter->user_data)->data))); |
185 | } |
186 | else |
187 | { |
188 | g_value_init (value, GTK_TYPE_TREE_VIEW_COLUMN); |
189 | g_value_set_object (value, v_object: ((GList *)iter->user_data)->data); |
190 | } |
191 | } |
192 | |
193 | static gboolean |
194 | view_column_model_iter_next (GtkTreeModel *tree_model, |
195 | GtkTreeIter *iter) |
196 | { |
197 | #ifndef G_DISABLE_CHECKS |
198 | ViewColumnModel *view_model = (ViewColumnModel *)tree_model; |
199 | |
200 | g_return_val_if_fail (view_model->stamp == iter->stamp, FALSE); |
201 | g_return_val_if_fail (iter->user_data != NULL, FALSE); |
202 | #endif |
203 | |
204 | iter->user_data = ((GList *)iter->user_data)->next; |
205 | return iter->user_data != NULL; |
206 | } |
207 | |
208 | static gboolean |
209 | view_column_model_iter_children (GtkTreeModel *tree_model, |
210 | GtkTreeIter *iter, |
211 | GtkTreeIter *parent) |
212 | { |
213 | ViewColumnModel *view_model = (ViewColumnModel *)tree_model; |
214 | |
215 | /* this is a list, nodes have no children */ |
216 | if (parent) |
217 | return FALSE; |
218 | |
219 | /* but if parent == NULL we return the list itself as children of the |
220 | * "root" |
221 | */ |
222 | |
223 | if (view_model->columns) |
224 | { |
225 | iter->stamp = view_model->stamp; |
226 | iter->user_data = view_model->columns; |
227 | return TRUE; |
228 | } |
229 | else |
230 | return FALSE; |
231 | } |
232 | |
233 | static gboolean |
234 | view_column_model_iter_has_child (GtkTreeModel *tree_model, |
235 | GtkTreeIter *iter) |
236 | { |
237 | return FALSE; |
238 | } |
239 | |
240 | static int |
241 | view_column_model_iter_n_children (GtkTreeModel *tree_model, |
242 | GtkTreeIter *iter) |
243 | { |
244 | return g_list_length (list: ((ViewColumnModel *)tree_model)->columns); |
245 | } |
246 | |
247 | static int |
248 | view_column_model_iter_nth_child (GtkTreeModel *tree_model, |
249 | GtkTreeIter *iter, |
250 | GtkTreeIter *parent, |
251 | int n) |
252 | { |
253 | ViewColumnModel *view_model = (ViewColumnModel *)tree_model; |
254 | |
255 | if (parent) |
256 | return FALSE; |
257 | |
258 | iter->stamp = view_model->stamp; |
259 | iter->user_data = g_list_nth (list: (GList *)view_model->columns, n); |
260 | |
261 | return (iter->user_data != NULL); |
262 | } |
263 | |
264 | static gboolean |
265 | view_column_model_iter_parent (GtkTreeModel *tree_model, |
266 | GtkTreeIter *iter, |
267 | GtkTreeIter *child) |
268 | { |
269 | return FALSE; |
270 | } |
271 | |
272 | static void |
273 | view_column_model_tree_model_init (GtkTreeModelIface *iface) |
274 | { |
275 | iface->get_n_columns = view_column_model_get_n_columns; |
276 | iface->get_column_type = view_column_model_get_column_type; |
277 | iface->get_iter = view_column_model_get_iter; |
278 | iface->get_path = view_column_model_get_path; |
279 | iface->get_value = view_column_model_get_value; |
280 | iface->iter_next = view_column_model_iter_next; |
281 | iface->iter_children = view_column_model_iter_children; |
282 | iface->iter_has_child = view_column_model_iter_has_child; |
283 | iface->iter_n_children = view_column_model_iter_n_children; |
284 | iface->iter_nth_child = view_column_model_iter_nth_child; |
285 | iface->iter_parent = view_column_model_iter_parent; |
286 | } |
287 | |
288 | static GdkContentProvider * |
289 | view_column_model_drag_data_get (GtkTreeDragSource *drag_source, |
290 | GtkTreePath *path) |
291 | { |
292 | return gtk_tree_create_row_drag_content (GTK_TREE_MODEL (drag_source), path); |
293 | } |
294 | |
295 | static gboolean |
296 | view_column_model_drag_data_delete (GtkTreeDragSource *drag_source, |
297 | GtkTreePath *path) |
298 | { |
299 | /* Nothing -- we handle moves on the dest side */ |
300 | |
301 | return TRUE; |
302 | } |
303 | |
304 | static gboolean |
305 | view_column_model_row_drop_possible (GtkTreeDragDest *drag_dest, |
306 | GtkTreePath *dest_path, |
307 | const GValue *value) |
308 | { |
309 | GtkTreeModel *src_model; |
310 | |
311 | if (gtk_tree_get_row_drag_data (value, |
312 | tree_model: &src_model, |
313 | NULL)) |
314 | { |
315 | if (src_model == left_tree_model || |
316 | src_model == top_right_tree_model || |
317 | src_model == bottom_right_tree_model) |
318 | return TRUE; |
319 | } |
320 | |
321 | return FALSE; |
322 | } |
323 | |
324 | static gboolean |
325 | view_column_model_drag_data_received (GtkTreeDragDest *drag_dest, |
326 | GtkTreePath *dest, |
327 | const GValue *value) |
328 | { |
329 | GtkTreeModel *src_model; |
330 | GtkTreePath *src_path = NULL; |
331 | gboolean retval = FALSE; |
332 | |
333 | if (gtk_tree_get_row_drag_data (value, |
334 | tree_model: &src_model, |
335 | path: &src_path)) |
336 | { |
337 | GtkTreeIter src_iter; |
338 | GtkTreeIter dest_iter; |
339 | gboolean have_dest; |
340 | |
341 | /* We are a little lazy here, and assume if we can't convert dest |
342 | * to an iter, we need to append. See gtkliststore.c for a more |
343 | * careful handling of this. |
344 | */ |
345 | have_dest = gtk_tree_model_get_iter (GTK_TREE_MODEL (drag_dest), iter: &dest_iter, path: dest); |
346 | |
347 | if (gtk_tree_model_get_iter (tree_model: src_model, iter: &src_iter, path: src_path)) |
348 | { |
349 | if (src_model == left_tree_model || |
350 | src_model == top_right_tree_model || |
351 | src_model == bottom_right_tree_model) |
352 | { |
353 | move_row (src: src_model, src_iter: &src_iter, GTK_TREE_MODEL (drag_dest), |
354 | dest_iter: have_dest ? &dest_iter : NULL); |
355 | retval = TRUE; |
356 | } |
357 | } |
358 | |
359 | gtk_tree_path_free (path: src_path); |
360 | } |
361 | |
362 | return retval; |
363 | } |
364 | |
365 | static void |
366 | view_column_model_drag_source_init (GtkTreeDragSourceIface *iface) |
367 | { |
368 | iface->drag_data_get = view_column_model_drag_data_get; |
369 | iface->drag_data_delete = view_column_model_drag_data_delete; |
370 | } |
371 | |
372 | static void |
373 | view_column_model_drag_dest_init (GtkTreeDragDestIface *iface) |
374 | { |
375 | iface->drag_data_received = view_column_model_drag_data_received; |
376 | iface->row_drop_possible = view_column_model_row_drop_possible; |
377 | } |
378 | |
379 | static void |
380 | view_column_model_class_init (ViewColumnModelClass *klass) |
381 | { |
382 | } |
383 | |
384 | static void |
385 | update_columns (GtkTreeView *view, ViewColumnModel *view_model) |
386 | { |
387 | GList *old_columns = view_model->columns; |
388 | int old_length, length; |
389 | GList *a, *b; |
390 | |
391 | view_model->columns = gtk_tree_view_get_columns (tree_view: view_model->view); |
392 | |
393 | /* As the view tells us one change at a time, we can do this hack. */ |
394 | length = g_list_length (list: view_model->columns); |
395 | old_length = g_list_length (list: old_columns); |
396 | if (length != old_length) |
397 | { |
398 | GtkTreePath *path; |
399 | int i = 0; |
400 | |
401 | /* where are they different */ |
402 | for (a = old_columns, b = view_model->columns; a && b; a = a->next, b = b->next) |
403 | { |
404 | if (a->data != b->data) |
405 | break; |
406 | i++; |
407 | } |
408 | path = gtk_tree_path_new (); |
409 | gtk_tree_path_append_index (path, index_: i); |
410 | if (length < old_length) |
411 | { |
412 | view_model->stamp++; |
413 | gtk_tree_model_row_deleted (GTK_TREE_MODEL (view_model), path); |
414 | } |
415 | else |
416 | { |
417 | GtkTreeIter iter; |
418 | iter.stamp = view_model->stamp; |
419 | iter.user_data = b; |
420 | gtk_tree_model_row_inserted (GTK_TREE_MODEL (view_model), path, iter: &iter); |
421 | } |
422 | gtk_tree_path_free (path); |
423 | } |
424 | else |
425 | { |
426 | int i; |
427 | int m = 0, n = 1; |
428 | int *new_order; |
429 | GtkTreePath *path; |
430 | |
431 | new_order = g_new (int, length); |
432 | a = old_columns; b = view_model->columns; |
433 | |
434 | while (a->data == b->data) |
435 | { |
436 | a = a->next; |
437 | b = b->next; |
438 | if (a == NULL) |
439 | return; |
440 | m++; |
441 | } |
442 | |
443 | if (a->next->data == b->data) |
444 | { |
445 | b = b->next; |
446 | while (b->data != a->data) |
447 | { |
448 | b = b->next; |
449 | n++; |
450 | } |
451 | for (i = 0; i < m; i++) |
452 | new_order[i] = i; |
453 | for (i = m; i < m+n; i++) |
454 | new_order[i] = i+1; |
455 | new_order[i] = m; |
456 | for (i = m + n +1; i < length; i++) |
457 | new_order[i] = i; |
458 | } |
459 | else |
460 | { |
461 | a = a->next; |
462 | while (a->data != b->data) |
463 | { |
464 | a = a->next; |
465 | n++; |
466 | } |
467 | for (i = 0; i < m; i++) |
468 | new_order[i] = i; |
469 | new_order[m] = m+n; |
470 | for (i = m+1; i < m + n+ 1; i++) |
471 | new_order[i] = i - 1; |
472 | for (i = m + n + 1; i < length; i++) |
473 | new_order[i] = i; |
474 | } |
475 | |
476 | path = gtk_tree_path_new (); |
477 | gtk_tree_model_rows_reordered (GTK_TREE_MODEL (view_model), |
478 | path, |
479 | NULL, |
480 | new_order); |
481 | gtk_tree_path_free (path); |
482 | g_free (mem: new_order); |
483 | } |
484 | if (old_columns) |
485 | g_list_free (list: old_columns); |
486 | } |
487 | |
488 | static GtkTreeModel * |
489 | view_column_model_new (GtkTreeView *view) |
490 | { |
491 | GtkTreeModel *retval; |
492 | |
493 | retval = g_object_new (object_type: view_column_model_get_type (), NULL); |
494 | ((ViewColumnModel *)retval)->view = view; |
495 | ((ViewColumnModel *)retval)->columns = gtk_tree_view_get_columns (tree_view: view); |
496 | |
497 | g_signal_connect (view, "columns_changed" , G_CALLBACK (update_columns), retval); |
498 | |
499 | return retval; |
500 | } |
501 | |
502 | /* Back to sanity. |
503 | */ |
504 | |
505 | static void |
506 | add_clicked (GtkWidget *button, gpointer data) |
507 | { |
508 | static int i = 0; |
509 | |
510 | GtkTreeIter iter; |
511 | GtkTreeViewColumn *column; |
512 | GtkTreeSelection *selection; |
513 | GtkCellRenderer *cell; |
514 | char *label = g_strdup_printf (format: "Column %d" , i); |
515 | |
516 | cell = gtk_cell_renderer_text_new (); |
517 | column = gtk_tree_view_column_new_with_attributes (title: label, cell, "text" , 0, NULL); |
518 | g_object_set_data_full (G_OBJECT (column), column_data, data: label, destroy: g_free); |
519 | gtk_tree_view_column_set_reorderable (tree_column: column, TRUE); |
520 | gtk_tree_view_column_set_sizing (tree_column: column, type: GTK_TREE_VIEW_COLUMN_GROW_ONLY); |
521 | gtk_tree_view_column_set_resizable (tree_column: column, TRUE); |
522 | gtk_list_store_append (GTK_LIST_STORE (left_tree_model), iter: &iter); |
523 | gtk_list_store_set (GTK_LIST_STORE (left_tree_model), iter: &iter, 0, label, 1, column, -1); |
524 | i++; |
525 | |
526 | selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (left_tree_view)); |
527 | gtk_tree_selection_select_iter (selection, iter: &iter); |
528 | } |
529 | |
530 | static void |
531 | get_visible (GtkTreeViewColumn *tree_column, |
532 | GtkCellRenderer *cell, |
533 | GtkTreeModel *tree_model, |
534 | GtkTreeIter *iter, |
535 | gpointer data) |
536 | { |
537 | GtkTreeViewColumn *column; |
538 | |
539 | gtk_tree_model_get (tree_model, iter, 1, &column, -1); |
540 | if (column) |
541 | { |
542 | gtk_cell_renderer_toggle_set_active (GTK_CELL_RENDERER_TOGGLE (cell), |
543 | setting: gtk_tree_view_column_get_visible (tree_column: column)); |
544 | } |
545 | } |
546 | |
547 | static void |
548 | set_visible (GtkCellRendererToggle *cell, |
549 | char *path_str, |
550 | gpointer data) |
551 | { |
552 | GtkTreeView *tree_view = (GtkTreeView *) data; |
553 | GtkTreeViewColumn *column; |
554 | GtkTreeModel *model; |
555 | GtkTreeIter iter; |
556 | GtkTreePath *path = gtk_tree_path_new_from_string (path: path_str); |
557 | |
558 | model = gtk_tree_view_get_model (tree_view); |
559 | |
560 | gtk_tree_model_get_iter (tree_model: model, iter: &iter, path); |
561 | gtk_tree_model_get (tree_model: model, iter: &iter, 1, &column, -1); |
562 | |
563 | if (column) |
564 | { |
565 | gtk_tree_view_column_set_visible (tree_column: column, visible: ! gtk_tree_view_column_get_visible (tree_column: column)); |
566 | gtk_tree_model_row_changed (tree_model: model, path, iter: &iter); |
567 | } |
568 | gtk_tree_path_free (path); |
569 | } |
570 | |
571 | static void |
572 | move_to_left (GtkTreeModel *src, |
573 | GtkTreeIter *src_iter, |
574 | GtkTreeIter *dest_iter) |
575 | { |
576 | GtkTreeIter iter; |
577 | GtkTreeViewColumn *column; |
578 | GtkTreeSelection *selection; |
579 | char *label; |
580 | |
581 | gtk_tree_model_get (tree_model: src, iter: src_iter, 0, &label, 1, &column, -1); |
582 | |
583 | if (src == top_right_tree_model) |
584 | gtk_tree_view_remove_column (GTK_TREE_VIEW (sample_tree_view_top), column); |
585 | else |
586 | gtk_tree_view_remove_column (GTK_TREE_VIEW (sample_tree_view_bottom), column); |
587 | |
588 | /* gtk_list_store_remove (GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (data))), &iter);*/ |
589 | |
590 | /* Put it back on the left */ |
591 | if (dest_iter) |
592 | gtk_list_store_insert_before (GTK_LIST_STORE (left_tree_model), |
593 | iter: &iter, sibling: dest_iter); |
594 | else |
595 | gtk_list_store_append (GTK_LIST_STORE (left_tree_model), iter: &iter); |
596 | |
597 | gtk_list_store_set (GTK_LIST_STORE (left_tree_model), iter: &iter, 0, label, 1, column, -1); |
598 | selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (left_tree_view)); |
599 | gtk_tree_selection_select_iter (selection, iter: &iter); |
600 | |
601 | g_free (mem: label); |
602 | } |
603 | |
604 | static void |
605 | move_to_right (GtkTreeIter *src_iter, |
606 | GtkTreeModel *dest, |
607 | GtkTreeIter *dest_iter) |
608 | { |
609 | char *label; |
610 | GtkTreeViewColumn *column; |
611 | int before = -1; |
612 | |
613 | gtk_tree_model_get (GTK_TREE_MODEL (left_tree_model), |
614 | iter: src_iter, 0, &label, 1, &column, -1); |
615 | gtk_list_store_remove (GTK_LIST_STORE (left_tree_model), iter: src_iter); |
616 | |
617 | if (dest_iter) |
618 | { |
619 | GtkTreePath *path = gtk_tree_model_get_path (tree_model: dest, iter: dest_iter); |
620 | before = (gtk_tree_path_get_indices (path))[0]; |
621 | gtk_tree_path_free (path); |
622 | } |
623 | |
624 | if (dest == top_right_tree_model) |
625 | gtk_tree_view_insert_column (GTK_TREE_VIEW (sample_tree_view_top), column, position: before); |
626 | else |
627 | gtk_tree_view_insert_column (GTK_TREE_VIEW (sample_tree_view_bottom), column, position: before); |
628 | |
629 | g_free (mem: label); |
630 | } |
631 | |
632 | static void |
633 | move_up_or_down (GtkTreeModel *src, |
634 | GtkTreeIter *src_iter, |
635 | GtkTreeModel *dest, |
636 | GtkTreeIter *dest_iter) |
637 | { |
638 | GtkTreeViewColumn *column; |
639 | char *label; |
640 | int before = -1; |
641 | |
642 | gtk_tree_model_get (tree_model: src, iter: src_iter, 0, &label, 1, &column, -1); |
643 | |
644 | if (dest_iter) |
645 | { |
646 | GtkTreePath *path = gtk_tree_model_get_path (tree_model: dest, iter: dest_iter); |
647 | before = (gtk_tree_path_get_indices (path))[0]; |
648 | gtk_tree_path_free (path); |
649 | } |
650 | |
651 | if (src == top_right_tree_model) |
652 | gtk_tree_view_remove_column (GTK_TREE_VIEW (sample_tree_view_top), column); |
653 | else |
654 | gtk_tree_view_remove_column (GTK_TREE_VIEW (sample_tree_view_bottom), column); |
655 | |
656 | if (dest == top_right_tree_model) |
657 | gtk_tree_view_insert_column (GTK_TREE_VIEW (sample_tree_view_top), column, position: before); |
658 | else |
659 | gtk_tree_view_insert_column (GTK_TREE_VIEW (sample_tree_view_bottom), column, position: before); |
660 | |
661 | g_free (mem: label); |
662 | } |
663 | |
664 | static void |
665 | move_row (GtkTreeModel *src, |
666 | GtkTreeIter *src_iter, |
667 | GtkTreeModel *dest, |
668 | GtkTreeIter *dest_iter) |
669 | { |
670 | if (src == left_tree_model) |
671 | move_to_right (src_iter, dest, dest_iter); |
672 | else if (dest == left_tree_model) |
673 | move_to_left (src, src_iter, dest_iter); |
674 | else |
675 | move_up_or_down (src, src_iter, dest, dest_iter); |
676 | } |
677 | |
678 | static void |
679 | add_left_clicked (GtkWidget *button, |
680 | gpointer data) |
681 | { |
682 | GtkTreeIter iter; |
683 | |
684 | GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (data)); |
685 | |
686 | gtk_tree_selection_get_selected (selection, NULL, iter: &iter); |
687 | |
688 | move_to_left (src: gtk_tree_view_get_model (GTK_TREE_VIEW (data)), src_iter: &iter, NULL); |
689 | } |
690 | |
691 | static void |
692 | add_right_clicked (GtkWidget *button, gpointer data) |
693 | { |
694 | GtkTreeIter iter; |
695 | |
696 | GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (left_tree_view)); |
697 | |
698 | gtk_tree_selection_get_selected (selection, NULL, iter: &iter); |
699 | |
700 | move_to_right (src_iter: &iter, dest: gtk_tree_view_get_model (GTK_TREE_VIEW (data)), NULL); |
701 | } |
702 | |
703 | static void |
704 | selection_changed (GtkTreeSelection *selection, GtkWidget *button) |
705 | { |
706 | if (gtk_tree_selection_get_selected (selection, NULL, NULL)) |
707 | gtk_widget_set_sensitive (widget: button, TRUE); |
708 | else |
709 | gtk_widget_set_sensitive (widget: button, FALSE); |
710 | } |
711 | |
712 | static void |
713 | quit_cb (GtkWidget *widget, |
714 | gpointer data) |
715 | { |
716 | gboolean *done = data; |
717 | |
718 | *done = TRUE; |
719 | |
720 | g_main_context_wakeup (NULL); |
721 | } |
722 | |
723 | int |
724 | main (int argc, char *argv[]) |
725 | { |
726 | GtkWidget *window; |
727 | GtkWidget *hbox, *vbox; |
728 | GtkWidget *vbox2, *bbox; |
729 | GtkWidget *button; |
730 | GtkTreeViewColumn *column; |
731 | GtkCellRenderer *cell; |
732 | GtkWidget *swindow; |
733 | GtkTreeModel *sample_model; |
734 | GdkContentFormats *targets; |
735 | int i; |
736 | gboolean done = FALSE; |
737 | |
738 | gtk_init (); |
739 | |
740 | /* First initialize all the models for signal purposes */ |
741 | left_tree_model = (GtkTreeModel *) gtk_list_store_new (n_columns: 2, G_TYPE_STRING, G_TYPE_POINTER); |
742 | sample_model = (GtkTreeModel *) gtk_list_store_new (n_columns: 1, G_TYPE_STRING); |
743 | sample_tree_view_top = gtk_tree_view_new_with_model (model: sample_model); |
744 | sample_tree_view_bottom = gtk_tree_view_new_with_model (model: sample_model); |
745 | top_right_tree_model = (GtkTreeModel *) view_column_model_new (GTK_TREE_VIEW (sample_tree_view_top)); |
746 | bottom_right_tree_model = (GtkTreeModel *) view_column_model_new (GTK_TREE_VIEW (sample_tree_view_bottom)); |
747 | top_right_tree_view = gtk_tree_view_new_with_model (model: top_right_tree_model); |
748 | bottom_right_tree_view = gtk_tree_view_new_with_model (model: bottom_right_tree_model); |
749 | |
750 | for (i = 0; i < 10; i++) |
751 | { |
752 | GtkTreeIter iter; |
753 | char *string = g_strdup_printf (format: "%d" , i); |
754 | gtk_list_store_append (GTK_LIST_STORE (sample_model), iter: &iter); |
755 | gtk_list_store_set (GTK_LIST_STORE (sample_model), iter: &iter, 0, string, -1); |
756 | g_free (mem: string); |
757 | } |
758 | |
759 | /* Set up the test windows. */ |
760 | window = gtk_window_new (); |
761 | g_signal_connect (window, "destroy" , G_CALLBACK (quit_cb), &done); |
762 | gtk_window_set_default_size (GTK_WINDOW (window), width: 300, height: 300); |
763 | gtk_window_set_title (GTK_WINDOW (window), title: "Top Window" ); |
764 | swindow = gtk_scrolled_window_new (); |
765 | gtk_window_set_child (GTK_WINDOW (window), child: swindow); |
766 | gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (swindow), child: sample_tree_view_top); |
767 | gtk_widget_show (widget: window); |
768 | |
769 | window = gtk_window_new (); |
770 | g_signal_connect (window, "destroy" , G_CALLBACK (quit_cb), &done); |
771 | gtk_window_set_default_size (GTK_WINDOW (window), width: 300, height: 300); |
772 | gtk_window_set_title (GTK_WINDOW (window), title: "Bottom Window" ); |
773 | swindow = gtk_scrolled_window_new (); |
774 | gtk_window_set_child (GTK_WINDOW (window), child: swindow); |
775 | gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (swindow), child: sample_tree_view_bottom); |
776 | gtk_widget_show (widget: window); |
777 | |
778 | /* Set up the main window */ |
779 | window = gtk_window_new (); |
780 | g_signal_connect (window, "destroy" , G_CALLBACK (quit_cb), &done); |
781 | gtk_window_set_default_size (GTK_WINDOW (window), width: 500, height: 300); |
782 | vbox = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 8); |
783 | gtk_window_set_child (GTK_WINDOW (window), child: vbox); |
784 | |
785 | hbox = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 8); |
786 | gtk_box_append (GTK_BOX (vbox), child: hbox); |
787 | |
788 | /* Left Pane */ |
789 | cell = gtk_cell_renderer_text_new (); |
790 | |
791 | swindow = gtk_scrolled_window_new (); |
792 | gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow), hscrollbar_policy: GTK_POLICY_AUTOMATIC, vscrollbar_policy: GTK_POLICY_AUTOMATIC); |
793 | left_tree_view = gtk_tree_view_new_with_model (model: left_tree_model); |
794 | gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (swindow), child: left_tree_view); |
795 | gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (left_tree_view), position: -1, |
796 | title: "Unattached Columns" , cell, "text" , 0, NULL); |
797 | cell = gtk_cell_renderer_toggle_new (); |
798 | g_signal_connect (cell, "toggled" , G_CALLBACK (set_visible), left_tree_view); |
799 | column = gtk_tree_view_column_new_with_attributes (title: "Visible" , cell, NULL); |
800 | gtk_tree_view_append_column (GTK_TREE_VIEW (left_tree_view), column); |
801 | |
802 | gtk_tree_view_column_set_cell_data_func (tree_column: column, cell_renderer: cell, func: get_visible, NULL, NULL); |
803 | gtk_box_append (GTK_BOX (hbox), child: swindow); |
804 | |
805 | /* Middle Pane */ |
806 | vbox2 = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 8); |
807 | gtk_box_append (GTK_BOX (hbox), child: vbox2); |
808 | |
809 | bbox = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 0); |
810 | gtk_box_append (GTK_BOX (vbox2), child: bbox); |
811 | |
812 | button = gtk_button_new_with_mnemonic (label: "<< (_Q)" ); |
813 | gtk_widget_set_sensitive (widget: button, FALSE); |
814 | g_signal_connect (button, "clicked" , G_CALLBACK (add_left_clicked), top_right_tree_view); |
815 | g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (top_right_tree_view)), |
816 | "changed" , G_CALLBACK (selection_changed), button); |
817 | gtk_box_append (GTK_BOX (bbox), child: button); |
818 | |
819 | button = gtk_button_new_with_mnemonic (label: ">> (_W)" ); |
820 | gtk_widget_set_sensitive (widget: button, FALSE); |
821 | g_signal_connect (button, "clicked" , G_CALLBACK (add_right_clicked), top_right_tree_view); |
822 | g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (left_tree_view)), |
823 | "changed" , G_CALLBACK (selection_changed), button); |
824 | gtk_box_append (GTK_BOX (bbox), child: button); |
825 | |
826 | bbox = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 0); |
827 | gtk_box_append (GTK_BOX (vbox2), child: bbox); |
828 | |
829 | button = gtk_button_new_with_mnemonic (label: "<< (_E)" ); |
830 | gtk_widget_set_sensitive (widget: button, FALSE); |
831 | g_signal_connect (button, "clicked" , G_CALLBACK (add_left_clicked), bottom_right_tree_view); |
832 | g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (bottom_right_tree_view)), |
833 | "changed" , G_CALLBACK (selection_changed), button); |
834 | gtk_box_append (GTK_BOX (bbox), child: button); |
835 | |
836 | button = gtk_button_new_with_mnemonic (label: ">> (_R)" ); |
837 | gtk_widget_set_sensitive (widget: button, FALSE); |
838 | g_signal_connect (button, "clicked" , G_CALLBACK (add_right_clicked), bottom_right_tree_view); |
839 | g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (left_tree_view)), |
840 | "changed" , G_CALLBACK (selection_changed), button); |
841 | gtk_box_append (GTK_BOX (bbox), child: button); |
842 | |
843 | |
844 | /* Right Pane */ |
845 | vbox2 = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 8); |
846 | gtk_box_append (GTK_BOX (hbox), child: vbox2); |
847 | |
848 | swindow = gtk_scrolled_window_new (); |
849 | gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow), hscrollbar_policy: GTK_POLICY_AUTOMATIC, vscrollbar_policy: GTK_POLICY_AUTOMATIC); |
850 | gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (top_right_tree_view), FALSE); |
851 | cell = gtk_cell_renderer_text_new (); |
852 | gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (top_right_tree_view), position: -1, |
853 | NULL, cell, "text" , 0, NULL); |
854 | cell = gtk_cell_renderer_toggle_new (); |
855 | g_signal_connect (cell, "toggled" , G_CALLBACK (set_visible), top_right_tree_view); |
856 | column = gtk_tree_view_column_new_with_attributes (NULL, cell, NULL); |
857 | gtk_tree_view_column_set_cell_data_func (tree_column: column, cell_renderer: cell, func: get_visible, NULL, NULL); |
858 | gtk_tree_view_append_column (GTK_TREE_VIEW (top_right_tree_view), column); |
859 | |
860 | gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (swindow), child: top_right_tree_view); |
861 | gtk_box_append (GTK_BOX (vbox2), child: swindow); |
862 | |
863 | swindow = gtk_scrolled_window_new (); |
864 | gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow), hscrollbar_policy: GTK_POLICY_AUTOMATIC, vscrollbar_policy: GTK_POLICY_AUTOMATIC); |
865 | gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (bottom_right_tree_view), FALSE); |
866 | cell = gtk_cell_renderer_text_new (); |
867 | gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (bottom_right_tree_view), position: -1, |
868 | NULL, cell, "text" , 0, NULL); |
869 | cell = gtk_cell_renderer_toggle_new (); |
870 | g_signal_connect (cell, "toggled" , G_CALLBACK (set_visible), bottom_right_tree_view); |
871 | column = gtk_tree_view_column_new_with_attributes (NULL, cell, NULL); |
872 | gtk_tree_view_column_set_cell_data_func (tree_column: column, cell_renderer: cell, func: get_visible, NULL, NULL); |
873 | gtk_tree_view_append_column (GTK_TREE_VIEW (bottom_right_tree_view), column); |
874 | gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (swindow), child: bottom_right_tree_view); |
875 | gtk_box_append (GTK_BOX (vbox2), child: swindow); |
876 | |
877 | |
878 | /* Drag and Drop */ |
879 | targets = gdk_content_formats_new_for_gtype (GTK_TYPE_TREE_ROW_DATA); |
880 | gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (left_tree_view), |
881 | start_button_mask: GDK_BUTTON1_MASK, |
882 | formats: targets, |
883 | actions: GDK_ACTION_MOVE); |
884 | gtk_tree_view_enable_model_drag_dest (GTK_TREE_VIEW (left_tree_view), |
885 | formats: targets, |
886 | actions: GDK_ACTION_MOVE); |
887 | |
888 | gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (top_right_tree_view), |
889 | start_button_mask: GDK_BUTTON1_MASK, |
890 | formats: targets, |
891 | actions: GDK_ACTION_MOVE); |
892 | gtk_tree_view_enable_model_drag_dest (GTK_TREE_VIEW (top_right_tree_view), |
893 | formats: targets, |
894 | actions: GDK_ACTION_MOVE); |
895 | |
896 | gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (bottom_right_tree_view), |
897 | start_button_mask: GDK_BUTTON1_MASK, |
898 | formats: targets, |
899 | actions: GDK_ACTION_MOVE); |
900 | gtk_tree_view_enable_model_drag_dest (GTK_TREE_VIEW (bottom_right_tree_view), |
901 | formats: targets, |
902 | actions: GDK_ACTION_MOVE); |
903 | gdk_content_formats_unref (formats: targets); |
904 | |
905 | gtk_box_append (GTK_BOX (vbox), child: gtk_separator_new (orientation: GTK_ORIENTATION_HORIZONTAL)); |
906 | |
907 | hbox = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 8); |
908 | gtk_box_append (GTK_BOX (vbox), child: hbox); |
909 | button = gtk_button_new_with_mnemonic (label: "_Add new Column" ); |
910 | g_signal_connect (button, "clicked" , G_CALLBACK (add_clicked), left_tree_model); |
911 | gtk_box_append (GTK_BOX (hbox), child: button); |
912 | |
913 | gtk_widget_show (widget: window); |
914 | |
915 | while (!done) |
916 | g_main_context_iteration (NULL, TRUE); |
917 | |
918 | return 0; |
919 | } |
920 | |