diff --git a/ChangeLog b/ChangeLog index 094a0b3..25c6c2b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,5 @@ 1.2.1.1 + + Feature #25 Fast commenting + x64 installation package * Fixed tab order * Fixed encoding problems diff --git a/src/xmlcopyeditor.cpp b/src/xmlcopyeditor.cpp index c6c18bd..bb52e50 100644 --- a/src/xmlcopyeditor.cpp +++ b/src/xmlcopyeditor.cpp @@ -131,6 +131,7 @@ BEGIN_EVENT_TABLE ( MyFrame, wxFrame ) EVT_MENU ( ID_FIND, MyFrame::OnFind ) EVT_MENU ( ID_FIND_AGAIN, MyFrame::OnFindAgain ) EVT_MENU ( ID_GOTO, MyFrame::OnGoto ) + EVT_MENU ( ID_TOGGLE_COMMENT, MyFrame::OnToggleComment ) EVT_MENU ( ID_FEEDBACK, MyFrame::OnFeedback ) EVT_MENU ( ID_PREVIOUS_DOCUMENT, MyFrame::OnPreviousDocument ) EVT_MENU ( ID_NEXT_DOCUMENT, MyFrame::OnNextDocument ) @@ -2799,6 +2800,16 @@ void MyFrame::OnGlobalReplace ( wxCommandEvent& event ) statusProgress ( msg ); } +void MyFrame::OnToggleComment ( wxCommandEvent& event ) +{ + XmlDoc *doc = getActiveDocument(); + if ( doc == NULL ) + return; + + wxBusyCursor cursor; + doc->toggleComment(); +} + void MyFrame::OnFrameClose ( wxCloseEvent& event ) { wxCommandEvent e; @@ -4936,6 +4947,10 @@ wxMenuBar *MyFrame::getMenuBar() new wxMenuItem ( NULL, ID_GOTO, _ ( "G&o To...\tCtrl+G" ), _ ( "Go To..." ) ); gotoItem->SetBitmap ( wxNullBitmap ); + wxMenuItem *commentItem = + new wxMenuItem ( NULL, ID_TOGGLE_COMMENT, _ ( "&Toggle Comment\tCtrl+/" ), _ ( "Toggle Comment" ) ); + commentItem->SetBitmap ( wxNullBitmap ); + editMenu->Append ( undoItem ); editMenu->Append ( redoItem ); editMenu->AppendSeparator(); @@ -4948,8 +4963,9 @@ wxMenuBar *MyFrame::getMenuBar() editMenu->Append ( findAgainItem ); editMenu->Append ( replaceItem ); editMenu->Append ( globalReplaceItem ); - editMenu->AppendSeparator(); editMenu->Append ( gotoItem ); + editMenu->AppendSeparator(); + editMenu->Append ( commentItem ); #ifndef __WXMSW__ wxMenuItem *preferencesItem = diff --git a/src/xmlcopyeditor.h b/src/xmlcopyeditor.h index 8e1bdc7..c155877 100644 --- a/src/xmlcopyeditor.h +++ b/src/xmlcopyeditor.h @@ -109,6 +109,7 @@ enum ID_FIND, ID_FIND_AGAIN, ID_GOTO, + ID_TOGGLE_COMMENT, ID_PRINT, ID_WORD_COUNT, ID_PRINT_PREVIEW, @@ -235,6 +236,7 @@ class MyFrame : public wxFrame void OnFindReplace ( wxCommandEvent& event ); void OnCommand ( wxCommandEvent& event ); void OnGlobalReplace ( wxCommandEvent& event ); + void OnToggleComment ( wxCommandEvent& event ); void OnWordCount ( wxCommandEvent& event ); void OnFeedback ( wxCommandEvent& event ); void OnSplitTab ( wxCommandEvent& event ); diff --git a/src/xmlctrl.cpp b/src/xmlctrl.cpp index dc549e8..3197109 100644 --- a/src/xmlctrl.cpp +++ b/src/xmlctrl.cpp @@ -973,40 +973,102 @@ wxString XmlCtrl::getLastAttributeName ( int pos ) return GetTextRange ( startPos + 1, pos ); } -int XmlCtrl::getParentCloseAngleBracket ( int pos, int range ) +int XmlCtrl::findNextEndTag ( + int pos, + unsigned depth /*= 1*/, + int character /*= '>'*/, + int range /*= USHRT_MAX * 4*/ ) { - int cutoff, iteratorPos, depth; - cutoff = ( ( pos - range ) > 2 ) ? pos - range : 2; - depth = 1; - for ( - iteratorPos = pos; - iteratorPos > cutoff; - --iteratorPos ) - { - int type, style; - style = getLexerStyleAt ( iteratorPos ); + wxASSERT ( character == '<' || character == '>' ); - if ( GetCharAt ( iteratorPos ) == '>' && - ( style == wxSTC_H_TAG || - style == wxSTC_H_TAGUNKNOWN ) ) + 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 ) { - type = getTagType ( iteratorPos ); + int type = getTagType ( pos ); switch ( type ) { - case ( TAG_TYPE_CLOSE ) : - ++depth; - break; - case ( TAG_TYPE_OPEN ) : - --depth; - break; - case ( TAG_TYPE_EMPTY ) : - case ( TAG_TYPE_OTHER ) : - case ( TAG_TYPE_ERROR ) : - break; + 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 iteratorPos; } + + 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; } @@ -2119,3 +2181,111 @@ void XmlCtrl::OnMiddleDown ( wxMouseEvent& event ) 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 offset = itr - start; + int ret = text.compare ( offset, commentStart.length(), commentStart ); + if ( ret == 0 ) + { + start = itr; + itr = end; + do { + --itr; + } while ( itr != start && wxIsspace ( *itr ) ); + + offset = itr - start; + if ( offset > commentEnd.length() ) + { + offset = itr - text.begin() - commentEnd.length() + 1; + ret = text.compare ( offset, commentEnd.length(), commentEnd ); + + // Is commented? + if ( ret == 0 ) + { + text.erase ( offset, commentEnd.length() ); + text.erase ( start, start + commentStart.length() ); + + ReplaceSelection ( text ); + if ( pos >= 0 ) + { + pos -= commentStart.length(); + SetEmptySelection ( pos >= 0 ? pos : 0 ); + } + return; + } + } + } + + // Comment selection + + // "--" is not allowed in comments + const static wxString doubleHyphen = _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 ) + SetEmptySelection ( pos + commentStart.length() ); +} diff --git a/src/xmlctrl.h b/src/xmlctrl.h index af9fb15..7b90ec0 100644 --- a/src/xmlctrl.h +++ b/src/xmlctrl.h @@ -109,7 +109,20 @@ class XmlCtrl: public wxStyledTextCtrl long style = 0 ); ~XmlCtrl(); int getType(); - int getParentCloseAngleBracket ( int pos, int range = USHRT_MAX * 4 ); + int getParentCloseAngleBracket ( int pos ) + { + return findPreviousStartTag ( pos, 1, '>' ); + } + int findNextEndTag ( + int pos, + unsigned depth = 1, + int character = '>', + int range = USHRT_MAX * 4 ); + int findPreviousStartTag ( + int pos, + unsigned depth = 1, + int character = '<', + int range = USHRT_MAX * 4 ); void applyProperties ( const XmlCtrlProperties &propertiesParameter, bool zoomOnly = false ); @@ -144,6 +157,7 @@ class XmlCtrl: public wxStyledTextCtrl std::string myGetTextRaw(); // alternative to faulty stc implementation bool getValidationRequired(); void setValidationRequired ( bool b ); + void toggleComment(); private: ValidationThread *validationThread; // used for background validation