Bump gEDA version
[geda-gaf.git] / gschem / src / o_basic.c
blob65c870b165ec8c03e6861b16e6df0cb5942f763b
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
20 #include <config.h>
21 #include <stdio.h>
22 #include <math.h>
23 #include "gschem.h"
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
31 * readability issues
34 /*! \todo Finish function documentation!!!
35 * \brief
36 * \par Function Description
39 void o_redraw_rect (GschemToplevel *w_current,
40 GdkDrawable *drawable,
41 PAGE *page,
42 GschemPageGeometry *geometry,
43 GdkRectangle *rectangle)
45 TOPLEVEL *toplevel = gschem_toplevel_get_toplevel (w_current);
46 gboolean draw_selected;
47 int grip_half_size;
48 double cue_half_size;
49 int bloat;
50 double dummy = 0.0;
51 GList *obj_list;
52 GList *iter;
53 BOX *world_rect;
54 EdaRenderer *renderer;
55 int render_flags;
56 GArray *render_color_map = NULL;
57 GArray *render_outline_color_map = NULL;
58 cairo_t *cr;
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);
69 cairo_clip (cr);
71 cairo_save (cr);
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,
96 page,
97 world_rect,
98 1);
100 g_free (world_rect);
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. */
112 render_color_map =
113 g_array_sized_new (FALSE, FALSE, sizeof(COLOR), MAX_COLORS);
114 render_color_map =
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,
123 MAX_COLORS);
125 /* Set up renderer */
126 renderer = g_object_ref (w_current->renderer);
127 g_object_set (G_OBJECT (renderer),
128 "cairo-context", cr,
129 "grip-size", ((double) grip_half_size * geometry->to_world_x_constant),
130 "render-flags", render_flags,
131 "color-map", render_color_map,
132 NULL);
134 /* Paint background */
135 COLOR *color = x_color_lookup (BACKGROUND_COLOR);
137 cairo_set_source_rgba (cr,
138 color->r / 255.0,
139 color->g / 255.0,
140 color->b / 255.0,
141 color->a / 255.0);
143 cairo_paint (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. */
175 if (draw_selected) {
176 g_object_set (G_OBJECT (renderer),
177 "override-color", SELECT_COLOR,
178 NULL);
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,
190 NULL);
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) {
198 case COMPMODE:
199 case TEXTMODE:
200 case COPYMODE:
201 case MCOPYMODE:
202 case PASTEMODE:
203 if (w_current->rubber_visible) {
204 /* FIXME shouldn't need to save/restore colormap here */
205 cairo_save (cr);
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);
211 cairo_restore (cr);
213 break;
214 case MOVEMODE:
215 if (w_current->last_drawb_mode != -1) {
216 /* FIXME shouldn't need to save/restore colormap here */
217 cairo_save (cr);
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);
223 cairo_restore (cr);
225 break;
226 default: break;
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;
239 case BUSMODE:
240 /* FIXME shouldn't need to save/restore colormap here */
241 cairo_save (cr);
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);
247 cairo_restore (cr);
248 break;
249 case NETMODE:
250 /* FIXME shouldn't need to save/restore colormap here */
251 cairo_save (cr);
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);
257 cairo_restore (cr);
258 break;
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;
265 default: 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);
275 cairo_destroy (cr);
279 /*! \todo Finish function documentation!!!
280 * \brief
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)
289 return(FALSE);
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;
304 default:
305 return(FALSE);
306 break;
309 return(TRUE);
313 /*! \todo Finish function documentation!!!
314 * \brief
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
320 * screen.
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) {
328 return FALSE;
331 switch (w_current->event_state) {
332 /* all states with something on the dc */
333 case(COMPMODE):
334 /* De-select the lists in the component selector */
335 x_compselect_deselect (w_current);
337 /* Fall through */
338 case(ARCMODE):
339 case(BOXMODE):
340 case(BUSMODE):
341 case(CIRCLEMODE):
342 case(LINEMODE):
343 case(NETMODE):
344 case(PATHMODE):
345 case(PICTUREMODE):
346 case(PINMODE):
347 case(COPYMODE):
348 case(MCOPYMODE):
349 case(MOVEMODE):
350 case(PASTEMODE):
351 case(TEXTMODE):
352 case(GRIPS):
353 case(ZOOMBOX):
354 case(OGNRSTMODE):
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));
382 return TRUE;
384 /* all remaining states without dc changes */
385 case(SELECT):
386 case(PAN):
387 case(MIRRORMODE):
388 case(ROTATEMODE):
389 case(SBOX):
390 return FALSE;
393 return FALSE;
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
408 * used.
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,
434 y2);
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 */
457 if (page == NULL) {
458 return;
461 if (world_get_single_object_bounds(page->toplevel, object, &left, &top,
462 &right, &bottom)) {
463 gschem_page_view_invalidate_world_rect (page_view,
464 left,
465 top,
466 right,
467 bottom);
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,
492 &right, &bottom)) {
493 gschem_page_view_invalidate_world_rect (page_view,
494 left,
495 top,
496 right,
497 bottom);