KiCad PCB EDA Suite
Loading...
Searching...
No Matches
hierarchy_pane.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2004 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2008 Wayne Stambaugh <[email protected]>
6 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26#include <bitmaps.h>
27#include <sch_edit_frame.h>
28#include <sch_commit.h>
29#include <tool/tool_manager.h>
30#include <tools/sch_actions.h>
31#include <hierarchy_pane.h>
33#include <kiface_base.h>
34#include <wx/object.h>
35#include <wx/generic/textdlgg.h>
36#include <wx/menu.h>
37#include <wx/wupdlock.h>
38#include <wx/msgdlg.h>
39
43class TREE_ITEM_DATA : public wxTreeItemData
44{
45public:
47
48 TREE_ITEM_DATA( SCH_SHEET_PATH& sheet ) : wxTreeItemData(), m_SheetPath( sheet ) {}
49};
50
51
52// Need to use wxRTTI macros in order for OnCompareItems to work properly
53// See: https://docs.wxwidgets.org/3.1/classwx_tree_ctrl.html#ab90a465793c291ca7aa827a576b7d146
55
56
57int HIERARCHY_TREE::OnCompareItems( const wxTreeItemId& item1, const wxTreeItemId& item2 )
58{
59 SCH_SHEET_PATH* item1Path = &static_cast<TREE_ITEM_DATA*>( GetItemData( item1 ) )->m_SheetPath;
60 SCH_SHEET_PATH* item2Path = &static_cast<TREE_ITEM_DATA*>( GetItemData( item2 ) )->m_SheetPath;
61
62 return item1Path->ComparePageNum( *item2Path );
63}
64
65
67 WX_PANEL( aParent )
68{
69 wxASSERT( dynamic_cast<SCH_EDIT_FRAME*>( aParent ) );
70
71 m_frame = aParent;
72
73 wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL );
74 SetSizer( sizer );
75 m_tree = new HIERARCHY_TREE( this );
76
77 wxVector<wxBitmapBundle> images;
78 images.push_back( KiBitmapBundle( BITMAPS::tree_nosel ) );
79 images.push_back( KiBitmapBundle( BITMAPS::tree_sel ) );
80 m_tree->SetImages( images );
81
82 sizer->Add( m_tree, 1, wxEXPAND, wxBORDER_NONE );
83
84 m_events_bound = false;
85
86 PROJECT_LOCAL_SETTINGS& localSettings = m_frame->Prj().GetLocalSettings();
87
88 for( const wxString& path : localSettings.m_SchHierarchyCollapsed )
89 m_collapsedPaths.insert( path );
90
92
93 // Enable selection events
94 Bind( wxEVT_TREE_ITEM_ACTIVATED, &HIERARCHY_PANE::onSelectSheetPath, this );
95 Bind( wxEVT_TREE_SEL_CHANGED, &HIERARCHY_PANE::onSelectSheetPath, this );
96 Bind( wxEVT_TREE_ITEM_RIGHT_CLICK, &HIERARCHY_PANE::onTreeItemRightClick, this );
97 Bind( wxEVT_CHAR_HOOK, &HIERARCHY_PANE::onCharHook, this );
98 m_tree->Bind( wxEVT_TREE_END_LABEL_EDIT, &HIERARCHY_PANE::onTreeEditFinished, this );
99 m_tree->Bind( wxEVT_CONTEXT_MENU, &HIERARCHY_PANE::onContextMenu, this );
100 m_events_bound = true;
101}
102
103
105{
106 Unbind( wxEVT_TREE_ITEM_ACTIVATED, &HIERARCHY_PANE::onSelectSheetPath, this );
107 Unbind( wxEVT_TREE_SEL_CHANGED, &HIERARCHY_PANE::onSelectSheetPath, this );
108 Unbind( wxEVT_TREE_ITEM_RIGHT_CLICK, &HIERARCHY_PANE::onTreeItemRightClick, this );
109 Unbind( wxEVT_CHAR_HOOK, &HIERARCHY_PANE::onCharHook, this );
110 m_tree->Unbind( wxEVT_TREE_END_LABEL_EDIT, &HIERARCHY_PANE::onTreeEditFinished, this );
111 m_tree->Unbind( wxEVT_CONTEXT_MENU, &HIERARCHY_PANE::onContextMenu, this );
112}
113
114
115void HIERARCHY_PANE::buildHierarchyTree( SCH_SHEET_PATH* aList, const wxTreeItemId& aParent )
116{
117 std::vector<SCH_ITEM*> sheetChildren;
118 aList->LastScreen()->GetSheets( &sheetChildren );
119
120 for( SCH_ITEM* aItem : sheetChildren )
121 {
122 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( aItem );
123 aList->push_back( sheet );
124
125 wxString sheetNameBase = sheet->GetField( FIELD_T::SHEET_NAME )->GetShownText( false );
126
127 // If the sheet name is empty, use the filename (without extension) as fallback
128 if( sheetNameBase.IsEmpty() )
129 {
130 wxFileName fn( sheet->GetFileName() );
131 sheetNameBase = fn.GetName();
132 }
133
134 wxString sheetName = formatPageString( sheetNameBase, aList->GetPageNumber() );
135 wxTreeItemId child = m_tree->AppendItem( aParent, sheetName, 0, 1 );
136 m_tree->SetItemData( child, new TREE_ITEM_DATA( *aList ) );
137
138 buildHierarchyTree( aList, child );
139 aList->pop_back();
140 }
141
142 m_tree->SortChildren( aParent );
143}
144
145
147{
148 bool eventsWereBound = m_events_bound;
149
150 if( eventsWereBound )
151 {
152 // Disable selection events
153 Unbind( wxEVT_TREE_ITEM_ACTIVATED, &HIERARCHY_PANE::onSelectSheetPath, this );
154 Unbind( wxEVT_TREE_SEL_CHANGED, &HIERARCHY_PANE::onSelectSheetPath, this );
155 Unbind( wxEVT_TREE_ITEM_RIGHT_CLICK, &HIERARCHY_PANE::onTreeItemRightClick, this );
156 m_tree->Unbind( wxEVT_CONTEXT_MENU, &HIERARCHY_PANE::onContextMenu, this );
157
158 m_events_bound = false;
159 }
160
161 std::function<void( const wxTreeItemId& )> recursiveDescent =
162 [&]( const wxTreeItemId& id )
163 {
164 wxCHECK_RET( id.IsOk(), wxT( "Invalid tree item" ) );
165
166 TREE_ITEM_DATA* itemData = static_cast<TREE_ITEM_DATA*>( m_tree->GetItemData( id ) );
167
168 // Skip items without data (e.g., project root node)
169 if( !itemData )
170 {
171 wxTreeItemIdValue cookie;
172 wxTreeItemId child = m_tree->GetFirstChild( id, cookie );
173
174 while( child.IsOk() )
175 {
176 recursiveDescent( child );
177 child = m_tree->GetNextChild( id, cookie );
178 }
179
180 return;
181 }
182
183 if( itemData->m_SheetPath == m_frame->GetCurrentSheet() )
184 {
185 wxTreeItemId parent = m_tree->GetItemParent( id );
186
187 if( parent.IsOk() )
188 {
189 // AT least on MSW, wxTreeCtrl::IsExpanded(item) and wxTreeCtrl::Expand(item)
190 // can be called only if item is visible.
191 // Otherwise wxWidgets alerts are thrown and Expand() say the item is invisible
192 if( m_tree->IsVisible( parent ) && !m_tree->IsExpanded( parent ) )
193 m_tree->Expand( parent );
194 }
195
196 if( !m_tree->IsVisible( id ) )
197 m_tree->EnsureVisible( id );
198
199 m_tree->SetItemBold( id, true );
200 m_tree->SetFocusedItem( id );
201 }
202 else
203 {
204 m_tree->SetItemBold( id, false );
205 }
206
207 wxTreeItemIdValue cookie;
208 wxTreeItemId child = m_tree->GetFirstChild( id, cookie );
209
210 while( child.IsOk() )
211 {
212 recursiveDescent( child );
213 child = m_tree->GetNextChild( id, cookie );
214 }
215 };
216
217 recursiveDescent( m_tree->GetRootItem() );
218
219 if( eventsWereBound )
220 {
221 // Enable selection events
222 Bind( wxEVT_TREE_ITEM_ACTIVATED, &HIERARCHY_PANE::onSelectSheetPath, this );
223 Bind( wxEVT_TREE_SEL_CHANGED, &HIERARCHY_PANE::onSelectSheetPath, this );
224 Bind( wxEVT_TREE_ITEM_RIGHT_CLICK, &HIERARCHY_PANE::onTreeItemRightClick, this );
225 m_tree->Bind( wxEVT_CONTEXT_MENU, &HIERARCHY_PANE::onContextMenu, this );
226
227 m_events_bound = true;
228 }
229}
230
231
233{
234 wxWindowUpdateLocker updateLock( this );
235
236 // If hierarchy hasn't been built yet (e.g., during frame construction before schematic
237 // is loaded), just return. The tree will be updated later when the schematic is loaded.
238 if( !m_frame->Schematic().HasHierarchy() )
239 return;
240
241 bool eventsWereBound = m_events_bound;
242
243 if( eventsWereBound )
244 {
245 // Disable selection events
246 Unbind( wxEVT_TREE_ITEM_ACTIVATED, &HIERARCHY_PANE::onSelectSheetPath, this );
247 Unbind( wxEVT_TREE_SEL_CHANGED, &HIERARCHY_PANE::onSelectSheetPath, this );
248 Unbind( wxEVT_TREE_ITEM_RIGHT_CLICK, &HIERARCHY_PANE::onTreeItemRightClick, this );
249 m_tree->Unbind( wxEVT_CONTEXT_MENU, &HIERARCHY_PANE::onContextMenu, this );
250
251 m_events_bound = false;
252 }
253
254 SCH_SHEET_LIST hierarchy = m_frame->Schematic().Hierarchy();
255 std::set<wxString> collapsedNodes = m_collapsedPaths;
256
257 std::function<void( const wxTreeItemId& )> getCollapsedNodes =
258 [&]( const wxTreeItemId& id )
259 {
260 wxCHECK_RET( id.IsOk(), wxT( "Invalid tree item" ) );
261
262 TREE_ITEM_DATA* itemData = static_cast<TREE_ITEM_DATA*>( m_tree->GetItemData( id ) );
263
264 // Skip items without data (e.g., project root node)
265 if( itemData && m_tree->ItemHasChildren( id ) && !m_tree->IsExpanded( id )
266 && hierarchy.HasPath( itemData->m_SheetPath.Path() ) )
267 {
268 collapsedNodes.emplace( itemData->m_SheetPath.PathAsString() );
269 return;
270 }
271
272 wxTreeItemIdValue cookie;
273 wxTreeItemId child = m_tree->GetFirstChild( id, cookie );
274
275 while( child.IsOk() )
276 {
277 getCollapsedNodes( child );
278 child = m_tree->GetNextChild( id, cookie );
279 }
280 };
281
282 // If we are clearing the tree, don't try to get collapsed nodes as they
283 // might be deleted
284 if( !aClear && !m_tree->IsEmpty() )
285 {
286 collapsedNodes.clear();
287 getCollapsedNodes( m_tree->GetRootItem() );
288 }
289
290 m_tree->DeleteAllItems();
291
292 // Create project root node (not associated with virtual root sheet)
293 wxTreeItemId projectRoot = m_tree->AddRoot( getRootString(), 0, 1 );
294 // Don't set item data for the project root - it doesn't correspond to a real sheet
295
296 // Get all top-level sheets
297 std::vector<SCH_SHEET*> topLevelSheets = m_frame->Schematic().GetTopLevelSheets();
298
299 // For each top-level sheet, build its hierarchy under the project root
300 for( SCH_SHEET* sheet : topLevelSheets )
301 {
302 if( sheet )
303 {
304 m_list.clear();
305 m_list.push_back( sheet );
306
307 wxString sheetNameBase = sheet->GetShownName( false );
308
309 // If the sheet name is empty, use the filename (without extension) as fallback
310 if( sheetNameBase.IsEmpty() && sheet->GetScreen() )
311 {
312 wxFileName fn( sheet->GetScreen()->GetFileName() );
313 sheetNameBase = fn.GetName();
314 }
315
316 // Create tree item for this top-level sheet
317 wxString sheetName = formatPageString( sheetNameBase, m_list.GetPageNumber() );
318 wxTreeItemId topLevelItem = m_tree->AppendItem( projectRoot, sheetName, 0, 1 );
319 m_tree->SetItemData( topLevelItem, new TREE_ITEM_DATA( m_list ) );
320
321 // Build hierarchy for this top-level sheet
322 buildHierarchyTree( &m_list, topLevelItem );
323 }
324 }
325
327
328 m_tree->ExpandAll();
329
330 std::function<void( const wxTreeItemId& )> collapseNodes =
331 [&]( const wxTreeItemId& id )
332 {
333 wxCHECK_RET( id.IsOk(), wxT( "Invalid tree item" ) );
334
335 TREE_ITEM_DATA* itemData =
336 static_cast<TREE_ITEM_DATA*>( m_tree->GetItemData( id ) );
337
338 if( id != projectRoot && itemData &&
339 collapsedNodes.find( itemData->m_SheetPath.PathAsString() ) != collapsedNodes.end() )
340 {
341 m_tree->Collapse( id );
342 return;
343 }
344
345 wxTreeItemIdValue cookie;
346 wxTreeItemId child = m_tree->GetFirstChild( id, cookie );
347
348 while( child.IsOk() )
349 {
350 collapseNodes( child );
351 child = m_tree->GetNextChild( id, cookie );
352 }
353 };
354
355 collapseNodes( projectRoot );
356 m_collapsedPaths = std::move( collapsedNodes );
357
358 if( eventsWereBound )
359 {
360 // Enable selection events
361 Bind( wxEVT_TREE_ITEM_ACTIVATED, &HIERARCHY_PANE::onSelectSheetPath, this );
362 Bind( wxEVT_TREE_SEL_CHANGED, &HIERARCHY_PANE::onSelectSheetPath, this );
363 Bind( wxEVT_TREE_ITEM_RIGHT_CLICK, &HIERARCHY_PANE::onTreeItemRightClick, this );
364 m_tree->Bind( wxEVT_CONTEXT_MENU, &HIERARCHY_PANE::onContextMenu, this );
365
366 m_events_bound = true;
367 }
368}
369
370
371void HIERARCHY_PANE::onSelectSheetPath( wxTreeEvent& aEvent )
372{
373 wxTreeItemId itemSel = m_tree->GetSelection();
374
375 if( !itemSel.IsOk() )
376 return;
377
378 TREE_ITEM_DATA* itemData = static_cast<TREE_ITEM_DATA*>( m_tree->GetItemData( itemSel ) );
379
380 if( !itemData )
381 return;
382
383 SetCursor( wxCURSOR_ARROWWAIT );
384 m_frame->GetToolManager()->RunAction<SCH_SHEET_PATH*>( SCH_ACTIONS::changeSheet, &itemData->m_SheetPath );
385 SetCursor( wxCURSOR_ARROW );
386}
387
388
390{
391 // Update the labels of the hierarchical tree of the schematic.
392 // Must be called only for an up to date tree, to update displayed labels after
393 // a sheet name or a sheet number change.
394
395 std::function<void( const wxTreeItemId& )> updateLabel =
396 [&]( const wxTreeItemId& id )
397 {
398 TREE_ITEM_DATA* itemData = static_cast<TREE_ITEM_DATA*>( m_tree->GetItemData( id ) );
399
400 if( !itemData ) // happens if not shown in wxTreeCtrl m_tree (virtual sheet)
401 return;
402
403 SCH_SHEET* sheet = itemData->m_SheetPath.Last();
404 wxString sheetNameBase = sheet->GetField( FIELD_T::SHEET_NAME )->GetShownText( false );
405 wxString sheetName = formatPageString( sheetNameBase,
406 itemData->m_SheetPath.GetPageNumber() );
407
408 if( m_tree->GetItemText( id ) != sheetName )
409 m_tree->SetItemText( id, sheetName );
410 };
411
412 wxTreeItemId rootId = m_tree->GetRootItem();
413 updateLabel( rootId );
414
415 std::function<void( const wxTreeItemId& )> recursiveDescent =
416 [&]( const wxTreeItemId& id )
417 {
418 wxCHECK_RET( id.IsOk(), wxT( "Invalid tree item" ) );
419 wxTreeItemIdValue cookie;
420 wxTreeItemId child = m_tree->GetFirstChild( id, cookie );
421
422 while( child.IsOk() )
423 {
424 updateLabel( child );
425 recursiveDescent( child );
426 child = m_tree->GetNextChild( id, cookie );
427 }
428 };
429
430 recursiveDescent( rootId );
431}
432
433
434std::vector<wxString> HIERARCHY_PANE::GetCollapsedPaths() const
435{
436 std::vector<wxString> collapsed;
437
438 if( m_tree->IsEmpty() )
439 return collapsed;
440
441 std::function<void( const wxTreeItemId& )> collect =
442 [&]( const wxTreeItemId& id )
443 {
444 wxCHECK_RET( id.IsOk(), wxT( "Invalid tree item" ) );
445
446 TREE_ITEM_DATA* itemData = static_cast<TREE_ITEM_DATA*>( m_tree->GetItemData( id ) );
447
448 if( id != m_tree->GetRootItem() && m_tree->ItemHasChildren( id )
449 && !m_tree->IsExpanded( id ) )
450 {
451 collapsed.push_back( itemData->m_SheetPath.PathAsString() );
452 return;
453 }
454
455 wxTreeItemIdValue cookie;
456 wxTreeItemId child = m_tree->GetFirstChild( id, cookie );
457
458 while( child.IsOk() )
459 {
460 collect( child );
461 child = m_tree->GetNextChild( id, cookie );
462 }
463 };
464
465 collect( m_tree->GetRootItem() );
466 return collapsed;
467}
468
469
470void HIERARCHY_PANE::onTreeItemRightClick( wxTreeEvent& aEvent )
471{
472 onRightClick( aEvent.GetItem() );
473}
474
475
476void HIERARCHY_PANE::onContextMenu( wxContextMenuEvent& aEvent )
477{
478 // Handle right-click in empty space - treat as if clicking on an invalid item
479 onRightClick( wxTreeItemId() );
480}
481
482
483void HIERARCHY_PANE::onRightClick( wxTreeItemId aItem )
484{
485 wxMenu ctxMenu;
486 TREE_ITEM_DATA* itemData = nullptr;
487 bool isProjectRoot = false;
488
489 if( !aItem.IsOk() )
490 aItem = m_tree->GetSelection();
491
492 if( aItem.IsOk() )
493 {
494 itemData = static_cast<TREE_ITEM_DATA*>( m_tree->GetItemData( aItem ) );
495 isProjectRoot = ( m_tree->GetRootItem() == aItem.GetID() );
496 }
497
498 if( itemData )
499 {
500 ctxMenu.Append( EDIT_PAGE_NUMBER, _( "Edit Page Number" ) );
501 // The root item cannot be renamed
502 if( !isProjectRoot )
503 {
504 ctxMenu.Append( RENAME, _( "Rename" ), _( "Change name of this sheet" ) );
505
506 // Allow deleting top-level sheets (but not sub-sheets)
507 if( itemData->m_SheetPath.size() == 1 )
508 {
509 ctxMenu.Append( DELETE_TOP_LEVEL_SHEET, _( "Delete Top-Level Sheet" ),
510 _( "Remove this top-level sheet from the project" ) );
511 }
512 }
513
514 ctxMenu.AppendSeparator();
515 }
516
517 // Always allow creating new top-level sheets (root is hidden with wxTR_HIDE_ROOT)
518 ctxMenu.Append( NEW_TOP_LEVEL_SHEET, _( "New Top-Level Sheet" ), _( "Create a new top-level sheet" ) );
519 ctxMenu.AppendSeparator();
520
521 ctxMenu.Append( EXPAND_ALL, ACTIONS::expandAll.GetMenuItem() );
522 ctxMenu.Append( COLLAPSE_ALL, ACTIONS::collapseAll.GetMenuItem() );
523
524
525 int selected = GetPopupMenuSelectionFromUser( ctxMenu );
526
527 switch( selected )
528 {
530 {
531 // Create a new top-level sheet
532 wxTextEntryDialog dlg( m_frame, _( "Enter name for new top-level sheet:" ),
533 _( "New Top-Level Sheet" ),
534 _( "Untitled" ) );
535
536 if( dlg.ShowModal() == wxID_OK )
537 {
538 wxString newName = dlg.GetValue();
539
540 if( !newName.IsEmpty() )
541 {
542 SCH_COMMIT commit( m_frame );
543
544 // Create new sheet and screen
545 SCH_SHEET* newSheet = new SCH_SHEET( &m_frame->Schematic() );
546 SCH_SCREEN* newScreen = new SCH_SCREEN( &m_frame->Schematic() );
547
548 newSheet->SetScreen( newScreen );
549 newSheet->GetField( FIELD_T::SHEET_NAME )->SetText( newName );
550
551 // Generate a unique filename
552 wxString filename = newName;
553 filename.Replace( " ", "_" );
554 filename = filename.Lower();
555
556 if( !filename.EndsWith( ".kicad_sch" ) )
557 filename += ".kicad_sch";
558
559 newScreen->SetFileName( filename );
560
561 // Calculate the next page number
562 int nextPage = m_frame->Schematic().GetTopLevelSheets().size() + 1;
563 newScreen->SetPageNumber( wxString::Format( "%d", nextPage ) );
564
565 // Add to schematic
566 m_frame->Schematic().AddTopLevelSheet( newSheet );
567
568 commit.Push( _( "Add new top-level sheet" ) );
569
570 // Refresh the hierarchy tree
572 }
573 }
574 break;
575 }
576
578 {
579 if( itemData && itemData->m_SheetPath.size() == 1 )
580 {
581 SCH_SHEET* sheet = itemData->m_SheetPath.Last();
582
583 // Confirm deletion
584 wxString msg = wxString::Format( _( "Delete top-level sheet '%s'?\n\nThis cannot be undone." ),
585 sheet->GetName() );
586
587 if( wxMessageBox( msg, _( "Delete Top-Level Sheet" ), wxYES_NO | wxICON_QUESTION, m_frame ) == wxYES )
588 {
589 // Don't allow deleting the last top-level sheet
590 if( m_frame->Schematic().GetTopLevelSheets().size() <= 1 )
591 {
592 wxMessageBox( _( "Cannot delete the last top-level sheet." ), _( "Delete Top-Level Sheet" ),
593 wxOK | wxICON_ERROR, m_frame );
594 break;
595 }
596
597 SCH_COMMIT commit( m_frame );
598
599 // Remove from schematic
600 if( m_frame->Schematic().RemoveTopLevelSheet( sheet ) )
601 {
602 commit.Push( _( "Delete top-level sheet" ) );
603
604 // Refresh the hierarchy tree
606 }
607 }
608 }
609 break;
610 }
611
612 case EDIT_PAGE_NUMBER:
613 {
614 wxString msg;
615 wxString sheetPath = itemData->m_SheetPath.PathHumanReadable( false, true );
616 wxString pageNumber = itemData->m_SheetPath.GetPageNumber();
617
618 msg.Printf( _( "Enter page number for sheet path %s" ),
619 ( sheetPath.Length() > 20 ) ? wxS( " \n" ) + sheetPath + wxT( ": " )
620 : sheetPath + wxT( ": " ) );
621
622 wxTextEntryDialog dlg( m_frame, msg, _( "Edit Sheet Page Number" ), pageNumber );
623
624 dlg.SetTextValidator( wxFILTER_ALPHANUMERIC ); // No white space.
625
626 if( dlg.ShowModal() == wxID_OK && dlg.GetValue() != itemData->m_SheetPath.GetPageNumber() )
627 {
628 SCH_COMMIT commit( m_frame );
629 SCH_SHEET_PATH parentPath = itemData->m_SheetPath;
630 parentPath.pop_back();
631
632 commit.Modify( itemData->m_SheetPath.Last(), parentPath.LastScreen() );
633
634 itemData->m_SheetPath.SetPageNumber( dlg.GetValue() );
635
636 if( itemData->m_SheetPath == m_frame->GetCurrentSheet() )
637 {
638 if( m_frame->GetScreen() )
639 {
640 m_frame->GetScreen()->SetPageNumber( dlg.GetValue() );
641 m_frame->OnPageSettingsChange();
642 }
643 }
644
645 commit.Push( wxS( "Change sheet page number." ) );
646
648 }
649
650 break;
651 }
652 case EXPAND_ALL:
653 m_tree->ExpandAll();
654 break;
655 case COLLAPSE_ALL:
656 m_tree->CollapseAll();
657 break;
658 case RENAME:
659 m_tree->SetItemText( aItem, itemData->m_SheetPath.Last()->GetName() );
660 m_tree->EditLabel( aItem );
662 break;
663 }
664}
665
666
667void HIERARCHY_PANE::onTreeEditFinished( wxTreeEvent& event )
668{
669 TREE_ITEM_DATA* data = static_cast<TREE_ITEM_DATA*>( m_tree->GetItemData( event.GetItem() ) );
670 wxString newName = event.GetLabel();
671
672 if( data && data->m_SheetPath.Last() )
673 {
674 if( !newName.IsEmpty() )
675 {
676 // The editor holds only the page name as a text, while normally
677 // the tree items displaying it suffixed with the page number
678 if( data->m_SheetPath.Last()->GetName() != newName )
679 {
680 SCH_COMMIT commit( m_frame );
681 SCH_SCREEN* modifyScreen = nullptr;
682
683 // For top-level sheets (size == 1), modify on the virtual root's screen
684 // For sub-sheets, modify on the parent sheet's screen
685 if( data->m_SheetPath.size() == 1 )
686 {
687 modifyScreen = m_frame->Schematic().Root().GetScreen();
688 }
689 else
690 {
691 const SCH_SHEET* parentSheet = data->m_SheetPath.GetSheet( data->m_SheetPath.size() - 2 );
692 if( parentSheet )
693 modifyScreen = parentSheet->GetScreen();
694 }
695
696 if( modifyScreen )
697 {
699 modifyScreen );
700
701 data->m_SheetPath.Last()->SetName( newName );
702
703 renameIdenticalSheets( data->m_SheetPath, newName, &commit );
704
705 if( !commit.Empty() )
706 commit.Push( _( "Renaming sheet" ) );
707
708 if( data->m_SheetPath == m_frame->GetCurrentSheet() )
709 {
710 m_frame->OnPageSettingsChange();
711 }
712 }
713 }
714 }
715
716 m_tree->SetItemText( event.GetItem(), formatPageString( data->m_SheetPath.Last()->GetName(),
717 data->m_SheetPath.GetPageNumber() ) );
719 // The event needs to be rejected otherwise the SetItemText call above
720 // will be ineffective (the treeview item will hold the editor's content)
721 event.Veto();
722 }
723}
724
725
726void HIERARCHY_PANE::onCharHook( wxKeyEvent& aKeyStroke )
727{
728 int hotkey = aKeyStroke.GetKeyCode();
729
730 int mods = aKeyStroke.GetModifiers();
731
732 // the flag wxMOD_ALTGR is defined in wxWidgets as wxMOD_CONTROL|wxMOD_ALT
733 // So AltGr key cannot used as modifier key because it is the same as Alt key + Ctrl key.
734#if CAN_USE_ALTGR_KEY
735 if( wxmods & wxMOD_ALTGR )
736 mods |= MD_ALTGR;
737 else
738#endif
739 {
740 if( mods & wxMOD_CONTROL )
741 hotkey += MD_CTRL;
742
743 if( mods & wxMOD_ALT )
744 hotkey += MD_ALT;
745 }
746
747 if( mods & wxMOD_SHIFT )
748 hotkey += MD_SHIFT;
749
750#ifdef wxMOD_META
751 if( mods & wxMOD_META )
752 hotkey += MD_META;
753#endif
754
755#ifdef wxMOD_WIN
756 if( mods & wxMOD_WIN )
757 hotkey += MD_SUPER;
758#endif
759
760 if( hotkey == ACTIONS::expandAll.GetHotKey()
761 || hotkey == ACTIONS::expandAll.GetHotKeyAlt() )
762 {
763 m_tree->ExpandAll();
764 return;
765 }
766 else if( hotkey == ACTIONS::collapseAll.GetHotKey()
767 || hotkey == ACTIONS::collapseAll.GetHotKeyAlt() )
768 {
769 m_tree->CollapseAll();
770 return;
771 }
772 else
773 {
774 aKeyStroke.Skip();
775 }
776}
777
778
780{
781 // Pane may be repainting while schematic is in flux
782 if ( !m_frame->Schematic().IsValid() )
783 return _( "Schematic" );
784
785 // Return the project name for the root node
786 wxString projectName = m_frame->Schematic().Project().GetProjectName();
787
788 if( projectName.IsEmpty() )
789 projectName = _( "Schematic" );
790
791 return projectName;
792}
793
794
795wxString HIERARCHY_PANE::formatPageString( const wxString& aName, const wxString& aPage )
796{
797 return aName + wxT( " " ) + wxString::Format( _( "(page %s)" ), aPage );
798}
799
801{
802 std::function<void( const wxTreeItemId& )> recursiveDescent = [&]( const wxTreeItemId& id )
803 {
804 wxCHECK_RET( id.IsOk(), wxT( "Invalid tree item" ) );
805
806 TREE_ITEM_DATA* itemData = static_cast<TREE_ITEM_DATA*>( m_tree->GetItemData( id ) );
807
808 // Skip items without data (e.g., project root node)
809 if( itemData && itemData->m_SheetPath.Cmp( path ) != 0 && itemData->m_SheetPath.Last() == path.Last() )
810 {
811 wxFont font = m_tree->GetItemFont( id );
812 font.SetUnderlined( highLighted );
813 m_tree->SetItemFont( id, font );
814 }
815
816 wxTreeItemIdValue cookie;
817 wxTreeItemId child = m_tree->GetFirstChild( id, cookie );
818
819 while( child.IsOk() )
820 {
821 recursiveDescent( child );
822 child = m_tree->GetNextChild( id, cookie );
823 }
824 };
825
826 recursiveDescent( m_tree->GetRootItem() );
827}
828
830 const wxString newName, SCH_COMMIT* commit )
831{
832 std::function<void( const wxTreeItemId& )> recursiveDescent = [&]( const wxTreeItemId& id )
833 {
834 wxCHECK_RET( id.IsOk(), wxT( "Invalid tree item" ) );
835
836 TREE_ITEM_DATA* data = static_cast<TREE_ITEM_DATA*>( m_tree->GetItemData( id ) );
837
838 // Skip items without data (e.g., project root node)
839 if( !data )
840 {
841 wxTreeItemIdValue cookie;
842 wxTreeItemId child = m_tree->GetFirstChild( id, cookie );
843
844 while( child.IsOk() )
845 {
846 recursiveDescent( child );
847 child = m_tree->GetNextChild( id, cookie );
848 }
849
850 return;
851 }
852
853 // Check if this is an identical sheet that needs renaming (but not the renamed sheet itself)
854 if( data->m_SheetPath.Cmp( renamedSheet ) != 0
855 && data->m_SheetPath.Last() == renamedSheet.Last() )
856 {
857 SCH_SCREEN* modifyScreen = nullptr;
858
859 // For top-level sheets (size == 1), modify on the virtual root's screen
860 // For sub-sheets, modify on the parent sheet's screen
861 if( data->m_SheetPath.size() == 1 )
862 {
863 modifyScreen = m_frame->Schematic().Root().GetScreen();
864 }
865 else
866 {
867 const SCH_SHEET* parentSheet = data->m_SheetPath.GetSheet( data->m_SheetPath.size() - 2 );
868 if( parentSheet )
869 modifyScreen = parentSheet->GetScreen();
870 }
871
872 if( modifyScreen )
873 {
875 modifyScreen );
876
877 data->m_SheetPath.Last()->SetName( newName );
878
879 if( data->m_SheetPath == m_frame->GetCurrentSheet() )
880 {
881 m_frame->OnPageSettingsChange();
882 }
883
884 m_tree->SetItemText( id, formatPageString( data->m_SheetPath.Last()->GetName(),
885 data->m_SheetPath.GetPageNumber() ) );
886 }
887 }
888
889 wxTreeItemIdValue cookie;
890 wxTreeItemId child = m_tree->GetFirstChild( id, cookie );
891
892 while( child.IsOk() )
893 {
894 recursiveDescent( child );
895 child = m_tree->GetNextChild( id, cookie );
896 }
897 };
898
899 recursiveDescent( m_tree->GetRootItem() );
900}
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:110
static TOOL_ACTION expandAll
Definition actions.h:90
static TOOL_ACTION collapseAll
Definition actions.h:91
void SetPageNumber(const wxString &aPageNumber)
Definition base_screen.h:79
bool Empty() const
Definition commit.h:137
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE)
Modify a given item in the model.
Definition commit.h:106
SCH_EDIT_FRAME * m_frame
void onRightClick(wxTreeItemId aItem)
HIERARCHY_TREE * m_tree
void UpdateHierarchyTree(bool aClear=false)
Update the hierarchical tree of the schematic.
void onSelectSheetPath(wxTreeEvent &aEvent)
Open the selected sheet and display the corresponding screen when a tree item is selected.
void UpdateLabelsHierarchyTree()
Update the labels of the hierarchical tree of the schematic.
wxString formatPageString(const wxString &aName, const wxString &aPage)
wxString getRootString()
SCH_SHEET_PATH m_list
void UpdateHierarchySelection()
Updates the tree's selection to match current page.
HIERARCHY_PANE(SCH_EDIT_FRAME *aParent)
std::set< wxString > m_collapsedPaths
void onCharHook(wxKeyEvent &aKeyStroke)
void renameIdenticalSheets(const SCH_SHEET_PATH &renamedSheet, const wxString newName, SCH_COMMIT *commit)
Rename all sheets in a hierarchial desing which has the same source renamed sheet.
void buildHierarchyTree(SCH_SHEET_PATH *aList, const wxTreeItemId &aParent)
Create the hierarchical tree of the schematic.
void onContextMenu(wxContextMenuEvent &aEvent)
void onTreeItemRightClick(wxTreeEvent &aEvent)
std::vector< wxString > GetCollapsedPaths() const
Returns a list of sheet paths for nodes that are currently collapsed.
void onTreeEditFinished(wxTreeEvent &event)
void setIdenticalSheetsHighlighted(const SCH_SHEET_PATH &path, bool highLighted=true)
When renaming the sheets in tree it is helpful to highlight the identical sheets which got renamed by...
Navigation hierarchy tree control.
int OnCompareItems(const wxTreeItemId &item1, const wxTreeItemId &item2) override
The project local settings are things that are attached to a particular project, but also might be pa...
std::vector< wxString > m_SchHierarchyCollapsed
Collapsed nodes in the schematic hierarchy navigator.
static TOOL_ACTION changeSheet
virtual void Push(const wxString &aMessage=wxT("A commit"), int aCommitFlags=0) override
Execute the changes.
Schematic editor (Eeschema) main window.
wxString GetShownText(const SCH_SHEET_PATH *aPath, bool aAllowExtraText, int aDepth=0, const wxString &aVariantName=wxEmptyString) const
void SetText(const wxString &aText) override
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:167
void SetFileName(const wxString &aFileName)
Set the file name for this screen to aFileName.
void GetSheets(std::vector< SCH_ITEM * > *aItems) const
Similar to Items().OfType( SCH_SHEET_T ), but return the sheets in a deterministic order (L-R,...
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
bool HasPath(const KIID_PATH &aPath) const
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
const SCH_SHEET * GetSheet(unsigned aIndex) const
int ComparePageNum(const SCH_SHEET_PATH &aSheetPathToTest) const
Compare sheets by their page number.
wxString PathHumanReadable(bool aUseShortRootName=true, bool aStripTrailingSeparator=false) const
Return the sheet path in a human readable form made from the sheet names.
KIID_PATH Path() const
Get the sheet path as an KIID_PATH.
SCH_SCREEN * LastScreen()
int Cmp(const SCH_SHEET_PATH &aSheetPathToTest) const
Compare if this is the same sheet path as aSheetPathToTest.
wxString GetPageNumber() const
wxString PathAsString() const
Return the path of time stamps which do not changes even when editing sheet parameters.
void SetPageNumber(const wxString &aPageNumber)
Set the sheet instance user definable page number.
SCH_SHEET * Last() const
Return a pointer to the last SCH_SHEET of the list.
void push_back(SCH_SHEET *aSheet)
Forwarded method from std::vector.
size_t size() const
Forwarded method from std::vector.
void pop_back()
Forwarded method from std::vector.
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:48
wxString GetFileName() const
Return the filename corresponding to this sheet.
Definition sch_sheet.h:376
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this sheet.
wxString GetName() const
Definition sch_sheet.h:142
void SetName(const wxString &aName)
Definition sch_sheet.h:143
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:145
void SetScreen(SCH_SCREEN *aScreen)
Set the SCH_SCREEN associated with this sheet to aScreen.
Store an SCH_SHEET_PATH of each sheet in hierarchy.
SCH_SHEET_PATH m_SheetPath
TREE_ITEM_DATA(SCH_SHEET_PATH &sheet)
WX_PANEL(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxTAB_TRAVERSAL, const wxString &name=wxEmptyString)
Definition wx_panel.cpp:28
static void recursiveDescent(wxSizer *aSizer, std::map< int, wxString > &aLabels)
#define _(s)
wxIMPLEMENT_ABSTRACT_CLASS(HIERARCHY_TREE, wxTreeCtrl)
std::string path
@ MD_META
Definition tool_event.h:147
@ MD_ALT
Definition tool_event.h:145
@ MD_CTRL
Definition tool_event.h:144
@ MD_SUPER
Definition tool_event.h:146
@ MD_ALTGR
Definition tool_event.h:148
@ MD_SHIFT
Definition tool_event.h:143