1 /* Dia -- a diagram creation/manipulation program
2  * Copyright (C) 1998 Alexander Larsson
3  *
4  * diagram_tree.c : a tree showing open diagrams
5  * Copyright (C) 2001 Jose A Ortega Ruiz
6  *
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.
14  *
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.
19  *
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.
23  *
24  */
25 
26 #include <string.h>
27 
28 #undef GTK_DISABLE_DEPRECATED /* GtkCTree */
29 
30 #include "properties-dialog.h"
31 #include "diagram_tree_menu.h"
32 #include "diagram_tree_menu_callbacks.h"
33 #include "diagram_tree.h"
34 #include "persistence.h"
35 
36 struct _DiagramTree {
37   GtkCTree *tree;		/* the tree widget */
38   GtkCTreeNode *last;		/* last clicked node */
39   DiagramTreeMenus *menus;	/* popup menus */
40   GtkCListCompareFunc dia_cmp;	/* diagram ordering function */
41   GtkCListCompareFunc obj_cmp;	/* object ordering function */
42 };
43 
44 #define is_object_node(node) (GTK_CTREE_ROW(node)->is_leaf)
45 
46 static gint
find_hidden_type(gconstpointer type,gconstpointer object_type)47 find_hidden_type(gconstpointer type, gconstpointer object_type)
48 {
49     return !type
50       || !object_type
51       || strcmp((const gchar *)object_type, (const gchar *)type);
52 }
53 
54 #define is_hidden_type(dtree, type) \
55   g_list_find_custom(persistent_list_get_glist(HIDDEN_TYPES_NAME), \
56                      (gpointer)type, find_hidden_type)
57 
58 #define is_hidden_object(dtree, object)		\
59   is_hidden_type(dtree, ((DiaObject *)object)->type->name)
60 
61 static void
62 update_object(DiagramTree *tree, GtkCTreeNode *node, DiaObject *object);
63 
64 static void
select_node(DiagramTree * tree,GtkCTreeNode * node,gboolean raise)65 select_node(DiagramTree *tree, GtkCTreeNode *node, gboolean raise)
66 {
67   Diagram *d = NULL;
68   GtkCTreeNode *dnode = (is_object_node(node)) ?
69     GTK_CTREE_ROW(node)->parent : node;
70   DiaObject *o = (DiaObject *)gtk_ctree_node_get_row_data(tree->tree, node);
71 
72 
73   d = (Diagram *)gtk_ctree_node_get_row_data(tree->tree, dnode);
74   if (d) {
75     GSList *dlist = d->displays;
76     if (is_object_node(node)) {
77       if (o) {
78 	update_object(tree, node, o);
79 	diagram_remove_all_selected(d, FALSE);
80 	diagram_select(d, o);
81       }
82     }
83     while (dlist) {
84       DDisplay *ddisp = (DDisplay *)dlist->data;
85       if (raise) {
86 	gdk_window_raise(ddisp->shell->window);
87 	/* if object exists */
88 	if (o) {
89 	  ddisplay_scroll_to_object(ddisp, o);
90 	}
91       }
92       gtk_widget_draw(ddisp->shell, NULL);
93       dlist = g_slist_next(dlist);
94     }
95   }
96 }
97 
98 static void
update_last_node(DiagramTree * tree)99 update_last_node(DiagramTree *tree)
100 {
101   if (is_object_node(tree->last)) {
102     DiaObject *o = (DiaObject *)gtk_ctree_node_get_row_data(tree->tree, tree->last);
103     if (o) update_object(tree, tree->last, o);
104   }
105 }
106 
107 /* signal handlers */
108 static gint
button_press_callback(GtkCTree * tree,GdkEventButton * event,DiagramTree * dtree)109 button_press_callback(GtkCTree *tree, GdkEventButton *event,
110 		      DiagramTree *dtree)
111 {
112   gint row = -1;
113   gint column = -1;
114 
115   gtk_clist_get_selection_info(GTK_CLIST(tree), event->x, event->y,
116 			       &row, &column);
117   if (row != -1) dtree->last = gtk_ctree_node_nth(tree, row);
118   else dtree->last = NULL;
119 
120   if (dtree->last) update_last_node(dtree);
121 
122   /* if doubleclick */
123   if (dtree->last && event->type == GDK_2BUTTON_PRESS) {
124     /* equivalent of "Locate" */
125     select_node(dtree, dtree->last, TRUE);
126   } else if (dtree->last && event->type == GDK_BUTTON_PRESS) {
127     if (event->button == 3) {
128       DiagramTreeMenuType menu = (is_object_node(dtree->last))?
129 	DIA_MENU_OBJECT : DIA_MENU_DIAGRAM;
130       diagram_tree_menus_popup_menu(dtree->menus, menu, event->time);
131     } else if (event->button == 1) {
132       select_node(dtree, dtree->last, FALSE);
133       /* need to return FALSE to let gtk process it further */
134       return FALSE;
135     }
136   }
137   return TRUE;
138 }
139 
140 /* private functions */
141 static void
sort_objects(DiagramTree * tree,GtkCTreeNode * node)142 sort_objects(DiagramTree *tree, GtkCTreeNode *node)
143 {
144   if (tree->obj_cmp) {
145     GtkCTreeNode *dnode =
146       (is_object_node(node))? GTK_CTREE_ROW(node)->parent : node;
147     gtk_clist_set_compare_func(GTK_CLIST(tree->tree), tree->obj_cmp);
148     gtk_ctree_sort_node(tree->tree, dnode);
149   }
150 }
151 
152 static void
sort_diagrams(DiagramTree * tree)153 sort_diagrams(DiagramTree *tree)
154 {
155   if (tree->dia_cmp) {
156     gtk_clist_set_compare_func(GTK_CLIST(tree->tree), tree->dia_cmp);
157     gtk_ctree_sort_node(tree->tree, NULL);
158   }
159 }
160 
161 static GList*
get_diagram_objects(Diagram * diagram)162 get_diagram_objects(Diagram *diagram)
163 {
164   GList *result = NULL;
165   GPtrArray *layers = diagram->data->layers;
166   if (layers) {
167     int k = 0;
168     Layer *lay;
169     for (k = 0; k < layers->len; k++) {
170       lay = (Layer *)g_ptr_array_index(layers, k);
171       result = g_list_concat(result, g_list_copy(lay->objects));
172     }
173   }
174   return result;
175 }
176 
177 static void
create_object_pixmap(DiaObject * object,GtkWidget * parent,GdkPixmap ** pixmap,GdkBitmap ** mask)178 create_object_pixmap(DiaObject *object, GtkWidget *parent,
179 		     GdkPixmap **pixmap, GdkBitmap **mask)
180 {
181   GtkStyle *style;
182 
183   g_assert(object);
184   g_assert(pixmap);
185   g_assert(mask);
186 
187   style = gtk_widget_get_style(parent);
188 
189   if (object->type->pixmap != NULL) {
190     if (strncmp((char *)object->type->pixmap, "GdkP", 4) == 0) {
191       GdkPixbuf *p;
192       p = gdk_pixbuf_new_from_inline(-1, (guint8*)object->type->pixmap, TRUE, NULL);
193       gdk_pixbuf_render_pixmap_and_mask_for_colormap(p, gtk_widget_get_colormap(parent), pixmap, mask, 128);
194       g_object_unref (p);
195     } else {
196       *pixmap =
197 	gdk_pixmap_colormap_create_from_xpm_d(NULL,
198 					      gtk_widget_get_colormap(parent),
199 					      mask,
200 					      &style->bg[GTK_STATE_NORMAL],
201 					      object->type->pixmap);
202     }
203   } else if (object->type->pixmap_file != NULL) {
204     *pixmap =
205       gdk_pixmap_colormap_create_from_xpm(NULL,
206 					  gtk_widget_get_colormap(parent),
207 					  mask,
208 					  &style->bg[GTK_STATE_NORMAL],
209 					  object->type->pixmap_file);
210   } else {
211     *pixmap = NULL;
212     *mask = NULL;
213   }
214 }
215 
216 static gchar *
get_object_name(DiaObject * object)217 get_object_name(DiaObject *object)
218 {
219   enum {SIZE = 31};
220   static gchar BUFFER[SIZE];
221 
222   gchar *name = object_get_displayname (object);
223   g_snprintf(BUFFER, SIZE, " %s", name);
224   g_free(name);
225 
226   return BUFFER;
227 }
228 
229 static void
update_object(DiagramTree * tree,GtkCTreeNode * node,DiaObject * object)230 update_object(DiagramTree *tree, GtkCTreeNode *node, DiaObject *object)
231 {
232   char *text = get_object_name(object);
233   char *old = NULL;
234   gtk_ctree_node_get_text(tree->tree, node, 0, &old);
235   if (!old || (text && strcmp(text, old))) {
236     GdkPixmap *pixmap;
237     GdkBitmap *mask;
238     create_object_pixmap(object, GTK_WIDGET(tree->tree), &pixmap, &mask);
239     gtk_ctree_set_node_info(tree->tree, node, text, 3, pixmap, mask,
240 			    pixmap, mask, TRUE, TRUE);
241     sort_objects(tree, node);
242   }
243 }
244 
245 typedef struct _PixmapsAndMasks
246 {
247   GdkPixmap *pixmap;
248   GdkBitmap *mask;
249 } PixmapsAndMasks;
250 
251 static GHashTable *_pixmaps_and_masks = NULL;
252 
253 static void
create_object_node(DiagramTree * tree,GtkCTreeNode * dnode,DiaObject * obj)254 create_object_node(DiagramTree *tree, GtkCTreeNode *dnode, DiaObject *obj)
255 {
256   gboolean expanded = GTK_CTREE_ROW(dnode)->expanded;
257   char *text[] = {NULL};
258   GdkPixmap *pixmap;
259   GdkBitmap *mask;
260   GtkCTreeNode *node;
261   PixmapsAndMasks *pnm;
262 
263   text[0] = get_object_name(obj);
264 
265   if (!_pixmaps_and_masks)
266     _pixmaps_and_masks = g_hash_table_new_full(
267 			    g_str_hash, g_str_equal, NULL, g_free);
268 
269   pnm = g_hash_table_lookup (_pixmaps_and_masks, obj->type->name);
270   if (!pnm) {
271     pnm = g_new0 (PixmapsAndMasks, 1);
272     create_object_pixmap(obj, GTK_WIDGET(tree->tree), &pnm->pixmap, &pnm->mask);
273     g_hash_table_insert (_pixmaps_and_masks, obj->type->name, pnm);
274   }
275   node =  gtk_ctree_insert_node(tree->tree, dnode, NULL, text, 3,
276 				pnm->pixmap ? g_object_ref (pnm->pixmap) : NULL,
277 				pnm->mask ? g_object_ref (pnm->mask) : NULL,
278 				NULL, NULL, TRUE, FALSE);
279   gtk_ctree_node_set_row_data(tree->tree, node, (gpointer)obj);
280   if (expanded) gtk_ctree_expand(tree->tree, dnode);
281   sort_objects(tree, dnode);
282 }
283 
284 
285 static void
create_diagram_children(DiagramTree * tree,GtkCTreeNode * node,Diagram * diagram)286 create_diagram_children(DiagramTree *tree, GtkCTreeNode *node,
287 			Diagram *diagram)
288 {
289   GList *objects = get_diagram_objects(diagram);
290   GList *org = objects;
291   while (objects) {
292     if (!is_hidden_object(tree, objects->data)) {
293       create_object_node(tree, node, (DiaObject *)objects->data);
294     }
295     objects = g_list_next(objects);
296   }
297   g_list_free(org);
298 }
299 
300 static void
update_diagram_children(DiagramTree * tree,GtkCTreeNode * node,Diagram * diagram)301 update_diagram_children(DiagramTree *tree, GtkCTreeNode *node,
302 			Diagram *diagram)
303 {
304   GList *dobjects = get_diagram_objects(diagram);
305   GList *org = dobjects;
306   GtkCTreeNode *child = GTK_CTREE_ROW(node)->children;
307   if (dobjects) {
308     while (child) {
309       GtkCTreeNode *current = child;
310       gpointer obj = gtk_ctree_node_get_row_data(tree->tree, current);
311       child = GTK_CTREE_ROW(child)->sibling;
312       if (!g_list_find(dobjects, obj) || is_hidden_object(tree, obj))
313 	gtk_ctree_remove_node(tree->tree, current);
314     }
315   }
316   while (dobjects) {
317     if (!is_hidden_object(tree, dobjects->data)
318 	&& !gtk_ctree_find_by_row_data(tree->tree, node, dobjects->data))
319       create_object_node(tree, node, (DiaObject *)dobjects->data);
320     dobjects = g_list_next(dobjects);
321   }
322   g_list_free(org);
323   sort_objects(tree, node);
324 }
325 
326 /* external interface */
327 DiagramTree*
diagram_tree_new(GList * diagrams,GtkWindow * window,DiagramTreeSortType dia_sort,DiagramTreeSortType obj_sort)328 diagram_tree_new(GList *diagrams, GtkWindow *window,
329 		 DiagramTreeSortType dia_sort,
330 		 DiagramTreeSortType obj_sort)
331 {
332   GList *tmplist;
333   DiagramTree *result = g_new(DiagramTree, 1);
334   result->tree = GTK_CTREE(gtk_ctree_new(1, 0));
335   result->last = NULL;
336   result->dia_cmp = result->obj_cmp = NULL;
337 
338   g_signal_connect(GTK_OBJECT(result->tree),
339 		     "button_press_event",
340 		   G_CALLBACK(button_press_callback),
341 		     (gpointer)result);
342   while (diagrams) {
343     diagram_tree_add(result, (Diagram *)diagrams->data);
344     diagrams = g_list_next(diagrams);
345   }
346   diagram_tree_set_diagram_sort_type(result, dia_sort);
347   diagram_tree_set_object_sort_type(result, obj_sort);
348   result->menus = diagram_tree_menus_new(result, window);
349   /* Set up menu items for the list of hidden types */
350   tmplist = persistent_list_get_glist(HIDDEN_TYPES_NAME);
351   for (; tmplist != NULL; tmplist = g_list_next(tmplist)) {
352     diagram_tree_menus_add_hidden_type(result->menus, tmplist->data);
353   }
354   return result;
355 }
356 
357 void
diagram_tree_delete(DiagramTree * tree)358 diagram_tree_delete(DiagramTree *tree)
359 {
360   g_return_if_fail(tree);
361   gtk_widget_destroy(GTK_WIDGET(tree->tree));
362 }
363 
364 void
diagram_tree_add(DiagramTree * tree,Diagram * diagram)365 diagram_tree_add(DiagramTree *tree, Diagram *diagram)
366 {
367     if (tree) {
368         if (diagram
369             && !gtk_ctree_find_by_row_data(tree->tree, NULL, (gpointer)diagram))
370         {
371             char *text[] = {(char *)g_basename(diagram->filename)};
372             GtkCTreeNode *node =
373                 gtk_ctree_insert_node(tree->tree, NULL, NULL, text, 1,
374                                       NULL, NULL, NULL, NULL, FALSE, FALSE);
375             gtk_ctree_node_set_row_data(tree->tree, node, (gpointer)diagram);
376             create_diagram_children(tree, node, diagram);
377             sort_diagrams(tree);
378         }
379     }
380 }
381 
382 void
diagram_tree_remove(DiagramTree * tree,Diagram * diagram)383 diagram_tree_remove(DiagramTree *tree, Diagram *diagram)
384 {
385   if (tree) {
386     GtkCTreeNode *node;
387     if (diagram
388 	&& (node = gtk_ctree_find_by_row_data(tree->tree, NULL,
389 					      (gpointer)diagram)))
390       gtk_ctree_remove_node(tree->tree, node);
391   }
392 }
393 
394 void
diagram_tree_update(DiagramTree * tree,Diagram * diagram)395 diagram_tree_update(DiagramTree *tree, Diagram *diagram)
396 {
397   if (tree) {
398     if (diagram_is_modified(diagram)) {
399       GtkCTreeNode *dnode =
400 	gtk_ctree_find_by_row_data(tree->tree, NULL, (gpointer)diagram);
401       if (dnode) update_diagram_children(tree, dnode, diagram);
402       else diagram_tree_add(tree, diagram);
403     }
404   }
405 }
406 
407 void
diagram_tree_update_all(DiagramTree * tree)408 diagram_tree_update_all(DiagramTree *tree)
409 {
410   if (tree) {
411     GtkCTreeNode *node = gtk_ctree_node_nth(tree->tree, 0);
412     while (node) {
413       Diagram *d = (Diagram *)gtk_ctree_node_get_row_data(tree->tree, node);
414       if (d) update_diagram_children(tree, node, d);
415       node = GTK_CTREE_ROW(node)->sibling;
416     }
417   }
418 }
419 
420 void
diagram_tree_update_name(DiagramTree * tree,Diagram * diagram)421 diagram_tree_update_name(DiagramTree *tree, Diagram *diagram)
422 {
423   if (tree) {
424     GtkCTreeNode *node;
425     g_return_if_fail(diagram);
426     node = gtk_ctree_find_by_row_data(tree->tree, NULL, (gpointer)diagram);
427     if (node) {
428       gtk_ctree_node_set_text(tree->tree, node, 0,
429 			      g_basename(diagram->filename));
430       sort_diagrams(tree);
431     }
432   }
433 }
434 
435 void
diagram_tree_add_object(DiagramTree * tree,Diagram * diagram,DiaObject * object)436 diagram_tree_add_object(DiagramTree *tree, Diagram *diagram, DiaObject *object)
437 {
438   if (tree) {
439     g_return_if_fail(diagram);
440     if (object && !is_hidden_object(tree, object)) {
441       GtkCTreeNode *dnode =
442 	gtk_ctree_find_by_row_data(tree->tree, NULL, (gpointer)diagram);
443       if (!dnode) diagram_tree_add(tree, diagram);
444       else if (!gtk_ctree_find_by_row_data(tree->tree, dnode, (gpointer)object))
445 	create_object_node(tree, dnode, object);
446     }
447   }
448 }
449 
450 void
diagram_tree_add_objects(DiagramTree * tree,Diagram * diagram,GList * objects)451 diagram_tree_add_objects(DiagramTree *tree, Diagram *diagram, GList *objects)
452 {
453   if (tree) {
454     g_return_if_fail(diagram);
455     while (objects) {
456       diagram_tree_add_object(tree, diagram, (DiaObject *)objects->data);
457       objects = g_list_next(objects);
458     }
459   }
460 }
461 
462 
463 void
diagram_tree_remove_object(DiagramTree * tree,DiaObject * object)464 diagram_tree_remove_object(DiagramTree *tree, DiaObject *object)
465 {
466   if (tree) {
467     if (object) {
468       GtkCTreeNode *node =
469 	gtk_ctree_find_by_row_data(tree->tree, NULL, (gpointer)object);
470       if (node) gtk_ctree_remove_node(tree->tree, node);
471     }
472   }
473 }
474 
475 void
diagram_tree_remove_objects(DiagramTree * tree,GList * objects)476 diagram_tree_remove_objects(DiagramTree *tree, GList *objects)
477 {
478   if (tree) {
479     while (objects) {
480       diagram_tree_remove_object(tree, (DiaObject *)objects->data);
481       objects = g_list_next(objects);
482     }
483   }
484 }
485 
486 void
diagram_tree_update_object(DiagramTree * tree,Diagram * diagram,DiaObject * object)487 diagram_tree_update_object(DiagramTree *tree, Diagram *diagram,
488 			   DiaObject *object)
489 {
490   if (tree) {
491     g_return_if_fail(diagram);
492     if (object) {
493       GtkCTreeNode *node =
494 	gtk_ctree_find_by_row_data(tree->tree, NULL,(gpointer)object);
495       if (node) {
496 	update_object(tree, node, object);
497       }
498     }
499   }
500 }
501 
502 void
diagram_tree_raise(DiagramTree * tree)503 diagram_tree_raise(DiagramTree *tree)
504 {
505   if (tree && tree->last) {
506     select_node(tree, tree->last, TRUE);
507   }
508 }
509 
510 void
diagram_tree_show_properties(const DiagramTree * tree)511 diagram_tree_show_properties(const DiagramTree *tree)
512 {
513   if (tree && tree->last && is_object_node(tree->last)) {
514     GtkCTreeNode *parent = GTK_CTREE_ROW(tree->last)->parent;
515     if (parent) {
516       Diagram *dia = (Diagram *)gtk_ctree_node_get_row_data(tree->tree, parent);
517       DiaObject *obj =
518 	(DiaObject *)gtk_ctree_node_get_row_data(tree->tree, tree->last);
519       object_properties_show(dia, obj);
520     }
521   }
522 }
523 
524 const gchar *
diagram_tree_hide_type(DiagramTree * tree)525 diagram_tree_hide_type(DiagramTree *tree)
526 {
527   if (tree && tree->last && is_object_node(tree->last)) {
528     DiaObject *obj =
529       (DiaObject *)gtk_ctree_node_get_row_data(tree->tree, tree->last);
530     g_assert(!is_hidden_object(tree, obj));
531     diagram_tree_hide_explicit_type(tree, obj->type->name);
532     return obj->type->name;
533   }
534   return NULL;
535 }
536 
537 void
diagram_tree_hide_explicit_type(DiagramTree * tree,const gchar * type)538 diagram_tree_hide_explicit_type(DiagramTree *tree, const gchar *type)
539 {
540   if (tree && type) {
541     persistent_list_add(HIDDEN_TYPES_NAME, type);
542     diagram_tree_menus_add_hidden_type(tree->menus, type);
543     diagram_tree_update_all(tree);
544   }
545 }
546 
547 void
diagram_tree_unhide_type(DiagramTree * tree,const gchar * type)548 diagram_tree_unhide_type(DiagramTree *tree, const gchar *type)
549 {
550   if (tree && type) {
551     GList *t = is_hidden_type(tree, type);
552     if (t) {
553       persistent_list_remove(HIDDEN_TYPES_NAME, type);
554       diagram_tree_update_all(tree);
555     }
556   }
557 }
558 
559 /* sorting functions */
560 static gint
cmp_name_(GtkCList * tree,GtkCListRow * lhm,GtkCListRow * rhm)561 cmp_name_(GtkCList *tree, GtkCListRow *lhm, GtkCListRow *rhm)
562 {
563   int k;
564   gchar *name1 = lhm->cell->u.text;
565   gchar *name2 = rhm->cell->u.text;
566   if (name1 && !name2) k = -1;
567   else if (!name1 && name2) k = 1;
568   else if (!name1 && !name2) k = 0;
569   else k = strcmp(name1, name2);
570   if (k > 0) return 1;
571   if (k < 0) return -1;
572   return 0;
573 }
574 
575 static gint
cmp_type_(GtkCList * tree,GtkCListRow * lhm,GtkCListRow * rhm)576 cmp_type_(GtkCList *tree, GtkCListRow *lhm, GtkCListRow *rhm)
577 {
578   int k;
579   DiaObject *o1 = (DiaObject *)lhm->data;
580   DiaObject *o2 = (DiaObject *)rhm->data;
581   k = strcmp(o1->type->name, o2->type->name);
582   if (k > 0) return 1;
583   if (k < 0) return -1;
584   return 0;
585 }
586 
587 static GtkCListCompareFunc cmp_funcs_[] = {
588   (GtkCListCompareFunc)cmp_name_,
589   (GtkCListCompareFunc)cmp_type_,
590   NULL
591 };
592 
593 void
diagram_tree_sort_objects(DiagramTree * tree,DiagramTreeSortType type)594 diagram_tree_sort_objects(DiagramTree *tree, DiagramTreeSortType type)
595 {
596   if (tree && type <= DIA_TREE_SORT_INSERT && tree->last) {
597     GtkCTreeNode *node = is_object_node(tree->last)?
598       GTK_CTREE_ROW(tree->last)->parent : tree->last;
599     gtk_clist_set_compare_func(GTK_CLIST(tree->tree), cmp_funcs_[type]);
600     gtk_ctree_sort_node(tree->tree, node);
601   }
602 }
603 
604 void
diagram_tree_sort_all_objects(DiagramTree * tree,DiagramTreeSortType type)605 diagram_tree_sort_all_objects(DiagramTree *tree, DiagramTreeSortType type)
606 {
607   /* FIXME: should not depend on tree->last != NULL */
608   if (tree && type <= DIA_TREE_SORT_INSERT && tree->last) {
609     GtkCTreeNode *node = is_object_node(tree->last)?
610       GTK_CTREE_ROW(tree->last)->parent : tree->last;
611     while (GTK_CTREE_NODE_PREV(node)) node = GTK_CTREE_NODE_PREV(node);
612     while (node) {
613       gtk_clist_set_compare_func(GTK_CLIST(tree->tree), cmp_funcs_[type]);
614       gtk_ctree_sort_node(tree->tree, node);
615       node = GTK_CTREE_ROW(node)->sibling;
616     }
617   }
618 }
619 
620 void
diagram_tree_sort_diagrams(DiagramTree * tree,DiagramTreeSortType type)621 diagram_tree_sort_diagrams(DiagramTree *tree, DiagramTreeSortType type)
622 {
623   if (tree && type <= DIA_TREE_SORT_INSERT && type != DIA_TREE_SORT_TYPE) {
624     gtk_clist_set_compare_func(GTK_CLIST(tree->tree), cmp_funcs_[type]);
625     gtk_ctree_sort_node(tree->tree, NULL);
626   }
627 }
628 
629 void
diagram_tree_set_object_sort_type(DiagramTree * tree,DiagramTreeSortType type)630 diagram_tree_set_object_sort_type(DiagramTree *tree, DiagramTreeSortType type)
631 {
632   if (tree && type <= DIA_TREE_SORT_INSERT) {
633     tree->obj_cmp = cmp_funcs_[type];
634     diagram_tree_sort_all_objects(tree, type);
635   }
636 }
637 
638 void
diagram_tree_set_diagram_sort_type(DiagramTree * tree,DiagramTreeSortType type)639 diagram_tree_set_diagram_sort_type(DiagramTree *tree, DiagramTreeSortType type)
640 {
641   if (tree && type <= DIA_TREE_SORT_INSERT && type != DIA_TREE_SORT_TYPE) {
642     tree->dia_cmp = cmp_funcs_[type];
643     diagram_tree_sort_diagrams(tree, type);
644   }
645 }
646 
647 static DiagramTreeSortType
sort_type_lookup(GtkCListCompareFunc func)648 sort_type_lookup(GtkCListCompareFunc func)
649 {
650   int k;
651   for (k = 0; k <= DIA_TREE_SORT_INSERT; ++k) {
652     if (cmp_funcs_[k] == func) return k;
653   }
654   g_assert_not_reached();
655   return 0;
656 }
657 
658 DiagramTreeSortType
diagram_tree_diagram_sort_type(const DiagramTree * tree)659 diagram_tree_diagram_sort_type(const DiagramTree *tree)
660 {
661   if (tree) {
662     return sort_type_lookup(tree->dia_cmp);
663   } else
664     return DIA_TREE_SORT_INSERT;
665 }
666 
667 
668 DiagramTreeSortType
diagram_tree_object_sort_type(const DiagramTree * tree)669 diagram_tree_object_sort_type(const DiagramTree *tree)
670 {
671   if (tree) {
672     return sort_type_lookup(tree->obj_cmp);
673   } else
674     return DIA_TREE_SORT_INSERT;
675 }
676 
677 GtkWidget*
diagram_tree_widget(const DiagramTree * tree)678 diagram_tree_widget(const DiagramTree *tree)
679 {
680   g_return_val_if_fail(tree, NULL);
681   return GTK_WIDGET(tree->tree);
682 }
683