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