pcb 4.1.1
An interactive printed circuit board layout editor.
|
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)