~ubuntu-branches/ubuntu/hardy/codeblocks/hardy-backports

« back to all changes in this revision

Viewing changes to src/sdk/cbproject.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Michael Casadevall
  • Date: 2008-07-17 04:39:23 UTC
  • Revision ID: james.westby@ubuntu.com-20080717043923-gmsy5cwkdjswghkm
Tags: upstream-8.02
ImportĀ upstreamĀ versionĀ 8.02

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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: 4909 $
 
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 $
 
8
 */
 
9
 
 
10
#include "sdk_precomp.h"
 
11
 
 
12
#ifndef wxUSE_CHOICEDLG
 
13
    #define wxUSE_CHOICEDLG 1
 
14
#endif
 
15
 
 
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>
 
21
 
 
22
#ifndef CB_PRECOMP
 
23
    #include "cbproject.h" // class's header file
 
24
    #include "compiler.h" // GetSwitches
 
25
    #include "sdk_events.h"
 
26
    #include "manager.h"
 
27
    #include "cbeditor.h"
 
28
    #include "globals.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"
 
40
#endif
 
41
 
 
42
#include <map>
 
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"
 
51
 
 
52
 
 
53
namespace compatibility { typedef TernaryCondTypedef<wxMinimumVersion<2,5>::eval, wxTreeItemIdValue, long int>::eval tree_cookie_t; };
 
54
 
 
55
 
 
56
// class constructor
 
57
cbProject::cbProject(const wxString& filename)
 
58
    : m_CustomMakefile(false),
 
59
    m_Loaded(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)
 
66
{
 
67
    SetCompilerID(CompilerFactory::GetDefaultCompilerID());
 
68
    SetModified(false);
 
69
 
 
70
    m_Files.Clear();
 
71
    if (!filename.IsEmpty() && wxFileExists(filename) || wxDirExists(filename))
 
72
    {
 
73
        // existing project
 
74
        m_Filename = filename;
 
75
        m_BasePath = GetBasePath();
 
76
        Open();
 
77
    }
 
78
    else
 
79
    {
 
80
        // new project
 
81
        SetModified(true);
 
82
        if (filename.IsEmpty())
 
83
        {
 
84
            m_Filename = CreateUniqueFilename();
 
85
            m_Loaded = SaveAs();
 
86
        }
 
87
        else
 
88
        {
 
89
            m_Filename = filename;
 
90
            m_Loaded = Save();
 
91
        }
 
92
        if (m_Loaded)
 
93
        {
 
94
            wxFileName fname(m_Filename);
 
95
            m_Title = fname.GetName();
 
96
            m_BasePath = GetBasePath();
 
97
            m_CommonTopLevelPath = GetBasePath() + wxFileName::GetPathSeparator();
 
98
 
 
99
            // moved to ProjectManager::LoadProject()
 
100
            // see explanation there...
 
101
//            NotifyPlugins(cbEVT_PROJECT_OPEN);
 
102
        }
 
103
    }
 
104
}
 
105
 
 
106
// class destructor
 
107
cbProject::~cbProject()
 
108
{
 
109
    // moved to ProjectManager::CloseProject()
 
110
    // see explanation there...
 
111
//    NotifyPlugins(cbEVT_PROJECT_CLOSE);
 
112
 
 
113
    ClearAllProperties();
 
114
}
 
115
 
 
116
void cbProject::NotifyPlugins(wxEventType type, const wxString& targetName, const wxString& oldTargetName)
 
117
{
 
118
    CodeBlocksEvent event(type);
 
119
    event.SetProject(this);
 
120
    event.SetBuildTargetName(targetName);
 
121
    event.SetOldBuildTargetName(oldTargetName);
 
122
    Manager::Get()->ProcessEvent(event);
 
123
}
 
124
 
 
125
void cbProject::SetCompilerID(const wxString& id)
 
126
{
 
127
// TODO (mandrav##): Is this needed? The project's compiler has nothing to do with the targets' compilers...
 
128
 
 
129
    CompileTargetBase::SetCompilerID(id);
 
130
    if (id != GetCompilerID())
 
131
    {
 
132
        // update object filenames
 
133
        for (unsigned int i = 0; i < m_Targets.GetCount(); ++i)
 
134
        {
 
135
            ProjectBuildTarget* target = m_Targets[i];
 
136
            if (target)
 
137
            {
 
138
                Compiler* compiler = CompilerFactory::GetCompiler(target->GetCompilerID());
 
139
                if (!compiler)
 
140
                    continue;
 
141
 
 
142
                int count = GetFilesCount();
 
143
                for (int i = 0; i < count; ++i)
 
144
                {
 
145
                    ProjectFile* pf = GetFile(i);
 
146
                    wxFileName obj(pf->GetObjName());
 
147
                    if (FileTypeOf(pf->relativeFilename) != ftResource &&
 
148
                        obj.GetExt() == compiler->GetSwitches().objectExtension)
 
149
                    {
 
150
                        obj.SetExt(compiler->GetSwitches().objectExtension);
 
151
                        pf->SetObjName(obj.GetFullName());
 
152
                    }
 
153
                }
 
154
            }
 
155
        }
 
156
    }
 
157
}
 
158
 
 
159
bool cbProject::GetModified() const
 
160
{
 
161
    // check base options
 
162
    if (CompileOptionsBase::GetModified())
 
163
        return true;
 
164
 
 
165
    // check targets
 
166
    for (unsigned int i = 0; i < m_Targets.GetCount(); ++i)
 
167
    {
 
168
        ProjectBuildTarget* target = m_Targets[i];
 
169
        if (target->GetModified())
 
170
            return true;
 
171
    }
 
172
 
 
173
    return false;
 
174
}
 
175
 
 
176
void cbProject::SetModified(bool modified)
 
177
{
 
178
    CompileOptionsBase::SetModified(modified);
 
179
 
 
180
    // modify targets
 
181
    for (unsigned int i = 0; i < m_Targets.GetCount(); ++i)
 
182
    {
 
183
        ProjectBuildTarget* target = m_Targets[i];
 
184
        target->SetModified(modified);
 
185
    }
 
186
 
 
187
    if (!modified)
 
188
        m_LastSavedActiveTarget = m_ActiveTarget;
 
189
}
 
190
 
 
191
void cbProject::SetMakefileCustom(bool custom)
 
192
{
 
193
    if (m_CustomMakefile != custom)
 
194
    {
 
195
        m_CustomMakefile = custom;
 
196
        SetModified(true);
 
197
    }
 
198
}
 
199
 
 
200
wxString cbProject::CreateUniqueFilename()
 
201
{
 
202
    const wxString prefix = _("Untitled");
 
203
    wxString tmp;
 
204
    ProjectsArray* arr = Manager::Get()->GetProjectManager()->GetProjects();
 
205
    int projCount = arr->GetCount();
 
206
    int iter = 1;
 
207
    bool ok = false;
 
208
    tmp << prefix << wxString::Format(_T("%d"), iter);
 
209
    while (!ok)
 
210
    {
 
211
        tmp.Clear();
 
212
        tmp << prefix << wxString::Format(_T("%d"), iter);
 
213
 
 
214
        ok = true;
 
215
        for (int i = 0; i < projCount; ++i)
 
216
        {
 
217
            cbProject* prj = arr->Item(i);
 
218
            wxFileName fname(prj->GetFilename());
 
219
 
 
220
            if (fname.GetName().Matches(tmp))
 
221
            {
 
222
                ok = false;
 
223
                break;
 
224
            }
 
225
        }
 
226
        if (ok)
 
227
            break;
 
228
        ++iter;
 
229
    }
 
230
    return tmp << _T(".") << FileFilters::CODEBLOCKS_EXT;
 
231
}
 
232
 
 
233
void cbProject::ClearAllProperties()
 
234
{
 
235
    Delete(m_pExtensionsElement);
 
236
 
 
237
    m_Files.DeleteContents(true);
 
238
    m_Files.Clear();
 
239
    m_Files.DeleteContents(false);
 
240
    m_CompilerOptions.Clear();
 
241
    m_LinkerOptions.Clear();
 
242
    m_IncludeDirs.Clear();
 
243
    m_LibDirs.Clear();
 
244
 
 
245
    while (m_Targets.GetCount())
 
246
    {
 
247
        ProjectBuildTarget* target = m_Targets[0];
 
248
        delete target;
 
249
        m_Targets.RemoveAt(0);
 
250
    }
 
251
    SetModified(true);
 
252
 
 
253
    NotifyPlugins(cbEVT_BUILDTARGET_SELECTED);
 
254
}
 
255
 
 
256
void cbProject::Open()
 
257
{
 
258
    m_Loaded = false;
 
259
    m_ProjectFilesMap.clear();
 
260
    Delete(m_pExtensionsElement);
 
261
 
 
262
    if (!wxFileName::FileExists(m_Filename) && !wxFileName::DirExists(m_Filename))
 
263
    {
 
264
        wxString msg;
 
265
        msg.Printf(_("Project '%s' does not exist..."), m_Filename.c_str());
 
266
        cbMessageBox(msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR);
 
267
        return;
 
268
    }
 
269
 
 
270
    bool fileUpgraded = false;
 
271
    bool fileModified = false;
 
272
    wxFileName fname(m_Filename);
 
273
    FileType ft = FileTypeOf(m_Filename);
 
274
    if (ft == ftCodeBlocksProject)
 
275
    {
 
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;
 
283
 
 
284
        if (m_Loaded)
 
285
        {
 
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);
 
293
 
 
294
            // moved to ProjectManager::LoadProject()
 
295
            // see explanation there...
 
296
    //        NotifyPlugins(cbEVT_PROJECT_OPEN);
 
297
 
 
298
            if (fileUpgraded)
 
299
            {
 
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);
 
305
            }
 
306
            m_LastModified = fname.GetModificationTime();
 
307
        }
 
308
    }
 
309
} // end of Open
 
310
 
 
311
void cbProject::CalculateCommonTopLevelPath()
 
