1 #include "CtrlLib.h"
2 
3 namespace Upp {
4 
5 #define LLOG(x) //  LOG(x)
6 #define LTIMING(x) // DTIMING(x)
7 
LineEdit()8 LineEdit::LineEdit() {
9 	isdrag = false;
10 	nohbar = false;
11 	showtabs = false;
12 	tabsize = 4;
13 	font = CourierZ(12);
14 	SetFrame(ViewFrame());
15 	sb.NoBox();
16 	AddFrame(sb);
17 	sb.WhenScroll = THISBACK(Scroll);
18 	cutline = true;
19 	bordercolumn = -1;
20 	bordercolor = Null;
21 	overwrite = false;
22 	filter = NULL;
23 	showspaces = false;
24 	showlines = false;
25 	showreadonly = true;
26 	dorectsel = false;
27 	hline = vline = Null;
28 	vlinex = 0;
29 	warnwhitespace = false;
30 }
31 
~LineEdit()32 LineEdit::~LineEdit() {}
33 
MouseWheel(Point,int zdelta,dword keyflags)34 void LineEdit::MouseWheel(Point, int zdelta, dword keyflags) {
35 	if(keyflags & K_SHIFT)
36 		sb.WheelX(zdelta);
37 	else
38 		sb.WheelY(zdelta);
39 }
40 
Clear()41 void   LineEdit::Clear() {
42 	gcolumn = 0;
43 	TextCtrl::Clear();
44 	sb.SetTotal(0, 0);
45 	sb.Set(0, 0);
46 	NewScrollPos();
47 	PlaceCaret(0, false);
48 }
49 
TabSize(int n)50 LineEdit& LineEdit::TabSize(int n) {
51 	tabsize = n;
52 	PlaceCaret0(GetColumnLine(cursor));
53 	Refresh();
54 	return *this;
55 }
56 
BorderColumn(int col,Color c)57 LineEdit& LineEdit::BorderColumn(int col, Color c)
58 {
59 	bordercolumn = col;
60 	bordercolor = c;
61 	Refresh();
62 	return *this;
63 }
64 
SetFont(Font f)65 LineEdit& LineEdit::SetFont(Font f) {
66 	font = f;
67 	Layout();
68 	TabSize(tabsize);
69 	SetSb();
70 	return *this;
71 }
72 
GetFontSize() const73 Size LineEdit::GetFontSize() const {
74 	FontInfo fi = font.Info();
75 	return Size(max(fi['M'], fi['W']), fi.GetHeight());
76 }
77 
SetRectSelection(int64 anchor,int64 cursor)78 void LineEdit::SetRectSelection(int64 anchor, int64 cursor)
79 {
80 	dorectsel = true;
81 	SetSelection(anchor, cursor);
82 	dorectsel = false;
83 }
84 
SetRectSelection(const Rect & rect)85 void LineEdit::SetRectSelection(const Rect& rect)
86 {
87 	SetRectSelection(GetGPos(rect.top, rect.left), GetGPos(rect.bottom, rect.right));
88 }
89 
GetRectSelection() const90 Rect LineEdit::GetRectSelection() const
91 {
92 	if(IsRectSelection()) {
93 		int64 sell, selh;
94 		GetSelection(sell, selh);
95 		Rect r(GetColumnLine(sell), GetColumnLine(selh));
96 		if(r.left > r.right)
97 			Swap(r.left, r.right);
98 		return r;
99 	}
100 	return Null;
101 }
102 
GetRectSelection(const Rect & rect,int line,int64 & l,int64 & h)103 bool LineEdit::GetRectSelection(const Rect& rect, int line, int64& l, int64& h)
104 {
105 	if(line >= rect.top && line <= rect.bottom) {
106 		l = GetGPos(line, rect.left);
107 		h = GetGPos(line, rect.right);
108 		return true;
109 	}
110 	l = h = 0;
111 	return false;
112 }
113 
RemoveRectSelection()114 int LineEdit::RemoveRectSelection()
115 {
116 	Rect rect = GetRectSelection();
117 	WString txt;
118 	for(int i = rect.top; i <= rect.bottom; i++) {
119 		int64 l, h;
120 		CacheLinePos(i);
121 		GetRectSelection(rect, i, l, h);
122 		WString s = GetWLine(i);
123 		s.Remove(int(l - GetPos64(i)), int(h - l));
124 		txt.Cat(s);
125 		txt.Cat('\n');
126 	}
127 	int l = GetPos32(rect.top);
128 	int h = GetPos32(rect.bottom) + GetLineLength(rect.bottom);
129 	if(h < GetLength32())
130 		h++;
131 	Remove((int)l, int(h - l));
132 	Insert((int)l, txt);
133 	return (int)GetGPos(rect.bottom, rect.left);
134 }
135 
CopyRectSelection()136 WString LineEdit::CopyRectSelection()
137 {
138 	WString txt;
139 	Rect rect = GetRectSelection();
140 	for(int i = rect.top; i <= rect.bottom && txt.GetCount() < max_total; i++) {
141 		int64 l, h;
142 		CacheLinePos(i);
143 		int64 pos = GetPos64(i);
144 		GetRectSelection(rect, i, l, h);
145 		txt.Cat(GetWLine(i).Mid(int(l - pos), int(h - l)));
146 #ifdef PLATFORM_WIN32
147 		txt.Cat('\r');
148 #endif
149 		txt.Cat('\n');
150 	}
151 	return txt;
152 }
153 
PasteRectSelection(const WString & s)154 int LineEdit::PasteRectSelection(const WString& s)
155 {
156 	Vector<WString> cl = Split(s, '\n', false);
157 	Rect rect = GetRectSelection();
158 	int64 pos = cursor;
159 	int n = 0;
160 	for(int i = 0; i < cl.GetCount() && rect.top + i <= rect.bottom; i++) {
161 		int64 l, h;
162 		CacheLinePos(i);
163 		GetRectSelection(rect, i + rect.top, l, h);
164 		Remove((int)l, int(h - l));
165 		int nn = Insert((int)l, cl[i]);
166 		n += nn;
167 		pos = l + nn;
168 	}
169 	PlaceCaret(pos);
170 	return n;
171 }
172 
PasteColumn(const WString & text)173 void LineEdit::PasteColumn(const WString& text)
174 {
175 	Vector<WString> cl = Split(text, '\n', false);
176 	if(cl.GetCount() && cl.Top().IsEmpty())
177 		cl.Drop();
178 	if(cl.GetCount() == 0)
179 		return;
180 	int pos;
181 	if(IsRectSelection()) {
182 		Rect t = GetRectSelection();
183 		RemoveSelection();
184 		Point p = t.TopLeft();
185 		pos = (int)cursor;
186 		for(int i = 0; i < t.bottom - t.top + 1; i++) {
187 			CacheLinePos(i + p.y);
188 			int l = (int)GetGPos(i + p.y, p.x);
189 			pos = l + Insert(l, cl[i % cl.GetCount()]);
190 		}
191 	}
192 	else {
193 		RemoveSelection();
194 		Point p = GetColumnLine(cursor);
195 		pos = (int)cursor;
196 		for(int i = 0; i < cl.GetCount(); i++) {
197 			CacheLinePos(i + p.y);
198 			int li = p.y + i;
199 			if(li < GetLineCount()) {
200 				int l = (int)GetGPos(i + p.y, p.x);
201 				pos = l + Insert(l, cl[i]);
202 			}
203 			else {
204 				Insert(GetLength32(), cl[i] + "\n");
205 				pos = GetLength32();
206 			}
207 		}
208 	}
209 	PlaceCaret(pos);
210 }
211 
PasteColumn()212 void LineEdit::PasteColumn()
213 {
214 	WString w = ReadClipboardUnicodeText();
215 	if(w.IsEmpty())
216 		w = ReadClipboardText().ToWString();
217 	PasteColumn(w);
218 	Action();
219 }
220 
sSortLineOrder(const WString & l1,const WString & l2)221 bool sSortLineOrder(const WString& l1, const WString& l2)
222 {
223 	return ToUpper(l1) < ToUpper(l2);
224 }
225 
Sort()226 void LineEdit::Sort()
227 {
228 	if(!IsRectSelection())
229 		return;
230 	CopyRectSelection();
231 	Rect rect = GetRectSelection();
232 	Vector<WString> key;
233 	Vector<WString> ln;
234 	for(int i = rect.top; i <= rect.bottom; i++) {
235 		int64 l, h;
236 		GetRectSelection(rect, i, l, h);
237 		key.Add(GetW((int)l, int(h - l)));
238 		ln.Add(GetWLine(i));
239 	}
240 	int sell = GetPos32(rect.top);
241 	int selh = rect.bottom + 1 < GetLineCount() ? GetPos32(rect.bottom + 1) : GetLength32();
242 	IndexSort(key, ln, sSortLineOrder);
243 	Remove(sell, selh - sell);
244 	Insert(sell, Join(ln, "\n"));
245 }
246 
247 class sOptimizedRectRenderer {
248 	Draw& w;
249 	Rect  cr;
250 	Color color;
251 
252 public:
253 	void DrawRect(const Rect& r, Color color);
DrawRect(int x,int y,int cx,int cy,Color color)254 	void DrawRect(int x, int y, int cx, int cy, Color color) { DrawRect(RectC(x, y, cx, cy), color); }
255 	void Flush();
256 
sOptimizedRectRenderer(Draw & w)257 	sOptimizedRectRenderer(Draw& w) : w(w) { cr = Null; color = Null; }
~sOptimizedRectRenderer()258 	~sOptimizedRectRenderer()              { Flush(); }
259 };
260 
Flush()261 void sOptimizedRectRenderer::Flush()
262 {
263 	LTIMING("RectFlush");
264 	if(!IsNull(cr)) {
265 		w.DrawRect(cr, color);
266 		cr = Null;
267 	}
268 }
269 
DrawRect(const Rect & r,Color c)270 void sOptimizedRectRenderer::DrawRect(const Rect& r, Color c)
271 {
272 	LTIMING("DrawRect");
273 	if(cr.top == r.top && cr.bottom == r.bottom && cr.right == r.left && c == color) {
274 		cr.right = r.right;
275 		return;
276 	}
277 	Flush();
278 	cr = r;
279 	color = c;
280 }
281 
282 #if 1 // This is a more ambitious approach combining non-continual chunks of text, it is a bit faster...
283 class sOptimizedTextRenderer {
284 	Draw&       w;
285 	int         y;
286 	struct Chrs : Moveable<Chrs> {
287 		Vector<int> x;
288 		Vector<int> width;
289 		WString     text;
290 	};
291 	VectorMap< Tuple2<Font, Color>, Chrs > cache;
292 
293 public:
294 	void DrawChar(int x, int y, int chr, int width, Font afont, Color acolor);
295 	void Flush();
296 
sOptimizedTextRenderer(Draw & w)297 	sOptimizedTextRenderer(Draw& w) : w(w) { y = Null; }
~sOptimizedTextRenderer()298 	~sOptimizedTextRenderer()              { Flush(); }
299 };
300 
Flush()301 void sOptimizedTextRenderer::Flush()
302 {
303 	if(cache.GetCount() == 0)
304 		return;
305 	LTIMING("Flush");
306 	for(int i = 0; i < cache.GetCount(); i++) {
307 		Chrs& c = cache[i];
308 		if(c.x.GetCount()) {
309 			Tuple2<Font, Color> fc = cache.GetKey(i);
310 			int x = c.x[0];
311 			for(int i = 0; i < c.x.GetCount() - 1; i++)
312 				c.x[i] = c.x[i + 1] - c.x[i];
313 			c.x.Top() = c.width.Top();
314 			w.DrawText(x, y, c.text, fc.a, fc.b, c.x);
315 		}
316 	}
317 	cache.Clear();
318 }
319 
DrawChar(int x,int _y,int chr,int width,Font font,Color color)320 void sOptimizedTextRenderer::DrawChar(int x, int _y, int chr, int width, Font font, Color color)
321 {
322 	LTIMING("DrawChar");
323 	if(y != _y) {
324 		Flush();
325 		y = _y;
326 	}
327 	Chrs *c;
328 	{
329 		LTIMING("Map");
330 		c = &cache.GetAdd(MakeTuple(font, color));
331 	}
332 	if(c->x.GetCount() && c->x.Top() > x) {
333 		Flush();
334 		c = &cache.GetAdd(MakeTuple(font, color));
335 	}
336 	c->text.Cat(chr);
337 	c->width.Add(width);
338 	c->x.Add(x);
339 }
340 #else
341 class sOptimizedTextRenderer {
342 	Draw&       w;
343 	int         x, y;
344 	int         xpos;
345 	Vector<int> dx;
346 	WString     text;
347 	Font        font;
348 	Color       color;
349 
350 public:
351 	void DrawChar(int x, int y, int chr, int width, Font afont, Color acolor);
352 	void Flush();
353 
sOptimizedTextRenderer(Draw & w)354 	sOptimizedTextRenderer(Draw& w) : w(w) { y = Null; }
~sOptimizedTextRenderer()355 	~sOptimizedTextRenderer()              { Flush(); }
356 };
357 
Flush()358 void sOptimizedTextRenderer::Flush()
359 {
360 	if(text.GetCount() == 0)
361 		return;
362 	LTIMING("Flush");
363 	w.DrawText(x, y, text, font, color, dx);
364 	y = Null;
365 	text.Clear();
366 	dx.Clear();
367 }
368 
DrawChar(int _x,int _y,int chr,int width,Font _font,Color _color)369 void sOptimizedTextRenderer::DrawChar(int _x, int _y, int chr, int width, Font _font, Color _color)
370 {
371 	LTIMING("DrawChar");
372 	if(y == _y && font == _font && color == _color && dx.GetCount() && _x >= xpos - dx.Top())
373 		dx.Top() += _x - xpos;
374 	else {
375 		LTIMING("DrawChar flush");
376 		Flush();
377 		x = _x;
378 		y = _y;
379 		font = _font;
380 		color = _color;
381 	}
382 	dx.Add(width);
383 	text.Cat(chr);
384 	xpos = _x + width;
385 }
386 #endif
387 
Paint0(Draw & w)388 void   LineEdit::Paint0(Draw& w) {
389 	LTIMING("LineEdit::Paint0");
390 	GuiLock __;
391 	int64 sell, selh;
392 	GetSelection(sell, selh);
393 	if(!IsEnabled())
394 		sell = selh = 0;
395 	Rect rect(0, 0, 0, 0);
396 	bool rectsel = IsRectSelection();
397 	if(rectsel)
398 		rect = GetRectSelection();
399 	Size sz = GetSize();
400 	Size fsz = GetFontSize();
401 	Point sc = sb;
402 	int ll = min(GetLineCount(), sz.cy / fsz.cy + sc.y + 1);
403 	int  y = 0;
404 	sc.y = minmax(sc.y, 0, GetLineCount() - 1);
405 	cpos = GetPos64(sc.y);
406 	cline = sc.y;
407 	sell -= cpos;
408 	selh -= cpos;
409 	int64 pos = cpos;
410 	int fascent = font.Info().GetAscent();
411 	int cursorline = GetLine(cursor);
412 	Highlight ih;
413 	ih.ink = color[IsShowEnabled() ? INK_NORMAL : INK_DISABLED];
414 	ih.paper = color[IsReadOnly() && showreadonly || !IsShowEnabled() ? PAPER_READONLY : PAPER_NORMAL];
415 	if(nobg)
416 		ih.paper = Null;
417 	ih.font = font;
418 	ih.chr = 0;
419 	for(int i = sc.y; i < ll; i++) {
420 		Color showcolor = color[WHITESPACE];
421 		WString tx = GetWLine(i);
422 		bool warn_whitespace = false;
423 		if(warnwhitespace && !IsSelection()) {
424 			int64 pos = GetCursor64();
425 			int linei = GetLinePos64(pos);
426 			if(linei != i || pos < tx.GetCount()) {
427 				int wkind = 0;
428 				bool empty = true;
429 				for(const wchar *s = tx; *s; s++) {
430 					if(*s == '\t') {
431 						if(wkind == ' ') {
432 							warn_whitespace = true;
433 							break;
434 						}
435 						wkind = '\t';
436 					}
437 					else
438 					if(*s == ' ')
439 						wkind = ' ';
440 					else
441 					if(*s > ' ') {
442 						empty = false;
443 						wkind = 0;
444 					}
445 				}
446 				if(wkind && !empty)
447 					warn_whitespace = true;
448 				if(warn_whitespace)
449 					showcolor = color[WARN_WHITESPACE];
450 			}
451 		}
452 		bool do_highlight = tx.GetCount() < 100000;
453 		int len = tx.GetLength();
454 		if(w.IsPainting(0, y, sz.cx, fsz.cy)) {
455 			LTIMING("PaintLine");
456 			Vector<Highlight> hl;
457 			int ln;
458 			if(do_highlight) {
459 				hl.SetCount(len + 1, ih);
460 				for(int q = 0; q < tx.GetCount(); q++)
461 					hl[q].chr = tx[q];
462 				LTIMING("HighlightLine");
463 				HighlightLine(i, hl, pos);
464 				ln = hl.GetCount() - 1;
465 			}
466 			else
467 				ln = tx.GetCount();
468 			int lgp = -1;
469 			for(int pass = 0; pass < 3; pass++) {
470 				int gp = 0;
471 				int scx = fsz.cx * sc.x;
472 				sOptimizedRectRenderer rw(w);
473 				if(ln >= 0) {
474 					int q = 0;
475 					int x = 0;
476 					int scx2 = scx - max(2, tabsize) * fsz.cx;
477 					while(q < ln && x < scx2) { // Skip part before left border
478 						wchar chr = do_highlight ? hl[q++].chr : tx[q++];
479 						if(chr == '\t') {
480 							gp = (gp + tabsize) / tabsize * tabsize;
481 							x = fsz.cx * gp;
482 						}
483 						else
484 						if(IsDoubleWidth(chr)) {
485 							x += 2 * fsz.cx;
486 							gp += 2;
487 						}
488 						else {
489 							x += fsz.cx;
490 							gp++;
491 						}
492 					}
493 					sOptimizedTextRenderer tw(w);
494 					Highlight lastHighlight;
495 					while(q < ln) {
496 						if(q == tx.GetCount())
497 							lgp = gp;
498 						Highlight h;
499 						if(do_highlight)
500 							h = hl[q];
501 						else {
502 							h = ih;
503 							h.chr = tx[q];
504 						}
505 						int pos = min(q, len); // Highligting can add chars at the end of line
506 						if(rectsel ? i >= rect.top && i <= rect.bottom && gp >= rect.left && gp < rect.right
507 						           : pos >= sell && pos < selh) {
508 							h.paper = color[PAPER_SELECTED];
509 							h.ink = color[INK_SELECTED];
510 						}
511 						int x = gp * fsz.cx - scx;
512 						bool cjk = IsDoubleWidth(h.chr);
513 						int xx = x + (gp + 1 + cjk) * fsz.cx;
514 						if(h.chr == '\t') {
515 							int ngp = (gp + tabsize) / tabsize * tabsize;
516 							int l = ngp - gp;
517 							LLOG("Highlight -> tab[" << q << "] paper = " << h.paper);
518 							if(x >= -fsz.cy * tabsize) {
519 								if(pass == 0) {
520 									rw.DrawRect(x, y, fsz.cx * l, fsz.cy, h.paper);
521 									if((showtabs || warn_whitespace) &&
522 									   h.paper != SColorHighlight && q < tx.GetLength()) {
523 										rw.DrawRect(x + 2, y + fsz.cy / 2, l * fsz.cx - 4, 1, showcolor);
524 										rw.DrawRect(ngp * fsz.cx - scx - 3, y + 3, 1, fsz.cy - 6, showcolor);
525 									}
526 									if(bordercolumn > 0 && bordercolumn >= gp && bordercolumn < gp + l)
527 										rw.DrawRect((bordercolumn - sc.x) * fsz.cx, y, 1, fsz.cy, bordercolor);
528 								}
529 								if(pass == 2) // resolve underlined tabs
530 									tw.DrawChar(x, y, ' ', fsz.cx * l, h.font, h.ink);
531 							}
532 							q++;
533 							gp = ngp;
534 						}
535 						else
536 						if(h.chr == ' ') {
537 							LLOG("Highlight -> space[" << q << "] paper = " << h.paper);
538 							if(x >= -fsz.cy) {
539 								if(pass == 0) {
540 									rw.DrawRect(x, y, fsz.cx, fsz.cy, h.paper);
541 									if((showspaces || warn_whitespace)
542 									   && h.paper != SColorHighlight && q < tx.GetLength()) {
543 										int n = fsz.cy / 10 + 1;
544 										rw.DrawRect(x + fsz.cx / 2, y + fsz.cy / 2, n, n, showcolor);
545 									}
546 									if(bordercolumn > 0 && bordercolumn >= gp && bordercolumn < gp + 1)
547 										rw.DrawRect((bordercolumn - sc.x) * fsz.cx, y, 1, fsz.cy, bordercolor);
548 								}
549 								if(pass == 2) // resolve underlined spaces
550 									tw.DrawChar(x, y, ' ', fsz.cx, h.font, h.ink);
551 							}
552 							q++;
553 							gp++;
554 						}
555 						else {
556 							LLOG("Highlight -> paper[" << q << "] = " << h.paper);
557 							if(max(x, 0) < min(xx, sz.cx) && fsz.cx >= -fsz.cy) {
558 								if(pass == 0) {
559 									rw.DrawRect(x, y, (cjk + 1) * fsz.cx, fsz.cy, h.paper);
560 									if(bordercolumn > 0 && bordercolumn >= gp && bordercolumn < gp + 1 + cjk)
561 										rw.DrawRect((bordercolumn - sc.x) * fsz.cx, y, 1, fsz.cy, bordercolor);
562 								}
563 								if(pass == 1 && (h.flags & SPELLERROR))
564 									rw.DrawRect(x, max(y, y + fsz.cy - Zy(1)), (cjk + 1) * fsz.cx, Zy(1), LtRed());
565 								if(pass == 2)
566 									tw.DrawChar(x + (h.flags & SHIFT_L ? -fsz.cx / 6 : h.flags & SHIFT_R ? fsz.cx / 6 : 0),
567 									            y + fascent - h.font.GetAscent(),
568 									            h.chr, (cjk + 1) * fsz.cx, h.font, h.ink);
569 							}
570 							q++;
571 							gp += 1 + cjk;
572 							if(x > sz.cx)
573 								break;
574 						}
575 					}
576 				}
577 				if(pass == 0) {
578 					int gpx = gp * fsz.cx - scx;
579 					rw.DrawRect(gpx, y, sz.cx - gpx, fsz.cy,
580 					            !rectsel && sell <= len && len < selh ? color[PAPER_SELECTED]
581 					            : (do_highlight ? hl.Top() : ih).paper);
582 					if(bordercolumn > 0 && bordercolumn >= gp)
583 						rw.DrawRect((bordercolumn - sc.x) * fsz.cx, y, 1, fsz.cy, bordercolor);
584 					if((showlines || warn_whitespace)) {
585 						int yy = 2 * fsz.cy / 3;
586 						int x = (lgp >= 0 ? lgp : gp) * fsz.cx - scx;
587 						rw.DrawRect(x, y + yy, fsz.cx / 2, 1, showcolor);
588 						if(fsz.cx > 2)
589 							rw.DrawRect(x + 1, y + yy - 1, 1, 3, showcolor);
590 						if(fsz.cx > 5)
591 							rw.DrawRect(x + 2, y + yy - 2, 1, 5, showcolor);
592 						rw.DrawRect(x + fsz.cx / 2, y + yy / 2, 1, yy - yy / 2, showcolor);
593 					}
594 					if(sell == selh) {
595 						if(!IsNull(hline) && i == cursorline) {
596 							rw.DrawRect(0, y, sz.cx, 1, hline);
597 							rw.DrawRect(0, y + fsz.cy - 1, sz.cx, 1, hline);
598 						}
599 						if(!IsNull(vline))
600 							rw.DrawRect(caretpos.x, y, 1, fsz.cy, vline);
601 					}
602 					if(rectsel && rect.left == rect.right && i >= rect.top && i <= rect.bottom)
603 						rw.DrawRect(rect.left * fsz.cx - scx, y, 2, fsz.cy, Blend(color[PAPER_SELECTED], color[PAPER_NORMAL]));
604 				}
605 			}
606 		}
607 		y += fsz.cy;
608 		sell -= len + 1;
609 		selh -= len + 1;
610 		pos += len + 1;
611 	}
612 	w.DrawRect(0, y, sz.cx, sz.cy - y, color[IsReadOnly() && showreadonly || !IsShowEnabled() ? PAPER_READONLY : PAPER_NORMAL]);
613 	DrawTiles(w, DropCaret(), CtrlImg::checkers());
614 	vlinex = caretpos.x;
615 }
616 
Paint(Draw & w)617 void LineEdit::Paint(Draw& w)
618 {
619 	Paint0(w);
620 	scroller.Set(sb);
621 }
622 
623 struct LineEdit::RefreshDraw : public NilDraw {
624 	Ctrl  *ctrl;
625 	bool (*chars)(int c);
626 	Size   fsz;
DrawTextOpUpp::LineEdit::RefreshDraw627 	virtual void DrawTextOp(int x, int y, int angle, const wchar *text, Font,
628 	                        Color, int n, const int *dx) {
629 		if(dx)
630 			while(n > 0) {
631 				if((*chars)(*text))
632 					ctrl->Refresh(x, y, fsz.cx, fsz.cy);
633 				text++;
634 				x += *dx++;
635 				n--;
636 			}
637 	}
IsPaintingOpUpp::LineEdit::RefreshDraw638 	bool IsPaintingOp(const Rect& r) const {
639 		return true;
640 	}
641 };
642 
RefreshChars(bool (* chars)(int c))643 void LineEdit::RefreshChars(bool (*chars)(int c))
644 {
645 	RefreshDraw rw;
646 	rw.ctrl = this;
647 	rw.fsz = GetFontSize();
648 	rw.chars = chars;
649 	Paint(rw);
650 }
651 
Layout()652 void   LineEdit::Layout() {
653 	Size sz = sb.GetReducedViewSize();
654 	if(nohbar || isdrag)
655 		sz.cy = GetSize().cy;
656 	sb.SetPage(sz / GetFontSize());
657 	SetHBar();
658 }
659 
GetGPos(int ln,int cl) const660 int64  LineEdit::GetGPos(int ln, int cl) const {
661 	ln = minmax(ln, 0, GetLineCount() - 1);
662 	String h = GetUtf8Line(ln);
663 	const char *s = h.begin();
664 	const char *e = h.end();
665 	const char *b = s;
666 	int gl = 0;
667 	int wpos = 0;
668 	while(s < e) {
669 		if(*s == '\t')
670 			gl = (gl + tabsize) / tabsize * tabsize;
671 		else
672 		if((byte)*s < 128)
673 			gl++;
674 		else {
675 			WString txt = FromUtf8(s, int(e - s));
676 			const wchar *b = txt;
677 			const wchar *e = txt.End();
678 			const wchar *s = b;
679 			while(s < e) {
680 				if(*s == '\t')
681 					gl = (gl + tabsize) / tabsize * tabsize;
682 				else
683 					gl += 1 + IsDoubleWidth(*s);
684 				if(cl < gl) break;
685 				s++;
686 			}
687 			wpos = int(s - b);
688 			break;
689 		}
690 		if(cl < gl) break;
691 		s++;
692 	}
693 
694 	return GetPos64(ln, int(s - b) + wpos);
695 }
696 
GetColumnLine(int64 pos) const697 Point LineEdit::GetColumnLine(int64 pos) const {
698 	Point p;
699 	if(pos > GetLength64()) pos = GetLength64();
700 	p.y = GetLinePos64(pos);
701 	p.x = 0;
702 	WString txt = GetWLine(p.y);
703 	const wchar *s = txt;
704 	while(pos--) {
705 		if(*s == '\t')
706 			p.x = (p.x + tabsize) / tabsize * tabsize;
707 		else
708 			p.x += 1 + IsDoubleWidth(*s);
709 		s++;
710 	}
711 	return p;
712 }
713 
GetIndexLine(int64 pos) const714 Point LineEdit::GetIndexLine(int64 pos) const
715 {
716 	Point p;
717 	if(pos > GetLength64()) pos = GetLength64();
718 	p.y = GetLinePos64(pos);
719 	p.x = minmax((int)pos, 0, GetLineLength(p.y));
720 	return p;
721 }
722 
GetIndexLinePos(Point pos) const723 int64 LineEdit::GetIndexLinePos(Point pos) const
724 {
725 	if(pos.y < 0)
726 		return 0;
727 	if(pos.y >= GetLineCount())
728 		return GetLength64();
729 	return GetPos64(pos.y, minmax(pos.x, 0, GetLineLength(pos.y)));
730 }
731 
RefreshLine(int i)732 void LineEdit::RefreshLine(int i) {
733 	Size sz = GetSize();
734 	int fcy = GetFontSize().cy;
735 	Refresh(0, (i - sb.Get().y) * fcy, sz.cx, fcy);
736 }
737 
GetLineScreenRect(int line) const738 Rect LineEdit::GetLineScreenRect(int line) const {
739 	int fcy = GetFontSize().cy;
740 	Rect r = RectC(0, (line - sb.Get().y) * fcy, GetSize().cx, fcy);
741 	r.Offset(GetScreenView().TopLeft());
742 	return r;
743 }
744 
SetSb()745 void LineEdit::SetSb() {
746 	sb.SetTotalY(GetLineCount());
747 	SetHBar();
748 }
749 
NewScrollPos()750 void LineEdit::NewScrollPos() {}
HighlightLine(int line,Vector<Highlight> & h,int64 pos)751 void LineEdit::HighlightLine(int line, Vector<Highlight>& h, int64 pos) {}
752 
AlignChar()753 void LineEdit::AlignChar() {
754 	int c = GetCursor32();
755 	if(c == 0)
756 		return;
757 	Point pos = GetColumnLine(c);
758 	if(pos.x == 0)
759 		return;
760 	for(int d = 1; d <= pos.y && d < 100; d++) {
761 		int lny = pos.y - d;
762 		WString above = GetWLine(lny);
763 		int offset = int(GetGPos(lny, pos.x) - GetPos64(lny));
764 		int end = offset;
765 		char ch = GetChar(c - 1);
766 		if(ch == ' ')
767 		{
768 			offset++;
769 			while(end < above.GetLength() && above[end] != ' ')
770 				end++;
771 			while(end < above.GetLength() && above[end] == ' ')
772 				end++;
773 		}
774 		else
775 			while(end < above.GetLength() && above[end] != ch)
776 				end++;
777 		if(end < above.GetLength()) {
778 			int count = end - offset + 1;
779 			WString s(' ', count);
780 			Insert(c - 1, s, true);
781 			SetCursor(c + count);
782 			return;
783 		}
784 	}
785 }
786 
PlaceCaret0(Point p)787 void LineEdit::PlaceCaret0(Point p) {
788 	Size fsz = GetFontSize();
789 	p -= sb;
790 	caretpos = Point(p.x * fsz.cx, p.y * fsz.cy);
791 	if(overwrite)
792 		SetCaret(caretpos.x, caretpos.y + fsz.cy - 2, fsz.cx, 2);
793 	else
794 		SetCaret(caretpos.x, caretpos.y, 2, fsz.cy);
795 }
796 
PlaceCaretNoG(int64 newcursor,bool sel)797 int LineEdit::PlaceCaretNoG(int64 newcursor, bool sel) {
798 	if(newcursor > GetLength64()) newcursor = GetLength64();
799 	Point p = GetColumnLine(newcursor);
800 	if(sel) {
801 		if(anchor < 0) {
802 			anchor = cursor;
803 		}
804 		if(rectsel || rectsel != dorectsel)
805 			Refresh();
806 		else
807 			RefreshLines(p.y, GetLine(cursor));
808 		rectsel = dorectsel;
809 	}
810 	else {
811 		if(anchor >= 0) {
812 			if(rectsel || dorectsel)
813 				Refresh();
814 			else
815 				RefreshLines(GetLine(cursor), GetLine(anchor));
816 			anchor = -1;
817 		}
818 		rectsel = false;
819 	}
820 	RefreshLine(GetColumnLine(cursor).y);
821 	RefreshLine(p.y);
822 	cursor = newcursor;
823 	ScrollIntoCursor();
824 	PlaceCaret0(p);
825 	SelectionChanged();
826 	WhenSel();
827 	if(IsAnySelection())
828 		SetSelectionSource(ClipFmtsText());
829 	if(!IsNull(hline)) {
830 		Size sz = GetSize();
831 		Refresh(vlinex, 0, 1, sz.cy);
832 		Refresh(caretpos.x, 0, 1, sz.cy);
833 	}
834 	return p.x;
835 }
836 
PlaceCaret(int64 newcursor,bool sel)837 void LineEdit::PlaceCaret(int64 newcursor, bool sel) {
838 	gcolumn = PlaceCaretNoG(newcursor, sel);
839 }
840 
TopCursor(int lines)841 void LineEdit::TopCursor(int lines)
842 {
843 	sb.SetY(max(0, GetLine(cursor) - lines));
844 }
845 
CenterCursor()846 void LineEdit::CenterCursor() {
847 	int cy = sb.GetPage().cy;
848 	if(cy > 4)
849 		sb.SetY(max(min(GetLine(cursor) - cy / 2, GetLineCount() - cy), 0));
850 }
851 
Scroll()852 void LineEdit::Scroll() {
853 	PlaceCaret0(GetColumnLine(cursor));
854 	scroller.Scroll(*this, GetSize(), sb.Get(), GetFontSize());
855 	SetHBar();
856 	NewScrollPos();
857 	WhenScroll();
858 }
859 
GetMousePos(Point p) const860 int64 LineEdit::GetMousePos(Point p) const {
861 	Size fsz = GetFontSize();
862 	p = (p + fsz.cx / 2 + fsz * (Size)sb.Get()) / fsz;
863 	return GetGPos(p.y, p.x);
864 }
865 
LeftDown(Point p,dword flags)866 void LineEdit::LeftDown(Point p, dword flags) {
867 	mpos = GetMousePos(p);
868 	int64 l, h;
869 	if(GetSelection(l, h) && mpos >= l && mpos < h) {
870 		selclick = true;
871 		return;
872 	}
873 	dorectsel = flags & K_ALT;
874 	PlaceCaret(mpos, (flags & K_SHIFT) || dorectsel);
875 	dorectsel = false;
876 	SetFocus();
877 	SetCapture();
878 }
879 
LeftUp(Point p,dword flags)880 void LineEdit::LeftUp(Point p, dword flags)
881 {
882 	if(!HasCapture() && selclick && !IsDragAndDropSource()) {
883 		mpos = GetMousePos(p);
884 		PlaceCaret(mpos, flags & K_SHIFT);
885 		SetFocus();
886 	}
887 	selclick = false;
888 	ReleaseCapture();
889 }
890 
RightDown(Point p,dword flags)891 void LineEdit::RightDown(Point p, dword flags)
892 {
893 	mpos = GetMousePos(p);
894 	SetFocus();
895 	int64 l, h;
896 	GetSelection(l, h);
897 	if(!IsAnySelection() || !(mpos >= l && mpos < h))
898 		PlaceCaret(mpos, false);
899 	MenuBar::Execute(WhenBar);
900 }
901 
LeftDouble(Point,dword)902 void LineEdit::LeftDouble(Point, dword)
903 {
904 	int64 l, h;
905 	if(GetWordSelection(cursor, l, h))
906 		SetSelection(l, h);
907 }
908 
LeftTriple(Point,dword)909 void LineEdit::LeftTriple(Point, dword)
910 {
911 	int64 q = cursor;
912 	int i = GetLinePos64(q);
913 	q = cursor - q;
914 	SetSelection(q, q + GetLineLength(i) + 1);
915 }
916 
MouseMove(Point p,dword flags)917 void LineEdit::MouseMove(Point p, dword flags) {
918 	if((flags & K_MOUSELEFT) && HasFocus() && HasCapture()) {
919 		int64 c = GetMousePos(p);
920 		dorectsel = flags & K_ALT;
921 		PlaceCaret(c, mpos != c || HasCapture());
922 		dorectsel = false;
923 	}
924 }
925 
LeftRepeat(Point p,dword flags)926 void LineEdit::LeftRepeat(Point p, dword flags) {
927 	if(HasCapture()) {
928 		int64 c = GetMousePos(p);
929 		if(mpos != c) {
930 			dorectsel = flags & K_ALT;
931 			PlaceCaret(c, true);
932 			dorectsel = false;
933 		}
934 	}
935 }
936 
CursorImage(Point,dword)937 Image LineEdit::CursorImage(Point, dword) {
938 	return Image::IBeam();
939 }
940 
MoveUpDown(int n,bool sel)941 void LineEdit::MoveUpDown(int n, bool sel) {
942 	int64 cl = cursor;
943 	int ln = GetLinePos64(cl);
944 	if(ln + n >= GetLineCount())
945 		WaitView(ln + n);
946 	ln = minmax(ln + n, 0, GetLineCount() - 1);
947 	PlaceCaretNoG(GetGPos(ln, gcolumn), sel);
948 }
949 
MoveLeft(bool sel)950 void LineEdit::MoveLeft(bool sel) {
951 	if(cursor)
952 		PlaceCaret(cursor - 1, sel);
953 }
954 
MoveRight(bool sel)955 void LineEdit::MoveRight(bool sel) {
956 	if(cursor < GetLength64())
957 		PlaceCaret(cursor + 1, sel);
958 }
959 
MoveUp(bool sel)960 void LineEdit::MoveUp(bool sel) {
961 	MoveUpDown(-1, sel);
962 }
963 
MoveDown(bool sel)964 void LineEdit::MoveDown(bool sel) {
965 	MoveUpDown(1, sel);
966 }
967 
MovePage(int dir,bool sel)968 void LineEdit::MovePage(int dir, bool sel) {
969 	int n = dir * max(GetSize().cy / GetFontSize().cy - 2, 2);
970 	sb.SetY(Point(sb).y + n);
971 	MoveUpDown(n, sel);
972 }
973 
MovePageUp(bool sel)974 void LineEdit::MovePageUp(bool sel) {
975 	MovePage(-1, sel);
976 }
977 
MovePageDown(bool sel)978 void LineEdit::MovePageDown(bool sel) {
979 	MovePage(1, sel);
980 }
981 
sTabSpace(int c)982 inline bool sTabSpace(int c) { return c == '\t' || c == ' '; }
983 
MoveHome(bool sel)984 void LineEdit::MoveHome(bool sel) {
985 	int64 cl = cursor;
986 	int li = GetLinePos64(cl);
987 	int i = 0;
988 	WString l = GetWLine(li);
989 	while(sTabSpace(l[i]))
990 		i++;
991 	PlaceCaret(GetPos64(li, cl == i ? 0 : i), sel);
992 }
993 
MoveEnd(bool sel)994 void LineEdit::MoveEnd(bool sel) {
995 	int i = GetLine(cursor);
996 	PlaceCaret(GetPos64(i, GetLineLength(i)), sel);
997 }
998 
MoveTextBegin(bool sel)999 void LineEdit::MoveTextBegin(bool sel) {
1000 	PlaceCaret(0, sel);
1001 }
1002 
MoveTextEnd(bool sel)1003 void LineEdit::MoveTextEnd(bool sel) {
1004 	WaitView(INT_MAX, true);
1005 	PlaceCaret(GetLength64(), sel);
1006 }
1007 
InsertChar(dword key,int count,bool canow)1008 bool LineEdit::InsertChar(dword key, int count, bool canow) {
1009 	if(key == K_TAB && !processtab)
1010 		return false;
1011 	if(filter && key >= 32 && key < 65535)
1012 		key = (*filter)(key);
1013 	if(!IsReadOnly() && (key >= 32 && key < 65536 || key == '\t' || key == '\n' ||
1014 	   key == K_ENTER && processenter || key == K_SHIFT_SPACE)) {
1015 		if(key >= 128 && key < 65536 && (charset != CHARSET_UNICODE && charset != CHARSET_UTF8_BOM)
1016 		   && FromUnicode((wchar)key, charset) == DEFAULTCHAR)
1017 			return true;
1018 		if(!RemoveSelection() && overwrite && key != '\n' && key != K_ENTER && canow) {
1019 			int64 q = cursor;
1020 			int i = GetLinePos64(q);
1021 			if(q + count - 1 < GetLineLength(i))
1022 				Remove((int)cursor, (int)count);
1023 		}
1024 		WString text(key == K_ENTER ? '\n' : key == K_SHIFT_SPACE ? ' ' : key, count);
1025 		Insert((int)cursor, text, true);
1026 		PlaceCaret(cursor + count);
1027 		Action();
1028 		return true;
1029 	}
1030 	return false;
1031 }
1032 
DeleteChar()1033 void LineEdit::DeleteChar() {
1034 	if(IsReadOnly() || RemoveSelection()) {
1035 		Action();
1036 		return;
1037 	}
1038 	if(cursor < GetLength32()) {
1039 		Remove((int)cursor, 1);
1040 		Action();
1041 	}
1042 }
1043 
Backspace()1044 void LineEdit::Backspace() {
1045 	if(IsReadOnly() || RemoveSelection() || cursor == 0) return;
1046 	MoveLeft();
1047 	DeleteChar();
1048 	Action();
1049 }
1050 
DeleteLine()1051 void LineEdit::DeleteLine()
1052 {
1053 	int64 b, e;
1054 	if(GetSelection(b, e) && GetLine(b) != GetLine(e)) {
1055 		RemoveSelection();
1056 		return;
1057 	}
1058 	int i = GetLine(cursor);
1059 	int p = GetPos32(i);
1060 	Remove((int)p, GetLineLength(i) + 1);
1061 	PlaceCaret(p);
1062 	Action();
1063 }
1064 
CutLine()1065 void LineEdit::CutLine()
1066 {
1067 	if(IsReadOnly()) return;
1068 	int64 b, e;
1069 	if(GetSelection(b, e) && GetLine(b) != GetLine(e)) {
1070 		Cut();
1071 		return;
1072 	}
1073 	int i = GetLine(cursor);
1074 	int p = GetPos32(i);
1075 	WString txt = Get(p, GetLineLength(i) + 1).ToWString();
1076 	WriteClipboardUnicodeText(txt);
1077 	AppendClipboardText(txt.ToString());
1078 	ClearSelection();
1079 	DeleteLine();
1080 }
1081 
Serialize(Stream & s)1082 void LineEdit::EditPos::Serialize(Stream& s) {
1083 	int version = 1;
1084 	s / version;
1085 	if(version >= 1)
1086 		s % sby % cursor;
1087 	else {
1088 		int c = (int)cursor;
1089 		s % sby % c;
1090 		cursor = c;
1091 	}
1092 }
1093 
GetEditPos() const1094 LineEdit::EditPos LineEdit::GetEditPos() const {
1095 	EditPos pos;
1096 	pos.sby = sb.Get().y;
1097 	pos.cursor = cursor;
1098 	return pos;
1099 }
1100 
SetEditPos(const LineEdit::EditPos & pos)1101 void LineEdit::SetEditPos(const LineEdit::EditPos& pos) {
1102 	SetEditPosSbOnly(pos);
1103 	SetCursor(pos.cursor);
1104 }
1105 
SetEditPosSb(const LineEdit::EditPos & pos)1106 void LineEdit::SetEditPosSb(const LineEdit::EditPos& pos) {
1107 	SetCursor(pos.cursor);
1108 	SetEditPosSbOnly(pos);
1109 }
1110 
SetEditPosSbOnly(const LineEdit::EditPos & pos)1111 void LineEdit::SetEditPosSbOnly(const LineEdit::EditPos& pos) {
1112 	sb.SetY(minmax(pos.sby, 0, GetLineCount() - 1));
1113 }
1114 
SetHBar()1115 void LineEdit::SetHBar()
1116 {
1117 	int mpos = 0;
1118 	if(!nohbar && !isdrag) {
1119 		int m = min(sb.y + sb.GetPage().cy + 2, GetLineCount());
1120 		for(int i = sb.y; i < m; i++) {
1121 			int pos = 0;
1122 			WString l = GetWLine(i);
1123 			const wchar *s = l;
1124 			const wchar *e = l.End();
1125 			while(s < e) {
1126 				if(*s == '\t')
1127 					pos = (pos + tabsize) / tabsize * tabsize;
1128 				else
1129 					pos += 1 + IsDoubleWidth(*s);
1130 				s++;
1131 			}
1132 			mpos = max(mpos, pos);
1133 		}
1134 	}
1135 	sb.SetTotalX(mpos + 1);
1136 }
1137 
ScrollIntoCursor()1138 void LineEdit::ScrollIntoCursor()
1139 {
1140 	Point p = GetColumnLine(GetCursor64());
1141 	sb.ScrollInto(p);
1142 	SetHBar();
1143 	sb.ScrollInto(p);
1144 }
1145 
Key(dword key,int count)1146 bool LineEdit::Key(dword key, int count) {
1147 	NextUndo();
1148 	switch(key) {
1149 	case K_CTRL_UP:
1150 		ScrollUp();
1151 		return true;
1152 	case K_CTRL_DOWN:
1153 		ScrollDown();
1154 		return true;
1155 	case K_INSERT:
1156 		OverWriteMode(!IsOverWriteMode());
1157 		break;
1158 	}
1159 	bool sel = key & K_SHIFT;
1160 	dorectsel = key & K_ALT;
1161 	dword k = key & ~K_SHIFT;
1162 	if((key & (K_SHIFT|K_ALT)) == (K_SHIFT|K_ALT))
1163 		k &= ~K_ALT;
1164 	switch(k) {
1165 	case K_CTRL_LEFT:
1166 		{
1167 			PlaceCaret(GetPrevWord(cursor), sel);
1168 			break;
1169 		}
1170 	case K_CTRL_RIGHT:
1171 		{
1172 			PlaceCaret(GetNextWord(cursor), sel);
1173 			break;
1174 		}
1175 	case K_LEFT:
1176 		MoveLeft(sel);
1177 		break;
1178 	case K_RIGHT:
1179 		MoveRight(sel);
1180 		break;
1181 	case K_HOME:
1182 		MoveHome(sel);
1183 		break;
1184 	case K_END:
1185 		MoveEnd(sel);
1186 		break;
1187 	case K_UP:
1188 		MoveUp(sel);
1189 		break;
1190 	case K_DOWN:
1191 		MoveDown(sel);
1192 		break;
1193 	case K_PAGEUP:
1194 		MovePageUp(sel);
1195 		break;
1196 	case K_PAGEDOWN:
1197 		MovePageDown(sel);
1198 		break;
1199 	case K_CTRL_PAGEUP:
1200 	case K_CTRL_HOME:
1201 		MoveTextBegin(sel);
1202 		break;
1203 	case K_CTRL_PAGEDOWN:
1204 	case K_CTRL_END:
1205 		MoveTextEnd(sel);
1206 		break;
1207 	case K_CTRL_C:
1208 	case K_CTRL_INSERT:
1209 		Copy();
1210 		break;
1211 	case K_CTRL_A:
1212 		SelectAll();
1213 		break;
1214 	default:
1215 		dorectsel = false;
1216 		if(IsReadOnly())
1217 			return MenuBar::Scan(WhenBar, key);
1218 		switch(key) {
1219 		case K_DELETE:
1220 			DeleteChar();
1221 			break;
1222 		case K_BACKSPACE:
1223 		case K_SHIFT|K_BACKSPACE:
1224 			Backspace();
1225 			break;
1226 		case K_SHIFT_TAB:
1227 			AlignChar();
1228 			break;
1229 		case K_CTRL_Y:
1230 		case K_CTRL_L:
1231 			if(cutline) {
1232 				CutLine();
1233 				break;
1234 			}
1235 		default:
1236 			if(InsertChar(key, count, true))
1237 				return true;
1238 			return MenuBar::Scan(WhenBar, key);
1239 		}
1240 		return true;
1241 	}
1242 	dorectsel = false;
1243 	Sync();
1244 	return true;
1245 }
1246 
DragAndDrop(Point p,PasteClip & d)1247 void LineEdit::DragAndDrop(Point p, PasteClip& d)
1248 {
1249 	if(IsReadOnly()) return;
1250 	int c = GetMousePos32(p);
1251 	if(AcceptText(d)) {
1252 		NextUndo();
1253 		int a = sb.y;
1254 		int sell, selh;
1255 		WString text = GetWString(d);
1256 		if(GetSelection32(sell, selh)) {
1257 			if(c >= sell && c < selh) {
1258 				if(!IsReadOnly())
1259 					RemoveSelection();
1260 				if(IsDragAndDropSource())
1261 					d.SetAction(DND_COPY);
1262 				c = sell;
1263 			}
1264 			else
1265 			if(d.GetAction() == DND_MOVE && IsDragAndDropSource()) {
1266 				if(c > sell)
1267 					c -= selh - sell;
1268 				if(!IsReadOnly())
1269 					RemoveSelection();
1270 				d.SetAction(DND_COPY);
1271 			}
1272 		}
1273 		int count = Insert(c, text);
1274 		sb.y = a;
1275 		SetFocus();
1276 		SetSelection(c, c + count);
1277 		Action();
1278 		return;
1279 	}
1280 	if(!d.IsAccepted()) return;
1281 	if(!isdrag) {
1282 		isdrag = true;
1283 		ScrollIntoCursor();
1284 	}
1285 	Point dc = Null;
1286 	if(c >= 0)
1287 		dc = GetColumnLine(c);
1288 	if(dc != dropcaret) {
1289 		RefreshDropCaret();
1290 		dropcaret = dc;
1291 		RefreshDropCaret();
1292 	}
1293 }
1294 
DropCaret()1295 Rect LineEdit::DropCaret()
1296 {
1297 	if(IsNull(dropcaret))
1298 		return Rect(0, 0, 0, 0);
1299 	Size fsz = GetFontSize();
1300 	Point p = dropcaret - sb;
1301 	p = Point(p.x * fsz.cx, p.y * fsz.cy);
1302 	return RectC(p.x, p.y, 1, fsz.cy);
1303 }
1304 
RefreshDropCaret()1305 void LineEdit::RefreshDropCaret()
1306 {
1307 	Refresh(DropCaret());
1308 }
1309 
DragRepeat(Point p)1310 void LineEdit::DragRepeat(Point p)
1311 {
1312 	sb.y = (int)sb.y + GetDragScroll(this, p, 1).y;
1313 }
1314 
DragLeave()1315 void LineEdit::DragLeave()
1316 {
1317 	RefreshDropCaret();
1318 	dropcaret = Null;
1319 	isdrag = false;
1320 	Layout();
1321 }
1322 
LeftDrag(Point p,dword flags)1323 void LineEdit::LeftDrag(Point p, dword flags)
1324 {
1325 	int64 c = GetMousePos(p);
1326 	int64 l, h;
1327 	if(!HasCapture() && GetSelection(l, h) && c >= l && c < h) {
1328 		WString sample = GetW(l, (int)min(h - l, (int64)3000));
1329 		Size sz = StdSampleSize();
1330 		ImageDraw iw(sz);
1331 		iw.DrawRect(sz, Black());
1332 		iw.Alpha().DrawRect(sz, Black());
1333 		DrawTLText(iw.Alpha(), 0, 0, 9999, sample, CourierZ(10), White());
1334 		NextUndo();
1335 		if(DoDragAndDrop(ClipFmtsText(), iw) == DND_MOVE && !IsReadOnly()) {
1336 			RemoveSelection();
1337 			Action();
1338 		}
1339 	}
1340 }
1341 
1342 }
1343