Remove extra debug message
[geda-gaf.git] / gschem / src / o_select.c
blob466dcb9193520425c914f9ac177a01d2ede45b17
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
26 * readable
28 * the count == 0 stuff really only applies to when you are coming from a
29 * multi select case
31 #include <config.h>
33 #include <math.h>
34 #include <stdio.h>
35 #ifdef HAVE_STRING_H
36 #include <string.h>
37 #endif
39 #include "gschem.h"
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
48 * grip found.
50 * The function is intended to be called by pressing the left
51 * mouse button.
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
78 * mouse button.
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);
122 } else {
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!!!
129 * \brief
130 * \par Function Description
133 void o_select_run_hooks(GschemToplevel *w_current, OBJECT *o_current, int flag)
135 switch (flag) {
136 /* If flag == 0, then we are deselecting something. */
137 case 0:
138 g_run_hook_object (w_current, "%deselect-objects-hook", o_current);
139 break;
140 /* If flag == 1, then we are selecting something. */
141 case 1:
142 g_run_hook_object (w_current, "%select-objects-hook", o_current);
143 break;
144 default:
145 g_assert_not_reached ();
149 /*! \todo Finish function documentation!!!
150 * \brief
151 * \par Function Description
153 * \note
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,
158 int type, int count)
160 TOPLEVEL *toplevel = gschem_toplevel_get_toplevel (w_current);
161 int SHIFTKEY;
162 int CONTROLKEY;
163 int removing_obj = 0;
165 SHIFTKEY = w_current->SHIFTKEY;
166 CONTROLKEY = w_current->CONTROLKEY;
168 #if DEBUG
169 printf("OBJECT id: %d\n", o_current->sid);
170 #endif
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 */
180 break;
182 case(FALSE):
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);
191 break;
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);
200 break;
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);
215 removing_obj = 1;
218 break;
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);
247 if (CONTROLKEY) {
248 o_select_run_hooks(w_current, o_current, 0);
249 o_selection_remove (toplevel, toplevel->page_current->
250 selection_list, o_current);
251 removing_obj = 1;
254 break;
256 break; /* end object selected switch */
259 /* do the attributes */
260 if (removing_obj) {
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,
266 o_current);
267 } else {
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,
276 o_current);
277 } else {
278 /* Select all attributes of the object for a single click select */
279 o_attrib_add_selected (w_current, toplevel->page_current->selection_list,
280 o_current);
285 /*! \todo Finish function documentation!!!
286 * \brief
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));
306 if (dist >= 10) {
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!!!
316 * \brief
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!!!
334 * \brief
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!!!
353 * \brief
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,
364 w_current->first_wx,
365 w_current->first_wy,
366 w_current->second_wx,
367 w_current->second_wy);
370 /*! \todo Finish function documentation!!!
371 * \brief
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!!!
381 * \brief
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;
393 const GList *iter;
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) &&
409 cleft >= left &&
410 cright <= right &&
411 ctop >= top &&
412 cbottom <= bottom ) {
414 o_select_object(w_current, o_current, MULTIPLE, count);
415 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);
441 const GList *o_iter;
442 GList *iter1;
443 OBJECT *o_current;
444 int count=0;
445 gchar* netname;
447 GList *netstack = NULL;
448 GList *netnamestack = NULL;
449 GList *netnameiter;
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
456 * deselect it. */
457 if (w_current->SHIFTKEY || w_current->CONTROLKEY) {
458 o_select_object (w_current, o_net, SINGLE, 0);
459 return;
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);
469 count = 0;
470 while (1) {
471 netnameiter = g_list_last(netnamestack);
472 for (iter1 = g_list_last(netstack);
473 iter1 != NULL;
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) {
480 /* collect nets */
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);
490 else {
491 g_free(netname);
497 g_list_free(netstack);
498 netstack = NULL;
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);
505 o_iter != NULL;
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);
516 g_free(netname);
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))
528 g_free(iter1->data);
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 )))
538 return NULL;
539 else
540 return (OBJECT *)g_list_first( geda_list_get_glist( toplevel->page_current->selection_list ))->data;
543 /*! \todo Finish function documentation!!!
544 * \brief
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 )) {
554 return(TRUE);
556 return(FALSE);
560 /*! \todo Finish function documentation!!!
561 * \brief
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;
570 GList *iter;
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);
577 /* Call hooks */
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.
591 void
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;
596 const GList *iter;
597 GList *added;
599 o_select_unselect_all (w_current);
600 for (iter = s_page_objects (toplevel->page_current);
601 iter != NULL;
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)
607 continue;
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
617 * (e.g. Ctrl-A). */
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);
626 if (added != NULL) {
627 g_run_hook_object_list (w_current, "%select-objects-hook", added);
631 /*! \todo Finish function documentation!!!
632 * \brief
633 * \par Function Description
636 void
637 o_select_move_to_place_list(GschemToplevel *w_current)
639 TOPLEVEL *toplevel = gschem_toplevel_get_toplevel (w_current);
640 GList *selection;
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;