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
28 * the count == 0 stuff really only applies to when you are coming from a
41 #ifdef HAVE_LIBDMALLOC
45 /*! \todo Finish function documentation!!!
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
)
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
)
71 && (o_current
->type
== OBJ_COMPLEX
)
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
)
85 && (o_current
->type
== OBJ_COMPLEX
)
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
)
99 && (o_current
->type
== OBJ_NET
)
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
)
113 && (o_current
->type
== OBJ_NET
)
116 scm_run_hook(deselect_net_hook
,
117 scm_cons(g_make_attrib_smob_list(o_current
), SCM_EOL
));
121 /*! \todo Finish function documentation!!!
123 * \par Function Description
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
,
132 TOPLEVEL
*toplevel
= w_current
->toplevel
;
133 PAGE
*page
= toplevel
->page_current
;
137 SHIFTKEY
= w_current
->SHIFTKEY
;
138 CONTROLKEY
= w_current
->CONTROLKEY
;
141 printf("OBJECT id: %d\n", o_current
->sid
);
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 */
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
);
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
);
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
);
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
);
208 o_select_run_hooks(toplevel
, o_current
, 0);
209 o_selection_remove(page
->selection_list
, o_current
);
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
)
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
);
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!!!
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
;
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) {
276 w_current
->second_wx
= w_x
;
277 w_current
->second_wy
= w_y
;
281 /*! \todo Finish function documentation!!!
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!!!
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!!!
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
;
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
,
335 box_left
, box_top
, box_width
, box_height
);
336 o_invalidate_rect(w_current
,
338 box_left
+ box_width
, box_top
+ box_height
);
341 /*! \todo Finish function documentation!!!
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
);
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
{
394 static void prepend_netname_connected(OBJECT
*o
, void *userdata
)
396 struct net_collection
*nets
= userdata
;
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
);
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
;
430 struct net_collection nets
= {
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
);
447 netnameiter
= g_list_last(nets
.names
);
448 for (iter1
= g_list_last(nets
.objects
);
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) {
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
);
475 g_list_free(nets
.objects
);
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
,
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
))
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
)))
505 return g_list_first(geda_list_get_glist(page
->selection_list
))->data
;
508 /*! \todo Finish function documentation!!!
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
)) {
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!!!
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!!!
559 * \par Function Description
563 o_select_move_to_place_list(GSCHEM_TOPLEVEL
*w_current
)
565 TOPLEVEL
*toplevel
= w_current
->toplevel
;
566 PAGE
*page
= toplevel
->page_current
;
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
);