/*
* This file is part of Code::Blocks Studio, an open-source cross-platform IDE
* Copyright (C) 2003  Yiannis An. Mandravellos
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
* Contact e-mail: Yiannis An. Mandravellos <mandrav@codeblocks.org>
* Program URL   : http://www.codeblocks.org
*
* $Id: projectmanager.cpp,v 1.76.2.1 2005/10/25 07:59:03 mandrav Exp $
* $Date: 2005/10/25 07:59:03 $
*/

#include "sdk_precomp.h"
#include <wx/imaglist.h>
#include <wx/menu.h>
#include <wx/utils.h>
#include <wx/dir.h>
#include <wx/textdlg.h>
#include <wx/splitter.h>
#include <wx/filename.h>
#include <wx/progdlg.h>

#include "projectmanager.h" // class's header file
#include "sdk_events.h"
#include "manager.h"
#include "configmanager.h"
#include "cbproject.h"
#include "incrementalselectlistdlg.h"
#include "messagemanager.h"
#include "pluginmanager.h"
#include "editormanager.h"
#include "workspaceloader.h"
#include "filegroupsandmasks.h"
#include "projectsfilemasksdlg.h"
#include "managerproxy.h"
#include "multiselectdlg.h"
#include "cbworkspace.h"
#include "xtra_classes.h"

// static
bool ProjectManager::s_CanShutdown = true;

ProjectManager* ProjectManager::Get(wxNotebook* parent)
{
    if(Manager::isappShuttingDown()) // The mother of all sanity checks
        ProjectManager::Free();
    else if (!ProjectManagerProxy::Get())
	{
		ProjectManagerProxy::Set(new ProjectManager(parent));
		Manager::Get()->GetMessageManager()->Log(_("ProjectManager initialized"));
	}
    return ProjectManagerProxy::Get();
}

void ProjectManager::Free()
{
	if (ProjectManagerProxy::Get())
	{
		delete ProjectManagerProxy::Get();
		ProjectManagerProxy::Set( 0L );
	}
}

int ID_ProjectManager = wxNewId();
int idMenuSetActiveProject = wxNewId();
int idMenuOpenFile = wxNewId();
int idMenuCloseProject = wxNewId();
int idMenuCloseFile = wxNewId();
int idMenuAddFilePopup = wxNewId();
int idMenuAddFilesRecursivelyPopup = wxNewId();
int idMenuAddFile = wxNewId();
int idMenuAddFilesRecursively = wxNewId();
int idMenuRemoveFilePopup = wxNewId();
int idMenuRemoveFile = wxNewId();
int idMenuProjectProperties = wxNewId();
int idMenuFileProperties = wxNewId();
int idMenuTreeProjectProperties = wxNewId();
int idMenuTreeFileProperties = wxNewId();
int idMenuGotoFile = wxNewId();
int idMenuExecParams = wxNewId();
int idMenuViewCategorize = wxNewId();
int idMenuViewUseFolders = wxNewId();
int idMenuViewFileMasks = wxNewId();
int idMenuNextProject = wxNewId();
int idMenuPriorProject = wxNewId();
int idMenuProjectTreeProps = wxNewId();
int idMenuProjectUp = wxNewId();
int idMenuProjectDown = wxNewId();
int idMenuViewCategorizePopup = wxNewId();
int idMenuViewUseFoldersPopup = wxNewId();
int idMenuTreeRenameWorkspace = wxNewId();
// TODO (mandrav#1#): Add "save workspace" context menu entry

#ifndef __WXMSW__
/*
    Under wxGTK, I have noticed that wxTreeCtrl is not sending a EVT_COMMAND_RIGHT_CLICK
    event when right-clicking on the client area.
    This is a "proxy" wxTreeCtrl descendant that handles this for us...
*/

class PrjTree : public wxTreeCtrl
{
	public:
		PrjTree(wxWindow* parent, int id) : wxTreeCtrl(parent, id) {}
	protected:
		void OnRightClick(wxMouseEvent& event)
		{
            if(!this) return;
		    //Manager::Get()->GetMessageManager()->DebugLog("OnRightClick");
		    int flags;
		    HitTest(wxPoint(event.GetX(), event.GetY()), flags);
		    if (flags & (wxTREE_HITTEST_ABOVE | wxTREE_HITTEST_BELOW | wxTREE_HITTEST_NOWHERE))
		    {
		    	// "proxy" the call
			    wxCommandEvent e(wxEVT_COMMAND_RIGHT_CLICK, ID_ProjectManager);
				wxPostEvent(GetParent(), e);
			}
			else
		    	event.Skip();
		}
		DECLARE_EVENT_TABLE();
};
BEGIN_EVENT_TABLE(PrjTree, wxTreeCtrl)
	EVT_RIGHT_DOWN(PrjTree::OnRightClick)
END_EVENT_TABLE()
#endif // !__WXMSW__


BEGIN_EVENT_TABLE(ProjectManager, wxEvtHandler)
    EVT_TREE_ITEM_ACTIVATED(ID_ProjectManager, ProjectManager::OnProjectFileActivated)
    EVT_TREE_ITEM_RIGHT_CLICK(ID_ProjectManager, ProjectManager::OnTreeItemRightClick)
    EVT_COMMAND_RIGHT_CLICK(ID_ProjectManager, ProjectManager::OnRightClick)
    EVT_MENU(idMenuSetActiveProject, ProjectManager::OnSetActiveProject)
    EVT_MENU(idMenuNextProject, ProjectManager::OnSetActiveProject)
    EVT_MENU(idMenuPriorProject, ProjectManager::OnSetActiveProject)
    EVT_MENU(idMenuProjectUp, ProjectManager::OnSetActiveProject)
    EVT_MENU(idMenuProjectDown, ProjectManager::OnSetActiveProject)
    EVT_MENU(idMenuTreeRenameWorkspace, ProjectManager::OnRenameWorkspace)
    EVT_MENU(idMenuAddFile, ProjectManager::OnAddFileToProject)
    EVT_MENU(idMenuAddFilesRecursively, ProjectManager::OnAddFilesToProjectRecursively)
    EVT_MENU(idMenuRemoveFile, ProjectManager::OnRemoveFileFromProject)
    EVT_MENU(idMenuAddFilePopup, ProjectManager::OnAddFileToProject)
    EVT_MENU(idMenuAddFilesRecursivelyPopup, ProjectManager::OnAddFilesToProjectRecursively)
    EVT_MENU(idMenuRemoveFilePopup, ProjectManager::OnRemoveFileFromProject)
    EVT_MENU(idMenuCloseProject, ProjectManager::OnCloseProject)
    EVT_MENU(idMenuCloseFile, ProjectManager::OnCloseFile)
    EVT_MENU(idMenuOpenFile, ProjectManager::OnOpenFile)
    EVT_MENU(idMenuProjectProperties, ProjectManager::OnProperties)
    EVT_MENU(idMenuFileProperties, ProjectManager::OnProperties)
    EVT_MENU(idMenuTreeProjectProperties, ProjectManager::OnProperties)
    EVT_MENU(idMenuTreeFileProperties, ProjectManager::OnProperties)
	EVT_MENU(idMenuGotoFile, ProjectManager::OnGotoFile)
    EVT_MENU(idMenuExecParams, ProjectManager::OnExecParameters)
    EVT_MENU(idMenuViewCategorize, ProjectManager::OnViewCategorize)
    EVT_MENU(idMenuViewCategorizePopup, ProjectManager::OnViewCategorize)
    EVT_MENU(idMenuViewUseFolders, ProjectManager::OnViewUseFolders)
    EVT_MENU(idMenuViewUseFoldersPopup, ProjectManager::OnViewUseFolders)
    EVT_MENU(idMenuViewFileMasks, ProjectManager::OnViewFileMasks)
    EVT_IDLE(ProjectManager::OnIdle)
