/* * Copyright 2005-2007 Gerald Schmidt. * * This file is part of Xml Copy Editor. * * Xml Copy Editor 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; version 2 of the License. * * Xml Copy Editor 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 Xml Copy Editor; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "xmlctrl.h" #include "xmlpromptgenerator.h" #include "xmlshallowvalidator.h" #include "xmlencodinghandler.h" //#include "wrapxerces.h" #include "xmlcopyeditor.h" // needed to enable validation-as-you-type alerts #include #include #include "validationthread.h" // adapted from wxSTEdit (c) 2005 John Labenski, Otto Wyss #define XMLCTRL_HASBIT(value, bit) (((value) & (bit)) != 0) BEGIN_EVENT_TABLE ( XmlCtrl, wxStyledTextCtrl ) EVT_CHAR ( XmlCtrl::OnChar ) EVT_KEY_DOWN ( XmlCtrl::OnKeyPressed ) EVT_IDLE ( XmlCtrl::OnIdle ) EVT_STC_MARGINCLICK ( wxID_ANY, XmlCtrl::OnMarginClick ) EVT_LEFT_DOWN ( XmlCtrl::OnMouseLeftDown ) EVT_LEFT_UP ( XmlCtrl::OnMouseLeftUp ) EVT_RIGHT_UP ( XmlCtrl::OnMouseRightUp ) EVT_MIDDLE_DOWN ( XmlCtrl::OnMiddleDown ) EVT_COMMAND(wxID_ANY, wxEVT_COMMAND_VALIDATION_COMPLETED, XmlCtrl::OnValidationCompleted) EVT_NOTIFY ( myEVT_NOTIFY_PROMPT_GENERATED, wxID_ANY, XmlCtrl::OnPromptGenerated ) END_EVENT_TABLE() // global protection for validation threads wxCriticalSection xmlcopyeditorCriticalSection; XmlCtrl::XmlCtrl ( wxWindow *parent, const XmlCtrlProperties &propertiesParameter, bool *protectTagsParameter, int visibilityStateParameter, int typeParameter, wxWindowID id, const char *buffer, // could be NULL size_t bufferLen, const wxString& basePathParameter, const wxString& auxPathParameter, const wxPoint& position, const wxSize& size, long style ) : wxStyledTextCtrl ( parent, id, position, size, style ) , type ( typeParameter ) , protectTags ( protectTagsParameter ) , visibilityState ( visibilityStateParameter ) , basePath ( basePathParameter ) , auxPath ( auxPathParameter ) { validationThread = NULL; mPromptGeneratorThread = NULL; grammarFound = false; validationRequired = (buffer) ? true : false; // NULL for plain XML template currentMaxLine = 1; applyProperties ( propertiesParameter ); SetTabWidth ( 2 ); SetWrapStartIndent ( 2 ); SetWrapVisualFlags ( wxSTC_WRAPVISUALFLAG_START ); SetUseTabs ( false ); SetBackSpaceUnIndents ( true ); SetTabIndents ( false ); SetUndoCollection ( false ); // handle NULL buffer if ( !buffer ) { buffer = DEFAULT_XML_DECLARATION_UTF8; bufferLen = strlen ( DEFAULT_XML_DECLARATION_UTF8 ); } #if wxCHECK_VERSION(2,9,0) if ( type != FILE_TYPE_BINARY ) SetScrollWidthTracking ( true ); #endif #if wxCHECK_VERSION(2,9,0) AddTextRaw ( buffer, bufferLen ); #else SendMsg ( 2001, bufferLen, ( wxIntPtr ) buffer ); #endif SetSelection ( 0, 0 ); // position cursor if ( type == FILE_TYPE_XML && bufferLen > 5 && buffer[0] == '<' && buffer[1] == '?' && buffer[2] == 'x' && buffer[3] == 'm' && buffer[4] == 'l' && GetLineCount() > 1 ) { GotoLine ( 1 ); // == line 2 of the document } SetSavePoint(); SetUndoCollection ( true ); AutoCompSetSeparator ( '<' ); applyVisibilityState ( visibilityState ); lineBackgroundState = BACKGROUND_STATE_NORMAL; for ( int i = 0; i < wxSTC_INDIC_MAX; ++i ) IndicatorSetStyle ( i, wxSTC_INDIC_HIDDEN ); IndicatorSetStyle ( 2, wxSTC_INDIC_SQUIGGLE ); IndicatorSetForeground ( 2, *wxRED ); #if wxCHECK_VERSION(2,9,0) SetIndicatorCurrent ( 2 ); #endif } XmlCtrl::~XmlCtrl() { attributeMap.clear(); elementMap.clear(); entitySet.clear(); if ( validationThread != NULL ) { validationThread->PendingDelete(); //validationThread->Delete(); //delete validationThread; } if ( mPromptGeneratorThread != NULL ) mPromptGeneratorThread->PendingDelete(); } // taken from wxStyledNotebook (c) Eran Ifrah static wxColor LightColour ( const wxColour& color, int percent ) { int rd, gd, bd, high = 0; wxColor end_color = wxT ( "WHITE" ); rd = end_color.Red() - color.Red(); gd = end_color.Green() - color.Green(); bd = end_color.Blue() - color.Blue(); high = 100; // We take the percent way of the color from color --> white int i = percent; int r = color.Red() + ( ( i*rd*100 ) /high ) /100; int g = color.Green() + ( ( i*gd*100 ) /high ) /100; int b = color.Blue() + ( ( i*bd*100 ) /high ) /100; return wxColor ( r, g, b ); } void XmlCtrl::OnIdle ( wxIdleEvent& event ) { if ( properties.number && type != FILE_TYPE_BINARY ) adjustNoColumnWidth(); // exits if unchanged } void XmlCtrl::OnValidationCompleted ( wxCommandEvent &event ) { wxCriticalSectionLocker locker ( xmlcopyeditorCriticalSection ); if ( validationThread == NULL ) return; MyFrame *frame = (MyFrame *)GetGrandParent(); clearErrorIndicators ( GetLineCount() ); if ( validationThread->isSucceeded() ) { frame->statusProgress ( wxEmptyString ); } else { setErrorIndicator ( validationThread->getPosition().first - 1, 0 ); frame->statusProgress ( validationThread->getMessage() ); } validationThread->Wait(); delete validationThread; validationThread = NULL; } void XmlCtrl::OnChar ( wxKeyEvent& event ) { if ( *protectTags ) { SetOvertype ( false ); if ( GetSelectionStart() != GetSelectionEnd() ) adjustSelection(); if ( !canInsertAt ( GetCurrentPos() ) ) adjustPosRight(); } if ( ( !properties.completion || GetOvertype() ) && !*protectTags ) { event.Skip(); return; } switch ( event.GetKeyCode() ) { case '<': handleOpenAngleBracket ( event ); return; case '>': handleCloseAngleBracket ( event ); return; case ' ': handleSpace ( event ); return; case '=': handleEquals ( event ); return; case '&': handleAmpersand ( event ); return; case '/': handleForwardSlash ( event ); return; default: break; } event.Skip(); } void XmlCtrl::handleBackspace ( wxKeyEvent& event ) { protectHeadLine(); validationRequired = true; if ( canMoveLeftAt ( GetCurrentPos() ) ) { DeleteBack();//event.Skip(); return; } int currentPos, limitPos; currentPos = GetCurrentPos(); if ( currentPos < 1 ) return; limitPos = currentPos - 1; // tag int limitStyle = getLexerStyleAt ( limitPos ); if ( GetCharAt ( limitPos ) == '>' && ( limitStyle == wxSTC_H_TAG || limitStyle == wxSTC_H_TAGUNKNOWN || limitStyle == wxSTC_H_TAGEND || limitStyle == wxSTC_H_XMLSTART || limitStyle == wxSTC_H_XMLEND ) ) { if ( GetSelectionStart() != GetSelectionEnd() ) { if ( *protectTags ) adjustSelection(); else DeleteBack();//event.Skip(); return; } if ( !properties.deleteWholeTag ) { if ( ! ( *protectTags ) ) DeleteBack();//event.Skip(); return; } // delete tag to left of caret for ( ; limitPos && GetCharAt ( limitPos ) != '<'; limitPos-- ) ; SetSelection ( currentPos, limitPos ); if ( *protectTags ) { SetReadOnly ( true ); // needed to prevent erroneous BS insertion by control int ret = wxMessageBox ( _ ( "Delete tag?" ), _ ( "Tags Locked" ), wxOK | wxCANCEL | wxICON_QUESTION ); SetReadOnly ( false ); if ( ret != wxOK ) { return; } // ensure selection is set correctly if ( GetSelectionStart() != currentPos || GetSelectionEnd() != limitPos ) SetSelection ( currentPos, limitPos ); } DeleteBack(); return; } // entity reference else if ( GetCharAt ( limitPos ) == ';' && getLexerStyleAt ( limitPos ) == wxSTC_H_ENTITY ) { // delete entity to left of caret for ( ; limitPos && getLexerStyleAt ( limitPos ) == wxSTC_H_ENTITY && GetCharAt ( limitPos ) != '&'; limitPos-- ) ; SetSelection ( limitPos, currentPos ); if ( *protectTags ) { SetReadOnly ( true ); // needed to prevent erroneous BS insertion by control int ret = wxMessageBox ( _ ( "Delete entity reference?" ), _ ( "Tags Locked" ), wxOK | wxCANCEL | wxICON_QUESTION ); SetReadOnly ( false ); if ( ret != wxOK ) return; // ensure selection is set correctly if ( GetSelectionStart() != currentPos || GetSelectionEnd() != limitPos ) SetSelection ( currentPos, limitPos ); } DeleteBack(); return; } else if ( *protectTags ) { return; } event.Skip(); } void XmlCtrl::handleDelete ( wxKeyEvent& event ) { protectHeadLine(); validationRequired = true; if ( !canMoveRightAt ( GetCurrentPos() ) && GetSelectionStart() == GetSelectionEnd() ) { int currentPos, limitPos; limitPos = currentPos = GetCurrentPos(); // tag int limitStyle = getLexerStyleAt ( limitPos ); if ( GetCharAt ( limitPos ) == '<' && ( limitStyle == wxSTC_H_TAG || limitStyle == wxSTC_H_TAGUNKNOWN || limitStyle == wxSTC_H_TAGEND || limitStyle == wxSTC_H_XMLSTART || limitStyle == wxSTC_H_XMLEND ) ) { if ( GetSelectionStart() != GetSelectionEnd() ) { if ( *protectTags ) adjustSelection(); else DeleteBack();//event.Skip(); return; } if ( !properties.deleteWholeTag ) { if ( ! ( *protectTags ) ) DeleteBack();//event.Skip(); return; } for ( ; GetCharAt ( limitPos ) != '>' && limitPos < GetLength(); limitPos++ ) { if ( limitPos > ( currentPos + BUFSIZ ) ) { DeleteBack();//event.Skip(); return; } } if ( currentPos != limitPos ) { SetSelection ( currentPos, limitPos + 1 ); if ( *protectTags ) { int ret = wxMessageBox ( _ ( "Delete tag?" ), _ ( "Tags Locked" ), wxOK | wxCANCEL | wxICON_QUESTION ); if ( ret != wxOK ) return; } DeleteBack(); return; } } // entity else if ( GetCharAt ( limitPos ) == '&' && getLexerStyleAt ( limitPos ) == wxSTC_H_ENTITY ) { for ( ; getLexerStyleAt ( limitPos ) == wxSTC_H_ENTITY && limitPos <= GetLength(); limitPos++ ) { if ( GetCharAt ( limitPos ) == ';' ) break; else if ( GetCharAt ( limitPos ) == '\n' || limitPos > ( currentPos + BUFSIZ ) ) { DeleteBack();//event.Skip(); return; } } if ( currentPos != limitPos ) { SetSelection ( currentPos, limitPos + 1 ); if ( *protectTags ) { int ret = wxMessageBox ( _ ( "Delete entity reference?" ), _ ( "Tags Locked" ), wxOK | wxCANCEL | wxICON_QUESTION ); if ( ret != wxOK ) return; } DeleteBack(); return; } } else if ( *protectTags ) return; } event.Skip(); } void XmlCtrl::handleOpenAngleBracket ( wxKeyEvent& event ) { if ( AutoCompActive() ) AutoCompCancel(); validationRequired = true; if ( *protectTags ) { AddText ( _T ( "<" ) ); return; } AddText ( _T ( "<" ) ); int pos = GetCurrentPos(); // exit conditions based on style int style = getLexerStyleAt ( pos ); switch ( style ) { case wxSTC_H_DOUBLESTRING: case wxSTC_H_SINGLESTRING: return; default: break; } // determine parent element int parentCloseAngleBracket; parentCloseAngleBracket = getParentCloseAngleBracket ( pos ); if ( parentCloseAngleBracket < 0 ) return; wxString parent = getLastElementName ( parentCloseAngleBracket ); if ( elementMap.find ( parent ) == elementMap.end() ) return; wxString choice; std::set &childSet = elementMap[parent]; std::set::iterator it; for ( it = childSet.begin(); it != childSet.end(); ++it ) { if ( !choice.empty() ) choice.append ( _T ( "<" ) ); choice.append ( *it ); } if ( !choice.empty() ) UserListShow ( 0, choice ); } void XmlCtrl::handleCloseAngleBracket ( wxKeyEvent& event ) { if ( AutoCompActive() ) AutoCompCancel(); validationRequired = true; if ( *protectTags ) { AddText ( _T ( ">" ) ); return; } wxString insertBuffer; int pos; pos = GetCurrentPos(); wxString elementName = getLastElementName ( pos ); if ( !elementName.empty() ) { attributeMap[elementName]; // Just want to put it there } // exit condition 1 if ( pos <= 1 ) { event.Skip(); return; } // exit condition 2 (empty tag/end of CDATA section) else if ( GetCharAt ( pos - 1 ) == '/' || GetCharAt ( pos - 1 ) == ']' ) { event.Skip(); return; } // exit condition 3 (comment/CDATA) else if ( getLexerStyleAt ( pos - 1 ) == wxSTC_H_COMMENT || getLexerStyleAt ( pos - 1 ) == wxSTC_H_CDATA || ( getLexerStyleAt ( pos - 1 ) == wxSTC_H_DOUBLESTRING && ( GetCharAt ( pos - 1 ) != '"' ) ) || ( getLexerStyleAt ( pos - 1 ) == wxSTC_H_SINGLESTRING && ( GetCharAt ( pos - 1 ) != '\'' ) ) ) { event.Skip(); return; } if ( !elementName.empty() ) { if ( !properties.insertCloseTag ) { event.Skip(); return; } AddText ( _T ( ">" ) ); insertBuffer += _T ( "" ); InsertText ( pos + 1, insertBuffer ); SetSelection ( pos + 1, pos + 1 ); } else event.Skip(); } void XmlCtrl::handleEquals ( wxKeyEvent& event ) { if ( AutoCompActive() ) AutoCompCancel(); int pos = GetCurrentPos(); if ( pos <= 0 || getLexerStyleAt ( pos - 1 ) != wxSTC_H_ATTRIBUTE ) { event.Skip(); return; } AddText ( _T ( "=\"\"" ) ); SetSelection ( pos + 2, pos + 2 ); // tbd: identify possible attribute values wxString choice, elementName, attributeName; elementName = getLastElementName ( pos ); attributeName = getLastAttributeName ( pos ); std::set &valueSet = attributeMap[elementName][attributeName]; if ( valueSet.empty() ) return; std::set::iterator valueSetIterator; int cutoff = BUFSIZ; for ( valueSetIterator = valueSet.begin(); valueSetIterator != valueSet.end(); ++valueSetIterator ) { if ( ! ( cutoff-- ) ) break; if ( !choice.empty() ) choice.Append ( _T ( "<" ) ); choice.Append ( *valueSetIterator ); } if ( !choice.empty() ) UserListShow ( 0, choice ); } void XmlCtrl::handleSpace ( wxKeyEvent& event ) { if ( AutoCompActive() ) AutoCompCancel(); int pos = GetCurrentPos(); if ( pos <= 2 ) { event.Skip(); return; } int style = getLexerStyleAt ( pos - 1 ); char c = GetCharAt ( pos - 1 ); bool proceed = false; // space pressed after element name if ( style == wxSTC_H_TAG || style == wxSTC_H_TAGUNKNOWN || style == wxSTC_H_ATTRIBUTEUNKNOWN || style == wxSTC_H_ATTRIBUTE ) { proceed = true; } // space pressed after attribute value else if ( ( style == wxSTC_H_DOUBLESTRING || style == wxSTC_H_SINGLESTRING ) && ( c == '\'' || c == '"' ) && GetCharAt ( pos - 2 ) != '=' ) { proceed = true; } int tagStartPos = getTagStartPos ( pos ); if ( !proceed || tagStartPos == -1 ) { event.Skip(); return; } AddText ( _T ( " " ) ); wxString elementName = getLastElementName ( pos ); if ( attributeMap.find ( elementName ) == attributeMap.end() ) return; wxString choice; wxString tag = GetTextRange ( tagStartPos, pos ); std::map > &curAttMap = attributeMap[elementName]; std::map >::iterator it; for ( it = curAttMap.begin(); it != curAttMap.end(); ++it ) { // avoid duplicate attributes if ( tag.Contains ( it->first + _T ( "=" ) ) ) continue; if ( !choice.empty() ) choice.Append ( _T ( "<" ) ); choice.Append ( it->first ); } if ( !choice.empty() ) { UserListShow ( 0, choice ); validationRequired = true; } } void XmlCtrl::handleAmpersand ( wxKeyEvent& event ) { if ( AutoCompActive() ) AutoCompCancel(); validationRequired = true; if ( *protectTags ) { AddText ( _T ( "&" ) ); return; } int pos, style; pos = GetCurrentPos(); style = getLexerStyleAt ( pos ); if ( style != wxSTC_H_COMMENT && style != wxSTC_H_CDATA && style != wxSTC_H_TAGUNKNOWN && entitySet.size() >= 4 ) // min. 4 default entities { AddText ( _T ( "&" ) ); wxString choice; std::set::iterator it = entitySet.begin(); choice += *it; choice += _T ( ";" ); for ( it++; it != entitySet.end(); ++it ) { choice += _T ( "<" ); choice += *it; choice += _T ( ";" ); } UserListShow ( 0, choice ); } else event.Skip(); } void XmlCtrl::handleForwardSlash ( wxKeyEvent& event ) { if ( AutoCompActive() ) AutoCompCancel(); int pos = GetCurrentPos(); if ( ( pos <= 0 ) || GetCharAt ( pos - 1 ) != '<' || getLexerStyleAt ( pos ) == wxSTC_H_COMMENT || getLexerStyleAt ( pos ) == wxSTC_H_CDATA ) { event.Skip(); return; } AddText ( _T ( "/" ) ); int parentCloseAngleBracket = getParentCloseAngleBracket ( pos ); if ( parentCloseAngleBracket < 0 ) return; wxString wideParent = getLastElementName ( parentCloseAngleBracket ); if ( wideParent.empty() ) return; AddText ( wideParent + _T ( ">" ) ); validationRequired = true; } void XmlCtrl::OnKeyPressed ( wxKeyEvent& event ) { if ( *protectTags && GetSelectionStart() != GetSelectionEnd() ) { adjustSelection(); } if ( *protectTags ) SetOvertype ( false ); int pos, iteratorPos, maxPos; char c; wxString s; switch ( event.GetKeyCode() ) { case WXK_RETURN: if ( AutoCompActive() ) { AutoCompComplete(); return; } if ( *protectTags ) adjustPosRight(); insertNewLine(); return; case WXK_RIGHT: pos = GetCurrentPos(); if ( *protectTags && !canMoveRightAt ( pos ) ) { SetSelection ( pos + 1, pos + 1 ); adjustPosRight(); return; } maxPos = GetLength(); c = GetCharAt ( pos ); if ( c == '<' && event.ControlDown() && !event.ShiftDown() ) { for ( iteratorPos = pos; iteratorPos < maxPos && GetCharAt ( iteratorPos ) != ' ' && GetCharAt ( iteratorPos ) != '>' && GetCharAt ( iteratorPos ) != '\n'; ++iteratorPos ) ; ++iteratorPos; SetSelection ( iteratorPos, iteratorPos ); return; } else break; case WXK_LEFT: pos = GetCurrentPos(); if ( *protectTags && !canMoveLeftAt ( pos ) ) { adjustPosLeft(); return; } if ( pos < 3 ) break; c = GetCharAt ( pos - 1 ); if ( c == '>' && event.ControlDown() && !event.ShiftDown() ) { for ( iteratorPos = pos - 1; iteratorPos > 0 && GetCharAt ( iteratorPos ) != '<' && GetCharAt ( iteratorPos ) != ' ' && GetCharAt ( iteratorPos ) != '=' && GetCharAt ( iteratorPos ) != '\n'; --iteratorPos ) ; if ( GetCharAt ( iteratorPos ) != '<' && GetCharAt ( iteratorPos ) != '=' ) ++iteratorPos; SetSelection ( iteratorPos, iteratorPos ); return; } else break; case WXK_UP: if ( *protectTags ) { LineUp(); if ( !canInsertAt ( GetCurrentPos() ) ) adjustPosLeft(); return; } break; case WXK_DOWN: if ( *protectTags ) { LineDown(); if ( !canInsertAt ( GetCurrentPos() ) ) adjustPosRight(); return; } break; case WXK_INSERT: if ( *protectTags ) return; break; case WXK_BACK: handleBackspace ( event ); validationRequired = true; return; case WXK_TAB: if ( *protectTags ) { if ( !canInsertAt ( GetCurrentPos() ) ) adjustPosRight(); } break; case WXK_HOME: if ( *protectTags && !event.ControlDown() && !event.ShiftDown() ) { Home(); if ( !canInsertAt ( GetCurrentPos() ) ) adjustPosLeft(); return; } break; case WXK_END: if ( *protectTags && !event.ControlDown() && !event.ShiftDown() ) { LineEnd(); if ( !canInsertAt ( GetCurrentPos() ) ) adjustPosRight(); return; } break; case WXK_PAGEUP: if ( *protectTags ) { PageUp(); if ( !canInsertAt ( GetCurrentPos() ) ) adjustPosLeft(); return; } break; case WXK_PAGEDOWN: if ( *protectTags ) { PageDown(); if ( !canInsertAt ( GetCurrentPos() ) ) adjustPosRight(); return; } break; case WXK_DELETE: handleDelete ( event ); validationRequired = true; return; default: break; } event.Skip(); } wxString XmlCtrl::getLastElementName ( int pos ) { if ( pos < 1 ) return _T ( "" ); int startPos, iteratorPos; for ( startPos = pos - 1; startPos >= 0; --startPos ) { char c = GetCharAt ( startPos ); if ( c == '>' ) return _T ( "" ); else if ( c == '<' ) break; } // exit if not found or closing/declaration/command tag if ( GetCharAt ( startPos ) != '<' || GetCharAt ( startPos + 1 ) == '/' || GetCharAt ( startPos + 1 ) == '?' || GetCharAt ( startPos + 1 ) == '!' ) return _T ( "" ); ++startPos; for ( iteratorPos = startPos; iteratorPos < pos && GetCharAt ( iteratorPos ) != ' ' && GetCharAt ( iteratorPos ) != '\n' && GetCharAt ( iteratorPos ) != '\t' && GetCharAt ( iteratorPos ) != '/' && GetCharAt ( iteratorPos ) != '>'; ++iteratorPos ) ; if ( startPos == iteratorPos ) return _T ( "" ); return GetTextRange ( startPos, iteratorPos ); } const std::set &XmlCtrl::getChildren ( const wxString& parent ) { static std::set mySet; if ( elementMap.find ( parent ) == elementMap.end() ) return mySet; return elementMap[parent]; } wxString XmlCtrl::getLastAttributeName ( int pos ) { if ( pos < 1 ) return _T ( "" ); int startPos; for ( startPos = pos - 1; startPos >= 0; --startPos ) { char c = GetCharAt ( startPos ); if ( c == ' ' || c == '<' ) break; } if ( GetCharAt ( startPos ) != ' ' || startPos >= pos - 1 ) return _T ( "" ); return GetTextRange ( startPos + 1, pos ); } int XmlCtrl::findNextEndTag ( int pos, unsigned depth /*= 1*/, int character /*= '>'*/, int range /*= USHRT_MAX * 4*/ ) { wxASSERT ( character == '<' || character == '>' ); unsigned openAngleBrackets = 0; int cutoff = pos + range; if ( cutoff > GetEndStyled() ) cutoff = GetEndStyled(); for ( ; pos < cutoff; ++pos ) { int ch = GetCharAt ( pos ); if ( ch == '<' ) openAngleBrackets = ( openAngleBrackets << 1 ) | 1;// Just a flag // Check for empty tags, which have start tags but no end tags if ( character != '>' || !openAngleBrackets ) if ( ch == '>' && getLexerStyleAt ( pos ) == wxSTC_H_TAGEND ) --depth; if ( ch != character ) continue; int style = getLexerStyleAt ( pos ); if ( style == wxSTC_H_TAG || style == wxSTC_H_TAGUNKNOWN ) { int type = getTagType ( pos ); switch ( type ) { case TAG_TYPE_CLOSE: --depth; break; case TAG_TYPE_OPEN: // In case that the cursor is inside a start tag if ( character != '>' || openAngleBrackets > 0 ) ++depth; break; case TAG_TYPE_EMPTY: case TAG_TYPE_OTHER: case TAG_TYPE_ERROR: break; } } if ( !depth ) return pos + 1; } return -1; } int XmlCtrl::findPreviousStartTag ( int pos, unsigned depth /*= 1*/, int character /*= '<'*/, int range /*= USHRT_MAX * 4*/ ) { wxASSERT ( character == '<' || character == '>' ); int cutoff = ( ( pos - range ) > 2 ) ? pos - range : 2; unsigned closeAngleBrackets = 0; while ( pos-- > cutoff ) { int ch = GetCharAt ( pos ); if ( ch == '>' ) { closeAngleBrackets = ( closeAngleBrackets << 1 ) | 1;// Just a flag // Check for empty tags, which have start tags but no end tags if ( character != '>' && getLexerStyleAt ( pos ) == wxSTC_H_TAGEND ) ++depth; } if ( ch != character ) continue; int style = getLexerStyleAt ( pos ); if ( style == wxSTC_H_TAG || style == wxSTC_H_TAGUNKNOWN ) { int type = getTagType ( pos ); switch ( type ) { case TAG_TYPE_CLOSE: // If the cursor is already in an end tag if ( character != '<' || closeAngleBrackets > 0 ) ++depth; break; case TAG_TYPE_OPEN: --depth; break; case TAG_TYPE_EMPTY: case TAG_TYPE_OTHER: case TAG_TYPE_ERROR: break; } } if ( !depth ) return pos; } return -1; } void XmlCtrl::adjustNoColumnWidth() { int maxLine = GetLineCount(); if ( maxLine == currentMaxLine ) return; int digits, charWidth, width; digits = 0; do { ++digits; maxLine /= 10; } while ( maxLine ); digits = ( digits > 2 ) ? digits : 2; charWidth = TextWidth ( wxSTC_STYLE_LINENUMBER, _T ( "9" ) ); width = ( digits + 2 ) * charWidth; SetMarginWidth ( 0, width ); currentMaxLine = maxLine; } void XmlCtrl::updatePromptMaps() { std::string bufferUtf8 = myGetTextRaw(); updatePromptMaps ( bufferUtf8.c_str(), bufferUtf8.size() ); } void XmlCtrl::updatePromptMaps ( const char *utf8Buffer, size_t bufferLen ) { attributeMap.clear(); elementMap.clear(); elementStructureMap.clear(); wxCriticalSectionLocker locker ( xmlcopyeditorCriticalSection ); if ( mPromptGeneratorThread ) return; mPromptGeneratorThread = new XmlPromptGenerator ( GetEventHandler(), utf8Buffer, bufferLen, basePath, auxPath, "UTF-8" ); if ( mPromptGeneratorThread->Create() != wxTHREAD_NO_ERROR || mPromptGeneratorThread->Run() != wxTHREAD_NO_ERROR ) { delete mPromptGeneratorThread; mPromptGeneratorThread = NULL; } } void XmlCtrl::OnPromptGenerated ( wxNotifyEvent &event ) { wxCriticalSectionLocker locker ( xmlcopyeditorCriticalSection ); if ( mPromptGeneratorThread == NULL ) return; attributeMap = mPromptGeneratorThread->getAttributeMap(); requiredAttributeMap = mPromptGeneratorThread->getRequiredAttributeMap(); elementMap = mPromptGeneratorThread->getElementMap(); elementStructureMap = mPromptGeneratorThread->getElementStructureMap(); entitySet = mPromptGeneratorThread->getEntitySet(); grammarFound = mPromptGeneratorThread->getGrammarFound(); entitySet.insert ( _T ( "amp" ) ); entitySet.insert ( _T ( "apos" ) ); entitySet.insert ( _T ( "quot" ) ); entitySet.insert ( _T ( "lt" ) ); entitySet.insert ( _T ( "gt" ) ); mPromptGeneratorThread->Wait(); delete mPromptGeneratorThread; mPromptGeneratorThread = NULL; wxPostEvent ( GetParent()->GetEventHandler(), event ); } void XmlCtrl::applyProperties ( const XmlCtrlProperties &propertiesParameter, bool zoomOnly ) { properties = propertiesParameter; SetZoom ( ( type != FILE_TYPE_BINARY ) ? properties.zoom : 0 ); if ( zoomOnly ) return; SetCaretLineVisible ( properties.currentLine ); SetIndentationGuides ( ( type != FILE_TYPE_BINARY ) ? properties.indentLines : false ); SetWrapMode ( ( type != FILE_TYPE_BINARY ) ? properties.wrap : false ); SetViewWhiteSpace ( ( properties.whitespaceVisible ) ? wxSTC_WS_VISIBLEAFTERINDENT : wxSTC_WS_INVISIBLE ); switch ( type ) { case FILE_TYPE_BINARY: SetLexer ( wxSTC_LEX_NULL ); break; case FILE_TYPE_CSS: SetLexer ( wxSTC_LEX_CSS ); break; default: SetLexer ( wxSTC_LEX_XML ); break; } setColorScheme ( ( type != FILE_TYPE_BINARY ) ? properties.colorScheme : COLOR_SCHEME_NONE ); // line no margin if ( properties.number ) // permit line nos for large files { SetMarginType ( 0, wxSTC_MARGIN_NUMBER ); // width set at idle time adjustNoColumnWidth(); } else SetMarginWidth ( 0, 0 ); SetMarginWidth ( 1, 0 ); if ( properties.fold && type != FILE_TYPE_BINARY ) { // folding margin SetMarginType ( 2, wxSTC_MARGIN_SYMBOL ); SetMarginMask ( 2, wxSTC_MASK_FOLDERS ); SetMarginSensitive ( 2, true ); SetMarginWidth ( 2, 16 ); // define folding markers MarkerDefine ( wxSTC_MARKNUM_FOLDEREND, wxSTC_MARK_BOXPLUSCONNECTED, *wxWHITE, *wxBLACK ); MarkerDefine ( wxSTC_MARKNUM_FOLDEROPENMID, wxSTC_MARK_BOXMINUSCONNECTED, *wxWHITE, *wxBLACK ); MarkerDefine ( wxSTC_MARKNUM_FOLDERMIDTAIL, wxSTC_MARK_TCORNER, *wxWHITE, *wxBLACK ); MarkerDefine ( wxSTC_MARKNUM_FOLDERTAIL, wxSTC_MARK_LCORNER, *wxWHITE, *wxBLACK ); MarkerDefine ( wxSTC_MARKNUM_FOLDERSUB, wxSTC_MARK_VLINE, *wxWHITE, *wxBLACK ); MarkerDefine ( wxSTC_MARKNUM_FOLDER, wxSTC_MARK_BOXPLUS, *wxWHITE, *wxBLACK ); MarkerDefine ( wxSTC_MARKNUM_FOLDEROPEN, wxSTC_MARK_BOXMINUS, *wxWHITE, *wxBLACK ); } else { SetMarginWidth ( 2, 0 ); } if ( type != FILE_TYPE_BINARY ) { Colourise ( 0, -1 ); } wxString value = ( properties.fold && type != FILE_TYPE_BINARY ) ? _T ( "1" ) : _T ( "0" ); SetProperty ( _T ( "fold" ), value ); SetProperty ( _T ( "fold.compact" ), value ); SetProperty ( _T ( "fold.html" ), value ); } void XmlCtrl::OnMarginClick ( wxStyledTextEvent& event ) { const int line = LineFromPosition ( event.GetPosition() ); const int margin = event.GetMargin(); // let others process this first if ( GetParent()->GetEventHandler()->ProcessEvent ( event ) ) return; if ( margin == 2 ) { const int level = GetFoldLevel ( line ); if ( ( ( level ) & ( wxSTC_FOLDLEVELHEADERFLAG ) ) != 0 ) ToggleFold ( line ); } else event.Skip(); } void XmlCtrl::OnMouseLeftDown ( wxMouseEvent& event ) { SetMouseDownCaptures ( !*protectTags ); event.Skip(); } void XmlCtrl::OnMouseLeftUp ( wxMouseEvent& event ) { event.Skip(); if ( *protectTags && !canInsertAt ( GetCurrentPos() ) ) adjustPosRight(); } void XmlCtrl::OnMouseRightUp ( wxMouseEvent& event ) { if ( *protectTags ) return; event.Skip(); } bool XmlCtrl::isCloseTag ( int pos ) { int iteratorPos; for ( iteratorPos = pos; iteratorPos >= 0; --iteratorPos ) { if ( GetCharAt ( iteratorPos ) == '<' && getLexerStyleAt ( iteratorPos ) == wxSTC_H_TAG ) return ( GetCharAt ( iteratorPos + 1 ) == '/' ) ? true : false; } return false; } int XmlCtrl::getTagStartPos ( int pos ) { int iteratorPos; for ( iteratorPos = pos; iteratorPos >= 0; --iteratorPos ) { if ( GetCharAt ( iteratorPos ) == '<' && getLexerStyleAt ( iteratorPos ) == wxSTC_H_TAG ) return iteratorPos; } return -1; } int XmlCtrl::getAttributeStartPos ( int pos ) { int iteratorPos; char c; bool attributeSeen = false; for ( iteratorPos = pos; iteratorPos > 0; iteratorPos-- ) { c = GetCharAt ( iteratorPos ); if ( ( getLexerStyleAt ( iteratorPos ) ) == wxSTC_H_ATTRIBUTE ) attributeSeen = true; else if ( ( c == ' ' || c == '\t' || c == '\n' ) && attributeSeen ) return iteratorPos; } return -1; } int XmlCtrl::getAttributeSectionEndPos ( int pos, int range ) { int iteratorPos, limit, style; limit = pos + range; for ( iteratorPos = pos; iteratorPos < limit; iteratorPos++ ) { style = getLexerStyleAt ( iteratorPos ); switch ( style ) { case wxSTC_H_TAG: case wxSTC_H_TAGEND: case wxSTC_H_XMLEND: case wxSTC_H_QUESTION: return iteratorPos; default: continue; } } return -1; } void XmlCtrl::adjustCursor() { if ( !canInsertAt ( GetCurrentPos() ) ) adjustPosRight(); } bool XmlCtrl::canInsertAt ( int pos ) { if ( pos < 0 ) return false; int style = getLexerStyleAt ( pos ); switch ( style ) { case wxSTC_H_TAG: case wxSTC_H_TAGUNKNOWN: case wxSTC_H_QUESTION: case wxSTC_H_CDATA: case wxSTC_H_COMMENT: case wxSTC_H_SGML_DEFAULT: return ( GetCharAt ( pos ) == '<' ) ? true : false; case wxSTC_H_ENTITY: return ( GetCharAt ( pos ) == '&' ) ? true : false; case wxSTC_H_DEFAULT: return true; } return false; } bool XmlCtrl::canMoveRightAt ( int pos ) { int style = getLexerStyleAt ( pos ); switch ( style ) { case wxSTC_H_DEFAULT: return true; } return false; } bool XmlCtrl::canMoveLeftAt ( int pos ) { if ( pos < 1 ) return false; int style = getLexerStyleAt ( pos - 1 ); switch ( style ) { case wxSTC_H_DEFAULT: return true; } return false; } void XmlCtrl::adjustPosRight() { int pos, max; pos = GetCurrentPos(); max = GetLength(); for ( ; pos <= max; pos++ ) if ( canInsertAt ( pos ) ) { break; } SetSelection ( pos, pos ); } void XmlCtrl::adjustPosLeft() { int pos; pos = GetCurrentPos() - 1; if ( pos < 0 ) { SetSelection ( 0, 0 ); return; } for ( ; pos > 0; pos-- ) if ( canInsertAt ( pos ) ) { break; } SetSelection ( pos, pos ); } void XmlCtrl::adjustSelection() { int start, end, iterator; start = GetSelectionStart(); end = GetSelectionEnd(); // exit condition 1 if ( start == end && canInsertAt ( start ) ) return; // exit condition 2 else if ( !canInsertAt ( start ) || start > end ) { SetSelection ( start, start ); return; } for ( iterator = start; iterator < end; iterator++ ) { if ( !canMoveRightAt ( iterator ) ) break; } SetSelection ( start, iterator ); } void XmlCtrl::setColorScheme ( int scheme ) { StyleSetFaceName ( wxSTC_STYLE_DEFAULT, properties.font ); switch ( scheme ) { case COLOR_SCHEME_DEFAULT: StyleSetForeground ( wxSTC_STYLE_DEFAULT, *wxBLACK ); StyleSetBackground ( wxSTC_STYLE_DEFAULT, *wxWHITE ); StyleClearAll(); baseBackground = LightColour ( wxTheColourDatabase->Find ( _T ( "CYAN" ) ), 75 ); alternateBackground = LightColour ( wxTheColourDatabase->Find ( _T ( "CYAN" ) ), 95 ); SetCaretLineBackground ( baseBackground ); SetCaretForeground ( *wxBLACK ); SetSelBackground ( true, LightColour ( wxTheColourDatabase->Find ( _T ( "YELLOW" ) ), 20));//wxTheColourDatabase->Find ( _T ( "LIGHT GREY" ) ) ); if ( type == FILE_TYPE_CSS ) { // CSS colours StyleSetForeground ( wxSTC_CSS_DEFAULT, *wxBLACK ); StyleSetForeground ( wxSTC_CSS_TAG, *wxBLUE ); StyleSetForeground ( wxSTC_CSS_CLASS, *wxBLUE ); StyleSetForeground ( wxSTC_CSS_PSEUDOCLASS, *wxBLUE ); StyleSetForeground ( wxSTC_CSS_UNKNOWN_PSEUDOCLASS, *wxBLUE ); StyleSetForeground ( wxSTC_CSS_OPERATOR, *wxBLUE ); StyleSetForeground ( wxSTC_CSS_IDENTIFIER, *wxBLUE ); StyleSetForeground ( wxSTC_CSS_UNKNOWN_IDENTIFIER, *wxBLUE ); StyleSetForeground ( wxSTC_CSS_VALUE, *wxBLACK ); StyleSetForeground ( wxSTC_CSS_COMMENT, wxTheColourDatabase->Find ( _T ( "GREY" ) ) ); StyleSetForeground ( wxSTC_CSS_ID, *wxBLACK ); StyleSetForeground ( wxSTC_CSS_IMPORTANT, *wxRED ); StyleSetForeground ( wxSTC_CSS_DIRECTIVE, *wxBLUE ); StyleSetForeground ( wxSTC_CSS_DOUBLESTRING, *wxRED ); StyleSetForeground ( wxSTC_CSS_SINGLESTRING, *wxRED ); StyleSetForeground ( wxSTC_CSS_IDENTIFIER2, *wxRED ); StyleSetForeground ( wxSTC_CSS_ATTRIBUTE, *wxBLUE ); } else // XML { StyleSetForeground ( wxSTC_H_DEFAULT, *wxBLACK ); StyleSetForeground ( wxSTC_H_TAG, *wxBLUE ); StyleSetForeground ( wxSTC_H_TAGUNKNOWN, *wxBLUE ); StyleSetForeground ( wxSTC_H_ATTRIBUTE, *wxRED ); StyleSetForeground ( wxSTC_H_ATTRIBUTEUNKNOWN, *wxRED ); StyleSetForeground ( wxSTC_H_NUMBER, *wxBLACK ); StyleSetForeground ( wxSTC_H_DOUBLESTRING, *wxBLACK ); StyleSetForeground ( wxSTC_H_SINGLESTRING, *wxBLACK ); StyleSetForeground ( wxSTC_H_OTHER, *wxBLUE ); StyleSetForeground ( wxSTC_H_COMMENT, wxTheColourDatabase->Find ( _T ( "GREY" ) ) ); StyleSetForeground ( wxSTC_H_ENTITY, *wxRED ); StyleSetForeground ( wxSTC_H_TAGEND, *wxBLUE ); StyleSetForeground ( wxSTC_H_XMLSTART, wxTheColourDatabase->Find ( _T ( "GREY" ) ) ); StyleSetForeground ( wxSTC_H_XMLEND, wxTheColourDatabase->Find ( _T ( "GREY" ) ) ); StyleSetForeground ( wxSTC_H_CDATA, *wxRED ); StyleSetForeground ( wxSTC_H_QUESTION, wxTheColourDatabase->Find ( _T ( "GREY" ) ) ); for ( int i = wxSTC_H_SGML_DEFAULT; i <= wxSTC_H_SGML_BLOCK_DEFAULT; i++ ) StyleSetForeground ( i, *wxBLUE ); StyleSetForeground ( wxSTC_H_SGML_ENTITY, *wxRED ); StyleSetForeground ( wxSTC_H_SGML_SPECIAL, *wxBLACK ); StyleSetForeground ( wxSTC_H_SGML_SIMPLESTRING, *wxRED ); StyleSetForeground ( wxSTC_H_SGML_DEFAULT, wxTheColourDatabase->Find ( _T ( "GREY" ) ) ); } break; case COLOR_SCHEME_DEFAULT_BACKGROUND: StyleSetForeground ( wxSTC_STYLE_DEFAULT, *wxBLACK ); StyleSetBackground ( wxSTC_STYLE_DEFAULT, *wxWHITE ); StyleClearAll(); baseBackground = LightColour ( wxTheColourDatabase->Find ( _T ( "CYAN" ) ), 75 ); alternateBackground = LightColour ( wxTheColourDatabase->Find ( _T ( "CYAN" ) ), 95 ); SetCaretLineBackground ( baseBackground ); SetCaretForeground ( *wxBLACK ); SetSelBackground ( true, LightColour ( wxTheColourDatabase->Find ( _T ( "YELLOW" ) ), 20) );//wxTheColourDatabase->Find ( _T ( "LIGHT GREY" ) ) ); if ( type == FILE_TYPE_CSS ) { // CSS colours StyleSetForeground ( wxSTC_CSS_DEFAULT, *wxBLACK ); StyleSetForeground ( wxSTC_CSS_TAG, wxTheColourDatabase->Find ( _T ( "SKY BLUE" ) ) ); StyleSetForeground ( wxSTC_CSS_CLASS, wxTheColourDatabase->Find ( _T ( "SKY BLUE" ) ) ); StyleSetForeground ( wxSTC_CSS_PSEUDOCLASS, wxTheColourDatabase->Find ( _T ( "SKY BLUE" ) ) ); StyleSetForeground ( wxSTC_CSS_UNKNOWN_PSEUDOCLASS, wxTheColourDatabase->Find ( _T ( "SKY BLUE" ) ) ); StyleSetForeground ( wxSTC_CSS_OPERATOR, wxTheColourDatabase->Find ( _T ( "SKY BLUE" ) ) ); StyleSetForeground ( wxSTC_CSS_IDENTIFIER, wxTheColourDatabase->Find ( _T ( "SKY BLUE" ) ) ); StyleSetForeground ( wxSTC_CSS_UNKNOWN_IDENTIFIER, wxTheColourDatabase->Find ( _T ( "SKY BLUE" ) ) ); StyleSetForeground ( wxSTC_CSS_VALUE, *wxBLACK ); StyleSetForeground ( wxSTC_CSS_COMMENT, wxTheColourDatabase->Find ( _T ( "GREY" ) ) ); StyleSetForeground ( wxSTC_CSS_ID, *wxBLACK ); StyleSetForeground ( wxSTC_CSS_IMPORTANT, wxTheColourDatabase->Find ( _T ( "ORANGE" ) ) ); StyleSetForeground ( wxSTC_CSS_DIRECTIVE, wxTheColourDatabase->Find ( _T ( "SKY BLUE" ) ) ); StyleSetForeground ( wxSTC_CSS_DOUBLESTRING, wxTheColourDatabase->Find ( _T ( "ORANGE" ) ) ); StyleSetForeground ( wxSTC_CSS_SINGLESTRING, wxTheColourDatabase->Find ( _T ( "ORANGE" ) ) ); StyleSetForeground ( wxSTC_CSS_IDENTIFIER2, wxTheColourDatabase->Find ( _T ( "ORANGE" ) ) ); StyleSetForeground ( wxSTC_CSS_ATTRIBUTE, wxTheColourDatabase->Find ( _T ( "SKY BLUE" ) ) ); } else // XML { StyleSetForeground ( wxSTC_H_DEFAULT, *wxBLACK ); StyleSetForeground ( wxSTC_H_TAG, wxTheColourDatabase->Find ( _T ( "SKY BLUE" ) ) ); StyleSetForeground ( wxSTC_H_TAGUNKNOWN, wxTheColourDatabase->Find ( _T ( "SKY BLUE" ) ) ); StyleSetForeground ( wxSTC_H_ATTRIBUTE, wxTheColourDatabase->Find ( _T ( "ORANGE" ) ) ); StyleSetForeground ( wxSTC_H_ATTRIBUTEUNKNOWN, wxTheColourDatabase->Find ( _T ( "ORANGE" ) ) ); StyleSetForeground ( wxSTC_H_NUMBER, wxTheColourDatabase->Find ( _T ( "GREY" ) ) ); StyleSetForeground ( wxSTC_H_DOUBLESTRING, wxTheColourDatabase->Find ( _T ( "GREY" ) ) ); StyleSetForeground ( wxSTC_H_SINGLESTRING, wxTheColourDatabase->Find ( _T ( "GREY" ) ) ); StyleSetForeground ( wxSTC_H_OTHER, wxTheColourDatabase->Find ( _T ( "SKY BLUE" ) ) ); StyleSetForeground ( wxSTC_H_COMMENT, wxTheColourDatabase->Find ( _T ( "GREY" ) ) ); StyleSetForeground ( wxSTC_H_ENTITY, wxTheColourDatabase->Find ( _T ( "ORANGE" ) ) ); StyleSetForeground ( wxSTC_H_TAGEND, wxTheColourDatabase->Find ( _T ( "SKY BLUE" ) ) ); StyleSetForeground ( wxSTC_H_XMLSTART, wxTheColourDatabase->Find ( _T ( "GREY" ) ) ); StyleSetForeground ( wxSTC_H_XMLEND, wxTheColourDatabase->Find ( _T ( "GREY" ) ) ); StyleSetForeground ( wxSTC_H_CDATA, wxTheColourDatabase->Find ( _T ( "ORANGE" ) ) ); StyleSetForeground ( wxSTC_H_QUESTION, wxTheColourDatabase->Find ( _T ( "GREY" ) ) ); for ( int i = wxSTC_H_SGML_DEFAULT; i <= wxSTC_H_SGML_BLOCK_DEFAULT; i++ ) StyleSetForeground ( i, wxTheColourDatabase->Find ( _T ( "SKY BLUE" ) ) ); StyleSetForeground ( wxSTC_H_SGML_ENTITY, wxTheColourDatabase->Find ( _T ( "ORANGE" ) ) ); StyleSetForeground ( wxSTC_H_SGML_SPECIAL, *wxBLACK ); StyleSetForeground ( wxSTC_H_SGML_SIMPLESTRING, wxTheColourDatabase->Find ( _T ( "ORANGE" ) ) ); StyleSetForeground ( wxSTC_H_SGML_DEFAULT, wxTheColourDatabase->Find ( _T ( "GREY" ) ) ); } break; case COLOR_SCHEME_REDUCED_GLARE: StyleSetForeground ( wxSTC_STYLE_DEFAULT, wxTheColourDatabase->Find ( _T ( "LIGHT GREY" ) ) ); StyleSetBackground ( wxSTC_STYLE_DEFAULT, wxTheColourDatabase->Find ( _T ( "MIDNIGHT BLUE" ) ) ); StyleClearAll(); baseBackground = LightColour ( wxTheColourDatabase->Find ( _T ( "NAVY" ) ), 0 ); alternateBackground = LightColour ( wxTheColourDatabase->Find ( _T ( "NAVY" ) ), 10 ); SetCaretLineBackground ( baseBackground ); StyleSetForeground ( wxSTC_STYLE_LINENUMBER, *wxBLACK ); SetCaretForeground ( *wxWHITE ); SetSelBackground ( true, wxTheColourDatabase->Find ( _T ( "GREY" ) ) ); if ( type == FILE_TYPE_CSS ) { // CSS colours StyleSetForeground ( wxSTC_CSS_DEFAULT, wxTheColourDatabase->Find ( _T ( "LIGHT GREY" ) ) ); StyleSetForeground ( wxSTC_CSS_TAG, wxTheColourDatabase->Find ( _T ( "SKY BLUE" ) ) ); StyleSetForeground ( wxSTC_CSS_CLASS, wxTheColourDatabase->Find ( _T ( "SKY BLUE" ) ) ); StyleSetForeground ( wxSTC_CSS_PSEUDOCLASS, wxTheColourDatabase->Find ( _T ( "SKY BLUE" ) ) ); StyleSetForeground ( wxSTC_CSS_UNKNOWN_PSEUDOCLASS, wxTheColourDatabase->Find ( _T ( "SKY BLUE" ) ) ); StyleSetForeground ( wxSTC_CSS_OPERATOR, wxTheColourDatabase->Find ( _T ( "SKY BLUE" ) ) ); StyleSetForeground ( wxSTC_CSS_IDENTIFIER, wxTheColourDatabase->Find ( _T ( "SKY BLUE" ) ) ); StyleSetForeground ( wxSTC_CSS_UNKNOWN_IDENTIFIER, wxTheColourDatabase->Find ( _T ( "SKY BLUE" ) ) ); StyleSetForeground ( wxSTC_CSS_VALUE, *wxWHITE ); StyleSetForeground ( wxSTC_CSS_COMMENT, wxTheColourDatabase->Find ( _T ( "LIGHT GREY" ) ) ); StyleSetForeground ( wxSTC_CSS_ID, *wxWHITE ); StyleSetForeground ( wxSTC_CSS_IMPORTANT, wxTheColourDatabase->Find ( _T ( "ORANGE" ) ) ); StyleSetForeground ( wxSTC_CSS_DIRECTIVE, wxTheColourDatabase->Find ( _T ( "SKY BLUE" ) ) ); StyleSetForeground ( wxSTC_CSS_DOUBLESTRING, wxTheColourDatabase->Find ( _T ( "ORANGE" ) ) ); StyleSetForeground ( wxSTC_CSS_SINGLESTRING, wxTheColourDatabase->Find ( _T ( "ORANGE" ) ) ); StyleSetForeground ( wxSTC_CSS_IDENTIFIER2, wxTheColourDatabase->Find ( _T ( "ORANGE" ) ) ); StyleSetForeground ( wxSTC_CSS_ATTRIBUTE, wxTheColourDatabase->Find ( _T ( "SKY BLUE" ) ) ); } else // XML { StyleSetForeground ( wxSTC_H_DEFAULT, *wxWHITE ); StyleSetForeground ( wxSTC_H_TAG, wxTheColourDatabase->Find ( _T ( "SKY BLUE" ) ) ); StyleSetForeground ( wxSTC_H_TAGUNKNOWN, wxTheColourDatabase->Find ( _T ( "SKY BLUE" ) ) ); StyleSetForeground ( wxSTC_H_ATTRIBUTE, wxTheColourDatabase->Find ( _T ( "ORANGE" ) ) ); StyleSetForeground ( wxSTC_H_ATTRIBUTEUNKNOWN, wxTheColourDatabase->Find ( _T ( "ORANGE" ) ) ); StyleSetForeground ( wxSTC_H_NUMBER, wxTheColourDatabase->Find ( _T ( "LIGHT GREY" ) ) ); StyleSetForeground ( wxSTC_H_DOUBLESTRING, wxTheColourDatabase->Find ( _T ( "LIGHT GREY" ) ) ); StyleSetForeground ( wxSTC_H_SINGLESTRING, wxTheColourDatabase->Find ( _T ( "LIGHT GREY" ) ) ); StyleSetForeground ( wxSTC_H_OTHER, wxTheColourDatabase->Find ( _T ( "SKY BLUE" ) ) ); StyleSetForeground ( wxSTC_H_COMMENT, wxTheColourDatabase->Find ( _T ( "LIGHT GREY" ) ) ); StyleSetForeground ( wxSTC_H_ENTITY, wxTheColourDatabase->Find ( _T ( "ORANGE" ) ) ); StyleSetForeground ( wxSTC_H_TAGEND, wxTheColourDatabase->Find ( _T ( "SKY BLUE" ) ) ); StyleSetForeground ( wxSTC_H_XMLSTART, wxTheColourDatabase->Find ( _T ( "LIGHT GREY" ) ) ); StyleSetForeground ( wxSTC_H_XMLEND, wxTheColourDatabase->Find ( _T ( "LIGHT GREY" ) ) ); StyleSetForeground ( wxSTC_H_CDATA, wxTheColourDatabase->Find ( _T ( "ORANGE" ) ) ); StyleSetForeground ( wxSTC_H_QUESTION, wxTheColourDatabase->Find ( _T ( "LIGHT GREY" ) ) ); for ( int i = wxSTC_H_SGML_DEFAULT; i <= wxSTC_H_SGML_BLOCK_DEFAULT; i++ ) StyleSetForeground ( i, wxTheColourDatabase->Find ( _T ( "SKY BLUE" ) ) ); StyleSetForeground ( wxSTC_H_SGML_ENTITY, wxTheColourDatabase->Find ( _T ( "ORANGE" ) ) ); StyleSetForeground ( wxSTC_H_SGML_SPECIAL, *wxWHITE ); StyleSetForeground ( wxSTC_H_SGML_SIMPLESTRING, wxTheColourDatabase->Find ( _T ( "ORANGE" ) ) ); StyleSetForeground ( wxSTC_H_SGML_DEFAULT, wxTheColourDatabase->Find ( _T ( "LIGHT GREY" ) ) ); } break; case COLOR_SCHEME_NONE: baseBackground = LightColour ( wxTheColourDatabase->Find ( _T ( "CYAN" ) ), 75 ); alternateBackground = LightColour ( wxTheColourDatabase->Find ( _T ( "CYAN" ) ), 95 ); SetCaretLineBackground ( baseBackground ); SetSelBackground ( true, wxTheColourDatabase->Find ( _T ( "LIGHT GREY" ) ) ); SetCaretForeground ( *wxBLACK ); StyleSetForeground ( wxSTC_STYLE_DEFAULT, *wxBLACK ); StyleSetBackground ( wxSTC_STYLE_DEFAULT, *wxWHITE ); StyleClearAll(); break; default: break; } if ( type == FILE_TYPE_CSS ) { StyleSetBold ( wxSTC_CSS_CLASS, true ); StyleSetBold ( wxSTC_CSS_PSEUDOCLASS, true ); StyleSetBold ( wxSTC_CSS_UNKNOWN_PSEUDOCLASS, true ); StyleSetBold ( wxSTC_CSS_DOUBLESTRING, true ); StyleSetBold ( wxSTC_CSS_SINGLESTRING, true ); StyleSetBold ( wxSTC_CSS_DIRECTIVE, true ); } else { StyleSetBold ( wxSTC_H_XMLSTART, true ); StyleSetBold ( wxSTC_H_XMLEND, true ); StyleSetBold ( wxSTC_H_ATTRIBUTEUNKNOWN, true ); StyleSetBold ( wxSTC_H_ENTITY, true ); StyleSetBold ( wxSTC_H_QUESTION, true ); StyleSetBold ( wxSTC_H_SGML_ENTITY, true ); StyleSetBold ( wxSTC_H_SGML_DEFAULT, true ); } applyVisibilityState ( visibilityState ); } void XmlCtrl::applyVisibilityState ( int state ) { if ( type == FILE_TYPE_BINARY ) return; visibilityState = state; bool visible; // hide tags/attributes visible = ( state == HIDE_ATTRIBUTES || state == HIDE_TAGS ) ? false : true; StyleSetVisible ( wxSTC_H_OTHER, visible ); StyleSetVisible ( wxSTC_H_ATTRIBUTE, visible ); StyleSetVisible ( wxSTC_H_ATTRIBUTEUNKNOWN, visible ); StyleSetVisible ( wxSTC_H_DOUBLESTRING, visible ); StyleSetVisible ( wxSTC_H_SINGLESTRING, visible ); // hide tags visible = ( state == HIDE_TAGS ) ? false : true; StyleSetVisible ( wxSTC_H_TAG, visible ); StyleSetVisible ( wxSTC_H_TAGUNKNOWN, visible ); StyleSetVisible ( wxSTC_H_TAGEND, visible ); StyleSetVisible ( wxSTC_H_XMLSTART, visible ); StyleSetVisible ( wxSTC_H_XMLEND, visible ); StyleSetVisible ( wxSTC_H_CDATA, visible ); StyleSetVisible ( wxSTC_H_QUESTION, visible ); StyleSetVisible ( wxSTC_H_COMMENT, visible ); for ( int i = wxSTC_H_SGML_DEFAULT; i <= wxSTC_H_SGML_BLOCK_DEFAULT; i++ ) StyleSetVisible ( i, visible ); Colourise ( 0, -1 ); } int XmlCtrl::getType() { return type; } void XmlCtrl::foldAll() { expandFoldsToLevel ( 1, false ); } void XmlCtrl::unfoldAll() { expandFoldsToLevel ( wxSTC_FOLDLEVELNUMBERMASK, true ); } void XmlCtrl::toggleFold() { int pos, line, level; pos = GetCurrentPos(); if ( pos == -1 ) return; line = LineFromPosition ( pos ); level = GetFoldLevel ( line ); for ( int iterator = line; iterator > 0; iterator-- ) { if ( iterator != line ) { GotoLine ( iterator ); level = GetFoldLevel ( iterator ); } if ( XMLCTRL_HASBIT ( level, wxSTC_FOLDLEVELHEADERFLAG ) ) { ToggleFold ( iterator ); break; } } } // adapted from wxSTEdit (c) 2005 John Labenski, Otto Wyss void XmlCtrl::expandFoldsToLevel ( int level, bool expand ) { Colourise ( 0, -1 ); const int line_n = GetLineCount(); for ( int n = 0; n < line_n; n++ ) { int line_level = GetFoldLevel ( n ); if ( XMLCTRL_HASBIT ( line_level, wxSTC_FOLDLEVELHEADERFLAG ) ) { line_level -= wxSTC_FOLDLEVELBASE; line_level &= wxSTC_FOLDLEVELNUMBERMASK; if ( ( ( expand && ( line_level <= level ) ) || ( !expand && ( line_level >= level ) ) ) && ( GetFoldExpanded ( n ) != expand ) ) ToggleFold ( n ); } } EnsureCaretVisible(); // seems to keep it in nearly the same place } void XmlCtrl::protectHeadLine() { if ( *protectTags || !properties.fold ) return; int pos1, pos2, line1, line2, level; pos1 = GetSelectionStart(); pos2 = GetSelectionEnd(); line1 = LineFromPosition ( pos1 ); line2 = LineFromPosition ( pos2 ); if ( line2 < line1 ) { int temp = line1; line1 = line2; line2 = temp; } for ( int i = line1; i <= line2; i++ ) { level = GetFoldLevel ( i ); if ( XMLCTRL_HASBIT ( level, wxSTC_FOLDLEVELHEADERFLAG ) && !GetFoldExpanded ( i ) ) ToggleFold ( i ); } } wxString XmlCtrl::getOpenTag ( const wxString& element ) { wxString openTag; openTag = _T ( "<" ) + element; std::set requiredAttributeSet; std::set::iterator it; requiredAttributeSet = requiredAttributeMap[element]; if ( !requiredAttributeSet.empty() ) { for ( it = requiredAttributeSet.begin(); it != requiredAttributeSet.end(); ++it ) { openTag += _T ( " " ); openTag += *it; openTag += _T ( "=\"\"" ); } } openTag += _T ( ">" ); return openTag; } bool XmlCtrl::insertChild ( const wxString& child ) { int start, end; start = GetSelectionStart(); end = GetSelectionEnd(); int offset; wxString openTag, closeTag; openTag = getOpenTag ( child ); closeTag = _T ( "" ); if ( start == end ) { if ( !canInsertAt ( start ) ) return false; offset = openTag.Length(); wxString tag; tag = openTag + closeTag; InsertText ( start, tag ); SetSelection ( start + offset, start + offset ); SetFocus(); return true; } if ( *protectTags ) adjustSelection(); offset = openTag.Length(); if ( start > end ) { int temp = end; start = end; end = temp; } InsertText ( start, openTag ); InsertText ( end + offset, closeTag ); SetSelection ( start + offset, end + offset ); return true; } bool XmlCtrl::insertSibling ( const wxString& sibling, const wxString& parent ) { int start = GetSelectionStart(); int limit = GetLength(); wxString parentCloseTag = _T ( "" ); int parentCloseTagStart = FindText ( start, limit, parentCloseTag ); if ( parentCloseTagStart == -1 || !canInsertAt ( parentCloseTagStart + parentCloseTag.Length() ) ) return false; int insertionPoint = parentCloseTagStart + parentCloseTag.Length(); SetSelection ( insertionPoint, insertionPoint ); insertNewLine(); wxString openTag, closeTag; int newCurrentPos, newAdjustedPos; newCurrentPos = GetCurrentPos(); openTag = getOpenTag ( sibling ); closeTag = _T ( "" ); InsertText ( newCurrentPos, openTag + closeTag ); newAdjustedPos = newCurrentPos + openTag.Length(); SetSelection ( newAdjustedPos, newAdjustedPos ); return true; } bool XmlCtrl::insertEntity ( const wxString& entity ) { if ( *protectTags ) adjustCursor(); wxString insertBuffer; insertBuffer.Printf ( _T ( "&%s;" ), entity.c_str() ); int pos = GetCurrentPos(); InsertText ( GetCurrentPos(), insertBuffer ); pos += insertBuffer.size(); SetSelection ( pos, pos ); return true; } wxString XmlCtrl::getParent() { int current, parentCloseAngleBracket; current = GetCurrentPos(); parentCloseAngleBracket = getParentCloseAngleBracket ( current ); return getLastElementName ( parentCloseAngleBracket ); } void XmlCtrl::insertNewLine() { bool autoindent = false; int pos, line, startPos, iteratorPos, newPos; pos = GetCurrentPos(); line = LineFromPosition ( pos ); startPos = PositionFromLine ( line ); iteratorPos = startPos; for ( iteratorPos = startPos; ( GetCharAt ( iteratorPos ) == ' ' || GetCharAt ( iteratorPos ) == '\t' ) && iteratorPos < pos; ++iteratorPos ) autoindent = true; wxString s = GetTextRange ( startPos, iteratorPos ); NewLine(); if ( autoindent ) { newPos = PositionFromLine ( line + 1 ); InsertText ( newPos, s ); SetSelection ( newPos + s.size(), newPos + s.size() ); } } void XmlCtrl::toggleLineBackground() { if ( !properties.toggleLineBackground || visibilityState != HIDE_TAGS ) { if ( lineBackgroundState != BACKGROUND_STATE_NORMAL ) { SetCaretLineBackground ( baseBackground ); lineBackgroundState = BACKGROUND_STATE_NORMAL; } return; } lineBackgroundState = ( lineBackgroundState == BACKGROUND_STATE_NORMAL ) ? BACKGROUND_STATE_LIGHT : BACKGROUND_STATE_NORMAL; SetCaretLineBackground ( ( lineBackgroundState == BACKGROUND_STATE_NORMAL ) ? baseBackground : alternateBackground ); } const std::set &XmlCtrl::getEntitySet() { return entitySet; } const std::set &XmlCtrl::getAttributes ( const wxString& parent ) { static std::set retVal; return retVal; } wxString XmlCtrl::getElementStructure ( const wxString& element ) { if ( elementStructureMap.find ( element ) == elementStructureMap.end() ) { return wxEmptyString; } return elementStructureMap[element]; } bool XmlCtrl::backgroundValidate() { if ( !properties.validateAsYouType || type != FILE_TYPE_XML ) return true; std::string bufferUtf8 = myGetTextRaw(); return backgroundValidate ( bufferUtf8.c_str(), basePath, bufferUtf8.size() ); } bool XmlCtrl::backgroundValidate ( const char *buffer, const wxString &system, size_t bufferLen ) { if ( !validationRequired ) return true; wxCriticalSectionLocker locker ( xmlcopyeditorCriticalSection ); if ( validationThread != NULL ) { return true; // wait for next idle cycle call from main app frame } validationRequired = false; validationThread = new ValidationThread( GetEventHandler(), buffer, system ); if ( validationThread->Create() != wxTHREAD_NO_ERROR || validationThread->Run() != wxTHREAD_NO_ERROR ) { delete validationThread; validationThread = NULL; return false; } return true; } std::string XmlCtrl::myGetTextRaw() { return ( const char * ) GetTextRaw(); } void XmlCtrl::setErrorIndicator ( int line, int column ) { int startPos, endPos, endStyled; startPos = PositionFromLine ( line ) + column; endPos = GetLineEndPosition ( line ); endStyled = GetEndStyled(); if ( endPos > endStyled ) endPos = endStyled; int length = endPos - startPos; if ( length > 0 && length + startPos < GetLength() ) { #if wxCHECK_VERSION(2,9,0) IndicatorFillRange ( startPos, length ); #else StartStyling ( startPos, wxSTC_INDIC2_MASK ); SetStyling ( length, wxSTC_INDIC2_MASK ); #endif } } void XmlCtrl::clearErrorIndicators ( int maxLine ) { if ( maxLine < 0 ) return; int length = GetLength(); if ( !length ) return; int end = GetEndStyled(); length = ( maxLine ) ? GetLineEndPosition ( maxLine ) : length; if ( end > 0 && length > end ) length = end; #if wxCHECK_VERSION(2,9,0) IndicatorClearRange ( 0, length ); #else StartStyling ( 0, wxSTC_INDIC2_MASK ); SetStyling ( length, 0 ); #endif } bool XmlCtrl::getValidationRequired() { return validationRequired; } void XmlCtrl::setValidationRequired ( bool b ) { validationRequired = b; } int XmlCtrl::getTagType ( int pos ) { int iteratorPos; // preliminary checks if ( pos < 2 ) { return TAG_TYPE_ERROR; } else if ( GetCharAt ( pos - 1 ) == '/' ) { return TAG_TYPE_EMPTY; } // go to start of tag for ( iteratorPos = pos; iteratorPos >= 0; --iteratorPos ) { int style = getLexerStyleAt ( iteratorPos ); //style &= ~wxSTC_INDIC2_MASK; if ( GetCharAt ( iteratorPos ) == '<' && ( style == wxSTC_H_TAG || style == wxSTC_H_TAGUNKNOWN ) ) break; } if ( GetCharAt ( iteratorPos ) != '<' ) return TAG_TYPE_ERROR; char c = GetCharAt ( iteratorPos + 1 ); if ( c == '!' || c == '?' ) return TAG_TYPE_OTHER; else if ( c == '/' ) { return TAG_TYPE_CLOSE; } else { return TAG_TYPE_OPEN; } } // fetch style int disregarding indicator int XmlCtrl::getLexerStyleAt ( int pos ) { int style = GetStyleAt ( pos ); #if !wxCHECK_VERSION(2,9,0) style &= ~wxSTC_INDIC2_MASK; #endif return style; } bool XmlCtrl::getGrammarFound() { return grammarFound; } void XmlCtrl::OnMiddleDown ( wxMouseEvent& event ) { if ( GetSelectionStart() == GetSelectionEnd() ) { event.Skip(); return; } Copy(); long x, y; event.GetPosition ( &x, &y ); int pastePosition = PositionFromPointClose ( x, y ); if ( pastePosition == wxSTC_INVALID_POSITION ) { event.Skip(); return; } SetSelection ( pastePosition, pastePosition ); Paste(); } void XmlCtrl::toggleComment() { int pos = -1; wxString text = GetSelectedText(); if ( text.IsEmpty() ) { if ( type == FILE_TYPE_BINARY ) return; pos = GetCurrentPos(); Colourise ( 0, -1 ); int style = getLexerStyleAt ( pos ) ; if ( style == wxSTC_H_COMMENT ) { int i = pos; while ( --i >= 0 && getLexerStyleAt ( i ) == wxSTC_H_COMMENT ) continue; SetSelectionStart ( i + 1 ); int styled = GetEndStyled(); i = pos; while ( i < styled && getLexerStyleAt ( i ) == wxSTC_H_COMMENT ) i++; SetSelectionEnd ( i ); } else { // Select current tag int start = findPreviousStartTag ( pos, 1, '<', pos ); if ( start < 0 ) { wxMessageBox(_T("Cann't find the start tag")); return; } int range = GetTextLength() - pos; int end = findNextEndTag ( pos, 1, '>', range ); if ( end < 0 ) { wxMessageBox(_T("Cann't find the end tag")); return; } SetSelection ( start, end ); } text = GetSelectedText(); } // Skip leading spaces wxString::iterator itr, start, end; itr = start = text.begin(); end = text.end(); while ( itr != end && wxIsspace ( *itr ) ) ++itr; const static wxString commentStart = _T ( "" ); size_t startPos = itr - start; int ret = text.compare ( startPos, commentStart.length(), commentStart ); if ( ret == 0 ) { start = itr; itr = end; do { --itr; } while ( itr != start && wxIsspace ( *itr ) ); size_t endPos = itr - start; if ( endPos > commentEnd.length() ) { endPos = itr - text.begin() - commentEnd.length() + 1; ret = text.compare ( endPos, commentEnd.length(), commentEnd ); // Is commented? if ( ret == 0 ) { text.erase ( endPos, commentEnd.length() ); text.erase ( startPos, commentStart.length() ); ReplaceSelection ( text ); pos -= commentStart.length(); if ( pos >= 0 ) SetSelection ( pos, pos ); return; } } } // Comment selection // "--" is not allowed in comments const static wxString doubleHyphen = _T ( "--" ); size_t offset = 0; while ( ( offset = text.find ( doubleHyphen, offset ) ) != wxString::npos ) { text.replace ( offset, doubleHyphen.length(), _T ( "- -" ) ); offset += 2; // WARNING: Not three! } text = commentStart + text + commentEnd; ReplaceSelection ( text ); if ( pos >= 0 ) { pos += commentStart.length(); SetSelection ( pos, pos ); } }