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