1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2010 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
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_rects (GSCHEM_TOPLEVEL
*w_current
,
41 GdkRectangle
*rectangles
, int n_rectangles
)
43 TOPLEVEL
*toplevel
= w_current
->toplevel
;
44 gboolean draw_selected
;
53 for (i
= 0; i
< n_rectangles
; i
++) {
54 x_repaint_background_region (w_current
, rectangles
[i
].x
, rectangles
[i
].y
,
55 rectangles
[i
].width
, rectangles
[i
].height
);
58 g_return_if_fail (toplevel
!= NULL
);
59 g_return_if_fail (toplevel
->page_current
!= NULL
);
61 grip_half_size
= o_grips_size (w_current
);
62 cue_half_size
= SCREENabs (w_current
, CUE_BOX_SIZE
);
63 bloat
= MAX (grip_half_size
, cue_half_size
);
65 world_rects
= g_new (BOX
, n_rectangles
);
67 for (i
= 0; i
< n_rectangles
; i
++) {
68 int x
, y
, width
, height
;
72 width
= rectangles
[i
].width
;
73 height
= rectangles
[i
].height
;
75 SCREENtoWORLD (w_current
, x
- bloat
, y
+ height
+ bloat
,
76 &world_rects
[i
].lower_x
, &world_rects
[i
].lower_y
);
77 SCREENtoWORLD (w_current
, x
+ width
+ bloat
, y
- bloat
,
78 &world_rects
[i
].upper_x
, &world_rects
[i
].upper_y
);
81 obj_list
= s_page_objects_in_regions (toplevel
, toplevel
->page_current
,
82 world_rects
, n_rectangles
);
85 draw_selected
= !(w_current
->inside_action
&&
86 ((w_current
->event_state
== MOVE
) ||
87 (w_current
->event_state
== ENDMOVE
)));
89 for (iter
= obj_list
; iter
!= NULL
; iter
= g_list_next (iter
)) {
90 OBJECT
*o_current
= iter
->data
;
92 if (o_current
->dont_redraw
||
93 (!draw_selected
&& o_current
->selected
))
96 o_redraw_single (w_current
, o_current
);
99 o_cue_redraw_all (w_current
, obj_list
, draw_selected
);
101 if (w_current
->inside_action
) {
102 /* Redraw the rubberband objects (if they were previously visible) */
103 switch (w_current
->event_state
) {
106 if (w_current
->last_drawb_mode
!= -1) {
107 o_move_draw_rubber (w_current
, TRUE
);
116 if (w_current
->rubber_visible
)
117 o_place_draw_rubber (w_current
, TRUE
);
123 if (w_current
->rubber_visible
)
124 o_net_draw_rubber (w_current
);
130 if (w_current
->rubber_visible
)
131 o_bus_draw_rubber(w_current
);
135 if (w_current
->rubber_visible
)
136 o_grips_draw_rubber (w_current
);
140 if (w_current
->rubber_visible
)
141 o_select_box_draw_rubber (w_current
);
145 if (w_current
->rubber_visible
)
146 a_zoom_box_draw_rubber (w_current
);
150 if (w_current
->rubber_visible
)
151 o_line_draw_rubber (w_current
);
155 if (w_current
->rubber_visible
)
156 o_box_draw_rubber (w_current
);
160 if (w_current
->rubber_visible
)
161 o_picture_draw_rubber (w_current
);
165 if (w_current
->rubber_visible
)
166 o_circle_draw_rubber (w_current
);
170 if (w_current
->rubber_visible
)
171 o_arc_draw_rubber (w_current
);
175 if (w_current
->rubber_visible
)
176 o_pin_draw_rubber (w_current
);
181 g_list_free (obj_list
);
185 /*! \todo Finish function documentation!!!
187 * \par Function Description
190 void o_redraw (GSCHEM_TOPLEVEL
*w_current
, GList
*object_list
, gboolean draw_selected
)
195 for (iter
= object_list
; iter
!= NULL
; iter
= g_list_next (iter
)) {
196 o_current
= (OBJECT
*)iter
->data
;
198 if (o_current
->dont_redraw
||
199 (!draw_selected
&& o_current
->selected
))
202 o_redraw_single (w_current
, o_current
);
206 /*! \brief Redraw an object on the screen.
207 * \par Function Description
208 * This function will redraw a single object on the screen.
210 * \param [in] w_current The GSCHEM_TOPLEVEL object.
211 * \param [in] o_current The OBJECT to redraw.
214 void o_redraw_single(GSCHEM_TOPLEVEL
*w_current
, OBJECT
*o_current
)
216 void (*func
) (GSCHEM_TOPLEVEL
*, OBJECT
*) = NULL
;
218 if (o_current
== NULL
)
221 switch (o_current
->type
) {
222 case OBJ_LINE
: func
= o_line_draw
; break;
223 case OBJ_NET
: func
= o_net_draw
; break;
224 case OBJ_BUS
: func
= o_bus_draw
; break;
225 case OBJ_BOX
: func
= o_box_draw
; break;
226 case OBJ_PICTURE
: func
= o_picture_draw
; break;
227 case OBJ_CIRCLE
: func
= o_circle_draw
; break;
228 case OBJ_PLACEHOLDER
:
229 case OBJ_COMPLEX
: func
= o_complex_draw
; break;
230 case OBJ_TEXT
: func
= o_text_draw
; break;
231 case OBJ_PATH
: func
= o_path_draw
; break;
232 case OBJ_PIN
: func
= o_pin_draw
; break;
233 case OBJ_ARC
: func
= o_arc_draw
; break;
235 g_critical ("o_redraw_single: object %p has bad type '%c'\n",
236 o_current
, o_current
->type
);
240 (*func
) (w_current
, o_current
);
244 /*! \todo Finish function documentation!!!
246 * \par Function Description
249 int o_invalidate_rubber (GSCHEM_TOPLEVEL
*w_current
)
251 /* return FALSE if it did not erase anything */
253 if (!w_current
->inside_action
)
256 switch(w_current
->event_state
) {
261 o_bus_invalidate_rubber (w_current
);
267 o_net_invalidate_rubber (w_current
);
272 o_pin_invalidate_rubber (w_current
);
277 o_line_invalidate_rubber (w_current
);
282 o_box_invalidate_rubber (w_current
);
287 o_picture_invalidate_rubber (w_current
);
292 o_circle_invalidate_rubber (w_current
);
297 o_arc_invalidate_rubber (w_current
);
309 /*! \todo Finish function documentation!!!
311 * \par Function Description
312 * This function is neccesary to make jumps between event_states.
313 * If we are inside an drawing action that created something on the dc,
314 * e.g. if we are drawing a box and then jump to line drawing without
315 * leaving the box drawing mode, there will remain some rubberbands on the
317 * Usually a intermediate select state would clean (redraw) the screen.
319 int o_redraw_cleanstates(GSCHEM_TOPLEVEL
*w_current
)
321 TOPLEVEL
*toplevel
= w_current
->toplevel
;
322 /* returns FALSE if the function was'nt nessecary */
323 if (w_current
->inside_action
== 0) {
327 switch (w_current
->event_state
) {
328 /* all states with something on the dc */
330 /* De-select the lists in the component selector */
331 x_compselect_deselect (w_current
);
352 /* it is possible to cancel in the middle of a place,
353 * so lets be sure to clean up the place_list structure */
355 /* If we're cancelling from a move action, re-wind the
356 * page contents back to their state before we started. */
357 if ((w_current
->event_state
== MOVE
) ||
358 (w_current
->event_state
== ENDMOVE
)) {
359 o_move_cancel (w_current
);
362 /* If we're cancelling from a grip action, call the specific cancel
363 * routine to reset the visibility of the object being modified */
364 if (w_current
->event_state
== GRIPS
)
365 o_grips_cancel (w_current
);
367 /* Free the place list and its contents. If we were in a move
368 * action, the list (refering to objects on the page) would
369 * already have been cleared in o_move_cancel(), so this is OK. */
370 s_delete_object_glist(toplevel
, toplevel
->page_current
->place_list
);
371 toplevel
->page_current
->place_list
= NULL
;
373 w_current
->inside_action
= 0;
375 /* touch the select state */
376 i_set_state(w_current
, SELECT
);
378 /* from i_callback_cancel() */
379 o_invalidate_all (w_current
);
382 /* all remaining states without dc changes */
417 /*! \todo Finish function documentation!!!
419 * \par Function Description
422 void o_draw_place (GSCHEM_TOPLEVEL
*w_current
, int dx
, int dy
, OBJECT
*object
)
424 void (*func
) (GSCHEM_TOPLEVEL
*, int, int, OBJECT
*) = NULL
;
426 switch (object
->type
) {
427 case OBJ_LINE
: func
= o_line_draw_place
; break;
428 case OBJ_NET
: func
= o_net_draw_place
; break;
429 case OBJ_BUS
: func
= o_bus_draw_place
; break;
430 case OBJ_BOX
: func
= o_box_draw_place
; break;
431 case OBJ_PICTURE
: func
= o_picture_draw_place
; break;
432 case OBJ_CIRCLE
: func
= o_circle_draw_place
; break;
433 case OBJ_PLACEHOLDER
:
434 case OBJ_COMPLEX
: func
= o_complex_draw_place
; break;
435 case OBJ_TEXT
: func
= o_text_draw_place
; break;
436 case OBJ_PATH
: func
= o_path_draw_place
; break;
437 case OBJ_PIN
: func
= o_pin_draw_place
; break;
438 case OBJ_ARC
: func
= o_arc_draw_place
; break;
440 g_assert_not_reached ();
444 (*func
) (w_current
, dx
, dy
, object
);
449 /*! \todo Finish function documentation!!!
451 * \par Function Description
454 void o_glist_draw_place (GSCHEM_TOPLEVEL
*w_current
, int dx
, int dy
, GList
*list
)
458 while (iter
!= NULL
) {
459 o_draw_place (w_current
, dx
, dy
, (OBJECT
*)iter
->data
);
460 iter
= g_list_next(iter
);
465 /*! \brief Invalidates a rectangular region of the on screen drawing area
466 * \par Function Description
468 * Given a pair of (x,y) coordinates in SCREEN units, invalidate the
469 * rectangular on-screen drawing area which has those two coordinate
470 * pairs as opposite corners of its region. This will cause that region
471 * to be blitted from the back-buffer once the mainloop reaches idle.
473 * A margin, INVALIDATE_MARGIN is added to the invalidated region as
474 * a hacky workaround for rounding errors which may occur in the
475 * WORLD -> SCREEN coordinate transform. This margin may also be used
476 * to expand the invalidated region if anti-aliased drawing is ever
479 * A further, larger margin is added to account for invalidating the
480 * size occupied by an object's grips.
482 * If the GSCHEM_TOPLEVEL in question is not rendering to a GDK_WINDOW,
483 * (e.g. image export), this function call is a no-op. A test is used:
484 * GDK_IS_WINDOW(), which should be safe since in either case,
485 * w_current->window is a GObject. This is really a _HACK_,
486 * and should be fixed with a re-worked drawing model.
488 * \param [in] w_current The GSCHEM_TOPLEVEL who's drawing area is being invalidated.
489 * \param [in] x1 X coord for corner 1 (SCREEN units)
490 * \param [in] y1 Y coord for corner 1 (SCREEN units)
491 * \param [in] x2 X coord for corner 2 (SCREEN units)
492 * \param [in] y2 Y coord for corner 2 (SCREEN units)
494 void o_invalidate_rect (GSCHEM_TOPLEVEL
*w_current
,
495 int x1
, int y1
, int x2
, int y2
)
502 /* BUG: We get called when rendering an image, and w_current->window
503 * is a GdkPixmap. Ensure we only invalidate GdkWindows. */
504 if (!GDK_IS_WINDOW( w_current
->window
))
507 grip_half_size
= o_grips_size (w_current
);
508 cue_half_size
= SCREENabs (w_current
, CUE_BOX_SIZE
);
509 bloat
= MAX (grip_half_size
, cue_half_size
) + INVALIDATE_MARGIN
;
511 rect
.x
= MIN(x1
, x2
) - bloat
;
512 rect
.y
= MIN(y1
, y2
) - bloat
;
513 rect
.width
= 1 + abs( x1
- x2
) + 2 * bloat
;
514 rect
.height
= 1 + abs( y1
- y2
) + 2 * bloat
;
515 gdk_window_invalidate_rect( w_current
->window
, &rect
, FALSE
);
519 /*! \brief Invalidate the whole on-screen area
521 * \par Function Description
522 * This function calls gdk_window_invalidate_rect() with a rect
523 * of NULL, causing the entire drawing area to be invalidated.
525 * \param [in] w_current The GSCHEM_TOPLEVEL object.
527 void o_invalidate_all (GSCHEM_TOPLEVEL
*w_current
)
529 gdk_window_invalidate_rect (w_current
->window
, NULL
, FALSE
);
533 /*! \brief Invalidate on-screen area for an object
535 * \par Function Description
536 * This function calls o_invalidate_rect() with the bounds of the
537 * passed OBJECT, converted to screen coordinates.
539 * \param [in] w_current The GSCHEM_TOPLEVEL object.
540 * \param [in] object The OBJECT invalidated on screen.
542 void o_invalidate (GSCHEM_TOPLEVEL
*w_current
, OBJECT
*object
)
544 TOPLEVEL
*toplevel
= w_current
->toplevel
;
545 int left
, top
, bottom
, right
;
546 int s_left
, s_top
, s_bottom
, s_right
;
547 if (world_get_single_object_bounds(toplevel
, object
, &left
, &top
,
549 WORLDtoSCREEN (w_current
, left
, top
, &s_left
, &s_top
);
550 WORLDtoSCREEN (w_current
, right
, bottom
, &s_right
, &s_bottom
);
551 o_invalidate_rect (w_current
, s_left
, s_top
, s_right
, s_bottom
);
556 /*! \brief Invalidate on-screen area for a GList of objects
558 * \par Function Description
559 * This function calls o_invalidate_rect() with the bounds of the
560 * passed GList, converted to screen coordinates.
562 * \param [in] w_current The GSCHEM_TOPLEVEL object.
563 * \param [in] list The glist objects invalidated on screen.
565 void o_invalidate_glist (GSCHEM_TOPLEVEL
*w_current
, GList
*list
)
567 TOPLEVEL
*toplevel
= w_current
->toplevel
;
568 int left
, top
, bottom
, right
;
569 int s_left
, s_top
, s_bottom
, s_right
;
570 if (world_get_object_glist_bounds (toplevel
, list
, &left
, &top
,
572 WORLDtoSCREEN (w_current
, left
, top
, &s_left
, &s_top
);
573 WORLDtoSCREEN (w_current
, right
, bottom
, &s_right
, &s_bottom
);
574 o_invalidate_rect (w_current
, s_left
, s_top
, s_right
, s_bottom
);
579 /*! \brief Returns the color an object should be drawn in
581 * \par Function Description
582 * This function looks up and returns either the SELECT_COLOR, or the
583 * OBJECT's natural colour, as appropriate. If toplevel->override_color
584 * is set, that takes precedence.
586 * The parent field of the OBJECT structure is used to recurse down
587 * and check whether the OBJECT being drawn is a prim_obj belonging
588 * to some selected OBJECT. If so, SELECT_COLOR is used.
590 * As a convenience, the appropriate color index is looked up using
591 * x_color_lookup(), so that code is not duplicated in each drawing
594 * \param [in] w_current The GSCHEM_TOPLEVEL object.
595 * \param [in] object The OBJECT whos color to return.
597 COLOR
*o_drawing_color (GSCHEM_TOPLEVEL
*w_current
, OBJECT
*object
)
602 color_idx
= object
->color
;
604 if (object
->selected
)
605 color_idx
= SELECT_COLOR
;
607 /* Check if the object, or its parent(s) are selected */
608 for (temp
= object
; temp
!= NULL
; temp
= temp
->parent
) {
609 if (temp
->selected
) {
610 color_idx
= SELECT_COLOR
;
615 if (w_current
->toplevel
->override_color
!= -1)
616 color_idx
= w_current
->toplevel
->override_color
;
618 return x_color_lookup (color_idx
);