1 //------------------------------------------------------------------------------
2 // emTextField.cpp
3 //
4 // Copyright (C) 2005-2011,2014-2016,2018 Oliver Hamann.
5 //
6 // Homepage: http://eaglemode.sourceforge.net/
7 //
8 // This program is free software: you can redistribute it and/or modify it under
9 // the terms of the GNU General Public License version 3 as published by the
10 // Free Software Foundation.
11 //
12 // This program is distributed in the hope that it will be useful, but WITHOUT
13 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 // FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
15 // more details.
16 //
17 // You should have received a copy of the GNU General Public License version 3
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 //------------------------------------------------------------------------------
20 
21 #include <emCore/emTextField.h>
22 
23 
emTextField(ParentArg parent,const emString & name,const emString & caption,const emString & description,const emImage & icon,const emString & text,bool editable)24 emTextField::emTextField(
25 	ParentArg parent, const emString & name, const emString & caption,
26 	const emString & description, const emImage & icon,
27 	const emString & text, bool editable
28 )
29 	: emBorder(parent,name,caption,description,icon)
30 {
31 	Clipboard=emClipboard::LookupInherited(GetView());
32 	if (!Clipboard) {
33 		emFatalError("emTextField: No emClipboard available.");
34 	}
35 	Editable=editable;
36 	MultiLineMode=false;
37 	PasswordMode=false;
38 	OverwriteMode=false;
39 	Text=text;
40 	TextLen=Text.GetLen();
41 	CursorIndex=TextLen;
42 	SelectionStartIndex=0;
43 	SelectionEndIndex=0;
44 	MagicCursorColumn=-1;
45 	SelectionId=-1;
46 	CursorBlinkTime=emGetClockMS();
47 	CursorBlinkOn=true;
48 	DragMode=DM_NONE;
49 	DragPosC=0.0;
50 	DragPosR=0.0;
51 	SetBorderType(OBT_INSTRUMENT,Editable?IBT_INPUT_FIELD:IBT_OUTPUT_FIELD);
52 }
53 
54 
~emTextField()55 emTextField::~emTextField()
56 {
57 }
58 
59 
SetEditable(bool editable)60 void emTextField::SetEditable(bool editable)
61 {
62 	if (Editable!=editable) {
63 		Editable=editable;
64 		if (editable) {
65 			if (GetInnerBorderType()==IBT_OUTPUT_FIELD) {
66 				SetInnerBorderType(IBT_INPUT_FIELD);
67 			}
68 		}
69 		else {
70 			if (GetInnerBorderType()==IBT_INPUT_FIELD) {
71 				SetInnerBorderType(IBT_OUTPUT_FIELD);
72 			}
73 		}
74 		InvalidatePainting();
75 	}
76 }
77 
78 
SetMultiLineMode(bool multiLineMode)79 void emTextField::SetMultiLineMode(bool multiLineMode)
80 {
81 	if (MultiLineMode!=multiLineMode) {
82 		MultiLineMode=multiLineMode;
83 		InvalidatePainting();
84 	}
85 }
86 
87 
SetPasswordMode(bool passwordMode)88 void emTextField::SetPasswordMode(bool passwordMode)
89 {
90 	if (PasswordMode!=passwordMode) {
91 		PasswordMode=passwordMode;
92 		InvalidatePainting();
93 	}
94 }
95 
96 
SetOverwriteMode(bool overwriteMode)97 void emTextField::SetOverwriteMode(bool overwriteMode)
98 {
99 	if (OverwriteMode!=overwriteMode) {
100 		OverwriteMode=overwriteMode;
101 		InvalidatePainting();
102 	}
103 }
104 
105 
SetText(const emString & text)106 void emTextField::SetText(const emString & text)
107 {
108 	if (Text==text) return;
109 	EmptySelection();
110 	Text=text;
111 	TextLen=Text.GetLen();
112 	CursorIndex=TextLen;
113 	MagicCursorColumn=-1;
114 	InvalidatePainting();
115 	Signal(TextSignal);
116 	TextChanged();
117 }
118 
119 
SetCursorIndex(int index)120 void emTextField::SetCursorIndex(int index)
121 {
122 	if (index<0) index=0;
123 	if (index>TextLen) index=TextLen;
124 	if (CursorIndex!=index) {
125 		index=GetNormalizedIndex(index);
126 		if (CursorIndex!=index) {
127 			CursorIndex=index;
128 			InvalidatePainting();
129 		}
130 	}
131 }
132 
133 
Select(int startIndex,int endIndex,bool publish)134 void emTextField::Select(int startIndex, int endIndex, bool publish)
135 {
136 	if (startIndex<0) startIndex=0;
137 	if (endIndex>TextLen) endIndex=TextLen;
138 	if (startIndex>=endIndex) {
139 		startIndex=0;
140 		endIndex=0;
141 	}
142 	if (SelectionStartIndex==startIndex && SelectionEndIndex==endIndex) return;
143 	startIndex=GetNormalizedIndex(startIndex);
144 	endIndex=GetNormalizedIndex(endIndex);
145 	if (SelectionStartIndex==startIndex && SelectionEndIndex==endIndex) return;
146 	if (SelectionId!=-1) {
147 		Clipboard->Clear(true,SelectionId);
148 		SelectionId=-1;
149 	}
150 	SelectionStartIndex=startIndex;
151 	SelectionEndIndex=endIndex;
152 	InvalidatePainting();
153 	if (publish) PublishSelection();
154 	Signal(SelectionSignal);
155 	SelectionChanged();
156 }
157 
158 
EmptySelection()159 void emTextField::EmptySelection()
160 {
161 	Select(0,0,false);
162 }
163 
164 
SelectAll(bool publish)165 void emTextField::SelectAll(bool publish)
166 {
167 	Select(0,TextLen,publish);
168 }
169 
170 
PublishSelection()171 void emTextField::PublishSelection()
172 {
173 	emString str;
174 	int len;
175 
176 	len=SelectionEndIndex-SelectionStartIndex;
177 	if (len>0 && SelectionId==-1) {
178 		if (PasswordMode) str=emString('*',len);
179 		else str=Text.GetSubString(SelectionStartIndex,len);
180 		SelectionId=Clipboard->PutText(str,true);
181 	}
182 }
183 
184 
CutSelectedTextToClipboard()185 void emTextField::CutSelectedTextToClipboard()
186 {
187 	CopySelectedTextToClipboard();
188 	DeleteSelectedText();
189 }
190 
191 
CopySelectedTextToClipboard()192 void emTextField::CopySelectedTextToClipboard()
193 {
194 	emString str;
195 	int len;
196 
197 	len=SelectionEndIndex-SelectionStartIndex;
198 	if (len>0) {
199 		if (PasswordMode) str=emString('*',len);
200 		else str=Text.GetSubString(SelectionStartIndex,len);
201 		Clipboard->PutText(str);
202 	}
203 }
204 
205 
PasteSelectedTextFromClipboard()206 void emTextField::PasteSelectedTextFromClipboard()
207 {
208 	PasteSelectedText(Clipboard->GetText());
209 }
210 
211 
PasteSelectedText(const emString & text)212 void emTextField::PasteSelectedText(const emString & text)
213 {
214 	int i,l,d;
215 
216 	if (text.IsEmpty()) return;
217 	if (SelectionStartIndex<SelectionEndIndex) {
218 		i=SelectionStartIndex;
219 		l=SelectionEndIndex-SelectionStartIndex;
220 		d=TextLen-SelectionEndIndex;
221 		EmptySelection();
222 	}
223 	else {
224 		i=CursorIndex;
225 		l=0;
226 		d=TextLen-CursorIndex;
227 	}
228 	Text.Replace(i,l,text);
229 	TextLen=Text.GetLen();
230 	CursorIndex=TextLen-d;
231 	MagicCursorColumn=-1;
232 	InvalidatePainting();
233 	Signal(TextSignal);
234 	TextChanged();
235 }
236 
237 
DeleteSelectedText()238 void emTextField::DeleteSelectedText()
239 {
240 	int i,l;
241 
242 	i=SelectionStartIndex;
243 	l=SelectionEndIndex-SelectionStartIndex;
244 	if (l<=0) return;
245 	CursorIndex=i;
246 	EmptySelection();
247 	Text.Remove(i,l);
248 	TextLen=Text.GetLen();
249 	MagicCursorColumn=-1;
250 	InvalidatePainting();
251 	Signal(TextSignal);
252 	TextChanged();
253 }
254 
255 
TextChanged()256 void emTextField::TextChanged()
257 {
258 }
259 
260 
SelectionChanged()261 void emTextField::SelectionChanged()
262 {
263 }
264 
265 
Cycle()266 bool emTextField::Cycle()
267 {
268 	emUInt64 clk;
269 	bool busy;
270 
271 	clk=emGetClockMS();
272 	busy=false;
273 
274 	if (IsInFocusedPath()) {
275 		if (clk>=CursorBlinkTime+1000) {
276 			CursorBlinkTime=clk;
277 			if (!CursorBlinkOn) {
278 				CursorBlinkOn=true;
279 				InvalidatePainting();
280 			}
281 		}
282 		else if (clk>=CursorBlinkTime+500) {
283 			if (CursorBlinkOn) {
284 				CursorBlinkOn=false;
285 				InvalidatePainting();
286 			}
287 		}
288 		busy=true;
289 	}
290 	else {
291 		CursorBlinkTime=clk;
292 		if (!CursorBlinkOn) {
293 			CursorBlinkOn=true;
294 			InvalidatePainting();
295 		}
296 	}
297 
298 	if (emBorder::Cycle()) busy=true;
299 	return busy;
300 }
301 
302 
Notice(NoticeFlags flags)303 void emTextField::Notice(NoticeFlags flags)
304 {
305 	if ((flags&(NF_FOCUS_CHANGED))!=0 && IsInFocusedPath()) {
306 		RestartCursorBlinking();
307 		WakeUp();
308 	}
309 	emBorder::Notice(flags);
310 }
311 
312 
Input(emInputEvent & event,const emInputState & state,double mx,double my)313 void emTextField::Input(
314 	emInputEvent & event, const emInputState & state, double mx, double my
315 )
316 {
317 	static const double minExt=4.5;
318 	double mc,mr;
319 	int col,row,i,i1,i2,j1,j2;
320 	bool inArea;
321 	emString str;
322 
323 	inArea=CheckMouse(mx,my,&mc,&mr);
324 
325 	switch (DragMode) {
326 	case DM_NONE:
327 		if (
328 			inArea && event.IsKey(EM_KEY_LEFT_BUTTON) &&
329 			!state.GetAlt() && !state.GetMeta() &&
330 			GetViewCondition(VCT_MIN_EXT)>=minExt
331 		) {
332 			MagicCursorColumn=-1;
333 			if (state.GetCtrl()) {
334 				if (IsEditable() && IsEnabled()) {
335 					i=ColRow2Index(mc,mr,false);
336 					if (i<SelectionStartIndex || i>=SelectionEndIndex) {
337 						SetCursorIndex(ColRow2Index(mc,mr,true));
338 						SetDragMode(DM_INSERT);
339 					}
340 					else {
341 						SetCursorIndex(SelectionEndIndex);
342 						Index2ColRow(SelectionStartIndex,&col,&row);
343 						DragPosC=mc-col;
344 						DragPosR=mr-row;
345 						SetDragMode(DM_MOVE);
346 					}
347 				}
348 			}
349 			else if (event.GetRepeat()==0) {
350 				i=ColRow2Index(mc,mr,true);
351 				if (state.GetShift()) ModifySelection(i,i,false);
352 				else EmptySelection();
353 				SetCursorIndex(i);
354 				SetDragMode(DM_SELECT);
355 			}
356 			else if (event.GetRepeat()==1) {
357 				i2=GetNextWordBoundaryIndex(ColRow2Index(mc,mr,false));
358 				i1=GetPrevWordBoundaryIndex(i2);
359 				if (!state.GetShift() || IsSelectionEmpty()) {
360 					Select(i1,i2,false);
361 					SetCursorIndex(i2);
362 				}
363 				else if (i2>SelectionEndIndex) {
364 					ModifySelection(i2,i2,false);
365 					SetCursorIndex(i2);
366 				}
367 				else {
368 					ModifySelection(i1,i1,false);
369 					SetCursorIndex(i1);
370 				}
371 				SetDragMode(DM_SELECT_BY_WORDS);
372 			}
373 			else if (event.GetRepeat()==2) {
374 				i2=GetNextRowIndex(ColRow2Index(mc,mr,false));
375 				i1=GetPrevRowIndex(i2);
376 				if (!state.GetShift() || IsSelectionEmpty()) {
377 					Select(i1,i2,false);
378 					SetCursorIndex(i2);
379 				}
380 				else if (i2>SelectionEndIndex) {
381 					ModifySelection(i2,i2,false);
382 					SetCursorIndex(i2);
383 				}
384 				else {
385 					ModifySelection(i1,i1,false);
386 					SetCursorIndex(i1);
387 				}
388 				SetDragMode(DM_SELECT_BY_ROWS);
389 			}
390 			else {
391 				SelectAll(true);
392 				SetCursorIndex(TextLen);
393 			}
394 			RestartCursorBlinking();
395 			Focus();
396 			event.Eat();
397 		}
398 		break;
399 	case DM_SELECT:
400 		i=ColRow2Index(mc,mr,true);
401 		if (i!=CursorIndex) {
402 			MagicCursorColumn=-1;
403 			ModifySelection(CursorIndex,i,false);
404 			SetCursorIndex(i);
405 			RestartCursorBlinking();
406 		}
407 		if (!state.Get(EM_KEY_LEFT_BUTTON)) {
408 			PublishSelection();
409 			SetDragMode(DM_NONE);
410 		}
411 		break;
412 	case DM_SELECT_BY_WORDS:
413 		i2=GetNextWordBoundaryIndex(ColRow2Index(mc,mr,false));
414 		i1=GetPrevWordBoundaryIndex(i2);
415 		if (IsSelectionEmpty()) {
416 			Select(i1,i2,false);
417 			SetCursorIndex(i2);
418 		}
419 		else {
420 			j1=SelectionStartIndex;
421 			j2=SelectionEndIndex;
422 			if (CursorIndex<=j1) j1=GetPrevWordBoundaryIndex(j2);
423 			else j2=GetNextWordBoundaryIndex(j1);
424 			if (j1<=i1) {
425 				Select(j1,i2,false);
426 				SetCursorIndex(i2);
427 			}
428 			else {
429 				Select(i1,j2,false);
430 				SetCursorIndex(i1);
431 			}
432 		}
433 		MagicCursorColumn=-1;
434 		RestartCursorBlinking();
435 		if (!state.Get(EM_KEY_LEFT_BUTTON)) {
436 			PublishSelection();
437 			SetDragMode(DM_NONE);
438 		}
439 		break;
440 	case DM_SELECT_BY_ROWS:
441 		i2=GetNextRowIndex(ColRow2Index(mc,mr,false));
442 		i1=GetPrevRowIndex(i2);
443 		if (IsSelectionEmpty()) {
444 			Select(i1,i2,false);
445 			SetCursorIndex(i2);
446 		}
447 		else {
448 			j1=SelectionStartIndex;
449 			j2=SelectionEndIndex;
450 			if (CursorIndex<=j1) j1=GetPrevRowIndex(j2);
451 			else j2=GetNextRowIndex(j1);
452 			if (j1<=i1) {
453 				Select(j1,i2,false);
454 				SetCursorIndex(i2);
455 			}
456 			else {
457 				Select(i1,j2,false);
458 				SetCursorIndex(i1);
459 			}
460 		}
461 		MagicCursorColumn=-1;
462 		RestartCursorBlinking();
463 		if (!state.Get(EM_KEY_LEFT_BUTTON)) {
464 			PublishSelection();
465 			SetDragMode(DM_NONE);
466 		}
467 		break;
468 	case DM_INSERT:
469 		i=ColRow2Index(mc,mr,true);
470 		if (i!=CursorIndex) {
471 			SetCursorIndex(i);
472 			MagicCursorColumn=-1;
473 			RestartCursorBlinking();
474 		}
475 		if (!state.Get(EM_KEY_LEFT_BUTTON)) {
476 			if (inArea && IsEditable() && IsEnabled()) {
477 				SelectionId=-1;
478 				EmptySelection();
479 				PasteSelectedText(Clipboard->GetText(true));
480 			}
481 			SetDragMode(DM_NONE);
482 		}
483 		break;
484 	case DM_MOVE:
485 		// When extending this for moving the text to somewhere else,
486 		// don't forget to disable that in password mode.
487 		i1=SelectionStartIndex;
488 		i2=SelectionEndIndex;
489 		if (i1<i2 && IsEditable() && IsEnabled()) {
490 			str=Text.Extract(i1,i2-i1);
491 			TextLen=Text.GetLen();
492 			i=ColRow2Index(mc-DragPosC,mr-DragPosR+0.5,true);
493 			Text.Insert(i,str);
494 			TextLen=Text.GetLen();
495 			if (i!=i1) {
496 				SelectionStartIndex+=i-i1;
497 				SelectionEndIndex+=i-i1;
498 				CursorIndex=SelectionEndIndex;
499 				MagicCursorColumn=-1;
500 				RestartCursorBlinking();
501 				InvalidatePainting();
502 				Signal(TextSignal);
503 				TextChanged();
504 				Signal(SelectionSignal);
505 				SelectionChanged();
506 			}
507 		}
508 		if (!state.Get(EM_KEY_LEFT_BUTTON)) {
509 			SetDragMode(DM_NONE);
510 		}
511 		break;
512 	}
513 
514 	if (
515 		!event.IsEmpty() && DragMode==DM_NONE &&
516 		GetViewCondition(VCT_MIN_EXT)>=minExt
517 	) {
518 		if (event.IsKey(EM_KEY_CURSOR_LEFT) && !state.GetAlt() && !state.GetMeta()) {
519 			if (state.GetCtrl()) i=GetPrevWordIndex(CursorIndex);
520 			else i=GetPrevIndex(CursorIndex);
521 			if (state.GetShift()) ModifySelection(CursorIndex,i,true);
522 			else EmptySelection();
523 			SetCursorIndex(i);
524 			MagicCursorColumn=-1;
525 			RestartCursorBlinking();
526 			ScrollToCursor();
527 			event.Eat();
528 		}
529 		if (event.IsKey(EM_KEY_CURSOR_RIGHT) && !state.GetAlt() && !state.GetMeta()) {
530 			if (state.GetCtrl()) i=GetNextWordIndex(CursorIndex);
531 			else i=GetNextIndex(CursorIndex);
532 			if (state.GetShift()) ModifySelection(CursorIndex,i,true);
533 			else EmptySelection();
534 			SetCursorIndex(i);
535 			MagicCursorColumn=-1;
536 			RestartCursorBlinking();
537 			ScrollToCursor();
538 			event.Eat();
539 		}
540 		if (MultiLineMode) {
541 			if (event.IsKey(EM_KEY_CURSOR_UP) && !state.GetAlt() && !state.GetMeta()) {
542 				if (state.GetCtrl()) {
543 					i=GetPrevParagraphIndex(CursorIndex);
544 					MagicCursorColumn=-1;
545 				}
546 				else {
547 					Index2ColRow(CursorIndex,&col,&row);
548 					if (MagicCursorColumn<0) MagicCursorColumn=col;
549 					i=ColRow2Index(MagicCursorColumn,row-1,true);
550 				}
551 				if (state.GetShift()) ModifySelection(CursorIndex,i,true);
552 				else EmptySelection();
553 				SetCursorIndex(i);
554 				RestartCursorBlinking();
555 				ScrollToCursor();
556 				event.Eat();
557 			}
558 			if (event.IsKey(EM_KEY_CURSOR_DOWN) && !state.GetAlt() && !state.GetMeta()) {
559 				if (state.GetCtrl()) {
560 					i=GetNextParagraphIndex(CursorIndex);
561 					MagicCursorColumn=-1;
562 				}
563 				else {
564 					Index2ColRow(CursorIndex,&col,&row);
565 					if (MagicCursorColumn<0) MagicCursorColumn=col;
566 					i=ColRow2Index(MagicCursorColumn,row+1,true);
567 				}
568 				if (state.GetShift()) ModifySelection(CursorIndex,i,true);
569 				else EmptySelection();
570 				SetCursorIndex(i);
571 				RestartCursorBlinking();
572 				ScrollToCursor();
573 				event.Eat();
574 			}
575 		}
576 		if (event.IsKey(EM_KEY_HOME) && !state.GetAlt() && !state.GetMeta()) {
577 			if (state.GetCtrl()) i=0;
578 			else i=GetRowStartIndex(CursorIndex);
579 			if (state.GetShift()) ModifySelection(CursorIndex,i,true);
580 			else EmptySelection();
581 			SetCursorIndex(i);
582 			MagicCursorColumn=-1;
583 			RestartCursorBlinking();
584 			ScrollToCursor();
585 			event.Eat();
586 		}
587 		if (event.IsKey(EM_KEY_END) && !state.GetAlt() && !state.GetMeta()) {
588 			if (state.GetCtrl()) i=TextLen;
589 			else i=GetRowEndIndex(CursorIndex);
590 			if (state.GetShift()) ModifySelection(CursorIndex,i,true);
591 			else EmptySelection();
592 			SetCursorIndex(i);
593 			MagicCursorColumn=-1;
594 			RestartCursorBlinking();
595 			ScrollToCursor();
596 			event.Eat();
597 		}
598 		if (event.IsKey(EM_KEY_A) && state.IsCtrlMod()) {
599 			SelectAll(true);
600 			SetCursorIndex(TextLen);
601 			MagicCursorColumn=-1;
602 			RestartCursorBlinking();
603 			ScrollToCursor();
604 			event.Eat();
605 		}
606 		if (event.IsKey(EM_KEY_INSERT) && state.IsNoMod()) {
607 			SetOverwriteMode(!GetOverwriteMode());
608 			RestartCursorBlinking();
609 			event.Eat();
610 		}
611 		if ((event.IsKey(EM_KEY_INSERT) && state.IsCtrlMod()) ||
612 		    (event.IsKey(EM_KEY_C) && state.IsCtrlMod())) {
613 			CopySelectedTextToClipboard();
614 			RestartCursorBlinking();
615 			event.Eat();
616 		}
617 	}
618 
619 	if (
620 		!event.IsEmpty() && DragMode==DM_NONE &&
621 		IsEditable() && IsEnabled() &&
622 		GetViewCondition(VCT_MIN_EXT)>=minExt
623 	) {
624 		if (event.IsKey(EM_KEY_BACKSPACE) && state.IsNoMod()) {
625 			if (IsSelectionEmpty()) {
626 				Select(GetPrevIndex(CursorIndex),CursorIndex,false);
627 			}
628 			DeleteSelectedText();
629 			RestartCursorBlinking();
630 			ScrollToCursor();
631 			event.Eat();
632 		}
633 		if (event.IsKey(EM_KEY_DELETE) && state.IsNoMod()) {
634 			if (IsSelectionEmpty()) {
635 				Select(CursorIndex,GetNextIndex(CursorIndex),false);
636 			}
637 			DeleteSelectedText();
638 			RestartCursorBlinking();
639 			ScrollToCursor();
640 			event.Eat();
641 		}
642 		if ((event.IsKey(EM_KEY_DELETE) && state.IsShiftMod()) ||
643 		    (event.IsKey(EM_KEY_X) && state.IsCtrlMod())) {
644 			CutSelectedTextToClipboard();
645 			RestartCursorBlinking();
646 			ScrollToCursor();
647 			event.Eat();
648 		}
649 		if ((event.IsKey(EM_KEY_INSERT) && state.IsShiftMod()) ||
650 		    (event.IsKey(EM_KEY_V) && state.IsCtrlMod())) {
651 			PasteSelectedTextFromClipboard();
652 			RestartCursorBlinking();
653 			ScrollToCursor();
654 			event.Eat();
655 		}
656 		if (event.IsKey(EM_KEY_BACKSPACE) && state.IsCtrlMod()) {
657 			if (IsSelectionEmpty()) {
658 				Select(GetPrevWordIndex(CursorIndex),CursorIndex,false);
659 			}
660 			DeleteSelectedText();
661 			RestartCursorBlinking();
662 			ScrollToCursor();
663 			event.Eat();
664 		}
665 		if (event.IsKey(EM_KEY_DELETE) && state.IsCtrlMod()) {
666 			if (IsSelectionEmpty()) {
667 				Select(
668 					CursorIndex,
669 					GetNextWordIndex(CursorIndex),
670 					false
671 				);
672 			}
673 			DeleteSelectedText();
674 			RestartCursorBlinking();
675 			ScrollToCursor();
676 			event.Eat();
677 		}
678 		if (event.IsKey(EM_KEY_BACKSPACE) && state.IsShiftCtrlMod()) {
679 			if (IsSelectionEmpty()) {
680 				Select(GetRowStartIndex(CursorIndex),CursorIndex,false);
681 			}
682 			DeleteSelectedText();
683 			RestartCursorBlinking();
684 			ScrollToCursor();
685 			event.Eat();
686 		}
687 		if (event.IsKey(EM_KEY_DELETE) && state.IsShiftCtrlMod()) {
688 			if (IsSelectionEmpty()) {
689 				Select(CursorIndex,GetRowEndIndex(CursorIndex),false);
690 			}
691 			DeleteSelectedText();
692 			RestartCursorBlinking();
693 			ScrollToCursor();
694 			event.Eat();
695 		}
696 		if (MultiLineMode && event.IsKey(EM_KEY_ENTER) && state.IsNoMod()) {
697 			PasteSelectedText("\n");
698 			RestartCursorBlinking();
699 			ScrollToCursor();
700 			event.Eat();
701 		}
702 		if (
703 			!event.GetChars().IsEmpty() &&
704 			(unsigned char)(event.GetChars()[0])>=32 &&
705 			event.GetChars()[0]!=127
706 		) {
707 			if (
708 				OverwriteMode && IsSelectionEmpty() &&
709 				CursorIndex<GetRowEndIndex(CursorIndex)
710 			) {
711 				Select(CursorIndex,GetNextIndex(CursorIndex),false);
712 			}
713 			PasteSelectedText(event.GetChars());
714 			RestartCursorBlinking();
715 			ScrollToCursor();
716 			event.Eat();
717 		}
718 	}
719 
720 	emBorder::Input(event,state,mx,my);
721 }
722 
723 
HasHowTo() const724 bool emTextField::HasHowTo() const
725 {
726 	return true;
727 }
728 
729 
GetHowTo() const730 emString emTextField::GetHowTo() const
731 {
732 	emString h;
733 
734 	h=emBorder::GetHowTo();
735 	h+=HowToTextField;
736 	if (MultiLineMode) h+=HowToMultiLineOn; else h+=HowToMultiLineOff;
737 	if (!IsEditable()) h+=HowToReadOnly;
738 	return h;
739 }
740 
741 
PaintContent(const emPainter & painter,double x,double y,double w,double h,emColor canvasColor) const742 void emTextField::PaintContent(
743 	const emPainter & painter, double x, double y, double w, double h,
744 	emColor canvasColor
745 ) const
746 {
747 	DoTextField(TEXT_FIELD_FUNC_PAINT,&painter,canvasColor,0.0,0.0,NULL,NULL,NULL);
748 }
749 
750 
CheckMouse(double mx,double my,double * pCol,double * pRow) const751 bool emTextField::CheckMouse(
752 	double mx, double my, double * pCol, double * pRow
753 ) const
754 {
755 	bool b;
756 
757 	DoTextField(TEXT_FIELD_FUNC_XY2CR,NULL,0,mx,my,pCol,pRow,&b);
758 	return b;
759 }
760 
761 
CheckMouse(double mx,double my,double * pCol,double * pRow)762 bool emTextField::CheckMouse(
763 	double mx, double my, double * pCol, double * pRow
764 )
765 {
766 	return ((const emTextField*)this)->CheckMouse(mx,my,pCol,pRow);
767 }
768 
769 
DoTextField(DoTextFieldFunc func,const emPainter * painter,emColor canvasColor,double xIn,double yIn,double * pXOut,double * pYOut,bool * pHit) const770 void emTextField::DoTextField(
771 	DoTextFieldFunc func, const emPainter * painter, emColor canvasColor,
772 	double xIn, double yIn, double * pXOut, double * pYOut, bool * pHit
773 ) const
774 {
775 	emColor bgColor,fgColor,hlColor,selColor,curColor;
776 	emString txt;
777 	double xy[10*2];
778 	double x,y,w,h,r,ws,d,d1,d2,cx,cy,cw,ch,dx,dy,tx,ty,tw,th;
779 	int i,i0,j,n,c,rows,cols,col,row,col0,row0,selIdx,selEnd;
780 	bool selected,selected0;
781 
782 	GetContentRoundRect(&x,&y,&w,&h,&r);
783 
784 	d=emMin(h,w)*0.1+r*0.5;
785 	tx=x+d;
786 	ty=y+d;
787 	tw=w-2*d;
788 	th=h-2*d;
789 
790 	CalcTotalColsRows(&cols,&rows);
791 	if (OverwriteMode && IsInFocusedPath()) {
792 		Index2ColRow(CursorIndex,&col,&row);
793 		if (col==cols) cols++;
794 	}
795 	ch=th/rows;
796 	cw=emPainter::GetTextSize("X",ch,false);
797 
798 	ws=1.0;
799 	if (cw*cols>tw) {
800 		ws=tw/(cw*cols);
801 		cw=tw/cols;
802 		d=0.66;
803 		if (ws<d) {
804 			ty+=(ch-ch*ws/d)*0.5;
805 			ch-=ch-ch*ws/d;
806 			ws=d;
807 		}
808 	}
809 
810 	if (func!=TEXT_FIELD_FUNC_PAINT) {
811 		if (func==TEXT_FIELD_FUNC_XY2CR) {
812 			*pXOut=(xIn-tx)/cw;
813 			*pYOut=(yIn-ty)/ch;
814 		}
815 		else {
816 			xIn=tx+xIn*cw;
817 			yIn=ty+yIn*ch;
818 			*pXOut=xIn;
819 			*pYOut=yIn;
820 		}
821 		dx=emMax(emMax(x-xIn,xIn-x-w)+r,0.0);
822 		dy=emMax(emMax(y-yIn,yIn-y-h)+r,0.0);
823 		*pHit = dx*dx+dy*dy <= r*r;
824 		return;
825 	}
826 
827 	if (IsEditable()) {
828 		bgColor=GetLook().GetInputBgColor();
829 		fgColor=GetLook().GetInputFgColor();
830 		hlColor=GetLook().GetInputHlColor();
831 	}
832 	else {
833 		bgColor=GetLook().GetOutputBgColor();
834 		fgColor=GetLook().GetOutputFgColor();
835 		hlColor=GetLook().GetOutputHlColor();
836 	}
837 
838 	if (!IsEnabled()) {
839 		bgColor=bgColor.GetBlended(GetLook().GetBgColor(),80.0F);
840 		fgColor=fgColor.GetBlended(GetLook().GetBgColor(),80.0F);
841 		hlColor=hlColor.GetBlended(GetLook().GetBgColor(),80.0F);
842 	}
843 
844 	selColor=hlColor;
845 	selIdx=GetSelectionStartIndex();
846 	selEnd=GetSelectionEndIndex();
847 	if (selIdx<selEnd) {
848 		if (!IsInFocusedPath()) {
849 			selColor=bgColor.GetBlended(fgColor,40.0);
850 		}
851 		Index2ColRow(selIdx,&col0,&row0);
852 		Index2ColRow(selEnd,&col,&row);
853 		xy[ 0]=tx+col0*cw; xy[ 1]=ty+row0*ch;
854 		xy[ 2]=tx+tw;      xy[ 3]=ty+row0*ch;
855 		xy[ 4]=tx+tw;      xy[ 5]=ty+row*ch;
856 		xy[ 6]=tx+col*cw;  xy[ 7]=ty+row*ch;
857 		xy[ 8]=tx+col*cw;  xy[ 9]=ty+row*ch+ch;
858 		xy[10]=tx;         xy[11]=ty+row*ch+ch;
859 		xy[12]=tx;         xy[13]=ty+row0*ch+ch;
860 		xy[14]=tx+col0*cw; xy[15]=ty+row0*ch+ch;
861 		painter->PaintPolygon(xy,8,selColor,canvasColor);
862 	}
863 
864 	row0=0;
865 	col0=0;
866 	i0=0;
867 	selected0=(i0>=selIdx && i0<selEnd);
868 	row=0;
869 	col=0;
870 	emMBState mbState;
871 	for (i=0;;) {
872 		n=emDecodeChar(&c,Text.Get()+i,INT_MAX,&mbState);
873 		selected=(i>=selIdx && i<selEnd);
874 		if (
875 			(
876 				c<=0x0d &&
877 				(c==0 || (MultiLineMode && (c==0x09 || c==0x0d || c==0x0a)))
878 			) ||
879 			selected0!=selected
880 		) {
881 			if (i0<i) {
882 				if (PasswordMode) {
883 					for (j=0; j<col-col0; j++) {
884 						painter->PaintText(
885 							tx+(col0+j)*cw,ty+row0*ch,"*",ch,ws,
886 							selected0 ? bgColor : fgColor,
887 							selected0 ? selColor : canvasColor
888 						);
889 					}
890 				}
891 				else {
892 					painter->PaintText(
893 						tx+col0*cw,ty+row0*ch,Text.Get()+i0,ch,ws,
894 						selected0 ? bgColor : fgColor,
895 						selected0 ? selColor : canvasColor,
896 						i-i0
897 					);
898 				}
899 			}
900 			if (c==0) break;
901 			row0=row;
902 			col0=col;
903 			i0=i;
904 			selected0=selected;
905 		}
906 		i+=n;
907 		col++;
908 		if (c<=0x0d && MultiLineMode) {
909 			if (c==0x09) {
910 				col=(col+7)&~7;
911 				col0=col;
912 				i0=i;
913 				selected0=(i0>=selIdx && i0<selEnd);
914 			}
915 			else if (c==0x0a || c==0x0d) {
916 				if (c==0x0d && Text[i]==0x0a) i++;
917 				col=0;
918 				row++;
919 				row0=row;
920 				col0=col;
921 				i0=i;
922 				selected0=(i0>=selIdx && i0<selEnd);
923 			}
924 		}
925 	}
926 
927 	if (IsInFocusedPath()) {
928 		curColor=fgColor;
929 		if (!IsEditable()) curColor=curColor.GetTransparented(75.0F);
930 		else if (!IsCursorBlinkOn()) curColor=curColor.GetTransparented(88.0F);
931 		Index2ColRow(CursorIndex,&col,&row);
932 		cx=tx+cw*col;
933 		cy=ty+ch*row;
934 		if (GetOverwriteMode()) {
935 			d=ch*0.07;
936 			xy[ 0]=cx-d;    xy[ 1]=cy-d;
937 			xy[ 2]=cx+cw+d; xy[ 3]=cy-d;
938 			xy[ 4]=cx+cw+d; xy[ 5]=cy+ch+d;
939 			xy[ 6]=cx-d;    xy[ 7]=cy+ch+d;
940 			xy[ 8]=cx-d;    xy[ 9]=cy-d;
941 			xy[10]=cx;      xy[11]=cy;
942 			xy[12]=cx;      xy[13]=cy+ch;
943 			xy[14]=cx+cw;   xy[15]=cy+ch;
944 			xy[16]=cx+cw;   xy[17]=cy;
945 			xy[18]=cx;      xy[19]=cy;
946 			painter->PaintPolygon(xy,10,curColor);
947 		}
948 		else {
949 			d=ch*0.07;
950 			d1=d*0.5;
951 			d2=d*2.2;
952 			xy[ 0]=cx-d2; xy[ 1]=cy-d;
953 			xy[ 2]=cx+d2; xy[ 3]=cy-d;
954 			xy[ 4]=cx+d1; xy[ 5]=cy;
955 			xy[ 6]=cx+d1; xy[ 7]=cy+ch;
956 			xy[ 8]=cx+d2; xy[ 9]=cy+ch+d;
957 			xy[10]=cx-d2; xy[11]=cy+ch+d;
958 			xy[12]=cx-d1; xy[13]=cy+ch;
959 			xy[14]=cx-d1; xy[15]=cy;
960 			painter->PaintPolygon(xy,8,curColor);
961 		}
962 	}
963 }
964 
965 
SetDragMode(DragModeType dragMode)966 void emTextField::SetDragMode(DragModeType dragMode)
967 {
968 	if (DragMode!=dragMode) {
969 		DragMode=dragMode;
970 		InvalidatePainting();
971 		RestartCursorBlinking();
972 	}
973 }
974 
975 
RestartCursorBlinking()976 void emTextField::RestartCursorBlinking()
977 {
978 	CursorBlinkTime=emGetClockMS();
979 	if (!CursorBlinkOn) {
980 		CursorBlinkOn=true;
981 		InvalidatePainting();
982 	}
983 }
984 
985 
ScrollToCursor()986 void emTextField::ScrollToCursor()
987 {
988 	int col,row;
989 	double x1,y1,x2,y2,dx,dy;
990 	bool b;
991 
992 	if (!IsViewed() || !IsActive()) return;
993 	Index2ColRow(CursorIndex,&col,&row);
994 	DoTextField(TEXT_FIELD_FUNC_CR2XY,NULL,0,col-0.5,row-0.2,&x1,&y1,&b);
995 	DoTextField(TEXT_FIELD_FUNC_CR2XY,NULL,0,col+0.5,row+1.2,&x2,&y2,&b);
996 	b=false;
997 	dx=PanelToViewX(x1)-GetView().GetCurrentX();
998 	if (dx<0.0) b=true;
999 	else {
1000 		dx=PanelToViewX(x2)-GetView().GetCurrentX()-GetView().GetCurrentWidth();
1001 		if (dx>0.0) b=true; else dx=0.0;
1002 	}
1003 	dy=PanelToViewY(y1)-GetView().GetCurrentY();
1004 	if (dy<0.0) b=true;
1005 	else {
1006 		dy=PanelToViewY(y2)-GetView().GetCurrentY()-GetView().GetCurrentHeight();
1007 		if (dy>0.0) b=true; else dy=0.0;
1008 	}
1009 	if (b) {
1010 		GetView().Scroll(dx,dy);
1011 		if (!IsActive()) Activate();
1012 	}
1013 }
1014 
1015 
ColRow2Index(double column,double row,bool forCursor) const1016 int emTextField::ColRow2Index(double column, double row, bool forCursor) const
1017 {
1018 	int i,j,k,n,c;
1019 
1020 	if (!MultiLineMode) {
1021 		emMBState mbState;
1022 		for (i=0; ; i+=n, column-=1.0) {
1023 			if (forCursor) { if (column<0.5) break; }
1024 			else           { if (column<1.0) break; }
1025 			n=emDecodeChar(&c,Text.Get()+i,INT_MAX,&mbState);
1026 			if (c==0) break;
1027 		}
1028 	}
1029 	else {
1030 		emMBState mbState,mbState2;
1031 		for (j=0, i=0; row>=1.0; ) {
1032 			j+=emDecodeChar(&c,Text.Get()+j,INT_MAX,&mbState);
1033 			if (c==0x0a || c==0x0d) {
1034 				if (c==0x0d && Text[j]==0x0a) j++;
1035 				mbState2=mbState;
1036 				i=j;
1037 				row-=1.0;
1038 			}
1039 			if (c==0) break;
1040 		}
1041 		for (j=0; ; i+=n, j=k) {
1042 			n=emDecodeChar(&c,Text.Get()+i,INT_MAX,&mbState2);
1043 			if (c==0x0a || c==0x0d || c==0) break;
1044 			k=j+1;
1045 			if (c==0x09) k=(k+7)&~7;
1046 			if (column<=k) {
1047 				if (forCursor) { if (k-column<column-j) i+=n; }
1048 				else           { if (column==k)         i+=n; }
1049 				break;
1050 			}
1051 		}
1052 	}
1053 	return i;
1054 }
1055 
1056 
Index2ColRow(int index,int * pColumn,int * pRow) const1057 void emTextField::Index2ColRow(int index, int * pColumn, int * pRow) const
1058 {
1059 	int i,col,row,n,c;
1060 
1061 	if (!MultiLineMode) {
1062 		col=emGetDecodedCharCount(Text,index);
1063 		row=0;
1064 	}
1065 	else {
1066 		col=0;
1067 		row=0;
1068 		emMBState mbState;
1069 		for (i=0; i<index; i+=n) {
1070 			n=emDecodeChar(&c,Text.Get()+i,INT_MAX,&mbState);
1071 			if (c==0x09) {
1072 				col=(col+8)&~7;
1073 			}
1074 			else if (c==0x0a || c==0x0d) {
1075 				if (c==0x0d && Text[i+1]==0x0a) n++;
1076 				col=0;
1077 				row++;
1078 			}
1079 			else if (c==0) {
1080 				break;
1081 			}
1082 			else {
1083 				col++;
1084 			}
1085 		}
1086 	}
1087 	*pColumn=col;
1088 	*pRow=row;
1089 }
1090 
1091 
CalcTotalColsRows(int * pCols,int * pRows) const1092 void emTextField::CalcTotalColsRows(int * pCols, int * pRows) const
1093 {
1094 	int i,cols,rows,rowcols,n,c;
1095 
1096 	if (!MultiLineMode) {
1097 		cols=emGetDecodedCharCount(Text);
1098 		rows=1;
1099 	}
1100 	else {
1101 		cols=0;
1102 		rows=1;
1103 		rowcols=0;
1104 		emMBState mbState;
1105 		for (i=0; ; i+=n) {
1106 			n=emDecodeChar(&c,Text.Get()+i,INT_MAX,&mbState);
1107 			if (c==0x09) {
1108 				rowcols=(rowcols+8)&~7;
1109 			}
1110 			else if (c==0x0a || c==0x0d) {
1111 				if (cols<rowcols) cols=rowcols;
1112 				if (c==0x0d && Text[i+1]==0x0a) n++;
1113 				rowcols=0;
1114 				rows++;
1115 			}
1116 			else if (c==0) {
1117 				break;
1118 			}
1119 			else {
1120 				rowcols++;
1121 			}
1122 		}
1123 		if (cols<rowcols) cols=rowcols;
1124 	}
1125 
1126 	if (cols<1) cols=1;
1127 	if (rows<1) rows=1;
1128 	*pCols=cols;
1129 	*pRows=rows;
1130 }
1131 
1132 
GetNormalizedIndex(int index) const1133 int emTextField::GetNormalizedIndex(int index) const
1134 {
1135 	int i,j;
1136 
1137 	emMBState mbState;
1138 	for (i=0; ; i=j) {
1139 		j=GetNextIndex(i,&mbState);
1140 		if (j>index || j==i) return i;
1141 	}
1142 }
1143 
1144 
ModifySelection(int oldIndex,int newIndex,bool publish)1145 void emTextField::ModifySelection(int oldIndex, int newIndex, bool publish)
1146 {
1147 	int d1,d2;
1148 
1149 	if (SelectionStartIndex<SelectionEndIndex) {
1150 		d1=oldIndex-SelectionStartIndex; if (d1<0) d1=-d1;
1151 		d2=oldIndex-SelectionEndIndex; if (d2<0) d2=-d2;
1152 		if (d1<d2) oldIndex=SelectionEndIndex;
1153 		else oldIndex=SelectionStartIndex;
1154 	}
1155 	if (oldIndex<newIndex) Select(oldIndex,newIndex,publish);
1156 	else Select(newIndex,oldIndex,publish);
1157 }
1158 
1159 
GetMBStateAtIndex(int index) const1160 emMBState emTextField::GetMBStateAtIndex(int index) const
1161 {
1162 	int i,j;
1163 
1164 	emMBState mbState;
1165 	for (i=0; ; i=j) {
1166 		j=GetNextIndex(i,&mbState);
1167 		if (j>index || j==i) return mbState;
1168 	}
1169 }
1170 
1171 
GetNextIndex(int index,emMBState * mbState) const1172 int emTextField::GetNextIndex(int index, emMBState * mbState) const
1173 {
1174 	int c;
1175 
1176 	emMBState ownMBState;
1177 	if (!mbState) {
1178 		ownMBState=GetMBStateAtIndex(index);
1179 		mbState=&ownMBState;
1180 	}
1181 
1182 	index+=emDecodeChar(&c,Text.Get()+index,INT_MAX,mbState);
1183 	if (c==0x0d && Text[index]==0x0a && MultiLineMode) index++;
1184 	return index;
1185 }
1186 
1187 
GetPrevIndex(int index) const1188 int emTextField::GetPrevIndex(int index) const
1189 {
1190 	int i,j;
1191 
1192 	emMBState mbState;
1193 	for (i=0; ; i=j) {
1194 		j=GetNextIndex(i,&mbState);
1195 		if (j>=index || j==i) return i;
1196 	}
1197 }
1198 
1199 
GetNextWordBoundaryIndex(int index,bool * pIsDelimiter,emMBState * mbState) const1200 int emTextField::GetNextWordBoundaryIndex(
1201 	int index, bool * pIsDelimiter, emMBState * mbState
1202 ) const
1203 {
1204 	const char * p;
1205 	int i,n,c;
1206 	bool prevDelim,delim,first;
1207 
1208 	emMBState ownMBState;
1209 	if (!mbState) {
1210 		ownMBState=GetMBStateAtIndex(index);
1211 		mbState=&ownMBState;
1212 	}
1213 
1214 	p=Text.Get();
1215 	i=index;
1216 	delim=false;
1217 	first=true;
1218 	for (;;) {
1219 		emMBState prevMBState(*mbState);
1220 		n=emDecodeChar(&c,p+i,INT_MAX,mbState);
1221 		if (n<=0) {
1222 			*mbState=prevMBState;
1223 			delim=true;
1224 			break;
1225 		}
1226 		prevDelim=delim;
1227 		if (
1228 			PasswordMode ||
1229 			(c>='0' && c<='9') ||
1230 			(c>='A' && c<='Z') ||
1231 			(c>='a' && c<='z') ||
1232 			c=='_' ||
1233 			c>127
1234 		) {
1235 			delim=false;
1236 		}
1237 		else {
1238 			delim=true;
1239 		}
1240 		if (!first && delim!=prevDelim) {
1241 			*mbState=prevMBState;
1242 			break;
1243 		}
1244 		i+=n;
1245 		first=false;
1246 	}
1247 	if (pIsDelimiter) *pIsDelimiter=delim;
1248 	return i;
1249 }
1250 
1251 
GetPrevWordBoundaryIndex(int index,bool * pIsDelimiter) const1252 int emTextField::GetPrevWordBoundaryIndex(
1253 	int index, bool * pIsDelimiter
1254 ) const
1255 {
1256 	int i,j;
1257 
1258 	emMBState mbState;
1259 	for (i=0; ; i=j) {
1260 		j=GetNextWordBoundaryIndex(i,pIsDelimiter,&mbState);
1261 		if (j>=index || j==i) return i;
1262 	}
1263 }
1264 
1265 
GetNextWordIndex(int index,emMBState * mbState) const1266 int emTextField::GetNextWordIndex(int index, emMBState * mbState) const
1267 {
1268 	bool isDelim;
1269 
1270 	emMBState ownMBState;
1271 	if (!mbState) {
1272 		ownMBState=GetMBStateAtIndex(index);
1273 		mbState=&ownMBState;
1274 	}
1275 
1276 	for (;;) {
1277 		index=GetNextWordBoundaryIndex(index,&isDelim,mbState);
1278 		if (!isDelim || index>=TextLen) break;
1279 	}
1280 	return index;
1281 }
1282 
1283 
GetPrevWordIndex(int index) const1284 int emTextField::GetPrevWordIndex(int index) const
1285 {
1286 	int i,j;
1287 
1288 	emMBState mbState;
1289 	for (i=0; ; i=j) {
1290 		j=GetNextWordIndex(i,&mbState);
1291 		if (j>=index || j==i) return i;
1292 	}
1293 }
1294 
1295 
GetRowStartIndex(int index) const1296 int emTextField::GetRowStartIndex(int index) const
1297 {
1298 	int i,j,c;
1299 
1300 	if (!MultiLineMode) return 0;
1301 	emMBState mbState;
1302 	for (i=0, j=0; ; ) {
1303 		i+=emDecodeChar(&c,Text.Get()+i,INT_MAX,&mbState);
1304 		if (c==0x0d && Text[i]==0x0a) i++;
1305 		if (c==0 || i>index) return j;
1306 		if (c==0x0a || c==0x0d) j=i;
1307 	}
1308 }
1309 
1310 
GetRowEndIndex(int index) const1311 int emTextField::GetRowEndIndex(int index) const
1312 {
1313 	int n,c;
1314 
1315 	if (!MultiLineMode) return TextLen;
1316 	emMBState mbState=GetMBStateAtIndex(index);
1317 	for (;; index+=n) {
1318 		n=emDecodeChar(&c,Text.Get()+index,INT_MAX,&mbState);
1319 		if (c==0 || c==0x0a || c==0x0d) return index;
1320 	}
1321 }
1322 
1323 
GetNextRowIndex(int index,emMBState * mbState) const1324 int emTextField::GetNextRowIndex(int index, emMBState * mbState) const
1325 {
1326 	int c;
1327 
1328 	emMBState ownMBState;
1329 	if (!mbState) {
1330 		ownMBState=GetMBStateAtIndex(index);
1331 		mbState=&ownMBState;
1332 	}
1333 
1334 	if (!MultiLineMode) return TextLen;
1335 	for (;;) {
1336 		index+=emDecodeChar(&c,Text.Get()+index,INT_MAX,mbState);
1337 		if (c==0 || c==0x0a || c==0x0d) {
1338 			if (c==0x0d && Text[index]==0x0a) index++;
1339 			return index;
1340 		}
1341 	}
1342 }
1343 
1344 
GetPrevRowIndex(int index) const1345 int emTextField::GetPrevRowIndex(int index) const
1346 {
1347 	int i,j;
1348 
1349 	emMBState mbState;
1350 	for (i=0; ; i=j) {
1351 		j=GetNextRowIndex(i,&mbState);
1352 		if (j>=index || j==i) return i;
1353 	}
1354 }
1355 
1356 
GetNextParagraphIndex(int index,emMBState * mbState) const1357 int emTextField::GetNextParagraphIndex(int index, emMBState * mbState) const
1358 {
1359 	bool e;
1360 
1361 	if (!MultiLineMode) return TextLen;
1362 
1363 	emMBState ownMBState;
1364 	if (!mbState) {
1365 		ownMBState=GetMBStateAtIndex(index);
1366 		mbState=&ownMBState;
1367 	}
1368 
1369 	for (e=false; index<TextLen; ) {
1370 		index=GetNextRowIndex(index,mbState);
1371 		if (Text[index]==0x0a || Text[index]==0x0d) e=true;
1372 		else if (e) break;
1373 	}
1374 	return index;
1375 }
1376 
1377 
GetPrevParagraphIndex(int index) const1378 int emTextField::GetPrevParagraphIndex(int index) const
1379 {
1380 	int i,j;
1381 
1382 	emMBState mbState;
1383 	for (i=0; ; i=j) {
1384 		j=GetNextParagraphIndex(i,&mbState);
1385 		if (j>=index || j==i) return i;
1386 	}
1387 }
1388 
1389 
1390 const char * emTextField::HowToTextField=
1391 	"\n"
1392 	"\n"
1393 	"TEXT FIELD\n"
1394 	"\n"
1395 	"This is a text field. In such a field, a text can be viewed and edited.\n"
1396 	"\n"
1397 	"Quick hint about an incompatibility against other user interfaces: For inserting\n"
1398 	"selected text, press Ctrl + left mouse button instead of the middle mouse\n"
1399 	"button.\n"
1400 	"\n"
1401 	"Mouse control:\n"
1402 	"\n"
1403 	"  Left-Button-Click        - Set cursor position, clear selection.\n"
1404 	"\n"
1405 	"  Left-Button-Double-Click - Select a word.\n"
1406 	"\n"
1407 	"  Left-Button-Triple-Click - Select a row.\n"
1408 	"\n"
1409 	"  Left-Button-Quad-Click   - Select all.\n"
1410 	"\n"
1411 	"  Left-Button-Drag         - Select passed characters.\n"
1412 	"\n"
1413 	"  Shift+Left-Button-Drag   - Extend or reduce selection by passed characters.\n"
1414 	"\n"
1415 	"  Ctrl+Left-Button-Click on non-selected area - Insert a copy of common selected\n"
1416 	"                                                text.\n"
1417 	"\n"
1418 	"  Ctrl+Left-Button-Drag on selected area      - Move selected text.\n"
1419 	"\n"
1420 	"\n"
1421 	"Keyboard control:\n"
1422 	"\n"
1423 	"  Normal key input inserts the corresponding character at the cursor position.\n"
1424 	"  Any selected text is replaced by the character. Special key combinations are:\n"
1425 	"\n"
1426 	"  Cursor-Keys             - Move the cursor.\n"
1427 	"\n"
1428 	"  Ctrl+Cursor-Keys        - Move the cursor by words or paragraphs.\n"
1429 	"\n"
1430 	"  Home or End             - Move the cursor to beginning or end of row.\n"
1431 	"\n"
1432 	"  Ctrl+Home or Ctrl+End   - Move the cursor to beginning or end of all.\n"
1433 	"\n"
1434 	"  Shift+<Cursor Movement> - Select text: Hold the Shift key while moving the\n"
1435 	"                            cursor with one of the above key combinations, to\n"
1436 	"                            select the passed characters.\n"
1437 	"\n"
1438 	"  Ctrl+A                  - Select the whole text.\n"
1439 	"\n"
1440 	"  Insert                  - Switch between insert mode and replace mode.\n"
1441 	"\n"
1442 	"  Backspace               - Delete the selected text, or the character on the\n"
1443 	"                            left side of the cursor.\n"
1444 	"\n"
1445 	"  Delete                  - Delete the selected text, or the character on the\n"
1446 	"                            right side of the cursor.\n"
1447 	"\n"
1448 	"  Shift+Delete or Ctrl+X  - Cut operation: Copy the selected text to the\n"
1449 	"                            clipboard and delete it.\n"
1450 	"\n"
1451 	"  Ctrl+Insert or Ctrl+C   - Copy operation: Copy the selected text to the\n"
1452 	"                            clipboard.\n"
1453 	"\n"
1454 	"  Shift+Insert or Ctrl+V  - Paste operation: Insert text from the clipboard. Any\n"
1455 	"                            selected text is replaced by the insertion.\n"
1456 	"\n"
1457 	"  Ctrl+Backspace          - Delete to the left until beginning of a word.\n"
1458 	"\n"
1459 	"  Ctrl+Delete             - Delete to the right until beginning of a word.\n"
1460 	"\n"
1461 	"  Shift+Ctrl+Backspace    - Delete all on the left side of the cursor.\n"
1462 	"\n"
1463 	"  Shift+Ctrl+Delete       - Delete all on the right side of the cursor.\n"
1464 ;
1465 
1466 
1467 const char * emTextField::HowToMultiLineOff=
1468 	"\n"
1469 	"\n"
1470 	"MULTI-LINE: DISABLED\n"
1471 	"\n"
1472 	"This text field has the multi-line mode disabled. You can view or edit only\n"
1473 	"a single line.\n"
1474 ;
1475 
1476 
1477 const char * emTextField::HowToMultiLineOn=
1478 	"\n"
1479 	"\n"
1480 	"MULTI-LINE: ENABLED\n"
1481 	"\n"
1482 	"This text field has the multi-line mode enabled. You may view or edit multiple\n"
1483 	"lines.\n"
1484 ;
1485 
1486 
1487 const char * emTextField::HowToReadOnly=
1488 	"\n"
1489 	"\n"
1490 	"READ-ONLY\n"
1491 	"\n"
1492 	"This text field is read-only. You cannot edit the text.\n"
1493 ;
1494