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