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