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