END_EVENT_TABLE()

// class constructor
ProjectManager::ProjectManager(wxNotebook* parent)
	: m_pTree(0),
    m_pPanel(0),
    m_pWorkspace(0),
	m_pTopEditor(0),
    m_TreeCategorize(false),
    m_TreeUseFolders(true),
    m_TreeFreezeCounter(0),
    m_IsLoadingProject(false),
	m_IsLoadingWorkspace(false),
	m_InitialDir(_T(""))
{
    SC_CONSTRUCTOR_BEGIN
    m_InitialDir=wxFileName::GetCwd();
    m_pParent = parent;
    m_pActiveProject = 0L;
    m_pProjects = new ProjectsArray;
    m_pProjects->Clear();
	// m_pPanel = new wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxCLIP_CHILDREN);
    ProjectManagerProxy::Set(this);
    InitPane();

	m_pFileGroups = new FilesGroupsAndMasks;
	m_TreeCategorize = ConfigManager::Get()->Read(_T("/project_manager/categorize_tree"), 1);
	m_TreeUseFolders = ConfigManager::Get()->Read(_T("/project_manager/use_folders"), 1);

    RebuildTree();

	ConfigManager::AddConfiguration(_("Project Manager"), _T("/project_manager"));

	// 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);
}

void ProjectManager::InitPane()
{
    SANITY_CHECK();
    if(Manager::isappShuttingDown())
        return;
    if(m_pTree)
        return;
    wxSplitPanel* mypanel = (wxSplitPanel*)(Manager::Get()->GetNotebookPage(_("Projects"),wxTAB_TRAVERSAL | wxCLIP_CHILDREN,true));
    mypanel->SetConfigEntryForSplitter(_T("/editor/opened_files_tree_height"));
    m_pPanel = mypanel;
    wxSplitterWindow* mysplitter = mypanel->GetSplitter();
    BuildTree(mysplitter);
    mypanel->SetAutoLayout(true);
    mypanel->RefreshSplitter(ID_EditorManager,ID_ProjectManager);
}

void ProjectManager::BuildTree(wxWindow* parent)
{
    #ifndef __WXMSW__
        m_pTree = new PrjTree(parent, ID_ProjectManager);
    #else
        m_pTree = new wxTreeCtrl(parent, ID_ProjectManager);
    #endif

    wxBitmap bmp;
    m_pImages = new wxImageList(16, 16);
    wxString prefix = ConfigManager::Get()->Read(_T("data_path")) + _T("/images/");
    bmp.LoadFile(prefix + _T("gohome.png"), wxBITMAP_TYPE_PNG); // workspace
    m_pImages->Add(bmp);
    bmp.LoadFile(prefix + _T("codeblocks.png"), wxBITMAP_TYPE_PNG); // project
    m_pImages->Add(bmp);
    bmp.LoadFile(prefix + _T("ascii.png"), wxBITMAP_TYPE_PNG); // file
    m_pImages->Add(bmp);
    bmp.LoadFile(prefix + _T("folder_open.png"), wxBITMAP_TYPE_PNG); // folder
    m_pImages->Add(bmp);
    m_pTree->SetImageList(m_pImages);

    // make sure tree is not "frozen"
    UnfreezeTree(true);
}
// class destructor
ProjectManager::~ProjectManager()
{
    SC_DESTRUCTOR_BEGIN

    // this is a core manager, so it is removed when the app is shutting down.
    // in this case, the app has already un-hooked us, so no need to do it ourselves...
//	Manager::Get()->GetAppWindow()->RemoveEventHandler(this);

    if (m_pWorkspace)
        delete m_pWorkspace;
    m_pWorkspace = 0;

    int count = m_pProjects->GetCount();
    for (int i = 0; i < count; ++i)
    {
        cbProject* project = m_pProjects->Item(i);
        if (project)
            delete project;
    }
    m_pProjects->Clear();

    delete m_pProjects;m_pProjects = 0;
    delete m_pImages;m_pImages = 0;
	delete m_pFileGroups;m_pFileGroups = 0;

	delete m_pTree; m_pTree = 0;

	SC_DESTRUCTOR_END
}

void ProjectManager::CreateMenu(wxMenuBar* menuBar)
{
/* TODO (mandrav#1#): Move menu items from main.cpp, 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)
        {
            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(idMenuRemoveFile, _("Remove files..."), _("Remove files from the project"));

/* FIXME (mandrav#1#): Move this submenu creation in a function.
It is duplicated in ShowMenu() */
            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(idMenuViewCategorize, _("Categorize by file types"));
            treeprops->AppendCheckItem(idMenuViewUseFolders, _("Display folders as on disk"));
            treeprops->Check(idMenuViewCategorize, ConfigManager::Get()->Read(_T("/project_manager/categorize_tree"), 1));
            treeprops->Check(idMenuViewUseFolders, ConfigManager::Get()->Read(_T("/project_manager/use_folders"), 1));
            treeprops->Append(idMenuViewFileMasks, _("Edit file types && categories..."));
            menu->AppendSeparator();
            menu->Append(idMenuProjectTreeProps, _("Project tree"), treeprops);

            menu->Append(idMenuExecParams, _("Set &programs' arguments..."), _("Set execution parameters for the targets of this project"));
            menu->Append(idMenuProjectProperties, _("Properties"));
        }
	}
}

void ProjectManager::ReleaseMenu(wxMenuBar* menuBar)
{
    SANITY_CHECK();
}

