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.

837 lines
25 KiB

  1. /*
  2. * This program source code file is part of KICAD, a free EDA CAD application.
  3. *
  4. * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version 2
  9. * of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, you may find one here:
  18. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  19. * or you may search the http://www.gnu.org website for the version 2 license,
  20. * or you may write to the Free Software Foundation, Inc.,
  21. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  22. */
  23. #include <dialogs/panel_toolbar_customization.h>
  24. #include <bitmaps.h>
  25. #include <settings/app_settings.h>
  26. #include <tool/ui/toolbar_configuration.h>
  27. #include <widgets/split_button.h>
  28. #include <widgets/std_bitmap_button.h>
  29. #include <magic_enum.hpp>
  30. #include <wx/listctrl.h>
  31. #include <wx/menu.h>
  32. // Simple IDs for the split button menu
  33. enum
  34. {
  35. ID_SEPARATOR_MENU = ( wxID_HIGHEST + 5 ),
  36. ID_SPACER_MENU,
  37. ID_GROUP_MENU
  38. };
  39. static std::map<TOOLBAR_LOC, wxString> s_toolbarNameMap = {
  40. { TOOLBAR_LOC::LEFT, _( "Left" ) },
  41. { TOOLBAR_LOC::RIGHT, _( "Right" ) },
  42. { TOOLBAR_LOC::TOP_MAIN, _( "Top main" ) },
  43. { TOOLBAR_LOC::TOP_AUX, _( "Top auxillary" ) }
  44. };
  45. class TOOLBAR_TREE_ITEM_DATA : public wxTreeItemData
  46. {
  47. public:
  48. TOOLBAR_TREE_ITEM_DATA()
  49. { }
  50. TOOLBAR_TREE_ITEM_DATA( TOOLBAR_ITEM_TYPE aType ) :
  51. m_type( aType )
  52. { }
  53. TOOLBAR_TREE_ITEM_DATA( TOOLBAR_ITEM_TYPE aType, int aSize ) :
  54. m_type( aType ),
  55. m_size( aSize )
  56. {
  57. wxASSERT( aType == TOOLBAR_ITEM_TYPE::SPACER );
  58. }
  59. TOOLBAR_TREE_ITEM_DATA( TOOLBAR_ITEM_TYPE aType, wxString aName ) :
  60. m_type( aType ),
  61. m_name( aName )
  62. {
  63. wxASSERT( aType == TOOLBAR_ITEM_TYPE::CONTROL
  64. || aType == TOOLBAR_ITEM_TYPE::TB_GROUP );
  65. }
  66. TOOLBAR_TREE_ITEM_DATA( TOOLBAR_ITEM_TYPE aType, TOOL_ACTION* aAction ) :
  67. m_type( aType ),
  68. m_action( aAction )
  69. {
  70. wxASSERT( aType == TOOLBAR_ITEM_TYPE::TOOL );
  71. }
  72. void SetAction( TOOL_ACTION* aAction ) { m_action = aAction; }
  73. TOOL_ACTION* GetAction() const { return m_action; }
  74. void SetName( const wxString& aName ) { m_name = aName; }
  75. const wxString& GetName() const { return m_name; }
  76. void SetSize( int aSize ) { m_size = aSize; }
  77. int GetSize() const { return m_size; }
  78. TOOLBAR_ITEM_TYPE GetType() const { return m_type; }
  79. private:
  80. // Item type
  81. TOOLBAR_ITEM_TYPE m_type;
  82. // Tool properties
  83. TOOL_ACTION* m_action;
  84. // Spacer properties
  85. int m_size;
  86. // Group/control properties
  87. wxString m_name;
  88. };
  89. PANEL_TOOLBAR_CUSTOMIZATION::PANEL_TOOLBAR_CUSTOMIZATION( wxWindow* aParent, APP_SETTINGS_BASE* aCfg,
  90. TOOLBAR_SETTINGS* aTbSettings,
  91. std::vector<TOOL_ACTION*> aTools,
  92. std::vector<ACTION_TOOLBAR_CONTROL*> aControls ) :
  93. PANEL_TOOLBAR_CUSTOMIZATION_BASE( aParent ),
  94. m_actionImageList( nullptr ),
  95. m_appSettings( aCfg ),
  96. m_appTbSettings( aTbSettings )
  97. {
  98. // Copy the tools and controls into the internal maps
  99. for( auto& tool : aTools )
  100. m_availableTools.emplace( tool->GetName(), tool );
  101. for( auto& control : aControls )
  102. m_availableControls.emplace( control->GetName(), control );
  103. // Configure the Ui elements
  104. m_btnToolDelete->SetBitmap( KiBitmapBundle( BITMAPS::small_trash ) );
  105. m_btnToolMoveUp->SetBitmap( KiBitmapBundle( BITMAPS::small_up ) );
  106. m_btnToolMoveDown->SetBitmap( KiBitmapBundle( BITMAPS::small_down ) );
  107. m_btnAddTool->SetBitmap( KiBitmapBundle( BITMAPS::left ) );
  108. m_insertButton->SetLabel( _( "Insert separator" ) );
  109. //m_insertButton->SetWidthPadding( 4 );
  110. // Populate the browse library options
  111. wxMenu* insertMenu = m_insertButton->GetSplitButtonMenu();
  112. insertMenu->Append( ID_SPACER_MENU, _( "Insert spacer" ) );
  113. insertMenu->Append( ID_GROUP_MENU, _( "Insert group" ) );
  114. insertMenu->Bind( wxEVT_COMMAND_MENU_SELECTED, &PANEL_TOOLBAR_CUSTOMIZATION::onSpacerPress,
  115. this, ID_SPACER_MENU );
  116. insertMenu->Bind( wxEVT_COMMAND_MENU_SELECTED, &PANEL_TOOLBAR_CUSTOMIZATION::onGroupPress,
  117. this, ID_GROUP_MENU );
  118. // This is the button only press for the browse button instead of the menu
  119. m_insertButton->Bind( wxEVT_BUTTON, &PANEL_TOOLBAR_CUSTOMIZATION::onSeparatorPress, this );
  120. // TODO (ISM): Enable moving up/down and draging
  121. m_btnToolMoveDown->Enable( false );
  122. m_btnToolMoveUp->Enable( false );
  123. }
  124. PANEL_TOOLBAR_CUSTOMIZATION::~PANEL_TOOLBAR_CUSTOMIZATION()
  125. {
  126. delete m_actionImageList;
  127. }
  128. void PANEL_TOOLBAR_CUSTOMIZATION::ResetPanel()
  129. {
  130. // Go over every toolbar and initialize things
  131. for( auto& tb : magic_enum::enum_values<TOOLBAR_LOC>() )
  132. {
  133. // Create a shadow toolbar
  134. auto tbConfig = m_appTbSettings->DefaultToolbarConfig( tb );
  135. if( tbConfig.has_value() )
  136. m_toolbars[tb] = tbConfig.value();
  137. }
  138. // Populate the toolbar view with the default toolbar
  139. m_tbChoice->SetSelection( 0 );
  140. auto firstTb = magic_enum::enum_cast<TOOLBAR_LOC>( 0 );
  141. if( firstTb.has_value() )
  142. m_currentToolbar = firstTb.value();
  143. populateToolbarTree();
  144. }
  145. bool PANEL_TOOLBAR_CUSTOMIZATION::TransferDataToWindow()
  146. {
  147. wxArrayString tbChoices;
  148. // Go over every toolbar and initialize things
  149. for( auto& tb : magic_enum::enum_values<TOOLBAR_LOC>() )
  150. {
  151. // Create a shadow toolbar
  152. auto tbConfig = m_appTbSettings->GetToolbarConfig( tb );
  153. if( tbConfig.has_value() )
  154. m_toolbars.emplace( tb, tbConfig.value() );
  155. // Setup the UI name
  156. const auto& tbName = s_toolbarNameMap.find( tb );
  157. wxASSERT_MSG( tbName != s_toolbarNameMap.end(),
  158. wxString::Format( "Unknown toolbar: %s", magic_enum::enum_name( tb ) ) );
  159. tbChoices.Add( tbName->second );
  160. }
  161. m_tbChoice->Set( tbChoices );
  162. // Always populate the actions before the toolbars, that way the icons are available
  163. populateActions();
  164. // Populate the toolbar view
  165. m_tbChoice->SetSelection( 0 );
  166. auto firstTb = magic_enum::enum_cast<TOOLBAR_LOC>( 0 );
  167. if( firstTb.has_value() )
  168. m_currentToolbar = firstTb.value();
  169. populateToolbarTree();
  170. // Sync the enable/disable control
  171. enableCustomControls( m_appSettings->m_CustomToolbars );
  172. m_customToolbars->SetValue( m_appSettings->m_CustomToolbars );
  173. return true;
  174. }
  175. bool PANEL_TOOLBAR_CUSTOMIZATION::TransferDataFromWindow()
  176. {
  177. m_appSettings->m_CustomToolbars = m_customToolbars->GetValue();
  178. // Store the current toolbar
  179. auto currentTb = parseToolbarTree();
  180. if( currentTb.has_value() )
  181. m_toolbars[m_currentToolbar] = currentTb.value();
  182. // Write the shadow toolbars with changes back to the app toolbar settings
  183. for( auto& tb : m_toolbars )
  184. m_appTbSettings->SetStoredToolbarConfig( tb.first, tb.second );
  185. return true;
  186. }
  187. std::optional<TOOLBAR_CONFIGURATION> PANEL_TOOLBAR_CUSTOMIZATION::parseToolbarTree()
  188. {
  189. TOOLBAR_CONFIGURATION config;
  190. wxTreeItemId mainId;
  191. wxTreeItemId rootId = m_toolbarTree->GetRootItem();
  192. wxTreeItemIdValue mainCookie;
  193. if( !rootId.IsOk() )
  194. return std::nullopt;
  195. mainId = m_toolbarTree->GetFirstChild( rootId, mainCookie );
  196. while( mainId.IsOk() )
  197. {
  198. wxTreeItemData* treeData = m_toolbarTree->GetItemData( mainId );
  199. TOOLBAR_TREE_ITEM_DATA* tbData = dynamic_cast<TOOLBAR_TREE_ITEM_DATA*>( treeData );
  200. wxASSERT( tbData );
  201. switch( tbData->GetType() )
  202. {
  203. case TOOLBAR_ITEM_TYPE::SPACER:
  204. config.AppendSpacer( tbData->GetSize() );
  205. break;
  206. case TOOLBAR_ITEM_TYPE::SEPARATOR:
  207. config.AppendSeparator();
  208. break;
  209. case TOOLBAR_ITEM_TYPE::CONTROL:
  210. config.AppendControl( tbData->GetName().ToStdString() );
  211. break;
  212. case TOOLBAR_ITEM_TYPE::TOOL:
  213. config.AppendAction( *( tbData->GetAction() ) );
  214. break;
  215. case TOOLBAR_ITEM_TYPE::TB_GROUP:
  216. TOOLBAR_GROUP_CONFIG grpConfig( tbData->GetName() );
  217. if( m_toolbarTree->ItemHasChildren( mainId ) )
  218. {
  219. wxTreeItemIdValue childCookie;
  220. wxTreeItemId childId = m_toolbarTree->GetFirstChild( mainId, childCookie );
  221. while( childId.IsOk() )
  222. {
  223. wxTreeItemData* childTreeData = m_toolbarTree->GetItemData( childId );
  224. TOOLBAR_TREE_ITEM_DATA* childTbData = dynamic_cast<TOOLBAR_TREE_ITEM_DATA*>( childTreeData );
  225. wxASSERT( childTbData );
  226. switch( childTbData->GetType() )
  227. {
  228. case TOOLBAR_ITEM_TYPE::TB_GROUP:
  229. case TOOLBAR_ITEM_TYPE::SPACER:
  230. case TOOLBAR_ITEM_TYPE::SEPARATOR:
  231. case TOOLBAR_ITEM_TYPE::CONTROL:
  232. wxASSERT_MSG( false, "Invalid entry in a group" );
  233. break;
  234. case TOOLBAR_ITEM_TYPE::TOOL:
  235. grpConfig.AddAction( *( childTbData->GetAction() ) );
  236. break;
  237. }
  238. childId = m_toolbarTree->GetNextChild( mainId, childCookie );
  239. }
  240. }
  241. config.AppendGroup( grpConfig );
  242. }
  243. mainId = m_toolbarTree->GetNextChild( rootId, mainCookie );
  244. }
  245. return config;
  246. }
  247. void PANEL_TOOLBAR_CUSTOMIZATION::populateToolbarTree()
  248. {
  249. m_toolbarTree->DeleteAllItems();
  250. m_toolbarTree->SetImageList( m_actionImageList );
  251. const auto& it = m_toolbars.find( m_currentToolbar );
  252. if( it == m_toolbars.end() )
  253. {
  254. // Disable the controls and bail out - no toolbar here
  255. enableToolbarControls( false );
  256. return;
  257. }
  258. // Ensure the controls are enabled
  259. enableToolbarControls( true );
  260. TOOLBAR_CONFIGURATION toolbar = it->second;
  261. wxTreeItemId root = m_toolbarTree->AddRoot( "Toolbar" );
  262. for( auto& item : toolbar.GetToolbarItems() )
  263. {
  264. switch( item.m_Type )
  265. {
  266. case TOOLBAR_ITEM_TYPE::SEPARATOR:
  267. {
  268. // Add a separator
  269. TOOLBAR_TREE_ITEM_DATA* sepTreeItem = new TOOLBAR_TREE_ITEM_DATA( TOOLBAR_ITEM_TYPE::SEPARATOR );
  270. m_toolbarTree->AppendItem( root, "Separator", -1, -1, sepTreeItem );
  271. break;
  272. }
  273. case TOOLBAR_ITEM_TYPE::SPACER:
  274. {
  275. // Add a spacer
  276. TOOLBAR_TREE_ITEM_DATA* spacerTreeItem = new TOOLBAR_TREE_ITEM_DATA( TOOLBAR_ITEM_TYPE::SPACER );
  277. spacerTreeItem->SetSize( item.m_Size );
  278. m_toolbarTree->AppendItem( root, wxString::Format( "Spacer: %i", item.m_Size ), -1, -1,
  279. spacerTreeItem );
  280. break;
  281. }
  282. case TOOLBAR_ITEM_TYPE::CONTROL:
  283. {
  284. // Add a control
  285. TOOLBAR_TREE_ITEM_DATA* controlTreeItem = new TOOLBAR_TREE_ITEM_DATA( TOOLBAR_ITEM_TYPE::CONTROL );
  286. controlTreeItem->SetName( item.m_ControlName );
  287. m_toolbarTree->AppendItem( root, item.m_ControlName, -1, -1,
  288. controlTreeItem );
  289. break;
  290. }
  291. case TOOLBAR_ITEM_TYPE::TOOL:
  292. {
  293. // Add a tool
  294. auto toolMap = m_availableTools.find( item.m_ActionName );
  295. if( toolMap == m_availableTools.end() )
  296. {
  297. wxASSERT_MSG( false, wxString::Format( "Unable to find tool %s", item.m_ActionName ) );
  298. continue;
  299. }
  300. TOOLBAR_TREE_ITEM_DATA* toolTreeItem = new TOOLBAR_TREE_ITEM_DATA( TOOLBAR_ITEM_TYPE::TOOL );
  301. toolTreeItem->SetAction( toolMap->second );
  302. int imgIdx = -1;
  303. auto imgMap = m_actionImageListMap.find( item.m_ActionName );
  304. if( imgMap != m_actionImageListMap.end() )
  305. imgIdx = imgMap->second;
  306. m_toolbarTree->AppendItem( root, toolMap->second->GetFriendlyName(),
  307. imgIdx, -1, toolTreeItem );
  308. break;
  309. }
  310. case TOOLBAR_ITEM_TYPE::TB_GROUP:
  311. {
  312. // Add a group of items to the toolbar
  313. TOOLBAR_TREE_ITEM_DATA* groupTreeItem = new TOOLBAR_TREE_ITEM_DATA( TOOLBAR_ITEM_TYPE::TB_GROUP );
  314. groupTreeItem->SetName( item.m_GroupName );
  315. wxTreeItemId groupId = m_toolbarTree->AppendItem( root, item.m_GroupName, -1, -1,
  316. groupTreeItem );
  317. // Add the elements below the group
  318. for( auto& groupItem : item.m_GroupItems )
  319. {
  320. auto toolMap = m_availableTools.find( groupItem.m_ActionName );
  321. if( toolMap == m_availableTools.end() )
  322. {
  323. wxASSERT_MSG( false, wxString::Format( "Unable to find group tool %s", groupItem.m_ActionName ) );
  324. continue;
  325. }
  326. TOOLBAR_TREE_ITEM_DATA* toolTreeItem = new TOOLBAR_TREE_ITEM_DATA( TOOLBAR_ITEM_TYPE::TOOL );
  327. toolTreeItem->SetAction( toolMap->second );
  328. int imgIdx = -1;
  329. auto imgMap = m_actionImageListMap.find( groupItem.m_ActionName );
  330. if( imgMap != m_actionImageListMap.end() )
  331. imgIdx = imgMap->second;
  332. m_toolbarTree->AppendItem( groupId, toolMap->second->GetFriendlyName(),
  333. imgIdx, -1, toolTreeItem );
  334. }
  335. break;
  336. }
  337. }
  338. }
  339. m_toolbarTree->ExpandAll();
  340. wxTreeItemIdValue temp;
  341. wxTreeItemId firstItem = m_toolbarTree->GetFirstChild( root, temp );
  342. if( firstItem.IsOk() )
  343. {
  344. m_toolbarTree->SelectItem( firstItem );
  345. m_toolbarTree->EnsureVisible( firstItem );
  346. }
  347. }
  348. void PANEL_TOOLBAR_CUSTOMIZATION::populateActions()
  349. {
  350. // Clear all existing information for the actions
  351. delete m_actionImageList;
  352. m_actionImageListMap.clear();
  353. m_actionImageBundleVector.clear();
  354. // Prep the control
  355. m_actionsList->DeleteAllItems();
  356. m_actionsList->DeleteAllColumns();
  357. m_actionsList->InsertColumn( 0, "", wxLIST_FORMAT_LEFT, wxLIST_AUTOSIZE );
  358. // Prepare the image list (taken from project_tree.cpp)
  359. int logicSize = 24 * GetDPIScaleFactor() / GetContentScaleFactor(); // Cross-platform way
  360. int physSize = ToPhys( logicSize ); // aka *GetContentScaleFactor()
  361. if( physSize >= 64 )
  362. physSize = 64;
  363. else if( physSize >= 48 )
  364. physSize = 48;
  365. else if( physSize >= 32 )
  366. physSize = 32;
  367. else
  368. physSize = 24;
  369. logicSize = std::min( logicSize, physSize );
  370. int bmpsf = std::max( 1, physSize / logicSize );
  371. logicSize = physSize / bmpsf;
  372. auto toBitmap = [&]( BITMAPS aBmps )
  373. {
  374. wxBitmap bmp = KiBitmap( aBmps, physSize );
  375. bmp.SetScaleFactor( bmpsf );
  376. wxASSERT(bmp.IsOk());
  377. return bmp;
  378. };
  379. m_actionImageList = new wxImageList( logicSize, logicSize, true,
  380. static_cast<int>( m_availableTools.size() ) );
  381. // Populate the various image lists for the action icons, and the actual control
  382. int itemIdx = 0;
  383. for( auto [k, tool] : m_availableTools )
  384. {
  385. if( tool->CheckToolbarState( TOOLBAR_STATE::HIDDEN ) )
  386. continue;
  387. wxListItem item;
  388. item.SetText( tool->GetFriendlyName() );
  389. item.SetData( static_cast<void*>( tool ) );
  390. item.SetId( itemIdx++ );
  391. if( tool->GetIcon() != BITMAPS::INVALID_BITMAP )
  392. {
  393. int idx = m_actionImageList->Add( toBitmap( tool->GetIcon() ) );
  394. // If the image list throws away the image, then we shouldn't show the image anywhere.
  395. // TODO: Make sure all images have all possible sizes so the image list doesn't get grumpy.
  396. if( idx != -1 )
  397. {
  398. m_actionImageBundleVector.push_back( KiBitmapBundle( tool->GetIcon() ) );
  399. m_actionImageListMap.emplace( tool->GetName(), idx );
  400. item.SetImage( idx );
  401. }
  402. }
  403. m_actionsList->InsertItem( item );
  404. }
  405. m_actionsList->SetSmallImages( m_actionImageBundleVector );
  406. // This must be done after adding everything to the list to make the columns wide enough
  407. m_actionsList->SetColumnWidth( 0, wxLIST_AUTOSIZE );
  408. }
  409. void PANEL_TOOLBAR_CUSTOMIZATION::onGroupPress( wxCommandEvent& aEvent )
  410. {
  411. TOOLBAR_TREE_ITEM_DATA* treeItem = new TOOLBAR_TREE_ITEM_DATA( TOOLBAR_ITEM_TYPE::TB_GROUP,
  412. _( "Group" ) );
  413. wxTreeItemId newItem;
  414. wxTreeItemId selItem = m_toolbarTree->GetSelection();
  415. if( selItem.IsOk() )
  416. {
  417. // Can't add a group onto a group
  418. wxTreeItemId parent = m_toolbarTree->GetItemParent( selItem );
  419. if( parent.IsOk() )
  420. {
  421. wxTreeItemId secondParent = m_toolbarTree->GetItemParent( parent );
  422. if( secondParent.IsOk() )
  423. {
  424. delete treeItem;
  425. return;
  426. }
  427. }
  428. newItem = m_toolbarTree->InsertItem( m_toolbarTree->GetRootItem(), selItem, treeItem->GetName(),
  429. -1, -1, treeItem );
  430. }
  431. else
  432. {
  433. newItem = m_toolbarTree->AppendItem( m_toolbarTree->GetRootItem(), treeItem->GetName(), -1, -1,
  434. treeItem );
  435. }
  436. if( newItem.IsOk() )
  437. {
  438. m_toolbarTree->SelectItem( newItem );
  439. m_toolbarTree->EnsureVisible( newItem );
  440. }
  441. }
  442. void PANEL_TOOLBAR_CUSTOMIZATION::onSpacerPress( wxCommandEvent& aEvent )
  443. {
  444. TOOLBAR_TREE_ITEM_DATA* treeItem = new TOOLBAR_TREE_ITEM_DATA( TOOLBAR_ITEM_TYPE::SPACER, 5 );
  445. wxString label = wxString::Format( "Spacer: %i", treeItem->GetSize() );
  446. wxTreeItemId newItem;
  447. wxTreeItemId selItem = m_toolbarTree->GetSelection();
  448. if( selItem.IsOk() )
  449. {
  450. // Insert after the current selection at the same level
  451. wxTreeItemId parent = m_toolbarTree->GetItemParent( selItem );
  452. // Can't insert a spacer in a group yet
  453. if( parent.IsOk() )
  454. {
  455. wxTreeItemId secondParent = m_toolbarTree->GetItemParent( parent );
  456. if( secondParent.IsOk() )
  457. {
  458. delete treeItem;
  459. return;
  460. }
  461. }
  462. newItem = m_toolbarTree->InsertItem( parent, selItem, label, -1, -1, treeItem );
  463. }
  464. else
  465. newItem = m_toolbarTree->AppendItem( m_toolbarTree->GetRootItem(), label, -1, -1, treeItem );
  466. if( newItem.IsOk() )
  467. {
  468. m_toolbarTree->SelectItem( newItem );
  469. m_toolbarTree->EnsureVisible( newItem );
  470. }
  471. }
  472. void PANEL_TOOLBAR_CUSTOMIZATION::onSeparatorPress( wxCommandEvent& aEvent )
  473. {
  474. TOOLBAR_TREE_ITEM_DATA* treeItem = new TOOLBAR_TREE_ITEM_DATA( TOOLBAR_ITEM_TYPE::SEPARATOR );
  475. wxTreeItemId newItem;
  476. wxTreeItemId selItem = m_toolbarTree->GetSelection();
  477. if( selItem.IsOk() )
  478. {
  479. // Insert after the current selection at the same level
  480. wxTreeItemId parent = m_toolbarTree->GetItemParent( selItem );
  481. // Can't insert a separator in a group yet
  482. if( parent.IsOk() )
  483. {
  484. wxTreeItemId secondParent = m_toolbarTree->GetItemParent( parent );
  485. if( secondParent.IsOk() )
  486. {
  487. delete treeItem;
  488. return;
  489. }
  490. }
  491. newItem = m_toolbarTree->InsertItem( parent, selItem, "Separator", -1, -1, treeItem );
  492. }
  493. else
  494. {
  495. newItem = m_toolbarTree->AppendItem( m_toolbarTree->GetRootItem(), "Separator", -1, -1,
  496. treeItem );
  497. }
  498. if( newItem.IsOk() )
  499. {
  500. m_toolbarTree->SelectItem( newItem );
  501. m_toolbarTree->EnsureVisible( newItem );
  502. }
  503. }
  504. void PANEL_TOOLBAR_CUSTOMIZATION::onCustomizeTbCb( wxCommandEvent& event )
  505. {
  506. enableCustomControls( event.IsChecked() );
  507. }
  508. void PANEL_TOOLBAR_CUSTOMIZATION::enableCustomControls( bool enable )
  509. {
  510. m_tbChoice->Enable( enable );
  511. enableToolbarControls( enable );
  512. }
  513. void PANEL_TOOLBAR_CUSTOMIZATION::enableToolbarControls( bool enable )
  514. {
  515. m_toolbarTree->Enable( enable );
  516. m_btnAddTool->Enable( enable );
  517. m_btnToolDelete->Enable( enable );
  518. // TODO (ISM): Enable moving up/down
  519. //m_btnToolMoveDown->Enable( enable );
  520. //m_btnToolMoveUp->Enable( enable );
  521. m_actionsList->Enable( enable );
  522. m_insertButton->Enable( enable );
  523. }
  524. void PANEL_TOOLBAR_CUSTOMIZATION::onToolDelete( wxCommandEvent& event )
  525. {
  526. wxTreeItemId item = m_toolbarTree->GetSelection();
  527. if( !item.IsOk() )
  528. return;
  529. // The tree control defaults to nothing selected if you delete the item
  530. // at the last place, so we have to manually get the itme immediately before
  531. // the one we will delete, and then select it if nothing is selected.
  532. wxTreeItemId prev = m_toolbarTree->GetPrevSibling( item );
  533. m_toolbarTree->Delete( item );
  534. item = m_toolbarTree->GetSelection();
  535. if( !item.IsOk() && prev.IsOk() )
  536. {
  537. m_toolbarTree->SelectItem( prev );
  538. m_toolbarTree->EnsureVisible( prev );
  539. }
  540. }
  541. void PANEL_TOOLBAR_CUSTOMIZATION::onToolMoveUp( wxCommandEvent& event )
  542. {
  543. }
  544. void PANEL_TOOLBAR_CUSTOMIZATION::onToolMoveDown( wxCommandEvent& event )
  545. {
  546. }
  547. void PANEL_TOOLBAR_CUSTOMIZATION::onBtnAddAction( wxCommandEvent& event )
  548. {
  549. // Get the selected item
  550. long actionIdx = m_actionsList->GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
  551. // Nothing is selected, bail out
  552. if( actionIdx < 0 )
  553. return;
  554. // This is needed because GetItemData returns a wxUIntPtr, which is actually size_t...
  555. void* v = ( void* ) m_actionsList->GetItemData( actionIdx );
  556. TOOL_ACTION* action = static_cast<TOOL_ACTION*>( v );
  557. // Build the item to add
  558. TOOLBAR_TREE_ITEM_DATA* toolTreeItem = new TOOLBAR_TREE_ITEM_DATA( TOOLBAR_ITEM_TYPE::TOOL );
  559. toolTreeItem->SetAction( action );
  560. int imgIdx = -1;
  561. auto imgMap = m_actionImageListMap.find( action->GetName() );
  562. if( imgMap != m_actionImageListMap.end() )
  563. imgIdx = imgMap->second;
  564. // Actually add the item
  565. wxString label = action->GetFriendlyName();
  566. wxTreeItemId selItem = m_toolbarTree->GetSelection();
  567. wxTreeItemId newItem;
  568. if( selItem.IsOk() )
  569. {
  570. TOOLBAR_TREE_ITEM_DATA* data =
  571. dynamic_cast<TOOLBAR_TREE_ITEM_DATA*>( m_toolbarTree->GetItemData( selItem ) );
  572. if( data && data->GetType() == TOOLBAR_ITEM_TYPE::TB_GROUP )
  573. {
  574. // Insert into the end of the group
  575. newItem = m_toolbarTree->AppendItem( selItem, label, imgIdx, -1, toolTreeItem );
  576. }
  577. else
  578. {
  579. // Insert after the current selection at the same level
  580. wxTreeItemId parent = m_toolbarTree->GetItemParent( selItem );
  581. newItem = m_toolbarTree->InsertItem( parent, selItem, label, imgIdx, -1, toolTreeItem );
  582. }
  583. }
  584. else
  585. {
  586. // Insert at the root level if there is no selection
  587. newItem = m_toolbarTree->AppendItem( m_toolbarTree->GetRootItem(), label, imgIdx, -1,
  588. toolTreeItem );
  589. }
  590. if( newItem.IsOk() )
  591. {
  592. m_toolbarTree->SelectItem( newItem );
  593. m_toolbarTree->EnsureVisible( newItem );
  594. // Move the action to the next available one, to be nice
  595. if( ++actionIdx < m_actionsList->GetItemCount() )
  596. m_actionsList->SetItemState( actionIdx, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
  597. }
  598. }
  599. void PANEL_TOOLBAR_CUSTOMIZATION::onTreeBeginLabelEdit( wxTreeEvent& event )
  600. {
  601. wxTreeItemId id = event.GetItem();
  602. if( id.IsOk() )
  603. {
  604. wxTreeItemData* treeData = m_toolbarTree->GetItemData( id );
  605. TOOLBAR_TREE_ITEM_DATA* tbData = dynamic_cast<TOOLBAR_TREE_ITEM_DATA*>( treeData );
  606. switch( tbData->GetType() )
  607. {
  608. case TOOLBAR_ITEM_TYPE::TOOL:
  609. case TOOLBAR_ITEM_TYPE::CONTROL:
  610. case TOOLBAR_ITEM_TYPE::SEPARATOR:
  611. // Don't let these be edited
  612. event.Veto();
  613. break;
  614. case TOOLBAR_ITEM_TYPE::TB_GROUP:
  615. case TOOLBAR_ITEM_TYPE::SPACER:
  616. // Do nothing here
  617. break;
  618. }
  619. }
  620. }
  621. void PANEL_TOOLBAR_CUSTOMIZATION::onTreeEndLabelEdit( wxTreeEvent& event )
  622. {
  623. }
  624. void PANEL_TOOLBAR_CUSTOMIZATION::onTbChoiceSelect( wxCommandEvent& event )
  625. {
  626. // Store the current toolbar
  627. auto currentTb = parseToolbarTree();
  628. if( currentTb.has_value() )
  629. m_toolbars[m_currentToolbar] = currentTb.value();
  630. // Populate the new one
  631. auto newTb = magic_enum::enum_cast<TOOLBAR_LOC>( event.GetInt() );
  632. if( newTb.has_value() )
  633. {
  634. m_currentToolbar = newTb.value();
  635. populateToolbarTree();
  636. }
  637. }