You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

315 lines
10 KiB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
  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 <stambaughw@gmail.com>
  6. * Copyright (C) 2004-2022 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. #include <bitmaps.h>
  26. #include <sch_edit_frame.h>
  27. #include <sch_sheet.h>
  28. #include <sch_sheet_path.h>
  29. #include <schematic.h>
  30. #include <tool/tool_manager.h>
  31. #include <tools/ee_actions.h>
  32. #include <hierarch.h>
  33. #include <kiface_base.h>
  34. #include <eeschema_settings.h>
  35. #include <wx/object.h>
  36. #include <wx/generic/textdlgg.h>
  37. #include <wx/menu.h>
  38. /**
  39. * Store an SCH_SHEET_PATH of each sheet in hierarchy.
  40. */
  41. class TREE_ITEM_DATA : public wxTreeItemData
  42. {
  43. public:
  44. SCH_SHEET_PATH m_SheetPath;
  45. TREE_ITEM_DATA( SCH_SHEET_PATH& sheet ) :
  46. wxTreeItemData()
  47. {
  48. m_SheetPath = sheet;
  49. }
  50. };
  51. // Need to use wxRTTI macros in order for OnCompareItems to work properly
  52. // See: https://docs.wxwidgets.org/3.1/classwx_tree_ctrl.html#ab90a465793c291ca7aa827a576b7d146
  53. wxIMPLEMENT_ABSTRACT_CLASS( HIERARCHY_TREE, wxTreeCtrl );
  54. int HIERARCHY_TREE::OnCompareItems( const wxTreeItemId& item1, const wxTreeItemId& item2 )
  55. {
  56. SCH_SHEET_PATH* item1Path = &static_cast<TREE_ITEM_DATA*>( GetItemData( item1 ) )->m_SheetPath;
  57. SCH_SHEET_PATH* item2Path = &static_cast<TREE_ITEM_DATA*>( GetItemData( item2 ) )->m_SheetPath;
  58. return item1Path->ComparePageNum( *item2Path );
  59. }
  60. HIERARCHY_NAVIG_PANEL::HIERARCHY_NAVIG_PANEL( SCH_EDIT_FRAME* aParent ) :
  61. WX_PANEL( aParent )
  62. {
  63. wxASSERT( dynamic_cast<SCH_EDIT_FRAME*>( aParent ) );
  64. m_frame = aParent;
  65. wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL );
  66. SetSizer( sizer );
  67. m_tree = new HIERARCHY_TREE( this );
  68. // Make an image list containing small icons
  69. // All icons are expected having the same size.
  70. wxBitmap tree_nosel_bm( KiBitmap( BITMAPS::tree_nosel ) );
  71. wxImageList* imageList = new wxImageList( tree_nosel_bm.GetWidth(), tree_nosel_bm.GetHeight(),
  72. true, 2 );
  73. imageList->Add( tree_nosel_bm );
  74. imageList->Add( KiBitmap( BITMAPS::tree_sel ) );
  75. m_tree->AssignImageList( imageList );
  76. sizer->Add( m_tree, 1, wxEXPAND, wxBORDER_NONE, 0 );
  77. m_events_bound = false;
  78. UpdateHierarchyTree();
  79. // Enable selection events
  80. Bind( wxEVT_TREE_ITEM_ACTIVATED, &HIERARCHY_NAVIG_PANEL::onSelectSheetPath, this );
  81. Bind( wxEVT_TREE_SEL_CHANGED, &HIERARCHY_NAVIG_PANEL::onSelectSheetPath, this );
  82. Bind( wxEVT_TREE_ITEM_RIGHT_CLICK, &HIERARCHY_NAVIG_PANEL::onRightClick, this );
  83. m_events_bound = true;
  84. }
  85. HIERARCHY_NAVIG_PANEL::~HIERARCHY_NAVIG_PANEL()
  86. {
  87. Unbind( wxEVT_TREE_ITEM_ACTIVATED, &HIERARCHY_NAVIG_PANEL::onSelectSheetPath, this );
  88. Unbind( wxEVT_TREE_SEL_CHANGED, &HIERARCHY_NAVIG_PANEL::onSelectSheetPath, this );
  89. Unbind( wxEVT_TREE_ITEM_RIGHT_CLICK, &HIERARCHY_NAVIG_PANEL::onRightClick, this );
  90. }
  91. void HIERARCHY_NAVIG_PANEL::buildHierarchyTree( SCH_SHEET_PATH* aList, const wxTreeItemId& aParent )
  92. {
  93. std::vector<SCH_ITEM*> sheetChildren;
  94. aList->LastScreen()->GetSheets( &sheetChildren );
  95. for( SCH_ITEM* aItem : sheetChildren )
  96. {
  97. SCH_SHEET* sheet = static_cast<SCH_SHEET*>( aItem );
  98. aList->push_back( sheet );
  99. wxString sheetName = formatPageString( sheet->GetFields()[SHEETNAME].GetShownText(),
  100. aList->GetPageNumber() );
  101. wxTreeItemId child = m_tree->AppendItem( aParent, sheetName, 0, 1 );
  102. m_tree->SetItemData( child, new TREE_ITEM_DATA( *aList ) );
  103. buildHierarchyTree( aList, child );
  104. aList->pop_back();
  105. }
  106. m_tree->SortChildren( aParent );
  107. }
  108. void HIERARCHY_NAVIG_PANEL::UpdateHierarchySelection()
  109. {
  110. bool eventsWereBound = m_events_bound;
  111. if( eventsWereBound )
  112. {
  113. // Disable selection events
  114. Unbind( wxEVT_TREE_ITEM_ACTIVATED, &HIERARCHY_NAVIG_PANEL::onSelectSheetPath, this );
  115. Unbind( wxEVT_TREE_SEL_CHANGED, &HIERARCHY_NAVIG_PANEL::onSelectSheetPath, this );
  116. Unbind( wxEVT_TREE_ITEM_RIGHT_CLICK, &HIERARCHY_NAVIG_PANEL::onRightClick, this );
  117. m_events_bound = false;
  118. }
  119. bool sheetSelected = false;
  120. std::function<void( const wxTreeItemId& )> recursiveDescent =
  121. [&]( const wxTreeItemId& id )
  122. {
  123. wxCHECK_RET( id.IsOk(), wxT( "Invalid tree item" ) );
  124. TREE_ITEM_DATA* itemData = static_cast<TREE_ITEM_DATA*>( m_tree->GetItemData( id ) );
  125. if( itemData->m_SheetPath == m_frame->GetCurrentSheet() )
  126. {
  127. m_tree->EnsureVisible( id );
  128. m_tree->SetItemBold( id, true );
  129. }
  130. else
  131. {
  132. m_tree->SetItemBold( id, false );
  133. }
  134. if( itemData->m_SheetPath.Last()->IsSelected() )
  135. {
  136. m_tree->EnsureVisible( id );
  137. m_tree->SelectItem( id );
  138. sheetSelected = true;
  139. }
  140. wxTreeItemIdValue cookie;
  141. wxTreeItemId child = m_tree->GetFirstChild( id, cookie );
  142. while( child.IsOk() )
  143. {
  144. recursiveDescent( child );
  145. child = m_tree->GetNextChild( id, cookie );
  146. }
  147. };
  148. recursiveDescent( m_tree->GetRootItem() );
  149. if( !sheetSelected && m_tree->GetSelection() )
  150. m_tree->SelectItem( m_tree->GetSelection(), false );
  151. if( eventsWereBound )
  152. {
  153. // Enable selection events
  154. Bind( wxEVT_TREE_ITEM_ACTIVATED, &HIERARCHY_NAVIG_PANEL::onSelectSheetPath, this );
  155. Bind( wxEVT_TREE_SEL_CHANGED, &HIERARCHY_NAVIG_PANEL::onSelectSheetPath, this );
  156. Bind( wxEVT_TREE_ITEM_RIGHT_CLICK, &HIERARCHY_NAVIG_PANEL::onRightClick, this );
  157. m_events_bound = true;
  158. }
  159. }
  160. void HIERARCHY_NAVIG_PANEL::UpdateHierarchyTree()
  161. {
  162. Freeze();
  163. bool eventsWereBound = m_events_bound;
  164. if( eventsWereBound )
  165. {
  166. // Disable selection events
  167. Unbind( wxEVT_TREE_ITEM_ACTIVATED, &HIERARCHY_NAVIG_PANEL::onSelectSheetPath, this );
  168. Unbind( wxEVT_TREE_SEL_CHANGED, &HIERARCHY_NAVIG_PANEL::onSelectSheetPath, this );
  169. Unbind( wxEVT_TREE_ITEM_RIGHT_CLICK, &HIERARCHY_NAVIG_PANEL::onRightClick, this );
  170. m_events_bound = false;
  171. }
  172. m_list.clear();
  173. m_list.push_back( &m_frame->Schematic().Root() );
  174. m_tree->DeleteAllItems();
  175. wxTreeItemId root = m_tree->AddRoot( getRootString(), 0, 1 );
  176. m_tree->SetItemData( root, new TREE_ITEM_DATA( m_list ) );
  177. buildHierarchyTree( &m_list, root );
  178. UpdateHierarchySelection();
  179. m_tree->ExpandAll();
  180. if( eventsWereBound )
  181. {
  182. // Enable selection events
  183. Bind( wxEVT_TREE_ITEM_ACTIVATED, &HIERARCHY_NAVIG_PANEL::onSelectSheetPath, this );
  184. Bind( wxEVT_TREE_SEL_CHANGED, &HIERARCHY_NAVIG_PANEL::onSelectSheetPath, this );
  185. Bind( wxEVT_TREE_ITEM_RIGHT_CLICK, &HIERARCHY_NAVIG_PANEL::onRightClick, this );
  186. m_events_bound = true;
  187. }
  188. Thaw();
  189. }
  190. void HIERARCHY_NAVIG_PANEL::onSelectSheetPath( wxTreeEvent& aEvent )
  191. {
  192. wxTreeItemId itemSel = m_tree->GetSelection();
  193. TREE_ITEM_DATA* itemData = static_cast<TREE_ITEM_DATA*>( m_tree->GetItemData( itemSel ) );
  194. SetCursor( wxCURSOR_ARROWWAIT );
  195. m_frame->GetToolManager()->RunAction( EE_ACTIONS::changeSheet, true, &itemData->m_SheetPath );
  196. SetCursor( wxCURSOR_ARROW );
  197. }
  198. void HIERARCHY_NAVIG_PANEL::onRightClick( wxTreeEvent& aEvent )
  199. {
  200. wxMenu ctxMenu;
  201. ctxMenu.Append( 1, _( "Edit Page Number" ) );
  202. if( GetPopupMenuSelectionFromUser( ctxMenu ) == 1 )
  203. {
  204. wxTreeItemId itemSel = m_tree->GetSelection();
  205. TREE_ITEM_DATA* itemData = static_cast<TREE_ITEM_DATA*>( m_tree->GetItemData( itemSel ) );
  206. wxString msg;
  207. wxString sheetPath = itemData->m_SheetPath.PathHumanReadable( false );
  208. wxString pageNumber = itemData->m_SheetPath.GetPageNumber();
  209. msg.Printf( _( "Enter page number for sheet path%s" ),
  210. ( sheetPath.Length() > 20 ) ? "\n" + sheetPath : " " + sheetPath );
  211. wxTextEntryDialog dlg( m_frame, msg, _( "Edit Sheet Page Number" ), pageNumber );
  212. dlg.SetTextValidator( wxFILTER_ALPHANUMERIC ); // No white space.
  213. if( dlg.ShowModal() == wxID_OK && dlg.GetValue() != itemData->m_SheetPath.GetPageNumber() )
  214. {
  215. m_frame->SaveCopyInUndoList( itemData->m_SheetPath.LastScreen(),
  216. itemData->m_SheetPath.Last(), UNDO_REDO::CHANGED, false );
  217. itemData->m_SheetPath.SetPageNumber( dlg.GetValue() );
  218. if( itemData->m_SheetPath == m_frame->GetCurrentSheet() )
  219. {
  220. m_frame->GetScreen()->SetPageNumber( dlg.GetValue() );
  221. m_frame->OnPageSettingsChange();
  222. }
  223. m_frame->OnModify();
  224. }
  225. }
  226. }
  227. wxString HIERARCHY_NAVIG_PANEL::getRootString()
  228. {
  229. SCH_SHEET* rootSheet = &m_frame->Schematic().Root();
  230. SCH_SHEET_PATH rootPath;
  231. rootPath.push_back( rootSheet );
  232. return formatPageString ( _( "Root" ), rootPath.GetPageNumber() );
  233. }
  234. wxString HIERARCHY_NAVIG_PANEL::formatPageString( const wxString& aName, const wxString& aPage )
  235. {
  236. return aName + wxT( " " ) + wxString::Format( _( "(page %s)" ), aPage );
  237. }