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