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