312
{
 
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 ..
 
316
    // in their paths
 
317
    wxString sep = wxFileName::GetPathSeparator();
 
318
    wxFileName base = GetBasePath() + sep;
 
319
    Manager::Get()->GetLogManager()->DebugLog(_T("Project's base path: ") + base.GetFullPath());
 
320
 
 
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())
 
325
    {
 
326
        ProjectFile* f = node->GetData();
 
327
        wxString tmp = f->relativeFilename;
 
328
        wxString tmpbase = m_BasePath;
 
329
 
 
330
        size_t pos = 0;
 
331
        while (pos < tmp.Length() &&
 
332
            (tmp.GetChar(pos) == _T('.') || tmp.GetChar(pos) == _T('/') || tmp.GetChar(pos) == _T('\\')))
 
333
        {
 
334
            ++pos;
 
335
        }
 
336
        if (pos > 0 && pos < tmp.Length())
 
337
        {
 
338
            tmpbase << sep << tmp.Left(pos) << sep;
 
339
            f->relativeToCommonTopLevelPath = tmp.Right(tmp.Length() - pos);
 
340
        }
 
341
        else
 
342
            f->relativeToCommonTopLevelPath = tmp;
 
343
        f->SetObjName(f->relativeToCommonTopLevelPath);
 
344
 
 
345
        wxFileName tmpbaseF(tmpbase);
 
346
        tmpbaseF.Normalize(wxPATH_NORM_DOTS);
 
347
        if (tmpbaseF.GetDirCount() < base.GetDirCount())
 
348
            base = tmpbaseF;
 
349
    }
 
350
 
 
351
    m_CommonTopLevelPath = base.GetFullPath();
 
352
    Manager::Get()->GetLogManager()->DebugLog(_T("Project's common toplevel path: ") + m_CommonTopLevelPath);
 
353
}
 
354
 
 
355
wxString cbProject::GetCommonTopLevelPath() const
 
356
{
 
357
    return m_CommonTopLevelPath;
 
358
}
 
359
 
 
360
void cbProject::Touch()
 
361
{
 
362
    m_LastModified = wxDateTime::Now();
 
363
}
 
364
 
 
365
bool cbProject::SaveAs()
 
366
{
 
367
    wxFileName fname;
 
368
    fname.Assign(m_Filename);
 
369
    wxFileDialog dlg(Manager::Get()->GetAppWindow(),
 
370
                    _("Save file"),
 
371
                    fname.GetPath(),
 
372
                    fname.GetFullName(),
 
373
                    FileFilters::GetFilterString(_T('.') + FileFilters::CODEBLOCKS_EXT),
 
374
                    wxSAVE | wxOVERWRITE_PROMPT);
 
375
 
 
376
    PlaceWindow(&dlg);
 
377
    if (dlg.ShowModal() != wxID_OK)
 
378
        return false;
 
379
    m_Filename = dlg.GetPath();
 
380
    fname.Assign(m_Filename);
 
381
 
 
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);
 
389
 
 
390
//    Manager::Get()->GetProjectManager()->GetTree()->SetItemText(m_ProjectNode, fname.GetFullName());
 
391
    if (!m_Loaded)
 
392
        AddDefaultBuildTarget();
 
393
    ProjectLoader loader(this);
 
394
    if (loader.Save(m_Filename, m_pExtensionsElement))
 
395
    {
 
396
        wxFileName fname(m_Filename);
 
397
        m_LastModified = fname.GetModificationTime();
 
398
        NotifyPlugins(cbEVT_PROJECT_SAVE);
 
399
        return true;
 
400
    }
 
401
 
 
402
    cbMessageBox(_("Couldn't save project ") + m_Filename + _("\n(Maybe the file is write-protected?)"), _("Warning"), wxICON_WARNING);
 
403
    return false;
 
404
}
 
405
 
 
406
bool cbProject::Save()
 
407
{
 
408
    if (m_Filename.IsEmpty())
 
409
        return SaveAs();
 
410
    ProjectLoader loader(this);
 
411
    if (loader.Save(m_Filename, m_pExtensionsElement))
 
412
    {
 
413
        wxFileName fname(m_Filename);
 
414
        m_LastModified = fname.GetModificationTime();
 
415
        NotifyPlugins(cbEVT_PROJECT_SAVE);
 
416
        return true;
 
417
    }
 
418
 
 
419
    cbMessageBox(_("Couldn't save project ") + m_Filename + _("\n(Maybe the file is write-protected?)"), _("Warning"), wxICON_WARNING);
 
420
    return false;
 
421
}
 
422
 
 
423
bool cbProject::SaveLayout()
 
424
{
 
425
    if (m_Filename.IsEmpty())
 
426
        return false;
 
427
 
 
428
    wxFileName fname(m_Filename);
 
429
    fname.SetExt(_T("layout"));
 
430
    ProjectLayoutLoader loader(this);
 
431
    return loader.Save(fname.GetFullPath());
 
432
}
 
433
 
 
434
bool cbProject::LoadLayout()
 
435
{
 
436
   if (m_Filename.IsEmpty())
 
437
        return false;
 
438
    int openmode = Manager::Get()->GetConfigManager(_T("project_manager"))->ReadInt(_T("/open_files"), (long int)1);
 
439
    bool result = false;
 
440
 
 
441
    if(openmode==2)
 
442
    {
 
443
        // Do not open any files
 
444
        result = true;
 
445
    }
 
446
    else
 
447
    {
 
448
        Manager::Get()->GetEditorManager()->HideNotebook();
 
449
        if(openmode == 0) // Open all files
 
450
        {
 
451
            FilesList::Node* node = m_Files.GetFirst();
 
452
            while(node)
 
453
            {
 
454
                ProjectFile* f = node->GetData();
 
455
                Manager::Get()->GetEditorManager()->Open(f->file.GetFullPath(),0,f);
 
456
                node = node->GetNext();
 
457
            }
 
458
            result = true;
 
459
        }
 
460
        else if(openmode == 1)// Open last open files
 
461
        {
 
462
            wxFileName fname(m_Filename);
 
463
            fname.SetExt(_T("layout"));
 
464
            ProjectLayoutLoader loader(this);
 
465
            if (loader.Open(fname.GetFullPath()))
 
466
            {
 
467
                typedef std::map<int, ProjectFile*> open_files_map;
 
468
                open_files_map open_files;
 
469
 
 
470
                // Get all files to open and sort them according to their tab-position:
 
471
                FilesList::Node* node = m_Files.GetFirst();
 
472
                while(node)
 
473
                {
 
474
                    ProjectFile* f = node->GetData();
 
475
                    if (f->editorOpen)
 
476
                        open_files[f->editorTabPos] = f;
 
477
                    node = node->GetNext();
 
478
                }
 
479
 
 
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)
 
483
                {
 
484
                    filesInMemory.push_back(Manager::Get()->GetFileManager()->Load((*it).second->file.GetFullPath()));
 
485
                }
 
486
                // Open all requested files:
 
487
                size_t i = 0;
 
488
                for (open_files_map::iterator it = open_files.begin(); it != open_files.end(); ++it)
 
489
                {
 
490
                    cbEditor* ed = Manager::Get()->GetEditorManager()->Open(filesInMemory[i], (*it).second->file.GetFullPath(),0,(*it).second);
 
491
                    if (ed)
 
492
                        ed->SetProjectFile((*it).second);
 
493
                    ++i;
 
494
                }
 
495
 
 
496
                ProjectFile* f = loader.GetTopProjectFile();
 
497
                if (f)
 
498
                {
 
499
                    Manager::Get()->GetLogManager()->DebugLog(_T("Top Editor: ") + f->file.GetFullPath());
 
500
                    EditorBase* eb = Manager::Get()->GetEditorManager()->Open(f->file.GetFullPath());
 
501
                    if(eb)
 
502
                        eb->Activate();
 
503
                }
 
504
//                Manager::Get()->GetAppWindow()->Thaw();
 
505
            }
 
506
            result = true;
 
507
        }
 
508
        else
 
509
            result = false;
 
510
        Manager::Get()->GetEditorManager()->ShowNotebook();
 
511
    }
 
512
    return result;
 
513
}
 
514
 
 
515
void cbProject::BeginAddFiles()
 
516
{
 
517
        CodeBlocksEvent event(cbEVT_PROJECT_BEGIN_ADD_FILES);
 
518
        event.SetProject(this);
 
519
        Manager::Get()->ProcessEvent(event);
 
520
}
 
521
 
 
522
void cbProject::EndAddFiles()
 
523
{
 
524
        CodeBlocksEvent event(cbEVT_PROJECT_END_ADD_FILES);
 
525
        event.SetProject(this);
 
526
        Manager::Get()->ProcessEvent(event);
 
527
}
 
528
 
 
529
void cbProject::BeginRemoveFiles()
 
530
{
 
531
        CodeBlocksEvent event(cbEVT_PROJECT_BEGIN_REMOVE_FILES);
 
532
        event.SetProject(this);
 
533
        Manager::Get()->ProcessEvent(event);
 
534
}
 
535
 
 
536
void cbProject::EndRemoveFiles()
 
537
{
 
538
        CodeBlocksEvent event(cbEVT_PROJECT_END_REMOVE_FILES);
 
539
        event.SetProject(this);
 
540
        Manager::Get()->ProcessEvent(event);
 
541
}
 
542
 
 
543
ProjectFile* cbProject::AddFile(const wxString& targetName, const wxString& filename, bool compile, bool link, unsigned short int weight)
 
544
{
 
545
    int idx = IndexOfBuildTargetName(targetName);
 
546
    return AddFile(idx, filename, compile, link, weight);
 
547
}
 
548
 
 
549
ProjectFile* cbProject::AddFile(int targetIndex, const wxString& filename, bool compile, bool link, unsigned short int weight)
 
550
{
 
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
 
555
 
 
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()
 
560
 
 
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)...
 
567
*/
 
568
//    f = GetFileByFilename(filename, true, true);
 
569
//    if (!f)
 
570
//        f = GetFileByFilename(filename, false, true);
 
571
//    if (f)
 
572
//    {
 
573
//        if (targetIndex >= 0 && targetIndex < (int)m_Targets.GetCount())
 
574
//            f->AddBuildTarget(m_Targets[targetIndex]->GetTitle());
 
