X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/8f6d9cb3f78b89e8afc29083ba06a4d09d29628b..11e3c6ef36393fb5863ea2f9601d8facd73acb12:/samples/exec/exec.cpp?ds=sidebyside diff --git a/samples/exec/exec.cpp b/samples/exec/exec.cpp index 75279b9ce9..d72c89a7ad 100644 --- a/samples/exec/exec.cpp +++ b/samples/exec/exec.cpp @@ -43,6 +43,8 @@ #include "wx/choicdlg.h" #include "wx/button.h" + #include "wx/checkbox.h" + #include "wx/stattext.h" #include "wx/textctrl.h" #include "wx/listbox.h" @@ -53,6 +55,8 @@ #include "wx/numdlg.h" #include "wx/textdlg.h" #include "wx/ffile.h" +#include "wx/scopedptr.h" +#include "wx/stopwatch.h" #include "wx/process.h" @@ -62,6 +66,10 @@ #include "wx/dde.h" #endif // __WINDOWS__ +#ifndef wxHAS_IMAGES_IN_RESOURCES + #include "../sample.xpm" +#endif + // ---------------------------------------------------------------------------- // the usual application and main frame classes // ---------------------------------------------------------------------------- @@ -81,14 +89,18 @@ public: // 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); @@ -97,8 +109,10 @@ public: void OnClear(wxCommandEvent& event); + void OnBeginBusyCursor(wxCommandEvent& event); + void OnEndBusyCursor(wxCommandEvent& event); + void OnSyncExec(wxCommandEvent& event); - void OnSyncNoEventsExec(wxCommandEvent& event); void OnAsyncExec(wxCommandEvent& event); void OnShell(wxCommandEvent& event); void OnExecWithRedirect(wxCommandEvent& event); @@ -107,49 +121,40 @@ public: void OnPOpen(wxCommandEvent& event); void OnFileExec(wxCommandEvent& event); + void OnFileLaunch(wxCommandEvent& event); void OnOpenURL(wxCommandEvent& event); + void OnShowCommandForExt(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, const wxString& title); - void DoAsyncExec(const wxString& cmd); + int GetExecFlags() const; - 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 + void DoAsyncExec(const wxString& cmd); - 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; @@ -171,11 +176,19 @@ private: 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() }; @@ -205,7 +218,7 @@ protected: void DoSend() { wxString s(m_textOut->GetValue()); - s += _T('\n'); + s += wxT('\n'); m_out.Write(s.c_str(), s.length()); m_textOut->Clear(); @@ -296,22 +309,32 @@ private: // IDs for the controls and the menu commands enum { + // timer ids + Exec_TimerIdle = 10, + Exec_TimerBg, + // menu items - Exec_Quit = 100, - Exec_Kill, + Exec_Kill = 100, Exec_ClearLog, + Exec_BeginBusyCursor, + Exec_EndBusyCursor, Exec_SyncExec = 200, - Exec_SyncNoEventsExec, Exec_AsyncExec, Exec_Shell, Exec_POpen, Exec_OpenFile, + Exec_ShowCommandForExt, + Exec_LaunchFile, Exec_OpenURL, Exec_DDEExec, Exec_DDERequest, Exec_Redirect, Exec_Pipe, - Exec_About = 300, + Exec_Flags_HideConsole, + Exec_Flags_ShowConsole, + Exec_Flags_NoEvents, + Exec_About = wxID_ABOUT, + Exec_Quit = wxID_EXIT, // control ids Exec_Btn_Send = 1000, @@ -320,7 +343,7 @@ enum Exec_Btn_Close }; -static const wxChar *DIALOG_TITLE = _T("Exec sample"); +static const wxChar *DIALOG_TITLE = wxT("Exec sample"); // ---------------------------------------------------------------------------- // event tables and other macros for wxWidgets @@ -333,9 +356,10 @@ BEGIN_EVENT_TABLE(MyFrame, wxFrame) EVT_MENU(Exec_Quit, MyFrame::OnQuit) EVT_MENU(Exec_Kill, MyFrame::OnKill) EVT_MENU(Exec_ClearLog, MyFrame::OnClear) + EVT_MENU(Exec_BeginBusyCursor, MyFrame::OnBeginBusyCursor) + EVT_MENU(Exec_EndBusyCursor, MyFrame::OnEndBusyCursor) EVT_MENU(Exec_SyncExec, MyFrame::OnSyncExec) - EVT_MENU(Exec_SyncNoEventsExec, MyFrame::OnSyncNoEventsExec) EVT_MENU(Exec_AsyncExec, MyFrame::OnAsyncExec) EVT_MENU(Exec_Shell, MyFrame::OnShell) EVT_MENU(Exec_Redirect, MyFrame::OnExecWithRedirect) @@ -344,6 +368,8 @@ BEGIN_EVENT_TABLE(MyFrame, wxFrame) EVT_MENU(Exec_POpen, MyFrame::OnPOpen) EVT_MENU(Exec_OpenFile, MyFrame::OnFileExec) + EVT_MENU(Exec_ShowCommandForExt, MyFrame::OnShowCommandForExt) + EVT_MENU(Exec_LaunchFile, MyFrame::OnFileLaunch) EVT_MENU(Exec_OpenURL, MyFrame::OnOpenURL) #ifdef __WINDOWS__ @@ -355,7 +381,8 @@ BEGIN_EVENT_TABLE(MyFrame, wxFrame) 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) @@ -393,12 +420,11 @@ bool MyApp::OnInit() return false; // Create the main application window - MyFrame *frame = new MyFrame(_T("Exec wxWidgets sample"), + MyFrame *frame = new MyFrame(wxT("Exec wxWidgets sample"), wxDefaultPosition, wxSize(500, 140)); - // Show it and tell the application that it's our main window + // Show it frame->Show(true); - SetTopWindow(frame); // success: wxApp::OnRun() will be called which will enter the main message // loop and the application will run. If we returned false here, the @@ -417,8 +443,11 @@ bool MyApp::OnInit() // 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) { + SetIcon(wxICON(sample)); + m_pidLast = 0; #ifdef __WXMAC__ @@ -429,50 +458,64 @@ MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) // create a menu bar wxMenu *menuFile = new wxMenu(wxEmptyString, wxMENU_TEAROFF); - menuFile->Append(Exec_Kill, _T("&Kill process...\tCtrl-K"), - _T("Kill a process by PID")); + menuFile->Append(Exec_Kill, wxT("&Kill process...\tCtrl-K"), + wxT("Kill a process by PID")); + menuFile->AppendSeparator(); + menuFile->Append(Exec_OpenFile, wxT("Open &file...\tCtrl-F"), + wxT("Launch the command to open this kind of files")); + menuFile->Append(Exec_ShowCommandForExt, + "Show association for extension...\tShift-Ctrl-A", + "Show the command associated with the given extension"); + menuFile->Append(Exec_LaunchFile, wxT("La&unch file...\tShift-Ctrl-F"), + wxT("Launch the default application associated with the file")); + menuFile->Append(Exec_OpenURL, wxT("Open &URL...\tCtrl-U"), + wxT("Launch the default browser with the given URL")); menuFile->AppendSeparator(); - menuFile->Append(Exec_ClearLog, _T("&Clear log\tCtrl-C"), - _T("Clear the log window")); + menuFile->Append(Exec_BeginBusyCursor, wxT("Show &busy cursor\tCtrl-C")); + menuFile->Append(Exec_EndBusyCursor, wxT("Show &normal cursor\tShift-Ctrl-C")); menuFile->AppendSeparator(); - menuFile->Append(Exec_Quit, _T("E&xit\tAlt-X"), _T("Quit this program")); + menuFile->Append(Exec_ClearLog, wxT("&Clear log\tCtrl-L"), + wxT("Clear the log window")); + menuFile->AppendSeparator(); + menuFile->Append(Exec_Quit, wxT("E&xit\tAlt-X"), wxT("Quit this program")); + + wxMenu *flagsMenu = new wxMenu; + flagsMenu->AppendCheckItem(Exec_Flags_HideConsole, "Always &hide console"); + flagsMenu->AppendCheckItem(Exec_Flags_ShowConsole, "Always &show console"); + flagsMenu->AppendCheckItem(Exec_Flags_NoEvents, "Disable &events", + "This flag is valid for sync execution only"); wxMenu *execMenu = new wxMenu; - execMenu->Append(Exec_SyncExec, _T("Sync &execution...\tCtrl-E"), - _T("Launch a program and return when it terminates")); - execMenu->Append(Exec_SyncNoEventsExec, _T("Sync execution and &block...\tCtrl-B"), - _T("Launch a program and block until it terminates")); - execMenu->Append(Exec_AsyncExec, _T("&Async execution...\tCtrl-A"), - _T("Launch a program and return immediately")); - execMenu->Append(Exec_Shell, _T("Execute &shell command...\tCtrl-S"), - _T("Launch a shell and execute a command in it")); + execMenu->AppendSubMenu(flagsMenu, "Execution flags"); execMenu->AppendSeparator(); - execMenu->Append(Exec_Redirect, _T("Capture command &output...\tCtrl-O"), - _T("Launch a program and capture its output")); - execMenu->Append(Exec_Pipe, _T("&Pipe through command..."), - _T("Pipe a string through a filter")); - execMenu->Append(Exec_POpen, _T("&Open a pipe to a command...\tCtrl-P"), - _T("Open a pipe to and from another program")); - + execMenu->Append(Exec_SyncExec, wxT("Sync &execution...\tCtrl-E"), + wxT("Launch a program and return when it terminates")); + execMenu->Append(Exec_AsyncExec, wxT("&Async execution...\tCtrl-A"), + wxT("Launch a program and return immediately")); + execMenu->Append(Exec_Shell, wxT("Execute &shell command...\tCtrl-S"), + wxT("Launch a shell and execute a command in it")); execMenu->AppendSeparator(); - execMenu->Append(Exec_OpenFile, _T("Open &file...\tCtrl-F"), - _T("Launch the command to open this kind of files")); - execMenu->Append(Exec_OpenURL, _T("Open &URL...\tCtrl-U"), - _T("Launch the default browser with the given URL")); + execMenu->Append(Exec_Redirect, wxT("Capture command &output...\tCtrl-O"), + wxT("Launch a program and capture its output")); + execMenu->Append(Exec_Pipe, wxT("&Pipe through command..."), + wxT("Pipe a string through a filter")); + execMenu->Append(Exec_POpen, wxT("&Open a pipe to a command...\tCtrl-P"), + wxT("Open a pipe to and from another program")); + #ifdef __WINDOWS__ execMenu->AppendSeparator(); - execMenu->Append(Exec_DDEExec, _T("Execute command via &DDE...\tCtrl-D")); - execMenu->Append(Exec_DDERequest, _T("Send DDE &request...\tCtrl-R")); + execMenu->Append(Exec_DDEExec, wxT("Execute command via &DDE...\tCtrl-D")); + execMenu->Append(Exec_DDERequest, wxT("Send DDE &request...\tCtrl-R")); #endif wxMenu *helpMenu = new wxMenu(wxEmptyString, wxMENU_TEAROFF); - helpMenu->Append(Exec_About, _T("&About...\tF1"), _T("Show about dialog")); + helpMenu->Append(Exec_About, wxT("&About\tF1"), wxT("Show about dialog")); // now append the freshly created menu to the menu bar... wxMenuBar *menuBar = new wxMenuBar(); - menuBar->Append(menuFile, _T("&File")); - menuBar->Append(execMenu, _T("&Exec")); - menuBar->Append(helpMenu, _T("&Help")); + menuBar->Append(menuFile, wxT("&File")); + menuBar->Append(execMenu, wxT("&Exec")); + menuBar->Append(helpMenu, wxT("&Help")); // ... and attach this menu bar to the frame SetMenuBar(menuBar); @@ -481,14 +524,27 @@ MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) m_lbox = new wxListBox(this, wxID_ANY); wxFont font(12, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL); - if ( font.Ok() ) + if ( font.IsOk() ) m_lbox->SetFont(font); #if wxUSE_STATUSBAR // create a status bar just for fun (by default with 1 pane only) - CreateStatusBar(); - SetStatusText(_T("Welcome to wxWidgets exec sample!")); + CreateStatusBar(2); + SetStatusText(wxT("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]; + } } // ---------------------------------------------------------------------------- @@ -506,17 +562,27 @@ void MyFrame::OnClear(wxCommandEvent& WXUNUSED(event)) m_lbox->Clear(); } +void MyFrame::OnBeginBusyCursor(wxCommandEvent& WXUNUSED(event)) +{ + wxBeginBusyCursor(); +} + +void MyFrame::OnEndBusyCursor(wxCommandEvent& WXUNUSED(event)) +{ + wxEndBusyCursor(); +} + void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event)) { - wxMessageBox(_T("Exec wxWidgets Sample\n(c) 2000-2002 Vadim Zeitlin"), - _T("About Exec"), wxOK | wxICON_INFORMATION, this); + wxMessageBox(wxT("Exec wxWidgets Sample\n(c) 2000-2002 Vadim Zeitlin"), + wxT("About Exec"), wxOK | wxICON_INFORMATION, this); } void MyFrame::OnKill(wxCommandEvent& WXUNUSED(event)) { - long pid = wxGetNumberFromUser(_T("Please specify the process to kill"), - _T("Enter PID:"), - _T("Exec question"), + long pid = wxGetNumberFromUser(wxT("Please specify the process to kill"), + wxT("Enter PID:"), + wxT("Exec question"), m_pidLast, // we need the full unsigned int range -INT_MAX, INT_MAX, @@ -527,34 +593,38 @@ void MyFrame::OnKill(wxCommandEvent& WXUNUSED(event)) return; } + m_pidLast = pid; + static const wxString signalNames[] = { - _T("Just test (SIGNONE)"), - _T("Hangup (SIGHUP)"), - _T("Interrupt (SIGINT)"), - _T("Quit (SIGQUIT)"), - _T("Illegal instruction (SIGILL)"), - _T("Trap (SIGTRAP)"), - _T("Abort (SIGABRT)"), - _T("Emulated trap (SIGEMT)"), - _T("FP exception (SIGFPE)"), - _T("Kill (SIGKILL)"), - _T("Bus (SIGBUS)"), - _T("Segment violation (SIGSEGV)"), - _T("System (SIGSYS)"), - _T("Broken pipe (SIGPIPE)"), - _T("Alarm (SIGALRM)"), - _T("Terminate (SIGTERM)"), + wxT("Just test (SIGNONE)"), + wxT("Hangup (SIGHUP)"), + wxT("Interrupt (SIGINT)"), + wxT("Quit (SIGQUIT)"), + wxT("Illegal instruction (SIGILL)"), + wxT("Trap (SIGTRAP)"), + wxT("Abort (SIGABRT)"), + wxT("Emulated trap (SIGEMT)"), + wxT("FP exception (SIGFPE)"), + wxT("Kill (SIGKILL)"), + wxT("Bus (SIGBUS)"), + wxT("Segment violation (SIGSEGV)"), + wxT("System (SIGSYS)"), + wxT("Broken pipe (SIGPIPE)"), + wxT("Alarm (SIGALRM)"), + wxT("Terminate (SIGTERM)"), }; - int sig = wxGetSingleChoiceIndex(_T("How to kill the process?"), - _T("Exec question"), + static int s_sigLast = wxSIGNONE; + int sig = wxGetSingleChoiceIndex(wxT("How to kill the process?"), + wxT("Exec question"), WXSIZEOF(signalNames), signalNames, + s_sigLast, this); switch ( sig ) { default: - wxFAIL_MSG( _T("unexpected return value") ); + wxFAIL_MSG( wxT("unexpected return value") ); // fall through case -1: @@ -580,93 +650,239 @@ void MyFrame::OnKill(wxCommandEvent& WXUNUSED(event)) break; } - if ( sig == 0 ) + s_sigLast = sig; + + if ( sig == wxSIGNONE ) { + // This simply calls Kill(wxSIGNONE) but using it is more convenient. if ( wxProcess::Exists(pid) ) - wxLogStatus(_T("Process %ld is running."), pid); + { + wxLogStatus(wxT("Process %ld is running."), pid); + } else - wxLogStatus(_T("No process with pid = %ld."), pid); + { + wxLogStatus(wxT("No process with pid = %ld."), pid); + } } else // not SIGNONE { wxKillError rc = wxProcess::Kill(pid, (wxSignal)sig); if ( rc == wxKILL_OK ) { - wxLogStatus(_T("Process %ld killed with signal %d."), pid, sig); + wxLogStatus(wxT("Process %ld killed with signal %d."), pid, sig); } else { static const wxChar *errorText[] = { - _T(""), // no error - _T("signal not supported"), - _T("permission denied"), - _T("no such process"), - _T("unspecified error"), + wxT(""), // no error + wxT("signal not supported"), + wxT("permission denied"), + wxT("no such process"), + wxT("unspecified error"), }; - wxLogStatus(_T("Failed to kill process %ld with signal %d: %s"), + wxLogStatus(wxT("Failed to kill process %ld with signal %d: %s"), pid, sig, errorText[rc]); } } } // ---------------------------------------------------------------------------- -// event handlers: exec menu +// execution options dialog // ---------------------------------------------------------------------------- -void MyFrame::DoAsyncExec(const wxString& cmd) +enum ExecQueryDialogID { - wxProcess *process = new MyProcess(this, cmd); - m_pidLast = wxExecute(cmd, wxEXEC_ASYNC, process); - if ( !m_pidLast ) + TEXT_EXECUTABLE, + TEXT_CWD, + TEXT_ENVIRONMENT +}; + +class ExecQueryDialog : public wxDialog +{ +public: + ExecQueryDialog(const wxString& cmd); + + wxString GetExecutable() const { - wxLogError( _T("Execution of '%s' failed."), cmd.c_str() ); + return m_executable->GetValue(); + } - delete process; + wxString GetWorkDir() const + { + return m_useCWD->GetValue() ? m_cwdtext->GetValue() : wxString(); } - else + + void GetEnvironment(wxEnvVariableHashMap& env); + +private: + void OnUpdateWorkingDirectoryUI(wxUpdateUIEvent& event) { - wxLogStatus( _T("Process %ld (%s) launched."), - m_pidLast, cmd.c_str() ); + event.Enable(m_useCWD->GetValue()); + } - m_cmdLast = cmd; + void OnUpdateEnvironmentUI(wxUpdateUIEvent& event) + { + event.Enable(m_useEnv->GetValue()); + } + + wxTextCtrl* m_executable; + wxTextCtrl* m_cwdtext; + wxTextCtrl* m_envtext; + wxCheckBox* m_useCWD; + wxCheckBox* m_useEnv; + + DECLARE_EVENT_TABLE() +}; + +BEGIN_EVENT_TABLE(ExecQueryDialog, wxDialog) + EVT_UPDATE_UI(TEXT_CWD, ExecQueryDialog::OnUpdateWorkingDirectoryUI) + EVT_UPDATE_UI(TEXT_ENVIRONMENT, ExecQueryDialog::OnUpdateEnvironmentUI) +END_EVENT_TABLE() + +ExecQueryDialog::ExecQueryDialog(const wxString& cmd) + : wxDialog(NULL, wxID_ANY, DIALOG_TITLE, + wxDefaultPosition, wxDefaultSize, + wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) +{ + wxSizer* globalSizer = new wxBoxSizer(wxVERTICAL); + + m_executable = new wxTextCtrl(this, TEXT_EXECUTABLE, wxString()); + m_cwdtext = new wxTextCtrl(this, TEXT_CWD, wxString()); + m_envtext = new wxTextCtrl(this, TEXT_ENVIRONMENT, wxString(), + wxDefaultPosition, wxSize(300, 200), + wxTE_MULTILINE|wxHSCROLL); + + const wxSizerFlags flagsExpand = wxSizerFlags().Expand().Border(); + globalSizer->Add(new wxStaticText(this, wxID_ANY, "Enter the command: "), + flagsExpand); + globalSizer->Add(m_executable, flagsExpand); + + m_useCWD = new wxCheckBox(this, wxID_ANY, "Working directory: "); + globalSizer->Add(m_useCWD, flagsExpand); + globalSizer->Add(m_cwdtext, flagsExpand); + + m_useEnv = new wxCheckBox(this, wxID_ANY, "Environment: "); + globalSizer->Add(m_useEnv, flagsExpand); + globalSizer->Add(m_envtext, wxSizerFlags(flagsExpand).Proportion(1)); + + globalSizer->Add(CreateStdDialogButtonSizer(wxOK|wxCANCEL), flagsExpand); + SetSizerAndFit(globalSizer); + + + m_executable->SetValue(cmd); + m_cwdtext->SetValue(wxGetCwd()); + wxEnvVariableHashMap env; + if ( wxGetEnvMap(&env) ) + { + for ( wxEnvVariableHashMap::iterator it = env.begin(); + it != env.end(); + ++it ) + { + m_envtext->AppendText(it->first + '=' + it->second + '\n'); + } } + m_useCWD->SetValue(false); + m_useEnv->SetValue(false); } -void MyFrame::OnSyncExec(wxCommandEvent& WXUNUSED(event)) +void ExecQueryDialog::GetEnvironment(wxEnvVariableHashMap& env) { - wxString cmd = wxGetTextFromUser(_T("Enter the command: "), - DIALOG_TITLE, - m_cmdLast); + env.clear(); + if ( m_useEnv->GetValue() ) + { + wxString name, + value; - if ( !cmd ) - return; + const int nb = m_envtext->GetNumberOfLines(); + for ( int l = 0; l < nb; l++ ) + { + const wxString line = m_envtext->GetLineText(l).Trim(); + + if ( !line.empty() ) + { + name = line.BeforeFirst('=', &value); + if ( name.empty() ) + { + wxLogWarning("Skipping invalid environment line \"%s\".", line); + continue; + } + + env[name] = value; + } + } + } +} - wxLogStatus( _T("'%s' is running please wait..."), cmd.c_str() ); +static bool QueryExec(wxString& cmd, wxExecuteEnv& env) +{ + ExecQueryDialog dialog(cmd); - int code = wxExecute(cmd, wxEXEC_SYNC); + if ( dialog.ShowModal() != wxID_OK ) + return false; - wxLogStatus(_T("Process '%s' terminated with exit code %d."), - cmd.c_str(), code); + cmd = dialog.GetExecutable(); + env.cwd = dialog.GetWorkDir(); + dialog.GetEnvironment(env.env); - m_cmdLast = cmd; + return true; } -void MyFrame::OnSyncNoEventsExec(wxCommandEvent& WXUNUSED(event)) +// ---------------------------------------------------------------------------- +// event handlers: exec menu +// ---------------------------------------------------------------------------- + +int MyFrame::GetExecFlags() const { - wxString cmd = wxGetTextFromUser(_T("Enter the command: "), - DIALOG_TITLE, - m_cmdLast); + wxMenuBar* const mbar = GetMenuBar(); - if ( !cmd ) + int flags = 0; + + if ( mbar->IsChecked(Exec_Flags_HideConsole) ) + flags |= wxEXEC_HIDE_CONSOLE; + if ( mbar->IsChecked(Exec_Flags_ShowConsole) ) + flags |= wxEXEC_SHOW_CONSOLE; + if ( mbar->IsChecked(Exec_Flags_NoEvents) ) + flags |= wxEXEC_NOEVENTS; + + return flags; +} + +void MyFrame::DoAsyncExec(const wxString& cmd) +{ + MyProcess * const process = new MyProcess(this, cmd); + m_pidLast = wxExecute(cmd, wxEXEC_ASYNC | GetExecFlags(), process); + if ( !m_pidLast ) + { + wxLogError(wxT("Execution of '%s' failed."), cmd.c_str()); + + delete process; + } + else + { + wxLogStatus(wxT("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::OnSyncExec(wxCommandEvent& WXUNUSED(event)) +{ + wxString cmd; + wxExecuteEnv env; + if ( !QueryExec(cmd, env) ) return; - wxLogStatus( _T("'%s' is running please wait..."), cmd.c_str() ); + wxLogStatus( wxT("'%s' is running please wait..."), cmd.c_str() ); - int code = wxExecute(cmd, wxEXEC_BLOCK); + int code = wxExecute(cmd, wxEXEC_SYNC | GetExecFlags(), NULL, &env); - wxLogStatus(_T("Process '%s' terminated with exit code %d."), + wxLogStatus(wxT("Process '%s' terminated with exit code %d."), cmd.c_str(), code); m_cmdLast = cmd; @@ -674,7 +890,7 @@ void MyFrame::OnSyncNoEventsExec(wxCommandEvent& WXUNUSED(event)) void MyFrame::OnAsyncExec(wxCommandEvent& WXUNUSED(event)) { - wxString cmd = wxGetTextFromUser(_T("Enter the command: "), + wxString cmd = wxGetTextFromUser(wxT("Enter the command: "), DIALOG_TITLE, m_cmdLast); @@ -686,7 +902,7 @@ void MyFrame::OnAsyncExec(wxCommandEvent& WXUNUSED(event)) void MyFrame::OnShell(wxCommandEvent& WXUNUSED(event)) { - wxString cmd = wxGetTextFromUser(_T("Enter the command: "), + wxString cmd = wxGetTextFromUser(wxT("Enter the command: "), DIALOG_TITLE, m_cmdLast); @@ -694,14 +910,23 @@ void MyFrame::OnShell(wxCommandEvent& WXUNUSED(event)) return; int code = wxShell(cmd); - wxLogStatus(_T("Shell command '%s' terminated with exit code %d."), + wxLogStatus(wxT("Shell command '%s' terminated with exit code %d."), cmd.c_str(), code); m_cmdLast = cmd; } void MyFrame::OnExecWithRedirect(wxCommandEvent& WXUNUSED(event)) { - wxString cmd = wxGetTextFromUser(_T("Enter the command: "), + if ( !m_cmdLast ) + { +#ifdef __WXMSW__ + m_cmdLast = "type Makefile.in"; +#else + m_cmdLast = "cat -n Makefile"; +#endif + } + + wxString cmd = wxGetTextFromUser(wxT("Enter the command: "), DIALOG_TITLE, m_cmdLast); @@ -709,8 +934,8 @@ void MyFrame::OnExecWithRedirect(wxCommandEvent& WXUNUSED(event)) return; bool sync; - switch ( wxMessageBox(_T("Execute it synchronously?"), - _T("Exec question"), + switch ( wxMessageBox(wxT("Execute it synchronously?"), + wxT("Exec question"), wxYES_NO | wxCANCEL | wxICON_QUESTION, this) ) { case wxYES: @@ -727,15 +952,20 @@ void MyFrame::OnExecWithRedirect(wxCommandEvent& WXUNUSED(event)) 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 ) { - ShowOutput(cmd, output, _T("Output")); - ShowOutput(cmd, errors, _T("Errors")); + ShowOutput(cmd, output, wxT("Output")); + ShowOutput(cmd, errors, wxT("Errors")); } } else // async exec @@ -743,13 +973,13 @@ void MyFrame::OnExecWithRedirect(wxCommandEvent& WXUNUSED(event)) MyPipedProcess *process = new MyPipedProcess(this, cmd); if ( !wxExecute(cmd, wxEXEC_ASYNC, process) ) { - wxLogError(_T("Execution of '%s' failed."), cmd.c_str()); + wxLogError(wxT("Execution of '%s' failed."), cmd.c_str()); delete process; } else { - AddAsyncProcess(process); + AddPipedProcess(process); } } @@ -759,16 +989,16 @@ void MyFrame::OnExecWithRedirect(wxCommandEvent& WXUNUSED(event)) void MyFrame::OnExecWithPipe(wxCommandEvent& WXUNUSED(event)) { if ( !m_cmdLast ) - m_cmdLast = _T("tr [a-z] [A-Z]"); + m_cmdLast = wxT("tr [a-z] [A-Z]"); - wxString cmd = wxGetTextFromUser(_T("Enter the command: "), + wxString cmd = wxGetTextFromUser(wxT("Enter the command: "), DIALOG_TITLE, m_cmdLast); if ( !cmd ) return; - wxString input = wxGetTextFromUser(_T("Enter the string to send to it: "), + wxString input = wxGetTextFromUser(wxT("Enter the string to send to it: "), DIALOG_TITLE); if ( !input ) return; @@ -778,13 +1008,13 @@ void MyFrame::OnExecWithPipe(wxCommandEvent& WXUNUSED(event)) long pid = wxExecute(cmd, wxEXEC_ASYNC, process); if ( pid ) { - wxLogStatus( _T("Process %ld (%s) launched."), pid, cmd.c_str() ); + wxLogStatus(wxT("Process %ld (%s) launched."), pid, cmd.c_str()); - AddAsyncProcess(process); + AddPipedProcess(process); } else { - wxLogError(_T("Execution of '%s' failed."), cmd.c_str()); + wxLogError(wxT("Execution of '%s' failed."), cmd.c_str()); delete process; } @@ -794,7 +1024,7 @@ void MyFrame::OnExecWithPipe(wxCommandEvent& WXUNUSED(event)) void MyFrame::OnPOpen(wxCommandEvent& WXUNUSED(event)) { - wxString cmd = wxGetTextFromUser(_T("Enter the command to launch: "), + wxString cmd = wxGetTextFromUser(wxT("Enter the command to launch: "), DIALOG_TITLE, m_cmdLast); if ( cmd.empty() ) @@ -803,63 +1033,71 @@ void MyFrame::OnPOpen(wxCommandEvent& WXUNUSED(event)) wxProcess *process = wxProcess::Open(cmd); if ( !process ) { - wxLogError(_T("Failed to launch the command.")); + wxLogError(wxT("Failed to launch the command.")); return; } - wxLogVerbose(_T("PID of the new process: %ld"), process->GetPid()); + wxLogVerbose(wxT("PID of the new process: %ld"), process->GetPid()); wxOutputStream *out = process->GetOutputStream(); if ( !out ) { - wxLogError(_T("Failed to connect to child stdin")); + wxLogError(wxT("Failed to connect to child stdin")); return; } wxInputStream *in = process->GetInputStream(); if ( !in ) { - wxLogError(_T("Failed to connect to child stdout")); + wxLogError(wxT("Failed to connect to child stdout")); return; } 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"), NULL, s_filename, this); + filename = wxLoadFileSelector(wxT("any"), wxEmptyString, gs_lastFile); #else // !wxUSE_FILEDLG - filename = wxGetTextFromUser(_T("Enter the file name"), _T("exec sample"), - s_filename, this); + filename = wxGetTextFromUser(wxT("Enter the file name"), wxT("exec sample"), + gs_lastFile); #endif // wxUSE_FILEDLG/!wxUSE_FILEDLG if ( filename.empty() ) - return; + return false; - s_filename = filename; + gs_lastFile = filename; + + return true; +} - wxString ext = filename.AfterFirst(_T('.')); +void MyFrame::OnFileExec(wxCommandEvent& WXUNUSED(event)) +{ + if ( !AskUserForFileName() ) + return; + + wxString ext = gs_lastFile.AfterLast(wxT('.')); wxFileType *ft = wxTheMimeTypesManager->GetFileTypeFromExtension(ext); if ( !ft ) { - wxLogError(_T("Impossible to determine the file type for extension '%s'"), + wxLogError(wxT("Impossible to determine the file type for extension '%s'"), ext.c_str()); return; } wxString cmd; bool ok = ft->GetOpenCommand(&cmd, - wxFileType::MessageParameters(filename)); + wxFileType::MessageParameters(gs_lastFile)); delete ft; if ( !ok ) { - wxLogError(_T("Impossible to find out how to open files of extension '%s'"), + wxLogError(wxT("Impossible to find out how to open files of extension '%s'"), ext.c_str()); return; } @@ -867,25 +1105,73 @@ void MyFrame::OnFileExec(wxCommandEvent& WXUNUSED(event)) DoAsyncExec(cmd); } +void MyFrame::OnShowCommandForExt(wxCommandEvent& WXUNUSED(event)) +{ + static wxString s_ext; + + wxString ext = wxGetTextFromUser + ( + "Enter the extension without leading dot", + "Exec sample", + s_ext, + this + ); + if ( ext.empty() ) + return; + + s_ext = ext; + + wxScopedPtr + ft(wxTheMimeTypesManager->GetFileTypeFromExtension(ext)); + if ( !ft ) + { + wxLogError("Information for extension \"%s\" not found", ext); + return; + } + + const wxString cmd = ft->GetOpenCommand("file." + ext); + if ( cmd.empty() ) + { + wxLogWarning("Open command for extension \"%s\" not defined.", ext); + return; + } + + wxLogMessage("Open command for files of extension \"%s\" is\n%s", + ext, 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(wxT("http://www.wxwidgets.org/")); wxString filename = wxGetTextFromUser ( - _T("Enter the URL"), - _T("exec sample"), - s_filename, + wxT("Enter the URL"), + wxT("exec sample"), + 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(wxT("Failed to open URL \"%s\""), s_url.c_str()); + } } // ---------------------------------------------------------------------------- @@ -896,20 +1182,20 @@ void MyFrame::OnOpenURL(wxCommandEvent& WXUNUSED(event)) bool MyFrame::GetDDEServer() { - wxString server = wxGetTextFromUser(_T("Server to connect to:"), + wxString server = wxGetTextFromUser(wxT("Server to connect to:"), DIALOG_TITLE, m_server); if ( !server ) return false; m_server = server; - wxString topic = wxGetTextFromUser(_T("DDE topic:"), DIALOG_TITLE, m_topic); + wxString topic = wxGetTextFromUser(wxT("DDE topic:"), DIALOG_TITLE, m_topic); if ( !topic ) return false; m_topic = topic; - wxString cmd = wxGetTextFromUser(_T("DDE command:"), DIALOG_TITLE, m_cmdDde); + wxString cmd = wxGetTextFromUser(wxT("DDE command:"), DIALOG_TITLE, m_cmdDde); if ( !cmd ) return false; @@ -927,19 +1213,19 @@ void MyFrame::OnDDEExec(wxCommandEvent& WXUNUSED(event)) wxConnectionBase *conn = client.MakeConnection(wxEmptyString, m_server, m_topic); if ( !conn ) { - wxLogError(_T("Failed to connect to the DDE server '%s'."), + wxLogError(wxT("Failed to connect to the DDE server '%s'."), m_server.c_str()); } else { if ( !conn->Execute(m_cmdDde) ) { - wxLogError(_T("Failed to execute command '%s' via DDE."), + wxLogError(wxT("Failed to execute command '%s' via DDE."), m_cmdDde.c_str()); } else { - wxLogStatus(_T("Successfully executed DDE command")); + wxLogStatus(wxT("Successfully executed DDE command")); } } } @@ -953,19 +1239,19 @@ void MyFrame::OnDDERequest(wxCommandEvent& WXUNUSED(event)) wxConnectionBase *conn = client.MakeConnection(wxEmptyString, m_server, m_topic); if ( !conn ) { - wxLogError(_T("Failed to connect to the DDE server '%s'."), + wxLogError(wxT("Failed to connect to the DDE server '%s'."), m_server.c_str()); } else { if ( !conn->Request(m_cmdDde) ) { - wxLogError(_T("Failed to send request '%s' via DDE."), + wxLogError(wxT("Failed to send request '%s' via DDE."), m_cmdDde.c_str()); } else { - wxLogStatus(_T("Successfully sent DDE request.")); + wxLogStatus(wxT("Successfully sent DDE request.")); } } } @@ -989,16 +1275,54 @@ void MyFrame::OnIdle(wxIdleEvent& event) } } -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, @@ -1008,7 +1332,7 @@ void MyFrame::ShowOutput(const wxString& cmd, if ( !count ) return; - m_lbox->Append(wxString::Format(_T("--- %s of '%s' ---"), + m_lbox->Append(wxString::Format(wxT("--- %s of '%s' ---"), title.c_str(), cmd.c_str())); for ( size_t n = 0; n < count; n++ ) @@ -1016,7 +1340,7 @@ void MyFrame::ShowOutput(const wxString& cmd, m_lbox->Append(output[n]); } - m_lbox->Append(wxString::Format(_T("--- End of %s ---"), + m_lbox->Append(wxString::Format(wxT("--- End of %s ---"), title.Lower().c_str())); } @@ -1026,11 +1350,10 @@ void MyFrame::ShowOutput(const wxString& cmd, void MyProcess::OnTerminate(int pid, int status) { - wxLogStatus(m_parent, _T("Process %u ('%s') terminated with exit code %d."), + wxLogStatus(m_parent, wxT("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); } // ---------------------------------------------------------------------------- @@ -1047,7 +1370,7 @@ bool MyPipedProcess::HasInput() // this assumes that the output is always line buffered wxString msg; - msg << m_cmd << _T(" (stdout): ") << tis.ReadLine(); + msg << m_cmd << wxT(" (stdout): ") << tis.ReadLine(); m_parent->GetLogListBox()->Append(msg); @@ -1060,7 +1383,7 @@ bool MyPipedProcess::HasInput() // this assumes that the output is always line buffered wxString msg; - msg << m_cmd << _T(" (stderr): ") << tis.ReadLine(); + msg << m_cmd << wxT(" (stderr): ") << tis.ReadLine(); m_parent->GetLogListBox()->Append(msg); @@ -1137,13 +1460,13 @@ MyPipeFrame::MyPipeFrame(wxFrame *parent, wxSizer *sizerBtns = new wxBoxSizer(wxHORIZONTAL); sizerBtns-> - Add(new wxButton(panel, Exec_Btn_Send, _T("&Send")), 0, wxALL, 5); + Add(new wxButton(panel, Exec_Btn_Send, wxT("&Send")), 0, wxALL, 5); sizerBtns-> - Add(new wxButton(panel, Exec_Btn_SendFile, _T("&File...")), 0, wxALL, 5); + Add(new wxButton(panel, Exec_Btn_SendFile, wxT("&File...")), 0, wxALL, 5); sizerBtns-> - Add(new wxButton(panel, Exec_Btn_Get, _T("&Get")), 0, wxALL, 5); + Add(new wxButton(panel, Exec_Btn_Get, wxT("&Get")), 0, wxALL, 5); sizerBtns-> - Add(new wxButton(panel, Exec_Btn_Close, _T("&Close")), 0, wxALL, 5); + Add(new wxButton(panel, Exec_Btn_Close, wxT("&Close")), 0, wxALL, 5); sizerTop->Add(sizerBtns, 0, wxCENTRE | wxALL, 5); sizerTop->Add(m_textIn, 1, wxGROW | wxALL, 5); @@ -1158,11 +1481,11 @@ MyPipeFrame::MyPipeFrame(wxFrame *parent, void MyPipeFrame::OnBtnSendFile(wxCommandEvent& WXUNUSED(event)) { #if wxUSE_FILEDLG - wxFileDialog filedlg(this, _T("Select file to send")); + wxFileDialog filedlg(this, wxT("Select file to send")); if ( filedlg.ShowModal() != wxID_OK ) return; - wxFFile file(filedlg.GetFilename(), _T("r")); + wxFFile file(filedlg.GetFilename(), wxT("r")); wxString data; if ( !file.IsOpened() || !file.ReadAll(&data) ) return; @@ -1200,8 +1523,8 @@ void MyPipeFrame::DoGetFromStream(wxTextCtrl *text, wxInputStream& in) { while ( in.CanRead() ) { - wxChar buffer[4096]; - buffer[in.Read(buffer, WXSIZEOF(buffer) - 1).LastRead()] = _T('\0'); + char buffer[4096]; + buffer[in.Read(buffer, WXSIZEOF(buffer) - 1).LastRead()] = '\0'; text->AppendText(buffer); } @@ -1247,10 +1570,9 @@ void MyPipeFrame::OnProcessTerm(wxProcessEvent& WXUNUSED(event)) { DoGet(); - delete m_process; - m_process = NULL; + wxDELETE(m_process); - wxLogWarning(_T("The other process has terminated, closing")); + wxLogWarning(wxT("The other process has terminated, closing")); DisableInput(); DisableOutput();