mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 14:16:07 -06:00
RichText: Diagram Images
This commit is contained in:
parent
9d477ad65b
commit
62895f4df8
9 changed files with 288 additions and 165 deletions
|
|
@ -165,30 +165,36 @@ String BinDiff(const String& base, const String& data)
|
|||
return diff;
|
||||
}
|
||||
|
||||
void BinUndoRedo::Reset(const String& current)
|
||||
void BinUndoRedo::Reset(const String& current, const String& ids)
|
||||
{
|
||||
undo.Clear();
|
||||
redo.Clear();
|
||||
undosize = 0;
|
||||
commit = current;
|
||||
commit.data = current;
|
||||
commit.ids = ids;
|
||||
}
|
||||
|
||||
bool BinUndoRedo::Commit(const String& current, int limit)
|
||||
bool BinUndoRedo::Commit(const String& current, const String& ids, int limit)
|
||||
{
|
||||
bool ret = false;
|
||||
if(redo.GetCount()) {
|
||||
redo.Clear();
|
||||
ret = true;
|
||||
}
|
||||
if(current != commit) {
|
||||
String u = BinDiff(current, commit);
|
||||
if(current != commit.data || ids != commit.ids) {
|
||||
String u = BinDiff(current, commit.data);
|
||||
while(undo.GetCount() && undosize + u.GetCount() > limit) {
|
||||
undosize -= undo[0].GetCount();
|
||||
undosize -= undo[0].data.GetCount();
|
||||
undosize -= undo[0].ids.GetCount();
|
||||
undo.Remove(0);
|
||||
}
|
||||
undo.Add(u);
|
||||
Entry& e = undo.Add();
|
||||
e.data = u;
|
||||
e.ids = commit.ids;
|
||||
undosize += u.GetCount();
|
||||
commit = current;
|
||||
undosize += ids.GetCount();
|
||||
commit.data = current;
|
||||
commit.ids = ids;
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
|
|
@ -197,10 +203,13 @@ bool BinUndoRedo::Commit(const String& current, int limit)
|
|||
String BinUndoRedo::Undo(const String& current)
|
||||
{
|
||||
if(undo.GetCount()) {
|
||||
String prev = undo.Pop();
|
||||
commit = BinUndiff(commit, prev);
|
||||
redo.Add(BinDiff(commit, current));
|
||||
return commit;
|
||||
Entry prev = undo.Pop();
|
||||
commit.data = BinUndiff(commit.data, prev.data);
|
||||
|
||||
Entry& e = redo.Add();
|
||||
e.data = BinDiff(commit.data, current);
|
||||
e.ids = prev.ids;
|
||||
return commit.data;
|
||||
}
|
||||
return String::GetVoid();
|
||||
}
|
||||
|
|
@ -208,12 +217,23 @@ String BinUndoRedo::Undo(const String& current)
|
|||
String BinUndoRedo::Redo(const String& current)
|
||||
{
|
||||
if(redo.GetCount()) {
|
||||
String next = redo.Pop();
|
||||
commit = BinUndiff(commit, next);
|
||||
undo.Add(BinDiff(commit, current));
|
||||
return commit;
|
||||
Entry next = redo.Pop();
|
||||
commit.data = BinUndiff(commit.data, next.data);
|
||||
Entry& e = undo.Add();
|
||||
e.data = BinDiff(commit.data, current);
|
||||
e.ids = next.ids;
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,16 +2,22 @@ String BinDiff(const String& base, const String& data);
|
|||
String BinUndiff(const String& base, const String& bin_diff);
|
||||
|
||||
class BinUndoRedo {
|
||||
String commit;
|
||||
Vector<String> undo;
|
||||
Vector<String> redo;
|
||||
struct Entry : Moveable<Entry> {
|
||||
String data;
|
||||
String ids;
|
||||
};
|
||||
Entry commit;
|
||||
Vector<Entry> undo;
|
||||
Vector<Entry> redo;
|
||||
int undosize = 0;
|
||||
|
||||
public:
|
||||
void Reset(const String& current);
|
||||
bool Commit(const String& current, int limit = 4096*1024);
|
||||
bool IsUndo() const { return undo.GetCount(); }
|
||||
bool IsRedo() const { return redo.GetCount(); }
|
||||
void Reset(const String& current, const String& ids = String());
|
||||
bool Commit(const String& current, const String& ids, 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(); }
|
||||
String Undo(const String& current);
|
||||
String Redo(const String& current);
|
||||
void Ids(Event<const String&> fn);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -17,8 +17,10 @@ void DiagramEditor::Delete()
|
|||
void DiagramEditor::Copy()
|
||||
{
|
||||
StringBuffer cb;
|
||||
for(int i : sel)
|
||||
data.item[i].Save(cb);
|
||||
for(int i : sel) {
|
||||
const DiagramItem& m = data.item[i];
|
||||
m.Save(cb, m.blob_id);
|
||||
}
|
||||
WriteClipboardText(cb);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -144,7 +144,8 @@ Image DiagramEditor::MakeIcon(DiagramItem& m, Size isz)
|
|||
Image Make() const override {
|
||||
ImagePainter iw(isz);
|
||||
iw.Clear();
|
||||
m.Paint(iw, dark ? DiagramItem::DARK : 0);
|
||||
VectorMap<int, String> data;
|
||||
m.Paint(iw, data, dark ? DiagramItem::DARK : 0);
|
||||
return iw;
|
||||
}
|
||||
};
|
||||
|
|
@ -253,6 +254,7 @@ void DiagramEditor::ResetUndo()
|
|||
|
||||
void DiagramEditor::Commit()
|
||||
{
|
||||
RTIMING("Commit");
|
||||
if(IsCursor()) {
|
||||
DiagramItem& m = CursorItem();
|
||||
if(!m.IsLine())
|
||||
|
|
|
|||
|
|
@ -278,12 +278,11 @@ void DiagramEditor::RightDown(Point p, dword keyflags)
|
|||
Map(p);
|
||||
|
||||
auto PopPaint = [=](Draw& w, const Image& m, bool sel) {
|
||||
int x = DPI(2);
|
||||
int y = DPI(1);
|
||||
Point p = Rect(IconSz()).CenterPos(m.GetSize());
|
||||
if(sel)
|
||||
w.DrawImage(x, y, m, SColorHighlightText());
|
||||
w.DrawImage(p.x, p.y, m, SColorHighlightText());
|
||||
else
|
||||
w.DrawImage(x, y, m);
|
||||
w.DrawImage(p.x, p.y, m);
|
||||
};
|
||||
|
||||
FinishText();
|
||||
|
|
@ -335,7 +334,10 @@ void DiagramEditor::RightDown(Point p, dword keyflags)
|
|||
shape.columns = 3;
|
||||
shape.isz = IconSz() + Size(DPI(4), DPI(4));
|
||||
shape.WhenPaintItem = [=](Draw& w, Size isz, int ii, bool sel) {
|
||||
PopPaint(w, ii == DiagramItem::SHAPE_SVGPATH ? DiagramImg::FontSvg() : ShapeIcon(ii), sel);
|
||||
PopPaint(w, ii == DiagramItem::SHAPE_SVGPATH ? DiagramImg::FontSvg() :
|
||||
ii == DiagramItem::SHAPE_IMAGE ? CtrlImg::open()
|
||||
: ShapeIcon(ii),
|
||||
sel && ii != DiagramItem::SHAPE_IMAGE);
|
||||
};
|
||||
|
||||
tool = -1;
|
||||
|
|
@ -346,12 +348,39 @@ void DiagramEditor::RightDown(Point p, dword keyflags)
|
|||
return;
|
||||
|
||||
Sizef size;
|
||||
String svgpath;
|
||||
String mdata;
|
||||
if(si == DiagramItem::SHAPE_SVGPATH) {
|
||||
svgpath = SelectFontSymbolSvg(size);
|
||||
if(IsNull(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();
|
||||
|
||||
|
|
@ -380,8 +409,17 @@ void DiagramEditor::RightDown(Point p, dword keyflags)
|
|||
m.pt[1] = p;
|
||||
}
|
||||
m.shape = si; // shape must be set before SetAttrs to avoid Normalise
|
||||
m.data = svgpath;
|
||||
if(mdata.GetCount())
|
||||
m.blob_id = data.AddBlob(mdata);
|
||||
m.size = size;
|
||||
if(si == DiagramItem::SHAPE_IMAGE) {
|
||||
m.ink = Null;
|
||||
m.paper = Black();
|
||||
m.width = 0;
|
||||
m.pt[1] = m.pt[0] + size;
|
||||
SetAttrs(ATTR_ALL & ~(ATTR_SHAPE|ATTR_PAPER|ATTR_INK|ATTR_WIDTH));
|
||||
}
|
||||
else
|
||||
if(si == DiagramItem::SHAPE_SVGPATH) {
|
||||
m.ink = Null;
|
||||
m.paper = Black();
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ void SelectSymbolDlg::Variants(int codepoint)
|
|||
{
|
||||
String qtf = "[A5 ";
|
||||
Index<Image> h;
|
||||
svg.Clear();
|
||||
for(int i = 0; i < Font::GetFaceCount(); i++) {
|
||||
Font fnt(i, 10);
|
||||
if(HasCodepoint(fnt, codepoint)) {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ void DiagramItem::Reset()
|
|||
width = 2;
|
||||
ink = Black();
|
||||
paper = White();
|
||||
data.Clear();
|
||||
blob_id = 0;
|
||||
size = Null;
|
||||
|
||||
cap[0] = cap[1] = CAP_NONE;
|
||||
|
|
@ -129,7 +129,7 @@ Rect DiagramItem::GetTextEditRect() const
|
|||
return GetRect();
|
||||
}
|
||||
|
||||
void DiagramItem::Save(StringBuffer& r) const
|
||||
void DiagramItem::Save(StringBuffer& r, int blob_id) const
|
||||
{
|
||||
r << Shape[clamp(shape, 0, Shape.GetCount() - 1)] << ' ';
|
||||
r << pt[0].x << ' ' << pt[0].y << ' ' << pt[1].x << ' ' << pt[1].y;
|
||||
|
|
@ -140,8 +140,8 @@ void DiagramItem::Save(StringBuffer& r) const
|
|||
return String("null");
|
||||
return Format("%02x%02x%02x", (int)c.GetR(), (int)c.GetG(), (int)c.GetB());
|
||||
};
|
||||
if(data.GetCount())
|
||||
r << " data " << AsCString(data);
|
||||
if(blob_id)
|
||||
r << " blob_id " << blob_id;
|
||||
if(!IsNull(size))
|
||||
r << " size " << size.cx << ' ' << size.cy;
|
||||
if(ink != Black())
|
||||
|
|
@ -202,8 +202,8 @@ void DiagramItem::Load(CParser& p)
|
|||
if(p.Id("dash"))
|
||||
dash = clamp(p.ReadInt(), 0, (int)DASH_COUNT);
|
||||
else
|
||||
if(p.Id("data"))
|
||||
data = p.ReadString();
|
||||
if(p.Id("blob_id"))
|
||||
blob_id = p.ReadInt();
|
||||
else
|
||||
if(p.Id("size")) {
|
||||
size.cx = p.ReadDouble();
|
||||
|
|
@ -238,6 +238,19 @@ Size Diagram::GetSize() const
|
|||
return Size(ceil(max(isz.cx, fsz.cx)), ceil(max(isz.cy, fsz.cy)));
|
||||
}
|
||||
|
||||
int Diagram::AddBlob(const String& data_)
|
||||
{
|
||||
int newid = 1;
|
||||
for(const auto& kv : ~blob) {
|
||||
if(kv.value == data_)
|
||||
return kv.key;
|
||||
newid = max(newid, kv.key + 1);
|
||||
}
|
||||
|
||||
blob.Add(newid, data_);
|
||||
return newid;
|
||||
}
|
||||
|
||||
void Diagram::Paint(Painter& w, const Diagram::PaintInfo& p) const
|
||||
{
|
||||
w.Begin();
|
||||
|
|
@ -263,7 +276,7 @@ void Diagram::Paint(Painter& w, const Diagram::PaintInfo& p) const
|
|||
style |= DiagramItem::GRID;
|
||||
if(p.dark)
|
||||
style |= DiagramItem::DARK;
|
||||
item[i].Paint(w, style, &conn);
|
||||
item[i].Paint(w, blob, style, &conn);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -282,15 +295,33 @@ void Diagram::Save(StringBuffer& r) const
|
|||
r << "HD ";
|
||||
r << AsCString(Base64Encode(PNGEncoder().SaveString(img))) << ";\n";
|
||||
}
|
||||
VectorMap<int64, String> sd;
|
||||
for(const DiagramItem& m : item) {
|
||||
m.Save(r);
|
||||
int id = 0;
|
||||
if(m.blob_id) {
|
||||
int q = blob.Find(m.blob_id);
|
||||
if(q >= 0) {
|
||||
id = sd.Find(m.blob_id);
|
||||
if(id < 0) {
|
||||
sd.Add(m.blob_id, blob[q]);
|
||||
id = sd.GetCount();
|
||||
}
|
||||
else
|
||||
id++;
|
||||
}
|
||||
}
|
||||
m.Save(r, id);
|
||||
r << '\n';
|
||||
}
|
||||
|
||||
for(int i = 0; i < sd.GetCount(); i++)
|
||||
r << "blob " << i + 1 << " " << AsCString(Base64Encode(sd[i]), 1000, " ") << ";\n";
|
||||
}
|
||||
|
||||
void Diagram::Load(CParser& p)
|
||||
{
|
||||
item.Clear();
|
||||
blob.Clear();
|
||||
while(!p.IsEof())
|
||||
if(p.Id("size")) {
|
||||
size.cx = clamp(p.ReadInt(), 1, 10000);
|
||||
|
|
@ -303,6 +334,13 @@ void Diagram::Load(CParser& p)
|
|||
img = StreamRaster::LoadStringAny(Base64Decode(p.ReadString()));
|
||||
p.Char(';');
|
||||
}
|
||||
else
|
||||
if(p.Id("blob")) {
|
||||
int id = p.ReadInt();
|
||||
String data = Base64Decode(p.ReadString());
|
||||
blob.Add(id, data);
|
||||
p.Char(';');
|
||||
}
|
||||
else
|
||||
item.Add().Load(p);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ struct DiagramItem : Point2 {
|
|||
Color ink;
|
||||
Color paper;
|
||||
Sizef size;
|
||||
String data;
|
||||
int blob_id;
|
||||
|
||||
enum {
|
||||
SHAPE_LINE,
|
||||
|
|
@ -37,6 +37,7 @@ struct DiagramItem : Point2 {
|
|||
SHAPE_ARROWUP,
|
||||
SHAPE_ARROWVERT,
|
||||
SHAPE_SVGPATH,
|
||||
SHAPE_IMAGE,
|
||||
SHAPE_COUNT,
|
||||
};
|
||||
|
||||
|
|
@ -61,7 +62,7 @@ struct DiagramItem : Point2 {
|
|||
int cap[2] = { CAP_NONE, CAP_NONE };
|
||||
int dash = 0;
|
||||
|
||||
void Paint(Painter& w, dword style = 0, const Index<Pointf> *conn = nullptr) const;
|
||||
void Paint(Painter& w, const VectorMap<int, String>& data, dword style = 0, const Index<Pointf> *conn = nullptr) const;
|
||||
|
||||
bool IsLine() const { return shape == SHAPE_LINE; }
|
||||
|
||||
|
|
@ -73,10 +74,10 @@ struct DiagramItem : Point2 {
|
|||
|
||||
void FixPosition();
|
||||
|
||||
void Serialize(Stream& s) { Point2::Serialize(s); s % shape % ink % paper % qtf % width % cap[0] % cap[1] % dash % size % data; }
|
||||
void Serialize(Stream& s) { Point2::Serialize(s); s % shape % ink % paper % qtf % width % cap[0] % cap[1] % dash % size % blob_id; }
|
||||
|
||||
void Reset();
|
||||
void Save(StringBuffer& r) const;
|
||||
void Save(StringBuffer& r, int blob_id) const;
|
||||
void Load(CParser& p);
|
||||
|
||||
DiagramItem() { Reset(); }
|
||||
|
|
@ -87,10 +88,11 @@ private:
|
|||
};
|
||||
|
||||
struct Diagram {
|
||||
Size size = Null; // Null - auto
|
||||
Array<DiagramItem> item;
|
||||
Image img;
|
||||
bool img_hd = false;
|
||||
Size size = Null; // Null - auto
|
||||
Array<DiagramItem> item;
|
||||
Image img;
|
||||
bool img_hd = false;
|
||||
VectorMap<int, String> blob;
|
||||
|
||||
struct PaintInfo {
|
||||
bool editor = false;
|
||||
|
|
@ -102,6 +104,7 @@ struct Diagram {
|
|||
|
||||
Size GetSize() const;
|
||||
void Paint(Painter& w, const PaintInfo& pi) const;
|
||||
int AddBlob(const String& data);
|
||||
void Serialize(Stream& s);
|
||||
void Save(StringBuffer& r) const;
|
||||
void Load(CParser& p);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ Index<String> DiagramItem::Shape = { "line", "rect", "round_rect",
|
|||
"triangle1", "triangle2",
|
||||
"arrow_left", "arrow_right", "arrow_horz",
|
||||
"arrow_down", "arrow_up", "arrow_vert",
|
||||
"svgpath",
|
||||
"svgpath", "image"
|
||||
};
|
||||
|
||||
Vector<Pointf> DiagramItem::GetConnections() const
|
||||
|
|
@ -27,7 +27,7 @@ Vector<Pointf> DiagramItem::GetConnections() const
|
|||
return p;
|
||||
}
|
||||
|
||||
void DiagramItem::Paint(Painter& w, dword style, const Index<Pointf> *conn) const
|
||||
void DiagramItem::Paint(Painter& w, const VectorMap<int, String>& data, dword style, const Index<Pointf> *conn) const
|
||||
{
|
||||
bool dark = style & DARK;
|
||||
|
||||
|
|
@ -142,199 +142,212 @@ void DiagramItem::Paint(Painter& w, dword style, const Index<Pointf> *conn) cons
|
|||
Rectf r(pt[0], pt[1]);
|
||||
r.Normalize();
|
||||
r.Deflate(width / 2);
|
||||
Rect text_rect = r.Deflated(width + 2, 0);
|
||||
Pointf c = r.CenterPoint();
|
||||
double sz = min(r.Width(), r.Height());
|
||||
double arrow_width = min(r.Width() / 3, r.Height() / 2);
|
||||
double h = r.GetHeight();
|
||||
double h4 = h / 4;
|
||||
double th4 = r.top + h4;
|
||||
double bh4 = r.bottom - h4;
|
||||
double arrow_height = min(r.Height() / 3, r.Width() / 2);
|
||||
|
||||
w.Begin();
|
||||
w.Offset(r.left, r.top);
|
||||
|
||||
double w1 = r.GetWidth();
|
||||
double h = r.GetHeight();
|
||||
|
||||
double cx = r.GetWidth();
|
||||
double cy = r.GetHeight();
|
||||
|
||||
Rect text_rect = r.Deflated(width + 2, 0);
|
||||
|
||||
double sz = min(cx, cy);
|
||||
double arrow_width = min(cx / 3, cy / 3);
|
||||
double arrow_height = min(cx / 3, cy / 3);
|
||||
double h2 = h / 2;
|
||||
double h4 = h / 4;
|
||||
double bh4 = h - h4;
|
||||
double w2 = w1 / 2;
|
||||
double w4 = w1 / 4;
|
||||
double lw4 = r.left + w4;
|
||||
double rw4 = r.right - w4;
|
||||
Pointf m(w2, h2);
|
||||
double hc, thc, bhc; // cylinder
|
||||
switch(shape) {
|
||||
case SHAPE_ROUNDRECT:
|
||||
w.RoundedRectangle(r.left, r.top, r.GetWidth(), r.GetHeight(), sz > 30 ? 8 : sz > 15 ? 4 : 2);
|
||||
w.RoundedRectangle(0, 0, w1, h, sz > 30 ? 8 : sz > 15 ? 4 : 2);
|
||||
break;
|
||||
case SHAPE_OVAL:
|
||||
if(r.GetWidth() > r.GetHeight()) {
|
||||
double ra = r.GetHeight() / 2;
|
||||
w.Move(r.left + ra, r.top)
|
||||
.Line(r.right - ra, r.top)
|
||||
.Arc(r.right - ra, r.top + ra, ra, -M_PI / 2, M_PI)
|
||||
.Line(r.left + ra, r.bottom)
|
||||
.Arc(r.left + ra, r.top + ra, ra, M_PI / 2, M_PI);
|
||||
if(w1 > h) {
|
||||
double ra = h2;
|
||||
w.Move(ra, 0)
|
||||
.Line(w1 - ra, 0)
|
||||
.Arc(w1 - ra, 0 + ra, ra, -M_PI / 2, M_PI)
|
||||
.Line(ra, cy)
|
||||
.Arc(ra, 0 + ra, ra, M_PI / 2, M_PI);
|
||||
break;
|
||||
}
|
||||
case SHAPE_ELLIPSE:
|
||||
w.Ellipse(r);
|
||||
w.Ellipse(m.x, m.y, w2, h2);
|
||||
break;
|
||||
case SHAPE_DIAMOND:
|
||||
w.Move(c.x, r.top).Line(r.right, c.y).Line(c.x, r.bottom).Line(r.left, c.y).Close();
|
||||
w.Move(m.x, 0).Line(cx, m.y).Line(m.x, cy).Line(0, m.y).Close();
|
||||
break;
|
||||
case SHAPE_PARALLELOGRAM:
|
||||
w.Move(r.left + r.Width() / 6, r.top).Line(r.right, r.top)
|
||||
.Line(r.right - r.Width() / 6, r.bottom).Line(r.left, r.bottom).Close();
|
||||
w.Move(cx / 6, 0).Line(cx, 0).Line(5 * cx / 6, cy).Line(0, cy).Close();
|
||||
break;
|
||||
case SHAPE_CYLINDER:
|
||||
text_rect.top += int(w1 / 4);
|
||||
hc = h / 6;
|
||||
thc = r.top + hc;
|
||||
bhc = r.bottom - hc;
|
||||
w.Move(r.left, thc)
|
||||
.Arc(r.left + w2, thc, w2, hc, M_PI, M_PI)
|
||||
.Line(r.right, bhc)
|
||||
.Arc(r.left + w2, bhc, w2, hc, 0, M_PI)
|
||||
.Line(r.left, bhc);
|
||||
thc = 0 + hc;
|
||||
bhc = cy - hc;
|
||||
w.Move(0, thc)
|
||||
.Arc(0 + w2, thc, w2, hc, M_PI, M_PI)
|
||||
.Line(cx, bhc)
|
||||
.Arc(0 + w2, bhc, w2, hc, 0, M_PI)
|
||||
.Line(0, bhc);
|
||||
break;
|
||||
case SHAPE_TRIANGLE: {
|
||||
text_rect.left += int(r.Width() / 4);
|
||||
text_rect.right -= int(r.Width() / 4);
|
||||
text_rect.top += int(r.Width() / 3);
|
||||
w.Move(r.left + r.Width() / 2, r.top)
|
||||
.Line(r.right, r.bottom)
|
||||
.Line(r.left, r.bottom)
|
||||
.Close();
|
||||
text_rect.left += w4;
|
||||
text_rect.right -= w4;
|
||||
text_rect.top += int(cx / 3);
|
||||
w.Move(w2, 0).Line(cx, cy).Line(0, cy).Close();
|
||||
}
|
||||
break;
|
||||
case SHAPE_ITRIANGLE: {
|
||||
text_rect.left += int(r.Width() / 4);
|
||||
text_rect.right -= int(r.Width() / 4);
|
||||
text_rect.bottom -= int(r.Width() / 3);
|
||||
w.Move(r.left + r.Width() / 2, r.bottom)
|
||||
.Line(r.right, r.top)
|
||||
.Line(r.left, r.top)
|
||||
.Close();
|
||||
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 = r.left + arrow_width;
|
||||
double a = 0 + arrow_width;
|
||||
text_rect.left += int(arrow_width / 3);
|
||||
w.Move(r.left, r.top + r.Height() / 2)
|
||||
.Line(a, r.top)
|
||||
.Line(a, th4)
|
||||
.Line(r.right, th4)
|
||||
.Line(r.right, bh4)
|
||||
.Line(a, bh4)
|
||||
.Line(a, r.bottom)
|
||||
.Close();
|
||||
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 = r.right - arrow_width;
|
||||
double a = cx - arrow_width;
|
||||
text_rect.right -= int(arrow_width / 3);
|
||||
w.Move(r.right, r.top + r.Height() / 2)
|
||||
.Line(a, r.top)
|
||||
.Line(a, th4)
|
||||
.Line(r.left, th4)
|
||||
.Line(r.left, bh4)
|
||||
w.Move(cx, cy / 2)
|
||||
.Line(a, 0)
|
||||
.Line(a, h4)
|
||||
.Line(0, h4)
|
||||
.Line(0, bh4)
|
||||
.Line(a, bh4)
|
||||
.Line(a, r.bottom)
|
||||
.Line(a, cy)
|
||||
.Close();
|
||||
}
|
||||
break;
|
||||
case SHAPE_ARROWHORZ:
|
||||
{
|
||||
double a1 = r.left + arrow_width;
|
||||
double a1 = 0 + arrow_width;
|
||||
text_rect.left += int(arrow_width / 3);
|
||||
double a2 = r.right - arrow_width;
|
||||
double a2 = cx - arrow_width;
|
||||
text_rect.right -= int(arrow_width / 3);
|
||||
w.Move(r.left, r.top + r.Height() / 2)
|
||||
.Line(a1, r.top)
|
||||
.Line(a1, th4)
|
||||
.Line(a2, th4)
|
||||
.Line(a2, r.top)
|
||||
.Line(r.right, r.top + r.Height() / 2)
|
||||
.Line(a2, r.bottom)
|
||||
w.Move(0, h2)
|
||||
.Line(a1, 0)
|
||||
.Line(a1, h4)
|
||||
.Line(a2, h4)
|
||||
.Line(a2, 0)
|
||||
.Line(cx, h2)
|
||||
.Line(a2, cy)
|
||||
.Line(a2, bh4)
|
||||
.Line(a1, bh4)
|
||||
.Line(a1, r.bottom)
|
||||
.Line(a1, cy)
|
||||
.Close();
|
||||
}
|
||||
break;
|
||||
case SHAPE_ARROWUP: {
|
||||
double a = r.top + arrow_height;
|
||||
double a = arrow_height;
|
||||
text_rect.left += w4;
|
||||
text_rect.right -= w4;
|
||||
text_rect.top += 3 * arrow_height / 4;
|
||||
w.Move(r.left + r.Width() / 2, r.top)
|
||||
.Line(r.right, a)
|
||||
.Line(rw4, a)
|
||||
.Line(rw4, r.bottom)
|
||||
.Line(lw4, r.bottom)
|
||||
.Line(lw4, a)
|
||||
.Line(r.left, a)
|
||||
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 = r.bottom - arrow_height;
|
||||
double a = cy - arrow_height;
|
||||
text_rect.left += w4;
|
||||
text_rect.right -= w4;
|
||||
text_rect.bottom -= 3 * arrow_height / 4;
|
||||
w.Move(r.left + r.Width() / 2, r.bottom)
|
||||
.Line(r.right, a)
|
||||
.Line(rw4, a)
|
||||
.Line(rw4, r.top)
|
||||
.Line(lw4, r.top)
|
||||
.Line(lw4, a)
|
||||
.Line(r.left, a)
|
||||
w.Move(0 + cx / 2, cy)
|
||||
.Line(cx, a)
|
||||
.Line(cx - w4, a)
|
||||
.Line(cx - w4, 0)
|
||||
.Line(w4, 0)
|
||||
.Line(w4, a)
|
||||
.Line(0, a)
|
||||
.Close();
|
||||
}
|
||||
break;
|
||||
case SHAPE_ARROWVERT: {
|
||||
double a1 = r.top + arrow_height;
|
||||
double a2 = r.bottom - arrow_height;
|
||||
double a1 = 0 + arrow_height;
|
||||
double a2 = cy - arrow_height;
|
||||
text_rect.left += w4;
|
||||
text_rect.right -= w4;
|
||||
w.Move(r.left + r.Width() / 2, r.top)
|
||||
.Line(r.right, a1)
|
||||
.Line(rw4, a1)
|
||||
.Line(rw4, a2)
|
||||
.Line(r.right, a2)
|
||||
.Line(r.left + r.Width() / 2, r.bottom)
|
||||
.Line(r.left, a2)
|
||||
.Line(lw4, a2)
|
||||
.Line(lw4, a1)
|
||||
.Line(r.left, a1)
|
||||
w.Move(0 + cx / 2, 0)
|
||||
.Line(cx, a1)
|
||||
.Line(cx - w4, a1)
|
||||
.Line(cx - w4, a2)
|
||||
.Line(cx, a2)
|
||||
.Line(0 + cx / 2, cy)
|
||||
.Line(0, a2)
|
||||
.Line(w4, a2)
|
||||
.Line(w4, a1)
|
||||
.Line(0, a1)
|
||||
.Close();
|
||||
}
|
||||
break;
|
||||
case SHAPE_SVGPATH:
|
||||
if(data.GetCount() && !IsNull(size)) {
|
||||
w.Begin();
|
||||
w.Offset(r.TopLeft());
|
||||
w.Scale(w1 / size.cx, h / size.cy);
|
||||
w.Path(data);
|
||||
w.Path(data.Get(blob_id, String()));
|
||||
}
|
||||
break;
|
||||
case SHAPE_IMAGE:
|
||||
if(data.GetCount()) {
|
||||
String s = data.Get(blob_id, String());
|
||||
if(s.GetCount()) {
|
||||
StringStream ss(s);
|
||||
One<StreamRaster> r = StreamRaster::OpenAny(ss);
|
||||
if(r) {
|
||||
w.DrawImage(0, 0, cx, cy, r->GetImage());
|
||||
}
|
||||
else
|
||||
if(IsSVG(s)) {
|
||||
Rectf f = GetSVGBoundingBox(s);
|
||||
Sizef isz = f.GetSize();
|
||||
w.Scale(cx / f.GetWidth(), cy / f.GetHeight());
|
||||
w.Translate(-f.left, -f.top);
|
||||
RenderSVG(w, s, Event<String, String&>(), paper);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
w.Rectangle(r);
|
||||
w.Rectangle(0, 0, cx, cy);
|
||||
break;
|
||||
}
|
||||
DoDash();
|
||||
w.Fill(paper);
|
||||
Stroke();
|
||||
|
||||
if(shape != SHAPE_IMAGE) {
|
||||
DoDash();
|
||||
w.Fill(paper);
|
||||
Stroke();
|
||||
}
|
||||
|
||||
switch(shape) {
|
||||
case SHAPE_CYLINDER:
|
||||
w.Move(r.left, thc)
|
||||
.Arc(r.left + w2, thc, w2, hc, M_PI, -M_PI);
|
||||
w.Move(0, thc)
|
||||
.Arc(0 + w2, thc, w2, hc, M_PI, -M_PI);
|
||||
DoDash();
|
||||
Stroke();
|
||||
break;
|
||||
case SHAPE_SVGPATH:
|
||||
if(data.GetCount() && !IsNull(size))
|
||||
w.End();
|
||||
break;
|
||||
}
|
||||
|
||||
w.End();
|
||||
|
||||
int txt_cy = txt.GetHeight(pi.zoom, text_rect.GetWidth());
|
||||
txt.Paint(w, text_rect.left, text_rect.top + (text_rect.GetHeight() - txt_cy) / 2, text_rect.GetWidth(), pi);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue