1 #include "RichEdit.h"
2
3 namespace Upp {
4
5
PasteFilter(RichText & txt,const String &)6 void RichEdit::PasteFilter(RichText& txt, const String&) { Filter(txt); }
Filter(RichText & txt)7 void RichEdit::Filter(RichText& txt) {}
8
BegSelFixRaw(RichText & text)9 void BegSelFixRaw(RichText& text)
10 {
11 RichPos p = text.GetRichPos(0, 1);
12 ASSERT(p.table == 1);
13 if(p.table != 1)
14 return;
15 RichPara::Format fmt;
16 text.InsertParaSpecial(1, true, fmt);
17 }
18
BegSelUnFixRaw(RichText & text)19 void BegSelUnFixRaw(RichText& text)
20 {
21 ASSERT(text.GetLength() > 0);
22 RichPos p = text.GetRichPos(1, 1);
23 ASSERT(p.table == 1);
24 if(p.table != 1)
25 return;
26 text.RemoveParaSpecial(1, true);
27 }
28
Apply(RichText & txt)29 void RichEdit::UndoBegSelFix::Apply(RichText& txt)
30 {
31 BegSelUnFixRaw(txt);
32 }
33
GetRedo(const RichText & txt)34 One<RichEdit::UndoRec> RichEdit::UndoBegSelFix::GetRedo(const RichText& txt)
35 {
36 return MakeOne<RichEdit::UndoBegSelUnFix>();
37 }
38
Apply(RichText & text)39 void RichEdit::UndoBegSelUnFix::Apply(RichText& text)
40 {
41 BegSelFixRaw(text);
42 }
43
GetRedo(const RichText & txt)44 One<RichEdit::UndoRec> RichEdit::UndoBegSelUnFix::GetRedo(const RichText& txt)
45 {
46 return MakeOne<RichEdit::UndoBegSelFix>();
47 }
48
BegSelTabFix(int & count)49 bool RichEdit::BegSelTabFix(int& count)
50 {
51 if(begtabsel) { // If selection starts with first table which is the first element in the text
52 int c = cursor;
53 AddUndo(MakeOne<UndoBegSelFix>());
54 BegSelFixRaw(text); // adds an empty paragraph at the start
55 Move(0);
56 Move(c + 1, true); // and changes the selection
57 count++;
58 begtabsel = false;
59 return true;
60 }
61 return false;
62 }
63
BegSelTabFixEnd(bool fix)64 void RichEdit::BegSelTabFixEnd(bool fix)
65 { // removes empty paragraph added by BegSelTabFix
66 if(fix && GetLength() > 0) {
67 int c = cursor;
68 AddUndo(MakeOne<UndoBegSelUnFix>());
69 BegSelUnFixRaw(text);
70 Move(0);
71 Move(c - 1, true);
72 begtabsel = true;
73 }
74 }
75
InvalidRange(int l,int h)76 bool RichEdit::InvalidRange(int l, int h)
77 {
78 return !InSameTxt(text.GetRichPos(min(l, h)), text.GetRichPos(max(l, h)));
79 }
80
AddUndo(One<UndoRec> && ur)81 void RichEdit::AddUndo(One<UndoRec>&& ur)
82 {
83 redo.Clear();
84 SetModify();
85 modified = true;
86 incundoserial = true;
87 while(undo.GetCount() > undosteps)
88 undo.DropHead();
89 found = false;
90 ur->cursor = cursor;
91 ur->serial = undoserial;
92 undo.AddTail(pick(ur));
93 }
94
SaveStylesUndo()95 void RichEdit::SaveStylesUndo()
96 {
97 AddUndo(MakeOne<UndoStyles>(text));
98 }
99
SaveStyleUndo(const Uuid & id)100 void RichEdit::SaveStyleUndo(const Uuid& id)
101 {
102 AddUndo(MakeOne<UndoStyle>(text, id));
103 }
104
SaveFormat(int pos,int count)105 void RichEdit::SaveFormat(int pos, int count)
106 {
107 Limit(pos, count);
108 AddUndo(MakeOne<UndoFormat>(text, pos, count));
109 }
110
SaveFormat()111 void RichEdit::SaveFormat()
112 {
113 int pos, count;
114 if(IsSelection()) {
115 if(tablesel) {
116 SaveTable(tablesel);
117 return;
118 }
119 pos = min(cursor, anchor);
120 count = abs(cursor - anchor);
121 }
122 else {
123 pos = cursor;
124 count = 0;
125 }
126 bool b = BegSelTabFix(count);
127 SaveFormat(pos, count);
128 BegSelTabFixEnd(b);
129 }
130
Limit(int & pos,int & count)131 void RichEdit::Limit(int& pos, int& count)
132 {
133 int h = pos + count;
134 pos = min(GetLength(), pos);
135 count = min(GetLength(), h) - pos;
136 }
137
ModifyFormat(int pos,const RichText::FormatInfo & fi,int count)138 void RichEdit::ModifyFormat(int pos, const RichText::FormatInfo& fi, int count)
139 {
140 if(IsReadOnly())
141 return;
142 bool b = BegSelTabFix(count);
143 Limit(pos, count);
144 SaveFormat(pos, count);
145 text.ApplyFormatInfo(pos, fi, count);
146 BegSelTabFixEnd(b);
147 }
148
Remove(int pos,int len,bool forward)149 void RichEdit::Remove(int pos, int len, bool forward)
150 {
151 if(IsReadOnly())
152 return;
153 Limit(pos, len);
154 if(InvalidRange(pos, pos + len))
155 return;
156 RichTxt::FormatInfo fi;
157 if(forward)
158 fi = text.GetFormatInfo(pos, 0);
159 AddUndo(MakeOne<UndoRemove>(text, pos, len));
160 text.Remove(pos, len);
161 if(forward) {
162 SaveFormat(pos, 0);
163 text.ReplaceStyle(pos, fi.styleid);
164 fi.paravalid &= ~RichText::STYLE;
165 text.ApplyFormatInfo(pos, fi, 0);
166 }
167 SetModify();
168 modified = true;
169 }
170
Insert(int pos,const RichText & txt,bool typing)171 void RichEdit::Insert(int pos, const RichText& txt, bool typing)
172 {
173 if(IsReadOnly())
174 return;
175 Index<int> lng;
176 for(int i = 0; i < language.GetCount(); i++)
177 lng.Add(language.GetKey(i));
178 Vector<int> lngn = txt.GetAllLanguages();
179 for(int i = 0; i < lngn.GetCount(); i++)
180 lng.FindAdd(lngn[i]);
181 SetupLanguage(lng.PickKeys());
182 int l = text.GetLength();
183 text.Insert(pos, txt);
184 l = text.GetLength() - l;
185 SetModify();
186 modified = true;
187 if(undo.GetCount()) {
188 UndoRec& u = undo.Tail();
189 if(typing) {
190 UndoInsert *ui = dynamic_cast<UndoInsert *>(&u);
191 if(ui && ui->length > 0 && ui->typing && ui->pos + ui->length == pos) {
192 ui->length += l;
193 return;
194 }
195 }
196 }
197 AddUndo(MakeOne<UndoInsert>(pos, l, typing));
198 }
199
Undo()200 void RichEdit::Undo()
201 {
202 if(IsReadOnly())
203 return;
204 if(undo.IsEmpty()) return;
205 CancelSelection();
206 int serial = undo.Tail().serial;
207 int c = cursor;
208 while(undo.GetCount()) {
209 UndoRec& u = undo.Tail();
210 if(u.serial != serial) break;
211 One<UndoRec> r = u.GetRedo(text);
212 r->serial = u.serial;
213 r->cursor = cursor;
214 redo.Add(pick(r));
215 u.Apply(text);
216 c = u.cursor;
217 undo.DropTail();
218 modified = true;
219 }
220 ReadStyles();
221 Move(c);
222 }
223
Redo()224 void RichEdit::Redo()
225 {
226 if(IsReadOnly())
227 return;
228 if(redo.IsEmpty()) return;
229 NextUndo();
230 CancelSelection();
231 int serial = redo.Top().serial;
232 int c = cursor;
233 while(redo.GetCount()) {
234 UndoRec& r = redo.Top();
235 if(r.serial != serial) break;
236 One<UndoRec> u = r.GetRedo(text);
237 u->serial = r.serial;
238 u->cursor = cursor;
239 undo.AddTail(pick(u));
240 r.Apply(text);
241 c = r.cursor;
242 redo.Drop();
243 modified = true;
244 }
245 ReadStyles();
246 Move(c);
247 }
248
249 #ifdef PLATFORM_WIN32
250 #define RTFS "Rich Text Format"
251 #else
252 #define RTFS "text/richtext"
253 #endif
254
GetSelection(int maxcount) const255 RichText RichEdit::GetSelection(int maxcount) const
256 {
257 RichText clip;
258 if(tablesel) {
259 RichTable tab = text.CopyTable(tablesel, cells);
260 clip.SetStyles(text.GetStyles());
261 clip.CatPick(pick(tab));
262 }
263 else {
264 if(begtabsel) {
265 int pos = 0;
266 RichPos p = text.GetRichPos(0, 1);
267 if(p.table) {
268 clip.SetStyles(text.GetStyles());
269 do {
270 RichTable tab = text.CopyTable(p.table);
271 clip.CatPick(pick(tab));
272 pos += p.tablen + 1;
273 p = text.GetRichPos(pos, 1);
274 }
275 while(p.table);
276 clip.CatPick(text.Copy(pos, minmax(abs(cursor - pos), 0, maxcount)));
277 }
278 }
279 else
280 clip = text.Copy(min(cursor, anchor), min(maxcount, abs(cursor - anchor)));
281 }
282 return clip;
283 }
284
Cut()285 void RichEdit::Cut()
286 {
287 if(IsReadOnly())
288 return;
289 Copy();
290 if(IsSelection())
291 RemoveSelection();
292 else
293 if(objectpos >= 0) {
294 Remove(cursor, 1);
295 Move(cursor, false);
296 }
297 }
298
PasteText(const RichText & text)299 void RichEdit::PasteText(const RichText& text)
300 {
301 SetModify();
302 modified = true;
303 RemoveSelection();
304 int n = text.GetPartCount() - 1;
305 if(!text.IsPara(0) || !text.IsPara(n)) { // inserted section must start/end with para
306 RichText pp = clone(text);
307 pp.Normalize();
308 Insert(cursor, pp, false);
309 ReadStyles();
310 Move(cursor + pp.GetLength(), false);
311 }
312 else {
313 Insert(cursor, text, false);
314 ReadStyles();
315 Move(cursor + text.GetLength(), false);
316 }
317 }
318
319 struct ToParaIterator : RichText::Iterator {
320 RichPara para;
321 bool space;
322
operator ()Upp::ToParaIterator323 virtual bool operator()(int pos, const RichPara& p) {
324 for(int i = 0; i < p.GetCount(); i++) {
325 const RichPara::Part& part = p[i];
326 if(part.IsText()) {
327 const wchar *s = part.text;
328 while(*s) {
329 while(*s && *s <= ' ') {
330 space = true;
331 s++;
332 }
333 const wchar *t = s;
334 while(*s > ' ') s++;
335 if(s > t) {
336 if(space)
337 para.Cat(" ", part.format);
338 para.Cat(WString(t, s), part.format);
339 space = false;
340 }
341 }
342 }
343 else if(!part.field.IsNull()) {
344 para.Cat(part.field, part.fieldparam, part.format);
345 space = false;
346 }
347 else if(part.object) {
348 para.Cat(part.object, part.format);
349 space = false;
350 }
351 }
352 space = true;
353 return false;
354 }
355
ToParaIteratorUpp::ToParaIterator356 ToParaIterator() { space = false; }
357 };
358
ToPara()359 void RichEdit::ToPara()
360 {
361 if(IsReadOnly())
362 return;
363 if(!IsSelection() || tablesel)
364 return;
365 NextUndo();
366 RichText txt = text.Copy(min(cursor, anchor), abs(cursor - anchor));
367 ToParaIterator it;
368 txt.Iterate(it);
369 RichText h;
370 h.SetStyles(txt.GetStyles());
371 h.Cat(it.para);
372 PasteText(h);
373 }
374
RemoveText(int count)375 void RichEdit::RemoveText(int count)
376 {
377 CancelSelection();
378 Remove(cursor, count);
379 Finish();
380 }
381
CopyText(int pos,int count) const382 RichText RichEdit::CopyText(int pos, int count) const
383 {
384 return text.Copy(pos, count);
385 }
386
InsertObject(int type)387 void RichEdit::InsertObject(int type)
388 {
389 RichObjectType& richtype = RichObject::GetType(type);
390 RichObject object = RichObject(&richtype, Value());
391 RichObject o = object;
392 o.DefaultAction(context);
393 if(o.GetSerialId() != object.GetSerialId()) {
394 RichText::FormatInfo finfo = GetFormatInfo();
395 RemoveSelection();
396 RichPara p;
397 p.Cat(o, finfo);
398 RichText clip;
399 clip.Cat(p);
400 Insert(GetCursor(), clip, false);
401 Finish();
402 }
403 }
404
ReplaceObject(const RichObject & obj)405 void RichEdit::ReplaceObject(const RichObject& obj)
406 {
407 Remove(objectpos, 1);
408 RichPara p;
409 p.Cat(obj, formatinfo);
410 RichText clip;
411 clip.Cat(p);
412 Insert(objectpos, clip, false);
413 Finish();
414 objectrect = GetObjectRect(objectpos);
415 }
416
GetObject() const417 RichObject RichEdit::GetObject() const
418 {
419 return text.GetRichPos(objectpos).object;
420 }
421
Select(int pos,int count)422 void RichEdit::Select(int pos, int count)
423 {
424 found = false;
425 Move(pos);
426 Move(pos + count, true);
427 }
428
InsertLine()429 void RichEdit::InsertLine()
430 {
431 if(IsReadOnly())
432 return;
433 RichText::FormatInfo b = formatinfo;
434 RichText h;
435 h.SetStyles(text.GetStyles());
436 RichPara p;
437 p.format = formatinfo;
438 h.Cat(p);
439 h.Cat(p);
440 bool st = cursorp.paralen == cursorp.posinpara;
441 bool f = cursorp.posinpara == 0 && formatinfo.label.GetCount();
442 Insert(cursor, h, false);
443 if(f) {
444 String lbl = formatinfo.label;
445 formatinfo.label.Clear();
446 ApplyFormat(0, RichText::LABEL);
447 formatinfo.label = lbl;
448 }
449 anchor = cursor = cursor + 1;
450 begtabsel = false;
451 formatinfo.newpage = formatinfo.newhdrftr = false;
452 if(st) {
453 Uuid next = text.GetStyle(b.styleid).next;
454 if(next != formatinfo.styleid) {
455 formatinfo.label.Clear();
456 formatinfo.styleid = next;
457 ApplyFormat(0, RichText::STYLE|RichText::NEWPAGE|RichText::LABEL|RichText::NEWHDRFTR);
458 return;
459 }
460 }
461 ApplyFormat(0, RichText::NEWPAGE|RichText::LABEL|RichText::NEWHDRFTR);
462 objectpos = -1;
463 }
464
465 }
466