From 893a7924dac4102cdca340e6d8f189688c7a6842 Mon Sep 17 00:00:00 2001 From: cxl Date: Mon, 13 Oct 2008 20:06:12 +0000 Subject: [PATCH] Updating uppdev git-svn-id: svn://ultimatepp.org/upp/trunk@527 f0d560ea-af0d-0410-9eb7-867de7ffcac7 --- uppdev/AddressBookXMLDOM/AddressBook.cpp | 1 + uppdev/AlphaImage/main.cpp | 97 +- uppdev/Any/Any.cpp | 4 + uppdev/CoreTopics/src.tpp/TestTest$en-us.tpp | 590 +++++++++ uppdev/CoreTopics/src.tpp/TestTest$en-us.tppi | 15 + uppdev/CoreTopics/src.tpp/all.i | 4 + uppdev/Parser/Parser.upp | 5 +- uppdev/Parser/init | 4 +- uppdev/Parser/test.cpp | 154 +++ uppdev/Parser/test2.cpp | 4 + uppdev/Parser2/Parser2.upp | 1 + uppdev/Parser2/test.cpp | 69 +- uppdev/RichTextP/Copying | 21 + uppdev/RichTextP/EncodeHTML.cpp | 361 ++++++ uppdev/RichTextP/EncodeQtf.cpp | 552 ++++++++ uppdev/RichTextP/EncodeRTF.cpp | 658 ++++++++++ uppdev/RichTextP/Format.cpp | 212 ++++ uppdev/RichTextP/Object.cpp | 401 ++++++ uppdev/RichTextP/Para.h | 294 +++++ uppdev/RichTextP/ParaData.cpp | 772 ++++++++++++ uppdev/RichTextP/ParaPaint.cpp | 587 +++++++++ uppdev/RichTextP/ParaType.cpp | 376 ++++++ uppdev/RichTextP/ParseQtf.cpp | 967 ++++++++++++++ uppdev/RichTextP/ParseRTF.cpp | 1106 +++++++++++++++++ uppdev/RichTextP/RichImage.icpp | 237 ++++ uppdev/RichTextP/RichText.h | 462 +++++++ uppdev/RichTextP/RichText.iml | 9 + uppdev/RichTextP/RichText.usc | 0 uppdev/RichTextP/RichTextP.upp | 39 + uppdev/RichTextP/Table.h | 220 ++++ uppdev/RichTextP/TableCell.cpp | 194 +++ uppdev/RichTextP/TableData.cpp | 580 +++++++++ uppdev/RichTextP/TableLayout.cpp | 176 +++ uppdev/RichTextP/TablePaint.cpp | 372 ++++++ uppdev/RichTextP/Text.h | 135 ++ uppdev/RichTextP/TextData.cpp | 332 +++++ uppdev/RichTextP/TextPaint.cpp | 183 +++ uppdev/RichTextP/TextStyle.cpp | 153 +++ uppdev/RichTextP/TextTable.cpp | 225 ++++ uppdev/RichTextP/Txt.h | 247 ++++ uppdev/RichTextP/TxtData.cpp | 472 +++++++ uppdev/RichTextP/TxtOp.cpp | 358 ++++++ uppdev/RichTextP/TxtPaint.cpp | 377 ++++++ uppdev/RichTextP/Util.cpp | 123 ++ uppdev/RichTextP/init | 8 + uppdev/RichTextP/srcdoc.tpp/QTF$en-us.tpp | 705 +++++++++++ uppdev/SyntaxHighlight/test.h | 45 +- uppdev/uncompress/uncompress.cpp | 8 + uppdev/uncompress/uncompress.upp | 9 + 49 files changed, 12859 insertions(+), 65 deletions(-) create mode 100644 uppdev/CoreTopics/src.tpp/TestTest$en-us.tpp create mode 100644 uppdev/CoreTopics/src.tpp/TestTest$en-us.tppi create mode 100644 uppdev/Parser/test2.cpp create mode 100644 uppdev/RichTextP/Copying create mode 100644 uppdev/RichTextP/EncodeHTML.cpp create mode 100644 uppdev/RichTextP/EncodeQtf.cpp create mode 100644 uppdev/RichTextP/EncodeRTF.cpp create mode 100644 uppdev/RichTextP/Format.cpp create mode 100644 uppdev/RichTextP/Object.cpp create mode 100644 uppdev/RichTextP/Para.h create mode 100644 uppdev/RichTextP/ParaData.cpp create mode 100644 uppdev/RichTextP/ParaPaint.cpp create mode 100644 uppdev/RichTextP/ParaType.cpp create mode 100644 uppdev/RichTextP/ParseQtf.cpp create mode 100644 uppdev/RichTextP/ParseRTF.cpp create mode 100644 uppdev/RichTextP/RichImage.icpp create mode 100644 uppdev/RichTextP/RichText.h create mode 100644 uppdev/RichTextP/RichText.iml create mode 100644 uppdev/RichTextP/RichText.usc create mode 100644 uppdev/RichTextP/RichTextP.upp create mode 100644 uppdev/RichTextP/Table.h create mode 100644 uppdev/RichTextP/TableCell.cpp create mode 100644 uppdev/RichTextP/TableData.cpp create mode 100644 uppdev/RichTextP/TableLayout.cpp create mode 100644 uppdev/RichTextP/TablePaint.cpp create mode 100644 uppdev/RichTextP/Text.h create mode 100644 uppdev/RichTextP/TextData.cpp create mode 100644 uppdev/RichTextP/TextPaint.cpp create mode 100644 uppdev/RichTextP/TextStyle.cpp create mode 100644 uppdev/RichTextP/TextTable.cpp create mode 100644 uppdev/RichTextP/Txt.h create mode 100644 uppdev/RichTextP/TxtData.cpp create mode 100644 uppdev/RichTextP/TxtOp.cpp create mode 100644 uppdev/RichTextP/TxtPaint.cpp create mode 100644 uppdev/RichTextP/Util.cpp create mode 100644 uppdev/RichTextP/init create mode 100644 uppdev/RichTextP/srcdoc.tpp/QTF$en-us.tpp create mode 100644 uppdev/uncompress/uncompress.cpp create mode 100644 uppdev/uncompress/uncompress.upp diff --git a/uppdev/AddressBookXMLDOM/AddressBook.cpp b/uppdev/AddressBookXMLDOM/AddressBook.cpp index 865e973d4..4859ec628 100644 --- a/uppdev/AddressBookXMLDOM/AddressBook.cpp +++ b/uppdev/AddressBookXMLDOM/AddressBook.cpp @@ -56,6 +56,7 @@ AddressBook::AddressBook() SetupSearch(); fs.AllFilesType(); menu.Set(THISBACK(MainMenu)); + } void AddressBook::FileMenu(Bar& bar) diff --git a/uppdev/AlphaImage/main.cpp b/uppdev/AlphaImage/main.cpp index ef0b37c62..41854820a 100644 --- a/uppdev/AlphaImage/main.cpp +++ b/uppdev/AlphaImage/main.cpp @@ -1,48 +1,49 @@ -#include - -using namespace Upp; - -struct App : public TopWindow { - virtual void Paint(Draw& w) { - ImageDraw iw(400, 400); - iw.Alpha().DrawRect(0, 0, 400, 400, Black()); - iw.Alpha().DrawEllipse(0, 0, 200, 200, GrayColor(100)); - iw.DrawRect(0, 0, 400, 400, Black); - iw.DrawEllipse(0, 0, 200, 200, LtCyan); - - Image a = Rescale(iw, 200, 200); - - w.DrawRect(GetSize(), White); - w.DrawImage(30, 30, a, Rect(10, 10, 50, 50)); - w.DrawImage(500, 30, a, Blue); - - - ImageBuffer ib(50, 50); - for(int y = 0; y < 50; y++) { - RGBA *l = ib[y]; - for(int x = 0; x < 50; x++) { - if(y == 0 || y == 49 || x == 0 || x == 49) - *l++ = Black(); - else { - l->a = 2 * (x + y); - l->r = 4 * x; - l->g = 4 * y; - l->b = 200; - l++; - } - } - } - Premultiply(ib); - Image b = ib; - - Over(a, Point(70, 70), b, b.GetSize()); - - w.DrawImage(20, 500, a); - } -}; - -GUI_APP_MAIN -{ - App().Run(); -} - +#include + +using namespace Upp; + +struct App : public TopWindow { + virtual void Paint(Draw& w) { + ImageDraw iw(400, 400); + iw.Alpha().DrawRect(0, 0, 400, 400, Black()); + iw.Alpha().DrawEllipse(0, 0, 200, 200, GrayColor(100)); + iw.DrawRect(0, 0, 400, 400, Black); + iw.DrawEllipse(0, 0, 200, 200, LtCyan); + + Image a = Rescale(iw, 200, 200); + + w.DrawRect(GetSize(), White); + w.DrawImage(30, 30, a, Rect(10, 10, 50, 50)); + w.DrawImage(500, 30, a, Blue); + + + ImageBuffer ib(50, 50); + for(int y = 0; y < 50; y++) { + RGBA *l = ib[y]; + for(int x = 0; x < 50; x++) { + if(y == 0 || y == 49 || x == 0 || x == 49) + *l++ = Black(); + else { + l->a = 2 * (x + y); + l->r = 4 * x; + l->g = 4 * y; + l->b = 200; + l++; + } + } + } + Premultiply(ib); + Image b = ib; + + Over(a, Point(70, 70), b, b.GetSize()); + + w.DrawImage(20, 500, a); + } + +}; + +GUI_APP_MAIN +{ + App().Run(); +} + diff --git a/uppdev/Any/Any.cpp b/uppdev/Any/Any.cpp index 5c8c06b22..2d18e7052 100644 --- a/uppdev/Any/Any.cpp +++ b/uppdev/Any/Any.cpp @@ -9,7 +9,11 @@ thread__ int *alfa[10] = { &x, &x }; CONSOLE_APP_MAIN { Any alpha; + alpha.Create(); + + One x; + x. if(alpha.Is()) RLOG("int"); diff --git a/uppdev/CoreTopics/src.tpp/TestTest$en-us.tpp b/uppdev/CoreTopics/src.tpp/TestTest$en-us.tpp new file mode 100644 index 000000000..df1c0a05a --- /dev/null +++ b/uppdev/CoreTopics/src.tpp/TestTest$en-us.tpp @@ -0,0 +1,590 @@ +topic ""; +[ $$0,0#00000000000000000000000000000000:Default] +[H6;0 $$1,0#05600065144404261032431302351956:begin] +[i448;a25;kKO9;2 $$2,0#37138531426314131252341829483370:codeitem] +[l288;2 $$3,0#27521748481378242620020725143825:desc] +[0 $$4,0#96390100711032703541132217272105:end] +[{_}%EN-US +[s1;%- &] +[s2;:pteback`(OBJECT`*`,void`(`*`)`(P1 p1`,P2 p2`)`):%- Callback_[* Proxy](Callback[@(0.0.255) `& +]_[*@3 cb])&] +[s3; [%-*@3 cb].&] +[s4; &] +[s1; &] +[s2;:pteback`(OBJECT`*`,void`(`*`)`(P1 p1`,P2 p2`)`):%- Callback_[* callback](Callback_ +[*@3 cb1], Callback_[*@3 cb2])&] +[s3; [%-*@3 cb1] [%-*@3 cb2].&] +[s4; &] +[s1; &] +[s2;:pteback`(OBJECT`*`,void`(`*`)`(P1 p1`,P2 p2`)`):%- Callback[@(0.0.255) `&]_[* operat +or<<](Callback[@(0.0.255) `&]_[*@3 a], Callback_[*@3 b])&] +[s3; [%-*@3 a] [%-*@3 b].&] +[s4; &] +[s1; &] +[s2;:pteback`(OBJECT`*`,void`(`*`)`(P1 p1`,P2 p2`)`):%- [@(0.0.255) template]_<[@(0.0.255) c +lass]_[*@4 OBJECT], [@(0.0.255) class]_[*@4 METHOD], [@(0.0.255) class]_[*@4 P1][@(0.0.255) > +]_Callback1[@(0.0.255) <][*@4 P1][@(0.0.255) >]_[* pteback]([*@4 OBJECT]_`*[*@3 object], +[@(0.0.255) void]_(METHOD`::[@(0.0.255) `*][*@3 method])([*@4 P1]_p1))&] +[s3; [%-*@3 object] [%-*@3 method].&] +[s4; &] +[s1; &] +[s2;:pteback`(OBJECT`*`,void`(`*`)`(P1 p1`,P2 p2`)`):%- [@(0.0.255) template]_<[@(0.0.255) c +lass]_[*@4 OBJECT], [@(0.0.255) class]_[*@4 METHOD], [@(0.0.255) class]_[*@4 P1][@(0.0.255) > +]_Callback1[@(0.0.255) <][*@4 P1][@(0.0.255) >]_[* callback]([*@4 OBJECT]_`*[*@3 object], +[@(0.0.255) void]_(METHOD`::[@(0.0.255) `*][*@3 method])([*@4 P1]_p1))&] +[s3; [%-*@3 object] [%-*@3 method].&] +[s4; &] +[s1; &] +[s2;:pteback`(OBJECT`*`,void`(`*`)`(P1 p1`,P2 p2`)`):%- [@(0.0.255) template]_<[@(0.0.255) c +lass]_[*@4 OBJECT], [@(0.0.255) class]_[*@4 METHOD], [@(0.0.255) class]_[*@4 P1][@(0.0.255) > +]_Callback1[@(0.0.255) <][*@4 P1][@(0.0.255) >]_[* callback]([@(0.0.255) const]_[*@4 OBJECT]_ +`*[*@3 object], [@(0.0.255) void]_(METHOD`::[@(0.0.255) `*][*@3 method])([*@4 P1]_p1)_[@(0.0.255) c +onst])&] +[s3; [%-*@3 object] [%-*@3 method].&] +[s4; &] +[s1; &] +[s2;:pteback`(OBJECT`*`,void`(`*`)`(P1 p1`,P2 p2`)`):%- [@(0.0.255) template]_<[@(0.0.255) c +lass]_[*@4 P1][@(0.0.255) >]_Callback1[@(0.0.255) <][*@4 P1][@(0.0.255) >]_[* callback]([@(0.0.255) v +oid]_(`*[*@3 fn])([*@4 P1]_p1))&] +[s3; [%-*@3 fn].&] +[s4; &] +[s1; &] +[s2;:pteback`(OBJECT`*`,void`(`*`)`(P1 p1`,P2 p2`)`):%- [@(0.0.255) template]_<[@(0.0.255) c +lass]_[*@4 P1][@(0.0.255) >]_Callback1[@(0.0.255) <][*@4 P1][@(0.0.255) >]_[* Proxy](Callback +1[@(0.0.255) <][*@4 P1][@(0.0.255) >`&]_[*@3 cb])&] +[s3; [%-*@3 cb].&] +[s4; &] +[s1; &] +[s2;:pteback`(OBJECT`*`,void`(`*`)`(P1 p1`,P2 p2`)`):%- [@(0.0.255) template]_<[@(0.0.255) c +lass]_[*@4 P1][@(0.0.255) >]_Callback1[@(0.0.255) <][*@4 P1][@(0.0.255) >]_[* callback](Callb +ack1[@(0.0.255) <][*@4 P1][@(0.0.255) >]_[*@3 cb1], Callback1[@(0.0.255) <][*@4 P1][@(0.0.255) > +]_[*@3 cb2])&] +[s3; [%-*@3 cb1] [%-*@3 cb2].&] +[s4; &] +[s1; &] +[s2;:pteback`(OBJECT`*`,void`(`*`)`(P1 p1`,P2 p2`)`):%- [@(0.0.255) template]_<[@(0.0.255) c +lass]_[*@4 P1][@(0.0.255) >]_Callback1[@(0.0.255) <][*@4 P1][@(0.0.255) >`&]_[* operator<<](C +allback1[@(0.0.255) <][*@4 P1][@(0.0.255) >`&]_[*@3 a], Callback1[@(0.0.255) <][*@4 P1][@(0.0.255) > +]_[*@3 b])&] +[s3; [%-*@3 a] [%-*@3 b].&] +[s4; &] +[s1; &] +[s2;:pteback`(OBJECT`*`,void`(`*`)`(P1 p1`,P2 p2`)`):%- [@(0.0.255) template]_<[@(0.0.255) c +lass]_[*@4 OBJECT], [@(0.0.255) class]_[*@4 METHOD], [@(0.0.255) class]_[*@4 P1], +[@(0.0.255) class]_[*@4 P2][@(0.0.255) >]_Callback2[@(0.0.255) <][*@4 P1], +[*@4 P2][@(0.0.255) >]_[* pteback]([*@4 OBJECT]_`*[*@3 object], [@(0.0.255) void]_(METHOD`::[@(0.0.255) `* +][*@3 method])([*@4 P1]_p1, [*@4 P2]_p2))&] +[s3; [%-*@3 object] [%-*@3 method].&] +[s4; &] +[s0;%- &] +[ {{10000 [s0;%- AIndex] +:: [s0;%- AIndex`::ConstIterator] +:: [s0;%- AIndex`::ValueType] +:: [s0;%- AMap] +:: [s0;%- AMap`::ConstIterator] +:: [s0;%- AMap`::Iterator] +:: [s0;%- AMap`::KeyConstIterator] +:: [s0;%- AMap`::KeyType] +:: [s0;%- AMap`::ValueType] +:: [s0;%- AString] +:: [s0;%- AString`::String] +:: [s0;%- AString`::bchar] +:: [s0;%- AString`::buffer] +:: [s0;%- AString`::tchar] +:: [s0;%- AbortExc] +:: [s0;%- AddOps] +:: [s0;%- Any] +:: [s0;%- Any`::BaseData] +:: [s0;%- Any`::Data] +:: [s0;%- Array] +:: [s0;%- Array`::ConstIterator] +:: [s0;%- Array`::ConstIterator`::Array`::ConstIterator] +:: [s0;%- Array`::ConstIterator`::NP] +:: [s0;%- Array`::Iterator] +:: [s0;%- Array`::Iterator`::B] +:: [s0;%- Array`::Iterator`::NP] +:: [s0;%- Array`::ValueType] +:: [s0;%- ArrayIndex] +:: [s0;%- ArrayIndex`::B] +:: [s0;%- ArrayIndex`::ConstIterator] +:: [s0;%- ArrayMap] +:: [s0;%- ArrayMap`::B] +:: [s0;%- ArrayMap`::ConstIterator] +:: [s0;%- ArrayMap`::Iterator] +:: [s0;%- AssignValueTypeNo] +:: [s0;%- Atomic] +:: [s0;%- BiArray] +:: [s0;%- BiArray`::ConstIterator] +:: [s0;%- BiArray`::Iterator] +:: [s0;%- BiArray`::ValueType] +:: [s0;%- BiVector] +:: [s0;%- BiVector`::ConstIterator] +:: [s0;%- BiVector`::Iterator] +:: [s0;%- BiVector`::ValueType] +:: [s0;%- BitAndPtr] +:: [s0;%- Bits] +:: [s0;%- BlockStream] +:: [s0;%- Buffer] +:: [s0;%- CParser] +:: [s0;%- CParser`::Error] +:: [s0;%- CParser`::Pos] +:: [s0;%- Callback] +:: [s0;%- Callback1] +:: [s0;%- Callback1`::CLASSNAME] +:: [s0;%- Callback1Action] +:: [s0;%- Callback1FnAction] +:: [s0;%- Callback1ForkAction] +:: [s0;%- Callback1MethodAction] +:: [s0;%- Callback1MethodActionPte] +:: [s0;%- Callback2] +:: [s0;%- Callback2`::CLASSNAME] +:: [s0;%- Callback2Action] +:: [s0;%- Callback2FnAction] +:: [s0;%- Callback2ForkAction] +:: [s0;%- Callback2MethodAction] +:: [s0;%- Callback2MethodActionPte] +:: [s0;%- Callback3] +:: [s0;%- Callback3`::CLASSNAME] +:: [s0;%- Callback3Action] +:: [s0;%- Callback3FnAction] +:: [s0;%- Callback3ForkAction] +:: [s0;%- Callback3MethodAction] +:: [s0;%- Callback3MethodActionPte] +:: [s0;%- Callback4] +:: [s0;%- Callback4`::CLASSNAME] +:: [s0;%- Callback4Action] +:: [s0;%- Callback4FnAction] +:: [s0;%- Callback4ForkAction] +:: [s0;%- Callback4MethodAction] +:: [s0;%- Callback4MethodActionPte] +:: [s0;%- Callback`::CLASSNAME] +:: [s0;%- CallbackAction] +:: [s0;%- CallbackActionCallArg] +:: [s0;%- CallbackActionCallArg1] +:: [s0;%- CallbackActionCallArg2] +:: [s0;%- CallbackActionCallArg3] +:: [s0;%- CallbackActionCallArg4] +:: [s0;%- CallbackArgTarget] +:: [s0;%- CallbackArgTarget`::CLASSNAME] +:: [s0;%- CallbackFnAction] +:: [s0;%- CallbackForkAction] +:: [s0;%- CallbackMethodAction] +:: [s0;%- CallbackMethodActionArg] +:: [s0;%- CallbackMethodActionArg1] +:: [s0;%- CallbackMethodActionArg1Pte] +:: [s0;%- CallbackMethodActionArg2] +:: [s0;%- CallbackMethodActionArg2Pte] +:: [s0;%- CallbackMethodActionArg3] +:: [s0;%- CallbackMethodActionArg3Pte] +:: [s0;%- CallbackMethodActionArg4] +:: [s0;%- CallbackMethodActionArg4Pte] +:: [s0;%- CallbackMethodActionArgPte] +:: [s0;%- CallbackMethodActionPte] +:: [s0;%- Callexit] +:: [s0;%- Callinit] +:: [s0;%- CerrStream] +:: [s0;%- CharFilter] +:: [s0;%- CharFilterTextTest] +:: [s0;%- CharS] +:: [s0;%- CharSetData] +:: [s0;%- CoWork] +:: [s0;%- CoWork`::Lock] +:: [s0;%- CoWork`::MJob] +:: [s0;%- CoWork`::Pool] +:: [s0;%- Color] +:: [s0;%- ColorF] +:: [s0;%- CombineCompare] +:: [s0;%- CombineHash] +:: [s0;%- Comparable] +:: [s0;%- CompareRelOps] +:: [s0;%- CompareStream] +:: [s0;%- ConstIIterator] +:: [s0;%- ConstIIterator`::NP] +:: [s0;%- ConstIIterator`::T] +:: [s0;%- Convert] +:: [s0;%- ConvertDate] +:: [s0;%- ConvertDouble] +:: [s0;%- ConvertInt] +:: [s0;%- ConvertInt64] +:: [s0;%- ConvertString] +:: [s0;%- ConvertTime] +:: [s0;%- CoutStream] +:: [s0;%- Crc32] +:: [s0;%- CriticalSection] +:: [s0;%- DLLHANDLE] +:: [s0;%- DLLTYPE] +:: [s0;%- Data`_S`_] +:: [s0;%- Date] +:: [s0;%- DbgBlkHeader] +:: [s0;%- DebugLogBlock] +:: [s0;%- DeepCopyOption] +:: [s0;%- DefaultWildcardCompare] +:: [s0;%- EmptyClass] +:: [s0;%- Event] +:: [s0;%- Exc] +:: [s0;%- FieldRelationCls] +:: [s0;%- FileAppend] +:: [s0;%- FileIn] +:: [s0;%- FileMapping] +:: [s0;%- FileMapping`::stat] +:: [s0;%- FileOut] +:: [s0;%- FileStream] +:: [s0;%- FileSystemInfo] +:: [s0;%- FileSystemInfo`::FileInfo] +:: [s0;%- FileTime] +:: [s0;%- FindFile] +:: [s0;%- FindFile`::stat] +:: [s0;%- FnValueOrder] +:: [s0;%- FormId] +:: [s0;%- FormatConvert] +:: [s0;%- Formatter] +:: [s0;%- Formatting] +:: [s0;%- Gate] +:: [s0;%- Gate1] +:: [s0;%- Gate1`::CLASSNAME] +:: [s0;%- Gate1Action] +:: [s0;%- Gate1FnAction] +:: [s0;%- Gate1ForkAction] +:: [s0;%- Gate1MethodAction] +:: [s0;%- Gate1MethodActionPte] +:: [s0;%- Gate2] +:: [s0;%- Gate2`::CLASSNAME] +:: [s0;%- Gate2Action] +:: [s0;%- Gate2FnAction] +:: [s0;%- Gate2ForkAction] +:: [s0;%- Gate2MethodAction] +:: [s0;%- Gate2MethodActionPte] +:: [s0;%- Gate3] +:: [s0;%- Gate3`::CLASSNAME] +:: [s0;%- Gate3Action] +:: [s0;%- Gate3FnAction] +:: [s0;%- Gate3ForkAction] +:: [s0;%- Gate3MethodAction] +:: [s0;%- Gate3MethodActionPte] +:: [s0;%- Gate4] +:: [s0;%- Gate4`::CLASSNAME] +:: [s0;%- Gate4Action] +:: [s0;%- Gate4FnAction] +:: [s0;%- Gate4ForkAction] +:: [s0;%- Gate4MethodAction] +:: [s0;%- Gate4MethodActionPte] +:: [s0;%- Gate`::CLASSNAME] +:: [s0;%- GateAction] +:: [s0;%- GateFnAction] +:: [s0;%- GateForkAction] +:: [s0;%- GateMethodAction] +:: [s0;%- GateMethodActionPte] +:: [s0;%- H`_l`_] +:: [s0;%- HashBase] +:: [s0;%- HashBase`::Link] +:: [s0;%- Heap] +:: [s0;%- Heap`::BigHdr] +:: [s0;%- Heap`::DLink] +:: [s0;%- Heap`::FreeLink] +:: [s0;%- Heap`::Header] +:: [s0;%- Heap`::Page] +:: [s0;%- HitCountInspector] +:: [s0;%- IIterator] +:: [s0;%- IIterator`::NP] +:: [s0;%- IIterator`::T] +:: [s0;%- Id] +:: [s0;%- IdConst] +:: [s0;%- IdList] +:: [s0;%- Index] +:: [s0;%- Index`::B] +:: [s0;%- Index`::ConstIterator] +:: [s0;%- IndexSort2Iterator] +:: [s0;%- IndexSort2Iterator`::Iter] +:: [s0;%- IndexSort3Iterator] +:: [s0;%- IndexSort3Iterator`::Iter] +:: [s0;%- IndexSortIterator] +:: [s0;%- IndexSortIterator`::Iter] +:: [s0;%- JoinConvert] +:: [s0;%- JoinConvert`::Item] +:: [s0;%- LCIDMap] +:: [s0;%- LCTYPE] +:: [s0;%- LRUCache] +:: [s0;%- LRUCache`::Item] +:: [s0;%- LRUCache`::Maker] +:: [s0;%- LangConvertClass] +:: [s0;%- LangModuleRecord] +:: [s0;%- LangTextRecord] +:: [s0;%- LanguageInfo] +:: [s0;%- LanguageInfo`::WildcardCompare] +:: [s0;%- LanguageInfoCS] +:: [s0;%- LanguageInfoEN] +:: [s0;%- LanguageInfoES] +:: [s0;%- LanguageInfoFR] +:: [s0;%- LanguageInfoGE] +:: [s0;%- LanguageInfoMap] +:: [s0;%- Link] +:: [s0;%- LinkOwner] +:: [s0;%- LngEntry`_`_] +:: [s0;%- LngModule] +:: [s0;%- LngRec] +:: [s0;%- LoadingError] +:: [s0;%- LogStream] +:: [s0;%- MapConvert] +:: [s0;%- MemReadStream] +:: [s0;%- MemStream] +:: [s0;%- MemoryIgnoreLeaksBlock] +:: [s0;%- MemoryProfile] +:: [s0;%- MethodRelationCls] +:: [s0;%- Mitor] +:: [s0;%- Moveable] +:: [s0;%- MoveableAndDeepCopyOption] +:: [s0;%- MtInspector] +:: [s0;%- Mutex] +:: [s0;%- Mutex`::Lock] +:: [s0;%- NetNode] +:: [s0;%- NilStreamClass] +:: [s0;%- NoConvertClass] +:: [s0;%- NoCopy] +:: [s0;%- Nuller] +:: [s0;%- One] +:: [s0;%- ParamHelper`_`_] +:: [s0;%- PeFile] +:: [s0;%- Point] +:: [s0;%- Point16] +:: [s0;%- Point64] +:: [s0;%- Point`_] +:: [s0;%- Point`_`::Sz] +:: [s0;%- Pointf] +:: [s0;%- PolyDeepCopyNew] +:: [s0;%- PostfixOps] +:: [s0;%- Pte] +:: [s0;%- PteBase] +:: [s0;%- PteBase`::Prec] +:: [s0;%- Ptr] +:: [s0;%- PtrBase] +:: [s0;%- PtrHash] +:: [s0;%- RGBA] +:: [s0;%- RWMutex] +:: [s0;%- RWMutex`::ReadLock] +:: [s0;%- RWMutex`::WriteLock] +:: [s0;%- RawPickValue] +:: [s0;%- RawPickValue`::PickRep] +:: [s0;%- RawPickValueRep] +:: [s0;%- RawRef] +:: [s0;%- RawValue] +:: [s0;%- RawValue`::Rep] +:: [s0;%- RawValueCreate] +:: [s0;%- RawValueCreate`::Rep] +:: [s0;%- RawValueCreateRep] +:: [s0;%- RawValueRep] +:: [s0;%- Rect] +:: [s0;%- Rect16] +:: [s0;%- Rect64] +:: [s0;%- Rect`_] +:: [s0;%- Rect`_`::Pt] +:: [s0;%- Rect`_`::Sz] +:: [s0;%- Rectf] +:: [s0;%- Ref] +:: [s0;%- Ref`::ValueRef] +:: [s0;%- RefManager] +:: [s0;%- RelOps] +:: [s0;%- RichRef] +:: [s0;%- RichValue] +:: [s0;%- RichValue`::Rep] +:: [s0;%- RichValueRep] +:: [s0;%- Segtor] +:: [s0;%- Segtor`::Block] +:: [s0;%- Segtor`::ConstIterator] +:: [s0;%- Segtor`::Iterator] +:: [s0;%- Segtor`::ValueType] +:: [s0;%- SegtorMap] +:: [s0;%- SegtorMap`::B] +:: [s0;%- SegtorMap`::ConstIterator] +:: [s0;%- SegtorMap`::Iterator] +:: [s0;%- Semaphore] +:: [s0;%- Size] +:: [s0;%- Size16] +:: [s0;%- Size64] +:: [s0;%- SizeStream] +:: [s0;%- Size`_] +:: [s0;%- Sizef] +:: [s0;%- SortOrderIterator] +:: [s0;%- SortOrderIterator`::Iter] +:: [s0;%- StableSortItem] +:: [s0;%- StableSortIterator] +:: [s0;%- StableSortIterator`::Iter] +:: [s0;%- StableSortLessCmp`_] +:: [s0;%- StableSortLess`_] +:: [s0;%- StableSortOrderIterator] +:: [s0;%- StableSortOrderIterator`::Iter] +:: [s0;%- StaticCriticalSection] +:: [s0;%- StaticMutex] +:: [s0;%- StaticRWMutex] +:: [s0;%- StaticSemaphore] +:: [s0;%- StdCmp] +:: [s0;%- StdEqual] +:: [s0;%- StdGreater] +:: [s0;%- StdHash] +:: [s0;%- StdLess] +:: [s0;%- StdValueOrder] +:: [s0;%- Stream] +:: [s0;%- StreamError] +:: [s0;%- String] +:: [s0;%- String0] +:: [s0;%- String0`::Buffer] +:: [s0;%- String0`::Rc] +:: [s0;%- String0`::String] +:: [s0;%- String0`::bchar] +:: [s0;%- String0`::tchar] +:: [s0;%- String`::operatorstd] +:: [s0;%- StringBuffer] +:: [s0;%- StringBuffer`::Rc] +:: [s0;%- StringC] +:: [s0;%- StringICompare`_`_] +:: [s0;%- StringMap] +:: [s0;%- StringStream] +:: [s0;%- SyncObject] +:: [s0;%- TEC] +:: [s0;%- TextSettings] +:: [s0;%- TextTest] +:: [s0;%- Thread] +:: [s0;%- Time] +:: [s0;%- TimeStop] +:: [s0;%- TimingInspector] +:: [s0;%- TimingInspector`::Routine] +:: [s0;%- Topic] +:: [s0;%- TopicData`_`_] +:: [s0;%- TopicLink] +:: [s0;%- Uuid] +:: [s0;%- UuidValueGenClass] +:: [s0;%- Value] +:: [s0;%- Value`::Void] +:: [s0;%- ValueArray] +:: [s0;%- ValueArray`::Data] +:: [s0;%- ValueArray`::NullData] +:: [s0;%- ValueErrorCls] +:: [s0;%- ValueGen] +:: [s0;%- ValueMap] +:: [s0;%- ValueMap`::Data] +:: [s0;%- ValueMap`::NullData] +:: [s0;%- ValueOrder] +:: [s0;%- Vector] +:: [s0;%- Vector`::ConstIterator] +:: [s0;%- Vector`::Iterator] +:: [s0;%- Vector`::ValueType] +:: [s0;%- VectorMap] +:: [s0;%- VectorMap`::B] +:: [s0;%- VectorMap`::ConstIterator] +:: [s0;%- VectorMap`::Iterator] +:: [s0;%- Vector`_] +:: [s0;%- WString] +:: [s0;%- WString0] +:: [s0;%- WString0`::Buffer] +:: [s0;%- WString0`::String] +:: [s0;%- WString0`::bchar] +:: [s0;%- WString0`::tchar] +:: [s0;%- WString`::operatorstd] +:: [s0;%- WStringBuffer] +:: [s0;%- WStringICompare`_`_] +:: [s0;%- WildcardCompareCS] +:: [s0;%- WithDeepCopy] +:: [s0;%- XmlError] +:: [s0;%- XmlIO] +:: [s0;%- XmlNode] +:: [s0;%- XmlParser] +:: [s0;%- XmlParser`::Nesting] +:: [s0;%- XmlTag] +:: [s0;%- ZoneAlloc] +:: [s0;%- byte] +:: [s0;%- chrTextTest] +:: [s0;%- dword] +:: [s0;%- int16] +:: [s0;%- int32] +:: [s0;%- int64] +:: [s0;%- int8] +:: [s0;%- operatorstd] +:: [s0;%- qword] +:: [s0;%- stat] +:: [s0;%- uint16] +:: [s0;%- uint32] +:: [s0;%- uint64] +:: [s0;%- uint8] +:: [s0;%- wchar] +:: [s0;%- word]}}&] +[s1;%- &] +[s2;:cmp`(const T`&`,const T`&`):%- [@(0.0.255) template]_<[@(0.0.255) class]_[*@4 T][@(0.0.255) > +]_[@(0.0.255) int]_[* cmp]([@(0.0.255) const]_[*@4 T][@(0.0.255) `&]_[*@3 a], +[@(0.0.255) const]_[*@4 T][@(0.0.255) `&]_[*@3 b])&] +[s3; [%-*@3 a] [%-*@3 b].&] +[s4; &] +[s1; &] +[s2;:cmp`(const T`&`,const T`&`):%- [@(0.0.255) template]_<[@(0.0.255) class]_[*@4 I][@(0.0.255) > +]_[@(0.0.255) void]_[* Reverse]([*@4 I]_[*@3 start], [*@4 I]_[*@3 end])&] +[s3; [%-*@3 start] [%-*@3 end].&] +[s4; &] +[s1; &] +[s2;:cmp`(const T`&`,const T`&`):%- [@(0.0.255) template]_<[@(0.0.255) class]_[*@4 C][@(0.0.255) > +]_[@(0.0.255) void]_[* Reverse]([*@4 C][@(0.0.255) `&]_[*@3 container])&] +[s3; [%-*@3 container].&] +[s4; &] +[s1; &] +[s2;:cmp`(const T`&`,const T`&`):%- [@(0.0.255) template]_<[@(0.0.255) class]_[*@4 T], +[@(0.0.255) class]_[*@4 V][@(0.0.255) >]_[@(0.0.255) void]_[* Sum]([*@4 V][@(0.0.255) `&]_[*@3 su +m], [*@4 T]_[*@3 ptr], [*@4 T]_[*@3 end])&] +[s3; [%-*@3 sum] [%-*@3 ptr] [%-*@3 end].&] +[s4; &] +[s1; &] +[s2;:cmp`(const T`&`,const T`&`):%- [@(0.0.255) template]_<[@(0.0.255) class]_[*@4 T][@(0.0.255) > +]_[@(0.0.255) typename]_T`::ValueType_[* Sum]([@(0.0.255) const]_[*@4 T][@(0.0.255) `&]_[*@3 c +], [@(0.0.255) const]_[@(0.0.255) typename]_T`::ValueType[@(0.0.255) `&]_[*@3 init]_`=_[@(0.0.255) t +ypename]_T`::ValueType())&] +[s3; [%-*@3 c] [%-*@3 init].&] +[s4; &] +[s1; &] +[s2;:cmp`(const T`&`,const T`&`):%- [@(0.0.255) template]_<[@(0.0.255) class]_[*@4 T][@(0.0.255) > +]_[@(0.0.255) typename]_T`::ValueType_[* Sum0]([@(0.0.255) const]_[*@4 T][@(0.0.255) `&]_[*@3 c +])&] +[s3; [%-*@3 c].&] +[s4; &] +[s1; &] +[s2;:cmp`(const T`&`,const T`&`):%- [@(0.0.255) template]_<[@(0.0.255) class]_[*@4 T][@(0.0.255) > +]_[*@4 T]_[* MinElement]([*@4 T]_[*@3 ptr], [*@4 T]_[*@3 end])&] +[s3; [%-*@3 ptr] [%-*@3 end].&] +[s4; &] +[s1; &] +[s2;:cmp`(const T`&`,const T`&`):%- [@(0.0.255) template]_<[@(0.0.255) class]_[*@4 C][@(0.0.255) > +]_[@(0.0.255) int]_[* MinIndex]([@(0.0.255) const]_[*@4 C][@(0.0.255) `&]_[*@3 c])&] +[s3; [%-*@3 c].&] +[s4; &] +[s1; &] +[s2;:cmp`(const T`&`,const T`&`):%- [@(0.0.255) template]_<[@(0.0.255) class]_[*@4 C][@(0.0.255) > +]_[@(0.0.255) int]_[* MaxIndex]([@(0.0.255) const]_[*@4 C][@(0.0.255) `&]_[*@3 c])&] +[s3; [%-*@3 c].&] +[s4; &] +[s1; &] +[s2;:cmp`(const T`&`,const T`&`):%- [@(0.0.255) template]_<[@(0.0.255) class]_[*@4 T][@(0.0.255) > +]_[@(0.0.255) const]_[@(0.0.255) typename]_T`::ValueType[@(0.0.255) `&]_[* Min]([@(0.0.255) c +onst]_[*@4 T][@(0.0.255) `&]_[*@3 c])&] +[s3; [%-*@3 c].&] +[s4; &] +[s1; &] +[s2;:Min`(const T`&`):%- [@(0.0.255) template]_<[@(0.0.255) class]_[*@4 T][@(0.0.255) >]_[*@4 T +]_[* MaxElement]([*@4 T]_[*@3 ptr], [*@4 T]_[*@3 end])&] +[s3; [%-*@3 ptr] [%-*@3 end].&] +[s4; &] +[s1;%- &] +[s2;:MaxIndex`(const C`&`):%- [@(0.0.255) template]_<[@(0.0.255) class]_[*@4 T][@(0.0.255) >]_ +[*@4 T]_[* MinElement]([*@4 T]_[*@3 ptr], [*@4 T]_[*@3 end])&] +[s3; [%-*@3 ptr] [%-*@3 end].&] +[s4; &] +[s1; &] +[s2;:MinElement`(T`,T`):%- [@(0.0.255) template]_<[@(0.0.255) class]_[*@4 C][@(0.0.255) >]_[@(0.0.255) i +nt]_[* MinIndex]([@(0.0.255) const]_[*@4 C][@(0.0.255) `&]_[*@3 c])&] +[s3; [%-*@3 c].&] +[s4; &] +[s1; &] +[s2;:MaxIndex`(const C`&`):%- [@(0.0.255) template]_<[@(0.0.255) class]_[*@4 C][@(0.0.255) >]_ +[@(0.0.255) int]_[* MaxIndex]([@(0.0.255) const]_[*@4 C][@(0.0.255) `&]_[*@3 c])&] +[s3; [%-*@3 c].&] +[s4; &] +[s1;%- &] +[s2;:Sum0`(const T`&`):%- [@(0.0.255) template]_<[@(0.0.255) class]_[*@4 T][@(0.0.255) >]_[@(0.0.255) t +ypename]_T`::ValueType_[* Sum0]([@(0.0.255) const]_[*@4 T][@(0.0.255) `&]_[*@3 c])&] +[s3; [%-*@3 c].&] +[s4; &] +[s0; ] \ No newline at end of file diff --git a/uppdev/CoreTopics/src.tpp/TestTest$en-us.tppi b/uppdev/CoreTopics/src.tpp/TestTest$en-us.tppi new file mode 100644 index 000000000..a7da88020 --- /dev/null +++ b/uppdev/CoreTopics/src.tpp/TestTest$en-us.tppi @@ -0,0 +1,15 @@ +TITLE("") +COMPRESSED +120,156,237,91,251,115,219,184,17,254,87,52,211,75,70,206,56,25,241,33,219,177,211,206,217,178,28,43,39,201,26,75,137,218,122,52,18,68,194,50,107,138,228,145,80,98,93,38,255,123,1,62,36,98,241,160,227,58,246,165,109,126,72,132,239,195,99,119,177,88,44,64,228,170,246,203,47,141,221,198,95,26,21,127,14,79,241,53,90,249,100,114,117,190,119,212,160,141,12,214,168,185,71,169,189,166,97,219,118,195,54,247,140,134,101,218,150,97,53,76,171,105,188,109,238,29,206,241,194,11,38,87,158,109,31,28,33,179,121,116,251,219,197,219,35,147,182,54,105,107,107,223,176,14,154,150,65,27,210,191,12,203,48,155,166,101,27,7,230,91,251,192,178,246,27,135,78,232,98,143,224,229,228,202,55,15,14,210,118,22,109,103,238,55,77,99,223,62,176,15,12,107,255,192,164,205,205,70,195,108,236,155,84,14,235,192,108,30,186,56,113,38,87,76,72,155,86,127,187,103,189,109,24,141,198,190,193,196,219,111,88,77,219,48,44,147,118,97,238,155,70,163,121,136,3,119, +114,245,117,250,237,69,187,255,250,227,176,118,149,24,71,47,94,215,94,78,174,18,243,232,48,34,120,142,156,219,89,253,226,228,67,187,53,154,189,154,237,126,14,61,119,86,167,191,118,102,245,129,81,139,140,217,238,192,172,69,38,45,239,28,210,150,45,228,251,172,205,244,234,85,109,16,135,119,235,73,189,128,174,126,173,55,222,52,222,152,205,230,78,109,246,114,66,107,252,106,213,156,249,100,135,141,102,29,213,106,87,47,94,231,208,27,6,217,71,169,28,198,209,35,137,227,228,191,183,18,21,34,24,147,221,26,196,76,137,92,198,100,251,219,124,124,33,69,3,213,194,8,199,136,132,241,187,119,21,118,68,130,10,162,97,209,70,252,71,180,112,89,26,234,174,145,143,8,158,76,223,149,97,199,71,73,146,202,105,215,178,142,169,176,170,10,189,246,232,252,226,84,83,97,96,76,202,212,223,38,211,66,111,163,140,191,155,40,106,83,171,230,106,78,234,101,145,166,179,87,169,221,194,249,191,176,67,128,0,204,10,147,105,61,19,110,118,120,200,205,193,171,73,218,112,137, +201,77,232,78,118,234,197,192,211,200,216,129,115,144,247,94,20,243,54,255,211,179,177,93,151,255,159,142,63,215,116,148,7,9,131,132,240,98,254,152,25,154,138,163,254,185,39,237,135,153,60,55,97,110,229,235,160,194,149,105,133,159,216,4,32,87,184,79,187,167,202,33,158,211,31,190,183,169,144,206,124,87,195,39,204,121,158,212,168,154,100,234,187,28,13,61,204,176,255,197,153,152,134,52,85,51,102,202,45,71,187,146,183,123,210,132,109,43,197,52,50,31,144,46,52,242,227,83,237,235,87,131,29,29,107,57,116,220,9,92,124,55,57,60,228,1,42,89,139,237,113,29,146,185,166,172,194,39,228,175,240,104,29,225,50,217,67,17,40,234,186,202,104,53,243,27,94,87,180,165,53,36,34,168,164,27,146,216,11,22,34,66,235,107,168,185,115,131,98,57,179,186,190,198,114,138,192,70,243,48,38,237,59,167,12,185,238,69,148,148,129,96,205,151,104,63,39,40,193,167,136,32,129,128,96,28,163,53,44,235,108,47,227,105,249,161,237,250,3,73,37,77,251,82,211,19, +61,45,237,89,58,189,140,19,252,121,3,202,70,170,116,118,86,9,248,116,14,201,186,171,112,247,109,21,25,155,36,222,34,216,232,213,15,203,28,9,151,94,217,117,78,60,56,223,57,162,25,126,91,67,75,202,44,123,226,125,162,241,5,52,200,32,237,128,155,42,122,86,62,36,57,14,220,1,225,155,144,242,114,57,241,67,231,150,46,56,140,150,101,20,174,201,214,0,197,137,12,161,35,183,227,56,148,51,131,176,60,84,177,71,72,32,67,134,49,171,116,143,135,195,254,113,175,45,227,143,29,226,133,129,140,57,11,52,92,24,223,170,217,94,26,245,239,199,15,8,150,84,49,101,88,133,42,166,114,64,83,163,138,169,85,197,172,80,197,172,86,197,146,97,21,170,88,202,1,45,141,42,150,86,21,171,66,21,171,90,21,91,134,85,168,98,43,7,180,53,170,216,90,85,236,10,85,236,106,85,42,164,86,118,157,17,172,116,28,47,170,120,217,130,228,42,200,220,156,171,32,115,30,174,130,108,74,40,60,66,241,2,19,29,87,97,0,205,220,104,167,166,98,102,202,180, +220,128,160,134,204,132,176,138,124,134,65,45,153,169,97,149,123,117,36,155,18,88,229,94,29,201,166,14,86,185,87,71,213,149,196,26,248,206,131,238,225,5,60,132,227,88,216,210,90,52,149,60,243,124,194,111,98,27,112,132,239,200,8,39,4,144,67,88,198,4,228,140,173,112,76,189,74,0,168,147,118,67,71,138,247,62,132,115,25,62,8,67,159,195,125,126,91,101,229,51,14,88,206,189,0,211,127,34,20,99,145,56,71,201,13,143,210,122,104,238,99,1,196,151,216,231,179,232,28,23,173,152,166,41,146,76,132,39,96,198,41,176,35,158,252,140,99,34,34,167,136,159,252,28,13,87,80,135,20,239,4,146,46,40,184,103,139,176,112,80,201,241,145,183,228,123,94,17,209,4,177,99,113,75,50,246,136,231,32,127,136,97,232,56,237,118,207,143,251,167,221,54,143,141,254,49,224,16,234,79,179,233,112,54,229,177,178,28,167,243,197,137,127,123,142,145,203,121,239,41,158,175,22,221,112,145,230,112,28,142,163,86,24,173,47,34,40,80,246,169,115,236,249,174,131,98, +87,244,156,246,50,34,235,86,122,210,47,129,159,49,103,90,254,0,118,230,97,223,165,254,131,210,208,238,39,28,229,227,227,40,98,159,33,121,176,19,0,128,38,244,17,63,33,37,148,186,75,66,16,1,228,197,10,34,194,76,165,224,58,33,120,217,9,174,67,37,65,251,207,132,18,234,0,119,56,243,2,151,193,18,72,34,99,118,12,185,136,249,57,163,91,208,178,227,2,0,17,113,13,100,56,17,26,83,140,183,212,123,222,87,88,209,128,101,197,150,153,114,194,142,151,162,146,93,52,195,101,91,104,202,40,246,79,129,27,8,226,154,176,172,17,87,76,149,83,84,33,174,60,69,78,25,141,184,154,212,152,209,22,44,107,196,21,211,225,20,85,136,43,79,131,83,70,35,174,38,253,101,180,13,203,26,113,197,148,55,69,21,226,202,83,221,148,209,136,171,73,113,25,173,145,78,218,157,66,54,165,104,26,201,212,130,157,207,166,62,23,160,217,222,202,110,149,36,16,219,249,189,160,28,143,105,224,142,64,145,93,124,120,139,115,55,22,241,83,73,107,22,162,98,140,229, +140,176,47,228,248,0,45,56,249,60,26,101,86,1,221,18,147,8,94,68,200,118,116,229,102,174,218,199,185,176,214,113,211,61,159,67,186,30,15,128,91,38,217,5,83,213,221,82,202,15,195,152,152,247,34,243,91,20,89,29,75,215,129,117,143,14,116,237,213,205,63,132,94,32,6,255,18,154,53,41,111,107,221,86,231,148,191,78,235,182,64,78,209,189,252,216,66,206,13,150,64,146,254,182,76,15,221,114,210,117,81,176,200,229,128,89,1,163,122,161,187,242,105,242,232,132,177,11,40,150,79,75,137,21,245,74,176,213,150,97,42,132,58,61,41,87,108,13,21,68,187,175,34,84,45,206,46,21,196,251,182,130,0,230,231,151,37,43,94,124,9,120,67,6,139,118,64,226,245,108,202,133,145,110,97,67,30,163,134,43,3,33,114,233,150,15,175,217,104,226,39,100,60,84,46,209,153,122,120,121,73,67,132,88,25,47,101,88,24,175,59,139,32,140,113,23,163,219,4,230,150,89,133,65,28,94,243,137,80,22,59,229,121,96,207,227,151,69,47,252,140,193,57,164,128,142, +3,87,153,189,246,164,161,171,183,34,92,20,73,203,226,233,171,143,73,63,116,203,67,246,61,63,83,31,122,118,63,84,184,60,35,162,242,125,113,127,69,143,163,101,97,46,130,242,8,3,122,220,90,158,99,63,194,49,152,247,1,6,121,228,128,46,120,2,203,198,30,68,184,179,76,138,240,221,102,8,251,18,243,7,132,175,57,192,95,23,102,238,227,47,28,147,144,107,239,142,63,12,242,123,33,45,129,157,47,71,216,150,19,115,158,203,223,60,211,146,208,48,6,135,212,203,247,39,199,229,226,24,78,111,142,208,177,152,79,131,73,222,146,99,122,40,195,144,69,95,6,158,115,155,166,229,10,152,169,64,127,95,226,72,81,65,96,46,241,53,15,72,186,47,186,22,26,167,68,139,250,32,145,181,200,136,138,118,114,14,160,236,187,38,95,228,92,139,1,156,103,49,128,115,172,12,96,214,129,29,137,222,198,80,206,38,24,148,138,143,21,2,209,67,1,141,175,49,7,130,123,137,75,207,185,1,237,40,34,216,188,192,68,227,21,12,15,15,241,130,143,42,25,192,146,17, +16,255,54,132,42,35,217,84,208,113,178,143,53,25,201,239,44,27,12,164,69,101,92,47,136,242,83,217,16,47,81,116,19,114,251,235,208,251,3,22,57,71,97,0,231,40,12,16,246,16,6,114,222,195,128,242,148,177,140,40,61,23,203,196,130,156,152,50,13,9,219,41,242,180,106,169,34,132,142,5,82,215,115,23,39,73,107,25,241,106,112,172,130,82,234,37,175,33,149,129,120,142,250,82,41,227,97,88,204,80,49,92,102,184,116,174,137,75,21,228,129,246,239,43,228,243,208,251,52,202,240,2,186,32,104,83,132,89,132,71,164,183,31,162,175,164,0,76,109,132,235,185,12,104,136,8,91,23,240,91,229,150,186,116,164,176,170,123,201,51,133,45,3,223,34,108,30,41,20,47,143,18,226,10,180,66,182,12,150,11,216,18,144,78,158,8,131,28,34,35,65,188,72,49,209,202,235,192,185,200,158,183,108,193,81,187,60,20,203,215,135,56,189,91,74,0,12,174,197,71,55,180,247,178,170,224,150,140,21,135,36,140,120,136,233,33,201,222,0,195,76,18,174,168,16,92, +135,97,196,125,183,79,203,217,173,41,103,144,20,7,185,248,199,149,231,130,98,234,152,239,113,0,179,59,184,137,20,27,200,167,144,235,34,133,225,195,129,45,40,62,42,225,56,150,49,202,248,116,5,240,89,115,33,39,132,248,25,47,32,197,184,25,163,26,21,46,79,225,129,66,229,243,4,221,227,4,237,211,132,140,4,186,20,24,216,237,202,184,94,16,229,110,151,203,82,118,151,177,16,6,198,98,152,25,107,226,204,88,19,81,198,234,144,50,86,199,148,113,69,80,25,43,162,202,88,27,40,192,121,154,59,56,143,61,114,83,156,4,74,240,223,151,62,140,201,20,234,92,240,101,112,160,162,136,240,74,100,131,49,47,164,113,132,183,18,101,71,168,12,252,51,12,240,177,79,83,174,18,54,95,115,217,177,115,35,251,86,231,126,225,111,26,224,233,137,150,185,143,54,240,44,69,203,7,165,162,220,252,191,131,65,192,141,255,10,14,186,130,163,174,224,176,43,48,238,23,224,16,233,128,223,190,101,207,71,95,108,254,239,150,67,51,147,122,250,126,190,54,154,189,156,237, +110,127,62,228,177,232,72,120,148,89,42,178,99,105,250,108,153,230,10,202,255,52,192,247,192,61,169,253,206,38,15,121,78,251,184,214,232,232,172,145,189,60,165,230,184,196,159,49,117,235,252,209,106,39,151,158,58,68,76,138,199,174,5,200,62,127,65,165,178,138,69,137,213,120,10,213,90,15,80,173,37,159,39,42,8,65,30,187,227,18,94,149,111,152,167,80,73,247,198,249,211,189,212,29,174,150,185,170,159,228,170,38,180,66,62,165,163,28,138,72,12,33,233,44,211,150,197,111,214,228,169,231,91,187,176,9,221,143,3,68,51,183,233,168,188,69,111,77,242,125,11,215,81,172,245,234,17,165,221,165,47,58,166,179,191,222,167,131,186,240,148,219,217,152,58,237,231,79,110,235,198,3,140,45,106,252,60,74,110,150,64,173,231,5,109,31,47,217,83,129,250,67,22,203,115,44,16,109,64,44,118,62,170,88,246,165,74,57,75,170,24,249,76,179,116,63,173,208,221,207,165,149,118,129,253,39,209,134,77,240,15,92,129,180,247,178,21,30,125,217,161,187,31,184,236,182, +41,103,225,47,133,46,173,159,54,132,108,71,153,213,71,179,221,209,79,29,57,30,107,90,158,59,102,108,29,141,109,136,143,191,96,158,113,51,110,28,213,38,255,6,106,173,94,54, + diff --git a/uppdev/CoreTopics/src.tpp/all.i b/uppdev/CoreTopics/src.tpp/all.i index 462f171af..47eb97a84 100644 --- a/uppdev/CoreTopics/src.tpp/all.i +++ b/uppdev/CoreTopics/src.tpp/all.i @@ -158,3 +158,7 @@ TOPIC("Atest$en-us") #include "Atest$en-us.tppi" END_TOPIC +TOPIC("TestTest$en-us") +#include "TestTest$en-us.tppi" +END_TOPIC + diff --git a/uppdev/Parser/Parser.upp b/uppdev/Parser/Parser.upp index e37688faa..b7703b4e9 100644 --- a/uppdev/Parser/Parser.upp +++ b/uppdev/Parser/Parser.upp @@ -1,8 +1,11 @@ uses - RichEdit; + Core, + CtrlLib, + CodeEditor; file test.cpp, + test2.cpp, ignorelist; mainconfig diff --git a/uppdev/Parser/init b/uppdev/Parser/init index 85b9623b6..f5ffaff84 100644 --- a/uppdev/Parser/init +++ b/uppdev/Parser/init @@ -1,4 +1,6 @@ #ifndef _Parser_icpp_init_stub #define _Parser_icpp_init_stub -#include "RichEdit/init" +#include "Core/init" +#include "CtrlLib/init" +#include "CodeEditor/init" #endif diff --git a/uppdev/Parser/test.cpp b/uppdev/Parser/test.cpp index 21d8a52b9..470238dce 100644 --- a/uppdev/Parser/test.cpp +++ b/uppdev/Parser/test.cpp @@ -1,11 +1,124 @@ #include +struct alpha { + int beta; +}; + +struct oper2 { + int oper2m; +}; + +struct oper3 { + int oper3m; +}; + +struct X { + alpha a; + + alpha operator[](int i); +}; + +struct Y : X{ + X b; + int a; + + struct H { + int x; + }; + + H x; +}; + +template +struct Tm { + template + struct H { + T Test(); + V Test2(); + }; + + T x; +}; + + +void Test(int a, long b) +{ + X().; + Y x; + x.; + x.a. ; // should not work... + x.X::a. ; + x.b.a. ; + x[]. ; + x(). ; + x. ; + x-> ; + y. ; + Tm tm; + Tm< X > :: H tt; + tt. ; + tt.Test(). ; + tt.Test2(). ; + X:: ; + ; + +} + +// ------------------------------------------------------------------------------------------ + +void Foo() +{ + +} + +CppItemInfoDisplay ItemList::display; + +class ItemList : public ColumnList { + CppItemInfoDisplay display; + + friend struct ItemDisplay; + + int GetTopic(Point p, String& key); + String Item(int i); + +public: + bool active_topics; + + void Clear(); + + ItemList(); +}; + +void ItemList::Clear() +{ +} + +void ItemList::Clear(int) +{ +} + + void Clear(); + +typedef short unsigned word; + +typedef unsigned char byte; +typedef signed char int8; +typedef unsigned char uint8; + +typedef short int int16; +typedef short unsigned uint16; + struct FindBrokenRefIterator : RichTxt::Iterator { int cursor; Uuid itemstyle; + + void Test(); virtual bool operator()(int pos, const RichPara& para) { + + this-> ; + THISBACK( ; if(pos >= cursor) { if(para.format.label != "noref") { if(IsNull(para.format.label)) @@ -27,3 +140,44 @@ struct FindBrokenRefIterator : RichTxt::Iterator { } }; +void FindBrokenRefIterator::Test() +{ +} + +void Foo() +{ + String x; + x.Right(). ; + String(xxx). ; + String x; + x.; + x->; + AttrText("Hello!"). ; + Vector vx; + vx. ; + vx[0]. ; + vx.Add(). ; + vx[654].Right(). ; + vx[654]->Right(). ; + Vector(). ; + x > x.; + EditorBar bar; + bar. ; + EditString es; + es. ; + es.NullText(). ; + THISBACK( +} + +struct Dlg : WithEditStringLayout { + Dlg() { + Ctrl:: ; + Dlg x; + x. ; + this. ; + this-> ; + a + } + Vector:: +}; + diff --git a/uppdev/Parser/test2.cpp b/uppdev/Parser/test2.cpp new file mode 100644 index 000000000..b30450526 --- /dev/null +++ b/uppdev/Parser/test2.cpp @@ -0,0 +1,4 @@ +void ItemList::Clear(String x) +{ +} + diff --git a/uppdev/Parser2/Parser2.upp b/uppdev/Parser2/Parser2.upp index f32ce80e7..c05cefc38 100644 --- a/uppdev/Parser2/Parser2.upp +++ b/uppdev/Parser2/Parser2.upp @@ -1,4 +1,5 @@ file + hh.cpp, test.cpp; mainconfig diff --git a/uppdev/Parser2/test.cpp b/uppdev/Parser2/test.cpp index ad55d1fe9..781ac6270 100644 --- a/uppdev/Parser2/test.cpp +++ b/uppdev/Parser2/test.cpp @@ -1 +1,68 @@ -void Test(int a, long b); +struct alpha { + int beta; +}; + +struct oper2 { + int oper2m; +}; + +struct oper3 { + int oper3m; +}; + +struct X { + alpha a; + + alpha operator[](int i); +}; + +struct Y : X{ + X b; + int a; + + struct H { + int x; + }; + + H x; +}; + +template +struct Tm { + template + struct H { + T Test(); + V Test2(); + }; + + T x; +}; + + +void Test(int a, long b) +{ + X().; + Y x; + x.; + x.a. ; // should not work... + x.X::a. ; + x.b.a. ; + x[]. ; + x(). ; + x. ; + x-> ; + y. ; + Tm tm; + Tm< X > :: H tt; + tt. ; + tt.Test(). ; + tt.Test2(). ; + X:: ; + ; + +} + +void Foo(volatile ::String) +{ + +} \ No newline at end of file diff --git a/uppdev/RichTextP/Copying b/uppdev/RichTextP/Copying new file mode 100644 index 000000000..f135f3633 --- /dev/null +++ b/uppdev/RichTextP/Copying @@ -0,0 +1,21 @@ +Copyright 1998-2008 The U++ Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted +provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials + provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE U++ PROJECT ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FREEBSD PROJECT OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/uppdev/RichTextP/EncodeHTML.cpp b/uppdev/RichTextP/EncodeHTML.cpp new file mode 100644 index 000000000..c00d6a9bb --- /dev/null +++ b/uppdev/RichTextP/EncodeHTML.cpp @@ -0,0 +1,361 @@ +#include "RichText.h" + +NAMESPACE_UPP + +String HtmlFontStyle(Font f, Font base) +{ + String style; + if(f.GetFace() != base.GetFace()) + switch(f.GetFace()) { + case Font::ARIAL: style = "font-family:sans-serif;"; break; + case Font::ROMAN: style = "font-family:serif;"; break; + case Font::COURIER: style = "font-family:monospace;"; break; + } + if(f.GetHeight() != base.GetHeight()) + style << Sprintf("font-size:%dpt;", f.GetHeight() * 72 / 600); + if(f.IsBold() != base.IsBold()) + style << (f.IsBold() ? "font-weight:bold;" : "font-weight:normal;"); + if(f.IsItalic() != base.IsItalic()) + style << (f.IsItalic() ? "font-style:italic;" : "font-style:normal;"); + if(f.IsUnderline() != base.IsUnderline()) + style << (f.IsUnderline() ? "text-decoration:underline;" : "text-decoration:none;"); + return style; +} + +String HtmlFontStyle(Font f) +{ + String style; + switch(f.GetFace()) { + case Font::ARIAL: style = "font-family:sans-serif;"; break; + case Font::ROMAN: style = "font-family:serif;"; break; + case Font::COURIER: style = "font-family:monospace;"; break; + } + style << Sprintf("font-size:%dpt;", f.GetHeight() * 72 / 600); + style << (f.IsBold() ? "font-weight:bold;" : "font-weight:normal;"); + style << (f.IsItalic() ? "font-style:italic;" : "font-style:normal;"); + style << (f.IsUnderline() ? "text-decoration:underline;" : "text-decoration:none;"); + return style; +} + +String HtmlDot(int q, Zoom z) +{ + return String().Cat() << ' ' << z * q << "px"; +} + +String HtmlDotl(int q, Zoom z) +{ + return String().Cat() << ' ' << max((int)!!q, z * q) << "px"; +} + +String HtmlStyleColor(Color c, const char *cl = "color") +{ + return Format(String(cl) + ":#%02x%02x%02x;", c.GetR(), c.GetG(), c.GetB()); +} + +String HtmlCharStyle(const RichPara::CharFormat& cf, const RichPara::CharFormat& sf) +{ + String style; + if(cf.ink != sf.ink) + style = HtmlStyleColor(cf.ink); + return style + HtmlFontStyle(cf, sf); +} + +String HtmlParaStyle(const RichPara::Format& f, Zoom z) +{ + String style; + int lm = z * f.lm; + if(f.bullet && f.bullet != RichPara::BULLET_TEXT) { + style << "display:list-item;line-style-type:"; + switch(f.bullet) { + case RichPara::BULLET_ROUND: style << "disc"; break; + case RichPara::BULLET_ROUNDWHITE: style << "circle"; break; + case RichPara::BULLET_BOX: + case RichPara::BULLET_BOXWHITE: style << "square"; break; + } + style << ";line-style-position:inside;"; + lm += 20; + } + style << Format("margin:%d %d %d %d;text-indent:%d;", + z * f.before, + z * f.rm, + z * f.after, + lm, + z * (f.bullet ? 0 : f.indent) + ); + style << "text-align:"; + switch(f.align) { + case ALIGN_LEFT: style << "left;"; break; + case ALIGN_RIGHT: style << "right;"; break; + case ALIGN_CENTER: style << "center;"; break; + case ALIGN_JUSTIFY: style << "justify;"; break; + } + style << HtmlStyleColor(f.ink) + HtmlFontStyle(f); + if(!IsNull(f.paper)) + style << HtmlStyleColor(f.paper, "background-color"); + return style; +} + +String FormatClass(Index& css, const String& fmt) +{ + return " CLASS=" + FormatIntAlpha(css.FindAdd(fmt) + 1); +} + +void TabBorder(String& style, const char *txt, int border, Color bordercolor, const RichTable::Format& tf, Zoom z) +{ + style << "border-" << txt << ':' << HtmlDotl(border + tf.grid, z) << " solid " + << ColorToHtml(border ? bordercolor : tf.gridcolor) << ';'; +} + + +String AsHtml(const RichTxt& text, const RichStyles& styles, Index& css, + const VectorMap& links, + const VectorMap& labels, + const String& outdir, const String& namebase, Zoom z, int& im, + const VectorMap& escape, + int imtolerance) +{ + String html; + for(int i = 0; i < text.GetPartCount(); i++) + { + if(text.IsTable(i)) { + const RichTable& t = text.GetTable(i); + int nx = t.format.column.GetCount(); + int ny = t.cell.GetCount(); + const RichTable::Format& tf = t.format; + html << ""; + if(tf.before > 0) + html << ""; + html << "\r\n" + "\r\n" + << ""; + if(tf.after > 0) + html << ""; + html << "
"; + + String style; + style << "border-collapse:collapse;table-layout:auto;" + << "border:" << HtmlDotl(tf.frame, z) << " solid " << ColorToHtml(tf.framecolor) << ';'; + + html << ""; + int sum = 0; + for(int i = 0; i < nx; i++) + sum += t.format.column[i]; + html << ""; + for(int i = 0; i < nx; i++) + html << ""; + html << "\r\n"; + for(int i = 0; i < ny; i++) { + const Array& r = t.cell[i]; + html << ""; + for(int j = 0; j < r.GetCount(); j++) { + if(t(i, j)) { + const RichCell& c = r[j]; + const RichCell::Format& cf = c.format; + String style; + style << "padding:" << HtmlDot(cf.margin.top, z) << HtmlDot(cf.margin.right, z) + << HtmlDot(cf.margin.bottom, z) << HtmlDot(cf.margin.left, z) << ';'; + TabBorder(style, "left", cf.border.left, cf.bordercolor, tf, z); + TabBorder(style, "top", cf.border.top, cf.bordercolor, tf, z); + TabBorder(style, "right", cf.border.right, cf.bordercolor, tf, z); + TabBorder(style, "bottom", cf.border.bottom, cf.bordercolor, tf, z); + style << "background-color:" << ColorToHtml(cf.color) << ';'; + style << "vertical-align:"; + switch(cf.align) { + case ALIGN_TOP: style << "top"; break; + case ALIGN_CENTER: style << "middle"; break; + case ALIGN_BOTTOM: style << "bottom"; break; + } + style << ';'; + html << "\r\n"; + } + } + html << "\r\n"; + } + html << "
\r\n"; + } + else + if(text.IsPara(i)) { + RichPara p = text.Get(i, styles); + if(p.format.ruler) + html << "
"; + String lbl; + if(!IsNull(p.format.label)) { + lbl = labels.Get(p.format.label, Null); + if(lbl.GetCount()) + html << ""; + } + bool bultext = false; + if(p.format.bullet == RichPara::BULLET_TEXT) + for(int i = 0; i < p.part.GetCount(); i++) { + const RichPara::Part& part = p.part[i]; + if(part.text.Find(9) >= 0) { + bultext = true; + break; + } + } + if(bultext) { + html << "" + ""; + int q = z * p.format.lm - 8; + if(q > 0) + html << Format("", q); + html << Format("\r\n
\r\n", + max(z * p.format.indent, 0)); + p.format.ruler = p.format.after = p.format.before = p.format.indent = p.format.lm = 0; + } + String par = ""; + html << par; + for(int i = 0; i < p.part.GetCount(); i++) { + const RichPara::Part& part = p.part[i]; + int q; + if(part.object) { + String name; + name << namebase << "_" << im++ << ".png"; + Size psz = part.object.GetPixelSize(); + String lname; + lname << "L$" << name; + Size sz = z * part.object.GetSize(); + if(abs(100 * (psz.cx - sz.cx) / sz.cx) < imtolerance) + sz = psz; + PNGEncoder png; + png.SaveFile(AppendFileName(outdir, name), part.object.ToImage(sz)); + if(psz.cx * psz.cy) + html << ""; + html << "\"\""; + if(psz.cx * psz.cy) { + html << ""; + PNGEncoder png; + png.SaveFile(AppendFileName(outdir, lname), part.object.ToImage(psz)); + } + } + else + if(part.format.indexentry.GetCount() && + (q = escape.Find(part.format.indexentry.ToString())) >= 0) + html << escape[q]; + else { + String lnk = part.format.link; + if(lnk.GetCount()) { + int q = links.Find(lnk); + if(q < 0) { + int q = lnk.ReverseFind('#'); + if(q >= 0) { + String l = lnk.Left(q); + lnk = links.Get(l, l) + '#' + lnk.Mid(q + 1); + } + } + else + lnk = links[q]; + } + String endtag; + if(!lnk.IsEmpty() && lnk[0] != ':') { + html << ""; + endtag = ""; + } + String cs; + if(part.text[0] != 9) + cs = HtmlCharStyle(part.format, p.format); + if(!cs.IsEmpty()) { + html << ""; + endtag = "" + endtag; + } + if(part.format.sscript == 1) { + html << ""; + endtag = "" + endtag; + } + if(part.format.sscript == 2) { + html << ""; + endtag = "" + endtag; + } + if(part.format.IsStrikeout()) { + html << ""; + endtag = "" + endtag; + } + if(part.format.capitals) { + html << ""; + endtag << ""; + } + bool spc = false; + const wchar *end = part.text.End(); + for(const wchar *s = part.text.Begin(); s != end; s++) { + if(*s == ' ') { + html.Cat(spc ? " " : " "); + spc = true; + } + else { + spc = false; + if(*s == 160) html.Cat(" "); + else + if(*s == '<') html.Cat("<"); + else + if(*s == '>') html.Cat(">"); + else + if(*s == '&') html.Cat("&"); + else + if(*s == '\"') html.Cat("""); + else + if(*s == 9) { + if(bultext) { + if(!cs.IsEmpty() && part.text[0] != 9) + html << ""; + html << "

"; + html << "
\r\n"; + html << par; + if(s[1]) { + cs = HtmlCharStyle(part.format, p.format); + if(!cs.IsEmpty()) + html << ""; + } + } + else + html.Cat("    "); + } + else + html.Cat(ToUtf8(*s)); + } + } + html << endtag; + } + } + if(p.part.GetCount() == 0) + html << " "; + html << "

"; + if(bultext) + html << "
"; + if(lbl.GetCount()) + html << "
"; + html << "\r\n"; + } + } + return html; +} + +String EncodeHtml(const RichText& text, Index& css, + const VectorMap& links, + const VectorMap& labels, + const String& outdir, const String& namebase, Zoom z, + const VectorMap& escape, int imt) +{ + int im = 0; + return AsHtml(text, text.GetStyles(), css, links, labels, outdir, namebase, z, im, escape, imt); +} + +String AsCss(Index& ss) +{ + String css; + for(int i = 0; i < ss.GetCount(); i++) { + css << "." + FormatIntAlpha(i + 1); + css << "{" << ss[i] << "}\r\n"; + } + return css; +} + +END_UPP_NAMESPACE diff --git a/uppdev/RichTextP/EncodeQtf.cpp b/uppdev/RichTextP/EncodeQtf.cpp new file mode 100644 index 000000000..0864afdc3 --- /dev/null +++ b/uppdev/RichTextP/EncodeQtf.cpp @@ -0,0 +1,552 @@ +#include "RichText.h" + +NAMESPACE_UPP + +extern Color (*QTFColor[])(); +extern int QTFFontHeight[]; + +void SeparateNumber(String& s) +{ + if(*s.Last() >= '0' && *s.Last() <= '9') + s.Cat(';'); +} + +String FmtColor(Color c) +{ + if(IsNull(c)) return "N"; + for(int i = 0; i < 10; i++) + if((*QTFColor[i])() == c) + return String(i + '0', 1); + if(c.GetR() == c.GetG() && c.GetG() == c.GetB()) + return Sprintf("(%d)", c.GetR()); + return Sprintf("(%d.%d.%d)", c.GetR(), c.GetG(), c.GetB()); +} + +void LngFmt(String& fmt, dword l, dword lang) +{ + if(lang != (dword)l) + if(l == 0) + fmt << "%-"; + else + if(l == LNG_ENGLISH) + fmt << "%%"; + else + fmt << "%" << LNGAsText(l); +} + +void CharFmt(String& fmt, const RichPara::CharFormat& a, const RichPara::CharFormat& b) +{ + if(a.IsBold() != b.IsBold()) fmt.Cat('*'); + if(a.IsItalic() != b.IsItalic()) fmt.Cat('/'); + if(a.IsUnderline() != b.IsUnderline()) fmt.Cat('_'); + if(a.IsStrikeout() != b.IsStrikeout()) fmt.Cat('-'); + if(a.IsNonAntiAliased() != b.IsNonAntiAliased()) fmt.Cat('t'); + if(a.capitals != b.capitals) fmt.Cat('c'); + if(a.dashed != b.dashed) fmt.Cat('d'); + if(a.sscript != b.sscript) + fmt.Cat(b.sscript == 0 ? a.sscript == 1 ? '`' : ',' : + b.sscript == 1 ? '`' : ','); + if(a.GetFace() != b.GetFace()) + switch(b.GetFace()) { + case Font::ARIAL: fmt.Cat('A'); break; + case Font::ROMAN: fmt.Cat('R'); break; + case Font::COURIER: fmt.Cat('C'); break; + case Font::STDFONT: fmt.Cat('G'); break; + case Font::SYMBOL: fmt.Cat('S'); break; + default: + fmt << "!" << b.GetFaceName() << "!"; + } + if(a.link != b.link) + fmt << '^' << DeQtf(b.link) << '^'; + if(a.indexentry != b.indexentry) + fmt << 'I' << DeQtf(ToUtf8(b.indexentry)) << ';'; + if(a.ink != b.ink) + fmt << "@" << FmtColor(b.ink); + if(a.paper != b.paper) + fmt << "$" << FmtColor(b.paper); + if(a.GetHeight() != b.GetHeight()) { + for(int i = 0; i < 10; i++) + if(b.GetHeight() == QTFFontHeight[i]) { + SeparateNumber(fmt); + fmt.Cat('0' + i); + return; + } + fmt.Cat(Format("+%d", b.GetHeight())); + } +} + +void FmtNumber(String& qtf, char c, int former, int current) +{ + qtf << (former != current ? Format("%c%d;", c, current) : String()); +} + +void QTFEncodeParaFormat(String& qtf, const RichPara::Format& format, const RichPara::Format& style) +{ + if(format.align != style.align) + switch(format.align) { + case ALIGN_LEFT: qtf << '<'; break; + case ALIGN_RIGHT: qtf << '>'; break; + case ALIGN_CENTER: qtf << '='; break; + case ALIGN_JUSTIFY: qtf << '#'; break; + } + FmtNumber(qtf, 'l', style.lm, format.lm); + FmtNumber(qtf, 'r', style.rm, format.rm); + FmtNumber(qtf, 'i', style.indent, format.indent); + FmtNumber(qtf, 'H', style.ruler, format.ruler); + if(style.rulerink != format.rulerink) + qtf << "h" << FmtColor(format.rulerink); + FmtNumber(qtf, 'b', style.before, format.before); + FmtNumber(qtf, 'a', style.after, format.after); + if(style.newpage != format.newpage) + qtf << 'P'; + if(style.keep != format.keep) + qtf << 'k'; + if(style.keepnext != format.keepnext) + qtf << 'K'; + if(style.orphan != format.orphan) + qtf << 'Q'; + if(style.linespacing != format.linespacing) + switch(format.linespacing) { + case RichPara::LSP15: qtf << "ph"; break; + case RichPara::LSP20: qtf << "pd"; break; + default: qtf << "po"; break; + } + if(style.bullet != format.bullet) { + qtf << 'O'; + switch(format.bullet) { + case RichPara::BULLET_NONE: qtf << '_'; break; + case RichPara::BULLET_ROUNDWHITE: qtf << '1'; break; + case RichPara::BULLET_BOX: qtf << '2'; break; + case RichPara::BULLET_BOXWHITE: qtf << '3'; break; + case RichPara::BULLET_TEXT: qtf << '9'; break; + default: qtf << '0'; break; + } + qtf << ';'; + } + if(!IsEmpty(format.label)) + qtf << ':' << DeQtf(format.label) << ':'; + if(NumberingDiffers(style, format)) { + if(format.before_number != style.before_number) { + qtf << "n"; + qtf << DeQtf(format.before_number); + qtf << ';'; + } + if(format.after_number != style.after_number) { + qtf << "m"; + qtf << DeQtf(format.after_number); + qtf << ";"; + } + int l; + for(l = 7; l >= 0; --l) + if(format.number[l]) + break; + qtf << "N"; + for(int i = 0; i <= l; i++) { + static char h[] = { '-', '1', '0', 'a', 'A', 'i', 'I' }; + if(format.number[i] <= RichPara::NUMBER_I) + qtf << h[format.number[i]]; + else + qtf << '-'; + } + if(format.reset_number) + qtf << '!'; + else + qtf << ';'; + } + FmtNumber(qtf, 't', style.tabsize, format.tabsize); + if(style.tab != format.tab) { + qtf << "~~"; + int i; + for(i = 0; i < format.tab.GetCount(); i++) { + RichPara::Tab tab = format.tab[i]; + qtf << '~'; + if(tab.align == ALIGN_RIGHT) + qtf << '>'; + if(tab.align == ALIGN_CENTER) + qtf << '='; + if(tab.fillchar == 1) + qtf << '.'; + if(tab.fillchar == 2) + qtf << '-'; + if(tab.fillchar == 3) + qtf << '_'; + qtf << tab.pos; + } + if(i) + qtf << ';'; + } +} + +bool s_nodeqtf[128]; + +void QTFEncodePara(String& qtf, const RichPara& p, const RichPara::Format& style, byte charset, dword lang, bool crlf) +{ + int d = qtf.GetLength(); + QTFEncodeParaFormat(qtf, p.format, style); + if(p.part.GetCount()) { + dword l = p.part.Top().format.language; + LngFmt(qtf, l, lang); + lang = l; + } + else { + CharFmt(qtf, style, p.format); + LngFmt(qtf, p.format.language, lang); + } + qtf.Cat(' '); + d = qtf.GetLength() - d; + for(int i = 0; i < p.part.GetCount(); i++) { + const RichPara::Part& part = p.part[i]; + String cf; + LngFmt(cf, part.format.language, lang); + CharFmt(cf, style, part.format); + if(!cf.IsEmpty()) { + qtf << '[' << cf << ' '; + d += cf.GetLength(); + } + if(part.field) { + if(crlf) + qtf << "\r\n"; + qtf << "{:" + DeQtf(part.field.ToString()) + ":" + DeQtf(part.fieldparam) + ":}"; + if(crlf) + qtf << "\r\n"; + d = 0; + } + else + if(part.object) { + const RichObject& object = part.object; + Size sz = object.GetSize(); + if(crlf) + qtf << "\r\n"; + qtf << "@@" << object.GetTypeName() << ':' << sz.cx + << (object.IsKeepRatio() ? '&' : '*') << sz.cy; + if(object.GetYDelta()) + qtf << '/' << object.GetYDelta(); + if(crlf) + qtf << "\r\n"; + String data = object.Write(); + const char *q = data.Begin(); + const char *slim = data.End(); + int n = 0; + qtf.Reserve(8 * data.GetLength() / 7); + while(q < slim - 7) { + byte data[8]; + data[0] = ((q[0] & 0x80) >> 7) | + ((q[1] & 0x80) >> 6) | + ((q[2] & 0x80) >> 5) | + ((q[3] & 0x80) >> 4) | + ((q[4] & 0x80) >> 3) | + ((q[5] & 0x80) >> 2) | + ((q[6] & 0x80) >> 1) | + 0x80; + data[1] = q[0] | 0x80; + data[2] = q[1] | 0x80; + data[3] = q[2] | 0x80; + data[4] = q[3] | 0x80; + data[5] = q[4] | 0x80; + data[6] = q[5] | 0x80; + data[7] = q[6] | 0x80; + qtf.Cat(data, 8); + if(crlf && ++n % 10 == 0) + qtf << "\r\n"; + q += 7; + } + while(q < slim) { + byte seven = 0; + const char *lim = slim; + const char *s; + for(s = q; s < lim; s++) + seven = (seven >> 1) | (*s & 0x80); + seven >>= 8 - (lim - q); + qtf.Cat(seven | 0x80); + for(s = q; s < lim; s++) + qtf.Cat(*s | 0x80); + if(crlf && ++n % 10 == 0) + qtf << "\r\n"; + q += 7; + } + if(crlf) + qtf << "\r\n"; + d = 0; + } + else { + for(const wchar *s = part.text.Begin(); s != part.text.End(); s++) { + int c = *s; + if(c < 128) { + if(s_nodeqtf[c]) { + qtf.Cat(c); + d++; + } + else + if(c == 9) { + qtf.Cat("-|"); + d++; + } + else + if(c == ':' && s[1] != ':') + qtf.Cat(':'); + else { + qtf.Cat('`'); + qtf.Cat(c); + d += 2; + } + if(crlf && d > 60 && c == ' ') { + qtf.Cat("\r\n"); + d = 0; + } + } + else { + if(c == 160) { + qtf.Cat("_"); + d++; + } + else + if(charset == CHARSET_UTF8) { + String q = ToUtf8(c); + d += q.GetLength(); + qtf << q; + } + else { + int ch = FromUnicode(c, charset, 0); + if(ch) + qtf << (char)ch; + else + qtf << "@$" << Format("%04X", ch) << ';'; + d++; + } + } + if(crlf && d > 80) { + qtf.Cat("\r\n"); + d = 0; + } + } + } + if(!cf.IsEmpty()) { + d++; + qtf << ']'; + } + } +} + +void FmtNumber2(String& qtf, char c, int da, int a, int db, int b) +{ + if(da != a || db != b) { + qtf << c; + if(da != a) + qtf << a; + qtf << '/'; + qtf << b; + } +} + + +void QTFEncodeTxt(String& qtf, const RichTxt& text, const RichStyles& styles, const RichStyle& defstyle, + dword options, const Index& sm, byte charset, dword lang) +{ + qtf << '['; + for(int i = 0; i < text.GetPartCount(); i++) { + if(i) { + qtf << "&]"; + if(options & QTF_CRLF) + qtf << "\r\n"; + qtf << '['; + } + if(text.IsTable(i)) { + qtf << ' '; + + const RichTable& t = text.GetTable(i); + int nx = t.format.column.GetCount(); + int ny = t.cell.GetCount(); + qtf << "{{"; + for(int i = 0; i < nx; i++) { + if(i) + qtf << ':'; + qtf << t.format.column[i]; + } + const RichTable::Format& f = t.format; + const RichTable::Format& d = Single(); + FmtNumber(qtf, '<', d.lm, f.lm); + FmtNumber(qtf, '>', d.rm, f.rm); + FmtNumber(qtf, 'B', d.before, f.before); + FmtNumber(qtf, 'A', d.after, f.after); + FmtNumber(qtf, 'f', d.frame, f.frame); + if(f.keep) + qtf << "K"; + if(f.framecolor != d.framecolor) + qtf << 'F' << FmtColor(f.framecolor); + FmtNumber(qtf, 'g', d.grid, f.grid); + if(f.gridcolor != d.gridcolor) + qtf << 'G' << FmtColor(f.gridcolor); + FmtNumber(qtf, 'h', d.header, f.header); + RichCell::Format cf = Single(); + for(int i = 0; i < ny; i++) { + const Array& r = t.cell[i]; + for(int j = 0; j < r.GetCount(); j++) { + const RichCell& c = r[j]; + if(i || j) { + if(options & QTF_CRLF) + qtf << "\r\n"; + qtf << "::"; + } + const RichCell::Format& f = c.format; + if(f.align != cf.align) + switch(f.align) { + case ALIGN_TOP: qtf << '^'; break; + case ALIGN_CENTER: qtf << '='; break; + case ALIGN_BOTTOM: qtf << 'v'; break; + } + FmtNumber2(qtf, 'l', cf.border.left, f.border.left, cf.margin.left, f.margin.left); + FmtNumber2(qtf, 'r', cf.border.right, f.border.right, cf.margin.right, f.margin.right); + FmtNumber2(qtf, 't', cf.border.top, f.border.top, cf.margin.top, f.margin.top); + FmtNumber2(qtf, 'b', cf.border.bottom, f.border.bottom, cf.margin.bottom, f.margin.bottom); + FmtNumber(qtf, 'H', cf.minheight, f.minheight); + if(f.color != cf.color) + qtf << '@' << FmtColor(f.color); + if(f.bordercolor != cf.bordercolor) + qtf << 'R' << FmtColor(f.bordercolor); + cf = f; + if(c.hspan) + qtf << '-' << c.hspan; + if(c.vspan) + qtf << '|' << c.vspan; + if(f.keep) + qtf << "k"; + qtf << ' '; + QTFEncodeTxt(qtf, c.text, styles, defstyle, options, sm, charset, lang); + } + } + qtf << "}}"; + } + else { + RichPara p = text.Get(i, styles); + int si = sm.Find(text.GetParaStyle(i)); + if(!(options & QTF_NOSTYLES)) + qtf << "s" << si; + SeparateNumber(qtf); + const RichStyle& s = si < 0 ? defstyle : GetStyle(styles, sm[si]); + QTFEncodePara(qtf, p, options & QTF_NOSTYLES ? defstyle.format : s.format, charset, lang, + (options & QTF_CRLF)); + } + } + qtf << ']'; +} + +void init_s_nodeqtf() +{ + ONCELOCK { + for(int c = 1; c < 128; c++) + s_nodeqtf[c] = IsAlNum(c) || strchr(".,;!?%()/<># ", c); + } +} + +String AsQTF(const RichText& text, byte charset, dword options) +{ + int i; + String qtf; + + init_s_nodeqtf(); + + RichPara::Format dpf; + dpf.Face(Font::ARIAL); + dpf.Height(100); + dpf.language = LNG_ENGLISH; + + bool crlf = options & QTF_CRLF; + + Index sm; +// if(options & QTF_ALL_STYLES) //!!!! problem -> GetUsedStyles!!!! + for(i = 0; i < text.GetStyleCount(); i++) + sm.FindAdd(text.GetStyleId(i)); +// else +// for(i = 0; i < text.GetPartCount(); i++) +// sm.FindAdd(text.GetParaStyle(i)); + + if(!(options & QTF_NOSTYLES)) + for(i = 0; i < sm.GetCount(); i++) { + Uuid id = sm[i]; + const RichStyle& s = text.GetStyle(id); + qtf << '['; + QTFEncodeParaFormat(qtf, s.format, dpf); + CharFmt(qtf, dpf, s.format); + qtf << ' '; + qtf << "$$" << i << ',' << max(sm.Find(s.next), 0) + << '#' << Format(id) + << ':' << DeQtf(s.name) << "]"; + if(crlf) + qtf << "\r\n"; + } + + if(text.GetPartCount() && (options & QTF_BODY)) { + VectorMap lngc; + int m = min(text.GetPartCount(), 30); + for(int i = 0; i < m; i++) + if(text.IsPara(i)) { + RichPara p = text.Get(i); + lngc.GetAdd(p.format.language, 0)++; + } + dword lang = lngc.GetCount() ? lngc.GetKey(MaxIndex(lngc.GetValues())) : 0; + qtf << "["; + if(!(options & QTF_NOCHARSET)) { + qtf << "{"; + if(charset == CHARSET_UTF8) + qtf << "_"; + else + if(charset >= CHARSET_WIN1250 && charset <= CHARSET_WIN1258) + qtf << (char)('0' + charset - CHARSET_WIN1250); + else + if(charset >= CHARSET_ISO8859_1 && charset <= CHARSET_ISO8859_16) + qtf << (char)('A' + charset - CHARSET_ISO8859_1); + else + qtf << CharsetName(charset); + qtf << "}"; + } + if(lang && !(options & QTF_NOLANG)) + qtf << "%" << LNGAsText(SetLNGCharset(lang, CHARSET_DEFAULT)); + qtf << " "; + if(crlf) + qtf << "\r\n"; + RichStyle defstyle; + defstyle.format.Height(100); + + QTFEncodeTxt(qtf, text, text.GetStyles(), defstyle, options, sm, charset, lang); + } + return qtf; +} + +String DeQtf(const char *s) { + String r; + r.Reserve(256); + while(*s) { + if((byte)*s > ' ' && !IsDigit(*s) && !IsAlpha(*s) && (byte)*s < 128) + r.Cat('`'); + r.Cat(*s++); + } + return r; +} + +String DeQtfLf(const char *s) { + String r; + r.Reserve(256); + while(*s) { + if((byte)*s > ' ' && !IsDigit(*s) && !IsAlpha(*s) && (byte)*s < 128) + r.Cat('`'); + if((byte)*s >= ' ') + r.Cat(*s); + else + if(*s == '\n') + r.Cat('&'); + s++; + } + return r; +} + +String AsQTF(const RichObject& obj) +{ + RichText x; + RichPara p; + RichPara::Format fmt; + p.Cat(obj, fmt); + x.Cat(p); + return AsQTF(x); +} + +END_UPP_NAMESPACE diff --git a/uppdev/RichTextP/EncodeRTF.cpp b/uppdev/RichTextP/EncodeRTF.cpp new file mode 100644 index 000000000..a338af5c6 --- /dev/null +++ b/uppdev/RichTextP/EncodeRTF.cpp @@ -0,0 +1,658 @@ +#include "RichText.h" + +#include + +NAMESPACE_UPP + +static int GetParaHeight(const Array& parts) +{ + int ht = 0; + for(int i = 0; i < parts.GetCount(); i++) { + int pht = 0; + const RichPara::Part& part = parts[i]; + if(part.object) + pht = part.object.GetSize().cy; + else if(part.field) + pht = GetParaHeight(part.fieldpart); + else + pht = tabs(part.format.GetHeight()); + if(pht > ht) + ht = pht; + } + return ht; +} + +class RTFEncoder { +public: + RTFEncoder(Stream& stream, const RichText& richtext, byte charset); + + void Run(); + +private: + void FacesAddFormat(const RichPara::CharFormat& format); + void GetFaces(); + void GetTxtFaces(const RichTxt& txt); + + void PutHeader(); + void PutDocument(); + void PutTxt(const RichTxt& txt, int nesting, int dot_width); + void PutTable(const RichTable& table, int nesting, int dot_width); + void PutParts(const Array& parts, + RichPara::CharFormat& base, int bpart, int boff, int epart, int eoff); + + void Begin() { stream.Put('{'); } + void Begin(const char *cmd) { Begin(); Command(cmd); } + void Begin(const char *cmd, int param) { Begin(); Command(cmd, param); } + void End() { stream.Put('}'); } + + void Command(const char *cmd); + void Command(const char *cmd, int param); + void Space() { stream.Put(' '); } + void PutText(const char *text); + void PutObject(const RichObject& object); + void PutTabs(const Vector& tabs); + void PutBinHex(const byte *data, int count); + void PutBinHex(const String& s) { PutBinHex(s, s.GetLength()); } + + bool PutParaFormat(const RichPara::Format& pf, const RichPara::Format& difpf); + bool PutCharFormat(const RichPara::CharFormat& cf, const RichPara::CharFormat& difcf, bool pn); + + struct Group; + + friend struct Group; + + struct Group { + Group(RTFEncoder *owner) : owner(owner) { owner->Begin(); } + Group(RTFEncoder *owner, const char *cmd) : owner(owner) { owner->Begin(cmd); } + Group(RTFEncoder *owner, const char *cmd, int param) : owner(owner) { owner->Begin(cmd, param); } + ~Group() { owner->End(); } + RTFEncoder *owner; + }; + +private: + Stream& stream; + const RichText& richtext; + byte charset; + + RichPara::CharFormat charfmt; + RichPara::Format parafmt; + Uuid oldstyle; + int old_ht; + int para_ht; + + Index used_faces; + enum { SYMBOL_INDEX = 1, WINGDINGS_INDEX = 2 }; + VectorMap used_ink, used_paper; + Index phys_colors; + Index styleid; +}; + +void EncodeRTF(Stream& stream, const RichText& richtext, byte charset) +{ + RTFEncoder(stream, richtext, charset).Run(); +} + +String EncodeRTF(const RichText& richtext, byte charset) +{ + StringStream out; + EncodeRTF(out, richtext, charset); + String s = out.GetResult(); + LOG("EncodeRTF <<<<<\n" << s << "\n>>>>> EncodeRTF"); + return s; +} + +String EncodeRTF(const RichText& richtext) +{ + return EncodeRTF(richtext, GetDefaultCharset() == CHARSET_UTF8 ? GetLNGCharset(GetSystemLNG()) + : GetDefaultCharset()); +} + +RTFEncoder::RTFEncoder(Stream& stream, const RichText& richtext, byte charset) +: stream(stream) +, richtext(richtext) +, charset(charset) +{ + for(int i = 0; i < richtext.GetStyleCount(); i++) + styleid.Add(richtext.GetStyleId(i)); +} + +void RTFEncoder::Run() +{ + GetFaces(); + Group docgrp(this, "rtf"); + PutHeader(); + PutDocument(); +} + +void RTFEncoder::FacesAddFormat(const RichPara::CharFormat& format) +{ + used_faces.FindAdd(format.GetFace()); + if(used_ink.Find(format.ink) < 0) { + Color i(format.ink.GetR(), format.ink.GetG(), format.ink.GetB()); + int x = used_ink.Get(i, -1); + if(x < 0) + x = phys_colors.FindAdd(i); + used_ink.Add(format.ink, x); + } + if(used_paper.Find(format.paper) < 0) { + Color p(format.paper.GetR(), format.paper.GetG(), format.paper.GetB()); + int x = used_paper.Get(p, -1); + if(x < 0) + x = phys_colors.FindAdd(p); + used_paper.Add(format.paper, x); + } +} + +void RTFEncoder::GetFaces() +{ + used_faces.Add(Font::ARIAL); // default font + used_faces.Add(Font::SYMBOL); // used for bullets +#ifdef PLATFORM_WIN32 + used_faces.Add(Font::WINGDINGS); // used for bullets +#endif + phys_colors.Add(Null); + used_ink.Add(Null, 0); + used_paper.Add(Null, 0); + used_ink.Add(Black(), 0); + used_paper.Add(White(), 0); + GetTxtFaces(richtext); + for(int i = 0; i < richtext.GetStyleCount(); i++) + FacesAddFormat(richtext.GetStyle(i).format); +} + +void RTFEncoder::GetTxtFaces(const RichTxt& rt) +{ + for(int i = 0; i < rt.GetPartCount(); i++) { + if(rt.IsTable(i)) { + const RichTable& table = rt.GetTable(i); + const RichTable::Format& tfmt = table.GetFormat(); + phys_colors.FindAdd(tfmt.framecolor); + phys_colors.FindAdd(tfmt.gridcolor); + for(int r = 0; r < table.GetRows(); r++) + for(int c = 0; c < table.GetColumns(); c++) { + const RichCell& cell = table.cell[r][c]; + phys_colors.FindAdd(cell.format.color); + phys_colors.FindAdd(cell.format.bordercolor); + GetTxtFaces(cell.text); + } + } + else { + const RichPara& para = rt.Get(i, richtext.GetStyles()); + FacesAddFormat(para.format); + for(int p = 0; p < para.part.GetCount(); p++) { + const RichPara::Part& part = para.part[p]; + if(part.IsText()) + FacesAddFormat(part.format); + } + } + } +} + +void RTFEncoder::Command(const char *cmd) +{ + stream.Put('\\'); + stream.Put(cmd); +} + +void RTFEncoder::Command(const char *cmd, int param) +{ + stream.Put('\\'); + stream.Put(cmd); + stream.Put(IntStr(param)); +} + +void RTFEncoder::PutText(const char *text) +{ + while(*text) { + if(*text == '{' || *text == '}' || *text == '\\') + stream.Put('\\'); + stream.Put(*text++); + } +} + +void RTFEncoder::PutBinHex(const byte *b, int count) +{ + enum { BLOCK = 32 }; + for(int l = count; l > 0; l -= BLOCK) { + stream.PutCrLf(); + for(const byte *e = b + min(l, BLOCK); b < e; b++) { + static const char binhex[] = "0123456789abcdef"; + stream.Put(binhex[*b >> 4]); + stream.Put(binhex[*b & 15]); + } + } +} + +bool RTFEncoder::PutCharFormat(const RichPara::CharFormat& cf, const RichPara::CharFormat& difcf, bool pn) +{ + int64 pos = stream.GetPos(); + int pn2 = (pn ? 0 : 2); + bool f; + int t; + if(cf.GetFace() != difcf.GetFace()) + Command("pnf" + pn2, used_faces.Find(cf.GetFace())); + if((t = DotPoints(2 * tabs(cf.GetHeight()))) != DotPoints(2 * tabs(difcf.GetHeight()))) + Command("pnfs" + pn2, t); + if(!pn && dword(t = cf.sscript) != difcf.sscript) + Command(t == 0 ? "nosupersub" : t == 1 ? "super" : "sub"); + if((f = cf.IsBold()) != difcf.IsBold()) Command((f ? "pnb" : "pnb0") + pn2); + if((f = cf.IsItalic()) != difcf.IsItalic()) Command((f ? "pni" : "pni0") + pn2); + if((f = cf.IsUnderline()) != difcf.IsUnderline()) Command((f ? "pnul" : "pnul0") + pn2); + if((f = cf.IsStrikeout()) != difcf.IsStrikeout()) Command((f ? "pnstrike" : "pnstrike0") + pn2); + if((f = cf.capitals) != difcf.capitals) Command((f ? "pncaps" : "pncaps0") + pn2); + if((t = used_ink.Get(cf.ink)) != used_ink.Get(difcf.ink)) Command("pncf" + pn2, t); + if((t = used_paper.Get(cf.paper)) != used_paper.Get(difcf.paper)) Command("pncb" + pn2, t); +#ifdef PLATFORM_WIN32 //zapoznamkoval Fidler kdyz chtel zkompilovat pod Linuxem... + if(!pn && cf.language != difcf.language) Command("lang", GetLanguageLCID(cf.language)); +#endif + // todo: link + return stream.GetPos() != pos; +} + +void RTFEncoder::PutObject(const RichObject& object) +{ +#ifdef PLATFORM_WIN32 +#ifndef PLATFORM_WINCE + Size log_size = object.GetPixelSize(), out_size = object.GetSize(); + if(log_size.cx <= 0 || log_size.cy <= 0) log_size = out_size; +// Size scale = out_size * 100 / log_size; + Group pict_grp(this, "pict"); + Command("picw", log_size.cx); + Command("pich", log_size.cy); + Command("picwgoal", DotTwips(out_size.cx)); + Command("pichgoal", DotTwips(out_size.cy)); +// Command("picscalex", scale.cx); +// Command("picscaley", scale.cy); + + if(object.GetTypeName() == "PING") { + Command("pngblip"); + PutBinHex(object.GetData()); + } + else { + Command("wmetafile", 8); + WinMetaFileDraw wmd(log_size.cx, log_size.cy); + object.Paint(wmd, log_size); + WinMetaFile wmf = wmd.Close(); + HENHMETAFILE hemf = wmf.GetHEMF(); + int size = GetWinMetaFileBits(hemf, 0, 0, MM_ANISOTROPIC, ScreenHDC()); + if(size > 0) { + Buffer buffer(size); + GetWinMetaFileBits(hemf, size, buffer, MM_ANISOTROPIC, ScreenHDC()); + PutBinHex(buffer, size); + } + } +#endif +#endif +} + +bool RTFEncoder::PutParaFormat(const RichPara::Format& pf, const RichPara::Format& difpf) +{ + int64 pos = stream.GetPos(); + if(pf.align != difpf.align) + switch(pf.align) { + case ALIGN_NULL: + case ALIGN_LEFT: Command("ql"); break; + case ALIGN_CENTER: Command("qc"); break; + case ALIGN_RIGHT: Command("qr"); break; + case ALIGN_JUSTIFY: Command("qj"); break; + default: NEVER(); + } + int oind = difpf.indent, olm = difpf.lm; + if(difpf.bullet != RichPara::BULLET_NONE) { + olm += oind; + oind = -oind; + } + int nind = pf.indent, nlm = pf.lm; + if(pf.bullet != RichPara::BULLET_NONE) { + nlm += nind; + nind = -nind; + } + if(nind != oind) Command("fi", DotTwips(nind)); + if(nlm != olm) Command("li", DotTwips(nlm)); + if(pf.rm != difpf.rm) Command("ri", DotTwips(pf.rm)); + if(pf.newpage) + Command("pagebb"); + if(pf.before != difpf.before) Command("sb", DotTwips(pf.before)); + if(pf.after != difpf.after) Command("sa", DotTwips(pf.after)); + if(pf.tab != difpf.tab) PutTabs(pf.tab); + return stream.GetPos() != pos; +} + +void RTFEncoder::PutTabs(const Vector& tabs) +{ + for(int i = 0; i < tabs.GetCount(); i++) { + RichPara::Tab t = tabs[i]; + switch(t.align) { + case ALIGN_NULL: + case ALIGN_LEFT: break; + case ALIGN_CENTER: Command("tqc"); break; + case ALIGN_RIGHT: Command("tqr"); break; + default: NEVER(); + } + switch(t.fillchar) { + case 0: break; + case 1: Command("tldot"); break; + case 2: Command("tlhyph"); break; + case 3: Command("tlul"); break; + default: NEVER(); + } + Command("tx", DotTwips(t.pos)); + } +} + +void RTFEncoder::PutParts(const Array& parts, + RichPara::CharFormat& base, int bpart, int boff, int epart, int eoff) +{ + if(epart >= parts.GetCount()) { + epart = parts.GetCount() - 1; + eoff = INT_MAX; + } + ASSERT(boff >= 0 && bpart >= 0); + for(int p = bpart; p <= epart; p++) { + const RichPara::Part& part = parts[p]; + if(part.IsText()) { + WString px = part.text; + if(p == epart && px.GetLength() > eoff) + px.Trim(eoff); + if(p == bpart) + px.Remove(0, boff); + if(!px.IsEmpty()) { + if(PutCharFormat(part.format, base, false) || p == 0) + Space(); + base = part.format; + PutText(FromUnicode(px, charset)); + } + } + else if(part.object) + PutObject(part.object); + } + +} + +void RTFEncoder::PutHeader() +{ + static const short ansicpg[][2] = { + { CHARSET_WIN1250, 1250 }, + { CHARSET_WIN1251, 1251 }, + { CHARSET_WIN1252, 1252 }, + { CHARSET_WIN1253, 1253 }, + { CHARSET_WIN1254, 1254 }, + { CHARSET_WIN1255, 1255 }, + { CHARSET_WIN1256, 1256 }, + { CHARSET_WIN1257, 1257 }, + { CHARSET_WIN1258, 1258 }, + }; + int i; + for(i = __countof(ansicpg); --i >= 0;) + if(charset == ansicpg[i][0]) { + Command("ansicpg", ansicpg[i][1]); + break; + } + + Command("deff", 0); + { + Group ftbl(this, "fonttbl"); + for(int i = 0; i < used_faces.GetCount(); i++) { + Group fnt(this, "f", i); + int fn = used_faces[i]; + dword info = Font::GetFaceInfo(fn); + if(info & Font::SYMBOLTYPE) + Command("ftech"); + else if(info & Font::FIXEDPITCH) + Command("fmodern"); + else if(fn == Font::ROMAN) + Command("froman"); + else if(fn == Font::ARIAL +#ifdef PLATFORM_WIN32 + || fn == Font::TAHOMA +#endif + ) + Command("fswiss"); + Space(); + stream.Put(Font::GetFaceName(fn)); + stream.Put(';'); + } + } + + if(phys_colors.GetCount() > 1) { + Group ctbl(this, "colortbl"); + stream.Put(';'); + for(int i = 1; i < phys_colors.GetCount(); i++) { + Color rgb = phys_colors[i]; + Command("red", rgb.GetR()); + Command("green", rgb.GetG()); + Command("blue", rgb.GetB()); + stream.Put(';'); + } + } + + Begin("stylesheet"); + RichPara::CharFormat empcfmt; + RichPara::Format emppfmt; + + for(i = 0; i < richtext.GetStyleCount(); i++) { + const RichStyle& style = richtext.GetStyle(i); + Begin("s", i); + PutParaFormat(style.format, emppfmt); + PutCharFormat(style.format, empcfmt, false); + Command("sbasedon", 222); + if(style.next != richtext.GetStyleId(i)) + Command("snext", styleid.Find(style.next)); + Space(); + for(const char *n = style.name; *n; n++) + stream.Put(*n == ';' ? '_' : *n); + stream.Put(';'); + End(); + } + End(); +} + +void RTFEncoder::PutTable(const RichTable& table, int nesting, int dot_width) +{ + Vector vspan_counts; + const RichTable::Format& tfmt = table.GetFormat(); + Vector column_pos; + column_pos.SetCount(tfmt.column.GetCount() + 1); + column_pos[0] = tfmt.lm; + int sum = 0; + for(int c = 0; c < tfmt.column.GetCount(); c++) + sum += tfmt.column[c]; + int rem_width = dot_width - tfmt.lm - tfmt.rm; + for(int c = 0; c < tfmt.column.GetCount(); c++) { + int part = iscale(tfmt.column[c], rem_width, max(sum, 1)); + sum -= tfmt.column[c]; + rem_width -= part; + column_pos[c + 1] = column_pos[c] + part; + } + for(int r = 0; r < table.GetRows(); r++) { + String rowfmt; + if(nesting) + rowfmt << "\\*\\nesttableprops"; + rowfmt << "\\trowd" + "\\trleft" << DotTwips(tfmt.lm) + // trbrdr[ltrbv] + ; + Vector cellindex; + Rect dflt_margin(600, 600, 600, 600); + for(int c = 0; c < table.GetColumns(); c++) { + const RichCell& cell = table.cell[r][c]; + dflt_margin.left = min(dflt_margin.left, cell.format.margin.left); + dflt_margin.top = min(dflt_margin.top, cell.format.margin.top); + dflt_margin.right = min(dflt_margin.right, cell.format.margin.right); + dflt_margin.bottom = min(dflt_margin.bottom, cell.format.margin.bottom); + c += cell.hspan; + } +/* + rowfmt << + "\\trpaddl" << DotTwips(dflt_margin.left) << "\\trpaddfl3" + "\\trpaddt" << DotTwips(dflt_margin.top) << "\\trpaddft3" + "\\trpaddr" << DotTwips(dflt_margin.right) << "\\trpaddfr3" + "\\trpaddb" << DotTwips(dflt_margin.bottom) << "\\trpaddfb3"; +*/ + for(int c = 0; c < table.GetColumns(); c++) { + const RichCell& cell = table.cell[r][c]; +/* + if(cell.format.margin.left != dflt_margin.left) + rowfmt << "\\clpadl" << DotTwips(cell.format.margin.left) << "\\clpadfl3"; + if(cell.format.margin.top != dflt_margin.top) + rowfmt << "\\clpadt" << DotTwips(cell.format.margin.top) << "\\clpadft3"; + if(cell.format.margin.right != dflt_margin.right) + rowfmt << "\\clpadr" << DotTwips(cell.format.margin.right) << "\\clpadfr3"; + if(cell.format.margin.bottom != dflt_margin.bottom) + rowfmt << "\\clpadb" << DotTwips(cell.format.margin.bottom) << "\\clpadfb3"; +*/ + switch(cell.format.align) { + case ALIGN_TOP: rowfmt << "\\clvertalt"; break; + default: + case ALIGN_CENTER: rowfmt << "\\clvertalc"; break; + case ALIGN_BOTTOM: rowfmt << "\\clvertalb"; break; + } + cellindex.Add(c); + if(cell.hspan) + c += cell.hspan; + int cell_end = column_pos[c + 1]; + if(cell.vspan) { + vspan_counts.At(c, 0) = cell.vspan; + rowfmt << "\\clvmgf"; + } + else if(c < vspan_counts.GetCount() && vspan_counts[c] > 0) + rowfmt << "\\clvmrg"; + if(!IsNull(cell.format.color)) + rowfmt << "\\clcbpat" << phys_colors.Find(cell.format.color); + rowfmt << "\\cellx" << DotTwips(cell_end); + } + Begin(); + Command("pard"); + Command("intbl"); + parafmt = RichPara::Format(); + charfmt = RichPara::CharFormat(); + if(nesting) + Command("itap", nesting + 1); + if(!nesting) + stream.Put(rowfmt); + for(int c = 0; c < cellindex.GetCount(); c++) { + int cx = cellindex[c]; + const RichCell& cell = table.cell[r][cx]; + int cell_wd = column_pos[cx + cell.hspan + 1] - column_pos[cx]; + PutTxt(cell.text, nesting + 1, cell_wd); + Command(nesting ? "nestcell" : "cell"); + } + stream.Put(rowfmt); + Command(nesting ? "nestrow" : "row"); + End(); + } +} + +void RTFEncoder::PutTxt(const RichTxt& txt, int nesting, int dot_width) +{ + for(int i = 0; i < txt.GetPartCount(); i++) { + if(txt.IsTable(i)) + PutTable(txt.GetTable(i), nesting, dot_width); + else { + const RichPara& para = txt.Get(i, richtext.GetStyles()); + Uuid pstyle = txt.GetParaStyle(i); + int para_ht = GetParaHeight(para.part); + int first_part = 0, first_ind = 0; + bool hdiff = (para.format.bullet != RichPara::BULLET_NONE + && para.format.bullet != RichPara::BULLET_TEXT && para_ht != para_ht); + if(pstyle != oldstyle + || para.format.bullet != parafmt.bullet || para.format.bullet == RichPara::BULLET_TEXT + || hdiff || para.format.tab != parafmt.tab || para.format.newpage || parafmt.newpage) { + Command("s", styleid.Find(pstyle)); + oldstyle = pstyle; + parafmt = richtext.GetStyle(pstyle).format; + Command("pard"); + if(nesting) + Command("intbl"); + if(nesting > 1) + Command("itap", nesting); + parafmt = RichPara::Format(); + para_ht = 0; + } + if(para.format.bullet == RichPara::BULLET_TEXT) { + int epart = 0, eoff = 0; + while(epart < para.part.GetCount() && (eoff = para.part[epart].text.Find('\t')) < 0) + epart++; + + RichPara::CharFormat rcf = charfmt; + rcf.Height(0); + Begin("pntext"); + PutParts(para.part, rcf, 0, 0, epart, eoff); + stream.Put('\t'); + End(); + + Begin("*\\pn"); + Command("pnhang"); + Command("pnql"); + rcf = charfmt; + if(epart > 0 || eoff > 0) + PutCharFormat(para.part[0].format, rcf, true); + Begin("pntxta"); + End(); + Begin("pntxtb"); + Space(); + PutParts(para.part, rcf, 0, 0, epart, eoff); + stream.Put('\t'); + End(); + End(); + } + else if(para.format.bullet != RichPara::BULLET_NONE) { + int sym_face = SYMBOL_INDEX; + byte sym_char = 0xB7; + + switch(para.format.bullet) { + default: + NEVER(); + + case RichPara::BULLET_ROUND: sym_face = SYMBOL_INDEX; sym_char = 0xB7; break; + case RichPara::BULLET_ROUNDWHITE: sym_face = WINGDINGS_INDEX; sym_char = 0xA1; break; + case RichPara::BULLET_BOX: sym_face = WINGDINGS_INDEX; sym_char = 'n'; break; + case RichPara::BULLET_BOXWHITE: sym_face = WINGDINGS_INDEX; sym_char = 'o'; break; + } + + Begin("pntext"); + // Command("tqr"); + // Command("tx", DotTwips(para.format.indent)); + // Command("qr"); + Command("f", sym_face); + Command("fs", DotPoints(2 * para_ht)); + Space(); + stream.Put(sym_char); + stream.Put('\t'); + End(); + + Begin("*\\pn"); + Command("pnlvlblt"); + Command("pnf", sym_face); + Command("pnfs", DotPoints(2 * para_ht)); + Command("pnql"); + Begin("pntxtb"); + Space(); + stream.Put(sym_char); + // stream.Put('\t'); + End(); + Begin("pntxta"); + End(); + End(); + } + PutParaFormat(para.format, parafmt); + para_ht = para_ht; + parafmt = para.format; + PutParts(para.part, charfmt, first_part, first_ind, para.part.GetCount(), 0); + if(i + 1 < txt.GetPartCount()) + Command("par"); + } + } +} + +void RTFEncoder::PutDocument() +{ + charfmt.Height(PointDots(0)); + charfmt.language = 0; + old_ht = DotPoints(2 * tabs(charfmt.GetHeight())); + para_ht = 0; + oldstyle = Uuid::Create(); + PutTxt(richtext, 0, 3600); +} + +END_UPP_NAMESPACE diff --git a/uppdev/RichTextP/Format.cpp b/uppdev/RichTextP/Format.cpp new file mode 100644 index 000000000..05033ae0c --- /dev/null +++ b/uppdev/RichTextP/Format.cpp @@ -0,0 +1,212 @@ +#include "RichText.h" + +NAMESPACE_UPP + +void RichTxt::FormatInfo::Set(const RichPara::Format& fmt) +{ + (RichPara::Format&)*this = fmt; + charvalid = paravalid = 0xffffffff; +} + +void RichTxt::FormatInfo::Set(const RichPara::CharFormat& fmt) +{ + (RichPara::CharFormat&)*this = fmt; + charvalid = 0xffffffff; +} + +void RichTxt::FormatInfo::Combine(const RichPara::CharFormat& fmt) +{ + if(IsBold() != fmt.IsBold()) { + charvalid &= ~BOLD; + NoBold(); + } + if(IsItalic() != fmt.IsItalic()) { + charvalid &= ~ITALIC; + NoItalic(); + } + if(IsUnderline() != fmt.IsUnderline()) { + charvalid &= ~UNDERLINE; + NoUnderline(); + } + if(IsStrikeout() != fmt.IsStrikeout()) { + charvalid &= ~STRIKEOUT; + NoStrikeout(); + } + if(IsNonAntiAliased() != fmt.IsNonAntiAliased()) { + charvalid &= ~NOAA; + NoNonAntiAliased(); + } + if(capitals != fmt.capitals) { + charvalid &= ~CAPITALS; + capitals = false; + } + if(dashed != fmt.dashed) { + charvalid &= ~DASHED; + dashed = false; + } + if(sscript != fmt.sscript) { + charvalid &= ~SSCRIPT; + sscript = 0; + } + if(GetFace() != fmt.GetFace()) + charvalid &= ~FACE; + if(GetHeight() != fmt.GetHeight()) + charvalid &= ~HEIGHT; + if(ink != fmt.ink) { + charvalid &= ~INK; + ink = Null; + } + if(paper != fmt.paper) { + charvalid &= ~PAPER; + paper = Null; + } + if(language != fmt.language) { + charvalid &= ~LANG; + language = 0; + } + if(link != fmt.link) { + charvalid &= ~LINK; + link = Null; + } + if(indexentry != fmt.indexentry) { + charvalid &= ~INDEXENTRY; + indexentry = Null; + } +} + +void RichTxt::FormatInfo::Combine(const RichPara::Format& fmt) +{ + if(align != fmt.align) + paravalid &= ~ALIGN; + if(before != fmt.before) + paravalid &= ~BEFORE; + if(lm != fmt.lm) + paravalid &= ~LM; + if(indent != fmt.indent) + paravalid &= ~INDENT; + if(rm != fmt.rm) + paravalid &= ~RM; + if(after != fmt.after) + paravalid &= ~AFTER; + if(tabsize != fmt.tabsize) + paravalid &= ~TABSIZE; + if(bullet != fmt.bullet) + paravalid &= ~BULLET; + if(newpage != fmt.newpage) + paravalid &= ~NEWPAGE; + if(keep != fmt.keep) + paravalid &= ~KEEP; + if(keepnext != fmt.keepnext) + paravalid &= ~KEEPNEXT; + if(orphan != fmt.orphan) + paravalid &= ~ORPHAN; + if(label != fmt.label) + paravalid &= ~LABEL; + if(NumberingDiffers(*this, fmt)) + paravalid &= ~NUMBERING; + if(tab.GetCount() != fmt.tab.GetCount()) { + paravalid &= ~TABS; + tab.Clear(); + } + else + for(int i = 0; i < tab.GetCount(); i++) + if(tab[i].pos != fmt.tab[i].pos || tab[i].align != fmt.tab[i].align || + tab[i].fillchar != fmt.tab[i].fillchar) { + paravalid &= ~TABS; + tab.Clear(); + break; + } + if(styleid != fmt.styleid) + paravalid &= ~STYLE; + if(linespacing != fmt.linespacing) + paravalid &= ~SPACING; + if(ruler != fmt.ruler) + paravalid &= ~RULER; + if(rulerink != fmt.rulerink) + paravalid &= ~RULERINK; +} + +void RichTxt::FormatInfo::ApplyTo(RichPara::CharFormat& fmt) const +{ + if(charvalid & BOLD) + fmt.Bold(IsBold()); + if(charvalid & ITALIC) + fmt.Italic(IsItalic()); + if(charvalid & UNDERLINE) + fmt.Underline(IsUnderline()); + if(charvalid & STRIKEOUT) + fmt.Strikeout(IsStrikeout()); + if(charvalid & NOAA) + fmt.NonAntiAliased(IsNonAntiAliased()); + if(charvalid & CAPITALS) + fmt.capitals = capitals; + if(charvalid & DASHED) + fmt.dashed = dashed; + if(charvalid & SSCRIPT) + fmt.sscript = sscript; + if(charvalid & FACE) + fmt.Face(GetFace()); + if(charvalid & HEIGHT) + fmt.Height(GetHeight()); + if(charvalid & LANGUAGE) + fmt.language = language; + if(charvalid & INK) + fmt.ink = ink; + if(charvalid & PAPER) + fmt.paper = paper; + if(charvalid & LANG) + fmt.language = language; + if(charvalid & LINK) + fmt.link = link; + if(charvalid & INDEXENTRY) + fmt.indexentry = indexentry; +} + +void RichTxt::FormatInfo::ApplyTo(RichPara::Format& fmt) const +{ + ApplyTo((RichPara::CharFormat &)fmt); + if(paravalid & ALIGN) + fmt.align = align; + if(paravalid & RULER) + fmt.ruler = ruler; + if(paravalid & BEFORE) + fmt.before = before; + if(paravalid & LM) + fmt.lm = lm; + if(paravalid & INDENT) + fmt.indent = indent; + if(paravalid & RM) + fmt.rm = rm; + if(paravalid & AFTER) + fmt.after = after; + if(paravalid & TABSIZE) + fmt.tabsize = tabsize; + if(paravalid & BULLET) + fmt.bullet = bullet; + if(paravalid & NEWPAGE) + fmt.newpage = newpage; + if(paravalid & KEEP) + fmt.keep = keep; + if(paravalid & KEEPNEXT) + fmt.keepnext = keepnext; + if(paravalid & ORPHAN) + fmt.orphan = orphan; + if(paravalid & LABEL) + fmt.label = label; + if(paravalid & NUMBERING) { + fmt.before_number = before_number; + fmt.after_number = after_number; + fmt.reset_number = reset_number; + memcpy(fmt.number, number, sizeof(number)); + } +// if(paravalid & TABS) +// fmt.tab = tab; + if(paravalid & STYLE) + fmt.styleid = styleid; + if(paravalid & SPACING) + fmt.linespacing = linespacing; + if(paravalid & RULERINK) + fmt.rulerink = rulerink; +} + +END_UPP_NAMESPACE diff --git a/uppdev/RichTextP/Object.cpp b/uppdev/RichTextP/Object.cpp new file mode 100644 index 000000000..d5c2ad269 --- /dev/null +++ b/uppdev/RichTextP/Object.cpp @@ -0,0 +1,401 @@ +#include "RichText.h" + +NAMESPACE_UPP + +RichObjectType::RichObjectType() {} +RichObjectType::~RichObjectType() {} + +String RichObjectType::GetCreateName() const +{ + return Null; +} + +Size RichObjectType::GetDefaultSize(const Value& data, Size maxsize) const +{ + if(IsNull(data)) return Size(0, 0); + Size psz = GetPhysicalSize(data); + if((psz.cx | psz.cy) == 0) + psz = 625 * GetPixelSize(data) / 100; + Size sz; + for(int i = 1; i < 10000; i++) { + sz = psz / i; + if(sz.cx <= maxsize.cx && sz.cy <= maxsize.cy) + break; + } + return sz; +} + +Value RichObjectType::Read(const String& s) const +{ + return s; +} + +String RichObjectType::Write(const Value& v) const +{ + return v; +} + +bool RichObjectType::Accept(PasteClip& clip) +{ + return false; +} + +Value RichObjectType::Read(PasteClip& clip) +{ + return Null; +} + +String RichObjectType::GetClipFmts() const +{ + return Null; +} + +String RichObjectType::GetClip(const Value& data, const String& fmt) const +{ + return Null; +} + +void RichObjectType::Menu(Bar& bar, RichObject& data) const {} +void RichObjectType::DefaultAction(RichObject& data) const {} +Size RichObjectType::GetPhysicalSize(const Value& data) const { return Size(0, 0); } +Size RichObjectType::GetPixelSize(const Value& data) const { return Size(1, 1); } +void RichObjectType::Paint(const Value& data, Draw& w, Size sz) const {} + +Image RichObjectType::ToImage(const Value& data, Size sz) const +{ + ImageDraw w(sz); + Paint(data, w, sz); + return w; +} + +String RichObjectType::GetLink(const Value& data, Point pt, Size sz) const +{ + return Null; +} + +void RichObject::InitSize(int cx, int cy) +{ + Size sz; + Size phsz = GetPixelSize(); + if(cx || cy) + sz = GetRatioSize(phsz, cx, cy); + else + sz = phsz; + if(sz.cx > 2000 || sz.cy > 2000) + sz = sz.cx > sz.cy ? GetRatioSize(phsz, 2000, 0) + : GetRatioSize(phsz, 0, 2000); + SetSize(sz); +} + +typedef VectorMap RichObjectHT; + +GLOBAL_VAR(RichObjectHT, RichObject::Map); + +void RichObject::NewSerial() +{ + INTERLOCKED { + static int64 s; + serial = ++s; + } +} + +void RichObject::Register(const char *name, RichObjectType *type) +{ + AssertST(); + Map().FindAdd(name, type); +} + +void RichObject::Paint(Draw& w, Size sz) const +{ + if(type) + type->Paint(data, w, sz); + else { + w.DrawRect(sz, SColorFace()); + DrawFrame(w, sz, SColorText()); + w.DrawText(2, 2, type_name); + } +} + +Image RichObject::ToImage(Size sz) const +{ + if(type) + return type->ToImage(data, sz); + else { + ImageDraw w(sz); + Paint(w, sz); + return w; + } +} + +struct UnknownRichObject : RichObjectType { + virtual String GetTypeName(const Value&) const; +}; + +String UnknownRichObject::GetTypeName(const Value&) const +{ + return Null; +} + +const RichObjectType& RichObject::GetType() const +{ + if(type) + return *type; + return Single(); +} + +void RichObject::Set(RichObjectType *_type, const Value& _data, Size maxsize) +{ + Clear(); + type = _type; + if(type) { + data = _data; + physical_size = type->GetPhysicalSize(data); + pixel_size = type->GetPixelSize(data); + size = type->GetDefaultSize(data, maxsize); + } + NewSerial(); +} + +bool RichObject::Set(const String& _type_name, const Value& _data, Size maxsize) +{ + NewSerial(); + type_name = _type_name; + RichObjectType *t = Map().Get(type_name, NULL); + if(t) { + Set(t, _data); + return true; + } + else { + Clear(); + data = _data; + physical_size = pixel_size = size = Size(64, 64); + } + return false; +} + +bool RichObject::Read(const String& _type_name, const String& _data, Size sz) +{ + NewSerial(); + type_name = _type_name; + RichObjectType *t = Map().Get(type_name, NULL); + if(t) { + Clear(); + type = t; + data = type->Read(_data); + physical_size = type->GetPhysicalSize(data); + pixel_size = type->GetPixelSize(data); + size = sz; + return true; + } + data = _data; + physical_size = pixel_size = size = sz; + return false; +} + +String RichObject::GetTypeName() const +{ + return type ? type->GetTypeName(data) : type_name; +} + +void RichObject::Clear() +{ + NewSerial(); + keepratio = true; + type = NULL; + data = Value(); + size = physical_size = pixel_size = Size(0, 0); + ydelta = 0; + type_name.Clear(); +} + +RichObject::RichObject() +{ + Clear(); +} + +RichObject::RichObject(RichObjectType *type, const Value& data, Size maxsize) +{ + Set(type, data, maxsize); +} + +RichObject::RichObject(const String& type, const Value& data, Size maxsize) +{ + Set(type, data, maxsize); +} + +struct RichObjectTypeDrawingCls : public RichObjectType +{ + virtual String GetTypeName(const Value&) const; + virtual Size GetPhysicalSize(const Value& data) const; + virtual Size GetPixelSize(const Value& data) const; + virtual void Paint(const Value& data, Draw& w, Size sz) const; + virtual Value Read(const String& s) const; + virtual String Write(const Value& v) const; + + struct Data + { + void Serialize(Stream& stream); + + Drawing drawing; + Size dot_size; + }; +}; + +RichObjectType *RichObjectTypeDrawing() { return &Single(); } + +void RichObjectTypeDrawingCls::Data::Serialize(Stream& stream) +{ + int version = 1; + stream % version % dot_size % drawing; +} + +String RichObjectTypeDrawingCls::GetTypeName(const Value&) const +{ + return "Drawing"; +} + +RichObject CreateDrawingObject(const Drawing& dwg, Size dot_size, Size out_size) +{ + RichObjectTypeDrawingCls::Data data; + data.drawing = dwg; + data.dot_size = dot_size; + RichObject obj(RichObjectTypeDrawing(), RawToValue(data)); + if(!IsNull(out_size)) + obj.SetSize(out_size); + return obj; +} + +RichObject CreateDrawingObject(const Drawing& dwg, int cx, int cy) +{ + Size dsz = dwg.GetSize(); + return CreateDrawingObject(dwg, dsz, cx || cy ? GetRatioSize(dsz, cx, cy) : dsz); +} + +Size RichObjectTypeDrawingCls::GetPixelSize(const Value& data) const +{ + if(IsTypeRaw(data)) + return ValueTo(data).drawing.GetSize(); // dot_size; TRC 08/04/04 + return Size(0, 0); +} + +Size RichObjectTypeDrawingCls::GetPhysicalSize(const Value& data) const +{ + if(IsTypeRaw(data)) + return ValueTo(data).dot_size; + return Size(0, 0); +} + +Value RichObjectTypeDrawingCls::Read(const String& s) const +{ + Data data; + StringStream ss(s); + ss % data; + if(ss.IsError()) + return Value(); + return RawToValue(data); +} + +String RichObjectTypeDrawingCls::Write(const Value& v) const +{ + if(IsTypeRaw(v)) + { + StringStream ss; + ss % const_cast(ValueTo(v)); + return ss; + } + return Null; +} + +void RichObjectTypeDrawingCls::Paint(const Value& data, Draw& w, Size sz) const +{ + w.DrawRect(sz, White); + if(IsTypeRaw(data)) + w.DrawDrawing(Rect(sz), ValueTo(data).drawing); +} + +INITBLOCK { + RichObject::Register("Drawing", &Single()); +}; + +struct RichObjectTypePNGCls : public RichObjectType +{ + virtual String GetTypeName(const Value&) const; + virtual Size GetPhysicalSize(const Value& data) const; + virtual Size GetPixelSize(const Value& data) const; + virtual void Paint(const Value& data, Draw& w, Size sz) const; + virtual Value Read(const String& s) const; + virtual String Write(const Value& v) const; +}; + +RichObjectType *RichObjectTypePNG() { return &Single(); } + +String RichObjectTypePNGCls::GetTypeName(const Value&) const +{ + return "PING"; +} + +RichObject CreatePNGObject(const Image& img, Size dot_size, Size out_size) +{ + RichObject obj(RichObjectTypePNG(), PNGEncoder().SaveString(img)); + if(!IsNull(out_size)) + obj.SetSize(out_size); + return obj; +} + +RichObject CreatePNGObject(const Image& img, int cx, int cy) +{ + Size dsz = img.GetSize(); + return CreatePNGObject(img, dsz, cx || cy ? GetRatioSize(dsz, cx, cy) : dsz); +} + +Size RichObjectTypePNGCls::GetPixelSize(const Value& data) const +{ + if(IsString(data)) { + StringStream strm(data); + One ras = StreamRaster::OpenAny(strm); + if(!!ras) + return ras->GetSize(); + } + return Size(0, 0); +} + +Size RichObjectTypePNGCls::GetPhysicalSize(const Value& data) const +{ + if(IsString(data)) { + StringStream strm(data); + One ras = StreamRaster::OpenAny(strm); + if(!!ras) + return ras->GetInfo().dots; + } + return Size(0, 0); +} + +Value RichObjectTypePNGCls::Read(const String& s) const +{ + return s; +} + +String RichObjectTypePNGCls::Write(const Value& v) const +{ + return v; +} + +void RichObjectTypePNGCls::Paint(const Value& data, Draw& w, Size sz) const +{ + if(IsString(data)) { + StringStream strm(data); + One ras = StreamRaster::OpenAny(strm); + if(!!ras) { + w.DrawRect(sz, White); + w.DrawImage(Rect(sz), ras->GetImage()); + return; + } + } + w.DrawRect(sz, LtRed()); +} + +INITBLOCK { + RichObject::Register("PING", &Single()); +}; + +END_UPP_NAMESPACE diff --git a/uppdev/RichTextP/Para.h b/uppdev/RichTextP/Para.h new file mode 100644 index 000000000..bea0eaa5a --- /dev/null +++ b/uppdev/RichTextP/Para.h @@ -0,0 +1,294 @@ +struct RichCaret; +struct RichPos; + +struct RichPara { + enum Code { + NONE, + + INDEXENTRY = 2, + FIELD = 3, + FACE = 4, + HEIGHT = 5, + LANGUAGE = 6, + INK = 7, + PAPER = 8, + + TAB = 9, // placeholder + + LINK = 10, + SSCRIPT = 11, + + BOLD0 = 12, + BOLD1 = 13, + BOLDS = 14, + ITALIC0 = 15, + ITALIC1 = 16, + ITALICS = 17, + UNDERLINE0 = 18, + UNDERLINE1 = 19, + UNDERLINES = 20, + CAPITALS0 = 21, + CAPITALS1 = 22, + CAPITALSS = 23, + STRIKEOUT0 = 24, + STRIKEOUT1 = 25, + STRIKEOUTS = 26, + DASHED0 = 27, + DASHED1 = 28, + DASHEDS = 29, + + EXT = 30, + + OBJECT = 31 + }; + + enum ExtCode { + NONAA0 = 1, + NONAA1 = 2, + NONAAS = 3, + }; + + enum BULLET_STYLE { + BULLET_NONE, + BULLET_ROUND, + BULLET_ROUNDWHITE, + BULLET_BOX, + BULLET_BOXWHITE, + BULLET_TEXT + }; + + enum NUMBER_STYLE { + NUMBER_NONE, + NUMBER_1, + NUMBER_0, + NUMBER_a, + NUMBER_A, + NUMBER_i, + NUMBER_I, + }; + + enum LINESPACING { + LSP10 = 0, + LSP15 = -1, + LSP20 = -2, + }; + + struct CharFormat : public Font { + int language; + Color ink, paper; + String link; + WString indexentry; + dword sscript:2; + bool capitals; + bool dashed; + +#ifdef _DEBUG + String ToString() const; +#endif + + CharFormat(); + }; + + struct Tab : Moveable { + int pos; + byte align; + byte fillchar; + + Tab() { align = ALIGN_LEFT; pos = 0; fillchar = 0; } + }; + + struct NumberFormat { + String before_number, after_number; + bool reset_number; + byte number[8]; + + bool IsNumbered() const; + int GetNumberLevel() const; + }; + + struct Format : NumberFormat, CharFormat { + int align; + int ruler, before, lm, indent, rm, after; + Color rulerink; + int tabsize; + int bullet; + int linespacing; + bool newpage, keep, keepnext, orphan; + WithDeepCopy< Vector > tab; + String label; + Uuid styleid; + + void SortTabs(); + +#ifdef _DEBUG + String ToString() const; +#endif + + Format(); + }; + + struct Number { + int n[8]; + + String AsText(const NumberFormat& format) const; + void TestReset(const NumberFormat& format); + void Next(const NumberFormat& format); + + Number(); + }; + + struct Part { + RichObject object; + WString text; + CharFormat format; + Id field; + String fieldparam; + WithDeepCopy< Array > fieldpart; + + bool NonText() const { return object || field; } + bool IsText() const { return !NonText(); } + int GetLength() const { return object || field ? 1 : text.GetLength(); } + }; + + struct FieldType { + virtual Array Evaluate(const String& param, VectorMap& vars, + const CharFormat& fmt) = 0; + virtual void Menu(Bar& bar, String *param) const {} + virtual void DefaultAction(String *param) const {} + virtual ~FieldType() {} + }; + + struct HeightInfo { + int ascent; + int descent; + int external; + int ydelta; + const RichObject *object; + + int Sum() const { return ascent + descent + external; } + }; + + struct Line : public HeightInfo { + int pos; + int len; + int ppos; + int plen; + int xpos; + int cx; + int objecti; + bool withtabs; + }; + + struct Lines { + int len; + int clen; + int cx; + Buffer text; + Buffer spell; + Buffer pos; + Buffer width; + Buffer height; + Buffer format; + Array line; + Array hformat; + int first_indent; + int next_indent; + + + int GetCount() const { return line.GetCount(); } + Line& operator[](int i) { return line[i]; } + const Line& operator[](int i) const { return line[i]; } + void Justify(const Format& format); + int BodyHeight(); + }; + + static const VectorMap& fieldtype(); + static void Register(Id id, FieldType& ft) init_; + + Format format; + Array part; + + static void Charformat(Stream& out, const CharFormat& o, const CharFormat& n, + const CharFormat& s); + + void Cat(const WString& s, const CharFormat& f); + void Cat(const char *s, const CharFormat& f); + void Cat(const RichObject& o, const CharFormat& f); + void Cat(Id fieldtype, const String& param, const CharFormat& f); + + int GetLength() const; + int GetCount() const { return part.GetCount(); } + bool IsEmpty() const; + String Pack(const Format& style, Array& obj) const; + void Unpack(const String& s, const Array& obj, const Format& style); + + void ApplyStyle(const Format& newstyle); + + Part& operator[](int i) { return part[i]; } + const Part& operator[](int i) const { return part[i]; } + int FindPart(int& pos) const; + void Trim(int pos); + void Mid(int pos); + void Append(const RichPara& p) { part.Append(p.part); } + + void GetRichPos(RichPos& rp, int pos) const; + + Lines FormatLines(int cx) const; + void Paint(PageDraw& pw, const Rect& page, PageY py, const PaintInfo& pi, + const Number& n, const Bits& spellerrors, + int nbefore, int nline) const; + RichCaret GetCaret(int pos, const Rect& page, PageY py, int nbefore, int nline) const; + int GetPos(int x, PageY y, const Rect& page, PageY py, int nbefore, int nline) const; + void GatherLabels(Vector& info, const Rect& page, PageY py, + int pos, int nbefore, int nline) const; + void GatherIndexes(Vector& info, const Rect& page, PageY py, + int pos, int nbefore, int nline) const; + int GetVertMove(int pos, int gx, const Rect& page, int dir) const; + + WString GetText() const; + + bool HasPos() const; + + bool EvaluateFields(VectorMap& vars); + + void operator<<=(const RichPara& p) { format = p.format; part <<= p.part; } + +#ifdef _DEBUG + void Dump(); +#endif + + void ApplyZoom(Zoom z); + +private: + Tab GetNextTab(int pos) const; + void Smh(Lines& lines, HeightInfo *th, int cx) const; + Lines Begin(const Rect& page, PageY& py, int nbefore, int nline) const; + bool BreaksPage(PageY py, const Lines& pl, int i, const Rect& page) const; + void PackParts(Stream& out, const CharFormat& chrstyle, + const Array& part, CharFormat& cf, + Array& obj) const; + void UnpackParts(Stream& in, const CharFormat& chrstyle, + Array& part, const Array& obj, int& oi); + static void Flush(Draw& draw, const PaintInfo& pi, wchar *text, const CharFormat **i0, + int *wd, int pos, int len, int x0, int x, int y0, int y, int linecy, + int lineascent, Zoom z, bool highlight); + int PosInLine(int x, const Rect& page, const Lines& pl, int lni) const; + struct StorePart; +}; + +inline bool operator==(const RichPara::Tab& a, const RichPara::Tab& b) { + return a.pos == b.pos && a.align == b.align && a.fillchar == b.fillchar; +} + +bool operator==(const Vector& a, const Vector& b); + +inline +bool operator!=(const Vector& a, const Vector& b) { + return !(a == b); +} + +bool NumberingDiffers(const RichPara::Format& fmt1, const RichPara::Format& fmt2); + +void Sort(Vector& tab); + +void operator*=(RichPara::Format& fmt, Zoom z); diff --git a/uppdev/RichTextP/ParaData.cpp b/uppdev/RichTextP/ParaData.cpp new file mode 100644 index 000000000..1f0a69148 --- /dev/null +++ b/uppdev/RichTextP/ParaData.cpp @@ -0,0 +1,772 @@ +#include "RichText.h" + +NAMESPACE_UPP + +PaintInfo::PaintInfo() +{ + sell = selh = 0; + tablesel = 0; + top = PageY(0, 0); + bottom = PageY(INT_MAX, INT_MAX); + hyperlink = SColorMark(); + usecache = false; + sizetracking = false; + showcodes = Null; + spellingchecker = NULL; + highlightpara = -1; + highlight = Yellow(); + indexentry = LtGreen(); + coloroverride = false; +} + +String RichPara::Number::AsText(const RichPara::NumberFormat& format) const +{ + String result; + for(int i = 0; i < 8; i++) + if(format.number[i]) { + if(result.GetLength()) + result.Cat('.'); + int q = n[i]; + switch(format.number[i]) { + case NUMBER_1: + result << AsString(q); + break; + case NUMBER_0: + result << AsString(q - 1); + break; + case NUMBER_a: + result << FormatIntAlpha(q, false); + break; + case NUMBER_A: + result << FormatIntAlpha(q, true); + break; + case NUMBER_i: + result << FormatIntRoman(q, false); + break; + case NUMBER_I: + result << FormatIntRoman(q, true); + break; + } + } + return format.before_number + result + format.after_number; +} + +void RichPara::Number::TestReset(const RichPara::NumberFormat& fmt) +{ + if(fmt.reset_number) { + bool done = false; + for(int i = 7; i >= 0; --i) + if(fmt.number[i]) { + n[i] = 0; + done = true; + break; + } + if(!done && !IsNull(n[0])) + n[0] = 0; + } +} + +void RichPara::Number::Next(const RichPara::NumberFormat& fmt) +{ + for(int i = 7; i >= 0; --i) + if(fmt.number[i]) { + n[i++]++; + while(i <= 7) + n[i++] = 0; + break; + } +} + +RichPara::Number::Number() +{ + memset(n, 0, sizeof(n)); +} + +bool RichPara::NumberFormat::IsNumbered() const +{ + if(before_number.GetLength() || after_number.GetLength()) + return true; + for(int i = 0; i < 8; i++) + if(number[i]) + return true; + return false; +} + +int RichPara::NumberFormat::GetNumberLevel() const +{ + for(int i = 7; i >= 0; i--) + if(number[i]) + return i + 1; + if(before_number.GetLength() || after_number.GetLength()) + return 0; + return -1; +} + +bool NumberingDiffers(const RichPara::Format& fmt1, const RichPara::Format& fmt2) +{ + return fmt1.before_number != fmt2.before_number || + fmt1.after_number != fmt2.after_number || + fmt1.reset_number != fmt2.reset_number || + memcmp(fmt1.number, fmt2.number, sizeof(fmt1.number)); +} + +bool operator==(const Vector& a, const Vector& b) +{ + if(a.GetCount() != b.GetCount()) return false; + for(int i = 0; i < a.GetCount(); i++) + if(a[i].pos != b[i].pos || a[i].align != b[i].align || a[i].fillchar != b[i].fillchar) + return false; + return true; +} + +RichPara::CharFormat::CharFormat() +{ + (Font &)*this = Arial(100); + ink = Black; + paper = Null; + language = 0; + link = Null; + sscript = 0; + capitals = dashed = false; +} + +RichPara::Format::Format() +{ + align = ALIGN_LEFT; + ruler = before = lm = rm = indent = after = 0; + rulerink = Black; + bullet = 0; + keep = newpage = keepnext = orphan = false; + tabsize = 296; + memset(number, 0, sizeof(number)); + reset_number = false; + linespacing = 0; + tab.Clear(); + styleid = RichStyle::GetDefaultId(); +} + +void RichPara::Charformat(Stream& out, const RichPara::CharFormat& o, + const RichPara::CharFormat& n, const RichPara::CharFormat& s) +{ + if(o.IsBold() != n.IsBold()) + out.Put(n.IsBold() == s.IsBold() ? BOLDS : BOLD0 + n.IsBold()); + if(o.IsItalic() != n.IsItalic()) + out.Put(n.IsItalic() == s.IsItalic() ? ITALICS + : ITALIC0 + n.IsItalic()); + if(o.IsUnderline() != n.IsUnderline()) + out.Put(n.IsUnderline() == s.IsUnderline() ? UNDERLINES + : UNDERLINE0 + n.IsUnderline()); + if(o.IsStrikeout() != n.IsStrikeout()) + out.Put(n.IsStrikeout() == s.IsStrikeout() ? STRIKEOUTS + : STRIKEOUT0 + n.IsStrikeout()); + if(o.IsNonAntiAliased() != n.IsNonAntiAliased()) { + out.Put(EXT); + out.Put(n.IsNonAntiAliased() == s.IsNonAntiAliased() ? NONAAS + : NONAA0 + n.IsNonAntiAliased()); + } + if(o.capitals != n.capitals) + out.Put(n.capitals == s.capitals ? CAPITALSS + : CAPITALS0 + n.capitals); + if(o.dashed != n.dashed) + out.Put(n.dashed == s.dashed ? DASHEDS + : DASHED0 + n.dashed); + if(o.sscript != n.sscript) { + out.Put(SSCRIPT); + out.Put(n.sscript); + } + if(o.GetFace() != n.GetFace()) { + out.Put(FACE); + out.Put16(n.GetFace() == s.GetFace() ? 0xffff : n.GetFace()); + } + if(o.GetHeight() != n.GetHeight()) { + out.Put(HEIGHT); + out.Put16(n.GetHeight() == s.GetHeight() ? 0xffff : n.GetHeight()); + } + if(o.link != n.link) { + out.Put(LINK); + String s = n.link; + out % s; + } + if(o.ink != n.ink) { + out.Put(INK); + Color c = n.ink; + c.Serialize(out); + } + if(o.paper != n.paper) { + out.Put(PAPER); + Color c = n.paper; + c.Serialize(out); + } + if(o.language != n.language) { + out.Put(LANGUAGE); + out.Put32(n.language); + } + if(o.indexentry != n.indexentry) { + out.Put(INDEXENTRY); + WString s = n.indexentry; + out % s; + } +} + +void RichPara::Cat(const WString& s, const RichPara::CharFormat& f) +{ + part.Add(); + part.Top().text = s; + part.Top().format = f; +} + +void RichPara::Cat(const char *s, const CharFormat& f) +{ + Cat(WString(s), f); +} + +void RichPara::Cat(const RichObject& o, const RichPara::CharFormat& f) +{ + part.Add(); + part.Top().object = o; + part.Top().format = f; +} + +void RichPara::Cat(Id field, const String& param, const RichPara::CharFormat& f) +{ + Part& p = part.Add(); + p.field = field; + p.fieldparam = param; + p.format = f; + VectorMap dummy; + FieldType *ft = fieldtype().Get(field, NULL); + if(ft) + p.fieldpart ^= ft->Evaluate(param, dummy, f); +} + +struct TabLess { + bool operator () (const RichPara::Tab& a, const RichPara::Tab& b) const { + return a.pos == b.pos ? a.align < b.align : a.pos < b.pos; + } +}; + +void RichPara::Format::SortTabs() +{ + Sort(tab, TabLess()); +} + +void RichPara::PackParts(Stream& out, const RichPara::CharFormat& chrstyle, + const Array& part, CharFormat& cf, + Array& obj) const +{ + for(int i = 0; i < part.GetCount(); i++) { + const Part& p = part[i]; + Charformat(out, cf, p.format, chrstyle); + cf = p.format; + if(p.field) { + out.Put(FIELD); + out.Put32(p.field.AsNdx()); + String s = p.fieldparam; + out % s; + StringStream oout; + CharFormat subf = cf; + PackParts(oout, chrstyle, p.fieldpart, subf, obj); + s = oout; + out % s; + } + else + if(p.object) { + obj.Add(p.object); + out.Put(OBJECT); + } + else { + String x = ToUtf8(p.text); + out.Put(x); + } + } +} + +String RichPara::Pack(const RichPara::Format& style, Array& obj) const +{ + StringStream out; + dword pattr = 0; + if(format.align != style.align) pattr |= 1; + if(format.before != style.before) pattr |= 2; + if(format.lm != style.lm) pattr |= 4; + if(format.indent != style.indent) pattr |= 8; + if(format.rm != style.rm) pattr |= 0x10; + if(format.after != style.after) pattr |= 0x20; + if(format.bullet != style.bullet) pattr |= 0x40; + if(format.keep != style.keep) pattr |= 0x80; + if(format.newpage != style.newpage) pattr |= 0x100; + if(format.tabsize != style.tabsize) pattr |= 0x200; + if(!IsNull(format.label)) pattr |= 0x400; + if(format.keepnext != style.keepnext) pattr |= 0x800; + if(format.orphan != style.orphan) pattr |= 0x1000; + if(NumberingDiffers(format, style)) pattr |= 0x2000; + if(format.linespacing != style.linespacing) pattr |= 0x4000; + if(format.tab != style.tab) pattr |= 0x8000; + if(format.ruler != style.ruler) pattr |= 0x10000; + if(format.rulerink != style.rulerink) pattr |= 0x20000; + out.Put32(pattr); + if(pattr & 1) out.Put16(format.align); + if(pattr & 2) out.Put16(format.before); + if(pattr & 4) out.Put16(format.lm); + if(pattr & 8) out.Put16(format.indent); + if(pattr & 0x10) out.Put16(format.rm); + if(pattr & 0x20) out.Put16(format.after); + if(pattr & 0x40) out.Put16(format.bullet); + if(pattr & 0x80) out.Put(format.keep); + if(pattr & 0x100) out.Put(format.newpage); + if(pattr & 0x200) out.Put16(format.tabsize); + if(pattr & 0x400) { String t = format.label; out % t; } + if(pattr & 0x800) out.Put(format.keepnext); + if(pattr & 0x1000) out.Put(format.orphan); + if(pattr & 0x2000) { + String b = format.before_number, a = format.after_number; + out % b % a; + out.Put(format.reset_number); + out.Put(format.number, 8); + } + if(pattr & 0x4000) + out.Put(format.linespacing); + if(pattr & 0x8000) { + int c = 0; + int i; + for(i = 0; i < format.tab.GetCount(); i++) { + if(!IsNull(format.tab[i].pos)) + c++; + } + out.Put16(c); + for(i = 0; i < format.tab.GetCount(); i++) { + const RichPara::Tab& w = format.tab[i]; + if(!IsNull(w.pos)) { + out.Put32(w.pos); + out.Put(w.align); + out.Put(w.fillchar); + } + } + } + if(pattr & 0x10000) + out.Put16(format.ruler); + if(pattr & 0x20000) { + Color c = format.rulerink; + c.Serialize(out); + } + obj.Clear(); + CharFormat cf = style; + if(part.GetCount()) + PackParts(out, style, part, cf, obj); + else + Charformat(out, style, format, cf); + String r = out; + r.Shrink(); + return r; +} + +void RichPara::UnpackParts(Stream& in, const RichPara::CharFormat& chrstyle, + Array& part, const Array& obj, + int& oi) { + part.Add(); + part.Top().format = format; + int c; + while((c = in.Term()) >= 0) + if(c < 31 && c != 9 && c != FIELD) { + do + switch(in.Get()) { + case BOLD0: + format.NoBold(); + break; + case BOLD1: + format.Bold(); + break; + case BOLDS: + format.Bold(chrstyle.IsBold()); + break; + case ITALIC0: + format.NoItalic(); + break; + case ITALIC1: + format.Italic(); + break; + case ITALICS: + format.Italic(chrstyle.IsItalic()); + break; + case UNDERLINE0: + format.NoUnderline(); + break; + case UNDERLINE1: + format.Underline(); + break; + case UNDERLINES: + format.Underline(chrstyle.IsUnderline()); + break; + case STRIKEOUT0: + format.NoStrikeout(); + break; + case STRIKEOUT1: + format.Strikeout(); + break; + case STRIKEOUTS: + format.Strikeout(chrstyle.IsStrikeout()); + break; + case CAPITALS0: + format.capitals = false; + break; + case CAPITALS1: + format.capitals = true; + break; + case CAPITALSS: + format.capitals = chrstyle.capitals; + break; + case DASHED0: + format.dashed = false; + break; + case DASHED1: + format.dashed = true; + break; + case DASHEDS: + format.dashed = chrstyle.dashed; + break; + case SSCRIPT: + format.sscript = in.Get(); + if(format.sscript == 3) + format.sscript = chrstyle.sscript; + break; + case FACE: + c = in.Get16(); + format.Face(c == 0xffff ? chrstyle.GetFace() : c); + break; + case HEIGHT: + c = in.Get16(); + format.Height(c == 0xffff ? chrstyle.GetHeight() : c); + break; + case LINK: + in % format.link; + break; + case INDEXENTRY: + in % format.indexentry; + break; + case INK: + in % format.ink; + break; + case PAPER: + in % format.paper; + break; + case LANGUAGE: + format.language = in.Get32(); + break; + case EXT: + switch(in.Get()) { + case NONAA0: + format.NonAntiAliased(false); + break; + case NONAA1: + format.NonAntiAliased(true); + break; + case NONAAS: + format.NonAntiAliased(chrstyle.IsNonAntiAliased()); + break; + } + } + while((c = in.Term()) < 31 && c != 9 && c != FIELD && c >= 0); + if(part.Top().text.GetLength()) + part.Add(); + part.Top().format = format; + } + else + if(in.Term() == FIELD) { + RichPara::Format pformat = format; + if(part.Top().text.GetLength()) { + part.Add(); + part.Top().format = pformat; + } + in.Get(); + Part& p = part.Top(); + p.field = Id(in.Get32()); + in % p.fieldparam; + String s; + in % s; + StringStream sn(s); + UnpackParts(sn, chrstyle, p.fieldpart, obj, oi); + part.Add(); + part.Top().format = format = pformat; + } + else + if(in.Term() == OBJECT) { + if(part.Top().text.GetLength()) { + part.Add(); + part.Top().format = format; + } + part.Top().object = obj[oi++]; + part.Top().format = format; + part.Add(); + part.Top().format = format; + in.Get(); + } + else + part.Top().text.Cat(in.GetUtf8()); + if(part.Top().text.GetLength() == 0 && part.Top().IsText()) + part.Drop(); +} + +void RichPara::Unpack(const String& data, const Array& obj, + const RichPara::Format& style) +{ + part.Clear(); + StringStream in(data); + + format = style; + + dword pattr = in.Get32(); + + if(pattr & 1) format.align = in.Get16(); + if(pattr & 2) format.before = in.Get16(); + if(pattr & 4) format.lm = in.Get16(); + if(pattr & 8) format.indent = in.Get16(); + if(pattr & 0x10) format.rm = in.Get16(); + if(pattr & 0x20) format.after = in.Get16(); + if(pattr & 0x40) format.bullet = in.Get16(); + if(pattr & 0x80) format.keep = in.Get(); + if(pattr & 0x100) format.newpage = in.Get(); + if(pattr & 0x200) format.tabsize = in.Get16(); + if(pattr & 0x400) in % format.label; + if(pattr & 0x800) format.keepnext = in.Get(); + if(pattr & 0x1000) format.orphan = in.Get(); + if(pattr & 0x2000) { + in % format.before_number; + in % format.after_number; + format.reset_number = in.Get(); + in.Get(format.number, 8); + } + if(pattr & 0x4000) { + format.linespacing = (int8)in.Get(); + } + if(pattr & 0x8000) { + format.tab.Clear(); + int n = in.Get16(); + format.tab.Reserve(n); + for(int i = 0; i < n; i++) { + RichPara::Tab& w = format.tab.Add(); + w.pos = in.Get32(); + w.align = in.Get(); + w.fillchar = in.Get(); + } + } + if(pattr & 0x10000) + format.ruler = in.Get16(); + if(pattr & 0x20000) + format.rulerink.Serialize(in); + part.Clear(); + int oi = 0; + UnpackParts(in, style, part, obj, oi); +} + +bool RichPara::IsEmpty() const +{ + return part.GetCount() == 0 || GetLength() == 0; +} + +int RichPara::GetLength() const +{ + int n = 0; + for(int i = 0; i < part.GetCount(); i++) + n += part[i].GetLength(); + return n; +} + +WString RichPara::GetText() const +{ + WString r; + for(int i = 0; i < part.GetCount(); i++) + if(part[i].IsText()) + r.Cat(part[i].text); + else + r.Cat(127); + return r; +} + +typedef VectorMap FieldTypeMap; +GLOBAL_VAR(FieldTypeMap, fieldtype0) + +const VectorMap& RichPara::fieldtype() +{ + return fieldtype0(); +} + +void RichPara::Register(Id id, FieldType& ft) init_ +{ + AssertST(); + fieldtype0().GetAdd(id, &ft); +} + +bool RichPara::EvaluateFields(VectorMap& vars) +{ + bool b = false; + for(int i = 0; i < GetCount(); i++) { + Part& p = part[i]; + if(p.field) { + FieldType *f = fieldtype().Get(p.field, NULL); + if(f) { + p.fieldpart ^= f->Evaluate(p.fieldparam, vars, p.format); + b = true; + } + } + } + return b; +} + +bool RichPara::HasPos() const +{ + if(!format.label.IsEmpty()) return true; + for(int i = 0; i < part.GetCount(); i++) + if(!part[i].format.indexentry.IsEmpty()) + return true; + return false; +} + +int RichPara::FindPart(int& pos) const +{ + int pi = 0; + while(pi < part.GetCount() && pos >= part[pi].GetLength()) { + pos -= part[pi].GetLength(); + pi++; + } + return pi; +} + +void RichPara::Trim(int pos) +{ + int i = FindPart(pos); + if(pos) { + ASSERT(part[i].IsText()); + part[i].text.Trim(pos); + part.SetCount(i + 1); + } + else + part.SetCount(i); +} + +void RichPara::Mid(int pos) +{ + int i = FindPart(pos); + part.Remove(0, i); + if(pos) { + ASSERT(part[0].IsText()); + part[0].text = part[0].text.Mid(pos); + } +} + +void ApplyCharStyle(RichPara::CharFormat& format, const RichPara::CharFormat& f0, + const RichPara::CharFormat& newstyle) { + if(format.IsBold() == f0.IsBold()) + format.Bold(newstyle.IsBold()); + if(format.IsUnderline() == f0.IsUnderline()) + format.Underline(newstyle.IsUnderline()); + if(format.IsItalic() == f0.IsItalic()) + format.Italic(newstyle.IsItalic()); + if(format.IsStrikeout() == f0.IsStrikeout()) + format.Strikeout(newstyle.IsStrikeout()); + if(format.IsNonAntiAliased() == f0.IsNonAntiAliased()) + format.NonAntiAliased(newstyle.IsNonAntiAliased()); + if(format.capitals == f0.capitals) + format.capitals = newstyle.capitals; + if(format.dashed == f0.dashed) + format.dashed = newstyle.dashed; + if(format.sscript == f0.sscript) + format.sscript = newstyle.sscript; + if(format.GetFace() == f0.GetFace()) + format.Face(newstyle.GetFace()); + if(format.GetHeight() == f0.GetHeight()) + format.Height(newstyle.GetHeight()); + if(format.ink == f0.ink) + format.ink = newstyle.ink; + if(format.paper == f0.paper) + format.paper = newstyle.paper; +} + +void RichPara::ApplyStyle(const Format& newstyle) +{ + CharFormat f0 = part.GetCount() ? part[0].format : format; + for(int i = 0; i < part.GetCount(); i++) + ApplyCharStyle(part[i].format, f0, newstyle); + CharFormat h = format; + ApplyCharStyle(h, f0, newstyle); + format = newstyle; + (CharFormat&)format = h; +} + +#ifdef _DEBUG +void RichPara::Dump() +{ + LOG("RichPara dump" << LOG_BEGIN); + LOG("RULER: " << format.ruler << " " << format.rulerink); + LOG("BEFORE: " << format.before); + LOG("INDENT: " << format.indent); + LOG("LM: " << format.lm); + LOG("RM: " << format.rm); + LOG("AFTER: " << format.after); + LOG("KEEP: " << format.keep); + LOG("NEWPAGE: " << format.newpage); + LOG("BULLET: " << format.bullet); + int i; + for(i = 0; i < format.tab.GetCount(); i++) + LOG("TAB " << format.tab[i].pos << " : " << format.tab[i].align); + for(i = 0; i < part.GetCount(); i++) + LOG("Part[" << i << "] = \"" << part[i].text << "\" " + << part[i].format); + LOG(LOG_END << "---------"); +} + + +String RichPara::CharFormat::ToString() const +{ + String out; + out + << Font(*this) + << ", ink " << DumpColor(ink); + if(!UPP::IsNull(paper)) + out << ", paper " << DumpColor(paper); + switch(sscript) + { + case 0: break; + case 1: out << ", superscript"; break; + case 2: out << ", subscript"; break; + default: out << ", sscript(" << (int)sscript << ")"; break; + } + out << ", lang " << DumpLanguage(language); + if(!UPP::IsNull(link)) + out << ", link " << link; + if(capitals) + out << ", capitals"; + if(dashed) + out << ", dashed"; + return out; +} + +String RichPara::Format::ToString() const +{ + String out; + if(!UPP::IsNull(label)) + out << "label <" << label << ">: "; + out + << DumpAlign(align) << ", left " << lm << ", right " << rm + << ", indent " << indent << ", before " << before << ", after " << after + << ", tabsize " << tabsize << ", bullet " << bullet + << (newpage ? ", newpage" : "") + << (keep ? ", keep" : "") + << (keepnext ? ", keepnext" : "") + << (orphan ? ", orphan" : ""); + int i; + for(i = 0; i < tab.GetCount(); i++) + out << (i ? "\n" : ", ") + << "tab[" << i << "] = " << tab[i].pos << ", align " << DumpAlign(tab[i].align) + << ", fill " << FormatIntHex(tab[i].fillchar, 2); + out << "\n"; + out << "before_number " << before_number << ", after_number " << after_number + << (reset_number ? ", reset_number" : ""); + for(i = 0; i < __countof(number); i++) + if(number[i] != RichPara::NUMBER_NONE) + out << " num[" << i << "] = " << (int)number[i]; + out << "\n"; + return out; +} + +#endif + +END_UPP_NAMESPACE diff --git a/uppdev/RichTextP/ParaPaint.cpp b/uppdev/RichTextP/ParaPaint.cpp new file mode 100644 index 000000000..2882b4390 --- /dev/null +++ b/uppdev/RichTextP/ParaPaint.cpp @@ -0,0 +1,587 @@ +#include "RichText.h" + +NAMESPACE_UPP + +#define IMAGECLASS RichTextImg +#define IMAGEFILE +#include + +RichPara::Lines RichPara::Begin(const Rect& page, PageY& py, int nbefore, int nline) const +{ + Lines pl = FormatLines(page.Width()); + int cy = format.ruler + format.before; + if(format.keep || format.keepnext) + cy += pl.BodyHeight(); + else + cy += pl[0].Sum(); + int after = format.after; + if(!format.keepnext) + nbefore = nline = after = 0; + if(page.Height() < 32000 && + (format.newpage || py.y + cy + after + nbefore + nline > page.bottom && + cy < page.Height())) { + py.page++; + py.y = page.top; + } + py.y += format.before + format.ruler; + pl.Justify(format); + return pl; +} + +void RichPara::Flush(Draw& draw, const PaintInfo& pi, wchar *text, + const CharFormat **i0, + int *wd, int pos, int len, int x0, int x, int y0, + int y, int linecy, int lineascent, Zoom z, + bool highlight) +{ + if(*i0) { + const CharFormat& f = **i0; + int width = z * x - z * x0; + int zy0 = z * y0; + int zx0 = z * x0; + if(!IsNull(f.paper) && !highlight) + draw.DrawRect(zx0, z * y, width, z * (y + linecy) - z * y, + pi.coloroverride ? SColorPaper() : f.paper); + if(!IsNull(f.indexentry) && !IsNull(pi.indexentry)) + draw.DrawRect(zx0, zy0, width, 2, pi.indexentry); + Font fnt = f; + int zht = z * tabs(f.GetHeight()); + int ssa; + int ssd; + if(f.sscript) { + FontInfo fi = fnt(zht).Info(); + ssa = fi.GetAscent(); + ssd = fi.GetDescent(); + zht = 3 * zht / 5; + } + fnt.Height(zht ? zht : 1); + FontInfo fi = fnt.Info(); + if(f.dashed) { + int dx = max(fi.GetAscent() / 5, 2); + for(int i = 0; dx * i < width; i++) + draw.DrawRect(zx0 + i * dx, zy0 + fi.GetDescent() / 2, + dx / 2, max(fi.GetDescent() / 3, 1), + pi.coloroverride ? SColorText() : f.ink); + } + Color ink = pi.coloroverride ? SColorText() : f.ink; + if(!IsNull(f.link) && !IsNull(pi.hyperlink) && !(fnt.IsUnderline() || f.dashed)) { + fnt.Underline(); + if(!pi.coloroverride) + ink = pi.hyperlink; + } + x = zx0; + for(int i = 0; i < len; i++) { + int w = wd[pos + i]; + Image img; + wchar& c = text[pos + i]; + if(c == ' ') + img = RichTextImg::SpaceChar(); + if(c == 160) + img = RichTextImg::HardSpaceChar(); + if(c >= 9 && c < 9 + 4) { + if(c > 9) { + static char fct[] = { ' ', '.', '-', '_' }; + int fc = fct[c - 9]; + int pw = fi[fc]; + int px = (x + pw - 1) / pw * pw; + while(px + pw < x + w) { + draw.DrawText(px, zy0 - fi.GetAscent(), String(fc, 1), fnt, Black); + px += pw; + } + } + img = RichTextImg::TabChar(); + c = ' '; + } + if(img && !IsNull(pi.showcodes)) { + Size sz = img.GetSize(); + if(sz.cy < z * lineascent) + draw.DrawImage(x + (w - sz.cx) / 2, zy0 - sz.cy, img, pi.showcodes); + } + x += w; + } + draw.DrawText(zx0, + f.sscript == 1 ? zy0 - ssa : + f.sscript == 2 ? zy0 + ssd - fi.GetHeight() + : zy0 - fi.GetAscent(), + text + pos, fnt, ink, len, wd + pos); + } +} + +bool RichPara::BreaksPage(PageY py, const Lines& pl, int i, const Rect& page) const +{ + int linecy = pl[i].Sum(); + if(linecy >= page.Height()) return false; + if(linecy + py.y > page.bottom) + return true; + if(format.orphan || pl.GetCount() < 2) return false; + if((i == 0 || i == pl.GetCount() - 2) && py.y + linecy + pl[i + 1].Sum() > page.bottom) + return true; + return false; +} + +struct RichObjectImageMaker : ImageMaker { + RichObject object; + Size sz; + + virtual String Key() const; + virtual Image Make() const; +}; + +String RichObjectImageMaker::Key() const +{ + StringBuffer b; + int64 id = object.GetSerialId(); + b.Cat((const char *)&id, sizeof(id)); + b.Cat((const char *)&sz.cx, sizeof(sz.cx)); + b.Cat((const char *)&sz.cy, sizeof(sz.cy)); + return b; +} + +Image RichObjectImageMaker::Make() const +{ + ImageDraw iw(sz); + iw.DrawRect(sz, SColorPaper()); + object.Paint(iw, sz); + return iw; +} + +void RichPara::Paint(PageDraw& pw, const Rect& page, PageY py, const PaintInfo& pi, + const Number& n, const Bits& spellerror, + int nbefore, int nline) const +{ + Zoom z = pi.zoom; + PageY opy = py; + Lines pl = Begin(page, py, nbefore, nline); + bool highlight = pi.highlightpara >= 0 && pi.highlightpara < pl.len; + int hy = py.y - format.before - format.ruler; + int phy = py.page; + if(pi.sell < 0 && pi.selh > 0) + for(int p = opy.page; p <= py.page; p++) { + int top = z * (p == opy.page ? opy.y : page.top); + int bottom = z * (p == py.page ? py.y : page.bottom); + pw.Page(p).DrawRect(z * page.left, top, z * page.right - z * page.left, + bottom - top, InvertColor); + } + opy = py; + int oi = 0; + int x; + int y0; + int lineascent = 0; + for(int lni = 0; lni < pl.GetCount(); lni++) { + Line& li = pl[lni]; + int linecy = li.Sum(); + lineascent = li.ascent; + if(BreaksPage(py, pl, lni, page)) { + if(li.ppos > pi.sell && li.ppos < pi.selh) { + int y = z * py.y; + pw.Page(py.page).DrawRect(z * page.left, y, z * page.right - z * page.left, + z * page.bottom - y, InvertColor); + } + py.y = page.top; + py.page++; + } + if(py > pi.bottom) + break; + + const CharFormat **cf = pl.format + li.pos; + const CharFormat **i = cf; + const CharFormat **ilim = i + li.len; + const HeightInfo *hg = pl.height + li.pos; + if(py + linecy >= pi.top) { + Draw& draw = pw.Page(py.page); +#ifdef _DEBUG + int cloff = draw.GetCloffLevel(); +#endif + Buffer wd(li.len); + int x0 = li.xpos + page.left; + x = x0; + int *w = pl.width + li.pos; + int *wl = w + li.len; + int *t = wd; + while(w < wl) { + int x1 = x + *w++; + *t++ = z * x1 - z * x; + x = x1; + } + + if(highlight) + draw.DrawRect(z * page.left, z * py.y, z * page.Width(), + z * (py.y + linecy) - z * py.y, + pi.highlight); + + const CharFormat **i0 = i; + wchar *text = pl.text + li.pos; + x = x0; + w = pl.width + li.pos; + y0 = py.y + li.ascent; + int pp = li.pos; + int l = page.right; + int h = -1; + + while(i < ilim) { + if(pl.pos[pp] == pi.sell && x < l) + l = x; + if(pl.pos[pp] == pi.selh && h < 0) + h = x; + pp++; + if(*i0 != *i || hg->object) { + Flush(draw, pi, text, i0, wd, (int)(i0 - cf),(int)( i - i0), x0, x, y0, py.y, linecy, + lineascent, z, highlight); + i0 = i; + x0 = x; + } + if(hg->object) { + const RichObject& o = *hg->object; + if(o) { + Size sz = z * o.GetSize(); + draw.DrawRect(z * x, z * py.y, sz.cx, z * linecy, (*i)->paper); + draw.Clipoff(z * x, z * (y0 - hg->ascent), sz.cx, sz.cy); + if(pi.sizetracking) + draw.DrawRect(sz, SColorFace); + else + if(pi.usecache) { + RichObjectImageMaker im; + im.object = o; + im.sz = sz; + draw.DrawImage(0, 0, MakeImagePaintOnly(im)); + } + else + o.Paint(draw, sz); + draw.End(); + } + i++; + hg++; + x += *w++; + i0 = i; + x0 = x; + } + else { + i++; + hg++; + x += *w++; + } + } + if(i > i0) + Flush(draw, pi, text, i0, wd, (int)(i0 - cf), (int)(i - i0), x0, x, y0, py.y, linecy, + lineascent, z, highlight); + if(lni == 0) { + Rect r; + r.left = page.left + format.lm; + int q = li.ascent / 2; + r.top = py.y + 4 * (li.ascent - q) / 5; + r.right = r.left + q; + r.bottom = r.top + q; + q = z * (r.Width() / 4); + r.left = z * r.left; + r.top = z * r.top; + r.right = z * r.right; + r.bottom = z * r.bottom; + Rect r1 = r; + r1.Deflate(max(1, q)); + switch(format.bullet) { + case BULLET_BOX: + draw.DrawRect(r, pi.coloroverride ? SColorText() : format.ink); + break; + case BULLET_BOXWHITE: + draw.DrawRect(r, pi.coloroverride ? SColorText() : format.ink); + draw.DrawRect(r1, White); + break; + case BULLET_ROUNDWHITE: + draw.DrawEllipse(r, pi.coloroverride ? SColorText() : format.ink); + draw.DrawEllipse(r1, pi.coloroverride ? SColorPaper() : White); + break; + case BULLET_ROUND: + draw.DrawEllipse(r, pi.coloroverride ? SColorText() : format.ink); + break; + default: + String s = n.AsText(format); + if(!IsNull(s)) { + CharFormat cf = li.len && *pl.format ? **pl.format : format; + cf.Height(z * cf.GetHeight()); + draw.DrawText(r.left, + z * y0 - cf.Info().GetAscent(), + s, cf, pi.coloroverride ? SColorText() : cf.ink); + } + } + } + int zlcy = z * linecy; + if(pi.spellingchecker && zlcy > 3) { + int x = z * (li.xpos + page.left); + w = wd; + wl = w + li.len; + int i = li.pos; + int q = z * (py.y + linecy); + while(w < wl) { + if(spellerror[pl.pos[i++]]) { + if(zlcy > 10) + draw.DrawRect(x, q - 3, *w, 1, Red); + draw.DrawRect(x, q - 2, *w, 1, LtRed); + } + x += *w++; + } + } + + if(pi.selh == li.ppos + li.plen) + h = x; + if(pi.sell == li.ppos + li.plen) + l = x; + if(pi.sell < li.ppos) + l = page.left; + if(pi.selh > li.ppos + li.plen) + h = page.right; + if(pi.sell < pi.selh && pi.selh > li.ppos) + draw.DrawRect(z * l, z * py.y, z * h - z * l, + z * (py.y + linecy) - z * py.y, InvertColor); + ASSERT(draw.GetCloffLevel() == cloff); + } + else + while(i < ilim) { + if(hg->object) + oi++; + i++; + hg++; + } + py.y += linecy; + } + Size sz = RichTextImg::EndParaChar().GetSize(); + if(sz.cy < z * lineascent && !IsNull(pi.showcodes)) + pw.Page(py.page).DrawImage(z * x, z * y0 - sz.cy, + RichTextImg::EndParaChar(), + format.indexentry.GetCount() ? pi.indexentry : pi.showcodes); + if(format.newpage && !IsNull(pi.showcodes)) { + Draw& w = pw.Page(opy.page); + int wd = z * page.right - z * page.left; + int step = w.Pixels() ? 8 : 50; + int y = z * opy.y; + for(int x = 0; x < wd; x += step) + w.DrawRect(z * page.left + x, y, step >> 1, step >> 3, pi.showcodes); + } + if(pl.len >= pi.sell && pl.len < pi.selh && py < pi.bottom) { + int top = z * py.y; + pw.Page(py.page).DrawRect(z * page.left, top, z * page.right - z * page.left, + z * min(py.y + format.after, page.bottom) - top, InvertColor); + } + if(format.ruler && hy >= 0 && hy + format.ruler < page.bottom) + pw.Page(phy).DrawRect(z * page.left + z * format.lm, z * hy, + z * page.right - z * page.left - z * format.rm - z * format.lm, + max(1, z * format.ruler), format.rulerink); +} + +void RichPara::GetRichPos(RichPos& rp, int pos) const +{ + rp.format = format; + int i = FindPart(pos); + if(i < GetCount()) { + const Part& p = part[i]; + (CharFormat&)rp.format = p.format; + if(p.object) { + rp.chr = -1; + rp.object = p.object; + } + else + if(p.IsText()) + rp.chr = p.text[pos]; + else { + rp.chr = -2; + rp.field = p.field; + rp.fieldparam = p.fieldparam; + if(p.fieldpart.GetCount() > 0) + rp.fieldformat = p.fieldpart[0].format; + } + } + else + rp.chr = '\n'; +} + +RichCaret RichPara::GetCaret(int pos, const Rect& page, PageY py, int nbefore, int nline) const +{ + Lines pl = Begin(page, py, nbefore, nline); + RichCaret pr; + FontInfo fi = format.Info(); + pr.caretascent = fi.GetAscent(); + pr.caretdescent = fi.GetDescent(); + for(int lni = 0; lni < pl.GetCount(); lni++) { + Line& li = pl[lni]; + int linecy = li.Sum(); + if(BreaksPage(py, pl, lni, page)) { + py.y = page.top; + py.page++; + } + pr.page = py.page; + pr.top = py.y; + pr.bottom = py.y + linecy; + pr.lineascent = li.ascent; + pr.line = lni; + if(pos < li.ppos + li.plen) { + int *w = pl.width + li.pos; + int *p = pl.pos + li.pos; + const CharFormat **i = pl.format + li.pos; + const HeightInfo *h = pl.height + li.pos; + int x = li.xpos + page.left; + if(li.len && *i) { + pr.caretascent = h->ascent; + pr.caretdescent = h->descent; + } + while(pos > *p) { + x += *w++; + if(*i) { + pr.caretascent = h->ascent; + pr.caretdescent = h->descent; + } + h++; + i++; + p++; + } + pr.objectcy = h->ascent + h->ydelta; + pr.objectyd = h->ydelta; + pr.left = x; + pr.right = x + *w; + pr.caretdescent = min(pr.caretdescent, pr.Height() - pr.lineascent); + return pr; + } + py.y += linecy; + } + const Line& li = pl.line.Top(); + pr.left = li.cx + li.xpos + page.left; + pr.right = page.right; + pr.caretdescent = min(pr.caretdescent, pr.Height() - pr.lineascent); + return pr; +} + +int RichPara::PosInLine(int x, const Rect& page, const Lines& pl, int lni) const +{ + const Line& li = pl[lni]; + const int *w = pl.width + li.pos; + const int *wl = w + li.len; + if(lni < pl.GetCount() - 1 && li.len > 0 && pl.text[li.pos + li.len - 1] == ' ') + wl--; + int xp = li.xpos + page.left; + while(w < wl && xp + *w <= x) + xp += *w++; + int pos = (int)(w - pl.width); + return pos < pl.clen ? pl.pos[pos] : pl.len; +} + +int RichPara::GetPos(int x, PageY y, const Rect& page, PageY py, int nbefore, int nline) const +{ + Lines pl = Begin(page, py, nbefore, nline); + if(pl.len) + for(int lni = 0; lni < pl.GetCount(); lni++) { + const Line& li = pl[lni]; + int linecy = li.Sum(); + if(BreaksPage(py, pl, lni, page)) { + py.y = page.top; + py.page++; + } + py.y += linecy; + if(y < py || lni == pl.GetCount() - 1) + return PosInLine(x, page, pl, lni); + } + return pl.len; +} + +int RichPara::GetVertMove(int pos, int gx, const Rect& page, int dir) const +{ + Lines pl = FormatLines(page.Width()); + int lni; + if(pos >= 0) { + for(lni = 0; lni < pl.GetCount() - 1; lni++) { + const Line& li = pl[lni]; + if(pos < li.ppos + li.plen) + break; + } + lni += sgn(dir); + if(lni < 0 || lni >= pl.GetCount()) + return -1; + } + else + lni = dir < 0 ? pl.GetCount() - 1 : 0; + return PosInLine(gx, page, pl, lni); +} + +void RichPara::GatherLabels(Vector& info, const Rect& page, PageY py, + int pos, int nbefore, int nline) const +{ + Lines pl = Begin(page, py, nbefore, nline); + WString ie; + if(!pl.GetCount()) + return; + if(BreaksPage(py, pl, 0, page)) { + py.y = page.top; + py.page++; + } + if(format.label.IsEmpty()) + return; + RichValPos& f = info.Add(); + f.py = py; + f.pos = pos; + f.data = format.label.ToWString(); +} + +void RichPara::GatherIndexes(Vector& info, const Rect& page, PageY py, + int pos, int nbefore, int nline) const +{ + Lines pl = Begin(page, py, nbefore, nline); + WString ie; + for(int lni = 0; lni < pl.GetCount(); lni++) { + Line& li = pl[lni]; + int linecy = li.Sum(); + if(BreaksPage(py, pl, lni, page)) { + py.y = page.top; + py.page++; + } + const CharFormat **i0 = pl.format + li.pos; + const CharFormat **i = i0; + const CharFormat **ilim = i + li.len; + while(i < ilim) { + if(*i && (*i)->indexentry != ie) { + ie = (*i)->indexentry; + if(!ie.IsEmpty()) { + RichValPos& f = info.Add(); + f.py = py; + f.pos = (int)(i - i0) + pos; + f.data = ie; + } + } + i++; + } + py.y += linecy; + } +} + +void FontHeightRound(Font& fnt, Zoom z) +{ + fnt.Height((fnt.GetHeight() * z.m + (z.d >> 1)) / z.d); +} + +void operator*=(RichPara::Format& format, Zoom z) +{ + FontHeightRound(format, z); + format.before *= z; + if(format.ruler) { + format.ruler *= z; + if(format.ruler == 0) + format.ruler = 1; + } + int ll = format.lm + format.indent; + format.lm *= z; + format.indent = z * ll - format.lm; + format.rm *= z; + format.after *= z; + format.tabsize *= z; + for(int i = 0; i < format.tab.GetCount(); i++) + format.tab[i].pos *= z; +} + +void RichPara::ApplyZoom(Zoom z) +{ + format *= z; + for(int i = 0; i < part.GetCount(); i++) + if(part[i].IsText()) + FontHeightRound(part[i].format, z); + else + if(part[i].object) + part[i].object.SetSize(z * part[i].object.GetSize()); +} + +END_UPP_NAMESPACE diff --git a/uppdev/RichTextP/ParaType.cpp b/uppdev/RichTextP/ParaType.cpp new file mode 100644 index 000000000..fa1a74d46 --- /dev/null +++ b/uppdev/RichTextP/ParaType.cpp @@ -0,0 +1,376 @@ +#include "RichText.h" + +NAMESPACE_UPP + +void RichPara::Smh(Lines& lines, HeightInfo *th, int cx) const +{ + Line& l = lines.line.Top(); + l.ascent = l.descent = l.external = 0; + const HeightInfo *he = th + l.pos + l.len; + for(const HeightInfo *h = th + l.pos; h < he; h++) { + if(h->ascent > l.ascent) l.ascent = h->ascent; + if(h->descent > l.descent) l.descent = h->descent; + if(h->external > l.external) l.external = h->external; + } + if(format.linespacing == LSP15) { + l.ascent = (3 * l.ascent) >> 1; + l.descent = (3 * l.descent) >> 1; + } + if(format.linespacing == LSP20) { + l.ascent = 2 * l.ascent; + l.descent = 2 * l.descent; + } + l.xpos = format.lm; + cx -= format.lm + format.rm; + l.xpos += lines.GetCount() == 1 ? lines.first_indent : lines.next_indent; + if(!l.withtabs) + switch(format.align) { + case ALIGN_RIGHT: + l.xpos += cx - l.cx; + break; + case ALIGN_CENTER: + l.xpos += (cx - l.cx) / 2; + break; + } + l.cx -= lines.GetCount() == 1 ? lines.first_indent : lines.next_indent; +} + +RichPara::Tab RichPara::GetNextTab(int pos) const +{ + int tabi = -1; + int dist = INT_MAX; + for(int i = 0; i < format.tab.GetCount(); i++) { + const Tab& tab = format.tab[i]; + if(tab.pos > pos && tab.pos - pos < dist) { + tabi = i; + dist = tab.pos - pos; + } + } + if(format.bullet == BULLET_TEXT) { + int q = format.indent + format.lm; + if(q > pos && q - pos < dist) { + Tab tab; + tab.align = ALIGN_LEFT; + tab.pos = q; + return tab; + } + } + if(tabi < 0) { + Tab tab; + tab.pos = format.tabsize ? (pos + format.tabsize) / format.tabsize * format.tabsize : 0; + tab.align = ALIGN_LEFT; + return tab; + } + return format.tab[tabi]; +} + +struct RichPara::StorePart { + wchar *t; + int *w; + int *p; + const CharFormat **f; + HeightInfo *h; + int pos; + FontInfo pfi; + + void Store(Lines& lines, const Part& p, int pinc); +}; + +void RichPara::StorePart::Store(Lines& lines, const Part& part, int pinc) +{ + if(part.field && pinc) { + for(int i = 0; i < part.fieldpart.GetCount(); i++) + Store(lines, part.fieldpart[i], 0); + pos++; + } + else + if(part.object) { + *f++ = &part.format; + Size sz = part.object.GetSize(); + *w++ = sz.cx; + h->ydelta = part.object.GetYDelta(); + h->ascent = sz.cy - h->ydelta; + h->descent = max(h->ydelta, 0); + h->external = 0; + h->object = &part.object; + h++; + *t++ = 'x'; + *p++ = pos; + pos += pinc; + } + else { + const wchar *s = part.text; + const wchar *lim = part.text.End(); + Font fnt = part.format; + FontInfo fi = fnt.Info(); + FontInfo wfi = fi; + if(part.format.sscript) { + fnt.Height(fnt.GetHeight() * 3 / 5); + wfi = fnt.Info(); + } + if(part.format.capitals) { + CharFormat& cfmt = lines.hformat.Add(); + cfmt = part.format; + cfmt.Height(cfmt.GetHeight() * 4 / 5); + FontInfo cfi = cfmt.Info(); + FontInfo cwfi = cfi; + if(part.format.sscript) { + Font fnt = cfmt; + fnt.Height(fnt.GetHeight() * 3 / 5); + cwfi = fnt.Info(); + } + + while(s < lim) { + wchar c = *s++; + if(c == 9) { + *f++ = &part.format; + h->ascent = pfi.GetAscent(); + h->descent = pfi.GetDescent(); + h->external = pfi.GetExternal(); + *w++ = 0; + } + else + if(IsLower(c)) { + *f++ = &cfmt; + c = ToUpper(c); + h->ascent = cfi.GetAscent(); + h->descent = cfi.GetDescent(); + h->external = cfi.GetExternal(); + *w++ = c >= 32 ? cwfi[c] : 0; + } + else { + *f++ = &part.format; + h->ascent = fi.GetAscent(); + h->descent = fi.GetDescent(); + h->external = fi.GetExternal(); + *w++ = c >= 32 ? wfi[c] : 0; + } + h->object = NULL; + *t++ = c; + *p++ = pos; + pos += pinc; + h++; + } + } + else { + while(s < lim) { + wchar c = *s++; + *f++ = &part.format; + if(c == 9) { + h->ascent = pfi.GetAscent(); + h->descent = pfi.GetDescent(); + h->external = pfi.GetExternal(); + } + else { + h->ascent = fi.GetAscent(); + h->descent = fi.GetDescent(); + h->external = fi.GetExternal(); + } + h->object = NULL; + *p++ = pos; + pos += pinc; + h++; + *w++ = c >= 32 ? wfi[c] : 0; + *t++ = c; + } + } + } +} + +static int CountChars(const Array& part) +{ + int n = 0; + for(int i = 0; i < part.GetCount(); i++) { + const RichPara::Part& p = part[i]; + if(p.field) + n += CountChars(p.fieldpart); + else + n += p.GetLength(); + } + return n; +} + +RichPara::Lines RichPara::FormatLines(int acx) const +{ + int i; + Lines lines; + lines.cx = acx; + lines.len = GetLength(); + lines.clen = CountChars(part); + lines.first_indent = lines.next_indent = format.indent; + if(format.bullet == BULLET_TEXT) + lines.first_indent = 0; + else + if(!format.bullet && !format.IsNumbered()) + lines.next_indent = 0; + + FontInfo pfi = format.Info(); + if(lines.len == 0) { + Line& l = lines.line.Add(); + l.pos = 0; + l.ppos = 0; + l.plen = 0; + l.len = 0; + l.cx = lines.first_indent; + l.withtabs = false; + HeightInfo dummy; + Smh(lines, &dummy, lines.cx); + l.ascent = pfi.GetAscent(); + l.descent = pfi.GetDescent(); + l.external = pfi.GetExternal(); + return lines; + } + + lines.text.Alloc(lines.clen); + lines.width.Alloc(lines.clen); + lines.pos.Alloc(lines.clen); + lines.format.Alloc(lines.clen); + lines.height.Alloc(lines.clen); + + StorePart sp; + sp.t = lines.text; + sp.w = lines.width; + sp.p = lines.pos; + sp.f = lines.format; + sp.h = lines.height; + sp.pfi = pfi; + sp.pos = 0; + + for(i = 0; i < part.GetCount(); i++) + sp.Store(lines, part[i], 1); + + wchar *s = lines.text; + wchar *text = s; + wchar *end = lines.text + lines.clen; + wchar *space = NULL; + int *w = lines.width; + int cx = lines.first_indent; + int rcx = lines.cx - format.lm - format.rm; + bool withtabs = false; + int scx; + while(s < end) { + Tab t; + if(*s == ' ') { + space = s; + scx = cx; + } + else { + if(*s == '\t') { + t = GetNextTab(cx + format.lm); + space = NULL; + } + if(cx + *w > rcx && s > text || *s == '\t' && t.pos - format.lm >= rcx) { + Line& l = lines.line.Add(); + l.withtabs = withtabs; + l.pos = (int)(text - lines.text); + if(space) { + l.len = (int)(space - text) + 1; + l.cx = scx; + text = s = space + 1; + } + else { + l.len = (int)(s - text); + l.cx = cx; + text = s; + } + Smh(lines, lines.height, lines.cx); + cx = lines.next_indent; + w = text - ~lines.text + lines.width; + space = NULL; + rcx = lines.cx - format.lm - format.rm; + withtabs = false; + t = GetNextTab(cx + format.lm); + } + } + if(*s == '\t') { + *s += t.fillchar; + if(t.align == ALIGN_LEFT) { + *w++ = t.pos - format.lm - cx; + cx = t.pos - format.lm; + } + else { + int tcx = 0; + int *tw = w + 1; + for(wchar *ts = s + 1; ts < end && *ts != '\t'; ts++) + tcx += *tw++; + int ww = t.pos - format.lm - cx - (t.align == ALIGN_RIGHT ? tcx : tcx / 2); + if(ww > 0) { + *w++ = ww; + cx += ww; + } + else + *w++ = 0; + } + withtabs = true; + } + else + cx += *w++; + s++; + } + Line& l = lines.line.Add(); + l.withtabs = withtabs; + l.pos = (int)(text - lines.text); + l.len = (int)(s - text); + l.cx = cx; + Smh(lines, lines.height, lines.cx); + for(i = 0; i < lines.line.GetCount(); i++) { + Line& l = lines.line[i]; + l.ppos = lines.pos[l.pos]; + l.plen = (l.pos + l.len < lines.clen ? lines.pos[l.pos + l.len] : lines.len) - l.ppos; + } + + return lines; +} + +void RichPara::Lines::Justify(const RichPara::Format& format) +{ + if(format.align != ALIGN_JUSTIFY) return; + for(int i = 0; i < line.GetCount() - 1; i++) { + const Line& li = line[i]; + if(!li.withtabs && li.len) { + const wchar *s = ~text + li.pos; + const wchar *lim = s + li.len; + while(lim - 1 > s) { + if(*(lim - 1) != ' ') break; + lim--; + } + while(s < lim) { + if(*s != ' ' && *s != 160) break; + s++; + } + + const wchar *beg = s; + int nspc = 0; + while(s < lim) { + if(*s == ' ' || *s == 160) nspc++; + s++; + } + s = beg; + if(nspc) { + int q = ((cx - format.lm - format.rm - + (i == 0 ? first_indent : next_indent) - li.cx) << 16) + / nspc; + int *w = beg - ~text + width; + int prec = 0; + while(s < lim) { + if(*s == ' ' || *s == 160) { + *w += (prec + q) >> 16; + prec = (prec + q) & 0xffff; + } + w++; + s++; + } + } + } + } +} + +int RichPara::Lines::BodyHeight() +{ + int sum = 0; + for(int i = 0; i < line.GetCount(); i++) + sum += line[i].Sum(); + return sum; +} + +END_UPP_NAMESPACE diff --git a/uppdev/RichTextP/ParseQtf.cpp b/uppdev/RichTextP/ParseQtf.cpp new file mode 100644 index 000000000..3916ee2b6 --- /dev/null +++ b/uppdev/RichTextP/ParseQtf.cpp @@ -0,0 +1,967 @@ +#include "RichText.h" + +NAMESPACE_UPP + +Color (*QTFColor[])() = { + Black, LtGray, White, Red, Green, Blue, LtRed, WhiteGray, LtCyan, Yellow +}; + +Color (*QTFSColor[])()= { + SColorText, SColorFace, SColorPaper, Red, Green, Blue, LtRed, WhiteGray, LtCyan, Yellow +}; + +Color NullColorF() +{ + return Null; +} + +static Color (*QTFColorl[])() = { + /*a*/White, /*b*/Blue, /*c*/Cyan, /*d*/White, /*e*/White, /*f*/White, /*g*/ Green, /*h*/White, + /*i*/White, /*j*/White, /*k*/Black, /*l*/LtGray, /*m*/Magenta, /*n*/NullColorF, /*o*/Brown, /*p*/White, + /*q*/White, /*r*/Red, /*s*/White, /*t*/White, /*u*/White, /*v*/White, /*w*/WhiteGray, /*x*/White, + /*y*/Yellow, /*z*/ White +}; + +static Color (*QTFColorL[])() = { + /*A*/White, /*B*/LtBlue, /*C*/LtCyan, /*D*/White, /*E*/White, /*F*/White, /*G*/LtGreen, /*H*/White, + /*I*/White, /*J*/White, /*K*/Gray, /*L*/WhiteGray, /*M*/LtMagenta, /*N*/NullColorF, /*O*/Brown, /*P*/White, + /*Q*/White, /*R*/LtRed, /*S*/White, /*T*/White, /*U*/White, /*V*/White, /*W*/White, /*X*/White, + /*Y*/LtYellow, /*Z*/White +}; + +static Color (*QTFSColorl[])() = { //TODO + /*a*/SColorPaper, /*b*/SColorHighlight, /*c*/Cyan, /*d*/SColorPaper, /*e*/SColorPaper, /*f*/SColorPaper, /*g*/ Green, /*h*/SColorPaper, + /*i*/SColorPaper, /*j*/SColorPaper, /*k*/SColorText, /*l*/SColorFace, /*m*/Magenta, /*n*/NullColorF, /*o*/Brown, /*p*/SColorPaper, + /*q*/SColorPaper, /*r*/Red, /*s*/SColorPaper, /*t*/SColorPaper, /*u*/SColorPaper, /*v*/SColorPaper, /*w*/WhiteGray, /*x*/SColorPaper, + /*y*/Yellow, /*z*/ White +}; + +static Color (*QTFSColorL[])() = { //TODO + /*A*/SColorPaper, /*B*/LtBlue, /*C*/LtCyan, /*D*/SColorPaper, /*E*/SColorPaper, /*F*/SColorPaper, /*G*/LtGreen, /*H*/SColorPaper, + /*I*/SColorPaper, /*J*/SColorPaper, /*K*/SColorShadow, /*L*/WhiteGray, /*M*/LtMagenta, /*N*/NullColorF, /*O*/Brown, /*P*/SColorPaper, + /*Q*/SColorPaper, /*R*/LtRed, /*S*/SColorPaper, /*T*/SColorPaper, /*U*/SColorPaper, /*V*/SColorPaper, /*W*/SColorPaper, /*X*/SColorPaper, + /*Y*/LtYellow, /*Z*/White +}; + +int QTFFontHeight[] = { + 50, 67, 84, 100, 134, 167, 200, 234, 300, 400 +}; + +class RichQtfParser { + const char *term; + bool scolors; + WString text; + RichPara paragraph; + RichTable tablepart; + bool istable; + bool breakpage; + byte accesskey; + + struct PFormat : public RichPara::Format { + byte charset; + }; + + struct Tab { + RichCell::Format format; + int vspan, hspan; + RichTxt text; + RichTable table; + int cell; + Vector rown; + + void Old() { RichTable::Format fmt; fmt.grid = 10; table.SetFormat(fmt); } + + Tab() { cell = 0; vspan = hspan = 0; } + }; + + PFormat format; + Array fstack; + Vector styleid; + Vector stylenext; + Array table; + bool oldtab; + + bool Key(int c) { if(*term == c) { term++; return true; } return false; } + bool Key2(int c, int d); + bool Key2(int c) { return Key2(c, c); } + int GetNumber(); + int ReadNumber(); + String GetText(int delim); + Color GetColor(); + void Number2(int& a, int& b); + + void Flush(); + void SetFormat(); + void FlushStyles(); + void Error(const char *s); + + void ReadObject(); + + RichTable& Table(); + void TableFormat(bool bw = false); + void FinishCell(); + void FinishTable(); + void FinishOldTable(); + void S(int& x, int a); + + void EndPart(); + + void Cat(int chr); + +public: + struct Exc {}; + + RichText target; + + void Parse(const char *qtf, byte accesskey); + + RichQtfParser(bool scolors); +}; + +void init_s_nodeqtf(); + +RichQtfParser::RichQtfParser(bool _scolors) +{ + format.Face(Font::ARIAL); + format.Height(100); + format.charset = GetDefaultCharset(); + format.language = 0; + breakpage = false; + istable = false; + oldtab = false; + init_s_nodeqtf(); + scolors = _scolors; +} + +bool RichQtfParser::Key2(int c, int d) +{ + if(term[0] == c && term[1] == d) { + term += 2; + return true; + } + return false; +} + +int RichQtfParser::GetNumber() +{ + int n = 0; + int sgn = 1; + if(*term == '-') { + sgn = -1; + term++; + } + while(IsDigit(*term)) + n = n * 10 + *term++ - '0'; + return sgn * n; +} + +String RichQtfParser::GetText(int delim) { + String s; + for(;;) { + if(*term == '\0') return s; + if(*term == '`') { + term++; + if(*term == '\0') return s; + s.Cat(*term++); + } + else + if(*term == delim) { + term++; + return s; + } + else + s.Cat(*term++); + } +} + +int RichQtfParser::ReadNumber() +{ + if(!IsDigit(*term)) + Error("Expected number"); + return GetNumber(); +} + +void RichQtfParser::Number2(int& a, int& b) +{ + a = -1; + b = -1; + if(IsDigit(*term)) + a = GetNumber(); + if(*term == '/') { + term++; + b = GetNumber(); + } +} + +Color RichQtfParser::GetColor() +{ + int c = *term++; + if(c == '(') { + byte r = GetNumber(); + if(Key(')')) { + r &= 255; + return Color(r, r, r); + } + Key('.'); + byte g = GetNumber(); + Key('.'); + byte b = GetNumber(); + Key(')'); + return Color(r & 255, g & 255, b & 255); + } + else + if(c >= '0' && c <= '9') + return (*((scolors ? QTFSColor : QTFColor)[c - '0']))(); + else + if(c >= 'a' && c <= 'z') + return (*((scolors ? QTFSColorl : QTFColorl)[c - 'a']))(); + else + if(c >= 'A' && c <= 'Z') + return (*((scolors ? QTFSColorL : QTFColorL)[c - 'A']))(); + else + return Red; +} + +void RichQtfParser::SetFormat() +{ + paragraph.format = format; +} + +void RichQtfParser::Flush() { + if(text.GetLength()) { + ASSERT(!istable); + paragraph.Cat(text, format); + text.Clear(); + } +} + +void RichQtfParser::EndPart() +{ + if(istable) { + if(paragraph.GetCount() == 0 && text.GetCount() == 0) + if(table.GetCount()) + table.Top().text.CatPick(tablepart); + else + target.CatPick(tablepart); + else { + paragraph.part.Clear(); + text.Clear(); + } + } + else { + Flush(); + bool b = paragraph.format.newpage; + if(breakpage) + paragraph.format.newpage = true; + if(table.GetCount()) + table.Top().text.Cat(paragraph, target.GetStyles()); + else + target.Cat(paragraph); + paragraph.part.Clear();; + paragraph.format.newpage = b; + SetFormat(); + breakpage = false; + } + istable = false; +} + +void RichQtfParser::ReadObject() +{ + Flush(); + RichObject obj; + if(*term == '#') { + term++; + #ifdef CPU_64 + obj = *(RichObject *)stou64(term, &term); + #else + obj = *(RichObject *)stou(term, &term); + #endif + term++; + } + else { + String type; + while(IsAlNum(*term) || *term == '_') + type.Cat(*term++); + Size sz; + Key(':'); + sz.cx = ReadNumber(); + bool keepratio = false; + if(Key('&')) + keepratio = true; + else + Key('*'); + sz.cy = ReadNumber(); + int yd = 0; + if(Key('/')) + yd = GetNumber(); + StringBuffer data; + for(;;) { + while(*term < 32 && *term > 0) term++; + if((byte)*term >= ' ' && (byte)*term <= 127 || *term == '\0') break; + byte seven = *term++; + for(int i = 0; i < 7; i++) { + while((byte)*term < 32 && (byte)*term > 0) term++; + if((byte)*term >= ' ' && (byte)*term <= 127 || *term == '\0') break; + data.Cat((*term++ & 0x7f) | ((seven << 7) & 0x80)); + seven >>= 1; + } + } + obj.Read(type, data, sz); + obj.KeepRatio(keepratio); + obj.SetYDelta(yd); + } + paragraph.Cat(obj, format); +} + +int NoLow(int c) { + return (byte)c >= 32 ? c : 0; +} + +void RichQtfParser::Error(const char *s) { + RichPara::CharFormat ef; + (Font&) ef = Arial(84).Bold().Underline(); + ef.ink = Red; + paragraph.Cat(("ERROR: " + String(s) + ": " + + Filter(String(term, min((int)strlen(term), 20)), NoLow)).ToWString(), ef); + target.Cat(paragraph); + FlushStyles(); + throw Exc(); +} + +void RichQtfParser::FlushStyles() +{ + for(int i = 0; i < styleid.GetCount(); i++) + if(stylenext[i] >= 0 && stylenext[i] < styleid.GetCount()) { + RichStyle s = target.GetStyle(styleid[i]); + s.next = styleid[stylenext[i]]; + target.SetStyle(styleid[i], s); + } +} + +RichTable& RichQtfParser::Table() +{ + if(table.GetCount() == 0) + Error("Not in table"); + return table.Top().table; +} + +void RichQtfParser::S(int& x, int a) +{ + if(a >= 0) + x = a; +} + +void RichQtfParser::TableFormat(bool bw) +{ + RichTable& tab = Table(); + Tab& t = table.Top(); + int a, b; + for(;;) { + if(bw && IsDigit(*term)) { + t.hspan = GetNumber(); + } + else + switch(*term++) { + case ' ': return; + case ';': break; + case '<': tab.format.lm = ReadNumber(); break; + case '>': tab.format.rm = ReadNumber(); break; + case 'B': tab.format.before = ReadNumber(); break; + case 'A': tab.format.after = ReadNumber(); break; + case 'f': tab.format.frame = ReadNumber(); break; + case '_': + case 'F': tab.format.framecolor = GetColor(); break; + case 'g': tab.format.grid = ReadNumber(); break; + case 'G': tab.format.gridcolor = GetColor(); break; + case 'h': tab.format.header = GetNumber(); break; + case '~': tab.format.frame = tab.format.grid = 0; break; + case '^': t.format.align = ALIGN_TOP; break; + case '=': t.format.align = ALIGN_CENTER; break; + case 'v': t.format.align = ALIGN_BOTTOM; break; + case 'l': Number2(a, b); S(t.format.border.left, a); S(t.format.margin.left, b); break; + case 'r': Number2(a, b); S(t.format.border.right, a); S(t.format.margin.right, b); break; + case 't': Number2(a, b); S(t.format.border.top, a); S(t.format.margin.top, b); break; + case 'b': Number2(a, b); S(t.format.border.bottom, a); S(t.format.margin.bottom, b); break; + case 'H': t.format.minheight = ReadNumber(); break; + case '@': t.format.color = GetColor(); break; + case 'R': t.format.bordercolor = GetColor(); break; + case '!': t.format = RichCell::Format(); break; + case 'k': t.format.keep = true; + case 'K': tab.format.keep = true; + case 'a': + Number2(a, b); + if(a >= 0) + t.format.border.left = t.format.border.right = t.format.border.top = t.format.border.bottom = a; + if(b >= 0) + t.format.margin.left = t.format.margin.right = t.format.margin.top = t.format.margin.bottom = b; + break; //!!cell all lines + case '-': t.hspan = GetNumber(); break; + case '+': + case '|': t.vspan = GetNumber(); break; + default: + Error("Invalid cell format"); + } + } +} + +void RichQtfParser::FinishCell() +{ + EndPart(); + RichTable& t = Table(); + Tab& b = table.Top(); + int i, j; + if(oldtab) { + i = b.rown.GetCount() - 1; + j = b.rown.Top(); + b.rown.Top()++; + } + else { + i = b.cell / t.GetColumns(); + j = b.cell % t.GetColumns(); + } + t.SetPick(i, j, b.text); + b.text.Clear(); + t.SetFormat(i, j, b.format); + t.SetSpan(i, j, b.vspan, b.hspan); + if(oldtab && b.rown.GetCount() > 1 && j + 1 < b.rown[0]) + b.format = t.GetFormat(0, j + 1); + else { + b.cell++; + b.vspan = 0; + b.hspan = oldtab; + } + b.format.keep = false; +} + +void RichQtfParser::FinishTable() +{ + FinishCell(); + tablepart = Table(); + istable = true; + table.Drop(); +} + +void RichQtfParser::FinishOldTable() +{ + FinishCell(); + Index pos; + Vector srow; + RichTable& t = Table(); + Tab& b = table.Top(); + for(int i = 0; i < t.GetRows(); i++) { + int& s = srow.Add(); + s = 0; + int nx = b.rown[i]; + for(int j = 0; j < nx; j++) + s += t.GetSpan(i, j).cx; + int xn = 0; + for(int j = 0; j < nx; j++) { + pos.FindAdd(xn * 10000 / s); + xn += t.GetSpan(i, j).cx; + } + } + Vector h = pos.PickKeys(); + if(h.GetCount() == 0) + Error("table"); + Sort(h); + pos = h; + pos.Add(10000); + RichTable tab; + tab.SetFormat(t.GetFormat()); + for(int i = 0; i < pos.GetCount() - 1; i++) { + tab.AddColumn(pos[i + 1] - pos[i]); + } + for(int i = 0; i < t.GetRows(); i++) { + int s = srow[i]; + int nx = b.rown[i]; + int xn = 0; + int xi = 0; + for(int j = 0; j < nx; j++) { + Size span = t.GetSpan(i, j); + xn += span.cx; + int nxi = pos.Find(xn * 10000 / s); + tab.SetPick(i, xi, t.GetPick(i, j)); + tab.SetFormat(i, xi, t.GetFormat(i, j)); + tab.SetSpan(i, xi, max(span.cy - 1, 0), nxi - xi - 1); + xi = nxi; + } + } + table.Drop(); + if(table.GetCount()) + table.Top().text.CatPick(tab); + else + target.CatPick(tab); + oldtab = false; +} + +void RichQtfParser::Cat(int chr) +{ + if(accesskey && ToUpper(ToAscii(chr)) == accesskey) { + Flush(); + format.Underline(!format.IsUnderline()); + text.Cat(chr); + Flush(); + format.Underline(!format.IsUnderline()); + accesskey = 0; + } + else { + text.Cat(chr); + } +} + +extern bool s_nodeqtf[128]; + +void RichQtfParser::Parse(const char *qtf, byte _accesskey) +{ + accesskey = _accesskey; + term = qtf; + while(*term) { + if(Key('[')) { + Flush(); + fstack.Add(format); + for(;;) { + int c = *term; + if(!c) + Error("Unexpected end of text"); + term++; + if(c == ' ' || c == '\n') break; + switch(c) { + case 's': { + Uuid id; + c = *term; + if(Key('\"') || Key('\'')) + id = target.GetStyleId(GetText(c)); + else { + int i = ReadNumber(); + if(i >= 0 && i < styleid.GetCount()) + id = styleid[i]; + else + id = RichStyle::GetDefaultId(); + } + const RichStyle& s = target.GetStyle(id); + bool p = format.newpage; + int lng = format.language; + (RichPara::Format&) format = s.format; + format.styleid = id; + format.language = lng; + format.newpage = p; + break; + } + case '/': format.Italic(!format.IsItalic()); break; + case '*': format.Bold(!format.IsBold()); break; + case '_': format.Underline(!format.IsUnderline()); break; + case 'T': format.NonAntiAliased(!format.IsNonAntiAliased()); break; + case '-': format.Strikeout(!format.IsStrikeout()); break; + case 'c': format.capitals = !format.capitals; break; + case 'd': format.dashed = !format.dashed; break; + case '`': format.sscript = format.sscript == 1 ? 0 : 1; break; + case ',': format.sscript = format.sscript == 2 ? 0 : 2; break; + case '^': format.link = GetText('^'); break; + case 'I': format.indexentry = FromUtf8(GetText(';')); break; + case '+': format.Height(GetNumber()); break; + case '@': format.ink = GetColor(); break; + case '$': format.paper = GetColor(); break; + case 'A': format.Face(Font::ARIAL); break; + case 'R': format.Face(Font::ROMAN); break; + case 'C': format.Face(Font::COURIER); break; + case 'G': format.Face(Font::STDFONT); break; + case 'S': format.Face(Font::SYMBOL); break; + case '.': { + int n = GetNumber(); + if(n >= Font::GetFaceCount()) + Error("Invalid face number"); + format.Face(n); break; + } + case '!': { + String fn = GetText('!'); + int i = Font::FindFaceNameIndex(fn); + if(i < 0) + i = Font::ARIAL; + format.Face(i); + } + break; + case '{': { + String cs = GetText('}'); + if(cs.GetLength() == 1) { + int c = *cs; + if(c == '_') + format.charset = CHARSET_UTF8; + if(c >= '0' && c <= '8') + format.charset = c - '0' + CHARSET_WIN1250; + if(c >= 'A' && c <= 'Z') + format.charset = c - '0' + CHARSET_ISO8859_1; + } + else { + for(int i = 0; i < CharsetCount(); i++) + if(stricmp(CharsetName(i), cs) == 0) { + format.charset = i; + break; + } + } + break; + } + case '%': { + String h; + if(*term == '-') { + format.language = 0; + term++; + } + else + if(*term == '%') { + format.language = LNG_ENGLISH; + term++; + } + else { + while(*term && h.GetLength() < 5) + h.Cat(*term++); + format.language = LNGFromText(h); + } + break; + } + default: + if(c >= '0' && c <= '9') { + format.Height(QTFFontHeight[c - '0']); + break; + } + switch(c) { + case ':': format.label = GetText(':'); break; + case '<': format.align = ALIGN_LEFT; break; + case '>': format.align = ALIGN_RIGHT; break; + case '=': format.align = ALIGN_CENTER; break; + case '#': format.align = ALIGN_JUSTIFY; break; + case 'l': format.lm = GetNumber(); break; + case 'r': format.rm = GetNumber(); break; + case 'i': format.indent = GetNumber(); break; + case 'b': format.before = GetNumber(); break; + case 'a': format.after = GetNumber(); break; + case 'P': format.newpage = !format.newpage; break; + case 'k': format.keep = !format.keep; break; + case 'K': format.keepnext = !format.keepnext; break; + case 'H': format.ruler = GetNumber(); break; + case 'h': format.rulerink = GetColor(); break; + case 'Q': format.orphan = !format.orphan; break; + case 'n': format.before_number = GetText(';'); break; + case 'm': format.after_number = GetText(';'); break; + case 'N': { + memset(format.number, 0, sizeof(format.number)); + format.reset_number = false; + int i = 0; + while(i < 8) { + int c; + if(Key('-')) + c = RichPara::NUMBER_NONE; + else + if(Key('1')) + c = RichPara::NUMBER_1; + else + if(Key('0')) + c = RichPara::NUMBER_0; + else + if(Key('a')) + c = RichPara::NUMBER_a; + else + if(Key('A')) + c = RichPara::NUMBER_A; + else + if(Key('i')) + c = RichPara::NUMBER_i; + else + if(Key('I')) + c = RichPara::NUMBER_I; + else + break; + format.number[i++] = c; + } + if(Key('!')) + format.reset_number = true; + break; + } + case 'o': format.bullet = RichPara::BULLET_ROUND; + format.indent = 150; break; + case 'O': + if(Key('_')) + format.bullet = RichPara::BULLET_NONE; + else { + int c = *term++; + if(!c) + Error("Unexpected end of text"); + format.bullet = + c == '1' ? RichPara::BULLET_ROUNDWHITE : + c == '2' ? RichPara::BULLET_BOX : + c == '3' ? RichPara::BULLET_BOXWHITE : + c == '9' ? RichPara::BULLET_TEXT : + RichPara::BULLET_ROUND; + } + break; + case 'p': + switch(*term++) { + case 0: Error("Unexpected end of text"); + case 'h': format.linespacing = RichPara::LSP15; break; + case 'd': format.linespacing = RichPara::LSP20; break; + default: format.linespacing = RichPara::LSP10; + } + break; + case 't': + if(IsDigit(*term)) //temporary fix... :( + format.tabsize = ReadNumber(); + break; + case '~': { + if(Key('~')) + format.tab.Clear(); + else { + RichPara::Tab tab; + Key('<'); + if(Key('>')) + tab.align = ALIGN_RIGHT; + if(Key('=')) + tab.align = ALIGN_CENTER; + if(Key('.')) + tab.fillchar = 1; + if(Key('-')) + tab.fillchar = 2; + if(Key('_')) + tab.fillchar = 3; + tab.pos = ReadNumber(); + format.tab.Add(tab); + } + } + break; + default: + continue; + } + } + } + SetFormat(); + } + else + if(Key(']')) { + Flush(); + if(fstack.GetCount()) { + format = fstack.Top(); + fstack.Drop(); + } + else + Error("Unmatched ']'"); + } + else + if(Key2('{')) { + if(oldtab) + Error("{{ in ++ table"); + if(text.GetLength() || paragraph.GetCount()) + EndPart(); + table.Add(); + int r = IsDigit(*term) ? ReadNumber() : 1; + Table().AddColumn(r); + while(Key(':')) + Table().AddColumn(ReadNumber()); + TableFormat(); + } + else + if(Key2('}')) { + if(oldtab) + Error("}} in ++ table"); + FinishTable(); + } + else + if(Key2('+')) + if(oldtab) + FinishOldTable(); + else { + Flush(); + if(text.GetLength() || paragraph.GetCount()) + EndPart(); + Tab& b = table.Add(); + b.rown.Add(0); + b.hspan = 1; + b.Old(); + oldtab = true; + } + else + if(Key2('|')) + FinishCell(); + else + if(Key2('-')) { + FinishCell(); + table.Top().rown.Add(0); + } + else + if(Key2(':')) { + if(!oldtab) + FinishCell(); + TableFormat(oldtab); + } + else + if(Key2('^')) { + EndPart(); + breakpage = true; + } + else + if(Key2('@')) { + ReadObject(); + } + else + if(Key2('@', '$')) { + String xu; + while(isxdigit(*term)) + xu.Cat(*term++); + int c = stou(~xu, NULL, 16); + if(c >= 32) + Cat(c); + if(*term == ';') + term++; + SetFormat(); + } + else + if(Key2('{', ':')) { + Flush(); + String field = GetText(':'); + String param = GetText(':'); + Id fid(field); + if(RichPara::fieldtype().Find(fid) >= 0) + paragraph.Cat(fid, param, format); + Key('}'); + } + else + if(Key('&')) { + SetFormat(); + EndPart(); + } + else + if(Key2('$')) { + Flush(); + int i = GetNumber(); + Uuid id; + RichStyle style; + style.format = format; + if(Key(',')) + stylenext.At(i, 0) = GetNumber(); + else + stylenext.At(i, 0) = i; + if(Key('#')) { + String xu; + while(isxdigit(*term)) + xu.Cat(*term++); + if(xu.GetLength() != 32) + Error("Invalid UUID !"); + id = ScanUuid(xu); + } + else + if(i) + id = Uuid::Create(); + else + id = RichStyle::GetDefaultId(); + if(Key(':')) + style.name = GetText(']'); + if(fstack.GetCount()) { + format = fstack.Top(); + fstack.Drop(); + } + target.SetStyle(id, style); + styleid.At(i, RichStyle::GetDefaultId()) = id; + if(id == RichStyle::GetDefaultId()) { + bool p = format.newpage; + int lng = format.language; + (RichPara::Format&) format = style.format; + format.styleid = id; + format.language = lng; + format.newpage = p; + } + } + else + if(*term == '_') { + SetFormat(); + text.Cat(160); + term++; + } + else + if(Key2('-', '|')) { + SetFormat(); + text.Cat(9); + } + else + if(*term == '\1') { + if(istable) + EndPart(); + SetFormat(); + const char *b = ++term; + for(; *term && *term != '\1'; term++) + if(*term == '\n') { + text.Cat(ToUnicode(b, (int)(term - b), format.charset)); + EndPart(); + b = term + 1; + } + text.Cat(ToUnicode(b, (int)(term - b), format.charset)); + if(*term == '\1') + term++; + } + else { + if(!Key('`')) Key('\\'); + if((byte)*term >= ' ') { + SetFormat(); + do { + if(istable) + EndPart(); + if(format.charset == CHARSET_UTF8) { + word code = (byte)*term++; + if(code <= 0x7F) + Cat(code); + else + if(code <= 0xDF) { + if(*term == '\0') break; + int c0 = (byte)*term++; + if(c0 < 0x80) + Error("Invalid UTF-8 sequence"); + Cat(((code - 0xC0) << 6) + c0 - 0x80); + } + else + if(code <= 0xEF) { + int c0 = (byte)*term++; + int c1 = (byte)*term++; + if(c0 < 0x80 || c1 < 0x80) + Error("Invalid UTF-8 sequence"); + Cat(((code - 0xE0) << 12) + ((c0 - 0x80) << 6) + c1 - 0x80); + } + else + Error("Invalid UTF-8 sequence"); + } + else + Cat(ToUnicode((byte)*term++, format.charset)); + } + while((byte)*term >= 128 || s_nodeqtf[(byte)*term]); + } + else + if(*term) + term++; + } + } + if(paragraph.GetCount() == 0) + SetFormat(); + if(oldtab) + FinishOldTable(); + else + while(table.GetCount()) + FinishTable(); + EndPart(); + FlushStyles(); +} + +RichText ParseQTF(const char *qtf, bool scolors, byte accesskey) +{ + RichQtfParser p(scolors); + try { + p.Parse(qtf, accesskey); + } + catch(RichQtfParser::Exc) {} + return p.target; +} + +String QtfRichObject::ToString() const +{ + return String("@@#").Cat() << uintptr_t(&obj) << ";"; +} + +QtfRichObject::QtfRichObject(const RichObject& o) + : obj(o) +{} + +END_UPP_NAMESPACE diff --git a/uppdev/RichTextP/ParseRTF.cpp b/uppdev/RichTextP/ParseRTF.cpp new file mode 100644 index 000000000..12e39adb9 --- /dev/null +++ b/uppdev/RichTextP/ParseRTF.cpp @@ -0,0 +1,1106 @@ +#include "RichText.h" + +NAMESPACE_UPP + +#define LLOG(x) LOG(x) + +static int TwipDotsLim(int twips) { return minmax(TwipDots(twips), 0, MAX_DOTS); } + +static String FromCString(const char *p, const char **endptr = NULL) +{ + if(endptr) { + const char *e = p; + if(*e == '\"') + e++; + while(*e && *e != '\"') + if(*e++ == '\\' && *e) + e++; + if(*e == '\"') + e++; + *endptr = e; + } + + try { + CParser parser(p); + return parser.ReadOneString(); + } + catch(Exc e) { + return Null; + } +} + +class RTFParser +{ +public: + RTFParser(const char *rtf); + + RichText Run(); + +private: + enum TOKEN { T_EOF, T_TEXT, T_COMMAND, T_GROUP, T_END_GROUP }; + + void Flush(bool force, int itap); + void OpenTable(int level); + void FlushTable(int level); + TOKEN Fetch(); + void Skip(); + TOKEN Token() { if(!is_full) Fetch(); return token; } + bool PassIf(bool c) { is_full &= !c; return c; } + bool PassText() { return PassIf(Token() == T_TEXT); } + bool PassGroup() { return PassIf(Token() == T_GROUP); } + bool PassEndGroup() { return PassIf(Token() == T_END_GROUP || token == T_EOF); } + bool PassEndGroup(int level); + bool PassCmd(const char *cmd) { return PassIf(Token() == T_COMMAND && command == cmd); } + bool PassQ(const char *cmd) { return PassIf(command == cmd); } + + void SkipGroup() { SkipGroup(stack.GetCount()); } + void SkipGroup(int level); + int Level() const { return stack.GetCount(); } + + void ReadItem(); + void ReadItemGroup(int level); + void ReadItemGroup() { ReadItemGroup(Level()); } + void ReadText(); + void ReadText(const WString& text); + void ReadChar(word ch) { ReadText(WString(ch, 1)); } + void ReadCommand(); + + void ReadHeader(); + void ReadFaceTable(); + void ReadColorTable(); + void ReadCharSet(); + + void ReadMisc(); + void ReadField(); + bool ReadField(const char *def); + void ReadPict(); + void ReadShape(); + + void ReadParaStyle(); + void ReadTableStyle(); + void DefaultParaStyle(); + + void ReadCharStyle(); + + String ReadBinHex(char& odd) const; + +private: + const char *rtf_begin; + const char *rtf; + + TOKEN token; + bool is_full; +// bool new_dest; + bool next_command; + WString text; + String command; + int command_arg; + + struct Cell { + Cell() : end_dots(0), merge_first(false), merge(false) {} + RichTxt text; + RichCell::Format format; + int end_dots; + bool merge_first; + bool merge; + }; + + struct Face : Moveable { + int face; + byte charset; + }; + + struct TableState { + TableState() : textcol(0), stylecol(0) { cells.Add(); } + + RichTable::Format tableformat; + Vector< Array > cells; + int textcol; + int stylecol; + }; + + struct State { + String dest; + RichPara::Format format; + RichPara::CharFormat charformat; + RichCell::Format cellformat; + Rect rowmargin; + Rect rowmarginunits; + Rect cellmarginunits; + int uc_value; + int left_margin; + int right_margin; + int first_indent; + bool in_table; + int itap; + bool nestprop; + bool new_dest; + byte charset; + }; + + Array stack; + Array table_stack; + State state; + RichPara::CharFormat plain_format; + RichPara::Format pard_format; + RichCell::Format std_cell_format; + byte plain_charset; + byte default_charset; + int default_font; + Alignment tab_align; + byte tab_fill; + Vector face_table; + Vector color_table; + int paper_width; + int left_margin; + int right_margin; + + RichText output; + RichPara para; +}; + +RichText ParseRTF(const char *rtf) { return RTFParser(rtf).Run(); } + +RTFParser::RTFParser(const char *rtf) +: rtf_begin(rtf) +, rtf(rtf) +{ +#ifdef _DEBUG + SaveFile(ConfigFile("rtfparser.rtf"), rtf); + LOG(rtf); +#endif + is_full = false; + next_command = false; + default_font = 0; + plain_charset = default_charset = state.charset = CHARSET_WIN1250; + state.uc_value = 1; + state.new_dest = false; + plain_format.Face(Font::ARIAL).Height(100); + std_cell_format.align = ALIGN_TOP; + std_cell_format.margin = Rect(25, 25, 25, 25); + DefaultParaStyle(); + state.charformat = plain_format; + tab_align = ALIGN_LEFT; + tab_fill = 0; + paper_width = 5100; + left_margin = right_margin = 750; +} + +RichText RTFParser::Run() +{ + if(!PassGroup() || !PassCmd("rtf") || command_arg != 1 && !IsNull(command_arg)) + return output; + while(Token() != T_EOF) + ReadItem(); + Flush(false, 1); + FlushTable(0); + return output; +} + +void RTFParser::FlushTable(int level) +{ + while(table_stack.GetCount() > level) { + TableState& child = table_stack.Top(); + while(!child.cells.IsEmpty() && child.cells.Top().IsEmpty()) + child.cells.Drop(); + if(child.cells.IsEmpty()) { + table_stack.Drop(); + continue; + } + Index dot_index; + int pos = child.tableformat.lm; + dot_index.Add(pos); + for(int r = 0; r < child.cells.GetCount(); r++) { + Array& rw = child.cells[r]; + int pos = 0; + for(int c = 0; c < rw.GetCount(); c++) + dot_index.FindAdd(rw[c].end_dots = pos = max(pos + 100, rw[c].end_dots)); + } + Vector dot_order = dot_index.PickKeys(); + Sort(dot_order); + RichTable table; + if(table_stack.GetCount() == 1) + child.tableformat.rm = max(paper_width - left_margin - right_margin - dot_order.Top(), 0); +// child.tableformat.before = state.format.before; +// child.tableformat.after = state.format.after; + table.SetFormat(child.tableformat); + for(int c = 1; c < dot_order.GetCount(); c++) + table.AddColumn(dot_order[c] - dot_order[c - 1]); + dot_index = dot_order; + for(int r = 0; r < child.cells.GetCount(); r++) { + Array& rw = child.cells[r]; + int pos = child.tableformat.lm; + for(int c = 0; c < rw.GetCount(); c++) { + Cell& cell = rw[c]; + if(cell.merge) + continue; + int vspan = 0; + if(cell.merge_first) { + for(int m = r + 1; m < child.cells.GetCount(); m++) { + const Array& mrw = child.cells[m]; + int mc = mrw.GetCount(); + while(--mc >= 0 && mrw[mc].end_dots > cell.end_dots) + ; + if(mc >= 0 && mrw[mc].end_dots == cell.end_dots && mrw[mc].merge) + vspan++; + else + break; + } + } + int nbegin = dot_index.Find(pos); + int hspan = dot_index.Find(pos = cell.end_dots) - nbegin - 1; + hspan = max(hspan, 0); + vspan = max(vspan, 0); +// ASSERT(hspan >= 0 && vspan >= 0); + if(hspan || vspan) + table.SetSpan(r, nbegin, vspan, hspan); + table.SetFormat(r, nbegin, cell.format); + cell.text.Normalize(); + table.SetPick(r, nbegin, cell.text); + } + } + table.Normalize(); + table_stack.Drop(); + if(table_stack.IsEmpty()) + output.CatPick(table); + else { + TableState& par = table_stack.Top(); + par.cells.Top().At(par.textcol).text.CatPick(table); + } + } +} + +void RTFParser::Flush(bool force, int itap) +{ + if(!para.part.IsEmpty() || force) { + int fi = state.first_indent, li = state.left_margin, ri = state.right_margin; + if(state.format.bullet != RichPara::BULLET_NONE) { + li += fi; + fi = -fi; + } + state.format.indent = minmax(fi, 0, MAX_DOTS); + state.format.lm = minmax(li, 0, MAX_DOTS); + state.format.rm = minmax(ri, 0, MAX_DOTS); + para.format = state.format; + if(state.in_table) { + FlushTable(itap); + OpenTable(itap); + TableState& ts = table_stack[itap - 1]; + ts.cells.Top().At(ts.textcol).text.Cat(para, output.GetStyles()); + } + else { + FlushTable(0); + output.Cat(para); + } + para.part.Clear(); + } + else + FlushTable(itap); +} + +RTFParser::TOKEN RTFParser::Fetch() +{ + is_full = true; + text = WString(); + if(next_command) + { + next_command = false; + return token = T_COMMAND; + } + + text = Null; + command = Null; + command_arg = Null; + + int skip = 0; + while(*rtf && *rtf != '{' && *rtf != '}') + { + int c = 0, nskip = max(skip - 1, 0); + if((byte)*rtf < ' ') + rtf++; + else if(*rtf != '\\') + c = ToUnicode(*rtf++, state.charset); + else + switch(rtf++, *rtf++) + { + case 0: { + rtf--; + break; + } + + case '{': + case '}': + case '\\': { + c = rtf[-1]; + break; + } + + case '~': { + c = 160; + break; + } + + case '|': + case '-': + case '_': + case ':': { + command = String(rtf - 1, 1); + if(text.IsEmpty()) + return token = T_COMMAND; + next_command = true; + return token = T_TEXT; + } + + case '\'': { + int c1 = ctoi(*rtf); + if(c1 < 16) { + int c2 = ctoi(*++rtf); + if(c2 < 16) { + c1 = c1 * 16 + c2; + rtf++; + } + c = ToUnicode(c1, state.charset); + } + break; + } + + default: { + if(IsAlpha(*--rtf) || *rtf == '*' && rtf[1] == '\\' && IsAlpha(rtf[2])) { + if(*rtf == '*') { + rtf += 2; + state.new_dest = true; + } + const char *b = rtf; + while(IsAlpha(*++rtf)) + ; + command = String(b, rtf); + if(IsDigit(*rtf) || *rtf == '-') + command_arg = strtol(rtf, (char **)&rtf, 10); + if(*rtf == ' ') + rtf++; + if(command == "uc") + state.uc_value = command_arg; + else if(command == "u") { + c = command_arg; + nskip = state.uc_value; + } + else { // command - quit reading text + if(text.IsEmpty()) + return token = T_COMMAND; + next_command = true; + return token = T_TEXT; + } + } + break; + } + } + if(c && !skip) + text.Cat(c); + skip = nskip; + } + + if(!text.IsEmpty()) + return token = T_TEXT; + + if(*rtf == '{') { + stack.Add(state); + rtf++; + return token = T_GROUP; + } + + if(*rtf == '}') { + if(!stack.IsEmpty()) { + state = stack.Top(); + stack.Drop(); + } + rtf++; + return token = T_END_GROUP; + } + + return token = T_EOF; +} + +bool RTFParser::PassEndGroup(int level) +{ + if(Token() == T_EOF) + return true; + if(token != T_END_GROUP) + return false; + is_full = false; + return Level() < level; +} + +void RTFParser::Skip() +{ + bool is_group = (token == T_GROUP || token == T_COMMAND && state.new_dest); + is_full = false; + if(is_group) + SkipGroup(); +} + +void RTFParser::SkipGroup(int level) +{ + while(!PassEndGroup(level)) + is_full = false; +} + +void RTFParser::ReadItem() +{ + const char *p = rtf; + if(token == T_COMMAND) + ReadCommand(); + else if(token == T_TEXT) + ReadText(); + if(rtf == p && is_full) { + is_full = false; + if(token == T_COMMAND && state.new_dest) + SkipGroup(); + } +} + +void RTFParser::ReadItemGroup(int level) +{ + while(!PassEndGroup(level)) + ReadItem(); +} + +void RTFParser::ReadText() +{ + if(!IsNull(text)) + ReadText(text); +} + +void RTFParser::ReadText(const WString& text) +{ + if(!IsNull(state.dest)) + return; + LLOG("Output text: <" << FromUnicode(text, state.charset) << ">, " << state.charformat); + para.Cat(text, state.charformat); +} + +void RTFParser::ReadCommand() +{ + if(Token() == T_COMMAND) ReadHeader(); + if(Token() == T_COMMAND) ReadMisc(); + if(Token() == T_COMMAND) ReadParaStyle(); + if(Token() == T_COMMAND) ReadTableStyle(); + if(Token() == T_COMMAND) ReadCharStyle(); +} + +void RTFParser::ReadHeader() +{ + if(PassCmd("deff")) + default_font = command_arg; + else if(PassQ("fonttbl")) { + state.dest = command; + ReadFaceTable(); + } + else if(PassQ("colortbl")) { + state.dest = command; + ReadColorTable(); + } + else if(PassQ("stylesheet") || PassQ("list") || PassQ("listoverride") || PassQ("info")) { + state.dest = command; + SkipGroup(); + } + else if(Token() == T_COMMAND) + ReadCharSet(); +} + +void RTFParser::ReadCharSet() +{ + if(PassQ("ansi")) {} + else if(PassQ("mac")) {} + else if(PassQ("pc")) {} + else if(PassQ("pca")) {} + else if(PassQ("ansicpg")) { + static const struct { + int ansicpg; + byte charset; + } + charsets[] = + { + { 1250, CHARSET_WIN1250 }, + { 1251, CHARSET_WIN1251 }, + { 1252, CHARSET_WIN1252 }, + { 1253, CHARSET_WIN1253 }, + { 1254, CHARSET_WIN1254 }, + { 1255, CHARSET_WIN1255 }, + { 1256, CHARSET_WIN1256 }, + { 1257, CHARSET_WIN1257 }, + }; + for(int c = 0; c < __countof(charsets); c++) + if(charsets[c].ansicpg == command_arg) { + default_charset = state.charset = charsets[c].charset; + break; + } + } +} + +void RTFParser::ReadFaceTable() +{ + int fx = 0; + while(!PassEndGroup()) { + if(!PassGroup()) { + Skip(); + continue; + } + Face n; + n.face = Font::ARIAL; + n.charset = default_charset; + while(!PassEndGroup()) { + if(PassCmd("f")) + fx = command_arg; + else if(PassCmd("fnil")) + ; + else if(PassCmd("froman")) + n.face = Font::ROMAN; + else if(PassCmd("fswiss")) + n.face = Font::ARIAL; + else if(PassCmd("fmodern")) + n.face = Font::COURIER; + else if(PassCmd("ftech")) + n.face = Font::SYMBOL; + else if(PassCmd("fcharset")) { + switch(command_arg) { + case 0: n.charset = CHARSET_WIN1252; break; // ANSI + case 1: n.charset = default_charset; break; // Default + case 2: n.charset = CHARSET_WIN1252; break; // Symbol + case 3: break; // Invalid + case 77: break; // Mac + case 128: break; // Shift Jis + case 129: break; // Hangul + case 130: break; // Johab + case 134: break; // GB2312 + case 136: break; // Big5 + case 161: n.charset = CHARSET_WIN1253; break; // Greek + case 162: n.charset = CHARSET_WIN1254; break; // Turkish + case 163: break; // Vietnamese + case 177: n.charset = CHARSET_WIN1255; break; // Hebrew + case 178: break; // Arabic + case 179: break; // Arabic Traditional + case 180: break; // Arabic user + case 181: break; // Hebrew user + case 186: break; // Baltic + case 204: n.charset = CHARSET_WIN1251; break; // Russian + case 222: break; // Thai + case 238: n.charset = CHARSET_WIN1250; break; // Eastern European + case 254: break; // PC 437 + case 255: n.charset = CHARSET_WIN1252; break; // OEM + } + } +/* else if(PassText()) { + String s = FromUnicode(text, charset); + if(!s.IsEmpty() && *s.Last() == ';') + s.Trim(s.GetLength() - 1); + if(!s.IsEmpty()) + f = Font::FindFaceNameIndex(s); + } + else if(PassGroup()) { + int level = Level(); + if(PassCmd("falt") && PassText() && f < 0) + f = Font::FindFaceNameIndex(FromUnicode(text, charset)); + SkipGroup(level); + }*/ //Cxl 2005-11-29 - "Arial CE" makes mess here! + else + Skip(); + } + if(fx >= 0 && fx < MAX_FONTS) { +// if(f < 0) // Cxl 2005-11-29 + if(default_font == fx) { + plain_format.Face(n.face); + plain_charset = n.charset; + } + Face dflt; + dflt.face = Font::ARIAL; + dflt.charset = default_charset; + face_table.At(fx++, dflt) = n; + } + } +} + +void RTFParser::ReadColorTable() +{ + int r = Null, g = Null, b = Null; + for(; !PassEndGroup(); Skip()) + if(PassCmd("red")) + r = command_arg; + else if(PassCmd("green")) + g = command_arg; + else if(PassCmd("blue")) + b = command_arg; + else if(PassText()) + { + Color c = Null; + if(!IsNull(r) || !IsNull(g) || !IsNull(b)) + c = Color(Nvl(r, 0), Nvl(g, 0), Nvl(b, 0)); + color_table.Add(c); + } +} + +void RTFParser::ReadMisc() +{ + if(PassQ("field")) + ReadField(); + else if(PassQ("pict")) + ReadPict(); + else if(PassQ("shpinst")) + ReadShape(); + else if(PassQ("endash")) + ReadChar(0x2013); + else if(PassQ("emdash")) + ReadChar(0x2014); + else if(PassQ("tab")) + ReadText(WString(9, 1)); + else if(PassQ("enspace")) + ReadText(WString(" ")); // todo + else if(PassQ("emspace")) + ReadText(WString(" ")); // todo + else if(PassQ("bullet")) + ReadChar(0x2022); + else if(PassQ("lquote")) + ReadChar(0x2018); + else if(PassQ("rquote")) + ReadChar(0x2019); + else if(PassQ("ldblquote")) + ReadChar(0x201C); + else if(PassQ("rdblquote")) + ReadChar(0x201D); +} + +void RTFParser::ReadField() +{ + bool ign_rslt = false; + int level = Level(); + while(!PassEndGroup(level)) + if(PassGroup() && Level() == level + 1) { + if(PassCmd("fldinst")) { + WString source; + for(; !PassEndGroup(); Skip()) + if(PassText()) + source.Cat(text); + if(ReadField(FromUnicode(source, state.charset))) + ign_rslt = true; + continue; + } + else if(PassCmd("fldrslt")) { + if(!ign_rslt) + ReadItemGroup(); + } + } + else + Skip(); +} + +bool RTFParser::ReadField(const char *p) +{ + Index symdef; + while(*p) + if((byte)*p <= ' ') + p++; + else if(*p == '\"') + symdef.Add(FromCString(p, &p)); + else { + const char *b = p; + while(*++p && *p != ' ') + ; + symdef.Add(String(b, p)); + } + if(symdef.IsEmpty()) + return false; + if(symdef[0] == "SYMBOL" && symdef.GetCount() >= 2 && IsDigit(*symdef[1])) { + int code = atoi(symdef[1]); + int face = -1; + int height = 0; + int f = symdef.Find("\\f"); + if(f >= 0 && f + 1 < symdef.GetCount()) + face = Font::FindFaceNameIndex(symdef[f + 1]); + f = symdef.Find("\\s"); + if(f >= 0 && f + 1 < symdef.GetCount()) + height = PointDots(fround(2 * atof(symdef[f + 1]))) >> 1; + if(face < 0) + face = Font::SYMBOL; + if(height <= 0 || height >= MAX_DOT_HEIGHT) + height = state.charformat.GetHeight(); + if(code >= 0 && code < 255) { + state.charformat.Face(face).Height(height); + ReadText(WString(ToUnicode(code, state.charset), 1)); + return true; + } + } + return false; +} + +void RTFParser::DefaultParaStyle() +{ + state.format = pard_format; + state.first_indent = state.left_margin = state.right_margin = 0; + state.cellformat = std_cell_format; + state.in_table = false; + state.itap = 1; + state.nestprop = false; + state.rowmargin = Rect(25, 25, 25, 25); + state.cellmarginunits = state.rowmarginunits = Rect(0, 0, 0, 0); + state.charset = plain_charset; +} + +void RTFParser::ReadParaStyle() +{ + if(PassQ("par")) + Flush(true, state.itap); + else if(PassQ("cell")) { + Flush(false, 1); + if(!table_stack.IsEmpty()) + table_stack[0].textcol++; + } + else if(PassQ("nestcell")) { + Flush(false, state.itap); + if(state.itap <= table_stack.GetCount()) + table_stack[state.itap - 1].textcol++; + } + else if(PassQ("pard")) + DefaultParaStyle(); + else if(PassQ("pntext")) + SkipGroup(); + else if(PassQ("pn")) { + SkipGroup(); + state.format.bullet = RichPara::BULLET_ROUND; + } + else if(PassQ("pagebb")) + state.format.newpage = (command_arg != 0); + else if(PassQ("ql")) + state.format.align = ALIGN_LEFT; + else if(PassQ("qc")) + state.format.align = ALIGN_CENTER; + else if(PassQ("qr")) + state.format.align = ALIGN_RIGHT; + else if(PassQ("qj")) + state.format.align = ALIGN_JUSTIFY; + else if(PassQ("fi")) + state.first_indent = TwipDotsLim(command_arg); + else if(PassQ("li")) + state.left_margin = TwipDotsLim(command_arg); + else if(PassQ("ri")) + state.right_margin = TwipDotsLim(command_arg); + else if(PassQ("sb")) + state.format.before = TwipDotsLim(command_arg); + else if(PassQ("sa")) + state.format.after = TwipDotsLim(command_arg); + else if(PassQ("widctlpar")) + state.format.orphan = true; + else if(PassQ("nowidctlpar")) + state.format.orphan = false; + else if(PassQ("tql")) + tab_align = ALIGN_LEFT; + else if(PassQ("tqc")) + tab_align = ALIGN_CENTER; + else if(PassQ("tqr")) + tab_align = ALIGN_RIGHT; + else if(PassQ("tqdec")) + tab_align = ALIGN_RIGHT; // todo + else if(PassQ("tldot")) + tab_fill = 0; + else if(PassQ("tlhyph")) + tab_fill = 0; + else if(PassQ("tlul")) + tab_fill = 0; + else if(PassQ("tlth")) + tab_fill = 0; + else if(PassQ("tleq")) + tab_fill = 0; + else if(PassQ("tx") || PassQ("tb")) { // todo: bar tab ? + int pos = TwipDotSize(command_arg); + RichPara::Tab& tab = state.format.tab.Add(); + tab.align = tab_align; + tab.fillchar = tab_fill; + tab.pos = pos; + state.format.SortTabs(); + } + else if(PassQ("intbl")) + state.in_table = true; + else if(PassQ("itap")) { + state.itap = minmax(command_arg, 1, 10); + if(table_stack.GetCount() < state.itap) + OpenTable(state.itap); + } +} + +void RTFParser::ReadCharStyle() +{ + if(PassQ("plain")) { + state.charformat = plain_format; + state.charset = plain_charset; + } + else if(PassQ("b")) + state.charformat.Bold(command_arg != 0); + else if(PassQ("i")) + state.charformat.Italic(command_arg != 0); + else if(PassQ("ul") || PassQ("uld") || PassQ("uldb") + || PassQ("uldash") || PassQ("uldashd") || PassQ("uldashdd") + || PassQ("ulth") || PassQ("ulw") || PassQ("ulwave")) + state.charformat.Underline(); + else if(PassQ("ulnone")) + state.charformat.Underline(false); + else if(PassQ("strike") || PassQ("strikedl")) + state.charformat.Strikeout(command_arg != 0); + else if(PassQ("caps") || PassQ("scaps")) + state.charformat.capitals = (command_arg != 0); + else if(PassQ("super") || PassQ("up")) + state.charformat.sscript = 1; + else if(PassQ("sub") || PassQ("dn")) + state.charformat.sscript = 2; + else if(PassQ("nosupersub")) + state.charformat.sscript = 0; + else if(PassQ("f") && command_arg >= 0 && command_arg < face_table.GetCount()) { + LLOG("font = " << command_arg << ", face = " << face_table[command_arg].face + << ", charset = " << face_table[command_arg].charset); + state.charformat.Face(face_table[command_arg].face); + state.charset = face_table[command_arg].charset; + } + else if(PassQ("fs")) + state.charformat.Height(PointDotHeight(command_arg)); + else if(PassQ("cf") && command_arg >= 0 && command_arg < color_table.GetCount()) + state.charformat.ink = Nvl(color_table[command_arg], Black); + else if(PassQ("cb") && command_arg >= 0 && command_arg < color_table.GetCount()) + state.charformat.paper = color_table[command_arg]; + else if(PassQ("lang")) + {} // state.language = ... +} + +void RTFParser::ReadShape() +{ + int level = Level(); + while(!PassEndGroup(level)) + if(PassCmd("shppict")) { + state.new_dest = false; + ReadItemGroup(); + } + else + is_full = false; +} + +void RTFParser::ReadPict() +{ + Size log_size(1, 1), out_size(1, 1), scaling(100, 100); + Rect crop(0, 0, 0, 0); + enum BLIPTYPE { UNK_BLIP, EMF_BLIP, PNG_BLIP, JPEG_BLIP, WMF_BLIP, DIB_BLIP }; + BLIPTYPE blip_type = UNK_BLIP; +#ifdef PLATFORM_WIN32 +#ifndef PLATFORM_WINCE + int wmf_mode = MM_ANISOTROPIC; +#endif +#endif + String blip_data; + char odd = 0; + while(!PassEndGroup()) + if(PassText()) + blip_data.Cat(ReadBinHex(odd)); + else if(Token() == T_COMMAND) { + if(PassQ("picw")) log_size.cx = minmax(command_arg, 0, 30000); + else if(PassQ("pich")) log_size.cy = minmax(command_arg, 0, 30000); + else if(PassQ("picwgoal")) out_size.cx = TwipDotSize(command_arg); + else if(PassQ("pichgoal")) out_size.cy = TwipDotSize(command_arg); + else if(PassQ("picscalex")) scaling.cx = minmax(command_arg, 1, 1000); + else if(PassQ("picscaley")) scaling.cy = minmax(command_arg, 1, 1000); + else if(PassQ("piccropl")) crop.left = TwipDotSize(command_arg); + else if(PassQ("piccropt")) crop.top = TwipDotSize(command_arg); + else if(PassQ("piccropr")) crop.right = TwipDotSize(command_arg); + else if(PassQ("piccropb")) crop.bottom = TwipDotSize(command_arg); + else if(PassQ("emfblip")) blip_type = EMF_BLIP; + else if(PassQ("pngblip")) blip_type = PNG_BLIP; + else if(PassQ("jpegblip")) blip_type = JPEG_BLIP; +#ifdef PLATFORM_WIN32 +#ifndef PLATFORM_WINCE + else if(PassQ("wmetafile")) { blip_type = WMF_BLIP; wmf_mode = command_arg; } +#endif +#endif + else if(PassQ("dibitmap")) blip_type = DIB_BLIP; + else Skip(); + } + else + Skip(); + Size final_size = minmax(iscale(out_size, scaling, Size(100, 100)), Size(1, 1), Size(30000, 30000)); + Size drawing_size; + DrawingDraw dd; + RichObject object; +#ifdef PLATFORM_WIN32 +#ifndef PLATFORM_WINCE + if(blip_type == EMF_BLIP || blip_type == WMF_BLIP) { + log_size = min(log_size, GetFitSize(log_size, final_size)); + dd.Create(drawing_size = log_size); + WinMetaFile wmf; + if(blip_type == EMF_BLIP) + wmf = WinMetaFile(SetEnhMetaFileBits(blip_data.GetLength(), blip_data)); + else { + METAFILEPICT mfp; + Zero(mfp); + mfp.mm = wmf_mode; + mfp.xExt = log_size.cx; + mfp.yExt = log_size.cy; + wmf = WinMetaFile(SetWinMetaFileBits(blip_data.GetLength(), blip_data, ScreenHDC(), &mfp)); + } + wmf.Paint(dd, log_size); + object = CreateDrawingObject(dd, out_size, final_size); + } + else +#endif +#endif + if(blip_type == DIB_BLIP || blip_type == PNG_BLIP || blip_type == JPEG_BLIP) { + //FIXIMAGE + Image image = StreamRaster::LoadStringAny(blip_data); + object = CreatePNGObject(image, out_size, final_size); + } + if(object) { + LLOG("object (" << object.GetTypeName() << ", " << object.Write().GetLength() << " B), pixel size " + << object.GetPixelSize() << ", final size " << object.GetSize()); + para.Cat(object, state.charformat); + } +} + +String RTFParser::ReadBinHex(char& odd) const +{ + int t = odd; + byte v = ctoi(odd); + String out; + for(const wchar *s = text.Begin(); *s; s++) { + byte w = (*s >= '0' && *s <= '9' ? *s - '0' + : *s >= 'A' && *s <= 'F' ? *s - 'A' + 10 + : *s >= 'a' && *s <= 'f' ? *s - 'a' + 10 + : 255); + if(w < 16) + if(v >= 16) + { + t = *s; + v = w; + } + else + { + out.Cat(16 * v + w); + v = 255; + } + } + odd = (v < 16 ? t : 0); + return out; +} + +void RTFParser::OpenTable(int level) +{ + if(table_stack.GetCount() < level) { + TableState& ts = table_stack.At(level - 1); + ts.stylecol = 0; + state.cellformat = std_cell_format; + } +} + +void RTFParser::ReadTableStyle() +{ + if(PassQ("nesttableprops")) { + state.nestprop = true; + return; + } + if(PassQ("nonesttables")) { + SkipGroup(); + return; + } + int itap = (state.nestprop ? state.itap : 1); + if(PassQ("trowd")) { + OpenTable(itap); + table_stack[itap - 1].stylecol = 0; + return; + } + if(PassQ("row") && table_stack.GetCount() >= 1) { + TableState& ts0 = table_stack[0]; + ts0.textcol = ts0.stylecol = 0; + ts0.cells.Add(); + return; + } + if(PassQ("nestrow") && table_stack.GetCount() >= state.itap) { + TableState& ts = table_stack[state.itap - 1]; + ts.textcol = ts.stylecol = 0; + ts.cells.Add(); + return; + } + if(itap > table_stack.GetCount()) + return; + TableState& ts = table_stack[itap - 1]; + if(PassQ("trgaph")) {} + else if(PassQ("trql")) {} + else if(PassQ("trqr")) {} + else if(PassQ("trqc")) {} + else if(PassQ("trleft")) { + ts.tableformat.lm = TwipDotsLim(command_arg); + } + else if(PassQ("trbrdrl")) {} + else if(PassQ("trbrdrt")) {} + else if(PassQ("trbrdrr")) {} + else if(PassQ("trbrdrb")) {} + else if(PassQ("trbrdrv")) {} + else if(PassQ("trftsWidth")) {} + else if(PassQ("trautofit")) {} + else if(PassQ("trpaddl")) + state.rowmargin.left = TwipDotsLim(command_arg); + else if(PassQ("trpaddt")) + state.rowmargin.top = TwipDotsLim(command_arg); + else if(PassQ("trpaddr")) + state.rowmargin.right = TwipDotsLim(command_arg); + else if(PassQ("trpaddb")) + state.rowmargin.bottom = TwipDotsLim(command_arg); + else if(PassQ("trpaddfl")) + state.rowmarginunits.left = command_arg; + else if(PassQ("trpaddft")) + state.rowmarginunits.top = command_arg; + else if(PassQ("trpaddfr")) + state.rowmarginunits.right = command_arg; + else if(PassQ("trpaddfb")) + state.rowmarginunits.bottom = command_arg; + else if(PassQ("clpadl")) + state.cellformat.margin.left = TwipDotsLim(command_arg); + else if(PassQ("clpadt")) + state.cellformat.margin.top = TwipDotsLim(command_arg); + else if(PassQ("clpadr")) + state.cellformat.margin.right = TwipDotsLim(command_arg); + else if(PassQ("clpadb")) + state.cellformat.margin.bottom = TwipDotsLim(command_arg); + else if(PassQ("clpadfl")) + state.cellmarginunits.left = command_arg; + else if(PassQ("clpadft")) + state.cellmarginunits.top = command_arg; + else if(PassQ("clpadfr")) + state.cellmarginunits.right = command_arg; + else if(PassQ("clpadfb")) + state.cellmarginunits.bottom = command_arg; + else if(PassQ("clvertalt")) {} + else if(PassQ("clbrdrl")) {} + else if(PassQ("clbrdrt")) {} + else if(PassQ("clbrdrr")) {} + else if(PassQ("clbrdrb")) {} + else if(PassQ("cltxlrtb")) {} + else if(PassQ("clcbpat")) { + if(command_arg >= 0 && command_arg < color_table.GetCount()) + state.cellformat.color = color_table[command_arg]; + } + else if(PassQ("clvmrg")) + ts.cells.Top().At(ts.stylecol).merge = true; + else if(PassQ("clvmgf")) + ts.cells.Top().At(ts.stylecol).merge_first = true; + else if(PassQ("clftsWidth")) {} + else if(PassQ("clwWidth")) {} + else if(PassQ("cellx")) { + Cell& newcell = ts.cells.Top().At(ts.stylecol++); + newcell.end_dots = TwipDotsLim(command_arg); + newcell.format = state.cellformat; + if(state.cellmarginunits.left == 0) newcell.format.margin.left = state.rowmargin.left; + if(state.cellmarginunits.top == 0) newcell.format.margin.top = state.rowmargin.top; + if(state.cellmarginunits.right == 0) newcell.format.margin.right = state.rowmargin.right; + if(state.cellmarginunits.bottom == 0) newcell.format.margin.bottom = state.rowmargin.bottom; + state.cellformat = std_cell_format; + } + else if(PassQ("clvertalt")) + state.cellformat.align = ALIGN_TOP; + else if(PassQ("clvertalc")) + state.cellformat.align = ALIGN_CENTER; + else if(PassQ("clvertalb")) + state.cellformat.align = ALIGN_BOTTOM; +} + +END_UPP_NAMESPACE diff --git a/uppdev/RichTextP/RichImage.icpp b/uppdev/RichTextP/RichImage.icpp new file mode 100644 index 000000000..cf53145e7 --- /dev/null +++ b/uppdev/RichTextP/RichImage.icpp @@ -0,0 +1,237 @@ +#include "RichText.h" + +NAMESPACE_UPP + +#ifdef NEWIMAGE + +struct RichImage : public RichObjectType { + virtual String GetTypeName(const Value& v) const; + virtual Size GetPhysicalSize(const Value& data) const; + virtual Size GetPixelSize(const Value& data) const; + virtual void Paint(const Value& data, Draw& w, Size sz) const; + virtual Image ToImage(const Value& data, Size sz) const; + + virtual bool Accept(PasteClip& clip); + virtual Value Read(PasteClip& clip); + virtual String GetClipFmts() const; + virtual String GetClip(const Value& data, const String& fmt) const; + + typedef RichImage CLASSNAME; +}; + +String RichImage::GetTypeName(const Value& v) const +{ + return "image"; +} + +bool RichImage::Accept(PasteClip& clip) +{ + return AcceptImage(clip); +} + +Value RichImage::Read(PasteClip& clip) +{ + return StoreImageAsString(GetImage(clip)); +} + +String RichImage::GetClipFmts() const +{ + return ClipFmtsImage(); +} + +String RichImage::GetClip(const Value& data, const String& fmt) const +{ + return GetImageClip(LoadImageFromString(data), fmt); +} + +Size RichImage::GetPixelSize(const Value& data) const +{ + return GetImageStringSize(data); +} + +Size RichImage::GetPhysicalSize(const Value& data) const +{ + Size sz = GetImageStringDots(data); + if(sz.cx == 0 || sz.cy == 0) + sz = 600 * GetPixelSize(data) / 96; + return sz; +} + +void RichImage::Paint(const Value& data, Draw& w, Size sz) const +{ + Image x = LoadImageFromString(data); +// Size outsz(min(sz.cx, 4 * x.GetWidth()), min(sz.cy, 4 * x.GetHeight())); + w.DrawImage(0, 0, sz.cx, sz.cy, x); +} + +Image RichImage::ToImage(const Value& data, Size sz) const +{ + return Rescale(LoadImageFromString(data), sz); +} + +INITBLOCK { + RichObject::Register("image", &Single()); +}; + +RichObject CreateImageObject(const Image& img, int cx, int cy) +{ + RichObject o = RichObject("image", StoreImageAsString(img)); + if(cx || cy) + o.SetSize(GetRatioSize(o.GetPixelSize(), cx, cy)); + return o; +} + +struct RichPNG : public RichObjectType { + virtual String GetTypeName(const Value& v) const; + virtual Value Read(const String& s) const; + virtual String Write(const Value& v) const; + virtual Size GetPhysicalSize(const Value& data) const; + virtual Size GetPixelSize(const Value& data) const; + virtual void Paint(const Value& data, Draw& w, Size sz) const; + virtual Image ToImage(const Value& data, Size sz) const; +}; + +String RichPNG::GetTypeName(const Value& v) const +{ + return IsString(v) ? "PNG" : "image"; +} + +Value RichPNG::Read(const String& s) const +{ + Image img = StreamRaster::LoadStringAny(s); + if(img) + return img; + return s; +} + +String RichPNG::Write(const Value& v) const +{ + if(IsString(v)) + return v; + return StoreImageAsString(v); +} + +Size RichPNG::GetPhysicalSize(const Value& data) const +{ + if(IsString(data)) + return Size(0, 0); + return Image(data).GetDots(); +} + +Size RichPNG::GetPixelSize(const Value& data) const +{ + if(IsString(data)) + return Size(0, 0); + return Image(data).GetDots(); +} + +void RichPNG::Paint(const Value& data, Draw& w, Size sz) const +{ + if(IsString(data)) { + w.DrawRect(sz, SColorFace()); + DrawFrame(w, sz, SColorText()); + w.DrawText(2, 2, "plugin/png missing!"); + return; + } + Image x = Image(data); + Size outsz(min(sz.cx, 4 * x.GetWidth()), min(sz.cy, 4 * x.GetHeight())); + w.DrawImage(0, 0, outsz.cx, outsz.cy, x); +} + +Image RichPNG::ToImage(const Value& data, Size sz) const +{ + if(IsString(data)) { + ImageDraw iw(sz); + Paint(data, iw, sz); + return iw; + } + Image x = Image(data); + Size outsz(min(sz.cx, 4 * x.GetWidth()), min(sz.cy, 4 * x.GetHeight())); + return Rescale(x, outsz); +} + +INITBLOCK { + RichObject::Register("PNG", &Single()); +}; + +struct RichRawImage : public RichObjectType { + virtual String GetTypeName(const Value& v) const; + virtual Value Read(const String& s) const; + virtual String Write(const Value& v) const; + virtual Size GetPhysicalSize(const Value& data) const; + virtual Size GetPixelSize(const Value& data) const; + virtual void Paint(const Value& data, Draw& w, Size sz) const; + virtual Image ToImage(const Value& data, Size sz) const; +}; + +String RichRawImage::GetTypeName(const Value& v) const +{ + return "rawimage"; +} + +Value RichRawImage::Read(const String& s) const +{ + return s; +} + +String RichRawImage::Write(const Value& v) const +{ + return v; +} + +Size RichRawImage::GetPhysicalSize(const Value& data) const +{ + String s = data; + StringStream ss(s); + One r = StreamRaster::OpenAny(ss); + if(r) + return r->GetInfo().dots; + return Size(0, 0); +} + +Size RichRawImage::GetPixelSize(const Value& data) const +{ + String s = data; + StringStream ss(s); + One r = StreamRaster::OpenAny(ss); + if(r) + return r->GetSize(); + return Size(0, 0); +} + +void RichRawImage::Paint(const Value& data, Draw& w, Size sz) const +{ + String s = data; + StringStream ss(s); + One r = StreamRaster::OpenAny(ss); + if(r) { + ImageEncoder m; + Rescale(m, sz, *r, r->GetSize()); + w.DrawImage(0, 0, sz.cx, sz.cy, m); + } +} + +Image RichRawImage::ToImage(const Value& data, Size sz) const +{ + String s = data; + StringStream ss(s); + One r = StreamRaster::OpenAny(ss); + if(r) + return r->GetImage(); + return Null; +} + +INITBLOCK { + RichObject::Register("rawimage", &Single()); +}; + +RichObject CreateRawImageObject(const String& s, int cx, int cy) +{ + RichObject o = RichObject("rawimage", s); + o.InitSize(cx, cy); + return o; +} + +#endif + +END_UPP_NAMESPACE diff --git a/uppdev/RichTextP/RichText.h b/uppdev/RichTextP/RichText.h new file mode 100644 index 000000000..ee0ffed90 --- /dev/null +++ b/uppdev/RichTextP/RichText.h @@ -0,0 +1,462 @@ +#ifndef RICHTEXT_H +#define RICHTEXT_H + +#include +#include + +NAMESPACE_UPP + +struct Zoom { + int m, d; + + int operator*(int x) const { return d ? iscale(x, m, d) : 0; } + int operator&(int x) const { int q = d ? iscale(x, m, d) : 0; return x > 0 ? max(q, 1) : q; } + + double AsDouble() const { return (double)m / d; } + + Zoom() { m = d = 1; } + Zoom(const Nuller&) { m = d = 0; } + Zoom(int _m, int _d) { m = _m, d = _d; } + + void Serialize(Stream& s) { s % m % d; } + + bool operator==(Zoom a) { return m == a.m && d == a.d; } + bool operator!=(Zoom a) { return m != a.m || d != a.d; } + + friend int operator/(int x, Zoom z) { return z.m ? iscale(x, z.d, z.m) : 0; } +}; + +inline bool IsNull(Zoom z) { return (z.m | z.d) == 0; } + +inline int operator*(int x, Zoom m) +{ + return m * x; +} + +inline void operator*=(int& i, Zoom m) +{ + i = m * i; +} + +inline void operator*=(Rect& r, Zoom m) +{ + r.left *= m; + r.right *= m; + r.top *= m; + r.bottom *= m; +} + +inline Size operator*(Zoom m, Size sz) +{ + return Size(m * sz.cx, m * sz.cy); +} + +inline Size operator/(Size sz, Zoom m) +{ + return Size(sz.cx / m, sz.cy / m); +} + +struct PageY : Moveable > { + int page; + int y; + + PageY(int page, int y) : page(page), y(y) {} + PageY() { page = y = 0; } + +#ifdef _DEBUG + String ToString() const { return AsString(page) + ":" + AsString(y); } +#endif +}; + +inline bool operator<(PageY a, PageY b) +{ + return a.page < b.page ? true : a.page == b.page ? a.y < b.y : false; +} + +inline bool operator==(PageY a, PageY b) +{ + return a.page == b.page && a.y == b.y; +} + +inline PageY operator+(PageY a, int b) +{ + return PageY(a.page, a.y + b); +} + +struct PageRect : public Rect { + int page; + + operator int() const { return page; } + operator PageY() const { return PageY(page, top); } + PageRect& operator=(const Rect& r) { (Rect&)(*this) = r; page = 0; return *this; } + PageRect(const Rect& r) { (Rect&)(*this) = r; page = 0; } + PageRect() { Clear(); page = 0; } +}; + +struct PageDraw { + virtual Draw& Info() = 0; + virtual Draw& Page(int i) = 0; + + operator Draw&() { return Info(); } + + virtual ~PageDraw() {} +}; + +class RichObject; +class Bar; + +struct RichObjectType : Moveable { + virtual String GetTypeName(const Value& v) const = 0; + virtual String GetCreateName() const; + virtual Size GetDefaultSize(const Value& data, Size maxsize) const; + virtual Size GetPhysicalSize(const Value& data) const; + virtual Size GetPixelSize(const Value& data) const; + virtual void Paint(const Value& data, Draw& w, Size sz) const; + virtual Image ToImage(const Value& data, Size sz) const; + virtual Value Read(const String& s) const; + virtual String Write(const Value& v) const; + virtual void Menu(Bar& bar, RichObject& ex) const; + virtual void DefaultAction(RichObject& ex) const; + virtual String GetLink(const Value& data, Point pt, Size sz) const; + + virtual bool Accept(PasteClip& clip); + virtual Value Read(PasteClip& clip); + virtual String GetClipFmts() const; + virtual String GetClip(const Value& data, const String& fmt) const; + + RichObjectType(); + virtual ~RichObjectType(); +}; + +class RichObject : Moveable { + Value data; + int ydelta; + Size size; + Size physical_size; + Size pixel_size; + bool keepratio; + const RichObjectType *type; + int64 serial; + String type_name; + + static VectorMap& Map(); + + void NewSerial(); + +public: + static void Register(const char *name, RichObjectType *type) init_; + static int GetTypeCount() { return Map().GetCount(); } + static int FindType(const String& name) { return Map().Find(name); } + static RichObjectType& GetType(int i) { return *Map()[i]; } + static String GetTypeName(int i) { return Map().GetKey(i); } + + void SetSize(int cx, int cy) { size = Size(cx, cy); NewSerial(); } + void SetSize(Size sz) { size = sz; NewSerial(); } + Size GetSize() const { return size; } + void Paint(Draw& w, Size sz) const; + Image ToImage(Size sz) const; + Size GetPhysicalSize() const { return physical_size; } + Size GetPixelSize() const { return pixel_size; } + Size GetDefaultSize(Size maxsize) const { return type ? type->GetDefaultSize(data, maxsize) : physical_size; } + + void Set(RichObjectType *type, const Value& data, Size maxsize = Size(3967, 3967)); + bool Set(const String& type_name, const Value& data, Size maxsize = Size(3967, 3967)); + + String GetTypeName() const; + Value GetData() const { return data; } + String GetLink(Point pt, Size sz) const { return type ? type->GetLink(data, pt, sz) : String(); } + + const RichObjectType& GetType() const; + + bool Read(const String& type, const String& data, Size sz); + String Write() const { return type ? type->Write(data) : (String)data; } + + void KeepRatio(bool b) { keepratio = b; } + bool IsKeepRatio() const { return keepratio; } + + void SetYDelta(int yd) { ydelta = yd; } + int GetYDelta() const { return ydelta; } + + void Menu(Bar& bar) { if(type) type->Menu(bar, *this); } + void DefaultAction() { if(type) type->DefaultAction(*this); } + + operator bool() const { return !IsNull(data); } + + void Clear(); + + int64 GetSerialId() const { return serial; } + + void InitSize(int cx, int cy); + + RichObject(); + RichObject(RichObjectType *type, const Value& data, Size maxsize = Size(3967, 3967)); + RichObject(const String& type, const Value& data, Size maxsize = Size(3967, 3967)); +}; + +RichObject CreateDrawingObject(const Drawing& dwg, Size dot_size, Size size); +RichObject CreateDrawingObject(const Drawing& dwg, int cx = 0, int cy = 0); +RichObject CreatePNGObject(const Image& img, Size dot_size, Size size); +RichObject CreatePNGObject(const Image& img, Size dot_size, Size size); +RichObject CreateImageObject(const Image& img, int cx = 0, int cy = 0); +RichObject CreateRawImageObject(const String& s, int cx = 0, int cy = 0); + +struct RichPara; + +enum { + RICHHOT_LM = -1, + RICHHOT_RM = -2, +}; + +struct RichHotPos { + int table; + int column; + int delta; + int left, cx; + int textleft, textcx; + + RichHotPos() { table = 0; column = Null; } +}; + +struct RichValPos : Moveable { + PageY py; + int pos; + WString data; +}; + +struct PaintInfo { + Zoom zoom; + int sell, selh; + int tablesel; + Rect cells; + PageY top; + PageY bottom; + Color hyperlink; + Color indexentry; + bool usecache; + bool sizetracking; + Color showcodes; + Bits (*spellingchecker)(const RichPara& para); + int highlightpara; + Color highlight; + bool coloroverride; + + PaintInfo(); +}; + +int LineZoom(Zoom z, int a); + +class RichTable; + +#include "Para.h" + +struct RichPos { + int tabtextparti; + int tabtextpartcount; + int tabposintabtext; + int tabtextlen; + + int table; + Size tabsize; + Point cell; + + int tablen; + int posintab; + + int celllen; + int posincell; + + int parai; + int partcount; + int posinpara; + int paralen; + + int level; + int parenttab; + + RichPara::Format format; + int chr; + RichObject object; + Id field; + String fieldparam; + RichPara::CharFormat fieldformat; + +#ifdef _DEBUG + String ToString() const; +#endif + + RichPos(); +}; + +inline bool InSameTxt(const RichPos& a, const RichPos& b) +{ + return a.table == b.table && (a.table == 0 || a.cell == b.cell); +} + +struct RichCaret : PageRect { + int lineascent; + int caretascent; + int caretdescent; + int objectcy; + int objectyd; + int line; + Rect textpage; + + RichCaret() { lineascent = caretascent = caretdescent = 0; } +}; + +struct RichStyle { + RichPara::Format format; + String name; + Uuid next; + + static Uuid GetDefaultId(); + static const RichStyle& GetDefault(); + + RichStyle() { next = GetDefaultId(); } +}; + +typedef ArrayMap RichStyles; + +const RichStyle& GetStyle(const RichStyles& s, const Uuid& id); +int FindStyleWithName(const RichStyles& style, const String& name); + +struct RichContext { + const RichStyles& styles; + Rect page; + PageY py; + + void Page() { py.page++; py.y = page.top; } + + RichContext(const RichStyles& styles) : styles(styles) {} +}; + +struct RichCellPos; + +#include "Txt.h" +#include "Table.h" +#include "Text.h" + +struct RichCellPos { + int pos; + + int textlen; + + Size tabsize; + int tabpos; + int tablen; + int cellpos; + int celllen; + int level; + RichCell::Format format; + RichTable::Format tableformat; + +#ifdef _DEBUG + String ToString() const; +#endif +}; + +String DeQtf(const char *s); +String DeQtfLf(const char *s); + +struct QtfRichObject { + RichObject obj; + +public: + String ToString() const; + + QtfRichObject() {} + QtfRichObject(const RichObject& o); +}; + +String AsQTF(const RichObject& obj); + +RichText ParseQTF(const char *qtf, bool scolors = false, byte accesskey = 0); + +RichText AsRichText(const wchar *s, const RichPara::Format& f = RichPara::Format()); + +enum +{ + QTF_BODY = 1, + QTF_ALL_STYLES = 2, + QTF_NOSTYLES = 4, + QTF_CRLF = 8, + QTF_NOCHARSET = 16, + QTF_NOLANG = 32, +}; + +String AsQTF(const RichText& doc, byte charset = CHARSET_UTF8, + dword options = QTF_BODY|QTF_ALL_STYLES|QTF_CRLF); + +inline String StylesAsQTF(const RichText& doc, byte charset = CHARSET_UTF8) +{ return AsQTF(doc, charset, QTF_ALL_STYLES|QTF_CRLF); } + +inline String BodyAsQTF(const RichText& doc, byte charset = CHARSET_UTF8) +{ return AsQTF(doc, charset, QTF_BODY|QTF_CRLF); } + +enum +{ + ROUNDOFF = 1 << 20, + MAX_FONTS = 10000, + MAX_DOTS = 600 * 100, + MAX_DOT_HEIGHT = 1200, + MAX_POINT_HEIGHT = MAX_DOT_HEIGHT * 3 / 25, +}; + +inline int DotTwips (int dots) { return (dots * 12 + 5 * ROUNDOFF + 2) / 5 - ROUNDOFF; } +inline int TwipDots (int twp) { return (twp * 5 + 12 * ROUNDOFF + 6) / 12 - ROUNDOFF; } +inline int DotPoints(int dots) { return (dots * 3 + 25 * ROUNDOFF + 12) / 25 - ROUNDOFF; } +inline int PointDots(int pts) { return (pts * 25 + 3 * ROUNDOFF + 1) / 3 - ROUNDOFF; } +inline int TwipDotSize(int twp) { return IsNull(twp) ? 0 : minmax(TwipDots(twp), 0, MAX_DOTS); } +inline int PointDotHeight(int p) { return (minmax(Nvl(p, 0), 0, MAX_POINT_HEIGHT) * 25 + 5) / 6; } + +void EncodeRTF(Stream& stream, const RichText& richtext, byte charset); +String EncodeRTF(const RichText& richtext, byte charset); +String EncodeRTF(const RichText& richtext); +RichText ParseRTF(const char *rtf); + +Zoom GetRichTextStdScreenZoom(); + +const Display& QTFDisplay(); +const Display& QTFDisplayVCenter(); + +String EncodeHtml(const RichText& text, Index& css, + const VectorMap& links, + const VectorMap& labels, + const String& path, const String& base = Null, Zoom z = Zoom(8, 40), + const VectorMap& escape = VectorMap(), + int imtolerance = 0); +String AsCss(Index& ss); + +inline //BW - no labels +String EncodeHtml(const RichText& text, Index& css, + const VectorMap& links, + const String& path, const String& base = Null, Zoom z = Zoom(8, 40)) { + return EncodeHtml(text, css, links, VectorMap(), path, base, z); +} + +struct SimplePageDraw : PageDraw { + Draw& w; + + virtual Draw& Info(); + virtual Draw& Page(int); + + SimplePageDraw(Draw& w) : w(w) {} + virtual ~SimplePageDraw() {} +}; + +struct PrintPageDraw : PageDraw { + int page; + Draw& w; + NilDraw nw; + + Draw& Page(int _page) { return page == _page ? w : (Draw&)nw; } + Draw& Info() { return w; } + void SetPage(int _page) { page = _page; } + + PrintPageDraw(Draw& w) : w(w) {} + virtual ~PrintPageDraw() {} +}; + +END_UPP_NAMESPACE + +#endif diff --git a/uppdev/RichTextP/RichText.iml b/uppdev/RichTextP/RichText.iml new file mode 100644 index 000000000..604b87870 --- /dev/null +++ b/uppdev/RichTextP/RichText.iml @@ -0,0 +1,9 @@ +IMAGE_ID(EndParaChar) +IMAGE_ID(TabChar) +IMAGE_ID(SpaceChar) +IMAGE_ID(HardSpaceChar) + +IMAGE_BEGIN_DATA +IMAGE_DATA(120,156,99,96,101,224,96,192,3,254,147,33,246,31,11,141,206,70,199,232,234,176,155,205,198,192,74,138,67,73,177,28) +IMAGE_DATA(155,122,236,230,51,1,157,129,199,34,92,6,64,0,11,225,224,198,101,16,46,62,97,75,113,0,0,165,164,44,1,0) +IMAGE_END_DATA(64, 4) diff --git a/uppdev/RichTextP/RichText.usc b/uppdev/RichTextP/RichText.usc new file mode 100644 index 000000000..e69de29bb diff --git a/uppdev/RichTextP/RichTextP.upp b/uppdev/RichTextP/RichTextP.upp new file mode 100644 index 000000000..852ba873a --- /dev/null +++ b/uppdev/RichTextP/RichTextP.upp @@ -0,0 +1,39 @@ +description "Rich-text data structures and painting, including RTF and HTML export/import"; + +file + RichText.h, + Object.cpp, + RichImage.icpp, + Para.h, + ParaData.cpp optimize_speed, + ParaType.cpp optimize_speed, + ParaPaint.cpp, + Txt.h, + TxtData.cpp, + TxtPaint.cpp, + TxtOp.cpp, + Format.cpp, + Table.h, + TableCell.cpp, + TableLayout.cpp, + TablePaint.cpp, + TableData.cpp, + Text.h, + TextPaint.cpp, + TextStyle.cpp, + TextData.cpp, + TextTable.cpp, + EncodeQtf.cpp optimize_speed, + ParseQtf.cpp optimize_speed, + EncodeRTF.cpp optimize_speed, + ParseRTF.cpp optimize_speed, + EncodeHTML.cpp optimize_speed, + Util.cpp, + RichText.iml, + Info readonly separator, + srcdoc.tpp, + Copying; + +mainconfig + "" = ""; + diff --git a/uppdev/RichTextP/Table.h b/uppdev/RichTextP/Table.h new file mode 100644 index 000000000..c690701e6 --- /dev/null +++ b/uppdev/RichTextP/Table.h @@ -0,0 +1,220 @@ +class RichCell : DeepCopyOption { +public: + struct Format { + Rect border; + Rect margin; + int align; + int minheight; + Color color; + Color bordercolor; + bool keep; + + Format(); + }; + + Format format; + int vspan, hspan; + RichTxt text; + +private: + bool Reduce(RichContext& rc) const; + PageY Align(const RichContext& rc, PageY npy) const; + void DrawCell(Draw& w, int l, int r, int y, int yy, const Rect& border) const; + + void ClearText(); + void ClearText(const RichPara::Format& format, const RichStyles& style); + void Clear(); + + PageY GetTop(RichContext rc) const; + PageY GetHeight(RichContext rc) const; + void Paint(PageDraw& pw, RichContext rc, PageY npy, + const Rect& xpg, int y, int ny, const PaintInfo& pi, + bool select) const; + RichCaret GetCaret(int pos, RichContext rc, PageY pyy) const; + int GetPos(int x, PageY y, RichContext rc, PageY pyy) const; + RichHotPos GetHotPos(int x, PageY y, int tolerance, RichContext rc, PageY pyy) const; + + void GatherValPos(Vector& f, RichContext rc, PageY pyy, int pos, int type) const; + + friend class RichTable; + friend class RichText; + +public: + RichCell(const RichCell& src, int); + RichCell(); +}; + +class RichTable : DeepCopyOption { +public: + struct Format { + int before, lm, rm, after; + int frame; + Color framecolor; + int grid; + Color gridcolor; + WithDeepCopy< Vector > column; + int header; + bool keep; + + Format(); + }; + + struct CellInfo { + bool valid; + Point master; + + operator bool() const { return valid; } + + CellInfo() { valid = true; } + }; + + Format format; + Array< Array > cell; + +private: + struct PaintCell : Moveable { + int left; + int right; + Rect page; + PageY hy; + bool top; + bool bottom; + + PaintCell() { top = true; } + }; + + struct PaintRow : Moveable { + PageY gpy; + PageY py, pyy; + Buffer cell; + bool first; + + PaintCell& operator[](int i) { return cell[i]; } + const PaintCell& operator[](int i) const { return cell[i]; } + }; + + struct Layout { + Buffer col; + Buffer row; + int frame; + int grid; + PageY pyy; + + PaintRow& operator[](int i) { return row[i]; } + const PaintRow& operator[](int i) const { return row[i]; } + }; + + struct TabLayout : Layout { + bool hasheader; + Layout header; + Rect page; + int page0; + Size sz; + }; + + mutable TabLayout clayout; + mutable Rect cpage; + mutable PageY cpy; + + Buffer< Buffer > ci; + int r_row, r_column; + Rect r_page; + PageY r_py, r_pyy; + + void Invalidate(); + void InvalidateRefresh(int i, int j); + void InvalidateRefresh(Point p) { InvalidateRefresh(p.y, p.x); } + + bool Reduce(RichContext& rc) const; + Layout Realize(RichContext rc, int ny) const; + bool RowPaint(PageDraw& pw, const RichStyles& st, const Layout& tab, + int i, int ny, const Rect& pg, VectorMap& frr, + PaintInfo& pi, int pd, bool sel) const; + + const TabLayout& Realize(RichContext rc) const; + + mutable int length, tabcount; + + static void ExpandFrr(VectorMap& frr, int pi, int l, int r, int t, int b); + + friend class RichTxt; + friend class RichText; +public: + + Array& operator[](int i) { return cell[i]; } + const Array& operator[](int i) const { return cell[i]; } + RichCell& operator[](Point p) { return cell[p.y][p.x]; } + const RichCell& operator[](Point p) const { return cell[p.y][p.x]; } + int GetCellPos(int i, int j) const; + int GetCellPos(Point p) const { return GetCellPos(p.y, p.x); } + int GetTableCount(int i, int j) const; + int GetTableCount(Point p) const { return GetTableCount(p.y, p.x); } + Point GetMasterCell(int i, int j) const; + Point GetMasterCell(Point p) const { return GetMasterCell(p.y, p.x); } + const RichCell& GetMaster(int i, int j) const; + + int GetLength() const; + int GetTableCount() const; + +private: + void Normalize0(); + + RichTable Copy(const Rect& sel) const; + void Paste(Point pos, const RichTable& tab); + void RemoveRow0(int rowi); + void RemoveRow(int rowi); + void InsertRow(int rowi, const RichStyles& style); + void RemoveColumn0(int column); + void RemoveColumn(int column); + void InsertColumn(int column, const RichStyles& style); + bool IsRowEmpty(int row); + bool IsColumnEmpty(int column); + void SplitCell(Point cl, Size sz, const RichStyles& style); + RichCell::Format GetCellFormat(const Rect& sel) const; + void SetCellFormat(const Rect& sel, const RichCell::Format& fmt, bool setkeep); + + PageY GetHeight(RichContext rc) const; + PageY GetTop(RichContext rc) const; + void Paint(PageDraw& pw, RichContext rc, const PaintInfo& pi) const; + RichCaret GetCaret(int pos, RichContext rc) const; + int GetPos(int x, PageY y, RichContext rc) const; + int GetVertMove(int pos, int gx, RichContext rc, int dir) const; + RichHotPos GetHotPos(int x, PageY y, int tolerance, RichContext rc) const; + void AdjustSel(Rect& sel) const; + + void GatherValPos(Vector& f, RichContext rc, int pos, int type) const; + void ClearSpelling(); + + Point FindCell(int& pos) const; + RichPos GetRichPos(int pos, const RichStyles& st) const; + + CellInfo GetCellInfo(int i, int j) { return ci[i][j]; } + + int GetInvalid(PageY& top, PageY& bottom, RichContext rc) const; + void Validate(); + + void ApplyZoom(Zoom z, const RichStyles& ostyle, const RichStyles& zstyle); + +public: + void AddColumn(int cx); + void SetPick(int i, int j, pick_ RichTxt& text); + RichTxt GetPick(int i, int j); + const RichTxt& Get(int i, int j) const { return cell[i][j].text; } + void SetQTF(int i, int j, const char *qtf); + void SetFormat(int i, int j, const RichCell::Format& fmt); + const RichCell::Format& GetFormat(int i, int j) { return cell[i][j].format; } + bool operator()(int i, int j) const { return ci[i][j].valid; } + void SetSpan(int i, int j, int vspan, int hspan); + Size GetSpan(int i, int j) const; + void SetFormat(const Format& fmt); + const Format& GetFormat() const { return format; } + + int GetColumns() const { return format.column.GetCount(); } + int GetRows() const { return cell.GetCount(); } + Size GetSize() const { return Size(GetColumns(), GetRows()); } + + void Normalize(); + + RichTable(const RichTable& src, int); + RichTable(); +}; diff --git a/uppdev/RichTextP/TableCell.cpp b/uppdev/RichTextP/TableCell.cpp new file mode 100644 index 000000000..515fdfc4b --- /dev/null +++ b/uppdev/RichTextP/TableCell.cpp @@ -0,0 +1,194 @@ +#include "RichText.h" + +NAMESPACE_UPP + +int LineZoom(Zoom z, int a) +{ + return a ? max(1, z * a) : 0; +} + +RichCell::Format::Format() +{ + margin.left = margin.right = 25; + margin.top = margin.bottom = 15; + border.Clear(); + align = ALIGN_CENTER; + color = White; + bordercolor = Black; + keep = false; + minheight = 0; +} + +void RichCell::ClearText() +{ + text.Clear(); + RichPara h; + text.Cat(h, RichStyles()); +} + +void RichCell::ClearText(const RichPara::Format& format, const RichStyles& style) +{ + text.Clear(); + RichPara h; + h.format = format; + text.Cat(h, style); +} + +void RichCell::Clear() +{ + format = Format(); + vspan = hspan = 0; + ClearText(); +} + +bool RichCell::Reduce(RichContext& rc) const +{ + Rect br = rc.page; + rc.page.top += format.margin.top + format.border.top; + rc.page.bottom -= format.margin.bottom + format.border.bottom; + rc.page.left += format.margin.left + format.border.left; + rc.page.right -= format.margin.right + format.border.right; + if(rc.page.IsEmpty()) + rc.page = br; + return !rc.page.IsEmpty(); +} + +PageY RichCell::GetTop(RichContext rc) const +{ + if(!Reduce(rc)) + return rc.py; + rc.py.y += format.margin.top + format.border.top; + return text.GetTop(rc); +} + +PageY RichCell::GetHeight(RichContext rc) const +{ + if(Reduce(rc)) { + PageY py = rc.py; + rc.py.y += format.margin.top + format.border.top; + rc.py = text.GetHeight(rc); + rc.py.y += format.margin.bottom + format.border.bottom; + py.y += format.margin.top + format.border.top + + format.minheight + + format.margin.bottom + format.border.bottom; + return py.y <= rc.page.bottom && py > rc.py ? py : rc.py; + } + else { + rc.py.y += format.margin.top + format.border.top; + rc.py.y += 80; + rc.py.y += format.margin.bottom + format.border.bottom; + if(rc.py.y >= rc.page.bottom) { + rc.Page(); + rc.py.y += 80; + } + } + return rc.py; +} + +void RichCell::DrawCell(Draw& w, int l, int r, int y, int yy, const Rect& border) const +{ + w.DrawRect(l, y, r - l, border.top, format.bordercolor); + w.DrawRect(l, y, border.left, yy - y, format.bordercolor); + w.DrawRect(r - border.right, y, border.right, yy - y, format.bordercolor); + w.DrawRect(l, yy - border.bottom, r - l, border.bottom, format.bordercolor); + w.DrawRect(l + border.left, y + border.top, + r - l - border.left - border.right, yy - y - border.top - border.bottom, + format.color); +} + +PageY RichCell::Align(const RichContext& rc, PageY npy) const +{ + PageY y = rc.py; + y.y += format.margin.top + format.border.top; + if(rc.py.page == npy.page) { + int dx = npy.y - text.GetHeight(rc).y + - format.margin.top - format.border.top + - format.margin.bottom - format.border.bottom; + if(format.align == ALIGN_CENTER) + y.y += dx / 2; + else + if(format.align == ALIGN_BOTTOM) + y.y += dx; + } + return y; +} + +void RichCell::Paint(PageDraw& pw, RichContext rc, PageY npy, + const Rect& xpg, int y, int ny, const PaintInfo& pi, bool select) const +{ + if(!Reduce(rc)) + return; + Rect border(LineZoom(pi.zoom, format.border.left), LineZoom(pi.zoom, format.border.top), + LineZoom(pi.zoom, format.border.right), LineZoom(pi.zoom, format.border.bottom)); + if(rc.py.page == npy.page) + DrawCell(pw.Page(rc.py.page), xpg.left, xpg.right, y, ny, border); + else { + DrawCell(pw.Page(rc.py.page), xpg.left, xpg.right, y, xpg.bottom, border); + for(int i = rc.py.page + 1; i < npy.page; i++) + DrawCell(pw.Page(i), xpg.left, xpg.right, xpg.top, xpg.bottom, border); + DrawCell(pw.Page(npy.page), xpg.left, xpg.right, xpg.top, ny, border); + } + + rc.py = Align(rc, npy); + text.Paint(pw, rc, pi); + + if(select) { + int cx = xpg.right - xpg.left; + if(rc.py.page == npy.page) + pw.Page(rc.py.page).DrawRect(xpg.left, y, cx, ny - y, InvertColor); + else { + pw.Page(rc.py.page).DrawRect(xpg.left, y, cx, xpg.bottom - y, InvertColor); + for(int i = rc.py.page + 1; i < npy.page; i++) + pw.Page(i).DrawRect(xpg.left, xpg.top, cx, xpg.Height(), InvertColor); + pw.Page(npy.page).DrawRect(xpg.left, xpg.top, cx, ny - xpg.top, InvertColor); + } + } +} + +RichCaret RichCell::GetCaret(int pos, RichContext rc, PageY pyy) const +{ + if(!Reduce(rc)) + return RichCaret(); + rc.py = Align(rc, pyy); + return text.GetCaret(pos, rc); +} + +int RichCell::GetPos(int x, PageY y, RichContext rc, PageY pyy) const +{ + if(!Reduce(rc)) + return 0; + rc.py = Align(rc, pyy); + return text.GetPos(x, y, rc); +} + +RichHotPos RichCell::GetHotPos(int x, PageY y, int tolerance, RichContext rc, PageY pyy) const +{ + if(!Reduce(rc)) + return RichHotPos(); + rc.py = Align(rc, pyy); + return text.GetHotPos(x, y, tolerance, rc); +} + +void RichCell::GatherValPos(Vector& f, RichContext rc, PageY pyy, int pos, int type) const +{ + if(!Reduce(rc)) + return; + rc.py = Align(rc, pyy); + text.GatherValPos(f, rc, pos, type); +} + +RichCell::RichCell() +{ + vspan = hspan = 0; + Clear(); +} + +RichCell::RichCell(const RichCell& src, int) +{ + format = src.format; + hspan = src.hspan; + vspan = src.vspan; + text <<= src.text; +} + +END_UPP_NAMESPACE diff --git a/uppdev/RichTextP/TableData.cpp b/uppdev/RichTextP/TableData.cpp new file mode 100644 index 000000000..592cd64f9 --- /dev/null +++ b/uppdev/RichTextP/TableData.cpp @@ -0,0 +1,580 @@ +#include "RichText.h" + +NAMESPACE_UPP + +void RichTable::Invalidate() +{ + cpy.page = -1; + length = tabcount = -1; +} + +void RichTable::InvalidateRefresh(int i, int j) +{ + if(i < format.header) + r_row = -1; + else + if(r_row != i || r_column != j) + if(r_row == -2 && clayout.sz == GetSize()) { + r_row = i; + r_column = j; + r_py = cpy; + r_pyy = clayout[min(GetRows() - 1, i + cell[i][j].vspan)].pyy; + r_page = cpage; + } + else + r_row = -1; + Invalidate(); +} + +void RichTable::Normalize0() +{ + int nx = format.column.GetCount(); + int ny = cell.GetCount(); + + for(int i = 0; i < ny; i++) + cell[i].SetCount(nx); + + Invalidate(); + r_py.page = -1; + r_row = -1; + + ci.Alloc(ny); + for(int i = 0; i < ny; i++) + ci[i].Alloc(nx); + for(int i = 0; i < ny; i++) + for(int j = 0; j < nx; j++) + if(ci[i][j].valid) { + RichCell& c = cell[i][j]; + int vs = i < format.header ? min(format.header - 1, i + c.vspan) : min(ny - 1, i + c.vspan); + int hs = min(nx - 1, j + c.hspan); + c.vspan = vs - i; + c.hspan = hs - j; + for(int ii = i; ii <= vs; ii++) + for(int jj = j; jj <= hs; jj++) { + if(i != ii || j != jj) { + CellInfo& f = ci[ii][jj]; + f.valid = false; + cell[ii][jj].Clear(); + f.master = Point(j, i); + cell[ii][jj].hspan = hs - jj; + } + } + } +} + +bool RichTable::IsRowEmpty(int row) +{ + for(int i = 0; i < GetColumns(); i++) + if(ci[row][i].valid && cell[row][i].vspan == 0) + return false; + return true; +} + +bool RichTable::IsColumnEmpty(int column) +{ + for(int i = 0; i < GetRows(); i++) + if(ci[i][column].valid) + return false; + return true; +} + +void RichTable::RemoveRow0(int rowi) +{ + if(rowi < format.header) + format.header--; + for(int i = 0; i < GetColumns(); i++) { + CellInfo& cf = ci[rowi][i]; + if(!cf.valid && cf.master.x == i) + cell[cf.master.y][cf.master.x].vspan--; + } + cell.Remove(rowi); +} + +void RichTable::RemoveColumn0(int column) +{ + for(int i = 0; i < cell.GetCount(); i++) { + CellInfo& cf = ci[i][column]; + if(!cf.valid && cf.master.y == i) + cell[cf.master.y][cf.master.x].hspan--; + cell[i].Remove(column); + } + format.column.Remove(column); +} + +void RichTable::Normalize() +{ + Normalize0(); + int i = 0; + while(i < GetRows()) + if(IsRowEmpty(i)) { + Array r(cell[i], 1); + RemoveRow0(i); + for(int j = 0; j < GetColumns(); j++) + if(ci[i][j].valid) { + cell[i][j] <<= r[j]; + cell[i][j].vspan--; + } + Normalize0(); + } + else + i++; + int j = 0; + while(j < GetColumns()) + if(IsColumnEmpty(j)) { + Array r; + for(int i = 0; i < GetRows(); i++) + r.Add() <<= cell[i][j]; + int c = format.column[j]; + RemoveColumn0(j); + format.column[min(GetColumns() - 1, j)] += c; + for(int i = 0; i < GetRows(); i++) + if(ci[i][j].valid) + cell[i][j] <<= r[i]; + Normalize0(); + } + else + j++; + int sum = Sum0(format.column); + if(sum != 10000) { + r_row = -1; + if(format.column.GetCount()) { + if(format.column.GetCount() > 1) { + int q = 0; + for(int i = 0; i < format.column.GetCount() - 1; i++) + q += sum <= 0 ? (format.column[i] = 10000 / format.column.GetCount()) + : (format.column[i] = format.column[i] * 10000 / sum); + format.column[format.column.GetCount() - 1] = 10000 - q; + } + else + format.column[0] = 10000; + } + } +} + +int RichTable::GetLength() const +{ + if(length < 0) { + length = 0; + for(int i = 0; i < cell.GetCount(); i++) + for(int j = 0; j < format.column.GetCount(); j++) + if(ci[i][j].valid) + length += cell[i][j].text.GetLength() + 1; + } + return length ? length - 1 : 0; +} + +Point RichTable::FindCell(int& pos) const +{ + for(int i = 0; i < cell.GetCount(); i++) + for(int j = 0; j < format.column.GetCount(); j++) + if(ci[i][j].valid) { + int l = cell[i][j].text.GetLength() + 1; + if(pos < l) + return Point(j, i); + pos -= l; + } + NEVER(); + return Point(); +} + +int RichTable::GetCellPos(int ii, int jj) const +{ + int pos = 0; + for(int i = 0; i < ii; i++) + for(int j = 0; j < format.column.GetCount(); j++) + if(ci[i][j].valid) + pos += cell[i][j].text.GetLength() + 1; + for(int j = 0; j < jj; j++) + if(ci[ii][j].valid) + pos += cell[ii][j].text.GetLength() + 1; + return pos; +} + +Point RichTable::GetMasterCell(int i, int j) const +{ + i = min(GetRows() - 1, i); + j = min(GetColumns() - 1, j); + const CellInfo& cf = ci[i][j]; + return cf.valid ? Point(j, i) : cf.master; +} + +const RichCell& RichTable::GetMaster(int i, int j) const +{ + Point p = GetMasterCell(i, j); + return cell[p.y][p.x]; +} + +int RichTable::GetTableCount(int ii, int jj) const +{ + int ti = 0; + for(int i = 0; i < ii; i++) + for(int j = 0; j < format.column.GetCount(); j++) + if(ci[i][j].valid) + ti += cell[i][j].text.GetTableCount(); + for(int j = 0; j < jj; j++) + if(ci[ii][j].valid) + ti += cell[ii][j].text.GetTableCount(); + return ti; +} + + +void RichTable::ClearSpelling() +{ + for(int i = 0; i < cell.GetCount(); i++) + for(int j = 0; j < format.column.GetCount(); j++) + if(ci[i][j].valid) + cell[i][j].text.ClearSpelling(); +} + +int RichTable::GetTableCount() const +{ + if(tabcount < 0) { + tabcount = 0; + for(int i = 0; i < cell.GetCount(); i++) { + for(int j = 0; j < format.column.GetCount(); j++) + if(ci[i][j].valid) + tabcount += cell[i][j].text.GetTableCount(); + } + } + return tabcount; +} + +void RichTable::Validate() +{ + r_row = -2; +} + +int RichTable::GetInvalid(PageY& top, PageY& bottom, RichContext rc) const +{ + if(r_row == -2) + return -1; + const TabLayout& tab = Realize(rc); + if(r_row >= 0 && r_page == rc.page + && r_py == rc.py && tab[min(GetRows() - 1, r_row + cell[r_row][r_column].vspan)].pyy == r_pyy) { + const PaintRow& pr = tab[r_row]; + const RichCell& cl = cell[r_row][r_column]; + top = pr.py; + bottom = tab[min(cell.GetCount() - 1, r_row + cl.vspan)].pyy; + return 0; + } + return 1; +} + +void RichTable::AddColumn(int cx) +{ + format.column.Add(cx); +} + +void RichTable::SetPick(int i, int j, pick_ RichTxt& text) +{ + cell.At(i).At(j).text = text; +} + +RichTxt RichTable::GetPick(int i, int j) +{ + return cell[i][j].text; +} + +void RichTable::SetQTF(int i, int j, const char *qtf) +{ + SetPick(i, j, ParseQTF(qtf)); +} + +void RichTable::SetFormat(int i, int j, const RichCell::Format& fmt) +{ + cell.At(i).At(j).format = fmt; +} + +void RichTable::SetSpan(int i, int j, int vspan, int hspan) +{ + RichCell& c = cell.At(i).At(j); + c.vspan = vspan; + c.hspan = hspan; +} + +Size RichTable::GetSpan(int i, int j) const +{ + const RichCell& c = cell[i][j]; + return Size(c.hspan, c.vspan); +} + +void RichTable::SetFormat(const Format& fmt) +{ + format = fmt; +} + +RichTable RichTable::Copy(const Rect& sel) const +{ + RichTable r; + r.format = format; + r.format.header = max(0, format.header - sel.top); + r.format.column.Remove(0, sel.left); + r.format.column.SetCount(sel.right - sel.left + 1); + for(int i = sel.top; i <= sel.bottom; i++) + for(int j = sel.left; j <= sel.right; j++) + r.cell.At(i - sel.top).At(j - sel.left) <<= cell[i][j]; + r.Normalize(); + return r; +} + +void RichTable::RemoveRow(int rowi) +{ + RemoveRow0(rowi); + Normalize(); +} + +void RichTable::InsertRow(int rowi, const RichStyles& style) +{ + if(rowi < format.header) + format.header++; + int si; + if(rowi < GetRows()) { + for(int i = 0; i < GetColumns(); i++) { + CellInfo& cf = ci[rowi][i]; + if(!cf.valid && cf.master.x == i) + cell[cf.master.y][cf.master.x].vspan++; + } + si = rowi + 1; + } + else + si = rowi - 1; + cell.Insert(rowi).SetCount(GetColumns()); + if(si >= 0) + for(int i = 0; i < GetColumns(); i++) { + RichCell& c = cell[rowi][i]; + const RichCell& sc = cell[si][i]; + c.format = sc.format; + DUMP(sc.hspan); + c.hspan = sc.hspan; + c.ClearText(sc.text.GetFirstFormat(style), style); + } + Normalize(); +} + +void RichTable::RemoveColumn(int column) +{ + RemoveColumn0(column); + Normalize(); +} + +void RichTable::InsertColumn(int column, const RichStyles& style) +{ + int sci = column < GetColumns() ? column + 1 : column - 1; + for(int i = 0; i < cell.GetCount(); i++) { + CellInfo& cf = ci[i][column]; + if(!cf.valid && cf.master.y == i) + cell[cf.master.y][cf.master.x].hspan++; + RichCell& c = cell[i].Insert(column); + if(sci >= 0) { + const RichCell& sc = cell[i][sci]; + c.format = sc.format; + c.vspan = sc.vspan; + c.ClearText(sc.text.GetFirstFormat(style), style); + } + } + int c = format.column[min(column, GetColumns() - 1)]; + format.column.Insert(column, c); + Normalize(); +} + +void RichTable::SplitCell(Point cl, Size sz, const RichStyles& style) +{ + const RichCell& sc = cell[cl.y][cl.x]; + int ext = sz.cy - cell[cl.y][cl.x].vspan - 1; + if(ext > 0) { + cell.InsertN(cl.y + 1, ext); + if(cl.y < format.header) + format.header += ext; + for(int i = 0; i < ext; i++) { + cell[cl.y + 1 + i].SetCount(GetColumns()); + for(int j = 0; j < GetColumns(); j++) { + RichCell& c = cell[cl.y + 1 + i][j]; + const RichCell& sc = cell[cl.y][j]; + c.format = sc.format; + c.hspan = sc.hspan; + c.ClearText(sc.text.GetFirstFormat(style), style); + } + } + for(int i = 0; i < GetColumns(); i++) { + CellInfo& cf = ci[cl.y][i]; + if(cf.valid) + cell[cl.y][i].vspan += ext; + else + if(cf.master.x == i) + cell[cf.master.y][cf.master.x].vspan += ext; + } + } + + cell[cl.y][cl.x].vspan = 0; + if(ext < 0) + cell[cl.y + sz.cy - 1][cl.x].vspan = -ext; + for(int i = 1; i < sz.cy; i++) { + RichCell& c = cell[cl.y + i][cl.x]; + c.format = sc.format; + c.hspan = sc.hspan; + c.ClearText(sc.text.GetFirstFormat(style), style); + } + + Normalize0(); + ext = sz.cx - cell[cl.y][cl.x].hspan - 1; + if(ext > 0) { + int clx = 0; + for(int i = 0; i <= cell[cl.y][cl.x].hspan; i++) + clx += format.column[cl.x + i]; + format.column.InsertN(cl.x, ext); + int q = clx / sz.cx; + for(int i = 1; i < sz.cx; i++) + format.column[cl.x + i] = q; + format.column[cl.x] = clx - (sz.cx - 1) * q; + for(int i = 0; i < cell.GetCount(); i++) { + cell[i].InsertN(cl.x + 1, ext); + for(int q = 0; q < ext; q++) { + RichCell& c = cell[i][cl.x + 1 + q]; + const RichCell& sc = cell[i][cl.x]; + c.format = sc.format; + c.vspan = sc.vspan; + c.ClearText(sc.text.GetFirstFormat(style), style); + } + CellInfo& cf = ci[i][cl.x]; + if(cf.valid) + cell[i][cl.x].hspan += ext; + else + if(cf.master.y == i) + cell[cf.master.y][cf.master.x].hspan += ext; + } + } + for(int i = 0; i < sz.cy; i++) + for(int j = 0; j < sz.cx; j++) { + RichCell& c = cell[cl.y + i][cl.x + j]; + if(j < sz.cx - 1) + c.hspan = 0; + if(j) { + const RichCell& sc = cell[cl.y + i][cl.x]; + c.format = sc.format; + c.vspan = sc.vspan; + c.ClearText(sc.text.GetFirstFormat(style), style); + } + } + Normalize(); +} + +void sMatchRect(Rect& t, const Rect& s) +{ + if(t.left != s.left) + t.left = Null; + if(t.top != s.top) + t.top = Null; + if(t.right != s.right) + t.right = Null; + if(t.bottom != s.bottom) + t.bottom = Null; +} + +RichCell::Format RichTable::GetCellFormat(const Rect& sel) const +{ + RichCell::Format fmt; + for(int i = sel.top; i <= sel.bottom; i++) + for(int j = sel.left; j <= sel.right; j++) + if(i == sel.top && j == sel.left) + fmt = cell[i][j].format; + else + if(ci[i][j].valid) { + const RichCell::Format& f = cell[i][j].format; + sMatchRect(fmt.border, f.border); + sMatchRect(fmt.margin, f.margin); + if(fmt.align != f.align) + fmt.align = Null; + if(fmt.color != f.color) + fmt.color = Null; + if(fmt.bordercolor != f.bordercolor) + fmt.bordercolor = Null; + } + return fmt; +} + +void sSetRect(Rect& t, const Rect& s) +{ + if(!IsNull(s.left)) + t.left = s.left; + if(!IsNull(s.top)) + t.top = s.top; + if(!IsNull(s.right)) + t.right = s.right; + if(!IsNull(s.bottom)) + t.bottom = s.bottom; +} + +void RichTable::SetCellFormat(const Rect& sel, const RichCell::Format& fmt, bool setkeep) +{ + for(int i = sel.top; i <= sel.bottom; i++) + for(int j = sel.left; j <= sel.right; j++) + if(ci[i][j].valid) { + RichCell::Format& f = cell[i][j].format; + sSetRect(f.border, fmt.border); + sSetRect(f.margin, fmt.margin); + if(!IsNull(fmt.align)) + f.align = fmt.align; + if(!IsNull(fmt.color)) + f.color = fmt.color; + if(!IsNull(fmt.bordercolor)) + f.bordercolor = fmt.bordercolor; + if(!IsNull(fmt.minheight)) + f.minheight = fmt.minheight; + if(setkeep) + f.keep = fmt.keep; + } + + Normalize(); +} + +void RichTable::Paste(Point pos, const RichTable& tab) +{ + for(int i = 0; i < tab.GetRows(); i++) + for(int j = 0; j < min(GetColumns() - pos.x, tab.GetColumns()); j++) + cell.At(i + pos.y, cell[cell.GetCount() - 1]).At(j + pos.x) <<= tab[i][j]; + Normalize(); +} + +void RichTable::AdjustSel(Rect& sel) const +{ +again: + for(int i = sel.top; i <= sel.bottom; i++) + for(int j = sel.left; j <= sel.right; j++) { + Point p = GetMasterCell(i, j); + const RichCell& c = cell[p.y][p.x]; + Point pp = p + Point(c.hspan, c.vspan); + if(p.x < sel.left) { + sel.left = p.x; + goto again; + } + if(pp.x > sel.right) { + sel.right = pp.x; + goto again; + } + if(p.y < sel.top) { + sel.top = p.y; + goto again; + } + if(pp.y > sel.bottom) { + sel.bottom = pp.y; + goto again; + } + } +} + +RichTable::RichTable(const RichTable& src, int) +{ + format = src.format; + cell <<= src.cell; + r_row = -1; + Normalize(); +} + +RichTable::RichTable() +{ + cpy.page = -1; + r_row = -1; + Invalidate(); +} + +END_UPP_NAMESPACE diff --git a/uppdev/RichTextP/TableLayout.cpp b/uppdev/RichTextP/TableLayout.cpp new file mode 100644 index 000000000..16bad9f33 --- /dev/null +++ b/uppdev/RichTextP/TableLayout.cpp @@ -0,0 +1,176 @@ +#include "RichText.h" + +NAMESPACE_UPP + +bool RichTable::Reduce(RichContext& rc) const +{ + Rect br = rc.page; + rc.page.left += format.lm; + rc.page.right -= format.rm; + rc.page.Deflate(format.frame); + if(rc.page.IsEmpty()) + rc.page = br; + return !rc.page.IsEmpty(); +} + + +const RichTable::TabLayout& RichTable::Realize(RichContext rc) const +{ + if(rc.py != cpy || rc.page != cpage) { + rc.py.y += format.before; + if(rc.py.y > rc.page.bottom) { + rc.py.y = rc.page.top; + rc.py.page++; + } + cpage = rc.page; + cpy = rc.py; + Reduce(rc); + clayout.page = rc.page; + clayout.hasheader = false; + clayout.sz = GetSize(); + if(format.header && cell.GetCount()) { + int hy = min(format.header, cell.GetCount()); + RichContext nrc = rc; + nrc.py.page = 0; + nrc.py.y = clayout.page.top; + clayout.header = Realize(nrc, hy); + if(clayout.header[0].py.page == clayout.header[hy - 1].pyy.page) { + Layout x = Realize(rc, cell.GetCount()); + if(cell.GetCount() > hy && rc.py.page != x[hy].py.page) { + rc.py.page++; + rc.py.y = rc.page.top; + } + clayout.hasheader = true; + rc.page.top = clayout.page.top = clayout.header[hy - 1].pyy.y + format.grid; + } + } + clayout.page0 = rc.py.page; + (Layout&)clayout = Realize(rc, cell.GetCount()); + if(format.keep && cell.GetCount()) { + if(clayout[0].py.page != clayout[cell.GetCount() - 1].pyy.page) { + rc.py.page++; + rc.py.y = rc.page.top; + } + clayout.page0 = rc.py.page; + (Layout&)clayout = Realize(rc, cell.GetCount()); + } + } + return clayout; +} + +RichTable::Layout RichTable::Realize(RichContext rc, int ny) const +{ + Layout tab; + + int nx = format.column.GetCount(); + tab.row.Alloc(ny); + for(int i = 0; i < ny; i++) + tab[i].cell.Alloc(nx); + tab.col.Alloc(nx); + + int sum = 0; + for(int i = 0; i < nx; i++) + sum += format.column[i]; + + int x = 0; + int xx = rc.page.left; + int dcx = rc.page.Width(); + for(int i = 0; i < nx; i++) { + Rect& cp = tab.col[i]; + cp = rc.page; + cp.left = xx; + x += format.column[i]; + xx = cp.right = x * dcx / sum + rc.page.left; + } + + int f2 = format.grid / 2; + int ff2 = format.grid - f2; + + rc.py.y += format.frame; + for(int i = 0; i < ny; i++) { + const Array& row = cell[i]; + PaintRow& pr = tab[i]; + pr.first = i == 0; + pr.gpy = rc.py; + if(i) + rc.py.y += format.grid; + for(int j = 0; j < nx;) { + PaintCell& pc = pr[j]; + const RichCell& cell = row[j]; + if(pc.top) { + pc.page = tab.col[j]; + pc.page.right = tab.col[min(nx - 1, j + cell.hspan)].right; + pc.bottom = false; + int ms = min(ny - 1, i + cell.vspan); + for(int k = i + 1; k <= ms; k++) { + PaintCell& pc = tab[k][j]; + pc.top = pc.bottom = false; + } + tab[ms][j].bottom = true; + pc.left = pc.page.left; + pc.right = pc.page.right; + if(j) + pc.page.left += f2; + if(j + cell.hspan < nx - 1) + pc.page.right -= ff2; + } + j += cell.hspan + 1; + } + bool keep = i < format.header || format.keep; + for(int j = 0; j < nx;) { + const RichCell& cell = row[j]; + if(pr[j].top && cell.format.keep) { + keep = true; + break; + } + j += cell.hspan + 1; + } + bool span = false; + for(int j = 0; j < nx;) { + const RichCell& cell = row[j]; + if(!pr[j].top) { + span = true; + break; + } + j += cell.hspan + 1; + } + if(!span) + for(int j = 0; j < nx;) { + const RichCell& cell = row[j]; + PaintCell& pc = pr[j]; + if(pc.top) { + rc.page = pc.page; + PageY ty = cell.GetTop(rc); + PageY ky = rc.py; + if(keep) + ky = cell.GetHeight(rc); + if(ty.page != rc.py.page || ky.page != rc.py.page) { + rc.Page(); + pr.gpy = rc.py; + pr.first = true; + break; + } + } + j += cell.hspan + 1; + } + pr.py = rc.py; + for(int j = 0; j < nx;) { + const RichCell& cell = row[j]; + PaintCell& pc = pr[j]; + rc.page = pc.page; + if(pc.top) + tab[min(ny - 1, i + cell.vspan)][j].hy = cell.GetHeight(rc); + j += cell.hspan + 1; + } + for(int j = 0; j < nx;) { + const RichCell& cell = row[j]; + if(pr[j].bottom) + rc.py = max(pr[j].hy, rc.py); + j += cell.hspan + 1; + } + tab.pyy = pr.pyy = rc.py; + } + return tab; +} + +END_UPP_NAMESPACE diff --git a/uppdev/RichTextP/TablePaint.cpp b/uppdev/RichTextP/TablePaint.cpp new file mode 100644 index 000000000..799e47bb3 --- /dev/null +++ b/uppdev/RichTextP/TablePaint.cpp @@ -0,0 +1,372 @@ +#include "RichText.h" + +NAMESPACE_UPP + +RichTable::Format::Format() +{ + grid = 4; + gridcolor = Black; + frame = 10; + framecolor = Black; + before = after = lm = rm = 0; + header = 0; + keep = false; +} + +#include "RichText.h" + +void RichTable::ExpandFrr(VectorMap& frr, int pi, int l, int r, int t, int b) +{ + Rect& fr = frr.GetAdd(pi, Rect(INT_MAX, INT_MAX, INT_MIN, INT_MIN)); + fr.left = min(fr.left, l); + fr.right = max(fr.right, r); + fr.top = min(fr.top, t); + fr.bottom = max(fr.bottom, b); +} + +bool RichTable::RowPaint(PageDraw& pw, const RichStyles& st, const Layout& tab, + int i, int ny, const Rect& pg, VectorMap& frr, + PaintInfo& pi, int pd, bool sel) const +{ + RichContext rc(st); + const Array& row = cell[i]; + const PaintRow& pr = tab[i]; + rc.py = pr.py; + rc.py.page += pd; + int gridln = LineZoom(pi.zoom, format.grid); + int ff2ln = gridln - gridln / 2; + Color gc = format.gridcolor; + if(gridln == 0 && !IsNull(pi.showcodes)) { + gridln = 1; + gc = pi.showcodes; + } + for(int j = 0; j < format.column.GetCount();) { + const RichCell& cell = row[j]; + const PaintCell& pc = pr[j]; + if(pc.top) { + PageY pyy = tab[min(ny - 1, i + cell.vspan)].pyy; + pyy.page += pd; + bool paint = pyy > pi.top; + rc.page = pr[j].page; + Rect xpg = pg; + int y; + int ny = pi.zoom * pyy.y; + y = pi.zoom * pr.gpy.y; + if(j) { + xpg.left = pi.zoom * pc.left - ff2ln; + if(pyy.page == rc.py.page) + pw.Page(rc.py.page).DrawRect(xpg.left, y, gridln, ny - y, gc); + else { + pw.Page(rc.py.page).DrawRect(xpg.left, y, gridln, pg.bottom - y, gc); + for(int i = rc.py.page + 1; i < pyy.page; i++) + pw.Page(i).DrawRect(xpg.left, pg.top, gridln, pg.bottom - pg.top, gc); + pw.Page(pyy.page).DrawRect(xpg.left, pg.top, gridln, ny - pg.top, gc); + } + xpg.left += gridln; + } + if(j + cell.hspan < format.column.GetCount() - 1) + xpg.right = pi.zoom * pc.right - ff2ln; + if(!pr.first) { + y += gridln; + if(paint) + pw.Page(rc.py.page).DrawRect(xpg.left, y - gridln, xpg.Width(), gridln, gc); + } + if(paint) + row[j].Paint(pw, rc, pyy, xpg, y, ny, pi, + sel && j >= pi.cells.left && i >= pi.cells.top && + j + cell.hspan <= pi.cells.right && i + cell.vspan <= pi.cells.bottom); + if(pyy.page == rc.py.page) + ExpandFrr(frr, pyy.page, xpg.left, xpg.right, y, ny); + else { + ExpandFrr(frr, rc.py.page, xpg.left, xpg.right, y, xpg.bottom); + for(int i = rc.py.page + 1; i < pyy.page; i++) + ExpandFrr(frr, i, xpg.left, xpg.right, xpg.top, xpg.bottom); + ExpandFrr(frr, pyy.page, xpg.left, xpg.right, xpg.top, ny); + } + int l = cell.text.GetLength() + 1; + pi.sell -= l; + pi.selh -= l; + pi.tablesel -= cell.text.GetTableCount(); + } + j += cell.hspan + 1; + } + + return rc.py >= pi.bottom; +} + +void RichTable::Paint(PageDraw& pw, RichContext rc, const PaintInfo& _pi) const +{ + const TabLayout& tab = Realize(rc); + if(tab.page.IsEmpty()) + return; + Rect p = tab.page; + PaintInfo pi = _pi; + int frameln = LineZoom(pi.zoom, format.frame); + int gridln = LineZoom(pi.zoom, format.grid); + Rect pg = rc.page; + pg.left += format.lm; + pg.right -= format.rm; + pg.left = pi.zoom * pg.left; + pg.right = pi.zoom * pg.right; + pg.top = pi.zoom * pg.top; + pg.bottom = pi.zoom * pg.bottom; + pg.Deflate(frameln); + int hy = min(format.header, cell.GetCount()); + Rect hpg = pg; + if(tab.hasheader) { + hpg.bottom = pi.zoom * tab.header[hy - 1].pyy.y; + pg.top = hpg.bottom + gridln; + } + bool allsel = false; + if(pi.sell < 0 && pi.selh >= 0) { + pi.sell = pi.selh = 0; + allsel = true; + } + bool sel = pi.tablesel == 0; + int ny = cell.GetCount(); + VectorMap frr; + for(int i = 0; i < ny; i++) + if(RowPaint(pw, rc.styles, tab, i, ny, pg, frr, pi, 0, sel)) + break; + + Color gc = format.gridcolor; + Color fc = format.framecolor; + int fl = frameln; + if(!IsNull(pi.showcodes)) { + if(fl == 0 && !IsNull(pi.showcodes)) { + fl = 1; + fc = pi.showcodes; + } + if(gridln == 0) { + gridln = 1; + gc = pi.showcodes; + } + } + for(int i = 0; i < frr.GetCount(); i++) { + PaintInfo _pi = pi; + pi.tablesel = 0; + pi.sell = pi.selh = -1; + int pgi = frr.GetKey(i); + Draw& w = pw.Page(pgi); + if(pgi > tab.page0 && tab.hasheader) + for(int i = 0; i < hy; i++) { + RowPaint(pw, rc.styles, tab.header, i, hy, hpg, frr, pi, pgi, false); + w.DrawRect(pg.left, hpg.bottom, pg.Width(), gridln, format.gridcolor); + } + Rect r = frr[i].Inflated(frameln); + if(!r.IsEmpty()) { + DrawFatFrame(w, r, fc, fl); + if(allsel) + w.DrawRect(r, InvertColor); + } + } +} + +PageY RichTable::GetHeight(RichContext rc) const +{ + PageY pyy = Realize(rc).pyy; + pyy.y += format.frame; + pyy.y += format.after; + if(pyy.y > rc.page.bottom) { + pyy.y = rc.page.top; + pyy.page++; + } + return pyy; +} + +PageY RichTable::GetTop(RichContext rc) const +{ + if(cell.GetCount() == 0) + return rc.py; + rc.py = Realize(rc)[0].py; + rc.py.y -= format.frame; + return rc.py; +} + +RichCaret RichTable::GetCaret(int pos, RichContext rc) const +{ + ASSERT(pos >= 0); + int nx = format.column.GetCount(); + int ny = cell.GetCount(); + int ti = 0; + const TabLayout& tab = Realize(rc); + for(int i = 0; i < ny; i++) { + const PaintRow& pr = tab[i]; + rc.py = pr.py; + PageY pyy; + for(int j = 0; j < nx; j++) { + if(ci[i][j].valid) { + const RichCell& cl = cell[i][j]; + pyy = tab[min(ny - 1, i + cl.vspan)].pyy; + int l = cl.text.GetLength() + 1; + if(pos < l) { + rc.page = pr[j].page; + return cl.GetCaret(pos, rc, pyy); + } + ti += cl.text.GetTableCount(); + pos -= l; + } + } + } + NEVER(); + return RichCaret(); +} + +int RichTable::GetPos(int x, PageY y, RichContext rc) const +{ + int nx = format.column.GetCount(); + int ny = cell.GetCount(); + const TabLayout& tab = Realize(rc); + int pos = 0; + for(int i = 0; i < ny; i++) { + const PaintRow& pr = tab[i]; + rc.py = pr.py; + for(int j = 0; j < nx; j++) + if(ci[i][j].valid) { + const RichCell& cl = cell[i][j]; + const PaintCell& pc = pr[j]; + PageY pyy = tab[min(ny - 1, i + cl.vspan)].pyy; + if(y < pyy + && (j == 0 || x >= pc.page.left - format.grid) + && (j == nx - 1 || x < pc.page.right)) { + rc.page = pc.page; + return cl.GetPos(x, y, rc, pyy) + pos; + } + pos += cl.text.GetLength() + 1; + } + } + return pos ? pos - 1 : 0; +} + +RichHotPos RichTable::GetHotPos(int x, PageY y, int tolerance, RichContext rc) const +{ + RichHotPos hp; + hp.textleft = rc.page.left; + hp.textcx = rc.page.Width(); + int nx = format.column.GetCount(); + int ny = cell.GetCount(); + const TabLayout& tab = Realize(rc); + if(abs(x - tab.page.left) <= tolerance) { + hp.table = 0; + hp.column = RICHHOT_LM; + hp.delta = x - tab.page.left; + return hp; + } + if(abs(x - tab.page.right) <= tolerance) { + hp.table = 0; + hp.column = RICHHOT_RM; + hp.delta = x - tab.page.right; + return hp; + } + int ti = 0; + for(int i = 0; i < ny; i++) { + const PaintRow& pr = tab[i]; + rc.py = pr.py; + for(int j = 0; j < nx; j++) { + if(ci[i][j].valid) { + const RichCell& cl = cell[i][j]; + const PaintCell& pc = pr[j]; + PageY pyy = tab[min(ny - 1, i + cl.vspan)].pyy; + if(y < pyy) { + if(j < nx - 1 && abs(x - pc.page.right) <= tolerance) { + hp.table = 0; + hp.column = j + cl.hspan; + hp.delta = x - pc.page.right; + hp.left = tab.page.left; + hp.cx = tab.page.Width(); + return hp; + } + if((j == 0 || x >= pc.page.left - format.grid) && (j == nx - 1 || x < pc.page.right)) { + rc.page = pc.page; + hp = cl.GetHotPos(x, y, tolerance, rc, pyy); + hp.table += ti; + return hp; + } + } + ti += cl.text.GetTableCount(); + } + } + } + return RichHotPos(); +} + +int RichTable::GetVertMove(int pos, int gx, RichContext rc, int dir) const +{ + if(cell.GetCount() == 0) + return -1; + int nx = format.column.GetCount(); + int ny = cell.GetCount(); + const TabLayout& tab = Realize(rc); + Point cp; + if(pos >= 0) + cp = FindCell(pos); + else { + cp.y = dir > 0 ? 0 : cell.GetCount() - 1; + cp.x = 0; + const PaintRow& pr = tab[cp.y]; + for(int j = 0; j < nx; j++) + if(ci[cp.y][cp.x].valid && gx < pr[j].page.right) { + cp.x = j; + break; + } + pos = -1; + } + for(;;) { + rc.page = tab[cp.y][cp.x].page; + const RichCell& c = cell[cp.y][cp.x]; + RichContext rc1 = rc; + if(c.Reduce(rc1)) { + int q = cell[cp.y][cp.x].text.GetVertMove(pos, gx, rc1, dir); + if(q >= 0) + return q + GetCellPos(cp); + } + pos = -1; + cp.y += dir; + if(cp.y < 0 || cp.y >= ny) + return -1; + } +} + +void RichTable::GatherValPos(Vector& f, RichContext rc, int pos, int type) const +{ + ASSERT(pos >= 0); + int nx = format.column.GetCount(); + int ny = cell.GetCount(); + const TabLayout& tab = Realize(rc); + for(int i = 0; i < ny; i++) { + const PaintRow& pr = tab[i]; + rc.py = pr.py; + for(int j = 0; j < nx; j++) + if(ci[i][j].valid) { + const RichCell& cl = cell[i][j]; + rc.page = pr[j].page; + cl.GatherValPos(f, rc, tab[min(ny - 1, i + cl.vspan)].pyy, pos, type); + pos += cl.text.GetLength() + 1; + } + } +} + +void RichTable::ApplyZoom(Zoom z, const RichStyles& ostyle, const RichStyles& zstyle) +{ + Invalidate(); + r_row = -1; + int nx = format.column.GetCount(); + int ny = cell.GetCount(); + format.frame = LineZoom(z, format.frame); + format.grid = LineZoom(z, format.grid); + format.before *= z; + format.lm *= z; + format.rm *= z; + format.after *= z; + for(int i = 0; i < ny; i++) + for(int j = 0; j < nx; j++) + if(ci[i][j].valid) { + RichCell& c = cell[i][j]; + c.format.border *= z; + c.format.margin *= z; + c.format.minheight *= z; + c.text.ApplyZoom(z, ostyle, zstyle); + } +} + +END_UPP_NAMESPACE diff --git a/uppdev/RichTextP/Text.h b/uppdev/RichTextP/Text.h new file mode 100644 index 000000000..284031485 --- /dev/null +++ b/uppdev/RichTextP/Text.h @@ -0,0 +1,135 @@ +class RichText : public RichTxt, public DeepCopyOption { + RichStyles style; + String footer; // ugly hack + bool nolinks; + + void Init(); + + struct StyleChangeOp; + struct SetStylesOp; + struct RemoveStyleOp; + struct MergeStylesOp; + friend struct MergeStylesOp; + struct OverrideStylesOp; + +public: + void Clear(); + + int GetStyleCount() const { return style.GetCount(); } + Uuid GetStyleId(int i) const { return style.GetKey(i); } + Uuid GetStyleId(const String& name) const; + const RichStyle& GetStyle(int i) const { return style[i]; } + const RichStyle& GetStyle(const Uuid& id) const { return UPP::GetStyle(style, id); } + void SetStyle(const Uuid& id, const RichStyle& _style); + bool HasStyle(const Uuid& id) { return style.Find(id) >= 0; } + void RemoveStyle(const Uuid& id); + + const RichStyles& GetStyles() const { return style; } + void SetStyles(const RichStyles& styles); + void OverrideStyles(const RichStyles& masterstyles, + bool matchname = true, bool addmissing = true); + void MergeStyles(const RichText& text); + + RichPara Get(int i) const { return RichTxt::Get(i, style); } + void Cat(const RichPara& p) { RichTxt::Cat(p, style); } + void CatPick(pick_ RichText& p); + using RichTxt::CatPick; + + RichContext Context(const Rect& page) const; + + RichPos GetRichPos(int pos, int maxlevel = INT_MAX) const; + int operator[](int pos) const { return GetRichPos(pos).chr; } + + void ApplyZoom(Zoom z); + + PageY GetHeight(PageY py, const Rect& page) const; + PageY GetHeight(const Rect& page) const; + void Paint(PageDraw& w, PageY py, const Rect& page, const PaintInfo& pi) const; + void Paint(PageDraw& w, const Rect& page, const PaintInfo& pi) const; + RichCaret GetCaret(int pos, const Rect& page) const; + int GetPos(int x, PageY y, const Rect& page) const; + RichHotPos GetHotPos(int x, PageY y, int tolerance, const Rect& page) const; + int GetVertMove(int pos, int gx, const Rect& page, int dir) const; + int GetWidth() const; + + int GetHeight(int cx) const; + int GetHeight(Zoom zoom, int cx) const; + void Paint(Draw& w, int x, int y, int cx, const PaintInfo& pi) const; + void Paint(Zoom zoom, Draw& w, int x, int y, int cx) const; + void Paint(Draw& w, int x, int y, int cx) const; + + Vector GetValPos(const Rect& page, int type) const; + + int AdjustCursor(int anchor, int cursor) const; + + void Remove(int pos, int count); + void Insert(int pos, const RichText& p); + RichText Copy(int pos, int count) const; + + FormatInfo GetFormatInfo(int pos, int count) const; + void ApplyFormatInfo(int pos, const FormatInfo& fi, int count); + + void ReStyle(int pos, const Uuid& id); + + Formating SaveFormat(int pos, int count) const; + void RestoreFormat(int pos, const Formating& info); + + + RichTable::Format GetTableFormat(int table) const; + void SetTableFormat(int table, const RichTable::Format& fmt); + int SetTable(int pos, const RichTable& table); + void DestroyTable(int table); + RichTable CopyTable(int table) const; + void ReplaceTable(int table, const RichTable& tab); + void RemoveParaSpecial(int table, bool before); + bool CanRemoveParaSpecial(int table, bool before); + void InsertParaSpecial(int table, bool before, const RichPara::Format& fmt); + bool ShouldInsertParaSpecial(int table, bool before); + RichTable CopyTable(int table, const Rect& sel) const; + void ClearTable(int table, const Rect& sel); + void PasteTable(int table, Point pos, const RichTable& tab); + void InsertTableRow(int table, int row); + void RemoveTableRow(int table, int row); + void InsertTableColumn(int table, int column); + void RemoveTableColumn(int table, int column); + Point GetMasterCell(int table, int row, int column); + Point GetMasterCell(int table, Point p) { return GetMasterCell(table, p.y, p.x); } + void SplitCell(int table, Point cell, Size sz); + void JoinCell(int table, const Rect& sel); + RichCell::Format GetCellFormat(int table, const Rect& sel) const; + void SetCellFormat(int table, const Rect& sel, const RichCell::Format& fmt, bool setkeep); + FormatInfo GetTableFormatInfo(int table, const Rect& sel) const; + void ApplyTableFormatInfo(int table, const Rect& sel, const RichText::FormatInfo& fi); + void AdjustTableSel(int table, Rect& sel) const { return GetConstTable(table).AdjustSel(sel); } + + bool Iterate(Iterator& r) const { return RichTxt::Iterate(r, 0, style); } + bool Iterate(UpdateIterator& r) { return RichTxt::Iterate(r, 0, style); } + + bool EvaluateFields(VectorMap& vars) { return RichTxt::EvaluateFields(style, vars); } + + void InvalidateAll() { r_type = ALL; } + void Validate(); + bool GetInvalid(PageY& top, PageY& bottom, const Rect& page, + int sell, int selh, int osell, int oselh) const; + + struct ClipboardType { + ClipboardType(); + virtual ~ClipboardType(); + + virtual int Level(); + virtual RichText ReadClipboard(const RichPara::Format& f) = 0; + virtual void WriteClipboard(const RichText& text) = 0; + }; + + static void Register(ClipboardType& type); + + //Ugly hacks + void SetFooter(const String& s) { footer = s; } + String GetFooter() const { return footer; } + void PrintNoLinks(bool b = true) { nolinks = b; } + bool IsPrintNoLinks() const { return nolinks; } + + RichText() { Init(); } + RichText(const RichText& x, int); + RichText(pick_ RichTxt& x, pick_ RichStyles& st); +}; diff --git a/uppdev/RichTextP/TextData.cpp b/uppdev/RichTextP/TextData.cpp new file mode 100644 index 000000000..e337b4a3a --- /dev/null +++ b/uppdev/RichTextP/TextData.cpp @@ -0,0 +1,332 @@ +#include "RichText.h" + +NAMESPACE_UPP + +void RichText::CatPick(pick_ RichText& p) +{ + MergeStyles(p); + int c = part.GetCount(); + part.AppendPick(p.part); + for(int i = c; i < part.GetCount(); i++) + if(IsPara(i)) + part[i].Get().cx = -1; + RefreshAll(); +} + +RichPos::RichPos() +{ + tabtextparti = tabtextpartcount = tabposintabtext = tabtextlen = table = tablen = posintab = + celllen = posincell = parai = partcount = posinpara = paralen = 0; + parenttab = Null; + + tabsize = Size(0, 0); + cell = Point(0, 0); + chr = '?'; +} + +RichPos RichText::GetRichPos(int pos, int maxlevel) const +{ + RichPos rp; + rp.level = 0; + RichTxt::GetRichPos(pos, rp, 0, maxlevel, style); + if(IsNull(rp.parenttab)) + rp.parenttab = 0; + return rp; +} + +int RichText::AdjustCursor(int anchor, int cursor) const +{ + int d = anchor; + const RichTxt& txt = GetConstText(anchor); + d -= anchor; + if(!(cursor == txt.GetLength() && !txt.IsTable(txt.GetPartCount() - 1))) + cursor = minmax(cursor - d, 0, txt.GetLength()); + int c = cursor; + int pi = txt.FindPart(c); + if(txt.IsTable(pi)) { + if(cursor < anchor) { + while(--pi >= 0) + if(txt.IsPara(pi)) + return d + txt.GetPartPos(pi) + txt.GetPartLength(pi); + while(++pi < txt.GetPartCount()) + if(txt.IsPara(pi)) + return d + txt.GetPartPos(pi); + return d + anchor; + } + else { + while(++pi < txt.GetPartCount()) + if(txt.IsPara(pi)) + return d + txt.GetPartPos(pi); + while(--pi >= 0) + if(txt.IsPara(pi)) + return d + txt.GetPartPos(pi) + txt.GetPartLength(pi); + return d + anchor; + } + } + return d + cursor; +} + +void RichText::Remove(int pos, int count) +{ + GetLength(); + int p = pos; + int l = GetConstText(p).GetLength(); + count = min(l - p, count); + RichTxt& txt = GetUpdateText(pos); + int pos2 = pos + count; + int pi = txt.FindPart(pos); + int pi2 = txt.FindPart(pos2); + if(pi == pi2) { + RichPara pa, pa1; + pa = txt.Get(pi, style); + pa1 <<= pa; + pa.Trim(pos); + pa1.Mid(pos2); + pa.Append(pa1); + txt.Put(pi, pa, style); + } + else { + RichPara pa2 = txt.Get(pi2, style); + RichPara pa = txt.Get(pi, pa2.format.styleid, style); + txt.part.Remove(pi, pi2 - pi); + pa.Trim(pos); + pa2.Mid(pos2); + pa.Append(pa2); + pa.format = pa2.format; + txt.Put(pi, pa, style); + txt.SetRefreshFrom(pi); + } +} + +void RichText::Insert(int pos, const RichText& p) +{ + MergeStyles(p); + if(p.GetLength() == 0) + return; + ASSERT(pos >= 0 && pos <= GetLength()); + RichTxt& txt = GetUpdateText(pos); + int pi = txt.FindPart(pos); + int n = p.part.GetCount() - 1; + if(n) { + txt.part.Insert(pi + 1, p.part, 1, n); + RichPara pa1, pa2; + pa1 = txt.Get(pi, style); + pa2 = txt.Get(pi + n, pa1.format.styleid, style); + pa1.Mid(pos); + pa2.Append(pa1); + pa2.format = pa1.format; + txt.Put(pi + n, pa2, style); + pa2 = p.RichTxt::Get(0, p.GetStyle(p.GetParaStyle(0))); + pa1 = txt.Get(pi, pa2.format.styleid, style); + pa1.Trim(pos); + pa1.Append(pa2); + pa1.format = pa2.format; + txt.Put(pi, pa1, style); + txt.SetRefreshFrom(pi); + } + else { + RichPara pa0, pa2; + pa0 = txt.Get(pi, style); + pa2 <<= pa0; + pa0.Trim(pos); + pa2.Mid(pos); + pa0.Append(p.RichTxt::Get(0, p.GetStyle(pa0.format.styleid))); + pa0.Append(pa2); + txt.Put(pi, pa0, style); + } +} + +RichText RichText::Copy(int pos, int count) const +{ + RichText r; + r.SetStyles(style); + + const RichTxt& txt = GetConstText(pos); + bool addp = false; + if(count > txt.GetLength() - pos) { + count = txt.GetLength() - pos; + addp = true; + } + int pos2 = pos + count; + int pi = txt.FindPart(pos); + int pi2 = txt.FindPart(pos2); + + if(pi == pi2) { + RichPara pa = txt.Get(pi, style); + pa.Trim(pos2); + pa.Mid(pos); + r.Cat(pa); + } + else { + RichPara pa = txt.Get(pi++, style); + pa.Mid(pos); + r.Cat(pa); + while(pi < pi2) { + if(txt.IsPara(pi)) + r.Cat(txt.Get(pi, style)); + else { + RichTable tab(txt.GetTable(pi), 1); + r.CatPick(tab); + } + pi++; + } + pa = txt.Get(pi2, style); + pa.Trim(pos2); + r.Cat(pa); + } + if(addp) + r.Cat(RichPara()); + return r; +} + +RichText::FormatInfo RichText::GetFormatInfo(int pos, int count) const +{ + const RichTxt& txt = GetConstText(pos); + + count = min(txt.GetLength() - pos, count); + + int pos2 = pos + count; + int pi = txt.FindPart(pos); + int pi2 = txt.FindPart(pos2); + + FormatInfo fi; + RichPara::Format fmt; + RichPara pa = txt.Get(pi, style); + fmt = pa.format; + int i = pa.FindPart(pos); + if(i < pa.GetCount()) + (RichPara::CharFormat&)fmt = pa[i].format; + fi.Set(fmt); + i++; + if(pi == pi2) { + int i2 = pa.FindPart(pos2); + while(i < i2) + fi.Combine(pa[i++].format); + if(pos2 && i2 < pa.GetCount()) + fi.Combine(pa[i2].format); + } + else { + while(i < pa.GetCount()) + fi.Combine(pa[i++].format); + fi.Combine(pa.format); + pi++; + bool first = false; + txt.CombineFormat(fi, pi, pi2, first, style); + pa = txt.Get(pi2, style); + int i2 = pa.FindPart(pos2); + for(i = 0; i < i2; i++) + fi.Combine(pa[i].format); + if(pos2 && i2 < pa.GetCount()) + fi.Combine(pa[i2].format); + } + return fi; +} + +void RichText::ApplyFormatInfo(int pos, const FormatInfo& fi, int count) +{ + RichTxt& txt = GetUpdateText(pos); + + int pos2 = min(txt.GetLength(), pos + count); + int pi = txt.FindPart(pos); + int pi2 = txt.FindPart(pos2); + + ASSERT(txt.IsPara(pi) && txt.IsPara(pi2)); + + if(pi == pi2) { + RichPara pa, pa1, pa2; + pa = txt.Get(pi, style); + ApplyStyle(fi, pa, style); + pa1 <<= pa; + pa2 <<= pa; + pa.Trim(pos); + pa2.Mid(pos2); + pa1.Trim(pos2); + pa1.Mid(pos); + Apply(fi, pa1, style); + pa.Append(pa1); + pa.Append(pa2); + fi.ApplyTo(pa.format); + txt.Put(pi, pa, style); + } + else { + RichPara pa, pa1; +// if(fi.paravalid & RichText::STYLE) +// pa = txt.Get(pi, fi.styleid, style); +// else + pa = txt.Get(pi, style); + ApplyStyle(fi, pa, style); + pa1 <<= pa; + pa.Trim(pos); + pa1.Mid(pos); + Apply(fi, pa1, style); + fi.ApplyTo(pa.format); + pa.Append(pa1); + txt.Put(pi, pa, style); + pi++; + txt.ApplyFormat(fi, pi, pi2, style); +// if(fi.paravalid & RichText::STYLE) +// pa = txt.Get(pi2, fi.styleid, style); +// else + pa = txt.Get(pi2, style); + ApplyStyle(fi, pa, style); + pa1 <<= pa; + pa.Trim(pos2); + pa1.Mid(pos2); + Apply(fi, pa, style); + pa.Append(pa1); + txt.Put(pi2, pa, style); + } +} + +void RichText::ReStyle(int pos, const Uuid& id) +{ + int p = FindPart(pos); + RichPara pa = Get(p); + pa.format.styleid = id; + Put(p, pa, style); +} + +RichText::Formating RichText::SaveFormat(int pos, int count) const +{ + const RichTxt& txt = GetConstText(pos); + count += pos; + Formating r; + txt.SaveFormat(r, txt.FindPart(pos), txt.FindPart(count), style); + return r; +} + +void RichText::RestoreFormat(int pos, const RichText::Formating& info) +{ + RichTxt& txt = GetUpdateText(pos); + int ii = 0; + txt.RestoreFormat(txt.FindPart(pos), info, ii, style); +} + +void RichText::Init() +{ + RichTxt::Init(); + RichStyle& s = style.Add(RichStyle::GetDefaultId()); + s.name = "Default"; + nolinks = false; +} + +void RichText::Clear() +{ + RichTxt::Clear(); + style.Clear(); + Init(); +} + +RichText::RichText(const RichText& x, int) + : RichTxt(x, 1), style(x.style, 1) +{ + nolinks = x.nolinks; +} + +RichText::RichText(pick_ RichTxt& x, pick_ RichStyles& st) + : RichTxt(x), style(st) +{ + nolinks = false; +} + +END_UPP_NAMESPACE diff --git a/uppdev/RichTextP/TextPaint.cpp b/uppdev/RichTextP/TextPaint.cpp new file mode 100644 index 000000000..792844a96 --- /dev/null +++ b/uppdev/RichTextP/TextPaint.cpp @@ -0,0 +1,183 @@ +#include "RichText.h" + +NAMESPACE_UPP + +RichContext RichText::Context(const Rect& page) const +{ + RichContext c(style); + c.page = page; + c.py = PageY(0, page.top); + return c; +} + +PageY RichText::GetHeight(const Rect& page) const +{ + return RichTxt::GetHeight(Context(page)); +} + +PageY RichText::GetHeight(PageY py, const Rect& page) const +{ + RichContext ctx = Context(page); + ctx.py = py; + return RichTxt::GetHeight(ctx); +} + +int RichText::GetWidth() const +{ + return RichTxt::GetWidth(style); +} + +void RichText::Paint(PageDraw& w, PageY py, const Rect& page, const PaintInfo& pi) const +{ + RichContext ctx = Context(page); + ctx.py = py; + RichTxt::Paint(w, ctx, pi); +} + +void RichText::Paint(PageDraw& w, const Rect& page, const PaintInfo& pi) const +{ + RichTxt::Paint(w, Context(page), pi); +} + +RichCaret RichText::GetCaret(int pos, const Rect& page) const +{ + return RichTxt::GetCaret(pos, Context(page)); +} + +int RichText::GetPos(int x, PageY y, const Rect& page) const +{ + return RichTxt::GetPos(x, y, Context(page)); +} + +int RichText::GetVertMove(int pos, int gx, const Rect& page, int dir) const +{ + return RichTxt::GetVertMove(pos, gx, Context(page), dir); +} + +RichHotPos RichText::GetHotPos(int x, PageY y, int tolerance, const Rect& page) const +{ + RichHotPos p = RichTxt::GetHotPos(x, y, tolerance, Context(page)); + if(p.column < -2) + p.table = 0; + return p; +} + +Vector RichText::GetValPos(const Rect& page, int type) const +{ + Vector f; + GatherValPos(f, Context(page), 0, type); + return f; +} + +void RichText::Validate() +{ + r_type = NONE; + for(int i = 0; i < part.GetCount(); i++) + if(IsTable(i)) + part[i].Get().Validate(); +} + +bool RichText::GetInvalid(PageY& top, PageY& bottom, const Rect& page, + int sell, int selh, int osell, int oselh) const +{ + int spi; + int rtype = r_type; + if(sell != selh || osell != oselh) { + if(sell != osell) { + if(rtype == NONE) { + spi = FindPart(sell); + rtype = spi == FindPart(osell) ? SPARA : ALL; + } + else + rtype = ALL; + } + if(selh != oselh) { + if(rtype == NONE) { + spi = FindPart(selh); + rtype = spi == FindPart(oselh) ? SPARA : ALL; + } + else + rtype = ALL; + } + } + bottom = top = PageY(0, page.top); + if(rtype == NONE) { + bottom = top; + return false; + } + if(rtype == ALL) { + bottom = GetHeight(page); + return true; + } + RichContext rc = Context(page); + if(rtype == SPARA) { + rc.py = top = GetPartPageY(spi, rc); + bottom = GetNextPageY(spi, rc); + return true; + } + rc.py = top = GetPartPageY(r_parti, rc); + if(rtype == PARA) { + if(IsTable(r_parti)) + switch(GetTable(r_parti).GetInvalid(top, bottom, rc)) { + case -1: return false; + case 0: return true; + default: + bottom = GetHeight(page); + return true; + } + else { + Sync(r_parti, rc); + const Para& pp = part[r_parti].Get(); + if(r_paraocx == pp.cx && + r_paraocy == Sum(pp.linecy, 0) + pp.ruler + pp.before + pp.after && + r_keep == pp.keep && + r_keepnext == pp.keepnext && + r_newpage == pp.newpage) { + bottom = GetNextPageY(r_parti, rc); + return true; + } + } + } + bottom = GetHeight(page); + return true; +} + +int RichText::GetHeight(Zoom zoom, int cx) const +{ + int lwd = cx / zoom; + return GetHeight(Size(lwd, 0xFFFFFFF)).y * zoom; +} + +int RichText::GetHeight(int cx) const +{ + return GetHeight(Size(cx, 0xFFFFFFF)).y; +} + +void RichText::Paint(Draw& w, int x, int y, int cx, const PaintInfo& pinit) const +{ + SimplePageDraw pw(w); + PaintInfo pi(pinit); + pi.top = PageY(0, 0); + pi.bottom = PageY(0, INT_MAX); + pi.usecache = true; + pi.sizetracking = false; + pi.highlight = Null; + w.Offset(x, y); + Paint(pw, Size(cx / pi.zoom, INT_MAX), pi); + w.End(); +} + +void RichText::Paint(Zoom zoom, Draw& w, int x, int y, int cx) const +{ + PaintInfo pi; + pi.highlightpara = false; + pi.zoom = zoom; + Paint(w, x, y, cx, pi); +} + +void RichText::Paint(Draw& w, int x, int y, int cx) const +{ + Paint(Zoom(1, 1), w, x, y, cx); +} + +END_UPP_NAMESPACE diff --git a/uppdev/RichTextP/TextStyle.cpp b/uppdev/RichTextP/TextStyle.cpp new file mode 100644 index 000000000..c80be9310 --- /dev/null +++ b/uppdev/RichTextP/TextStyle.cpp @@ -0,0 +1,153 @@ +#include "RichText.h" + +NAMESPACE_UPP + +const RichStyle& RichStyle::GetDefault() +{ + return Single(); +} + +Uuid RichStyle::GetDefaultId() +{ + Uuid id; + id.a = 0; + id.b = 0; + id.c = 0; + id.d = 0; + return id; +} + +const RichStyle& GetStyle(const RichStyles& s, const Uuid& id) +{ + return s.Get(id, RichStyle::GetDefault()); +} + +int FindStyleWithName(const RichStyles& style, const String& name) +{ + for(int j = 0; j < style.GetCount(); j++) + if(style[j].name == name) + return j; + return -1; +} + +struct RichText::StyleChangeOp : RichTxt::ParaOp { + Uuid id, nid; + + virtual bool operator()(RichTxt::Para& p) { + if(p.styleid == id) { + p.styleid = nid; + p.cx = -1; + return true; + } + return false; + } +}; + +void RichText::SetStyle(const Uuid& id, const RichStyle& _style) +{ + Invalidate(); + RichStyle& s = style.GetAdd(id); + s = _style; + s.format.styleid = id; + s.format.language = 0; + s.format.newpage = false; + StyleChangeOp op; + op.nid = op.id = id; + Update(op); +} + +struct RichText::SetStylesOp : RichTxt::ParaOp { + RichStyles *style; + + virtual bool operator()(RichTxt::Para& p) { + p.cx = -1; + if(style->Find(p.styleid) < 0) + p.styleid = RichStyle::GetDefaultId(); + return true; + } +}; + +void RichText::SetStyles(const RichStyles& _style) +{ + Invalidate(); + style <<= _style; + SetStylesOp op; + op.style = &style; + Update(op); +} + +Uuid RichText::GetStyleId(const String& name) const +{ + int i = FindStyleWithName(style, name); + return i >= 0 ? style.GetKey(i) : RichStyle::GetDefaultId(); +} + +void RichText::RemoveStyle(const Uuid& id) +{ + ASSERT(id != RichStyle::GetDefaultId()); + Invalidate(); + StyleChangeOp op; + op.id = id; + op.nid = RichStyle::GetDefaultId(); + Update(op); + style.RemoveKey(id); +} + +struct RichText::MergeStylesOp : RichTxt::ParaOp { + RichText *text; + const RichText *stxt; + + virtual bool operator()(RichTxt::Para& p) { + if(text->style.Find(p.styleid) < 0) { + RichStyle& s = text->style.GetAdd(p.styleid); + s = stxt->GetStyle(p.styleid); + s.format.styleid = p.styleid; + } + return false; + } +}; + +void RichText::MergeStyles(const RichText& text) +{ + Invalidate(); + MergeStylesOp op; + op.stxt = &text; + op.text = this; + const_cast(text).Update(op); +} + +struct RichText::OverrideStylesOp : RichTxt::ParaOp { + VectorMap cs; + + virtual bool operator()(RichTxt::Para& p) { + p.styleid = cs.Get(p.styleid, RichStyle::GetDefaultId()); + p.cx = -1; + return true; + } +}; + +void RichText::OverrideStyles(const RichStyles& ms, bool matchname, bool addmissing) +{ + Invalidate(); + RichStyles orig = style; + style <<= ms; + OverrideStylesOp op; + for(int i = 0; i < orig.GetCount(); i++) { + Uuid pid = orig.GetKey(i); + int q; + if(ms.Find(pid) >= 0) + op.cs.Add(pid, pid); + else + if(matchname && (q = FindStyleWithName(style, orig[i].name)) >= 0) + op.cs.Add(pid, style.GetKey(q)); + else + if(addmissing) { + SetStyle(pid, orig[i]); + op.cs.Add(pid, pid); + } + } + + Update(op); +} + +END_UPP_NAMESPACE diff --git a/uppdev/RichTextP/TextTable.cpp b/uppdev/RichTextP/TextTable.cpp new file mode 100644 index 000000000..8ceb2dc34 --- /dev/null +++ b/uppdev/RichTextP/TextTable.cpp @@ -0,0 +1,225 @@ +#include "RichText.h" + +NAMESPACE_UPP + +RichTable::Format RichText::GetTableFormat(int table) const +{ + return GetConstTable(table).GetFormat(); +} + +void RichText::SetTableFormat(int table, const RichTable::Format& fmt) +{ + RichTable& tab = GetUpdateTable(table); + tab.SetFormat(fmt); + tab.Normalize(); +} + +int RichText::SetTable(int pos, const RichTable& table) +{ + RefreshAll(); + RichPos p = GetRichPos(pos); + int bpos = pos; + RichTxt& txt = GetUpdateText(pos); + int pi = txt.FindPart(pos); + ASSERT(pos == 0 && txt.GetPartLength(pi) == 0 && txt.IsPara(pi)); + RichTable pt(table, 1); + txt.SetPick(pi, pt); + return GetRichPos(bpos).table; +} + +RichTable RichText::CopyTable(int table) const +{ + RichTable tab(GetConstTable(table), 1); + return tab; +} + +void RichText::ReplaceTable(int table, const RichTable& tab) +{ + (GetUpdateTable(table) <<= tab).Normalize(); + RefreshAll(); +} + +void RichText::DestroyTable(int table) +{ + int pi; + RichTxt& txt = GetTableUpdateText(table, style, pi); + RichPara p; + txt.Set(pi, p, style); + RefreshAll(); +} + +void RichText::RemoveParaSpecial(int table, bool before) +{ + int pi; + RichTxt& txt = GetTableUpdateText(table, style, pi); + if(before) + pi--; + else + pi++; + ASSERT(txt.IsPara(pi) && txt.GetPartLength(pi) == 0); + txt.part.Remove(pi); + RefreshAll(); +} + +bool RichText::CanRemoveParaSpecial(int table, bool before) +{ + int pi; + if(!table) + return false; + RichTxt& txt = GetTableUpdateText(table, style, pi); + if(before) + pi--; + else + pi++; + return pi >= 0 && pi < txt.GetPartCount() && txt.IsPara(pi) && txt.GetPartLength(pi) == 0; +} + +bool RichText::ShouldInsertParaSpecial(int table, bool before) +{ + int pi; + RichTxt& txt = GetTableUpdateText(table, style, pi); + if(!before) + pi++; + return pi == 0 || pi >= txt.GetPartCount() || txt.IsTable(pi); +} + + +void RichText::InsertParaSpecial(int table, bool before, const RichPara::Format& fmt) +{ + int pi; + RichTxt& txt = GetTableUpdateText(table, style, pi); + if(!before) + pi++; + txt.RefreshAll(); + txt.part.Insert(pi); + RichPara p; + p.format = fmt; + txt.Set(pi, p, style); + RefreshAll(); +} + +RichTable RichText::CopyTable(int table, const Rect& sel) const +{ + return GetConstTable(table).Copy(sel); +} + +Point RichText::GetMasterCell(int table, int row, int column) +{ + return GetConstTable(table).GetMasterCell(row, column); +} + +void RichText::PasteTable(int table, Point pos, const RichTable& tab) +{ + GetUpdateTable(table).Paste(pos, tab); + RefreshAll(); +} + +void RichText::InsertTableRow(int table, int row) +{ + GetUpdateTable(table).InsertRow(row, style); + RefreshAll(); +} + +void RichText::RemoveTableRow(int table, int row) +{ + GetUpdateTable(table).RemoveRow(row); + RefreshAll(); +} + +void RichText::InsertTableColumn(int table, int column) +{ + GetUpdateTable(table).InsertColumn(column, style); + RefreshAll(); +} + +void RichText::RemoveTableColumn(int table, int column) +{ + GetUpdateTable(table).RemoveColumn(column); + RefreshAll(); +} + +void RichText::SplitCell(int table, Point cell, Size sz) +{ + GetUpdateTable(table).SplitCell(cell, sz, style); + RefreshAll(); +} + +void RichText::JoinCell(int table, const Rect& sel) +{ + RichTable& tab = GetUpdateTable(table); + RichCell& cell = tab[sel.top][sel.left]; + cell.vspan = sel.bottom - sel.top; + cell.hspan = sel.right - sel.left; + for(int i = sel.top; i <= sel.bottom; i++) + for(int j = sel.left; j <= sel.right; j++) { + if(tab(i, j) && (i != sel.top || j != sel.left)) { + RichTxt& t = tab[i][j].text; + for(int pi = 0; pi < t.GetPartCount(); pi++) + if(t.IsTable(pi)) + cell.text.CatPick(t.part[pi].Get()); + else + if(pi < t.GetPartCount() - 1 || t.GetPartLength(pi)) + cell.text.Cat(t.Get(pi, style), style); + tab[i][j].ClearText(); + } + } + tab.Normalize(); + RefreshAll(); +} + +RichCell::Format RichText::GetCellFormat(int table, const Rect& sel) const +{ + return GetConstTable(table).GetCellFormat(sel); +} + +void RichText::SetCellFormat(int table, const Rect& sel, const RichCell::Format& fmt, bool setkeep) +{ + GetUpdateTable(table).SetCellFormat(sel, fmt, setkeep); + RefreshAll(); +} + +void RichText::ClearTable(int table, const Rect& sel) +{ + RichTable& tab = GetUpdateTable(table); + for(int i = sel.top; i <= sel.bottom; i++) + for(int j = sel.left; j <= sel.right; j++) { + if(tab(i, j)) { + tab.InvalidateRefresh(i, j); + tab[i][j].ClearText(tab[i][j].text.GetFirstFormat(style), style); + } + } + tab.Normalize(); + RefreshAll(); +} + +RichText::FormatInfo RichText::GetTableFormatInfo(int table, const Rect& sel) const +{ + const RichTable& tab = GetConstTable(table); + bool first = true; + FormatInfo fi; + for(int i = sel.top; i <= sel.bottom; i++) + for(int j = sel.left; j <= sel.right; j++) { + if(tab(i, j)) { + const RichTxt& txt = tab[i][j].text; + txt.CombineFormat(fi, 0, txt.GetPartCount(), first, style); + } + } + return fi; +} + +void RichText::ApplyTableFormatInfo(int table, const Rect& sel, const RichText::FormatInfo& fi) +{ + RichTable& tab = GetUpdateTable(table); + for(int i = sel.top; i <= sel.bottom; i++) + for(int j = sel.left; j <= sel.right; j++) { + if(tab(i, j)) { + tab.InvalidateRefresh(i, j); + RichTxt& txt = tab[i][j].text; + txt.ApplyFormat(fi, 0, txt.GetPartCount(), style); + } + } + tab.Normalize(); + RefreshAll(); +} + +END_UPP_NAMESPACE diff --git a/uppdev/RichTextP/Txt.h b/uppdev/RichTextP/Txt.h new file mode 100644 index 000000000..c397ce26e --- /dev/null +++ b/uppdev/RichTextP/Txt.h @@ -0,0 +1,247 @@ +class RichTxt : DeepCopyOption { +public: + enum { + BOLD = 0x00000001, + ITALIC = 0x00000002, + UNDERLINE = 0x00000004, + FACE = 0x00000008, + HEIGHT = 0x00000010, + LANGUAGE = 0x00000020, + INK = 0x00000040, + PAPER = 0x00000080, + LINK = 0x00000100, + SSCRIPT = 0x00000200, + CAPITALS = 0x00000400, + STRIKEOUT = 0x00000800, + LANG = 0x00001000, + INDEXENTRY = 0x00002000, + DASHED = 0x00004000, + NOAA = 0x00008000, + }; + + enum { + ALIGN = 0x80000000, + BEFORE = 0x40000000, + LM = 0x20000000, + INDENT = 0x10000000, + RM = 0x08000000, + AFTER = 0x04000000, + TABSIZE = 0x02000000, + BULLET = 0x01000000, + NEWPAGE = 0x00800000, + KEEP = 0x00400000, + TABS = 0x00200000, + STYLE = 0x00100000, + LABEL = 0x00080000, + KEEPNEXT = 0x00040000, + ORPHAN = 0x00020000, + NUMBERING = 0x00010000, + SPACING = 0x00008000, + RULER = 0x00004000, + RULERINK = 0x00002000, + }; + + struct FormatInfo : RichPara::Format { + dword charvalid; + dword paravalid; + + void Set(const RichPara::Format& fmt); + void Set(const RichPara::CharFormat& fmt); + void Combine(const RichPara::CharFormat& fmt); + void Combine(const RichPara::Format& fmt); + void Combine(const FormatInfo& fmt); + void ApplyTo(RichPara::CharFormat& fmt) const; + void ApplyTo(RichPara::Format& fmt) const; + }; + + class Formating { + Vector styleid; + Vector format; + friend class RichText; + friend class RichTxt; + }; + +protected: + struct Para : DeepCopyOption { + Uuid styleid; + int length; + String content; + Array object; + mutable int cx; + mutable int cy; + mutable int ruler; + mutable int before; + mutable Vector linecy; + mutable int after; + mutable bool newpage; + mutable bool keep; + mutable bool keepnext; + mutable bool orphan; + mutable int numbering; + mutable Bits spellerrors; + mutable bool checked; + mutable bool haspos; + One number; + + Para(const Para& src, int); + Para() { length = 0; cx = -1; numbering = -1; checked = false; haspos = false; } + }; + + struct Part : MoveableAndDeepCopyOption< Part, Any> { + Part(const Part& src, int); + Part(); + }; + + Vector part; + mutable int length; + mutable int tabcount; + mutable Rect rect; + mutable Vector py; + + enum { + NONE, SPARA, PARA, FROM, ALL + }; + + int r_type; + int r_parti; + int r_paraocx; + int r_paraocy; + bool r_keep; + bool r_keepnext; + bool r_newpage; + + void Init(); + + void Invalidate(); + + void SetRefresh(int parai); + void SetRefreshFrom(int parai); + void RefreshAll() { SetRefreshFrom(0); } + + void ParaRemove(int parai, int pos, int count); + void ParaInsert(int parai, int pos, const RichPara& p); + RichPara ParaCopy(int parai, int pos, int count) const; + + void Put(int i, const RichPara& p, const RichStyle& s); + void Put(int i, const RichPara& p, const RichStyles& s); + + void Sync0(const Para& pp, int parti, const RichContext& rc) const; + inline void Sync(int parti, const RichContext& rc) const { + int cx = rc.page.Width(); + ASSERT(part[parti].Is()); + const Para& pp = part[parti].Get(); + if(pp.cx == cx) return; + Sync0(pp, parti, rc); + } + bool BreaksPage(PageY py, const Para& pp, int i, const Rect& page) const; + PageY GetNextPageY(int parti, const RichContext& rc) const; + PageY GetPartPageY(int parti, RichContext rc) const; + + struct ParaOp { + virtual bool operator()(RichTxt::Para& p) = 0; + virtual ~ParaOp() {} + }; + bool Update(ParaOp& op); + RichTxt& GetText0(int& pos, bool update); + RichTxt& GetUpdateText(int& pos); + const RichTxt& GetConstText(int& pos) const; + RichTable& GetTable0(int table, bool update); + RichTable& GetUpdateTable(int table); + const RichTable& GetConstTable(int table) const; + RichTxt& GetTableUpdateText(int table, const RichStyles& style, int& pi); + + void CombineFormat(FormatInfo& f, int pi, int pi2, bool& first, const RichStyles& style) const; + static void ApplyStyle(const FormatInfo& fi, RichPara& pa, const RichStyles& style); + static void Apply(const FormatInfo& fi, RichPara& pa, const RichStyles& style); + void ApplyFormat(const FormatInfo& f, int pi, int pi2, const RichStyles& style); + void SaveFormat(Formating& f, int p1, int p2, const RichStyles& style) const; + void RestoreFormat(int pi, const Formating& info, int& ii, const RichStyles& style); + + void GetAllLanguages(Index& all) const; + + friend class RichTable; + friend class RichText; + friend class RichCell; + + int ComputeLength() const; + + void GetRichPos(int pos, RichPos& rp, int ti, int maxlevel, const RichStyles& st) const; + RichPara::Format GetFirstFormat(const RichStyles& st) const; + + PageY GetTop(RichContext rc) const; + PageY GetHeight(RichContext rc) const; + int GetWidth(const RichStyles& st) const; + void Paint(PageDraw& w, RichContext rc, const PaintInfo& pi) const; + RichCaret GetCaret(int pos, RichContext rc) const; + int GetPos(int x, PageY y, RichContext rc) const; + int GetVertMove(int pos, int gx, RichContext rc, int dir) const; + RichHotPos GetHotPos(int x, PageY y, int tolerance, RichContext rc) const; + + void GatherValPos(Vector& f, RichContext rc, int pos, int type) const; + + bool EvaluateFields(const RichStyles& s, VectorMap& vars); + + void ApplyZoom(Zoom z, const RichStyles& ostyle, const RichStyles& zstyle); + +public: + enum ValPosType { LABELS, INDEXENTRIES }; + + int GetPartCount() const { return part.GetCount(); } + bool IsPara(int i) const { return part[i].Is(); } + bool IsTable(int i) const; + int GetPartLength(int pi) const; + int FindPart(int& pos) const; + int GetPartPos(int pi) const; + + RichPara Get(int i, const RichStyles& s) const; + RichPara Get(int i, const Uuid& styleid, const RichStyles& s) const; + RichPara Get(int i, const RichStyle& style) const; + Uuid GetParaStyle(int i) const { return part[i].Get().styleid; } + void SetParaStyle(int i, const Uuid& si); + + const RichTable& GetTable(int i) const; + + int GetLength() const; + int GetTableCount() const; + bool IsEmpty() const; + + RichCellPos GetCellPos(int table, int row, int column) const; + RichCellPos GetCellPos(int table, Point p) const; + + void Clear(); + + void ClearSpelling(); + + void SetPick(int parti, pick_ RichTable& table); + void CatPick(pick_ RichTable& table) { SetPick(GetPartCount(), table); } + void Set(int parai, const RichPara& p, const RichStyles& s); + void Insert(int parai, const RichPara& p, const RichStyles& s); + void Cat(const RichPara& p, const RichStyles& s) { Set(GetPartCount(), p, s); } + + void RemovePart(int parti); + + void Normalize(); + + Vector GetAllLanguages() const; + WString GetPlainText() const; + + struct UpdateIterator { + enum { CONTINUE = 0, STOP = 1, UPDATE = 2 }; + virtual int operator()(int pos, RichPara& para) = 0; + virtual ~UpdateIterator() {} + }; + bool Iterate(UpdateIterator& r, int gpos, const RichStyles& s); + + struct Iterator { + virtual bool operator()(int pos, const RichPara& para) = 0; + virtual ~Iterator() {} + }; + bool Iterate(Iterator& r, int gpos, const RichStyles& s) const; + + RichTxt(const RichTxt& src, int); + RichTxt(); + +#ifdef _DEBUG + void Dump(); +#endif +}; diff --git a/uppdev/RichTextP/TxtData.cpp b/uppdev/RichTextP/TxtData.cpp new file mode 100644 index 000000000..7436dfe4e --- /dev/null +++ b/uppdev/RichTextP/TxtData.cpp @@ -0,0 +1,472 @@ +#include "RichText.h" + +NAMESPACE_UPP + +RichTxt::Para::Para(const Para& src, int) +: object(src.object, 1) +{ + length = src.length; + styleid = src.styleid; + content = src.content; + haspos = src.haspos; + if(src.number) + number = new RichPara::NumberFormat(*src.number); + cx = -1; + checked = false; +} + +RichTxt::Part::Part(const Part& src, int) { + if(src.Is()) + Create() <<= src.Get(); + else + Create() <<= src.Get(); +} + +RichTxt::Part::Part() {} + +void RichTxt::Invalidate() +{ + length = -1; + tabcount = -1; + py.Clear(); +} + +int RichTxt::GetPartLength(int pi) const +{ + return part[pi].Is() ? part[pi].Get().GetLength() : part[pi].Get().length; +} + +bool RichTxt::IsTable(int i) const { return part[i].Is(); } +const RichTable& RichTxt::GetTable(int i) const { return part[i].Get(); } + + +int RichTxt::FindPart(int& pos) const +{ + int pi = 0; + while(pi < part.GetCount()) { + int l = GetPartLength(pi) + 1; + if(pos < l) + break; + pos -= l; + pi++; + } + if(pi >= part.GetCount()) + pos = 0; + return pi; +} + +int RichTxt::GetPartPos(int pi) const +{ + int pos = 0; + for(int i = 0; i < pi; i++) + pos += GetPartLength(i) + 1; + return pos; +} + +void RichTxt::SetRefresh(int parti) +{ + switch(r_type) { + case NONE: + r_parti = parti; + r_type = PARA; + if(IsPara(parti)) { + Para& pp = part[parti].Get(); + if(pp.cx >= 0) { + r_paraocx = pp.cx; + r_paraocy = Sum(pp.linecy, 0) + pp.before + pp.after; + r_keep = pp.keep; + r_keepnext = pp.keepnext; + r_newpage = pp.newpage; + break; + } + else + r_type = FROM; + } + break; + case PARA: + if(parti == r_parti) break; + case FROM: + r_parti = min(parti, r_parti); + r_type = FROM; + break; + } +} + +void RichTxt::SetRefreshFrom(int parti) +{ + r_type = FROM; + if(r_type == NONE) + r_parti = parti; + else + r_parti = min(parti, r_parti); +} + +void RichTxt::Put(int i, const RichPara& p, const RichStyle& s) +{ + if(i >= part.GetCount() || !IsPara(i)) + part.At(i).Create(); + Para& pp = part[i].Get(); + int numbering = p.format.GetNumberLevel(); + if(pp.numbering != numbering) + SetRefreshFrom(i); + else + SetRefresh(i); + pp.number.Clear(); + pp.content = p.Pack(s.format, pp.object); + pp.cx = -1; + pp.checked = false; + pp.styleid = p.format.styleid; + pp.length = p.GetLength(); + pp.numbering = numbering; + pp.spellerrors.Clear(); + pp.haspos = p.HasPos(); + if(numbering >= 0 || p.format.reset_number) { + pp.number = new RichPara::NumberFormat; + *pp.number = p.format; + } +} + +void RichTxt::Put(int i, const RichPara& p, const RichStyles& s) +{ + Put(i, p, GetStyle(s, p.format.styleid)); +} + +void RichTxt::Set(int i, const RichPara& p, const RichStyles& s) +{ + Put(i, p, s); + Invalidate(); +} + +void RichTxt::Insert(int i, const RichPara& p, const RichStyles& s) +{ + part.Insert(i); + Set(i, p, s); +} + +void RichTxt::RemovePart(int parti) +{ + part.Remove(parti); + Invalidate(); +} + +void RichTxt::SetPick(int i, pick_ RichTable& p) +{ + const_cast(p).Normalize(); + part.At(i).Create() = p; + Invalidate(); + SetRefresh(i); +} + +RichPara RichTxt::Get(int parai, const RichStyle& style) const +{ + ASSERT(part[parai].Is()); + const Para& pp = part[parai].Get(); + RichPara p; + p.Unpack(pp.content, pp.object, style.format); + return p; +} + +RichPara RichTxt::Get(int parai, const Uuid& styleid, const RichStyles& s) const +{ + RichPara p = Get(parai, GetStyle(s, styleid)); + p.format.styleid = styleid; + return p; +} + +RichPara RichTxt::Get(int parti, const RichStyles& s) const +{ + return Get(parti, part[parti].Get().styleid, s); +} + +bool RichTxt::IsEmpty() const +{ + return part.IsEmpty() || part.GetCount() == 1 && ComputeLength() == 0; +} + +int RichTxt::ComputeLength() const +{ + int length = part.GetCount() - 1; + for(int i = 0; i < part.GetCount(); i++) + length += GetPartLength(i); + return length; +} + +int RichTxt::GetLength() const +{ + if(length < 0) + length = ComputeLength(); + return length; +} + +int RichTxt::GetTableCount() const +{ + if(tabcount < 0) { + tabcount = 0; + for(int i = 0; i < part.GetCount(); i++) + if(IsTable(i)) + tabcount += GetTable(i).GetTableCount() + 1; + } + return tabcount; +} + +bool RichTxt::EvaluateFields(const RichStyles& s, VectorMap& vars) +{ + bool b = false; + for(int i = 0; i < part.GetCount(); i++) + if(IsTable(i)) { + RichTable& tab = part[i].Get(); + for(int i = 0; i < tab.GetRows(); i++) + for(int j = 0; j < tab.GetColumns(); j++) + if(tab(i, j) && tab[i][j].text.EvaluateFields(s, vars)) { + tab.InvalidateRefresh(i, j); + b = true; + } + } + else { + RichPara p = Get(i, s); + if(p.EvaluateFields(vars)) { + b = true; + Set(i, p, s); + } + } + return b; +} + +void RichTxt::ClearSpelling() +{ + for(int i = 0; i < part.GetCount(); i++) { + if(IsTable(i)) { + RichTable& tab = part[i].Get(); + for(int i = 0; i < tab.GetRows(); i++) + for(int j = 0; j < tab.GetColumns(); j++) + if(tab(i, j)) { + tab[i][j].text.ClearSpelling(); + tab.InvalidateRefresh(i, j); + } + } + else { + Para& p = part[i].Get(); + p.spellerrors.Clear(); + p.checked = false; + } + } +} + +void RichTxt::SetParaStyle(int i, const Uuid& id) +{ + ASSERT(IsPara(i)); + Para& p = part[i].Get(); + p.styleid = id; + p.cx = -1; + SetRefreshFrom(i); +} + +void RichTxt::GetRichPos(int pos, RichPos& rp, int ti, int maxlevel, const RichStyles& st) const +{ + int p = pos; + int pti = ti; + for(int i = 0; i < part.GetCount(); i++) { + int l = GetPartLength(i) + 1; + if(pos < l) { + if(IsTable(i)) { + const RichTable& tab = GetTable(i); + rp.level++; + rp.tabtextparti = i; + rp.tabtextpartcount = part.GetCount(); + rp.tabtextlen = GetLength(); + rp.tabposintabtext = p - pos; + rp.posintab = pos; + rp.cell = tab.FindCell(pos); + rp.posincell = pos; + rp.tabsize = tab.GetSize(); + rp.tablen = tab.GetLength(); + const RichTxt& ct = tab[rp.cell].text; + rp.celllen = ct.GetLength(); + rp.parenttab = pti; + rp.table = ti + 1; + if(rp.level < maxlevel) + ct.GetRichPos(pos, rp, ti + 1 + tab.GetTableCount(rp.cell), maxlevel, st); + return; + } + else { + rp.posinpara = pos; + rp.parai = i; + rp.partcount = part.GetCount(); + rp.paralen = l - 1; + Get(i, st).GetRichPos(rp, pos); + return; + } + } + pos -= l; + if(IsTable(i)) + ti += 1 + GetTable(i).GetTableCount(); + } + rp.parai = part.GetCount(); + rp.posinpara = 0; + rp.partcount = part.GetCount(); + rp.paralen = 0; +} + +RichPara::Format RichTxt::GetFirstFormat(const RichStyles& style) const +{ + if(IsTable(0)) { + const RichTable& tab = GetTable(0); + return tab[0][0].text.GetFirstFormat(style); + } + RichPos rp; + Get(0, style).GetRichPos(rp, 0); + return rp.format; +} + +RichCellPos RichTxt::GetCellPos(int table, int row, int column) const +{ + int pos = 0; + for(int i = 0;; i++) { + if(IsTable(i)) { + table--; + const RichTable& tab = part[i].Get(); + if(table <= tab.GetTableCount()) { + if(table == 0) { + RichCellPos p; + p.tabsize = tab.GetSize(); + p.tablen = tab.GetLength(); + p.cellpos = tab.GetCellPos(row, column); + p.pos = p.tabpos = pos; + p.pos += p.cellpos; + p.textlen = GetLength(); + p.level = 1; + return p; + } + for(int i = 0; i < tab.GetRows(); i++) + for(int j = 0; j < tab.GetColumns(); j++) + if(tab(i, j)) { + const RichTxt& txt = tab[i][j].text; + if(table <= txt.GetTableCount()) { + RichCellPos p = txt.GetCellPos(table, row, column); + p.pos += pos + tab.GetCellPos(i, j); + p.level++; + return p; + } + table -= txt.GetTableCount(); + } + NEVER(); + } + else + table -= tab.GetTableCount(); + } + pos += GetPartLength(i) + 1; + } + NEVER(); + return RichCellPos(); +} + +RichCellPos RichTxt::GetCellPos(int table, Point p) const +{ + return GetCellPos(table, p.y, p.x); +} + +bool RichTxt::Iterate(UpdateIterator& r, int gpos, const RichStyles& s) +{ + for(int pi = 0; pi < part.GetCount(); pi++) + if(IsTable(pi)) { + RichTable& tab = part[pi].Get(); + for(int i = 0; i < tab.GetRows(); i++) + for(int j = 0; j < tab.GetColumns(); j++) + if(tab(i, j)) { + if(tab[i][j].text.Iterate(r, gpos, s)) + return true; + gpos += tab[i][j].text.GetLength() + 1; + } + } + else { + RichPara p = Get(pi, s); + int q = r(gpos, p); + if(q & UpdateIterator::UPDATE) + Set(pi, p, s); + if(q & UpdateIterator::STOP) + return true; + gpos += GetPartLength(pi) + 1; + } + return false; +} + +struct sIter__ : RichTxt::UpdateIterator { + RichTxt::Iterator *iter; + virtual int operator()(int pos, RichPara& para) { + return iter->operator()(pos, para) ? STOP : CONTINUE; + } +}; + +bool RichTxt::Iterate(Iterator& r, int gpos, const RichStyles& s) const +{ + sIter__ it; + it.iter = &r; + return const_cast(this)->Iterate(it, gpos, s); +} + +void RichTxt::Init() +{ + r_type = ALL; + r_parti = 0; + tabcount = length = 0; +} + +void RichTxt::Clear() +{ + part.Clear(); + Init(); +} + +RichTxt::RichTxt() +{ + Init(); +} + +RichTxt::RichTxt(const RichTxt& src, int) +{ + Init(); + part <<= src.part; + length = src.length; + tabcount = src.tabcount; + py.Clear(); +} + +#ifdef _DEBUG + +#define DMP(x) s << #x << "=" << x << ", " + +String RichPos::ToString() const +{ + String s; + DMP(tabtextparti); + DMP(tabtextpartcount); + DMP(tabposintabtext); + DMP(tabtextlen); + DMP(table); + DMP(tabsize); + DMP(cell); + DMP(tablen); + DMP(posintab); + DMP(celllen); + DMP(posincell); + DMP(parai); + DMP(partcount); + DMP(posinpara); + DMP(paralen); + DMP(level); + DMP(parenttab); + s << "char: " << (char)chr; + return s; +} + +String RichCellPos::ToString() const +{ + String s; + s << "pos: " << pos << ", textlen: " << textlen << ", size: " << tabsize << ", tabpos: " << tabpos + << ", tablen: " << tablen << ", cellpos: " << cellpos << ", celllen: " << celllen << ", level: " << level; + return s; +} + +#endif + +END_UPP_NAMESPACE diff --git a/uppdev/RichTextP/TxtOp.cpp b/uppdev/RichTextP/TxtOp.cpp new file mode 100644 index 000000000..39e22b47e --- /dev/null +++ b/uppdev/RichTextP/TxtOp.cpp @@ -0,0 +1,358 @@ +#include "RichText.h" + +NAMESPACE_UPP + +void RichTxt::GetAllLanguages(Index& all) const +{ + for(int i = 0; i < part.GetCount(); i++) { + if(IsTable(i)) { + const RichTable& tab = part[i].Get(); + for(int i = 0; i < tab.GetRows(); i++) + for(int j = 0; j < tab.GetColumns(); j++) + if(tab(i, j)) + tab[i][j].text.GetAllLanguages(all); + } + else { + RichPara p = Get(i, RichStyle::GetDefault()); + all.FindAdd(p.format.language); + for(int i = 0; i < p.GetCount(); i++) + all.FindAdd(p[i].format.language); + } + } +} + +Vector RichTxt::GetAllLanguages() const +{ + Index all; + GetAllLanguages(all); + return all.PickKeys(); +} + +bool RichTxt::Update(ParaOp& op) +{ + bool val = false; + for(int i = 0; i < part.GetCount(); i++) + if(IsTable(i)) { + RichTable& tab = part[i].Get(); + for(int i = 0; i < tab.GetRows(); i++) + for(int j = 0; j < tab.GetColumns(); j++) + if(tab(i, j)) + if(tab[i][j].text.Update(op)) { + tab.InvalidateRefresh(i, j); + val = true; + } + } + else + if(op(part[i].Get())) + val = true; + RefreshAll(); + return val; +} + +RichTxt& RichTxt::GetText0(int& pos, bool update) +{ + if(update) + Invalidate(); + int p = pos; + int pi = FindPart(p); + if(IsTable(pi)) { + RichTable& tab = part[pi].Get(); + Point cl = tab.FindCell(p); + if(update) { + tab.InvalidateRefresh(cl); + SetRefresh(pi); + } + pos = p; + return tab[cl].text.GetText0(pos, update); + } + return *this; +} + +RichTxt& RichTxt::GetUpdateText(int& pos) +{ + return GetText0(pos, true); +} + +const RichTxt& RichTxt::GetConstText(int& pos) const +{ + return const_cast(this)->GetText0(pos, false); +} + +RichTable& RichTxt::GetTable0(int table, bool update) +{ + if(update) + Invalidate(); + for(int i = 0;; i++) + if(IsTable(i)) { + table--; + RichTable& tab = part[i].Get(); + if(table <= tab.GetTableCount()) { + if(update) + SetRefresh(i); + if(table == 0) + return tab; + for(int i = 0; i < tab.GetRows(); i++) + for(int j = 0; j < tab.GetColumns(); j++) + if(tab(i, j)) { + RichTxt& txt = tab[i][j].text; + if(table <= txt.GetTableCount()) { + if(update) + tab.InvalidateRefresh(i, j); + return txt.GetTable0(table, update); + } + table -= txt.GetTableCount(); + } + NEVER(); + } + else + table -= tab.GetTableCount(); + } +} + +RichTable& RichTxt::GetUpdateTable(int table) +{ + return GetTable0(table, true); +} + +const RichTable& RichTxt::GetConstTable(int table) const { + return const_cast(this)->GetTable0(table, false); +} + +void RichTxt::CombineFormat(FormatInfo& fi, int pi, int pi2, bool& first, const RichStyles& style) const +{ + while(pi < pi2) { + if(IsTable(pi)) { + const RichTable& tab = part[pi].Get(); + for(int i = 0; i < tab.GetRows(); i++) + for(int j = 0; j < tab.GetColumns(); j++) + if(tab(i, j)) { + const RichTxt& txt = tab[i][j].text; + txt.CombineFormat(fi, 0, txt.GetPartCount(), first, style); + } + } + else { + RichPara pa = Get(pi, style); + if(first) { + fi.Set(pa.format); + if(pa.GetCount()) + fi.Set(pa[0].format); + first = false; + } + else + fi.Combine(pa.format); + for(int i = first; i < pa.GetCount(); i++) + fi.Combine(pa[i].format); + } + pi++; + } +} + +void RichTxt::ApplyStyle(const RichText::FormatInfo& fi, RichPara& pa, const RichStyles& style) +{ + if(fi.paravalid & STYLE) { + int q = style.Find(fi.styleid); + if(q >= 0) { + pa.ApplyStyle(style[q].format); + pa.format.styleid = fi.styleid; + } + } +} + +void RichTxt::Apply(const RichText::FormatInfo& fi, RichPara& pa, const RichStyles& style) +{ + ApplyStyle(fi, pa, style); + for(int i = 0; i < pa.GetCount(); i++) + fi.ApplyTo(pa[i].format); + fi.ApplyTo(pa.format); +} + +void RichTxt::ApplyFormat(const FormatInfo& fi, int pi, int pi2, const RichStyles& style) +{ + while(pi < pi2) { + if(IsTable(pi)) { + RichTable& tab = part[pi].Get(); + for(int i = 0; i < tab.GetRows(); i++) + for(int j = 0; j < tab.GetColumns(); j++) + if(tab(i, j)) { + RichTxt& txt = tab[i][j].text; + tab.InvalidateRefresh(i, j); + txt.ApplyFormat(fi, 0, txt.GetPartCount(), style); + } + } + else { + RichPara pa; + if(fi.paravalid & RichText::STYLE) + pa = RichTxt::Get(pi, fi.styleid, style); + else + pa = Get(pi, style); + Apply(fi, pa, style); + Put(pi, pa, style); + } + pi++; + } +} + +void RichTxt::SaveFormat(Formating& r, int p1, int p2, const RichStyles& style) const +{ + Array dummy; + for(int i = p1; i <= p2; i++) + if(IsTable(i)) { + const RichTable& tab = part[i].Get(); + for(int i = 0; i < tab.GetRows(); i++) + for(int j = 0; j < tab.GetColumns(); j++) + if(tab(i, j)) { + const RichTxt& txt = tab[i][j].text; + txt.SaveFormat(r, 0, txt.GetPartCount() - 1, style); + } + } + else { + RichPara pa = Get(i, style); + for(int i = 0; i < pa.GetCount(); i++) { + RichPara::Part& p = pa[i]; + int q = p.GetLength(); + p.field = Id(); + p.object = RichObject(); + WString h; + while(q) { + int c = min(q, 50000); + h.Cat(c + 32); + q -= c; + } + p.text = h; + } + r.styleid.Add(pa.format.styleid); + r.format.Add(pa.Pack(GetStyle(style, pa.format.styleid).format, dummy)); + } +} + +void RichTxt::RestoreFormat(int pi, const Formating& info, int& ii, const RichStyles& style) +{ + Array dummy; + while(ii < info.format.GetCount() && pi < GetPartCount()) { + if(IsTable(pi)) { + RichTable& tab = part[pi].Get(); + for(int i = 0; i < tab.GetRows(); i++) + for(int j = 0; j < tab.GetColumns(); j++) { + if(tab(i, j)) { + if(ii >= info.format.GetCount()) + return; + tab.InvalidateRefresh(i, j); + tab[i][j].text.RestoreFormat(0, info, ii, style); + } + } + pi++; + } + else { + RichPara pa = Get(pi, style); + RichPara pf; + pf.Unpack(info.format[ii], dummy, GetStyle(style, info.styleid[ii]).format); + RichPara t; + t.format = pf.format; + int si = 0; + int sp = 0; + for(int j = 0; j < pf.GetCount(); j++) { + const RichPara::Part& q = pf[j]; + for(int k = 0; k < q.text.GetLength(); k++) { + int len = q.text[k] - 32; + t.part.Add().format = q.format; + while(len) { + const RichPara::Part& p = pa[si]; + if(p.IsText()) { + int l = min(len, p.GetLength() - sp); + t.part.Top().text.Cat(p.text.Mid(sp, l)); + sp += l; + len -= l; + ASSERT(sp <= p.GetLength()); + if(sp >= p.GetLength()) { + sp = 0; + si++; + } + } + else { + ASSERT(sp == 0); + (t.part.Add() = pa[si++]).format = q.format; + len--; + sp = 0; + } + } + } + } + ASSERT(si == pa.GetCount() && sp == 0); + Put(pi, t, style); + ii++; + pi++; + } + } +} + +WString RichTxt::GetPlainText() const { + WString clip; + for(int pi = 0; pi < GetPartCount(); pi++) { + if(pi) { + clip.Cat('\r'); + clip.Cat('\n'); + } + if(IsTable(pi)) { + const RichTable& tab = part[pi].Get(); + for(int i = 0; i < tab.GetRows(); i++) + for(int j = 0; j < tab.GetColumns(); j++) + if(tab(i, j)) { + if(i || j) { + clip.Cat('\r'); + clip.Cat('\n'); + } + clip << tab[i][j].text.GetPlainText(); + } + } + else + clip.Cat(Get(pi, RichStyle::GetDefault()).GetText()); + } + return clip; +} + +RichTxt& RichTxt::GetTableUpdateText(int table, const RichStyles& style, int& pi) +{ + Invalidate(); + for(int i = 0;; i++) + if(IsTable(i)) { + table--; + RichTable& tab = part[i].Get(); + if(table <= tab.GetTableCount()) { + SetRefresh(i); + if(table == 0) { + pi = i; + return *this; + } + for(int i = 0; i < tab.GetRows(); i++) + for(int j = 0; j < tab.GetColumns(); j++) + if(tab(i, j)) { + RichTxt& txt = tab[i][j].text; + if(table <= txt.GetTableCount()) { + tab.InvalidateRefresh(i, j); + return txt.GetTableUpdateText(table, style, pi); + } + table -= txt.GetTableCount(); + } + NEVER(); + } + else + table -= tab.GetTableCount(); + } + NEVER(); +} + +void RichTxt::Normalize() +{ + RichPara pa; + if(GetPartCount() && IsTable(0)) { + part.Insert(0); + Put(0, pa, RichStyle::GetDefault()); + Invalidate(); + } + if(GetPartCount() == 0 || IsTable(GetPartCount() - 1)) { + Put(GetPartCount(), pa, RichStyle::GetDefault()); + Invalidate(); + } +} + +END_UPP_NAMESPACE diff --git a/uppdev/RichTextP/TxtPaint.cpp b/uppdev/RichTextP/TxtPaint.cpp new file mode 100644 index 000000000..2fd10a5b6 --- /dev/null +++ b/uppdev/RichTextP/TxtPaint.cpp @@ -0,0 +1,377 @@ +#include "RichText.h" + +NAMESPACE_UPP + +int RichTxt::GetWidth(const RichStyles& st) const +{ + int cx = 0; + for(int i = 0; i < part.GetCount(); i++) { + if(IsPara(i)) { + RichPara p = Get(i, st); + RichPara::Lines pl = p.FormatLines(INT_MAX); + int ccx = 0; + Sum(ccx, ~pl.width, ~pl.width + pl.clen); + cx = max(cx, ccx); + } + else + return 10000; + } + return cx; +} + + +void RichTxt::Sync0(const Para& pp, int parti, const RichContext& rc) const +{ + int cx = rc.page.Width(); + pp.cx = cx; + RichPara p = Get(parti, rc.styles); + RichPara::Lines pl = p.FormatLines(cx); + pp.ruler = p.format.ruler; + pp.before = p.format.before; + pp.linecy.Clear(); + pp.linecy.SetCount(pl.GetCount()); + for(int i = 0; i < pl.GetCount(); i++) + pp.linecy[i] = pl[i].Sum(); + pp.cy = Sum0(pp.linecy); + pp.after = p.format.after; + pp.newpage = p.format.newpage; + pp.keep = p.format.keep; + pp.keepnext = p.format.keepnext; + pp.orphan = p.format.orphan; +} + +inline +bool RichTxt::BreaksPage(PageY py, const Para& pp, int i, const Rect& page) const +{ + int linecy = pp.linecy[i]; + if(linecy >= page.Height()) return false; + if(linecy + py.y > page.bottom) + return true; + if(pp.orphan || pp.linecy.GetCount() < 2) return false; + if((i == 0 || i == pp.linecy.GetCount() - 2) && + py.y + linecy + pp.linecy[i + 1] > page.bottom) + return true; + return false; +} + +PageY RichTxt::GetNextPageY(int parti, const RichContext& rc) const +{ + if(part[parti].Is()) + return GetTable(parti).GetHeight(rc); + else { + Sync(parti, rc); + const Para& pp = part[parti].Get(); + int cy = pp.before + pp.ruler; + if(pp.keep || pp.keepnext) + cy += pp.cy; + else + cy += pp.linecy[0]; + PageY py = rc.py; + if(rc.page.Height() < 30000) { + int nbefore = 0; + int nline = 0; + if(pp.keepnext && parti + 1 < part.GetCount() && part[parti + 1].Is()) { + Sync(parti + 1, rc); + const Para& p = part[parti + 1].Get(); + nbefore = p.before + p.ruler; + nline = p.linecy[0]; + } + if(pp.newpage || py.y + cy + nbefore + nline > rc.page.bottom && cy < rc.page.Height()) { + py.page++; + py.y = rc.page.top; + } + py.y += pp.before; + if(py.y + pp.cy < rc.page.bottom) + py.y += pp.cy; + else + for(int lni = 0; lni < pp.linecy.GetCount(); lni++) { + if(BreaksPage(py, pp, lni, rc.page)) { + py.y = rc.page.top; + py.page++; + } + py.y += pp.linecy[lni]; + } + py.y += pp.after; + if(py.y > rc.page.bottom) { + py.y = rc.page.top; + py.page++; + } + } + else + py.y += pp.before + pp.cy + pp.after; + return py; + } +} + +PageY RichTxt::GetPartPageY(int parti, RichContext rc) const +{ + for(int i = 0; i < parti; i++) + rc.py = GetNextPageY(i, rc); + return rc.py; +} + +bool IsPainting(PageDraw& pw, Zoom z, const Rect& page, PageY top, PageY bottom) +{ + int t = top.y; + for(int pi = top.page; pi < bottom.page; pi++) { + if(pw.Page(pi).IsPainting(Rect(z * page.left, z * top.y, z * page.right, z * page.bottom))) + return true; + t = page.top; + } + return pw.Page(bottom.page).IsPainting(Rect(z * page.left, z * t, z * page.right, z * bottom.y)); +} + + +void RichTxt::Paint(PageDraw& pw, RichContext rc, const PaintInfo& _pi) const +{ + PaintInfo pi = _pi; + int parti = 0; + int pos = 0; + RichPara::Number n; + while(rc.py < pi.bottom && parti < part.GetCount()) { + if(part[parti].Is()) { + pi.tablesel--; + const RichTable& tab = GetTable(parti); + tab.Paint(pw, rc, pi); + rc.py = tab.GetHeight(rc); + pi.tablesel -= tab.GetTableCount(); + } + else { + const Para& pp = part[parti].Get(); + if(pp.number) { + n.TestReset(*pp.number); + n.Next(*pp.number); + } + PageY next = GetNextPageY(parti, rc); + if(next >= pi.top) { + int nbefore = 0; + int nline = 0; + if(pp.keepnext && parti + 1 < part.GetCount() && part[parti + 1].Is()) { + Sync(parti + 1, rc); + const Para& pp = part[parti + 1].Get(); + nbefore = pp.before; + nline = pp.linecy[0]; + } + RichPara p = Get(parti, rc.styles); + if(pi.spellingchecker) { + if(!pp.checked) { + pp.spellerrors = (*pi.spellingchecker)(p); + pp.checked = true; + } + } + else { + pp.checked = false; + pp.spellerrors.Clear(); + } + if(IsPainting(pw, pi.zoom, rc.page, rc.py, next)) + p.Paint(pw, rc.page, rc.py, pi, n, pp.spellerrors, nbefore, nline); + } + rc.py = next; + } + int l = GetPartLength(parti) + 1; + pi.highlightpara -= l; + pi.sell -= l; + pi.selh -= l; + pos += l; + ++parti; + } +} + +RichCaret RichTxt::GetCaret(int pos, RichContext rc) const +{ + int parti = 0; + if(pos > GetLength()) + pos = GetLength(); + while(parti < part.GetCount()) { + int l = GetPartLength(parti) + 1; + if(pos < l) + if(IsTable(parti)) + return GetTable(parti).GetCaret(pos, rc); + else { + const Para& p = part[parti].Get(); + int nbefore = 0; + int nline = 0; + if(p.keepnext && parti + 1 < part.GetCount() && part[parti + 1].Is()) { + Sync(parti + 1, rc); + const Para& pp = part[parti + 1].Get(); + nbefore = pp.before; + nline = pp.linecy[0]; + } + RichCaret tp = Get(parti, rc.styles).GetCaret(pos, rc.page, rc.py, nbefore, nline); + tp.textpage = rc.page; + return tp; + } + pos -= l; + rc.py = GetNextPageY(parti++, rc); + } + return RichCaret(); +} + +int RichTxt::GetPos(int x, PageY y, RichContext rc) const +{ + int parti = 0; + int pos = 0; + + if(part.GetCount()) { + PageY nnext = GetNextPageY(parti, rc); + while(parti < part.GetCount()) { + PageY next = nnext; + if(parti + 1 < part.GetCount()) { + RichContext nrc = rc; + nrc.py = next; + nnext = GetNextPageY(parti + 1, nrc); + } + if(y < next || y.page < next.page) { + if(IsTable(parti)) + return GetTable(parti).GetPos(x, y, rc) + pos; + else { + int nbefore = 0; + int nline = 0; + if(part[parti].Get().keepnext && parti + 1 < part.GetCount() && IsPara(parti + 1)) { + Sync(parti + 1, rc); + const Para& pp = part[parti + 1].Get(); + nbefore = pp.before; + nline = pp.linecy[0]; + } + return Get(parti, rc.styles).GetPos(x, y, rc.page, rc.py, nbefore, nline) + pos; + } + } + pos += GetPartLength(parti) + 1; + parti++; + rc.py = next; + } + } + + return pos - 1; +} + +RichHotPos RichTxt::GetHotPos(int x, PageY y, int tolerance, RichContext rc) const +{ + int parti = 0; + int pos = 0; + int ti = 0; + if(part.GetCount()) { + PageY nnext = GetNextPageY(parti, rc); + while(parti < part.GetCount()) { + PageY next = nnext; + if(parti + 1 < part.GetCount()) { + RichContext nrc = rc; + nrc.py = next; + nnext = GetNextPageY(parti + 1, nrc); + } + if(y < next || y.page < next.page) { + if(IsTable(parti)) { + RichHotPos pos = GetTable(parti).GetHotPos(x, y, tolerance, rc); + pos.table += ti + 1; + return pos; + } + else + break; + } + if(IsTable(parti)) + ti += 1 + GetTable(parti).GetTableCount(); + pos += GetPartLength(parti) + 1; + parti++; + rc.py = next; + } + } + + return RichHotPos(); +} + +int RichTxt::GetVertMove(int pos, int gx, RichContext rc, int dir) const +{ + ASSERT(dir == -1 || dir == 1); + if(GetPartCount() == 0) + return -1; + int pi; + int p = pos; + if(pos >= 0) { + pi = FindPart(p); + pos -= p; + } + else { + pi = dir > 0 ? 0 : GetPartCount() - 1; + p = -1; + pos = 0; + } + while(pi < GetPartCount()) { + int q = IsTable(pi) ? GetTable(pi).GetVertMove(p, gx, rc, dir) + : Get(pi, rc.styles).GetVertMove(p, gx, rc.page, dir); + if(q >= 0) + return q + pos; + if(dir > 0) + pos += GetPartLength(pi) + 1; + p = -1; + pi += dir; + if(pi < 0) + break; + if(dir < 0) + pos -= GetPartLength(pi) + 1; + } + return -1; +} + +void RichTxt::GatherValPos(Vector& f, RichContext rc, int pos, int type) const +{ + int parti = 0; + while(parti < part.GetCount()) { + if(part[parti].Is()) + GetTable(parti).GatherValPos(f, rc, pos, type); + else { + int nbefore = 0; + int nline = 0; + const Para& p = part[parti].Get(); + if(p.keepnext && parti + 1 < part.GetCount() && IsPara(parti + 1)) { + Sync(parti + 1, rc); + const Para& pp = part[parti + 1].Get(); + nbefore = pp.before; + nline = pp.linecy[0]; + } + if(p.haspos) + if(type == LABELS) + Get(parti, rc.styles).GatherLabels(f, rc.page, rc.py, pos, nbefore, nline); + else + Get(parti, rc.styles).GatherIndexes(f, rc.page, rc.py, pos, nbefore, nline); + } + pos += GetPartLength(parti) + 1; + rc.py = GetNextPageY(parti++, rc); + } +} + +PageY RichTxt::GetHeight(RichContext rc) const +{ + for(int i = 0; i < GetPartCount(); i++) + rc.py = GetNextPageY(i, rc); + return rc.py; +} + +PageY RichTxt::GetTop(RichContext rc) const +{ + if(part.GetCount() == 0) + return rc.py; + if(part[0].Is()) + return GetTable(0).GetTop(rc); + else { + Sync(0, rc); + const Para& pp = part[0].Get(); + rc.py.y += pp.before; + if(BreaksPage(rc.py, pp, 0, rc.page)) + rc.Page(); + return rc.py; + } +} + +void RichTxt::ApplyZoom(Zoom z, const RichStyles& ostyle, const RichStyles& zstyle) +{ + for(int i = 0; i < GetPartCount(); i++) + if(IsTable(i)) + part[i].Get().ApplyZoom(z, ostyle, zstyle); + else { + RichPara p = Get(i, ostyle); + p.ApplyZoom(z); + Set(i, p, zstyle); + } +} + +END_UPP_NAMESPACE diff --git a/uppdev/RichTextP/Util.cpp b/uppdev/RichTextP/Util.cpp new file mode 100644 index 000000000..e607568b5 --- /dev/null +++ b/uppdev/RichTextP/Util.cpp @@ -0,0 +1,123 @@ +#include "RichText.h" + +NAMESPACE_UPP + +Draw& SimplePageDraw::Info() +{ + return w; +} + +Draw& SimplePageDraw::Page(int) +{ + return w; +} + +void RichText::ApplyZoom(Zoom z) +{ + RichStyles ostyle(style, 1); + for(int i = 0; i < style.GetCount(); i++) + style[i].format *= z; + RichTxt::ApplyZoom(z, ostyle, style); + RefreshAll(); +} + +Zoom GetRichTextStdScreenZoom() +{ + return Zoom(Ctrl::HorzLayoutZoom(96), 600); +} + +struct QTFDisplayCls : Display { + virtual void Paint(Draw& w, const Rect& r, const Value& q, + Color ink, Color paper, dword style) const; + virtual Size GetStdSize(const Value& q) const; + virtual Size RatioSize(const Value& q, int cx, int cy) const; +}; + +Size QTFDisplayCls::GetStdSize(const Value& q) const +{ + Size sz; + RichText txt = ParseQTF((String)q); + txt.ApplyZoom(GetRichTextStdScreenZoom()); + sz.cx = txt.GetWidth(); + sz.cy = txt.GetHeight(Zoom(1, 1), sz.cx); + return sz; +} + +Size QTFDisplayCls::RatioSize(const Value& q, int cx, int cy) const +{ + if(cy == 0 && cx > 0) { + RichText txt = ParseQTF((String)q); + return Size(cx, txt.GetHeight(Zoom(1, 1), cx)); + } + return GetStdSize(q); +} + +void QTFDisplayCls::Paint(Draw& draw, const Rect& r, const Value& v, Color ink, Color paper, dword style) const +{ + String s; + s << "[@(" << ink.GetR() << "." << ink.GetG() << "." << ink.GetB() << ") " << v; + RichText rtext = ParseQTF(s); + rtext.ApplyZoom(GetRichTextStdScreenZoom()); + draw.DrawRect(r, paper); + draw.Clipoff(r); + rtext.Paint(Zoom(1, 1), draw, 0, 0, r.Width()); + draw.End(); +} + +const Display& QTFDisplay() +{ + return Single(); +} + +struct QTFDisplayCCls : QTFDisplayCls { + virtual void Paint(Draw& w, const Rect& r, const Value& q, + Color ink, Color paper, dword style) const; +}; + +void QTFDisplayCCls::Paint(Draw& draw, const Rect& r, const Value& v, Color ink, Color paper, dword style) const +{ + String s; + s << "[@(" << ink.GetR() << "." << ink.GetG() << "." << ink.GetB() << ") " << v; + RichText rtext = ParseQTF(s); + rtext.ApplyZoom(GetRichTextStdScreenZoom()); + draw.DrawRect(r, paper); + draw.Clipoff(r); + int cy = rtext.GetHeight(Zoom(1, 1), r.Width()); + rtext.Paint(Zoom(1, 1), draw, 0, max(0, (r.Height() - cy) / 2), r.Width()); + draw.End(); +} + +const Display& QTFDisplayVCenter() +{ + return Single(); +} + +RichText AsRichText(const wchar *s, const RichPara::Format& f) +{ + RichText clip; + RichPara p; + p.format = f; + p.part.Add().format = f; + RichStyle cs; + cs.format = f; + cs.format.sscript = 0; + cs.format.link.Clear(); + cs.format.indexentry.Clear(); + cs.format.language = LNG_ENGLISH; + cs.format.label.Clear(); + clip.SetStyle(f.styleid, cs); + WString& part = p.part.Top().text; + while(*s) { + if(*s == '\n') { + clip.Cat(p); + part.Clear(); + } + if(*s >= 32 || *s == '\t') + part.Cat(*s); + s++; + } + clip.Cat(p); + return clip; +} + +END_UPP_NAMESPACE diff --git a/uppdev/RichTextP/init b/uppdev/RichTextP/init new file mode 100644 index 000000000..74e325758 --- /dev/null +++ b/uppdev/RichTextP/init @@ -0,0 +1,8 @@ +#ifndef _RichTextP_icpp_init_stub +#define _RichTextP_icpp_init_stub +#include "CtrlCore/init" +#include "plugin\png/init" +#define BLITZ_INDEX__ F70234807CBDCCBF7C7FC03C2E97EA2EC +#include "RichImage.icpp" +#undef BLITZ_INDEX__ +#endif diff --git a/uppdev/RichTextP/srcdoc.tpp/QTF$en-us.tpp b/uppdev/RichTextP/srcdoc.tpp/QTF$en-us.tpp new file mode 100644 index 000000000..06acffde0 --- /dev/null +++ b/uppdev/RichTextP/srcdoc.tpp/QTF$en-us.tpp @@ -0,0 +1,705 @@ +topic "QTF"; +[2 $$0,0#00000000000000000000000000000000:Default] +[@(128.0.255)2 $$1,0#65874547464505293575048467215454:QTF Chr] +[*/+117 $$2,0#07143242482611002448121871408047:title] +[*C@3+75 $$3,3#36268203433472503231438721581057:code] +[{_}%EN-US +[s0;= [*8 QTF]&] +[s0; &] +[s0; QTF is the native format of Ultimate`+`+ rich texts (formatted +texts).&] +[s0; &] +[s0; It is byte oriented format. Bytes with values 2`-31 are ignored. +Other are interpreted as characters or formatting commands.&] +[s0; &] +[s0; Letters ([@4 a]`-[@4 zA]`-[@4 Z]), numbers ([@4 0]`-[@4 9]), space (32) +and characters&] +[s0; &] +[s0; [*@4 . , ; ! ? % ( ) / < > #]&] +[s0; &] +[s0; and bytes greater than 127 are guaranteed to be never used as +command characters (not even in future versions of QTF). Other +characters should be prefixed with escape character `` (reverse +apostrophe). Group of characters can be escaped using byte 1. +Example:&] +[s0; &] +[s3; `"`\1a`[x`]`\1`[`* bold`]`"&] +[s0; &] +[s0; Byte 0 represents the end of input sequence.&] +[s0; &] +[s0; Dimension units of QTF are dots `- one dot is defined as 1/600 +of inch.&] +[s0; &] +[s0; Colors are described as either number [@(128.0.255) 0]`-[@(128.0.255) 9], +with meaning&] +[s0; &] +[ {{1000:1000:1000:1000:1000:1000:1000:1000:1000:1000<96;>96;f4; [s0;%- [* 0]] +:: [s0;%- [* 1]] +:: [s0;%- [* 2]] +:: [s0;%- [* 3]] +:: [s0;%- [* 4]] +:: [s0;%- [* 5]] +:: [s0;%- [* 6]] +:: [s0;%- [* 7]] +:: [s0;%- [* 8]] +:: [s0;%- [* 9]] +::l/0r/0t/0b/0@0 [s0; ] +::@1 [s0; ] +::@2 [s0; ] +::@3 [s0; ] +::@4 [s0; ] +::@5 [s0; ] +::@6 [s0; ] +::@7 [s0; ] +::@8 [s0; ] +::@9 [s0; ] +::l/25r/25t/15b/15@2 [s0;%- [1 Black]] +:: [s0; [1 LtGray]] +:: [s0; [1 White]] +:: [s0;%- [1 Red]] +:: [s0;%- [1 Green]] +:: [s0;%- [1 Blue]] +:: [s0;%- [1 LtRed]] +:: [s0;%- [1 WhiteGray]] +:: [s0;%- [1 LtCyan]] +:: [s0;%- [1 Yellow]]}}&] +[s0; &] +[s0; or letters&] +[s0; &] +[ {{1109:1109:1109:1109:1109:1109:1109:1109:1128<96;>96;f4; [s0;%- [*C@(128.0.255) b]] +:: [s0;%- [*C@(128.0.255) c]] +:: [s0;%- [*C@(128.0.255) g]] +:: [s0;%- [*C@(128.0.255) k]] +:: [s0;%- [*C@(128.0.255) l]] +:: [s0;%- [*C@(128.0.255) m]] +:: [s0;%- [*C@(128.0.255) o]] +:: [s0;%- [*C@(128.0.255) r]] +:: [s0;%- [*C@(128.0.255) y]] +::l/0r/0t/0b/0@5 [s0; ] +::@(0.128.128) [s0; ] +::@4 [s0; ] +::@0 [s0; ] +::@1 [s0; ] +::@(128.0.255) [s0; ] +::@(128.128.0) [s0; ] +::@3 [s0; ] +::@9 [s0; ] +::l/25r/25t/15b/15@2 [s0;%- [1 Blue]] +:: [s0;%- [1 Cyan]] +:: [s0;%- [1 Green]] +:: [s0;%- [1 Black]] +:: [s0;%- [1 LtGray]] +:: [s0;%- [1 Magenta]] +:: [s0;%- [1 Brown]] +:: [s0;%- [1 Red]] +:: [s0;%- [1 Yellow]]}}&] +[s0; &] +[ {{1109:1109:1109:1109:1109:1109:1109:1109:1128<96;>96;f4; [s0;%- [*@(128.0.255) B]] +:: [s0;%- [*@(128.0.255) C]] +:: [s0;%- [*@(128.0.255) G]] +:: [s0;%- [*@(128.0.255) K]] +:: [s0;%- [*@(128.0.255) L]] +:: [s0;%- [*@(128.0.255) M]] +:: [s0;%- [*@(128.0.255) W]] +:: [s0;%- [*@(128.0.255) R]] +:: [s0;%- [*@(128.0.255) Y]] +::l/0r/0t/0b/0@(0.0.255) [s0; ] +::@8 [s0; ] +::@(0.255.0) [s0; ] +::@(128) [s0; ] +::@(238) [s0; ] +::@(255.0.255) [s0; ] +::@2 [s0; ] +::@6 [s0; ] +::@(255.255.180) [s0; ] +::l/25r/25t/15b/15@2 [s0;%- [1 LtBlue]] +:: [s0;%- [1 LtCyan]] +:: [s0;%- [1 LtGreen]] +:: [s0;%- [1 Gray]] +:: [s0;%- [1 WhiteGray]] +:: [s0;%- [1 LtMagenta]] +:: [s0;%- [1 White]] +:: [s0;%- [1 LtRed]] +:: [s0;%- [1 LtYellow]]}}&] +[s0; &] +[s0; or as the RGB value in form&] +[s0; &] +[s0; [@(128.0.255) (][/@(0.0.255) number][@(128.0.255) .][/@(0.0.255) number][@(128.0.255) .][/@(0.0.255) n +umber][@(128.0.255) )]&] +[s0;@(128.0.255) &] +[s0; where [/@(0.0.255) number] is 0`-255.&] +[s0; &] +[s0; Form with single [/@(0.0.255) number]&] +[s0; &] +[s0; [@(128.0.255) (][/@(0.0.255) number][@(128.0.255) )]&] +[s0;@(128.0.255) &] +[s0; specifies grays.&] +[s0; &] +[s0; Letter&] +[s0; &] +[s0; [@(128.0.255) N]&] +[s0;@(128.0.255) &] +[s0; used in place of color designates transparent color.&] +[s0; &] +[s0; &] +[s2; Basic QTF codes&] +[s0; &] +[s0; &] +[ {{2003:7997<96;>96; [s0; [C@(128.0.255) `_]] +:: [s0; Hard`-space `- space that cannot be divided at the end of line.] +:: [s0; [C@(128.0.255) `&]] +:: [s0; New paragraph.] +:: [s0; [C@(128.0.255) `-`|]] +:: [s0; Tabulator] +:: [s0;%- [%%C@(128.0.255) `@`$][%%*C@(0.0.255) hex][C@(128.0.255) ;]] +:: [s0; Unicode character as hexadecimal number.]}}&] +[s0; &] +[s0; &] +[s2; Character and paragraph formatting&] +[s0; &] +[s0; Character and paragraph formatting starts with&] +[s0;@(128.0.255) &] +[s0; [@(128.0.255) `[]&] +[s0;@(128.0.255) &] +[s0; character followed by [*/ character/paragraph formating][/ sequence] +ended with single space character and applies to the text until +matching&] +[s0; &] +[s0; [@(128.0.255) `]]&] +[s0;@(128.0.255) &] +[s0; is encountered.&] +[s0; &] +[ {{1879:8121^@(229.229.248)-1 [s0; [*/ Character/paragraph formating sequence]] +::=@2 [s0;%- ] +::^ [s0;%- [C@(128.0.255) /]] +::= [s0; Italic.] +::^ [s0;%- [C@(128.0.255) `*]] +::= [s0; Bold.] +::^ [s0;%- [C@(128.0.255) `_]] +::= [s0; Underline.] +::^ [s0;%- [C@(128.0.255) `-]] +::= [s0; Strikeout.] +::^ [s0;%- [C@(128.0.255) c]] +::= [s0; Capitals.] +::^ [s0;%- [C@(128.0.255) ``]] +::= [s0; Superscript.] +::^ [s0;%- [C@(128.0.255) ,]] +::= [s0; Subscript.] +::^ [s0;%- [C@(128.0.255) d]] +::= [s0; Dashed underline.] +::^ [s0;%- [C@(128.0.255) T]] +::= [s0; Non anti aliased font.] +::^ [s0;%- [C@(128.0.255) `^][/C@(0.0.255) text][C@(128.0.255) `^]] +::= [s0; Hyperlink.] +::^ [s0;%- [C@(128.0.255) I][/C@(0.0.255) text][C@(128.0.255) ;]] +::= [s0; Index entry.] +::^ [s0;%- [C@(128.0.255) `+][/C@(0.0.255) number]] +::= [s0; Font height in dots.] +::^ [s0;%- [C@(128.0.255) 0]] +::= [s0; Font height 50 dots (6 points).] +::^ [s0;%- [C@(128.0.255) 1]] +::= [s0; Font height 67 dots (8 points).] +::^ [s0;%- [C@(128.0.255) 2]] +::= [s0; Font height 84 dots (10 points).] +::^ [s0;%- [C@(128.0.255) 3]] +::= [s0; Font height 100 dots (12 points).] +::^ [s0;%- [C@(128.0.255) 4]] +::= [s0; Font height 134 dots (16 points).] +::^ [s0;%- [C@(128.0.255) 5]] +::= [s0; Font height 167 dots (20 points).] +::^ [s0;%- [C@(128.0.255) 6]] +::= [s0; Font height 200 dots (24 points).] +::^ [s0;%- [C@(128.0.255) 7]] +::= [s0; Font height 234 dots (28 points).] +::^ [s0;%- [C@(128.0.255) 8]] +::= [s0; Font height 300 dots (36 points).] +::^ [s0;%- [C@(128.0.255) 9]] +::= [s0; Font height 400 dots (48 points).] +::^ [s0;%- [C@(128.0.255) `@][/C@(0.0.255) color]] +::= [s0; Text color.] +::^ [s0;%- [C@(128.0.255) `$][/C@(0.0.255) color]] +::= [s0; Text background color.] +::^ [s0;%- [C@(128.0.255) A]] +::= [s0; Arial font.] +::^ [s0;%- [C@(128.0.255) R]] +::= [s0; Times New Roman font.] +::^ [s0;%- [C@(128.0.255) C]] +::= [s0; Courier font.] +::^ [s0;%- [C@(128.0.255) G]] +::= [s0; Standard GUI font.] +::^ [s0;%- [C@(128.0.255) S]] +::= [s0; Symbol font.] +::^ [s0;%- [C@(128.0.255) .][/C@(0.0.255) number]] +::= [s0; Font with specified font [/@(0.0.255) number].] +::^ [s0;%- [C@(128.0.255) !][/C@(0.0.255) text][C@(128.0.255) !]] +::= [s0; Font with face name equal to [/@(0.0.255) text]. If such font +does not exist on system, Arial is used.] +::^ [s1;%- [C `{][/C@(0.0.255) charset][C `}]&] +[s0; ] +::= [s0; Character set. It can be defined as either single character&] +[s0; &] +[ {{1840:8160<330;>1757;f4;h1; [s0; [C@(128.0.255) `_]] +:: [s0; utf`-8] +:: [s0; [C@(128.0.255) 0]] +:: [s0; windows`-1250] +:: [s0; [C@(128.0.255) 1]] +:: [s0; windows`-1251] +:: [s0; [C@(128.0.255) 2]] +:: [s0; windows`-1252] +:: [s0; [C@(128.0.255) 3]] +:: [s0; windows`-1253] +:: [s0; [C@(128.0.255) 4]] +:: [s0; windows`-1254] +:: [s0; [C@(128.0.255) 5]] +:: [s0; windows`-1255] +:: [s0; [C@(128.0.255) 6]] +:: [s0; windows`-1256] +:: [s0; [C@(128.0.255) 7]] +:: [s0; windows`-1257] +:: [s0; [C@(128.0.255) A]] +:: [s0; iso`-8859`-1] +:: [s0; [C@(128.0.255) B]] +:: [s0; iso`-8859`-2] +:: [s0; [C@(128.0.255) C]] +:: [s0; iso`-8859`-3] +:: [s0; [C@(128.0.255) D]] +:: [s0; iso`-8859`-4] +:: [s0; [C@(128.0.255) E]] +:: [s0; iso`-8859`-5] +:: [s0; [C@(128.0.255) F]] +:: [s0; iso`-8859`-6] +:: [s0; [C@(128.0.255) G]] +:: [s0; iso`-8859`-7] +:: [s0; [C@(128.0.255) H]] +:: [s0; iso`-8859`-8] +:: [s0; [C@(128.0.255) I]] +:: [s0; iso`-8859`-9] +:: [s0; [C@(128.0.255) J]] +:: [s0; iso`-8859`-10]}}&] +[s0; &] +[s0; or as the string designating character set (example: [*C@3 `"`[`{`_`}_...`]`"][* , +][*C@3 `"`[`{windows`-1250`}_...`]`"]).] +::^ [s0;%- [C@(128.0.255) %][/C@(0.0.255) lang]] +::= [s0; Language. It is defined in form [@(0.0.255) XX]`-[@(0.0.255) YY], +according to ISO 639 and ISO 3166 standards. (example: [*C@3 `"`[%EN`-US_...`]`"]). +[*C@3 `"`[%00`-00_`"] represents `"none`" language. As special +optimization, [*@(128.0.255) %`-] is equivalent to [@(128.0.255) %][@(0.0.255) 00`-00] +(no language) and&] +[s0; [@(128.0.255) %%] is equivalent to [@(128.0.255) %][@(0.0.255) EN`-US].] +::^ [s0;%- [C@(128.0.255) :][/C@(0.0.255) text][C@(128.0.255) :]] +::= [s0; Paragraph label.] +::^ [s0;%- [C@(128.0.255) <]] +::= [s0; Align paragraph left.] +::^ [s0;%- [C@(128.0.255) `=]] +::= [s0; Center paragraph.] +::^ [s0;%- [C@(128.0.255) >]] +::= [s0; Align paragraph right.] +::^ [s0;%- [C@(128.0.255) #]] +::= [s0; Justify paragraph.] +::^ [s0;%- [C@(128.0.255) l][/C@(0.0.255) number]] +::= [s0; Left margin in dots.] +::^ [s0;%- [C@(128.0.255) r][/C@(0.0.255) number]] +::= [s0; Right margin in dots.] +::^ [s0;%- [C@(128.0.255) i][/C@(0.0.255) number]] +::= [s0; Indent in dots.] +::^ [s0;%- [C@(128.0.255) pn]] +::= [s0; Line spacing 1.0.] +::^ [s0;%- [C@(128.0.255) ph]] +::= [s0; Line spacing 1.5.] +::^ [s0;%- [C@(128.0.255) pd]] +::= [s0; Line spacing 2.0.] +::^ [s0;%- [C@(128.0.255) H][/C@(0.0.255) number]] +::= [s0; Horizontal ruler height (if zero, there is no ruler).] +::^ [s0;%- [C@(128.0.255) h][/C@(0.0.255) color]] +::= [s0; Color of horizontal ruler (default is black).] +::^ [s0;%- [C@(128.0.255) b][/C@(0.0.255) number]] +::= [s0; Space before paragraph in dots.] +::^ [s0;%- [C@(128.0.255) a][/C@(0.0.255) number]] +::= [s0; Space after paragraph in dots.] +::^ [s0;%- [C@(128.0.255) P]] +::= [s0; Page break before paragraph.] +::^ [s0;%- [C@(128.0.255) k]] +::= [s0; Keep paragraph on single page.] +::^ [s0;%- [C@(128.0.255) K]] +::= [s0; Keep paragraph on same page as next one.] +::^ [s0;%- [C@(128.0.255) Q]] +::= [s0; Orphan control.] +::^ [s0;%- [C@(128.0.255) n][/C@(0.0.255) text][C@(128.0.255) ;]] +::= [s0; Text to insert before paragraph number.] +::^ [s0;%- [C@(128.0.255) m][/C@(0.0.255) text][C@(128.0.255) ;]] +::= [s0; Text to insert after paragraph number.] +::^ [s0;%- [C@(128.0.255) N]] +::= [s0; Numbering. It is followed by up to 8 characters defining numbering +style of each level&] +[s0; &] +[ {{864:9136<342;>861;f4; [s0; [C@(128.0.255) `-]] +:: [s0; Level is not used.] +:: [s0; [C@(128.0.255) 1]] +:: [s0; Numbers, starting with 1.] +:: [s0; [C@(128.0.255) 0]] +:: [s0; Numbers, starting with 0.] +:: [s0; [C@(128.0.255) a]] +:: [s0; Lowercase letters, starting with a.] +:: [s0; [C@(128.0.255) A]] +:: [s0; Uppercase letters, starting with A.] +:: [s0; [C@(128.0.255) i]] +:: [s0; Lowercase roman numbers, starting with i.] +:: [s0; [C@(128.0.255) I]] +:: [s0; Uppercase roman numbers, starting with I]}}&] +[s0; ] +::^ [s0;%- [C@(128.0.255) o]] +::= [s0; Bullet style.] +::^ [s0;%- [C@(128.0.255) O`_]] +::= [s0; No bullet.] +::^ [s0;%- [C@(128.0.255) O0]] +::= [s0; Bullet style.] +::^ [s0;%- [C@(128.0.255) O1]] +::= [s0; Bullet style.] +::^ [s0;%- [C@(128.0.255) O2]] +::= [s0; Bullet style.] +::^ [s0;%- [C@(128.0.255) O3]] +::= [s0; Bullet style.] +::^ [s0;%- [C@(128.0.255) O9]] +::= [s0; Text bullet style.] +::^ [s0;%- [C@(128.0.255) t][/C@(0.0.255) number]] +::= [s0; Default tab size.] +::^ [s0;%- [C@(128.0.255) `~]] +::= [s0; Tabulator setting. Can be followed by character designating +type of tabulation&] +[s0; &] +[ {{864:9136<342;>861;f4; [s0; [C@(128.0.255) >]] +:: [s0; Normal tabulation.] +:: [s0; [C@(128.0.255) <]] +:: [s0; Left tabulation.] +:: [s0; [C@(128.0.255) `=]] +:: [s0; Centered tabulation.]}}&] +[s0; &] +[s0; (default is normal) and by filler character &] +[s0; &] +[ {{3312:3436:3252<342;>861;f4; [s0; [C@(128.0.255) .]] +:: [s0; [C@(128.0.255) `-]] +:: [s0; [C@(128.0.255) `_]]}}&] +[s0; &] +[s0; and must be followed by number specifying tabulator position +in dots.&] +[s0; &] +[s0; When followed by [C@(128.0.255) `~] ([*C@3 `"`~`~`"]), clears all +current tab settings (including those inherited from paragraph +style).] +::^ [s0;%- [C@(128.0.255) ;]] +::= [s0; NOP separator. In some cases it is needed to separate command +code. Example: [*C@3 `"`[l200;4 `"]] +::^ [s1; [%-C s][/C@(0.0.255) number]&] +[s0; [%-C@(128.0.255) s][/C@(0.0.255) `"text`"]] +:: [s0; Paragraph style, either defined by style number, or style name.]}}&] +[s0;3 &] +[s0;3 &] +[s2; Styles&] +[s0; &] +[s0; Paragraph styles are defined using normal character/paragraph +formatting sequence with&] +[s0; &] +[s0; [@(128.0.255) `$`$][@(0.0.255) number][@(128.0.255) ,][@(0.0.255) nnumber][@(128.0.255) #][@(0.0.255) u +uid][@(128.0.255) :][@(0.0.255) name]&] +[s0; &] +[s0; instead of text, where&] +[s0; &] +[ {{1879:8121^ [s0;%- [@(0.0.255) number]] +:: [s0; Number of style `- can be used with [@(128.0.255) s] paragraph +format command code.] +:: [s0;%- [@(0.0.255) nnumber]] +:: [s0; Number of style of next paragraph `- used by RichText editor +when inserting paragraphs.] +:: [s0;%- [@(0.0.255) uuid]] +:: [s0; 32 digit unique hexadecimal identifier of style.] +:: [s0;%- [@(0.0.255) name]] +:: [s0; Name of style, displayed by editors. Can also be used with [@(128.0.255) s] +paragraph format command code.]}}&] +[s0; &] +[s0; Style with [@(0.0.255) number] `= 0 and [@(0.0.255) uuid `= ]00000000000000000000000 +000000000 is [*/ default] style.&] +[s0; &] +[s0; Example:&] +[s0; &] +[s0; [C@3 `"`[`*/`+117 `$`$2,0#07143242482611002448121871408047:title`]`"]&] +[s0;@(0.0.255) &] +[s0;@(0.0.255) &] +[s2; Objects&] +[s0;@(0.0.255) &] +[s0; Object plays the role of the single character and is displayed +according to its type. It is started with a header in the form&] +[s0; &] +[s0; [@(128.0.255) `@`@][@(0.0.255) format][@(128.0.255) :][@(0.0.255) cx][@(128.0.255) `&][@(0.0.255) c +y]&] +[s0;@(0.0.255) &] +[s0;%- where&] +[s0; &] +[ {{1879:8121^ [s0; [@(0.0.255) format]] +:: [s0; Format of objects. This format must be recognized by the application. +By default, RichText recognizes the PNG format.] +:: [s0; [@(0.0.255) cx]] +:: [s0; Width of object in dots.] +:: [s0; [@(0.0.255) cy]] +:: [s0; Height of object in dots.]}}&] +[s0;@(0.0.255) &] +[s0; Header is followed by binary data of object, encoded in 7 bit +format. Bit 7 of data bytes is always 1, so that actual data +bytes are in range 128`-255. First byte in range 32`-127 ends +data sequence.&] +[s0; &] +[s0; Data are encoded in 7 byte groups, which corresponds to 8 bytes +of encoded format. First byte of this 8 bytes block always contains +eight bits of following bytes, LSB (that is bit 0) being the +eight bit for first byte in block.&] +[s0;@(0.0.255) &] +[s0;@(0.0.255) &] +[s2; Tables&] +[s0; &] +[s0; Table definition starts with&] +[s0;3 &] +[s0; [@(128.0.255) `{`{]&] +[s0;@(128.0.255) &] +[s0; pair, followed by set of numbers separated with&] +[s0; &] +[s0; [@(128.0.255) :]&] +[s0;@(128.0.255) &] +[s0; Numbers represent ratios of column widths; count of numbers +is equivalent to count of columns. Next there is [/ table/cell +formatting sequence] ended with single space character. Cells +are separated with&] +[s0; &] +[s0; [@(128.0.255) `::]&] +[s0;@(128.0.255) &] +[s0; characters and another table/cell formating sequence (to setup +format for each individual cell). Formating of cells is inherited +from previous cells. Table ends with&] +[s0; &] +[s0; [@(128.0.255) `}`}]&] +[s0;@(128.0.255) &] +[s0; pair.&] +[s0; &] +[ {{1993:8007^@(229.229.248)-1 [s0; [*/ Table/cell formating sequence]] +::=@2 [s0;%- ] +::^ [s0;%- [C@(128.0.255) <][/C@(0.0.255) number]] +:: [s0; Left margin of table in dots.] +:: [s0;%- [C@(128.0.255) >][/C@(0.0.255) number]] +:: [s0; Right margin of table in dots.] +:: [s0;%- [C@(128.0.255) B][/C@(0.0.255) number]] +:: [s0; Space before table in dots.] +:: [s0;%- [C@(128.0.255) A][/C@(0.0.255) number]] +:: [s0; Space after table in dots.] +:: [s0;%- [C@(128.0.255) f][/C@(0.0.255) number]] +:: [s0; Frame thickness in dots. Frame is outer border of table. Default +value is 10.] +:: [s0;%- [C@(128.0.255) F][/C@(0.0.255) color]] +:: [s0; Color of the frame.] +:: [s0; [%-C@(128.0.255) g][/C@(0.0.255) number]] +:: [s0; Grid thickness in dots. Grid are lines dividing cells inside +table. Default value is 4.] +:: [s0;%- [C@(128.0.255) G][/C@(0.0.255) color]] +:: [s0; Color of the grid.] +:: [s0;%- [C@(128.0.255) k]] +:: [s0; Keep the cell on single page.] +:: [s0;%- [C@(128.0.255) K]] +:: [s0; Keep the table on single page.] +:: [s0;%- [C@(128.0.255) `~]] +:: [s0; Sets grid and frame thickness to zero. Useful when using tables +to organize text.] +:: [s0; [%-C@(128.0.255) h][/C@(0.0.255) number]] +:: [s0; Number of header rows. Header rows are repeated at the beginning +of every page.] +:: [s0;%- [C@(128.0.255) `^]] +:: [s0; Cell aligns to top. Default.] +:: [s0;%- [C@(128.0.255) `=]] +:: [s0; Cell aligns to center (vertical).] +:: [s0;%- [C@(128.0.255) v]] +:: [s0; Cell aligns to bottom.] +:: [s1; [%-C l][/C@(0.0.255) number/number]&] +[s0; [%-C@(128.0.255) l][/C@(0.0.255) number]&] +[s0; [%-C@(128.0.255) l][/C@(0.0.255) /number]] +:: [s0; Sets left cell border (first [/@(0.0.255) number]) and margin +in dots. If any of numbers is missing, sets only the one present. +Default is border: 0, margin: 25.] +:: [s1; [C r][/C@(0.0.255) number/number]&] +[s0; [C@(128.0.255) r][/C@(0.0.255) number]&] +[s0; [C@(128.0.255) r][/C@(0.0.255) /number]] +:: [s0; Sets right cell border (first [/@(0.0.255) number]) and margin +in dots. If any of numbers is missing, sets only the one present. +Default is border: 0, margin: 25.] +:: [s1; [C t][/C@(0.0.255) number/number]&] +[s0; [C@(128.0.255) t][/C@(0.0.255) number]&] +[s0; [C@(128.0.255) t][/C@(0.0.255) /number]] +:: [s0; Sets top cell border (first [/@(0.0.255) number]) and margin in +dots. If any of numbers is missing, sets only the one present. +Default is border: 0, margin: 15.] +:: [s1; [C b][/C@(0.0.255) number/number]&] +[s0; [C@(128.0.255) b][/C@(0.0.255) number]&] +[s0; [C@(128.0.255) b][/C@(0.0.255) /number]] +:: [s0; Sets bottom cell border (first [/@(0.0.255) number]) and margin +in dots. If any of numbers is missing, sets only the one present. +Default is border: 0, margin: 15.] +:: [s1; [C a][/C@(0.0.255) number/number]&] +[s0; [C@(128.0.255) a][/C@(0.0.255) number]&] +[s0; [C@(128.0.255) a][/C@(0.0.255) /number]] +:: [s0; Sets all cell borders (first [/@(0.0.255) number]) and margins +in dots. If any of numbers is missing, sets only the one present.] +:: [s0; [C@(128.0.255) `@][/C@(0.0.255) color]] +:: [s0; Cell background color. Default is White.] +:: [s0; [C@(128.0.255) R][/C@(0.0.255) color]] +:: [s0; Cell border color. Default is Black.] +:: [s0; [C@(128.0.255) !]] +:: [s0; Resets cell formatting to default values.] +:: [s0; [C@(128.0.255) H][/C@(0.0.255) number]] +:: [s0; Sets the minimal height of cell (and therefore also of row) +in dots.] +:: [s0; [C@(128.0.255) `-][/C@(0.0.255) number]] +:: [s0; Horizontal cell span.] +:: [s0; [C@(128.0.255) `|][/C@(0.0.255) number]] +:: [s0; Vertical cell span.] +:: [s0; [C@(128.0.255) ;]] +::= [s0; NOP separator. In some cases it helps to separate command code.]}}&] +[s0;3 &] +[s0; Note: There is also legacy support for old table format (from +previous QTF version) that is based on [@(128.0.255) `+`+ ]pair +as table start/stop and [@(128.0.255) `|`| `-`-] to divide cells/lines. +&] +[s0;3 &] +[s0;3 &] +[s2; [3 Examples]&] +[s0; &] +[ {{10000 [s0; [*C@3;1 `"Normal `[`* bold`] `[/ italic`] `[`_ underline`] `[`` +superscript`] `[, subscript`]`"]] +:: [s0; Normal [* bold] [/ italic] [_ underline] [` superscript] [, subscript]]}}&] +[s0; &] +[ {{10000 [s0; [*C@3;1 `"```[ ```] `\1`[escaped`]`\1 `[`* bold`]`"]] +:: [s0; `[ `] `[escaped`] [* bold]]}}&] +[s0; &] +[ {{10000 [s0; [*C@3;1 `"`[A Arial (Sans`-Serif)`] `[R Times New Roman (Serif)`] +`[C Courier (Monospace)`]`"]] +:: [s0; Arial (Sans`-Serif) [R Times New Roman (Serif)] [C Courier (Monospace)]]}}&] +[s0; &] +[ {{10000 [s0; [*C@3;1 `"`[0 6pt `]`[1 8pt `]`[2 10pt `]`[3 12pt `]`[4 16pt `]`[5 +20pt `]`[6 24pt `]`[7 28pt `]`[8 36pt `]`[9 48pt `]`"]] +:: [s0; [0 6pt ][1 8pt ]10pt [3 12pt] [4 16pt ][5 20pt ][6 24pt ][7 28pt ][8 36pt ][9 48pt +]]}}&] +[s0; &] +[ {{10000 [s0; [*C@3;1 `"`[!Tahoma! Tahoma`]`"]] +:: [s0; Tahoma]}}&] +[s0; &] +[ {{10000 [s0; [*C@3;1 `"`[`+500 500dots`]`"]] +:: [s0; [+500 500dots]]}}&] +[s0; &] +[ {{10000 [s0; [*C@3;1 `"`[`@4 Green text`] `[`$(255.220.200) Pink background`]`"]] +:: [s0; [@4 Green text] [$(255.220.200) Pink background]]}}&] +[s0; &] +[ {{10000 [s0; [*C@3;1 `"`[%EN`-US English language`] `[%CS`-CZ Czech language`]`"]] +:: [s0;%CS-CZ [%% English language ]Czech language]}}&] +[s0; &] +[ {{10000 [s0; [*C@3;1 `"`[`^upp.sf.net`^ Hyperlink`] `[Icompiler, linker; Index +entry`]`"]] +:: [s0; [^upp`.sf`.net^ Hyperlink] [Icompiler`, linker; Index entry]]}}&] +[s0; &] +[ {{10000 [s0; [*C@3;1 `"`[:label: Labeled paragraph`]`"]] +:: [s0; Labeled paragraph]}}&] +[s0; &] +[ {{10000 [s0; [*C@3;1 `"`[< Left paragraph alignment`]`"]] +:: [s0; Left paragraph alignment]}}&] +[s0; &] +[ {{10000 [s0; [*C@3;1 `"`[`= Center paragraph alignment`]`"]] +:: [s0; Center paragraph alignment]}}&] +[s0; &] +[ {{10000 [s0; [*C@3;1 `"`[> Right paragraph alignment`]`"]] +:: [s0; Right paragraph alignment]}}&] +[s0; &] +[ {{10000 [s0; [*C@3;1 `"`[# Justify alignment. Just some text to demosntrate +it... Just some text to demonstrate it... Just some text to demonstrate +it...`]`"]] +:: [s0; Justify alignment. Just some text to demosntrate it... Just +some text to demonstrate it... Just some text to demonstrate +it...]}}&] +[s0; &] +[ {{10000 [s0; [*C@3;1 `"`[l1000 Left margin 1000dots`]`"]] +:: [s0; Left margin 1000dots]}}&] +[s0; &] +[ {{10000 [s0; [*C@3;1 `"`[i1000 Indent 1000 dots.Just some text to demonstrate +it... Just some text to demonstrate it...`]`"]] +:: [s0; Indent 1000 dots.Just some text to demonstrate it... Just some +text to demonstrate it...]}}&] +[s0; &] +[ {{10000 [s0; [*C@3;1 `"`[r1000 Right margin 1000 dots.Just some text to demonstrate +it... Just some text to demonstrate it...`]`"]] +:: [s0; Right margin 1000 dots.Just some text to demonstrate it... Just +some text to demonstrate it...]}}&] +[s0; &] +[ {{10000 [s0; [*C@3;1 `"Paragraph`&`[b200 Before 200dots`]`"]] +:: [s0; Paragraph&] +[s0; Before 200dots]}}&] +[s0; &] +[ {{10000 [s0; [*C@3;1 `"`[a200 After 200dots`]`&Paragraph`"]] +:: [s0;a200; After 200dots&] +[s0; Paragraph]}}&] +[s0; &] +[ {{10000 [s0; [*C@3;1 `"`[i200 `[O0 bullet`&`]`[O1 bullet`&`]`[O2 bullet`&`]`[O3 +bullet`]`]`"]] +:: [s0;i200;O0; bullet&] +[s0;i200;O1; bullet&] +[s0;i200;O2; bullet&] +[s0; bullet]}}&] +[s0; &] +[ {{10000 [s0; [*C@3;1 `"`[O9i500 text bullet`-`|Just some text to demonstrate +it... Just some text to demonstrate it...Just some text to demonstrate +it... Just some text to demonstrate it...`]`"]] +:: [s0; text bullet-|Just some text to demonstrate it... Just some text +to demonstrate it...Just some text to demonstrate it... Just +some text to demonstrate it...]}}&] +[s0; &] +[ {{10000 [s0; [*C@3;1 `"`[O9i200 `[N1m.; `-`|level 1`&`]`[N1a `-`|level 2`&`]`[N1a +`-`|level 2`&`]`[N1 `-`|level 1`&`]`[N1a `-`|level 2`]`]`"]] +:: [s0;i200;O9;m`.;N1; -|level 1&] +[s0;i200;O9;N1a; -|level 2&] +[s0;i200;O9;N1a; -|level 2&] +[s0;i200;O9;N1; -|level 1&] +[s0; -|level 2]}}&] +[s0; &] +[ {{10000 [s0; [*C@3;1 `"`[`~300`~`=.2000`~>`-3000 `-`|Normal tab`-`|Centered +tab`-`|Right tab`]`"]] +:: [s0; -|Normal tab-|Centered tab-|Right tab]}}&] +[s0; &] +[ {{10000 [s0; [*C@3;1 `"`{`{1:2 A1]] +:: [s0; [*C@3;1 A2]] +:: [s0; [*C@3;1 B1]] +:: [s0; [*C@3;1 B2`}`}`"]] +:: [ {{3333:6667 [s0; A1] +:: [s0; A2] +:: [s0; B1] +:: [s0; B2]}}]}}&] +[s0; &] +[ {{10000 [s0; [*C@3;1 `"`{`{2:1G4g100F5f50 A1`:: A2`:: B1`:: B2`}`}`"]] +:: [ {{6666:3334f50;F5g100;G4 [s0; A1] +:: [s0; A2] +:: [s0; B1] +:: [s0; B2]}}]}}&] +[s0; &] +[ {{10000 [s0; [*C@3;1 `"`{`{1:2 A1`::l40/60R6`@3 A2`::! B1`:: B2`}`}`"]] +:: [ {{3333:6667 [s0; A1] +::l40/60@3R6 [s0; A2] +::l0/25@2R0 [s0; B1] +:: [s0; B2]}}]}}&] +[s0; &] +[ {{10000 [s0; [*C@3;1 `"`{`{1:1:1`|2 A1`::`-2 A2`:: A3`:: B1`:: B2`:: B3`}`}`"]] +:: [ {{3333:3333:3334|1 [s0; A1] +::-1 [s0; A2] +:: [s0;%- ] +:: [s0;%- ] +:: [s0; B2] +:: [s0; B3]}}]}}&] +[s0; &] +[ {{10000 [s0; [*C@3;1 `"`{`{1:2 A1`:: A2`:: B1`:: `{`{1:2 a1`:: a2`:: a1`:: a2`}`}`}`}`"]] +:: [ {{3333:6667 [s0; A1] +:: [s0; A2] +:: [s0; B1] +:: [ {{3333:6667 [s0; a1] +:: [s0; a2] +:: [s0; a1] +:: [s0; a2]}}]}}]}}&] +[s0; ] \ No newline at end of file diff --git a/uppdev/SyntaxHighlight/test.h b/uppdev/SyntaxHighlight/test.h index 3b869c5f0..4fe588977 100644 --- a/uppdev/SyntaxHighlight/test.h +++ b/uppdev/SyntaxHighlight/test.h @@ -1,18 +1,35 @@ -#ifndef _SyntaxHighlight_test_h_ -#define _SyntaxHighlight_test_h_ +#include +#include -#include "Common.h" +using namespace Upp; +using namespace std; -class XXX { -typedef XXX yyy; -public: - -private: +namespace Upp { + template<> void Xmlize(XmlIO xml, vector& data) { + if(xml.IsStoring()) + for(int i = 0; i < (int)data.size(); i++) + Xmlize(xml.Add("item"), data[i]); + else { + data.clear(); + for(int i = 0; i < xml->GetCount(); i++) + if(xml->Node(i).IsTag("item")) { + data.push_back(0); + Xmlize(xml.At(i), data.back()); + } + } + } }; -struct Y { - -}; - - -#endif +CONSOLE_APP_MAIN +{ + vector x; + x.push_back(1); + x.push_back(2); + x.push_back(3); + String s = StoreAsXML(x, "std-test"); + DUMP(s); + vector y; + LoadFromXML(y, s); + for(int i = 0; i < (int)y.size(); i++) + DUMP(y[i]); +} diff --git a/uppdev/uncompress/uncompress.cpp b/uppdev/uncompress/uncompress.cpp new file mode 100644 index 000000000..96a8c9ce8 --- /dev/null +++ b/uppdev/uncompress/uncompress.cpp @@ -0,0 +1,8 @@ +#include + +using namespace Upp; + +CONSOLE_APP_MAIN +{ + SaveFile("z:/zaci/5-2007-2.xml", ZDecompress(LoadFile("z:/zaci/5-2007-2.zml"))); +} diff --git a/uppdev/uncompress/uncompress.upp b/uppdev/uncompress/uncompress.upp new file mode 100644 index 000000000..ab8acd7d3 --- /dev/null +++ b/uppdev/uncompress/uncompress.upp @@ -0,0 +1,9 @@ +uses + Core; + +file + uncompress.cpp; + +mainconfig + "" = ""; +