#include "RichEdit.h" namespace Upp { void DiagramEditor::SetCursor(int i) { cursor = i; if(i < 0) { sel.Clear(); return; } sel.FindAdd(i); GetAttrs(); } DiagramItem& DiagramEditor::CursorItem() { static DiagramItem nil; if(cursor >= 0 && cursor < data.item.GetCount()) return data.item[cursor]; return nil; } DiagramItem& DiagramEditor::AddItem(int shape) { int i = data.item.GetCount(); if(shape == 0) { // insert lines before shapes i = 0; while(i < data.item.GetCount() && data.item[i].IsLine()) i++; data.item.Insert(i); } DiagramItem& m = data.item.At(i); SetCursor(i); return m; } void DiagramEditor::SetAttrs(DiagramItem& m, dword attrs) { if(attrs & ATTR_SHAPE) m.shape = ~shape; if(attrs & ATTR_CAP0) m.cap[0] = ~line_start; if(attrs & ATTR_CAP1) m.cap[1] = ~line_end; if(attrs & ATTR_WIDTH) m.width = ~line_width; if(attrs & ATTR_DASH) m.dash = ~line_dash; if(attrs & ATTR_INK) m.ink = ~ink; if(attrs & ATTR_PAPER) m.paper = ~paper; } void DiagramEditor::ForEachConst(Event fn) const { for(int ii : sel) fn(data.item[ii]); } void DiagramEditor::ForEach(Event fn) { for(int ii : sel) fn(data.item[ii]); Sync(); Commit(); } void DiagramEditor::SetAttrs(dword attrs) { if(tool >= 0) SetAttrs(tl[tool], attrs); ForEach([&](DiagramItem& m) { SetAttrs(m, attrs); }); } void DiagramEditor::GetAttrs(const DiagramItem& m) { shape <<= m.shape; line_start <<= m.cap[0]; line_end <<= m.cap[1]; line_width <<= m.width; line_dash <<= m.dash; ink <<= m.ink; paper <<= m.paper; } void DiagramEditor::GetAttrs() { GetAttrs(CursorItem()); } void DiagramEditor::MoveFrontBack(bool back) { FinishText(); Array item; Index newsel; int newcursor = -1; for(int pass = 0; pass < 2; pass++) for(int i = 0; i < data.item.GetCount(); i++) { bool isel = sel.Find(i) >= 0; bool b = pass; if(back) b = !b; if(isel == b) { int ii = item.GetCount(); if(isel) newsel.Add(ii); if(cursor == i) newcursor = ii; item.Add(data.item[i]); } } data.item = pick(item); sel = pick(newsel); cursor = newcursor; Sync(); } void DiagramEditor::Align(bool horz, int align) { FinishText(); if(!IsCursor()) return; auto HoVe = [&](Pointf& p) -> double& { return horz ? p.x : p.y; }; Pointf sz0 = (Point)data.GetSize(); double dsz = HoVe(sz0); Rect cr = GetCursorRect(); Pointf cp1 = cr.TopLeft(); Pointf cp2 = cr.BottomRight(); double csz = HoVe(cp2) - HoVe(cp1); PrepareConns(); for(int ii : sel) { if(ii != cursor || align == ALIGN_NULL) { DiagramItem& m = data.item[ii]; m.Normalize(); 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); HoVe(p2) = HoVe(cp1) + sz; } if(align == ALIGN_RIGHT) { HoVe(p2) = HoVe(cp2); HoVe(p1) = HoVe(cp2) - sz; } if(align == ALIGN_CENTER) { HoVe(p1) = HoVe(cp1) + csz / 2 - sz / 2; HoVe(p2) = HoVe(p1) + sz; } if(align == ALIGN_JUSTIFY) HoVe(p2) = HoVe(p1) + csz; if(align == ALIGN_NULL) { 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(); Commit(); } void DiagramEditor::PrepareConns() { conns.Clear(); VectorMap>> map; for(int pass = 0; pass < 2; pass++) for(int i = 0; i < data.item.GetCount(); i++) { const DiagramItem& m = data.item[i]; if((int)m.IsLine() == pass) { Vector cp = m.GetConnections(); for(int j = 0; j < cp.GetCount(); j++) map.GetAdd(cp[j]) << MakeTuple(i, j); } } for(int i = 0; i < data.item.GetCount(); i++) { const DiagramItem& m = data.item[i]; if(m.IsLine()) { auto Add = [&](Pointf p, int j) { auto *q = map.FindPtr(p); if(q) { for(auto w : *q) { Cn& c = conns.Add(); c.mi = w.a; c.ci = w.b; c.li = i; c.pi = j; } } }; Add(m.pos, 0); Add(m.pos + m.size, 1); } } } void DiagramEditor::UseConns() { for(const Cn& cn: conns) 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& sz_cx, Sizef& sz_cy) { m.Normalize(); Sizef sz = m.GetRect().GetSize(); Sizef sz0 = m.GetStdSize(data); 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 { SizeDlg(); static Size sz[]; void Set(Size sz, Size asz); Size Get() const; void Sync(); }; Size SizeDlg::sz[] = { Null, Size(1250, 1000), Size(1600, 900), Size(1600, 800), Size(1000, 1250), }; Size SizeDlg::Get() const { int q = ~size; if(q >= 0 && q < __countof(sz)) return sz[q]; auto cl = [](int x) { return clamp(x, 64, 10000); }; return Size(cl(~cx), cl(~cy)); } void SizeDlg::Sync() { bool b = ~size == 5; cx.Enable(b); cy.Enable(b); } void SizeDlg::Set(Size sz, Size asz) { cx <<= asz.cx; cy <<= asz.cy; int i = FindIndex(SubRange(SizeDlg::sz, __countof(SizeDlg::sz)), sz); size <<= i < 0 ? 5 : i; Sync(); } SizeDlg::SizeDlg() { CtrlLayoutOKCancel(*this, "Canvas size"); size << [=] { Sync(); }; } void DiagramEditor::ChangeSize() { SizeDlg dlg; dlg.Set(data.size, data.GetSize()); if(dlg.ExecuteOK()) { data.size = dlg.Get(); Commit(); } } }