#include "wx/numdlg.h"
#include "wx/textdlg.h"
#include "wx/ffile.h"
+#include "wx/stopwatch.h"
#include "wx/process.h"
// Define an array of process pointers used by MyFrame
class MyPipedProcess;
-WX_DEFINE_ARRAY_PTR(MyPipedProcess *, MyProcessesArray);
+WX_DEFINE_ARRAY_PTR(MyPipedProcess *, MyPipedProcessesArray);
+
+class MyProcess;
+WX_DEFINE_ARRAY_PTR(MyProcess *, MyProcessesArray);
// Define a new frame type: this is going to be our main frame
class MyFrame : public wxFrame
{
public:
- // ctor(s)
+ // ctor and dtor
MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size);
+ virtual ~MyFrame();
// event handlers (these functions should _not_ be virtual)
void OnQuit(wxCommandEvent& event);
void OnPOpen(wxCommandEvent& event);
void OnFileExec(wxCommandEvent& event);
+ void OnFileLaunch(wxCommandEvent& event);
void OnOpenURL(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
// polling output of async processes
- void OnTimer(wxTimerEvent& event);
+ void OnIdleTimer(wxTimerEvent& event);
void OnIdle(wxIdleEvent& event);
// for MyPipedProcess
void OnProcessTerminated(MyPipedProcess *process);
wxListBox *GetLogListBox() const { return m_lbox; }
+ // for MyProcess
+ void OnAsyncTermination(MyProcess *process);
+
+ // timer updating a counter in the background
+ void OnBgTimer(wxTimerEvent& event);
+
private:
void ShowOutput(const wxString& cmd,
const wxArrayString& output,
void DoAsyncExec(const wxString& cmd);
- void AddAsyncProcess(MyPipedProcess *process)
- {
- if ( m_running.IsEmpty() )
- {
- // we want to start getting the timer events to ensure that a
- // steady stream of idle events comes in -- otherwise we
- // wouldn't be able to poll the child process input
- m_timerIdleWakeUp.Start(100);
- }
- //else: the timer is already running
-
- m_running.Add(process);
- }
+ void AddAsyncProcess(MyProcess *process) { m_allAsync.push_back(process); }
- void RemoveAsyncProcess(MyPipedProcess *process)
- {
- m_running.Remove(process);
+ void AddPipedProcess(MyPipedProcess *process);
+ void RemovePipedProcess(MyPipedProcess *process);
- if ( m_running.IsEmpty() )
- {
- // we don't need to get idle events all the time any more
- m_timerIdleWakeUp.Stop();
- }
- }
// the PID of the last process we launched asynchronously
long m_pidLast;
wxListBox *m_lbox;
- MyProcessesArray m_running;
+ // array of running processes with redirected IO
+ MyPipedProcessesArray m_running;
+
+ // array of all asynchrously running processes
+ MyProcessesArray m_allAsync;
// the idle event wake up timer
wxTimer m_timerIdleWakeUp;
+ // a background timer allowing to easily check visually whether the
+ // messages are processed or not
+ wxTimer m_timerBg;
+
// any class wishing to process wxWidgets events must use this macro
DECLARE_EVENT_TABLE()
};
// IDs for the controls and the menu commands
enum
{
+ // timer ids
+ Exec_TimerIdle = 10,
+ Exec_TimerBg,
+
// menu items
Exec_Quit = 100,
Exec_Kill,
Exec_Shell,
Exec_POpen,
Exec_OpenFile,
+ Exec_LaunchFile,
Exec_OpenURL,
Exec_DDEExec,
Exec_DDERequest,
Exec_Redirect,
Exec_Pipe,
- Exec_About = 300,
+ Exec_About = wxID_ABOUT,
// control ids
Exec_Btn_Send = 1000,
EVT_MENU(Exec_POpen, MyFrame::OnPOpen)
EVT_MENU(Exec_OpenFile, MyFrame::OnFileExec)
+ EVT_MENU(Exec_LaunchFile, MyFrame::OnFileLaunch)
EVT_MENU(Exec_OpenURL, MyFrame::OnOpenURL)
#ifdef __WINDOWS__
EVT_IDLE(MyFrame::OnIdle)
- EVT_TIMER(wxID_ANY, MyFrame::OnTimer)
+ EVT_TIMER(Exec_TimerIdle, MyFrame::OnIdleTimer)
+ EVT_TIMER(Exec_TimerBg, MyFrame::OnBgTimer)
END_EVENT_TABLE()
BEGIN_EVENT_TABLE(MyPipeFrame, wxFrame)
// frame constructor
MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
: wxFrame((wxFrame *)NULL, wxID_ANY, title, pos, size),
- m_timerIdleWakeUp(this)
+ m_timerIdleWakeUp(this, Exec_TimerIdle),
+ m_timerBg(this, Exec_TimerBg)
{
m_pidLast = 0;
execMenu->AppendSeparator();
execMenu->Append(Exec_OpenFile, _T("Open &file...\tCtrl-F"),
_T("Launch the command to open this kind of files"));
+ execMenu->Append(Exec_LaunchFile, _T("La&unch file...\tShift-Ctrl-F"),
+ _T("Launch the default application associated with the file"));
execMenu->Append(Exec_OpenURL, _T("Open &URL...\tCtrl-U"),
_T("Launch the default browser with the given URL"));
#ifdef __WINDOWS__
#if wxUSE_STATUSBAR
// create a status bar just for fun (by default with 1 pane only)
- CreateStatusBar();
+ CreateStatusBar(2);
SetStatusText(_T("Welcome to wxWidgets exec sample!"));
#endif // wxUSE_STATUSBAR
+
+ m_timerBg.Start(1000);
+}
+
+MyFrame::~MyFrame()
+{
+ // any processes left until now must be deleted manually: normally this is
+ // done when the associated process terminates but it must be still running
+ // if this didn't happen until now
+ for ( size_t n = 0; n < m_allAsync.size(); n++ )
+ {
+ delete m_allAsync[n];
+ }
}
// ----------------------------------------------------------------------------
void MyFrame::DoAsyncExec(const wxString& cmd)
{
- wxProcess *process = new MyProcess(this, cmd);
+ MyProcess * const process = new MyProcess(this, cmd);
m_pidLast = wxExecute(cmd, wxEXEC_ASYNC, process);
if ( !m_pidLast )
{
- wxLogError( _T("Execution of '%s' failed."), cmd.c_str() );
+ wxLogError(_T("Execution of '%s' failed."), cmd.c_str());
delete process;
}
else
{
- wxLogStatus( _T("Process %ld (%s) launched."),
- m_pidLast, cmd.c_str() );
+ wxLogStatus(_T("Process %ld (%s) launched."), m_pidLast, cmd.c_str());
m_cmdLast = cmd;
+
+ // the parent frame keeps track of all async processes as it needs to
+ // free them if we exit before the child process terminates
+ AddAsyncProcess(process);
}
}
void MyFrame::OnExecWithRedirect(wxCommandEvent& WXUNUSED(event))
{
+ if ( !m_cmdLast )
+ {
+#ifdef __WXMSW__
+ m_cmdLast = "type Makefile.in";
+#else
+ m_cmdLast = "cat -n Makefile";
+#endif
+ }
+
wxString cmd = wxGetTextFromUser(_T("Enter the command: "),
DIALOG_TITLE,
m_cmdLast);
if ( sync )
{
+ wxLogStatus("\"%s\" is running please wait...", cmd);
+
+ wxStopWatch sw;
+
wxArrayString output, errors;
int code = wxExecute(cmd, output, errors);
- wxLogStatus(_T("command '%s' terminated with exit code %d."),
- cmd.c_str(), code);
+
+ wxLogStatus("Command \"%s\" terminated after %ldms; exit code %d.",
+ cmd, sw.Time(), code);
if ( code != -1 )
{
}
else
{
- AddAsyncProcess(process);
+ AddPipedProcess(process);
}
}
long pid = wxExecute(cmd, wxEXEC_ASYNC, process);
if ( pid )
{
- wxLogStatus( _T("Process %ld (%s) launched."), pid, cmd.c_str() );
+ wxLogStatus(_T("Process %ld (%s) launched."), pid, cmd.c_str());
- AddAsyncProcess(process);
+ AddPipedProcess(process);
}
else
{
new MyPipeFrame(this, cmd, process);
}
-void MyFrame::OnFileExec(wxCommandEvent& WXUNUSED(event))
-{
- static wxString s_filename;
+static wxString gs_lastFile;
+static bool AskUserForFileName()
+{
wxString filename;
#if wxUSE_FILEDLG
- filename = wxLoadFileSelector(_T("any file"), wxEmptyString, s_filename, this);
+ filename = wxLoadFileSelector(_T("any"), wxEmptyString, gs_lastFile);
#else // !wxUSE_FILEDLG
filename = wxGetTextFromUser(_T("Enter the file name"), _T("exec sample"),
- s_filename, this);
+ gs_lastFile);
#endif // wxUSE_FILEDLG/!wxUSE_FILEDLG
if ( filename.empty() )
- return;
+ return false;
- s_filename = filename;
+ gs_lastFile = filename;
+
+ return true;
+}
+
+void MyFrame::OnFileExec(wxCommandEvent& WXUNUSED(event))
+{
+ if ( !AskUserForFileName() )
+ return;
- wxString ext = filename.AfterFirst(_T('.'));
+ wxString ext = gs_lastFile.AfterLast(_T('.'));
wxFileType *ft = wxTheMimeTypesManager->GetFileTypeFromExtension(ext);
if ( !ft )
{
wxString cmd;
bool ok = ft->GetOpenCommand(&cmd,
- wxFileType::MessageParameters(filename));
+ wxFileType::MessageParameters(gs_lastFile));
delete ft;
if ( !ok )
{
DoAsyncExec(cmd);
}
+void MyFrame::OnFileLaunch(wxCommandEvent& WXUNUSED(event))
+{
+ if ( !AskUserForFileName() )
+ return;
+
+ if ( !wxLaunchDefaultApplication(gs_lastFile) )
+ {
+ wxLogError("Opening \"%s\" in default application failed.", gs_lastFile);
+ }
+}
+
void MyFrame::OnOpenURL(wxCommandEvent& WXUNUSED(event))
{
- static wxString s_filename(_T("http://www.wxwidgets.org/"));
+ static wxString s_url(_T("http://www.wxwidgets.org/"));
wxString filename = wxGetTextFromUser
(
_T("Enter the URL"),
_T("exec sample"),
- s_filename,
+ s_url,
this
);
if ( filename.empty() )
return;
- s_filename = filename;
+ s_url = filename;
- if ( !wxLaunchDefaultBrowser(s_filename) )
- wxLogError(_T("Failed to open URL \"%s\""), s_filename.c_str());
+ if ( !wxLaunchDefaultBrowser(s_url) )
+ wxLogError(_T("Failed to open URL \"%s\""), s_url.c_str());
}
// ----------------------------------------------------------------------------
}
}
-void MyFrame::OnTimer(wxTimerEvent& WXUNUSED(event))
+void MyFrame::OnIdleTimer(wxTimerEvent& WXUNUSED(event))
{
wxWakeUpIdle();
}
+void MyFrame::OnBgTimer(wxTimerEvent& WXUNUSED(event))
+{
+ static unsigned long s_ticks = 0;
+ SetStatusText(wxString::Format("%lu ticks", s_ticks++), 1);
+}
+
void MyFrame::OnProcessTerminated(MyPipedProcess *process)
{
- RemoveAsyncProcess(process);
+ RemovePipedProcess(process);
}
+void MyFrame::OnAsyncTermination(MyProcess *process)
+{
+ m_allAsync.Remove(process);
+
+ delete process;
+}
+
+void MyFrame::AddPipedProcess(MyPipedProcess *process)
+{
+ if ( m_running.IsEmpty() )
+ {
+ // we want to start getting the timer events to ensure that a
+ // steady stream of idle events comes in -- otherwise we
+ // wouldn't be able to poll the child process input
+ m_timerIdleWakeUp.Start(100);
+ }
+ //else: the timer is already running
+
+ m_running.Add(process);
+ m_allAsync.Add(process);
+}
+
+void MyFrame::RemovePipedProcess(MyPipedProcess *process)
+{
+ m_running.Remove(process);
+
+ if ( m_running.IsEmpty() )
+ {
+ // we don't need to get idle events all the time any more
+ m_timerIdleWakeUp.Stop();
+ }
+}
void MyFrame::ShowOutput(const wxString& cmd,
const wxArrayString& output,
wxLogStatus(m_parent, _T("Process %u ('%s') terminated with exit code %d."),
pid, m_cmd.c_str(), status);
- // we're not needed any more
- delete this;
+ m_parent->OnAsyncTermination(this);
}
// ----------------------------------------------------------------------------