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: svn://svn.code.sf.net/p/codeblocks/code/branches/release-20.xx/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"
50 #include "pluginsconfigurationdlg.h"
51
52 #include "scripting/bindings/sc_plugin.h"
53
54 template<> PluginManager* Mgr<PluginManager>::instance = nullptr;
55 template<> bool Mgr<PluginManager>::isShutdown = false;
56
VersionStringToNumbers(const wxString & version,long * major,long * minor,long * release)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
CompareVersions(const wxString & new_version,const wxString & old_version)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 {
94 struct RefCountedLib
95 {
RefCountedLibLibLoader::RefCountedLib96 RefCountedLib() : lib(nullptr), ref(0) {}
97 wxDynamicLibrary* lib;
98 int ref;
99 };
100 typedef std::map<wxString, RefCountedLib> Libs;
101 Libs s_Libs;
102
LoadLibrary(const wxString & filename)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
RemoveLibrary(wxDynamicLibrary * lib)120 inline void RemoveLibrary(wxDynamicLibrary* lib)
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
134 if (!Manager::IsAppShuttingDown())
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
Cleanup()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
153 if (!Manager::IsAppShuttingDown())
154 delete rcl.lib;
155 }
156 s_Libs.clear();
157 }
158 };
159
160 //static
161 bool PluginManager::s_SafeMode = false;
162
BEGIN_EVENT_TABLE(PluginManager,wxEvtHandler)163 BEGIN_EVENT_TABLE(PluginManager, wxEvtHandler)
164 //
165 END_EVENT_TABLE()
166
167 // class constructor
168 PluginManager::PluginManager()
169 : m_pCurrentlyLoadingLib(nullptr),
170 m_pCurrentlyLoadingManifestDoc(nullptr)
171 {
172 Manager::Get()->GetAppWindow()->PushEventHandler(this);
173 }
174
175 // class destructor
~PluginManager()176 PluginManager::~PluginManager()
177 {
178 UnloadAllPlugins();
179 }
180
CreateMenu(cb_unused wxMenuBar * menuBar)181 void PluginManager::CreateMenu(cb_unused wxMenuBar* menuBar)
182 {
183 }
184
ReleaseMenu(cb_unused wxMenuBar * menuBar)185 void PluginManager::ReleaseMenu(cb_unused wxMenuBar* menuBar)
186 {
187 }
188
AttachPlugin(cbPlugin * plugin,bool ignoreSafeMode)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
DetachPlugin(cbPlugin * plugin)201 bool PluginManager::DetachPlugin(cbPlugin* plugin)
202 {
203 if (!plugin)
204 return false;
205 if (!plugin->IsAttached())
206 return true;
207
208 Manager::Get()->RemoveAllEventSinksFor(plugin);
209 plugin->Release(Manager::IsAppShuttingDown());
210 return true;
211 }
212
InstallPlugin(const wxString & pluginName,bool forAllUsers,bool askForConfirmation)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();
223 wxString version;
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 {
266 pluginDir = ConfigManager::GetFolder(sdPluginsGlobal);
267 resourceDir = ConfigManager::GetFolder(sdDataGlobal);
268 }
269 else
270 {
271 pluginDir = ConfigManager::GetFolder(sdPluginsUser);
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"));
353 CodeBlocksEvent evt(cbEVT_PLUGIN_INSTALLED);
354 evt.SetPlugin(plugin);
355 Manager::Get()->ProcessEvent(evt);
356 // Manager::Get()->GetLogManager()->DebugLog(F(_T("Menus updated")));
357
358 return true;
359 }
360
UninstallPlugin(cbPlugin * plugin,bool removeFiles)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"));
427 CodeBlocksEvent event(cbEVT_PLUGIN_UNINSTALLED);
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
ExportPlugin(cbPlugin * plugin,const wxString & filename)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
ExtractFile(const wxString & bundlename,const wxString & src_filename,const wxString & dst_filename,bool isMandatory)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
RegisterPlugin(const wxString & name,CreatePluginProc createProc,FreePluginProc freeProc,PluginSDKVersionProc versionProc)665 void PluginManager::RegisterPlugin(const wxString& name,
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,
703 PLUGIN_SDK_VERSION_MAJOR,
704 PLUGIN_SDK_VERSION_MINOR,
705 PLUGIN_SDK_VERSION_RELEASE);
706 Manager::Get()->GetLogManager()->LogError(fmt);
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).
713 PluginRegistration pr;
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
ReadManifestFile(const wxString & pluginFilename,const wxString & pluginName,PluginInfo * infoOut)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
738 actual = ConfigManager::LocateDataFile(actual, sdPluginsUser | sdDataUser | sdPluginsGlobal | sdDataGlobal);
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
ReadExtraFilesFromManifestFile(const wxString & pluginFilename,wxArrayString & extraFiles)863 void PluginManager::ReadExtraFilesFromManifestFile(const wxString& pluginFilename,
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
878 actual = ConfigManager::LocateDataFile(actual, sdPluginsUser | sdDataUser | sdPluginsGlobal | sdDataGlobal);
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
ScanForPlugins(const wxString & path)931 int PluginManager::ScanForPlugins(const wxString& path)
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
LoadPlugin(const wxString & pluginName)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
LoadAllPlugins()1067 void PluginManager::LoadAllPlugins()
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
UnloadAllPlugins()1127 void PluginManager::UnloadAllPlugins()
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();
1137 LibLoader::Cleanup();
1138 }
1139
UnloadPlugin(cbPlugin * plugin)1140 void PluginManager::UnloadPlugin(cbPlugin* plugin)
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
FindElementByName(const wxString & pluginName)1178 PluginElement* PluginManager::FindElementByName(const wxString& pluginName)
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
FindPluginByName(const wxString & pluginName)1190 cbPlugin* PluginManager::FindPluginByName(const wxString& pluginName)
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
FindPluginByFileName(const wxString & pluginFileName)1202 cbPlugin* PluginManager::FindPluginByFileName(const wxString& pluginFileName)
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
GetPluginInfo(const wxString & pluginName)1214 const PluginInfo* PluginManager::GetPluginInfo(const wxString& pluginName)
1215 {
1216 PluginElement* plugElem = FindElementByName(pluginName);
1217 if (plugElem && plugElem->info.name == pluginName)
1218 return &plugElem->info;
1219
1220 return nullptr;
1221 }
1222
GetPluginInfo(cbPlugin * plugin)1223 const PluginInfo* PluginManager::GetPluginInfo(cbPlugin* plugin)
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
ExecutePlugin(const wxString & pluginName)1235 int PluginManager::ExecutePlugin(const wxString& pluginName)
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
SortByConfigurationPriority(cbPlugin ** first,cbPlugin ** second)1264 inline int SortByConfigurationPriority(cbPlugin** first, cbPlugin** second)
1265 {
1266 return (*first)->GetConfigurationPriority() - (*second)->GetConfigurationPriority();
1267 }
1268
GetConfigurationPanels(int group,wxWindow * parent,ConfigurationPanelsArray & arrayToFill)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
GetProjectConfigurationPanels(wxWindow * parent,cbProject * project,ConfigurationPanelsArray & arrayToFill)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
GetFirstCompiler() const1309 cbCompilerPlugin* PluginManager::GetFirstCompiler() const
1310 {
1311 if (m_CompilerPlugins.empty())
1312 return nullptr;
1313 return m_CompilerPlugins.front();
1314 }
1315
GetToolOffers()1316 PluginsArray PluginManager::GetToolOffers()
1317 {
1318 return GetOffersFor(ptTool);
1319 }
1320
GetMimeOffers()1321 PluginsArray PluginManager::GetMimeOffers()
1322 {
1323 return GetOffersFor(ptMime);
1324 }
1325
GetDebuggerOffers()1326 PluginsArray PluginManager::GetDebuggerOffers()
1327 {
1328 return GetOffersFor(ptDebugger);
1329 }
1330
GetCodeCompletionOffers()1331 PluginsArray PluginManager::GetCodeCompletionOffers()
1332 {
1333 return GetOffersFor(ptCodeCompletion);
1334 }
1335
GetSmartIndentOffers()1336 PluginsArray PluginManager::GetSmartIndentOffers()
1337 {
1338 return GetOffersFor(ptSmartIndent);
1339 }
1340
GetOffersFor(PluginType type)1341 PluginsArray PluginManager::GetOffersFor(PluginType type)
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
AskPluginsForModuleMenu(const ModuleType type,wxMenu * menu,const FileTreeData * data)1374 void PluginManager::AskPluginsForModuleMenu(const ModuleType type, wxMenu* menu, const FileTreeData* data)
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
1398 wxArrayInt ids = ScriptBindings::ScriptPluginWrapper::CreateModuleMenu(type, menu, data);
1399 for (size_t i = 0; i < ids.GetCount(); ++i)
1400 {
1401 Connect(ids[i], -1, wxEVT_COMMAND_MENU_SELECTED,
1402 (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction)
1403 &PluginManager::OnScriptModuleMenu);
1404 }
1405 }
1406
ResetModuleMenu()1407 void PluginManager::ResetModuleMenu()
1408 {
1409 m_FindMenuItemCount = 0;
1410 m_FindMenuItemFirst = 0;
1411 m_LastNonPluginMenuId = 0;
1412 }
1413
RegisterFindMenuItems(bool before,int count)1414 void PluginManager::RegisterFindMenuItems(bool before, int count)
1415 {
1416 if (before)
1417 m_FindMenuItemFirst += count;
1418 else
1419 m_FindMenuItemCount += count;
1420 }
1421
GetFindMenuItemCount() const1422 int PluginManager::GetFindMenuItemCount() const
1423 {
1424 return m_FindMenuItemCount;
1425 }
1426
GetFindMenuItemFirst() const1427 int PluginManager::GetFindMenuItemFirst() const
1428 {
1429 return m_FindMenuItemFirst;
1430 }
1431
RegisterLastNonPluginMenuItem(int id)1432 void PluginManager::RegisterLastNonPluginMenuItem(int id)
1433 {
1434 m_LastNonPluginMenuId = id;
1435 }
1436
FindSortedMenuItemPosition(wxMenu & popup,const wxString & label) const1437 int PluginManager::FindSortedMenuItemPosition(wxMenu &popup, const wxString& label) const
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
OnScriptMenu(wxCommandEvent & event)1473 void PluginManager::OnScriptMenu(wxCommandEvent& event)
1474 {
1475 ScriptBindings::ScriptPluginWrapper::OnScriptMenu(event.GetId());
1476 }
1477
OnScriptModuleMenu(wxCommandEvent & event)1478 void PluginManager::OnScriptModuleMenu(wxCommandEvent& event)
1479 {
1480 ScriptBindings::ScriptPluginWrapper::OnScriptModuleMenu(event.GetId());
1481 }
1482
GetMIMEHandlerForFile(const wxString & filename)1483 cbMimePlugin* PluginManager::GetMIMEHandlerForFile(const wxString& filename)
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
Configure()1495 int PluginManager::Configure()
1496 {
1497 PluginsConfigurationDlg dlg(Manager::Get()->GetAppWindow());
1498 PlaceWindow(&dlg);
1499 return dlg.ShowModal();
1500 }
1501
SetupLocaleDomain(const wxString & DomainName)1502 void PluginManager::SetupLocaleDomain(const wxString& DomainName)
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
NotifyPlugins(CodeBlocksEvent & event)1520 void PluginManager::NotifyPlugins(CodeBlocksEvent& event)
1521 {
1522 Manager::Get()->ProcessEvent(event);
1523 }
1524
NotifyPlugins(CodeBlocksDockEvent & event)1525 void PluginManager::NotifyPlugins(CodeBlocksDockEvent& event)
1526 {
1527 Manager::Get()->ProcessEvent(event);
1528 }
1529
NotifyPlugins(CodeBlocksLayoutEvent & event)1530 void PluginManager::NotifyPlugins(CodeBlocksLayoutEvent& event)
1531 {
1532 Manager::Get()->ProcessEvent(event);
1533 }
1534
cbHasRunningCompilers(const PluginManager * manager)1535 bool cbHasRunningCompilers(const PluginManager *manager)
1536 {
1537 for (const cbCompilerPlugin *p : manager->GetCompilerPlugins())
1538 {
1539 if (p && p->IsRunning())
1540 return true;
1541 }
1542 return false;
1543 }
1544
cbStopRunningCompilers(PluginManager * manager)1545 void cbStopRunningCompilers(PluginManager *manager)
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 }
1559