1 #include "ide.h"
2
ViewCache()3 String ViewCache()
4 {
5 return ConfigFile("view_maps");
6 }
7
ViewFileHash(const String & path)8 String ViewFileHash(const String& path)
9 {
10 Sha1Stream s;
11 FindFile ff(path);
12 if(ff) {
13 Sha1Stream sha;
14 sha << path << ';' << Time(ff.GetLastWriteTime()) << ';' << ff.GetLength();
15 return AppendFileName(ViewCache(), sha.FinishString());
16 }
17 return Null;
18 }
19
ViewFile(LineEdit & edit,Stream & view_file,const String & path,byte charset)20 void ViewFile(LineEdit& edit, Stream& view_file, const String& path, byte charset)
21 {
22 edit.View(view_file, charset);
23 String f = ViewFileHash(path);
24 if(f.GetCount())
25 LoadFromFile([&](Stream& s) { edit.SerializeViewMap(s); }, f);
26 }
27
CacheViewFile(LineEdit & edit,const String & path)28 void CacheViewFile(LineEdit& edit, const String& path)
29 {
30 if(edit.IsView()) {
31 ReduceCacheFolder(ViewCache(), 20000000);
32 String f = ViewFileHash(path);
33 RealizePath(f);
34 if(f.GetCount())
35 StoreToFile([&](Stream& s) { edit.SerializeViewMap(s); }, f);
36 }
37 }
38
SetupEditor(int f,String hl,String path)39 void Ide::SetupEditor(int f, String hl, String path)
40 {
41 if(IsNull(hl)) {
42 hl = EditorSyntax::GetSyntaxForFilename(path);
43 if(IsNull(hl))
44 hl = EditorSyntax::GetSyntaxForFilename(ToLower(path));
45 if(IsNull(hl) && IsNull(GetFileExt(path))) {
46 FileIn in(path);
47 String h = in.Get(4096);
48 try {
49 CParser p(h);
50 while(!p.IsEof()) {
51 if(p.Char('#')) {
52 if(p.Id("define") || p.Id("ipathdef") || p.Id("ifdef") || p.Id("include") || p.Id("pragma")) {
53 hl = "cpp";
54 break;
55 }
56 }
57 p.SkipTerm();
58 }
59 }
60 catch(CParser::Error) {}
61 }
62 }
63 switch(f) {
64 case 1: editor.SetFont(font1); break;
65 case 2: editor.SetFont(font2); break;
66 case 3: editor.SetFont(consolefont); break;
67 default: editor.SetFont(editorsplit.GetZoom() < 0 && editorsplit.IsHorz() ? veditorfont :
68 hl == "t" ? tfont : editorfont); break;
69 }
70 editor.Highlight(hl);
71
72 editor.WarnWhiteSpace(warnwhitespace &&
73 findarg(hl, "cpp", "java", "js", "cs", "json", "css", "lay", "sch", "t", "usc") >= 0);
74
75 editor.WordwrapComments(wordwrap_comments);
76 editor.WordWrap(wordwrap);
77 }
78
SetupEditor()79 void Ide::SetupEditor()
80 {
81 if(!IsActiveFile())
82 return;
83 Package::File& f = ActiveFile();
84 String p = GetActiveFileName();
85 if(p != HELPNAME)
86 p = GetActiveFilePath();
87 SetupEditor(f.font, f.highlight, p);
88 editor.SyncNavigatorPlacement();
89 }
90
FileCursor()91 void Ide::FileCursor()
92 {
93 WorkspaceWork::FileCursor();
94 if(IsActiveFile() && !filelist[filelist.GetCursor()].isdir) {
95 Package::File& f = ActiveFile();
96 editor.SetEditable(!f.readonly);
97 editor.TabSize(f.tabsize > 0 ? f.tabsize : actual.tabsize > 0 ? actual.tabsize : editortabsize);
98 SetupEditor();
99 String headername;
100 if(insert_include)
101 for(int i = insert_include == 1 ? 0 : filelist.GetCursor();
102 i >= 0 && i < filelist.GetCount();
103 i += insert_include == 1 ? 1 : -1) {
104 String nm = FileName(i);
105 if(!IsSeparator(i) && GetFileExt(nm) == ".h") {
106 headername = nm;
107 break;
108 }
109 }
110 String p = GetActiveFileName();
111 if(p != HELPNAME)
112 p = GetActiveFilePath();
113 EditFile0(p, f.charset ? f.charset : actual.charset ? actual.charset : default_charset,
114 Nvl(f.spellcheck_comments, actual.spellcheck_comments, spellcheck_comments),
115 headername);
116 }
117 }
118
HasFileData(const String & file)119 bool Ide::HasFileData(const String& file)
120 {
121 return filedata.Find(NormalizePath(file)) >= 0;
122 }
123
Filedata(const String & file)124 Ide::FileData& Ide::Filedata(const String& file) {
125 return filedata.GetAdd(NormalizePath(file));
126 }
127
DlCharsetD(DropList & d)128 void DlCharsetD(DropList& d)
129 {
130 d.Add(0, "Default");
131 DlCharset(d);
132 }
133
ChangeFileCharset(const String & name,Package & p,byte charset)134 void Ide::ChangeFileCharset(const String& name, Package& p, byte charset)
135 {
136 if(IsNull(name))
137 return;
138 bool sv = false;
139 for(int i = 0; i < p.file.GetCount(); i++)
140 if(SourcePath(name, p.file[i]) == editfile && p.file[i].charset != charset) {
141 p.file[i].charset = charset;
142 sv = true;
143 }
144 if(sv) {
145 String pp = PackagePathA(name);
146 RealizePath(pp);
147 p.Save(pp);
148 }
149 }
150
FileProperties()151 void Ide::FileProperties()
152 {
153 if(!IsActiveFile()) return;
154 WithFileFormatLayout<TopWindow> d;
155 CtrlLayoutOKCancel(d, "File properties");
156 Package::File& f = ActiveFile();
157 DlCharsetD(d.charset);
158 d.font.Add(0, "Normal");
159 d.font.Add(1, "Small");
160 d.font.Add(2, "Special");
161 d.font.Add(3, "Console");
162 d.highlight.Add(Null, "Default");
163 for(int i = 0; i < EditorSyntax::GetSyntaxCount(); i++) {
164 String desc = EditorSyntax::GetSyntaxDescription(i);
165 if(desc.GetCount())
166 d.highlight.Add(EditorSyntax::GetSyntax(i), desc);
167 }
168 d.highlight.Add("none", "None");
169 d.tabsize <<= f.tabsize > 0 ? f.tabsize : Null;
170 d.tabsize <<= d.Breaker(111);
171 d.tabsize.MinMax(1, 100);
172 d.charset <<= (int)f.charset;
173 d.font <<= f.font;
174 d.font <<= d.Breaker(111);
175 d.highlight <<= f.highlight;
176 d.highlight <<= d.Breaker(111);
177 d.line_endings.Add(CRLF, "CRLF");
178 d.line_endings.Add(LF, "LF");
179 d.line_endings <<= findarg(Nvl(editfile_line_endings, line_endings), LF, DETECT_LF) >= 0 ? LF : CRLF;
180 d.line_endings.Enable(findarg(line_endings, DETECT_CRLF, DETECT_LF) >= 0);
181 d.spellcheck_comments.Add(Null, "Default");
182 DlSpellerLangs(d.spellcheck_comments);
183 d.spellcheck_comments <<= f.spellcheck_comments;
184 for(;;) {
185 switch(d.Run()) {
186 case IDCANCEL:
187 FlushFile();
188 FileCursor();
189 return;
190 case IDOK:
191 int c = filelist.GetCursor();
192 FlushFile();
193 f.charset = (byte)(int)~d.charset;
194 f.tabsize = Nvl((int)~d.tabsize);
195 f.font = Nvl((int)~d.font);
196 f.highlight = ~d.highlight;
197 f.spellcheck_comments = ~d.spellcheck_comments;
198 SavePackage();
199 PackageCursor();
200 filelist.SetCursor(c);
201 editor.ClearUndo();
202 if(editfile_line_endings != ~d.line_endings) {
203 editfile_line_endings = ~d.line_endings;
204 SaveFile(true);
205 }
206 MakeTitle();
207 return;
208 }
209 if(!IsNull(editfile)) {
210 int ts = Nvl((int)~d.tabsize);
211 editor.TabSize(ts ? ts : editortabsize);
212 SetupEditor(Nvl((int)~d.font), ~d.highlight, editfile);
213 }
214 }
215 }
216
ChangeCharset()217 void Ide::ChangeCharset()
218 {
219 if(!IsActiveFile()) return;
220 Package::File& f = ActiveFile();
221 SaveFile();
222 WithCharsetLayout<TopWindow> dlg;
223 CtrlLayoutOKCancel(dlg, "Save file with enconding");
224 DlCharsetD(dlg.charset);
225 dlg.charset <<= editor.GetCharset();
226 dlg.ActiveFocus(dlg.charset);
227 byte cs;
228 for(;;) {
229 if(dlg.Run() != IDOK)
230 return;
231 cs = (byte)(int)~dlg.charset;
232 int q = editor.GetInvalidCharPos(cs);
233 if(q >= 0) {
234 if(PromptYesNo("File contains characters that cannot be saved using selected encoding.&"
235 "Save anyway? (If you choose Yes, they will be replaced with question marks.)"))
236 break;
237 editor.SetCursor(q);
238 return;
239 }
240 else
241 break;
242 }
243 f.charset = cs;
244 SavePackage();
245 editor.SetCharset(f.charset);
246 SaveFile(true);
247 FlushFile();
248 FileCursor();
249 editor.ClearUndo();
250 }
251
PosSync()252 void Ide::PosSync()
253 {
254 if(designer || editfile.IsEmpty())
255 return;
256 for(int i = 0; i < 2; i++)
257 if(PathIsEqual(posfile[i], editfile))
258 editor.SetPtr(posline[i], posimg[i], i);
259 else
260 editor.SetPtr(-1, Image(), i);
261 editor.SyncTip();
262 }
263
IsProjectFile(const String & f) const264 bool Ide::IsProjectFile(const String& f) const
265 {
266 const Workspace& wspc = IdeWorkspace();
267 for(int i = 0; i < wspc.GetCount(); i++) {
268 const Package& pk = wspc.GetPackage(i);
269 String n = wspc[i];
270 for(int i = 0; i < pk.file.GetCount(); i++) {
271 String fn = pk.file[i];
272 String path = SourcePath(n, fn);
273 if(f == path)
274 return true;
275 }
276 }
277 return false;
278 }
279
ConvertTLine(const String & line,int flag)280 String ConvertTLine(const String& line, int flag)
281 {
282 String r;
283 const char *s = line;
284
285 while(*s) {
286 if(*s == '\"') {
287 CParser p(s);
288 p.NoSkipSpaces();
289 try {
290 r.Cat(AsCString(p.ReadOneString(), INT_MAX, NULL, flag));
291 }
292 catch(CParser::Error) {
293 return line;
294 }
295 s = p.GetPtr();
296 }
297 else
298 r.Cat(*s++);
299 }
300 return r;
301 }
302
SaveFile(bool always)303 void Ide::SaveFile(bool always)
304 {
305 issaving++;
306 SaveFile0(always);
307 issaving--;
308 }
309
SaveEditorFile(Stream & out)310 void Ide::SaveEditorFile(Stream& out)
311 {
312 if(GetFileExt(editfile) == ".t") {
313 for(int i = 0; i < editor.GetLineCount(); i++) {
314 if(i) out.PutCrLf();
315 out.Put(ConvertTLine(editor.GetUtf8Line(i), ASCSTRING_OCTALHI));
316 }
317 }
318 else {
319 int le = line_endings;
320 if(le == DETECT_CRLF)
321 le = Nvl(editfile_line_endings, CRLF);
322 if(le == DETECT_LF)
323 le = Nvl(editfile_line_endings, LF);
324 editor.Save(out, editor.GetCharset(), le == LF ? TextCtrl::LE_LF : TextCtrl::LE_CRLF);
325 }
326 }
327
SaveFile0(bool always)328 void Ide::SaveFile0(bool always)
329 {
330 if(designer) {
331 String fn = designer->GetFileName();
332 Time tm = FileGetTime(fn);
333 designer->Save();
334 if(tm != FileGetTime(fn))
335 TouchFile(fn);
336 if(IsProjectFile(fn) && ToUpper(GetFileExt(fn)) == ".LAY")
337 CodeBaseScanFile(fn, auto_check);
338 return;
339 }
340
341 if(editfile.IsEmpty())
342 return;
343
344 FileData& fd = Filedata(editfile);
345 fd.lineinfo = editor.GetLineInfo();
346 fd.lineinforem = editor.GetLineInfoRem();
347 fd.editpos = editor.GetEditPos();
348 fd.columnline = editor.GetColumnLine(fd.editpos.cursor);
349 fd.filetime = edittime;
350 if(editor.IsView())
351 return;
352 if(!editor.IsDirty() && !always)
353 return;
354 TouchFile(editfile);
355 for(;;) {
356 FileOut out(editfile);
357 SaveEditorFile(out);
358 if(!out.IsError())
359 break;
360 int art = Prompt(Ctrl::GetAppName(), CtrlImg::exclamation(),
361 "Unable to save current file.&"
362 "Retry save, ignore it or save file to another location?",
363 "Save as...", "Retry", "Ignore");
364 if(art < 0)
365 break;
366 if(IsDeactivationSave())
367 return;
368 if(art && AnySourceFs().ExecuteSaveAs()) {
369 editfile = AnySourceFs();
370 MakeTitle();
371 PosSync();
372 break;
373 }
374 }
375
376 FindFile ff(editfile);
377 fd.filetime = edittime = ff.GetLastWriteTime();
378
379 if(editor.IsDirty()) {
380 text_updated.Kill();
381 if(IsCppBaseFile())
382 CodeBaseScanFile(editfile, auto_check);
383 }
384
385 editor.ClearDirty();
386
387 if(ToLower(GetFileExt(editfile)) == ".usc")
388 SyncUsc();
389
390 MakeTitle();
391 }
392
FlushFile()393 void Ide::FlushFile() {
394 editor.CloseAssist();
395 SaveFile();
396 CacheViewFile(editor, editfile);
397 editor.assist_active = false;
398 if(designer) {
399 designer->SaveEditPos();
400 designer->DesignerCtrl().SetFrame(NullFrame());
401 if(dynamic_cast<TopicEditor *>(&designer->DesignerCtrl()))
402 RefreshBrowser();
403 }
404 else
405 if(!editfile.IsEmpty())
406 Filedata(editfile).undodata = editor.PickUndoData();
407 editfile.Clear();
408 editfile_repo = NOT_REPO_DIR;
409 editfile_isfolder = false;
410 repo_dirs = RepoDirs(true).GetCount(); // Perhaps not the best place, but should be ok
411 editor.Clear();
412 editor.Disable();
413 editor.SetEditable();
414 editorsplit.Ctrl::Remove();
415 designer.Clear();
416 view_file.Close();
417 SetBar();
418 }
419
CharFilterMacro(int c)420 int CharFilterMacro(int c)
421 {
422 return iscid(c) ? c : '_';
423 }
424
FileRename(const String & nm)425 void Ide::FileRename(const String& nm)
426 {
427 tabs.RenameFile(editfile, nm);
428 }
429
FileRemove()430 bool Ide::FileRemove()
431 {
432 int q = FindIndex(tablru, editfile);
433 if(q >= 0)
434 tablru.Remove(q);
435 q = tabs.GetCursor();
436 if(q >= 0) {
437 tabs.CloseForce(q, false);
438 if(filelist.GetCount())
439 return true;
440 if(tabs.GetCount())
441 TabFile();
442 else {
443 tabs.Refresh();
444 FlushFile();
445 }
446 return false;
447 }
448 return true;
449 }
450
EditFile0(const String & path,byte charset,int spellcheck_comments,const String & headername)451 void Ide::EditFile0(const String& path, byte charset, int spellcheck_comments, const String& headername)
452 {
453 text_updated.Kill();
454
455 AKEditor();
456 editor.CheckEdited(false);
457 editor.CloseAssist();
458 if(path.IsEmpty()) return;
459 FlushFile();
460
461 editfile = path;
462 editor.SetCharset(charset);
463 editor.SpellcheckComments(spellcheck_comments);
464 AddLru();
465
466 editfile_isfolder = IsFolder(editfile) || IsHelpName(editfile);
467 repo_dirs = RepoDirs(true).GetCount(); // Perhaps not the best place, but should be ok
468
469 bool candesigner = !(debugger && !editfile_isfolder && (PathIsEqual(path, posfile[0]) || PathIsEqual(path, posfile[0])))
470 && editastext.Find(path) < 0 && editashex.Find(path) < 0 && !IsNestReadOnly(editfile);
471
472 if(candesigner) {
473 for(int i = 0; i < GetIdeModuleCount() && !designer; i++)
474 designer = GetIdeModule(i).CreateDesigner(this, path, charset);
475 }
476
477 if(!designer && editastext.Find(path) < 0 &&
478 (findarg(GetFileExt(path), ".log") < 0 &&
479 findarg(charset, CHARSET_UTF8_BOM, CHARSET_UTF16_LE, CHARSET_UTF16_BE,
480 CHARSET_UTF16_LE_BOM, CHARSET_UTF16_BE_BOM) < 0 &&
481 FileIsBinary(path) || editashex.Find(path) >= 0))
482 designer.Create<FileHexView>().Open(path);
483
484 ManageDisplayVisibility();
485
486 if(designer) {
487 editpane.Add(designer->DesignerCtrl().SizePos());
488 designer->DesignerCtrl().SetFocus();
489 designer->RestoreEditPos();
490 if(filetabs)
491 tabs.SetAddFile(editfile);
492 MakeTitle();
493 SetBar();
494 editor.SyncNavigatorShow();
495 return;
496 }
497
498 tabs.SetAddFile(editfile);
499 tabs.SetSplitColor(editfile2, Yellow);
500 editor.Enable();
501 editpane.Add(editorsplit);
502 editor.HiliteScope(hilite_scope);
503 editor.OverWriteMode(false);
504 ActiveFocus(editor);
505 FileData& fd = Filedata(editfile);
506 FindFile ff(editfile);
507 bool tfile = GetFileExt(editfile) == ".t";
508 if(ff) {
509 edittime = ff.GetLastWriteTime();
510 if(edittime != fd.filetime || IsNull(fd.filetime))
511 fd.undodata.Clear();
512 view_file.Open(editfile);
513 if(view_file) {
514 if(tfile && editastext.Find(editfile) < 0) {
515 String f;
516 String ln;
517 for(;;) {
518 int c = view_file.Get();
519 if(c < 0) {
520 f.Cat(ConvertTLine(ln, 0));
521 break;
522 }
523 if(c != '\r') {
524 ln.Cat(c);
525 if(c == '\n') {
526 f.Cat(ConvertTLine(ln, 0));
527 ln.Clear();
528 }
529 }
530 }
531 editor.Set(f);
532 editor.SetCharset(CHARSET_UTF8);
533 }
534 else {
535 String s = view_file.Get(3);
536 if(s.GetCount() >= 2) {
537 if((byte)s[0] == 0xff && (byte)s[1] == 0xfe)
538 charset = CHARSET_UTF16_LE_BOM;
539 if((byte)s[0] == 0xfe && (byte)s[1] == 0xff)
540 charset = CHARSET_UTF16_BE_BOM;
541 }
542 if(s.GetCount() >= 3 && (byte)s[0] == 0xef && (byte)s[1] == 0xbb && (byte)s[2] == 0xbf)
543 charset = CHARSET_UTF8_BOM;
544 view_file.Seek(0);
545 int le = Null;
546 #ifdef CPU_64
547 const int64 max_size = (int64)4096*1024*1024;
548 #else
549 const int64 max_size = 768*1024*1024;
550 #endif
551 const int view_limit = 256*1024*1024;
552 if(view_file.GetSize() < view_limit || editastext.Find(editfile) >= 0 && view_file.GetSize() < max_size) {
553 le = editor.Load(view_file, charset);
554 view_file.Close();
555 }
556 else
557 ViewFile(editor, view_file, editfile, charset);
558
559 editfile_line_endings = le == TextCtrl::LE_CRLF ? CRLF : le == TextCtrl::LE_LF ? LF : (int)Null;
560 }
561 }
562 else
563 Exclamation("Failed to read the file.");
564 editor.SetEditPos(fd.editpos);
565 if(!IsNull(fd.columnline) && fd.columnline.y >= 0 && fd.columnline.y < editor.GetLineCount())
566 editor.SetCursor(editor.GetColumnLinePos(fd.columnline));
567 editor.SetEditPosSbOnly(fd.editpos);
568 if(!editor.IsView()) {
569 editor.SetPickUndoData(pick(fd.undodata));
570 editor.SetLineInfo(fd.lineinfo);
571 editor.SetLineInfoRem(pick(fd.lineinforem));
572 }
573 if(ff.IsReadOnly() || IsNestReadOnly(editfile) || editor.IsTruncated() || editor.IsView()) {
574 editor.SetReadOnly();
575 editor.NoShowReadOnly();
576 }
577 fd.ClearP();
578 PosSync();
579 editor.ClearDirty();
580 }
581 else {
582 RealizePath(editfile);
583 if(GetFileExt(editfile) == ".h") {
584 String n = '_' + Filter(String().Cat() << GetActivePackage() << '_' << GetFileTitle(editfile) << "_h_",
585 CharFilterMacro);
586 String s;
587 s << "#ifndef " << n << "\r\n";
588 s << "#define " << n << "\r\n";
589 s << "\r\n";
590 s << "#endif\r\n";
591 editor <<= s;
592 editor.SetCursor(editor.GetPos64(2));
593 }
594 if(IsCSourceFile(editfile) && !IsNull(headername)) {
595 editor <<= "#include \"" + headername + "\"\r\n";
596 editor.SetCursor(editor.GetPos64(1));
597 }
598 editor.SetCharset(tfile ? CHARSET_UTF8 : charset);
599 }
600 editor.SetFocus();
601 MakeTitle();
602 SetBar();
603 editor.SyncNavigatorShow();
604 editor.assist_active = IsProjectFile(editfile) && IsCppBaseFile();
605 editor.CheckEdited(true);
606 editor.Annotate(editfile);
607 editor.SyncNavigator();
608 editfile_repo = GetRepoKind(editfile);
609 editfile_includes = IncludesMD5();
610 }
611
IncludesMD5()612 String Ide::IncludesMD5()
613 { // keep track of includes for Assist++, so that we know when to save file and sync base
614 int n = min(editor.GetLineCount(), 1000); // ignore big files
615 Md5Stream md5;
616 for(int i = 0; i < n; i++) {
617 String l = editor.GetUtf8Line(i);
618 try {
619 CParser p(l);
620 if(p.Char('#') && p.Id("include"))
621 md5.Put(l);
622 }
623 catch(CParser::Error) {}
624 }
625 return md5.FinishString();
626 }
627
ScanFile(bool check_includes)628 void Ide::ScanFile(bool check_includes)
629 {
630 if(IsCppBaseFile()) {
631 if(check_includes) {
632 String imd5 = IncludesMD5();
633 if(editfile_includes != imd5) {
634 editfile_includes = imd5;
635 SaveFile(true);
636 return;
637 }
638 }
639 String s = ~editor;
640 StringStream ss(s);
641 CodeBaseScanFile(ss, editfile);
642 }
643 }
644
EditFileAssistSync2()645 void Ide::EditFileAssistSync2()
646 {
647 editor.Annotate(editfile);
648 editor.SyncNavigator();
649 }
650
EditFileAssistSync()651 void Ide::EditFileAssistSync()
652 {
653 ScanFile(false);
654 EditFileAssistSync2();
655 }
656
TriggerAssistSync()657 void Ide::TriggerAssistSync()
658 {
659 if(auto_rescan && editor.GetLength64() < 500000 && !file_scan) {
660 text_updated.KillSet(1000, [=] {
661 if(!file_scan && IsCppBaseFile()) {
662 String s = ~editor;
663 String fn = editfile;
664 file_scan++;
665 if(!CoWork::TrySchedule([=] {
666 StringStream ss(s);
667 CodeBaseScanFile(ss, editfile);
668 file_scan--;
669 file_scanned = true;
670 }))
671 file_scan--;
672 }
673 });
674 }
675 }
676
EditAsHex()677 void Ide::EditAsHex()
678 {
679 String path = editfile;
680 if(editashex.Find(path) >= 0)
681 return;
682 editastext.RemoveKey(editfile);
683 editashex.FindPut(editfile);
684 byte cs = editor.GetCharset();
685 int sc = editor.GetSpellcheckComments();
686 FlushFile();
687 EditFile0(path, cs, sc);
688 }
689
IsDesignerFile(const String & path)690 bool Ide::IsDesignerFile(const String& path)
691 {
692 for(int i = 0; i < GetIdeModuleCount(); i++)
693 if(GetIdeModule(i).AcceptsFile(path))
694 return true;
695 return false;
696 }
697
DoEditAsText(const String & path)698 void Ide::DoEditAsText(const String& path)
699 {
700 editastext.FindAdd(path);
701 editashex.RemoveKey(editfile);
702 }
703
EditAsText()704 void Ide::EditAsText()
705 {
706 String path = editfile;
707 if(editastext.Find(path) >= 0)
708 return;
709 if(!FileExists(path))
710 return;
711 DoEditAsText(path);
712 byte cs = editor.GetCharset();
713 int sc = editor.GetSpellcheckComments();
714 FlushFile();
715 EditFile0(path, cs, sc);
716 }
717
EditUsingDesigner()718 void Ide::EditUsingDesigner()
719 {
720 String path = editfile;
721 if (editastext.Find(editfile) < 0 && editashex.Find(editfile) < 0)
722 return;
723 editashex.RemoveKey(editfile);
724 editastext.RemoveKey(editfile);
725 byte cs = editor.GetCharset();
726 int sc = editor.GetSpellcheckComments();
727 FlushFile();
728 EditFile0(path, cs, sc);
729 }
730
AddEditFile(const String & path)731 void Ide::AddEditFile(const String& path)
732 {
733 actual.file.AddPick(Package::File(path));
734 if(IsAux())
735 SaveLoadPackageNS(false);
736 else
737 SaveLoadPackage(false);
738 ShowFile(package.GetCount() - 1);
739 filelist.SetCursor(filelist.GetCount() - 1);
740 }
741
EditFile(const String & p)742 void Ide::EditFile(const String& p)
743 {
744 if(p.IsEmpty()) {
745 FlushFile();
746 return;
747 }
748 if(p == HELPNAME) {
749 if(designer && designer->GetFileName() == p)
750 return;
751 package.FindSetCursor(METAPACKAGE);
752 filelist.FindSetCursor(HELPNAME);
753 return;
754 }
755
756 String path = NormalizePath(p);
757 if(designer ? path == designer->GetFileName() : path == editfile)
758 return;
759
760 FlushFile();
761 if(path.IsEmpty())
762 return;
763
764 for(int i = 0; i < package.GetCount(); i++) {
765 String pkg = package[i].name;
766 Package p;
767 p.Load(PackagePathA(pkg));
768 for(int j = 0; j < p.file.GetCount(); j++)
769 if(PathIsEqual(SourcePath(pkg, p.file[j]), path)) {
770 package.FindSetCursor(pkg);
771 ShowFile(j);
772 filelist.FindSetCursor(p.file[j]);
773 return;
774 }
775 if(GetFileExt(path) == ".tpp" && PathIsEqual(SourcePath(pkg, GetFileName(path)), path)) {
776 filelist.KillCursor();
777 package.KillCursor();
778 package.SetCursor(i);
779 AddEditFile(GetFileName(path));
780 return;
781 }
782 }
783 filelist.KillCursor();
784 package.KillCursor();
785 package.SetCursor(package.GetCount() - 2);
786 AddEditFile(path);
787 }
788
IsCppBaseFile()789 bool Ide::IsCppBaseFile()
790 {
791 return IsProjectFile(editfile) && (IsCSourceFile(editfile) || IsCHeaderFile(editfile) ||
792 ToUpper(GetFileExt(editfile)) == ".SCH");
793 }
794
CheckFileUpdate()795 void Ide::CheckFileUpdate()
796 {
797 if(editfile.IsEmpty() || !IsForeground() || designer) return;
798 FindFile ff(editfile);
799 if(!ff) return;
800 FileTime tm = ff.GetLastWriteTime();
801 if(tm == edittime) return;
802 edittime = tm;
803 if(editor.IsDirty() && !Prompt(Ctrl::GetAppName(), CtrlImg::exclamation(),
804 "Current file was changed outside the IDE, but was also edited inside it.&"
805 "Would you like to reload the file or to keep changes made in the IDE ?",
806 "Reload", "Keep")) return;
807
808 if(IsCppBaseFile())
809 CodeBaseScanFile(editfile, auto_check);
810 ReloadFile();
811 }
812
813 typedef Index<dword> HashBase;
814
GetLineIndex(String file,HashBase & hash,Vector<String> & lines)815 static void GetLineIndex(String file, HashBase& hash, Vector<String>& lines)
816 {
817 const char *p = file;
818 while(*p)
819 {
820 while(*p && *p != '\n' && (byte)*p <= ' ')
821 p++;
822 const char *b = p;
823 while(*p && *p++ != '\n')
824 ;
825 const char *e = p;
826 while(e > b && (byte)e[-1] <= ' ')
827 e--;
828 String s(b, e);
829 hash.Add(FoldHash(GetHashValue(s)));
830 lines.Add(s);
831 }
832 }
833
LocateLine(String old_file,int old_line,String new_file)834 int LocateLine(String old_file, int old_line, String new_file)
835 {
836 HashBase old_hash, new_hash;
837 Vector<String> old_lines, new_lines;
838 GetLineIndex(old_file, old_hash, old_lines);
839 GetLineIndex(new_file, new_hash, new_lines);
840 if(old_line <= 0)
841 return 0;
842 if(old_line >= old_lines.GetCount())
843 return new_lines.GetCount();
844 String line = old_lines[old_line];
845 //int hash = old_hash[old_line]; Mirek: unused
846 //int fore_count = old_lines.GetCount() - old_line - 1;
847 int best_match = 0, best_value = 0;
848 for(int r = 0; r < 10 && !best_value; r++)
849 {
850 int src = (r & 1 ? old_line + (r >> 1) + 1 : old_line - (r >> 1));
851 if(src < 0 || src >= old_lines.GetCount())
852 continue;
853 dword hash = old_hash[src];
854 for(int i = new_hash.Find(hash); i >= 0; i = new_hash.FindNext(i))
855 if(new_lines[i] == old_lines[src])
856 {
857 int max_back = min(i, src);
858 int max_fore = min(new_lines.GetCount() - i, old_lines.GetCount() - src) - 1;
859 if(max_back + max_fore <= best_value)
860 continue;
861 int back = 1;
862 while(back <= max_back && new_hash[i - back] == old_hash[src - back]
863 && new_lines[i - back] == old_lines[src - back])
864 back++;
865 int fore = 1;
866 while(fore < max_fore && new_hash[i + fore] == old_hash[src + fore]
867 && new_lines[i + fore] == old_lines[src + fore])
868 fore++;
869 if(back + fore > best_value)
870 {
871 best_value = back + fore;
872 best_match = minmax(i, 0, new_lines.GetCount());
873 }
874 }
875 }
876 return best_match;
877 }
878
ReloadFile()879 void Ide::ReloadFile()
880 {
881 if(editfile.IsEmpty())
882 return;
883 String fn = editfile;
884 String data = ~editor;
885 int ln = editor.GetCursorLine();
886 editfile.Clear();
887 int sc = filelist.GetSbPos();
888 EditFile0(fn, editor.GetCharset(), editor.GetSpellcheckComments());
889 filelist.SetSbPos(sc);
890 int l = LocateLine(data, ln, ~editor);
891 editor.SetCursor(editor.GetPos64(l));
892 }
893
EditAnyFile()894 void Ide::EditAnyFile() {
895 FileSel& fs = AnySourceFs();
896 #if 0
897 fs.Multi(false);
898 if(!fs.ExecuteOpen()) return;
899 EditFile(fs);
900 FileSelected();
901 #endif
902 if(fs.ExecuteOpen())
903 for(int i = 0; i < fs.GetCount(); i++) {
904 EditFile(fs[i]);
905 FileSelected();
906 }
907 }
908
DragAndDrop(Point,PasteClip & d)909 void Ide::DragAndDrop(Point, PasteClip& d)
910 {
911 if(AcceptFiles(d)) {
912 Vector<String> f = GetFiles(d);
913 for(int i = 0; i < f.GetCount(); i++)
914 if(FileExists(f[i])) {
915 EditFile(f[i]);
916 FileSelected();
917 editor.SetFocus();
918 }
919 }
920 }
921
AddLru()922 void Ide::AddLru()
923 {
924 if(editfile.IsEmpty() || tabi) return;
925 LruAdd(tablru, editfile, 200);
926 }
927
sExFiles(const char * fn,const char ** ext,int cnt)928 static String sExFiles(const char *fn, const char **ext, int cnt)
929 {
930 for(int i = 0; i < cnt; i++) {
931 String f = ForceExt(fn, ext[i]);
932 if(FileExists(f))
933 return f;
934 }
935 return Null;
936 }
937
GetOpposite()938 String Ide::GetOpposite()
939 {
940 static const char *cpp[] = { ".c", ".cpp", ".cc", ".cxx" };
941 static const char *hdr[] = { ".h", ".hpp", ".hh", ".hxx" };
942 if(IsNull(editfile) || designer)
943 return Null;
944 String ext = GetFileExt(editfile);
945 for(int i = 0; i < __countof(cpp); i++)
946 if(ext == cpp[i])
947 return sExFiles(editfile, hdr, __countof(hdr));
948 for(int i = 0; i < __countof(hdr); i++)
949 if(ext == hdr[i])
950 return sExFiles(editfile, cpp, __countof(cpp));
951 return Null;
952 }
953
GoOpposite()954 void Ide::GoOpposite()
955 {
956 String fn = GetOpposite();
957 if(!IsNull(fn))
958 EditFile(fn);
959 }
960
PassEditor()961 void Ide::PassEditor()
962 {
963 editorsplit.NoZoom();
964 SyncEditorSplit();
965 SetupEditor();
966 editfile2 = editfile;
967 editor2.SetFont(editor.GetFont());
968 editor2.Highlight(editor.GetHighlight());
969 editor2.LoadHlStyles(editor.StoreHlStyles());
970 editor2.NoShowReadOnly();
971 byte charset = editor.GetCharset();
972 editor2.CheckEdited(false);
973 view_file2.Close();
974 if(editor.IsView()) {
975 view_file2.Open(editfile2);
976 ViewFile(editor2, view_file2, editfile2, charset);
977 }
978 else
979 editor2.Set(editor.Get(charset), charset);
980 editor2.SetEditPosSb(editor.GetEditPos());
981 editor2.CheckEdited();
982 editor.SetFocus();
983 editor.ScrollIntoCursor();
984 editor2.Annotate(editfile2);
985 editor2.SpellcheckComments(editor.GetSpellcheckComments());
986 }
987
ClearEditedFile()988 void Ide::ClearEditedFile()
989 {
990 editor.ClearEdited();
991 }
992
ClearEditedAll()993 void Ide::ClearEditedAll()
994 {
995 ClearEditedFile();
996 for(int i = 0; i < filedata.GetCount(); i++) {
997 LineInfo li = editor.GetLineInfo();
998 LineInfoRem lir = editor.GetLineInfoRem();
999 FileData& fd = Filedata(filedata.GetKey(i));
1000 editor.SetLineInfo(fd.lineinfo);
1001 editor.SetLineInfoRem(pick(fd.lineinforem));
1002 ClearEditedFile();
1003 fd.lineinfo = editor.GetLineInfo();
1004 fd.lineinforem = editor.GetLineInfoRem();
1005 editor.SetLineInfo(li);
1006 }
1007 }
1008
SplitEditor(bool horz)1009 void Ide::SplitEditor(bool horz)
1010 {
1011 if(editorsplit.GetZoom() < 0)
1012 CloseSplit();
1013
1014 if(horz)
1015 editorsplit.Horz(editor2, editor);
1016 else
1017 editorsplit.Vert(editor2, editor);
1018
1019 tabs.SetSplitColor(editfile, Yellow);
1020 PassEditor();
1021 }
1022
SwapEditors()1023 void Ide::SwapEditors()
1024 {
1025 String f = editfile2;
1026 CodeEditor::EditPos p = editor2.GetEditPos();
1027 if(editorsplit.GetFirstChild() == &editor)
1028 if(editorsplit.IsVert())
1029 editorsplit.Vert(editor2, editor);
1030 else
1031 editorsplit.Horz(editor2, editor);
1032 else
1033 if(editorsplit.IsVert())
1034 editorsplit.Vert(editor, editor2);
1035 else
1036 editorsplit.Horz(editor, editor2);
1037 PassEditor();
1038 EditFile(f);
1039 editor.SetEditPos(p);
1040 }
1041
CloseSplit()1042 void Ide::CloseSplit()
1043 {
1044 editorsplit.Vert(editor, editor2);
1045 editorsplit.Zoom(0);
1046 view_file2.Close();
1047 editfile2.Clear();
1048 tabs.ClearSplitColor();
1049 SyncEditorSplit();
1050 editor.SetFocus();
1051 SetupEditor();
1052 }
1053
KeySplit(bool horz)1054 void Ide::KeySplit(bool horz)
1055 {
1056 if(editorsplit.GetZoom() >= 0)
1057 SplitEditor(horz);
1058 else
1059 CloseSplit();
1060 }
1061
SyncEditorSplit()1062 void Ide::SyncEditorSplit()
1063 {
1064 editor.topsbbutton.ScrollStyle().SetMonoImage(IdeImg::split()).Tip("Split (Ctrl+[-])");
1065 editor.topsbbutton <<= THISBACK1(SplitEditor, false);
1066 editor.topsbbutton1.ScrollStyle().SetMonoImage(IdeImg::vsplit()).Tip("Split (Ctrl+[\\])");
1067 editor.topsbbutton1 <<= THISBACK1(SplitEditor, true);
1068 editor2.topsbbutton.ScrollStyle().SetMonoImage(IdeImg::split()).Tip("Split (Ctrl+[-])");
1069 editor2.topsbbutton <<= THISBACK1(SplitEditor, false);
1070 editor2.topsbbutton1.ScrollStyle().SetMonoImage(IdeImg::vsplit()).Tip("Split (Ctrl+[\\])");
1071 editor2.topsbbutton1 <<= THISBACK1(SplitEditor, true);
1072 if(editorsplit.GetZoom() >= 0)
1073 return;
1074 if(editorsplit.IsVert()) {
1075 editor.topsbbutton.ScrollStyle().SetMonoImage(IdeImg::closesplit()).Tip("Close (Ctrl+[-])");
1076 editor.topsbbutton <<= THISBACK(CloseSplit);
1077 editor2.topsbbutton.ScrollStyle().SetMonoImage(IdeImg::closesplit()).Tip("Close (Ctrl+[-])");
1078 editor2.topsbbutton <<= THISBACK(CloseSplit);
1079 }
1080 else {
1081 editor.topsbbutton1.ScrollStyle().SetMonoImage(IdeImg::closesplit()).Tip("Close (Ctrl+[\\])");
1082 editor.topsbbutton1 <<= THISBACK(CloseSplit);
1083 editor2.topsbbutton1.ScrollStyle().SetMonoImage(IdeImg::closesplit()).Tip("Close (Ctrl+[\\])");
1084 editor2.topsbbutton1 <<= THISBACK(CloseSplit);
1085 }
1086 }
1087
HotKey(dword key)1088 bool Ide::HotKey(dword key)
1089 {
1090 if(designer && designer->DesignerCtrl().HotKey(key))
1091 return true;
1092 if(designer && dynamic_cast<FileHexView *>(~designer) && Match(IdeKeys::AK_EDITASHEX, key)) {
1093 EditUsingDesigner();
1094 return true;
1095 }
1096 return TopWindow::HotKey(key);
1097 }
1098
IdeGetFileName()1099 String Ide::IdeGetFileName()
1100 {
1101 return editfile;
1102 }
1103
IdeGetNestFolder()1104 String Ide::IdeGetNestFolder()
1105 {
1106 Vector<String> w = GetUppDirs();
1107 for(int i = 0; i < w.GetCount(); i++)
1108 if(editfile.StartsWith(w[i]))
1109 return w[i];
1110 return Null;
1111 }
1112