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.

203 lines
6.2 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2020 KiCad Developers, see change_log.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 <fctsys.h>
  24. #include <scintilla_tricks.h>
  25. #include <wx/stc/stc.h>
  26. #include <gal/color4d.h>
  27. #include <dialog_shim.h>
  28. SCINTILLA_TRICKS::SCINTILLA_TRICKS( wxStyledTextCtrl* aScintilla, const wxString& aBraces ) :
  29. m_te( aScintilla ),
  30. m_braces( aBraces ),
  31. m_lastCaretPos( -1 )
  32. {
  33. // A hack which causes Scintilla to auto-size the text editor canvas
  34. // See: https://github.com/jacobslusser/ScintillaNET/issues/216
  35. m_te->SetScrollWidth( 1 );
  36. m_te->SetScrollWidthTracking( true );
  37. // Set up the brace highlighting
  38. wxColour highlight = wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT );
  39. wxColour highlightText = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT );
  40. if( KIGFX::COLOR4D( highlightText ).GetBrightness() > 0.5 )
  41. highlight = highlight.ChangeLightness( 80 );
  42. else
  43. highlight = highlight.ChangeLightness( 120 );
  44. m_te->StyleSetForeground( wxSTC_STYLE_BRACELIGHT, highlightText );
  45. m_te->StyleSetBackground( wxSTC_STYLE_BRACELIGHT, highlight );
  46. m_te->StyleSetForeground( wxSTC_STYLE_BRACEBAD, *wxRED );
  47. // Set up autocomplete
  48. m_te->AutoCompSetIgnoreCase( true );
  49. m_te->AutoCompSetFillUps( m_braces[1] );
  50. m_te->AutoCompSetMaxHeight( 20 );
  51. // Hook up events
  52. m_te->Bind( wxEVT_STC_UPDATEUI, &SCINTILLA_TRICKS::onScintillaUpdateUI, this );
  53. // Dispatch command-keys in Scintilla control.
  54. m_te->Bind( wxEVT_CHAR_HOOK, &SCINTILLA_TRICKS::onCharHook, this );
  55. }
  56. void SCINTILLA_TRICKS::onCharHook( wxKeyEvent& aEvent )
  57. {
  58. if( aEvent.GetKeyCode() == WXK_TAB )
  59. {
  60. if( aEvent.ControlDown() )
  61. {
  62. int flags = 0;
  63. if( !aEvent.ShiftDown() )
  64. flags |= wxNavigationKeyEvent::IsForward;
  65. wxWindow* parent = m_te->GetParent();
  66. while( parent && dynamic_cast<DIALOG_SHIM*>( parent ) == nullptr )
  67. parent = parent->GetParent();
  68. if( parent )
  69. parent->NavigateIn( flags );
  70. }
  71. else
  72. {
  73. m_te->Tab();
  74. }
  75. }
  76. else if( aEvent.GetModifiers() == wxMOD_CONTROL && aEvent.GetKeyCode() == 'Z' )
  77. {
  78. m_te->Undo();
  79. }
  80. else if( ( aEvent.GetModifiers() == wxMOD_SHIFT+wxMOD_CONTROL && aEvent.GetKeyCode() == 'Z' )
  81. || ( aEvent.GetModifiers() == wxMOD_CONTROL && aEvent.GetKeyCode() == 'Y' ) )
  82. {
  83. m_te->Redo();
  84. }
  85. else if( aEvent.GetModifiers() == wxMOD_CONTROL && aEvent.GetKeyCode() == 'X' )
  86. {
  87. m_te->Cut();
  88. }
  89. else if( aEvent.GetModifiers() == wxMOD_CONTROL && aEvent.GetKeyCode() == 'C' )
  90. {
  91. m_te->Copy();
  92. }
  93. else if( aEvent.GetModifiers() == wxMOD_CONTROL && aEvent.GetKeyCode() == 'V' )
  94. {
  95. m_te->Paste();
  96. }
  97. else if( aEvent.GetKeyCode() == WXK_BACK )
  98. {
  99. m_te->DeleteBack();
  100. }
  101. else if( aEvent.GetKeyCode() == WXK_DELETE )
  102. {
  103. if( m_te->GetSelectionEnd() > m_te->GetSelectionStart() )
  104. m_te->DeleteBack();
  105. else
  106. m_te->DeleteRange( m_te->GetSelectionStart(), 1 );
  107. }
  108. else
  109. {
  110. aEvent.Skip();
  111. }
  112. }
  113. void SCINTILLA_TRICKS::onScintillaUpdateUI( wxStyledTextEvent& aEvent )
  114. {
  115. auto isBrace = [this]( int c ) -> bool
  116. {
  117. return m_braces.Find( (wxChar) c ) >= 0;
  118. };
  119. // Has the caret changed position?
  120. int caretPos = m_te->GetCurrentPos();
  121. if( m_lastCaretPos != caretPos )
  122. {
  123. m_lastCaretPos = caretPos;
  124. int bracePos1 = -1;
  125. int bracePos2 = -1;
  126. // Is there a brace to the left or right?
  127. if( caretPos > 0 && isBrace( m_te->GetCharAt( caretPos-1 ) ) )
  128. bracePos1 = ( caretPos - 1 );
  129. else if( isBrace( m_te->GetCharAt( caretPos ) ) )
  130. bracePos1 = caretPos;
  131. if( bracePos1 >= 0 )
  132. {
  133. // Find the matching brace
  134. bracePos2 = m_te->BraceMatch( bracePos1 );
  135. if( bracePos2 == -1 )
  136. {
  137. m_te->BraceBadLight( bracePos1 );
  138. m_te->SetHighlightGuide( 0 );
  139. }
  140. else
  141. {
  142. m_te->BraceHighlight( bracePos1, bracePos2 );
  143. m_te->SetHighlightGuide( m_te->GetColumn( bracePos1 ) );
  144. }
  145. }
  146. else
  147. {
  148. // Turn off brace matching
  149. m_te->BraceHighlight( -1, -1 );
  150. m_te->SetHighlightGuide( 0 );
  151. }
  152. }
  153. }
  154. void SCINTILLA_TRICKS::DoAutocomplete( const wxString& aPartial, const wxArrayString& aTokens )
  155. {
  156. wxArrayString matchedTokens;
  157. wxString filter = wxT( "*" ) + aPartial.Lower() + wxT( "*" );
  158. for( const wxString& token : aTokens )
  159. {
  160. if( token.Lower().Matches( filter ) )
  161. matchedTokens.push_back( token );
  162. }
  163. if( matchedTokens.size() > 0 )
  164. {
  165. // NB: tokens MUST be in alphabetical order because the Scintilla engine is going
  166. // to do a binary search on them
  167. matchedTokens.Sort( []( const wxString& first, const wxString& second ) -> int
  168. {
  169. return first.CmpNoCase( second );
  170. });
  171. m_te->AutoCompShow( aPartial.size(), wxJoin( matchedTokens, ' ' ) );
  172. }
  173. }