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