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