Merge ~craftyjon/kicad:bus_upgrades_merge into kicad:master

Proposed by Jon Evans on 2019-03-12
Status: Merged
Merge reported by: Wayne Stambaugh
Merged at revision: 41e689776e3e10505d5d823376e4dfc6efe8ae62
Proposed branch: ~craftyjon/kicad:bus_upgrades_merge
Merge into: kicad:master
Diff against target: 10860 lines (+7627/-469)
96 files modified
CMakeLists.txt (+1/-1)
common/validators.cpp (+54/-1)
eeschema/CMakeLists.txt (+7/-0)
eeschema/annotate.cpp (+6/-6)
eeschema/block.cpp (+1/-1)
eeschema/bus-wire-junction.cpp (+185/-38)
eeschema/bus_alias.cpp (+41/-0)
eeschema/bus_alias.h (+100/-0)
eeschema/connection_graph.cpp (+1912/-0)
eeschema/connection_graph.h (+341/-0)
eeschema/controle.cpp (+0/-3)
eeschema/dialogs/dialog_bom.cpp (+4/-1)
eeschema/dialogs/dialog_bus_manager.cpp (+517/-0)
eeschema/dialogs/dialog_bus_manager.h (+90/-0)
eeschema/dialogs/dialog_edit_label.cpp (+7/-4)
eeschema/dialogs/dialog_edit_label_base.cpp (+1/-3)
eeschema/dialogs/dialog_edit_label_base.fbp (+4/-7)
eeschema/dialogs/dialog_edit_label_base.h (+2/-3)
eeschema/dialogs/dialog_erc.cpp (+50/-43)
eeschema/dialogs/dialog_erc.h (+4/-5)
eeschema/dialogs/dialog_erc_base.cpp (+19/-1)
eeschema/dialogs/dialog_erc_base.fbp (+367/-0)
eeschema/dialogs/dialog_erc_base.h (+5/-1)
eeschema/dialogs/dialog_migrate_buses.cpp (+229/-0)
eeschema/dialogs/dialog_migrate_buses.h (+71/-0)
eeschema/dialogs/dialog_migrate_buses_base.cpp (+70/-0)
eeschema/dialogs/dialog_migrate_buses_base.fbp (+669/-0)
eeschema/dialogs/dialog_migrate_buses_base.h (+57/-0)
eeschema/dialogs/dialog_netlist.cpp (+8/-3)
eeschema/dialogs/dialog_sch_edit_sheet_pin.cpp (+1/-0)
eeschema/dialogs/dialog_sch_sheet_props.cpp (+1/-0)
eeschema/drc_erc_item.cpp (+14/-3)
eeschema/edit_component_in_schematic.cpp (+1/-2)
eeschema/eeschema_config.cpp (+20/-1)
eeschema/eeschema_id.h (+6/-0)
eeschema/erc.cpp (+69/-1)
eeschema/erc.h (+34/-15)
eeschema/erc_settings.h (+82/-0)
eeschema/files-io.cpp (+29/-10)
eeschema/find.cpp (+13/-13)
eeschema/general.h (+15/-1)
eeschema/getpart.cpp (+6/-5)
eeschema/help_common_strings.h (+1/-0)
eeschema/hierarch.cpp (+2/-8)
eeschema/highlight_connection.cpp (+29/-47)
eeschema/hotkeys.cpp (+5/-0)
eeschema/hotkeys.h (+1/-0)
eeschema/invoke_sch_dialog.h (+3/-0)
eeschema/lib_draw_item.h (+1/-0)
eeschema/lib_pin.cpp (+14/-0)
eeschema/menubar.cpp (+6/-0)
eeschema/netlist_exporters/netlist_exporter_generic.cpp (+84/-29)
eeschema/netlist_exporters/netlist_exporter_generic.h (+10/-3)
eeschema/netlist_exporters/netlist_exporter_kicad.cpp (+128/-0)
eeschema/netlist_exporters/netlist_exporter_kicad.h (+4/-2)
eeschema/netlist_generator.cpp (+16/-12)
eeschema/netlist_object.cpp (+95/-64)
eeschema/netlist_object.h (+15/-0)
eeschema/onleftclick.cpp (+1/-1)
eeschema/onrightclick.cpp (+56/-5)
eeschema/sch_base_frame.cpp (+41/-13)
eeschema/sch_base_frame.h (+6/-3)
eeschema/sch_bus_entry.cpp (+44/-0)
eeschema/sch_bus_entry.h (+16/-0)
eeschema/sch_component.cpp (+53/-7)
eeschema/sch_component.h (+19/-14)
eeschema/sch_connection.cpp (+478/-0)
eeschema/sch_connection.h (+359/-0)
eeschema/sch_edit_frame.cpp (+67/-40)
eeschema/sch_edit_frame.h (+109/-25)
eeschema/sch_item_struct.cpp (+46/-0)
eeschema/sch_item_struct.h (+51/-5)
eeschema/sch_junction.cpp (+5/-1)
eeschema/sch_legacy_plugin.cpp (+47/-0)
eeschema/sch_legacy_plugin.h (+4/-0)
eeschema/sch_line.cpp (+35/-0)
eeschema/sch_line.h (+2/-0)
eeschema/sch_painter.cpp (+40/-0)
eeschema/sch_pin_connection.cpp (+87/-0)
eeschema/sch_pin_connection.h (+77/-0)
eeschema/sch_screen.cpp (+76/-5)
eeschema/sch_screen.h (+39/-0)
eeschema/sch_sheet.cpp (+2/-1)
eeschema/sch_sheet.h (+1/-1)
eeschema/sch_sheet_path.cpp (+16/-0)
eeschema/sch_sheet_path.h (+12/-1)
eeschema/sch_text.cpp (+47/-3)
eeschema/sch_text.h (+3/-0)
eeschema/schedit.cpp (+168/-7)
eeschema/schematic_undo_redo.cpp (+6/-0)
include/core/typeinfo.h (+1/-0)
include/validators.h (+62/-0)
pcbnew/board_netlist_updater.cpp (+9/-0)
pcbnew/board_netlist_updater.h (+1/-0)
pcbnew/class_board.cpp (+11/-0)
pcbnew/class_board.h (+3/-0)
Reviewer Review Type Date Requested Status
Wayne Stambaugh 2019-03-12 Approve on 2019-04-01
Review via email: mp+364346@code.launchpad.net
To post a comment you must log in.
Seth Hillbrand (sethh) wrote :

The major issues I see here are:
1) ERC validation
2) deterministic netlisting

But I think that there is maybe a commit missing in this as I don't see the changes in sch_edit_frame.h that would be needed.

Wayne Stambaugh (stambaughw) wrote :

Overall it looks good. I share Seth's concern about the removed ERC checks. I've also added a few in-line comments as well. The dialog comments need to be fixed. I also noticed that you added REGEX_VALIDATOR to validators.cpp but I did not see the definition for it in validators.h. I still have to test it. I should get to that soon.

Wayne Stambaugh (stambaughw) wrote :

@Jon, I did some testing. Everything compiled without issue so my concerns about the definitions in validator.h were unfounded.

I found a bug in the netlist generation. You can easily test this by opening the video demo in the kicad source and updating the board from the schematic (it also happens importing a the netlist file). There will now be 4 unconnected nets.

This was just a quick test. I hope to do some more exhaustive testing this weekend.

review: Needs Fixing
Seth Hillbrand (sethh) wrote :

> The major issues I see here are:
> 1) ERC validation
> 2) deterministic netlisting
>
> But I think that there is maybe a commit missing in this as I don't see the
> changes in sch_edit_frame.h that would be needed.

* Scratch the note about a commit being missing. I see at the end, launchpad has "helpfully" truncated the diff because it was too large. So this is only part of the merge. Ugh. I'm really looking forward to GitLab.

Seth Hillbrand (sethh) wrote :

And the ERC checks look like they also didn't make it into the launchpad diff so scratch my other comment as well. I'm beginning to regret suggesting it. It looks like this combined diff is 10k lines and we are only seeing 5k here.

Maybe we split the merge proposals into groups? Unless others have better suggestions

Jon Evans (craftyjon) wrote :

If you all have GitHub accounts I can create a PR on GitHub and you can review/comment there (I can create the PR against master on my fork rather than the upstream master so that it doesn't look like we're allowing official GitHub PRs). Alternatively I could create multiple MRs here, but my patchset cannot be easily split into parts that each compile on their own.

Regarding the technical issues / questions, I will investigate/comment more later when I have some focus time.

jean-pierre charras (jp-charras) wrote :

Hi Jon,

I am not able to compile on msys2 (W7 32 bits)
gcc version 7.4.0 (Rev1, Built by MSYS2 project)

Attached the errors.
--
Jean-Pierre CHARRAS

Wayne Stambaugh (stambaughw) wrote :

@Seth, good catch. I completely missed the truncation warning at the end of the page.

@Jon, I think it would be a good idea to open a pull request against your master branch so we can comment on it. It certainly would be nice to be able to comment on the entire pull request.

Thomas Pointhuber (pointhi) wrote :

Problems found:

* highlight net does not work with "Bus to Wire Entry"
* Unfold Bus does not create the entry at the mouse position, but always at the same (annoying) position
* why are there explicit "Bus to Wire Entry" and "Bus to Bus Entry"? They are rather complicated from a UX view.
* there should be an edit button for the bus, where users can specify the entries. Especially defining aliases, lists, subitems,... should be possible without knowing the syntax, or selecting a label to edit.

jean-pierre charras (jp-charras) wrote :

@Jon,

I found the problem for my compil issue.
std::hash<wxPoint> must be defined.
It is defined in common.h and common.cpp (conditional compilation) when using wxWidgets 3.0.x

However there is a bug: it is not defined when using wxWidgets 3.1.0 and later.
(I am using 3.1.1)

My commit a1ee5405a fixes this bug.

Seth Hillbrand (sethh) wrote :

After working with this for a day, I found a few issues although I haven't tested out the new unfolding/naming paradigm. This is just trying to work with it as before.

- Hierarchical sheets are not handled correctly. 1 layer deep and the net loses its sheet prefix. 2 layers deep and the net loses the prefix 1 before the net name.

e.g. /Sheet1/Net1 becomes /Net1 and /Sheet1/Sheet2/Net2 becomes /Sheet1/Net2

Some of this can be seen if you open PIC programmer demo and update from the schematic. All tracks (most?) are disconnected

- Undo/Redo doesn't work correctly when placing wires or busses in eeschema. Place a wire with multiple bends and then undo, you will often end up with a single wire segment remaining that doesn't live in the undo/redo stack.

Jon Evans (craftyjon) wrote :

Thanks, all. I'm looking into these issues. I'll provide a link to GitHub for review once I've made some progress.

~craftyjon/kicad:bus_upgrades_merge updated on 2019-03-17
8584b25... by Jon Evans on 2019-03-17

Fix issue where some power symbols would show up in netlist

1d58f2f... by Jon Evans on 2019-03-17

Begin refactoring netlist creation

078239a... by Jon Evans on 2019-03-17

Don't generate connections between two bus-wire entries

f650f1f... by Jon Evans on 2019-03-17

Support bus entries with highlighting

9586b7e... by Jon Evans on 2019-03-17

Fix issues with bus unfolding position

Jon Evans (craftyjon) wrote :

Rebased and fixed several issues reported above. Cannot reproduce the undo/redo bug reported by Seth so far, will keep investigating.

~craftyjon/kicad:bus_upgrades_merge updated on 2019-03-19
0010695... by Jon Evans on 2019-03-17

Fix false assert when CONNECTIVITY_DEBUG is enabled

c2e6359... by Jon Evans on 2019-03-18

Fix undo handling of SchematicCleanUp

7eda4c1... by Jon Evans on 2019-03-18

Fix a few issues with hierarchical propagation

c2948fe... by Jon Evans on 2019-03-18

Clean up cruft in netlist export

17a1bac... by Jon Evans on 2019-03-18

Try harder to reassign copper zones on netlist import

afb57c3... by Jon Evans on 2019-03-19

Bump file format version

Jon Evans (craftyjon) wrote :

Review is also happening here:
https://github.com/craftyjon/kicad/pull/1

I'll keep both launchpad and github updated until review is done.

~craftyjon/kicad:bus_upgrades_merge updated on 2019-03-31
cf0b5f5... by Jon Evans on 2019-03-23

Don't connect bus entries and bus labels that happen to overlap

968ae42... by Jon Evans on 2019-03-23

Suppress ERC warnings about multiple labels if the text is the same

c24fe8f... by Jon Evans on 2019-03-23

Add missing junctions during schematic cleanup

d8749b1... by Jon Evans on 2019-03-23

Refactor how at-load schematic normalization is called

Don't prepend "/" for nets at the top level

Revert "Don't prepend "/" for nets at the top level"

This reverts commit fa9533222f7d33eee5f3fa2320bd9f3167e28076.

ba4ded6... by Jon Evans on 2019-03-28

Improve naming of weak subgraphs

1927979... by Jon Evans on 2019-03-29

Restore ERC checks that were accidentally removed

d8e8dd9... by Jon Evans on 2019-03-30

Don't update graph when entering/leaving sheets

24ae74c... by Jon Evans on 2019-03-30

Don't call OnModify() before placing new parts

Remove unnecessary calls to TestDanglingEnds()

Disable real-time connectivity updates for now

Revert "Remove unnecessary calls to TestDanglingEnds()"

This reverts commit d93e3894f2bcd6239862ac9eae0cb2f994b9d52a.

Remove debug code

3c7168e... by Jon Evans on 2019-03-31

Cache subgraph driver connections for improved performance

41e6897... by Jon Evans on 2019-03-31

Update connectivity before engaging highlight tool

Wayne Stambaugh (stambaughw) wrote :

