1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2007 Ales Hvezda
4 * Copyright (C) 1998-2007 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., 59 Temple Place, Suite 330, Boston, MA 02111 USA
25 #ifdef HAVE_LIBDMALLOC
29 #define INVALIDATE_MARGIN 1
31 /*! \todo Lots of Gross code... needs lots of cleanup - mainly
35 /*! \todo Finish function documentation!!!
37 * \par Function Description
40 void o_redraw_all(GSCHEM_TOPLEVEL
*w_current
)
42 TOPLEVEL
*toplevel
= w_current
->toplevel
;
43 gboolean draw_selected
= TRUE
;
45 if (!toplevel
->DONT_REDRAW
) {
46 x_repaint_background(w_current
);
49 draw_selected
= !(w_current
->inside_action
&&
50 ((w_current
->event_state
== MOVE
) ||
51 (w_current
->event_state
== ENDMOVE
) ||
52 (w_current
->event_state
== GRIPS
)));
53 o_redraw(w_current
, toplevel
->page_current
->object_head
, draw_selected
);
54 o_cue_redraw_all(w_current
,
55 toplevel
->page_current
->object_head
, draw_selected
);
57 if (w_current
->inside_action
) {
58 switch(w_current
->event_state
) {
61 o_move_rubbermove_xor (w_current
, TRUE
);
69 o_place_rubberplace_xor (w_current
, TRUE
);
75 w_current
->magnetic_visible
=0;
78 w_current
->rubber_visible
=0;
83 /*! \todo Finish function documentation!!!
85 * \par Function Description
88 void o_redraw(GSCHEM_TOPLEVEL
*w_current
, OBJECT
*list
, gboolean draw_selected
)
90 TOPLEVEL
*toplevel
= w_current
->toplevel
;
91 OBJECT
*o_current
= list
;
92 int redraw_state
= toplevel
->DONT_REDRAW
;
94 w_current
->inside_redraw
= 1;
95 while (o_current
!= NULL
) {
96 if ((o_current
->draw_func
!= NULL
) &&
97 (o_current
->type
!= OBJ_HEAD
)) {
98 toplevel
->DONT_REDRAW
= redraw_state
||
99 o_current
->dont_redraw
||
100 (!draw_selected
&& o_current
->selected
);
101 (*o_current
->draw_func
)(w_current
, o_current
);
104 o_grips_redraw_single(w_current
, o_current
);
106 o_current
= o_current
->next
;
108 w_current
->inside_redraw
= 0;
109 toplevel
->DONT_REDRAW
= redraw_state
;
111 o_invalidate_list (w_current
, list
);
114 /*! \brief Redraw an object on the screen.
115 * \par Function Description
116 * This function will redraw a single object on the screen.
118 * \param [in] w_current The GSCHEM_TOPLEVEL object.
119 * \param [in] o_current The OBJECT to redraw.
122 void o_redraw_single(GSCHEM_TOPLEVEL
*w_current
, OBJECT
*o_current
)
124 TOPLEVEL
*toplevel
= w_current
->toplevel
;
126 if (o_current
== NULL
)
129 if (toplevel
->DONT_REDRAW
) /* highly experimental */
132 if (o_current
->draw_func
!= NULL
&& o_current
->type
!= OBJ_HEAD
) {
133 w_current
->inside_redraw
= 1;
134 (*o_current
->draw_func
)(w_current
, o_current
);
135 w_current
->inside_redraw
= 0;
138 o_grips_redraw_single(w_current
, o_current
);
140 o_invalidate (w_current
, o_current
);
143 /*! \todo Finish function documentation!!!
145 * \par Function Description
148 void o_draw_list(GSCHEM_TOPLEVEL
*w_current
, GList
const *list
)
151 GList
const *l_current
;
153 if (w_current
->inside_redraw
) {
158 while (l_current
!= NULL
) {
160 o_current
= l_current
->data
;
163 o_redraw_single(w_current
, o_current
);
166 l_current
= g_list_next(l_current
);
170 /*! \todo Finish function documentation!!!
172 * \par Function Description
175 void o_draw_selected(GSCHEM_TOPLEVEL
*w_current
)
177 TOPLEVEL
*toplevel
= w_current
->toplevel
;
180 if (w_current
->inside_redraw
) {
184 s_current
= geda_list_get_glist( toplevel
->page_current
->selection_list
);
185 while (s_current
!= NULL
) {
186 o_current
= s_current
->data
;
189 o_redraw_single(w_current
, o_current
);
190 o_cue_draw_single(w_current
, o_current
);
192 s_current
=g_list_next(s_current
);
197 /*! \todo Finish function documentation!!!
199 * \par Function Description
202 void o_erase_selected(GSCHEM_TOPLEVEL
*w_current
)
204 TOPLEVEL
*toplevel
= w_current
->toplevel
;
209 if (w_current
->inside_redraw
) {
213 list
= iter
= geda_list_get_glist( toplevel
->page_current
->selection_list
);
214 while (iter
!= NULL
) {
215 o_current
= iter
->data
;
218 o_cue_erase_single(w_current
, o_current
);
219 o_erase_single(w_current
, o_current
);
222 iter
= g_list_next( iter
);
225 o_invalidate_glist (w_current
, list
);
228 /*! \brief Erase a given OBJECT
230 * \par Function Description
231 * This function erases the passed OBJECT, <B>object</B>.
233 * It makes a call to object's draw function after having set a
234 * color override to the background color. The object is drawn in
235 * the background color, causing it to disappear.
237 * \bug No redrawing is done of occluded objects, including the grid.
239 * \param [in] w_current The GSCHEM_TOPLEVEL object.
240 * \param [in] o_current Circle OBJECT to erase.
242 void o_erase_single(GSCHEM_TOPLEVEL
*w_current
, OBJECT
*object
)
244 TOPLEVEL
*toplevel
= w_current
->toplevel
;
247 if (w_current
->inside_redraw
) {
253 toplevel
->override_color
= toplevel
->background_color
;
254 if (o_current
!= NULL
) {
255 if (o_current
->draw_func
&&
256 o_current
->type
!= OBJ_HEAD
) {
257 (*o_current
->draw_func
)(w_current
, o_current
);
260 o_grips_redraw_single(w_current
, o_current
);
261 toplevel
->override_color
= -1;
263 o_invalidate (w_current
, o_current
);
266 /*! \todo Finish function documentation!!!
268 * \par Function Description
271 void o_erase_list(GSCHEM_TOPLEVEL
*w_current
, GList
const *list
)
276 if (w_current
->inside_redraw
) {
281 while (iter
!= NULL
) {
282 o_current
= iter
->data
;
283 o_erase_single(w_current
, o_current
);
284 iter
= g_list_next(iter
);
288 /*! \brief XOR draw a bounding box or outline for OBJECT placement
290 * \par Function Description
291 * This function XOR draws either the OBJECTS in the passed GList,
292 * or a rectangle around their bounding box, depending upon the
293 * currently selected w_current->actionfeedback_mode. This takes the
294 * value BOUNDINGBOX or OUTLINE.
296 * The function applies manhattan mode constraints to the coordinates
297 * before drawing if the CONTROL key is recording as being pressed in
298 * the w_current structure.
300 * The "drawing" parameter is used to indicate if this drawing should
301 * immediately use the selected feedback mode and positioning constraints.
303 * With drawing=TRUE, the selected conditions are used immediately,
304 * otherwise the conditions from the last drawing operation are used,
305 * saving the new state for next time.
307 * o_drawbounding() should be called with drawing=TRUE when starting a
308 * rubberbanding operation and when otherwise refreshing the rubberbanded
309 * outline (e.g. after a screen redraw). For any undraw operation, should
310 * be called with drawing=FALSE, ensuring that the undraw XOR matches the
311 * mode and constraints of the corresponding "draw" operation.
313 * If any mode / constraint changes are made between a undraw, redraw XOR
314 * pair, the latter (draw) operation must be called with drawing=TRUE. If
315 * no mode / constraint changes were made between the pair, it is not
316 * harmful to call the draw operation with "drawing=FALSE".
318 * \param [in] w_current GSCHEM_TOPLEVEL which we're drawing for.
319 * \param [in] o_glst GList of objects to XOR draw.
320 * \param [in] color GdkColor used for drawing in BOUNDINGBOX mode.
321 * \param [in] drawing Set to FALSE for undraw operations to ensure
322 * matching conditions to a previous draw operation.
324 void o_drawbounding(GSCHEM_TOPLEVEL
*w_current
, GList
const *o_glist
,
325 GdkColor
*color
, int drawing
)
327 TOPLEVEL
*toplevel
= w_current
->toplevel
;
328 PAGE
*page
= toplevel
->page_current
;
330 int left
, top
, bottom
, right
;
331 int s_left
, s_top
, s_bottom
, s_right
;
333 g_return_if_fail (o_glist
!= NULL
);
335 /* If drawing is true, then don't worry about the previous drawing
336 * method and movement constraints, use with the current settings */
338 w_current
->last_drawb_mode
= w_current
->actionfeedback_mode
;
339 w_current
->drawbounding_action_mode
= (w_current
->CONTROLKEY
)
340 ? CONSTRAINED
: FREE
;
343 /* Calculate delta of X-Y positions from buffer's origin */
344 diff_x
= w_current
->second_wx
- w_current
->first_wx
;
345 diff_y
= w_current
->second_wy
- w_current
->first_wy
;
347 /* Adjust the coordinates according to the movement constraints */
348 if (w_current
->drawbounding_action_mode
== CONSTRAINED
) {
349 if (abs(diff_x
) >= abs(diff_y
)) {
350 w_current
->second_wy
= w_current
->first_wy
;
353 w_current
->second_wx
= w_current
->first_wx
;
358 /* Find the bounds of the drawing to be done */
359 world_get_object_glist_bounds(o_glist
, &left
, &top
, &right
, &bottom
);
360 WORLDtoSCREEN(page
, left
+ diff_x
, top
+ diff_y
, &s_left
, &s_top
);
361 WORLDtoSCREEN(page
, right
+ diff_x
, bottom
+ diff_y
, &s_right
, &s_bottom
);
363 /* XOR draw with the appropriate mode */
364 if (w_current
->last_drawb_mode
== BOUNDINGBOX
) {
365 gdk_gc_set_foreground(w_current
->bounding_xor_gc
, color
);
366 gdk_draw_rectangle(w_current
->backingstore
,
367 w_current
->bounding_xor_gc
, FALSE
,
369 s_right
- s_left
, s_top
- s_bottom
);
371 o_glist_draw_xor (w_current
, diff_x
, diff_y
, o_glist
);
374 /* Invalidate the screen buffer where we drew */
375 o_invalidate_rect(w_current
, s_left
, s_top
,
378 /* Save movement constraints and drawing method for any
379 * corresponding undraw operation. */
380 w_current
->last_drawb_mode
= w_current
->actionfeedback_mode
;
381 w_current
->drawbounding_action_mode
= (w_current
->CONTROLKEY
)
382 ? CONSTRAINED
: FREE
;
386 /*! \todo Finish function documentation!!!
388 * \par Function Description
391 int o_erase_rubber(GSCHEM_TOPLEVEL
*w_current
)
393 /* return FALSE if it did not erase anything */
395 if (!w_current
->inside_action
)
398 switch(w_current
->event_state
) {
403 o_bus_eraserubber(w_current
);
409 o_net_eraserubber(w_current
);
414 o_pin_eraserubber(w_current
);
419 o_line_eraserubber(w_current
);
424 o_box_eraserubber(w_current
);
429 o_picture_eraserubber(w_current
);
434 o_circle_eraserubber(w_current
);
439 o_arc_eraserubber(w_current
);
451 /*! \todo Finish function documentation!!!
453 * \par Function Description
454 * This function is necessary to make jumps between event_states.
455 * If we are inside an drawing action that created something on the dc,
456 * e.g. if we are drawing a box and then jump to line drawing without
457 * leaving the box drawing mode, there will remain some rubberbands on the
459 * Usually a intermediate select state would clean (redraw) the screen.
461 int o_redraw_cleanstates(GSCHEM_TOPLEVEL
*w_current
)
463 TOPLEVEL
*toplevel
= w_current
->toplevel
;
464 /* returns FALSE if the function wasn't necessary */
465 if (w_current
->inside_action
== 0) {
469 switch (w_current
->event_state
) {
470 /* all states with something on the dc */
473 /* De-select the lists in the component selector */
474 x_compselect_deselect (w_current
);
495 /* it is possible to cancel in the middle of a place,
496 * so lets be sure to clean up the place_list structure */
498 /* If we're cancelling from a move action, re-wind the
499 * page contents back to their state before we started. */
500 if ((w_current
->event_state
== MOVE
) ||
501 (w_current
->event_state
== ENDMOVE
)) {
502 o_move_cancel (w_current
);
505 /* Free the place list and its contents. If we were in a move
506 * action, the list (referring to objects on the page) would
507 * already have been cleared in o_move_cancel(), so this is OK. */
508 s_page_replace_place_list(toplevel
, toplevel
->page_current
, NULL
);
510 w_current
->inside_action
= 0;
512 /* touch the select state */
513 i_set_state(w_current
, SELECT
);
515 /* from i_callback_cancel() */
516 o_redraw_all(w_current
);
519 /* all remaining states without dc changes */
555 /*! \todo Finish function documentation!!!
557 * \par Function Description
560 void o_draw_xor(GSCHEM_TOPLEVEL
*w_current
, int dx
, int dy
, OBJECT
*object
)
562 void (*func
) (GSCHEM_TOPLEVEL
*, int, int, OBJECT
*) = NULL
;
564 switch (object
->type
) {
565 case OBJ_HEAD
: /* Do nothing for head nodes */ break;
566 case OBJ_SLOT
: /* TODO: Give slots a graphical existence. */ break;
567 case OBJ_LINE
: func
= o_line_draw_xor
; break;
568 case OBJ_NET
: func
= o_net_draw_xor
; break;
569 case OBJ_BUS
: func
= o_bus_draw_xor
; break;
570 case OBJ_BOX
: func
= o_box_draw_xor
; break;
571 case OBJ_PICTURE
: func
= o_picture_draw_xor
; break;
572 case OBJ_CIRCLE
: func
= o_circle_draw_xor
; break;
573 case OBJ_PLACEHOLDER
:
574 case OBJ_COMPLEX
: func
= o_complex_draw_xor
; break;
575 case OBJ_TEXT
: func
= o_text_draw_xor
; break;
576 case OBJ_PATH
: func
= o_path_draw_xor
; break;
577 case OBJ_PIN
: func
= o_pin_draw_xor
; break;
578 case OBJ_ARC
: func
= o_arc_draw_xor
; break;
580 g_assert_not_reached ();
584 (*func
) (w_current
, dx
, dy
, object
);
589 /*! \todo Finish function documentation!!!
591 * \par Function Description
594 void o_list_draw_xor(GSCHEM_TOPLEVEL
*w_current
, int dx
, int dy
, OBJECT
*list
)
596 OBJECT
*o_current
= list
;
598 while(o_current
!= NULL
) {
599 o_draw_xor(w_current
, dx
, dy
, o_current
);
600 o_current
= o_current
->next
;
605 /*! \todo Finish function documentation!!!
607 * \par Function Description
610 void o_glist_draw_xor(GSCHEM_TOPLEVEL
*w_current
, int dx
, int dy
, GList
const *list
)
612 GList
const *iter
= list
;
614 while (iter
!= NULL
) {
615 o_draw_xor(w_current
, dx
, dy
, iter
->data
);
616 iter
= g_list_next(iter
);
620 /*! \brief Draw drawing subjects from GSCHEM_TOPLEVEL object.
621 * \par Function Description
622 * This function draws the current which_object object in the
623 * GSCHEM_TOPLEVEL structure <B>*w_current</B>.
625 * The objects are drawn with a xor-function over the current sheet with the
628 * \param [in] w_current The GSCHEM_TOPLEVEL object.
630 void o_draw_rubbersubject_xor(GSCHEM_TOPLEVEL
*w_current
)
634 /* draw the object from the previous variables */
635 gdk_gc_set_foreground(w_current
->xor_gc
,
636 x_get_darkcolor(w_current
->select_color
));
637 gdk_gc_set_line_attributes(w_current
->xor_gc
, 0,
638 GDK_LINE_SOLID
, GDK_CAP_NOT_LAST
,
641 saved_gc
= w_current
->gc
;
642 w_current
->gc
= w_current
->xor_gc
;
643 o_redraw_single(w_current
, w_current
->which_object
);
644 w_current
->gc
= saved_gc
;
647 /*! \brief Invalidates a rectangular region of the on screen drawing area
648 * \par Function Description
650 * Given a pair of (x,y) coordinates in SCREEN units, invalidate the
651 * rectangular on-screen drawing area which has those two coordinate
652 * pairs as opposite corners of its region. This will cause that region
653 * to be blitted from the back-buffer once the mainloop reaches idle.
655 * A margin, INVALIDATE_MARGIN is added to the invalidated region as
656 * a hacky workaround for rounding errors which may occur in the
657 * WORLD -> SCREEN coordinate transform. This margin may also be used
658 * to expand the invalidated region if anti-aliased drawing is ever
661 * If the GSCHEM_TOPLEVEL in question is not rendering to a GDK_WINDOW,
662 * (e.g. image export), this function call is a no-op. A test is used:
663 * GDK_IS_WINDOW(), which should be safe since in either case,
664 * w_current->window is a GObject. This is really a _HACK_,
665 * and should be fixed with a re-worked drawing model.
667 * \param [in] w_current The GSCHEM_TOPLEVEL whose drawing area is being invalidated.
668 * \param [in] x1 X coord for corner 1 (SCREEN units)
669 * \param [in] y1 Y coord for corner 1 (SCREEN units)
670 * \param [in] x2 X coord for corner 2 (SCREEN units)
671 * \param [in] y2 Y coord for corner 2 (SCREEN units)
673 void o_invalidate_rect( GSCHEM_TOPLEVEL
*w_current
,
674 int x1
, int y1
, int x2
, int y2
)
678 /* BUG: We get called when rendering an image, and w_current->window
679 * is a GdkPixmap. Ensure we only invalidate GdkWindows. */
680 if (!GDK_IS_WINDOW( w_current
->window
))
683 rect
.x
= MIN(x1
, x2
) - INVALIDATE_MARGIN
;
684 rect
.y
= MIN(y1
, y2
) - INVALIDATE_MARGIN
;
685 rect
.width
= 1 + abs( x1
- x2
) + 2 * INVALIDATE_MARGIN
;
686 rect
.height
= 1 + abs( y1
- y2
) + 2 * INVALIDATE_MARGIN
;
687 gdk_window_invalidate_rect( w_current
->window
, &rect
, FALSE
);
691 /*! \brief Invalidate the whole on-screen area
693 * \par Function Description
694 * This function calls gdk_window_invalidate_rect() with a rect
695 * of NULL, causing the entire drawing area to be invalidated.
697 * \param [in] w_current The GSCHEM_TOPLEVEL object.
698 * \param [in] object The OBJECT invalidated on screen.
700 void o_invalidate_all (GSCHEM_TOPLEVEL
*w_current
)
702 gdk_window_invalidate_rect (w_current
->window
, NULL
, FALSE
);
706 /*! \brief Invalidate on-screen area for an object
708 * \par Function Description
709 * This function calls o_invalidate_rect() with the bounds of the
710 * passed OBJECT, converted to screen coordinates.
712 * \param [in] w_current The GSCHEM_TOPLEVEL object.
713 * \param [in] object The OBJECT invalidated on screen.
715 void o_invalidate (GSCHEM_TOPLEVEL
*w_current
, OBJECT
*object
)
717 TOPLEVEL
*toplevel
= w_current
->toplevel
;
718 PAGE
*page
= toplevel
->page_current
;
719 int left
, top
, bottom
, right
;
720 int s_left
, s_top
, s_bottom
, s_right
;
721 if (world_get_single_object_bounds(object
, &left
, &top
,
723 WORLDtoSCREEN(page
, left
, top
, &s_left
, &s_top
);
724 WORLDtoSCREEN(page
, right
, bottom
, &s_right
, &s_bottom
);
725 o_invalidate_rect (w_current
, s_left
, s_top
, s_right
, s_bottom
);
730 /*! \brief Invalidate on-screen area for a list of objects
732 * \par Function Description
733 * This function calls o_invalidate_rect() with the bounds of the
734 * passed object list, converted to screen coordinates.
736 * \param [in] w_current The GSCHEM_TOPLEVEL object.
737 * \param [in] list The list objects invalidated on screen.
739 void o_invalidate_list (GSCHEM_TOPLEVEL
*w_current
, OBJECT
*list
)
741 TOPLEVEL
*toplevel
= w_current
->toplevel
;
742 PAGE
*page
= toplevel
->page_current
;
743 int left
, top
, bottom
, right
;
744 int s_left
, s_top
, s_bottom
, s_right
;
745 if (world_get_object_list_bounds(list
, &left
, &top
,
747 WORLDtoSCREEN(page
, left
, top
, &s_left
, &s_top
);
748 WORLDtoSCREEN(page
, right
, bottom
, &s_right
, &s_bottom
);
749 o_invalidate_rect (w_current
, s_left
, s_top
, s_right
, s_bottom
);
754 /*! \brief Invalidate on-screen area for a GList of objects
756 * \par Function Description
757 * This function calls o_invalidate_rect() with the bounds of the
758 * passed GList, converted to screen coordinates.
760 * \param [in] w_current The GSCHEM_TOPLEVEL object.
761 * \param [in] list The glist objects invalidated on screen.
763 void o_invalidate_glist (GSCHEM_TOPLEVEL
*w_current
, GList
*list
)
765 TOPLEVEL
*toplevel
= w_current
->toplevel
;
766 PAGE
*page
= toplevel
->page_current
;
767 int left
, top
, bottom
, right
;
768 int s_left
, s_top
, s_bottom
, s_right
;
769 if (world_get_object_glist_bounds(list
, &left
, &top
,
771 WORLDtoSCREEN(page
, left
, top
, &s_left
, &s_top
);
772 WORLDtoSCREEN(page
, right
, bottom
, &s_right
, &s_bottom
);
773 o_invalidate_rect (w_current
, s_left
, s_top
, s_right
, s_bottom
);