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