]> git.saurik.com Git - wxWidgets.git/blame - samples/exec/exec.cpp
Applied colspan corrections, #15274 and #15275 (dghart)
[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
VZ
964
965 if ( code != -1 )
966 {
9a83f860
VZ
967 ShowOutput(cmd, output, wxT("Output"));
968 ShowOutput(cmd, errors, wxT("Errors"));
cd6ce4a9
VZ
969 }
970 }
971 else // async exec
972 {
973 MyPipedProcess *process = new MyPipedProcess(this, cmd);
4c1ef422 974 if ( !wxExecute(cmd, wxEXEC_ASYNC, process) )
cd6ce4a9 975 {
9a83f860 976 wxLogError(wxT("Execution of '%s' failed."), cmd.c_str());
cd6ce4a9
VZ
977
978 delete process;
979 }
980 else
981 {
5a8561fc 982 AddPipedProcess(process);
cd6ce4a9 983 }
d8e41d42 984 }
cd6ce4a9
VZ
985
986 m_cmdLast = cmd;
d8e41d42
VZ
987}
988
f6bcfd97
BP
989void MyFrame::OnExecWithPipe(wxCommandEvent& WXUNUSED(event))
990{
991 if ( !m_cmdLast )
9a83f860 992 m_cmdLast = wxT("tr [a-z] [A-Z]");
f6bcfd97 993
9a83f860 994 wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
f6bcfd97
BP
995 DIALOG_TITLE,
996 m_cmdLast);
997
998 if ( !cmd )
999 return;
1000
9a83f860 1001 wxString input = wxGetTextFromUser(wxT("Enter the string to send to it: "),
f6bcfd97
BP
1002 DIALOG_TITLE);
1003 if ( !input )
1004 return;
1005
1006 // always execute the filter asynchronously
1007 MyPipedProcess2 *process = new MyPipedProcess2(this, cmd, input);
aec18ff7 1008 long pid = wxExecute(cmd, wxEXEC_ASYNC, process);
f6bcfd97
BP
1009 if ( pid )
1010 {
9a83f860 1011 wxLogStatus(wxT("Process %ld (%s) launched."), pid, cmd.c_str());
f6bcfd97 1012
5a8561fc 1013 AddPipedProcess(process);
f6bcfd97
BP
1014 }
1015 else
1016 {
9a83f860 1017 wxLogError(wxT("Execution of '%s' failed."), cmd.c_str());
f6bcfd97
BP
1018
1019 delete process;
1020 }
1021
1022 m_cmdLast = cmd;
1023}
1024
87728739 1025void MyFrame::OnPOpen(wxCommandEvent& WXUNUSED(event))
eb671557 1026{
9a83f860 1027 wxString cmd = wxGetTextFromUser(wxT("Enter the command to launch: "),
eb671557
VZ
1028 DIALOG_TITLE,
1029 m_cmdLast);
1030 if ( cmd.empty() )
1031 return;
1032
1033 wxProcess *process = wxProcess::Open(cmd);
1034 if ( !process )
1035 {
9a83f860 1036 wxLogError(wxT("Failed to launch the command."));
eb671557
VZ
1037 return;
1038 }
1039
9a83f860 1040 wxLogVerbose(wxT("PID of the new process: %ld"), process->GetPid());
a387938f 1041
eb671557
VZ
1042 wxOutputStream *out = process->GetOutputStream();
1043 if ( !out )
1044 {
9a83f860 1045 wxLogError(wxT("Failed to connect to child stdin"));
eb671557
VZ
1046 return;
1047 }
1048
1049 wxInputStream *in = process->GetInputStream();
1050 if ( !in )
1051 {
9a83f860 1052 wxLogError(wxT("Failed to connect to child stdout"));
eb671557
VZ
1053 return;
1054 }
1055
1056 new MyPipeFrame(this, cmd, process);
1057}
1058
979a7347 1059static wxString gs_lastFile;
6ba63600 1060
979a7347
VZ
1061static bool AskUserForFileName()
1062{
71307412
WS
1063 wxString filename;
1064
1065#if wxUSE_FILEDLG
9a83f860 1066 filename = wxLoadFileSelector(wxT("any"), wxEmptyString, gs_lastFile);
ba59de5d 1067#else // !wxUSE_FILEDLG
9a83f860 1068 filename = wxGetTextFromUser(wxT("Enter the file name"), wxT("exec sample"),
979a7347 1069 gs_lastFile);
ba59de5d 1070#endif // wxUSE_FILEDLG/!wxUSE_FILEDLG
71307412
WS
1071
1072 if ( filename.empty() )
979a7347 1073 return false;
6ba63600 1074
979a7347 1075 gs_lastFile = filename;
6ba63600 1076
979a7347
VZ
1077 return true;
1078}
1079
1080void MyFrame::OnFileExec(wxCommandEvent& WXUNUSED(event))
1081{
1082 if ( !AskUserForFileName() )
1083 return;
1084
9a83f860 1085 wxString ext = gs_lastFile.AfterLast(wxT('.'));
6ba63600
VZ
1086 wxFileType *ft = wxTheMimeTypesManager->GetFileTypeFromExtension(ext);
1087 if ( !ft )
1088 {
9a83f860 1089 wxLogError(wxT("Impossible to determine the file type for extension '%s'"),
6ba63600
VZ
1090 ext.c_str());
1091 return;
1092 }
1093
1094 wxString cmd;
1095 bool ok = ft->GetOpenCommand(&cmd,
979a7347 1096 wxFileType::MessageParameters(gs_lastFile));
6ba63600
VZ
1097 delete ft;
1098 if ( !ok )
1099 {
9a83f860 1100 wxLogError(wxT("Impossible to find out how to open files of extension '%s'"),
6ba63600
VZ
1101 ext.c_str());
1102 return;
1103 }
1104
1105 DoAsyncExec(cmd);
1106}
1107
5edaa5eb
VZ
1108void MyFrame::OnShowCommandForExt(wxCommandEvent& WXUNUSED(event))
1109{
1110 static wxString s_ext;
1111
1112 wxString ext = wxGetTextFromUser
1113 (
1114 "Enter the extension without leading dot",
1115 "Exec sample",
1116 s_ext,
1117 this
1118 );
1119 if ( ext.empty() )
1120 return;
1121
1122 s_ext = ext;
1123
1124 wxScopedPtr<wxFileType>
1125 ft(wxTheMimeTypesManager->GetFileTypeFromExtension(ext));
1126 if ( !ft )
1127 {
1128 wxLogError("Information for extension \"%s\" not found", ext);
1129 return;
1130 }
1131
1132 const wxString cmd = ft->GetOpenCommand("file." + ext);
1133 if ( cmd.empty() )
1134 {
1135 wxLogWarning("Open command for extension \"%s\" not defined.", ext);
1136 return;
1137 }
1138
1139 wxLogMessage("Open command for files of extension \"%s\" is\n%s",
1140 ext, cmd);
1141}
1142
979a7347
VZ
1143void MyFrame::OnFileLaunch(wxCommandEvent& WXUNUSED(event))
1144{
1145 if ( !AskUserForFileName() )
1146 return;
1147
1148 if ( !wxLaunchDefaultApplication(gs_lastFile) )
1149 {
1150 wxLogError("Opening \"%s\" in default application failed.", gs_lastFile);
1151 }
1152}
1153
ba59de5d
VZ
1154void MyFrame::OnOpenURL(wxCommandEvent& WXUNUSED(event))
1155{
9a83f860 1156 static wxString s_url(wxT("http://www.wxwidgets.org/"));
ba59de5d
VZ
1157
1158 wxString filename = wxGetTextFromUser
1159 (
9a83f860
VZ
1160 wxT("Enter the URL"),
1161 wxT("exec sample"),
979a7347 1162 s_url,
ba59de5d
VZ
1163 this
1164 );
1165
1166 if ( filename.empty() )
1167 return;
1168
979a7347 1169 s_url = filename;
ba59de5d 1170
979a7347 1171 if ( !wxLaunchDefaultBrowser(s_url) )
43b2d5e7 1172 {
9a83f860 1173 wxLogError(wxT("Failed to open URL \"%s\""), s_url.c_str());
43b2d5e7 1174 }
ba59de5d
VZ
1175}
1176
50567b69
VZ
1177// ----------------------------------------------------------------------------
1178// DDE stuff
1179// ----------------------------------------------------------------------------
1180
d93c719a 1181#ifdef __WINDOWS__
ca289436
VZ
1182
1183bool MyFrame::GetDDEServer()
1184{
9a83f860 1185 wxString server = wxGetTextFromUser(wxT("Server to connect to:"),
ca289436 1186 DIALOG_TITLE, m_server);
d93c719a 1187 if ( !server )
07850a49 1188 return false;
ca289436
VZ
1189
1190 m_server = server;
d93c719a 1191
9a83f860 1192 wxString topic = wxGetTextFromUser(wxT("DDE topic:"), DIALOG_TITLE, m_topic);
d93c719a 1193 if ( !topic )
07850a49 1194 return false;
d93c719a 1195
ca289436
VZ
1196 m_topic = topic;
1197
9a83f860 1198 wxString cmd = wxGetTextFromUser(wxT("DDE command:"), DIALOG_TITLE, m_cmdDde);
d93c719a 1199 if ( !cmd )
07850a49 1200 return false;
ca289436
VZ
1201
1202 m_cmdDde = cmd;
1203
07850a49 1204 return true;
ca289436
VZ
1205}
1206
1207void MyFrame::OnDDEExec(wxCommandEvent& WXUNUSED(event))
1208{
1209 if ( !GetDDEServer() )
d93c719a
VZ
1210 return;
1211
1212 wxDDEClient client;
71307412 1213 wxConnectionBase *conn = client.MakeConnection(wxEmptyString, m_server, m_topic);
d93c719a
VZ
1214 if ( !conn )
1215 {
9a83f860 1216 wxLogError(wxT("Failed to connect to the DDE server '%s'."),
ca289436 1217 m_server.c_str());
d93c719a
VZ
1218 }
1219 else
1220 {
ca289436 1221 if ( !conn->Execute(m_cmdDde) )
d93c719a 1222 {
9a83f860 1223 wxLogError(wxT("Failed to execute command '%s' via DDE."),
ca289436 1224 m_cmdDde.c_str());
d93c719a
VZ
1225 }
1226 else
1227 {
9a83f860 1228 wxLogStatus(wxT("Successfully executed DDE command"));
d93c719a
VZ
1229 }
1230 }
d93c719a
VZ
1231}
1232
ca289436
VZ
1233void MyFrame::OnDDERequest(wxCommandEvent& WXUNUSED(event))
1234{
1235 if ( !GetDDEServer() )
1236 return;
1237
1238 wxDDEClient client;
71307412 1239 wxConnectionBase *conn = client.MakeConnection(wxEmptyString, m_server, m_topic);
ca289436
VZ
1240 if ( !conn )
1241 {
9a83f860 1242 wxLogError(wxT("Failed to connect to the DDE server '%s'."),
ca289436
VZ
1243 m_server.c_str());
1244 }
1245 else
1246 {
1247 if ( !conn->Request(m_cmdDde) )
1248 {
9a83f860 1249 wxLogError(wxT("Failed to send request '%s' via DDE."),
ca289436
VZ
1250 m_cmdDde.c_str());
1251 }
1252 else
1253 {
9a83f860 1254 wxLogStatus(wxT("Successfully sent DDE request."));
ca289436
VZ
1255 }
1256 }
1257}
1258
1259#endif // __WINDOWS__
1260
50567b69
VZ
1261// ----------------------------------------------------------------------------
1262// various helpers
1263// ----------------------------------------------------------------------------
1264
cd6ce4a9
VZ
1265// input polling
1266void MyFrame::OnIdle(wxIdleEvent& event)
1267{
1268 size_t count = m_running.GetCount();
1269 for ( size_t n = 0; n < count; n++ )
1270 {
1271 if ( m_running[n]->HasInput() )
1272 {
1273 event.RequestMore();
1274 }
1275 }
1276}
1277
9cd203f7 1278void MyFrame::OnIdleTimer(wxTimerEvent& WXUNUSED(event))
92a2a7eb
VZ
1279{
1280 wxWakeUpIdle();
1281}
1282
9cd203f7
VZ
1283void MyFrame::OnBgTimer(wxTimerEvent& WXUNUSED(event))
1284{
1285 static unsigned long s_ticks = 0;
1286 SetStatusText(wxString::Format("%lu ticks", s_ticks++), 1);
1287}
1288
f6bcfd97
BP
1289void MyFrame::OnProcessTerminated(MyPipedProcess *process)
1290{
5a8561fc
VZ
1291 RemovePipedProcess(process);
1292}
1293
1294void MyFrame::OnAsyncTermination(MyProcess *process)
1295{
1296 m_allAsync.Remove(process);
1297
1298 delete process;
1299}
1300
1301void MyFrame::AddPipedProcess(MyPipedProcess *process)
1302{
1303 if ( m_running.IsEmpty() )
1304 {
1305 // we want to start getting the timer events to ensure that a
1306 // steady stream of idle events comes in -- otherwise we
1307 // wouldn't be able to poll the child process input
1308 m_timerIdleWakeUp.Start(100);
1309 }
1310 //else: the timer is already running
1311
1312 m_running.Add(process);
1313 m_allAsync.Add(process);
f6bcfd97
BP
1314}
1315
5a8561fc
VZ
1316void MyFrame::RemovePipedProcess(MyPipedProcess *process)
1317{
1318 m_running.Remove(process);
1319
1320 if ( m_running.IsEmpty() )
1321 {
1322 // we don't need to get idle events all the time any more
1323 m_timerIdleWakeUp.Stop();
1324 }
1325}
f6bcfd97
BP
1326
1327void MyFrame::ShowOutput(const wxString& cmd,
1328 const wxArrayString& output,
1329 const wxString& title)
1330{
1331 size_t count = output.GetCount();
1332 if ( !count )
1333 return;
1334
9a83f860 1335 m_lbox->Append(wxString::Format(wxT("--- %s of '%s' ---"),
f6bcfd97
BP
1336 title.c_str(), cmd.c_str()));
1337
1338 for ( size_t n = 0; n < count; n++ )
1339 {
1340 m_lbox->Append(output[n]);
1341 }
1342
9a83f860 1343 m_lbox->Append(wxString::Format(wxT("--- End of %s ---"),
eb671557 1344 title.Lower().c_str()));
f6bcfd97
BP
1345}
1346
69c33c6c
VZ
1347// ----------------------------------------------------------------------------
1348// MyProcess
1349// ----------------------------------------------------------------------------
1350
1351void MyProcess::OnTerminate(int pid, int status)
1352{
9a83f860 1353 wxLogStatus(m_parent, wxT("Process %u ('%s') terminated with exit code %d."),
69c33c6c
VZ
1354 pid, m_cmd.c_str(), status);
1355
5a8561fc 1356 m_parent->OnAsyncTermination(this);
69c33c6c 1357}
d8e41d42 1358
cd6ce4a9
VZ
1359// ----------------------------------------------------------------------------
1360// MyPipedProcess
1361// ----------------------------------------------------------------------------
1362
1363bool MyPipedProcess::HasInput()
d8e41d42 1364{
07850a49 1365 bool hasInput = false;
f6bcfd97 1366
92a2a7eb 1367 if ( IsInputAvailable() )
cd6ce4a9 1368 {
92a2a7eb 1369 wxTextInputStream tis(*GetInputStream());
cd6ce4a9
VZ
1370
1371 // this assumes that the output is always line buffered
1372 wxString msg;
9a83f860 1373 msg << m_cmd << wxT(" (stdout): ") << tis.ReadLine();
d8e41d42 1374
cd6ce4a9
VZ
1375 m_parent->GetLogListBox()->Append(msg);
1376
07850a49 1377 hasInput = true;
cd6ce4a9 1378 }
f6bcfd97 1379
92a2a7eb 1380 if ( IsErrorAvailable() )
d8e41d42 1381 {
92a2a7eb 1382 wxTextInputStream tis(*GetErrorStream());
f6bcfd97
BP
1383
1384 // this assumes that the output is always line buffered
1385 wxString msg;
9a83f860 1386 msg << m_cmd << wxT(" (stderr): ") << tis.ReadLine();
f6bcfd97
BP
1387
1388 m_parent->GetLogListBox()->Append(msg);
1389
07850a49 1390 hasInput = true;
d8e41d42 1391 }
f6bcfd97
BP
1392
1393 return hasInput;
cd6ce4a9
VZ
1394}
1395
1396void MyPipedProcess::OnTerminate(int pid, int status)
1397{
1398 // show the rest of the output
1399 while ( HasInput() )
1400 ;
1401
1402 m_parent->OnProcessTerminated(this);
d8e41d42
VZ
1403
1404 MyProcess::OnTerminate(pid, status);
1405}
f6bcfd97
BP
1406
1407// ----------------------------------------------------------------------------
1408// MyPipedProcess2
1409// ----------------------------------------------------------------------------
1410
1411bool MyPipedProcess2::HasInput()
1412{
11fdee42 1413 if ( !m_input.empty() )
f6bcfd97
BP
1414 {
1415 wxTextOutputStream os(*GetOutputStream());
1416 os.WriteString(m_input);
1417
1418 CloseOutput();
1419 m_input.clear();
1420
1421 // call us once again - may be we'll have output
07850a49 1422 return true;
f6bcfd97
BP
1423 }
1424
1425 return MyPipedProcess::HasInput();
1426}
eb671557
VZ
1427
1428// ============================================================================
1429// MyPipeFrame implementation
1430// ============================================================================
1431
1432MyPipeFrame::MyPipeFrame(wxFrame *parent,
1433 const wxString& cmd,
1434 wxProcess *process)
07850a49 1435 : wxFrame(parent, wxID_ANY, cmd),
eb671557
VZ
1436 m_process(process),
1437 // in a real program we'd check that the streams are !NULL here
f6def1fa 1438 m_out(*process->GetOutputStream()),
eb671557 1439 m_in(*process->GetInputStream()),
f6def1fa 1440 m_err(*process->GetErrorStream())
eb671557 1441{
7ca528cb
VZ
1442 m_process->SetNextHandler(this);
1443
07850a49 1444 wxPanel *panel = new wxPanel(this, wxID_ANY);
7ca528cb 1445
71307412 1446 m_textOut = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
eb671557
VZ
1447 wxDefaultPosition, wxDefaultSize,
1448 wxTE_PROCESS_ENTER);
71307412 1449 m_textIn = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
1a278e7b
VZ
1450 wxDefaultPosition, wxDefaultSize,
1451 wxTE_MULTILINE | wxTE_RICH);
1452 m_textIn->SetEditable(false);
71307412 1453 m_textErr = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
1a278e7b
VZ
1454 wxDefaultPosition, wxDefaultSize,
1455 wxTE_MULTILINE | wxTE_RICH);
1456 m_textErr->SetEditable(false);
eb671557
VZ
1457
1458 wxSizer *sizerTop = new wxBoxSizer(wxVERTICAL);
1a278e7b 1459 sizerTop->Add(m_textOut, 0, wxGROW | wxALL, 5);
eb671557
VZ
1460
1461 wxSizer *sizerBtns = new wxBoxSizer(wxHORIZONTAL);
1a278e7b 1462 sizerBtns->
9a83f860 1463 Add(new wxButton(panel, Exec_Btn_Send, wxT("&Send")), 0, wxALL, 5);
1a278e7b 1464 sizerBtns->
9a83f860 1465 Add(new wxButton(panel, Exec_Btn_SendFile, wxT("&File...")), 0, wxALL, 5);
1a278e7b 1466 sizerBtns->
9a83f860 1467 Add(new wxButton(panel, Exec_Btn_Get, wxT("&Get")), 0, wxALL, 5);
1a278e7b 1468 sizerBtns->
9a83f860 1469 Add(new wxButton(panel, Exec_Btn_Close, wxT("&Close")), 0, wxALL, 5);
eb671557
VZ
1470
1471 sizerTop->Add(sizerBtns, 0, wxCENTRE | wxALL, 5);
1a278e7b
VZ
1472 sizerTop->Add(m_textIn, 1, wxGROW | wxALL, 5);
1473 sizerTop->Add(m_textErr, 1, wxGROW | wxALL, 5);
eb671557 1474
7ca528cb 1475 panel->SetSizer(sizerTop);
eb671557
VZ
1476 sizerTop->Fit(this);
1477
1478 Show();
1479}
1480
1a278e7b
VZ
1481void MyPipeFrame::OnBtnSendFile(wxCommandEvent& WXUNUSED(event))
1482{
71307412 1483#if wxUSE_FILEDLG
9a83f860 1484 wxFileDialog filedlg(this, wxT("Select file to send"));
1a278e7b
VZ
1485 if ( filedlg.ShowModal() != wxID_OK )
1486 return;
1487
9a83f860 1488 wxFFile file(filedlg.GetFilename(), wxT("r"));
1a278e7b
VZ
1489 wxString data;
1490 if ( !file.IsOpened() || !file.ReadAll(&data) )
1491 return;
1492
1493 // can't write the entire string at once, this risk overflowing the pipe
1494 // and we would dead lock
1495 size_t len = data.length();
1496 const wxChar *pc = data.c_str();
1497 while ( len )
1498 {
1499 const size_t CHUNK_SIZE = 4096;
42d0aa30 1500 m_out.Write(pc, len > CHUNK_SIZE ? CHUNK_SIZE : len);
1a278e7b 1501
42d0aa30
VZ
1502 // note that not all data could have been written as we don't block on
1503 // the write end of the pipe
1504 const size_t lenChunk = m_out.LastWrite();
1a278e7b
VZ
1505
1506 pc += lenChunk;
1507 len -= lenChunk;
1508
1509 DoGet();
1510 }
71307412 1511#endif // wxUSE_FILEDLG
1a278e7b
VZ
1512}
1513
eb671557
VZ
1514void MyPipeFrame::DoGet()
1515{
7ca528cb
VZ
1516 // we don't have any way to be notified when any input appears on the
1517 // stream so we have to poll it :-(
1a278e7b
VZ
1518 DoGetFromStream(m_textIn, m_in);
1519 DoGetFromStream(m_textErr, m_err);
1520}
1521
1522void MyPipeFrame::DoGetFromStream(wxTextCtrl *text, wxInputStream& in)
1523{
1524 while ( in.CanRead() )
1525 {
630d8c19
VZ
1526 char buffer[4096];
1527 buffer[in.Read(buffer, WXSIZEOF(buffer) - 1).LastRead()] = '\0';
7ca528cb 1528
1a278e7b
VZ
1529 text->AppendText(buffer);
1530 }
1531}
1532
1533void MyPipeFrame::DoClose()
1534{
1535 m_process->CloseOutput();
1536
1537 DisableInput();
1538}
1539
1540void MyPipeFrame::DisableInput()
1541{
1542 m_textOut->SetEditable(false);
1543 FindWindow(Exec_Btn_Send)->Disable();
1544 FindWindow(Exec_Btn_SendFile)->Disable();
1545 FindWindow(Exec_Btn_Close)->Disable();
1546}
1547
1548void MyPipeFrame::DisableOutput()
1549{
1550 FindWindow(Exec_Btn_Get)->Disable();
eb671557
VZ
1551}
1552
1553void MyPipeFrame::OnClose(wxCloseEvent& event)
1554{
7ca528cb
VZ
1555 if ( m_process )
1556 {
1557 // we're not interested in getting the process termination notification
1558 // if we are closing it ourselves
1559 wxProcess *process = m_process;
1560 m_process = NULL;
1561 process->SetNextHandler(NULL);
1562
1563 process->CloseOutput();
1564 }
eb671557
VZ
1565
1566 event.Skip();
1567}
1568
87728739 1569void MyPipeFrame::OnProcessTerm(wxProcessEvent& WXUNUSED(event))
7ca528cb 1570{
1a278e7b
VZ
1571 DoGet();
1572
5276b0a5 1573 wxDELETE(m_process);
7ca528cb 1574
9a83f860 1575 wxLogWarning(wxT("The other process has terminated, closing"));
7ca528cb 1576
1a278e7b
VZ
1577 DisableInput();
1578 DisableOutput();
7ca528cb 1579}