gschem: Play with move net-rubberbanding heuristics a little
[geda-gaf/pcjc2.git] / gschem / src / o_move.c
blobd744a5c4f255bf57689792a8d972b868ddffd1c1
1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2011 gEDA Contributors (see ChangeLog for details)
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 #include <config.h>
22 #include <stdio.h>
24 #include "gschem.h"
26 #ifdef HAVE_LIBDMALLOC
27 #include <dmalloc.h>
28 #endif
30 /*! \todo Finish function documentation!!!
31 * \brief
32 * \par Function Description
35 void o_move_start(GSCHEM_TOPLEVEL *w_current, int w_x, int w_y)
37 TOPLEVEL *toplevel = w_current->toplevel;
38 GList *s_iter;
40 g_return_if_fail (w_current->stretch_list == NULL);
42 if (o_select_selected (w_current)) {
44 /* Save the current state. When rotating the selection when moving,
45 we have to come back to here */
46 o_undo_savestate(w_current, UNDO_ALL);
47 w_current->last_drawb_mode = LAST_DRAWB_MODE_NONE;
48 w_current->event_state = MOVE;
50 w_current->first_wx = w_current->second_wx = w_x;
51 w_current->first_wy = w_current->second_wy = w_y;
53 o_invalidate_glist (w_current,
54 geda_list_get_glist (toplevel->page_current->selection_list));
56 if (w_current->netconn_rubberband) {
57 o_move_prep_rubberband(w_current);
59 /* Set the dont_redraw flag on rubberbanded objects and invalidate them.
60 * This ensures that they are not drawn (in their un-stretched position)
61 * during screen updates. */
62 for (s_iter = w_current->stretch_list;
63 s_iter != NULL; s_iter = g_list_next (s_iter)) {
64 STRETCH *stretch = s_iter->data;
65 stretch->object->dont_redraw = TRUE;
66 o_invalidate (w_current, stretch->object);
70 o_select_move_to_place_list(w_current);
71 w_current->inside_action = 1;
73 o_move_invalidate_rubber (w_current, TRUE);
77 /*! \todo Finish function documentation!!!
78 * \brief
79 * \par Function Description
82 static void o_move_end_lowlevel_glist (GSCHEM_TOPLEVEL *w_current,
83 GList *list,
84 int diff_x, int diff_y)
86 OBJECT *object;
87 GList *iter;
89 iter = list;
90 while (iter != NULL) {
91 object = (OBJECT *)iter->data;
92 o_move_end_lowlevel (w_current, object, diff_x, diff_y);
93 iter = g_list_next (iter);
98 /*! \todo Finish function documentation!!!
99 * \brief
100 * \par Function Description
103 void o_move_end_lowlevel (GSCHEM_TOPLEVEL *w_current,
104 OBJECT *object,
105 int diff_x, int diff_y)
107 TOPLEVEL *toplevel = w_current->toplevel;
109 switch (object->type) {
111 case (OBJ_NET):
112 case (OBJ_BUS):
113 case (OBJ_PIN):
114 s_conn_remove_object (toplevel, object);
115 o_translate_world (toplevel, diff_x, diff_y, object);
116 s_conn_update_object (toplevel, object);
117 break;
119 default:
120 o_translate_world (toplevel, diff_x, diff_y, object);
121 break;
125 /*! \todo Finish function documentation!!!
126 * \brief
127 * \par Function Description
130 void o_move_end(GSCHEM_TOPLEVEL *w_current)
132 TOPLEVEL *toplevel = w_current->toplevel;
133 GList *s_current = NULL;
134 OBJECT *object;
135 int diff_x, diff_y;
136 int left, top, right, bottom;
137 GList *s_iter;
138 GList *rubbernet_objects = NULL;
140 object = o_select_return_first_object(w_current);
142 if (!object) {
143 /* actually this is an error condition hack */
144 w_current->inside_action = 0;
145 i_set_state(w_current, SELECT);
146 return;
149 diff_x = w_current->second_wx - w_current->first_wx;
150 diff_y = w_current->second_wy - w_current->first_wy;
152 o_move_invalidate_rubber (w_current, FALSE);
153 w_current->rubber_visible = 0;
155 if (w_current->netconn_rubberband) {
156 o_move_end_rubberband (w_current, diff_x, diff_y, &rubbernet_objects);
159 /* Unset the dont_redraw flag on rubberbanded objects.
160 * We set this above, in o_move_start(). */
161 for (s_iter = w_current->stretch_list;
162 s_iter != NULL; s_iter = g_list_next (s_iter)) {
163 STRETCH *stretch = s_iter->data;
164 stretch->object->dont_redraw = FALSE;
167 s_current = geda_list_get_glist( toplevel->page_current->selection_list );
169 while (s_current != NULL) {
171 object = (OBJECT *) s_current->data;
173 if (object == NULL) {
174 fprintf(stderr, _("ERROR: NULL object in o_move_end!\n"));
175 exit(-1);
179 switch (object->type) {
180 case (OBJ_COMPLEX):
181 case (OBJ_PLACEHOLDER):
183 /* TODO: Fix so we can just pass the complex to o_move_end_lowlevel,
184 * IE.. by falling through the bottom of this case statement. */
186 /* this next section of code is from */
187 /* o_complex_world_translate_world */
188 object->complex->x = object->complex->x + diff_x;
189 object->complex->y = object->complex->y + diff_y;
191 o_move_end_lowlevel_glist (w_current, object->complex->prim_objs,
192 diff_x, diff_y);
195 world_get_object_glist_bounds (toplevel, object->complex->prim_objs,
196 &left, &top, &right, &bottom);
198 object->w_left = left;
199 object->w_top = top;
200 object->w_right = right;
201 object->w_bottom = bottom;
203 break;
205 default:
206 o_move_end_lowlevel (w_current, object, diff_x, diff_y);
207 break;
210 s_current = g_list_next(s_current);
213 /* Remove the undo saved in o_move_start */
214 o_undo_remove_last_undo(w_current);
216 /* Draw the objects that were moved */
217 o_invalidate_glist (w_current,
218 geda_list_get_glist (toplevel->page_current->selection_list));
220 /* Draw the connected nets/buses that were also changed */
221 o_invalidate_glist (w_current, rubbernet_objects);
223 /* Call move-objects-hook for moved objects and changed connected
224 * nets/buses */
225 GList *moved_list = g_list_concat (toplevel->page_current->place_list,
226 rubbernet_objects);
227 toplevel->page_current->place_list = NULL;
228 rubbernet_objects = NULL;
229 g_run_hook_object_list ("%move-objects-hook", moved_list);
230 g_list_free (moved_list);
232 toplevel->page_current->CHANGED = 1;
233 o_undo_savestate(w_current, UNDO_ALL);
235 s_stretch_destroy_all (w_current->stretch_list);
236 w_current->stretch_list = NULL;
240 /*! \todo Finish function documentation!!!
241 * \brief
242 * \par Function Description
245 void o_move_cancel (GSCHEM_TOPLEVEL *w_current)
247 GList *s_iter;
249 /* Unset the dont_redraw flag on rubberbanded objects.
250 * We set this above, in o_move_start(). */
251 for (s_iter = w_current->stretch_list;
252 s_iter != NULL; s_iter = g_list_next (s_iter)) {
253 STRETCH *stretch = s_iter->data;
254 stretch->object->dont_redraw = FALSE;
256 g_list_free(w_current->toplevel->page_current->place_list);
257 w_current->toplevel->page_current->place_list = NULL;
259 s_stretch_destroy_all (w_current->stretch_list);
260 w_current->stretch_list = NULL;
262 w_current->inside_action = 0;
263 i_set_state (w_current, SELECT);
265 o_undo_callback(w_current, UNDO_ACTION);
269 /*! \todo Finish function documentation!!!
270 * \brief
271 * \par Function Description
274 void o_move_motion (GSCHEM_TOPLEVEL *w_current, int w_x, int w_y)
276 TOPLEVEL *toplevel = w_current->toplevel;
277 GList *selection, *s_current;
278 OBJECT *object;
279 gint object_x, object_y;
280 gboolean resnap = FALSE;
282 selection = geda_list_get_glist( toplevel->page_current->selection_list );
284 /* realign the object if we are in resnap mode */
285 if (selection != NULL
286 && w_current->snap == SNAP_RESNAP) {
288 if (g_list_length(selection) > 1) {
289 /* find an object that is not attached to any other object */
290 for (s_current = selection;
291 s_current != NULL;
292 s_current = g_list_next(s_current)) {
293 if (((OBJECT *) s_current->data)->attached_to == NULL) {
294 object = (OBJECT *) s_current->data;
295 resnap = TRUE;
296 break;
300 /* Only resnap single elements. This is also the case if
301 the selection contains one object and all other object
302 elements are attributes of the object element.*/
303 for (s_current = selection;
304 s_current != NULL && resnap == TRUE;
305 s_current = g_list_next(s_current)) {
306 if (!(object == (OBJECT *) s_current->data)
307 && !o_attrib_is_attached(toplevel,
308 (OBJECT *) s_current->data, object)) {
309 resnap = FALSE;
312 } else { /* single object */
313 resnap = TRUE;
314 object = (OBJECT *) selection->data;
317 /* manipulate w_x and w_y in a way that will lead to a position
318 of the object that is aligned with the grid */
319 if (resnap) {
320 if (o_get_position(toplevel, &object_x, &object_y, object)) {
321 w_x += snap_grid (w_current, object_x) - object_x;
322 w_y += snap_grid (w_current, object_y) - object_y;
327 o_move_invalidate_rubber (w_current, FALSE);
328 w_current->second_wx = w_x;
329 w_current->second_wy = w_y;
330 o_move_invalidate_rubber (w_current, TRUE);
334 /*! \todo Finish function documentation!!!
335 * \brief
336 * \par Function Description
339 void o_move_invalidate_rubber (GSCHEM_TOPLEVEL *w_current, int drawing)
341 GList *s_iter;
342 int diff_x, diff_y;
343 int dx1, dx2, dy1, dy2;
344 int x1, y1, x2, y2;
346 o_place_invalidate_rubber (w_current, drawing);
347 if (w_current->netconn_rubberband) {
349 diff_x = w_current->second_wx - w_current->first_wx;
350 diff_y = w_current->second_wy - w_current->first_wy;
352 for (s_iter = w_current->stretch_list;
353 s_iter != NULL; s_iter = g_list_next (s_iter)) {
354 STRETCH *s_current = s_iter->data;
355 OBJECT *object = s_current->object;
357 switch (object->type) {
358 case (OBJ_NET):
359 case (OBJ_BUS):
360 s_stretch_calc_deltas (w_current, s_current,
361 diff_x, diff_y, &dx1, &dy1, &dx2, &dy2);
363 WORLDtoSCREEN (w_current, object->line->x[0] + dx1,
364 object->line->y[0] + dy1, &x1, &y1);
365 WORLDtoSCREEN (w_current, object->line->x[1] + dx2,
366 object->line->y[1] + dy2, &x2, &y2);
368 o_invalidate_rect (w_current, x1, y1, x2, y2);
375 /*! \todo Finish function documentation!!!
376 * \brief
377 * \par Function Description
380 void o_move_draw_rubber (GSCHEM_TOPLEVEL *w_current, int drawing)
382 GList *s_iter;
383 int diff_x, diff_y;
384 int dx1, dy1, dx2, dy2;
386 o_place_draw_rubber (w_current, drawing);
388 if (!w_current->netconn_rubberband)
389 return;
391 diff_x = w_current->second_wx - w_current->first_wx;
392 diff_y = w_current->second_wy - w_current->first_wy;
394 for (s_iter = w_current->stretch_list;
395 s_iter != NULL; s_iter = g_list_next (s_iter)) {
396 STRETCH *s_current = s_iter->data;
397 OBJECT *object = s_current->object;
399 s_stretch_calc_deltas (w_current, s_current,
400 diff_x, diff_y, &dx1, &dy1, &dx2, &dy2);
402 switch (object->type) {
403 case (OBJ_NET):
404 o_net_draw_stretch (w_current, object, dx1, dy1, dx2, dy2);
405 break;
407 case (OBJ_BUS):
408 o_bus_draw_stretch (w_current, object, dx1, dy1, dx2, dy2);
409 break;
415 /*! \todo Finish function documentation!!!
416 * \brief
417 * \par Function Description
420 int o_move_return_whichone(OBJECT * object, int x, int y)
422 if (object->line->x[0] == x && object->line->y[0] == y) {
423 return (0);
426 if (object->line->x[1] == x && object->line->y[1] == y) {
427 return (1);
430 fprintf(stderr,
431 _("DOH! tried to find the whichone, but didn't find it!\n"));
432 return (-1);
436 static GList *add_dummy_stretch_net (GSCHEM_TOPLEVEL *w_current,
437 GList *new_nets, CONN *conn,
438 int x1_percent, int y1_percent,
439 int x2_percent, int y2_percent)
441 OBJECT *new_net = o_net_new (w_current->toplevel, OBJ_NET, NET_COLOR,
442 conn->x, conn->y, conn->x, conn->y);
444 w_current->stretch_list = s_stretch_add (w_current->stretch_list, new_net,
445 x1_percent, y1_percent,
446 x2_percent, y2_percent);
448 return g_list_prepend (new_nets, new_net);
451 /*! \todo Finish function documentation!!!
452 * \brief
453 * \par Function Description
456 void o_move_check_endpoint(GSCHEM_TOPLEVEL *w_current, OBJECT * object)
458 TOPLEVEL *toplevel = w_current->toplevel;
459 GList *cl_current;
460 CONN *c_current;
461 OBJECT *other;
462 int whichone;
463 GList *new_nets = NULL;
465 if (!object)
466 return;
468 if (object->type != OBJ_NET && object->type != OBJ_PIN &&
469 object->type != OBJ_BUS) {
470 fprintf(stderr, _("Got a non line object in o_move_check_endpoint\n"));
471 return;
474 for (cl_current = object->conn_list;
475 cl_current != NULL;
476 cl_current = g_list_next(cl_current)) {
478 c_current = (CONN *) cl_current->data;
479 other = c_current->other_object;
481 if (other == NULL)
482 continue;
484 /* really make sure that the object is not selected */
485 if (other->selected)
486 continue;
488 /* Catch pins, whos parent object is selected. */
489 if (other->parent != NULL && other->parent->selected)
490 continue;
492 if (c_current->type != CONN_ENDPOINT &&
493 (c_current->type != CONN_MIDPOINT ||
494 c_current->other_whichone == -1))
495 continue;
497 if (/* (net)pin to (net)pin contact */
498 (object->type == OBJ_PIN && object->pin_type == PIN_TYPE_NET &&
499 other->type == OBJ_PIN && other->pin_type == PIN_TYPE_NET)) {
501 /* net to (net)pin contact */
502 /* (object->type == OBJ_NET &&
503 other->type == OBJ_PIN && other->pin_type == PIN_TYPE_NET) */
505 /* other object is a pin, insert some nets to take up the slack */
507 /* This new net objects are explicitly added with the appropriate
508 * coefficients for them to take up the stretch between the pins.
509 * These nets become added to the page's connectivity list when
510 * they are added to the page at the end of this function, but won't
511 * really be important until they are rubber-banded into place.
513 * If the move operation is cancelled, the dummy 0 length nets
514 * are removed by the "undo" operation invoked.
516 * The following isn't a complete, there needs to be far better
517 * heuristics to adding these intermediate stretch nets.
520 /* ASSUME CASE FOR TWO BUTTING HORIZONTAL PINS */
521 if (1) {
522 /* Absorb half X movement */
523 new_nets = add_dummy_stretch_net (w_current, new_nets,
524 c_current, 0, 0, 50, 0);
525 /* Absorb the Y movement, shift by half X movement */
526 new_nets = add_dummy_stretch_net (w_current, new_nets,
527 c_current, 50, 0, 50, 100);
528 /* Absorb half X movement, shift by half X movement */
529 new_nets = add_dummy_stretch_net (w_current, new_nets,
530 c_current, 50, 100, 100, 100);
534 /* Only attempt to stretch nets and buses */
535 if (other->type != OBJ_NET && other->type != OBJ_BUS)
536 continue;
538 #if DEBUG
539 printf ("FOUND: %s type: %d, whichone: %d, x,y: %d %d\n",
540 other->name, c_current->type,
541 whichone, c_current->x, c_current->y);
543 printf("other x,y: %d %d\n", c_current->x, c_current->y);
544 printf("type: %d return: %d real: [ %d %d ]\n",
545 c_current->type, whichone, c_current->whichone,
546 c_current->other_whichone);
547 #endif
549 whichone = o_move_return_whichone (other, c_current->x, c_current->y);
550 if (whichone == 0) {
551 w_current->stretch_list =
552 s_stretch_add (w_current->stretch_list, other, 100, 100, 0, 0);
553 } else if (whichone == 1) {
554 w_current->stretch_list =
555 s_stretch_add (w_current->stretch_list, other, 0, 0, 100, 100);
559 s_page_append_list (toplevel, toplevel->page_current, new_nets);
562 /*! \todo Finish function documentation!!!
563 * \brief
564 * \par Function Description
567 void o_move_prep_rubberband(GSCHEM_TOPLEVEL *w_current)
569 TOPLEVEL *toplevel = w_current->toplevel;
570 GList *s_current;
571 OBJECT *object;
572 OBJECT *o_current;
573 GList *iter;
575 for (s_current = geda_list_get_glist (toplevel->page_current->selection_list);
576 s_current != NULL; s_current = g_list_next (s_current)) {
577 object = s_current->data;
579 if (object == NULL)
580 continue;
582 switch (object->type) {
583 case (OBJ_NET):
584 case (OBJ_PIN):
585 case (OBJ_BUS):
586 o_move_check_endpoint (w_current, object);
587 break;
589 case (OBJ_COMPLEX):
590 case (OBJ_PLACEHOLDER):
591 for (iter = object->complex->prim_objs;
592 iter != NULL; iter = g_list_next (iter)) {
593 o_current = iter->data;
595 if (o_current->type == OBJ_PIN) {
596 o_move_check_endpoint (w_current, o_current);
599 break;
604 /*! \todo Finish function documentation!!!
605 * \brief
606 * \par Function Description
609 int o_move_zero_length(OBJECT * object)
611 #if DEBUG
612 printf("x: %d %d y: %d %d\n",
613 object->line->x[0], object->line->x[1],
614 object->line->y[0], object->line->y[1]);
615 #endif
617 if (object->line->x[0] == object->line->x[1] &&
618 object->line->y[0] == object->line->y[1]) {
619 return TRUE;
620 } else {
621 return FALSE;
625 /*! \todo Finish function documentation!!!
626 * \brief
627 * \par Function Description
630 void o_move_end_rubberband (GSCHEM_TOPLEVEL *w_current,
631 int w_dx, int w_dy,
632 GList** objects)
634 TOPLEVEL *toplevel = w_current->toplevel;
635 GList *s_iter, *s_iter_next;
636 int dx1, dy1, dx2, dy2;
638 for (s_iter = w_current->stretch_list;
639 s_iter != NULL; s_iter = s_iter_next) {
640 STRETCH *s_current = s_iter->data;
641 OBJECT *object = s_current->object;
643 /* Store this now, since we may delete the current item */
644 s_iter_next = g_list_next (s_iter);
646 if (object->type == OBJ_NET ||
647 object->type == OBJ_BUS) {
649 /* remove the object's connections */
650 s_conn_remove_object (toplevel, object);
652 s_stretch_calc_deltas (w_current, s_current,
653 w_dx, w_dy, &dx1, &dy1, &dx2, &dy2);
654 object->line->x[0] += dx1; object->line->y[0] += dy1;
655 object->line->x[1] += dx2; object->line->y[1] += dy2;
657 if (o_move_zero_length (object)) {
658 w_current->stretch_list =
659 s_stretch_remove_object (w_current->stretch_list, object);
660 o_delete (w_current, object);
661 continue;
664 o_recalc_single_object (toplevel, object);
665 s_tile_update_object (toplevel, object);
666 s_conn_update_object (toplevel, object);
667 *objects = g_list_append (*objects, object);