void ProjectManager::SetProject(cbProject* project, bool refresh)
{
    SANITY_CHECK();
	if (project != m_pActiveProject)
	{
        if (m_pWorkspace)
            m_pWorkspace->SetModified(true);
    }
    if (m_pActiveProject)
        m_pTree->SetItemBold(m_pActiveProject->GetProjectNode(), false);
    m_pActiveProject = project;
    if (m_pActiveProject)
        m_pTree->SetItemBold(m_pActiveProject->GetProjectNode(), true);
	if (refresh)
		RebuildTree();

    if (m_pActiveProject)
        m_pTree->EnsureVisible(m_pActiveProject->GetProjectNode());

	CodeBlocksEvent event(cbEVT_PROJECT_ACTIVATE);
	event.SetProject(m_pActiveProject);
	Manager::Get()->GetPluginManager()->NotifyPlugins(event);
}

void ProjectManager::ShowMenu(wxTreeItemId id, const wxPoint& pt)
{
    SANITY_CHECK();
    if ( !id.IsOk() )
        return;

	wxString caption;
    wxMenu menu;

    FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(id);
    // if it is not the workspace, add some more options
    if (ftd && id != m_TreeRoot)
    {
	    // if it is a project...
    	if (ftd->GetFileIndex() == -1)
        {
			if (ftd->GetProject() != m_pActiveProject)
				menu.Append(idMenuSetActiveProject, _("Activate project"));
    		menu.Append(idMenuCloseProject, _("Close project"));
    		menu.AppendSeparator();
    	    menu.Append(idMenuAddFilePopup, _("Add files..."));
    	    menu.Append(idMenuAddFilesRecursivelyPopup, _("Add files recursively..."));
            menu.Append(idMenuRemoveFile, _("Remove files..."));
        }
        // if it is a file...
        else
        {
			// selected project file
			int idx = ftd->GetFileIndex();
			ProjectFile* pf = ftd->GetProject()->GetFile(idx);
			// is it already open in the editor?
            EditorBase* ed = Manager::Get()->GetEditorManager()->IsOpen(pf->file.GetFullPath());

			if (ed)
			{
				// is it already active?
				bool active = Manager::Get()->GetEditorManager()->GetActiveEditor() == ed;

				if (!active)
				{
					caption.Printf(_("Switch to %s"), m_pTree->GetItemText(id).c_str());
					menu.Append(idMenuOpenFile, caption);
				}
				caption.Printf(_("Close %s"), m_pTree->GetItemText(id).c_str());
				menu.Append(idMenuCloseFile, caption);
			}
			else
			{
				caption.Printf(_("Open %s"), m_pTree->GetItemText(id).c_str());
				menu.Append(idMenuOpenFile, caption);
			}
    		menu.AppendSeparator();
    	    menu.Append(idMenuRemoveFilePopup, _("Remove file from project"));
        }

        // ask any plugins to add items in this menu
        Manager::Get()->GetPluginManager()->AskPluginsForModuleMenu(mtProjectManager, &menu, m_pTree->GetItemText(id));

        menu.AppendSeparator();
        if (ftd->GetFileIndex() == -1)
        {
            // project
/* FIXME (mandrav#1#): Move this submenu creation in a function.
It is duplicated in CreateMenu() */
            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();
/* NOTE (mandrav#1#): If this is moved in a new function,\
it differs from the block currently in CreateMenu() by the following two IDs */
            treeprops->AppendCheckItem(idMenuViewCategorizePopup, _("Categorize by file types"));
            treeprops->AppendCheckItem(idMenuViewUseFoldersPopup, _("Display folders as on disk"));
            treeprops->Check(idMenuViewCategorizePopup, ConfigManager::Get()->Read(_T("/project_manager/categorize_tree"), 1));
            treeprops->Check(idMenuViewUseFoldersPopup, ConfigManager::Get()->Read(_T("/project_manager/use_folders"), 1));
            treeprops->Append(idMenuViewFileMasks, _("Edit file types && categories..."));

            menu.Append(idMenuProjectTreeProps, _("Project tree"), treeprops);
            menu.Append(idMenuTreeProjectProperties, _("Properties"));
        }
        else
            menu.Append(idMenuTreeFileProperties, _("Properties"));
    }
    else if (id == m_TreeRoot)
    {
        menu.Append(idMenuTreeRenameWorkspace, _("Rename workspace"));
    }

    if (menu.GetMenuItemCount() != 0)
        m_pTree->PopupMenu(&menu, pt);
}

cbProject* ProjectManager::IsOpen(const wxString& filename)
{
    SANITY_CHECK(0L);
	if (filename.IsEmpty())
		return 0L;
    int count = m_pProjects->GetCount();
    for (int i = 0; i < count; ++i)
    {
        cbProject* project = m_pProjects->Item(i);
        if(project)
        {
#ifdef __WXMSW__
            // MS Windows Filenames are case-insensitive, we have to
            // avoid opening the same project if the files are only
            // different in upper/lowercase.
            if(project->GetFilename().Lower().Matches(filename.Lower()))
                return project;
#else
            if (project->GetFilename().Matches(filename))
                return project;
#endif
        }
    }
	return 0L;
}

wxMenu* ProjectManager::GetProjectMenu()
{
    SANITY_CHECK(0L);
    wxMenu* result = 0L;
    do
    {
        wxFrame* frame = Manager::Get()->GetAppWindow();
        if(!frame)
            break;
        wxMenuBar* mb = frame->GetMenuBar();
        if(!mb)
            break;
        result = mb->GetMenu(mb->FindMenu(_("&Project")));
        break;
    }while(false);
    return result;
}

cbProject* ProjectManager::LoadProject(const wxString& filename)
{
    SANITY_CHECK(0L);
    cbProject* result = 0;

    // disallow application shutdown while opening files
    // WARNING: remember to set it to true, when exiting this function!!!
    s_CanShutdown = false;
	cbProject* project = IsOpen(filename);

	// "Try" block (loop which only gets executed once)
	// These blocks are extremely useful in constructs that need
	// premature exits. Instead of having multiple return points,
	// multiple return values and/or gotos,
	// you just break out of the loop (which only gets executed once) to exit.
	do
	{
        if (project)
        {
            if (m_pWorkspace)
                m_pWorkspace->SetModified(true);
            // we 're done
            result = project;
            break;
        }
        m_IsLoadingProject=true;
        project = new cbProject(filename);
        if(!sanity_check())
            break; // sanity check
        // We need to do this because creating cbProject allows the app to be
        // closed in the middle of the operation. So the class destructor gets
        // called in the middle of a method call.

        if (!project->IsLoaded())
        {
            delete project;
            break;
        }

        if(!sanity_check())
            break; // sanity check

        m_pProjects->Add(project);

        // to avoid tree flickering, we 'll manually call here the project's BuildTree
        // but before we do it, remove bold from current active project (if any)
        if (m_pActiveProject)
            m_pTree->SetItemBold(m_pActiveProject->GetProjectNode(), false);
        // ok, set the new project
        SetProject(project, false);
        project->BuildTree(m_pTree, m_TreeRoot, m_TreeCategorize, m_TreeUseFolders, m_pFileGroups);
        m_pTree->Expand(project->GetProjectNode());
        m_pTree->SetItemBold(project->GetProjectNode(), true);
        m_pTree->Expand(m_TreeRoot); // make sure the root node is open

        if(!sanity_check())
            break; // sanity check

        project->LoadLayout();
        if(!sanity_check())
            break; // sanity check
        if (m_pWorkspace)
            m_pWorkspace->SetModified(true);
        // we 're done

        result = project;
        break;
    }while(false);
    // we 're done

    m_IsLoadingProject=false;

    #ifdef USE_OPENFILES_TREE
    Manager::Get()->GetEditorManager()->RebuildOpenedFilesTree();
    #endif
	// Restore child windows' display
	// if(mywin)
    //    mywin->Show();

    s_CanShutdown = true;
    return result;
}

cbProject* ProjectManager::NewProject(const wxString& filename)
{
    return LoadProject(filename);
}

bool ProjectManager::QueryCloseAllProjects()
{
    SANITY_CHECK(true);
    unsigned int i;

    if (!Manager::Get()->GetEditorManager()->QueryCloseAll())
        return false;

    for(i=0;i<m_pProjects->GetCount();i++)
    {
        // Ask for saving modified projects. However,
        // we already asked to save projects' files;
        // do not ask again
        if(!QueryCloseProject(m_pProjects->Item(i),true))
            return false;
    }
    return true;
}

bool ProjectManager::QueryCloseProject(cbProject *proj,bool dontsavefiles)
{
    SANITY_CHECK(true);
    if(!proj)
        return true;
    if(!dontsavefiles)
        if(!proj->QueryCloseAllFiles())
            return false;
    if (proj->GetModified())
    {
        wxString msg;
        msg.Printf(_("Project '%s' is modified...\nDo you want to save the changes?"), proj->GetTitle().c_str());
        switch (wxMessageBox(msg, _("Save project"), wxICON_QUESTION | wxYES_NO | wxCANCEL))
        {
            case wxYES:     if (!proj->Save()) return false;
            case wxNO:      break;
            case wxCANCEL:  return false;
        }
    }
    return true;
}

bool ProjectManager::CloseAllProjects(bool dontsave)
{
    SANITY_CHECK(true);
    if(!dontsave)
        if(!QueryCloseAllProjects())
            return false;
    FreezeTree();
    while (m_pProjects->GetCount() != 0)
    {
        if (!CloseActiveProject(true))
        {
            UnfreezeTree(true);
            return false;
        }
    }
    RebuildTree();
    UnfreezeTree(true);
    if(!m_InitialDir.IsEmpty())
        wxFileName::SetCwd(m_InitialDir);
    return true;
}

bool ProjectManager::CloseProject(cbProject* project,bool dontsave)
{
    SANITY_CHECK(true);
    if (!project)
        return true;
    if(!dontsave)
         if(!QueryCloseProject(project))
            return false;

    cbProject* tmp = 0L;
	bool same = (project == m_pActiveProject);

    if (!same)
    {
	    tmp = m_pActiveProject;
    	SetProject(project);
    }

    // The project and its files should have been saved by now.
    // It's safe to not save files now.
    bool ret = CloseActiveProject(true);

    if (!same)
	    SetProject(tmp);
    if(!m_InitialDir.IsEmpty()) // Restore the working directory
        wxFileName::SetCwd(m_InitialDir);
    return ret;
}

bool ProjectManager::CloseActiveProject(bool dontsave)
{
    SANITY_CHECK(false);
    if (!m_pActiveProject)
        return true;
    if(m_sanitycheck_shutdown) // if shutdown, don't ask.
        dontsave=true;
    if(!dontsave)
         if(!QueryCloseProject(m_pActiveProject))
            return false;
    int index = m_pProjects->Index(m_pActiveProject);
    if (index == wxNOT_FOUND)
        return false;
	Manager::Get()->GetEditorManager()->UpdateProjectFiles(m_pActiveProject);
	m_pActiveProject->SaveTreeState(m_pTree);
    m_pActiveProject->SaveLayout();

    if (m_pWorkspace)
        m_pWorkspace->SetModified(true);

    m_pActiveProject->CloseAllFiles(true);
    m_pProjects->Remove(m_pActiveProject);
    delete m_pActiveProject;
	m_pActiveProject = 0L;
    if (m_pProjects->GetCount() > 0)
        SetProject(m_pProjects->Item(0));
    else
        SetProject(0L);

    return true;
}

bool ProjectManager::SaveProject(cbProject* project)
{
    SANITY_CHECK(false);

    if (!project)
        return false;
    if (project->Save())
    {
        RebuildTree();
        return true;
    }
    return false;
}

bool ProjectManager::SaveProjectAs(cbProject* project)
{
    SANITY_CHECK(false);
    if (!project)
        return false;

    if (project->SaveAs())
    {
        RebuildTree();
        return true;
    }
    return false;
}

bool ProjectManager::SaveActiveProject()
{
    SANITY_CHECK(false);
    return SaveProject(m_pActiveProject);
}

bool ProjectManager::SaveActiveProjectAs()
{
    SANITY_CHECK(false);
    return SaveProjectAs(m_pActiveProject);
}

bool ProjectManager::SaveAllProjects()
{
    SANITY_CHECK(false);
    FreezeTree();
    int prjCount = m_pProjects->GetCount();
    int count = 0;
    for (int i = 0; i < prjCount; ++i)
    {
        cbProject* project = m_pProjects->Item(i);
        if (SaveProject(project))
            ++count;
    }
    UnfreezeTree(true);
    return count == prjCount;
}

void ProjectManager::MoveProjectUp(cbProject* project, bool warpAround)
{
    SANITY_CHECK();
    if (!project)
        return;

    int idx = m_pProjects->Index(project);
    if (idx == wxNOT_FOUND)
        return; // project not opened in project manager???

    if (idx == 0)
    {
         if (!warpAround)
            return;
        else
            idx = m_pProjects->Count();
    }
    m_pProjects->RemoveAt(idx--);
    m_pProjects->Insert(project, idx);
    RebuildTree();
    if (m_pWorkspace)
        m_pWorkspace->SetModified(true);
}

