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