1 /* gEDA - GPL Electronic Design Automation
2 * gattrib -- gEDA component and net attribute manipulation using spreadsheet.
3 * Copyright (C) 2003-2010 Stuart D. Brorson.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
20 /*------------------------------------------------------------------*/
22 * \brief Functions to manipulate the TABLE structure
24 * This file holds functions involved in manipulating the TABLE structure,
25 * which is subsidiary to SHEET_DATA. TABLE is a 2 dimensional array
26 * of structs; each struct corresponds to the data about an element
27 * in a single cell of the spreadsheet.
28 * \todo TABLE should also store its dimensions in its own data
29 * structure to save carrying the dimensions around separately.
40 /*------------------------------------------------------------------
41 * Gattrib specific includes
42 *------------------------------------------------------------------*/
43 #include <libgeda/libgeda.h> /* geda library functions */
44 #include "../include/struct.h" /* typdef and struct declarations */
45 #include "../include/prototype.h" /* function prototypes */
46 #include "../include/globals.h"
48 #ifdef HAVE_LIBDMALLOC
52 /* =================== Public Functions ====================== */
54 /*------------------------------------------------------------------*/
55 /*! \brief Create a new table
57 * This is the table creator. It returns a pointer to
58 * an initialized TABLE struct. As calling args, it needs
59 * the number of rows and cols to allocate. The table is a
60 * dynamically allocated 2D array of structs. To access data in
61 * a cell in the table, you reference (for example):
62 * ((sheet_data->comp_table)[i][j]).attrib_value
63 * (Parens used only for clarity. It works without parens.)
64 * \param rows Number of rows required in the new table
65 * \param cols Number of columns required in the new table
66 * \returns a pointer to an initialized TABLE struct.
68 TABLE
**s_table_new(int rows
, int cols
)
73 /* Here I am trying to create a 2 dimensional array of structs */
74 new_table
= (TABLE
**) g_malloc(rows
*sizeof(TABLE
*));
75 for (i
= 0; i
< rows
; i
++) {
76 new_table
[i
] = (TABLE
*) g_malloc(cols
* sizeof(TABLE
));
77 /* Note that I should put some checks in here to verify that
78 * malloc worked correctly. */
81 /* Now pre-load the table with NULLs */
82 for (i
= 0; i
< rows
; i
++) {
83 for (j
= 0; j
< cols
; j
++) {
84 (new_table
[i
][j
]).attrib_value
= NULL
;
85 (new_table
[i
][j
]).row_name
= NULL
;
86 (new_table
[i
][j
]).col_name
= NULL
;
87 (new_table
[i
][j
]).row
= i
;
88 (new_table
[i
][j
]).col
= j
;
89 (new_table
[i
][j
]).visibility
= VISIBLE
;
90 (new_table
[i
][j
]).show_name_value
= SHOW_VALUE
;
99 /*------------------------------------------------------------------*/
100 /*! \brief Resize a TABLE
102 * This function recreates the table with
103 * a new size. It can only increase
104 * the number of cols. You can't increase the number of rows since
105 * gattrib doesn't allow you to input new components. Decreasing the
106 * number of cols is also TBD.
107 * \param table Table to resize
108 * \param rows Number of rows in the table
109 * \param old_cols Number of columns previously in the table
110 * \param new_cols Number of columns required in the table
111 * \returns a pointer to the resized table
112 * \todo The row and column information could be stored in the
115 TABLE
**s_table_resize(TABLE
**table
,
116 int rows
, int old_cols
, int new_cols
)
120 /* Here I am trying to resize the 2 dimensional array of structs */
121 for (i
= 0; i
< rows
; i
++) {
122 table
[i
] = (TABLE
*) realloc(table
[i
], new_cols
*sizeof(TABLE
) );
123 if (table
[i
] == NULL
) exit(-1); /* die if failed to realloc new memory */
126 /* Now pre-load new cols with NULLs */
127 for (i
= 0; i
< rows
; i
++) {
128 for (j
= old_cols
; j
< new_cols
; j
++) {
129 (table
[i
][j
]).attrib_value
= NULL
;
130 (table
[i
][j
]).row_name
= NULL
;
131 (table
[i
][j
]).col_name
= NULL
;
132 (table
[i
][j
]).row
= i
;
133 (table
[i
][j
]).col
= j
;
134 (table
[i
][j
]).visibility
= VISIBLE
;
135 (table
[i
][j
]).show_name_value
= SHOW_VALUE
;
143 /*------------------------------------------------------------------*/
144 /*! \brief Destroy a table
146 * This function destroys the old table.
147 * Use it after reading in a new
148 * page to get rid of the old table before building a new one.
149 * \param table Table to destroy
150 * \param row_count Number of rows in table
151 * \param col_count Number of columns in table
153 void s_table_destroy(TABLE
**table
, int row_count
, int col_count
)
160 for (i
= 0; i
< row_count
; i
++) {
161 for (j
= 0; j
< col_count
; j
++) {
162 g_free( (table
[i
][j
]).attrib_value
);
163 g_free( (table
[i
][j
]).row_name
);
164 g_free( (table
[i
][j
]).col_name
);
168 for (i
= 0; i
< row_count
; i
++) {
180 /*------------------------------------------------------------------*/
181 /*! \brief Get a string index number
183 * This function returns the index number
184 * when given a STRING_LIST and a
185 * string to match. It finds the index
186 * number by iterating through the master list.
188 * \param local_string
189 * \returns the index of the string
191 int s_table_get_index(STRING_LIST
*local_list
, char *local_string
) {
193 STRING_LIST
*list_element
;
196 printf("In s_table_get_index, examining %s to see if it is in the list.\n", local_string
);
200 list_element
= local_list
;
201 while (list_element
!= NULL
) {
202 if (strcmp(list_element
->data
, local_string
) == 0) {
206 list_element
= list_element
->next
;
208 return(-1); /* return code when string is not in master_list */
213 /*------------------------------------------------------------------*/
214 /*! \brief Create attribute pair
216 * This function takes a table, a row list, and a row name,
217 * and returns a list holding
218 * name=value pairs for all attribs pertainent to that particular
220 * If the row holds no attribs, it just returns NULL.
222 * \param row_name Name of the row to search for
223 * \param table Table to be searched
224 * \param row_list list of rows
226 * \returns STRING_LIST of name=value pairs
228 STRING_LIST
*s_table_create_attrib_pair(gchar
*row_name
,
230 STRING_LIST
*row_list
,
233 STRING_LIST
*attrib_pair_list
;
234 char *attrib_name
, *attrib_value
, *name_value_pair
;
238 attrib_pair_list
= s_string_list_new();
240 row
= s_table_get_index(row_list
, row_name
);
243 /* we didn't find the item in the list */
245 "In s_table_create_attrib_pair, we didn't find the row name in the row list!\n");
246 return attrib_pair_list
;
249 for (col
= 0; col
< num_attribs
; col
++) {
250 /* pull attrib from table. If non-null, add it to attrib_pair_list */
251 if ( (table
[row
][col
]).attrib_value
!= NULL
) {
252 attrib_name
= (table
[row
][col
]).col_name
;
253 attrib_value
= (table
[row
][col
]).attrib_value
;
254 name_value_pair
= g_strconcat(attrib_name
, "=", attrib_value
, NULL
);
255 s_string_list_add_item(attrib_pair_list
, &count
, name_value_pair
);
256 g_free(name_value_pair
);
260 return attrib_pair_list
;
266 /*------------------------------------------------------------------*/
267 /*! \brief Add components to the component table
269 * This fcn iterates over adds all
270 * objects found on this page looking
271 * for components. When it finds a component, it finds all component
272 * attribs and sticks them in the TABLE.
273 * \param obj_list pointer to GList containing objects on this page
275 void s_table_add_toplevel_comp_items_to_comp_table (const GList
*obj_list
) {
284 gint old_visibility
, old_show_name_value
;
288 printf("- Starting internal component TABLE creation\n");
294 printf("=========== Just entered s_table_add_toplevel_comp_items_to_comp_table! ==============\n");
297 /* ----- Iterate through all objects found on page ----- */
298 for (o_iter
= obj_list
; o_iter
!= NULL
; o_iter
= g_list_next (o_iter
)) {
299 OBJECT
*o_current
= o_iter
->data
;
302 printf(" ---> In s_table_add_toplevel_comp_items_to_comp_table, examining o_current->name = %s\n", o_current
->name
);
305 /* ----- Now process objects found on page ----- */
306 if (o_current
->type
== OBJ_COMPLEX
&&
307 o_current
->attribs
!= NULL
) {
309 /* ---- Don't process part if it lacks a refdes ----- */
310 temp_uref
= g_strdup(s_attrib_get_refdes(o_current
));
314 printf(" In s_table_add_toplevel_comp_items_to_comp_table, found component on page. Refdes = %s\n", temp_uref
);
318 /* Having found a component, we loop over all attribs in this
319 * component, and stick them
320 * into cells in the table. */
321 a_iter
= o_current
->attribs
;
322 while (a_iter
!= NULL
) {
323 a_current
= a_iter
->data
;
324 if (a_current
->type
== OBJ_TEXT
325 && a_current
->text
!= NULL
) { /* found an attribute */
326 /* may need to check more thoroughly here. . . . */
327 attrib_text
= g_strdup(a_current
->text
->string
);
328 attrib_name
= u_basic_breakup_string(attrib_text
, '=', 0);
329 attrib_value
= s_misc_remaining_string(attrib_text
, '=', 1);
330 old_visibility
= a_current
->visibility
;
331 old_show_name_value
= a_current
->show_name_value
;
333 /* Don't include "refdes" or "slot" because they form the row name. */
334 /* Also don't include "net" per bug found by Steve W. 4.3.2007 -- SDB */
335 if ( (strcmp(attrib_name
, "refdes") != 0) &&
336 (strcmp(attrib_name
, "net") != 0) &&
337 (strcmp(attrib_name
, "slot") != 0) ) {
339 /* Get row and col where to put this attrib */
340 row
= s_table_get_index(sheet_head
->master_comp_list_head
, temp_uref
);
341 col
= s_table_get_index(sheet_head
->master_comp_attrib_list_head
, attrib_name
);
343 if (row
== -1 || col
== -1) {
344 /* we didn't find the item in the table */
346 "In s_table_add_toplevel_comp_items_to_comp_table, we didn't find either row or col in the lists!\n");
350 printf(" In s_table_add_toplevel_comp_items_to_comp_table, about to add row %d, col %d, attrib_value = %s\n",
351 row
, col
, attrib_value
);
352 printf(" . . . current address of attrib_value cell is [%p]\n", &((sheet_head
->component_table
)[row
][col
]).attrib_value
);
354 /* Is there a compelling reason for me to put this into a separate fcn? */
355 ((sheet_head
->component_table
)[row
][col
]).row
= row
;
356 ((sheet_head
->component_table
)[row
][col
]).col
= col
;
357 ((sheet_head
->component_table
)[row
][col
]).row_name
= g_strdup(temp_uref
);
358 ((sheet_head
->component_table
)[row
][col
]).col_name
= g_strdup(attrib_name
);
359 ((sheet_head
->component_table
)[row
][col
]).attrib_value
= g_strdup(attrib_value
);
360 ((sheet_head
->component_table
)[row
][col
]).visibility
= old_visibility
;
361 ((sheet_head
->component_table
)[row
][col
]).show_name_value
= old_show_name_value
;
366 g_free(attrib_value
);
368 a_iter
= g_list_next (a_iter
);
370 } /* while (a_current != NULL) */
372 } /* if (temp_uref) */
373 } /* if (o_current->type == OBJ_COMPLEX) */
381 /*------------------------------------------------------------------*/
382 /*! \brief Add nets to net table
384 * This function iterates over adds all
385 * items found on this page looking
386 * for nets and adds them individually to the net table. Looping over
387 * objects occurs here.
389 * \param start_obj Pointer to first object
391 * \todo Why do the calling semantics of this function disagree with
392 * s_table_add_toplevel_pin_items_to_pin_table()? That function
393 * takes a GList, this one takes a pointer to OBJECT.
395 void s_table_add_toplevel_net_items_to_net_table(OBJECT
*start_obj
) {
404 /* ----- Iterate through all objects found on page ----- */
405 o_current
= start_obj
;
406 while (o_current
!= NULL
) {
408 /* ----- Now process objects found on page ----- */
409 if (o_current
->type
== OBJ_NET
) {
413 printf("In s_table_add_toplevel_net_items_to_net_table, Found net on page\n");
417 /* Having found a net, we stick it into the table. */
418 a_current
= o_current
->attribs
;
419 while (a_current
!= NULL
) {
420 if (a_current
->object
->type
== OBJ_TEXT
421 && a_current
->object
->text
!= NULL
) { /* found an attribute */
422 /* may need to check more thoroughly here. . . . */
423 attrib_text
= g_strdup(a_current
->object
->text
->string
);
424 attrib_name
= u_basic_breakup_string(attrib_text
, '=', 0);
425 attrib_value
= s_misc_remaining_string(attrib_text
, '=', 1);
426 if (strcmp(attrib_name
, "netname") != 0) {
427 /* Don't include "netname" */
429 /* Get row and col where to put this attrib */
430 row
= s_table_get_index(sheet_head
->master_net_list_head
, temp_netname
);
431 col
= s_table_get_index(sheet_head
->master_net_attrib_list_head
, attrib_name
);
435 printf("In s_table_add_toplevel_net_items_to_net_table, about to add row %d, col %d, attrib_value = %s\n",
436 row
, col
, attrib_value
);
437 printf(" . . . current address of attrib_value cell is [%p]\n", &((sheet_head
->net_table
)[row
][col
]).attrib_value
);
439 /* Is there a compelling reason for me to put this into a separate fcn? */
440 ((sheet_head
->net_table
)[row
][col
]).row
= row
;
441 ((sheet_head
->net_table
)[row
][col
]).col
= col
;
442 ((sheet_head
->net_table
)[row
][col
]).row_name
= g_strdup(temp_netname
);
443 ((sheet_head
->net_table
)[row
][col
]).col_name
= g_strdup(attrib_name
);
444 ((sheet_head
->net_table
)[row
][col
]).attrib_value
= g_strdup(attrib_value
);
448 g_free(attrib_value
);
450 a_current
= a_current
->next
;
452 } /* while (a_current != NULL) */
453 g_free(temp_netname
);
455 } /*--- if (o_current->type == OBJ_NET) ---*/
458 o_current
= o_current
->next
; /* iterate to next object on page */
459 } /* while o_current != NULL */
466 printf("In s_table_add_toplevel_net_items_to_net_table -- we are about to return\n");
473 /*------------------------------------------------------------------*/
474 /*! \brief Add pins to pin table.
476 * This function iterates over adds all items found on this page
477 * looking for pins. WHen it finds a pin, it gathers all
478 * pin attribs and sticks them into the pin table.
479 * \param obj_list List of objects on page
481 void s_table_add_toplevel_pin_items_to_pin_table (const GList
*obj_list
) {
495 printf("- Starting internal pin TABLE creation\n");
499 printf("=========== Just entered s_table_add_toplevel_pin_items_to_pin_table! ==============\n");
502 /* ----- Iterate through all objects found on page ----- */
503 for (o_iter
= obj_list
; o_iter
!= NULL
; o_iter
= g_list_next (o_iter
)) {
504 OBJECT
*o_current
= o_iter
->data
;
507 printf(" ---> In s_table_add_toplevel_pin_items_to_pin_table, examining o_current->name = %s\n", o_current
->name
);
510 /* ----- Now process objects found on page ----- */
511 if (o_current
->type
== OBJ_COMPLEX
&&
512 o_current
->attribs
!= NULL
) {
514 /* ---- Don't process part if it lacks a refdes ----- */
515 temp_uref
= s_attrib_get_refdes(o_current
);
518 /* ----- Now iterate through lower level objects looking for pins. ----- */
519 for (o_lower_iter
= o_current
->complex->prim_objs
;
520 o_lower_iter
!= NULL
;
521 o_lower_iter
= g_list_next (o_lower_iter
)) {
522 OBJECT
*o_lower_current
= o_lower_iter
->data
;
524 if (o_lower_current
->type
== OBJ_PIN
) {
525 /* ----- Found a pin. First get its pinnumber. then get attrib head and loop on attribs. ----- */
526 pinnumber
= o_attrib_search_object_attribs_by_name (o_lower_current
, "pinnumber", 0);
527 row_label
= g_strconcat(temp_uref
, ":", pinnumber
, NULL
);
530 printf(" In s_table_add_toplevel_pin_items_to_pin_table, examining pin %s\n", row_label
);
533 a_iter
= o_lower_current
->attribs
;
534 while (a_iter
!= NULL
) {
535 pin_attrib
= a_iter
->data
;
536 if (pin_attrib
->type
== OBJ_TEXT
537 && pin_attrib
->text
!= NULL
) { /* found an attribute */
538 attrib_text
= g_strdup(pin_attrib
->text
->string
);
539 attrib_name
= u_basic_breakup_string(attrib_text
, '=', 0);
540 attrib_value
= s_misc_remaining_string(attrib_text
, '=', 1);
542 if ( (strcmp(attrib_name
, "pinnumber") != 0)
543 && (attrib_value
!= 0) ) {
544 /* Don't include "pinnumber" because it is already in other master list.
545 * Also must ensure that value is non-null; certain symbols are not well formed.
548 /* Get row and col where to put this attrib */
549 row
= s_table_get_index(sheet_head
->master_pin_list_head
, row_label
);
550 col
= s_table_get_index(sheet_head
->master_pin_attrib_list_head
, attrib_name
);
552 if (row
== -1 || col
== -1) {
553 /* we didn't find the item in the table */
555 "In s_table_add_toplevel_pin_items_to_pin_table, we didn't find either row or col in the lists!\n");
559 printf(" In s_table_add_toplevel_pin_items_to_pin_table, about to add row %d, col %d, attrib_value = %s\n",
560 row
, col
, attrib_value
);
561 printf(" . . . current address of attrib_value cell is [%p]\n", &((sheet_head
->component_table
)[row
][col
]).attrib_value
);
563 /* Is there a compelling reason for me to put this into a separate fcn? */
564 ((sheet_head
->pin_table
)[row
][col
]).row
= row
;
565 ((sheet_head
->pin_table
)[row
][col
]).col
= col
;
566 ((sheet_head
->pin_table
)[row
][col
]).row_name
= g_strdup(row_label
);
567 ((sheet_head
->pin_table
)[row
][col
]).col_name
= g_strdup(attrib_name
);
568 ((sheet_head
->pin_table
)[row
][col
]).attrib_value
= g_strdup(attrib_value
);
573 g_free(attrib_value
);
575 a_iter
= g_list_next (a_iter
);
577 } /* while (pin_attrib != NULL) */
594 /*------------------------------------------------------------------*/
595 /*! \brief Push spreadsheet data to TABLEs.
597 * This function traverses the spreadsheet,
598 * extracts the attribs from
599 * the cells, and places them back into TABLE. This is the
600 * first step in saving out a project.
602 void s_table_gtksheet_to_all_tables() {
606 STRING_LIST
*master_row_list
;
607 STRING_LIST
*master_col_list
;
609 GtkSheet
*local_gtk_sheet
;
611 /* First handle component sheet */
612 num_rows
= sheet_head
->comp_count
;
613 num_cols
= sheet_head
->comp_attrib_count
;
614 local_gtk_sheet
= sheets
[0];
615 master_row_list
= sheet_head
->master_comp_list_head
;
616 master_col_list
= sheet_head
->master_comp_attrib_list_head
;
618 local_table
= sheet_head
->component_table
;
620 /* now fill out new table */
622 printf("In s_table_gtksheet_to_all_tables, now about to fill out new component table.\n");
624 s_table_gtksheet_to_table(local_gtk_sheet
, master_row_list
,
625 master_col_list
, local_table
,
629 /* Next handle net sheet */
630 num_rows
= sheet_head
->net_count
;
631 num_cols
= sheet_head
->net_attrib_count
;
632 local_gtk_sheet
= sheets
[1];
633 master_row_list
= sheet_head
->master_net_list_head
;
634 master_col_list
= sheet_head
->master_net_attrib_list_head
;
635 local_table
= sheet_head
->net_table
;
637 s_table_gtksheet_to_table(local_gtk_sheet
, master_row_list
,
638 master_col_list
, local_table
,
642 #ifdef UNIMPLEMENTED_FEATURES
643 /* Finally, handle component pin sheet */
644 num_rows
= sheet_head
->pin_count
;
645 num_cols
= sheet_head
->pin_attrib_count
;
646 local_gtk_sheet
= sheets
[2];
647 master_row_list
= sheet_head
->master_pin_list_head
;
648 master_col_list
= sheet_head
->master_pin_attrib_list_head
;
649 /* local_table = s_table_new(num_rows, num_cols); */
650 local_table
= sheet_head
->pin_table
;
652 s_table_gtksheet_to_table(local_gtk_sheet
, master_row_list
,
653 master_col_list
, local_table
,
661 /* =================== Private Functions ====================== */
662 /*------------------------------------------------------------------*/
663 /*! \brief Extract attributes from gtksheet into TABLE
665 * This function does the actual heavy lifting of looping
666 * through the spreadsheet, extracting the attribs from
667 * the cells, and placing them back into TABLE. This is the
668 * first step in saving out a project.
670 * \param local_gtk_sheet GtkSheet to save
671 * \param master_row_list STRING_LIST of rows
672 * \param master_col_list STRING_LIST of columns
673 * \param local_table TABLE structure to fill
674 * \param num_rows Number of rows in table
675 * \param num_cols Number of columns in table
677 void s_table_gtksheet_to_table(GtkSheet
*local_gtk_sheet
, STRING_LIST
*master_row_list
,
678 STRING_LIST
*master_col_list
, TABLE
**local_table
,
679 int num_rows
, int num_cols
)
683 STRING_LIST
*row_list_item
;
686 STRING_LIST
*col_list_item
;
692 printf("********** Entering s_table_gtksheet_to_table ******************\n");
696 row_list_item
= master_row_list
;
697 for (row
= 0; row
< num_rows
; row
++) {
698 row_title
= (gchar
*) g_strdup(row_list_item
->data
);
700 col_list_item
= master_col_list
;
701 for (col
= 0; col
< num_cols
; col
++) {
702 col_title
= (gchar
*) g_strdup(col_list_item
->data
);
704 /* get value of attrib in cell */
705 attrib_value
= (gchar
*) gtk_sheet_cell_get_text(GTK_SHEET(local_gtk_sheet
), row
, col
);
708 if (strlen(attrib_value
) == 0) {
709 /* g_free(attrib_value); */ /* sometimes we have spurious, zero length strings creep */
710 attrib_value
= NULL
; /* into the GtkSheet */
716 printf("In s_table_gtksheet_to_table, found attrib_value = %s in cell row=%d, col=%d\n",
717 attrib_value
, row
, col
);
720 /* first handle attrib value in cell */
722 printf(" Updating attrib_value %s\n", attrib_value
);
724 g_free( local_table
[row
][col
].attrib_value
);
725 if (attrib_value
!= NULL
) {
726 local_table
[row
][col
].attrib_value
= (gchar
*) g_strdup(attrib_value
);
728 local_table
[row
][col
].attrib_value
= NULL
;
731 /* next handle name of row (also held in TABLE cell) */
733 printf(" Updating row_name %s\n", row_title
);
735 g_free( local_table
[row
][col
].row_name
);
736 if (row_title
!= NULL
) {
737 local_table
[row
][col
].row_name
= (gchar
*) g_strdup(row_title
);
739 local_table
[row
][col
].row_name
= NULL
;
742 /* finally handle name of col */
744 printf(" Updating col_name %s\n", col_title
);
746 g_free( local_table
[row
][col
].col_name
);
747 if (col_title
!= NULL
) {
748 local_table
[row
][col
].col_name
= (gchar
*) g_strdup(col_title
);
750 local_table
[row
][col
].col_name
= NULL
;
753 /* get next col list item and then iterate. */
754 col_list_item
= col_list_item
->next
;
757 row_list_item
= row_list_item
->next
;