void ProjectManager::MoveProjectDown(cbProject* project, bool warpAround)
{
    SANITY_CHECK();
    if (!project)
        return;

    int idx = m_pProjects->Index(project);
    if (idx == wxNOT_FOUND)
        return; // project not opened in project manager???

    if (idx == (int)m_pProjects->Count() - 1)
    {
         if (!warpAround)
            return;
        else
            idx = 0;
    }
    m_pProjects->RemoveAt(idx++);
    m_pProjects->Insert(project, idx);
    RebuildTree();
    if (m_pWorkspace)
        m_pWorkspace->SetModified(true);
}

cbWorkspace* ProjectManager::GetWorkspace()
{
    SANITY_CHECK(0L);
    if (!m_pWorkspace)
    {
        m_pWorkspace = new cbWorkspace(_T(""));
        m_pWorkspace->SetTitle(_("Workspace"));
        m_pWorkspace->SetModified(false);
    }
    return m_pWorkspace;
}

void ProjectManager::SetTopEditor(EditorBase* ed)
{
    SANITY_CHECK();
    m_pTopEditor = ed;
}

EditorBase* ProjectManager::GetTopEditor()
{
    SANITY_CHECK(0);
    return m_pTopEditor;
}

bool ProjectManager::LoadWorkspace(const wxString& filename)
{
    SANITY_CHECK(false);
    m_pTopEditor = 0;
    if (!CloseWorkspace())
        return false; // didn't close
    m_IsLoadingWorkspace=true;
    m_pWorkspace = new cbWorkspace(filename);
    m_IsLoadingWorkspace=false;
    Manager::Get()->GetEditorManager()->RebuildOpenedFilesTree();
    SANITY_CHECK(false);
    m_pTree->SetItemText(m_TreeRoot, m_pWorkspace->GetTitle());
    if(m_pTopEditor)
        m_pTopEditor->Activate();
    Manager::Get()->GetEditorManager()->RefreshOpenedFilesTree(true);
    UnfreezeTree(true);
    return m_pWorkspace->IsOK();
}

bool ProjectManager::SaveWorkspace()
{
    return GetWorkspace()->Save();
}

bool ProjectManager::SaveWorkspaceAs(const wxString& filename)
{
    return GetWorkspace()->SaveAs(filename);
}

bool ProjectManager::QueryCloseWorkspace()
{
    SANITY_CHECK(true);
    if(!m_pWorkspace)
        return true;

    // don't ask to save the default workspace, if blank workspace is used on app startup
    if (m_pWorkspace->IsDefault() && ConfigManager::Get()->Read(_T("/environment/blank_workspace"), 0L) == 1)
        return true;

    if (m_pWorkspace->GetModified())
    {
        // workspace needs save
        wxString msg;
        msg.Printf(_("Workspace '%s' is modified. Do you want to save it?"), m_pWorkspace->GetTitle().c_str());
        switch (wxMessageBox(msg,
                        _("Save workspace"),
                        wxYES_NO | wxCANCEL | wxICON_QUESTION))
        {
            case wxYES: SaveWorkspace(); break;
            case wxCANCEL: return false;
            default: break;
        }
    }
    if(!QueryCloseAllProjects())
        return false;
    return true;
}

bool ProjectManager::CloseWorkspace()
{
    SANITY_CHECK(false);
    if (m_pWorkspace)
    {
        if(!QueryCloseWorkspace())
            return false;
        if(!CloseAllProjects(true))
            return false;

        delete m_pWorkspace;
        m_pWorkspace = 0;
    }
    else
        return CloseAllProjects(false);
    return true;
}

bool ProjectManager::IsLoading()
{
    SANITY_CHECK(false);
    return (m_IsLoadingProject || m_IsLoadingWorkspace);
}

void ProjectManager::FreezeTree()
{
    SANITY_CHECK();
    if (!m_pTree)
        return;
// wx 2.5.x implement nested Freeze()/Thaw() calls correctly
#if !wxCHECK_VERSION(2,5,0)
    ++m_TreeFreezeCounter;
#endif
    m_pTree->Freeze();
}

void ProjectManager::UnfreezeTree(bool force)
{
    SANITY_CHECK();
    if (!m_pTree)
        return;
// wx 2.5.x implement nested Freeze()/Thaw() calls correctly
#if !wxCHECK_VERSION(2,5,0)
    --m_TreeFreezeCounter;
    if (force || m_TreeFreezeCounter <= 0)
    {
        m_pTree->Thaw();
        m_TreeFreezeCounter = 0;
    }
#else
    m_pTree->Thaw();
#endif
}

void ProjectManager::RebuildTree()
{
    SANITY_CHECK();
    FreezeTree();
    int count = m_pProjects->GetCount();
    for (int i = 0; i < count; ++i)
    {
        cbProject* project = m_pProjects->Item(i);
        if (project)
            project->SaveTreeState(m_pTree);
    }
    m_pTree->DeleteAllItems();
    wxString title=_T("");
    if(m_pWorkspace)
        title=m_pWorkspace->GetTitle();
     if(title==_T(""))
         title=_("Workspace");
    m_TreeRoot = m_pTree->AddRoot(title, 0, 0);
    for (int i = 0; i < count; ++i)
    {
        cbProject* project = m_pProjects->Item(i);
        if (project)
        {
            project->BuildTree(m_pTree, m_TreeRoot, m_TreeCategorize, m_TreeUseFolders, m_pFileGroups);
            m_pTree->SetItemBold(project->GetProjectNode(), project == m_pActiveProject);
        }
    }
    m_pTree->Expand(m_TreeRoot);

    for (int i = 0; i < count; ++i)
    {
        cbProject* project = m_pProjects->Item(i);
        if (project)
            project->RestoreTreeState(m_pTree);
    }
    UnfreezeTree();
}

int ProjectManager::DoAddFileToProject(const wxString& filename, cbProject* project, wxArrayInt& targets)
{
    SANITY_CHECK(0);
	if (!project)
		return 0;

	// do we have to ask for target?
	if (targets.GetCount() == 0)
	{
		// if project has only one target, use this
		if (project->GetBuildTargetsCount() == 1)
			targets.Add(0);
		// else display multiple target selection dialog
		else
		{
			targets = AskForMultiBuildTargetIndex(project);
			if (targets.GetCount() == 0)
				return 0;
		}
	}

    // add the file to the first selected target
    SANITY_CHECK(0);
    ProjectFile* pf = project->AddFile(targets[0], filename);
    if (pf)
    {
        // if the file was added succesfully,
        // add to this file the rest of the selected targets...
        for (size_t i = 0; i < targets.GetCount(); ++i)
        {
            ProjectBuildTarget* target = project->GetBuildTarget(targets[i]);
            if (target)
                pf->AddBuildTarget(target->GetTitle());
        }
    }
    return targets.GetCount();
}

