1 #include "RichEdit.h"
2 
3 namespace Upp {
4 
ApplyFormat(dword charvalid,dword paravalid)5 void RichEdit::ApplyFormat(dword charvalid, dword paravalid)
6 {
7 	if(IsReadOnly())
8 		return;
9 	RichText::FormatInfo f = formatinfo;
10 	f.charvalid = charvalid;
11 	f.paravalid = paravalid;
12 	if(objectpos >= 0) {
13 		ModifyFormat(objectpos, f, 1);
14 		Finish();
15 	}
16 	if(IsSelection()) {
17 		if(tablesel) {
18 			NextUndo();
19 			SaveTable(tablesel);
20 			text.ApplyTableFormatInfo(tablesel, cells, f);
21 		}
22 		else {
23 			int l = min(cursor, anchor);
24 			int h = max(cursor, anchor);
25 			RichPos rp = text.GetRichPos(h);
26 			if(rp.posinpara == 0 && h > l) {
27 				RichPos rp1 = text.GetRichPos(h - 1);
28 				if(InSameTxt(rp, rp1))
29 					h--;
30 			}
31 			ModifyFormat(l, f, h - l);
32 		}
33 		Finish();
34 	}
35 	else
36 	if(cursorp.paralen == 0) {
37 		ModifyFormat(cursor, f, 0);
38 		Finish();
39 	}
40 	else
41 	if(f.paravalid) {
42 		ModifyFormat(cursor, f, 0);
43 		Finish();
44 	}
45 	else
46 		RefreshBar();
47 }
48 
ApplyFormatInfo(const RichText::FormatInfo & fi)49 void RichEdit::ApplyFormatInfo(const RichText::FormatInfo& fi)
50 {
51 	fi.ApplyTo(formatinfo);
52 	formatinfo.charvalid |= fi.charvalid;
53 	formatinfo.paravalid |= fi.paravalid;
54 	ApplyFormat(fi.charvalid, fi.paravalid);
55 }
56 
Bold()57 void RichEdit::Bold()
58 {
59 	NextUndo();
60 	formatinfo.Bold(!(formatinfo.IsBold() && (formatinfo.charvalid & RichText::BOLD)));
61 	ApplyFormat(RichText::BOLD);
62 }
63 
Italic()64 void RichEdit::Italic()
65 {
66 	NextUndo();
67 	formatinfo.Italic(!(formatinfo.IsItalic() && (formatinfo.charvalid & RichText::ITALIC)));
68 	ApplyFormat(RichText::ITALIC);
69 }
70 
Underline()71 void RichEdit::Underline()
72 {
73 	NextUndo();
74 	formatinfo.Underline(!(formatinfo.IsUnderline() && (formatinfo.charvalid & RichText::UNDERLINE)));
75 	ApplyFormat(RichText::UNDERLINE);
76 }
77 
Strikeout()78 void RichEdit::Strikeout()
79 {
80 	NextUndo();
81 	formatinfo.Strikeout(!(formatinfo.IsStrikeout() && (formatinfo.charvalid & RichText::STRIKEOUT)));
82 	ApplyFormat(RichText::STRIKEOUT);
83 }
84 
Capitals()85 void RichEdit::Capitals()
86 {
87 	NextUndo();
88 	formatinfo.capitals = !formatinfo.capitals && (formatinfo.charvalid & RichText::CAPITALS);
89 	ApplyFormat(RichText::CAPITALS);
90 }
91 
SetScript(int i)92 void RichEdit::SetScript(int i)
93 {
94 	NextUndo();
95 	formatinfo.sscript = i;
96 	ApplyFormat(RichText::SSCRIPT);
97 }
98 
SetFace()99 void RichEdit::SetFace()
100 {
101 	NextUndo();
102 	formatinfo.Face(~face);
103 	ApplyFormat(RichText::FACE);
104 	SetFocus();
105 }
106 
SetHeight()107 void RichEdit::SetHeight()
108 {
109 	NextUndo();
110 	formatinfo.Height(PtToDot(~height));
111 	ApplyFormat(RichText::HEIGHT);
112 	SetFocus();
113 }
114 
SetInk()115 void RichEdit::SetInk()
116 {
117 	NextUndo();
118 	formatinfo.ink = ~ink;
119 	ApplyFormat(RichText::INK);
120 	SetFocus();
121 }
122 
SetPaper()123 void RichEdit::SetPaper()
124 {
125 	NextUndo();
126 	formatinfo.paper = ~paper;
127 	ApplyFormat(RichText::PAPER);
128 	SetFocus();
129 }
130 
SetLanguage()131 void RichEdit::SetLanguage()
132 {
133 	NextUndo();
134 	formatinfo.language = (int)~language;
135 	ApplyFormat(RichText::LANGUAGE);
136 	SetFocus();
137 }
138 
Language()139 void RichEdit::Language()
140 {
141 	WithRichLanguageLayout<TopWindow> d;
142 	CtrlLayoutOKCancel(d, t_("Language"));
143 	d.lang <<= ~language;
144 	if(d.Run() != IDOK)
145 		return;
146 	formatinfo.language = (int)~d.lang;
147 	ApplyFormat(RichText::LANGUAGE);
148 	SetFocus();
149 	if(!language.HasKey((int)~d.lang)) {
150 		Vector<int> h;
151 		for(int i = 0; i < language.GetCount(); i++)
152 			h.Add(language.GetKey(i));
153 		h.Add(~d.lang);
154 		SetupLanguage(pick(h));
155 	}
156 }
157 
IndentMark()158 void RichEdit::IndentMark()
159 {
160 	RichRuler::Marker m;
161 	int l = formatinfo.lm;
162 	int r = cursorc.textpage.Width() - formatinfo.rm;
163 	m.pos = l + formatinfo.indent;
164 	m.minpos = max(l, 0);
165 	m.maxpos = max(r - 120, 0);
166 	m.top = true;
167 	m.image = formatinfo.paravalid & RichText::INDENT ? RichEditImg::Indent()
168 	                                                  : RichEditImg::IndentMixed();
169 	ruler.Set(2, m);
170 }
171 
ReadFormat()172 void RichEdit::ReadFormat()
173 {
174 	if(objectpos >= 0)
175 		formatinfo = text.GetFormatInfo(objectpos, 1);
176 	else
177 	if(IsSelection())
178 		if(tablesel)
179 			formatinfo = text.GetTableFormatInfo(tablesel, cells);
180 		else
181 			formatinfo = text.GetFormatInfo(min(cursor, anchor), abs(cursor - anchor));
182 	else {
183 		RichPos p = cursorp;
184 		if(cursor && p.posinpara)
185 			p = text.GetRichPos(cursor - 1);
186 		formatinfo.Set(p.format);
187 	}
188 	ShowFormat();
189 }
190 
ShowFormat()191 void RichEdit::ShowFormat()
192 {
193 	RefreshBar();
194 
195 	if(formatinfo.charvalid & RichText::FACE)
196 		face <<= formatinfo.GetFace();
197 	else
198 		face <<= Null;
199 
200 	if(formatinfo.charvalid & RichText::HEIGHT)
201 		height <<= DotToPt(formatinfo.GetHeight());
202 	else
203 		height <<= Null;
204 
205 	if(formatinfo.charvalid & RichText::LINK)
206 		hyperlink <<= formatinfo.link;
207 	else
208 		hyperlink <<= Null;
209 
210 	if(formatinfo.charvalid & RichText::INDEXENTRY)
211 		indexentry <<= formatinfo.indexentry;
212 	else
213 		indexentry <<= Null;
214 
215 	if(formatinfo.charvalid & RichText::INK)
216 		ink <<= formatinfo.ink;
217 	else
218 		ink <<= Null;
219 
220 	if(formatinfo.charvalid & RichText::PAPER)
221 		paper <<= formatinfo.paper;
222 	else
223 		paper <<= Null;
224 
225 	if(formatinfo.charvalid & RichText::LANG)
226 		language <<= (int)formatinfo.language;
227 	else
228 		language <<= Null;
229 
230 	if(IsSelection())
231 		label <<= Null;
232 	else
233 		label <<= formatinfo.label;
234 
235 	int l = formatinfo.lm;
236 	int r = cursorc.textpage.Width() - formatinfo.rm;
237 
238 	RichRuler::Marker m;
239 	m.pos = l;
240 	m.minpos = 0;
241 	m.maxpos = max(r - formatinfo.indent - 120, 0);
242 	m.image = formatinfo.paravalid & RichText::LM ? RichEditImg::Margin() : RichEditImg::MarginMixed();
243 	ruler.Set(0, m);
244 
245 	m.pos = r;
246 	m.minpos = max(l + formatinfo.indent + 120, 0);
247 	m.maxpos = cursorc.textpage.Width();
248 	m.image = formatinfo.paravalid & RichText::RM ? RichEditImg::Margin() : RichEditImg::MarginMixed();
249 	ruler.Set(1, m);
250 	IndentMark();
251 
252 	int maxpos = 0;
253 	m.minpos = 0;
254 	m.deletable = true;
255 	if(formatinfo.paravalid & RichText::TABS) {
256 		for(int i = 0; i < formatinfo.tab.GetCount(); i++) {
257 			RichPara::Tab tab = formatinfo.tab[i];
258 			m.pos = tab.pos;
259 			if(tab.pos > maxpos)
260 				maxpos = tab.pos;
261 			switch(tab.align) {
262 			case ALIGN_LEFT:
263 				m.image = RichEditImg::LeftTab();
264 				break;
265 			case ALIGN_RIGHT:
266 				m.image = RichEditImg::RightTab();
267 				break;
268 			case ALIGN_CENTER:
269 				m.image = RichEditImg::CenterTab();
270 				break;
271 			}
272 			ruler.Set(i + 3, m);
273 		}
274 		ruler.SetTabs(maxpos, formatinfo.tabsize);
275 		ruler.SetCount(formatinfo.tab.GetCount() + 3);
276 	}
277 	else {
278 		ruler.SetTabs(INT_MAX / 2, 1);
279 		ruler.SetCount(3);
280 	}
281 
282 	if(formatinfo.paravalid & RichText::STYLE)
283 		style <<= formatinfo.styleid;
284 	else
285 		style <<= Null;
286 	setstyle->Enable(!IsSelection());
287 }
288 
HighLightTab(int r)289 void RichEdit::HighLightTab(int r)
290 {
291 	RichRuler::Marker m = ruler[r + 3];
292 	RichPara::Tab tab = formatinfo.tab[r];
293 	m.image = tab.align == ALIGN_RIGHT  ? RichEditImg::RightTabTrack() :
294 	          tab.align == ALIGN_CENTER ? RichEditImg::CenterTabTrack() :
295 	                                      RichEditImg::LeftTabTrack();
296 	ruler.Set(r + 3, m);
297 }
298 
Hyperlink()299 void RichEdit::Hyperlink()
300 {
301 	String s = formatinfo.link;
302 	if(!IsSelection() && !IsNull(s) && cursorp.format.link == s && text[cursor] != '\n') {
303 		int l = cursor - 1;
304 		while(l >= 0 && text[l] != '\n' && text.GetRichPos(l).format.link == s)
305 			l--;
306 		l++;
307 		int h = cursor;
308 		while(h < text.GetLength() && text[h] != '\n' && text.GetRichPos(h).format.link == s)
309 			h++;
310 		if(l < h)
311 			Select(l, h - l);
312 	}
313 	WString linktext;
314 	WhenHyperlink(s, linktext);
315 	if(s != formatinfo.link || linktext.GetLength()) {
316 		formatinfo.link = s;
317 		hyperlink <<= s;
318 		NextUndo();
319 		ApplyFormat(RichText::LINK);
320 		if(linktext.GetLength()) {
321 			RemoveSelection();
322 			RichPara p;
323 			p.format = formatinfo;
324 			p.Cat(linktext, formatinfo);
325 			RichText txt;
326 			txt.SetStyles(text.GetStyles());
327 			txt.Cat(p);
328 			Insert(cursor, txt, true);
329 			Move(cursor + linktext.GetCount(), false);
330 		}
331 	}
332 	SetFocus();
333 }
334 
Label()335 void RichEdit::Label()
336 {
337 	if(IsSelection()) return;
338 	String s = formatinfo.label;
339 	WhenLabel(s);
340 	if(s != formatinfo.label) {
341 		formatinfo.label = s;
342 		NextUndo();
343 		ApplyFormat(0, RichText::LABEL);
344 		SetFocus();
345 	}
346 }
347 
IndexEntry()348 void RichEdit::IndexEntry()
349 {
350 	String s = formatinfo.indexentry.ToString();
351 	String s0 = s;
352 	WhenIndexEntry(s);
353 	if(s != s0) {
354 		formatinfo.indexentry = s.ToWString();
355 		ApplyFormat(RichText::INDEXENTRY);
356 		NextUndo();
357 		SetFocus();
358 	}
359 }
360 
BeginRulerTrack()361 void RichEdit::BeginRulerTrack()
362 {
363 	NextUndo();
364 	SaveFormat();
365 	int r = ruler.GetTrack();
366 	if(r < 0) return;
367 	RichRuler::Marker m = ruler[r];
368 	switch(r) {
369 	case 0:
370 	case 1:
371 		m.image = RichEditImg::MarginTrack();
372 		break;
373 	case 2:
374 		m.image = RichEditImg::IndentTrack();
375 		break;
376 	default:
377 		HighLightTab(r - 3);
378 		return;
379 	}
380 	ruler.Set(r, m);
381 }
382 
SetParaFormat(dword paravalid)383 void RichEdit::SetParaFormat(dword paravalid)
384 {
385 	RichText::FormatInfo f = formatinfo;
386 	f.charvalid = 0;
387 	f.paravalid = paravalid;
388 	if(IsSelection())
389 		if(tablesel)
390 			text.ApplyTableFormatInfo(tablesel, cells, f);
391 		else
392 			text.ApplyFormatInfo(min(cursor, anchor), f, abs(cursor - anchor));
393 	else
394 		text.ApplyFormatInfo(cursor, f, 0);
395 }
396 
RulerTrack()397 void RichEdit::RulerTrack()
398 {
399 	int r = ruler.GetTrack();
400 	if(r < 0) return;
401 	RichRuler::Marker m = ruler[r];
402 	switch(r) {
403 	case 0:
404 		formatinfo.lm = m.pos;
405 		SetParaFormat(RichText::LM);
406 		IndentMark();
407 		break;
408 	case 1:
409 		formatinfo.rm = cursorc.textpage.Width() - m.pos;
410 		SetParaFormat(RichText::RM);
411 		break;
412 	case 2:
413 		formatinfo.indent = m.pos - formatinfo.lm;
414 		SetParaFormat(RichText::INDENT);
415 		break;
416 	default:
417 		formatinfo.tab[r - 3].pos = m.pos;
418 		SetParaFormat(RichText::TABS);
419 		int maxpos = 0;
420 		for(int i = 0; i < formatinfo.tab.GetCount(); i++) {
421 			RichPara::Tab tab = formatinfo.tab[i];
422 			if(tab.pos > maxpos)
423 				maxpos = tab.pos;
424 		}
425 		ruler.SetTabs(maxpos, formatinfo.tabsize);
426 		break;
427 	}
428 	FinishNF();
429 }
430 
TabAdd(int align)431 void RichEdit::TabAdd(int align)
432 {
433 	RichPara::Tab tab;
434 	tab.pos = ruler.GetPos();
435 	tab.align = align;
436 	if(formatinfo.tab.GetCount() > 30000 || tab.pos < 0 || tab.pos >= cursorc.textpage.Width()) return;
437 	formatinfo.tab.Add(tab);
438 	SetParaFormat(RichText::TABS);
439 	Finish();
440 }
441 
AddTab()442 void RichEdit::AddTab()
443 {
444 	NextUndo();
445 	SaveFormat();
446 	TabAdd(ruler.GetNewTabAlign());
447 }
448 
TabMenu()449 void RichEdit::TabMenu()
450 {
451 	NextUndo();
452 	int r = ruler.GetTrack() - 3;
453 	if(r >= 0)
454 		HighLightTab(r);
455 	CallbackArgTarget<int> align;
456 	CallbackArgTarget<int> fill;
457 	MenuBar menu;
458 	menu.Add(t_("Left"), RichEditImg::LeftTab(), align[ALIGN_LEFT]);
459 	menu.Add(t_("Right"), RichEditImg::RightTab(), align[ALIGN_RIGHT]);
460 	menu.Add(t_("Center"), RichEditImg::CenterTab(), align[ALIGN_CENTER]);
461 	if(r >= 0) {
462 		int f = formatinfo.tab[r].fillchar;
463 		menu.Separator();
464 		menu.Add(t_("No fill"), fill[0])
465 		    .Radio(f == 0);
466 		menu.Add(t_("Fill with ...."), fill[1])
467 		    .Radio(f == 1);
468 		menu.Add(t_("Fill with ----"), fill[2])
469 		    .Radio(f == 2);
470 		menu.Add(t_("Fill with __"), fill[3])
471 		    .Radio(f == 3);
472 		menu.Separator();
473 		menu.Add(t_("Remove"), fill[-1]);
474 	}
475 	menu.Execute();
476 	if(!IsNull(align)) {
477 		SaveFormat();
478 		if(r >= 0) {
479 			formatinfo.tab[r].align = (int)align;
480 			SetParaFormat(RichText::TABS);
481 		}
482 		else
483 			TabAdd(align);
484 	}
485 	if(!IsNull(fill) && r >= 0) {
486 		SaveFormat();
487 		if(r >= 0) {
488 			if(fill == -1)
489 				formatinfo.tab[r].pos = Null;
490 			else
491 				formatinfo.tab[r].fillchar = (int)fill;
492 			SetParaFormat(RichText::TABS);
493 		}
494 	}
495 	Finish();
496 }
497 
AlignLeft()498 void RichEdit::AlignLeft()
499 {
500 	NextUndo();
501 	formatinfo.align = ALIGN_LEFT;
502 	ApplyFormat(0, RichText::ALIGN);
503 }
504 
AlignRight()505 void RichEdit::AlignRight()
506 {
507 	NextUndo();
508 	formatinfo.align = ALIGN_RIGHT;
509 	ApplyFormat(0, RichText::ALIGN);
510 }
511 
AlignCenter()512 void RichEdit::AlignCenter()
513 {
514 	NextUndo();
515 	formatinfo.align = ALIGN_CENTER;
516 	ApplyFormat(0, RichText::ALIGN);
517 }
518 
AlignJustify()519 void RichEdit::AlignJustify()
520 {
521 	NextUndo();
522 	formatinfo.align = ALIGN_JUSTIFY;
523 	ApplyFormat(0, RichText::ALIGN);
524 }
525 
SetBullet(int bullet)526 void  RichEdit::SetBullet(int bullet)
527 {
528 	NextUndo();
529 	if((formatinfo.paravalid & RichText::BULLET) && formatinfo.bullet == bullet) {
530 		formatinfo.bullet = RichPara::BULLET_NONE;
531 		formatinfo.indent = formatinfo.paravalid & RichText::STYLE ?
532 		                       text.GetStyle(formatinfo.styleid).format.indent : 0;
533 	}
534 	else {
535 		formatinfo.bullet = bullet;
536 		formatinfo.indent = bullet_indent;
537 	}
538 	ApplyFormat(0, RichText::INDENT|RichText::BULLET);
539 }
540 
Style()541 void RichEdit::Style()
542 {
543 	NextUndo();
544 	SaveFormat(cursor, 0);
545 	formatinfo.Set(text.GetStyle((Uuid)~style).format);
546 	ApplyFormat(0, RichText::STYLE);
547 	SetFocus();
548 	Finish();
549 }
550 
AdjustObjectSize()551 void RichEdit::AdjustObjectSize()
552 {
553 	NextUndo();
554 	RichObject obj = cursorp.object;
555 	if(!obj) return;
556 	WithObjectSizeLayout<TopWindow> d;
557 	CtrlLayoutOKCancel(d, t_("Object position"));
558 	Size sz = obj.GetSize();
559 	Size psz = GetPhysicalSize(obj);
560 	if(psz.cx == 0) psz.cx = 2000;
561 	if(psz.cy == 0) psz.cy = 2000;
562 	d.width.Set(unit, sz.cx);
563 	d.height.Set(unit, sz.cy);
564 	d.widthp.SetInc(5).Pattern("%.1f");
565 	d.widthp <<= 100.0 * sz.cx / psz.cx;
566 	d.heightp.SetInc(5).Pattern("%.1f");
567 	d.heightp <<= 100.0 * sz.cy / psz.cy;
568 	d.keepratio = obj.IsKeepRatio();
569 	d.width <<= d.height <<= d.widthp <<= d.heightp <<= d.Breaker(IDYES);
570 	d.ydelta.WithSgn().Set(unit, obj.GetYDelta());
571 	d.keepratio <<= d.Breaker(IDNO);
572 	for(;;) {
573 		switch(d.Run()) {
574 		case IDCANCEL:
575 			return;
576 		case IDYES:
577 			if(d.width.HasFocus() && !IsNull(d.width)) {
578 				d.widthp <<= 100 * (double)~d.width / psz.cx;
579 				if(d.keepratio) {
580 					d.height <<= psz.cy * (double)~d.width / psz.cx;
581 					d.heightp <<= ~d.widthp;
582 				}
583 			}
584 			if(d.height.HasFocus() && !IsNull(d.height)) {
585 				d.heightp <<= 100 * (double)~d.height / psz.cy;
586 				if(d.keepratio) {
587 					d.width <<= psz.cx * (double)~d.height / psz.cy;
588 					d.widthp <<= ~d.heightp;
589 				}
590 			}
591 			if(d.widthp.HasFocus() && !IsNull(d.widthp)) {
592 				d.width <<= psz.cx * (double)~d.widthp / 100;
593 				if(d.keepratio) {
594 					d.height <<= psz.cy * (double)~d.width / psz.cx;
595 					d.heightp <<= ~d.widthp;
596 				}
597 			}
598 			if(d.heightp.HasFocus() && !IsNull(d.heightp)) {
599 				d.height <<= psz.cy * (double)~d.heightp / 100;
600 				if(d.keepratio) {
601 					d.width <<= psz.cx * (double)~d.height / psz.cy;
602 					d.widthp <<= ~d.heightp;
603 				}
604 			}
605 			break;
606 		case IDNO:
607 			if(d.keepratio && !IsNull(d.width)) {
608 				d.widthp <<= 100 * (double)~d.width / psz.cx;
609 				if(d.keepratio) {
610 					d.height  <<= psz.cy * (double)~d.width / psz.cx;
611 					d.heightp <<= ~d.widthp;
612 				}
613 			}
614 			break;
615 		case IDOK:
616 			if(!IsNull(d.width) && (int)~d.width > 0)
617 				sz.cx = ~d.width;
618 			if(!IsNull(d.height) && (int)~d.height > 0)
619 				sz.cy = ~d.height;
620 			obj.SetSize(sz);
621 			if(!IsNull(d.ydelta))
622 				obj.SetYDelta(~d.ydelta);
623 			obj.KeepRatio(d.keepratio);
624 			ReplaceObject(obj);
625 			return;
626 		}
627 	}
628 }
629 
630 }
631