select.c: Rename SelectConnection() to SelectByFlag() and add flag parameter
[geda-pcb/pcjc2.git] / src / hid / gtk / gui-netlist-window.c
blob103da0cab45429833b89dee38654eeb02a7c7d20
1 /*
2 * COPYRIGHT
4 * PCB, interactive printed circuit board design
5 * Copyright (C) 1994,1995,1996,1997,1998 Thomas Nau
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 * Contact addresses for paper mail and Email:
22 * Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
23 * Thomas.Nau@rz.uni-ulm.de
27 /*
28 * This file written by Bill Wilson for the PCB Gtk port
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
35 #include <stdio.h>
36 #include <stdlib.h>
37 #ifdef HAVE_STRING_H
38 #include <string.h>
39 #endif
41 #include "global.h"
43 #include "create.h"
44 #include "data.h"
45 #include "draw.h"
46 #include "error.h"
47 #include "find.h"
48 #include "misc.h"
49 #include "mymem.h"
50 #include "rats.h"
51 #include "remove.h"
52 #include "search.h"
53 #include "select.h"
54 #include "set.h"
55 #include "undo.h"
57 #include "gui.h"
59 #ifdef HAVE_LIBDMALLOC
60 #include <dmalloc.h>
61 #endif
63 #define NET_HIERARCHY_SEPARATOR "/"
65 static GtkWidget *netlist_window;
66 static GtkWidget *disable_all_button;
68 static GtkTreeModel *node_model;
69 static GtkTreeView *node_treeview;
71 static gboolean selection_holdoff;
73 static LibraryMenuType *selected_net;
74 static LibraryMenuType *node_selected_net;
77 /* The Netlist window displays all the layout nets in a left treeview
78 | When one of the nets is selected, all of its nodes (or connections)
79 | will be displayed in a right treeview. If a "Select on layout" button
80 | is pressed, the net that is selected in the left treeview will be
81 | drawn selected on the layout.
83 | Gtk separates the data model from the view in its treeview widgets so
84 | here we maintain two data models. The net data model has pointers to
85 | all the nets in the layout and the node data model keeps pointers to all
86 | the nodes for the currently selected net. By updating the data models
87 | the net and node gtk treeviews handle displaying the results.
89 | The netlist window code has a public interface providing hooks so PCB
90 | code can control the net and node treeviews:
92 | ghid_get_net_from_node_name gchar *node_name, gboolean enabled_only)
93 | Given a node name (eg C101-1), walk through the nets in the net
94 | data model and search each net for the given node_name. If found
95 | and enabled_only is true, make the net treeview scroll to and
96 | highlight (select) the found net. Return the found net.
98 | ghid_netlist_highlight_node()
99 | Given some PCB internal pointers (not really a good gui api here)
100 | look up a node name determined by the pointers and highlight the node
101 | in the node treeview. By using ghid_get_net_from_node_name() to
102 | look up the node, the net the node belongs to will also be
103 | highlighted in the net treeview.
105 | ghid_netlist_window_update(gboolean init_nodes)
106 | PCB calls this to tell the gui netlist code the layout net has
107 | changed and the gui data structures (net and optionally node data
108 | models) should be rebuilt.
112 /* -------- The netlist nodes (LibraryEntryType) data model ----------
113 | Each time a net is selected in the left treeview, this node model
114 | is recreated containing all the nodes (pins/pads) that are connected
115 | to the net. Loading the new model will update the right treeview with
116 | all the new node names (C100-1, R100-1, etc).
118 | The terminology is a bit confusing because the PCB netlist data
119 | structures are generic structures used for library elements, netlist
120 | data, and possibly other things also. The mapping is that
121 | the layout netlist data structure is a LibraryType which
122 | contains an allocated array of LibraryMenuType structs. Each of these
123 | structs represents a net in the netlist and contains an array
124 | of LibraryEntryType structs which represent the nodes connecting to
125 | the net. So we have:
127 | Nets Nodes
128 | LibraryType LibraryMenuType LibraryEntryType
129 | -------------------------------------------------------
130 | PCB->NetlistLib------Menu[0]-----------Entry[0]
131 | | Entry[1]
132 | | ...
134 | --Menu[1]-----------Entry[0]
135 | | Entry[1]
136 | | ...
138 | -- ...
140 | Where for example Menu[] names would be nets GND, Vcc, etc and Entry[]
141 | names would be nodes C101-1, R101-2, etc
144 LibraryEntryType *node_get_node_from_name (gchar * node_name,
145 LibraryMenuType ** node_net);
147 enum
149 NODE_NAME_COLUMN, /* Name to show in the treeview */
150 NODE_LIBRARY_COLUMN, /* Pointer to this node (LibraryEntryType) */
151 N_NODE_COLUMNS
154 /* Given a net in the netlist (a LibraryMenuType) put all the Entry[]
155 | names (the nodes) into a newly created node tree model.
157 static GtkTreeModel *
158 node_model_create (LibraryMenuType * menu)
160 GtkListStore *store;
161 GtkTreeIter iter;
163 store = gtk_list_store_new (N_NODE_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER);
165 if (menu == NULL)
166 return GTK_TREE_MODEL (store);
168 ENTRY_LOOP (menu);
170 if (!entry->ListEntry)
171 continue;
172 gtk_list_store_append (store, &iter);
173 gtk_list_store_set (store, &iter,
174 NODE_NAME_COLUMN, entry->ListEntry,
175 NODE_LIBRARY_COLUMN, entry, -1);
177 END_LOOP;
179 return GTK_TREE_MODEL (store);
182 /* When there's a new node to display in the node treeview, call this.
183 | Create a new model containing the nodes of the given net, insert
184 | the model into the treeview and unref the old model.
186 static void
187 node_model_update (LibraryMenuType * menu)
189 GtkTreeModel *model;
191 model = node_model;
192 node_model = node_model_create (menu);
193 gtk_tree_view_set_model (node_treeview, node_model);
195 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (node_model),
196 NODE_NAME_COLUMN, GTK_SORT_ASCENDING);
198 /* We could be using gtk_list_store_clear() on the same model, but it's
199 | just as easy that we've created a new one and here unref the old one.
201 if (model)
202 g_object_unref (G_OBJECT (model));
205 static void
206 toggle_pin_selected (LibraryEntryType *entry)
208 ConnectionType conn;
210 if (!SeekPad (entry, &conn, false))
211 return;
213 AddObjectToFlagUndoList (conn.type, conn.ptr1, conn.ptr2, conn.ptr2);
214 TOGGLE_FLAG (SELECTEDFLAG, (AnyObjectType *)conn.ptr2);
215 DrawObject (conn.type, conn.ptr1, conn.ptr2);
219 /* Callback when the user clicks on a PCB node in the right node treeview.
221 static void
222 node_selection_changed_cb (GtkTreeSelection * selection, gpointer data)
224 GtkTreeIter iter;
225 GtkTreeModel *model;
226 LibraryMenuType *node_net;
227 LibraryEntryType *node;
228 ConnectionType conn;
229 Coord x, y;
230 static gchar *node_name;
232 if (selection_holdoff) /* PCB is highlighting, user is not selecting */
233 return;
235 /* Toggle off the previous selection. Look up node_name to make sure
236 | it still exists. This toggling can get out of sync if a node is
237 | toggled selected, then the net that includes the node is selected
238 | then unselected.
240 if ((node = node_get_node_from_name (node_name, &node_net)) != NULL)
242 /* If net node belongs to has been highlighted/unhighighed, toggling
243 | if off here will get our on/off toggling out of sync.
245 if (node_net == node_selected_net)
247 toggle_pin_selected (node);
248 ghid_cancel_lead_user ();
250 g_free (node_name);
251 node_name = NULL;
254 /* Get the selected treeview row.
256 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
258 if (node)
259 ghid_invalidate_all ();
260 return;
263 /* From the treeview row, extract the node pointer stored there and
264 | we've got a pointer to the LibraryEntryType (node) the row
265 | represents.
267 gtk_tree_model_get (model, &iter, NODE_LIBRARY_COLUMN, &node, -1);
269 dup_string (&node_name, node->ListEntry);
270 node_selected_net = selected_net;
272 /* Now just toggle a select of the node on the layout
274 toggle_pin_selected (node);
275 IncrementUndoSerialNumber ();
277 /* And lead the user to the location */
278 if (SeekPad (node, &conn, false))
279 switch (conn.type) {
280 case PIN_TYPE:
282 PinType *pin = (PinType *) conn.ptr2;
283 x = pin->X;
284 y = pin->Y;
285 gui->set_crosshair (x, y, 0);
286 ghid_lead_user_to_location (x, y);
287 break;
289 case PAD_TYPE:
291 PadType *pad = (PadType *) conn.ptr2;
292 x = pad->Point1.X + (pad->Point2.X - pad->Point1.X) / 2;
293 y = pad->Point1.Y + (pad->Point2.Y - pad->Point1.Y) / 2;
294 gui->set_crosshair (x, y, 0);
295 ghid_lead_user_to_location (x, y);
296 break;
302 /* -------- The net (LibraryMenuType) data model ----------
304 /* TODO: the enable and disable all nets. Can't seem to get how that's
305 | supposed to work, but it'll take updating the NET_ENABLED_COLUMN in
306 | the net_model. Probably it should be made into a gpointer and make
307 | a text renderer for it and just write a '*' or a ' ' similar to the
308 | the Xt PCB scheme. Or better, since it's an "all nets" function, just
309 | have a "Disable all nets" toggle button and don't mess with the
310 | model/treeview at all.
312 enum
314 NET_ENABLED_COLUMN, /* If enabled will be ' ', if disable '*' */
315 NET_NAME_COLUMN, /* Name to show in the treeview */
316 NET_LIBRARY_COLUMN, /* Pointer to this net (LibraryMenuType) */
317 N_NET_COLUMNS
320 static GtkTreeModel *net_model = NULL;
321 static GtkTreeView *net_treeview;
323 static gboolean loading_new_netlist;
325 static GtkTreeModel *
326 net_model_create (void)
328 GtkTreeModel *model;
329 GtkTreeStore *store;
330 GtkTreeIter new_iter;
331 GtkTreeIter parent_iter;
332 GtkTreeIter *parent_ptr;
333 GtkTreePath *path;
334 GtkTreeRowReference *row_ref;
335 GHashTable *prefix_hash;
336 char *display_name;
337 char *hash_string;
338 char **join_array;
339 char **path_segments;
340 int path_depth;
341 int try_depth;
343 store = gtk_tree_store_new (N_NET_COLUMNS,
344 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER);
346 model = GTK_TREE_MODEL (store);
348 /* Hash table stores GtkTreeRowReference for given path prefixes */
349 prefix_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
350 (GDestroyNotify)
351 gtk_tree_row_reference_free);
353 MENU_LOOP (&PCB->NetlistLib);
355 if (!menu->Name)
356 continue;
358 if (loading_new_netlist)
359 menu->flag = TRUE;
361 parent_ptr = NULL;
363 path_segments = g_strsplit (menu->Name, NET_HIERARCHY_SEPARATOR, 0);
364 path_depth = g_strv_length (path_segments);
366 for (try_depth = path_depth - 1; try_depth > 0; try_depth--)
368 join_array = g_new0 (char *, try_depth + 1);
369 memcpy (join_array, path_segments, sizeof (char *) * try_depth);
371 /* See if this net's parent node is in the hash table */
372 hash_string = g_strjoinv (NET_HIERARCHY_SEPARATOR, join_array);
373 g_free (join_array);
375 row_ref = (GtkTreeRowReference *)g_hash_table_lookup (prefix_hash, hash_string);
376 g_free (hash_string);
378 /* If we didn't find the path at this level, keep looping */
379 if (row_ref == NULL)
380 continue;
382 path = gtk_tree_row_reference_get_path (row_ref);
383 gtk_tree_model_get_iter (model, &parent_iter, path);
384 parent_ptr = &parent_iter;
385 break;
388 /* NB: parent_ptr may still be NULL if we reached the toplevel */
390 /* Now walk up the desired path, adding the nodes */
392 for (; try_depth < path_depth - 1; try_depth++)
394 display_name = g_strconcat (path_segments[try_depth],
395 NET_HIERARCHY_SEPARATOR, NULL);
396 gtk_tree_store_append (store, &new_iter, parent_ptr);
397 gtk_tree_store_set (store, &new_iter,
398 NET_ENABLED_COLUMN, "",
399 NET_NAME_COLUMN, display_name,
400 NET_LIBRARY_COLUMN, NULL, -1);
401 g_free (display_name);
403 path = gtk_tree_model_get_path (model, &new_iter);
404 row_ref = gtk_tree_row_reference_new (model, path);
405 parent_iter = new_iter;
406 parent_ptr = &parent_iter;
408 join_array = g_new0 (char *, try_depth + 2);
409 memcpy (join_array, path_segments, sizeof (char *) * (try_depth + 1));
411 hash_string = g_strjoinv (NET_HIERARCHY_SEPARATOR, join_array);
412 g_free (join_array);
414 /* Insert those node in the hash table */
415 g_hash_table_insert (prefix_hash, hash_string, row_ref);
416 /* Don't free hash_string, it is now oened by the hash table */
419 gtk_tree_store_append (store, &new_iter, parent_ptr);
420 gtk_tree_store_set (store, &new_iter,
421 NET_ENABLED_COLUMN, menu->flag ? "" : "*",
422 NET_NAME_COLUMN, path_segments[path_depth - 1],
423 NET_LIBRARY_COLUMN, menu, -1);
424 g_strfreev (path_segments);
426 END_LOOP;
428 g_hash_table_destroy (prefix_hash);
430 return model;
434 /* Called when the user double clicks on a net in the left treeview.
436 static void
437 net_selection_double_click_cb (GtkTreeView * treeview, GtkTreePath * path,
438 GtkTreeViewColumn * col, gpointer data)
440 GtkTreeModel *model;
441 GtkTreeIter iter;
442 gchar *str;
443 LibraryMenuType *menu;
445 model = gtk_tree_view_get_model (treeview);
446 if (gtk_tree_model_get_iter (model, &iter, path))
449 /* Expand / contract nodes with children */
450 if (gtk_tree_model_iter_has_child (model, &iter))
452 if (gtk_tree_view_row_expanded (treeview, path))
453 gtk_tree_view_collapse_row (treeview, path);
454 else
455 gtk_tree_view_expand_row (treeview, path, FALSE);
456 return;
459 /* Get the current enabled string and toggle it between "" and "*"
461 gtk_tree_model_get (model, &iter, NET_ENABLED_COLUMN, &str, -1);
462 gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
463 NET_ENABLED_COLUMN, !strcmp (str, "*") ? "" : "*",
464 -1);
465 /* set/clear the flag which says the net is enabled or disabled */
466 gtk_tree_model_get (model, &iter, NET_LIBRARY_COLUMN, &menu, -1);
467 menu->flag = strcmp (str, "*") == 0 ? 1 : 0;
468 g_free (str);
472 /* Called when the user clicks on a net in the left treeview.
474 static void
475 net_selection_changed_cb (GtkTreeSelection * selection, gpointer data)
477 GtkTreeIter iter;
478 GtkTreeModel *model;
479 LibraryMenuType *net;
481 if (selection_holdoff) /* PCB is highlighting, user is not selecting */
482 return;
484 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
486 selected_net = NULL;
488 return;
491 /* Get a pointer, net, to the LibraryMenuType of the newly selected
492 | netlist row, and create a new node model from the net entries
493 | and insert that model into the node view. Delete old entry model.
495 gtk_tree_model_get (model, &iter, NET_LIBRARY_COLUMN, &net, -1);
496 node_model_update (net);
498 selected_net = net;
501 static void
502 netlist_disable_all_cb (GtkToggleButton * button, gpointer data)
504 GtkTreeIter iter;
505 gboolean active = gtk_toggle_button_get_active (button);
506 LibraryMenuType *menu;
508 /* Get each net iter and change the NET_ENABLED_COLUMN to a "*" or ""
509 | to flag it as disabled or enabled based on toggle button state.
511 if (gtk_tree_model_get_iter_first (net_model, &iter))
514 gtk_tree_store_set (GTK_TREE_STORE (net_model), &iter,
515 NET_ENABLED_COLUMN, active ? "*" : "", -1);
516 /* set/clear the flag which says the net is enabled or disabled */
517 gtk_tree_model_get (net_model, &iter, NET_LIBRARY_COLUMN, &menu, -1);
518 menu->flag = active ? 0 : 1;
520 while (gtk_tree_model_iter_next (net_model, &iter));
523 /* Select on the layout the current net treeview selection
525 static void
526 netlist_select_cb (GtkWidget * widget, gpointer data)
528 LibraryEntryType *entry;
529 ConnectionType conn;
530 gint i;
531 gboolean select_flag = GPOINTER_TO_INT (data);
533 if (!selected_net)
534 return;
535 if (selected_net == node_selected_net)
536 node_selected_net = NULL;
538 InitConnectionLookup ();
539 ResetConnections (true, FOUNDFLAG);
541 for (i = selected_net->EntryN, entry = selected_net->Entry; i; i--, entry++)
542 if (SeekPad (entry, &conn, false))
543 RatFindHook (conn.type, conn.ptr1, conn.ptr2, conn.ptr2, true, FOUNDFLAG, true);
545 SelectByFlag (FOUNDFLAG, select_flag);
546 ResetConnections (false, FOUNDFLAG);
547 FreeConnectionLookupMemory ();
548 IncrementUndoSerialNumber ();
549 Draw ();
552 static void
553 netlist_find_cb (GtkWidget * widget, gpointer data)
555 char *name = NULL;
557 if (!selected_net)
558 return;
560 name = selected_net->Name + 2;
561 hid_actionl ("connection", "reset", NULL);
562 hid_actionl ("netlist", "find", name, NULL);
565 static void
566 netlist_rip_up_cb (GtkWidget * widget, gpointer data)
569 if (!selected_net)
570 return;
571 netlist_find_cb(widget, data);
573 VISIBLELINE_LOOP (PCB->Data);
575 if (TEST_FLAG (FOUNDFLAG, line) && !TEST_FLAG (LOCKFLAG, line))
576 RemoveObject (LINE_TYPE, layer, line, line);
578 ENDALL_LOOP;
580 VISIBLEARC_LOOP (PCB->Data);
582 if (TEST_FLAG (FOUNDFLAG, arc) && !TEST_FLAG (LOCKFLAG, arc))
583 RemoveObject (ARC_TYPE, layer, arc, arc);
585 ENDALL_LOOP;
587 if (PCB->ViaOn)
588 VIA_LOOP (PCB->Data);
590 if (TEST_FLAG (FOUNDFLAG, via) && !TEST_FLAG (LOCKFLAG, via))
591 RemoveObject (VIA_TYPE, via, via, via);
593 END_LOOP;
597 /**/
598 typedef struct {
599 LibraryEntryType *ret_val;
600 LibraryMenuType *node_net;
601 const gchar *node_name;
602 bool found;
603 } node_get_node_from_name_state;
605 static gboolean
606 node_get_node_from_name_helper (GtkTreeModel *model, GtkTreePath *path,
607 GtkTreeIter *iter, gpointer data)
609 LibraryMenuType *net;
610 LibraryEntryType *node;
611 node_get_node_from_name_state *state = data;
613 gtk_tree_model_get (net_model, iter, NET_LIBRARY_COLUMN, &net, -1);
614 /* Ignore non-nets (category headers) */
615 if (net == NULL)
616 return FALSE;
618 /* Look for the node name in this net. */
619 for (node = net->Entry; node - net->Entry < net->EntryN; node++)
620 if (node->ListEntry && !strcmp (state->node_name, node->ListEntry))
622 state->node_net = net;
623 state->ret_val = node;
624 /* stop iterating */
625 state->found = TRUE;
626 return TRUE;
628 return FALSE;
631 LibraryEntryType *
632 node_get_node_from_name (gchar * node_name, LibraryMenuType ** node_net)
634 node_get_node_from_name_state state;
636 if (!node_name)
637 return NULL;
639 /* Have to force the netlist window created because we need the treeview
640 | models constructed to do the search.
642 ghid_netlist_window_create (gport);
644 /* Now walk through node entries of each net in the net model looking for
645 | the node_name.
647 state.found = 0;
648 state.node_name = node_name;
649 gtk_tree_model_foreach (net_model, node_get_node_from_name_helper, &state);
650 if (state.found)
652 if (node_net)
653 *node_net = state.node_net;
654 return state.ret_val;
656 return NULL;
658 /**/
660 /* ---------- Manage the GUI treeview of the data models -----------
662 static gint
663 netlist_window_configure_event_cb (GtkWidget * widget, GdkEventConfigure * ev,
664 gpointer data)
666 GtkAllocation allocation;
668 gtk_widget_get_allocation (widget, &allocation);
669 ghidgui->netlist_window_height = allocation.height;
670 ghidgui->config_modified = TRUE;
671 return FALSE;
674 static void
675 netlist_close_cb (GtkWidget * widget, gpointer data)
677 gtk_widget_destroy (netlist_window);
678 selected_net = NULL;
679 netlist_window = NULL;
681 /* For now, we are the only consumer of this API, so we can just do this */
682 ghid_cancel_lead_user ();
686 static void
687 netlist_destroy_cb (GtkWidget * widget, GHidPort * out)
689 selected_net = NULL;
690 netlist_window = NULL;
693 void
694 ghid_netlist_window_create (GHidPort * out)
696 GtkWidget *vbox, *hbox, *button, *label, *sep;
697 GtkTreeView *treeview;
698 GtkTreeModel *model;
699 GtkCellRenderer *renderer;
700 GtkTreeViewColumn *column;
702 if (netlist_window)
703 return;
705 netlist_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
706 g_signal_connect (G_OBJECT (netlist_window), "destroy",
707 G_CALLBACK (netlist_destroy_cb), out);
708 gtk_window_set_title (GTK_WINDOW (netlist_window), _("PCB Netlist"));
709 gtk_window_set_wmclass (GTK_WINDOW (netlist_window), "PCB_Netlist", "PCB");
710 g_signal_connect (G_OBJECT (netlist_window), "configure_event",
711 G_CALLBACK (netlist_window_configure_event_cb), NULL);
712 gtk_window_set_default_size (GTK_WINDOW (netlist_window),
713 -1, ghidgui->netlist_window_height);
715 gtk_container_set_border_width (GTK_CONTAINER (netlist_window), 2);
717 vbox = gtk_vbox_new (FALSE, 4);
718 gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
719 gtk_container_add (GTK_CONTAINER (netlist_window), vbox);
720 hbox = gtk_hbox_new (FALSE, 8);
721 gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 4);
724 model = net_model_create ();
725 treeview = GTK_TREE_VIEW (gtk_tree_view_new_with_model (model));
726 net_model = model;
727 net_treeview = treeview;
728 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (net_model),
729 NET_NAME_COLUMN, GTK_SORT_ASCENDING);
731 gtk_tree_view_set_rules_hint (treeview, FALSE);
732 g_object_set (treeview, "enable-tree-lines", TRUE, NULL);
734 renderer = gtk_cell_renderer_text_new ();
735 gtk_tree_view_insert_column_with_attributes (treeview, -1, _(" "),
736 renderer,
737 "text", NET_ENABLED_COLUMN,
738 NULL);
740 renderer = gtk_cell_renderer_text_new ();
741 column = gtk_tree_view_column_new_with_attributes (_("Net Name"),
742 renderer,
743 "text", NET_NAME_COLUMN, NULL);
744 gtk_tree_view_insert_column (treeview, column, -1);
745 gtk_tree_view_set_expander_column (treeview, column);
747 /* TODO: dont expand all, but record expanded states when window is
748 | destroyed and restore state here.
750 gtk_tree_view_expand_all (treeview);
752 ghid_scrolled_selection (treeview, hbox,
753 GTK_SELECTION_SINGLE,
754 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC,
755 net_selection_changed_cb, NULL);
757 /* Connect to the double click event.
759 g_signal_connect (G_OBJECT (treeview), "row-activated",
760 G_CALLBACK (net_selection_double_click_cb), NULL);
764 /* Create the elements treeview and wait for a callback to populate it.
766 treeview = GTK_TREE_VIEW (gtk_tree_view_new ());
767 node_treeview = treeview;
769 gtk_tree_view_set_rules_hint (treeview, FALSE);
771 renderer = gtk_cell_renderer_text_new ();
772 gtk_tree_view_insert_column_with_attributes (treeview, -1, _("Nodes"),
773 renderer,
774 "text", NODE_NAME_COLUMN,
775 NULL);
777 ghid_scrolled_selection (treeview, hbox,
778 GTK_SELECTION_SINGLE,
779 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC,
780 node_selection_changed_cb, NULL);
782 hbox = gtk_hbox_new (FALSE, 0);
783 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
784 label = gtk_label_new (_("Operations on selected 'Net Name':"));
785 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 4);
786 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
788 hbox = gtk_hbox_new (FALSE, 0);
789 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 4);
791 button = gtk_button_new_with_label (_("Select"));
792 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
793 g_signal_connect (G_OBJECT (button), "clicked",
794 G_CALLBACK (netlist_select_cb), GINT_TO_POINTER (1));
796 button = gtk_button_new_with_label (_("Unselect"));
797 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
798 g_signal_connect (G_OBJECT (button), "clicked",
799 G_CALLBACK (netlist_select_cb), GINT_TO_POINTER (0));
801 button = gtk_button_new_with_label (_("Find"));
802 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
803 g_signal_connect (G_OBJECT (button), "clicked",
804 G_CALLBACK (netlist_find_cb), GINT_TO_POINTER (0));
806 button = gtk_button_new_with_label (_("Rip Up"));
807 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
808 g_signal_connect (G_OBJECT (button), "clicked",
809 G_CALLBACK (netlist_rip_up_cb), GINT_TO_POINTER (0));
811 ghid_check_button_connected (vbox, &disable_all_button, FALSE, TRUE, FALSE,
812 FALSE, 0, netlist_disable_all_cb, NULL,
813 _("Disable all nets for adding rats"));
815 sep = gtk_hseparator_new ();
816 gtk_box_pack_start (GTK_BOX (vbox), sep, FALSE, FALSE, 3);
818 hbox = gtk_hbutton_box_new ();
819 gtk_button_box_set_layout (GTK_BUTTON_BOX (hbox), GTK_BUTTONBOX_END);
820 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 4);
821 button = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
822 g_signal_connect (G_OBJECT (button), "clicked",
823 G_CALLBACK (netlist_close_cb), NULL);
824 gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
827 gtk_widget_realize (netlist_window);
828 if (Settings.AutoPlace)
829 gtk_window_move (GTK_WINDOW (netlist_window), 10, 10);
833 void
834 ghid_netlist_window_show (GHidPort * out, gboolean raise)
836 ghid_netlist_window_create (out);
837 gtk_widget_show_all (netlist_window);
838 ghid_netlist_window_update (TRUE);
839 if (raise)
840 gtk_window_present(GTK_WINDOW(netlist_window));
843 struct ggnfnn_task {
844 gboolean enabled_only;
845 gchar *node_name;
846 LibraryMenuType *found_net;
847 GtkTreeIter iter;
850 static gboolean
851 hunt_named_node (GtkTreeModel *model, GtkTreePath *path,
852 GtkTreeIter *iter, gpointer data)
854 struct ggnfnn_task *task = (struct ggnfnn_task *)data;
855 LibraryMenuType *net;
856 LibraryEntryType *node;
857 gchar *str;
858 gint j;
859 gboolean is_disabled;
861 /* We only want to inspect leaf nodes in the tree */
862 if (gtk_tree_model_iter_has_child (model, iter))
863 return FALSE;
865 gtk_tree_model_get (model, iter, NET_LIBRARY_COLUMN, &net, -1);
866 gtk_tree_model_get (model, iter, NET_ENABLED_COLUMN, &str, -1);
867 is_disabled = !strcmp (str, "*");
868 g_free (str);
870 /* Don't check net nodes of disabled nets. */
871 if (task->enabled_only && is_disabled)
872 return FALSE;
874 /* Look for the node name in this net. */
875 for (j = net->EntryN, node = net->Entry; j; j--, node++)
876 if (node->ListEntry && !strcmp (task->node_name, node->ListEntry))
878 task->found_net = net;
879 task->iter = *iter;
880 return TRUE;
883 return FALSE;
886 LibraryMenuType *
887 ghid_get_net_from_node_name (gchar * node_name, gboolean enabled_only)
889 GtkTreePath *path;
890 struct ggnfnn_task task;
892 if (!node_name)
893 return NULL;
895 /* Have to force the netlist window created because we need the treeview
896 | models constructed so we can find the LibraryMenuType pointer the
897 | caller wants.
899 ghid_netlist_window_create (gport);
901 /* If no netlist is loaded the window doesn't appear. */
902 if (netlist_window == NULL)
903 return NULL;
905 task.enabled_only = enabled_only;
906 task.node_name = node_name;
907 task.found_net = NULL;
909 /* Now walk through node entries of each net in the net model looking for
910 | the node_name.
912 gtk_tree_model_foreach (net_model, hunt_named_node, &task);
914 /* We are asked to highlight the found net if enabled_only is TRUE.
915 | Set holdoff TRUE since this is just a highlight and user is not
916 | expecting normal select action to happen? Or should the node
917 | treeview also get updated? Original PCB code just tries to highlight.
919 if (task.found_net && enabled_only)
921 selection_holdoff = TRUE;
922 path = gtk_tree_model_get_path (net_model, &task.iter);
923 gtk_tree_view_scroll_to_cell (net_treeview, path, NULL, TRUE, 0.5, 0.5);
924 gtk_tree_selection_select_path (gtk_tree_view_get_selection
925 (net_treeview), path);
926 selection_holdoff = FALSE;
928 return task.found_net;
931 /* PCB LookupConnection code in find.c calls this if it wants a node
932 | and its net highlighted.
934 void
935 ghid_netlist_highlight_node (gchar * node_name)
937 GtkTreePath *path;
938 GtkTreeIter iter;
939 LibraryMenuType *net;
940 gchar *name;
942 if (!node_name)
943 return;
945 if ((net = ghid_get_net_from_node_name (node_name, TRUE)) == NULL)
946 return;
948 /* We've found the net containing the node, so update the node treeview
949 | to contain the nodes from the net. Then we have to find the node
950 | in the new node model so we can highlight it.
952 node_model_update (net);
954 if (gtk_tree_model_get_iter_first (node_model, &iter))
957 gtk_tree_model_get (node_model, &iter, NODE_NAME_COLUMN, &name, -1);
959 if (!strcmp (node_name, name))
960 { /* found it, so highlight it */
961 selection_holdoff = TRUE;
962 selected_net = net;
963 path = gtk_tree_model_get_path (node_model, &iter);
964 gtk_tree_view_scroll_to_cell (node_treeview, path, NULL,
965 TRUE, 0.5, 0.5);
966 gtk_tree_selection_select_path (gtk_tree_view_get_selection
967 (node_treeview), path);
968 selection_holdoff = FALSE;
970 g_free (name);
972 while (gtk_tree_model_iter_next (node_model, &iter));
975 /* If code in PCB should change the netlist, call this to update
976 | what's in the netlist window.
978 void
979 ghid_netlist_window_update (gboolean init_nodes)
981 GtkTreeModel *model;
983 /* Make sure there is something to update */
984 ghid_netlist_window_create (gport);
986 model = net_model;
987 net_model = net_model_create ();
988 gtk_tree_view_set_model (net_treeview, net_model);
989 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (net_model),
990 NET_NAME_COLUMN, GTK_SORT_ASCENDING);
991 if (model)
993 gtk_tree_store_clear (GTK_TREE_STORE (model));
994 g_object_unref (model);
997 selected_net = NULL;
999 /* XXX Check if the select callback does this for us */
1000 if (init_nodes)
1001 node_model_update ((&PCB->NetlistLib)->Menu);
1004 static gint
1005 GhidNetlistChanged (int argc, char **argv, Coord x, Coord y)
1007 /* XXX: We get called before the GUI is up when
1008 * exporting from the command-line. */
1009 if (ghidgui == NULL || !ghidgui->is_up)
1010 return 0;
1012 /* There is no need to update if the netlist window isn't open */
1013 if (netlist_window == NULL)
1014 return 0;
1016 loading_new_netlist = TRUE;
1017 ghid_netlist_window_update (TRUE);
1018 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (disable_all_button),
1019 FALSE);
1020 loading_new_netlist = FALSE;
1021 return 0;
1024 static const char netlistshow_syntax[] =
1025 "NetlistShow(pinname|netname)";
1027 static const char netlistshow_help[] =
1028 "Selects the given pinname or netname in the netlist window. Does not \
1029 show the window if it isn't already shown.";
1031 static gint
1032 GhidNetlistShow (int argc, char **argv, Coord x, Coord y)
1034 ghid_netlist_window_create (gport);
1035 if (argc > 0)
1036 ghid_netlist_highlight_node(argv[0]);
1037 return 0;
1040 static const char netlistpresent_syntax[] =
1041 "NetlistPresent()";
1043 static const char netlistpresent_help[] =
1044 "Presents the netlist window.";
1046 static gint
1047 GhidNetlistPresent (int argc, char **argv, Coord x, Coord y)
1049 ghid_netlist_window_show (gport, TRUE);
1050 return 0;
1053 HID_Action ghid_netlist_action_list[] = {
1054 {"NetlistChanged", 0, GhidNetlistChanged,
1055 netlistchanged_help, netlistchanged_syntax},
1056 {"NetlistShow", 0, GhidNetlistShow,
1057 netlistshow_help, netlistshow_syntax},
1058 {"NetlistPresent", 0, GhidNetlistPresent,
1059 netlistpresent_help, netlistpresent_syntax}
1063 REGISTER_ACTIONS (ghid_netlist_action_list)