int ProjectManager::AddFileToProject(const wxString& filename, cbProject* project, int target)
{
    SANITY_CHECK(-1);

	if (!project)
		project = GetActiveProject();

    wxArrayInt targets;
    targets.Add(target);
    if (AddFileToProject(filename, project, targets) == 1)
        return targets[0];
    return -1;
}

int ProjectManager::AddFileToProject(const wxString& filename, cbProject* project, wxArrayInt& targets)
{
    SANITY_CHECK(0);

	if (!project)
		project = GetActiveProject();

    int ret = DoAddFileToProject(filename, project, targets);
    if (ret > 0)
    {
        CodeBlocksEvent event(cbEVT_PROJECT_FILE_ADDED);
        event.SetProject(project);
        event.SetString(filename);
        Manager::Get()->GetPluginManager()->NotifyPlugins(event);
    }
	return ret;
}

int ProjectManager::AddMultipleFilesToProject(const wxArrayString& filelist, cbProject* project, int target)
{
    SANITY_CHECK(-1);

	if (!project)
		project = GetActiveProject();

    wxArrayInt targets;
    targets.Add(target);
    if (AddMultipleFilesToProject(filelist, project, targets) == 1)
        return targets[0];
    return -1;
}

int ProjectManager::AddMultipleFilesToProject(const wxArrayString& filelist, cbProject* project, wxArrayInt& targets)
{
    SANITY_CHECK(0);

    wxProgressDialog progress(_("Project Manager"), _("Please wait while adding files to project..."), filelist.GetCount());

	if (!project)
		project = GetActiveProject();

    wxArrayString addedFiles; // to know which files were added succesfully
    for (unsigned int i = 0; i < filelist.GetCount(); ++i)
    {
    	if (DoAddFileToProject(filelist[i], project, targets) != 0)
            addedFiles.Add(filelist[i]);
        progress.Update(i);
    }

    if (addedFiles.GetCount() != 0)
    {
        for (unsigned int i = 0; i < addedFiles.GetCount(); ++i)
        {
            CodeBlocksEvent event(cbEVT_PROJECT_FILE_ADDED);
            event.SetProject(project);
            event.SetString(addedFiles[i]);
            Manager::Get()->GetPluginManager()->NotifyPlugins(event);
        }
    }
	return targets.GetCount();
}

int ProjectManager::AskForBuildTargetIndex(cbProject* project)
{
    SANITY_CHECK(-1);
	cbProject* prj = project;
	if (!prj)
		prj = GetActiveProject();
	if (!prj)
		return -1;

	// ask for target
	wxArrayString array;
	int count = prj->GetBuildTargetsCount();
	for (int i = 0; i < count; ++i)
		array.Add(prj->GetBuildTarget(i)->GetTitle());
	int target = wxGetSingleChoiceIndex(_("Select the target:"), _("Project targets"), array);

	return target;
}

wxArrayInt ProjectManager::AskForMultiBuildTargetIndex(cbProject* project)
{
    wxArrayInt indices;
    SANITY_CHECK(indices);
	cbProject* prj = project;
	if (!prj)
		prj = GetActiveProject();
	if (!prj)
		return indices;

	// ask for target
	wxArrayString array;
	int count = prj->GetBuildTargetsCount();
	for (int i = 0; i < count; ++i)
		array.Add(prj->GetBuildTarget(i)->GetTitle());

    MultiSelectDlg dlg(0, array, false, _("Select the targets this file should belong to:"));
    if (dlg.ShowModal() == wxID_OK)
        indices = dlg.GetSelectedIndices();

	return indices;
}

void ProjectManager::DoOpenFile(ProjectFile* pf, const wxString& filename)
{
    SANITY_CHECK();
	FileType ft = FileTypeOf(filename);

	if (ft == ftHeader ||
		ft == ftSource)
	{
        // C/C++ header/source files, always get opened inside Code::Blocks
        cbEditor* ed = Manager::Get()->GetEditorManager()->Open(filename);
        if (ed)
        {
            ed->SetProjectFile(pf);
            ed->Show(true);

        }
        else
        {
            wxString msg;
            msg.Printf(_("Failed to open '%s'."), filename.c_str());
            Manager::Get()->GetMessageManager()->DebugLogError(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 || plugin->OpenFile(filename) != 0)
		{
            wxString msg;
            msg.Printf(_("Could not open file '%s'.\nNo handler registered for this type of file."), filename.c_str());
            Manager::Get()->GetMessageManager()->DebugLogError(msg);
		}
	}
}

void ProjectManager::DoOpenSelectedFile()
{
    SANITY_CHECK();
    wxTreeItemId sel = m_pTree->GetSelection();
    FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel);

    if (ftd)
    {
	    cbProject* project = ftd->GetProject();
	    ProjectFile* f = project->GetFile(ftd->GetFileIndex());
    	if (f)
        {
			DoOpenFile(f, f->file.GetFullPath());
        }
    }
}

// events

void ProjectManager::OnProjectFileActivated(wxTreeEvent& event)
{
    SANITY_CHECK();
    #ifdef USE_OPENFILES_TREE
    if(!MiscTreeItemData::OwnerCheck(event,m_pTree,this))
        return;
    #endif
	DoOpenSelectedFile();
}

void ProjectManager::OnExecParameters(wxCommandEvent& event)
{
    SANITY_CHECK();
    if (m_pActiveProject)
		m_pActiveProject->SelectTarget(0, true);
}

void ProjectManager::OnRightClick(wxCommandEvent& event)
{
    //Manager::Get()->GetMessageManager()->DebugLog("OnRightClick");
    SANITY_CHECK();

    wxMenu menu;

    // ask any plugins to add items in this menu
    Manager::Get()->GetPluginManager()->AskPluginsForModuleMenu(mtProjectManager, &menu, _T(""));

    // if plugins added to this menu, add a separator
    if (menu.GetMenuItemCount() != 0)
        menu.AppendSeparator();

    menu.AppendCheckItem(idMenuViewCategorizePopup, _("Categorize by file types"));
    menu.AppendCheckItem(idMenuViewUseFoldersPopup, _("Display folders as on disk"));
    menu.AppendSeparator();
    menu.Append(idMenuViewFileMasks, _("Edit file types && categories..."));

    menu.Check(idMenuViewCategorizePopup, m_TreeCategorize);
    menu.Check(idMenuViewUseFoldersPopup, m_TreeUseFolders);

    wxPoint pt = wxGetMousePosition();
    pt = m_pPanel->ScreenToClient(pt);
    m_pPanel->PopupMenu(&menu, pt);
}

