2001-11-20 Hans Breuer <hans@breuer.org>
[dia.git] / app / diagram_tree.c
blob4e667bf8c4e7aec2245d06123cdc4c5ea7f83e7a
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
6 *
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"
29 struct _DiagramTree {
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 */
39 static void
40 update_object(DiagramTree *tree, GtkCTreeNode *node, Object *object);
42 static void
43 raise_display(Diagram *d)
45 if (d) {
46 GSList *dlist = d->displays;
47 while (dlist) {
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);
56 static void
57 select_and_raise(DiagramTree *tree, GtkCTreeNode *node)
59 Diagram *d = NULL;
60 GtkCTreeNode *dnode =
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);
63 if (d) {
64 if (GTK_CTREE_ROW(node)->is_leaf) {
65 Object *o = (Object *)gtk_ctree_node_get_row_data(tree->tree, node);
66 if (o) {
67 update_object(tree, node, o);
68 diagram_unselect_objects(d, d->data->selected);
69 diagram_select(d, o);
72 gtk_ctree_select(tree->tree, node);
73 raise_display(d);
78 /* signal handlers */
79 static void
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);
89 static gint
90 button_press_callback(GtkCTree *tree, GdkEventButton *event,
91 DiagramTree *dtree)
93 gint row = -1;
94 gint column = -1;
96 gtk_clist_get_selection_info(GTK_CLIST(tree), event->x, event->y,
97 &row, &column);
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);
109 return TRUE;
112 /* private functions */
113 static void
114 sort_objects(DiagramTree *tree, GtkCTreeNode *node)
116 if (tree->obj_cmp) {
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);
124 static void
125 sort_diagrams(DiagramTree *tree)
127 if (tree->dia_cmp) {
128 gtk_clist_set_compare_func(GTK_CLIST(tree->tree), tree->dia_cmp);
129 gtk_ctree_sort_node(tree->tree, NULL);
133 static GList*
134 get_diagram_objects(Diagram *diagram)
136 GList *result = NULL;
137 GPtrArray *layers = diagram->data->layers;
138 if (layers) {
139 int k = 0;
140 Layer *lay;
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));
146 return result;
149 static void
150 create_object_pixmap(Object *object, GtkWidget *parent,
151 GdkPixmap **pixmap, GdkBitmap **mask)
153 GtkStyle *style;
155 g_assert(object);
156 g_assert(pixmap);
157 g_assert(mask);
159 style = gtk_widget_get_style(parent);
161 if (object->type->pixmap != NULL) {
162 *pixmap =
163 gdk_pixmap_colormap_create_from_xpm_d(NULL,
164 gtk_widget_get_colormap(parent),
165 mask,
166 &style->bg[GTK_STATE_NORMAL],
167 object->type->pixmap);
168 } else if (object->type->pixmap_file != NULL) {
169 *pixmap =
170 gdk_pixmap_colormap_create_from_xpm(NULL,
171 gtk_widget_get_colormap(parent),
172 mask,
173 &style->bg[GTK_STATE_NORMAL],
174 object->type->pixmap_file);
175 } else {
176 *pixmap = NULL;
177 *mask = NULL;
181 static gchar *
182 get_object_name(Object *object)
184 enum {SIZE = 31};
185 static gchar BUFFER[SIZE];
187 Property *prop = NULL;
188 gchar *result = NULL;
189 g_assert(object);
190 prop = object_prop_by_name(object, "name");
191 if (prop) result = ((StringProperty *)prop)->string_data;
192 else {
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", ' ');
200 return BUFFER;
203 static void
204 update_object(DiagramTree *tree, GtkCTreeNode *node, Object *object)
206 char *text = get_object_name(object);
207 char *old = NULL;
208 gtk_ctree_node_get_text(tree->tree, node, 0, &old);
209 if (!old || (text && strcmp(text, old))) {
210 GdkPixmap *pixmap;
211 GdkBitmap *mask;
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);
219 static void
220 create_object_node(DiagramTree *tree, GtkCTreeNode *dnode, Object *obj)
222 gboolean expanded = GTK_CTREE_ROW(dnode)->expanded;
223 char *text[] = {NULL};
224 GdkPixmap *pixmap;
225 GdkBitmap *mask;
226 GtkCTreeNode *node;
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);
236 static void
237 create_diagram_children(DiagramTree *tree, GtkCTreeNode *node,
238 Diagram *diagram)
240 GList *objects = get_diagram_objects(diagram);
241 while (objects) {
242 create_object_node(tree, node, (Object *)objects->data);
243 objects = g_list_next(objects);
245 g_list_free(objects);
248 static void
249 update_diagram_children(DiagramTree *tree, GtkCTreeNode *node,
250 Diagram *diagram)
252 GList *dobjects = get_diagram_objects(diagram);
253 GList *org = dobjects;
254 GtkCTreeNode *child = GTK_CTREE_ROW(node)->children;
255 if (dobjects) {
256 while (child) {
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,
260 current)))
261 gtk_ctree_remove_node(tree->tree, current);
264 while (dobjects) {
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);
269 g_list_free(org);
270 sort_objects(tree, node);
273 /* external interface */
274 DiagramTree*
275 diagram_tree_new(GList *diagrams)
277 DiagramTree *result = g_new(DiagramTree, 1);
278 result->tree = GTK_CTREE(gtk_ctree_new(1, 0));
279 result->last = NULL;
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),
283 (gpointer)result);
284 gtk_signal_connect(GTK_OBJECT(result->tree),
285 "button_press_event",
286 GTK_SIGNAL_FUNC(button_press_callback),
287 (gpointer)result);
288 result->dia_menu = result->object_menu = NULL;
289 while (diagrams) {
290 diagram_tree_add(result, (Diagram *)diagrams->data);
291 diagrams = g_list_next(diagrams);
293 return result;
296 void
297 diagram_tree_delete(DiagramTree *tree)
299 g_return_if_fail(tree);
300 gtk_widget_destroy(GTK_WIDGET(tree->tree));
303 void
304 diagram_tree_add(DiagramTree *tree, Diagram *diagram)
306 if (tree) {
307 if (diagram
308 && !gtk_ctree_find_by_row_data(tree->tree, NULL, (gpointer)diagram))
310 char *text[] = {g_basename(diagram->filename)};
311 GtkCTreeNode *node =
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);
316 sort_diagrams(tree);
321 void
322 diagram_tree_remove(DiagramTree *tree, Diagram *diagram)
324 if (tree) {
325 GtkCTreeNode *node;
326 if (diagram
327 && (node = gtk_ctree_find_by_row_data(tree->tree, NULL,
328 (gpointer)diagram)))
329 gtk_ctree_remove_node(tree->tree, node);
333 void
334 diagram_tree_update(DiagramTree *tree, Diagram *diagram)
336 if (tree) {
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);
346 void
347 diagram_tree_update_name(DiagramTree *tree, Diagram *diagram)
349 if (tree) {
350 GtkCTreeNode *node;
351 g_return_if_fail(diagram);
352 node = gtk_ctree_find_by_row_data(tree->tree, NULL, (gpointer)diagram);
353 if (node) {
354 gtk_ctree_node_set_text(tree->tree, node, 0,
355 g_basename(diagram->filename));
356 sort_diagrams(tree);
361 void
362 diagram_tree_add_object(DiagramTree *tree, Diagram *diagram, Object *object)
364 if (tree) {
365 g_return_if_fail(diagram);
366 if (object) {
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);
376 void
377 diagram_tree_add_objects(DiagramTree *tree, Diagram *diagram, GList *objects)
379 if (tree) {
380 g_return_if_fail(diagram);
381 while (objects) {
382 diagram_tree_add_object(tree, diagram, (Object *)objects->data);
383 objects = g_list_next(objects);
389 void
390 diagram_tree_remove_object(DiagramTree *tree, Object *object)
392 if (tree) {
393 if (object) {
394 GtkCTreeNode *node =
395 gtk_ctree_find_by_row_data(tree->tree, NULL, (gpointer)object);
396 if (node) gtk_ctree_remove_node(tree->tree, node);
401 void
402 diagram_tree_remove_objects(DiagramTree *tree, GList *objects)
404 if (tree) {
405 while (objects) {
406 diagram_tree_remove_object(tree, (Object *)objects->data);
407 objects = g_list_next(objects);
412 void
413 diagram_tree_update_object(DiagramTree *tree, Diagram *diagram,
414 Object *object)
416 if (tree) {
417 g_return_if_fail(diagram);
418 if (object) {
419 GtkCTreeNode *node =
420 gtk_ctree_find_by_row_data(tree->tree, NULL,(gpointer)object);
421 if (node) {
422 update_object(tree, node, object);
428 void
429 diagram_tree_raise(DiagramTree *tree)
431 if (tree && tree->last) {
432 select_and_raise(tree, tree->last);
436 void
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;
441 if (parent) {
442 Diagram *dia = (Diagram *)gtk_ctree_node_get_row_data(tree->tree, parent);
443 Object *obj =
444 (Object *)gtk_ctree_node_get_row_data(tree->tree, tree->last);
445 properties_show(dia, obj);
450 /* sorting functions */
451 static gint
452 cmp_name_(GtkCList *tree, GtkCListRow *lhm, GtkCListRow *rhm)
454 int k;
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);
461 if (k > 0) return 1;
462 if (k < 0) return -1;
463 return 0;
466 static gint
467 cmp_type_(GtkCList *tree, GtkCListRow *lhm, GtkCListRow *rhm)
469 int k;
470 Object *o1 = (Object *)lhm->data;
471 Object *o2 = (Object *)rhm->data;
472 k = strcmp(o1->type->name, o2->type->name);
473 if (k > 0) return 1;
474 if (k < 0) return -1;
475 return 0;
478 static GtkCListCompareFunc cmp_funcs_[] = {
479 (GtkCListCompareFunc)cmp_name_,
480 (GtkCListCompareFunc)cmp_type_,
481 NULL
484 void
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);
495 void
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);
503 while (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;
511 void
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);
520 void
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);
529 void
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)
541 int k;
542 for (k = 0; k <= DIA_TREE_SORT_INSERT; ++k) {
543 if (cmp_funcs_[k] == func) return k;
545 g_assert_not_reached();
546 return 0;
549 DiagramTreeSortType
550 diagram_tree_diagram_sort_type(const DiagramTree *tree)
552 if (tree) {
553 return sort_type_lookup(tree->dia_cmp);
554 } else
555 return DIA_TREE_SORT_INSERT;
559 DiagramTreeSortType
560 diagram_tree_object_sort_type(const DiagramTree *tree)
562 if (tree) {
563 return sort_type_lookup(tree->obj_cmp);
564 } else
565 return DIA_TREE_SORT_INSERT;
568 void
569 diagram_tree_attach_dia_menu(DiagramTree *tree, GtkWidget *menu)
571 if (tree) {
572 tree->dia_menu = GTK_MENU(menu);
576 void
577 diagram_tree_attach_obj_menu(DiagramTree *tree, GtkWidget *menu)
579 if (tree) {
580 tree->object_menu = GTK_MENU(menu);
584 GtkWidget*
585 diagram_tree_widget(const DiagramTree *tree)
587 g_return_val_if_fail(tree, NULL);
588 return GTK_WIDGET(tree->tree);