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 The 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/panel.h>
38#include <wx/sizer.h>
39#include <wx/statline.h>
40#include <wx/stattext.h>
41#include <wx/treelist.h>
42#include <wx/wupdlock.h>
43
56
57
64class WIDGET_HOTKEY_CLIENT_DATA : public wxClientData
65{
67
68public:
69 WIDGET_HOTKEY_CLIENT_DATA( HOTKEY& aChangedHotkey ) :
70 m_changed_hotkey( aChangedHotkey )
71 {}
72
74};
75
76
81{
82public:
83 HK_PROMPT_DIALOG( wxWindow* aParent, wxWindowID aId, const wxString& aTitle,
84 const wxString& aName, const wxString& aCurrentKey ) :
85 DIALOG_SHIM( aParent, aId, aTitle, wxDefaultPosition, wxDefaultSize )
86 {
87 wxBoxSizer* mainSizer = new wxBoxSizer( wxVERTICAL );
88 SetSizer( mainSizer );
89
90 /* Dialog layout:
91 *
92 * inst_label........................
93 * ----------------------------------
94 *
95 * cmd_label_0 cmd_label_1 \
96 * | fgsizer
97 * key_label_0 key_label_1 /
98 */
99
100 wxStaticText* inst_label = new wxStaticText( this, wxID_ANY, wxEmptyString,
101 wxDefaultPosition, wxDefaultSize,
102 wxALIGN_CENTRE_HORIZONTAL );
103
104 inst_label->SetLabelText( _( "Press a new hotkey, or press Esc to cancel..." ) );
105 mainSizer->Add( inst_label, 0, wxALL, 10 );
106
107 mainSizer->Add( new wxStaticLine( this ), 0, wxALL | wxEXPAND, 2 );
108
109 wxPanel* panelDisplayCurrent = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize );
110 mainSizer->Add( panelDisplayCurrent, 0, wxALL | wxEXPAND, 5 );
111
112 wxFlexGridSizer* fgsizer = new wxFlexGridSizer( 2 );
113 panelDisplayCurrent->SetSizer( fgsizer );
114
115 wxStaticText* cmd_label_0 = new wxStaticText( panelDisplayCurrent, wxID_ANY, _( "Command:" ) );
116 fgsizer->Add( cmd_label_0, 0, wxALL | wxALIGN_CENTRE_VERTICAL, 5 );
117
118 wxStaticText* cmd_label_1 = new wxStaticText( panelDisplayCurrent, wxID_ANY, wxEmptyString );
119 cmd_label_1->SetFont( cmd_label_1->GetFont().Bold() );
120 cmd_label_1->SetLabel( aName );
121 fgsizer->Add( cmd_label_1, 0, wxALL | wxALIGN_CENTRE_VERTICAL, 5 );
122
123 wxStaticText* key_label_0 = new wxStaticText( panelDisplayCurrent, wxID_ANY, _( "Current key:" ) );
124 fgsizer->Add( key_label_0, 0, wxALL | wxALIGN_CENTRE_VERTICAL, 5 );
125
126 wxStaticText* key_label_1 = new wxStaticText( panelDisplayCurrent, wxID_ANY, wxEmptyString );
127 key_label_1->SetFont( key_label_1->GetFont().Bold() );
128 key_label_1->SetLabel( aCurrentKey );
129 fgsizer->Add( key_label_1, 0, wxALL | wxALIGN_CENTRE_VERTICAL, 5 );
130
131 fgsizer->AddStretchSpacer();
132
133 wxButton* resetButton = new wxButton( this, wxID_ANY, _( "Clear assigned hotkey" ) );
134
135 mainSizer->Add( resetButton, 0, wxALL | wxALIGN_CENTRE_HORIZONTAL, 5 );
136
137 Layout();
138 mainSizer->Fit( this );
139 Center();
140
141 SetMinClientSize( GetClientSize() );
142
143 // Binding both EVT_CHAR and EVT_CHAR_HOOK ensures that all key events, including
144 // specials like Tab and Return, are received, particularly on MSW.
145 panelDisplayCurrent->Bind( wxEVT_CHAR, &HK_PROMPT_DIALOG::OnChar, this );
146 panelDisplayCurrent->Bind( wxEVT_CHAR_HOOK, &HK_PROMPT_DIALOG::OnCharHook, this );
147 panelDisplayCurrent->Bind( wxEVT_KEY_UP, &HK_PROMPT_DIALOG::OnKeyUp, this );
148
149 resetButton->Bind( wxEVT_COMMAND_BUTTON_CLICKED, &HK_PROMPT_DIALOG::onResetButton, this );
150
151 SetInitialFocus( panelDisplayCurrent );
152 }
153
154 static std::optional<long> PromptForKey( wxWindow* aParent, const wxString& aName,
155 const wxString& aCurrentKey )
156 {
157 HK_PROMPT_DIALOG dialog( aParent, wxID_ANY, _( "Set Hotkey" ), aName, aCurrentKey );
158
159 if( dialog.ShowModal() == wxID_OK )
160 {
161 if( dialog.m_resetkey )
162 {
163 return std::make_optional( 0 );
164 }
165 else
166 {
168
169 if( key )
170 return std::make_optional( key );
171 else // The ESC key was used to close the dialog
172 return std::nullopt;
173 }
174 }
175 else
176 {
177 return std::nullopt;
178 }
179 }
180
181protected:
182 void OnCharHook( wxKeyEvent& aEvent ) override
183 {
184 // On certain platforms, EVT_CHAR_HOOK is the only handler that receives certain
185 // "special" keys. However, it doesn't always receive "normal" keys correctly. For
186 // example, with a US keyboard, it sees ? as shift+/.
187 //
188 // Untangling these incorrect keys would be too much trouble, so we bind both events,
189 // and simply skip the EVT_CHAR_HOOK if it receives a "normal" key.
190
191 const enum wxKeyCode skipped_keys[] =
192 {
193 WXK_NONE, WXK_SHIFT, WXK_ALT, WXK_CONTROL, WXK_CAPITAL, WXK_NUMLOCK, WXK_SCROLL,
194 WXK_RAW_CONTROL
195 };
196
197 int key = aEvent.GetKeyCode();
198
199 for( wxKeyCode skipped_key : skipped_keys )
200 {
201 if( key == skipped_key )
202 return;
203 }
204
205 if( key <= 255 && isprint( key ) && !isspace( key ) )
206 {
207 // Let EVT_CHAR handle this one
208 aEvent.DoAllowNextEvent();
209
210#ifdef __WXMSW__
211 // On Windows, looks like a OnChar event is not generated when
212 // using the Alt key modifier. So ensure m_event is up to date
213 // to handle the right key code when releasing the key and therefore
214 // closing the dialog
215 m_event = aEvent;
216#endif
217 aEvent.Skip();
218 }
219 else
220 {
221 OnChar( aEvent );
222 }
223 }
224
225 void OnChar( wxKeyEvent& aEvent )
226 {
227 m_event = aEvent;
228 }
229
230 void OnKeyUp( wxKeyEvent& aEvent )
231 {
232 // If dialog opened using Enter key, prevent closing when releasing Enter.
233 if( m_event.GetEventType() != wxEVT_NULL )
234 {
236 wxPostEvent( this, wxCommandEvent( wxEVT_COMMAND_BUTTON_CLICKED, wxID_OK ) );
237 }
238 }
239
240 void onResetButton( wxCommandEvent& aEvent )
241 {
242 m_resetkey = true;
243 wxPostEvent( this, wxCommandEvent( wxEVT_COMMAND_BUTTON_CLICKED, wxID_OK ) );
244 }
245
246private:
247 bool m_resetkey = false;
248 wxKeyEvent m_event;
249};
250
251
256{
257public:
258 HOTKEY_FILTER( const wxString& aFilterStr )
259 {
260 m_normalised_filter_str = aFilterStr.Upper();
261 m_valid = m_normalised_filter_str.size() > 0;
262 }
263
269 bool FilterMatches( const HOTKEY& aHotkey ) const
270 {
271 if( !m_valid )
272 return true;
273
274 // Match in the (translated) filter string
275 const auto normedInfo = wxGetTranslation( aHotkey.m_Actions[0]->GetFriendlyName() ).Upper();
276
277 if( normedInfo.Contains( m_normalised_filter_str ) )
278 return true;
279
280 const wxString keyName = KeyNameFromKeyCode( aHotkey.m_EditKeycode );
281
282 if( keyName.Upper().Contains( m_normalised_filter_str ) )
283 return true;
284
285 return false;
286 }
287
288private:
291};
292
293
295{
296 if( aItem.IsOk() )
297 {
298 wxClientData* data = GetItemData( aItem );
299
300 if( data )
301 return static_cast<WIDGET_HOTKEY_CLIENT_DATA*>( data );
302 }
303
304 return nullptr;
305}
306
307
309{
310 for( wxTreeListItem i = GetFirstItem(); i.IsOk(); i = GetNextItem( i ) )
311 {
313
314 if( hkdata )
315 {
316 const HOTKEY& changed_hk = hkdata->GetChangedHotkey();
317 wxString label = changed_hk.m_Actions[ 0 ]->GetFriendlyName();
318 wxString key_text = KeyNameFromKeyCode( changed_hk.m_EditKeycode );
319 wxString alt_text = KeyNameFromKeyCode( changed_hk.m_EditKeycodeAlt );
320 wxString description = changed_hk.m_Actions[ 0 ]->GetDescription();
321
322 if( label.IsEmpty() )
323 label = changed_hk.m_Actions[ 0 ]->GetName();
324
325 label.Replace( wxT( "..." ), wxEmptyString );
326 label.Replace( wxT( "…" ), wxEmptyString );
327
328 // mark unsaved changes
329 if( changed_hk.m_EditKeycode != changed_hk.m_Actions[ 0 ]->GetHotKey() )
330 label += wxS( " *" );
331
332 description.Replace( wxS( "\n" ), wxS( " " ) );
333 description.Replace( wxS( "\r" ), wxS( " " ) );
334
335 SetItemText( i, 0, label );
336 SetItemText( i, 1, key_text );
337 SetItemText( i, 2, alt_text );
338 SetItemText( i, 3, description );
339 }
340 }
341}
342
343
344void WIDGET_HOTKEY_LIST::changeHotkey( HOTKEY& aHotkey, long aKey, bool alternate )
345{
346 // See if this key code is handled in hotkeys names list
347 bool exists;
348 KeyNameFromKeyCode( aKey, &exists );
349
350 if( exists && aHotkey.m_EditKeycode != aKey )
351 {
352 if( aKey == 0 || resolveKeyConflicts( aHotkey.m_Actions[ 0 ], aKey ) )
353 {
354 if( alternate )
355 aHotkey.m_EditKeycodeAlt = aKey;
356 else
357 aHotkey.m_EditKeycode = aKey;
358 }
359 }
360}
361
362
363void WIDGET_HOTKEY_LIST::editItem( wxTreeListItem aItem, int aEditId )
364{
366
367 if( !hkdata )
368 return;
369
370 wxString name = GetItemText( aItem, 0 );
371 wxString current_key = aEditId == ID_EDIT_HOTKEY ? GetItemText( aItem, 1 )
372 : GetItemText( aItem, 2 );
373
374 std::optional<long> key = HK_PROMPT_DIALOG::PromptForKey( this, name, current_key );
375
376 // An empty optional means don't change the key
377 if( key.has_value() )
378 {
379 auto it = m_reservedHotkeys.find( key.value() );
380
381 if( it != m_reservedHotkeys.end() )
382 {
383 wxString msg = wxString::Format( _( "'%s' is a reserved hotkey in KiCad and cannot "
384 "be assigned." ),
385 it->second );
386
387 DisplayErrorMessage( this, msg );
388 return;
389 }
390
391 changeHotkey( hkdata->GetChangedHotkey(), key.value(), aEditId == ID_EDIT_ALT );
393 }
394}
395
396
397void WIDGET_HOTKEY_LIST::resetItem( wxTreeListItem aItem, int aResetId )
398{
400
401 if( !hkdata )
402 return;
403
404 HOTKEY& changed_hk = hkdata->GetChangedHotkey();
405
406 if( aResetId == ID_RESET )
407 {
408 changeHotkey( changed_hk, changed_hk.m_Actions[0]->GetHotKey(), false );
409 changeHotkey( changed_hk, changed_hk.m_Actions[0]->GetHotKey(), true );
410 }
411 else if( aResetId == ID_CLEAR )
412 {
413 changeHotkey( changed_hk, 0, false );
414 }
415 else if( aResetId == ID_CLEAR_ALT )
416 {
417 changeHotkey( changed_hk, 0, true );
418 }
419 else if( aResetId == ID_DEFAULT )
420 {
421 changeHotkey( changed_hk, changed_hk.m_Actions[0]->GetDefaultHotKey(), false );
422 changeHotkey( changed_hk, changed_hk.m_Actions[0]->GetDefaultHotKeyAlt(), true );
423 }
424
426}
427
428
429void WIDGET_HOTKEY_LIST::onActivated( wxTreeListEvent& aEvent )
430{
431 editItem( aEvent.GetItem(), ID_EDIT_HOTKEY );
432}
433
434
435void WIDGET_HOTKEY_LIST::onContextMenu( wxTreeListEvent& aEvent )
436{
437 // Save the active event for use in OnMenu
438 m_context_menu_item = aEvent.GetItem();
439
440 wxMenu menu;
441
443
444 // Some actions only apply if the row is hotkey data
445 if( hkdata )
446 {
447 menu.Append( ID_EDIT_HOTKEY, _( "Edit..." ) );
448 menu.Append( ID_EDIT_ALT, _( "Edit Alternate..." ) );
449 menu.Append( ID_RESET, _( "Undo Changes" ) );
450 menu.Append( ID_CLEAR, _( "Clear Assigned Hotkey" ) );
451 menu.Append( ID_CLEAR_ALT, _( "Clear Assigned Alternate" ) );
452 menu.Append( ID_DEFAULT, _( "Restore Defaults" ) );
453 menu.Append( wxID_SEPARATOR );
454
455 PopupMenu( &menu );
456 }
457}
458
459
460void WIDGET_HOTKEY_LIST::onMenu( wxCommandEvent& aEvent )
461{
462 switch( aEvent.GetId() )
463 {
464 case ID_EDIT_HOTKEY:
465 case ID_EDIT_ALT:
466 editItem( m_context_menu_item, aEvent.GetId() );
467 break;
468
469 case ID_RESET:
470 case ID_CLEAR:
471 case ID_CLEAR_ALT:
472 case ID_DEFAULT:
473 resetItem( m_context_menu_item, aEvent.GetId() );
474 break;
475
476 default:
477 wxFAIL_MSG( wxT( "Unknown ID in context menu event" ) );
478 }
479}
480
481
482void WIDGET_HOTKEY_LIST::onCharHook( wxKeyEvent& aEvent )
483{
484 // On macOS with international keyboards, "dead keys" (like backtick or circumflex on
485 // French keyboards) are used for composing accented characters. When pressed on a
486 // wxTreeListCtrl, these can trigger IME composition handling that the control doesn't
487 // support, causing a crash.
488 //
489 // Per wxWidgets documentation: if both GetUnicodeKey() and GetKeyCode() return WXK_NONE,
490 // the key has no WXK_xxx mapping (e.g., dead keys). We filter these out to prevent crashes.
491 // Navigation keys (arrows, function keys, etc.) have valid GetKeyCode() values even when
492 // GetUnicodeKey() returns WXK_NONE, so they pass through correctly.
493 if( aEvent.GetUnicodeKey() == WXK_NONE && aEvent.GetKeyCode() == WXK_NONE )
494 return;
495
496 aEvent.Skip();
497}
498
499
501{
502 HOTKEY* conflictingHotKey = nullptr;
503
504 m_hk_store.CheckKeyConflicts( aAction, aKey, &conflictingHotKey );
505
506 if( !conflictingHotKey )
507 return true;
508
509 TOOL_ACTION* conflictingAction = conflictingHotKey->m_Actions[ 0 ];
510 wxString msg = wxString::Format( _( "'%s' is already assigned to '%s' in section '%s'. "
511 "Are you sure you want to change its assignment?" ),
512 KeyNameFromKeyCode( aKey ),
513 conflictingAction->GetFriendlyName(),
514 HOTKEY_STORE::GetSectionName( conflictingAction ) );
515
516 KICAD_MESSAGE_DIALOG dlg( GetParent(), msg, _( "Confirm change" ), wxYES_NO | wxNO_DEFAULT );
517
518 if( dlg.ShowModal() == wxID_YES )
519 {
520 // Reset the other hotkey
521 conflictingHotKey->m_EditKeycode = 0;
523 return true;
524 }
525
526 return false;
527}
528
529
530WIDGET_HOTKEY_LIST::WIDGET_HOTKEY_LIST( wxWindow* aParent, HOTKEY_STORE& aHotkeyStore, bool readOnly ) :
531 wxTreeListCtrl( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTL_SINGLE ),
532 m_hk_store( aHotkeyStore )
533{
534 wxString command_header;
535
536 if( readOnly )
537 command_header = _( "Command" );
538 else
539 command_header = _( "Command (double-click to edit)" );
540
541 AppendColumn( command_header, 450, wxALIGN_LEFT, wxCOL_RESIZABLE | wxCOL_SORTABLE );
542 AppendColumn( _( "Hotkey" ), 120, wxALIGN_LEFT, wxCOL_RESIZABLE | wxCOL_SORTABLE );
543 AppendColumn( _( "Alternate" ), 120, wxALIGN_LEFT, wxCOL_RESIZABLE | wxCOL_SORTABLE );
544 AppendColumn( _( "Description" ), 900, wxALIGN_LEFT, wxCOL_RESIZABLE | wxCOL_SORTABLE );
545
546
547#if defined( __WXGTK__ )// && !wxCHECK_VERSION( 3, 1, 0 )
548 // Automatic column widths are broken in wxGTK 3.0.x; set min widths to ensure visibility
549 // They are also broken in wxGTK 3.1.4
550
551 wxDataViewCtrl* dv = GetDataView();
552
553 wxString longKey = wxT( "Ctrl+Alt+Shift+X" );
554 int pad = 20;
555
556 dv->GetColumn( 0 )->SetMinWidth( aParent->GetTextExtent( command_header ).x * 2 + pad );
557 dv->GetColumn( 1 )->SetMinWidth( aParent->GetTextExtent( longKey ).x + pad );
558 dv->GetColumn( 2 )->SetMinWidth( aParent->GetTextExtent( longKey ).x + pad );
559 dv->GetColumn( 3 )->SetMinWidth( aParent->GetTextExtent( command_header ).x * 5 + pad );
560
561 CallAfter( [this]()
562 {
563 GetDataView()->Update();
564 } );
565#endif
566
567 std::vector<wxString> reserved_keys =
568 {
569 wxS( "Ctrl+Tab" ),
570 wxS( "Ctrl+Shift+Tab" )
571 };
572
573 for( const wxString& key : reserved_keys )
574 {
575 long code = KeyCodeFromKeyName( key );
576
577 if( code )
578 m_reservedHotkeys[code] = key;
579 else
580 wxLogWarning( wxS( "Unknown reserved keycode %s\n" ), key );
581 }
582
583 GetDataView()->SetIndent( 10 );
584
585 // The event only apply if the widget is in editable mode
586 if( !readOnly )
587 {
588 Bind( wxEVT_TREELIST_ITEM_ACTIVATED, &WIDGET_HOTKEY_LIST::onActivated, this );
589 Bind( wxEVT_TREELIST_ITEM_CONTEXT_MENU, &WIDGET_HOTKEY_LIST::onContextMenu, this );
590 Bind( wxEVT_MENU, &WIDGET_HOTKEY_LIST::onMenu, this );
591 }
592
593 // Filter out dead keys that can crash on macOS with international keyboards
594 Bind( wxEVT_CHAR_HOOK, &WIDGET_HOTKEY_LIST::onCharHook, this );
595}
596
597
598void WIDGET_HOTKEY_LIST::ApplyFilterString( const wxString& aFilterStr )
599{
600 updateShownItems( aFilterStr );
601}
602
603
604void WIDGET_HOTKEY_LIST::ResetAllHotkeys( bool aResetToDefault )
605{
606 wxWindowUpdateLocker updateLock( this );
607
608 // Reset all the hotkeys, not just the ones shown
609 // Should not need to check conflicts, as the state we're about
610 // to set to a should be consistent
611 if( aResetToDefault )
612 m_hk_store.ResetAllHotkeysToDefault();
613 else
614 m_hk_store.ResetAllHotkeysToOriginal();
615
618}
619
620
622{
623 updateShownItems( "" );
625
626 return true;
627}
628
629
631{
632 wxDataViewColumn* col = GetDataView()->GetColumn( 0 );
633 col->SetWidth( wxCOL_WIDTH_AUTOSIZE );
634 col->SetWidth( col->GetWidth() );
635
636 col = GetDataView()->GetColumn( 1 );
637 col->SetWidth( wxCOL_WIDTH_AUTOSIZE );
638 col->SetWidth( col->GetWidth() );
639
640 col = GetDataView()->GetColumn( 2 );
641 col->SetWidth( wxCOL_WIDTH_AUTOSIZE );
642 col->SetWidth( col->GetWidth() );
643
644 col = GetDataView()->GetColumn( 3 );
645 col->SetWidth( wxCOL_WIDTH_AUTOSIZE );
646 col->SetWidth( col->GetWidth() );
647}
648
649
650void WIDGET_HOTKEY_LIST::updateShownItems( const wxString& aFilterStr )
651{
652 wxWindowUpdateLocker updateLock( this );
653
654 DeleteAllItems();
655
656 HOTKEY_FILTER filter( aFilterStr );
657
658 for( HOTKEY_SECTION& section: m_hk_store.GetSections() )
659 {
660 // Create parent tree item
661 wxTreeListItem parent = AppendItem( GetRootItem(), section.m_SectionName );
662
663 for( HOTKEY& hotkey: section.m_HotKeys )
664 {
665 if( filter.FilterMatches( hotkey ) )
666 {
667 wxTreeListItem item = AppendItem( parent, wxEmptyString );
668 SetItemData( item, new WIDGET_HOTKEY_CLIENT_DATA( hotkey ) );
669 }
670 }
671
672 Expand( parent );
673 }
674
676}
677
678
680{
681 m_hk_store.SaveAllHotkeys();
682 return true;
683}
684
685
686long WIDGET_HOTKEY_LIST::MapKeypressToKeycode( const wxKeyEvent& aEvent )
687{
688 long key = aEvent.GetKeyCode();
689 bool is_tab = aEvent.IsKeyInCategory( WXK_CATEGORY_TAB );
690
691 if( key == WXK_ESCAPE )
692 {
693 return 0;
694 }
695 else
696 {
697 if( key >= 'a' && key <= 'z' ) // convert to uppercase
698 key = key + ('A' - 'a');
699
700 // Remap Ctrl A (=1+GR_KB_CTRL) to Ctrl Z(=26+GR_KB_CTRL)
701 // to GR_KB_CTRL+'A' .. GR_KB_CTRL+'Z'
702 if( !is_tab && aEvent.ControlDown() && key >= WXK_CONTROL_A && key <= WXK_CONTROL_Z )
703 key += 'A' - 1;
704
705 /* Disallow shift for keys that have two keycodes on them (e.g. number and
706 * punctuation keys) leaving only the "letter keys" of A-Z, tab and space
707 * Then, you can have, e.g. Ctrl-5 and Ctrl-% (GB layout)
708 * and Ctrl-( and Ctrl-5 (FR layout).
709 * Otherwise, you'd have to have to say Ctrl-Shift-5 on a FR layout
710 */
711 bool keyIsLetter = key >= 'A' && key <= 'Z';
712
713 int mods = aEvent.GetModifiers();
714
715 if( ( mods & wxMOD_SHIFT ) && ( keyIsLetter || key > 256 || key == 9 || key == 32 ) )
716 key |= MD_SHIFT;
717
718 // the flag wxMOD_ALTGR is defined in wxWidgets as wxMOD_CONTROL|wxMOD_ALT
719 // So AltGr key cannot used as modifier key because it is the same as Alt key + Ctrl key.
720 #if CAN_USE_ALTGR_KEY
721 if( wxmods & wxMOD_ALTGR )
722 mods |= MD_ALTGR;
723 else
724 #endif
725 {
726 if( mods & wxMOD_CONTROL )
727 key |= MD_CTRL;
728
729 if( mods & wxMOD_ALT )
730 key |= MD_ALT;
731 }
732
733#ifdef wxMOD_META
734 if( mods & wxMOD_META )
735 key |= MD_META;
736#endif
737
738#ifdef wxMOD_WIN
739 if( mods & wxMOD_WIN )
740 key |= MD_SUPER;
741#endif
742
743 return key;
744 }
745}
const char * name
void SetInitialFocus(wxWindow *aWindow)
Sets the window (usually a wxTextCtrl) that should be focused when the dialog is shown.
Definition dialog_shim.h:82
DIALOG_SHIM(wxWindow *aParent, wxWindowID id, const wxString &title, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=wxDEFAULT_FRAME_STYLE|wxRESIZE_BORDER, const wxString &name=wxDialogNameStr)
int ShowModal() override
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...
static wxString GetSectionName(TOOL_ACTION *aAction)
Represent a single user action.
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)
void ResetAllHotkeys(bool aResetToDefault)
Set hotkeys in the control to default or original values.
void onActivated(wxTreeListEvent &aEvent)
Handle activation of a row.
void updateShownItems(const wxString &aFilterStr)
Update the items shown in the widget based on a given filter string.
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)
Prompt the user for a new hotkey given a list item.
bool TransferDataToControl()
Load the hotkey data from the store into the control.
void onMenu(wxCommandEvent &aEvent)
Handle activation of a context menu item.
wxTreeListItem m_context_menu_item
bool TransferDataFromControl()
Save the hotkey data from the control.
void updateColumnWidths()
Recalculate column widths after model has changed.
void ApplyFilterString(const wxString &aFilterStr)
Apply a filter string to the hotkey list, selecting which hotkeys to show.
void updateFromClientData()
Refresh the visible text on the widget from the rows' client data objects.
void resetItem(wxTreeListItem aItem, int aResetId)
Reset the item to either the default, the value when the dialog was opened, or none.
WIDGET_HOTKEY_CLIENT_DATA * getHKClientData(wxTreeListItem aItem)
Return the WIDGET_HOTKEY_CLIENT_DATA for the given item, or NULL if the item is invalid.
void onContextMenu(wxTreeListEvent &aEvent)
Handle right-click on a row.
static long MapKeypressToKeycode(const wxKeyEvent &aEvent)
Map a keypress event to the correct key code for use as a hotkey.
WIDGET_HOTKEY_LIST(wxWindow *aParent, HOTKEY_STORE &aHotkeyStore, bool readOnly)
Create a WIDGET_HOTKEY_LIST.
void onCharHook(wxKeyEvent &aEvent)
Handle key events to filter out dead keys that can crash on macOS.
HOTKEY_STORE & m_hk_store
std::unordered_map< long, wxString > m_reservedHotkeys
bool resolveKeyConflicts(TOOL_ACTION *aAction, long aKey)
Check if we can set a hotkey, and prompt the user if there is a conflict between keys.
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:202
This file is part of the common library.
#define KICAD_MESSAGE_DIALOG
Definition confirm.h:52
#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
int m_EditKeycode
std::vector< TOOL_ACTION * > m_Actions
@ MD_META
Definition tool_event.h:147
@ MD_ALT
Definition tool_event.h:145
@ MD_CTRL
Definition tool_event.h:144
@ MD_SUPER
Definition tool_event.h:146
@ MD_ALTGR
Definition tool_event.h:148
@ MD_SHIFT
Definition tool_event.h:143
ID_WHKL_MENU_IDS
Menu IDs for the hotkey context menu.
@ ID_EDIT_ALT
@ ID_CLEAR_ALT
@ ID_EDIT_HOTKEY