/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include #include #include #include #include #include PANEL_SETUP_RULES::PANEL_SETUP_RULES( PAGED_DIALOG* aParent, PCB_EDIT_FRAME* aFrame ) : PANEL_SETUP_RULES_BASE( aParent->GetTreebook() ), m_Parent( aParent ), m_frame( aFrame ), m_scintillaTricks( nullptr ) { m_scintillaTricks = new SCINTILLA_TRICKS( m_textEditor, wxT( "()" ) ); int size = wxNORMAL_FONT->GetPointSize(); wxFont fixedFont( size, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL ); for( size_t i = 0; i < wxSTC_STYLE_MAX; ++i ) m_textEditor->StyleSetFont( i, fixedFont ); m_textEditor->Bind( wxEVT_STC_CHARADDED, &PANEL_SETUP_RULES::onScintillaCharAdded, this ); m_textEditor->Bind( wxEVT_STC_AUTOCOMP_CHAR_DELETED, &PANEL_SETUP_RULES::onScintillaCharAdded, this ); } PANEL_SETUP_RULES::~PANEL_SETUP_RULES( ) { delete m_scintillaTricks; }; void PANEL_SETUP_RULES::onScintillaCharAdded( wxStyledTextEvent &aEvent ) { constexpr int flags = wxSTC_FIND_REGEXP| wxSTC_FIND_POSIX; m_textEditor->SearchAnchor(); int i = std::max( 0, m_textEditor->SearchPrev( flags, "\( *rule " ) ); int currentPos = m_textEditor->GetCurrentPos(); enum { NONE, STRING, SEXPR_OPEN, SEXPR_TOKEN, STRUCT_REF }; std::stack sexprs; wxString partial; int context = NONE; int expr_context = NONE; for( ; i < currentPos; ++i ) { wxChar c = m_textEditor->GetCharAt( i ); if( c == '\\' ) { i++; // skip escaped char } else if( context == STRING ) { if( c == '"' ) { context = NONE; } else { if( expr_context == STRING ) { if( c == '\'' ) expr_context = NONE; else partial += c; } else if( c == '\'' ) { partial = wxEmptyString; expr_context = STRING; } else if( c == '.' ) { partial = wxEmptyString; expr_context = STRUCT_REF; } else { partial += c; } } } else if( c == '"' ) { partial = wxEmptyString; context = STRING; } else if( c == '(' ) { if( context == SEXPR_OPEN && !partial.IsEmpty() ) { m_textEditor->AutoCompCancel(); sexprs.push( partial ); } partial = wxEmptyString; context = SEXPR_OPEN; } else if( c == ')' ) { if( !sexprs.empty() ) sexprs.pop(); context = NONE; } else if( c == ' ' ) { if( context == SEXPR_OPEN && !partial.IsEmpty() ) { m_textEditor->AutoCompCancel(); sexprs.push( partial ); if( sexprs.size() && ( sexprs.top() == "constraint" || sexprs.top() == "disallow" ) ) { partial = wxEmptyString; context = SEXPR_TOKEN; continue; } } context = NONE; } else { partial += c; } } wxString tokens; if( context == SEXPR_OPEN ) { if( sexprs.empty() ) tokens = "rule version"; else if( sexprs.top() == "rule" ) tokens = "condition constraint disallow"; else if( sexprs.top() == "constraint" ) tokens = "max min opt"; } else if( context == SEXPR_TOKEN ) { if( sexprs.empty() ) /* badly formed grammar */; else if( sexprs.top() == "constraint" ) tokens = "annulus_width clearance hole track_width"; else if( sexprs.top() == "disallow" ) tokens = "buried_via graphic hole micro_via pad text track via zone"; } else if( context == STRING && expr_context == STRUCT_REF ) { if( !sexprs.empty() && sexprs.top() == "condition" ) { PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance(); std::set propNames; for( const PROPERTY_MANAGER::CLASS_INFO& cls : propMgr.GetAllClasses() ) { const PROPERTY_LIST& props = propMgr.GetProperties( cls.type ); for( PROPERTY_BASE* prop : props ) { wxString ref( prop->Name() ); ref.Replace( " ", "_" ); propNames.insert( ref ); } } for( const wxString& propName : propNames ) tokens += " " + propName; PCB_EXPR_BUILTIN_FUNCTIONS& functions = PCB_EXPR_BUILTIN_FUNCTIONS::Instance(); for( const wxString& funcSig : functions.GetSignatures() ) tokens += " " + funcSig; } } if( !tokens.IsEmpty() ) m_scintillaTricks->DoAutocomplete( partial, wxSplit( tokens, ' ' ) ); } bool PANEL_SETUP_RULES::TransferDataToWindow() { wxString rulesFilepath = m_frame->Prj().AbsolutePath( "drc-rules" ); wxFileName rulesFile( rulesFilepath ); if( rulesFile.FileExists() ) m_textEditor->LoadFile( rulesFile.GetFullPath() ); m_originalText = m_textEditor->GetText(); return true; } bool PANEL_SETUP_RULES::TransferDataFromWindow() { if( m_originalText == m_textEditor->GetText() ) return true; try { std::vector dummyRules; DRC_RULES_PARSER parser( m_frame->GetBoard(), m_textEditor->GetText(), _( "DRC rules" ) ); parser.Parse( dummyRules ); } catch( PARSE_ERROR& pe ) { m_Parent->SetError( pe.What(), this, m_textEditor, pe.lineNumber, pe.byteIndex ); return false; } if( m_textEditor->SaveFile( m_frame->Prj().AbsolutePath( "drc-rules" ) ) ) { m_frame->GetToolManager()->GetTool()->LoadRules(); return true; } return false; } void PANEL_SETUP_RULES::OnSyntaxHelp( wxHyperlinkEvent& aEvent ) { // Do not make this full sentence translatable: it contains keywords // Only a few titles can be traslated. wxString msg; msg << "" << _( "Top-level Clauses" ) << ""; msg << "
"
            "(version <number>)\r"
            "(rule <rule_name> <rule_clause> ...)\r"
            "\r
