Use new text accessors.
[geda-gaf/berndj.git] / gschem / src / x_autonumber.c
blob5180e7167a5a42c4b56414baf0de2c04acaeb785
1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2007 Ales Hvezda
4 * Copyright (C) 1998-2007 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., 59 Temple Place, Suite 330, Boston, MA 02111 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 #include "support.h"
37 #ifdef HAVE_LIBDMALLOC
38 #include <dmalloc.h>
39 #endif
41 #define GLADE_HOOKUP_OBJECT(component,widget,name) \
42 g_object_set_data_full (G_OBJECT (component), name, \
43 gtk_widget_ref (widget), (GDestroyNotify) gtk_widget_unref)
45 /** @brief How many entries to keep in the "Search text" combo box. */
46 #define HISTORY_LENGTH 15
48 /* autonumber text structs and enums */
49 enum {
50 AUTONUMBER_SORT_DIAGONAL,
51 AUTONUMBER_SORT_YX,
52 AUTONUMBER_SORT_YX_REV,
53 AUTONUMBER_SORT_XY,
54 AUTONUMBER_SORT_XY_REV,
55 AUTONUMBER_SORT_FILE
58 enum {
59 AUTONUMBER_IGNORE,
60 AUTONUMBER_RENUMBER,
61 AUTONUMBER_RESPECT
64 enum {
65 SCOPE_SELECTED,
66 SCOPE_PAGE,
67 SCOPE_HIERARCHY
70 typedef struct autonumber_text_t AUTONUMBER_TEXT;
72 /** @brief Stored state of the autonumber text dialog */
73 struct autonumber_text_t {
74 /** @brief Search text history */
75 GList *scope_text;
77 /** @brief Scope for searching existing numbers */
78 gint scope_skip;
80 /** @brief Scope for autonumbering text */
81 gint scope_number;
83 /** @brief Overwrite existing numbers in scope */
84 gboolean scope_overwrite;
86 /** @brief Sort order */
87 gint order;
89 /** @brief Starting number for automatic numbering */
90 gint startnum;
92 /** @brief Remove numbers instead of automatic numbering */
93 gboolean removenum;
95 /** @brief Automatic assignments of slots */
96 gboolean slotting;
98 /** @brief Pointer to the dialog */
99 GtkWidget *dialog;
101 /** @brief Pointer to the GSCHEM_TOPLEVEL struct */
102 GSCHEM_TOPLEVEL *w_current;
104 /* variables used while autonumbering */
105 gchar * current_searchtext;
106 gint root_page; /* flag whether its the root page or not */
107 GList *used_numbers; /* list of used numbers */
108 GList *free_slots; /* list of FREE_SLOT objects */
109 GList *used_slots; /* list of USED_SLOT objects */
112 typedef struct autonumber_slot_t AUTONUMBER_SLOT;
114 struct autonumber_slot_t {
115 gchar *symbolname; /* or should I use the device name? (Werner) */
116 gint number; /* usually this is the refdes number */
117 gint slotnr; /* just the number of the free slot */
122 /* ***** BACK-END CODE ***************************************************** */
124 /********** compare functions for g_list_sort, ... ***********************/
125 /*! \brief GCompareFunc function to sort a list with g_list_sort().
126 * \par Function Description
127 * Compares the integer values of the gconstpointers a and b.
128 * \return -1 if a<b, 1 if a>b, 0 if a==b
130 gint autonumber_sort_numbers(gconstpointer a, gconstpointer b) {
131 if (GPOINTER_TO_INT(a) < GPOINTER_TO_INT(b))
132 return -1;
133 if (GPOINTER_TO_INT(a) > GPOINTER_TO_INT(b))
134 return 1;
135 return 0;
138 /*! \brief GCompareFunc function to sort text objects by there location
139 * \par Function Description
140 * This Function takes two <B>OBJECT*</B> arguments and compares the
141 * location of the two text objects. The first sort criteria is the x location,
142 * the second sort criteria is the y location.
143 * The Function is used as GCompareFunc by g_list_sort().
145 gint autonumber_sort_xy(gconstpointer a, gconstpointer b) {
146 OBJECT const *aa = a, *bb = b;
147 int xa, ya, xb, yb;
149 o_text_get_xy(aa, &xa, &ya);
150 o_text_get_xy(bb, &xb, &yb);
152 if (xa < xb)
153 return -1;
154 if (xa > xb)
155 return 1;
156 /* x == x */
157 if (ya > yb)
158 return -1;
159 if (ya < yb)
160 return 1;
161 return 0;
164 /*! \brief GCompareFunc function to sort text objects by there location
165 * \par Function Description
166 * This function takes two <B>OBJECT*</B> arguments and compares the
167 * location of the two text objects. The first sort criteria is the x location,
168 * the second sort criteria is the y location.
169 * This function sorts the objects in reverse order.
170 * The function is used as GCompareFunc by g_list_sort().
172 gint autonumber_sort_xy_rev(gconstpointer a, gconstpointer b) {
173 OBJECT const *aa = a, *bb = b;
174 int xa, ya, xb, yb;
176 o_text_get_xy(aa, &xa, &ya);
177 o_text_get_xy(bb, &xb, &yb);
179 if (xa < xb)
180 return 1;
181 if (xa > xb)
182 return -1;
183 /* x == x */
184 if (ya < yb)
185 return 1;
186 if (ya > yb)
187 return -1;
188 return 0;
191 /*! \brief GCompareFunc function to sort text objects by there location
192 * \par Function Description
193 * This function takes two <B>OBJECT*</B> arguments and compares the
194 * location of the two text objects. The first sort criteria is the y location,
195 * the second sort criteria is the x location.
196 * The function is used as GCompareFunc by g_list_sort().
198 int autonumber_sort_yx(gconstpointer a, gconstpointer b) {
199 OBJECT const *aa = a, *bb = b;
200 int xa, ya, xb, yb;
202 o_text_get_xy(aa, &xa, &ya);
203 o_text_get_xy(bb, &xb, &yb);
205 if (ya > yb)
206 return -1;
207 if (ya < yb)
208 return 1;
209 /* y == y */
210 if (xa < xb)
211 return -1;
212 if (xa > xb)
213 return 1;
214 return 0;
217 /*! \brief GCompareFunc function to sort text objects by there location
218 * \par Function Description
219 * This Function takes two <B>OBJECT*</B> arguments and compares the
220 * location of the two text objects. The first sort criteria is the y location,
221 * the second sort criteria is the x location.
222 * This function sorts the objects in reverse order.
223 * The function is used as GCompareFunc by g_list_sort().
225 int autonumber_sort_yx_rev(gconstpointer a, gconstpointer b) {
226 OBJECT const *aa = a, *bb = b;
227 int xa, ya, xb, yb;
229 o_text_get_xy(aa, &xa, &ya);
230 o_text_get_xy(bb, &xb, &yb);
232 if (ya > yb)
233 return 1;
234 if (ya < yb)
235 return -1;
236 /* y == y */
237 if (xa > xb)
238 return 1;
239 if (xa < xb)
240 return -1;
241 return 0;
244 /*! \brief GCompareFunc function to sort text objects by there location
245 * \par Function Description
246 * This Function takes two <B>OBJECT*</B> arguments and compares the
247 * location of the two text objects. The sort criteria is the combined x- and the
248 * y-location. The function sorts from top left to bottom right.
249 * The function is used as GCompareFunc by g_list_sort().
251 int autonumber_sort_diagonal(gconstpointer a, gconstpointer b) {
252 OBJECT const *aa = a, *bb = b;
253 int xa, ya, xb, yb;
255 o_text_get_xy(aa, &xa, &ya);
256 o_text_get_xy(bb, &xb, &yb);
258 if (xa - ya < xb - yb)
259 return -1;
260 if (xa - ya > xb - yb)
261 return 1;
262 return 0;
265 /*! \brief GCompareFunc function to access <B>AUTONUMBER_SLOT</B> object in a GList
266 * \par Function Description
267 * This Function takes two <B>AUTONUMBER_SLOT*</B> arguments and compares them.
268 * Sorting criteria is are the AUTONUMBER_SLOT members: first the symbolname, than the
269 * number and last the slotnr.
270 * If the number or the slotnr is set to zero it acts as a wildcard.
271 * The function is used as GCompareFunc by GList functions.
273 gint freeslot_compare(gconstpointer a, gconstpointer b)
275 AUTONUMBER_SLOT const *aa, *bb;
276 gint res;
277 aa = a; bb = b;
279 if ((res = strcmp(aa->symbolname, bb->symbolname)) != 0)
280 return res;
282 /* aa->symbolname == bb->symbolname */
283 if (aa->number == 0 || bb->number == 0)
284 return 0;
285 if (aa->number > bb->number)
286 return 1;
287 if (aa->number < bb->number)
288 return -1;
290 /* aa->number == bb->number */
291 if (aa->slotnr == 0 || bb->slotnr == 0)
292 return 0;
293 if (aa->slotnr > bb->slotnr)
294 return 1;
295 if (aa->slotnr < bb->slotnr)
296 return -1;
298 return 0;
301 /*! \brief Prints a <B>GList</B> of <B>AUTONUMBER_SLOT</B> elements
302 * \par Function Description
303 * This function prints the elements of a GList that contains <B>AUTONUMBER_SLOT</B> elements
304 * It is only used for debugging purposes.
306 void freeslot_print(GList *list) {
307 GList *item;
308 AUTONUMBER_SLOT *fs;
310 printf("freeslot_print(): symname, number, slot\n");
311 for (item = list; item != NULL; item = g_list_next(item)) {
312 fs = item ->data;
313 printf(" %s, %d, %d\n",fs->symbolname, fs->number, fs->slotnr);
318 /*! \brief Function to clear the databases of used parts
319 * \par Function Descriptions
320 * Just remove the list of used numbers, used slots and free slots.
322 void autonumber_clear_database (AUTONUMBER_TEXT *autotext)
324 /* cleanup everything for the next searchtext */
325 if (autotext->used_numbers != NULL) {
326 g_list_free(autotext->used_numbers);
327 autotext->used_numbers = NULL;
329 if (autotext->free_slots != NULL) {
330 g_list_foreach(autotext->free_slots, (GFunc) g_free, NULL);
331 g_list_free(autotext->free_slots);
332 autotext->free_slots = NULL;
334 if (autotext->used_slots != NULL) {
335 g_list_foreach(autotext->used_slots, (GFunc) g_free, NULL);
336 g_list_free(autotext->used_slots);
337 autotext->used_slots = NULL;
341 /*! \brief Function to test, whether the OBJECT matches the autotext criteria
342 * \par Function Description
343 * The criteria are those of the autonumber text dialog. The function decides
344 * whether the <B>OBJECT</B> has to be renumberd, ignored or taken care of when
345 * renumbering all other objects.
346 * \return one of these integer values: <B>AUTONUMBER_IGNORE</B>,
347 * <B>AUTONUMBER_RESPECT</B> or <B>AUTONUMBER_RENUMBER</B> and the current number
348 * of the text object in <B>*number</B>.
350 gint autonumber_match(AUTONUMBER_TEXT *autotext, OBJECT *o_current, gint *number)
352 gint i, len, isnumbered=1;
353 const gchar *str = NULL;
355 len = strlen(autotext->current_searchtext);
356 /* first find out whether we can ignore that object */
357 if (o_current->type != OBJ_TEXT) /* text object */
358 return AUTONUMBER_IGNORE;
360 str = o_text_get_string(o_current);
362 if (!(strlen(str) - len > 0)
363 || !g_str_has_prefix(str, autotext->current_searchtext))
364 return AUTONUMBER_IGNORE;
366 /* the string object matches with its leading characters to the searchtext */
367 /* now look for the extension, either a number or the "?" */
368 if (g_str_has_suffix (str,"?")) {
369 isnumbered = 0;
370 /* There must not be any character between the "?" and the searchtext */
371 if (strlen(str) != len+1)
372 return AUTONUMBER_IGNORE;
374 else {
375 if (!isdigit( (int) (str[len]) )) /* has at least one digit */
376 return AUTONUMBER_IGNORE;
378 for (i=len+1; str[i]; i++) /* and only digits */
379 if (!isdigit( (int) (str[i]) ))
380 return AUTONUMBER_IGNORE;
383 /* we have six cases, 3 from focus multiplied by 2 selection cases */
384 if ((autotext->root_page || autotext->scope_number == SCOPE_HIERARCHY)
385 && (o_current->selected
386 || autotext->scope_number == SCOPE_HIERARCHY || autotext->scope_number == SCOPE_PAGE)
387 && (!isnumbered || (autotext->scope_overwrite)))
388 return AUTONUMBER_RENUMBER;
390 if (isnumbered
391 && !(autotext->scope_skip == SCOPE_SELECTED
392 && !(o_current->selected) && autotext->root_page)) {
393 sscanf(&(str[len])," %d", number);
394 return AUTONUMBER_RESPECT; /* numbered objects which we don't renumber */
396 else
397 return AUTONUMBER_IGNORE; /* unnumbered objects outside the focus */
400 static enum visit_result
401 autonumber_get_used_one(OBJECT *o_current, void *userdata)
403 AUTONUMBER_TEXT *autotext = userdata;
404 gint number, numslots, slotnr, i;
405 OBJECT *o_parent, *o_numslots;
406 AUTONUMBER_SLOT *slot;
407 GList *slot_item;
408 char *numslot_str, *slot_str;
410 if (autonumber_match(autotext, o_current, &number) == AUTONUMBER_RESPECT) {
411 /* check slot and maybe add it to the lists */
412 o_parent = o_current->attached_to;
413 if (autotext->slotting && o_parent != NULL) {
414 /* check for slotted symbol */
415 if ((numslot_str = o_attrib_search_numslots(o_parent, &o_numslots)) != NULL) {
416 sscanf(numslot_str," %d",&numslots);
417 g_free(numslot_str);
419 if (numslots > 0) {
420 slot_str=o_attrib_search_attrib_name(o_parent->attribs,"slot",0);
421 if (slot_str == NULL) {
422 s_log_message(_("slotted object without slot attribute may cause "
423 "problems when autonumbering slots\n"));
425 else {
426 sscanf(slot_str, " %d", &slotnr);
427 slot = g_new(AUTONUMBER_SLOT,1);
428 slot->number = number;
429 slot->slotnr = slotnr;
430 slot->symbolname = o_parent->complex_basename;
433 slot_item = g_list_find_custom(autotext->used_slots,
434 slot,
435 (GCompareFunc) freeslot_compare);
436 if (slot_item != NULL) { /* duplicate slot in used_slots */
437 s_log_message(_("duplicate slot may cause problems: "
438 "[symbolname=%s, number=%d, slot=%d]\n"),
439 slot->symbolname, slot->number, slot->slotnr);
440 g_free(slot);
442 else {
443 autotext->used_slots = g_list_insert_sorted(autotext->used_slots,
444 slot,
445 (GCompareFunc) freeslot_compare);
447 slot_item = g_list_find_custom(autotext->free_slots,
448 slot,
449 (GCompareFunc) freeslot_compare);
450 if (slot_item == NULL) {
451 /* insert all slots to the list, except of the current one */
452 for (i=1; i <= numslots; i++) {
453 if (i != slotnr) {
454 slot = g_memdup(slot, sizeof(AUTONUMBER_SLOT));
455 slot->slotnr = i;
456 autotext->free_slots = g_list_insert_sorted(autotext->free_slots,
457 slot,
458 (GCompareFunc) freeslot_compare);
462 else {
463 g_free(slot_item->data);
464 autotext->free_slots = g_list_delete_link(autotext->free_slots, slot_item);
471 /* put number into the used list */
472 autotext->used_numbers = g_list_insert_sorted(autotext->used_numbers,
473 GINT_TO_POINTER(number),
474 (GCompareFunc) autonumber_sort_numbers);
477 return VISIT_RES_OK;
480 /*! \brief Creates a list of already numbered objects and slots
481 * \par Function Description
482 * This function collects the used numbers of a single schematic page.
483 * The used element numbers are stored in a GList container
484 * inside the <B>AUTONUMBER_TEXT</B> struct.
485 * The slotting container is a little bit different. It stores free slots of
486 * multislotted symbols, that were used only partially.
487 * The criteria are derived from the autonumber dialog entries.
489 void autonumber_get_used(PAGE *page, AUTONUMBER_TEXT *autotext)
491 s_visit_page(page, &autonumber_get_used_one, autotext, VISIT_ANY, 1);
495 /*! \brief Gets or generates free numbers for the autonumbering process.
496 * \par Function Description
497 * This function gets or generates new numbers for the <B>OBJECT o_current</B>.
498 * It uses the element numbers <B>used_numbers</B> and the list of the free slots
499 * <B>free_slots</B> of the <B>AUTONUMBER_TEXT</B> struct.
500 * \return
501 * The new number is returned into the <B>number</B> parameter.
502 * <B>slot</B> is set if autoslotting is active, else it is set to zero.
504 void autonumber_get_new_numbers(AUTONUMBER_TEXT *autotext, OBJECT *o_current,
505 gint *number, gint *slot)
507 GList *item;
508 gint new_number, numslots, i;
509 AUTONUMBER_SLOT *freeslot;
510 OBJECT *o_parent = NULL, *o_numslots;
511 GList *freeslot_item;
512 gchar *numslot_str;
514 new_number = autotext->startnum;
516 /* Check for slots first */
517 /* 1. are there any unused slots in the database? */
518 o_parent = o_current->attached_to;
519 if (autotext->slotting && o_parent != NULL) {
520 freeslot = g_new(AUTONUMBER_SLOT,1);
521 freeslot->symbolname = o_parent->complex_basename;
522 freeslot->number = 0;
523 freeslot->slotnr = 0;
524 freeslot_item = g_list_find_custom(autotext->free_slots,
525 freeslot,
526 (GCompareFunc) freeslot_compare);
527 g_free(freeslot);
528 /* Yes! -> remove from database, apply it */
529 if (freeslot_item != NULL) {
530 freeslot = freeslot_item->data;
531 *number = freeslot->number;
532 *slot = freeslot->slotnr;
533 g_free(freeslot);
534 autotext->free_slots = g_list_delete_link(autotext->free_slots, freeslot_item);
536 return;
540 /* get a new number */
541 item = autotext->used_numbers;
542 while (1) {
543 while (item != NULL && GPOINTER_TO_INT(item->data) < new_number)
544 item = g_list_next(item);
546 if (item == NULL || GPOINTER_TO_INT(item->data) > new_number)
547 break;
548 else /* new_number == item->data */
549 new_number++;
551 *number = new_number;
552 *slot = 0;
554 /* insert the new number to the used list */
555 autotext->used_numbers = g_list_insert_sorted(autotext->used_numbers,
556 GINT_TO_POINTER(new_number),
557 (GCompareFunc) autonumber_sort_numbers);
559 /* 3. is o_current a slotted object ? */
560 if ((autotext->slotting) && o_parent != NULL) {
561 if ((numslot_str = o_attrib_search_numslots(o_parent, &o_numslots)) != NULL) {
562 sscanf(numslot_str," %d",&numslots);
563 g_free(numslot_str);
564 if (numslots > 0) {
565 /* Yes! -> new number and slot=1; add the other slots to the database */
566 *slot = 1;
567 for (i=2; i <=numslots; i++) {
568 freeslot = g_new(AUTONUMBER_SLOT,1);
569 freeslot->symbolname = o_parent->complex_basename;
570 freeslot->number = new_number;
571 freeslot->slotnr = i;
572 autotext->free_slots = g_list_insert_sorted(autotext->free_slots,
573 freeslot,
574 (GCompareFunc) freeslot_compare);
581 /** @brief Removes the number from the element.
583 * This function updates the text content of the \a o_current object.
585 * @param autotext Pointer to the state structure
586 * @param o_current Pointer to the object from which to remove the number
589 void autonumber_remove_number(AUTONUMBER_TEXT * autotext, OBJECT *o_current)
591 OBJECT *o_parent, *o_slot;
592 gchar *slot_str;
593 gchar *str = NULL;
595 /* replace old text */
596 str = g_strdup_printf("%s?", autotext->current_searchtext);
597 o_text_set_string(o_current, str);
598 g_free (str);
600 /* redraw the text */
601 o_erase_single(autotext->w_current, o_current);
602 o_text_recreate(o_current);
603 o_text_draw(autotext->w_current, o_current);
605 /* remove the slot attribute if slotting is active */
606 if (autotext->slotting) {
607 /* get the slot attribute */
608 o_parent = o_current->attached_to;
609 if (o_parent != NULL) {
610 slot_str = o_attrib_search_slot(o_parent, &o_slot);
611 if (slot_str != NULL && o_slot != NULL) {
612 g_free(slot_str);
613 /* delete the slot attribute */
614 o_selection_remove (autotext->w_current->toplevel->page_current->selection_list, o_slot);
615 o_delete (autotext->w_current, o_slot);
619 autotext->w_current->toplevel->page_current->CHANGED = 1;
622 /*! \brief Changes the number <B>OBJECT</B> element. Changes the slot attribute.
623 * \par Function Description
624 * This function updates the text content of the <B>o_current</B> object.
625 * If the <B>slot</B> value is not zero. It updates the slot attribute of the
626 * complex element that is also the parent object of the o_current element.
628 void autonumber_apply_new_text(AUTONUMBER_TEXT * autotext, OBJECT *o_current,
629 gint number, gint slot)
631 OBJECT *o_parent, *o_slot;
632 gchar *slot_str;
633 gchar *str = NULL;
635 /* add the slot as attribute to the object */
636 o_parent = o_current->attached_to;
637 if (slot != 0 && o_parent != NULL) {
638 slot_str = o_attrib_search_slot(o_parent, &o_slot);
639 if (slot_str != NULL) {
640 /* update the slot attribute */
641 g_free(slot_str);
642 slot_str = g_strdup_printf("slot=%d",slot);
643 o_text_set_string(o_slot, slot_str);
644 g_free (slot_str);
645 o_erase_single(autotext->w_current, o_slot);
646 o_text_recreate(o_slot);
647 o_text_draw(autotext->w_current, o_slot);
649 else {
650 /* create a new attribute and attach it */
651 o_attrib_add_attrib(autotext->w_current,
652 g_strdup_printf("slot=%d",slot),
653 INVISIBLE, SHOW_NAME_VALUE,
654 o_parent);
656 o_attrib_slot_update(autotext->w_current->toplevel, o_parent);
659 /* replace old text */
660 str = g_strdup_printf("%s%d", autotext->current_searchtext, number);
661 o_text_set_string(o_current, str);
662 g_free (str);
664 /* redraw the text */
665 o_erase_single(autotext->w_current, o_current);
666 o_text_recreate(o_current);
667 o_text_draw(autotext->w_current, o_current);
668 autotext->w_current->toplevel->page_current->CHANGED = 1;
671 struct autonumber_visit_context {
672 gchar *searchtext;
673 GList *searchtext_list;
674 GList *matching_objects;
675 AUTONUMBER_TEXT *autotext;
678 static enum visit_result
679 collect_searchtext_one(OBJECT *o_current, void *userdata)
681 struct autonumber_visit_context *context = userdata;
682 gchar *new_searchtext;
683 int i;
685 if (o_current->type == OBJ_TEXT) {
686 if (context->autotext->scope_number == SCOPE_HIERARCHY
687 || context->autotext->scope_number == SCOPE_PAGE
688 || ((context->autotext->scope_number == SCOPE_SELECTED)
689 && (o_current->selected))) {
690 const gchar *str = o_text_get_string(o_current);
691 if (g_str_has_prefix (str, context->searchtext)) {
692 /* the beginnig of the current text matches with the searchtext now */
693 /* strip of the trailing [0-9?] chars and add it too the searchtext */
694 for (i = strlen (str)-1;
695 (i >= strlen(context->searchtext))
696 && (str[i] == '?' || isdigit( (int) (str[i]) ));
697 i--)
698 ; /* void */
700 new_searchtext = g_strndup (str, i+1);
701 if (g_list_find_custom(context->searchtext_list, new_searchtext,
702 (GCompareFunc) strcmp) == NULL ) {
703 context->searchtext_list = g_list_append(context->searchtext_list,
704 new_searchtext);
706 else {
707 g_free(new_searchtext);
713 return VISIT_RES_OK;
716 static enum visit_result
717 autonumber_match_one(OBJECT *o_current, void *userdata)
719 struct autonumber_visit_context *context = userdata;
720 gint number;
722 if (autonumber_match(context->autotext, o_current, &number)
723 == AUTONUMBER_RENUMBER) {
724 /* put number into the used list */
725 context->matching_objects = g_list_append(context->matching_objects,
726 o_current);
729 return VISIT_RES_OK;
732 /*! \brief Handles all the options of the autonumber text dialog
733 * \par Function Description
734 * This function is the master of all autonumber code. It receives the options of
735 * the the autonumber text dialog in an <B>AUTONUMBER_TEXT</B> structure.
736 * First it collects all pages of a hierarchical schematic.
737 * Second it gets all matching text elements for the searchtext.
738 * Then it renumbers all text elements of all schematic pages. The renumbering
739 * follows the rules of the parameters given in the autonumber text dialog.
741 void autonumber_text_autonumber(AUTONUMBER_TEXT *autotext)
743 GList *pages;
744 GList *text_item, *obj_item, *page_item;
745 OBJECT *o_current;
746 GSCHEM_TOPLEVEL *w_current;
747 gchar *scope_text;
748 gint number, slot;
749 struct autonumber_visit_context context = {
750 .searchtext_list = NULL,
751 .matching_objects = NULL,
752 .autotext = autotext,
755 w_current = autotext->w_current;
756 autotext->current_searchtext = NULL;
757 autotext->root_page = 1;
758 autotext->used_numbers = NULL;
759 autotext->free_slots = NULL;
760 autotext->used_slots = NULL;
762 scope_text = g_list_first(autotext->scope_text)->data;
764 /* Step1: get all pages of the hierarchy */
765 pages = s_hierarchy_traversepages(w_current->toplevel,
766 w_current->toplevel->page_current,
767 HIERARCHY_NODUPS);
769 /* g_list_foreach(pages, (GFunc) s_hierarchy_print_page, NULL); */
771 /* Step2: if searchtext has an asterisk at the end we have to find
772 all matching searchtextes.
774 Example: "refdes=*" will match each text that starts with "refdes="
775 and has a trailing "?" or a trailing number if the "all"-option is set.
776 We get a list of possible prefixes: refdes=R, refdes=C.
778 If there is only one search pattern, it becomes a single item
779 in the searchtext list */
781 if (strlen(scope_text) == 0) {
782 s_log_message(_("No searchstring given in autonumber text.\n"));
783 return; /* error */
785 else if (g_str_has_suffix(scope_text,"?") == TRUE) {
786 /* single searchtext, strip of the "?" */
787 context.searchtext = g_strndup(scope_text, strlen(scope_text)-1);
788 context.searchtext_list = g_list_append(context.searchtext_list,
789 context.searchtext);
791 else if (g_str_has_suffix(scope_text,"*") == TRUE) {
792 /* strip of the "*" */
793 context.searchtext = g_strndup(scope_text, strlen(scope_text)-1);
794 /* collect all the possible searchtexts in all pages of the hierarchy */
795 for (page_item = pages; page_item != NULL; page_item = g_list_next(page_item)) {
796 PAGE *page = page_item->data;
798 s_toplevel_goto_page(w_current->toplevel, page);
799 /* iterate over all objects an look for matching searchtext's */
800 s_visit_page(page, &collect_searchtext_one, &context, VISIT_ANY, 1);
801 if (autotext->scope_number == SCOPE_SELECTED || autotext->scope_number == SCOPE_PAGE)
802 break; /* search only in the first page */
804 g_free(context.searchtext);
806 else {
807 s_log_message(_("No '*' or '?' given at the end of the autonumber text.\n"));
808 return;
811 /* Step3: iterate over the search items in the list */
812 for (text_item = context.searchtext_list; text_item != NULL; text_item = g_list_next(text_item)) {
813 autotext->current_searchtext = text_item->data;
814 /* printf("autonumber_text_autonumber: searchtext %s\n", autotext->current_searchtext); */
815 /* decide whether to renumber page by page or get a global used-list */
816 if (autotext->scope_skip == SCOPE_HIERARCHY) { /* whole hierarchy database */
817 /* renumbering all means that no db is required */
818 if (!(autotext->scope_number == SCOPE_HIERARCHY
819 && autotext->scope_overwrite)) {
820 for (page_item = pages; page_item != NULL; page_item = g_list_next(page_item)) {
821 PAGE *page = page_item->data;
823 autotext->root_page = (pages->data == page);
824 s_toplevel_goto_page(w_current->toplevel, page);
825 autonumber_get_used(page, autotext);
830 /* renumber the elements */
831 for (page_item = pages; page_item != NULL; page_item = g_list_next(page_item)) {
832 PAGE *page = page_item->data;
834 s_toplevel_goto_page(w_current->toplevel, page);
835 autotext->root_page = (pages->data == page);
836 /* build a page database if we're numbering pagebypage or selection only*/
837 if (autotext->scope_skip == SCOPE_PAGE || autotext->scope_skip == SCOPE_SELECTED) {
838 autonumber_get_used(page, autotext);
841 /* RENUMBER CODE FOR ONE PAGE AND ONE SEARCHTEXT*/
842 /* 1. get objects to renumber */
843 s_visit_page(page, &autonumber_match_one, &context, VISIT_ANY, 1);
845 /* 2. sort object list */
846 switch (autotext->order) {
847 case AUTONUMBER_SORT_YX:
848 context.matching_objects = g_list_sort(context.matching_objects,
849 autonumber_sort_yx);
850 break;
851 case AUTONUMBER_SORT_YX_REV:
852 context.matching_objects = g_list_sort(context.matching_objects,
853 autonumber_sort_yx_rev);
854 break;
855 case AUTONUMBER_SORT_XY:
856 context.matching_objects = g_list_sort(context.matching_objects,
857 autonumber_sort_xy);
858 break;
859 case AUTONUMBER_SORT_XY_REV:
860 context.matching_objects = g_list_sort(context.matching_objects,
861 autonumber_sort_xy_rev);
862 break;
863 case AUTONUMBER_SORT_DIAGONAL:
864 context.matching_objects = g_list_sort(context.matching_objects,
865 autonumber_sort_diagonal);
866 break;
867 default:
868 ; /* unsorted file order */
871 /* 3. renumber/reslot the objects */
872 for (obj_item = context.matching_objects; obj_item != NULL; obj_item = g_list_next(obj_item)) {
873 o_current= obj_item->data;
874 if(autotext->removenum) {
875 autonumber_remove_number(autotext, o_current);
876 } else {
877 /* get valid numbers from the database */
878 autonumber_get_new_numbers(autotext, o_current, &number, &slot);
879 /* and apply it. TODO: join these two functions */
880 autonumber_apply_new_text(autotext, o_current, number, slot);
883 g_list_free(context.matching_objects);
884 context.matching_objects = NULL;
886 /* destroy the page database */
887 if (autotext->scope_skip == SCOPE_PAGE
888 || autotext->scope_skip == SCOPE_SELECTED)
889 autonumber_clear_database(autotext);
891 if (autotext->scope_number == SCOPE_SELECTED
892 || autotext->scope_number == SCOPE_PAGE)
893 break; /* only renumber the parent page (the first page) */
895 autonumber_clear_database(autotext); /* cleanup */
898 /* cleanup and redraw all*/
899 g_list_foreach(context.searchtext_list, (GFunc) g_free, NULL);
900 g_list_free(context.searchtext_list);
901 /* go back to the root page */
902 s_toplevel_goto_page(w_current->toplevel, pages->data);
903 o_redraw_all(w_current);
904 g_list_free(pages);
905 o_undo_savestate(w_current, UNDO_ALL);
908 /* ***** UTILITY GUI FUNCTIONS (move to a separate file in the future?) **** */
910 /*! \brief Put the icons and the text into the sortorder combobox
911 * \par Function Description
912 * Load all bitmaps for the combobox and store them together with the label
913 * in a GtkListStore.
915 void autonumber_sortorder_create(GSCHEM_TOPLEVEL *w_current, GtkWidget *sort_order)
917 GtkListStore *store;
918 GtkTreeIter iter;
919 GtkCellRenderer *renderer;
920 GdkPixbuf *pixbuf;
921 gchar *path;
922 GError *error=NULL;
924 gchar *filenames[] = {"gschem-diagonal.png",
925 "gschem-top2bottom.png", "gschem-bottom2top.png",
926 "gschem-left2right.png", "gschem-right2left.png",
927 "gschem-fileorder.png",
928 NULL};
929 gchar *names[] = {N_("Diagonal"),
930 N_("Top to bottom"), N_("Bottom to top"),
931 N_("Left to right"), N_("Right to left"),
932 N_("File order"),
933 NULL};
934 gint i;
936 store = gtk_list_store_new(2, G_TYPE_STRING, GDK_TYPE_PIXBUF);
938 for (i=0; filenames[i] != NULL; i++) {
939 path=g_build_filename(w_current->toplevel->bitmap_directory,
940 filenames[i], NULL);
941 pixbuf = gdk_pixbuf_new_from_file(path, &error);
942 g_free(path);
943 gtk_list_store_append(store, &iter);
944 gtk_list_store_set(store, &iter,
945 0, _(names[i]),
946 1, pixbuf,
947 -1);
950 gtk_combo_box_set_model(GTK_COMBO_BOX(sort_order), GTK_TREE_MODEL(store));
951 renderer = gtk_cell_renderer_text_new ();
953 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (sort_order),
954 renderer, TRUE);
955 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (sort_order),
956 renderer, "text", 0, NULL);
957 renderer = gtk_cell_renderer_pixbuf_new();
958 g_object_set(G_OBJECT(renderer), "xpad", 5, "ypad", 5, NULL);
960 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (sort_order),
961 renderer, FALSE);
962 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (sort_order),
963 renderer, "pixbuf", 1, NULL);
966 /* ***** STATE STRUCT HANDLING (interface between GUI and backend code) **** */
968 /** @brief Adds a line to the search text history list
970 * Function makes sure that: 1) There are no duplicates in the list and 2) the
971 * last search text is always at the top of the list.
973 GList *autonumber_history_add(GList *history, gchar *text)
975 /* Search for this text in history and delete it (so we don't have
976 * duplicate entries) */
978 GList *cur;
980 cur=history;
981 while(cur!=NULL) {
982 if(!strcmp(text, cur->data)) {
983 history=g_list_remove_link(history, cur);
985 g_free(cur->data);
986 g_list_free(cur);
987 break;
989 cur=g_list_next(cur);
992 /* Add the new text at the beginning of the list */
994 history=g_list_prepend(history, text);
996 /* Truncate history */
997 while(g_list_length(history) > HISTORY_LENGTH) {
998 GList *last = g_list_last(history);
1000 history = g_list_remove_link(history, last);
1002 g_free(last->data);
1003 g_list_free(last);
1006 return history;
1009 /** @brief Allocate and initialize the state structure
1011 * @return Pointer to the allocated structure or NULL on error.
1013 AUTONUMBER_TEXT *autonumber_init_state()
1015 AUTONUMBER_TEXT *autotext;
1017 /* Default contents of the combo box history */
1018 gchar *default_text[] = {
1019 "refdes=*",
1020 "refdes=C?",
1021 "refdes=D?",
1022 "refdes=I?",
1023 "refdes=L?",
1024 "refdes=Q?",
1025 "refdes=R?",
1026 "refdes=T?",
1027 "refdes=U?",
1028 "refdes=X?",
1029 "netname=*",
1030 "netname=A?",
1031 "netname=D?",
1032 NULL
1034 gchar **t;
1036 autotext = g_new(AUTONUMBER_TEXT, 1);
1038 if(autotext==NULL) return NULL;
1040 autotext->scope_text = NULL;
1041 t=default_text;
1042 while(*t!=NULL) {
1043 autotext->scope_text=g_list_append(autotext->scope_text,
1044 g_strdup(*t));
1045 t++;
1048 autotext->scope_skip = SCOPE_PAGE;
1049 autotext->scope_number = SCOPE_SELECTED;
1051 autotext->scope_overwrite = 0;
1052 autotext->order = AUTONUMBER_SORT_DIAGONAL;
1054 autotext->startnum=1;
1056 autotext->removenum=0;
1057 autotext->slotting=0;
1059 autotext->dialog = NULL;
1061 return autotext;
1064 /** @brief Restore the settings for the autonumber text dialog
1066 * @param autotext Pointer to the state struct.
1068 void autonumber_set_state(AUTONUMBER_TEXT *autotext)
1070 GtkWidget *widget;
1071 GtkTreeModel *model;
1072 GList *el;
1073 /* Scope */
1075 /* Search text history */
1076 widget = lookup_widget(autotext->dialog, "scope_text");
1078 /* Simple way to clear the ComboBox. Owen from #gtk+ says:
1080 * Yeah, it's just slightly "shady" ... if you want to stick to fully
1081 * advertised API, you need to remember how many rows you added and
1082 * use gtk_combo_box_remove_text() */
1084 model = gtk_combo_box_get_model(GTK_COMBO_BOX(widget));
1085 gtk_list_store_clear(GTK_LIST_STORE(model));
1087 for (el= autotext->scope_text; el != NULL; el=g_list_next(el)) {
1088 gtk_combo_box_append_text(GTK_COMBO_BOX(widget), el->data);
1091 widget = gtk_bin_get_child(GTK_BIN(widget));
1092 gtk_entry_set_text(GTK_ENTRY(widget), g_list_first(autotext->scope_text)->data);
1094 widget = lookup_widget(autotext->dialog, "scope_skip");
1095 gtk_combo_box_set_active(GTK_COMBO_BOX(widget),
1096 autotext->scope_skip);
1098 widget = lookup_widget(autotext->dialog, "scope_number");
1099 gtk_combo_box_set_active(GTK_COMBO_BOX(widget),
1100 autotext->scope_number);
1102 widget = lookup_widget(autotext->dialog, "scope_overwrite");
1103 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),
1104 autotext->scope_overwrite);
1106 /* Options */
1107 widget = lookup_widget(autotext->dialog, "opt_startnum");
1108 gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget),
1109 autotext->startnum);
1111 widget = lookup_widget(autotext->dialog, "sort_order");
1112 gtk_combo_box_set_active(GTK_COMBO_BOX(widget), autotext->order);
1114 widget = lookup_widget(autotext->dialog, "opt_removenum");
1115 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),
1116 autotext->removenum);
1118 widget = lookup_widget(autotext->dialog, "opt_slotting");
1119 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),
1120 autotext->slotting);
1123 /** @brief Get the settings from the autonumber text dialog
1125 * Get the settings from the autonumber text dialog and store it in the
1126 * <B>AUTONUMBER_TEXT</B> structure.
1128 * @param autotext Pointer to the state struct.
1130 void autonumber_get_state(AUTONUMBER_TEXT *autotext)
1132 GtkWidget *widget;
1133 gchar *text;
1135 /* Scope */
1137 /* Search text history */
1138 widget = lookup_widget(autotext->dialog, "scope_text");
1139 widget = gtk_bin_get_child(GTK_BIN(widget));
1140 text = g_strdup(gtk_entry_get_text( GTK_ENTRY(widget)));
1142 autotext->scope_text=autonumber_history_add(autotext->scope_text, text);
1144 widget = lookup_widget(autotext->dialog, "scope_skip");
1145 autotext->scope_skip = gtk_combo_box_get_active( GTK_COMBO_BOX(widget) );
1147 widget = lookup_widget(autotext->dialog, "scope_number");
1148 autotext->scope_number = gtk_combo_box_get_active(GTK_COMBO_BOX(widget) );
1150 widget = lookup_widget(autotext->dialog, "scope_overwrite");
1151 autotext->scope_overwrite = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
1153 /* Sort order */
1154 widget = lookup_widget(autotext->dialog, "sort_order");
1155 autotext->order= gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
1157 /* Options */
1158 widget = lookup_widget(autotext->dialog, "opt_startnum");
1159 autotext->startnum=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
1161 widget = lookup_widget(autotext->dialog, "opt_removenum");
1162 autotext->removenum = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
1164 widget = lookup_widget(autotext->dialog, "opt_slotting");
1165 autotext->slotting = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
1168 /* ***** CALLBACKS (functions that get called directly from the GTK) ******* */
1170 /*! \brief response callback for the autonumber text dialog
1171 * \par Function Description
1172 * The function just closes the dialog if the close button is pressed or the
1173 * user closes the dialog window.
1174 * Triggering the apply button will call the autonumber action functions.
1176 void autonumber_text_response(GtkWidget * widget, gint response,
1177 AUTONUMBER_TEXT *autotext)
1179 switch (response) {
1180 case GTK_RESPONSE_ACCEPT:
1181 autonumber_get_state(autotext);
1182 if (autotext->removenum == TRUE && autotext->scope_overwrite == FALSE) {
1183 /* temporarly set the overwrite flag */
1184 autotext->scope_overwrite = TRUE;
1185 autonumber_text_autonumber(autotext);
1186 autotext->scope_overwrite = FALSE;
1188 else {
1189 autonumber_text_autonumber(autotext);
1191 break;
1192 case GTK_RESPONSE_REJECT:
1193 case GTK_RESPONSE_DELETE_EVENT:
1194 gtk_widget_destroy(autotext->dialog);
1195 autotext->dialog = NULL;
1196 break;
1197 default:
1198 printf("ERROR: autonumber_text_response(): strange signal %d\n",response);
1203 /** @brief Callback that activates or deactivates "overwrite existing numbers"
1204 * check box.
1206 * This gets called each time "remove numbers" check box gets clicked.
1208 void autonumber_removenum_toggled(GtkWidget * opt_removenum,
1209 AUTONUMBER_TEXT *autotext)
1211 GtkWidget *scope_overwrite;
1213 scope_overwrite=lookup_widget(autotext->dialog, "scope_overwrite");
1215 /* toggle activity of scope overwrite with respect to removenum */
1216 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(opt_removenum))) {
1217 gtk_widget_set_sensitive(scope_overwrite, 0);
1218 } else {
1219 gtk_widget_set_sensitive(scope_overwrite, 1);
1224 /* ***** DIALOG SET-UP ***************************************************** */
1226 /** @brief Creates the autonumber text dialog.
1228 * Dialog is not shown. No callbacks are registered. This is basically
1229 * unmodified code returned by Glade.
1231 * Only modification was the following substitution:
1233 * %s/create_pixmap (autonumber_text, "\(.*\)")/autonumber_create_pixmap("gschem-\1", w_current)/
1235 * and addition of the "w_current" parameter.
1237 * @param w_current Pointer to the top level struct.
1238 * @return Pointer to the dialog window.
1240 GtkWidget* autonumber_create_dialog(GSCHEM_TOPLEVEL *w_current)
1242 GtkWidget *autonumber_text;
1243 GtkWidget *vbox1;
1244 GtkWidget *alignment1;
1245 GtkWidget *vbox3;
1246 GtkWidget *table1;
1247 GtkWidget *label4;
1248 GtkWidget *scope_text;
1249 GtkWidget *label8;
1250 GtkWidget *label6;
1251 GtkWidget *scope_number;
1252 GtkWidget *scope_skip;
1253 GtkWidget *scope_overwrite;
1254 GtkWidget *label1;
1255 GtkWidget *alignment3;
1256 GtkWidget *vbox4;
1257 GtkWidget *table3;
1258 GtkWidget *label12;
1259 GtkWidget *label13;
1260 GtkObject *opt_startnum_adj;
1261 GtkWidget *opt_startnum;
1262 GtkWidget *sort_order;
1263 GtkWidget *opt_removenum;
1264 GtkWidget *opt_slotting;
1265 GtkWidget *label3;
1268 autonumber_text = gschem_dialog_new_with_buttons(_("Autonumber text"),
1269 GTK_WINDOW(w_current->main_window),
1270 0, /* not modal */
1271 "autonumber", w_current,
1272 GTK_STOCK_CLOSE,
1273 GTK_RESPONSE_REJECT,
1274 GTK_STOCK_APPLY,
1275 GTK_RESPONSE_ACCEPT,
1276 NULL);
1277 /* Set the alternative button order (ok, cancel, help) for other systems */
1278 gtk_dialog_set_alternative_button_order(GTK_DIALOG(autonumber_text),
1279 GTK_RESPONSE_ACCEPT,
1280 GTK_RESPONSE_REJECT,
1281 -1);
1283 gtk_window_position (GTK_WINDOW (autonumber_text),
1284 GTK_WIN_POS_MOUSE);
1286 gtk_container_border_width(GTK_CONTAINER(autonumber_text),
1287 DIALOG_BORDER_SPACING);
1288 vbox1 = GTK_DIALOG(autonumber_text)->vbox;
1289 gtk_box_set_spacing(GTK_BOX(vbox1), DIALOG_V_SPACING);
1291 /* scope section */
1292 label1 = gtk_label_new (_("<b>Scope</b>"));
1293 gtk_label_set_use_markup (GTK_LABEL (label1), TRUE);
1294 gtk_misc_set_alignment (GTK_MISC(label1), 0, 0);
1295 gtk_box_pack_start (GTK_BOX(vbox1), label1, TRUE, TRUE, 0);
1296 gtk_widget_show (label1);
1298 alignment1 = gtk_alignment_new (0, 0, 1, 1);
1299 gtk_widget_show (alignment1);
1300 gtk_box_pack_start (GTK_BOX (vbox1), alignment1, TRUE, TRUE, 0);
1301 gtk_alignment_set_padding (GTK_ALIGNMENT (alignment1),
1302 0, 0, DIALOG_INDENTATION, 0);
1304 vbox3 = gtk_vbox_new (FALSE, 0);
1305 gtk_widget_show (vbox3);
1306 gtk_container_add (GTK_CONTAINER (alignment1), vbox3);
1308 table1 = gtk_table_new (3, 2, FALSE);
1309 gtk_widget_show (table1);
1310 gtk_box_pack_start (GTK_BOX (vbox3), table1, TRUE, TRUE, 0);
1311 gtk_table_set_row_spacings (GTK_TABLE (table1), DIALOG_V_SPACING);
1312 gtk_table_set_col_spacings (GTK_TABLE (table1), DIALOG_H_SPACING);
1314 label4 = gtk_label_new (_("Search for:"));
1315 gtk_widget_show (label4);
1316 gtk_table_attach (GTK_TABLE (table1), label4, 0, 1, 0, 1,
1317 (GtkAttachOptions) (GTK_FILL),
1318 (GtkAttachOptions) (0), 0, 0);
1319 gtk_misc_set_alignment (GTK_MISC (label4), 0, 0.5);
1321 scope_text = gtk_combo_box_entry_new_text ();
1322 gtk_entry_set_activates_default(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(scope_text))), TRUE);
1323 gtk_widget_show (scope_text);
1324 gtk_table_attach (GTK_TABLE (table1), scope_text, 1, 2, 0, 1,
1325 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1326 (GtkAttachOptions) (GTK_FILL), 0, 0);
1328 label8 = gtk_label_new (_("Autonumber text in:"));
1329 gtk_widget_show (label8);
1330 gtk_table_attach (GTK_TABLE (table1), label8, 0, 1, 1, 2,
1331 (GtkAttachOptions) (GTK_FILL),
1332 (GtkAttachOptions) (0), 0, 0);
1333 gtk_misc_set_alignment (GTK_MISC (label8), 0, 0.5);
1335 label6 = gtk_label_new (_("Skip numbers found in:"));
1336 gtk_widget_show (label6);
1337 gtk_table_attach (GTK_TABLE (table1), label6, 0, 1, 2, 3,
1338 (GtkAttachOptions) (GTK_FILL),
1339 (GtkAttachOptions) (0), 0, 0);
1340 gtk_misc_set_alignment (GTK_MISC (label6), 0, 0.5);
1342 scope_number = gtk_combo_box_new_text ();
1343 gtk_widget_show (scope_number);
1344 gtk_table_attach (GTK_TABLE (table1), scope_number, 1, 2, 1, 2,
1345 (GtkAttachOptions) (GTK_FILL),
1346 (GtkAttachOptions) (GTK_FILL), 0, 0);
1347 gtk_combo_box_append_text (GTK_COMBO_BOX (scope_number), _("Selected objects"));
1348 gtk_combo_box_append_text (GTK_COMBO_BOX (scope_number), _("Current page"));
1349 gtk_combo_box_append_text (GTK_COMBO_BOX (scope_number), _("Whole hierarchy"));
1351 scope_skip = gtk_combo_box_new_text ();
1352 gtk_widget_show (scope_skip);
1353 gtk_table_attach (GTK_TABLE (table1), scope_skip, 1, 2, 2, 3,
1354 (GtkAttachOptions) (GTK_FILL),
1355 (GtkAttachOptions) (GTK_FILL), 0, 0);
1356 gtk_combo_box_append_text (GTK_COMBO_BOX (scope_skip), _("Selected objects"));
1357 gtk_combo_box_append_text (GTK_COMBO_BOX (scope_skip), _("Current page"));
1358 gtk_combo_box_append_text (GTK_COMBO_BOX (scope_skip), _("Whole hierarchy"));
1360 scope_overwrite = gtk_check_button_new_with_mnemonic (_("Overwrite existing numbers"));
1361 gtk_widget_show (scope_overwrite);
1362 gtk_box_pack_start (GTK_BOX (vbox3), scope_overwrite, FALSE, FALSE, 6);
1364 /* Options section */
1365 label3 = gtk_label_new (_("<b>Options</b>"));
1366 gtk_label_set_use_markup (GTK_LABEL (label3), TRUE);
1367 gtk_misc_set_alignment(GTK_MISC(label3), 0, 0);
1368 gtk_widget_show (label3);
1369 gtk_box_pack_start(GTK_BOX(vbox1), label3, TRUE, TRUE, 0);
1371 alignment3 = gtk_alignment_new (0, 0, 1, 1);
1372 gtk_widget_show (alignment3);
1373 gtk_box_pack_start(GTK_BOX(vbox1), alignment3, TRUE, TRUE, 0);
1374 gtk_alignment_set_padding (GTK_ALIGNMENT (alignment3),
1375 0, 0, DIALOG_INDENTATION, 0);
1377 vbox4 = gtk_vbox_new (FALSE, 3);
1378 gtk_widget_show (vbox4);
1379 gtk_container_add (GTK_CONTAINER (alignment3), vbox4);
1381 table3 = gtk_table_new (2, 2, FALSE);
1382 gtk_widget_show (table3);
1383 gtk_box_pack_start (GTK_BOX (vbox4), table3, TRUE, TRUE, 0);
1384 gtk_table_set_row_spacings (GTK_TABLE (table3), DIALOG_V_SPACING);
1385 gtk_table_set_col_spacings (GTK_TABLE (table3), DIALOG_H_SPACING);
1387 label12 = gtk_label_new (_("Starting number:"));
1388 gtk_widget_show (label12);
1389 gtk_table_attach (GTK_TABLE (table3), label12, 0, 1, 0, 1,
1390 (GtkAttachOptions) (GTK_FILL),
1391 (GtkAttachOptions) (0), 0, 0);
1392 gtk_misc_set_alignment (GTK_MISC (label12), 0, 0.5);
1394 label13 = gtk_label_new (_("Sort order:"));
1395 gtk_widget_show (label13);
1396 gtk_table_attach (GTK_TABLE (table3), label13, 0, 1, 1, 2,
1397 (GtkAttachOptions) (GTK_FILL),
1398 (GtkAttachOptions) (0), 0, 0);
1399 gtk_misc_set_alignment (GTK_MISC (label13), 0, 0.5);
1401 opt_startnum_adj = gtk_adjustment_new (1, 0, 10000, 1, 10, 10);
1402 opt_startnum = gtk_spin_button_new (GTK_ADJUSTMENT (opt_startnum_adj), 1, 0);
1403 gtk_entry_set_activates_default(GTK_ENTRY(opt_startnum), TRUE);
1404 gtk_widget_show (opt_startnum);
1405 gtk_table_attach (GTK_TABLE (table3), opt_startnum, 1, 2, 0, 1,
1406 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1407 (GtkAttachOptions) (0), 0, 0);
1409 sort_order = gtk_combo_box_new();
1410 gtk_widget_show (sort_order);
1411 gtk_table_attach (GTK_TABLE (table3), sort_order, 1, 2, 1, 2,
1412 (GtkAttachOptions) (GTK_FILL),
1413 (GtkAttachOptions) (GTK_FILL), 0, 0);
1415 opt_removenum = gtk_check_button_new_with_mnemonic (_("Remove numbers"));
1416 gtk_widget_show (opt_removenum);
1417 gtk_box_pack_start (GTK_BOX (vbox4), opt_removenum, FALSE, FALSE, 0);
1419 opt_slotting = gtk_check_button_new_with_mnemonic (_("Automatic slotting"));
1420 gtk_widget_show (opt_slotting);
1421 gtk_box_pack_start (GTK_BOX (vbox4), opt_slotting, FALSE, FALSE, 0);
1423 /* Store pointers to all widgets, for use by lookup_widget(). */
1424 GLADE_HOOKUP_OBJECT (autonumber_text, scope_text, "scope_text");
1425 GLADE_HOOKUP_OBJECT (autonumber_text, scope_number, "scope_number");
1426 GLADE_HOOKUP_OBJECT (autonumber_text, scope_skip, "scope_skip");
1427 GLADE_HOOKUP_OBJECT (autonumber_text, scope_overwrite, "scope_overwrite");
1428 GLADE_HOOKUP_OBJECT (autonumber_text, opt_startnum, "opt_startnum");
1429 GLADE_HOOKUP_OBJECT (autonumber_text, sort_order, "sort_order");
1430 GLADE_HOOKUP_OBJECT (autonumber_text, opt_removenum, "opt_removenum");
1431 GLADE_HOOKUP_OBJECT (autonumber_text, opt_slotting, "opt_slotting");
1433 return autonumber_text;
1436 /*! \brief Create or restore the autonumber text dialog
1438 * If the function is called the first time the dialog is created.
1439 * If the dialog is only in background it is moved to the foreground.
1441 * @param w_current Pointer to the top level struct
1443 void autonumber_text_dialog(GSCHEM_TOPLEVEL *w_current)
1445 static AUTONUMBER_TEXT *autotext = NULL;
1447 GtkWidget *opt_removenum = NULL;
1448 GtkWidget *sort_order = NULL;
1450 if(autotext == NULL) {
1451 /* first call of this function, init dialog structure */
1452 autotext=autonumber_init_state();
1455 /* set the GSCHEM_TOPLEVEL always. Can it be changed between the calls??? */
1456 autotext->w_current = w_current;
1458 if(autotext->dialog == NULL) {
1459 /* Dialog is not currently displayed - create it */
1461 autotext->dialog = autonumber_create_dialog(w_current);
1463 opt_removenum = lookup_widget(autotext->dialog, "opt_removenum");
1464 sort_order = lookup_widget(autotext->dialog, "sort_order");
1466 autonumber_sortorder_create(w_current, sort_order);
1468 gtk_dialog_set_default_response (GTK_DIALOG (autotext->dialog),
1469 GTK_RESPONSE_ACCEPT);
1471 gtk_signal_connect(GTK_OBJECT(autotext->dialog), "response",
1472 GTK_SIGNAL_FUNC(autonumber_text_response),
1473 autotext);
1475 gtk_signal_connect(GTK_OBJECT(opt_removenum),
1476 "clicked",
1477 GTK_SIGNAL_FUNC(autonumber_removenum_toggled),
1478 autotext);
1480 autonumber_set_state(autotext);
1482 gtk_widget_show_all(autotext->dialog);
1485 /* if the dialog is in the background or minimized: show it */
1486 gtk_window_present(GTK_WINDOW(autotext->dialog));