changed lbox font to be more readable
[wxWidgets.git] / samples / exec / exec.cpp
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
20 #ifdef __GNUG__
21 #pragma implementation "exec.cpp"
22 #pragma interface "exec.cpp"
23 #endif
24
25 // For compilers that support precompilation, includes "wx/wx.h".
26 #include "wx/wxprec.h"
27
28 #ifdef __BORLANDC__
29 #pragma hdrstop
30 #endif
31
32 // for all others, include the necessary headers (this file is usually all you
33 // need because it includes almost all "standard" wxWindows headers
34 #ifndef WX_PRECOMP
35 #include "wx/app.h"
36 #include "wx/frame.h"
37 #include "wx/utils.h"
38 #include "wx/menu.h"
39 #include "wx/msgdlg.h"
40 #include "wx/textdlg.h"
41 #include "wx/listbox.h"
42 #include "wx/filedlg.h"
43 #endif
44
45 #include "wx/txtstrm.h"
46
47 #include "wx/process.h"
48
49 #include "wx/mimetype.h"
50
51 #ifdef __WINDOWS__
52 #include "wx/dde.h"
53 #endif // __WINDOWS__
54
55 // ----------------------------------------------------------------------------
56 // private classes
57 // ----------------------------------------------------------------------------
58
59 // Define a new application type, each program should derive a class from wxApp
60 class MyApp : public wxApp
61 {
62 public:
63 // override base class virtuals
64 // ----------------------------
65
66 // this one is called on application startup and is a good place for the app
67 // initialization (doing it here and not in the ctor allows to have an error
68 // return: if OnInit() returns false, the application terminates)
69 virtual bool OnInit();
70 };
71
72 // Define an array of process pointers used by MyFrame
73 class MyPipedProcess;
74 WX_DEFINE_ARRAY(MyPipedProcess *, MyProcessesArray);
75
76 // Define a new frame type: this is going to be our main frame
77 class MyFrame : public wxFrame
78 {
79 public:
80 // ctor(s)
81 MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size);
82
83 // event handlers (these functions should _not_ be virtual)
84 void OnQuit(wxCommandEvent& event);
85
86 void OnClear(wxCommandEvent& event);
87
88 void OnSyncExec(wxCommandEvent& event);
89 void OnAsyncExec(wxCommandEvent& event);
90 void OnShell(wxCommandEvent& event);
91 void OnExecWithRedirect(wxCommandEvent& event);
92 void OnExecWithPipe(wxCommandEvent& event);
93
94 void OnFileExec(wxCommandEvent& event);
95
96 void OnDDEExec(wxCommandEvent& event);
97
98 void OnAbout(wxCommandEvent& event);
99
100 // polling output of async processes
101 void OnIdle(wxIdleEvent& event);
102
103 // for MyPipedProcess
104 void OnProcessTerminated(MyPipedProcess *process);
105 wxListBox *GetLogListBox() const { return m_lbox; }
106
107 private:
108 void ShowOutput(const wxString& cmd,
109 const wxArrayString& output,
110 const wxString& title);
111
112 void DoAsyncExec(const wxString& cmd);
113
114 wxString m_cmdLast;
115
116 wxListBox *m_lbox;
117
118 MyProcessesArray m_running;
119
120 // any class wishing to process wxWindows events must use this macro
121 DECLARE_EVENT_TABLE()
122 };
123
124 // This is the handler for process termination events
125 class MyProcess : public wxProcess
126 {
127 public:
128 MyProcess(MyFrame *parent, const wxString& cmd)
129 : wxProcess(parent), m_cmd(cmd)
130 {
131 m_parent = parent;
132 }
133
134 // instead of overriding this virtual function we might as well process the
135 // event from it in the frame class - this might be more convenient in some
136 // cases
137 virtual void OnTerminate(int pid, int status);
138
139 protected:
140 MyFrame *m_parent;
141 wxString m_cmd;
142 };
143
144 // A specialization of MyProcess for redirecting the output
145 class MyPipedProcess : public MyProcess
146 {
147 public:
148 MyPipedProcess(MyFrame *parent, const wxString& cmd)
149 : MyProcess(parent, cmd)
150 {
151 Redirect();
152 }
153
154 virtual void OnTerminate(int pid, int status);
155
156 virtual bool HasInput();
157 };
158
159 // A version of MyPipedProcess which also sends input to the stdin of the
160 // child process
161 class MyPipedProcess2 : public MyPipedProcess
162 {
163 public:
164 MyPipedProcess2(MyFrame *parent, const wxString& cmd, const wxString& input)
165 : MyPipedProcess(parent, cmd), m_input(input)
166 {
167 }
168
169 virtual bool HasInput();
170
171 private:
172 wxString m_input;
173 };
174
175 // ----------------------------------------------------------------------------
176 // constants
177 // ----------------------------------------------------------------------------
178
179 // IDs for the controls and the menu commands
180 enum
181 {
182 // menu items
183 Exec_Quit = 100,
184 Exec_ClearLog,
185 Exec_SyncExec = 200,
186 Exec_AsyncExec,
187 Exec_Shell,
188 Exec_OpenFile,
189 Exec_DDEExec,
190 Exec_Redirect,
191 Exec_Pipe,
192 Exec_About = 300
193 };
194
195 static const wxChar *DIALOG_TITLE = _T("Exec sample");
196
197 // ----------------------------------------------------------------------------
198 // event tables and other macros for wxWindows
199 // ----------------------------------------------------------------------------
200
201 // the event tables connect the wxWindows events with the functions (event
202 // handlers) which process them. It can be also done at run-time, but for the
203 // simple menu events like this the static method is much simpler.
204 BEGIN_EVENT_TABLE(MyFrame, wxFrame)
205 EVT_MENU(Exec_Quit, MyFrame::OnQuit)
206 EVT_MENU(Exec_ClearLog, MyFrame::OnClear)
207
208 EVT_MENU(Exec_SyncExec, MyFrame::OnSyncExec)
209 EVT_MENU(Exec_AsyncExec, MyFrame::OnAsyncExec)
210 EVT_MENU(Exec_Shell, MyFrame::OnShell)
211 EVT_MENU(Exec_Redirect, MyFrame::OnExecWithRedirect)
212 EVT_MENU(Exec_Pipe, MyFrame::OnExecWithPipe)
213
214 EVT_MENU(Exec_OpenFile, MyFrame::OnFileExec)
215
216 EVT_MENU(Exec_DDEExec, MyFrame::OnDDEExec)
217
218 EVT_MENU(Exec_About, MyFrame::OnAbout)
219
220 EVT_IDLE(MyFrame::OnIdle)
221 END_EVENT_TABLE()
222
223 // Create a new application object: this macro will allow wxWindows to create
224 // the application object during program execution (it's better than using a
225 // static object for many reasons) and also declares the accessor function
226 // wxGetApp() which will return the reference of the right type (i.e. MyApp and
227 // not wxApp)
228 IMPLEMENT_APP(MyApp)
229
230 // ============================================================================
231 // implementation
232 // ============================================================================
233
234 // ----------------------------------------------------------------------------
235 // the application class
236 // ----------------------------------------------------------------------------
237
238 // `Main program' equivalent: the program execution "starts" here
239 bool MyApp::OnInit()
240 {
241 // Create the main application window
242 MyFrame *frame = new MyFrame(_T("Exec wxWindows sample"),
243 wxDefaultPosition, wxSize(500, 140));
244
245 // Show it and tell the application that it's our main window
246 frame->Show(TRUE);
247 SetTopWindow(frame);
248
249 // success: wxApp::OnRun() will be called which will enter the main message
250 // loop and the application will run. If we returned FALSE here, the
251 // application would exit immediately.
252 return TRUE;
253 }
254
255 // ----------------------------------------------------------------------------
256 // main frame
257 // ----------------------------------------------------------------------------
258
259 // frame constructor
260 MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
261 : wxFrame((wxFrame *)NULL, -1, title, pos, size)
262 {
263 #ifdef __WXMAC__
264 // we need this in order to allow the about menu relocation, since ABOUT is
265 // not the default id of the about menu
266 wxApp::s_macAboutMenuItemId = Exec_About;
267 #endif
268
269 // create a menu bar
270 wxMenu *menuFile = new wxMenu(_T(""), wxMENU_TEAROFF);
271 menuFile->Append(Exec_ClearLog, _T("&Clear log\tCtrl-C"),
272 _T("Clear the log window"));
273 menuFile->AppendSeparator();
274 menuFile->Append(Exec_Quit, _T("E&xit\tAlt-X"), _T("Quit this program"));
275
276 wxMenu *execMenu = new wxMenu;
277 execMenu->Append(Exec_SyncExec, _T("Sync &execution...\tCtrl-E"),
278 _T("Launch a program and return when it terminates"));
279 execMenu->Append(Exec_AsyncExec, _T("&Async execution...\tCtrl-A"),
280 _T("Launch a program and return immediately"));
281 execMenu->Append(Exec_Shell, _T("Execute &shell command...\tCtrl-S"),
282 _T("Launch a shell and execute a command in it"));
283 execMenu->AppendSeparator();
284 execMenu->Append(Exec_Redirect, _T("Capture command &output...\tCtrl-O"),
285 _T("Launch a program and capture its output"));
286 execMenu->Append(Exec_Pipe, _T("&Pipe through command...\tCtrl-P"),
287 _T("Pipe a string through a filter"));
288
289 execMenu->AppendSeparator();
290 execMenu->Append(Exec_OpenFile, _T("Open &file...\tCtrl-F"),
291 _T("Launch the command to open this kind of files"));
292 #ifdef __WINDOWS__
293 execMenu->AppendSeparator();
294 execMenu->Append(Exec_DDEExec, _T("Execute command via &DDE...\tCtrl-D"));
295 #endif
296
297 wxMenu *helpMenu = new wxMenu(_T(""), wxMENU_TEAROFF);
298 helpMenu->Append(Exec_About, _T("&About...\tF1"), _T("Show about dialog"));
299
300 // now append the freshly created menu to the menu bar...
301 wxMenuBar *menuBar = new wxMenuBar();
302 menuBar->Append(menuFile, _T("&File"));
303 menuBar->Append(execMenu, _T("&Exec"));
304 menuBar->Append(helpMenu, _T("&Help"));
305
306 // ... and attach this menu bar to the frame
307 SetMenuBar(menuBar);
308
309 // create the listbox in which we will show misc messages as they come
310 m_lbox = new wxListBox(this, -1);
311 wxFont font(12, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL,
312 wxFONTWEIGHT_NORMAL);
313 if ( font.Ok() )
314 m_lbox->SetFont(font);
315
316 #if wxUSE_STATUSBAR
317 // create a status bar just for fun (by default with 1 pane only)
318 CreateStatusBar();
319 SetStatusText(_T("Welcome to wxWindows exec sample!"));
320 #endif // wxUSE_STATUSBAR
321 }
322
323
324 // event handlers
325
326 void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
327 {
328 // TRUE is to force the frame to close
329 Close(TRUE);
330 }
331
332 void MyFrame::OnClear(wxCommandEvent& WXUNUSED(event))
333 {
334 m_lbox->Clear();
335 }
336
337 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
338 {
339 wxMessageBox(_T("Exec sample\n© 2000 Vadim Zeitlin"),
340 _T("About Exec"), wxOK | wxICON_INFORMATION, this);
341 }
342
343 void MyFrame::DoAsyncExec(const wxString& cmd)
344 {
345 wxProcess *process = new MyProcess(this, cmd);
346 long pid = wxExecute(cmd, FALSE /* async */, process);
347 if ( !pid )
348 {
349 wxLogError(_T("Execution of '%s' failed."), cmd.c_str());
350
351 delete process;
352 }
353 else
354 {
355 wxLogStatus(_T("Process %ld (%s) launched."), pid, cmd.c_str());
356
357 m_cmdLast = cmd;
358 }
359 }
360
361 void MyFrame::OnSyncExec(wxCommandEvent& WXUNUSED(event))
362 {
363 wxString cmd = wxGetTextFromUser(_T("Enter the command: "),
364 DIALOG_TITLE,
365 m_cmdLast);
366
367 if ( !cmd )
368 return;
369
370 wxLogStatus(_T("'%s' is running please wait..."), cmd.c_str());
371
372 int code = wxExecute(cmd, TRUE /* sync */);
373
374 wxLogStatus(_T("Process '%s' terminated with exit code %d."),
375 cmd.c_str(), code);
376 m_cmdLast = cmd;
377 }
378
379 void MyFrame::OnAsyncExec(wxCommandEvent& WXUNUSED(event))
380 {
381 wxString cmd = wxGetTextFromUser(_T("Enter the command: "),
382 DIALOG_TITLE,
383 m_cmdLast);
384
385 if ( !cmd )
386 return;
387
388 DoAsyncExec(cmd);
389 }
390
391 void MyFrame::OnShell(wxCommandEvent& WXUNUSED(event))
392 {
393 wxString cmd = wxGetTextFromUser(_T("Enter the command: "),
394 DIALOG_TITLE,
395 m_cmdLast);
396
397 if ( !cmd )
398 return;
399
400 int code = wxShell(cmd);
401 wxLogStatus(_T("Shell command '%s' terminated with exit code %d."),
402 cmd.c_str(), code);
403 m_cmdLast = cmd;
404 }
405
406 void MyFrame::OnExecWithRedirect(wxCommandEvent& WXUNUSED(event))
407 {
408 wxString cmd = wxGetTextFromUser(_T("Enter the command: "),
409 DIALOG_TITLE,
410 m_cmdLast);
411
412 if ( !cmd )
413 return;
414
415 bool sync;
416 switch ( wxMessageBox(_T("Execute it synchronously?"),
417 _T("Exec question"),
418 wxYES_NO | wxCANCEL | wxICON_QUESTION, this) )
419 {
420 case wxYES:
421 sync = TRUE;
422 break;
423
424 case wxNO:
425 sync = FALSE;
426 break;
427
428 default:
429 return;
430 }
431
432 if ( sync )
433 {
434 wxArrayString output, errors;
435 int code = wxExecute(cmd, output, errors);
436 wxLogStatus(_T("command '%s' terminated with exit code %d."),
437 cmd.c_str(), code);
438
439 if ( code != -1 )
440 {
441 ShowOutput(cmd, output, _T("Output"));
442 ShowOutput(cmd, errors, _T("Errors"));
443 }
444 }
445 else // async exec
446 {
447 MyPipedProcess *process = new MyPipedProcess(this, cmd);
448 if ( !wxExecute(cmd, FALSE /* async */, process) )
449 {
450 wxLogError(_T("Execution of '%s' failed."), cmd.c_str());
451
452 delete process;
453 }
454 else
455 {
456 m_running.Add(process);
457 }
458 }
459
460 m_cmdLast = cmd;
461 }
462
463 void MyFrame::OnExecWithPipe(wxCommandEvent& WXUNUSED(event))
464 {
465 if ( !m_cmdLast )
466 m_cmdLast = _T("tr [a-z] [A-Z]");
467
468 wxString cmd = wxGetTextFromUser(_T("Enter the command: "),
469 DIALOG_TITLE,
470 m_cmdLast);
471
472 if ( !cmd )
473 return;
474
475 wxString input = wxGetTextFromUser(_T("Enter the string to send to it: "),
476 DIALOG_TITLE);
477 if ( !input )
478 return;
479
480 // always execute the filter asynchronously
481 MyPipedProcess2 *process = new MyPipedProcess2(this, cmd, input);
482 int pid = wxExecute(cmd, FALSE /* async */, process);
483 if ( pid )
484 {
485 wxLogStatus(_T("Process %ld (%s) launched."), pid, cmd.c_str());
486
487 m_running.Add(process);
488 }
489 else
490 {
491 wxLogError(_T("Execution of '%s' failed."), cmd.c_str());
492
493 delete process;
494 }
495
496 m_cmdLast = cmd;
497 }
498
499 void MyFrame::OnFileExec(wxCommandEvent& event)
500 {
501 static wxString s_filename;
502
503 wxString filename = wxLoadFileSelector(_T("file"), _T(""), s_filename);
504 if ( !filename )
505 return;
506
507 s_filename = filename;
508
509 wxString ext = filename.AfterFirst(_T('.'));
510 wxFileType *ft = wxTheMimeTypesManager->GetFileTypeFromExtension(ext);
511 if ( !ft )
512 {
513 wxLogError(_T("Impossible to determine the file type for extension '%s'"),
514 ext.c_str());
515 return;
516 }
517
518 wxString cmd;
519 bool ok = ft->GetOpenCommand(&cmd,
520 wxFileType::MessageParameters(filename, _T("")));
521 delete ft;
522 if ( !ok )
523 {
524 wxLogError(_T("Impossible to find out how to open files of extension '%s'"),
525 ext.c_str());
526 return;
527 }
528
529 DoAsyncExec(cmd);
530 }
531
532 void MyFrame::OnDDEExec(wxCommandEvent& WXUNUSED(event))
533 {
534 #ifdef __WINDOWS__
535 wxString server = wxGetTextFromUser(_T("Server to connect to:"),
536 DIALOG_TITLE, _T("IExplore"));
537 if ( !server )
538 return;
539
540 wxString topic = wxGetTextFromUser(_T("DDE topic:"),
541 DIALOG_TITLE, _T("WWW_OpenURL"));
542 if ( !topic )
543 return;
544
545 wxString cmd = wxGetTextFromUser(_T("DDE command:"),
546 DIALOG_TITLE,
547 _T("\"file:F:\\wxWindows\\samples\\"
548 "image\\horse.gif\",,-1,,,,,"));
549 if ( !cmd )
550 return;
551
552 wxDDEClient client;
553 wxConnectionBase *conn = client.MakeConnection("", server, topic);
554 if ( !conn )
555 {
556 wxLogError(_T("Failed to connect to the DDE server '%s'."),
557 server.c_str());
558 }
559 else
560 {
561 if ( !conn->Execute(cmd) )
562 {
563 wxLogError(_T("Failed to execute command '%s' via DDE."),
564 cmd.c_str());
565 }
566 else
567 {
568 wxLogStatus(_T("Successfully executed DDE command"));
569 }
570 }
571 #endif // __WINDOWS__
572 }
573
574 // input polling
575 void MyFrame::OnIdle(wxIdleEvent& event)
576 {
577 size_t count = m_running.GetCount();
578 for ( size_t n = 0; n < count; n++ )
579 {
580 if ( m_running[n]->HasInput() )
581 {
582 event.RequestMore();
583 }
584 }
585 }
586
587 void MyFrame::OnProcessTerminated(MyPipedProcess *process)
588 {
589 m_running.Remove(process);
590 }
591
592
593 void MyFrame::ShowOutput(const wxString& cmd,
594 const wxArrayString& output,
595 const wxString& title)
596 {
597 size_t count = output.GetCount();
598 if ( !count )
599 return;
600
601 m_lbox->Append(wxString::Format(_T("--- %s of '%s' ---"),
602 title.c_str(), cmd.c_str()));
603
604 for ( size_t n = 0; n < count; n++ )
605 {
606 m_lbox->Append(output[n]);
607 }
608
609 m_lbox->Append(_T("--- End of output ---"));
610 }
611
612 // ----------------------------------------------------------------------------
613 // MyProcess
614 // ----------------------------------------------------------------------------
615
616 void MyProcess::OnTerminate(int pid, int status)
617 {
618 wxLogStatus(m_parent, _T("Process %u ('%s') terminated with exit code %d."),
619 pid, m_cmd.c_str(), status);
620
621 // we're not needed any more
622 delete this;
623 }
624
625 // ----------------------------------------------------------------------------
626 // MyPipedProcess
627 // ----------------------------------------------------------------------------
628
629 bool MyPipedProcess::HasInput()
630 {
631 bool hasInput = FALSE;
632
633 wxInputStream& is = *GetInputStream();
634 if ( !is.Eof() )
635 {
636 wxTextInputStream tis(is);
637
638 // this assumes that the output is always line buffered
639 wxString msg;
640 msg << m_cmd << _T(" (stdout): ") << tis.ReadLine();
641
642 m_parent->GetLogListBox()->Append(msg);
643
644 hasInput = TRUE;
645 }
646
647 wxInputStream& es = *GetErrorStream();
648 if ( !es.Eof() )
649 {
650 wxTextInputStream tis(es);
651
652 // this assumes that the output is always line buffered
653 wxString msg;
654 msg << m_cmd << _T(" (stderr): ") << tis.ReadLine();
655
656 m_parent->GetLogListBox()->Append(msg);
657
658 hasInput = TRUE;
659 }
660
661 return hasInput;
662 }
663
664 void MyPipedProcess::OnTerminate(int pid, int status)
665 {
666 // show the rest of the output
667 while ( HasInput() )
668 ;
669
670 m_parent->OnProcessTerminated(this);
671
672 MyProcess::OnTerminate(pid, status);
673 }
674
675 // ----------------------------------------------------------------------------
676 // MyPipedProcess2
677 // ----------------------------------------------------------------------------
678
679 bool MyPipedProcess2::HasInput()
680 {
681 if ( !!m_input )
682 {
683 wxTextOutputStream os(*GetOutputStream());
684 os.WriteString(m_input);
685
686 CloseOutput();
687 m_input.clear();
688
689 // call us once again - may be we'll have output
690 return TRUE;
691 }
692
693 return MyPipedProcess::HasInput();
694 }