1 /* Lepton EDA Schematic Capture
2  * Copyright (C) 1998-2010 Ales Hvezda
3  * Copyright (C) 1998-2016 gEDA Contributors
4  * Copyright (C) 2017-2021 Lepton EDA Contributors
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 
21 #include <config.h>
22 
23 #include <stdio.h>
24 #include <ctype.h>
25 #ifdef HAVE_STDLIB_H
26 #include <stdlib.h>
27 #endif
28 #ifdef HAVE_STRING_H
29 #include <string.h>
30 #endif
31 
32 #include "gschem.h"
33 #include <gdk/gdkkeysyms.h>
34 
35 #define GLADE_HOOKUP_OBJECT(component,widget,name) \
36   g_object_set_data_full (G_OBJECT (component), name, \
37     g_object_ref (widget), (GDestroyNotify) g_object_unref)
38 
39 /** @brief How many entries to keep in the "Search text" combo box. */
40 #define HISTORY_LENGTH 15
41 
42 /* autonumber text structs and enums */
43 enum {
44   AUTONUMBER_SORT_DIAGONAL,
45   AUTONUMBER_SORT_YX,
46   AUTONUMBER_SORT_YX_REV,
47   AUTONUMBER_SORT_XY,
48   AUTONUMBER_SORT_XY_REV,
49   AUTONUMBER_SORT_FILE
50 };
51 
52 enum {
53   AUTONUMBER_IGNORE,
54   AUTONUMBER_RENUMBER,
55   AUTONUMBER_RESPECT
56 };
57 
58 enum {
59   SCOPE_SELECTED,
60   SCOPE_PAGE,
61   SCOPE_HIERARCHY
62 };
63 
64 typedef struct autonumber_text_t AUTONUMBER_TEXT;
65 
66 /** @brief Stored state of the autonumber text dialog */
67 struct autonumber_text_t {
68   /** @brief Search text history */
69   GList *scope_text;
70 
71   /** @brief Scope for searching existing numbers */
72   gint scope_skip;
73 
74   /** @brief Scope for autonumbering text */
75   gint scope_number;
76 
77   /** @brief Overwrite existing numbers in scope */
78   gboolean scope_overwrite;
79 
80   /** @brief Sort order */
81   gint order;
82 
83   /** @brief Starting number for automatic numbering */
84   gint startnum;
85 
86   /** @brief Remove numbers instead of automatic numbering */
87   gboolean removenum;
88 
89   /** @brief Automatic assignments of slots */
90   gboolean slotting;
91 
92   /** @brief Pointer to the dialog */
93   GtkWidget *dialog;
94 
95   /** @brief Pointer to the GschemToplevel struct */
96   GschemToplevel *w_current;
97 
98   /* variables used while autonumbering */
99   gchar * current_searchtext;
100   gint root_page;      /* flag whether its the root page or not */
101   GList *used_numbers; /* list of used numbers */
102   GList *free_slots;   /* list of FREE_SLOT objects */
103   GList *used_slots;   /* list of USED_SLOT objects */
104 };
105 
106 typedef struct autonumber_slot_t AUTONUMBER_SLOT;
107 
108 struct autonumber_slot_t {
109   gchar *symbolname;     /* or should I use the device name? (Werner) */
110   gint number;           /* usually this is the refdes number */
111   gint slotnr;      /* just the number of the free slot */
112 };
113 
114 
115 
116 /* ***** BACK-END CODE ***************************************************** */
117 
118 /********** compare functions for g_list_sort, ... ***********************/
119 /*! \brief GCompareFunc function to sort a list with g_list_sort().
120  *  \par Function Description
121  *  Compares the integer values of the gconstpointers a and b.
122  *  \return -1 if a<b, 1 if a>b, 0 if a==b
123  */
autonumber_sort_numbers(gconstpointer a,gconstpointer b)124 gint autonumber_sort_numbers(gconstpointer a, gconstpointer b) {
125   if (GPOINTER_TO_INT(a) < GPOINTER_TO_INT(b))
126     return -1;
127   if (GPOINTER_TO_INT(a) > GPOINTER_TO_INT(b))
128     return 1;
129   return 0;
130 }
131 
132 /*! \brief GCompareFunc function to sort text objects by there location
133  *  \par Function Description
134  *  This function takes two <B>LeptonObject*</B> arguments and compares the
135  *  location of the two text objects. The first sort criteria is the x location,
136  *  the second sort criteria is the y location.
137  *  The Function is used as GCompareFunc by g_list_sort().
138  */
autonumber_sort_xy(gconstpointer a,gconstpointer b)139 gint autonumber_sort_xy(gconstpointer a, gconstpointer b) {
140   LeptonObject *aa, *bb;
141   aa=(LeptonObject *) a;  bb=(LeptonObject *) b;
142   if (lepton_text_object_get_x (aa) < lepton_text_object_get_x (bb))
143     return -1;
144   if (lepton_text_object_get_x (aa) > lepton_text_object_get_x (bb))
145     return 1;
146   /* x == x */
147   if (lepton_text_object_get_y (aa) > lepton_text_object_get_y (bb))
148     return -1;
149   if (lepton_text_object_get_y (aa) < lepton_text_object_get_y (bb))
150     return 1;
151   return 0;
152 }
153 
154 /*! \brief GCompareFunc function to sort text objects by there location
155  *  \par Function Description
156  *  This function takes two <B>LeptonObject*</B> arguments and compares the
157  *  location of the two text objects. The first sort criteria is the x location,
158  *  the second sort criteria is the y location.
159  *  This function sorts the objects in reverse order.
160  *  The function is used as GCompareFunc by g_list_sort().
161  */
autonumber_sort_xy_rev(gconstpointer a,gconstpointer b)162 gint autonumber_sort_xy_rev(gconstpointer a, gconstpointer b) {
163   LeptonObject *aa, *bb;
164   aa=(LeptonObject *) a;  bb=(LeptonObject *) b;
165   if (lepton_text_object_get_x (aa) < lepton_text_object_get_x (bb))
166     return 1;
167   if (lepton_text_object_get_x (aa) > lepton_text_object_get_x (bb))
168     return -1;
169   /* x == x */
170   if (lepton_text_object_get_y (aa) < lepton_text_object_get_y (bb))
171     return 1;
172   if (lepton_text_object_get_y (aa) > lepton_text_object_get_y (bb))
173     return -1;
174   return 0;
175 }
176 
177 /*! \brief GCompareFunc function to sort text objects by there location
178  *  \par Function Description
179  *  This function takes two <B>LeptonObject*</B> arguments and compares the
180  *  location of the two text objects. The first sort criteria is the y location,
181  *  the second sort criteria is the x location.
182  *  The function is used as GCompareFunc by g_list_sort().
183  */
autonumber_sort_yx(gconstpointer a,gconstpointer b)184 int autonumber_sort_yx(gconstpointer a, gconstpointer b) {
185   LeptonObject *aa, *bb;
186   aa=(LeptonObject *) a;  bb=(LeptonObject *) b;
187   if (lepton_text_object_get_y (aa) > lepton_text_object_get_y (bb))
188     return -1;
189   if (lepton_text_object_get_y (aa) < lepton_text_object_get_y (bb))
190     return 1;
191   /* y == y */
192   if (lepton_text_object_get_x (aa) < lepton_text_object_get_x (bb))
193     return -1;
194   if (lepton_text_object_get_x (aa) > lepton_text_object_get_x (bb))
195     return 1;
196   return 0;
197 }
198 
199 /*! \brief GCompareFunc function to sort text objects by there location
200  *  \par Function Description
201  *  This function takes two <B>LeptonObject*</B> arguments and compares the
202  *  location of the two text objects. The first sort criteria is the y location,
203  *  the second sort criteria is the x location.
204  *  This function sorts the objects in reverse order.
205  *  The function is used as GCompareFunc by g_list_sort().
206  */
autonumber_sort_yx_rev(gconstpointer a,gconstpointer b)207 int autonumber_sort_yx_rev(gconstpointer a, gconstpointer b) {
208   LeptonObject *aa, *bb;
209   aa=(LeptonObject *) a;  bb=(LeptonObject *) b;
210   if (lepton_text_object_get_y (aa) > lepton_text_object_get_y (bb))
211     return 1;
212   if (lepton_text_object_get_y (aa) < lepton_text_object_get_y (bb))
213     return -1;
214   /* y == y */
215   if (lepton_text_object_get_x (aa) > lepton_text_object_get_x (bb))
216     return 1;
217   if (lepton_text_object_get_x (aa) < lepton_text_object_get_x (bb))
218     return -1;
219   return 0;
220 }
221 
222 /*! \brief GCompareFunc function to sort text objects by there location
223  *  \par Function Description
224  *  This function takes two <B>LeptonObject*</B> arguments and compares the
225  *  location of the two text objects. The sort criteria is the combined x- and the
226  *  y-location. The function sorts from top left to bottom right.
227  *  The function is used as GCompareFunc by g_list_sort().
228  */
autonumber_sort_diagonal(gconstpointer a,gconstpointer b)229 int autonumber_sort_diagonal(gconstpointer a, gconstpointer b) {
230   LeptonObject *aa, *bb;
231   aa=(LeptonObject *) a;  bb=(LeptonObject *) b;
232   if (lepton_text_object_get_x (aa) - lepton_text_object_get_y (aa) <
233       lepton_text_object_get_x (bb) - lepton_text_object_get_y (bb))
234     return -1;
235   if (lepton_text_object_get_x (aa) - lepton_text_object_get_y (aa) >
236       lepton_text_object_get_x (bb) - lepton_text_object_get_y (bb))
237     return 1;
238   return 0;
239 }
240 
241 /*! \brief GCompareFunc function to acces <B>AUTONUMBER_SLOT</B> object in a GList
242  *  \par Function Description
243  *  This function takes two <B>AUTONUMBER_SLOT*</B> arguments and compares them.
244  *  Sorting criteria is are the AUTONUMBER_SLOT members: first the symbolname, than the
245  *  number and last the slotnr.
246  *  If the number or the slotnr is set to zero it acts as a wildcard.
247  *  The function is used as GCompareFunc by GList functions.
248  */
freeslot_compare(gconstpointer a,gconstpointer b)249 gint freeslot_compare(gconstpointer a, gconstpointer b)
250 {
251   AUTONUMBER_SLOT *aa, *bb;
252   gint res;
253   aa = (AUTONUMBER_SLOT *) a;  bb = (AUTONUMBER_SLOT *) b;
254 
255   if ((res = strcmp(aa->symbolname, bb->symbolname)) != 0)
256     return res;
257 
258   /* aa->symbolname == bb->symbolname */
259   if (aa->number == 0 || bb->number == 0)
260     return 0;
261   if (aa->number > bb->number)
262     return 1;
263   if (aa->number < bb->number)
264     return -1;
265 
266   /* aa->number == bb->number */
267   if (aa->slotnr == 0 || bb->slotnr == 0)
268     return 0;
269   if (aa->slotnr > bb->slotnr)
270     return 1;
271   if (aa->slotnr < bb->slotnr)
272     return -1;
273 
274   return 0;
275 }
276 
277 /*! \brief Prints a <B>GList</B> of <B>AUTONUMBER_SLOT</B> elements
278  *  \par Function Description
279  *  This function prints the elements of a GList that contains <B>AUTONUMBER_SLOT</B> elements
280  *  It is only used for debugging purposes.
281  */
freeslot_print(GList * list)282 void freeslot_print(GList *list) {
283   GList *item;
284   AUTONUMBER_SLOT *fs;
285 
286   printf("freeslot_print(): symname, number, slot\n");
287   for (item = list; item != NULL; item = g_list_next(item)) {
288     fs = (AUTONUMBER_SLOT*) item ->data;
289     printf("  %s, %d, %d\n",fs->symbolname, fs->number, fs->slotnr);
290   }
291 }
292 
293 
294 /*! \brief Function to clear the databases of used parts
295  *  \par Function Descriptions
296  *  Just remove the list of used numbers, used slots and free slots.
297  */
autonumber_clear_database(AUTONUMBER_TEXT * autotext)298 void autonumber_clear_database (AUTONUMBER_TEXT *autotext)
299 {
300   /* cleanup everything for the next searchtext */
301   if (autotext->used_numbers != NULL) {
302     g_list_free(autotext->used_numbers);
303     autotext->used_numbers = NULL;
304   }
305   if (autotext->free_slots != NULL) {
306     g_list_foreach(autotext->free_slots, (GFunc) g_free, NULL);
307     g_list_free(autotext->free_slots);
308     autotext->free_slots = NULL;
309   }
310   if (autotext->used_slots != NULL) {
311     g_list_foreach(autotext->used_slots, (GFunc) g_free, NULL);
312     g_list_free(autotext->used_slots);
313     autotext->used_slots = NULL;
314   }
315 }
316 
317 /*! \brief Function to test, whether the LeptonObject matches the autotext criterias
318  *  \par Function Description
319  *  The criterias are those of the autonumber text dialog. The function decides
320  *  whether the <B>LeptonObject</B> has to be renumberd, ignored or taken care of when
321  *  renumbering all other objects.
322  *  \return one of these integer values: <B>AUTONUMBER_IGNORE</B>,
323  *  <B>AUTONUMBER_RESPECT</B> or <B>AUTONUMBER_RENUMBER</B> and the current number
324  *  of the text object in <B>*number</B>.
325  */
autonumber_match(AUTONUMBER_TEXT * autotext,LeptonObject * o_current,gint * number)326 gint autonumber_match(AUTONUMBER_TEXT *autotext, LeptonObject *o_current, gint *number)
327 {
328   gint i, isnumbered=1;
329   size_t len;
330   const gchar *str = NULL;
331 
332   len = strlen(autotext->current_searchtext);
333   /* first find out whether we can ignore that object */
334   if (!lepton_object_is_text (o_current))  /* text object */
335     return AUTONUMBER_IGNORE;
336 
337   str = lepton_text_object_get_string (o_current);
338 
339   if (!(strlen(str) - len > 0)
340       || !g_str_has_prefix(str, autotext->current_searchtext))
341     return AUTONUMBER_IGNORE;
342 
343   /* the string object matches with its leading characters to the searchtext */
344   /* now look for the extension, either a number or the "?" */
345   if (g_str_has_suffix (str,"?")) {
346     isnumbered = 0;
347     /* There must not be any character between the "?" and the searchtext */
348     if (strlen(str) != len+1)
349       return AUTONUMBER_IGNORE;
350   }
351   else {
352     if (!isdigit( (int) (str[len]) )) /* has at least one digit */
353       return AUTONUMBER_IGNORE;
354 
355     for (i=len+1; str[i]; i++) /* and only digits */
356       if (!isdigit( (int) (str[i]) ))
357         return AUTONUMBER_IGNORE;
358   }
359 
360   /* we have six cases, 3 from focus multiplied by 2 selection cases */
361   if ((autotext->root_page || autotext->scope_number == SCOPE_HIERARCHY)
362       && (o_current->selected
363           || autotext->scope_number == SCOPE_HIERARCHY || autotext->scope_number == SCOPE_PAGE)
364       && (!isnumbered || (autotext->scope_overwrite)))
365     return AUTONUMBER_RENUMBER;
366 
367   if (isnumbered
368       && !(autotext->scope_skip == SCOPE_SELECTED
369            && !(o_current->selected)  && autotext->root_page)) {
370     sscanf(&(str[len])," %d", number);
371     return AUTONUMBER_RESPECT; /* numbered objects which we don't renumber */
372   }
373   else
374     return AUTONUMBER_IGNORE;  /* unnumbered objects outside the focus */
375 }
376 
377 
378 /*! \brief Creates a list of already numbered objects and slots
379  *  \par Function Description
380  *  This function collects the used numbers of a single schematic page.
381  *  The used element numbers are stored in a GList container
382  *  inside the <B>AUTONUMBER_TEXT</B> struct.
383  *  The slotting container is a little bit different. It stores free slots of
384  *  multislotted symbols, that were used only partially.
385  *  The criterias are derivated from the autonumber dialog entries.
386  */
autonumber_get_used(GschemToplevel * w_current,AUTONUMBER_TEXT * autotext)387 void autonumber_get_used(GschemToplevel *w_current, AUTONUMBER_TEXT *autotext)
388 {
389   gint number, numslots, slotnr, i;
390   LeptonObject *o_current, *o_parent;
391   AUTONUMBER_SLOT *slot;
392   GList *slot_item;
393   char *numslot_str, *slot_str;
394   const GList *iter;
395 
396   LeptonPage *active_page = schematic_window_get_active_page (w_current);
397 
398   for (iter = lepton_page_objects (active_page);
399        iter != NULL;
400        iter = g_list_next (iter)) {
401     o_current = (LeptonObject*) iter->data;
402     if (autonumber_match(autotext, o_current, &number) == AUTONUMBER_RESPECT) {
403       /* check slot and maybe add it to the lists */
404       o_parent = lepton_object_get_attached_to (o_current);
405       if (autotext->slotting && o_parent != NULL) {
406         /* check for slotted symbol */
407         numslot_str =
408           o_attrib_search_object_attribs_by_name (o_parent, "numslots", 0);
409         if (numslot_str != NULL) {
410           sscanf(numslot_str," %d",&numslots);
411           g_free(numslot_str);
412 
413           if (numslots > 0) {
414             slot_str = o_attrib_search_object_attribs_by_name (o_parent, "slot", 0);
415             if (slot_str == NULL) {
416               g_message (_("slotted object without slot attribute may cause "
417                            "problems when autonumbering slots"));
418             }
419             else {
420               sscanf(slot_str, " %d", &slotnr);
421               slot = g_new(AUTONUMBER_SLOT,1);
422               slot->number = number;
423               slot->slotnr = slotnr;
424               slot->symbolname = lepton_component_object_get_basename (o_parent);
425 
426 
427               slot_item = g_list_find_custom(autotext->used_slots,
428                                              slot,
429                                              (GCompareFunc) freeslot_compare);
430               if (slot_item != NULL) { /* duplicate slot in used_slots */
431                 g_message (_("duplicate slot may cause problems: "
432                              "[symbolname=%1$s, number=%2$d, slot=%3$d]"),
433                            slot->symbolname, slot->number, slot->slotnr);
434                 g_free(slot);
435               }
436               else {
437                 autotext->used_slots = g_list_insert_sorted(autotext->used_slots,
438                                                             slot,
439                                                             (GCompareFunc) freeslot_compare);
440 
441                 slot_item = g_list_find_custom(autotext->free_slots,
442                                                slot,
443                                                (GCompareFunc) freeslot_compare);
444                 if (slot_item == NULL) {
445                   /* insert all slots to the list, except of the current one */
446                   for (i=1; i <= numslots; i++) {
447                     if (i != slotnr) {
448                       slot = (AUTONUMBER_SLOT*) g_memdup (slot, sizeof (AUTONUMBER_SLOT));
449                       slot->slotnr = i;
450                       autotext->free_slots = g_list_insert_sorted(autotext->free_slots,
451                                                                   slot,
452                                                                   (GCompareFunc) freeslot_compare);
453                     }
454                   }
455                 }
456                 else {
457                   g_free(slot_item->data);
458                   autotext->free_slots = g_list_delete_link(autotext->free_slots, slot_item);
459                 }
460               }
461             }
462           }
463         }
464       }
465       /* put number into the used list */
466       autotext->used_numbers = g_list_insert_sorted(autotext->used_numbers,
467                                                     GINT_TO_POINTER(number),
468                                                     (GCompareFunc) autonumber_sort_numbers);
469     }
470   }
471 }
472 
473 
474 /*! \brief Gets or generates free numbers for the autonumbering process.
475  *  \par Function Description
476  *  This function gets or generates new numbers for the <B>LeptonObject o_current</B>.
477  *  It uses the element numbers <B>used_numbers</B> and the list of the free slots
478  *  <B>free_slots</B> of the <B>AUTONUMBER_TEXT</B> struct.
479  *  \return
480  *  The new number is returned into the <B>number</B> parameter.
481  *  <B>slot</B> is set if autoslotting is active, else it is set to zero.
482  */
autonumber_get_new_numbers(AUTONUMBER_TEXT * autotext,LeptonObject * o_current,gint * number,gint * slot)483 void autonumber_get_new_numbers(AUTONUMBER_TEXT *autotext, LeptonObject *o_current,
484                                 gint *number, gint *slot)
485 {
486   GList *item;
487   gint new_number, numslots, i;
488   AUTONUMBER_SLOT *freeslot;
489   LeptonObject *o_parent = NULL;
490   GList *freeslot_item;
491   gchar *numslot_str;
492 
493   new_number = autotext->startnum;
494 
495   /* Check for slots first */
496   /* 1. are there any unused slots in the database? */
497   o_parent = lepton_object_get_attached_to (o_current);
498   if (autotext->slotting && o_parent != NULL) {
499     freeslot = g_new(AUTONUMBER_SLOT,1);
500     freeslot->symbolname = lepton_component_object_get_basename (o_parent);
501     freeslot->number = 0;
502     freeslot->slotnr = 0;
503     freeslot_item = g_list_find_custom(autotext->free_slots,
504                                        freeslot,
505                                        (GCompareFunc) freeslot_compare);
506     g_free(freeslot);
507     /* Yes! -> remove from database, apply it */
508     if (freeslot_item != NULL) {
509       freeslot = (AUTONUMBER_SLOT*) freeslot_item->data;
510       *number = freeslot->number;
511       *slot = freeslot->slotnr;
512       g_free(freeslot);
513       autotext->free_slots = g_list_delete_link(autotext->free_slots, freeslot_item);
514 
515       return;
516     }
517   }
518 
519   /* get a new number */
520   item = autotext->used_numbers;
521   while (1) {
522     while (item != NULL && GPOINTER_TO_INT(item->data) < new_number)
523       item = g_list_next(item);
524 
525     if (item == NULL || GPOINTER_TO_INT(item->data) > new_number)
526       break;
527     else  /* new_number == item->data */
528       new_number++;
529   }
530   *number = new_number;
531   *slot = 0;
532 
533   /* insert the new number to the used list */
534   autotext->used_numbers = g_list_insert_sorted(autotext->used_numbers,
535                                                 GINT_TO_POINTER(new_number),
536                                                 (GCompareFunc) autonumber_sort_numbers);
537 
538   /* 3. is o_current a slotted object ? */
539   if ((autotext->slotting) && o_parent != NULL) {
540     numslot_str =
541       o_attrib_search_object_attribs_by_name (o_parent, "numslots", 0);
542     if (numslot_str != NULL) {
543       sscanf(numslot_str," %d",&numslots);
544       g_free(numslot_str);
545       if (numslots > 0) {
546         /* Yes! -> new number and slot=1; add the other slots to the database */
547         *slot = 1;
548         for (i=2; i <=numslots; i++) {
549           freeslot = g_new(AUTONUMBER_SLOT,1);
550           freeslot->symbolname = lepton_component_object_get_basename (o_parent);
551           freeslot->number = new_number;
552           freeslot->slotnr = i;
553           autotext->free_slots = g_list_insert_sorted(autotext->free_slots,
554                                                       freeslot,
555                                                       (GCompareFunc) freeslot_compare);
556         }
557       }
558     }
559   }
560 }
561 
562 /** @brief Removes the number from the element.
563  *
564  *  This function updates the text content of the \a o_current object.
565  *
566  *  @param autotext Pointer to the state structure
567  *  @param o_current Pointer to the object from which to remove the number
568  *
569  */
autonumber_remove_number(AUTONUMBER_TEXT * autotext,LeptonObject * o_current)570 void autonumber_remove_number(AUTONUMBER_TEXT * autotext, LeptonObject *o_current)
571 {
572   LeptonObject *o_parent, *o_slot;
573   gchar *slot_str;
574   gchar *str = NULL;
575 
576   /* replace old text */
577   str = g_strdup_printf("%s?", autotext->current_searchtext);
578   lepton_text_object_set_string (o_current, str);
579   g_free (str);
580 
581   /* remove the slot attribute if slotting is active */
582   if (autotext->slotting) {
583     /* get the slot attribute */
584     o_parent = lepton_object_get_attached_to (o_current);
585     if (o_parent != NULL) {
586       slot_str = s_slot_search_slot (o_parent, &o_slot);
587       g_free (slot_str);
588       /* Only attempt to remove non-inherited slot attributes */
589       if (o_slot != NULL && !o_attrib_is_inherited (o_slot)) {
590         /* delete the slot attribute */
591         o_delete (autotext->w_current, o_slot);
592       }
593     }
594   }
595 
596   schematic_window_active_page_changed (autotext->w_current);
597 }
598 
599 /*! \brief Changes the number <B>LeptonObject</B> element. Changes the slot attribute.
600  *  \par Function Description
601  *  This function updates the text content of the <B>o_current</B> object.
602  *  If the <B>slot</B> value is not zero. It updates the slot attribut of the
603  *  component element that is also the parent object of the o_current element.
604  */
autonumber_apply_new_text(AUTONUMBER_TEXT * autotext,LeptonObject * o_current,gint number,gint slot)605 void autonumber_apply_new_text(AUTONUMBER_TEXT * autotext, LeptonObject *o_current,
606                                gint number, gint slot)
607 {
608   char *str;
609 
610   if (slot != 0) {
611     /* update the slot on the owning object */
612     str = g_strdup_printf ("slot=%d", slot);
613     o_slot_end (autotext->w_current,
614                 lepton_object_get_attached_to (o_current),
615                 str);
616     g_free (str);
617   }
618 
619   /* replace old text */
620   str = g_strdup_printf("%s%d", autotext->current_searchtext, number);
621   lepton_text_object_set_string (o_current, str);
622   g_free (str);
623 
624   schematic_window_active_page_changed (autotext->w_current);
625 }
626 
627 
628 /*! \brief Handles all the options of the autonumber text dialog
629  *  \par Function Description
630  *  This function is the master of all autonumber code. It receives the options of
631  *  the the autonumber text dialog in an <B>AUTONUMBER_TEXT</B> structure.
632  *  First it collects all pages of a hierarchical schematic.
633  *  Second it gets all matching text elements for the searchtext.
634  *  Then it renumbers all text elements of all schematic pages. The renumbering
635  *  follows the rules of the parameters given in the autonumber text dialog.
636  */
autonumber_text_autonumber(AUTONUMBER_TEXT * autotext)637 void autonumber_text_autonumber(AUTONUMBER_TEXT *autotext)
638 {
639   GList *pages;
640   GList *searchtext_list=NULL;
641   GList *text_item, *obj_item, *page_item;
642   LeptonObject *o_current;
643   GschemToplevel *w_current;
644   gchar *searchtext;
645   gchar *scope_text;
646   gchar *new_searchtext;
647   gint number, slot;
648   size_t i;
649   GList *o_list = NULL;
650   const GList *iter;
651   LeptonPage *active_page = NULL;
652 
653   w_current = autotext->w_current;
654   active_page = schematic_window_get_active_page (w_current);
655   autotext->current_searchtext = NULL;
656   autotext->root_page = 1;
657   autotext->used_numbers = NULL;
658   autotext->free_slots = NULL;
659   autotext->used_slots = NULL;
660 
661   scope_text = (gchar*) g_list_first(autotext->scope_text)->data;
662 
663   /* Step1: get all pages of the hierarchy */
664   pages = s_hierarchy_traversepages (w_current,
665                                      active_page,
666                                      HIERARCHY_NODUPS);
667 
668   /*  g_list_foreach(pages, (GFunc) s_hierarchy_print_page, NULL); */
669 
670   /* Step2: if searchtext has an asterisk at the end we have to find
671      all matching searchtextes.
672 
673      Example:  "refdes=*" will match each text that starts with "refdes="
674      and has a trailing "?" or a trailing number if the "all"-option is set.
675      We get a list of possible prefixes: refdes=R, refdes=C.
676 
677      If there is only one search pattern, it becomes a single item
678      in the searchtext list */
679 
680   if (strlen(scope_text) == 0) {
681     g_message (_("No search string given in autonumber text."));
682     return; /* error */
683   }
684   else if (g_str_has_suffix(scope_text,"?") == TRUE) {
685     /* single searchtext, strip of the "?" */
686     searchtext = g_strndup(scope_text, strlen(scope_text)-1);
687     searchtext_list=g_list_append (searchtext_list, searchtext);
688   }
689   else if (g_str_has_suffix(scope_text,"*") == TRUE) {
690     /* strip of the "*" */
691     searchtext = g_strndup(scope_text, strlen(scope_text)-1);
692     /* collect all the possible searchtexts in all pages of the hierarchy */
693     for (page_item = pages; page_item != NULL; page_item = g_list_next(page_item)) {
694       lepton_toplevel_goto_page (w_current->toplevel, (LeptonPage*) page_item->data);
695       gschem_toplevel_page_changed (w_current);
696       /* iterate over all objects an look for matching searchtext's */
697       for (iter = lepton_page_objects (active_page);
698            iter != NULL;
699            iter = g_list_next (iter)) {
700         o_current = (LeptonObject*) iter->data;
701         if (lepton_object_is_text (o_current))
702         {
703           if (autotext->scope_number == SCOPE_HIERARCHY
704               || autotext->scope_number == SCOPE_PAGE
705               || ((autotext->scope_number == SCOPE_SELECTED) && (o_current->selected))) {
706             const gchar *str = lepton_text_object_get_string (o_current);
707             if (g_str_has_prefix (str, searchtext)) {
708               /* the beginnig of the current text matches with the searchtext now */
709               /* strip of the trailing [0-9?] chars and add it too the searchtext */
710               for (i = strlen (str)-1;
711                    (i >= strlen(searchtext))
712                      && (str[i] == '?'
713                          || isdigit( (int) (str[i]) ));
714                    i--)
715                 ; /* void */
716 
717               new_searchtext = g_strndup (str, i+1);
718               if (g_list_find_custom(searchtext_list, new_searchtext,
719                                      (GCompareFunc) strcmp) == NULL ) {
720                 searchtext_list = g_list_append(searchtext_list, new_searchtext);
721               }
722               else {
723                 g_free(new_searchtext);
724               }
725             }
726           }
727         }
728       }
729       if (autotext->scope_number == SCOPE_SELECTED || autotext->scope_number == SCOPE_PAGE)
730         break; /* search only in the first page */
731     }
732     g_free(searchtext);
733   }
734   else {
735     g_message (_("No '*' or '?' given at the end of the autonumber text."));
736     return;
737   }
738 
739   /* Step3: iterate over the search items in the list */
740   for (text_item=searchtext_list; text_item !=NULL; text_item=g_list_next(text_item)) {
741     autotext->current_searchtext = (gchar*) text_item->data;
742     /* printf("autonumber_text_autonumber: searchtext %s\n", autotext->current_searchtext); */
743     /* decide whether to renumber page by page or get a global used-list */
744     if (autotext->scope_skip == SCOPE_HIERARCHY) {  /* whole hierarchy database */
745       /* renumbering all means that no db is required */
746       if (!(autotext->scope_number == SCOPE_HIERARCHY
747             && autotext->scope_overwrite)) {
748         for (page_item = pages; page_item != NULL; page_item = g_list_next(page_item)) {
749           autotext->root_page = (pages->data == page_item->data);
750           lepton_toplevel_goto_page (w_current->toplevel,
751                                      (LeptonPage*) page_item->data);
752           gschem_toplevel_page_changed (w_current);
753           autonumber_get_used(w_current, autotext);
754         }
755       }
756     }
757 
758     /* renumber the elements */
759     for (page_item = pages; page_item != NULL; page_item = g_list_next(page_item)) {
760       lepton_toplevel_goto_page (w_current->toplevel,
761                                  (LeptonPage*) page_item->data);
762       gschem_toplevel_page_changed (w_current);
763       autotext->root_page = (pages->data == page_item->data);
764       /* build a page database if we're numbering pagebypage or selection only*/
765       if (autotext->scope_skip == SCOPE_PAGE || autotext->scope_skip == SCOPE_SELECTED) {
766         autonumber_get_used(w_current, autotext);
767       }
768 
769       /* RENUMBER CODE FOR ONE PAGE AND ONE SEARCHTEXT*/
770       /* 1. get objects to renumber */
771       for (iter = lepton_page_objects (active_page);
772            iter != NULL;
773            iter = g_list_next (iter)) {
774         o_current = (LeptonObject*) iter->data;
775         if (autonumber_match(autotext, o_current, &number) == AUTONUMBER_RENUMBER) {
776           /* put number into the used list */
777           o_list = g_list_append(o_list, o_current);
778         }
779       }
780 
781       /* 2. sort object list */
782       switch (autotext->order) {
783       case AUTONUMBER_SORT_YX:
784         o_list=g_list_sort(o_list, autonumber_sort_yx);
785         break;
786       case AUTONUMBER_SORT_YX_REV:
787         o_list=g_list_sort(o_list, autonumber_sort_yx_rev);
788         break;
789       case AUTONUMBER_SORT_XY:
790         o_list=g_list_sort(o_list, autonumber_sort_xy);
791         break;
792       case AUTONUMBER_SORT_XY_REV:
793         o_list=g_list_sort(o_list, autonumber_sort_xy_rev);
794         break;
795       case AUTONUMBER_SORT_DIAGONAL:
796         o_list=g_list_sort(o_list, autonumber_sort_diagonal);
797         break;
798       default:
799         ; /* unsorted file order */
800       }
801 
802       /* 3. renumber/reslot the objects */
803       for(obj_item=o_list; obj_item != NULL; obj_item=g_list_next(obj_item)) {
804         o_current = (LeptonObject*) obj_item->data;
805         if(autotext->removenum) {
806           autonumber_remove_number(autotext, o_current);
807         } else {
808           /* get valid numbers from the database */
809           autonumber_get_new_numbers(autotext, o_current, &number, &slot);
810           /* and apply it. TODO: join these two functions */
811           autonumber_apply_new_text(autotext, o_current, number, slot);
812         }
813       }
814       g_list_free(o_list);
815       o_list = NULL;
816 
817       /* destroy the page database */
818       if (autotext->scope_skip == SCOPE_PAGE
819           || autotext->scope_skip == SCOPE_SELECTED)
820         autonumber_clear_database(autotext);
821 
822       if (autotext->scope_number == SCOPE_SELECTED
823           || autotext->scope_number == SCOPE_PAGE)
824         break; /* only renumber the parent page (the first page) */
825     }
826     autonumber_clear_database(autotext);   /* cleanup */
827   }
828 
829   /* cleanup and redraw all*/
830   g_list_foreach(searchtext_list, (GFunc) g_free, NULL);
831   g_list_free(searchtext_list);
832   lepton_toplevel_goto_page (w_current->toplevel,
833                              (LeptonPage*) pages->data); /* go back to the root page */
834   gschem_toplevel_page_changed (w_current);
835   gschem_page_view_invalidate_all (gschem_toplevel_get_current_page_view (w_current));
836   g_list_free(pages);
837   o_undo_savestate_old(w_current, UNDO_ALL);
838 }
839 
840 /* ***** UTILITY GUI FUNCTIONS (move to a separate file in the future?) **** */
841 
842 /** @brief Finds a widget by its name given a pointer to its parent.
843  *
844  * @param widget Pointer to the parent widget.
845  * @param widget_name Name of the widget.
846  * @return Pointer to the widget or NULL if not found. */
lookup_widget(GtkWidget * widget,const gchar * widget_name)847 GtkWidget* lookup_widget(GtkWidget *widget, const gchar *widget_name)
848 {
849   GtkWidget *found_widget;
850 
851   found_widget = (GtkWidget*) g_object_get_data(G_OBJECT(widget),
852                                                 widget_name);
853 
854   return found_widget;
855 }
856 
857 /*! \brief Put the icons and the text into the sortorder combobox
858  *  \par Function Description
859  *  Load all bitmaps for the combobox and store them together with the label
860  *  in a GtkListStore.
861  */
autonumber_sortorder_create(GschemToplevel * w_current,GtkWidget * sort_order)862 void autonumber_sortorder_create(GschemToplevel *w_current, GtkWidget *sort_order)
863 {
864   GtkListStore *store;
865   GtkTreeIter iter;
866   GtkCellRenderer *renderer;
867   GdkPixbuf *pixbuf;
868   gchar *path;
869   GError *error=NULL;
870 
871   const gchar *filenames[] =
872     {
873       "gschem-diagonal.png",
874       "gschem-top2bottom.png",
875       "gschem-bottom2top.png",
876       "gschem-left2right.png",
877       "gschem-right2left.png",
878       "gschem-fileorder.png",
879       NULL
880     };
881   const gchar *names[] =
882     {
883       N_("Diagonal"),
884       N_("Top to bottom"),
885       N_("Bottom to top"),
886       N_("Left to right"),
887       N_("Right to left"),
888       N_("File order"),
889       NULL
890     };
891   gint i;
892 
893   store = gtk_list_store_new(2, G_TYPE_STRING, GDK_TYPE_PIXBUF);
894 
895   for (i=0; filenames[i] != NULL; i++) {
896     path=g_build_filename(BITMAP_DIRECTORY,
897                           filenames[i], NULL);
898     pixbuf = gdk_pixbuf_new_from_file(path, &error);
899     g_free(path);
900     gtk_list_store_append(store, &iter);
901     gtk_list_store_set(store, &iter,
902                        0, _(names[i]),
903                        1, pixbuf ? pixbuf : NULL,
904                        -1);
905     g_clear_error (&error);
906   }
907 
908   gtk_combo_box_set_model(GTK_COMBO_BOX(sort_order), GTK_TREE_MODEL(store));
909   renderer = gtk_cell_renderer_text_new ();
910 
911   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (sort_order),
912                               renderer, TRUE);
913   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (sort_order),
914                                   renderer, "text", 0, NULL);
915   renderer = gtk_cell_renderer_pixbuf_new();
916   g_object_set(G_OBJECT(renderer), "xpad", 5, "ypad", 5, NULL);
917 
918   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (sort_order),
919                               renderer, FALSE);
920   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (sort_order),
921                                   renderer, "pixbuf", 1, NULL);
922 }
923 
924 /* ***** STATE STRUCT HANDLING (interface between GUI and backend code) **** */
925 
926 /** @brief Adds a line to the search text history list
927  *
928  * Function makes sure that: 1) There are no duplicates in the list and 2) the
929  * last search text is always at the top of the list.
930  */
autonumber_history_add(GList * history,gchar * text)931 GList *autonumber_history_add(GList *history, gchar *text)
932 {
933   /* Search for this text in history and delete it (so we don't have
934    * duplicate entries) */
935 
936   GList *cur;
937 
938   cur=history;
939   while(cur!=NULL) {
940     if (!strcmp (text, (const char*) cur->data)) {
941       history=g_list_remove_link(history, cur);
942 
943       g_free(cur->data);
944       g_list_free(cur);
945       break;
946     }
947     cur=g_list_next(cur);
948   }
949 
950   /* Add the new text at the beginning of the list */
951 
952   history=g_list_prepend(history, text);
953 
954   /* Truncate history */
955   while(g_list_length(history) > HISTORY_LENGTH) {
956     GList *last = g_list_last(history);
957 
958     history = g_list_remove_link(history, last);
959 
960     g_free(last->data);
961     g_list_free(last);
962   }
963 
964   return history;
965 }
966 
967 /** @brief Allocate and initialize the state structure
968  *
969  * @return Pointer to the allocated structure or NULL on error.
970  */
autonumber_init_state()971 AUTONUMBER_TEXT *autonumber_init_state()
972 {
973   AUTONUMBER_TEXT *autotext;
974 
975   /* Default contents of the combo box history */
976   const gchar *default_text[] = {
977     "refdes=*",
978     "refdes=C?",
979     "refdes=D?",
980     "refdes=I?",
981     "refdes=L?",
982     "refdes=Q?",
983     "refdes=R?",
984     "refdes=T?",
985     "refdes=U?",
986     "refdes=X?",
987     "netname=*",
988     "netname=A?",
989     "netname=D?",
990     NULL
991   };
992   const gchar **t;
993 
994   autotext = g_new(AUTONUMBER_TEXT, 1);
995 
996   if(autotext==NULL) return NULL;
997 
998   autotext->scope_text = NULL;
999   t = default_text;
1000   while (*t != NULL) {
1001     /* t is both an array address and the address of its first
1002        element, it post-increments after we get its value. */
1003     autotext->scope_text=g_list_append (autotext->scope_text, g_strdup (*t++));
1004   }
1005 
1006   autotext->scope_skip = SCOPE_PAGE;
1007   autotext->scope_number = SCOPE_SELECTED;
1008 
1009   autotext->scope_overwrite = 0;
1010   autotext->order = AUTONUMBER_SORT_DIAGONAL;
1011 
1012   autotext->startnum=1;
1013 
1014   autotext->removenum=0;
1015   autotext->slotting=0;
1016 
1017   autotext->dialog = NULL;
1018 
1019   return autotext;
1020 }
1021 
1022 /** @brief Restore the settings for the autonumber text dialog
1023  *
1024  * @param autotext Pointer to the state struct.
1025  */
autonumber_set_state(AUTONUMBER_TEXT * autotext)1026 void autonumber_set_state(AUTONUMBER_TEXT *autotext)
1027 {
1028   GtkWidget *widget;
1029   GtkTreeModel *model;
1030   GList *el;
1031   /* Scope */
1032 
1033   /* Search text history */
1034   widget = lookup_widget(autotext->dialog, "scope_text");
1035 
1036   /* Simple way to clear the ComboBox. Owen from #gtk+ says:
1037    *
1038    * Yeah, it's just slightly "shady" ... if you want to stick to fully
1039    * advertised API, you need to remember how many rows you added and
1040    * use gtk_combo_box_remove_text() */
1041 
1042   model = gtk_combo_box_get_model(GTK_COMBO_BOX(widget));
1043   gtk_list_store_clear(GTK_LIST_STORE(model));
1044 
1045   for (el= autotext->scope_text; el != NULL; el=g_list_next(el)) {
1046     gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget),
1047                                     (const gchar*) el->data);
1048   }
1049 
1050   widget = gtk_bin_get_child(GTK_BIN(widget));
1051   gtk_entry_set_text(GTK_ENTRY(widget), (const gchar*) g_list_first(autotext->scope_text)->data);
1052 
1053   widget = lookup_widget(autotext->dialog, "scope_skip");
1054   gtk_combo_box_set_active(GTK_COMBO_BOX(widget),
1055                            autotext->scope_skip);
1056 
1057   widget = lookup_widget(autotext->dialog, "scope_number");
1058   gtk_combo_box_set_active(GTK_COMBO_BOX(widget),
1059                            autotext->scope_number);
1060 
1061   widget = lookup_widget(autotext->dialog, "scope_overwrite");
1062   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),
1063                                autotext->scope_overwrite);
1064 
1065   /* Options */
1066   widget = lookup_widget(autotext->dialog, "opt_startnum");
1067   gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget),
1068                             autotext->startnum);
1069 
1070   widget = lookup_widget(autotext->dialog, "sort_order");
1071   gtk_combo_box_set_active(GTK_COMBO_BOX(widget), autotext->order);
1072 
1073   widget = lookup_widget(autotext->dialog, "opt_removenum");
1074   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),
1075                                autotext->removenum);
1076 
1077   widget = lookup_widget(autotext->dialog, "opt_slotting");
1078   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),
1079                                autotext->slotting);
1080 }
1081 
1082 /** @brief Get the settings from the autonumber text dialog
1083  *
1084  * Get the settings from the autonumber text dialog and store it in the
1085  * <B>AUTONUMBER_TEXT</B> structure.
1086  *
1087  * @param autotext Pointer to the state struct.
1088  */
autonumber_get_state(AUTONUMBER_TEXT * autotext)1089 void autonumber_get_state(AUTONUMBER_TEXT *autotext)
1090 {
1091   GtkWidget *widget;
1092   gchar *text;
1093 
1094   /* Scope */
1095 
1096   /* Search text history */
1097   widget = lookup_widget(autotext->dialog, "scope_text");
1098   widget = gtk_bin_get_child(GTK_BIN(widget));
1099   text = g_strdup(gtk_entry_get_text( GTK_ENTRY(widget)));
1100 
1101   autotext->scope_text=autonumber_history_add(autotext->scope_text, text);
1102 
1103   widget = lookup_widget(autotext->dialog, "scope_skip");
1104   autotext->scope_skip = gtk_combo_box_get_active( GTK_COMBO_BOX(widget) );
1105 
1106   widget = lookup_widget(autotext->dialog, "scope_number");
1107   autotext->scope_number = gtk_combo_box_get_active(GTK_COMBO_BOX(widget) );
1108 
1109   widget = lookup_widget(autotext->dialog, "scope_overwrite");
1110   autotext->scope_overwrite = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
1111 
1112   /* Sort order */
1113   widget = lookup_widget(autotext->dialog, "sort_order");
1114   autotext->order= gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
1115 
1116   /* Options */
1117   widget = lookup_widget(autotext->dialog, "opt_startnum");
1118   autotext->startnum=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
1119 
1120   widget = lookup_widget(autotext->dialog, "opt_removenum");
1121   autotext->removenum = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
1122 
1123   widget = lookup_widget(autotext->dialog, "opt_slotting");
1124   autotext->slotting = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
1125 }
1126 
1127 /* ***** CALLBACKS (functions that get called directly from the GTK) ******* */
1128 
1129 /*! \brief response  callback for the autonumber text dialog
1130  *  \par Function Description
1131  *  The function just closes the dialog if the close button is pressed or the
1132  *  user closes the dialog window.
1133  *  Triggering the apply button will call the autonumber action functions.
1134  */
autonumber_text_response(GtkWidget * widget,gint response,AUTONUMBER_TEXT * autotext)1135 void autonumber_text_response(GtkWidget * widget, gint response,
1136                               AUTONUMBER_TEXT *autotext)
1137 {
1138   switch (response) {
1139   case GTK_RESPONSE_ACCEPT:
1140     autonumber_get_state(autotext);
1141     if (autotext->removenum == TRUE && autotext->scope_overwrite == FALSE) {
1142       /* temporarly set the overwrite flag */
1143       autotext->scope_overwrite = TRUE;
1144       autonumber_text_autonumber(autotext);
1145       autotext->scope_overwrite = FALSE;
1146     }
1147     else {
1148       autonumber_text_autonumber(autotext);
1149     }
1150     break;
1151   case GTK_RESPONSE_REJECT:
1152   case GTK_RESPONSE_DELETE_EVENT:
1153     gtk_widget_destroy(autotext->dialog);
1154     autotext->dialog = NULL;
1155     break;
1156   default:
1157     printf("ERROR: autonumber_text_response(): strange signal %d\n",response);
1158   }
1159 }
1160 
1161 
1162 /** @brief Callback that activates or deactivates "overwrite existing numbers"
1163  * check box.
1164  *
1165  * This gets called each time "remove numbers" check box gets clicked.
1166  */
autonumber_removenum_toggled(GtkWidget * opt_removenum,AUTONUMBER_TEXT * autotext)1167 void autonumber_removenum_toggled(GtkWidget * opt_removenum,
1168                                   AUTONUMBER_TEXT *autotext)
1169 {
1170   GtkWidget *scope_overwrite;
1171 
1172   scope_overwrite=lookup_widget(autotext->dialog, "scope_overwrite");
1173 
1174   /* toggle activity of scope overwrite with respect to removenum */
1175   if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(opt_removenum))) {
1176     gtk_widget_set_sensitive(scope_overwrite, 0);
1177   } else {
1178     gtk_widget_set_sensitive(scope_overwrite, 1);
1179   }
1180 }
1181 
1182 
1183 /* ***** DIALOG SET-UP ***************************************************** */
1184 
1185 /** @brief Creates the autonumber text dialog.
1186  *
1187  * Dialog is not shown. No callbacks are registered. This is basically
1188  * unmodified code returned by Glade.
1189  *
1190  * Only modification was the following substitution:
1191  *
1192  * %s/create_pixmap (autonumber_text, "\(.*\)")/autonumber_create_pixmap("gschem-\1", w_current)/
1193  *
1194  * and addition of the "w_current" parameter.
1195  *
1196  * @param w_current Pointer to the top level struct.
1197  * @return Pointer to the dialog window.
1198  */
autonumber_create_dialog(GschemToplevel * w_current)1199 GtkWidget* autonumber_create_dialog(GschemToplevel *w_current)
1200 {
1201   GtkWidget *autonumber_text;
1202   GtkWidget *vbox1;
1203   GtkWidget *alignment1;
1204   GtkWidget *vbox3;
1205   GtkWidget *label4;
1206   GtkWidget *scope_text;
1207   GtkWidget *label8;
1208   GtkWidget *label6;
1209   GtkWidget *scope_number;
1210   GtkWidget *scope_skip;
1211   GtkWidget *scope_overwrite;
1212   GtkWidget *label1;
1213   GtkWidget *alignment3;
1214   GtkWidget *vbox4;
1215   GtkWidget *label12;
1216   GtkWidget *label13;
1217   GtkWidget *opt_startnum;
1218   GtkWidget *sort_order;
1219   GtkWidget *opt_removenum;
1220   GtkWidget *opt_slotting;
1221   GtkWidget *label3;
1222 
1223 
1224   autonumber_text = gschem_dialog_new_with_buttons(_("Autonumber Text"),
1225                                                    GTK_WINDOW(w_current->main_window),
1226                                                    (GtkDialogFlags) 0, /* not modal */
1227                                                    "autonumber", w_current,
1228                                                    GTK_STOCK_CLOSE,
1229                                                    GTK_RESPONSE_REJECT,
1230                                                    GTK_STOCK_APPLY,
1231                                                    GTK_RESPONSE_ACCEPT,
1232                                                    NULL);
1233   /* Set the alternative button order (ok, cancel, help) for other systems */
1234   gtk_dialog_set_alternative_button_order(GTK_DIALOG(autonumber_text),
1235                                           GTK_RESPONSE_ACCEPT,
1236                                           GTK_RESPONSE_REJECT,
1237                                           -1);
1238 
1239   gtk_window_set_position (GTK_WINDOW (autonumber_text), GTK_WIN_POS_MOUSE);
1240 
1241   gtk_container_set_border_width (GTK_CONTAINER (autonumber_text),
1242                                   DIALOG_BORDER_SPACING);
1243   vbox1 = gtk_dialog_get_content_area (GTK_DIALOG (autonumber_text));
1244   gtk_box_set_spacing(GTK_BOX(vbox1), DIALOG_V_SPACING);
1245 
1246   /* scope section */
1247   label1 = gtk_label_new (_("<b>Scope</b>"));
1248   gtk_label_set_use_markup (GTK_LABEL (label1), TRUE);
1249   gtk_misc_set_alignment (GTK_MISC(label1), 0, 0);
1250   gtk_box_pack_start (GTK_BOX(vbox1), label1, TRUE, TRUE, 0);
1251   gtk_widget_show (label1);
1252 
1253   alignment1 = gtk_alignment_new (0, 0, 1, 1);
1254   gtk_widget_show (alignment1);
1255   gtk_box_pack_start (GTK_BOX (vbox1), alignment1, TRUE, TRUE, 0);
1256   gtk_alignment_set_padding (GTK_ALIGNMENT (alignment1),
1257                              0, 0, DIALOG_INDENTATION, 0);
1258 
1259 #ifdef ENABLE_GTK3
1260   vbox3 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1261 #else
1262   vbox3 = gtk_vbox_new (FALSE, 0);
1263 #endif
1264   gtk_widget_show (vbox3);
1265   gtk_container_add (GTK_CONTAINER (alignment1), vbox3);
1266 
1267 #ifdef ENABLE_GTK3
1268   GtkWidget* grid1 = gtk_grid_new ();
1269   gtk_widget_show (grid1);
1270   gtk_box_pack_start (GTK_BOX (vbox3), grid1, TRUE, TRUE, 0);
1271   gtk_grid_set_row_spacing (GTK_GRID (grid1), DIALOG_V_SPACING);
1272   gtk_grid_set_column_spacing (GTK_GRID (grid1), DIALOG_H_SPACING);
1273 #else
1274   GtkWidget *table1 = gtk_table_new (3, 2, FALSE);
1275   gtk_widget_show (table1);
1276   gtk_box_pack_start (GTK_BOX (vbox3), table1, TRUE, TRUE, 0);
1277   gtk_table_set_row_spacings (GTK_TABLE (table1), DIALOG_V_SPACING);
1278   gtk_table_set_col_spacings (GTK_TABLE (table1), DIALOG_H_SPACING);
1279 #endif
1280 
1281   label4 = gtk_label_new (_("Search for:"));
1282   gtk_widget_show (label4);
1283 #ifdef ENABLE_GTK3
1284   gtk_grid_attach (GTK_GRID (grid1), label4, 0, 0, 1, 1);
1285 #else
1286   gtk_table_attach (GTK_TABLE (table1), label4, 0, 1, 0, 1,
1287                     (GtkAttachOptions) (GTK_FILL),
1288                     (GtkAttachOptions) (0), 0, 0);
1289 #endif
1290   gtk_misc_set_alignment (GTK_MISC (label4), 0, 0.5);
1291 
1292   scope_text = gtk_combo_box_text_new_with_entry ();
1293   gtk_entry_set_activates_default(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(scope_text))), TRUE);
1294   gtk_widget_show (scope_text);
1295 #ifdef ENABLE_GTK3
1296   gtk_grid_attach (GTK_GRID (grid1), scope_text, 1, 0, 1, 1);
1297 #else
1298   gtk_table_attach (GTK_TABLE (table1), scope_text, 1, 2, 0, 1,
1299                     (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1300                     (GtkAttachOptions) (GTK_FILL), 0, 0);
1301 #endif
1302 
1303   label8 = gtk_label_new (_("Autonumber text in:"));
1304   gtk_widget_show (label8);
1305 #ifdef ENABLE_GTK3
1306   gtk_grid_attach (GTK_GRID (grid1), label8, 0, 1, 1, 1);
1307 #else
1308   gtk_table_attach (GTK_TABLE (table1), label8, 0, 1, 1, 2,
1309                     (GtkAttachOptions) (GTK_FILL),
1310                     (GtkAttachOptions) (0), 0, 0);
1311 #endif
1312   gtk_misc_set_alignment (GTK_MISC (label8), 0, 0.5);
1313 
1314   label6 = gtk_label_new (_("Skip numbers found in:"));
1315   gtk_widget_show (label6);
1316 #ifdef ENABLE_GTK3
1317   gtk_grid_attach (GTK_GRID (grid1), label6, 0, 2, 1, 1);
1318 #else
1319   gtk_table_attach (GTK_TABLE (table1), label6, 0, 1, 2, 3,
1320                     (GtkAttachOptions) (GTK_FILL),
1321                     (GtkAttachOptions) (0), 0, 0);
1322 #endif
1323   gtk_misc_set_alignment (GTK_MISC (label6), 0, 0.5);
1324 
1325   scope_number = gtk_combo_box_text_new ();
1326   gtk_widget_show (scope_number);
1327 #ifdef ENABLE_GTK3
1328   gtk_grid_attach (GTK_GRID (grid1), scope_number, 1, 1, 1, 1);
1329 #else
1330   gtk_table_attach (GTK_TABLE (table1), scope_number, 1, 2, 1, 2,
1331                     (GtkAttachOptions) (GTK_FILL),
1332                     (GtkAttachOptions) (GTK_FILL), 0, 0);
1333 #endif
1334   gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (scope_number), _("Selected objects"));
1335   gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (scope_number), _("Current page"));
1336   gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (scope_number), _("Whole hierarchy"));
1337 
1338   scope_skip = gtk_combo_box_text_new ();
1339   gtk_widget_show (scope_skip);
1340 #ifdef ENABLE_GTK3
1341   gtk_grid_attach (GTK_GRID (grid1), scope_skip, 1, 2, 1, 1);
1342 #else
1343   gtk_table_attach (GTK_TABLE (table1), scope_skip, 1, 2, 2, 3,
1344                     (GtkAttachOptions) (GTK_FILL),
1345                     (GtkAttachOptions) (GTK_FILL), 0, 0);
1346 #endif
1347   gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (scope_skip), _("Selected objects"));
1348   gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (scope_skip), _("Current page"));
1349   gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (scope_skip), _("Whole hierarchy"));
1350 
1351   scope_overwrite = gtk_check_button_new_with_mnemonic (_("Overwrite existing numbers"));
1352   gtk_widget_show (scope_overwrite);
1353   gtk_box_pack_start (GTK_BOX (vbox3), scope_overwrite, FALSE, FALSE, 6);
1354 
1355   /* Options section */
1356   label3 = gtk_label_new (_("<b>Options</b>"));
1357   gtk_label_set_use_markup (GTK_LABEL (label3), TRUE);
1358   gtk_misc_set_alignment(GTK_MISC(label3), 0, 0);
1359   gtk_widget_show (label3);
1360   gtk_box_pack_start(GTK_BOX(vbox1), label3, TRUE, TRUE, 0);
1361 
1362   alignment3 = gtk_alignment_new (0, 0, 1, 1);
1363   gtk_widget_show (alignment3);
1364   gtk_box_pack_start(GTK_BOX(vbox1), alignment3, TRUE, TRUE, 0);
1365   gtk_alignment_set_padding (GTK_ALIGNMENT (alignment3),
1366                              0, 0, DIALOG_INDENTATION, 0);
1367 
1368 #ifdef ENABLE_GTK3
1369   vbox4 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 3);
1370 #else
1371   vbox4 = gtk_vbox_new (FALSE, 3);
1372 #endif
1373   gtk_widget_show (vbox4);
1374   gtk_container_add (GTK_CONTAINER (alignment3), vbox4);
1375 
1376 #ifdef ENABLE_GTK3
1377   GtkWidget *grid3 = gtk_grid_new ();
1378   gtk_widget_show (grid3);
1379   gtk_box_pack_start (GTK_BOX (vbox4), grid3, TRUE, TRUE, 0);
1380   gtk_grid_set_row_spacing (GTK_GRID (grid3), DIALOG_V_SPACING);
1381   gtk_grid_set_column_spacing (GTK_GRID (grid3), DIALOG_H_SPACING);
1382 #else
1383   GtkWidget *table3 = gtk_table_new (2, 2, FALSE);
1384   gtk_widget_show (table3);
1385   gtk_box_pack_start (GTK_BOX (vbox4), table3, TRUE, TRUE, 0);
1386   gtk_table_set_row_spacings (GTK_TABLE (table3), DIALOG_V_SPACING);
1387   gtk_table_set_col_spacings (GTK_TABLE (table3), DIALOG_H_SPACING);
1388 #endif
1389 
1390   label12 = gtk_label_new (_("Starting number:"));
1391   gtk_widget_show (label12);
1392 #ifdef ENABLE_GTK3
1393   gtk_grid_attach (GTK_GRID (grid3), label12, 0, 0, 1, 1);
1394 #else
1395   gtk_table_attach (GTK_TABLE (table3), label12, 0, 1, 0, 1,
1396                     (GtkAttachOptions) (GTK_FILL),
1397                     (GtkAttachOptions) (0), 0, 0);
1398 #endif
1399   gtk_misc_set_alignment (GTK_MISC (label12), 0, 0.5);
1400 
1401   label13 = gtk_label_new (_("Sort order:"));
1402   gtk_widget_show (label13);
1403 #ifdef ENABLE_GTK3
1404   gtk_grid_attach (GTK_GRID (grid3), label13, 0, 1, 1, 1);
1405 #else
1406   gtk_table_attach (GTK_TABLE (table3), label13, 0, 1, 1, 2,
1407                     (GtkAttachOptions) (GTK_FILL),
1408                     (GtkAttachOptions) (0), 0, 0);
1409 #endif
1410   gtk_misc_set_alignment (GTK_MISC (label13), 0, 0.5);
1411 
1412 
1413   /* this sets the page size to 10 (step * 10).
1414    * if different page size is required,
1415    * use gtk_spin_button_set_increments()
1416   */
1417   opt_startnum = gtk_spin_button_new_with_range (0,     /* min */
1418                                                  10000, /* max */
1419                                                  1);    /* step */
1420 
1421   gtk_spin_button_set_digits (GTK_SPIN_BUTTON (opt_startnum), 0);
1422   gtk_spin_button_set_value (GTK_SPIN_BUTTON (opt_startnum), 1);
1423 
1424 
1425   gtk_entry_set_activates_default(GTK_ENTRY(opt_startnum), TRUE);
1426   gtk_widget_show (opt_startnum);
1427 #ifdef ENABLE_GTK3
1428   gtk_grid_attach (GTK_GRID (grid3), opt_startnum, 1, 0, 1, 1);
1429 #else
1430   gtk_table_attach (GTK_TABLE (table3), opt_startnum, 1, 2, 0, 1,
1431                     (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1432                     (GtkAttachOptions) (0), 0, 0);
1433 #endif
1434 
1435   sort_order = gtk_combo_box_new();
1436   gtk_widget_show (sort_order);
1437 #ifdef ENABLE_GTK3
1438   gtk_grid_attach (GTK_GRID (grid3), sort_order, 1, 1, 1, 1);
1439 #else
1440   gtk_table_attach (GTK_TABLE (table3), sort_order, 1, 2, 1, 2,
1441                     (GtkAttachOptions) (GTK_FILL),
1442                     (GtkAttachOptions) (GTK_FILL), 0, 0);
1443 #endif
1444 
1445   opt_removenum = gtk_check_button_new_with_mnemonic (_("Remove numbers"));
1446   gtk_widget_show (opt_removenum);
1447   gtk_box_pack_start (GTK_BOX (vbox4), opt_removenum, FALSE, FALSE, 0);
1448 
1449   opt_slotting = gtk_check_button_new_with_mnemonic (_("Automatic slotting"));
1450   gtk_widget_show (opt_slotting);
1451   gtk_box_pack_start (GTK_BOX (vbox4), opt_slotting, FALSE, FALSE, 0);
1452 
1453   /* Store pointers to all widgets, for use by lookup_widget(). */
1454   GLADE_HOOKUP_OBJECT (autonumber_text, scope_text, "scope_text");
1455   GLADE_HOOKUP_OBJECT (autonumber_text, scope_number, "scope_number");
1456   GLADE_HOOKUP_OBJECT (autonumber_text, scope_skip, "scope_skip");
1457   GLADE_HOOKUP_OBJECT (autonumber_text, scope_overwrite, "scope_overwrite");
1458   GLADE_HOOKUP_OBJECT (autonumber_text, opt_startnum, "opt_startnum");
1459   GLADE_HOOKUP_OBJECT (autonumber_text, sort_order, "sort_order");
1460   GLADE_HOOKUP_OBJECT (autonumber_text, opt_removenum, "opt_removenum");
1461   GLADE_HOOKUP_OBJECT (autonumber_text, opt_slotting, "opt_slotting");
1462 
1463   return autonumber_text;
1464 }
1465 
1466 /*! \brief Create or restore the autonumber text dialog
1467  *
1468  *  If the function is called the first time the dialog is created.
1469  *  If the dialog is only in background it is moved to the foreground.
1470  *
1471  *  @param w_current Pointer to the top level struct
1472  */
autonumber_text_dialog(GschemToplevel * w_current)1473 void autonumber_text_dialog(GschemToplevel *w_current)
1474 {
1475   static AUTONUMBER_TEXT *autotext = NULL;
1476 
1477   GtkWidget *opt_removenum = NULL;
1478   GtkWidget *sort_order = NULL;
1479 
1480   if(autotext == NULL) {
1481     /* first call of this function, init dialog structure */
1482     autotext=autonumber_init_state();
1483   }
1484 
1485   /* set the GschemToplevel always. Can it be changed between the calls??? */
1486   autotext->w_current = w_current;
1487 
1488   if(autotext->dialog == NULL) {
1489     /* Dialog is not currently displayed - create it */
1490 
1491     autotext->dialog = autonumber_create_dialog(w_current);
1492 
1493     opt_removenum = lookup_widget(autotext->dialog, "opt_removenum");
1494     sort_order = lookup_widget(autotext->dialog, "sort_order");
1495 
1496     autonumber_sortorder_create(w_current, sort_order);
1497 
1498     gtk_dialog_set_default_response (GTK_DIALOG (autotext->dialog),
1499                                      GTK_RESPONSE_ACCEPT);
1500 
1501     g_signal_connect (G_OBJECT (autotext->dialog), "response",
1502                       G_CALLBACK (autonumber_text_response),
1503                       autotext);
1504 
1505     g_signal_connect (G_OBJECT (opt_removenum), "clicked",
1506                       G_CALLBACK (autonumber_removenum_toggled),
1507                       autotext);
1508 
1509     autonumber_set_state(autotext);
1510 
1511     gtk_widget_show_all(autotext->dialog);
1512   }
1513 
1514   /* if the dialog is in the background or minimized: show it */
1515   gtk_window_present(GTK_WINDOW(autotext->dialog));
1516 }
1517