KiCad PCB EDA Suite
Loading...
Searching...
No Matches
panel_toolbar_customization.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 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, see <https://www.gnu.org/licenses/>.
18 */
19
21
22#include <bitmaps.h>
24#include <tool/action_manager.h>
25#include <tool/actions.h>
30
31#include <magic_enum.hpp>
32#include <wx/listctrl.h>
33#include <wx/menu.h>
34#include <widgets/ui_common.h>
35
36#include <algorithm>
37#include <set>
38
39// Simple IDs for the split button menu
40enum
41{
42 ID_SEPARATOR_MENU = ( wxID_HIGHEST + 5 ),
45};
46
47
48static std::map<TOOLBAR_LOC, wxString> s_toolbarNameMap = {
49 { TOOLBAR_LOC::LEFT, _( "Left" ) },
50 { TOOLBAR_LOC::RIGHT, _( "Right" ) },
51 { TOOLBAR_LOC::TOP_MAIN, _( "Top main" ) },
52 { TOOLBAR_LOC::TOP_AUX, _( "Top auxiliary" ) }
53};
54
55
56class TOOLBAR_TREE_ITEM_DATA : public wxTreeItemData
57{
58public:
60 m_type( TOOLBAR_ITEM_TYPE::SEPARATOR ), // Init m_type to something
61 m_action( nullptr ),
62 m_control( nullptr ),
63 m_size( 0 )
64 { }
65
67 m_type( aType ),
68 m_action( nullptr ),
69 m_control( nullptr ),
70 m_size( 0 )
71 { }
72
74 m_type( aType ),
75 m_action( nullptr ),
76 m_control( nullptr ),
77 m_size( aSize )
78 {
79 wxASSERT( aType == TOOLBAR_ITEM_TYPE::SPACER );
80 }
81
82 TOOLBAR_TREE_ITEM_DATA( TOOLBAR_ITEM_TYPE aType, wxString aName ) :
83 m_type( aType ),
84 m_action( nullptr ),
85 m_control( nullptr ),
86 m_size( 0 ),
87 m_name( aName )
88 {
89 wxASSERT( aType == TOOLBAR_ITEM_TYPE::CONTROL
90 || aType == TOOLBAR_ITEM_TYPE::TB_GROUP );
91 }
92
93 void SetAction( TOOL_ACTION* aAction ) { m_action = aAction; }
95 {
96 wxASSERT( m_type == TOOLBAR_ITEM_TYPE::TOOL );
97 return m_action;
98 }
99
100 void SetControl( ACTION_TOOLBAR_CONTROL* aControl ) { m_control = aControl; }
102 {
103 wxASSERT( m_type == TOOLBAR_ITEM_TYPE::CONTROL );
104 return m_control;
105 }
106
107 void SetName( const wxString& aName ) { m_name = aName; }
108 const wxString& GetName() const { return m_name; }
109
110 void SetSize( int aSize ) { m_size = aSize; }
111 int GetSize() const { return m_size; }
112
113 TOOLBAR_ITEM_TYPE GetType() const { return m_type; }
114
115private:
116 // Item type
118
119 // Tool properties (can be one or the other, but never both)
122
123 // Spacer properties
125
126 // Group/control properties
127 wxString m_name;
128};
129
130
132 TOOLBAR_SETTINGS* aTbSettings, FRAME_T aActionContext,
133 const std::vector<TOOL_ACTION*>& aTools,
134 const std::vector<ACTION_TOOLBAR_CONTROL*>& aControls ) :
136 m_appSettings( aCfg ),
137 m_appTbSettings( aTbSettings ),
139 m_actionContext( aActionContext )
140{
141 // Copy the tools and controls into the internal maps
142 for( auto& tool : aTools )
143 m_availableTools.emplace( tool->GetName(), tool );
144
145 for( auto& control : aControls )
146 m_availableControls.emplace( control->GetName(), control );
147
148 // Configure the Ui elements
153
154 m_insertButton->SetLabel( _( "Insert Separator" ) );
155 //m_insertButton->SetWidthPadding( 4 );
156
157 // Populate the browse library options
158 wxMenu* insertMenu = m_insertButton->GetSplitButtonMenu();
159
160 insertMenu->Append( ID_SPACER_MENU, _( "Insert Spacer" ) );
161 insertMenu->Append( ID_GROUP_MENU, _( "Insert Group" ) );
162
163 insertMenu->Bind( wxEVT_COMMAND_MENU_SELECTED, &PANEL_TOOLBAR_CUSTOMIZATION::onSpacerPress,
164 this, ID_SPACER_MENU );
165 insertMenu->Bind( wxEVT_COMMAND_MENU_SELECTED, &PANEL_TOOLBAR_CUSTOMIZATION::onGroupPress,
166 this, ID_GROUP_MENU );
167
168 // This is the button only press for the browse button instead of the menu
170
171 m_actionFilter->ShowSearchButton( false );
172 m_actionFilter->ShowCancelButton( true );
173 m_actionFilter->SetDescriptiveText( _( "Filter actions" ) );
174
175#ifdef __WXGTK__
176 m_actionFilter->SetMinSize( wxSize( -1, GetTextExtent( wxT( "qb" ) ).y + 10 ) );
177#endif
178
180 m_actionFilter->Bind( wxEVT_SEARCHCTRL_CANCEL_BTN,
183 m_actionsList->Bind( wxEVT_LEAVE_WINDOW, &PANEL_TOOLBAR_CUSTOMIZATION::onActionListMouseMove, this );
184
185 m_toolbarTree->Bind( wxEVT_TREE_ITEM_ACTIVATED, &PANEL_TOOLBAR_CUSTOMIZATION::onTreeItemActivated, this );
186
187 // TODO (ISM): Enable draging
188 m_btnToolMoveDown->Enable( false );
189 m_btnToolMoveUp->Enable( false );
190}
191
192
202
203
205{
206 const std::string& name = aAction.GetName();
207
208 auto hasPrefix = [&]( const char* aPrefix ) -> bool
209 {
210 return name.rfind( aPrefix, 0 ) == 0;
211 };
212
214 {
215 if( hasPrefix( "3DViewer." ) )
216 return true;
217
218 return name == ACTIONS::zoomRedraw.GetName() || name == ACTIONS::zoomInCenter.GetName()
219 || name == ACTIONS::zoomOutCenter.GetName() || name == ACTIONS::zoomFitScreen.GetName();
220 }
221
222 if( hasPrefix( "common." ) )
223 return true;
224
225 const std::string framePrefix = ACTION_MANAGER::FrameNamespacePrefix( m_actionContext );
226
227 return !framePrefix.empty() && hasPrefix( framePrefix.c_str() );
228}
229
230
232{
233 m_toolbars.clear();
234 m_toolbarChoices.clear();
235
236 // Go over every toolbar and initialize things
237 for( auto& tb : magic_enum::enum_values<TOOLBAR_LOC>() )
238 {
239 // Create a shadow toolbar
240 auto tbConfig = m_appTbSettings->DefaultToolbarConfig( tb );
241
242 if( !tbConfig.has_value() )
243 continue;
244
245 m_toolbars[tb] = tbConfig.value();
246 m_toolbarChoices.push_back( tb );
247 }
248
249 if( !m_toolbarChoices.empty() )
250 {
251 m_tbChoice->SetSelection( 0 );
253 }
254
256}
257
258
260{
261 wxArrayString tbChoices;
262
263 m_toolbars.clear();
264 m_toolbarChoices.clear();
265
266 // Go over every toolbar and initialize things
267 for( auto& tb : magic_enum::enum_values<TOOLBAR_LOC>() )
268 {
269 // Create a shadow toolbar
270 auto tbConfig = m_appTbSettings->GetToolbarConfig( tb );
271
272 if( !tbConfig.has_value() )
273 continue;
274
275 m_toolbars.emplace( tb, tbConfig.value() );
276 m_toolbarChoices.push_back( tb );
277
278 // Setup the UI name
279 const auto& tbName = s_toolbarNameMap.find( tb );
280
281 wxASSERT_MSG( tbName != s_toolbarNameMap.end(),
282 wxString::Format( "Unknown toolbar: %s", magic_enum::enum_name( tb ) ) );
283
284 tbChoices.Add( tbName->second );
285 }
286
287 m_tbChoice->Set( tbChoices );
288
289 // Always populate the actions before the toolbars, that way the icons are available
291
292 if( !m_toolbarChoices.empty() )
293 {
294 m_tbChoice->SetSelection( 0 );
296 }
297
299
300 // Sync the enable/disable control
301 enableCustomControls( m_appSettings->m_CustomToolbars );
302 m_customToolbars->SetValue( m_appSettings->m_CustomToolbars );
303
304 return true;
305}
306
307
309{
310 m_appSettings->m_CustomToolbars = m_customToolbars->GetValue();
311
312 // Store the current toolbar
313 std::optional<TOOLBAR_CONFIGURATION> currentTb = parseToolbarTree();
314
315 if( currentTb.has_value() )
316 m_toolbars[m_currentToolbar] = currentTb.value();
317
318 std::set<std::string> seenControls;
319
320 for( auto& [loc, config] : m_toolbars )
321 {
322 auto& items = config.m_toolbarItems;
323
324 items.erase( std::remove_if( items.begin(), items.end(),
325 [&]( const TOOLBAR_ITEM& item )
326 {
327 if( item.m_Type != TOOLBAR_ITEM_TYPE::CONTROL )
328 return false;
329
330 return !seenControls.insert( item.m_ControlName ).second;
331 } ),
332 items.end() );
333 }
334
335 // Write the shadow toolbars with changes back to the app toolbar settings
336 for( const auto& [loc, config] : m_toolbars )
337 m_appTbSettings->SetStoredToolbarConfig( loc, config );
338
339 return true;
340}
341
342std::optional<TOOLBAR_CONFIGURATION> PANEL_TOOLBAR_CUSTOMIZATION::parseToolbarTree()
343{
345
346 wxTreeItemId mainId;
347 wxTreeItemId rootId = m_toolbarTree->GetRootItem();
348 wxTreeItemIdValue mainCookie;
349
350 if( !rootId.IsOk() )
351 return std::nullopt;
352
353 mainId = m_toolbarTree->GetFirstChild( rootId, mainCookie );
354
355 while( mainId.IsOk() )
356 {
357 wxTreeItemData* treeData = m_toolbarTree->GetItemData( mainId );
358
359 TOOLBAR_TREE_ITEM_DATA* tbData = dynamic_cast<TOOLBAR_TREE_ITEM_DATA*>( treeData );
360
361 wxCHECK2( tbData, continue );
362
363 switch( tbData->GetType() )
364 {
366 config.AppendSpacer( tbData->GetSize() );
367 break;
368
370 config.AppendSeparator();
371 break;
372
374 config.AppendControl( tbData->GetControl()->GetName() );
375 break;
376
378 config.AppendAction( *( tbData->GetAction() ) );
379 break;
380
382 TOOLBAR_GROUP_CONFIG grpConfig( tbData->GetName() );
383
384 if( m_toolbarTree->ItemHasChildren( mainId ) )
385 {
386 wxTreeItemIdValue childCookie;
387 wxTreeItemId childId = m_toolbarTree->GetFirstChild( mainId, childCookie );
388
389 while( childId.IsOk() )
390 {
391 wxTreeItemData* childTreeData = m_toolbarTree->GetItemData( childId );
392
393 TOOLBAR_TREE_ITEM_DATA* childTbData = dynamic_cast<TOOLBAR_TREE_ITEM_DATA*>( childTreeData );
394
395 wxCHECK2( childTbData, break );
396
397 switch( childTbData->GetType() )
398 {
403 wxASSERT_MSG( false, "Invalid entry in a group" );
404 break;
405
407 grpConfig.AddAction( *( childTbData->GetAction() ) );
408 break;
409 }
410
411 childId = m_toolbarTree->GetNextChild( mainId, childCookie );
412 }
413 }
414
415 if( !grpConfig.GetGroupItems().empty() )
416 config.AppendGroup( grpConfig );
417 }
418
419 mainId = m_toolbarTree->GetNextChild( rootId, mainCookie );
420 }
421
422 return config;
423}
424
425
427{
428 m_toolbarTree->DeleteAllItems();
430
431 const auto& it = m_toolbars.find( m_currentToolbar );
432
433 if( it == m_toolbars.end() )
434 {
435 // Disable the controls and bail out - no toolbar here
436 enableToolbarControls( false );
437 return;
438 }
439
440 // Ensure the controls are enabled
441 enableToolbarControls( true );
442
443 TOOLBAR_CONFIGURATION toolbar = it->second;
444
445 wxTreeItemId root = m_toolbarTree->AddRoot( "Toolbar" );
446
447 for( const TOOLBAR_ITEM& item : toolbar.GetToolbarItems() )
448 {
449 switch( item.m_Type )
450 {
452 {
453 // Add a separator
455 m_toolbarTree->AppendItem( root, _( "Separator" ), -1, -1, sepTreeItem );
456 break;
457 }
458
460 {
461 // Add a spacer
463 spacerTreeItem->SetSize( item.m_Size );
464 m_toolbarTree->AppendItem( root, wxString::Format( _( "Spacer: %i" ), item.m_Size ), -1, -1,
465 spacerTreeItem );
466 break;
467 }
468
470 {
471 auto controlIter = m_availableControls.find( item.m_ControlName );
472
473 if( controlIter == m_availableControls.end() )
474 {
475 wxASSERT_MSG( false, wxString::Format( "Unable to find control %s", item.m_ControlName ) );
476 continue;
477 }
478
479 // Add a control
481 controlTreeItem->SetControl( controlIter->second );
482 m_toolbarTree->AppendItem( root, controlIter->second->GetUiName(), -1, -1, controlTreeItem );
483 break;
484 }
485
487 {
488 // Add a tool
489 auto toolIter = m_availableTools.find( item.m_ActionName );
490
491 if( toolIter == m_availableTools.end() )
492 {
493 wxASSERT_MSG( false, wxString::Format( "Unable to find tool %s", item.m_ActionName ) );
494 continue;
495 }
496
497 if( !isActionSupported( *toolIter->second ) )
498 continue;
499
501 toolTreeItem->SetAction( toolIter->second );
502
503 int imgIdx = -1;
504 auto imgMap = m_actionImageListMap.find( item.m_ActionName );
505
506 if( imgMap != m_actionImageListMap.end() )
507 imgIdx = imgMap->second;
508
509 m_toolbarTree->AppendItem( root, toolIter->second->GetFriendlyName(), imgIdx, -1, toolTreeItem );
510 break;
511 }
512
514 {
515 // Add a group of items to the toolbar
517 groupTreeItem->SetName( item.m_GroupName );
518
519 wxTreeItemId groupId = m_toolbarTree->AppendItem( root, item.m_GroupName, -1, -1, groupTreeItem );
520 bool haveVisibleGroupItems = false;
521
522 // Add the elements below the group
523 for( const TOOLBAR_ITEM& groupItem : item.m_GroupItems )
524 {
525 auto toolMap = m_availableTools.find( groupItem.m_ActionName );
526
527 if( toolMap == m_availableTools.end() )
528 {
529 wxASSERT_MSG( false, wxString::Format( "Unable to find group tool %s", groupItem.m_ActionName ) );
530 continue;
531 }
532
533 if( !isActionSupported( *toolMap->second ) )
534 continue;
535
537 toolTreeItem->SetAction( toolMap->second );
538
539 int imgIdx = -1;
540 auto imgMap = m_actionImageListMap.find( groupItem.m_ActionName );
541
542 if( imgMap != m_actionImageListMap.end() )
543 imgIdx = imgMap->second;
544
545 m_toolbarTree->AppendItem( groupId, toolMap->second->GetFriendlyName(), imgIdx, -1, toolTreeItem );
546
547 haveVisibleGroupItems = true;
548 }
549
550 if( !haveVisibleGroupItems )
551 m_toolbarTree->Delete( groupId );
552
553 break;
554 }
555 }
556 }
557
558 m_toolbarTree->ExpandAll();
559
560 wxTreeItemIdValue temp;
561 wxTreeItemId firstItem = m_toolbarTree->GetFirstChild( root, temp );
562
563 if( firstItem.IsOk() )
564 {
565 m_toolbarTree->SelectItem( firstItem );
566 m_toolbarTree->EnsureVisible( firstItem );
567 }
568}
569
570
572{
573 const int c_defSize = 24; // Default icon size for toolbar actions
574
575 // Clear all existing information for the actions
576 m_actionImageListMap.clear();
578 m_actionEntries.clear();
579
580 // Prep the control
581 m_actionsList->DeleteAllItems();
582 m_actionsList->DeleteAllColumns();
583 m_actionsList->InsertColumn( 0, "", wxLIST_FORMAT_LEFT, wxLIST_AUTOSIZE );
584
585 for( const auto& [k, tool] : m_availableTools )
586 {
587 if( !isActionSupported( *tool ) )
588 continue;
589
590 if( tool->CheckToolbarState( TOOLBAR_STATE::HIDDEN ) )
591 continue;
592
593 ACTION_LIST_ENTRY entry;
594 entry.label = tool->GetFriendlyName();
595 entry.tooltip = tool->GetDescription(); // falls back to tooltip if no description provided
596 entry.action = tool;
597 entry.search_text = entry.label.Upper() + wxS( " " ) + entry.tooltip.Upper();
598
599 if( tool->GetIcon() != BITMAPS::INVALID_BITMAP )
600 {
601 int imgIdx = m_actionImageBundleVector.size();
602 m_actionImageBundleVector.push_back( KiBitmapBundleDef( tool->GetIcon(), c_defSize ) );
603 m_actionImageListMap.emplace( tool->GetName(), imgIdx );
604 entry.image_index = imgIdx;
605 }
606
607 m_actionEntries.push_back( std::move( entry ) );
608 }
609
610 for( const auto& [k, control] : m_availableControls )
611 {
612 ACTION_LIST_ENTRY entry;
613 entry.label = control->GetUiName();
614 entry.tooltip = control->GetDescription();
615 entry.control = control;
616 entry.search_text = entry.label.Upper() + wxS( " " ) + control->GetDescription().Upper();
617 m_actionEntries.push_back( std::move( entry ) );
618 }
619
620 std::sort( m_actionEntries.begin(), m_actionEntries.end(),
621 []( const ACTION_LIST_ENTRY& a, const ACTION_LIST_ENTRY& b )
622 {
623 return a.label.CmpNoCase( b.label ) < 0;
624 } );
625
626 m_actionsList->SetSmallImages( m_actionImageBundleVector );
628}
629
630
632 const wxString& aFilter ) const
633{
634 if( aFilter.IsEmpty() )
635 return true;
636
637 return aEntry.search_text.Contains( aFilter.Upper() );
638}
639
640
642{
643 wxFont listFont = KIUI::GetInfoFont( this );
644 wxString filter = m_actionFilter->GetValue();
645
647 m_actionsList->UnsetToolTip();
648
649 m_actionsList->DeleteAllItems();
650
651 for( size_t idx = 0; idx < m_actionEntries.size(); ++idx )
652 {
653 const ACTION_LIST_ENTRY& entry = m_actionEntries[idx];
654
655 if( !actionMatchesFilter( entry, filter ) )
656 continue;
657
658 wxListItem item;
659 item.SetId( m_actionsList->GetItemCount() );
660 item.SetText( entry.label );
661 item.SetFont( listFont );
662 item.SetData( static_cast<long>( idx ) );
663 item.SetImage( entry.image_index );
664
665 m_actionsList->InsertItem( item );
666 }
667
668 if( m_actionsList->GetItemCount() > 0 )
669 m_actionsList->SetItemState( 0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
670
671 m_actionsList->SetColumnWidth( 0, wxLIST_AUTOSIZE );
672}
673
674
675void PANEL_TOOLBAR_CUSTOMIZATION::onGroupPress( wxCommandEvent& aEvent )
676{
678
679 wxTreeItemId newItem;
680 wxTreeItemId selItem = m_toolbarTree->GetSelection();
681
682 if( selItem.IsOk() )
683 {
684 // Can't add a group onto a group
685 wxTreeItemId parent = m_toolbarTree->GetItemParent( selItem );
686
687 if( parent.IsOk() )
688 {
689 wxTreeItemId secondParent = m_toolbarTree->GetItemParent( parent );
690
691 if( secondParent.IsOk() )
692 {
693 delete treeItem;
694 return;
695 }
696 }
697
698 newItem = m_toolbarTree->InsertItem( m_toolbarTree->GetRootItem(), selItem, treeItem->GetName(),
699 -1, -1, treeItem );
700 }
701 else
702 {
703 newItem = m_toolbarTree->AppendItem( m_toolbarTree->GetRootItem(), treeItem->GetName(), -1, -1,
704 treeItem );
705 }
706
707 if( newItem.IsOk() )
708 {
709 m_toolbarTree->SelectItem( newItem );
710 m_toolbarTree->EnsureVisible( newItem );
711 }
712}
713
714
715void PANEL_TOOLBAR_CUSTOMIZATION::onSpacerPress( wxCommandEvent& aEvent )
716{
718
719 wxString label = wxString::Format( "Spacer: %i", treeItem->GetSize() );
720
721 wxTreeItemId newItem;
722 wxTreeItemId selItem = m_toolbarTree->GetSelection();
723
724 if( selItem.IsOk() )
725 {
726 // Insert after the current selection at the same level
727 wxTreeItemId parent = m_toolbarTree->GetItemParent( selItem );
728
729 // Can't insert a spacer in a group yet
730 if( parent.IsOk() )
731 {
732 wxTreeItemId secondParent = m_toolbarTree->GetItemParent( parent );
733
734 if( secondParent.IsOk() )
735 {
736 delete treeItem;
737 return;
738 }
739 }
740
741 newItem = m_toolbarTree->InsertItem( parent, selItem, label, -1, -1, treeItem );
742 }
743 else
744 {
745 newItem = m_toolbarTree->AppendItem( m_toolbarTree->GetRootItem(), label, -1, -1, treeItem );
746 }
747
748 if( newItem.IsOk() )
749 {
750 m_toolbarTree->SelectItem( newItem );
751 m_toolbarTree->EnsureVisible( newItem );
752 }
753}
754
755
757{
759
760 wxTreeItemId newItem;
761 wxTreeItemId selItem = m_toolbarTree->GetSelection();
762
763 if( selItem.IsOk() )
764 {
765 // Insert after the current selection at the same level
766 wxTreeItemId parent = m_toolbarTree->GetItemParent( selItem );
767
768 // Can't insert a separator in a group yet
769 if( parent.IsOk() )
770 {
771 wxTreeItemId secondParent = m_toolbarTree->GetItemParent( parent );
772
773 if( secondParent.IsOk() )
774 {
775 delete treeItem;
776 return;
777 }
778 }
779
780 newItem = m_toolbarTree->InsertItem( parent, selItem, _( "Separator" ), -1, -1, treeItem );
781 }
782 else
783 {
784 newItem = m_toolbarTree->AppendItem( m_toolbarTree->GetRootItem(), _( "Separator" ), -1, -1, treeItem );
785 }
786
787 if( newItem.IsOk() )
788 {
789 m_toolbarTree->SelectItem( newItem );
790 m_toolbarTree->EnsureVisible( newItem );
791 }
792}
793
794
796{
797 enableCustomControls( event.IsChecked() );
798}
799
800
802{
803 m_tbChoice->Enable( enable );
804 enableToolbarControls( enable );
805}
806
807
809{
810 m_toolbarTree->Enable( enable );
811 m_btnAddTool->Enable( enable );
812 m_btnToolDelete->Enable( enable );
813
814 m_btnToolMoveDown->Enable( enable );
815 m_btnToolMoveUp->Enable( enable );
816 m_actionsList->Enable( enable );
817 m_actionFilter->Enable( enable );
818 m_insertButton->Enable( enable );
819}
820
821
822void PANEL_TOOLBAR_CUSTOMIZATION::onToolDelete( wxCommandEvent& event )
823{
824 wxTreeItemId item = m_toolbarTree->GetSelection();
825
826 if( !item.IsOk() )
827 return;
828
829 // The tree control defaults to nothing selected if you delete the item
830 // at the last place, so we have to manually get the itme immediately before
831 // the one we will delete, and then select it if nothing is selected.
832 wxTreeItemId prev = m_toolbarTree->GetPrevSibling( item );
833
834 m_toolbarTree->Delete( item );
835
836 item = m_toolbarTree->GetSelection();
837
838 if( !item.IsOk() && prev.IsOk() )
839 {
840 m_toolbarTree->SelectItem( prev );
841 m_toolbarTree->EnsureVisible( prev );
842 }
843}
844
845
846void PANEL_TOOLBAR_CUSTOMIZATION::onToolMoveUp( wxCommandEvent& event )
847{
848 m_toolbarTree->MoveItemUp( m_toolbarTree->GetSelection() );
849}
850
851
853{
854 m_toolbarTree->MoveItemDown( m_toolbarTree->GetSelection() );
855}
856
857
859{
860 // Get the selected item
861 long actionIdx = m_actionsList->GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
862
863 // Nothing is selected, bail out
864 if( actionIdx < 0 )
865 return;
866
867 size_t entryIdx = m_actionsList->GetItemData( actionIdx );
868
869 if( entryIdx >= m_actionEntries.size() )
870 return;
871
872 const ACTION_LIST_ENTRY& entry = m_actionEntries[entryIdx];
873 TOOL_ACTION* action = entry.action;
874 ACTION_TOOLBAR_CONTROL* control = entry.control;
875
876 if( control )
877 {
878 removeControlFromOtherToolbars( control->GetName() );
879 removeControlFromCurrentTree( control->GetName() );
880 }
881
882 // Build the item to add
885 toolTreeItem->SetAction( action );
886 toolTreeItem->SetControl( control );
887
888 int imgIdx = -1;
889
890 if( action )
891 {
892 auto imgMap = m_actionImageListMap.find( action->GetName() );
893
894 if( imgMap != m_actionImageListMap.end() )
895 imgIdx = imgMap->second;
896 }
897
898 // Actually add the item
899 wxTreeItemId selItem = m_toolbarTree->GetSelection();
900 wxTreeItemId newItem;
901
902 if( selItem.IsOk() )
903 {
905 dynamic_cast<TOOLBAR_TREE_ITEM_DATA*>( m_toolbarTree->GetItemData( selItem ) );
906
907 if( data && data->GetType() == TOOLBAR_ITEM_TYPE::TB_GROUP )
908 {
909 // Insert into the end of the group
910 newItem = m_toolbarTree->AppendItem( selItem, entry.label, imgIdx, -1, toolTreeItem );
911 }
912 else
913 {
914 // Insert after the current selection at the same level
915 wxTreeItemId parent = m_toolbarTree->GetItemParent( selItem );
916 newItem = m_toolbarTree->InsertItem( parent, selItem, entry.label, imgIdx, -1, toolTreeItem );
917 }
918 }
919 else
920 {
921 // Insert at the root level if there is no selection
922 newItem = m_toolbarTree->AppendItem( m_toolbarTree->GetRootItem(), entry.label, imgIdx, -1, toolTreeItem );
923 }
924
925 if( newItem.IsOk() )
926 {
927 m_toolbarTree->SelectItem( newItem );
928 m_toolbarTree->EnsureVisible( newItem );
929
930 // Move the action to the next available one, to be nice
931 if( ++actionIdx < m_actionsList->GetItemCount() )
932 m_actionsList->SetItemState( actionIdx, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
933 }
934}
935
936
938{
940 aEvent.Skip();
941}
942
943
945{
946 aEvent.Skip();
947
948 if( aEvent.Leaving() )
949 {
951 m_actionsList->UnsetToolTip();
952 return;
953 }
954
955 int flags = 0;
956 long item = m_actionsList->HitTest( aEvent.GetPosition(), flags );
957
958 if( item >= 0 )
959 {
960 long entryIdx = static_cast<long>( m_actionsList->GetItemData( item ) );
961
962 if( entryIdx >= 0 && entryIdx < static_cast<long>( m_actionEntries.size() ) )
963 {
964 if( m_hoveredActionEntry != entryIdx )
965 {
966 m_hoveredActionEntry = entryIdx;
967
968 if( const wxString& tooltip = m_actionEntries[entryIdx].tooltip; tooltip.IsEmpty() )
969 m_actionsList->UnsetToolTip();
970 else
971 m_actionsList->SetToolTip( tooltip );
972 }
973
974 return;
975 }
976 }
977
978 if( m_hoveredActionEntry != -1 )
979 {
981 m_actionsList->UnsetToolTip();
982 }
983}
984
985
987{
988 wxTreeItemId id = event.GetItem();
989
990 if( id.IsOk() )
991 {
992 wxTreeItemData* treeData = m_toolbarTree->GetItemData( id );
993
994 if( TOOLBAR_TREE_ITEM_DATA* tbData = dynamic_cast<TOOLBAR_TREE_ITEM_DATA*>( treeData ) )
995 {
996 switch( tbData->GetType() )
997 {
1001 // Don't let these be edited
1002 event.Veto();
1003 break;
1004
1007 // Do nothing here
1008 break;
1009 }
1010 }
1011 }
1012}
1013
1014
1016{
1017 wxTreeItemId id = event.GetItem();
1018
1019 if( auto* tbData = dynamic_cast<TOOLBAR_TREE_ITEM_DATA*>( m_toolbarTree->GetItemData( id ) ) )
1020 {
1021 if( tbData->GetType() == TOOLBAR_ITEM_TYPE::TB_GROUP )
1022 {
1023 m_toolbarTree->EditLabel( id );
1024 return;
1025 }
1026 }
1027
1028 event.Skip();
1029}
1030
1031
1033{
1034 if( event.IsEditCancelled() )
1035 return;
1036
1037 wxTreeItemId id = event.GetItem();
1038
1039 if( auto* tbData = dynamic_cast<TOOLBAR_TREE_ITEM_DATA*>( m_toolbarTree->GetItemData( id ) ) )
1040 {
1041 if( tbData->GetType() == TOOLBAR_ITEM_TYPE::TB_GROUP )
1042 {
1043 if( event.GetLabel().Strip( wxString::both ).IsEmpty() )
1044 {
1045 event.Veto();
1046 return;
1047 }
1048
1049 tbData->SetName( event.GetLabel() );
1050 }
1051 }
1052
1053 event.Skip();
1054}
1055
1056
1058{
1059 // Store the current toolbar
1060 std::optional<TOOLBAR_CONFIGURATION> currentTb = parseToolbarTree();
1061
1062 if( currentTb.has_value() )
1063 m_toolbars[m_currentToolbar] = currentTb.value();
1064
1065 int idx = event.GetInt();
1066
1067 if( idx >= 0 && idx < static_cast<int>( m_toolbarChoices.size() ) )
1068 {
1071 }
1072}
1073
1074
1076{
1077 wxCommandEvent dummy;
1079}
1080
1081
1083{
1084 for( auto& [loc, config] : m_toolbars )
1085 {
1086 if( loc == m_currentToolbar )
1087 continue;
1088
1089 auto& items = config.m_toolbarItems;
1090
1091 items.erase( std::remove_if( items.begin(), items.end(),
1092 [&]( const TOOLBAR_ITEM& item )
1093 {
1094 return item.m_Type == TOOLBAR_ITEM_TYPE::CONTROL
1095 && item.m_ControlName == aControlName;
1096 } ),
1097 items.end() );
1098 }
1099}
1100
1101
1103{
1104 wxTreeItemId rootId = m_toolbarTree->GetRootItem();
1105
1106 if( !rootId.IsOk() )
1107 return;
1108
1109 std::vector<wxTreeItemId> toDelete;
1110 wxTreeItemIdValue cookie;
1111
1112 for( wxTreeItemId id = m_toolbarTree->GetFirstChild( rootId, cookie );
1113 id.IsOk();
1114 id = m_toolbarTree->GetNextChild( rootId, cookie ) )
1115 {
1117 dynamic_cast<TOOLBAR_TREE_ITEM_DATA*>( m_toolbarTree->GetItemData( id ) );
1118
1119 if( data
1121 && data->GetControl()
1122 && data->GetControl()->GetName() == aControlName )
1123 {
1124 toDelete.push_back( id );
1125 }
1126 }
1127
1128 for( const wxTreeItemId& id : toDelete )
1129 m_toolbarTree->Delete( id );
1130}
const char * name
wxBitmapBundle KiBitmapBundleDef(BITMAPS aBitmap, int aDefHeight)
Constructs and returns a bitmap bundle for the given icon ID, with the default bitmap size being aDef...
Definition bitmap.cpp:112
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:106
@ INVALID_BITMAP
static TOOL_ACTION zoomRedraw
Definition actions.h:128
static TOOL_ACTION zoomOutCenter
Definition actions.h:132
static TOOL_ACTION zoomFitScreen
Definition actions.h:138
static TOOL_ACTION zoomInCenter
Definition actions.h:131
static std::string FrameNamespacePrefix(FRAME_T aFrameType)
Return the action-name namespace prefix (e.g.
Class to hold basic information about controls that can be added to the toolbars.
const std::string & GetName() const
APP_SETTINGS_BASE is a settings class that should be derived for each standalone KiCad application.
PANEL_TOOLBAR_CUSTOMIZATION_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxTAB_TRAVERSAL, const wxString &name=wxEmptyString)
bool actionMatchesFilter(const ACTION_LIST_ENTRY &aEntry, const wxString &aFilter) const
void onSpacerPress(wxCommandEvent &aEvent)
wxVector< wxBitmapBundle > m_actionImageBundleVector
void onActionListMouseMove(wxMouseEvent &event)
void onTbChoiceSelect(wxCommandEvent &event) override
bool isActionSupported(const TOOL_ACTION &aAction) const
void onTreeEndLabelEdit(wxTreeEvent &event) override
void onTreeBeginLabelEdit(wxTreeEvent &event) override
void removeControlFromCurrentTree(const std::string &aControlName)
void onToolDelete(wxCommandEvent &event) override
std::map< TOOLBAR_LOC, TOOLBAR_CONFIGURATION > m_toolbars
void onGroupPress(wxCommandEvent &aEvent)
void onSeparatorPress(wxCommandEvent &aEvent)
void ResetPanel() override
Reset the contents of this panel.
std::vector< TOOLBAR_LOC > m_toolbarChoices
std::map< std::string, int > m_actionImageListMap
void onBtnAddAction(wxCommandEvent &event) override
void removeControlFromOtherToolbars(const std::string &aControlName)
std::map< std::string, TOOL_ACTION * > m_availableTools
std::map< std::string, ACTION_TOOLBAR_CONTROL * > m_availableControls
std::optional< TOOLBAR_CONFIGURATION > parseToolbarTree()
PANEL_TOOLBAR_CUSTOMIZATION(wxWindow *aParent, APP_SETTINGS_BASE *aCfg, TOOLBAR_SETTINGS *aTbSettings, FRAME_T aActionContext, const std::vector< TOOL_ACTION * > &aTools, const std::vector< ACTION_TOOLBAR_CONTROL * > &aControls)
void onCustomizeTbCb(wxCommandEvent &event) override
void onActionFilterText(wxCommandEvent &event)
void onToolMoveDown(wxCommandEvent &event) override
void onListItemActivated(wxListEvent &event) override
void onToolMoveUp(wxCommandEvent &event) override
std::vector< ACTION_LIST_ENTRY > m_actionEntries
std::vector< TOOLBAR_ITEM > GetToolbarItems() const
TOOLBAR_GROUP_CONFIG & AddAction(std::string aActionName)
std::vector< TOOLBAR_ITEM > GetGroupItems() const
std::vector< TOOLBAR_ITEM > m_GroupItems
std::string m_ActionName
void SetName(const wxString &aName)
void SetAction(TOOL_ACTION *aAction)
void SetControl(ACTION_TOOLBAR_CONTROL *aControl)
TOOLBAR_TREE_ITEM_DATA(TOOLBAR_ITEM_TYPE aType)
TOOLBAR_TREE_ITEM_DATA(TOOLBAR_ITEM_TYPE aType, int aSize)
TOOLBAR_TREE_ITEM_DATA(TOOLBAR_ITEM_TYPE aType, wxString aName)
TOOLBAR_ITEM_TYPE GetType() const
ACTION_TOOLBAR_CONTROL * GetControl() const
Represent a single user action.
const std::string & GetName() const
Return name of the action.
#define _(s)
FRAME_T
The set of EDA_BASE_FRAME derivatives, typically stored in EDA_BASE_FRAME::m_Ident.
Definition frame_type.h:29
@ FRAME_PCB_DISPLAY3D
Definition frame_type.h:43
KICOMMON_API wxFont GetInfoFont(wxWindow *aWindow)
static std::map< TOOLBAR_LOC, wxString > s_toolbarNameMap
std::vector< FAB_LAYER_COLOR > dummy
TOOL_ACTION * action
wxString label
ACTION_TOOLBAR_CONTROL * control
int image_index
wxString tooltip
wxString search_text
@ HIDDEN
Action is hidden from the toolbar.
Definition tool_action.h:59
@ RIGHT
Toolbar on the right side of the canvas.
@ LEFT
Toolbar on the left side of the canvas.
@ TOP_AUX
Toolbar on the top of the canvas.
@ TOP_MAIN
Toolbar on the top of the canvas.
Functions to provide common constants and other functions to assist in making a consistent UI.
#define SEPARATOR