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.

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