schdiff: Use a here document for usage.
[geda-gaf/whiteaudio.git] / gschem / src / o_basic.c
blob8f2674cde6df1980e33cc86069dd6f761193ffd8
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
20 #include <config.h>
21 #include <stdio.h>
23 #include "gschem.h"
25 #ifdef HAVE_LIBDMALLOC
26 #include <dmalloc.h>
27 #endif
29 #define INVALIDATE_MARGIN 1
31 /*! \todo Lots of Gross code... needs lots of cleanup - mainly
32 * readability issues
35 /*! \todo Finish function documentation!!!
36 * \brief
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;
45 int grip_half_size;
46 int cue_half_size;
47 int bloat;
48 int i;
49 GList *obj_list;
50 GList *iter;
51 BOX *world_rects;
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;
70 x = rectangles[i].x;
71 y = rectangles[i].y;
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);
83 g_free (world_rects);
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))
94 continue;
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) {
104 case MOVE:
105 case ENDMOVE:
106 if (w_current->last_drawb_mode != -1) {
107 o_move_draw_rubber (w_current, TRUE);
109 break;
111 case ENDCOPY:
112 case ENDMCOPY:
113 case ENDCOMP:
114 case ENDTEXT:
115 case ENDPASTE:
116 if (w_current->rubber_visible)
117 o_place_draw_rubber (w_current, TRUE);
118 break;
120 case STARTDRAWNET:
121 case DRAWNET:
122 case NETCONT:
123 if (w_current->rubber_visible)
124 o_net_draw_rubber (w_current);
125 break;
127 case STARTDRAWBUS:
128 case DRAWBUS:
129 case BUSCONT:
130 if (w_current->rubber_visible)
131 o_bus_draw_rubber(w_current);
132 break;
134 case GRIPS:
135 if (w_current->rubber_visible)
136 o_grips_draw_rubber (w_current);
137 break;
139 case SBOX:
140 if (w_current->rubber_visible)
141 o_select_box_draw_rubber (w_current);
142 break;
144 case ZOOMBOXEND:
145 if (w_current->rubber_visible)
146 a_zoom_box_draw_rubber (w_current);
147 break;
149 case ENDLINE:
150 if (w_current->rubber_visible)
151 o_line_draw_rubber (w_current);
152 break;
154 case ENDBOX:
155 if (w_current->rubber_visible)
156 o_box_draw_rubber (w_current);
157 break;
159 case ENDPICTURE:
160 if (w_current->rubber_visible)
161 o_picture_draw_rubber (w_current);
162 break;
164 case ENDCIRCLE:
165 if (w_current->rubber_visible)
166 o_circle_draw_rubber (w_current);
167 break;
169 case ENDARC:
170 if (w_current->rubber_visible)
171 o_arc_draw_rubber (w_current);
172 break;
174 case ENDPIN:
175 if (w_current->rubber_visible)
176 o_pin_draw_rubber (w_current);
177 break;
181 g_list_free (obj_list);
185 /*! \todo Finish function documentation!!!
186 * \brief
187 * \par Function Description
190 void o_redraw (GSCHEM_TOPLEVEL *w_current, GList *object_list, gboolean draw_selected)
192 OBJECT *o_current;
193 GList *iter;
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))
200 continue;
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)
219 return;
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;
234 default:
235 g_critical ("o_redraw_single: object %p has bad type '%c'\n",
236 o_current, o_current->type);
239 if (func != NULL)
240 (*func) (w_current, o_current);
244 /*! \todo Finish function documentation!!!
245 * \brief
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)
254 return(FALSE);
256 switch(w_current->event_state) {
258 case(STARTDRAWBUS):
259 case(DRAWBUS):
260 case(BUSCONT):
261 o_bus_invalidate_rubber (w_current);
262 break;
264 case(STARTDRAWNET):
265 case(DRAWNET):
266 case(NETCONT):
267 o_net_invalidate_rubber (w_current);
268 break;
270 case(DRAWPIN):
271 case(ENDPIN):
272 o_pin_invalidate_rubber (w_current);
273 break;
275 case(DRAWLINE):
276 case(ENDLINE):
277 o_line_invalidate_rubber (w_current);
278 break;
280 case(DRAWBOX):
281 case(ENDBOX):
282 o_box_invalidate_rubber (w_current);
283 break;
285 case(DRAWPICTURE):
286 case(ENDPICTURE):
287 o_picture_invalidate_rubber (w_current);
288 break;
290 case(DRAWCIRCLE):
291 case(ENDCIRCLE):
292 o_circle_invalidate_rubber (w_current);
293 break;
295 case(DRAWARC):
296 case(ENDARC):
297 o_arc_invalidate_rubber (w_current);
298 break;
300 default:
301 return(FALSE);
302 break;
305 return(TRUE);
309 /*! \todo Finish function documentation!!!
310 * \brief
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
316 * screen.
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) {
324 return FALSE;
327 switch (w_current->event_state) {
328 /* all states with something on the dc */
329 case(ENDCOMP):
330 /* De-select the lists in the component selector */
331 x_compselect_deselect (w_current);
333 /* Fall through */
334 case(COPY):
335 case(MCOPY):
336 case(DRAWBUS):
337 case(DRAWNET):
338 case(ENDARC):
339 case(ENDBOX):
340 case(ENDCIRCLE):
341 case(ENDCOPY):
342 case(ENDMCOPY):
343 case(ENDLINE):
344 case(ENDMOVE):
345 case(ENDPASTE):
346 case(ENDPIN):
347 case(ENDTEXT):
348 case(GRIPS):
349 case(MOVE):
350 case(NETCONT):
351 case(ZOOMBOXEND):
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);
380 return TRUE;
382 /* all remaining states without dc changes */
383 case(NONE):
384 case(SELECT):
385 case(DRAWLINE):
386 case(DRAWBOX):
387 case(DRAWCIRCLE):
388 case(ZOOM):
389 case(PAN):
390 case(BUSCONT):
391 case(DRAWARC):
392 case(DRAWPICTURE):
393 case(DRAWPIN):
394 case(ENDMIRROR):
395 case(ENDPICTURE):
396 case(ENDROTATEP):
397 case(ENDROUTENET):
398 case(MOUSEPAN):
399 case(SBOX):
400 case(STARTCOPY):
401 case(STARTMCOPY):
402 case(STARTDRAWBUS):
403 case(STARTDRAWNET):
404 case(STARTMOVE):
405 case(STARTPAN):
406 case(STARTPASTE):
407 case(STARTROUTENET):
408 case(STARTSELECT):
409 case(ZOOMBOXSTART):
410 return FALSE;
413 return FALSE;
417 /*! \todo Finish function documentation!!!
418 * \brief
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;
439 default:
440 g_assert_not_reached ();
443 if (func != NULL) {
444 (*func) (w_current, dx, dy, object);
449 /*! \todo Finish function documentation!!!
450 * \brief
451 * \par Function Description
454 void o_glist_draw_place (GSCHEM_TOPLEVEL *w_current, int dx, int dy, GList *list)
456 GList *iter = 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
477 * used.
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)
497 GdkRectangle rect;
498 int grip_half_size;
499 int cue_half_size;
500 int bloat;
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 ))
505 return;
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,
548 &right, &bottom)) {
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,
571 &right, &bottom)) {
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
592 * function.
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)
599 int color_idx;
600 OBJECT *temp;
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;
611 break;
615 if (w_current->toplevel->override_color != -1)
616 color_idx = w_current->toplevel->override_color;
618 return x_color_lookup (color_idx);