1 #include "RichEdit.h"
2 
3 namespace Upp {
4 
FinishNF()5 void RichEdit::FinishNF()
6 {
7 	cursor = clamp(cursor, 0, text.GetLength());
8 	anchor = clamp(anchor, 0, text.GetLength());
9 	anchorp = text.GetRichPos(anchor);
10 	cursorp = text.GetRichPos(cursor);
11 	tablesel = 0;
12 	begtabsel = false;
13 	if(anchor != cursor) {
14 		RichPos p = text.GetRichPos(cursor, anchorp.level);
15 		if(anchorp.level == 0 || anchorp.level < cursorp.level) {
16 			cursor = text.AdjustCursor(anchor, cursor);
17 			cursorp = text.GetRichPos(cursor);
18 		}
19 		else
20 		if(p.table != anchorp.table) {
21 			if(anchor == 0 && anchorp.level == 1 && text.GetRichPos(anchor, 1).table == 1 && anchor < cursor) {
22 				while(cursor > 0 && cursorp.level) // selection must be at plain text
23 					cursorp = text.GetRichPos(--cursor);
24 				begtabsel = true;
25 				anchor = 0;
26 			}
27 			else {
28 				tablesel = anchorp.table;
29 				if(cursor < anchor) {
30 					cells.left = 0;
31 					cells.right = anchorp.cell.x;
32 					cells.top = 0;
33 					cells.bottom = anchorp.cell.y;
34 				}
35 				else {
36 					cells.left = anchorp.cell.x;
37 					cells.right = anchorp.tabsize.cx - 1;
38 					cells.top = anchorp.cell.y;
39 					cells.bottom = anchorp.tabsize.cy - 1;
40 				}
41 				text.AdjustTableSel(tablesel, cells);
42 			}
43 		}
44 		else
45 		if(p.cell != anchorp.cell) {
46 			tablesel = anchorp.table;
47 			cells.left = min(anchorp.cell.x, p.cell.x);
48 			cells.right = max(anchorp.cell.x, p.cell.x);
49 			cells.top = min(anchorp.cell.y, p.cell.y);
50 			cells.bottom = max(anchorp.cell.y, p.cell.y);
51 			text.AdjustTableSel(tablesel, cells);
52 		}
53 	}
54 	cursorc = text.GetCaret(cursor, pagesz);
55 	Size sz = GetSize();
56 	SetSb();
57 	Rect r = PlaceCaret();
58 	if(r.top == GetPosY(text.GetCaret(0, pagesz)))
59 		sb = 0;
60 	else
61 		sb.ScrollInto(r.top, r.Height());
62 	sb.ScrollInto(r.bottom, 1); // if r.Height is bigger than view height, make sure we rather see the bottom
63 	SetZsc();
64 	PageY top, bottom;
65 	int sell = min(cursor, anchor);
66 	int selh = max(cursor, anchor);
67 	if(tablesel)
68 		Refresh();
69 	else
70 	if(text.GetInvalid(top, bottom, pagesz, sell, selh, osell, oselh)) {
71 		int y = GetPosY(top);
72 		Refresh(0, y - sb, sz.cx, GetPosY(bottom) - y);
73 		y = GetPosY(text.GetHeight(pagesz)) - sb;
74 		if(y < sz.cy)
75 			Refresh(0, y, sz.cx, sz.cy - y);
76 	}
77 	osell = sell;
78 	oselh = selh;
79 	text.Validate();
80 	FixObjectRect();
81 	SetupRuler();
82 	if(modified) {
83 		if(useraction)
84 			Action();
85 	}
86 	useraction = modified = false;
87 }
88 
Finish()89 void RichEdit::Finish()
90 {
91 	FinishNF();
92 	ReadFormat();
93 }
94 
MoveNG(int newpos,bool select)95 void RichEdit::MoveNG(int newpos, bool select)
96 {
97 	if(newpos < 0) newpos = 0;
98 	if(newpos >= text.GetLength() + select) newpos = text.GetLength() + select;
99 	CloseFindReplace();
100 	cursor = newpos;
101 	if(!select) {
102 		anchor = cursor;
103 		begtabsel = false;
104 	}
105 	objectpos = -1;
106 	Finish();
107 	if(select)
108 		SetSelectionSource(String().Cat() << "text/QTF;Rich Text Format;text/rtf;application/rtf;"
109 		                   << ClipFmtsText());
110 }
111 
Move(int newpos,bool select)112 void RichEdit::Move(int newpos, bool select)
113 {
114 	MoveNG(newpos, select);
115 	gx = cursorc.left;
116 }
117 
MoveUpDown(int dir,bool select,int pg)118 void RichEdit::MoveUpDown(int dir, bool select, int pg)
119 {
120 	Rect page = pagesz;
121 	if(dir > 0 && cursor >= GetLength() && select) {
122 		Move(GetLength() + 1, true);
123 		return;
124 	}
125 	if(dir < 0 && cursor > GetLength()) {
126 		Move(GetLength(), select);
127 		return;
128 	}
129 	int c = text.GetVertMove(min(GetLength(), cursor), gx, page, dir);
130 	if(c >= 0)
131 		MoveNG(c, select);
132 	else
133 		Move(dir < 0 ? 0 : GetLength(), select);
134 	if(pg) {
135 		RichCaret pr = text.GetCaret(cursor, pagesz);
136 		PageY py;
137 		py.page = pr.page;
138 		py.y = pr.top + dir * pg;
139 		while(py.y > pagesz.cy) {
140 			py.y -= pagesz.cy;
141 			py.page++;
142 		}
143 		while(py.y < 0) {
144 			py.y += pagesz.cy;
145 			py.page--;
146 		}
147 		MoveNG(text.GetPos(pr.left, py, pagesz), select);
148 	}
149 }
150 
MovePageUpDown(int dir,bool select)151 void RichEdit::MovePageUpDown(int dir, bool select)
152 {
153 	PageRect p = text.GetCaret(cursor, pagesz);
154 	int q = GetPosY(p) - sb;
155 	MoveUpDown(dir, select, 4 * GetTextRect().Height() / GetZoom() / 5);
156 	p = text.GetCaret(cursor, pagesz);
157 	sb = GetPosY(p) - q;
158 }
159 
MoveHomeEnd(int dir,bool select)160 void RichEdit::MoveHomeEnd(int dir, bool select)
161 {
162 	int c = cursor;
163 	while(c + dir >= 0 && c + dir <= text.GetLength()) {
164 		PageRect p1 = text.GetCaret(c + dir, pagesz);
165 		if(p1.page != cursorc.page || p1.top != cursorc.top)
166 			break;
167 		c += dir;
168 	}
169 	Move(c, select);
170 }
171 
IsW(int c)172 bool RichEdit::IsW(int c)
173 {
174 	return IsLetter(c) || IsDigit(c) || c == '_';
175 }
176 
MoveWordRight(bool select)177 void RichEdit::MoveWordRight(bool select)
178 {
179 	Move((int)GetNextWord(cursor), select);
180 }
181 
MoveWordLeft(bool select)182 void RichEdit::MoveWordLeft(bool select)
183 {
184 	Move((int)GetPrevWord(cursor), select);
185 }
186 
SelBeg(bool select)187 bool RichEdit::SelBeg(bool select)
188 {
189 	if(IsSelection() && !select) {
190 		Move(min(cursor, anchor), false);
191 		return true;
192 	}
193 	return false;
194 }
195 
SelEnd(bool select)196 bool RichEdit::SelEnd(bool select)
197 {
198 	if(IsSelection() && !select) {
199 		Move(max(cursor, anchor), false);
200 		return true;
201 	}
202 	return false;
203 }
204 
SelCell(int dx,int dy)205 void RichEdit::SelCell(int dx, int dy)
206 {
207 	Move(text.GetCellPos(tablesel, minmax(cursorp.cell.y + dy, 0, cursorp.tabsize.cy - 1),
208 	                               minmax(cursorp.cell.x + dx, 0, cursorp.tabsize.cx - 1)).pos, true);
209 }
210 
CursorKey(dword key,int count)211 bool RichEdit::CursorKey(dword key, int count)
212 {
213 	bool select = key & K_SHIFT;
214 	if(key == K_CTRL_ADD) {
215 		ZoomView(1);
216 		return true;
217 	}
218 	if(key == K_CTRL_SUBTRACT) {
219 		ZoomView(-1);
220 		return true;
221 	}
222 	if(select && tablesel)
223 		switch(key & ~K_SHIFT) {
224 		case K_LEFT:
225 			SelCell(-1, 0);
226 			break;
227 		case K_RIGHT:
228 			SelCell(1, 0);
229 			break;
230 		case K_UP:
231 			SelCell(0, -1);
232 			break;
233 		case K_DOWN:
234 			SelCell(0, 1);
235 			break;
236 		default:
237 			return false;
238 		}
239 	else {
240 		switch(key) {
241 		case K_CTRL_UP:
242 			sb.PrevLine();
243 			break;
244 		case K_CTRL_DOWN:
245 			sb.NextLine();
246 			break;
247 		default:
248 			switch(key & ~K_SHIFT) {
249 			case K_LEFT:
250 				if(!SelBeg(select))
251 					Move(cursor - 1, select);
252 				break;
253 			case K_RIGHT:
254 				if(!SelEnd(select))
255 					Move(cursor + 1, select);
256 				break;
257 			case K_UP:
258 				if(!SelBeg(select))
259 					MoveUpDown(-1, select);
260 				break;
261 			case K_DOWN:
262 				if(!SelEnd(select))
263 					MoveUpDown(1, select);
264 				break;
265 			case K_PAGEUP:
266 				if(!SelBeg(select))
267 					MovePageUpDown(-1, select);
268 				break;
269 			case K_PAGEDOWN:
270 				if(!SelEnd(select))
271 					MovePageUpDown(1, select);
272 				break;
273 			case K_END:
274 				MoveHomeEnd(1, select);
275 				break;
276 			case K_HOME:
277 				MoveHomeEnd(-1, select);
278 				break;
279 			case K_CTRL_LEFT:
280 				if(!SelBeg(select))
281 					MoveWordLeft(select);
282 				break;
283 			case K_CTRL_RIGHT:
284 				if(!SelEnd(select))
285 					MoveWordRight(select);
286 				break;
287 			case K_CTRL_HOME:
288 			case K_CTRL_PAGEUP:
289 				Move(0, select);
290 				break;
291 			case K_CTRL_END:
292 			case K_CTRL_PAGEDOWN:
293 				Move(text.GetLength(), select);
294 				break;
295 			case K_CTRL_A:
296 				Move(0, false);
297 				Move(text.GetLength(), true);
298 				break;
299 			default:
300 				return false;
301 			}
302 		}
303 	}
304 	Sync();
305 	return true;
306 }
307 
IsSelection() const308 bool RichEdit::IsSelection() const
309 {
310 	return anchor != cursor;
311 }
312 
GetSelection(int & l,int & h) const313 bool RichEdit::GetSelection(int& l, int& h) const
314 {
315 	if(IsSelection()) {
316 		l = min(anchor, cursor);
317 		h = max(anchor, cursor);
318 		return true;
319 	}
320 	l = h = cursor;
321 	return false;
322 }
323 
InSelection(int & c) const324 bool RichEdit::InSelection(int& c) const
325 {
326 	int sell, selh;
327 	if(GetSelection(sell, selh) && c >= sell && c < selh) {
328 		c = sell;
329 		return true;
330 	}
331 	return false;
332 }
333 
CancelSelection()334 void RichEdit::CancelSelection()
335 {
336 	if(IsSelection()) {
337 		tablesel = 0;
338 		anchor = cursor;
339 		begtabsel = false;
340 		found = notfoundfw = false;
341 		CloseFindReplace();
342 		Finish();
343 	}
344 }
345 
RemoveSelection(bool back)346 bool RichEdit::RemoveSelection(bool back)
347 {
348 	if(IsSelection()) {
349 		if(tablesel) {
350 			NextUndo();
351 			SaveTable(tablesel);
352 			text.ClearTable(tablesel, cells);
353 			Move(text.GetCellPos(tablesel, cells.top, cells.left).pos);
354 		}
355 		else {
356 			BegSelTabFix();
357 			int c = min(cursor, anchor);
358 			Remove(c, abs(cursor - anchor), back);
359 			found = notfoundfw = false;
360 			CloseFindReplace();
361 			Move(c);
362 		}
363 		return true;
364 	}
365 	return false;
366 }
367 
GetWordAtCursorPos(int & pos,int & count)368 void    RichEdit::GetWordAtCursorPos(int& pos, int& count)
369 {
370 	WString w;
371 	int c = cursor;
372 	pos = count = 0;
373 	if(IsLetter(text[c])) {
374 		while(c > 0 && IsLetter(text[c - 1]))
375 			c--;
376 		pos = c;
377 		while(w.GetLength() < 64 && IsLetter(text[c]))
378 			c++;
379 		count = c - pos;
380 	}
381 }
382 
GetWordAtCursor()383 WString RichEdit::GetWordAtCursor()
384 {
385 	int pos, count;
386 	GetWordAtCursorPos(pos, count);
387 	WString w;
388 	for(int i = 0; i < count; i++)
389 		w.Cat(text[i + pos]);
390 	return w;
391 }
392 
AddUserDict()393 void RichEdit::AddUserDict()
394 {
395 	if(IsSelection()) return;
396 	WString w = GetWordAtCursor();
397 	if(w.IsEmpty()) return;
398 	SpellerAdd(w, fixedlang ? fixedlang : formatinfo.language);
399 	text.ClearSpelling();
400 	Refresh();
401 }
402 
Goto()403 void RichEdit::Goto()
404 {
405 	SetFocus();
406 	if(gototable.IsCursor())
407 	{
408 		Move(gototable.Get(1), false);
409 		Move(gototable.Get(2), true);
410 	}
411 }
412 
GotoType(int type,Ctrl & l)413 void RichEdit::GotoType(int type, Ctrl& l)
414 {
415 	Vector<RichValPos> f = text.GetValPos(pagesz, type);
416 	gototable.Clear();
417 	for(int i = 0; i < f.GetCount(); i++) {
418 		const RichValPos& q = f[i];
419 		int endpos = q.pos;
420 		if(type == RichText::INDEXENTRIES) {
421 			WString ie = text.GetRichPos(endpos).format.indexentry;
422 			int l = text.GetLength();
423 			while(endpos < l) {
424 				RichPos p = text.GetRichPos(++endpos);
425 				if(p.format.indexentry != ie || p.chr == '\n')
426 					break;
427 			}
428 		}
429 		gototable.Add(q.data, q.pos, endpos);
430 	}
431 	if(gototable.GetCount())
432 		gototable.PopUp(&l);
433 }
434 
GotoLbl()435 void RichEdit::GotoLbl()
436 {
437 	GotoType(RichText::LABELS, label);
438 }
439 
GotoEntry()440 void RichEdit::GotoEntry()
441 {
442 	GotoType(RichText::INDEXENTRIES, indexentry);
443 }
444 
GotoLabel(const String & lbl)445 bool RichEdit::GotoLabel(const String& lbl)
446 {
447 	Vector<RichValPos> f = text.GetValPos(pagesz, RichText::LABELS);
448 	for(int i = 0; i < f.GetCount(); i++)
449 		if(f[i].data == WString(lbl)) {
450 			Move(f[i].pos);
451 			return true;
452 		}
453 	return false;
454 }
455 
BeginPara()456 void RichEdit::BeginPara()
457 {
458 	RichPos pos = text.GetRichPos(anchor);
459 	Move(cursor - pos.posinpara);
460 }
461 
NextPara()462 void RichEdit::NextPara()
463 {
464 	RichPos pos = text.GetRichPos(anchor);
465 	Move(cursor - pos.posinpara + pos.paralen + 1);
466 }
467 
PrevPara()468 void RichEdit::PrevPara()
469 {
470 	RichPos pos = text.GetRichPos(anchor);
471 	Move(cursor - pos.posinpara - 1);
472 	BeginPara();
473 }
474 
475 }
476