1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2020 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 #define INVALIDATE_MARGIN 1
27 extern COLOR display_colors
[MAX_COLORS
];
28 extern COLOR display_outline_colors
[MAX_COLORS
];
30 /*! \todo Lots of Gross code... needs lots of cleanup - mainly
34 /*! \todo Finish function documentation!!!
36 * \par Function Description
39 void o_redraw_rect (GschemToplevel
*w_current
,
40 GdkDrawable
*drawable
,
42 GschemPageGeometry
*geometry
,
43 GdkRectangle
*rectangle
)
45 TOPLEVEL
*toplevel
= gschem_toplevel_get_toplevel (w_current
);
46 gboolean draw_selected
;
54 EdaRenderer
*renderer
;
56 GArray
*render_color_map
= NULL
;
57 GArray
*render_outline_color_map
= NULL
;
60 g_return_if_fail (w_current
!= NULL
);
61 g_return_if_fail (toplevel
!= NULL
);
62 g_return_if_fail (w_current
->toplevel
== toplevel
);
63 g_return_if_fail (page
!= NULL
);
64 g_return_if_fail (geometry
!= NULL
);
66 cr
= gdk_cairo_create (drawable
);
68 gdk_cairo_rectangle (cr
, rectangle
);
72 cairo_set_matrix (cr
, gschem_page_geometry_get_world_to_screen_matrix (geometry
));
74 grip_half_size
= GRIP_SIZE
/ 2;
75 cue_half_size
= CUE_BOX_SIZE
;
76 cairo_user_to_device (cr
, &cue_half_size
, &dummy
);
77 bloat
= MAX (grip_half_size
, (int)cue_half_size
);
80 world_rect
= g_new (BOX
, 1);
82 double lower_x
= rectangle
->x
- bloat
;
83 double lower_y
= rectangle
->y
+ rectangle
->height
+ bloat
;
84 double upper_x
= rectangle
->x
+ rectangle
->width
+ bloat
;
85 double upper_y
= rectangle
->y
- bloat
;
87 cairo_device_to_user (cr
, &lower_x
, &lower_y
);
88 cairo_device_to_user (cr
, &upper_x
, &upper_y
);
90 world_rect
->lower_x
= floor (lower_x
);
91 world_rect
->lower_y
= floor (lower_y
);
92 world_rect
->upper_x
= ceil (upper_x
);
93 world_rect
->upper_y
= ceil (upper_y
);
95 obj_list
= s_page_objects_in_regions (toplevel
,
102 /* Set up renderer based on configuration in w_current */
103 render_flags
= EDA_RENDERER_FLAG_HINTING
;
104 if (toplevel
->show_hidden_text
)
105 render_flags
|= EDA_RENDERER_FLAG_TEXT_HIDDEN
;
106 if (w_current
->fast_mousepan
&&
107 gschem_toplevel_get_current_page_view(w_current
)->doing_pan
)
108 render_flags
|= (EDA_RENDERER_FLAG_TEXT_OUTLINE
109 | EDA_RENDERER_FLAG_PICTURE_OUTLINE
);
111 /* This color map is used for "normal" rendering. */
113 g_array_sized_new (FALSE
, FALSE
, sizeof(COLOR
), MAX_COLORS
);
115 g_array_append_vals (render_color_map
, display_colors
, MAX_COLORS
);
117 /* This color map is used for rendering rubberbanding nets and
118 buses, and objects which are in the process of being placed. */
119 render_outline_color_map
=
120 g_array_sized_new (FALSE
, FALSE
, sizeof(COLOR
), MAX_COLORS
);
121 render_outline_color_map
=
122 g_array_append_vals (render_outline_color_map
, display_outline_colors
,
125 /* Set up renderer */
126 renderer
= g_object_ref (w_current
->renderer
);
127 g_object_set (G_OBJECT (renderer
),
129 "grip-size", ((double) grip_half_size
* geometry
->to_world_x_constant
),
130 "render-flags", render_flags
,
131 "color-map", render_color_map
,
134 /* Paint background */
135 COLOR
*color
= x_color_lookup (BACKGROUND_COLOR
);
137 cairo_set_source_rgba (cr
,
145 /* Draw grid lines */
146 x_grid_draw_region (w_current
, cr
,
147 rectangle
->x
, rectangle
->y
,
148 rectangle
->width
, rectangle
->height
);
150 /* Determine whether we should draw the selection at all */
151 draw_selected
= !(w_current
->inside_action
&&
152 (w_current
->event_state
== MOVEMODE
));
154 /* First pass -- render non-selected objects */
155 for (iter
= obj_list
; iter
!= NULL
; iter
= g_list_next (iter
)) {
156 OBJECT
*o_current
= iter
->data
;
158 if (!(o_current
->dont_redraw
|| o_current
->selected
)) {
159 eda_renderer_draw (renderer
, o_current
);
163 /* Second pass -- render cues */
164 for (iter
= obj_list
; iter
!= NULL
; iter
= g_list_next (iter
)) {
165 OBJECT
*o_current
= iter
->data
;
167 if (!(o_current
->dont_redraw
|| o_current
->selected
)) {
168 eda_renderer_draw_cues (renderer
, o_current
);
172 /* Second pass -- render selected objects, cues & grips. This is
173 * done in a separate pass to non-selected items to make sure that
174 * the selection and grips are never obscured by other objects. */
176 g_object_set (G_OBJECT (renderer
),
177 "override-color", SELECT_COLOR
,
179 for (iter
= geda_list_get_glist (page
->selection_list
);
180 iter
!= NULL
; iter
= g_list_next (iter
)) {
181 OBJECT
*o_current
= iter
->data
;
182 if (!o_current
->dont_redraw
) {
183 eda_renderer_draw (renderer
, o_current
);
184 eda_renderer_draw_cues (renderer
, o_current
);
185 eda_renderer_draw_grips (renderer
, o_current
);
188 g_object_set (G_OBJECT (renderer
),
189 "override-color", -1,
193 if (w_current
->inside_action
) {
195 /* Redraw the rubberband objects (if they were previously visible) */
196 if (page
->place_list
!= NULL
) {
197 switch (w_current
->event_state
) {
203 if (w_current
->rubber_visible
) {
204 /* FIXME shouldn't need to save/restore colormap here */
206 eda_renderer_set_color_map (renderer
, render_outline_color_map
);
208 o_place_draw_rubber (w_current
, renderer
);
210 eda_renderer_set_color_map (renderer
, render_color_map
);
215 if (w_current
->last_drawb_mode
!= -1) {
216 /* FIXME shouldn't need to save/restore colormap here */
218 eda_renderer_set_color_map (renderer
, render_outline_color_map
);
220 o_move_draw_rubber (w_current
, renderer
);
222 eda_renderer_set_color_map (renderer
, render_color_map
);
230 if (w_current
->rubber_visible
) {
231 switch (w_current
->event_state
) {
232 case ARCMODE
: o_arc_draw_rubber (w_current
, renderer
); break;
233 case BOXMODE
: o_box_draw_rubber (w_current
, renderer
); break;
234 case CIRCLEMODE
: o_circle_draw_rubber (w_current
, renderer
); break;
235 case LINEMODE
: o_line_draw_rubber (w_current
, renderer
); break;
236 case PATHMODE
: o_path_draw_rubber (w_current
, renderer
); break;
237 case PICTUREMODE
: o_picture_draw_rubber (w_current
, renderer
); break;
238 case PINMODE
: o_pin_draw_rubber (w_current
, renderer
); break;
240 /* FIXME shouldn't need to save/restore colormap here */
242 eda_renderer_set_color_map (renderer
, render_outline_color_map
);
244 o_bus_draw_rubber(w_current
, renderer
);
246 eda_renderer_set_color_map (renderer
, render_color_map
);
250 /* FIXME shouldn't need to save/restore colormap here */
252 eda_renderer_set_color_map (renderer
, render_outline_color_map
);
254 o_net_draw_rubber (w_current
, renderer
);
256 eda_renderer_set_color_map (renderer
, render_color_map
);
259 case GRIPS
: o_grips_draw_rubber (w_current
, renderer
); break;
260 case SBOX
: o_select_box_draw_rubber (w_current
, renderer
); break;
261 case ZOOMBOX
: a_zoom_box_draw_rubber (w_current
, renderer
); break;
262 case OGNRSTMODE
: o_ognrst_draw_rubber (w_current
, renderer
,
263 rectangle
->x
, rectangle
->y
,
264 rectangle
->width
, rectangle
->height
); break;
270 g_list_free (obj_list
);
271 g_object_unref (G_OBJECT (renderer
));
272 g_array_free (render_color_map
, TRUE
);
273 g_array_free (render_outline_color_map
, TRUE
);
279 /*! \todo Finish function documentation!!!
281 * \par Function Description
284 int o_invalidate_rubber (GschemToplevel
*w_current
)
286 /* return FALSE if it did not erase anything */
288 if (!w_current
->inside_action
)
291 switch(w_current
->event_state
) {
293 case (ARCMODE
) : o_arc_invalidate_rubber (w_current
); break;
294 case (BOXMODE
) : o_box_invalidate_rubber (w_current
); break;
295 case (BUSMODE
) : o_bus_invalidate_rubber (w_current
); break;
296 case (CIRCLEMODE
) : o_circle_invalidate_rubber (w_current
); break;
297 case (LINEMODE
) : o_line_invalidate_rubber (w_current
); break;
298 case (NETMODE
) : o_net_invalidate_rubber (w_current
); break;
299 case (PATHMODE
) : o_path_invalidate_rubber (w_current
); break;
300 case (PICTUREMODE
): o_picture_invalidate_rubber (w_current
); break;
301 case (PINMODE
) : o_pin_invalidate_rubber (w_current
); break;
302 case (OGNRSTMODE
) : o_ognrst_invalidate_rubber (w_current
); break;
313 /*! \todo Finish function documentation!!!
315 * \par Function Description
316 * This function is neccesary to make jumps between event_states.
317 * If we are inside an drawing action that created something on the dc,
318 * e.g. if we are drawing a box and then jump to line drawing without
319 * leaving the box drawing mode, there will remain some rubberbands on the
321 * Usually a intermediate select state would clean (redraw) the screen.
323 int o_redraw_cleanstates(GschemToplevel
*w_current
)
325 TOPLEVEL
*toplevel
= gschem_toplevel_get_toplevel (w_current
);
326 /* returns FALSE if the function was'nt nessecary */
327 if (w_current
->inside_action
== 0) {
331 switch (w_current
->event_state
) {
332 /* all states with something on the dc */
334 /* De-select the lists in the component selector */
335 x_compselect_deselect (w_current
);
355 /* it is possible to cancel in the middle of a place,
356 * so lets be sure to clean up the place_list structure */
358 /* If we're cancelling from a move action, re-wind the
359 * page contents back to their state before we started. */
360 if (w_current
->event_state
== MOVEMODE
) {
361 o_move_cancel (w_current
);
364 /* If we're cancelling from a grip action, call the specific cancel
365 * routine to reset the visibility of the object being modified */
366 if (w_current
->event_state
== GRIPS
)
367 o_grips_cancel (w_current
);
369 /* Free the place list and its contents. If we were in a move
370 * action, the list (refering to objects on the page) would
371 * already have been cleared in o_move_cancel(), so this is OK. */
372 s_delete_object_glist(toplevel
, toplevel
->page_current
->place_list
);
373 toplevel
->page_current
->place_list
= NULL
;
375 i_action_stop (w_current
);
377 /* touch the select state */
378 i_set_state(w_current
, SELECT
);
380 /* from i_cancel() */
381 gschem_page_view_invalidate_all (gschem_toplevel_get_current_page_view (w_current
));
384 /* all remaining states without dc changes */
396 /*! \brief Invalidates a rectangular region of the on screen drawing area
397 * \par Function Description
399 * Given a pair of (x,y) coordinates in SCREEN units, invalidate the
400 * rectangular on-screen drawing area which has those two coordinate
401 * pairs as opposite corners of its region. This will cause that region
402 * to be blitted from the back-buffer once the mainloop reaches idle.
404 * A margin, INVALIDATE_MARGIN is added to the invalidated region as
405 * a hacky workaround for rounding errors which may occur in the
406 * WORLD -> SCREEN coordinate transform. This margin may also be used
407 * to expand the invalidated region if anti-aliased drawing is ever
410 * A further, larger margin is added to account for invalidating the
411 * size occupied by an object's grips.
413 * If the GschemToplevel in question is not rendering to a GDK_WINDOW,
414 * (e.g. image export), this function call is a no-op. A test is used:
415 * GDK_IS_WINDOW(), which should be safe since in either case,
416 * w_current->window is a GObject. This is really a _HACK_,
417 * and should be fixed with a re-worked drawing model.
419 * \param [in] w_current The GschemToplevel who's drawing area is being invalidated.
420 * \param [in] x1 X coord for corner 1 (SCREEN units)
421 * \param [in] y1 Y coord for corner 1 (SCREEN units)
422 * \param [in] x2 X coord for corner 2 (SCREEN units)
423 * \param [in] y2 Y coord for corner 2 (SCREEN units)
425 void o_invalidate_rect (GschemToplevel
*w_current
,
426 int x1
, int y1
, int x2
, int y2
)
428 GschemPageView
*page_view
= gschem_toplevel_get_current_page_view (w_current
);
430 gschem_page_view_invalidate_screen_rect (page_view
,
438 /*! \brief Invalidate on-screen area for an object
440 * \par Function Description
441 * This function calls o_invalidate_rect() with the bounds of the
442 * passed OBJECT, converted to screen coordinates.
444 * \param [in] w_current The GschemToplevel object.
445 * \param [in] object The OBJECT invalidated on screen.
447 void o_invalidate (GschemToplevel
*w_current
, OBJECT
*object
)
449 if (w_current
== NULL
|| w_current
->dont_invalidate
) return;
451 int left
, top
, bottom
, right
;
453 GschemPageView
*page_view
= gschem_toplevel_get_current_page_view (w_current
);
454 PAGE
*page
= gschem_page_view_get_page (page_view
);
456 /* this function may be called before a page is created */
461 if (world_get_single_object_bounds(page
->toplevel
, object
, &left
, &top
,
463 gschem_page_view_invalidate_world_rect (page_view
,
472 /*! \brief Invalidate on-screen area for a GList of objects
474 * \par Function Description
475 * This function calls o_invalidate_rect() with the bounds of the
476 * passed GList, converted to screen coordinates.
478 * \param [in] w_current The GschemToplevel object.
479 * \param [in] list The glist objects invalidated on screen.
481 void o_invalidate_glist (GschemToplevel
*w_current
, GList
*list
)
483 int left
, top
, bottom
, right
;
485 GschemPageView
*page_view
= gschem_toplevel_get_current_page_view (w_current
);
486 g_return_if_fail (page_view
!= NULL
);
488 PAGE
*page
= gschem_page_view_get_page (page_view
);
489 g_return_if_fail (page
!= NULL
);
491 if (world_get_object_glist_bounds (page
->toplevel
, list
, &left
, &top
,
493 gschem_page_view_invalidate_world_rect (page_view
,