]> git.saurik.com Git - wxWidgets.git/blob - samples/exec/exec.cpp
fix tab traversal of wxStaticBox children, closes #15376
[wxWidgets.git] / samples / exec / exec.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: exec.cpp
3 // Purpose: exec sample demonstrates wxExecute and related functions
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 15.01.00
7 // Copyright: (c) Vadim Zeitlin
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // declarations
13 // ============================================================================
14
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18
19 // For compilers that support precompilation, includes "wx/wx.h".
20 #include "wx/wxprec.h"
21
22 #ifdef __BORLANDC__
23 #pragma hdrstop
24 #endif
25
26 // for all others, include the necessary headers (this file is usually all you
27 // need because it includes almost all "standard" wxWidgets headers
28 #ifndef WX_PRECOMP
29 #include "wx/app.h"
30 #include "wx/log.h"
31 #include "wx/frame.h"
32 #include "wx/panel.h"
33
34 #include "wx/timer.h"
35
36 #include "wx/utils.h"
37 #include "wx/menu.h"
38
39 #include "wx/msgdlg.h"
40 #include "wx/textdlg.h"
41 #include "wx/filedlg.h"
42 #include "wx/choicdlg.h"
43
44 #include "wx/button.h"
45 #include "wx/checkbox.h"
46 #include "wx/stattext.h"
47 #include "wx/textctrl.h"
48 #include "wx/listbox.h"
49
50 #include "wx/sizer.h"
51 #endif
52
53 #include "wx/txtstrm.h"
54 #include "wx/numdlg.h"
55 #include "wx/textdlg.h"
56 #include "wx/ffile.h"
57 #include "wx/scopedptr.h"
58 #include "wx/stopwatch.h"
59
60 #include "wx/process.h"
61
62 #include "wx/mimetype.h"
63
64 #ifdef __WINDOWS__
65 #include "wx/dde.h"
66 #endif // __WINDOWS__
67
68 #ifndef wxHAS_IMAGES_IN_RESOURCES
69 #include "../sample.xpm"
70 #endif
71
72 // ----------------------------------------------------------------------------
73 // the usual application and main frame classes
74 // ----------------------------------------------------------------------------
75
76 // Define a new application type, each program should derive a class from wxApp
77 class MyApp : public wxApp
78 {
79 public:
80 // override base class virtuals
81 // ----------------------------
82
83 // this one is called on application startup and is a good place for the app
84 // initialization (doing it here and not in the ctor allows to have an error
85 // return: if OnInit() returns false, the application terminates)
86 virtual bool OnInit();
87 };
88
89 // Define an array of process pointers used by MyFrame
90 class MyPipedProcess;
91 WX_DEFINE_ARRAY_PTR(MyPipedProcess *, MyPipedProcessesArray);
92
93 class MyProcess;
94 WX_DEFINE_ARRAY_PTR(MyProcess *, MyProcessesArray);
95
96 // Define a new frame type: this is going to be our main frame
97 class MyFrame : public wxFrame
98 {
99 public:
100 // ctor and dtor
101 MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size);
102 virtual ~MyFrame();
103
104 // event handlers (these functions should _not_ be virtual)
105 void OnQuit(wxCommandEvent& event);
106
107 void OnKill(wxCommandEvent& event);
108
109 void OnClear(wxCommandEvent& event);
110
111 void OnBeginBusyCursor(wxCommandEvent& event);
112 void OnEndBusyCursor(wxCommandEvent& event);
113
114 void OnSyncExec(wxCommandEvent& event);
115 void OnAsyncExec(wxCommandEvent& event);
116 void OnShell(wxCommandEvent& event);
117 void OnExecWithRedirect(wxCommandEvent& event);
118 void OnExecWithPipe(wxCommandEvent& event);
119
120 void OnPOpen(wxCommandEvent& event);
121
122 void OnFileExec(wxCommandEvent& event);
123 void OnFileLaunch(wxCommandEvent& event);
124 void OnOpenURL(wxCommandEvent& event);
125 void OnShowCommandForExt(wxCommandEvent& event);
126
127 void OnAbout(wxCommandEvent& event);
128
129 // polling output of async processes
130 void OnIdleTimer(wxTimerEvent& event);
131 void OnIdle(wxIdleEvent& event);
132
133 // for MyPipedProcess
134 void OnProcessTerminated(MyPipedProcess *process);
135 wxListBox *GetLogListBox() const { return m_lbox; }
136
137 // for MyProcess
138 void OnAsyncTermination(MyProcess *process);
139
140 // timer updating a counter in the background
141 void OnBgTimer(wxTimerEvent& event);
142
143 private:
144 void ShowOutput(const wxString& cmd,
145 const wxArrayString& output,
146 const wxString& title);
147
148 int GetExecFlags() const;
149
150 void DoAsyncExec(const wxString& cmd);
151
152 void AddAsyncProcess(MyProcess *process) { m_allAsync.push_back(process); }
153
154 void AddPipedProcess(MyPipedProcess *process);
155 void RemovePipedProcess(MyPipedProcess *process);
156
157
158 // the PID of the last process we launched asynchronously
159 long m_pidLast;
160
161 // last command we executed
162 wxString m_cmdLast;
163
164 #ifdef __WINDOWS__
165 void OnDDEExec(wxCommandEvent& event);
166 void OnDDERequest(wxCommandEvent& event);
167
168 bool GetDDEServer();
169
170 // last params of a DDE transaction
171 wxString m_server,
172 m_topic,
173 m_cmdDde;
174 #endif // __WINDOWS__
175
176 wxListBox *m_lbox;
177
178 // array of running processes with redirected IO
179 MyPipedProcessesArray m_running;
180
181 // array of all asynchrously running processes
182 MyProcessesArray m_allAsync;
183
184 // the idle event wake up timer
185 wxTimer m_timerIdleWakeUp;
186
187 // a background timer allowing to easily check visually whether the
188 // messages are processed or not
189 wxTimer m_timerBg;
190
191 // any class wishing to process wxWidgets events must use this macro
192 DECLARE_EVENT_TABLE()
193 };
194
195 // ----------------------------------------------------------------------------
196 // MyPipeFrame: allows the user to communicate with the child process
197 // ----------------------------------------------------------------------------
198
199 class MyPipeFrame : public wxFrame
200 {
201 public:
202 MyPipeFrame(wxFrame *parent,
203 const wxString& cmd,
204 wxProcess *process);
205
206 protected:
207 void OnTextEnter(wxCommandEvent& WXUNUSED(event)) { DoSend(); }
208 void OnBtnSend(wxCommandEvent& WXUNUSED(event)) { DoSend(); }
209 void OnBtnSendFile(wxCommandEvent& WXUNUSED(event));
210 void OnBtnGet(wxCommandEvent& WXUNUSED(event)) { DoGet(); }
211 void OnBtnClose(wxCommandEvent& WXUNUSED(event)) { DoClose(); }
212
213 void OnClose(wxCloseEvent& event);
214
215 void OnProcessTerm(wxProcessEvent& event);
216
217 void DoSend()
218 {
219 wxString s(m_textOut->GetValue());
220 s += wxT('\n');
221 m_out.Write(s.c_str(), s.length());
222 m_textOut->Clear();
223
224 DoGet();
225 }
226
227 void DoGet();
228 void DoClose();
229
230 private:
231 void DoGetFromStream(wxTextCtrl *text, wxInputStream& in);
232 void DisableInput();
233 void DisableOutput();
234
235
236 wxProcess *m_process;
237
238 wxOutputStream &m_out;
239 wxInputStream &m_in,
240 &m_err;
241
242 wxTextCtrl *m_textOut,
243 *m_textIn,
244 *m_textErr;
245
246 DECLARE_EVENT_TABLE()
247 };
248
249 // ----------------------------------------------------------------------------
250 // wxProcess-derived classes
251 // ----------------------------------------------------------------------------
252
253 // This is the handler for process termination events
254 class MyProcess : public wxProcess
255 {
256 public:
257 MyProcess(MyFrame *parent, const wxString& cmd)
258 : wxProcess(parent), m_cmd(cmd)
259 {
260 m_parent = parent;
261 }
262
263 // instead of overriding this virtual function we might as well process the
264 // event from it in the frame class - this might be more convenient in some
265 // cases
266 virtual void OnTerminate(int pid, int status);
267
268 protected:
269 MyFrame *m_parent;
270 wxString m_cmd;
271 };
272
273 // A specialization of MyProcess for redirecting the output
274 class MyPipedProcess : public MyProcess
275 {
276 public:
277 MyPipedProcess(MyFrame *parent, const wxString& cmd)
278 : MyProcess(parent, cmd)
279 {
280 Redirect();
281 }
282
283 virtual void OnTerminate(int pid, int status);
284
285 virtual bool HasInput();
286 };
287
288 // A version of MyPipedProcess which also sends input to the stdin of the
289 // child process
290 class MyPipedProcess2 : public MyPipedProcess
291 {
292 public:
293 MyPipedProcess2(MyFrame *parent, const wxString& cmd, const wxString& input)
294 : MyPipedProcess(parent, cmd), m_input(input)
295 {
296 }
297
298 virtual bool HasInput();
299
300 private:
301 wxString m_input;
302 };
303
304 // ----------------------------------------------------------------------------
305 // constants
306 // ----------------------------------------------------------------------------
307
308 // IDs for the controls and the menu commands
309 enum
310 {
311 // timer ids
312 Exec_TimerIdle = 10,
313 Exec_TimerBg,
314
315 // menu items
316 Exec_Kill = 100,
317 Exec_ClearLog,
318 Exec_BeginBusyCursor,
319 Exec_EndBusyCursor,
320 Exec_SyncExec = 200,
321 Exec_AsyncExec,
322 Exec_Shell,
323 Exec_POpen,
324 Exec_OpenFile,
325 Exec_ShowCommandForExt,
326 Exec_LaunchFile,
327 Exec_OpenURL,
328 Exec_DDEExec,
329 Exec_DDERequest,
330 Exec_Redirect,
331 Exec_Pipe,
332 Exec_Flags_HideConsole,
333 Exec_Flags_ShowConsole,
334 Exec_Flags_NoEvents,
335 Exec_About = wxID_ABOUT,
336 Exec_Quit = wxID_EXIT,
337
338 // control ids
339 Exec_Btn_Send = 1000,
340 Exec_Btn_SendFile,
341 Exec_Btn_Get,
342 Exec_Btn_Close
343 };
344
345 static const wxChar *DIALOG_TITLE = wxT("Exec sample");
346
347 // ----------------------------------------------------------------------------
348 // event tables and other macros for wxWidgets
349 // ----------------------------------------------------------------------------
350
351 // the event tables connect the wxWidgets events with the functions (event
352 // handlers) which process them. It can be also done at run-time, but for the
353 // simple menu events like this the static method is much simpler.
354 BEGIN_EVENT_TABLE(MyFrame, wxFrame)
355 EVT_MENU(Exec_Quit, MyFrame::OnQuit)
356 EVT_MENU(Exec_Kill, MyFrame::OnKill)
357 EVT_MENU(Exec_ClearLog, MyFrame::OnClear)
358 EVT_MENU(Exec_BeginBusyCursor, MyFrame::OnBeginBusyCursor)
359 EVT_MENU(Exec_EndBusyCursor, MyFrame::OnEndBusyCursor)
360
361 EVT_MENU(Exec_SyncExec, MyFrame::OnSyncExec)
362 EVT_MENU(Exec_AsyncExec, MyFrame::OnAsyncExec)
363 EVT_MENU(Exec_Shell, MyFrame::OnShell)
364 EVT_MENU(Exec_Redirect, MyFrame::OnExecWithRedirect)
365 EVT_MENU(Exec_Pipe, MyFrame::OnExecWithPipe)
366
367 EVT_MENU(Exec_POpen, MyFrame::OnPOpen)
368
369 EVT_MENU(Exec_OpenFile, MyFrame::OnFileExec)
370 EVT_MENU(Exec_ShowCommandForExt, MyFrame::OnShowCommandForExt)
371 EVT_MENU(Exec_LaunchFile, MyFrame::OnFileLaunch)
372 EVT_MENU(Exec_OpenURL, MyFrame::OnOpenURL)
373
374 #ifdef __WINDOWS__
375 EVT_MENU(Exec_DDEExec, MyFrame::OnDDEExec)
376 EVT_MENU(Exec_DDERequest, MyFrame::OnDDERequest)
377 #endif // __WINDOWS__
378
379 EVT_MENU(Exec_About, MyFrame::OnAbout)
380
381 EVT_IDLE(MyFrame::OnIdle)
382
383 EVT_TIMER(Exec_TimerIdle, MyFrame::OnIdleTimer)
384 EVT_TIMER(Exec_TimerBg, MyFrame::OnBgTimer)
385 END_EVENT_TABLE()
386
387 BEGIN_EVENT_TABLE(MyPipeFrame, wxFrame)
388 EVT_BUTTON(Exec_Btn_Send, MyPipeFrame::OnBtnSend)
389 EVT_BUTTON(Exec_Btn_SendFile, MyPipeFrame::OnBtnSendFile)
390 EVT_BUTTON(Exec_Btn_Get, MyPipeFrame::OnBtnGet)
391 EVT_BUTTON(Exec_Btn_Close, MyPipeFrame::OnBtnClose)
392
393 EVT_TEXT_ENTER(wxID_ANY, MyPipeFrame::OnTextEnter)
394
395 EVT_CLOSE(MyPipeFrame::OnClose)
396
397 EVT_END_PROCESS(wxID_ANY, MyPipeFrame::OnProcessTerm)
398 END_EVENT_TABLE()
399
400 // Create a new application object: this macro will allow wxWidgets to create
401 // the application object during program execution (it's better than using a
402 // static object for many reasons) and also declares the accessor function
403 // wxGetApp() which will return the reference of the right type (i.e. MyApp and
404 // not wxApp)
405 IMPLEMENT_APP(MyApp)
406
407 // ============================================================================
408 // implementation
409 // ============================================================================
410
411 // ----------------------------------------------------------------------------
412 // the application class
413 // ----------------------------------------------------------------------------
414
415 // `Main program' equivalent: the program execution "starts" here
416 bool MyApp::OnInit()
417 {
418 if ( !wxApp::OnInit() )
419 return false;
420
421 // Create the main application window
422 MyFrame *frame = new MyFrame(wxT("Exec wxWidgets sample"),
423 wxDefaultPosition, wxSize(500, 140));
424
425 // Show it
426 frame->Show(true);
427
428 // success: wxApp::OnRun() will be called which will enter the main message
429 // loop and the application will run. If we returned false here, the
430 // application would exit immediately.
431 return true;
432 }
433
434 // ----------------------------------------------------------------------------
435 // main frame
436 // ----------------------------------------------------------------------------
437
438 #ifdef __VISUALC__
439 #pragma warning(disable: 4355) // this used in base member initializer list
440 #endif
441
442 // frame constructor
443 MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
444 : wxFrame((wxFrame *)NULL, wxID_ANY, title, pos, size),
445 m_timerIdleWakeUp(this, Exec_TimerIdle),
446 m_timerBg(this, Exec_TimerBg)
447 {
448 SetIcon(wxICON(sample));
449
450 m_pidLast = 0;
451
452 #ifdef __WXMAC__
453 // we need this in order to allow the about menu relocation, since ABOUT is
454 // not the default id of the about menu
455 wxApp::s_macAboutMenuItemId = Exec_About;
456 #endif
457
458 // create a menu bar
459 wxMenu *menuFile = new wxMenu(wxEmptyString, wxMENU_TEAROFF);
460 menuFile->Append(Exec_Kill, wxT("&Kill process...\tCtrl-K"),
461 wxT("Kill a process by PID"));
462 menuFile->AppendSeparator();
463 menuFile->Append(Exec_OpenFile, wxT("Open &file...\tCtrl-F"),
464 wxT("Launch the command to open this kind of files"));
465 menuFile->Append(Exec_ShowCommandForExt,
466 "Show association for extension...\tShift-Ctrl-A",
467 "Show the command associated with the given extension");
468 menuFile->Append(Exec_LaunchFile, wxT("La&unch file...\tShift-Ctrl-F"),
469 wxT("Launch the default application associated with the file"));
470 menuFile->Append(Exec_OpenURL, wxT("Open &URL...\tCtrl-U"),
471 wxT("Launch the default browser with the given URL"));
472 menuFile->AppendSeparator();
473 menuFile->Append(Exec_BeginBusyCursor, wxT("Show &busy cursor\tCtrl-C"));
474 menuFile->Append(Exec_EndBusyCursor, wxT("Show &normal cursor\tShift-Ctrl-C"));
475 menuFile->AppendSeparator();
476 menuFile->Append(Exec_ClearLog, wxT("&Clear log\tCtrl-L"),
477 wxT("Clear the log window"));
478 menuFile->AppendSeparator();
479 menuFile->Append(Exec_Quit, wxT("E&xit\tAlt-X"), wxT("Quit this program"));
480
481 wxMenu *flagsMenu = new wxMenu;
482 flagsMenu->AppendCheckItem(Exec_Flags_HideConsole, "Always &hide console");
483 flagsMenu->AppendCheckItem(Exec_Flags_ShowConsole, "Always &show console");
484 flagsMenu->AppendCheckItem(Exec_Flags_NoEvents, "Disable &events",
485 "This flag is valid for sync execution only");
486
487 wxMenu *execMenu = new wxMenu;
488 execMenu->AppendSubMenu(flagsMenu, "Execution flags");
489 execMenu->AppendSeparator();
490 execMenu->Append(Exec_SyncExec, wxT("Sync &execution...\tCtrl-E"),
491 wxT("Launch a program and return when it terminates"));
492 execMenu->Append(Exec_AsyncExec, wxT("&Async execution...\tCtrl-A"),
493 wxT("Launch a program and return immediately"));
494 execMenu->Append(Exec_Shell, wxT("Execute &shell command...\tCtrl-S"),
495 wxT("Launch a shell and execute a command in it"));
496 execMenu->AppendSeparator();
497 execMenu->Append(Exec_Redirect, wxT("Capture command &output...\tCtrl-O"),
498 wxT("Launch a program and capture its output"));
499 execMenu->Append(Exec_Pipe, wxT("&Pipe through command..."),
500 wxT("Pipe a string through a filter"));
501 execMenu->Append(Exec_POpen, wxT("&Open a pipe to a command...\tCtrl-P"),
502 wxT("Open a pipe to and from another program"));
503
504 #ifdef __WINDOWS__
505 execMenu->AppendSeparator();
506 execMenu->Append(Exec_DDEExec, wxT("Execute command via &DDE...\tCtrl-D"));
507 execMenu->Append(Exec_DDERequest, wxT("Send DDE &request...\tCtrl-R"));
508 #endif
509
510 wxMenu *helpMenu = new wxMenu(wxEmptyString, wxMENU_TEAROFF);
511 helpMenu->Append(Exec_About, wxT("&About\tF1"), wxT("Show about dialog"));
512
513 // now append the freshly created menu to the menu bar...
514 wxMenuBar *menuBar = new wxMenuBar();
515 menuBar->Append(menuFile, wxT("&File"));
516 menuBar->Append(execMenu, wxT("&Exec"));
517 menuBar->Append(helpMenu, wxT("&Help"));
518
519 // ... and attach this menu bar to the frame
520 SetMenuBar(menuBar);
521
522 // create the listbox in which we will show misc messages as they come
523 m_lbox = new wxListBox(this, wxID_ANY);
524 wxFont font(12, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL,
525 wxFONTWEIGHT_NORMAL);
526 if ( font.IsOk() )
527 m_lbox->SetFont(font);
528
529 #if wxUSE_STATUSBAR
530 // create a status bar just for fun (by default with 1 pane only)
531 CreateStatusBar(2);
532 SetStatusText(wxT("Welcome to wxWidgets exec sample!"));
533 #endif // wxUSE_STATUSBAR
534
535 m_timerBg.Start(1000);
536 }
537
538 MyFrame::~MyFrame()
539 {
540 // any processes left until now must be deleted manually: normally this is
541 // done when the associated process terminates but it must be still running
542 // if this didn't happen until now
543 for ( size_t n = 0; n < m_allAsync.size(); n++ )
544 {
545 delete m_allAsync[n];
546 }
547 }
548
549 // ----------------------------------------------------------------------------
550 // event handlers: file and help menu
551 // ----------------------------------------------------------------------------
552
553 void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
554 {
555 // true is to force the frame to close
556 Close(true);
557 }
558
559 void MyFrame::OnClear(wxCommandEvent& WXUNUSED(event))
560 {
561 m_lbox->Clear();
562 }
563
564 void MyFrame::OnBeginBusyCursor(wxCommandEvent& WXUNUSED(event))
565 {
566 wxBeginBusyCursor();
567 }
568
569 void MyFrame::OnEndBusyCursor(wxCommandEvent& WXUNUSED(event))
570 {
571 wxEndBusyCursor();
572 }
573
574 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
575 {
576 wxMessageBox(wxT("Exec wxWidgets Sample\n(c) 2000-2002 Vadim Zeitlin"),
577 wxT("About Exec"), wxOK | wxICON_INFORMATION, this);
578 }
579
580 void MyFrame::OnKill(wxCommandEvent& WXUNUSED(event))
581 {
582 long pid = wxGetNumberFromUser(wxT("Please specify the process to kill"),
583 wxT("Enter PID:"),
584 wxT("Exec question"),
585 m_pidLast,
586 // we need the full unsigned int range
587 -INT_MAX, INT_MAX,
588 this);
589 if ( pid == -1 )
590 {
591 // cancelled
592 return;
593 }
594
595 m_pidLast = pid;
596
597 static const wxString signalNames[] =
598 {
599 wxT("Just test (SIGNONE)"),
600 wxT("Hangup (SIGHUP)"),
601 wxT("Interrupt (SIGINT)"),
602 wxT("Quit (SIGQUIT)"),
603 wxT("Illegal instruction (SIGILL)"),
604 wxT("Trap (SIGTRAP)"),
605 wxT("Abort (SIGABRT)"),
606 wxT("Emulated trap (SIGEMT)"),
607 wxT("FP exception (SIGFPE)"),
608 wxT("Kill (SIGKILL)"),
609 wxT("Bus (SIGBUS)"),
610 wxT("Segment violation (SIGSEGV)"),
611 wxT("System (SIGSYS)"),
612 wxT("Broken pipe (SIGPIPE)"),
613 wxT("Alarm (SIGALRM)"),
614 wxT("Terminate (SIGTERM)"),
615 };
616
617 static int s_sigLast = wxSIGNONE;
618 int sig = wxGetSingleChoiceIndex(wxT("How to kill the process?"),
619 wxT("Exec question"),
620 WXSIZEOF(signalNames), signalNames,
621 s_sigLast,
622 this);
623 switch ( sig )
624 {
625 default:
626 wxFAIL_MSG( wxT("unexpected return value") );
627 // fall through
628
629 case -1:
630 // cancelled
631 return;
632
633 case wxSIGNONE:
634 case wxSIGHUP:
635 case wxSIGINT:
636 case wxSIGQUIT:
637 case wxSIGILL:
638 case wxSIGTRAP:
639 case wxSIGABRT:
640 case wxSIGEMT:
641 case wxSIGFPE:
642 case wxSIGKILL:
643 case wxSIGBUS:
644 case wxSIGSEGV:
645 case wxSIGSYS:
646 case wxSIGPIPE:
647 case wxSIGALRM:
648 case wxSIGTERM:
649 break;
650 }
651
652 s_sigLast = sig;
653
654 if ( sig == wxSIGNONE )
655 {
656 // This simply calls Kill(wxSIGNONE) but using it is more convenient.
657 if ( wxProcess::Exists(pid) )
658 {
659 wxLogStatus(wxT("Process %ld is running."), pid);
660 }
661 else
662 {
663 wxLogStatus(wxT("No process with pid = %ld."), pid);
664 }
665 }
666 else // not SIGNONE
667 {
668 wxKillError rc = wxProcess::Kill(pid, (wxSignal)sig);
669 if ( rc == wxKILL_OK )
670 {
671 wxLogStatus(wxT("Process %ld killed with signal %d."), pid, sig);
672 }
673 else
674 {
675 static const wxChar *errorText[] =
676 {
677 wxT(""), // no error
678 wxT("signal not supported"),
679 wxT("permission denied"),
680 wxT("no such process"),
681 wxT("unspecified error"),
682 };
683
684 wxLogStatus(wxT("Failed to kill process %ld with signal %d: %s"),
685 pid, sig, errorText[rc]);
686 }
687 }
688 }
689
690 // ----------------------------------------------------------------------------
691 // execution options dialog
692 // ----------------------------------------------------------------------------
693
694 enum ExecQueryDialogID
695 {
696 TEXT_EXECUTABLE,
697 TEXT_CWD,
698 TEXT_ENVIRONMENT
699 };
700
701 class ExecQueryDialog : public wxDialog
702 {
703 public:
704 ExecQueryDialog(const wxString& cmd);
705
706 wxString GetExecutable() const
707 {
708 return m_executable->GetValue();
709 }
710
711 wxString GetWorkDir() const
712 {
713 return m_useCWD->GetValue() ? m_cwdtext->GetValue() : wxString();
714 }
715
716 void GetEnvironment(wxEnvVariableHashMap& env);
717
718 private:
719 void OnUpdateWorkingDirectoryUI(wxUpdateUIEvent& event)
720 {
721 event.Enable(m_useCWD->GetValue());
722 }
723
724 void OnUpdateEnvironmentUI(wxUpdateUIEvent& event)
725 {
726 event.Enable(m_useEnv->GetValue());
727 }
728
729 wxTextCtrl* m_executable;
730 wxTextCtrl* m_cwdtext;
731 wxTextCtrl* m_envtext;
732 wxCheckBox* m_useCWD;
733 wxCheckBox* m_useEnv;
734
735 DECLARE_EVENT_TABLE()
736 };
737
738 BEGIN_EVENT_TABLE(ExecQueryDialog, wxDialog)
739 EVT_UPDATE_UI(TEXT_CWD, ExecQueryDialog::OnUpdateWorkingDirectoryUI)
740 EVT_UPDATE_UI(TEXT_ENVIRONMENT, ExecQueryDialog::OnUpdateEnvironmentUI)
741 END_EVENT_TABLE()
742
743 ExecQueryDialog::ExecQueryDialog(const wxString& cmd)
744 : wxDialog(NULL, wxID_ANY, DIALOG_TITLE,
745 wxDefaultPosition, wxDefaultSize,
746 wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
747 {
748 wxSizer* globalSizer = new wxBoxSizer(wxVERTICAL);
749
750 m_executable = new wxTextCtrl(this, TEXT_EXECUTABLE, wxString());
751 m_cwdtext = new wxTextCtrl(this, TEXT_CWD, wxString());
752 m_envtext = new wxTextCtrl(this, TEXT_ENVIRONMENT, wxString(),
753 wxDefaultPosition, wxSize(300, 200),
754 wxTE_MULTILINE|wxHSCROLL);
755
756 const wxSizerFlags flagsExpand = wxSizerFlags().Expand().Border();
757 globalSizer->Add(new wxStaticText(this, wxID_ANY, "Enter the command: "),
758 flagsExpand);
759 globalSizer->Add(m_executable, flagsExpand);
760
761 m_useCWD = new wxCheckBox(this, wxID_ANY, "Working directory: ");
762 globalSizer->Add(m_useCWD, flagsExpand);
763 globalSizer->Add(m_cwdtext, flagsExpand);
764
765 m_useEnv = new wxCheckBox(this, wxID_ANY, "Environment: ");
766 globalSizer->Add(m_useEnv, flagsExpand);
767 globalSizer->Add(m_envtext, wxSizerFlags(flagsExpand).Proportion(1));
768
769 globalSizer->Add(CreateStdDialogButtonSizer(wxOK|wxCANCEL), flagsExpand);
770 SetSizerAndFit(globalSizer);
771
772
773 m_executable->SetValue(cmd);
774 m_cwdtext->SetValue(wxGetCwd());
775 wxEnvVariableHashMap env;
776 if ( wxGetEnvMap(&env) )
777 {
778 for ( wxEnvVariableHashMap::iterator it = env.begin();
779 it != env.end();
780 ++it )
781 {
782 m_envtext->AppendText(it->first + '=' + it->second + '\n');
783 }
784 }
785 m_useCWD->SetValue(false);
786 m_useEnv->SetValue(false);
787 }
788
789 void ExecQueryDialog::GetEnvironment(wxEnvVariableHashMap& env)
790 {
791 env.clear();
792 if ( m_useEnv->GetValue() )
793 {
794 wxString name,
795 value;
796
797 const int nb = m_envtext->GetNumberOfLines();
798 for ( int l = 0; l < nb; l++ )
799 {
800 const wxString line = m_envtext->GetLineText(l).Trim();
801
802 if ( !line.empty() )
803 {
804 name = line.BeforeFirst('=', &value);
805 if ( name.empty() )
806 {
807 wxLogWarning("Skipping invalid environment line \"%s\".", line);
808 continue;
809 }
810
811 env[name] = value;
812 }
813 }
814 }
815 }
816
817 static bool QueryExec(wxString& cmd, wxExecuteEnv& env)
818 {
819 ExecQueryDialog dialog(cmd);
820
821 if ( dialog.ShowModal() != wxID_OK )
822 return false;
823
824 cmd = dialog.GetExecutable();
825 env.cwd = dialog.GetWorkDir();
826 dialog.GetEnvironment(env.env);
827
828 return true;
829 }
830
831 // ----------------------------------------------------------------------------
832 // event handlers: exec menu
833 // ----------------------------------------------------------------------------
834
835 int MyFrame::GetExecFlags() const
836 {
837 wxMenuBar* const mbar = GetMenuBar();
838
839 int flags = 0;
840
841 if ( mbar->IsChecked(Exec_Flags_HideConsole) )
842 flags |= wxEXEC_HIDE_CONSOLE;
843 if ( mbar->IsChecked(Exec_Flags_ShowConsole) )
844 flags |= wxEXEC_SHOW_CONSOLE;
845 if ( mbar->IsChecked(Exec_Flags_NoEvents) )
846 flags |= wxEXEC_NOEVENTS;
847
848 return flags;
849 }
850
851 void MyFrame::DoAsyncExec(const wxString& cmd)
852 {
853 MyProcess * const process = new MyProcess(this, cmd);
854 m_pidLast = wxExecute(cmd, wxEXEC_ASYNC | GetExecFlags(), process);
855 if ( !m_pidLast )
856 {
857 wxLogError(wxT("Execution of '%s' failed."), cmd.c_str());
858
859 delete process;
860 }
861 else
862 {
863 wxLogStatus(wxT("Process %ld (%s) launched."), m_pidLast, cmd.c_str());
864
865 m_cmdLast = cmd;
866
867 // the parent frame keeps track of all async processes as it needs to
868 // free them if we exit before the child process terminates
869 AddAsyncProcess(process);
870 }
871 }
872
873 void MyFrame::OnSyncExec(wxCommandEvent& WXUNUSED(event))
874 {
875 wxString cmd;
876 wxExecuteEnv env;
877 if ( !QueryExec(cmd, env) )
878 return;
879
880 wxLogStatus( wxT("'%s' is running please wait..."), cmd.c_str() );
881
882 int code = wxExecute(cmd, wxEXEC_SYNC | GetExecFlags(), NULL, &env);
883
884 wxLogStatus(wxT("Process '%s' terminated with exit code %d."),
885 cmd.c_str(), code);
886
887 m_cmdLast = cmd;
888 }
889
890 void MyFrame::OnAsyncExec(wxCommandEvent& WXUNUSED(event))
891 {
892 wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
893 DIALOG_TITLE,
894 m_cmdLast);
895
896 if ( !cmd )
897 return;
898
899 DoAsyncExec(cmd);
900 }
901
902 void MyFrame::OnShell(wxCommandEvent& WXUNUSED(event))
903 {
904 wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
905 DIALOG_TITLE,
906 m_cmdLast);
907
908 if ( !cmd )
909 return;
910
911 int code = wxShell(cmd);
912 wxLogStatus(wxT("Shell command '%s' terminated with exit code %d."),
913 cmd.c_str(), code);
914 m_cmdLast = cmd;
915 }
916
917 void MyFrame::OnExecWithRedirect(wxCommandEvent& WXUNUSED(event))
918 {
919 if ( !m_cmdLast )
920 {
921 #ifdef __WXMSW__
922 m_cmdLast = "type Makefile.in";
923 #else
924 m_cmdLast = "cat -n Makefile";
925 #endif
926 }
927
928 wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
929 DIALOG_TITLE,
930 m_cmdLast);
931
932 if ( !cmd )
933 return;
934
935 bool sync;
936 switch ( wxMessageBox(wxT("Execute it synchronously?"),
937 wxT("Exec question"),
938 wxYES_NO | wxCANCEL | wxICON_QUESTION, this) )
939 {
940 case wxYES:
941 sync = true;
942 break;
943
944 case wxNO:
945 sync = false;
946 break;
947
948 default:
949 return;
950 }
951
952 if ( sync )
953 {
954 wxLogStatus("\"%s\" is running please wait...", cmd);
955
956 wxStopWatch sw;
957
958 wxArrayString output, errors;
959 int code = wxExecute(cmd, output, errors);
960
961 wxLogStatus("Command \"%s\" terminated after %ldms; exit code %d.",
962 cmd, sw.Time(), code);
963
964 ShowOutput(cmd, output, wxT("Output"));
965 ShowOutput(cmd, errors, wxT("Errors"));
966 }
967 else // async exec
968 {
969 MyPipedProcess *process = new MyPipedProcess(this, cmd);
970 if ( !wxExecute(cmd, wxEXEC_ASYNC, process) )
971 {
972 wxLogError(wxT("Execution of '%s' failed."), cmd.c_str());
973
974 delete process;
975 }
976 else
977 {
978 AddPipedProcess(process);
979 }
980 }
981
982 m_cmdLast = cmd;
983 }
984
985 void MyFrame::OnExecWithPipe(wxCommandEvent& WXUNUSED(event))
986 {
987 if ( !m_cmdLast )
988 m_cmdLast = wxT("tr [a-z] [A-Z]");
989
990 wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
991 DIALOG_TITLE,
992 m_cmdLast);
993
994 if ( !cmd )
995 return;
996
997 wxString input = wxGetTextFromUser(wxT("Enter the string to send to it: "),
998 DIALOG_TITLE);
999 if ( !input )
1000 return;
1001
1002 // always execute the filter asynchronously
1003 MyPipedProcess2 *process = new MyPipedProcess2(this, cmd, input);
1004 long pid = wxExecute(cmd, wxEXEC_ASYNC, process);
1005 if ( pid )
1006 {
1007 wxLogStatus(wxT("Process %ld (%s) launched."), pid, cmd.c_str());
1008
1009 AddPipedProcess(process);
1010 }
1011 else
1012 {
1013 wxLogError(wxT("Execution of '%s' failed."), cmd.c_str());
1014
1015 delete process;
1016 }
1017
1018 m_cmdLast = cmd;
1019 }
1020
1021 void MyFrame::OnPOpen(wxCommandEvent& WXUNUSED(event))
1022 {
1023 wxString cmd = wxGetTextFromUser(wxT("Enter the command to launch: "),
1024 DIALOG_TITLE,
1025 m_cmdLast);
1026 if ( cmd.empty() )
1027 return;
1028
1029 wxProcess *process = wxProcess::Open(cmd);
1030 if ( !process )
1031 {
1032 wxLogError(wxT("Failed to launch the command."));
1033 return;
1034 }
1035
1036 wxLogVerbose(wxT("PID of the new process: %ld"), process->GetPid());
1037
1038 wxOutputStream *out = process->GetOutputStream();
1039 if ( !out )
1040 {
1041 wxLogError(wxT("Failed to connect to child stdin"));
1042 return;
1043 }
1044
1045 wxInputStream *in = process->GetInputStream();
1046 if ( !in )
1047 {
1048 wxLogError(wxT("Failed to connect to child stdout"));
1049 return;
1050 }
1051
1052 new MyPipeFrame(this, cmd, process);
1053 }
1054
1055 static wxString gs_lastFile;
1056
1057 static bool AskUserForFileName()
1058 {
1059 wxString filename;
1060
1061 #if wxUSE_FILEDLG
1062 filename = wxLoadFileSelector(wxT("any"), wxEmptyString, gs_lastFile);
1063 #else // !wxUSE_FILEDLG
1064 filename = wxGetTextFromUser(wxT("Enter the file name"), wxT("exec sample"),
1065 gs_lastFile);
1066 #endif // wxUSE_FILEDLG/!wxUSE_FILEDLG
1067
1068 if ( filename.empty() )
1069 return false;
1070
1071 gs_lastFile = filename;
1072
1073 return true;
1074 }
1075
1076 void MyFrame::OnFileExec(wxCommandEvent& WXUNUSED(event))
1077 {
1078 if ( !AskUserForFileName() )
1079 return;
1080
1081 wxString ext = gs_lastFile.AfterLast(wxT('.'));
1082 wxFileType *ft = wxTheMimeTypesManager->GetFileTypeFromExtension(ext);
1083 if ( !ft )
1084 {
1085 wxLogError(wxT("Impossible to determine the file type for extension '%s'"),
1086 ext.c_str());
1087 return;
1088 }
1089
1090 wxString cmd;
1091 bool ok = ft->GetOpenCommand(&cmd,
1092 wxFileType::MessageParameters(gs_lastFile));
1093 delete ft;
1094 if ( !ok )
1095 {
1096 wxLogError(wxT("Impossible to find out how to open files of extension '%s'"),
1097 ext.c_str());
1098 return;
1099 }
1100
1101 DoAsyncExec(cmd);
1102 }
1103
1104 void MyFrame::OnShowCommandForExt(wxCommandEvent& WXUNUSED(event))
1105 {
1106 static wxString s_ext;
1107
1108 wxString ext = wxGetTextFromUser
1109 (
1110 "Enter the extension without leading dot",
1111 "Exec sample",
1112 s_ext,
1113 this
1114 );
1115 if ( ext.empty() )
1116 return;
1117
1118 s_ext = ext;
1119
1120 wxScopedPtr<wxFileType>
1121 ft(wxTheMimeTypesManager->GetFileTypeFromExtension(ext));
1122 if ( !ft )
1123 {
1124 wxLogError("Information for extension \"%s\" not found", ext);
1125 return;
1126 }
1127
1128 const wxString cmd = ft->GetOpenCommand("file." + ext);
1129 if ( cmd.empty() )
1130 {
1131 wxLogWarning("Open command for extension \"%s\" not defined.", ext);
1132 return;
1133 }
1134
1135 wxLogMessage("Open command for files of extension \"%s\" is\n%s",
1136 ext, cmd);
1137 }
1138
1139 void MyFrame::OnFileLaunch(wxCommandEvent& WXUNUSED(event))
1140 {
1141 if ( !AskUserForFileName() )
1142 return;
1143
1144 if ( !wxLaunchDefaultApplication(gs_lastFile) )
1145 {
1146 wxLogError("Opening \"%s\" in default application failed.", gs_lastFile);
1147 }
1148 }
1149
1150 void MyFrame::OnOpenURL(wxCommandEvent& WXUNUSED(event))
1151 {
1152 static wxString s_url(wxT("http://www.wxwidgets.org/"));
1153
1154 wxString filename = wxGetTextFromUser
1155 (
1156 wxT("Enter the URL"),
1157 wxT("exec sample"),
1158 s_url,
1159 this
1160 );
1161
1162 if ( filename.empty() )
1163 return;
1164
1165 s_url = filename;
1166
1167 if ( !wxLaunchDefaultBrowser(s_url) )
1168 {
1169 wxLogError(wxT("Failed to open URL \"%s\""), s_url.c_str());
1170 }
1171 }
1172
1173 // ----------------------------------------------------------------------------
1174 // DDE stuff
1175 // ----------------------------------------------------------------------------
1176
1177 #ifdef __WINDOWS__
1178
1179 bool MyFrame::GetDDEServer()
1180 {
1181 wxString server = wxGetTextFromUser(wxT("Server to connect to:"),
1182 DIALOG_TITLE, m_server);
1183 if ( !server )
1184 return false;
1185
1186 m_server = server;
1187
1188 wxString topic = wxGetTextFromUser(wxT("DDE topic:"), DIALOG_TITLE, m_topic);
1189 if ( !topic )
1190 return false;
1191
1192 m_topic = topic;
1193
1194 wxString cmd = wxGetTextFromUser(wxT("DDE command:"), DIALOG_TITLE, m_cmdDde);
1195 if ( !cmd )
1196 return false;
1197
1198 m_cmdDde = cmd;
1199
1200 return true;
1201 }
1202
1203 void MyFrame::OnDDEExec(wxCommandEvent& WXUNUSED(event))
1204 {
1205 if ( !GetDDEServer() )
1206 return;
1207
1208 wxDDEClient client;
1209 wxConnectionBase *conn = client.MakeConnection(wxEmptyString, m_server, m_topic);
1210 if ( !conn )
1211 {
1212 wxLogError(wxT("Failed to connect to the DDE server '%s'."),
1213 m_server.c_str());
1214 }
1215 else
1216 {
1217 if ( !conn->Execute(m_cmdDde) )
1218 {
1219 wxLogError(wxT("Failed to execute command '%s' via DDE."),
1220 m_cmdDde.c_str());
1221 }
1222 else
1223 {
1224 wxLogStatus(wxT("Successfully executed DDE command"));
1225 }
1226 }
1227 }
1228
1229 void MyFrame::OnDDERequest(wxCommandEvent& WXUNUSED(event))
1230 {
1231 if ( !GetDDEServer() )
1232 return;
1233
1234 wxDDEClient client;
1235 wxConnectionBase *conn = client.MakeConnection(wxEmptyString, m_server, m_topic);
1236 if ( !conn )
1237 {
1238 wxLogError(wxT("Failed to connect to the DDE server '%s'."),
1239 m_server.c_str());
1240 }
1241 else
1242 {
1243 if ( !conn->Request(m_cmdDde) )
1244 {
1245 wxLogError(wxT("Failed to send request '%s' via DDE."),
1246 m_cmdDde.c_str());
1247 }
1248 else
1249 {
1250 wxLogStatus(wxT("Successfully sent DDE request."));
1251 }
1252 }
1253 }
1254
1255 #endif // __WINDOWS__
1256
1257 // ----------------------------------------------------------------------------
1258 // various helpers
1259 // ----------------------------------------------------------------------------
1260
1261 // input polling
1262 void MyFrame::OnIdle(wxIdleEvent& event)
1263 {
1264 size_t count = m_running.GetCount();
1265 for ( size_t n = 0; n < count; n++ )
1266 {
1267 if ( m_running[n]->HasInput() )
1268 {
1269 event.RequestMore();
1270 }
1271 }
1272 }
1273
1274 void MyFrame::OnIdleTimer(wxTimerEvent& WXUNUSED(event))
1275 {
1276 wxWakeUpIdle();
1277 }
1278
1279 void MyFrame::OnBgTimer(wxTimerEvent& WXUNUSED(event))
1280 {
1281 static unsigned long s_ticks = 0;
1282 SetStatusText(wxString::Format("%lu ticks", s_ticks++), 1);
1283 }
1284
1285 void MyFrame::OnProcessTerminated(MyPipedProcess *process)
1286 {
1287 RemovePipedProcess(process);
1288 }
1289
1290 void MyFrame::OnAsyncTermination(MyProcess *process)
1291 {
1292 m_allAsync.Remove(process);
1293
1294 delete process;
1295 }
1296
1297 void MyFrame::AddPipedProcess(MyPipedProcess *process)
1298 {
1299 if ( m_running.IsEmpty() )
1300 {
1301 // we want to start getting the timer events to ensure that a
1302 // steady stream of idle events comes in -- otherwise we
1303 // wouldn't be able to poll the child process input
1304 m_timerIdleWakeUp.Start(100);
1305 }
1306 //else: the timer is already running
1307
1308 m_running.Add(process);
1309 m_allAsync.Add(process);
1310 }
1311
1312 void MyFrame::RemovePipedProcess(MyPipedProcess *process)
1313 {
1314 m_running.Remove(process);
1315
1316 if ( m_running.IsEmpty() )
1317 {
1318 // we don't need to get idle events all the time any more
1319 m_timerIdleWakeUp.Stop();
1320 }
1321 }
1322
1323 void MyFrame::ShowOutput(const wxString& cmd,
1324 const wxArrayString& output,
1325 const wxString& title)
1326 {
1327 size_t count = output.GetCount();
1328 if ( !count )
1329 return;
1330
1331 m_lbox->Append(wxString::Format(wxT("--- %s of '%s' ---"),
1332 title.c_str(), cmd.c_str()));
1333
1334 for ( size_t n = 0; n < count; n++ )
1335 {
1336 m_lbox->Append(output[n]);
1337 }
1338
1339 m_lbox->Append(wxString::Format(wxT("--- End of %s ---"),
1340 title.Lower().c_str()));
1341 }
1342
1343 // ----------------------------------------------------------------------------
1344 // MyProcess
1345 // ----------------------------------------------------------------------------
1346
1347 void MyProcess::OnTerminate(int pid, int status)
1348 {
1349 wxLogStatus(m_parent, wxT("Process %u ('%s') terminated with exit code %d."),
1350 pid, m_cmd.c_str(), status);
1351
1352 m_parent->OnAsyncTermination(this);
1353 }
1354
1355 // ----------------------------------------------------------------------------
1356 // MyPipedProcess
1357 // ----------------------------------------------------------------------------
1358
1359 bool MyPipedProcess::HasInput()
1360 {
1361 bool hasInput = false;
1362
1363 if ( IsInputAvailable() )
1364 {
1365 wxTextInputStream tis(*GetInputStream());
1366
1367 // this assumes that the output is always line buffered
1368 wxString msg;
1369 msg << m_cmd << wxT(" (stdout): ") << tis.ReadLine();
1370
1371 m_parent->GetLogListBox()->Append(msg);
1372
1373 hasInput = true;
1374 }
1375
1376 if ( IsErrorAvailable() )
1377 {
1378 wxTextInputStream tis(*GetErrorStream());
1379
1380 // this assumes that the output is always line buffered
1381 wxString msg;
1382 msg << m_cmd << wxT(" (stderr): ") << tis.ReadLine();
1383
1384 m_parent->GetLogListBox()->Append(msg);
1385
1386 hasInput = true;
1387 }
1388
1389 return hasInput;
1390 }
1391
1392 void MyPipedProcess::OnTerminate(int pid, int status)
1393 {
1394 // show the rest of the output
1395 while ( HasInput() )
1396 ;
1397
1398 m_parent->OnProcessTerminated(this);
1399
1400 MyProcess::OnTerminate(pid, status);
1401 }
1402
1403 // ----------------------------------------------------------------------------
1404 // MyPipedProcess2
1405 // ----------------------------------------------------------------------------
1406
1407 bool MyPipedProcess2::HasInput()
1408 {
1409 if ( !m_input.empty() )
1410 {
1411 wxTextOutputStream os(*GetOutputStream());
1412 os.WriteString(m_input);
1413
1414 CloseOutput();
1415 m_input.clear();
1416
1417 // call us once again - may be we'll have output
1418 return true;
1419 }
1420
1421 return MyPipedProcess::HasInput();
1422 }
1423
1424 // ============================================================================
1425 // MyPipeFrame implementation
1426 // ============================================================================
1427
1428 MyPipeFrame::MyPipeFrame(wxFrame *parent,
1429 const wxString& cmd,
1430 wxProcess *process)
1431 : wxFrame(parent, wxID_ANY, cmd),
1432 m_process(process),
1433 // in a real program we'd check that the streams are !NULL here
1434 m_out(*process->GetOutputStream()),
1435 m_in(*process->GetInputStream()),
1436 m_err(*process->GetErrorStream())
1437 {
1438 m_process->SetNextHandler(this);
1439
1440 wxPanel *panel = new wxPanel(this, wxID_ANY);
1441
1442 m_textOut = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
1443 wxDefaultPosition, wxDefaultSize,
1444 wxTE_PROCESS_ENTER);
1445 m_textIn = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
1446 wxDefaultPosition, wxDefaultSize,
1447 wxTE_MULTILINE | wxTE_RICH);
1448 m_textIn->SetEditable(false);
1449 m_textErr = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
1450 wxDefaultPosition, wxDefaultSize,
1451 wxTE_MULTILINE | wxTE_RICH);
1452 m_textErr->SetEditable(false);
1453
1454 wxSizer *sizerTop = new wxBoxSizer(wxVERTICAL);
1455 sizerTop->Add(m_textOut, 0, wxGROW | wxALL, 5);
1456
1457 wxSizer *sizerBtns = new wxBoxSizer(wxHORIZONTAL);
1458 sizerBtns->
1459 Add(new wxButton(panel, Exec_Btn_Send, wxT("&Send")), 0, wxALL, 5);
1460 sizerBtns->
1461 Add(new wxButton(panel, Exec_Btn_SendFile, wxT("&File...")), 0, wxALL, 5);
1462 sizerBtns->
1463 Add(new wxButton(panel, Exec_Btn_Get, wxT("&Get")), 0, wxALL, 5);
1464 sizerBtns->
1465 Add(new wxButton(panel, Exec_Btn_Close, wxT("&Close")), 0, wxALL, 5);
1466
1467 sizerTop->Add(sizerBtns, 0, wxCENTRE | wxALL, 5);
1468 sizerTop->Add(m_textIn, 1, wxGROW | wxALL, 5);
1469 sizerTop->Add(m_textErr, 1, wxGROW | wxALL, 5);
1470
1471 panel->SetSizer(sizerTop);
1472 sizerTop->Fit(this);
1473
1474 Show();
1475 }
1476
1477 void MyPipeFrame::OnBtnSendFile(wxCommandEvent& WXUNUSED(event))
1478 {
1479 #if wxUSE_FILEDLG
1480 wxFileDialog filedlg(this, wxT("Select file to send"));
1481 if ( filedlg.ShowModal() != wxID_OK )
1482 return;
1483
1484 wxFFile file(filedlg.GetFilename(), wxT("r"));
1485 wxString data;
1486 if ( !file.IsOpened() || !file.ReadAll(&data) )
1487 return;
1488
1489 // can't write the entire string at once, this risk overflowing the pipe
1490 // and we would dead lock
1491 size_t len = data.length();
1492 const wxChar *pc = data.c_str();
1493 while ( len )
1494 {
1495 const size_t CHUNK_SIZE = 4096;
1496 m_out.Write(pc, len > CHUNK_SIZE ? CHUNK_SIZE : len);
1497
1498 // note that not all data could have been written as we don't block on
1499 // the write end of the pipe
1500 const size_t lenChunk = m_out.LastWrite();
1501
1502 pc += lenChunk;
1503 len -= lenChunk;
1504
1505 DoGet();
1506 }
1507 #endif // wxUSE_FILEDLG
1508 }
1509
1510 void MyPipeFrame::DoGet()
1511 {
1512 // we don't have any way to be notified when any input appears on the
1513 // stream so we have to poll it :-(
1514 DoGetFromStream(m_textIn, m_in);
1515 DoGetFromStream(m_textErr, m_err);
1516 }
1517
1518 void MyPipeFrame::DoGetFromStream(wxTextCtrl *text, wxInputStream& in)
1519 {
1520 while ( in.CanRead() )
1521 {
1522 char buffer[4096];
1523 buffer[in.Read(buffer, WXSIZEOF(buffer) - 1).LastRead()] = '\0';
1524
1525 text->AppendText(buffer);
1526 }
1527 }
1528
1529 void MyPipeFrame::DoClose()
1530 {
1531 m_process->CloseOutput();
1532
1533 DisableInput();
1534 }
1535
1536 void MyPipeFrame::DisableInput()
1537 {
1538 m_textOut->SetEditable(false);
1539 FindWindow(Exec_Btn_Send)->Disable();
1540 FindWindow(Exec_Btn_SendFile)->Disable();
1541 FindWindow(Exec_Btn_Close)->Disable();
1542 }
1543
1544 void MyPipeFrame::DisableOutput()
1545 {
1546 FindWindow(Exec_Btn_Get)->Disable();
1547 }
1548
1549 void MyPipeFrame::OnClose(wxCloseEvent& event)
1550 {
1551 if ( m_process )
1552 {
1553 // we're not interested in getting the process termination notification
1554 // if we are closing it ourselves
1555 wxProcess *process = m_process;
1556 m_process = NULL;
1557 process->SetNextHandler(NULL);
1558
1559 process->CloseOutput();
1560 }
1561
1562 event.Skip();
1563 }
1564
1565 void MyPipeFrame::OnProcessTerm(wxProcessEvent& WXUNUSED(event))
1566 {
1567 DoGet();
1568
1569 wxDELETE(m_process);
1570
1571 wxLogWarning(wxT("The other process has terminated, closing"));
1572
1573 DisableInput();
1574 DisableOutput();
1575 }