1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2011 gEDA Contributors (see ChangeLog for details)
5 *
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.
10 *
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.
15 *
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
19 */
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)
23 *
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
27 *
28 * the count == 0 stuff really only applies to when you are coming from a
29 * multi select case
30 */
31 #include <config.h>
32
33 #include <math.h>
34 #include <stdio.h>
35 #ifdef HAVE_STRING_H
36 #include <string.h>
37 #endif
38
39 #include "gschem.h"
40
41 #ifdef HAVE_LIBDMALLOC
42 #include <dmalloc.h>
43 #endif
44
45 /*! \todo Finish function documentation!!!
46 * \brief
47 * \par Function Description
48 *
49 */
o_select_run_hooks(GSCHEM_TOPLEVEL * w_current,OBJECT * o_current,int flag)50 void o_select_run_hooks(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current, int flag)
51 {
52 switch (flag) {
53 /* If flag == 0, then we are deselecting something. */
54 case 0:
55 g_run_hook_object (w_current, "%deselect-objects-hook", o_current);
56 break;
57 /* If flag == 1, then we are selecting something. */
58 case 1:
59 g_run_hook_object (w_current, "%select-objects-hook", o_current);
60 break;
61 default:
62 g_assert_not_reached ();
63 }
64 }
65
66 /*! \todo Finish function documentation!!!
67 * \brief
68 * \par Function Description
69 *
70 * \note
71 * type can be either SINGLE meaning selection is a single mouse click
72 * or it can be MULTIPLE meaning selection is a selection box
73 */
o_select_object(GSCHEM_TOPLEVEL * w_current,OBJECT * o_current,int type,int count)74 void o_select_object(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current,
75 int type, int count)
76 {
77 TOPLEVEL *toplevel = w_current->toplevel;
78 int SHIFTKEY;
79 int CONTROLKEY;
80 int removing_obj = 0;
81
82 SHIFTKEY = w_current->SHIFTKEY;
83 CONTROLKEY = w_current->CONTROLKEY;
84
85 #if DEBUG
86 printf("OBJECT id: %d\n", o_current->sid);
87 #endif
88
89 switch(o_current->selected) {
90
91 case(FALSE): /* object not selected */
92
93 switch(SHIFTKEY) { /* shift key pressed? */
94
95 case(TRUE): /* shift key pressed */
96 /* just fall through */
97 break;
98
99 case(FALSE):
100
101 /* condition: first object being added */
102 /* condition: control key not pressed */
103 /* condition: for both multiple and single object added */
104 /* result: remove all objects from selection */
105 if (count == 0 && !CONTROLKEY) {
106 o_select_unselect_all(w_current);
107 }
108 break;
109
110 } /* end shift key switch */
111
112 /* object not select, add it to the selection list */
113 o_select_run_hooks( w_current, o_current, 1 );
114 o_selection_add (toplevel,
115 toplevel->page_current->selection_list, o_current);
116
117 break;
118
119
120 case(TRUE): /* object was already selected */
121
122 switch(SHIFTKEY) { /* shift key pressed ? */
123
124 case(TRUE): /* shift key pressed */
125
126 /* condition: not doing multiple */
127 /* result: remove object from selection */
128 if (type != MULTIPLE) {
129 o_select_run_hooks( w_current, o_current, 0 );
130 o_selection_remove (toplevel, toplevel->page_current->
131 selection_list, o_current);
132 removing_obj = 1;
133 }
134
135 break;
136
137 case(FALSE): /* shift key not pressed */
138
139 /* condition: doing multiple */
140 /* condition: first object being added */
141 /* condition: control key not pressed */
142 /* 1st result: remove all objects from selection */
143 /* 2nd result: add object to selection */
144 if (type == MULTIPLE && count == 0 && !CONTROLKEY) {
145 o_select_unselect_all (w_current);
146
147 o_select_run_hooks( w_current, o_current, 1 );
148 o_selection_add (toplevel,
149 toplevel->page_current->selection_list, o_current);
150 }
151
152 /* condition: doing single object add */
153 /* condition: control key not pressed */
154 /* 1st result: remove all objects from selection */
155 /* 2nd result: add object to selection list */
156 if (type == SINGLE && !CONTROLKEY) {
157 o_select_unselect_all (w_current);
158
159 o_select_run_hooks (w_current, o_current, 1);
160 o_selection_add (toplevel, toplevel->page_current->
161 selection_list, o_current);
162 }
163
164 if (CONTROLKEY) {
165 o_select_run_hooks(w_current, o_current, 0);
166 o_selection_remove (toplevel, toplevel->page_current->
167 selection_list, o_current);
168 removing_obj = 1;
169 }
170
171 break;
172 }
173 break; /* end object selected switch */
174 }
175
176 /* do the attributes */
177 if (removing_obj) {
178 /* Remove the invisible attributes from the object list as well,
179 * so they don't remain selected without the user knowing.
180 */
181 o_attrib_deselect_invisible (w_current,
182 toplevel->page_current->selection_list,
183 o_current);
184 } else {
185 /* If the type is MULTIPLE (meaning a select box was/is being used), only
186 * select invisible attributes on objects. Otherwise attributes will be
187 * "double selected", causing them to remain unselected if using
188 * invert-selection (CONTROLKEY is pressed)
189 */
190 if( type == MULTIPLE) {
191 o_attrib_select_invisible (w_current,
192 toplevel->page_current->selection_list,
193 o_current);
194 } else {
195 /* Select all attributes of the object for a single click select */
196 o_attrib_add_selected (w_current, toplevel->page_current->selection_list,
197 o_current);
198 }
199 }
200 }
201
202 /*! \todo Finish function documentation!!!
203 * \brief
204 * \par Function Description
205 *
206 */
o_select_box_start(GSCHEM_TOPLEVEL * w_current,int w_x,int w_y)207 int o_select_box_start(GSCHEM_TOPLEVEL *w_current, int w_x, int w_y)
208 {
209 int diff_x, diff_y;
210
211 diff_x = abs(w_current->first_wx - w_x);
212 diff_y = abs(w_current->first_wy - w_y);
213
214 /* if we are still close to the button press location,
215 then don't enter the selection box mode */
216 if (SCREENabs (w_current, max(diff_x, diff_y)) < 10) {
217 return FALSE;
218 }
219
220 w_current->second_wx = w_x;
221 w_current->second_wy = w_y;
222 return TRUE;
223 }
224
225 /*! \todo Finish function documentation!!!
226 * \brief
227 * \par Function Description
228 *
229 */
o_select_box_end(GSCHEM_TOPLEVEL * w_current,int w_x,int w_y)230 void o_select_box_end(GSCHEM_TOPLEVEL *w_current, int w_x, int w_y)
231 {
232 o_select_box_invalidate_rubber (w_current);
233 w_current->rubber_visible = 0;
234
235 o_select_box_search(w_current);
236 }
237
238 /*! \todo Finish function documentation!!!
239 * \brief
240 * \par Function Description
241 *
242 */
o_select_box_motion(GSCHEM_TOPLEVEL * w_current,int w_x,int w_y)243 void o_select_box_motion (GSCHEM_TOPLEVEL *w_current, int w_x, int w_y)
244 {
245 if (w_current->rubber_visible)
246 o_select_box_invalidate_rubber (w_current);
247
248 w_current->second_wx = w_x;
249 w_current->second_wy = w_y;
250
251 o_select_box_invalidate_rubber (w_current);
252 w_current->rubber_visible = 1;
253 }
254
255 /*! \todo Finish function documentation!!!
256 * \brief
257 * \par Function Description
258 */
o_select_box_invalidate_rubber(GSCHEM_TOPLEVEL * w_current)259 void o_select_box_invalidate_rubber (GSCHEM_TOPLEVEL *w_current)
260 {
261 int x1, y1, x2, y2;
262
263 WORLDtoSCREEN (w_current, w_current->first_wx, w_current->first_wy, &x1, &y1);
264 WORLDtoSCREEN (w_current, w_current->second_wx, w_current->second_wy, &x2, &y2);
265
266 o_invalidate_rect (w_current, x1, y1, x2, y1);
267 o_invalidate_rect (w_current, x1, y1, x1, y2);
268 o_invalidate_rect (w_current, x2, y1, x2, y2);
269 o_invalidate_rect (w_current, x1, y2, x2, y2);
270 }
271
272 /*! \todo Finish function documentation!!!
273 * \brief
274 * \par Function Description
275 *
276 */
o_select_box_draw_rubber(GSCHEM_TOPLEVEL * w_current)277 void o_select_box_draw_rubber (GSCHEM_TOPLEVEL *w_current)
278 {
279 gschem_cairo_box (w_current, 0,
280 w_current->first_wx, w_current->first_wy,
281 w_current->second_wx, w_current->second_wy);
282
283 gschem_cairo_set_source_color (w_current,
284 x_color_lookup_dark (SELECT_COLOR));
285 gschem_cairo_stroke (w_current, TYPE_SOLID, END_NONE, 0, -1, -1);
286 }
287
288 /*! \todo Finish function documentation!!!
289 * \brief
290 * \par Function Description
291 *
292 */
o_select_box_search(GSCHEM_TOPLEVEL * w_current)293 void o_select_box_search(GSCHEM_TOPLEVEL *w_current)
294 {
295 TOPLEVEL *toplevel = w_current->toplevel;
296 OBJECT *o_current=NULL;
297 int count = 0; /* object count */
298 int SHIFTKEY = w_current->SHIFTKEY;
299 int CONTROLKEY = w_current->CONTROLKEY;
300 int left, right, top, bottom;
301 const GList *iter;
302
303 left = min(w_current->first_wx, w_current->second_wx);
304 right = max(w_current->first_wx, w_current->second_wx);
305 top = min(w_current->first_wy, w_current->second_wy);
306 bottom = max(w_current->first_wy, w_current->second_wy);
307
308 iter = s_page_objects (toplevel->page_current);
309 while (iter != NULL) {
310 o_current = iter->data;
311 /* only select visible objects */
312 if (o_is_visible (toplevel, o_current) || toplevel->show_hidden_text) {
313
314 if ( o_current->w_left >= left &&
315 o_current->w_right <= right &&
316 o_current->w_top >= top &&
317 o_current->w_bottom <= bottom ) {
318
319 o_select_object(w_current, o_current, MULTIPLE, count);
320 count++;
321 }
322 }
323 iter = g_list_next (iter);
324 }
325
326 /* if there were no objects to be found in select box, count will be */
327 /* zero, and you need to deselect anything remaining (except when the */
328 /* shift or control keys are pressed) */
329 if (count == 0 && !SHIFTKEY && !CONTROLKEY) {
330 o_select_unselect_all (w_current);
331 }
332 i_update_menus(w_current);
333 }
334
335 /*! \brief Select all nets connected to the current net
336 * \par Depending on the state of the w_current->net_selection_mode variable
337 * and the net_selection_state of the current net this function will either
338 * select the single net, all directly connected nets or all nets connected
339 * with netname labels.
340 * \param [in] w_current GSCHEM_TOPLEVEL struct.
341 * \param [in] o_net Pointer to a single net object
342 */
o_select_connected_nets(GSCHEM_TOPLEVEL * w_current,OBJECT * o_net)343 void o_select_connected_nets(GSCHEM_TOPLEVEL *w_current, OBJECT* o_net)
344 {
345 TOPLEVEL *toplevel = w_current->toplevel;
346 const GList *o_iter;
347 GList *iter1;
348 OBJECT *o_current;
349 int count=0;
350 gchar* netname;
351
352 GList *netstack = NULL;
353 GList *netnamestack = NULL;
354 GList *netnameiter;
355
356 g_assert(o_net->type == OBJ_NET);
357
358 if (!o_net->selected) {
359 w_current->net_selection_state = 1;
360 }
361
362 /* the current net is the startpoint for the stack */
363 netstack = g_list_prepend(netstack, o_net);
364
365 count = 0;
366 while (1) {
367 netnameiter = g_list_last(netnamestack);
368 for (iter1 = g_list_last(netstack);
369 iter1 != NULL;
370 iter1 = g_list_previous(iter1), count++) {
371 o_current = iter1->data;
372 if (o_current->type == OBJ_NET &&
373 (!o_current->selected || count == 0)) {
374 o_select_object (w_current, o_current, SINGLE, count);
375 if (w_current->net_selection_state > 1) {
376 /* collect nets */
377 netstack = g_list_concat(s_conn_return_others(NULL, o_current), netstack);
378 }
379 if (w_current->net_selection_state > 2) {
380 /* collect netnames */
381 netname = o_attrib_search_object_attribs_by_name (o_current, "netname", 0);
382 if (netname != NULL) {
383 if (g_list_find_custom(netnamestack, netname, (GCompareFunc) strcmp) == NULL) {
384 netnamestack = g_list_append(netnamestack, netname);
385 }
386 else {
387 g_free(netname);
388 }
389 }
390 }
391 }
392 }
393 g_list_free(netstack);
394 netstack = NULL;
395
396 if (netnameiter == g_list_last(netnamestack))
397 break; /* no new netnames in the stack --> finished */
398
399 /* get all the nets of the stacked netnames */
400 for (o_iter = s_page_objects (toplevel->page_current);
401 o_iter != NULL;
402 o_iter = g_list_next (o_iter)) {
403 o_current = o_iter->data;
404 if (o_current->type == OBJ_TEXT
405 && o_current->attached_to != NULL) {
406 if (o_current->attached_to->type == OBJ_NET) {
407 netname = o_attrib_search_object_attribs_by_name (o_current->attached_to, "netname", 0);
408 if (netname != NULL) {
409 if (g_list_find_custom(netnamestack, netname, (GCompareFunc) strcmp) != NULL) {
410 netstack = g_list_prepend(netstack, o_current->attached_to);
411 }
412 g_free(netname);
413 }
414 }
415 }
416 }
417 }
418
419 w_current->net_selection_state += 1;
420 if (w_current->net_selection_state > w_current->net_selection_mode)
421 w_current->net_selection_state = 1;
422
423 for (iter1 = netnamestack; iter1 != NULL; iter1 = g_list_next(iter1))
424 g_free(iter1->data);
425 g_list_free(netnamestack);
426 }
427
428 /* This is a wrapper for o_selection_return_first_object */
429 /* This function always looks at the current page selection list */
o_select_return_first_object(GSCHEM_TOPLEVEL * w_current)430 OBJECT *o_select_return_first_object(GSCHEM_TOPLEVEL *w_current)
431 {
432 TOPLEVEL *toplevel = w_current->toplevel;
433 if (! (w_current && toplevel->page_current && geda_list_get_glist( toplevel->page_current->selection_list )))
434 return NULL;
435 else
436 return (OBJECT *)g_list_first( geda_list_get_glist( toplevel->page_current->selection_list ))->data;
437 }
438
439 /*! \todo Finish function documentation!!!
440 * \brief
441 * \par Function Description
442 *
443 * \return TRUE if the selection list is not empty, otherwise false.
444 * also make sure item is valid
445 */
o_select_selected(GSCHEM_TOPLEVEL * w_current)446 int o_select_selected(GSCHEM_TOPLEVEL *w_current)
447 {
448 TOPLEVEL *toplevel = w_current->toplevel;
449 if ( geda_list_get_glist( toplevel->page_current->selection_list )) {
450 return(TRUE);
451 }
452 return(FALSE);
453 }
454
455
456 /*! \todo Finish function documentation!!!
457 * \brief
458 * \par Function Description
459 *
460 */
o_select_unselect_all(GSCHEM_TOPLEVEL * w_current)461 void o_select_unselect_all(GSCHEM_TOPLEVEL *w_current)
462 {
463 TOPLEVEL *toplevel = w_current->toplevel;
464 SELECTION *selection = toplevel->page_current->selection_list;
465 GList *removed = NULL;
466 GList *iter;
467
468 removed = g_list_copy (geda_list_get_glist (selection));
469 for (iter = removed; iter != NULL; iter = g_list_next (iter)) {
470 o_selection_remove (toplevel, selection, (OBJECT *) iter->data);
471 }
472
473 /* Call hooks */
474 if (removed != NULL) {
475 g_run_hook_object_list (w_current, "%deselect-objects-hook", removed);
476 }
477 }
478
479 /*! \brief Selects all visible objects on the current page.
480 * \par Function Description
481 * Clears any existing selection, then selects everything visible and
482 * unlocked on the current page, and any attached attributes whether
483 * visible or invisible..
484 *
485 * \param w_current The current #GSCHEM_TOPLEVEL structure.
486 */
487 void
o_select_visible_unlocked(GSCHEM_TOPLEVEL * w_current)488 o_select_visible_unlocked (GSCHEM_TOPLEVEL *w_current)
489 {
490 TOPLEVEL *toplevel = w_current->toplevel;
491 SELECTION *selection = toplevel->page_current->selection_list;
492 const GList *iter;
493 GList *added;
494
495 o_select_unselect_all (w_current);
496 for (iter = s_page_objects (toplevel->page_current);
497 iter != NULL;
498 iter = g_list_next (iter)) {
499 OBJECT *obj = (OBJECT *) iter->data;
500
501 /* Skip invisible objects. */
502 if (!o_is_visible (toplevel, obj) && !toplevel->show_hidden_text)
503 continue;
504
505 /* Skip locked objects. */
506 if (!obj->selectable) continue;
507
508 /* Add object to selection. */
509 /*! \bug We can't call o_select_object() because it
510 * behaves differently depending on the state of
511 * w_current->SHIFTKEY and w_current->CONTROLKEY, which may well
512 * be set if this function is called via a keystroke
513 * (e.g. Ctrl-A). */
514 o_selection_add (toplevel, selection, obj);
515
516 /* Add any attributes of object to selection as well. */
517 o_attrib_add_selected (w_current, selection, obj);
518 }
519
520 /* Run hooks for all items selected */
521 added = geda_list_get_glist (selection);
522 if (added != NULL) {
523 g_run_hook_object_list (w_current, "%select-objects-hook", added);
524 }
525 }
526
527 /*! \todo Finish function documentation!!!
528 * \brief
529 * \par Function Description
530 *
531 */
532 void
o_select_move_to_place_list(GSCHEM_TOPLEVEL * w_current)533 o_select_move_to_place_list(GSCHEM_TOPLEVEL *w_current)
534 {
535 TOPLEVEL *toplevel = w_current->toplevel;
536 GList *selection;
537 GList *selection_copy;
538
539 /* remove the old place list if it exists */
540 s_delete_object_glist(toplevel, toplevel->page_current->place_list);
541 toplevel->page_current->place_list = NULL;
542
543 selection = geda_list_get_glist( toplevel->page_current->selection_list );
544 selection_copy = g_list_copy( selection );
545 toplevel->page_current->place_list = selection_copy;
546 }
547