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
6
* $Id: cbproject.cpp 4909 2008-02-27 13:15:26Z mortenmacfly $
7
* $HeadURL: svn://svn.berlios.de/codeblocks/tags/8.02/src/sdk/cbproject.cpp $
10
#include "sdk_precomp.h"
12
#ifndef wxUSE_CHOICEDLG
13
#define wxUSE_CHOICEDLG 1
16
#include <wx/choicdlg.h>
17
#include <wx/settings.h>
18
#include <wx/filedlg.h>
19
#include <wx/filename.h>
20
#include <wx/textdlg.h>
23
#include "cbproject.h" // class's header file
24
#include "compiler.h" // GetSwitches
25
#include "sdk_events.h"
29
#include "pluginmanager.h"
30
#include "projectmanager.h"
31
#include "macrosmanager.h"
32
#include "logmanager.h"
33
#include "editormanager.h"
34
#include "filemanager.h"
35
#include "configmanager.h"
36
#include "compilerfactory.h"
37
#include "projectbuildtarget.h"
38
#include "projectfile.h"
39
#include "infowindow.h"
43
#include "projectoptionsdlg.h"
44
#include "projectloader.h"
45
#include "projectlayoutloader.h"
46
#include "selecttargetdlg.h"
47
#include "filegroupsandmasks.h"
48
#include "filefilters.h"
49
#include "annoyingdialog.h"
50
#include "genericmultilinenotesdlg.h"
53
namespace compatibility { typedef TernaryCondTypedef<wxMinimumVersion<2,5>::eval, wxTreeItemIdValue, long int>::eval tree_cookie_t; };
57
cbProject::cbProject(const wxString& filename)
58
: m_CustomMakefile(false),
60
m_CurrentlyLoading(false),
61
m_PCHMode(pchSourceFile),
62
m_CurrentlyCompilingTarget(0),
63
m_ExtendedObjectNamesGeneration(false),
64
m_AutoShowNotesOnLoad(false),
65
m_pExtensionsElement(0)
67
SetCompilerID(CompilerFactory::GetDefaultCompilerID());
71
if (!filename.IsEmpty() && wxFileExists(filename) || wxDirExists(filename))
74
m_Filename = filename;
75
m_BasePath = GetBasePath();
82
if (filename.IsEmpty())
84
m_Filename = CreateUniqueFilename();
89
m_Filename = filename;
94
wxFileName fname(m_Filename);
95
m_Title = fname.GetName();
96
m_BasePath = GetBasePath();
97
m_CommonTopLevelPath = GetBasePath() + wxFileName::GetPathSeparator();
99
// moved to ProjectManager::LoadProject()
100
// see explanation there...
101
// NotifyPlugins(cbEVT_PROJECT_OPEN);
107
cbProject::~cbProject()
109
// moved to ProjectManager::CloseProject()
110
// see explanation there...
111
// NotifyPlugins(cbEVT_PROJECT_CLOSE);
113
ClearAllProperties();
116
void cbProject::NotifyPlugins(wxEventType type, const wxString& targetName, const wxString& oldTargetName)
118
CodeBlocksEvent event(type);
119
event.SetProject(this);
120
event.SetBuildTargetName(targetName);
121
event.SetOldBuildTargetName(oldTargetName);
122
Manager::Get()->ProcessEvent(event);
125
void cbProject::SetCompilerID(const wxString& id)
127
// TODO (mandrav##): Is this needed? The project's compiler has nothing to do with the targets' compilers...
129
CompileTargetBase::SetCompilerID(id);
130
if (id != GetCompilerID())
132
// update object filenames
133
for (unsigned int i = 0; i < m_Targets.GetCount(); ++i)
135
ProjectBuildTarget* target = m_Targets[i];
138
Compiler* compiler = CompilerFactory::GetCompiler(target->GetCompilerID());
142
int count = GetFilesCount();
143
for (int i = 0; i < count; ++i)
145
ProjectFile* pf = GetFile(i);
146
wxFileName obj(pf->GetObjName());
147
if (FileTypeOf(pf->relativeFilename) != ftResource &&
148
obj.GetExt() == compiler->GetSwitches().objectExtension)
150
obj.SetExt(compiler->GetSwitches().objectExtension);
151
pf->SetObjName(obj.GetFullName());
159
bool cbProject::GetModified() const
161
// check base options
162
if (CompileOptionsBase::GetModified())
166
for (unsigned int i = 0; i < m_Targets.GetCount(); ++i)
168
ProjectBuildTarget* target = m_Targets[i];
169
if (target->GetModified())
176
void cbProject::SetModified(bool modified)
178
CompileOptionsBase::SetModified(modified);
181
for (unsigned int i = 0; i < m_Targets.GetCount(); ++i)
183
ProjectBuildTarget* target = m_Targets[i];
184
target->SetModified(modified);
188
m_LastSavedActiveTarget = m_ActiveTarget;
191
void cbProject::SetMakefileCustom(bool custom)
193
if (m_CustomMakefile != custom)
195
m_CustomMakefile = custom;
200
wxString cbProject::CreateUniqueFilename()
202
const wxString prefix = _("Untitled");
204
ProjectsArray* arr = Manager::Get()->GetProjectManager()->GetProjects();
205
int projCount = arr->GetCount();
208
tmp << prefix << wxString::Format(_T("%d"), iter);
212
tmp << prefix << wxString::Format(_T("%d"), iter);
215
for (int i = 0; i < projCount; ++i)
217
cbProject* prj = arr->Item(i);
218
wxFileName fname(prj->GetFilename());
220
if (fname.GetName().Matches(tmp))
230
return tmp << _T(".") << FileFilters::CODEBLOCKS_EXT;
233
void cbProject::ClearAllProperties()
235
Delete(m_pExtensionsElement);
237
m_Files.DeleteContents(true);
239
m_Files.DeleteContents(false);
240
m_CompilerOptions.Clear();
241
m_LinkerOptions.Clear();
242
m_IncludeDirs.Clear();
245
while (m_Targets.GetCount())
247
ProjectBuildTarget* target = m_Targets[0];
249
m_Targets.RemoveAt(0);
253
NotifyPlugins(cbEVT_BUILDTARGET_SELECTED);
256
void cbProject::Open()
259
m_ProjectFilesMap.clear();
260
Delete(m_pExtensionsElement);
262
if (!wxFileName::FileExists(m_Filename) && !wxFileName::DirExists(m_Filename))
265
msg.Printf(_("Project '%s' does not exist..."), m_Filename.c_str());
266
cbMessageBox(msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR);
270
bool fileUpgraded = false;
271
bool fileModified = false;
272
wxFileName fname(m_Filename);
273
FileType ft = FileTypeOf(m_Filename);
274
if (ft == ftCodeBlocksProject)
276
Manager::Get()->GetLogManager()->Log(_("Opening ") + m_Filename);
277
m_CurrentlyLoading = true;
278
ProjectLoader loader(this);
279
m_Loaded = loader.Open(m_Filename, &m_pExtensionsElement);
280
fileUpgraded = loader.FileUpgraded();
281
fileModified = loader.FileModified();
282
m_CurrentlyLoading = false;
286
CalculateCommonTopLevelPath();
287
Manager::Get()->GetLogManager()->Log(_("done"));
288
if (!m_Targets.GetCount())
289
AddDefaultBuildTarget();
290
// in case of batch build discard upgrade messages
291
fileUpgraded = fileUpgraded && !Manager::IsBatchBuild();
292
SetModified(ft != ftCodeBlocksProject || fileUpgraded || fileModified);
294
// moved to ProjectManager::LoadProject()
295
// see explanation there...
296
// NotifyPlugins(cbEVT_PROJECT_OPEN);
300
InfoWindow::Display(m_Title,
301
_("The loaded project file was generated\n"
302
"with an older version of Code::Blocks.\n\n"
303
"Code::Blocks can import older project files,\n"
304
"but will always save in the current format."), 12000, 2000);
306
m_LastModified = fname.GetModificationTime();
311
void cbProject::CalculateCommonTopLevelPath()
313
// find the common toplevel path
314
// for simple projects, this might be the path to the project file
315
// for projects where the project file is in a subdir, files will have ..
317
wxString sep = wxFileName::GetPathSeparator();
318
wxFileName base = GetBasePath() + sep;
319
Manager::Get()->GetLogManager()->DebugLog(_T("Project's base path: ") + base.GetFullPath());
321
// this loop takes ~30ms for 1000 project files
322
// it's as fast as it can get, considered that it used to take ~1200ms ;)
323
// don't even bother making it faster - you can't :)
324
for (FilesList::Node* node = m_Files.GetFirst(); node; node = node->GetNext())
326
ProjectFile* f = node->GetData();
327
wxString tmp = f->relativeFilename;
328
wxString tmpbase = m_BasePath;
331
while (pos < tmp.Length() &&
332
(tmp.GetChar(pos) == _T('.') || tmp.GetChar(pos) == _T('/') || tmp.GetChar(pos) == _T('\\')))
336
if (pos > 0 && pos < tmp.Length())
338
tmpbase << sep << tmp.Left(pos) << sep;
339
f->relativeToCommonTopLevelPath = tmp.Right(tmp.Length() - pos);
342
f->relativeToCommonTopLevelPath = tmp;
343
f->SetObjName(f->relativeToCommonTopLevelPath);
345
wxFileName tmpbaseF(tmpbase);
346
tmpbaseF.Normalize(wxPATH_NORM_DOTS);
347
if (tmpbaseF.GetDirCount() < base.GetDirCount())
351
m_CommonTopLevelPath = base.GetFullPath();
352
Manager::Get()->GetLogManager()->DebugLog(_T("Project's common toplevel path: ") + m_CommonTopLevelPath);
355
wxString cbProject::GetCommonTopLevelPath() const
357
return m_CommonTopLevelPath;
360
void cbProject::Touch()
362
m_LastModified = wxDateTime::Now();
365
bool cbProject::SaveAs()
368
fname.Assign(m_Filename);
369
wxFileDialog dlg(Manager::Get()->GetAppWindow(),
373
FileFilters::GetFilterString(_T('.') + FileFilters::CODEBLOCKS_EXT),
374
wxSAVE | wxOVERWRITE_PROMPT);
377
if (dlg.ShowModal() != wxID_OK)
379
m_Filename = dlg.GetPath();
380
fname.Assign(m_Filename);
382
// make sure the project file uses the correct extension
383
// we don't use wxFileName::SetExt() because if the user has added a dot
384
// in the filename, the part after it would be interpeted as extension
385
// (and it might not be)
386
// so we just append the correct extension
387
if (!fname.GetExt().Matches(FileFilters::CODEBLOCKS_EXT))
388
fname.Assign(m_Filename + _T('.') + FileFilters::CODEBLOCKS_EXT);
390
// Manager::Get()->GetProjectManager()->GetTree()->SetItemText(m_ProjectNode, fname.GetFullName());
392
AddDefaultBuildTarget();
393
ProjectLoader loader(this);
394
if (loader.Save(m_Filename, m_pExtensionsElement))
396
wxFileName fname(m_Filename);
397
m_LastModified = fname.GetModificationTime();
398
NotifyPlugins(cbEVT_PROJECT_SAVE);
402
cbMessageBox(_("Couldn't save project ") + m_Filename + _("\n(Maybe the file is write-protected?)"), _("Warning"), wxICON_WARNING);
406
bool cbProject::Save()
408
if (m_Filename.IsEmpty())
410
ProjectLoader loader(this);
411
if (loader.Save(m_Filename, m_pExtensionsElement))
413
wxFileName fname(m_Filename);
414
m_LastModified = fname.GetModificationTime();
415
NotifyPlugins(cbEVT_PROJECT_SAVE);
419
cbMessageBox(_("Couldn't save project ") + m_Filename + _("\n(Maybe the file is write-protected?)"), _("Warning"), wxICON_WARNING);
423
bool cbProject::SaveLayout()
425
if (m_Filename.IsEmpty())
428
wxFileName fname(m_Filename);
429
fname.SetExt(_T("layout"));
430
ProjectLayoutLoader loader(this);
431
return loader.Save(fname.GetFullPath());
434
bool cbProject::LoadLayout()
436
if (m_Filename.IsEmpty())
438
int openmode = Manager::Get()->GetConfigManager(_T("project_manager"))->ReadInt(_T("/open_files"), (long int)1);
443
// Do not open any files
448
Manager::Get()->GetEditorManager()->HideNotebook();
449
if(openmode == 0) // Open all files
451
FilesList::Node* node = m_Files.GetFirst();
454
ProjectFile* f = node->GetData();
455
Manager::Get()->GetEditorManager()->Open(f->file.GetFullPath(),0,f);
456
node = node->GetNext();
460
else if(openmode == 1)// Open last open files
462
wxFileName fname(m_Filename);
463
fname.SetExt(_T("layout"));
464
ProjectLayoutLoader loader(this);
465
if (loader.Open(fname.GetFullPath()))
467
typedef std::map<int, ProjectFile*> open_files_map;
468
open_files_map open_files;
470
// Get all files to open and sort them according to their tab-position:
471
FilesList::Node* node = m_Files.GetFirst();
474
ProjectFile* f = node->GetData();
476
open_files[f->editorTabPos] = f;
477
node = node->GetNext();
480
// Load all requested files
481
std::vector<LoaderBase*> filesInMemory;
482
for (open_files_map::iterator it = open_files.begin(); it != open_files.end(); ++it)
484
filesInMemory.push_back(Manager::Get()->GetFileManager()->Load((*it).second->file.GetFullPath()));
486
// Open all requested files:
488
for (open_files_map::iterator it = open_files.begin(); it != open_files.end(); ++it)
490
cbEditor* ed = Manager::Get()->GetEditorManager()->Open(filesInMemory[i], (*it).second->file.GetFullPath(),0,(*it).second);
492
ed->SetProjectFile((*it).second);
496
ProjectFile* f = loader.GetTopProjectFile();
499
Manager::Get()->GetLogManager()->DebugLog(_T("Top Editor: ") + f->file.GetFullPath());
500
EditorBase* eb = Manager::Get()->GetEditorManager()->Open(f->file.GetFullPath());
504
// Manager::Get()->GetAppWindow()->Thaw();
510
Manager::Get()->GetEditorManager()->ShowNotebook();
515
void cbProject::BeginAddFiles()
517
CodeBlocksEvent event(cbEVT_PROJECT_BEGIN_ADD_FILES);
518
event.SetProject(this);
519
Manager::Get()->ProcessEvent(event);
522
void cbProject::EndAddFiles()
524
CodeBlocksEvent event(cbEVT_PROJECT_END_ADD_FILES);
525
event.SetProject(this);
526
Manager::Get()->ProcessEvent(event);
529
void cbProject::BeginRemoveFiles()
531
CodeBlocksEvent event(cbEVT_PROJECT_BEGIN_REMOVE_FILES);
532
event.SetProject(this);
533
Manager::Get()->ProcessEvent(event);
536
void cbProject::EndRemoveFiles()
538
CodeBlocksEvent event(cbEVT_PROJECT_END_REMOVE_FILES);
539
event.SetProject(this);
540
Manager::Get()->ProcessEvent(event);
543
ProjectFile* cbProject::AddFile(const wxString& targetName, const wxString& filename, bool compile, bool link, unsigned short int weight)
545
int idx = IndexOfBuildTargetName(targetName);
546
return AddFile(idx, filename, compile, link, weight);
549
ProjectFile* cbProject::AddFile(int targetIndex, const wxString& filename, bool compile, bool link, unsigned short int weight)
551
// NOTE (Rick#1#): When loading the project, do not search for existing files
552
// (Assumming that there are no duplicate entries in the .cbp file)
553
// This saves us a lot of processing when loading large projects.
554
// Remove the if to do the search anyway
556
// NOTE (mandrav#1#): We can't ignore that because even if we can rely on .cbp
557
// containing discrete files, we can't do that for imported projects...
558
// This means we have to search anyway.
559
// NP though, I added a hashmap for fast searches in GetFileByFilename()
561
/* NOTE (mandrav#1#): Calling GetFileByFilename() twice, is costly.
562
Instead of searching for duplicate files when entering here,
563
we 'll search before exiting.
564
The rationale is that by then, we 'll have the relative filename
565
in our own representation and this will make everything quicker
566
(check GetFileByFilename implementation to understand why)...
568
// f = GetFileByFilename(filename, true, true);
570
// f = GetFileByFilename(filename, false, true);
573
// if (targetIndex >= 0 && targetIndex < (int)m_Targets.GetCount())
574
// f->AddBuildTarget(m_Targets[targetIndex]->GetTitle());
579
ProjectFile* f = m_ProjectFilesMap[UnixFilename(filename)];
584
f = new ProjectFile(this);
585
bool localCompile, localLink;
586
wxFileName fname(filename);
589
FileType ft = FileTypeOf(filename);
591
ext = filename.AfterLast(_T('.')).Lower();
592
if (ext.IsSameAs(FileFilters::C_EXT))
593
f->compilerVar = _T("CC");
594
else if (platform::windows && ext.IsSameAs(FileFilters::RESOURCE_EXT))
595
f->compilerVar = _T("WINDRES");
597
f->compilerVar = _T("CPP"); // default
599
if (!m_Targets.GetCount())
601
// no targets in project; add default
602
AddDefaultBuildTarget();
603
if (!m_Targets.GetCount())
606
return 0L; // if that failed, fail addition of file...
610
bool isResource = FileTypeOf(filename) == ftResource;
612
// NOTE (mandrav#1#): targetIndex == -1 means "don't add file to any targets"
613
// This case gives us problems though because then we don't know the compiler
614
// this file will be using (per-target) which means we can't decide if it
615
// generates any files...
616
// We solve this issue with a hack (only for the targetIndex == -1 case!):
617
// We iterate all available target compilers tool and use generatedFiles from
618
// all of them. It works and is also safe.
619
std::map<Compiler*, const CompilerTool*> GenFilesHackMap;
620
if (targetIndex < 0 || targetIndex >= (int)m_Targets.GetCount())
622
Compiler* c = CompilerFactory::GetCompiler(GetCompilerID());
625
const CompilerTool* t = &c->GetCompilerTool(isResource ? ctCompileResourceCmd : ctCompileObjectCmd, fname.GetExt());
626
if (t->generatedFiles.GetCount())
628
GenFilesHackMap[c] = t;
632
for (unsigned int i = 0; i < m_Targets.GetCount(); ++i)
634
Compiler* c = CompilerFactory::GetCompiler(m_Targets[i]->GetCompilerID());
635
if (GenFilesHackMap.find(c) != GenFilesHackMap.end())
636
continue; // compiler already in map
640
const CompilerTool* t = &c->GetCompilerTool(isResource ? ctCompileResourceCmd : ctCompileObjectCmd, fname.GetExt());
641
if (t->generatedFiles.GetCount())
643
GenFilesHackMap[c] = t;
650
// targetIndex is valid: just add a single entry to the map
651
Compiler* c = CompilerFactory::GetCompiler(m_Targets[targetIndex]->GetCompilerID());
654
const CompilerTool* t = &c->GetCompilerTool(isResource ? ctCompileResourceCmd : ctCompileObjectCmd, fname.GetExt());
655
if (t->generatedFiles.GetCount())
657
GenFilesHackMap[c] = t;
661
// so... now, if GenFilesHackMap is not empty, we know
662
// 1) this file generates other files and
663
// 2) iterating the map will give us the generated file names :)
665
// add the build target
666
if (targetIndex >= 0 && targetIndex < (int)m_Targets.GetCount())
667
f->AddBuildTarget(m_Targets[targetIndex]->GetTitle());
669
localCompile = compile &&
672
!GenFilesHackMap.empty());
677
ft == ftResourceBin ||
680
f->compile = localCompile;
683
wxString local_filename = filename;
686
// for windows, make sure the filename is not on another drive...
687
if (local_filename.Length() > 1 &&
688
local_filename.GetChar(1) == _T(':') && // quick test to avoid the costly wxFileName ctor below
689
fname.GetVolume() != wxFileName(m_Filename).GetVolume())
691
fname.Assign(filename);
696
// make sure the filename is relative to the project's base path
697
if (fname.IsAbsolute())
699
fname.MakeRelativeTo(GetBasePath());
700
local_filename = fname.GetFullPath();
702
fname.Assign(GetBasePath() + wxFILE_SEP_PATH + local_filename);
704
fname.Normalize(wxPATH_NORM_DOTS | wxPATH_NORM_TILDE, GetBasePath());
706
wxString fullFilename = fname.GetFullPath();
707
f->file.Assign(fname);
708
f->relativeFilename = UnixFilename(local_filename);
710
// now check if we have already added this file
711
// if we have, return the existing file, but add the specified target
712
ProjectFile* existing = GetFileByFilename(f->relativeFilename, true, true);
716
if (targetIndex >= 0 && targetIndex < (int)m_Targets.GetCount())
717
existing->AddBuildTarget(m_Targets[targetIndex]->GetTitle());
722
if (!m_CurrentlyLoading)
724
// check if we really need to recalculate the common top-level path for the project
725
if (!fullFilename.StartsWith(m_CommonTopLevelPath))
726
CalculateCommonTopLevelPath();
729
// set f->relativeToCommonTopLevelPath
730
f->relativeToCommonTopLevelPath = fullFilename.Right(fullFilename.Length() - m_CommonTopLevelPath.Length());
734
m_ProjectFilesMap[UnixFilename(f->relativeFilename)] = f; // add to hashmap
736
if (!wxFileExists(fullFilename))
737
f->SetFileState(fvsMissing);
738
else if (!wxFile::Access(fullFilename.c_str(), wxFile::write)) // readonly
739
f->SetFileState(fvsReadOnly);
741
if (!GenFilesHackMap.empty())
743
// auto-generated files!
744
wxFileName tmp = f->file;
745
for (std::map<Compiler*, const CompilerTool*>::const_iterator it = GenFilesHackMap.begin(); it != GenFilesHackMap.end(); ++it)
747
const CompilerTool* tool = it->second;
748
for (size_t i = 0; i < tool->generatedFiles.GetCount(); ++i)
750
tmp.SetFullName(tool->generatedFiles[i]);
751
wxString tmps = tmp.GetFullPath();
752
// any macro replacements here, should also be done in
753
// CompilerCommandGenerator::GenerateCommandLine !!!
754
tmps.Replace(_T("$file_basename"), f->file.GetName()); // old way - remove later
755
tmps.Replace(_T("$file_name"), f->file.GetName());
756
tmps.Replace(_T("$file_dir"), f->file.GetPath());
757
tmps.Replace(_T("$file_ext"), f->file.GetExt());
758
tmps.Replace(_T("$file"), f->file.GetFullName());
759
Manager::Get()->GetMacrosManager()->ReplaceMacros(tmps);
761
ProjectFile* pfile = AddFile(targetIndex, UnixFilename(tmps));
763
Manager::Get()->GetLogManager()->DebugLog(_T("Can't add auto-generated file ") + tmps);
766
f->generatedFiles.push_back(pfile);
767
pfile->autoGeneratedBy = f;
776
bool cbProject::RemoveFile(ProjectFile* pf)
780
m_ProjectFilesMap.erase(UnixFilename(pf->relativeFilename)); // remove from hashmap
781
Manager::Get()->GetEditorManager()->Close(pf->file.GetFullPath());
783
FilesList::Node* node = m_Files.Find(pf);
786
Manager::Get()->GetLogManager()->DebugLog(_T("Can't locate node for ProjectFile* !"));
790
m_Files.DeleteNode(node);
793
// remove this file from all targets too
794
for (unsigned int i = 0; i < m_Targets.GetCount(); ++i)
796
ProjectBuildTarget* target = m_Targets[i];
799
target->GetFilesList().DeleteObject(pf);
803
// if this is auto-generated, inform "parent"
804
if (pf->autoGeneratedBy)
806
ProjectFilesVector::iterator it = std::find(pf->autoGeneratedBy->generatedFiles.begin(), pf->autoGeneratedBy->generatedFiles.end(), pf);
807
pf->autoGeneratedBy->generatedFiles.erase(it);
810
// also remove generated files (see above code: files will empty the vector)
811
while (pf->generatedFiles.size())
813
RemoveFile(pf->generatedFiles[0]);
815
pf->generatedFiles.clear();
823
bool cbProject::RemoveFile(int index)
825
if (index < 0 || index >= (int)m_Files.GetCount())
826
return false; // invalid index
827
ProjectFile* f = m_Files[index];
828
return RemoveFile(f);
831
int filesSort(const ProjectFile** arg1, const ProjectFile** arg2)
833
return (*arg1)->file.GetFullPath().CompareTo((*arg2)->file.GetFullPath());
836
void cbProject::BuildTree(wxTreeCtrl* tree, const wxTreeItemId& root, bool categorize, bool useFolders, FilesGroupsAndMasks* fgam)
841
int fldIdx = Manager::Get()->GetProjectManager()->FolderIconIndex();
842
int vfldIdx = Manager::Get()->GetProjectManager()->VirtualFolderIconIndex();
843
bool read_only = (!wxFile::Access(GetFilename().c_str(), wxFile::write));
844
int prjIdx = Manager::Get()->GetProjectManager()->ProjectIconIndex(read_only);
847
m_Files.Sort(filesSort);
849
// add our project's root item
850
FileTreeData* ftd = new FileTreeData(this, FileTreeData::ftdkProject);
851
m_ProjectNode = tree->AppendItem(root, GetTitle(), prjIdx, prjIdx, ftd);
852
wxTreeItemId others = m_ProjectNode;
853
wxTreeItemId generated = m_ProjectNode;
855
// create file-type categories nodes (if enabled)
856
wxTreeItemId* pGroupNodes = 0L;
857
if (categorize && fgam)
859
pGroupNodes = new wxTreeItemId[fgam->GetGroupsCount()];
860
for (unsigned int i = 0; i < fgam->GetGroupsCount(); ++i)
862
ftd = new FileTreeData(this, FileTreeData::ftdkVirtualGroup);
863
ftd->SetFolder(fgam->GetGroupName(i));
864
pGroupNodes[i] = tree->AppendItem(m_ProjectNode, fgam->GetGroupName(i), fldIdx, fldIdx, ftd);
866
// add a default category "Generated" for all auto-generated file types
867
ftd = new FileTreeData(this, FileTreeData::ftdkVirtualGroup);
868
generated = tree->AppendItem(m_ProjectNode, _("Auto-generated"), fldIdx, fldIdx, ftd);
869
// add a default category "Others" for all non-matching file-types
870
ftd = new FileTreeData(this, FileTreeData::ftdkVirtualGroup);
871
others = tree->AppendItem(m_ProjectNode, _("Others"), fldIdx, fldIdx, ftd);
873
// Now add any virtual folders
874
for (size_t i = 0; i < m_VirtualFolders.GetCount(); ++i)
876
ftd = new FileTreeData(this, FileTreeData::ftdkVirtualFolder);
877
ftd->SetFolder(m_VirtualFolders[i]);
878
AddTreeNode(tree, m_VirtualFolders[i], m_ProjectNode, true, FileTreeData::ftdkVirtualFolder, true, vfldIdx, ftd);
881
// iterate all project files and add them to the tree
883
for (FilesList::Node* node = m_Files.GetFirst(); node; node = node->GetNext())
885
ProjectFile* f = node->GetData();
886
ftd = new FileTreeData(this, FileTreeData::ftdkFile);
887
ftd->SetFileIndex(count++);
888
ftd->SetProjectFile(f);
889
ftd->SetFolder(f->file.GetFullPath());
891
wxFileName nodefile = f->file;
892
nodefile.MakeRelativeTo(m_CommonTopLevelPath);
893
wxString nodetext = nodefile.GetFullPath();
894
FileTreeData::FileTreeDataKind folders_kind = FileTreeData::ftdkFolder;
896
wxTreeItemId parentNode = m_ProjectNode;
897
// check if files grouping is enabled and find the group parent
898
// Also make a check that the file is not under virtual folder
899
if (categorize && pGroupNodes && fgam && f->virtual_path.IsEmpty())
902
// auto-generated files end up all together
903
if (f->autoGeneratedBy)
905
parentNode = generated;
909
// else try to match a group
912
for (unsigned int i = 0; i < fgam->GetGroupsCount(); ++i)
914
wxFileName fname(f->relativeToCommonTopLevelPath);
915
if (fgam->MatchesMask(fname.GetFullName(), i))
917
parentNode = pGroupNodes[i];
924
// if not matched a group, put it in "Others" group
928
else if ((!categorize || !pGroupNodes || !fgam) && f->virtual_path.IsEmpty())
930
parentNode = m_ProjectNode;
932
/* Else put the file under virtual folder */
933
else if (!f->virtual_path.IsEmpty())
935
nodetext = f->virtual_path + wxFILE_SEP_PATH + f->file.GetFullName();
936
folders_kind = FileTreeData::ftdkVirtualFolder;
937
wxString slash = f->virtual_path.Last() == wxFILE_SEP_PATH ? _T("") : wxString(wxFILE_SEP_PATH);
938
ftd->SetFolder(f->virtual_path);
940
if (m_VirtualFolders.Index(f->virtual_path + slash) == wxNOT_FOUND)
941
m_VirtualFolders.Add(f->virtual_path + slash);
944
// add file in the tree
945
f->m_TreeItemId = AddTreeNode(tree, nodetext, parentNode, useFolders || folders_kind == FileTreeData::ftdkVirtualFolder, folders_kind, f->compile, (int)f->m_VisualState, ftd);
948
// remove empty tree nodes (like empty groups)
949
if (categorize && fgam)
951
for (unsigned int i = 0; i < fgam->GetGroupsCount(); ++i)
953
if (tree->GetChildrenCount(pGroupNodes[i], false) == 0)
954
tree->Delete(pGroupNodes[i]);
956
if (tree->GetChildrenCount(others, false) == 0)
957
tree->Delete(others);
958
if (tree->GetChildrenCount(generated, false) == 0)
959
tree->Delete(generated);
961
delete[] pGroupNodes;
963
tree->Expand(m_ProjectNode);
966
// helper function used by AddTreeNode
967
static wxString GetRelativeFolderPath(wxTreeCtrl* tree, wxTreeItemId parent)
970
while (parent.IsOk())
972
FileTreeData* ftd = (FileTreeData*)tree->GetItemData(parent);
973
if (!ftd || (ftd->GetKind() != FileTreeData::ftdkFolder && ftd->GetKind() != FileTreeData::ftdkVirtualFolder))
975
fld.Prepend(tree->GetItemText(parent) + wxFILE_SEP_PATH);
976
parent = tree->GetItemParent(parent);
981
wxTreeItemId cbProject::AddTreeNode(wxTreeCtrl* tree,
982
const wxString& text,
983
const wxTreeItemId& parent,
985
FileTreeData::FileTreeDataKind folders_kind,
990
// see if the text contains any path info, e.g. plugins/compilergcc/compilergcc.cpp
991
// in that case, take the first element (plugins in this example), create a sub-folder
992
// with the same name and recurse with the result...
999
wxString path = text;
1001
// special case for windows and files on a different drive
1002
if (platform::windows && path.Length() > 1 && path.GetChar(1) == _T(':'))
1005
int pos = path.Find(_T('/'));
1007
pos = path.Find(_T('\\'));
1008
if (useFolders && pos >= 0)
1010
// ok, we got it. now split it up and recurse
1011
wxString folder = path.Left(pos);
1012
// avoid consecutive path separators
1013
while (path.GetChar(pos + 1) == _T('/') || path.GetChar(pos + 1) == _T('\\'))
1015
path = path.Right(path.Length() - pos - 1);
1017
compatibility::tree_cookie_t cookie = 0;
1019
wxTreeItemId newparent = tree->GetFirstChild(parent, cookie);
1022
wxString itemText = tree->GetItemText(newparent);
1023
if (itemText.Matches(folder))
1025
newparent = tree->GetNextChild(parent, cookie);
1030
// in order not to override wxTreeCtrl to sort alphabetically but the
1031
// folders be always on top, we just search here where to put the new folder...
1032
int fldIdx = Manager::Get()->GetProjectManager()->FolderIconIndex();
1033
int vfldIdx = Manager::Get()->GetProjectManager()->VirtualFolderIconIndex();
1035
newparent = FindNodeToInsertAfter(tree, folder, parent, true);
1037
FileTreeData* ftd = new FileTreeData(*data);
1038
ftd->SetKind(folders_kind);
1039
if (folders_kind != FileTreeData::ftdkVirtualFolder)
1040
ftd->SetFolder(m_CommonTopLevelPath + GetRelativeFolderPath(tree, parent) + folder + wxFILE_SEP_PATH);
1042
ftd->SetFolder(GetRelativeFolderPath(tree, parent) + folder + wxFILE_SEP_PATH);
1043
ftd->SetProjectFile(0);
1044
int idx = folders_kind != FileTreeData::ftdkVirtualFolder ? fldIdx : vfldIdx;
1045
newparent = tree->InsertItem(parent, newparent, folder, idx, idx, ftd);
1047
//tree->SortChildren(parent);
1048
ret = AddTreeNode(tree, path, newparent, true, folders_kind, compiles, image, data);
1052
ret = tree->AppendItem(parent, text, image, image, data);
1054
tree->SetItemTextColour(ret, wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT));
1059
wxTreeItemId cbProject::FindNodeToInsertAfter(wxTreeCtrl* tree, const wxString& text, const wxTreeItemId& parent, bool in_folders)
1061
wxTreeItemId result;
1063
if (tree && parent.IsOk())
1065
compatibility::tree_cookie_t cookie = 0;
1067
int fldIdx = Manager::Get()->GetProjectManager()->FolderIconIndex();
1068
int vfldIdx = Manager::Get()->GetProjectManager()->VirtualFolderIconIndex();
1070
bool last_is_folder = false;
1071
wxTreeItemId child = tree->GetFirstChild(parent, cookie);
1074
bool is_folder = tree->GetItemImage(child) == fldIdx || tree->GetItemImage(child) == vfldIdx;
1078
if (!is_folder || text.CmpNoCase(tree->GetItemText(child)) < 0)
1086
if (!is_folder && text.CmpNoCase(tree->GetItemText(child)) < 0)
1094
last_is_folder = is_folder;
1095
child = tree->GetNextChild(parent, cookie);
1104
void cbProject::CopyTreeNodeRecursively(wxTreeCtrl* tree, const wxTreeItemId& item, const wxTreeItemId& new_parent)
1106
// first, some sanity checks
1107
if (!tree || !item.IsOk() || !new_parent.IsOk())
1110
FileTreeData* ftd = (FileTreeData*)tree->GetItemData(item);
1111
FileTreeData* ftd_moved = ftd ? new FileTreeData(*ftd) : 0;
1112
int idx = tree->GetItemImage(item); // old image
1113
wxColour col = tree->GetItemTextColour(item); // old colour
1115
wxTreeItemId insert = FindNodeToInsertAfter(tree, tree->GetItemText(item), new_parent, ftd && ftd->GetKind() == FileTreeData::ftdkVirtualFolder);
1116
wxTreeItemId target = tree->InsertItem(new_parent, insert, tree->GetItemText(item), idx, idx, ftd_moved);
1117
tree->SetItemTextColour(target, col);
1119
// recurse for folders
1120
if (tree->ItemHasChildren(item))
1122
// vfolder: recurse for files all contained files virtual path
1123
wxTreeItemIdValue cookie;
1124
wxTreeItemId child = tree->GetFirstChild(item, cookie);
1125
while (child.IsOk())
1127
CopyTreeNodeRecursively(tree, child, target);
1128
child = tree->GetNextChild(item, cookie);
1132
if (!tree->IsExpanded(new_parent))
1133
tree->Expand(new_parent);
1135
if (ftd_moved->GetProjectFile())
1136
ftd_moved->GetProjectFile()->virtual_path = GetRelativeFolderPath(tree, new_parent);
1139
const wxArrayString& cbProject::GetVirtualFolders() const
1141
return m_VirtualFolders;
1144
void cbProject::SetVirtualFolders(const wxArrayString& folders)
1146
m_VirtualFolders = folders;
1147
for (size_t i = 0; i < m_VirtualFolders.GetCount(); ++i)
1149
m_VirtualFolders[i].Replace(_T("/"), wxString(wxFILE_SEP_PATH));
1150
m_VirtualFolders[i].Replace(_T("\\"), wxString(wxFILE_SEP_PATH));
1154
bool cbProject::CanDragNode(wxTreeCtrl* tree, wxTreeItemId node)
1156
// what item do we start dragging?
1160
// if no data associated with it, disallow
1161
FileTreeData* ftd = (FileTreeData*)tree->GetItemData(node);
1165
// if not ours, disallow
1166
if (ftd->GetProject() != this)
1169
// allow only if it is a file or a virtual folder
1170
return ftd->GetKind() == FileTreeData::ftdkFile || ftd->GetKind() == FileTreeData::ftdkVirtualFolder;
1173
bool cbProject::NodeDragged(wxTreeCtrl* tree, wxTreeItemId from, wxTreeItemId to)
1175
// what items did we drag?
1176
if (!from.IsOk() || !to.IsOk())
1179
// if no data associated with it, disallow
1180
FileTreeData* ftd1 = (FileTreeData*)tree->GetItemData(from);
1181
FileTreeData* ftd2 = (FileTreeData*)tree->GetItemData(to);
1185
// if not ours, disallow
1186
if (ftd1->GetProject() != this || ftd2->GetProject() != this)
1189
// allow only if a file or vfolder was dragged on a file, another vfolder or the project itself
1190
if ((ftd1->GetKind() != FileTreeData::ftdkFile &&
1191
ftd1->GetKind() != FileTreeData::ftdkVirtualFolder) ||
1192
(ftd2->GetKind() != FileTreeData::ftdkFile &&
1193
ftd2->GetKind() != FileTreeData::ftdkVirtualFolder &&
1194
ftd2->GetKind() != FileTreeData::ftdkProject))
1199
// don't drag under the same parent
1200
wxTreeItemId parent1 = ftd1->GetKind() == FileTreeData::ftdkFile ? tree->GetItemParent(from) : from;
1201
wxTreeItemId parent2 = ftd2->GetKind() == FileTreeData::ftdkFile ? tree->GetItemParent(to) : to;
1202
if (parent1 == parent2)
1205
// A special check for virtual folders.
1206
if (ftd1->GetKind() == FileTreeData::ftdkVirtualFolder && ftd2->GetKind() == FileTreeData::ftdkVirtualFolder)
1208
wxTreeItemId root = tree->GetRootItem();
1209
wxTreeItemId toParent = tree->GetItemParent(to);
1210
while (toParent != root)
1212
if (toParent == from)
1214
toParent = tree->GetItemParent(toParent);
1218
// finally; make the move
1219
CopyTreeNodeRecursively(tree, from, parent2);
1228
bool cbProject::VirtualFolderAdded(wxTreeCtrl* tree, wxTreeItemId parent_node, const wxString& virtual_folder)
1230
wxString foldername = GetRelativeFolderPath(tree, parent_node);
1231
foldername << virtual_folder;
1232
foldername.Replace(_T("/"), wxString(wxFILE_SEP_PATH), true);
1233
foldername.Replace(_T("\\"), wxString(wxFILE_SEP_PATH), true);
1234
if (foldername.Last() != wxFILE_SEP_PATH)
1235
foldername << wxFILE_SEP_PATH;
1237
for (size_t i = 0; i < m_VirtualFolders.GetCount(); ++i)
1239
if (m_VirtualFolders[i].StartsWith(foldername))
1241
cbMessageBox(_("A virtual folder with the same name already exists."),
1242
_("Error"), wxICON_WARNING);
1246
m_VirtualFolders.Add(foldername);
1248
FileTreeData* ftd = new FileTreeData(this, FileTreeData::ftdkVirtualFolder);
1249
ftd->SetProjectFile(0);
1250
ftd->SetFolder(foldername);
1252
int vfldIdx = Manager::Get()->GetProjectManager()->VirtualFolderIconIndex();
1254
AddTreeNode(tree, foldername, m_ProjectNode, true, FileTreeData::ftdkVirtualFolder, true, vfldIdx, ftd);
1255
if (!tree->IsExpanded(parent_node))
1256
tree->Expand(parent_node);
1260
// Manager::Get()->GetLogManager()->DebugLog(F(_T("VirtualFolderAdded: %s: %s"), foldername.c_str(), GetStringFromArray(m_VirtualFolders, _T(";")).c_str()));
1264
void cbProject::VirtualFolderDeleted(wxTreeCtrl* tree, wxTreeItemId node)
1266
// what item do we start dragging?
1270
// if no data associated with it, disallow
1271
FileTreeData* ftd = (FileTreeData*)tree->GetItemData(node);
1275
// if not ours, disallow
1276
if (ftd->GetProject() != this)
1279
wxString foldername = GetRelativeFolderPath(tree, node);
1280
wxString parent_foldername = GetRelativeFolderPath(tree, tree->GetItemParent(node));
1282
// now loop all project files and remove them from this virtual folder
1283
for (FilesList::Node* node = m_Files.GetFirst(); node; node = node->GetNext())
1285
ProjectFile* f = node->GetData();
1286
if (f && !f->virtual_path.IsEmpty())
1288
if (f->virtual_path.StartsWith(foldername)) // need 2 checks because of last separator
1289
f->virtual_path.Clear();
1293
for (int i = (int)m_VirtualFolders.GetCount() - 1; i >= 0; --i)
1295
if (m_VirtualFolders[i].StartsWith(foldername))
1296
m_VirtualFolders.RemoveAt(i);
1298
if (!parent_foldername.IsEmpty() && m_VirtualFolders.Index(parent_foldername) == wxNOT_FOUND)
1299
m_VirtualFolders.Add(parent_foldername);
1302
// Manager::Get()->GetLogManager()->DebugLog(F(_T("VirtualFolderDeleted: %s: %s"), foldername.c_str(), GetStringFromArray(m_VirtualFolders, _T(";")).c_str()));
1305
bool cbProject::VirtualFolderRenamed(wxTreeCtrl* tree, wxTreeItemId node, const wxString& new_name)
1307
if (new_name.IsEmpty())
1310
if (new_name.First(_T(';')) != wxNOT_FOUND ||
1311
new_name.First(_T('/')) != wxNOT_FOUND ||
1312
new_name.First(_T('\\')) != wxNOT_FOUND)
1314
cbMessageBox(_("A virtual folder name cannot contain these special characters: \";\", \"\\\" or \"/\"."),
1315
_("Error"), wxICON_WARNING);
1319
// what item are we renaming?
1323
// is it a different name?
1324
if (tree->GetItemText(node) == new_name)
1327
// if no data associated with it, disallow
1328
FileTreeData* ftd = (FileTreeData*)tree->GetItemData(node);
1332
// if not ours, disallow
1333
if (ftd->GetProject() != this)
1336
wxString old_foldername = GetRelativeFolderPath(tree, node);
1337
wxString new_foldername = GetRelativeFolderPath(tree, tree->GetItemParent(node)) + new_name + wxFILE_SEP_PATH;
1339
for (size_t i = 0; i < m_VirtualFolders.GetCount(); ++i)
1341
if (m_VirtualFolders[i].StartsWith(new_foldername))
1343
cbMessageBox(_("A virtual folder with the same name already exists."),
1344
_("Error"), wxICON_WARNING);
1348
int idx = m_VirtualFolders.Index(old_foldername);
1349
if (idx != wxNOT_FOUND)
1350
m_VirtualFolders[idx] = new_foldername;
1352
m_VirtualFolders.Add(new_foldername);
1354
// now loop all project files and rename this virtual folder
1355
for (FilesList::Node* node = m_Files.GetFirst(); node; node = node->GetNext())
1357
ProjectFile* f = node->GetData();
1358
if (f && !f->virtual_path.IsEmpty())
1360
if (f->virtual_path.StartsWith(old_foldername))
1361
f->virtual_path.Replace(old_foldername, new_foldername);
1367
// Manager::Get()->GetLogManager()->DebugLog(F(_T("VirtualFolderRenamed: %s to %s: %s"), old_foldername.c_str(), new_foldername.c_str(), GetStringFromArray(m_VirtualFolders, _T(";")).c_str()));
1371
void cbProject::RenameInTree(const wxString &newname)
1373
wxTreeCtrl* tree = Manager::Get()->GetProjectManager()->GetTree();
1374
if(!tree || !m_ProjectNode)
1376
tree->SetItemText(m_ProjectNode, newname);
1379
void cbProject::SaveTreeState(wxTreeCtrl* tree)
1381
::SaveTreeState(tree, m_ProjectNode, m_ExpandedNodes);
1384
void cbProject::RestoreTreeState(wxTreeCtrl* tree)
1386
::RestoreTreeState(tree, m_ProjectNode, m_ExpandedNodes);
1389
const wxString& cbProject::GetMakefile()
1391
if (!m_Makefile.IsEmpty())
1394
wxFileName makefile(m_Makefile);
1395
makefile.Assign(m_Filename);
1396
makefile.SetName(_T("Makefile"));
1397
makefile.SetExt(_T(""));
1398
makefile.MakeRelativeTo(GetBasePath());
1400
m_Makefile = makefile.GetFullPath();
1405
ProjectFile* cbProject::GetFile(int index)
1407
FilesList::Node* node = m_Files.Item(index);
1409
return node->GetData();
1414
ProjectFile* cbProject::GetFileByFilename(const wxString& filename, bool isRelative, bool isUnixFilename)
1416
// m_ProjectFilesMap keeps UnixFilename(ProjectFile::relativeFilename)
1417
wxString tmp = filename;
1420
// if the search is not relative, make it
1421
wxFileName fname(filename);
1422
fname.MakeRelativeTo(GetBasePath());
1423
tmp = fname.GetFullPath();
1427
// make sure filename doesn't start with ".\"
1428
// our own relative files don't have it, so the search would fail
1429
// this happens when importing MS projects...
1430
if (tmp.StartsWith(_T(".\\")) ||
1431
tmp.StartsWith(_T("./")))
1438
return m_ProjectFilesMap[tmp];
1439
return m_ProjectFilesMap[UnixFilename(tmp)];
1442
bool cbProject::QueryCloseAllFiles()
1444
FilesList::Node* node;
1445
node = m_Files.GetFirst();
1448
ProjectFile* f = node->GetData();
1449
cbEditor* ed = Manager::Get()->GetEditorManager()->IsBuiltinOpen(f->file.GetFullPath());
1450
if (ed && ed->GetModified())
1452
if (!Manager::Get()->GetEditorManager()->QueryClose(ed))
1455
node = node->GetNext();
1460
bool cbProject::CloseAllFiles(bool dontsave)
1462
// first try to close modified editors
1465
if(!QueryCloseAllFiles())
1468
// now free the rest of the project files
1469
Manager::Get()->GetEditorManager()->HideNotebook();
1470
FilesList::Node* node = m_Files.GetFirst();
1473
ProjectFile* f = node->GetData();
1474
Manager::Get()->GetEditorManager()->Close(f->file.GetFullPath(),true);
1477
node = m_Files.GetFirst();
1479
Manager::Get()->GetEditorManager()->ShowNotebook();
1483
bool cbProject::SaveAllFiles()
1485
int count = m_Files.GetCount();
1486
FilesList::Node* node = m_Files.GetFirst();
1489
ProjectFile* f = node->GetData();
1490
if (Manager::Get()->GetEditorManager()->Save(f->file.GetFullPath()))
1492
node = node->GetNext();
1497
bool cbProject::ShowOptions()
1499
ProjectOptionsDlg dlg(Manager::Get()->GetAppWindow(), this);
1501
if (dlg.ShowModal() == wxID_OK)
1503
// update file details
1504
FilesList::Node* node = m_Files.GetFirst();
1507
ProjectFile* f = node->GetData();
1508
f->UpdateFileDetails();
1509
node = node->GetNext();
1516
int cbProject::SelectTarget(int initial, bool evenIfOne)
1518
if (!evenIfOne && GetBuildTargetsCount() == 1)
1521
SelectTargetDlg dlg(0L, this, initial);
1523
if (dlg.ShowModal() == wxID_OK)
1524
return dlg.GetSelection();
1530
ProjectBuildTarget* cbProject::AddDefaultBuildTarget()
1532
return AddBuildTarget(_T("default"));
1535
ProjectBuildTarget* cbProject::AddBuildTarget(const wxString& targetName)
1537
if (GetBuildTarget(targetName)) // Don't add the target if it exists
1539
ProjectBuildTarget* target = new ProjectBuildTarget(this);
1540
target->m_Filename = m_Filename; // really important
1541
target->SetTitle(targetName);
1542
target->SetCompilerID(GetCompilerID()); // same compiler as project's
1543
target->SetOutputFilename(wxFileName(GetOutputFilename()).GetFullName());
1544
target->SetWorkingDir(_T("."));
1545
target->SetObjectOutput(_T(".objs"));
1546
target->SetDepsOutput(_T(".deps"));
1547
m_Targets.Add(target);
1549
// remove any virtual targets with the same name
1550
if (HasVirtualBuildTarget(targetName))
1552
RemoveVirtualBuildTarget(targetName);
1553
Manager::Get()->GetLogManager()->LogWarning(F(_T("Deleted existing virtual target '%s' because real target was added with the same name"), targetName.c_str()));
1558
NotifyPlugins(cbEVT_BUILDTARGET_ADDED, targetName);
1559
NotifyPlugins(cbEVT_PROJECT_TARGETS_MODIFIED);
1563
bool cbProject::RenameBuildTarget(int index, const wxString& targetName)
1565
ProjectBuildTarget* target = GetBuildTarget(index);
1568
wxString oldTargetName = target->GetTitle();
1570
// rename target if referenced in any virtual target too
1571
for (VirtualBuildTargetsMap::iterator it = m_VirtualTargets.begin(); it != m_VirtualTargets.end(); ++it)
1573
wxArrayString& tgts = it->second;
1574
int index = tgts.Index(target->GetTitle());
1577
tgts[index] = targetName;
1581
// rename target for all files that reference it
1582
int count = GetFilesCount();
1583
for (int i = 0; i < count; ++i)
1585
ProjectFile* pf = GetFile(i);
1586
pf->RenameBuildTarget(target->GetTitle(), targetName);
1589
// finally rename the target
1590
target->SetTitle(targetName);
1592
NotifyPlugins(cbEVT_BUILDTARGET_RENAMED, targetName, oldTargetName);
1593
NotifyPlugins(cbEVT_PROJECT_TARGETS_MODIFIED);
1599
bool cbProject::RenameBuildTarget(const wxString& oldTargetName, const wxString& newTargetName)
1601
return RenameBuildTarget(IndexOfBuildTargetName(oldTargetName), newTargetName);
1604
ProjectBuildTarget* cbProject::DuplicateBuildTarget(int index, const wxString& newName)
1606
ProjectBuildTarget* newTarget = 0;
1607
ProjectBuildTarget* target = GetBuildTarget(index);
1610
newTarget = new ProjectBuildTarget(*target);
1611
wxString newTargetName = !newName.IsEmpty() ? newName : (_("Copy of ") + target->GetTitle());
1612
newTarget->SetTitle(newTargetName);
1613
// just notify the files of this target that they belong to the new target too
1614
for (FilesList::Node* it = newTarget->GetFilesList().GetFirst(); it; it = it->GetNext())
1616
ProjectFile* pf = it->GetData();
1617
pf->AddBuildTarget(newTargetName);
1620
m_Targets.Add(newTarget);
1621
NotifyPlugins(cbEVT_BUILDTARGET_ADDED, newName);
1622
NotifyPlugins(cbEVT_PROJECT_TARGETS_MODIFIED);
1627
ProjectBuildTarget* cbProject::DuplicateBuildTarget(const wxString& targetName, const wxString& newName)
1629
return DuplicateBuildTarget(IndexOfBuildTargetName(targetName), newName);
1632
bool cbProject::ExportTargetAsProject(int index)
1634
ProjectBuildTarget* target = GetBuildTarget(index);
1637
return ExportTargetAsProject(target->GetTitle());
1640
bool cbProject::ExportTargetAsProject(const wxString& targetName)
1642
ProjectBuildTarget* target = GetBuildTarget(targetName);
1646
// ask for the new project's name
1647
wxString newName = wxGetTextFromUser(_("Please enter the new project's name (no path, no extension)."),
1648
_("Export target as new project"),
1649
target->GetTitle());
1650
if (newName.IsEmpty())
1652
wxFileName fname(GetFilename());
1653
fname.SetName(newName);
1656
bool alreadyModified = GetModified();
1657
wxString oldTitle = GetTitle();
1658
SetTitle(targetName);
1660
ProjectLoader loader(this);
1661
bool ret = loader.ExportTargetAsProject(fname.GetFullPath(), target->GetTitle(), m_pExtensionsElement);
1664
if (!alreadyModified)
1670
bool cbProject::RemoveBuildTarget(int index)
1672
ProjectBuildTarget* target = GetBuildTarget(index);
1675
wxString oldTargetName = target->GetTitle();
1677
// remove target from any virtual targets it belongs to
1678
for (VirtualBuildTargetsMap::iterator it = m_VirtualTargets.begin(); it != m_VirtualTargets.end(); ++it)
1680
wxArrayString& tgts = it->second;
1681
int index = tgts.Index(target->GetTitle());
1684
tgts.RemoveAt(index);
1688
// remove target from any project files that reference it
1689
int count = GetFilesCount();
1690
for (int i = 0; i < count; ++i)
1692
ProjectFile* pf = GetFile(i);
1693
pf->RemoveBuildTarget(target->GetTitle());
1696
// finally remove the target
1698
m_Targets.RemoveAt(index);
1700
NotifyPlugins(cbEVT_BUILDTARGET_REMOVED, oldTargetName);
1701
NotifyPlugins(cbEVT_PROJECT_TARGETS_MODIFIED);
1707
bool cbProject::RemoveBuildTarget(const wxString& targetName)
1709
return RemoveBuildTarget(IndexOfBuildTargetName(targetName));
1712
int cbProject::IndexOfBuildTargetName(const wxString& targetName) const
1714
for (unsigned int i = 0; i < m_Targets.GetCount(); ++i)
1716
ProjectBuildTarget* target = m_Targets[i];
1717
if (target->GetTitle().Matches(targetName))
1723
bool cbProject::BuildTargetValid(const wxString& name, bool virtuals_too) const
1725
if (virtuals_too && HasVirtualBuildTarget(name))
1727
else if (IndexOfBuildTargetName(name) != -1)
1732
wxString cbProject::GetFirstValidBuildTargetName(bool virtuals_too) const
1734
if (virtuals_too && !m_VirtualTargets.empty())
1735
return m_VirtualTargets.begin()->first;
1736
else if (m_Targets.GetCount() && m_Targets[0])
1737
return m_Targets[0]->GetTitle();
1739
return wxEmptyString;
1742
bool cbProject::SetActiveBuildTarget(const wxString& name)
1744
if (name == m_ActiveTarget)
1746
wxString oldActiveTarget = m_ActiveTarget;
1747
m_ActiveTarget = name;
1749
bool valid = BuildTargetValid(name);
1753
// no target (virtual or real) by that name
1754
m_ActiveTarget = GetFirstValidBuildTargetName();
1757
NotifyPlugins(cbEVT_BUILDTARGET_SELECTED, m_ActiveTarget, oldActiveTarget);
1762
const wxString& cbProject::GetActiveBuildTarget() const
1764
return m_ActiveTarget;
1767
void cbProject::SetDefaultExecuteTarget(const wxString& name)
1769
if (name == m_DefaultExecuteTarget)
1772
m_DefaultExecuteTarget = name;
1776
const wxString& cbProject::GetDefaultExecuteTarget() const
1778
return m_DefaultExecuteTarget;
1781
ProjectBuildTarget* cbProject::GetBuildTarget(int index)
1783
if (index >= 0 && index < (int)m_Targets.GetCount())
1784
return m_Targets[index];
1788
ProjectBuildTarget* cbProject::GetBuildTarget(const wxString& targetName)
1790
int idx = IndexOfBuildTargetName(targetName);
1791
return GetBuildTarget(idx);
1794
void cbProject::ReOrderTargets(const wxArrayString& nameOrder)
1796
LogManager* msgMan = Manager::Get()->GetLogManager();
1797
if (nameOrder.GetCount() != m_Targets.GetCount())
1799
msgMan->DebugLog(F(_T("cbProject::ReOrderTargets() : Count does not match (%d sent, %d had)..."), nameOrder.GetCount(), m_Targets.GetCount()));
1803
for (unsigned int i = 0; i < nameOrder.GetCount(); ++i)
1805
ProjectBuildTarget* target = GetBuildTarget(nameOrder[i]);
1808
msgMan->DebugLog(F(_T("cbProject::ReOrderTargets() : Target \"%s\" not found..."), nameOrder[i].c_str()));
1812
m_Targets.Remove(target);
1813
m_Targets.Insert(target, i);
1815
// we have to re-order the targets which are kept inside
1816
// the virtual targets array too!
1817
VirtualBuildTargetsMap::iterator it;
1818
for (it = m_VirtualTargets.begin(); it != m_VirtualTargets.end(); ++it)
1820
wxArrayString& vt = it->second;
1821
if (vt.Index(nameOrder[i]) != wxNOT_FOUND)
1823
vt.Remove(nameOrder[i]);
1824
vt.Insert(nameOrder[i], i);
1831
void cbProject::SetCurrentlyCompilingTarget(ProjectBuildTarget* bt)
1833
m_CurrentlyCompilingTarget = bt;
1836
bool cbProject::DefineVirtualBuildTarget(const wxString& alias, const wxArrayString& targets)
1838
if (targets.GetCount() == 0)
1840
Manager::Get()->GetLogManager()->LogWarning(F(_T("Can't define virtual build target '%s': Group of build targets is empty!"), alias.c_str()));
1844
ProjectBuildTarget* existing = GetBuildTarget(alias);
1847
Manager::Get()->GetLogManager()->LogWarning(F(_T("Can't define virtual build target '%s': Real build target exists with that name!"), alias.c_str()));
1851
m_VirtualTargets[alias] = targets;
1853
NotifyPlugins(cbEVT_PROJECT_TARGETS_MODIFIED);
1857
bool cbProject::HasVirtualBuildTarget(const wxString& alias) const
1859
return m_VirtualTargets.find(alias) != m_VirtualTargets.end();
1862
bool cbProject::RemoveVirtualBuildTarget(const wxString& alias)
1864
VirtualBuildTargetsMap::iterator it = m_VirtualTargets.find(alias);
1865
if (it == m_VirtualTargets.end())
1868
m_VirtualTargets.erase(it);
1870
NotifyPlugins(cbEVT_PROJECT_TARGETS_MODIFIED);
1874
wxArrayString cbProject::GetVirtualBuildTargets() const
1876
wxArrayString result;
1877
for (VirtualBuildTargetsMap::const_iterator it = m_VirtualTargets.begin(); it != m_VirtualTargets.end(); ++it)
1878
result.Add(it->first);
1882
const wxArrayString& cbProject::GetVirtualBuildTargetGroup(const wxString& alias) const
1884
static wxArrayString resultIfError;
1886
VirtualBuildTargetsMap::const_iterator it = m_VirtualTargets.find(alias);
1887
if (it == m_VirtualTargets.end())
1888
return resultIfError;
1892
wxArrayString cbProject::GetExpandedVirtualBuildTargetGroup(const wxString& alias) const
1894
wxArrayString result;
1896
VirtualBuildTargetsMap::const_iterator it = m_VirtualTargets.find(alias);
1897
if (it == m_VirtualTargets.end())
1900
ExpandVirtualBuildTargetGroup(alias, result);
1904
bool cbProject::CanAddToVirtualBuildTarget(const wxString& alias, const wxString& target)
1906
// virtual not there?
1907
if (!HasVirtualBuildTarget(alias))
1910
// real targets can be added safely (as long as they 're unique)
1911
if (!HasVirtualBuildTarget(target))
1914
// virtual targets are checked in two ways:
1915
// 1) it is checked if it contains alias
1916
// 2) all its virtual targets are recursively checked if they contain alias
1917
const wxArrayString& group = GetVirtualBuildTargetGroup(target);
1918
if (group.Index(alias) != wxNOT_FOUND)
1921
for (size_t i = 0; i < group.GetCount(); ++i)
1924
if (HasVirtualBuildTarget(group[i]))
1926
if (!CanAddToVirtualBuildTarget(group[i], alias))
1933
void cbProject::ExpandVirtualBuildTargetGroup(const wxString& alias, wxArrayString& result) const
1935
const wxArrayString& group = GetVirtualBuildTargetGroup(alias);
1936
for (size_t i = 0; i < group.GetCount(); ++i)
1938
// real targets get added
1939
if (IndexOfBuildTargetName(group[i]) != -1)
1941
if (result.Index(group[i]) == wxNOT_FOUND)
1942
result.Add(group[i]);
1944
// virtual targets recurse
1946
ExpandVirtualBuildTargetGroup(group[i], result);
1950
#ifdef USE_OPENFILES_TREE
1951
bool MiscTreeItemData::OwnerCheck(wxTreeEvent& event,wxTreeCtrl *tree,wxEvtHandler *handler,bool strict)
1953
if(!tree) // No tree to get data from - ignore event
1956
MiscTreeItemData* data =
1957
(MiscTreeItemData*)tree->GetItemData(event.GetItem());
1961
return true; // On doubt, allow event
1968
wxEvtHandler *h = data->GetOwner();
1969
if((h && h!=handler) || (strict && !h))
1970
{ // Tree Item belongs to another handler - skip
1978
void cbProject::SetExtendedObjectNamesGeneration(bool ext)
1980
bool changed = m_ExtendedObjectNamesGeneration != ext;
1982
// update it now because SetObjName() below will call GetExtendedObjectNamesGeneration()
1983
// so it must be up-to-date
1984
m_ExtendedObjectNamesGeneration = ext;
1988
for (FilesList::Node* node = m_Files.GetFirst(); node; node = node->GetNext())
1990
ProjectFile* f = node->GetData();
1991
f->SetObjName(f->relativeToCommonTopLevelPath);
1992
f->UpdateFileDetails();
1999
bool cbProject::GetExtendedObjectNamesGeneration() const
2001
return m_ExtendedObjectNamesGeneration;
2004
void cbProject::SetNotes(const wxString& notes)
2006
if (m_Notes != notes)
2013
const wxString& cbProject::GetNotes() const
2018
void cbProject::SetShowNotesOnLoad(bool show)
2020
if (m_AutoShowNotesOnLoad != show)
2022
m_AutoShowNotesOnLoad = show;
2027
bool cbProject::GetShowNotesOnLoad() const
2029
return m_AutoShowNotesOnLoad;
2032
void cbProject::ShowNotes(bool nonEmptyOnly, bool editable)
2034
if (!editable && nonEmptyOnly && m_Notes.IsEmpty())
2037
GenericMultiLineNotesDlg dlg(Manager::Get()->GetAppWindow(),
2038
_("Notes about ") + m_Title,
2042
if (dlg.ShowModal() == wxID_OK)
2045
SetNotes(dlg.GetNotes());
2049
void cbProject::SetTitle(const wxString& title)
2051
if ( title != GetTitle() )
2053
CompileTargetBase::SetTitle(title);
2054
NotifyPlugins(cbEVT_PROJECT_RENAMED);
2058
TiXmlNode* cbProject::GetExtensionsNode()
2060
if (!m_pExtensionsElement)
2061
m_pExtensionsElement = new TiXmlElement(cbU2C(_T("Extensions")));
2062
return m_pExtensionsElement;
2065
void cbProject::AddToExtensions(const wxString& stringDesc)
2067
// sample stringDesc:
2068
// node/+subnode/subsubnode:attr=val
2070
TiXmlElement* elem = GetExtensionsNode()->ToElement();
2074
// ignore consecutive slashes
2075
while (pos < stringDesc.Length() && stringDesc.GetChar(pos) == _T('/'))
2080
// find next slash or colon
2081
size_t nextPos = pos;
2082
while (nextPos < stringDesc.Length() && stringDesc.GetChar(++nextPos) != _T('/') && stringDesc.GetChar(nextPos) != _T(':'))
2085
wxString current = stringDesc.Mid(pos, nextPos - pos);
2086
if (current.IsEmpty() || current[0] == _T(':')) // abort on invalid case: "node/:attr=val" (consecutive "/:")
2089
// find or create the subnode
2090
bool forceAdd = current[0] == _T('+');
2092
current.Remove(0, 1); // remove '+'
2093
TiXmlElement* sub = !forceAdd ? elem->FirstChildElement(cbU2C(current)) : 0;
2096
sub = elem->InsertEndChild(TiXmlElement(cbU2C(current)))->ToElement();
2102
if (stringDesc.GetChar(nextPos) == _T(':'))
2104
// yes, just parse the attribute now
2105
pos = nextPos + 1; // skip the colon
2107
while (nextPos < stringDesc.Length() && stringDesc.GetChar(++nextPos) != _T('='))
2109
if (pos == nextPos || nextPos == stringDesc.Length())
2111
// invalid attribute
2115
wxString key = stringDesc.Mid(pos, nextPos - pos);
2116
wxString val = stringDesc.Mid(nextPos + 1, stringDesc.Length() - nextPos - 1);
2117
sub->SetAttribute(cbU2C(key), cbU2C(val));
2125
pos = nextPos; // prepare for next loop
2129
void cbProject::ProjectFileRenamed(ProjectFile* pf)
2131
for (ProjectFiles::iterator it = m_ProjectFilesMap.begin(); it != m_ProjectFilesMap.end(); ++it)
2133
ProjectFile* itpf = it->second;
2137
m_ProjectFilesMap.erase(it);
2138
m_ProjectFilesMap[UnixFilename(pf->file.GetFullPath())] = pf;