void ProjectManager::OnTreeItemRightClick(wxTreeEvent& event)
{
    SANITY_CHECK();
    #ifdef USE_OPENFILES_TREE
    if(!MiscTreeItemData::OwnerCheck(event,m_pTree,this))
        return;
    #endif
    if(m_IsLoadingProject)
    {
        wxBell();
        return;
    }

    //Manager::Get()->GetMessageManager()->DebugLog("OnTreeItemRightClick");
	m_pTree->SelectItem(event.GetItem());
    ShowMenu(event.GetItem(), event.GetPoint());
}

void ProjectManager::OnRenameWorkspace(wxCommandEvent& event)
{
    SANITY_CHECK();
    if (m_pWorkspace)
    {
        wxString text = wxGetTextFromUser(_("Please enter the new name for the workspace:"), _("Rename workspace"), m_pWorkspace->GetTitle());
        if (!text.IsEmpty())
        {
            m_pWorkspace->SetTitle(text);
            m_pTree->SetItemText(m_TreeRoot, m_pWorkspace->GetTitle());
        }
    }
}

void ProjectManager::OnSetActiveProject(wxCommandEvent& event)
{
    SANITY_CHECK();
    if (event.GetId() == idMenuSetActiveProject)
    {
        wxTreeItemId sel = m_pTree->GetSelection();
        FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel);
        if (!ftd)
            return;

        SetProject(ftd->GetProject(), false);
	}
    else if (event.GetId() == idMenuPriorProject)
    {
        int index = m_pProjects->Index(m_pActiveProject);
        if (index == wxNOT_FOUND)
            return;
        --index;
        if (index < 0)
            index = m_pProjects->GetCount() - 1;
        SetProject(m_pProjects->Item(index), false);
    }
    else if (event.GetId() == idMenuNextProject)
    {
        int index = m_pProjects->Index(m_pActiveProject);
        if (index == wxNOT_FOUND)
            return;
        ++index;
        if (index == (int)m_pProjects->GetCount())
            index = 0;
        SetProject(m_pProjects->Item(index), false);
    }
    else if (event.GetId() == idMenuProjectUp)
    {
        MoveProjectUp(m_pActiveProject);
    }
    else if (event.GetId() == idMenuProjectDown)
    {
        MoveProjectDown(m_pActiveProject);
    }
}

void ProjectManager::OnAddFilesToProjectRecursively(wxCommandEvent& event)
{
    SANITY_CHECK();
    cbProject* prj = 0;
    if (event.GetId() == idMenuAddFilesRecursively)
        prj = GetActiveProject();
    else
    {
        wxTreeItemId sel = m_pTree->GetSelection();
        FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel);
        if (ftd)
            prj = ftd->GetProject();
    }
    if (!prj)
        return;

    wxString dir = ChooseDirectory(m_pPanel,
                                    _("Add files recursively..."),
                                    prj->GetBasePath(),
                                    wxEmptyString,
                                    false,
                                    false);
    if (dir.IsEmpty())
        return;

    wxArrayInt targets;
    // ask for target only if more than one
    if (prj->GetBuildTargetsCount() == 1)
         targets.Add(0);

    // generate list of files to add
    wxArrayString array;
    wxDir::GetAllFiles(dir, &array, wxEmptyString, wxDIR_FILES | wxDIR_DIRS);
    if (array.GetCount() == 0)
        return;

    // for usability reasons, remove any directory entries from the list...
    unsigned int i = 0;
    while (i < array.GetCount())
    {
    	// 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("\\.svn\\")) ||
            array[i].Contains(_T("/.svn/")) ||
            array[i].Contains(_T("\\CVS\\")) ||
            array[i].Contains(_T("/CVS/")) ||
            array[i].Lower().Matches(_T("*.cbp")))
        {
            array.RemoveAt(i);
        }
        else
            ++i;
    }

    // ask the user which files to add
// TODO (mandrav#1#): Make these masks configurable
    wxString wild = _T("*.c;*.cc;*.cpp;*.cxx;*.h;*.hh;*.hpp;*.hxx;*.inl;*.rc;*.xrc");
    MultiSelectDlg dlg(0, array, wild, _("Select the files to add to the project:"));
    if (dlg.ShowModal() != wxID_OK)
        return;
    array = dlg.GetSelectedStrings();

    // finally add the files
    AddMultipleFilesToProject(array, prj, targets);
    RebuildTree();
}

void ProjectManager::OnAddFileToProject(wxCommandEvent& event)
{
    SANITY_CHECK();
    cbProject* prj = 0;
    if (event.GetId() == idMenuAddFile)
        prj = GetActiveProject();
    else
    {
        wxTreeItemId sel = m_pTree->GetSelection();
        FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel);
        if (ftd)
            prj = ftd->GetProject();
    }
    if (!prj)
        return;

    wxFileDialog dlg(m_pPanel,
                    _("Add files to project..."),
                    prj->GetBasePath(),
                    wxEmptyString,
                    KNOWN_SOURCES_DIALOG_FILTER,
                    wxOPEN | wxMULTIPLE | wxFILE_MUST_EXIST);
    dlg.SetFilterIndex(KNOWN_SOURCES_FILTER_INDEX);

    if (dlg.ShowModal() == wxID_OK)
    {
		wxArrayInt targets;
        // ask for target only if more than one
		if (prj->GetBuildTargetsCount() == 1)
             targets.Add(0);

		wxArrayString array;
        dlg.GetPaths(array);
        AddMultipleFilesToProject(array, prj, targets);
        RebuildTree();
    }
}

