Remove extra debug message
[geda-gaf.git] / gschem / src / o_place.c
blob32f85e87ceccba27ee829aa3326d8e9c68298401
1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2019 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 /*! \brief Start placement action
27 * \par Function Description
28 * This function remembers the current world coordinates and
29 * invalidates the bounding box of the objects in the current
30 * place list.
32 * \param [in] w_current GschemToplevel which we're drawing for.
33 * \param [in] w_x The current world X coordinate.
34 * \param [in] w_y The current world Y coordinate.
36 void o_place_start (GschemToplevel *w_current, int w_x, int w_y)
38 g_return_if_fail (w_current != NULL);
40 i_action_start (w_current);
42 w_current->second_wx = w_x;
43 w_current->second_wy = w_y;
45 o_place_invalidate_rubber (w_current, TRUE);
46 w_current->rubber_visible = 1;
49 /*! \brief End placement action
51 * \par Function Description
52 * This function finishes the current placement action by adding
53 * objects to the current page at the given new world coordinates
54 * and redrawing them on the canvas. It also saves the current
55 * state in the undo list and updates the menus.
57 * If \a continue_placing is TRUE, a copy of the placement list
58 * is saved to start a new place action.
60 * \param [in] w_current GschemToplevel which we're drawing for.
61 * \param [in] w_x The current world X coordinate.
62 * \param [in] w_y The current world Y coordinate.
63 * \param [in] hook_name The hook to run after adding the objects.
65 void o_place_end (GschemToplevel *w_current,
66 int w_x, int w_y,
67 int continue_placing,
68 const char* hook_name,
69 const gchar *undo_desc)
71 int w_diff_x, w_diff_y;
72 OBJECT *o_current;
73 GList *temp_dest_list = NULL;
74 GList *connected_objects = NULL;
75 GList *iter;
77 g_return_if_fail (w_current != NULL);
78 g_assert (w_current->inside_action != 0);
80 GschemPageView *page_view = gschem_toplevel_get_current_page_view (w_current);
81 g_return_if_fail (page_view != NULL);
83 PAGE *page = gschem_page_view_get_page (page_view);
84 g_return_if_fail (page != NULL);
86 /* erase old image */
87 /* o_place_invalidate_rubber (w_current, FALSE); */
88 w_current->rubber_visible = 0;
90 /* Calc final object positions */
91 w_current->second_wx = w_x;
92 w_current->second_wy = w_y;
94 w_diff_x = w_current->second_wx - w_current->first_wx;
95 w_diff_y = w_current->second_wy - w_current->first_wy;
97 if (continue_placing) {
98 /* Make a copy of the place list if we want to keep it afterwards */
99 temp_dest_list = o_glist_copy_all (page->toplevel,
100 page->place_list,
101 temp_dest_list);
102 } else {
103 /* Otherwise just take it */
104 temp_dest_list = page->place_list;
105 page->place_list = NULL;
108 o_glist_translate_world(temp_dest_list, w_diff_x, w_diff_y);
110 /* Attach each item back onto the page's object list. Update object
111 * connectivity and add the new objects to the selection list.*/
112 for (iter = temp_dest_list; iter != NULL; iter = g_list_next (iter)) {
113 o_current = iter->data;
115 s_page_append (page->toplevel, page, o_current);
117 /* Update object connectivity */
118 s_conn_update_object (page, o_current);
119 connected_objects = s_conn_return_others (connected_objects, o_current);
122 if (hook_name != NULL) {
123 g_run_hook_object_list (w_current, hook_name, temp_dest_list);
126 o_invalidate_glist (w_current, connected_objects);
127 g_list_free (connected_objects);
128 connected_objects = NULL;
130 o_invalidate_glist (w_current, temp_dest_list); /* only redraw new objects */
131 g_list_free (temp_dest_list);
133 gschem_toplevel_page_content_changed (w_current, page);
134 o_undo_savestate_old (w_current, UNDO_ALL, undo_desc);
135 i_update_menus (w_current);
137 if (!continue_placing) {
138 i_set_state(w_current, SELECT);
139 i_action_stop (w_current);
143 /*! \brief Move the objects in the place list to new coordinates
145 * \par Function Description
146 * This function erases the objects in the current place list at
147 * their previous coordinates and draws them at the new given
148 * coordinates.
150 * \param [in] w_current GschemToplevel which we're drawing for.
151 * \param [in] w_x The current world X coordinate.
152 * \param [in] w_y The current world Y coordinate.
154 void o_place_motion (GschemToplevel *w_current, int w_x, int w_y)
156 PAGE *page = gschem_page_view_get_page (gschem_toplevel_get_current_page_view (w_current));
157 g_return_if_fail (page != NULL);
159 g_return_if_fail (page->place_list != NULL);
160 g_assert (w_current->inside_action != 0);
162 if (w_current->rubber_visible)
163 o_place_invalidate_rubber (w_current, FALSE);
164 w_current->second_wx = w_x;
165 w_current->second_wy = w_y;
166 o_place_invalidate_rubber (w_current, TRUE);
167 w_current->rubber_visible = 1;
171 /*! \brief Invalidate bounding box or outline for OBJECT placement
173 * \par Function Description
174 * This function invalidates the bounding box where objects would be
175 * drawn by o_place_draw_rubber()
177 * The function applies manhatten mode constraints to the coordinates
178 * before drawing if the CONTROL key is recording as being pressed in
179 * the w_current structure.
181 * The "drawing" parameter is used to indicate if this drawing should
182 * immediately use the selected feedback mode and positioning constraints.
184 * With drawing=TRUE, the selected conditions are used immediately,
185 * otherwise the conditions from the last drawing operation are used,
186 * saving the new state for next time.
188 * This function should be called with drawing=TRUE when starting a
189 * rubberbanding operation and when otherwise refreshing the rubberbanded
190 * outline (e.g. after a screen redraw). For any undraw operation, should
191 * be called with drawing=FALSE, ensuring that the undraw XOR matches the
192 * mode and constraints of the corresponding "draw" operation.
194 * If any mode / constraint changes are made between a undraw, redraw XOR
195 * pair, the latter (draw) operation must be called with drawing=TRUE. If
196 * no mode / constraint changes were made between the pair, it is not
197 * harmful to call the draw operation with "drawing=FALSE".
199 * \param [in] w_current GschemToplevel which we're drawing for.
200 * \param [in] drawing Set to FALSE for undraw operations to ensure
201 * matching conditions to a previous draw operation.
203 void o_place_invalidate_rubber (GschemToplevel *w_current, int drawing)
205 int diff_x, diff_y;
206 int left, top, bottom, right;
208 g_return_if_fail (w_current != NULL);
210 GschemPageView *page_view = gschem_toplevel_get_current_page_view (w_current);
211 g_return_if_fail (page_view != NULL);
213 PAGE *page = gschem_page_view_get_page (page_view);
214 g_return_if_fail (page != NULL);
215 g_return_if_fail (page->place_list != NULL);
217 /* If drawing is true, then don't worry about the previous drawing
218 * method and movement constraints, use with the current settings */
219 if (drawing) {
220 /* Ensure we set this to flag there is "something" supposed to be
221 * drawn when the invalidate call below causes an expose event. */
222 w_current->last_drawb_mode = w_current->actionfeedback_mode;
223 w_current->drawbounding_action_mode = (w_current->CONTROLKEY &&
224 ! ((w_current->event_state == PASTEMODE) ||
225 (w_current->event_state == COMPMODE) ||
226 (w_current->event_state == TEXTMODE)))
227 ? CONSTRAINED : FREE;
230 /* Calculate delta of X-Y positions from buffer's origin */
231 diff_x = w_current->second_wx - w_current->first_wx;
232 diff_y = w_current->second_wy - w_current->first_wy;
234 /* Adjust the coordinates according to the movement constraints */
236 /* Need to update the w_current->{first,second}_w{x,y} coords even
237 * though we're only invalidating because the move rubberband code
238 * (which may execute right after this function) expects these
239 * coordinates to be correct.
241 if (w_current->drawbounding_action_mode == CONSTRAINED) {
242 if (abs (diff_x) >= abs (diff_y)) {
243 w_current->second_wy = w_current->first_wy;
244 diff_y = 0;
245 } else {
246 w_current->second_wx = w_current->first_wx;
247 diff_x = 0;
251 /* Find the bounds of the drawing to be done */
252 world_get_object_glist_bounds (page->toplevel, page->place_list,
253 &left, &top, &right, &bottom);
255 gschem_page_view_invalidate_world_rect (page_view,
256 left + diff_x,
257 top + diff_y,
258 right + diff_x,
259 bottom + diff_y);
263 /*! \brief Draw a bounding box or outline for OBJECT placement
264 * \par Function Description
265 * This function draws either the OBJECTS in the place list
266 * or a rectangle around their bounding box, depending upon the
267 * currently selected w_current->actionfeedback_mode. This takes the
268 * value BOUNDINGBOX or OUTLINE.
270 * The function applies manhatten mode constraints to the coordinates
271 * before drawing if the CONTROL key is recording as being pressed in
272 * the w_current structure.
274 * \param w_current GschemToplevel which we're drawing for.
275 * \param renderer Renderer to use for drawing.
277 void
278 o_place_draw_rubber (GschemToplevel *w_current, EdaRenderer *renderer)
280 int diff_x, diff_y;
281 cairo_t *cr = eda_renderer_get_cairo_context (renderer);
283 g_return_if_fail (w_current != NULL);
285 GschemPageView *page_view = gschem_toplevel_get_current_page_view (w_current);
286 g_return_if_fail (page_view != NULL);
288 PAGE *page = gschem_page_view_get_page (page_view);
289 g_return_if_fail (page != NULL);
290 g_return_if_fail (page->place_list != NULL);
292 /* Don't worry about the previous drawing method and movement
293 * constraints, use with the current settings */
294 w_current->last_drawb_mode = w_current->actionfeedback_mode;
295 w_current->drawbounding_action_mode = (w_current->CONTROLKEY &&
296 ! ((w_current->event_state == PASTEMODE) ||
297 (w_current->event_state == COMPMODE) ||
298 (w_current->event_state == TEXTMODE)))
299 ? CONSTRAINED : FREE;
301 /* Calculate delta of X-Y positions from buffer's origin */
302 diff_x = w_current->second_wx - w_current->first_wx;
303 diff_y = w_current->second_wy - w_current->first_wy;
305 /* Adjust the coordinates according to the movement constraints */
306 if (w_current->drawbounding_action_mode == CONSTRAINED ) {
307 if (abs(diff_x) >= abs(diff_y)) {
308 w_current->second_wy = w_current->first_wy;
309 diff_y = 0;
310 } else {
311 w_current->second_wx = w_current->first_wx;
312 diff_x = 0;
316 /* Translate the cairo context to the required offset before drawing. */
317 cairo_save (cr);
318 cairo_translate (cr, diff_x, diff_y);
320 /* Draw with the appropriate mode */
321 if (w_current->last_drawb_mode == BOUNDINGBOX) {
322 GArray *map = eda_renderer_get_color_map (renderer);
323 int flags = eda_renderer_get_cairo_flags (renderer);
324 int left, top, bottom, right;
326 /* Find the bounds of the drawing to be done */
327 world_get_object_glist_bounds (page->toplevel,
328 page->place_list,
329 &left, &top, &right, &bottom);
331 /* Draw box outline */
332 eda_cairo_box (cr, flags, 0, left, top, right, bottom);
333 eda_cairo_set_source_color (cr, BOUNDINGBOX_COLOR, map);
334 eda_cairo_stroke (cr, flags, TYPE_SOLID, END_NONE, 0, -1, -1);
335 } else {
336 GList *iter;
337 for (iter = page->place_list; iter != NULL;
338 iter = g_list_next (iter)) {
339 eda_renderer_draw (renderer, (OBJECT *) iter->data);
342 cairo_restore (cr);
346 /*! \brief Rotate the objects being placed
348 * \par Function Description
349 * This function erases the objects in the place list, rotates
350 * them, runs %rotate-objects-hook, and redraws the objects after
351 * rotating.
353 * \param [in] w_current The GschemToplevel object.
355 void o_place_rotate (GschemToplevel *w_current)
357 GschemPageView *page_view = gschem_toplevel_get_current_page_view (w_current);
358 g_return_if_fail (page_view != NULL);
360 PAGE *page = gschem_page_view_get_page (page_view);
361 g_return_if_fail (page != NULL);
363 o_place_invalidate_rubber (w_current, FALSE);
365 o_glist_rotate_world (page->toplevel,
366 w_current->first_wx,
367 w_current->first_wy,
369 page->place_list);
371 /* Run rotate-objects-hook */
372 g_run_hook_object_list (w_current, "%rotate-objects-hook", page->place_list);
374 o_place_invalidate_rubber (w_current, TRUE);
378 /*! \brief Mirror the objects being placed
380 * \par Function Description
381 * This function erases the objects in the place list, mirrors
382 * them, runs %mirror-objects-hook, and redraws the objects after
383 * mirroring.
385 * \param [in] w_current The GschemToplevel object.
387 void o_place_mirror (GschemToplevel *w_current)
389 GschemPageView *page_view = gschem_toplevel_get_current_page_view (w_current);
390 g_return_if_fail (page_view != NULL);
392 PAGE *page = gschem_page_view_get_page (page_view);
393 g_return_if_fail (page != NULL);
395 o_place_invalidate_rubber (w_current, FALSE);
397 o_glist_mirror_world (page->toplevel,
398 w_current->first_wx,
399 w_current->first_wy,
400 page->place_list);
402 /* Run mirror-objects-hook */
403 g_run_hook_object_list (w_current, "%mirror-objects-hook", page->place_list);
405 o_place_invalidate_rubber (w_current, TRUE);