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( TOOL_ACTION* tool : aTools )
143 m_availableTools.emplace( tool->GetName(), tool );
144
145 for( ACTION_TOOLBAR_CONTROL* 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, this, ID_SPACER_MENU );
164 insertMenu->Bind( wxEVT_COMMAND_MENU_SELECTED, &PANEL_TOOLBAR_CUSTOMIZATION::onGroupPress, this, ID_GROUP_MENU );
165
166 // This is the button only press for the browse button instead of the menu
168
169 m_actionFilter->ShowSearchButton( false );
170 m_actionFilter->ShowCancelButton( true );
171 m_actionFilter->SetDescriptiveText( _( "Filter actions" ) );
172
173#ifdef __WXGTK__
174 m_actionFilter->SetMinSize( wxSize( -1, GetTextExtent( wxT( "qb" ) ).y + 10 ) );
175#endif
176
178 m_actionFilter->Bind( wxEVT_SEARCHCTRL_CANCEL_BTN,
181 m_actionsList->Bind( wxEVT_LEAVE_WINDOW, &PANEL_TOOLBAR_CUSTOMIZATION::onActionListMouseMove, this );
182
183 m_toolbarTree->Bind( wxEVT_TREE_ITEM_ACTIVATED, &PANEL_TOOLBAR_CUSTOMIZATION::onTreeItemActivated, this );
184
185 // TODO (ISM): Enable draging
186 m_btnToolMoveDown->Enable( false );
187 m_btnToolMoveUp->Enable( false );
188}
189
190
199
200
202{
203 const std::string& name = aAction.GetName();
204
205 auto hasPrefix =
206 [&]( const char* aPrefix ) -> bool
207 {
208 return name.rfind( aPrefix, 0 ) == 0;
209 };
210
212 {
213 if( hasPrefix( "3DViewer." ) )
214 return true;
215
216 return name == ACTIONS::zoomRedraw.GetName() || name == ACTIONS::zoomInCenter.GetName()
217 || name == ACTIONS::zoomOutCenter.GetName() || name == ACTIONS::zoomFitScreen.GetName();
218 }
219
220 if( hasPrefix( "common." ) )
221 return true;
222
223 const std::string framePrefix = ACTION_MANAGER::FrameNamespacePrefix( m_actionContext );
224
225 return !framePrefix.empty() && hasPrefix( framePrefix.c_str() );
226}
227
228
230{
231 m_toolbars.clear();
232 m_toolbarChoices.clear();
233
234 // Go over every toolbar and initialize things
235 for( const TOOLBAR_LOC& tb : magic_enum::enum_values<TOOLBAR_LOC>() )
236 {
237 // Create a shadow toolbar
238 std::optional<TOOLBAR_CONFIGURATION> tbConfig = m_appTbSettings->DefaultToolbarConfig( tb );
239
240 if( !tbConfig.has_value() )
241 continue;
242
243 m_toolbars[tb] = tbConfig.value();
244 m_toolbarChoices.push_back( tb );
245 }
246
247 if( !m_toolbarChoices.empty() )
248 {
249 m_tbChoice->SetSelection( 0 );
251 }
252
254}
255
256
258{
259 wxArrayString tbChoices;
260
261 m_toolbars.clear();
262 m_toolbarChoices.clear();
263
264 // Go over every toolbar and initialize things
265 for( const TOOLBAR_LOC& tb : magic_enum::enum_values<TOOLBAR_LOC>() )
266 {
267 // Create a shadow toolbar
268 std::optional<TOOLBAR_CONFIGURATION> tbConfig = m_appTbSettings->GetToolbarConfig( tb );
269
270 if( !tbConfig.has_value() )
271 continue;
272
273 m_toolbars.emplace( tb, tbConfig.value() );
274 m_toolbarChoices.push_back( tb );
275
276 // Setup the UI name
277 const auto& tbName = s_toolbarNameMap.find( tb );
278
279 wxASSERT_MSG( tbName != s_toolbarNameMap.end(),
280 wxString::Format( "Unknown toolbar: %s", magic_enum::enum_name( tb ) ) );
281
282 tbChoices.Add( tbName->second );
283 }
284
285 m_tbChoice->Set( tbChoices );
286
287 // Always populate the actions before the toolbars, that way the icons are available
289
290 if( !m_toolbarChoices.empty() )
291 {
292 m_tbChoice->SetSelection( 0 );
294 }
295
297
298 // Sync the enable/disable control
299 enableCustomControls( m_appSettings->m_CustomToolbars );
300 m_customToolbars->SetValue( m_appSettings->m_CustomToolbars );
301
302 return true;
303}
304
305
307{
308 m_appSettings->m_CustomToolbars = m_customToolbars->GetValue();
309
310 // Store the current toolbar
311 std::optional<TOOLBAR_CONFIGURATION> currentTb = parseToolbarTree();
312
313 if( currentTb.has_value() )
314 m_toolbars[m_currentToolbar] = currentTb.value();
315
316 std::set<std::string> seenControls;
317
318 for( auto& [loc, config] : m_toolbars )
319 {
320 auto& items = config.m_toolbarItems;
321
322 items.erase( std::remove_if( items.begin(), items.end(),
323 [&]( const TOOLBAR_ITEM& item )
324 {
325 if( item.m_Type != TOOLBAR_ITEM_TYPE::CONTROL )
326 return false;
327
328 return !seenControls.insert( item.m_ControlName ).second;
329 } ),
330 items.end() );
331 }
332
333 // Write the shadow toolbars with changes back to the app toolbar settings
334 for( const auto& [loc, config] : m_toolbars )
335 m_appTbSettings->SetStoredToolbarConfig( loc, config );
336
337 return true;
338}
339
340std::optional<TOOLBAR_CONFIGURATION> PANEL_TOOLBAR_CUSTOMIZATION::parseToolbarTree()
341{
343
344 wxTreeItemId mainId;
345 wxTreeItemId rootId = m_toolbarTree->GetRootItem();
346 wxTreeItemIdValue mainCookie;
347
348 if( !rootId.IsOk() )
349 return std::nullopt;
350
351 mainId = m_toolbarTree->GetFirstChild( rootId, mainCookie );
352
353 while( mainId.IsOk() )
354 {
355 wxTreeItemData* treeData = m_toolbarTree->GetItemData( mainId );
356
357 TOOLBAR_TREE_ITEM_DATA* tbData = dynamic_cast<TOOLBAR_TREE_ITEM_DATA*>( treeData );
358
359 wxCHECK2( tbData, continue );
360
361 switch( tbData->GetType() )
362 {
364 config.AppendSpacer( tbData->GetSize() );
365 break;
366
368 config.AppendSeparator();
369 break;
370
372 config.AppendControl( tbData->GetControl()->GetName() );
373 break;
374
376 config.AppendAction( *( tbData->GetAction() ) );
377 break;
378
380 TOOLBAR_GROUP_CONFIG grpConfig( tbData->GetName() );
381
382 if( m_toolbarTree->ItemHasChildren( mainId ) )
383 {
384 wxTreeItemIdValue childCookie;
385 wxTreeItemId childId = m_toolbarTree->GetFirstChild( mainId, childCookie );
386
387 while( childId.IsOk() )
388 {
389 wxTreeItemData* childTreeData = m_toolbarTree->GetItemData( childId );
390
391 TOOLBAR_TREE_ITEM_DATA* childTbData = dynamic_cast<TOOLBAR_TREE_ITEM_DATA*>( childTreeData );
392
393 wxCHECK2( childTbData, break );
394
395 switch( childTbData->GetType() )
396 {
401 wxASSERT_MSG( false, "Invalid entry in a group" );
402 break;
403
405 grpConfig.AddAction( *( childTbData->GetAction() ) );
406 break;
407 }
408
409 childId = m_toolbarTree->GetNextChild( mainId, childCookie );
410 }
411 }
412
413 if( !grpConfig.GetGroupItems().empty() )
414 config.AppendGroup( grpConfig );
415 }
416
417 mainId = m_toolbarTree->GetNextChild( rootId, mainCookie );
418 }
419
420 return config;
421}
422
423
425{
426 m_toolbarTree->DeleteAllItems();
428
429 const auto& it = m_toolbars.find( m_currentToolbar );
430
431 if( it == m_toolbars.end() )
432 {
433 // Disable the controls and bail out - no toolbar here
434 enableToolbarControls( false );
435 return;
436 }
437
438 // Ensure the controls are enabled
439 enableToolbarControls( true );
440
441 TOOLBAR_CONFIGURATION toolbar = it->second;
442
443 wxTreeItemId root = m_toolbarTree->AddRoot( "Toolbar" );
444
445 for( const TOOLBAR_ITEM& item : toolbar.GetToolbarItems() )
446 {
447 switch( item.m_Type )
448 {
450 {
451 // Add a separator
453 m_toolbarTree->AppendItem( root, _( "Separator" ), -1, -1, sepTreeItem );
454 break;
455 }
456
458 {
459 // Add a spacer
461 spacerTreeItem->SetSize( item.m_Size );
462 m_toolbarTree->AppendItem( root, wxString::Format( _( "Spacer: %i" ), item.m_Size ), -1, -1,
463 spacerTreeItem );
464 break;
465 }
466
468 {
469 auto controlIter = m_availableControls.find( item.m_ControlName );
470
471 if( controlIter == m_availableControls.end() )
472 {
473 wxASSERT_MSG( false, wxString::Format( "Unable to find control %s", item.m_ControlName ) );
474 continue;
475 }
476
477 // Add a control
479 controlTreeItem->SetControl( controlIter->second );
480 m_toolbarTree->AppendItem( root, controlIter->second->GetUiName(), -1, -1, controlTreeItem );
481 break;
482 }
483
485 {
486 // Add a tool
487 auto toolIter = m_availableTools.find( item.m_ActionName );
488
489 if( toolIter == m_availableTools.end() )
490 {
491 wxASSERT_MSG( false, wxString::Format( "Unable to find tool %s", item.m_ActionName ) );
492 continue;
493 }
494
495 if( !isActionSupported( *toolIter->second ) )
496 continue;
497
499 toolTreeItem->SetAction( toolIter->second );
500
501 int imgIdx = -1;
502 auto imgMap = m_actionImageListMap.find( item.m_ActionName );
503
504 if( imgMap != m_actionImageListMap.end() )
505 imgIdx = imgMap->second;
506
507 m_toolbarTree->AppendItem( root, toolIter->second->GetFriendlyName(), imgIdx, -1, toolTreeItem );
508 break;
509 }
510
512 {
513 // Add a group of items to the toolbar
515 groupTreeItem->SetName( item.m_GroupName );
516
517 wxTreeItemId groupId = m_toolbarTree->AppendItem( root, item.m_GroupName, -1, -1, groupTreeItem );
518 bool haveVisibleGroupItems = false;
519
520 // Add the elements below the group
521 for( const TOOLBAR_ITEM& groupItem : item.m_GroupItems )
522 {
523 auto toolMap = m_availableTools.find( groupItem.m_ActionName );
524
525 if( toolMap == m_availableTools.end() )
526 {
527 wxASSERT_MSG( false, wxString::Format( "Unable to find group tool %s", groupItem.m_ActionName ) );
528 continue;
529 }
530
531 if( !isActionSupported( *toolMap->second ) )
532 continue;
533
535 toolTreeItem->SetAction( toolMap->second );
536
537 int imgIdx = -1;
538 auto imgMap = m_actionImageListMap.find( groupItem.m_ActionName );
539
540 if( imgMap != m_actionImageListMap.end() )
541 imgIdx = imgMap->second;
542
543 m_toolbarTree->AppendItem( groupId, toolMap->second->GetFriendlyName(), imgIdx, -1, toolTreeItem );
544
545 haveVisibleGroupItems = true;
546 }
547
548 if( !haveVisibleGroupItems )
549 m_toolbarTree->Delete( groupId );
550
551 break;
552 }
553 }
554 }
555
556 m_toolbarTree->ExpandAll();
557
558 wxTreeItemIdValue temp;
559 wxTreeItemId firstItem = m_toolbarTree->GetFirstChild( root, temp );
560
561 if( firstItem.IsOk() )
562 {
563 m_toolbarTree->SelectItem( firstItem );
564 m_toolbarTree->EnsureVisible( firstItem );
565 }
566}
567
568
570{
571 const int c_defSize = 24; // Default icon size for toolbar actions
572
573 // Clear all existing information for the actions
574 m_actionImageListMap.clear();
576 m_actionEntries.clear();
577
578 // Prep the control
579 m_actionsList->DeleteAllItems();
580 m_actionsList->DeleteAllColumns();
581 m_actionsList->InsertColumn( 0, "", wxLIST_FORMAT_LEFT, wxLIST_AUTOSIZE );
582
583 for( const auto& [k, tool] : m_availableTools )
584 {
585 if( !isActionSupported( *tool ) )
586 continue;
587
588 if( tool->CheckToolbarState( TOOLBAR_STATE::HIDDEN ) )
589 continue;
590
591 ACTION_LIST_ENTRY entry;
592 entry.label = tool->GetFriendlyName();
593 entry.tooltip = tool->GetDescription(); // falls back to tooltip if no description provided
594 entry.action = tool;
595 entry.search_text = entry.label.Upper() + wxS( " " ) + entry.tooltip.Upper();
596
597 if( tool->GetIcon() != BITMAPS::INVALID_BITMAP )
598 {
599 int imgIdx = m_actionImageBundleVector.size();
600 m_actionImageBundleVector.push_back( KiBitmapBundleDef( tool->GetIcon(), c_defSize ) );
601 m_actionImageListMap.emplace( tool->GetName(), imgIdx );
602 entry.image_index = imgIdx;
603 }
604
605 m_actionEntries.push_back( std::move( entry ) );
606 }
607
608 for( const auto& [k, control] : m_availableControls )
609 {
610 ACTION_LIST_ENTRY entry;
611 entry.label = control->GetUiName();
612 entry.tooltip = control->GetDescription();
613 entry.control = control;
614 entry.search_text = entry.label.Upper() + wxS( " " ) + control->GetDescription().Upper();
615 m_actionEntries.push_back( std::move( entry ) );
616 }
617
618 std::sort( m_actionEntries.begin(), m_actionEntries.end(),
619 []( const ACTION_LIST_ENTRY& a, const ACTION_LIST_ENTRY& b )
620 {
621 return a.label.CmpNoCase( b.label ) < 0;
622 } );
623
624 m_actionsList->SetSmallImages( m_actionImageBundleVector );
626}
627
628
630 const wxString& aFilter ) const
631{
632 if( aFilter.IsEmpty() )
633 return true;
634
635 return aEntry.search_text.Contains( aFilter.Upper() );
636}
637
638
640{
641 wxFont listFont = KIUI::GetInfoFont( this );
642 wxString filter = m_actionFilter->GetValue();
643
645 m_actionsList->UnsetToolTip();
646
647 m_actionsList->DeleteAllItems();
648
649 for( size_t idx = 0; idx < m_actionEntries.size(); ++idx )
650 {
651 const ACTION_LIST_ENTRY& entry = m_actionEntries[idx];
652
653 if( !actionMatchesFilter( entry, filter ) )
654 continue;
655
656 wxListItem item;
657 item.SetId( m_actionsList->GetItemCount() );
658 item.SetText( entry.label );
659 item.SetFont( listFont );
660 item.SetData( static_cast<long>( idx ) );
661 item.SetImage( entry.image_index );
662
663 m_actionsList->InsertItem( item );
664 }
665
666 if( m_actionsList->GetItemCount() > 0 )
667 m_actionsList->SetItemState( 0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
668
669 m_actionsList->SetColumnWidth( 0, wxLIST_AUTOSIZE );
670}
671
672
673void PANEL_TOOLBAR_CUSTOMIZATION::onGroupPress( wxCommandEvent& aEvent )
674{
676
677 wxTreeItemId newItem;
678 wxTreeItemId selItem = m_toolbarTree->GetSelection();
679
680 if( selItem.IsOk() )
681 {
682 // Can't add a group onto a group
683 wxTreeItemId parent = m_toolbarTree->GetItemParent( selItem );
684
685 if( parent.IsOk() )
686 {
687 wxTreeItemId secondParent = m_toolbarTree->GetItemParent( parent );
688
689 if( secondParent.IsOk() )
690 {
691 delete treeItem;
692 return;
693 }
694 }
695
696 newItem = m_toolbarTree->InsertItem( m_toolbarTree->GetRootItem(), selItem, treeItem->GetName(), -1, -1,
697 treeItem );
698 }
699 else
700 {
701 newItem = m_toolbarTree->AppendItem( m_toolbarTree->GetRootItem(), treeItem->GetName(), -1, -1, treeItem );
702 }
703
704 if( newItem.IsOk() )
705 {
706 m_toolbarTree->SelectItem( newItem );
707 m_toolbarTree->EnsureVisible( newItem );
708 }
709}
710
711
712void PANEL_TOOLBAR_CUSTOMIZATION::onSpacerPress( wxCommandEvent& aEvent )
713{
715
716 wxString label = wxString::Format( "Spacer: %i", treeItem->GetSize() );
717
718 wxTreeItemId newItem;
719 wxTreeItemId selItem = m_toolbarTree->GetSelection();
720
721 if( selItem.IsOk() )
722 {
723 // Insert after the current selection at the same level
724 wxTreeItemId parent = m_toolbarTree->GetItemParent( selItem );
725
726 // Can't insert a spacer in a group yet
727 if( parent.IsOk() )
728 {
729 wxTreeItemId secondParent = m_toolbarTree->GetItemParent( parent );
730
731 if( secondParent.IsOk() )
732 {
733 delete treeItem;
734 return;
735 }
736 }
737
738 newItem = m_toolbarTree->InsertItem( parent, selItem, label, -1, -1, treeItem );
739 }
740 else
741 {
742 newItem = m_toolbarTree->AppendItem( m_toolbarTree->GetRootItem(), label, -1, -1, treeItem );
743 }
744
745 if( newItem.IsOk() )
746 {
747 m_toolbarTree->SelectItem( newItem );
748 m_toolbarTree->EnsureVisible( newItem );
749 }
750}
751
752
754{
756
757 wxTreeItemId newItem;
758 wxTreeItemId selItem = m_toolbarTree->GetSelection();
759
760 if( selItem.IsOk() )
761 {
762 // Insert after the current selection at the same level
763 wxTreeItemId parent = m_toolbarTree->GetItemParent( selItem );
764
765 // Can't insert a separator in a group yet
766 if( parent.IsOk() )
767 {
768 wxTreeItemId secondParent = m_toolbarTree->GetItemParent( parent );
769
770 if( secondParent.IsOk() )
771 {
772 delete treeItem;
773 return;
774 }
775 }
776
777 newItem = m_toolbarTree->InsertItem( parent, selItem, _( "Separator" ), -1, -1, treeItem );
778 }
779 else
780 {
781 newItem = m_toolbarTree->AppendItem( m_toolbarTree->GetRootItem(), _( "Separator" ), -1, -1, treeItem );
782 }
783
784 if( newItem.IsOk() )
785 {
786 m_toolbarTree->SelectItem( newItem );
787 m_toolbarTree->EnsureVisible( newItem );
788 }
789}
790
791
793{
794 enableCustomControls( event.IsChecked() );
795}
796
797
799{
800 m_tbChoice->Enable( enable );
801 enableToolbarControls( enable );
802}
803
804
806{
807 m_toolbarTree->Enable( enable );
808 m_btnAddTool->Enable( enable );
809 m_btnToolDelete->Enable( enable );
810
811 m_btnToolMoveDown->Enable( enable );
812 m_btnToolMoveUp->Enable( enable );
813 m_actionsList->Enable( enable );
814 m_actionFilter->Enable( enable );
815 m_insertButton->Enable( enable );
816}
817
818
819void PANEL_TOOLBAR_CUSTOMIZATION::onToolDelete( wxCommandEvent& event )
820{
821 wxTreeItemId item = m_toolbarTree->GetSelection();
822
823 if( !item.IsOk() )
824 return;
825
826 // The tree control defaults to nothing selected if you delete the item
827 // at the last place, so we have to manually get the itme immediately before
828 // the one we will delete, and then select it if nothing is selected.
829 wxTreeItemId prev = m_toolbarTree->GetPrevSibling( item );
830
831 m_toolbarTree->Delete( item );
832
833 item = m_toolbarTree->GetSelection();
834
835 if( !item.IsOk() && prev.IsOk() )
836 {
837 m_toolbarTree->SelectItem( prev );
838 m_toolbarTree->EnsureVisible( prev );
839 }
840}
841
842
843void PANEL_TOOLBAR_CUSTOMIZATION::onToolMoveUp( wxCommandEvent& event )
844{
845 m_toolbarTree->MoveItemUp( m_toolbarTree->GetSelection() );
846}
847
848
850{
851 m_toolbarTree->MoveItemDown( m_toolbarTree->GetSelection() );
852}
853
854
856{
857 // Get the selected item
858 long actionIdx = m_actionsList->GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
859
860 // Nothing is selected, bail out
861 if( actionIdx < 0 )
862 return;
863
864 size_t entryIdx = m_actionsList->GetItemData( actionIdx );
865
866 if( entryIdx >= m_actionEntries.size() )
867 return;
868
869 const ACTION_LIST_ENTRY& entry = m_actionEntries[entryIdx];
870 TOOL_ACTION* action = entry.action;
871 ACTION_TOOLBAR_CONTROL* control = entry.control;
872
873 if( control )
874 {
875 removeControlFromOtherToolbars( control->GetName() );
876 removeControlFromCurrentTree( control->GetName() );
877 }
878
879 // Build the item to add
882 toolTreeItem->SetAction( action );
883 toolTreeItem->SetControl( control );
884
885 int imgIdx = -1;
886
887 if( action )
888 {
889 auto imgMap = m_actionImageListMap.find( action->GetName() );
890
891 if( imgMap != m_actionImageListMap.end() )
892 imgIdx = imgMap->second;
893 }
894
895 // Actually add the item
896 wxTreeItemId selItem = m_toolbarTree->GetSelection();
897 wxTreeItemId newItem;
898
899 if( selItem.IsOk() )
900 {
901 TOOLBAR_TREE_ITEM_DATA* data = dynamic_cast<TOOLBAR_TREE_ITEM_DATA*>( m_toolbarTree->GetItemData( selItem ) );
902
903 if( data && data->GetType() == TOOLBAR_ITEM_TYPE::TB_GROUP )
904 {
905 // Insert into the end of the group
906 newItem = m_toolbarTree->AppendItem( selItem, entry.label, imgIdx, -1, toolTreeItem );
907 }
908 else
909 {
910 // Insert after the current selection at the same level
911 wxTreeItemId parent = m_toolbarTree->GetItemParent( selItem );
912 newItem = m_toolbarTree->InsertItem( parent, selItem, entry.label, imgIdx, -1, toolTreeItem );
913 }
914 }
915 else
916 {
917 // Insert at the root level if there is no selection
918 newItem = m_toolbarTree->AppendItem( m_toolbarTree->GetRootItem(), entry.label, imgIdx, -1, toolTreeItem );
919 }
920
921 if( newItem.IsOk() )
922 {
923 m_toolbarTree->SelectItem( newItem );
924 m_toolbarTree->EnsureVisible( newItem );
925
926 // Move the action to the next available one, to be nice
927 if( ++actionIdx < m_actionsList->GetItemCount() )
928 m_actionsList->SetItemState( actionIdx, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
929 }
930}
931
932
934{
936 aEvent.Skip();
937}
938
939
941{
942 aEvent.Skip();
943
944 if( aEvent.Leaving() )
945 {
947 m_actionsList->UnsetToolTip();
948 return;
949 }
950
951 int flags = 0;
952 long item = m_actionsList->HitTest( aEvent.GetPosition(), flags );
953
954 if( item >= 0 )
955 {
956 long entryIdx = static_cast<long>( m_actionsList->GetItemData( item ) );
957
958 if( entryIdx >= 0 && entryIdx < static_cast<long>( m_actionEntries.size() ) )
959 {
960 if( m_hoveredActionEntry != entryIdx )
961 {
962 m_hoveredActionEntry = entryIdx;
963
964 if( const wxString& tooltip = m_actionEntries[entryIdx].tooltip; tooltip.IsEmpty() )
965 m_actionsList->UnsetToolTip();
966 else
967 m_actionsList->SetToolTip( tooltip );
968 }
969
970 return;
971 }
972 }
973
974 if( m_hoveredActionEntry != -1 )
975 {
977 m_actionsList->UnsetToolTip();
978 }
979}
980
981
983{
984 wxTreeItemId id = event.GetItem();
985
986 if( id.IsOk() )
987 {
988 wxTreeItemData* treeData = m_toolbarTree->GetItemData( id );
989
990 if( TOOLBAR_TREE_ITEM_DATA* tbData = dynamic_cast<TOOLBAR_TREE_ITEM_DATA*>( treeData ) )
991 {
992 switch( tbData->GetType() )
993 {
997 // Don't let these be edited
998 event.Veto();
999 break;
1000
1003 // Do nothing here
1004 break;
1005 }
1006 }
1007 }
1008}
1009
1010
1012{
1013 wxTreeItemId id = event.GetItem();
1014
1015 if( TOOLBAR_TREE_ITEM_DATA* tbData = dynamic_cast<TOOLBAR_TREE_ITEM_DATA*>( m_toolbarTree->GetItemData( id ) ) )
1016 {
1017 if( tbData->GetType() == TOOLBAR_ITEM_TYPE::TB_GROUP )
1018 {
1019 m_toolbarTree->EditLabel( id );
1020 return;
1021 }
1022 }
1023
1024 event.Skip();
1025}
1026
1027
1029{
1030 if( event.IsEditCancelled() )
1031 return;
1032
1033 wxTreeItemId id = event.GetItem();
1034
1035 if( TOOLBAR_TREE_ITEM_DATA* tbData = dynamic_cast<TOOLBAR_TREE_ITEM_DATA*>( m_toolbarTree->GetItemData( id ) ) )
1036 {
1037 if( tbData->GetType() == TOOLBAR_ITEM_TYPE::TB_GROUP )
1038 {
1039 if( event.GetLabel().Strip( wxString::both ).IsEmpty() )
1040 {
1041 event.Veto();
1042 return;
1043 }
1044
1045 tbData->SetName( event.GetLabel() );
1046 }
1047 }
1048
1049 event.Skip();
1050}
1051
1052
1054{
1055 // Store the current toolbar
1056 std::optional<TOOLBAR_CONFIGURATION> currentTb = parseToolbarTree();
1057
1058 if( currentTb.has_value() )
1059 m_toolbars[m_currentToolbar] = currentTb.value();
1060
1061 int idx = event.GetInt();
1062
1063 if( idx >= 0 && idx < static_cast<int>( m_toolbarChoices.size() ) )
1064 {
1067 }
1068}
1069
1070
1072{
1073 wxCommandEvent dummy;
1075}
1076
1077
1079{
1080 for( auto& [loc, config] : m_toolbars )
1081 {
1082 if( loc == m_currentToolbar )
1083 continue;
1084
1085 auto& items = config.m_toolbarItems;
1086
1087 items.erase( std::remove_if( items.begin(), items.end(),
1088 [&]( const TOOLBAR_ITEM& item )
1089 {
1090 return item.m_Type == TOOLBAR_ITEM_TYPE::CONTROL
1091 && item.m_ControlName == aControlName;
1092 } ),
1093 items.end() );
1094 }
1095}
1096
1097
1099{
1100 wxTreeItemId rootId = m_toolbarTree->GetRootItem();
1101
1102 if( !rootId.IsOk() )
1103 return;
1104
1105 std::vector<wxTreeItemId> toDelete;
1106 wxTreeItemIdValue cookie;
1107
1108 for( wxTreeItemId id = m_toolbarTree->GetFirstChild( rootId, cookie );
1109 id.IsOk();
1110 id = m_toolbarTree->GetNextChild( rootId, cookie ) )
1111 {
1112 TOOLBAR_TREE_ITEM_DATA* data = dynamic_cast<TOOLBAR_TREE_ITEM_DATA*>( m_toolbarTree->GetItemData( id ) );
1113
1114 if( data
1116 && data->GetControl()
1117 && data->GetControl()->GetName() == aControlName )
1118 {
1119 toDelete.push_back( id );
1120 }
1121 }
1122
1123 for( const wxTreeItemId& id : toDelete )
1124 m_toolbarTree->Delete( id );
1125}
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