1 #include "CodeEditor.h"
2 
3 namespace Upp {
4 
InitFindReplace()5 void CodeEditor::InitFindReplace()
6 {
7 	findreplace.find.AddButton().SetMonoImage(CtrlImg::smallright()).Tip("Wildcard")
8 		<<= THISBACK(FindWildcard);
9 	findreplace.replace.AddButton().SetMonoImage(CtrlImg::smallright()).Tip("Wildcard")
10 		<<= THISBACK(ReplaceWildcard);
11 	PutI(findreplace.find);
12 	PutI(findreplace.replace);
13 	findreplace.amend <<= THISBACK(Replace);
14 	findreplace.amend_all <<= THISBACK1(ReplaceAll, false);
15 	findreplace.amend_rest <<= THISBACK1(ReplaceAll, true);
16 	findreplace.prev <<= THISBACK(DoFindBack);
17 	findreplace.replacing = false;
18 	found = notfoundfw = notfoundbk = foundsel = false;
19 	persistent_find_replace = false;
20 	findreplace.find <<= findreplace.wholeword <<= findreplace.wildcards
21 	                 <<= findreplace.incremental <<= findreplace.regexp
22 	                 <<= findreplace.ignorecase <<= THISBACK(IncrementalFind);
23 	ff_start_pos = -1;
24 	findreplace.find_all << [=] { FindAll(); };
25 }
26 
FindReplaceDlg()27 FindReplaceDlg::FindReplaceDlg()
28 {
29 	find.NoUpDownKeys();
30 	samecase <<= true;
31 	close.Cancel();
32 	prev.SetImage(CtrlImg::SmallUp());
33 	amend.SetImage(CodeEditorImg::Replace());
34 	amend_all.SetImage(CodeEditorImg::ReplaceAll());
35 	amend_rest.SetImage(CodeEditorImg::ReplaceRest());
36 	find_all.SetImage(CodeEditorImg::FindAll());
37 	incremental <<= true;
38 	mode <<= THISBACK(Sync);
39 	mode.Hide();
40 	Sync();
41 
42 	find.NullText("Find");
43 	replace.NullText("Replace");
44 }
45 
Sync()46 void FindReplaceDlg::Sync()
47 {
48 	samecase.Enable(ignorecase);
49 	bool b = !regexp;
50 	wildcards.Enable(b);
51 	prev.Enable(b);
52 	ignorecase.Enable(b);
53 	wholeword.Enable(b);
54 	incremental_from_cursor.Enable(IsIncremental());
55 	b = !mode.IsVisible() || ~mode == 0;
56 	replace.Enable(b);
57 }
58 
59 dword CodeEditor::find_next_key = K_F3;
60 dword CodeEditor::find_prev_key = K_SHIFT_F3;
61 dword CodeEditor::replace_key = K_CTRL_R;
62 
Key(dword key,int cnt)63 bool FindReplaceDlg::Key(dword key, int cnt) {
64 	if(key == K_CTRL_I) {
65 		if(find.HasFocus()) {
66 			find <<= itext;
67 			return true;
68 		}
69 		if(replace.HasFocus()) {
70 			replace <<= itext;
71 			return true;
72 		}
73 	}
74 	if(key == K_ENTER) {
75 		next.WhenAction();
76 		return true;
77 	}
78 	if(findarg(key, K_TAB, K_SHIFT_TAB) >= 0 && replace.IsShown()) {
79 		if(find.HasFocus())
80 			replace.SetFocus();
81 		else
82 			find.SetFocus();
83 		return true;
84 	}
85 	if(key == K_ESCAPE) {
86 		close.WhenAction();
87 		return true;
88 	}
89 	return TopWindow::Key(key, cnt);
90 }
91 
Setup(bool doreplace)92 void FindReplaceDlg::Setup(bool doreplace)
93 {
94 	CtrlLayout(*this);
95 	close.SetImage(CodeEditorImg::Cancel());
96 	close.Tip("Close (Esc)");
97 	close.Normal();
98 	close.SetLabel("");
99 	next.SetImage(CtrlImg::SmallDown());
100 	next.Tip("Find next (" + GetKeyDesc(CodeEditor::find_next_key) + ")");
101 	next.Normal();
102 	next.SetLabel("");
103 	prev.Tip("Find prev (" + GetKeyDesc(CodeEditor::find_prev_key) + ")");
104 	amend.Tip("Replace (" + GetKeyDesc(CodeEditor::replace_key) + ")");
105 	find_all.Show();
106 	find_all.Tip("Find all");
107 	amend.Disable();
108 	replacing = doreplace;
109 	replace.Show(replacing);
110 	amend.Show(replacing);
111 	amend_all.Show(replacing);
112 	amend_rest.Show(replacing);
113 	Height(doreplace ? GetLayoutSize().cy : replace.GetRect().top);
114 	SetFrame(TopSeparatorFrame());
115 	Sync();
116 }
117 
SetFound(int fi,int type,const WString & text)118 void CodeEditor::SetFound(int fi, int type, const WString& text)
119 {
120 	Found& f = foundwild.At(fi);
121 	f.type = type;
122 	f.text = text;
123 }
124 
Match(const wchar * f,const wchar * s,int line,bool we,bool ignorecase,int fi)125 int CodeEditor::Match(const wchar *f, const wchar *s, int line, bool we, bool ignorecase, int fi)
126 {
127 	const wchar *b = s;
128 	int n = 0;
129 	WString ln;
130 	while(*f) {
131 		if(*f == WILDANY) {
132 			f++;
133 			WString wild;
134 			for(;;) {
135 				int nn = Match(f, s, line, we, ignorecase, fi + 1);
136 				if(nn >= 0) {
137 					SetFound(fi, WILDANY, wild);
138 					return int(s - b) + n + nn;
139 				}
140 				wild.Cat(*s ? *s : '\n');
141 				if(!*s++) return -1;
142 			}
143 			return -1;
144 		}
145 		else
146 		if(*f == WILDONE) {
147 			if(!*s) return -1;
148 			SetFound(fi++, WILDONE, WString(*s, 1));
149 			s++;
150 		}
151 		else
152 		if(*f == WILDSPACE) {
153 			const wchar *wb = s;
154 			if(*s != ' ' && *s != '\t') return -1;
155 			s++;
156 			while(*s == ' ' || *s == '\t')
157 				s++;
158 			SetFound(fi++, WILDSPACE, WString(wb, s));
159 		}
160 		else
161 		if(*f == WILDNUMBER) {
162 			const wchar *wb = s;
163 			if(*s < '0' || *s > '9') return -1;
164 			s++;
165 			while(*s >= '0' && *s <= '9')
166 				s++;
167 			SetFound(fi++, WILDNUMBER, WString(wb, s));
168 		}
169 		else
170 		if(*f == WILDID) {
171 			const wchar *wb = s;
172 			if(!iscib(*s)) return -1;
173 			s++;
174 			while(iscidl(*s)) s++;
175 			SetFound(fi++, WILDID, WString(wb, s));
176 		}
177 		else
178 		if(*f == '\n') {
179 			if(*s != '\0' || ++line >= GetLineCount()) return -1;
180 			n += int(s - b) + 1;
181 			ln = GetWLine(line);
182 			s = b = ln;
183 		}
184 		else {
185 			if(ignorecase ? ToUpper(*s) != ToUpper(*f) : *s != *f) return -1;
186 			s++;
187 		}
188 		f++;
189 	}
190 	return we && iscidl(*s) ? -1 : int(s - b) + n;
191 }
192 
Find(bool back,bool block)193 bool CodeEditor::Find(bool back, bool block)
194 {
195 	foundsel = true;
196 	if(notfoundfw) MoveTextBegin();
197 	if(notfoundbk) MoveTextEnd();
198 	foundsel = false;
199 	int64 cursor, pos;
200 	if(found)
201 		GetSelection(pos, cursor);
202 	else
203 		GetSelection(cursor, pos);
204 	pos = cursor;
205 	bool b = FindFrom(pos, back, block);
206 	findreplace.amend.Enable(b);
207 	return b;
208 }
209 
RegExpFind(int64 pos,bool block)210 bool CodeEditor::RegExpFind(int64 pos, bool block)
211 {
212 	RegExp regex((String)~findreplace.find);
213 
214 	int line = GetLinePos64(pos);
215 	String ln = ToUtf8(GetWLine(line).Mid(LimitSize(pos)));
216 	for(;;) {
217 		if(regex.Match(ln)) {
218 			for(int i = 0; i < regex.GetCount(); i++)
219 				SetFound(i, WILDANY, regex.GetString(i).ToWString());
220 			int off = regex.GetOffset();
221 			int len = utf8len(~ln + off, regex.GetLength());
222 			pos = GetPos64(line, utf8len(~ln, off) + (int)pos);
223 			foundtext = GetW(pos, len);
224 			if(!block) {
225 				foundsel = true;
226 				SetSelection(pos, pos + len);
227 				foundsel = false;
228 				CenterCursor();
229 			}
230 			foundpos = pos;
231 			foundsize = len;
232 			found = true;
233 			return true;
234 		}
235 		if(++line >= GetLineCount()) {
236 			WaitView(line);
237 			if(line >= GetLineCount())
238 				return false;
239 		}
240 		ln = GetUtf8Line(line);
241 		pos = 0;
242 		if(!SearchProgress(line))
243 			return false;
244 	}
245 }
246 
FindAll()247 void CodeEditor::FindAll()
248 {
249 	Vector<Tuple<int64, int>> found;
250 	foundpos = 0;
251 	while(FindFrom(foundpos, false, true)) {
252 		found.Add(MakeTuple(foundpos, foundsize));
253 		foundpos += foundsize;
254 		if(found.GetCount() >= 10000) {
255 			Exclamation("Too many matches, only first 10000 will be shown.");
256 			break;
257 		}
258 	}
259 	WhenFindAll(found);
260 }
261 
FindFrom(int64 pos,bool back,bool block)262 bool CodeEditor::FindFrom(int64 pos, bool back, bool block)
263 {
264 	notfoundbk = notfoundfw = false;
265 	if(findreplace.regexp) {
266 		if(RegExpFind(pos, block))
267 			return true;
268 		if(back)
269 			notfoundbk = true;
270 		else
271 			notfoundfw = true;
272 		return false;
273 	}
274 	WString text = ~findreplace.find;
275 	WString ft;
276 	const wchar *s = text;
277 	while(*s) {
278 		int c = *s++;
279 		if(c == '\\') {
280 			c = *s++;
281 			if(c == '\0') break;
282 			if(c == 'n')
283 				ft.Cat('\n');
284 			else
285 			if(c == 't')
286 				ft.Cat('\t');
287 			else
288 			if(c >= ' ')
289 				ft.Cat(c);
290 		}
291 		else
292 		if(c >= ' ') {
293 			if(findreplace.wildcards)
294 				ft.Cat(c == '*' ? WILDANY :
295 					   c == '?' ? WILDONE :
296 					   c == '%' ? WILDSPACE :
297 					   c == '#' ? WILDNUMBER :
298 					   c == '$' ? WILDID :
299 								  c
300 				);
301 			else
302 				ft.Cat(c);
303 		}
304 	}
305 	bool wb = findreplace.wholeword ? iscidl(*ft) : false;
306 	bool we = findreplace.wholeword ? iscidl(*ft.Last()) : false;
307 	if(ft.IsEmpty()) return false;
308 	foundwild.Clear();
309 	int line = GetLinePos64(pos);
310 	WString ln = GetWLine(line);
311 	const wchar *l = ln;
312 	s = l + pos;
313 	bool ignorecase = findreplace.ignorecase;
314 	for(;;) {
315 		for(;;) {
316 			if(!wb || (s == l || !iscidl(s[-1]))) {
317 				int n = Match(ft, s, line, we, ignorecase);
318 				if(n >= 0) {
319 					int64 pos = GetPos64(line, int(s - l));
320 					foundtext = GetW(pos, n);
321 					if(!back || pos + n < cursor) {
322 						if(!block) {
323 							foundsel = true;
324 							SetSelection(pos, pos + n);
325 							foundsel = false;
326 							CenterCursor();
327 						}
328 						foundpos = pos;
329 						foundsize = n;
330 						found = true;
331 						return true;
332 					}
333 				}
334 			}
335 			if(back) {
336 				if(s-- == l) break;
337 			}
338 			else
339 				if(!*s++) break;
340 		}
341 		if(back) {
342 			if(--line < 0) break;
343 			ln = GetWLine(line);
344 			l = ln;
345 			s = ln.End();
346 		}
347 		else {
348 			if(++line >= GetLineCount()) {
349 				WaitView(line);
350 				if(line >= GetLineCount())
351 					break;
352 			}
353 			ln = GetWLine(line);
354 			l = s = ln;
355 		}
356 		if(!SearchProgress(back ? GetLineCount() - line : line))
357 			break;
358 	}
359 	if(back)
360 		notfoundbk = true;
361 	else
362 		notfoundfw = true;
363 	return false;
364 }
365 
FindReplaceAddHistory()366 void CodeEditor::FindReplaceAddHistory()
367 {
368 	if(!IsNull(findreplace.find))
369 		findreplace.find.AddHistory();
370 	if(!IsNull(findreplace.replace))
371 		findreplace.replace.AddHistory();
372 }
373 
NoFindError()374 void CodeEditor::NoFindError()
375 {
376 	findreplace.find.Error(false);
377 }
378 
NotFound()379 void CodeEditor::NotFound()
380 {
381 	findreplace.find.Error();
382 	if(!findreplace.IsIncremental())
383 		SetFocus();
384 	findreplace.amend.Disable();
385 }
386 
Find(bool back,bool blockreplace,bool replace)387 bool CodeEditor::Find(bool back, bool blockreplace, bool replace)
388 {
389 	NoFindError();
390 	if(Find(back, blockreplace)) {
391 		if(!blockreplace) {
392 			if(!findreplace.IsOpen())
393 				OpenNormalFindReplace(replace);
394 			if(!findreplace.IsIncremental())
395 				SetFocus();
396 		}
397 		return true;
398 	}
399 	else {
400 		NotFound();
401 		return false;
402 	}
403 }
404 
GetWild(int type,int & i)405 WString CodeEditor::GetWild(int type, int& i)
406 {
407 	for(;;) {
408 		if(i >= foundwild.GetCount()) break;
409 		Found& f = foundwild[i++];
410 		if(f.type == type) return f.text;
411 	}
412 	for(int j = 0; j < foundwild.GetCount(); j++) {
413 		Found& f = foundwild[j++];
414 		if(f.type == type) return f.text;
415 	}
416 	return WString();
417 }
418 
GetReplaceText()419 WString CodeEditor::GetReplaceText()
420 {
421 	WString rs = ~findreplace.replace;
422 	bool wildcards = findreplace.wildcards;
423 	bool samecase = findreplace.ignorecase && findreplace.samecase;
424 
425 	int anyi = 0, onei = 0, spacei = 0, numberi = 0, idi = 0;
426 	WString rt;
427 	const wchar *s = rs;
428 	while(*s) {
429 		int c = *s++;
430 		if(c == '\\') {
431 			c = *s++;
432 			if(c == '\0') break;
433 			if(c == 'n')
434 				rt.Cat('\n');
435 			else
436 			if(c == 't')
437 				rt.Cat('\t');
438 			else
439 			if(c >= ' ')
440 				rt.Cat(c);
441 		}
442 		else
443 		if(c >= ' ') {
444 			if(wildcards) {
445 				WString w;
446 				if(c == '*')
447 					w = GetWild(WILDANY, anyi);
448 				else
449 				if(c == '?')
450 					w = GetWild(WILDONE, onei);
451 				else
452 				if(c == '%')
453 					w = GetWild(WILDSPACE, spacei);
454 				else
455 				if(c == '#')
456 					w = GetWild(WILDNUMBER, numberi);
457 				else
458 				if(c == '$')
459 					w = GetWild(WILDID, idi);
460 				else
461 				if(c == '@') {
462 					c = *s++;
463 					if(c == '\0') break;
464 					if(c == '@') {
465 						rt << AsString(replacei).ToWString();
466 						continue;
467 					}
468 					if(c == '#') {
469 						rt << AsString(replacei + 1).ToWString();
470 						continue;
471 					}
472 					if(c >= '1' && c <= '9') {
473 						c -= '1';
474 						w = c < foundwild.GetCount() ? foundwild[c].text : WString();
475 					}
476 					else {
477 						rt.Cat('@');
478 						if(c >= ' ' && c < 255) rt.Cat(c);
479 						continue;
480 					}
481 				}
482 				else {
483 					rt.Cat(c);
484 					continue;
485 				}
486 				if(*s == '+') {
487 					w = ToUpper(w);
488 					s++;
489 				}
490 				else
491 				if(*s == '-') {
492 					w = ToLower(w);
493 					s++;
494 				}
495 				else
496 				if(*s == '!') {
497 					w = InitCaps(w);
498 					s++;
499 				}
500 				rt.Cat(w);
501 			}
502 			else
503 				rt.Cat(c);
504 		}
505 	}
506 	if(samecase) {
507 		if(foundtext.GetCount() && rt.GetCount()) {
508 			if(IsUpper(foundtext[0]))
509 				rt.Set(0, ToUpper(rt[0]));
510 			if(IsLower(foundtext[0]))
511 				rt.Set(0, ToLower(rt[0]));
512 		}
513 		if(foundtext.GetCount() > 1) {
514 			if(IsUpper(foundtext[1]))
515 				for(int i = 1; i < rt.GetCount(); i++)
516 					rt.Set(i, ToUpper(rt[i]));
517 			if(IsLower(foundtext[1]))
518 				for(int i = 1; i < rt.GetCount(); i++)
519 					rt.Set(i, ToLower(rt[i]));
520 		}
521 	}
522 	replacei++;
523 	return rt;
524 }
525 
Replace()526 void CodeEditor::Replace()
527 {
528 	if(!findreplace.replacing)
529 		return;
530 	NextUndo();
531 	FindReplaceAddHistory();
532 	if(!found) return;
533 	bool h = persistent_find_replace;
534 	persistent_find_replace = true; // avoid closing of findreplace by selection change
535 	if(RemoveSelection()) {
536 		Paste(GetReplaceText());
537 		Find(false, false, true);
538 	}
539 	persistent_find_replace = h;
540 }
541 
BlockReplace()542 int CodeEditor::BlockReplace()
543 {
544 	NextUndo();
545 	Refresh(); // Setting full-refresh here avoids Pre/Post Remove/Insert costs
546 	int l, h;
547 	if(!GetSelection32(l, h)) return 0;
548 	PlaceCaret(l);
549 	int count = 0;
550 	foundpos = l;
551 	Index<int> ln;
552 	StartSearchProgress(l, h);
553 	while(FindFrom(foundpos, false, true) && foundpos + foundsize <= h) {
554 		CachePos(foundpos);
555 		if(~findreplace.mode == 0) {
556 			Remove((int)foundpos, foundsize);
557 			WString rt = GetReplaceText();
558 			Insert((int)foundpos, rt);
559 			foundpos += rt.GetCount();
560 			h = h - foundsize + rt.GetCount();
561 			count++;
562 		}
563 		else {
564 			ln.FindAdd(GetLine(foundpos));
565 			foundpos += foundsize;
566 		}
567 	}
568 	if(SearchCanceled())
569 		return count;
570 	EndSearchProgress();
571 	Progress pi("Removing lines");
572 	if(~findreplace.mode != 0) {
573 		ClearSelection();
574 		int ll = GetLine(l);
575 		int lh = GetLine(h);
576 		if(GetPos64(lh) == h)
577 			lh--;
578 		bool mm = ~findreplace.mode == 1;
579 		String replace;
580 		pi.SetTotal(lh - ll + 1);
581 		for(int i = ll; i <= lh; i++) {
582 			pi.Set(i, lh - ll + 1);
583 			if(pi.Canceled())
584 				return 0;
585 			if((ln.Find(i) >= 0) == mm) {
586 				replace << GetUtf8Line(i) << "\n";
587 				count++;
588 			}
589 		}
590 		int pos = GetPos32(ll);
591 		Remove(pos, GetPos32(GetLine(h)) - pos);
592 		SetSelection(pos, pos + Insert(pos, replace));
593 	}
594 	else
595 		SetSelection(l, h);
596 	return count;
597 }
598 
OpenNormalFindReplace0(bool replace)599 void CodeEditor::OpenNormalFindReplace0(bool replace)
600 {
601 	findreplace.incremental.Enable(GetLength64() < 2000000);
602 	findreplace.Setup(replace);
603 	findreplace.find_all.Show(!replace && WhenFindAll);
604 	findreplace.itext = GetI();
605 	findreplace.prev.Show();
606 	findreplace.next <<= THISBACK(DoFind);
607 	findreplace.close <<= THISBACK(EscapeFindReplace);
608 	if(!findreplace.IsOpen())
609 		InsertFrame(FindFrame(sb), findreplace);
610 	WhenOpenFindReplace();
611 	findreplace.find.SetFocus();
612 }
613 
OpenNormalFindReplace(bool replace)614 void CodeEditor::OpenNormalFindReplace(bool replace)
615 {
616 	OpenNormalFindReplace0(replace);
617 //	if(!findreplace.incremental_from_cursor)
618 //		IncrementalFind();
619 }
620 
FindReplace(bool pick_selection,bool pick_text,bool replace)621 void CodeEditor::FindReplace(bool pick_selection, bool pick_text, bool replace)
622 {
623 	if(IsReadOnly())
624 		replace = false;
625 
626 	if(findreplace.IsOpen())
627 		CloseFindReplace();
628 
629 	ff_start_pos = GetCursor32();
630 
631 	replacei = 0;
632 	WString find_text;
633 	int find_pos = -1;
634 
635 	findreplace.Setup(replace);
636 
637 	if(pick_text || pick_selection)
638 	{
639 		if(IsSelection()) {
640 			int l, h;
641 			GetSelection32(l, h);
642 			if(h - l < 100) {
643 				find_text = GetSelectionW();
644 				if(find_text.Find('\n') >= 0)
645 					find_text.Clear();
646 			}
647 		}
648 		else
649 		if(pick_text) {
650 			int l, h;
651 			l = h = GetCursor32();
652 			while(l > 0 && CharFilterCIdent(GetChar(l - 1)))
653 				l--;
654 			while(h < GetLength64() && CharFilterCIdent(GetChar(h)))
655 				h++;
656 			find_text = Get(l, h - l).ToWString();
657 			find_pos = h;
658 		}
659 		if(find_text.GetCount())
660 			findreplace.find <<= find_text;
661 	}
662 	if(IsSelection() && replace) {
663 		findreplace.itext = GetI();
664 		SetLayout_BlockReplaceLayout(findreplace);
665 		findreplace.SetRect(WithBlockReplaceLayout<EmptyClass>::GetLayoutSize());
666 		findreplace.Title(t_("Replace in selection"));
667 		findreplace.find_all.Hide();
668 		findreplace.amend.Hide();
669 		findreplace.amend_all.Hide();
670 		findreplace.amend_rest.Hide();
671 		findreplace.prev.Hide();
672 		findreplace.next.Ok() <<= findreplace.Breaker(IDOK);
673 		findreplace.close.Cancel() <<= findreplace.Breaker(IDCANCEL);
674 		findreplace.close.SetImage(Null);
675 		findreplace.close.Tip("");
676 		findreplace.next.SetImage(Null);
677 		findreplace.next.Tip("");
678 		findreplace.mode.Show();
679 		findreplace.mode <<= 0;
680 		findreplace.Sync();
681 		if(findreplace.Execute() == IDOK)
682 			BlockReplace();
683 		findreplace.mode.Hide();
684 	}
685 	else {
686 		if(find_pos >= 0)
687 			SetCursor(find_pos);
688 		OpenNormalFindReplace(replace);
689 		findreplace.find.SetFocus();
690 	}
691 }
692 
ReplaceAll(bool rest)693 void CodeEditor::ReplaceAll(bool rest)
694 {
695 	int l, h;
696 	GetSelection32(l, h);
697 	int c = min(l, h);
698 	findreplace.mode <<= 0;
699 	SetSelection(rest * c, GetLength64());
700 	BlockReplace();
701 	SetCursor(c);
702 }
703 
InsertWildcard(const char * s)704 void CodeEditor::InsertWildcard(const char *s)
705 {
706 	iwc = s;
707 }
708 
FindWildcardMenu(Callback1<const char * > cb,Point p,bool tablf,Ctrl * owner,bool regexp)709 void FindWildcardMenu(Callback1<const char *> cb, Point p, bool tablf, Ctrl *owner, bool regexp)
710 {
711 	MenuBar menu;
712 	if(regexp) {
713 		menu.Add(t_("One or more spaces"), callback1(cb, " +"));
714 		menu.Add(t_("One or more any characters"), callback1(cb, ".+"));
715 		menu.Add(t_("Word"), callback1(cb, "\\w+"));
716 		menu.Add(t_("Number"), callback1(cb, "\\d+"));
717 		menu.Add(t_("Any character"), callback1(cb, "."));
718 		if(tablf) {
719 			menu.Separator();
720 			menu.Add(t_("Tab"), callback1(cb, "\\t"));
721 		}
722 	}
723 	else {
724 		menu.Add(t_("One or more spaces"), callback1(cb, "%"));
725 		menu.Add(t_("One or more any characters"), callback1(cb, "*"));
726 		menu.Add(t_("C++ identifier"), callback1(cb, "$"));
727 		menu.Add(t_("Number"), callback1(cb, "#"));
728 		menu.Add(t_("Any character"), callback1(cb, "?"));
729 		if(tablf) {
730 			menu.Separator();
731 			menu.Add(t_("Tab"), callback1(cb, "\\t"));
732 			menu.Add(t_("Line feed"), callback1(cb, "\\n"));
733 		}
734 	}
735 	menu.Execute(owner, p);
736 }
737 
FindWildcard()738 void CodeEditor::FindWildcard()
739 {
740 	int l, h;
741 	findreplace.find.GetSelection(l, h);
742 	iwc.Clear();
743 	FindWildcardMenu(THISBACK(InsertWildcard), findreplace.find.GetPushScreenRect().TopRight(), true,
744 	                 &findreplace, findreplace.regexp);
745 	if(iwc.GetCount()) {
746 		if(!findreplace.regexp)
747 			findreplace.wildcards = true;
748 		findreplace.find.SetFocus();
749 		findreplace.find.SetSelection(l, h);
750 		findreplace.find.RemoveSelection();
751 		findreplace.find.Insert(iwc);
752 	}
753 }
754 
ReplaceWildcard()755 void CodeEditor::ReplaceWildcard()
756 {
757 	MenuBar menu;
758 	String ptxt;
759 	if(findreplace.regexp)
760 		ptxt = t_("Matched subpattern %d");
761 	else {
762 		menu.Add(t_("Matched spaces"), THISBACK1(InsertWildcard, "%"));
763 		menu.Add(t_("Matched one or more any characters"), THISBACK1(InsertWildcard, "*"));
764 		menu.Add(t_("Matched C++ identifier"), THISBACK1(InsertWildcard, "$"));
765 		menu.Add(t_("Matched number"), THISBACK1(InsertWildcard, "#"));
766 		menu.Add(t_("Matched any character"), THISBACK1(InsertWildcard, "?"));
767 		ptxt = t_("Matched wildcard %d");
768 	}
769 	menu.Add(t_("0-based replace index"), THISBACK1(InsertWildcard, "@@"));
770 	menu.Add(t_("1-based replace index"), THISBACK1(InsertWildcard, "@#"));
771 	menu.Separator();
772 	for(int i = 1; i <= 9; i++)
773 		menu.Add(Format(ptxt, i), THISBACK1(InsertWildcard, "@"+AsString(i)));
774 	menu.Separator();
775 	menu.Add(t_("To upper"), THISBACK1(InsertWildcard, "+"));
776 	menu.Add(t_("To lower"), THISBACK1(InsertWildcard, "-"));
777 	menu.Add(t_("InitCaps"), THISBACK1(InsertWildcard, "!"));
778 	menu.Separator();
779 	menu.Add(t_("Tab"), THISBACK1(InsertWildcard, "\\t"));
780 	menu.Add(t_("Line feed"), THISBACK1(InsertWildcard, "\\n"));
781 	int l, h;
782 	findreplace.replace.GetSelection(l, h);
783 	iwc.Clear();
784 	menu.Execute(&findreplace, findreplace.replace.GetPushScreenRect().TopRight());
785 	if(iwc.GetCount()) {
786 		if(!findreplace.regexp)
787 			findreplace.wildcards = true;
788 		findreplace.replace.SetFocus();
789 		findreplace.replace.SetSelection(l, h);
790 		findreplace.replace.RemoveSelection();
791 		findreplace.replace.Insert(iwc);
792 	}
793 }
794 
CloseFindReplace()795 void CodeEditor::CloseFindReplace()
796 {
797 	if(findreplace.IsOpen()) {
798 		FindReplaceAddHistory();
799 		RemoveFrame(findreplace);
800 	}
801 }
802 
EscapeFindReplace()803 void CodeEditor::EscapeFindReplace()
804 {
805 	CloseFindReplace();
806 	if(ff_start_pos >= 0 && ff_start_pos < GetLength64() && findreplace.IsIncremental() && do_ff_restore_pos) {
807 		SetCursor(ff_start_pos);
808 		ff_start_pos = -1;
809 	}
810 }
811 
IncrementalFind()812 void CodeEditor::IncrementalFind()
813 {
814 	NoFindError();
815 	findreplace.Sync();
816 	if(!findreplace.IsIncremental() || findreplace.GetTopCtrl() == &findreplace) // || we are block replace
817 		return;
818 	bool b = FindFrom(ff_start_pos >= 0 && ff_start_pos < GetLength64()
819 	                  && findreplace.incremental_from_cursor ? ff_start_pos : 0, false, false);
820 	findreplace.amend.Enable(b);
821 	if(!b)
822 		NotFound();
823 }
824 
DoFind()825 void CodeEditor::DoFind()
826 {
827 	Find(false, false);
828 }
829 
DoFindBack()830 void CodeEditor::DoFindBack()
831 {
832 	Find(true, false);
833 }
834 
SerializeFind(Stream & s)835 void CodeEditor::SerializeFind(Stream& s)
836 {
837 	int version = 2;
838 	s / version;
839 	s % findreplace.find;
840 	findreplace.find.SerializeList(s);
841 	s % findreplace.wholeword % findreplace.ignorecase % findreplace.wildcards;
842 	if(version >= 0)
843 		s % findreplace.samecase;
844 	s % findreplace.replace;
845 	if(version >= 1)
846 		s % findreplace.incremental;
847 	if(version >= 2)
848 		s % findreplace.regexp;
849 	findreplace.replace.SerializeList(s);
850 }
851 
ReadList(WithDropChoice<EditString> & e)852 String ReadList(WithDropChoice<EditString>& e)
853 {
854 	StringStream ss;
855 	e.SerializeList(ss);
856 	return ss;
857 }
858 
WriteList(WithDropChoice<EditString> & e,const String & data)859 void WriteList(WithDropChoice<EditString>& e, const String& data)
860 {
861 	StringStream ss(data);
862 	e.SerializeList(ss);
863 }
864 
GetFindReplaceData()865 CodeEditor::FindReplaceData CodeEditor::GetFindReplaceData()
866 {
867 	FindReplaceData r;
868 	r.find = ~findreplace.find;
869 	r.replace = ~findreplace.replace;
870 	r.wholeword = ~findreplace.wholeword;
871 	r.ignorecase = ~findreplace.ignorecase;
872 	r.wildcards = ~findreplace.wildcards;
873 	r.samecase = ~findreplace.samecase;
874 	r.regexp = ~findreplace.regexp;
875 	r.find_list = ReadList(findreplace.find);
876 	r.replace_list = ReadList(findreplace.replace);
877 	return r;
878 }
879 
SetFindReplaceData(const FindReplaceData & r)880 void CodeEditor::SetFindReplaceData(const FindReplaceData& r)
881 {
882 	findreplace.find <<= r.find;
883 	findreplace.replace <<= r.replace;
884 	findreplace.wholeword <<= r.wholeword;
885 	findreplace.ignorecase <<= r.ignorecase;
886 	findreplace.wildcards <<= r.wildcards;
887 	findreplace.samecase <<= r.samecase;
888 	findreplace.regexp <<= r.regexp;
889 	findreplace.mode <<= 0;
890 	WriteList(findreplace.find, r.find_list);
891 	WriteList(findreplace.replace, r.replace_list);
892 }
893 
FindPrevNext(bool prev)894 void CodeEditor::FindPrevNext(bool prev)
895 {
896 	StartSearchProgress(-1, -1);
897 	if(!findreplace.IsOpen()) {
898 		WString find_text;
899 		if(IsSelection()) {
900 			int l, h;
901 			GetSelection32(l, h);
902 			if(h - l < 100) {
903 				find_text = GetSelectionW();
904 				if(find_text.Find('\n') >= 0)
905 					find_text.Clear();
906 			}
907 		}
908 		if(find_text.GetCount())
909 			findreplace.find <<= find_text;
910 		OpenNormalFindReplace0(false);
911 	}
912 	if(Find(prev, false))
913 		NoFindError();
914 	else
915 		NotFound();
916 	EndSearchProgress();
917 }
918 
FindNext()919 void CodeEditor::FindNext()
920 {
921 	FindPrevNext(false);
922 }
923 
FindPrev()924 void CodeEditor::FindPrev()
925 {
926 	FindPrevNext(true);
927 }
928 
StartSearchProgress(int64,int64)929 void CodeEditor::StartSearchProgress(int64, int64)
930 {
931 	search_canceled = false;
932 	search_progress.Create();
933 	search_progress->SetText("Scanning the file");
934 	search_time0 = msecs();
935 }
936 
SearchProgress(int line)937 bool CodeEditor::SearchProgress(int line)
938 {
939 	if(search_progress && !search_canceled && msecs(search_time0) > 20) {
940 		search_time0 = msecs();
941 		search_progress->Create();
942 		search_canceled = IsView() ? search_progress->SetCanceled(int(GetPos64(line) >> 8), int(GetViewSize() >> 8))
943 		                           : search_progress->SetCanceled(line, GetLineCount());
944 	}
945 	return !search_canceled;
946 }
947 
SearchCanceled()948 bool CodeEditor::SearchCanceled()
949 {
950 	return search_canceled;
951 }
952 
EndSearchProgress()953 void CodeEditor::EndSearchProgress()
954 {
955 	search_progress.Clear();
956 	search_canceled = false;
957 }
958 
959 }
960