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 ) :
531 wxTreeListCtrl( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTL_SINGLE ),
532 m_hk_store( aHotkeyStore )
533{
534 wxString command_header = _( "Command (double-click to edit)" );
535
536 AppendColumn( command_header, 450, wxALIGN_LEFT, wxCOL_RESIZABLE | wxCOL_SORTABLE );
537 AppendColumn( _( "Hotkey" ), 120, wxALIGN_LEFT, wxCOL_RESIZABLE | wxCOL_SORTABLE );
538 AppendColumn( _( "Alternate" ), 120, wxALIGN_LEFT, wxCOL_RESIZABLE | wxCOL_SORTABLE );
539 AppendColumn( _( "Description" ), 900, wxALIGN_LEFT, wxCOL_RESIZABLE | wxCOL_SORTABLE );
540
541
542#if defined( __WXGTK__ )// && !wxCHECK_VERSION( 3, 1, 0 )
543 // Automatic column widths are broken in wxGTK 3.0.x; set min widths to ensure visibility
544 // They are also broken in wxGTK 3.1.4
545
546 wxDataViewCtrl* dv = GetDataView();
547
548 wxString longKey = wxT( "Ctrl+Alt+Shift+X" );
549 int pad = 20;
550
551 dv->GetColumn( 0 )->SetMinWidth( aParent->GetTextExtent( command_header ).x * 2 + pad );
552 dv->GetColumn( 1 )->SetMinWidth( aParent->GetTextExtent( longKey ).x + pad );
553 dv->GetColumn( 2 )->SetMinWidth( aParent->GetTextExtent( longKey ).x + pad );
554 dv->GetColumn( 3 )->SetMinWidth( aParent->GetTextExtent( command_header ).x * 5 + pad );
555
556 CallAfter( [this]()
557 {
558 GetDataView()->Update();
559 } );
560#endif
561
562 std::vector<wxString> reserved_keys =
563 {
564 wxS( "Ctrl+Tab" ),
565 wxS( "Ctrl+Shift+Tab" )
566 };
567
568 for( const wxString& key : reserved_keys )
569 {
570 long code = KeyCodeFromKeyName( key );
571
572 if( code )
573 m_reservedHotkeys[code] = key;
574 else
575 wxLogWarning( wxS( "Unknown reserved keycode %s\n" ), key );
576 }
577
578 GetDataView()->SetIndent( 10 );
579
580 // The event only apply if the widget is in editable mode
581 Bind( wxEVT_TREELIST_ITEM_ACTIVATED, &WIDGET_HOTKEY_LIST::onActivated, this );
582 Bind( wxEVT_TREELIST_ITEM_CONTEXT_MENU, &WIDGET_HOTKEY_LIST::onContextMenu, this );
583 Bind( wxEVT_MENU, &WIDGET_HOTKEY_LIST::onMenu, this );
584
585 // Filter out dead keys that can crash on macOS with international keyboards
586 Bind( wxEVT_CHAR_HOOK, &WIDGET_HOTKEY_LIST::onCharHook, this );
587}
588
589
590void WIDGET_HOTKEY_LIST::ApplyFilterString( const wxString& aFilterStr )
591{
592 updateShownItems( aFilterStr );
593}
594
595
596void WIDGET_HOTKEY_LIST::ResetAllHotkeys( bool aResetToDefault )
597{
598 wxWindowUpdateLocker updateLock( this );
599
600 // Reset all the hotkeys, not just the ones shown
601 // Should not need to check conflicts, as the state we're about
602 // to set to a should be consistent
603 if( aResetToDefault )
604 m_hk_store.ResetAllHotkeysToDefault();
605 else
606 m_hk_store.ResetAllHotkeysToOriginal();
607
610}
611
612
614{
615 updateShownItems( "" );
617
618 return true;
619}
620
621
623{
624 wxDataViewColumn* col = GetDataView()->GetColumn( 0 );
625 col->SetWidth( wxCOL_WIDTH_AUTOSIZE );
626 col->SetWidth( col->GetWidth() );
627
628 col = GetDataView()->GetColumn( 1 );
629 col->SetWidth( wxCOL_WIDTH_AUTOSIZE );
630 col->SetWidth( col->GetWidth() );
631
632 col = GetDataView()->GetColumn( 2 );
633 col->SetWidth( wxCOL_WIDTH_AUTOSIZE );
634 col->SetWidth( col->GetWidth() );
635
636 col = GetDataView()->GetColumn( 3 );
637 col->SetWidth( wxCOL_WIDTH_AUTOSIZE );
638 col->SetWidth( col->GetWidth() );
639}
640
641
642void WIDGET_HOTKEY_LIST::updateShownItems( const wxString& aFilterStr )
643{
644 wxWindowUpdateLocker updateLock( this );
645
646 DeleteAllItems();
647
648 HOTKEY_FILTER filter( aFilterStr );
649
650 for( HOTKEY_SECTION& section: m_hk_store.GetSections() )
651 {
652 // Create parent tree item
653 wxTreeListItem parent = AppendItem( GetRootItem(), section.m_SectionName );
654
655 for( HOTKEY& hotkey: section.m_HotKeys )
656 {
657 if( filter.FilterMatches( hotkey ) )
658 {
659 wxTreeListItem item = AppendItem( parent, wxEmptyString );
660 SetItemData( item, new WIDGET_HOTKEY_CLIENT_DATA( hotkey ) );
661 }
662 }
663
664 Expand( parent );
665 }
666
668}
669
670
672{
673 m_hk_store.SaveAllHotkeys();
674 return true;
675}
676
677
678long WIDGET_HOTKEY_LIST::MapKeypressToKeycode( const wxKeyEvent& aEvent )
679{
680 long key = aEvent.GetKeyCode();
681 bool is_tab = aEvent.IsKeyInCategory( WXK_CATEGORY_TAB );
682
683 if( key == WXK_ESCAPE )
684 {
685 return 0;
686 }
687 else
688 {
689 if( key >= 'a' && key <= 'z' ) // convert to uppercase
690 key = key + ('A' - 'a');
691
692 // Remap Ctrl A (=1+GR_KB_CTRL) to Ctrl Z(=26+GR_KB_CTRL)
693 // to GR_KB_CTRL+'A' .. GR_KB_CTRL+'Z'
694 if( !is_tab && aEvent.ControlDown() && key >= WXK_CONTROL_A && key <= WXK_CONTROL_Z )
695 key += 'A' - 1;
696
697 /* Disallow shift for keys that have two keycodes on them (e.g. number and
698 * punctuation keys) leaving only the "letter keys" of A-Z, tab and space
699 * Then, you can have, e.g. Ctrl-5 and Ctrl-% (GB layout)
700 * and Ctrl-( and Ctrl-5 (FR layout).
701 * Otherwise, you'd have to have to say Ctrl-Shift-5 on a FR layout
702 */
703 bool keyIsLetter = key >= 'A' && key <= 'Z';
704
705 int mods = aEvent.GetModifiers();
706
707 if( ( mods & wxMOD_SHIFT ) && ( keyIsLetter || key > 256 || key == 9 || key == 32 ) )
708 key |= MD_SHIFT;
709
710 // the flag wxMOD_ALTGR is defined in wxWidgets as wxMOD_CONTROL|wxMOD_ALT
711 // So AltGr key cannot used as modifier key because it is the same as Alt key + Ctrl key.
712 #if CAN_USE_ALTGR_KEY
713 if( wxmods & wxMOD_ALTGR )
714 mods |= MD_ALTGR;
715 else
716 #endif
717 {
718 if( mods & wxMOD_CONTROL )
719 key |= MD_CTRL;
720
721 if( mods & wxMOD_ALT )
722 key |= MD_ALT;
723 }
724
725#ifdef wxMOD_META
726 if( mods & wxMOD_META )
727 key |= MD_META;
728#endif
729
730#ifdef wxMOD_WIN
731 if( mods & wxMOD_WIN )
732 key |= MD_SUPER;
733#endif
734
735 return key;
736 }
737}
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.
WIDGET_HOTKEY_LIST(wxWindow *aParent, HOTKEY_STORE &aHotkeyStore)
Create a WIDGET_HOTKEY_LIST.
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.
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