2
* This file is part of the Code::Blocks IDE and licensed under the GNU Lesser General Public License, version 3
3
* http://www.gnu.org/licenses/lgpl-3.0.html
6
* $Id: editormanager.cpp 4909 2008-02-27 13:15:26Z mortenmacfly $
7
* $HeadURL: svn://svn.berlios.de/codeblocks/tags/8.02/src/sdk/editormanager.cpp $
10
#include "sdk_precomp.h"
13
#include <wx/notebook.h>
15
#include <wx/splitter.h>
16
#include <wx/imaglist.h>
18
#include <wx/listctrl.h>
20
#include "editormanager.h" // class's header file
21
#include "configmanager.h"
22
#include <wx/xrc/xmlres.h>
23
#include "logmanager.h"
24
#include "projectmanager.h"
25
#include "projectfile.h"
26
#include "pluginmanager.h"
28
#include "filemanager.h"
29
#include "sdk_events.h"
30
#include "projectbuildtarget.h"
31
#include "cbproject.h"
34
#include "sdk_events.h"
38
#include "cbstyledtextctrl.h"
40
#include <wx/bmpbuttn.h>
41
#include <wx/progdlg.h>
42
#include <wx/fontutil.h>
44
#include "editorcolourset.h"
45
#include "editorconfigurationdlg.h"
47
#include "replacedlg.h"
48
#include "confirmreplacedlg.h"
49
#include "filefilters.h"
50
#include "searchresultslog.h"
51
#include "projectfileoptionsdlg.h"
53
#include "wx/wxFlatNotebook/wxFlatNotebook.h"
55
template<> EditorManager* Mgr<EditorManager>::instance = 0;
56
template<> bool Mgr<EditorManager>::isShutdown = false;
58
int ID_NBEditorManager = wxNewId();
59
int ID_EditorManager = wxNewId();
60
int idEditorManagerCheckFiles = wxNewId();
63
bool EditorManager::s_CanShutdown = true;
65
struct cbFindReplaceData
71
bool initialreplacing;
79
bool originEntireScope;
85
bool NewSearch; //!< only true when a new search has been started
86
int SearchInSelectionStart; //!< keep track of the start of a 'search' selection
87
int SearchInSelectionEnd; //!< keep track of the end of a 'search' selection
89
bool findUsesSelectedText;
92
static const int idNBTabSplitHorz = wxNewId();
93
static const int idNBTabSplitVert = wxNewId();
94
static const int idNBTabUnsplit = wxNewId();
95
static const int idNBTabClose = wxNewId();
96
static const int idNBTabCloseAll = wxNewId();
97
static const int idNBTabCloseAllOthers = wxNewId();
98
static const int idNBTabSave = wxNewId();
99
static const int idNBTabSaveAll = wxNewId();
100
static const int idNBSwapHeaderSource = wxNewId();
101
static const int idNBTabTop = wxNewId();
102
static const int idNBTabBottom = wxNewId();
103
static const int idNBProperties = wxNewId();
104
static const int idNB = wxNewId();
106
/** *******************************************************
107
* struct EditorManagerInternalData *
108
* This is the private data holder for the EditorManager *
109
* All data not relevant to other classes should go here *
110
********************************************************* */
112
struct EditorManagerInternalData
116
EditorManagerInternalData(EditorManager* owner)
122
EditorManager* m_pOwner;
126
// *********** End of EditorManagerInternalData **********
129
BEGIN_EVENT_TABLE(EditorManager, wxEvtHandler)
130
EVT_APP_STARTUP_DONE(EditorManager::OnAppDoneStartup)
131
EVT_APP_START_SHUTDOWN(EditorManager::OnAppStartShutdown)
132
EVT_FLATNOTEBOOK_PAGE_CHANGED(ID_NBEditorManager, EditorManager::OnPageChanged)
133
EVT_FLATNOTEBOOK_PAGE_CHANGING(ID_NBEditorManager, EditorManager::OnPageChanging)
134
EVT_FLATNOTEBOOK_PAGE_CLOSING(ID_NBEditorManager, EditorManager::OnPageClosing)
135
EVT_FLATNOTEBOOK_CONTEXT_MENU(ID_NBEditorManager, EditorManager::OnPageContextMenu)
136
EVT_MENU(idNBTabSplitHorz, EditorManager::OnGenericContextMenuHandler)
137
EVT_MENU(idNBTabSplitVert, EditorManager::OnGenericContextMenuHandler)
138
EVT_MENU(idNBTabUnsplit, EditorManager::OnGenericContextMenuHandler)
139
EVT_MENU(idNBTabTop, EditorManager::OnTabPosition)
140
EVT_MENU(idNBTabBottom, EditorManager::OnTabPosition)
141
EVT_MENU(idNBTabClose, EditorManager::OnClose)
142
EVT_MENU(idNBTabCloseAll, EditorManager::OnCloseAll)
143
EVT_MENU(idNBTabCloseAllOthers, EditorManager::OnCloseAllOthers)
144
EVT_MENU(idNBTabSave, EditorManager::OnSave)
145
EVT_MENU(idNBTabSaveAll, EditorManager::OnSaveAll)
146
EVT_MENU(idNBSwapHeaderSource, EditorManager::OnSwapHeaderSource)
147
EVT_MENU(idNBProperties, EditorManager::OnProperties)
148
EVT_MENU(idEditorManagerCheckFiles, EditorManager::OnCheckForModifiedFiles)
152
EditorManager::EditorManager()
154
m_LastFindReplaceData(0L),
156
m_SearchLogIndex(-1),
157
m_SashPosition(150), // no longer used
158
m_isCheckingForExternallyModifiedFiles(false)
160
m_pData = new EditorManagerInternalData(this);
162
m_pNotebook = new wxFlatNotebook(Manager::Get()->GetAppWindow(), ID_NBEditorManager, wxDefaultPosition, wxDefaultSize, wxNO_FULL_REPAINT_ON_RESIZE | wxCLIP_CHILDREN);
163
m_pNotebook->SetWindowStyleFlag(Manager::Get()->GetConfigManager(_T("app"))->ReadInt(_T("/environment/editor_tabs_style"), wxFNB_DEFAULT_STYLE | wxFNB_MOUSE_MIDDLE_CLOSES_TABS));
165
Manager::Get()->GetLogManager()->DebugLog(_T("Initialize EditColourSet ....."));
166
m_Theme = new EditorColourSet(Manager::Get()->GetConfigManager(_T("editor"))->Read(_T("/colour_sets/active_colour_set"), COLORSET_DEFAULT));
167
Manager::Get()->GetLogManager()->DebugLog(_T("Initialize EditColourSet: done."));
169
Manager::Get()->GetAppWindow()->PushEventHandler(this);
173
m_zoom = Manager::Get()->GetConfigManager(_T("editor"))->ReadInt(_T("/zoom"));
177
EditorManager::~EditorManager()
181
CodeBlocksLogEvent evt(cbEVT_REMOVE_LOG_WINDOW, m_pSearchLog);
182
Manager::Get()->ProcessEvent(evt);
185
delete m_LastFindReplaceData;
187
Manager::Get()->GetConfigManager(_T("editor"))->Write(_T("/zoom"), m_zoom);
188
} // end of destructor
190
void EditorManager::CreateMenu(wxMenuBar* menuBar)
194
void EditorManager::ReleaseMenu(wxMenuBar* menuBar)
198
void EditorManager::Configure()
200
// editor lexers loading takes some time; better reflect this with a hourglass
203
EditorConfigurationDlg dlg(Manager::Get()->GetAppWindow());
206
// done, restore pointer
209
if (dlg.ShowModal() == wxID_OK)
211
// tell all open editors to re-create their styles
212
for (int i = 0; i < m_pNotebook->GetPageCount(); ++i)
214
cbEditor* ed = InternalGetBuiltinEditor(i);
217
bool saveSuccess = ed->SaveFoldState(); //First Save the old fold levels
218
ed->SetEditorStyle();
221
ed->FixFoldState(); //Compare old fold levels with new and change the bugs
226
} // end of Configure
228
void EditorManager::CreateSearchLog()
230
if (Manager::IsBatchBuild())
234
wxArrayString titles;
235
titles.Add(_("File"));
236
titles.Add(_("Line"));
237
titles.Add(_("Text"));
242
wxString prefix = ConfigManager::GetDataFolder() + _T("/images/16x16/");
243
wxBitmap * bmp = new wxBitmap(cbLoadBitmap(prefix + _T("filefind.png"), wxBITMAP_TYPE_PNG));
245
m_pSearchLog = new SearchResultsLog(titles, widths);
246
CodeBlocksLogEvent evt(cbEVT_ADD_LOG_WINDOW, m_pSearchLog, _("Search results"), bmp);
247
Manager::Get()->ProcessEvent(evt);
250
void EditorManager::LogSearch(const wxString& file, int line, const wxString& lineText)
252
wxArrayString values;
256
// line number -1 is used for empty string
259
lineStr.Printf(_T("%d"), line);
263
lineStr.Printf(_T(" "));
265
lineTextL = lineText;
266
lineTextL.Replace(_T("\t"), _T(" "));
267
lineTextL.Replace(_T("\r"), _T(" "));
268
lineTextL.Replace(_T("\n"), _T(" "));
269
lineTextL.Trim(false);
270
lineTextL.Trim(true);
274
values.Add(lineTextL);
276
m_pSearchLog->Append(values, line == -1 ? Logger::caption : Logger::info);
279
void EditorManager::LoadAutoComplete()
281
m_AutoCompleteMap.clear();
282
wxArrayString list = Manager::Get()->GetConfigManager(_T("editor"))->EnumerateSubPaths(_T("/auto_complete"));
283
for (unsigned int i = 0; i < list.GetCount(); ++i)
285
wxString name = Manager::Get()->GetConfigManager(_T("editor"))->Read(_T("/auto_complete/") + list[i] + _T("/name"), wxEmptyString);
286
wxString code = Manager::Get()->GetConfigManager(_T("editor"))->Read(_T("/auto_complete/") + list[i] + _T("/code"), wxEmptyString);
287
if (name.IsEmpty() || code.IsEmpty())
289
// convert non-printable chars to printable
290
code.Replace(_T("\\n"), _T("\n"));
291
code.Replace(_T("\\r"), _T("\r"));
292
code.Replace(_T("\\t"), _T("\t"));
293
m_AutoCompleteMap[name] = code;
296
if (m_AutoCompleteMap.size() == 0)
298
// default auto-complete items
299
m_AutoCompleteMap[_T("if")] = _T("if (|)\n\t;");
300
m_AutoCompleteMap[_T("ifb")] = _T("if (|)\n{\n\t\n}");
301
m_AutoCompleteMap[_T("ife")] = _T("if (|)\n{\n\t\n}\nelse\n{\n\t\n}");
302
m_AutoCompleteMap[_T("ifei")] = _T("if (|)\n{\n\t\n}\nelse if ()\n{\n\t\n}\nelse\n{\n\t\n}");
303
m_AutoCompleteMap[_T("guard")] = _T("#ifndef $(Guard token)\n#define $(Guard token)\n\n|\n\n#endif // $(Guard token)\n");
304
m_AutoCompleteMap[_T("while")] = _T("while (|)\n\t;");
305
m_AutoCompleteMap[_T("whileb")] = _T("while (|)\n{\n\t\n}");
306
m_AutoCompleteMap[_T("switch")] = _T("switch (|)\n{\n\tcase :\n\t\tbreak;\n\n\tdefault:\n\t\tbreak;\n}\n");
307
m_AutoCompleteMap[_T("for")] = _T("for (|; ; )\n\t;");
308
m_AutoCompleteMap[_T("forb")] = _T("for (|; ; )\n{\n\t\n}");
309
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");
310
m_AutoCompleteMap[_T("struct")] = _T("struct |\n{\n\t\n};\n");
313
// date and time macros
314
// these are auto-added if they 're found to be missing
315
const wxString timeAndDate[9][2] =
317
{ _T("tday"), _T("$TDAY") },
318
{ _T("tdayu"), _T("$TDAY_UTC") },
319
{ _T("today"), _T("$TODAY") },
320
{ _T("todayu"), _T("$TODAY_UTC") },
321
{ _T("now"), _T("$NOW") },
322
{ _T("nowl"), _T("$NOW_L") },
323
{ _T("nowu"), _T("$NOW_UTC") },
324
{ _T("nowlu"), _T("$NOW_L_UTC") },
325
{ _T("wdu"), _T("$WEEKDAY_UTC") },
327
for (int i = 0; i < 9; ++i)
329
if (m_AutoCompleteMap.find(timeAndDate[i][0]) == m_AutoCompleteMap.end())
330
m_AutoCompleteMap[timeAndDate[i][0]] = timeAndDate[i][1];
334
void EditorManager::SaveAutoComplete()
336
Manager::Get()->GetConfigManager(_T("editor"))->DeleteSubPath(_T("/auto_complete"));
337
AutoCompleteMap::iterator it;
339
for (it = m_AutoCompleteMap.begin(); it != m_AutoCompleteMap.end(); ++it)
341
wxString code = it->second;
342
// convert non-printable chars to printable
343
code.Replace(_T("\n"), _T("\\n"));
344
code.Replace(_T("\r"), _T("\\r"));
345
code.Replace(_T("\t"), _T("\\t"));
349
key.Printf(_T("/auto_complete/entry%d/name"), count);
350
Manager::Get()->GetConfigManager(_T("editor"))->Write(key, it->first);
351
key.Printf(_T("/auto_complete/entry%d/code"), count);
352
Manager::Get()->GetConfigManager(_T("editor"))->Write(key, code);
356
int EditorManager::GetEditorsCount()
358
return m_pNotebook->GetPageCount();
361
EditorBase* EditorManager::InternalGetEditorBase(int page)
363
EditorBase* eb = static_cast<EditorBase*>(m_pNotebook->GetPage(page));
367
cbEditor* EditorManager::InternalGetBuiltinEditor(int page)
369
EditorBase* eb = InternalGetEditorBase(page);
370
if (eb && eb->IsBuiltinEditor())
371
return (cbEditor*)eb;
375
cbEditor* EditorManager::GetBuiltinEditor(EditorBase* eb)
377
return eb && eb->IsBuiltinEditor() ? (cbEditor*)eb : 0;
380
EditorBase* EditorManager::IsOpen(const wxString& filename)
382
wxString uFilename = UnixFilename(filename);
383
for (int i = 0; i < m_pNotebook->GetPageCount(); ++i)
385
EditorBase* eb = InternalGetEditorBase(i);
388
wxString fname = eb->GetFilename();
390
// MSW must use case-insensitive comparison
391
if (fname.IsSameAs(uFilename, platform::windows == false) || fname.IsSameAs(g_EditorModified + uFilename, platform::windows == false))
398
EditorBase* EditorManager::GetEditor(int index)
400
return InternalGetEditorBase(index);
403
void EditorManager::SetColourSet(EditorColourSet* theme)
409
m_Theme = new EditorColourSet(*theme);
411
for (int i = 0; i < m_pNotebook->GetPageCount(); ++i)
413
cbEditor* ed = InternalGetBuiltinEditor(i);
415
ed->SetColourSet(m_Theme);
419
cbEditor* EditorManager::Open(const wxString& filename, int pos, ProjectFile* data)
421
LoaderBase* fileLdr = Manager::Get()->GetFileManager()->Load(filename);
425
return Open(fileLdr, filename, pos, data);
428
cbEditor* EditorManager::Open(LoaderBase* fileLdr, const wxString& filename, int pos,ProjectFile* data)
430
bool can_updateui = !GetActiveEditor() || !Manager::Get()->GetProjectManager()->IsLoading();
431
wxFileName fn(filename);
432
NormalizePath(fn, wxEmptyString);
433
wxString fname = UnixFilename(fn.GetFullPath());
434
// Manager::Get()->GetLogManager()->DebugLog("Trying to open '%s'", fname.c_str());
435
if (!wxFileExists(fname))
437
// Manager::Get()->GetLogManager()->DebugLog("File exists '%s'", fname.c_str());
439
// disallow application shutdown while opening files
440
// WARNING: remember to set it to true, when exiting this function!!!
441
s_CanShutdown = false;
443
EditorBase* eb = IsOpen(fname);
447
if (eb->IsBuiltinEditor())
450
return 0; // is open but not a builtin editor
455
ed = new cbEditor(m_pNotebook, fileLdr, fname, m_Theme);
470
ed->GetControl()->SetFocus();
474
// check for ProjectFile
475
if (ed && !ed->GetProjectFile())
477
// First checks if we're already being passed a ProjectFile
481
Manager::Get()->GetLogManager()->DebugLog(_T("project data set for ") + data->file.GetFullPath());
485
ProjectsArray* projects = Manager::Get()->GetProjectManager()->GetProjects();
486
for (unsigned int i = 0; i < projects->GetCount(); ++i)
488
cbProject* prj = projects->Item(i);
489
ProjectFile* pf = prj->GetFileByFilename(ed->GetFilename(), false);
492
Manager::Get()->GetLogManager()->DebugLog(_T("found ") + pf->file.GetFullPath());
499
ed->SetProjectFile(data,true);
503
s_CanShutdown = true;
508
EditorBase* EditorManager::GetActiveEditor()
510
return InternalGetEditorBase(m_pNotebook->GetSelection());
513
void EditorManager::ActivateNext()
515
m_pNotebook->AdvanceSelection(true);
518
void EditorManager::ActivatePrevious()
520
m_pNotebook->AdvanceSelection(false);
523
void EditorManager::SetActiveEditor(EditorBase* ed)
527
if (ed->IsBuiltinEditor())
528
static_cast<cbEditor*>(ed)->GetControl()->SetFocus();
529
int page = FindPageFromEditor(ed);
532
m_pNotebook->SetSelection(page);
536
cbEditor* EditorManager::New(const wxString& newFileName)
538
// wxString old_title = Manager::Get()->GetAppWindow()->GetTitle(); // Fix for Bug #1389450
539
// create a dummy file
540
if (!newFileName.IsEmpty() && !wxFileExists(newFileName) && wxDirExists(wxPathOnly(newFileName)))
542
wxFile f(newFileName, wxFile::write);
546
cbEditor* ed = new cbEditor(m_pNotebook, newFileName);
547
// if ((newFileName.IsEmpty() && !ed->SaveAs()) || !ed->Save())
549
// //DeletePage(ed->GetPageIndex());
551
// Manager::Get()->GetAppWindow()->SetTitle(old_title); // Though I can't reproduce the bug, this does no harm
557
key.Printf(_T("/default_code/set%d"), (int)FileTypeOf(ed->GetFilename()));
558
wxString code = Manager::Get()->GetConfigManager(_T("editor"))->Read(key, wxEmptyString);
559
ed->GetControl()->SetText(code);
561
ed->SetColourSet(m_Theme);
566
CodeBlocksEvent evt(cbEVT_EDITOR_OPEN, -1, 0, ed);
567
Manager::Get()->GetPluginManager()->NotifyPlugins(evt);
571
void EditorManager::AddCustomEditor(EditorBase* eb)
576
void EditorManager::RemoveCustomEditor(EditorBase* eb)
578
RemoveEditorBase(eb, false);
581
void EditorManager::AddEditorBase(EditorBase* eb)
583
int page = FindPageFromEditor(eb);
586
// LOGSTREAM << wxString::Format(_T("AddEditorBase(): ed=%p, title=%s\n"), eb, eb ? eb->GetTitle().c_str() : _T(""));
587
m_pNotebook->AddPage(eb, eb->GetTitle(), true);
591
void EditorManager::RemoveEditorBase(EditorBase* eb, bool deleteObject)
593
// LOGSTREAM << wxString::Format(_T("RemoveEditorBase(): ed=%p, title=%s\n"), eb, eb ? eb->GetFilename().c_str() : _T(""));
594
int page = FindPageFromEditor(eb);
595
if (page != -1 && !Manager::isappShuttingDown())
596
m_pNotebook->RemovePage(page, false);
602
bool EditorManager::UpdateProjectFiles(cbProject* project)
604
for (int i = 0; i < m_pNotebook->GetPageCount(); ++i)
606
cbEditor* ed = InternalGetBuiltinEditor(i);
609
ProjectFile* pf = ed->GetProjectFile();
612
if (pf->GetParentProject() != project)
614
pf->editorTopLine = ed->GetControl()->GetFirstVisibleLine();
615
pf->editorPos = ed->GetControl()->GetCurrentPos();
616
pf->editorTabPos = i + 1;
617
pf->editorOpen = true;
622
bool EditorManager::CloseAll(bool dontsave)
624
//return CloseAllExcept(0L,dontsave);
625
return CloseAllExcept(GetEditor(_("Start here")), dontsave);
628
bool EditorManager::QueryCloseAll()
630
for (int i = m_pNotebook->GetPageCount() - 1; i >= 0; --i)
632
EditorBase* eb = InternalGetEditorBase(i);
633
if(eb && !QueryClose(eb))
634
return false; // aborted
639
bool EditorManager::CloseAllExcept(EditorBase* editor,bool dontsave)
643
for (int i = 0; i < m_pNotebook->GetPageCount(); ++i)
645
EditorBase* eb = InternalGetEditorBase(i);
646
if(eb && eb != editor && !QueryClose(eb))
647
return false; // aborted
651
m_pNotebook->Freeze();
652
int count = m_pNotebook->GetPageCount();
653
for (int i = m_pNotebook->GetPageCount() - 1; i >= 0; --i)
655
EditorBase* eb = InternalGetEditorBase(i);
656
if (eb && eb != editor && Close(eb, true))
660
return count == (editor ? 1 : 0);
663
bool EditorManager::CloseActive(bool dontsave)
665
return Close(GetActiveEditor(),dontsave);
668
bool EditorManager::QueryClose(EditorBase *ed)
672
if (ed->GetModified())
674
// TODO (mandrav#1#): Move this in EditorBase
676
msg.Printf(_("File %s is modified...\nDo you want to save the changes?"), ed->GetFilename().c_str());
677
switch (cbMessageBox(msg, _("Save file"), wxICON_QUESTION | wxYES_NO | wxCANCEL))
688
ed->SetModified(false);
692
return ed->QueryClose();
697
int EditorManager::FindPageFromEditor(EditorBase* eb)
699
for (int i = 0; i < m_pNotebook->GetPageCount(); ++i)
701
if (m_pNotebook->GetPage(i) == eb)
707
bool EditorManager::Close(const wxString& filename,bool dontsave)
709
return Close(IsOpen(filename),dontsave);
712
bool EditorManager::Close(EditorBase* editor,bool dontsave)
716
int idx = FindPageFromEditor(editor);
720
if(!QueryClose(editor))
722
wxString filename = editor->GetFilename();
723
// LOGSTREAM << wxString::Format(_T("Close(): ed=%p, title=%s\n"), editor, editor ? editor->GetTitle().c_str() : _T(""));
724
m_pNotebook->DeletePage(idx, true);
730
bool EditorManager::Close(int index,bool dontsave)
732
EditorBase* ed = InternalGetEditorBase(index);
734
return Close(ed,dontsave);
738
bool EditorManager::Save(const wxString& filename)
740
// cbEditor* ed = GetBuiltinEditor(IsOpen(filename));
741
EditorBase* ed = IsOpen(filename);
744
wxString oldname = ed->GetFilename();
752
bool EditorManager::Save(int index)
754
EditorBase* ed = InternalGetEditorBase(index);
757
wxString oldname = ed->GetFilename();
765
bool EditorManager::SaveActive()
767
EditorBase* ed = GetActiveEditor();
770
wxString oldname = ed->GetFilename();
778
bool EditorManager::SaveAs(int index)
780
cbEditor* ed = GetBuiltinEditor(GetEditor(index));
786
bool EditorManager::SaveActiveAs()
788
cbEditor* ed = GetBuiltinEditor(GetActiveEditor());
796
bool EditorManager::SaveAll()
798
for (int i = 0; i < m_pNotebook->GetPageCount(); ++i)
800
EditorBase* ed = InternalGetEditorBase(i);
801
if (ed && ed->GetModified() && !ed->Save())
804
msg.Printf(_("File %s could not be saved..."), ed->GetFilename().c_str());
805
cbMessageBox(msg, _("Error saving file"), wxICON_ERROR);
812
void EditorManager::Print(PrintScope ps, PrintColourMode pcm, bool line_numbers)
816
case psAllOpenEditors:
818
for (int i = 0; i < m_pNotebook->GetPageCount(); ++i)
820
cbEditor* ed = InternalGetBuiltinEditor(i);
822
ed->Print(false, pcm, line_numbers);
828
cbEditor* ed = GetBuiltinEditor(GetActiveEditor());
830
ed->Print(ps == psSelection, pcm, line_numbers);
836
void EditorManager::CheckForExternallyModifiedFiles()
838
if(m_isCheckingForExternallyModifiedFiles) // for some reason, a mutex locker does not work???
840
m_isCheckingForExternallyModifiedFiles = true;
842
bool reloadAll = false; // flag to stop bugging the user
843
wxArrayString failedFiles; // list of files failed to reload
844
for (int i = 0; i < m_pNotebook->GetPageCount(); ++i)
846
cbEditor* ed = InternalGetBuiltinEditor(i);
847
bool b_modified = false;
849
// no builtin editor or new file not yet saved
850
if (!ed || !ed->IsOK())
853
if (!wxFileExists(ed->GetFilename()))
855
if(ed->GetModified()) // Already set the flag
858
msg.Printf(_("%s has been deleted, or is no longer available.\n"
859
"Do you wish to keep the file open?\n"
860
"Yes to keep the file, No to close it."), ed->GetFilename().c_str());
861
if (cbMessageBox(msg, _("File changed!"), wxICON_QUESTION | wxYES_NO) == wxID_YES)
862
ed->SetModified(true);
866
ProjectFile* pf = ed->GetProjectFile();
868
pf->SetFileState(fvsMissing);
873
ProjectFile* pf = ed->GetProjectFile();
874
wxFileName fname(ed->GetFilename());
875
wxDateTime last = fname.GetModificationTime();
877
//File changed from RO -> RW?
878
if (ed->GetControl()->GetReadOnly() &&
879
wxFile::Access(ed->GetFilename().c_str(), wxFile::write))
882
ed->GetControl()->SetReadOnly(false);
884
pf->SetFileState(fvsNormal);
886
//File changed from RW -> RO?
887
if (!ed->GetControl()->GetReadOnly() &&
888
!wxFile::Access(ed->GetFilename().c_str(), wxFile::write))
891
ed->GetControl()->SetReadOnly(true);
893
pf->SetFileState(fvsReadOnly);
895
//File content changed?
896
if (last.IsLaterThan(ed->GetLastModificationTime()))
901
// modified; ask to reload
906
msg.Printf(_("File %s is modified outside the IDE...\nDo you want to reload it (you will lose any unsaved work)?"),
907
ed->GetFilename().c_str());
908
ConfirmReplaceDlg dlg(Manager::Get()->GetAppWindow(), false, msg);
909
dlg.SetTitle(_("Reload file?"));
911
ret = dlg.ShowModal();
912
reloadAll = ret == crAll;
914
if (reloadAll || ret == crYes)
917
failedFiles.Add(ed->GetFilename());
919
else if (ret == crCancel)
921
else if (ret == crNo)
926
// this will emmit a EVT_EDITOR_ACTIVATED event, which in turn will notify
927
// the app to update the currently active file's info
928
// (we 're interested in updating read-write state)
929
SetActiveEditor(GetActiveEditor());
931
if (failedFiles.GetCount())
934
msg.Printf(_("Could not reload all files:\n\n%s"), GetStringFromArray(failedFiles, _T("\n")).c_str());
935
cbMessageBox(msg, _("Error"), wxICON_ERROR);
937
m_isCheckingForExternallyModifiedFiles = false;
938
} // end of CheckForExternallyModifiedFiles
940
bool EditorManager::SwapActiveHeaderSource()
942
cbEditor* ed = GetBuiltinEditor(GetActiveEditor());
946
FileType ft = FileTypeOf(ed->GetFilename());
947
if (ft != ftHeader && ft != ftSource)
950
// create a list of search dirs
953
cbProject* project = 0;
955
// if the file in question belongs to a different open project,
956
// then use that project instead.
957
// this fixes locating the file's pair in a workspace when the active file
958
// does not belong to the active project.
959
ProjectFile* opf = ed->GetProjectFile();
961
project = opf->GetParentProject();
963
// if we didn't get a valid project, try the active one
965
project = Manager::Get()->GetProjectManager()->GetActiveProject();
967
// get project's include dirs
970
dirs = project->GetIncludeDirs();
972
// first add all paths that contain project files
973
for (int i = 0; i < project->GetFilesCount(); ++i)
975
ProjectFile* pf = project->GetFile(i);
978
wxString dir = pf->file.GetPath(wxPATH_GET_VOLUME);
979
if (dirs.Index(dir) == wxNOT_FOUND)
984
// get targets include dirs
985
for (int i = 0; i < project->GetBuildTargetsCount(); ++i)
987
ProjectBuildTarget* target = project->GetBuildTarget(i);
990
for (unsigned int ti = 0; ti < target->GetIncludeDirs().GetCount(); ++ti)
992
wxString dir = target->GetIncludeDirs()[ti];
993
if (dirs.Index(dir) == wxNOT_FOUND)
1001
wxFileName fn(ed->GetFilename());
1002
dirs.Insert(fn.GetPath(wxPATH_GET_VOLUME), 0); // add file's dir
1004
for (unsigned int i = 0; i < dirs.GetCount(); ++i)
1006
fname.Assign(dirs[i] + wxFileName::GetPathSeparator() + fn.GetFullName());
1007
if (!fname.IsAbsolute() && project)
1009
fname.Normalize(wxPATH_NORM_ALL & ~wxPATH_NORM_CASE, project->GetBasePath());
1011
//Manager::Get()->GetLogManager()->DebugLog("Looking for '%s'", fname.GetFullPath().c_str());
1014
fname.SetExt(FileFilters::C_EXT);
1015
if (fname.FileExists())
1017
fname.SetExt(FileFilters::CC_EXT);
1018
if (fname.FileExists())
1020
fname.SetExt(FileFilters::CPP_EXT);
1021
if (fname.FileExists())
1023
fname.SetExt(FileFilters::CXX_EXT);
1024
if (fname.FileExists())
1027
else if (ft == ftSource)
1029
fname.SetExt(FileFilters::H_EXT);
1030
if (fname.FileExists())
1032
fname.SetExt(FileFilters::HH_EXT);
1033
if (fname.FileExists())
1035
fname.SetExt(FileFilters::HPP_EXT);
1036
if (fname.FileExists())
1038
fname.SetExt(FileFilters::HXX_EXT);
1039
if (fname.FileExists())
1044
if (fname.FileExists())
1046
//Manager::Get()->GetLogManager()->DebugLog("ed=%s, pair=%s", ed->GetFilename().c_str(), pair.c_str());
1047
cbEditor* newEd = Open(fname.GetFullPath());
1049
// newEd->SetProjectFile(ed->GetProjectFile());
1053
// We couldn't find the file, maybe it does not exist. Ask the user if we
1054
// should create it:
1055
if (cbMessageBox(_("The file does not exist. Do you want to create it?"),
1056
_("Error"), wxICON_QUESTION | wxYES_NO) == wxID_YES)
1058
cbProject* project = Manager::Get()->GetProjectManager()->GetActiveProject();
1060
wxSetWorkingDirectory(project->GetBasePath());
1062
// Create a suggestion for the new file name:
1064
fn.SetExt(FileFilters::CPP_EXT);
1065
else if (ft == ftSource)
1066
fn.SetExt(FileFilters::H_EXT);
1067
// else? Well, if the filename is not changed we could possibly
1068
// overwrite an existing file with our suggestion.
1070
cbEditor* newEd = New(fn.GetFullPath());
1071
if (cbMessageBox(_("Do you want to add this new file in the active project?"),
1072
_("Add file to project"),
1073
wxYES_NO | wxICON_QUESTION) == wxID_YES)
1076
if (Manager::Get()->GetProjectManager()->AddFileToProject(newEd->GetFilename(), project, targets) != 0)
1078
ProjectFile* pf = project->GetFileByFilename(newEd->GetFilename(), false);
1079
newEd->SetProjectFile(pf);
1080
Manager::Get()->GetProjectManager()->RebuildTree();
1083
// verify that the open files are still in sync
1084
// the new file might have overwritten an existing one)
1085
Manager::Get()->GetEditorManager()->CheckForExternallyModifiedFiles();
1090
int EditorManager::ShowFindDialog(bool replace, bool explicitly_find_in_files)
1092
wxString phraseAtCursor;
1093
bool hasSelection = false;
1094
cbStyledTextCtrl* control = 0;
1096
cbEditor* ed = GetBuiltinEditor(GetActiveEditor());
1099
control = ed->GetControl();
1101
hasSelection = control->GetSelectionStart() != control->GetSelectionEnd();
1102
int wordStart = control->WordStartPosition(control->GetCurrentPos(), true);
1103
int wordEnd = control->WordEndPosition(control->GetCurrentPos(), true);
1104
wxString wordAtCursor = control->GetTextRange(wordStart, wordEnd);
1105
phraseAtCursor = control->GetSelectedText();
1106
// if selected text is part of a single line, don't suggest "search in selection"
1107
if (control->LineFromPosition(control->GetSelectionStart())
1108
== control->LineFromPosition(control->GetSelectionEnd()))
1109
hasSelection = false;
1111
if ( phraseAtCursor.IsEmpty())
1112
phraseAtCursor = wordAtCursor;
1114
int selstartline = control->LineFromPosition(control->GetSelectionStart());
1115
int selendline = control->LineFromPosition(control->GetSelectionEnd());
1116
// the selection of several lines is not proposed as search pattern
1117
if ( selstartline != selendline )
1118
phraseAtCursor = wxEmptyString;
1122
FindReplaceBase* dlg;
1124
dlg = new FindDlg(Manager::Get()->GetAppWindow(), phraseAtCursor, hasSelection, !ed, explicitly_find_in_files);
1126
dlg = new ReplaceDlg(Manager::Get()->GetAppWindow(), phraseAtCursor, hasSelection, !ed, explicitly_find_in_files);
1129
if (dlg->ShowModal() == wxID_CANCEL)
1135
// Don't look for empty strings:
1136
if (dlg->GetFindString().empty())
1139
cbMessageBox(_("Can't look for an empty search criterion!"), _("Error"), wxOK | wxICON_EXCLAMATION, Manager::Get()->GetAppWindow());
1143
if (!m_LastFindReplaceData)
1144
m_LastFindReplaceData = new cbFindReplaceData;
1146
m_LastFindReplaceData->start = 0;
1147
m_LastFindReplaceData->end = 0;
1148
m_LastFindReplaceData->findText = dlg->GetFindString();
1149
m_LastFindReplaceData->replaceText = dlg->GetReplaceString();
1151
m_LastFindReplaceData->findInFiles = dlg->IsFindInFiles();
1152
if(!m_LastFindReplaceData->findInFiles)
1154
//AutoWrapSearch does not exist in FindInFiles dialog
1155
m_LastFindReplaceData->autoWrapSearch = dlg->GetAutoWrapSearch();
1157
//FindUsesSelectedText does not exist in Replace dialogs
1159
m_LastFindReplaceData->findUsesSelectedText = dlg->GetFindUsesSelectedText();
1161
m_LastFindReplaceData->delOldSearches = dlg->GetDeleteOldSearches();
1162
m_LastFindReplaceData->matchWord = dlg->GetMatchWord();
1163
m_LastFindReplaceData->startWord = dlg->GetStartWord();
1164
m_LastFindReplaceData->matchCase = dlg->GetMatchCase();
1165
m_LastFindReplaceData->regEx = dlg->GetRegEx();
1166
m_LastFindReplaceData->directionDown = dlg->GetDirection() == 1;
1167
m_LastFindReplaceData->originEntireScope = dlg->GetOrigin() == 1;
1168
m_LastFindReplaceData->scope = dlg->GetScope();
1169
m_LastFindReplaceData->searchPath = dlg->GetSearchPath();
1170
m_LastFindReplaceData->searchMask = dlg->GetSearchMask();
1171
m_LastFindReplaceData->recursiveSearch = dlg->GetRecursive();
1172
m_LastFindReplaceData->hiddenSearch = dlg->GetHidden();
1173
m_LastFindReplaceData->initialreplacing = false;
1174
m_LastFindReplaceData->NewSearch = true;
1176
{ // if editor : store the selection start/end
1177
// only use this in case of !findInFiles and scope==1 (search in selection)
1178
m_LastFindReplaceData->SearchInSelectionStart = control->GetSelectionStart();
1179
m_LastFindReplaceData->SearchInSelectionEnd = control->GetSelectionEnd();
1183
int ReturnValue = 0;
1186
if (m_LastFindReplaceData->findInFiles)
1187
ReturnValue = FindInFiles(m_LastFindReplaceData);
1189
ReturnValue = Find(control, m_LastFindReplaceData);
1193
m_LastFindReplaceData->initialreplacing = true;
1195
if (m_LastFindReplaceData->findInFiles)
1196
ReturnValue = ReplaceInFiles(m_LastFindReplaceData);
1198
ReturnValue = Replace(control, m_LastFindReplaceData);
1200
m_LastFindReplaceData->NewSearch = false; // we have searched, so no longer new search
1202
//Default back to find or replace in Editor
1203
if(m_LastFindReplaceData->findInFiles)
1205
m_LastFindReplaceData->findInFiles = false;
1208
} // end of ShowFindDialog
1210
void EditorManager::CalculateFindReplaceStartEnd(cbStyledTextCtrl* control, cbFindReplaceData* data, bool replace)
1212
if (!control || !data)
1215
if (!data->findInFiles) // Find in current Editor
1217
int ssta = control->GetSelectionStart();
1218
int send = control->GetSelectionEnd();
1219
int cpos = control->GetCurrentPos();
1220
int clen = control->GetLength();
1222
// when the user initially had a selection, but then changed the scope
1223
// to entire scope, the values of ssta and send will have a bad influence in
1224
// the following calculations, therefor check for the scenario
1225
// and set the ssta en send to cpos (in the case there would be no selection
1226
// that's the value they have [no selection : ssta=send=cpos])
1227
// only do this when it's a new search (when the search is continued (F3/Shift-F3)
1228
// there can be a selection, the last found match)
1229
if(data->scope== 0 && data->NewSearch && (ssta != cpos || send != cpos))
1240
if (!data->originEntireScope || !data->NewSearch) // from pos or next/prev search
1242
if (!data->directionDown) // up
1243
// initial replacing mode - include selection end : normal mode - skip until selection start
1244
data->start = (data->initialreplacing)? std::max(send, cpos) : std::min(ssta, cpos);
1246
// initial replacing mode - include selection start : normal mode - skip until selection end
1247
data->start = (data->initialreplacing)? std::min(ssta, cpos) : std::max(send, cpos);
1249
else // entire scope
1251
if (!data->directionDown) // up
1255
if (!data->directionDown) // up
1258
// selected text, if user has deslected since last, then change scope
1259
if (data->scope == 1 &&
1260
control->GetSelectionStart()==control->GetSelectionEnd())
1263
if (data->scope == 1) // selected text
1267
if (!data->directionDown) // up
1269
data->start = std::max(ssta, send);
1270
data->end = std::min(ssta, send);
1274
data->start = std::min(ssta, send);
1275
data->end = std::max(ssta, send);
1279
{ // this is the result of a next/previous search
1280
// rebase depending on the cursor position
1281
ssta = data->SearchInSelectionStart;
1282
send = data->SearchInSelectionEnd;
1283
if(cpos < ssta || cpos > send)
1284
{ // regular reset (this also provide some sort of wrap around) (other editors also did it like that)
1291
data->end = (data->directionDown)?send:ssta;
1297
{ // searching direction down, entire scope
1298
//Replace needs the entire scope, while find can wrap around.
1299
data->start = ( replace ? 0 : control->GetCurrentPos() );
1300
data->end = control->GetLength();
1302
} // end of CalculateFindReplaceStartEnd
1304
int EditorManager::Replace(cbStyledTextCtrl* control, cbFindReplaceData* data)
1306
if (!control || !data)
1309
bool AdvRegex=false;
1313
CalculateFindReplaceStartEnd(control, data);
1315
if (data->matchWord)
1316
flags |= wxSCI_FIND_WHOLEWORD;
1317
if (data->startWord)
1318
flags |= wxSCI_FIND_WORDSTART;
1319
if (data->matchCase)
1320
flags |= wxSCI_FIND_MATCHCASE;
1323
flags |= wxSCI_FIND_REGEXP;
1324
if (Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/use_posix_style_regexes"), false))
1325
flags |= wxSCI_FIND_POSIX;
1326
#ifdef wxHAS_REGEX_ADVANCED
1327
AdvRegex=Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/use_advanced_regexes"), false);
1332
#ifdef wxHAS_REGEX_ADVANCED
1336
re.Compile(data->findText,wxRE_ADVANCED|wxRE_NEWLINE);
1338
re.Compile(data->findText,wxRE_ADVANCED|wxRE_NEWLINE|wxRE_ICASE);
1342
control->BeginUndoAction();
1344
bool replace = false;
1345
bool confirm = true;
1347
wxPoint LastDlgPosition;
1348
bool HaveLastDlgPosition = false;
1349
bool wrapAround = false;
1350
int data_start_initial = data->start;
1354
int lengthFound = 0;
1356
pos = control->FindText(data->start, data->end, data->findText, flags, &lengthFound);
1359
wxString text=control->GetTextRange(data->start, data->end);
1360
if(re.Matches(text))
1363
re.GetMatch(&start,&len,0);
1364
pos=start+data->start;
1366
if(start==0&&len==0) //For searches for "^" or "$" (and null returning variants on this) need to make sure we have forward progress and not simply matching on a previous BOL/EOL find
1369
if(re.Matches(text))
1372
re.GetMatch(&start,&len,0);
1373
pos=start+data->start+1;
1381
if (pos != -1 && data->start!=data->end)
1383
control->GotoPos(pos);
1384
control->EnsureVisible(control->LineFromPosition(pos));
1385
control->SetSelection(pos, pos + lengthFound);
1387
data->initialreplacing = false; // special treatment only necessary the first time
1389
else if (!wrapAround)
1391
if ( (data->scope == 0) && // scope = global text
1392
((data->directionDown && data_start_initial != 0) ||
1393
(!data->directionDown && data_start_initial != control->GetLength())))
1396
if (data->directionDown)
1397
msg = _("Text not found.\nSearch from the start of the document?");
1399
msg = _("Text not found.\nSearch from the end of the document?");
1401
bool auto_wrap_around = data->autoWrapSearch;
1402
if (auto_wrap_around)
1404
if (auto_wrap_around || cbMessageBox(msg, _("Result"), wxOK | wxCANCEL | wxICON_QUESTION) == wxID_OK)
1406
data->end = data_start_initial;
1407
data->start = (data->directionDown)? 0 : control->GetLength();
1408
wrapAround = true; // signal the wrap-around
1412
break; // done - user doesn't want to wrap around
1415
break; // done - we're replacing in a selection of text
1418
break; // done - already wrapped around once
1424
ConfirmReplaceDlg dlg(Manager::Get()->GetAppWindow());
1425
// dlg.CalcPosition(control);
1426
// TODO (thomas#1#): Check whether the existing code actually works with twin view
1427
// else, we need something like:
1428
// PlaceWindow(&dlg, pdlRelative);
1430
// NOTE (Tiwag#1#): dlg.CalcPosition doesn't work for me with dual monitor setup,
1431
// workaround : remember last dialog position, user can position
1432
// it outside of text where he wants
1433
// Move dialog to last position if already available,
1434
// else place it according to environments settings
1435
if ( HaveLastDlgPosition )
1436
dlg.Move(LastDlgPosition);
1438
dlg.CalcPosition(control);
1440
int ans = dlg.ShowModal();
1441
LastDlgPosition = dlg.GetPosition();
1442
HaveLastDlgPosition = true;
1465
int lengthReplace = data->replaceText.Length();
1469
// set target same as selection
1470
control->SetTargetStart(control->GetSelectionStart());
1471
control->SetTargetEnd(control->GetSelectionEnd());
1474
wxString text=control->GetSelectedText();
1475
re.Replace(&text,data->replaceText,1);
1476
lengthReplace=text.Len();
1477
control->ReplaceSelection(text);
1480
// replace with regEx support
1481
lengthReplace = control->ReplaceTargetRE(data->replaceText);
1484
control->SetTargetStart(0);
1485
control->SetTargetEnd(0);
1488
control->ReplaceSelection(data->replaceText);
1489
if (data->directionDown)
1491
data->start += lengthReplace;
1493
// adjust end pos by adding the length difference between find and replace strings
1494
int diff = lengthReplace - lengthFound;
1495
if (data->directionDown)
1501
if(data->end < diff)
1512
if (data->directionDown)
1513
data->start += lengthFound;
1515
data->start -= lengthFound;
1519
control->EndUndoAction();
1521
if (foundcount == 0)
1522
msg = _T("No matches found for \"") + data->findText + _T("\"");
1523
else if (replacecount == 0 && foundcount == 1)
1524
msg = _T("One match found but not replaced");
1526
msg.Printf(_("Replaced %i of %i matches"), replacecount, foundcount);
1527
cbMessageBox(msg, _("Result"), wxICON_INFORMATION);
1528
control->SetSCIFocus(true);
1533
int EditorManager::ReplaceInFiles(cbFindReplaceData* data)
1535
if (!data) return 0;
1536
if (data->findText.IsEmpty()) return 0;
1538
// let's make a list of all the files to search in
1539
wxArrayString filesList;
1541
if (data->scope == 0) // find in project files
1543
// fill the search list with all the project files
1544
cbProject* prj = Manager::Get()->GetProjectManager()->GetActiveProject();
1548
wxString fullpath = _T("");
1549
for (int i = 0; i < prj->GetFilesCount(); ++i)
1551
ProjectFile* pf = prj->GetFile(i);
1554
fullpath = pf->file.GetFullPath();
1555
if (filesList.Index(fullpath) == -1) // avoid adding duplicates
1557
if(wxFileExists(fullpath)) // Does the file exist?
1558
filesList.Add(fullpath);
1563
else if (data->scope == 1) // find in open files
1565
// fill the search list with the open files
1566
for (int i = 0; i < m_pNotebook->GetPageCount(); ++i)
1568
cbEditor* ed = InternalGetBuiltinEditor(i);
1570
filesList.Add(ed->GetFilename());
1573
else if (data->scope == 2) // find in workspace
1575
// loop over all the projects in the workspace (they are contained in the ProjectManager)
1576
const ProjectsArray* pProjects = Manager::Get()->GetProjectManager()->GetProjects();
1579
int count = pProjects->GetCount();
1580
for (int idxProject = 0; idxProject < count; ++idxProject)
1582
cbProject* pProject = pProjects->Item(idxProject);
1585
wxString fullpath = _T("");
1586
for (int idxFile = 0; idxFile < pProject->GetFilesCount(); ++idxFile)
1588
ProjectFile* pf = pProject->GetFile(idxFile);
1591
fullpath = pf->file.GetFullPath();
1592
if (filesList.Index(fullpath) == -1) // avoid adding duplicates
1594
if(wxFileExists(fullpath)) // Does the file exist?
1595
filesList.Add(fullpath);
1598
} // end for : idx : idxFile
1600
} // end for : idx : idxProject
1604
// if the list is empty, leave
1605
int filesCount = filesList.GetCount();
1606
if (filesCount == 0)
1608
cbMessageBox(_("No files to search in!"), _("Error"), wxICON_WARNING);
1612
bool AdvRegex=false;
1614
if (data->matchWord)
1615
flags |= wxSCI_FIND_WHOLEWORD;
1616
if (data->startWord)
1617
flags |= wxSCI_FIND_WORDSTART;
1618
if (data->matchCase)
1619
flags |= wxSCI_FIND_MATCHCASE;
1622
flags |= wxSCI_FIND_REGEXP;
1623
if (Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/use_posix_style_regexes"), false))
1624
flags |= wxSCI_FIND_POSIX;
1625
#ifdef wxHAS_REGEX_ADVANCED
1626
AdvRegex=Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/use_advanced_regexes"), false);
1631
#ifdef wxHAS_REGEX_ADVANCED
1635
re.Compile(data->findText,wxRE_ADVANCED|wxRE_NEWLINE);
1637
re.Compile(data->findText,wxRE_ADVANCED|wxRE_NEWLINE|wxRE_ICASE);
1642
bool replace = false;
1643
bool confirm = true;
1645
bool wholeFile = false;
1649
wxPoint LastDlgPosition;
1650
bool HaveLastDlgPosition = false;
1652
wxProgressDialog* progress = 0;
1653
wxString fileContents;
1654
wxString enc_name = Manager::Get()->GetConfigManager(_T("editor"))->Read(_T("/default_encoding"), wxLocale::GetSystemEncodingName());
1655
wxFontEncoding def_encoding = wxFontMapper::GetEncodingFromName(enc_name);
1657
// keep a copy of the find struct
1658
cbFindReplaceData dataCopy = *data;
1660
for (int i = 0; i<filesCount && !stop; ++i)
1662
cbEditor *ed = NULL;
1663
cbStyledTextCtrl *control = NULL;
1664
bool fileWasNotOpen = false;
1668
if (!progress->Update(i))
1670
if (cbMessageBox(_("Are you sure you want to stop replacing in files?"), _("Confirmation"), wxICON_QUESTION | wxYES_NO) == wxID_YES)
1677
//Check if this file is already open
1678
EditorBase *eb = IsOpen(filesList[i]);
1681
//File was already open
1682
fileWasNotOpen = false;
1684
ed = GetBuiltinEditor(eb);
1685
if (ed) control = ed->GetControl();
1688
//If it's still NULL, open a new editor
1691
wxFile file(filesList[i]);
1692
if (!file.IsOpened())
1694
fileContents = cbReadFileContents(file, def_encoding);
1695
if (fileContents.Find(data->findText) == -1)
1698
//File was not open, i opened it.
1699
fileWasNotOpen = true;
1701
ed = Open(filesList[i]);
1702
if (ed) control = ed->GetControl();
1705
if (!control || !ed)
1708
SetActiveEditor(ed);
1709
control->BeginUndoAction(); //undo
1712
CalculateFindReplaceStartEnd(control, data, true);
1716
if (!all) confirm = true;
1718
//Replace in this file
1719
while(!stop || wholeFile)
1721
int lengthFound = 0;
1723
pos = control->FindText(data->start, data->end, data->findText, flags, &lengthFound);
1726
wxString text=control->GetTextRange(data->start, data->end);
1727
if(re.Matches(text))
1730
re.GetMatch(&start,&len,0);
1731
pos=start+data->start;
1733
if(start==0&&len==0) //For searches for "^" or "$" (and null returning variants on this) need to make sure we have forward progress and not simply matching on a previous BOL/EOL find
1736
if(re.Matches(text))
1739
re.GetMatch(&start,&len,0);
1740
pos=start+data->start+1;
1749
if (pos == -1 || data->start==data->end)
1754
control->GotoPos(pos);
1755
control->EnsureVisible(control->LineFromPosition(pos));
1757
control->SetSelection(pos, pos + lengthFound);
1759
data->initialreplacing = false; // special treatment only necessary the first time
1763
ConfirmReplaceDlg dlg(Manager::Get()->GetAppWindow(), true);
1764
// dlg.CalcPosition(control);
1765
// TODO (thomas#1#): Check whether the existing code actually works with twin view
1766
// else, we need something like:
1767
// PlaceWindow(&dlg, pdlRelative);
1769
// NOTE (Tiwag#1#): dlg.CalcPosition doesn't work for me with dual monitor setup,
1770
// workaround : remember last dialog position, user can position
1771
// it outside of text where he wants
1772
// Move dialog to last position if already available,
1773
// else place it according to environments settings
1774
if ( HaveLastDlgPosition )
1775
dlg.Move(LastDlgPosition);
1777
dlg.CalcPosition(control);
1779
int ans = dlg.ShowModal();
1780
LastDlgPosition = dlg.GetPosition();
1781
HaveLastDlgPosition = true;
1804
// let's create a progress dialog because it might take some time depending on the files count
1805
progress = new wxProgressDialog(_("Replace in files"),
1806
_("Please wait while replacing in files..."),
1808
Manager::Get()->GetAppWindow(),
1809
wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);
1810
PlaceWindow(progress);
1811
// now that we need no confirmation, freeze the app window
1812
Manager::Get()->GetAppWindow()->Freeze();
1824
int lengthReplace = data->replaceText.Length();
1827
// set target same as selection
1828
control->SetTargetStart(control->GetSelectionStart());
1829
control->SetTargetEnd(control->GetSelectionEnd());
1832
wxString text=control->GetSelectedText();
1833
re.Replace(&text,data->replaceText,1);
1834
lengthReplace=text.Len();
1835
control->ReplaceSelection(text);
1838
// replace with regEx support
1839
lengthReplace = control->ReplaceTargetRE(data->replaceText);
1842
control->SetTargetStart(0);
1843
control->SetTargetEnd(0);
1846
control->ReplaceSelection(data->replaceText);
1848
data->start += lengthReplace;
1850
// adjust end pos by adding the length difference
1851
//between find and replace strings
1852
int diff = lengthReplace - lengthFound;
1853
if (data->directionDown)
1860
if (data->directionDown)
1861
data->start += lengthFound;
1863
data->start -= lengthFound;
1868
control->EndUndoAction(); // undo
1870
//If i opened the file and no replacement was made,
1872
if (!ed->GetModified() && fileWasNotOpen)
1876
// if we showed the progress, the app window is frozen; unfreeze it
1878
Manager::Get()->GetAppWindow()->Thaw();
1884
int EditorManager::Find(cbStyledTextCtrl* control, cbFindReplaceData* data)
1886
if (!control || !data)
1889
bool AdvRegex=false;
1891
CalculateFindReplaceStartEnd(control, data);
1893
if (data->matchWord)
1894
flags |= wxSCI_FIND_WHOLEWORD;
1895
if (data->startWord)
1896
flags |= wxSCI_FIND_WORDSTART;
1897
if (data->matchCase)
1898
flags |= wxSCI_FIND_MATCHCASE;
1901
flags |= wxSCI_FIND_REGEXP;
1902
if (Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/use_posix_style_regexes"), false))
1903
flags |= wxSCI_FIND_POSIX;
1904
#ifdef wxHAS_REGEX_ADVANCED
1905
AdvRegex=Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/use_advanced_regexes"), false);
1910
#ifdef wxHAS_REGEX_ADVANCED
1914
re.Compile(data->findText,wxRE_ADVANCED|wxRE_NEWLINE);
1916
re.Compile(data->findText,wxRE_ADVANCED|wxRE_NEWLINE|wxRE_ICASE);
1921
// avoid infinite loop when wrapping search around, eventually crashing WinLogon O.O
1922
bool wrapAround = false;
1924
int EndPos = control->GetLength();
1925
if(data->scope == 1)
1927
StartPos = data->SearchInSelectionStart;
1928
EndPos = data->SearchInSelectionEnd;
1930
while (true) // loop while not found and user selects to start again from the top
1932
int lengthFound = 0;
1934
pos = control->FindText(data->start, data->end, data->findText, flags, &lengthFound);
1937
wxString text=control->GetTextRange(data->start, data->end);
1938
if(re.Matches(text))
1941
re.GetMatch(&start,&len,0);
1942
pos=start+data->start;
1944
if(start==0&&len==0) //For searches for "^" or "$" (and null returning variants on this) need to make sure we have forward progress and not simply matching on a previous BOL/EOL find
1947
if(re.Matches(text))
1950
re.GetMatch(&start,&len,0);
1951
pos=start+data->start+1;
1959
if (pos != -1 && data->start!=data->end)
1961
int line = control->LineFromPosition(pos);
1962
int onScreen = control->LinesOnScreen() >> 1;
1963
int l1 = line - onScreen;
1964
int l2 = line + onScreen;
1965
for(int l=l1; l<=l2;l+=2) // unfold visible lines on screen
1966
control->EnsureVisible(l);
1967
control->GotoLine(l1); // center selection on screen
1968
control->GotoLine(l2);
1969
control->GotoLine(line);
1970
control->SetSelection(pos, pos + lengthFound);
1971
// Manager::Get()->GetLogManager()->DebugLog("pos=%d, selLen=%d, length=%d", pos, data->end - data->start, lengthFound);
1975
else if (!wrapAround && !data->findInFiles) // for "find in files" we don't want to show messages
1977
if ( (data->directionDown && data->start != StartPos) ||
1978
(!data->directionDown && data->start != EndPos) )
1981
if(!data->scope == 1)
1983
if (data->directionDown)
1984
msg = _("Text not found.\nSearch from the start of the document?");
1986
msg = _("Text not found.\nSearch from the end of the document?");
1990
if (data->directionDown)
1991
msg = _("Text not found.\nSearch from the start of the selection?");
1993
msg = _("Text not found.\nSearch from the end of the selection?");
1996
bool auto_wrap_around = data->autoWrapSearch;
1997
if (auto_wrap_around)
1999
if (auto_wrap_around || cbMessageBox(msg, _("Result"), wxOK | wxCANCEL | wxICON_QUESTION) == wxID_OK)
2001
wrapAround = true; // signal the wrap-around
2002
if(!data->scope == 1)
2004
if (data->directionDown)
2007
data->end = control->GetLength();
2011
data->start = control->GetLength();
2017
if (data->directionDown)
2019
data->start = data->SearchInSelectionStart;
2020
data->end = data->SearchInSelectionEnd;
2024
data->start = data->SearchInSelectionEnd;
2025
data->end = data->SearchInSelectionStart;
2035
msg.Printf(_("Not found: %s"), data->findText.c_str());
2036
cbMessageBox(msg, _("Result"), wxICON_INFORMATION);
2037
control->SetSCIFocus(true);
2041
else if (wrapAround)
2044
msg.Printf(_("Not found: %s"), data->findText.c_str());
2045
cbMessageBox(msg, _("Result"), wxICON_INFORMATION);
2054
int EditorManager::FindInFiles(cbFindReplaceData* data)
2056
if (!data || data->findText.IsEmpty())
2059
// clear old search results
2060
if ( data->delOldSearches )
2062
m_pSearchLog->Clear();
2064
int oldcount = m_pSearchLog->GetItemsCount();
2066
// let's make a list of all the files to search in
2067
wxArrayString filesList;
2069
if (data->scope == 0) // find in project files
2071
// fill the search list with all the project files
2072
cbProject* prj = Manager::Get()->GetProjectManager()->GetActiveProject();
2075
cbMessageBox(_("No project to search in!"), _("Error"), wxICON_WARNING);
2079
wxString fullpath = _T("");
2080
for (int i = 0; i < prj->GetFilesCount(); ++i)
2082
ProjectFile* pf = prj->GetFile(i);
2085
fullpath = pf->file.GetFullPath();
2086
if (filesList.Index(fullpath) == -1) // avoid adding duplicates
2088
if(wxFileExists(fullpath)) // Does the file exist?
2089
filesList.Add(fullpath);
2094
else if (data->scope == 1) // find in open files
2096
// fill the search list with the open files
2097
for (int i = 0; i < m_pNotebook->GetPageCount(); ++i)
2099
cbEditor* ed = InternalGetBuiltinEditor(i);
2101
filesList.Add(ed->GetFilename());
2104
else if (data->scope == 2) // find in custom search path and mask
2106
// fill the search list with the files found under the search path
2107
int flags = wxDIR_FILES |
2108
(data->recursiveSearch ? wxDIR_DIRS : 0) |
2109
(data->hiddenSearch ? wxDIR_HIDDEN : 0);
2110
wxArrayString masks = GetArrayFromString(data->searchMask);
2111
if (!masks.GetCount())
2113
unsigned int count = masks.GetCount();
2115
for (unsigned int i = 0; i < count; ++i)
2117
// wxDir::GetAllFiles() does *not* clear the array, so it suits us just fine ;)
2118
wxDir::GetAllFiles(data->searchPath, &filesList, masks[i], flags);
2121
else if (data->scope == 3) // find in workspace
2123
// loop over all the projects in the workspace (they are contained in the ProjectManager)
2124
const ProjectsArray* pProjects = Manager::Get()->GetProjectManager()->GetProjects();
2127
count = pProjects->GetCount();
2130
cbMessageBox(_("No workspace to search in!"), _("Error"), wxICON_WARNING);
2133
for (int idxProject = 0; idxProject < count; ++idxProject)
2135
cbProject* pProject = pProjects->Item(idxProject);
2138
wxString fullpath = _T("");
2139
for (int idxFile = 0; idxFile < pProject->GetFilesCount(); ++idxFile)
2141
ProjectFile* pf = pProject->GetFile(idxFile);
2144
fullpath = pf->file.GetFullPath();
2145
if (filesList.Index(fullpath) == -1) // avoid adding duplicates
2147
if(wxFileExists(fullpath)) // Does the file exist?
2148
filesList.Add(fullpath);
2151
} // end for : idx : idxFile
2153
} // end for : idx : idxProject
2156
// if the list is empty, leave
2157
if (filesList.GetCount() == 0)
2159
cbMessageBox(_("No files to search in!"), _("Error"), wxICON_WARNING);
2163
// now that list is filled, we'll search
2164
// but first we'll create a hidden cbStyledTextCtrl to do the search for us ;)
2165
cbStyledTextCtrl* control = new cbStyledTextCtrl(m_pNotebook, -1, wxDefaultPosition, wxSize(0, 0));
2166
control->Show(false); //hidden
2168
// let's create a progress dialog because it might take some time depending on the files count
2169
wxProgressDialog* progress = new wxProgressDialog(_("Find in files"),
2170
_("Please wait while searching inside the files..."),
2171
filesList.GetCount(),
2172
Manager::Get()->GetAppWindow(),
2173
wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);
2175
PlaceWindow(progress);
2177
// keep a copy of the find struct
2178
cbFindReplaceData localData = *data;
2180
if ( !data->delOldSearches )
2182
LogSearch(_T("=========="), -1, _T("=== \"") + data->findText + _T("\" ==="));
2188
for (size_t i = 0; i < filesList.GetCount(); ++i)
2190
// update the progress bar
2191
if (!progress->Update(i))
2192
break; // user pressed "Cancel"
2194
// re-initialize the find struct for every file searched
2197
// check if the file is already opened in built-in editor and do search in it
2198
cbEditor* ed = IsBuiltinOpen(filesList[i]);
2200
control->SetText(ed->GetControl()->GetText());
2201
else if (!control->LoadFile(filesList[i])) // else load the file in the control
2203
// LOGSTREAM << _("Failed opening ") << filesList[i] << wxT('\n');
2207
// now search for first occurence
2208
if (Find(control, data) == -1)
2214
int line = control->LineFromPosition(control->GetSelectionStart());
2217
// make the filename relative
2218
wxString filename = filesList[i];
2219
if (filename.StartsWith(data->searchPath))
2221
wxFileName fname(filename);
2222
fname.MakeRelativeTo(data->searchPath);
2223
filename = fname.GetFullPath();
2227
LogSearch(filename, line + 1, control->GetLine(line));
2230
// now loop finding the next occurence
2231
while (FindNext(true, control, data) != -1)
2234
line = control->LineFromPosition(control->GetSelectionStart());
2235
if(line == lastline) // avoid multiple hits on the same line (try search for "manager")
2239
LogSearch(filename, line + 1, control->GetLine(line));
2243
delete control; // done with it
2244
delete progress; // done here too
2248
static_cast<SearchResultsLog*>(m_pSearchLog)->SetBasePath(data->searchPath);
2249
if (Manager::Get()->GetConfigManager(_T("message_manager"))->ReadBool(_T("/auto_show_search"), true))
2251
CodeBlocksLogEvent evtSwitch(cbEVT_SWITCH_TO_LOG_WINDOW, m_pSearchLog);
2252
CodeBlocksLogEvent evtShow(cbEVT_SHOW_LOG_MANAGER);
2254
Manager::Get()->ProcessEvent(evtSwitch);
2255
Manager::Get()->ProcessEvent(evtShow);
2257
static_cast<SearchResultsLog*>(m_pSearchLog)->FocusEntry(oldcount);
2262
if ( data->delOldSearches )
2264
msg.Printf(_("Not found: %s"), data->findText.c_str());
2265
cbMessageBox(msg, _("Result"), wxICON_INFORMATION);
2266
cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
2268
ed->GetControl()->SetSCIFocus(true);
2272
msg.Printf(_("not found in %d files"), filesList.GetCount());
2273
LogSearch(_T(""), -1, msg );
2274
static_cast<SearchResultsLog*>(m_pSearchLog)->FocusEntry(oldcount);
2281
int EditorManager::FindNext(bool goingDown, cbStyledTextCtrl* control, cbFindReplaceData* data)
2285
cbEditor* ed = GetBuiltinEditor(GetActiveEditor());
2287
control = ed->GetControl();
2294
data = m_LastFindReplaceData;
2295
//FindNext/Previous called from Search menu (F3/Shift-F3)
2296
if(data) data->findInFiles = false;
2300
return ShowFindDialog(false, false);
2302
if(!data->findInFiles)
2304
wxString phraseAtCursor = control->GetSelectedText();
2306
if ( not data->findUsesSelectedText )
2307
{ // The mandrav find behavior
2308
// change findText to selected text (if any text is selected and no search text was set before)
2309
if (!phraseAtCursor.IsEmpty() && data->findText.IsEmpty())
2310
data->findText = phraseAtCursor;
2313
{ // The tiwag find behavior
2314
// change findText to selected text (if any text is selected)
2315
if (!phraseAtCursor.IsEmpty())
2317
data->findText = phraseAtCursor;
2318
data->originEntireScope = false; //search from cursor
2319
data->scope = 0; // global ("selected text" is useful only from Find Dialog)
2324
data->directionDown = goingDown;
2325
return Find(control, data);
2328
void EditorManager::OnGenericContextMenuHandler(wxCommandEvent& event)
2330
EditorBase* eb = GetActiveEditor();
2331
cbEditor* ed = GetBuiltinEditor(eb);
2332
int id = event.GetId();
2334
if (id == idNBTabSplitHorz && ed)
2335
ed->Split(cbEditor::stHorizontal);
2336
else if (id == idNBTabSplitVert && ed)
2337
ed->Split(cbEditor::stVertical);
2338
else if (id == idNBTabUnsplit && ed)
2342
void EditorManager::OnPageChanged(wxFlatNotebookEvent& event)
2344
EditorBase* eb = static_cast<EditorBase*>(m_pNotebook->GetPage(event.GetSelection()));
2345
// LOGSTREAM << wxString::Format(_T("OnPageChanged(): ed=%p, title=%s\n"), eb, eb ? eb->GetTitle().c_str() : _T(""));
2346
CodeBlocksEvent evt(cbEVT_EDITOR_ACTIVATED, -1, 0, eb);
2347
Manager::Get()->GetPluginManager()->NotifyPlugins(evt);
2349
// focus editor on next update event
2350
m_pData->m_SetFocusFlag = true;
2352
event.Skip(); // allow others to process it too
2355
void EditorManager::OnPageChanging(wxFlatNotebookEvent& event)
2357
EditorBase* eb = static_cast<EditorBase*>(m_pNotebook->GetPage(event.GetOldSelection()));
2358
// LOGSTREAM << wxString::Format(_T("OnPageChanging(): ed=%p, title=%s\n"), eb, eb ? eb->GetTitle().c_str() : _T(""));
2359
CodeBlocksEvent evt(cbEVT_EDITOR_DEACTIVATED, -1, 0, eb);
2360
Manager::Get()->GetPluginManager()->NotifyPlugins(evt);
2362
event.Skip(); // allow others to process it too
2365
void EditorManager::OnPageClosing(wxFlatNotebookEvent& event)
2367
EditorBase* eb = static_cast<EditorBase*>(m_pNotebook->GetPage(event.GetSelection()));
2368
// LOGSTREAM << wxString::Format(_T("OnPageClosing(): ed=%p, title=%s\n"), eb, eb ? eb->GetTitle().c_str() : _T(""));
2369
if (!QueryClose(eb))
2371
event.Skip(); // allow others to process it too
2374
void EditorManager::OnPageContextMenu(wxFlatNotebookEvent& event)
2376
if (event.GetSelection() == -1)
2378
wxMenu* pop = new wxMenu;
2379
pop->Append(idNBTabClose, _("Close"));
2380
if (GetEditorsCount() > 1)
2382
pop->Append(idNBTabCloseAll, _("Close all"));
2383
pop->Append(idNBTabCloseAllOthers, _("Close all others"));
2385
pop->AppendSeparator();
2386
pop->Append(idNBTabSave, _("Save"));
2387
pop->Append(idNBTabSaveAll, _("Save all"));
2388
pop->AppendSeparator();
2389
pop->Append(idNBSwapHeaderSource, _("Swap header/source"));
2390
pop->AppendSeparator();
2391
pop->Append(idNBTabTop, _("Tabs at top"));
2392
pop->Append(idNBTabBottom, _("Tabs at bottom"));
2394
cbEditor* ed = GetBuiltinEditor(event.GetSelection());
2397
pop->AppendSeparator();
2398
pop->Append(idNBProperties, _("Properties..."));
2400
wxMenu* splitMenu = new wxMenu;
2401
splitMenu->Append(idNBTabSplitHorz, _("Horizontally"));
2402
splitMenu->Append(idNBTabSplitVert, _("Vertically"));
2403
splitMenu->AppendSeparator();
2404
splitMenu->Append(idNBTabUnsplit, _("Unsplit"));
2405
splitMenu->Enable(idNBTabSplitHorz, ed->GetSplitType() != cbEditor::stHorizontal);
2406
splitMenu->Enable(idNBTabSplitVert, ed->GetSplitType() != cbEditor::stVertical);
2407
splitMenu->Enable(idNBTabUnsplit, ed->GetSplitType() != cbEditor::stNoSplit);
2409
pop->AppendSeparator();
2410
pop->Append(-1, _("Split view"), splitMenu);
2413
bool any_modified = false;
2415
for(int i = 0; i < GetEditorsCount(); ++i)
2417
EditorBase* ed = GetEditor(i);
2418
if (ed && ed->GetModified())
2420
any_modified = true;
2425
pop->Enable(idNBTabSave, GetEditor(event.GetSelection())->GetModified());
2426
pop->Enable(idNBTabSaveAll, any_modified );
2428
m_pNotebook->PopupMenu(pop);
2432
void EditorManager::OnClose(wxCommandEvent& event)
2434
Manager::Get()->GetEditorManager()->Close(GetActiveEditor());
2437
void EditorManager::OnCloseAll(wxCommandEvent& event)
2439
Manager::Get()->GetEditorManager()->CloseAll();
2442
void EditorManager::OnCloseAllOthers(wxCommandEvent& event)
2444
Manager::Get()->GetEditorManager()->CloseAllExcept(GetActiveEditor());
2447
void EditorManager::OnSave(wxCommandEvent& event)
2449
Manager::Get()->GetEditorManager()->Save(m_pNotebook->GetSelection());
2452
void EditorManager::OnSaveAll(wxCommandEvent& event)
2454
Manager::Get()->GetEditorManager()->SaveAll();
2457
void EditorManager::OnSwapHeaderSource(wxCommandEvent& event)
2459
Manager::Get()->GetEditorManager()->SwapActiveHeaderSource();
2462
void EditorManager::OnTabPosition(wxCommandEvent& event)
2464
long style = m_pNotebook->GetWindowStyleFlag();
2465
style &= ~wxFNB_BOTTOM;
2467
if (event.GetId() == idNBTabBottom)
2468
style |= wxFNB_BOTTOM;
2469
m_pNotebook->SetWindowStyleFlag(style);
2470
// (style & wxFNB_BOTTOM) saves info only about the the tabs position
2471
Manager::Get()->GetConfigManager(_T("app"))->Write(_T("/environment/editor_tabs_bottom"), (bool)(style & wxFNB_BOTTOM));
2474
void EditorManager::OnProperties(wxCommandEvent& event)
2476
cbEditor* ed = GetBuiltinActiveEditor();
2477
ProjectFile* pf = 0;
2479
pf = ed->GetProjectFile();
2481
pf->ShowOptions(Manager::Get()->GetAppWindow());
2484
ProjectFileOptionsDlg dlg(Manager::Get()->GetAppWindow(), GetActiveEditor()->GetFilename());
2490
void EditorManager::OnAppDoneStartup(wxCommandEvent& event)
2492
event.Skip(); // allow others to process it too
2495
void EditorManager::OnAppStartShutdown(wxCommandEvent& event)
2497
event.Skip(); // allow others to process it too
2500
void EditorManager::OnCheckForModifiedFiles(wxCommandEvent& event)
2502
CheckForExternallyModifiedFiles();
2505
void EditorManager::HideNotebook()
2510
// m_pNotebook->Hide();
2511
// m_pData->m_NeedsRefresh = false;
2515
void EditorManager::ShowNotebook()
2520
// m_pNotebook->Show();
2521
// m_pData->m_NeedsRefresh = true;
2522
// m_pData->InvalidateTree();
2526
void EditorManager::OnUpdateUI(wxUpdateUIEvent& event)
2528
/* Don't process UpdateUI event when the app is closing.
2529
* It may crash C::B */
2530
if (!Manager::Get()->IsAppShuttingDown() && m_pData->m_SetFocusFlag)
2532
cbEditor* ed = GetBuiltinActiveEditor();
2534
ed->GetControl()->SetFocus();
2535
m_pData->m_SetFocusFlag = false;
2538
// allow other UpdateUI handlers to process this event
2539
// *very* important! don't forget it...
2543
void EditorManager::SetZoom(int zoom)
2548
int EditorManager::GetZoom() const