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