1 /* Dia -- an diagram creation/manipulation program
2  * Copyright (C) 1998 Alexander Larsson
3  *              Hacked (c) 2007 Thomas Harding for tree object
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18 
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22 
23 #include <assert.h>
24 #include <math.h>
25 
26 #include "intl.h"
27 #include "object.h"
28 #include "connection.h"
29 #include "connectionpoint.h"
30 #include "diarenderer.h"
31 #include "attributes.h"
32 #include "diamenu.h"
33 
34 #include "pixmaps/tree.xpm"
35 
36 #define LINE_WIDTH 0.1
37 
38 #define DEFAULT_NUMHANDLES 6
39 #define HANDLE_BUS (HANDLE_CUSTOM1)
40 
41 
42 typedef struct _Tree {
43   Connection connection;
44 
45   int num_handles;
46   Handle **handles;
47   Point *parallel_points;
48   Point real_ends[2];
49   Color line_color;
50 } Tree;
51 
52 enum change_type {
53   TYPE_ADD_POINT,
54   TYPE_REMOVE_POINT
55 };
56 
57 struct PointChange {
58   ObjectChange obj_change;
59 
60   enum change_type type;
61   int applied;
62 
63   Point point;
64   Handle *handle; /* owning ref when not applied for ADD_POINT
65 		     owning ref when applied for REMOVE_POINT */
66   ConnectionPoint *connected_to; /* NULL if not connected */
67 };
68 
69 static ObjectChange* tree_move_handle(Tree *tree, Handle *handle,
70 				     Point *to, ConnectionPoint *cp,
71 				     HandleMoveReason reason, ModifierKeys modifiers);
72 static ObjectChange* tree_move(Tree *tree, Point *to);
73 static void tree_select(Tree *tree, Point *clicked_point,
74 		       DiaRenderer *interactive_renderer);
75 static void tree_draw(Tree *tree, DiaRenderer *renderer);
76 static DiaObject *tree_create(Point *startpoint,
77 			  void *user_data,
78 			  Handle **handle1,
79 			  Handle **handle2);
80 static real tree_distance_from(Tree *tree, Point *point);
81 static void tree_update_data(Tree *tree);
82 static void tree_destroy(Tree *tree);
83 static DiaObject *tree_copy(Tree *tree);
84 
85 static PropDescription *tree_describe_props(Tree *tree);
86 static void tree_get_props(Tree *tree, GPtrArray *props);
87 static void tree_set_props(Tree *tree, GPtrArray *props);
88 static void tree_save(Tree *tree, ObjectNode obj_node, const char *filename);
89 static DiaObject *tree_load(ObjectNode obj_node, int version,
90 			const char *filename);
91 static DiaMenu *tree_get_object_menu(Tree *tree, Point *clickedpoint);
92 
93 static ObjectChange *
94 tree_create_change(Tree *tree, enum change_type type,
95 		  Point *point, Handle *handle,
96 		  ConnectionPoint *connected_to);
97 
98 
99 static ObjectTypeOps tree_type_ops =
100 {
101   (CreateFunc) tree_create,
102   (LoadFunc)   tree_load,
103   (SaveFunc)   tree_save,
104   (GetDefaultsFunc)   NULL,
105   (ApplyDefaultsFunc) NULL
106 };
107 
108 DiaObjectType tree_type =
109 {
110   "Misc - Tree",   /* name */
111   0,                  /* version */
112   (char **) tree_xpm,  /* pixmap */
113   &tree_type_ops       /* ops */
114 };
115 
116 static ObjectOps tree_ops = {
117   (DestroyFunc)         tree_destroy,
118   (DrawFunc)            tree_draw,
119   (DistanceFunc)        tree_distance_from,
120   (SelectFunc)          tree_select,
121   (CopyFunc)            tree_copy,
122   (MoveFunc)            tree_move,
123   (MoveHandleFunc)      tree_move_handle,
124   (GetPropertiesFunc)   object_create_props_dialog,
125   (ApplyPropertiesDialogFunc) object_apply_props_from_dialog,
126   (ObjectMenuFunc)      tree_get_object_menu,
127   (DescribePropsFunc)   tree_describe_props,
128   (GetPropsFunc)        tree_get_props,
129   (SetPropsFunc)        tree_set_props,
130   (TextEditFunc) 0,
131   (ApplyPropertiesListFunc) object_apply_props,
132 };
133 
134 static PropDescription tree_props[] = {
135   OBJECT_COMMON_PROPERTIES,
136   PROP_STD_LINE_COLOUR,
137   PROP_DESC_END
138 };
139 
140 static PropDescription *
tree_describe_props(Tree * tree)141 tree_describe_props(Tree *tree)
142 {
143   if (tree_props[0].quark == 0)
144     prop_desc_list_calculate_quarks(tree_props);
145   return tree_props;
146 }
147 
148 static PropOffset tree_offsets[] = {
149   OBJECT_COMMON_PROPERTIES_OFFSETS,
150   { "line_colour", PROP_TYPE_COLOUR, offsetof(Tree, line_color) },
151   { NULL, 0, 0 }
152 };
153 
154 static void
tree_get_props(Tree * tree,GPtrArray * props)155 tree_get_props(Tree *tree, GPtrArray *props)
156 {
157   object_get_props_from_offsets(&tree->connection.object, tree_offsets,
158 				props);
159 }
160 
161 static void
tree_set_props(Tree * tree,GPtrArray * props)162 tree_set_props(Tree *tree, GPtrArray *props)
163 {
164   object_set_props_from_offsets(&tree->connection.object, tree_offsets,
165 				props);
166   tree_update_data(tree);
167 }
168 
169 static real
tree_distance_from(Tree * tree,Point * point)170 tree_distance_from(Tree *tree, Point *point)
171 {
172   Point *endpoints;
173   real min_dist;
174   int i;
175 
176   endpoints = &tree->real_ends[0];
177   min_dist = distance_line_point( &endpoints[0], &endpoints[1],
178 				  LINE_WIDTH, point);
179   for (i=0;i<tree->num_handles;i++) {
180     min_dist = MIN(min_dist,
181 		   distance_line_point( &tree->handles[i]->pos,
182 					&tree->parallel_points[i],
183 					LINE_WIDTH, point));
184   }
185   return min_dist;
186 }
187 
188 static void
tree_select(Tree * tree,Point * clicked_point,DiaRenderer * interactive_renderer)189 tree_select(Tree *tree, Point *clicked_point,
190 	    DiaRenderer *interactive_renderer)
191 {
192   connection_update_handles(&tree->connection);
193 }
194 
195 static ObjectChange*
tree_move_handle(Tree * tree,Handle * handle,Point * to,ConnectionPoint * cp,HandleMoveReason reason,ModifierKeys modifiers)196 tree_move_handle(Tree *tree, Handle *handle,
197 		Point *to, ConnectionPoint *cp,
198 		HandleMoveReason reason, ModifierKeys modifiers)
199 {
200   Connection *conn = &tree->connection;
201   Point *endpoints;
202   static real *parallel=NULL;
203   static real *perp=NULL;
204   static int max_num=0;
205   Point vhat, vhatperp;
206   Point u;
207   real vlen, vlen2;
208   real len_scale;
209   int i;
210 
211   if (tree->num_handles>max_num) {
212     if (parallel!=NULL) {
213       g_free(parallel);
214       g_free(perp);
215     }
216     parallel = g_malloc(sizeof(real)*tree->num_handles);
217     perp = g_malloc(sizeof(real)*tree->num_handles);
218     max_num = tree->num_handles;
219   }
220 
221   if (handle->id == HANDLE_BUS) {
222     handle->pos = *to;
223   } else {
224     endpoints = &conn->endpoints[0];
225     vhat = endpoints[1];
226     point_sub(&vhat, &endpoints[0]);
227     if ((fabs(vhat.x) == 0.0) && (fabs(vhat.y)==0.0)) {
228       vhat.y += 0.01;
229     }
230     vlen = sqrt(point_dot(&vhat, &vhat));
231     point_scale(&vhat, 1.0/vlen);
232 
233     vhatperp.y = -vhat.x;
234     vhatperp.x =  vhat.y;
235     for (i=0;i<tree->num_handles;i++) {
236       u = tree->handles[i]->pos;
237       point_sub(&u, &endpoints[0]);
238       parallel[i] = point_dot(&vhat, &u);
239       perp[i] = point_dot(&vhatperp, &u);
240     }
241 
242     connection_move_handle(&tree->connection, handle->id, to, cp,
243 			   reason, modifiers);
244 
245     vhat = endpoints[1];
246     point_sub(&vhat, &endpoints[0]);
247     if ((fabs(vhat.x) == 0.0) && (fabs(vhat.y)==0.0)) {
248       vhat.y += 0.01;
249     }
250     vlen2 = sqrt(point_dot(&vhat, &vhat));
251     len_scale = vlen2 / vlen;
252     point_normalize(&vhat);
253     vhatperp.y = -vhat.x;
254     vhatperp.x =  vhat.y;
255     for (i=0;i<tree->num_handles;i++) {
256       if (tree->handles[i]->connected_to == NULL) {
257 	u = vhat;
258 	point_scale(&u, parallel[i]*len_scale);
259 	point_add(&u, &endpoints[0]);
260 	tree->parallel_points[i] = u;
261 	u = vhatperp;
262 	point_scale(&u, perp[i]);
263 	point_add(&u, &tree->parallel_points[i]);
264 	tree->handles[i]->pos = u;
265 	}
266     }
267   }
268 
269   tree_update_data(tree);
270 
271   return NULL;
272 }
273 
274 static ObjectChange*
tree_move(Tree * tree,Point * to)275 tree_move(Tree *tree, Point *to)
276 {
277   Point delta;
278   Point *endpoints = &tree->connection.endpoints[0];
279   DiaObject *obj = &tree->connection.object;
280   int i;
281 
282   delta = *to;
283   point_sub(&delta, &obj->position);
284 
285   for (i=0;i<2;i++) {
286     point_add(&endpoints[i], &delta);
287     point_add(&tree->real_ends[i], &delta);
288   }
289 
290   for (i=0;i<tree->num_handles;i++) {
291     if (tree->handles[i]->connected_to == NULL) {
292       point_add(&tree->handles[i]->pos, &delta);
293     }
294   }
295 
296   tree_update_data(tree);
297 
298   return NULL;
299 }
300 
301 static void
tree_draw(Tree * tree,DiaRenderer * renderer)302 tree_draw(Tree *tree, DiaRenderer *renderer)
303 {
304   DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
305   Point *endpoints;
306   int i;
307 
308   assert(tree != NULL);
309   assert(renderer != NULL);
310 
311   endpoints = &tree->real_ends[0];
312 
313   renderer_ops->set_linewidth(renderer, LINE_WIDTH);
314   renderer_ops->set_linestyle(renderer, LINESTYLE_SOLID);
315   renderer_ops->set_linecaps(renderer, LINECAPS_BUTT);
316 
317   renderer_ops->draw_line(renderer,
318 			   &endpoints[0], &endpoints[1],
319  			   &tree->line_color);
320 
321   for (i=0;i<tree->num_handles;i++) {
322     renderer_ops->draw_line(renderer,
323 			     &tree->parallel_points[i],
324 			     &tree->handles[i]->pos,
325 			     &tree->line_color);
326   }
327 }
328 
329 static DiaObject *
tree_create(Point * startpoint,void * user_data,Handle ** handle1,Handle ** handle2)330 tree_create(Point *startpoint,
331 	   void *user_data,
332 	   Handle **handle1,
333 	   Handle **handle2)
334 {
335   Tree *tree;
336   Connection *conn;
337   LineBBExtras *extra;
338   DiaObject *obj;
339   Point defaultlen = { 0.0, 20.0 };
340   int i;
341 
342   tree = g_malloc0(sizeof(Tree));
343 
344   conn = &tree->connection;
345   conn->endpoints[0] = *startpoint;
346   conn->endpoints[1] = *startpoint;
347   point_add(&conn->endpoints[1], &defaultlen);
348 
349   obj = &conn->object;
350   extra = &conn->extra_spacing;
351 
352   obj->type = &tree_type;
353   obj->ops = &tree_ops;
354 
355   tree->num_handles = DEFAULT_NUMHANDLES;
356 
357   connection_init(conn, 2+tree->num_handles, 0);
358   tree->line_color = attributes_get_foreground();
359   tree->handles = g_malloc(sizeof(Handle *)*tree->num_handles);
360   tree->parallel_points = g_malloc(sizeof(Point)*tree->num_handles);
361   for (i=0;i<tree->num_handles;i++) {
362     tree->handles[i] = g_new0(Handle,1);
363     tree->handles[i]->id = HANDLE_BUS;
364     tree->handles[i]->type = HANDLE_MINOR_CONTROL;
365     tree->handles[i]->connect_type = HANDLE_CONNECTABLE_NOBREAK;
366     tree->handles[i]->connected_to = NULL;
367     tree->handles[i]->pos = *startpoint;
368     tree->handles[i]->pos.y += 20*((real)i+1)/(tree->num_handles+1);
369     tree->handles[i]->pos.x += 1.0; /* (i%2==0)?1.0:-1.0; */
370     obj->handles[2+i] = tree->handles[i];
371   }
372 
373   extra->start_trans =
374     extra->end_trans =
375     extra->start_long =
376     extra->end_long = LINE_WIDTH/2.0;
377   tree_update_data(tree);
378 
379   *handle1 = obj->handles[0];
380   *handle2 = obj->handles[1];
381   return &tree->connection.object;
382 }
383 
384 static void
tree_destroy(Tree * tree)385 tree_destroy(Tree *tree)
386 {
387   int i;
388   connection_destroy(&tree->connection);
389   for (i=0;i<tree->num_handles;i++)
390     g_free(tree->handles[i]);
391   g_free(tree->handles);
392   g_free(tree->parallel_points);
393 }
394 
395 static DiaObject *
tree_copy(Tree * tree)396 tree_copy(Tree *tree)
397 {
398   Tree *newtree;
399   Connection *conn, *newconn;
400   DiaObject *newobj;
401   int i;
402 
403   conn = &tree->connection;
404 
405   newtree = g_malloc0(sizeof(Tree));
406   newconn = &newtree->connection;
407   newobj = &newconn->object;
408 
409   connection_copy(conn, newconn);
410 
411   newtree->num_handles = tree->num_handles;
412   newtree->line_color = tree->line_color;
413 
414   newtree->handles = g_malloc(sizeof(Handle *)*newtree->num_handles);
415   newtree->parallel_points = g_malloc(sizeof(Point)*newtree->num_handles);
416 
417   for (i=0;i<newtree->num_handles;i++) {
418     newtree->handles[i] = g_new0(Handle,1);
419     *newtree->handles[i] = *tree->handles[i];
420     newtree->handles[i]->connected_to = NULL;
421     newobj->handles[2+i] = newtree->handles[i];
422     newtree->parallel_points[i] = tree->parallel_points[i];
423   }
424 
425   newtree->real_ends[0] = tree->real_ends[0];
426   newtree->real_ends[1] = tree->real_ends[1];
427 
428   return &newtree->connection.object;
429 }
430 
431 
432 static void
tree_update_data(Tree * tree)433 tree_update_data(Tree *tree)
434 {
435   Connection *conn = &tree->connection;
436   DiaObject *obj = &conn->object;
437   int i;
438   Point u, v, vhat;
439   Point *endpoints;
440   real ulen;
441   real min_par, max_par;
442 
443 /*
444  * This seems to break stuff wildly.
445  */
446 /*
447   if (connpoint_is_autogap(conn->endpoint_handles[0].connected_to) ||
448       connpoint_is_autogap(conn->endpoint_handles[1].connected_to)) {
449     connection_adjust_for_autogap(conn);
450   }
451 */
452   endpoints = &conn->endpoints[0];
453   obj->position = endpoints[0];
454 
455   v = endpoints[1];
456   point_sub(&v, &endpoints[0]);
457   if ((fabs(v.x) == 0.0) && (fabs(v.y)==0.0)) {
458     v.y += 0.01;
459   }
460   vhat = v;
461   point_normalize(&vhat);
462   min_par = 0.0;
463   max_par = point_dot(&vhat, &v);
464   for (i=0;i<tree->num_handles;i++) {
465     u = tree->handles[i]->pos;
466     point_sub(&u, &endpoints[0]);
467     ulen = point_dot(&u, &vhat);
468     min_par = MIN(min_par, ulen);
469     max_par = MAX(max_par, ulen);
470     tree->parallel_points[i] = vhat;
471     point_scale(&tree->parallel_points[i], ulen);
472     point_add(&tree->parallel_points[i], &endpoints[0]);
473   }
474 
475   min_par -= LINE_WIDTH/2.0;
476   max_par += LINE_WIDTH/2.0;
477 
478   tree->real_ends[0] = vhat;
479   point_scale(&tree->real_ends[0], min_par);
480   point_add(&tree->real_ends[0], &endpoints[0]);
481   tree->real_ends[1] = vhat;
482   point_scale(&tree->real_ends[1], max_par);
483   point_add(&tree->real_ends[1], &endpoints[0]);
484 
485   connection_update_boundingbox(conn);
486   rectangle_add_point(&obj->bounding_box, &tree->real_ends[0]);
487   rectangle_add_point(&obj->bounding_box, &tree->real_ends[1]);
488   for (i=0;i<tree->num_handles;i++) {
489     rectangle_add_point(&obj->bounding_box, &tree->handles[i]->pos);
490   }
491 
492   connection_update_handles(conn);
493 }
494 
495 static void
tree_add_handle(Tree * tree,Point * p,Handle * handle)496 tree_add_handle(Tree *tree, Point *p, Handle *handle)
497 {
498   int i;
499 
500   tree->num_handles++;
501 
502   /* Allocate more handles */
503   tree->handles = g_realloc(tree->handles,
504 			   sizeof(Handle *)*tree->num_handles);
505   tree->parallel_points = g_realloc(tree->parallel_points,
506 				   sizeof(Point)*tree->num_handles);
507 
508   i = tree->num_handles - 1;
509 
510   tree->handles[i] = handle;
511   tree->handles[i]->id = HANDLE_BUS;
512   tree->handles[i]->type = HANDLE_MINOR_CONTROL;
513   tree->handles[i]->connect_type = HANDLE_CONNECTABLE_NOBREAK;
514   tree->handles[i]->connected_to = NULL;
515   tree->handles[i]->pos = *p;
516   object_add_handle(&tree->connection.object, tree->handles[i]);
517 }
518 
519 static void
tree_remove_handle(Tree * tree,Handle * handle)520 tree_remove_handle(Tree *tree, Handle *handle)
521 {
522   int i, j;
523 
524   for (i=0;i<tree->num_handles;i++) {
525     if (tree->handles[i] == handle) {
526       object_remove_handle(&tree->connection.object, handle);
527 
528       for (j=i;j<tree->num_handles-1;j++) {
529 	tree->handles[j] = tree->handles[j+1];
530 	tree->parallel_points[j] = tree->parallel_points[j+1];
531       }
532 
533       tree->num_handles--;
534       tree->handles = g_realloc(tree->handles,
535 			       sizeof(Handle *)*tree->num_handles);
536       tree->parallel_points = g_realloc(tree->parallel_points,
537 				       sizeof(Point)*tree->num_handles);
538 
539       break;
540     }
541   }
542 }
543 
544 static ObjectChange *
tree_add_handle_callback(DiaObject * obj,Point * clicked,gpointer data)545 tree_add_handle_callback (DiaObject *obj, Point *clicked, gpointer data)
546 {
547   Tree *tree = (Tree *) obj;
548   Handle *handle;
549 
550   handle = g_new0(Handle,1);
551   tree_add_handle(tree, clicked, handle);
552   tree_update_data(tree);
553 
554   return tree_create_change(tree, TYPE_ADD_POINT, clicked, handle, NULL);
555 }
556 
557 static int
tree_point_near_handle(Tree * tree,Point * p)558 tree_point_near_handle(Tree *tree, Point *p)
559 {
560   int i, min;
561   real dist = 1000.0;
562   real d;
563 
564   min = -1;
565   for (i=0;i<tree->num_handles;i++) {
566     d = distance_line_point(&tree->parallel_points[i],
567 			    &tree->handles[i]->pos, 0.0, p);
568 
569     if (d < dist) {
570       dist = d;
571       min = i;
572     }
573   }
574 
575   if (dist < 0.5)
576     return min;
577   else
578     return -1;
579 }
580 
581 static ObjectChange *
tree_delete_handle_callback(DiaObject * obj,Point * clicked,gpointer data)582 tree_delete_handle_callback (DiaObject *obj, Point *clicked, gpointer data)
583 {
584   Tree *tree = (Tree *) obj;
585   Handle *handle;
586   int handle_num;
587   ConnectionPoint *connectionpoint;
588   Point p;
589 
590   handle_num = tree_point_near_handle(tree, clicked);
591 
592   handle = tree->handles[handle_num];
593   p = handle->pos;
594   connectionpoint = handle->connected_to;
595 
596   object_unconnect(obj, handle);
597   tree_remove_handle(tree, handle );
598   tree_update_data(tree);
599 
600   return tree_create_change(tree, TYPE_REMOVE_POINT, &p, handle,
601 			   connectionpoint);
602 }
603 
604 static DiaMenuItem tree_menu_items[] = {
605   { N_("Add Handle"), tree_add_handle_callback, NULL, 1 },
606   { N_("Delete Handle"), tree_delete_handle_callback, NULL, 1 },
607 };
608 
609 static DiaMenu tree_menu = {
610   "Tree",
611   sizeof(tree_menu_items)/sizeof(DiaMenuItem),
612   tree_menu_items,
613   NULL
614 };
615 
616 static DiaMenu *
tree_get_object_menu(Tree * tree,Point * clickedpoint)617 tree_get_object_menu(Tree *tree, Point *clickedpoint)
618 {
619   /* Set entries sensitive/selected etc here */
620   tree_menu_items[0].active = 1;
621   tree_menu_items[1].active = (tree_point_near_handle(tree, clickedpoint) >= 0);
622   return &tree_menu;
623 }
624 
625 static void
tree_save(Tree * tree,ObjectNode obj_node,const char * filename)626 tree_save(Tree *tree, ObjectNode obj_node, const char *filename)
627 {
628   int i;
629   AttributeNode attr;
630 
631   connection_save(&tree->connection, obj_node);
632 
633   data_add_color( new_attribute(obj_node, "line_color"), &tree->line_color);
634 
635   attr = new_attribute(obj_node, "tree_handles");
636 
637   for (i=0;i<tree->num_handles;i++) {
638     data_add_point(attr, &tree->handles[i]->pos);
639   }
640 }
641 
642 static DiaObject *
tree_load(ObjectNode obj_node,int version,const char * filename)643 tree_load(ObjectNode obj_node, int version, const char *filename)
644 {
645   Tree *tree;
646   Connection *conn;
647   LineBBExtras *extra;
648   DiaObject *obj;
649   AttributeNode attr;
650   DataNode data;
651   int i;
652 
653   tree = g_malloc0(sizeof(Tree));
654 
655   conn = &tree->connection;
656   obj = &conn->object;
657   extra = &conn->extra_spacing;
658 
659   obj->type = &tree_type;
660   obj->ops = &tree_ops;
661 
662   connection_load(conn, obj_node);
663 
664   attr = object_find_attribute(obj_node, "tree_handles");
665 
666   tree->num_handles = 0;
667   if (attr != NULL)
668     tree->num_handles = attribute_num_data(attr);
669 
670   connection_init(conn, 2 + tree->num_handles, 0);
671 
672   data = attribute_first_data(attr);
673   tree->handles = g_malloc(sizeof(Handle *)*tree->num_handles);
674   tree->parallel_points = g_malloc(sizeof(Point)*tree->num_handles);
675   for (i=0;i<tree->num_handles;i++) {
676     tree->handles[i] = g_new0(Handle,1);
677     tree->handles[i]->id = HANDLE_BUS;
678     tree->handles[i]->type = HANDLE_MINOR_CONTROL;
679     tree->handles[i]->connect_type = HANDLE_CONNECTABLE_NOBREAK;
680     tree->handles[i]->connected_to = NULL;
681     data_point(data, &tree->handles[i]->pos);
682     obj->handles[2+i] = tree->handles[i];
683 
684     data = data_next(data);
685   }
686 
687   tree->line_color = color_black;
688   attr = object_find_attribute(obj_node, "line_color");
689   if (attr != NULL)
690     data_color(attribute_first_data(attr), &tree->line_color);
691 
692   extra->start_trans =
693     extra->end_trans =
694     extra->start_long =
695     extra->end_long = LINE_WIDTH/2.0;
696   tree_update_data(tree);
697 
698   return &tree->connection.object;
699 }
700 
701 static void
tree_change_free(struct PointChange * change)702 tree_change_free(struct PointChange *change)
703 {
704   if ( (change->type==TYPE_ADD_POINT && !change->applied) ||
705        (change->type==TYPE_REMOVE_POINT && change->applied) ){
706     if (change->handle)
707       g_free(change->handle);
708     change->handle = NULL;
709   }
710 }
711 
712 static void
tree_change_apply(struct PointChange * change,DiaObject * obj)713 tree_change_apply(struct PointChange *change, DiaObject *obj)
714 {
715   change->applied = 1;
716   switch (change->type) {
717   case TYPE_ADD_POINT:
718     tree_add_handle((Tree *)obj, &change->point, change->handle);
719     break;
720   case TYPE_REMOVE_POINT:
721     object_unconnect(obj, change->handle);
722     tree_remove_handle((Tree *)obj, change->handle);
723     break;
724   }
725   tree_update_data((Tree *)obj);
726 }
727 
728 static void
tree_change_revert(struct PointChange * change,DiaObject * obj)729 tree_change_revert(struct PointChange *change, DiaObject *obj)
730 {
731   switch (change->type) {
732   case TYPE_ADD_POINT:
733     tree_remove_handle((Tree *)obj, change->handle);
734     break;
735   case TYPE_REMOVE_POINT:
736     tree_add_handle((Tree *)obj, &change->point, change->handle);
737     if (change->connected_to) {
738       object_connect(obj, change->handle, change->connected_to);
739     }
740     break;
741   }
742   tree_update_data((Tree *)obj);
743   change->applied = 0;
744 }
745 
746 static ObjectChange *
tree_create_change(Tree * tree,enum change_type type,Point * point,Handle * handle,ConnectionPoint * connected_to)747 tree_create_change(Tree *tree, enum change_type type,
748 		  Point *point, Handle *handle,
749 		  ConnectionPoint *connected_to)
750 {
751   struct PointChange *change;
752 
753   change = g_new0(struct PointChange, 1);
754 
755   change->obj_change.apply = (ObjectChangeApplyFunc) tree_change_apply;
756   change->obj_change.revert = (ObjectChangeRevertFunc) tree_change_revert;
757   change->obj_change.free = (ObjectChangeFreeFunc) tree_change_free;
758 
759   change->type = type;
760   change->applied = 1;
761   change->point = *point;
762   change->handle = handle;
763   change->connected_to = connected_to;
764 
765   return (ObjectChange *)change;
766 }
767