KiCad PCB EDA Suite
panel_setup_rules.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) 2020 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 
24 #include <widgets/paged_dialog.h>
25 #include <pcb_edit_frame.h>
26 #include <project.h>
27 #include <tool/tool_manager.h>
28 #include <drc/drc.h>
29 #include <panel_setup_rules.h>
30 #include <html_messagebox.h>
31 #include <scintilla_tricks.h>
32 #include <drc/drc_rule_parser.h>
33 
35  PANEL_SETUP_RULES_BASE( aParent->GetTreebook() ),
36  m_Parent( aParent ),
37  m_frame( aFrame ),
38  m_scintillaTricks( nullptr )
39 {
40  m_scintillaTricks = new SCINTILLA_TRICKS( m_textEditor, wxT( "{}" ) );
41 
42  int size = wxNORMAL_FONT->GetPointSize();
43  wxFont fixedFont( size, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL );
44 
45  for( size_t i = 0; i < wxSTC_STYLE_MAX; ++i )
46  m_textEditor->StyleSetFont( i, fixedFont );
47 
48  m_textEditor->Bind( wxEVT_STC_CHARADDED, &PANEL_SETUP_RULES::onScintillaCharAdded, this );
49 }
50 
51 
53 {
54  delete m_scintillaTricks;
55 };
56 
57 
58 void PANEL_SETUP_RULES::onScintillaCharAdded( wxStyledTextEvent &aEvent )
59 {
60  constexpr int flags = wxSTC_FIND_REGEXP| wxSTC_FIND_POSIX;
61 
62  m_textEditor->SearchAnchor();
63 
64  int i = std::max( 0, m_textEditor->SearchPrev( flags, "\( *rule " ) );
65  int currentPos = m_textEditor->GetCurrentPos();
66 
67  enum
68  {
69  NONE,
70  STRING,
71  SEXPR_OPEN,
72  SEXPR_TOKEN,
73  };
74 
75  std::stack<wxString> sexprs;
76  wxString partial;
77  int context = NONE;
78 
79  for( ; i < currentPos; ++i )
80  {
81  wxChar c = m_textEditor->GetCharAt( i );
82 
83  if( c == '\\' )
84  {
85  i++; // skip escaped char
86  continue;
87  }
88 
89  if( context == STRING )
90  {
91  if( c == '"' )
92  context = NONE;
93  else
94  partial += c;
95 
96  continue;
97  }
98 
99  if( c == '"' )
100  {
101  partial = wxEmptyString;
102  context = STRING;
103  }
104  else if( c == '(' )
105  {
106  if( context == SEXPR_OPEN && !partial.IsEmpty() )
107  {
108  m_textEditor->AutoCompCancel();
109  sexprs.push( partial );
110  }
111 
112  partial = wxEmptyString;
113  context = SEXPR_OPEN;
114  }
115  else if( c == ')' )
116  {
117  sexprs.pop();
118  context = NONE;
119  }
120  else if( c == ' ' )
121  {
122  if( context == SEXPR_OPEN && !partial.IsEmpty() )
123  {
124  m_textEditor->AutoCompCancel();
125  sexprs.push( partial );
126 
127  if( sexprs.size()
128  && ( sexprs.top() == "constraint" || sexprs.top() == "disallow" ) )
129  {
130  partial = wxEmptyString;
131  context = SEXPR_TOKEN;
132  continue;
133  }
134  }
135 
136  context = NONE;
137  }
138  else
139  {
140  partial += c;
141  }
142  }
143 
144  wxString tokens;
145 
146  if( context == SEXPR_OPEN )
147  {
148  if( sexprs.empty() )
149  tokens = "rule version";
150  else if( sexprs.top() == "rule" )
151  tokens = "condition constraint disallow";
152  else if( sexprs.top() == "constraint" )
153  tokens = "max min opt";
154 
155  if( !tokens.IsEmpty() )
156  m_scintillaTricks->DoAutocomplete( partial, wxSplit( tokens, ' ' ) );
157  }
158  else if( context == SEXPR_TOKEN )
159  {
160  if( sexprs.top() == "constraint" )
161  tokens = "annulus_width clearance hole track_width";
162  else if( sexprs.top() == "disallow" )
163  tokens = "buried_via graphic hole micro_via pad text track via zone";
164 
165  if( !tokens.IsEmpty() )
166  m_scintillaTricks->DoAutocomplete( partial, wxSplit( tokens, ' ' ) );
167  }
168 }
169 
170 
172 {
173  wxString rulesFilepath = m_frame->Prj().AbsolutePath( "drc-rules" );
174  wxFileName rulesFile( rulesFilepath );
175 
176  if( rulesFile.FileExists() )
177  m_textEditor->LoadFile( rulesFile.GetFullPath() );
178 
179  m_originalText = m_textEditor->GetText();
180 
181  return true;
182 }
183 
184 
186 {
187  if( m_originalText == m_textEditor->GetText() )
188  return true;
189 
190  try
191  {
192  std::vector<DRC_SELECTOR*> dummySelectors;
193  std::vector<DRC_RULE*> dummyRules;
194 
195  DRC_RULES_PARSER parser( m_frame->GetBoard(), m_textEditor->GetText(), _( "DRC rules" ) );
196 
197  parser.Parse( dummySelectors, dummyRules );
198  }
199  catch( PARSE_ERROR& pe )
200  {
201  m_Parent->SetError( pe.What(), this, m_textEditor, pe.lineNumber, pe.byteIndex );
202  return false;
203  }
204 
205  if( m_textEditor->SaveFile( m_frame->Prj().AbsolutePath( "drc-rules" ) ) )
206  {
208  return true;
209  }
210 
211  return false;
212 }
213 
214 
215 void PANEL_SETUP_RULES::OnSyntaxHelp( wxHyperlinkEvent& aEvent )
216 {
217  // Do not make this full sentence translatable: it contains keywords
218  // Only a few titles can be traslated.
219  wxString msg;
220  msg << "<b>" << _( "Top-level Clauses" ) << "</b>";
221  msg << "<pre>"
222  "(version &lt;number>)\r"
223  "(rule &lt;rule_name> &lt;rule_clause> ...)\r"
224  "\r</pre><b>";
225  msg << _( "Rule Clauses" );
226  msg << "</b>"
227  "<pre>"
228  "(disallow &lt;item_type>)\r"
229  "(constraint &lt;constraint_type> ...)\r"
230  "(condition \"&lt;expression>\")\r"
231  "\r</pre>"
232  "<b>";
233  msg << _( "Item Types" );
234  msg << "</b>"
235  "<pre>"
236  "track via zone\r"
237  "pad micro_via text\r"
238  "hole buried_via graphic\r"
239  "\r</pre>"
240  "<b>";
241  msg << _( "Constraint Types" );
242  msg << "</b>"
243  "<pre>"
244  "clearance annulus_width track_width hole\r"
245  "\r</pre>"
246  "<b>";
247  msg << _( "Examples" );
248  msg << "</b>"
249  "<pre>"
250  "(rule \"copper keepout\"\r"
251  " (disallow track) (disallow via) (disallow zone)\r"
252  " (condition \"A.name == no_copper\"))\r"
253  "\r"
254  "(rule \"BGA neckdown\"\r"
255  " (constraint track_width (min 0.2mm) (opt 0.25mm))\r"
256  " (constraint clearance (min 0.05) (opt 0.08mm))\r"
257  " (condition \"A.insideCourtyard('U3')\"))\r"
258  "\r"
259  "(rule HV\r"
260  " (constraint clearance (min 1.5mm))\r"
261  " (condition \"A.netclass == HV\"))\r"
262  "\r"
263  "(rule HV_HV\r"
264  " (constraint clearance (min 2.0mm))\r"
265  " (condition \"A.netclass == HV && B.netclass == HV\"))\r"
266  "</pre>";
267 
268  HTML_MESSAGE_BOX dlg( m_parent, _( "Syntax Help" ) );
269  dlg.SetDialogSizeInDU( 320, 320 );
270 
271  dlg.AddHTML_Text( msg );
272  dlg.ShowModal();
273 }
bool LoadRules()
Load the DRC rules.
bool TransferDataFromWindow() override
SCINTILLA_TRICKS is used to add cut/copy/paste, autocomplete and brace highlighting to a wxStyleTextC...
bool TransferDataToWindow() override
void DoAutocomplete(const wxString &aPartial, const wxArrayString &aTokens)
void SetError(const wxString &aMessage, const wxString &aPageName, int aCtrlId, int aRow=-1, int aCol=-1)
SCINTILLA_TRICKS * m_scintillaTricks
void Parse(std::vector< DRC_SELECTOR * > &aSelectors, std::vector< DRC_RULE * > &aRules)
PCB_EDIT_FRAME * m_frame
VTBL_ENTRY const wxString AbsolutePath(const wxString &aFileName) const
Function AbsolutePath fixes up aFileName if it is relative to the project's directory to be an absolu...
Definition: project.cpp:270
Class PANEL_SETUP_RULES_BASE.
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:33
PAGED_DIALOG * m_Parent
int lineNumber
at which line number, 1 based index.
Definition: ki_exception.h:125
PROJECT & Prj() const
Function Prj returns a reference to the PROJECT "associated with" this KIWAY.
Subclass of DIALOG_DISPLAY_HTML_TEXT_BASE, which is generated by wxFormBuilder.
~PANEL_SETUP_RULES() override
PANEL_SETUP_RULES(PAGED_DIALOG *aParent, PCB_EDIT_FRAME *aFrame)
void onScintillaCharAdded(wxStyledTextEvent &aEvent)
HTML_MESSAGE_BOX.
void OnSyntaxHelp(wxHyperlinkEvent &aEvent) override
void SetDialogSizeInDU(int aWidth, int aHeight)
set the dialog size, using a "logical value.
Struct PARSE_ERROR contains a filename or source description, a problem input line,...
Definition: ki_exception.h:123
#define _(s)
Definition: 3d_actions.cpp:33
Design Rule Checker object that performs all the DRC tests.
void AddHTML_Text(const wxString &message)
Add HTML text (without any change) to message list.
PCB_EDIT_FRAME is the main frame for Pcbnew.
wxStyledTextCtrl * m_textEditor
int byteIndex
at which byte offset within the line, 1 based index
Definition: ki_exception.h:126
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
Definition: tools_holder.h:74
BOARD * GetBoard() const