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.
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 PathHumanReadable(bool aUseShortRootName=true, bool aStripTrailingSeparator=false, bool aEscapeSheetNames=false) const
Return the sheet path in a human readable form made from the sheet names.
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