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 "diagram_tree_menu.h"
30 #include "diagram_tree_menu_callbacks.h"
31 #include "diagram_tree.h"
32 #include "persistence.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 */
42 #define is_object_node(node) (GTK_CTREE_ROW(node)->is_leaf)
45 find_hidden_type(gconstpointer type
, gconstpointer object_type
)
49 || strcmp((const gchar
*)object_type
, (const gchar
*)type
);
52 #define is_hidden_type(dtree, type) \
53 g_list_find_custom(persistent_list_get_glist(HIDDEN_TYPES_NAME), \
54 (gpointer)type, find_hidden_type)
56 #define is_hidden_object(dtree, object) \
57 is_hidden_type(dtree, ((DiaObject *)object)->type->name)
60 update_object(DiagramTree
*tree
, GtkCTreeNode
*node
, DiaObject
*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 DiaObject
*o
= (DiaObject
*)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_remove_all_selected(d
, FALSE
);
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 DiaObject
*o
= (DiaObject
*)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(DiaObject
*object
, GtkWidget
*parent
,
177 GdkPixmap
**pixmap
, GdkBitmap
**mask
)
185 style
= gtk_widget_get_style(parent
);
187 if (object
->type
->pixmap
!= NULL
) {
188 if (strncmp((char *)object
->type
->pixmap
, "GdkP", 4) == 0) {
190 p
= gdk_pixbuf_new_from_inline(-1, (guint8
*)object
->type
->pixmap
, TRUE
, NULL
);
191 gdk_pixbuf_render_pixmap_and_mask_for_colormap(p
, gtk_widget_get_colormap(parent
), pixmap
, mask
, 128);
194 gdk_pixmap_colormap_create_from_xpm_d(NULL
,
195 gtk_widget_get_colormap(parent
),
197 &style
->bg
[GTK_STATE_NORMAL
],
198 object
->type
->pixmap
);
200 } else if (object
->type
->pixmap_file
!= NULL
) {
202 gdk_pixmap_colormap_create_from_xpm(NULL
,
203 gtk_widget_get_colormap(parent
),
205 &style
->bg
[GTK_STATE_NORMAL
],
206 object
->type
->pixmap_file
);
214 get_object_name(DiaObject
*object
)
217 static gchar BUFFER
[SIZE
];
219 gchar
*name
= object_get_displayname (object
);
220 g_snprintf(BUFFER
, SIZE
, " %s", name
);
227 update_object(DiagramTree
*tree
, GtkCTreeNode
*node
, DiaObject
*object
)
229 char *text
= get_object_name(object
);
231 gtk_ctree_node_get_text(tree
->tree
, node
, 0, &old
);
232 if (!old
|| (text
&& strcmp(text
, old
))) {
235 create_object_pixmap(object
, GTK_WIDGET(tree
->tree
), &pixmap
, &mask
);
236 gtk_ctree_set_node_info(tree
->tree
, node
, text
, 3, pixmap
, mask
,
237 pixmap
, mask
, TRUE
, TRUE
);
238 sort_objects(tree
, node
);
243 create_object_node(DiagramTree
*tree
, GtkCTreeNode
*dnode
, DiaObject
*obj
)
245 gboolean expanded
= GTK_CTREE_ROW(dnode
)->expanded
;
246 char *text
[] = {NULL
};
250 text
[0] = get_object_name(obj
);
251 create_object_pixmap(obj
, GTK_WIDGET(tree
->tree
), &pixmap
, &mask
);
252 node
= gtk_ctree_insert_node(tree
->tree
, dnode
, NULL
, text
, 3,
253 pixmap
, mask
, NULL
, NULL
, TRUE
, FALSE
);
254 gtk_ctree_node_set_row_data(tree
->tree
, node
, (gpointer
)obj
);
255 if (expanded
) gtk_ctree_expand(tree
->tree
, dnode
);
256 sort_objects(tree
, dnode
);
261 create_diagram_children(DiagramTree
*tree
, GtkCTreeNode
*node
,
264 GList
*objects
= get_diagram_objects(diagram
);
265 GList
*org
= objects
;
267 if (!is_hidden_object(tree
, objects
->data
)) {
268 create_object_node(tree
, node
, (DiaObject
*)objects
->data
);
270 objects
= g_list_next(objects
);
276 update_diagram_children(DiagramTree
*tree
, GtkCTreeNode
*node
,
279 GList
*dobjects
= get_diagram_objects(diagram
);
280 GList
*org
= dobjects
;
281 GtkCTreeNode
*child
= GTK_CTREE_ROW(node
)->children
;
284 GtkCTreeNode
*current
= child
;
285 gpointer obj
= gtk_ctree_node_get_row_data(tree
->tree
, current
);
286 child
= GTK_CTREE_ROW(child
)->sibling
;
287 if (!g_list_find(dobjects
, obj
) || is_hidden_object(tree
, obj
))
288 gtk_ctree_remove_node(tree
->tree
, current
);
292 if (!is_hidden_object(tree
, dobjects
->data
)
293 && !gtk_ctree_find_by_row_data(tree
->tree
, node
, dobjects
->data
))
294 create_object_node(tree
, node
, (DiaObject
*)dobjects
->data
);
295 dobjects
= g_list_next(dobjects
);
298 sort_objects(tree
, node
);
301 /* external interface */
303 diagram_tree_new(GList
*diagrams
, GtkWindow
*window
,
304 DiagramTreeSortType dia_sort
,
305 DiagramTreeSortType obj_sort
)
308 DiagramTree
*result
= g_new(DiagramTree
, 1);
309 result
->tree
= GTK_CTREE(gtk_ctree_new(1, 0));
311 result
->dia_cmp
= result
->obj_cmp
= NULL
;
313 g_signal_connect(GTK_OBJECT(result
->tree
),
314 "button_press_event",
315 G_CALLBACK(button_press_callback
),
318 diagram_tree_add(result
, (Diagram
*)diagrams
->data
);
319 diagrams
= g_list_next(diagrams
);
321 diagram_tree_set_diagram_sort_type(result
, dia_sort
);
322 diagram_tree_set_object_sort_type(result
, obj_sort
);
323 result
->menus
= diagram_tree_menus_new(result
, window
);
324 /* Set up menu items for the list of hidden types */
325 tmplist
= persistent_list_get_glist(HIDDEN_TYPES_NAME
);
326 for (; tmplist
!= NULL
; tmplist
= g_list_next(tmplist
)) {
327 diagram_tree_menus_add_hidden_type(result
->menus
, tmplist
->data
);
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_is_modified(diagram
)) {
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
, DiaObject
*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
, (DiaObject
*)objects
->data
);
432 objects
= g_list_next(objects
);
439 diagram_tree_remove_object(DiagramTree
*tree
, DiaObject
*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
, (DiaObject
*)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 (DiaObject
*)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 (DiaObject
*)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 persistent_list_add(HIDDEN_TYPES_NAME
, type
);
517 diagram_tree_menus_add_hidden_type(tree
->menus
, type
);
518 diagram_tree_update_all(tree
);
523 diagram_tree_unhide_type(DiagramTree
*tree
, const gchar
*type
)
526 GList
*t
= is_hidden_type(tree
, type
);
528 persistent_list_remove(HIDDEN_TYPES_NAME
, type
);
529 diagram_tree_update_all(tree
);
534 /* sorting functions */
536 cmp_name_(GtkCList
*tree
, GtkCListRow
*lhm
, GtkCListRow
*rhm
)
539 gchar
*name1
= lhm
->cell
->u
.text
;
540 gchar
*name2
= rhm
->cell
->u
.text
;
541 if (name1
&& !name2
) k
= -1;
542 else if (!name1
&& name2
) k
= 1;
543 else if (!name1
&& !name2
) k
= 0;
544 else k
= strcmp(name1
, name2
);
546 if (k
< 0) return -1;
551 cmp_type_(GtkCList
*tree
, GtkCListRow
*lhm
, GtkCListRow
*rhm
)
554 DiaObject
*o1
= (DiaObject
*)lhm
->data
;
555 DiaObject
*o2
= (DiaObject
*)rhm
->data
;
556 k
= strcmp(o1
->type
->name
, o2
->type
->name
);
558 if (k
< 0) return -1;
562 static GtkCListCompareFunc cmp_funcs_
[] = {
563 (GtkCListCompareFunc
)cmp_name_
,
564 (GtkCListCompareFunc
)cmp_type_
,
569 diagram_tree_sort_objects(DiagramTree
*tree
, DiagramTreeSortType type
)
571 if (tree
&& type
<= DIA_TREE_SORT_INSERT
&& tree
->last
) {
572 GtkCTreeNode
*node
= is_object_node(tree
->last
)?
573 GTK_CTREE_ROW(tree
->last
)->parent
: tree
->last
;
574 gtk_clist_set_compare_func(GTK_CLIST(tree
->tree
), cmp_funcs_
[type
]);
575 gtk_ctree_sort_node(tree
->tree
, node
);
580 diagram_tree_sort_all_objects(DiagramTree
*tree
, DiagramTreeSortType type
)
582 /* FIXME: should not depend on tree->last != NULL */
583 if (tree
&& type
<= DIA_TREE_SORT_INSERT
&& tree
->last
) {
584 GtkCTreeNode
*node
= is_object_node(tree
->last
)?
585 GTK_CTREE_ROW(tree
->last
)->parent
: tree
->last
;
586 while (GTK_CTREE_NODE_PREV(node
)) node
= GTK_CTREE_NODE_PREV(node
);
588 gtk_clist_set_compare_func(GTK_CLIST(tree
->tree
), cmp_funcs_
[type
]);
589 gtk_ctree_sort_node(tree
->tree
, node
);
590 node
= GTK_CTREE_ROW(node
)->sibling
;
596 diagram_tree_sort_diagrams(DiagramTree
*tree
, DiagramTreeSortType type
)
598 if (tree
&& type
<= DIA_TREE_SORT_INSERT
&& type
!= DIA_TREE_SORT_TYPE
) {
599 gtk_clist_set_compare_func(GTK_CLIST(tree
->tree
), cmp_funcs_
[type
]);
600 gtk_ctree_sort_node(tree
->tree
, NULL
);
605 diagram_tree_set_object_sort_type(DiagramTree
*tree
, DiagramTreeSortType type
)
607 if (tree
&& type
<= DIA_TREE_SORT_INSERT
) {
608 tree
->obj_cmp
= cmp_funcs_
[type
];
609 diagram_tree_sort_all_objects(tree
, type
);
614 diagram_tree_set_diagram_sort_type(DiagramTree
*tree
, DiagramTreeSortType type
)
616 if (tree
&& type
<= DIA_TREE_SORT_INSERT
&& type
!= DIA_TREE_SORT_TYPE
) {
617 tree
->dia_cmp
= cmp_funcs_
[type
];
618 diagram_tree_sort_diagrams(tree
, type
);
622 static DiagramTreeSortType
623 sort_type_lookup(GtkCListCompareFunc func
)
626 for (k
= 0; k
<= DIA_TREE_SORT_INSERT
; ++k
) {
627 if (cmp_funcs_
[k
] == func
) return k
;
629 g_assert_not_reached();
634 diagram_tree_diagram_sort_type(const DiagramTree
*tree
)
637 return sort_type_lookup(tree
->dia_cmp
);
639 return DIA_TREE_SORT_INSERT
;
644 diagram_tree_object_sort_type(const DiagramTree
*tree
)
647 return sort_type_lookup(tree
->obj_cmp
);
649 return DIA_TREE_SORT_INSERT
;
653 diagram_tree_widget(const DiagramTree
*tree
)
655 g_return_val_if_fail(tree
, NULL
);
656 return GTK_WIDGET(tree
->tree
);