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.

368 lines
12 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2013 Jean-Pierre Charras, jp.charras at wanadoo.fr
  5. * Copyright (C) 2013 Wayne Stambaugh <stambaughw@verizon.net>
  6. * Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors.
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, you may find one here:
  20. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  21. * or you may search the http://www.gnu.org website for the version 2 license,
  22. * or you may write to the Free Software Foundation, Inc.,
  23. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  24. */
  25. #include <sch_edit_frame.h>
  26. #include <base_units.h>
  27. #include <sch_validators.h>
  28. #include <tool/tool_manager.h>
  29. #include <general.h>
  30. #include <gr_text.h>
  31. #include <confirm.h>
  32. #include <sch_component.h>
  33. #include <sch_reference_list.h>
  34. #include <schematic.h>
  35. #include <widgets/unit_binder.h>
  36. #include <dialogs/html_messagebox.h>
  37. #include <dialog_edit_label.h>
  38. #include <kicad_string.h>
  39. #include <tool/actions.h>
  40. #include <scintilla_tricks.h>
  41. #include <sch_iref.h>
  42. class SCH_EDIT_FRAME;
  43. class SCH_TEXT;
  44. DIALOG_LABEL_EDITOR::DIALOG_LABEL_EDITOR( SCH_EDIT_FRAME* aParent, SCH_TEXT* aTextItem ) :
  45. DIALOG_LABEL_EDITOR_BASE( aParent ),
  46. m_textSize( aParent, m_textSizeLabel, m_textSizeCtrl, m_textSizeUnits, false ),
  47. m_netNameValidator(),
  48. m_scintillaTricks( nullptr ),
  49. m_helpWindow( nullptr )
  50. {
  51. m_Parent = aParent;
  52. m_CurrentText = aTextItem;
  53. switch( m_CurrentText->Type() )
  54. {
  55. case SCH_GLOBAL_LABEL_T: SetTitle( _( "Global Label Properties" ) ); break;
  56. case SCH_HIER_LABEL_T: SetTitle( _( "Hierarchical Label Properties" ) ); break;
  57. case SCH_LABEL_T: SetTitle( _( "Label Properties" ) ); break;
  58. case SCH_SHEET_PIN_T: SetTitle( _( "Hierarchical Sheet Pin Properties" ) ); break;
  59. default: SetTitle( _( "Text Properties" ) ); break;
  60. }
  61. m_valueMultiLine->SetEOLMode( wxSTC_EOL_LF );
  62. m_scintillaTricks = new SCINTILLA_TRICKS( m_valueMultiLine, wxT( "()" ) );
  63. if( m_CurrentText->IsMultilineAllowed() )
  64. {
  65. m_activeTextCtrl = m_valueMultiLine;
  66. m_activeTextEntry = nullptr;
  67. m_labelSingleLine->Show( false );
  68. m_valueSingleLine->Show( false );
  69. m_labelCombo->Show( false );
  70. m_valueCombo->Show( false );
  71. m_textEntrySizer->AddGrowableRow( 0 );
  72. }
  73. else if( m_CurrentText->Type() == SCH_GLOBAL_LABEL_T || m_CurrentText->Type() == SCH_LABEL_T )
  74. {
  75. m_activeTextCtrl = m_valueCombo;
  76. m_activeTextEntry = m_valueCombo;
  77. m_labelSingleLine->Show( false ); m_valueSingleLine->Show( false );
  78. m_labelMultiLine->Show( false ); m_valueMultiLine->Show( false );
  79. m_valueCombo->SetValidator( m_netNameValidator );
  80. }
  81. else
  82. {
  83. m_activeTextCtrl = m_valueSingleLine;
  84. m_activeTextEntry = m_valueSingleLine;
  85. m_labelCombo->Show( false );
  86. m_valueCombo->Show( false );
  87. m_labelMultiLine->Show( false );
  88. m_valueMultiLine->Show( false );
  89. if( m_CurrentText->Type() != SCH_TEXT_T )
  90. m_valueSingleLine->SetValidator( m_netNameValidator );
  91. m_valueCombo->SetValidator( m_netNameValidator );
  92. }
  93. SetInitialFocus( m_activeTextCtrl );
  94. m_TextShape->Show( m_CurrentText->Type() == SCH_GLOBAL_LABEL_T ||
  95. m_CurrentText->Type() == SCH_HIER_LABEL_T );
  96. if( m_CurrentText->Type() == SCH_GLOBAL_LABEL_T )
  97. {
  98. wxFont infoFont = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT );
  99. infoFont.SetSymbolicSize( wxFONTSIZE_X_SMALL );
  100. m_note1->SetFont( infoFont );
  101. m_note2->SetFont( infoFont );
  102. }
  103. else
  104. {
  105. m_note1->Show( false );
  106. m_note2->Show( false );
  107. }
  108. m_sdbSizer1OK->SetDefault();
  109. Layout();
  110. m_valueMultiLine->Bind( wxEVT_STC_CHARADDED, &DIALOG_LABEL_EDITOR::onScintillaCharAdded, this );
  111. // DIALOG_SHIM needs a unique hash_key because classname is not sufficient because the
  112. // various versions have different controls so we want to store sizes for each version.
  113. m_hash_key = TO_UTF8( GetTitle() );
  114. // Now all widgets have the size fixed, call FinishDialogSettings
  115. FinishDialogSettings();
  116. }
  117. DIALOG_LABEL_EDITOR::~DIALOG_LABEL_EDITOR()
  118. {
  119. delete m_scintillaTricks;
  120. if( m_helpWindow )
  121. m_helpWindow->Destroy();
  122. }
  123. bool DIALOG_LABEL_EDITOR::TransferDataToWindow()
  124. {
  125. if( !wxDialog::TransferDataToWindow() )
  126. return false;
  127. if( m_CurrentText->Type() == SCH_TEXT_T )
  128. {
  129. SCHEMATIC& schematic = m_Parent->Schematic();
  130. // show text variable cross-references in a human-readable format
  131. m_valueMultiLine->SetValue( schematic.ConvertKIIDsToRefs( m_CurrentText->GetText() ) );
  132. }
  133. else
  134. {
  135. // show control characters in a human-readable format
  136. m_activeTextEntry->SetValue( UnescapeString( m_CurrentText->GetText() ) );
  137. }
  138. if( m_valueCombo->IsShown() )
  139. {
  140. // Load the combobox with the existing labels of the same type
  141. std::set<wxString> existingLabels;
  142. SCH_SCREENS allScreens( m_Parent->Schematic().Root() );
  143. for( SCH_SCREEN* screen = allScreens.GetFirst(); screen; screen = allScreens.GetNext() )
  144. {
  145. for( SCH_ITEM* item : screen->Items().OfType( m_CurrentText->Type() ) )
  146. {
  147. auto textItem = static_cast<const SCH_TEXT*>( item );
  148. existingLabels.insert( UnescapeString( textItem->GetText() ) );
  149. }
  150. }
  151. wxArrayString existingLabelArray;
  152. for( const wxString& label : existingLabels )
  153. existingLabelArray.push_back( label );
  154. // existingLabelArray.Sort();
  155. m_valueCombo->Append( existingLabelArray );
  156. }
  157. // Set text options:
  158. m_TextOrient->SetSelection( static_cast<int>( m_CurrentText->GetLabelSpinStyle() ) );
  159. m_TextShape->SetSelection( static_cast<int>( m_CurrentText->GetShape() ) );
  160. int style = 0;
  161. if( m_CurrentText->IsItalic() )
  162. style = 1;
  163. if( m_CurrentText->IsBold() )
  164. style += 2;
  165. m_TextStyle->SetSelection( style );
  166. m_textSize.SetValue( m_CurrentText->GetTextWidth() );
  167. return true;
  168. }
  169. /*!
  170. * wxEVT_COMMAND_ENTER event handler for single-line control
  171. */
  172. void DIALOG_LABEL_EDITOR::OnEnterKey( wxCommandEvent& aEvent )
  173. {
  174. wxPostEvent( this, wxCommandEvent( wxEVT_COMMAND_BUTTON_CLICKED, wxID_OK ) );
  175. }
  176. void DIALOG_LABEL_EDITOR::onScintillaCharAdded( wxStyledTextEvent &aEvent )
  177. {
  178. wxStyledTextCtrl* te = m_valueMultiLine;
  179. wxArrayString autocompleteTokens;
  180. int text_pos = te->GetCurrentPos();
  181. int start = te->WordStartPosition( text_pos, true );
  182. wxString partial;
  183. auto textVarRef =
  184. [&]( int pos )
  185. {
  186. return pos >= 2 && te->GetCharAt( pos-2 ) == '$' && te->GetCharAt( pos-1 ) == '{';
  187. };
  188. // Check for cross-reference
  189. if( start > 1 && te->GetCharAt( start-1 ) == ':' )
  190. {
  191. int refStart = te->WordStartPosition( start-1, true );
  192. if( textVarRef( refStart ) )
  193. {
  194. partial = te->GetRange( start+1, text_pos );
  195. wxString ref = te->GetRange( refStart, start-1 );
  196. SCH_SHEET_LIST sheets = m_Parent->Schematic().GetSheets();
  197. SCH_REFERENCE_LIST refs;
  198. SCH_COMPONENT* refComponent = nullptr;
  199. sheets.GetComponents( refs );
  200. for( size_t jj = 0; jj < refs.GetCount(); jj++ )
  201. {
  202. if( refs[ jj ].GetComp()->GetRef( &refs[ jj ].GetSheetPath(), true ) == ref )
  203. {
  204. refComponent = refs[ jj ].GetComp();
  205. break;
  206. }
  207. }
  208. if( refComponent )
  209. refComponent->GetContextualTextVars( &autocompleteTokens );
  210. }
  211. }
  212. else if( textVarRef( start ) )
  213. {
  214. partial = te->GetTextRange( start, text_pos );
  215. m_CurrentText->GetContextualTextVars( &autocompleteTokens );
  216. SCHEMATIC* schematic = m_CurrentText->Schematic();
  217. if( schematic && schematic->CurrentSheet().Last() )
  218. schematic->CurrentSheet().Last()->GetContextualTextVars( &autocompleteTokens );
  219. for( std::pair<wxString, wxString> entry : Prj().GetTextVars() )
  220. autocompleteTokens.push_back( entry.first );
  221. }
  222. m_scintillaTricks->DoAutocomplete( partial, autocompleteTokens );
  223. m_valueMultiLine->SetFocus();
  224. }
  225. bool DIALOG_LABEL_EDITOR::TransferDataFromWindow()
  226. {
  227. if( !wxDialog::TransferDataFromWindow() )
  228. return false;
  229. // Don't allow text to disappear; it can be difficult to correct if you can't select it
  230. if( !m_textSize.Validate( 0.01, 1000.0, EDA_UNITS::MILLIMETRES ) )
  231. return false;
  232. wxString text;
  233. /* save old text in undo list if not already in edit */
  234. if( m_CurrentText->GetEditFlags() == 0 )
  235. m_Parent->SaveCopyInUndoList( m_Parent->GetScreen(), m_CurrentText, UNDO_REDO::CHANGED, false );
  236. m_Parent->GetCanvas()->Refresh();
  237. if( m_CurrentText->Type() == SCH_TEXT_T )
  238. {
  239. // convert any text variable cross-references to their UUIDs
  240. text = m_Parent->Schematic().ConvertRefsToKIIDs( m_valueMultiLine->GetValue() );
  241. }
  242. else
  243. {
  244. // labels need escaping
  245. text = EscapeString( m_activeTextEntry->GetValue(), CTX_NETNAME );
  246. }
  247. if( !text.IsEmpty() )
  248. m_CurrentText->SetText( text );
  249. else if( !m_CurrentText->IsNew() )
  250. {
  251. DisplayError( this, _( "Empty Text!" ) );
  252. return false;
  253. }
  254. m_CurrentText->SetLabelSpinStyle( (LABEL_SPIN_STYLE::SPIN) m_TextOrient->GetSelection() );
  255. m_CurrentText->SetTextSize( wxSize( m_textSize.GetValue(), m_textSize.GetValue() ) );
  256. if( m_TextShape )
  257. m_CurrentText->SetShape( (PINSHEETLABEL_SHAPE) m_TextShape->GetSelection() );
  258. int style = m_TextStyle->GetSelection();
  259. m_CurrentText->SetItalic( ( style & 1 ) );
  260. if( ( style & 2 ) )
  261. {
  262. m_CurrentText->SetBold( true );
  263. m_CurrentText->SetTextThickness( GetPenSizeForBold( m_CurrentText->GetTextWidth() ) );
  264. }
  265. else
  266. {
  267. m_CurrentText->SetBold( false );
  268. m_CurrentText->SetTextThickness( 0 ); // Use default pen width
  269. }
  270. m_Parent->UpdateItem( m_CurrentText );
  271. m_Parent->GetCanvas()->Refresh();
  272. m_Parent->OnModify();
  273. if( m_CurrentText->Type() == SCH_GLOBAL_LABEL_T )
  274. {
  275. SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( m_CurrentText );
  276. SCH_IREF* iref = label->GetIref();
  277. if( iref )
  278. {
  279. if( iref->GetBoundingBox().Intersects( label->GetBoundingBox() ) )
  280. iref->PlaceAtDefaultPosition();
  281. iref->CopyParentStyle();
  282. }
  283. }
  284. return true;
  285. }
  286. void DIALOG_LABEL_EDITOR::OnFormattingHelp( wxHyperlinkEvent& aEvent )
  287. {
  288. m_helpWindow = SCH_TEXT::ShowSyntaxHelp( this );
  289. }