/*
* 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: editormanager.cpp,v 1.79.2.1 2005/10/25 07:59:03 mandrav Exp $
* $Date: 2005/10/25 07:59:03 $
*/
#include "sdk_precomp.h"
#include <wx/notebook.h>
#include <wx/menu.h>
#include <wx/splitter.h>
#include <wx/imaglist.h>
#include <wx/bmpbuttn.h>
#include <wx/file.h>
#include <wx/progdlg.h>
#include <wx/dir.h>
#include "editormanager.h" // class's header file
#include "configmanager.h"
#include <wx/xrc/xmlres.h>
#include "messagemanager.h"
#include "projectmanager.h"
#include "manager.h"
#include "editorcolorset.h"
#include "editorconfigurationdlg.h"
#include "finddlg.h"
#include "replacedlg.h"
#include "confirmreplacedlg.h"
#include "projectbuildtarget.h"
#include "cbproject.h"
#include "cbeditor.h"
#include "globals.h"
#include "managerproxy.h"
#include "xtra_classes.h"
#include "sdk_events.h"
#include "searchresultslog.h"
#include <wx/listimpl.cpp>
WX_DEFINE_LIST(EditorsList);
#define MIN(a,b) (a<b?a:b)
#define MAX(a,b) (a>b?a:b)
//#define DONT_USE_OPENFILES_TREE
int ID_NBEditorManager = wxNewId();
int ID_EditorManager = wxNewId();
int ID_EditorManagerCloseButton = XRCID("ID_EditorManagerCloseButton");
int ID_EditorManagerPanel = XRCID("ID_EditorManagerPanel");
int idEditorManagerCheckFiles = wxNewId();
BEGIN_EVENT_TABLE(EditorManager, wxEvtHandler)
EVT_APP_STARTUP_DONE(EditorManager::OnAppDoneStartup)
EVT_APP_START_SHUTDOWN(EditorManager::OnAppStartShutdown)
EVT_NOTEBOOK_PAGE_CHANGED(ID_NBEditorManager, EditorManager::OnPageChanged)
EVT_NOTEBOOK_PAGE_CHANGING(ID_NBEditorManager, EditorManager::OnPageChanging)
EVT_MENU(idEditorManagerCheckFiles, EditorManager::OnCheckForModifiedFiles)
#ifdef USE_OPENFILES_TREE
EVT_UPDATE_UI(ID_EditorManager, EditorManager::OnUpdateUI)
EVT_TREE_SEL_CHANGING(ID_EditorManager, EditorManager::OnTreeItemActivated)
EVT_TREE_ITEM_ACTIVATED(ID_EditorManager, EditorManager::OnTreeItemActivated)
EVT_TREE_ITEM_RIGHT_CLICK(ID_EditorManager, EditorManager::OnTreeItemRightClick)
#endif
END_EVENT_TABLE()
// static
bool EditorManager::s_CanShutdown = true;
wxButton *edman_closebutton = NULL; // for private use
struct cbFindReplaceData
{
int start;
int end;
wxString findText;
wxString replaceText;
bool findInFiles;
bool matchWord;
bool startWord;
bool matchCase;
bool regEx;
bool directionDown;
bool originEntireScope;
int scope;
wxString searchPath;
wxString searchMask;
bool recursiveSearch;
bool hiddenSearch;
};
/** *******************************************************
* struct EditorManagerInternalData *
* This is the private data holder for the EditorManager *
* All data not relevant to other classes should go here *
********************************************************* */
struct EditorManagerInternalData
{
/* Methods */
EditorManagerInternalData(EditorManager* owner)
: m_pOwner(owner),
m_NeedsRefresh(false),
m_TreeNeedsRefresh(false),
m_pImages(NULL)
{}
void BuildTree(wxTreeCtrl* pTree)
{
wxBitmap bmp;
m_pImages = new wxImageList(16, 16);
wxString prefix = ConfigManager::Get()->Read(_T("data_path")) + _T("/images/");
bmp.LoadFile(prefix + _T("folder_open.png"), wxBITMAP_TYPE_PNG); // folder
m_pImages->Add(bmp);
bmp.LoadFile(prefix + _T("ascii.png"), wxBITMAP_TYPE_PNG); // file
m_pImages->Add(bmp);
bmp.LoadFile(prefix + _T("modified_file.png"), wxBITMAP_TYPE_PNG); // modified file
m_pImages->Add(bmp);
pTree->SetImageList(m_pImages);
m_TreeOpenedFiles=pTree->AddRoot(_T("Opened Files"), 0, 0);
pTree->SetItemBold(m_TreeOpenedFiles);
}
void InvalidateTree() { m_TreeNeedsRefresh = true; }
/* Static data */
EditorManager* m_pOwner;
/* Used for refreshing the notebook if necessary */
bool m_NeedsRefresh;
bool m_TreeNeedsRefresh;
wxImageList* m_pImages;
wxTreeItemId m_TreeOpenedFiles;
};
// *********** End of EditorManagerInternalData **********
EditorManager* EditorManager::Get(wxWindow* parent)
{
if(Manager::isappShuttingDown()) // The mother of all sanity checks
EditorManager::Free();
else
if (!EditorManagerProxy::Get())
{
EditorManagerProxy::Set( new EditorManager(parent) );
Manager::Get()->GetMessageManager()->Log(_("EditorManager initialized"));
}
return EditorManagerProxy::Get();
}
void EditorManager::Free()
{
if (EditorManagerProxy::Get())
{
delete EditorManagerProxy::Get();
EditorManagerProxy::Set( 0L );
}
}
// class constructor
EditorManager::EditorManager(wxWindow* parent)
:
m_pNotebook(0L),
m_pPanel(0L),
m_LastFindReplaceData(0L),
m_pTree(0L),
m_LastActiveFile(_T("")),
m_LastModifiedflag(false),
m_pSearchLog(0),
m_SearchLogIndex(-1),
m_SashPosition(150) // no longer used
{
SC_CONSTRUCTOR_BEGIN
EditorManagerProxy::Set(this);
m_pData = new EditorManagerInternalData(this);
// *** Load Panel and close button from XRC ***
m_pPanel = wxXmlResource::Get()->LoadPanel(parent,_T("ID_EditorManagerPanel"));
wxBitmapButton* myclosebutton = XRCCTRL(*m_pPanel,"ID_EditorManagerCloseButton",wxBitmapButton);
edman_closebutton = (wxButton*)myclosebutton;
m_pNotebook = new wxNotebook(m_pPanel, ID_NBEditorManager, wxDefaultPosition, wxDefaultSize, wxNO_FULL_REPAINT_ON_RESIZE | wxCLIP_CHILDREN);
m_pPanel->GetSizer()->Add(m_pNotebook,1,wxGROW);
// remove the ugly close-button, if not enabled in configuration
if (ConfigManager::Get()->Read(_T("/editor/show_close_button"), 0L) == 0)
{
m_pPanel->GetSizer()->Remove(edman_closebutton);
delete edman_closebutton;
edman_closebutton = 0;
}
// ***
m_EditorsList.Clear();
#ifdef USE_OPENFILES_TREE
m_pData->m_TreeNeedsRefresh = false;
ShowOpenFilesTree(ConfigManager::Get()->Read(_T("/editor/show_opened_files_tree"), true));
#endif
m_Theme = new EditorColorSet(ConfigManager::Get()->Read(_T("/editor/color_sets/active_color_set"), COLORSET_DEFAULT));
ConfigManager::AddConfiguration(_("Editor"), _T("/editor"));
parent->PushEventHandler(this);
CreateSearchLog();
LoadAutoComplete();
#if !wxCHECK_VERSION(2, 5, 0)
/*wxNotebookSizer* nbs =*/ new wxNotebookSizer(m_pNotebook);
#endif
}
// class destructor
EditorManager::~EditorManager()
{
SC_DESTRUCTOR_BEGIN
SaveAutoComplete();
// Clean up the notebook to prevent segfaults later
if(m_pNotebook)
{
m_pNotebook->Freeze(); // To prevent UpdateUI events
while(m_pNotebook->GetPageCount())
m_pNotebook->RemovePage(0); // Deletes the page, not the object
}
// Clean up editor list. The notebook is empty, we're free to wipe them out
// with no fear of segfaults! :)
m_EditorsList.DeleteContents(true);
m_EditorsList.Clear();
if(m_pNotebook)
m_pNotebook->Thaw();
if (m_Theme)
delete m_Theme;
if (m_LastFindReplaceData)
delete m_LastFindReplaceData;
if (m_pTree)
{
m_pTree->Destroy();
m_pTree = NULL;
}
if (m_pData->m_pImages)
{
delete m_pData->m_pImages;
m_pData->m_pImages = NULL;
}
if (m_pData)
{
delete m_pData;
m_pData = NULL;
}
edman_closebutton = NULL; // will be deleted by the window
SC_DESTRUCTOR_END
}
void EditorManager::CreateMenu(wxMenuBar* menuBar)
{
SANITY_CHECK();
}
void EditorManager::ReleaseMenu(wxMenuBar* menuBar)
{
SANITY_CHECK();
}
void EditorManager::Configure()
{
SANITY_CHECK();
EditorConfigurationDlg dlg(Manager::Get()->GetAppWindow());
if (dlg.ShowModal() == wxID_OK)
{
// tell all open editors to re-create their styles
for (EditorsList::Node* node = m_EditorsList.GetFirst(); node; node = node->GetNext())
{
cbEditor* ed = InternalGetBuiltinEditor(node);
if (ed)
ed->SetEditorStyle();
}
RebuildOpenedFilesTree(0); // maybe the tab text naming changed
}
}
void EditorManager::CreateSearchLog()
{
wxArrayString titles;
int widths[3] = {128, 48, 640};
titles.Add(_("File"));
titles.Add(_("Line"));
titles.Add(_("Text"));
m_pSearchLog = new SearchResultsLog(LOGGER, _("Search results"), 3, widths, titles);
m_SearchLogIndex = LOGGER->AddLog(m_pSearchLog);
wxFont font(8, wxMODERN, wxNORMAL, wxNORMAL);
m_pSearchLog->GetListControl()->SetFont(font);
// set log image
wxBitmap bmp;
wxString prefix = ConfigManager::Get()->Read(_T("data_path")) + _T("/images/");
bmp.LoadFile(prefix + _T("filefind.png"), wxBITMAP_TYPE_PNG);
Manager::Get()->GetMessageManager()->SetLogImage(m_pSearchLog, bmp);
}
void EditorManager::LogSearch(const wxString& file, int line, const wxString& lineText)
{
wxArrayString values;
wxString lineTextL;
wxString lineStr;
lineStr.Printf(_T("%d"), line);
lineTextL = lineText;
lineTextL.Replace(_T("\r"), _T(" "));
lineTextL.Replace(_T("\n"), _T(" "));
lineTextL.Trim(false);
lineTextL.Trim(true);
values.Add(file);
values.Add(lineStr);
values.Add(lineTextL);
m_pSearchLog->AddLog(values);
m_pSearchLog->GetListControl()->SetColumnWidth(2, wxLIST_AUTOSIZE);
}
void EditorManager::LoadAutoComplete()
{
m_AutoCompleteMap.clear();
long cookie;
wxString entry;
wxConfigBase* conf = ConfigManager::Get();
wxString oldPath = conf->GetPath();
conf->SetPath(_T("/editor/auto_complete"));
bool cont = conf->GetFirstEntry(entry, cookie);
while (cont)
{
wxString code = conf->Read(entry, _T(""));
// convert non-printable chars to printable
code.Replace(_T("\\n"), _T("\n"));
code.Replace(_T("\\r"), _T("\r"));
code.Replace(_T("\\t"), _T("\t"));
m_AutoCompleteMap[entry] = code;
cont = conf->GetNextEntry(entry, cookie);
}
conf->SetPath(oldPath);
if (m_AutoCompleteMap.size() == 0)
{
// default auto-complete items
m_AutoCompleteMap[_T("if")] = _T("if (|)\n\t;");
m_AutoCompleteMap[_T("ifb")] = _T("if (|)\n{\n\t\n}");
m_AutoCompleteMap[_T("ife")] = _T("if (|)\n{\n\t\n}\nelse\n{\n\t\n}");
m_AutoCompleteMap[_T("ifei")] = _T("if (|)\n{\n\t\n}\nelse if ()\n{\n\t\n}\nelse\n{\n\t\n}");
m_AutoCompleteMap[_T("while")] = _T("while (|)\n\t;");
m_AutoCompleteMap[_T("whileb")] = _T("while (|)\n{\n\t\n}");
m_AutoCompleteMap[_T("for")] = _T("for (|; ; )\n\t;");
m_AutoCompleteMap[_T("forb")] = _T("for (|; ; )\n{\n\t\n}");
m_AutoCompleteMap[_T("class")] = _T("class $(Class name)|\n{\n\tpublic:\n\t\t$(Class name)();\n\t\t~$(Class name)();\n\tprotected:\n\t\t\n\tprivate:\n\t\t\n};\n");
m_AutoCompleteMap[_T("struct")] = _T("struct |\n{\n\t\n};\n");
}
}
void EditorManager::SaveAutoComplete()
{
wxConfigBase* conf = ConfigManager::Get();
conf->DeleteGroup(_T("/editor/auto_complete"));
wxString oldPath = conf->GetPath();
conf->SetPath(_T("/editor/auto_complete"));
AutoCompleteMap::iterator it;
for (it = m_AutoCompleteMap.begin(); it != m_AutoCompleteMap.end(); ++it)
{
wxString code = it->second;
// convert non-printable chars to printable
code.Replace(_T("\n"), _T("\\n"));
code.Replace(_T("\r"), _T("\\r"));
code.Replace(_T("\t"), _T("\\t"));
conf->Write(it->first, code);
}
conf->SetPath(oldPath);
}
cbEditor* EditorManager::InternalGetBuiltinEditor(EditorsList::Node* node)
{
EditorBase* eb = node->GetData();
if (eb && eb->IsBuiltinEditor())
return (cbEditor*)eb;
return 0;
}
cbEditor* EditorManager::GetBuiltinEditor(EditorBase* eb)
{
return eb && eb->IsBuiltinEditor() ? (cbEditor*)eb : 0;
}
EditorBase* EditorManager::IsOpen(const wxString& filename)
{
SANITY_CHECK(NULL);
wxString uFilename = UnixFilename(filename);
for (EditorsList::Node* node = m_EditorsList.GetFirst(); node; node = node->GetNext())
{
EditorBase* eb = node->GetData();
wxString fname = eb->GetFilename();
#ifdef __WXMSW__
// MSW must use case-insensitive comparison
if (fname.IsSameAs(uFilename,false) || fname.IsSameAs(EDITOR_MODIFIED + uFilename,false))
return eb;
#else
if (fname.IsSameAs(uFilename) || fname.IsSameAs(EDITOR_MODIFIED + uFilename))
return eb;
#endif
}
return NULL;
}
EditorBase* EditorManager::GetEditor(int index)
{
SANITY_CHECK(0L);
EditorsList::Node* node = m_EditorsList.Item(index);
if (node)
return node->GetData();
return 0L;
}
void EditorManager::SetColorSet(EditorColorSet* theme)
{
SANITY_CHECK();
if (m_Theme)
delete m_Theme;
// copy locally
m_Theme = new EditorColorSet(*theme);
for (EditorsList::Node* node = m_EditorsList.GetFirst(); node; node = node->GetNext())
{
cbEditor* ed = InternalGetBuiltinEditor(node);
if (ed)
ed->SetColorSet(m_Theme);
}
}
cbEditor* EditorManager::Open(const wxString& filename, int pos,ProjectFile* data)
{
SANITY_CHECK(0L);
bool can_updateui = !GetActiveEditor() || !Manager::Get()->GetProjectManager()->IsLoading();
wxString fname = UnixFilename(filename);
// Manager::Get()->GetMessageManager()->DebugLog("Trying to open '%s'", fname.c_str());
if (!wxFileExists(fname))
return NULL;
// Manager::Get()->GetMessageManager()->DebugLog("File exists '%s'", fname.c_str());
// disallow application shutdown while opening files
// WARNING: remember to set it to true, when exiting this function!!!
s_CanShutdown = false;
EditorBase* eb = IsOpen(fname);
cbEditor* ed = 0;
if (eb)
{
if (eb->IsBuiltinEditor())
ed = (cbEditor*)eb;
else
return 0; // is open but not a builtin editor
}
if (!ed)
{
ed = new cbEditor(m_pNotebook, fname, m_Theme);
if (ed->IsOK())
AddEditorBase(ed);
else
{
ed->Destroy();
ed = NULL;
}
}
if(can_updateui)
{
if (ed)
{
SetActiveEditor(ed);
ed->GetControl()->SetFocus();
}
}
// check for ProjectFile
if (ed && !ed->GetProjectFile())
{
// First checks if we're already being passed a ProjectFile
// as a parameter
if(data)
{
Manager::Get()->GetMessageManager()->DebugLog(_("project data set for %s"), data->file.GetFullPath().c_str());
}
else
{
ProjectsArray* projects = Manager::Get()->GetProjectManager()->GetProjects();
for (unsigned int i = 0; i < projects->GetCount(); ++i)
{
cbProject* prj = projects->Item(i);
ProjectFile* pf = prj->GetFileByFilename(ed->GetFilename(), false);
if (pf)
{
Manager::Get()->GetMessageManager()->DebugLog(_("found %s"), pf->file.GetFullPath().c_str());
data = pf;
break;
}
}
}
if(data)
ed->SetProjectFile(data,true);
}
#ifdef USE_OPENFILES_TREE
if(can_updateui)
AddFiletoTree(ed);
#endif
// we 're done
s_CanShutdown = true;
return ed;
}
EditorBase* EditorManager::GetActiveEditor()
{
SANITY_CHECK(0L);
int sel = m_pNotebook->GetSelection();
if (sel == -1)
return 0;
// get the wxNotebookPage object
wxNotebookPage* page = m_pNotebook->GetPage(sel);
if (!page)
return 0;
// now see if it's a managed editor
if (!m_EditorsList.Find(static_cast<EditorBase*>(page)))
return 0;
return static_cast<EditorBase*>(page);
}
void EditorManager::ActivateNext()
{
int sel = m_pNotebook->GetSelection();
if (sel < (int)m_pNotebook->GetPageCount() - 1)
++sel;
else
sel = 0;
m_pNotebook->SetSelection(sel);
}
void EditorManager::ActivatePrevious()
{
int sel = m_pNotebook->GetSelection();
if (sel > 0)
--sel;
else
sel = m_pNotebook->GetPageCount() - 1;
m_pNotebook->SetSelection(sel);
}
void EditorManager::SetActiveEditor(EditorBase* ed)
{
SANITY_CHECK();
int page = FindPageFromEditor(ed);
if (page != -1)
m_pNotebook->SetSelection(page);
}
cbEditor* EditorManager::New()
{
SANITY_CHECK(0L);
cbEditor* ed = new cbEditor(m_pNotebook, wxEmptyString);
if (!ed->SaveAs())
{
//DeletePage(ed->GetPageIndex());
ed->Destroy();
return 0L;
}
// add default text
wxString key;
key.Printf(_T("/editor/default_code/%d"), (int)FileTypeOf(ed->GetFilename()));
wxString code = ConfigManager::Get()->Read(key, wxEmptyString);
ed->GetControl()->SetText(code);
ed->SetColorSet(m_Theme);
AddEditorBase(ed);
#ifdef USE_OPENFILES_TREE
AddFiletoTree(ed);
#endif
ed->Show(true);
SetActiveEditor(ed);
return ed;
}
void EditorManager::AddCustomEditor(EditorBase* eb)
{
SANITY_CHECK();
AddEditorBase(eb);
}
void EditorManager::RemoveCustomEditor(EditorBase* eb)
{
SANITY_CHECK();
RemoveEditorBase(eb);
}
void EditorManager::AddEditorBase(EditorBase* eb)
{
SANITY_CHECK();
if (!m_EditorsList.Find(eb))
{
int page = FindPageFromEditor(eb);
if (page == -1)
m_pNotebook->AddPage(eb, eb->GetTitle(), true);
m_EditorsList.Append(eb);
}
}
void EditorManager::RemoveEditorBase(EditorBase* eb, bool deleteObject)
{
SANITY_CHECK();
if (m_EditorsList.Find(eb))
{
int page = FindPageFromEditor(eb);
if (page != -1)
m_pNotebook->RemovePage(page);
m_EditorsList.DeleteObject(eb);
}
}
bool EditorManager::UpdateProjectFiles(cbProject* project)
{
SANITY_CHECK(false);
for (EditorsList::Node* node = m_EditorsList.GetFirst(); node; node = node->GetNext())
{
cbEditor* ed = InternalGetBuiltinEditor(node);
if (!ed)
continue;
ProjectFile* pf = ed->GetProjectFile();
if (!pf)
continue;
if (pf->project != project)
continue;
pf->editorTopLine = ed->GetControl()->GetFirstVisibleLine();
pf->editorPos = ed->GetControl()->GetCurrentPos();
pf->editorOpen = true;
}
return true;
}
bool EditorManager::CloseAll(bool dontsave)
{
SANITY_CHECK(true);
return CloseAllExcept(0L,dontsave);
}
bool EditorManager::QueryCloseAll()
{
SANITY_CHECK(true);
EditorsList::Node* node = m_EditorsList.GetFirst();
while (node)
{
EditorBase* eb = node->GetData();
if(eb && !QueryClose(eb))
return false; // aborted
node = node->GetNext();
}
return true;
}
bool EditorManager::CloseAllExcept(EditorBase* editor,bool dontsave)
{
if(!editor)
SANITY_CHECK(true);
SANITY_CHECK(false);
int count = m_EditorsList.GetCount();
EditorsList::Node* node = m_EditorsList.GetFirst();
if(!dontsave)
{
while (node)
{
EditorBase* eb = node->GetData();
if(eb && eb != editor && !QueryClose(eb))
return false; // aborted
node = node->GetNext();
}
}
count = m_EditorsList.GetCount();
node = m_EditorsList.GetFirst();
m_pNotebook->Hide();
while (node)
{
EditorBase* eb = node->GetData();
EditorsList::Node* next = node->GetNext();
if (eb && eb != editor && Close(eb, true))
{
node = next;
--count;
}
else
node = node->GetNext();
}
m_pNotebook->Show();
#ifdef USE_OPENFILES_TREE
RebuildOpenedFilesTree();
#endif
return count == (editor ? 1 : 0);
}
bool EditorManager::CloseActive(bool dontsave)
{
SANITY_CHECK(false);
return Close(GetActiveEditor(),dontsave);
}
bool EditorManager::QueryClose(EditorBase *ed)
{
if(!ed)
return true;
if (ed->GetModified())
{
// TODO (mandrav#1#): Move this in EditorBase
wxString msg;
msg.Printf(_("File %s is modified...\nDo you want to save the changes?"), ed->GetFilename().c_str());
switch (wxMessageBox(msg, _("Save file"), wxICON_QUESTION | wxYES_NO | wxCANCEL))
{
case wxYES: if (!ed->Save())
return false;
break;
case wxNO: break;
case wxCANCEL: return false;
}
}
else
{
return ed->QueryClose();
}
return true;
}
int EditorManager::FindPageFromEditor(EditorBase* eb)
{
for (int i = 0; i < (int)m_pNotebook->GetPageCount(); ++i)
{
if (m_pNotebook->GetPage(i) == eb)
return i;
}
return -1;
}
bool EditorManager::Close(const wxString& filename,bool dontsave)
{
SANITY_CHECK(false);
return Close(IsOpen(filename),dontsave);
}
bool EditorManager::Close(EditorBase* editor,bool dontsave)
{
SANITY_CHECK(false);
if (editor)
{
EditorsList::Node* node = m_EditorsList.Find(editor);
if (node)
{
if(!dontsave)
if(!QueryClose(editor))
return false;
wxString filename = editor->GetFilename();
// WARNING! The DeleteObject must be BEFORE DeletePage!
// Also, do NOT use DeleteNode. Doing so can result
// in a segfault (bug #1247249, confirmed several times).
m_EditorsList.DeleteObject(editor); // deletes the node, but not the editor
int edpage = FindPageFromEditor(editor);
if (edpage != -1)
m_pNotebook->DeletePage(edpage);
#ifdef USE_OPENFILES_TREE
DeleteFilefromTree(filename);
#endif
}
}
m_pData->m_NeedsRefresh = true;
return true;
}
bool EditorManager::Close(int index,bool dontsave)
{
SANITY_CHECK(false);
int i = 0;
for (EditorsList::Node* node = m_EditorsList.GetFirst(); node; node = node->GetNext(), ++i)
{
if (i == index)
{
return Close(node->GetData(),dontsave);
}
}
return false;
}
bool EditorManager::Save(const wxString& filename)
{
SANITY_CHECK(false);
// cbEditor* ed = GetBuiltinEditor(IsOpen(filename));
EditorBase* ed = IsOpen(filename);
if (ed)
return ed->Save();
return true;
}
bool EditorManager::Save(int index)
{
SANITY_CHECK(false);
int i = 0;
for (EditorsList::Node* node = m_EditorsList.GetFirst(); node; node = node->GetNext(), ++i)
{
if (i == index)
{
EditorBase* ed = node->GetData();
if (ed)
return ed->Save();
}
}
return false;
}
bool EditorManager::SaveActive()
{
SANITY_CHECK(false);
EditorBase* ed = GetActiveEditor();
if (ed)
return ed->Save();
return true;
}
bool EditorManager::SaveAs(int index)
{
SANITY_CHECK(false);
cbEditor* ed = GetBuiltinEditor(GetEditor(index));
if(!ed)
return false;
wxString oldname=ed->GetFilename();
if(!ed->SaveAs())
return false;
RenameTreeFile(oldname,ed->GetFilename());
return true;
}
bool EditorManager::SaveActiveAs()
{
SANITY_CHECK(false);
cbEditor* ed = GetBuiltinEditor(GetActiveEditor());
if (ed)
{
wxString oldname=ed->GetFilename();
if(ed->SaveAs())
RenameTreeFile(oldname,ed->GetFilename());
}
return true;
}
bool EditorManager::SaveAll()
{
SANITY_CHECK(false);
EditorsList::Node* node = m_EditorsList.GetFirst();
while (node)
{
EditorBase* ed = node->GetData();
if (ed && !ed->Save())
{
wxString msg;
msg.Printf(_("File %s could not be saved..."), ed->GetFilename().c_str());
wxMessageBox(msg, _("Error saving file"));
}
node = node->GetNext();
}
#ifdef USE_OPENFILES_TREE
RefreshOpenedFilesTree(true);
#endif
return true;
}
void EditorManager::Print(PrintScope ps, PrintColorMode pcm)
{
switch (ps)
{
case psAllOpenEditors:
{
for (EditorsList::Node* node = m_EditorsList.GetFirst(); node; node = node->GetNext())
{
cbEditor* ed = InternalGetBuiltinEditor(node);
if (ed)
ed->Print(false, pcm);
}
break;
}
default:
{
cbEditor* ed = GetBuiltinEditor(GetActiveEditor());
if (ed)
ed->Print(ps == psSelection, pcm);
break;
}
}
}
void EditorManager::CheckForExternallyModifiedFiles()
{
SANITY_CHECK();
wxLogNull ln;
bool reloadAll = false; // flag to stop bugging the user
wxArrayString failedFiles; // list of files failed to reload
for (EditorsList::Node* node = m_EditorsList.GetFirst(); node; node = node->GetNext())
{
bool b_modified = false;
cbEditor* ed = InternalGetBuiltinEditor(node);
// no builtin editor or new file not yet saved
if (!ed || !ed->IsOK()) continue;
//File was deleted?
if (!wxFileExists(ed->GetFilename()))
{
wxString msg;
msg.Printf(_("%s has been deleted, or is no longer available.\n"
"Do you wish to keep the file open?\n"
"Yes to keep the file, No to close it."), ed->GetFilename().c_str());
if (wxMessageBox(msg, _("File changed!"), wxYES_NO) == wxYES)
ed->SetModified(true);
else
ed->Close();
continue;
}
wxFileName fname(ed->GetFilename());
wxDateTime last = fname.GetModificationTime();
//File changed from RO -> RW?
if (ed->GetControl()->GetReadOnly() &&
wxFile::Access(ed->GetFilename().c_str(), wxFile::write))
{
b_modified = true;
}
//File changed from RW -> RO?
if (!ed->GetControl()->GetReadOnly() &&
!wxFile::Access(ed->GetFilename().c_str(), wxFile::write))
{
b_modified = true;
}
//File content changed?
if (last.IsLaterThan(ed->GetLastModificationTime()))
b_modified = true;
if (b_modified)
{
// modified; ask to reload
int ret = -1;
if (!reloadAll)
{
wxString msg;
msg.Printf(_("File %s is modified outside the IDE...\nDo you want to reload it (you will lose any unsaved work)?"),
ed->GetFilename().c_str());
ConfirmReplaceDlg dlg(Manager::Get()->GetAppWindow(), msg);
dlg.SetTitle(_("Reload file?"));
ret = dlg.ShowModal();
reloadAll = ret == crAll;
}
if (reloadAll || ret == crYes)
{
if (!ed->Reload())
failedFiles.Add(ed->GetFilename());
}
else if (ret == crCancel)
break;
else if (ret == crNo)
ed->Touch();
}
}
if (failedFiles.GetCount())
{
wxString msg;
msg.Printf(_("Could not reload all files:\n\n%s"), GetStringFromArray(failedFiles, _T("\n")).c_str());
wxMessageBox(msg, _("Error"), wxICON_ERROR);
}
}
bool EditorManager::SwapActiveHeaderSource()
{
SANITY_CHECK(false);
cbEditor* ed = GetBuiltinEditor(GetActiveEditor());
if (!ed)
return false;
FileType ft = FileTypeOf(ed->GetFilename());
if (ft != ftHeader && ft != ftSource)
return 0L;
// create a list of search dirs
wxArrayString dirs;
// get project's include dirs
cbProject* project = Manager::Get()->GetProjectManager()->GetActiveProject();
if (project)
{
dirs = project->GetIncludeDirs();
// first add all paths that contain project files
for (int i = 0; i < project->GetFilesCount(); ++i)
{
ProjectFile* pf = project->GetFile(i);
if (pf)
{
wxString dir = pf->file.GetPath(wxPATH_GET_VOLUME);
if (dirs.Index(dir) == wxNOT_FOUND)
dirs.Add(dir);
}
}
// get targets include dirs
for (int i = 0; i < project->GetBuildTargetsCount(); ++i)
{
ProjectBuildTarget* target = project->GetBuildTarget(i);
if (target)
{
for (unsigned int ti = 0; ti < target->GetIncludeDirs().GetCount(); ++ti)
{
wxString dir = target->GetIncludeDirs()[ti];
if (dirs.Index(dir) == wxNOT_FOUND)
dirs.Add(dir);
}
}
}
}
wxFileName fname;
wxFileName fn(ed->GetFilename());
dirs.Insert(fn.GetPath(wxPATH_GET_VOLUME), 0); // add file's dir
for (unsigned int i = 0; i < dirs.GetCount(); ++i)
{
fname.Assign(dirs[i] + wxFileName::GetPathSeparator() + fn.GetFullName());
if (!fname.IsAbsolute() && project)
{
fname.Normalize(wxPATH_NORM_ALL & ~wxPATH_NORM_CASE, project->GetBasePath());
}
//Manager::Get()->GetMessageManager()->DebugLog("Looking for '%s'", fname.GetFullPath().c_str());
if (ft == ftHeader)
{
fname.SetExt(CPP_EXT);
if (fname.FileExists())
break;
fname.SetExt(C_EXT);
if (fname.FileExists())
break;
fname.SetExt(CC_EXT);
if (fname.FileExists())
break;
fname.SetExt(CXX_EXT);
if (fname.FileExists())
break;
}
else if (ft == ftSource)
{
fname.SetExt(HPP_EXT);
if (fname.FileExists())
break;
fname.SetExt(H_EXT);
if (fname.FileExists())
break;
fname.SetExt(HH_EXT);
if (fname.FileExists())
break;
fname.SetExt(HXX_EXT);
if (fname.FileExists())
break;
}
}
if (fname.FileExists())
{
//Manager::Get()->GetMessageManager()->DebugLog("ed=%s, pair=%s", ed->GetFilename().c_str(), pair.c_str());
cbEditor* newEd = Open(fname.GetFullPath());
//if (newEd)
// newEd->SetProjectFile(ed->GetProjectFile());
return newEd;
}
return 0L;
}
int EditorManager::ShowFindDialog(bool replace)
{
SANITY_CHECK(-1);
wxString wordAtCursor;
wxString phraseAtCursor;
bool hasSelection = false;
cbStyledTextCtrl* control = 0;
cbEditor* ed = GetBuiltinEditor(GetActiveEditor());
if (ed)
{
control = ed->GetControl();
hasSelection = control->GetSelectionStart() != control->GetSelectionEnd();
int wordStart = control->WordStartPosition(control->GetCurrentPos(), true);
int wordEnd = control->WordEndPosition(control->GetCurrentPos(), true);
wordAtCursor = control->GetTextRange(wordStart, wordEnd);
phraseAtCursor = control->GetSelectedText();
// if selected text is the last searched text, don't suggest "search in selection"
if ((m_LastFindReplaceData &&
!phraseAtCursor.IsEmpty() &&
phraseAtCursor == m_LastFindReplaceData->findText)
|| phraseAtCursor == wordAtCursor)
{
hasSelection = false;
}
if (phraseAtCursor.IsEmpty())
phraseAtCursor = wordAtCursor;
}
FindReplaceBase* dlg;
if (!replace)
dlg = new FindDlg(Manager::Get()->GetAppWindow(), phraseAtCursor, hasSelection, !ed);
else
dlg = new ReplaceDlg(Manager::Get()->GetAppWindow(), phraseAtCursor, hasSelection);
if (dlg->ShowModal() == wxID_CANCEL)
{
delete dlg;
return -2;
}
if (!m_LastFindReplaceData)
m_LastFindReplaceData = new cbFindReplaceData;
m_LastFindReplaceData->start = 0;
m_LastFindReplaceData->end = 0;
m_LastFindReplaceData->findText = dlg->GetFindString();
m_LastFindReplaceData->replaceText = dlg->GetReplaceString();
m_LastFindReplaceData->findInFiles = dlg->IsFindInFiles();
m_LastFindReplaceData->matchWord = dlg->GetMatchWord();
m_LastFindReplaceData->startWord = dlg->GetStartWord();
m_LastFindReplaceData->matchCase = dlg->GetMatchCase();
m_LastFindReplaceData->regEx = dlg->GetRegEx();
m_LastFindReplaceData->directionDown = dlg->GetDirection() == 1;
m_LastFindReplaceData->originEntireScope = dlg->GetOrigin() == 1;
m_LastFindReplaceData->scope = dlg->GetScope();
m_LastFindReplaceData->searchPath = dlg->GetSearchPath();
m_LastFindReplaceData->searchMask = dlg->GetSearchMask();
m_LastFindReplaceData->recursiveSearch = dlg->GetRecursive();
m_LastFindReplaceData->hiddenSearch = dlg->GetHidden();
delete dlg;
if (!replace)
{
if (m_LastFindReplaceData->findInFiles)
return FindInFiles(m_LastFindReplaceData);
else
return Find(control, m_LastFindReplaceData);
}
else
return Replace(control, m_LastFindReplaceData);
}
void EditorManager::CalculateFindReplaceStartEnd(cbStyledTextCtrl* control, cbFindReplaceData* data)
{
SANITY_CHECK();
if (!control || !data)
return;
data->start = 0;
data->end = control->GetLength();
if (!data->findInFiles)
{
if (!data->originEntireScope) // from pos
data->start = control->GetCurrentPos();
else // entire scope
{
if (!data->directionDown) // up
data->start = control->GetLength();
}
if (!data->directionDown) // up
data->end = 0;
if (data->scope == 1) // selected text
{
if (!data->directionDown) // up
{
data->start = MAX(control->GetSelectionStart(), control->GetSelectionEnd());
data->end = MIN(control->GetSelectionStart(), control->GetSelectionEnd());
}
else // down
{
data->start = MIN(control->GetSelectionStart(), control->GetSelectionEnd());
data->end = MAX(control->GetSelectionStart(), control->GetSelectionEnd());
}
}
}
}
int EditorManager::Replace(cbStyledTextCtrl* control, cbFindReplaceData* data)
{
SANITY_CHECK(-1);
if (!control || !data)
return -1;
int flags = 0;
int start = data->start;
int end = data->end;
CalculateFindReplaceStartEnd(control, data);
if ((data->directionDown && (data->start < start)) ||
(!data->directionDown && (data->start > start)))
data->start = start;
if ((data->directionDown && (data->end < end)) ||
(!data->directionDown && (data->end > end)))
data->end = end;
if (data->matchWord)
flags |= wxSCI_FIND_WHOLEWORD;
if (data->startWord)
flags |= wxSCI_FIND_WORDSTART;
if (data->matchCase)
flags |= wxSCI_FIND_MATCHCASE;
if (data->regEx)
flags |= wxSCI_FIND_REGEXP;
control->BeginUndoAction();
int pos = -1;
bool replace = false;
bool confirm = true;
bool stop = false;
while (!stop)
{
int lengthFound = 0;
pos = control->FindText(data->start, data->end, data->findText, flags/*, &lengthFound*/);
lengthFound = data->findText.Length();
if (pos == -1)
break;
control->GotoPos(pos);
control->EnsureVisible(control->LineFromPosition(pos));
control->SetSelection(pos, pos + lengthFound);
data->start = pos;
//Manager::Get()->GetMessageManager()->DebugLog("pos=%d, selLen=%d, length=%d", pos, data->end - data->start, lengthFound);
if (confirm)
{
ConfirmReplaceDlg dlg(Manager::Get()->GetAppWindow());
dlg.CalcPosition(control);
switch (dlg.ShowModal())
{
case crYes:
replace = true;
break;
case crNo:
replace = false;
break;
case crAll:
replace = true;
confirm = false;
break;
case crCancel:
stop = true;
break;
}
}
if (!stop)
{
if (replace)
{
if (data->regEx)
{
// set target same as selection
control->SetTargetStart(control->GetSelectionStart());
control->SetTargetEnd(control->GetSelectionEnd());
// replace with regEx support
control->ReplaceTargetRE(data->replaceText);
// reset target
control->SetTargetStart(0);
control->SetTargetEnd(0);
}
else
control->ReplaceSelection(data->replaceText);
data->start += data->replaceText.Length();
// adjust end pos by adding the length difference between find and replace strings
int diff = data->replaceText.Length() - lengthFound;
if (data->directionDown)
data->end += diff;
else
data->end -= diff;
}
else
data->start += lengthFound;
}
}
control->EndUndoAction();
return pos;
}
int EditorManager::Find(cbStyledTextCtrl* control, cbFindReplaceData* data)
{
SANITY_CHECK(-1);
if (!control || !data)
return -1;
int flags = 0;
int start = data->start;
int end = data->end;
CalculateFindReplaceStartEnd(control, data);
if ((data->directionDown && (data->start < start)) ||
(!data->directionDown && (data->start > start)))
data->start = start;
if ((data->directionDown && (data->end < end)) ||
(!data->directionDown && (data->end > end)))
data->end = end;
if (data->matchWord)
flags |= wxSCI_FIND_WHOLEWORD;
if (data->startWord)
flags |= wxSCI_FIND_WORDSTART;
if (data->matchCase)
flags |= wxSCI_FIND_MATCHCASE;
if (data->regEx)
flags |= wxSCI_FIND_REGEXP;
int pos = -1;
// avoid infinite loop when wrapping search around, eventually crashing WinLogon O.O
bool wrapAround = false;
while (true) // loop while not found and user selects to start again from the top
{
int lengthFound = 0;
pos = control->FindText(data->start, data->end, data->findText, flags/*, &lengthFound*/);
lengthFound = data->findText.Length();
if (pos != -1)
{
control->GotoPos(pos);
control->EnsureVisible(control->LineFromPosition(pos));
control->SetSelection(pos, pos + lengthFound);
// Manager::Get()->GetMessageManager()->DebugLog("pos=%d, selLen=%d, length=%d", pos, data->end - data->start, lengthFound);
data->start = pos;
break; // done
}
else if (!wrapAround && !data->findInFiles) // for "find in files" we don't want to show messages
{
if (!data->scope == 1 &&
((data->directionDown && start != 0) ||
(!data->directionDown && start != control->GetLength())))
{
wxString msg;
if (data->directionDown)
msg = _("Text not found.\nSearch from the start of the document?");
else
msg = _("Text not found.\nSearch from the end of the document?");
// we can make a user-definable // tiwag 050902
bool DONTASK = ConfigManager::Get()->Read(_T("/editor/auto_wrap_search"), 1);
if (DONTASK) wxBell(); // tiwag 050902
if (DONTASK || wxMessageBox(msg, _("Result"), wxOK | wxCANCEL | wxICON_QUESTION) == wxOK)
{
if (data->directionDown)
{
data->start = 0;
data->end = control->GetLength();
wrapAround = true; // signal the wrap-around
}
else
{
data->start = control->GetLength();
data->end = 0;
wrapAround = true; // signal the wrap-around
}
}
else
break; // done
}
else
{
wxString msg;
msg.Printf(_("Not found: %s"), data->findText.c_str());
wxMessageBox(msg, _("Result"), wxICON_INFORMATION);
break; // done
}
}
else
break; // done
}
return pos;
}
int EditorManager::FindInFiles(cbFindReplaceData* data)
{
// clear old search results
m_pSearchLog->GetListControl()->DeleteAllItems();
if (!data || data->findText.IsEmpty())
return 0;
// let's make a list of all the files to search in
wxArrayString filesList;
if (data->scope == 0) // find in project files
{
// fill the search list with all the project files
cbProject* prj = Manager::Get()->GetProjectManager()->GetActiveProject();
if (!prj)
return 0;
wxString fullpath = _T("");
for (int i = 0; i < prj->GetFilesCount(); ++i)
{
ProjectFile* pf = prj->GetFile(i);
if (pf)
{
fullpath = pf->file.GetFullPath();
if (filesList.Index(fullpath) == -1) // avoid adding dulpicates
{
if(wxFileExists(fullpath)) // Does the file exist?
filesList.Add(fullpath);
}
}
}
}
else if (data->scope == 1) // find in open files
{
// fill the search list with the open files
for (EditorsList::Node* node = m_EditorsList.GetFirst(); node; node = node->GetNext())
{
cbEditor* ed = InternalGetBuiltinEditor(node);
if (ed)
filesList.Add(ed->GetFilename());
}
}
if (data->scope == 2) // find in custom search path and mask
{
// fill the search list with the files found under the search path
int flags = wxDIR_FILES |
(data->recursiveSearch ? wxDIR_DIRS : 0) |
(data->hiddenSearch ? wxDIR_HIDDEN : 0);
wxArrayString masks = GetArrayFromString(data->searchMask);
unsigned int count = masks.GetCount();
for (unsigned int i = 0; i < count; ++i)
{
// wxDir::GetAllFiles() does *not* clear the array, so it suits us just fine ;)
wxDir::GetAllFiles(data->searchPath, &filesList, masks[i], flags);
}
}
// if the list is empty, leave
if (filesList.GetCount() == 0)
{
wxMessageBox(_("No files to search in!"), _("Error"), wxICON_WARNING);
return 0;
}
// now that are list is filled, we 'll search
// but first we 'll create a hidden cbStyledTextCtrl to do the search for us ;)
cbStyledTextCtrl* control = new cbStyledTextCtrl(m_pNotebook, -1, wxDefaultPosition, wxSize(0, 0));
control->Show(false); //hidden
// let's create a progress dialog because it might take some time depending on the files count
wxProgressDialog* progress = new wxProgressDialog(_("Find in files"),
_("Please wait while searching inside the files..."),
filesList.GetCount());
// keep a copy of the find struct
cbFindReplaceData localData = *data;
int count = 0;
for (size_t i = 0; i < filesList.GetCount(); ++i)
{
// update the progress bar
progress->Update(i);
// re-initialize the find struct for every file searched
*data = localData;
// first load the file in the control
if (!control->LoadFile(filesList[i]))
{
LOGSTREAM << _("Failed opening ") << filesList[i] << wxT('\n');
continue; // failed
}
// now search for first occurence
if (Find(control, data) == -1)
continue; // none
int line = control->LineFromPosition(control->GetSelectionStart());
// log it
LogSearch(filesList[i], line + 1, control->GetLine(line));
++count;
// now loop finding the next occurence
while (FindNext(true, control, data) != -1)
{
// log it
line = control->LineFromPosition(control->GetSelectionStart());
LogSearch(filesList[i], line + 1, control->GetLine(line));
++count;
}
}
delete control; // done with it
delete progress; // done here too
if (count > 0)
{
Manager::Get()->GetMessageManager()->SwitchTo(m_SearchLogIndex);
Manager::Get()->GetMessageManager()->Open();
reinterpret_cast<SearchResultsLog*>(m_pSearchLog)->FocusEntry(0);
}
else
{
wxString msg;
msg.Printf(_("Not found: %s"), data->findText.c_str());
wxMessageBox(msg, _("Result"), wxICON_INFORMATION);
}
return count;
}
int EditorManager::FindNext(bool goingDown, cbStyledTextCtrl* control, cbFindReplaceData* data)
{
SANITY_CHECK(-1);
// if (m_LastFindReplaceData->findInFiles) // no "find next" for find in files
// return -1;
if (!control)
{
cbEditor* ed = GetBuiltinEditor(GetActiveEditor());
if (ed)
control = ed->GetControl();
}
if (!data)
data = m_LastFindReplaceData;
if (!data || !control)
return -1;
if (!goingDown && data->directionDown)
data->end = 0;
else if (goingDown && !data->directionDown)
data->end = data->start;
data->directionDown = goingDown;
// when going down, no need to add the search-text length, because the cursor
// is already positioned at the end of the word...
int multi = goingDown ? 0 : -1;
data->start = control->GetCurrentPos();
data->start += multi * (data->findText.Length() + 1);
return Find(control, data);
}
void EditorManager::OnPageChanged(wxNotebookEvent& event)
{
if (event.GetEventObject() == this)
{
}
event.Skip(); // allow others to process it too
}
void EditorManager::OnPageChanging(wxNotebookEvent& event)
{
if (event.GetEventObject() == this)
{
}
event.Skip(); // allow others to process it too
}
void EditorManager::OnAppDoneStartup(wxCommandEvent& event)
{
event.Skip(); // allow others to process it too
}
void EditorManager::OnAppStartShutdown(wxCommandEvent& event)
{
event.Skip(); // allow others to process it too
}
void EditorManager::OnCheckForModifiedFiles(wxCommandEvent& event)
{
CheckForExternallyModifiedFiles();
cbEditor* ed = GetBuiltinActiveEditor();
if (ed)
ed->GetControl()->SetFocus();
}
bool EditorManager::OpenFilesTreeSupported()
{
#ifdef DONT_USE_OPENFILES_TREE
return false;
#else
return true;
#endif
}
void EditorManager::RefreshOpenFilesTree()
{
if (!OpenFilesTreeSupported())
return;
if (!m_pTree)
InitPane();
if (!m_pTree)
return;
if(Manager::isappShuttingDown())
return;
wxWindow* win = Manager::Get()->GetNotebookPage(_("Projects"),wxTAB_TRAVERSAL | wxCLIP_CHILDREN,true);
wxSplitPanel* mypanel = (wxSplitPanel*)(win);
mypanel->RefreshSplitter(ID_EditorManager,ID_ProjectManager);
mypanel->Refresh();
m_pTree->Refresh();
}
void EditorManager::ShowOpenFilesTree(bool show)
{
if (!OpenFilesTreeSupported())
return;
if (!m_pTree)
InitPane();
if (!m_pTree)
return;
if(Manager::isappShuttingDown())
return;
if (show && !IsOpenFilesTreeVisible())
m_pTree->Show(true);
else if (!show && IsOpenFilesTreeVisible())
m_pTree->Show(false);
RefreshOpenFilesTree();
// update user prefs
ConfigManager::Get()->Write(_T("/editor/show_opened_files_tree"), show);
}
bool EditorManager::IsOpenFilesTreeVisible()
{
return m_pTree && m_pTree->IsShown();
}
wxTreeCtrl* EditorManager::GetTree()
{
SANITY_CHECK(0L);
return m_pTree;
// Manager::Get()->GetProjectManager()->GetTree();
}
wxTreeItemId EditorManager::FindTreeFile(const wxString& filename)
{
wxTreeItemId item = wxTreeItemId();
SANITY_CHECK(item);
do
{
if(Manager::isappShuttingDown())
break;
if(filename==_T(""))
break;
wxTreeCtrl *tree=GetTree();
if(!tree || !m_pData->m_TreeOpenedFiles)
break;
#if !wxCHECK_VERSION(2,5,0)
long int cookie = 0;
#else
wxTreeItemIdValue cookie; //2.6.0
#endif
for(item = tree->GetFirstChild(m_pData->m_TreeOpenedFiles,cookie);
item;
item = tree->GetNextChild(m_pData->m_TreeOpenedFiles, cookie))
{
if(GetTreeItemFilename(item)==filename)
break;
}
}while(false);
return item;
}
wxString EditorManager::GetTreeItemFilename(wxTreeItemId item)
{
SANITY_CHECK(_T(""));
if(Manager::isappShuttingDown())
return _T("");
wxTreeCtrl *tree=GetTree();
if(!tree || !m_pData->m_TreeOpenedFiles || !item)
return _T("");
MiscTreeItemData *data=(MiscTreeItemData*)tree->GetItemData(item);
if(!data)
return _T("");
if(data->GetOwner()!=this)
return _T("");
return ((EditorTreeData*)data)->GetFullName();
}
void EditorManager::DeleteItemfromTree(wxTreeItemId item)
{
SANITY_CHECK();
if(Manager::isappShuttingDown())
return;
wxTreeCtrl *tree=GetTree();
if(!tree || !m_pData->m_TreeOpenedFiles || !item)
return;
wxTreeItemId itemparent=tree->GetItemParent(item);
if(itemparent!=m_pData->m_TreeOpenedFiles)
return;
tree->Delete(item);
}
void EditorManager::DeleteFilefromTree(const wxString& filename)
{
SANITY_CHECK();
if(Manager::isappShuttingDown())
return;
DeleteItemfromTree(FindTreeFile(filename));
RefreshOpenedFilesTree();
}
void EditorManager::AddFiletoTree(EditorBase* ed)
{
SANITY_CHECK();
if(Manager::isappShuttingDown())
return;
if(!ed)
return;
if(!ed->VisibleToTree())
return;
wxString shortname=ed->GetShortName();
wxString filename=ed->GetFilename();
wxTreeItemId item=FindTreeFile(filename);
if(item.IsOk())
return;
wxTreeCtrl *tree=GetTree();
if(!tree)
return;
if(!m_pData->m_TreeOpenedFiles)
return;
int mod = ed->GetModified() ? 2 : 1;
tree->AppendItem(m_pData->m_TreeOpenedFiles,shortname,mod,mod,
new EditorTreeData(this,filename));
tree->SortChildren(m_pData->m_TreeOpenedFiles);
RefreshOpenedFilesTree(true);
}
void EditorManager::HideNotebook()
{
if(!this)
return;
if(m_pNotebook)
m_pNotebook->Hide();
if(m_pPanel)
m_pPanel->Refresh();
m_pData->m_NeedsRefresh = false;
return;
}
void EditorManager::ShowNotebook()
{
if(!this)
return;
if(m_pNotebook)
m_pNotebook->Show();
m_pData->m_NeedsRefresh = true;
m_pData->InvalidateTree();
return;
}
bool EditorManager::RenameTreeFile(const wxString& oldname, const wxString& newname)
{
SANITY_CHECK(false);
if(Manager::isappShuttingDown())
return false;
wxTreeCtrl *tree = GetTree();
if(!tree)
return false;
#if !wxCHECK_VERSION(2,5,0)
long int cookie = 0;
#else
wxTreeItemIdValue cookie; //2.6.0
#endif
wxTreeItemId item;
wxString filename,shortname;
for(item=tree->GetFirstChild(m_pData->m_TreeOpenedFiles,cookie);
item;
item = tree->GetNextChild(m_pData->m_TreeOpenedFiles, cookie))
{
EditorTreeData *data=(EditorTreeData*)tree->GetItemData(item);
if(!data)
continue;
filename=data->GetFullName();
if(filename!=oldname)
continue;
data->SetFullName(newname);
EditorBase *ed=GetEditor(filename);
if(ed)
{
shortname=ed->GetShortName();
int mod = ed->GetModified() ? 2 : 1;
if(tree->GetItemText(item)!=shortname)
tree->SetItemText(item,shortname);
if (tree->GetItemImage(item) != mod)
{
tree->SetItemImage(item, mod, wxTreeItemIcon_Normal);
tree->SetItemImage(item, mod, wxTreeItemIcon_Selected);
}
if(ed==GetActiveEditor())
tree->SelectItem(item);
}
return true;
}
return false;
}
void EditorManager::InitPane()
{
#if defined(DONT_USE_OPENFILES_TREE)
return;
#endif
SANITY_CHECK();
if(Manager::isappShuttingDown())
return;
if(m_pTree)
return;
Manager* man = Manager::Get();
wxWindow* win = man->GetNotebookPage(_("Projects"),wxTAB_TRAVERSAL | wxCLIP_CHILDREN,true);
wxSplitPanel* mypanel = (wxSplitPanel*)(win);
mypanel->SetConfigEntryForSplitter(_T("/editor/opened_files_tree_height"));
wxSplitterWindow* mysplitter = mypanel->GetSplitter();
BuildOpenedFilesTree(mysplitter);
mypanel->SetAutoLayout(true);
mypanel->RefreshSplitter(ID_EditorManager,ID_ProjectManager);
}
void EditorManager::BuildOpenedFilesTree(wxWindow* parent)
{
#if defined(DONT_USE_OPENFILES_TREE)
return;
#endif
SANITY_CHECK();
if(m_pTree)
return;
m_pTree = new wxTreeCtrl(parent, ID_EditorManager,wxDefaultPosition,wxDefaultSize,wxTR_HAS_BUTTONS | wxNO_BORDER);
m_pData->BuildTree(m_pTree);
RebuildOpenedFilesTree(m_pTree);
}
void EditorManager::RebuildOpenedFilesTree(wxTreeCtrl *tree)
{
#if defined(DONT_USE_OPENFILES_TREE)
return;
#endif
SANITY_CHECK();
if(Manager::isappShuttingDown())
return;
if(!tree)
tree=GetTree();
if(!tree)
return;
tree->DeleteChildren(m_pData->m_TreeOpenedFiles);
if(!GetEditorsCount())
return;
tree->Freeze();
for (EditorsList::Node* node = m_EditorsList.GetFirst(); node; node = node->GetNext())
{
EditorBase* ed = node->GetData();
if(!ed)
continue;
if(!ed->VisibleToTree())
continue;
wxString shortname=ed->GetShortName();
int mod = ed->GetModified() ? 2 : 1;
wxTreeItemId item=tree->AppendItem(m_pData->m_TreeOpenedFiles,shortname,mod,mod,
new EditorTreeData(this,ed->GetFilename()));
if(GetActiveEditor()==ed)
tree->SelectItem(item);
}
tree->Expand(m_pData->m_TreeOpenedFiles);
tree->Thaw();
m_pData->InvalidateTree();
}
void EditorManager::RefreshOpenedFilesTree(bool force)
{
#if defined(DONT_USE_OPENFILES_TREE)
return;
#endif
SANITY_CHECK();
if(Manager::isappShuttingDown())
return;
wxTreeCtrl *tree=GetTree();
if(!tree)
return;
wxString fname;
EditorBase *aed=GetActiveEditor();
if(!aed)
return;
if(!aed->VisibleToTree())
return;
bool ismodif=aed->GetModified();
fname=aed->GetFilename();
if(!force && m_LastActiveFile==fname && m_LastModifiedflag==ismodif)
return; // Nothing to do
m_LastActiveFile=fname;
m_LastModifiedflag=ismodif;
Manager::Get()->GetProjectManager()->FreezeTree();
#if !wxCHECK_VERSION(2,5,0)
long int cookie = 0;
#else
wxTreeItemIdValue cookie; //2.6.0
#endif
wxTreeItemId item = tree->GetFirstChild(m_pData->m_TreeOpenedFiles,cookie);
wxString filename,shortname;
while (item)
{
EditorTreeData *data=(EditorTreeData*)tree->GetItemData(item);
if(data)
{
filename=data->GetFullName();
EditorBase *ed=GetEditor(filename);
if(ed)
{
shortname=ed->GetShortName();
int mod = ed->GetModified() ? 2 : 1;
if(tree->GetItemText(item)!=shortname)
tree->SetItemText(item,shortname);
if (tree->GetItemImage(item) != mod)
{
tree->SetItemImage(item, mod, wxTreeItemIcon_Normal);
tree->SetItemImage(item, mod, wxTreeItemIcon_Selected);
}
if(ed==aed)
tree->SelectItem(item);
// tree->SetItemBold(item,(ed==aed));
}
}
item = tree->GetNextChild(m_pData->m_TreeOpenedFiles, cookie);
}
Manager::Get()->GetProjectManager()->UnfreezeTree();
}
void EditorManager::OnTreeItemActivated(wxTreeEvent &event)
{
SANITY_CHECK();
if(Manager::isappShuttingDown())
return;
if(!MiscTreeItemData::OwnerCheck(event,GetTree(),this,true))
return;
wxString filename=GetTreeItemFilename(event.GetItem());
if(filename==_T(""))
return;
Open(filename);
}
void EditorManager::OnTreeItemRightClick(wxTreeEvent &event)
{
SANITY_CHECK();
if(Manager::isappShuttingDown())
return;
if(!MiscTreeItemData::OwnerCheck(event,GetTree(),this,true))
return;
wxString filename=GetTreeItemFilename(event.GetItem());
if(filename.IsEmpty())
return;
EditorBase* ed = GetEditor(filename);
if(ed)
{
wxPoint pt = m_pTree->ClientToScreen(event.GetPoint());
ed->DisplayContextMenu(pt,true);
}
}
void EditorManager::OnUpdateUI(wxUpdateUIEvent& event)
{
// no need for check (happens in RefreshOpenedFilesTree, if called)
// SANITY_CHECK();
if(!Manager::isappShuttingDown())
RefreshOpenedFilesTree();
if(m_pTree && m_pData->m_TreeNeedsRefresh && m_pTree->IsShown())
{
m_pTree->Refresh();
m_pData->m_TreeNeedsRefresh=false;
}
if(edman_closebutton)
edman_closebutton->Show(GetActiveEditor()!=NULL);
if(m_pData->m_NeedsRefresh && m_pNotebook->IsShown())
{
if(m_pNotebook)
m_pNotebook->Refresh();
if(GetActiveEditor())
GetActiveEditor()->Refresh();
m_pData->m_NeedsRefresh=false;
}
// allow other UpdateUI handlers to process this event
// *very* important! don't forget it...
event.Skip();
}
syntax highlighted by Code2HTML, v. 0.9.1