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