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
26 #ifdef HAVE_LIBDMALLOC
30 /*! \todo Finish function documentation!!!
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
;
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!!!
79 * \par Function Description
82 static void o_move_end_lowlevel_glist (GSCHEM_TOPLEVEL
*w_current
,
84 int diff_x
, int diff_y
)
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!!!
100 * \par Function Description
103 void o_move_end_lowlevel (GSCHEM_TOPLEVEL
*w_current
,
105 int diff_x
, int diff_y
)
107 TOPLEVEL
*toplevel
= w_current
->toplevel
;
109 switch (object
->type
) {
114 s_conn_remove_object (toplevel
, object
);
115 o_translate_world (toplevel
, diff_x
, diff_y
, object
);
116 s_conn_update_object (toplevel
, object
);
120 o_translate_world (toplevel
, diff_x
, diff_y
, object
);
125 /*! \todo Finish function documentation!!!
127 * \par Function Description
130 void o_move_end(GSCHEM_TOPLEVEL
*w_current
)
132 TOPLEVEL
*toplevel
= w_current
->toplevel
;
133 GList
*s_current
= NULL
;
136 int left
, top
, right
, bottom
;
138 GList
*rubbernet_objects
= NULL
;
140 object
= o_select_return_first_object(w_current
);
143 /* actually this is an error condition hack */
144 w_current
->inside_action
= 0;
145 i_set_state(w_current
, SELECT
);
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"));
179 switch (object
->type
) {
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
,
195 world_get_object_glist_bounds (toplevel
, object
->complex->prim_objs
,
196 &left
, &top
, &right
, &bottom
);
198 object
->w_left
= left
;
200 object
->w_right
= right
;
201 object
->w_bottom
= bottom
;
206 o_move_end_lowlevel (w_current
, object
, diff_x
, diff_y
);
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
225 GList
*moved_list
= g_list_concat (toplevel
->page_current
->place_list
,
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!!!
242 * \par Function Description
245 void o_move_cancel (GSCHEM_TOPLEVEL
*w_current
)
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!!!
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
;
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
;
292 s_current
= g_list_next(s_current
)) {
293 if (((OBJECT
*) s_current
->data
)->attached_to
== NULL
) {
294 object
= (OBJECT
*) s_current
->data
;
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
)) {
312 } else { /* single object */
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 */
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!!!
336 * \par Function Description
339 void o_move_invalidate_rubber (GSCHEM_TOPLEVEL
*w_current
, int drawing
)
343 int dx1
, dx2
, dy1
, dy2
;
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
) {
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!!!
377 * \par Function Description
380 void o_move_draw_rubber (GSCHEM_TOPLEVEL
*w_current
, int drawing
)
384 int dx1
, dy1
, dx2
, dy2
;
386 o_place_draw_rubber (w_current
, drawing
);
388 if (!w_current
->netconn_rubberband
)
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
) {
404 o_net_draw_stretch (w_current
, object
, dx1
, dy1
, dx2
, dy2
);
408 o_bus_draw_stretch (w_current
, object
, dx1
, dy1
, dx2
, dy2
);
415 /*! \todo Finish function documentation!!!
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
) {
426 if (object
->line
->x
[1] == x
&& object
->line
->y
[1] == y
) {
431 _("DOH! tried to find the whichone, but didn't find it!\n"));
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!!!
453 * \par Function Description
456 void o_move_check_endpoint(GSCHEM_TOPLEVEL
*w_current
, OBJECT
* object
)
458 TOPLEVEL
*toplevel
= w_current
->toplevel
;
463 GList
*new_nets
= NULL
;
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"));
474 for (cl_current
= object
->conn_list
;
476 cl_current
= g_list_next(cl_current
)) {
478 c_current
= (CONN
*) cl_current
->data
;
479 other
= c_current
->other_object
;
484 /* really make sure that the object is not selected */
488 /* Catch pins, whos parent object is selected. */
489 if (other
->parent
!= NULL
&& other
->parent
->selected
)
492 if (c_current
->type
!= CONN_ENDPOINT
&&
493 (c_current
->type
!= CONN_MIDPOINT
||
494 c_current
->other_whichone
== -1))
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 */
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
)
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
);
549 whichone
= o_move_return_whichone (other
, c_current
->x
, c_current
->y
);
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!!!
564 * \par Function Description
567 void o_move_prep_rubberband(GSCHEM_TOPLEVEL
*w_current
)
569 TOPLEVEL
*toplevel
= w_current
->toplevel
;
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
;
582 switch (object
->type
) {
586 o_move_check_endpoint (w_current
, object
);
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
);
604 /*! \todo Finish function documentation!!!
606 * \par Function Description
609 int o_move_zero_length(OBJECT
* object
)
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]);
617 if (object
->line
->x
[0] == object
->line
->x
[1] &&
618 object
->line
->y
[0] == object
->line
->y
[1]) {
625 /*! \todo Finish function documentation!!!
627 * \par Function Description
630 void o_move_end_rubberband (GSCHEM_TOPLEVEL
*w_current
,
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
);
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
);