575
//        return f;
 
576
//    }
 
577
 
 
578
    // quick test
 
579
    ProjectFile* f = m_ProjectFilesMap[UnixFilename(filename)];
 
580
    if (f)
 
581
        return f;
 
582
 
 
583
    // create file
 
584
    f = new ProjectFile(this);
 
585
    bool localCompile, localLink;
 
586
    wxFileName fname(filename);
 
587
    wxString ext;
 
588
 
 
589
    FileType ft = FileTypeOf(filename);
 
590
 
 
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");
 
596
    else
 
597
        f->compilerVar = _T("CPP"); // default
 
598
 
 
599
    if (!m_Targets.GetCount())
 
600
    {
 
601
        // no targets in project; add default
 
602
        AddDefaultBuildTarget();
 
603
        if (!m_Targets.GetCount())
 
604
        {
 
605
            delete f;
 
606
            return 0L; // if that failed, fail addition of file...
 
607
        }
 
608
    }
 
609
 
 
610
    bool isResource = FileTypeOf(filename) == ftResource;
 
611
 
 
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())
 
621
        {
 
622
                Compiler* c = CompilerFactory::GetCompiler(GetCompilerID());
 
623
                if (c)
 
624
                {
 
625
                        const CompilerTool* t = &c->GetCompilerTool(isResource ? ctCompileResourceCmd : ctCompileObjectCmd, fname.GetExt());
 
626
                        if (t->generatedFiles.GetCount())
 
627
                        {
 
628
                                GenFilesHackMap[c] = t;
 
629
                        }
 
630
                }
 
631
 
 
632
                for (unsigned int i = 0; i < m_Targets.GetCount(); ++i)
 
633
                {
 
634
                        Compiler* c = CompilerFactory::GetCompiler(m_Targets[i]->GetCompilerID());
 
635
                        if (GenFilesHackMap.find(c) != GenFilesHackMap.end())
 
636
                                continue; // compiler already in map
 
637
 
 
638
                        if (c)
 
639
                        {
 
640
                                const CompilerTool* t = &c->GetCompilerTool(isResource ? ctCompileResourceCmd : ctCompileObjectCmd, fname.GetExt());
 
641
                                if (t->generatedFiles.GetCount())
 
642
                                {
 
643
                                        GenFilesHackMap[c] = t;
 
644
                                }
 
645
                        }
 
646
                }
 
647
        }
 
648
        else
 
649
        {
 
650
                // targetIndex is valid: just add a single entry to the map
 
651
                Compiler* c = CompilerFactory::GetCompiler(m_Targets[targetIndex]->GetCompilerID());
 
652
                if (c)
 
653
                {
 
654
                        const CompilerTool* t = &c->GetCompilerTool(isResource ? ctCompileResourceCmd : ctCompileObjectCmd, fname.GetExt());
 
655
                        if (t->generatedFiles.GetCount())
 
656
                        {
 
657
                                GenFilesHackMap[c] = t;
 
658
                        }
 
659
                }
 
660
        }
 
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 :)
 
664
 
 
665
    // add the build target
 
666
    if (targetIndex >= 0 && targetIndex < (int)m_Targets.GetCount())
 
667
        f->AddBuildTarget(m_Targets[targetIndex]->GetTitle());
 
668
 
 
669
    localCompile = compile &&
 
670
                    (ft == ftSource ||
 
671
                    ft == ftResource ||
 
672
                    !GenFilesHackMap.empty());
 
673
    localLink = link &&
 
674
                (ft == ftSource ||
 
675
                ft == ftResource ||
 
676
                ft == ftObject ||
 
677
                ft == ftResourceBin ||
 
678
                ft == ftStaticLib);
 
679
 
 
680
    f->compile = localCompile;
 
681
    f->link = localLink;
 
682
 
 
683
    wxString local_filename = filename;
 
684
 
 
685
#ifdef __WXMSW__
 
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())
 
690
    {
 
691
        fname.Assign(filename);
 
692
    }
 
693
    else
 
694
#endif
 
695
    {
 
696
        // make sure the filename is relative to the project's base path
 
697
        if (fname.IsAbsolute())
 
698
        {
 
699
            fname.MakeRelativeTo(GetBasePath());
 
700
            local_filename = fname.GetFullPath();
 
701
        }
 
702
        fname.Assign(GetBasePath() + wxFILE_SEP_PATH + local_filename);
 
703
    }
 
704
    fname.Normalize(wxPATH_NORM_DOTS | wxPATH_NORM_TILDE, GetBasePath());
 
705
 
 
706
    wxString fullFilename = fname.GetFullPath();
 
707
    f->file.Assign(fname);
 
708
    f->relativeFilename = UnixFilename(local_filename);
 
709
 
 
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);
 
713
    if (existing == f)
 
714
    {
 
715
        delete f;
 
716
        if (targetIndex >= 0 && targetIndex < (int)m_Targets.GetCount())
 
717
            existing->AddBuildTarget(m_Targets[targetIndex]->GetTitle());
 
718
        return existing;
 
719
    }
 
720
 
 
721
    m_Files.Append(f);
 
722
    if (!m_CurrentlyLoading)
 
723
    {
 
724
        // check if we really need to recalculate the common top-level path for the project
 
725
        if (!fullFilename.StartsWith(m_CommonTopLevelPath))
 
726
            CalculateCommonTopLevelPath();
 
727
        else
 
728
        {
 
729
            // set f->relativeToCommonTopLevelPath
 
730
            f->relativeToCommonTopLevelPath = fullFilename.Right(fullFilename.Length() - m_CommonTopLevelPath.Length());
 
731
        }
 
732
    }
 
733
    SetModified(true);
 
734
    m_ProjectFilesMap[UnixFilename(f->relativeFilename)] = f; // add to hashmap
 
735
 
 
736
    if (!wxFileExists(fullFilename))
 
737
        f->SetFileState(fvsMissing);
 
738
    else if (!wxFile::Access(fullFilename.c_str(), wxFile::write)) // readonly
 
739
        f->SetFileState(fvsReadOnly);
 
740
 
 
741
    if (!GenFilesHackMap.empty())
 
742
    {
 
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)
 
746
                {
 
747
                        const CompilerTool* tool = it->second;
 
748
                        for (size_t i = 0; i < tool->generatedFiles.GetCount(); ++i)
 
749
                        {
 
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);
 
760
 
 
761
                                ProjectFile* pfile = AddFile(targetIndex, UnixFilename(tmps));
 
762
                                if (!pfile)
 
763
                                        Manager::Get()->GetLogManager()->DebugLog(_T("Can't add auto-generated file ") + tmps);
 
764
                                else
 
765
                                {
 
766
                                        f->generatedFiles.push_back(pfile);
 
767
                                        pfile->autoGeneratedBy = f;
 
768
                                }
 
769
                        }
 
770
                }
 
771
    }
 
772
 
 
773
    return f;
 
774
}
 
775
 
 
776
bool cbProject::RemoveFile(ProjectFile* pf)
 
777
{
 
778
    if (!pf)
 
779
        return false;
 
780
    m_ProjectFilesMap.erase(UnixFilename(pf->relativeFilename)); // remove from hashmap
 
781
    Manager::Get()->GetEditorManager()->Close(pf->file.GetFullPath());
 
782
 
 
783
    FilesList::Node* node = m_Files.Find(pf);
 
784
    if (!node)
 
785
    {
 
786
        Manager::Get()->GetLogManager()->DebugLog(_T("Can't locate node for ProjectFile* !"));
 
787
    }
 
788
    else
 
789
    {
 
790
        m_Files.DeleteNode(node);
 
791
    }
 
792
 
 
793
    // remove this file from all targets too
 
794
    for (unsigned int i = 0; i < m_Targets.GetCount(); ++i)
 
795
    {
 
796
        ProjectBuildTarget* target = m_Targets[i];
 
797
        if (target)
 
798
        {
 
799
            target->GetFilesList().DeleteObject(pf);
 
800
        }
 
801
    }
 
802
 
 
803
    // if this is auto-generated, inform "parent"
 
804
    if (pf->autoGeneratedBy)
 
805
    {
 
806
        ProjectFilesVector::iterator it = std::find(pf->autoGeneratedBy->generatedFiles.begin(), pf->autoGeneratedBy->generatedFiles.end(), pf);
 
807
        pf->autoGeneratedBy->generatedFiles.erase(it);
 
808
    }
 
809
 
 
810
    // also remove generated files (see above code: files will empty the vector)
 
811
    while (pf->generatedFiles.size())
 
812
    {
 
813
        RemoveFile(pf->generatedFiles[0]);
 
814
    }
 
815
    pf->generatedFiles.clear();
 
816
 
 
817
    delete pf;
 
818
 
 
819
    SetModified(true);
 
820
    return true;
 
821
}
 
822
 
 
823
bool cbProject::RemoveFile(int index)
 
824
{
 
825
    if (index < 0 || index >= (int)m_Files.GetCount())
 
826
        return false; // invalid index
 
827
    ProjectFile* f = m_Files[index];
 
828
    return RemoveFile(f);
 
829
}
 
830
 
 
831
int filesSort(const ProjectFile** arg1, const ProjectFile** arg2)
 
832
{
 
833
    return (*arg1)->file.GetFullPath().CompareTo((*arg2)->file.GetFullPath());
 
834
}
 
835
 
 
836
void cbProject::BuildTree(wxTreeCtrl* tree, const wxTreeItemId& root, bool categorize, bool useFolders, FilesGroupsAndMasks* fgam)
 
837
{
 
838
    if (!tree)
 
839
        return;
 
840
 
 
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);
 
845
 
 
846
    //sort list of files
 
847
    m_Files.Sort(filesSort);
 
848
 
 
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;
 
854
 
 
855
    // create file-type categories nodes (if enabled)
 
856
    wxTreeItemId* pGroupNodes = 0L;
 
857
    if (categorize && fgam)
 
858
    {
 
859
        pGroupNodes = new wxTreeItemId[fgam->GetGroupsCount()];
 
860
        for (unsigned int i = 0; i < fgam->GetGroupsCount(); ++i)
 
861
        {
 
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);
 
865
        }
 
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);
 
872
    }
 
873
    // Now add any virtual folders
 
874
    for (size_t i = 0; i < m_VirtualFolders.GetCount(); ++i)
 
875
    {
 
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);
 
879
    }
 
880
 
 
881
    // iterate all project files and add them to the tree
 
882
    int count = 0;
 
883
    for (FilesList::Node* node = m_Files.GetFirst(); node; node = node->GetNext())
 
884
    {
 
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());
 
890
 
 
891
        wxFileName nodefile = f->file;
 
892
        nodefile.MakeRelativeTo(m_CommonTopLevelPath);
 
893
        wxString nodetext = nodefile.GetFullPath();
 
894
        FileTreeData::FileTreeDataKind folders_kind = FileTreeData::ftdkFolder;
 
895
 
 
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())
 
900
        {
 
901
            bool found = false;
 
902
            // auto-generated files end up all together
 
903
            if (f->autoGeneratedBy)
 
904
            {
 
905
                parentNode = generated;
 
906
                found = true;
 
907
            }
 
908
 
 
909
            // else try to match a group
 
910
            if (!found)
 
911
            {
 
912
                for (unsigned int i = 0; i < fgam->GetGroupsCount(); ++i)
 
913
                {
 
914
                    wxFileName fname(f->relativeToCommonTopLevelPath);
 
915
                    if (fgam->MatchesMask(fname.GetFullName(), i))
 
916
                    {
 
917
                        parentNode = pGroupNodes[i];
 
918
                        found = true;
 
919
                        break;
 
920
                    }
 
921
                }
 
922
            }
 
923
 
 
924
            // if not matched a group, put it in "Others" group
 
925
            if (!found)
 
926
                parentNode = others;
 
927
        }
 
928
        else if ((!categorize || !pGroupNodes || !fgam) && f->virtual_path.IsEmpty())
 
929
        {
 
930
            parentNode = m_ProjectNode;
 
931
        }
 
932
        /* Else put the file under virtual folder */
 
933
        else if (!f->virtual_path.IsEmpty())
 
934
        {
 
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);
 
939
 
 
940
            if (m_VirtualFolders.Index(f->virtual_path + slash) == wxNOT_FOUND)
 
941
                m_VirtualFolders.Add(f->virtual_path + slash);
 
942
        }
 
943
 
 
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);
 
946
    }
 
947
 
 
948
    // remove empty tree nodes (like empty groups)
 
949
    if (categorize && fgam)
 
950
    {
 
951
        for (unsigned int i = 0; i < fgam->GetGroupsCount(); ++i)
 
952
        {
 
953
            if (tree->GetChildrenCount(pGroupNodes[i], false) == 0)
 
954
                tree->Delete(pGroupNodes[i]);
 
955
        }
 
956
        if (tree->GetChildrenCount(others, false) == 0)
 
957
            tree->Delete(others);
 
958
        if (tree->GetChildrenCount(generated, false) == 0)
 
959
            tree->Delete(generated);
 
960
    }
 
961
    delete[] pGroupNodes;
 
962
 
 
963
    tree->Expand(m_ProjectNode);
 
964
}
 
965
 
 
966
// helper function used by AddTreeNode
 
967
static wxString GetRelativeFolderPath(wxTreeCtrl* tree, wxTreeItemId parent)
 
968
{
 
969
    wxString fld;
 
970
    while (parent.IsOk())
 
971
    {
 
972
        FileTreeData* ftd = (FileTreeData*)tree->GetItemData(parent);
 
973
        if (!ftd || (ftd->GetKind() != FileTreeData::ftdkFolder && ftd->GetKind() != FileTreeData::ftdkVirtualFolder))
 
974
            break;
 
975
        fld.Prepend(tree->GetItemText(parent) + wxFILE_SEP_PATH);
 
976
        parent = tree->GetItemParent(parent);
 
977
    }
 
978
    return fld;
 
979
}
 
980
 
 
981
wxTreeItemId cbProject::AddTreeNode(wxTreeCtrl* tree,
 
982
                                    const wxString& text,
 
983
                                    const wxTreeItemId& parent,
 
984
                                    bool useFolders,
 
985
                                    FileTreeData::FileTreeDataKind folders_kind,
 
986
                                    bool compiles,
 
987
                                    int image,
 
988
                                    FileTreeData* data)
 
989
{
 
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...
 
993
 
 
994
    wxTreeItemId ret;
 
995
 
 
996
    if (text.IsEmpty())
 
997
        return ret;
 
998
 
 
999
    wxString path = text;
 
1000
 
 
1001
    // special case for windows and files on a different drive
 
1002
    if (platform::windows && path.Length() > 1 && path.GetChar(1) == _T(':'))
 
1003
        path.Remove(1, 1);
 
1004
 
 
1005
    int pos = path.Find(_T('/'));
 
1006
    if (pos == -1)
 
1007
        pos = path.Find(_T('\\'));
 
1008
    if (useFolders && pos >= 0)
 
1009
    {
 
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('\\'))
 
1014
            ++pos;
 
1015
        path = path.Right(path.Length() - pos - 1);
 
1016
 
 
1017
        compatibility::tree_cookie_t cookie = 0;
 
1018
 
 
1019
        wxTreeItemId newparent = tree->GetFirstChild(parent, cookie);
 
1020
        while (newparent)
 
1021
        {
 
1022
            wxString itemText = tree->GetItemText(newparent);
 
1023
            if (itemText.Matches(folder))
 
1024
                break;
 
1025
            newparent = tree->GetNextChild(parent, cookie);
 
1026
        }
 
1027
 
 
1028
        if (!newparent)
 
1029
        {
 
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();
 
1034
 
 
1035
            newparent = FindNodeToInsertAfter(tree, folder, parent, true);
 
1036
 
 
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);
 
1041
            else
 
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);
 
1046
        }
 
1047
        //tree->SortChildren(parent);
 
1048
        ret = AddTreeNode(tree, path, newparent, true, folders_kind, compiles, image, data);
 
1049
    }
 
1050
    else
 
1051
    {
 
1052
        ret = tree->AppendItem(parent, text, image, image, data);
 
1053
        if (!compiles)
 
1054
            tree->SetItemTextColour(ret, wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT));
 
1055
    }
 
1056
    return ret;
 
1057
}
 
1058
 
 
1059
wxTreeItemId cbProject::FindNodeToInsertAfter(wxTreeCtrl* tree, const wxString& text, const wxTreeItemId& parent, bool in_folders)
 
1060
{
 
1061
    wxTreeItemId result;
 
1062
 
 
1063
    if (tree && parent.IsOk())
 
1064
    {
 
1065
        compatibility::tree_cookie_t cookie = 0;
 
1066
 
 
1067
        int fldIdx = Manager::Get()->GetProjectManager()->FolderIconIndex();
 
1068
        int vfldIdx = Manager::Get()->GetProjectManager()->VirtualFolderIconIndex();
 
1069
        wxTreeItemId last;
 
1070
        bool last_is_folder = false;
 
1071
        wxTreeItemId child = tree->GetFirstChild(parent, cookie);
 
1072
        while (child)
 
1073
        {
 
1074
            bool is_folder = tree->GetItemImage(child) == fldIdx || tree->GetItemImage(child) == vfldIdx;
 
1075
 
 
1076
            if (in_folders)
 
1077
            {
 
1078
                if (!is_folder || text.CmpNoCase(tree->GetItemText(child)) < 0)
 
1079
                {
 
1080
                    result = last;
 
1081
                    break;
 
1082
                }
 
1083
            }
 
1084
            else
 
1085
            {
 
1086
                if (!is_folder && text.CmpNoCase(tree->GetItemText(child)) < 0)
 
1087
                {
 
1088
                    result = last;
 
1089
                    break;
 
1090
                }
 
1091
            }
 
1092
 
 
1093
            last = child;
 
1094
            last_is_folder = is_folder;
 
1095
            child = tree->GetNextChild(parent, cookie);
 
1096
        }
 
1097
        if (!result.IsOk())
 
1098
            result = last;
 
1099
    }
 
1100
 
 
1101
    return result;
 
1102
}
 
1103
 
 
1104
void cbProject::CopyTreeNodeRecursively(wxTreeCtrl* tree, const wxTreeItemId& item, const wxTreeItemId& new_parent)
 
1105
{
 
1106
    // first, some sanity checks
 
1107
    if (!tree || !item.IsOk() || !new_parent.IsOk())
 
1108
        return;
 
1109
 
 
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
 
1114
 
 
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);
 
1118
 
 
1119
    // recurse for folders
 
1120
    if (tree->ItemHasChildren(item))
 
1121
    {
 
1122
        // vfolder: recurse for files all contained files virtual path
 
1123
        wxTreeItemIdValue cookie;
 
1124
        wxTreeItemId child = tree->GetFirstChild(item, cookie);
 
1125
        while (child.IsOk())
 
1126
        {
 
1127
            CopyTreeNodeRecursively(tree, child, target);
 
1128
            child = tree->GetNextChild(item, cookie);
 
1129
        }
 
1130
    }
 
1131
 
 
1132
    if (!tree->IsExpanded(new_parent))
 
1133
        tree->Expand(new_parent);
 
1134
 
 
1135
    if (ftd_moved->GetProjectFile())
 
1136
        ftd_moved->GetProjectFile()->virtual_path = GetRelativeFolderPath(tree, new_parent);
 
1137
}
 
1138
 
 
1139
const wxArrayString& cbProject::GetVirtualFolders() const
 
1140
{
 
1141
    return m_VirtualFolders;
 
1142
}
 
1143
 
 
1144
void cbProject::SetVirtualFolders(const wxArrayString& folders)
 
1145
{
 
1146
    m_VirtualFolders = folders;
 
1147
    for (size_t i = 0; i < m_VirtualFolders.GetCount(); ++i)
 
1148
    {
 
1149
        m_VirtualFolders[i].Replace(_T("/"), wxString(wxFILE_SEP_PATH));
 
1150
        m_VirtualFolders[i].Replace(_T("\\"), wxString(wxFILE_SEP_PATH));
 
1151
    }
 
1152
}
 
1153
 
 
1154
bool cbProject::CanDragNode(wxTreeCtrl* tree, wxTreeItemId node)
 
1155
{
 
1156
    // what item do we start dragging?
 
1157
    if (!node.IsOk())
 
1158
        return false;
 
1159
 
 
1160
    // if no data associated with it, disallow
 
1161
    FileTreeData* ftd = (FileTreeData*)tree->GetItemData(node);
 
1162
    if (!ftd)
 
1163
        return false;
 
1164
 
 
1165
    // if not ours, disallow
 
1166
    if (ftd->GetProject() != this)
 
1167
        return false;
 
1168
 
 
1169
    // allow only if it is a file or a virtual folder
 
1170
    return ftd->GetKind() == FileTreeData::ftdkFile || ftd->GetKind() == FileTreeData::ftdkVirtualFolder;
 
1171
}
 
1172
 
 
1173
bool cbProject::NodeDragged(wxTreeCtrl* tree, wxTreeItemId from, wxTreeItemId to)
 
1174
{
 
1175
    // what items did we drag?
 
1176
    if (!from.IsOk() || !to.IsOk())
 
1177
        return false;
 
1178
 
 
1179
    // if no data associated with it, disallow
 
1180
    FileTreeData* ftd1 = (FileTreeData*)tree->GetItemData(from);
 
1181
    FileTreeData* ftd2 = (FileTreeData*)tree->GetItemData(to);
 
1182
    if (!ftd1 || !ftd2)
 
1183
        return false;
 
1184
 
 
1185
    // if not ours, disallow
 
1186
    if (ftd1->GetProject() != this || ftd2->GetProject() != this)
 
1187
        return false;
 
1188
 
 
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))
 
1195
    {
 
1196
        return false;
 
1197
    }
 
1198
 
 
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)
 
1203
        return false;
 
1204
 
 
1205
    // A special check for virtual folders.
 
1206
    if (ftd1->GetKind() == FileTreeData::ftdkVirtualFolder && ftd2->GetKind() == FileTreeData::ftdkVirtualFolder)
 
1207
    {
 
1208
        wxTreeItemId root = tree->GetRootItem();
 
1209
        wxTreeItemId toParent = tree->GetItemParent(to);
 
1210
        while (toParent != root)
 
1211
        {
 
1212
            if (toParent == from)
 
1213
                return false;
 
1214
            toParent = tree->GetItemParent(toParent);
 
1215
        }
 
1216
    }
 
1217
 
 
1218
    // finally; make the move
 
1219
    CopyTreeNodeRecursively(tree, from, parent2);
 
1220
    // remove old node
 
1221
    tree->Delete(from);
 
1222
 
 
1223
    SetModified(true);
 
1224
 
 
1225
    return true;
 
1226
}
 
1227
 
 
1228
bool cbProject::VirtualFolderAdded(wxTreeCtrl* tree, wxTreeItemId parent_node, const wxString& virtual_folder)
 
1229
{
 
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;
 
1236
 
 
1237
    for (size_t i = 0; i < m_VirtualFolders.GetCount(); ++i)
 
1238
    {
 
1239
        if (m_VirtualFolders[i].StartsWith(foldername))
 
1240
        {
 
1241
            cbMessageBox(_("A virtual folder with the same name already exists."),
 
1242
                        _("Error"), wxICON_WARNING);
 
1243
            return false;
 
1244
        }
 
1245
    }
 
1246
    m_VirtualFolders.Add(foldername);
 
1247
 
 
1248
    FileTreeData* ftd = new FileTreeData(this, FileTreeData::ftdkVirtualFolder);
 
1249
    ftd->SetProjectFile(0);
 
1250
    ftd->SetFolder(foldername);
 
1251
 
 
1252
    int vfldIdx = Manager::Get()->GetProjectManager()->VirtualFolderIconIndex();
 
1253
 
 
1254
    AddTreeNode(tree, foldername, m_ProjectNode, true, FileTreeData::ftdkVirtualFolder, true, vfldIdx, ftd);
 
1255
    if (!tree->IsExpanded(parent_node))
 
1256
        tree->Expand(parent_node);
 
1257
 
 
1258
    SetModified(true);
 
1259
 
 
1260
//    Manager::Get()->GetLogManager()->DebugLog(F(_T("VirtualFolderAdded: %s: %s"), foldername.c_str(), GetStringFromArray(m_VirtualFolders, _T(";")).c_str()));
 
1261
    return true;
 
1262
}
 
1263
 
 
1264
void cbProject::VirtualFolderDeleted(wxTreeCtrl* tree, wxTreeItemId node)
 
1265
{
 
1266
    // what item do we start dragging?
 
1267
    if (!node.IsOk())
 
1268
        return;
 
1269
 
 
1270
    // if no data associated with it, disallow
 
1271
    FileTreeData* ftd = (FileTreeData*)tree->GetItemData(node);
 
1272
    if (!ftd)
 
1273
        return;
 
1274
 
 
1275
    // if not ours, disallow
 
1276
    if (ftd->GetProject() != this)
 
1277
        return;
 
1278
 
 
1279
    wxString foldername = GetRelativeFolderPath(tree, node);
 
1280
    wxString parent_foldername = GetRelativeFolderPath(tree, tree->GetItemParent(node));
 
1281
 
 
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())
 
1284
    {
 
1285
        ProjectFile* f = node->GetData();
 
1286
        if (f && !f->virtual_path.IsEmpty())
 
1287
        {
 
1288
            if (f->virtual_path.StartsWith(foldername)) // need 2 checks because of last separator
 
1289
                f->virtual_path.Clear();
 
1290
        }
 
1291
    }
 
1292
 
 
1293
    for (int i = (int)m_VirtualFolders.GetCount() - 1; i >= 0; --i)
 
1294
    {
 
1295
        if (m_VirtualFolders[i].StartsWith(foldername))
 
1296
            m_VirtualFolders.RemoveAt(i);
 
1297
    }
 
1298
    if (!parent_foldername.IsEmpty() && m_VirtualFolders.Index(parent_foldername) == wxNOT_FOUND)
 
1299
        m_VirtualFolders.Add(parent_foldername);
 
1300
 
 
1301
    SetModified(true);
 
1302
//    Manager::Get()->GetLogManager()->DebugLog(F(_T("VirtualFolderDeleted: %s: %s"), foldername.c_str(), GetStringFromArray(m_VirtualFolders, _T(";")).c_str()));
 
1303
}
 
1304
 
 
1305
bool cbProject::VirtualFolderRenamed(wxTreeCtrl* tree, wxTreeItemId node, const wxString& new_name)
 
1306
{
 
1307
    if (new_name.IsEmpty())
 
1308
        return false;
 
1309
 
 
1310
    if (new_name.First(_T(';')) != wxNOT_FOUND ||
 
1311
        new_name.First(_T('/')) != wxNOT_FOUND ||
 
1312
        new_name.First(_T('\\')) != wxNOT_FOUND)
 
1313
    {
 
1314
        cbMessageBox(_("A virtual folder name cannot contain these special characters: \";\", \"\\\" or \"/\"."),
 
1315
                    _("Error"), wxICON_WARNING);
 
1316
        return false;
 
1317
    }
 
1318
 
 
1319
    // what item are we renaming?
 
1320
    if (!node.IsOk())
 
1321
        return false;
 
1322
 
 
1323
    // is it a different name?
 
1324
    if (tree->GetItemText(node) == new_name)
 
1325
        return false;
 
1326
 
 
1327
    // if no data associated with it, disallow
 
1328
    FileTreeData* ftd = (FileTreeData*)tree->GetItemData(node);
 
1329
    if (!ftd)
 
1330
        return false;
 
1331
 
 
1332
    // if not ours, disallow
 
1333
    if (ftd->GetProject() != this)
 
1334
        return false;
 
1335
 
 
1336
    wxString old_foldername = GetRelativeFolderPath(tree, node);
 
1337
    wxString new_foldername = GetRelativeFolderPath(tree, tree->GetItemParent(node)) + new_name + wxFILE_SEP_PATH;
 
1338
 
 
1339
    for (size_t i = 0; i < m_VirtualFolders.GetCount(); ++i)
 
1340
    {
 
1341
        if (m_VirtualFolders[i].StartsWith(new_foldername))
 
1342
        {
 
1343
            cbMessageBox(_("A virtual folder with the same name already exists."),
 
1344
                        _("Error"), wxICON_WARNING);
 
1345
            return false;
 
1346
        }
 
1347
    }
 
1348
    int idx = m_VirtualFolders.Index(old_foldername);
 
1349
    if (idx != wxNOT_FOUND)
 
1350
        m_VirtualFolders[idx] = new_foldername;
 
1351
    else
 
1352
        m_VirtualFolders.Add(new_foldername);
 
1353
 
 
1354
    // now loop all project files and rename this virtual folder
 
1355
    for (FilesList::Node* node = m_Files.GetFirst(); node; node = node->GetNext())
 
1356
    {
 
1357
        ProjectFile* f = node->GetData();
 
1358
        if (f && !f->virtual_path.IsEmpty())
 
1359
        {
 
1360
            if (f->virtual_path.StartsWith(old_foldername))
 
1361
                f->virtual_path.Replace(old_foldername, new_foldername);
 
1362
        }
 
1363
    }
 
1364
 
 
1365
    SetModified(true);
 
1366
 
 
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()));
 
1368
    return true;
 
1369
}
 
1370
 
 
1371
void cbProject::RenameInTree(const wxString &newname)
 
1372
{
 
1373
    wxTreeCtrl* tree = Manager::Get()->GetProjectManager()->GetTree();
 
1374
    if(!tree || !m_ProjectNode)
 
1375
        return;
 
1376
    tree->SetItemText(m_ProjectNode, newname);
 
1377
}
 
1378
 
 
1379
void cbProject::SaveTreeState(wxTreeCtrl* tree)
 
1380
{
 
1381
    ::SaveTreeState(tree, m_ProjectNode, m_ExpandedNodes);
 
1382
}
 
1383
 
 
1384
void cbProject::RestoreTreeState(wxTreeCtrl* tree)
 
1385
{
 
1386
    ::RestoreTreeState(tree, m_ProjectNode, m_ExpandedNodes);
 
1387
}
 
1388
 
 
1389
const wxString& cbProject::GetMakefile()
 
1390
{
 
1391
    if (!m_Makefile.IsEmpty())
 
1392
        return m_Makefile;
 
1393
 
 
1394
    wxFileName makefile(m_Makefile);
 
1395
    makefile.Assign(m_Filename);
 
1396
    makefile.SetName(_T("Makefile"));
 
1397
    makefile.SetExt(_T(""));
 
1398
    makefile.MakeRelativeTo(GetBasePath());
 
1399
 
 
1400
    m_Makefile = makefile.GetFullPath();
 
1401
 
 
1402
    return m_Makefile;
 
1403
}
 
1404
 
 
1405
ProjectFile* cbProject::GetFile(int index)
 
1406
{
 
1407
    FilesList::Node* node = m_Files.Item(index);
 
1408
    if (node)
 
1409
        return node->GetData();
 
1410
 
 
1411
    return NULL;
 
1412
}
 
1413
 
 
1414
ProjectFile* cbProject::GetFileByFilename(const wxString& filename, bool isRelative, bool isUnixFilename)
 
1415
{
 
1416
    // m_ProjectFilesMap keeps UnixFilename(ProjectFile::relativeFilename)
 
1417
    wxString tmp = filename;
 
1418
    if (!isRelative)
 
1419
    {
 
1420
        // if the search is not relative, make it
 
1421
        wxFileName fname(filename);
 
1422
        fname.MakeRelativeTo(GetBasePath());
 
1423
        tmp = fname.GetFullPath();
 
1424
    }
 
1425
    else
 
1426
    {
 
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("./")))
 
1432
        {
 
1433
            tmp.Remove(0, 2);
 
1434
        }
 
1435
    }
 
1436
 
 
1437
    if (isUnixFilename)
 
1438
        return m_ProjectFilesMap[tmp];
 
1439
    return m_ProjectFilesMap[UnixFilename(tmp)];
 
1440
}
 
1441
 
 
1442
bool cbProject::QueryCloseAllFiles()
 
1443
{
 
1444
    FilesList::Node* node;
 
1445
    node = m_Files.GetFirst();
 
1446
    while(node)
 
1447
    {
 
1448
        ProjectFile* f = node->GetData();
 
1449
        cbEditor* ed = Manager::Get()->GetEditorManager()->IsBuiltinOpen(f->file.GetFullPath());
 
1450
        if (ed && ed->GetModified())
 
1451
        {
 
1452
            if (!Manager::Get()->GetEditorManager()->QueryClose(ed))
 
1453
                return false;
 
1454
        }
 
1455
        node = node->GetNext();
 
1456
    }
 
1457
    return true;
 
1458
}
 
1459
 
 
1460
bool cbProject::CloseAllFiles(bool dontsave)
 
1461
{
 
1462
    // first try to close modified editors
 
1463
 
 
1464
    if(!dontsave)
 
1465
        if(!QueryCloseAllFiles())
 
1466
            return false;
 
1467
 
 
1468
    // now free the rest of the project files
 
1469
    Manager::Get()->GetEditorManager()->HideNotebook();
 
1470
    FilesList::Node* node = m_Files.GetFirst();
 
1471
    while(node)
 
1472
    {
 
1473
        ProjectFile* f = node->GetData();
 
1474
        Manager::Get()->GetEditorManager()->Close(f->file.GetFullPath(),true);
 
1475
        delete f;
 
1476
        delete node;
 
1477
        node = m_Files.GetFirst();
 
1478
    }
 
1479
    Manager::Get()->GetEditorManager()->ShowNotebook();
 
1480
    return true;
 
1481
}
 
1482
 
 
1483
bool cbProject::SaveAllFiles()
 
1484
{
 
1485
    int count = m_Files.GetCount();
 
1486
    FilesList::Node* node = m_Files.GetFirst();
 
1487
    while(node)
 
1488
    {
 
1489
        ProjectFile* f = node->GetData();
 
1490
        if (Manager::Get()->GetEditorManager()->Save(f->file.GetFullPath()))
 
1491
            --count;
 
1492
        node = node->GetNext();
 
1493
    }
 
1494
    return count == 0;
 
1495
}
 
1496
 
 
1497
bool cbProject::ShowOptions()
 
1498
{
 
1499
    ProjectOptionsDlg dlg(Manager::Get()->GetAppWindow(), this);
 
1500
    PlaceWindow(&dlg);
 
1501
    if (dlg.ShowModal() == wxID_OK)
 
1502
    {
 
1503
        // update file details
 
1504
        FilesList::Node* node = m_Files.GetFirst();
 
1505
        while(node)
 
1506
        {
 
1507
            ProjectFile* f = node->GetData();
 
1508
            f->UpdateFileDetails();
 
1509
            node = node->GetNext();
 
1510
        }
 
1511
        return true;
 
1512
    }
 
1513
    return false;
 
1514
}
 
1515
 
 
1516
int cbProject::SelectTarget(int initial, bool evenIfOne)
 
1517
{
 
1518
    if (!evenIfOne && GetBuildTargetsCount() == 1)
 
1519
        return 0;
 
1520
 
 
1521
    SelectTargetDlg dlg(0L, this, initial);
 
1522
    PlaceWindow(&dlg);
 
1523
    if (dlg.ShowModal() == wxID_OK)
 
1524
        return dlg.GetSelection();
 
1525
    return -1;
 
1526
}
 
1527
 
 
1528
// Build targets
 
1529
 
 
1530
ProjectBuildTarget* cbProject::AddDefaultBuildTarget()
 
1531
{
 
1532
    return AddBuildTarget(_T("default"));
 
1533
}
 
1534
 
 
1535
ProjectBuildTarget* cbProject::AddBuildTarget(const wxString& targetName)
 
1536
{
 
1537
    if (GetBuildTarget(targetName)) // Don't add the target if it exists
 
1538
        return 0L;
 
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);
 
1548
 
 
1549
    // remove any virtual targets with the same name
 
1550
    if (HasVirtualBuildTarget(targetName))
 
1551
    {
 
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()));
 
1554
    }
 
1555
 
 
1556
    SetModified(true);
 
1557
 
 
1558
    NotifyPlugins(cbEVT_BUILDTARGET_ADDED, targetName);
 
1559
    NotifyPlugins(cbEVT_PROJECT_TARGETS_MODIFIED);
 
1560
    return target;
 
1561
}
 
1562
 
 
1563
bool cbProject::RenameBuildTarget(int index, const wxString& targetName)
 
1564
{
 
1565
    ProjectBuildTarget* target = GetBuildTarget(index);
 
1566
    if (target)
 
1567
    {
 
1568
        wxString oldTargetName = target->GetTitle();
 
1569
 
 
1570
        // rename target if referenced in any virtual target too
 
1571
        for (VirtualBuildTargetsMap::iterator it = m_VirtualTargets.begin(); it != m_VirtualTargets.end(); ++it)
 
1572
        {
 
1573
            wxArrayString& tgts = it->second;
 
1574
            int index = tgts.Index(target->GetTitle());
 
1575
            if (index != -1)
 
1576
            {
 
1577
                tgts[index] = targetName;
 
1578
            }
 
1579
        }
 
1580
 
 
1581
        // rename target for all files that reference it
 
1582
        int count = GetFilesCount();
 
1583
        for (int i = 0; i < count; ++i)
 
1584
        {
 
1585
            ProjectFile* pf = GetFile(i);
 
1586
            pf->RenameBuildTarget(target->GetTitle(), targetName);
 
1587
        }
 
1588
 
 
1589
        // finally rename the target
 
1590
        target->SetTitle(targetName);
 
1591
        SetModified(true);
 
1592
        NotifyPlugins(cbEVT_BUILDTARGET_RENAMED, targetName, oldTargetName);
 
1593
        NotifyPlugins(cbEVT_PROJECT_TARGETS_MODIFIED);
 
1594
        return true;
 
1595
    }
 
1596
    return false;
 
1597
}
 
1598
 
 
1599
bool cbProject::RenameBuildTarget(const wxString& oldTargetName, const wxString& newTargetName)
 
1600
{
 
1601
    return RenameBuildTarget(IndexOfBuildTargetName(oldTargetName), newTargetName);
 
1602
}
 
1603
 
 
1604
ProjectBuildTarget* cbProject::DuplicateBuildTarget(int index, const wxString& newName)
 
1605
{
 
1606
    ProjectBuildTarget* newTarget = 0;
 
1607
    ProjectBuildTarget* target = GetBuildTarget(index);
 
1608
    if (target)
 
1609
    {
 
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())
 
1615
        {
 
1616
            ProjectFile* pf = it->GetData();
 
1617
            pf->AddBuildTarget(newTargetName);
 
1618
        }
 
1619
        SetModified(true);
 
1620
        m_Targets.Add(newTarget);
 
1621
        NotifyPlugins(cbEVT_BUILDTARGET_ADDED, newName);
 
1622
        NotifyPlugins(cbEVT_PROJECT_TARGETS_MODIFIED);
 
1623
    }
 
1624
    return newTarget;
 
1625
}
 
1626
 
 
1627
ProjectBuildTarget* cbProject::DuplicateBuildTarget(const wxString& targetName, const wxString& newName)
 
1628
{
 
1629
    return DuplicateBuildTarget(IndexOfBuildTargetName(targetName), newName);
 
1630
}
 
1631
 
 
1632
bool cbProject::ExportTargetAsProject(int index)
 
1633
{
 
1634
    ProjectBuildTarget* target = GetBuildTarget(index);
 
1635
    if (!target)
 
1636
        return false;
 
1637
    return ExportTargetAsProject(target->GetTitle());
 
1638
}
 
1639
 
 
1640
bool cbProject::ExportTargetAsProject(const wxString& targetName)
 
1641
{
 
1642
    ProjectBuildTarget* target = GetBuildTarget(targetName);
 
1643
    if (!target)
 
1644
        return false;
 
1645
 
 
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())
 
1651
        return false;
 
1652
    wxFileName fname(GetFilename());
 
1653
    fname.SetName(newName);
 
1654
 
 
1655
    Save();
 
1656
    bool alreadyModified = GetModified();
 
1657
    wxString oldTitle = GetTitle();
 
1658
    SetTitle(targetName);
 
1659
 
 
1660
    ProjectLoader loader(this);
 
1661
    bool ret = loader.ExportTargetAsProject(fname.GetFullPath(), target->GetTitle(), m_pExtensionsElement);
 
1662
 
 
1663
    SetTitle(oldTitle);
 
1664
    if (!alreadyModified)
 
1665
        SetModified(false);
 
1666
 
 
1667
    return ret;
 
1668
}
 
1669
 
 
1670
bool cbProject::RemoveBuildTarget(int index)
 
1671
{
 
1672
    ProjectBuildTarget* target = GetBuildTarget(index);
 
1673
    if (target)
 
1674
    {
 
1675
        wxString oldTargetName = target->GetTitle();
 
1676
 
 
1677
        // remove target from any virtual targets it belongs to
 
1678
        for (VirtualBuildTargetsMap::iterator it = m_VirtualTargets.begin(); it != m_VirtualTargets.end(); ++it)
 
1679
        {
 
1680
            wxArrayString& tgts = it->second;
 
1681
            int index = tgts.Index(target->GetTitle());
 
1682
            if (index != -1)
 
1683
            {
 
1684
                tgts.RemoveAt(index);
 
1685
            }
 
1686
        }
 
1687
 
 
1688
        // remove target from any project files that reference it
 
1689
        int count = GetFilesCount();
 
1690
        for (int i = 0; i < count; ++i)
 
1691
        {
 
1692
            ProjectFile* pf = GetFile(i);
 
1693
            pf->RemoveBuildTarget(target->GetTitle());
 
1694
        }
 
1695
 
 
1696
        // finally remove the target
 
1697
        delete target;
 
1698
        m_Targets.RemoveAt(index);
 
1699
        SetModified(true);
 
1700
        NotifyPlugins(cbEVT_BUILDTARGET_REMOVED, oldTargetName);
 
1701
        NotifyPlugins(cbEVT_PROJECT_TARGETS_MODIFIED);
 
1702
        return true;
 
1703
    }
 
1704
    return false;
 
1705
}
 
1706
 
 
1707
bool cbProject::RemoveBuildTarget(const wxString& targetName)
 
1708
{
 
1709
    return RemoveBuildTarget(IndexOfBuildTargetName(targetName));
 
1710
}
 
1711
 
 
1712
int cbProject::IndexOfBuildTargetName(const wxString& targetName) const
 
1713
{
 
1714
    for (unsigned int i = 0; i < m_Targets.GetCount(); ++i)
 
1715
    {
 
1716
        ProjectBuildTarget* target = m_Targets[i];
 
1717
        if (target->GetTitle().Matches(targetName))
 
1718
            return i;
 
1719
    }
 
1720
    return -1;
 
1721
}
 
1722
 
 
1723
bool cbProject::BuildTargetValid(const wxString& name, bool virtuals_too) const
 
1724
{
 
1725
    if (virtuals_too && HasVirtualBuildTarget(name))
 
1726
        return true;
 
1727
    else if (IndexOfBuildTargetName(name) != -1)
 
1728
        return true;
 
1729
    return false;
 
1730
}
 
1731
 
 
1732
wxString cbProject::GetFirstValidBuildTargetName(bool virtuals_too) const
 
1733
{
 
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();
 
1738
 
 
1739
    return wxEmptyString;
 
1740
}
 
1741
 
 
1742
bool cbProject::SetActiveBuildTarget(const wxString& name)
 
1743
{
 
1744
    if (name == m_ActiveTarget)
 
1745
        return true;
 
1746
    wxString oldActiveTarget = m_ActiveTarget;
 
1747
    m_ActiveTarget = name;
 
1748
 
 
1749
    bool valid = BuildTargetValid(name);
 
1750
 
 
1751
    if (!valid)
 
1752
    {
 
1753
        // no target (virtual or real) by that name
 
1754
        m_ActiveTarget = GetFirstValidBuildTargetName();
 
1755
    }
 
1756
 
 
1757
    NotifyPlugins(cbEVT_BUILDTARGET_SELECTED, m_ActiveTarget, oldActiveTarget);
 
1758
 
 
1759
    return valid;
 
1760
}
 
1761
 
 
1762
const wxString& cbProject::GetActiveBuildTarget() const
 
1763
{
 
1764
    return m_ActiveTarget;
 
1765
}
 
1766
 
 
1767
void cbProject::SetDefaultExecuteTarget(const wxString& name)
 
1768
{
 
1769
    if (name == m_DefaultExecuteTarget)
 
1770
        return;
 
1771
 
 
1772
    m_DefaultExecuteTarget = name;
 
1773
    SetModified(true);
 
1774
}
 
1775
 
 
1776
const wxString& cbProject::GetDefaultExecuteTarget() const
 
1777
{
 
1778
    return m_DefaultExecuteTarget;
 
1779
}
 
1780
 
 
1781
ProjectBuildTarget* cbProject::GetBuildTarget(int index)
 
1782
{
 
1783
    if (index >= 0 && index < (int)m_Targets.GetCount())
 
1784
        return m_Targets[index];
 
1785
    return 0L;
 
1786
}
 
1787
 
 
1788
ProjectBuildTarget* cbProject::GetBuildTarget(const wxString& targetName)
 
1789
{
 
1790
    int idx = IndexOfBuildTargetName(targetName);
 
1791
    return GetBuildTarget(idx);
 
1792
}
 
1793
 
 
1794
void cbProject::ReOrderTargets(const wxArrayString& nameOrder)
 
1795
{
 
1796
    LogManager* msgMan = Manager::Get()->GetLogManager();
 
1797
    if (nameOrder.GetCount() != m_Targets.GetCount())
 
1798
    {
 
1799
        msgMan->DebugLog(F(_T("cbProject::ReOrderTargets() : Count does not match (%d sent, %d had)..."), nameOrder.GetCount(), m_Targets.GetCount()));
 
1800
        return;
 
1801
    }
 
1802
 
 
1803
    for (unsigned int i = 0; i < nameOrder.GetCount(); ++i)
 
1804
    {
 
1805
        ProjectBuildTarget* target = GetBuildTarget(nameOrder[i]);
 
1806
        if (!target)
 
1807
        {
 
1808
            msgMan->DebugLog(F(_T("cbProject::ReOrderTargets() : Target \"%s\" not found..."), nameOrder[i].c_str()));
 
1809
            break;
 
1810
        }
 
1811
 
 
1812
        m_Targets.Remove(target);
 
1813
        m_Targets.Insert(target, i);
 
1814
 
 
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)
 
1819
        {
 
1820
            wxArrayString& vt = it->second;
 
1821
            if (vt.Index(nameOrder[i]) != wxNOT_FOUND)
 
1822
            {
 
1823
                vt.Remove(nameOrder[i]);
 
1824
                vt.Insert(nameOrder[i], i);
 
1825
            }
 
1826
        }
 
1827
    }
 
1828
    SetModified(true);
 
1829
}
 
1830
 
 
1831
void cbProject::SetCurrentlyCompilingTarget(ProjectBuildTarget* bt)
 
1832
{
 
1833
    m_CurrentlyCompilingTarget = bt;
 
1834
}
 
1835
 
 
1836
bool cbProject::DefineVirtualBuildTarget(const wxString& alias, const wxArrayString& targets)
 
1837
{
 
1838
    if (targets.GetCount() == 0)
 
1839
    {
 
1840
        Manager::Get()->GetLogManager()->LogWarning(F(_T("Can't define virtual build target '%s': Group of build targets is empty!"), alias.c_str()));
 
1841
        return false;
 
1842
    }
 
1843
 
 
1844
    ProjectBuildTarget* existing = GetBuildTarget(alias);
 
1845
    if (existing)
 
1846
    {
 
1847
        Manager::Get()->GetLogManager()->LogWarning(F(_T("Can't define virtual build target '%s': Real build target exists with that name!"), alias.c_str()));
 
1848
        return false;
 
1849
    }
 
1850
 
 
1851
    m_VirtualTargets[alias] = targets;
 
1852
    SetModified(true);
 
1853
    NotifyPlugins(cbEVT_PROJECT_TARGETS_MODIFIED);
 
1854
    return true;
 
1855
}
 
1856
 
 
1857
bool cbProject::HasVirtualBuildTarget(const wxString& alias) const
 
1858
{
 
1859
    return m_VirtualTargets.find(alias) != m_VirtualTargets.end();
 
1860
}
 
1861
 
 
1862
bool cbProject::RemoveVirtualBuildTarget(const wxString& alias)
 
1863
{
 
1864
    VirtualBuildTargetsMap::iterator it = m_VirtualTargets.find(alias);
 
1865
    if (it == m_VirtualTargets.end())
 
1866
        return false;
 
1867
 
 
1868
    m_VirtualTargets.erase(it);
 
1869
    SetModified(true);
 
1870
    NotifyPlugins(cbEVT_PROJECT_TARGETS_MODIFIED);
 
1871
    return true;
 
1872
}
 
1873
 
 
1874
wxArrayString cbProject::GetVirtualBuildTargets() const
 
1875
{
 
1876
    wxArrayString result;
 
1877
    for (VirtualBuildTargetsMap::const_iterator it = m_VirtualTargets.begin(); it != m_VirtualTargets.end(); ++it)
 
1878
        result.Add(it->first);
 
1879
    return result;
 
1880
}
 
1881
 
 
1882
const wxArrayString& cbProject::GetVirtualBuildTargetGroup(const wxString& alias) const
 
1883
{
 
1884
    static wxArrayString resultIfError;
 
1885
 
 
1886
    VirtualBuildTargetsMap::const_iterator it = m_VirtualTargets.find(alias);
 
1887
    if (it == m_VirtualTargets.end())
 
1888
        return resultIfError;
 
1889
    return it->second;
 
1890
}
 
1891
 
 
1892
wxArrayString cbProject::GetExpandedVirtualBuildTargetGroup(const wxString& alias) const
 
1893
{
 
1894
    wxArrayString result;
 
1895
 
 
1896
    VirtualBuildTargetsMap::const_iterator it = m_VirtualTargets.find(alias);
 
1897
    if (it == m_VirtualTargets.end())
 
1898
        return result;
 
1899
 
 
1900
    ExpandVirtualBuildTargetGroup(alias, result);
 
1901
    return result;
 
1902
}
 
1903
 
 
1904
bool cbProject::CanAddToVirtualBuildTarget(const wxString& alias, const wxString& target)
 
1905
{
 
1906
    // virtual not there?
 
1907
    if (!HasVirtualBuildTarget(alias))
 
1908
        return false;
 
1909
 
 
1910
    // real targets can be added safely (as long as they 're unique)
 
1911
    if (!HasVirtualBuildTarget(target))
 
1912
        return true;
 
1913
 
 
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)
 
1919
        return false;
 
1920
 
 
1921
    for (size_t i = 0; i < group.GetCount(); ++i)
 
1922
    {
 
1923
        // only virtuals
 
1924
        if (HasVirtualBuildTarget(group[i]))
 
1925
        {
 
1926
            if (!CanAddToVirtualBuildTarget(group[i], alias))
 
1927
                return false;
 
1928
        }
 
1929
    }
 
1930
    return true;
 
1931
}
 
1932
 
 
1933
void cbProject::ExpandVirtualBuildTargetGroup(const wxString& alias, wxArrayString& result) const
 
1934
{
 
1935
    const wxArrayString& group = GetVirtualBuildTargetGroup(alias);
 
1936
    for (size_t i = 0; i < group.GetCount(); ++i)
 
1937
    {
 
1938
        // real targets get added
 
1939
        if (IndexOfBuildTargetName(group[i]) != -1)
 
1940
        {
 
1941
            if (result.Index(group[i]) == wxNOT_FOUND)
 
1942
                result.Add(group[i]);
 
1943
        }
 
1944
        // virtual targets recurse
 
1945
        else
 
1946
            ExpandVirtualBuildTargetGroup(group[i], result);
 
1947
    }
 
1948
}
 
1949
 
 
1950
#ifdef USE_OPENFILES_TREE
 
1951
bool MiscTreeItemData::OwnerCheck(wxTreeEvent& event,wxTreeCtrl *tree,wxEvtHandler *handler,bool strict)
 
1952
{
 
1953
    if(!tree)   // No tree to get data from - ignore event
 
1954
        return false;
 
1955
 
 
1956
    MiscTreeItemData* data =
 
1957
        (MiscTreeItemData*)tree->GetItemData(event.GetItem());
 
1958
    if(!data)
 
1959
    {
 
1960
        if(!strict)
 
1961
            return true; // On doubt, allow event
 
1962
        else
 
1963
        {
 
1964
            event.Skip();
 
1965
            return false;
 
1966
        }
 
1967
    }
 
1968
    wxEvtHandler *h = data->GetOwner();
 
1969
    if((h && h!=handler) || (strict && !h))
 
1970
    {   // Tree Item belongs to another handler - skip
 
1971
        event.Skip();
 
1972
        return false;
 
1973
    }
 
1974
    return true;
 
1975
}
 
1976
#endif
 
1977
 
 
1978
void cbProject::SetExtendedObjectNamesGeneration(bool ext)
 
1979
{
 
1980
    bool changed = m_ExtendedObjectNamesGeneration != ext;
 
1981
 
 
1982
    // update it now because SetObjName() below will call GetExtendedObjectNamesGeneration()
 
1983
    // so it must be up-to-date
 
1984
    m_ExtendedObjectNamesGeneration = ext;
 
1985
 
 
1986
    if (changed)
 
1987
    {
 
1988
        for (FilesList::Node* node = m_Files.GetFirst(); node; node = node->GetNext())
 
1989
        {
 
1990
            ProjectFile* f = node->GetData();
 
1991
            f->SetObjName(f->relativeToCommonTopLevelPath);
 
1992
            f->UpdateFileDetails();
 
1993
        }
 
1994
 
 
1995
        SetModified(true);
 
1996
    }
 
1997
}
 
1998
 
 
1999
bool cbProject::GetExtendedObjectNamesGeneration() const
 
2000
{
 
2001
    return m_ExtendedObjectNamesGeneration;
 
2002
}
 
2003
 
 
2004
void cbProject::SetNotes(const wxString& notes)
 
2005
{
 
2006
    if (m_Notes != notes)
 
2007
    {
 
2008
        m_Notes = notes;
 
2009
        SetModified(true);
 
2010
    }
 
2011
}
 
2012
 
 
2013
const wxString& cbProject::GetNotes() const
 
2014
{
 
2015
    return m_Notes;
 
2016
}
 
2017
 
 
2018
void cbProject::SetShowNotesOnLoad(bool show)
 
2019
{
 
2020
    if (m_AutoShowNotesOnLoad != show)
 
2021
    {
 
2022
        m_AutoShowNotesOnLoad = show;
 
2023
        SetModified(true);
 
2024
    }
 
2025
}
 
2026
 
 
2027
bool cbProject::GetShowNotesOnLoad() const
 
2028
{
 
2029
    return m_AutoShowNotesOnLoad;
 
2030
}
 
2031
 
 
2032
void cbProject::ShowNotes(bool nonEmptyOnly, bool editable)
 
2033
{
 
2034
    if (!editable && nonEmptyOnly && m_Notes.IsEmpty())
 
2035
        return;
 
2036
 
 
2037
    GenericMultiLineNotesDlg dlg(Manager::Get()->GetAppWindow(),
 
2038
                                _("Notes about ") + m_Title,
 
2039
                                m_Notes,
 
2040
                                !editable);
 
2041
    PlaceWindow(&dlg);
 
2042
    if (dlg.ShowModal() == wxID_OK)
 
2043
    {
 
2044
        if (editable)
 
2045
            SetNotes(dlg.GetNotes());
 
2046
    }
 
2047
}
 
2048
 
 
2049
void cbProject::SetTitle(const wxString& title)
 
2050
{
 
2051
    if ( title != GetTitle() )
 
2052
    {
 
2053
        CompileTargetBase::SetTitle(title);
 
2054
        NotifyPlugins(cbEVT_PROJECT_RENAMED);
 
2055
    }
 
2056
}
 
2057
 
 
2058
TiXmlNode* cbProject::GetExtensionsNode()
 
2059
{
 
2060
    if (!m_pExtensionsElement)
 
2061
        m_pExtensionsElement = new TiXmlElement(cbU2C(_T("Extensions")));
 
2062
    return m_pExtensionsElement;
 
2063
}
 
2064
 
 
2065
void cbProject::AddToExtensions(const wxString& stringDesc)
 
2066
{
 
2067
    // sample stringDesc:
 
2068
    // node/+subnode/subsubnode:attr=val
 
2069
 
 
2070
    TiXmlElement* elem = GetExtensionsNode()->ToElement();
 
2071
    size_t pos = 0;
 
2072
    while (true)
 
2073
    {
 
2074
        // ignore consecutive slashes
 
2075
        while (pos < stringDesc.Length() && stringDesc.GetChar(pos) == _T('/'))
 
2076
        {
 
2077
            ++pos;
 
2078
        }
 
2079
 
 
2080
        // find next slash or colon
 
2081
        size_t nextPos = pos;
 
2082
        while (nextPos < stringDesc.Length() && stringDesc.GetChar(++nextPos) != _T('/') && stringDesc.GetChar(nextPos) != _T(':'))
 
2083
            ;
 
2084
 
 
2085
        wxString current = stringDesc.Mid(pos, nextPos - pos);
 
2086
        if (current.IsEmpty() || current[0] == _T(':')) // abort on invalid case: "node/:attr=val" (consecutive "/:")
 
2087
            break;
 
2088
 
 
2089
        // find or create the subnode
 
2090
        bool forceAdd = current[0] == _T('+');
 
2091
        if (forceAdd)
 
2092
            current.Remove(0, 1); // remove '+'
 
2093
        TiXmlElement* sub = !forceAdd ? elem->FirstChildElement(cbU2C(current)) : 0;
 
2094
        if (!sub)
 
2095
        {
 
2096
            sub = elem->InsertEndChild(TiXmlElement(cbU2C(current)))->ToElement();
 
2097
            SetModified(true);
 
2098
        }
 
2099
        elem = sub;
 
2100
 
 
2101
        // last node?
 
2102
        if (stringDesc.GetChar(nextPos) == _T(':'))
 
2103
        {
 
2104
            // yes, just parse the attribute now
 
2105
            pos = nextPos + 1; // skip the colon
 
2106
            nextPos = pos;
 
2107
            while (nextPos < stringDesc.Length() && stringDesc.GetChar(++nextPos) != _T('='))
 
2108
                ;
 
2109
            if (pos == nextPos || nextPos == stringDesc.Length())
 
2110
            {
 
2111
                // invalid attribute
 
2112
            }
 
2113
            else
 
2114
            {
 
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));
 
2118
                SetModified(true);
 
2119
            }
 
2120
 
 
2121
            // all done
 
2122
            break;
 
2123
        }
 
2124
 
 
2125
        pos = nextPos; // prepare for next loop
 
2126
    }
 
2127
}
 
2128
 
 
2129
void cbProject::ProjectFileRenamed(ProjectFile* pf)
 
2130
{
 
2131
        for (ProjectFiles::iterator it = m_ProjectFilesMap.begin(); it != m_ProjectFilesMap.end(); ++it)
 
2132
        {
 
2133
                ProjectFile* itpf = it->second;
 
2134
                if (itpf == pf)
 
2135
                {
 
2136
                        // got it
 
2137
                        m_ProjectFilesMap.erase(it);
 
2138
                        m_ProjectFilesMap[UnixFilename(pf->file.GetFullPath())] = pf;
 
2139
                        break;
 
2140
                }
 
2141
        }
 
2142
}