1 #include <wx/notebook.h>
2 #include <wx/textctrl.h>
3 #include <wx/regex.h>
4 #include "PipedProcessCtrl.h"
5 #include <globals.h>
6 #include <cbeditor.h>
7
8 ////////////////////////////////////// PipedProcessCtrl /////////////////////////////////////////////
9 #define PP_ERROR_STYLE 1
10 #define PP_LINK_STYLE 2
11
12
13 int ID_PROC=wxNewId();
14
BEGIN_EVENT_TABLE(PipedTextCtrl,wxScintilla)15 BEGIN_EVENT_TABLE(PipedTextCtrl, wxScintilla)
16 EVT_LEFT_DCLICK(PipedTextCtrl::OnDClick)
17 EVT_KEY_DOWN(PipedTextCtrl::OnUserInput)
18 END_EVENT_TABLE()
19
20 PipedTextCtrl::PipedTextCtrl(wxWindow *parent, PipedProcessCtrl *pp) : wxScintilla(parent, wxID_ANY)
21 {
22 m_pp = pp;
23
24 wxFont font(10, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
25
26 ConfigManager* mgr = Manager::Get()->GetConfigManager(_T("editor"));
27 wxString fontstring = mgr->Read(_T("/font"), wxEmptyString);
28 if (!fontstring.IsEmpty())
29 {
30 wxNativeFontInfo nfi;
31 nfi.FromString(fontstring);
32 font.SetNativeFontInfo(nfi);
33 }
34
35 StyleSetFont(wxSCI_STYLE_DEFAULT, font);
36 StyleSetForeground(PP_ERROR_STYLE,wxColor(200,0,0));
37 StyleSetForeground(PP_LINK_STYLE,wxColor(0,0,200));
38 StyleSetUnderline(PP_LINK_STYLE,true);
39 }
40
41
OnDClick(wxMouseEvent & e)42 void PipedTextCtrl::OnDClick(wxMouseEvent &e)
43 {
44 m_pp->OnDClick(e);
45 }
46
47
OnUserInput(wxKeyEvent & e)48 void PipedTextCtrl::OnUserInput(wxKeyEvent &e)
49 {
50 m_pp->OnUserInput(e);
51 }
52
53
BEGIN_EVENT_TABLE(PipedProcessCtrl,wxPanel)54 BEGIN_EVENT_TABLE(PipedProcessCtrl, wxPanel)
55 EVT_CHAR(PipedProcessCtrl::OnUserInput)
56 EVT_END_PROCESS(ID_PROC, PipedProcessCtrl::OnEndProcess)
57 EVT_SIZE (PipedProcessCtrl::OnSize)
58 END_EVENT_TABLE()
59
60 IMPLEMENT_DYNAMIC_CLASS(PipedProcessCtrl, wxPanel)
61
62
63 PipedProcessCtrl::PipedProcessCtrl(wxWindow* parent, int id, const wxString &name, ShellManager *shellmgr) : ShellCtrlBase(parent, id, name, shellmgr)
64 {
65 m_shellmgr=shellmgr;
66 m_name=name;
67 m_dead=true;
68 m_proc=NULL;
69 m_killlevel=0;
70 m_linkclicks=true;
71 m_parselinks=true;
72 m_textctrl=new PipedTextCtrl(this,this);//(this, id, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_RICH|wxTE_MULTILINE|wxTE_READONLY|wxTE_PROCESS_ENTER|wxEXPAND);
73 wxBoxSizer* bs = new wxBoxSizer(wxVERTICAL);
74 bs->Add(m_textctrl, 1, wxEXPAND | wxALL);
75 SetAutoLayout(TRUE);
76 SetSizer(bs);
77 }
78
79
OnEndProcess(wxProcessEvent & event)80 void PipedProcessCtrl::OnEndProcess(wxProcessEvent &event)
81 {
82 m_exitcode=event.GetExitCode();
83 SyncOutput(-1); //read any left over output TODO: while loop to handle extremely large amount of output
84 m_dead=true;
85 delete m_proc;
86 m_proc=NULL;
87 m_killlevel=0;
88 if(m_shellmgr)
89 m_shellmgr->OnShellTerminate(this);
90 }
91
LaunchProcess(const wxString & processcmd,const wxArrayString &)92 long PipedProcessCtrl::LaunchProcess(const wxString &processcmd, const wxArrayString &/*options*/) // bool ParseLinks, bool LinkClicks, const wxString &LinkRegex
93 {
94 if(!m_dead)
95 return -1;
96 if(m_proc) //this should never happen
97 m_proc->Detach(); //self cleanup
98 m_proc=new wxProcess(this,ID_PROC);
99 m_proc->Redirect();
100 m_procid=wxExecute(processcmd,wxEXEC_ASYNC,m_proc);
101 m_parselinks=true;//ParseLinks;
102 m_linkclicks=true;//LinkClicks;
103 m_linkregex=LinkRegexDefault; //LinkRegex;
104 if(m_procid>0)
105 {
106 m_ostream=m_proc->GetOutputStream();
107 m_istream=m_proc->GetInputStream();
108 m_estream=m_proc->GetErrorStream();
109 m_dead=false;
110 m_killlevel=0;
111 }
112 return m_procid;
113 }
114
KillProcess()115 void PipedProcessCtrl::KillProcess()
116 {
117 if(m_dead)
118 return;
119 // if(m_killlevel==0) //some process will complete if we send EOF. TODO: make sending EOF a separate option
120 // {
121 // m_proc->CloseOutput();
122 // m_killlevel=1;
123 // return;
124 // }
125 long pid=GetPid();
126 #ifdef __WXGTK__
127 if(m_killlevel==0)
128 {
129 m_killlevel=1;
130 if(wxProcess::Exists(pid))
131 wxProcess::Kill(pid,wxSIGTERM);
132 return;
133 }
134 if(m_killlevel==1)
135 {
136 if(wxProcess::Exists(pid))
137 {
138 wxProcess::Kill(pid,wxSIGKILL);
139 }
140 }
141 #else
142 if(wxProcess::Exists(pid))
143 {
144 wxProcess::Kill(pid,wxSIGKILL);
145 }
146 #endif //__WXGTK__
147 }
148
149 wxString PipedProcessCtrl::LinkRegexDefault=
150 _T("[\"']?((?:\\w\\:)?[^'\",\\s:;*?]+?)[\"']?[\\s]*(\\:|\\(|\\[|\\,?\\s*[Ll]ine)?\\s*(\\d*)");
151 // a: \path\to\file line 300
SyncOutput(int maxchars)152 void PipedProcessCtrl::SyncOutput(int maxchars)
153 {
154 if(!m_proc)
155 return;
156 bool oneshot=true;
157 if(maxchars<=0)
158 {
159 maxchars=20000;
160 oneshot=false;
161 }
162 int lineno=m_textctrl->GetLineCount()-1;
163 while(m_proc->IsInputAvailable())
164 {
165 char buf0[maxchars+1];
166 for(int i=0;i<maxchars+1;i++)
167 buf0[i]=0;
168 m_istream->Read(buf0,maxchars);
169 wxString m_latest=wxString::FromAscii(buf0);
170 long start,end;
171 start=m_textctrl->GetSelectionStart();
172 end=m_textctrl->GetSelectionEnd();
173 int pos=start>end?start:end;
174 bool move_caret=(pos>=m_textctrl->PositionFromLine(m_textctrl->GetLineCount())&&
175 (start==end));
176 m_textctrl->AppendText(m_latest);
177 if(move_caret)
178 {
179 m_textctrl->GotoLine(m_textctrl->GetLineCount());
180 // m_textctrl->SetSelectionStart(-1);
181 // m_textctrl->SetSelectionEnd(-1);
182 }
183 if(oneshot)
184 break;
185 }
186 if(m_proc->IsErrorAvailable())
187 {
188 while(m_proc->IsErrorAvailable())
189 {
190 char buf0[maxchars+1];
191 for(int i=0;i<maxchars+1;i++)
192 buf0[i]=0;
193 m_estream->Read(buf0,maxchars);
194 wxString m_latest=wxString::FromAscii(buf0);
195 long start,end;
196 start=m_textctrl->GetSelectionStart();
197 end=m_textctrl->GetSelectionEnd();
198 int pos=start>end?start:end;
199 bool move_caret=(pos>=m_textctrl->PositionFromLine(m_textctrl->GetLineCount())&&
200 (start==end));
201 int style_start=m_textctrl->PositionFromLine(m_textctrl->GetLineCount());
202 m_textctrl->AppendText(m_latest);
203 if(move_caret)
204 {
205 m_textctrl->GotoLine(m_textctrl->GetLineCount());
206 // m_textctrl->SetSelectionStart(-1);
207 // m_textctrl->SetSelectionEnd(-1);
208 }
209
210 m_textctrl->StartStyling(style_start);
211 m_textctrl->SetStyling(m_textctrl->PositionFromLine(m_textctrl->GetLineCount())-style_start,PP_ERROR_STYLE);
212 if(oneshot)
213 break;
214 }
215 }
216 if(m_parselinks)
217 ParseLinks(lineno,m_textctrl->GetLineCount());
218 }
219
ParseLinks(int lineno,int lastline)220 void PipedProcessCtrl::ParseLinks(int lineno, int lastline)
221 {
222 wxRegEx re(m_linkregex,wxRE_ADVANCED|wxRE_NEWLINE);
223 while(lineno<lastline)
224 {
225 int col=0;
226 wxString text=m_textctrl->GetLine(lineno);
227 wxString file;
228 while(re.Matches(text))
229 {
230 size_t start,len;
231 if(re.GetMatch(&start,&len,0))
232 {
233 size_t fstart, flen;
234 if(re.GetMatch(&fstart,&flen,1))
235 file=text.Mid(fstart,flen);
236 wxFileName f(file);
237 if(f.FileExists())
238 {
239 int pos=m_textctrl->PositionFromLine(lineno)+col+start;
240 m_textctrl->StartStyling(pos);
241 m_textctrl->SetStyling(len,PP_LINK_STYLE);
242
243 }
244 }
245 col+=start+len;
246 text=text.Mid(start+len);
247 }
248 lineno++;
249 }
250 }
251
OnSize(wxSizeEvent & event)252 void PipedProcessCtrl::OnSize(wxSizeEvent& event)
253 {
254 m_textctrl->SetSize(event.GetSize());
255 }
256
257
OnUserInput(wxKeyEvent & ke)258 void PipedProcessCtrl::OnUserInput(wxKeyEvent& ke)
259 {
260 if(m_dead)
261 {
262 ke.Skip();
263 return;
264 }
265 //todo: if user presses navigational keys accept them as navigational (also copy/paste shortcuts?)
266 char kc1[2];
267 kc1[0]=ke.GetKeyCode()%256;
268 kc1[1]=0;
269 if(kc1[0]=='\r')
270 kc1[0]='\n';
271 wxChar kc2=ke.GetUnicodeKey();
272 wxString buf(kc2);
273 if (!ke.ControlDown() && !ke.AltDown())
274 if (ke.GetKeyCode()<WXK_START ||
275 ke.GetKeyCode()>WXK_COMMAND)
276 {
277 m_ostream->Write(&kc1,1);
278 m_textctrl->AppendText(kc2);
279 m_textctrl->GotoPos(m_textctrl->GetLength());
280 return;
281 }
282
283 ke.Skip();
284 }
285
286
287
288 /////////////////////////////////////////////////////////////////////////////////////////////////
289 /////////////////////////////////////// HyperLinking ////////////////////////////////////////////
290 /////////////////////////////////////////////////////////////////////////////////////////////////
291
292
293
OnDClick(wxMouseEvent & e)294 void PipedProcessCtrl::OnDClick(wxMouseEvent &e)
295 {
296 //First retrieve the link text
297 if(!m_linkclicks)
298 return;
299 long pos=m_textctrl->PositionFromPoint(e.GetPosition());
300 int style=m_textctrl->GetStyleAt(pos);
301 if((style&PP_LINK_STYLE)!=PP_LINK_STYLE)
302 return; //didn't click a link
303 long start=pos;
304 while(start>0)
305 {
306 style=m_textctrl->GetStyleAt(start-1);
307 if((style&PP_LINK_STYLE)!=PP_LINK_STYLE)
308 break;
309 start--;
310 }
311 long end=pos;
312 while(end<m_textctrl->PositionFromLine(m_textctrl->GetLineCount()-1))
313 {
314 style=m_textctrl->GetStyleAt(end+1);
315 if((style&PP_LINK_STYLE)!=PP_LINK_STYLE)
316 break;
317 end++;
318 }
319 wxString text=m_textctrl->GetTextRange(start,end+1);
320
321 //retrieve the file and line number parts of the link
322 wxRegEx re(m_linkregex,wxRE_ADVANCED|wxRE_NEWLINE);
323 wxString file;
324 long line;
325 if(!re.Matches(text))
326 return;
327 size_t ind,len;
328 re.GetMatch(&ind,&len,0);
329 if(re.GetMatch(&ind,&len,1))
330 file=text.Mid(ind,len);
331 else
332 file=wxEmptyString;
333 if(re.GetMatch(&ind,&len,3))
334 text.Mid(ind,len).ToLong(&line);
335 else
336 line=0;
337
338 //open the file in the editor
339 wxFileName f(file);
340 if(f.FileExists())
341 {
342 cbEditor* ed = Manager::Get()->GetEditorManager()->Open(f.GetFullPath());
343 if (ed)
344 {
345 ed->Show(true);
346 // if (!ed->GetProjectFile())
347 // ed->SetProjectFile(f.GetFullPath());
348 ed->GotoLine(line - 1, false);
349 if(line>0)
350 if(!ed->HasBookmark(line - 1))
351 ed->ToggleBookmark(line -1);
352 }
353 }
354
355 }
356
357 /////////////////////////////////////////////////////////////////////////////////////////////////
358 /////////////////////////////////////////////////////////////////////////////////////////////////
359