Code::Blocks  SVN r11506
pluginmanager.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: 11354 $
6  * $Id: pluginmanager.cpp 11354 2018-03-31 21:50:05Z fuscated $
7  * $HeadURL: https://svn.code.sf.net/p/codeblocks/code/trunk/src/sdk/pluginmanager.cpp $
8  */
9 
10 #include "sdk_precomp.h"
11 
12 #ifndef CB_PRECOMP
13  #include <wx/dir.h>
14  #include <wx/filesys.h>
15  #include <wx/intl.h>
16  #include <wx/menu.h>
17  #include <wx/string.h>
18 
19  #include "pluginmanager.h"
20  #include "cbexception.h"
21  #include "cbplugin.h"
22  #include "infowindow.h"
23  #include "logmanager.h"
24  #include "macrosmanager.h"
25  #include "manager.h"
26  #include "editormanager.h"
27  #include "configmanager.h"
28  #include "personalitymanager.h"
29  #include "scriptingmanager.h"
30  #include "globals.h"
31  #include "sdk_events.h"
32 #endif
33 
34 #include <algorithm>
35 
36 #include <wx/dynlib.h>
37 #include <wx/filesys.h>
38 #include <wx/progdlg.h>
39 #include <wx/utils.h>
40 #include <wx/filename.h>
41 
42 #include <wx/wfstream.h>
43 #include <wx/zipstrm.h>
44 #include <wx/txtstrm.h>
45 
46 #include "filefilters.h"
47 #include <tinyxml.h>
48 
49 #include "annoyingdialog.h"
51 
53 
54 template<> PluginManager* Mgr<PluginManager>::instance = nullptr;
55 template<> bool Mgr<PluginManager>::isShutdown = false;
56 
57 inline void VersionStringToNumbers(const wxString& version, long* major, long* minor, long* release)
58 {
59  wxString majorS = version.BeforeFirst(_T('.')); // 6.3.2 -> 6
60  wxString minorS = version.AfterFirst(_T('.')); // 6.3.2 -> 3.2
61  wxString releaseS = version.AfterLast(_T('.')); // 6.3.2 -> 2
62  minorS = minorS.BeforeFirst(_T('.')); // 3.2 -> 3
63  if (major)
64  majorS.ToLong(major);
65  if (minor)
66  minorS.ToLong(minor);
67  if (release)
68  releaseS.ToLong(release);
69 }
70 
71 // returns -1 if new is less then old, 0 if equal and 1 if new is greater than old
72 inline int CompareVersions(const wxString& new_version, const wxString& old_version)
73 {
74  long new_major, new_minor, new_release;
75  long old_major, old_minor, old_release;
76 
77  VersionStringToNumbers(new_version, &new_major, &new_minor, &new_release);
78  VersionStringToNumbers(old_version, &old_major, &old_minor, &old_release);
79 
80 #define SIGN(a) (a>0?1:(a<0?-1:0))
81  int result = 0;
82  result += SIGN(new_major - old_major) << 2;
83  result += SIGN(new_minor - old_minor) << 1;
84  result += SIGN(new_release - old_release) << 0;
85 #undef SIGN
86 
87  if (result < 0) return -1;
88  else if (result > 0) return 1;
89  return 0;
90 }
91 
92 namespace LibLoader
93 {
95  {
98  int ref;
99  };
100  typedef std::map<wxString, RefCountedLib> Libs;
101  Libs s_Libs;
102 
103  inline wxDynamicLibrary* LoadLibrary(const wxString& filename)
104  {
105  Libs::iterator it = s_Libs.find(filename);
106  if (it != s_Libs.end())
107  {
108  // existing lib./codeblocks
109  it->second.ref++;
110  return it->second.lib;
111  }
112  // new lib
113  it = s_Libs.insert(s_Libs.end(), std::make_pair(filename, RefCountedLib()));
114  it->second.lib = new wxDynamicLibrary;
115  it->second.ref = 1;
116  it->second.lib->Load(filename);
117  return it->second.lib;
118  }
119 
121  {
122  Libs::iterator it;
123  for (it = s_Libs.begin(); it != s_Libs.end(); ++it)
124  {
125  RefCountedLib& rcl = it->second;
126  if (rcl.lib == lib)
127  {
128  // found
129  rcl.ref--;
130  if (rcl.ref == 0)
131  {
132  // only delete the lib if not shutting down
133  // if we are shutting down, it will be deleted automatically
135  delete rcl.lib;
136  s_Libs.erase(it);
137  }
138  return;
139  }
140  }
141  // if we reached here, it's a lib that was not handled by us
142  // (or had wrong refcounting)
143  }
144 
145  inline void Cleanup()
146  {
147  Libs::iterator it;
148  for (it = s_Libs.begin(); it != s_Libs.end(); ++it)
149  {
150  RefCountedLib& rcl = it->second;
151  // only delete the lib if not shutting down
152  // if we are shutting down, it will be deleted automatically
154  delete rcl.lib;
155  }
156  s_Libs.clear();
157  }
158 };
159 
160 //static
161 bool PluginManager::s_SafeMode = false;
162 
163 BEGIN_EVENT_TABLE(PluginManager, wxEvtHandler)
164 //
165 END_EVENT_TABLE()
166 
167 // class constructor
169  : m_pCurrentlyLoadingLib(nullptr),
170  m_pCurrentlyLoadingManifestDoc(nullptr)
171 {
172  Manager::Get()->GetAppWindow()->PushEventHandler(this);
173 }
174 
175 // class destructor
177 {
178  UnloadAllPlugins();
179 }
180 
181 void PluginManager::CreateMenu(cb_unused wxMenuBar* menuBar)
182 {
183 }
184 
185 void PluginManager::ReleaseMenu(cb_unused wxMenuBar* menuBar)
186 {
187 }
188 
189 bool PluginManager::AttachPlugin(cbPlugin* plugin, bool ignoreSafeMode)
190 {
191  if (!plugin)
192  return false;
193  if (plugin->IsAttached())
194  return true;
195 
196  if (!s_SafeMode || ignoreSafeMode)
197  plugin->Attach();
198  return true;
199 }
200 
202 {
203  if (!plugin)
204  return false;
205  if (!plugin->IsAttached())
206  return true;
207 
210  return true;
211 }
212 
213 bool PluginManager::InstallPlugin(const wxString& pluginName, bool forAllUsers, bool askForConfirmation)
214 {
215  if (pluginName.IsEmpty())
216  return false;
217 
218  wxString actualName = pluginName;
219  Manager::Get()->GetMacrosManager()->ReplaceMacros(actualName);
220 
221  // base name
222  wxString basename = wxFileName(actualName).GetName();
224  if (basename.Contains(_T('-')))
225  {
226  version = basename.AfterFirst(_T('-'));
227  basename = basename.BeforeFirst(_T('-'));
228  }
229 
230 // Manager::Get()->GetLogManager()->DebugLog(F(_T("InstallPlugin: basename='%s', version=%s"), basename.c_str(), version.c_str()));
231 
232  // if plugin with the same name exists, ask to uninstall first
233  cbPlugin* existingPlugin = FindPluginByName(basename);
234  if (existingPlugin)
235  {
236  if (askForConfirmation)
237  {
238  wxString msg = _("A plugin with the same name is already installed.\n");
239  if (!version.IsEmpty())
240  {
241  const PluginInfo* existingInfo = GetPluginInfo(existingPlugin);
242  if (CompareVersions(version, existingInfo->version) < 0)
243  {
244  msg = _("The plugin you are trying to install, is older "
245  "than the one currently installed.");
246  }
247  }
248 
249  if (cbMessageBox(msg + _T('\n') +
250  _("If you want to proceed, the installed plugin will be "
251  "uninstalled first.\n"
252  "Do you want to proceed?"),
253  _("Confirmation"), wxICON_QUESTION | wxYES_NO) == wxID_NO)
254  {
255  return false;
256  }
257  }
258  if (!UninstallPlugin(existingPlugin))
259  return false;
260  }
261 
262  wxString pluginDir;
263  wxString resourceDir;
264  if (forAllUsers)
265  {
267  resourceDir = ConfigManager::GetFolder(sdDataGlobal);
268  }
269  else
270  {
272  resourceDir = ConfigManager::GetFolder(sdDataUser);
273  }
274 
275  wxProgressDialog pd(_("Installing: ") + basename, _T("A description wide enough for the dialog ;)"), 5);
276 
277  wxString localName = basename + FileFilters::DYNAMICLIB_DOT_EXT;
278  wxString resourceName = basename + _T(".zip");
279  wxString settingsOnName = basename + _T(".png");
280  wxString settingsOffName = basename + _T("-off.png");
281  if (!platform::windows && resourceName.StartsWith(_T("lib")))
282  resourceName.Remove(0, 3);
283  if (!platform::windows && settingsOnName.StartsWith(_T("lib")))
284  settingsOnName.Remove(0, 3);
285  if (!platform::windows && settingsOffName.StartsWith(_T("lib")))
286  settingsOffName.Remove(0, 3);
287  wxString pluginFilename = UnixFilename(pluginDir + _T('/') + localName);
288 // Manager::Get()->GetLogManager()->DebugLog(F(_T("Plugin filename: ") + pluginFilename));
289 // Manager::Get()->GetLogManager()->DebugLog(F(_T("Plugin resources: ") + ConfigManager::GetDataFolder() + _T('/') + resourceName));
290 
291  pd.Update(1, _("Extracting plugin"));
292 
293  // extract plugin from bundle
294  if (!ExtractFile(actualName,
295  localName,
296  pluginFilename))
297  return false;
298 // Manager::Get()->GetLogManager()->DebugLog(F(_T("Extracted plugin")));
299 
300  pd.Update(2, _("Extracting plugin resources"));
301 
302  // extract resources from bundle
303  if (!ExtractFile(actualName,
304  resourceName,
305  resourceDir + _T('/') + resourceName))
306  return false;
307 // Manager::Get()->GetLogManager()->DebugLog(F(_T("Extracted resources")));
308 
309  pd.Update(3, _("Extracting plugin icons for \"Settings\" dialog"));
310 
311  // extract resources from bundle
312  ExtractFile(actualName,
313  settingsOnName,
314  resourceDir + _T("/images/settings/") + settingsOnName,
315  false);
316 // Manager::Get()->GetLogManager()->DebugLog(F(_T("Extracted resources")));
317 
318  // extract resources from bundle
319  ExtractFile(actualName,
320  settingsOffName,
321  resourceDir + _T("/images/settings/") + settingsOffName,
322  false);
323 // Manager::Get()->GetLogManager()->DebugLog(F(_T("Extracted resources")));
324 
325  // extract extra files
326  wxArrayString extraFiles;
327  ReadExtraFilesFromManifestFile(localName, extraFiles);
328  for (size_t i = 0; i < extraFiles.GetCount(); ++i)
329  {
330  ExtractFile(actualName,
331  extraFiles[i],
332  resourceDir + _T("/") + extraFiles[i],
333  false);
334  }
335 
336  pd.Update(4, _("Loading plugin"));
337 
338  // bundle extracted; now load the plugin on-the-fly
339 // Manager::Get()->GetLogManager()->DebugLog(F(_T("Loading plugin...")));
340  ScanForPlugins(pluginDir);
341  LoadAllPlugins();
342  cbPlugin* plugin = FindPluginByFileName(pluginFilename);
343  const PluginInfo* info = GetPluginInfo(plugin);
344  if (!plugin || !info)
345  {
346  Manager::Get()->GetLogManager()->DebugLog(_T("Failed"));
347  return false;
348  }
349 // Manager::Get()->GetLogManager()->DebugLog(F(_T("Succeeded")));
350 
351  // inform app to update menus and toolbars
352  pd.Update(5, _("Updating menus and toolbars"));
354  evt.SetPlugin(plugin);
355  Manager::Get()->ProcessEvent(evt);
356 // Manager::Get()->GetLogManager()->DebugLog(F(_T("Menus updated")));
357 
358  return true;
359 }
360 
361 bool PluginManager::UninstallPlugin(cbPlugin* plugin, bool removeFiles)
362 {
363  if (!plugin)
364  return false;
365 
366  wxString title;
367  wxString pluginFilename;
368  wxString resourceFilename;
369  wxString settingsOnFilename;
370  wxString settingsOffFilename;
371  wxArrayString extrafiles;
372 
373  // find the plugin element
374  for (size_t i = 0; i < m_Plugins.GetCount(); ++i)
375  {
376  PluginElement* elem = m_Plugins[i];
377  if (elem && elem->plugin == plugin)
378  {
379  // got it
380  title = elem->info.title;
381  pluginFilename = elem->fileName;
382  // now get the resource name
383  wxFileName fname(pluginFilename);
384  resourceFilename = fname.GetName() + _T(".zip");
385  settingsOnFilename = fname.GetName() + _T(".png");
386  settingsOffFilename = fname.GetName() + _T("-off.png");
387  if (!platform::windows && resourceFilename.StartsWith(_T("lib")))
388  resourceFilename.Remove(0, 3);
389  if (!platform::windows && settingsOnFilename.StartsWith(_T("lib")))
390  settingsOnFilename.Remove(0, 3);
391  if (!platform::windows && settingsOffFilename.StartsWith(_T("lib")))
392  settingsOffFilename.Remove(0, 3);
393  resourceFilename = ConfigManager::LocateDataFile(resourceFilename, sdDataGlobal | sdDataUser);
394  settingsOnFilename = ConfigManager::LocateDataFile(_T("images/settings/") + settingsOnFilename, sdDataGlobal | sdDataUser);
395  settingsOffFilename = ConfigManager::LocateDataFile(_T("images/settings/") + settingsOffFilename, sdDataGlobal | sdDataUser);
396 
397  ReadExtraFilesFromManifestFile(resourceFilename, extrafiles);
398  for (size_t n = 0; n < extrafiles.GetCount(); ++n)
399  {
400  extrafiles[n] = ConfigManager::LocateDataFile(extrafiles[n], sdDataGlobal | sdDataUser);
401  }
402  break;
403  }
404  }
405 
406  if (wxFileExists(pluginFilename) && !wxFile::Access(pluginFilename, wxFile::write))
407  {
408  // no write-access; abort
409  cbMessageBox(_("You don't have the needed privileges to uninstall this plugin.\n"
410  "Ask your administrator to uninstall this plugin for you..."),
411  _("Warning"), wxICON_WARNING);
412  return false;
413  }
414 
415 // Manager::Get()->GetLogManager()->DebugLog(F(_T("UninstallPlugin:")));
416 // Manager::Get()->GetLogManager()->DebugLog(F(_T("Plugin filename: ") + pluginFilename));
417 // Manager::Get()->GetLogManager()->DebugLog(F(_T("Plugin resources: ") + resourceFilename));
418 
419  wxProgressDialog pd(wxString::Format(_("Uninstalling %s"), title.c_str()),
420  _T("A description wide enough for the dialog ;)"), 3);
421 
422  pd.Update(1, _("Detaching plugin"));
423  DetachPlugin(plugin);
424 // Manager::Get()->GetLogManager()->DebugLog(F(_T("Plugin released")));
425 
426  pd.Update(2, _("Updating menus and toolbars"));
428  event.SetPlugin(plugin);
429  Manager::Get()->ProcessEvent(event);
430 // Manager::Get()->GetLogManager()->DebugLog(F(_T("Menus updated")));
431 
432  pd.Update(3, _("Unloading plugin"));
433  UnloadPlugin(plugin);
434 // Manager::Get()->GetLogManager()->DebugLog(F(_T("Plugin unloaded")));
435 
436  if (!removeFiles)
437  return true;
438 
439  // under linux, if the progress dialog is still visible and updated
440  // causes a crash because it re-enters gtk_main_iteration() calling
441  // eventually OnUpdateUI() in the config dialog, which in turn references
442  // an invalid plugin...
443 // pd.Update(4, _("Removing files"));
444 
445  if (!pluginFilename.IsEmpty())
446  {
447  if (wxRemoveFile(pluginFilename))
448  {
449 // Manager::Get()->GetLogManager()->DebugLog(F(_T("Plugin file removed")));
450  if (!resourceFilename.IsEmpty())
451  {
452  if (!wxRemoveFile(resourceFilename))
453  Manager::Get()->GetLogManager()->LogWarning(_T("Failed to remove plugin resources: ") + resourceFilename);
454  }
455  if (!settingsOnFilename.IsEmpty() && wxFileExists(settingsOnFilename))
456  {
457  if (!wxRemoveFile(settingsOnFilename))
458  Manager::Get()->GetLogManager()->LogWarning(_T("Failed to remove icon for \"Settings\" dialog: ") + settingsOnFilename);
459  }
460  if (!settingsOffFilename.IsEmpty() && wxFileExists(settingsOffFilename))
461  {
462  if (!wxRemoveFile(settingsOffFilename))
463  Manager::Get()->GetLogManager()->LogWarning(_T("Failed to remove icon for \"Settings\" dialog: ") + settingsOffFilename);
464  }
465  for (size_t i = 0; i < extrafiles.GetCount(); ++i)
466  {
467  if (!extrafiles[i].IsEmpty() && wxFileExists(extrafiles[i]))
468  {
469  if (!wxRemoveFile(extrafiles[i]))
470  Manager::Get()->GetLogManager()->LogWarning(_T("Failed to remove extra file: ") + extrafiles[i]);
471  }
472  }
473  return true;
474  }
475  else
476  {
477  Manager::Get()->GetLogManager()->LogWarning(_T("Failed to remove plugin file: ") + pluginFilename);
478  cbMessageBox(_("Plugin could not be completely uninstalled because its files could not be removed.\n\n"
479  "This can happen if the plugin's file is in-use like, for "
480  "example, when the same plugin file provides more than one "
481  "plugin.\n"
482  "In this case either uninstall all other plugins "
483  "which are provided by the same file, or remove it yourself "
484  "(manually) when you shut down Code::Blocks.\n"
485  "The files that could not be deleted are:\n\n") +
486  pluginFilename + _T('\n') +
487  resourceFilename + _T('\n') +
488  settingsOnFilename + _T('\n') +
489  settingsOffFilename,
490  _("Warning"), wxICON_WARNING);
491  return false;
492  }
493  }
494  return false;
495 }
496 
497 bool PluginManager::ExportPlugin(cbPlugin* plugin, const wxString& filename)
498 {
499  if (!plugin)
500  return false;
501 
502  wxArrayString sourcefiles;
503  wxArrayString extrafiles;
504  wxArrayString extrafilesdest;
505  wxFileName fname;
506  wxString resourceFilename;
507 
508  // find the plugin element
509  for (size_t i = 0; i < m_Plugins.GetCount(); ++i)
510  {
511  PluginElement* elem = m_Plugins[i];
512  if (elem && elem->plugin == plugin)
513  {
514  // got it
515 
516  // plugin file
517  sourcefiles.Add(elem->fileName);
518  fname.Assign(elem->fileName);
519 
520  // now get the resource zip filename
521  resourceFilename = fname.GetName() + _T(".zip");
522  if (!platform::windows && resourceFilename.StartsWith(_T("lib")))
523  resourceFilename.Remove(0, 3);
524  resourceFilename = ConfigManager::LocateDataFile(resourceFilename, sdDataGlobal | sdDataUser);
525  sourcefiles.Add(resourceFilename);
526 
527  // the highlighted icon the plugin may have for its "settings" page
528  resourceFilename = fname.GetName() + _T(".png");
529  if (!platform::windows && resourceFilename.StartsWith(_T("lib")))
530  resourceFilename.Remove(0, 3);
531  resourceFilename.Prepend(_T("images/settings/"));
532  resourceFilename = ConfigManager::LocateDataFile(resourceFilename, sdDataGlobal | sdDataUser);
533  if (!resourceFilename.IsEmpty())
534  sourcefiles.Add(resourceFilename);
535 
536  // the non-highlighted icon the plugin may have for its "settings" page
537  resourceFilename = fname.GetName() + _T("-off.png");
538  if (!platform::windows && resourceFilename.StartsWith(_T("lib")))
539  resourceFilename.Remove(0, 3);
540  resourceFilename.Prepend(_T("images/settings/"));
541  resourceFilename = ConfigManager::LocateDataFile(resourceFilename, sdDataGlobal | sdDataUser);
542  if (!resourceFilename.IsEmpty())
543  sourcefiles.Add(resourceFilename);
544 
545  // export extra files
546  resourceFilename = fname.GetName() + _T(".zip");
547  if (!platform::windows && resourceFilename.StartsWith(_T("lib")))
548  resourceFilename.Remove(0, 3);
549  ReadExtraFilesFromManifestFile(resourceFilename, extrafilesdest);
550  for (size_t n = 0; n < extrafilesdest.GetCount(); ++n)
551  {
552  extrafiles.Add(ConfigManager::LocateDataFile(extrafilesdest[n], sdDataGlobal | sdDataUser));
553  }
554 
555  break;
556  }
557  }
558 
559  if (wxFileExists(filename))
560  {
561  if (!wxFile::Access(filename, wxFile::write))
562  {
563  cbMessageBox(wxString::Format(_("%s is in use.\nAborting..."), filename.c_str()),
564  _("Warning"), wxICON_WARNING);
565  return false;
566  }
567  }
568 
569 // Manager::Get()->GetLogManager()->DebugLog(F(_T("Creating archive: ") + filename));
570  wxFileOutputStream out(filename);
571  wxZipOutputStream zip(out, 9); // max compression
572  for (size_t i = 0; i < sourcefiles.GetCount(); ++i)
573  {
574  if (sourcefiles[i].IsEmpty())
575  continue;
576 
577  wxFileInputStream in(sourcefiles[i]);
578  zip.PutNextEntry(wxFileName(sourcefiles[i]).GetFullName());
579  zip << in;
580  }
581  for (size_t i = 0; i < extrafiles.GetCount(); ++i)
582  {
583  if (extrafiles[i].IsEmpty() || extrafilesdest[i].IsEmpty())
584  continue;
585 
586  wxFileInputStream in(extrafiles[i]);
587 
588  zip.PutNextEntry(extrafilesdest[i]);
589  zip << in;
590  }
591  zip.SetComment(_T("This is a redistributable plugin for the Code::Blocks IDE.\n"
592  "See http://www.codeblocks.org for details..."));
593 
594  return true;
595 }
596 
597 bool PluginManager::ExtractFile(const wxString& bundlename,
598  const wxString& src_filename,
599  const wxString& dst_filename,
600  bool isMandatory)
601 {
602 // Manager::Get()->GetLogManager()->DebugLog(F(_T("ExtractFile:")));
603 // Manager::Get()->GetLogManager()->DebugLog(F(_T("Plugin filename: ") + bundlename));
604 // Manager::Get()->GetLogManager()->DebugLog(F(_T("Source filename: ") + src_filename));
605 // Manager::Get()->GetLogManager()->DebugLog(F(_T("Destination filename: ") + dst_filename));
606 
607  // check if the destination file already exists
608  if (wxFileExists(dst_filename) && !wxFile::Access(dst_filename, wxFile::write))
609  {
610 // Manager::Get()->GetLogManager()->DebugLog(F(_T("Destination file in use")));
611  cbMessageBox(_("The destination file is in use.\nAborting..."), _("Warning"), wxICON_WARNING);
612  return false;
613  }
614 
615  // make sure destination dir exists
616  CreateDirRecursively(wxFileName(dst_filename).GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR));
617 
618  // actually extract file
619 // Manager::Get()->GetLogManager()->DebugLog(F(_T("Extracting...")));
620  wxFileSystem* fs = new wxFileSystem;
621  wxFSFile* f = fs->OpenFile(bundlename + _T("#zip:") + src_filename);
622  if (f)
623  {
624  // open output file for writing
625  wxFile output(dst_filename, wxFile::write);
626  if (!output.IsOpened())
627  {
628 // Manager::Get()->GetLogManager()->DebugLog(F(_T("Can't open destination file for writing")));
629  wxString msg = wxString::Format(_("Can't open destination file '%s' for writing..."),
630  dst_filename.c_str());
631  cbMessageBox(msg, _("Error"), wxICON_ERROR);
632  delete f;
633  delete fs;
634  return false;
635  }
636 
637  // copy file
638  wxInputStream* is = f->GetStream();
639  char tmp[1025] = {};
640  while (!is->Eof() && is->CanRead())
641  {
642  memset(tmp, 0, sizeof(tmp));
643  is->Read(tmp, sizeof(tmp) - 1);
644  output.Write(tmp, is->LastRead());
645  }
646  delete f;
647 // Manager::Get()->GetLogManager()->DebugLog(F(_T("Extracted")));
648  }
649  else
650  {
651 // Manager::Get()->GetLogManager()->DebugLog(F(_T("File not found in plugin")));
652  if (isMandatory)
653  {
654  wxString msg = wxString::Format(_("File '%s' not found in plugin '%s'"),
655  src_filename.c_str(), bundlename.c_str());
656  cbMessageBox(msg, _("Error"), wxICON_ERROR);
657  delete fs;
658  return false;
659  }
660  }
661  delete fs;
662  return true;
663 }
664 
666  CreatePluginProc createProc,
667  FreePluginProc freeProc,
668  PluginSDKVersionProc versionProc)
669 {
670  // sanity checks
671  if (name.IsEmpty() || !createProc || !freeProc || !versionProc)
672  return;
673 
674  // first check to see it's not already loaded
675  if (FindPluginByName(name))
676  return; // yes, already loaded
677 
678  // read manifest file for plugin
679  PluginInfo info;
680  if (!ReadManifestFile(m_CurrentlyLoadingFilename, name, &info) ||
681  info.name.IsEmpty())
682  {
683  Manager::Get()->GetLogManager()->LogError(_T("Invalid manifest file for: ") + name);
684  return;
685  }
686 
687  // now get the SDK version number (extra check)
688  int major;
689  int minor;
690  int release;
691  versionProc(&major, &minor, &release);
692  if (major != PLUGIN_SDK_VERSION_MAJOR ||
693  minor != PLUGIN_SDK_VERSION_MINOR ||
694  release != PLUGIN_SDK_VERSION_RELEASE)
695  {
696  // wrong version: in this case, inform the user...
697  wxString fmt;
698  fmt.Printf(_("SDK version mismatch for %s (%d.%d.%d). Expecting %d.%d.%d"),
699  name.c_str(),
700  major,
701  minor,
702  release,
707  return;
708  }
709 
710  // all done
711  // add this plugin in the temporary registration vector to be loaded
712  // by LoadPlugin() (which triggered the call to this function).
714  pr.name = name;
715  pr.createProc = createProc;
716  pr.freeProc = freeProc;
717  pr.versionProc = versionProc;
718  pr.info = info;
719  m_RegisteredPlugins.push_back(pr);
720 }
721 
722 bool PluginManager::ReadManifestFile(const wxString& pluginFilename,
723  const wxString& pluginName,
724  PluginInfo* infoOut)
725 {
726  if (!m_pCurrentlyLoadingManifestDoc)
727  {
728  // find and load plugin's resource file
729  // (pluginFilename contains no path info)
730  wxFileName fname(pluginFilename);
731  fname.SetExt(_T("zip"));
732  wxString actual = fname.GetFullName();
733 
734  // remove 'lib' prefix from plugin name (if any)
735  if (!platform::windows && actual.StartsWith(_T("lib")))
736  actual.Remove(0, 3);
737 
739  if (actual.IsEmpty())
740  {
741  Manager::Get()->GetLogManager()->LogError(_T("Plugin resource not found: ") + fname.GetFullName());
742  return false; // not found
743  }
744 
745  // load XML from ZIP
746  wxString contents;
747  wxFileSystem* fs = new wxFileSystem;
748  wxFSFile* f = fs->OpenFile(actual + _T("#zip:manifest.xml"));
749  if (f)
750  {
751  wxInputStream* is = f->GetStream();
752  char tmp[1024] = {};
753  while (!is->Eof() && is->CanRead())
754  {
755  memset(tmp, 0, sizeof(tmp));
756  is->Read(tmp, sizeof(tmp) - 1);
757  contents << cbC2U((const char*)tmp);
758  }
759  delete f;
760  }
761  else
762  {
763  Manager::Get()->GetLogManager()->LogError(_T("No plugin manifest file in resource: ") + actual);
764  delete fs;
765  return false;
766  }
767  delete fs;
768 
769  // actually load XML document
770  m_pCurrentlyLoadingManifestDoc = new TiXmlDocument;
771  if (!m_pCurrentlyLoadingManifestDoc->Parse(cbU2C(contents)))
772  {
773  Manager::Get()->GetLogManager()->LogError(_T("Plugin manifest could not be parsed: ") + actual);
774  return false;
775  }
776  }
777 
778  TiXmlElement* root = m_pCurrentlyLoadingManifestDoc->FirstChildElement("CodeBlocks_plugin_manifest_file");
779  if (!root)
780  {
781  Manager::Get()->GetLogManager()->LogError(_T("Plugin resource file not valid (no root element found) for: ") + pluginFilename);
782  return false;
783  }
784 
785  TiXmlElement* version = root->FirstChildElement("SdkVersion");
786  if (!version)
787  {
788  Manager::Get()->GetLogManager()->LogError(_T("Plugin resource file not valid (no SdkVersion element found) for: ") + pluginFilename);
789  return false;
790  }
791 
792  // check version
793 // int major;
794 // int minor;
795 // int release;
796 // if (version->QueryIntAttribute("major", &major) != TIXML_SUCCESS)
797 // major = 0;
798 // if (version->QueryIntAttribute("minor", &minor) != TIXML_SUCCESS)
799 // minor = 0;
800 // if (version->QueryIntAttribute("release", &release) != TIXML_SUCCESS)
801 // release = 0;
802 //
803 // if (major != PLUGIN_SDK_VERSION_MAJOR ||
804 // minor != PLUGIN_SDK_VERSION_MINOR ||
805 // release != PLUGIN_SDK_VERSION_RELEASE)
806 // {
807 // // wrong version: in this case, inform the user...
808 // wxString fmt;
809 // fmt.Printf(_("SDK version mismatch for %s (%d.%d.%d). Expecting %d.%d.%d"),
810 // pluginName.c_str(),
811 // major,
812 // minor,
813 // release,
814 // PLUGIN_SDK_VERSION_MAJOR,
815 // PLUGIN_SDK_VERSION_MINOR,
816 // PLUGIN_SDK_VERSION_RELEASE);
817 // Manager::Get()->GetLogManager()->LogError(fmt);
818 // return false;
819 // }
820 
821  // if no plugin name specified, we 're done here (successfully)
822  if (pluginName.IsEmpty() || !infoOut)
823  return true;
824 
825  TiXmlElement* plugin = root->FirstChildElement("Plugin");
826  while (plugin)
827  {
828  const char* name = plugin->Attribute("name");
829  if (name && cbC2U(name) == pluginName)
830  {
831  infoOut->name = pluginName;
832  TiXmlElement* value = plugin->FirstChildElement("Value");
833  while (value)
834  {
835  if (value->Attribute("title"))
836  infoOut->title = cbC2U(value->Attribute("title"));
837  if (value->Attribute("version"))
838  infoOut->version = cbC2U(value->Attribute("version"));
839  if (value->Attribute("description"))
840  infoOut->description = cbC2U(value->Attribute("description"));
841  if (value->Attribute("author"))
842  infoOut->author = cbC2U(value->Attribute("author"));
843  if (value->Attribute("authorEmail"))
844  infoOut->authorEmail = cbC2U(value->Attribute("authorEmail"));
845  if (value->Attribute("authorWebsite"))
846  infoOut->authorWebsite = cbC2U(value->Attribute("authorWebsite"));
847  if (value->Attribute("thanksTo"))
848  infoOut->thanksTo = cbC2U(value->Attribute("thanksTo"));
849  if (value->Attribute("license"))
850  infoOut->license = cbC2U(value->Attribute("license"));
851 
852  value = value->NextSiblingElement("Value");
853  }
854  break;
855  }
856 
857  plugin = plugin->NextSiblingElement("Plugin");
858  }
859 
860  return true;
861 }
862 
864  wxArrayString& extraFiles)
865 {
866  extraFiles.Clear();
867 
868  // find and load plugin's resource file
869  // (pluginFilename contains no path info)
870  wxFileName fname(pluginFilename);
871  fname.SetExt(_T("zip"));
872  wxString actual = fname.GetFullName();
873 
874  // remove 'lib' prefix from plugin name (if any)
875  if (!platform::windows && actual.StartsWith(_T("lib")))
876  actual.Remove(0, 3);
877 
879  if (actual.IsEmpty())
880  {
881  Manager::Get()->GetLogManager()->LogError(_T("Plugin resource not found: ") + fname.GetFullName());
882  return; // not found
883  }
884 
885  // load XML from ZIP
886  wxString contents;
887  wxFileSystem* fs = new wxFileSystem;
888  wxFSFile* f = fs->OpenFile(actual + _T("#zip:manifest.xml"));
889  if (f)
890  {
891  wxInputStream* is = f->GetStream();
892  char tmp[1024] = {};
893  while (!is->Eof() && is->CanRead())
894  {
895  memset(tmp, 0, sizeof(tmp));
896  is->Read(tmp, sizeof(tmp) - 1);
897  contents << cbC2U((const char*)tmp);
898  }
899  delete f;
900  }
901  else
902  {
903  Manager::Get()->GetLogManager()->LogError(_T("No plugin manifest file in resource: ") + actual);
904  delete fs;
905  return;
906  }
907  delete fs;
908 
909  // actually load XML document
910  TiXmlDocument doc;
911  if (!doc.Parse(cbU2C(contents)))
912  return;
913 
914  TiXmlElement* root = doc.FirstChildElement("CodeBlocks_plugin_manifest_file");
915  if (!root)
916  return;
917 
918  TiXmlElement* extra = root->FirstChildElement("Extra");
919  while (extra)
920  {
921  const char* file = extra->Attribute("file");
922  if (file && *file)
923  {
924  extraFiles.Add(cbC2U(file));
925  }
926 
927  extra = extra->NextSiblingElement("Extra");
928  }
929 }
930 
932 {
933  static const wxString PluginsMask = platform::windows ? _T("*.dll")
934  : (platform::darwin || platform::macosx) ? _T("*.dylib")
935  : _T("*.so");
936  int count = 0;
937  if (!wxDirExists(path))
938  return count;
939  wxDir dir(path);
940 
941  if (!dir.IsOpened())
942  return count;
943 
944  bool batch = Manager::IsBatchBuild();
945  wxArrayString bbplugins;
946  if (batch)
947  bbplugins = cbReadBatchBuildPlugins();
948 
949  wxString filename;
950  wxString failed;
951  bool ok = dir.GetFirst(&filename, PluginsMask, wxDIR_FILES);
952  while (ok)
953  {
954  if (batch)
955  {
956  // for batch builds, we will load only those plugins that the
957  // user has set (default only compiler.dll)
958  bool matched = false;
959  for (size_t i = 0; i < bbplugins.GetCount(); ++i)
960  {
961  if (bbplugins[i] == filename)
962  {
963  matched = true;
964  break;
965  }
966  }
967  if (!matched)
968  {
969  ok = dir.GetNext(&filename);
970  continue;
971  }
972  }
973 
974  // load manifest
975  m_pCurrentlyLoadingManifestDoc = nullptr;
976  if (ReadManifestFile(filename))
977  {
978  if (LoadPlugin(path + wxFILE_SEP_PATH + filename))
979  ++count;
980  else
981  failed << _T('\n') << filename;
982  }
983  if (m_pCurrentlyLoadingManifestDoc)
984  {
985  delete m_pCurrentlyLoadingManifestDoc;
986  m_pCurrentlyLoadingManifestDoc = nullptr;
987  }
988  ok = dir.GetNext(&filename);
989  }
990  Manager::Get()->GetLogManager()->Log(F(_("Loaded %d plugins"), count));
991  if (!failed.IsEmpty())
992  {
993  InfoWindow::Display(_("Warning"),
994  _("One or more plugins were not loaded.\n"
995  "This usually happens when a plugin is built for\n"
996  "a different version of the Code::Blocks SDK.\n"
997  "Check the application log for more info.\n\n"
998  "List of failed plugins:\n") + failed,
999  15000, 3000);
1000  }
1001  return count;
1002 }
1003 
1004 bool PluginManager::LoadPlugin(const wxString& pluginName)
1005 {
1006  // clear registration temporary vector
1007  m_RegisteredPlugins.clear();
1008 
1009  // load library
1010  m_CurrentlyLoadingFilename = pluginName;
1011  m_pCurrentlyLoadingLib = LibLoader::LoadLibrary(pluginName);
1012  if (!m_pCurrentlyLoadingLib->IsLoaded())
1013  {
1014  Manager::Get()->GetLogManager()->LogError(F(_T("%s: not loaded (missing symbols?)"), pluginName.wx_str()));
1015  LibLoader::RemoveLibrary(m_pCurrentlyLoadingLib);
1016  m_pCurrentlyLoadingLib = nullptr;
1017  m_CurrentlyLoadingFilename.Clear();
1018  return false;
1019  }
1020 
1021  // by now, the library has loaded and its global variables are initialized.
1022  // this means it has already called RegisterPlugin()
1023  // now we can actually create the plugin(s) instance(s) :)
1024 
1025  // try to load the plugin(s)
1026  std::vector<PluginRegistration>::iterator it;
1027  for (it = m_RegisteredPlugins.begin(); it != m_RegisteredPlugins.end(); ++it)
1028  {
1029  PluginRegistration& pr = *it;
1030  cbPlugin* plug = nullptr;
1031  try
1032  {
1033  plug = pr.createProc();
1034  }
1035  catch (cbException& exception)
1036  {
1037  exception.ShowErrorMessage(false);
1038  continue;
1039  }
1040 
1041  // all done; add it to our list
1042  PluginElement* plugElem = new PluginElement;
1043  plugElem->fileName = m_CurrentlyLoadingFilename;
1044  plugElem->info = pr.info;
1045  plugElem->library = m_pCurrentlyLoadingLib;
1046  plugElem->freeProc = pr.freeProc;
1047  plugElem->plugin = plug;
1048  m_Plugins.Add(plugElem);
1049  if (plug->GetType() == ptCompiler)
1050  m_CompilerPlugins.push_back(static_cast<cbCompilerPlugin*>(plug));
1051 
1052  SetupLocaleDomain(pr.name);
1053 
1054  Manager::Get()->GetLogManager()->DebugLog(F(_T("%s: loaded"), pr.name.wx_str()));
1055  }
1056 
1057  if (m_RegisteredPlugins.empty())
1058  {
1059  // no plugins loaded from this library, but it's not an error
1060  LibLoader::RemoveLibrary(m_pCurrentlyLoadingLib);
1061  }
1062  m_pCurrentlyLoadingLib = nullptr;
1063  m_CurrentlyLoadingFilename.Clear();
1064  return true;
1065 }
1066 
1068 {
1069  // check if a plugin crashed the app last time
1070  wxString probPlugin = Manager::Get()->GetConfigManager(_T("plugins"))->Read(_T("/try_to_activate"), wxEmptyString);
1071  if (!probPlugin.IsEmpty())
1072  {
1073  wxString msg;
1074  msg.Printf(_("Plugin \"%s\" failed to load last time Code::Blocks was executed.\n"
1075  "Do you want to disable this plugin from loading?"), probPlugin.c_str());
1076  if (cbMessageBox(msg, _("Warning"), wxICON_WARNING | wxYES_NO) == wxID_NO)
1077  probPlugin = _T("");
1078  }
1079 
1080  PluginElement* elem = nullptr;
1081  for (unsigned int i = 0; i < m_Plugins.GetCount(); ++i)
1082  {
1083  elem = m_Plugins[i];
1084  cbPlugin* plug = elem->plugin;
1085  if (!plug || plug->IsAttached())
1086  continue;
1087 
1088  // do not load it if the user has explicitly asked not to...
1089  wxString baseKey;
1090  baseKey << _T("/") << elem->info.name;
1091  bool loadIt = Manager::Get()->GetConfigManager(_T("plugins"))->ReadBool(baseKey, true);
1092 
1093  // if we have a problematic plugin, check if this is it
1094  if (loadIt && !probPlugin.IsEmpty())
1095  {
1096  loadIt = elem->info.title != probPlugin;
1097  // if this is the problematic plugin, don't load it
1098  if (!loadIt)
1099  Manager::Get()->GetConfigManager(_T("plugins"))->Write(baseKey, false);
1100  }
1101 
1102  if (loadIt)
1103  {
1104  Manager::Get()->GetConfigManager(_T("plugins"))->Write(_T("/try_to_activate"), elem->info.title);
1105  Manager::Get()->GetLogManager()->Log(elem->info.name);
1106  try
1107  {
1108  AttachPlugin(plug);
1109  Manager::Get()->GetConfigManager(_T("plugins"))->Write(_T("/try_to_activate"), wxEmptyString, false);
1110  }
1111  catch (cbException& exception)
1112  {
1113  Manager::Get()->GetLogManager()->Log(_T("[failed]"));
1114  exception.ShowErrorMessage(false);
1115 
1116  wxString msg;
1117  msg.Printf(_("Plugin \"%s\" failed to load...\n"
1118  "Do you want to disable this plugin from loading next time?"), elem->info.title.c_str());
1119  if (cbMessageBox(msg, _("Warning"), wxICON_WARNING | wxYES_NO) == wxID_YES)
1120  Manager::Get()->GetConfigManager(_T("plugins"))->Write(baseKey, false);
1121  }
1122  }
1123  }
1124  Manager::Get()->GetConfigManager(_T("plugins"))->Write(_T("/try_to_activate"), wxEmptyString, false);
1125 }
1126 
1128 {
1129 // Manager::Get()->GetLogManager()->DebugLog("Count %d", m_Plugins.GetCount());
1130 
1131  while (m_Plugins.GetCount())
1132  {
1133  UnloadPlugin(m_Plugins[0]->plugin);
1134  }
1135  m_CompilerPlugins.clear();
1136  m_Plugins.Clear();
1138 }
1139 
1141 {
1142  if (!plugin)
1143  return;
1144 
1145  // detach plugin if needed
1146  DetachPlugin(plugin);
1147 
1148  // find plugin element
1149  for (unsigned int i = 0; i < m_Plugins.GetCount(); ++i)
1150  {
1151  PluginElement* plugElem = m_Plugins[i];
1152  if (plugElem->plugin == plugin)
1153  {
1154  if (plugin->GetType() == ptCompiler)
1155  {
1156  auto removeIter = std::remove(m_CompilerPlugins.begin(), m_CompilerPlugins.end(), plugin);
1157  if (removeIter != m_CompilerPlugins.end())
1158  m_CompilerPlugins.erase(removeIter);
1159  }
1160 
1161  // found
1162  // free plugin
1163  if (plugElem->freeProc)
1164  plugElem->freeProc(plugin);
1165  else
1166  delete plugin; // try to delete it ourselves...
1167  // remove lib
1168  LibLoader::RemoveLibrary(plugElem->library);
1169  // and delete plugin element
1170  delete plugElem;
1171  m_Plugins.RemoveAt(i);
1172 
1173  break;
1174  }
1175  }
1176 }
1177 
1179 {
1180  for (unsigned int i = 0; i < m_Plugins.GetCount(); ++i)
1181  {
1182  PluginElement* plugElem = m_Plugins[i];
1183  if (plugElem->info.name == pluginName)
1184  return plugElem;
1185  }
1186 
1187  return nullptr;
1188 }
1189 
1191 {
1192  for (unsigned int i = 0; i < m_Plugins.GetCount(); ++i)
1193  {
1194  PluginElement* plugElem = m_Plugins[i];
1195  if (plugElem->info.name == pluginName)
1196  return plugElem->plugin;
1197  }
1198 
1199  return nullptr;
1200 }
1201 
1203 {
1204  for (unsigned int i = 0; i < m_Plugins.GetCount(); ++i)
1205  {
1206  PluginElement* plugElem = m_Plugins[i];
1207  if (plugElem->fileName == pluginFileName)
1208  return plugElem->plugin;
1209  }
1210 
1211  return nullptr;
1212 }
1213 
1215 {
1216  PluginElement* plugElem = FindElementByName(pluginName);
1217  if (plugElem && plugElem->info.name == pluginName)
1218  return &plugElem->info;
1219 
1220  return nullptr;
1221 }
1222 
1224 {
1225  for (unsigned int i = 0; i < m_Plugins.GetCount(); ++i)
1226  {
1227  PluginElement* plugElem = m_Plugins[i];
1228  if (plugElem->plugin == plugin)
1229  return &plugElem->info;
1230  }
1231 
1232  return nullptr;
1233 }
1234 
1236 {
1237  PluginElement* elem = FindElementByName(pluginName);
1238  cbPlugin* plug = elem ? elem->plugin : nullptr;
1239  if (plug)
1240  {
1241  if (plug->GetType() != ptTool)
1242  {
1243  Manager::Get()->GetLogManager()->LogError(F(_T("Plugin %s is not a tool to have Execute() method!"), elem->info.name.wx_str()));
1244  }
1245  else
1246  {
1247  try
1248  {
1249  return ((cbToolPlugin*)plug)->Execute();
1250  }
1251  catch (cbException& exception)
1252  {
1253  exception.ShowErrorMessage(false);
1254  }
1255  }
1256  }
1257  else
1258  {
1259  Manager::Get()->GetLogManager()->LogError(F(_T("No plugin registered by this name: %s"), pluginName.wx_str()));
1260  }
1261  return 0;
1262 }
1263 
1264 inline int SortByConfigurationPriority(cbPlugin** first, cbPlugin** second)
1265 {
1266  return (*first)->GetConfigurationPriority() - (*second)->GetConfigurationPriority();
1267 }
1268 
1269 void PluginManager::GetConfigurationPanels(int group, wxWindow* parent, ConfigurationPanelsArray& arrayToFill)
1270 {
1271  // build an array of Plugins* because we need to order it by configuration priority
1272  PluginsArray arr;
1273  for (unsigned int i = 0; i < m_Plugins.GetCount(); ++i)
1274  {
1275  cbPlugin* plug = m_Plugins[i]->plugin;
1276  // all check are done here
1277  if (plug && plug->IsAttached() && (plug->GetConfigurationGroup() & group))
1278  arr.Add(plug);
1279  }
1280 
1281  // sort the array
1282  arr.Sort(SortByConfigurationPriority);
1283 
1284  // now enumerate the array and fill the supplied configurations panel array
1285  arrayToFill.Clear();
1286  for (unsigned int i = 0; i < arr.GetCount(); ++i)
1287  {
1288  cbPlugin* plug = arr[i];
1289  cbConfigurationPanel* pnl = plug->GetConfigurationPanel(parent);
1290  if (pnl)
1291  arrayToFill.Add(pnl);
1292  }
1293 }
1294 
1295 void PluginManager::GetProjectConfigurationPanels(wxWindow* parent, cbProject* project, ConfigurationPanelsArray& arrayToFill)
1296 {
1297  for (unsigned int i = 0; i < m_Plugins.GetCount(); ++i)
1298  {
1299  cbPlugin* plug = m_Plugins[i]->plugin;
1300  if (plug && plug->IsAttached())
1301  {
1302  cbConfigurationPanel* pnl = plug->GetProjectConfigurationPanel(parent, project);
1303  if (pnl)
1304  arrayToFill.Add(pnl);
1305  }
1306  }
1307 }
1308 
1310 {
1311  if (m_CompilerPlugins.empty())
1312  return nullptr;
1313  return m_CompilerPlugins.front();
1314 }
1315 
1317 {
1318  return GetOffersFor(ptTool);
1319 }
1320 
1322 {
1323  return GetOffersFor(ptMime);
1324 }
1325 
1327 {
1328  return GetOffersFor(ptDebugger);
1329 }
1330 
1332 {
1333  return GetOffersFor(ptCodeCompletion);
1334 }
1335 
1337 {
1338  return GetOffersFor(ptSmartIndent);
1339 }
1340 
1342 {
1343  PluginsArray arr;
1344 
1345  // special case for MIME plugins
1346  // we 'll add the default MIME handler, last in the returned array
1347  cbPlugin* dflt = nullptr;
1348 
1349  for (unsigned int i = 0; i < m_Plugins.GetCount(); ++i)
1350  {
1351  cbPlugin* plug = m_Plugins[i]->plugin;
1352  if (plug && plug->IsAttached() && plug->GetType() == type)
1353  {
1354  if (type == ptMime)
1355  {
1356  // default MIME handler?
1357  if (((cbMimePlugin*)plug)->HandlesEverything())
1358  dflt = plug;
1359  else
1360  arr.Add(plug);
1361  }
1362  else
1363  arr.Add(plug);
1364  }
1365  }
1366 
1367  // add default MIME handler last
1368  if (dflt)
1369  arr.Add(dflt);
1370 
1371  return arr;
1372 }
1373 
1375 {
1376  std::map<wxString, cbPlugin*> sortedPlugins;
1377  for (unsigned int i = 0; i < m_Plugins.GetCount(); ++i)
1378  {
1379  cbPlugin* plug = m_Plugins[i]->plugin;
1380  if (plug && plug->IsAttached())
1381  sortedPlugins[m_Plugins[i]->info.name] = plug;
1382  }
1383 
1384  // We want the order of iteration to be more stable, so there are fewer surprises on different machines.
1385  for (auto &pair : sortedPlugins)
1386  {
1387  try
1388  {
1389  pair.second->BuildModuleMenu(type, menu, data);
1390  }
1391  catch (cbException& exception)
1392  {
1393  exception.ShowErrorMessage(false);
1394  }
1395  }
1396 
1397  // script plugins now
1399  for (size_t i = 0; i < ids.GetCount(); ++i)
1400  {
1401  Connect(ids[i], -1, wxEVT_COMMAND_MENU_SELECTED,
1402  (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction)
1404  }
1405 }
1406 
1408 {
1409  m_FindMenuItemCount = 0;
1410  m_FindMenuItemFirst = 0;
1411  m_LastNonPluginMenuId = 0;
1412 }
1413 
1414 void PluginManager::RegisterFindMenuItems(bool before, int count)
1415 {
1416  if (before)
1417  m_FindMenuItemFirst += count;
1418  else
1419  m_FindMenuItemCount += count;
1420 }
1421 
1423 {
1424  return m_FindMenuItemCount;
1425 }
1426 
1428 {
1429  return m_FindMenuItemFirst;
1430 }
1431 
1433 {
1434  m_LastNonPluginMenuId = id;
1435 }
1436 
1438 {
1439  wxString labelNoAmpersands = label;
1440  // Remove ampersands, because they are not visible but affect the sorting if they are at the
1441  // start of the label.
1442  labelNoAmpersands.erase(std::remove(labelNoAmpersands.begin(), labelNoAmpersands.end(), '&'),
1443  labelNoAmpersands.end());
1444 
1445  int position = -1;
1446  const wxMenuItemList &items = popup.GetMenuItems();
1447  const int count = int(items.size());
1448  for (int ii = 0; ii < count; ++ii)
1449  {
1450  if (items[ii]->GetId() == m_LastNonPluginMenuId)
1451  {
1452  position = ii + 1;
1453  break;
1454  }
1455  }
1456 
1457  if (position == -1 || (position >= count))
1458  return count;
1459  if (items[position]->GetKind() == wxITEM_SEPARATOR)
1460  position++;
1461 
1462  // Linear search for now. The number of items isn't large, so it shouldn't be a performance
1463  // problem.
1464  for (int ii = position; ii < count; ++ii)
1465  {
1466  const wxString &itemLabel = items[ii]->GetItemLabelText();
1467  if (labelNoAmpersands.CmpNoCase(itemLabel) <= 0)
1468  return ii;
1469  }
1470  return count;
1471 }
1472 
1474 {
1476 }
1477 
1479 {
1481 }
1482 
1484 {
1485  PluginsArray mimes = GetMimeOffers();
1486  for (unsigned int i = 0; i < mimes.GetCount(); ++i)
1487  {
1488  cbMimePlugin* plugin = (cbMimePlugin*)mimes[i];
1489  if (plugin && plugin->CanHandleFile(filename))
1490  return plugin;
1491  }
1492  return nullptr;
1493 }
1494 
1496 {
1497  PluginsConfigurationDlg dlg(Manager::Get()->GetAppWindow());
1498  PlaceWindow(&dlg);
1499  return dlg.ShowModal();
1500 }
1501 
1503 {
1504  int catalogNum=Manager::Get()->GetConfigManager(_T("app"))->ReadInt(_T("/locale/catalogNum"),(int)0);
1505  int i = 1;
1506  for (; i <= catalogNum; ++i)
1507  {
1508  wxString catalogName=Manager::Get()->GetConfigManager(_T("app"))->Read(wxString::Format(_T("/locale/Domain%d"), i), wxEmptyString);
1509  if (catalogName.Cmp(DomainName) == 0)
1510  break;
1511  }
1512  if (i > catalogNum)
1513  {
1514  ++catalogNum;
1515  Manager::Get()->GetConfigManager(_T("app"))->Write(_T("/locale/catalogNum"), (int)catalogNum);
1516  Manager::Get()->GetConfigManager(_T("app"))->Write(wxString::Format(_T("/locale/Domain%d"), i), DomainName);
1517  }
1518 }
1519 
1521 {
1522  Manager::Get()->ProcessEvent(event);
1523 }
1524 
1526 {
1527  Manager::Get()->ProcessEvent(event);
1528 }
1529 
1531 {
1532  Manager::Get()->ProcessEvent(event);
1533 }
1534 
1536 {
1537  for (const cbCompilerPlugin *p : manager->GetCompilerPlugins())
1538  {
1539  if (p && p->IsRunning())
1540  return true;
1541  }
1542  return false;
1543 }
1544 
1546 {
1547  for (cbCompilerPlugin *compiler : manager->GetCompilerPlugins())
1548  {
1549  if (!compiler || !compiler->IsRunning())
1550  continue;
1551  compiler->KillProcess();
1552  while (compiler->IsRunning())
1553  {
1554  wxMilliSleep(100);
1555  Manager::Yield();
1556  }
1557  }
1558 }
void Attach()
Attach is not a virtual function, so you can&#39;t override it.
Definition: cbplugin.cpp:62
wxString AfterLast(wxUniChar ch) const
wxString F(const wxChar *msg,...)
sprintf-like function
Definition: logmanager.h:20
wxString authorEmail
Definition: pluginmanager.h:45
static void Display(const wxString &title, const wxString &message, unsigned int delay=5000, unsigned int hysteresis=1)
Definition: infowindow.cpp:294
Base class for plugins.
Definition: cbplugin.h:84
wxString license
Definition: pluginmanager.h:48
wxString name
Definition: pluginmanager.h:40
int Execute(const wxString &command)
Definition: sc_io.cpp:183
virtual bool Update(int value, const wxString &newmsg=wxEmptyString, bool *skip=NULL)
void RemoveLibrary(wxDynamicLibrary *lib)
Definition: globals.h:27
static wxString GetFolder(SearchDirs dir)
Access one of Code::Blocks&#39; folders.
bool wxRemoveFile(const wxString &file)
#define wxICON_QUESTION
wxString fileName
Definition: pluginmanager.h:55
const_iterator begin() const
void CreateMenu(wxMenuBar *menuBar)
Data folder in user&#39;s dir.
Definition: configmanager.h:75
void RegisterPlugin(const wxString &name, CreatePluginProc createProc, FreePluginProc freeProc, PluginSDKVersionProc versionProc)
static bool s_SafeMode
#define wxICON_WARNING
PluginsArray GetSmartIndentOffers()
int FindSortedMenuItemPosition(wxMenu &popup, const wxString &label) const
Called by plugins when they want to add menu items to the editor&#39;s context menu.
const int version
void Assign(const wxFileName &filepath)
ConfigManager * GetConfigManager(const wxString &name_space) const
Definition: manager.cpp:474
int ReadInt(const wxString &name, int defaultVal=0)
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
bool IsOpened() const
Dialog for Enabling/Disabling/Installing/Uninstalling a plug-in.
int Cmp(const wxString &s) const
int SortByConfigurationPriority(cbPlugin **first, cbPlugin **second)
Information about the plugin.
Definition: pluginmanager.h:38
bool ExtractFile(const wxString &bundlename, const wxString &src_filename, const wxString &dst_filename, bool isMandatory=true)
static bool IsAppShuttingDown()
Definition: manager.cpp:333
void cbStopRunningCompilers(PluginManager *manager)
#define PLUGIN_SDK_VERSION_MINOR
Definition: cbplugin.h:40
bool GetFirst(wxString *filename, const wxString &filespec=wxEmptyString, int flags=wxDIR_DEFAULT) const
#define wxICON_ERROR
void ReadExtraFilesFromManifestFile(const wxString &pluginFilename, wxArrayString &extraFiles)
void GetConfigurationPanels(int group, wxWindow *parent, ConfigurationPanelsArray &arrayToFill)
~PluginManager() override
bool wxFileExists(const wxString &filename)
void LogWarning(const wxString &msg, int i=app_log)
Definition: logmanager.h:141
bool ReadBool(const wxString &name, bool defaultVal=false)
void SetPlugin(cbPlugin *plugin)
Definition: sdk_events.h:51
virtual bool CanHandleFile(const wxString &filename) const =0
Can a file be handled by this plugin?
std::map< wxString, RefCountedLib > Libs
Event used to request from the main app to add a window to the docking system.
Definition: sdk_events.h:83
wxCStrData c_str() const
bool wxDirExists(const wxString &dirname)
#define PLUGIN_SDK_VERSION_MAJOR
Definition: cbplugin.h:39
#define _T(string)
FreePluginProc freeProc
Definition: pluginmanager.h:57
virtual void BuildModuleMenu(cb_optional const ModuleType type, cb_optional wxMenu *menu, cb_optional const FileTreeData *data=nullptr)
This method is called by Code::Blocks core modules (EditorManager, ProjectManager etc) and is used by...
Definition: cbplugin.h:155
PluginsArray GetDebuggerOffers()
int ExecutePlugin(const wxString &pluginName)
wxString title
Definition: pluginmanager.h:41
#define wxYES_NO
PluginInfo info
Definition: pluginmanager.h:54
int ScanForPlugins(const wxString &path)
const DLLIMPORT wxString DYNAMICLIB_DOT_EXT
wxString & Remove(size_t pos)
wxString GetName() const
virtual PluginType GetType() const
The plugin must return its type on request.
Definition: cbplugin.h:97
bool ExportPlugin(cbPlugin *plugin, const wxString &filename)
wxString AfterFirst(wxUniChar ch) const
Base class for mime plugins.
Definition: cbplugin.h:684
wxDynamicLibrary * LoadLibrary(const wxString &filename)
A generic Code::Blocks event.
Definition: sdk_events.h:20
int CmpNoCase(const wxString &s) const
static wxString LocateDataFile(const wxString &filename, int search_dirs=sdAllKnown)
Locate a file in an installation- and platform-independent way.
Base class for compiler plugins.
Definition: cbplugin.h:263
void Release(bool appShutDown)
Release is not a virtual function, so you can&#39;t override it.
Definition: cbplugin.cpp:82
wxWindow * GetAppWindow() const
Definition: manager.cpp:424
const PluginInfo * GetPluginInfo(const wxString &pluginName)
cbMimePlugin * GetMIMEHandlerForFile(const wxString &filename)
DLLIMPORT const wxWX2MBbuf cbU2C(const wxString &str)
Return multibyte (C string) representation of the string.
Definition: globals.cpp:743
wxFSFile * OpenFile(const wxString &location, int flags=wxFS_READ)
void LogError(const wxString &msg, int i=app_log)
Definition: logmanager.h:142
void OnScriptMenu(wxCommandEvent &event)
PluginSDKVersionProc versionProc
wxString BeforeFirst(wxUniChar ch, wxString *rest=NULL) const
void ResetModuleMenu()
Called by the code creating the context menu for the editor.
PluginManager manages plugins.
Definition: pluginmanager.h:76
PluginType
Known plugin types.
Definition: globals.h:24
int CompareVersions(const wxString &new_version, const wxString &old_version)
PluginsArray GetCodeCompletionOffers()
bool Contains(const wxString &str) const
void Write(const wxString &name, const wxString &value, bool ignoreEmpty=false)
bool DetachPlugin(cbPlugin *plugin)
void SetComment(const wxString &comment)
DLLIMPORT wxString UnixFilename(const wxString &filename, wxPathFormat format=wxPATH_NATIVE)
Definition: globals.cpp:228
Represents a Code::Blocks project.
Definition: cbproject.h:96
null_pointer_t nullptr
Definition: nullptr.cpp:16
const CompilerPlugins & GetCompilerPlugins() const
ModuleType
The type of module offering a context menu.
Definition: globals.h:38
DLLIMPORT wxString cbC2U(const char *str)
Return str as a proper unicode-compatible string.
Definition: globals.cpp:733
void GetProjectConfigurationPanels(wxWindow *parent, cbProject *project, ConfigurationPanelsArray &arrayToFill)
void(* FreePluginProc)(cbPlugin *)
Definition: pluginmanager.h:35
cbPlugin * FindPluginByFileName(const wxString &pluginFileName)
void SetupLocaleDomain(const wxString &DomainName)
void RemoveAllEventSinksFor(void *owner)
Definition: manager.cpp:570
static bool IsBatchBuild()
Definition: manager.h:66
cbPlugin * FindPluginByName(const wxString &pluginName)
LogManager * GetLogManager() const
Definition: manager.cpp:439
wxString Read(const wxString &key, const wxString &defaultVal=wxEmptyString)
bool IsEmpty() const
bool IsOpened() const
cbPlugin *(* CreatePluginProc)()
Definition: pluginmanager.h:34
const wxStringCharType * wx_str() const
virtual int ShowModal()
wxString wxEmptyString
wxDynamicLibrary * library
Definition: pluginmanager.h:56
void OnScriptModuleMenu(wxCommandEvent &event)
void NotifyPlugins(CodeBlocksEvent &event)
wxString version
Definition: pluginmanager.h:42
Definition: manager.h:183
MacrosManager * GetMacrosManager() const
Definition: manager.cpp:454
static bool Access(const wxString &name, wxFile::OpenMode mode)
const wxString & _(const wxString &string)
bool cbHasRunningCompilers(const PluginManager *manager)
void ReplaceMacros(wxString &buffer, ProjectBuildTarget *target=nullptr, bool subrequest=false)
Base class for plugin configuration panels.
void ShowErrorMessage(bool safe=true)
Display exception error message.
Definition: cbexception.cpp:31
static void Yield()
Whenever you need to call wxYield(), call Manager::Yield(). It&#39;s safer.
Definition: manager.cpp:221
void RegisterLastNonPluginMenuItem(int id)
Called by the editor code which adds the non-plugin related menu items to store the id of the last fi...
bool PutNextEntry(wxZipEntry *entry)
wxString thanksTo
Definition: pluginmanager.h:47
wxArray< int > wxArrayInt
Plugins folder in user&#39;s dir.
Definition: configmanager.h:73
Plugins folder in base dir.
Definition: configmanager.h:79
PluginsArray GetMimeOffers()
bool ToLong(long *val, int base=10) const
bool AttachPlugin(cbPlugin *plugin, bool ignoreSafeMode=false)
void ReleaseMenu(wxMenuBar *menuBar)
wxString & erase(size_type pos=0, size_type n=npos)
EVTIMPORT const wxEventType cbEVT_PLUGIN_UNINSTALLED
Definition: sdk_events.cpp:77
#define PLUGIN_SDK_VERSION_RELEASE
Definition: cbplugin.h:41
bool GetNext(wxString *filename) const
bool IsEmpty() const
DLLIMPORT void PlaceWindow(wxTopLevelWindow *w, cbPlaceDialogMode mode=pdlBest, bool enforce=false)
Definition: globals.cpp:1177
DLLIMPORT wxArrayString cbReadBatchBuildPlugins()
Read the list of batch build plugins and return them.
void Log(const wxString &msg, int i=app_log, Logger::level lv=Logger::info)
Definition: logmanager.h:140
wxString GetFullName() const
void AskPluginsForModuleMenu(const ModuleType type, wxMenu *menu, const FileTreeData *data=nullptr)
int GetFindMenuItemFirst() const
Returns the position of the first menu item in the find group.
bool UninstallPlugin(cbPlugin *plugin, bool removeFiles=true)
wxString authorWebsite
Definition: pluginmanager.h:46
PluginsArray GetToolOffers()
void DebugLog(const wxString &msg, Logger::level lv=Logger::info)
Definition: logmanager.h:146
const_iterator end() const
bool ProcessEvent(CodeBlocksEvent &event)
Definition: manager.cpp:246
void wxMilliSleep(unsigned long milliseconds)
wxDynamicLibrary * lib
void RegisterFindMenuItems(bool before, int count)
Can be called by plugins&#39; BuildModuleMenu when building the EditorManager&#39;s context menu...
void SetExt(const wxString &ext)
wxString & Prepend(const wxString &str)
void Cleanup()
cbCompilerPlugin * GetFirstCompiler() const
size_t Add(const wxString &str, size_t copies=1)
bool ReadManifestFile(const wxString &pluginFilename, const wxString &pluginName=wxEmptyString, PluginInfo *infoOut=nullptr)
Base class for tool plugins.
Definition: cbplugin.h:658
virtual cbConfigurationPanel * GetProjectConfigurationPanel(cb_optional wxWindow *parent, cb_optional cbProject *project)
Return plugin&#39;s configuration panel for projects.
Definition: cbplugin.h:125
bool LoadPlugin(const wxString &pluginName)
bool InstallPlugin(const wxString &pluginName, bool forAllUsers=true, bool askForConfirmation=true)
bool StartsWith(const wxString &prefix, wxString *rest=NULL) const
wxInputStream * GetStream() const
virtual int GetConfigurationGroup() const
Return the configuration group for this plugin.
Definition: cbplugin.h:110
PluginsArray GetOffersFor(PluginType type)
size_t GetCount() const
wxArrayInt CreateModuleMenu(const ModuleType type, wxMenu *menu, const FileTreeData *data)
Definition: sc_plugin.cpp:104
bool Load(const wxString &name, int flags=wxDL_DEFAULT)
bool IsAttached() const
See whether this plugin is attached or not.
Definition: cbplugin.h:187
DLLIMPORT bool CreateDirRecursively(const wxString &full_path, int perms=0755)
Definition: globals.cpp:610
cbPlugin * plugin
Definition: pluginmanager.h:58
Data folder in base dir.
Definition: configmanager.h:81
PluginElement * FindElementByName(const wxString &pluginName)
int Printf(const wxString &pszFormat,...)
int GetFindMenuItemCount() const
Returns the number of items in the find group already added to the menu.
wxMenuItemList & GetMenuItems()
void VersionStringToNumbers(const wxString &version, long *major, long *minor, long *release)
#define SIGN(a)
Definition: globals.h:28
void(* PluginSDKVersionProc)(int *, int *, int *)
Definition: pluginmanager.h:33
EVTIMPORT const wxEventType cbEVT_PLUGIN_INSTALLED
Definition: sdk_events.cpp:76
Code::Blocks error handling unit.
Definition: cbexception.h:23
static wxString Format(const wxString &format,...)
size_t Write(const void *buffer, size_t count)
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
Event used to request from the main app to manage the view layouts.
Definition: sdk_events.h:158
void UnloadPlugin(cbPlugin *plugin)
virtual cbConfigurationPanel * GetConfigurationPanel(cb_optional wxWindow *parent)
Return plugin&#39;s configuration panel.
Definition: cbplugin.h:116
wxString description
Definition: pluginmanager.h:43
wxString author
Definition: pluginmanager.h:44