]> git.saurik.com Git - wxWidgets.git/blame - samples/exec/exec.cpp
Use TAbs in debian/rules, not spaces, as it's a makefile.
[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
416 // Show it and tell the application that it's our main window
07850a49 417 frame->Show(true);
69c33c6c
VZ
418 SetTopWindow(frame);
419
420 // success: wxApp::OnRun() will be called which will enter the main message
07850a49 421 // loop and the application will run. If we returned false here, the
69c33c6c 422 // application would exit immediately.
07850a49 423 return true;
69c33c6c
VZ
424}
425
426// ----------------------------------------------------------------------------
427// main frame
428// ----------------------------------------------------------------------------
429
2b5f62a0
VZ
430#ifdef __VISUALC__
431#pragma warning(disable: 4355) // this used in base member initializer list
432#endif
433
69c33c6c
VZ
434// frame constructor
435MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
07850a49 436 : wxFrame((wxFrame *)NULL, wxID_ANY, title, pos, size),
9cd203f7
VZ
437 m_timerIdleWakeUp(this, Exec_TimerIdle),
438 m_timerBg(this, Exec_TimerBg)
69c33c6c 439{
50567b69
VZ
440 m_pidLast = 0;
441
69c33c6c
VZ
442#ifdef __WXMAC__
443 // we need this in order to allow the about menu relocation, since ABOUT is
444 // not the default id of the about menu
445 wxApp::s_macAboutMenuItemId = Exec_About;
446#endif
447
69c33c6c 448 // create a menu bar
71307412 449 wxMenu *menuFile = new wxMenu(wxEmptyString, wxMENU_TEAROFF);
9a83f860
VZ
450 menuFile->Append(Exec_Kill, wxT("&Kill process...\tCtrl-K"),
451 wxT("Kill a process by PID"));
50567b69 452 menuFile->AppendSeparator();
9a83f860
VZ
453 menuFile->Append(Exec_ClearLog, wxT("&Clear log\tCtrl-L"),
454 wxT("Clear the log window"));
d8e41d42 455 menuFile->AppendSeparator();
9a83f860
VZ
456 menuFile->Append(Exec_BeginBusyCursor, wxT("Show &busy cursor\tCtrl-C"));
457 menuFile->Append(Exec_EndBusyCursor, wxT("Show &normal cursor\tShift-Ctrl-C"));
c6f9dfe8 458 menuFile->AppendSeparator();
9a83f860 459 menuFile->Append(Exec_Quit, wxT("E&xit\tAlt-X"), wxT("Quit this program"));
69c33c6c
VZ
460
461 wxMenu *execMenu = new wxMenu;
9a83f860
VZ
462 execMenu->Append(Exec_SyncExec, wxT("Sync &execution...\tCtrl-E"),
463 wxT("Launch a program and return when it terminates"));
464 execMenu->Append(Exec_SyncNoEventsExec, wxT("Sync execution and &block...\tCtrl-B"),
465 wxT("Launch a program and block until it terminates"));
466 execMenu->Append(Exec_AsyncExec, wxT("&Async execution...\tCtrl-A"),
467 wxT("Launch a program and return immediately"));
468 execMenu->Append(Exec_Shell, wxT("Execute &shell command...\tCtrl-S"),
469 wxT("Launch a shell and execute a command in it"));
f6bcfd97 470 execMenu->AppendSeparator();
9a83f860
VZ
471 execMenu->Append(Exec_Redirect, wxT("Capture command &output...\tCtrl-O"),
472 wxT("Launch a program and capture its output"));
473 execMenu->Append(Exec_Pipe, wxT("&Pipe through command..."),
474 wxT("Pipe a string through a filter"));
475 execMenu->Append(Exec_POpen, wxT("&Open a pipe to a command...\tCtrl-P"),
476 wxT("Open a pipe to and from another program"));
69c33c6c 477
6ba63600 478 execMenu->AppendSeparator();
9a83f860
VZ
479 execMenu->Append(Exec_OpenFile, wxT("Open &file...\tCtrl-F"),
480 wxT("Launch the command to open this kind of files"));
481 execMenu->Append(Exec_LaunchFile, wxT("La&unch file...\tShift-Ctrl-F"),
482 wxT("Launch the default application associated with the file"));
483 execMenu->Append(Exec_OpenURL, wxT("Open &URL...\tCtrl-U"),
484 wxT("Launch the default browser with the given URL"));
d93c719a
VZ
485#ifdef __WINDOWS__
486 execMenu->AppendSeparator();
9a83f860
VZ
487 execMenu->Append(Exec_DDEExec, wxT("Execute command via &DDE...\tCtrl-D"));
488 execMenu->Append(Exec_DDERequest, wxT("Send DDE &request...\tCtrl-R"));
d93c719a
VZ
489#endif
490
71307412 491 wxMenu *helpMenu = new wxMenu(wxEmptyString, wxMENU_TEAROFF);
9a83f860 492 helpMenu->Append(Exec_About, wxT("&About...\tF1"), wxT("Show about dialog"));
69c33c6c
VZ
493
494 // now append the freshly created menu to the menu bar...
495 wxMenuBar *menuBar = new wxMenuBar();
9a83f860
VZ
496 menuBar->Append(menuFile, wxT("&File"));
497 menuBar->Append(execMenu, wxT("&Exec"));
498 menuBar->Append(helpMenu, wxT("&Help"));
69c33c6c
VZ
499
500 // ... and attach this menu bar to the frame
501 SetMenuBar(menuBar);
502
9121bed2 503 // create the listbox in which we will show misc messages as they come
07850a49 504 m_lbox = new wxListBox(this, wxID_ANY);
8ce88dfa
VZ
505 wxFont font(12, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL,
506 wxFONTWEIGHT_NORMAL);
507 if ( font.Ok() )
508 m_lbox->SetFont(font);
9121bed2 509
69c33c6c
VZ
510#if wxUSE_STATUSBAR
511 // create a status bar just for fun (by default with 1 pane only)
9cd203f7 512 CreateStatusBar(2);
9a83f860 513 SetStatusText(wxT("Welcome to wxWidgets exec sample!"));
69c33c6c 514#endif // wxUSE_STATUSBAR
9cd203f7
VZ
515
516 m_timerBg.Start(1000);
69c33c6c
VZ
517}
518
5a8561fc
VZ
519MyFrame::~MyFrame()
520{
521 // any processes left until now must be deleted manually: normally this is
522 // done when the associated process terminates but it must be still running
523 // if this didn't happen until now
524 for ( size_t n = 0; n < m_allAsync.size(); n++ )
525 {
526 delete m_allAsync[n];
527 }
528}
529
50567b69
VZ
530// ----------------------------------------------------------------------------
531// event handlers: file and help menu
532// ----------------------------------------------------------------------------
69c33c6c
VZ
533
534void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
535{
07850a49
WS
536 // true is to force the frame to close
537 Close(true);
69c33c6c
VZ
538}
539
d8e41d42
VZ
540void MyFrame::OnClear(wxCommandEvent& WXUNUSED(event))
541{
542 m_lbox->Clear();
543}
544
c6f9dfe8
VZ
545void MyFrame::OnBeginBusyCursor(wxCommandEvent& WXUNUSED(event))
546{
547 wxBeginBusyCursor();
548}
549
550void MyFrame::OnEndBusyCursor(wxCommandEvent& WXUNUSED(event))
551{
552 wxEndBusyCursor();
553}
554
69c33c6c
VZ
555void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
556{
9a83f860
VZ
557 wxMessageBox(wxT("Exec wxWidgets Sample\n(c) 2000-2002 Vadim Zeitlin"),
558 wxT("About Exec"), wxOK | wxICON_INFORMATION, this);
69c33c6c
VZ
559}
560
50567b69
VZ
561void MyFrame::OnKill(wxCommandEvent& WXUNUSED(event))
562{
9a83f860
VZ
563 long pid = wxGetNumberFromUser(wxT("Please specify the process to kill"),
564 wxT("Enter PID:"),
565 wxT("Exec question"),
50567b69 566 m_pidLast,
9c05a3ca
VZ
567 // we need the full unsigned int range
568 -INT_MAX, INT_MAX,
50567b69
VZ
569 this);
570 if ( pid == -1 )
571 {
572 // cancelled
573 return;
574 }
575
b54ceb72
VZ
576 m_pidLast = pid;
577
50567b69
VZ
578 static const wxString signalNames[] =
579 {
9a83f860
VZ
580 wxT("Just test (SIGNONE)"),
581 wxT("Hangup (SIGHUP)"),
582 wxT("Interrupt (SIGINT)"),
583 wxT("Quit (SIGQUIT)"),
584 wxT("Illegal instruction (SIGILL)"),
585 wxT("Trap (SIGTRAP)"),
586 wxT("Abort (SIGABRT)"),
587 wxT("Emulated trap (SIGEMT)"),
588 wxT("FP exception (SIGFPE)"),
589 wxT("Kill (SIGKILL)"),
590 wxT("Bus (SIGBUS)"),
591 wxT("Segment violation (SIGSEGV)"),
592 wxT("System (SIGSYS)"),
593 wxT("Broken pipe (SIGPIPE)"),
594 wxT("Alarm (SIGALRM)"),
595 wxT("Terminate (SIGTERM)"),
50567b69
VZ
596 };
597
b54ceb72 598 static int s_sigLast = wxSIGNONE;
9a83f860
VZ
599 int sig = wxGetSingleChoiceIndex(wxT("How to kill the process?"),
600 wxT("Exec question"),
50567b69 601 WXSIZEOF(signalNames), signalNames,
b54ceb72 602 s_sigLast,
50567b69
VZ
603 this);
604 switch ( sig )
605 {
606 default:
9a83f860 607 wxFAIL_MSG( wxT("unexpected return value") );
50567b69
VZ
608 // fall through
609
610 case -1:
611 // cancelled
612 return;
613
614 case wxSIGNONE:
615 case wxSIGHUP:
616 case wxSIGINT:
617 case wxSIGQUIT:
618 case wxSIGILL:
619 case wxSIGTRAP:
620 case wxSIGABRT:
621 case wxSIGEMT:
622 case wxSIGFPE:
623 case wxSIGKILL:
624 case wxSIGBUS:
625 case wxSIGSEGV:
626 case wxSIGSYS:
627 case wxSIGPIPE:
628 case wxSIGALRM:
629 case wxSIGTERM:
630 break;
631 }
632
b54ceb72
VZ
633 s_sigLast = sig;
634
635 if ( sig == wxSIGNONE )
50567b69 636 {
b54ceb72 637 // This simply calls Kill(wxSIGNONE) but using it is more convenient.
50567b69 638 if ( wxProcess::Exists(pid) )
43b2d5e7 639 {
9a83f860 640 wxLogStatus(wxT("Process %ld is running."), pid);
43b2d5e7 641 }
50567b69 642 else
43b2d5e7 643 {
9a83f860 644 wxLogStatus(wxT("No process with pid = %ld."), pid);
43b2d5e7 645 }
50567b69
VZ
646 }
647 else // not SIGNONE
648 {
649 wxKillError rc = wxProcess::Kill(pid, (wxSignal)sig);
650 if ( rc == wxKILL_OK )
651 {
9a83f860 652 wxLogStatus(wxT("Process %ld killed with signal %d."), pid, sig);
50567b69
VZ
653 }
654 else
655 {
656 static const wxChar *errorText[] =
657 {
9a83f860
VZ
658 wxT(""), // no error
659 wxT("signal not supported"),
660 wxT("permission denied"),
661 wxT("no such process"),
662 wxT("unspecified error"),
50567b69
VZ
663 };
664
9a83f860 665 wxLogStatus(wxT("Failed to kill process %ld with signal %d: %s"),
50567b69
VZ
666 pid, sig, errorText[rc]);
667 }
668 }
669}
670
164db92c
VZ
671// ----------------------------------------------------------------------------
672// execution options dialog
673// ----------------------------------------------------------------------------
674
675enum ExecQueryDialogID
676{
677 TEXT_EXECUTABLE,
678 TEXT_CWD,
679 TEXT_ENVIRONMENT
680};
681
682class ExecQueryDialog : public wxDialog
683{
684public:
685 ExecQueryDialog(const wxString& cmd);
686
687 wxString GetExecutable() const
688 {
689 return m_executable->GetValue();
690 }
691
692 wxString GetWorkDir() const
693 {
694 return m_useCWD->GetValue() ? m_cwdtext->GetValue() : wxString();
695 }
696
697 void GetEnvironment(wxEnvVariableHashMap& env);
698
699private:
700 void OnUpdateWorkingDirectoryUI(wxUpdateUIEvent& event)
701 {
702 event.Enable(m_useCWD->GetValue());
703 }
704
705 void OnUpdateEnvironmentUI(wxUpdateUIEvent& event)
706 {
707 event.Enable(m_useEnv->GetValue());
708 }
709
710 wxTextCtrl* m_executable;
711 wxTextCtrl* m_cwdtext;
712 wxTextCtrl* m_envtext;
713 wxCheckBox* m_useCWD;
714 wxCheckBox* m_useEnv;
715
716 DECLARE_EVENT_TABLE()
717};
718
719BEGIN_EVENT_TABLE(ExecQueryDialog, wxDialog)
720 EVT_UPDATE_UI(TEXT_CWD, ExecQueryDialog::OnUpdateWorkingDirectoryUI)
721 EVT_UPDATE_UI(TEXT_ENVIRONMENT, ExecQueryDialog::OnUpdateEnvironmentUI)
722END_EVENT_TABLE()
723
724ExecQueryDialog::ExecQueryDialog(const wxString& cmd)
725 : wxDialog(NULL, wxID_ANY, DIALOG_TITLE,
726 wxDefaultPosition, wxDefaultSize,
727 wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
728{
729 wxSizer* globalSizer = new wxBoxSizer(wxVERTICAL);
730
731 m_executable = new wxTextCtrl(this, TEXT_EXECUTABLE, wxString());
732 m_cwdtext = new wxTextCtrl(this, TEXT_CWD, wxString());
733 m_envtext = new wxTextCtrl(this, TEXT_ENVIRONMENT, wxString(),
734 wxDefaultPosition, wxSize(300, 200),
735 wxTE_MULTILINE|wxHSCROLL);
736
737 const wxSizerFlags flagsExpand = wxSizerFlags().Expand().Border();
738 globalSizer->Add(new wxStaticText(this, wxID_ANY, "Enter the command: "),
739 flagsExpand);
740 globalSizer->Add(m_executable, flagsExpand);
741
742 m_useCWD = new wxCheckBox(this, wxID_ANY, "Working directory: ");
743 globalSizer->Add(m_useCWD, flagsExpand);
744 globalSizer->Add(m_cwdtext, flagsExpand);
745
746 m_useEnv = new wxCheckBox(this, wxID_ANY, "Environment: ");
747 globalSizer->Add(m_useEnv, flagsExpand);
748 globalSizer->Add(m_envtext, wxSizerFlags(flagsExpand).Proportion(1));
749
750 globalSizer->Add(CreateStdDialogButtonSizer(wxOK|wxCANCEL), flagsExpand);
751 SetSizerAndFit(globalSizer);
752
753
754 m_executable->SetValue(cmd);
755 m_cwdtext->SetValue(wxGetCwd());
756 wxEnvVariableHashMap env;
757 if ( wxGetEnvMap(&env) )
758 {
759 for ( wxEnvVariableHashMap::iterator it = env.begin();
760 it != env.end();
761 ++it )
762 {
763 m_envtext->AppendText(it->first + '=' + it->second + '\n');
764 }
765 }
766 m_useCWD->SetValue(false);
767 m_useEnv->SetValue(false);
768}
769
770void ExecQueryDialog::GetEnvironment(wxEnvVariableHashMap& env)
771{
772 env.clear();
773 if ( m_useEnv->GetValue() )
774 {
775 wxString name,
776 value;
777
778 const int nb = m_envtext->GetNumberOfLines();
779 for ( int l = 0; l < nb; l++ )
780 {
781 const wxString line = m_envtext->GetLineText(l).Trim();
782
783 if ( !line.empty() )
784 {
785 name = line.BeforeFirst('=', &value);
786 if ( name.empty() )
787 {
788 wxLogWarning("Skipping invalid environment line \"%s\".", line);
789 continue;
790 }
791
792 env[name] = value;
793 }
794 }
795 }
796}
797
798static bool QueryExec(wxString& cmd, wxExecuteEnv& env)
799{
800 ExecQueryDialog dialog(cmd);
801
802 if ( dialog.ShowModal() != wxID_OK )
803 return false;
804
805 cmd = dialog.GetExecutable();
806 env.cwd = dialog.GetWorkDir();
807 dialog.GetEnvironment(env.env);
808
809 return true;
810}
811
50567b69
VZ
812// ----------------------------------------------------------------------------
813// event handlers: exec menu
814// ----------------------------------------------------------------------------
815
6ba63600
VZ
816void MyFrame::DoAsyncExec(const wxString& cmd)
817{
5a8561fc 818 MyProcess * const process = new MyProcess(this, cmd);
4c1ef422 819 m_pidLast = wxExecute(cmd, wxEXEC_ASYNC, process);
50567b69 820 if ( !m_pidLast )
6ba63600 821 {
9a83f860 822 wxLogError(wxT("Execution of '%s' failed."), cmd.c_str());
6ba63600
VZ
823
824 delete process;
825 }
826 else
827 {
9a83f860 828 wxLogStatus(wxT("Process %ld (%s) launched."), m_pidLast, cmd.c_str());
6ba63600
VZ
829
830 m_cmdLast = cmd;
5a8561fc
VZ
831
832 // the parent frame keeps track of all async processes as it needs to
833 // free them if we exit before the child process terminates
834 AddAsyncProcess(process);
6ba63600
VZ
835 }
836}
837
69c33c6c
VZ
838void MyFrame::OnSyncExec(wxCommandEvent& WXUNUSED(event))
839{
164db92c
VZ
840 wxString cmd;
841 wxExecuteEnv env;
842 if ( !QueryExec(cmd, env) )
69c33c6c
VZ
843 return;
844
9a83f860 845 wxLogStatus( wxT("'%s' is running please wait..."), cmd.c_str() );
d31b7b68 846
164db92c 847 int code = wxExecute(cmd, wxEXEC_SYNC, NULL, &env);
d31b7b68 848
9a83f860 849 wxLogStatus(wxT("Process '%s' terminated with exit code %d."),
aec18ff7
MB
850 cmd.c_str(), code);
851
69c33c6c
VZ
852 m_cmdLast = cmd;
853}
854
9b6b9e0c
VZ
855void MyFrame::OnSyncNoEventsExec(wxCommandEvent& WXUNUSED(event))
856{
9a83f860 857 wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
9b6b9e0c
VZ
858 DIALOG_TITLE,
859 m_cmdLast);
860
861 if ( !cmd )
862 return;
863
9a83f860 864 wxLogStatus( wxT("'%s' is running please wait..."), cmd.c_str() );
9b6b9e0c
VZ
865
866 int code = wxExecute(cmd, wxEXEC_BLOCK);
867
9a83f860 868 wxLogStatus(wxT("Process '%s' terminated with exit code %d."),
9b6b9e0c
VZ
869 cmd.c_str(), code);
870
871 m_cmdLast = cmd;
872}
873
69c33c6c
VZ
874void MyFrame::OnAsyncExec(wxCommandEvent& WXUNUSED(event))
875{
9a83f860 876 wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
d93c719a 877 DIALOG_TITLE,
69c33c6c
VZ
878 m_cmdLast);
879
880 if ( !cmd )
881 return;
882
6ba63600 883 DoAsyncExec(cmd);
69c33c6c
VZ
884}
885
886void MyFrame::OnShell(wxCommandEvent& WXUNUSED(event))
887{
9a83f860 888 wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
d93c719a 889 DIALOG_TITLE,
69c33c6c
VZ
890 m_cmdLast);
891
892 if ( !cmd )
893 return;
894
895 int code = wxShell(cmd);
9a83f860 896 wxLogStatus(wxT("Shell command '%s' terminated with exit code %d."),
69c33c6c
VZ
897 cmd.c_str(), code);
898 m_cmdLast = cmd;
899}
900
d8e41d42
VZ
901void MyFrame::OnExecWithRedirect(wxCommandEvent& WXUNUSED(event))
902{
69328170
VZ
903 if ( !m_cmdLast )
904 {
905#ifdef __WXMSW__
906 m_cmdLast = "type Makefile.in";
907#else
908 m_cmdLast = "cat -n Makefile";
909#endif
910 }
911
9a83f860 912 wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
d8e41d42
VZ
913 DIALOG_TITLE,
914 m_cmdLast);
915
916 if ( !cmd )
917 return;
918
cd6ce4a9 919 bool sync;
9a83f860
VZ
920 switch ( wxMessageBox(wxT("Execute it synchronously?"),
921 wxT("Exec question"),
cd6ce4a9 922 wxYES_NO | wxCANCEL | wxICON_QUESTION, this) )
d8e41d42 923 {
cd6ce4a9 924 case wxYES:
07850a49 925 sync = true;
cd6ce4a9 926 break;
d8e41d42 927
cd6ce4a9 928 case wxNO:
07850a49 929 sync = false;
cd6ce4a9
VZ
930 break;
931
932 default:
933 return;
d8e41d42 934 }
cd6ce4a9
VZ
935
936 if ( sync )
d8e41d42 937 {
22386791
VZ
938 wxLogStatus("\"%s\" is running please wait...", cmd);
939
940 wxStopWatch sw;
ce0bb89d 941
f6bcfd97
BP
942 wxArrayString output, errors;
943 int code = wxExecute(cmd, output, errors);
ce0bb89d 944
22386791
VZ
945 wxLogStatus("Command \"%s\" terminated after %ldms; exit code %d.",
946 cmd, sw.Time(), code);
cd6ce4a9
VZ
947
948 if ( code != -1 )
949 {
9a83f860
VZ
950 ShowOutput(cmd, output, wxT("Output"));
951 ShowOutput(cmd, errors, wxT("Errors"));
cd6ce4a9
VZ
952 }
953 }
954 else // async exec
955 {
956 MyPipedProcess *process = new MyPipedProcess(this, cmd);
4c1ef422 957 if ( !wxExecute(cmd, wxEXEC_ASYNC, process) )
cd6ce4a9 958 {
9a83f860 959 wxLogError(wxT("Execution of '%s' failed."), cmd.c_str());
cd6ce4a9
VZ
960
961 delete process;
962 }
963 else
964 {
5a8561fc 965 AddPipedProcess(process);
cd6ce4a9 966 }
d8e41d42 967 }
cd6ce4a9
VZ
968
969 m_cmdLast = cmd;
d8e41d42
VZ
970}
971
f6bcfd97
BP
972void MyFrame::OnExecWithPipe(wxCommandEvent& WXUNUSED(event))
973{
974 if ( !m_cmdLast )
9a83f860 975 m_cmdLast = wxT("tr [a-z] [A-Z]");
f6bcfd97 976
9a83f860 977 wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
f6bcfd97
BP
978 DIALOG_TITLE,
979 m_cmdLast);
980
981 if ( !cmd )
982 return;
983
9a83f860 984 wxString input = wxGetTextFromUser(wxT("Enter the string to send to it: "),
f6bcfd97
BP
985 DIALOG_TITLE);
986 if ( !input )
987 return;
988
989 // always execute the filter asynchronously
990 MyPipedProcess2 *process = new MyPipedProcess2(this, cmd, input);
aec18ff7 991 long pid = wxExecute(cmd, wxEXEC_ASYNC, process);
f6bcfd97
BP
992 if ( pid )
993 {
9a83f860 994 wxLogStatus(wxT("Process %ld (%s) launched."), pid, cmd.c_str());
f6bcfd97 995
5a8561fc 996 AddPipedProcess(process);
f6bcfd97
BP
997 }
998 else
999 {
9a83f860 1000 wxLogError(wxT("Execution of '%s' failed."), cmd.c_str());
f6bcfd97
BP
1001
1002 delete process;
1003 }
1004
1005 m_cmdLast = cmd;
1006}
1007
87728739 1008void MyFrame::OnPOpen(wxCommandEvent& WXUNUSED(event))
eb671557 1009{
9a83f860 1010 wxString cmd = wxGetTextFromUser(wxT("Enter the command to launch: "),
eb671557
VZ
1011 DIALOG_TITLE,
1012 m_cmdLast);
1013 if ( cmd.empty() )
1014 return;
1015
1016 wxProcess *process = wxProcess::Open(cmd);
1017 if ( !process )
1018 {
9a83f860 1019 wxLogError(wxT("Failed to launch the command."));
eb671557
VZ
1020 return;
1021 }
1022
9a83f860 1023 wxLogVerbose(wxT("PID of the new process: %ld"), process->GetPid());
a387938f 1024
eb671557
VZ
1025 wxOutputStream *out = process->GetOutputStream();
1026 if ( !out )
1027 {
9a83f860 1028 wxLogError(wxT("Failed to connect to child stdin"));
eb671557
VZ
1029 return;
1030 }
1031
1032 wxInputStream *in = process->GetInputStream();
1033 if ( !in )
1034 {
9a83f860 1035 wxLogError(wxT("Failed to connect to child stdout"));
eb671557
VZ
1036 return;
1037 }
1038
1039 new MyPipeFrame(this, cmd, process);
1040}
1041
979a7347 1042static wxString gs_lastFile;
6ba63600 1043
979a7347
VZ
1044static bool AskUserForFileName()
1045{
71307412
WS
1046 wxString filename;
1047
1048#if wxUSE_FILEDLG
9a83f860 1049 filename = wxLoadFileSelector(wxT("any"), wxEmptyString, gs_lastFile);
ba59de5d 1050#else // !wxUSE_FILEDLG
9a83f860 1051 filename = wxGetTextFromUser(wxT("Enter the file name"), wxT("exec sample"),
979a7347 1052 gs_lastFile);
ba59de5d 1053#endif // wxUSE_FILEDLG/!wxUSE_FILEDLG
71307412
WS
1054
1055 if ( filename.empty() )
979a7347 1056 return false;
6ba63600 1057
979a7347 1058 gs_lastFile = filename;
6ba63600 1059
979a7347
VZ
1060 return true;
1061}
1062
1063void MyFrame::OnFileExec(wxCommandEvent& WXUNUSED(event))
1064{
1065 if ( !AskUserForFileName() )
1066 return;
1067
9a83f860 1068 wxString ext = gs_lastFile.AfterLast(wxT('.'));
6ba63600
VZ
1069 wxFileType *ft = wxTheMimeTypesManager->GetFileTypeFromExtension(ext);
1070 if ( !ft )
1071 {
9a83f860 1072 wxLogError(wxT("Impossible to determine the file type for extension '%s'"),
6ba63600
VZ
1073 ext.c_str());
1074 return;
1075 }
1076
1077 wxString cmd;
1078 bool ok = ft->GetOpenCommand(&cmd,
979a7347 1079 wxFileType::MessageParameters(gs_lastFile));
6ba63600
VZ
1080 delete ft;
1081 if ( !ok )
1082 {
9a83f860 1083 wxLogError(wxT("Impossible to find out how to open files of extension '%s'"),
6ba63600
VZ
1084 ext.c_str());
1085 return;
1086 }
1087
1088 DoAsyncExec(cmd);
1089}
1090
979a7347
VZ
1091void MyFrame::OnFileLaunch(wxCommandEvent& WXUNUSED(event))
1092{
1093 if ( !AskUserForFileName() )
1094 return;
1095
1096 if ( !wxLaunchDefaultApplication(gs_lastFile) )
1097 {
1098 wxLogError("Opening \"%s\" in default application failed.", gs_lastFile);
1099 }
1100}
1101
ba59de5d
VZ
1102void MyFrame::OnOpenURL(wxCommandEvent& WXUNUSED(event))
1103{
9a83f860 1104 static wxString s_url(wxT("http://www.wxwidgets.org/"));
ba59de5d
VZ
1105
1106 wxString filename = wxGetTextFromUser
1107 (
9a83f860
VZ
1108 wxT("Enter the URL"),
1109 wxT("exec sample"),
979a7347 1110 s_url,
ba59de5d
VZ
1111 this
1112 );
1113
1114 if ( filename.empty() )
1115 return;
1116
979a7347 1117 s_url = filename;
ba59de5d 1118
979a7347 1119 if ( !wxLaunchDefaultBrowser(s_url) )
43b2d5e7 1120 {
9a83f860 1121 wxLogError(wxT("Failed to open URL \"%s\""), s_url.c_str());
43b2d5e7 1122 }
ba59de5d
VZ
1123}
1124
50567b69
VZ
1125// ----------------------------------------------------------------------------
1126// DDE stuff
1127// ----------------------------------------------------------------------------
1128
d93c719a 1129#ifdef __WINDOWS__
ca289436
VZ
1130
1131bool MyFrame::GetDDEServer()
1132{
9a83f860 1133 wxString server = wxGetTextFromUser(wxT("Server to connect to:"),
ca289436 1134 DIALOG_TITLE, m_server);
d93c719a 1135 if ( !server )
07850a49 1136 return false;
ca289436
VZ
1137
1138 m_server = server;
d93c719a 1139
9a83f860 1140 wxString topic = wxGetTextFromUser(wxT("DDE topic:"), DIALOG_TITLE, m_topic);
d93c719a 1141 if ( !topic )
07850a49 1142 return false;
d93c719a 1143
ca289436
VZ
1144 m_topic = topic;
1145
9a83f860 1146 wxString cmd = wxGetTextFromUser(wxT("DDE command:"), DIALOG_TITLE, m_cmdDde);
d93c719a 1147 if ( !cmd )
07850a49 1148 return false;
ca289436
VZ
1149
1150 m_cmdDde = cmd;
1151
07850a49 1152 return true;
ca289436
VZ
1153}
1154
1155void MyFrame::OnDDEExec(wxCommandEvent& WXUNUSED(event))
1156{
1157 if ( !GetDDEServer() )
d93c719a
VZ
1158 return;
1159
1160 wxDDEClient client;
71307412 1161 wxConnectionBase *conn = client.MakeConnection(wxEmptyString, m_server, m_topic);
d93c719a
VZ
1162 if ( !conn )
1163 {
9a83f860 1164 wxLogError(wxT("Failed to connect to the DDE server '%s'."),
ca289436 1165 m_server.c_str());
d93c719a
VZ
1166 }
1167 else
1168 {
ca289436 1169 if ( !conn->Execute(m_cmdDde) )
d93c719a 1170 {
9a83f860 1171 wxLogError(wxT("Failed to execute command '%s' via DDE."),
ca289436 1172 m_cmdDde.c_str());
d93c719a
VZ
1173 }
1174 else
1175 {
9a83f860 1176 wxLogStatus(wxT("Successfully executed DDE command"));
d93c719a
VZ
1177 }
1178 }
d93c719a
VZ
1179}
1180
ca289436
VZ
1181void MyFrame::OnDDERequest(wxCommandEvent& WXUNUSED(event))
1182{
1183 if ( !GetDDEServer() )
1184 return;
1185
1186 wxDDEClient client;
71307412 1187 wxConnectionBase *conn = client.MakeConnection(wxEmptyString, m_server, m_topic);
ca289436
VZ
1188 if ( !conn )
1189 {
9a83f860 1190 wxLogError(wxT("Failed to connect to the DDE server '%s'."),
ca289436
VZ
1191 m_server.c_str());
1192 }
1193 else
1194 {
1195 if ( !conn->Request(m_cmdDde) )
1196 {
9a83f860 1197 wxLogError(wxT("Failed to send request '%s' via DDE."),
ca289436
VZ
1198 m_cmdDde.c_str());
1199 }
1200 else
1201 {
9a83f860 1202 wxLogStatus(wxT("Successfully sent DDE request."));
ca289436
VZ
1203 }
1204 }
1205}
1206
1207#endif // __WINDOWS__
1208
50567b69
VZ
1209// ----------------------------------------------------------------------------
1210// various helpers
1211// ----------------------------------------------------------------------------
1212
cd6ce4a9
VZ
1213// input polling
1214void MyFrame::OnIdle(wxIdleEvent& event)
1215{
1216 size_t count = m_running.GetCount();
1217 for ( size_t n = 0; n < count; n++ )
1218 {
1219 if ( m_running[n]->HasInput() )
1220 {
1221 event.RequestMore();
1222 }
1223 }
1224}
1225
9cd203f7 1226void MyFrame::OnIdleTimer(wxTimerEvent& WXUNUSED(event))
92a2a7eb
VZ
1227{
1228 wxWakeUpIdle();
1229}
1230
9cd203f7
VZ
1231void MyFrame::OnBgTimer(wxTimerEvent& WXUNUSED(event))
1232{
1233 static unsigned long s_ticks = 0;
1234 SetStatusText(wxString::Format("%lu ticks", s_ticks++), 1);
1235}
1236
f6bcfd97
BP
1237void MyFrame::OnProcessTerminated(MyPipedProcess *process)
1238{
5a8561fc
VZ
1239 RemovePipedProcess(process);
1240}
1241
1242void MyFrame::OnAsyncTermination(MyProcess *process)
1243{
1244 m_allAsync.Remove(process);
1245
1246 delete process;
1247}
1248
1249void MyFrame::AddPipedProcess(MyPipedProcess *process)
1250{
1251 if ( m_running.IsEmpty() )
1252 {
1253 // we want to start getting the timer events to ensure that a
1254 // steady stream of idle events comes in -- otherwise we
1255 // wouldn't be able to poll the child process input
1256 m_timerIdleWakeUp.Start(100);
1257 }
1258 //else: the timer is already running
1259
1260 m_running.Add(process);
1261 m_allAsync.Add(process);
f6bcfd97
BP
1262}
1263
5a8561fc
VZ
1264void MyFrame::RemovePipedProcess(MyPipedProcess *process)
1265{
1266 m_running.Remove(process);
1267
1268 if ( m_running.IsEmpty() )
1269 {
1270 // we don't need to get idle events all the time any more
1271 m_timerIdleWakeUp.Stop();
1272 }
1273}
f6bcfd97
BP
1274
1275void MyFrame::ShowOutput(const wxString& cmd,
1276 const wxArrayString& output,
1277 const wxString& title)
1278{
1279 size_t count = output.GetCount();
1280 if ( !count )
1281 return;
1282
9a83f860 1283 m_lbox->Append(wxString::Format(wxT("--- %s of '%s' ---"),
f6bcfd97
BP
1284 title.c_str(), cmd.c_str()));
1285
1286 for ( size_t n = 0; n < count; n++ )
1287 {
1288 m_lbox->Append(output[n]);
1289 }
1290
9a83f860 1291 m_lbox->Append(wxString::Format(wxT("--- End of %s ---"),
eb671557 1292 title.Lower().c_str()));
f6bcfd97
BP
1293}
1294
69c33c6c
VZ
1295// ----------------------------------------------------------------------------
1296// MyProcess
1297// ----------------------------------------------------------------------------
1298
1299void MyProcess::OnTerminate(int pid, int status)
1300{
9a83f860 1301 wxLogStatus(m_parent, wxT("Process %u ('%s') terminated with exit code %d."),
69c33c6c
VZ
1302 pid, m_cmd.c_str(), status);
1303
5a8561fc 1304 m_parent->OnAsyncTermination(this);
69c33c6c 1305}
d8e41d42 1306
cd6ce4a9
VZ
1307// ----------------------------------------------------------------------------
1308// MyPipedProcess
1309// ----------------------------------------------------------------------------
1310
1311bool MyPipedProcess::HasInput()
d8e41d42 1312{
07850a49 1313 bool hasInput = false;
f6bcfd97 1314
92a2a7eb 1315 if ( IsInputAvailable() )
cd6ce4a9 1316 {
92a2a7eb 1317 wxTextInputStream tis(*GetInputStream());
cd6ce4a9
VZ
1318
1319 // this assumes that the output is always line buffered
1320 wxString msg;
9a83f860 1321 msg << m_cmd << wxT(" (stdout): ") << tis.ReadLine();
d8e41d42 1322
cd6ce4a9
VZ
1323 m_parent->GetLogListBox()->Append(msg);
1324
07850a49 1325 hasInput = true;
cd6ce4a9 1326 }
f6bcfd97 1327
92a2a7eb 1328 if ( IsErrorAvailable() )
d8e41d42 1329 {
92a2a7eb 1330 wxTextInputStream tis(*GetErrorStream());
f6bcfd97
BP
1331
1332 // this assumes that the output is always line buffered
1333 wxString msg;
9a83f860 1334 msg << m_cmd << wxT(" (stderr): ") << tis.ReadLine();
f6bcfd97
BP
1335
1336 m_parent->GetLogListBox()->Append(msg);
1337
07850a49 1338 hasInput = true;
d8e41d42 1339 }
f6bcfd97
BP
1340
1341 return hasInput;
cd6ce4a9
VZ
1342}
1343
1344void MyPipedProcess::OnTerminate(int pid, int status)
1345{
1346 // show the rest of the output
1347 while ( HasInput() )
1348 ;
1349
1350 m_parent->OnProcessTerminated(this);
d8e41d42
VZ
1351
1352 MyProcess::OnTerminate(pid, status);
1353}
f6bcfd97
BP
1354
1355// ----------------------------------------------------------------------------
1356// MyPipedProcess2
1357// ----------------------------------------------------------------------------
1358
1359bool MyPipedProcess2::HasInput()
1360{
11fdee42 1361 if ( !m_input.empty() )
f6bcfd97
BP
1362 {
1363 wxTextOutputStream os(*GetOutputStream());
1364 os.WriteString(m_input);
1365
1366 CloseOutput();
1367 m_input.clear();
1368
1369 // call us once again - may be we'll have output
07850a49 1370 return true;
f6bcfd97
BP
1371 }
1372
1373 return MyPipedProcess::HasInput();
1374}
eb671557
VZ
1375
1376// ============================================================================
1377// MyPipeFrame implementation
1378// ============================================================================
1379
1380MyPipeFrame::MyPipeFrame(wxFrame *parent,
1381 const wxString& cmd,
1382 wxProcess *process)
07850a49 1383 : wxFrame(parent, wxID_ANY, cmd),
eb671557
VZ
1384 m_process(process),
1385 // in a real program we'd check that the streams are !NULL here
f6def1fa 1386 m_out(*process->GetOutputStream()),
eb671557 1387 m_in(*process->GetInputStream()),
f6def1fa 1388 m_err(*process->GetErrorStream())
eb671557 1389{
7ca528cb
VZ
1390 m_process->SetNextHandler(this);
1391
07850a49 1392 wxPanel *panel = new wxPanel(this, wxID_ANY);
7ca528cb 1393
71307412 1394 m_textOut = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
eb671557
VZ
1395 wxDefaultPosition, wxDefaultSize,
1396 wxTE_PROCESS_ENTER);
71307412 1397 m_textIn = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
1a278e7b
VZ
1398 wxDefaultPosition, wxDefaultSize,
1399 wxTE_MULTILINE | wxTE_RICH);
1400 m_textIn->SetEditable(false);
71307412 1401 m_textErr = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
1a278e7b
VZ
1402 wxDefaultPosition, wxDefaultSize,
1403 wxTE_MULTILINE | wxTE_RICH);
1404 m_textErr->SetEditable(false);
eb671557
VZ
1405
1406 wxSizer *sizerTop = new wxBoxSizer(wxVERTICAL);
1a278e7b 1407 sizerTop->Add(m_textOut, 0, wxGROW | wxALL, 5);
eb671557
VZ
1408
1409 wxSizer *sizerBtns = new wxBoxSizer(wxHORIZONTAL);
1a278e7b 1410 sizerBtns->
9a83f860 1411 Add(new wxButton(panel, Exec_Btn_Send, wxT("&Send")), 0, wxALL, 5);
1a278e7b 1412 sizerBtns->
9a83f860 1413 Add(new wxButton(panel, Exec_Btn_SendFile, wxT("&File...")), 0, wxALL, 5);
1a278e7b 1414 sizerBtns->
9a83f860 1415 Add(new wxButton(panel, Exec_Btn_Get, wxT("&Get")), 0, wxALL, 5);
1a278e7b 1416 sizerBtns->
9a83f860 1417 Add(new wxButton(panel, Exec_Btn_Close, wxT("&Close")), 0, wxALL, 5);
eb671557
VZ
1418
1419 sizerTop->Add(sizerBtns, 0, wxCENTRE | wxALL, 5);
1a278e7b
VZ
1420 sizerTop->Add(m_textIn, 1, wxGROW | wxALL, 5);
1421 sizerTop->Add(m_textErr, 1, wxGROW | wxALL, 5);
eb671557 1422
7ca528cb 1423 panel->SetSizer(sizerTop);
eb671557
VZ
1424 sizerTop->Fit(this);
1425
1426 Show();
1427}
1428
1a278e7b
VZ
1429void MyPipeFrame::OnBtnSendFile(wxCommandEvent& WXUNUSED(event))
1430{
71307412 1431#if wxUSE_FILEDLG
9a83f860 1432 wxFileDialog filedlg(this, wxT("Select file to send"));
1a278e7b
VZ
1433 if ( filedlg.ShowModal() != wxID_OK )
1434 return;
1435
9a83f860 1436 wxFFile file(filedlg.GetFilename(), wxT("r"));
1a278e7b
VZ
1437 wxString data;
1438 if ( !file.IsOpened() || !file.ReadAll(&data) )
1439 return;
1440
1441 // can't write the entire string at once, this risk overflowing the pipe
1442 // and we would dead lock
1443 size_t len = data.length();
1444 const wxChar *pc = data.c_str();
1445 while ( len )
1446 {
1447 const size_t CHUNK_SIZE = 4096;
42d0aa30 1448 m_out.Write(pc, len > CHUNK_SIZE ? CHUNK_SIZE : len);
1a278e7b 1449
42d0aa30
VZ
1450 // note that not all data could have been written as we don't block on
1451 // the write end of the pipe
1452 const size_t lenChunk = m_out.LastWrite();
1a278e7b
VZ
1453
1454 pc += lenChunk;
1455 len -= lenChunk;
1456
1457 DoGet();
1458 }
71307412 1459#endif // wxUSE_FILEDLG
1a278e7b
VZ
1460}
1461
eb671557
VZ
1462void MyPipeFrame::DoGet()
1463{
7ca528cb
VZ
1464 // we don't have any way to be notified when any input appears on the
1465 // stream so we have to poll it :-(
1a278e7b
VZ
1466 DoGetFromStream(m_textIn, m_in);
1467 DoGetFromStream(m_textErr, m_err);
1468}
1469
1470void MyPipeFrame::DoGetFromStream(wxTextCtrl *text, wxInputStream& in)
1471{
1472 while ( in.CanRead() )
1473 {
1474 wxChar buffer[4096];
9a83f860 1475 buffer[in.Read(buffer, WXSIZEOF(buffer) - 1).LastRead()] = wxT('\0');
7ca528cb 1476
1a278e7b
VZ
1477 text->AppendText(buffer);
1478 }
1479}
1480
1481void MyPipeFrame::DoClose()
1482{
1483 m_process->CloseOutput();
1484
1485 DisableInput();
1486}
1487
1488void MyPipeFrame::DisableInput()
1489{
1490 m_textOut->SetEditable(false);
1491 FindWindow(Exec_Btn_Send)->Disable();
1492 FindWindow(Exec_Btn_SendFile)->Disable();
1493 FindWindow(Exec_Btn_Close)->Disable();
1494}
1495
1496void MyPipeFrame::DisableOutput()
1497{
1498 FindWindow(Exec_Btn_Get)->Disable();
eb671557
VZ
1499}
1500
1501void MyPipeFrame::OnClose(wxCloseEvent& event)
1502{
7ca528cb
VZ
1503 if ( m_process )
1504 {
1505 // we're not interested in getting the process termination notification
1506 // if we are closing it ourselves
1507 wxProcess *process = m_process;
1508 m_process = NULL;
1509 process->SetNextHandler(NULL);
1510
1511 process->CloseOutput();
1512 }
eb671557
VZ
1513
1514 event.Skip();
1515}
1516
87728739 1517void MyPipeFrame::OnProcessTerm(wxProcessEvent& WXUNUSED(event))
7ca528cb 1518{
1a278e7b
VZ
1519 DoGet();
1520
5276b0a5 1521 wxDELETE(m_process);
7ca528cb 1522
9a83f860 1523 wxLogWarning(wxT("The other process has terminated, closing"));
7ca528cb 1524
1a278e7b
VZ
1525 DisableInput();
1526 DisableOutput();
7ca528cb 1527}