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 * patch to center objects in drawing viewport when doing "Locate"
8 * Copyright (C) 2003 Andrew Halper
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28 #include "properties.h"
29 #include "prop_text.h"
30 #include "diagram_tree_menu.h"
31 #include "diagram_tree_menu_callbacks.h"
32 #include "diagram_tree.h"
35 GtkCTree
*tree
; /* the tree widget */
36 GtkCTreeNode
*last
; /* last clicked node */
37 DiagramTreeMenus
*menus
; /* popup menus */
38 GtkCListCompareFunc dia_cmp
; /* diagram ordering function */
39 GtkCListCompareFunc obj_cmp
; /* object ordering function */
40 GList
*hidden
; /* list of hidden object types */
43 #define is_object_node(node) (GTK_CTREE_ROW(node)->is_leaf)
46 find_hidden_type(gconstpointer type
, gconstpointer object_type
)
50 || strcmp((const gchar
*)object_type
, (const gchar
*)type
);
53 #define is_hidden_type(dtree, type) \
54 g_list_find_custom(dtree->hidden, (gpointer)type, find_hidden_type)
56 #define is_hidden_object(dtree, object) \
57 is_hidden_type(dtree, ((Object *)object)->type->name)
60 update_object(DiagramTree
*tree
, GtkCTreeNode
*node
, Object
*object
);
63 select_node(DiagramTree
*tree
, GtkCTreeNode
*node
, gboolean raise
)
66 GtkCTreeNode
*dnode
= (is_object_node(node
)) ?
67 GTK_CTREE_ROW(node
)->parent
: node
;
68 Object
*o
= (Object
*)gtk_ctree_node_get_row_data(tree
->tree
, node
);
71 d
= (Diagram
*)gtk_ctree_node_get_row_data(tree
->tree
, dnode
);
73 GSList
*dlist
= d
->displays
;
74 if (is_object_node(node
)) {
76 update_object(tree
, node
, o
);
77 diagram_unselect_objects(d
, d
->data
->selected
);
82 DDisplay
*ddisp
= (DDisplay
*)dlist
->data
;
84 gdk_window_raise(ddisp
->shell
->window
);
85 /* if object exists */
87 ddisplay_scroll_to_object(ddisp
, o
);
90 gtk_widget_draw(ddisp
->shell
, NULL
);
91 dlist
= g_slist_next(dlist
);
97 update_last_node(DiagramTree
*tree
)
99 if (is_object_node(tree
->last
)) {
100 Object
*o
= (Object
*)gtk_ctree_node_get_row_data(tree
->tree
, tree
->last
);
101 if (o
) update_object(tree
, tree
->last
, o
);
105 /* signal handlers */
107 button_press_callback(GtkCTree
*tree
, GdkEventButton
*event
,
113 gtk_clist_get_selection_info(GTK_CLIST(tree
), event
->x
, event
->y
,
115 if (row
!= -1) dtree
->last
= gtk_ctree_node_nth(tree
, row
);
116 else dtree
->last
= NULL
;
118 if (dtree
->last
) update_last_node(dtree
);
121 if (dtree
->last
&& event
->type
== GDK_2BUTTON_PRESS
) {
122 /* equivalent of "Locate" */
123 select_node(dtree
, dtree
->last
, TRUE
);
124 } else if (dtree
->last
&& event
->type
== GDK_BUTTON_PRESS
) {
125 if (event
->button
== 3) {
126 DiagramTreeMenuType menu
= (is_object_node(dtree
->last
))?
127 DIA_MENU_OBJECT
: DIA_MENU_DIAGRAM
;
128 diagram_tree_menus_popup_menu(dtree
->menus
, menu
, event
->time
);
129 } else if (event
->button
== 1) {
130 select_node(dtree
, dtree
->last
, FALSE
);
131 /* need to return FALSE to let gtk process it further */
138 /* private functions */
140 sort_objects(DiagramTree
*tree
, GtkCTreeNode
*node
)
143 GtkCTreeNode
*dnode
=
144 (is_object_node(node
))? GTK_CTREE_ROW(node
)->parent
: node
;
145 gtk_clist_set_compare_func(GTK_CLIST(tree
->tree
), tree
->obj_cmp
);
146 gtk_ctree_sort_node(tree
->tree
, dnode
);
151 sort_diagrams(DiagramTree
*tree
)
154 gtk_clist_set_compare_func(GTK_CLIST(tree
->tree
), tree
->dia_cmp
);
155 gtk_ctree_sort_node(tree
->tree
, NULL
);
160 get_diagram_objects(Diagram
*diagram
)
162 GList
*result
= NULL
;
163 GPtrArray
*layers
= diagram
->data
->layers
;
167 for (k
= 0; k
< layers
->len
; k
++) {
168 lay
= (Layer
*)g_ptr_array_index(layers
, k
);
169 result
= g_list_concat(result
, g_list_copy(lay
->objects
));
176 create_object_pixmap(Object
*object
, GtkWidget
*parent
,
177 GdkPixmap
**pixmap
, GdkBitmap
**mask
)
185 style
= gtk_widget_get_style(parent
);
187 if (object
->type
->pixmap
!= NULL
) {
189 gdk_pixmap_colormap_create_from_xpm_d(NULL
,
190 gtk_widget_get_colormap(parent
),
192 &style
->bg
[GTK_STATE_NORMAL
],
193 object
->type
->pixmap
);
194 } else if (object
->type
->pixmap_file
!= NULL
) {
196 gdk_pixmap_colormap_create_from_xpm(NULL
,
197 gtk_widget_get_colormap(parent
),
199 &style
->bg
[GTK_STATE_NORMAL
],
200 object
->type
->pixmap_file
);
208 get_object_name(Object
*object
)
211 static gchar BUFFER
[SIZE
];
213 Property
*prop
= NULL
;
214 gchar
*result
= NULL
;
216 prop
= object_prop_by_name(object
, "name");
217 if (prop
) result
= ((StringProperty
*)prop
)->string_data
;
219 prop
= object_prop_by_name(object
, "text");
220 if (prop
) result
= ((TextProperty
*)prop
)->text_data
;
222 if (!result
) result
= object
->type
->name
;
224 g_snprintf(BUFFER
, SIZE
, " %s", result
);
225 (void)g_strdelimit(BUFFER
, "\n", ' ');
228 prop
->ops
->free(prop
);
234 update_object(DiagramTree
*tree
, GtkCTreeNode
*node
, Object
*object
)
236 char *text
= get_object_name(object
);
238 gtk_ctree_node_get_text(tree
->tree
, node
, 0, &old
);
239 if (!old
|| (text
&& strcmp(text
, old
))) {
242 create_object_pixmap(object
, GTK_WIDGET(tree
->tree
), &pixmap
, &mask
);
243 gtk_ctree_set_node_info(tree
->tree
, node
, text
, 3, pixmap
, mask
,
244 pixmap
, mask
, TRUE
, TRUE
);
245 sort_objects(tree
, node
);
250 create_object_node(DiagramTree
*tree
, GtkCTreeNode
*dnode
, Object
*obj
)
252 gboolean expanded
= GTK_CTREE_ROW(dnode
)->expanded
;
253 char *text
[] = {NULL
};
257 text
[0] = get_object_name(obj
);
258 create_object_pixmap(obj
, GTK_WIDGET(tree
->tree
), &pixmap
, &mask
);
259 node
= gtk_ctree_insert_node(tree
->tree
, dnode
, NULL
, text
, 3,
260 pixmap
, mask
, NULL
, NULL
, TRUE
, FALSE
);
261 gtk_ctree_node_set_row_data(tree
->tree
, node
, (gpointer
)obj
);
262 if (expanded
) gtk_ctree_expand(tree
->tree
, dnode
);
263 sort_objects(tree
, dnode
);
268 create_diagram_children(DiagramTree
*tree
, GtkCTreeNode
*node
,
271 GList
*objects
= get_diagram_objects(diagram
);
273 if (!is_hidden_object(tree
, objects
->data
)) {
274 create_object_node(tree
, node
, (Object
*)objects
->data
);
276 objects
= g_list_next(objects
);
278 g_list_free(objects
);
282 update_diagram_children(DiagramTree
*tree
, GtkCTreeNode
*node
,
285 GList
*dobjects
= get_diagram_objects(diagram
);
286 GList
*org
= dobjects
;
287 GtkCTreeNode
*child
= GTK_CTREE_ROW(node
)->children
;
290 GtkCTreeNode
*current
= child
;
291 gpointer obj
= gtk_ctree_node_get_row_data(tree
->tree
, current
);
292 child
= GTK_CTREE_ROW(child
)->sibling
;
293 if (!g_list_find(dobjects
, obj
) || is_hidden_object(tree
, obj
))
294 gtk_ctree_remove_node(tree
->tree
, current
);
298 if (!is_hidden_object(tree
, dobjects
->data
)
299 && !gtk_ctree_find_by_row_data(tree
->tree
, node
, dobjects
->data
))
300 create_object_node(tree
, node
, (Object
*)dobjects
->data
);
301 dobjects
= g_list_next(dobjects
);
304 sort_objects(tree
, node
);
307 /* external interface */
309 diagram_tree_new(GList
*diagrams
, GtkWindow
*window
,
310 DiagramTreeSortType dia_sort
,
311 DiagramTreeSortType obj_sort
)
313 DiagramTree
*result
= g_new(DiagramTree
, 1);
314 result
->tree
= GTK_CTREE(gtk_ctree_new(1, 0));
316 result
->dia_cmp
= result
->obj_cmp
= NULL
;
317 result
->hidden
= NULL
;
318 gtk_signal_connect(GTK_OBJECT(result
->tree
),
319 "button_press_event",
320 GTK_SIGNAL_FUNC(button_press_callback
),
323 diagram_tree_add(result
, (Diagram
*)diagrams
->data
);
324 diagrams
= g_list_next(diagrams
);
326 diagram_tree_set_diagram_sort_type(result
, dia_sort
);
327 diagram_tree_set_object_sort_type(result
, obj_sort
);
328 result
->menus
= diagram_tree_menus_new(result
, window
);
333 diagram_tree_delete(DiagramTree
*tree
)
335 g_return_if_fail(tree
);
336 gtk_widget_destroy(GTK_WIDGET(tree
->tree
));
340 diagram_tree_add(DiagramTree
*tree
, Diagram
*diagram
)
344 && !gtk_ctree_find_by_row_data(tree
->tree
, NULL
, (gpointer
)diagram
))
346 char *text
[] = {(char *)g_basename(diagram
->filename
)};
348 gtk_ctree_insert_node(tree
->tree
, NULL
, NULL
, text
, 1,
349 NULL
, NULL
, NULL
, NULL
, FALSE
, FALSE
);
350 gtk_ctree_node_set_row_data(tree
->tree
, node
, (gpointer
)diagram
);
351 create_diagram_children(tree
, node
, diagram
);
358 diagram_tree_remove(DiagramTree
*tree
, Diagram
*diagram
)
363 && (node
= gtk_ctree_find_by_row_data(tree
->tree
, NULL
,
365 gtk_ctree_remove_node(tree
->tree
, node
);
370 diagram_tree_update(DiagramTree
*tree
, Diagram
*diagram
)
373 if (diagram
->modified
) {
374 GtkCTreeNode
*dnode
=
375 gtk_ctree_find_by_row_data(tree
->tree
, NULL
, (gpointer
)diagram
);
376 if (dnode
) update_diagram_children(tree
, dnode
, diagram
);
377 else diagram_tree_add(tree
, diagram
);
383 diagram_tree_update_all(DiagramTree
*tree
)
386 GtkCTreeNode
*node
= gtk_ctree_node_nth(tree
->tree
, 0);
388 Diagram
*d
= (Diagram
*)gtk_ctree_node_get_row_data(tree
->tree
, node
);
389 if (d
) update_diagram_children(tree
, node
, d
);
390 node
= GTK_CTREE_ROW(node
)->sibling
;
396 diagram_tree_update_name(DiagramTree
*tree
, Diagram
*diagram
)
400 g_return_if_fail(diagram
);
401 node
= gtk_ctree_find_by_row_data(tree
->tree
, NULL
, (gpointer
)diagram
);
403 gtk_ctree_node_set_text(tree
->tree
, node
, 0,
404 g_basename(diagram
->filename
));
411 diagram_tree_add_object(DiagramTree
*tree
, Diagram
*diagram
, Object
*object
)
414 g_return_if_fail(diagram
);
415 if (object
&& !is_hidden_object(tree
, object
)) {
416 GtkCTreeNode
*dnode
=
417 gtk_ctree_find_by_row_data(tree
->tree
, NULL
, (gpointer
)diagram
);
418 if (!dnode
) diagram_tree_add(tree
, diagram
);
419 else if (!gtk_ctree_find_by_row_data(tree
->tree
, dnode
, (gpointer
)object
))
420 create_object_node(tree
, dnode
, object
);
426 diagram_tree_add_objects(DiagramTree
*tree
, Diagram
*diagram
, GList
*objects
)
429 g_return_if_fail(diagram
);
431 diagram_tree_add_object(tree
, diagram
, (Object
*)objects
->data
);
432 objects
= g_list_next(objects
);
439 diagram_tree_remove_object(DiagramTree
*tree
, Object
*object
)
444 gtk_ctree_find_by_row_data(tree
->tree
, NULL
, (gpointer
)object
);
445 if (node
) gtk_ctree_remove_node(tree
->tree
, node
);
451 diagram_tree_remove_objects(DiagramTree
*tree
, GList
*objects
)
455 diagram_tree_remove_object(tree
, (Object
*)objects
->data
);
456 objects
= g_list_next(objects
);
462 diagram_tree_update_object(DiagramTree
*tree
, Diagram
*diagram
,
466 g_return_if_fail(diagram
);
469 gtk_ctree_find_by_row_data(tree
->tree
, NULL
,(gpointer
)object
);
471 update_object(tree
, node
, object
);
478 diagram_tree_raise(DiagramTree
*tree
)
480 if (tree
&& tree
->last
) {
481 select_node(tree
, tree
->last
, TRUE
);
486 diagram_tree_show_properties(const DiagramTree
*tree
)
488 if (tree
&& tree
->last
&& is_object_node(tree
->last
)) {
489 GtkCTreeNode
*parent
= GTK_CTREE_ROW(tree
->last
)->parent
;
491 Diagram
*dia
= (Diagram
*)gtk_ctree_node_get_row_data(tree
->tree
, parent
);
493 (Object
*)gtk_ctree_node_get_row_data(tree
->tree
, tree
->last
);
494 properties_show(dia
, obj
);
500 diagram_tree_hide_type(DiagramTree
*tree
)
502 if (tree
&& tree
->last
&& is_object_node(tree
->last
)) {
504 (Object
*)gtk_ctree_node_get_row_data(tree
->tree
, tree
->last
);
505 g_assert(!is_hidden_object(tree
, obj
));
506 diagram_tree_hide_explicit_type(tree
, obj
->type
->name
);
507 return obj
->type
->name
;
513 diagram_tree_hide_explicit_type(DiagramTree
*tree
, const gchar
*type
)
516 tree
->hidden
= g_list_prepend(tree
->hidden
, g_strdup(type
));
517 diagram_tree_update_all(tree
);
518 diagram_tree_menus_add_hidden_type(tree
->menus
, type
);
523 diagram_tree_unhide_type(DiagramTree
*tree
, const gchar
*type
)
526 GList
*t
= is_hidden_type(tree
, type
);
528 tree
->hidden
= g_list_remove_link(tree
->hidden
, t
);
531 diagram_tree_update_all(tree
);
536 /* sorting functions */
538 cmp_name_(GtkCList
*tree
, GtkCListRow
*lhm
, GtkCListRow
*rhm
)
541 gchar
*name1
= lhm
->cell
->u
.text
;
542 gchar
*name2
= rhm
->cell
->u
.text
;
543 if (name1
&& !name2
) k
= -1;
544 else if (!name1
&& name2
) k
= 1;
545 else if (!name1
&& !name2
) k
= 0;
546 else k
= strcmp(name1
, name2
);
548 if (k
< 0) return -1;
553 cmp_type_(GtkCList
*tree
, GtkCListRow
*lhm
, GtkCListRow
*rhm
)
556 Object
*o1
= (Object
*)lhm
->data
;
557 Object
*o2
= (Object
*)rhm
->data
;
558 k
= strcmp(o1
->type
->name
, o2
->type
->name
);
560 if (k
< 0) return -1;
564 static GtkCListCompareFunc cmp_funcs_
[] = {
565 (GtkCListCompareFunc
)cmp_name_
,
566 (GtkCListCompareFunc
)cmp_type_
,
571 diagram_tree_sort_objects(DiagramTree
*tree
, DiagramTreeSortType type
)
573 if (tree
&& type
<= DIA_TREE_SORT_INSERT
&& tree
->last
) {
574 GtkCTreeNode
*node
= is_object_node(tree
->last
)?
575 GTK_CTREE_ROW(tree
->last
)->parent
: tree
->last
;
576 gtk_clist_set_compare_func(GTK_CLIST(tree
->tree
), cmp_funcs_
[type
]);
577 gtk_ctree_sort_node(tree
->tree
, node
);
582 diagram_tree_sort_all_objects(DiagramTree
*tree
, DiagramTreeSortType type
)
584 /* FIXME: should not depend on tree->last != NULL */
585 if (tree
&& type
<= DIA_TREE_SORT_INSERT
&& tree
->last
) {
586 GtkCTreeNode
*node
= is_object_node(tree
->last
)?
587 GTK_CTREE_ROW(tree
->last
)->parent
: tree
->last
;
588 while (GTK_CTREE_NODE_PREV(node
)) node
= GTK_CTREE_NODE_PREV(node
);
590 gtk_clist_set_compare_func(GTK_CLIST(tree
->tree
), cmp_funcs_
[type
]);
591 gtk_ctree_sort_node(tree
->tree
, node
);
592 node
= GTK_CTREE_ROW(node
)->sibling
;
598 diagram_tree_sort_diagrams(DiagramTree
*tree
, DiagramTreeSortType type
)
600 if (tree
&& type
<= DIA_TREE_SORT_INSERT
&& type
!= DIA_TREE_SORT_TYPE
) {
601 gtk_clist_set_compare_func(GTK_CLIST(tree
->tree
), cmp_funcs_
[type
]);
602 gtk_ctree_sort_node(tree
->tree
, NULL
);
607 diagram_tree_set_object_sort_type(DiagramTree
*tree
, DiagramTreeSortType type
)
609 if (tree
&& type
<= DIA_TREE_SORT_INSERT
) {
610 tree
->obj_cmp
= cmp_funcs_
[type
];
611 diagram_tree_sort_all_objects(tree
, type
);
616 diagram_tree_set_diagram_sort_type(DiagramTree
*tree
, DiagramTreeSortType type
)
618 if (tree
&& type
<= DIA_TREE_SORT_INSERT
&& type
!= DIA_TREE_SORT_TYPE
) {
619 tree
->dia_cmp
= cmp_funcs_
[type
];
620 diagram_tree_sort_diagrams(tree
, type
);
624 static DiagramTreeSortType
625 sort_type_lookup(GtkCListCompareFunc func
)
628 for (k
= 0; k
<= DIA_TREE_SORT_INSERT
; ++k
) {
629 if (cmp_funcs_
[k
] == func
) return k
;
631 g_assert_not_reached();
636 diagram_tree_diagram_sort_type(const DiagramTree
*tree
)
639 return sort_type_lookup(tree
->dia_cmp
);
641 return DIA_TREE_SORT_INSERT
;
646 diagram_tree_object_sort_type(const DiagramTree
*tree
)
649 return sort_type_lookup(tree
->obj_cmp
);
651 return DIA_TREE_SORT_INSERT
;
655 diagram_tree_widget(const DiagramTree
*tree
)
657 g_return_val_if_fail(tree
, NULL
);
658 return GTK_WIDGET(tree
->tree
);