pre3 updates
[dia.git] / app / diagram_tree.c
blob2c89b3bc306b60fa29ed6b940fc83fcf2ce66599
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
9 *
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.
26 #include <string.h>
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"
34 struct _DiagramTree {
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)
44 static gint
45 find_hidden_type(gconstpointer type, gconstpointer object_type)
47 return !type
48 || !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)
59 static void
60 update_object(DiagramTree *tree, GtkCTreeNode *node, DiaObject *object);
62 static void
63 select_node(DiagramTree *tree, GtkCTreeNode *node, gboolean raise)
65 Diagram *d = NULL;
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);
72 if (d) {
73 GSList *dlist = d->displays;
74 if (is_object_node(node)) {
75 if (o) {
76 update_object(tree, node, o);
77 diagram_remove_all_selected(d, FALSE);
78 diagram_select(d, o);
81 while (dlist) {
82 DDisplay *ddisp = (DDisplay *)dlist->data;
83 if (raise) {
84 gdk_window_raise(ddisp->shell->window);
85 /* if object exists */
86 if (o) {
87 ddisplay_scroll_to_object(ddisp, o);
90 gtk_widget_draw(ddisp->shell, NULL);
91 dlist = g_slist_next(dlist);
96 static void
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 */
106 static gint
107 button_press_callback(GtkCTree *tree, GdkEventButton *event,
108 DiagramTree *dtree)
110 gint row = -1;
111 gint column = -1;
113 gtk_clist_get_selection_info(GTK_CLIST(tree), event->x, event->y,
114 &row, &column);
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);
120 /* if doubleclick */
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 */
132 return FALSE;
135 return TRUE;
138 /* private functions */
139 static void
140 sort_objects(DiagramTree *tree, GtkCTreeNode *node)
142 if (tree->obj_cmp) {
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);
150 static void
151 sort_diagrams(DiagramTree *tree)
153 if (tree->dia_cmp) {
154 gtk_clist_set_compare_func(GTK_CLIST(tree->tree), tree->dia_cmp);
155 gtk_ctree_sort_node(tree->tree, NULL);
159 static GList*
160 get_diagram_objects(Diagram *diagram)
162 GList *result = NULL;
163 GPtrArray *layers = diagram->data->layers;
164 if (layers) {
165 int k = 0;
166 Layer *lay;
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));
172 return result;
175 static void
176 create_object_pixmap(DiaObject *object, GtkWidget *parent,
177 GdkPixmap **pixmap, GdkBitmap **mask)
179 GtkStyle *style;
181 g_assert(object);
182 g_assert(pixmap);
183 g_assert(mask);
185 style = gtk_widget_get_style(parent);
187 if (object->type->pixmap != NULL) {
188 if (strncmp((char *)object->type->pixmap, "GdkP", 4) == 0) {
189 GdkPixbuf *p;
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);
192 } else {
193 *pixmap =
194 gdk_pixmap_colormap_create_from_xpm_d(NULL,
195 gtk_widget_get_colormap(parent),
196 mask,
197 &style->bg[GTK_STATE_NORMAL],
198 object->type->pixmap);
200 } else if (object->type->pixmap_file != NULL) {
201 *pixmap =
202 gdk_pixmap_colormap_create_from_xpm(NULL,
203 gtk_widget_get_colormap(parent),
204 mask,
205 &style->bg[GTK_STATE_NORMAL],
206 object->type->pixmap_file);
207 } else {
208 *pixmap = NULL;
209 *mask = NULL;
213 static gchar *
214 get_object_name(DiaObject *object)
216 enum {SIZE = 31};
217 static gchar BUFFER[SIZE];
219 gchar *name = object_get_displayname (object);
220 g_snprintf(BUFFER, SIZE, " %s", name);
221 g_free(name);
223 return BUFFER;
226 static void
227 update_object(DiagramTree *tree, GtkCTreeNode *node, DiaObject *object)
229 char *text = get_object_name(object);
230 char *old = NULL;
231 gtk_ctree_node_get_text(tree->tree, node, 0, &old);
232 if (!old || (text && strcmp(text, old))) {
233 GdkPixmap *pixmap;
234 GdkBitmap *mask;
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);
242 static void
243 create_object_node(DiagramTree *tree, GtkCTreeNode *dnode, DiaObject *obj)
245 gboolean expanded = GTK_CTREE_ROW(dnode)->expanded;
246 char *text[] = {NULL};
247 GdkPixmap *pixmap;
248 GdkBitmap *mask;
249 GtkCTreeNode *node;
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);
260 static void
261 create_diagram_children(DiagramTree *tree, GtkCTreeNode *node,
262 Diagram *diagram)
264 GList *objects = get_diagram_objects(diagram);
265 GList *org = objects;
266 while (objects) {
267 if (!is_hidden_object(tree, objects->data)) {
268 create_object_node(tree, node, (DiaObject *)objects->data);
270 objects = g_list_next(objects);
272 g_list_free(org);
275 static void
276 update_diagram_children(DiagramTree *tree, GtkCTreeNode *node,
277 Diagram *diagram)
279 GList *dobjects = get_diagram_objects(diagram);
280 GList *org = dobjects;
281 GtkCTreeNode *child = GTK_CTREE_ROW(node)->children;
282 if (dobjects) {
283 while (child) {
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);
291 while (dobjects) {
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);
297 g_list_free(org);
298 sort_objects(tree, node);
301 /* external interface */
302 DiagramTree*
303 diagram_tree_new(GList *diagrams, GtkWindow *window,
304 DiagramTreeSortType dia_sort,
305 DiagramTreeSortType obj_sort)
307 GList *tmplist;
308 DiagramTree *result = g_new(DiagramTree, 1);
309 result->tree = GTK_CTREE(gtk_ctree_new(1, 0));
310 result->last = NULL;
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),
316 (gpointer)result);
317 while (diagrams) {
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);
329 return result;
332 void
333 diagram_tree_delete(DiagramTree *tree)
335 g_return_if_fail(tree);
336 gtk_widget_destroy(GTK_WIDGET(tree->tree));
339 void
340 diagram_tree_add(DiagramTree *tree, Diagram *diagram)
342 if (tree) {
343 if (diagram
344 && !gtk_ctree_find_by_row_data(tree->tree, NULL, (gpointer)diagram))
346 char *text[] = {(char *)g_basename(diagram->filename)};
347 GtkCTreeNode *node =
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);
352 sort_diagrams(tree);
357 void
358 diagram_tree_remove(DiagramTree *tree, Diagram *diagram)
360 if (tree) {
361 GtkCTreeNode *node;
362 if (diagram
363 && (node = gtk_ctree_find_by_row_data(tree->tree, NULL,
364 (gpointer)diagram)))
365 gtk_ctree_remove_node(tree->tree, node);
369 void
370 diagram_tree_update(DiagramTree *tree, Diagram *diagram)
372 if (tree) {
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);
382 void
383 diagram_tree_update_all(DiagramTree *tree)
385 if (tree) {
386 GtkCTreeNode *node = gtk_ctree_node_nth(tree->tree, 0);
387 while (node) {
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;
395 void
396 diagram_tree_update_name(DiagramTree *tree, Diagram *diagram)
398 if (tree) {
399 GtkCTreeNode *node;
400 g_return_if_fail(diagram);
401 node = gtk_ctree_find_by_row_data(tree->tree, NULL, (gpointer)diagram);
402 if (node) {
403 gtk_ctree_node_set_text(tree->tree, node, 0,
404 g_basename(diagram->filename));
405 sort_diagrams(tree);
410 void
411 diagram_tree_add_object(DiagramTree *tree, Diagram *diagram, DiaObject *object)
413 if (tree) {
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);
425 void
426 diagram_tree_add_objects(DiagramTree *tree, Diagram *diagram, GList *objects)
428 if (tree) {
429 g_return_if_fail(diagram);
430 while (objects) {
431 diagram_tree_add_object(tree, diagram, (DiaObject *)objects->data);
432 objects = g_list_next(objects);
438 void
439 diagram_tree_remove_object(DiagramTree *tree, DiaObject *object)
441 if (tree) {
442 if (object) {
443 GtkCTreeNode *node =
444 gtk_ctree_find_by_row_data(tree->tree, NULL, (gpointer)object);
445 if (node) gtk_ctree_remove_node(tree->tree, node);
450 void
451 diagram_tree_remove_objects(DiagramTree *tree, GList *objects)
453 if (tree) {
454 while (objects) {
455 diagram_tree_remove_object(tree, (DiaObject *)objects->data);
456 objects = g_list_next(objects);
461 void
462 diagram_tree_update_object(DiagramTree *tree, Diagram *diagram,
463 DiaObject *object)
465 if (tree) {
466 g_return_if_fail(diagram);
467 if (object) {
468 GtkCTreeNode *node =
469 gtk_ctree_find_by_row_data(tree->tree, NULL,(gpointer)object);
470 if (node) {
471 update_object(tree, node, object);
477 void
478 diagram_tree_raise(DiagramTree *tree)
480 if (tree && tree->last) {
481 select_node(tree, tree->last, TRUE);
485 void
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;
490 if (parent) {
491 Diagram *dia = (Diagram *)gtk_ctree_node_get_row_data(tree->tree, parent);
492 DiaObject *obj =
493 (DiaObject *)gtk_ctree_node_get_row_data(tree->tree, tree->last);
494 properties_show(dia, obj);
499 const gchar *
500 diagram_tree_hide_type(DiagramTree *tree)
502 if (tree && tree->last && is_object_node(tree->last)) {
503 DiaObject *obj =
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;
509 return NULL;
512 void
513 diagram_tree_hide_explicit_type(DiagramTree *tree, const gchar *type)
515 if (tree && 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);
522 void
523 diagram_tree_unhide_type(DiagramTree *tree, const gchar *type)
525 if (tree && type) {
526 GList *t = is_hidden_type(tree, type);
527 if (t) {
528 persistent_list_remove(HIDDEN_TYPES_NAME, type);
529 diagram_tree_update_all(tree);
534 /* sorting functions */
535 static gint
536 cmp_name_(GtkCList *tree, GtkCListRow *lhm, GtkCListRow *rhm)
538 int k;
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);
545 if (k > 0) return 1;
546 if (k < 0) return -1;
547 return 0;
550 static gint
551 cmp_type_(GtkCList *tree, GtkCListRow *lhm, GtkCListRow *rhm)
553 int k;
554 DiaObject *o1 = (DiaObject *)lhm->data;
555 DiaObject *o2 = (DiaObject *)rhm->data;
556 k = strcmp(o1->type->name, o2->type->name);
557 if (k > 0) return 1;
558 if (k < 0) return -1;
559 return 0;
562 static GtkCListCompareFunc cmp_funcs_[] = {
563 (GtkCListCompareFunc)cmp_name_,
564 (GtkCListCompareFunc)cmp_type_,
565 NULL
568 void
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);
579 void
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);
587 while (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;
595 void
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);
604 void
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);
613 void
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)
625 int k;
626 for (k = 0; k <= DIA_TREE_SORT_INSERT; ++k) {
627 if (cmp_funcs_[k] == func) return k;
629 g_assert_not_reached();
630 return 0;
633 DiagramTreeSortType
634 diagram_tree_diagram_sort_type(const DiagramTree *tree)
636 if (tree) {
637 return sort_type_lookup(tree->dia_cmp);
638 } else
639 return DIA_TREE_SORT_INSERT;
643 DiagramTreeSortType
644 diagram_tree_object_sort_type(const DiagramTree *tree)
646 if (tree) {
647 return sort_type_lookup(tree->obj_cmp);
648 } else
649 return DIA_TREE_SORT_INSERT;
652 GtkWidget*
653 diagram_tree_widget(const DiagramTree *tree)
655 g_return_val_if_fail(tree, NULL);
656 return GTK_WIDGET(tree->tree);