mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 14:16:07 -06:00
RichText, RichEdit: DiagramEditor
This commit is contained in:
parent
eafd322058
commit
0d6c7f0601
34 changed files with 977 additions and 594 deletions
|
|
@ -165,36 +165,53 @@ String BinDiff(const String& base, const String& data)
|
|||
return diff;
|
||||
}
|
||||
|
||||
void BinUndoRedo::Reset(const String& current, const String& ids)
|
||||
void BinUndoRedo::Reset(const String& current, const Value& info)
|
||||
{
|
||||
undo.Clear();
|
||||
redo.Clear();
|
||||
undosize = 0;
|
||||
commit.data = current;
|
||||
commit.ids = ids;
|
||||
commit.info = info;
|
||||
}
|
||||
|
||||
bool BinUndoRedo::Commit(const String& current, const String& ids, int limit)
|
||||
bool BinUndoRedo::DropUndo()
|
||||
{
|
||||
if(undo.GetCount()) {
|
||||
undosize -= undo[0].data.GetCount();
|
||||
undo.Remove(0);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BinUndoRedo::DropRedo()
|
||||
{
|
||||
if(redo.GetCount()) {
|
||||
redo.Remove(0);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BinUndoRedo::Commit(const String& current, const Value& info, int limit)
|
||||
{
|
||||
bool ret = false;
|
||||
if(redo.GetCount()) {
|
||||
redo.Clear();
|
||||
ret = true;
|
||||
}
|
||||
if(current != commit.data || ids != commit.ids) {
|
||||
if(current != commit.data || info != commit.info) {
|
||||
String u = BinDiff(current, commit.data);
|
||||
while(undo.GetCount() && undosize + u.GetCount() > limit) {
|
||||
undosize -= undo[0].data.GetCount();
|
||||
undosize -= undo[0].ids.GetCount();
|
||||
undo.Remove(0);
|
||||
}
|
||||
while(undosize + u.GetCount() > limit)
|
||||
if(!DropUndo())
|
||||
break;
|
||||
Entry& e = undo.Add();
|
||||
e.data = u;
|
||||
e.ids = commit.ids;
|
||||
e.info = commit.info;
|
||||
undosize += u.GetCount();
|
||||
undosize += ids.GetCount();
|
||||
undosize += info.GetCount();
|
||||
commit.data = current;
|
||||
commit.ids = ids;
|
||||
commit.info = info;
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
|
|
@ -205,10 +222,11 @@ String BinUndoRedo::Undo(const String& current)
|
|||
if(undo.GetCount()) {
|
||||
Entry prev = undo.Pop();
|
||||
commit.data = BinUndiff(commit.data, prev.data);
|
||||
commit.info = prev.info;
|
||||
|
||||
Entry& e = redo.Add();
|
||||
e.data = BinDiff(commit.data, current);
|
||||
e.ids = prev.ids;
|
||||
e.info = prev.info;
|
||||
return commit.data;
|
||||
}
|
||||
return String::GetVoid();
|
||||
|
|
@ -219,21 +237,14 @@ String BinUndoRedo::Redo(const String& current)
|
|||
if(redo.GetCount()) {
|
||||
Entry next = redo.Pop();
|
||||
commit.data = BinUndiff(commit.data, next.data);
|
||||
commit.info = next.info;
|
||||
|
||||
Entry& e = undo.Add();
|
||||
e.data = BinDiff(commit.data, current);
|
||||
e.ids = next.ids;
|
||||
e.info = next.info;
|
||||
return commit.data;
|
||||
}
|
||||
return String::GetVoid();
|
||||
}
|
||||
|
||||
void BinUndoRedo::Ids(Event<const String&> fn)
|
||||
{
|
||||
fn(commit.ids);
|
||||
for(const Entry& e : undo)
|
||||
fn(e.ids);
|
||||
for(const Entry& e : redo)
|
||||
fn(e.ids);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ String BinUndiff(const String& base, const String& bin_diff);
|
|||
class BinUndoRedo {
|
||||
struct Entry : Moveable<Entry> {
|
||||
String data;
|
||||
String ids;
|
||||
Value info;
|
||||
};
|
||||
Entry commit;
|
||||
Vector<Entry> undo;
|
||||
|
|
@ -12,12 +12,18 @@ class BinUndoRedo {
|
|||
int undosize = 0;
|
||||
|
||||
public:
|
||||
void Reset(const String& current, const String& ids = String());
|
||||
bool Commit(const String& current, const String& ids, int limit = 4096*1024);
|
||||
void Reset(const String& current, const Value& info = Value());
|
||||
bool Commit(const String& current, const Value& info, int limit = 4096*1024);
|
||||
bool Commit(const String& current, int limit = 4096*1024) { return Commit(current, String(), limit); }
|
||||
bool IsUndo() const { return undo.GetCount(); }
|
||||
bool IsRedo() const { return redo.GetCount(); }
|
||||
int GetUndoCount() const { return undo.GetCount(); }
|
||||
int GetRedoCount() const { return redo.GetCount(); }
|
||||
bool DropUndo();
|
||||
bool DropRedo();
|
||||
Value GetUndoInfo(int i) const { return undo[i].info; }
|
||||
Value GetRedoInfo(int i) const { return redo[i].info; }
|
||||
Value GetCommitInfo() const { return commit.info; }
|
||||
String Undo(const String& current);
|
||||
String Redo(const String& current);
|
||||
void Ids(Event<const String&> fn);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1334,6 +1334,28 @@ bool StoreToFile(Event<Stream&> serialize, const char *file, int version) {
|
|||
return !f.IsError();
|
||||
}
|
||||
|
||||
String StoreAsString(Event<Stream&> serialize) {
|
||||
StringStream ss;
|
||||
Store(serialize, ss);
|
||||
return ss;
|
||||
}
|
||||
|
||||
bool LoadFromString(Event<Stream&> serialize, const String& s) {
|
||||
StringStream ss(s);
|
||||
return Load(serialize, ss);
|
||||
}
|
||||
|
||||
Vector<String> StoreAsStrings(Event<Stream&> serialize) {
|
||||
StringsStreamOut ss;
|
||||
Store(serialize, ss);
|
||||
return ss.PickResult();
|
||||
}
|
||||
|
||||
bool LoadFromStrings(Event<Stream&> serialize, const Vector<String>& s) {
|
||||
StringsStreamIn ss(s);
|
||||
return Load(serialize, ss);
|
||||
}
|
||||
|
||||
Stream& Pack16(Stream& s, int& i) {
|
||||
if(s.IsLoading()) {
|
||||
i = (int16) s.Get16le();
|
||||
|
|
|
|||
|
|
@ -428,6 +428,10 @@ bool Load(Event<Stream&> serialize, Stream& stream, int version = Null);
|
|||
bool Store(Event<Stream&> serialize, Stream& stream, int version = Null);
|
||||
bool LoadFromFile(Event<Stream&> serialize, const char *file = NULL, int version = Null);
|
||||
bool StoreToFile(Event<Stream&> serialize, const char *file = NULL, int version = Null);
|
||||
String StoreAsString(Event<Stream&> serialize);
|
||||
bool LoadFromString(Event<Stream&> serialize, const String& s);
|
||||
Vector<String> StoreAsStrings(Event<Stream&> serialize);
|
||||
bool LoadFromStrings(Event<Stream&> serialize, const Vector<String>& s);
|
||||
|
||||
template <class T>
|
||||
bool Load(T& x, Stream& s, int version = Null) {
|
||||
|
|
@ -451,33 +455,27 @@ bool StoreToFile(T& x, const char *name = NULL, int version = Null) {
|
|||
|
||||
template <class T>
|
||||
String StoreAsString(T& x) {
|
||||
StringStream ss;
|
||||
Store(x, ss);
|
||||
return ss;
|
||||
return StoreAsString([&](Stream& s) { s % x; });
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool LoadFromString(T& x, const String& s) {
|
||||
StringStream ss(s);
|
||||
return Load(x, ss);
|
||||
return LoadFromString([&](Stream& s) { s % x; }, s);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
Vector<String> StoreAsStrings(T& x) {
|
||||
StringsStreamOut ss;
|
||||
Store(x, ss);
|
||||
return ss.PickResult();
|
||||
return StoreAsStrings([&](Stream& s) { s % x; });
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool LoadFromStrings(T& x, const Vector<String>& s) {
|
||||
StringsStreamIn ss(s);
|
||||
return Load(x, ss);
|
||||
return LoadFromStrings([&](Stream& s) { s % x; }, s);
|
||||
}
|
||||
|
||||
void RegisterGlobalConfig(const char *name);
|
||||
void RegisterGlobalSerialize(const char *name, Event<Stream&> WhenSerialize);
|
||||
void RegisterGlobalConfig(const char *name, Event<> WhenFlush);
|
||||
void RegisterGlobalConfig(const char *name, Event<> WhenFlush);
|
||||
|
||||
String GetGlobalConfigData(const char *name);
|
||||
void SetGlobalConfigData(const char *name, const String& data);
|
||||
|
|
|
|||
|
|
@ -36,19 +36,31 @@ data into BinUndoRedo. Lately, it can retrieve binary String
|
|||
with [* Undo]/[* Redo] a serialize it back into content.&]
|
||||
[s3; &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:BinUndoRedo`:`:Reset`(const String`&`): [@(0.0.255) void]
|
||||
[* Reset]([@(0.0.255) const] String[@(0.0.255) `&] [*@3 current])&]
|
||||
[s5;:Upp`:`:BinUndoRedo`:`:Reset`(const String`&`,const Value`&`): [@(0.0.255) void]
|
||||
[* Reset]([@(0.0.255) const] String[@(0.0.255) `&] [*@3 current], [@(0.0.255) const]
|
||||
Value[@(0.0.255) `&] [*@3 info] [@(0.0.255) `=] Value())&]
|
||||
[s2;%% Sets the initial data content. This is typically used after
|
||||
loading the data into application or creating a new content.&]
|
||||
loading the data into application or creating a new content.
|
||||
[%-*@3 info] is additional client code information that is associated
|
||||
with each undo/redo step and can be retrieved. Client code might
|
||||
typically use this information to manage resources that are not
|
||||
stored within [%-*@3 current] data.&]
|
||||
[s3; &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:BinUndoRedo`:`:Commit`(const String`&`,const Value`&`,int`): [@(0.0.255) bo
|
||||
ol] [* Commit]([@(0.0.255) const] String[@(0.0.255) `&] [*@3 current],
|
||||
[@(0.0.255) const] Value[@(0.0.255) `&] [*@3 info], [@(0.0.255) int]
|
||||
[*@3 limit] [@(0.0.255) `=] [@3 4096] [@(0.0.255) `*][@3 1024])&]
|
||||
[s5;:Upp`:`:BinUndoRedo`:`:Commit`(const String`&`,int`): [@(0.0.255) bool]
|
||||
[* Commit]([@(0.0.255) const] String[@(0.0.255) `&] [*@3 current], [@(0.0.255) int]
|
||||
[*@3 limit] [@(0.0.255) `=] [@3 4096] [@(0.0.255) `*][@3 1024])&]
|
||||
[s2;%% Adds a snapshot of content [%-*@3 current] effectively creating
|
||||
single undo step. [%-*@3 limit] represents maximum memory that
|
||||
can be used to store undo steps (when there is more, oldest steps
|
||||
are dropped).&]
|
||||
are dropped). [%-*@3 info] is additional client code information
|
||||
that is associated with each undo/redo step and can be retrieved.
|
||||
Client code might typically use this information to manage resources
|
||||
that are not stored within [%-*@3 current] data.&]
|
||||
[s3; &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:BinUndoRedo`:`:IsUndo`(`)const: [@(0.0.255) bool] [* IsUndo]()
|
||||
|
|
@ -61,6 +73,39 @@ are dropped).&]
|
|||
[s2;%% Returns true if undo steps are available..&]
|
||||
[s3; &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:BinUndoRedo`:`:GetUndoCount`(`)const: [@(0.0.255) int]
|
||||
[* GetUndoCount]() [@(0.0.255) const]&]
|
||||
[s2;%% Returns the number of undo steps.&]
|
||||
[s3; &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:BinUndoRedo`:`:GetRedoCount`(`)const: [@(0.0.255) int]
|
||||
[* GetRedoCount]() [@(0.0.255) const]&]
|
||||
[s2;%% Returns the number of redo steps.&]
|
||||
[s3; &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:BinUndoRedo`:`:DropUndo`(`): [@(0.0.255) bool] [* DropUndo]()&]
|
||||
[s2;%% Deletes oldest undo step.&]
|
||||
[s3; &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:BinUndoRedo`:`:DropRedo`(`): [@(0.0.255) bool] [* DropRedo]()&]
|
||||
[s2;%% Deletes oldest redo step.&]
|
||||
[s3; &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:BinUndoRedo`:`:GetUndoInfo`(int`)const: Value [* GetUndoInfo]([@(0.0.255) int
|
||||
] [*@3 i]) [@(0.0.255) const]&]
|
||||
[s2;%% Returns the information associated with undo step [%-*@3 i].&]
|
||||
[s3; &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:BinUndoRedo`:`:GetRedoInfo`(int`)const: Value [* GetRedoInfo]([@(0.0.255) int
|
||||
] [*@3 i]) [@(0.0.255) const]&]
|
||||
[s2;%% Returns the information associated with redo step [%-*@3 i].&]
|
||||
[s3; &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:BinUndoRedo`:`:GetCommitInfo`(`)const: Value [* GetCommitInfo]()
|
||||
[@(0.0.255) const]&]
|
||||
[s2;%% Returns the information associated with current commit.&]
|
||||
[s3; &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:BinUndoRedo`:`:Undo`(const String`&`): String [* Undo]([@(0.0.255) const]
|
||||
String[@(0.0.255) `&] [*@3 current])&]
|
||||
[s2;%% Given snapshot of content [%-*@3 current], does one undo step
|
||||
|
|
|
|||
|
|
@ -78,6 +78,32 @@ oad], restores data from the [%-*@3 file].&]
|
|||
tore], stores data to the [%-*@3 file].&]
|
||||
[s3; &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:StoreAsString`(Event`): String [* StoreAsString](Event<Stream[@(0.0.255) `&]>
|
||||
[*@3 serialize])&]
|
||||
[s2;%% Using [%-*^topic`:`/`/Core`/src`/SerializationUtils`_en`-us`#Upp`:`:Store`(Event`,Stream`&`,int`)^ S
|
||||
tore], stores data as String.&]
|
||||
[s3; &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:LoadFromString`(Event`,const String`&`): [@(0.0.255) bool]
|
||||
[* LoadFromString](Event<Stream[@(0.0.255) `&]> [*@3 serialize], [@(0.0.255) const]
|
||||
String[@(0.0.255) `&] [*@3 s])&]
|
||||
[s2;%% Using [%-*^topic`:`/`/Core`/src`/SerializationUtils`_en`-us`#Upp`:`:Load`(Event`,Stream`&`,int`)^ L
|
||||
oad], restores data from the String.&]
|
||||
[s3; &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:StoreAsStrings`(Event`): Vector<String> [* StoreAsStrings](Event<Stream[@(0.0.255) `&
|
||||
]> [*@3 serialize])&]
|
||||
[s2;%% Stores data as Vector<String>. Useful in rare cases where
|
||||
stored data is >2GB.&]
|
||||
[s3; &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:LoadFromStrings`(Event`,const Vector`&`): [@(0.0.255) bool]
|
||||
[* LoadFromStrings](Event<Stream[@(0.0.255) `&]> [*@3 serialize], [@(0.0.255) const]
|
||||
Vector<String>[@(0.0.255) `&] [*@3 s])&]
|
||||
[s2;%% Loads data from Vector<String>. Useful in rare cases where
|
||||
stored data is >2GB.&]
|
||||
[s3; &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:Load`(T`&`,Stream`&`,int`): [@(0.0.255) template] <[@(0.0.255) class]
|
||||
T> [@(0.0.255) bool] [* Load](T[@(0.0.255) `&] [*@3 x], Stream[@(0.0.255) `&]
|
||||
[*@3 s], [@(0.0.255) int] [*@3 version ][@(0.0.255) `=] [* Null])&]
|
||||
|
|
@ -121,6 +147,19 @@ tring]_[* StoreAsString]([*@4 T][@(0.0.255) `&]_[*@3 x])&]
|
|||
[s2;%% Restores serialized data from the String (e.g. previously
|
||||
stored by StoreAsString).&]
|
||||
[s3;%% &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:StoreAsStrings`(T`&`): [@(0.0.255) template] <[@(0.0.255) class]
|
||||
T> Vector<String> [* StoreAsStrings](T[@(0.0.255) `&] [*@3 x])&]
|
||||
[s2;%% Stores [%-*@3 x] using its Serialize method a Vector<String>.
|
||||
Useful in rare cases where stored data is >2GB.&]
|
||||
[s3; &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:LoadFromStrings`(T`&`,const Vector`&`): [@(0.0.255) template]
|
||||
<[@(0.0.255) class] T> [@(0.0.255) bool] [* LoadFromStrings](T[@(0.0.255) `&]
|
||||
[*@3 x], [@(0.0.255) const] Vector<String>[@(0.0.255) `&] [*@3 s])&]
|
||||
[s2;%% Restores serialized data from the Vector<String> (previously
|
||||
stored by StoreAsStrings).&]
|
||||
[s0;%% &]
|
||||
[s0;%% &]
|
||||
[s0;%% [*@3;4 Global modular serialization support]&]
|
||||
[s0;#%% Modular serialization is a viable option for storing configuration
|
||||
|
|
@ -133,7 +172,7 @@ serialization of all such data with single stream.&]
|
|||
[s5;:RegisterGlobalConfig`(const char`*`): [@(0.0.255) void]_[* RegisterGlobalConfig]([@(0.0.255) c
|
||||
onst]_[@(0.0.255) char]_`*[*@3 name])&]
|
||||
[s7;%% Registers name as global configuration key.&]
|
||||
[s3;%% &]
|
||||
[s3; &]
|
||||
[s4; &]
|
||||
[s5;:Upp`:`:RegisterGlobalSerialize`(const char`*`,Upp`:`:Event`<Upp`:`:Stream`&`>`): [@(0.0.255) v
|
||||
oid]_[* RegisterGlobalSerialize]([@(0.0.255) const]_[@(0.0.255) char]_`*[*@3 name],
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ nt] [*@3 part`_size] [@(0.0.255) `=] [@N 1024`*1024 `- 256])&]
|
|||
][@(0.0.255)3 :][3 ][@(0.0.255)3 public][3 Stream]&]
|
||||
[s2;%% Input stream corresponding to StringsStreamOut `- reads data
|
||||
from multiple chunks.&]
|
||||
[s0;i448;a25;kKO9;:noref:@(0.0.255) &]
|
||||
[s3; &]
|
||||
[ {{10000F(128)G(128)@1 [s0;%% [* Public Method List]]}}&]
|
||||
[s3; &]
|
||||
[s5;:Upp`:`:StringsStreamIn`:`:StringsStreamIn`(const Vector`&`): [* StringsStreamIn]([@(0.0.255) c
|
||||
|
|
|
|||
|
|
@ -1026,7 +1026,7 @@ String Ctrl::Name0() const {
|
|||
return s;
|
||||
}
|
||||
|
||||
String Ctrl::Name(Ctrl *ctrl)
|
||||
String Ctrl::Name(const Ctrl *ctrl)
|
||||
{
|
||||
return Upp::Name(ctrl);
|
||||
}
|
||||
|
|
@ -1057,7 +1057,7 @@ bool Ctrl::InLoop() const
|
|||
bool Ctrl::InCurrentLoop() const
|
||||
{
|
||||
GuiLock __;
|
||||
return GetLoopCtrl() == this;
|
||||
return GetLoopCtrl() && GetLoopCtrl()->GetOwner() == GetOwner();
|
||||
}
|
||||
|
||||
#ifdef HAS_TopFrameDraw
|
||||
|
|
|
|||
|
|
@ -1431,7 +1431,7 @@ public:
|
|||
void DoSkin();
|
||||
|
||||
String Name() const;
|
||||
static String Name(Ctrl *ctrl);
|
||||
static String Name(const Ctrl *ctrl);
|
||||
|
||||
#ifdef _DEBUG
|
||||
virtual void Dump() const;
|
||||
|
|
|
|||
|
|
@ -161,11 +161,11 @@ void BufferPainter::Create(ImageBuffer& ib, int mode_)
|
|||
|
||||
if(mode_ != mode || (Size)size != ib.GetSize()) {
|
||||
mode = mode_;
|
||||
|
||||
|
||||
rasterizer.Create(ib.GetWidth(), ib.GetHeight(), mode == MODE_SUBPIXEL);
|
||||
cojob.Clear();
|
||||
cofill.Clear();
|
||||
|
||||
|
||||
render_cx = ib.GetWidth();
|
||||
if(mode == MODE_SUBPIXEL) {
|
||||
render_cx *= 3;
|
||||
|
|
@ -178,7 +178,7 @@ void BufferPainter::Create(ImageBuffer& ib, int mode_)
|
|||
co_subpixel.Clear();
|
||||
co_span.Clear();
|
||||
span.Clear();
|
||||
|
||||
|
||||
co_clear.Clear();
|
||||
}
|
||||
|
||||
|
|
@ -201,11 +201,11 @@ void BufferPainter::Create(ImageBuffer& ib, int mode_)
|
|||
attr.mask = false;
|
||||
attr.invert = false;
|
||||
attr.mtx_serial = 0;
|
||||
|
||||
|
||||
pathattr = attr;
|
||||
|
||||
ClearPath();
|
||||
|
||||
|
||||
jobcount = fillcount = emptycount = 0;
|
||||
|
||||
attrstack.Clear();
|
||||
|
|
@ -227,4 +227,4 @@ BufferPainter::BufferPainter(PainterTarget& t, double tolerance)
|
|||
dummy.Create(1, 1);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,8 @@ RichObject RichEdit::Adjust(RichObject o)
|
|||
|
||||
void RichEdit::InsertImage()
|
||||
{
|
||||
if(!allow_objects)
|
||||
return;
|
||||
if(!imagefs.ExecuteOpen(t_("Open image from file")))
|
||||
return;
|
||||
String fn = ~imagefs;
|
||||
|
|
@ -34,21 +36,18 @@ void RichEdit::InsertImage()
|
|||
|
||||
void RichEdit::InsertDiagram()
|
||||
{
|
||||
TopWindow app;
|
||||
app.Icon(DiagramImg::Diagram());
|
||||
app.Title("Diagram");
|
||||
app.Sizeable().Zoomable();
|
||||
DiagramEditor de;
|
||||
app.Add(de.SizePos());
|
||||
app.Execute();
|
||||
if(!allow_objects)
|
||||
return;
|
||||
|
||||
RichText clip;
|
||||
RichPara p;
|
||||
RichObject o = RichObject("qdf", ZCompress(de.Save()));
|
||||
o.InitSize(0, 0);
|
||||
p.Cat(o, formatinfo);
|
||||
clip.Cat(p);
|
||||
ClipPaste(clip, "image/qdf");
|
||||
RichObject o;
|
||||
if(EditDiagram(o)) {
|
||||
RichText clip;
|
||||
RichPara p;
|
||||
o.InitSize(0, 0);
|
||||
p.Cat(o, formatinfo);
|
||||
clip.Cat(p);
|
||||
ClipPaste(clip, "image/qdf");
|
||||
}
|
||||
}
|
||||
|
||||
bool RichEdit::Accept(PasteClip& d, RichText& clip, String& fmt)
|
||||
|
|
|
|||
|
|
@ -81,6 +81,8 @@ int ColumnPopUp::Execute()
|
|||
void ColumnPopUp::Deactivate()
|
||||
{
|
||||
Close();
|
||||
IgnoreMouseClick();
|
||||
cursor = Null;
|
||||
}
|
||||
|
||||
};
|
||||
|
|
@ -3,7 +3,7 @@ IMAGE_ID(DisplayGrid__UHD)
|
|||
IMAGE_ID(Diagram__UHD)
|
||||
IMAGE_ID(RoundRect__UHD)
|
||||
IMAGE_ID(RotateCursor__UHD)
|
||||
IMAGE_ID(SelectMode__UHD)
|
||||
IMAGE_ID(LineCursor__UHD)
|
||||
IMAGE_ID(Line__UHD)
|
||||
IMAGE_ID(Grid)
|
||||
IMAGE_ID(HorzCenter__UHD)
|
||||
|
|
@ -36,7 +36,7 @@ IMAGE_ID(DisplayGrid)
|
|||
IMAGE_ID(Diagram)
|
||||
IMAGE_ID(RoundRect)
|
||||
IMAGE_ID(RotateCursor)
|
||||
IMAGE_ID(SelectMode)
|
||||
IMAGE_ID(LineCursor)
|
||||
IMAGE_ID(Line)
|
||||
IMAGE_ID(HorzCenter)
|
||||
IMAGE_ID(VertCenter)
|
||||
|
|
@ -140,23 +140,45 @@ IMAGE_DATA(147,189,151,176,222,27,79,247,94,99,250,253,18,52,196,29,57,207,199,9
|
|||
IMAGE_END_DATA(736, 1)
|
||||
|
||||
IMAGE_BEGIN_DATA
|
||||
IMAGE_DATA(120,156,237,217,81,110,131,48,16,69,209,183,156,46,43,251,223,68,42,71,85,36,192,6,143,25,123,92,124,143,148,79)
|
||||
IMAGE_DATA(248,184,175,184,105,249,121,233,37,0,88,216,251,239,131,24,111,177,65,164,183,216,32,210,190,63,27,140,245,105,158,136)
|
||||
IMAGE_DATA(13,34,124,251,179,65,136,77,127,54,24,238,208,159,13,134,202,246,103,131,97,138,253,119,27,160,143,211,254,108,208,157)
|
||||
IMAGE_DATA(165,63,27,248,187,236,207,6,93,85,245,103,131,110,170,251,179,65,23,166,254,108,224,206,220,159,13,92,53,245,103,3)
|
||||
IMAGE_DATA(55,205,253,217,192,197,173,254,108,112,219,237,254,108,112,139,75,127,54,104,230,214,159,13,154,184,246,103,3,51,247,254)
|
||||
IMAGE_DATA(108,96,210,165,255,110,3,148,141,232,207,6,101,221,250,179,65,149,174,253,217,224,82,247,254,108,112,106,72,127,54,40)
|
||||
IMAGE_DATA(26,214,159,13,178,134,246,103,131,131,225,253,217,96,35,164,63,27,124,133,245,103,131,143,208,254,108,16,223,127,241,13)
|
||||
IMAGE_DATA(166,232,159,104,205,13,166,233,159,104,189,13,166,234,159,104,173,13,232,31,107,186,254,137,214,217,96,202,254,137,214,216)
|
||||
IMAGE_DATA(96,218,254,137,158,191,193,212,253,19,61,123,131,233,251,39,122,238,6,255,162,127,162,103,110,48,250,253,175,231,231,9)
|
||||
IMAGE_DATA(186,246,87,191,246,79,217,96,244,207,63,182,154,251,91,174,21,253,75,110,247,175,185,94,244,47,105,234,175,204,89,204)
|
||||
IMAGE_DATA(6,77,204,253,117,60,207,233,223,206,212,95,199,246,82,229,51,144,185,14,109,191,67,247,13,57,131,218,89,187,149,218)
|
||||
IMAGE_DATA(209,191,141,229,220,56,235,198,119,209,54,167,221,84,215,190,234,94,244,207,42,54,147,173,253,233,189,232,95,148,109,38)
|
||||
IMAGE_DATA(123,251,226,189,216,224,212,161,153,218,218,23,239,71,255,83,155,94,186,215,254,112,191,138,254,171,111,240,237,37,159,30)
|
||||
IMAGE_DATA(156,65,54,185,255,229,184,220,147,254,85,122,156,5,60,3,118,158,13,232,31,143,13,98,209,63,30,223,69,99,209,63)
|
||||
IMAGE_DATA(22,103,80,60,54,136,69,255,88,213,253,217,160,27,158,129,88,244,143,213,210,159,13,124,241,183,64,44,158,129,120,151)
|
||||
IMAGE_DATA(27,104,219,158,254,190,106,222,245,211,189,159,220,187,78,186,143,69,243,88,52,7,0,0,192,242,126,1,183,240,62,238)
|
||||
IMAGE_END_DATA(512, 1)
|
||||
IMAGE_DATA(120,156,237,214,81,174,219,56,12,5,208,44,103,118,53,91,237,206,58,83,160,254,121,136,37,203,150,68,74,62,167,240)
|
||||
IMAGE_DATA(215,75,44,138,188,44,242,207,175,207,175,207,191,255,255,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)
|
||||
IMAGE_DATA(0,120,161,223,127,31,120,155,223,63,30,120,139,159,217,183,3,188,137,252,243,86,103,217,183,3,236,174,150,125,59,192)
|
||||
IMAGE_DATA(174,174,102,223,14,176,155,214,236,219,1,118,34,255,188,213,105,190,15,165,207,132,84,12,125,84,179,111,7,216,84,49)
|
||||
IMAGE_DATA(211,141,249,183,3,172,164,41,251,118,128,205,52,103,223,239,32,54,113,59,251,118,128,197,61,206,190,29,96,81,183,126)
|
||||
IMAGE_DATA(243,223,204,191,29,32,147,174,217,183,3,44,166,123,246,47,238,0,68,27,150,125,59,64,114,195,179,111,7,72,106,200)
|
||||
IMAGE_DATA(111,254,155,249,183,3,204,52,53,251,118,128,68,66,178,111,7,72,32,52,251,118,128,96,242,207,91,165,200,190,29,32)
|
||||
IMAGE_DATA(64,170,236,219,1,38,74,153,125,59,192,36,105,179,127,40,213,24,210,49,118,145,250,255,254,67,173,206,128,190,177,190)
|
||||
IMAGE_DATA(37,178,127,168,213,27,208,63,214,181,84,246,15,181,186,3,250,200,154,150,203,254,161,84,123,72,39,89,205,178,217,63)
|
||||
IMAGE_DATA(148,238,16,210,81,86,177,228,239,158,159,106,247,8,232,43,249,109,145,253,67,237,62,1,253,37,175,173,178,127,168,221)
|
||||
IMAGE_DATA(43,160,207,228,180,93,246,15,165,187,133,116,154,108,174,252,63,249,248,233,156,219,158,15,239,53,45,103,137,243,111,7)
|
||||
IMAGE_DATA(222,105,106,198,146,231,223,14,188,143,252,203,255,91,77,207,215,2,249,183,3,239,16,146,173,69,242,111,7,120,106,116)
|
||||
IMAGE_DATA(254,33,51,249,231,205,228,159,55,147,127,222,76,254,121,51,249,231,205,228,159,55,147,127,222,76,254,121,51,249,39,131)
|
||||
IMAGE_DATA(168,172,236,152,127,123,183,150,232,172,236,152,127,59,176,134,232,188,236,150,255,232,243,105,19,157,153,157,242,31,125,62)
|
||||
IMAGE_DATA(237,162,103,38,255,68,201,144,155,93,242,159,161,6,218,100,152,153,252,19,161,52,175,153,115,219,33,255,89,122,201,117)
|
||||
IMAGE_DATA(87,102,54,99,110,171,231,63,75,31,105,147,101,110,242,207,108,87,103,54,122,118,167,103,118,204,127,72,253,147,235,160)
|
||||
IMAGE_DATA(77,244,220,170,103,118,206,127,216,61,6,159,207,61,173,115,235,53,187,203,231,13,202,127,200,93,58,159,203,51,167,121)
|
||||
IMAGE_DATA(59,251,219,167,207,236,134,101,63,104,7,138,245,15,60,151,103,138,153,59,251,251,231,254,236,134,231,62,96,15,170,119)
|
||||
IMAGE_DATA(232,124,30,125,92,202,94,233,115,61,206,170,157,31,176,3,93,238,53,160,135,244,53,99,118,161,185,159,176,7,242,191)
|
||||
IMAGE_DATA(174,203,25,60,251,236,167,60,191,84,217,191,120,151,46,247,107,60,151,249,154,50,120,246,249,207,247,249,165,204,125,227)
|
||||
IMAGE_DATA(157,30,221,243,198,153,204,115,43,139,165,239,213,222,219,122,214,76,173,117,15,238,33,227,221,206,100,233,187,45,79,54)
|
||||
IMAGE_DATA(179,238,85,249,62,115,132,229,63,187,209,247,171,124,159,241,30,103,179,244,142,167,239,206,98,228,29,43,239,97,172,199)
|
||||
IMAGE_DATA(249,60,123,199,211,247,102,52,234,174,133,247,48,78,183,156,150,222,117,247,157,119,107,152,117,206,196,254,49,198,148,249)
|
||||
IMAGE_DATA(141,150,237,220,222,253,27,158,130,247,234,154,155,94,239,185,123,94,239,251,220,173,163,215,123,62,242,63,90,247,188,244)
|
||||
IMAGE_DATA(120,71,203,57,87,159,89,245,244,120,199,151,135,254,194,178,242,68,169,238,43,79,102,149,218,233,107,169,124,156,213,123)
|
||||
IMAGE_DATA(247,201,170,80,51,125,45,145,139,179,58,123,61,217,20,106,165,159,213,179,80,203,136,29,160,36,117,14,206,234,43,60)
|
||||
IMAGE_DATA(77,247,92,248,254,60,151,118,254,165,218,78,158,199,119,94,176,15,60,147,110,230,103,53,21,158,174,119,95,172,39,60)
|
||||
IMAGE_DATA(147,106,214,103,245,20,158,33,247,95,168,47,60,147,98,198,223,234,168,60,195,251,80,122,18,245,136,251,194,103,123,86)
|
||||
IMAGE_DATA(67,225,9,233,73,242,94,209,46,124,166,165,26,78,158,240,222,36,238,23,109,178,207,51,195,108,229,127,95,225,191,105)
|
||||
IMAGE_DATA(191,213,240,201,57,211,20,249,175,244,141,235,82,204,177,48,203,108,51,77,147,253,74,207,184,102,133,89,102,154,167,252)
|
||||
IMAGE_DATA(239,35,213,44,43,243,204,48,83,253,218,75,186,121,38,158,169,94,237,39,221,60,19,207,52,101,254,43,253,226,92,202)
|
||||
IMAGE_DATA(89,30,206,234,251,196,204,53,109,246,15,137,122,181,138,85,231,41,255,95,36,234,213,42,82,207,243,143,179,26,63,115)
|
||||
IMAGE_DATA(231,154,62,251,127,36,232,211,74,106,217,74,49,227,74,109,225,189,74,220,27,59,80,214,218,191,176,249,87,106,8,235)
|
||||
IMAGE_DATA(83,130,187,203,255,61,61,251,58,37,31,149,179,166,247,105,226,221,236,64,127,51,123,222,37,59,149,119,78,239,83,242)
|
||||
IMAGE_DATA(156,203,127,89,244,28,110,229,170,242,221,105,61,74,158,113,249,127,38,122,70,167,153,171,124,102,90,31,146,231,156,49)
|
||||
IMAGE_DATA(162,231,58,115,246,209,247,144,243,117,68,231,96,247,252,179,174,21,243,178,98,205,172,33,123,158,178,215,199,158,178,228)
|
||||
IMAGE_DATA(43,75,29,240,205,200,236,201,53,43,235,157,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)
|
||||
IMAGE_DATA(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,46,248,15,117,184,188,62,0,0,0,0,0,0)
|
||||
IMAGE_END_DATA(1216, 1)
|
||||
|
||||
IMAGE_BEGIN_DATA
|
||||
IMAGE_DATA(120,156,237,214,187,13,195,48,16,68,65,149,227,178,220,127,19,114,226,204,146,172,15,143,183,193,12,192,6,246,1,36)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
LAYOUT(SizeLayout, 212, 164)
|
||||
ITEM(Upp::Switch, size, SetLabel(t_("Automatic\n1250x1000 (4:3)\n1600x900 (16:9)\n1600x800 (2:1)\n1000x1250 (3:4)\nCustom")).LeftPosZ(8, 192).TopPosZ(8, 112))
|
||||
ITEM(Upp::Label, dv___1, SetLabel(t_("x")).LeftPosZ(92, 40).TopPosZ(104, 19))
|
||||
ITEM(Upp::WithDropChoice<Upp::EditInt>, cx, NotNull(true).LeftPosZ(24, 64).TopPosZ(104, 19))
|
||||
ITEM(Upp::WithDropChoice<Upp::EditInt>, cy, NotNull(true).LeftPosZ(100, 64).TopPosZ(104, 19))
|
||||
ITEM(Upp::EditInt, cx, NotNull(true).LeftPosZ(24, 64).TopPosZ(104, 19))
|
||||
ITEM(Upp::EditInt, cy, NotNull(true).LeftPosZ(100, 64).TopPosZ(104, 19))
|
||||
ITEM(Upp::Button, ok, SetLabel(t_("OK")).LeftPosZ(68, 64).TopPosZ(136, 24))
|
||||
ITEM(Upp::Button, cancel, SetLabel(t_("Cancel")).LeftPosZ(138, 64).TopPosZ(136, 24))
|
||||
END_LAYOUT
|
||||
|
|
|
|||
|
|
@ -33,8 +33,8 @@ void DiagramEditor::TheBar(Bar& bar)
|
|||
bar.Add(b, "Move back", DiagramImg::MoveBack(), [=] { MoveFrontBack(true); });
|
||||
bar.Add(b, "Move front", DiagramImg::MoveFront(), [=] { MoveFrontBack(false); });
|
||||
bar.Separator();
|
||||
bar.Add(b, DiagramImg::HorzCenter(), [=] { Align(true, ALIGN_NULL); });
|
||||
bar.Add(b, DiagramImg::VertCenter(), [=] { Align(false, ALIGN_NULL); });
|
||||
bar.Add(b, "Horizontal center", DiagramImg::HorzCenter(), [=] { Align(true, ALIGN_NULL); });
|
||||
bar.Add(b, "Vertical center", DiagramImg::VertCenter(), [=] { Align(false, ALIGN_NULL); });
|
||||
bar.Separator();
|
||||
bool multi = sel.GetCount() > 1;
|
||||
bar.Add(multi, "Align left", DiagramImg::AlignLeft(), [=] { Align(true, ALIGN_LEFT); });
|
||||
|
|
@ -55,11 +55,13 @@ void DiagramEditor::TheBar(Bar& bar)
|
|||
bar.Separator();
|
||||
bar.Add("Diagram size", DiagramImg::Size(), [=] { ChangeSize(); });
|
||||
bar.Separator();
|
||||
bar.Add(shape, DPI(45));
|
||||
bar.Add(line_start, DPI(45));
|
||||
bar.Add(line_end, DPI(45));
|
||||
bar.Add(line_width, DPI(45));
|
||||
bar.Add(line_dash, DPI(45));
|
||||
int icx = IconSz().cx + DPI(4) + DPI(18);
|
||||
bar.Add(shape, icx);
|
||||
shape.Enable(!(IsCursor() && findarg(CursorItem().shape, DiagramItem::SHAPE_SVGPATH, DiagramItem::SHAPE_IMAGE) >= 0));
|
||||
bar.Add(line_start, icx);
|
||||
bar.Add(line_end, icx);
|
||||
bar.Add(line_width, icx);
|
||||
bar.Add(line_dash, icx);
|
||||
ink.DarkContent(IsDarkContent());
|
||||
bar.Add(ink);
|
||||
paper.DarkContent(IsDarkContent());
|
||||
|
|
@ -86,7 +88,7 @@ void DiagramEditor::TheBar(Bar& bar)
|
|||
if(m.aspect_ratio && !m.IsLine()) {
|
||||
Sizef sz1, sz2;
|
||||
ComputeAspectSize(m, sz1, sz2);
|
||||
m.pt[1] = m.pt[0] + (sz1.cx < sz2.cx ? sz1 : sz2);
|
||||
m.size = (sz1.cx < sz2.cx ? sz1 : sz2) / 2;
|
||||
}
|
||||
});
|
||||
})
|
||||
|
|
@ -95,8 +97,14 @@ void DiagramEditor::TheBar(Bar& bar)
|
|||
Size isz = IconSz();
|
||||
for(int i = 0; i < tool_count; i++) {
|
||||
DiagramItem m = tl[i];
|
||||
m.pt[0] = Point(2, 2);
|
||||
m.pt[1] = Point(isz.cx - 2, isz.cy - 2);
|
||||
if(m.IsLine()) {
|
||||
m.pos = Point(2, 2);
|
||||
m.size = Size(isz.cx - 4, isz.cy - 4);
|
||||
}
|
||||
else {
|
||||
m.pos = Pointf(Point(isz)) / 2;
|
||||
m.size = m.pos - 2;
|
||||
}
|
||||
m.width = log(m.width + 1);
|
||||
bar.Add(MakeIcon(m, isz), [=] {
|
||||
CancelSelection();
|
||||
|
|
@ -112,11 +120,31 @@ void DiagramEditor::TheBar(Bar& bar)
|
|||
.Check(tool == i);
|
||||
}
|
||||
bar.Break();
|
||||
text_editor.FontTools(bar);
|
||||
text_editor.InkTool(bar);
|
||||
text_editor.PaperTool(bar);
|
||||
RichEdit& be = edit_text ? text_editor : editor_bar;
|
||||
if(!edit_text) {
|
||||
if(sel.GetCount()) {
|
||||
editor_bar.formatinfo = GetSelectionFormatInfo();
|
||||
editor_bar.SetEditable();
|
||||
editor_bar.ShowFormat();
|
||||
editor_bar.diagram_bar_hack = true;
|
||||
editor_bar.WhenSel = [=] {
|
||||
for(int i : sel) {
|
||||
String& qtf = data.item[i].qtf;
|
||||
RichText txt = ParseQTF(qtf);
|
||||
txt.ApplyFormatInfo(0, editor_bar.formatinfo, txt.GetLength());
|
||||
qtf = AsQTF(txt, CHARSET_UTF8, QTF_BODY|QTF_NOCHARSET|QTF_NOLANG|QTF_NOSTYLES);
|
||||
}
|
||||
Sync();
|
||||
};
|
||||
}
|
||||
else
|
||||
editor_bar.SetReadOnly();
|
||||
}
|
||||
be.FontTools(bar);
|
||||
be.InkTool(bar);
|
||||
be.PaperTool(bar);
|
||||
bar.Separator();
|
||||
text_editor.ParaTools(bar);
|
||||
be.ParaTools(bar);
|
||||
}
|
||||
|
||||
void DiagramEditor::SetBar()
|
||||
|
|
|
|||
|
|
@ -34,21 +34,25 @@ void DiagramEditor::Cut()
|
|||
Delete();
|
||||
}
|
||||
|
||||
void DiagramEditor::AddImage(Pointf pos, const Image& img)
|
||||
{
|
||||
if(IsNull(img))
|
||||
return;
|
||||
Sizef sz = img.GetSize();
|
||||
DiagramItem& m = data.item.Add();
|
||||
m.shape = DiagramItem::SHAPE_IMAGE;
|
||||
m.blob_id = data.AddBlob(PNGEncoder().SaveString(img));
|
||||
m.pos = pos;
|
||||
m.size = sz * 0.5;
|
||||
while(max(m.size.cx, m.size.cy) > 2000)
|
||||
m.size *= 0.5;
|
||||
SetCursor(data.item.GetCount() - 1);
|
||||
}
|
||||
|
||||
void DiagramEditor::Paste()
|
||||
{
|
||||
if(IsClipboardAvailableImage()) {
|
||||
Image img = ReadClipboardImage();
|
||||
if(IsNull(img))
|
||||
return;
|
||||
Sizef sz = img.GetSize();
|
||||
int ii = data.item.GetCount();
|
||||
DiagramItem& m = data.item.Add();
|
||||
m.shape = DiagramItem::SHAPE_IMAGE;
|
||||
m.blob_id = data.AddBlob(PNGEncoder().SaveString(img));
|
||||
m.pt[0] = Rectf(Sizef(data.GetSize())).CenterPos(sz);
|
||||
m.pt[1] = m.pt[0] + sz;
|
||||
SetCursor(ii);
|
||||
}
|
||||
if(IsClipboardAvailableImage())
|
||||
AddImage(Rectf(Sizef(data.GetSize())).CenterPoint(), ReadClipboardImage());
|
||||
else {
|
||||
String txt = ReadClipboardText();
|
||||
Diagram clip;
|
||||
|
|
@ -88,8 +92,7 @@ void DiagramEditor::Duplicate()
|
|||
for(int i : sel) {
|
||||
DiagramItem& m = data.item[q++];
|
||||
m = data.item[i];
|
||||
m.pt[0] += offset;
|
||||
m.pt[1] += offset;
|
||||
m.pos += offset;
|
||||
}
|
||||
sel.Clear();
|
||||
for(int i = p; i < data.item.GetCount(); i++) {
|
||||
|
|
|
|||
|
|
@ -63,6 +63,16 @@ DiagramEditor::DiagramEditor()
|
|||
GetAttrs(DiagramItem());
|
||||
}
|
||||
|
||||
void DiagramEditor::SerializeSettings(Stream& s)
|
||||
{
|
||||
int version = 1;
|
||||
|
||||
s % grid % display_grid;
|
||||
|
||||
for(int i = 0; i < 2; i++)
|
||||
s % tl[i];
|
||||
}
|
||||
|
||||
void DiagramEditor::SetupDark(ColorPusher& c) const
|
||||
{
|
||||
c.AllowDarkContent(allow_dark_content);
|
||||
|
|
@ -91,47 +101,7 @@ DiagramEditor& DiagramEditor::AllowDarkContent(bool b)
|
|||
void DiagramEditor::Skin()
|
||||
{
|
||||
SetBar();
|
||||
/*
|
||||
Size icon_sz = IconSz();
|
||||
shape.ClearList();
|
||||
shape.SetLineCy(icon_sz.cy);
|
||||
for(int i = 0; i < DiagramItem::SHAPE_SVGPATH; i++) {
|
||||
DiagramItem m;
|
||||
m.pt[0] = Point(2, 2);
|
||||
m.pt[1] = Point(icon_sz.cx - 2, icon_sz.cy - 2);
|
||||
m.width = DPI(1);
|
||||
m.shape = i;
|
||||
shape.Add(i, MakeIcon(m, icon_sz));
|
||||
}
|
||||
|
||||
struct Dialine : DiagramItem {
|
||||
Dialine() {
|
||||
shape = SHAPE_LINE;
|
||||
pt[0].y = pt[1].y = 7;
|
||||
pt[0].x = -9999;
|
||||
pt[1].x = 9999;
|
||||
}
|
||||
};
|
||||
|
||||
auto LDL = [=](DropList& dl, bool left) {
|
||||
dl.SetLineCy(icon_sz.cy);
|
||||
dl.ClearList();
|
||||
for(int i = DiagramItem::CAP_NONE; i < DiagramItem::CAP_COUNT; i++) {
|
||||
dl.Add(i, CapIcon(left ? i : 0, left ? 0 : i));
|
||||
}
|
||||
};
|
||||
|
||||
LDL(line_start, true);
|
||||
LDL(line_end, false);
|
||||
|
||||
line_dash.ClearList();
|
||||
line_dash.SetLineCy(icon_sz.cy);
|
||||
for(int i = 0; i < DiagramItem::DASH_COUNT; i++) {
|
||||
Dialine m;
|
||||
m.dash = i;
|
||||
line_dash.Add(i, MakeIcon(m, icon_sz));
|
||||
}
|
||||
*/
|
||||
editor_bar.Skin();
|
||||
}
|
||||
|
||||
void DiagramEditor::Paint(Draw& w)
|
||||
|
|
@ -159,19 +129,13 @@ void DiagramEditor::Paint(Draw& w)
|
|||
|
||||
if(display_grid)
|
||||
for(int x = 0; x < dsz.cx; x += 8)
|
||||
for(int y = 0; y < dsz.cy; y += 8) {
|
||||
if(((x | y) & 15) == 0) {
|
||||
iw.DrawRect(x - 2, y, 5, 1, Blend(SWhite(), SGreen(), 60));
|
||||
iw.DrawRect(x, y - 2, 1, 5, Blend(SWhite(), SGreen(), 60));
|
||||
}
|
||||
else
|
||||
iw.DrawRect(x, y, 1, 1, Blend(SWhite(), SGreen()));
|
||||
}
|
||||
for(int y = 0; y < dsz.cy; y += 8)
|
||||
iw.DrawRect(x, y, 1, 1, Blend(SWhite(), SGreen()));
|
||||
|
||||
dark = IsDarkContent();
|
||||
data.Paint(iw, *this);
|
||||
|
||||
if(HasCapture() && doselection) {
|
||||
if(HasCapture() && doselection && tool < 0) {
|
||||
Rect r(dragstart, dragcurrent);
|
||||
r.Normalize();
|
||||
iw.Rectangle(r).Fill(30 * SColorHighlight()).Stroke(1, LtRed()).Dash("4").Stroke(1, White());
|
||||
|
|
@ -190,6 +154,9 @@ void DiagramEditor::Sync()
|
|||
sb.SetPage(sb.GetReducedViewSize() / GetZoom());
|
||||
sb.SetLine(8, 8);
|
||||
SyncEditor();
|
||||
|
||||
if(!IsCursor() && findarg((int)~shape, DiagramItem::SHAPE_SVGPATH, DiagramItem::SHAPE_IMAGE) >= 0)
|
||||
shape <<= DiagramItem::SHAPE_ROUNDRECT;
|
||||
}
|
||||
|
||||
void DiagramEditor::Layout()
|
||||
|
|
@ -199,7 +166,11 @@ void DiagramEditor::Layout()
|
|||
|
||||
void DiagramEditor::ResetUndo()
|
||||
{
|
||||
undoredo.Reset(GetCurrent());
|
||||
Index<Value> blob_ids;
|
||||
for(const DiagramItem& m : data.item)
|
||||
if(m.blob_id.GetCount())
|
||||
blob_ids.FindAdd(m.blob_id);
|
||||
undoredo.Reset(GetCurrent(), ValueArray(blob_ids.PickKeys()));
|
||||
}
|
||||
|
||||
void DiagramEditor::Commit()
|
||||
|
|
@ -209,7 +180,48 @@ void DiagramEditor::Commit()
|
|||
if(!m.IsLine())
|
||||
m.Normalize();
|
||||
}
|
||||
if(undoredo.Commit(GetCurrent())) {
|
||||
Index<Value> blob_ids;
|
||||
size_t blobsz = 0;
|
||||
for(const DiagramItem& m : data.item) {
|
||||
if(m.blob_id.GetCount()) {
|
||||
if(blob_ids.Find(m.blob_id) < 0) {
|
||||
blob_ids.Add(m.blob_id);
|
||||
blobsz += data.GetBlob(m.blob_id).GetCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
if(undoredo.Commit(GetCurrent(), ValueArray(clone(blob_ids.GetKeys())))) {
|
||||
size_t limit = max((size_t)20000000, 2 * blobsz);
|
||||
for(;;) { // make sure that blobs are not excessive
|
||||
Index<String> ublob_ids;
|
||||
size_t ublobsz = 0;
|
||||
auto AddIds = [&](const ValueArray& va) {
|
||||
for(Value v : va) {
|
||||
String id = ~v;
|
||||
if(ublob_ids.Find(id) < 0) {
|
||||
ublob_ids.Add(id);
|
||||
ublobsz += data.GetBlob(id).GetCount();
|
||||
}
|
||||
}
|
||||
};
|
||||
AddIds(undoredo.GetCommitInfo());
|
||||
for(int i = 0; i < undoredo.GetUndoCount(); i++)
|
||||
AddIds(undoredo.GetUndoInfo(i));
|
||||
for(int i = 0; i < undoredo.GetRedoCount(); i++)
|
||||
AddIds(undoredo.GetRedoInfo(i));
|
||||
if(ublobsz <= limit) {
|
||||
data.SweepBlobs(ublob_ids); // remove blobs that are not used anymore
|
||||
break;
|
||||
}
|
||||
if(undoredo.GetUndoCount())
|
||||
undoredo.DropUndo();
|
||||
else
|
||||
if(undoredo.GetRedoCount())
|
||||
undoredo.DropRedo();
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
SetBar();
|
||||
Sync();
|
||||
}
|
||||
|
|
@ -224,11 +236,7 @@ String DiagramEditor::GetCurrent()
|
|||
bool DiagramEditor::SetCurrent(const String& s)
|
||||
{
|
||||
KillCursor();
|
||||
// DLOG("===========");
|
||||
// DDUMP(data.item.GetCount());
|
||||
bool b = LoadFromString(data, s);
|
||||
// DDUMP(data.item.GetCount());
|
||||
// DDUMP(cursor);
|
||||
Sync();
|
||||
return b;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,9 +5,11 @@
|
|||
#define LAYOUTFILE <RichEdit/Diagram.lay>
|
||||
#include <CtrlCore/lay.h>
|
||||
|
||||
struct DiaRichEdit : RichEdit {
|
||||
class DiaRichEdit : public RichEdit {
|
||||
bool Key(dword key, int count) override;
|
||||
void PasteFilter(RichText& txt, const String& fmt) override;
|
||||
|
||||
public:
|
||||
Event<> WhenEnter;
|
||||
Event<> WhenEsc;
|
||||
};
|
||||
|
|
@ -74,10 +76,10 @@ private:
|
|||
Point draghandle = Point(0, 0);
|
||||
Point dragstart = Point(0, 0);
|
||||
Point dragcurrent = Point(0, 0);
|
||||
Rectf dragfrom = Rect(0, 0, 0, 0);
|
||||
Pointf dragfrom = Point(0, 0);
|
||||
Pointf drag_cp = Point(0, 0);
|
||||
double base_rotate = 0;
|
||||
Vector<Point2> sdragfrom;
|
||||
Vector<Pointf> sdragfrom;
|
||||
bool doselection = false; // we are doing rect selection
|
||||
bool grid = true; // snap to grid
|
||||
bool edit_text = false; // text editor is visible
|
||||
|
|
@ -101,6 +103,7 @@ private:
|
|||
ToolBar toolbar;
|
||||
DropColumns shape, line_start, line_end, line_dash, line_width;
|
||||
DiaRichEdit text_editor;
|
||||
RichEdit editor_bar; // misusing RichEdit to implement selection bar
|
||||
|
||||
ColorButton ink, paper;
|
||||
|
||||
|
|
@ -109,7 +112,7 @@ private:
|
|||
DiagramItem tl[2];
|
||||
|
||||
ScrollBars sb;
|
||||
|
||||
|
||||
void SetupDark(ColorPusher& c) const;
|
||||
bool IsDarkContent() const;
|
||||
|
||||
|
|
@ -148,15 +151,18 @@ private:
|
|||
Image WidthIcon(int i);
|
||||
void PrepareConns();
|
||||
void UseConns();
|
||||
void Grid(int shape, Point& p);
|
||||
void Grid(const DiagramItem& m, Point& p) { Grid(m.shape, p); }
|
||||
void Grid(Point& p);
|
||||
void Grid(Pointf& p);
|
||||
void ChangeSize();
|
||||
void PopPaint(Draw& w, const Image& m, bool sel);
|
||||
void Shapes(ColumnPopUp& shape);
|
||||
void Caps(ColumnPopUp& m, bool left);
|
||||
void Dashes(ColumnPopUp& m);
|
||||
void Widths(ColumnPopUp& m);
|
||||
void AddImage(Pointf pos, const Image& img);
|
||||
|
||||
RichText::FormatInfo GetFormatInfo(int itemi) const;
|
||||
RichText::FormatInfo GetSelectionFormatInfo() const;
|
||||
|
||||
void FixPositions();
|
||||
void ForEachConst(Event<const DiagramItem&> fn) const;
|
||||
|
|
@ -176,7 +182,7 @@ private:
|
|||
void GetAttrs(const DiagramItem& m);
|
||||
void GetAttrs();
|
||||
|
||||
void ComputeAspectSize(DiagramItem& m, Sizef& sz1, Sizef& sz2);
|
||||
void ComputeAspectSize(DiagramItem& m, Sizef& sz_cx, Sizef& sz_cy);
|
||||
|
||||
DiagramItem& AddItem(int shape);
|
||||
|
||||
|
|
@ -192,6 +198,8 @@ private:
|
|||
public:
|
||||
String Save() const;
|
||||
bool Load(const String& s);
|
||||
|
||||
void SerializeSettings(Stream& s);
|
||||
|
||||
DiagramEditor& DarkContent(bool b = true);
|
||||
DiagramEditor& AllowDarkContent(bool b = true);
|
||||
|
|
|
|||
|
|
@ -23,9 +23,10 @@ Image DiagramEditor::MakeIcon(DiagramItem& m, Size isz)
|
|||
};
|
||||
|
||||
IconMaker mk;
|
||||
m.ink = SColorText();
|
||||
mk.dark = IsDarkTheme();
|
||||
mk.m = m;
|
||||
mk.isz = isz;
|
||||
mk.dark = IsDarkContent();
|
||||
return MakeImage(mk);
|
||||
}
|
||||
|
||||
|
|
@ -33,10 +34,16 @@ Image DiagramEditor::ShapeIcon(int i)
|
|||
{
|
||||
Size isz = IconSz();
|
||||
DiagramItem m;
|
||||
m.pt[0] = Point(2, 2);
|
||||
m.pt[1] = Point(isz.cx - 2, isz.cy - 2);
|
||||
m.width = DPI(1);
|
||||
m.shape = i;
|
||||
if(m.IsLine()) {
|
||||
m.pos = Point(2, 2);
|
||||
m.size = isz - 4;
|
||||
}
|
||||
else {
|
||||
m.pos = Pointf(Point(isz)) / 2;
|
||||
m.size = m.pos - 2;
|
||||
}
|
||||
m.width = DPI(1);
|
||||
m.paper = Null;
|
||||
return MakeIcon(m, isz);
|
||||
}
|
||||
|
|
@ -45,10 +52,11 @@ Image DiagramEditor::CapIcon(int start, int end)
|
|||
{
|
||||
Size isz = IconSz();
|
||||
DiagramItem m;
|
||||
m.pt[0] = Point(DPI(6), isz.cy / 2);
|
||||
m.pt[1] = Point(isz.cx - DPI(6), isz.cy / 2);
|
||||
m.pos = Point(findarg(start, DiagramItem::CAP_CIRCLEL, DiagramItem::CAP_DISCL) >= 0 ? DPI(6) : DPI(4), isz.cy / 2);
|
||||
m.size = Size(isz.cx - (findarg(end, DiagramItem::CAP_CIRCLEL, DiagramItem::CAP_DISCL) >= 0 ? DPI(10) :
|
||||
findarg(end, DiagramItem::CAP_CIRCLE, DiagramItem::CAP_DISC) >= 0 ? DPI(8) : DPI(4)), 0);
|
||||
m.shape = DiagramItem::SHAPE_LINE;
|
||||
m.width = DPI(2);
|
||||
m.width = DPI(1);
|
||||
m.cap[0] = start;
|
||||
m.cap[1] = end;
|
||||
return MakeIcon(m, isz);
|
||||
|
|
@ -57,7 +65,7 @@ Image DiagramEditor::CapIcon(int start, int end)
|
|||
Image DiagramEditor::DashIcon(int i)
|
||||
{
|
||||
return MakeValue(
|
||||
[=] { return String((char *)&i, sizeof(i)); },
|
||||
[=] { return String((char *)&i, sizeof(i)) + String("D", (int)IsDarkTheme()); },
|
||||
[=](Value& v) {
|
||||
Size isz = IconSz();
|
||||
ImagePainter p(isz);
|
||||
|
|
@ -69,7 +77,7 @@ Image DiagramEditor::DashIcon(int i)
|
|||
p.Move(DPI(2), isz.cy / 2 - (i & 1) * 0.5)
|
||||
.RelLine(isz.cx - DPI(4), 0)
|
||||
.Dash(h, 0)
|
||||
.Stroke(DPI(2), dark ? White() : Black());
|
||||
.Stroke(DPI(2), SColorText());
|
||||
Image m = p;
|
||||
v = m;
|
||||
return m.GetLength() * sizeof(RGBA);
|
||||
|
|
@ -80,15 +88,14 @@ Image DiagramEditor::DashIcon(int i)
|
|||
Image DiagramEditor::WidthIcon(int i)
|
||||
{
|
||||
return MakeValue(
|
||||
[=] { return String((char *)&i, sizeof(i)); },
|
||||
[=] { return String((char *)&i, sizeof(i)) + String("D", (int)IsDarkTheme()); },
|
||||
[=](Value& v) {
|
||||
Size isz = IconSz();
|
||||
Color ink = dark ? White() : Black();
|
||||
ImagePainter p(isz);
|
||||
p.Clear();
|
||||
p.Move(DPI(2), isz.cy / 2.0 - (i & 1) * 0.5)
|
||||
.RelLine(isz.cx - DPI(4), 0)
|
||||
.Stroke(i, ink);
|
||||
.Stroke(i, SColorText());
|
||||
Image m = p;
|
||||
v = m;
|
||||
return m.GetLength() * sizeof(RGBA);
|
||||
|
|
@ -156,8 +163,11 @@ void Upp::DiagramEditor::DropColumns::Paint(Draw& w, const Rect& r, const Value&
|
|||
DiagramEditor::DropColumns::DropColumns()
|
||||
{
|
||||
AddButton().Main().WhenPush << [=] {
|
||||
SetData(popup.Execute(GetScreenRect(), this));
|
||||
Action();
|
||||
int c = popup.Execute(GetScreenRect(), this);
|
||||
if(IsNull(c))
|
||||
return;
|
||||
SetData(c);
|
||||
UpdateAction();
|
||||
};
|
||||
SetDisplay(*this);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,9 +27,10 @@ Point DiagramEditor::GetHandle(int i, Point p_) const
|
|||
if(i >= 0) {
|
||||
const DiagramItem& m = data.item[i];
|
||||
if(m.IsLine()) {
|
||||
if(Distance(m.pt[0], p) < 6)
|
||||
double r = (m.width + 12) / 2 - 1;
|
||||
if(Distance(m.pos, p) <= r)
|
||||
return Point(-1, -1);
|
||||
if(Distance(m.pt[1], p) < 6)
|
||||
if(Distance(m.pos + m.size, p) <= r)
|
||||
return Point(1, 1);
|
||||
|
||||
}
|
||||
|
|
@ -97,19 +98,19 @@ Image DiagramEditor::CursorImage(Point p, dword keyflags)
|
|||
if(IsNull(h))
|
||||
return Image::Arrow();
|
||||
|
||||
if(HasCapture() && i >= 0 && data.item[i].IsLine())
|
||||
if(HasCapture() && IsCursor() && CursorItem().IsLine())
|
||||
return Image::Arrow();
|
||||
|
||||
int m = h.x * h.y;
|
||||
if((h.x || h.y) && i >= 0 && data.item[i].IsLine())
|
||||
return Image::SizeAll();
|
||||
if((h.x || h.y) && IsCursor() && CursorItem().IsLine())
|
||||
return DiagramImg::LineCursor();
|
||||
|
||||
if(h.x == -1 && h.y == 1)
|
||||
return DiagramImg::RotateCursor();
|
||||
|
||||
double rot;
|
||||
if(m > 0)
|
||||
rot = - M_PI / 4;
|
||||
rot = -M_PI / 4;
|
||||
else
|
||||
if(m < 0)
|
||||
rot = M_PI / 4;
|
||||
|
|
@ -160,7 +161,13 @@ void DiagramEditor::MouseWheel(Point, int zdelta, dword keyflags) {
|
|||
if(keyflags & K_ALT) {
|
||||
if(IsCursor()) {
|
||||
DiagramItem& m = CursorItem();
|
||||
m.rotate = ((int(m.rotate) + sgn(zdelta) * 15) / 15 * 15) % 360;
|
||||
if(m.IsLine()) {
|
||||
int angle = int(Bearing(m.size) * 180 / M_PI);
|
||||
angle = ((angle + 360 + sgn(zdelta) * 15) / 15 * 15) % 360;
|
||||
m.size = Length(m.size) * Polar(angle * M_PI / 180);
|
||||
}
|
||||
else
|
||||
m.rotate = ((int(m.rotate) + sgn(zdelta) * 15) / 15 * 15) % 360;
|
||||
Commit();
|
||||
Sync();
|
||||
}
|
||||
|
|
@ -183,10 +190,17 @@ void DiagramEditor::LeftDouble(Point p, dword keyflags)
|
|||
StartText();
|
||||
}
|
||||
|
||||
void DiagramEditor::Grid(int shape, Point& p)
|
||||
void DiagramEditor::Grid(Point& p)
|
||||
{
|
||||
if(grid && !GetShift())
|
||||
p = shape == DiagramItem::SHAPE_LINE ? (p + Point(3, 3)) / 8 * 8 : (p + Point(7, 7)) / 16 * 16;
|
||||
p = (p + Point(3, 3)) / 8 * 8;
|
||||
}
|
||||
|
||||
void DiagramEditor::Grid(Pointf& p)
|
||||
{
|
||||
Point pp = p;
|
||||
Grid(pp);
|
||||
p = pp;
|
||||
}
|
||||
|
||||
void DiagramEditor::LeftDown(Point p, dword keyflags)
|
||||
|
|
@ -207,19 +221,8 @@ void DiagramEditor::LeftDown(Point p, dword keyflags)
|
|||
if(sizehandle.x || sizehandle.y)
|
||||
return;
|
||||
|
||||
if(tool >= 0) {
|
||||
KillCursor();
|
||||
DiagramItem& m = AddItem(tl[tool].shape);
|
||||
m = tl[tool];
|
||||
Grid(m, p);
|
||||
m.pt[0] = m.pt[1] = p;
|
||||
m.FixPosition();
|
||||
draghandle = Point(1, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if(IsCursor()) {
|
||||
drag_cp = CursorItem().GetRect().CenterPoint();
|
||||
drag_cp = CursorItem().pos;
|
||||
Point h = GetHandle(cursor, p);
|
||||
if(h.x || h.y) {
|
||||
draghandle = h;
|
||||
|
|
@ -246,10 +249,10 @@ void DiagramEditor::LeftDown(Point p, dword keyflags)
|
|||
else
|
||||
SetCursor(i);
|
||||
if(IsCursor()) {
|
||||
dragfrom = GetCursorRect();
|
||||
dragfrom = CursorItem().pos;
|
||||
sdragfrom.SetCount(sel.GetCount());
|
||||
for(int i = 0; i < sel.GetCount(); i++)
|
||||
sdragfrom[i] = data.item[sel[i]];
|
||||
sdragfrom[i] = data.item[sel[i]].pos;
|
||||
if(sel.GetCount() > 1 || !CursorItem().IsLine())
|
||||
PrepareConns();
|
||||
else
|
||||
|
|
@ -277,22 +280,57 @@ void DiagramEditor::MouseMove(Point p, dword keyflags)
|
|||
|
||||
moved = moved || p != dragstart;
|
||||
|
||||
if(HasCapture() && doselection) { // do rectangular selection
|
||||
dragcurrent = p;
|
||||
Rect r(dragstart, dragcurrent);
|
||||
r.Normalize();
|
||||
sel.Clear();
|
||||
KillCursor();
|
||||
for(int i = 0; i < data.item.GetCount(); i++)
|
||||
if(r.Contains(data.item[i].pt[0]) && r.Contains(data.item[i].pt[1])) {
|
||||
sel.FindAdd(i);
|
||||
SetCursor(i);
|
||||
}
|
||||
if(HasCapture() && IsCursor() && draghandle == Point(999,999) && tool >= 0) { // adding tool based shape
|
||||
DiagramItem& m = CursorItem();
|
||||
Pointf p0 = dragstart;
|
||||
Grid(p0);
|
||||
Pointf p1 = p;
|
||||
Grid(p1);
|
||||
m.size = p1 - p0;
|
||||
if(m.IsLine())
|
||||
m.pos = p0;
|
||||
else {
|
||||
m.size.cx = max(0.5 * m.size.cx, 4.0);
|
||||
m.size.cy = max(0.5 * m.size.cy, 4.0);
|
||||
m.pos = p0 + m.size;
|
||||
ASSERT(m.pos - m.size == p0);
|
||||
m.FixPosition();
|
||||
ASSERT(m.pos - m.size == p0);
|
||||
}
|
||||
m.FixPosition();
|
||||
Sync();
|
||||
return;
|
||||
}
|
||||
if(HasCapture() && (sizehandle.x || sizehandle.y)) {
|
||||
Grid(DiagramItem::SHAPE_RECT, p);
|
||||
if(HasCapture() && doselection) { // do rectangular selection
|
||||
if(tool >= 0) { // start tool
|
||||
if(Distance(dragstart, p) >= 8) {
|
||||
KillCursor();
|
||||
DiagramItem& m = AddItem(tl[tool].shape);
|
||||
m = tl[tool];
|
||||
Grid(p);
|
||||
m.pos = p;
|
||||
m.size = Sizef(8, 8);
|
||||
draghandle = Point(999,999);
|
||||
}
|
||||
return;
|
||||
}
|
||||
dragcurrent = p;
|
||||
Rectf r(dragstart, dragcurrent);
|
||||
r.Normalize();
|
||||
sel.Clear();
|
||||
KillCursor();
|
||||
for(int i = 0; i < data.item.GetCount(); i++) {
|
||||
Rectf m = data.item[i].GetRect();
|
||||
if(r.Contains(m.TopLeft()) && r.Contains(m.BottomRight())) {
|
||||
sel.FindAdd(i);
|
||||
SetCursor(i);
|
||||
}
|
||||
}
|
||||
Sync();
|
||||
return;
|
||||
}
|
||||
if(HasCapture() && (sizehandle.x || sizehandle.y)) { // resize canvas
|
||||
Grid(p);
|
||||
if(IsNull(data.size))
|
||||
data.size = data.GetSize();
|
||||
if(sizehandle.x)
|
||||
|
|
@ -302,39 +340,49 @@ void DiagramEditor::MouseMove(Point p, dword keyflags)
|
|||
Sync();
|
||||
return;
|
||||
}
|
||||
|
||||
if(HasCapture() && IsCursor() && (moving || Distance(dragstart, p) >= 8)) {
|
||||
moving = true;
|
||||
DiagramItem& m = CursorItem();
|
||||
Pointf p0 = p;
|
||||
Grid(m, p);
|
||||
if(IsNull(draghandle)) { // move selection
|
||||
Rectf to = dragfrom.Offseted(p - dragstart);
|
||||
Pointf tp = to.TopLeft();
|
||||
if(grid)
|
||||
tp = (Point)tp / 16 * 16;
|
||||
Sizef sz = to.GetSize();
|
||||
m.pt[0] = tp;
|
||||
m.pt[1] = tp + sz;
|
||||
Pointf offset = tp - dragfrom.TopLeft();
|
||||
Pointf offset = Point(p - dragstart);
|
||||
Pointf p = dragfrom + offset;
|
||||
Grid(p);
|
||||
offset = p - dragfrom;
|
||||
for(int i = 0; i < sel.GetCount(); i++) {
|
||||
int ii = sel[i];
|
||||
if(ii >= 0 && ii < data.item.GetCount() && i < sdragfrom.GetCount()) {
|
||||
(Point2 &)data.item[ii] = sdragfrom[i].Offseted(offset);
|
||||
data.item[ii].pos = sdragfrom[i] + offset;
|
||||
Grid(data.item[ii].pos);
|
||||
data.item[ii].FixPosition();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
auto Do = [](int h, double& a1, double& a2, double a) {
|
||||
if(h)
|
||||
(h < 0 ? a1 : a2) = a;
|
||||
};
|
||||
Pointf p0 = p;
|
||||
Grid(p);
|
||||
if(!m.IsLine())
|
||||
m.Normalize();
|
||||
Rectf r = m.GetRect();
|
||||
if(m.IsLine()) {
|
||||
Do(draghandle.x, m.pt[0].x, m.pt[1].x, p.x);
|
||||
Do(draghandle.y, m.pt[0].y, m.pt[1].y, p.y);
|
||||
Pointf pf = p;
|
||||
if(!GetShift()) {
|
||||
double d0 = SquaredDistance(p0, pf);
|
||||
for(const DiagramItem& m : data.item)
|
||||
for(Pointf cs : m.GetConnections()) {
|
||||
double d1 = SquaredDistance(p0, cs);
|
||||
if(d1 < d0) {
|
||||
d0 = d1;
|
||||
pf = cs;
|
||||
}
|
||||
}
|
||||
}
|
||||
Pointf p2 = m.pos + m.size;
|
||||
if(draghandle.x < 0)
|
||||
m.pos = pf;
|
||||
else
|
||||
p2 = pf;
|
||||
m.size = p2 - m.pos;
|
||||
}
|
||||
else
|
||||
if(draghandle.x == -1 && draghandle.y == 1) {
|
||||
|
|
@ -345,33 +393,24 @@ void DiagramEditor::MouseMove(Point p, dword keyflags)
|
|||
}
|
||||
else {
|
||||
bool rotated = m.rotate;
|
||||
r -= drag_cp;
|
||||
if(rotated)
|
||||
p = m.Rotation(-1).Transform(Pointf(p) - drag_cp) + drag_cp;
|
||||
Do(draghandle.x, r.left, r.right, p.x);
|
||||
Do(draghandle.y, r.top, r.bottom, p.y);
|
||||
if(m.aspect_ratio && 0) {
|
||||
m.Normalize();
|
||||
Sizef hsz = r.GetSize() / 2;
|
||||
auto Do = [](int h, double& hsz, double a, double cp) {
|
||||
if(h)
|
||||
hsz = abs(a - cp);
|
||||
};
|
||||
Do(draghandle.x, hsz.cx, p.x, drag_cp.x);
|
||||
Do(draghandle.y, hsz.cy, p.y, drag_cp.y);
|
||||
hsz.cx = max(hsz.cx, 8.0);
|
||||
hsz.cy = max(hsz.cy, 8.0);
|
||||
m.size = hsz;
|
||||
if(m.aspect_ratio) {
|
||||
Sizef sz1, sz2;
|
||||
ComputeAspectSize(m, sz1, sz2);
|
||||
Sizef sz;
|
||||
if(draghandle.y == 0)
|
||||
sz = sz1;
|
||||
else
|
||||
if(draghandle.x == 0)
|
||||
sz = sz2;
|
||||
else
|
||||
sz = sz1.cx < sz2.cx ? sz1 : sz2;
|
||||
if(draghandle.x < 0)
|
||||
m.pt[0].x = m.pt[1].x - sz.cx;
|
||||
else
|
||||
m.pt[1].x = m.pt[0].x + sz.cx;
|
||||
if(draghandle.y < 0)
|
||||
m.pt[0].y = m.pt[1].y - sz.cy;
|
||||
else
|
||||
m.pt[1].y = m.pt[0].y + sz.cy;
|
||||
m.size = (draghandle.x && draghandle.y && sz1.cx < sz2.cx || draghandle.x ? sz1 : sz2) / 2;
|
||||
}
|
||||
m.pt[0] = r.TopLeft();
|
||||
m.pt[1] = r.BottomRight();
|
||||
}
|
||||
}
|
||||
UseConns();
|
||||
|
|
@ -380,160 +419,17 @@ void DiagramEditor::MouseMove(Point p, dword keyflags)
|
|||
}
|
||||
}
|
||||
|
||||
void DiagramEditor::LeftUp(Point, dword)
|
||||
void DiagramEditor::LeftUp(Point p, dword flags)
|
||||
{
|
||||
Map(p);
|
||||
if(!moving && !(flags & K_CTRL) && !doselection) {
|
||||
sel.Clear();
|
||||
SetCursor(FindItem(p));
|
||||
}
|
||||
moving = doselection = false;
|
||||
tool = -1;
|
||||
conns.Clear();
|
||||
Sync();
|
||||
Commit();
|
||||
}
|
||||
|
||||
void DiagramEditor::RightDown(Point p, dword keyflags)
|
||||
{
|
||||
Map(p);
|
||||
|
||||
FinishText();
|
||||
|
||||
int ii = FindItem(p);
|
||||
if(ii >= 0) {
|
||||
DiagramItem& m = data.item[ii];
|
||||
if(m.IsLine()) {
|
||||
SetCursor(ii);
|
||||
Point h = GetHandle(cursor, p);
|
||||
if(h.x) {
|
||||
int i = h.x > 0;
|
||||
ColumnPopUp menu;
|
||||
Caps(menu, i == 0);
|
||||
int cap = menu.Execute();
|
||||
if(cap < 0)
|
||||
return;
|
||||
m.cap[i] = cap;
|
||||
GetAttrs();
|
||||
Sync();
|
||||
return;
|
||||
}
|
||||
if(m.IsClick(p, data)) {
|
||||
ColumnPopUp menu;
|
||||
Dashes(menu);
|
||||
menu.count = DiagramItem::DASH_COUNT + 15;
|
||||
menu.columns = 5;
|
||||
menu.WhenPaintItem = [=](Draw& w, Size isz, int ii, bool sel) {
|
||||
PopPaint(w, ii < DiagramItem::DASH_COUNT ? DashIcon(ii) : WidthIcon(ii - DiagramItem::DASH_COUNT), sel);
|
||||
};
|
||||
|
||||
int n = menu.Execute();
|
||||
if(n < 0)
|
||||
return;
|
||||
if(n < DiagramItem::DASH_COUNT)
|
||||
m.dash = n;
|
||||
else
|
||||
m.width = n - DiagramItem::DASH_COUNT;
|
||||
GetAttrs();
|
||||
Sync();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnPopUp shape;
|
||||
Shapes(shape);
|
||||
|
||||
tool = -1;
|
||||
|
||||
int si = shape.Execute();
|
||||
|
||||
if(si < 0)
|
||||
return;
|
||||
|
||||
Sizef size;
|
||||
String mdata;
|
||||
if(si == DiagramItem::SHAPE_SVGPATH) {
|
||||
mdata = SelectFontSymbolSvg(size);
|
||||
if(IsNull(mdata))
|
||||
return;
|
||||
}
|
||||
|
||||
if(si == DiagramItem::SHAPE_IMAGE) {
|
||||
String path = SelectFileOpen("Images (*.png *.gif *.jpg *.bmp *.svg)\t*.png *.gif *.jpg *.bmp *.svg");
|
||||
|
||||
if(GetFileLength(path) > 17000000) {
|
||||
Exclamation("Image is too large!");
|
||||
return;
|
||||
}
|
||||
mdata = LoadFile(path);
|
||||
if(IsNull(mdata))
|
||||
return;
|
||||
size = Null;
|
||||
if(IsSVG(mdata)) {
|
||||
Rectf f = GetSVGBoundingBox(mdata);
|
||||
size = f.GetSize();
|
||||
}
|
||||
else {
|
||||
StringStream ss(mdata);
|
||||
One<StreamRaster> r = StreamRaster::OpenAny(ss);
|
||||
if(r)
|
||||
size = r->GetSize();
|
||||
}
|
||||
if(IsNull(size)) {
|
||||
Exclamation(Format(t_("Unsupported image format in file [* \1%s\1]."), path));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
CancelSelection();
|
||||
|
||||
Point p0 = p;
|
||||
Grid(si, p);
|
||||
Pointf cp = Null; // connect line with nearest connection point
|
||||
if(si == DiagramItem::SHAPE_LINE) {
|
||||
double mind = DBL_MAX;
|
||||
for(const DiagramItem& m : data.item)
|
||||
for(Pointf c : m.GetConnections()) {
|
||||
double d = Squared(c - (Pointf)p0);
|
||||
if(d < mind) {
|
||||
cp = c;
|
||||
mind = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Size sz;
|
||||
|
||||
DiagramItem& m = AddItem(si);
|
||||
if(mdata.GetCount())
|
||||
m.blob_id = data.AddBlob(mdata);
|
||||
m.shape = si; // shape must be set before SetAttrs to avoid Normalise
|
||||
Sizef szf = m.GetStdSize(data);
|
||||
if(IsNull(cp)) {
|
||||
m.pt[0] = p;
|
||||
m.pt[1] = p + szf;
|
||||
}
|
||||
else {
|
||||
m.pt[0] = cp;
|
||||
m.pt[1] = p;
|
||||
}
|
||||
if(si == DiagramItem::SHAPE_IMAGE) {
|
||||
m.ink = Null;
|
||||
m.paper = Black();
|
||||
m.width = 0;
|
||||
SetAttrs(ATTR_ALL & ~(ATTR_SHAPE|ATTR_PAPER|ATTR_INK|ATTR_WIDTH));
|
||||
}
|
||||
else
|
||||
if(si == DiagramItem::SHAPE_SVGPATH) {
|
||||
m.ink = Null;
|
||||
m.paper = Black();
|
||||
m.width = 0;
|
||||
SetAttrs(ATTR_ALL & ~(ATTR_SHAPE|ATTR_PAPER|ATTR_INK));
|
||||
}
|
||||
else
|
||||
SetAttrs(ATTR_ALL & ~ATTR_SHAPE);
|
||||
Sync();
|
||||
}
|
||||
|
||||
void DiagramEditor::RightUp(Point, dword keyflags)
|
||||
{
|
||||
Commit();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -142,8 +142,9 @@ void DiagramEditor::Align(bool horz, int align)
|
|||
if(ii != cursor || align == ALIGN_NULL) {
|
||||
DiagramItem& m = data.item[ii];
|
||||
m.Normalize();
|
||||
Pointf& p1 = m.pt[0];
|
||||
Pointf& p2 = m.pt[1];
|
||||
Rectf r = m.GetRect();
|
||||
Pointf p1 = r.TopLeft();
|
||||
Pointf p2 = r.BottomRight();
|
||||
double sz = abs(HoVe(p2) - HoVe(p1));
|
||||
if(align == ALIGN_LEFT) {
|
||||
HoVe(p1) = HoVe(cp1);
|
||||
|
|
@ -163,6 +164,14 @@ void DiagramEditor::Align(bool horz, int align)
|
|||
HoVe(p1) = (dsz - csz) / 2;
|
||||
HoVe(p2) = HoVe(p1) + csz;
|
||||
}
|
||||
if(m.IsLine()) {
|
||||
m.pos = p1;
|
||||
m.size = p2 - p1;
|
||||
}
|
||||
else {
|
||||
m.pos = (p1 + p2) / 2;
|
||||
m.size = Pointf(abs(p1.x - p2.x), abs(p1.y - p2.y)) / 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
UseConns();
|
||||
|
|
@ -185,8 +194,8 @@ void DiagramEditor::PrepareConns()
|
|||
for(int i = 0; i < data.item.GetCount(); i++) {
|
||||
const DiagramItem& m = data.item[i];
|
||||
if(m.IsLine()) {
|
||||
for(int j = 0; j < 2; j++) {
|
||||
auto *q = map.FindPtr(m.pt[j]);
|
||||
auto Add = [&](Pointf p, int j) {
|
||||
auto *q = map.FindPtr(p);
|
||||
if(q) {
|
||||
for(auto w : *q) {
|
||||
Cn& c = conns.Add();
|
||||
|
|
@ -196,7 +205,9 @@ void DiagramEditor::PrepareConns()
|
|||
c.pi = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
Add(m.pos, 0);
|
||||
Add(m.pos + m.size, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -204,17 +215,24 @@ void DiagramEditor::PrepareConns()
|
|||
void DiagramEditor::UseConns()
|
||||
{
|
||||
for(const Cn& cn: conns)
|
||||
if(sel.Find(cn.li) < 0 && sel.Find(cn.mi) >= 0)
|
||||
data.item[cn.li].pt[cn.pi] = data.item[cn.mi].GetConnections()[cn.ci];
|
||||
if(sel.Find(cn.li) < 0 && sel.Find(cn.mi) >= 0) {
|
||||
Pointf pt[2];
|
||||
DiagramItem& m = data.item[cn.li];
|
||||
pt[0] = m.pos;
|
||||
pt[1] = pt[0] + m.size;
|
||||
pt[cn.pi] = data.item[cn.mi].GetConnections()[cn.ci];
|
||||
m.pos = pt[0];
|
||||
m.size = pt[1] - m.pos;
|
||||
}
|
||||
}
|
||||
|
||||
void DiagramEditor::ComputeAspectSize(DiagramItem& m, Sizef& sz1, Sizef& sz2)
|
||||
void DiagramEditor::ComputeAspectSize(DiagramItem& m, Sizef& sz_cx, Sizef& sz_cy)
|
||||
{
|
||||
m.Normalize();
|
||||
Sizef sz = m.GetRect().GetSize();
|
||||
Sizef sz0 = m.GetStdSize(data);
|
||||
sz1 = Sizef(max(sz.cx, 8.0), max(sz0.cy * sz.cx / sz0.cx, 8.0));
|
||||
sz2 = Sizef(max(sz0.cx * sz.cy / sz0.cy, 8.0), max(sz.cy, 8.0));
|
||||
sz_cx = Sizef(max(sz.cx, 8.0), max(sz0.cy * sz.cx / sz0.cx, 8.0));
|
||||
sz_cy = Sizef(max(sz0.cx * sz.cy / sz0.cy, 8.0), max(sz.cy, 8.0));
|
||||
}
|
||||
|
||||
struct SizeDlg : WithSizeLayout<TopWindow> {
|
||||
|
|
|
|||
154
uppsrc/RichEdit/DiagramRight.cpp
Normal file
154
uppsrc/RichEdit/DiagramRight.cpp
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
#include "RichEdit.h"
|
||||
|
||||
namespace Upp {
|
||||
|
||||
void DiagramEditor::RightDown(Point p, dword keyflags)
|
||||
{
|
||||
Map(p);
|
||||
|
||||
FinishText();
|
||||
|
||||
int ii = FindItem(p);
|
||||
if(ii >= 0) {
|
||||
DiagramItem& m = data.item[ii];
|
||||
if(m.IsLine()) {
|
||||
SetCursor(ii);
|
||||
Point h = GetHandle(cursor, p);
|
||||
if(h.x) {
|
||||
int i = h.x > 0;
|
||||
ColumnPopUp menu;
|
||||
Caps(menu, i == 0);
|
||||
int cap = menu.Execute();
|
||||
if(cap < 0)
|
||||
return;
|
||||
m.cap[i] = cap;
|
||||
GetAttrs();
|
||||
Sync();
|
||||
return;
|
||||
}
|
||||
if(m.IsClick(p, data)) {
|
||||
ColumnPopUp menu;
|
||||
Dashes(menu);
|
||||
menu.count = DiagramItem::DASH_COUNT + 15;
|
||||
menu.columns = 5;
|
||||
menu.WhenPaintItem = [=](Draw& w, Size isz, int ii, bool sel) {
|
||||
PopPaint(w, ii < DiagramItem::DASH_COUNT ? DashIcon(ii) : WidthIcon(ii - DiagramItem::DASH_COUNT), sel);
|
||||
};
|
||||
|
||||
int n = menu.Execute();
|
||||
if(n < 0)
|
||||
return;
|
||||
if(n < DiagramItem::DASH_COUNT)
|
||||
m.dash = n;
|
||||
else
|
||||
m.width = n - DiagramItem::DASH_COUNT;
|
||||
GetAttrs();
|
||||
Sync();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tool = -1;
|
||||
SetBar();
|
||||
|
||||
ColumnPopUp shape;
|
||||
Shapes(shape);
|
||||
|
||||
int si = shape.Execute();
|
||||
|
||||
if(si < 0)
|
||||
return;
|
||||
|
||||
Sizef size;
|
||||
String mdata;
|
||||
if(si == DiagramItem::SHAPE_SVGPATH) {
|
||||
mdata = SelectFontSymbolSvg(size);
|
||||
if(IsNull(mdata))
|
||||
return;
|
||||
}
|
||||
|
||||
if(si == DiagramItem::SHAPE_IMAGE) {
|
||||
String path = SelectFileOpen("Images (*.png *.gif *.jpg *.bmp *.svg)\t*.png *.gif *.jpg *.bmp *.svg");
|
||||
|
||||
if(GetFileLength(path) > 17000000) {
|
||||
Exclamation("Image is too large!");
|
||||
return;
|
||||
}
|
||||
mdata = LoadFile(path);
|
||||
if(IsNull(mdata))
|
||||
return;
|
||||
bool loaded = false;
|
||||
if(IsSVG(mdata)) {
|
||||
loaded = true;
|
||||
}
|
||||
else {
|
||||
StringStream ss(mdata);
|
||||
One<StreamRaster> r = StreamRaster::OpenAny(ss);
|
||||
loaded = true;
|
||||
}
|
||||
if(IsNull(size)) {
|
||||
Exclamation(t_("Unsupported image format."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
CancelSelection();
|
||||
|
||||
Point p0 = p;
|
||||
Grid(p);
|
||||
Pointf cp = Null; // connect line with nearest connection point
|
||||
if(si == DiagramItem::SHAPE_LINE) {
|
||||
double mind = DBL_MAX;
|
||||
for(const DiagramItem& m : data.item)
|
||||
for(Pointf c : m.GetConnections()) {
|
||||
double d = Squared(c - (Pointf)p0);
|
||||
if(d < mind) {
|
||||
cp = c;
|
||||
mind = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Size sz;
|
||||
|
||||
DiagramItem& m = AddItem(si);
|
||||
if(mdata.GetCount())
|
||||
m.blob_id = data.AddBlob(mdata);
|
||||
m.shape = si; // shape must be set before SetAttrs to avoid Normalise
|
||||
Sizef szf = m.GetStdSize(data);
|
||||
while(max(szf.cx, szf.cy) > 1000)
|
||||
szf *= 0.5;
|
||||
if(IsNull(cp)) {
|
||||
m.pos = p;
|
||||
m.size = szf / 2;
|
||||
}
|
||||
else {
|
||||
m.pos = cp;
|
||||
m.size = Pointf(p) - cp;
|
||||
}
|
||||
if(si == DiagramItem::SHAPE_IMAGE) {
|
||||
m.ink = Null;
|
||||
m.paper = Black();
|
||||
m.width = 0;
|
||||
SetAttrs(ATTR_ALL & ~(ATTR_SHAPE|ATTR_PAPER|ATTR_INK|ATTR_WIDTH));
|
||||
}
|
||||
else
|
||||
if(si == DiagramItem::SHAPE_SVGPATH) {
|
||||
m.ink = Null;
|
||||
m.paper = Black();
|
||||
m.width = 0;
|
||||
SetAttrs(ATTR_ALL & ~(ATTR_SHAPE|ATTR_PAPER|ATTR_INK));
|
||||
}
|
||||
else
|
||||
SetAttrs(ATTR_ALL & ~ATTR_SHAPE);
|
||||
Sync();
|
||||
GetAttrs();
|
||||
}
|
||||
|
||||
void DiagramEditor::RightUp(Point, dword keyflags)
|
||||
{
|
||||
Commit();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#include "RichEdit.h"
|
||||
|
||||
namespace Upp {
|
||||
|
||||
|
||||
bool DiaRichEdit::Key(dword key, int count)
|
||||
{
|
||||
if(key == K_ENTER) {
|
||||
|
|
@ -18,8 +18,26 @@ bool DiaRichEdit::Key(dword key, int count)
|
|||
return RichEdit::Key(key, count);
|
||||
}
|
||||
|
||||
void DiaRichEdit::PasteFilter(RichText& txt, const String& fmt)
|
||||
{
|
||||
if(GetLength() + txt.GetLength() > 2000) {
|
||||
txt.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
struct RichTextRemoveObjects : RichText::UpdateIterator {
|
||||
virtual int operator()(int pos, RichPara& para) {
|
||||
para.part.RemoveIf([&](int i) { return para[i].object; });
|
||||
return UPDATE;
|
||||
}
|
||||
} h;
|
||||
|
||||
txt.Iterate(h);
|
||||
}
|
||||
|
||||
void DiagramEditor::SyncEditor()
|
||||
{
|
||||
text_editor.AllowObjects(false);
|
||||
text_editor.AllowDarkContent(allow_dark_content);
|
||||
text_editor.DarkContent(dark_content);
|
||||
if(edit_text && cursor >= 0) {
|
||||
|
|
@ -67,7 +85,8 @@ void DiagramEditor::StartText()
|
|||
edit_text = true;
|
||||
Sync();
|
||||
text_editor.SetFocus();
|
||||
text_editor.SetQTF("[= " + CursorItem().qtf);
|
||||
const String& qtf = CursorItem().qtf;
|
||||
text_editor.SetQTF(qtf.GetCount() ? qtf : "[= ");
|
||||
text_editor.Select(0, text_editor.GetLength());
|
||||
SyncEditorRect();
|
||||
}
|
||||
|
|
@ -76,8 +95,26 @@ void DiagramEditor::FinishText()
|
|||
{
|
||||
if(edit_text && cursor >= 0)
|
||||
CursorItem().qtf = AsQTF(text_editor.Get(), CHARSET_UTF8, QTF_BODY|QTF_NOCHARSET|QTF_NOLANG|QTF_NOSTYLES);
|
||||
|
||||
edit_text = false;
|
||||
Sync();
|
||||
}
|
||||
|
||||
RichText::FormatInfo DiagramEditor::GetFormatInfo(int itemi) const
|
||||
{
|
||||
RichText text = ParseQTF(data.item[itemi].qtf);
|
||||
return text.GetFormatInfo(0, text.GetLength());
|
||||
}
|
||||
|
||||
RichText::FormatInfo DiagramEditor::GetSelectionFormatInfo() const
|
||||
{
|
||||
RichText::FormatInfo fi;
|
||||
if(cursor >= 0)
|
||||
fi = GetFormatInfo(cursor);
|
||||
for(int ci : sel)
|
||||
if(ci != cursor)
|
||||
fi.Combine(GetFormatInfo(ci));
|
||||
return fi;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -555,7 +555,7 @@ void RichEdit::SpellCheck()
|
|||
|
||||
void RichEdit::SerializeSettings(Stream& s)
|
||||
{
|
||||
int version = 3;
|
||||
int version = 4;
|
||||
s / version;
|
||||
s % unit;
|
||||
s % showcodes;
|
||||
|
|
@ -575,6 +575,8 @@ void RichEdit::SerializeSettings(Stream& s)
|
|||
StyleKey& k = stylekey[i];
|
||||
s % k.styleid % k.stylename % k.face % k.height % k.ink % k.paper;
|
||||
}
|
||||
if(version >= 4)
|
||||
s % diagram_editor_settings % diagram_editor_placement;
|
||||
}
|
||||
|
||||
void RichEdit::Reset()
|
||||
|
|
@ -744,6 +746,26 @@ RichEdit& RichEdit::OverridePaper(Color p)
|
|||
return *this;
|
||||
}
|
||||
|
||||
bool RichEdit::EditDiagram(RichObject& o)
|
||||
{
|
||||
TopWindow app;
|
||||
app.Icon(DiagramImg::Diagram());
|
||||
app.Title("Diagram");
|
||||
app.Sizeable().Zoomable();
|
||||
DiagramEditor de;
|
||||
String s = ~o.GetData();
|
||||
if(s.GetCount())
|
||||
de.Load(ZDecompress(~o.GetData()));
|
||||
LoadFromString([&](Stream& s) { de.SerializeSettings(s); }, diagram_editor_settings);
|
||||
LoadFromString([&](Stream& s) { app.SerializePlacement(s); }, diagram_editor_placement);
|
||||
app << de.SizePos();
|
||||
app.Execute();
|
||||
o = RichObject("qdf", ZCompress(de.Save()));
|
||||
diagram_editor_settings = StoreAsString([&](Stream& s) { de.SerializeSettings(s); });
|
||||
diagram_editor_placement = StoreAsString([&](Stream& s) { app.SerializePlacement(s); });
|
||||
return true;
|
||||
}
|
||||
|
||||
RichEdit::RichEdit()
|
||||
{
|
||||
floating_zoom = Null;
|
||||
|
|
|
|||
|
|
@ -29,6 +29,11 @@ void RichEdit::ApplyFormat(dword charvalid, dword paravalid)
|
|||
RichText::FormatInfo f = formatinfo;
|
||||
f.charvalid = charvalid;
|
||||
f.paravalid = paravalid;
|
||||
if(diagram_bar_hack) {
|
||||
formatinfo = f;
|
||||
WhenSel();
|
||||
return;
|
||||
}
|
||||
if(objectpos >= 0) {
|
||||
ModifyFormat(objectpos, f, 1);
|
||||
Finish();
|
||||
|
|
|
|||
|
|
@ -307,8 +307,10 @@ void RichEdit::StdBar(Bar& menu)
|
|||
ObjectTool(menu);
|
||||
}
|
||||
}
|
||||
LoadImageTool(menu);
|
||||
InsertDiagramTool(menu);
|
||||
if(allow_objects) {
|
||||
LoadImageTool(menu);
|
||||
InsertDiagramTool(menu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -372,21 +374,15 @@ void RichEdit::LeftDouble(Point p, dword flags)
|
|||
if(objectpos == c) {
|
||||
RichObject object = GetObject();
|
||||
Size osz = object.GetSize();
|
||||
Sizef pxsz = object.GetPixelSize();
|
||||
if(!object) return;
|
||||
if(object.GetTypeName() == "qdf") {
|
||||
TopWindow app;
|
||||
app.Icon(DiagramImg::Diagram());
|
||||
app.Title("Diagram");
|
||||
app.Sizeable().Zoomable();
|
||||
DiagramEditor de;
|
||||
de.Load(ZDecompress(~object.GetData()));
|
||||
app.Add(de.SizePos());
|
||||
app.Execute();
|
||||
RichText clip;
|
||||
RichPara p;
|
||||
RichObject o = RichObject("qdf", ZCompress(de.Save()));
|
||||
o.InitSize(osz.cx, osz.cy);
|
||||
ReplaceObject(o);
|
||||
RichObject o = object;
|
||||
if(EditDiagram(o)) {
|
||||
Sizef sz = osz / pxsz * o.GetPixelSize();
|
||||
o.SetSize(Size(max((int)round(sz.cx), 1), max((int)round(sz.cy), 1)));
|
||||
ReplaceObject(o);
|
||||
}
|
||||
}
|
||||
else {
|
||||
RichObject o = object;
|
||||
|
|
|
|||
|
|
@ -310,6 +310,7 @@ private:
|
|||
|
||||
PaintInfo paint_info;
|
||||
bool ignore_physical_size;
|
||||
bool allow_objects = true;
|
||||
|
||||
bool pixel_mode = false;
|
||||
bool dark_content = false;
|
||||
|
|
@ -318,6 +319,8 @@ private:
|
|||
bool show_zoom = false;
|
||||
|
||||
Color override_paper = Null;
|
||||
|
||||
bool diagram_bar_hack = false; // if true, calls WhenSel in ApplyFormat
|
||||
|
||||
static int fh[];
|
||||
|
||||
|
|
@ -478,6 +481,9 @@ private:
|
|||
|
||||
RichPara::CharFormat last_format;
|
||||
Image last_format_img;
|
||||
|
||||
String diagram_editor_settings;
|
||||
String diagram_editor_placement;
|
||||
|
||||
Size GetZoomedPage() const;
|
||||
int GetPosY(PageY py) const;
|
||||
|
|
@ -661,6 +667,8 @@ private:
|
|||
|
||||
Size GetPhysicalSize(const RichObject& obj);
|
||||
|
||||
bool EditDiagram(RichObject& o);
|
||||
|
||||
struct DisplayDefault : public Display {
|
||||
virtual void Paint(Draw& w, const Rect& r, const Value& q,
|
||||
Color ink, Color paper, dword style) const;
|
||||
|
|
@ -675,6 +683,7 @@ private:
|
|||
friend class StyleKeysDlg;
|
||||
friend class StyleManager;
|
||||
friend class ParaFormatting;
|
||||
friend class DiagramEditor;
|
||||
|
||||
using Ctrl::Accept;
|
||||
|
||||
|
|
@ -860,6 +869,7 @@ public:
|
|||
RichEdit& DarkContent(bool b = true);
|
||||
RichEdit& AllowDarkContent(bool b = true);
|
||||
RichEdit& OverridePaper(Color p);
|
||||
RichEdit& AllowObjects(bool b) { allow_objects = b; return *this; }
|
||||
|
||||
struct UndoInfo {
|
||||
int undoserial;
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ file
|
|||
DiagramEditor.cpp,
|
||||
DiagramIcon.cpp,
|
||||
DiagramMouse.cpp,
|
||||
DiagramRight.cpp,
|
||||
DiagramOps.cpp,
|
||||
DiagramBar.cpp,
|
||||
DiagramText.cpp,
|
||||
|
|
|
|||
|
|
@ -38,44 +38,47 @@ void DiagramItem::Reset()
|
|||
dash = 0;
|
||||
}
|
||||
|
||||
void Point2::Normalize()
|
||||
void DiagramItem::Serialize(Stream& s)
|
||||
{ // used for undo/redo mostly
|
||||
s % pos
|
||||
% size
|
||||
% shape
|
||||
% qtf
|
||||
% width
|
||||
% ink
|
||||
% paper
|
||||
% blob_id
|
||||
% flip_horz
|
||||
% flip_vert
|
||||
% aspect_ratio
|
||||
% rotate
|
||||
% cap[0]
|
||||
% cap[1];
|
||||
}
|
||||
|
||||
void DiagramItem::Normalize()
|
||||
{
|
||||
if(pt[0].x > pt[1].x)
|
||||
Swap(pt[0].x, pt[1].x);
|
||||
if(pt[0].y > pt[1].y)
|
||||
Swap(pt[0].y, pt[1].y);
|
||||
if(IsLine())
|
||||
return;
|
||||
size.cx = abs(size.cx);
|
||||
size.cy = abs(size.cy);
|
||||
}
|
||||
|
||||
void DiagramItem::FixPosition()
|
||||
{
|
||||
double x = min(pt[0].x, pt[1].x);
|
||||
if(x < 0) {
|
||||
pt[0].x -= x;
|
||||
pt[1].x -= x;
|
||||
}
|
||||
double y = min(pt[0].y, pt[1].y);
|
||||
if(y < 0) {
|
||||
pt[0].y -= y;
|
||||
pt[1].y -= y;
|
||||
}
|
||||
auto Clamp = [](Pointf& p) {
|
||||
p.x = clamp(p.x, 0.0, 10000.0);
|
||||
p.y = clamp(p.y, 0.0, 10000.0);
|
||||
};
|
||||
Clamp(pt[0]);
|
||||
Clamp(pt[1]);
|
||||
Normalize();
|
||||
pos.x = clamp(pos.x, 0.0, 100000.0);
|
||||
pos.y = clamp(pos.y, 0.0, 100000.0);
|
||||
if(IsLine())
|
||||
return;
|
||||
if(pt[1].x - pt[0].x < 8)
|
||||
pt[1].x = pt[0].x + 8;
|
||||
if(pt[1].y - pt[0].y < 8)
|
||||
pt[1].y = pt[0].y + 8;
|
||||
size.cx = clamp(size.cx, 4.0, 100000.0);
|
||||
size.cy = clamp(size.cy, 4.0, 100000.0);
|
||||
}
|
||||
|
||||
bool DiagramItem::IsClick(Point p, const Diagram& diagram, bool relaxed) const
|
||||
{
|
||||
if(IsLine())
|
||||
return DistanceFromSegment(p, pt[0], pt[1]) < width + 10;
|
||||
return DistanceFromSegment(p, pos, pos + size) < width + 10;
|
||||
Rectf rect = GetRect();
|
||||
Pointf cp = rect.CenterPoint();
|
||||
if(rotate) {
|
||||
|
|
@ -97,8 +100,8 @@ bool DiagramItem::IsClick(Point p, const Diagram& diagram, bool relaxed) const
|
|||
DiagramItem m = *this;
|
||||
m.paper = Blue();
|
||||
m.ink = Blue();
|
||||
m.pt[0] = Pointf(0, 0);
|
||||
m.pt[1] = Pointf(64, 64);
|
||||
m.pos = Pointf(0, 0);
|
||||
m.size = Sizef(64, 64);
|
||||
m.Paint(p, diagram);
|
||||
Image img = p.GetResult();
|
||||
v = img;
|
||||
|
|
@ -109,6 +112,7 @@ bool DiagramItem::IsClick(Point p, const Diagram& diagram, bool relaxed) const
|
|||
[clamp(int(64 * (p.x - rect.left) / rect.GetWidth()), 0, 63)].a;
|
||||
}
|
||||
|
||||
#if 0
|
||||
bool DiagramItem::IsTextClick(Point p0) const
|
||||
{
|
||||
Zoom zoom = Diagram::TextZoom();
|
||||
|
|
@ -154,13 +158,13 @@ bool DiagramItem::IsTextClick(Point p0) const
|
|||
int pos = txt.GetPos((int)p.x, PageY(0, (int)p.y), page);
|
||||
return pos < txt.GetLength() && txt.GetRichPos(pos).chr != '\n' && txt.GetCaret(pos, page).Contains(p);
|
||||
}
|
||||
#endif
|
||||
|
||||
Rect DiagramItem::GetTextEditRect() const
|
||||
{
|
||||
if(IsLine()) {
|
||||
int d = max(10, int(Distance(pt[0], pt[1]) + 0.5));
|
||||
Point c = (pt[0] + pt[1]) / 2;
|
||||
return Rect(c.x - d / 2, c.y, c.x + d, c.y);
|
||||
int d = max(10, int(Length(size) + 0.5));
|
||||
return Rect(pos.x - d / 2, pos.y, pos.x + d, pos.y);
|
||||
}
|
||||
return GetRect();
|
||||
}
|
||||
|
|
@ -168,7 +172,7 @@ Rect DiagramItem::GetTextEditRect() const
|
|||
void DiagramItem::Save(StringBuffer& r) const
|
||||
{
|
||||
r << Shape[clamp(shape, 0, Shape.GetCount() - 1)] << ' ';
|
||||
r << pt[0].x << ' ' << pt[0].y << ' ' << pt[1].x << ' ' << pt[1].y;
|
||||
r << pos.x << ' ' << pos.y << ' ' << size.cx << ' ' << size.cy;
|
||||
if(qtf.GetCount())
|
||||
r << " " << AsCString(qtf);
|
||||
auto col = [&](Color c) {
|
||||
|
|
@ -208,10 +212,10 @@ void DiagramItem::Load(CParser& p, const Diagram& diagram)
|
|||
if(q < 0)
|
||||
p.ThrowError("Unknown element");
|
||||
shape = q;
|
||||
this->pt[0].x = p.ReadDouble();
|
||||
this->pt[0].y = p.ReadDouble();
|
||||
this->pt[1].x = p.ReadDouble();
|
||||
this->pt[1].y = p.ReadDouble();
|
||||
this->pos.x = p.ReadDouble();
|
||||
this->pos.y = p.ReadDouble();
|
||||
this->size.cx = p.ReadDouble();
|
||||
this->size.cy = p.ReadDouble();
|
||||
auto col = [&] {
|
||||
if(p.Id("null"))
|
||||
return Color(Null);
|
||||
|
|
@ -367,6 +371,11 @@ Rectf Diagram::GetBlobSvgPathBoundingBox(const String& id) const
|
|||
return v.Is<Rectf>() ? (Rectf)v : (Rectf)Null;
|
||||
}
|
||||
|
||||
void Diagram::SweepBlobs(const Index<String>& keep_ids)
|
||||
{
|
||||
blob.RemoveIf([&](int i) { return keep_ids.Find(blob.GetKey(i)) < 0; });
|
||||
}
|
||||
|
||||
void Diagram::Paint(Painter& w, const Diagram::PaintInfo& p) const
|
||||
{
|
||||
w.Begin();
|
||||
|
|
@ -378,7 +387,7 @@ void Diagram::Paint(Painter& w, const Diagram::PaintInfo& p) const
|
|||
if(p.display_grid)
|
||||
for(const DiagramItem& m : item)
|
||||
if(m.IsLine())
|
||||
conn << m.pt[0] << m.pt[1];
|
||||
conn << m.pos << m.pos + m.size;
|
||||
for(int i = 0; i < item.GetCount(); i++) {
|
||||
dword style = 0;
|
||||
if(i == p.cursor)
|
||||
|
|
@ -405,6 +414,7 @@ void Diagram::Serialize(Stream& s)
|
|||
|
||||
void Diagram::Save(StringBuffer& r) const
|
||||
{
|
||||
r << "QDF 1.0;\n";
|
||||
if(!IsNull(size))
|
||||
r << "size " << size.cx << " " << size.cy << ";\n";
|
||||
if(!IsNull(img)) {
|
||||
|
|
@ -440,7 +450,15 @@ void Diagram::Load(CParser& p)
|
|||
{
|
||||
item.Clear();
|
||||
blob.Clear();
|
||||
img.Clear();
|
||||
img_hd = false;
|
||||
size = Null;
|
||||
while(!p.IsEof())
|
||||
if(p.Id("QDF")) {
|
||||
p.ReadDouble();
|
||||
p.PassChar(';');
|
||||
}
|
||||
else
|
||||
if(p.Id("size")) {
|
||||
size.cx = clamp(p.ReadInt(), 1, 10000);
|
||||
size.cy = clamp(p.ReadInt(), 1, 10000);
|
||||
|
|
|
|||
|
|
@ -1,18 +1,9 @@
|
|||
struct Point2 : Moveable<Point2> {
|
||||
Pointf pt[2];
|
||||
|
||||
void Offset(Pointf p) { pt[0] += p; pt[1] += p; }
|
||||
Point2 Offseted(Pointf p) const { Point2 r = *this; r.Offset(p); return r; }
|
||||
void Normalize();
|
||||
Rectf GetRect() const { return Rectf(pt[0], pt[1]).Normalized(); }
|
||||
String ToString() const { return String() << pt[0] << " - " << pt[1]; }
|
||||
|
||||
void Serialize(Stream& s) { s % pt[0] % pt[1]; }
|
||||
};
|
||||
|
||||
struct Diagram;
|
||||
|
||||
struct DiagramItem : Point2 {
|
||||
struct DiagramItem {
|
||||
Pointf pos;
|
||||
Sizef size;
|
||||
|
||||
int shape;
|
||||
String qtf;
|
||||
double width;
|
||||
|
|
@ -34,12 +25,9 @@ struct DiagramItem : Point2 {
|
|||
SHAPE_PARALLELOGRAM,
|
||||
SHAPE_CYLINDER,
|
||||
SHAPE_TRIANGLE,
|
||||
SHAPE_ITRIANGLE,
|
||||
SHAPE_ARROWLEFT,
|
||||
SHAPE_ARROWRIGHT,
|
||||
SHAPE_ARROWHORZ,
|
||||
SHAPE_ARROWDOWN,
|
||||
SHAPE_ARROWUP,
|
||||
SHAPE_ARROWVERT,
|
||||
SHAPE_ARC,
|
||||
|
||||
|
|
@ -55,6 +43,12 @@ struct DiagramItem : Point2 {
|
|||
CAP_DISC,
|
||||
CAP_DIM,
|
||||
CAP_T,
|
||||
|
||||
CAP_ARROWL,
|
||||
CAP_CIRCLEL,
|
||||
CAP_DISCL,
|
||||
CAP_DIML,
|
||||
CAP_TL,
|
||||
|
||||
CAP_COUNT
|
||||
};
|
||||
|
|
@ -73,6 +67,11 @@ struct DiagramItem : Point2 {
|
|||
int cap[2] = { CAP_NONE, CAP_NONE };
|
||||
int dash = 0;
|
||||
|
||||
void Offset(Pointf p) { pos += p; }
|
||||
void Normalize();
|
||||
Rectf GetRect() const { return IsLine() ? Rectf(pos, size) : Rectf(pos - size, pos + size); }
|
||||
String ToString() const { return String() << pos << " " << size; }
|
||||
|
||||
void Paint(Painter& w, const Diagram& diagram, dword style = 0, const Index<Pointf> *conn = nullptr) const;
|
||||
Sizef GetStdSize(const Diagram& diagram) const;
|
||||
|
||||
|
|
@ -90,7 +89,7 @@ struct DiagramItem : Point2 {
|
|||
|
||||
Xform2D Rotation(int d = 1) const { return Xform2D::Rotation(d * M_PI * rotate / 180); }
|
||||
|
||||
void Serialize(Stream& s) { Point2::Serialize(s); s % shape % ink % paper % qtf % width % cap[0] % cap[1] % dash % blob_id % flip_horz % flip_vert % aspect_ratio; }
|
||||
void Serialize(Stream& s);
|
||||
|
||||
void Reset();
|
||||
void Save(StringBuffer& r) const;
|
||||
|
|
@ -124,6 +123,7 @@ struct Diagram {
|
|||
void Paint(Painter& w, const PaintInfo& pi) const;
|
||||
String AddBlob(const String& data);
|
||||
String GetBlob(const String& id) const;
|
||||
void SweepBlobs(const Index<String>& keep_ids);
|
||||
Image GetBlobImage(const String& id) const;
|
||||
Rectf GetBlobSvgPathBoundingBox(const String& id) const;
|
||||
void Serialize(Stream& s);
|
||||
|
|
|
|||
|
|
@ -2,14 +2,13 @@
|
|||
|
||||
namespace Upp {
|
||||
|
||||
Index<String> DiagramItem::LineCap = { "none", "arrow", "circle", "disc", "dim", "T" };
|
||||
Index<String> DiagramItem::LineCap = { "none", "arrow", "circle", "disc", "dim", "T",
|
||||
"arrowL", "circleL", "discL", "dimL", "TL" };
|
||||
|
||||
Index<String> DiagramItem::Shape = { "line", "rect", "round_rect",
|
||||
"ellipse", "diamond", "oval", "parallelogram",
|
||||
"cylinder",
|
||||
"triangle1", "triangle2",
|
||||
"arrow_left", "arrow_right", "arrow_horz",
|
||||
"arrow_down", "arrow_up", "arrow_vert",
|
||||
"cylinder", "triangle",
|
||||
"arrow_right", "arrow_horz", "arrow_down", "arrow_vert",
|
||||
"arc",
|
||||
"svgpath", "image"
|
||||
};
|
||||
|
|
@ -17,20 +16,18 @@ Index<String> DiagramItem::Shape = { "line", "rect", "round_rect",
|
|||
Vector<Pointf> DiagramItem::GetConnections() const
|
||||
{
|
||||
Vector<Pointf> p;
|
||||
if(shape > SHAPE_ITRIANGLE || rotate)
|
||||
if(shape > SHAPE_TRIANGLE)
|
||||
return p;
|
||||
if(IsLine()) {
|
||||
p << pt[0] << pt[1];
|
||||
p << pos << pos + size;
|
||||
return p;
|
||||
}
|
||||
Rectf r = GetRect();
|
||||
p << r.TopCenter() << r.BottomCenter();
|
||||
if(findarg(shape, SHAPE_PARALLELOGRAM, SHAPE_TRIANGLE, SHAPE_ITRIANGLE) < 0)
|
||||
if(findarg(shape, SHAPE_PARALLELOGRAM, SHAPE_TRIANGLE) < 0)
|
||||
p << r.CenterLeft() << r.CenterRight();
|
||||
if(shape == SHAPE_TRIANGLE)
|
||||
p << r.BottomLeft() << r.BottomRight();
|
||||
if(shape == SHAPE_ITRIANGLE)
|
||||
p << r.TopLeft() << r.TopRight();
|
||||
if(rotate) {
|
||||
Xform2D rot = Rotation();
|
||||
Pointf c = r.CenterPoint();
|
||||
|
|
@ -86,83 +83,131 @@ void DiagramItem::Paint(Painter& w, const Diagram& diagram, dword style, const I
|
|||
w.Stroke(0.2, sel1);
|
||||
};
|
||||
|
||||
w.Move(0, 0).EndPath(); // this is to start a new path for every item
|
||||
|
||||
if(IsLine()) {
|
||||
Pointf v = pt[1] - pt[0];
|
||||
if(style) {
|
||||
w.Move(pt[0]).Line(pt[1]).EndPath();
|
||||
w.Move(pos).RelLine(size).EndPath();
|
||||
w.Begin();
|
||||
if((style & EDITOR) && width == 0)
|
||||
w.Dash("5 1").Stroke(1, 100 * sel2);
|
||||
if(style & (Display::CURSOR | Display::SELECT)) {
|
||||
w.LineCap(LINECAP_ROUND).Stroke(width + 12, (style & Display::SELECT ? 30 : 200) * sel2);
|
||||
double r = (width + 12) / 2 - 1;
|
||||
w.Circle(pt[0], r).Fill(sel1);
|
||||
w.Circle(pt[1], r).Fill(sel1);
|
||||
w.Circle(pos, r).Fill(sel1);
|
||||
w.Circle(pos + size, r).Fill(sel1);
|
||||
}
|
||||
w.End();
|
||||
}
|
||||
|
||||
Pointf v = size;
|
||||
double d = Length(v);
|
||||
v = Upp::Normalize(v);
|
||||
|
||||
Pointf a1 = pt[0];
|
||||
Pointf a2 = pt[1];
|
||||
if(d > 4 * width) { // enough length to have caps
|
||||
if(findarg(cap[0], CAP_ARROW, CAP_DIM) >= 0)
|
||||
a1 += v * 4 * width;
|
||||
if(findarg(cap[1], CAP_ARROW, CAP_DIM) >= 0)
|
||||
a2 -= v * 4 * width;
|
||||
|
||||
Pointf a1 = pos;
|
||||
Pointf a2 = pos + size;
|
||||
|
||||
auto CapDef = [&](int i, double& reserve, double& reduce) {
|
||||
reserve = 0;
|
||||
reduce = 0;
|
||||
switch(cap[i]) {
|
||||
case CAP_DIM:
|
||||
case CAP_ARROW:
|
||||
reserve = reduce = 4 * width;
|
||||
break;
|
||||
case CAP_DISC:
|
||||
case CAP_CIRCLE:
|
||||
reserve = 1.5 * width;
|
||||
break;
|
||||
case CAP_DIML:
|
||||
case CAP_ARROWL:
|
||||
reserve = reduce = 12 * width;
|
||||
break;
|
||||
case CAP_DISCL:
|
||||
case CAP_CIRCLEL:
|
||||
reserve = 2.5 * width;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
bool docap[2];
|
||||
double reserve, reduce;
|
||||
double dd = d;
|
||||
CapDef(0, reserve, reduce);
|
||||
docap[0] = dd > reserve;
|
||||
if(docap[0]) {
|
||||
a1 += v * reduce;
|
||||
dd -= reserve;
|
||||
}
|
||||
CapDef(1, reserve, reduce);
|
||||
docap[1] = dd > reserve;
|
||||
if(docap[1])
|
||||
a2 -= v * reduce;
|
||||
|
||||
w.Move(a1).Line(a2);
|
||||
DoDash();
|
||||
Stroke();
|
||||
|
||||
Pointf o = Orthogonal(v);
|
||||
if(d > 4 * width) {
|
||||
auto PaintCap = [&](int k, Pointf p, Pointf a) {
|
||||
Pointf oo = max(3.0, width * 2) * o;
|
||||
switch(k) {
|
||||
case CAP_NONE:
|
||||
w.Circle(p, width / 2).Fill(ink);
|
||||
break;
|
||||
case CAP_T:
|
||||
w.Move(p - 2 * oo).Line(p + 2 * oo).Stroke(1, ink);
|
||||
break;
|
||||
case CAP_DIM:
|
||||
w.Move(p - 2 * oo).Line(p + 2 * oo).Stroke(1, ink);
|
||||
case CAP_ARROW:
|
||||
w.Move(p).Line(a + oo).Line(a - oo).Fill(ink);
|
||||
break;
|
||||
case CAP_DISC:
|
||||
w.Circle(p, 5).Fill(ink);
|
||||
break;
|
||||
case CAP_CIRCLE:
|
||||
w.Circle(p, 5).Fill(paper).Stroke(1, ink);
|
||||
break;
|
||||
}
|
||||
};
|
||||
PaintCap(cap[0], pt[0], a1 + v);
|
||||
PaintCap(cap[1], pt[1], a2 - v);
|
||||
}
|
||||
auto PaintCap = [&](int k, Pointf p, Pointf a) {
|
||||
Pointf oo = max(3.0, width * 2) * o;
|
||||
Pointf ool = max(6.0, width * 4) * o;
|
||||
switch(k) {
|
||||
case CAP_NONE:
|
||||
w.Circle(p, width / 2).Fill(ink);
|
||||
break;
|
||||
case CAP_T:
|
||||
w.Move(p - 2 * oo).Line(p + 2 * oo).Stroke(1, ink);
|
||||
break;
|
||||
case CAP_DIM:
|
||||
w.Move(p - 2 * oo).Line(p + 2 * oo).Stroke(1, ink);
|
||||
case CAP_ARROW:
|
||||
w.Move(p).Line(a + oo).Line(a - oo).Fill(ink);
|
||||
break;
|
||||
case CAP_DISC:
|
||||
w.Circle(p, 1.5 * width).Fill(ink);
|
||||
break;
|
||||
case CAP_CIRCLE:
|
||||
w.Circle(p, 1.5 * width).Fill(paper).Stroke(1, ink);
|
||||
break;
|
||||
case CAP_TL:
|
||||
w.Move(p - 2 * ool).Line(p + 2 * ool).Stroke(1, ink);
|
||||
break;
|
||||
case CAP_DIML:
|
||||
w.Move(p - 2 * ool).Line(p + 2 * ool).Stroke(1, ink);
|
||||
case CAP_ARROWL:
|
||||
w.Move(p).Line(a + ool).Line(a - ool).Fill(ink);
|
||||
break;
|
||||
case CAP_DISCL:
|
||||
w.Circle(p, 2.5 * width).Fill(ink);
|
||||
break;
|
||||
case CAP_CIRCLEL:
|
||||
w.Circle(p, 2.5 * width).Fill(paper).Stroke(1, ink);
|
||||
break;
|
||||
}
|
||||
};
|
||||
if(docap[0])
|
||||
PaintCap(cap[0], pos, a1 + v);
|
||||
if(docap[1])
|
||||
PaintCap(cap[1], pos + size, a2 - v);
|
||||
|
||||
int cx = (int)Distance(pt[0], pt[1]);
|
||||
int cx = (int)d;
|
||||
int txt_cy = txt.GetHeight(pi.zoom, cx);
|
||||
|
||||
w.Begin();
|
||||
double angle = Bearing(pt[1] - pt[0]);
|
||||
double angle = Bearing(size);
|
||||
if(angle >= -M_PI / 2 && angle <= M_PI / 2) {
|
||||
w.Translate(pt[0] - o * (txt_cy + 10));
|
||||
w.Translate(pos - o * (txt_cy + 10));
|
||||
w.Rotate(angle);
|
||||
}
|
||||
else {
|
||||
w.Translate(pt[1] + o * (txt_cy + 10));
|
||||
w.Translate(pos + size + o * (txt_cy + 10));
|
||||
w.Rotate(angle + M_PI);
|
||||
}
|
||||
txt.Paint(w, 0, 0, cx, pi);
|
||||
w.End();
|
||||
}
|
||||
else {
|
||||
Rectf r(pt[0], pt[1]);
|
||||
Rectf r = GetRect();
|
||||
r.Normalize();
|
||||
r.Deflate(width / 2);
|
||||
double w1 = r.GetWidth();
|
||||
|
|
@ -254,20 +299,6 @@ void DiagramItem::Paint(Painter& w, const Diagram& diagram, dword style, const I
|
|||
w.Move(w2, 0).Line(cx, cy).Line(0, cy).Close();
|
||||
}
|
||||
break;
|
||||
case SHAPE_ITRIANGLE: {
|
||||
text_rect.left += int(cx / 4);
|
||||
text_rect.right -= int(cx / 4);
|
||||
text_rect.bottom -= int(cx / 3);
|
||||
w.Move(w2, cy).Line(cx, 0).Line(0, 0).Close();
|
||||
}
|
||||
break;
|
||||
case SHAPE_ARROWLEFT: {
|
||||
double a = 0 + arrow_width;
|
||||
text_rect.left += int(arrow_width / 3);
|
||||
w.Move(0, h2).Line(a, 0).Line(a, h4).Line(cx, h4)
|
||||
.Line(cx, bh4).Line(a, bh4).Line(a, cy).Close();
|
||||
}
|
||||
break;
|
||||
case SHAPE_ARROWRIGHT:
|
||||
{
|
||||
double a = cx - arrow_width;
|
||||
|
|
@ -301,21 +332,6 @@ void DiagramItem::Paint(Painter& w, const Diagram& diagram, dword style, const I
|
|||
.Close();
|
||||
}
|
||||
break;
|
||||
case SHAPE_ARROWUP: {
|
||||
double a = arrow_height;
|
||||
text_rect.left += w4;
|
||||
text_rect.right -= w4;
|
||||
text_rect.top += 3 * arrow_height / 4;
|
||||
w.Move(w2, 0)
|
||||
.Line(cx, a)
|
||||
.Line(cx - w4, a)
|
||||
.Line(cx - w4, cy)
|
||||
.Line(w4, cy)
|
||||
.Line(w4, a)
|
||||
.Line(0, a)
|
||||
.Close();
|
||||
}
|
||||
break;
|
||||
case SHAPE_ARROWDOWN: {
|
||||
double a = cy - arrow_height;
|
||||
text_rect.left += w4;
|
||||
|
|
@ -448,7 +464,7 @@ void DiagramItem::Paint(Painter& w, const Diagram& diagram, dword style, const I
|
|||
|
||||
w.End();
|
||||
|
||||
if((style & GRID) && !rotate)
|
||||
if((style & GRID))
|
||||
for(Pointf p : GetConnections()) {
|
||||
w.Circle(p, 5);
|
||||
if(conn && conn->Find(p) >= 0)
|
||||
|
|
@ -492,9 +508,9 @@ Sizef DiagramItem::GetStdSize(const Diagram& diagram) const
|
|||
}
|
||||
|
||||
if(shape == SHAPE_CYLINDER)
|
||||
return Size(100, 128);
|
||||
return Size(96, 128);
|
||||
|
||||
if(findarg(shape, SHAPE_CYLINDER, SHAPE_ARROWDOWN, SHAPE_ARROWUP, SHAPE_ARROWVERT) >= 0)
|
||||
if(findarg(shape, SHAPE_CYLINDER, SHAPE_ARROWDOWN, SHAPE_ARROWVERT) >= 0)
|
||||
return Size(64, 128);
|
||||
|
||||
return Size(128, 64);
|
||||
|
|
|
|||
|
|
@ -524,7 +524,9 @@ String AsQTF(const RichText& text, byte charset, dword options)
|
|||
lngc.GetAdd(p.format.language, 0)++;
|
||||
}
|
||||
dword lang = lngc.GetCount() ? lngc.GetKey(FindMax(lngc.GetValues())) : 0;
|
||||
qtf << "[";
|
||||
bool bracket = !(options & QTF_NOCHARSET) || lang && !(options & QTF_NOLANG);
|
||||
if(bracket)
|
||||
qtf << "[";
|
||||
if(!(options & QTF_NOCHARSET)) {
|
||||
qtf << "{";
|
||||
if(charset == CHARSET_UTF8)
|
||||
|
|
@ -541,7 +543,8 @@ String AsQTF(const RichText& text, byte charset, dword options)
|
|||
}
|
||||
if(lang && !(options & QTF_NOLANG))
|
||||
qtf << "%" << LNGAsText(SetLNGCharset(lang, CHARSET_DEFAULT));
|
||||
qtf << " ";
|
||||
if(bracket)
|
||||
qtf << " ";
|
||||
if(crlf)
|
||||
qtf << "\r\n";
|
||||
RichStyle defstyle;
|
||||
|
|
@ -549,13 +552,10 @@ String AsQTF(const RichText& text, byte charset, dword options)
|
|||
|
||||
QTFEncodeTxt(qtf, text, text.GetStyles(), defstyle, options, sm, charset, lang);
|
||||
|
||||
qtf << "]";
|
||||
if(bracket)
|
||||
qtf << "]";
|
||||
}
|
||||
|
||||
if(options & QTF_NOSTYLES) // remove redundant []
|
||||
while(qtf.StartsWith("[ ") && qtf.EndsWith("]"))
|
||||
qtf = qtf.Mid(2, qtf.GetCount() - 3);
|
||||
|
||||
return qtf;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -132,6 +132,14 @@ void RichTxt::FormatInfo::Combine(const RichPara::Format& fmt)
|
|||
paravalid &= ~NEWHDRFTR;
|
||||
}
|
||||
|
||||
void RichTxt::FormatInfo::Combine(const FormatInfo& fmt)
|
||||
{
|
||||
Combine((const RichPara::Format&)fmt);
|
||||
Combine((const RichPara::CharFormat&)fmt);
|
||||
paravalid &= fmt.paravalid;
|
||||
charvalid &= fmt.charvalid;
|
||||
}
|
||||
|
||||
void RichTxt::FormatInfo::ApplyTo(RichPara::CharFormat& fmt) const
|
||||
{
|
||||
if(charvalid & BOLD)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ uses
|
|||
RichEdit;
|
||||
|
||||
file
|
||||
main.cpp;
|
||||
main.cpp,
|
||||
app.tpp;
|
||||
|
||||
mainconfig
|
||||
"" = "GUI";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue