KiCad PCB EDA Suite
Loading...
Searching...
No Matches
widget_hotkey_list.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 (C) 2016 Chris Pavlina <[email protected]>
5 * Copyright (C) 2016-2023 KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 3
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25#include <cctype>
26
27#include <confirm.h>
29#include <tool/tool_event.h>
30#include <dialog_shim.h>
31
32#include <wx/button.h>
33#include <wx/log.h>
34#include <wx/dcclient.h>
35#include <wx/menu.h>
36#include <wx/msgdlg.h>
37#include <wx/statline.h>
38#include <wx/stattext.h>
39#include <wx/treelist.h>
40
45{
52};
53
54
61class WIDGET_HOTKEY_CLIENT_DATA : public wxClientData
62{
64
65public:
66 WIDGET_HOTKEY_CLIENT_DATA( HOTKEY& aChangedHotkey ) :
67 m_changed_hotkey( aChangedHotkey )
68 {}
69
71};
72
73
78{
79public:
80 HK_PROMPT_DIALOG( wxWindow* aParent, wxWindowID aId, const wxString& aTitle,
81 const wxString& aName, const wxString& aCurrentKey ) :
82 DIALOG_SHIM( aParent, aId, aTitle, wxDefaultPosition, wxDefaultSize )
83 {
84 wxBoxSizer* mainSizer = new wxBoxSizer( wxVERTICAL );
85 SetSizer( mainSizer );
86
87 /* Dialog layout:
88 *
89 * inst_label........................
90 * ----------------------------------
91 *
92 * cmd_label_0 cmd_label_1 \
93 * | fgsizer
94 * key_label_0 key_label_1 /
95 */
96
97 wxStaticText* inst_label = new wxStaticText( this, wxID_ANY, wxEmptyString,
98 wxDefaultPosition, wxDefaultSize,
99 wxALIGN_CENTRE_HORIZONTAL );
100
101 inst_label->SetLabelText( _( "Press a new hotkey, or press Esc to cancel..." ) );
102 mainSizer->Add( inst_label, 0, wxALL, 10 );
103
104 mainSizer->Add( new wxStaticLine( this ), 0, wxALL | wxEXPAND, 2 );
105
106 wxPanel* panelDisplayCurrent = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize );
107 mainSizer->Add( panelDisplayCurrent, 0, wxALL | wxEXPAND, 5 );
108
109 wxFlexGridSizer* fgsizer = new wxFlexGridSizer( 2 );
110 panelDisplayCurrent->SetSizer( fgsizer );
111
112 wxStaticText* cmd_label_0 = new wxStaticText( panelDisplayCurrent, wxID_ANY, _( "Command:" ) );
113 fgsizer->Add( cmd_label_0, 0, wxALL | wxALIGN_CENTRE_VERTICAL, 5 );
114
115 wxStaticText* cmd_label_1 = new wxStaticText( panelDisplayCurrent, wxID_ANY, wxEmptyString );
116 cmd_label_1->SetFont( cmd_label_1->GetFont().Bold() );
117 cmd_label_1->SetLabel( aName );
118 fgsizer->Add( cmd_label_1, 0, wxALL | wxALIGN_CENTRE_VERTICAL, 5 );
119
120 wxStaticText* key_label_0 = new wxStaticText( panelDisplayCurrent, wxID_ANY, _( "Current key:" ) );
121 fgsizer->Add( key_label_0, 0, wxALL | wxALIGN_CENTRE_VERTICAL, 5 );
122
123 wxStaticText* key_label_1 = new wxStaticText( panelDisplayCurrent, wxID_ANY, wxEmptyString );
124 key_label_1->SetFont( key_label_1->GetFont().Bold() );
125 key_label_1->SetLabel( aCurrentKey );
126 fgsizer->Add( key_label_1, 0, wxALL | wxALIGN_CENTRE_VERTICAL, 5 );
127
128 fgsizer->AddStretchSpacer();
129
130 wxButton* resetButton = new wxButton( this, wxID_ANY, _( "Clear assigned hotkey" ), wxDefaultPosition, wxDefaultSize, 0 );
131
132 mainSizer->Add( resetButton, 0, wxALL | wxALIGN_CENTRE_HORIZONTAL, 5 );
133
134 Layout();
135 mainSizer->Fit( this );
136 Center();
137
138 SetMinClientSize( GetClientSize() );
139
140 // Binding both EVT_CHAR and EVT_CHAR_HOOK ensures that all key events, including
141 // specials like Tab and Return, are received, particularly on MSW.
142 panelDisplayCurrent->Bind( wxEVT_CHAR, &HK_PROMPT_DIALOG::OnChar, this );
143 panelDisplayCurrent->Bind( wxEVT_CHAR_HOOK, &HK_PROMPT_DIALOG::OnCharHook, this );
144 panelDisplayCurrent->Bind( wxEVT_KEY_UP, &HK_PROMPT_DIALOG::OnKeyUp, this );
145
146 resetButton->Bind( wxEVT_COMMAND_BUTTON_CLICKED, &HK_PROMPT_DIALOG::onResetButton, this );
147
148 SetInitialFocus( panelDisplayCurrent );
149 }
150
151 static std::optional<long> PromptForKey( wxWindow* aParent, const wxString& aName,
152 const wxString& aCurrentKey )
153 {
154 HK_PROMPT_DIALOG dialog( aParent, wxID_ANY, _( "Set Hotkey" ), aName, aCurrentKey );
155
156 if( dialog.ShowModal() == wxID_OK )
157 {
158 if( dialog.m_resetkey )
159 return std::make_optional( 0 );
160 else
161 {
163
164 if( key )
165 return std::make_optional( key );
166 else // The ESC key was used to close the dialog
167 return std::nullopt;
168 }
169 }
170 else
171 {
172 return std::nullopt;
173 }
174 }
175
176protected:
177 void OnCharHook( wxKeyEvent& aEvent ) override
178 {
179 // On certain platforms, EVT_CHAR_HOOK is the only handler that receives certain
180 // "special" keys. However, it doesn't always receive "normal" keys correctly. For
181 // example, with a US keyboard, it sees ? as shift+/.
182 //
183 // Untangling these incorrect keys would be too much trouble, so we bind both events,
184 // and simply skip the EVT_CHAR_HOOK if it receives a "normal" key.
185
186 const enum wxKeyCode skipped_keys[] =
187 {
188 WXK_NONE, WXK_SHIFT, WXK_ALT, WXK_CONTROL, WXK_CAPITAL, WXK_NUMLOCK, WXK_SCROLL,
189 WXK_RAW_CONTROL
190 };
191
192 int key = aEvent.GetKeyCode();
193
194 for( wxKeyCode skipped_key : skipped_keys )
195 {
196 if( key == skipped_key )
197 return;
198 }
199
200 if( key <= 255 && isprint( key ) && !isspace( key ) )
201 {
202 // Let EVT_CHAR handle this one
203 aEvent.DoAllowNextEvent();
204
205 // On Windows, wxEvent::Skip must NOT be called.
206 // On Linux and OSX, wxEvent::Skip MUST be called.
207 // No, I don't know why.
208#ifndef __WXMSW__
209 aEvent.Skip();
210#endif
211 }
212 else
213 {
214 OnChar( aEvent );
215 }
216 }
217
218 void OnChar( wxKeyEvent& aEvent )
219 {
220 m_event = aEvent;
221 }
222
223 void OnKeyUp( wxKeyEvent& aEvent )
224 {
225 // If dialog opened using Enter key, prevent closing when releasing Enter.
226 if( m_event.GetEventType() != wxEVT_NULL )
227 {
229 wxPostEvent( this, wxCommandEvent( wxEVT_COMMAND_BUTTON_CLICKED, wxID_OK ) );
230 }
231 }
232
233 void onResetButton( wxCommandEvent& aEvent )
234 {
235 m_resetkey = true;
236 wxPostEvent( this, wxCommandEvent( wxEVT_COMMAND_BUTTON_CLICKED, wxID_OK ) );
237 }
238
239private:
240 bool m_resetkey = false;
241 wxKeyEvent m_event;
242};
243
244
249{
250public:
251 HOTKEY_FILTER( const wxString& aFilterStr )
252 {
253 m_normalised_filter_str = aFilterStr.Upper();
254 m_valid = m_normalised_filter_str.size() > 0;
255 }
256
262 bool FilterMatches( const HOTKEY& aHotkey ) const
263 {
264 if( !m_valid )
265 return true;
266
267 // Match in the (translated) filter string
268 const auto normedInfo = wxGetTranslation( aHotkey.m_Actions[ 0 ]->GetFriendlyName() ).Upper();
269
270 if( normedInfo.Contains( m_normalised_filter_str ) )
271 return true;
272
273 const wxString keyName = KeyNameFromKeyCode( aHotkey.m_EditKeycode );
274
275 if( keyName.Upper().Contains( m_normalised_filter_str ) )
276 return true;
277
278 return false;
279 }
280
281private:
284};
285
286
288{
289 if( aItem.IsOk() )
290 {
291 wxClientData* data = GetItemData( aItem );
292
293 if( data )
294 return static_cast<WIDGET_HOTKEY_CLIENT_DATA*>( data );
295 }
296
297 return nullptr;
298}
299
300
302{
303 const auto hkdata = getHKClientData( aItem );
304
305 // This probably means a hotkey-only action is being attempted on
306 // a row that is not a hotkey (like a section heading)
307 wxASSERT_MSG( hkdata != nullptr, "No hotkey data found for list item" );
308
309 return hkdata;
310}
311
312
314{
315 for( wxTreeListItem i = GetFirstItem(); i.IsOk(); i = GetNextItem( i ) )
316 {
318
319 if( hkdata )
320 {
321 const HOTKEY& changed_hk = hkdata->GetChangedHotkey();
322 wxString label = changed_hk.m_Actions[ 0 ]->GetFriendlyName();
323 wxString key_text = KeyNameFromKeyCode( changed_hk.m_EditKeycode );
324 wxString alt_text = KeyNameFromKeyCode( changed_hk.m_EditKeycodeAlt );
325 wxString description = changed_hk.m_Actions[ 0 ]->GetDescription();
326
327 if( label.IsEmpty() )
328 label = changed_hk.m_Actions[ 0 ]->GetName();
329
330 label.Replace( wxT( "..." ), wxEmptyString );
331 label.Replace( wxT( "…" ), wxEmptyString );
332
333 // mark unsaved changes
334 if( changed_hk.m_EditKeycode != changed_hk.m_Actions[ 0 ]->GetHotKey() )
335 label += wxS( " *" );
336
337 description.Replace( wxS( "\n" ), wxS( " " ) );
338 description.Replace( wxS( "\r" ), wxS( " " ) );
339
340 SetItemText( i, 0, label );
341 SetItemText( i, 1, key_text );
342 SetItemText( i, 2, alt_text );
343 SetItemText( i, 3, description );
344 }
345 }
346}
347
348
349void WIDGET_HOTKEY_LIST::changeHotkey( HOTKEY& aHotkey, long aKey, bool alternate )
350{
351 // See if this key code is handled in hotkeys names list
352 bool exists;
353 KeyNameFromKeyCode( aKey, &exists );
354
355 if( exists && aHotkey.m_EditKeycode != aKey )
356 {
357 if( aKey == 0 || resolveKeyConflicts( aHotkey.m_Actions[ 0 ], aKey ) )
358 {
359 if( alternate )
360 aHotkey.m_EditKeycodeAlt = aKey;
361 else
362 aHotkey.m_EditKeycode = aKey;
363 }
364 }
365}
366
367
368void WIDGET_HOTKEY_LIST::editItem( wxTreeListItem aItem, int aEditId )
369{
371
372 if( !hkdata )
373 return;
374
375 wxString name = GetItemText( aItem, 0 );
376 wxString current_key =
377 aEditId == ID_EDIT_HOTKEY ? GetItemText( aItem, 1 ) : GetItemText( aItem, 2 );
378
379 std::optional<long> key = HK_PROMPT_DIALOG::PromptForKey( this, name, current_key );
380
381 // An empty optional means don't change the key
382 if( key.has_value() )
383 {
384 auto it = m_reservedHotkeys.find( key.value() );
385
386 if( it != m_reservedHotkeys.end() )
387 {
388 wxString msg = wxString::Format( _( "'%s' is a reserved hotkey in KiCad and cannot "
389 "be assigned." ),
390 it->second );
391
392 DisplayErrorMessage( this, msg );
393 return;
394 }
395
396 changeHotkey( hkdata->GetChangedHotkey(), key.value(), aEditId == ID_EDIT_ALT );
398 }
399}
400
401
402void WIDGET_HOTKEY_LIST::resetItem( wxTreeListItem aItem, int aResetId )
403{
405
406 if( !hkdata )
407 return;
408
409 HOTKEY& changed_hk = hkdata->GetChangedHotkey();
410
411 if( aResetId == ID_RESET )
412 {
413 changeHotkey( changed_hk, changed_hk.m_Actions[0]->GetHotKey(), false );
414 changeHotkey( changed_hk, changed_hk.m_Actions[0]->GetHotKey(), true );
415 }
416 else if( aResetId == ID_CLEAR )
417 changeHotkey( changed_hk, 0, false );
418 else if( aResetId == ID_CLEAR_ALT )
419 changeHotkey( changed_hk, 0, true );
420 else if( aResetId == ID_DEFAULT )
421 {
422 changeHotkey( changed_hk, changed_hk.m_Actions[0]->GetDefaultHotKey(), false );
423 changeHotkey( changed_hk, changed_hk.m_Actions[0]->GetDefaultHotKeyAlt(), true );
424 }
425
427}
428
429
430void WIDGET_HOTKEY_LIST::onActivated( wxTreeListEvent& aEvent )
431{
432 editItem( aEvent.GetItem(), ID_EDIT_HOTKEY );
433}
434
435
436void WIDGET_HOTKEY_LIST::onContextMenu( wxTreeListEvent& aEvent )
437{
438 // Save the active event for use in OnMenu
439 m_context_menu_item = aEvent.GetItem();
440
441 wxMenu menu;
442
444
445 // Some actions only apply if the row is hotkey data
446 if( hkdata )
447 {
448 menu.Append( ID_EDIT_HOTKEY, _( "Edit..." ) );
449 menu.Append( ID_EDIT_ALT, _( "Edit Alternate..." ) );
450 menu.Append( ID_RESET, _( "Undo Changes" ) );
451 menu.Append( ID_CLEAR, _( "Clear Assigned Hotkey" ) );
452 menu.Append( ID_CLEAR_ALT, _( "Clear Assigned Alternate" ) );
453 menu.Append( ID_DEFAULT, _( "Restore Defaults" ) );
454 menu.Append( wxID_SEPARATOR );
455
456 PopupMenu( &menu );
457 }
458}
459
460
461void WIDGET_HOTKEY_LIST::onMenu( wxCommandEvent& aEvent )
462{
463 switch( aEvent.GetId() )
464 {
465 case ID_EDIT_HOTKEY:
466 case ID_EDIT_ALT:
467 editItem( m_context_menu_item, aEvent.GetId() );
468 break;
469
470 case ID_RESET:
471 case ID_CLEAR:
472 case ID_CLEAR_ALT:
473 case ID_DEFAULT:
474 resetItem( m_context_menu_item, aEvent.GetId() );
475 break;
476
477 default:
478 wxFAIL_MSG( wxT( "Unknown ID in context menu event" ) );
479 }
480}
481
482
484{
485 HOTKEY* conflictingHotKey = nullptr;
486
487 m_hk_store.CheckKeyConflicts( aAction, aKey, &conflictingHotKey );
488
489 if( !conflictingHotKey )
490 return true;
491
492 TOOL_ACTION* conflictingAction = conflictingHotKey->m_Actions[ 0 ];
493 wxString msg = wxString::Format( _( "'%s' is already assigned to '%s' in section '%s'. "
494 "Are you sure you want to change its assignment?" ),
495 KeyNameFromKeyCode( aKey ),
496 conflictingAction->GetFriendlyName(),
497 HOTKEY_STORE::GetSectionName( conflictingAction ) );
498
499 wxMessageDialog dlg( GetParent(), msg, _( "Confirm change" ), wxYES_NO | wxNO_DEFAULT );
500
501 if( dlg.ShowModal() == wxID_YES )
502 {
503 // Reset the other hotkey
504 conflictingHotKey->m_EditKeycode = 0;
506 return true;
507 }
508
509 return false;
510}
511
512
513WIDGET_HOTKEY_LIST::WIDGET_HOTKEY_LIST( wxWindow* aParent, HOTKEY_STORE& aHotkeyStore,
514 bool aReadOnly ) :
515 wxTreeListCtrl( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTL_SINGLE ),
516 m_hk_store( aHotkeyStore ),
517 m_readOnly( aReadOnly )
518{
519 wxString command_header = _( "Command" );
520
521 if( !m_readOnly )
522 command_header << wxS( " " ) << _( "(double-click to edit)" );
523
524 AppendColumn( command_header, 450, wxALIGN_LEFT, wxCOL_RESIZABLE | wxCOL_SORTABLE );
525 AppendColumn( _( "Hotkey" ), 120, wxALIGN_LEFT, wxCOL_RESIZABLE | wxCOL_SORTABLE );
526 AppendColumn( _( "Alternate" ), 120, wxALIGN_LEFT, wxCOL_RESIZABLE | wxCOL_SORTABLE );
527 AppendColumn( _( "Description" ), 900, wxALIGN_LEFT, wxCOL_RESIZABLE | wxCOL_SORTABLE );
528
529
530#if defined( __WXGTK__ )// && !wxCHECK_VERSION( 3, 1, 0 )
531 // Automatic column widths are broken in wxGTK 3.0.x; set min widths to ensure visibility
532 // They are also broken in wxGTK 3.1.4
533
534 wxDataViewCtrl* dv = GetDataView();
535
536 wxString longKey = wxT( "Ctrl+Alt+Shift+X" );
537 int pad = 20;
538
539 dv->GetColumn( 0 )->SetMinWidth( aParent->GetTextExtent( command_header ).x * 2 + pad );
540 dv->GetColumn( 1 )->SetMinWidth( aParent->GetTextExtent( longKey ).x + pad );
541 dv->GetColumn( 2 )->SetMinWidth( aParent->GetTextExtent( longKey ).x + pad );
542 dv->GetColumn( 3 )->SetMinWidth( aParent->GetTextExtent( command_header ).x * 5 + pad );
543
544 CallAfter( [&]()
545 {
546 GetDataView()->Update();
547 } );
548#endif
549
550 std::vector<wxString> reserved_keys =
551 {
552 wxS( "Ctrl+Tab" ),
553 wxS( "Ctrl+Shift+Tab" )
554 };
555
556 for( const wxString& key : reserved_keys )
557 {
558 long code = KeyCodeFromKeyName( key );
559
560 if( code )
561 m_reservedHotkeys[code] = key;
562 else
563 wxLogWarning( wxS( "Unknown reserved keycode %s\n" ), key );
564 }
565
566 GetDataView()->SetIndent( 10 );
567
568 if( !m_readOnly )
569 {
570 // The event only apply if the widget is in editable mode
571 Bind( wxEVT_TREELIST_ITEM_ACTIVATED, &WIDGET_HOTKEY_LIST::onActivated, this );
572 Bind( wxEVT_TREELIST_ITEM_CONTEXT_MENU, &WIDGET_HOTKEY_LIST::onContextMenu, this );
573 Bind( wxEVT_MENU, &WIDGET_HOTKEY_LIST::onMenu, this );
574 }
575}
576
577
578void WIDGET_HOTKEY_LIST::ApplyFilterString( const wxString& aFilterStr )
579{
580 updateShownItems( aFilterStr );
581}
582
583
584void WIDGET_HOTKEY_LIST::ResetAllHotkeys( bool aResetToDefault )
585{
586 Freeze();
587
588 // Reset all the hotkeys, not just the ones shown
589 // Should not need to check conflicts, as the state we're about
590 // to set to a should be consistent
591 if( aResetToDefault )
593 else
595
598
599 Thaw();
600}
601
602
604{
605 updateShownItems( "" );
607
608 return true;
609}
610
611
613{
614 wxDataViewColumn* col = GetDataView()->GetColumn( 0 );
615 col->SetWidth( wxCOL_WIDTH_AUTOSIZE );
616 col->SetWidth( col->GetWidth() );
617
618 col = GetDataView()->GetColumn( 1 );
619 col->SetWidth( wxCOL_WIDTH_AUTOSIZE );
620 col->SetWidth( col->GetWidth() );
621
622 col = GetDataView()->GetColumn( 2 );
623 col->SetWidth( wxCOL_WIDTH_AUTOSIZE );
624 col->SetWidth( col->GetWidth() );
625
626 col = GetDataView()->GetColumn( 3 );
627 col->SetWidth( wxCOL_WIDTH_AUTOSIZE );
628 col->SetWidth( col->GetWidth() );
629}
630
631
632void WIDGET_HOTKEY_LIST::updateShownItems( const wxString& aFilterStr )
633{
634 Freeze();
635 DeleteAllItems();
636
637 HOTKEY_FILTER filter( aFilterStr );
638
639 for( HOTKEY_SECTION& section: m_hk_store.GetSections() )
640 {
641 // Create parent tree item
642 wxTreeListItem parent = AppendItem( GetRootItem(), section.m_SectionName );
643
644 for( HOTKEY& hotkey: section.m_HotKeys )
645 {
646 if( filter.FilterMatches( hotkey ) )
647 {
648 wxTreeListItem item = AppendItem( parent, wxEmptyString );
649 SetItemData( item, new WIDGET_HOTKEY_CLIENT_DATA( hotkey ) );
650 }
651 }
652
653 Expand( parent );
654 }
655
657 Thaw();
658}
659
660
662{
664 return true;
665}
666
667
668long WIDGET_HOTKEY_LIST::MapKeypressToKeycode( const wxKeyEvent& aEvent )
669{
670 long key = aEvent.GetKeyCode();
671 bool is_tab = aEvent.IsKeyInCategory( WXK_CATEGORY_TAB );
672
673 if( key == WXK_ESCAPE )
674 {
675 return 0;
676 }
677 else
678 {
679 if( key >= 'a' && key <= 'z' ) // convert to uppercase
680 key = key + ('A' - 'a');
681
682 // Remap Ctrl A (=1+GR_KB_CTRL) to Ctrl Z(=26+GR_KB_CTRL)
683 // to GR_KB_CTRL+'A' .. GR_KB_CTRL+'Z'
684 if( !is_tab && aEvent.ControlDown() && key >= WXK_CONTROL_A && key <= WXK_CONTROL_Z )
685 key += 'A' - 1;
686
687 /* Disallow shift for keys that have two keycodes on them (e.g. number and
688 * punctuation keys) leaving only the "letter keys" of A-Z, tab and space
689 * Then, you can have, e.g. Ctrl-5 and Ctrl-% (GB layout)
690 * and Ctrl-( and Ctrl-5 (FR layout).
691 * Otherwise, you'd have to have to say Ctrl-Shift-5 on a FR layout
692 */
693 bool keyIsLetter = key >= 'A' && key <= 'Z';
694
695 if( aEvent.ShiftDown() && ( keyIsLetter || key > 256 || key == 9 || key == 32 ) )
696 key |= MD_SHIFT;
697
698 if( aEvent.ControlDown() )
699 key |= MD_CTRL;
700
701 if( aEvent.AltDown() )
702 key |= MD_ALT;
703
704 return key;
705 }
706}
const char * name
Definition: DXF_plotter.cpp:57
Dialog helper object to sit in the inheritance tree between wxDialog and any class written by wxFormB...
Definition: dialog_shim.h:83
void SetInitialFocus(wxWindow *aWindow)
Sets the window (usually a wxTextCtrl) that should be focused when the dialog is shown.
Definition: dialog_shim.h:97
Dialog to prompt the user to enter a key.
static std::optional< long > PromptForKey(wxWindow *aParent, const wxString &aName, const wxString &aCurrentKey)
void OnCharHook(wxKeyEvent &aEvent) override
HK_PROMPT_DIALOG(wxWindow *aParent, wxWindowID aId, const wxString &aTitle, const wxString &aName, const wxString &aCurrentKey)
void OnKeyUp(wxKeyEvent &aEvent)
void onResetButton(wxCommandEvent &aEvent)
void OnChar(wxKeyEvent &aEvent)
Manage logic for filtering hotkeys based on user input.
wxString m_normalised_filter_str
bool FilterMatches(const HOTKEY &aHotkey) const
Check if the filter matches the given hotkey.
HOTKEY_FILTER(const wxString &aFilterStr)
A class that contains a set of hotkeys, arranged into "sections" and provides some book-keeping funct...
Definition: hotkey_store.h:66
bool CheckKeyConflicts(TOOL_ACTION *aAction, long aKey, HOTKEY **aConflict)
Check whether the given key conflicts with anything in this store.
void ResetAllHotkeysToOriginal()
Resets every hotkey to the original values.
void ResetAllHotkeysToDefault()
Reset every hotkey in the store to the default values.
std::vector< HOTKEY_SECTION > & GetSections()
Get the list of sections managed by this store.
void SaveAllHotkeys()
Persist all changes to hotkeys in the store to the underlying data structures.
static wxString GetSectionName(TOOL_ACTION *aAction)
Represent a single user action.
Definition: tool_action.h:269
wxString GetFriendlyName() const
Return the translated user-friendly name of the action.
Store the hotkey change data associated with each row.
WIDGET_HOTKEY_CLIENT_DATA(HOTKEY &aChangedHotkey)
WIDGET_HOTKEY_LIST(wxWindow *aParent, HOTKEY_STORE &aHotkeyStore, bool aReadOnly)
Constructor WIDGET_HOTKEY_LIST Create a WIDGET_HOTKEY_LIST.
void ResetAllHotkeys(bool aResetToDefault)
Set hotkeys in the control to default or original values.
void onActivated(wxTreeListEvent &aEvent)
Method onActivated Handle activation of a row.
void updateShownItems(const wxString &aFilterStr)
Method updateShownItems.
void changeHotkey(HOTKEY &aHotkey, long aKey, bool alternate)
Attempt to change the given hotkey to the given key code.
void editItem(wxTreeListItem aItem, int aEditId)
Method editItem Prompt the user for a new hotkey given a list item.
bool TransferDataToControl()
Method TransferDataToControl Load the hotkey data from the store into the control.
void onMenu(wxCommandEvent &aEvent)
Method onMenu Handle activation of a context menu item.
wxTreeListItem m_context_menu_item
bool TransferDataFromControl()
Method TransferDataFromControl Save the hotkey data from the control.
void updateColumnWidths()
Recalculates column widths after model has changed.
void ApplyFilterString(const wxString &aFilterStr)
Method ApplyFilterString Apply a filter string to the hotkey list, selecting which hotkeys to show.
void updateFromClientData()
Method updateFromClientData Refresh the visible text on the widget from the rows' client data objects...
void resetItem(wxTreeListItem aItem, int aResetId)
Method resetItem Reset the item to either the default, the value when the dialog was opened,...
WIDGET_HOTKEY_CLIENT_DATA * getExpectedHkClientData(wxTreeListItem aItem)
Get the WIDGET_HOTKEY_CLIENT_DATA form an item and assert if it isn't found.
WIDGET_HOTKEY_CLIENT_DATA * getHKClientData(wxTreeListItem aItem)
Method getHKClientData Return the WIDGET_HOTKEY_CLIENT_DATA for the given item, or NULL if the item i...
void onContextMenu(wxTreeListEvent &aEvent)
Method onContextMenu Handle right-click on a row.
static long MapKeypressToKeycode(const wxKeyEvent &aEvent)
Static method MapKeypressToKeycode Map a keypress event to the correct key code for use as a hotkey.
HOTKEY_STORE & m_hk_store
std::unordered_map< long, wxString > m_reservedHotkeys
bool resolveKeyConflicts(TOOL_ACTION *aAction, long aKey)
Method resolveKeyConflicts Check if we can set a hotkey, and prompt the user if there is a conflict b...
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:305
This file is part of the common library.
#define _(s)
wxString KeyNameFromKeyCode(int aKeycode, bool *aIsFound)
Return the key name from the key code.
int KeyCodeFromKeyName(const wxString &keyname)
Return the key code from its user-friendly key name (ie: "Ctrl+M").
int m_EditKeycodeAlt
Definition: hotkey_store.h:38
int m_EditKeycode
Definition: hotkey_store.h:37
std::vector< TOOL_ACTION * > m_Actions
Definition: hotkey_store.h:36
@ MD_ALT
Definition: tool_event.h:144
@ MD_CTRL
Definition: tool_event.h:143
@ MD_SHIFT
Definition: tool_event.h:142
ID_WHKL_MENU_IDS
Menu IDs for the hotkey context menu.
@ ID_EDIT_ALT
@ ID_CLEAR_ALT
@ ID_EDIT_HOTKEY
@ ID_DEFAULT
@ ID_CLEAR
@ ID_RESET