Take page as input and take advantage of s_visit_page.
[geda-gaf/berndj.git] / gschem / src / o_select.c
blob5fb7cf94caa96bcd40a51aa05c53e573754cfb5c
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 /*! 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 #ifdef HAVE_LIBDMALLOC
42 #include <dmalloc.h>
43 #endif
45 /*! \todo Finish function documentation!!!
46 * \brief
47 * \par Function Description
50 void o_select_run_hooks(TOPLEVEL *toplevel, OBJECT *o_current, int flag)
53 * Run the select_component_hook if the hook has been defined and we
54 * are selecting a component. This will likely be used for cross probing
55 * between schematics and PCB layout or schematics and simulation results.
57 if ( (scm_hook_empty_p(deselect_all_hook) == SCM_BOOL_F)
58 && flag == 2 )
60 scm_run_hook(deselect_all_hook,
61 scm_cons(g_make_attrib_smob_list(o_current), SCM_EOL));
65 * Run the select_component_hook if the hook has been defined and we
66 * are selecting a component. This will likely be used for cross probing
67 * between schematics and PCB layout or schematics and simulation results.
69 if ( (scm_hook_empty_p(select_component_hook) == SCM_BOOL_F)
70 && o_current
71 && (o_current->type == OBJ_COMPLEX)
72 && flag == 1 )
74 scm_run_hook(select_component_hook,
75 scm_cons(g_make_attrib_smob_list(o_current), SCM_EOL));
79 * Run the deselect_component_hook if the hook has been defined and we
80 * are deselecting a component. This will likely be used for cross probing
81 * between schematics and PCB layout or schematics and simulation results.
83 if ( (scm_hook_empty_p(deselect_component_hook) == SCM_BOOL_F)
84 && o_current
85 && (o_current->type == OBJ_COMPLEX)
86 && flag == 0 )
88 scm_run_hook(deselect_component_hook,
89 scm_cons(g_make_attrib_smob_list(o_current), SCM_EOL));
93 * Run the select_net_hook if the hook has been defined and we
94 * are selecting a net. This will likely be used for cross probing
95 * between schematics and PCB layout or schematics and simulation results.
97 if ( (scm_hook_empty_p(select_net_hook) == SCM_BOOL_F)
98 && o_current
99 && (o_current->type == OBJ_NET)
100 && flag == 1)
102 scm_run_hook(select_net_hook,
103 scm_cons(g_make_attrib_smob_list(o_current), SCM_EOL));
107 * Run the deselect_net_hook if the hook has been defined and we
108 * are deselecting a net. This will likely be used for cross probing
109 * between schematics and PCB layout or schematics and simulation results.
111 if ( (scm_hook_empty_p(select_net_hook) == SCM_BOOL_F)
112 && o_current
113 && (o_current->type == OBJ_NET)
114 && flag == 0)
116 scm_run_hook(deselect_net_hook,
117 scm_cons(g_make_attrib_smob_list(o_current), SCM_EOL));
121 /*! \todo Finish function documentation!!!
122 * \brief
123 * \par Function Description
125 * \note
126 * type can be either SINGLE meaning selection is a single mouse click
127 * or it can be MULTIPLE meaning selection is a selection box
129 void o_select_object(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current,
130 int type, int count)
132 TOPLEVEL *toplevel = w_current->toplevel;
133 PAGE *page = toplevel->page_current;
134 int SHIFTKEY;
135 int CONTROLKEY;
137 SHIFTKEY = w_current->SHIFTKEY;
138 CONTROLKEY = w_current->CONTROLKEY;
140 #if DEBUG
141 printf("OBJECT id: %d\n", o_current->sid);
142 #endif
144 switch(o_current->selected) {
145 case(FALSE): /* object not selected */
146 switch(SHIFTKEY) { /* shift key pressed? */
147 case(TRUE): /* shift key pressed */
148 /* just fall through */
149 break;
151 case(FALSE):
152 /* condition: first object being added */
153 /* condition: control key not pressed */
154 /* condition: for both multiple and single object added */
155 /* result: remove all objects from selection */
156 if (count == 0 && !CONTROLKEY) {
157 o_select_run_hooks(toplevel, NULL, 2);
158 o_select_unselect_list(w_current, page->selection_list);
160 break;
161 } /* end shift key switch */
163 /* object not select, add it to the selection list */
164 o_select_run_hooks(toplevel, o_current, 1);
165 o_selection_add(page->selection_list, o_current);
167 break;
169 case(TRUE): /* object was already selected */
170 switch(SHIFTKEY) { /* shift key pressed ? */
171 case(TRUE): /* shift key pressed */
172 /* condition: not doing multiple */
173 /* result: remove object from selection */
174 if (type != MULTIPLE) {
175 o_select_run_hooks(toplevel, o_current, 0);
176 o_selection_remove(page->selection_list, o_current);
179 break;
181 case(FALSE): /* shift key not pressed */
182 /* condition: doing multiple */
183 /* condition: first object being added */
184 /* condition: control key not pressed */
185 /* 1st result: remove all objects from selection */
186 /* 2nd result: add object to selection */
187 if (type == MULTIPLE && count == 0 && !CONTROLKEY) {
188 o_select_run_hooks(toplevel, NULL, 2);
189 o_select_unselect_list(w_current, page->selection_list);
191 o_select_run_hooks(toplevel, o_current, 1);
192 o_selection_add(page->selection_list, o_current);
195 /* condition: doing single object add */
196 /* condition: control key not pressed */
197 /* 1st result: remove all objects from selection */
198 /* 2nd result: add object to selection list */
199 if (type == SINGLE && !CONTROLKEY) {
200 o_select_run_hooks(toplevel, NULL, 2);
201 o_select_unselect_list(w_current, page->selection_list);
203 o_select_run_hooks(toplevel, o_current, 1);
204 o_selection_add(page->selection_list, o_current);
207 if (CONTROLKEY) {
208 o_select_run_hooks(toplevel, o_current, 0);
209 o_selection_remove(page->selection_list, o_current);
212 break;
214 break; /* end object selected switch */
217 /* do the attributes */
218 o_attrib_add_selected(w_current, page->selection_list, o_current);
220 /* finally redraw object */
221 o_redraw_single(w_current, o_current);
224 /*! \brief Select or unselect an object without regard to state.
225 * \par Function Description
226 * Select or unselect an object without considering what else is in the
227 * selection list, or whether the object is already selected or not.
229 * \param [in] w_current The current window
230 * \param [in] toplevel The TOPLEVEL in which the object exists
232 * \bug Passing both w_current and toplevel in addition to o_current
233 * violates the DRY principle. Nevertheless, toplevel isn't simply
234 * w_current->toplevel, and comes from an OBJECT smob instead.
236 void o_select_object_simple(GSCHEM_TOPLEVEL *w_current, TOPLEVEL *toplevel,
237 PAGE *page, OBJECT *o_current, gboolean flag)
239 if (flag) {
240 if (!o_current->selected) {
241 o_select_run_hooks(toplevel, o_current, 1);
242 o_selection_add(page->selection_list, o_current);
243 o_attrib_add_selected(w_current, page->selection_list, o_current);
245 } else {
246 if (o_current->selected) {
247 o_select_run_hooks(toplevel, o_current, 0);
248 o_selection_remove(page->selection_list, o_current);
249 /* XXX Where do attributes get unselected? */
253 o_redraw_single(w_current, o_current);
256 /*! \todo Finish function documentation!!!
257 * \brief
258 * \par Function Description
261 int o_select_box_start(GSCHEM_TOPLEVEL *w_current, int w_x, int w_y)
263 TOPLEVEL *toplevel = w_current->toplevel;
264 PAGE *page = toplevel->page_current;
265 int diff_x, diff_y;
267 diff_x = abs(w_current->first_wx - w_x);
268 diff_y = abs(w_current->first_wy - w_y);
270 /* if we are still close to the button press location,
271 then don't enter the selection box mode */
272 if (SCREENabs(page, max(diff_x, diff_y)) < 10) {
273 return FALSE;
276 w_current->second_wx = w_x;
277 w_current->second_wy = w_y;
278 return TRUE;
281 /*! \todo Finish function documentation!!!
282 * \brief
283 * \par Function Description
286 void o_select_box_end(GSCHEM_TOPLEVEL *w_current, int w_x, int w_y)
288 o_select_box_rubberband_xor(w_current);
289 w_current->rubber_visible = 0;
291 o_select_box_search(w_current);
294 /*! \todo Finish function documentation!!!
295 * \brief
296 * \par Function Description
299 void o_select_box_rubberband(GSCHEM_TOPLEVEL *w_current, int w_x, int w_y)
301 if (w_current->rubber_visible)
302 o_select_box_rubberband_xor(w_current);
304 w_current->second_wx = w_x;
305 w_current->second_wy = w_y;
307 o_select_box_rubberband_xor(w_current);
308 w_current->rubber_visible = 1;
311 /*! \todo Finish function documentation!!!
312 * \brief
313 * \par Function Description
316 void o_select_box_rubberband_xor(GSCHEM_TOPLEVEL *w_current)
318 TOPLEVEL *toplevel = w_current->toplevel;
319 PAGE *page = toplevel->page_current;
320 int box_width, box_height, box_left, box_top;
321 int x1, y1, x2, y2;
323 WORLDtoSCREEN(page, w_current->first_wx, w_current->first_wy, &x1, &y1);
324 WORLDtoSCREEN(page, w_current->second_wx, w_current->second_wy, &x2, &y2);
326 box_width = abs(x1 - x2);
327 box_height = abs(y1 - y2);
328 box_left = min(x1, x2);
329 box_top = min(y1, y2);
331 gdk_gc_set_foreground(w_current->xor_gc,
332 x_get_darkcolor(w_current->select_color));
333 gdk_draw_rectangle(w_current->backingstore, w_current->xor_gc,
334 FALSE,
335 box_left, box_top, box_width, box_height);
336 o_invalidate_rect(w_current,
337 box_left, box_top,
338 box_left + box_width, box_top + box_height);
341 /*! \todo Finish function documentation!!!
342 * \brief
343 * \par Function Description
346 void o_select_box_search(GSCHEM_TOPLEVEL *w_current)
348 TOPLEVEL *toplevel = w_current->toplevel;
349 OBJECT *o_current=NULL;
350 int count = 0; /* object count */
351 int SHIFTKEY = w_current->SHIFTKEY;
352 int left, right, top, bottom;
354 left = min(w_current->first_wx, w_current->second_wx);
355 right = max(w_current->first_wx, w_current->second_wx);
356 top = min(w_current->first_wy, w_current->second_wy);
357 bottom = max(w_current->first_wy, w_current->second_wy);
359 o_current = toplevel->page_current->object_head;
361 while (o_current != NULL) {
362 /* only select visible objects */
363 if (o_current->type != OBJ_HEAD &&
364 (o_current->visibility == VISIBLE ||
365 (o_current->visibility == INVISIBLE && o_current->show_hidden))) {
367 if ( o_current->w_left >= left &&
368 o_current->w_right <= right &&
369 o_current->w_top >= top &&
370 o_current->w_bottom <= bottom ) {
372 o_select_object(w_current, o_current, MULTIPLE, count);
373 count++;
376 o_current = o_current->next;
379 /* if there were no objects to be found in select box, count will be */
380 /* zero, and you need to deselect anything remaining (unless the shift */
381 /* key was pressed */
382 if (count == 0 && !SHIFTKEY) {
383 o_select_run_hooks(toplevel, NULL, 2);
384 o_select_unselect_list( w_current, toplevel->page_current->selection_list );
386 i_update_menus(w_current);
389 struct net_collection {
390 GList *names;
391 GList *objects;
394 static void prepend_netname_connected(OBJECT *o, void *userdata)
396 struct net_collection *nets = userdata;
397 gchar *netname;
399 if (o->type == OBJ_TEXT && o->attached_to != NULL) {
400 if (o->attached_to->type == OBJ_NET) {
401 /* Add if it shares a netname with anything already collected. */
402 netname = o_attrib_search_attrib_name(o->attached_to->attribs, "netname", 0);
403 if (netname != NULL) {
404 if (g_list_find_custom(nets->names, netname,
405 (GCompareFunc) strcmp) != NULL) {
406 nets->objects = g_list_prepend(nets->objects, o->attached_to);
408 g_free(netname);
414 /*! \brief Select all nets connected to the current net
415 * \par Depending on the state of the w_current->net_selection_mode variable
416 * and the net_selection_state of the current net this function will either
417 * select the single net, all directly connected nets or all nets connected
418 * with netname labels.
419 * \param [in] w_current GSCHEM_TOPLEVEL struct.
420 * \param [in] o_net Pointer to a single net object
422 void o_select_connected_nets(GSCHEM_TOPLEVEL *w_current, OBJECT* o_net)
424 TOPLEVEL *toplevel = w_current->toplevel;
425 GList *iter1;
426 OBJECT *o_current;
427 int count=0;
428 gchar* netname;
430 struct net_collection nets = {
431 .names = NULL,
432 .objects = NULL,
434 GList *netnameiter;
436 g_assert(o_net->type == OBJ_NET);
438 if (!o_net->selected) {
439 w_current->net_selection_state = 1;
442 /* the current net is the startpoint for the stack */
443 nets.objects = g_list_prepend(nets.objects, o_net);
445 count = 0;
446 while (1) {
447 netnameiter = g_list_last(nets.names);
448 for (iter1 = g_list_last(nets.objects);
449 iter1 != NULL;
450 iter1 = g_list_previous(iter1), count++) {
451 o_current = iter1->data;
452 if (o_current->type == OBJ_NET &&
453 (!o_current->selected || count == 0)) {
454 o_select_object(w_current, o_current, SINGLE, count);
455 if (w_current->net_selection_state > 1) {
456 /* collect nets */
457 GList *connected = s_conn_return_others(NULL, o_current);
458 nets.objects = g_list_concat(connected, nets.objects);
460 if (w_current->net_selection_state > 2) {
461 /* collect netnames */
462 netname = o_attrib_search_attrib_name(o_current->attribs, "netname", 0);
463 if (netname != NULL) {
464 if (g_list_find_custom(nets.names, netname,
465 (GCompareFunc) strcmp) == NULL) {
466 nets.names = g_list_append(nets.names, netname);
468 else {
469 g_free(netname);
475 g_list_free(nets.objects);
476 nets.objects = NULL;
478 if (netnameiter == g_list_last(nets.names))
479 break; /* no new netnames in the stack --> finished */
481 /* get all the nets of the stacked netnames */
482 s_visit_page(toplevel->page_current, &prepend_netname_connected, &nets,
483 VISIT_UNORDERED, 1);
486 w_current->net_selection_state += 1;
487 if (w_current->net_selection_state > w_current->net_selection_mode)
488 w_current->net_selection_state = 1;
490 for (iter1 = nets.names; iter1 != NULL; iter1 = g_list_next(iter1))
491 g_free(iter1->data);
492 g_list_free(nets.names);
495 /* This is a wrapper for o_selection_return_first_object */
496 /* This function always looks at the current page selection list */
497 OBJECT *o_select_return_first_object(GSCHEM_TOPLEVEL *w_current)
499 TOPLEVEL *toplevel = w_current->toplevel;
500 PAGE *page = toplevel->page_current;
502 if (! (w_current && page && geda_list_get_glist(page->selection_list)))
503 return NULL;
504 else
505 return g_list_first(geda_list_get_glist(page->selection_list))->data;
508 /*! \todo Finish function documentation!!!
509 * \brief
510 * \par Function Description
512 * \return TRUE if the selection list is not empty, otherwise false.
513 * also make sure item is valid
515 int o_select_selected(GSCHEM_TOPLEVEL *w_current)
517 TOPLEVEL *toplevel = w_current->toplevel;
518 if ( geda_list_get_glist( toplevel->page_current->selection_list )) {
519 return(TRUE);
521 return(FALSE);
525 /*! \brief Unselects all the objects in the given list.
526 * \par Unselects all objects in the given list, does the
527 * needed work to make the objects visually unselected, and redraw them.
528 * \param [in] w_current GSCHEM_TOPLEVEL struct.
529 * \param [in] head Pointer to the selection list
531 void o_select_unselect_list(GSCHEM_TOPLEVEL *w_current, SELECTION *selection)
533 const GList *list = geda_list_get_glist( selection );
535 while ( list != NULL ) {
536 o_selection_unselect(list->data);
537 o_redraw_single(w_current, list->data);
538 list = g_list_next( list );
541 geda_list_remove_all( (GedaList *)selection );
545 /*! \todo Finish function documentation!!!
546 * \brief
547 * \par Function Description
550 void o_select_unselect_all(GSCHEM_TOPLEVEL *w_current)
552 TOPLEVEL *toplevel = w_current->toplevel;
553 o_select_run_hooks(toplevel, NULL, 2);
554 o_select_unselect_list( w_current, toplevel->page_current->selection_list );
557 /*! \todo Finish function documentation!!!
558 * \brief
559 * \par Function Description
562 void
563 o_select_move_to_place_list(GSCHEM_TOPLEVEL *w_current)
565 TOPLEVEL *toplevel = w_current->toplevel;
566 PAGE *page = toplevel->page_current;
567 GList *selection;
568 GList *selection_copy;
570 selection = geda_list_get_glist(page->selection_list);
571 selection_copy = g_list_copy( selection );
572 s_page_replace_place_list(toplevel, page, selection_copy);