Take page as input and take advantage of s_visit_page.
[geda-gaf/berndj.git] / gschem / src / o_basic.c
blob4f3826df08fad60b4d6065f6aca2273215db0395
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
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_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) {
59 case(MOVE):
60 case(ENDMOVE):
61 o_move_rubbermove_xor (w_current, TRUE);
62 break;
64 case(ENDCOPY):
65 case(ENDMCOPY):
66 case(ENDCOMP):
67 case(ENDTEXT):
68 case(ENDPASTE):
69 o_place_rubberplace_xor (w_current, TRUE);
70 break;
72 case(STARTDRAWNET):
73 case(DRAWNET):
74 case(NETCONT):
75 w_current->magnetic_visible=0;
76 break;
78 w_current->rubber_visible=0;
83 /*! \todo Finish function documentation!!!
84 * \brief
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)
127 return;
129 if (toplevel->DONT_REDRAW) /* highly experimental */
130 return;
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!!!
144 * \brief
145 * \par Function Description
148 void o_draw_list(GSCHEM_TOPLEVEL *w_current, GList const *list)
150 OBJECT* o_current;
151 GList const *l_current;
153 if (w_current->inside_redraw) {
154 return;
157 l_current = list;
158 while (l_current != NULL) {
160 o_current = l_current->data;
162 if (o_current) {
163 o_redraw_single(w_current, o_current);
166 l_current = g_list_next(l_current);
170 /*! \todo Finish function documentation!!!
171 * \brief
172 * \par Function Description
175 void o_draw_selected(GSCHEM_TOPLEVEL *w_current)
177 TOPLEVEL *toplevel = w_current->toplevel;
178 GList* s_current;
179 OBJECT* o_current;
180 if (w_current->inside_redraw) {
181 return;
184 s_current = geda_list_get_glist( toplevel->page_current->selection_list );
185 while (s_current != NULL) {
186 o_current = s_current->data;
188 if (o_current) {
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!!!
198 * \brief
199 * \par Function Description
202 void o_erase_selected(GSCHEM_TOPLEVEL *w_current)
204 TOPLEVEL *toplevel = w_current->toplevel;
205 GList *list;
206 GList *iter;
207 OBJECT* o_current;
209 if (w_current->inside_redraw) {
210 return;
213 list = iter = geda_list_get_glist( toplevel->page_current->selection_list );
214 while (iter != NULL) {
215 o_current = iter->data;
217 if (o_current) {
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;
245 OBJECT *o_current;
247 if (w_current->inside_redraw) {
248 return;
251 o_current = object;
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!!!
267 * \brief
268 * \par Function Description
271 void o_erase_list(GSCHEM_TOPLEVEL *w_current, GList const *list)
273 OBJECT *o_current;
274 GList const *iter;
276 if (w_current->inside_redraw) {
277 return;
280 iter = list;
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;
329 int diff_x, diff_y;
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 */
337 if (drawing) {
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;
351 diff_y = 0;
352 } else {
353 w_current->second_wx = w_current->first_wx;
354 diff_x = 0;
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,
368 s_left, s_bottom,
369 s_right - s_left, s_top - s_bottom);
370 } else {
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,
376 s_right, s_bottom);
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!!!
387 * \brief
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)
396 return(FALSE);
398 switch(w_current->event_state) {
400 case(STARTDRAWBUS):
401 case(DRAWBUS):
402 case(BUSCONT):
403 o_bus_eraserubber(w_current);
404 break;
406 case(STARTDRAWNET):
407 case(DRAWNET):
408 case(NETCONT):
409 o_net_eraserubber(w_current);
410 break;
412 case(DRAWPIN):
413 case(ENDPIN):
414 o_pin_eraserubber(w_current);
415 break;
417 case(DRAWLINE):
418 case(ENDLINE):
419 o_line_eraserubber(w_current);
420 break;
422 case(DRAWBOX):
423 case(ENDBOX):
424 o_box_eraserubber(w_current);
425 break;
427 case(DRAWPICTURE):
428 case(ENDPICTURE):
429 o_picture_eraserubber(w_current);
430 break;
432 case(DRAWCIRCLE):
433 case(ENDCIRCLE):
434 o_circle_eraserubber(w_current);
435 break;
437 case(DRAWARC):
438 case(ENDARC):
439 o_arc_eraserubber(w_current);
440 break;
442 default:
443 return(FALSE);
444 break;
447 return(TRUE);
451 /*! \todo Finish function documentation!!!
452 * \brief
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
458 * screen.
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) {
466 return FALSE;
469 switch (w_current->event_state) {
470 /* all states with something on the dc */
471 case(DRAWCOMP):
472 case(ENDCOMP):
473 /* De-select the lists in the component selector */
474 x_compselect_deselect (w_current);
476 /* Fall through */
477 case(COPY):
478 case(MCOPY):
479 case(DRAWBUS):
480 case(DRAWNET):
481 case(ENDARC):
482 case(ENDBOX):
483 case(ENDCIRCLE):
484 case(ENDCOPY):
485 case(ENDMCOPY):
486 case(ENDLINE):
487 case(ENDMOVE):
488 case(ENDPASTE):
489 case(ENDPIN):
490 case(ENDTEXT):
491 case(GRIPS):
492 case(MOVE):
493 case(NETCONT):
494 case(ZOOMBOXEND):
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);
517 return TRUE;
519 /* all remaining states without dc changes */
520 case(NONE):
521 case(SELECT):
522 case(DRAWLINE):
523 case(DRAWBOX):
524 case(DRAWCIRCLE):
525 case(ZOOM):
526 case(PAN):
527 case(BUSCONT):
528 case(DRAWARC):
529 case(DRAWPICTURE):
530 case(DRAWPIN):
531 case(DRAWTEXT):
532 case(ENDMIRROR):
533 case(ENDPICTURE):
534 case(ENDROTATEP):
535 case(ENDROUTENET):
536 case(MOUSEPAN):
537 case(SBOX):
538 case(STARTCOPY):
539 case(STARTMCOPY):
540 case(STARTDRAWBUS):
541 case(STARTDRAWNET):
542 case(STARTMOVE):
543 case(STARTPAN):
544 case(STARTPASTE):
545 case(STARTROUTENET):
546 case(STARTSELECT):
547 case(ZOOMBOXSTART):
548 return FALSE;
551 return FALSE;
555 /*! \todo Finish function documentation!!!
556 * \brief
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;
579 default:
580 g_assert_not_reached ();
583 if (func != NULL) {
584 (*func) (w_current, dx, dy, object);
589 /*! \todo Finish function documentation!!!
590 * \brief
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!!!
606 * \brief
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
626 * selection color.
628 * \param [in] w_current The GSCHEM_TOPLEVEL object.
630 void o_draw_rubbersubject_xor(GSCHEM_TOPLEVEL *w_current)
632 GdkGC *saved_gc;
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,
639 GDK_JOIN_MITER);
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
659 * used.
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 )
676 GdkRectangle rect;
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 ))
681 return;
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,
722 &right, &bottom)) {
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,
746 &right, &bottom)) {
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,
770 &right, &bottom)) {
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);