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