This has been merged.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/CMakeLists.txt b/CMakeLists.txt
2index 28b262d..2e09e18 100644
3--- a/CMakeLists.txt
4+++ b/CMakeLists.txt
5@@ -585,7 +585,7 @@ find_package( Pixman 0.30 REQUIRED )
6
7 #
8 # Find Boost headers, required.
9-find_package( Boost 1.54.0 REQUIRED )
10+find_package( Boost 1.54.0 REQUIRED COMPONENTS regex )
11
12 # Include MinGW resource compiler.
13 include( MinGWResourceCompiler )
14diff --git a/common/validators.cpp b/common/validators.cpp
15index f0529bd..ec7a3d9 100644
16--- a/common/validators.cpp
17+++ b/common/validators.cpp
18@@ -3,6 +3,7 @@
19 *
20 * Copyright (C) 2013 Wayne Stambaugh <stambaughw@verizon.net>
21 * Copyright (C) 2004-2013 KiCad Developers, see change_log.txt for contributors.
22+ * Copyright (C) 2018 CERN
23 *
24 * This program is free software; you can redistribute it and/or
25 * modify it under the terms of the GNU General Public License
26@@ -28,6 +29,7 @@
27 */
28
29 #include <kicad_string.h>
30+#include <confirm.h>
31 #include <validators.h>
32
33 #include <wx/grid.h>
34@@ -220,6 +222,57 @@ void ENV_VAR_NAME_VALIDATOR::OnTextChanged( wxCommandEvent& event )
35 }
36
37
38+bool REGEX_VALIDATOR::Validate( wxWindow* aParent )
39+{
40+ // If window is disabled, simply return
41+ if( !m_validatorWindow->IsEnabled() )
42+ return true;
43+
44+ wxTextEntry* const textEntry = GetTextEntry();
45+
46+ if( !textEntry )
47+ return false;
48+
49+ bool valid = true;
50+ const wxString& value = textEntry->GetValue();
51+
52+ if( m_regEx.Matches( value ) )
53+ {
54+ size_t start, len;
55+ m_regEx.GetMatch( &start, &len );
56+
57+ if( start != 0 || len != value.Length() ) // whole string must match
58+ valid = false;
59+ }
60+ else // no match at all
61+ {
62+ valid = false;
63+ }
64+
65+ if( !valid )
66+ {
67+ m_validatorWindow->SetFocus();
68+ DisplayError( aParent, wxString::Format( _( "Incorrect value: %s" ), value ) );
69+ return false;
70+ }
71+
72+ return true;
73+}
74+
75+
76+void REGEX_VALIDATOR::compileRegEx( const wxString& aRegEx, int aFlags )
77+{
78+ if( !m_regEx.Compile( aRegEx, aFlags ) )
79+ {
80+ throw std::runtime_error( "REGEX_VALIDATOR: Invalid regular expression: "
81+ + aRegEx.ToStdString() );
82+ }
83+
84+ m_regExString = aRegEx;
85+ m_regExFlags = aFlags;
86+}
87+
88+
89 void KIUI::ValidatorTransferToWindowWithoutEvents( wxValidator& aValidator )
90 {
91 wxWindow* ctrl = aValidator.GetWindow();
92@@ -228,4 +281,4 @@ void KIUI::ValidatorTransferToWindowWithoutEvents( wxValidator& aValidator )
93
94 wxEventBlocker orient_update_blocker( ctrl, wxEVT_ANY );
95 aValidator.TransferToWindow();
96-}
97\ No newline at end of file
98+}
99diff --git a/eeschema/CMakeLists.txt b/eeschema/CMakeLists.txt
100index c521c8f..39df4db 100644
101--- a/eeschema/CMakeLists.txt
102+++ b/eeschema/CMakeLists.txt
103@@ -37,6 +37,7 @@ set( EESCHEMA_DLGS
104 dialogs/dialog_bom.cpp
105 dialogs/dialog_bom_base.cpp
106 dialogs/dialog_bom_cfg_keywords.cpp
107+ dialogs/dialog_bus_manager.cpp
108 dialogs/dialog_fields_editor_global.cpp
109 dialogs/dialog_fields_editor_global_base.cpp
110 dialogs/dialog_choose_component.cpp
111@@ -64,6 +65,8 @@ set( EESCHEMA_DLGS
112 dialogs/dialog_lib_edit_text_base.cpp
113 dialogs/dialog_lib_new_component.cpp
114 dialogs/dialog_lib_new_component_base.cpp
115+ dialogs/dialog_migrate_buses.cpp
116+ dialogs/dialog_migrate_buses_base.cpp
117 dialogs/dialog_netlist.cpp
118 dialogs/dialog_netlist_base.cpp
119 dialogs/dialog_plot_schematic.cpp
120@@ -136,6 +139,7 @@ set( EESCHEMA_SRCS
121 autoplace_fields.cpp
122 backanno.cpp
123 block.cpp
124+ bus_alias.cpp
125 bus-wire-junction.cpp
126 busentry.cpp
127 class_libentry.cpp
128@@ -144,6 +148,7 @@ set( EESCHEMA_SRCS
129 cmp_library_lexer.cpp
130 component_references_lister.cpp
131 controle.cpp
132+ connection_graph.cpp
133 cross-probing.cpp
134 drc_erc_item.cpp
135 edit_bitmap.cpp
136@@ -195,6 +200,7 @@ set( EESCHEMA_SRCS
137 sch_bus_entry.cpp
138 sch_collectors.cpp
139 sch_component.cpp
140+ sch_connection.cpp
141 sch_eagle_plugin.cpp
142 sch_field.cpp
143 sch_io_mgr.cpp
144@@ -204,6 +210,7 @@ set( EESCHEMA_SRCS
145 sch_line.cpp
146 sch_marker.cpp
147 sch_no_connect.cpp
148+ sch_pin_connection.cpp
149 sch_plugin.cpp
150 sch_preview_panel.cpp
151 sch_screen.cpp
152diff --git a/eeschema/annotate.cpp b/eeschema/annotate.cpp
153index af92eab..353f40f 100644
154--- a/eeschema/annotate.cpp
155+++ b/eeschema/annotate.cpp
156@@ -62,7 +62,7 @@ void SCH_EDIT_FRAME::DeleteAnnotation( bool aCurrentSheetOnly )
157 {
158 SCH_SCREEN* screen = GetScreen();
159 wxCHECK_RET( screen != NULL, wxT( "Attempt to clear annotation of a NULL screen." ) );
160- screen->ClearAnnotation( m_CurrentSheet );
161+ screen->ClearAnnotation( g_CurrentSheet );
162 }
163 else
164 {
165@@ -71,7 +71,7 @@ void SCH_EDIT_FRAME::DeleteAnnotation( bool aCurrentSheetOnly )
166 }
167
168 // Update the references for the sheet that is currently being displayed.
169- m_CurrentSheet->UpdateAllScreenReferences();
170+ g_CurrentSheet->UpdateAllScreenReferences();
171
172 SyncView();
173 GetCanvas()->Refresh();
174@@ -125,7 +125,7 @@ void SCH_EDIT_FRAME::AnnotateComponents( bool aAnnotateSchematic,
175 }
176 else
177 {
178- m_CurrentSheet->GetMultiUnitComponents( lockedComponents );
179+ g_CurrentSheet->GetMultiUnitComponents( lockedComponents );
180 }
181 }
182
183@@ -146,7 +146,7 @@ void SCH_EDIT_FRAME::AnnotateComponents( bool aAnnotateSchematic,
184 }
185 else
186 {
187- m_CurrentSheet->GetComponents( references );
188+ g_CurrentSheet->GetComponents( references );
189 }
190
191 // Break full components reference in name (prefix) and number:
192@@ -233,7 +233,7 @@ void SCH_EDIT_FRAME::AnnotateComponents( bool aAnnotateSchematic,
193 aReporter.ReportTail( _( "Annotation complete." ), REPORTER::RPT_ACTION );
194
195 // Update on screen references, that can be modified by previous calculations:
196- m_CurrentSheet->UpdateAllScreenReferences();
197+ g_CurrentSheet->UpdateAllScreenReferences();
198 SetSheetNumberAndCount();
199
200 SyncView();
201@@ -252,7 +252,7 @@ int SCH_EDIT_FRAME::CheckAnnotate( REPORTER& aReporter, bool aOneSheetOnly )
202 if( !aOneSheetOnly )
203 sheetList.GetComponents( componentsList );
204 else
205- m_CurrentSheet->GetComponents( componentsList );
206+ g_CurrentSheet->GetComponents( componentsList );
207
208 return componentsList.CheckAnnotation( aReporter );
209 }
210diff --git a/eeschema/block.cpp b/eeschema/block.cpp
211index 9a56299..e28c792 100644
212--- a/eeschema/block.cpp
213+++ b/eeschema/block.cpp
214@@ -455,7 +455,7 @@ void SCH_EDIT_FRAME::PasteListOfItems( wxDC* DC )
215 return;
216 }
217
218- wxFileName destFn = m_CurrentSheet->Last()->GetFileName();
219+ wxFileName destFn = g_CurrentSheet->Last()->GetFileName();
220
221 if( destFn.IsRelative() )
222 destFn.MakeAbsolute( Prj().GetProjectPath() );
223diff --git a/eeschema/bus-wire-junction.cpp b/eeschema/bus-wire-junction.cpp
224index 3d0fb0d..fc43730 100644
225--- a/eeschema/bus-wire-junction.cpp
226+++ b/eeschema/bus-wire-junction.cpp
227@@ -42,6 +42,7 @@
228 #include <sch_text.h>
229 #include <sch_component.h>
230 #include <sch_sheet.h>
231+#include <list_operations.h>
232 #include <sch_view.h>
233 #include <view/view_group.h>
234
235@@ -53,7 +54,6 @@ static void ComputeBreakPoint( SCH_SCREEN* aScreen, SCH_LINE* aSegment, wxPoint&
236 static DLIST< SCH_ITEM > s_wires; // when creating a new set of wires,
237 // stores here the new wires.
238
239-
240 /**
241 * In a contiguous list of wires, remove wires that backtrack over the previous
242 * wire. Example:
243@@ -130,15 +130,49 @@ static void DrawSegment( EDA_DRAW_PANEL* aPanel, wxDC* aDC, const wxPoint& aPosi
244
245 wxPoint endpos = frame->GetCrossHairPosition();
246
247+ auto view = static_cast<SCH_DRAW_PANEL*>( aPanel )->GetView();
248+ view->ClearPreview();
249+
250+ // Update the bus unfold posture based on the mouse movement
251+ if( frame->m_busUnfold.in_progress && !frame->m_busUnfold.label_placed )
252+ {
253+ auto cursor_delta = frame->GetCursorPosition( false ) - frame->m_busUnfold.origin;
254+ auto entry = frame->m_busUnfold.entry;
255+
256+ bool offset = ( cursor_delta.x < 0 );
257+ char shape = ( offset ? ( ( cursor_delta.y >= 0 ) ? '/' : '\\' )
258+ : ( ( cursor_delta.y >= 0 ) ? '\\' : '/' ) );
259+
260+ // Erase and redraw if necessary
261+ if( shape != entry->GetBusEntryShape() ||
262+ offset != frame->m_busUnfold.offset )
263+ {
264+ entry->SetBusEntryShape( shape );
265+ wxPoint entry_pos = frame->m_busUnfold.origin;
266+
267+ if( offset )
268+ entry_pos -= entry->GetSize();
269+
270+ entry->SetPosition( entry_pos );
271+ frame->m_busUnfold.offset = offset;
272+
273+ frame->RefreshItem( entry );
274+
275+ wxPoint wire_start = ( offset ? entry->GetPosition() : entry->m_End() );
276+ ( (SCH_LINE*) s_wires.begin() )->SetStartPoint( wire_start );
277+ }
278+
279+ // Update the label "ghost" position
280+ auto label = frame->m_busUnfold.label;
281+ label->SetPosition( endpos );
282+ view->AddToPreview( label->Clone() );
283+ }
284+
285 if( frame->GetForceHVLines() ) /* Coerce the line to vertical or horizontal one: */
286 ComputeBreakPoint( frame->GetScreen(), (SCH_LINE*) s_wires.GetLast()->Back(), endpos );
287 else
288 ( (SCH_LINE*) s_wires.GetLast() )->SetEndPoint( endpos );
289
290- auto view = static_cast<SCH_DRAW_PANEL*>( aPanel )->GetView();
291-
292- view->ClearPreview();
293-
294 for( SCH_LINE* segment = (SCH_LINE*) s_wires.begin(); segment; segment = segment->Next() )
295 {
296 if( !segment->IsNull() ) // Add to preview if segment length != 0
297@@ -206,6 +240,30 @@ void SCH_EDIT_FRAME::BeginSegment( int type )
298 }
299 else // A segment is in progress: terminates the current segment and add a new segment.
300 {
301+ // Place the label for bus unfolding if needed
302+ if( IsBusUnfoldInProgress() && !m_busUnfold.label_placed )
303+ {
304+ wxASSERT( type == LAYER_WIRE );
305+
306+ AddToScreen( m_busUnfold.label );
307+ m_busUnfold.label_placed = true;
308+
309+ nextSegment = new SCH_LINE( cursorpos, LAYER_WIRE );
310+
311+ segment->ClearFlags( IS_NEW );
312+ segment->SetFlags( SELECTED );
313+
314+ nextSegment->SetStartPoint( cursorpos );
315+ nextSegment->SetFlags( IS_NEW );
316+
317+ s_wires.PushBack( nextSegment );
318+ GetScreen()->SetCurItem( nextSegment );
319+
320+ m_canvas->SetMouseCapture( DrawSegment, AbortCreateNewLine );
321+ SetRepeatItem( NULL );
322+ return;
323+ }
324+
325 SCH_LINE* prevSegment = segment->Back();
326
327 // Be aware prevSegment can be null when the horizontal and vertical lines only switch
328@@ -230,7 +288,8 @@ void SCH_EDIT_FRAME::BeginSegment( int type )
329 m_canvas->CallMouseCapture( nullptr, wxDefaultPosition, false );
330
331 // Terminate the command if the end point is on a pin, junction, or another wire or bus.
332- if( GetScreen()->IsTerminalPoint( cursorpos, segment->GetLayer() ) )
333+ if( !IsBusUnfoldInProgress() &&
334+ GetScreen()->IsTerminalPoint( cursorpos, segment->GetLayer() ) )
335 {
336 EndSegment();
337 return;
338@@ -328,6 +387,18 @@ void SCH_EDIT_FRAME::EndSegment()
339 itemList.PushItem( ITEM_PICKER( wire, UR_NEW ) );
340 }
341
342+ if( IsBusUnfoldInProgress() && m_busUnfold.label_placed )
343+ {
344+ wxASSERT( m_busUnfold.entry && m_busUnfold.label );
345+
346+ PICKED_ITEMS_LIST bus_items;
347+
348+ bus_items.PushItem( ITEM_PICKER( m_busUnfold.entry, UR_NEW ) );
349+ bus_items.PushItem( ITEM_PICKER( m_busUnfold.label, UR_NEW ) );
350+
351+ SaveCopyInUndoList( bus_items, UR_NEW, false );
352+ }
353+
354 // Get the last non-null wire (this is the last created segment).
355 SetRepeatItem( segment = (SCH_LINE*) s_wires.GetLast() );
356
357@@ -363,10 +434,16 @@ void SCH_EDIT_FRAME::EndSegment()
358 for( auto i : new_ends )
359 {
360 if( screen->IsJunctionNeeded( i, true ) )
361- AddJunction( i, true );
362+ AddJunction( i, true, false );
363+ }
364+
365+ if( IsBusUnfoldInProgress() )
366+ {
367+ FinishBusUnfold();
368 }
369
370 TestDanglingEnds();
371+
372 screen->ClearDrawingState();
373 screen->SetCurItem( NULL );
374 m_canvas->EndMouseCapture( -1, -1, wxEmptyString, false );
375@@ -556,6 +633,7 @@ bool SCH_EDIT_FRAME::TrimWire( const wxPoint& aStart, const wxPoint& aEnd, bool
376
377 SaveCopyInUndoList( (SCH_ITEM*)line, UR_DELETED, aAppend );
378 RemoveFromScreen( (SCH_ITEM*)line );
379+
380 aAppend = true;
381 retval = true;
382 }
383@@ -564,12 +642,14 @@ bool SCH_EDIT_FRAME::TrimWire( const wxPoint& aStart, const wxPoint& aEnd, bool
384 }
385
386
387-bool SCH_EDIT_FRAME::SchematicCleanUp( bool aAppend )
388+bool SCH_EDIT_FRAME::SchematicCleanUp( bool aUndo, SCH_SCREEN* aScreen )
389 {
390 SCH_ITEM* item = NULL;
391 SCH_ITEM* secondItem = NULL;
392 PICKED_ITEMS_LIST itemList;
393- SCH_SCREEN* screen = GetScreen();
394+
395+ if( aScreen == nullptr )
396+ aScreen = GetScreen();
397
398 auto remove_item = [ &itemList ]( SCH_ITEM* aItem ) -> void
399 {
400@@ -577,9 +657,9 @@ bool SCH_EDIT_FRAME::SchematicCleanUp( bool aAppend )
401 itemList.PushItem( ITEM_PICKER( aItem, UR_DELETED ) );
402 };
403
404- BreakSegmentsOnJunctions( true );
405+ BreakSegmentsOnJunctions( true, aScreen );
406
407- for( item = screen->GetDrawItems(); item; item = item->Next() )
408+ for( item = aScreen->GetDrawItems(); item; item = item->Next() )
409 {
410 if( ( item->Type() != SCH_LINE_T )
411 && ( item->Type() != SCH_JUNCTION_T )
412@@ -591,7 +671,7 @@ bool SCH_EDIT_FRAME::SchematicCleanUp( bool aAppend )
413
414 // Remove unneeded junctions
415 if( ( item->Type() == SCH_JUNCTION_T )
416- && ( !screen->IsJunctionNeeded( item->GetPosition() ) ) )
417+ && ( !aScreen->IsJunctionNeeded( item->GetPosition() ) ) )
418 {
419 remove_item( item );
420 continue;
421@@ -634,16 +714,16 @@ bool SCH_EDIT_FRAME::SchematicCleanUp( bool aAppend )
422
423 // If the end points overlap, check if we still need the junction
424 if( secondLine->IsEndPoint( firstLine->GetStartPoint() ) )
425- needed = screen->IsJunctionNeeded( firstLine->GetStartPoint() );
426+ needed = aScreen->IsJunctionNeeded( firstLine->GetStartPoint() );
427 else if( secondLine->IsEndPoint( firstLine->GetEndPoint() ) )
428- needed = screen->IsJunctionNeeded( firstLine->GetEndPoint() );
429+ needed = aScreen->IsJunctionNeeded( firstLine->GetEndPoint() );
430
431 if( !needed && ( line = (SCH_LINE*) secondLine->MergeOverlap( firstLine ) ) )
432 {
433 remove_item( item );
434 remove_item( secondItem );
435 itemList.PushItem( ITEM_PICKER( line, UR_NEW ) );
436- AddToScreen( line );
437+ AddToScreen( line, aScreen );
438 break;
439 }
440 }
441@@ -653,33 +733,81 @@ bool SCH_EDIT_FRAME::SchematicCleanUp( bool aAppend )
442 }
443 }
444
445- for( item = screen->GetDrawItems(); item; item = secondItem )
446+ for( item = aScreen->GetDrawItems(); item; item = secondItem )
447 {
448 secondItem = item->Next();
449
450 if( item->GetFlags() & STRUCT_DELETED )
451- RemoveFromScreen( item );
452+ RemoveFromScreen( item, aScreen );
453 }
454
455- SaveCopyInUndoList( itemList, UR_CHANGED, aAppend );
456+ if( itemList.GetCount() && aUndo )
457+ SaveCopyInUndoList( itemList, UR_DELETED, true );
458
459 return itemList.GetCount() > 0;
460 }
461
462
463-bool SCH_EDIT_FRAME::BreakSegment( SCH_LINE* aSegment, const wxPoint& aPoint, bool aAppend,
464- SCH_LINE** aNewSegment )
465+bool SCH_EDIT_FRAME::AddMissingJunctions( SCH_SCREEN* aScreen )
466+{
467+ bool added = false;
468+
469+ auto add_junction = [ & ]( const wxPoint& aPosition ) -> void
470+ {
471+ auto junction = new SCH_JUNCTION( aPosition );
472+ AddToScreen( junction, aScreen );
473+ BreakSegments( aPosition, false );
474+ added = true;
475+ };
476+
477+ for( auto item = aScreen->GetDrawItems(); item; item = item->Next() )
478+ {
479+ if( item->Type() == SCH_LINE_T )
480+ {
481+ auto line = static_cast<SCH_LINE*>( item );
482+
483+ if( aScreen->IsJunctionNeeded( line->GetStartPoint(), true ) )
484+ add_junction( line->GetStartPoint() );
485+
486+ if( aScreen->IsJunctionNeeded( line->GetEndPoint(), true ) )
487+ add_junction( line->GetEndPoint() );
488+ }
489+ }
490+
491+ return added;
492+}
493+
494+
495+void SCH_EDIT_FRAME::NormalizeSchematicOnFirstLoad()
496+{
497+ BreakSegmentsOnJunctions();
498+ SchematicCleanUp();
499+
500+ SCH_SHEET_LIST list( g_RootSheet );
501+
502+ for( const auto& sheet : list )
503+ AddMissingJunctions( sheet.LastScreen() );
504+}
505+
506+
507+bool SCH_EDIT_FRAME::BreakSegment( SCH_LINE* aSegment, const wxPoint& aPoint,
508+ bool aAppend, SCH_LINE** aNewSegment,
509+ SCH_SCREEN* aScreen )
510 {
511 if( !IsPointOnSegment( aSegment->GetStartPoint(), aSegment->GetEndPoint(), aPoint )
512 || aSegment->IsEndPoint( aPoint ) )
513 return false;
514
515- SaveCopyInUndoList( aSegment, UR_CHANGED, aAppend );
516+ if( aScreen == nullptr )
517+ aScreen = GetScreen();
518+
519 SCH_LINE* newSegment = new SCH_LINE( *aSegment );
520- SaveCopyInUndoList( newSegment, UR_NEW, true );
521
522 newSegment->SetStartPoint( aPoint );
523- AddToScreen( newSegment );
524+ AddToScreen( newSegment, aScreen );
525+
526+ if( aAppend )
527+ SaveCopyInUndoList( newSegment, UR_NEW, true );
528
529 RefreshItem( aSegment );
530 aSegment->SetEndPoint( aPoint );
531@@ -691,33 +819,41 @@ bool SCH_EDIT_FRAME::BreakSegment( SCH_LINE* aSegment, const wxPoint& aPoint, bo
532 }
533
534
535-bool SCH_EDIT_FRAME::BreakSegments( const wxPoint& aPoint, bool aAppend )
536+bool SCH_EDIT_FRAME::BreakSegments( const wxPoint& aPoint, bool aAppend,
537+ SCH_SCREEN* aScreen )
538 {
539+ if( aScreen == nullptr )
540+ aScreen = GetScreen();
541+
542 bool brokenSegments = false;
543
544- for( SCH_ITEM* segment = GetScreen()->GetDrawItems(); segment; segment = segment->Next() )
545+ for( SCH_ITEM* segment = aScreen->GetDrawItems(); segment; segment = segment->Next() )
546 {
547 if( ( segment->Type() != SCH_LINE_T ) || ( segment->GetLayer() == LAYER_NOTES ) )
548 continue;
549
550- brokenSegments |= BreakSegment( (SCH_LINE*) segment, aPoint, aAppend || brokenSegments );
551+ brokenSegments |= BreakSegment( (SCH_LINE*) segment, aPoint,
552+ aAppend || brokenSegments, NULL, aScreen );
553 }
554
555 return brokenSegments;
556 }
557
558
559-bool SCH_EDIT_FRAME::BreakSegmentsOnJunctions( bool aAppend )
560+bool SCH_EDIT_FRAME::BreakSegmentsOnJunctions( bool aAppend, SCH_SCREEN* aScreen )
561 {
562+ if( aScreen == nullptr )
563+ aScreen = GetScreen();
564+
565 bool brokenSegments = false;
566
567- for( SCH_ITEM* item = GetScreen()->GetDrawItems(); item; item = item->Next() )
568+ for( SCH_ITEM* item = aScreen->GetDrawItems(); item; item = item->Next() )
569 {
570 if( item->Type() == SCH_JUNCTION_T )
571 {
572 SCH_JUNCTION* junction = ( SCH_JUNCTION* ) item;
573
574- if( BreakSegments( junction->GetPosition(), brokenSegments || aAppend ) )
575+ if( BreakSegments( junction->GetPosition(), brokenSegments || aAppend, aScreen ) )
576 brokenSegments = true;
577 }
578 else
579@@ -725,8 +861,8 @@ bool SCH_EDIT_FRAME::BreakSegmentsOnJunctions( bool aAppend )
580 SCH_BUS_ENTRY_BASE* busEntry = dynamic_cast<SCH_BUS_ENTRY_BASE*>( item );
581 if( busEntry )
582 {
583- if( BreakSegments( busEntry->GetPosition(), brokenSegments || aAppend )
584- || BreakSegments( busEntry->m_End(), brokenSegments || aAppend ) )
585+ if( BreakSegments( busEntry->GetPosition(), brokenSegments || aAppend, aScreen )
586+ || BreakSegments( busEntry->m_End(), brokenSegments || aAppend, aScreen ) )
587 brokenSegments = true;
588 }
589 }
590@@ -801,21 +937,26 @@ void SCH_EDIT_FRAME::DeleteJunction( SCH_ITEM* aJunction, bool aAppend )
591 }
592
593
594-SCH_JUNCTION* SCH_EDIT_FRAME::AddJunction( const wxPoint& aPosition, bool aAppend )
595+SCH_JUNCTION* SCH_EDIT_FRAME::AddJunction( const wxPoint& aPosition,
596+ bool aAppend, bool aFinal )
597 {
598 SCH_JUNCTION* junction = new SCH_JUNCTION( aPosition );
599 bool broken_segments = false;
600
601 AddToScreen( junction );
602 broken_segments = BreakSegments( aPosition, aAppend );
603- TestDanglingEnds();
604- OnModify();
605 SaveCopyInUndoList( junction, UR_NEW, broken_segments || aAppend );
606
607- auto view = GetCanvas()->GetView();
608- view->ClearPreview();
609- view->ShowPreview( false );
610- view->ClearHiddenFlags();
611+ if( aFinal )
612+ {
613+ TestDanglingEnds();
614+ OnModify();
615+
616+ auto view = GetCanvas()->GetView();
617+ view->ClearPreview();
618+ view->ShowPreview( false );
619+ view->ClearHiddenFlags();
620+ }
621
622 return junction;
623 }
624@@ -846,6 +987,7 @@ SCH_NO_CONNECT* SCH_EDIT_FRAME::AddNoConnect( const wxPoint& aPosition )
625 static void AbortCreateNewLine( EDA_DRAW_PANEL* aPanel, wxDC* aDC )
626 {
627 SCH_SCREEN* screen = (SCH_SCREEN*) aPanel->GetScreen();
628+
629 SCH_EDIT_FRAME* parent = ( SCH_EDIT_FRAME* ) aPanel->GetParent();
630
631 if( screen->GetCurItem() )
632@@ -858,6 +1000,11 @@ static void AbortCreateNewLine( EDA_DRAW_PANEL* aPanel, wxDC* aDC )
633 parent->SetRepeatItem( NULL );
634 }
635
636+ if( parent->IsBusUnfoldInProgress() )
637+ {
638+ parent->CancelBusUnfold();
639+ }
640+
641 auto view = static_cast<SCH_DRAW_PANEL*>(aPanel)->GetView();
642 view->ClearPreview();
643 view->ShowPreview( false );
644diff --git a/eeschema/bus_alias.cpp b/eeschema/bus_alias.cpp
645new file mode 100644
646index 0000000..b7eb0f5
647--- /dev/null
648+++ b/eeschema/bus_alias.cpp
649@@ -0,0 +1,41 @@
650+/*
651+ * This program source code file is part of KiCad, a free EDA CAD application.
652+ *
653+ * Copyright (C) 2018 CERN
654+ * @author Jon Evans <jon@craftyjon.com>
655+ *
656+ * This program is free software; you can redistribute it and/or
657+ * modify it under the terms of the GNU General Public License
658+ * as published by the Free Software Foundation; either version 2
659+ * of the License, or (at your option) any later version.
660+ *
661+ * This program is distributed in the hope that it will be useful,
662+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
663+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
664+ * GNU General Public License for more details.
665+ *
666+ * You should have received a copy of the GNU General Public License along
667+ * with this program. If not, see <http://www.gnu.org/licenses/>.
668+ */
669+
670+#include <algorithm>
671+
672+#include "bus_alias.h"
673+
674+
675+BUS_ALIAS::BUS_ALIAS( SCH_SCREEN* aParent ) :
676+ m_parent( aParent )
677+{
678+}
679+
680+
681+BUS_ALIAS::~BUS_ALIAS()
682+{
683+}
684+
685+
686+bool BUS_ALIAS::Contains( const wxString& aName )
687+{
688+ return ( std::find( m_members.begin(), m_members.end(), aName )
689+ != m_members.end() );
690+}
691diff --git a/eeschema/bus_alias.h b/eeschema/bus_alias.h
692new file mode 100644
693index 0000000..4307327
694--- /dev/null
695+++ b/eeschema/bus_alias.h
696@@ -0,0 +1,100 @@
697+/*
698+ * This program source code file is part of KiCad, a free EDA CAD application.
699+ *
700+ * Copyright (C) 2018 CERN
701+ * @author Jon Evans <jon@craftyjon.com>
702+ *
703+ * This program is free software; you can redistribute it and/or
704+ * modify it under the terms of the GNU General Public License
705+ * as published by the Free Software Foundation; either version 2
706+ * of the License, or (at your option) any later version.
707+ *
708+ * This program is distributed in the hope that it will be useful,
709+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
710+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
711+ * GNU General Public License for more details.
712+ *
713+ * You should have received a copy of the GNU General Public License along
714+ * with this program. If not, see <http://www.gnu.org/licenses/>.
715+ */
716+
717+#ifndef _BUS_ALIAS_H
718+#define _BUS_ALIAS_H
719+
720+#include <memory>
721+#include <vector>
722+#include <wx/string.h>
723+
724+
725+class SCH_SCREEN;
726+
727+
728+class BUS_ALIAS
729+{
730+public:
731+ BUS_ALIAS( SCH_SCREEN* aParent = NULL );
732+
733+ ~BUS_ALIAS();
734+
735+ std::shared_ptr< BUS_ALIAS > Clone() const
736+ {
737+ return std::make_shared< BUS_ALIAS >( *this );
738+ }
739+
740+ wxString GetName()
741+ {
742+ return m_name;
743+ }
744+
745+ void SetName( const wxString& aName )
746+ {
747+ m_name = aName;
748+ }
749+
750+ void ClearMembers()
751+ {
752+ m_members.clear();
753+ }
754+
755+ void AddMember( const wxString& aName )
756+ {
757+ m_members.push_back( aName );
758+ }
759+
760+ int GetMemberCount()
761+ {
762+ return m_members.size();
763+ }
764+
765+ std::vector< wxString >& Members()
766+ {
767+ return m_members;
768+ }
769+
770+ bool Contains( const wxString& aName );
771+
772+ SCH_SCREEN* GetParent()
773+ {
774+ return m_parent;
775+ }
776+
777+ void SetParent( SCH_SCREEN* aParent )
778+ {
779+ m_parent = aParent;
780+ }
781+
782+protected:
783+
784+ wxString m_name;
785+
786+ std::vector< wxString > m_members;
787+
788+ /**
789+ * The bus alias editor dialog can edit aliases from all open sheets.
790+ * This means we have to store a reference back to our parent so that
791+ * the dialog can update the parent if aliases are changed or removed.
792+ */
793+ SCH_SCREEN* m_parent;
794+};
795+
796+#endif
797diff --git a/eeschema/connection_graph.cpp b/eeschema/connection_graph.cpp
798new file mode 100644
799index 0000000..578f218
800--- /dev/null
801+++ b/eeschema/connection_graph.cpp
802@@ -0,0 +1,1912 @@
803+/*
804+ * This program source code file is part of KiCad, a free EDA CAD application.
805+ *
806+ * Copyright (C) 2018 CERN
807+ * @author Jon Evans <jon@craftyjon.com>
808+ *
809+ * This program is free software; you can redistribute it and/or
810+ * modify it under the terms of the GNU General Public License
811+ * as published by the Free Software Foundation; either version 2
812+ * of the License, or (at your option) any later version.
813+ *
814+ * This program is distributed in the hope that it will be useful,
815+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
816+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
817+ * GNU General Public License for more details.
818+ *
819+ * You should have received a copy of the GNU General Public License along
820+ * with this program. If not, see <http://www.gnu.org/licenses/>.
821+ */
822+
823+#include <list>
824+#include <thread>
825+#include <unordered_map>
826+#include <profile.h>
827+
828+#include <common.h>
829+#include <erc.h>
830+#include <sch_edit_frame.h>
831+#include <sch_bus_entry.h>
832+#include <sch_component.h>
833+#include <sch_line.h>
834+#include <sch_pin_connection.h>
835+#include <sch_screen.h>
836+#include <sch_sheet.h>
837+#include <sch_sheet_path.h>
838+#include <sch_text.h>
839+
840+#include <connection_graph.h>
841+
842+using std::map;
843+using std::unordered_map;
844+using std::unordered_set;
845+using std::vector;
846+
847+
848+bool CONNECTION_SUBGRAPH::ResolveDrivers( bool aCreateMarkers )
849+{
850+ int highest_priority = -1;
851+ vector<SCH_ITEM*> candidates;
852+
853+ m_driver = nullptr;
854+
855+ // Hierarchical labels are lower priority than local labels here,
856+ // because on the first pass we want local labels to drive subgraphs
857+ // so that we can identify same-sheet neighbors and link them together.
858+ // Hierarchical labels will end up overriding the final net name if
859+ // a higher-level sheet has a different name during the hierarchical
860+ // pass.
861+
862+ for( auto item : m_drivers )
863+ {
864+ int item_priority = 0;
865+
866+ switch( item->Type() )
867+ {
868+ case SCH_SHEET_PIN_T: item_priority = 2; break;
869+ case SCH_HIERARCHICAL_LABEL_T: item_priority = 3; break;
870+ case SCH_LABEL_T: item_priority = 4; break;
871+ case SCH_PIN_CONNECTION_T:
872+ {
873+ auto pin_connection = static_cast<SCH_PIN_CONNECTION*>( item );
874+
875+ if( pin_connection->m_pin->IsPowerConnection() )
876+ item_priority = 5;
877+ else
878+ item_priority = 1;
879+
880+ // Skip power flags, etc
881+ if( item_priority == 1 && !pin_connection->m_comp->IsInNetlist() )
882+ continue;
883+
884+ break;
885+ }
886+ case SCH_GLOBAL_LABEL_T: item_priority = 6; break;
887+ default: break;
888+ }
889+
890+ if( item_priority > highest_priority )
891+ {
892+ candidates.clear();
893+ candidates.push_back( item );
894+ highest_priority = item_priority;
895+ }
896+ else if( candidates.size() && ( item_priority == highest_priority ) )
897+ {
898+ candidates.push_back( item );
899+ }
900+ }
901+
902+ if( highest_priority >= 4 )
903+ m_strong_driver = true;
904+
905+ if( candidates.size() )
906+ {
907+ if( candidates.size() > 1 )
908+ {
909+ if( highest_priority == 1 || highest_priority == 5 )
910+ {
911+ // We have multiple options and they are all component pins.
912+ std::sort( candidates.begin(), candidates.end(),
913+ [this]( SCH_ITEM* a, SCH_ITEM* b) -> bool
914+ {
915+ auto pin_a = static_cast<SCH_PIN_CONNECTION*>( a );
916+ auto pin_b = static_cast<SCH_PIN_CONNECTION*>( b );
917+
918+ auto name_a = pin_a->GetDefaultNetName( m_sheet );
919+ auto name_b = pin_b->GetDefaultNetName( m_sheet );
920+
921+ return name_a < name_b;
922+ } );
923+ }
924+
925+ if( highest_priority == 2 )
926+ {
927+ // We have multiple options, and they are all hierarchical
928+ // sheet pins. Let's prefer outputs over inputs.
929+
930+ for( auto c : candidates )
931+ {
932+ auto p = static_cast<SCH_SHEET_PIN*>( c );
933+
934+ if( p->GetShape() == NET_OUTPUT )
935+ {
936+ m_driver = c;
937+ break;
938+ }
939+ }
940+ }
941+ }
942+
943+ if( !m_driver )
944+ m_driver = candidates[0];
945+ }
946+
947+ // For power connections, we allow multiple drivers
948+ if( highest_priority == 5 && candidates.size() > 1 &&
949+ candidates[0]->Type() == SCH_PIN_CONNECTION_T )
950+ {
951+ auto pc = static_cast<SCH_PIN_CONNECTION*>( candidates[0] );
952+
953+ wxASSERT( pc->m_pin->IsPowerConnection() );
954+
955+ m_multiple_power_ports = true;
956+ }
957+
958+ if( aCreateMarkers && !m_multiple_power_ports &&
959+ candidates.size() > 1 && highest_priority > 1 )
960+ {
961+ // First check if all the candidates are actually the same
962+ bool same = true;
963+ auto first = candidates[0]->Connection( m_sheet )->Name();
964+
965+ for( unsigned i = 1; i < candidates.size(); i++ )
966+ {
967+ if( candidates[i]->Connection( m_sheet )->Name() != first )
968+ {
969+ same = false;
970+ break;
971+ }
972+ }
973+
974+ if( !same )
975+ {
976+ wxString msg;
977+ msg.Printf( _( "%s and %s are both attached to the same wires. "
978+ "%s was picked as the label to use for netlisting." ),
979+ candidates[0]->GetSelectMenuText( m_frame->GetUserUnits() ),
980+ candidates[1]->GetSelectMenuText( m_frame->GetUserUnits() ),
981+ candidates[0]->Connection( m_sheet )->Name() );
982+
983+ wxASSERT( candidates[0] != candidates[1] );
984+
985+ auto marker = new SCH_MARKER();
986+ marker->SetTimeStamp( GetNewTimeStamp() );
987+ marker->SetMarkerType( MARKER_BASE::MARKER_ERC );
988+ marker->SetErrorLevel( MARKER_BASE::MARKER_SEVERITY_WARNING );
989+ marker->SetData( ERCE_DRIVER_CONFLICT,
990+ candidates[0]->GetPosition(), msg,
991+ candidates[1]->GetPosition() );
992+
993+ m_sheet.LastScreen()->Append( marker );
994+
995+ // If aCreateMarkers is true, then this is part of ERC check, so we
996+ // should return false even if the driver was assigned
997+ return false;
998+ }
999+ }
1000+
1001+ return aCreateMarkers || ( m_driver != nullptr );
1002+}
1003+
1004+
1005+wxString CONNECTION_SUBGRAPH::GetNetName()
1006+{
1007+ if( !m_driver || m_dirty )
1008+ return "";
1009+
1010+ if( !m_driver->Connection( m_sheet ) )
1011+ {
1012+ #ifdef CONNECTIVITY_DEBUG
1013+ wxASSERT_MSG( false, "Tried to get the net name of an item with no connection" );
1014+ #endif
1015+
1016+ return "";
1017+ }
1018+
1019+ return m_driver->Connection( m_sheet )->Name();
1020+}
1021+
1022+
1023+std::vector<SCH_ITEM*> CONNECTION_SUBGRAPH::GetBusLabels()
1024+{
1025+ vector<SCH_ITEM*> labels;
1026+
1027+ for( auto item : m_drivers )
1028+ {
1029+ switch( item->Type() )
1030+ {
1031+ case SCH_LABEL_T:
1032+ case SCH_GLOBAL_LABEL_T:
1033+ {
1034+ auto label_conn = item->Connection( m_sheet );
1035+
1036+ // Only consider bus vectors
1037+ if( label_conn->Type() == CONNECTION_BUS )
1038+ labels.push_back( item );
1039+ }
1040+ default: break;
1041+ }
1042+ }
1043+
1044+ return labels;
1045+}
1046+
1047+
1048+void CONNECTION_GRAPH::Reset()
1049+{
1050+ for( auto sg : m_subgraphs )
1051+ delete sg;
1052+
1053+ m_items.clear();
1054+ m_subgraphs.clear();
1055+ m_invisible_power_pins.clear();
1056+ m_bus_alias_cache.clear();
1057+ m_net_name_to_code_map.clear();
1058+ m_bus_name_to_code_map.clear();
1059+ m_net_code_to_subgraphs_map.clear();
1060+ m_last_net_code = 1;
1061+ m_last_bus_code = 1;
1062+ m_last_subgraph_code = 1;
1063+}
1064+
1065+
1066+void CONNECTION_GRAPH::Recalculate( SCH_SHEET_LIST aSheetList, bool aUnconditional )
1067+{
1068+#ifdef CONNECTIVITY_PROFILE
1069+ PROF_COUNTER phase1;
1070+#endif
1071+
1072+ if( aUnconditional )
1073+ Reset();
1074+
1075+ for( const auto& sheet : aSheetList )
1076+ {
1077+ std::vector<SCH_ITEM*> items;
1078+
1079+ for( auto item = sheet.LastScreen()->GetDrawItems();
1080+ item; item = item->Next() )
1081+ {
1082+ if( item->IsConnectable() &&
1083+ ( aUnconditional || item->IsConnectivityDirty() ) )
1084+ {
1085+ items.push_back( item );
1086+ }
1087+ }
1088+
1089+ updateItemConnectivity( sheet, items );
1090+ }
1091+
1092+#ifdef CONNECTIVITY_PROFILE
1093+ phase1.Stop();
1094+ std::cout << "UpdateItemConnectivity() " << phase1.msecs() << " ms" << std::endl;
1095+ PROF_COUNTER tde;
1096+#endif
1097+
1098+ // IsDanglingStateChanged() also adds connected items for things like SCH_TEXT
1099+ SCH_SCREENS schematic;
1100+ schematic.TestDanglingEnds();
1101+
1102+#ifdef CONNECTIVITY_PROFILE
1103+ tde.Stop();
1104+ std::cout << "TestDanglingEnds() " << tde.msecs() << " ms" << std::endl;
1105+#endif
1106+
1107+ buildConnectionGraph();
1108+}
1109+
1110+
1111+void CONNECTION_GRAPH::updateItemConnectivity( SCH_SHEET_PATH aSheet,
1112+ vector<SCH_ITEM*> aItemList )
1113+{
1114+ unordered_map< wxPoint, vector<SCH_ITEM*> > connection_map;
1115+
1116+ for( auto item : aItemList )
1117+ {
1118+ vector< wxPoint > points;
1119+ item->GetConnectionPoints( points );
1120+ item->ConnectedItems().clear();
1121+
1122+ if( item->Type() == SCH_SHEET_T )
1123+ {
1124+ for( auto& pin : static_cast<SCH_SHEET*>( item )->GetPins() )
1125+ {
1126+ if( !pin.Connection( aSheet ) )
1127+ {
1128+ pin.InitializeConnection( aSheet );
1129+ }
1130+
1131+ pin.ConnectedItems().clear();
1132+ pin.Connection( aSheet )->Reset();
1133+
1134+ connection_map[ pin.GetTextPos() ].push_back( &pin );
1135+ m_items.insert( &pin );
1136+ }
1137+ }
1138+ else if( item->Type() == SCH_COMPONENT_T )
1139+ {
1140+ auto component = static_cast<SCH_COMPONENT*>( item );
1141+
1142+ component->UpdatePinConnections( aSheet );
1143+
1144+ for( auto it : component->PinConnections() )
1145+ {
1146+ auto pin_connection = it.second;
1147+
1148+ // TODO(JE) use cached location from m_Pins
1149+ auto pin_pos = pin_connection->m_pin->GetPosition();
1150+ auto pos = component->GetTransform().TransformCoordinate( pin_pos ) +
1151+ component->GetPosition();
1152+
1153+ // because calling the first time is not thread-safe
1154+ pin_connection->GetDefaultNetName( aSheet );
1155+ pin_connection->ConnectedItems().clear();
1156+
1157+ connection_map[ pos ].push_back( pin_connection );
1158+ m_items.insert( pin_connection );
1159+ }
1160+ }
1161+ else
1162+ {
1163+ m_items.insert( item );
1164+
1165+ if( !item->Connection( aSheet ) )
1166+ {
1167+ item->InitializeConnection( aSheet );
1168+ }
1169+
1170+ auto conn = item->Connection( aSheet );
1171+
1172+ conn->Reset();
1173+
1174+ // Set bus/net property here so that the propagation code uses it
1175+ switch( item->Type() )
1176+ {
1177+ case SCH_LINE_T:
1178+ conn->SetType( ( item->GetLayer() == LAYER_BUS ) ?
1179+ CONNECTION_BUS : CONNECTION_NET );
1180+ break;
1181+
1182+ case SCH_BUS_BUS_ENTRY_T:
1183+ conn->SetType( CONNECTION_BUS );
1184+ break;
1185+
1186+ case SCH_PIN_CONNECTION_T:
1187+ case SCH_BUS_WIRE_ENTRY_T:
1188+ conn->SetType( CONNECTION_NET );
1189+ break;
1190+
1191+ default:
1192+ break;
1193+ }
1194+
1195+ for( auto point : points )
1196+ {
1197+ connection_map[ point ].push_back( item );
1198+ }
1199+ }
1200+
1201+ item->SetConnectivityDirty( false );
1202+ }
1203+
1204+ for( auto it : connection_map )
1205+ {
1206+ auto connection_vec = it.second;
1207+ SCH_ITEM* junction = nullptr;
1208+
1209+ for( auto connected_item : connection_vec )
1210+ {
1211+ // Look for junctions. For points that have a junction, we want all
1212+ // items to connect to the junction but not to each other.
1213+
1214+ if( connected_item->Type() == SCH_JUNCTION_T )
1215+ {
1216+ junction = connected_item;
1217+ }
1218+
1219+ // Bus entries are special: they can have connection points in the
1220+ // middle of a wire segment, because the junction algo doesn't split
1221+ // the segment in two where you place a bus entry. This means that
1222+ // bus entries that don't land on the end of a line segment need to
1223+ // have "virtual" connection points to the segments they graphically
1224+ // touch.
1225+ if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
1226+ {
1227+ // If this location only has the connection point of the bus
1228+ // entry itself, this means that either the bus entry is not
1229+ // connected to anything graphically, or that it is connected to
1230+ // a segment at some point other than at one of the endpoints.
1231+ if( connection_vec.size() == 1 )
1232+ {
1233+ auto screen = aSheet.LastScreen();
1234+ auto bus = screen->GetBus( it.first );
1235+
1236+ if( bus )
1237+ {
1238+ auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
1239+ bus_entry->m_connected_bus_item = bus;
1240+ }
1241+ }
1242+ }
1243+
1244+ // Bus-to-bus entries are treated just like bus wires
1245+ if( connected_item->Type() == SCH_BUS_BUS_ENTRY_T )
1246+ {
1247+ if( connection_vec.size() < 2 )
1248+ {
1249+ auto screen = aSheet.LastScreen();
1250+ auto bus = screen->GetBus( it.first );
1251+
1252+ if( bus )
1253+ {
1254+ auto bus_entry = static_cast<SCH_BUS_BUS_ENTRY*>( connected_item );
1255+
1256+ if( it.first == bus_entry->GetPosition() )
1257+ bus_entry->m_connected_bus_items[0] = bus;
1258+ else
1259+ bus_entry->m_connected_bus_items[1] = bus;
1260+
1261+ bus_entry->ConnectedItems().insert( bus );
1262+ bus->ConnectedItems().insert( bus_entry );
1263+ }
1264+ }
1265+ }
1266+
1267+ for( auto test_item : connection_vec )
1268+ {
1269+ if( !junction && test_item->Type() == SCH_JUNCTION_T )
1270+ {
1271+ junction = test_item;
1272+ }
1273+
1274+ if( connected_item != test_item &&
1275+ connected_item != junction &&
1276+ connected_item->ConnectionPropagatesTo( test_item ) &&
1277+ test_item->ConnectionPropagatesTo( connected_item ) )
1278+ {
1279+ connected_item->ConnectedItems().insert( test_item );
1280+ test_item->ConnectedItems().insert( connected_item );
1281+ }
1282+
1283+ // Set up the link between the bus entry net and the bus
1284+ if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
1285+ {
1286+ if( test_item->Connection( aSheet )->IsBus() )
1287+ {
1288+ auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
1289+ bus_entry->m_connected_bus_item = test_item;
1290+ }
1291+ }
1292+ }
1293+ }
1294+ }
1295+}
1296+
1297+
1298+// TODO(JE) This won't give the same subgraph IDs (and eventually net/graph codes)
1299+// to the same subgraph necessarily if it runs over and over again on the same
1300+// sheet. We need:
1301+//
1302+// a) a cache of net/bus codes, like used before
1303+// b) to persist the CONNECTION_GRAPH globally so the cache is persistent,
1304+// c) some way of trying to avoid changing net names. so we should keep track
1305+// of the previous driver of a net, and if it comes down to choosing between
1306+// equally-prioritized drivers, choose the one that already exists as a driver
1307+// on some portion of the items.
1308+
1309+
1310+void CONNECTION_GRAPH::buildConnectionGraph()
1311+{
1312+#ifdef CONNECTIVITY_PROFILE
1313+ PROF_COUNTER phase2;
1314+#endif
1315+
1316+ // Recache all bus aliases for later use
1317+
1318+ SCH_SHEET_LIST all_sheets( g_RootSheet );
1319+
1320+ for( unsigned i = 0; i < all_sheets.size(); i++ )
1321+ {
1322+ for( auto alias : all_sheets[i].LastScreen()->GetBusAliases() )
1323+ {
1324+ m_bus_alias_cache[ alias->GetName() ] = alias;
1325+ }
1326+ }
1327+
1328+ // Build subgraphs from items (on a per-sheet basis)
1329+
1330+ for( auto item : m_items )
1331+ {
1332+ for( auto it : item->m_connection_map )
1333+ {
1334+ const auto sheet = it.first;
1335+ auto connection = it.second;
1336+
1337+ if( connection->SubgraphCode() == 0 )
1338+ {
1339+ auto subgraph = new CONNECTION_SUBGRAPH( m_frame );
1340+
1341+ subgraph->m_code = m_last_subgraph_code++;
1342+ subgraph->m_sheet = sheet;
1343+
1344+ subgraph->m_items.push_back( item );
1345+
1346+ if( connection->IsDriver() )
1347+ subgraph->m_drivers.push_back( item );
1348+
1349+ if( item->Type() == SCH_NO_CONNECT_T )
1350+ {
1351+ subgraph->m_no_connect = item;
1352+ }
1353+ else if( item->Type() == SCH_PIN_CONNECTION_T )
1354+ {
1355+ auto pc = static_cast<SCH_PIN_CONNECTION*>( item );
1356+
1357+ if( pc->m_pin->GetType() == PIN_NC )
1358+ subgraph->m_no_connect = item;
1359+
1360+ // Invisible power pins need to be post-processed later
1361+
1362+ if( pc->m_pin->IsPowerConnection() &&
1363+ !pc->m_pin->IsVisible() )
1364+ {
1365+ m_invisible_power_pins.push_back( pc );
1366+ }
1367+ }
1368+
1369+ connection->SetSubgraphCode( subgraph->m_code );
1370+
1371+ std::list<SCH_ITEM*> members( item->ConnectedItems().begin(),
1372+ item->ConnectedItems().end() );
1373+
1374+ for( auto connected_item : members )
1375+ {
1376+ if( !connected_item->Connection( sheet ) )
1377+ connected_item->InitializeConnection( sheet );
1378+
1379+ if( connected_item->Type() == SCH_NO_CONNECT_T )
1380+ subgraph->m_no_connect = connected_item;
1381+
1382+ auto connected_conn = connected_item->Connection( sheet );
1383+
1384+ wxASSERT( connected_conn );
1385+
1386+ if( connected_conn->SubgraphCode() == 0 )
1387+ {
1388+ connected_conn->SetSubgraphCode( subgraph->m_code );
1389+ subgraph->m_items.push_back( connected_item );
1390+
1391+ if( connected_conn->IsDriver() )
1392+ subgraph->m_drivers.push_back( connected_item );
1393+
1394+ members.insert( members.end(),
1395+ connected_item->ConnectedItems().begin(),
1396+ connected_item->ConnectedItems().end() );
1397+ }
1398+ }
1399+
1400+ subgraph->m_dirty = true;
1401+ m_subgraphs.push_back( subgraph );
1402+ }
1403+ }
1404+ }
1405+
1406+ /**
1407+ * TODO(JE)
1408+ *
1409+ * It would be good if net codes were preserved as much as possible when
1410+ * generating netlists, so that unnamed nets don't keep shifting around when
1411+ * you regenerate.
1412+ *
1413+ * Right now, we are clearing out the old connections up in
1414+ * UpdateItemConnectivity(), but that is useful information, so maybe we
1415+ * need to just set the dirty flag or something.
1416+ *
1417+ * That way, ResolveDrivers() can check what the driver of the subgraph was
1418+ * previously, and if it is in the situation of choosing between equal
1419+ * candidates for an auto-generated net name, pick the previous one.
1420+ *
1421+ * N.B. the old algorithm solves this by sorting the possible net names
1422+ * alphabetically, so as long as the same refdes components are involved,
1423+ * the net will be the same.
1424+ */
1425+
1426+ // Resolve drivers for subgraphs and propagate connectivity info
1427+
1428+ std::atomic<size_t> nextSubgraph( 0 );
1429+ std::atomic<size_t> threadsFinished( 0 );
1430+ size_t parallelThreadCount = std::max<size_t>( std::thread::hardware_concurrency(), 2 );
1431+
1432+ for( size_t ii = 0; ii < parallelThreadCount; ++ii )
1433+ {
1434+ auto t = std::thread( [&]()
1435+ {
1436+ for( size_t subgraphId = nextSubgraph.fetch_add( 1 );
1437+ subgraphId < static_cast<size_t>( m_subgraphs.size() );
1438+ subgraphId = nextSubgraph.fetch_add( 1 ) )
1439+ {
1440+ auto subgraph = m_subgraphs[subgraphId];
1441+
1442+ if( !subgraph->m_dirty )
1443+ continue;
1444+
1445+ if( !subgraph->ResolveDrivers() )
1446+ {
1447+ subgraph->m_dirty = false;
1448+ }
1449+ else
1450+ {
1451+ // Now the subgraph has only one driver
1452+ auto driver = subgraph->m_driver;
1453+ auto sheet = subgraph->m_sheet;
1454+ auto connection = driver->Connection( sheet );
1455+
1456+ // Cache the driving connection for later use
1457+ subgraph->m_driver_connection = connection;
1458+
1459+ // TODO(JE) This should live in SCH_CONNECTION probably
1460+ switch( driver->Type() )
1461+ {
1462+ case SCH_LABEL_T:
1463+ case SCH_GLOBAL_LABEL_T:
1464+ case SCH_HIERARCHICAL_LABEL_T:
1465+ {
1466+ auto text = static_cast<SCH_TEXT*>( driver );
1467+ connection->ConfigureFromLabel( text->GetText() );
1468+ break;
1469+ }
1470+ case SCH_SHEET_PIN_T:
1471+ {
1472+ auto pin = static_cast<SCH_SHEET_PIN*>( driver );
1473+ auto txt = pin->GetParent()->GetName() + "/" + pin->GetText();
1474+
1475+ connection->ConfigureFromLabel( txt );
1476+ break;
1477+ }
1478+ case SCH_PIN_CONNECTION_T:
1479+ {
1480+ auto pin = static_cast<SCH_PIN_CONNECTION*>( driver );
1481+ // NOTE(JE) GetDefaultNetName is not thread-safe.
1482+ connection->ConfigureFromLabel( pin->GetDefaultNetName( sheet ) );
1483+
1484+ break;
1485+ }
1486+ default:
1487+ #ifdef CONNECTIVITY_DEBUG
1488+ wxLogDebug( "Driver type unsupported: %s",
1489+ driver->GetSelectMenuText( MILLIMETRES ) );
1490+ #endif
1491+ break;
1492+ }
1493+
1494+ connection->SetDriver( driver );
1495+ connection->ClearDirty();
1496+
1497+ subgraph->m_dirty = false;
1498+ }
1499+ }
1500+
1501+ threadsFinished++;
1502+ } );
1503+
1504+ t.detach();
1505+ }
1506+
1507+ while( threadsFinished < parallelThreadCount )
1508+ std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
1509+
1510+ // Check for subgraphs with the same net name but only weak drivers.
1511+ // For example, two wires that are both connected to hierarchical
1512+ // sheet pins that happen to have the same name, but are not the same.
1513+
1514+ for( auto subgraph : m_subgraphs )
1515+ {
1516+ subgraph->m_dirty = true;
1517+
1518+ if( subgraph->m_strong_driver )
1519+ {
1520+ // Add strong drivers to the cache, for later checking against conflicts
1521+
1522+ auto driver = subgraph->m_driver;
1523+ auto conn = subgraph->m_driver_connection;
1524+ auto sheet = subgraph->m_sheet;
1525+ auto name = conn->Name( true );
1526+
1527+ switch( driver->Type() )
1528+ {
1529+ case SCH_LABEL_T:
1530+ case SCH_HIERARCHICAL_LABEL_T:
1531+ {
1532+ m_local_label_cache[std::make_pair( sheet, name )].push_back( subgraph );
1533+ break;
1534+ }
1535+ case SCH_GLOBAL_LABEL_T:
1536+ {
1537+ m_global_label_cache[name].push_back( subgraph );
1538+ break;
1539+ }
1540+ case SCH_PIN_CONNECTION_T:
1541+ {
1542+ auto pc = static_cast<SCH_PIN_CONNECTION*>( driver );
1543+ wxASSERT( pc->m_pin->IsPowerConnection() );
1544+ m_global_label_cache[name].push_back( subgraph );
1545+ break;
1546+ }
1547+ default:
1548+ #ifdef CONNECTIVITY_DEBUG
1549+ wxLogDebug( "Unexpected strong driver %s",
1550+ driver->GetSelectMenuText( MILLIMETRES ) );
1551+ #endif
1552+ break;
1553+ }
1554+ }
1555+ }
1556+
1557+ // Test subgraphs for net name conflicts against higher priority subgraphs
1558+ // Suffix is a global increment to make things simpler, that way if we have
1559+ // multiple instances of the same name that needs to get renamed, they will
1560+ // definitely get unique names. While this will potentially lead to some
1561+ // confusing net names, this is really a corner case and won't happen if
1562+ // users follow best practices to label their nets.
1563+ unsigned suffix = 1;
1564+
1565+ for( auto subgraph : m_subgraphs )
1566+ {
1567+ if( !subgraph->m_dirty )
1568+ continue;
1569+
1570+ subgraph->m_dirty = false;
1571+
1572+ if( !subgraph->m_driver || subgraph->m_strong_driver )
1573+ continue;
1574+
1575+ auto conn = subgraph->m_driver_connection;
1576+ auto name = conn->Name();
1577+
1578+ bool conflict = false;
1579+
1580+ // First check the caches
1581+ try
1582+ {
1583+ auto v = m_global_label_cache.at( name );
1584+ conflict = true;
1585+ }
1586+ catch( const std::out_of_range& oor )
1587+ {}
1588+
1589+ try
1590+ {
1591+ auto local_name = conn->Name( true );
1592+ auto v = m_local_label_cache.at( std::make_pair( subgraph->m_sheet,
1593+ local_name ) );
1594+ conflict = true;
1595+ }
1596+ catch( const std::out_of_range& oor )
1597+ {}
1598+
1599+ if( conflict )
1600+ {
1601+ auto new_name = wxString::Format( _( "%s%u" ), name, suffix );
1602+
1603+ #ifdef CONNECTIVITY_DEBUG
1604+ wxLogDebug( "Subgraph %ld default name %s conflicts with a label. Changing to %s.",
1605+ subgraph->m_code, name, new_name );
1606+ #endif
1607+
1608+ conn->SetSuffix( wxString::Format( _( "%u" ), suffix ) );
1609+ suffix++;
1610+ name = new_name;
1611+ }
1612+
1613+ for( auto candidate : m_subgraphs )
1614+ {
1615+ if( !candidate->m_dirty )
1616+ continue;
1617+
1618+ if( candidate == subgraph || !candidate->m_driver || candidate->m_strong_driver )
1619+ continue;
1620+
1621+ auto c_conn = candidate->m_driver_connection;
1622+ auto check_name = c_conn->Name();
1623+
1624+ if( check_name == name )
1625+ {
1626+ auto new_name = wxString::Format( _( "%s%u" ), name, suffix );
1627+
1628+ #ifdef CONNECTIVITY_DEBUG
1629+ wxLogDebug( "Subgraph %ld and %ld both have name %s. Changing %ld to %s.",
1630+ subgraph->m_code, candidate->m_code, name,
1631+ candidate->m_code, new_name );
1632+ #endif
1633+
1634+ c_conn->SetSuffix( wxString::Format( _( "%u" ), suffix ) );
1635+
1636+ candidate->m_dirty = false;
1637+ suffix++;
1638+ }
1639+ }
1640+ }
1641+
1642+ // Generate net codes
1643+
1644+ for( auto subgraph : m_subgraphs )
1645+ {
1646+ if( !subgraph->m_driver )
1647+ continue;
1648+
1649+ auto connection = subgraph->m_driver_connection;
1650+ int code;
1651+
1652+ auto name = subgraph->GetNetName();
1653+
1654+ if( connection->IsBus() )
1655+ {
1656+ try
1657+ {
1658+ code = m_bus_name_to_code_map.at( name );
1659+ }
1660+ catch( const std::out_of_range& oor )
1661+ {
1662+ code = m_last_bus_code++;
1663+ m_bus_name_to_code_map[ name ] = code;
1664+ }
1665+
1666+ connection->SetBusCode( code );
1667+ }
1668+ else
1669+ {
1670+ assignNewNetCode( *connection );
1671+ }
1672+
1673+ for( auto item : subgraph->m_items )
1674+ {
1675+ auto item_conn = item->Connection( subgraph->m_sheet );
1676+
1677+ if( ( connection->IsBus() && item_conn->IsNet() ) ||
1678+ ( connection->IsNet() && item_conn->IsBus() ) )
1679+ {
1680+ continue;
1681+ }
1682+
1683+ if( item != subgraph->m_driver )
1684+ {
1685+ item_conn->Clone( *connection );
1686+ item_conn->ClearDirty();
1687+ }
1688+ }
1689+
1690+ // Reset the flag for the next loop below
1691+ subgraph->m_dirty = true;
1692+
1693+ auto sheet = subgraph->m_sheet;
1694+
1695+ auto candidate_subgraphs( m_subgraphs );
1696+ auto connections_to_check( connection->Members() );
1697+
1698+ bool contains_hier_stuff = false;
1699+
1700+ for( auto item : subgraph->m_items )
1701+ {
1702+ if( item->Type() == SCH_HIERARCHICAL_LABEL_T ||
1703+ item->Type() == SCH_SHEET_PIN_T )
1704+ {
1705+ contains_hier_stuff = true;
1706+ break;
1707+ }
1708+ }
1709+
1710+ // TODO(JE) maybe it will be better to form these links eventually,
1711+ // but for now let's only include subgraphs that contain hierarchical
1712+ // links in one direction or another
1713+ if( !contains_hier_stuff )
1714+ continue;
1715+
1716+ // For plain nets, just link based on the driver
1717+ if( !connection->IsBus() )
1718+ {
1719+ connections_to_check.push_back( std::make_shared<SCH_CONNECTION>( *connection ) );
1720+ }
1721+
1722+ // Look for "neighbors" for subgraphs that have hierarchical connections.
1723+ // These are usually other subgraphs that have local labels on the
1724+ // same sheet and so should be connected together.
1725+
1726+ for( unsigned i = 0; i < connections_to_check.size(); i++ )
1727+ {
1728+ auto member = connections_to_check[i];
1729+
1730+ if( member->IsBus() )
1731+ {
1732+ connections_to_check.insert( connections_to_check.end(),
1733+ member->Members().begin(),
1734+ member->Members().end() );
1735+ continue;
1736+ }
1737+
1738+ for( auto candidate : candidate_subgraphs )
1739+ {
1740+ if( candidate->m_sheet != sheet || !candidate->m_driver || candidate == subgraph )
1741+ continue;
1742+
1743+ auto candidate_connection = candidate->m_driver_connection;
1744+
1745+ if( !candidate_connection->IsNet() )
1746+ continue;
1747+
1748+ if( candidate_connection->Name( false ) == member->Name( false ) )
1749+ subgraph->m_neighbor_map[ member ].push_back( candidate );
1750+ }
1751+ }
1752+ }
1753+
1754+ // Generate subgraphs for invisible power pins
1755+
1756+ for( auto pc : m_invisible_power_pins )
1757+ {
1758+ if( pc->ConnectedItems().size() > 0 && !pc->m_pin->GetParent()->IsPower() )
1759+ {
1760+ // ERC will warn about this: user has wired up an invisible pin
1761+ continue;
1762+ }
1763+
1764+ auto name = pc->m_pin->GetName();
1765+ int code = -1;
1766+ auto sheet = all_sheets[0];
1767+
1768+ auto connection = pc->Connection( sheet );
1769+
1770+ if( !connection )
1771+ {
1772+ pc->InitializeConnection( sheet );
1773+ connection = pc->Connection( sheet );
1774+ }
1775+ else
1776+ {
1777+ continue;
1778+ }
1779+
1780+ try
1781+ {
1782+ code = m_net_name_to_code_map.at( name );
1783+ }
1784+ catch( const std::out_of_range& oor )
1785+ {
1786+ code = assignNewNetCode( *connection );
1787+ }
1788+
1789+ // Find a subgraph with the same net and just throw this pin on to it.
1790+ // TODO(JE) should there be a dedicated subgraph for invisible pins?
1791+ // Since this is currently done at the very end, the fact that some
1792+ // subgraph will be getting random pins added shouldn't be a problem,
1793+ // but this could be a gotcha if subgraph data is used after the end
1794+ // of this method at some point in the future.
1795+
1796+ CONNECTION_SUBGRAPH* subgraph = nullptr;
1797+
1798+ try
1799+ {
1800+ auto subgraphs = m_net_code_to_subgraphs_map.at( code );
1801+ subgraph = subgraphs[0];
1802+ }
1803+ catch( const std::out_of_range& oor )
1804+ {
1805+ }
1806+
1807+ if( subgraph && subgraph->m_driver )
1808+ {
1809+ auto parent = subgraph->m_driver_connection;
1810+ pc->Connection( sheet )->Clone( *parent );
1811+ }
1812+ else
1813+ {
1814+ subgraph = new CONNECTION_SUBGRAPH( m_frame );
1815+ m_net_code_to_subgraphs_map[ code ].push_back( subgraph );
1816+
1817+ subgraph->m_code = m_last_subgraph_code++;
1818+ subgraph->m_sheet = sheet;
1819+ subgraph->m_items.push_back( pc );
1820+ subgraph->m_drivers.push_back( pc );
1821+
1822+ subgraph->ResolveDrivers();
1823+
1824+ connection->SetSubgraphCode( subgraph->m_code );
1825+ }
1826+ }
1827+
1828+ // Collapse net codes between hierarchical sheets
1829+
1830+ for( auto subgraph : m_subgraphs )
1831+ {
1832+ if( !subgraph->m_driver || !subgraph->m_dirty )
1833+ continue;
1834+
1835+ auto sheet = subgraph->m_sheet;
1836+ auto connection = std::make_shared<SCH_CONNECTION>( *subgraph->m_driver_connection );
1837+
1838+ // Collapse power nets that are shorted together
1839+
1840+ if( subgraph->m_multiple_power_ports )
1841+ {
1842+ for( auto obj : subgraph->m_drivers )
1843+ {
1844+ if( obj == subgraph->m_driver )
1845+ continue;
1846+
1847+ auto power_object = dynamic_cast<SCH_PIN_CONNECTION*>( obj );
1848+
1849+ wxASSERT( power_object );
1850+
1851+ auto name = power_object->GetDefaultNetName( subgraph->m_sheet );
1852+ int code = -1;
1853+
1854+ try
1855+ {
1856+ code = m_net_name_to_code_map.at( name );
1857+ }
1858+ catch( const std::out_of_range& oor )
1859+ {
1860+ continue;
1861+ }
1862+
1863+ for( auto subgraph_to_update : m_subgraphs )
1864+ {
1865+ if( !subgraph_to_update->m_driver )
1866+ continue;
1867+
1868+ auto subsheet = subgraph_to_update->m_sheet;
1869+ auto conn = subgraph_to_update->m_driver_connection;
1870+
1871+ if( conn->IsBus() || conn->NetCode() != code )
1872+ continue;
1873+
1874+ for( auto item : subgraph_to_update->m_items )
1875+ {
1876+ auto item_conn = item->Connection( subsheet );
1877+ item_conn->Clone( *connection );
1878+ }
1879+ }
1880+ }
1881+ }
1882+
1883+ /**
1884+ * Is this bus in the highest level of hierarchy? That is, does it
1885+ * contain no hierarchical ports to parent sheets? If so, we process it
1886+ * here. If not, we continue, since the bus will be reached from one in
1887+ * a higher level sheet.
1888+ */
1889+
1890+ bool contains_hier_labels = false;
1891+
1892+ for( auto item : subgraph->m_drivers )
1893+ {
1894+ if( item->Type() == SCH_HIERARCHICAL_LABEL_T )
1895+ {
1896+ contains_hier_labels = true;
1897+ break;
1898+ }
1899+ }
1900+
1901+ if( contains_hier_labels )
1902+ continue;
1903+
1904+ // On the top level sheet, copy the neighbors onto the bus members
1905+ // because the members won't have net codes yet. Then recurse into the
1906+ // child sheets and propagate those net codes down.
1907+
1908+ // TODO(JE) should we assign bus member net codes in the bus first, and
1909+ // then reverse this operation so we overwrite the net codes generated
1910+ // for the neighbors earlier rather than pulling them in?
1911+
1912+ if( connection->IsBus() )
1913+ {
1914+ for( auto& kv : subgraph->m_neighbor_map )
1915+ {
1916+ auto member = kv.first;
1917+
1918+ int candidate_net_code = 0;
1919+
1920+ for( auto neighbor : kv.second )
1921+ {
1922+ auto neighbor_conn = neighbor->m_driver_connection;
1923+
1924+ try
1925+ {
1926+ int c = m_net_name_to_code_map.at( neighbor_conn->Name() );
1927+
1928+ if( candidate_net_code == 0 )
1929+ candidate_net_code = c;
1930+ else
1931+ {
1932+ #ifdef CONNECTIVITY_DEBUG
1933+ if( c != candidate_net_code )
1934+ wxASSERT_MSG( false, "More than one net code for a neighbor!" );
1935+ #endif
1936+ }
1937+ }
1938+ catch( const std::out_of_range& oor )
1939+ {
1940+ #ifdef CONNECTIVITY_DEBUG
1941+ wxASSERT_MSG( false, "No net code found for an existing net" );
1942+ #endif
1943+ }
1944+
1945+ member->SetNetCode( candidate_net_code );
1946+ }
1947+ }
1948+
1949+ // Some bus members might not have a neighbor to establish a net
1950+ // code, so generate new ones as needed.
1951+ for( auto& member : connection->Members() )
1952+ {
1953+ if( member->IsNet() && member->NetCode() == 0 )
1954+ {
1955+ assignNewNetCode( *member );
1956+ }
1957+ else if( member->IsBus() )
1958+ {
1959+ for( auto& sub_member : member->Members() )
1960+ {
1961+ if( sub_member->NetCode() == 0 )
1962+ assignNewNetCode( *sub_member );
1963+ }
1964+ }
1965+ }
1966+ }
1967+
1968+ /**
1969+ * The general plan:
1970+ *
1971+ * Find subsheet subgraphs that match this one (because the driver is a
1972+ * hierarchical label with the same name as a sheet pin on this one).
1973+ *
1974+ * Iterate over the bus members of the subsheet subgraph:
1975+ *
1976+ * 1) Find the matching bus member of the top level subgraph.
1977+ * For bus groups this is just a name match (minus path).
1978+ * For bus vectors the names *don't have to match*, just
1979+ * the vector index!
1980+ *
1981+ * 2) Clone the connection of the top level subgraph onto all
1982+ * the neighbor subgraphs.
1983+ *
1984+ * 3) Recurse down onto any subsheets connected to the SSSG.
1985+ */
1986+
1987+ std::vector<CONNECTION_SUBGRAPH*> child_subgraphs;
1988+
1989+ child_subgraphs.push_back( subgraph );
1990+
1991+ for( unsigned i = 0; i < child_subgraphs.size(); i++ )
1992+ {
1993+ // child_subgraphs[i] now refers to the "parent" subgraph that we
1994+ // are descending the hierarchy with. If there are multiple levels
1995+ // of hierarchy, those will get pushed onto child_subgraphs below.
1996+
1997+ for( auto item : child_subgraphs[i]->m_items )
1998+ {
1999+ if( item->Type() == SCH_SHEET_PIN_T )
2000+ {
2001+ auto sp = static_cast<SCH_SHEET_PIN*>( item );
2002+ auto sp_name = sp->GetText();
2003+ auto subsheet = child_subgraphs[i]->m_sheet;
2004+ subsheet.push_back( sp->GetParent() );
2005+
2006+ #ifdef CONNECTIVITY_DEBUG
2007+ wxLogDebug( wxT("Propagating sheet pin %s on %s with connection %s to subsheet %s"),
2008+ sp_name, child_subgraphs[i]->m_sheet.PathHumanReadable(),
2009+ connection->Name(), subsheet.PathHumanReadable() );
2010+ #endif
2011+
2012+ for( auto candidate : m_subgraphs )
2013+ {
2014+ if( !candidate->m_dirty )
2015+ continue;
2016+
2017+ if( candidate->m_sheet == subsheet && candidate->m_driver )
2018+ {
2019+ SCH_ITEM* hier_label = nullptr;
2020+
2021+ for( auto d : candidate->m_drivers )
2022+ {
2023+ if( ( d->Type() == SCH_HIERARCHICAL_LABEL_T ) &&
2024+ ( static_cast<SCH_HIERLABEL*>( d )->GetText() == sp_name ) )
2025+ hier_label = d;
2026+ }
2027+
2028+ if( hier_label )
2029+ {
2030+ #ifdef CONNECTIVITY_DEBUG
2031+ wxLogDebug( "Found child %s", static_cast<SCH_HIERLABEL*>( hier_label )->GetText() );
2032+ #endif
2033+
2034+ // We found a subgraph that is a subsheet child of
2035+ // our top-level subgraph, so let's mark it
2036+
2037+ candidate->m_dirty = false;
2038+
2039+ auto type = hier_label->Connection( subsheet )->Type();
2040+
2041+ bool candidate_has_sheet_pins = false;
2042+
2043+ // Directly update subsheet net connections
2044+
2045+ for( auto c_item : candidate->m_items )
2046+ {
2047+ if( c_item->Type() == SCH_SHEET_PIN_T )
2048+ candidate_has_sheet_pins = true;
2049+
2050+ auto c = c_item->Connection( subsheet );
2051+
2052+ wxASSERT( c );
2053+
2054+ if( ( connection->IsBus() && c->IsNet() ) ||
2055+ ( connection->IsNet() && c->IsBus() ) )
2056+ {
2057+ continue;
2058+ }
2059+
2060+ c->Clone( *connection );
2061+ }
2062+
2063+ // Now propagate to subsheet neighbors
2064+ for( auto& kv : candidate->m_neighbor_map )
2065+ {
2066+ auto member = kv.first;
2067+ std::shared_ptr<SCH_CONNECTION> top_level_conn;
2068+
2069+ #ifdef CONNECTIVITY_DEBUG
2070+ wxLogDebug( "Found child neighbor from member %s",
2071+ member->Name() );
2072+ #endif
2073+
2074+ if( type == CONNECTION_BUS_GROUP )
2075+ {
2076+ // Bus group: match parent by name
2077+ for( auto parent_member : connection->Members() )
2078+ {
2079+ if( parent_member->IsNet() &&
2080+ parent_member->Name( true ) == member->Name( true ) )
2081+ {
2082+ top_level_conn = parent_member;
2083+ }
2084+ else if( parent_member->IsBus() )
2085+ {
2086+ for( auto& sub_member : parent_member->Members() )
2087+ {
2088+ if( sub_member->Name( true ) == member->Name( true ) )
2089+ top_level_conn = sub_member;
2090+ }
2091+ }
2092+ }
2093+ }
2094+ else if( type == CONNECTION_BUS )
2095+ {
2096+ // Bus vector: match parent by index
2097+ for( auto parent_member : connection->Members() )
2098+ {
2099+ if( parent_member->VectorIndex() == member->VectorIndex() )
2100+ top_level_conn = parent_member;
2101+ }
2102+ }
2103+ else
2104+ {
2105+ top_level_conn = connection;
2106+ }
2107+
2108+ // If top_level_conn was not found, probably it's
2109+ // an ERC error and will be caught by ERC
2110+
2111+ if( !top_level_conn )
2112+ {
2113+ continue;
2114+ }
2115+
2116+ for( auto neighbor : kv.second )
2117+ {
2118+ #ifdef CONNECTIVITY_DEBUG
2119+ wxLogDebug( "Propagating to neighbor driven by %s",
2120+ neighbor->m_driver->GetSelectMenuText( MILLIMETRES ) );
2121+ #endif
2122+
2123+ bool neighbor_has_sheet_pins = false;
2124+
2125+ for( auto n_item : neighbor->m_items )
2126+ {
2127+ auto c = n_item->Connection( subsheet );
2128+
2129+ wxASSERT( c );
2130+
2131+ c->Clone( *top_level_conn );
2132+
2133+ if( n_item->Type() == SCH_SHEET_PIN_T )
2134+ neighbor_has_sheet_pins = true;
2135+ }
2136+
2137+ if( neighbor_has_sheet_pins )
2138+ {
2139+ #ifdef CONNECTIVITY_DEBUG
2140+ wxLogDebug( "Neighbor driven by %s has subsheet pins",
2141+ neighbor->m_driver->GetSelectMenuText( MILLIMETRES ) );
2142+ #endif
2143+ child_subgraphs.push_back( neighbor );
2144+ }
2145+ }
2146+ }
2147+
2148+ // Now, check to see if the candidate also has
2149+ // sheet pin members. If so, add to the queue.
2150+ if( candidate_has_sheet_pins)
2151+ {
2152+ #ifdef CONNECTIVITY_DEBUG
2153+ wxLogDebug( "Candidate %s has subsheet pins",
2154+ candidate->m_driver->GetSelectMenuText( MILLIMETRES ) );
2155+ #endif
2156+ child_subgraphs.push_back( candidate );
2157+ }
2158+ }
2159+ }
2160+ }
2161+ }
2162+ }
2163+ }
2164+
2165+ subgraph->m_dirty = false;
2166+ }
2167+
2168+ m_net_code_to_subgraphs_map.clear();
2169+
2170+ for( auto subgraph : m_subgraphs )
2171+ {
2172+ if( !subgraph->m_driver )
2173+ continue;
2174+
2175+ if( subgraph->m_dirty )
2176+ subgraph->m_dirty = false;
2177+
2178+ if( subgraph->m_driver_connection->IsBus() )
2179+ continue;
2180+
2181+ int code = subgraph->m_driver_connection->NetCode();
2182+ m_net_code_to_subgraphs_map[ code ].push_back( subgraph );
2183+ }
2184+
2185+#ifdef CONNECTIVITY_PROFILE
2186+ phase2.Stop();
2187+ std::cout << "BuildConnectionGraph() " << phase2.msecs() << " ms" << std::endl;
2188+#endif
2189+}
2190+
2191+
2192+int CONNECTION_GRAPH::assignNewNetCode( SCH_CONNECTION& aConnection )
2193+{
2194+ int code;
2195+
2196+ try
2197+ {
2198+ code = m_net_name_to_code_map.at( aConnection.Name() );
2199+ }
2200+ catch( const std::out_of_range& oor )
2201+ {
2202+ code = m_last_net_code++;
2203+ m_net_name_to_code_map[ aConnection.Name() ] = code;
2204+ }
2205+
2206+ aConnection.SetNetCode( code );
2207+
2208+ return code;
2209+}
2210+
2211+
2212+std::shared_ptr<BUS_ALIAS> CONNECTION_GRAPH::GetBusAlias( wxString aName )
2213+{
2214+ try
2215+ {
2216+ return m_bus_alias_cache.at( aName );
2217+ }
2218+ catch( const std::out_of_range& oor )
2219+ {
2220+ return nullptr;
2221+ }
2222+}
2223+
2224+
2225+std::vector<CONNECTION_SUBGRAPH*> CONNECTION_GRAPH::GetBusesNeedingMigration()
2226+{
2227+ std::vector<CONNECTION_SUBGRAPH*> ret;
2228+
2229+ for( auto subgraph : m_subgraphs )
2230+ {
2231+ // Graph is supposed to be up-to-date before calling this
2232+ wxASSERT( !subgraph->m_dirty );
2233+
2234+ if( !subgraph->m_driver )
2235+ continue;
2236+
2237+ auto sheet = subgraph->m_sheet;
2238+ auto connection = subgraph->m_driver->Connection( sheet );
2239+
2240+ if( !connection->IsBus() )
2241+ continue;
2242+
2243+ if( subgraph->GetBusLabels().size() > 1 )
2244+ {
2245+ #ifdef CONNECTIVITY_DEBUG
2246+ wxLogDebug( "SG %ld (%s) has multiple bus labels", subgraph->m_code,
2247+ connection->Name() );
2248+ #endif
2249+
2250+ ret.push_back( subgraph );
2251+ }
2252+ }
2253+
2254+ return ret;
2255+}
2256+
2257+
2258+bool CONNECTION_GRAPH::UsesNewBusFeatures() const
2259+{
2260+ for( auto subgraph : m_subgraphs )
2261+ {
2262+ if( !subgraph->m_driver )
2263+ continue;
2264+
2265+ auto sheet = subgraph->m_sheet;
2266+ auto connection = subgraph->m_driver->Connection( sheet );
2267+
2268+ if( !connection->IsBus() )
2269+ continue;
2270+
2271+ if( connection->Type() == CONNECTION_BUS_GROUP )
2272+ return true;
2273+ }
2274+
2275+ return false;
2276+}
2277+
2278+
2279+int CONNECTION_GRAPH::RunERC( const ERC_SETTINGS& aSettings, bool aCreateMarkers )
2280+{
2281+ int error_count = 0;
2282+
2283+ for( auto subgraph : m_subgraphs )
2284+ {
2285+ // Graph is supposed to be up-to-date before calling RunERC()
2286+ wxASSERT( !subgraph->m_dirty );
2287+
2288+ /**
2289+ * NOTE:
2290+ *
2291+ * We could check that labels attached to bus subgraphs follow the
2292+ * proper format (i.e. actually define a bus).
2293+ *
2294+ * This check doesn't need to be here right now because labels
2295+ * won't actually be connected to bus wires if they aren't in the right
2296+ * format due to their TestDanglingEnds() implementation.
2297+ */
2298+
2299+ if( aSettings.check_bus_driver_conflicts &&
2300+ !subgraph->ResolveDrivers( aCreateMarkers ) )
2301+ error_count++;
2302+
2303+ if( aSettings.check_bus_to_net_conflicts &&
2304+ !ercCheckBusToNetConflicts( subgraph, aCreateMarkers ) )
2305+ error_count++;
2306+
2307+ if( aSettings.check_bus_entry_conflicts &&
2308+ !ercCheckBusToBusEntryConflicts( subgraph, aCreateMarkers ) )
2309+ error_count++;
2310+
2311+ if( aSettings.check_bus_to_bus_conflicts &&
2312+ !ercCheckBusToBusConflicts( subgraph, aCreateMarkers ) )
2313+ error_count++;
2314+
2315+ // The following checks are always performed since they don't currently
2316+ // have an option exposed to the user
2317+
2318+ if( !ercCheckNoConnects( subgraph, aCreateMarkers ) )
2319+ error_count++;
2320+
2321+ if( !ercCheckLabels( subgraph, aCreateMarkers ) )
2322+ error_count++;
2323+ }
2324+
2325+ return error_count;
2326+}
2327+
2328+
2329+bool CONNECTION_GRAPH::ercCheckBusToNetConflicts( CONNECTION_SUBGRAPH* aSubgraph,
2330+ bool aCreateMarkers )
2331+{
2332+ wxString msg;
2333+ auto sheet = aSubgraph->m_sheet;
2334+ auto screen = sheet.LastScreen();
2335+
2336+ SCH_ITEM* net_item = nullptr;
2337+ SCH_ITEM* bus_item = nullptr;
2338+ SCH_CONNECTION conn;
2339+
2340+ for( auto item : aSubgraph->m_items )
2341+ {
2342+ switch( item->Type() )
2343+ {
2344+ case SCH_LINE_T:
2345+ {
2346+ if( item->GetLayer() == LAYER_BUS )
2347+ bus_item = ( !bus_item ) ? item : bus_item;
2348+ else
2349+ net_item = ( !net_item ) ? item : net_item;
2350+ break;
2351+ }
2352+
2353+ case SCH_GLOBAL_LABEL_T:
2354+ case SCH_SHEET_PIN_T:
2355+ case SCH_HIERARCHICAL_LABEL_T:
2356+ {
2357+ auto text = static_cast<SCH_TEXT*>( item )->GetText();
2358+ conn.ConfigureFromLabel( text );
2359+
2360+ if( conn.IsBus() )
2361+ bus_item = ( !bus_item ) ? item : bus_item;
2362+ else
2363+ net_item = ( !net_item ) ? item : net_item;
2364+ break;
2365+ }
2366+
2367+ default:
2368+ break;
2369+ }
2370+ }
2371+
2372+ if( net_item && bus_item )
2373+ {
2374+ if( aCreateMarkers )
2375+ {
2376+ msg.Printf( _( "%s and %s are graphically connected but cannot"
2377+ " electrically connect because one is a bus and"
2378+ " the other is a net." ),
2379+ bus_item->GetSelectMenuText( m_frame->GetUserUnits() ),
2380+ net_item->GetSelectMenuText( m_frame->GetUserUnits() ) );
2381+
2382+ auto marker = new SCH_MARKER();
2383+ marker->SetTimeStamp( GetNewTimeStamp() );
2384+ marker->SetMarkerType( MARKER_BASE::MARKER_ERC );
2385+ marker->SetErrorLevel( MARKER_BASE::MARKER_SEVERITY_ERROR );
2386+ marker->SetData( ERCE_BUS_TO_NET_CONFLICT,
2387+ net_item->GetPosition(), msg,
2388+ bus_item->GetPosition() );
2389+
2390+ screen->Append( marker );
2391+ }
2392+
2393+ return false;
2394+ }
2395+
2396+ return true;
2397+}
2398+
2399+
2400+bool CONNECTION_GRAPH::ercCheckBusToBusConflicts( CONNECTION_SUBGRAPH* aSubgraph,
2401+ bool aCreateMarkers )
2402+{
2403+ wxString msg;
2404+ auto sheet = aSubgraph->m_sheet;
2405+ auto screen = sheet.LastScreen();
2406+
2407+ SCH_ITEM* label = nullptr;
2408+ SCH_ITEM* port = nullptr;
2409+
2410+ for( auto item : aSubgraph->m_items )
2411+ {
2412+ switch( item->Type() )
2413+ {
2414+ case SCH_TEXT_T:
2415+ case SCH_GLOBAL_LABEL_T:
2416+ {
2417+ if( !label && item->Connection( sheet )->IsBus() )
2418+ label = item;
2419+ break;
2420+ }
2421+
2422+ case SCH_SHEET_PIN_T:
2423+ case SCH_HIERARCHICAL_LABEL_T:
2424+ {
2425+ if( !port && item->Connection( sheet )->IsBus() )
2426+ port = item;
2427+ break;
2428+ }
2429+
2430+ default:
2431+ break;
2432+ }
2433+ }
2434+
2435+ if( label && port )
2436+ {
2437+ bool match = false;
2438+
2439+ for( auto member : label->Connection( sheet )->Members() )
2440+ {
2441+ for( auto test : port->Connection( sheet )->Members() )
2442+ {
2443+ if( test != member && member->Name() == test->Name() )
2444+ {
2445+ match = true;
2446+ break;
2447+ }
2448+ }
2449+
2450+ if( match )
2451+ break;
2452+ }
2453+
2454+ if( !match )
2455+ {
2456+ if( aCreateMarkers )
2457+ {
2458+ msg.Printf( _( "%s and %s are graphically connected but do "
2459+ "not share any bus members" ),
2460+ label->GetSelectMenuText( m_frame->GetUserUnits() ),
2461+ port->GetSelectMenuText( m_frame->GetUserUnits() ) );
2462+
2463+ auto marker = new SCH_MARKER();
2464+ marker->SetTimeStamp( GetNewTimeStamp() );
2465+ marker->SetMarkerType( MARKER_BASE::MARKER_ERC );
2466+ marker->SetErrorLevel( MARKER_BASE::MARKER_SEVERITY_ERROR );
2467+ marker->SetData( ERCE_BUS_TO_BUS_CONFLICT,
2468+ label->GetPosition(), msg,
2469+ port->GetPosition() );
2470+
2471+ screen->Append( marker );
2472+ }
2473+
2474+ return false;
2475+ }
2476+ }
2477+
2478+ return true;
2479+}
2480+
2481+
2482+bool CONNECTION_GRAPH::ercCheckBusToBusEntryConflicts( CONNECTION_SUBGRAPH* aSubgraph,
2483+ bool aCreateMarkers )
2484+{
2485+ wxString msg;
2486+ bool conflict = false;
2487+ auto sheet = aSubgraph->m_sheet;
2488+ auto screen = sheet.LastScreen();
2489+
2490+ SCH_BUS_WIRE_ENTRY* bus_entry = nullptr;
2491+ SCH_ITEM* bus_wire = nullptr;
2492+
2493+ for( auto item : aSubgraph->m_items )
2494+ {
2495+ switch( item->Type() )
2496+ {
2497+ case SCH_BUS_WIRE_ENTRY_T:
2498+ {
2499+ if( !bus_entry )
2500+ bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( item );
2501+ break;
2502+ }
2503+
2504+ default:
2505+ break;
2506+ }
2507+ }
2508+
2509+ if( bus_entry && bus_entry->m_connected_bus_item )
2510+ {
2511+ bus_wire = bus_entry->m_connected_bus_item;
2512+ conflict = true;
2513+
2514+ auto test_name = bus_entry->Connection( sheet )->Name();
2515+
2516+ for( auto member : bus_wire->Connection( sheet )->Members() )
2517+ {
2518+ if( member->Type() == CONNECTION_BUS )
2519+ {
2520+ for( const auto& sub_member : member->Members() )
2521+ if( sub_member->Name() == test_name )
2522+ conflict = false;
2523+ }
2524+ else if( member->Name() == test_name )
2525+ {
2526+ conflict = false;
2527+ }
2528+ }
2529+ }
2530+
2531+ if( conflict )
2532+ {
2533+ if( aCreateMarkers )
2534+ {
2535+ msg.Printf( _( "%s (%s) is connected to %s (%s) but is not a member of the bus" ),
2536+ bus_entry->GetSelectMenuText( m_frame->GetUserUnits() ),
2537+ bus_entry->Connection( sheet )->Name(),
2538+ bus_wire->GetSelectMenuText( m_frame->GetUserUnits() ),
2539+ bus_wire->Connection( sheet )->Name() );
2540+
2541+ auto marker = new SCH_MARKER();
2542+ marker->SetTimeStamp( GetNewTimeStamp() );
2543+ marker->SetMarkerType( MARKER_BASE::MARKER_ERC );
2544+ marker->SetErrorLevel( MARKER_BASE::MARKER_SEVERITY_WARNING );
2545+ marker->SetData( ERCE_BUS_ENTRY_CONFLICT,
2546+ bus_entry->GetPosition(), msg,
2547+ bus_entry->GetPosition() );
2548+
2549+ screen->Append( marker );
2550+ }
2551+
2552+ return false;
2553+ }
2554+
2555+ return true;
2556+}
2557+
2558+
2559+// TODO(JE) Check sheet pins here too?
2560+bool CONNECTION_GRAPH::ercCheckNoConnects( CONNECTION_SUBGRAPH* aSubgraph,
2561+ bool aCreateMarkers )
2562+{
2563+ wxString msg;
2564+ auto sheet = aSubgraph->m_sheet;
2565+ auto screen = sheet.LastScreen();
2566+
2567+ if( aSubgraph->m_no_connect != nullptr )
2568+ {
2569+ bool has_invalid_items = false;
2570+ SCH_PIN_CONNECTION* pin = nullptr;
2571+ std::vector<SCH_ITEM*> invalid_items;
2572+
2573+ // Any subgraph that contains both a pin and a no-connect should not
2574+ // contain any other connectable items.
2575+
2576+ for( auto item : aSubgraph->m_items )
2577+ {
2578+ switch( item->Type() )
2579+ {
2580+ case SCH_PIN_CONNECTION_T:
2581+ pin = static_cast<SCH_PIN_CONNECTION*>( item );
2582+ break;
2583+
2584+ case SCH_NO_CONNECT_T:
2585+ break;
2586+
2587+ default:
2588+ has_invalid_items = true;
2589+ invalid_items.push_back( item );
2590+ }
2591+ }
2592+
2593+ // TODO: Should it be an error to have a NC item but no pin?
2594+ if( pin && has_invalid_items )
2595+ {
2596+ auto pos = pin->GetPosition();
2597+ msg.Printf( _( "Pin %s of component %s has a no-connect marker but is connected" ),
2598+ GetChars( pin->m_pin->GetName() ),
2599+ GetChars( pin->m_comp->GetRef( &aSubgraph->m_sheet ) ) );
2600+
2601+ auto marker = new SCH_MARKER();
2602+ marker->SetTimeStamp( GetNewTimeStamp() );
2603+ marker->SetMarkerType( MARKER_BASE::MARKER_ERC );
2604+ marker->SetErrorLevel( MARKER_BASE::MARKER_SEVERITY_WARNING );
2605+ marker->SetData( ERCE_NOCONNECT_CONNECTED, pos, msg, pos );
2606+
2607+ screen->Append( marker );
2608+
2609+ return false;
2610+ }
2611+ }
2612+ else
2613+ {
2614+ bool has_other_connections = false;
2615+ SCH_PIN_CONNECTION* pin = nullptr;
2616+
2617+ // Any subgraph that lacks a no-connect and contains a pin should also
2618+ // contain at least one other connectable item.
2619+
2620+ for( auto item : aSubgraph->m_items )
2621+ {
2622+ switch( item->Type() )
2623+ {
2624+ case SCH_PIN_CONNECTION_T:
2625+ if( !pin )
2626+ pin = static_cast<SCH_PIN_CONNECTION*>( item );
2627+ else
2628+ has_other_connections = true;
2629+ break;
2630+
2631+ default:
2632+ if( item->IsConnectable() )
2633+ has_other_connections = true;
2634+ break;
2635+ }
2636+ }
2637+
2638+ if( pin && !has_other_connections &&
2639+ pin->m_pin->GetType() != PIN_NC )
2640+ {
2641+ auto pos = pin->GetPosition();
2642+ msg.Printf( _( "Pin %s of component %s is unconnected." ),
2643+ GetChars( pin->m_pin->GetName() ),
2644+ GetChars( pin->m_comp->GetRef( &aSubgraph->m_sheet ) ) );
2645+
2646+ auto marker = new SCH_MARKER();
2647+ marker->SetTimeStamp( GetNewTimeStamp() );
2648+ marker->SetMarkerType( MARKER_BASE::MARKER_ERC );
2649+ marker->SetErrorLevel( MARKER_BASE::MARKER_SEVERITY_WARNING );
2650+ marker->SetData( ERCE_PIN_NOT_CONNECTED, pos, msg, pos );
2651+
2652+ screen->Append( marker );
2653+
2654+ return false;
2655+ }
2656+ }
2657+
2658+ return true;
2659+}
2660+
2661+
2662+bool CONNECTION_GRAPH::ercCheckLabels( CONNECTION_SUBGRAPH* aSubgraph,
2663+ bool aCreateMarkers )
2664+{
2665+ wxString msg;
2666+ auto sheet = aSubgraph->m_sheet;
2667+ auto screen = sheet.LastScreen();
2668+
2669+ SCH_TEXT* text = nullptr;
2670+ bool has_other_connections = false;
2671+
2672+ // Any subgraph that contains a label should also contain at least one other
2673+ // connectable item.
2674+
2675+ for( auto item : aSubgraph->m_items )
2676+ {
2677+ switch( item->Type() )
2678+ {
2679+ case SCH_LABEL_T:
2680+ case SCH_GLOBAL_LABEL_T:
2681+ case SCH_HIERARCHICAL_LABEL_T:
2682+ text = static_cast<SCH_TEXT*>( item );
2683+ break;
2684+
2685+ case SCH_PIN_CONNECTION_T:
2686+ has_other_connections = true;
2687+ break;
2688+
2689+ default:
2690+ if( item->IsConnectable() )
2691+ has_other_connections = true;
2692+ break;
2693+ }
2694+ }
2695+
2696+ if( text && !has_other_connections )
2697+ {
2698+ auto pos = text->GetPosition();
2699+ msg.Printf( _( "Label %s is unconnected." ),
2700+ GetChars( text->ShortenedShownText() ) );
2701+
2702+ auto marker = new SCH_MARKER();
2703+ marker->SetTimeStamp( GetNewTimeStamp() );
2704+ marker->SetMarkerType( MARKER_BASE::MARKER_ERC );
2705+ marker->SetErrorLevel( MARKER_BASE::MARKER_SEVERITY_WARNING );
2706+ marker->SetData( ERCE_LABEL_NOT_CONNECTED, pos, msg, pos );
2707+
2708+ screen->Append( marker );
2709+
2710+ return false;
2711+ }
2712+
2713+ return true;
2714+}
2715diff --git a/eeschema/connection_graph.h b/eeschema/connection_graph.h
2716new file mode 100644
2717index 0000000..d2fab3b
2718--- /dev/null
2719+++ b/eeschema/connection_graph.h
2720@@ -0,0 +1,341 @@
2721+/*
2722+ * This program source code file is part of KiCad, a free EDA CAD application.
2723+ *
2724+ * Copyright (C) 2018 CERN
2725+ * @author Jon Evans <jon@craftyjon.com>
2726+ *
2727+ * This program is free software; you can redistribute it and/or
2728+ * modify it under the terms of the GNU General Public License
2729+ * as published by the Free Software Foundation; either version 2
2730+ * of the License, or (at your option) any later version.
2731+ *
2732+ * This program is distributed in the hope that it will be useful,
2733+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2734+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2735+ * GNU General Public License for more details.
2736+ *
2737+ * You should have received a copy of the GNU General Public License along
2738+ * with this program. If not, see <http://www.gnu.org/licenses/>.
2739+ */
2740+
2741+#ifndef _CONNECTION_GRAPH_H
2742+#define _CONNECTION_GRAPH_H
2743+
2744+#include <mutex>
2745+#include <vector>
2746+
2747+#include <common.h>
2748+#include <erc_settings.h>
2749+#include <sch_connection.h>
2750+#include <sch_item_struct.h>
2751+
2752+
2753+#ifdef DEBUG
2754+// Uncomment this line to enable connectivity debugging features
2755+// #define CONNECTIVITY_DEBUG
2756+#endif
2757+
2758+#ifdef CONNECTIVITY_DEBUG
2759+#define CONNECTIVITY_PROFILE
2760+#endif
2761+
2762+// Uncomment this line to enable real-time connectivity updates
2763+// TODO(JE) re-enable this once performance concerns are sorted out
2764+// #define CONNECTIVITY_REAL_TIME
2765+
2766+class SCH_PIN_CONNECTION;
2767+
2768+class SCH_EDIT_FRAME;
2769+
2770+
2771+/**
2772+ * A subgraph is a set of items that are "physically" connected in the schematic.
2773+ *
2774+ * For example, a label connected to a wire and so on.
2775+ * A net is composed of one or more subgraphs.
2776+ *
2777+ * A set of items that appears to be physically connected may actually be more
2778+ * than one subgraph, because some items don't connect electrically.
2779+ *
2780+ * For example, multiple bus wires can come together at a junction but have
2781+ * different labels on each branch. Each label+wire branch is its own subgraph.
2782+ *
2783+ */
2784+class CONNECTION_SUBGRAPH
2785+{
2786+public:
2787+ CONNECTION_SUBGRAPH( SCH_EDIT_FRAME* aFrame ) :
2788+ m_dirty( false ), m_code( -1 ), m_multiple_power_ports( false ),
2789+ m_strong_driver( false ), m_no_connect( nullptr ), m_driver( nullptr ),
2790+ m_frame( aFrame )
2791+ {}
2792+ /**
2793+ * Determines which potential driver should drive the subgraph.
2794+ *
2795+ * If multiple possible drivers exist, picks one according to the priority.
2796+ * If multiple "winners" exist, returns false and sets m_driver to nullptr.
2797+ *
2798+ * @param aCreateMarkers controls whether ERC markers should be added for conflicts
2799+ * @return true if m_driver was set, or false if a conflict occurred
2800+ */
2801+ bool ResolveDrivers( bool aCreateMarkers = false );
2802+
2803+ /**
2804+ * Returns the fully-qualified net name for this subgraph (if one exists)
2805+ */
2806+ wxString GetNetName();
2807+
2808+ /// Returns all the bus labels attached to this subgraph (if any)
2809+ std::vector<SCH_ITEM*> GetBusLabels();
2810+
2811+ bool m_dirty;
2812+
2813+ long m_code;
2814+
2815+ /// True if this subgraph contains multiple power ports to join in one net
2816+ bool m_multiple_power_ports;
2817+
2818+ /// True if the driver is "strong": a label or power object
2819+ bool m_strong_driver;
2820+
2821+ /// No-connect item in graph, if any
2822+ SCH_ITEM* m_no_connect;
2823+
2824+ std::vector<SCH_ITEM*> m_items;
2825+
2826+ std::vector<SCH_ITEM*> m_drivers;
2827+
2828+ SCH_ITEM* m_driver;
2829+
2830+ SCH_SHEET_PATH m_sheet;
2831+
2832+ // Needed for m_UserUnits for now; maybe refactor later
2833+ SCH_EDIT_FRAME* m_frame;
2834+
2835+ /// Cache for driver connection
2836+ SCH_CONNECTION* m_driver_connection;
2837+
2838+ /**
2839+ * This map stores pointers to other subgraphs on the same sheet as this one
2840+ * which should be connected to this one.
2841+ *
2842+ * For example, if this subgraph is part of the bus D[7..0] and there is
2843+ * another subgraph on this sheet with connection D7, this map will include
2844+ * a pointer to that subgraph under the key D7 (where the key comes from
2845+ * the m_members list of the SCH_CONNECTION that drives this subgraph)
2846+ */
2847+ std::unordered_map< std::shared_ptr<SCH_CONNECTION>,
2848+ std::vector<CONNECTION_SUBGRAPH*> > m_neighbor_map;
2849+};
2850+
2851+
2852+/**
2853+ * Calculates the connectivity of a schematic and generates netlists
2854+ */
2855+class CONNECTION_GRAPH
2856+{
2857+public:
2858+ CONNECTION_GRAPH( SCH_EDIT_FRAME* aFrame) :
2859+ m_frame( aFrame )
2860+ {}
2861+
2862+ void Reset();
2863+
2864+ /**
2865+ * Updates the connection graph for the given list of sheets.
2866+ *
2867+ * @param aSheetList is the list of possibly modified sheets
2868+ * @param aUnconditional is true if an unconditional full recalculation should be done
2869+ */
2870+ void Recalculate( SCH_SHEET_LIST aSheetList, bool aUnconditional = false );
2871+
2872+ /**
2873+ * Returns a bus alias pointer for the given name if it exists (from cache)
2874+ *
2875+ * CONNECTION_GRAPH caches these, they are owned by the SCH_SCREEN that
2876+ * the alias was defined on. The cache is only used to update the graph.
2877+ */
2878+ std::shared_ptr<BUS_ALIAS> GetBusAlias( wxString aName );
2879+
2880+ /**
2881+ * Determines which subgraphs have more than one conflicting bus label.
2882+ *
2883+ * @see DIALOG_MIGRATE_BUSES
2884+ * @return a list of subgraphs that need migration
2885+ */
2886+
2887+ std::vector<CONNECTION_SUBGRAPH*> GetBusesNeedingMigration();
2888+
2889+ /**
2890+ * Returns true if the graph makes use of any of the new bus features
2891+ *
2892+ * For quality control during rollout of new bus features:
2893+ * - Aliases
2894+ * - Bus groups
2895+ */
2896+ bool UsesNewBusFeatures() const;
2897+
2898+ /**
2899+ * Runs electrical rule checks on the connectivity graph.
2900+ *
2901+ * Precondition: graph is up-to-date
2902+ *
2903+ * @param aSettings is used to control which tests to run
2904+ * @param aCreateMarkers controls whether error markers are created
2905+ * @return the number of errors found
2906+ */
2907+ int RunERC( const ERC_SETTINGS& aSettings, bool aCreateMarkers = true );
2908+
2909+ // TODO(JE) firm up API and move to private
2910+ std::map<int, std::vector<CONNECTION_SUBGRAPH*> > m_net_code_to_subgraphs_map;
2911+
2912+private:
2913+
2914+ std::unordered_set<SCH_ITEM*> m_items;
2915+
2916+ std::vector<CONNECTION_SUBGRAPH*> m_subgraphs;
2917+
2918+ std::vector<SCH_PIN_CONNECTION*> m_invisible_power_pins;
2919+
2920+ std::map<wxString, std::shared_ptr<BUS_ALIAS>> m_bus_alias_cache;
2921+
2922+ std::map<wxString, int> m_net_name_to_code_map;
2923+
2924+ std::map<wxString, int> m_bus_name_to_code_map;
2925+
2926+ std::map<wxString, std::vector<CONNECTION_SUBGRAPH*>> m_global_label_cache;
2927+
2928+ std::map< std::pair<SCH_SHEET_PATH, wxString>,
2929+ std::vector<CONNECTION_SUBGRAPH*> > m_local_label_cache;
2930+
2931+ int m_last_net_code;
2932+
2933+ int m_last_bus_code;
2934+
2935+ int m_last_subgraph_code;
2936+
2937+ std::mutex m_item_mutex;
2938+
2939+ // Needed for m_UserUnits for now; maybe refactor later
2940+ SCH_EDIT_FRAME* m_frame;
2941+
2942+ /**
2943+ * Updates the graphical connectivity between items (i.e. where they touch)
2944+ * The items passed in must be on the same sheet.
2945+ *
2946+ * In the first phase, all items in aItemList have their connections
2947+ * initialized for the given sheet (since they may have connections on more
2948+ * than one sheet, and each needs to be calculated individually). The
2949+ * graphical connection points for the item are added to a map that stores
2950+ * (x, y) -> [list of items].
2951+ *
2952+ * Any item that is stored in the list of items that have a connection point
2953+ * at a given (x, y) location will eventually be electrically connected.
2954+ * This means that we can't store SCH_COMPONENTs in this map -- we must store
2955+ * a structure that links a specific pin on a component back to that
2956+ * component: a SCH_PIN_CONNECTION. This wrapper class is a convenience for
2957+ * linking a pin and component to a specific (x, y) point.
2958+ *
2959+ * In the second phase, we iterate over each value in the map, which is a
2960+ * vector of items that have overlapping connection points. After some
2961+ * checks to ensure that the items should actually connect, the items are
2962+ * linked together using ConnectedItems().
2963+ *
2964+ * As a side effect, items are loaded into m_items for BuildConnectionGraph()
2965+ *
2966+ * @param aSheet is the path to the sheet of all items in the list
2967+ * @param aItemList is a list of items to consider
2968+ */
2969+ void updateItemConnectivity( SCH_SHEET_PATH aSheet,
2970+ std::vector<SCH_ITEM*> aItemList );
2971+
2972+ /**
2973+ * Generates the connection graph (after all item connectivity has been updated)
2974+ *
2975+ * In the first phase, the algorithm iterates over all items, and then over
2976+ * all items that are connected (graphically) to each item, placing them into
2977+ * CONNECTION_SUBGRAPHs. Items that can potentially drive connectivity (i.e.
2978+ * labels, pins, etc.) are added to the m_drivers vector of the subgraph.
2979+ *
2980+ * In the second phase, each subgraph is resolved. To resolve a subgraph,
2981+ * the driver is first selected by CONNECTION_SUBGRAPH::ResolveDrivers(),
2982+ * and then the connection for the chosen driver is propagated to all the
2983+ * other items in the subgraph.
2984+ */
2985+ void buildConnectionGraph();
2986+
2987+ /**
2988+ * Helper to assign a new net code to a connection
2989+ *
2990+ * @return the assigned code
2991+ */
2992+ int assignNewNetCode( SCH_CONNECTION& aConnection );
2993+
2994+ /**
2995+ * Checks one subgraph for conflicting connections between net and bus labels
2996+ *
2997+ * For example, a net wire connected to a bus port/pin, or vice versa
2998+ *
2999+ * @param aSubgraph is the subgraph to examine
3000+ * @param aCreateMarkers controls whether error markers are created
3001+ * @return true for no errors, false for errors
3002+ */
3003+ bool ercCheckBusToNetConflicts( CONNECTION_SUBGRAPH* aSubgraph,
3004+ bool aCreateMarkers );
3005+
3006+ /**
3007+ * Checks one subgraph for conflicting connections between two bus items
3008+ *
3009+ * For example, a labeled bus wire connected to a hierarchical sheet pin
3010+ * where the labeled bus doesn't contain any of the same bus members as the
3011+ * sheet pin
3012+ *
3013+ * @param aSubgraph is the subgraph to examine
3014+ * @param aCreateMarkers controls whether error markers are created
3015+ * @return true for no errors, false for errors
3016+ */
3017+ bool ercCheckBusToBusConflicts( CONNECTION_SUBGRAPH* aSubgraph,
3018+ bool aCreateMarkers );
3019+
3020+ /**
3021+ * Checks one subgraph for conflicting bus entry to bus connections
3022+ *
3023+ * For example, a wire with label "A0" is connected to a bus labeled "D[8..0]"
3024+ *
3025+ * Will also check for mistakes related to bus group names, for example:
3026+ * A bus group named "USB{DP DM}" should have bus entry connections like
3027+ * "USB.DP" but someone might accidentally just enter "DP"
3028+ *
3029+ * @param aSubgraph is the subgraph to examine
3030+ * @param aCreateMarkers controls whether error markers are created
3031+ * @return true for no errors, false for errors
3032+ */
3033+ bool ercCheckBusToBusEntryConflicts( CONNECTION_SUBGRAPH* aSubgraph,
3034+ bool aCreateMarkers );
3035+
3036+ /**
3037+ * Checks one subgraph for proper presence or absence of no-connect symbols
3038+ *
3039+ * A pin with a no-connect symbol should not have any connections
3040+ * A pin without a no-connect symbol should have at least one connection
3041+ *
3042+ * @param aSubgraph is the subgraph to examine
3043+ * @param aCreateMarkers controls whether error markers are created
3044+ * @return true for no errors, false for errors
3045+ */
3046+ bool ercCheckNoConnects( CONNECTION_SUBGRAPH* aSubgraph,
3047+ bool aCreateMarkers );
3048+
3049+ /**
3050+ * Checks one subgraph for proper connection of labels
3051+ *
3052+ * Labels should be connected to something
3053+ *
3054+ * @param aSubgraph is the subgraph to examine
3055+ * @param aCreateMarkers controls whether error markers are created
3056+ * @return true for no errors, false for errors
3057+ */
3058+ bool ercCheckLabels( CONNECTION_SUBGRAPH* aSubgraph, bool aCreateMarkers );
3059+};
3060+
3061+#endif
3062diff --git a/eeschema/controle.cpp b/eeschema/controle.cpp
3063index eab621a..aa96d08 100644
3064--- a/eeschema/controle.cpp
3065+++ b/eeschema/controle.cpp
3066@@ -228,9 +228,6 @@ SCH_ITEM* SCH_EDIT_FRAME::LocateItem( const wxPoint& aPosition, const KICAD_T aF
3067
3068 if( item )
3069 {
3070- if( item->Type() == SCH_COMPONENT_T )
3071- ( (SCH_COMPONENT*) item )->SetCurrentSheetPath( &GetCurrentSheet() );
3072-
3073 MSG_PANEL_ITEMS items;
3074 item->GetMsgPanelInfo( m_UserUnits, items );
3075 SetMsgPanel( items );
3076diff --git a/eeschema/dialogs/dialog_bom.cpp b/eeschema/dialogs/dialog_bom.cpp
3077index a54ab31..e99cf69 100644
3078--- a/eeschema/dialogs/dialog_bom.cpp
3079+++ b/eeschema/dialogs/dialog_bom.cpp
3080@@ -472,7 +472,10 @@ void DIALOG_BOM::OnRunPlugin( wxCommandEvent& event )
3081 m_parent->SetExecFlags( wxEXEC_SHOW_CONSOLE );
3082 #endif
3083
3084- m_parent->CreateNetlist( -1, fullfilename, 0, &reporter, false );
3085+ auto netlist = m_parent->CreateNetlist( false, false );
3086+
3087+ m_parent->WriteNetListFile( netlist, -1,
3088+ fullfilename, 0, &reporter );
3089
3090 m_Messages->SetValue( reportmsg );
3091 }
3092diff --git a/eeschema/dialogs/dialog_bus_manager.cpp b/eeschema/dialogs/dialog_bus_manager.cpp
3093new file mode 100644
3094index 0000000..4144d1b
3095--- /dev/null
3096+++ b/eeschema/dialogs/dialog_bus_manager.cpp
3097@@ -0,0 +1,517 @@
3098+/*
3099+ * This program source code file is part of KiCad, a free EDA CAD application.
3100+ *
3101+ * Copyright (C) 2018 CERN
3102+ * @author Jon Evans <jon@craftyjon.com>
3103+ *
3104+ * This program is free software: you can redistribute it and/or modify it
3105+ * under the terms of the GNU General Public License as published by the
3106+ * Free Software Foundation, either version 3 of the License, or (at your
3107+ * option) any later version.
3108+ *
3109+ * This program is distributed in the hope that it will be useful, but
3110+ * WITHOUT ANY WARRANTY; without even the implied warranty of
3111+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3112+ * General Public License for more details.
3113+ *
3114+ * You should have received a copy of the GNU General Public License along
3115+ * with this program. If not, see <http://www.gnu.org/licenses/>.
3116+ */
3117+
3118+#include <wx/tokenzr.h>
3119+
3120+#include <invoke_sch_dialog.h>
3121+#include <sch_sheet_path.h>
3122+
3123+#include "dialog_bus_manager.h"
3124+
3125+
3126+BEGIN_EVENT_TABLE( DIALOG_BUS_MANAGER, DIALOG_SHIM )
3127+ EVT_BUTTON( wxID_OK, DIALOG_BUS_MANAGER::OnOkClick )
3128+ EVT_BUTTON( wxID_CANCEL, DIALOG_BUS_MANAGER::OnCancelClick )
3129+END_EVENT_TABLE()
3130+
3131+
3132+DIALOG_BUS_MANAGER::DIALOG_BUS_MANAGER( SCH_EDIT_FRAME* aParent )
3133+ : DIALOG_SHIM( aParent, wxID_ANY, _( "Bus Definitions" ),
3134+ wxDefaultPosition, wxSize( 640, 480 ),
3135+ wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER ),
3136+ m_parent( aParent )
3137+{
3138+ auto sizer = new wxBoxSizer( wxVERTICAL );
3139+ auto buttons = new wxStdDialogButtonSizer();
3140+
3141+ buttons->AddButton( new wxButton( this, wxID_OK ) );
3142+ buttons->AddButton( new wxButton( this, wxID_CANCEL ) );
3143+ buttons->Realize();
3144+
3145+ auto top_container = new wxBoxSizer( wxHORIZONTAL );
3146+ auto left_pane = new wxBoxSizer( wxVERTICAL );
3147+ auto right_pane = new wxBoxSizer( wxVERTICAL );
3148+
3149+ // Left pane: alias list
3150+ auto lbl_aliases = new wxStaticText( this, wxID_ANY, _( "Bus Aliases" ),
3151+ wxDefaultPosition, wxDefaultSize,
3152+ wxALIGN_LEFT );
3153+
3154+ m_bus_list_view = new wxListView( this, wxID_ANY, wxDefaultPosition,
3155+ wxSize( 300, 300 ), wxLC_ALIGN_LEFT |
3156+ wxLC_NO_HEADER | wxLC_REPORT |
3157+ wxLC_SINGLE_SEL );
3158+ m_bus_list_view->InsertColumn( 0, "" );
3159+
3160+ auto lbl_alias_edit = new wxStaticText( this, wxID_ANY, _( "Alias Name" ),
3161+ wxDefaultPosition, wxDefaultSize,
3162+ wxALIGN_LEFT );
3163+
3164+ m_bus_edit = new wxTextCtrl( this, wxID_ANY, wxEmptyString,
3165+ wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER );
3166+
3167+ auto left_button_sizer = new wxBoxSizer( wxHORIZONTAL );
3168+
3169+ m_btn_add_bus = new wxButton( this, wxID_ANY, _( "Add" ) );
3170+ m_btn_rename_bus = new wxButton( this, wxID_ANY, _( "Rename" ) );
3171+ m_btn_remove_bus = new wxButton( this, wxID_ANY, _( "Remove" ) );
3172+
3173+ left_button_sizer->Add( m_btn_add_bus );
3174+ left_button_sizer->Add( m_btn_rename_bus );
3175+ left_button_sizer->Add( m_btn_remove_bus );
3176+
3177+ left_pane->Add( lbl_aliases, 0, wxEXPAND | wxALL, 5 );
3178+ left_pane->Add( m_bus_list_view, 1, wxEXPAND | wxALL, 5 );
3179+ left_pane->Add( lbl_alias_edit, 0, wxEXPAND | wxALL, 5 );
3180+ left_pane->Add( m_bus_edit, 0, wxEXPAND | wxALL, 5 );
3181+ left_pane->Add( left_button_sizer, 0, wxEXPAND | wxALL, 5 );
3182+
3183+ // Right pane: signal list
3184+ auto lbl_signals = new wxStaticText( this, wxID_ANY, _( "Alias Members" ),
3185+ wxDefaultPosition, wxDefaultSize,
3186+ wxALIGN_LEFT );
3187+
3188+ m_signal_list_view = new wxListView( this, wxID_ANY, wxDefaultPosition,
3189+ wxSize( 300, 300 ), wxLC_ALIGN_LEFT |
3190+ wxLC_NO_HEADER | wxLC_REPORT |
3191+ wxLC_SINGLE_SEL );
3192+ m_signal_list_view->InsertColumn( 0, "" );
3193+
3194+ auto lbl_signal_edit = new wxStaticText( this, wxID_ANY, _( "Member Name" ),
3195+ wxDefaultPosition, wxDefaultSize,
3196+ wxALIGN_LEFT );
3197+
3198+ m_signal_edit = new wxTextCtrl( this, wxID_ANY, wxEmptyString,
3199+ wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER );
3200+
3201+ auto right_button_sizer = new wxBoxSizer( wxHORIZONTAL );
3202+
3203+ m_btn_add_signal = new wxButton( this, wxID_ANY, _( "Add" ) );
3204+ m_btn_rename_signal = new wxButton( this, wxID_ANY, _( "Rename" ) );
3205+ m_btn_remove_signal = new wxButton( this, wxID_ANY, _( "Remove" ) );
3206+
3207+ right_button_sizer->Add( m_btn_add_signal );
3208+ right_button_sizer->Add( m_btn_rename_signal );
3209+ right_button_sizer->Add( m_btn_remove_signal );
3210+
3211+ right_pane->Add( lbl_signals, 0, wxEXPAND | wxALL, 5 );
3212+ right_pane->Add( m_signal_list_view, 1, wxEXPAND | wxALL, 5 );
3213+ right_pane->Add( lbl_signal_edit, 0, wxEXPAND | wxALL, 5 );
3214+ right_pane->Add( m_signal_edit, 0, wxEXPAND | wxALL, 5 );
3215+ right_pane->Add( right_button_sizer, 0, wxEXPAND | wxALL, 5 );
3216+
3217+ top_container->Add( left_pane, 1, wxEXPAND );
3218+ top_container->Add( right_pane, 1, wxEXPAND );
3219+
3220+ sizer->Add( top_container, 1, wxEXPAND | wxALL, 5 );
3221+ sizer->Add( buttons, 0, wxEXPAND | wxBOTTOM, 10 );
3222+ SetSizer( sizer );
3223+
3224+ // Setup validators
3225+
3226+ wxTextValidator validator;
3227+ validator.SetStyle( wxFILTER_EXCLUDE_CHAR_LIST );
3228+ validator.SetCharExcludes( "\r\n\t " );
3229+ m_bus_edit->SetValidator( validator );
3230+
3231+ // Allow spaces in the signal edit, so that you can type in a list of
3232+ // signals in the box and it can automatically split them when you add.
3233+ validator.SetCharExcludes( "\r\n\t" );
3234+ m_signal_edit->SetValidator( validator );
3235+
3236+ // Setup events
3237+
3238+ Bind( wxEVT_INIT_DIALOG, &DIALOG_BUS_MANAGER::OnInitDialog, this );
3239+ m_bus_list_view->Connect( wxEVT_COMMAND_LIST_ITEM_DESELECTED,
3240+ wxListEventHandler( DIALOG_BUS_MANAGER::OnSelectBus ), NULL, this );
3241+ m_bus_list_view->Connect( wxEVT_COMMAND_LIST_ITEM_SELECTED,
3242+ wxListEventHandler( DIALOG_BUS_MANAGER::OnSelectBus ), NULL, this );
3243+ m_signal_list_view->Connect( wxEVT_COMMAND_LIST_ITEM_DESELECTED,
3244+ wxListEventHandler( DIALOG_BUS_MANAGER::OnSelectSignal ), NULL, this );
3245+ m_signal_list_view->Connect( wxEVT_COMMAND_LIST_ITEM_SELECTED,
3246+ wxListEventHandler( DIALOG_BUS_MANAGER::OnSelectSignal ), NULL, this );
3247+
3248+ m_btn_add_bus->Connect( wxEVT_COMMAND_BUTTON_CLICKED,
3249+ wxCommandEventHandler( DIALOG_BUS_MANAGER::OnAddBus ), NULL, this );
3250+ m_btn_rename_bus->Connect( wxEVT_COMMAND_BUTTON_CLICKED,
3251+ wxCommandEventHandler( DIALOG_BUS_MANAGER::OnRenameBus ), NULL, this );
3252+ m_btn_remove_bus->Connect( wxEVT_COMMAND_BUTTON_CLICKED,
3253+ wxCommandEventHandler( DIALOG_BUS_MANAGER::OnRemoveBus ), NULL, this );
3254+ m_signal_edit->Connect( wxEVT_TEXT_ENTER,
3255+ wxCommandEventHandler( DIALOG_BUS_MANAGER::OnAddSignal ), NULL, this );
3256+
3257+ m_btn_add_signal->Connect( wxEVT_COMMAND_BUTTON_CLICKED,
3258+ wxCommandEventHandler( DIALOG_BUS_MANAGER::OnAddSignal ), NULL, this );
3259+ m_btn_rename_signal->Connect( wxEVT_COMMAND_BUTTON_CLICKED,
3260+ wxCommandEventHandler( DIALOG_BUS_MANAGER::OnRenameSignal ), NULL, this );
3261+ m_btn_remove_signal->Connect( wxEVT_COMMAND_BUTTON_CLICKED,
3262+ wxCommandEventHandler( DIALOG_BUS_MANAGER::OnRemoveSignal ), NULL, this );
3263+ m_bus_edit->Connect( wxEVT_TEXT_ENTER,
3264+ wxCommandEventHandler( DIALOG_BUS_MANAGER::OnAddBus ), NULL, this );
3265+
3266+ // Set initial UI state
3267+
3268+ m_btn_rename_bus->Disable();
3269+ m_btn_remove_bus->Disable();
3270+
3271+ m_btn_add_signal->Disable();
3272+ m_btn_rename_signal->Disable();
3273+ m_btn_remove_signal->Disable();
3274+
3275+ m_bus_edit->SetHint( _T( "Bus Alias Name" ) );
3276+ m_signal_edit->SetHint( _T( "Net or Bus Name" ) );
3277+
3278+ FinishDialogSettings();
3279+}
3280+
3281+
3282+void DIALOG_BUS_MANAGER::OnInitDialog( wxInitDialogEvent& aEvent )
3283+{
3284+ TransferDataToWindow();
3285+}
3286+
3287+
3288+bool DIALOG_BUS_MANAGER::TransferDataToWindow()
3289+{
3290+ m_aliases.clear();
3291+
3292+ SCH_SHEET_LIST aSheets( g_RootSheet );
3293+ std::vector< std::shared_ptr< BUS_ALIAS > > original_aliases;
3294+
3295+ // collect aliases from each open sheet
3296+ for( unsigned i = 0; i < aSheets.size(); i++ )
3297+ {
3298+ auto sheet_aliases = aSheets[i].LastScreen()->GetBusAliases();
3299+ original_aliases.insert( original_aliases.end(), sheet_aliases.begin(),
3300+ sheet_aliases.end() );
3301+ }
3302+
3303+ original_aliases.erase( std::unique( original_aliases.begin(),
3304+ original_aliases.end() ),
3305+ original_aliases.end() );
3306+
3307+ // clone into a temporary working set
3308+ int idx = 0;
3309+ for( auto alias : original_aliases )
3310+ {
3311+ m_aliases.push_back( alias->Clone() );
3312+ auto text = getAliasDisplayText( alias );
3313+ m_bus_list_view->InsertItem( idx, text );
3314+ m_bus_list_view->SetItemPtrData( idx, wxUIntPtr( m_aliases[idx].get() ) );
3315+ idx++;
3316+ }
3317+
3318+ m_bus_list_view->SetColumnWidth( 0, -1 );
3319+
3320+ return true;
3321+}
3322+
3323+
3324+void DIALOG_BUS_MANAGER::OnOkClick( wxCommandEvent& aEvent )
3325+{
3326+ if( TransferDataFromWindow() )
3327+ {
3328+ ( ( SCH_EDIT_FRAME* )GetParent() )->OnModify();
3329+ EndModal( wxID_OK );
3330+ }
3331+}
3332+
3333+
3334+void DIALOG_BUS_MANAGER::OnCancelClick( wxCommandEvent& aEvent )
3335+{
3336+ EndModal( wxID_CANCEL );
3337+}
3338+
3339+
3340+bool DIALOG_BUS_MANAGER::TransferDataFromWindow()
3341+{
3342+ // Since we have a clone of all the data, and it is from potentially
3343+ // multiple screens, the way this works is to rebuild each screen's aliases.
3344+ // A list of screens is stored here so that the screen's alias list is only
3345+ // cleared once.
3346+
3347+ std::unordered_set< SCH_SCREEN* > cleared_list;
3348+
3349+ for( auto alias : m_aliases )
3350+ {
3351+ auto screen = alias->GetParent();
3352+
3353+ if( cleared_list.count( screen ) == 0 )
3354+ {
3355+ screen->ClearBusAliases();
3356+ cleared_list.insert( screen );
3357+ }
3358+
3359+ screen->AddBusAlias( alias );
3360+ }
3361+
3362+ return true;
3363+}
3364+
3365+
3366+void DIALOG_BUS_MANAGER::OnSelectBus( wxListEvent& event )
3367+{
3368+ if( event.GetEventType() == wxEVT_COMMAND_LIST_ITEM_SELECTED )
3369+ {
3370+ auto alias = m_aliases[ event.GetIndex() ];
3371+
3372+ if( m_active_alias != alias )
3373+ {
3374+ m_active_alias = alias;
3375+
3376+ m_bus_edit->ChangeValue( alias->GetName() );
3377+
3378+ m_btn_add_bus->Disable();
3379+ m_btn_rename_bus->Enable();
3380+ m_btn_remove_bus->Enable();
3381+
3382+ auto members = alias->Members();
3383+
3384+ // TODO(JE) Clear() seems to be clearing the hint, contrary to
3385+ // the wx documentation.
3386+ m_signal_edit->Clear();
3387+ m_signal_list_view->DeleteAllItems();
3388+
3389+ for( unsigned i = 0; i < members.size(); i++ )
3390+ {
3391+ m_signal_list_view->InsertItem( i, members[i] );
3392+ }
3393+
3394+ m_signal_list_view->SetColumnWidth( 0, -1 );
3395+
3396+ m_btn_add_signal->Enable();
3397+ m_btn_rename_signal->Disable();
3398+ m_btn_remove_signal->Disable();
3399+ }
3400+ }
3401+ else
3402+ {
3403+ m_active_alias = NULL;
3404+ m_bus_edit->Clear();
3405+ m_signal_edit->Clear();
3406+ m_signal_list_view->DeleteAllItems();
3407+
3408+ m_btn_add_bus->Enable();
3409+ m_btn_rename_bus->Disable();
3410+ m_btn_remove_bus->Disable();
3411+
3412+ m_btn_add_signal->Disable();
3413+ m_btn_rename_signal->Disable();
3414+ m_btn_remove_signal->Disable();
3415+ }
3416+}
3417+
3418+
3419+void DIALOG_BUS_MANAGER::OnSelectSignal( wxListEvent& event )
3420+{
3421+ if( event.GetEventType() == wxEVT_COMMAND_LIST_ITEM_SELECTED )
3422+ {
3423+ m_signal_edit->ChangeValue( event.GetText() );
3424+ m_btn_rename_signal->Enable();
3425+ m_btn_remove_signal->Enable();
3426+ }
3427+ else
3428+ {
3429+ m_signal_edit->Clear();
3430+ m_btn_rename_signal->Disable();
3431+ m_btn_remove_signal->Disable();
3432+ }
3433+}
3434+
3435+
3436+void DIALOG_BUS_MANAGER::OnAddBus( wxCommandEvent& aEvent )
3437+{
3438+ // If there is an active alias, then check that the user actually
3439+ // changed the text in the edit box (we can't have duplicate aliases)
3440+
3441+ auto new_name = m_bus_edit->GetValue();
3442+
3443+ if( new_name.Length() == 0 )
3444+ {
3445+ return;
3446+ }
3447+
3448+ for( auto alias : m_aliases )
3449+ {
3450+ if( alias->GetName() == new_name )
3451+ {
3452+ // TODO(JE) display error?
3453+ return;
3454+ }
3455+ }
3456+
3457+ if( !m_active_alias ||
3458+ ( m_active_alias && m_active_alias->GetName().Cmp( new_name ) ) )
3459+ {
3460+ // The values are different; create a new alias
3461+ auto alias = std::make_shared<BUS_ALIAS>();
3462+ alias->SetName( new_name );
3463+
3464+ // New aliases get stored on the currently visible sheet
3465+ alias->SetParent( static_cast<SCH_EDIT_FRAME*>( GetParent() )->GetScreen() );
3466+ auto text = getAliasDisplayText( alias );
3467+
3468+ m_aliases.push_back( alias );
3469+ long idx = m_bus_list_view->InsertItem( m_aliases.size() - 1, text );
3470+ m_bus_list_view->SetColumnWidth( 0, -1 );
3471+ m_bus_list_view->Select( idx );
3472+ m_bus_edit->Clear();
3473+ }
3474+ else
3475+ {
3476+ // TODO(JE) Check about desired result here.
3477+ // Maybe warn the user? Or just do nothing
3478+ }
3479+}
3480+
3481+
3482+void DIALOG_BUS_MANAGER::OnRenameBus( wxCommandEvent& aEvent )
3483+{
3484+ // We should only get here if there is an active alias
3485+ wxASSERT( m_active_alias );
3486+
3487+ m_active_alias->SetName( m_bus_edit->GetValue() );
3488+ long idx = m_bus_list_view->FindItem( -1, wxUIntPtr( m_active_alias.get() ) );
3489+
3490+ wxASSERT( idx >= 0 );
3491+
3492+ m_bus_list_view->SetItemText( idx, getAliasDisplayText( m_active_alias ) );
3493+}
3494+
3495+
3496+void DIALOG_BUS_MANAGER::OnRemoveBus( wxCommandEvent& aEvent )
3497+{
3498+ // We should only get here if there is an active alias
3499+ wxASSERT( m_active_alias );
3500+ long i = m_bus_list_view->GetFirstSelected();
3501+ wxASSERT( m_active_alias == m_aliases[ i ] );
3502+
3503+ m_bus_list_view->DeleteItem( i );
3504+ m_aliases.erase( m_aliases.begin() + i );
3505+ m_bus_edit->Clear();
3506+
3507+ m_active_alias = NULL;
3508+
3509+ auto evt = wxListEvent( wxEVT_COMMAND_LIST_ITEM_DESELECTED );
3510+ OnSelectBus( evt );
3511+}
3512+
3513+
3514+void DIALOG_BUS_MANAGER::OnAddSignal( wxCommandEvent& aEvent )
3515+{
3516+ auto name_list = m_signal_edit->GetValue();
3517+
3518+ if( !m_active_alias || name_list.Length() == 0 )
3519+ {
3520+ return;
3521+ }
3522+
3523+ // String collecting net names that were not added to the bus
3524+ wxString notAdded;
3525+
3526+ // Parse a space-separated list and add each one
3527+ wxStringTokenizer tok( name_list, " " );
3528+ while( tok.HasMoreTokens() )
3529+ {
3530+ auto name = tok.GetNextToken();
3531+
3532+ if( !m_active_alias->Contains( name ) )
3533+ {
3534+ m_active_alias->AddMember( name );
3535+
3536+ long idx = m_signal_list_view->InsertItem(
3537+ m_active_alias->GetMemberCount() - 1, name );
3538+
3539+ m_signal_list_view->SetColumnWidth( 0, -1 );
3540+ m_signal_list_view->Select( idx );
3541+ }
3542+ else
3543+ {
3544+ // Some of the requested net names were not added to the list, so keep them for editing
3545+ notAdded = notAdded.IsEmpty() ? name : notAdded + " " + name;
3546+ }
3547+ }
3548+
3549+ m_signal_edit->SetValue( notAdded );
3550+ m_signal_edit->SetInsertionPointEnd();
3551+}
3552+
3553+
3554+void DIALOG_BUS_MANAGER::OnRenameSignal( wxCommandEvent& aEvent )
3555+{
3556+ // We should only get here if there is an active alias
3557+ wxASSERT( m_active_alias );
3558+
3559+ auto new_name = m_signal_edit->GetValue();
3560+ long idx = m_signal_list_view->GetFirstSelected();
3561+
3562+ wxASSERT( idx >= 0 );
3563+
3564+ auto old_name = m_active_alias->Members()[ idx ];
3565+
3566+ // User could have typed a space here, so check first
3567+ if( new_name.Find( " " ) != wxNOT_FOUND )
3568+ {
3569+ // TODO(JE) error feedback
3570+ m_signal_edit->ChangeValue( old_name );
3571+ return;
3572+ }
3573+
3574+ m_active_alias->Members()[ idx ] = new_name;
3575+ m_signal_list_view->SetItemText( idx, new_name );
3576+ m_signal_list_view->SetColumnWidth( 0, -1 );
3577+}
3578+
3579+
3580+void DIALOG_BUS_MANAGER::OnRemoveSignal( wxCommandEvent& aEvent )
3581+{
3582+ // We should only get here if there is an active alias
3583+ wxASSERT( m_active_alias );
3584+
3585+ long idx = m_signal_list_view->GetFirstSelected();
3586+
3587+ wxASSERT( idx >= 0 );
3588+
3589+ m_active_alias->Members().erase( m_active_alias->Members().begin() + idx );
3590+
3591+ m_signal_list_view->DeleteItem( idx );
3592+ m_signal_edit->Clear();
3593+ m_btn_rename_signal->Disable();
3594+ m_btn_remove_signal->Disable();
3595+}
3596+
3597+
3598+wxString DIALOG_BUS_MANAGER::getAliasDisplayText( std::shared_ptr< BUS_ALIAS > aAlias )
3599+{
3600+ wxString name = aAlias->GetName();
3601+ wxFileName sheet_name( aAlias->GetParent()->GetFileName() );
3602+
3603+ name += _T( " (" ) + sheet_name.GetFullName() + _T( ")" );
3604+
3605+ return name;
3606+}
3607+
3608+
3609+// see invoke_sch_dialog.h
3610+void InvokeDialogBusManager( SCH_EDIT_FRAME* aCaller )
3611+{
3612+ DIALOG_BUS_MANAGER dlg( aCaller );
3613+ dlg.ShowModal();
3614+}
3615diff --git a/eeschema/dialogs/dialog_bus_manager.h b/eeschema/dialogs/dialog_bus_manager.h
3616new file mode 100644
3617index 0000000..4f7bdbb
3618--- /dev/null
3619+++ b/eeschema/dialogs/dialog_bus_manager.h
3620@@ -0,0 +1,90 @@
3621+/*
3622+ * This program source code file is part of KiCad, a free EDA CAD application.
3623+ *
3624+ * Copyright (C) 2018 CERN
3625+ * @author Jon Evans <jon@craftyjon.com>
3626+ *
3627+ * This program is free software: you can redistribute it and/or modify it
3628+ * under the terms of the GNU General Public License as published by the
3629+ * Free Software Foundation, either version 3 of the License, or (at your
3630+ * option) any later version.
3631+ *
3632+ * This program is distributed in the hope that it will be useful, but
3633+ * WITHOUT ANY WARRANTY; without even the implied warranty of
3634+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3635+ * General Public License for more details.
3636+ *
3637+ * You should have received a copy of the GNU General Public License along
3638+ * with this program. If not, see <http://www.gnu.org/licenses/>.
3639+ */
3640+
3641+#ifndef _DIALOG_BUS_MANAGER_H_
3642+#define _DIALOG_BUS_MANAGER_H_
3643+
3644+#include "dialog_shim.h"
3645+
3646+#include <sch_edit_frame.h>
3647+#include <wx/listctrl.h>
3648+
3649+#include <bus_alias.h>
3650+
3651+class DIALOG_BUS_MANAGER : public DIALOG_SHIM
3652+{
3653+public:
3654+ DIALOG_BUS_MANAGER( SCH_EDIT_FRAME* aParent );
3655+
3656+ ~DIALOG_BUS_MANAGER() {}
3657+
3658+ bool TransferDataFromWindow() override;
3659+
3660+ bool TransferDataToWindow() override;
3661+
3662+ void OnAddBus( wxCommandEvent& aEvent );
3663+ void OnRenameBus( wxCommandEvent& aEvent );
3664+ void OnRemoveBus( wxCommandEvent& aEvent );
3665+
3666+ void OnAddSignal( wxCommandEvent& aEvent );
3667+ void OnRenameSignal( wxCommandEvent& aEvent );
3668+ void OnRemoveSignal( wxCommandEvent& aEvent );
3669+
3670+protected:
3671+
3672+ void OnInitDialog( wxInitDialogEvent& aEvent );
3673+
3674+ void OnSelectBus( wxListEvent& event );
3675+
3676+ void OnSelectSignal( wxListEvent& event );
3677+
3678+ SCH_EDIT_FRAME* m_parent;
3679+
3680+ wxListView* m_bus_list_view;
3681+ wxListView* m_signal_list_view;
3682+ wxTextCtrl* m_bus_edit;
3683+ wxTextCtrl* m_signal_edit;
3684+
3685+ wxButton* m_btn_add_bus;
3686+ wxButton* m_btn_rename_bus;
3687+ wxButton* m_btn_remove_bus;
3688+
3689+ wxButton* m_btn_add_signal;
3690+ wxButton* m_btn_rename_signal;
3691+ wxButton* m_btn_remove_signal;
3692+
3693+private:
3694+ virtual void OnOkClick( wxCommandEvent& aEvent );
3695+
3696+ virtual void OnCancelClick( wxCommandEvent& aEvent );
3697+
3698+ wxString getAliasDisplayText( std::shared_ptr< BUS_ALIAS > aAlias );
3699+
3700+ std::vector< std::shared_ptr< BUS_ALIAS > > m_aliases;
3701+
3702+ std::shared_ptr< BUS_ALIAS > m_active_alias;
3703+
3704+ DECLARE_EVENT_TABLE()
3705+};
3706+
3707+
3708+#endif
3709+
3710+// _DIALOG_BUS_MANAGER_H_
3711diff --git a/eeschema/dialogs/dialog_edit_label.cpp b/eeschema/dialogs/dialog_edit_label.cpp
3712index 3e68a27..8bf27b3 100644
3713--- a/eeschema/dialogs/dialog_edit_label.cpp
3714+++ b/eeschema/dialogs/dialog_edit_label.cpp
3715@@ -29,10 +29,9 @@
3716 */
3717
3718 #include <fctsys.h>
3719-#include <wx/valgen.h>
3720-#include <wx/valnum.h>
3721 #include <sch_edit_frame.h>
3722 #include <base_units.h>
3723+#include <validators.h>
3724
3725 #include <sch_draw_panel.h>
3726 #include <general.h>
3727@@ -87,6 +86,7 @@ private:
3728 wxWindow* m_activeTextCtrl;
3729 wxTextEntry* m_activeTextEntry;
3730 UNIT_BINDER m_textSize;
3731+ REGEX_VALIDATOR m_netNameValidator;
3732 };
3733
3734
3735@@ -108,7 +108,9 @@ const int MAX_TEXTSIZE = INT_MAX;
3736
3737 DIALOG_LABEL_EDITOR::DIALOG_LABEL_EDITOR( SCH_EDIT_FRAME* aParent, SCH_TEXT* aTextItem ) :
3738 DIALOG_LABEL_EDITOR_BASE( aParent ),
3739- m_textSize( aParent, m_textSizeLabel, m_textSizeCtrl, m_textSizeUnits, false )
3740+ m_textSize( aParent, m_textSizeLabel, m_textSizeCtrl, m_textSizeUnits, false ),
3741+ // first part matches single nets and bus vector, the second part matches complex buses
3742+ m_netNameValidator( "([^/ ]+)|({[^/]+})" )
3743 {
3744 m_Parent = aParent;
3745 m_CurrentText = aTextItem;
3746@@ -151,8 +153,9 @@ DIALOG_LABEL_EDITOR::DIALOG_LABEL_EDITOR( SCH_EDIT_FRAME* aParent, SCH_TEXT* aTe
3747
3748 SetInitialFocus( m_activeTextCtrl );
3749
3750+ // Enable validator for net names
3751 if( m_CurrentText->Type() != SCH_TEXT_T )
3752- ( (wxTextValidator*) m_activeTextCtrl->GetValidator() )->SetCharExcludes( wxT( " /" ) );
3753+ m_activeTextCtrl->SetValidator( m_netNameValidator );
3754
3755 m_TextShape->Show( m_CurrentText->Type() == SCH_GLOBAL_LABEL_T ||
3756 m_CurrentText->Type() == SCH_HIERARCHICAL_LABEL_T );
3757diff --git a/eeschema/dialogs/dialog_edit_label_base.cpp b/eeschema/dialogs/dialog_edit_label_base.cpp
3758index b158562..1f77fbf 100644
3759--- a/eeschema/dialogs/dialog_edit_label_base.cpp
3760+++ b/eeschema/dialogs/dialog_edit_label_base.cpp
3761@@ -1,5 +1,5 @@
3762 ///////////////////////////////////////////////////////////////////////////
3763-// C++ code generated with wxFormBuilder (version Aug 4 2017)
3764+// C++ code generated with wxFormBuilder (version Oct 17 2016)
3765 // http://www.wxformbuilder.org/
3766 //
3767 // PLEASE DO "NOT" EDIT THIS FILE!
3768@@ -31,8 +31,6 @@ DIALOG_LABEL_EDITOR_BASE::DIALOG_LABEL_EDITOR_BASE( wxWindow* parent, wxWindowID
3769 m_valueSingleLine = new wxTextCtrl( this, wxID_VALUESINGLE, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER|wxTE_RICH );
3770 m_valueSingleLine->SetMinSize( wxSize( 360,-1 ) );
3771
3772- m_valueSingleLine->SetValidator( wxTextValidator( wxFILTER_EXCLUDE_CHAR_LIST, &m_labelText ) );
3773-
3774 m_textEntrySizer->Add( m_valueSingleLine, 0, wxEXPAND|wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT, 3 );
3775
3776 m_labelMultiLine = new wxStaticText( this, wxID_ANY, _("Text:"), wxDefaultPosition, wxDefaultSize, 0 );
3777diff --git a/eeschema/dialogs/dialog_edit_label_base.fbp b/eeschema/dialogs/dialog_edit_label_base.fbp
3778index 6e8bb77..f718add 100644
3779--- a/eeschema/dialogs/dialog_edit_label_base.fbp
3780+++ b/eeschema/dialogs/dialog_edit_label_base.fbp
3781@@ -14,7 +14,6 @@
3782 <property name="file">dialog_edit_label_base</property>
3783 <property name="first_id">1000</property>
3784 <property name="help_provider">none</property>
3785- <property name="indent_with_spaces"></property>
3786 <property name="internationalize">1</property>
3787 <property name="name">dialog_edit_label_base</property>
3788 <property name="namespace"></property>
3789@@ -247,10 +246,10 @@
3790 <property name="subclass"></property>
3791 <property name="toolbar_pane">0</property>
3792 <property name="tooltip"></property>
3793- <property name="validator_data_type">wxString</property>
3794- <property name="validator_style">wxFILTER_EXCLUDE_CHAR_LIST</property>
3795- <property name="validator_type">wxTextValidator</property>
3796- <property name="validator_variable">m_labelText</property>
3797+ <property name="validator_data_type"></property>
3798+ <property name="validator_style">wxFILTER_NONE</property>
3799+ <property name="validator_type">wxDefaultValidator</property>
3800+ <property name="validator_variable"></property>
3801 <property name="value"></property>
3802 <property name="window_extra_style"></property>
3803 <property name="window_name"></property>
3804@@ -606,8 +605,6 @@
3805 <property name="window_style"></property>
3806 <event name="OnChar"></event>
3807 <event name="OnCombobox"></event>
3808- <event name="OnComboboxCloseup"></event>
3809- <event name="OnComboboxDropdown"></event>
3810 <event name="OnEnterWindow"></event>
3811 <event name="OnEraseBackground"></event>
3812 <event name="OnKeyDown"></event>
3813diff --git a/eeschema/dialogs/dialog_edit_label_base.h b/eeschema/dialogs/dialog_edit_label_base.h
3814index 719d6ce..5f5d1f0 100644
3815--- a/eeschema/dialogs/dialog_edit_label_base.h
3816+++ b/eeschema/dialogs/dialog_edit_label_base.h
3817@@ -1,5 +1,5 @@
3818 ///////////////////////////////////////////////////////////////////////////
3819-// C++ code generated with wxFormBuilder (version Aug 4 2017)
3820+// C++ code generated with wxFormBuilder (version Oct 17 2016)
3821 // http://www.wxformbuilder.org/
3822 //
3823 // PLEASE DO "NOT" EDIT THIS FILE!
3824@@ -21,8 +21,8 @@ class DIALOG_SHIM;
3825 #include <wx/colour.h>
3826 #include <wx/settings.h>
3827 #include <wx/textctrl.h>
3828-#include <wx/valtext.h>
3829 #include <wx/combobox.h>
3830+#include <wx/valtext.h>
3831 #include <wx/sizer.h>
3832 #include <wx/radiobox.h>
3833 #include <wx/button.h>
3834@@ -67,7 +67,6 @@ class DIALOG_LABEL_EDITOR_BASE : public DIALOG_SHIM
3835
3836
3837 public:
3838- wxString m_labelText;
3839 wxString m_comboText;
3840
3841 DIALOG_LABEL_EDITOR_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Text Editor"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER );
3842diff --git a/eeschema/dialogs/dialog_erc.cpp b/eeschema/dialogs/dialog_erc.cpp
3843index 26f83b4..fb8338f 100644
3844--- a/eeschema/dialogs/dialog_erc.cpp
3845+++ b/eeschema/dialogs/dialog_erc.cpp
3846@@ -48,6 +48,7 @@
3847 #include <sch_sheet.h>
3848 #include <lib_pin.h>
3849 #include <sch_component.h>
3850+#include <connection_graph.h>
3851
3852 #include <dialog_erc.h>
3853 #include <erc.h>
3854@@ -58,10 +59,7 @@ extern int DiagErc[PINTYPE_COUNT][PINTYPE_COUNT];
3855 extern int DefaultDiagErc[PINTYPE_COUNT][PINTYPE_COUNT];
3856
3857
3858-bool DIALOG_ERC::m_writeErcFile = false; // saved only for the current session
3859-bool DIALOG_ERC::m_TestSimilarLabels = true; // Save in project config
3860 bool DIALOG_ERC::m_diagErcTableInit = false; // saved only for the current session
3861-bool DIALOG_ERC::m_tstUniqueGlobalLabels = true; // saved only for the current session
3862
3863 // Control identifiers for events
3864 #define ID_MATRIX_0 1800
3865@@ -102,8 +100,13 @@ DIALOG_ERC::DIALOG_ERC( SCH_EDIT_FRAME* parent ) :
3866
3867 DIALOG_ERC::~DIALOG_ERC()
3868 {
3869- m_TestSimilarLabels = m_cbTestSimilarLabels->GetValue();
3870- m_tstUniqueGlobalLabels = m_cbTestUniqueGlbLabels->GetValue();
3871+ transferControlsToSettings();
3872+
3873+ if( m_settings != m_parent->GetErcSettings() )
3874+ {
3875+ m_parent->UpdateErcSettings( m_settings );
3876+ m_parent->SaveProjectSettings( false );
3877+ }
3878 }
3879
3880
3881@@ -117,9 +120,8 @@ void DIALOG_ERC::Init()
3882 m_buttonList[ii][jj] = NULL;
3883 }
3884
3885- m_WriteResultOpt->SetValue( m_writeErcFile );
3886- m_cbTestSimilarLabels->SetValue( m_TestSimilarLabels );
3887- m_cbTestUniqueGlbLabels->SetValue( m_tstUniqueGlobalLabels );
3888+ m_settings = m_parent->GetErcSettings();
3889+ transferSettingsToControls();
3890
3891 SCH_SCREENS screens;
3892 updateMarkerCounts( &screens );
3893@@ -131,6 +133,30 @@ void DIALOG_ERC::Init()
3894 }
3895
3896
3897+void DIALOG_ERC::transferSettingsToControls()
3898+{
3899+ m_WriteResultOpt->SetValue( m_settings.write_erc_file );
3900+ m_cbTestSimilarLabels->SetValue( m_settings.check_similar_labels );
3901+ m_cbTestUniqueGlbLabels->SetValue( m_settings.check_unique_global_labels );
3902+ m_cbCheckBusDriverConflicts->SetValue( m_settings.check_bus_driver_conflicts );
3903+ m_cbCheckBusEntries->SetValue( m_settings.check_bus_entry_conflicts );
3904+ m_cbCheckBusToBusConflicts->SetValue( m_settings.check_bus_to_bus_conflicts );
3905+ m_cbCheckBusToNetConflicts->SetValue( m_settings.check_bus_to_net_conflicts );
3906+}
3907+
3908+
3909+void DIALOG_ERC::transferControlsToSettings()
3910+{
3911+ m_settings.write_erc_file = m_WriteResultOpt->GetValue();
3912+ m_settings.check_similar_labels = m_cbTestSimilarLabels->GetValue();
3913+ m_settings.check_unique_global_labels = m_cbTestUniqueGlbLabels->GetValue();
3914+ m_settings.check_bus_driver_conflicts = m_cbCheckBusDriverConflicts->GetValue();
3915+ m_settings.check_bus_entry_conflicts = m_cbCheckBusEntries->GetValue();
3916+ m_settings.check_bus_to_bus_conflicts = m_cbCheckBusToBusConflicts->GetValue();
3917+ m_settings.check_bus_to_net_conflicts = m_cbCheckBusToNetConflicts->GetValue();
3918+}
3919+
3920+
3921 void DIALOG_ERC::updateMarkerCounts( SCH_SCREENS *screens )
3922 {
3923 int markers = screens->GetMarkerCount( MARKER_BASE::MARKER_ERC,
3924@@ -436,10 +462,8 @@ void DIALOG_ERC::ResetDefaultERCDiag( wxCommandEvent& event )
3925 {
3926 memcpy( DiagErc, DefaultDiagErc, sizeof( DiagErc ) );
3927 ReBuildMatrixPanel();
3928- m_TestSimilarLabels = true;
3929- m_cbTestSimilarLabels->SetValue( m_TestSimilarLabels );
3930- m_tstUniqueGlobalLabels = true;
3931- m_cbTestUniqueGlbLabels->SetValue( m_tstUniqueGlobalLabels );
3932+ m_settings.LoadDefaults();
3933+ transferSettingsToControls();
3934 }
3935
3936
3937@@ -483,9 +507,7 @@ void DIALOG_ERC::TestErc( REPORTER& aReporter )
3938 {
3939 wxFileName fn;
3940
3941- m_writeErcFile = m_WriteResultOpt->GetValue();
3942- m_TestSimilarLabels = m_cbTestSimilarLabels->GetValue();
3943- m_tstUniqueGlobalLabels = m_cbTestUniqueGlbLabels->GetValue();
3944+ transferControlsToSettings();
3945
3946 // Build the whole sheet list in hierarchy (sheet, not screen)
3947 SCH_SHEET_LIST sheets( g_RootSheet );
3948@@ -509,6 +531,12 @@ void DIALOG_ERC::TestErc( REPORTER& aReporter )
3949 */
3950 TestDuplicateSheetNames( true );
3951
3952+ TestConflictingBusAliases();
3953+
3954+ // The connection graph has a whole set of ERC checks it can run
3955+ m_parent->RecalculateConnections();
3956+ g_ConnectionGraph->RunERC( m_settings );
3957+
3958 /* Test is all units of each multiunit component have the same footprint assigned.
3959 */
3960 TestMultiunitFootprints( sheets );
3961@@ -518,8 +546,8 @@ void DIALOG_ERC::TestErc( REPORTER& aReporter )
3962 // Reset the connection type indicator
3963 objectsConnectedList->ResetConnectionsType();
3964
3965- unsigned lastItemIdx;
3966- unsigned nextItemIdx = lastItemIdx = 0;
3967+ unsigned lastItemIdx = 0;
3968+ unsigned nextItemIdx = 0;
3969 int MinConn = NOC;
3970
3971 /* Check that a pin appears in only one net. This check is necessary
3972@@ -567,30 +595,7 @@ void DIALOG_ERC::TestErc( REPORTER& aReporter )
3973 case NET_GLOBBUSLABELMEMBER:
3974 break;
3975
3976- case NET_HIERLABEL:
3977- case NET_HIERBUSLABELMEMBER:
3978- case NET_SHEETLABEL:
3979- case NET_SHEETBUSLABELMEMBER:
3980- // ERC problems when pin sheets do not match hierarchical labels.
3981- // Each pin sheet must match a hierarchical label
3982- // Each hierarchical label must match a pin sheet
3983- objectsConnectedList->TestforNonOrphanLabel( itemIdx, nextItemIdx );
3984- break;
3985- case NET_GLOBLABEL:
3986- if( m_tstUniqueGlobalLabels )
3987- objectsConnectedList->TestforNonOrphanLabel( itemIdx, nextItemIdx );
3988- break;
3989-
3990- case NET_NOCONNECT:
3991-
3992- // ERC problems when a noconnect symbol is connected to more than one pin.
3993- MinConn = NET_NC;
3994-
3995- if( objectsConnectedList->CountPinsInNet( nextItemIdx ) > 1 )
3996- Diagnose( item, NULL, MinConn, UNC );
3997-
3998- break;
3999-
4000+ // TODO(JE) Port this to the new system
4001 case NET_PIN:
4002 {
4003 // Check if this pin has appeared before on a different net
4004@@ -623,6 +628,8 @@ void DIALOG_ERC::TestErc( REPORTER& aReporter )
4005 TestOthersItems( objectsConnectedList.get(), itemIdx, nextItemIdx, &MinConn );
4006 break;
4007 }
4008+ default:
4009+ break;
4010 }
4011
4012 lastItemIdx = itemIdx;
4013@@ -630,7 +637,7 @@ void DIALOG_ERC::TestErc( REPORTER& aReporter )
4014
4015 // Test similar labels (i;e. labels which are identical when
4016 // using case insensitive comparisons)
4017- if( m_TestSimilarLabels )
4018+ if( m_settings.check_similar_labels )
4019 objectsConnectedList->TestforSimilarLabels();
4020
4021 // Displays global results:
4022@@ -653,7 +660,7 @@ void DIALOG_ERC::TestErc( REPORTER& aReporter )
4023 // Display message
4024 aReporter.ReportTail( _( "Finished" ), REPORTER::RPT_INFO );
4025
4026- if( m_writeErcFile )
4027+ if( m_settings.write_erc_file )
4028 {
4029 fn = g_RootSheet->GetScreen()->GetFileName();
4030 fn.SetExt( wxT( "erc" ) );
4031diff --git a/eeschema/dialogs/dialog_erc.h b/eeschema/dialogs/dialog_erc.h
4032index b0c1335..a7736bf 100644
4033--- a/eeschema/dialogs/dialog_erc.h
4034+++ b/eeschema/dialogs/dialog_erc.h
4035@@ -30,6 +30,7 @@
4036 #include <lib_pin.h> // For PINTYPE_COUNT definition
4037
4038 #include <dialog_erc_base.h>
4039+#include <erc_settings.h>
4040 #include "dialog_erc_listbox.h"
4041
4042 // DIALOG_ERC class declaration
4043@@ -43,12 +44,8 @@ private:
4044 wxBitmapButton* m_buttonList[PINTYPE_COUNT][PINTYPE_COUNT];
4045 bool m_initialized;
4046 const SCH_MARKER* m_lastMarkerFound;
4047- static bool m_writeErcFile;
4048 static bool m_diagErcTableInit; // go to true after DiagErc init
4049- static bool m_tstUniqueGlobalLabels;
4050-
4051-public:
4052- static bool m_TestSimilarLabels;
4053+ ERC_SETTINGS m_settings;
4054
4055 public:
4056 DIALOG_ERC( SCH_EDIT_FRAME* parent );
4057@@ -79,6 +76,8 @@ private:
4058 void ReBuildMatrixPanel();
4059 void setDRCMatrixButtonState( wxBitmapButton *aButton, int aState );
4060 void updateMarkerCounts( SCH_SCREENS *screens );
4061+ void transferSettingsToControls();
4062+ void transferControlsToSettings();
4063 };
4064
4065
4066diff --git a/eeschema/dialogs/dialog_erc_base.cpp b/eeschema/dialogs/dialog_erc_base.cpp
4067index 0d39fca..e896b5f 100644
4068--- a/eeschema/dialogs/dialog_erc_base.cpp
4069+++ b/eeschema/dialogs/dialog_erc_base.cpp
4070@@ -1,5 +1,5 @@
4071 ///////////////////////////////////////////////////////////////////////////
4072-// C++ code generated with wxFormBuilder (version Jan 17 2019)
4073+// C++ code generated with wxFormBuilder (version Feb 17 2019)
4074 // http://www.wxformbuilder.org/
4075 //
4076 // PLEASE DO *NOT* EDIT THIS FILE!
4077@@ -131,6 +131,24 @@ DIALOG_ERC_BASE::DIALOG_ERC_BASE( wxWindow* parent, wxWindowID id, const wxStrin
4078
4079 m_panelMatrixSizer->Add( sbSizer3, 0, wxALL|wxEXPAND, 5 );
4080
4081+ wxStaticBoxSizer* sbSizer31;
4082+ sbSizer31 = new wxStaticBoxSizer( new wxStaticBox( m_PanelERCOptions, wxID_ANY, _("Bus Connections") ), wxVERTICAL );
4083+
4084+ m_cbCheckBusToNetConflicts = new wxCheckBox( sbSizer31->GetStaticBox(), wxID_ANY, _("Check that bus wires are not connected to hierarchical net pins and vice versa"), wxDefaultPosition, wxDefaultSize, 0 );
4085+ sbSizer31->Add( m_cbCheckBusToNetConflicts, 0, wxALL, 5 );
4086+
4087+ m_cbCheckBusToBusConflicts = new wxCheckBox( sbSizer31->GetStaticBox(), wxID_ANY, _("Check that bus-to-bus connections have shared members"), wxDefaultPosition, wxDefaultSize, 0 );
4088+ sbSizer31->Add( m_cbCheckBusToBusConflicts, 0, wxALL, 5 );
4089+
4090+ m_cbCheckBusEntries = new wxCheckBox( sbSizer31->GetStaticBox(), wxID_ANY, _("Check that nets are members of buses they graphically connect to"), wxDefaultPosition, wxDefaultSize, 0 );
4091+ sbSizer31->Add( m_cbCheckBusEntries, 0, wxALL, 5 );
4092+
4093+ m_cbCheckBusDriverConflicts = new wxCheckBox( sbSizer31->GetStaticBox(), wxID_ANY, _("Check buses for conflicting drivers"), wxDefaultPosition, wxDefaultSize, 0 );
4094+ sbSizer31->Add( m_cbCheckBusDriverConflicts, 0, wxALL, 5 );
4095+
4096+
4097+ m_panelMatrixSizer->Add( sbSizer31, 0, wxALL|wxEXPAND, 5 );
4098+
4099
4100 m_PanelERCOptions->SetSizer( m_panelMatrixSizer );
4101 m_PanelERCOptions->Layout();
4102diff --git a/eeschema/dialogs/dialog_erc_base.fbp b/eeschema/dialogs/dialog_erc_base.fbp
4103index fa6ef7b..fcab33b 100644
4104--- a/eeschema/dialogs/dialog_erc_base.fbp
4105+++ b/eeschema/dialogs/dialog_erc_base.fbp
4106@@ -1318,6 +1318,373 @@
4107 </object>
4108 </object>
4109 </object>
4110+ <object class="sizeritem" expanded="1">
4111+ <property name="border">5</property>
4112+ <property name="flag">wxALL|wxEXPAND</property>
4113+ <property name="proportion">0</property>
4114+ <object class="wxStaticBoxSizer" expanded="1">
4115+ <property name="id">wxID_ANY</property>
4116+ <property name="label">Bus Connections</property>
4117+ <property name="minimum_size"></property>
4118+ <property name="name">sbSizer31</property>
4119+ <property name="orient">wxVERTICAL</property>
4120+ <property name="parent">1</property>
4121+ <property name="permission">none</property>
4122+ <event name="OnUpdateUI"></event>
4123+ <object class="sizeritem" expanded="1">
4124+ <property name="border">5</property>
4125+ <property name="flag">wxALL</property>
4126+ <property name="proportion">0</property>
4127+ <object class="wxCheckBox" expanded="1">
4128+ <property name="BottomDockable">1</property>
4129+ <property name="LeftDockable">1</property>
4130+ <property name="RightDockable">1</property>
4131+ <property name="TopDockable">1</property>
4132+ <property name="aui_layer"></property>
4133+ <property name="aui_name"></property>
4134+ <property name="aui_position"></property>
4135+ <property name="aui_row"></property>
4136+ <property name="best_size"></property>
4137+ <property name="bg"></property>
4138+ <property name="caption"></property>
4139+ <property name="caption_visible">1</property>
4140+ <property name="center_pane">0</property>
4141+ <property name="checked">0</property>
4142+ <property name="close_button">1</property>
4143+ <property name="context_help"></property>
4144+ <property name="context_menu">1</property>
4145+ <property name="default_pane">0</property>
4146+ <property name="dock">Dock</property>
4147+ <property name="dock_fixed">0</property>
4148+ <property name="docking">Left</property>
4149+ <property name="enabled">1</property>
4150+ <property name="fg"></property>
4151+ <property name="floatable">1</property>
4152+ <property name="font"></property>
4153+ <property name="gripper">0</property>
4154+ <property name="hidden">0</property>
4155+ <property name="id">wxID_ANY</property>
4156+ <property name="label">Check that bus wires are not connected to hierarchical net pins and vice versa</property>
4157+ <property name="max_size"></property>
4158+ <property name="maximize_button">0</property>
4159+ <property name="maximum_size"></property>
4160+ <property name="min_size"></property>
4161+ <property name="minimize_button">0</property>
4162+ <property name="minimum_size"></property>
4163+ <property name="moveable">1</property>
4164+ <property name="name">m_cbCheckBusToNetConflicts</property>
4165+ <property name="pane_border">1</property>
4166+ <property name="pane_position"></property>
4167+ <property name="pane_size"></property>
4168+ <property name="permission">protected</property>
4169+ <property name="pin_button">1</property>
4170+ <property name="pos"></property>
4171+ <property name="resize">Resizable</property>
4172+ <property name="show">1</property>
4173+ <property name="size"></property>
4174+ <property name="style"></property>
4175+ <property name="subclass"></property>
4176+ <property name="toolbar_pane">0</property>
4177+ <property name="tooltip"></property>
4178+ <property name="validator_data_type"></property>
4179+ <property name="validator_style">wxFILTER_NONE</property>
4180+ <property name="validator_type">wxDefaultValidator</property>
4181+ <property name="validator_variable"></property>
4182+ <property name="window_extra_style"></property>
4183+ <property name="window_name"></property>
4184+ <property name="window_style"></property>
4185+ <event name="OnChar"></event>
4186+ <event name="OnCheckBox"></event>
4187+ <event name="OnEnterWindow"></event>
4188+ <event name="OnEraseBackground"></event>
4189+ <event name="OnKeyDown"></event>
4190+ <event name="OnKeyUp"></event>
4191+ <event name="OnKillFocus"></event>
4192+ <event name="OnLeaveWindow"></event>
4193+ <event name="OnLeftDClick"></event>
4194+ <event name="OnLeftDown"></event>
4195+ <event name="OnLeftUp"></event>
4196+ <event name="OnMiddleDClick"></event>
4197+ <event name="OnMiddleDown"></event>
4198+ <event name="OnMiddleUp"></event>
4199+ <event name="OnMotion"></event>
4200+ <event name="OnMouseEvents"></event>
4201+ <event name="OnMouseWheel"></event>
4202+ <event name="OnPaint"></event>
4203+ <event name="OnRightDClick"></event>
4204+ <event name="OnRightDown"></event>
4205+ <event name="OnRightUp"></event>
4206+ <event name="OnSetFocus"></event>
4207+ <event name="OnSize"></event>
4208+ <event name="OnUpdateUI"></event>
4209+ </object>
4210+ </object>
4211+ <object class="sizeritem" expanded="1">
4212+ <property name="border">5</property>
4213+ <property name="flag">wxALL</property>
4214+ <property name="proportion">0</property>
4215+ <object class="wxCheckBox" expanded="1">
4216+ <property name="BottomDockable">1</property>
4217+ <property name="LeftDockable">1</property>
4218+ <property name="RightDockable">1</property>
4219+ <property name="TopDockable">1</property>
4220+ <property name="aui_layer"></property>
4221+ <property name="aui_name"></property>
4222+ <property name="aui_position"></property>
4223+ <property name="aui_row"></property>
4224+ <property name="best_size"></property>
4225+ <property name="bg"></property>
4226+ <property name="caption"></property>
4227+ <property name="caption_visible">1</property>
4228+ <property name="center_pane">0</property>
4229+ <property name="checked">0</property>
4230+ <property name="close_button">1</property>
4231+ <property name="context_help"></property>
4232+ <property name="context_menu">1</property>
4233+ <property name="default_pane">0</property>
4234+ <property name="dock">Dock</property>
4235+ <property name="dock_fixed">0</property>
4236+ <property name="docking">Left</property>
4237+ <property name="enabled">1</property>
4238+ <property name="fg"></property>
4239+ <property name="floatable">1</property>
4240+ <property name="font"></property>
4241+ <property name="gripper">0</property>
4242+ <property name="hidden">0</property>
4243+ <property name="id">wxID_ANY</property>
4244+ <property name="label">Check that bus-to-bus connections have shared members</property>
4245+ <property name="max_size"></property>
4246+ <property name="maximize_button">0</property>
4247+ <property name="maximum_size"></property>
4248+ <property name="min_size"></property>
4249+ <property name="minimize_button">0</property>
4250+ <property name="minimum_size"></property>
4251+ <property name="moveable">1</property>
4252+ <property name="name">m_cbCheckBusToBusConflicts</property>
4253+ <property name="pane_border">1</property>
4254+ <property name="pane_position"></property>
4255+ <property name="pane_size"></property>
4256+ <property name="permission">protected</property>
4257+ <property name="pin_button">1</property>
4258+ <property name="pos"></property>
4259+ <property name="resize">Resizable</property>
4260+ <property name="show">1</property>
4261+ <property name="size"></property>
4262+ <property name="style"></property>
4263+ <property name="subclass"></property>
4264+ <property name="toolbar_pane">0</property>
4265+ <property name="tooltip"></property>
4266+ <property name="validator_data_type"></property>
4267+ <property name="validator_style">wxFILTER_NONE</property>
4268+ <property name="validator_type">wxDefaultValidator</property>
4269+ <property name="validator_variable"></property>
4270+ <property name="window_extra_style"></property>
4271+ <property name="window_name"></property>
4272+ <property name="window_style"></property>
4273+ <event name="OnChar"></event>
4274+ <event name="OnCheckBox"></event>
4275+ <event name="OnEnterWindow"></event>
4276+ <event name="OnEraseBackground"></event>
4277+ <event name="OnKeyDown"></event>
4278+ <event name="OnKeyUp"></event>
4279+ <event name="OnKillFocus"></event>
4280+ <event name="OnLeaveWindow"></event>
4281+ <event name="OnLeftDClick"></event>
4282+ <event name="OnLeftDown"></event>
4283+ <event name="OnLeftUp"></event>
4284+ <event name="OnMiddleDClick"></event>
4285+ <event name="OnMiddleDown"></event>
4286+ <event name="OnMiddleUp"></event>
4287+ <event name="OnMotion"></event>
4288+ <event name="OnMouseEvents"></event>
4289+ <event name="OnMouseWheel"></event>
4290+ <event name="OnPaint"></event>
4291+ <event name="OnRightDClick"></event>
4292+ <event name="OnRightDown"></event>
4293+ <event name="OnRightUp"></event>
4294+ <event name="OnSetFocus"></event>
4295+ <event name="OnSize"></event>
4296+ <event name="OnUpdateUI"></event>
4297+ </object>
4298+ </object>
4299+ <object class="sizeritem" expanded="1">
4300+ <property name="border">5</property>
4301+ <property name="flag">wxALL</property>
4302+ <property name="proportion">0</property>
4303+ <object class="wxCheckBox" expanded="1">
4304+ <property name="BottomDockable">1</property>
4305+ <property name="LeftDockable">1</property>
4306+ <property name="RightDockable">1</property>
4307+ <property name="TopDockable">1</property>
4308+ <property name="aui_layer"></property>
4309+ <property name="aui_name"></property>
4310+ <property name="aui_position"></property>
4311+ <property name="aui_row"></property>
4312+ <property name="best_size"></property>
4313+ <property name="bg"></property>
4314+ <property name="caption"></property>
4315+ <property name="caption_visible">1</property>
4316+ <property name="center_pane">0</property>
4317+ <property name="checked">0</property>
4318+ <property name="close_button">1</property>
4319+ <property name="context_help"></property>
4320+ <property name="context_menu">1</property>
4321+ <property name="default_pane">0</property>
4322+ <property name="dock">Dock</property>
4323+ <property name="dock_fixed">0</property>
4324+ <property name="docking">Left</property>
4325+ <property name="enabled">1</property>
4326+ <property name="fg"></property>
4327+ <property name="floatable">1</property>
4328+ <property name="font"></property>
4329+ <property name="gripper">0</property>
4330+ <property name="hidden">0</property>
4331+ <property name="id">wxID_ANY</property>
4332+ <property name="label">Check that nets are members of buses they graphically connect to</property>
4333+ <property name="max_size"></property>
4334+ <property name="maximize_button">0</property>
4335+ <property name="maximum_size"></property>
4336+ <property name="min_size"></property>
4337+ <property name="minimize_button">0</property>
4338+ <property name="minimum_size"></property>
4339+ <property name="moveable">1</property>
4340+ <property name="name">m_cbCheckBusEntries</property>
4341+ <property name="pane_border">1</property>
4342+ <property name="pane_position"></property>
4343+ <property name="pane_size"></property>
4344+ <property name="permission">protected</property>
4345+ <property name="pin_button">1</property>
4346+ <property name="pos"></property>
4347+ <property name="resize">Resizable</property>
4348+ <property name="show">1</property>
4349+ <property name="size"></property>
4350+ <property name="style"></property>
4351+ <property name="subclass"></property>
4352+ <property name="toolbar_pane">0</property>
4353+ <property name="tooltip"></property>
4354+ <property name="validator_data_type"></property>
4355+ <property name="validator_style">wxFILTER_NONE</property>
4356+ <property name="validator_type">wxDefaultValidator</property>
4357+ <property name="validator_variable"></property>
4358+ <property name="window_extra_style"></property>
4359+ <property name="window_name"></property>
4360+ <property name="window_style"></property>
4361+ <event name="OnChar"></event>
4362+ <event name="OnCheckBox"></event>
4363+ <event name="OnEnterWindow"></event>
4364+ <event name="OnEraseBackground"></event>
4365+ <event name="OnKeyDown"></event>
4366+ <event name="OnKeyUp"></event>
4367+ <event name="OnKillFocus"></event>
4368+ <event name="OnLeaveWindow"></event>
4369+ <event name="OnLeftDClick"></event>
4370+ <event name="OnLeftDown"></event>
4371+ <event name="OnLeftUp"></event>
4372+ <event name="OnMiddleDClick"></event>
4373+ <event name="OnMiddleDown"></event>
4374+ <event name="OnMiddleUp"></event>
4375+ <event name="OnMotion"></event>
4376+ <event name="OnMouseEvents"></event>
4377+ <event name="OnMouseWheel"></event>
4378+ <event name="OnPaint"></event>
4379+ <event name="OnRightDClick"></event>
4380+ <event name="OnRightDown"></event>
4381+ <event name="OnRightUp"></event>
4382+ <event name="OnSetFocus"></event>
4383+ <event name="OnSize"></event>
4384+ <event name="OnUpdateUI"></event>
4385+ </object>
4386+ </object>
4387+ <object class="sizeritem" expanded="1">
4388+ <property name="border">5</property>
4389+ <property name="flag">wxALL</property>
4390+ <property name="proportion">0</property>
4391+ <object class="wxCheckBox" expanded="1">
4392+ <property name="BottomDockable">1</property>
4393+ <property name="LeftDockable">1</property>
4394+ <property name="RightDockable">1</property>
4395+ <property name="TopDockable">1</property>
4396+ <property name="aui_layer"></property>
4397+ <property name="aui_name"></property>
4398+ <property name="aui_position"></property>
4399+ <property name="aui_row"></property>
4400+ <property name="best_size"></property>
4401+ <property name="bg"></property>
4402+ <property name="caption"></property>
4403+ <property name="caption_visible">1</property>
4404+ <property name="center_pane">0</property>
4405+ <property name="checked">0</property>
4406+ <property name="close_button">1</property>
4407+ <property name="context_help"></property>
4408+ <property name="context_menu">1</property>
4409+ <property name="default_pane">0</property>
4410+ <property name="dock">Dock</property>
4411+ <property name="dock_fixed">0</property>
4412+ <property name="docking">Left</property>
4413+ <property name="enabled">1</property>
4414+ <property name="fg"></property>
4415+ <property name="floatable">1</property>
4416+ <property name="font"></property>
4417+ <property name="gripper">0</property>
4418+ <property name="hidden">0</property>
4419+ <property name="id">wxID_ANY</property>
4420+ <property name="label">Check buses for conflicting drivers</property>
4421+ <property name="max_size"></property>
4422+ <property name="maximize_button">0</property>
4423+ <property name="maximum_size"></property>
4424+ <property name="min_size"></property>
4425+ <property name="minimize_button">0</property>
4426+ <property name="minimum_size"></property>
4427+ <property name="moveable">1</property>
4428+ <property name="name">m_cbCheckBusDriverConflicts</property>
4429+ <property name="pane_border">1</property>
4430+ <property name="pane_position"></property>
4431+ <property name="pane_size"></property>
4432+ <property name="permission">protected</property>
4433+ <property name="pin_button">1</property>
4434+ <property name="pos"></property>
4435+ <property name="resize">Resizable</property>
4436+ <property name="show">1</property>
4437+ <property name="size"></property>
4438+ <property name="style"></property>
4439+ <property name="subclass"></property>
4440+ <property name="toolbar_pane">0</property>
4441+ <property name="tooltip"></property>
4442+ <property name="validator_data_type"></property>
4443+ <property name="validator_style">wxFILTER_NONE</property>
4444+ <property name="validator_type">wxDefaultValidator</property>
4445+ <property name="validator_variable"></property>
4446+ <property name="window_extra_style"></property>
4447+ <property name="window_name"></property>
4448+ <property name="window_style"></property>
4449+ <event name="OnChar"></event>
4450+ <event name="OnCheckBox"></event>
4451+ <event name="OnEnterWindow"></event>
4452+ <event name="OnEraseBackground"></event>
4453+ <event name="OnKeyDown"></event>
4454+ <event name="OnKeyUp"></event>
4455+ <event name="OnKillFocus"></event>
4456+ <event name="OnLeaveWindow"></event>
4457+ <event name="OnLeftDClick"></event>
4458+ <event name="OnLeftDown"></event>
4459+ <event name="OnLeftUp"></event>
4460+ <event name="OnMiddleDClick"></event>
4461+ <event name="OnMiddleDown"></event>
4462+ <event name="OnMiddleUp"></event>
4463+ <event name="OnMotion"></event>
4464+ <event name="OnMouseEvents"></event>
4465+ <event name="OnMouseWheel"></event>
4466+ <event name="OnPaint"></event>
4467+ <event name="OnRightDClick"></event>
4468+ <event name="OnRightDown"></event>
4469+ <event name="OnRightUp"></event>
4470+ <event name="OnSetFocus"></event>
4471+ <event name="OnSize"></event>
4472+ <event name="OnUpdateUI"></event>
4473+ </object>
4474+ </object>
4475+ </object>
4476+ </object>
4477 </object>
4478 </object>
4479 </object>
4480diff --git a/eeschema/dialogs/dialog_erc_base.h b/eeschema/dialogs/dialog_erc_base.h
4481index 1620170..e98944f 100644
4482--- a/eeschema/dialogs/dialog_erc_base.h
4483+++ b/eeschema/dialogs/dialog_erc_base.h
4484@@ -1,5 +1,5 @@
4485 ///////////////////////////////////////////////////////////////////////////
4486-// C++ code generated with wxFormBuilder (version Jan 17 2019)
4487+// C++ code generated with wxFormBuilder (version Feb 17 2019)
4488 // http://www.wxformbuilder.org/
4489 //
4490 // PLEASE DO *NOT* EDIT THIS FILE!
4491@@ -65,6 +65,10 @@ class DIALOG_ERC_BASE : public DIALOG_SHIM
4492 wxCheckBox* m_cbTestUniqueGlbLabels;
4493 wxPanel* m_matrixPanel;
4494 wxButton* m_ResetOptButton;
4495+ wxCheckBox* m_cbCheckBusToNetConflicts;
4496+ wxCheckBox* m_cbCheckBusToBusConflicts;
4497+ wxCheckBox* m_cbCheckBusEntries;
4498+ wxCheckBox* m_cbCheckBusDriverConflicts;
4499 wxButton* m_buttondelmarkers;
4500 wxStdDialogButtonSizer* m_sdbSizer1;
4501 wxButton* m_sdbSizer1OK;
4502diff --git a/eeschema/dialogs/dialog_migrate_buses.cpp b/eeschema/dialogs/dialog_migrate_buses.cpp
4503new file mode 100644
4504index 0000000..c6d63f6
4505--- /dev/null
4506+++ b/eeschema/dialogs/dialog_migrate_buses.cpp
4507@@ -0,0 +1,229 @@
4508+/*
4509+ * This program source code file is part of KiCad, a free EDA CAD application.
4510+ *
4511+ * Copyright (C) 2018 CERN
4512+ * @author Jon Evans <jon@craftyjon.com>
4513+ *
4514+ * This program is free software; you can redistribute it and/or
4515+ * modify it under the terms of the GNU General Public License
4516+ * as published by the Free Software Foundation; either version 2
4517+ * of the License, or (at your option) any later version.
4518+ *
4519+ * This program is distributed in the hope that it will be useful,
4520+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4521+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4522+ * GNU General Public License for more details.
4523+ *
4524+ * You should have received a copy of the GNU General Public License along
4525+ * with this program. If not, see <http://www.gnu.org/licenses/>.
4526+ */
4527+
4528+#include <sch_connection.h>
4529+#include <connection_graph.h>
4530+
4531+#include <dialog_migrate_buses.h>
4532+
4533+/**
4534+ * Migrates buses using legacy multi-label joining behavior.
4535+ *
4536+ * In KiCad verions before 6.0, you were allowed to place multiple labels
4537+ * on a given bus subgraph, and that would have the effect of making those
4538+ * bus descriptions equivalent according to the bus vector number.
4539+ *
4540+ * For example, if the labels PCA[0..15], ADR[0.7], and BUS[5..10] are all
4541+ * attached to the same subgraph, the intention is that there is connectivity
4542+ * between PCA0 and ADR0, between PCA10 and BUS10, and between PCA5, ADR5,
4543+ * and BUS5 (basically connect all the prefix names where the vector numbers
4544+ * line up).
4545+ *
4546+ * This is no longer allowed, because it doesn't map well onto the new
4547+ * bus groups feature and because it is confusing (the netlist will take on
4548+ * one of the possible names and it's impossible to control which one is
4549+ * used).
4550+ *
4551+ * This dialog identifies all of the subgraphs that have this behavior,
4552+ * and corrects them by determining a new name for the subgraph and removing
4553+ * all but one label. The name is determined by choosing a prefix and bus
4554+ * vector notation that can capture all the attached buses. In the above
4555+ * example, the result would need to have the vector notation [0..15] to
4556+ * capture all of the attached buses, and the name could be any of PCA, ADR,
4557+ * or BUS. We present a dialog to the user for them to select which name
4558+ * they want to use.
4559+ */
4560+
4561+
4562+DIALOG_MIGRATE_BUSES::DIALOG_MIGRATE_BUSES( SCH_EDIT_FRAME* aParent ) :
4563+ DIALOG_MIGRATE_BUSES_BASE( aParent ),
4564+ m_frame( aParent )
4565+{
4566+ m_migration_list->Bind( wxEVT_LIST_ITEM_SELECTED,
4567+ &DIALOG_MIGRATE_BUSES::onItemSelected, this );
4568+
4569+ m_btn_accept->Bind( wxEVT_COMMAND_BUTTON_CLICKED,
4570+ &DIALOG_MIGRATE_BUSES::onAcceptClicked, this );
4571+
4572+ loadGraphData();
4573+ updateUi();
4574+
4575+ m_frame->Zoom_Automatique( false );
4576+}
4577+
4578+
4579+void DIALOG_MIGRATE_BUSES::loadGraphData()
4580+{
4581+ m_items.clear();
4582+ auto subgraphs = g_ConnectionGraph->GetBusesNeedingMigration();
4583+
4584+ for( auto subgraph : subgraphs )
4585+ {
4586+ BUS_MIGRATION_STATUS status;
4587+
4588+ status.subgraph = subgraph;
4589+ status.approved = false;
4590+
4591+ auto labels = subgraph->GetBusLabels();
4592+ wxASSERT( labels.size() > 1 );
4593+
4594+ for( auto label : labels )
4595+ status.labels.push_back( static_cast<SCH_TEXT*>( label )->GetText() );
4596+
4597+ status.possible_labels = getProposedLabels( status.labels );
4598+ m_items.push_back( status );
4599+ }
4600+}
4601+
4602+
4603+void DIALOG_MIGRATE_BUSES::updateUi()
4604+{
4605+ m_migration_list->DeleteAllItems();
4606+
4607+ m_migration_list->InsertColumn( 0, _( "Sheet" ) );
4608+ m_migration_list->InsertColumn( 1, _( "Conflicting Labels" ) );
4609+ m_migration_list->InsertColumn( 2, _( "New Label" ) );
4610+ m_migration_list->InsertColumn( 3, _( "Status" ) );
4611+
4612+ for( auto item : m_items )
4613+ {
4614+ wxString old = item.labels[0];
4615+ for( unsigned j = 1; j < item.labels.size(); j++ )
4616+ old << ", " << item.labels[j];
4617+
4618+ auto i = m_migration_list->InsertItem( m_migration_list->GetItemCount(),
4619+ wxEmptyString );
4620+
4621+ m_migration_list->SetItem( i, 0, item.subgraph->m_sheet.PathHumanReadable() );
4622+ m_migration_list->SetItem( i, 1, old );
4623+ m_migration_list->SetItem( i, 2, item.possible_labels[0] );
4624+ m_migration_list->SetItem( i, 3, "" );
4625+ }
4626+
4627+ m_migration_list->Select( 0 );
4628+ m_migration_list->SetColumnWidth( 1, -1 );
4629+}
4630+
4631+
4632+std::vector<wxString> DIALOG_MIGRATE_BUSES::getProposedLabels( std::vector<wxString> aLabelList )
4633+{
4634+ int lowest_start = INT_MAX;
4635+ int highest_end = -1;
4636+ int widest_bus = -1;
4637+
4638+ for( auto label : aLabelList )
4639+ {
4640+ SCH_CONNECTION conn;
4641+ conn.ConfigureFromLabel( label );
4642+
4643+ int start = conn.VectorStart();
4644+ int end = conn.VectorEnd();
4645+
4646+ if( start < lowest_start )
4647+ lowest_start = start;
4648+
4649+ if( end > highest_end )
4650+ highest_end = end;
4651+
4652+ if( end - start + 1 > widest_bus )
4653+ widest_bus = end - start + 1;
4654+ }
4655+
4656+ SCH_CONNECTION conn;
4657+ std::vector<wxString> proposals;
4658+
4659+ for( auto label : aLabelList )
4660+ {
4661+ conn.ConfigureFromLabel( label );
4662+ wxString proposal = conn.VectorPrefix();
4663+ proposal << "[" << highest_end << ".." << lowest_start << "]";
4664+ proposals.push_back( proposal );
4665+ }
4666+
4667+ return proposals;
4668+}
4669+
4670+
4671+void DIALOG_MIGRATE_BUSES::onItemSelected( wxListEvent& aEvent )
4672+{
4673+ unsigned sel = aEvent.GetIndex();
4674+ wxASSERT( sel < m_items.size() );
4675+
4676+ m_selected_index = sel;
4677+
4678+ auto subgraph = m_items[sel].subgraph;
4679+
4680+ auto sheet = subgraph->m_sheet;
4681+ auto driver = subgraph->m_driver;
4682+
4683+ if( sheet != *g_CurrentSheet )
4684+ {
4685+ sheet.LastScreen()->SetZoom( m_frame->GetScreen()->GetZoom() );
4686+ *g_CurrentSheet = sheet;
4687+ g_CurrentSheet->UpdateAllScreenReferences();
4688+ sheet.LastScreen()->TestDanglingEnds();
4689+ }
4690+
4691+ auto pos = driver->GetPosition();
4692+
4693+ m_frame->SetCrossHairPosition( pos );
4694+ m_frame->RedrawScreen( pos, false );
4695+
4696+ m_cb_new_name->Clear();
4697+
4698+ for( auto option : m_items[sel].possible_labels )
4699+ m_cb_new_name->Append( option );
4700+
4701+ m_cb_new_name->Select( 0 );
4702+}
4703+
4704+
4705+void DIALOG_MIGRATE_BUSES::onAcceptClicked( wxCommandEvent& aEvent )
4706+{
4707+ wxASSERT( m_selected_index < m_items.size() );
4708+
4709+ auto sel = m_selected_index;
4710+
4711+ m_items[sel].approved_label = m_cb_new_name->GetStringSelection();
4712+ m_items[sel].approved = true;
4713+
4714+ auto sheet = m_items[sel].subgraph->m_sheet;
4715+ auto screen = sheet.LastScreen();
4716+
4717+ auto labels = m_items[sel].subgraph->GetBusLabels();
4718+
4719+ static_cast<SCH_TEXT*>( labels[0] )->SetText( m_items[sel].approved_label );
4720+
4721+ labels.erase( labels.begin() );
4722+
4723+ for( auto label : labels )
4724+ {
4725+ label->SetFlags( STRUCT_DELETED );
4726+ screen->Remove( label );
4727+ }
4728+
4729+ m_migration_list->SetItem( sel, 2, m_items[sel].approved_label );
4730+ m_migration_list->SetItem( sel, 3, _( "Updated" ) );
4731+
4732+ if( sel < m_items.size() - 1 )
4733+ {
4734+ m_migration_list->Select( sel + 1 );
4735+ }
4736+}
4737diff --git a/eeschema/dialogs/dialog_migrate_buses.h b/eeschema/dialogs/dialog_migrate_buses.h
4738new file mode 100644
4739index 0000000..4038010
4740--- /dev/null
4741+++ b/eeschema/dialogs/dialog_migrate_buses.h
4742@@ -0,0 +1,71 @@
4743+/*
4744+ * This program source code file is part of KiCad, a free EDA CAD application.
4745+ *
4746+ * Copyright (C) 2018 CERN
4747+ * @author Jon Evans <jon@craftyjon.com>
4748+ *
4749+ * This program is free software; you can redistribute it and/or
4750+ * modify it under the terms of the GNU General Public License
4751+ * as published by the Free Software Foundation; either version 2
4752+ * of the License, or (at your option) any later version.
4753+ *
4754+ * This program is distributed in the hope that it will be useful,
4755+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4756+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4757+ * GNU General Public License for more details.
4758+ *
4759+ * You should have received a copy of the GNU General Public License along
4760+ * with this program. If not, see <http://www.gnu.org/licenses/>.
4761+ */
4762+
4763+#ifndef _DIALOG_MIGRATE_BUSES_H
4764+#define _DIALOG_MIGRATE_BUSES_H
4765+
4766+#include <vector>
4767+
4768+#include <sch_edit_frame.h>
4769+
4770+#include <dialog_migrate_buses_base.h>
4771+
4772+class CONNECTION_SUBGRAPH;
4773+
4774+
4775+struct BUS_MIGRATION_STATUS
4776+{
4777+ CONNECTION_SUBGRAPH* subgraph;
4778+
4779+ std::vector<wxString> labels;
4780+
4781+ std::vector<wxString> possible_labels;
4782+
4783+ wxString approved_label;
4784+
4785+ bool approved;
4786+};
4787+
4788+class DIALOG_MIGRATE_BUSES : public DIALOG_MIGRATE_BUSES_BASE
4789+{
4790+public:
4791+
4792+ DIALOG_MIGRATE_BUSES( SCH_EDIT_FRAME* aParent );
4793+
4794+private:
4795+
4796+ SCH_EDIT_FRAME* m_frame;
4797+
4798+ unsigned m_selected_index;
4799+
4800+ void loadGraphData();
4801+
4802+ void updateUi();
4803+
4804+ std::vector<wxString> getProposedLabels( std::vector<wxString> aLabelList );
4805+
4806+ void onItemSelected( wxListEvent& aEvent );
4807+
4808+ void onAcceptClicked( wxCommandEvent& aEvent );
4809+
4810+ std::vector<BUS_MIGRATION_STATUS> m_items;
4811+};
4812+
4813+#endif
4814diff --git a/eeschema/dialogs/dialog_migrate_buses_base.cpp b/eeschema/dialogs/dialog_migrate_buses_base.cpp
4815new file mode 100644
4816index 0000000..46cdf22
4817--- /dev/null
4818+++ b/eeschema/dialogs/dialog_migrate_buses_base.cpp
4819@@ -0,0 +1,70 @@
4820+///////////////////////////////////////////////////////////////////////////
4821+// C++ code generated with wxFormBuilder (version Oct 17 2016)
4822+// http://www.wxformbuilder.org/
4823+//
4824+// PLEASE DO "NOT" EDIT THIS FILE!
4825+///////////////////////////////////////////////////////////////////////////
4826+
4827+#include "dialog_migrate_buses_base.h"
4828+
4829+///////////////////////////////////////////////////////////////////////////
4830+
4831+DIALOG_MIGRATE_BUSES_BASE::DIALOG_MIGRATE_BUSES_BASE( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : DIALOG_SHIM( parent, id, title, pos, size, style )
4832+{
4833+ this->SetSizeHints( wxDefaultSize, wxDefaultSize );
4834+
4835+ wxBoxSizer* main_sizer;
4836+ main_sizer = new wxBoxSizer( wxVERTICAL );
4837+
4838+ m_staticText5 = new wxStaticText( this, wxID_ANY, _("This schematic has one or more buses with more than one label. This was allowed in previous KiCad versions but is no longer permitted."), wxDefaultPosition, wxDefaultSize, 0 );
4839+ m_staticText5->Wrap( 480 );
4840+ main_sizer->Add( m_staticText5, 0, wxALL|wxEXPAND, 5 );
4841+
4842+ m_staticText7 = new wxStaticText( this, wxID_ANY, _("Please select a new name for each of the buses below.\nA name has been suggested for you based on the labels attached to the bus."), wxDefaultPosition, wxDefaultSize, 0 );
4843+ m_staticText7->Wrap( 480 );
4844+ m_staticText7->SetMinSize( wxSize( -1,60 ) );
4845+
4846+ main_sizer->Add( m_staticText7, 0, wxALL|wxEXPAND, 5 );
4847+
4848+ m_migration_list = new wxListView( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_HRULES|wxLC_REPORT|wxLC_SINGLE_SEL|wxLC_VRULES );
4849+ m_migration_list->SetMinSize( wxSize( 460,100 ) );
4850+
4851+ main_sizer->Add( m_migration_list, 1, wxALL|wxEXPAND, 5 );
4852+
4853+ m_staticText6 = new wxStaticText( this, wxID_ANY, _("Proposed new name:"), wxDefaultPosition, wxDefaultSize, 0 );
4854+ m_staticText6->Wrap( -1 );
4855+ main_sizer->Add( m_staticText6, 0, wxALL, 5 );
4856+
4857+ wxBoxSizer* bSizer7;
4858+ bSizer7 = new wxBoxSizer( wxHORIZONTAL );
4859+
4860+ m_cb_new_name = new wxComboBox( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 );
4861+ m_cb_new_name->SetMinSize( wxSize( 300,-1 ) );
4862+ m_cb_new_name->SetMaxSize( wxSize( 460,-1 ) );
4863+
4864+ bSizer7->Add( m_cb_new_name, 1, wxALL|wxEXPAND, 5 );
4865+
4866+ m_btn_accept = new wxButton( this, wxID_ANY, _("Accept Name"), wxDefaultPosition, wxDefaultSize, 0 );
4867+ bSizer7->Add( m_btn_accept, 0, wxALL, 5 );
4868+
4869+
4870+ main_sizer->Add( bSizer7, 0, wxEXPAND, 5 );
4871+
4872+ m_sdbSizer1 = new wxStdDialogButtonSizer();
4873+ m_sdbSizer1OK = new wxButton( this, wxID_OK );
4874+ m_sdbSizer1->AddButton( m_sdbSizer1OK );
4875+ m_sdbSizer1->Realize();
4876+
4877+ main_sizer->Add( m_sdbSizer1, 0, wxEXPAND, 5 );
4878+
4879+
4880+ this->SetSizer( main_sizer );
4881+ this->Layout();
4882+ main_sizer->Fit( this );
4883+
4884+ this->Centre( wxBOTH );
4885+}
4886+
4887+DIALOG_MIGRATE_BUSES_BASE::~DIALOG_MIGRATE_BUSES_BASE()
4888+{
4889+}
4890diff --git a/eeschema/dialogs/dialog_migrate_buses_base.fbp b/eeschema/dialogs/dialog_migrate_buses_base.fbp
4891new file mode 100644
4892index 0000000..02524c0
4893--- /dev/null
4894+++ b/eeschema/dialogs/dialog_migrate_buses_base.fbp
4895@@ -0,0 +1,669 @@
4896+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
4897+<wxFormBuilder_Project>
4898+ <FileVersion major="1" minor="13" />
4899+ <object class="Project" expanded="1">
4900+ <property name="class_decoration"></property>
4901+ <property name="code_generation">C++</property>
4902+ <property name="disconnect_events">1</property>
4903+ <property name="disconnect_mode">source_name</property>
4904+ <property name="disconnect_php_events">0</property>
4905+ <property name="disconnect_python_events">0</property>
4906+ <property name="embedded_files_path">res</property>
4907+ <property name="encoding">UTF-8</property>
4908+ <property name="event_generation">connect</property>
4909+ <property name="file">dialog_migrate_buses_base</property>
4910+ <property name="first_id">1000</property>
4911+ <property name="help_provider">none</property>
4912+ <property name="internationalize">1</property>
4913+ <property name="name">DIALOG_MIGRATE_BUSES_BASE</property>
4914+ <property name="namespace"></property>
4915+ <property name="path">.</property>
4916+ <property name="precompiled_header"></property>
4917+ <property name="relative_path">1</property>
4918+ <property name="skip_lua_events">1</property>
4919+ <property name="skip_php_events">1</property>
4920+ <property name="skip_python_events">1</property>
4921+ <property name="ui_table">UI</property>
4922+ <property name="use_enum">0</property>
4923+ <property name="use_microsoft_bom">0</property>
4924+ <object class="Dialog" expanded="1">
4925+ <property name="aui_managed">0</property>
4926+ <property name="aui_manager_style">wxAUI_MGR_DEFAULT</property>
4927+ <property name="bg"></property>
4928+ <property name="center">wxBOTH</property>
4929+ <property name="context_help"></property>
4930+ <property name="context_menu">1</property>
4931+ <property name="enabled">1</property>
4932+ <property name="event_handler">impl_virtual</property>
4933+ <property name="extra_style"></property>
4934+ <property name="fg"></property>
4935+ <property name="font"></property>
4936+ <property name="hidden">0</property>
4937+ <property name="id">wxID_ANY</property>
4938+ <property name="maximum_size"></property>
4939+ <property name="minimum_size"></property>
4940+ <property name="name">DIALOG_MIGRATE_BUSES_BASE</property>
4941+ <property name="pos"></property>
4942+ <property name="size"></property>
4943+ <property name="style">wxDEFAULT_DIALOG_STYLE</property>
4944+ <property name="subclass">DIALOG_SHIM; dialog_shim.h</property>
4945+ <property name="title">Migrate Buses</property>
4946+ <property name="tooltip"></property>
4947+ <property name="window_extra_style"></property>
4948+ <property name="window_name"></property>
4949+ <property name="window_style"></property>
4950+ <event name="OnActivate"></event>
4951+ <event name="OnActivateApp"></event>
4952+ <event name="OnAuiFindManager"></event>
4953+ <event name="OnAuiPaneButton"></event>
4954+ <event name="OnAuiPaneClose"></event>
4955+ <event name="OnAuiPaneMaximize"></event>
4956+ <event name="OnAuiPaneRestore"></event>
4957+ <event name="OnAuiRender"></event>
4958+ <event name="OnChar"></event>
4959+ <event name="OnClose"></event>
4960+ <event name="OnEnterWindow"></event>
4961+ <event name="OnEraseBackground"></event>
4962+ <event name="OnHibernate"></event>
4963+ <event name="OnIconize"></event>
4964+ <event name="OnIdle"></event>
4965+ <event name="OnInitDialog"></event>
4966+ <event name="OnKeyDown"></event>
4967+ <event name="OnKeyUp"></event>
4968+ <event name="OnKillFocus"></event>
4969+ <event name="OnLeaveWindow"></event>
4970+ <event name="OnLeftDClick"></event>
4971+ <event name="OnLeftDown"></event>
4972+ <event name="OnLeftUp"></event>
4973+ <event name="OnMiddleDClick"></event>
4974+ <event name="OnMiddleDown"></event>
4975+ <event name="OnMiddleUp"></event>
4976+ <event name="OnMotion"></event>
4977+ <event name="OnMouseEvents"></event>
4978+ <event name="OnMouseWheel"></event>
4979+ <event name="OnPaint"></event>
4980+ <event name="OnRightDClick"></event>
4981+ <event name="OnRightDown"></event>
4982+ <event name="OnRightUp"></event>
4983+ <event name="OnSetFocus"></event>
4984+ <event name="OnSize"></event>
4985+ <event name="OnUpdateUI"></event>
4986+ <object class="wxBoxSizer" expanded="1">
4987+ <property name="minimum_size"></property>
4988+ <property name="name">main_sizer</property>
4989+ <property name="orient">wxVERTICAL</property>
4990+ <property name="permission">none</property>
4991+ <object class="sizeritem" expanded="1">
4992+ <property name="border">5</property>
4993+ <property name="flag">wxALL|wxEXPAND</property>
4994+ <property name="proportion">0</property>
4995+ <object class="wxStaticText" expanded="1">
4996+ <property name="BottomDockable">1</property>
4997+ <property name="LeftDockable">1</property>
4998+ <property name="RightDockable">1</property>
4999+ <property name="TopDockable">1</property>
5000+ <property name="aui_layer"></property>
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to status/vote changes: