/*
* This file is part of the Code::Blocks IDE and licensed under the GNU General Public License, version 3
* [Link]
* $Revision$
* $Id$
* $HeadURL$
*/
#include "sdk.h"
#include "projectmanagerui.h"
#ifndef CB_PRECOMP
#include <algorithm>
#include <wx/checkbox.h>
#include <wx/choicdlg.h>
#include <wx/dir.h>
#include <wx/filedlg.h>
#include <wx/imaglist.h>
#include <wx/listctrl.h>
#include <wx/menu.h>
#include <wx/settings.h>
#include <wx/textdlg.h>
#include <wx/xrc/xmlres.h>
#include "cbeditor.h"
#include "cbproject.h"
#include "cbworkspace.h"
#include "configmanager.h"
#include "editormanager.h"
#include "logmanager.h"
#endif
#include <unordered_map>
#include <vector>
#include "wxstringhash.h"
#include <wx/dataobj.h>
#include <wx/dnd.h>
#include <wx/progdlg.h>
#include "annoyingdialog.h"
#include "cbauibook.h"
#include "cbcolourmanager.h"
#include "confirmreplacedlg.h"
#include "filefilters.h"
#include "filegroupsandmasks.h"
#include "macrosmanager.h"
#include "multiselectdlg.h"
#include "projectdepsdlg.h"
#include "projectfileoptionsdlg.h"
#include "projectoptionsdlg.h"
#include "projectsfilemasksdlg.h"
#include "manageglobsdlg.h"
#include "projectloader.h"
#include "goto_file.h"
#include "startherepage.h"
namespace
// maximum number of items in "Open with" context menu
static const unsigned int MAX_OPEN_WITH_ITEMS = 20; // keep it in sync with below array!
static const int idOpenWith[] =
static_cast<int>(wxNewId()), static_cast<int>(wxNewId()), static_cast<int>(wxNewId()),
static_cast<int>(wxNewId()), static_cast<int>(wxNewId()),
static_cast<int>(wxNewId()), static_cast<int>(wxNewId()), static_cast<int>(wxNewId()),
static_cast<int>(wxNewId()), static_cast<int>(wxNewId()),
static_cast<int>(wxNewId()), static_cast<int>(wxNewId()), static_cast<int>(wxNewId()),
static_cast<int>(wxNewId()), static_cast<int>(wxNewId()),
static_cast<int>(wxNewId()), static_cast<int>(wxNewId()), static_cast<int>(wxNewId()),
static_cast<int>(wxNewId()), static_cast<int>(wxNewId()),
};
// special entry: force open with internal editor
static const int idOpenWithInternal = wxNewId();
const int ID_ProjectManager = wxNewId();
const int idMenuSetActiveProject = wxNewId();
const int idMenuOpenFile = wxNewId();
const int idMenuSaveProject = wxNewId();
const int idMenuSaveFile = wxNewId();
const int idMenuCloseProject = wxNewId();
const int idMenuCloseFile = wxNewId();
const int idMenuAddFilePopup = wxNewId();
const int idMenuAddFilesRecursivelyPopup = wxNewId();
const int idMenuAddFile = wxNewId();
const int idMenuAddFilesRecursively = wxNewId();
const int idMenuManageGlobs = wxNewId();
const int idMenuManageGlobsPopup = wxNewId();
const int idMenuRemoveFolderFilesPopup = wxNewId();
const int idMenuOpenFolderFilesPopup = wxNewId();
const int idMenuRemoveFilePopup = wxNewId();
const int idMenuRemoveFile = wxNewId();
const int idMenuRenameFile = wxNewId();
const int idMenuRenameVFolder = wxNewId();
const int idMenuProjectNotes = wxNewId();
const int idMenuProjectProperties = wxNewId();
const int idMenuFileProperties = wxNewId();
const int idMenuOpenInSystemFileBrowser = wxNewId();
const int idMenuTreeProjectProperties = wxNewId();
const int idMenuTreeFileProperties = wxNewId();
const int idMenuTreeOptionsCompile = wxNewId();
const int idMenuTreeOptionsLink = wxNewId();
const int idMenuTreeOptionsDisableBoth = wxNewId();
const int idMenuTreeOptionsEnableBoth = wxNewId();
const int idMenuGotoFile = wxNewId();
const int idMenuExecParams = wxNewId();
const int idMenuViewCategorize = wxNewId();
const int idMenuViewUseFolders = wxNewId();
const int idMenuViewHideFolderName = wxNewId();
const int idMenuViewFileMasks = wxNewId();
const int idMenuNextProject = wxNewId();
const int idMenuPriorProject = wxNewId();
const int idMenuProjectTreeProps = wxNewId();
const int idMenuProjectUp = wxNewId();
const int idMenuProjectDown = wxNewId();
const int idMenuViewCategorizePopup = wxNewId();
const int idMenuViewUseFoldersPopup = wxNewId();
const int idMenuViewHideFolderNamePopup = wxNewId();
const int idMenuViewSortAlphabetically = wxNewId();
const int idMenuTreeRenameWorkspace = wxNewId();
const int idMenuTreeSaveWorkspace = wxNewId();
const int idMenuTreeSaveAsWorkspace = wxNewId();
const int idMenuTreeCloseWorkspace = wxNewId();
const int idMenuAddVirtualFolder = wxNewId();
const int idMenuDeleteVirtualFolder = wxNewId();
const int idMenuFindFile = wxNewId();
const int idNB = wxNewId();
const int idNB_TabTop = wxNewId();
const int idNB_TabBottom = wxNewId();
} // anonymous namespace
namespace
static bool ProjectCanDragNode(cbProject* project, wxTreeCtrl* tree, wxTreeItemId node);
static bool TestProjectNodeDragged(cbProject* project, wxTreeCtrl* tree, const wxArrayTreeItemIds&
fromArray,
wxTreeItemId to);
static bool TestProjectVirtualFolderDragged(cbProject* project, wxTreeCtrl* tree, wxTreeItemId from,
wxTreeItemId to);
static bool ProjectNodeDragged(cbProject* project, wxTreeCtrl* tree, wxArrayTreeItemIds& fromArray,
wxTreeItemId to);
static bool ProjectVirtualFolderAdded(cbProject* project, wxTreeCtrl* tree,
wxTreeItemId parent_node, const wxString& virtual_folder);
static void ProjectVirtualFolderDeleted(cbProject* project, wxTreeCtrl* tree, wxTreeItemId node);
static bool ProjectVirtualFolderRenamed(cbProject* project, wxTreeCtrl* tree, wxTreeItemId node,
const wxString& new_name);
static bool ProjectVirtualFolderDragged(cbProject* project, wxTreeCtrl* tree, wxTreeItemId from,
wxTreeItemId to);
static bool ProjectShowOptions(cbProject* project);
static wxString GetRelativeFolderPath(wxTreeCtrl* tree, wxTreeItemId parent);
} // anonymous namespace
ProjectTreeDropTarget::ProjectTreeDropTarget(cbTreeCtrl* ctrl, ProjectManagerUI* ui) :
m_treeCtrl(ctrl), m_ui(ui)
wxDataObjectComposite* dataobj = new wxDataObjectComposite();
dataobj->Add(new TreeDNDObject(), true);
dataobj->Add(new wxFileDataObject());
SetDataObject(dataobj);
wxDragResult ProjectTreeDropTarget::OnData(wxCoord x, wxCoord y, wxDragResult defaultDragResult)
// We dropped on an item, clean up highlighting before we do anything,
// because old item may be invalid after we are finished with
// this function and so we can not use it in OnDragOver
if([Link]())
m_treeCtrl->SetItemDropHighlight(oldItem, false);
[Link](); // invalidate old item
int flag = 0;
wxUnusedVar(flag);
GetData();
wxDataObjectComposite *dataobjComp = static_cast<wxDataObjectComposite *>(GetDataObject());
const wxDataFormat format = dataobjComp->GetReceivedFormat();
const wxTreeItemId item = m_treeCtrl->HitTest(wxPoint(x,y), flag);
wxArrayInt emptyTargets;
// When the format is a file we add it to the project, or if no project is open, we simply open the file in
the editor
if (format == wxDF_FILENAME)
wxFileDataObject *dataobjFile = static_cast<wxFileDataObject *>(dataobjComp-
>GetObject(wxDF_FILENAME));
ProjectManager* mgr = Manager::Get()->GetProjectManager();
if (!mgr || !m_treeCtrl || !dataobjFile)
return wxDragNone;
if ([Link]())
// Drop point is valid tree item, so we add the files to a specific user selected project
FileTreeData* ftd = (FileTreeData*) m_treeCtrl->GetItemData(item);
if (ftd)
cbProject* prj = ftd->GetProject();
mgr->AddMultipleFilesToProject(dataobjFile->GetFilenames(), mgr->GetActiveProject(),
emptyTargets); // project found, add files
if (ftd->GetKind() == FileTreeData::ftdkVirtualFolder)
{
// The files are dropped on a virtual folder, so we try to move them to it
for (const wxString& filename : dataobjFile->GetFilenames())
// first find the files again with the non relative paths
ProjectFile* file = prj->GetFileByFilename(filename, false);
if (file)
file->virtual_path = GetRelativeFolderPath(m_treeCtrl, item);
mgr->GetUI().RebuildTree();
else if(mgr->GetActiveProject())
// if the files are not dropped on a project tree item, but there is an active project:
// add them to the current active project
mgr->AddMultipleFilesToProject(dataobjFile->GetFilenames(), mgr->GetActiveProject(),
emptyTargets);
mgr->GetUI().RebuildTree();
else
// if no (active) project is found, we simply open the file in the editor
for (const wxString& file : dataobjFile->GetFilenames())
Manager::Get()->GetEditorManager()->Open(file);
// Return result is not so important?
return wxDragCopy;
else if (format == TreeDNDObject::GetDnDDataFormat())
// The dnd object is an internal tree dnd
TreeDNDObject* tt = dynamic_cast<TreeDNDObject*>(dataobjComp-
>GetObject(TreeDNDObject::GetDnDDataFormat()));
if (tt != nullptr) // This checks if the source of the tt object is this codeblock instance, if not tt would
be nullptr
if ([Link]())
if (m_ui->HandleDropOnItem(item))
return wxDragMove;
return wxDragNone;
return wxDragNone;
wxDragResult ProjectTreeDropTarget::OnDragOver(wxCoord x, wxCoord y, wxDragResult defResult)
{
bool allowDrop = false;
int flag = 0;
wxUnusedVar(flag);
const wxTreeItemId item = m_treeCtrl->HitTest(wxPoint(x,y), flag);
m_treeCtrl->CalculateScrollingAfterMove(x, y);
// GetData in OnDragOver seems only to work in windows...
if (GetData())
// If we get any data, we can check the target and give user feedback if he can drop the item here...
const wxDataObjectComposite *dataobjComp = static_cast<wxDataObjectComposite
*>(GetDataObject());
const wxDataFormat format = dataobjComp->GetReceivedFormat();
if (format == wxDF_FILENAME)
// For files we allow always dropping,
// if it is over a valid tree item, we get the project
// to add the file from the item, if the drop is over no
// valid tree item, we use the current active project
allowDrop = true;
defResult = wxDragCopy;
}
else if (format == TreeDNDObject::GetDnDDataFormat())
// For tree internal data we make a hit testing
if ([Link]() && m_ui->TestDropOnItem(item))
// this is a drag and drop item from the tree
allowDrop = true;
else
// If we get no data/format information we allow dropping always,
// give no feedback and check in the OnData function if the drop was
// allowed
allowDrop = true;
// for user feedback we color the current active item, but
// we also have to reset the old item
if (item != oldItem)
if ([Link]())
m_treeCtrl->SetItemDropHighlight(oldItem, false);
oldItem = item;
if ([Link]() && allowDrop)
m_treeCtrl->SetItemDropHighlight(item, true);
if (!allowDrop)
return wxDragNone;
return defResult;
BEGIN_EVENT_TABLE(ProjectManagerUI, wxEvtHandler)
EVT_TREE_BEGIN_DRAG(ID_ProjectManager, ProjectManagerUI::OnTreeBeginDrag)
EVT_TREE_BEGIN_LABEL_EDIT(ID_ProjectManager, ProjectManagerUI::OnBeginEditNode)
EVT_TREE_END_LABEL_EDIT(ID_ProjectManager, ProjectManagerUI::OnEndEditNode)
EVT_TREE_ITEM_ACTIVATED(ID_ProjectManager, ProjectManagerUI::OnProjectFileActivated)
EVT_TREE_ITEM_RIGHT_CLICK(ID_ProjectManager, ProjectManagerUI::OnTreeItemRightClick)
EVT_TREE_KEY_DOWN(ID_ProjectManager, ProjectManagerUI::OnKeyDown)
EVT_COMMAND_RIGHT_CLICK(ID_ProjectManager, ProjectManagerUI::OnRightClick)
EVT_AUINOTEBOOK_TAB_RIGHT_UP(idNB, ProjectManagerUI::OnTabContextMenu)
EVT_MENU_RANGE(idOpenWith[0], idOpenWith[MAX_OPEN_WITH_ITEMS - 1],
ProjectManagerUI::OnOpenWith)
EVT_MENU(idOpenWithInternal, ProjectManagerUI::OnOpenWith)
EVT_MENU(idNB_TabTop, ProjectManagerUI::OnTabPosition)
EVT_MENU(idNB_TabBottom, ProjectManagerUI::OnTabPosition)
EVT_MENU(idMenuSetActiveProject, ProjectManagerUI::OnSetActiveProject)
EVT_MENU(idMenuNextProject, ProjectManagerUI::OnSetActiveProject)
EVT_MENU(idMenuPriorProject, ProjectManagerUI::OnSetActiveProject)
EVT_MENU(idMenuProjectUp, ProjectManagerUI::OnSetActiveProject)
EVT_MENU(idMenuProjectDown, ProjectManagerUI::OnSetActiveProject)
EVT_MENU(idMenuTreeRenameWorkspace, ProjectManagerUI::OnRenameWorkspace)
EVT_MENU(idMenuTreeSaveWorkspace, ProjectManagerUI::OnSaveWorkspace)
EVT_MENU(idMenuTreeSaveAsWorkspace, ProjectManagerUI::OnSaveAsWorkspace)
EVT_MENU(idMenuTreeCloseWorkspace, ProjectManagerUI::OnCloseWorkspace)
EVT_MENU(idMenuAddVirtualFolder, ProjectManagerUI::OnAddVirtualFolder)
EVT_MENU(idMenuDeleteVirtualFolder, ProjectManagerUI::OnDeleteVirtualFolder)
EVT_MENU(idMenuAddFile, ProjectManagerUI::OnAddFileToProject)
EVT_MENU(idMenuAddFilesRecursively, ProjectManagerUI::OnAddFilesToProjectRecursively)
EVT_MENU(idMenuRemoveFile, ProjectManagerUI::OnRemoveFileFromProject)
EVT_MENU(idMenuAddFilePopup, ProjectManagerUI::OnAddFileToProject)
EVT_MENU(idMenuAddFilesRecursivelyPopup, ProjectManagerUI::OnAddFilesToProjectRecursively)
EVT_MENU(idMenuManageGlobs, ProjectManagerUI::OnManageGlobs)
EVT_MENU(idMenuManageGlobsPopup, ProjectManagerUI::OnManageGlobs)
EVT_MENU(idMenuRemoveFolderFilesPopup, ProjectManagerUI::OnRemoveFileFromProject)
EVT_MENU(idMenuOpenFolderFilesPopup, ProjectManagerUI::OnOpenFolderFiles)
EVT_MENU(idMenuRemoveFilePopup, ProjectManagerUI::OnRemoveFileFromProject)
EVT_MENU(idMenuRenameFile, ProjectManagerUI::OnRenameFile)
EVT_MENU(idMenuRenameVFolder, ProjectManagerUI::OnRenameVirtualFolder)
EVT_MENU(idMenuSaveProject, ProjectManagerUI::OnSaveProject)
EVT_MENU(idMenuSaveFile, ProjectManagerUI::OnSaveFile)
EVT_MENU(idMenuCloseProject, ProjectManagerUI::OnCloseProject)
EVT_MENU(idMenuCloseFile, ProjectManagerUI::OnCloseFile)
EVT_MENU(idMenuOpenFile, ProjectManagerUI::OnOpenFile)
EVT_MENU(idMenuProjectNotes, ProjectManagerUI::OnNotes)
EVT_MENU(idMenuProjectProperties, ProjectManagerUI::OnProperties)
EVT_MENU(idMenuFileProperties, ProjectManagerUI::OnProperties)
EVT_MENU(idMenuOpenInSystemFileBrowser, ProjectManagerUI::OnOpenFileInSystemBrowser)
EVT_MENU(idMenuTreeOptionsCompile, ProjectManagerUI::OnFileOptions)
EVT_MENU(idMenuTreeOptionsLink, ProjectManagerUI::OnFileOptions)
EVT_MENU(idMenuTreeOptionsEnableBoth, ProjectManagerUI::OnFileOptions)
EVT_MENU(idMenuTreeOptionsDisableBoth, ProjectManagerUI::OnFileOptions)
EVT_MENU(idMenuTreeProjectProperties, ProjectManagerUI::OnProperties)
EVT_MENU(idMenuTreeFileProperties, ProjectManagerUI::OnProperties)
EVT_MENU(idMenuGotoFile, ProjectManagerUI::OnGotoFile)
EVT_MENU(idMenuExecParams, ProjectManagerUI::OnExecParameters)
EVT_MENU(idMenuViewCategorize, ProjectManagerUI::OnViewCategorize)
EVT_MENU(idMenuViewUseFolders, ProjectManagerUI::OnViewUseFolders)
EVT_MENU(idMenuViewHideFolderName, ProjectManagerUI::OnViewHideFolderName)
EVT_MENU(idMenuViewCategorizePopup, ProjectManagerUI::OnViewCategorize)
EVT_MENU(idMenuViewUseFoldersPopup, ProjectManagerUI::OnViewUseFolders)
EVT_MENU(idMenuViewHideFolderNamePopup, ProjectManagerUI::OnViewHideFolderName)
EVT_MENU(idMenuViewSortAlphabetically, ProjectManagerUI::OnViewSortAlphabetically)
EVT_MENU(idMenuViewFileMasks, ProjectManagerUI::OnViewFileMasks)
EVT_MENU(idMenuFindFile, ProjectManagerUI::OnFindFile)
EVT_IDLE( ProjectManagerUI::OnIdle)
EVT_UPDATE_UI(idMenuFileProperties, ProjectManagerUI::OnUpdateUI)
EVT_UPDATE_UI(idMenuProjectProperties, ProjectManagerUI::OnUpdateUI)
EVT_UPDATE_UI(idMenuAddFile, ProjectManagerUI::OnUpdateUI)
EVT_UPDATE_UI(idMenuManageGlobs, ProjectManagerUI::OnUpdateUI)
EVT_UPDATE_UI(idMenuAddFilesRecursively, ProjectManagerUI::OnUpdateUI)
EVT_UPDATE_UI(idMenuRemoveFile, ProjectManagerUI::OnUpdateUI)
EVT_UPDATE_UI(idMenuProjectTreeProps, ProjectManagerUI::OnUpdateUI)
EVT_UPDATE_UI(idMenuAddVirtualFolder, ProjectManagerUI::OnUpdateUI)
EVT_UPDATE_UI(idMenuDeleteVirtualFolder, ProjectManagerUI::OnUpdateUI)
EVT_UPDATE_UI(idMenuExecParams, ProjectManagerUI::OnUpdateUI)
EVT_UPDATE_UI(idMenuProjectNotes, ProjectManagerUI::OnUpdateUI)
END_EVENT_TABLE()
ProjectManagerUI::ProjectManagerUI() :
m_pTree(nullptr),
m_pImages(nullptr),
m_TreeFreezeCounter(0),
m_isCheckingForExternallyModifiedProjects(false)
m_pNotebook = new cbAuiNotebook(Manager::Get()->GetAppWindow(), idNB,
wxDefaultPosition, wxDefaultSize, wxAUI_NB_WINDOWLIST_BUTTON);
if (Manager::Get()->GetConfigManager(_T("app"))->ReadBool(_T("/environment/
project_tabs_bottom"), false))
m_pNotebook->SetWindowStyleFlag(m_pNotebook->GetWindowStyleFlag() |
wxAUI_NB_BOTTOM);
InitPane();
ConfigManager *cfg = Manager::Get()->GetConfigManager(_T("project_manager"));
m_TreeVisualState = ptvsNone;
m_TreeVisualState |= (cfg->ReadBool(_T("/categorize_tree"), true) ? ptvsCategorize : ptvsNone);
m_TreeVisualState |= (cfg->ReadBool(_T("/use_folders"), true) ? ptvsUseFolders : ptvsNone);
m_TreeVisualState |= (cfg->ReadBool(_T("/hide_folder_name"), false) ? ptvsHideFolderName :
ptvsNone);
m_TreeVisualState |= (cfg->ReadBool(_T("/sort_alpha"), false) ? ptvsSortAlpha : ptvsNone);
// fix invalid combination, "use folders" has precedence
if ( (m_TreeVisualState&ptvsUseFolders) && (m_TreeVisualState&ptvsHideFolderName) )
m_TreeVisualState &= ~ptvsHideFolderName;
cfg->Write(_T("/hide_folder_name"), false);
RebuildTree();
Manager::Get()->GetColourManager()->RegisterColour(_("Project Tree"), _("Not-compiled files
(headers/resources)"),
wxT("project_tree_non_source_files"),
wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT));
// Event handling. This must be THE LAST THING activated on startup.
// Constructors and destructors must always follow the LIFO rule:
// Last in, first out.
Manager::Get()->GetAppWindow()->PushEventHandler(this);
m_fileSystemTimer.Bind(wxEVT_TIMER, &ProjectManagerUI::OnFileSystemTimer, this);
ProjectManagerUI::~ProjectManagerUI()
m_pNotebook->Destroy();
void ProjectManagerUI::InitPane()
if (Manager::IsAppShuttingDown())
return;
if (m_pTree)
return;
m_pTree = new cbTreeCtrl(m_pNotebook, ID_ProjectManager);
// Set drop target for adding files, and dragging tree items
m_pTree->SetDropTarget(new ProjectTreeDropTarget(m_pTree, this));
m_pImages = cbProjectTreeImages::MakeImageList(16, *m_pNotebook);
m_pTree->SetImageList(m_pImages.get());
m_pNotebook->AddPage(m_pTree, _("Projects"));
void ProjectManagerUI::RebuildTree()
if (Manager::IsAppShuttingDown()) // saves a lot of time at startup for large projects
return;
wxStopWatch timer;
FreezeTree();
ProjectManager* pm = Manager::Get()->GetProjectManager();
ProjectsArray* pa = pm->GetProjects();
const int count = pa->GetCount();
for (int i = 0; i < count; ++i)
if ( cbProject* prj = pa->Item(i) )
prj->SaveTreeState(m_pTree);
// Save the path (excluding the root item) to the last visible item
// Saving the first puts it at the bottom of the window
std::vector <wxString> path;
wxTreeItemId item = m_pTree->GetFirstVisibleItem();
if ([Link]() && (item != m_pTree->GetRootItem()))
while (m_pTree->GetNextVisible(item).IsOk())
item = m_pTree->GetNextVisible(item);
while ([Link]() && (item != m_pTree->GetRootItem()))
path.push_back(m_pTree->GetItemText(item));
item = m_pTree->GetItemParent(item);
m_pTree->DeleteAllItems();
wxString title;
bool read_only = false;
cbWorkspace* wkspc = pm->GetWorkspace();
if (wkspc)
title = wkspc->GetTitle();
wxString ws_file = wkspc->GetFilename();
read_only = ( !ws_file.IsEmpty()
&& wxFile::Exists(ws_file.c_str())
&& !wxFile::Access(ws_file.c_str(), wxFile::write) );
if ([Link]())
title = _("Workspace");
m_TreeRoot = m_pTree->AddRoot(title, cbProjectTreeImages::WorkspaceIconIndex(read_only),
cbProjectTreeImages::WorkspaceIconIndex(read_only));
std::vector<cbProject*> prjv;
for (int i = 0; i < count; ++i)
if (pa && pa->Item(i))
prjv.push_back(pa->Item(i));
if (m_TreeVisualState & ptvsSortAlpha)
std::sort([Link](), [Link](), [](cbProject* a, cbProject* b) { return a->GetTitle().Upper() < b-
>GetTitle().Upper();});
for (cbProject* prj : prjv)
BuildProjectTree(prj, m_pTree, m_TreeRoot, m_TreeVisualState, pm->GetFilesGroupsAndMasks());
m_pTree->SetItemBold(prj->GetProjectNode(), prj == pm->GetActiveProject());
}
m_pTree->Expand(m_TreeRoot);
for (int i = 0; i < count; ++i)
if ( cbProject* prj = pa->Item(i) )
prj->RestoreTreeState(m_pTree);
UnfreezeTree();
// Restore the last visible item
item = m_TreeRoot;
for (std::vector <wxString>::const_reverse_iterator it = [Link](); it != [Link](); ++it)
wxTreeItemIdValue cookie;
wxTreeItemId child = m_pTree->GetFirstChild(item, cookie);
while ([Link]())
if (m_pTree->GetItemText(child) == *it)
break;
child = m_pTree->GetNextChild(item, cookie);
if (![Link]())
break;
item = child;
m_pTree->EnsureVisible(item);
const long time = [Link]();
if (time >= 100)
LogManager *log = Manager::Get()->GetLogManager();
log->Log(wxString::Format(_("ProjectManagerUI::RebuildTree took %.3f seconds"), time / 1000.0f));
void ProjectManagerUI::FreezeTree()
if (!m_pTree)
return;
++m_TreeFreezeCounter;
m_pTree->Freeze();
void ProjectManagerUI::UnfreezeTree(cb_unused bool force)
{
if (!m_pTree)
return;
if (m_TreeFreezeCounter)
--m_TreeFreezeCounter;
m_pTree->Thaw();
void ProjectManagerUI::ReloadFileSystemWatcher(cbProject* prj)
#if wxUSE_FSWATCHER
auto oldPrjItr = m_FileSystemWatcherMap.find(prj);
if (oldPrjItr != m_FileSystemWatcherMap.end())
for (const auto& watcher : oldPrjItr->second)
[Link]->Unbind(wxEVT_FSWATCHER, [Link]);
m_FileSystemWatcherMap.erase(oldPrjItr);
std::vector<FileSystemWatcher> projectWatches;
ProjectLoader loader(prj);
bool refresh = false;
for (const ProjectGlob& glob : prj->GetGlobs())
FileSystemWatcher newWatcher;
[Link] = std::unique_ptr<wxFileSystemWatcher>(new wxFileSystemWatcher());
wxFileName fname = wxFileName::DirName([Link]());
if ([Link]())
[Link](wxFileName(prj->GetFilename()).GetPath());
if ([Link]())
[Link]->AddTree(fname, wxFSW_EVENT_CREATE | wxFSW_EVENT_DELETE |
wxFSW_EVENT_RENAME);
else
[Link]->Add(fname, wxFSW_EVENT_CREATE | wxFSW_EVENT_DELETE |
wxFSW_EVENT_RENAME);
[Link] = [=](wxFileSystemWatcherEvent& evt) {this->OnFileSystemEvent(evt);};
[Link]->Bind(wxEVT_FSWATCHER, [Link], wxID_ANY, wxID_ANY, new
FileSystemEventObject(prj, glob));
projectWatches.push_back(std::move(newWatcher));
refresh |= [Link](glob);
m_FileSystemWatcherMap[prj] = std::move(projectWatches);
if (refresh)
RebuildTree();
#endif //wxUSE_FSWATCHER
}
void ProjectManagerUI::UpdateActiveProject(cbProject* oldProject, cbProject* newProject, bool
refresh)
if (oldProject)
wxTreeItemId tid = oldProject->GetProjectNode();
if (tid)
m_pTree->SetItemBold(tid, false);
if (newProject)
wxTreeItemId tid = newProject->GetProjectNode();
if (tid)
m_pTree->SetItemBold(tid, true);
#if wxUSE_FSWATCHER
auto oldPrjItr = m_FileSystemWatcherMap.find(oldProject);
if (oldPrjItr != m_FileSystemWatcherMap.end())
for (const auto& watcher : oldPrjItr->second)
[Link]->Unbind(wxEVT_FSWATCHER, [Link]);
}
m_FileSystemWatcherMap.erase(oldPrjItr);
std::vector<FileSystemWatcher> projectWatches;
ProjectLoader loader(newProject);
for (const ProjectGlob& glob : newProject->GetGlobs())
FileSystemWatcher newWatcher;
[Link] = std::unique_ptr<wxFileSystemWatcher>(new wxFileSystemWatcher());
wxFileName fname = wxFileName::DirName([Link]());
if ([Link]())
[Link](wxFileName(newProject->GetFilename()).GetPath());
if ([Link]())
[Link]->AddTree(fname, wxFSW_EVENT_CREATE | wxFSW_EVENT_DELETE |
wxFSW_EVENT_RENAME);
else
[Link]->Add(fname, wxFSW_EVENT_CREATE | wxFSW_EVENT_DELETE |
wxFSW_EVENT_RENAME);
}
[Link] = [=](wxFileSystemWatcherEvent& evt) {this->OnFileSystemEvent(evt);};
[Link]->Bind(wxEVT_FSWATCHER, [Link], wxID_ANY, wxID_ANY, new
FileSystemEventObject(newProject, glob));
projectWatches.push_back(std::move(newWatcher));
refresh |= [Link](glob);
m_FileSystemWatcherMap[newProject] = std::move(projectWatches);
#endif //wxUSE_FSWATCHER
if (refresh)
RebuildTree();
if (newProject)
m_pTree->EnsureVisible(newProject->GetProjectNode());
m_pTree->Refresh();
#if wxUSE_FSWATCHER
void ProjectManagerUI::OnFileSystemEvent(wxFileSystemWatcherEvent& evt)
if (Manager::IsAppShuttingDown())
return;
FileSystemEventObject* obj = (FileSystemEventObject*) [Link]();
int type = [Link]();
if (type == wxFSW_EVENT_CREATE || type == wxFSW_EVENT_DELETE || type ==
wxFSW_EVENT_RENAME)
if (std::find(m_globsToUpdate.cbegin(), m_globsToUpdate.cend(), *obj) == m_globsToUpdate.end())
m_globsToUpdate.push_back(*obj);
if (!m_fileSystemTimer.IsRunning())
m_fileSystemTimer.StartOnce(1000);
#endif // wxUSE_FSWATCHER
void ProjectManagerUI::OnFileSystemTimer(wxTimerEvent& evt)
wxStopWatch timer;
for (auto itr = m_globsToUpdate.begin(); itr != m_globsToUpdate.end();)
ProjectLoader loader(itr->project);
[Link](itr->glob);
itr = m_globsToUpdate.erase(itr);
long time = [Link]();
Manager::Get()->GetLogManager()->Log(wxString::Format("Loading globs took: %f s", time / 1000.0));
[Link]();
RebuildTree();
time = [Link]();
Manager::Get()->GetLogManager()->Log(wxString::Format("Rebuilding tree took: %f s", time /
1000.0));
void ProjectManagerUI::RemoveProject(cbProject* project)
if (!project)
return;
#if wxUSE_FSWATCHER
auto prjItr = m_FileSystemWatcherMap.find(project);
if (prjItr != m_FileSystemWatcherMap.end())
for (const auto& watcher : prjItr->second)
[Link]->Unbind(wxEVT_FSWATCHER, [Link]);
m_FileSystemWatcherMap.erase(prjItr);
#endif // wxUSE_FSWATCHER
m_pTree->Delete(project->GetProjectNode());
wxTreeItemId ProjectManagerUI::GetTreeSelection()
{
// User may have selected several items and right-clicked on one,
// so return the right-click item instead in that case.
if (m_RightClickItem.IsOk())
return m_RightClickItem;
wxArrayTreeItemIds selections;
unsigned int sel = m_pTree->GetSelections(selections);
if (sel)
// Usually return the first item in the selection list.
return selections[0];
return wxTreeItemId();
void ProjectManagerUI::BeginLoadingWorkspace()
FreezeTree();
m_pTree->AppendItem(m_pTree->GetRootItem(), _("Loading workspace..."));
m_pTree->Expand(m_pTree->GetRootItem());
UnfreezeTree();
void ProjectManagerUI::CloseWorkspace()
{
if (m_pTree)
m_pTree->SetItemText(m_TreeRoot, _("Workspace"));
if (!Manager::IsAppShuttingDown())
RebuildTree(); // update the workspace icon if required
void ProjectManagerUI::FinishLoadingProject(cbProject* project, bool newAddition, cb_unused
FilesGroupsAndMasks* fgam)
// If the project tree is sorted alphabetically we have to rebuild the project tree
// also when it is a new addition...
if (newAddition && !(m_TreeVisualState & ptvsSortAlpha))
ProjectManager* pm = Manager::Get()->GetProjectManager();
BuildProjectTree(project, m_pTree, m_TreeRoot, m_TreeVisualState, pm-
>GetFilesGroupsAndMasks());
else
RebuildTree();
m_pTree->Expand(project->GetProjectNode());
m_pTree->Expand(m_TreeRoot); // make sure the root node is open
}
void ProjectManagerUI::FinishLoadingWorkspace(cbProject* activeProject, const wxString
&workspaceTitle)
RebuildTree();
if (activeProject)
m_pTree->Expand(activeProject->GetProjectNode());
m_pTree->Expand(m_TreeRoot); // make sure the root node is open
m_pTree->SetItemText(m_TreeRoot, workspaceTitle);
UnfreezeTree(true);
void ProjectManagerUI::SwitchToProjectsPage()
CodeBlocksDockEvent showEvent(cbEVT_SHOW_DOCK_WINDOW);
[Link] = m_pNotebook;
Manager::Get()->ProcessEvent(showEvent);
int page = m_pNotebook->GetPageIndex(m_pTree);
if (page != wxNOT_FOUND)
m_pNotebook->SetSelection(page);
void ProjectManagerUI::ShowFileInTree(ProjectFile &projectFile)
// first unselect previous selected item if any, needed because of wxTR_MULTIPLE flag
m_pTree->UnselectAll();
const wxTreeItemId &itemId = [Link]();
if ([Link]())
m_pTree->EnsureVisible(itemId);
m_pTree->SelectItem(itemId, true);
void ProjectManagerUI::CreateMenu(wxMenuBar* menuBar)
/* TODO (mandrav#1#): Move menu items from [Link], here */
if (menuBar)
int pos = menuBar->FindMenu(_("Sea&rch"));
wxMenu* menu = menuBar->GetMenu(pos);
if (menu)
menu->Append(idMenuGotoFile, _("Goto file...\tAlt-G"));
pos = menuBar->FindMenu(_("&File"));
menu = menuBar->GetMenu(pos);
if (menu)
menu->Insert(menu->GetMenuItemCount() - 1, idMenuFileProperties, _("Properties..."));
menu->Insert(menu->GetMenuItemCount() - 1, wxID_SEPARATOR, _T("")); // instead of
AppendSeparator();
pos = menuBar->FindMenu(_("&Project"));
menu = menuBar->GetMenu(pos);
if (menu)
if (menu->GetMenuItemCount())
menu->AppendSeparator();
menu->Append(idMenuAddFile, _("Add files..."), _("Add files to the project"));
menu->Append(idMenuAddFilesRecursively, _("Add files recursively..."), _("Add files recursively
to the project"));
menu->Append(idMenuManageGlobs, _("Automatic source paths..."), _("Manage automatic
source paths"));
menu->Append(idMenuRemoveFile, _("Remove files..."), _("Remove files from the
project"));
menu->AppendSeparator();
CreateMenuTreeProps(menu, false);
menu->Append(idMenuExecParams, _("Set &programs' arguments..."), _("Set execution
parameters for the targets of this project"));
menu->Append(idMenuProjectNotes, _("Notes..."));
menu->Append(idMenuProjectProperties, _("Properties..."));
}
}
void ProjectManagerUI::CreateMenuTreeProps(wxMenu* menu, bool popup)
wxMenu* treeprops = new wxMenu;
treeprops->Append(idMenuProjectUp, _("Move project up\tCtrl-Shift-Up"),
_("Move project up in project tree"));
treeprops->Append(idMenuProjectDown, _("Move project down\tCtrl-Shift-Down"),
_("Move project down in project tree"));
treeprops->AppendSeparator();
treeprops->Append(idMenuPriorProject, _("Activate prior project\tAlt-F5"),
_("Activate prior project in open projects list"));
treeprops->Append(idMenuNextProject, _("Activate next project\tAlt-F6"),
_("Activate next project in open projects list"));
treeprops->AppendSeparator();
treeprops->AppendCheckItem((popup ? idMenuViewCategorizePopup : idMenuViewCategorize),
_("Categorize by file types"));
treeprops->AppendCheckItem((popup ? idMenuViewUseFoldersPopup : idMenuViewUseFolders),
_("Display folders as on disk"));
treeprops->AppendCheckItem((popup ? idMenuViewHideFolderNamePopup :
idMenuViewHideFolderName),
_("Hide folder name"));
treeprops->AppendCheckItem(idMenuViewSortAlphabetically, _("Sort projects alphabetically"));
ConfigManager *cfg = Manager::Get()->GetConfigManager(_T("project_manager"));
bool do_categorise = cfg->ReadBool(_T("/categorize_tree"), true);
bool do_use_folders = cfg->ReadBool(_T("/use_folders"), true);
bool do_sort_alpha = cfg->ReadBool(_T("/sort_alpha"), false);
bool do_hide_folder_name = !do_use_folders && cfg->ReadBool(_T("/hide_folder_name"), false); //
"use folders" has precedence
cfg->Write(_T("/hide_folder_name"), do_hide_folder_name); // make sure that configuration is
consistent
treeprops->Check((popup ? idMenuViewCategorizePopup : idMenuViewCategorize),
do_categorise);
treeprops->Check((popup ? idMenuViewUseFoldersPopup : idMenuViewUseFolders),
do_use_folders);
treeprops->Check((popup ? idMenuViewHideFolderNamePopup : idMenuViewHideFolderName),
do_hide_folder_name);
treeprops->Enable((popup ? idMenuViewUseFoldersPopup : idMenuViewUseFolders), !
do_hide_folder_name);
treeprops->Enable((popup ? idMenuViewHideFolderNamePopup : idMenuViewHideFolderName), !
do_use_folders);
treeprops->Check(idMenuViewSortAlphabetically, do_sort_alpha);
treeprops->Enable(idMenuProjectUp, !do_sort_alpha);
treeprops->Enable(idMenuProjectDown, !do_sort_alpha);
treeprops->Append(idMenuViewFileMasks, _("Edit file types && categories..."));
menu->Append(idMenuProjectTreeProps, _("Project tree"), treeprops);
void ProjectManagerUI::ShowMenu(wxTreeItemId id, const wxPoint& pt)
if ( ![Link]() )
return;
wxString caption;
wxMenu menu;
FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(id);
bool is_vfolder = ftd && ftd->GetKind() == FileTreeData::ftdkVirtualFolder;
/* Following code will check for currently compiling project.
* If it finds the selected is project is currently compiling,
* then it will disable some of the options */
bool PopUpMenuOption = true;
ProjectsArray* pa = Manager::Get()->GetProjectManager()->GetProjects();
if ( pa && ftd
&& ( ftd->GetKind() == FileTreeData::ftdkProject
|| ftd->GetKind() == FileTreeData::ftdkFile
|| ftd->GetKind() == FileTreeData::ftdkFolder ) )
PopUpMenuOption = !cbHasRunningCompilers(Manager::Get()->GetPluginManager());
}
// if it is not the workspace, add some more options
ProjectManager* pm = Manager::Get()->GetProjectManager();
if (ftd)
// if it is a project...
if (ftd->GetKind() == FileTreeData::ftdkProject)
if (ftd->GetProject() != pm->GetActiveProject())
[Link](idMenuSetActiveProject, _("Activate project"));
[Link](idMenuSetActiveProject, PopUpMenuOption);
[Link](idMenuSaveProject, _("Save project"));
[Link](idMenuSaveProject, PopUpMenuOption);
[Link](idMenuCloseProject, _("Close project"));
[Link](idMenuCloseProject, PopUpMenuOption);
[Link]();
[Link](idMenuAddFilePopup, _("Add files..."));
[Link](idMenuAddFilePopup, PopUpMenuOption);
[Link](idMenuAddFilesRecursivelyPopup, _("Add files recursively..."));
[Link](idMenuAddFilesRecursivelyPopup, PopUpMenuOption);
[Link](idMenuManageGlobsPopup, _("Automatic source paths..."));
[Link](idMenuManageGlobsPopup, PopUpMenuOption);
[Link](idMenuRemoveFile, _("Remove files..."));
[Link]();
[Link](idMenuFindFile, _("Find file..."));
[Link]();
CreateMenuTreeProps(&menu, true);
[Link](idMenuAddVirtualFolder, _("Add new virtual folder..."));
if (is_vfolder)
[Link](idMenuDeleteVirtualFolder, _("Delete this virtual folder..."));
// if it is a file...
else if (ftd->GetKind() == FileTreeData::ftdkFile)
// selected project file
ProjectFile* pf = ftd->GetProjectFile();
// is it already open in the editor?
EditorBase* eb = Manager::Get()->GetEditorManager()->IsOpen(pf->[Link]());
if (eb)
// is it already active?
bool active = Manager::Get()->GetEditorManager()->GetActiveEditor() == eb;
if (!active)
{
[Link](_("Switch to %s"), m_pTree->GetItemText(id).c_str());
[Link](idMenuOpenFile, caption);
[Link](_("Save %s"), m_pTree->GetItemText(id).c_str());
[Link](idMenuSaveFile, caption);
[Link](_("Close %s"), m_pTree->GetItemText(id).c_str());
[Link](idMenuCloseFile, caption);
else
[Link](_("Open %s"), m_pTree->GetItemText(id).c_str());
[Link](idMenuOpenFile, caption);
// add "Open with" menu
wxMenu* openWith = new wxMenu;
PluginsArray mimes = Manager::Get()->GetPluginManager()->GetMimeOffers();
for (unsigned int i = 0; i < [Link]() && i < MAX_OPEN_WITH_ITEMS; ++i)
cbMimePlugin* plugin = (cbMimePlugin*)mimes[i];
if (plugin && plugin->CanHandleFile(m_pTree->GetItemText(id)))
const PluginInfo* info = Manager::Get()->GetPluginManager()->GetPluginInfo(plugin);
openWith->Append(idOpenWith[i], info ? info->title : wxString(_("<Unknown plugin>")));
}
}
openWith->AppendSeparator();
openWith->Append(idOpenWithInternal, _("Internal editor"));
[Link](wxID_ANY, _("Open with"), openWith);
if (pf->GetFileState() == fvsNormal || pf->GetFileState() == fvsModified)
[Link]();
[Link](idMenuRenameFile, _("Rename file..."));
[Link](idMenuRenameFile, PopUpMenuOption);
// project files loaded by a glob can not be removed from the project.
// they will added automatically on next reload
if (!pf->IsGlobValid())
[Link]();
[Link](idMenuRemoveFilePopup, _("Remove file from project"));
[Link](idMenuRemoveFilePopup, PopUpMenuOption);
// if it is a folder...
else if (ftd->GetKind() == FileTreeData::ftdkFolder)
{
[Link](idMenuAddFilePopup, _("Add files..."));
[Link](idMenuAddFilePopup, PopUpMenuOption);
[Link](idMenuAddFilesRecursivelyPopup, _("Add files recursively..."));
[Link](idMenuAddFilesRecursivelyPopup, PopUpMenuOption);
[Link](idMenuManageGlobsPopup, _("Automatic source paths..."));
[Link](idMenuManageGlobsPopup, PopUpMenuOption);
[Link]();
[Link](idMenuRemoveFile, _("Remove files..."));
[Link]();
[Link](idMenuFindFile, _("Find file..."));
[Link]();
wxFileName f(ftd->GetFolder());
[Link](ftd->GetProject()->GetCommonTopLevelPath());
[Link](idMenuRemoveFolderFilesPopup, wxString::Format(_("Remove %s*"),
[Link]().c_str()));
[Link](idMenuRemoveFolderFilesPopup, PopUpMenuOption);
[Link](idMenuOpenFolderFilesPopup, wxString::Format(_("Open %s*"),
[Link]().c_str()));
// if it is a virtual folder
else if (is_vfolder)
[Link](idMenuAddVirtualFolder, _("Add new virtual folder..."));
[Link](idMenuDeleteVirtualFolder, _("Delete this virtual folder"));
[Link](idMenuRenameVFolder, _("Rename this virtual folder"));
[Link]();
[Link](idMenuRemoveFile, _("Remove files..."));
[Link](idMenuRemoveFolderFilesPopup, wxString::Format(_("Remove %s*"), ftd-
>GetFolder().c_str()));
[Link](idMenuOpenFolderFilesPopup, wxString::Format(_("Open %s*"), ftd-
>GetFolder().c_str()));
[Link]();
[Link](idMenuFindFile, _("Find file..."));
// if it is a virtual group (wild-card matching)
else if (ftd->GetKind() == FileTreeData::ftdkVirtualGroup)
[Link](idMenuFindFile, _("Find file..."));
// ask any plugins to add items in this menu
Manager::Get()->GetPluginManager()->AskPluginsForModuleMenu(mtProjectManager, &menu,
ftd);
// more project options
if (ftd->GetKind() == FileTreeData::ftdkProject)
[Link](idMenuOpenInSystemFileBrowser, _("Open containing folder"));
[Link](idMenuTreeProjectProperties, _("Properties..."));
[Link](idMenuTreeProjectProperties, PopUpMenuOption);
}
// more file options
else if (ftd->GetKind() == FileTreeData::ftdkFile)
[Link]();
wxMenu *options = new wxMenu;
wxMenuItem *optionsItem = [Link](options, _("Options"));
optionsItem->Enable(PopUpMenuOption);
options->AppendCheckItem(idMenuTreeOptionsCompile, _("Compile file"));
options->AppendCheckItem(idMenuTreeOptionsLink, _("Link file"));
options->AppendSeparator();
options->Append(idMenuTreeOptionsEnableBoth, _("Enable both"));
options->Append(idMenuTreeOptionsDisableBoth, _("Disable both"));
if ( ProjectFile* pf = ftd->GetProjectFile() )
[Link](idMenuTreeOptionsCompile, pf->compile);
[Link](idMenuTreeOptionsLink, pf->link);
[Link](idMenuOpenInSystemFileBrowser, _("Open containing folder"));
[Link](idMenuTreeFileProperties, _("Properties..."));
[Link](idMenuTreeFileProperties, PopUpMenuOption);
}
else if (!ftd && pm->GetWorkspace())
wxCommandEvent event;
OnRightClick(event);
return;
if ([Link]() != 0)
m_pTree->PopupMenu(&menu, pt);
void ProjectManagerUI::DoOpenFile(ProjectFile* pf, const wxString& filename)
// Basic stuff: We can only open files that are still present
wxFileName the_file(filename);
if (!the_file.FileExists())
wxString msg;
[Link](_("Could not open the file '%s'.\nThe file does not exist."), filename.c_str());
cbMessageBox(msg, _("Error"));
pf->SetFileState(fvsMissing);
Manager::Get()->GetLogManager()->LogError(msg);
return;
}
FileType ft = FileTypeOf(filename);
if (ft == ftHeader || ft == ftSource || ft == ftTemplateSource)
// C/C++ header/source files, always get opened inside Code::Blocks
if ( cbEditor* ed = Manager::Get()->GetEditorManager()->Open(filename) )
ed->SetProjectFile(pf);
ed->Activate();
else
wxString msg;
[Link](_("Failed to open '%s'."), filename.c_str());
Manager::Get()->GetLogManager()->LogError(msg);
else
// first look for custom editors
// if that fails, try MIME handlers
EditorBase* eb = Manager::Get()->GetEditorManager()->IsOpen(filename);
if (eb && !eb->IsBuiltinEditor())
// custom editors just get activated
eb->Activate();
return;
// not a recognized file type
cbMimePlugin* plugin = Manager::Get()->GetPluginManager()->GetMIMEHandlerForFile(filename);
if (!plugin)
wxString msg;
[Link](_("Could not open file '%s'.\nNo handler registered for this type of file."),
filename.c_str());
Manager::Get()->GetLogManager()->LogError(msg);
else if (plugin->OpenFile(filename) != 0)
const PluginInfo* info = Manager::Get()->GetPluginManager()->GetPluginInfo(plugin);
wxString msg;
[Link](_("Could not open file '%s'.\nThe registered handler (%s) could not open it."),
filename, info ? info->title : wxString(_("<Unknown plugin>")));
Manager::Get()->GetLogManager()->LogError(msg);
void ProjectManagerUI::DoOpenSelectedFile()
wxTreeItemId sel = GetTreeSelection();
if (![Link]())
return;
if ( FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel) )
if ( ProjectFile* pf = ftd->GetProjectFile() )
DoOpenFile(pf, pf->[Link]());
void ProjectManagerUI::RemoveFilesRecursively(wxTreeItemId& sel_id)
wxTreeItemIdValue cookie;
wxTreeItemId child;
size_t i = 0;
while (i < m_pTree->GetChildrenCount(sel_id))
if (i == 0)
child = m_pTree->GetFirstChild(sel_id, cookie);
else
child = m_pTree->GetNextChild(sel_id, cookie);
if ([Link]())
if ( FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(child) )
{
cbProject* prj = ftd->GetProject();
if (prj && ftd->GetKind() == FileTreeData::ftdkFile)
ProjectFile* pf = ftd->GetProjectFile();
if (pf)
wxString dir = pf->[Link]();
if(::wxDirExists(dir))
Manager::Get()->GetProjectManager()->RemoveFileFromProject(pf, prj);
else
wxString msg = wxString::Format(_("The directory '%s' no longer exists on disk") , dir);
msg += "\n\n" + _("Instead, use 'Remove files...' and select all") + " ...";
cbMessageBox(msg, _("Error"), wxICON_ERROR);
Manager::Get()->GetLogManager()->LogError(msg);
break;
else if ( ftd->GetKind() == FileTreeData::ftdkFolder
|| ftd->GetKind() == FileTreeData::ftdkVirtualFolder)
{
RemoveFilesRecursively(child);
++i;
else
break;
void ProjectManagerUI::OpenFilesRecursively(wxTreeItemId& sel_id)
wxTreeItemIdValue cookie;
wxTreeItemId child;
size_t i = 0;
while (i < m_pTree->GetChildrenCount(sel_id))
if (i == 0)
child = m_pTree->GetFirstChild(sel_id, cookie);
else
child = m_pTree->GetNextChild(sel_id, cookie);
if ([Link]())
FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(child);
if (ftd)
{
cbProject* prj = ftd->GetProject();
if (prj && ftd->GetKind() == FileTreeData::ftdkFile)
if ( ProjectFile* pf = ftd->GetProjectFile() )
DoOpenFile(pf, pf->[Link]());
else if ( ftd->GetKind() == FileTreeData::ftdkFolder
|| ftd->GetKind() == FileTreeData::ftdkVirtualFolder )
OpenFilesRecursively(child);
++i;
else
break;
// events
void ProjectManagerUI::OnTabContextMenu(cb_unused wxAuiNotebookEvent& event)
{
wxMenu* NBmenu = new wxMenu();
if (Manager::Get()->GetConfigManager(_T("app"))->ReadBool(_T("/environment/
project_tabs_bottom"), false))
NBmenu->Append(idNB_TabTop, _("Tabs at top"));
else
NBmenu->Append(idNB_TabBottom, _("Tabs at bottom"));
m_pNotebook->PopupMenu(NBmenu);
delete NBmenu;
void ProjectManagerUI::OnTabPosition(wxCommandEvent& event)
long style = m_pNotebook->GetWindowStyleFlag();
style &= ~wxAUI_NB_BOTTOM;
if ([Link]() == idNB_TabBottom)
style |= wxAUI_NB_BOTTOM;
m_pNotebook->SetWindowStyleFlag(style);
m_pNotebook->Refresh();
// (style & wxAUI_NB_BOTTOM) saves info only about the the tabs position
Manager::Get()->GetConfigManager(_T("app"))->Write(_T("/environment/project_tabs_bottom"),
(bool)(style & wxAUI_NB_BOTTOM));
void ProjectManagerUI::OnTreeBeginDrag(wxTreeEvent& event)
{
[Link]();
wxArrayString fileList;
size_t count = m_pTree->GetSelections(m_DraggingSelection);
for (size_t i = 0; i < count; i++)
//what item do we start dragging?
wxTreeItemId id = m_DraggingSelection[i];
if (![Link]())
return;
// if no data associated with it, disallow
FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(id);
if (!ftd)
return;
// if no project, disallow
cbProject* prj = ftd->GetProject();
if (!prj)
return;
// allow only if the project approves
if (!ProjectCanDragNode(prj, m_pTree, id))
continue;
// We allow drag and drop for normal files, projects, or virtual folders,
// but not for mixed selection, or any other project items
if (ftd->GetKind() == FileTreeData::ftdkFile)
[Link](ftd->GetProjectFile()->[Link]());
else if (ftd->GetKind() == FileTreeData::ftdkProject)
[Link](ftd->GetProject()->GetFilename());
else if(ftd->GetKind() == FileTreeData::ftdkVirtualFolder)
[Link](ftd->GetFolder());
if (![Link]())
m_pTree->SetCursor(wxCursor(wxCURSOR_HAND)); //show feedback to user
// create a composite data object, to make it possible
// drag objects in text editor and also fix bug, where a user drags an items
// outside the control and then back in. This triggers this part of code,
// and with the composite TreeDNDObject we know that this is from the tree in
// the drop code
wxDataObjectComposite dropObject;
[Link](new wxTextDataObject(GetStringFromArray(fileList , wxT("\n"), false)));
[Link](new TreeDNDObject(), true);
wxDropSource dragSource(m_pTree);
[Link](dropObject);
[Link]();
m_pTree->SetCursor(wxCursor(wxNullCursor));
return;
bool ProjectManagerUI::TestDropOnItem(const wxTreeItemId& to) const
// is the drag target valid?
if (![Link]())
return false;
// if no data associated with any of them, disallow
FileTreeData* ftdTo = (FileTreeData*)m_pTree->GetItemData(to);
if (!ftdTo)
return false;
// if no project or different projects, disallow
cbProject* prjTo = ftdTo->GetProject();
if (!prjTo)
return false;
const size_t count = m_DraggingSelection.Count();
for (size_t i = 0; i < count; i++)
wxTreeItemId from = m_DraggingSelection[i];
// is the item valid?
if (![Link]())
return false;
// if no data associated with any of them, disallow
FileTreeData* ftdFrom = (FileTreeData*)m_pTree->GetItemData(from);
if (!ftdFrom)
return false;
// if no project or different projects, disallow
cbProject* prjFrom = ftdTo->GetProject();
if (prjFrom != prjTo)
return false;
if (!TestProjectNodeDragged(prjTo, m_pTree, m_DraggingSelection, to))
return false;
return true;
bool ProjectManagerUI::HandleDropOnItem(const wxTreeItemId& to)
if (!TestDropOnItem(to))
return false;
FileTreeData* ftdTo = (FileTreeData*)m_pTree->GetItemData(to);
if (!ftdTo)
return false;
cbProject* prjTo = ftdTo->GetProject();
if (!prjTo)
return false;
// allow only if the project approves
if (!ProjectNodeDragged(prjTo, m_pTree, m_DraggingSelection, to))
return false;
return true;
}
void ProjectManagerUI::OnProjectFileActivated(wxTreeEvent& event)
wxTreeItemId id = [Link]();
FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(id);
ProjectManager* pm = Manager::Get()->GetProjectManager();
if (ftd && ftd->GetKind() == FileTreeData::ftdkProject)
if (ftd->GetProject() != pm->GetActiveProject())
pm->SetProject(ftd->GetProject(), false);
// prevent item expand state toggle when project is activated
// toggle it one time so that it is toggled back by wx
m_pTree->IsExpanded(id) ? m_pTree->Collapse(id) : m_pTree->Expand(id);
else if ( ftd
&& ( (ftd->GetKind() == FileTreeData::ftdkVirtualGroup)
|| (ftd->GetKind() == FileTreeData::ftdkVirtualFolder)
|| (ftd->GetKind() == FileTreeData::ftdkFolder) ) )
m_pTree->IsExpanded(id) ? m_pTree->Collapse(id) : m_pTree->Expand(id);
else if (!ftd && pm->GetWorkspace())
m_pTree->IsExpanded(m_TreeRoot) ? m_pTree->Collapse(m_TreeRoot) : m_pTree-
>Expand(m_TreeRoot);
else
DoOpenSelectedFile();
void ProjectManagerUI::OnExecParameters(cb_unused wxCommandEvent& event)
if (Manager::Get()->GetProjectManager()->GetActiveProject())
Manager::Get()->GetProjectManager()->GetActiveProject()->SelectTarget(-1, true);
void ProjectManagerUI::OnRightClick(cb_unused wxCommandEvent& event)
ProjectManager* pm = Manager::Get()->GetProjectManager();
if (!pm)
return;
bool notCompilingProject = true;
cbProject *project = pm->GetActiveProject();
if (project && project->GetCurrentlyCompilingTarget())
notCompilingProject = false;
wxMenu menu;
if (pm->GetWorkspace())
[Link](idMenuTreeRenameWorkspace, _("Rename workspace..."));
[Link](idMenuTreeRenameWorkspace, notCompilingProject);
[Link]();
[Link](idMenuTreeSaveWorkspace, _("Save workspace"));
[Link](idMenuTreeSaveWorkspace, notCompilingProject);
[Link](idMenuTreeSaveAsWorkspace, _("Save workspace as..."));
[Link](idMenuTreeSaveAsWorkspace, notCompilingProject);
[Link]();
[Link](idMenuFindFile, _("Find file..."));
// ask any plugins to add items in this menu
Manager::Get()->GetPluginManager()->AskPluginsForModuleMenu(mtProjectManager, &menu);
// if plugins added to this menu, add a separator
if ([Link]() != 0)
[Link]();
[Link](idMenuViewCategorizePopup, _("Categorize by file types"));
[Link](idMenuViewUseFoldersPopup, _("Display folders as on disk"));
[Link](idMenuViewHideFolderNamePopup, _("Hide folder name"));
[Link](idMenuViewSortAlphabetically, _("Sort projects alphabetically"));
bool do_categorise = (m_TreeVisualState&ptvsCategorize);
bool do_use_folders = (m_TreeVisualState&ptvsUseFolders);
bool do_hide_folder_name = !do_use_folders && (m_TreeVisualState&ptvsHideFolderName); // "use
folders" has precedence
bool do_sort_alpha = (m_TreeVisualState&ptvsSortAlpha);
[Link](idMenuViewCategorizePopup, do_categorise);
[Link](idMenuViewUseFoldersPopup, do_use_folders);
[Link](idMenuViewHideFolderNamePopup, do_hide_folder_name);
[Link](idMenuViewSortAlphabetically, do_sort_alpha);
[Link](idMenuViewUseFoldersPopup, !do_hide_folder_name);
[Link](idMenuViewHideFolderNamePopup, !do_use_folders);
[Link]();
[Link](idMenuViewFileMasks, _("Edit file types && categories..."));
[Link](idMenuViewFileMasks, notCompilingProject);
if (pm->GetWorkspace())
// this menu items should be always the last one
[Link]();
[Link](idMenuTreeCloseWorkspace, _("Close workspace"));
[Link](idMenuTreeCloseWorkspace, notCompilingProject);
wxPoint pt = wxGetMousePosition();
pt = m_pTree->ScreenToClient(pt);
m_pTree->PopupMenu(&menu, pt);
}
void ProjectManagerUI::OnTreeItemRightClick(wxTreeEvent& event)
if (Manager::Get()->GetProjectManager()->IsLoadingProject())
wxBell();
return;
// We have a popup menu, so we will use the right-click item instead of the first tree selection.
m_RightClickItem = [Link]();
m_pTree->SelectItem([Link]());
ShowMenu([Link](), [Link]());
// Unset it so that we go back to using the first tree selection again.
m_RightClickItem.Unset();
void ProjectManagerUI::OnRenameWorkspace(cb_unused wxCommandEvent& event)
cbWorkspace* wkspc = Manager::Get()->GetProjectManager()->GetWorkspace();
if (wkspc)
wxString text = cbGetTextFromUser(_("Please enter the new name for the workspace:"),
_("Rename workspace"),
wkspc->GetTitle());
if (![Link]())
wkspc->SetTitle(text);
m_pTree->SetItemText(m_TreeRoot, wkspc->GetTitle());
void ProjectManagerUI::OnSaveWorkspace(cb_unused wxCommandEvent& event)
ProjectManager* pm = Manager::Get()->GetProjectManager();
if (pm->GetWorkspace())
pm->SaveWorkspace();
void ProjectManagerUI::OnSaveAsWorkspace(cb_unused wxCommandEvent& event)
ProjectManager* pm = Manager::Get()->GetProjectManager();
if (pm->GetWorkspace())
pm->SaveWorkspaceAs(wxString());
void ProjectManagerUI::OnCloseWorkspace(cb_unused wxCommandEvent& event)
{
ProjectManager* pm = Manager::Get()->GetProjectManager();
if (pm->GetWorkspace())
pm->CloseWorkspace();
void ProjectManagerUI::OnSetActiveProject(wxCommandEvent& event)
ProjectManager* pm = Manager::Get()->GetProjectManager();
ProjectsArray* pa = pm->GetProjects();
if ([Link]() == idMenuSetActiveProject)
wxTreeItemId sel = GetTreeSelection();
if (![Link]())
return;
FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel);
if (!ftd)
return;
pm->SetProject(ftd->GetProject(), false);
else if ([Link]() == idMenuPriorProject)
{
int index = pa->Index(pm->GetActiveProject());
if (index == wxNOT_FOUND)
return;
--index;
if (index < 0)
index = pa->GetCount() - 1;
pm->SetProject(pa->Item(index), false);
else if ([Link]() == idMenuNextProject)
int index = pa->Index(pm->GetActiveProject());
if (index == wxNOT_FOUND)
return;
++index;
if (index == (int)pa->GetCount())
index = 0;
pm->SetProject(pa->Item(index), false);
else if ([Link]() == idMenuProjectUp)
wxTreeItemId sel = GetTreeSelection();
if (![Link]())
return;
if ( FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel) )
MoveProjectUp(ftd->GetProject());
}
else if ([Link]() == idMenuProjectDown)
wxTreeItemId sel = GetTreeSelection();
if (![Link]())
return;
if ( FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel) )
MoveProjectDown(ftd->GetProject());
void ProjectManagerUI::OnAddFilesToProjectRecursively(wxCommandEvent& event)
ProjectManager* pm = Manager::Get()->GetProjectManager();
cbProject* prj = nullptr;
wxString basePath;
if ([Link]() == idMenuAddFilesRecursively)
prj = pm->GetActiveProject();
if (prj)
basePath = prj->GetBasePath();
else
{
wxTreeItemId sel = GetTreeSelection();
if (![Link]())
return;
if ( FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel) )
if ( (prj = ftd->GetProject()) )
basePath = ftd->GetFolder();
if (!wxDirExists(basePath))
basePath = prj->GetBasePath();
if (!prj)
return;
wxString dir = ChooseDirectory(m_pTree,
_("Add files recursively..."),
basePath,
wxEmptyString,
false,
false);
if ([Link]())
return;
wxArrayInt targets;
// ask for target only if more than one
if (prj->GetBuildTargetsCount() == 1)
[Link](0);
// generate list of files to add
wxArrayString array;
wxDir::GetAllFiles(dir, &array, wxEmptyString, wxDIR_FILES | wxDIR_DIRS);
if ([Link]() == 0)
return;
// for usability reasons, remove any directory entries from the list...
unsigned int i = 0;
while (i < [Link]())
// discard directories, as well as some well known SCMs control folders ;)
// also discard C::B project files
if (wxDirExists(array[i]) ||
array[i].Contains(_T("/.git/")) ||
array[i].Contains(_T("\\.git\\")) ||
array[i].Contains(_T("\\.hg\\")) ||
array[i].Contains(_T("/.hg/")) ||
array[i].Contains(_T("\\.svn\\")) ||
array[i].Contains(_T("/.svn/")) ||
array[i].Contains(_T("\\CVS\\")) ||
array[i].Contains(_T("/CVS/")) ||
array[i].Lower().Matches(_T("*.cbp")))
[Link](i);
else
++i;
wxString wild;
const FilesGroupsAndMasks* fgam = pm->GetFilesGroupsAndMasks();
for (unsigned fm_idx = 0; fm_idx < fgam->GetGroupsCount(); fm_idx++)
wild += fgam->GetFileMasks(fm_idx);
MultiSelectDlg dlg(nullptr, array, wild, _("Select the files to add to the project:"));
PlaceWindow(&dlg);
if ([Link]() != wxID_OK)
return;
array = [Link]();
// finally add the files
pm->AddMultipleFilesToProject(array, prj, targets);
RebuildTree();
}
void ProjectManagerUI::OnManageGlobs(cb_unused wxCommandEvent& event)
ManageGlobsDlg globManager(Manager::Get()->GetProjectManager()->GetActiveProject(),
Manager::Get()->GetAppWindow());
PlaceWindow(&globManager);
[Link]();
void ProjectManagerUI::OnAddFileToProject(wxCommandEvent& event)
ProjectManager* pm = Manager::Get()->GetProjectManager();
cbProject* prj = nullptr;
wxString basePath;
if ([Link]() == idMenuAddFile)
prj = pm->GetActiveProject();
if (prj)
basePath = prj->GetBasePath();
else
wxTreeItemId sel = GetTreeSelection();
if (![Link]())
return;
if ( FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel) )
if ( (prj = ftd->GetProject()) )
basePath = ftd->GetFolder();
if (!wxDirExists(basePath))
basePath = prj->GetBasePath();
if (!prj)
return;
wxFileDialog dlg(Manager::Get()->GetAppWindow(),
_("Add files to project..."),
basePath,
wxEmptyString,
FileFilters::GetFilterString(),
wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST | compatibility::wxHideReadonly);
[Link](FileFilters::GetIndexForFilterAll());
PlaceWindow(&dlg);
if ([Link]() == wxID_OK)
{
wxArrayInt targets;
// ask for target only if more than one
if (prj->GetBuildTargetsCount() == 1)
[Link](0);
wxArrayString array;
[Link](array);
pm->AddMultipleFilesToProject(array, prj, targets);
RebuildTree();
namespace
void FindFiles(wxArrayString &resultFiles, wxTreeCtrl &tree, wxTreeItemId item)
FileTreeData* ftd = static_cast<FileTreeData*>([Link](item));
if (!ftd)
return;
switch (ftd->GetKind())
case FileTreeData::ftdkFile:
[Link](ftd->GetProjectFile()->relativeFilename);
break;
case FileTreeData::ftdkFolder:
wxTreeItemIdValue cookie;
wxTreeItemId i = [Link](item, cookie);
while ([Link]())
FindFiles(resultFiles, tree, i);
i = [Link](item, cookie);
break;
case FileTreeData::ftdkUndefined: // fall-through
case FileTreeData::ftdkProject: // fall-through
case FileTreeData::ftdkVirtualGroup: // fall-through
case FileTreeData::ftdkVirtualFolder: // fall-through
default:
for (FilesList::iterator it = ftd->GetProject()->GetFilesList().begin(); it != ftd->GetProject()-
>GetFilesList().end(); ++it)
[Link](((ProjectFile*)*it)->relativeFilename);
} // namespace
void ProjectManagerUI::OnRemoveFileFromProject(wxCommandEvent& event)
{
ProjectManager* pm = Manager::Get()->GetProjectManager();
wxTreeItemId sel = GetTreeSelection();
if (![Link]())
return;
FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel);
if (!ftd)
return;
cbProject* prj = ftd->GetProject();
if (!prj)
return;
wxString oldpath = prj->GetCommonTopLevelPath();
if ([Link]() == idMenuRemoveFile)
// remove multiple-files
wxArrayString files;
FindFiles(files, *m_pTree, sel);
if ([Link]())
cbMessageBox(_("This project does not contain any files to remove."),
_("Error"), wxICON_WARNING);
return;
}
[Link]();
wxString msg;
[Link](_("Select files to remove from %s:"), prj->GetTitle());
MultiSelectDlg dlg(nullptr, files, false, msg); // deselect all files
PlaceWindow(&dlg);
if ([Link]() == wxID_OK)
wxArrayInt indices = [Link]();
if ([Link]())
return;
if (cbMessageBox(_("Are you sure you want to remove these files from the project?"),
_("Confirmation"),
wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT) != wxID_YES)
return;
wxStopWatch timer;
wxProgressDialog progress(_("Project Manager"),
_("Please wait while removing files from the project..."),
[Link](),
Manager::Get()->GetAppFrame());
prj->BeginRemoveFiles();
wxStopWatch updateProgressTimer;
// we iterate the array backwards, because if we iterate it normally,
// when we remove the first index, the rest becomes invalid...
for (int i = (int)[Link]() - 1; i >= 0; --i)
if ( ProjectFile* pf = prj->GetFileByFilename(files[indices[i]]) )
pm->RemoveFileFromProject(pf, prj);
if ((i % 256 == 0) && ([Link]() >= 100))
[Link]([Link]() - i);
[Link]();
prj->CalculateCommonTopLevelPath();
prj->EndRemoveFiles();
[Link]([Link]());
const long time = [Link]();
if (time >= 100)
{
LogManager *log = Manager::Get()->GetLogManager();
log->Log(wxString::Format(_("ProjectManagerUI::OnRemoveFileFromProject took: %.3f
seconds for %zu files."),
time / 1000.0f, [Link]()));
RebuildTree();
else if ([Link]() == idMenuRemoveFilePopup)
wxArrayTreeItemIds selections;
std::map <cbProject*, wxArrayTreeItemIds> projectMap;
// Classify selected files by project
const size_t fileCount = m_pTree->GetSelections(selections);
for (size_t i = 0; i < fileCount; ++i)
if (!selections[i].IsOk())
continue;
FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(selections[i]);
if (!ftd)
continue;
cbProject* prj = ftd->GetProject();
if (!prj)
continue;
[Link](std::pair <cbProject*, wxArrayTreeItemIds> (prj, wxArrayTreeItemIds())).first-
>[Link](selections[i]);
if (![Link]())
// Remove files project by project
for (std::map <cbProject*, wxArrayTreeItemIds>::const_iterator it = [Link](); it !=
[Link](); ++it)
cbProject* prj = it->first;
prj->BeginRemoveFiles();
const size_t idCount = it->[Link]();
for (size_t i = 0; i < idCount; ++i)
FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(it->second[i]);
ProjectFile* pf = ftd->GetProjectFile();
if (!pf)
continue;
const wxString topLevelPath(prj->GetCommonTopLevelPath());
pm->RemoveFileFromProject(pf, prj);
prj->CalculateCommonTopLevelPath();
if (prj->GetCommonTopLevelPath() == topLevelPath)
m_pTree->Delete(selections[i]);
prj->EndRemoveFiles();
RebuildTree();
else if ([Link]() == idMenuRemoveFolderFilesPopup)
// remove all files from a folder
if (cbMessageBox(_("Are you sure you want to recursively remove from the project all the files
under this folder?"),
_("Confirmation"),
wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT) != wxID_YES)
return;
bool is_virtual = ftd->GetKind() == FileTreeData::ftdkVirtualFolder;
if (is_virtual || ftd->GetKind() == FileTreeData::ftdkFolder)
prj->BeginRemoveFiles();
RemoveFilesRecursively(sel);
prj->EndRemoveFiles();
}
prj->CalculateCommonTopLevelPath();
if (prj->GetCommonTopLevelPath() == oldpath && !is_virtual)
m_pTree->Delete(sel);
else if (is_virtual)
ProjectVirtualFolderDeleted(prj, m_pTree, sel);
RebuildTree();
void ProjectManagerUI::OnSaveProject(wxCommandEvent& WXUNUSED(event))
wxTreeItemId sel = GetTreeSelection();
if (![Link]())
return;
ProjectManager* pm = Manager::Get()->GetProjectManager();
if (FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel))
if (cbProject* prj = ftd->GetProject())
// TODO : does it make sense NOT to save project file while compiling ??
if (pm->IsLoadingProject() || prj->GetCurrentlyCompilingTarget())
wxBell();
else
pm->SaveProject(prj);
void ProjectManagerUI::OnCloseProject(wxCommandEvent& WXUNUSED(event))
ProjectManager* pm = Manager::Get()->GetProjectManager();
if (pm->IsLoadingProject())
wxBell();
return;
wxArrayTreeItemIds selections;
int count = m_pTree->GetSelections(selections);
if (count == 0)
return;
std::set<cbProject*> projectsToClose;
for (size_t ii = 0; ii < [Link](); ++ii)
FileTreeData* ftd = reinterpret_cast<FileTreeData*>(m_pTree->GetItemData(selections[ii]));
if (!ftd || ftd->GetKind() != FileTreeData::ftdkProject)
continue;
cbProject* prj = ftd->GetProject();
if (prj)
if (prj->GetCurrentlyCompilingTarget())
wxBell();
else
[Link](prj);
for (std::set<cbProject*>::iterator it = [Link](); it != [Link](); ++it)
pm->CloseProject(*it);
if (pm->GetProjects()->GetCount() > 0 && !pm->GetActiveProject())
pm->SetProject(pm->GetProjects()->Item(0), false);
Manager::Get()->GetAppWindow()->Refresh();
void ProjectManagerUI::OnSaveFile(wxCommandEvent& WXUNUSED(event))
wxTreeItemId sel = GetTreeSelection();
if (![Link]())
return;
if (FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel))
if ( ProjectFile* pf = ftd->GetProjectFile() )
Manager::Get()->GetEditorManager()->Save(pf->[Link]());
void ProjectManagerUI::OnCloseFile(wxCommandEvent& WXUNUSED(event))
wxTreeItemId sel = GetTreeSelection();
if (![Link]())
return;
if (FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel))
if ( ProjectFile* pf = ftd->GetProjectFile() )
Manager::Get()->GetEditorManager()->Close(pf->[Link]());
void ProjectManagerUI::OnOpenFile(wxCommandEvent& WXUNUSED(event))
DoOpenSelectedFile();
}
void ProjectManagerUI::OnOpenFolderFiles(wxCommandEvent& event)
wxTreeItemId sel = GetTreeSelection();
FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel);
if (!ftd)
return;
// open all files from a folder
if (cbMessageBox(_("Are you sure you want to recursively open from the project all the files under
this folder?"),
_("Confirmation"),
wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT) != wxID_YES)
return;
if ( ftd->GetKind() == FileTreeData::ftdkFolder
|| ftd->GetKind() == FileTreeData::ftdkVirtualFolder )
OpenFilesRecursively(sel);
[Link]();
}
void ProjectManagerUI::OnOpenWith(wxCommandEvent& event)
wxTreeItemId sel = GetTreeSelection();
if (![Link]())
return;
FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel);
if (!ftd)
return;
if ( ProjectFile* pf = ftd->GetProjectFile() )
wxString filename = pf->[Link]();
if ([Link]() == idOpenWithInternal)
if ( cbEditor* ed = Manager::Get()->GetEditorManager()->Open(filename) )
ed->SetProjectFile(pf);
ed->Show(true);
return;
else
{
PluginsArray mimes = Manager::Get()->GetPluginManager()->GetMimeOffers();
cbMimePlugin* plugin = (cbMimePlugin*)mimes[[Link]() - idOpenWith[0]];
if (plugin && plugin->OpenFile(filename) == 0)
return;
wxString msg;
[Link](_("Failed to open '%s'."), filename.c_str());
Manager::Get()->GetLogManager()->LogError(msg);
void ProjectManagerUI::OnNotes(wxCommandEvent& WXUNUSED(event))
if ( cbProject* prj = Manager::Get()->GetProjectManager()->GetActiveProject() )
prj->ShowNotes(false, true);
void ProjectManagerUI::OnOpenFileInSystemBrowser(wxCommandEvent& event)
wxTreeItemId sel = GetTreeSelection();
if (![Link]())
return;
FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel);
if (!ftd)
return;
if (ftd->GetKind() == FileTreeData::ftdkProject && ftd->GetProject())
wxLaunchDefaultApplication(ftd->GetProject()->GetCommonTopLevelPath());
return;
if (ProjectFile* pf = ftd->GetProjectFile())
wxLaunchDefaultApplication(pf->[Link]());
void ProjectManagerUI::OnProperties(wxCommandEvent& event)
ProjectManager* pm = Manager::Get()->GetProjectManager();
cbProject* activePrj = pm->GetActiveProject();
if ([Link]() == idMenuProjectProperties)
wxString backupTitle = activePrj ? activePrj->GetTitle() : _T("");
if (ProjectShowOptions(activePrj))
// make sure that cbEVT_PROJECT_ACTIVATE
// is sent (maybe targets have changed)...
// rebuild tree only if title has changed
pm->SetProject(activePrj, backupTitle != activePrj->GetTitle());
}
else if ([Link]() == idMenuTreeProjectProperties)
wxTreeItemId sel = GetTreeSelection();
if (![Link]())
return;
FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel);
cbProject* prj = ftd ? ftd->GetProject() : activePrj;
wxString backupTitle = prj ? prj->GetTitle() : _T("");
if (ProjectShowOptions(prj) && prj == activePrj)
// rebuild tree and make sure that cbEVT_PROJECT_ACTIVATE
// is sent (maybe targets have changed)...
// rebuild tree only if title has changed
pm->SetProject(prj, backupTitle != prj->GetTitle());
// if project title has changed, update the appropriate tab tooltips
wxString newTitle = prj->GetTitle();
if (backupTitle != newTitle)
// title has changed, if the tree is sorted alphabetically, we have to rebuild the tree
if (m_TreeVisualState & ptvsSortAlpha)
RebuildTree();
cbAuiNotebook* nb = Manager::Get()->GetEditorManager()->GetNotebook();
if (nb)
wxString toolTip;
for (size_t i = 0; i < nb->GetPageCount(); ++i)
toolTip = nb->GetPageToolTip(i);
if ([Link](_("Project: ") + backupTitle))
[Link](_("Project: ") + backupTitle,_("Project: ") + newTitle);
nb->SetPageToolTip(i, toolTip);
else if ([Link]() == idMenuTreeFileProperties)
wxTreeItemId sel = GetTreeSelection();
if (![Link]())
return;
FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel);
cbProject* prj = ftd ? ftd->GetProject() : activePrj;
if (prj)
if (ftd && ftd->GetFileIndex() != -1)
if (ProjectFile* pf = ftd->GetProjectFile())
pf->ShowOptions(Manager::Get()->GetAppWindow());
else // active editor properties
if (cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor())
if ( ProjectFile* pf = ed->GetProjectFile() )
pf->ShowOptions(Manager::Get()->GetAppWindow());
else
// active editor not-in-project
ProjectFileOptionsDlg dlg(Manager::Get()->GetAppWindow(), ed->GetFilename());
PlaceWindow(&dlg);
[Link]();
}
}
/// Find all selected tree items which are files and call the func on them.
/// The function is expected to return true when it made modifications to the ProjectFile parameter,
/// and false when it didn't.
/// The modified parameter will be filled with the set of modified projects.
template<typename Func>
static void applyFileOptionChange(std::set<cbProject*> &modified, wxTreeCtrl &tree, Func func)
wxArrayTreeItemIds selected;
size_t count = [Link](selected);
for (size_t ii = 0; ii < count; ++ii)
wxTreeItemId id = selected[ii];
if (![Link]())
continue;
FileTreeData* ftd = (FileTreeData*)[Link](id);
if (!ftd || ftd->GetKind() != FileTreeData::ftdkFile)
continue;
ProjectFile* pf = ftd->GetProjectFile();
if (pf && func(*pf))
{
if (pf->GetParentProject())
[Link](pf->GetParentProject());
void ProjectManagerUI::OnFileOptions(wxCommandEvent &event)
std::set<cbProject*> modified;
if ([Link]() == idMenuTreeOptionsCompile)
const bool checked = [Link]();
applyFileOptionChange(modified, *m_pTree, [checked](ProjectFile &pf) -> bool {
if ([Link] != checked)
[Link] = checked;
return true;
else
return false;
});
else if ([Link]() == idMenuTreeOptionsLink)
const bool checked = [Link]();
applyFileOptionChange(modified, *m_pTree, [checked](ProjectFile &pf) -> bool {
if ([Link] != checked)
[Link] = checked;
return true;
else
return false;
});
else if ([Link]() == idMenuTreeOptionsEnableBoth || [Link]() ==
idMenuTreeOptionsDisableBoth)
const bool newValue = ([Link]() == idMenuTreeOptionsEnableBoth);
applyFileOptionChange(modified, *m_pTree, [newValue](ProjectFile &pf) -> bool {
[Link] = [Link] = newValue;
return true;
});
for (std::set<cbProject*>::iterator it = [Link](); it != [Link](); ++it)
(*it)->SetModified(true);
if (![Link]())
RebuildTree();
[Link]();
}
struct ProjectFileRelativePathCmp
ProjectFileRelativePathCmp(cbProject* pActiveProject) : m_pActiveProject(pActiveProject) {}
bool operator()(ProjectFile* pf1, ProjectFile* pf2)
if (pf1->GetParentProject() == m_pActiveProject && pf2->GetParentProject() != m_pActiveProject)
return true;
else if (pf1->GetParentProject() != m_pActiveProject && pf2->GetParentProject() ==
m_pActiveProject)
return false;
else
int relCmp = pf1->[Link](pf2->relativeFilename);
if (relCmp == 0)
return pf1 < pf2;
else
return relCmp < 0;
private:
cbProject* m_pActiveProject;
};
void ProjectManagerUI::OnGotoFile(cb_unused wxCommandEvent& event)
{
ProjectManager* pm = Manager::Get()->GetProjectManager();
cbProject* activePrj = pm->GetActiveProject();
if (!activePrj)
Manager::Get()->GetLogManager()->DebugLog(_("No active project!"));
return;
ProjectsArray* pa = pm->GetProjects();
std::unordered_map<wxString, ProjectFile*> uniqueAbsPathFiles;
for (size_t prjIdx = 0; prjIdx < pa->GetCount(); ++prjIdx)
cbProject* prj = (*pa)[prjIdx];
if (!prj) continue;
for (FilesList::iterator it = prj->GetFilesList().begin(); it != prj->GetFilesList().end(); ++it)
ProjectFile *projectFile = *it;
[Link]({projectFile->[Link](), projectFile});
}
typedef std::vector<ProjectFile*> VProjectFiles;
VProjectFiles pfiles;
if (![Link]())
[Link]([Link]());
for (const auto &pf : uniqueAbsPathFiles)
pfiles.push_back([Link]);
std::sort([Link](), [Link](), ProjectFileRelativePathCmp(activePrj));
struct Iterator : IncrementalSelectIteratorIndexed
Iterator(VProjectFiles &pfiles, bool showProject) :
m_pfiles(pfiles),
m_ShowProject(showProject),
m_ColumnWidth(300)
int GetTotalCount() const override
return m_pfiles.size();
const wxString& GetItemFilterString(int index) const override
{
return m_pfiles[index]->relativeFilename;
wxString GetDisplayText(int index, cb_unused int column) const override
ProjectFile* pf = m_pfiles[m_indices[index]];
return MakeDisplayName(*pf);
int GetColumnWidth(cb_unused int column) const override
return m_ColumnWidth;
void CalcColumnWidth(wxListCtrl &list) override
int length = 0;
ProjectFile *pfLongest = nullptr;
for (const auto &pf : m_pfiles)
int pfLength = pf->[Link]();
if (m_ShowProject)
pfLength += pf->GetParentProject()->GetTitle().length() + 3;
if (pfLength > length)
length = pfLength;
pfLongest = pf;
}
if (pfLongest)
const wxString &longestString = MakeDisplayName(*pfLongest);
int yTemp;
[Link](longestString, &m_ColumnWidth, &yTemp);
// just to be safe if the longest string is made of thin letters.
m_ColumnWidth += 50;
else
m_ColumnWidth = 300;
private:
wxString MakeDisplayName(ProjectFile &pf) const
if (m_ShowProject)
return [Link] + wxT(" (") + [Link]()->GetTitle() + wxT(")");
else
return [Link];
private:
const VProjectFiles &m_pfiles;
wxString temp;
bool m_ShowProject;
int m_ColumnWidth;
};
Iterator iterator(pfiles, (pa->GetCount() > 1));
GotoFile dlg(Manager::Get()->GetAppWindow(), &iterator, _("Select file..."), _("Please select file to
open:"));
PlaceWindow(&dlg);
if ([Link]() == wxID_OK)
int selection = [Link]();
if (selection >= 0 && selection < int([Link]()))
DoOpenFile(pfiles[selection], pfiles[selection]->[Link]());
void ProjectManagerUI::OnViewCategorize(wxCommandEvent& event)
bool do_categorise = [Link]();
if (do_categorise)
m_TreeVisualState |= ptvsCategorize;
else
m_TreeVisualState &= ~ptvsCategorize;
Manager::Get()->GetAppFrame()->GetMenuBar()->Check(idMenuViewCategorize, do_categorise);
Manager::Get()->GetConfigManager(_T("project_manager"))->Write(_T("/categorize_tree"),
do_categorise);
RebuildTree();
void ProjectManagerUI::OnViewUseFolders(wxCommandEvent& event)
bool do_use_folders = [Link]();
if (do_use_folders)
m_TreeVisualState |= ptvsUseFolders;
else
m_TreeVisualState &= ~ptvsUseFolders;
Manager::Get()->GetAppFrame()->GetMenuBar()->Check(idMenuViewUseFolders, do_use_folders);
Manager::Get()->GetAppFrame()->GetMenuBar()->Enable(idMenuViewHideFolderName, !
do_use_folders);
Manager::Get()->GetConfigManager(_T("project_manager"))->Write(_T("/use_folders"),
do_use_folders);
// Do not create an invalid state
if (do_use_folders)
m_TreeVisualState &= ~ptvsHideFolderName;
Manager::Get()->GetAppFrame()->GetMenuBar()->Check(idMenuViewHideFolderName, false);
Manager::Get()->GetConfigManager(_T("project_manager"))->Write(_T("/hide_folder_name"),
false);
RebuildTree();
void ProjectManagerUI::OnViewSortAlphabetically(wxCommandEvent& event)
bool do_sort_alpha = [Link]();
Manager::Get()->GetConfigManager(_T("project_manager"))->Write(_T("/sort_alpha"),
do_sort_alpha);
// Do not create an invalid state
if (do_sort_alpha)
m_TreeVisualState |= ptvsSortAlpha;
else
m_TreeVisualState &= ~ptvsSortAlpha;
RebuildTree();
void ProjectManagerUI::OnViewHideFolderName(wxCommandEvent& event)
bool do_hide_folder_name = [Link]();
if (do_hide_folder_name)
m_TreeVisualState |= ptvsHideFolderName;
else
m_TreeVisualState &= ~ptvsHideFolderName;
Manager::Get()->GetAppFrame()->GetMenuBar()->Check(idMenuViewHideFolderName,
do_hide_folder_name);
Manager::Get()->GetAppFrame()->GetMenuBar()->Enable(idMenuViewUseFolders, !
do_hide_folder_name);
Manager::Get()->GetConfigManager(_T("project_manager"))->Write(_T("/hide_folder_name"),
do_hide_folder_name);
// Do not create an invalid state
if (do_hide_folder_name)
m_TreeVisualState &= ~ptvsUseFolders;
Manager::Get()->GetAppFrame()->GetMenuBar()->Check(idMenuViewUseFolders, false);
Manager::Get()->GetConfigManager(_T("project_manager"))->Write(_T("/use_folders"), false);
RebuildTree();
void ProjectManagerUI::OnViewFileMasks(cb_unused wxCommandEvent& event)
FilesGroupsAndMasks* fgam = Manager::Get()->GetProjectManager()->GetFilesGroupsAndMasks();
ProjectsFileMasksDlg dlg(Manager::Get()->GetAppWindow(), fgam);
PlaceWindow(&dlg);
if ([Link]() == wxID_OK)
fgam->Save();
RebuildTree();
wxArrayString ProjectManagerUI::ListNodes(wxTreeItemId node) const
wxArrayString nodes;
wxTreeItemIdValue cookie;
wxTreeItemId item = m_pTree->GetFirstChild(node, cookie);
while ([Link]())
[Link](m_pTree->GetItemText(item));
if (m_pTree->ItemHasChildren(item))
const wxArrayString& children = ListNodes(item);
const wxString parent = [Link]();
for (size_t i = 0; i < [Link](); ++i)
[Link](parent + wxT("/") + children[i]);
item = m_pTree->GetNextChild(node, cookie);
return nodes;
}
void ProjectManagerUI::OnFindFile(cb_unused wxCommandEvent& event)
wxTreeItemId sel = GetTreeSelection();
if (![Link]())
return;
wxArrayString files = ListNodes(sel);
if ([Link]())
return;
ProjectManager* pm = Manager::Get()->GetProjectManager();
// workspace selected, add *.cbp filenames
ConfigManagerContainer::StringToStringMap fileNameMap;
if ( pm->GetWorkspace() && !(FileTreeData*)m_pTree->GetItemData(sel) )
for (size_t i = 0; i < pm->GetProjects()->GetCount(); ++i)
const cbProject* prj = pm->GetProjects()->Item(i);
const wxFileName file(prj->GetFilename());
[Link]([Link]());
fileNameMap[[Link]()] = prj->GetTitle();
}
struct Iterator : IncrementalSelectIteratorIndexed
Iterator(const wxArrayString &files) : m_files(files), m_ColumnWidth(300)
int GetTotalCount() const override
return m_files.size();
const wxString& GetItemFilterString(int index) const override
return m_files[index];
wxString GetDisplayText(int index, cb_unused int column) const override
return m_files[m_indices[index]];
int GetColumnWidth(cb_unused int column) const override
return m_ColumnWidth;
}
void CalcColumnWidth(wxListCtrl &list) override
int index = -1;
size_t length = 0;
for (size_t ii = 0; ii < m_files.size(); ++ii)
size_t itemLength = m_files[ii].length();
if (itemLength > length)
index = ii;
length = itemLength;
if (index >= 0 && index < int(m_files.size()))
int yTemp;
[Link](m_files[index], &m_ColumnWidth, &yTemp);
// just to be safe if the longest string is made of thin letters.
m_ColumnWidth += 50;
else
m_ColumnWidth = 300;
private:
const wxArrayString &m_files;
int m_ColumnWidth;
};
Iterator iter(files);
GotoFile dlg(Manager::Get()->GetAppWindow(), &iter, _("Find file..."),
_("Please enter the name of the file you are searching:"));
ConfigManager *cfg = Manager::Get()->GetConfigManager(wxT("project_manager"));
// Add a checkbox at the bottom that control if the selected file will be opened in an editor.
wxCheckBox *chkOpen = new wxCheckBox(&dlg, wxID_ANY, _("Open file"));
chkOpen->SetValue(cfg->ReadBool(wxT("/find_file_open"), false));
[Link](chkOpen);
PlaceWindow(&dlg);
if ([Link]() != wxID_OK)
return;
const long selection = [Link]();
if (selection == wxNOT_FOUND)
return;
wxString file = files[selection];
ConfigManagerContainer::StringToStringMap::iterator it = [Link](file);
if (it != [Link]())
file = it->second; // resolve .cbp project filename
wxTreeItemIdValue cookie;
wxTreeItemId item = m_pTree->GetFirstChild(sel, cookie);
while ([Link]())
if (m_pTree->GetItemText(item) == file)
break; // found it, exit
else if ([Link](m_pTree->GetItemText(item) + wxT("/")))
// expand node
file = [Link](m_pTree->GetItemText(item).Length() + 1);
sel = item;
item = m_pTree->GetFirstChild(sel, cookie);
else // try next node
item = m_pTree->GetNextChild(sel, cookie);
if ([Link]())
m_pTree->UnselectAll();
m_pTree->SelectItem(item);
const FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(item);
if (ftd && chkOpen->IsChecked())
if ( ProjectFile* pf = ftd->GetProjectFile() )
DoOpenFile(pf, pf->[Link]()); // open the file
else if ( ftd->GetKind() == FileTreeData::ftdkProject
&& ftd->GetProject() )
// change active project
pm->SetProject(ftd->GetProject(), false);
cfg->Write(wxT("/find_file_open"), chkOpen->IsChecked());
else
// error ?!
// ... this should not fail (unless the tree was modified during selection)
void ProjectManagerUI::OnAddVirtualFolder(cb_unused wxCommandEvent& event)
wxString fld = cbGetTextFromUser(_("Please enter the new virtual folder path:"), _("New virtual
folder"));
if ([Link]())
return;
wxTreeItemId sel = GetTreeSelection();
if (![Link]())
return;
FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel);
if (!ftd)
return;
cbProject* prj = ftd->GetProject();
if (!prj)
return;
ProjectVirtualFolderAdded(prj, m_pTree, sel, fld);
void ProjectManagerUI::OnDeleteVirtualFolder(cb_unused wxCommandEvent& event)
wxTreeItemId sel = GetTreeSelection();
if (![Link]())
return;
FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel);
if (!ftd)
return;
cbProject* prj = ftd->GetProject();
if (!prj)
return;
ProjectVirtualFolderDeleted(prj, m_pTree, sel);
RebuildTree();
void ProjectManagerUI::OnRenameVirtualFolder(cb_unused wxCommandEvent& event)
wxTreeItemId sel = GetTreeSelection();
if (![Link]())
return;
FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel);
if (!ftd)
return;
cbProject* prj = ftd->GetProject();
if (!prj)
return;
wxTextEntryDialog dlg(Manager::Get()->GetAppWindow(),
_("Please enter the new name for the virtual folder:"),
_("Rename Virtual Folder"),
m_pTree->GetItemText(sel),
wxOK | wxCANCEL | wxCENTRE);
if ([Link]() == wxID_OK)
if (ProjectVirtualFolderRenamed(prj, m_pTree, sel, [Link]()))
RebuildTree();
void ProjectManagerUI::OnBeginEditNode(wxTreeEvent& event)
// what item do we start editing?
wxTreeItemId id = [Link]();
if (![Link]())
[Link]();
return;
// if no data associated with it, disallow
FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(id);
if (!ftd)
[Link]();
return;
}
// only allow editing virtual folders
if (ftd->GetKind() != FileTreeData::ftdkVirtualFolder)
[Link]();
void ProjectManagerUI::OnEndEditNode(wxTreeEvent& event)
FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData([Link]());
if (!ftd)
[Link]();
return;
cbProject* prj = ftd->GetProject();
if (!prj)
[Link]();
return;
if (!ProjectVirtualFolderRenamed(prj, m_pTree, [Link](), [Link]()))
[Link]();
void ProjectManagerUI::OnUpdateUI(wxUpdateUIEvent& event)
{
if ([Link]() == idMenuFileProperties)
EditorManager *editorManager = Manager::Get()->GetEditorManager();
bool enableProperties = false;
if (editorManager)
EditorBase *editor = editorManager->GetActiveEditor();
EditorBase *startHerePage = editorManager->GetEditor(GetStartHereTitle());
enableProperties = (editor && editor != startHerePage);
if (enableProperties)
enableProperties = !cbHasRunningCompilers(Manager::Get()->GetPluginManager());
[Link](enableProperties);
else if ([Link]() == idMenuProjectProperties || [Link]() == idMenuAddFile
|| [Link]() == idMenuAddFilesRecursively || [Link]() == idMenuRemoveFile
|| [Link]() == idMenuProjectTreeProps || [Link]() == idMenuAddVirtualFolder
|| [Link]() == idMenuDeleteVirtualFolder || [Link]() == idMenuManageGlobs
|| [Link]() == idMenuProjectNotes || [Link]() == idMenuExecParams )
ProjectManager *projectManager = Manager::Get()->GetProjectManager();
if (!projectManager || (projectManager->GetIsRunning() != nullptr))
[Link](false);
else
cbProject *project = projectManager->GetActiveProject();
if (!project)
[Link](false);
else
[Link](!cbHasRunningCompilers(Manager::Get()->GetPluginManager()));
else
[Link]();
void ProjectManagerUI::OnIdle(wxIdleEvent& event)
[Link]();
void ProjectManagerUI::OnRenameFile(cb_unused wxCommandEvent& event)
wxTreeItemId sel = GetTreeSelection();
if (![Link]())
return;
FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel);
if (!ftd)
return;
cbProject* prj = ftd->GetProject();
if (!prj)
return;
ProjectFile* pf = ftd->GetProjectFile();
if (!pf)
return;
if (pf->AutoGeneratedBy())
cbMessageBox(_("Can't rename file because it is auto-generated..."), _("Error"));
return;
const wxString path(pf->[Link](wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR));
const wxString oldName(pf->[Link]());
wxTextEntryDialog dlg(Manager::Get()->GetAppWindow(), _("Please enter the new name:"),
_("Rename file"), oldName, wxOK | wxCANCEL | wxCENTRE);
PlaceWindow(&dlg);
if ([Link]() == wxID_OK)
{
wxFileName fn([Link]());
const wxString newName([Link]());
if (oldName != newName)
const wxString absoluteOldName(path + oldName);
EditorManager* editorManager = Manager::Get()->GetEditorManager();
EditorBase* ed = editorManager->GetEditor(absoluteOldName);
if (ed)
editorManager->SetActiveEditor(ed);
AnnoyingDialog dialog(_("Close warning"),
_("The file must be closed before renaming, continue?"),
wxART_QUESTION, AnnoyingDialog::YES_NO, AnnoyingDialog::rtYES,
_("C&lose"), _("&Cancel"));
if (([Link]() != AnnoyingDialog::rtYES) || !editorManager->Close(ed))
return;
const wxString absoluteNewName(path + newName);
#ifdef __WXMSW__
// only overwrite files, if the names are the same, but with different cases
if (!wxRenameFile(absoluteOldName, absoluteNewName,
([Link]() == [Link]())))
#else
if (!wxRenameFile(absoluteOldName, absoluteNewName, false))
#endif
wxBell();
return;
pf->Rename(newName);
RebuildTree();
void ProjectManagerUI::OnKeyDown(wxTreeEvent& event)
const wxKeyEvent& key_event = [Link]();
cbProject* prj = Manager::Get()->GetProjectManager()->GetActiveProject();
if ( prj
&& (prj->GetCurrentlyCompilingTarget() == nullptr)
&& ( key_event.GetKeyCode() == WXK_DELETE
|| key_event.GetKeyCode() == WXK_NUMPAD_DELETE ) )
m_DraggingSelection.Clear(); // fix delete while drag crash
wxCommandEvent command(0, idMenuRemoveFilePopup);
OnRemoveFileFromProject(command);
else
[Link]();
void ProjectManagerUI::MoveProjectUp(cbProject* project, bool warpAround)
if (!project)
return;
ProjectManager* pm = Manager::Get()->GetProjectManager();
ProjectsArray* pa = pm->GetProjects();
int idx = pa->Index(project);
if (idx == wxNOT_FOUND)
return; // project not opened in project manager???
if (idx == 0)
if (!warpAround)
return;
else
idx = pa->Count();
}
pa->RemoveAt(idx--);
pa->Insert(project, idx);
RebuildTree();
if (pm->GetWorkspace())
pm->GetWorkspace()->SetModified(true);
// re-select the project
wxTreeItemId itemId = project->GetProjectNode();
cbAssert([Link]());
m_pTree->SelectItem(itemId);
m_pTree->EnsureVisible(itemId);
void ProjectManagerUI::MoveProjectDown(cbProject* project, bool warpAround)
if (!project)
return;
ProjectManager* pm = Manager::Get()->GetProjectManager();
ProjectsArray* pa = pm->GetProjects();
int idx = pa->Index(project);
if (idx == wxNOT_FOUND)
return; // project not opened in project manager???
if (idx == (int)pa->Count() - 1)
{
if (!warpAround)
return;
else
idx = 0;
pa->RemoveAt(idx++);
pa->Insert(project, idx);
RebuildTree();
if (pm->GetWorkspace())
pm->GetWorkspace()->SetModified(true);
// re-select the project
wxTreeItemId itemId = project->GetProjectNode();
cbAssert([Link]());
m_pTree->SelectItem(itemId);
m_pTree->EnsureVisible(itemId);
bool ProjectManagerUI::QueryCloseAllProjects()
if (!Manager::Get()->GetEditorManager()->QueryCloseAll())
return false;
ProjectsArray* pa = Manager::Get()->GetProjectManager()->GetProjects();
for (size_t i = 0; i < pa->GetCount(); ++i)
// Ask for saving modified projects. However,
// we already asked to save projects' files;
// do not ask again
if (!QueryCloseProject((*pa)[i], true))
return false;
return true;
bool ProjectManagerUI::QueryCloseProject(cbProject* proj, bool dontsavefiles)
if (!proj)
return true;
if (proj->GetCurrentlyCompilingTarget())
return false;
if (!dontsavefiles)
if (!proj->QueryCloseAllFiles())
return false;
if (proj->GetModified() && !Manager::IsBatchBuild())
wxString msg;
[Link](_("Project '%s' is modified...\nDo you want to save the changes?"), proj-
>GetTitle().c_str());
switch (cbMessageBox(msg, _("Save project"), wxICON_QUESTION | wxYES_NO | wxCANCEL))
case wxID_YES:
if (!proj->Save())
return false;
case wxID_NO:
break;
case wxID_CANCEL: // fall-through
default:
return false;
return true;
bool ProjectManagerUI::QueryCloseWorkspace()
cbWorkspace* wkspc = Manager::Get()->GetProjectManager()->GetWorkspace();
if (!wkspc)
return true;
// Don't ask to save the default workspace, if blank workspace is used on app startup.
bool blankWorkspace =
Manager::Get()->GetConfigManager(_T("app"))->ReadBool(_T("/environment/blank_workspace"), true);
if (!(wkspc->IsDefault() && blankWorkspace))
// always save workspace layout
wkspc->SaveLayout();
if (wkspc->GetModified())
// workspace needs save
wxString msg;
[Link](_("Workspace '%s' is modified. Do you want to save it?"), wkspc->GetTitle().c_str());
switch (cbMessageBox(msg, _("Save workspace"),
wxYES_NO | wxCANCEL | wxICON_QUESTION))
case wxID_YES:
Manager::Get()->GetProjectManager()->SaveWorkspace();
break;
case wxID_CANCEL:
return false;
default:
break;
// We always want to ask to save all loaded projects.
if (!QueryCloseAllProjects())
return false;
return true;
int ProjectManagerUI::AskForBuildTargetIndex(cbProject* project)
cbProject* prj = project;
if (!prj)
prj = Manager::Get()->GetProjectManager()->GetActiveProject();
if (!prj)
return -1;
// ask for target
wxArrayString array;
int count = prj->GetBuildTargetsCount();
for (int i = 0; i < count; ++i)
[Link](prj->GetBuildTarget(i)->GetTitle());
int target = cbGetSingleChoiceIndex(_("Select the target:"), _("Project targets"), array, m_pTree,
wxSize(300, 400));
return target;
wxArrayInt ProjectManagerUI::AskForMultiBuildTargetIndex(cbProject* project)
wxArrayInt indices;
cbProject* prj = project;
if (!prj)
prj = Manager::Get()->GetProjectManager()->GetActiveProject();
if (!prj)
return indices;
// ask for target
wxArrayString array;
int count = prj->GetBuildTargetsCount();
for (int i = 0; i < count; ++i)
[Link](prj->GetBuildTarget(i)->GetTitle());
MultiSelectDlg dlg(nullptr, array, true, _("Select the targets this file should belong to:"));
PlaceWindow(&dlg);
if ([Link]() == wxID_OK)
indices = [Link]();
return indices;
void ProjectManagerUI::ConfigureProjectDependencies(cbProject* base, wxWindow *parent)
ProjectDepsDlg dlg(parent, base);
PlaceWindow(&dlg);
[Link]();
}
void ProjectManagerUI::CheckForExternallyModifiedProjects()
if (m_isCheckingForExternallyModifiedProjects) // for some reason, a mutex locker does not work???
return;
wxStopWatch timer;
m_isCheckingForExternallyModifiedProjects = true;
// check also the projects (TO DO : what if we gonna reload while compiling/debugging)
// TODO : make sure the same project is the active one again
ProjectManager* pm = Manager::Get()->GetProjectManager();
if ( ProjectsArray* pa = pm->GetProjects())
bool reloadAll = false;
// make a copy of all the pointers before we start messing with closing and opening projects
// the hash (pa) could change the order
std::vector<cbProject*> ProjectPointers;
for (unsigned int idxProject = 0; idxProject < pa->Count(); ++idxProject)
ProjectPointers.push_back(pa->Item(idxProject));
for (unsigned int idxProject = 0; idxProject < [Link](); ++idxProject)
cbProject* prj = ProjectPointers[idxProject];
wxFileName fname(prj->GetFilename());
wxDateTime last = [Link]();
if ([Link](prj->GetLastModificationTime()))
{ // was modified -> reload
int ret = -1;
if (!reloadAll)
Manager::Get()->GetLogManager()->Log(prj->GetFilename());
wxString msg;
[Link](_("Project %s is modified outside the IDE...\nDo you want to reload it (you will
lose any unsaved work)?"),
prj->GetFilename().c_str());
ConfirmReplaceDlg dlg(Manager::Get()->GetAppWindow(), false, msg);
[Link](_("Reload Project?"));
PlaceWindow(&dlg);
// Find the window, that actually has the mouse-focus and force a release.
// This prevents crash on windows or hang on wxGTK.
wxWindow* win = wxWindow::GetCapture();
if (win)
win->ReleaseMouse();
[Link]();
ret = [Link]();
[Link]();
reloadAll = ret == crAll;
}
if (reloadAll || ret == crYes)
pm->ReloadProject(prj);
else if (ret == crCancel)
break;
else if (ret == crNo)
prj->Touch();
} // end for : idx : idxProject
long durationMS = [Link]();
if (durationMS > 100)
LogManager* log = Manager::Get()->GetLogManager();
log->Log(wxString::Format(_("Checking for externally modified projects took %.3lf seconds"),
durationMS / 1000.0f));
m_isCheckingForExternallyModifiedProjects = false;
namespace
static void ProjectTreeSortChildrenRecursive(cbTreeCtrl* tree, const wxTreeItemId& parent)
{
if (!tree->ItemHasChildren(parent))
return;
tree->SortChildren(parent);
wxTreeItemIdValue cookie = nullptr;
wxTreeItemId current = tree->GetFirstChild(parent, cookie);
while (current)
ProjectTreeSortChildrenRecursive(tree, current);
current = tree->GetNextChild(parent, cookie);
// helper function used by AddTreeNode
static wxString GetRelativeFolderPath(wxTreeCtrl* tree, wxTreeItemId parent)
wxString fld;
while ([Link]())
FileTreeData* ftd = (FileTreeData*)tree->GetItemData(parent);
if ( !ftd
|| ( (ftd->GetKind() != FileTreeData::ftdkFolder)
&& (ftd->GetKind() != FileTreeData::ftdkVirtualFolder) ) )
break;
[Link](tree->GetItemText(parent) + wxFILE_SEP_PATH);
parent = tree->GetItemParent(parent);
return fld;
static wxTreeItemId ProjectFindNodeToInsertAfter(wxTreeCtrl* tree, const wxString& text,
const wxTreeItemId& parent, bool in_folders)
wxTreeItemId result;
if (tree && [Link]())
wxTreeItemIdValue cookie = nullptr;
int fldIdx = cbProjectTreeImages::FolderIconIndex();
int vfldIdx = cbProjectTreeImages::VirtualFolderIconIndex();
wxTreeItemId last;
wxTreeItemId child = tree->GetFirstChild(parent, cookie);
while (child)
bool is_folder = tree->GetItemImage(child) == fldIdx || tree->GetItemImage(child) == vfldIdx;
if (in_folders)
{
if (!is_folder || [Link](tree->GetItemText(child)) < 0)
result = last;
break;
else
if (!is_folder && [Link](tree->GetItemText(child)) < 0)
result = last;
break;
last = child;
child = tree->GetNextChild(parent, cookie);
if (![Link]())
result = last;
return result;
}
static wxTreeItemId ProjectAddTreeNode(cbProject* project, wxTreeCtrl* tree, const wxString& text,
const wxTreeItemId& parent, bool useFolders,
FileTreeData::FileTreeDataKind folders_kind, bool compiles,
int image, FileTreeData* data)
// see if the text contains any path info, e.g. plugins/compilergcc/[Link]
// in that case, take the first element (plugins in this example), create a sub-folder
// with the same name and recurse with the result...
wxTreeItemId ret;
if ([Link]())
delete data;
return ret;
wxString path = text;
// special case for windows and files on a different drive
if ( platform::windows && ([Link]() > 1) && ([Link](1) == _T(':')) )
[Link](1, 1);
// avoid empty node names in case of UNC paths, then, at least the first two chars are slashes
while (([Link]() > 1) && ([Link](0) == _T('\\') || [Link](0) == _T('/')) )
[Link](0, 1);
if ([Link]())
delete data;
return ret;
int pos = [Link](_T('/'));
if (pos == -1)
pos = [Link](_T('\\'));
if (useFolders && pos >= 0)
// ok, we got it. now split it up and re-curse
wxString folder = [Link](pos);
// avoid consecutive path separators
while ([Link](pos + 1) == _T('/') || [Link](pos + 1) == _T('\\'))
++pos;
path = [Link]([Link]() - pos - 1);
wxTreeItemIdValue cookie = nullptr;
wxTreeItemId newparent = tree->GetFirstChild(parent, cookie);
while (newparent)
{
wxString itemText = tree->GetItemText(newparent);
if ([Link](folder))
break;
newparent = tree->GetNextChild(parent, cookie);
if (!newparent)
// in order not to override wxTreeCtrl to sort alphabetically but the
// folders be always on top, we just search here where to put the new folder...
int fldIdx = cbProjectTreeImages::FolderIconIndex();
int vfldIdx = cbProjectTreeImages::VirtualFolderIconIndex();
newparent = ProjectFindNodeToInsertAfter(tree, folder, parent, true);
FileTreeData* ftd = new FileTreeData(*data);
ftd->SetKind(folders_kind);
if (folders_kind != FileTreeData::ftdkVirtualFolder)
ftd->SetFolder(project->GetCommonTopLevelPath() + GetRelativeFolderPath(tree, parent) +
folder + wxFILE_SEP_PATH);
else
ftd->SetFolder(GetRelativeFolderPath(tree, parent) + folder + wxFILE_SEP_PATH);
ftd->SetProjectFile(nullptr);
int idx = folders_kind != FileTreeData::ftdkVirtualFolder ? fldIdx : vfldIdx;
newparent = tree->InsertItem(parent, newparent, folder, idx, idx, ftd);
}
ret = ProjectAddTreeNode(project, tree, path, newparent, true, folders_kind, compiles, image,
data);
else
ret = tree->AppendItem(parent, text, image, image, data);
if (!compiles)
ColourManager *manager = Manager::Get()->GetColourManager();
tree->SetItemTextColour(ret, manager->GetColour(wxT("project_tree_non_source_files")));
return ret;
static bool ProjectCanDragNode(cbProject* project, wxTreeCtrl* tree, wxTreeItemId node)
// what item do we start dragging?
if (![Link]())
return false;
// if no data associated with it, disallow
FileTreeData* ftd = (FileTreeData*)tree->GetItemData(node);
if (!ftd)
return false;
// if not ours, disallow
if (ftd->GetProject() != project)
return false;
// allow only if it is a file or a virtual folder or project file (.cbp)
return ( (ftd->GetKind() == FileTreeData::ftdkFile)
|| (ftd->GetKind() == FileTreeData::ftdkVirtualFolder)
|| (ftd->GetKind() == FileTreeData::ftdkProject) );
static void ProjectCopyTreeNodeRecursively(wxTreeCtrl* tree, const wxTreeItemId& item,
const wxTreeItemId& new_parent)
// first, some sanity checks
if (!tree || ![Link]() || !new_parent.IsOk())
return;
FileTreeData* ftd = (FileTreeData*)tree->GetItemData(item);
FileTreeData* ftd_moved = ftd ? new FileTreeData(*ftd) : nullptr;
int idx = tree->GetItemImage(item); // old image
wxColour col = tree->GetItemTextColour(item); // old colour
wxTreeItemId insert = ProjectFindNodeToInsertAfter(tree, tree->GetItemText(item), new_parent, ftd
&& ftd->GetKind() == FileTreeData::ftdkVirtualFolder);
wxTreeItemId target = tree->InsertItem(new_parent, insert, tree->GetItemText(item), idx, idx,
ftd_moved);
tree->SetItemTextColour(target, col);
// recurse for folders
if (tree->ItemHasChildren(item))
// vfolder: recurse for files all contained files virtual path
wxTreeItemIdValue cookie;
wxTreeItemId child = tree->GetFirstChild(item, cookie);
while ([Link]())
ProjectCopyTreeNodeRecursively(tree, child, target);
child = tree->GetNextChild(item, cookie);
if (!tree->IsExpanded(new_parent))
tree->Expand(new_parent);
if (ftd_moved && ftd_moved->GetProjectFile())
ftd_moved->GetProjectFile()->virtual_path = GetRelativeFolderPath(tree, new_parent);
static bool TestProjectVirtualFolderDragged(cbProject* project, wxTreeCtrl* tree, wxTreeItemId from,
wxTreeItemId to)
{
FileTreeData* ftdFrom = static_cast<FileTreeData*>(tree->GetItemData(from));
FileTreeData* ftdTo = static_cast<FileTreeData*>(tree->GetItemData(to) );
if (!ftdFrom || !ftdTo)
return false;
wxString sep = wxString(wxFileName::GetPathSeparator());
wxChar sepChar = wxFileName::GetPathSeparator();
wxString fromFolderPath = ftdFrom->GetFolder();
wxString toFolderPath = ftdTo->GetFolder();
wxString fromFolder = fromFolderPath;
fromFolder = [Link]();
fromFolder = [Link](sepChar) + sep;
wxString toFolder = toFolderPath;
toFolder = [Link]();
toFolder = [Link](sepChar) + sep;
if (ftdFrom->GetKind() == FileTreeData::ftdkVirtualFolder && ftdTo->GetKind() ==
FileTreeData::ftdkVirtualFolder)
const wxArrayString &oldArray = project->GetVirtualFolders();
for (size_t i = 0; i < [Link](); ++i)
if ())
const wxString& item = oldArray[i];
// A virtual folder has been dropped from a different place
if ([Link](toFolderPath + fromFolder) != wxNOT_FOUND)
return false;
else if (ftdFrom->GetKind() == FileTreeData::ftdkVirtualFolder && ftdTo->GetKind() ==
FileTreeData::ftdkProject)
const wxArrayString &oldArray = project->GetVirtualFolders();
for (size_t i = 0; i < [Link](); ++i)
const wxString& item = oldArray[i];
if ([Link](fromFolder))
// We can't overwrite an existing folder
return false;
return true;
}
static bool ProjectVirtualFolderDragged(cbProject* project, wxTreeCtrl* tree, wxTreeItemId from,
wxTreeItemId to)
FileTreeData* ftdFrom = static_cast<FileTreeData*>(tree->GetItemData(from));
FileTreeData* ftdTo = static_cast<FileTreeData*>(tree->GetItemData(to) );
if (!ftdFrom || !ftdTo)
return false;
wxString sep = wxString(wxFileName::GetPathSeparator());
wxChar sepChar = wxFileName::GetPathSeparator();
wxString fromFolderPath = ftdFrom->GetFolder();
wxString toFolderPath = ftdTo->GetFolder();
wxString fromFolder = fromFolderPath;
fromFolder = [Link]();
fromFolder = [Link](sepChar) + sep;
wxString toFolder = toFolderPath;
toFolder = [Link]();
toFolder = [Link](sepChar) + sep;
if (ftdFrom->GetKind() == FileTreeData::ftdkVirtualFolder && ftdTo->GetKind() ==
FileTreeData::ftdkVirtualFolder)
const wxArrayString &oldArray = project->GetVirtualFolders();
wxArrayString newFolders;
for (size_t i = 0; i < [Link](); ++i)
{
wxString item = oldArray[i];
if ([Link]([Link](sepChar)))
// The virtual folder is dragged under same root
int posFrom = [Link](fromFolderPath);
if (posFrom != wxNOT_FOUND)
item = [Link](posFrom);
if (![Link]())
[Link](item);
else if ([Link](toFolderPath))
// First add it to folder structure
[Link](item);
wxString fromFolderStr = fromFolderPath;
fromFolderStr = [Link]();
fromFolderStr = [Link](wxFileName::GetPathSeparator());
[Link](item + fromFolderStr + sep);
else
[Link](item);
else
{
// A virtual folder has been dropped from a different place
if ([Link](toFolderPath + fromFolder) != wxNOT_FOUND)
cbMessageBox(_("Another Virtual folder with same name exists in the destination folder!"),
_("Error"), wxOK | wxICON_ERROR, Manager::Get()->GetAppWindow());
return false;
if ([Link]([Link](sepChar)))
[Link](item);
else if ([Link]([Link](sepChar)))
int pos = [Link](fromFolder);
if (pos == 0)
if (![Link]())
[Link](toFolderPath + item);
else
[Link]([Link](pos));
if (![Link]())
[Link](toFolderPath + [Link](pos));
}
}
project->SetVirtualFolders(newFolders);
else if (ftdFrom->GetKind() == FileTreeData::ftdkVirtualFolder && ftdTo->GetKind() ==
FileTreeData::ftdkProject)
const wxArrayString &oldArray = project->GetVirtualFolders();
wxArrayString newFolders;
[Link](fromFolder);
for (size_t i = 0; i < [Link](); ++i)
wxString item = oldArray[i];
if ([Link](fromFolder))
// We can't overwrite an existing folder
cbMessageBox(_("Another Virtual folder with same name exists in the Project tree!"),
_("Error"), wxOK | wxICON_ERROR, Manager::Get()->GetAppWindow());
return false;
else if ([Link](fromFolderPath))
int pos = [Link](fromFolderPath);
if (pos != wxNOT_FOUND)
item = [Link](pos + [Link]());
if ([Link]() > 1)
item = [Link](fromFolder);
if ([Link](item) == wxNOT_FOUND)
[Link](item);
else
continue;
else
[Link](item);
else
[Link](item);
project->SetVirtualFolders(newFolders);
return true;
static bool TestProjectNodeDragged(cbProject* project, wxTreeCtrl* tree, const wxArrayTreeItemIds&
fromArray,
wxTreeItemId to)
// what items did we drag?
if (![Link]())
return false;
// if no data associated with it, disallow
FileTreeData* ftdTo = (FileTreeData*)tree->GetItemData(to);
if (!ftdTo)
return false;
// if not ours, disallow
if (ftdTo->GetProject() != project)
return false;
// allow only if a file or vfolder was dragged on a file, another vfolder or the project itself
if ( (ftdTo->GetKind() != FileTreeData::ftdkFile)
&& (ftdTo->GetKind() != FileTreeData::ftdkVirtualFolder)
&& (ftdTo->GetKind() != FileTreeData::ftdkProject) )
return false;
wxTreeItemId parentTo = ftdTo->GetKind() == FileTreeData::ftdkFile ? tree->GetItemParent(to) : to;
// do all the checking for all selected items first (no movement yet, just checking!)
size_t count = [Link]();
for (size_t i = 0; i < count; i++)
const wxTreeItemId from = fromArray[i];
if (![Link]())
return false;
// if no data associated with it, disallow
FileTreeData* ftdFrom = (FileTreeData*)tree->GetItemData(from);
if (!ftdFrom)
return false;
// if not ours, disallow
if (ftdFrom->GetProject() != project)
return false;
// allow only if a file or vfolder was dragged on a file, another vfolder or the project itself
if ( (ftdFrom->GetKind() != FileTreeData::ftdkFile)
&& (ftdFrom->GetKind() != FileTreeData::ftdkVirtualFolder) )
return false;
// don't drag under the same parent
wxTreeItemId parentFrom = ftdFrom->GetKind() == FileTreeData::ftdkFile ? tree-
>GetItemParent(from) : from;
if (parentFrom == parentTo)
return false;
// A special check for virtual folders.
if ( (ftdFrom->GetKind() == FileTreeData::ftdkVirtualFolder)
|| (ftdTo->GetKind() == FileTreeData::ftdkVirtualFolder) )
wxTreeItemId root = tree->GetRootItem();
wxTreeItemId toParent = tree->GetItemParent(to);
while (toParent != root)
if (toParent == from)
return false;
toParent = tree->GetItemParent(toParent);
if (!TestProjectVirtualFolderDragged(project, tree, from, to))
return false;
return true;
static bool ProjectNodeDragged(cbProject* project, wxTreeCtrl* tree, wxArrayTreeItemIds& fromArray,
wxTreeItemId to)
if (!TestProjectNodeDragged(project, tree, fromArray, to))
return false;
FileTreeData* ftdTo = (FileTreeData*) tree->GetItemData(to);
if (!ftdTo)
return false;
wxTreeItemId parentTo = ftdTo->GetKind() == FileTreeData::ftdkFile ? tree->GetItemParent(to) : to;
size_t count = [Link]();
// now that we have successfully done the checking, do the moving
for (size_t i = 0; i < count; i++)
wxTreeItemId from = fromArray[i];
FileTreeData* ftdFrom = (FileTreeData*)tree->GetItemData(from);
// A special check for virtual folders.
if ( (ftdFrom->GetKind() == FileTreeData::ftdkVirtualFolder)
|| (ftdTo->GetKind() == FileTreeData::ftdkVirtualFolder) )
wxTreeItemId root = tree->GetRootItem();
wxTreeItemId toParent = tree->GetItemParent(to);
while (toParent != root)
if (toParent == from)
return false;
toParent = tree->GetItemParent(toParent);
}
if (!ProjectVirtualFolderDragged(project, tree, from, to))
return false;
// finally; make the move
ProjectCopyTreeNodeRecursively(tree, from, parentTo);
// remove old node
tree->Delete(from);
project->SetModified(true);
Manager::Get()->GetProjectManager()->GetUI().RebuildTree();
return true;
static bool ProjectHasVirtualFolder(const wxString &folderName, const wxArrayString &virtualFolders)
for (size_t i = 0; i < [Link](); ++i)
if (virtualFolders[i].StartsWith(folderName))
cbMessageBox(_("A virtual folder with the same name already exists."),
_("Error"), wxICON_WARNING);
return true;
return false;
static bool ProjectVirtualFolderAdded(cbProject* project, wxTreeCtrl* tree,
wxTreeItemId parent_node, const wxString& virtual_folder)
wxString foldername = GetRelativeFolderPath(tree, parent_node);
foldername << virtual_folder;
[Link](_T("/"), wxString(wxFILE_SEP_PATH), true);
[Link](_T("\\"), wxString(wxFILE_SEP_PATH), true);
if ([Link]() != wxFILE_SEP_PATH)
foldername << wxFILE_SEP_PATH;
const wxArrayString &virtualFolders = project->GetVirtualFolders();
if (ProjectHasVirtualFolder(foldername, virtualFolders))
return false;
project->AppendUniqueVirtualFolder(foldername);
FileTreeData* ftd = new FileTreeData(project, FileTreeData::ftdkVirtualFolder);
ftd->SetProjectFile(nullptr);
ftd->SetFolder(foldername);
int vfldIdx = cbProjectTreeImages::VirtualFolderIconIndex();
ProjectAddTreeNode(project, tree, foldername, project->GetProjectNode(), true,
FileTreeData::ftdkVirtualFolder, true, vfldIdx, ftd);
if (!tree->IsExpanded(parent_node))
tree->Expand(parent_node);
project->SetModified(true);
// Manager::Get()->GetLogManager()->DebugLog(wxString::Format("VirtualFolderAdded: %s: %s",
foldername, GetStringFromArray(m_VirtualFolders, ";")));
return true;
static void ProjectVirtualFolderDeleted(cbProject* project, wxTreeCtrl* tree, wxTreeItemId node)
// what item do we start dragging?
if (![Link]())
return;
// if no data associated with it, disallow
FileTreeData* ftd = (FileTreeData*)tree->GetItemData(node);
if (!ftd)
return;
// if not ours, disallow
if (ftd->GetProject() != project)
return;
wxString foldername = GetRelativeFolderPath(tree, node);
wxString parent_foldername = GetRelativeFolderPath(tree, tree->GetItemParent(node));
project->RemoveVirtualFolders(foldername);
if (!parent_foldername.IsEmpty())
project->AppendUniqueVirtualFolder(parent_foldername);
// Manager::Get()->GetLogManager()->DebugLog(wxString::Format("VirtualFolderDeleted: %s: %s",
foldername, GetStringFromArray(m_VirtualFolders, ";")));
static bool ProjectVirtualFolderRenamed(cbProject* project, wxTreeCtrl* tree, wxTreeItemId node,
const wxString& new_name)
if (new_name.empty())
return false;
if (new_name.find_first_of(";/\\") != std::string::npos)
cbMessageBox(_("A virtual folder name cannot contain these special characters: \";\", \"\\\"
or \"/\"."),
_("Error"), wxICON_WARNING);
return false;
// what item are we renaming?
if (![Link]())
return false;
// is it a different name?
const wxString old_name(tree->GetItemText(node));
if (old_name == new_name)
return false;
// if no data associated with it, disallow
FileTreeData* ftd = (FileTreeData*)tree->GetItemData(node);
if (!ftd)
return false;
// if not ours, disallow
if (ftd->GetProject() != project)
return false;
const wxString old_foldername(GetRelativeFolderPath(tree, node));
const wxString new_foldername(GetRelativeFolderPath(tree, tree->GetItemParent(node)) +
new_name + wxFILE_SEP_PATH);
if (ProjectHasVirtualFolder(new_foldername, project->GetVirtualFolders()))
return false;
project->ReplaceVirtualFolder(old_foldername, new_foldername);
// Manager::Get()->GetLogManager()->DebugLog(wxString::Format("VirtualFolderRenamed: %s to %s:
%s", old_foldername, new_foldername, GetStringFromArray(m_VirtualFolders, ";")));
return true;
/** Display the project options dialog.
* @return True if the dialog was closed with "OK", false if closed with "Cancel".
*/
static bool ProjectShowOptions(cbProject* project)
if (!project)
return false;
ProjectOptionsDlg dlg(Manager::Get()->GetAppWindow(), project);
PlaceWindow(&dlg);
if ([Link]() == wxID_OK)
// update file details
FilesList &filesList = project->GetFilesList();
for (FilesList::iterator it = [Link](); it != [Link](); ++it)
if (ProjectFile* pf = *it)
pf->UpdateFileDetails();
}
CodeBlocksEvent event(cbEVT_PROJECT_OPTIONS_CHANGED);
[Link](project);
Manager::Get()->ProcessEvent(event);
return true;
return false;
} // anonymous namespace
void ProjectManagerUI::BuildProjectTree(cbProject* project, cbTreeCtrl* tree,
const wxTreeItemId& root, int ptvs,
FilesGroupsAndMasks* fgam)
if (!tree)
return;
#ifdef fileload_measuring
wxStopWatch sw;
#endif
int fldIdx = cbProjectTreeImages::FolderIconIndex();
int vfldIdx = cbProjectTreeImages::VirtualFolderIconIndex();
bool read_only = (!wxFile::Access(project->GetFilename().c_str(), wxFile::write));
int prjIdx = cbProjectTreeImages::ProjectIconIndex(read_only);
tree->SetCompareFunction(ptvs);
// add our project's root item
FileTreeData* ftd = new FileTreeData(project, FileTreeData::ftdkProject);
project->SetProjectNode(tree->AppendItem(root, project->GetTitle(), prjIdx, prjIdx, ftd));
wxTreeItemId others, generated;
others = generated = project->GetProjectNode();
wxTreeItemId* pGroupNodes = nullptr; // file group nodes (if enabled)
// create file-type categories nodes (if enabled)
bool do_categorise = ((ptvs&ptvsCategorize) && fgam);
if (do_categorise)
// obtain all group nodes available from "file groups and masks"
pGroupNodes = new wxTreeItemId[fgam->GetGroupsCount()];
for (unsigned int i = 0; i < fgam->GetGroupsCount(); ++i)
ftd = new FileTreeData(project, FileTreeData::ftdkVirtualGroup);
ftd->SetFolder(fgam->GetGroupName(i));
pGroupNodes[i] = tree->AppendItem(project->GetProjectNode(), fgam->GetGroupName(i), fldIdx,
fldIdx, ftd);
// add a default category "Generated" for all auto-generated file types
ftd = new FileTreeData(project, FileTreeData::ftdkVirtualGroup);
generated = tree->AppendItem(project->GetProjectNode(), _("Auto-generated"), fldIdx, fldIdx, ftd);
// add a default category "Others" for all non-matching file-types
ftd = new FileTreeData(project, FileTreeData::ftdkVirtualGroup);
others = tree->AppendItem(project->GetProjectNode(), _("Others"), fldIdx, fldIdx, ftd);
// Now add any virtual folders
const wxArrayString& virtualFolders = project->GetVirtualFolders();
for (size_t i = 0; i < [Link](); ++i)
ftd = new FileTreeData(project, FileTreeData::ftdkVirtualFolder);
ftd->SetFolder(virtualFolders[i]);
ProjectAddTreeNode(project, tree, virtualFolders[i], project->GetProjectNode(), true,
FileTreeData::ftdkVirtualFolder, true, vfldIdx, ftd);
// iterate all project files and add them to the tree
int count = 0;
FilesList& fileList = project->GetFilesList();
for (FilesList::iterator it = [Link](); it != [Link](); ++it)
ProjectFile* pf = *it;
if (!pf)
Manager::Get()->GetLogManager()->DebugLogError(_T("Looks like the project's file list is
broken?!"));
continue;
}
ftd = new FileTreeData(project, FileTreeData::ftdkFile);
ftd->SetFileIndex(count++);
ftd->SetProjectFile(pf);
ftd->SetFolder(pf->[Link]());
wxString nodetext = pf->relativeToCommonTopLevelPath;
FileTreeData::FileTreeDataKind folders_kind = FileTreeData::ftdkFolder;
// by default, the parent node is the project node (in case of no grouping, no virtual folders)
wxTreeItemId parentNode = project->GetProjectNode();
// now change the parent node for virtual folders and/or if grouping is enabled
// first check, if the file is under a virtual folder
if (!pf->virtual_path.IsEmpty())
nodetext = pf->virtual_path + wxFILE_SEP_PATH + pf->[Link]();
folders_kind = FileTreeData::ftdkVirtualFolder;
wxString slash = pf->virtual_path.Last() == wxFILE_SEP_PATH ? _T("") :
wxString(wxFILE_SEP_PATH);
ftd->SetFolder(pf->virtual_path);
project->AppendUniqueVirtualFolder(pf->virtual_path + slash);
// second check, if files grouping is enabled and find the group parent
else if (do_categorise && pGroupNodes)
{
bool found = false;
// auto-generated files end up all together
if (pf->AutoGeneratedBy())
parentNode = generated;
found = true;
else // else try to match a group
const wxFileName fname(pf->relativeToCommonTopLevelPath);
const wxString &fnameFullname = [Link]();
for (unsigned int i = 0; i < fgam->GetGroupsCount(); ++i)
if (fgam->MatchesMask(fnameFullname, i))
parentNode = pGroupNodes[i];
found = true;
break;
}
// if not matched a group, put it in "Others" group
if (!found)
parentNode = others;
// probably remove the path of the entry (depending on settings)
if ( !(ptvs&ptvsUseFolders)
&& (ptvs&ptvsHideFolderName)
&& (folders_kind != FileTreeData::ftdkVirtualFolder) )
nodetext = pf->[Link]();
// add file in the tree
bool useFolders = (ptvs&ptvsUseFolders) || (folders_kind == FileTreeData::ftdkVirtualFolder);
pf->SetTreeItemId(ProjectAddTreeNode(project, tree, nodetext, parentNode, useFolders,
folders_kind,
pf->compile, (int)pf->GetFileState(), ftd));
}// iteration of project files
// finally remove empty tree nodes (like empty groups)
if (do_categorise && pGroupNodes)
for (unsigned int i = 0; i < fgam->GetGroupsCount(); ++i)
if (tree->GetChildrenCount(pGroupNodes[i], false) == 0)
tree->Delete(pGroupNodes[i]);
if (tree->GetChildrenCount(others, false) == 0)
tree->Delete(others);
if (tree->GetChildrenCount(generated, false) == 0)
tree->Delete(generated);
delete[] pGroupNodes;
ProjectTreeSortChildrenRecursive(tree, project->GetProjectNode());
tree->Expand(project->GetProjectNode());
#ifdef fileload_measuring
Manager::Get()->GetLogManager()->DebugLogError(wxString::Format("%s::%s:%d took: %ld ms",
cbC2U(__FILE__),cbC2U(__PRETTY_FUNCTION__), __LINE__, [Link]()));
#endif