switched buffer insertion mode to world coords
[geda-gaf/werner.git] / gschem / src / o_basic.c
blobecca6cff68cd73bf693cbb1695ff2fd47934ddfe
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 <libgeda/libgeda.h>
25 #include "../include/gschem_struct.h"
26 #include "../include/x_states.h"
27 #include "../include/prototype.h"
29 #ifdef HAVE_LIBDMALLOC
30 #include <dmalloc.h>
31 #endif
33 #define INVALIDATE_MARGIN 1
35 /*! \todo Lots of Gross code... needs lots of cleanup - mainly
36 * readability issues
39 /*! \todo Finish function documentation!!!
40 * \brief
41 * \par Function Description
44 void o_redraw_all(GSCHEM_TOPLEVEL *w_current)
46 TOPLEVEL *toplevel = w_current->toplevel;
47 gboolean draw_selected = TRUE;
49 if (!toplevel->DONT_REDRAW) {
50 x_repaint_background(w_current);
53 draw_selected = !(w_current->inside_action &&
54 ((w_current->event_state == MOVE) ||
55 (w_current->event_state == ENDMOVE) ||
56 (w_current->event_state == GRIPS)));
57 o_redraw(w_current, toplevel->page_current->object_head, draw_selected);
58 o_cue_redraw_all(w_current,
59 toplevel->page_current->object_head, draw_selected);
61 if (w_current->inside_action) {
62 switch(w_current->event_state) {
63 case(MOVE):
64 case(ENDMOVE):
65 case(ENDCOPY):
66 case(ENDMCOPY):
67 o_drawbounding(w_current,
68 geda_list_get_glist( toplevel->page_current->selection_list ),
69 x_get_darkcolor(w_current->bb_color), TRUE);
70 break;
72 case(DRAWCOMP):
73 case(ENDCOMP):
74 o_drawbounding(w_current, toplevel->page_current->complex_place_list,
75 x_get_darkcolor(w_current->bb_color), TRUE);
76 break;
78 case(DRAWTEXT):
79 case(ENDTEXT):
80 o_drawbounding(w_current, toplevel->page_current->attrib_place_list,
81 x_get_darkcolor(w_current->bb_color), TRUE);
82 break;
83 case(STARTDRAWNET):
84 case(DRAWNET):
85 case(NETCONT):
86 w_current->magnetic_visible=0;
87 break;
89 w_current->rubber_visible=0;
94 /*! \todo Finish function documentation!!!
95 * \brief
96 * \par Function Description
99 void o_redraw(GSCHEM_TOPLEVEL *w_current, OBJECT *list, gboolean draw_selected)
101 TOPLEVEL *toplevel = w_current->toplevel;
102 OBJECT *o_current = list;
103 int redraw_state = toplevel->DONT_REDRAW;
104 int left, top, bottom, right;
105 int s_left, s_top, s_bottom, s_right;
107 w_current->inside_redraw = 1;
108 while (o_current != NULL) {
109 if ((o_current->draw_func != NULL) &&
110 (o_current->type != OBJ_HEAD)) {
111 toplevel->DONT_REDRAW = redraw_state ||
112 (!draw_selected && o_current->selected);
113 (*o_current->draw_func)(w_current, o_current);
116 o_current = o_current->next;
118 w_current->inside_redraw = 0;
119 toplevel->DONT_REDRAW = redraw_state;
121 if (world_get_object_list_bounds(toplevel, list, &left, &top,
122 &right, &bottom)) {
123 WORLDtoSCREEN( toplevel, left, top, &s_left, &s_top );
124 WORLDtoSCREEN( toplevel, right, bottom, &s_right, &s_bottom );
125 o_invalidate_rect( w_current, s_left, s_top, s_right, s_bottom );
129 /*! \brief Redraw an object on the screen.
130 * \par Function Description
131 * This function will redraw a single object on the screen.
133 * \param [in] w_current The GSCHEM_TOPLEVEL object.
134 * \param [in] o_current The OBJECT to redraw.
137 void o_redraw_single(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current)
139 TOPLEVEL *toplevel = w_current->toplevel;
140 int left, top, right, bottom;
141 int s_left, s_top, s_right, s_bottom;
143 if (o_current == NULL)
144 return;
146 if (toplevel->DONT_REDRAW) /* highly experimental */
147 return;
149 if (o_current->draw_func != NULL && o_current->type != OBJ_HEAD) {
150 w_current->inside_redraw = 1;
151 (*o_current->draw_func)(w_current, o_current);
152 w_current->inside_redraw = 0;
155 if (world_get_single_object_bounds(toplevel, o_current, &left, &top,
156 &right, &bottom)) {
157 WORLDtoSCREEN(toplevel, left, top, &s_left, &s_top);
158 WORLDtoSCREEN(toplevel, right, bottom, &s_right, &s_bottom);
159 o_invalidate_rect (w_current, s_left, s_bottom, s_right, s_top);
163 /*! \todo Finish function documentation!!!
164 * \brief
165 * \par Function Description
168 void o_draw_list(GSCHEM_TOPLEVEL *w_current, GList* list)
170 OBJECT* o_current;
171 GList *l_current;
173 if (w_current->inside_redraw) {
174 return;
177 l_current = list;
178 while (l_current != NULL) {
180 o_current = (OBJECT *) l_current->data;
182 if (o_current) {
183 o_redraw_single(w_current, o_current);
186 l_current = g_list_next(l_current);
190 /*! \todo Finish function documentation!!!
191 * \brief
192 * \par Function Description
195 void o_draw_selected(GSCHEM_TOPLEVEL *w_current)
197 TOPLEVEL *toplevel = w_current->toplevel;
198 GList* s_current;
199 OBJECT* o_current;
200 if (w_current->inside_redraw) {
201 return;
204 s_current = geda_list_get_glist( toplevel->page_current->selection_list );
205 while (s_current != NULL) {
206 o_current = (OBJECT *) s_current->data;
208 if (o_current) {
209 o_redraw_single(w_current, o_current);
210 o_cue_draw_single(w_current, o_current);
212 s_current=g_list_next(s_current);
217 /*! \todo Finish function documentation!!!
218 * \brief
219 * \par Function Description
222 void o_erase_selected(GSCHEM_TOPLEVEL *w_current)
224 TOPLEVEL *toplevel = w_current->toplevel;
225 GList *list;
226 GList *iter;
227 OBJECT* o_current;
228 int left, top, right, bottom;
229 int s_left, s_top, s_right, s_bottom;
231 if (w_current->inside_redraw) {
232 return;
235 list = iter = geda_list_get_glist( toplevel->page_current->selection_list );
236 while (iter != NULL) {
237 o_current = iter->data;
239 if (o_current) {
240 o_cue_erase_single(w_current, o_current);
241 o_erase_single(w_current, o_current);
244 iter = g_list_next( iter );
247 if (world_get_object_glist_bounds(toplevel, list, &left, &top,
248 &right, &bottom)) {
249 WORLDtoSCREEN(toplevel, left, top, &s_left, &s_top);
250 WORLDtoSCREEN(toplevel, right, bottom, &s_right, &s_bottom);
251 o_invalidate_rect (w_current, s_left, s_bottom, s_right, s_top);
255 /*! \brief Erase a given OBJECT
257 * \par Function Description
258 * This function erases the passed OBJECT, <B>object</B>.
260 * It makes a call to object's draw function after having set a
261 * color override to the background color. The object is drawn in
262 * the background color, causing it to dissapear.
264 * \bug No redrawing is done of occluded objects, including the grid.
266 * \param [in] w_current The GSCHEM_TOPLEVEL object.
267 * \param [in] o_current Circle OBJECT to erase.
269 void o_erase_single(GSCHEM_TOPLEVEL *w_current, OBJECT *object)
271 TOPLEVEL *toplevel = w_current->toplevel;
272 OBJECT *o_current;
273 int left, top, right, bottom;
274 int s_left, s_top, s_right, s_bottom;
276 if (w_current->inside_redraw) {
277 return;
280 o_current = object;
282 toplevel->override_color = toplevel->background_color;
283 if (o_current != NULL) {
284 if (o_current->draw_func &&
285 o_current->type != OBJ_HEAD) {
286 (*o_current->draw_func)(w_current, o_current);
289 toplevel->override_color = -1;
291 if (world_get_single_object_bounds(toplevel, o_current, &left, &top,
292 &right, &bottom)) {
293 WORLDtoSCREEN(toplevel, left, top, &s_left, &s_top);
294 WORLDtoSCREEN(toplevel, right, bottom, &s_right, &s_bottom);
295 o_invalidate_rect (w_current, s_left, s_bottom, s_right, s_top);
299 /*! \todo Finish function documentation!!!
300 * \brief
301 * \par Function Description
304 void o_erase_list(GSCHEM_TOPLEVEL *w_current, GList* list)
306 OBJECT *o_current;
307 GList *iter;
309 if (w_current->inside_redraw) {
310 return;
313 iter = list;
314 while (iter != NULL) {
315 o_current = iter->data;
316 o_erase_single(w_current, o_current);
317 iter = g_list_next(iter);
321 /*! \brief XOR draw a bounding box or outline for OBJECT placement
323 * \par Function Description
324 * This function XOR draws either the OBJECTS in the passed GList,
325 * or a rectangle around their bounding box, depending upon the
326 * currently selected w_current->actionfeedback_mode. This takes the
327 * value BOUNDINGBOX or OUTLINE.
329 * The function applies manhatten mode constraints to the coordinates
330 * before drawing if the CONTROL key is recording as being pressed in
331 * the w_current structure.
333 * The "drawing" parameter is used to indicate if this drawing should
334 * immediately use the selected feedback mode and positioning constraints.
336 * With drawing=TRUE, the selected conditions are used immediately,
337 * otherwise the conditions from the last drawing operation are used,
338 * saving the new state for next time.
340 * o_drawbounding() should be called with drawing=TRUE when starting a
341 * rubberbanding operation and when otherwise refreshing the rubberbanded
342 * outline (e.g. after a screen redraw). For any undraw operation, should
343 * be called with drawing=FALSE, ensuring that the undraw XOR matches the
344 * mode and constraints of the corresponding "draw" operation.
346 * If any mode / constraint changes are made between a undraw, redraw XOR
347 * pair, the latter (draw) operation must be called with drawing=TRUE. If
348 * no mode / constraint changes were made between the pair, it is not
349 * harmful to call the draw operation with "drawing=FALSE".
351 * \param [in] w_current GSCHEM_TOPLEVEL which we're drawing for.
352 * \param [in] o_glst GList of objects to XOR draw.
353 * \param [in] color GdkColor used for drawing in BOUNDINGBOX mode.
354 * \param [in] drawing Set to FALSE for undraw operations to ensure
355 * matching conditions to a previous draw operation.
357 void o_drawbounding(GSCHEM_TOPLEVEL *w_current, GList *o_glist,
358 GdkColor *color, int drawing)
360 TOPLEVEL *toplevel = w_current->toplevel;
361 int diff_x, diff_y;
362 int left, top, bottom, right;
363 int s_left, s_top, s_bottom, s_right;
365 if (o_glist == NULL) {
366 return;
369 /* BUG: temporary fix while switching to world corrds */
370 if (!(w_current->event_state == MOVE)
371 && !(w_current->event_state == ENDMOVE)
372 && !(w_current->event_state == DRAWTEXT)
373 && !(w_current->event_state == ENDTEXT)
374 && !(w_current->event_state == ENDPASTE)) {
375 SCREENtoWORLD(toplevel, w_current->start_x, w_current->start_y,
376 &(w_current->first_wx), &(w_current->first_wy));
377 SCREENtoWORLD(toplevel, w_current->last_x, w_current->last_y,
378 &(w_current->second_wx), &(w_current->second_wy));
381 /* If drawing is true, then don't worry about the previous drawing
382 * method and movement constraints, use with the current settings */
383 if (drawing) {
384 w_current->last_drawb_mode = w_current->actionfeedback_mode;
385 w_current->drawbounding_action_mode = (w_current->CONTROLKEY)
386 ? CONSTRAINED : FREE;
389 /* Calculate delta of X-Y positions from buffer's origin */
390 diff_x = w_current->second_wx - w_current->first_wx;
391 diff_y = w_current->second_wy - w_current->first_wy;
393 /* Adjust the coordinates according to the movement constraints */
394 if (w_current->drawbounding_action_mode == CONSTRAINED ) {
395 if (abs(diff_x) >= abs(diff_y)) {
396 w_current->second_wy = w_current->first_wy;
397 } else {
398 w_current->second_wx = w_current->first_wx;
402 /* Find the bounds of the drawing to be done */
403 world_get_object_glist_bounds(toplevel, o_glist,
404 &left, &top, &right, &bottom);
405 WORLDtoSCREEN(toplevel, left + diff_x, top + diff_y, &s_left, &s_top);
406 WORLDtoSCREEN(toplevel, right + diff_x, bottom + diff_y, &s_right, &s_bottom);
408 /* XOR draw with the appropriate mode */
409 if (w_current->last_drawb_mode == BOUNDINGBOX) {
410 gdk_gc_set_foreground(w_current->bounding_xor_gc, color);
411 gdk_draw_rectangle(w_current->backingstore,
412 w_current->bounding_xor_gc, FALSE,
413 s_left, s_bottom,
414 s_right - s_left, s_top - s_bottom);
415 } else {
416 o_glist_draw_xor (w_current, diff_x, diff_y, o_glist);
419 /* Invalidate the screen buffer where we drew */
420 o_invalidate_rect(w_current, s_left, s_top,
421 s_right, s_bottom);
423 /* Save movement constraints and drawing method for any
424 * corresponding undraw operation. */
425 w_current->last_drawb_mode = w_current->actionfeedback_mode;
426 w_current->drawbounding_action_mode = (w_current->CONTROLKEY)
427 ? CONSTRAINED : FREE;
431 /*! \todo Finish function documentation!!!
432 * \brief
433 * \par Function Description
436 int o_erase_rubber(GSCHEM_TOPLEVEL *w_current)
438 /* return FALSE if it did not erase anything */
440 if (!w_current->inside_action)
441 return(FALSE);
443 switch(w_current->event_state) {
445 case(STARTDRAWBUS):
446 case(DRAWBUS):
447 case(BUSCONT):
448 o_bus_eraserubber(w_current);
449 break;
451 case(STARTDRAWNET):
452 case(DRAWNET):
453 case(NETCONT):
454 o_net_eraserubber(w_current);
455 break;
457 case(DRAWPIN):
458 case(ENDPIN):
459 o_pin_eraserubber(w_current);
460 break;
462 case(DRAWLINE):
463 case(ENDLINE):
464 o_line_eraserubber(w_current);
465 break;
467 case(DRAWBOX):
468 case(ENDBOX):
469 o_box_eraserubber(w_current);
470 break;
472 case(DRAWPICTURE):
473 case(ENDPICTURE):
474 o_picture_eraserubber(w_current);
475 break;
477 case(DRAWCIRCLE):
478 case(ENDCIRCLE):
479 o_circle_eraserubber(w_current);
480 break;
482 case(DRAWARC):
483 case(ENDARC):
484 o_arc_eraserubber(w_current);
485 break;
487 default:
488 return(FALSE);
489 break;
492 return(TRUE);
496 /*! \todo Finish function documentation!!!
497 * \brief
498 * \par Function Description
499 * This function is neccesary to make jumps between event_states.
500 * If we are inside an drawing action that created something on the dc,
501 * e.g. if we are drawing a box and then jump to line drawing without
502 * leaving the box drawing mode, there will remain some rubberbands on the
503 * screen.
504 * Usually a intermediate select state would clean (redraw) the screen.
506 int o_redraw_cleanstates(GSCHEM_TOPLEVEL *w_current)
508 TOPLEVEL *toplevel = w_current->toplevel;
509 /* returns FALSE if the function was'nt nessecary */
510 if (w_current->inside_action == 0) {
511 return FALSE;
514 switch (w_current->event_state) {
515 /* all states with something on the dc */
516 case(DRAWCOMP):
517 case(ENDCOMP):
518 /* De-select the lists in the component selector */
519 x_compselect_deselect (w_current);
521 /* Fall through */
522 case(COPY):
523 case(MCOPY):
524 case(DRAWBUS):
525 case(DRAWNET):
526 case(ENDARC):
527 case(ENDBOX):
528 case(ENDCIRCLE):
529 case(ENDCOPY):
530 case(ENDMCOPY):
531 case(ENDLINE):
532 case(ENDMOVE):
533 case(ENDPASTE):
534 case(ENDPIN):
535 case(ENDTEXT):
536 case(GRIPS):
537 case(MOVE):
538 case(NETCONT):
539 case(ZOOMBOXEND):
540 /* it is possible to cancel in the middle of a place,
541 * so lets be sure to clean up the complex_place_list
542 * structure and also clean up the attrib_place_list. */
544 /* If it is a move command, then free the complex place list WITHOUT
545 freeing the individual objects. */
546 if ((w_current->event_state == MOVE) ||
547 (w_current->event_state == ENDMOVE)) {
548 g_list_free (toplevel->page_current->complex_place_list);
549 } else {
550 s_delete_object_glist(toplevel,
551 toplevel->page_current->complex_place_list);
553 toplevel->page_current->complex_place_list = NULL;
555 s_delete_object_glist (toplevel,
556 toplevel->page_current->attrib_place_list);
557 toplevel->page_current->attrib_place_list = NULL;
559 /* also free internal current_attribute */
560 o_attrib_free_current(toplevel);
561 w_current->inside_action = 0;
563 /* touch the select state */
564 i_set_state(w_current, SELECT);
566 /* from i_callback_cancel() */
567 o_redraw_all(w_current);
568 return TRUE;
570 /* all remaining states without dc changes */
571 case(NONE):
572 case(SELECT):
573 case(DRAWLINE):
574 case(DRAWBOX):
575 case(DRAWCIRCLE):
576 case(ZOOM):
577 case(PAN):
578 case(BUSCONT):
579 case(DRAWARC):
580 case(DRAWPICTURE):
581 case(DRAWPIN):
582 case(DRAWTEXT):
583 case(ENDMIRROR):
584 case(ENDPICTURE):
585 case(ENDROTATEP):
586 case(ENDROUTENET):
587 case(MOUSEPAN):
588 case(SBOX):
589 case(STARTCOPY):
590 case(STARTMCOPY):
591 case(STARTDRAWBUS):
592 case(STARTDRAWNET):
593 case(STARTMOVE):
594 case(STARTPAN):
595 case(STARTPASTE):
596 case(STARTROUTENET):
597 case(STARTSELECT):
598 case(TEXTENTRY):
599 case(ZOOMBOXSTART):
600 return FALSE;
603 return FALSE;
607 /*! \todo Finish function documentation!!!
608 * \brief
609 * \par Function Description
612 void o_draw_xor(GSCHEM_TOPLEVEL *w_current, int dx, int dy, OBJECT *object)
614 void (*func) (GSCHEM_TOPLEVEL *, int, int, OBJECT*) = NULL;
616 switch (object->type) {
617 case OBJ_HEAD: /* Do nothing for head nodes */ break;
618 case OBJ_LINE: func = o_line_draw_xor; break;
619 case OBJ_NET: func = o_net_draw_xor; break;
620 case OBJ_BUS: func = o_bus_draw_xor; break;
621 case OBJ_BOX: func = o_box_draw_xor; break;
622 case OBJ_PICTURE: func = o_picture_draw_xor; break;
623 case OBJ_CIRCLE: func = o_circle_draw_xor; break;
624 case OBJ_PLACEHOLDER:
625 case OBJ_COMPLEX: func = o_complex_draw_xor; break;
626 case OBJ_TEXT: func = o_text_draw_xor; break;
627 case OBJ_PIN: func = o_pin_draw_xor; break;
628 case OBJ_ARC: func = o_arc_draw_xor; break;
629 default:
630 g_assert_not_reached ();
633 if (func != NULL) {
634 (*func) (w_current, dx, dy, object);
639 /*! \todo Finish function documentation!!!
640 * \brief
641 * \par Function Description
644 void o_list_draw_xor(GSCHEM_TOPLEVEL *w_current, int dx, int dy, OBJECT *list)
646 OBJECT *o_current = list;
648 while(o_current != NULL) {
649 o_draw_xor(w_current, dx, dy, o_current);
650 o_current = o_current->next;
655 /*! \todo Finish function documentation!!!
656 * \brief
657 * \par Function Description
660 void o_glist_draw_xor(GSCHEM_TOPLEVEL *w_current, int dx, int dy, GList *list)
662 GList *iter = list;
664 while (iter != NULL) {
665 o_draw_xor(w_current, dx, dy, (OBJECT *)iter->data);
666 iter = g_list_next(iter);
671 /*! \brief Invalidates a rectangular region of the on screen drawing area
672 * \par Function Description
674 * Given a pair of (x,y) coordinates in SCREEN units, invalidate the
675 * rectangular on-screen drawing area which has those two coordinate
676 * pairs as opposite corners of its region. This will cause that region
677 * to be blitted from the back-buffer once the mainloop reaches idle.
679 * A margin, INVALIDATE_MARGIN is added to the invalidated region as
680 * a hacky workaround for rounding errors which may occur in the
681 * WORLD -> SCREEN coordinate transform. This margin may also be used
682 * to expand the invalidated region if anti-aliased drawing is ever
683 * used.
685 * If the GSCHEM_TOPLEVEL in question is not rendering to a GDK_WINDOW,
686 * (e.g. image export), this function call is a no-op. A test is used:
687 * GDK_IS_WINDOW(), which should be safe since in either case,
688 * w_current->window is a GObject. This is really a _HACK_,
689 * and should be fixed with a re-worked drawing model.
691 * \param [in] w_current The GSCHEM_TOPLEVEL who's drawing area is being invalidated.
692 * \param [in] x1 X coord for corner 1 (SCREEN units)
693 * \param [in] y1 Y coord for corner 1 (SCREEN units)
694 * \param [in] x2 X coord for corner 2 (SCREEN units)
695 * \param [in] y2 Y coord for corner 2 (SCREEN units)
697 void o_invalidate_rect( GSCHEM_TOPLEVEL *w_current,
698 int x1, int y1, int x2, int y2 )
700 GdkRectangle rect;
702 /* BUG: We get called when rendering an image, and w_current->window
703 * is a GdkPixmap. Ensure we only invalidate GdkWindows. */
704 if (!GDK_IS_WINDOW( w_current->window ))
705 return;
707 rect.x = MIN(x1, x2) - INVALIDATE_MARGIN;
708 rect.y = MIN(y1, y2) - INVALIDATE_MARGIN;
709 rect.width = 1 + abs( x1 - x2 ) + 2 * INVALIDATE_MARGIN;
710 rect.height = 1 + abs( y1 - y2 ) + 2 * INVALIDATE_MARGIN;
711 gdk_window_invalidate_rect( w_current->window, &rect, FALSE );