To throw in some code... The following code seems to work as an example:
Code: Select all
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
// for all others, include the necessary headers (this file is usually all you
// need because it includes almost all "standard" wxWidgets headers)
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include <wx/stc/stc.h>
#include <vector>
#include <utility>
#include <algorithm>
#define MY_FOLDMARGIN 2
class myFrame : public wxFrame {
//be sure to enable the C++11 standard for this example if you're using GCC
public:
myFrame( wxWindow* parent, int id = wxID_ANY, wxString title = "Demo"
, wxPoint pos = wxDefaultPosition, wxSize size = wxSize(481,466)
, int style = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL );
private:
void onMarginClick(wxStyledTextEvent& event);
void OnStyleNeeded(wxStyledTextEvent& event);
void highlightSTCsyntax(size_t fromPos,size_t toPos,wxString &text);
void setfoldlevels(size_t fromPos,int startfoldlevel,wxString &text);
wxStyledTextCtrl* m_activeSTC;
wxColor m_GCodecolor{255,130,0}; //color for highlighted parts
//this is the mask for styling- just remember to set this to 0 with >3.1.0
int m_stylemask=255; //for wxwidgets >3.1.0 set this to 0
};
myFrame::myFrame( wxWindow* parent, int id, wxString title, wxPoint pos
, wxSize size, int style )
:wxFrame( parent, id, title, pos, size, style ) {
m_activeSTC = new wxStyledTextCtrl(this);
//set Lexer to LEX_CONTAINER: This will trigger the styleneeded event so you can do your own highlighting
m_activeSTC->SetLexer(wxSTC_LEX_CONTAINER);
//folding example by New Pagodi copied from WxWidgets Forum
//Set the fold marging to have a width of 14 pixels and give it a
//distinctive background
m_activeSTC->SetMarginWidth(MY_FOLDMARGIN,14);
m_activeSTC->SetMarginMask(MY_FOLDMARGIN,wxSTC_MASK_FOLDERS);
m_activeSTC->SetFoldMarginColour(true,wxColor(255,255,255));
m_activeSTC->SetFoldMarginHiColour(true,wxColor(233,233,233));
//Set up the markers that will be shown in the fold margin
m_activeSTC->MarkerDefine(wxSTC_MARKNUM_FOLDEREND,wxSTC_MARK_BOXPLUSCONNECTED);
m_activeSTC->MarkerSetForeground(wxSTC_MARKNUM_FOLDEREND,wxColor(243,243,243));
m_activeSTC->MarkerSetBackground(wxSTC_MARKNUM_FOLDEREND,wxColor(128,128,128));
m_activeSTC->MarkerDefine(wxSTC_MARKNUM_FOLDEROPENMID,wxSTC_MARK_BOXMINUSCONNECTED);
m_activeSTC->MarkerSetForeground(wxSTC_MARKNUM_FOLDEROPENMID,wxColor(243,243,243));
m_activeSTC->MarkerSetBackground(wxSTC_MARKNUM_FOLDEROPENMID,wxColor(128,128,128));
m_activeSTC->MarkerDefine(wxSTC_MARKNUM_FOLDERMIDTAIL, wxSTC_MARK_TCORNER);
m_activeSTC->MarkerSetForeground(wxSTC_MARKNUM_FOLDERMIDTAIL,wxColor(243,243,243));
m_activeSTC->MarkerSetBackground(wxSTC_MARKNUM_FOLDERMIDTAIL,wxColor(128,128,128));
m_activeSTC->MarkerDefine(wxSTC_MARKNUM_FOLDERTAIL,wxSTC_MARK_LCORNER);
m_activeSTC->MarkerSetForeground(wxSTC_MARKNUM_FOLDERTAIL,wxColor(243,243,243));
m_activeSTC->MarkerSetBackground(wxSTC_MARKNUM_FOLDERTAIL,wxColor(128,128,128));
m_activeSTC->MarkerDefine(wxSTC_MARKNUM_FOLDERSUB,wxSTC_MARK_VLINE);
m_activeSTC->MarkerSetForeground(wxSTC_MARKNUM_FOLDERSUB,wxColor(243,243,243));
m_activeSTC->MarkerSetBackground(wxSTC_MARKNUM_FOLDERSUB,wxColor(128,128,128));
m_activeSTC->MarkerDefine(wxSTC_MARKNUM_FOLDER,wxSTC_MARK_BOXPLUS);
m_activeSTC->MarkerSetForeground(wxSTC_MARKNUM_FOLDER,wxColor(243,243,243));
m_activeSTC->MarkerSetBackground(wxSTC_MARKNUM_FOLDER,wxColor(128,128,128));
m_activeSTC->MarkerDefine(wxSTC_MARKNUM_FOLDEROPEN,wxSTC_MARK_BOXMINUS);
m_activeSTC->MarkerSetForeground(wxSTC_MARKNUM_FOLDEROPEN,wxColor(243,243,243));
m_activeSTC->MarkerSetBackground(wxSTC_MARKNUM_FOLDEROPEN,wxColor(128,128,128));
//Turn the fold markers red when the caret is a line in the group (optional)
m_activeSTC->MarkerEnableHighlight(true);
//The margin will only respond to clicks if it set sensitive. Also, connect
//the event handler that will do the collapsing/restoring
m_activeSTC->SetMarginSensitive(MY_FOLDMARGIN,true);
m_activeSTC->Bind(wxEVT_STC_MARGINCLICK, &myFrame::onMarginClick, this);
m_activeSTC->Bind(wxEVT_STC_STYLENEEDED, &myFrame::OnStyleNeeded, this);
//set color for G-Code highlighting
m_activeSTC->StyleSetForeground(19,m_GCodecolor);
//enter some text and set fold levels
m_activeSTC->AppendText(";Foldtest\n");
m_activeSTC->AppendText("DEF BOOL TESTVAR=FALSE\n");
m_activeSTC->AppendText("IF (TESTVAR)\n");
m_activeSTC->AppendText(" G0 G54 M0\n");
m_activeSTC->AppendText("ENDIF\n");
m_activeSTC->AppendText("M30\n");
/*given the text above, folding should produce this output:
m_activeSTC->SetFoldLevel(0, 1024);
m_activeSTC->SetFoldLevel(1, 1024);
m_activeSTC->SetFoldLevel(2, 1024|wxSTC_FOLDLEVELHEADERFLAG); //header flag: one item before increasing fold level!
m_activeSTC->SetFoldLevel(3, 1025); //here comes the new fold level in line G0 G54 M0
m_activeSTC->SetFoldLevel(4, 1025); //the ENDIF
m_activeSTC->SetFoldLevel(5, 1024); //and this has the lower fold level again
m_activeSTC->SetFoldLevel(6, 1024|wxSTC_FOLDLEVELWHITEFLAG); //this is an empty line: set fold level white flag
*/
/*note: If you load text into the styled text control for example with file load or like we did above:
style the whole document for once. If you don't, the document remains unstyled until you click some position below*/
wxString text=m_activeSTC->GetText().Upper(); //Upper() makes it case insensitive
this->highlightSTCsyntax(0,m_activeSTC->GetTextLength(),text);
this->setfoldlevels(0,0,text);
}
void myFrame::onMarginClick(wxStyledTextEvent& event) {
int margin = event.GetMargin();
int position = event.GetPosition();
int line = m_activeSTC->LineFromPosition(position);
int foldLevel = m_activeSTC->GetFoldLevel(line);
bool headerFlag = (foldLevel & wxSTC_FOLDLEVELHEADERFLAG)!=0;
if( margin==MY_FOLDMARGIN && headerFlag ) {
m_activeSTC->ToggleFold(line);
}
}
void myFrame::OnStyleNeeded(wxStyledTextEvent& event) {
/*this is called every time the styler detects a line that needs style, so we style that range.
This will save a lot of performance since we only style text when needed instead of parsing the whole file every time.*/
size_t line_end=m_activeSTC->LineFromPosition(m_activeSTC->GetCurrentPos());
size_t line_start=m_activeSTC->LineFromPosition(m_activeSTC->GetEndStyled());
/*fold level: May need to include the two lines in front because of the fold level these lines have- the line above
may be affected*/
if(line_start>1) {
line_start-=2;
} else {
line_start=0;
}
if(line_end<line_start) {
//that happens when you select parts that are in front of the styled area
line_end=line_start;
}
//style the line following the style area too (if present) in case fold level decreases in that one
if(line_end<m_activeSTC->GetLineCount()-1){
line_end++;
}
//get exact start positions
size_t startpos=m_activeSTC->PositionFromLine(line_start);
size_t endpos=(m_activeSTC->GetLineEndPosition(line_end));
int startfoldlevel=m_activeSTC->GetFoldLevel(line_start);
startfoldlevel &= wxSTC_FOLDFLAG_LEVELNUMBERS; //mask out the flags and only use the fold level
wxString text=m_activeSTC->GetTextRange(startpos,endpos).Upper();
this->highlightSTCsyntax(startpos,endpos,text);
//calculate and apply foldings
this->setfoldlevels(startpos,startfoldlevel,text);
}
void myFrame::highlightSTCsyntax(size_t fromPos,size_t toPos, wxString &text) {
//this vector will hold the start and end position of each word to highlight
//if you want to highlight more than one, you should pass a whole class or struct containing the offsets
std::vector<std::pair<size_t,size_t>>GcodeVector;
//the following example is a quick and dirty parser for G-Codes.
//it just iterates through the Text Range and finds "Gxx" where xx is a digit.
//you could also use regex, but one can build a pretty fast routine based on single char evaluation
size_t actual_cursorpos = 0;
size_t startpos = 0;
size_t end_of_text = text.length();
bool word_boundary = true; //check for word boundary
char actualchar;
while (actual_cursorpos<end_of_text) {
actualchar= text[actual_cursorpos];
//check if syntax matches "G" followed by a couple of numbers
if((actualchar=='G')&&(word_boundary==true)) {
//this is a new G-Code, store startposition
startpos=actual_cursorpos;
word_boundary=false;
actual_cursorpos++;
if(actual_cursorpos<end_of_text) {
//refresh actual character
actualchar= text[actual_cursorpos];
}
//add digits
while((std::isdigit(actualchar)&&(actual_cursorpos<end_of_text))) {
actual_cursorpos++;
actualchar= text[actual_cursorpos];
}
//check if word boundary occurs at end of digits
if((actualchar==' ')||(actualchar=='\n')||(actualchar=='\r')||(actualchar=='\t')||(actual_cursorpos==end_of_text)) {
//success, append this one
if((actual_cursorpos-startpos)>1) {
//success, append to vector. DO NOT FORGET THE OFFSET HERE! We start from fromPos, so we need to add this
GcodeVector.push_back(std::make_pair(startpos+fromPos, actual_cursorpos+fromPos));
}
word_boundary=true;
}
}
if((actualchar==' ')||(actualchar=='\n')||(actualchar=='\r')||(actualchar=='\t')||(actual_cursorpos==end_of_text)) {
word_boundary=true;
}
actual_cursorpos++;
}
//remove old styling
m_activeSTC->StartStyling(fromPos,m_stylemask); //from here
m_activeSTC->SetStyling(toPos-fromPos,0); //with that length and style -> cleared
//now style the G-Codes
for (int i=0; i<GcodeVector.size(); i++) {
size_t startpos=GcodeVector[i].first;
size_t endpos=GcodeVector[i].second;
size_t length=(endpos-startpos);
m_activeSTC->StartStyling(startpos,m_stylemask);
m_activeSTC->SetStyling(length,19); //must match the style set above
}
}
void myFrame::setfoldlevels(size_t fromPos, int startfoldlevel, wxString& text) {
/*we'll increase the fold level with "IF" and decrease it with "ENDIF".
First, find all "IF" included in the text. Then we check if this IF is actually an ENDIF.
Keep in mind that you still need to check if this is actually commented out and so on.
This is a pretty simple and not perfect example to demonstrate basic folding*/
std::vector<size_t>if_positions;
size_t actual_cursorpos=0;
while ((actual_cursorpos<text.size())&&(actual_cursorpos!=wxNOT_FOUND)) {
actual_cursorpos=text.find("IF",actual_cursorpos+1);
if(actual_cursorpos!=wxNOT_FOUND) {
if_positions.push_back(actual_cursorpos+fromPos);
}
}
//build a vector to include line and folding level
//also, check if this IF is actually an ENDIF
std::vector<std::pair<size_t,int>>foldingvector;
int actualfoldlevel=startfoldlevel;
for(int i=0; i<if_positions.size(); i++) {
size_t this_line=m_activeSTC->LineFromPosition(if_positions[i]);
//check if that "IF" is an ENDIF
wxString endif_string;
if(if_positions[i]>3) {
endif_string=text.substr(if_positions[i]-3-fromPos,5);
}
//if it's an IF the fold level increases, if it's an ENDIF the foldlevel decreases
if(endif_string=="ENDIF") {
actualfoldlevel--;
foldingvector.push_back(std::make_pair(this_line,actualfoldlevel));
} else {
actualfoldlevel++;
foldingvector.push_back(std::make_pair(this_line,actualfoldlevel));
}
}
//now that we know which lines shall influence folding, we can apply to folding level to the STC line for line
int foldlevel=startfoldlevel; //this is a temporary marker containing the foldlevel of that position
size_t vectorcount=0;
//get positions from line from start and end
size_t startline=m_activeSTC->LineFromPosition(fromPos);
size_t endline=m_activeSTC->LineFromPosition(fromPos+text.size());
//set folding for these lines
for(size_t i=startline; i<=endline; i++) {
int prevlevel=foldlevel; //previous foldlevel
int foldflag=foldlevel; //this flag will be applied to the line
if((foldingvector.size()>0)&&(vectorcount<foldingvector.size())) {
if(i==foldingvector[vectorcount].first) { //if the fold level changes in that line
//new foldlevel = foldlevel in that line
foldlevel=foldingvector[vectorcount].second;
vectorcount++;
if(foldlevel>prevlevel) {
//when incremented, this line keeps the previous fold level (!) but is marked as a folder level header
foldflag= foldflag | wxSTC_FOLDLEVELHEADERFLAG; //incremented, set header flag
}
}
}
foldflag= foldflag | wxSTC_FOLDLEVELBASE; //add 1024 to the fold level
if(m_activeSTC->GetLineLength(i)==0) { //if this does not contain any characters, set the white flag
foldflag= foldflag | wxSTC_FOLDLEVELWHITEFLAG;
}
//finally, set fold level to line
m_activeSTC->SetFoldLevel(i,foldflag);
}
}
class myApp : public wxApp {
public:
virtual bool OnInit() {
myFrame* frame = new myFrame(NULL);
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(myApp);
This will highlight all "Gxx" occurences in the STC in a custom color (where xx is a digit) and apply folding to IF and ENDIF.
the example will fold on everything containing an "IF" but as an example I think it won't matter.
Basically, it is the fold example by New Pagodi with some enhancements.
--> Is it possible to understand what I am doing there? Still, a wiki is more than code- but I'd like to have someone to have a second look since I'm rather new to wxWidgets and have only a year experience of C++. Also, I'm using GCC TDM and things behave different with the VS build tools from time to time.