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