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#ifdef __WXMAC__
78 // HiDPI-aware API; will be generally available in wxWidgets 3.4
79 wxVector<wxBitmapBundle> images;
80 images.push_back( KiBitmapBundle( BITMAPS::tree_nosel ) );
81 images.push_back( KiBitmapBundle( BITMAPS::tree_sel ) );
82 m_tree->SetImages( images );
83#else
84 // Make an image list containing small icons
85 // All icons are expected having the same size.
86 wxBitmap tree_nosel_bm( KiBitmap( BITMAPS::tree_nosel ) );
87 wxImageList* imageList = new wxImageList( tree_nosel_bm.GetWidth(), tree_nosel_bm.GetHeight(),
88 true, 2 );
89
90 imageList->Add( tree_nosel_bm );
91 imageList->Add( KiBitmap( BITMAPS::tree_sel ) );
92
93 m_tree->AssignImageList( imageList );
94#endif
95
96 sizer->Add( m_tree, 1, wxEXPAND, wxBORDER_NONE );
97
98 m_events_bound = false;
99
100 PROJECT_LOCAL_SETTINGS& localSettings = m_frame->Prj().GetLocalSettings();
101
102 for( const wxString& path : localSettings.m_SchHierarchyCollapsed )
103 m_collapsedPaths.insert( path );
104
106
107 // Enable selection events
108 Bind( wxEVT_TREE_ITEM_ACTIVATED, &HIERARCHY_PANE::onSelectSheetPath, this );
109 Bind( wxEVT_TREE_SEL_CHANGED, &HIERARCHY_PANE::onSelectSheetPath, this );
110 Bind( wxEVT_TREE_ITEM_RIGHT_CLICK, &HIERARCHY_PANE::onTreeItemRightClick, this );
111 Bind( wxEVT_CHAR_HOOK, &HIERARCHY_PANE::onCharHook, this );
112 m_tree->Bind( wxEVT_TREE_END_LABEL_EDIT, &HIERARCHY_PANE::onTreeEditFinished, this );
113 m_tree->Bind( wxEVT_CONTEXT_MENU, &HIERARCHY_PANE::onContextMenu, this );
114 m_events_bound = true;
115}
116
117
119{
120 Unbind( wxEVT_TREE_ITEM_ACTIVATED, &HIERARCHY_PANE::onSelectSheetPath, this );
121 Unbind( wxEVT_TREE_SEL_CHANGED, &HIERARCHY_PANE::onSelectSheetPath, this );
122 Unbind( wxEVT_TREE_ITEM_RIGHT_CLICK, &HIERARCHY_PANE::onTreeItemRightClick, this );
123 Unbind( wxEVT_CHAR_HOOK, &HIERARCHY_PANE::onCharHook, this );
124 m_tree->Unbind( wxEVT_TREE_END_LABEL_EDIT, &HIERARCHY_PANE::onTreeEditFinished, this );
125 m_tree->Unbind( wxEVT_CONTEXT_MENU, &HIERARCHY_PANE::onContextMenu, this );
126}
127
128
129void HIERARCHY_PANE::buildHierarchyTree( SCH_SHEET_PATH* aList, const wxTreeItemId& aParent )
130{
131 std::vector<SCH_ITEM*> sheetChildren;
132 aList->LastScreen()->GetSheets( &sheetChildren );
133
134 for( SCH_ITEM* aItem : sheetChildren )
135 {
136 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( aItem );
137 aList->push_back( sheet );
138
139 wxString sheetNameBase = sheet->GetField( FIELD_T::SHEET_NAME )->GetShownText( false );
140
141 // If the sheet name is empty, use the filename (without extension) as fallback
142 if( sheetNameBase.IsEmpty() )
143 {
144 wxFileName fn( sheet->GetFileName() );
145 sheetNameBase = fn.GetName();
146 }
147
148 wxString sheetName = formatPageString( sheetNameBase, aList->GetPageNumber() );
149 wxTreeItemId child = m_tree->AppendItem( aParent, sheetName, 0, 1 );
150 m_tree->SetItemData( child, new TREE_ITEM_DATA( *aList ) );
151
152 buildHierarchyTree( aList, child );
153 aList->pop_back();
154 }
155
156 m_tree->SortChildren( aParent );
157}
158
159
161{
162 bool eventsWereBound = m_events_bound;
163
164 if( eventsWereBound )
165 {
166 // Disable selection events
167 Unbind( wxEVT_TREE_ITEM_ACTIVATED, &HIERARCHY_PANE::onSelectSheetPath, this );
168 Unbind( wxEVT_TREE_SEL_CHANGED, &HIERARCHY_PANE::onSelectSheetPath, this );
169 Unbind( wxEVT_TREE_ITEM_RIGHT_CLICK, &HIERARCHY_PANE::onTreeItemRightClick, this );
170 m_tree->Unbind( wxEVT_CONTEXT_MENU, &HIERARCHY_PANE::onContextMenu, this );
171
172 m_events_bound = false;
173 }
174
175 std::function<void( const wxTreeItemId& )> recursiveDescent =
176 [&]( const wxTreeItemId& id )
177 {
178 wxCHECK_RET( id.IsOk(), wxT( "Invalid tree item" ) );
179
180 TREE_ITEM_DATA* itemData = static_cast<TREE_ITEM_DATA*>( m_tree->GetItemData( id ) );
181
182 // Skip items without data (e.g., project root node)
183 if( !itemData )
184 {
185 wxTreeItemIdValue cookie;
186 wxTreeItemId child = m_tree->GetFirstChild( id, cookie );
187
188 while( child.IsOk() )
189 {
190 recursiveDescent( child );
191 child = m_tree->GetNextChild( id, cookie );
192 }
193
194 return;
195 }
196
197 if( itemData->m_SheetPath == m_frame->GetCurrentSheet() )
198 {
199 wxTreeItemId parent = m_tree->GetItemParent( id );
200
201 if( parent.IsOk() )
202 {
203 // AT least on MSW, wxTreeCtrl::IsExpanded(item) and wxTreeCtrl::Expand(item)
204 // can be called only if item is visible.
205 // Otherwise wxWidgets alerts are thrown and Expand() say the item is invisible
206 if( m_tree->IsVisible( parent ) && !m_tree->IsExpanded( parent ) )
207 m_tree->Expand( parent );
208 }
209
210 if( !m_tree->IsVisible( id ) )
211 m_tree->EnsureVisible( id );
212
213 m_tree->SetItemBold( id, true );
214 m_tree->SetFocusedItem( id );
215 }
216 else
217 {
218 m_tree->SetItemBold( id, false );
219 }
220
221 wxTreeItemIdValue cookie;
222 wxTreeItemId child = m_tree->GetFirstChild( id, cookie );
223
224 while( child.IsOk() )
225 {
226 recursiveDescent( child );
227 child = m_tree->GetNextChild( id, cookie );
228 }
229 };
230
231 recursiveDescent( m_tree->GetRootItem() );
232
233 if( eventsWereBound )
234 {
235 // Enable selection events
236 Bind( wxEVT_TREE_ITEM_ACTIVATED, &HIERARCHY_PANE::onSelectSheetPath, this );
237 Bind( wxEVT_TREE_SEL_CHANGED, &HIERARCHY_PANE::onSelectSheetPath, this );
238 Bind( wxEVT_TREE_ITEM_RIGHT_CLICK, &HIERARCHY_PANE::onTreeItemRightClick, this );
239 m_tree->Bind( wxEVT_CONTEXT_MENU, &HIERARCHY_PANE::onContextMenu, this );
240
241 m_events_bound = true;
242 }
243}
244
245
247{
248 wxWindowUpdateLocker updateLock( this );
249
250 bool eventsWereBound = m_events_bound;
251
252 if( eventsWereBound )
253 {
254 // Disable selection events
255 Unbind( wxEVT_TREE_ITEM_ACTIVATED, &HIERARCHY_PANE::onSelectSheetPath, this );
256 Unbind( wxEVT_TREE_SEL_CHANGED, &HIERARCHY_PANE::onSelectSheetPath, this );
257 Unbind( wxEVT_TREE_ITEM_RIGHT_CLICK, &HIERARCHY_PANE::onTreeItemRightClick, this );
258 m_tree->Unbind( wxEVT_CONTEXT_MENU, &HIERARCHY_PANE::onContextMenu, this );
259
260 m_events_bound = false;
261 }
262
263 SCH_SHEET_LIST hierarchy = m_frame->Schematic().Hierarchy();
264 std::set<wxString> collapsedNodes = m_collapsedPaths;
265
266 std::function<void( const wxTreeItemId& )> getCollapsedNodes =
267 [&]( const wxTreeItemId& id )
268 {
269 wxCHECK_RET( id.IsOk(), wxT( "Invalid tree item" ) );
270
271 TREE_ITEM_DATA* itemData = static_cast<TREE_ITEM_DATA*>( m_tree->GetItemData( id ) );
272
273 // Skip items without data (e.g., project root node)
274 if( itemData && m_tree->ItemHasChildren( id ) && !m_tree->IsExpanded( id )
275 && hierarchy.HasPath( itemData->m_SheetPath.Path() ) )
276 {
277 collapsedNodes.emplace( itemData->m_SheetPath.PathAsString() );
278 return;
279 }
280
281 wxTreeItemIdValue cookie;
282 wxTreeItemId child = m_tree->GetFirstChild( id, cookie );
283
284 while( child.IsOk() )
285 {
286 getCollapsedNodes( child );
287 child = m_tree->GetNextChild( id, cookie );
288 }
289 };
290
291 // If we are clearing the tree, don't try to get collapsed nodes as they
292 // might be deleted
293 if( !aClear && !m_tree->IsEmpty() )
294 {
295 collapsedNodes.clear();
296 getCollapsedNodes( m_tree->GetRootItem() );
297 }
298
299 m_tree->DeleteAllItems();
300
301 // Create project root node (not associated with virtual root sheet)
302 wxTreeItemId projectRoot = m_tree->AddRoot( getRootString(), 0, 1 );
303 // Don't set item data for the project root - it doesn't correspond to a real sheet
304
305 // Get all top-level sheets
306 std::vector<SCH_SHEET*> topLevelSheets = m_frame->Schematic().GetTopLevelSheets();
307
308 // For each top-level sheet, build its hierarchy under the project root
309 for( SCH_SHEET* sheet : topLevelSheets )
310 {
311 if( sheet )
312 {
313 m_list.clear();
314 m_list.push_back( sheet );
315
316 wxString sheetNameBase = sheet->GetShownName( false );
317
318 // If the sheet name is empty, use the filename (without extension) as fallback
319 if( sheetNameBase.IsEmpty() && sheet->GetScreen() )
320 {
321 wxFileName fn( sheet->GetScreen()->GetFileName() );
322 sheetNameBase = fn.GetName();
323 }
324
325 // Create tree item for this top-level sheet
326 wxString sheetName = formatPageString( sheetNameBase, m_list.GetPageNumber() );
327 wxTreeItemId topLevelItem = m_tree->AppendItem( projectRoot, sheetName, 0, 1 );
328 m_tree->SetItemData( topLevelItem, new TREE_ITEM_DATA( m_list ) );
329
330 // Build hierarchy for this top-level sheet
331 buildHierarchyTree( &m_list, topLevelItem );
332 }
333 }
334
336
337 m_tree->ExpandAll();
338
339 std::function<void( const wxTreeItemId& )> collapseNodes =
340 [&]( const wxTreeItemId& id )
341 {
342 wxCHECK_RET( id.IsOk(), wxT( "Invalid tree item" ) );
343
344 TREE_ITEM_DATA* itemData =
345 static_cast<TREE_ITEM_DATA*>( m_tree->GetItemData( id ) );
346
347 if( id != projectRoot && itemData &&
348 collapsedNodes.find( itemData->m_SheetPath.PathAsString() ) != collapsedNodes.end() )
349 {
350 m_tree->Collapse( id );
351 return;
352 }
353
354 wxTreeItemIdValue cookie;
355 wxTreeItemId child = m_tree->GetFirstChild( id, cookie );
356
357 while( child.IsOk() )
358 {
359 collapseNodes( child );
360 child = m_tree->GetNextChild( id, cookie );
361 }
362 };
363
364 collapseNodes( projectRoot );
365 m_collapsedPaths = std::move( collapsedNodes );
366
367 if( eventsWereBound )
368 {
369 // Enable selection events
370 Bind( wxEVT_TREE_ITEM_ACTIVATED, &HIERARCHY_PANE::onSelectSheetPath, this );
371 Bind( wxEVT_TREE_SEL_CHANGED, &HIERARCHY_PANE::onSelectSheetPath, this );
372 Bind( wxEVT_TREE_ITEM_RIGHT_CLICK, &HIERARCHY_PANE::onTreeItemRightClick, this );
373 m_tree->Bind( wxEVT_CONTEXT_MENU, &HIERARCHY_PANE::onContextMenu, this );
374
375 m_events_bound = true;
376 }
377}
378
379
380void HIERARCHY_PANE::onSelectSheetPath( wxTreeEvent& aEvent )
381{
382 wxTreeItemId itemSel = m_tree->GetSelection();
383
384 if( !itemSel.IsOk() )
385 return;
386
387 TREE_ITEM_DATA* itemData = static_cast<TREE_ITEM_DATA*>( m_tree->GetItemData( itemSel ) );
388
389 if( !itemData )
390 return;
391
392 SetCursor( wxCURSOR_ARROWWAIT );
393 m_frame->GetToolManager()->RunAction<SCH_SHEET_PATH*>( SCH_ACTIONS::changeSheet, &itemData->m_SheetPath );
394 SetCursor( wxCURSOR_ARROW );
395}
396
397
399{
400 // Update the labels of the hierarchical tree of the schematic.
401 // Must be called only for an up to date tree, to update displayed labels after
402 // a sheet name or a sheet number change.
403
404 std::function<void( const wxTreeItemId& )> updateLabel =
405 [&]( const wxTreeItemId& id )
406 {
407 TREE_ITEM_DATA* itemData = static_cast<TREE_ITEM_DATA*>( m_tree->GetItemData( id ) );
408
409 if( !itemData ) // happens if not shown in wxTreeCtrl m_tree (virtual sheet)
410 return;
411
412 SCH_SHEET* sheet = itemData->m_SheetPath.Last();
413 wxString sheetNameBase = sheet->GetField( FIELD_T::SHEET_NAME )->GetShownText( false );
414 wxString sheetName = formatPageString( sheetNameBase,
415 itemData->m_SheetPath.GetPageNumber() );
416
417 if( m_tree->GetItemText( id ) != sheetName )
418 m_tree->SetItemText( id, sheetName );
419 };
420
421 wxTreeItemId rootId = m_tree->GetRootItem();
422 updateLabel( rootId );
423
424 std::function<void( const wxTreeItemId& )> recursiveDescent =
425 [&]( const wxTreeItemId& id )
426 {
427 wxCHECK_RET( id.IsOk(), wxT( "Invalid tree item" ) );
428 wxTreeItemIdValue cookie;
429 wxTreeItemId child = m_tree->GetFirstChild( id, cookie );
430
431 while( child.IsOk() )
432 {
433 updateLabel( child );
434 recursiveDescent( child );
435 child = m_tree->GetNextChild( id, cookie );
436 }
437 };
438
439 recursiveDescent( rootId );
440}
441
442
443std::vector<wxString> HIERARCHY_PANE::GetCollapsedPaths() const
444{
445 std::vector<wxString> collapsed;
446
447 if( m_tree->IsEmpty() )
448 return collapsed;
449
450 std::function<void( const wxTreeItemId& )> collect =
451 [&]( const wxTreeItemId& id )
452 {
453 wxCHECK_RET( id.IsOk(), wxT( "Invalid tree item" ) );
454
455 TREE_ITEM_DATA* itemData = static_cast<TREE_ITEM_DATA*>( m_tree->GetItemData( id ) );
456
457 if( id != m_tree->GetRootItem() && m_tree->ItemHasChildren( id )
458 && !m_tree->IsExpanded( id ) )
459 {
460 collapsed.push_back( itemData->m_SheetPath.PathAsString() );
461 return;
462 }
463
464 wxTreeItemIdValue cookie;
465 wxTreeItemId child = m_tree->GetFirstChild( id, cookie );
466
467 while( child.IsOk() )
468 {
469 collect( child );
470 child = m_tree->GetNextChild( id, cookie );
471 }
472 };
473
474 collect( m_tree->GetRootItem() );
475 return collapsed;
476}
477
478
479void HIERARCHY_PANE::onTreeItemRightClick( wxTreeEvent& aEvent )
480{
481 onRightClick( aEvent.GetItem() );
482}
483
484
485void HIERARCHY_PANE::onContextMenu( wxContextMenuEvent& aEvent )
486{
487 // Handle right-click in empty space - treat as if clicking on an invalid item
488 onRightClick( wxTreeItemId() );
489}
490
491
492void HIERARCHY_PANE::onRightClick( wxTreeItemId aItem )
493{
494 wxMenu ctxMenu;
495 TREE_ITEM_DATA* itemData = nullptr;
496 bool isProjectRoot = false;
497
498 if( !aItem.IsOk() )
499 aItem = m_tree->GetSelection();
500
501 if( aItem.IsOk() )
502 {
503 itemData = static_cast<TREE_ITEM_DATA*>( m_tree->GetItemData( aItem ) );
504 isProjectRoot = ( m_tree->GetRootItem() == aItem.GetID() );
505 }
506
507 if( itemData )
508 {
509 ctxMenu.Append( EDIT_PAGE_NUMBER, _( "Edit Page Number" ) );
510 // The root item cannot be renamed
511 if( !isProjectRoot )
512 {
513 ctxMenu.Append( RENAME, _( "Rename" ), _( "Change name of this sheet" ) );
514
515 // Allow deleting top-level sheets (but not sub-sheets)
516 if( itemData->m_SheetPath.size() == 1 )
517 {
518 ctxMenu.Append( DELETE_TOP_LEVEL_SHEET, _( "Delete Top-Level Sheet" ),
519 _( "Remove this top-level sheet from the project" ) );
520 }
521 }
522
523 ctxMenu.AppendSeparator();
524 }
525
526 // Always allow creating new top-level sheets (root is hidden with wxTR_HIDE_ROOT)
527 ctxMenu.Append( NEW_TOP_LEVEL_SHEET, _( "New Top-Level Sheet" ), _( "Create a new top-level sheet" ) );
528 ctxMenu.AppendSeparator();
529
530 ctxMenu.Append( EXPAND_ALL, ACTIONS::expandAll.GetMenuItem() );
531 ctxMenu.Append( COLLAPSE_ALL, ACTIONS::collapseAll.GetMenuItem() );
532
533
534 int selected = GetPopupMenuSelectionFromUser( ctxMenu );
535
536 switch( selected )
537 {
539 {
540 // Create a new top-level sheet
541 wxTextEntryDialog dlg( m_frame, _( "Enter name for new top-level sheet:" ),
542 _( "New Top-Level Sheet" ),
543 _( "Untitled" ) );
544
545 if( dlg.ShowModal() == wxID_OK )
546 {
547 wxString newName = dlg.GetValue();
548
549 if( !newName.IsEmpty() )
550 {
551 SCH_COMMIT commit( m_frame );
552
553 // Create new sheet and screen
554 SCH_SHEET* newSheet = new SCH_SHEET( &m_frame->Schematic() );
555 SCH_SCREEN* newScreen = new SCH_SCREEN( &m_frame->Schematic() );
556
557 newSheet->SetScreen( newScreen );
558 newSheet->GetField( FIELD_T::SHEET_NAME )->SetText( newName );
559
560 // Generate a unique filename
561 wxString filename = newName;
562 filename.Replace( " ", "_" );
563 filename = filename.Lower();
564
565 if( !filename.EndsWith( ".kicad_sch" ) )
566 filename += ".kicad_sch";
567
568 newScreen->SetFileName( filename );
569
570 // Calculate the next page number
571 int nextPage = m_frame->Schematic().GetTopLevelSheets().size() + 1;
572 newScreen->SetPageNumber( wxString::Format( "%d", nextPage ) );
573
574 // Add to schematic
575 m_frame->Schematic().AddTopLevelSheet( newSheet );
576
577 commit.Push( _( "Add new top-level sheet" ) );
578
579 // Refresh the hierarchy tree
581 }
582 }
583 break;
584 }
585
587 {
588 if( itemData && itemData->m_SheetPath.size() == 1 )
589 {
590 SCH_SHEET* sheet = itemData->m_SheetPath.Last();
591
592 // Confirm deletion
593 wxString msg = wxString::Format( _( "Delete top-level sheet '%s'?\n\nThis cannot be undone." ),
594 sheet->GetName() );
595
596 if( wxMessageBox( msg, _( "Delete Top-Level Sheet" ), wxYES_NO | wxICON_QUESTION, m_frame ) == wxYES )
597 {
598 // Don't allow deleting the last top-level sheet
599 if( m_frame->Schematic().GetTopLevelSheets().size() <= 1 )
600 {
601 wxMessageBox( _( "Cannot delete the last top-level sheet." ), _( "Delete Top-Level Sheet" ),
602 wxOK | wxICON_ERROR, m_frame );
603 break;
604 }
605
606 SCH_COMMIT commit( m_frame );
607
608 // Remove from schematic
609 if( m_frame->Schematic().RemoveTopLevelSheet( sheet ) )
610 {
611 commit.Push( _( "Delete top-level sheet" ) );
612
613 // Refresh the hierarchy tree
615 }
616 }
617 }
618 break;
619 }
620
621 case EDIT_PAGE_NUMBER:
622 {
623 wxString msg;
624 wxString sheetPath = itemData->m_SheetPath.PathHumanReadable( false, true );
625 wxString pageNumber = itemData->m_SheetPath.GetPageNumber();
626
627 msg.Printf( _( "Enter page number for sheet path %s" ),
628 ( sheetPath.Length() > 20 ) ? wxS( " \n" ) + sheetPath + wxT( ": " )
629 : sheetPath + wxT( ": " ) );
630
631 wxTextEntryDialog dlg( m_frame, msg, _( "Edit Sheet Page Number" ), pageNumber );
632
633 dlg.SetTextValidator( wxFILTER_ALPHANUMERIC ); // No white space.
634
635 if( dlg.ShowModal() == wxID_OK && dlg.GetValue() != itemData->m_SheetPath.GetPageNumber() )
636 {
637 SCH_COMMIT commit( m_frame );
638 SCH_SHEET_PATH parentPath = itemData->m_SheetPath;
639 parentPath.pop_back();
640
641 commit.Modify( itemData->m_SheetPath.Last(), parentPath.LastScreen() );
642
643 itemData->m_SheetPath.SetPageNumber( dlg.GetValue() );
644
645 if( itemData->m_SheetPath == m_frame->GetCurrentSheet() )
646 {
647 if( m_frame->GetScreen() )
648 {
649 m_frame->GetScreen()->SetPageNumber( dlg.GetValue() );
650 m_frame->OnPageSettingsChange();
651 }
652 }
653
654 commit.Push( wxS( "Change sheet page number." ) );
655
657 }
658
659 break;
660 }
661 case EXPAND_ALL:
662 m_tree->ExpandAll();
663 break;
664 case COLLAPSE_ALL:
665 m_tree->CollapseAll();
666 break;
667 case RENAME:
668 m_tree->SetItemText( aItem, itemData->m_SheetPath.Last()->GetName() );
669 m_tree->EditLabel( aItem );
671 break;
672 }
673}
674
675
676void HIERARCHY_PANE::onTreeEditFinished( wxTreeEvent& event )
677{
678 TREE_ITEM_DATA* data = static_cast<TREE_ITEM_DATA*>( m_tree->GetItemData( event.GetItem() ) );
679 wxString newName = event.GetLabel();
680
681 if( data && data->m_SheetPath.Last() )
682 {
683 if( !newName.IsEmpty() )
684 {
685 // The editor holds only the page name as a text, while normally
686 // the tree items displaying it suffixed with the page number
687 if( data->m_SheetPath.Last()->GetName() != newName )
688 {
689 SCH_COMMIT commit( m_frame );
690 SCH_SCREEN* modifyScreen = nullptr;
691
692 // For top-level sheets (size == 1), modify on the virtual root's screen
693 // For sub-sheets, modify on the parent sheet's screen
694 if( data->m_SheetPath.size() == 1 )
695 {
696 modifyScreen = m_frame->Schematic().Root().GetScreen();
697 }
698 else
699 {
700 const SCH_SHEET* parentSheet = data->m_SheetPath.GetSheet( data->m_SheetPath.size() - 2 );
701 if( parentSheet )
702 modifyScreen = parentSheet->GetScreen();
703 }
704
705 if( modifyScreen )
706 {
708 modifyScreen );
709
710 data->m_SheetPath.Last()->SetName( newName );
711
712 renameIdenticalSheets( data->m_SheetPath, newName, &commit );
713
714 if( !commit.Empty() )
715 commit.Push( _( "Renaming sheet" ) );
716
717 if( data->m_SheetPath == m_frame->GetCurrentSheet() )
718 {
719 m_frame->OnPageSettingsChange();
720 }
721 }
722 }
723 }
724
725 m_tree->SetItemText( event.GetItem(), formatPageString( data->m_SheetPath.Last()->GetName(),
726 data->m_SheetPath.GetPageNumber() ) );
728 // The event needs to be rejected otherwise the SetItemText call above
729 // will be ineffective (the treeview item will hold the editor's content)
730 event.Veto();
731 }
732}
733
734
735void HIERARCHY_PANE::onCharHook( wxKeyEvent& aKeyStroke )
736{
737 int hotkey = aKeyStroke.GetKeyCode();
738
739 int mods = aKeyStroke.GetModifiers();
740
741 // the flag wxMOD_ALTGR is defined in wxWidgets as wxMOD_CONTROL|wxMOD_ALT
742 // So AltGr key cannot used as modifier key because it is the same as Alt key + Ctrl key.
743#if CAN_USE_ALTGR_KEY
744 if( wxmods & wxMOD_ALTGR )
745 mods |= MD_ALTGR;
746 else
747#endif
748 {
749 if( mods & wxMOD_CONTROL )
750 hotkey += MD_CTRL;
751
752 if( mods & wxMOD_ALT )
753 hotkey += MD_ALT;
754 }
755
756 if( mods & wxMOD_SHIFT )
757 hotkey += MD_SHIFT;
758
759#ifdef wxMOD_META
760 if( mods & wxMOD_META )
761 hotkey += MD_META;
762#endif
763
764#ifdef wxMOD_WIN
765 if( mods & wxMOD_WIN )
766 hotkey += MD_SUPER;
767#endif
768
769 if( hotkey == ACTIONS::expandAll.GetHotKey()
770 || hotkey == ACTIONS::expandAll.GetHotKeyAlt() )
771 {
772 m_tree->ExpandAll();
773 return;
774 }
775 else if( hotkey == ACTIONS::collapseAll.GetHotKey()
776 || hotkey == ACTIONS::collapseAll.GetHotKeyAlt() )
777 {
778 m_tree->CollapseAll();
779 return;
780 }
781 else
782 {
783 aKeyStroke.Skip();
784 }
785}
786
787
789{
790 // Pane may be repainting while schematic is in flux
791 if ( !m_frame->Schematic().IsValid() )
792 return _( "Schematic" );
793
794 // Return the project name for the root node
795 wxString projectName = m_frame->Schematic().Project().GetProjectName();
796
797 if( projectName.IsEmpty() )
798 projectName = _( "Schematic" );
799
800 return projectName;
801}
802
803
804wxString HIERARCHY_PANE::formatPageString( const wxString& aName, const wxString& aPage )
805{
806 return aName + wxT( " " ) + wxString::Format( _( "(page %s)" ), aPage );
807}
808
810{
811 std::function<void( const wxTreeItemId& )> recursiveDescent = [&]( const wxTreeItemId& id )
812 {
813 wxCHECK_RET( id.IsOk(), wxT( "Invalid tree item" ) );
814
815 TREE_ITEM_DATA* itemData = static_cast<TREE_ITEM_DATA*>( m_tree->GetItemData( id ) );
816
817 // Skip items without data (e.g., project root node)
818 if( itemData && itemData->m_SheetPath.Cmp( path ) != 0 && itemData->m_SheetPath.Last() == path.Last() )
819 {
820 wxFont font = m_tree->GetItemFont( id );
821 font.SetUnderlined( highLighted );
822 m_tree->SetItemFont( id, font );
823 }
824
825 wxTreeItemIdValue cookie;
826 wxTreeItemId child = m_tree->GetFirstChild( id, cookie );
827
828 while( child.IsOk() )
829 {
830 recursiveDescent( child );
831 child = m_tree->GetNextChild( id, cookie );
832 }
833 };
834
835 recursiveDescent( m_tree->GetRootItem() );
836}
837
839 const wxString newName, SCH_COMMIT* commit )
840{
841 std::function<void( const wxTreeItemId& )> recursiveDescent = [&]( const wxTreeItemId& id )
842 {
843 wxCHECK_RET( id.IsOk(), wxT( "Invalid tree item" ) );
844
845 TREE_ITEM_DATA* data = static_cast<TREE_ITEM_DATA*>( m_tree->GetItemData( id ) );
846
847 // Skip items without data (e.g., project root node)
848 if( !data )
849 {
850 wxTreeItemIdValue cookie;
851 wxTreeItemId child = m_tree->GetFirstChild( id, cookie );
852
853 while( child.IsOk() )
854 {
855 recursiveDescent( child );
856 child = m_tree->GetNextChild( id, cookie );
857 }
858
859 return;
860 }
861
862 // Check if this is an identical sheet that needs renaming (but not the renamed sheet itself)
863 if( data->m_SheetPath.Cmp( renamedSheet ) != 0
864 && data->m_SheetPath.Last() == renamedSheet.Last() )
865 {
866 SCH_SCREEN* modifyScreen = nullptr;
867
868 // For top-level sheets (size == 1), modify on the virtual root's screen
869 // For sub-sheets, modify on the parent sheet's screen
870 if( data->m_SheetPath.size() == 1 )
871 {
872 modifyScreen = m_frame->Schematic().Root().GetScreen();
873 }
874 else
875 {
876 const SCH_SHEET* parentSheet = data->m_SheetPath.GetSheet( data->m_SheetPath.size() - 2 );
877 if( parentSheet )
878 modifyScreen = parentSheet->GetScreen();
879 }
880
881 if( modifyScreen )
882 {
884 modifyScreen );
885
886 data->m_SheetPath.Last()->SetName( newName );
887
888 if( data->m_SheetPath == m_frame->GetCurrentSheet() )
889 {
890 m_frame->OnPageSettingsChange();
891 }
892
893 m_tree->SetItemText( id, formatPageString( data->m_SheetPath.Last()->GetName(),
894 data->m_SheetPath.GetPageNumber() ) );
895 }
896 }
897
898 wxTreeItemIdValue cookie;
899 wxTreeItemId child = m_tree->GetFirstChild( id, cookie );
900
901 while( child.IsOk() )
902 {
903 recursiveDescent( child );
904 child = m_tree->GetNextChild( id, cookie );
905 }
906 };
907
908 recursiveDescent( m_tree->GetRootItem() );
909}
wxBitmap KiBitmap(BITMAPS aBitmap, int aHeightTag)
Construct a wxBitmap from an image identifier Returns the image from the active theme if the image ha...
Definition bitmap.cpp:104
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
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:47
wxString GetFileName() const
Return the filename corresponding to this sheet.
Definition sch_sheet.h:341
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this sheet.
wxString GetName() const
Definition sch_sheet.h:113
void SetName(const wxString &aName)
Definition sch_sheet.h:114
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:116
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)
@ 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