void ProjectManager::OnRemoveFileFromProject(wxCommandEvent& event)
{
    SANITY_CHECK();
    if (event.GetId() == idMenuRemoveFile)
    {
        // remove multiple-files
        cbProject* prj = GetActiveProject();
        if (!prj)
            return;
        wxArrayString files;
        for (int i = 0; i < prj->GetFilesCount(); ++i)
        {
            files.Add(prj->GetFile(i)->relativeFilename);
        }
        wxString msg;
        msg.Printf(_("Select files to remove from %s:"), prj->GetTitle().c_str());
        MultiSelectDlg dlg(0, files, false, msg);
        if (dlg.ShowModal() == wxID_OK)
        {
            wxArrayInt indices = dlg.GetSelectedIndices();
            if (indices.GetCount() == 0)
                return;
            if (wxMessageBox(_("Are you sure you want to remove these files from the project?"),
                            _("Confirmation"),
                            wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT) != wxYES)
            {
                return;
            }
            // we iterate the arry backwards, because if we iterate it normally,
            // when we remove the first index, the rest become invalid...
            for (int i = (int)indices.GetCount() - 1; i >= 0; --i)
            {
                ProjectFile* pf = prj->GetFile(indices[i]);
                if (!pf)
                {
                    Manager::Get()->GetMessageManager()->DebugLog(_("Invalid project file: Index %d"), indices[i]);
                    continue;
                }
                wxString filename = pf->file.GetFullPath();
                Manager::Get()->GetMessageManager()->DebugLog(_("Removing index %d, %s"), indices[i], filename.c_str());
                prj->RemoveFile(indices[i]);
                CodeBlocksEvent evt(cbEVT_PROJECT_FILE_REMOVED);
                evt.SetProject(prj);
                evt.SetString(filename);
                Manager::Get()->GetPluginManager()->NotifyPlugins(evt);
            }
            prj->CalculateCommonTopLevelPath();
            RebuildTree();
        }
    }
    else
    {
        // remove single file
        wxTreeItemId sel = m_pTree->GetSelection();
        FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel);
        if (ftd)
        {
            cbProject* prj = ftd->GetProject();
            if (!prj)
                return;
            int fileindex = ftd->GetFileIndex();
            wxString filename = prj->GetFile(fileindex)->file.GetFullPath();
            prj->RemoveFile(fileindex);
            prj->CalculateCommonTopLevelPath();
            RebuildTree();
            CodeBlocksEvent evt(cbEVT_PROJECT_FILE_REMOVED);
            evt.SetProject(prj);
            evt.SetString(filename);
            Manager::Get()->GetPluginManager()->NotifyPlugins(evt);
        }
    }
}

void ProjectManager::OnCloseProject(wxCommandEvent& event)
{
    SANITY_CHECK();
    wxTreeItemId sel = m_pTree->GetSelection();
    FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel);
    cbProject *proj;
    if (ftd)
        proj = ftd->GetProject();
    if(proj)
    {
        if(m_IsLoadingProject)
        {
            wxBell();
        }
        else
            CloseProject(proj);
    }
    Manager::Get()->GetAppWindow()->Refresh();
}

void ProjectManager::OnCloseFile(wxCommandEvent& event)
{
    SANITY_CHECK();
    wxTreeItemId sel = m_pTree->GetSelection();
    FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel);

    if (ftd)
    {
	    cbProject* project = ftd->GetProject();
	    ProjectFile* f = project->GetFile(ftd->GetFileIndex());
    	if (f)
        	Manager::Get()->GetEditorManager()->Close(f->file.GetFullPath());
    }
}

void ProjectManager::OnOpenFile(wxCommandEvent& event)
{
	DoOpenSelectedFile();
}

void ProjectManager::OnProperties(wxCommandEvent& event)
{
    SANITY_CHECK();
    if (event.GetId() == idMenuProjectProperties)
    {
        wxString backupTitle = m_pActiveProject ? m_pActiveProject->GetTitle() : _T("");
        if (m_pActiveProject && m_pActiveProject->ShowOptions())
        {
            // make sure that cbEVT_PROJECT_ACTIVATE
            // is sent (maybe targets have changed)...
            // rebuild tree  only if title has changed
            SetProject(m_pActiveProject, backupTitle != m_pActiveProject->GetTitle());
        }
    }
    else if (event.GetId() == idMenuTreeProjectProperties)
    {
        wxTreeItemId sel = m_pTree->GetSelection();
        FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel);

        cbProject* project = ftd ? ftd->GetProject() : m_pActiveProject;
        wxString backupTitle = project ? project->GetTitle() : _T("");
        if (project && project->ShowOptions() && project == m_pActiveProject)
        {
            // rebuild tree and make sure that cbEVT_PROJECT_ACTIVATE
            // is sent (maybe targets have changed)...
            // rebuild tree  only if title has changed
            SetProject(project, backupTitle != project->GetTitle());
        }
    }
    else if (event.GetId() == idMenuTreeFileProperties)
    {
        wxTreeItemId sel = m_pTree->GetSelection();
        FileTreeData* ftd = (FileTreeData*)m_pTree->GetItemData(sel);

        cbProject* project = ftd ? ftd->GetProject() : m_pActiveProject;
        if (project)
        {
            if (ftd && ftd->GetFileIndex() != -1)
            {
                ProjectFile* pf = project->GetFile(ftd->GetFileIndex());
                if (pf)
                    pf->ShowOptions(m_pPanel);
            }
        }
    }
    else // active editor properties
    {
        cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
        if (ed)
        {
            ProjectFile* pf = ed->GetProjectFile();
            if (pf)
                pf->ShowOptions(m_pPanel);
        }
    }
}

void ProjectManager::OnGotoFile(wxCommandEvent& event)
{
    SANITY_CHECK();
	if (!m_pActiveProject)
	{
		Manager::Get()->GetMessageManager()->DebugLog(_("No active project!"));
		return;
	}

	wxArrayString files;
	for (int i = 0; i < m_pActiveProject->GetFilesCount(); ++i)
		files.Add(m_pActiveProject->GetFile(i)->relativeFilename);

	IncrementalSelectListDlg dlg(m_pPanel, files, _("Select file..."), _("Please select file to open:"));
	if (dlg.ShowModal() == wxID_OK)
	{
		ProjectFile* pf = m_pActiveProject->GetFileByFilename(dlg.GetStringSelection(), true);
		if (pf)
		{
			DoOpenFile(pf, pf->file.GetFullPath());
		}
	}
}

void ProjectManager::OnViewCategorize(wxCommandEvent& event)
{
    SANITY_CHECK();
    m_TreeCategorize = event.IsChecked();
    Manager::Get()->GetAppWindow()->GetMenuBar()->Check(idMenuViewCategorize, m_TreeCategorize);
	ConfigManager::Get()->Write(_T("/project_manager/categorize_tree"), m_TreeCategorize);
    RebuildTree();
}

void ProjectManager::OnViewUseFolders(wxCommandEvent& event)
{
    SANITY_CHECK();
    m_TreeUseFolders = event.IsChecked();
    Manager::Get()->GetAppWindow()->GetMenuBar()->Check(idMenuViewUseFolders, m_TreeUseFolders);
	ConfigManager::Get()->Write(_T("/project_manager/use_folders"), m_TreeUseFolders);
    RebuildTree();
}

void ProjectManager::OnViewFileMasks(wxCommandEvent& event)
{
    SANITY_CHECK();
	ProjectsFileMasksDlg dlg(Manager::Get()->GetAppWindow(), m_pFileGroups);
	if (dlg.ShowModal() == wxID_OK)
	{
		m_pFileGroups->Save();
		RebuildTree();
	}
}

void ProjectManager::OnIdle(wxIdleEvent& event)
{
    event.Skip();
}


syntax highlighted by Code2HTML, v. 0.9.1