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