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