Code::Blocks  SVN r11506
toolsmanager.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the Code::Blocks IDE and licensed under the GNU Lesser General Public License, version 3
3  * http://www.gnu.org/licenses/lgpl-3.0.html
4  *
5  * $Revision: 11435 $
6  * $Id: toolsmanager.cpp 11435 2018-08-07 07:13:14Z fuscated $
7  * $HeadURL: https://svn.code.sf.net/p/codeblocks/code/trunk/src/sdk/toolsmanager.cpp $
8  */
9 
10 #include "sdk_precomp.h"
11 
12 #ifndef CB_PRECOMP
13  #include <wx/intl.h>
14  #include <wx/process.h>
15  #include <wx/menu.h>
16  #include <wx/msgdlg.h>
17 
18  #include "toolsmanager.h"
19  #include "manager.h"
20  #include "macrosmanager.h"
21  #include "configmanager.h"
22  #include "logmanager.h"
23  #include "pipedprocess.h"
24  #include "globals.h"
25  #include "sdk_events.h"
26 #endif
27 
28 #include <wx/mdi.h>
29 #include <wx/listimpl.cpp>
30 #include "configuretoolsdlg.h"
31 
32 template<> ToolsManager* Mgr<ToolsManager>::instance = nullptr;
33 template<> bool Mgr<ToolsManager>::isShutdown = false;
34 
35 WX_DEFINE_LIST(ToolsList);
36 
37 const int idToolsConfigure = wxNewId();
38 const int idToolProcess = wxNewId();
39 
40 BEGIN_EVENT_TABLE(ToolsManager, wxEvtHandler)
42 
43  EVT_IDLE(ToolsManager::OnIdle)
44 
48 END_EVENT_TABLE()
49 
51  : m_Menu(nullptr),
52  m_pProcess(nullptr),
53  m_Pid(0)
54 {
55  LoadTools();
56  Manager::Get()->GetAppWindow()->PushEventHandler(this);
57 }
58 
60 {
61  // this is a core manager, so it is removed when the app is shutting down.
62  // in this case, the app has already un-hooked us, so no need to do it ourselves...
63 // Manager::Get()->GetAppWindow()->RemoveEventHandler(this);
64 
66 
67  // free-up any memory used for tools
68  m_Tools.DeleteContents(true);
69  m_Tools.Clear();
70 }
71 
72 void ToolsManager::CreateMenu(cb_unused wxMenuBar* menuBar)
73 {
74 }
75 
76 void ToolsManager::ReleaseMenu(cb_unused wxMenuBar* menuBar)
77 {
78 }
79 
80 bool ToolsManager::Execute(const cbTool* tool)
81 {
82  if (m_pProcess)
83  {
84  cbMessageBox(_("Another tool is currently executing.\n"
85  "Please allow for it to finish before launching another tool..."),
86  _("Error"), wxICON_ERROR);
87  return false;
88  }
89 
90  if (!tool)
91  return false;
92 
93  wxString cmdline;
94  wxString cmd = tool->GetCommand();
95  wxString params = tool->GetParams();
96  wxString dir = tool->GetWorkingDir();
97 
98  // hack to force-update macros
99  Manager::Get()->GetMacrosManager()->RecalcVars(nullptr, nullptr, nullptr);
100 
104 
106  {
107 #ifndef __WXMSW__
108  // for non-win platforms, use m_ConsoleTerm to run the console app
109  wxString term = Manager::Get()->GetConfigManager(_T("app"))->Read(_T("/console_terminal"), DEFAULT_CONSOLE_TERM);
110  term.Replace(_T("$TITLE"), _T("'") + tool->GetName() + _T("'"));
111  cmdline << term << _T(" ");
112  #define CONSOLE_RUNNER "cb_console_runner"
113 #else
114  #define CONSOLE_RUNNER "cb_console_runner.exe"
115 #endif
117  if (wxFileExists(baseDir + wxT("/" CONSOLE_RUNNER)))
118  cmdline << baseDir << wxT("/" CONSOLE_RUNNER " ");
119  }
120 
121  if (!cmdline.Replace(_T("$SCRIPT"), cmd << _T(" ") << params))
122  // if they didn't specify $SCRIPT, append:
123  cmdline << cmd;
124 
125  if(!(Manager::Get()->GetMacrosManager()))
126  return false; // We cannot afford the Macros Manager to fail here!
127  // What if it failed already?
129 
130  // log info so user can troubleshoot
131  dir = wxGetCwd(); // read in the actual working dir
132  #if wxCHECK_VERSION(3, 0, 0)
133  Manager::Get()->GetLogManager()->Log(F(_("Launching tool '%s': %s (in %s)"), tool->GetName().wx_str(), cmdline.wx_str(), dir.wx_str()));
134  #else
135  Manager::Get()->GetLogManager()->Log(F(_("Launching tool '%s': %s (in %s)"), tool->GetName().c_str(), cmdline.c_str(), dir.c_str()));
136  #endif
137 
138  bool pipe = true;
139  int flags = wxEXEC_ASYNC;
140 
141  switch (tool->GetLaunchOption())
142  {
144  pipe = false; // no need to pipe output channels...
145  break;
146 
149  flags |= wxEXEC_NOHIDE;
150  pipe = false;
151  break;
152 
153  case cbTool::LAUNCH_HIDDEN: // fall-through
154  default:
155  break; // use the default values of pipe and flags...
156  }
157 
159  {
160  int pid = wxExecute(cmdline, flags);
161 
162  if (!pid)
163  {
164  cbMessageBox(_("Couldn't execute tool. Check the log for details."), _("Error"), wxICON_ERROR);
165  return false;
166  }
167  else
168  {
170  Manager::Get()->ProcessEvent(evtSwitch); // switch to default log
171  }
172  }
173  else
174  {
175  m_pProcess = new PipedProcess(&m_pProcess, this, idToolProcess, pipe, dir);
176  m_Pid = wxExecute(cmdline, flags, m_pProcess);
177 
178  if (!m_Pid)
179  {
180  cbMessageBox(_("Couldn't execute tool. Check the log for details."), _("Error"), wxICON_ERROR);
181  delete m_pProcess;
182  m_pProcess = nullptr;
183  m_Pid = 0;
184  return false;
185  }
186  else
187  {
189  Manager::Get()->ProcessEvent(evtSwitch); // switch to default log
190  }
191  }
192 
193  return true;
194 } // end of Execute
195 
196 void ToolsManager::AddTool(const cbTool* tool, bool save)
197 {
198  if (tool)
199  {
200  InsertTool(m_Tools.GetCount(), tool, save);
201  }
202 } // end of AddTool
203 
204 void ToolsManager::InsertTool(int position, const cbTool* tool, bool save)
205 {
206  m_Tools.Insert(position, new cbTool(*tool));
207  if (save)
208  {
209  SaveTools();
210  }
211 } // end of InsertTool
212 
214 {
215  int idx = 0;
216  for (ToolsList::iterator it = m_Tools.begin(); it != m_Tools.end(); ++it)
217  {
218  if (idx == index)
219  {
220  m_Tools.erase(it);
221  SaveTools();
222  return;
223  }
224  ++idx;
225  }
226 }
227 
229 {
230  for (ToolsList::iterator it = m_Tools.begin(); it != m_Tools.end(); ++it)
231  {
232  cbTool* tool = *it;
233  if (tool->GetMenuId() == id)
234  return tool;
235  }
236  return nullptr;
237 }
238 
240 {
241  int idx = 0;
242  for (ToolsList::iterator it = m_Tools.begin(); it != m_Tools.end(); ++it)
243  {
244  cbTool* tool = *it;
245  if (idx == index)
246  return tool;
247  ++idx;
248  }
249  return nullptr;
250 }
251 
253 {
254  ConfigManager* cfg = Manager::Get()->GetConfigManager(_T("tools"));
255  wxArrayString list = cfg->EnumerateSubPaths(_("/"));
256  for (unsigned int i = 0; i < list.GetCount(); ++i)
257  {
258  cbTool tool;
259  tool.SetName( cfg->Read(_T("/") + list[i] + _T("/name")));
260  if (tool.GetName().IsEmpty())
261  continue;
262  tool.SetCommand(cfg->Read(_T("/") + list[i] + _T("/command")));
263  if (tool.GetCommand().IsEmpty())
264  continue;
265  tool.SetParams(cfg->Read(_T("/") + list[i] + _T("/params")));
266  tool.SetWorkingDir(cfg->Read(_T("/") + list[i] + _T("/workingDir")));
267  tool.SetLaunchOption(static_cast<cbTool::eLaunchOption>(cfg->ReadInt(_T("/") + list[i] + _T("/launchOption"))));
268 
269  AddTool(&tool, false);
270  }
271  Manager::Get()->GetLogManager()->Log(F(_("Configured %d tools"), m_Tools.GetCount()));
272 }
273 
275 {
276  ConfigManager* cfg = Manager::Get()->GetConfigManager(_T("tools"));
277  wxArrayString list = cfg->EnumerateSubPaths(_("/"));
278  for (unsigned int i = 0; i < list.GetCount(); ++i)
279  {
280  cfg->DeleteSubPath(list[i]);
281  }
282 
283  int count = 0;
284  for (ToolsList::iterator it = m_Tools.begin(); it != m_Tools.end(); ++it)
285  {
286  cbTool* tool = *it;
287  wxString elem;
288 
289  // prepend a 0-padded 2-digit number to keep ordering
290  wxString tmp;
291  tmp.Printf(_T("tool%2.2d"), count++);
292 
293  elem << _T("/") << tmp << _T("/");
294  cfg->Write(elem + _T("name"), tool->GetName());
295  cfg->Write(elem + _T("command"), tool->GetCommand());
296  cfg->Write(elem + _T("params"), tool->GetParams());
297  cfg->Write(elem + _T("workingDir"), tool->GetWorkingDir());
298  cfg->Write(elem + _T("launchOption"), static_cast<int>(tool->GetLaunchOption()));
299  }
300 }
301 
303 {
304  // clear previously added menu items
306 
307  // add menu items for tools
308  m_Menu = menu;
309  if (m_Menu->GetMenuItemCount() > 0)
310  {
311  m_ItemsManager.Add(menu, wxID_SEPARATOR, _T(""), _T(""));
312  }
313 
314  for (ToolsList::iterator it = m_Tools.begin(); it != m_Tools.end(); ++it)
315  {
316  cbTool* tool = *it;
317  if (tool->GetName() == CB_TOOLS_SEPARATOR)
318  {
319  m_ItemsManager.Add(menu, wxID_SEPARATOR, _T(""), _T(""));
320  continue;
321  }
322  if (tool->GetMenuId() == -1)
323  {
324  tool->SetMenuId(wxNewId());
325  }
326  m_ItemsManager.Add(menu, tool->GetMenuId(), tool->GetName(), tool->GetName());
327  Connect(tool->GetMenuId(), -1, wxEVT_COMMAND_MENU_SELECTED,
328  (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction)
330  }
331 
332  if (m_Tools.GetCount() > 0)
333  {
334  m_ItemsManager.Add(menu, wxID_SEPARATOR, _T(""), _T(""));
335  }
336  m_ItemsManager.Add(menu, idToolsConfigure, _("&Configure tools..."), _("Add/remove user-defined tools"));
337 }
338 
340 {
342  Manager::Get()->ProcessEvent(event);
343 
344  ConfigureToolsDlg dlg(Manager::Get()->GetAppWindow());
345  PlaceWindow(&dlg);
346  dlg.ShowModal();
347  SaveTools();
349 
351  Manager::Get()->ProcessEvent(event2);
352 
353  return 0;
354 } // end of Configure
355 
356 // events
357 
359 {
360  Configure();
361 }
362 
364 {
365  cbTool* tool = GetToolByMenuId(event.GetId());
366  if (!Execute(tool))
367  cbMessageBox(_("Could not execute ") + tool->GetName());
368 }
369 
371 {
372  if (m_pProcess)
373  {
374  if (m_pProcess->HasInput())
375  {
376  event.RequestMore();
377  }
378  }
379  else
380  event.Skip();
381 }
382 
384 {
385  Manager::Get()->GetLogManager()->Log(_T("stdout> ") + event.GetString());
386 }
387 
389 {
390  Manager::Get()->GetLogManager()->Log(_T("stderr> ") + event.GetString());
391 }
392 
394 {
395  m_Pid = 0;
396  m_pProcess = nullptr;
397 
398  Manager::Get()->GetLogManager()->Log(F(_T("Tool execution terminated with status %d"), event.GetInt()));
399 }
wxString F(const wxChar *msg,...)
sprintf-like function
Definition: logmanager.h:20
void RemoveToolByIndex(int index)
int wxNewId()
bool Execute(const cbTool *tool)
PipedProcess * m_pProcess
Definition: toolsmanager.h:55
ConfigManager * GetConfigManager(const wxString &name_space) const
Definition: manager.cpp:474
WX_DEFINE_LIST(ToolsList)
int ReadInt(const wxString &name, int defaultVal=0)
cbTool * GetToolByMenuId(int id)
static Manager * Get()
Use Manager::Get() to get a pointer to its instance Manager::Get() is guaranteed to never return an i...
Definition: manager.cpp:182
DLLIMPORT const wxString DEFAULT_CONSOLE_TERM
Definition: globals.cpp:59
#define wxICON_ERROR
Definition: cbtool.h:13
bool wxFileExists(const wxString &filename)
eLaunchOption GetLaunchOption() const
Definition: cbtool.h:30
wxString GetParams() const
Definition: cbtool.h:28
MenuItemsManager m_ItemsManager
Definition: toolsmanager.h:53
wxString GetName() const
Definition: cbtool.h:26
void OnIdle(wxIdleEvent &event)
void OnToolClick(wxCommandEvent &event)
Event used to request from the main app to add a log.
Definition: sdk_events.h:182
wxCStrData c_str() const
const int idToolsConfigure
void OnToolErrOutput(CodeBlocksEvent &event)
ToolsList m_Tools
Definition: toolsmanager.h:52
#define _T(string)
~ToolsManager() override
int GetMenuId() const
Definition: cbtool.h:31
wxString GetCommand() const
Definition: cbtool.h:27
void ReleaseMenu(wxMenuBar *menuBar)
void BuildToolsMenu(wxMenu *menu)
#define wxT(string)
size_t GetMenuItemCount() const
A generic Code::Blocks event.
Definition: sdk_events.h:20
wxWindow * GetAppWindow() const
Definition: manager.cpp:424
void Write(const wxString &name, const wxString &value, bool ignoreEmpty=false)
bool wxSetWorkingDirectory(const wxString &dir)
void RecalcVars(cbProject *project, EditorBase *editor, ProjectBuildTarget *target)
void AddTool(const cbTool *tool, bool save=true)
wxArrayString EnumerateSubPaths(const wxString &path)
#define EVT_PIPEDPROCESS_TERMINATED(id, fn)
Definition: sdk_events.h:366
size_t Replace(const wxString &strOld, const wxString &strNew, bool replaceAll=true)
void SetWorkingDir(const wxString &WorkingDir)
Definition: cbtool.h:36
void OnConfigure(wxCommandEvent &event)
#define CONSOLE_RUNNER
LogManager * GetLogManager() const
Definition: manager.cpp:439
EVTIMPORT const wxEventType cbEVT_SWITCH_TO_LOG_WINDOW
Definition: sdk_events.cpp:165
wxString Read(const wxString &key, const wxString &defaultVal=wxEmptyString)
const wxStringCharType * wx_str() const
virtual int ShowModal()
void OnToolTerminated(CodeBlocksEvent &event)
virtual void Clear()
Clear all managed menu items.
void OnToolStdOutput(CodeBlocksEvent &event)
void SetMenuId(int MenuId)
Definition: cbtool.h:38
Definition: manager.h:183
MacrosManager * GetMacrosManager() const
Definition: manager.cpp:454
const wxString & _(const wxString &string)
void ReplaceMacros(wxString &buffer, ProjectBuildTarget *target=nullptr, bool subrequest=false)
void InsertTool(int position, const cbTool *tool, bool save=true)
wxString GetWorkingDir() const
Definition: cbtool.h:29
wxMenu * m_Menu
Definition: toolsmanager.h:54
EVTIMPORT const wxEventType cbEVT_MENUBAR_CREATE_END
Definition: sdk_events.cpp:146
#define EVT_PIPEDPROCESS_STDERR(id, fn)
Definition: sdk_events.h:363
void SetLaunchOption(eLaunchOption LaunchOption)
Definition: cbtool.h:37
void DeleteSubPath(const wxString &strPath)
bool IsEmpty() const
DLLIMPORT void PlaceWindow(wxTopLevelWindow *w, cbPlaceDialogMode mode=pdlBest, bool enforce=false)
Definition: globals.cpp:1177
virtual bool HasInput()
void Log(const wxString &msg, int i=app_log, Logger::level lv=Logger::info)
Definition: logmanager.h:140
static wxString GetExecutableFolder()
#define EVT_PIPEDPROCESS_STDOUT(id, fn)
Definition: sdk_events.h:360
bool ProcessEvent(CodeBlocksEvent &event)
Definition: manager.cpp:246
#define CB_TOOLS_SEPARATOR
Definition: cbtool.h:11
void SetCommand(const wxString &Command)
Definition: cbtool.h:34
void SetParams(const wxString &Params)
Definition: cbtool.h:35
const int idToolProcess
EVTIMPORT const wxEventType cbEVT_MENUBAR_CREATE_BEGIN
Definition: sdk_events.cpp:144
size_t GetCount() const
void CreateMenu(wxMenuBar *menuBar)
cbTool * GetToolByIndex(int index)
int Printf(const wxString &pszFormat,...)
void SetName(const wxString &Name)
Definition: cbtool.h:33
DLLIMPORT int cbMessageBox(const wxString &message, const wxString &caption=wxEmptyString, int style=wxOK, wxWindow *parent=NULL, int x=-1, int y=-1)
wxMessageBox wrapper.
Definition: globals.cpp:1395
virtual wxMenuItem * Add(wxMenu *parent, int id, const wxString &caption, const wxString &helptext)
Add a menu item.
long wxExecute(const wxString &command, int flags=wxEXEC_ASYNC, wxProcess *callback=NULL, const wxExecuteEnv *env=NULL)
wxString wxGetCwd()