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