missing NULL terminator in set_config_x
[geda-gaf.git] / gschem / src / x_autonumber.c
blob251c1b619ddb757d78315d5b323e4410a8ceb5b3
1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2020 gEDA Contributors (see ChangeLog for details)
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 #include <config.h>
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
32 #include "gschem.h"
33 #include <gdk/gdkkeysyms.h>
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)
39 /** @brief How many entries to keep in the "Search text" combo box. */
40 #define HISTORY_LENGTH 15
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
52 enum {
53 AUTONUMBER_IGNORE,
54 AUTONUMBER_RENUMBER,
55 AUTONUMBER_RESPECT
58 enum {
59 SCOPE_SELECTED,
60 SCOPE_PAGE,
61 SCOPE_HIERARCHY
64 typedef struct autonumber_text_t AUTONUMBER_TEXT;
66 /** @brief Stored state of the autonumber text dialog */
67 struct autonumber_text_t {
68 /** @brief Search text history */
69 GList *scope_text;
71 /** @brief Scope for searching existing numbers */
72 gint scope_skip;
74 /** @brief Scope for autonumbering text */
75 gint scope_number;
77 /** @brief Overwrite existing numbers in scope */
78 gboolean scope_overwrite;
80 /** @brief Sort order */
81 gint order;
83 /** @brief Starting number for automatic numbering */
84 gint startnum;
86 /** @brief Remove numbers instead of automatic numbering */
87 gboolean removenum;
89 /** @brief Automatic assignments of slots */
90 gboolean slotting;
92 /** @brief Pointer to the dialog */
93 GtkWidget *dialog;
95 /** @brief Pointer to the GschemToplevel struct */
96 GschemToplevel *w_current;
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 */
106 typedef struct autonumber_slot_t AUTONUMBER_SLOT;
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 */
116 /* ***** BACK-END CODE ***************************************************** */
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
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;
132 /*! \brief GCompareFunc function to sort text objects by there location
133 * \par Function Description
134 * This Funcion takes two <B>OBJECT*</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().
139 gint autonumber_sort_xy(gconstpointer a, gconstpointer b) {
140 OBJECT *aa, *bb;
141 aa=(OBJECT *) a; bb=(OBJECT *) b;
142 if (aa->text->x < bb->text->x)
143 return -1;
144 if (aa->text->x > bb->text->x)
145 return 1;
146 /* x == x */
147 if (aa->text->y > bb->text->y)
148 return -1;
149 if (aa->text->y < bb->text->y)
150 return 1;
151 return 0;
154 /*! \brief GCompareFunc function to sort text objects by there location
155 * \par Function Description
156 * This funcion takes two <B>OBJECT*</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().
162 gint autonumber_sort_xy_rev(gconstpointer a, gconstpointer b) {
163 OBJECT *aa, *bb;
164 aa=(OBJECT *) a; bb=(OBJECT *) b;
165 if (aa->text->x < bb->text->x)
166 return 1;
167 if (aa->text->x > bb->text->x)
168 return -1;
169 /* x == x */
170 if (aa->text->y < bb->text->y)
171 return 1;
172 if (aa->text->y > bb->text->y)
173 return -1;
174 return 0;
177 /*! \brief GCompareFunc function to sort text objects by there location
178 * \par Function Description
179 * This funcion takes two <B>OBJECT*</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().
184 int autonumber_sort_yx(gconstpointer a, gconstpointer b) {
185 OBJECT *aa, *bb;
186 aa=(OBJECT *) a; bb=(OBJECT *) b;
187 if (aa->text->y > bb->text->y)
188 return -1;
189 if (aa->text->y < bb->text->y)
190 return 1;
191 /* y == y */
192 if (aa->text->x < bb->text->x)
193 return -1;
194 if (aa->text->x > bb->text->x)
195 return 1;
196 return 0;
199 /*! \brief GCompareFunc function to sort text objects by there location
200 * \par Function Description
201 * This Funcion takes two <B>OBJECT*</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().
207 int autonumber_sort_yx_rev(gconstpointer a, gconstpointer b) {
208 OBJECT *aa, *bb;
209 aa=(OBJECT *) a; bb=(OBJECT *) b;
210 if (aa->text->y > bb->text->y)
211 return 1;
212 if (aa->text->y < bb->text->y)
213 return -1;
214 /* y == y */
215 if (aa->text->x > bb->text->x)
216 return 1;
217 if (aa->text->x < bb->text->x)
218 return -1;
219 return 0;
222 /*! \brief GCompareFunc function to sort text objects by there location
223 * \par Function Description
224 * This Funcion takes two <B>OBJECT*</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().
229 int autonumber_sort_diagonal(gconstpointer a, gconstpointer b) {
230 OBJECT *aa, *bb;
231 aa=(OBJECT *) a; bb=(OBJECT *) b;
232 if (aa->text->x - aa->text->y < bb->text->x - bb->text->y)
233 return -1;
234 if (aa->text->x - aa->text->y > bb->text->x - bb->text->y)
235 return 1;
236 return 0;
239 /*! \brief GCompareFunc function to acces <B>AUTONUMBER_SLOT</B> object in a GList
240 * \par Function Description
241 * This Funcion takes two <B>AUTONUMBER_SLOT*</B> arguments and compares them.
242 * Sorting criteria is are the AUTONUMBER_SLOT members: first the symbolname, than the
243 * number and last the slotnr.
244 * If the number or the slotnr is set to zero it acts as a wildcard.
245 * The function is used as GCompareFunc by GList functions.
247 gint freeslot_compare(gconstpointer a, gconstpointer b)
249 AUTONUMBER_SLOT *aa, *bb;
250 gint res;
251 aa = (AUTONUMBER_SLOT *) a; bb = (AUTONUMBER_SLOT *) b;
253 if ((res = strcmp(aa->symbolname, bb->symbolname)) != 0)
254 return res;
256 /* aa->symbolname == bb->symbolname */
257 if (aa->number == 0 || bb->number == 0)
258 return 0;
259 if (aa->number > bb->number)
260 return 1;
261 if (aa->number < bb->number)
262 return -1;
264 /* aa->number == bb->number */
265 if (aa->slotnr == 0 || bb->slotnr == 0)
266 return 0;
267 if (aa->slotnr > bb->slotnr)
268 return 1;
269 if (aa->slotnr < bb->slotnr)
270 return -1;
272 return 0;
275 /*! \brief Prints a <B>GList</B> of <B>AUTONUMBER_SLOT</B> elements
276 * \par Function Description
277 * This funcion prints the elements of a GList that contains <B>AUTONUMBER_SLOT</B> elements
278 * It is only used for debugging purposes.
280 void freeslot_print(GList *list) {
281 GList *item;
282 AUTONUMBER_SLOT *fs;
284 printf("freeslot_print(): symname, number, slot\n");
285 for (item = list; item != NULL; item = g_list_next(item)) {
286 fs = item ->data;
287 printf(" %s, %d, %d\n",fs->symbolname, fs->number, fs->slotnr);
292 /*! \brief Function to clear the databases of used parts
293 * \par Function Descriptions
294 * Just remove the list of used numbers, used slots and free slots.
296 void autonumber_clear_database (AUTONUMBER_TEXT *autotext)
298 /* cleanup everything for the next searchtext */
299 if (autotext->used_numbers != NULL) {
300 g_list_free(autotext->used_numbers);
301 autotext->used_numbers = NULL;
303 if (autotext->free_slots != NULL) {
304 g_list_foreach(autotext->free_slots, (GFunc) g_free, NULL);
305 g_list_free(autotext->free_slots);
306 autotext->free_slots = NULL;
308 if (autotext->used_slots != NULL) {
309 g_list_foreach(autotext->used_slots, (GFunc) g_free, NULL);
310 g_list_free(autotext->used_slots);
311 autotext->used_slots = NULL;
315 /*! \brief Function to test, whether the OBJECT matches the autotext criterias
316 * \par Function Description
317 * The criterias are those of the autonumber text dialog. The function decides
318 * whether the <B>OBJECT</B> has to be renumberd, ignored or taken care of when
319 * renumbering all other objects.
320 * \return one of these integer values: <B>AUTONUMBER_IGNORE</B>,
321 * <B>AUTONUMBER_RESPECT</B> or <B>AUTONUMBER_RENUMBER</B> and the current number
322 * of the text object in <B>*number</B>.
324 gint autonumber_match(AUTONUMBER_TEXT *autotext, OBJECT *o_current, gint *number)
326 gint i, len, isnumbered=1;
327 const gchar *str = NULL;
329 len = strlen(autotext->current_searchtext);
330 /* first find out whether we can ignore that object */
331 if (o_current->type != OBJ_TEXT) /* text object */
332 return AUTONUMBER_IGNORE;
334 str = o_text_get_string (autotext->w_current->toplevel, o_current);
336 if (!(strlen(str) - len > 0)
337 || !g_str_has_prefix(str, autotext->current_searchtext))
338 return AUTONUMBER_IGNORE;
340 /* the string object matches with its leading characters to the searchtext */
341 /* now look for the extension, either a number or the "?" */
342 if (g_str_has_suffix (str,"?")) {
343 isnumbered = 0;
344 /* There must not be any character between the "?" and the searchtext */
345 if (strlen(str) != len+1)
346 return AUTONUMBER_IGNORE;
348 else {
349 if (!isdigit( (int) (str[len]) )) /* has at least one digit */
350 return AUTONUMBER_IGNORE;
352 for (i=len+1; str[i]; i++) /* and only digits */
353 if (!isdigit( (int) (str[i]) ))
354 return AUTONUMBER_IGNORE;
357 /* we have six cases, 3 from focus multiplied by 2 selection cases */
358 if ((autotext->root_page || autotext->scope_number == SCOPE_HIERARCHY)
359 && (o_current->selected
360 || autotext->scope_number == SCOPE_HIERARCHY || autotext->scope_number == SCOPE_PAGE)
361 && (!isnumbered || (autotext->scope_overwrite)))
362 return AUTONUMBER_RENUMBER;
364 if (isnumbered
365 && !(autotext->scope_skip == SCOPE_SELECTED
366 && !(o_current->selected) && autotext->root_page)) {
367 sscanf(&(str[len])," %d", number);
368 return AUTONUMBER_RESPECT; /* numbered objects which we don't renumber */
370 else
371 return AUTONUMBER_IGNORE; /* unnumbered objects outside the focus */
375 /*! \brief Creates a list of already numbered objects and slots
376 * \par Function Description
377 * This function collects the used numbers of a single schematic page.
378 * The used element numbers are stored in a GList container
379 * inside the <B>AUTONUMBER_TEXT</B> struct.
380 * The slotting container is a little bit different. It stores free slots of
381 * multislotted symbols, that were used only partially.
382 * The criterias are derivated from the autonumber dialog entries.
384 void autonumber_get_used(GschemToplevel *w_current, AUTONUMBER_TEXT *autotext)
386 gint number, numslots, slotnr, i;
387 OBJECT *o_current, *o_parent;
388 AUTONUMBER_SLOT *slot;
389 GList *slot_item;
390 char *numslot_str, *slot_str;
391 const GList *iter;
393 for (iter = s_page_objects (w_current->toplevel->page_current);
394 iter != NULL;
395 iter = g_list_next (iter)) {
396 o_current = iter->data;
397 if (autonumber_match(autotext, o_current, &number) == AUTONUMBER_RESPECT) {
398 /* check slot and maybe add it to the lists */
399 o_parent = o_current->attached_to;
400 if (autotext->slotting && o_parent != NULL) {
401 /* check for slotted symbol */
402 numslot_str =
403 o_attrib_search_object_attribs_by_name (o_parent, "numslots", 0);
404 if (numslot_str != NULL) {
405 sscanf(numslot_str," %d",&numslots);
406 g_free(numslot_str);
408 if (numslots > 0) {
409 slot_str = o_attrib_search_object_attribs_by_name (o_parent, "slot", 0);
410 if (slot_str == NULL) {
411 s_log_message(_("slotted object without slot attribute may cause "
412 "problems when autonumbering slots\n"));
414 else {
415 sscanf(slot_str, " %d", &slotnr);
416 slot = g_new(AUTONUMBER_SLOT,1);
417 slot->number = number;
418 slot->slotnr = slotnr;
419 slot->symbolname = o_parent->complex_basename;
422 slot_item = g_list_find_custom(autotext->used_slots,
423 slot,
424 (GCompareFunc) freeslot_compare);
425 if (slot_item != NULL) { /* duplicate slot in used_slots */
426 s_log_message(_("duplicate slot may cause problems: "
427 "[symbolname=%s, number=%d, slot=%d]\n"),
428 slot->symbolname, slot->number, slot->slotnr);
429 g_free(slot);
431 else {
432 autotext->used_slots = g_list_insert_sorted(autotext->used_slots,
433 slot,
434 (GCompareFunc) freeslot_compare);
436 slot_item = g_list_find_custom(autotext->free_slots,
437 slot,
438 (GCompareFunc) freeslot_compare);
439 if (slot_item == NULL) {
440 /* insert all slots to the list, except of the current one */
441 for (i=1; i <= numslots; i++) {
442 if (i != slotnr) {
443 slot = g_memdup(slot, sizeof(AUTONUMBER_SLOT));
444 slot->slotnr = i;
445 autotext->free_slots = g_list_insert_sorted(autotext->free_slots,
446 slot,
447 (GCompareFunc) freeslot_compare);
451 else {
452 g_free(slot_item->data);
453 autotext->free_slots = g_list_delete_link(autotext->free_slots, slot_item);
460 /* put number into the used list */
461 autotext->used_numbers = g_list_insert_sorted(autotext->used_numbers,
462 GINT_TO_POINTER(number),
463 (GCompareFunc) autonumber_sort_numbers);
469 /*! \brief Gets or generates free numbers for the autonumbering process.
470 * \par Function Description
471 * This function gets or generates new numbers for the <B>OBJECT o_current</B>.
472 * It uses the element numbers <B>used_numbers</B> and the list of the free slots
473 * <B>free_slots</B> of the <B>AUTONUMBER_TEXT</B> struct.
474 * \return
475 * The new number is returned into the <B>number</B> parameter.
476 * <B>slot</B> is set if autoslotting is active, else it is set to zero.
478 void autonumber_get_new_numbers(AUTONUMBER_TEXT *autotext, OBJECT *o_current,
479 gint *number, gint *slot)
481 GList *item;
482 gint new_number, numslots, i;
483 AUTONUMBER_SLOT *freeslot;
484 OBJECT *o_parent = NULL;
485 GList *freeslot_item;
486 gchar *numslot_str;
488 new_number = autotext->startnum;
490 /* Check for slots first */
491 /* 1. are there any unused slots in the database? */
492 o_parent = o_current->attached_to;
493 if (autotext->slotting && o_parent != NULL) {
494 freeslot = g_new(AUTONUMBER_SLOT,1);
495 freeslot->symbolname = o_parent->complex_basename;
496 freeslot->number = 0;
497 freeslot->slotnr = 0;
498 freeslot_item = g_list_find_custom(autotext->free_slots,
499 freeslot,
500 (GCompareFunc) freeslot_compare);
501 g_free(freeslot);
502 /* Yes! -> remove from database, apply it */
503 if (freeslot_item != NULL) {
504 freeslot = freeslot_item->data;
505 *number = freeslot->number;
506 *slot = freeslot->slotnr;
507 g_free(freeslot);
508 autotext->free_slots = g_list_delete_link(autotext->free_slots, freeslot_item);
510 return;
514 /* get a new number */
515 item = autotext->used_numbers;
516 while (1) {
517 while (item != NULL && GPOINTER_TO_INT(item->data) < new_number)
518 item = g_list_next(item);
520 if (item == NULL || GPOINTER_TO_INT(item->data) > new_number)
521 break;
522 else /* new_number == item->data */
523 new_number++;
525 *number = new_number;
526 *slot = 0;
528 /* insert the new number to the used list */
529 autotext->used_numbers = g_list_insert_sorted(autotext->used_numbers,
530 GINT_TO_POINTER(new_number),
531 (GCompareFunc) autonumber_sort_numbers);
533 /* 3. is o_current a slotted object ? */
534 if ((autotext->slotting) && o_parent != NULL) {
535 numslot_str =
536 o_attrib_search_object_attribs_by_name (o_parent, "numslots", 0);
537 if (numslot_str != NULL) {
538 sscanf(numslot_str," %d",&numslots);
539 g_free(numslot_str);
540 if (numslots > 0) {
541 /* Yes! -> new number and slot=1; add the other slots to the database */
542 *slot = 1;
543 for (i=2; i <=numslots; i++) {
544 freeslot = g_new(AUTONUMBER_SLOT,1);
545 freeslot->symbolname = o_parent->complex_basename;
546 freeslot->number = new_number;
547 freeslot->slotnr = i;
548 autotext->free_slots = g_list_insert_sorted(autotext->free_slots,
549 freeslot,
550 (GCompareFunc) freeslot_compare);
557 /** @brief Removes the number from the element.
559 * This function updates the text content of the \a o_current object.
561 * @param autotext Pointer to the state structure
562 * @param o_current Pointer to the object from which to remove the number
565 void autonumber_remove_number(AUTONUMBER_TEXT * autotext, OBJECT *o_current)
567 OBJECT *o_parent, *o_slot;
568 gchar *slot_str;
569 gchar *str = NULL;
571 /* replace old text */
572 str = g_strdup_printf("%s?", autotext->current_searchtext);
573 o_text_set_string (autotext->w_current->toplevel, o_current, str);
574 g_free (str);
576 /* remove the slot attribute if slotting is active */
577 if (autotext->slotting) {
578 /* get the slot attribute */
579 o_parent = o_current->attached_to;
580 if (o_parent != NULL) {
581 slot_str = s_slot_search_slot (o_parent, &o_slot);
582 g_free (slot_str);
583 /* Only attempt to remove non-inherited slot attributes */
584 if (o_slot != NULL && !o_attrib_is_inherited (o_slot)) {
585 /* delete the slot attribute */
586 o_delete (autotext->w_current, o_slot);
591 gschem_toplevel_page_content_changed (autotext->w_current,
592 autotext->w_current->toplevel->page_current);
595 /*! \brief Changes the number <B>OBJECT</B> element. Changes the slot attribute.
596 * \par Function Description
597 * This function updates the text content of the <B>o_current</B> object.
598 * If the <B>slot</B> value is not zero. It updates the slot attribut of the
599 * complex element that is also the parent object of the o_current element.
601 void autonumber_apply_new_text(AUTONUMBER_TEXT * autotext, OBJECT *o_current,
602 gint number, gint slot)
604 char *str;
606 if (slot != 0) {
607 /* update the slot on the owning object */
608 str = g_strdup_printf ("slot=%d", slot);
609 o_slot_end (autotext->w_current, o_current->attached_to, str);
610 g_free (str);
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);
618 gschem_toplevel_page_content_changed (autotext->w_current,
619 autotext->w_current->toplevel->page_current);
623 /*! \brief Handles all the options of the autonumber text dialog
624 * \par Function Description
625 * This function is the master of all autonumber code. It receives the options of
626 * the the autonumber text dialog in an <B>AUTONUMBER_TEXT</B> structure.
627 * First it collects all pages of a hierarchical schematic.
628 * Second it gets all matching text elements for the searchtext.
629 * Then it renumbers all text elements of all schematic pages. The renumbering
630 * follows the rules of the parameters given in the autonumber text dialog.
632 void autonumber_text_autonumber(AUTONUMBER_TEXT *autotext)
634 GList *pages;
635 GList *searchtext_list=NULL;
636 GList *text_item, *obj_item, *page_item;
637 OBJECT *o_current;
638 GschemToplevel *w_current;
639 PAGE *saved_page;
640 gchar *searchtext;
641 gchar *scope_text;
642 gchar *new_searchtext;
643 gint i, number, slot;
644 GList *o_list = NULL;
645 const GList *iter;
647 w_current = autotext->w_current;
648 saved_page = w_current->toplevel->page_current;
649 autotext->current_searchtext = NULL;
650 autotext->root_page = 1;
651 autotext->used_numbers = NULL;
652 autotext->free_slots = NULL;
653 autotext->used_slots = NULL;
655 scope_text = g_list_first(autotext->scope_text)->data;
657 /* Step1: get all pages of the hierarchy */
658 pages = s_hierarchy_traversepages (w_current->toplevel,
659 w_current->toplevel->page_current,
660 HIERARCHY_NODUPS);
662 /* g_list_foreach(pages, (GFunc) s_hierarchy_print_page, NULL); */
664 /* Step2: if searchtext has an asterisk at the end we have to find
665 all matching searchtextes.
667 Example: "refdes=*" will match each text that starts with "refdes="
668 and has a trailing "?" or a trailing number if the "all"-option is set.
669 We get a list of possible prefixes: refdes=R, refdes=C.
671 If there is only one search pattern, it becomes a single item
672 in the searchtext list */
674 if (strlen(scope_text) == 0) {
675 s_log_message(_("No searchstring given in autonumber text.\n"));
676 return; /* error */
678 else if (g_str_has_suffix(scope_text,"?") == TRUE) {
679 /* single searchtext, strip of the "?" */
680 searchtext = g_strndup(scope_text, strlen(scope_text)-1);
681 searchtext_list=g_list_append (searchtext_list, searchtext);
683 else if (g_str_has_suffix(scope_text,"*") == TRUE) {
684 /* strip of the "*" */
685 searchtext = g_strndup(scope_text, strlen(scope_text)-1);
686 /* collect all the possible searchtexts in all pages of the hierarchy */
687 for (page_item = pages; page_item != NULL; page_item = g_list_next(page_item)) {
688 s_page_goto(w_current->toplevel, page_item->data);
689 gschem_toplevel_page_changed (w_current);
690 /* iterate over all objects an look for matching searchtext's */
691 for (iter = s_page_objects (w_current->toplevel->page_current);
692 iter != NULL;
693 iter = g_list_next (iter)) {
694 o_current = iter->data;
695 if (o_current->type == OBJ_TEXT) {
696 if (autotext->scope_number == SCOPE_HIERARCHY
697 || autotext->scope_number == SCOPE_PAGE
698 || ((autotext->scope_number == SCOPE_SELECTED) && (o_current->selected))) {
699 const gchar *str = o_text_get_string (w_current->toplevel, o_current);
700 if (g_str_has_prefix (str, searchtext)) {
701 /* the beginnig of the current text matches with the searchtext now */
702 /* strip of the trailing [0-9?] chars and add it too the searchtext */
703 for (i = strlen (str)-1;
704 (i >= strlen(searchtext))
705 && (str[i] == '?'
706 || isdigit( (int) (str[i]) ));
707 i--)
708 ; /* void */
710 new_searchtext = g_strndup (str, i+1);
711 if (g_list_find_custom(searchtext_list, new_searchtext,
712 (GCompareFunc) strcmp) == NULL ) {
713 searchtext_list = g_list_append(searchtext_list, new_searchtext);
715 else {
716 g_free(new_searchtext);
722 if (autotext->scope_number == SCOPE_SELECTED || autotext->scope_number == SCOPE_PAGE)
723 break; /* search only in the first page */
725 g_free(searchtext);
727 else {
728 s_log_message(_("No '*' or '?' given at the end of the autonumber text.\n"));
729 return;
732 /* Step3: iterate over the search items in the list */
733 for (text_item=searchtext_list; text_item !=NULL; text_item=g_list_next(text_item)) {
734 autotext->current_searchtext = text_item->data;
735 /* printf("autonumber_text_autonumber: searchtext %s\n", autotext->current_searchtext); */
736 /* decide whether to renumber page by page or get a global used-list */
737 if (autotext->scope_skip == SCOPE_HIERARCHY) { /* whole hierarchy database */
738 /* renumbering all means that no db is required */
739 if (!(autotext->scope_number == SCOPE_HIERARCHY
740 && autotext->scope_overwrite)) {
741 for (page_item = pages; page_item != NULL; page_item = g_list_next(page_item)) {
742 autotext->root_page = (pages->data == page_item->data);
743 s_page_goto(w_current->toplevel, page_item->data);
744 gschem_toplevel_page_changed (w_current);
745 autonumber_get_used(w_current, autotext);
750 /* renumber the elements */
751 for (page_item = pages; page_item != NULL; page_item = g_list_next(page_item)) {
752 s_page_goto(w_current->toplevel, page_item->data);
753 gschem_toplevel_page_changed (w_current);
754 autotext->root_page = (pages->data == page_item->data);
755 /* build a page database if we're numbering pagebypage or selection only*/
756 if (autotext->scope_skip == SCOPE_PAGE || autotext->scope_skip == SCOPE_SELECTED) {
757 autonumber_get_used(w_current, autotext);
760 /* RENUMBER CODE FOR ONE PAGE AND ONE SEARCHTEXT*/
761 /* 1. get objects to renumber */
762 for (iter = s_page_objects (w_current->toplevel->page_current);
763 iter != NULL;
764 iter = g_list_next (iter)) {
765 o_current = iter->data;
766 if (autonumber_match(autotext, o_current, &number) == AUTONUMBER_RENUMBER) {
767 /* put number into the used list */
768 o_list = g_list_append(o_list, o_current);
772 /* 2. sort object list */
773 switch (autotext->order) {
774 case AUTONUMBER_SORT_YX:
775 o_list=g_list_sort(o_list, autonumber_sort_yx);
776 break;
777 case AUTONUMBER_SORT_YX_REV:
778 o_list=g_list_sort(o_list, autonumber_sort_yx_rev);
779 break;
780 case AUTONUMBER_SORT_XY:
781 o_list=g_list_sort(o_list, autonumber_sort_xy);
782 break;
783 case AUTONUMBER_SORT_XY_REV:
784 o_list=g_list_sort(o_list, autonumber_sort_xy_rev);
785 break;
786 case AUTONUMBER_SORT_DIAGONAL:
787 o_list=g_list_sort(o_list, autonumber_sort_diagonal);
788 break;
789 default:
790 ; /* unsorted file order */
793 /* 3. renumber/reslot the objects */
794 for(obj_item=o_list; obj_item != NULL; obj_item=g_list_next(obj_item)) {
795 o_current= obj_item->data;
796 if(autotext->removenum) {
797 autonumber_remove_number(autotext, o_current);
798 } else {
799 /* get valid numbers from the database */
800 autonumber_get_new_numbers(autotext, o_current, &number, &slot);
801 /* and apply it. TODO: join these two functions */
802 autonumber_apply_new_text(autotext, o_current, number, slot);
805 g_list_free(o_list);
806 o_list = NULL;
808 /* destroy the page database */
809 if (autotext->scope_skip == SCOPE_PAGE
810 || autotext->scope_skip == SCOPE_SELECTED)
811 autonumber_clear_database(autotext);
813 if (autotext->scope_number == SCOPE_SELECTED
814 || autotext->scope_number == SCOPE_PAGE)
815 break; /* only renumber the parent page (the first page) */
817 autonumber_clear_database(autotext); /* cleanup */
820 /* cleanup and redraw all*/
821 g_list_foreach(searchtext_list, (GFunc) g_free, NULL);
822 g_list_free(searchtext_list);
823 if (saved_page != NULL) {
824 s_page_goto (w_current->toplevel, saved_page);
825 gschem_toplevel_page_changed (w_current);
827 gschem_page_view_invalidate_all (gschem_toplevel_get_current_page_view (w_current));
828 g_list_free(pages);
829 /* FIXME: If hierarchy renumbering is enabled, an undo step should be
830 * added for all pages which were modified. Also, an undo step
831 * shouldn't be added if the page wasn't actually modified. */
832 o_undo_savestate_old (w_current, UNDO_ALL, _("Autonumber"));
835 /* ***** UTILITY GUI FUNCTIONS (move to a separate file in the future?) **** */
837 /** @brief Finds a widget by its name given a pointer to its parent.
839 * @param widget Pointer to the parent widget.
840 * @param widget_name Name of the widget.
841 * @return Pointer to the widget or NULL if not found. */
842 GtkWidget* lookup_widget(GtkWidget *widget, const gchar *widget_name)
844 GtkWidget *found_widget;
846 found_widget = (GtkWidget*) g_object_get_data(G_OBJECT(widget),
847 widget_name);
849 return found_widget;
852 /*! \brief Put the icons and the text into the sortorder combobox
853 * \par Function Description
854 * Load all bitmaps for the combobox and store them together with the label
855 * in a GtkListStore.
857 void autonumber_sortorder_create(GschemToplevel *w_current, GtkWidget *sort_order)
859 GtkListStore *store;
860 GtkTreeIter iter;
861 GtkCellRenderer *renderer;
862 GdkPixbuf *pixbuf;
863 gchar *path;
864 GError *error=NULL;
866 gchar *filenames[] = {"gschem-diagonal.png",
867 "gschem-top2bottom.png", "gschem-bottom2top.png",
868 "gschem-left2right.png", "gschem-right2left.png",
869 "gschem-fileorder.png",
870 NULL};
871 gchar *names[] = {N_("Diagonal"),
872 N_("Top to bottom"), N_("Bottom to top"),
873 N_("Left to right"), N_("Right to left"),
874 N_("File order"),
875 NULL};
876 gint i;
878 store = gtk_list_store_new(2, G_TYPE_STRING, GDK_TYPE_PIXBUF);
880 for (i=0; filenames[i] != NULL; i++) {
881 path=g_build_filename(w_current->toplevel->bitmap_directory,
882 filenames[i], NULL);
883 pixbuf = gdk_pixbuf_new_from_file(path, &error);
884 g_free(path);
885 gtk_list_store_append(store, &iter);
886 gtk_list_store_set(store, &iter,
887 0, _(names[i]),
888 1, pixbuf,
889 -1);
892 gtk_combo_box_set_model(GTK_COMBO_BOX(sort_order), GTK_TREE_MODEL(store));
893 renderer = gtk_cell_renderer_text_new ();
895 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (sort_order),
896 renderer, TRUE);
897 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (sort_order),
898 renderer, "text", 0, NULL);
899 renderer = gtk_cell_renderer_pixbuf_new();
900 g_object_set(G_OBJECT(renderer), "xpad", 5, "ypad", 5, NULL);
902 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (sort_order),
903 renderer, FALSE);
904 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (sort_order),
905 renderer, "pixbuf", 1, NULL);
908 /* ***** STATE STRUCT HANDLING (interface between GUI and backend code) **** */
910 /** @brief Adds a line to the search text history list
912 * Function makes sure that: 1) There are no duplicates in the list and 2) the
913 * last search text is always at the top of the list.
915 GList *autonumber_history_add(GList *history, gchar *text)
917 /* Search for this text in history and delete it (so we don't have
918 * duplicate entries) */
920 GList *cur;
922 cur=history;
923 while(cur!=NULL) {
924 if(!strcmp(text, cur->data)) {
925 history=g_list_remove_link(history, cur);
927 g_free(cur->data);
928 g_list_free(cur);
929 break;
931 cur=g_list_next(cur);
934 /* Add the new text at the beginning of the list */
936 history=g_list_prepend(history, text);
938 /* Truncate history */
939 while(g_list_length(history) > HISTORY_LENGTH) {
940 GList *last = g_list_last(history);
942 history = g_list_remove_link(history, last);
944 g_free(last->data);
945 g_list_free(last);
948 return history;
951 /** @brief Allocate and initialize the state structure
953 * @return Pointer to the allocated structure or NULL on error.
955 AUTONUMBER_TEXT *autonumber_init_state()
957 AUTONUMBER_TEXT *autotext;
959 /* Default contents of the combo box history */
960 gchar *default_text[] = {
961 "refdes=*",
962 "refdes=C?",
963 "refdes=D?",
964 "refdes=I?",
965 "refdes=L?",
966 "refdes=Q?",
967 "refdes=R?",
968 "refdes=T?",
969 "refdes=U?",
970 "refdes=X?",
971 "netname=*",
972 "netname=A?",
973 "netname=D?",
974 NULL
976 gchar **t;
978 autotext = g_new(AUTONUMBER_TEXT, 1);
980 if(autotext==NULL) return NULL;
982 autotext->scope_text = NULL;
983 t=default_text;
984 while(*t!=NULL) {
985 autotext->scope_text=g_list_append(autotext->scope_text,
986 g_strdup(*t));
987 t++;
990 autotext->scope_skip = SCOPE_PAGE;
991 autotext->scope_number = SCOPE_SELECTED;
993 autotext->scope_overwrite = 0;
994 autotext->order = AUTONUMBER_SORT_DIAGONAL;
996 autotext->startnum=1;
998 autotext->removenum=0;
999 autotext->slotting=0;
1001 autotext->dialog = NULL;
1003 return autotext;
1006 /** @brief Restore the settings for the autonumber text dialog
1008 * @param autotext Pointer to the state struct.
1010 void autonumber_set_state(AUTONUMBER_TEXT *autotext)
1012 GtkWidget *widget;
1013 GtkTreeModel *model;
1014 GList *el;
1015 /* Scope */
1017 /* Search text history */
1018 widget = lookup_widget(autotext->dialog, "scope_text");
1020 /* Simple way to clear the ComboBox. Owen from #gtk+ says:
1022 * Yeah, it's just slightly "shady" ... if you want to stick to fully
1023 * advertised API, you need to remember how many rows you added and
1024 * use gtk_combo_box_remove_text() */
1026 model = gtk_combo_box_get_model(GTK_COMBO_BOX(widget));
1027 gtk_list_store_clear(GTK_LIST_STORE(model));
1029 for (el= autotext->scope_text; el != NULL; el=g_list_next(el)) {
1030 gtk_combo_box_append_text(GTK_COMBO_BOX(widget), el->data);
1033 widget = gtk_bin_get_child(GTK_BIN(widget));
1034 gtk_entry_set_text(GTK_ENTRY(widget), g_list_first(autotext->scope_text)->data);
1036 widget = lookup_widget(autotext->dialog, "scope_skip");
1037 gtk_combo_box_set_active(GTK_COMBO_BOX(widget),
1038 autotext->scope_skip);
1040 widget = lookup_widget(autotext->dialog, "scope_number");
1041 gtk_combo_box_set_active(GTK_COMBO_BOX(widget),
1042 autotext->scope_number);
1044 widget = lookup_widget(autotext->dialog, "scope_overwrite");
1045 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),
1046 autotext->scope_overwrite);
1048 /* Options */
1049 widget = lookup_widget(autotext->dialog, "opt_startnum");
1050 gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget),
1051 autotext->startnum);
1053 widget = lookup_widget(autotext->dialog, "sort_order");
1054 gtk_combo_box_set_active(GTK_COMBO_BOX(widget), autotext->order);
1056 widget = lookup_widget(autotext->dialog, "opt_removenum");
1057 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),
1058 autotext->removenum);
1060 widget = lookup_widget(autotext->dialog, "opt_slotting");
1061 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),
1062 autotext->slotting);
1065 /** @brief Get the settings from the autonumber text dialog
1067 * Get the settings from the autonumber text dialog and store it in the
1068 * <B>AUTONUMBER_TEXT</B> structure.
1070 * @param autotext Pointer to the state struct.
1072 void autonumber_get_state(AUTONUMBER_TEXT *autotext)
1074 GtkWidget *widget;
1075 gchar *text;
1077 /* Scope */
1079 /* Search text history */
1080 widget = lookup_widget(autotext->dialog, "scope_text");
1081 widget = gtk_bin_get_child(GTK_BIN(widget));
1082 text = g_strdup(gtk_entry_get_text( GTK_ENTRY(widget)));
1084 autotext->scope_text=autonumber_history_add(autotext->scope_text, text);
1086 widget = lookup_widget(autotext->dialog, "scope_skip");
1087 autotext->scope_skip = gtk_combo_box_get_active( GTK_COMBO_BOX(widget) );
1089 widget = lookup_widget(autotext->dialog, "scope_number");
1090 autotext->scope_number = gtk_combo_box_get_active(GTK_COMBO_BOX(widget) );
1092 widget = lookup_widget(autotext->dialog, "scope_overwrite");
1093 autotext->scope_overwrite = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
1095 /* Sort order */
1096 widget = lookup_widget(autotext->dialog, "sort_order");
1097 autotext->order= gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
1099 /* Options */
1100 widget = lookup_widget(autotext->dialog, "opt_startnum");
1101 autotext->startnum=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
1103 widget = lookup_widget(autotext->dialog, "opt_removenum");
1104 autotext->removenum = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
1106 widget = lookup_widget(autotext->dialog, "opt_slotting");
1107 autotext->slotting = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
1110 /* ***** CALLBACKS (functions that get called directly from the GTK) ******* */
1112 /*! \brief response callback for the autonumber text dialog
1113 * \par Function Description
1114 * The function just closes the dialog if the close button is pressed or the
1115 * user closes the dialog window.
1116 * Triggering the apply button will call the autonumber action functions.
1118 void autonumber_text_response(GtkWidget * widget, gint response,
1119 AUTONUMBER_TEXT *autotext)
1121 switch (response) {
1122 case GTK_RESPONSE_ACCEPT:
1123 autonumber_get_state(autotext);
1124 if (autotext->removenum == TRUE && autotext->scope_overwrite == FALSE) {
1125 /* temporarly set the overwrite flag */
1126 autotext->scope_overwrite = TRUE;
1127 autonumber_text_autonumber(autotext);
1128 autotext->scope_overwrite = FALSE;
1130 else {
1131 autonumber_text_autonumber(autotext);
1133 break;
1134 case GTK_RESPONSE_REJECT:
1135 case GTK_RESPONSE_DELETE_EVENT:
1136 gtk_widget_destroy(autotext->dialog);
1137 autotext->dialog = NULL;
1138 break;
1139 default:
1140 printf("ERROR: autonumber_text_response(): strange signal %d\n",response);
1145 /** @brief Callback that activates or deactivates "overwrite existing numbers"
1146 * check box.
1148 * This gets called each time "remove numbers" check box gets clicked.
1150 void autonumber_removenum_toggled(GtkWidget * opt_removenum,
1151 AUTONUMBER_TEXT *autotext)
1153 GtkWidget *scope_overwrite;
1155 scope_overwrite=lookup_widget(autotext->dialog, "scope_overwrite");
1157 /* toggle activity of scope overwrite with respect to removenum */
1158 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(opt_removenum))) {
1159 gtk_widget_set_sensitive(scope_overwrite, 0);
1160 } else {
1161 gtk_widget_set_sensitive(scope_overwrite, 1);
1166 /* ***** DIALOG SET-UP ***************************************************** */
1168 /** @brief Creates the autonumber text dialog.
1170 * Dialog is not shown. No callbacks are registered. This is basically
1171 * unmodified code returned by Glade.
1173 * Only modification was the following substitution:
1175 * %s/create_pixmap (autonumber_text, "\(.*\)")/autonumber_create_pixmap("gschem-\1", w_current)/
1177 * and addition of the "w_current" parameter.
1179 * @param w_current Pointer to the top level struct.
1180 * @return Pointer to the dialog window.
1182 GtkWidget* autonumber_create_dialog(GschemToplevel *w_current)
1184 GtkWidget *autonumber_text;
1185 GtkWidget *vbox1;
1186 GtkWidget *alignment1;
1187 GtkWidget *vbox3;
1188 GtkWidget *table1;
1189 GtkWidget *label4;
1190 GtkWidget *scope_text;
1191 GtkWidget *label8;
1192 GtkWidget *label6;
1193 GtkWidget *scope_number;
1194 GtkWidget *scope_skip;
1195 GtkWidget *scope_overwrite;
1196 GtkWidget *label1;
1197 GtkWidget *alignment3;
1198 GtkWidget *vbox4;
1199 GtkWidget *table3;
1200 GtkWidget *label12;
1201 GtkWidget *label13;
1202 GtkObject *opt_startnum_adj;
1203 GtkWidget *opt_startnum;
1204 GtkWidget *sort_order;
1205 GtkWidget *opt_removenum;
1206 GtkWidget *opt_slotting;
1207 GtkWidget *label3;
1210 autonumber_text = gschem_dialog_new_with_buttons(_("Autonumber text"),
1211 GTK_WINDOW(w_current->main_window),
1212 0, /* not modal */
1213 "autonumber", w_current,
1214 GTK_STOCK_CLOSE,
1215 GTK_RESPONSE_REJECT,
1216 GTK_STOCK_APPLY,
1217 GTK_RESPONSE_ACCEPT,
1218 NULL);
1219 /* Set the alternative button order (ok, cancel, help) for other systems */
1220 gtk_dialog_set_alternative_button_order(GTK_DIALOG(autonumber_text),
1221 GTK_RESPONSE_ACCEPT,
1222 GTK_RESPONSE_REJECT,
1223 -1);
1225 gtk_window_set_position (GTK_WINDOW (autonumber_text), GTK_WIN_POS_MOUSE);
1227 gtk_container_set_border_width (GTK_CONTAINER (autonumber_text),
1228 DIALOG_BORDER_SPACING);
1229 vbox1 = GTK_DIALOG(autonumber_text)->vbox;
1230 gtk_box_set_spacing(GTK_BOX(vbox1), DIALOG_V_SPACING);
1232 /* scope section */
1233 label1 = gtk_label_new (_("<b>Scope</b>"));
1234 gtk_label_set_use_markup (GTK_LABEL (label1), TRUE);
1235 gtk_misc_set_alignment (GTK_MISC(label1), 0, 0);
1236 gtk_box_pack_start (GTK_BOX(vbox1), label1, TRUE, TRUE, 0);
1237 gtk_widget_show (label1);
1239 alignment1 = gtk_alignment_new (0, 0, 1, 1);
1240 gtk_widget_show (alignment1);
1241 gtk_box_pack_start (GTK_BOX (vbox1), alignment1, TRUE, TRUE, 0);
1242 gtk_alignment_set_padding (GTK_ALIGNMENT (alignment1),
1243 0, 0, DIALOG_INDENTATION, 0);
1245 vbox3 = gtk_vbox_new (FALSE, 0);
1246 gtk_widget_show (vbox3);
1247 gtk_container_add (GTK_CONTAINER (alignment1), vbox3);
1249 table1 = gtk_table_new (3, 2, FALSE);
1250 gtk_widget_show (table1);
1251 gtk_box_pack_start (GTK_BOX (vbox3), table1, TRUE, TRUE, 0);
1252 gtk_table_set_row_spacings (GTK_TABLE (table1), DIALOG_V_SPACING);
1253 gtk_table_set_col_spacings (GTK_TABLE (table1), DIALOG_H_SPACING);
1255 label4 = gtk_label_new (_("Search for:"));
1256 gtk_widget_show (label4);
1257 gtk_table_attach (GTK_TABLE (table1), label4, 0, 1, 0, 1,
1258 (GtkAttachOptions) (GTK_FILL),
1259 (GtkAttachOptions) (0), 0, 0);
1260 gtk_misc_set_alignment (GTK_MISC (label4), 0, 0.5);
1262 scope_text = gtk_combo_box_entry_new_text ();
1263 gtk_entry_set_activates_default(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(scope_text))), TRUE);
1264 gtk_widget_show (scope_text);
1265 gtk_table_attach (GTK_TABLE (table1), scope_text, 1, 2, 0, 1,
1266 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1267 (GtkAttachOptions) (GTK_FILL), 0, 0);
1269 label8 = gtk_label_new (_("Autonumber text in:"));
1270 gtk_widget_show (label8);
1271 gtk_table_attach (GTK_TABLE (table1), label8, 0, 1, 1, 2,
1272 (GtkAttachOptions) (GTK_FILL),
1273 (GtkAttachOptions) (0), 0, 0);
1274 gtk_misc_set_alignment (GTK_MISC (label8), 0, 0.5);
1276 label6 = gtk_label_new (_("Skip numbers found in:"));
1277 gtk_widget_show (label6);
1278 gtk_table_attach (GTK_TABLE (table1), label6, 0, 1, 2, 3,
1279 (GtkAttachOptions) (GTK_FILL),
1280 (GtkAttachOptions) (0), 0, 0);
1281 gtk_misc_set_alignment (GTK_MISC (label6), 0, 0.5);
1283 scope_number = gtk_combo_box_new_text ();
1284 gtk_widget_show (scope_number);
1285 gtk_table_attach (GTK_TABLE (table1), scope_number, 1, 2, 1, 2,
1286 (GtkAttachOptions) (GTK_FILL),
1287 (GtkAttachOptions) (GTK_FILL), 0, 0);
1288 gtk_combo_box_append_text (GTK_COMBO_BOX (scope_number), _("Selected objects"));
1289 gtk_combo_box_append_text (GTK_COMBO_BOX (scope_number), _("Current page"));
1290 gtk_combo_box_append_text (GTK_COMBO_BOX (scope_number), _("Whole hierarchy"));
1292 scope_skip = gtk_combo_box_new_text ();
1293 gtk_widget_show (scope_skip);
1294 gtk_table_attach (GTK_TABLE (table1), scope_skip, 1, 2, 2, 3,
1295 (GtkAttachOptions) (GTK_FILL),
1296 (GtkAttachOptions) (GTK_FILL), 0, 0);
1297 gtk_combo_box_append_text (GTK_COMBO_BOX (scope_skip), _("Selected objects"));
1298 gtk_combo_box_append_text (GTK_COMBO_BOX (scope_skip), _("Current page"));
1299 gtk_combo_box_append_text (GTK_COMBO_BOX (scope_skip), _("Whole hierarchy"));
1301 scope_overwrite = gtk_check_button_new_with_mnemonic (_("Overwrite existing numbers"));
1302 gtk_widget_show (scope_overwrite);
1303 gtk_box_pack_start (GTK_BOX (vbox3), scope_overwrite, FALSE, FALSE, 6);
1305 /* Options section */
1306 label3 = gtk_label_new (_("<b>Options</b>"));
1307 gtk_label_set_use_markup (GTK_LABEL (label3), TRUE);
1308 gtk_misc_set_alignment(GTK_MISC(label3), 0, 0);
1309 gtk_widget_show (label3);
1310 gtk_box_pack_start(GTK_BOX(vbox1), label3, TRUE, TRUE, 0);
1312 alignment3 = gtk_alignment_new (0, 0, 1, 1);
1313 gtk_widget_show (alignment3);
1314 gtk_box_pack_start(GTK_BOX(vbox1), alignment3, TRUE, TRUE, 0);
1315 gtk_alignment_set_padding (GTK_ALIGNMENT (alignment3),
1316 0, 0, DIALOG_INDENTATION, 0);
1318 vbox4 = gtk_vbox_new (FALSE, 3);
1319 gtk_widget_show (vbox4);
1320 gtk_container_add (GTK_CONTAINER (alignment3), vbox4);
1322 table3 = gtk_table_new (2, 2, FALSE);
1323 gtk_widget_show (table3);
1324 gtk_box_pack_start (GTK_BOX (vbox4), table3, TRUE, TRUE, 0);
1325 gtk_table_set_row_spacings (GTK_TABLE (table3), DIALOG_V_SPACING);
1326 gtk_table_set_col_spacings (GTK_TABLE (table3), DIALOG_H_SPACING);
1328 label12 = gtk_label_new (_("Starting number:"));
1329 gtk_widget_show (label12);
1330 gtk_table_attach (GTK_TABLE (table3), label12, 0, 1, 0, 1,
1331 (GtkAttachOptions) (GTK_FILL),
1332 (GtkAttachOptions) (0), 0, 0);
1333 gtk_misc_set_alignment (GTK_MISC (label12), 0, 0.5);
1335 label13 = gtk_label_new (_("Sort order:"));
1336 gtk_widget_show (label13);
1337 gtk_table_attach (GTK_TABLE (table3), label13, 0, 1, 1, 2,
1338 (GtkAttachOptions) (GTK_FILL),
1339 (GtkAttachOptions) (0), 0, 0);
1340 gtk_misc_set_alignment (GTK_MISC (label13), 0, 0.5);
1342 opt_startnum_adj = gtk_adjustment_new (1, 0, 10000, 1, 10, 10);
1343 opt_startnum = gtk_spin_button_new (GTK_ADJUSTMENT (opt_startnum_adj), 1, 0);
1344 gtk_entry_set_activates_default(GTK_ENTRY(opt_startnum), TRUE);
1345 gtk_widget_show (opt_startnum);
1346 gtk_table_attach (GTK_TABLE (table3), opt_startnum, 1, 2, 0, 1,
1347 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1348 (GtkAttachOptions) (0), 0, 0);
1350 sort_order = gtk_combo_box_new();
1351 gtk_widget_show (sort_order);
1352 gtk_table_attach (GTK_TABLE (table3), sort_order, 1, 2, 1, 2,
1353 (GtkAttachOptions) (GTK_FILL),
1354 (GtkAttachOptions) (GTK_FILL), 0, 0);
1356 opt_removenum = gtk_check_button_new_with_mnemonic (_("Remove numbers"));
1357 gtk_widget_show (opt_removenum);
1358 gtk_box_pack_start (GTK_BOX (vbox4), opt_removenum, FALSE, FALSE, 0);
1360 opt_slotting = gtk_check_button_new_with_mnemonic (_("Automatic slotting"));
1361 gtk_widget_show (opt_slotting);
1362 gtk_box_pack_start (GTK_BOX (vbox4), opt_slotting, FALSE, FALSE, 0);
1364 /* Store pointers to all widgets, for use by lookup_widget(). */
1365 GLADE_HOOKUP_OBJECT (autonumber_text, scope_text, "scope_text");
1366 GLADE_HOOKUP_OBJECT (autonumber_text, scope_number, "scope_number");
1367 GLADE_HOOKUP_OBJECT (autonumber_text, scope_skip, "scope_skip");
1368 GLADE_HOOKUP_OBJECT (autonumber_text, scope_overwrite, "scope_overwrite");
1369 GLADE_HOOKUP_OBJECT (autonumber_text, opt_startnum, "opt_startnum");
1370 GLADE_HOOKUP_OBJECT (autonumber_text, sort_order, "sort_order");
1371 GLADE_HOOKUP_OBJECT (autonumber_text, opt_removenum, "opt_removenum");
1372 GLADE_HOOKUP_OBJECT (autonumber_text, opt_slotting, "opt_slotting");
1374 return autonumber_text;
1377 /*! \brief Create or restore the autonumber text dialog
1379 * If the function is called the first time the dialog is created.
1380 * If the dialog is only in background it is moved to the foreground.
1382 * @param w_current Pointer to the top level struct
1384 void autonumber_text_dialog(GschemToplevel *w_current)
1386 static AUTONUMBER_TEXT *autotext = NULL;
1388 GtkWidget *opt_removenum = NULL;
1389 GtkWidget *sort_order = NULL;
1391 if(autotext == NULL) {
1392 /* first call of this function, init dialog structure */
1393 autotext=autonumber_init_state();
1396 /* set the GschemToplevel always. Can it be changed between the calls??? */
1397 autotext->w_current = w_current;
1399 if(autotext->dialog == NULL) {
1400 /* Dialog is not currently displayed - create it */
1402 autotext->dialog = autonumber_create_dialog(w_current);
1404 opt_removenum = lookup_widget(autotext->dialog, "opt_removenum");
1405 sort_order = lookup_widget(autotext->dialog, "sort_order");
1407 autonumber_sortorder_create(w_current, sort_order);
1409 gtk_dialog_set_default_response (GTK_DIALOG (autotext->dialog),
1410 GTK_RESPONSE_ACCEPT);
1412 g_signal_connect (G_OBJECT (autotext->dialog), "response",
1413 G_CALLBACK (autonumber_text_response),
1414 autotext);
1416 g_signal_connect (G_OBJECT (opt_removenum), "clicked",
1417 G_CALLBACK (autonumber_removenum_toggled),
1418 autotext);
1420 autonumber_set_state(autotext);
1422 gtk_widget_show_all(autotext->dialog);
1425 /* if the dialog is in the background or minimized: show it */
1426 gtk_window_present(GTK_WINDOW(autotext->dialog));