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 /*! The code in this file is sometimes not obvious, especially
21 * o_select_object (which implements the selection of objects either
22 * when doing a single or multi select)
24 * Also, there are cases where it looks like there is redundant code, which
25 * could be removed/merged, but I purposely didn't do so to keep the code
28 * the count == 0 stuff really only applies to when you are coming from a
41 /*! \brief Start the process of selection
42 * \par Function Description
43 * Chooses the way of how to start the selection process. If no
44 * grip was found at the given coordinates the function sets
45 * \a w_current->inside_action in order to force other functions
46 * (\a o_select_motion() or \a o_select_end()) to decide that.
47 * Otherwise, it switches on the GRIPS mode for working with the
50 * The function is intended to be called by pressing the left
53 * \param [in] w_current The GschemToplevel structure.
54 * \param [in] wx The world X coordinate.
55 * \param [in] wy The world Y coordinate.
57 void o_select_start (GschemToplevel
*w_current
, int wx
, int wy
)
59 /* look for grips or fall through if not enabled */
60 o_grips_start(w_current
, wx
, wy
);
62 if (w_current
->event_state
!= GRIPS
) {
63 /* now go into normal SELECT */
64 i_action_start (w_current
);
65 w_current
->first_wx
= w_current
->second_wx
= wx
;
66 w_current
->first_wy
= w_current
->second_wy
= wy
;
70 /*! \brief End the process of selection
71 * \par Function Description
72 * Finishes the process of selection if the \a o_select_start()
73 * or \a o_select_motion() functions haven't defined other
74 * functions to finish it. In this case the function tries to
75 * find an object under the mouse pointer and select it.
77 * The function is intended to be called by releasing the left
80 * \param [in] w_current The GschemToplevel structure.
81 * \param [in] wx The world X coordinate.
82 * \param [in] wy The world Y coordinate.
84 void o_select_end (GschemToplevel
*w_current
, int wx
, int wy
)
86 g_assert (w_current
->inside_action
!= 0);
88 /* look for objects to select */
89 o_find_object(w_current
, wx
, wy
, TRUE
);
90 i_action_stop (w_current
);
94 /*! \brief Determine whether objects have to be selected or moved
95 * \par Function Description
96 * Checks if the shift or control keys are pressed, (that means
97 * the user definitely wants to drag out a selection box), or
98 * there are no selected objects under the cursor. In that case
99 * the function starts drawing the selection box. Otherwise, it
100 * looks for the objects that have been or could be selected and
101 * starts moving them.
103 * The function is intended to be called by motion of the mouse
104 * while the left mouse button is pressed.
106 * \param [in] w_current The GschemToplevel structure.
107 * \param [in] wx The world X coordinate.
108 * \param [in] wy The world Y coordinate.
110 void o_select_motion (GschemToplevel
*w_current
, int wx
, int wy
)
112 g_assert (w_current
->inside_action
!= 0);
114 /* Check if a mod key is pressed or there is no selected object
115 * under the cursor */
116 if (w_current
->SHIFTKEY
|| w_current
->CONTROLKEY
117 || (!o_find_selected_object(w_current
, w_current
->first_wx
, w_current
->first_wy
)
118 && (!o_find_object(w_current
, w_current
->first_wx
, w_current
->first_wy
, TRUE
)
119 || !o_select_selected(w_current
)))) {
120 /* Start drawing a selection box to select objects */
121 o_select_box_start(w_current
, wx
, wy
);
123 /* Start moving the selected object(s) */
124 o_move_start(w_current
, w_current
->first_wx
, w_current
->first_wy
);
128 /*! \todo Finish function documentation!!!
130 * \par Function Description
133 void o_select_run_hooks(GschemToplevel
*w_current
, OBJECT
*o_current
, int flag
)
136 /* If flag == 0, then we are deselecting something. */
138 g_run_hook_object (w_current
, "%deselect-objects-hook", o_current
);
140 /* If flag == 1, then we are selecting something. */
142 g_run_hook_object (w_current
, "%select-objects-hook", o_current
);
145 g_assert_not_reached ();
149 /*! \todo Finish function documentation!!!
151 * \par Function Description
154 * type can be either SINGLE meaning selection is a single mouse click
155 * or it can be MULTIPLE meaning selection is a selection box
157 void o_select_object(GschemToplevel
*w_current
, OBJECT
*o_current
,
160 TOPLEVEL
*toplevel
= gschem_toplevel_get_toplevel (w_current
);
163 int removing_obj
= 0;
165 SHIFTKEY
= w_current
->SHIFTKEY
;
166 CONTROLKEY
= w_current
->CONTROLKEY
;
169 printf("OBJECT id: %d\n", o_current
->sid
);
172 switch(o_current
->selected
) {
174 case(FALSE
): /* object not selected */
176 switch(SHIFTKEY
) { /* shift key pressed? */
178 case(TRUE
): /* shift key pressed */
179 /* just fall through */
184 /* condition: first object being added */
185 /* condition: control key not pressed */
186 /* condition: for both multiple and single object added */
187 /* result: remove all objects from selection */
188 if (count
== 0 && !CONTROLKEY
) {
189 o_select_unselect_all(w_current
);
193 } /* end shift key switch */
195 /* object not select, add it to the selection list */
196 o_select_run_hooks( w_current
, o_current
, 1 );
197 o_selection_add (toplevel
,
198 toplevel
->page_current
->selection_list
, o_current
);
203 case(TRUE
): /* object was already selected */
205 switch(SHIFTKEY
) { /* shift key pressed ? */
207 case(TRUE
): /* shift key pressed */
209 /* condition: not doing multiple */
210 /* result: remove object from selection */
211 if (type
!= MULTIPLE
) {
212 o_select_run_hooks( w_current
, o_current
, 0 );
213 o_selection_remove (toplevel
, toplevel
->page_current
->
214 selection_list
, o_current
);
220 case(FALSE
): /* shift key not pressed */
222 /* condition: doing multiple */
223 /* condition: first object being added */
224 /* condition: control key not pressed */
225 /* 1st result: remove all objects from selection */
226 /* 2nd result: add object to selection */
227 if (type
== MULTIPLE
&& count
== 0 && !CONTROLKEY
) {
228 o_select_unselect_all (w_current
);
230 o_select_run_hooks( w_current
, o_current
, 1 );
231 o_selection_add (toplevel
,
232 toplevel
->page_current
->selection_list
, o_current
);
235 /* condition: doing single object add */
236 /* condition: control key not pressed */
237 /* 1st result: remove all objects from selection */
238 /* 2nd result: add object to selection list */
239 if (type
== SINGLE
&& !CONTROLKEY
) {
240 o_select_unselect_all (w_current
);
242 o_select_run_hooks (w_current
, o_current
, 1);
243 o_selection_add (toplevel
, toplevel
->page_current
->
244 selection_list
, o_current
);
248 o_select_run_hooks(w_current
, o_current
, 0);
249 o_selection_remove (toplevel
, toplevel
->page_current
->
250 selection_list
, o_current
);
256 break; /* end object selected switch */
259 /* do the attributes */
261 /* Remove the invisible attributes from the object list as well,
262 * so they don't remain selected without the user knowing.
264 o_attrib_deselect_invisible (w_current
,
265 toplevel
->page_current
->selection_list
,
268 /* If the type is MULTIPLE (meaning a select box was/is being used), only
269 * select invisible attributes on objects. Otherwise attributes will be
270 * "double selected", causing them to remain unselected if using
271 * invert-selection (CONTROLKEY is pressed)
273 if( type
== MULTIPLE
) {
274 o_attrib_select_invisible (w_current
,
275 toplevel
->page_current
->selection_list
,
278 /* Select all attributes of the object for a single click select */
279 o_attrib_add_selected (w_current
, toplevel
->page_current
->selection_list
,
285 /*! \todo Finish function documentation!!!
287 * \par Function Description
290 void o_select_box_start(GschemToplevel
*w_current
, int w_x
, int w_y
)
292 g_return_if_fail (w_current
!= NULL
);
294 GschemPageView
*page_view
= gschem_toplevel_get_current_page_view (w_current
);
295 g_return_if_fail (page_view
!= NULL
);
297 int diff_x
, diff_y
, dist
;
299 diff_x
= abs(w_current
->first_wx
- w_x
);
300 diff_y
= abs(w_current
->first_wy
- w_y
);
302 /* if we are still close to the button press location,
303 then don't enter the selection box mode */
304 dist
= gschem_page_view_SCREENabs (page_view
, max(diff_x
, diff_y
));
307 w_current
->second_wx
= w_x
;
308 w_current
->second_wy
= w_y
;
310 i_set_state (w_current
, SBOX
);
311 i_action_start (w_current
);
315 /*! \todo Finish function documentation!!!
317 * \par Function Description
320 void o_select_box_end(GschemToplevel
*w_current
, int w_x
, int w_y
)
322 g_assert (w_current
->inside_action
!= 0);
324 o_select_box_invalidate_rubber (w_current
);
325 w_current
->rubber_visible
= 0;
327 o_select_box_search(w_current
);
329 i_set_state(w_current
, SELECT
);
330 i_action_stop (w_current
);
333 /*! \todo Finish function documentation!!!
335 * \par Function Description
338 void o_select_box_motion (GschemToplevel
*w_current
, int w_x
, int w_y
)
340 g_assert (w_current
->inside_action
!= 0);
342 if (w_current
->rubber_visible
)
343 o_select_box_invalidate_rubber (w_current
);
345 w_current
->second_wx
= w_x
;
346 w_current
->second_wy
= w_y
;
348 o_select_box_invalidate_rubber (w_current
);
349 w_current
->rubber_visible
= 1;
352 /*! \todo Finish function documentation!!!
354 * \par Function Description
356 void o_select_box_invalidate_rubber (GschemToplevel
*w_current
)
358 g_return_if_fail (w_current
!= NULL
);
360 GschemPageView
*page_view
= gschem_toplevel_get_current_page_view (w_current
);
361 g_return_if_fail (page_view
!= NULL
);
363 gschem_page_view_invalidate_world_rect (page_view
,
366 w_current
->second_wx
,
367 w_current
->second_wy
);
370 /*! \todo Finish function documentation!!!
372 * \par Function Description
375 void o_select_box_draw_rubber (GschemToplevel
*w_current
, EdaRenderer
*renderer
)
377 o_box_draw_rubber (w_current
, renderer
);
380 /*! \todo Finish function documentation!!!
382 * \par Function Description
385 void o_select_box_search(GschemToplevel
*w_current
)
387 TOPLEVEL
*toplevel
= gschem_toplevel_get_toplevel (w_current
);
388 OBJECT
*o_current
=NULL
;
389 int count
= 0; /* object count */
390 int SHIFTKEY
= w_current
->SHIFTKEY
;
391 int CONTROLKEY
= w_current
->CONTROLKEY
;
392 int left
, right
, top
, bottom
;
395 left
= min(w_current
->first_wx
, w_current
->second_wx
);
396 right
= max(w_current
->first_wx
, w_current
->second_wx
);
397 top
= min(w_current
->first_wy
, w_current
->second_wy
);
398 bottom
= max(w_current
->first_wy
, w_current
->second_wy
);
400 iter
= s_page_objects (toplevel
->page_current
);
401 while (iter
!= NULL
) {
402 o_current
= iter
->data
;
403 /* only select visible objects */
404 if (o_is_visible (o_current
) || toplevel
->show_hidden_text
) {
405 int cleft
, ctop
, cright
, cbottom
;
407 if ( world_get_single_object_bounds(toplevel
, o_current
,
408 &cleft
, &ctop
, &cright
, &cbottom
) &&
412 cbottom
<= bottom
) {
414 o_select_object(w_current
, o_current
, MULTIPLE
, count
);
418 iter
= g_list_next (iter
);
421 /* if there were no objects to be found in select box, count will be */
422 /* zero, and you need to deselect anything remaining (except when the */
423 /* shift or control keys are pressed) */
424 if (count
== 0 && !SHIFTKEY
&& !CONTROLKEY
) {
425 o_select_unselect_all (w_current
);
427 i_update_menus(w_current
);
430 /*! \brief Select all nets connected to the current net
431 * \par Depending on the state of the w_current->net_selection_mode variable
432 * and the net_selection_state of the current net this function will either
433 * select the single net, all directly connected nets or all nets connected
434 * with netname labels.
435 * \param [in] w_current GschemToplevel struct.
436 * \param [in] o_net Pointer to a single net object
438 void o_select_connected_nets(GschemToplevel
*w_current
, OBJECT
* o_net
)
440 TOPLEVEL
*toplevel
= gschem_toplevel_get_toplevel (w_current
);
447 GList
*netstack
= NULL
;
448 GList
*netnamestack
= NULL
;
451 g_assert(o_net
->type
== OBJ_NET
);
453 /* If either SHIFT or CTRL are pressed, behave exactly the same as a
454 * single object selection. This makes it possible to <mouse-1> on
455 * a net segment to select it and then Shift+<mouse-1> on it to
457 if (w_current
->SHIFTKEY
|| w_current
->CONTROLKEY
) {
458 o_select_object (w_current
, o_net
, SINGLE
, 0);
462 if (!o_net
->selected
) {
463 w_current
->net_selection_state
= 1;
466 /* the current net is the startpoint for the stack */
467 netstack
= g_list_prepend(netstack
, o_net
);
471 netnameiter
= g_list_last(netnamestack
);
472 for (iter1
= g_list_last(netstack
);
474 iter1
= g_list_previous(iter1
), count
++) {
475 o_current
= iter1
->data
;
476 if (o_current
->type
== OBJ_NET
&&
477 (!o_current
->selected
|| count
== 0)) {
478 o_select_object (w_current
, o_current
, SINGLE
, count
);
479 if (w_current
->net_selection_state
> 1) {
481 netstack
= g_list_concat(s_conn_return_others(NULL
, o_current
), netstack
);
483 if (w_current
->net_selection_state
> 2) {
484 /* collect netnames */
485 netname
= o_attrib_search_object_attribs_by_name (o_current
, "netname", 0);
486 if (netname
!= NULL
) {
487 if (g_list_find_custom(netnamestack
, netname
, (GCompareFunc
) strcmp
) == NULL
) {
488 netnamestack
= g_list_append(netnamestack
, netname
);
497 g_list_free(netstack
);
500 if (netnameiter
== g_list_last(netnamestack
))
501 break; /* no new netnames in the stack --> finished */
503 /* get all the nets of the stacked netnames */
504 for (o_iter
= s_page_objects (toplevel
->page_current
);
506 o_iter
= g_list_next (o_iter
)) {
507 o_current
= o_iter
->data
;
508 if (o_current
->type
== OBJ_TEXT
509 && o_current
->attached_to
!= NULL
) {
510 if (o_current
->attached_to
->type
== OBJ_NET
) {
511 netname
= o_attrib_search_object_attribs_by_name (o_current
->attached_to
, "netname", 0);
512 if (netname
!= NULL
) {
513 if (g_list_find_custom(netnamestack
, netname
, (GCompareFunc
) strcmp
) != NULL
) {
514 netstack
= g_list_prepend(netstack
, o_current
->attached_to
);
523 w_current
->net_selection_state
+= 1;
524 if (w_current
->net_selection_state
> w_current
->net_selection_mode
)
525 w_current
->net_selection_state
= 1;
527 for (iter1
= netnamestack
; iter1
!= NULL
; iter1
= g_list_next(iter1
))
529 g_list_free(netnamestack
);
532 /* This is a wrapper for o_selection_return_first_object */
533 /* This function always looks at the current page selection list */
534 OBJECT
*o_select_return_first_object(GschemToplevel
*w_current
)
536 TOPLEVEL
*toplevel
= gschem_toplevel_get_toplevel (w_current
);
537 if (! (w_current
&& toplevel
->page_current
&& geda_list_get_glist( toplevel
->page_current
->selection_list
)))
540 return (OBJECT
*)g_list_first( geda_list_get_glist( toplevel
->page_current
->selection_list
))->data
;
543 /*! \todo Finish function documentation!!!
545 * \par Function Description
547 * \return TRUE if the selection list is not empty, otherwise false.
548 * also make sure item is valid
550 int o_select_selected(GschemToplevel
*w_current
)
552 TOPLEVEL
*toplevel
= gschem_toplevel_get_toplevel (w_current
);
553 if ( geda_list_get_glist( toplevel
->page_current
->selection_list
)) {
560 /*! \todo Finish function documentation!!!
562 * \par Function Description
565 void o_select_unselect_all(GschemToplevel
*w_current
)
567 TOPLEVEL
*toplevel
= gschem_toplevel_get_toplevel (w_current
);
568 SELECTION
*selection
= toplevel
->page_current
->selection_list
;
569 GList
*removed
= NULL
;
572 removed
= g_list_copy (geda_list_get_glist (selection
));
573 for (iter
= removed
; iter
!= NULL
; iter
= g_list_next (iter
)) {
574 o_selection_remove (toplevel
, selection
, (OBJECT
*) iter
->data
);
578 if (removed
!= NULL
) {
579 g_run_hook_object_list (w_current
, "%deselect-objects-hook", removed
);
583 /*! \brief Selects all visible objects on the current page.
584 * \par Function Description
585 * Clears any existing selection, then selects everything visible and
586 * unlocked on the current page, and any attached attributes whether
587 * visible or invisible..
589 * \param w_current The current #GschemToplevel structure.
592 o_select_visible_unlocked (GschemToplevel
*w_current
)
594 TOPLEVEL
*toplevel
= gschem_toplevel_get_toplevel (w_current
);
595 SELECTION
*selection
= toplevel
->page_current
->selection_list
;
599 o_select_unselect_all (w_current
);
600 for (iter
= s_page_objects (toplevel
->page_current
);
602 iter
= g_list_next (iter
)) {
603 OBJECT
*obj
= (OBJECT
*) iter
->data
;
605 /* Skip invisible objects. */
606 if (!o_is_visible (obj
) && !toplevel
->show_hidden_text
)
609 /* Skip locked objects. */
610 if (!obj
->selectable
) continue;
612 /* Add object to selection. */
613 /*! \bug We can't call o_select_object() because it
614 * behaves differently depending on the state of
615 * w_current->SHIFTKEY and w_current->CONTROLKEY, which may well
616 * be set if this function is called via a keystroke
618 o_selection_add (toplevel
, selection
, obj
);
620 /* Add any attributes of object to selection as well. */
621 o_attrib_add_selected (w_current
, selection
, obj
);
624 /* Run hooks for all items selected */
625 added
= geda_list_get_glist (selection
);
627 g_run_hook_object_list (w_current
, "%select-objects-hook", added
);
631 /*! \todo Finish function documentation!!!
633 * \par Function Description
637 o_select_move_to_place_list(GschemToplevel
*w_current
)
639 TOPLEVEL
*toplevel
= gschem_toplevel_get_toplevel (w_current
);
641 GList
*selection_copy
;
643 /* remove the old place list if it exists */
644 s_delete_object_glist(toplevel
, toplevel
->page_current
->place_list
);
645 toplevel
->page_current
->place_list
= NULL
;
647 selection
= geda_list_get_glist( toplevel
->page_current
->selection_list
);
648 selection_copy
= g_list_copy( selection
);
649 toplevel
->page_current
->place_list
= selection_copy
;