Code::Blocks  SVN r11506
projectmanager.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: 11498 $
6  * $Id: projectmanager.cpp 11498 2018-10-07 09:45:04Z fuscated $
7  * $HeadURL: https://svn.code.sf.net/p/codeblocks/code/trunk/src/sdk/projectmanager.cpp $
8  */
9 
10 #include "sdk_precomp.h"
11 
12 #ifndef CB_PRECOMP
13  #include <wx/datetime.h>
14  #include <wx/dir.h>
15  #include <wx/filename.h>
16 
17  #include "projectmanager.h" // class's header file
18  #include "sdk_events.h"
19  #include "manager.h"
20  #include "configmanager.h"
21  #include "cbproject.h"
22  #include "logmanager.h"
23  #include "pluginmanager.h"
24  #include "editormanager.h"
25  #include "uservarmanager.h"
26  #include "workspaceloader.h"
27  #include "cbworkspace.h"
28  #include "cbeditor.h"
29  #include "globals.h"
30  #include "cbexception.h" // for cbassert
31 #endif
32 
33 #include <wx/progdlg.h>
34 
35 #include "cbauibook.h"
36 //#include "filefilters.h"
37 #include "filegroupsandmasks.h"
38 
39 template<> ProjectManager* Mgr<ProjectManager>::instance = nullptr;
40 template<> bool Mgr<ProjectManager>::isShutdown = false;
41 
42 // static
44 
45 
46 BEGIN_EVENT_TABLE(ProjectManager, wxEvtHandler)
47 END_EVENT_TABLE()
48 
49 class NullProjectManagerUI : public cbProjectManagerUI
50 {
51  public:
52  cbAuiNotebook* GetNotebook() override { return nullptr; }
53  cbTreeCtrl* GetTree() override { return nullptr; }
54  void RebuildTree() override {}
55  void FreezeTree() override {}
56  void UnfreezeTree(bool force = false) override { (void)force; }
57  wxTreeItemId GetTreeSelection() override { return wxTreeItemId(); }
58  void UpdateActiveProject(cbProject* WXUNUSED(oldProject), cbProject* WXUNUSED(newProject), bool WXUNUSED(refresh)) override {}
59  void RemoveProject(cbProject* WXUNUSED(project)) override {}
60  void BeginLoadingWorkspace() override {}
61  void CloseWorkspace() override {}
62  void FinishLoadingProject(cbProject* WXUNUSED(project), bool WXUNUSED(newAddition), FilesGroupsAndMasks* WXUNUSED(fileGroups)) override {}
63  void FinishLoadingWorkspace(cbProject* WXUNUSED(activeProject), const wxString& WXUNUSED(workspaceTitle)) override {}
64  void ShowFileInTree(ProjectFile& WXUNUSED(projectFile)) override {}
65  bool QueryCloseAllProjects() override { return true; }
66  bool QueryCloseProject(cbProject* WXUNUSED(proj), bool dontsavefiles = false) override { (void)dontsavefiles; return true; }
67  bool QueryCloseWorkspace() override { return true; }
68  int AskForBuildTargetIndex(cbProject* project = nullptr) override { (void)project; return -1; }
69  wxArrayInt AskForMultiBuildTargetIndex(cbProject* project = nullptr) override { (void)project; return wxArrayInt(); }
70  void ConfigureProjectDependencies(cbProject* base = nullptr) override { (void)base; }
71  void SwitchToProjectsPage() override {}
72 };
73 
74 // class constructor
76  m_ui(new NullProjectManagerUI),
77  m_pWorkspace(nullptr),
78  m_IsLoadingProject(false),
79  m_IsLoadingWorkspace(false),
80  m_IsClosingProject(false),
81  m_IsClosingWorkspace(false),
82  m_InitialDir(_T("")),
83  m_CanSendWorkspaceChanged(false),
84  m_RunningPlugin(nullptr)
85 {
87  m_pActiveProject = nullptr;
88  m_pProjectToActivate = nullptr;
89  m_pProjects = new ProjectsArray;
90  m_pProjects->Clear();
91 
93 
94  // register event sinks
96 
97  // Event handling. This must be THE LAST THING activated on startup.
98  // Constructors and destructors must always follow the LIFO rule:
99  // Last in, first out.
100  Manager::Get()->GetAppWindow()->PushEventHandler(this);
101 }
102 
103 // class destructor
105 {
106  // this is a core manager, so it is removed when the app is shutting down.
107  // in this case, the app has already un-hooked us, so no need to do it ourselves...
108 // Manager::Get()->GetAppWindow()->RemoveEventHandler(this);
109 
110  delete m_pWorkspace;
111  m_pWorkspace = nullptr;
112 
113  int count = m_pProjects->GetCount();
114  for (int i = 0; i < count; ++i)
115  {
116  cbProject* project = m_pProjects->Item(i);
117  if (project)
118  delete project;
119  }
120  m_pProjects->Clear();
121 
122  delete m_pProjects;
123  m_pProjects = nullptr;
124 
125  delete m_pFileGroups;
126  m_pFileGroups = nullptr;
127 
128  delete m_ui;
129  m_ui = nullptr;
130 }
131 
132 void ProjectManager::SetUI(cbProjectManagerUI *ui)
133 {
134  delete m_ui;
135  m_ui = ui;
136 }
137 
139 {
140  wxString path = Manager::Get()->GetConfigManager(_T("project_manager"))->Read(_T("default_path"), wxEmptyString);
141  if (!path.IsEmpty() && path.Last() != _T('/') && path.Last() != _T('\\'))
142  path.Append(wxFILE_SEP_PATH);
143  return path;
144 }
145 
147 {
148  Manager::Get()->GetConfigManager(_T("project_manager"))->Write(_T("default_path"), path);
149 }
150 
152 {
153  int count = m_pProjects->GetCount();
154  for (int i = 0; i < count; ++i)
155  {
156  if (m_pProjects->Item(i) == project)
157  return true;
158  }
159  return false;
160 }
161 
162 void ProjectManager::SetProject(cbProject* project, bool refresh)
163 {
164  bool activeProjectChanged = false;
165  if (project != m_pActiveProject)
166  {
167  // Only set workspace as modified, if there was an active project before
169  activeProjectChanged = true;
170  }
171  else
172  return; // already active
173 
174  cbProject *oldActiveProject = m_pActiveProject;
175  m_pActiveProject = project;
176 
177  m_ui->UpdateActiveProject(oldActiveProject, m_pActiveProject, refresh);
178 
179  if (activeProjectChanged)
181 
183  event.SetProject(m_pActiveProject);
185 }
186 
188 {
189  if (filename.IsEmpty())
190  return nullptr;
191  wxString realFile = realpath(filename);
192  int count = m_pProjects->GetCount();
193  for (int i = 0; i < count; ++i)
194  {
195  cbProject* project = m_pProjects->Item(i);
196  if (project)
197  {
198 #ifdef __WXMSW__
199  // MS Windows Filenames are case-insensitive, we have to
200  // avoid opening the same project if the files are only
201  // different in upper/lowercase.
202  if (project->GetFilename().Lower().Matches(realFile.Lower()))
203  return project;
204 #else
205  if (project->GetFilename().Matches(realFile))
206  return project;
207 #endif
208  }
209  }
210  return nullptr;
211 }
212 
213 cbProject* ProjectManager::LoadProject(const wxString& filename, bool activateIt)
214 {
215  cbProject* result = nullptr;
216  if (!wxFileExists(filename) || !BeginLoadingProject())
217  {
218  return nullptr;
219  }
220 
221  // "Try" block (loop which only gets executed once)
222  // These blocks are extremely useful in constructs that need
223  // premature exits. Instead of having multiple return points,
224  // multiple return values and/or gotos,
225  // you just break out of the loop (which only gets executed once) to exit.
226  do
227  {
228  cbProject* project = IsOpen(filename);
229  if (project)
230  {
231  // already open
232  result = project;
233  break;
234  }
235 
236  if (FileTypeOf(filename) == ftCodeBlocksProject)
237  {
238  project = new cbProject(filename);
239 
240  // We need to do this because creating cbProject allows the app to be
241  // closed in the middle of the operation. So the class destructor gets
242  // called in the middle of a method call.
243 
244  if (!project->IsLoaded())
245  {
246  delete project;
247  break;
248  }
249 
250  result = project;
251  }
252  else // !ftCodeBlocksProject
253  {
254  // the plugin handler should call begin/end on its own...
255  EndLoadingProject(nullptr);
256 
258  if (plugin)
259  plugin->OpenFile(filename);
260  }
261 
262  break;
263  } while (false);
264  // we 're done
265 
266  EndLoadingProject(result);
267  if (activateIt)
268  {
270  // postpone the call to SetProject() until EndLoadingWorkspace() is called
271  // (we must call RebuildTree() before SetProject() is called)
272  m_pProjectToActivate = result;
273  else
274  {
275  // I don't think we need to refresh the tree one more time.
276  // This is already done in the EndLoadingProject call.
277  SetProject(result, false);
278  }
279  }
280 
281  return result;
282 }
283 
285 {
286  m_ui->FreezeTree();
287 
288  bool workspaceModified = m_pWorkspace ? m_pWorkspace->GetModified() : false;
289  wxString name = project->GetFilename();
291  ProjectsArray projectDependencies; // all projects that the reloaded project depends on
292  ProjectsArray projectsDependingOnReloaded; // all projects that depend on the reloaded project
293 
294  for (DepsMap::iterator it = m_ProjectDeps.begin(); it != m_ProjectDeps.end(); ++it)
295  {
296  if (!it->second)
297  continue;
298 
299  if (it->first == project)
300  projectDependencies = *(it->second);
301  else
302  {
303  if (it->second->Index(project) != wxNOT_FOUND)
304  projectsDependingOnReloaded.push_back(it->first);
305  }
306  }
307 
308 
309  int originalPosition = m_pProjects->Index(project);
310 
311  CloseProject(project);
312  cbProject *loadedProject = LoadProject(name);
313 
314  if (loadedProject)
315  {
316  if (!projectDependencies.empty())
317  {
318  for (ProjectsArray::iterator it = projectDependencies.begin(); it != projectDependencies.end(); ++it)
319  AddProjectDependency(loadedProject, *it);
320  }
321  if (!projectsDependingOnReloaded.empty())
322  {
323  for (ProjectsArray::iterator it = projectsDependingOnReloaded.begin();
324  it != projectsDependingOnReloaded.end(); ++it)
325  {
326  AddProjectDependency(*it, loadedProject);
327  }
328  }
329 
330  int loadedPosition = -1;
331  int index = 0;
332  cbProject *projectToActivate = nullptr;
333  for (ProjectsArray::iterator it = m_pProjects->begin(); it != m_pProjects->end(); ++it, ++index)
334  {
335  if (*it == loadedProject)
336  loadedPosition = index;
337 
338  if ((*it)->GetFilename() == activeProjectName)
339  projectToActivate = *it;
340  }
341 
342  m_pProjects->RemoveAt(loadedPosition);
343  m_pProjects->Insert(loadedProject, originalPosition);
344 
345  if (projectToActivate)
346  m_pActiveProject = projectToActivate;
347 
348  m_ui->RebuildTree();
349 
350  if (m_pWorkspace)
351  m_pWorkspace->SetModified(workspaceModified);
352  }
353 
354  m_ui->UnfreezeTree();
355 }
356 
358 {
359  if (!filename.IsEmpty() && wxFileExists(filename))
360  {
361  if (cbMessageBox(_("Project file already exists.\nAre you really sure you want to OVERWRITE it?"),
362  _("Confirmation"), wxYES_NO | wxICON_QUESTION) == wxID_YES)
363  {
364  if (!wxRemoveFile(filename))
365  {
366  cbMessageBox(_("Couldn't remove the old project file to create the new one.\nThe file might be read-only?!"),
367  _("Error"), wxICON_WARNING);
368  return nullptr;
369  }
370  }
371  else
372  return nullptr;
373  }
374 
375  cbProject* prj = IsOpen(filename);
376  if (!prj && BeginLoadingProject())
377  {
378  prj = new cbProject(filename);
379  EndLoadingProject(prj);
381  }
382  return prj;
383 }
384 
386 {
387  if (!dontsave)
388  {
389  if (!m_ui->QueryCloseAllProjects())
390  return false;
391  }
392 
393  m_ui->FreezeTree();
394  m_IsClosingProject = true;
395  while (m_pProjects->GetCount() != 0)
396  {
397 // Commented it by Heromyth
398 // if (!CloseActiveProject(true))
399  if (!CloseProject(m_pProjects->Item(0), true, false))
400  {
401  m_ui->UnfreezeTree(true);
402  m_IsClosingProject = false;
403  return false;
404  }
405  }
406 
408  m_ui->RebuildTree();
409  m_ui->UnfreezeTree(true);
410 
411  if (!m_InitialDir.IsEmpty())
413  m_IsClosingProject = false;
415 
416  return true;
417 }
418 
419 bool ProjectManager::CloseProject(cbProject* project, bool dontsave, bool refresh)
420 {
421  if (!project)
422  return true;
423  if (project->GetCurrentlyCompilingTarget())
424  return false;
425  if (!dontsave)
426  {
427  if (!m_ui->QueryCloseProject(project))
428  return false;
429  }
430 
431  bool wasActive = project == m_pActiveProject;
432  if (wasActive)
433  m_pActiveProject = nullptr;
434 
435  int index = m_pProjects->Index(project);
436  if (index == wxNOT_FOUND)
437  return false;
438 
439  // CloseProject is also called by CloseAllProjects, so we need to save
440  // the state of m_IsClosingProject.
441  bool isClosingOtherProjects = m_IsClosingProject;
442  m_IsClosingProject = true;
444  project->SaveLayout();
445 
446  if (m_pWorkspace)
447  m_pWorkspace->SetModified(true);
448 
450  ClearProjectDependencies(project);
451  m_pProjects->Remove(project);
452 
453  // moved here from cbProject's destructor, because by then
454  // the list of project files was already emptied...
456  event.SetProject(project);
458 
459  project->CloseAllFiles(true);
460  if (refresh)
461  m_ui->RemoveProject(project);
462  if (wasActive && m_pProjects->GetCount())
463  SetProject(m_pProjects->Item(0), refresh);
464  delete project;
465  if (!m_InitialDir.IsEmpty()) // Restore the working directory
467  m_IsClosingProject = isClosingOtherProjects;
469  return true;
470 }
471 
473 {
474  if (!CloseProject(m_pActiveProject, dontsave))
475  return false;
476  if (m_pProjects->GetCount() > 0)
477  SetProject(m_pProjects->Item(0));
478  return true;
479 }
480 
482 {
483  if (!project)
484  return false;
485  if (project->Save())
486  {
487  m_ui->RebuildTree();
488  return true;
489  }
490  return false;
491 }
492 
494 {
495  if (!project)
496  return false;
497 
498  if (project->SaveAs())
499  {
500  m_ui->RebuildTree();
501  return true;
502  }
503  return false;
504 }
505 
507 {
509 }
510 
512 {
514 }
515 
517 {
518  m_ui->FreezeTree();
519  int prjCount = m_pProjects->GetCount();
520  int count = 0;
521  for (int i = 0; i < prjCount; ++i)
522  {
523  cbProject* project = m_pProjects->Item(i);
524  if (project)
525  {
526  bool isModified = project->GetModified();
527  if (isModified && SaveProject(project))
528  ++count;
529  }
530  }
531  m_ui->UnfreezeTree(true);
532  return count == prjCount;
533 }
534 
536 {
537  if (!m_pWorkspace)
538  {
539  m_pWorkspace = new cbWorkspace(_T(""));
540  m_pWorkspace->SetTitle(_("Workspace"));
541  m_pWorkspace->SetModified(false);
542  }
543  return m_pWorkspace;
544 }
545 
546 
548 {
549  if ( !BeginLoadingWorkspace() )
550  return false;
551 
552  cbWorkspace *temp = new cbWorkspace(filename);
553 
554  // Do this after the c-tor call, because the c-tor calls methods which use call GetWorkspace
555  // and if the pointer is equal to nullptr the GetWorkspace will create a new one and we'll have
556  // a leak.
557  delete m_pWorkspace;
558  m_pWorkspace = temp;
559 
561 
562  if (m_pProjects->GetCount() > 0 && !m_pActiveProject)
563  SetProject(m_pProjects->Item(0), false);
564 
565  if ( m_pWorkspace && m_pWorkspace->IsOK() )
566  {
567  // Fire-up event here, where we're sure there's an active project
570 
571  return true;
572  }
573 
574  return false;
575 }
576 
578 {
579  return GetWorkspace()->Save();
580 }
581 
583 {
584  return GetWorkspace()->SaveAs(filename);
585 }
586 
588 {
589  bool result = false;
590  m_IsClosingWorkspace = true;
591 
593  Manager::Get()->GetPluginManager()->NotifyPlugins(eventBegin);
594 
595  if (m_pWorkspace)
596  {
598  {
599  m_IsClosingWorkspace = false;
600  return false;
601  }
602  // m_ui->QueryCloseWorkspace asked for saving workspace AND projects, no need to do again
603  if (!CloseAllProjects(true))
604  {
605  m_IsClosingWorkspace = false;
606  return false;
607  }
608 
609  delete m_pWorkspace;
610  m_pWorkspace = nullptr;
611 
612  m_ui->CloseWorkspace();
613  result = true;
614  }
615  else
616  result = CloseAllProjects(false);
617  m_IsClosingWorkspace = false;
618 
620  Manager::Get()->GetPluginManager()->NotifyPlugins(eventComplete);
621 
623  return result;
624 }
625 
626 // This function is static for your convenience :)
628 {
630  return true;
631 
633  if (!projman)
634  return true;
635 
636  return projman->IsLoadingOrClosing();
637 }
638 
640 {
642 }
643 
645 {
646  return m_IsLoadingProject;
647 }
648 
650 {
651  return m_IsLoadingWorkspace;
652 }
653 
655 {
657 }
658 
660 {
661  return m_IsClosingProject;
662 }
663 
665 {
666  return m_IsClosingWorkspace;
667 }
668 
669 
670 int ProjectManager::DoAddFileToProject(const wxString& filename, cbProject* project, wxArrayInt& targets)
671 {
672  if (!project)
673  return 0;
674 
675  // do we have to ask for target?
676  if (targets.GetCount() == 0)
677  {
678  // if project has only one target, use this
679  if (project->GetBuildTargetsCount() == 1)
680  targets.Add(0);
681  // else display multiple target selection dialog
682  else
683  {
684  targets = m_ui->AskForMultiBuildTargetIndex(project);
685  if (targets.GetCount() == 0)
686  return 0;
687  }
688  }
689 
690  // make sure filename is relative to project path
691  wxFileName fname(filename);
693  fname.MakeRelativeTo(project->GetBasePath());
694 
695  // add the file to the project first
696  ProjectFile* pf = project->AddFile(-1, fname.GetFullPath());
697  if (pf)
698  {
699  // if the file was added successfully,
700  // add to this file the selected targets...
701  for (size_t i = 0; i < targets.GetCount(); ++i)
702  {
703  ProjectBuildTarget* target = project->GetBuildTarget(targets[i]);
704  if (target)
705  pf->AddBuildTarget(target->GetTitle());
706  }
707  }
708  return targets.GetCount();
709 }
710 
711 int ProjectManager::AddFileToProject(const wxString& filename, cbProject* project, int target)
712 {
713  if (!project)
714  project = GetActiveProject();
715 
716  wxArrayInt targets;
717  targets.Add(target);
718  if (AddFileToProject(filename, project, targets) == 1)
719  return targets[0];
720  return -1;
721 }
722 
723 int ProjectManager::AddFileToProject(const wxString& filename, cbProject* project, wxArrayInt& targets)
724 {
725  if (!project)
726  project = GetActiveProject();
727 
728  int ret = DoAddFileToProject(filename, project, targets);
729  if (ret > 0)
730  {
732  event.SetProject(project);
733  event.SetString(filename);
735  }
736  return ret;
737 }
738 
739 int ProjectManager::AddMultipleFilesToProject(const wxArrayString& filelist, cbProject* project, int target)
740 {
741  if (!project)
742  project = GetActiveProject();
743 
744  wxArrayInt targets;
745  targets.Add(target);
746  if (AddMultipleFilesToProject(filelist, project, targets) == 1)
747  return targets[0];
748  return -1;
749 }
750 
752 {
753  wxProgressDialog progress(_("Project Manager"), _("Please wait while adding files to project..."), filelist.GetCount(), Manager::Get()->GetAppFrame());
754 
755  if (!project)
756  project = GetActiveProject();
757 
758  if (project)
759  {
760  project->BeginAddFiles();
761 
762  wxArrayString addedFiles; // to know which files were added successfully
763  for (unsigned int i = 0; i < filelist.GetCount(); ++i)
764  {
765  if (DoAddFileToProject(filelist[i], project, targets) != 0)
766  addedFiles.Add(filelist[i]);
767  progress.Update(i);
768  }
769 
770  if (addedFiles.GetCount() != 0)
771  {
772  for (unsigned int i = 0; i < addedFiles.GetCount(); ++i)
773  {
775  event.SetProject(project);
776  event.SetString(addedFiles[i]);
778  }
779  }
780 
781  project->EndAddFiles();
782  }
783 
784  return targets.GetCount();
785 }
786 
788 {
789  if (!base || !dependsOn)
790  return false;
791 
792  // 1st check is both projects are the same one
793  if (base == dependsOn)
794  return true;
795 
796  const ProjectsArray* arr = GetDependenciesForProject(dependsOn);
797  if (arr)
798  {
799  // now check deeper
800  for (size_t i = 0; i < arr->GetCount(); ++i)
801  {
802  if (CausesCircularDependency(base, arr->Item(i)))
803  return true;
804  }
805  }
806 
807  // if we reached here, no possibility of circular dependency :)
808  return false;
809 }
810 
812 {
813  if (!base || !dependsOn)
814  return false;
815 
816  // avoid circular dependencies
817  if ( CausesCircularDependency(base, dependsOn) )
818  return false;
819 
820  ProjectsArray* arr = nullptr;
821  DepsMap::iterator it = m_ProjectDeps.find(base);
822  if (it == m_ProjectDeps.end())
823  {
824  // create a ProjectsArray* to hold the dependencies for base
825  arr = new ProjectsArray;
826  m_ProjectDeps[base] = arr;
827  }
828  else
829  arr = it->second;
830 
831  // add dependency only if not already there
832  if (arr && arr->Index(dependsOn) == wxNOT_FOUND)
833  {
834  arr->Add(dependsOn);
835  if (m_pWorkspace)
836  m_pWorkspace->SetModified(true);
837  Manager::Get()->GetLogManager()->DebugLog(F(_T("%s now depends on %s (%lu deps)"), base->GetTitle().wx_str(), dependsOn->GetTitle().wx_str(), static_cast<unsigned long>(arr->GetCount())));
838  }
839  return true;
840 }
841 
843 {
844  if (!base || !doesNotDependOn)
845  return;
846 
847  DepsMap::iterator it = m_ProjectDeps.find(base);
848  if (it == m_ProjectDeps.end())
849  return; // nothing to remove
850 
851  ProjectsArray* arr = it->second;
852  arr->Remove(doesNotDependOn);
853 
854  Manager::Get()->GetLogManager()->DebugLog(F(_T("%s now does not depend on %s (%lu deps)"), base->GetTitle().wx_str(), doesNotDependOn->GetTitle().wx_str(), static_cast<unsigned long>(arr->GetCount())));
855  // if it was the last dependency, delete the array
856  if (!arr->GetCount())
857  {
858  m_ProjectDeps.erase(it);
859  delete arr;
860  }
861  if (m_pWorkspace)
862  m_pWorkspace->SetModified(true);
863 }
864 
866 {
867  if (!base)
868  return;
869  DepsMap::iterator it = m_ProjectDeps.find(base);
870  if (it == m_ProjectDeps.end())
871  return; // nothing to remove
872 
873  delete it->second;
874  m_ProjectDeps.erase(it);
875  if (m_pWorkspace)
876  m_pWorkspace->SetModified(true);
877 
878  Manager::Get()->GetLogManager()->DebugLog(_T("Removed all deps from ") + base->GetTitle());
879 }
880 
882 {
883  if (!base)
884  return;
885  DepsMap::iterator it = m_ProjectDeps.begin();
886  while (it != m_ProjectDeps.end())
887  {
888  if (it->first == base)
889  {
890  ++it;
891  continue;
892  }
893 
894  ProjectsArray* arr = it->second;
895  // only check projects that do have a dependencies array
896  if (!arr)
897  {
898  ++it;
899  continue;
900  }
901 
902  int index = arr->Index(base);
903  if (index != wxNOT_FOUND)
904  arr->RemoveAt(index);
905 
906  if (m_pWorkspace)
907  m_pWorkspace->SetModified(true);
908 
909  // if it was the last dependency, delete the array
910  if (!arr->GetCount())
911  {
912  DepsMap::iterator it2 = it++;
913  m_ProjectDeps.erase(it2);
914  delete arr;
915  }
916  else
917  ++it;
918  }
919  Manager::Get()->GetLogManager()->DebugLog(F(_T("Removed %s from all deps"), base->GetTitle().wx_str()));
920 }
921 
923 {
924  DepsMap::iterator it = m_ProjectDeps.find(base);
925  if (it != m_ProjectDeps.end())
926  return it->second;
927  return nullptr;
928 }
929 
930 // events
931 
933 {
934  // we do not send the workspace loaded event yet because: a) We don't know
935  // if there's a workspace yet, and b) app.cpp hasn't finished init'ing yet.
936  // We'll let app.cpp send the workspace changed for us when it's done.
938  event.Skip();
939 }
940 
942 {
943  // We use IsBusy() to check *ALL* the conditions: If we're in the process of
944  // opening or closing a project, we cannot send the event yet.
945  // Specifically, *DO NOT* send the event if the application hasn't been
946  // initialized yet!!
948  {
952  }
953 }
954 
956 {
957  if (!pfile)
958  {
959  Manager::Get()->GetLogManager()->DebugLog(_T("Invalid project file!"));
960  return;
961  }
962 
963  if (pfile->AutoGeneratedBy())
964  {
965  cbMessageBox(_("Can't remove file because it is auto-generated..."), _("Error"));
966  return;
967  }
968 
969  if (!project)
970  project = pfile->GetParentProject(); // should actually not be necessary
971 
972  wxString filename = pfile->file.GetFullPath();
973  project->RemoveFile(pfile);
974 
976  evt.SetProject(project);
977  evt.SetString(filename);
979 
980  Manager::Get()->GetLogManager()->DebugLog(_T("Removed ") + filename + _T(" from ") + project->GetTitle());
981 }
982 
984 {
985  if (m_IsLoadingProject)
986  return false;
987 
988  if (!Manager::Get()->GetPluginManager()->FindPluginByName(_T("Compiler")))
989  {
990  cbMessageBox(_("Deactivating the compiler plugin is most unwise.\n\nIf you intend to open a project, you have to re-activate the compiler plugin first."), _("Error"));
991  return false;
992  }
993 
994  // disallow application shutdown while opening files
995  s_CanShutdown = false;
996  // flag project loading
997  m_IsLoadingProject = true;
998 
999  return true;
1000 }
1001 
1003 {
1004  s_CanShutdown = true;
1005  if (!m_IsLoadingProject)
1006  return;
1007 
1008  if (project)
1009  {
1010  bool newAddition = m_pProjects->Index(project) == -1;
1011  if (newAddition)
1012  {
1013  m_pProjects->Add(project);
1014  project->LoadLayout();
1015  }
1016 
1017  if (!m_IsLoadingWorkspace)
1018  m_ui->FinishLoadingProject(project, newAddition, m_pFileGroups);
1019 
1020  if (m_pWorkspace)
1021  m_pWorkspace->SetModified(true);
1022 
1023  // if loading a workspace, avoid sending the event now
1024  // we 'll send them after all projects have been loaded
1025  // (look in LoadWorkspace)
1026  if (!m_IsLoadingWorkspace)
1027  {
1028  // notify plugins that the project is loaded
1029  // moved here from cbProject::Open() because code-completion
1030  // kicks in too early and the perceived loading time is long...
1032  event.SetProject(project);
1033  Manager::Get()->ProcessEvent(event);
1034 
1035  // finally, display project notes (if appropriate)
1036  if (project->GetShowNotesOnLoad())
1037  project->ShowNotes(true);
1038  }
1039  }
1040 
1041  /* While loading the project layout, the ProjectManager is still working.
1042  Thus it should be set to Not Busy only at the end.*/
1043  m_IsLoadingProject = false;
1044 
1045  // sort out any global user vars that need to be defined now (in a batch) :)
1046  // but only if not loading workspace (else LoadWorkspace() will handle this)
1047  if (!m_IsLoadingWorkspace)
1049 
1050  WorkspaceChanged();
1051 }
1052 
1054 {
1056  return false;
1057 
1058  m_IsLoadingWorkspace = true;
1059  if (!CloseWorkspace())
1060  {
1061  m_IsLoadingWorkspace = false;
1062  return false; // didn't close
1063  }
1064 
1066 
1067  return true;
1068 }
1069 
1071 {
1072  if (!m_IsLoadingWorkspace)
1073  return;
1074 
1075  m_IsLoadingWorkspace = false;
1076  if (!m_pWorkspace)
1077  return;
1078 
1079  if (m_pWorkspace->IsOK())
1080  {
1082  {
1084  m_pProjectToActivate = nullptr;
1085  }
1086 
1088 
1089  // sort out any global user vars that need to be defined now (in a batch) :)
1091 
1092  int numNotes = 0;
1093 
1094  // and now send the project loaded events
1095  // since we were loading a workspace, these events were not sent before
1096  for (size_t i = 0; i < m_pProjects->GetCount(); ++i)
1097  {
1098  cbProject* project = m_pProjects->Item(i);
1099 
1100  // notify plugins that the project is loaded
1101  // moved here from cbProject::Open() because code-completion
1102  // kicks in too early and the perceived loading time is long...
1104  event.SetProject(project);
1106 
1107  // since we 're iterating anyway, let's count the project notes that should be displayed
1108  if (project->GetShowNotesOnLoad() && !project->GetNotes().IsEmpty())
1109  ++numNotes;
1110  }
1111 
1112  // finally, display projects notes (if appropriate)
1113  if (numNotes)
1114  {
1115  if (numNotes == 1 || // if only one project has notes, don't bother asking
1116  cbMessageBox(wxString::Format(_("%d projects contain notes that should be displayed on-load.\n"
1117  "Do you want to display them now, one after the other?"),
1118  numNotes),
1119  _("Display project notes?"),
1121  {
1122  for (size_t i = 0; i < m_pProjects->GetCount(); ++i)
1123  {
1124  cbProject* project = m_pProjects->Item(i);
1125  if (project->GetShowNotesOnLoad())
1126  project->ShowNotes(true);
1127  }
1128  }
1129  }
1130 
1131  WorkspaceChanged();
1132  }
1133  else
1134  CloseWorkspace();
1135 }
1136 
1138 {
1139  m_RunningPlugin = plugin;
1140 }
1141 
1143 {
1144  return m_RunningPlugin;
1145 }
1146 
1148  bool isRelative, bool isUnixFilename)
1149 {
1150  for (size_t i = 0; i < m_pProjects->GetCount(); ++i)
1151  {
1152  cbProject* prj = m_pProjects->Item(i);
1153  ProjectFile *temp = prj->GetFileByFilename(file, isRelative, isUnixFilename);
1154  if (temp)
1155  {
1156  if (resultFile)
1157  *resultFile = temp;
1158  return prj;
1159  }
1160  }
1161  if (resultFile)
1162  *resultFile = nullptr;
1163  return nullptr;
1164 }
ProjectFile * GetFileByFilename(const wxString &filename, bool isRelative=true, bool isUnixFilename=false)
Access a file of the project.
Definition: cbproject.cpp:1049
wxString F(const wxChar *msg,...)
sprintf-like function
Definition: logmanager.h:20
void SetDefaultPath(const wxString &path)
Set the default path for new projects.
Base class for plugins.
Definition: cbplugin.h:84
bool IsLoadingProject()
Check if the project manager is loading a project.
EVTIMPORT const wxEventType cbEVT_PROJECT_FILE_ADDED
Definition: sdk_events.cpp:104
bool IsClosingProject()
Check if the project manager is closing a project.
DepsMap m_ProjectDeps
void RemoveProject(cbProject *WXUNUSED(project)) override
EVTIMPORT const wxEventType cbEVT_WORKSPACE_CLOSING_COMPLETE
Definition: sdk_events.cpp:114
bool Matches(const wxString &mask) const
static wxString GetCwd(const wxString &volume=wxEmptyString)
bool IsProjectStillOpen(cbProject *project)
Is this a valid project? (i.e.
PluginManager * GetPluginManager() const
Definition: manager.cpp:444
bool wxRemoveFile(const wxString &file)
#define wxICON_QUESTION
void FreezeTree() override
Stop the tree control from updating.
virtual bool IsOK() const
Was this workspace loaded successfully?
Definition: cbworkspace.h:87
bool SaveActiveProject()
Save the active project to disk.
#define wxICON_WARNING
void SetIsRunning(cbPlugin *plugin)
This method should be called when the applications is started by a plugin.
cbProject * IsOpen(const wxString &filename)
Check if a project is open based on the project&#39;s filename.
void ReloadProject(cbProject *project)
Reloads a project and tries to keep everything the same (project order, dependencies, active project)
void RebuildTree() override
Rebuild the project manager&#39;s tree.
ConfigManager * GetConfigManager(const wxString &name_space) const
Definition: manager.cpp:474
UserVariableManager * GetUserVariableManager() const
Definition: manager.cpp:464
virtual void RebuildTree()=0
Rebuild the project manager&#39;s tree.
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
wxString m_InitialDir
bool SaveWorkspaceAs(const wxString &filename)
Save the open workspace under a different filename.
wxString Lower() const
void RemoveProjectFromAllDependencies(cbProject *base)
Removes the project base from being a dependency of any other project.
virtual bool GetModified() const
Is this workspace modified?
Definition: cbworkspace.h:102
static bool IsAppShuttingDown()
Definition: manager.cpp:333
EVTIMPORT const wxEventType cbEVT_WORKSPACE_CLOSING_BEGIN
Definition: sdk_events.cpp:113
int DoAddFileToProject(const wxString &filename, cbProject *project, wxArrayInt &targets)
bool wxFileExists(const wxString &filename)
bool QueryCloseProject(cbProject *WXUNUSED(proj), bool dontsavefiles=false) override
wxFileName file
The full filename of this file.
Definition: projectfile.h:126
void RemoveFileFromProject(ProjectFile *pfile, cbProject *project)
Remove a file from a project.
virtual void SetTitle(const wxString &title)
Set the workspace&#39;s title.
wxTreeItemId GetTreeSelection() override
Get the selection of the project manager&#39;s tree (GUI).
cbTreeCtrl * GetTree() override
Retrieve a pointer to the project manager&#39;s tree (GUI).
virtual bool QueryCloseAllProjects()=0
Checks whether all projects are saved.
virtual bool Save(bool force=false)
Save the workspace.
wxString GetDefaultPath()
Retrieve the default path for new projects.
EVTIMPORT const wxEventType cbEVT_PROJECT_ACTIVATE
Definition: sdk_events.cpp:99
void UnfreezeTree(bool force=false) override
Le the tree control be updated again.
cbPlugin * m_RunningPlugin
#define _T(string)
virtual void SetModified(bool modified)
Mark the workspace as modified or not.
virtual void UnfreezeTree(bool force=false)=0
Le the tree control be updated again.
#define wxYES_NO
void EndAddFiles()
Notify that file(s) addition finished.
Definition: cbproject.cpp:600
bool SaveAs()
Save the project under a different name.
Definition: cbproject.cpp:433
bool IsLoading()
Check if the project manager is loading a project/workspace.
DLLIMPORT FileType FileTypeOf(const wxString &filename)
Definition: globals.cpp:285
int AddMultipleFilesToProject(const wxArrayString &filelist, cbProject *project, int target=-1)
Add multiple files to a project.
bool m_CanSendWorkspaceChanged
bool SetCwd() const
Base class for mime plugins.
Definition: cbplugin.h:684
#define wxNOT_FOUND
int AskForBuildTargetIndex(cbProject *project=nullptr) override
Utility function.
Represents a file in a Code::Blocks project.
Definition: projectfile.h:39
ProjectBuildTarget * GetCurrentlyCompilingTarget()
Get a pointer to the currently compiling target.
Definition: cbproject.h:478
A generic Code::Blocks event.
Definition: sdk_events.h:20
ProjectsArray * m_pProjects
bool RemoveFile(ProjectFile *pf)
Remove a file from the project.
Definition: cbproject.cpp:860
void BeginAddFiles()
Notify that file(s) will be added shortly.
Definition: cbproject.cpp:593
wxWindow * GetAppWindow() const
Definition: manager.cpp:424
~ProjectManager() override
cbMimePlugin * GetMIMEHandlerForFile(const wxString &filename)
EditorManager * GetEditorManager() const
Definition: manager.cpp:434
bool CloseAllFiles(bool dontsave=false)
Close all project files.
Definition: cbproject.cpp:1094
bool CausesCircularDependency(cbProject *base, cbProject *dependsOn)
Checks for circular dependencies between base and dependsOn.
void BeginLoadingWorkspace() override
ProjectManager * GetProjectManager() const
Functions returning pointers to the respective sub-manager instances.
Definition: manager.cpp:429
bool CloseActiveProject(bool dontsave=false)
Close the active project.
bool MakeRelativeTo(const wxString &pathBase=wxEmptyString, wxPathFormat format=wxPATH_NATIVE)
bool BeginLoadingProject()
Begins the project loading process.
void Write(const wxString &name, const wxString &value, bool ignoreEmpty=false)
Event functor class.
Definition: cbfunctor.h:37
Represents a Code::Blocks project.
Definition: cbproject.h:96
cbProject * NewProject(const wxString &filename=wxEmptyString)
Create a new empty project.
EVTIMPORT const wxEventType cbEVT_WORKSPACE_LOADING_COMPLETE
Definition: sdk_events.cpp:112
virtual const wxString & GetFilename() const
bool SaveActiveProjectAs()
Save the active project to disk, asking for a filename.
null_pointer_t nullptr
Definition: nullptr.cpp:16
cbWorkspace * m_pWorkspace
cbPlugin * GetIsRunning() const
Return a pointer to the plugin which is running the application.
cbProject * LoadProject(const wxString &filename, bool activateIt=true)
Load a project from disk.
void ConfigureProjectDependencies(cbProject *base=nullptr) override
Displays a dialog to setup project dependencies.
virtual const wxString & GetTitle() const
Read the target&#39;s title.
bool IsLoadingOrClosing()
Check if the project manager is loading/closing a project/workspace.
wxFrame * GetAppFrame() const
Definition: manager.cpp:419
bool CloseProject(cbProject *project, bool dontsave=false, bool refresh=true)
Close a project.
void ShowNotes(bool nonEmptyOnly, bool editable=false)
Show project notes now.
Definition: cbproject.cpp:1633
A workspace class.
Definition: cbworkspace.h:26
static bool IsBatchBuild()
Definition: manager.h:66
void OnAppDoneStartup(CodeBlocksEvent &event)
LogManager * GetLogManager() const
Definition: manager.cpp:439
void EndLoadingWorkspace()
Ends the workspace loading process.
cbProject * GetActiveProject()
Retrieve the active project.
wxString Read(const wxString &key, const wxString &defaultVal=wxEmptyString)
void RemoveProjectDependency(cbProject *base, cbProject *doesNotDependOn)
Removes a project dependency.
virtual wxString GetBasePath() const
Read the target&#39;s base path, e.g. if GetFilename() returns "/usr/local/bin/xxx", base path will retur...
virtual bool QueryCloseProject(cbProject *proj, bool dontsavefiles=false)=0
Checks whether project is saved.
virtual void RemoveProject(cbProject *project)=0
bool BeginLoadingWorkspace()
Begins the workspace loading process.
virtual wxArrayInt AskForMultiBuildTargetIndex(cbProject *project=nullptr)=0
Utility function.
const wxStringCharType * wx_str() const
bool AddProjectDependency(cbProject *base, cbProject *dependsOn)
Adds a project as a dependency of another project.
virtual void FinishLoadingProject(cbProject *project, bool newAddition, FilesGroupsAndMasks *fileGroups)=0
wxString wxEmptyString
FilesGroupsAndMasks * m_pFileGroups
void NotifyPlugins(CodeBlocksEvent &event)
virtual void BeginLoadingWorkspace()=0
const wxString & GetNotes() const
Get notes on the project.
Definition: cbproject.cpp:1600
Definition: manager.h:183
ProjectFile * AddFile(const wxString &targetName, const wxString &filename, bool compile=true, bool link=true, unsigned short int weight=50)
Add a file to the project.
Definition: cbproject.cpp:621
bool SaveProjectAs(cbProject *project)
Save a project to disk, asking for a filename.
bool SaveProject(cbProject *project)
Save a project to disk.
const wxString & _(const wxString &string)
virtual bool SaveAs(const wxString &filename)
Save the workspace under a different filename.
int GetBuildTargetsCount()
Definition: cbproject.h:200
virtual void UpdateActiveProject(cbProject *oldProject, cbProject *newProject, bool refresh)=0
cbProject * m_pProjectToActivate
wxArray< int > wxArrayInt
bool IsClosingWorkspace()
Check if the project manager is closing a workspace.
EVTIMPORT const wxEventType cbEVT_APP_STARTUP_DONE
Definition: sdk_events.cpp:68
static bool IsBusy()
For use with plugins.
ProjectBuildTarget * GetBuildTarget(int index)
Access a build target.
Definition: cbproject.cpp:1392
cbWorkspace * GetWorkspace()
Get the current workspace filename.
void EndLoadingProject(cbProject *project)
Ends the project loading process.
void WorkspaceChanged()
Sends message to the plugins that the workspace has been changed.
wxString & Append(const char *psz)
void SetProject(cbProject *project, bool refresh=true)
Set the active project.
cbAuiNotebook * GetNotebook() override
virtual void CloseWorkspace()=0
bool IsEmpty() const
The entry point singleton for working with projects.
void SetProject(cbProject *project)
Definition: sdk_events.h:42
void CloseWorkspace() override
bool CloseAllProjects(bool dontsave=false)
Close all projects.
cbAuiNotebook * GetNotebook()
Definition: editormanager.h:73
bool GetShowNotesOnLoad() const
Get show project notes on load automatically.
Definition: cbproject.cpp:1614
bool LoadLayout()
Load the project&#39;s layout.
Definition: cbproject.cpp:513
A notebook class This class is derived from wxAuiNotebook, to enhance its abilities.
Definition: cbauibook.h:30
void DebugLog(const wxString &msg, Logger::level lv=Logger::info)
Definition: logmanager.h:146
bool CloseWorkspace()
Close the workspace.
bool ProcessEvent(CodeBlocksEvent &event)
Definition: manager.cpp:246
bool GetModified() const override
Definition: cbproject.cpp:162
EVTIMPORT const wxEventType cbEVT_WORKSPACE_CHANGED
Definition: sdk_events.cpp:111
EVTIMPORT const wxEventType cbEVT_PROJECT_OPEN
Definition: sdk_events.cpp:97
void RegisterEventSink(wxEventType eventType, IEventFunctorBase< CodeBlocksEvent > *functor)
Definition: manager.cpp:550
virtual void FinishLoadingWorkspace(cbProject *activeProject, const wxString &workspaceTitle)=0
void SwitchToProjectsPage() override
Switches the management&#39;s notebook to the Projects tab.
size_t Add(const wxString &str, size_t copies=1)
cbProject * FindProjectForFile(const wxString &file, ProjectFile **resultFile, bool isRelative, bool isUnixFilename)
Return the project which has the file in it, also return the pointer to the ProjectFile object...
cbProject * GetParentProject()
Definition: projectfile.h:93
virtual bool QueryCloseWorkspace()=0
Asks user to save the workspace, projects and files (Yes/No/cancel).
Represents a Code::Blocks project build target.
bool Normalize(int flags=wxPATH_NORM_ALL, const wxString &cwd=wxEmptyString, wxPathFormat format=wxPATH_NATIVE)
bool QueryCloseAllProjects() override
Checks whether all projects are saved.
EVTIMPORT const wxEventType cbEVT_PROJECT_CLOSE
Definition: sdk_events.cpp:96
size_t GetCount() const
cbProjectManagerUI * m_ui
void SetUI(cbProjectManagerUI *ui)
ProjectFile * AutoGeneratedBy() const
If this is an auto-generated file, which file is generating it?
Definition: projectfile.h:201
bool QueryCloseWorkspace() override
Asks user to save the workspace, projects and files (Yes/No/cancel).
bool IsLoadingWorkspace()
Check if the project manager is loading a workspace.
wxUniChar Last() const
bool SaveAllProjects()
Saves all projects to disk.
void ShowFileInTree(ProjectFile &WXUNUSED(projectFile)) override
EVTIMPORT const wxEventType cbEVT_PROJECT_FILE_REMOVED
Definition: sdk_events.cpp:105
bool LoadWorkspace(const wxString &filename=DEFAULT_WORKSPACE)
Load a workspace.
void AddBuildTarget(const wxString &targetName)
Make this file belong to an additional build target.
Definition: projectfile.cpp:78
bool UpdateProjectFiles(cbProject *project)
void ActiveProjectChanged()
virtual void FreezeTree()=0
Stop the tree control from updating.
bool IsLoaded() const
Definition: cbproject.h:105
void MinimizeFreeSpace()
Minmize free horizontal page.
Definition: cbauibook.cpp:480
bool Save()
Save the project.
Definition: cbproject.cpp:482
void UpdateActiveProject(cbProject *WXUNUSED(oldProject), cbProject *WXUNUSED(newProject), bool WXUNUSED(refresh)) override
wxString GetFullPath(wxPathFormat format=wxPATH_NATIVE) const
virtual int OpenFile(const wxString &filename)=0
Open the file.
int AddFileToProject(const wxString &filename, cbProject *project=nullptr, int target=-1)
Add a file to a project.
virtual wxString GetTitle() const
Get the workspace&#39;s title.
Definition: cbworkspace.h:69
cbProject * m_pActiveProject
static wxString Format(const wxString &format,...)
void FinishLoadingProject(cbProject *WXUNUSED(project), bool WXUNUSED(newAddition), FilesGroupsAndMasks *WXUNUSED(fileGroups)) override
bool SaveWorkspace()
Save the open workspace.
void FinishLoadingWorkspace(cbProject *WXUNUSED(activeProject), const wxString &WXUNUSED(workspaceTitle)) override
const ProjectsArray * GetDependenciesForProject(cbProject *base)
Get the array of projects base depends on.
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
bool SaveLayout()
Save the project&#39;s layout.
Definition: cbproject.cpp:499
wxArrayInt AskForMultiBuildTargetIndex(cbProject *project=nullptr) override
Utility function.
DLLIMPORT wxString realpath(const wxString &path)
Definition: globals.cpp:1348
static bool s_CanShutdown
void ClearProjectDependencies(cbProject *base)
Removes all dependencies from project base.