pcb 4.1.1
An interactive printed circuit board layout editor.

gui-netlist-window.c

Go to the documentation of this file.
00001 /*
00002  *                            COPYRIGHT
00003  *
00004  *  PCB, interactive printed circuit board design
00005  *  Copyright (C) 1994,1995,1996,1997,1998 Thomas Nau
00006  *
00007  *  This program is free software; you can redistribute it and/or modify
00008  *  it under the terms of the GNU General Public License as published by
00009  *  the Free Software Foundation; either version 2 of the License, or
00010  *  (at your option) any later version.
00011  *
00012  *  This program is distributed in the hope that it will be useful,
00013  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *  GNU General Public License for more details.
00016  *
00017  *  You should have received a copy of the GNU General Public License along
00018  *  with this program; if not, write to the Free Software Foundation, Inc.,
00019  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
00020  *
00021  *  Contact addresses for paper mail and Email:
00022  *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
00023  *  Thomas.Nau@rz.uni-ulm.de
00024  *
00025  */
00026 
00027 /* 
00028  * This file written by Bill Wilson for the PCB Gtk port
00029  */
00030 
00031 #ifdef HAVE_CONFIG_H
00032 #include "config.h"
00033 #endif
00034 
00035 #include <stdio.h>
00036 #include <stdlib.h>
00037 #ifdef HAVE_STRING_H
00038 #include <string.h>
00039 #endif
00040 
00041 #include "global.h"
00042 
00043 #include "create.h"
00044 #include "data.h"
00045 #include "draw.h"
00046 #include "error.h"
00047 #include "find.h"
00048 #include "misc.h"
00049 #include "mymem.h"
00050 #include "rats.h"
00051 #include "remove.h"
00052 #include "search.h"
00053 #include "select.h"
00054 #include "set.h"
00055 #include "undo.h"
00056 
00057 #include "gui.h"
00058 
00059 #ifdef HAVE_LIBDMALLOC
00060 #include <dmalloc.h>
00061 #endif
00062 
00063 #define NET_HIERARCHY_SEPARATOR "/"
00064 
00065 static GtkWidget        *netlist_window;
00066 static GtkWidget        *disable_all_button;
00067 
00068 static GtkTreeModel *node_model;
00069 static GtkTreeView *node_treeview;
00070 
00071 static gboolean selection_holdoff;
00072 
00073 static LibraryMenuType *selected_net;
00074 static LibraryMenuType *node_selected_net;
00075 
00076 
00077 /* The Netlist window displays all the layout nets in a left treeview
00078    |  When one of the nets is selected, all of its nodes (or connections)
00079    |  will be displayed in a right treeview.  If a "Select on layout" button
00080    |  is pressed, the net that is selected in the left treeview will be
00081    |  drawn selected on the layout.
00082    |
00083    |  Gtk separates the data model from the view in its treeview widgets so
00084    |  here we maintain two data models.  The net data model has pointers to
00085    |  all the nets in the layout and the node data model keeps pointers to all
00086    |  the nodes for the currently selected net.  By updating the data models
00087    |  the net and node gtk treeviews handle displaying the results.
00088    |
00089    |  The netlist window code has a public interface providing hooks so PCB
00090    |  code can control the net and node treeviews:
00091    |
00092    |    ghid_get_net_from_node_name gchar *node_name, gboolean enabled_only)
00093    |            Given a node name (eg C101-1), walk through the nets in the net
00094    |            data model and search each net for the given node_name.  If found
00095    |            and enabled_only is true, make the net treeview scroll to and
00096    |            highlight (select) the found net.  Return the found net.
00097    |
00098    |    ghid_netlist_highlight_node()
00099    |            Given some PCB internal pointers (not really a good gui api here)
00100    |            look up a node name determined by the pointers and highlight the node
00101    |            in the node treeview.  By using ghid_get_net_from_node_name() to
00102    |            look up the node, the net the node belongs to will also be
00103    |            highlighted in the net treeview.
00104    |
00105    |    ghid_netlist_window_update(gboolean init_nodes)
00106    |            PCB calls this to tell the gui netlist code the layout net has
00107    |            changed and the gui data structures (net and optionally node data
00108    |            models) should be rebuilt.
00109 */
00110 
00111 
00112 /* -------- The netlist nodes (LibraryEntryType) data model ----------
00113    |  Each time a net is selected in the left treeview, this node model
00114    |  is recreated containing all the nodes (pins/pads) that are connected
00115    |  to the net.  Loading the new model will update the right treeview with
00116    |  all the new node names (C100-1, R100-1, etc).
00117    |
00118    |  The terminology is a bit confusing because the PCB netlist data
00119    |  structures are generic structures used for library elements, netlist
00120    |  data, and possibly other things also.  The mapping is that
00121    |  the layout netlist data structure is a LibraryType which
00122    |  contains an allocated array of LibraryMenuType structs.  Each of these
00123    |  structs represents a net in the netlist and contains an array
00124    |  of LibraryEntryType structs which represent the nodes connecting to
00125    |  the net.  So we have: 
00126    |
00127    |                      Nets              Nodes
00128    |       LibraryType    LibraryMenuType   LibraryEntryType
00129    | -------------------------------------------------------
00130    |  PCB->NetlistLib------Menu[0]-----------Entry[0]
00131    |                     |                   Entry[1]
00132    |                     |                     ...
00133    |                     |
00134    |                     --Menu[1]-----------Entry[0]
00135    |                     |                   Entry[1]
00136    |                     |                     ...
00137    |                     |
00138    |                     -- ...
00139    |
00140    | Where for example Menu[] names would be nets GND, Vcc, etc and Entry[]
00141    | names would be nodes C101-1, R101-2, etc
00142 */
00143 
00144 LibraryEntryType *node_get_node_from_name (gchar * node_name,
00145                                            LibraryMenuType ** node_net);
00146 
00147 enum
00148 {
00149   NODE_NAME_COLUMN,             /* Name to show in the treeview         */
00150   NODE_LIBRARY_COLUMN,          /* Pointer to this node (LibraryEntryType)      */
00151   N_NODE_COLUMNS
00152 };
00153 
00154 /* Given a net in the netlist (a LibraryMenuType) put all the Entry[]
00155    |  names (the nodes) into a newly created node tree model.
00156 */
00157 static GtkTreeModel *
00158 node_model_create (LibraryMenuType * menu)
00159 {
00160   GtkListStore *store;
00161   GtkTreeIter iter;
00162 
00163   store = gtk_list_store_new (N_NODE_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER);
00164 
00165   if (menu == NULL)
00166     return GTK_TREE_MODEL (store);
00167 
00168   ENTRY_LOOP (menu);
00169   {
00170     if (!entry->ListEntry)
00171       continue;
00172     gtk_list_store_append (store, &iter);
00173     gtk_list_store_set (store, &iter,
00174                         NODE_NAME_COLUMN, entry->ListEntry,
00175                         NODE_LIBRARY_COLUMN, entry, -1);
00176   }
00177   END_LOOP;
00178 
00179   return GTK_TREE_MODEL (store);
00180 }
00181 
00182 /* When there's a new node to display in the node treeview, call this.
00183    |  Create a new model containing the nodes of the given net, insert
00184    |  the model into the treeview and unref the old model.
00185 */
00186 static void
00187 node_model_update (LibraryMenuType * menu)
00188 {
00189   GtkTreeModel *model;
00190 
00191   model = node_model;
00192   node_model = node_model_create (menu);
00193   gtk_tree_view_set_model (node_treeview, node_model);
00194 
00195   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (node_model),
00196                                         NODE_NAME_COLUMN, GTK_SORT_ASCENDING);
00197 
00198   /* We could be using gtk_list_store_clear() on the same model, but it's
00199      |  just as easy that we've created a new one and here unref the old one.
00200    */
00201   if (model)
00202     g_object_unref (G_OBJECT (model));
00203 }
00204 
00205 static void
00206 toggle_pin_selected (LibraryEntryType *entry)
00207 {
00208   ConnectionType conn;
00209 
00210   if (!SeekPad (entry, &conn, false))
00211     return;
00212 
00213   AddObjectToFlagUndoList (conn.type, conn.ptr1, conn.ptr2, conn.ptr2);
00214   TOGGLE_FLAG (SELECTEDFLAG, (AnyObjectType *)conn.ptr2);
00215   DrawObject (conn.type, conn.ptr1, conn.ptr2);
00216 }
00217 
00218 
00219 /* Callback when the user clicks on a PCB node in the right node treeview.
00220  */
00221 static void
00222 node_selection_changed_cb (GtkTreeSelection * selection, gpointer data)
00223 {
00224   GtkTreeIter iter;
00225   GtkTreeModel *model;
00226   LibraryMenuType *node_net;
00227   LibraryEntryType *node;
00228   ConnectionType conn;
00229   Coord x, y;
00230   static gchar *node_name;
00231 
00232   if (selection_holdoff)        /* PCB is highlighting, user is not selecting */
00233     return;
00234 
00235   /* Toggle off the previous selection.  Look up node_name to make sure
00236   |  it still exists.  This toggling can get out of sync if a node is
00237   |  toggled selected, then the net that includes the node is selected
00238   |  then unselected.
00239   */
00240   if ((node = node_get_node_from_name (node_name, &node_net)) != NULL)
00241     {
00242       /* If net node belongs to has been highlighted/unhighighed, toggling
00243       |  if off here will get our on/off toggling out of sync.
00244       */
00245       if (node_net == node_selected_net)
00246         {
00247           toggle_pin_selected (node);
00248           ghid_cancel_lead_user ();
00249         }
00250       g_free (node_name);
00251       node_name = NULL;
00252     }
00253 
00254   /* Get the selected treeview row.
00255    */
00256   if (!gtk_tree_selection_get_selected (selection, &model, &iter))
00257     {
00258       if (node)
00259         ghid_invalidate_all ();
00260       return;
00261     }
00262 
00263   /* From the treeview row, extract the node pointer stored there and
00264   |  we've got a pointer to the LibraryEntryType (node) the row
00265   |  represents.
00266   */
00267   gtk_tree_model_get (model, &iter, NODE_LIBRARY_COLUMN, &node, -1);
00268 
00269   dup_string (&node_name, node->ListEntry);
00270   node_selected_net = selected_net;
00271 
00272   /* Now just toggle a select of the node on the layout
00273    */
00274   toggle_pin_selected (node);
00275   IncrementUndoSerialNumber ();
00276 
00277   /* And lead the user to the location */
00278   if (SeekPad (node, &conn, false))
00279     switch (conn.type) {
00280       case PIN_TYPE:
00281         {
00282           PinType *pin = (PinType *) conn.ptr2;
00283           x = pin->X;
00284           y = pin->Y;
00285           gui->set_crosshair (x, y, 0);
00286           ghid_lead_user_to_location (x, y);
00287           break;
00288         }
00289       case PAD_TYPE:
00290         {
00291           PadType *pad = (PadType *) conn.ptr2;
00292           x = pad->Point1.X + (pad->Point2.X - pad->Point1.X) / 2;
00293           y = pad->Point1.Y + (pad->Point2.Y - pad->Point1.Y) / 2;
00294           gui->set_crosshair (x, y, 0);
00295           ghid_lead_user_to_location (x, y);
00296           break;
00297         }
00298     }
00299 }
00300 
00301 
00302 /* -------- The net (LibraryMenuType) data model ----------
00303  */
00304 /* TODO: the enable and disable all nets.  Can't seem to get how that's
00305    |  supposed to work, but it'll take updating the NET_ENABLED_COLUMN in
00306    |  the net_model.  Probably it should be made into a gpointer and make
00307    |  a text renderer for it and just write a '*' or a ' ' similar to the
00308    |  the Xt PCB scheme.  Or better, since it's an "all nets" function, just
00309    |  have a "Disable all nets" toggle button and don't mess with the
00310    |  model/treeview at all.
00311 */
00312 enum
00313 {
00314   NET_ENABLED_COLUMN,           /* If enabled will be ' ', if disable '*'       */
00315   NET_NAME_COLUMN,              /* Name to show in the treeview */
00316   NET_LIBRARY_COLUMN,           /* Pointer to this net (LibraryMenuType)        */
00317   N_NET_COLUMNS
00318 };
00319 
00320 static GtkTreeModel *net_model = NULL;
00321 static GtkTreeView *net_treeview;
00322 
00323 static gboolean         loading_new_netlist;
00324 
00325 static GtkTreeModel *
00326 net_model_create (void)
00327 {
00328   GtkTreeModel *model;
00329   GtkTreeStore *store;
00330   GtkTreeIter new_iter;
00331   GtkTreeIter parent_iter;
00332   GtkTreeIter *parent_ptr;
00333   GtkTreePath *path;
00334   GtkTreeRowReference *row_ref;
00335   GHashTable *prefix_hash;
00336   char *display_name;
00337   char *hash_string;
00338   char **join_array;
00339   char **path_segments;
00340   int path_depth;
00341   int try_depth;
00342 
00343   store = gtk_tree_store_new (N_NET_COLUMNS,
00344                               G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER);
00345 
00346   model = GTK_TREE_MODEL (store);
00347 
00348   /* Hash table stores GtkTreeRowReference for given path prefixes */
00349   prefix_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
00350                                        (GDestroyNotify)
00351                                          gtk_tree_row_reference_free);
00352 
00353   MENU_LOOP (&PCB->NetlistLib);
00354   {
00355     if (!menu->Name)
00356       continue;
00357 
00358     if (loading_new_netlist)
00359       menu->flag = TRUE;
00360 
00361     parent_ptr = NULL;
00362 
00363     path_segments = g_strsplit (menu->Name, NET_HIERARCHY_SEPARATOR, 0);
00364     path_depth = g_strv_length (path_segments);
00365 
00366     for (try_depth = path_depth - 1; try_depth > 0; try_depth--)
00367       {
00368         join_array = g_new0 (char *, try_depth + 1);
00369         memcpy (join_array, path_segments, sizeof (char *) * try_depth);
00370 
00371         /* See if this net's parent node is in the hash table */
00372         hash_string = g_strjoinv (NET_HIERARCHY_SEPARATOR, join_array);
00373         g_free (join_array);
00374 
00375         row_ref = (GtkTreeRowReference *)g_hash_table_lookup (prefix_hash, hash_string);
00376         g_free (hash_string);
00377 
00378         /* If we didn't find the path at this level, keep looping */
00379         if (row_ref == NULL)
00380           continue;
00381 
00382         path = gtk_tree_row_reference_get_path (row_ref);
00383         gtk_tree_model_get_iter (model, &parent_iter, path);
00384         parent_ptr = &parent_iter;
00385         break;
00386       }
00387 
00388     /* NB: parent_ptr may still be NULL if we reached the toplevel */
00389 
00390     /* Now walk up the desired path, adding the nodes */
00391 
00392     for (; try_depth < path_depth - 1; try_depth++)
00393       {
00394         display_name = g_strconcat (path_segments[try_depth],
00395                                     NET_HIERARCHY_SEPARATOR, NULL);
00396         gtk_tree_store_append (store, &new_iter, parent_ptr);
00397         gtk_tree_store_set (store, &new_iter,
00398                             NET_ENABLED_COLUMN, "",
00399                             NET_NAME_COLUMN, display_name,
00400                             NET_LIBRARY_COLUMN, NULL, -1);
00401         g_free (display_name);
00402 
00403         path = gtk_tree_model_get_path (model, &new_iter);
00404         row_ref = gtk_tree_row_reference_new (model, path);
00405         parent_iter = new_iter;
00406         parent_ptr = &parent_iter;
00407 
00408         join_array = g_new0 (char *, try_depth + 2);
00409         memcpy (join_array, path_segments, sizeof (char *) * (try_depth + 1));
00410 
00411         hash_string = g_strjoinv (NET_HIERARCHY_SEPARATOR, join_array);
00412         g_free (join_array);
00413 
00414         /* Insert those node in the hash table */
00415         g_hash_table_insert (prefix_hash, hash_string, row_ref);
00416         /* Don't free hash_string, it is now oened by the hash table */
00417       }
00418 
00419     gtk_tree_store_append (store, &new_iter, parent_ptr);
00420     gtk_tree_store_set (store, &new_iter,
00421                         NET_ENABLED_COLUMN, menu->flag ? "" : "*",
00422                         NET_NAME_COLUMN, path_segments[path_depth - 1],
00423                         NET_LIBRARY_COLUMN, menu, -1);
00424     g_strfreev (path_segments);
00425   }
00426   END_LOOP;
00427 
00428   g_hash_table_destroy (prefix_hash);
00429 
00430   return model;
00431 }
00432 
00433 
00434 /* Called when the user double clicks on a net in the left treeview.
00435  */
00436 static void
00437 net_selection_double_click_cb (GtkTreeView * treeview, GtkTreePath * path,
00438                                GtkTreeViewColumn * col, gpointer data)
00439 {
00440   GtkTreeModel *model;
00441   GtkTreeIter iter;
00442   gchar *str;
00443   LibraryMenuType *menu;
00444 
00445   model = gtk_tree_view_get_model (treeview);
00446   if (gtk_tree_model_get_iter (model, &iter, path))
00447     {
00448 
00449       /* Expand / contract nodes with children */
00450       if (gtk_tree_model_iter_has_child (model, &iter))
00451         {
00452           if (gtk_tree_view_row_expanded (treeview, path))
00453             gtk_tree_view_collapse_row (treeview, path);
00454           else
00455             gtk_tree_view_expand_row (treeview, path, FALSE);
00456           return;
00457         }
00458 
00459       /* Get the current enabled string and toggle it between "" and "*"
00460        */
00461       gtk_tree_model_get (model, &iter, NET_ENABLED_COLUMN, &str, -1);
00462       gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
00463                           NET_ENABLED_COLUMN, !strcmp (str, "*") ? "" : "*",
00464                           -1);
00465       /* set/clear the flag which says the net is enabled or disabled */
00466       gtk_tree_model_get (model, &iter, NET_LIBRARY_COLUMN, &menu, -1);
00467       menu->flag = strcmp (str, "*") == 0 ? 1 : 0;
00468       g_free (str);
00469     }
00470 }
00471 
00472 /* Called when the user clicks on a net in the left treeview.
00473  */
00474 static void
00475 net_selection_changed_cb (GtkTreeSelection * selection, gpointer data)
00476 {
00477   GtkTreeIter iter;
00478   GtkTreeModel *model;
00479   LibraryMenuType *net;
00480 
00481   if (selection_holdoff)        /* PCB is highlighting, user is not selecting */
00482     return;
00483 
00484   if (!gtk_tree_selection_get_selected (selection, &model, &iter))
00485     {
00486       selected_net = NULL;
00487 
00488       return;
00489     }
00490 
00491   /* Get a pointer, net, to the LibraryMenuType of the newly selected
00492      |  netlist row, and create a new node model from the net entries
00493      |  and insert that model into the node view.  Delete old entry model.
00494    */
00495   gtk_tree_model_get (model, &iter, NET_LIBRARY_COLUMN, &net, -1);
00496   node_model_update (net);
00497 
00498   selected_net = net;
00499 }
00500 
00501 static void
00502 netlist_disable_all_cb (GtkToggleButton * button, gpointer data)
00503 {
00504   GtkTreeIter iter;
00505   gboolean active = gtk_toggle_button_get_active (button);
00506   LibraryMenuType *menu;
00507 
00508   /* Get each net iter and change the NET_ENABLED_COLUMN to a "*" or ""
00509      |  to flag it as disabled or enabled based on toggle button state.
00510    */
00511   if (gtk_tree_model_get_iter_first (net_model, &iter))
00512     do
00513       {
00514         gtk_tree_store_set (GTK_TREE_STORE (net_model), &iter,
00515                             NET_ENABLED_COLUMN, active ? "*" : "", -1);
00516         /* set/clear the flag which says the net is enabled or disabled */
00517         gtk_tree_model_get (net_model, &iter, NET_LIBRARY_COLUMN, &menu, -1);
00518         menu->flag = active ? 0 : 1;
00519       }
00520     while (gtk_tree_model_iter_next (net_model, &iter));
00521 }
00522 
00523 /* Select on the layout the current net treeview selection
00524  */
00525 static void
00526 netlist_select_cb (GtkWidget * widget, gpointer data)
00527 {
00528   LibraryEntryType *entry;
00529   ConnectionType conn;
00530   gint i;
00531   gboolean select_flag = GPOINTER_TO_INT (data);
00532 
00533   if (!selected_net)
00534     return;
00535   if (selected_net == node_selected_net)
00536     node_selected_net = NULL;
00537 
00538   InitConnectionLookup ();
00539   ClearFlagOnAllObjects (true, FOUNDFLAG);
00540 
00541   for (i = selected_net->EntryN, entry = selected_net->Entry; i; i--, entry++)
00542     if (SeekPad (entry, &conn, false))
00543       RatFindHook (conn.type, conn.ptr1, conn.ptr2, conn.ptr2, true, FOUNDFLAG, true);
00544 
00545   SelectByFlag (FOUNDFLAG, select_flag);
00546   ClearFlagOnAllObjects (false, FOUNDFLAG);
00547   FreeConnectionLookupMemory ();
00548   IncrementUndoSerialNumber ();
00549   Draw ();
00550 }
00551 
00552 static void
00553 netlist_find_cb (GtkWidget * widget, gpointer data)
00554 {
00555   char *name = NULL;
00556 
00557   if (!selected_net)
00558     return;
00559 
00560   name = selected_net->Name + 2;
00561   hid_actionl ("connection", "reset", NULL);
00562   hid_actionl ("netlist", "find", name, NULL);
00563 }
00564 
00565 static void
00566 netlist_rip_up_cb (GtkWidget * widget, gpointer data)
00567 {
00568 
00569   if (!selected_net)
00570     return;
00571   netlist_find_cb(widget, data);
00572 
00573   VISIBLELINE_LOOP (PCB->Data);
00574   {
00575     if (TEST_FLAG (FOUNDFLAG, line) && !TEST_FLAG (LOCKFLAG, line))
00576       RemoveObject (LINE_TYPE, layer, line, line);
00577   }
00578   ENDALL_LOOP;
00579 
00580   VISIBLEARC_LOOP (PCB->Data);
00581   {
00582     if (TEST_FLAG (FOUNDFLAG, arc) && !TEST_FLAG (LOCKFLAG, arc))
00583       RemoveObject (ARC_TYPE, layer, arc, arc);
00584   }
00585   ENDALL_LOOP;
00586 
00587   if (PCB->ViaOn)
00588     VIA_LOOP (PCB->Data);
00589   {
00590     if (TEST_FLAG (FOUNDFLAG, via) && !TEST_FLAG (LOCKFLAG, via))
00591       RemoveObject (VIA_TYPE, via, via, via);
00592   }
00593   END_LOOP;
00594 
00595 }
00596 
00597 
00598 typedef struct {
00599   LibraryEntryType *ret_val;
00600   LibraryMenuType *node_net;
00601   const gchar *node_name;
00602   bool found;
00603 } node_get_node_from_name_state;
00604 
00605 static gboolean
00606 node_get_node_from_name_helper (GtkTreeModel *model, GtkTreePath *path,
00607                                 GtkTreeIter *iter, gpointer data)
00608 {
00609   LibraryMenuType *net;
00610   LibraryEntryType *node;
00611   node_get_node_from_name_state *state = data;
00612 
00613   gtk_tree_model_get (net_model, iter, NET_LIBRARY_COLUMN, &net, -1);
00614   /* Ignore non-nets (category headers) */
00615   if (net == NULL)
00616     return FALSE;
00617 
00618   /* Look for the node name in this net. */
00619   for (node = net->Entry; node - net->Entry < net->EntryN; node++)
00620     if (node->ListEntry && !strcmp (state->node_name, node->ListEntry))
00621       {
00622         state->node_net = net;
00623         state->ret_val = node;
00624         /* stop iterating */
00625         state->found = TRUE;
00626         return TRUE;
00627       }
00628   return FALSE;
00629 }
00630 
00631 LibraryEntryType *
00632 node_get_node_from_name (gchar * node_name, LibraryMenuType ** node_net)
00633 {
00634   node_get_node_from_name_state state;
00635 
00636   if (!node_name)
00637     return NULL;
00638 
00639   /* Have to force the netlist window created because we need the treeview
00640      |  models constructed to do the search.
00641    */
00642   ghid_netlist_window_create (gport);
00643 
00644   /* Now walk through node entries of each net in the net model looking for
00645      |  the node_name.
00646    */
00647   state.found = 0;
00648   state.node_name = node_name;
00649   gtk_tree_model_foreach (net_model, node_get_node_from_name_helper, &state);
00650   if (state.found)
00651     {
00652       if (node_net)
00653         *node_net = state.node_net;
00654       return state.ret_val;
00655     }
00656   return NULL;
00657 }
00658 
00659 
00660 /* ---------- Manage the GUI treeview of the data models -----------
00661  */
00662 static gint
00663 netlist_window_configure_event_cb (GtkWidget * widget, GdkEventConfigure * ev,
00664                                    gpointer data)
00665 {
00666   GtkAllocation allocation;
00667 
00668   gtk_widget_get_allocation (widget, &allocation);
00669   ghidgui->netlist_window_width = allocation.width;
00670   ghidgui->netlist_window_height = allocation.height;
00671   ghidgui->config_modified = TRUE;
00672   return FALSE;
00673 }
00674 
00675 static void
00676 netlist_close_cb (GtkWidget * widget, gpointer data)
00677 {
00678   gtk_widget_destroy (netlist_window);
00679   selected_net = NULL;
00680   netlist_window = NULL;
00681 
00682   /* For now, we are the only consumer of this API, so we can just do this */
00683   ghid_cancel_lead_user ();
00684 }
00685 
00686 
00687 static void
00688 netlist_destroy_cb (GtkWidget * widget, GHidPort * out)
00689 {
00690   selected_net = NULL;
00691   netlist_window = NULL;
00692 }
00693 
00694 void
00695 ghid_netlist_window_create (GHidPort * out)
00696 {
00697   GtkWidget *vbox, *hbox, *button, *label, *sep;
00698   GtkTreeView *treeview;
00699   GtkTreeModel *model;
00700   GtkCellRenderer *renderer;
00701   GtkTreeViewColumn *column;
00702 
00703   if (netlist_window)
00704     return;
00705 
00706   netlist_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
00707   g_signal_connect (G_OBJECT (netlist_window), "destroy",
00708                     G_CALLBACK (netlist_destroy_cb), out);
00709   gtk_window_set_title (GTK_WINDOW (netlist_window), _("PCB Netlist"));
00710   gtk_window_set_wmclass (GTK_WINDOW (netlist_window), "PCB_Netlist", "PCB");
00711   g_signal_connect (G_OBJECT (netlist_window), "configure_event",
00712                     G_CALLBACK (netlist_window_configure_event_cb), NULL);
00713   gtk_window_resize (GTK_WINDOW (netlist_window),
00714                      ghidgui->netlist_window_width,
00715                      ghidgui->netlist_window_height);
00716 
00717   gtk_container_set_border_width (GTK_CONTAINER (netlist_window), 2);
00718 
00719   vbox = gtk_vbox_new (FALSE, 4);
00720   gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
00721   gtk_container_add (GTK_CONTAINER (netlist_window), vbox);
00722   hbox = gtk_hbox_new (FALSE, 8);
00723   gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 4);
00724 
00725 
00726   model = net_model_create ();
00727   treeview = GTK_TREE_VIEW (gtk_tree_view_new_with_model (model));
00728   net_model = model;
00729   net_treeview = treeview;
00730   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (net_model),
00731                                         NET_NAME_COLUMN, GTK_SORT_ASCENDING);
00732 
00733   gtk_tree_view_set_rules_hint (treeview, FALSE);
00734   g_object_set (treeview, "enable-tree-lines", TRUE, NULL);
00735 
00736   renderer = gtk_cell_renderer_text_new ();
00737   gtk_tree_view_insert_column_with_attributes (treeview, -1, _(" "),
00738                                                renderer,
00739                                                "text", NET_ENABLED_COLUMN,
00740                                                NULL);
00741 
00742   renderer = gtk_cell_renderer_text_new ();
00743   column = gtk_tree_view_column_new_with_attributes (_("Net Name"),
00744                                                      renderer,
00745                                                      "text", NET_NAME_COLUMN, NULL);
00746   gtk_tree_view_insert_column (treeview, column, -1);
00747   gtk_tree_view_set_expander_column (treeview, column);
00748 
00749   /* TODO: dont expand all, but record expanded states when window is
00750      |  destroyed and restore state here.
00751    */
00752   gtk_tree_view_expand_all (treeview);
00753 
00754   ghid_scrolled_selection (treeview, hbox,
00755                            GTK_SELECTION_SINGLE,
00756                            GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC,
00757                            net_selection_changed_cb, NULL);
00758 
00759   /* Connect to the double click event.
00760    */
00761   g_signal_connect (G_OBJECT (treeview), "row-activated",
00762                     G_CALLBACK (net_selection_double_click_cb), NULL);
00763 
00764 
00765 
00766   /* Create the elements treeview and wait for a callback to populate it.
00767    */
00768   treeview = GTK_TREE_VIEW (gtk_tree_view_new ());
00769   node_treeview = treeview;
00770 
00771   gtk_tree_view_set_rules_hint (treeview, FALSE);
00772 
00773   renderer = gtk_cell_renderer_text_new ();
00774   gtk_tree_view_insert_column_with_attributes (treeview, -1, _("Nodes"),
00775                                                renderer,
00776                                                "text", NODE_NAME_COLUMN,
00777                                                NULL);
00778 
00779   ghid_scrolled_selection (treeview, hbox,
00780                            GTK_SELECTION_SINGLE,
00781                            GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC,
00782                            node_selection_changed_cb, NULL);
00783 
00784   hbox = gtk_hbox_new (FALSE, 0);
00785   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
00786   label = gtk_label_new (_("Operations on selected 'Net Name':"));
00787   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 4);
00788   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
00789 
00790   hbox = gtk_hbox_new (FALSE, 0);
00791   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 4);
00792 
00793   button = gtk_button_new_with_label (C_("netlist", "Select"));
00794   gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
00795   g_signal_connect (G_OBJECT (button), "clicked",
00796                     G_CALLBACK (netlist_select_cb), GINT_TO_POINTER (1));
00797 
00798   button = gtk_button_new_with_label (_("Unselect"));
00799   gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
00800   g_signal_connect (G_OBJECT (button), "clicked",
00801                     G_CALLBACK (netlist_select_cb), GINT_TO_POINTER (0));
00802 
00803   button = gtk_button_new_with_label (_("Find"));
00804   gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
00805   g_signal_connect (G_OBJECT (button), "clicked",
00806                     G_CALLBACK (netlist_find_cb), GINT_TO_POINTER (0));
00807 
00808   button = gtk_button_new_with_label (_("Rip Up"));
00809   gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
00810   g_signal_connect (G_OBJECT (button), "clicked",
00811                     G_CALLBACK (netlist_rip_up_cb), GINT_TO_POINTER (0));
00812   
00813   ghid_check_button_connected (vbox, &disable_all_button, FALSE, TRUE, FALSE,
00814                                FALSE, 0, netlist_disable_all_cb, NULL,
00815                                _("Disable all nets for adding rats"));
00816 
00817   sep = gtk_hseparator_new ();
00818   gtk_box_pack_start (GTK_BOX (vbox), sep, FALSE, FALSE, 3);
00819 
00820   hbox = gtk_hbutton_box_new ();
00821   gtk_button_box_set_layout (GTK_BUTTON_BOX (hbox), GTK_BUTTONBOX_END);
00822   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 4);
00823   button = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
00824   g_signal_connect (G_OBJECT (button), "clicked",
00825                     G_CALLBACK (netlist_close_cb), NULL);
00826   gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
00827 
00828 
00829   gtk_widget_realize (netlist_window);
00830   if (Settings.AutoPlace)
00831     gtk_window_move (GTK_WINDOW (netlist_window), 10, 10);
00832 
00833 }
00834 
00835 void
00836 ghid_netlist_window_show (GHidPort * out, gboolean raise)
00837 {
00838   ghid_netlist_window_create (out);
00839   gtk_widget_show_all (netlist_window);
00840   ghid_netlist_window_update (TRUE);
00841   if (raise)
00842     gtk_window_present(GTK_WINDOW(netlist_window));
00843 }
00844 
00845 struct ggnfnn_task {
00846   gboolean enabled_only;
00847   gchar *node_name;
00848   LibraryMenuType *found_net;
00849   GtkTreeIter iter;
00850 };
00851 
00852 static gboolean
00853 hunt_named_node (GtkTreeModel *model, GtkTreePath *path,
00854                  GtkTreeIter *iter, gpointer data)
00855 {
00856   struct ggnfnn_task *task = (struct ggnfnn_task *)data;
00857   LibraryMenuType *net;
00858   LibraryEntryType *node;
00859   gchar *str;
00860   gint j;
00861   gboolean is_disabled;
00862 
00863   /* We only want to inspect leaf nodes in the tree */
00864   if (gtk_tree_model_iter_has_child (model, iter))
00865     return FALSE;
00866 
00867   gtk_tree_model_get (model, iter, NET_LIBRARY_COLUMN, &net, -1);
00868   gtk_tree_model_get (model, iter, NET_ENABLED_COLUMN, &str, -1);
00869   is_disabled = !strcmp (str, "*");
00870   g_free (str);
00871 
00872   /* Don't check net nodes of disabled nets. */
00873   if (task->enabled_only && is_disabled)
00874     return FALSE;
00875 
00876   /* Look for the node name in this net. */
00877   for (j = net->EntryN, node = net->Entry; j; j--, node++)
00878     if (node->ListEntry && !strcmp (task->node_name, node->ListEntry))
00879       {
00880         task->found_net = net;
00881         task->iter = *iter;
00882         return TRUE;
00883       }
00884 
00885   return FALSE;
00886 }
00887 
00888 LibraryMenuType *
00889 ghid_get_net_from_node_name (gchar * node_name, gboolean enabled_only)
00890 {
00891   GtkTreePath *path;
00892   struct ggnfnn_task task;
00893 
00894   if (!node_name)
00895     return NULL;
00896 
00897   /* Have to force the netlist window created because we need the treeview
00898      |  models constructed so we can find the LibraryMenuType pointer the
00899      |  caller wants.
00900    */
00901   ghid_netlist_window_create (gport);
00902 
00903   /* If no netlist is loaded the window doesn't appear. */
00904   if (netlist_window == NULL)
00905     return NULL;
00906 
00907   task.enabled_only = enabled_only;
00908   task.node_name = node_name;
00909   task.found_net = NULL;
00910 
00911   /* Now walk through node entries of each net in the net model looking for
00912      |  the node_name.
00913    */
00914   gtk_tree_model_foreach (net_model, hunt_named_node, &task);
00915 
00916   /* We are asked to highlight the found net if enabled_only is TRUE.
00917      |  Set holdoff TRUE since this is just a highlight and user is not
00918      |  expecting normal select action to happen?  Or should the node
00919      |  treeview also get updated?  Original PCB code just tries to highlight.
00920    */
00921   if (task.found_net && enabled_only)
00922     {
00923       selection_holdoff = TRUE;
00924       path = gtk_tree_model_get_path (net_model, &task.iter);
00925       gtk_tree_view_scroll_to_cell (net_treeview, path, NULL, TRUE, 0.5, 0.5);
00926       gtk_tree_selection_select_path (gtk_tree_view_get_selection
00927                                       (net_treeview), path);
00928       selection_holdoff = FALSE;
00929     }
00930   return task.found_net;
00931 }
00932 
00933 /* PCB LookupConnection code in find.c calls this if it wants a node
00934    |  and its net highlighted.
00935 */
00936 void
00937 ghid_netlist_highlight_node (gchar * node_name)
00938 {
00939   GtkTreePath *path;
00940   GtkTreeIter iter;
00941   LibraryMenuType *net;
00942   gchar *name;
00943 
00944   if (!node_name)
00945     return;
00946 
00947   if ((net = ghid_get_net_from_node_name (node_name, TRUE)) == NULL)
00948     return;
00949 
00950   /* We've found the net containing the node, so update the node treeview
00951      |  to contain the nodes from the net.  Then we have to find the node
00952      |  in the new node model so we can highlight it.
00953    */
00954   node_model_update (net);
00955 
00956   if (gtk_tree_model_get_iter_first (node_model, &iter))
00957     do
00958       {
00959         gtk_tree_model_get (node_model, &iter, NODE_NAME_COLUMN, &name, -1);
00960 
00961         if (!strcmp (node_name, name))
00962           {                     /* found it, so highlight it */
00963             selection_holdoff = TRUE;
00964             selected_net = net;
00965             path = gtk_tree_model_get_path (node_model, &iter);
00966             gtk_tree_view_scroll_to_cell (node_treeview, path, NULL,
00967                                           TRUE, 0.5, 0.5);
00968             gtk_tree_selection_select_path (gtk_tree_view_get_selection
00969                                             (node_treeview), path);
00970             selection_holdoff = FALSE;
00971           }
00972         g_free (name);
00973       }
00974     while (gtk_tree_model_iter_next (node_model, &iter));
00975 }
00976 
00977 /* If code in PCB should change the netlist, call this to update
00978    |  what's in the netlist window.
00979 */
00980 void
00981 ghid_netlist_window_update (gboolean init_nodes)
00982 {
00983   GtkTreeModel *model;
00984 
00985   /* Make sure there is something to update */
00986   ghid_netlist_window_create (gport);
00987 
00988   model = net_model;
00989   net_model = net_model_create ();
00990   gtk_tree_view_set_model (net_treeview, net_model);
00991   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (net_model),
00992                                         NET_NAME_COLUMN, GTK_SORT_ASCENDING);
00993   if (model)
00994     {
00995       gtk_tree_store_clear (GTK_TREE_STORE (model));
00996       g_object_unref (model);
00997     }
00998 
00999   selected_net = NULL;
01000 
01001   /* XXX Check if the select callback does this for us */
01002   if (init_nodes)
01003     node_model_update ((&PCB->NetlistLib)->Menu);
01004 }
01005 
01006 static gint
01007 GhidNetlistChanged (int argc, char **argv, Coord x, Coord y)
01008 {
01009   /* XXX: We get called before the GUI is up when
01010    *         exporting from the command-line. */
01011   if (ghidgui == NULL || !ghidgui->is_up)
01012     return 0;
01013 
01014   /* There is no need to update if the netlist window isn't open */
01015   if (netlist_window == NULL)
01016     return 0;
01017 
01018   loading_new_netlist = TRUE;
01019   ghid_netlist_window_update (TRUE);
01020   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (disable_all_button),
01021                                 FALSE);
01022   loading_new_netlist = FALSE;
01023   return 0;
01024 }
01025 
01026 static const char netlistshow_syntax[] =
01027 "NetlistShow(pinname|netname)";
01028 
01029 static const char netlistshow_help[] =
01030 "Selects the given pinname or netname in the netlist window. Does not \
01031 show the window if it isn't already shown.";
01032 
01033 static gint
01034 GhidNetlistShow (int argc, char **argv, Coord x, Coord y)
01035 {
01036   ghid_netlist_window_create (gport);
01037   if (argc > 0)
01038     ghid_netlist_highlight_node(argv[0]);
01039   return 0;
01040 }
01041 
01042 static const char netlistpresent_syntax[] =
01043 "NetlistPresent()";
01044 
01045 static const char netlistpresent_help[] =
01046 "Presents the netlist window.";
01047 
01048 static gint
01049 GhidNetlistPresent (int argc, char **argv, Coord x, Coord y)
01050 {
01051   ghid_netlist_window_show (gport, TRUE);
01052   return 0;
01053 }
01054 
01055 HID_Action ghid_netlist_action_list[] = {
01056   {"NetlistChanged", 0, GhidNetlistChanged,
01057    netlistchanged_help, netlistchanged_syntax},
01058   {"NetlistShow", 0, GhidNetlistShow,
01059    netlistshow_help, netlistshow_syntax},
01060   {"NetlistPresent", 0, GhidNetlistPresent,
01061    netlistpresent_help, netlistpresent_syntax}
01062   ,
01063 };
01064 
01065 REGISTER_ACTIONS (ghid_netlist_action_list)