1 /* Dia -- a diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
4 * diagram_tree.c : a tree showing open diagrams
5 * Copyright (C) 2001 Jose A Ortega Ruiz
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 #include "properties.h"
24 #include "prop_text.h"
25 #include "diagram_tree_menu.h"
26 #include "diagram_tree_menu_callbacks.h"
27 #include "diagram_tree.h"
30 GtkCTree
*tree
; /* the tree widget */
31 GtkCTreeNode
*last
; /* last clicked node */
32 GtkMenu
*dia_menu
; /* diagram popup menu */
33 GtkMenu
*object_menu
; /* object popup menu */
34 GtkCListCompareFunc dia_cmp
; /* diagram ordering function */
35 GtkCListCompareFunc obj_cmp
; /* object ordering function */
40 update_object(DiagramTree
*tree
, GtkCTreeNode
*node
, Object
*object
);
43 raise_display(Diagram
*d
)
46 GSList
*dlist
= d
->displays
;
48 DDisplay
*dis
= (DDisplay
*)dlist
->data
;
49 gdk_window_raise(dis
->shell
->window
);
50 gtk_widget_draw(dis
->shell
, NULL
);
51 dlist
= g_slist_next(dlist
);
57 select_and_raise(DiagramTree
*tree
, GtkCTreeNode
*node
)
61 (GTK_CTREE_ROW(node
)->is_leaf
)? GTK_CTREE_ROW(node
)->parent
: node
;
62 d
= (Diagram
*)gtk_ctree_node_get_row_data(tree
->tree
, dnode
);
64 if (GTK_CTREE_ROW(node
)->is_leaf
) {
65 Object
*o
= (Object
*)gtk_ctree_node_get_row_data(tree
->tree
, node
);
67 update_object(tree
, node
, o
);
68 diagram_unselect_objects(d
, d
->data
->selected
);
72 gtk_ctree_select(tree
->tree
, node
);
80 select_tree_widget(GtkCTree
*tree
, GtkCTreeNode
*node
,
81 gint column
, DiagramTree
*data
)
83 if (GTK_CTREE_ROW(node
)->is_leaf
) {
84 Object
*o
= (Object
*)gtk_ctree_node_get_row_data(tree
, node
);
85 if (o
) update_object(data
, node
, o
);
90 button_press_callback(GtkCTree
*tree
, GdkEventButton
*event
,
96 gtk_clist_get_selection_info(GTK_CLIST(tree
), event
->x
, event
->y
,
98 if (row
!= -1) dtree
->last
= gtk_ctree_node_nth(tree
, row
);
99 else dtree
->last
= NULL
;
101 if (dtree
->last
&& event
->type
== GDK_2BUTTON_PRESS
) {
102 select_and_raise(dtree
, dtree
->last
);
103 } else if (dtree
->last
&& event
->type
== GDK_BUTTON_PRESS
104 && event
->button
== 3) {
105 GtkMenu
*menu
= (GTK_CTREE_ROW(dtree
->last
)->is_leaf
)?
106 dtree
->object_menu
: dtree
->dia_menu
;
107 gtk_menu_popup(menu
, NULL
, NULL
, NULL
, NULL
, 3, event
->time
);
112 /* private functions */
114 sort_objects(DiagramTree
*tree
, GtkCTreeNode
*node
)
117 GtkCTreeNode
*dnode
=
118 (GTK_CTREE_ROW(node
)->is_leaf
)? GTK_CTREE_ROW(node
)->parent
: node
;
119 gtk_clist_set_compare_func(GTK_CLIST(tree
->tree
), tree
->obj_cmp
);
120 gtk_ctree_sort_node(tree
->tree
, dnode
);
125 sort_diagrams(DiagramTree
*tree
)
128 gtk_clist_set_compare_func(GTK_CLIST(tree
->tree
), tree
->dia_cmp
);
129 gtk_ctree_sort_node(tree
->tree
, NULL
);
134 get_diagram_objects(Diagram
*diagram
)
136 GList
*result
= NULL
;
137 GPtrArray
*layers
= diagram
->data
->layers
;
141 for (k
= 0; k
< layers
->len
; k
++) {
142 lay
= (Layer
*)g_ptr_array_index(layers
, k
);
143 result
= g_list_concat(result
, g_list_copy(lay
->objects
));
150 create_object_pixmap(Object
*object
, GtkWidget
*parent
,
151 GdkPixmap
**pixmap
, GdkBitmap
**mask
)
159 style
= gtk_widget_get_style(parent
);
161 if (object
->type
->pixmap
!= NULL
) {
163 gdk_pixmap_colormap_create_from_xpm_d(NULL
,
164 gtk_widget_get_colormap(parent
),
166 &style
->bg
[GTK_STATE_NORMAL
],
167 object
->type
->pixmap
);
168 } else if (object
->type
->pixmap_file
!= NULL
) {
170 gdk_pixmap_colormap_create_from_xpm(NULL
,
171 gtk_widget_get_colormap(parent
),
173 &style
->bg
[GTK_STATE_NORMAL
],
174 object
->type
->pixmap_file
);
182 get_object_name(Object
*object
)
185 static gchar BUFFER
[SIZE
];
187 Property
*prop
= NULL
;
188 gchar
*result
= NULL
;
190 prop
= object_prop_by_name(object
, "name");
191 if (prop
) result
= ((StringProperty
*)prop
)->string_data
;
193 prop
= object_prop_by_name(object
, "text");
194 if (prop
) result
= ((TextProperty
*)prop
)->text_data
;
196 if (!result
) result
= object
->type
->name
;
198 g_snprintf(BUFFER
, SIZE
, " %s", result
);
199 (void)g_strdelimit(BUFFER
, "\n", ' ');
204 update_object(DiagramTree
*tree
, GtkCTreeNode
*node
, Object
*object
)
206 char *text
= get_object_name(object
);
208 gtk_ctree_node_get_text(tree
->tree
, node
, 0, &old
);
209 if (!old
|| (text
&& strcmp(text
, old
))) {
212 create_object_pixmap(object
, GTK_WIDGET(tree
->tree
), &pixmap
, &mask
);
213 gtk_ctree_set_node_info(tree
->tree
, node
, text
, 3, pixmap
, mask
,
214 pixmap
, mask
, TRUE
, TRUE
);
215 sort_objects(tree
, node
);
220 create_object_node(DiagramTree
*tree
, GtkCTreeNode
*dnode
, Object
*obj
)
222 gboolean expanded
= GTK_CTREE_ROW(dnode
)->expanded
;
223 char *text
[] = {NULL
};
227 text
[0] = get_object_name(obj
);
228 create_object_pixmap(obj
, GTK_WIDGET(tree
->tree
), &pixmap
, &mask
);
229 node
= gtk_ctree_insert_node(tree
->tree
, dnode
, NULL
, text
, 3,
230 pixmap
, mask
, NULL
, NULL
, TRUE
, FALSE
);
231 gtk_ctree_node_set_row_data(tree
->tree
, node
, (gpointer
)obj
);
232 if (expanded
) gtk_ctree_expand(tree
->tree
, dnode
);
233 sort_objects(tree
, dnode
);
237 create_diagram_children(DiagramTree
*tree
, GtkCTreeNode
*node
,
240 GList
*objects
= get_diagram_objects(diagram
);
242 create_object_node(tree
, node
, (Object
*)objects
->data
);
243 objects
= g_list_next(objects
);
245 g_list_free(objects
);
249 update_diagram_children(DiagramTree
*tree
, GtkCTreeNode
*node
,
252 GList
*dobjects
= get_diagram_objects(diagram
);
253 GList
*org
= dobjects
;
254 GtkCTreeNode
*child
= GTK_CTREE_ROW(node
)->children
;
257 GtkCTreeNode
*current
= child
;
258 child
= GTK_CTREE_ROW(child
)->sibling
;
259 if (!g_list_find(dobjects
, gtk_ctree_node_get_row_data(tree
->tree
,
261 gtk_ctree_remove_node(tree
->tree
, current
);
265 if (!gtk_ctree_find_by_row_data(tree
->tree
, node
, dobjects
->data
))
266 create_object_node(tree
, node
, (Object
*)dobjects
->data
);
267 dobjects
= g_list_next(dobjects
);
270 sort_objects(tree
, node
);
273 /* external interface */
275 diagram_tree_new(GList
*diagrams
)
277 DiagramTree
*result
= g_new(DiagramTree
, 1);
278 result
->tree
= GTK_CTREE(gtk_ctree_new(1, 0));
280 result
->dia_cmp
= result
->obj_cmp
= NULL
;
281 gtk_signal_connect(GTK_OBJECT(result
->tree
), "tree-select-row",
282 GTK_SIGNAL_FUNC(select_tree_widget
),
284 gtk_signal_connect(GTK_OBJECT(result
->tree
),
285 "button_press_event",
286 GTK_SIGNAL_FUNC(button_press_callback
),
288 result
->dia_menu
= result
->object_menu
= NULL
;
290 diagram_tree_add(result
, (Diagram
*)diagrams
->data
);
291 diagrams
= g_list_next(diagrams
);
297 diagram_tree_delete(DiagramTree
*tree
)
299 g_return_if_fail(tree
);
300 gtk_widget_destroy(GTK_WIDGET(tree
->tree
));
304 diagram_tree_add(DiagramTree
*tree
, Diagram
*diagram
)
308 && !gtk_ctree_find_by_row_data(tree
->tree
, NULL
, (gpointer
)diagram
))
310 char *text
[] = {g_basename(diagram
->filename
)};
312 gtk_ctree_insert_node(tree
->tree
, NULL
, NULL
, text
, 1,
313 NULL
, NULL
, NULL
, NULL
, FALSE
, FALSE
);
314 gtk_ctree_node_set_row_data(tree
->tree
, node
, (gpointer
)diagram
);
315 create_diagram_children(tree
, node
, diagram
);
322 diagram_tree_remove(DiagramTree
*tree
, Diagram
*diagram
)
327 && (node
= gtk_ctree_find_by_row_data(tree
->tree
, NULL
,
329 gtk_ctree_remove_node(tree
->tree
, node
);
334 diagram_tree_update(DiagramTree
*tree
, Diagram
*diagram
)
337 if (diagram
->modified
) {
338 GtkCTreeNode
*dnode
=
339 gtk_ctree_find_by_row_data(tree
->tree
, NULL
, (gpointer
)diagram
);
340 if (dnode
) update_diagram_children(tree
, dnode
, diagram
);
341 else diagram_tree_add(tree
, diagram
);
347 diagram_tree_update_name(DiagramTree
*tree
, Diagram
*diagram
)
351 g_return_if_fail(diagram
);
352 node
= gtk_ctree_find_by_row_data(tree
->tree
, NULL
, (gpointer
)diagram
);
354 gtk_ctree_node_set_text(tree
->tree
, node
, 0,
355 g_basename(diagram
->filename
));
362 diagram_tree_add_object(DiagramTree
*tree
, Diagram
*diagram
, Object
*object
)
365 g_return_if_fail(diagram
);
367 GtkCTreeNode
*dnode
=
368 gtk_ctree_find_by_row_data(tree
->tree
, NULL
, (gpointer
)diagram
);
369 if (!dnode
) diagram_tree_add(tree
, diagram
);
370 else if (!gtk_ctree_find_by_row_data(tree
->tree
, dnode
, (gpointer
)object
))
371 create_object_node(tree
, dnode
, object
);
377 diagram_tree_add_objects(DiagramTree
*tree
, Diagram
*diagram
, GList
*objects
)
380 g_return_if_fail(diagram
);
382 diagram_tree_add_object(tree
, diagram
, (Object
*)objects
->data
);
383 objects
= g_list_next(objects
);
390 diagram_tree_remove_object(DiagramTree
*tree
, Object
*object
)
395 gtk_ctree_find_by_row_data(tree
->tree
, NULL
, (gpointer
)object
);
396 if (node
) gtk_ctree_remove_node(tree
->tree
, node
);
402 diagram_tree_remove_objects(DiagramTree
*tree
, GList
*objects
)
406 diagram_tree_remove_object(tree
, (Object
*)objects
->data
);
407 objects
= g_list_next(objects
);
413 diagram_tree_update_object(DiagramTree
*tree
, Diagram
*diagram
,
417 g_return_if_fail(diagram
);
420 gtk_ctree_find_by_row_data(tree
->tree
, NULL
,(gpointer
)object
);
422 update_object(tree
, node
, object
);
429 diagram_tree_raise(DiagramTree
*tree
)
431 if (tree
&& tree
->last
) {
432 select_and_raise(tree
, tree
->last
);
437 diagram_tree_show_properties(const DiagramTree
*tree
)
439 if (tree
&& tree
->last
&& GTK_CTREE_ROW(tree
->last
)->is_leaf
) {
440 GtkCTreeNode
*parent
= GTK_CTREE_ROW(tree
->last
)->parent
;
442 Diagram
*dia
= (Diagram
*)gtk_ctree_node_get_row_data(tree
->tree
, parent
);
444 (Object
*)gtk_ctree_node_get_row_data(tree
->tree
, tree
->last
);
445 properties_show(dia
, obj
);
450 /* sorting functions */
452 cmp_name_(GtkCList
*tree
, GtkCListRow
*lhm
, GtkCListRow
*rhm
)
455 gchar
*name1
= lhm
->cell
->u
.text
;
456 gchar
*name2
= rhm
->cell
->u
.text
;
457 if (name1
&& !name2
) k
= -1;
458 else if (!name1
&& name2
) k
= 1;
459 else if (!name1
&& !name2
) k
= 0;
460 else k
= strcmp(name1
, name2
);
462 if (k
< 0) return -1;
467 cmp_type_(GtkCList
*tree
, GtkCListRow
*lhm
, GtkCListRow
*rhm
)
470 Object
*o1
= (Object
*)lhm
->data
;
471 Object
*o2
= (Object
*)rhm
->data
;
472 k
= strcmp(o1
->type
->name
, o2
->type
->name
);
474 if (k
< 0) return -1;
478 static GtkCListCompareFunc cmp_funcs_
[] = {
479 (GtkCListCompareFunc
)cmp_name_
,
480 (GtkCListCompareFunc
)cmp_type_
,
485 diagram_tree_sort_objects(DiagramTree
*tree
, DiagramTreeSortType type
)
487 if (tree
&& type
<= DIA_TREE_SORT_INSERT
&& tree
->last
) {
488 GtkCTreeNode
*node
= GTK_CTREE_ROW(tree
->last
)->is_leaf
?
489 GTK_CTREE_ROW(tree
->last
)->parent
: tree
->last
;
490 gtk_clist_set_compare_func(GTK_CLIST(tree
->tree
), cmp_funcs_
[type
]);
491 gtk_ctree_sort_node(tree
->tree
, node
);
496 diagram_tree_sort_all_objects(DiagramTree
*tree
, DiagramTreeSortType type
)
498 /* FIXME: should not depend on tree->last != NULL */
499 if (tree
&& type
<= DIA_TREE_SORT_INSERT
&& tree
->last
) {
500 GtkCTreeNode
*node
= GTK_CTREE_ROW(tree
->last
)->is_leaf
?
501 GTK_CTREE_ROW(tree
->last
)->parent
: tree
->last
;
502 while (GTK_CTREE_NODE_PREV(node
)) node
= GTK_CTREE_NODE_PREV(node
);
504 gtk_clist_set_compare_func(GTK_CLIST(tree
->tree
), cmp_funcs_
[type
]);
505 gtk_ctree_sort_node(tree
->tree
, node
);
506 node
= GTK_CTREE_ROW(node
)->sibling
;
512 diagram_tree_sort_diagrams(DiagramTree
*tree
, DiagramTreeSortType type
)
514 if (tree
&& type
<= DIA_TREE_SORT_INSERT
&& type
!= DIA_TREE_SORT_TYPE
) {
515 gtk_clist_set_compare_func(GTK_CLIST(tree
->tree
), cmp_funcs_
[type
]);
516 gtk_ctree_sort_node(tree
->tree
, NULL
);
521 diagram_tree_set_object_sort_type(DiagramTree
*tree
, DiagramTreeSortType type
)
523 if (tree
&& type
<= DIA_TREE_SORT_INSERT
) {
524 tree
->obj_cmp
= cmp_funcs_
[type
];
525 diagram_tree_sort_all_objects(tree
, type
);
530 diagram_tree_set_diagram_sort_type(DiagramTree
*tree
, DiagramTreeSortType type
)
532 if (tree
&& type
<= DIA_TREE_SORT_INSERT
&& type
!= DIA_TREE_SORT_TYPE
) {
533 tree
->dia_cmp
= cmp_funcs_
[type
];
534 diagram_tree_sort_diagrams(tree
, type
);
538 static DiagramTreeSortType
539 sort_type_lookup(GtkCListCompareFunc func
)
542 for (k
= 0; k
<= DIA_TREE_SORT_INSERT
; ++k
) {
543 if (cmp_funcs_
[k
] == func
) return k
;
545 g_assert_not_reached();
550 diagram_tree_diagram_sort_type(const DiagramTree
*tree
)
553 return sort_type_lookup(tree
->dia_cmp
);
555 return DIA_TREE_SORT_INSERT
;
560 diagram_tree_object_sort_type(const DiagramTree
*tree
)
563 return sort_type_lookup(tree
->obj_cmp
);
565 return DIA_TREE_SORT_INSERT
;
569 diagram_tree_attach_dia_menu(DiagramTree
*tree
, GtkWidget
*menu
)
572 tree
->dia_menu
= GTK_MENU(menu
);
577 diagram_tree_attach_obj_menu(DiagramTree
*tree
, GtkWidget
*menu
)
580 tree
->object_menu
= GTK_MENU(menu
);
585 diagram_tree_widget(const DiagramTree
*tree
)
587 g_return_val_if_fail(tree
, NULL
);
588 return GTK_WIDGET(tree
->tree
);