You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

253 lines
8.4 KiB

  1. /*
  2. * This program source code file is part of KICAD, a free EDA CAD application.
  3. *
  4. * Copyright (C) 1992-2019 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. #include <hotkey_store.h>
  24. #include <eda_base_frame.h>
  25. #include <tool/tool_manager.h>
  26. #include <tool/action_manager.h>
  27. #include <tool/tool_event.h>
  28. #include <tool/tool_action.h>
  29. #include <advanced_config.h>
  30. class PSEUDO_ACTION : public TOOL_ACTION
  31. {
  32. public:
  33. PSEUDO_ACTION( const wxString& aLabel, int aHotKey )
  34. {
  35. m_label = aLabel;
  36. m_hotKey = aHotKey;
  37. }
  38. };
  39. static PSEUDO_ACTION* g_gesturePseudoActions[] = {
  40. new PSEUDO_ACTION( _( "Pan Left/Right" ), MD_CTRL + PSEUDO_WXK_WHEEL ),
  41. new PSEUDO_ACTION( _( "Pan Up/Down" ), MD_SHIFT + PSEUDO_WXK_WHEEL ),
  42. new PSEUDO_ACTION( _( "Finish Drawing" ), PSEUDO_WXK_DBLCLICK ),
  43. #ifdef __WXOSX_MAC__
  44. new PSEUDO_ACTION( _( "Show Clarify Selection Menu" ), MD_ALT + PSEUDO_WXK_CLICK ),
  45. new PSEUDO_ACTION( _( "Add to Selection" ), MD_SHIFT + PSEUDO_WXK_CLICK ),
  46. new PSEUDO_ACTION( _( "Toggle Selection State" ), MD_CTRL + PSEUDO_WXK_CLICK ),
  47. new PSEUDO_ACTION( _( "Remove from Selection" ), MD_SHIFT + MD_CTRL + PSEUDO_WXK_CLICK ),
  48. #else
  49. new PSEUDO_ACTION( _( "Show Clarify Selection Menu" ), MD_CTRL + PSEUDO_WXK_CLICK ),
  50. new PSEUDO_ACTION( _( "Add to Selection" ), MD_SHIFT + PSEUDO_WXK_CLICK ),
  51. new PSEUDO_ACTION( _( "Toggle Selection State" ), MD_ALT + PSEUDO_WXK_CLICK ),
  52. new PSEUDO_ACTION( _( "Remove from Selection" ), MD_SHIFT + MD_ALT + PSEUDO_WXK_CLICK ),
  53. #endif
  54. new PSEUDO_ACTION( _( "Ignore Grid Snaps" ), MD_CTRL ),
  55. new PSEUDO_ACTION( _( "Ignore Other Snaps" ), MD_SHIFT ),
  56. new PSEUDO_ACTION( _( "Ignore H/V/45 Constraints" ), MD_SHIFT )
  57. };
  58. static PSEUDO_ACTION* g_standardPlatformCommands[] = {
  59. #ifndef __WINDOWS__
  60. new PSEUDO_ACTION( _( "Close" ), MD_CTRL + 'W' ),
  61. #endif
  62. new PSEUDO_ACTION( _( "Quit" ), MD_CTRL + 'Q' )
  63. };
  64. wxString HOTKEY_STORE::GetAppName( TOOL_ACTION* aAction )
  65. {
  66. wxString name( aAction->GetName() );
  67. return name.BeforeFirst( '.' );
  68. }
  69. wxString HOTKEY_STORE::GetSectionName( TOOL_ACTION* aAction )
  70. {
  71. std::map<wxString, wxString> s_AppNames = {
  72. { wxT( "common" ), _( "Common" ) },
  73. { wxT( "kicad" ), _( "Project Manager" ) },
  74. { wxT( "eeschema" ), _( "Schematic Editor" ) },
  75. { wxT( "pcbnew" ), _( "PCB Editor" ) },
  76. { wxT( "plEditor" ), _( "Drawing Sheet Editor" ), },
  77. { wxT( "3DViewer" ), _( "3D Viewer" ) }
  78. };
  79. wxString appName = GetAppName( aAction );
  80. if( s_AppNames.count( appName ) )
  81. return s_AppNames[ appName ];
  82. else
  83. return appName;
  84. }
  85. HOTKEY_STORE::HOTKEY_STORE()
  86. {
  87. }
  88. void HOTKEY_STORE::Init( std::vector<TOOL_MANAGER*> aToolManagerList, bool aIncludeReadOnlyCmds )
  89. {
  90. m_toolManagers = std::move( aToolManagerList );
  91. // Collect all action maps into a single master map. This will re-group everything
  92. // and collect duplicates together
  93. std::map<std::string, HOTKEY> masterMap;
  94. for( TOOL_MANAGER* toolMgr : m_toolManagers )
  95. {
  96. for( const std::pair<const std::string, TOOL_ACTION*>& entry : toolMgr->GetActions() )
  97. {
  98. // Internal actions probably shouldn't be allowed hotkeys
  99. if( entry.second->GetLabel().IsEmpty() )
  100. continue;
  101. if( !ADVANCED_CFG::GetCfg().m_ExtraZoneDisplayModes )
  102. {
  103. if( entry.second->GetName() == "pcbnew.Control.zoneDisplayOutlines"
  104. || entry.second->GetName() == "pcbnew.Control.zoneDisplayTesselation" )
  105. {
  106. continue;
  107. }
  108. }
  109. HOTKEY& hotkey = masterMap[ entry.first ];
  110. hotkey.m_Actions.push_back( entry.second );
  111. hotkey.m_EditKeycode = entry.second->GetHotKey();
  112. }
  113. }
  114. wxString currentApp;
  115. HOTKEY_SECTION* currentSection = nullptr;
  116. // If a previous list was built, ensure this previous list is cleared:
  117. m_hk_sections.clear();
  118. for( const std::pair<const std::string, HOTKEY>& entry : masterMap )
  119. {
  120. TOOL_ACTION* entryAction = entry.second.m_Actions[ 0 ];
  121. wxString entryApp = GetAppName( entryAction );
  122. if( !currentSection || entryApp != currentApp )
  123. {
  124. m_hk_sections.emplace_back( HOTKEY_SECTION() );
  125. currentApp = entryApp;
  126. currentSection = &m_hk_sections.back();
  127. currentSection->m_SectionName = GetSectionName( entryAction );
  128. if( aIncludeReadOnlyCmds && currentApp == "common" )
  129. {
  130. for( TOOL_ACTION* command : g_standardPlatformCommands )
  131. currentSection->m_HotKeys.emplace_back( HOTKEY( command ) );
  132. }
  133. }
  134. currentSection->m_HotKeys.emplace_back( HOTKEY( entry.second ) );
  135. }
  136. if( aIncludeReadOnlyCmds )
  137. {
  138. m_hk_sections.emplace_back( HOTKEY_SECTION() );
  139. currentSection = &m_hk_sections.back();
  140. currentSection->m_SectionName = _( "Gestures" );
  141. for( TOOL_ACTION* gesture : g_gesturePseudoActions )
  142. currentSection->m_HotKeys.emplace_back( HOTKEY( gesture ) );
  143. }
  144. }
  145. std::vector<HOTKEY_SECTION>& HOTKEY_STORE::GetSections()
  146. {
  147. return m_hk_sections;
  148. }
  149. void HOTKEY_STORE::SaveAllHotkeys()
  150. {
  151. for( HOTKEY_SECTION& section : m_hk_sections )
  152. {
  153. for( HOTKEY& hotkey : section.m_HotKeys )
  154. {
  155. for( TOOL_ACTION* action : hotkey.m_Actions )
  156. action->SetHotKey( hotkey.m_EditKeycode );
  157. }
  158. }
  159. }
  160. void HOTKEY_STORE::ResetAllHotkeysToDefault()
  161. {
  162. for( HOTKEY_SECTION& section : m_hk_sections )
  163. {
  164. for( HOTKEY& hotkey : section.m_HotKeys )
  165. hotkey.m_EditKeycode = hotkey.m_Actions[ 0 ]->GetDefaultHotKey();
  166. }
  167. }
  168. void HOTKEY_STORE::ResetAllHotkeysToOriginal()
  169. {
  170. for( HOTKEY_SECTION& section : m_hk_sections )
  171. {
  172. for( HOTKEY& hotkey : section.m_HotKeys )
  173. hotkey.m_EditKeycode = hotkey.m_Actions[ 0 ]->GetHotKey();
  174. }
  175. }
  176. bool HOTKEY_STORE::CheckKeyConflicts( TOOL_ACTION* aAction, long aKey, HOTKEY** aConflict )
  177. {
  178. wxString sectionName = GetSectionName( aAction );
  179. // Create a fake "TOOL_ACTION" so we can get the section name for "Common" through the API.
  180. // Simply declaring a wxString with the value "Common" works, but the goal is to futureproof
  181. // the code here as much as possible.
  182. TOOL_ACTION commonAction( "common.Control.Fake", AS_GLOBAL, 0, "", "", "" );
  183. wxString commonName = GetSectionName( &commonAction );
  184. for( HOTKEY_SECTION& section : m_hk_sections )
  185. {
  186. // We can have the same hotkey in multiple sections (i.e. Kicad programs), but if a hotkey
  187. // is in "Common" it can't be in any other section and vice versa.
  188. if( !( section.m_SectionName == sectionName || section.m_SectionName == commonName ) )
  189. continue;
  190. for( HOTKEY& hotkey : section.m_HotKeys )
  191. {
  192. if( hotkey.m_Actions[0] == aAction )
  193. continue;
  194. if( hotkey.m_EditKeycode == aKey )
  195. {
  196. // We can use the same key for a different action if both actions are contextual and
  197. // for different tools.
  198. if( hotkey.m_Actions[0]->GetScope() == AS_CONTEXT &&
  199. aAction->GetScope() == AS_CONTEXT &&
  200. hotkey.m_Actions[0]->GetToolName() != aAction->GetToolName() )
  201. {
  202. continue;
  203. }
  204. *aConflict = &hotkey;
  205. return true;
  206. }
  207. }
  208. }
  209. return false;
  210. }