1 #include "ide.h"
2 
MakeTitle()3 void Ide::MakeTitle()
4 {
5 	String title;
6 	if(!main.IsEmpty())
7 		title << main;
8 	if(!mainconfigname.IsEmpty() &&  mainconfigname == mainconfigparam)
9 		title << " - " << mainconfigname;
10 	else
11 	if(!mainconfigname.IsEmpty()) {
12 		title << " - " << mainconfigname;
13 		title << " ( " << mainconfigparam << " )";
14 	}
15 	if(!title.IsEmpty())
16 		title << " - ";
17 	title << "TheIDE";
18 	if(designer) {
19 		title << " - " << designer->GetFileName();
20 		int cs = designer->GetCharset();
21 		if(cs >= 0)
22 			title << " " << CharsetName(cs);
23 	}
24 	else
25 	if(!editfile.IsEmpty()) {
26 		title << " - " << editfile;
27 		int chrset = editor.GetCharset();
28 		title << " " << IdeCharsetName(chrset)
29 		      << " " << (findarg(Nvl(editfile_line_endings, line_endings), LF, DETECT_LF) >= 0 ? "LF" : "CRLF");
30 		if(editor.IsTruncated())
31 			title << " [Truncated]";
32 		if(editor.IsView())
33 			title << " [View]";
34 		else
35 		if(editor.IsReadOnly())
36 			title << " [Read Only]";
37 		if(editor.IsDirty())
38 			title << " *";
39 	}
40 	if(!IsNull(editfile))
41 		for(int i = 0; i < 10; i++)
42 			if(NormalizePath(editfile) == NormalizePath(bookmark[i].file))
43 				title << Format(" <%d>", i);
44 	title << " { " << GetVarsName() << " }";
45 	if(isscanning)
46 		title << " (scanning files)";
47 	Title(title.ToWString());
48 }
49 
CanToggleReadOnly()50 bool Ide::CanToggleReadOnly()
51 {
52 	return NormalizePath(GetActiveFilePath()) == NormalizePath(editfile) && !editor.IsView();
53 }
54 
ToggleReadOnly()55 void Ide::ToggleReadOnly()
56 {
57 	if(CanToggleReadOnly() && IsActiveFile()) {
58 #ifdef PLATFORM_WIN32
59 		FindFile ff(editfile);
60 		if(ff && ff.IsReadOnly()) {
61 			dword attrib = GetFileAttributes(editfile);
62 			attrib &= ~FILE_ATTRIBUTE_READONLY;
63 			SetFileAttributes(editfile, attrib);
64 		}
65 #endif
66 		editor.SetEditable(editor.IsReadOnly());
67 		ActiveFile().readonly = editor.IsReadOnly();
68 		SavePackage();
69 		MakeTitle();
70 		SetBar();
71 	}
72 }
73 
AdjustMainConfig()74 void Ide::AdjustMainConfig()
75 {
76 	const Workspace& wspc = IdeWorkspace();
77 	if(!wspc.GetCount())
78 		return;
79 	const Array<Package::Config>& f = wspc.GetPackage(0).config;
80 	for(int i = 0; i < f.GetCount(); i++)
81 		if(f[i].param == mainconfigparam)
82 			return;
83 	if(f.GetCount()) {
84 		mainconfigparam = f[0].param;
85 		mainconfigname = f[0].name;
86 		SetMainConfigList();
87 	}
88 }
89 
GetFirstFile()90 String Ide::GetFirstFile()
91 {
92 	const Workspace& wspc = IdeWorkspace();
93 	for(int i = 0; i < wspc.GetCount(); i++) {
94 		const Package& p = wspc.GetPackage(i);
95 		for(int j = 0; j < p.GetCount(); j++)
96 			if(!p[j].separator)
97 				return SourcePath(wspc[i], p[j]);
98 	}
99 	return Null;
100 }
101 
SetMain(const String & package)102 void Ide::SetMain(const String& package)
103 {
104 	FlushFile();
105 	SaveWorkspace();
106 	transferfilecache.Clear();
107 	main = package;
108 	export_dir = GetHomeDirFile(main);
109 	history.Clear();
110 	mainconfigname.Clear();
111 	mainconfigparam.Clear();
112 	ScanWorkspace();
113 	editfile.Clear();
114 	LoadFromFile(THISBACK(SerializeWorkspace), WorkspaceFile());
115 	tabs.FixIcons();
116 	editorsplit.Zoom(0);
117 	String e = editfile;
118 	UpdateFormat();
119 	editfile.Clear();
120 	MakeTitle();
121 	MakeIcon();
122 	SyncMainConfigList();
123 	AdjustMainConfig();
124 	SyncBuildMode();
125 	SetHdependDirs();
126 	SetBar();
127 	HideBottom();
128 	SyncUsc();
129 	if(auto_check)
130 		NewCodeBase();
131 	if(IsNull(e))
132 		e = GetFirstFile();
133 	EditFile(e);
134 }
135 
Exit()136 void Ide::Exit()
137 {
138 	if(debugger)
139 		debugger->Stop();
140 	SaveFile();
141 	SaveWorkspace();
142 	FlushFile();
143 	console.Kill();
144 	Break(IDOK);
145 	IdeExit = true;
146 }
147 
OpenMainPackage()148 bool Ide::OpenMainPackage()
149 {
150 	String version = SplashCtrl::GenerateVersionNumber();
151 	String tt = "Select main package";
152 #ifdef bmYEAR
153 	tt << " (TheIDE " << version
154 	   << Format(" %d-%02d-%02d %d:%02d)", bmYEAR , bmMONTH, bmDAY, bmHOUR, bmMINUTE);
155 #else
156 	tt << " (TheIDE " << version << ')';
157 #endif
158 	String p = SelectPackage(tt, main, true, true);
159 	if(p.IsEmpty()) return false;
160 	main.Clear();
161 	if(!IsOpen())
162 		Open();
163 	SetMain(p);
164 	return true;
165 }
166 
NewMainPackage()167 void Ide::NewMainPackage()
168 {
169 	if(setmain_newide) {
170 		CreateHost(false, false)->Launch(GetExeFilePath() + " --nosplash");
171 	}
172 	else {
173 		SaveCodeBase();
174 		OpenMainPackage();
175 	}
176 }
177 
PackageCursor()178 void Ide::PackageCursor()
179 {
180 	WorkspaceWork::PackageCursor();
181 	String p = GetActivePackage();
182 	if(p.IsEmpty()) return;
183 	String ef = ToLower(NormalizePath(editfile));
184 	for(int i = 0; i < filelist.GetCount(); i++)
185 		if(ToLower(NormalizePath(SourcePath(p, filelist[i]))) == ef) {
186 			filelist.SetCursor(i);
187 			break;
188 		}
189 	SetBar();
190 }
191 
EditWorkspace()192 void Ide::EditWorkspace()
193 {
194 	EditPackages(main, GetActivePackage(), pocfg);
195 	ScanWorkspace();
196 	SyncWorkspace();
197 }
198 
WorkspaceFile()199 String Ide::WorkspaceFile()
200 {
201 	String nm;
202 	for(const char *s = main; *s; s++)
203 		nm.Cat(*s == '\\' || *s == '/' ? '$' : *s);
204 	String cfg = ConfigFile("cfg");
205 	RealizeDirectory(cfg);
206 	return AppendFileName(cfg, ForceExt(nm + '@' + GetVarsName(), ".cfg"));
207 }
208 
SaveWorkspace()209 void Ide::SaveWorkspace()
210 {
211 	if(console.console) return;
212 	if(main.IsEmpty()) return;
213 	StoreToFile(THISBACK(SerializeWorkspace), WorkspaceFile());
214 }
215 
SyncMainConfigList()216 void Ide::SyncMainConfigList()
217 {
218 	mainconfiglist.Clear();
219 	const Workspace& wspc = IdeWorkspace();
220 	if(wspc.GetCount() <= 0) return;
221 	const Array<Package::Config>& f = wspc.GetPackage(0).config;
222 	for(int i = 0; i < f.GetCount(); i++)
223 		mainconfiglist.Add(f[i].param, Nvl(f[i].name, f[i].param));
224 	SetMainConfigList();
225 }
226 
SetMainConfigList()227 void Ide::SetMainConfigList()
228 {
229 	mainconfiglist <<= mainconfigparam;
230 	mainconfigname = mainconfiglist.GetValue();
231 	mainconfiglist.Tip("Main configuration: " + mainconfigparam);
232 }
233 
OnMainConfigList()234 void Ide::OnMainConfigList()
235 {
236 	mainconfigparam = ~mainconfiglist;
237 	SetMainConfigList();
238 	MakeTitle();
239 }
240 
UscFile(const String & file)241 void Ide::UscFile(const String& file)
242 {
243 	try {
244 		ParseUscFile(file);
245 	}
246 	catch(CParser::Error& e) {
247 		ShowConsole();
248 		console << e << "\n";
249 	}
250 }
251 
UscProcessDir(const String & dir)252 void Ide::UscProcessDir(const String& dir)
253 {
254 	for(FindFile ff(AppendFileName(dir, "*.usc")); ff; ff.Next())
255 		UscFile(AppendFileName(dir, ff.GetName()));
256 }
257 
UscProcessDirDeep(const String & dir)258 void Ide::UscProcessDirDeep(const String& dir)
259 {
260 	UscProcessDir(dir);
261 	for(FindFile ff(AppendFileName(dir, "*")); ff; ff.Next())
262 		if(ff.IsFolder())
263 			UscProcessDirDeep(ff.GetPath());
264 }
265 
SyncUsc()266 void Ide::SyncUsc()
267 {
268 	CleanUsc();
269 	UscProcessDir(GetLocalDir());
270 	UscProcessDir(GetFileFolder(ConfigFile("x")));
271 
272 	if(IsNull(main))
273 		return;
274 	::Workspace wspc;
275 	wspc.Scan(main);
276 	int i;
277 	for(i = 0; i < wspc.GetCount(); i++) {
278 		const Package& p = wspc.GetPackage(i);
279 		for(int j = 0; j < p.file.GetCount(); j++) {
280 			String file = SourcePath(wspc[i], p.file[j]);
281 			if(ToLower(GetFileExt(file)) == ".usc")
282 				UscFile(file);
283 		}
284 	}
285 }
286 
CodeBaseSync()287 void Ide::CodeBaseSync()
288 {
289 	if(auto_check)
290 		SyncCodeBase();
291 }
292 
SyncWorkspace()293 void Ide::SyncWorkspace()
294 {
295 	SyncUsc();
296 	CodeBaseSync();
297 }
298 
IsTextFile(const String & file,int maxline)299 bool IsTextFile(const String& file, int maxline) {
300 	byte buffer[16384];
301 	FileIn fi(file);
302 	if(!fi.IsOpen())
303 		return false;
304 	int count = fi.Get(buffer, sizeof(buffer) - 1);
305 	buffer[count] = 0;
306 	const byte *end = buffer + count;
307 	const byte *q = buffer;
308 	const byte *x = q;
309 	while(q < end) {
310 		if(*q < 32) {
311 			int c = *q;
312 			if(c == '\n') {
313 				if(q - x > maxline) return false;
314 				x = q;
315 			}
316 			else
317 			if(c != '\r' && c != '\t' && c != '\v' && c != '\f' && c != 0x1a)
318 				return false;
319 		}
320 		q++;
321 	}
322 	return true;
323 }
324 
325 /*
326 Console& Ide::GetConsole()
327 {
328 	int q = btabs.GetCursor();
329 	return q == BFINDINFILES ? console2 : console;
330 }
331 */
Renumber()332 void Ide::Renumber() {
333 	for(int i = 0; i < filedata.GetCount(); i++)
334 		::Renumber(filedata[i].lineinfo);
335 	editor.Renumber();
336 }
337 
CycleFiles()338 void Ide::CycleFiles()
339 {
340 	if(++tabi >= tablru.GetCount())
341 		tabi = 0;
342 	if(tabi < tablru.GetCount()) {
343 		blocktabs = true;
344 		EditFile(tablru[tabi]);
345 	}
346 }
347 
DeactivateBy(Ctrl * new_focus)348 void Ide::DeactivateBy(Ctrl *new_focus)
349 {
350 	if(deactivate_save && issaving == 0 && !new_focus && editor.GetLength64() < 1000000) {
351 		DeactivationSave(true);
352 		SaveFile();
353 		DeactivationSave(false);
354 	}
355 	TopWindow::DeactivateBy(new_focus);
356 }
357 
Activate()358 void Ide::Activate()
359 {
360 	InvalidateFileTimeCache();
361 	TopWindow::Activate();
362 }
363 
Key(dword key,int count)364 bool Ide::Key(dword key, int count)
365 {
366 	dword *k = IdeKeys::AK_DELLINE().key;
367 	if(key == k[0] || key == k[1]) {
368 		editor.DeleteLine();
369 		return true;
370 	}
371 	k = IdeKeys::AK_CUTLINE().key;
372 	if(key == k[0] || key == k[1]) {
373 		editor.CutLine();
374 		return true;
375 	}
376 	switch(key) {
377 	case K_SHIFT_CTRL_UP:
378 	case K_SHIFT_CTRL_DOWN:
379 	case K_ALT_DELETE:
380 		return filelist.Key(key, count);
381 	case K_ALT_UP:
382 		return filelist.Key(K_UP, 0);
383 	case K_ALT_DOWN:
384 		return filelist.Key(K_DOWN, 0);
385 	case K_ALT_PAGEUP:
386 		return package.Key(K_UP, 0);
387 	case K_ALT_PAGEDOWN:
388 		return package.Key(K_DOWN, 0);
389 	case K_CTRL|K_ALT_LEFT:
390 		TabsLR( TabBar::JumpDirLeft );
391 		return true;
392 	case K_CTRL|K_ALT_RIGHT:
393 		TabsLR( TabBar::JumpDirRight );
394 		return true;
395 	case K_CTRL|K_ALT_B:
396 		TabsStackLR( TabBar::JumpDirLeft );
397 		return true;
398 	case K_CTRL|K_ALT_N:
399 		TabsStackLR( TabBar::JumpDirRight );
400 		return true;
401 	case K_SHIFT|K_CTRL_O:
402 		AddFile(WorkspaceWork::ANY_FILE);
403 		return true;
404 #ifdef PLATFORM_COCOA
405 	case K_ALT_KEY|K_KEYUP:
406 	case K_OPTION_KEY|K_KEYUP:
407 #endif
408 	case K_CTRL_KEY|K_KEYUP:
409 		if(tabi) {
410 			tabi = 0;
411 			AddLru();
412 		}
413 		return true;
414 	case K_CTRL_TAB:
415 #ifdef PLATFORM_COCOA
416 	case K_ALT|K_TAB:
417 	case K_OPTION|K_TAB:
418 #endif
419 		CycleFiles();
420 		return true;
421 	case K_ALT_C|K_SHIFT:
422 		CodeBrowser();
423 		return true;
424 	case K_ALT_RIGHT:
425 	default:
426 		if(key >= K_SHIFT_CTRL_0 && key <= K_SHIFT_CTRL_9) {
427 			Bookmark& b = bookmark[key - K_SHIFT_CTRL_0];
428 			b.file = editfile;
429 			b.pos = editor.GetEditPos();
430 			MakeTitle();
431 			return true;
432 		}
433 		if(key >= K_CTRL_0 && key <= K_CTRL_9) {
434 			GotoBookmark(bookmark[key - K_CTRL_0]);
435 			return true;
436 		}
437 	}
438 	return false;
439 }
440 
GotoBookmark(const Bookmark & b)441 void Ide::GotoBookmark(const Bookmark& b)
442 {
443 	if(b.file.IsEmpty()) return;
444 	EditFile(b.file);
445 	if(bookmark_pos)
446 		editor.SetEditPos(b.pos);
447 }
448 
IsHistDiff(int i)449 bool Ide::IsHistDiff(int i)
450 {
451 	if(i < 0 || i >= history.GetCount())
452 		return false;
453 	Bookmark& b = history[i];
454 	return b.file != editfile || abs(editor.GetCursor64() - b.pos.cursor) > 20;
455 }
456 
IdePaste(String & data)457 void Ide::IdePaste(String& data)
458 {
459 	data.Clear();
460 	if(AcceptFiles(Clipboard())) {
461 		Vector<String> s = GetFiles(Clipboard());
462 		for(int i = 0; i < s.GetCount(); i++)
463 			if(FileExists(s[i]) && IsTextFile(s[i], 10000)) {
464 				int64 len = GetFileLength(s[i]);
465 				if(data.GetLength() + len > 104857600) {
466 					Exclamation("The paste size breaks the 100MB limit.");
467 					return;
468 				}
469 				data.Cat(LoadFile(s[i]));
470 			}
471 	}
472 }
473 
AddHistory()474 void Ide::AddHistory()
475 {
476 	if(history.GetCount()) {
477 		if(IsHistDiff(histi))
478 			++histi;
479 	}
480 	else
481 		histi = 0;
482 	history.At(histi);
483 	Bookmark& b = history.Top();
484 	b.file = editfile;
485 	b.pos = editor.GetEditPos();
486 	SetBar();
487 }
488 
EditorEdit()489 void Ide::EditorEdit()
490 {
491 	AddHistory();
492 	TouchFile(editfile);
493 }
494 
GetHistory(int d)495 int  Ide::GetHistory(int d)
496 {
497 	if(history.GetCount())
498 		for(int i = histi + (d > 0); i >= 0 && i < history.GetCount(); i += d)
499 			if(IsHistDiff(i))
500 				return i;
501 	return -1;
502 }
503 
History(int d)504 void Ide::History(int d)
505 {
506 	int i = GetHistory(d);
507 	if(i >= 0) {
508 		histi = i;
509 		GotoBookmark(history[histi]);
510 		SetBar();
511 	}
512 }
513 
BookKey(int key)514 void Ide::BookKey(int key)
515 {
516 	Key(key, 1);
517 }
518 
DoDisplay()519 void Ide::DoDisplay()
520 {
521 	Point p = editor.GetColumnLine(editor.GetCursor64());
522 	String s;
523 	s << "Ln " << p.y + 1 << ", Col " << p.x + 1;
524 	int64 l, h;
525 	editor.GetSelection(l, h);
526 	if(h > l)
527 		s << ", Sel " << h - l;
528 	display.SetLabel(s);
529 
530 	ManageDisplayVisibility();
531 }
532 
ManageDisplayVisibility()533 void Ide::ManageDisplayVisibility()
534 {
535 	display.Show(!designer);
536 }
537 
SetIdeState(int newstate)538 void Ide::SetIdeState(int newstate)
539 {
540 	if(newstate != idestate)
541 	{
542 		if(newstate == BUILDING)
543 			build_start_time = GetSysTime();
544 		else
545 		{
546 			if(idestate == BUILDING && !IsNull(build_start_time))
547 				stat_build_time += int(GetSysTime() - build_start_time);
548 			build_start_time = Null;
549 		}
550 	}
551 	idestate = newstate;
552 	MakeTitle();
553 	SetBar();
554 }
555 
MakeIcon()556 void Ide::MakeIcon() {
557 	Image li = IdeImg::PackageLarge2();
558 	WString mp = main.ToWString();
559 	if(!IsNull(mp))
560 	{
561 		Size isz = li.GetSize();
562 		ImageDraw idraw(isz);
563 		Draw& mdraw = idraw.Alpha();
564 		idraw.DrawImage(0, 0, li);
565 		mdraw.DrawImage(0, 0, li, White);
566 		int fh = DPI(14);
567 		Size sz(0, 0);
568 		Font font;
569 		while(fh > DPI(8)) {
570 			font = StdFont(fh);
571 			sz = GetTextSize(mp, font) + Size(4, 2);
572 			if(sz.cx <= isz.cx)
573 				break;
574 			fh--;
575 		}
576 		int x = max((isz.cx - sz.cx) / 2, 0);
577 		int y = isz.cy - sz.cy;
578 		idraw.DrawRect(x, y, sz.cx, sz.cy, White);
579 		mdraw.DrawRect(x, y, sz.cx, sz.cy, White);
580 		idraw.DrawText(x + 2, y + 1, mp, font, Black);
581 		DrawFrame(idraw, x, y, sz.cx, sz.cy, LtBlue);
582 		if(state_icon)
583 			idraw.DrawImage(0, 0, decode(state_icon, 1, IdeImg::IconDebuggingLarge2(),
584 			                                         2, IdeImg::IconRunningLarge2(),
585 			                                         IdeImg::IconBuildingLarge2()));
586 		li = idraw;
587 	}
588 	LargeIcon(li);
589 }
590 
SetIcon()591 void Ide::SetIcon()
592 {
593 	int new_state_icon = 0;
594 	if((bool)debugger && !IdeIsDebugLock()) {
595 		new_state_icon = 1;
596 		return;
597 	}
598 	else
599 	if((GetTimeClick() / 800) & 1) {
600 		if(debugger)
601 			new_state_icon = 2;
602 		else
603 		if(idestate == BUILDING)
604 			new_state_icon = 3;
605 	}
606 	if(state_icon == new_state_icon)
607 		return;
608 	state_icon = new_state_icon;
609 	MakeIcon();
610 #ifdef PLATFORM_WIN32
611 	switch(state_icon) {
612 	case 1:  Icon(DPI(IdeImg::IconDebugging(), IdeImg::IconDebuggingLarge())); break;
613 	case 2:  Icon(DPI(IdeImg::IconRunning(), IdeImg::IconRunningLarge())); break;
614 	case 3:  Icon(DPI(IdeImg::IconBuilding(), IdeImg::IconBuildingLarge())); break;
615 	default: Icon(DPI(IdeImg::Icon(), IdeImg::PackageLarge()));
616 	}
617 #else
618 	switch(state_icon) {
619 	case 1:  Icon(IdeImg::IconDebugging()); break;
620 	case 2:  Icon(IdeImg::IconRunning()); break;
621 	case 3:  Icon(IdeImg::IconBuilding()); break;
622 	default: Icon(IdeImg::Icon());
623 	}
624 #endif
625 }
626 
Periodic()627 void Ide::Periodic()
628 {
629 	CheckFileUpdate();
630 	SetIcon();
631 	if(debugger && debugger->IsFinished() && !IdeIsDebugLock())
632 		IdeEndDebug();
633 	if(file_scanned) {
634 		EditFileAssistSync2();
635 		file_scanned = false;
636 	}
637 }
638 
IdeWorkspace() const639 const Workspace& Ide::IdeWorkspace() const
640 {
641 	static Workspace wspc;
642 	static String _main;
643 	if(main != _main || wspc.GetCount() == 0) {
644 		wspc.Scan(main);
645 		_main = main;
646 	}
647 	else {
648 		for(int i = 0; i < wspc.GetCount(); i++)
649 			if(wspc.GetPackage(i).time != FileGetTime(PackagePath(wspc[i]))) {
650 				wspc.Scan(main);
651 				break;
652 			}
653 	}
654 	return wspc;
655 }
656 
AddPackage(const String & p)657 void Ide::AddPackage(const String& p)
658 {
659 	const Workspace& wspc = IdeWorkspace();
660 	for(int i = 0; i < wspc.GetCount(); i++){
661 		if(wspc[i] == p)
662 			return;
663 	}
664 	if(!PromptOKCancel("Package [* " + p + "] is not yet in the workspace.&Do you want to add it?"))
665 		return;
666 	OptItem& m = actual.uses.Add();
667 	m.text = p;
668 	SaveLoadPackage();
669 }
670 
GetPackageIndex()671 int Ide::GetPackageIndex()
672 {
673 	const Workspace& wspc = IdeWorkspace();
674 	for(int i = 0; i < wspc.GetCount(); i++)
675 		if(wspc[i] == package.GetCurrentName())
676 			return i;
677 	return -1;
678 }
679 
GotoDiffLeft(int line,DiffDlg * df)680 void Ide::GotoDiffLeft(int line, DiffDlg *df)
681 {
682 	EditFile(df->editfile);
683 	editor.SetCursor(editor.GetPos64(line));
684 	editor.SetFocus();
685 }
686 
GotoDiffRight(int line,FileDiff * df)687 void Ide::GotoDiffRight(int line, FileDiff *df)
688 {
689 	EditFile(df->GetExtPath());
690 	editor.SetCursor(editor.GetPos64(line));
691 	editor.SetFocus();
692 }
693 
Diff()694 void Ide::Diff()
695 {
696 	if(IsNull(editfile))
697 		return;
698 	FileDiff diffdlg(AnySourceFs());
699 	diffdlg.diff.WhenLeftLine = THISBACK1(GotoDiffLeft, &diffdlg);
700 	diffdlg.diff.WhenRightLine = THISBACK1(GotoDiffRight, &diffdlg);
701 	diffdlg.Execute(editfile);
702 }
703 
DiffLog()704 void Ide::DiffLog()
705 {
706 	String log_path = GetTargetLogPath();
707 	if(IsNull(editfile) || IsNull(log_path) || max(GetFileLength(editfile), GetFileLength(log_path)) > 100*1024*1024)
708 		return;
709 	FileDiff diffdlg(AnySourceFs());
710 	diffdlg.diff.WhenLeftLine = THISBACK1(GotoDiffLeft, &diffdlg);
711 	diffdlg.diff.WhenRightLine = THISBACK1(GotoDiffRight, &diffdlg);
712 	diffdlg.Execute(editfile, log_path);
713 }
714 
715 struct ConflictDiff : TopWindow {
716 	Label        left, right;
717 	TextDiffCtrl diff;
718 
LayoutConflictDiff719 	virtual void Layout()
720 	{
721 		Size sz = GetSize();
722 		int  fy = GetStdFont().GetCy() + DPI(5);
723 		left.LeftPos(0, sz.cx / 2).TopPos(0, fy);
724 		right.RightPos(0, sz.cx / 2).TopPos(0, fy);
725 		diff.HSizePos().VSizePos(fy, 0);
726 	}
727 
SetConflictDiff728 	void Set(const char *lname, const String& l, const char *rname, const String& r)
729 	{
730 		left = "\1[=* \1" + String(lname);
731 		right = "\1[=* \1" + String(rname);
732 		diff.Set(l, r);
733 	}
734 
ConflictDiffConflictDiff735 	ConflictDiff() {
736 		SetRect(GetWorkArea().Deflated(DPI(32)));
737 		Sizeable().Zoomable();
738 		Add(left);
739 		Add(right);
740 		Add(diff);
741 	}
742 };
743 
LoadConflictFile(const String & n)744 String Ide::LoadConflictFile(const String& n)
745 {
746 	return n.GetCount() == 1 ? GitCmd(GetFileFolder(editfile), "show :" + n + ":./" + GetFileName(editfile))
747 	                         : LoadFile(n);
748 }
749 
DiffFiles(const char * lname,const String & l,const char * rname,const String & r)750 void Ide::DiffFiles(const char *lname, const String& l, const char *rname, const String& r)
751 {
752 	ConflictDiff diff;
753 	diff.Set(lname, LoadConflictFile(l), rname, LoadConflictFile(r));
754 	diff.Execute();
755 }
756 
SvnHistory()757 void Ide::SvnHistory()
758 {
759 	if(IsNull(editfile))
760 		return;
761 	RunRepoDiff(editfile);
762 }
763