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
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
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
,
68 const char* hook_name
,
69 const gchar
*undo_desc
)
71 int w_diff_x
, w_diff_y
;
73 GList
*temp_dest_list
= NULL
;
74 GList
*connected_objects
= NULL
;
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
);
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
,
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
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
)
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 */
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
;
246 w_current
->second_wx
= w_current
->first_wx
;
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
,
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.
278 o_place_draw_rubber (GschemToplevel
*w_current
, EdaRenderer
*renderer
)
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
;
311 w_current
->second_wx
= w_current
->first_wx
;
316 /* Translate the cairo context to the required offset before drawing. */
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
,
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);
337 for (iter
= page
->place_list
; iter
!= NULL
;
338 iter
= g_list_next (iter
)) {
339 eda_renderer_draw (renderer
, (OBJECT
*) iter
->data
);
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
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
,
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
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
,
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
);