"; msg << _( "Rule Clauses" ); msg << "" "
"
            "(disallow <item_type>)\r"
            "(constraint <constraint_type> ...)\r"
            "(condition \"<expression>\")\r"
            "\r
" ""; msg << _( "Item Types" ); msg << "" "
"
            "track         via               zone\r"
            "pad           micro_via         text\r"
            "hole          buried_via        graphic\r"
            "\r
" ""; msg << _( "Constraint Types" ); msg << "" "
"
            "clearance    annulus_width   track_width     hole\r"
            "\r
" ""; msg << _( "Examples" ); msg << "" "
"
            "(rule \"copper keepout\"\r"
            "   (disallow track) (disallow via) (disallow zone)\r"
            "   (condition \"A.name == 'no_copper'\"))\r"
            "\r"
            "(rule \"BGA neckdown\"\r"
            "   (constraint track_width (min 0.2mm) (opt 0.25mm))\r"
            "   (constraint clearance (min 0.05) (opt 0.08mm))\r"
            "   (condition \"A.insideCourtyard('U3')\"))\r"
            "\r"
            "(rule HV\r"
            "   (constraint clearance (min 1.5mm))\r"
            "   (condition \"A.netclass == 'HV'\"))\r"
            "\r"
            "(rule HV_HV\r"
            "   (constraint clearance (min \"1.5mm + 2.0mm\"))\r"
            "   (condition \"A.netclass == 'HV' && B.netclass == 'HV'\"))\r"
            "
"; HTML_MESSAGE_BOX dlg( m_parent, _( "Syntax Help" ) ); dlg.SetDialogSizeInDU( 320, 320 ); dlg.AddHTML_Text( msg ); dlg.ShowModal(); }