ultimatepp/uppsrc/Geom/Ctrl/pathCtrl.cpp
cxl 8ebdcbb0d5 uppsrc: NAMESPACE_UPP / END_UPP_NAMESPACE removed
git-svn-id: svn://ultimatepp.org/upp/trunk@10186 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2016-08-26 17:15:30 +00:00

2317 lines
55 KiB
C++

#include "GeomCtrl.h"
namespace Upp {
#define LAYOUTFILE <Geom/Ctrl/pathedit.lay>
#include <CtrlCore/lay.h>
#define IMAGEFILE <Geom/Ctrl/pathedit.iml>
#define IMAGECLASS PathImg
#include <Draw/iml.h>
class DlgPathStyleSetup
{
public:
typedef DlgPathStyleSetup CLASSNAME;
DlgPathStyleSetup();
bool Run(PathStyleMisc& style);
void PutHelp();
private:
void Pump(PathStyleMisc& style, bool write);
void OnMiter();
private:
// WithHelp<
WithPathStyleSetupLayout<TopWindow>
// >
dialog;
};
bool RunDlgPathStyleSetup(PathStyleMisc& style) { return DlgPathStyleSetup().Run(style); }
RegisterHelpTopicObjectTitle(DlgPathStyleSetup, "Vlastnosti stylu")
DlgPathStyleSetup::DlgPathStyleSetup()
{
CtrlLayoutOKCancel(dialog, DlgPathStyleSetupHelpTitle());
dialog.HelpTopic("DlgPathStyleSetup");
dialog.width.MinMax(0.001, 20);
dialog.begin.MinMax(0, 100);
dialog.segment.MinMax(0, 100);
dialog.end.MinMax(0, 100);
dialog.miter.Hide();
dialog.miter.GetPrev()->Hide();
dialog.miter.Add(PathStyle::MITER_ROUND, "zaoblit");
dialog.miter.Add(PathStyle::MITER_SHARP, "ostrý roh");
dialog.miter.Add(PathStyle::MITER_FLAT, "zkosit");
dialog.miter <<= THISBACK(OnMiter);
dialog.chamfer.NotNull().MinMax(0, 5);
}
bool DlgPathStyleSetup::Run(PathStyleMisc& style)
{
Pump(style, false);
OnMiter();
if(dialog.Run() != IDOK)
return false;
Pump(style, true);
return true;
}
void DlgPathStyleSetup::OnMiter()
{
dialog.chamfer.Enable((int)~dialog.miter == PathStyle::MITER_SHARP);
if(dialog.chamfer.IsEnabled() && dialog.chamfer.GetText().IsEmpty())
dialog.chamfer <<= STD_CHAMFER;
}
void DlgPathStyleSetup::Pump(PathStyleMisc& style, bool write)
{
UPP::Pump pump;
pump
<< PumpData(style.width, dialog.width)
<< PumpData(style.begin, dialog.begin)
<< PumpData(style.segment, dialog.segment)
<< PumpData(style.end, dialog.end)
<< PumpData(style.miter, dialog.miter);
if(style.miter == style.MITER_SHARP)
pump << PumpData(style.chamfer, dialog.chamfer);
pump << write;
}
static double CalcDecadicStep(double scale, int minfine, int mincoarse, int& fine)
{
double mu = mincoarse / scale;
if(mu <= 1e-20)
{
fine = 0;
return Null;
}
int exp = ffloor(log10(mu));
double lo = pow(10.0, exp);
int tc = (lo * 5 <= mu ? 5 : lo * 2 <= mu ? 2 : 1);
double mf = scale * lo * tc / minfine;
int tf = 1;
while(tf < 100 && mf >= 10)
mf /= 10, tf *= 10;
if(tf <= tc)
tf = 1;
else
tf *= (mf >= 5 && tc != 2 ? 5 : mf >= 2 && tc != 5 ? 2 : 1);
fine = tf;
return lo * tc;
}
//////////////////////////////////////////////////////////////////////
// DlgPathEditorSetup::
class DlgPathEditorSetup
{
public:
typedef DlgPathEditorSetup CLASSNAME;
DlgPathEditorSetup();
bool Run(PathEditorCtrl::Setup& setup);
void PutHelp();
private:
void Pump(PathEditorCtrl::Setup& setup, bool write);
private:
// WithHelp<
WithPathEditorSetupLayout<TopWindow>
// >
dialog;
};
bool RunDlgPathEditorSetup(PathEditorCtrl::Setup& setup) { return DlgPathEditorSetup().Run(setup); }
RegisterHelpTopicObjectTitle(DlgPathEditorSetup, "Vlastnosti editoru")
DlgPathEditorSetup::DlgPathEditorSetup()
{
CtrlLayoutOKCancel(dialog, DlgPathEditorSetupHelpTitle());
dialog.HelpTopic("DlgPathEditorSetup");
dialog.grid.MinMax(1e-5, 100);
dialog.snap.MinMax(1e-5, 100);
}
bool DlgPathEditorSetup::Run(PathEditorCtrl::Setup& setup)
{
Pump(setup, false);
if(dialog.Run() != IDOK)
return false;
Pump(setup, true);
return true;
}
void DlgPathEditorSetup::Pump(PathEditorCtrl::Setup& setup, bool write)
{
UPP::Pump pump;
pump
<< PumpEnumData(setup.do_grid, dialog.do_grid)
<< PumpData (setup.grid, dialog.grid)
<< PumpEnumData(setup.do_snap, dialog.do_snap)
<< PumpData (setup.snap, dialog.snap)
<< PumpEnumData(setup.do_ruler, dialog.do_ruler)
<< PumpEnumData(setup.do_axis, dialog.do_axis)
<< write;
}
//////////////////////////////////////////////////////////////////////
// PathEditorCtrl::Setup::
void PathEditorCtrl::Setup::Serialize(Stream& stream)
{
int version = StreamHeading(stream, 4, 4, 4, "PathEditorCtrl::Setup");
if(version >= 1)
{
stream.Pack(do_grid, do_snap, do_ruler, do_axis);
stream % grid % snap / sample_size / sample_width;
}
}
//////////////////////////////////////////////////////////////////////
// PathEditorCtrl::ViewPlotter::
PathEditorCtrl::ViewPlotter::ViewPlotter(PathEditorCtrl *ctrl)
: draw(ctrl)
{
Set(draw, ctrl->GetScale(), ctrl->GetDelta(), 10);
PathMap(&PathStyleMap::App());
}
PathEditorCtrl::PathEditorCtrl()
: style(0)
{
edit_mode = EDIT_NORMAL;
drag_mode = DRAG_NONE;
SetFrame(InsetFrame());
zoom_horz_in .SetImage(PathImg::view_zoom_in());
zoom_vert_in .SetImage(PathImg::view_zoom_in());
zoom_horz_out .SetImage(PathImg::view_zoom_out());
zoom_vert_out .SetImage(PathImg::view_zoom_out());
zoom_horz_full.SetImage(PathImg::view_zoom_horz_full());
zoom_vert_full.SetImage(PathImg::view_zoom_vert_full());
zoom_full .SetImage(PathImg::view_zoom_full());
zoom_horz_in <<= THISBACK(OnViewZoomHorzIn);
zoom_horz_out <<= THISBACK(OnViewZoomHorzOut);
zoom_vert_in <<= THISBACK(OnViewZoomVertIn);
zoom_vert_out <<= THISBACK(OnViewZoomVertOut);
zoom_horz_full <<= THISBACK(OnViewZoomHorzFull);
zoom_vert_full <<= THISBACK(OnViewZoomVertFull);
zoom_full <<= THISBACK(OnViewZoomFull);
sample_size_tag.SetLabel("Ukázka: ");
sample_size.Add(0, "skrýt");
sample_size.Add(40, "malá");
sample_size.Add(70, "støední");
sample_size.Add(100, "velká");
// sample_size.SetRect(LayoutZoom(80, 0));
sample_size <<= 40;
sample_size <<= THISBACK(OnSampleSize);
sample_width_tag.SetLabel("Šíøka: ");
sample_width.NotNull().MinMax(0, MAX_WIDTH);
sample_width <<= THISBACK(OnSampleWidth);
sample_width <<= 2;
// sample_width.SetRect(LayoutZoom(40, 0));
zoom_horz_in.NoWantFocus();
zoom_horz_out.NoWantFocus();
zoom_horz_full.NoWantFocus();
zoom_vert_in.NoWantFocus();
zoom_vert_out.NoWantFocus();
zoom_vert_full.NoWantFocus();
zoom_full.NoWantFocus();
AddFrame(*this);
hruler.Horz();
vruler.Vert();
hruler.SetZoom(500, 0);
hruler.MinMax(0, 20);
// hruler.Units(1, 5);
vruler.SetZoom(500, 0);
vruler.MinMax(-5, 5);
// vruler.Units(1, 5);
hscroll <<= vscroll <<= THISBACK(OnScroll);
hscroll.NoAutoDisable();
vscroll.NoAutoDisable();
}
void PathEditorCtrl::FrameLayout(Rect& rc)
{
if(!style)
return;
outer_sample = rc;
rc.right -= setup.sample_size;
rc.bottom -= setup.sample_size;
inner_sample = rc;
Size avail = rc.Size();
int box = ScrollBarSize();
int rsz = RulerCtrl::GetStdWidth();
if(setup.do_ruler)
avail -= rsz;
double pwd = hruler.GetLength() * hruler.GetScale() + 2 * GAP + HGAP;
double pht = vruler.GetLength() * vruler.GetScale() + 2 * GAP;
zoom_horz_in.SetFrameRect(rc.left, rc.bottom - box, box, box);
hscroll.SetFrameRect(rc.left + box, rc.bottom - box, rc.Width() - 4 * box, box);
zoom_horz_out.SetFrameRect(rc.right - 3 * box, rc.bottom - box, box, box);
zoom_horz_full.SetFrameRect(rc.right - 2 * box, rc.bottom - box, box, box);
hscroll.Set(hscroll, avail.cx - box, fround(pwd));
zoom_vert_in.SetFrameRect(rc.right - box, rc.top, box, box);
vscroll.SetFrameRect(rc.right - box, rc.top + box, box, rc.Height() - 4 * box);
zoom_vert_out.SetFrameRect(rc.right - box, rc.bottom - 3 * box, box, box);
zoom_vert_full.SetFrameRect(rc.right - box, rc.bottom - 2 * box, box, box);
vscroll.Set(vscroll, avail.cy - box, fround(pht));
zoom_full.SetFrameRect(rc.right - box, rc.bottom - box, box, box);
rc.right -= box;
rc.bottom -= box;
if(setup.do_ruler)
{
hruler.SetFrameRect(rc.left + rsz, rc.top, rc.Width() - rsz, rsz);
vruler.SetFrameRect(rc.left, rc.top + rsz, rsz, rc.Height() - rsz);
rc.left += rsz;
rc.top += rsz;
}
}
void PathEditorCtrl::FrameAddSize(Size& sz)
{
if(!style)
return;
int box = ScrollBarSize();
int rsz = RulerCtrl::GetStdWidth();
if(setup.do_ruler)
sz += rsz;
sz += box;
}
static void PaintSamplePath(PathDraw& path, const Rect& mid)
{
path.MoveTo(mid.left, mid.bottom);
path.LineTo(mid.left + 20, mid.bottom);
path.LineTo(mid.left + 40, mid.bottom - 10);
path.LineTo(mid.left + 50, mid.bottom + 10);
path.LineTo(mid.left + 70, mid.bottom);
if(mid.Width() >= 200)
{
path.LineTo(mid.left + 100, mid.bottom);
path.LineTo(mid.left + 130, mid.bottom - 10);
path.LineTo(mid.left + 170, mid.bottom + 10);
path.LineTo(mid.left + 200, mid.bottom);
}
if(mid.Width() >= 300)
{
path.LineTo(mid.left + 240, mid.bottom - 10);
path.LineTo(mid.left + 250, mid.bottom + 10);
path.LineTo(mid.left + 260, mid.bottom - 10);
path.LineTo(mid.left + 300, mid.bottom);
}
path.LineTo(mid.right, mid.bottom);
path.LineTo(mid.right, mid.bottom - 20);
path.LineTo(mid.right - 10, mid.bottom - 40);
path.LineTo(mid.right + 10, mid.bottom - 50);
path.LineTo(mid.right, mid.bottom - 70);
if(mid.Height() >= 200)
{
path.LineTo(mid.right, mid.bottom - 100);
path.LineTo(mid.right - 10, mid.bottom - 130);
path.LineTo(mid.right + 10, mid.bottom - 170);
path.LineTo(mid.right, mid.bottom - 200);
}
path.LineTo(mid.right, mid.top);
path.Paint();
}
void PathEditorCtrl::FramePaint(Draw& draw, const Rect& r)
{
Rect rc = r;
if(setup.sample_size > 0)
{
PumpTraces(true);
draw.Clip(outer_sample);
draw.ExcludeClip(inner_sample);
draw.DrawRect(outer_sample, SLtGray);
PathDraw path(draw, *style, Black, setup.sample_width);
int half = setup.sample_size >> 1;
Rect mid(outer_sample.left + 10, outer_sample.top + 10,
(outer_sample.right + inner_sample.right) >> 1,
(outer_sample.bottom + inner_sample.bottom) >> 1);
PaintSamplePath(path, mid);
if(setup.do_axis)
{
path.Set(draw, PathStyle::solid(), LtRed, 0);
PaintSamplePath(path, mid);
}
draw.End();
rc = inner_sample;
}
if(setup.do_ruler)
{
int r = RulerCtrl::GetStdWidth();
draw.DrawRect(rc.left, rc.top, r, r, SGray);
}
Size scb(vscroll.GetRect().Width(), hscroll.GetRect().Height());
if(scb.cx && scb.cy)
draw.DrawRect(rc.right - scb.cx, rc.bottom - scb.cy, scb.cx, scb.cy, SGray);
}
void PathEditorCtrl::FrameAdd(Ctrl& ctrl)
{
ctrl << hruler << vruler << (Ctrl &)hscroll << (Ctrl &)vscroll
<< zoom_horz_in << zoom_horz_out << zoom_horz_full
<< zoom_vert_in << zoom_vert_out << zoom_vert_full
<< zoom_full;
}
void PathEditorCtrl::FrameRemove()
{
hruler.Remove();
vruler.Remove();
hscroll.Remove();
vscroll.Remove();
zoom_horz_in.Remove();
zoom_horz_out.Remove();
zoom_horz_full.Remove();
zoom_vert_in.Remove();
zoom_vert_out.Remove();
zoom_vert_full.Remove();
zoom_full.Remove();
}
static inline bool PathStyleTraceLeftLess(const PathStyle::Trace& a, const PathStyle::Trace& b)
{ return a.left < b.left; }
void PathEditorCtrl::PumpTraces(bool write)
{
if(write)
{
style->traces <<= traces;
Sort(style->traces, &PathStyleTraceLeftLess);
}
else
{
selection.Clear();
traces <<= style->traces;
}
}
void PathEditorCtrl::Layout()
{
if(!style)
return;
Size size = GetSize();
double cx = style->begin + style->segment + style->end, cy = style->width / 2;
int fine;
double dstep = CalcDecadicStep(hruler.GetScale(), 5, 100, fine);
hruler.MinMax(0, cx);
hruler.SetTextStep(dstep);
hruler.SetSmallStep(dstep / fine);
vruler.MinMax(-cy, cy);
dstep = CalcDecadicStep(vruler.GetScale(), 5, 100, fine);
vruler.SetTextStep(dstep);
vruler.SetSmallStep(dstep / fine);
/* if(cx * hruler.GetScale() <= size.cx)
hruler.SetZoomDelta(GAP);
if(style->width * vruler.GetScale() <= size.cy)
vruler.Delta(GAP + cy * vruler.GetScale());
*/ OnScroll();
Refresh();
}
void PathEditorCtrl::UpdateScroll()
{
Size size = GetSize();
double pwd = hruler.GetLength() * hruler.GetScale() + 2 * GAP + HGAP;
double pht = vruler.GetLength() * vruler.GetScale() + 2 * GAP;
hruler.SetZoom(hruler.GetScale(), pwd <= size.cx ? GAP : minmax(hruler.GetDelta(), size.cx - double(GAP) - pwd, double(GAP)));
double t = style->width / 2 * vruler.GetScale();
vruler.SetZoom(vruler.GetScale(), pht <= size.cy ? pht / 2 : minmax(vruler.GetDelta(), size.cy - double(GAP) - t, double(GAP) + t));
int hpos = GAP - fround(hruler.GetDelta());
int vpos = GAP + fround(style->width * vruler.GetScale() / 2 - vruler.GetDelta());
Layout();
hscroll = hpos;
vscroll = vpos;
OnScroll();
Refresh();
}
void PathEditorCtrl::OnScroll()
{
double dx = hruler.GetDelta(), dy = vruler.GetDelta();
if(!hscroll.GetRect().IsEmpty())
dx = GAP - (int)hscroll;
if(!vscroll.GetRect().IsEmpty())
dy = GAP + style->width * vruler.GetScale() / 2 - (int)vscroll;
if(dx != hruler.GetDelta() || dy != vruler.GetDelta())
{
hruler.SetZoom(hruler.GetScale(), dx);
vruler.SetZoom(vruler.GetScale(), dy);
Refresh();
}
}
void PathEditorCtrl::UpdateSetup()
{
sample_size <<= setup.sample_size;
sample_width <<= setup.sample_width;
Layout();
sample_width.Enable(setup.sample_size > 0);
}
void PathEditorCtrl::OnSampleSize()
{
setup.sample_size = ~sample_size;
Layout();
sample_width.Enable(setup.sample_size > 0);
}
void PathEditorCtrl::OnSampleWidth()
{
setup.sample_width = ~sample_width;
UpdateSample();
}
static void PaintSizeBreak(PlotterTool& tool, double x, double y, Pointf size)
{
tool.MoveTo(x, -y + size.y);
tool.LineTo(x - size.x, -y);
tool.LineTo(x + size.x, -y);
tool.LineTo(x, -y + size.y);
tool.LineTo(x, y - size.y);
tool.LineTo(x + size.x, y);
tool.LineTo(x - size.x, y);
tool.LineTo(x, y - size.y);
tool.Paint();
}
void PathEditorCtrl::Paint(Draw& draw)
{
Rectf full = GetFullExtent();
Rectf vis = ClientToUnits(draw.GetPaintRect()) & full;
Rect cvis = UnitsToClient(vis) & draw.GetPaintRect();
DrawRectMinusRect(draw, draw.GetPaintRect(), cvis, SGray);
if(cvis.IsEmpty())
return;
draw.Clip(cvis);
draw.DrawRect(cvis, White);
if(setup.do_grid && setup.grid >= 1e-10)
{
double s = setup.grid;
while(s * hruler.GetScale() < 4)
s *= 10;
int l, h;
for(l = ffloor(vis.left / s), h = fceil(vis.right / s); l <= h; l++)
{
Point top = UnitsToClient(Pointf(l * s, vis.top));
draw.DrawRect(top.x, cvis.top, 1, cvis.Height(), LtGray);
}
s = setup.grid;
while(s * vruler.GetScale() < 4)
s *= 10;
for(l = ffloor(vis.top / s), h = fceil(vis.bottom / s); l <= h; l++)
{
Point left = UnitsToClient(Pointf(vis.left, l * s));
draw.DrawRect(cvis.left, left.y, cvis.Width(), 1, LtGray);
}
}
Plotter plotter;
plotter.Set(draw, GetScale(), GetDelta(), 10);
plotter.PathMap(&PathStyleMap::App());
AreaTool area;
PathTool path;
for(int i = 0; i < traces.GetCount(); i++)
{
const PathStyle::Trace& trace = traces[i];
int s = (selection.Find(i) >= 0 ? i == selection.Top() ? 2 : 1 : 0);
Color color = Nvl(trace.color, Black);
Color outline = (s == 0 ? color : s == 1 ? LtBlue : Color(192, 192, 255));
if(trace.IsAreaEmpty())
{
path.Set(plotter, ".dot", outline, 3);
if(path.SetExtent(trace.GetExtent()))
trace.Paint(path, true, outline);
}
else
{
area.Set(plotter, Nvl(trace.color, Black), I64(0xaa55aa55aa55aa55), Null, outline, 3);
if(area.SetExtent(trace.GetExtent()))
trace.Paint(area, false, outline);
}
}
draw.End();
{ // draw begin & end bar
Pointf size = Pointf(16, 16) / GetScale();
AreaTool area;
area.Set(plotter, LtGreen, 0, Null, Green, 2);
double x = style->begin;
double h = style->width / 2;
PaintSizeBreak(area, x, h, size);
x += style->segment;
PaintSizeBreak(area, x, h, size);
x += style->end;
PaintSizeBreak(area, x, h, size);
}
if(!selection.IsEmpty())
{
const PathStyle::Trace& trace = traces[selection.Top()];
Pointf list[] =
{
trace.LeftTop(), trace.CenterTop(), trace.RightTop(),
trace.LeftCenter(), trace.RightCenter(),
trace.LeftBottom(), trace.CenterBottom(), trace.RightBottom(),
};
for(int i = 0; i < __countof(list); i++)
if(vis.Contains(list[i]))
{
enum { DELTA = 3 };
Point pt = UnitsToClient(list[i]);
Rect rc(pt.x - DELTA, pt.y - DELTA, pt.x + DELTA + 1, pt.y + DELTA + 1);
draw.DrawRect(rc, LtRed);
rc.Inflate(1);
DrawFrame(draw, rc, White, Black);
}
}
}
Image PathEditorCtrl::CursorImage(Point pt, dword keyflags)
{
switch(edit_mode)
{
case EDIT_ZOOM: return PathImg::drag_zoom_cursor();
case EDIT_PAN: return PathImg::drag_pan_cursor();
}
if(IsDragging())
switch(drag_mode)
{
default: NEVER();
case DRAG_SELECT: return Image::Arrow();
case DRAG_BEGIN:
case DRAG_SEGMENT:
case DRAG_END: return Image::SizeHorz();
case DRAG_INSERT: return PathImg::drag_insert_cursor();
case DRAG_TRACK: return PathStyle::Trace::GetTrackCursor(track_style);
case DRAG_MOVE: return Image::SizeAll();
}
Pointf up = ClientToUnits(pt), start;
int i = GetTrackStyle(up, start);
if(i)
return PathStyle::Trace::GetTrackCursor(i);
if(GetDragSize(up))
return Image::SizeHorz();
i = FindObject(up);
if(i >= 0 && selection.Find(i) >= 0)
return Image::SizeAll(); // move
return Image::Arrow();
}
int PathEditorCtrl::AddObject(const PathStyle::Trace& trace)
{
int i = traces.GetCount();
traces.Add(trace);
RefreshObject(i);
Update();
UpdateSample();
return i;
}
void PathEditorCtrl::SetObject(int i, const PathStyle::Trace& trace)
{
RefreshObject(i);
traces[i] = trace;
RefreshObject(i);
Update();
UpdateSample();
}
void PathEditorCtrl::UpdateSample()
{
if(setup.sample_size > 0)
{
RefreshFrame(Rect(outer_sample.left, outer_sample.top, outer_sample.right, inner_sample.top));
RefreshFrame(Rect(outer_sample.left, inner_sample.bottom, outer_sample.right, outer_sample.bottom));
RefreshFrame(Rect(outer_sample.left, inner_sample.top, outer_sample.right, inner_sample.bottom));
RefreshFrame(Rect(inner_sample.right, inner_sample.top, outer_sample.right, inner_sample.bottom));
}
}
void PathEditorCtrl::RefreshObject(int item)
{
if(item >= 0 && item < traces.GetCount())
{
const PathStyle::Trace& trace = traces[item];
Rectf rc(trace.left, min(trace.left_top, trace.right_top), trace.right, max(trace.left_bottom, trace.right_bottom));
Rect cl = UnitsToClient(rc);
cl.Inflate(selection.Find(item) >= 0 ? 10 : 5);
Refresh(cl);
}
}
bool PathEditorCtrl::WriteClipboard()
{
if(!IsSelection())
return false; // no-op
PathStyle::Clip clip;
for(int i = 0; i < selection.GetCount(); i++)
clip.traces.Add(traces[selection[i]]);
clip.Write();
return true;
// return clip.Write();
}
bool PathEditorCtrl::ReadClipboard()
{
PathStyle::Clip clip;
if(!clip.Read())
return false;
Rectf extent = clip.GetExtent();
Rectf full = GetFullExtent();
bool setwd = false, setsg = false;
if(extent.top < full.top || extent.bottom > full.bottom)
{
int r = PromptYesNoCancel("Vybrané objekty pøesahují limit daný šíøkou èáry. "
"Chcete šíøku èáry upravit?");
if(r < 0)
return true;
setwd = !!r;
}
if(extent.right > full.right)
{
int r = PromptYesNoCancel("Vybrané objekty pøesahují limit daný délkou segmentu. "
"Chcete délku segmentu upravit?");
if(r < 0)
return true;
setsg = !!r;
}
if(setwd)
style->width = max(style->width, max(-extent.top, extent.bottom));
if(setsg)
style->segment += full.right - extent.right;
ClearSelection();
int c = traces.GetCount();
Append(traces, clip.traces);
Vector<int> to_add;
while(c < traces.GetCount())
to_add.Add(c++);
AddSelection(to_add);
if(setwd || setsg)
Layout();
UpdateSample();
return true;
}
Rectf PathEditorCtrl::GetFullExtent() const
{
return Rectf(0, -style->width / 2,
style->begin + style->segment + style->end, style->width / 2);
}
Rectf PathEditorCtrl::GetSelectionExtent() const
{
if(selection.IsEmpty())
return Null;
Rectf rc = traces[selection[0]].GetExtent();
for(int i = 1; i < selection.GetCount(); i++)
rc |= traces[selection[i]].GetExtent();
return rc;
}
int PathEditorCtrl::GetSelectionLeader() const
{
return selection.IsEmpty() ? -1 : selection.Top();
}
void PathEditorCtrl::AddSelection(const Vector<int>& list)
{
int old = GetSelectionLeader();
for(int i = 0; i < list.GetCount(); i++)
if(selection.Find(list[i]) < 0)
{
RefreshObject(list[i]);
selection.Add(list[i]);
}
RefreshObject(old);
WhenRescan();
}
void PathEditorCtrl::XorSelection(const Vector<int>& list)
{
int old_leader = GetSelectionLeader();
for(int i = 0; i < list.GetCount(); i++)
{
RefreshObject(list[i]);
int pos = selection.Find(list[i]);
if(pos >= 0)
selection.Remove(pos);
else
selection.Add(list[i]);
}
int new_leader = GetSelectionLeader();
if(new_leader != old_leader)
{
RefreshObject(old_leader);
RefreshObject(new_leader);
}
WhenRescan();
}
void PathEditorCtrl::RemoveSelection(const Vector<int>& list)
{
int count = selection.GetCount();
for(int i = 0; i < list.GetCount(); i++)
{
int pos = selection.Find(list[i]);
if(pos >= 0)
{
RefreshObject(list[i]);
selection.Remove(pos);
}
}
if(selection.GetCount() != count && !selection.IsEmpty())
RefreshObject(selection.Top());
WhenRescan();
}
void PathEditorCtrl::RefreshSelection()
{
for(int i = 0; i < selection.GetCount(); i++)
RefreshObject(selection[i]);
}
void PathEditorCtrl::ClearSelection()
{
RefreshSelection();
selection.Clear();
WhenRescan();
}
void PathEditorCtrl::SetSelection(const Vector<int>& list)
{
ClearSelection();
Append(selection, list);
// selection <<= list;
RefreshSelection();
WhenRescan();
}
Pointf PathEditorCtrl::ClientToUnits(Point pt) const
{
return Pointf(hruler.FromClient(pt.x), vruler.FromClient(pt.y));
}
Point PathEditorCtrl::UnitsToClient(Pointf pt) const
{
return Point(hruler.ToClient(pt.x), vruler.ToClient(pt.y));
}
Rectf PathEditorCtrl::ClientToUnits(const Rect& rc) const
{
return SortRectf(ClientToUnits(rc.TopLeft()), ClientToUnits(rc.BottomRight()));
}
Rect PathEditorCtrl::UnitsToClient(const Rectf& rc) const
{
return RectSort(UnitsToClient(rc.TopLeft()), UnitsToClient(rc.BottomRight()));
}
Pointf PathEditorCtrl::Snap(Pointf pt) const
{
if(IsNull(pt) || !setup.do_snap || setup.snap <= 1e-10)
return pt;
return Pointf(floor(pt.x / setup.snap + 0.5) * setup.snap, floor(pt.y / setup.snap + 0.5) * setup.snap);
}
Pointf PathEditorCtrl::ClientToSnap(Point pt) const
{
return Snap(ClientToUnits(pt));
}
bool PathEditorCtrl::Key(dword key, int repcnt)
{
Size shift(0, 0);
switch(key)
{
case K_ADD: if(setup.sample_width < MAX_WIDTH) { sample_width <<= ++setup.sample_width; UpdateSample(); } return true;
case K_SUBTRACT: if(setup.sample_width > 0) { sample_width <<= --setup.sample_width; UpdateSample(); }; return true;
case K_LEFT: shift.cx = -1; break;
case K_UP: shift.cy = -1; break;
case K_RIGHT: shift.cx = +1; break;
case K_DOWN: shift.cy = +1; break;
default:
return DragDropCtrl::Key(key, repcnt);
}
if((shift.cx | shift.cy) && IsSelection())
{
Rectf ext = GetSelectionExtent();
Rectf full = GetFullExtent();
Pointf snap(setup.snap, setup.snap);
if(!setup.do_snap || setup.snap <= 1e-10)
snap = Pointf(1, 1) / GetScale();
Pointf step = snap * shift;
Pointf lt = traces[GetSelectionLeader()].LeftTop();
if(step.x)
step.x = snap.x * floor((lt.x + step.x) / snap.x + 0.5) - lt.x;
if(step.y)
step.y = snap.y * floor((lt.y + step.y) / snap.y + 0.5) - lt.y;
for(int i = 0; i < selection.GetCount(); i++)
SetObject(selection[i], traces[selection[i]].GetMove(step));
}
return true;
}
bool PathEditorCtrl::Push(Point pt, dword keyflags)
{
if(edit_mode == EDIT_ZOOM)
{
drag_mode = DRAG_ZOOM;
return true;
}
if(edit_mode == EDIT_PAN)
{
track_start = GetDelta();
drag_mode = DRAG_PAN;
return true;
}
track_limit = GetFullExtent();
Pointf up = ClientToUnits(pt);
if(track_style = GetTrackStyle(up, track_start))
{
drag_mode = DRAG_TRACK;
return true;
}
int i = FindObject(up);
if(i < 0)
{
if((drag_mode = GetDragSize(up)) == 0)
drag_mode = (keyflags & (K_SHIFT | K_CTRL) ? DRAG_SELECT : DRAG_INSERT);
return true;
}
if(keyflags & K_CTRL)
XorSelection(i);
else if(keyflags & K_SHIFT)
{
RemoveSelection(i);
AddSelection(i);
}
else if(selection.Find(i) < 0)
SetSelection(i);
track_start = traces[i].LeftTop();
drag_mode = DRAG_MOVE;
Rectf rc = GetSelectionExtent();
track_limit.left -= rc.left;
track_limit.top -= rc.top;
track_limit.right -= rc.right;
track_limit.bottom -= rc.bottom;
return true;
}
void PathEditorCtrl::MouseMove(Point pt, dword keyflags)
{
Pointf up = ClientToSnap(pt);
// hruler.Mouse(up.x);
hruler.Sync();
// vruler.Mouse(up.y);
vruler.Sync();
if(coords)
{
String s;
s << "x = " << up.x << ", y = " << up.y;
*coords <<= s;
coords->Sync();
}
DragDropCtrl::MouseMove(pt, keyflags);
}
void PathEditorCtrl::RightDown(Point pt, dword keyflags)
{
Pointf up = ClientToUnits(pt);
int i = FindObject(up);
if(i >= 0)
{
RemoveSelection(i);
AddSelection(i);
}
MenuBar::Execute(THISBACK(ToolEdit));
}
void PathEditorCtrl::Drag(Point pt, Point last, Point next, dword keyflags)
{
ViewPlotter plotter(this);
PathTool path;
bool drag_size = (drag_mode == DRAG_BEGIN || drag_mode == DRAG_SEGMENT || drag_mode == DRAG_END);
path.Set(plotter, ".dot", drag_mode == DRAG_SELECT ? LtRed : drag_size ? Green : LtBlue, 3);
Pointf up = ClientToSnap(pt);
// int rop = SetROP2(plotter.draw, R2_NOTXORPEN);
if(drag_mode == DRAG_INSERT)
{
if(!IsNull(next))
path.Rectangle(SortRectf(up, ClientToSnap(next)) & track_limit, true);
if(!IsNull(last))
path.Rectangle(SortRectf(up, ClientToSnap(last)) & track_limit, true);
}
else if(drag_mode == DRAG_SELECT)
{
up = ClientToUnits(pt);
if(!IsNull(next))
path.Rectangle(SortRectf(up, ClientToUnits(next)), true);
if(!IsNull(last))
path.Rectangle(SortRectf(up, ClientToUnits(last)), true);
}
else if(drag_mode == DRAG_TRACK)
{
if(IsSelection())
{
const PathStyle::Trace& base = traces[selection.Top()];
if(!IsNull(next))
PathStyle::Trace(base).Track(ClientToSnap(next) - track_start, track_style).Bind(track_limit).Paint(path);
if(!IsNull(last))
PathStyle::Trace(base).Track(ClientToSnap(last) - track_start, track_style).Bind(track_limit).Paint(path);
}
else
NEVER();
}
else if(drag_mode == DRAG_MOVE)
{
if(IsSelection())
{
Size delta = UnitsToClient(track_start) - pt;
Pointf un(Null), ul(Null);
if(!IsNull(next))
un = fpminmax(ClientToSnap(next + delta) - track_start, track_limit);
if(!IsNull(last))
ul = fpminmax(ClientToSnap(last + delta) - track_start, track_limit);
for(int i = 0; i < selection.GetCount(); i++)
{
const PathStyle::Trace& base = traces[selection[i]];
if(!IsNull(next))
base.GetMove(un).Paint(path, true);
if(!IsNull(last))
base.GetMove(ul).Paint(path, true);
}
}
else
NEVER();
}
else if(drag_size)
{
Pointf size = Pointf(16, 16) / GetScale();
if(!IsNull(next))
{
Pointf un = ClientToSnap(next);
PaintSizeBreak(path, un.x, style->width / 2, size);
}
if(!IsNull(last))
{
Pointf ul = ClientToSnap(last);
PaintSizeBreak(path, ul.x, style->width / 2, size);
}
}
else if(drag_mode == DRAG_ZOOM)
{
up = ClientToUnits(pt);
if(!IsNull(next))
path.Rectangle(SortRectf(up, ClientToUnits(next)), true);
if(!IsNull(last))
path.Rectangle(SortRectf(up, ClientToUnits(last)), true);
path.Paint();
}
else if(drag_mode == DRAG_PAN)
{
Pointf new_offset = track_start;
if(!IsNull(next))
new_offset += next - pt;
hruler.SetZoom(hruler.GetScale(), new_offset.x);
vruler.SetZoom(vruler.GetScale(), new_offset.y);
UpdateScroll();
if(!IsNull(next))
Sync();
}
else
NEVER();
path.Paint();
}
void PathEditorCtrl::Drop(Point pt, Point end, dword keyflags)
{
Pointf up = ClientToSnap(end);
if(drag_mode == DRAG_INSERT)
{
Rectf rc = SortRectf(ClientToSnap(pt), up) & track_limit;
if(rc.left == rc.right && rc.top == rc.bottom)
return;
PathStyle::Trace trace;
trace.left = rc.left;
trace.right = rc.right;
trace.left_top = trace.right_top = rc.top;
trace.left_bottom = trace.right_bottom = rc.bottom;
SetSelection(AddObject(trace));
}
else if(drag_mode == DRAG_SELECT)
{
Rectf rc = SortRectf(ClientToUnits(pt), ClientToUnits(end));
Vector<int> list = FindObject(rc);
if(keyflags & K_SHIFT)
AddSelection(list);
else if(keyflags & K_CTRL)
XorSelection(list);
else
SetSelection(list);
}
else if(drag_mode == DRAG_TRACK)
{
up -= track_start;
if(IsSelection())
{
int i = selection.Top();
SetObject(i, PathStyle::Trace(traces[i]).Track(up, track_style).Bind(track_limit));
}
else
NEVER();
}
else if(drag_mode == DRAG_MOVE)
{
Size delta = UnitsToClient(track_start) - pt;
up = fpminmax(ClientToSnap(end + delta) - track_start, track_limit);
for(int i = 0; i < selection.GetCount(); i++)
SetObject(selection[i], traces[selection[i]].GetMove(up));
}
else if(drag_mode == DRAG_BEGIN || drag_mode == DRAG_SEGMENT || drag_mode == DRAG_END)
{
double lse = style->begin + style->segment;
double le = lse + style->end;
switch(drag_mode)
{
case DRAG_BEGIN:
style->begin = max(0.0, up.x);
style->segment = max(0.0, lse - style->begin);
style->end = max(0.0, le - style->segment - style->begin);
break;
case DRAG_SEGMENT:
style->segment = up.x - style->begin;
if(style->segment < 0)
{
style->begin = max(0.0, style->begin + style->segment);
style->segment = 0;
}
style->end = max(0.0, le - style->begin - style->segment);
break;
case DRAG_END:
style->end = up.x - style->segment - style->begin;
if(style->end < 0)
{
if(up.x < style->begin)
style->begin = max(0.0, up.x);
if(up.x < style->begin + style->segment)
style->segment = max(0.0, up.x - style->begin);
style->end = 0;
}
break;
default:
NEVER();
}
Layout();
UpdateSample();
}
else if(drag_mode == DRAG_ZOOM)
{
Rectf rc = SortRectf(ClientToUnits(pt), ClientToUnits(end));
rc &= GetFullExtent();
Size client = GetSize();
Size avail = max(client - 2 * GAP, Size(1, 1));
double ratio = min(avail.cx / max(rc.Width(), 1e-3), avail.cy / max(rc.Height(), 1e-3));
hruler.SetZoom(ratio, (client.cx - (rc.left + rc.right) * ratio) / 2);
vruler.SetZoom(ratio, (client.cy - (rc.top + rc.bottom) * ratio) / 2);
UpdateScroll();
}
else if(drag_mode == DRAG_PAN)
{
Pointf dest = track_start + Sizef(end - pt);
hruler.SetZoom(hruler.GetScale(), dest.x);
vruler.SetZoom(vruler.GetScale(), dest.y);
UpdateScroll();
}
else
NEVER();
}
void PathEditorCtrl::Click(Point pt, dword keyflags)
{
if(drag_mode == DRAG_INSERT && !(keyflags & (K_CTRL | K_SHIFT)))
ClearSelection();
}
int PathEditorCtrl::FindObject(Pointf pt) const
{
enum { TOLERANCE = 10 };
Pointf inflate = Pointf(TOLERANCE, TOLERANCE) / GetScale();
double best = TOLERANCE;
int found = -1;
for(int i = 0; i < traces.GetCount(); i++)
{
PathStyle::Trace t = traces[i];
t.Inflate(inflate);
if(!t.Contains(pt))
continue;
double d = t.GetDistance(pt);
if(d < best)
{
best = d;
found = i;
}
}
return found;
}
Vector<int> PathEditorCtrl::FindObject(const Rectf& rc) const
{
Vector<int> list;
for(int i = 0; i < traces.GetCount(); i++)
if(rc.Contains(traces[i].GetExtent()))
list.Add(i);
return list;
}
int PathEditorCtrl::GetTrackStyle(Pointf pt, Pointf& track_start) const
{
Pointf tolerance = Pointf(10, 10) / GetScale();
return selection.IsEmpty() ? 0
: traces[selection.Top()].GetTrackStyle(pt, tolerance, track_start);
}
int PathEditorCtrl::GetDragSize(Pointf pt) const
{
Pointf tolerance = Pointf(16, 16) / GetScale();
double h = style->width / 2;
if(pt.y >= -h + tolerance.y && pt.y <= h - tolerance.y)
return 0;
double db = fabs(pt.x - style->begin);
double ds = fabs(pt.x - style->begin - style->segment);
double de = fabs(pt.x - style->begin - style->segment - style->end);
if(db >= tolerance.x && ds >= tolerance.x && de >= tolerance.x)
return 0;
return (ds <= db && ds <= de ? DRAG_SEGMENT : de <= db && de <= ds ? DRAG_END : DRAG_BEGIN);
}
void PathEditorCtrl::ToolEdit(Bar& bar)
{
ToolEditColor(bar);
bar.Separator();
ToolEditCut(bar);
ToolEditCopy(bar);
ToolEditPaste(bar);
ToolEditDelete(bar);
bar.Separator();
// bar.Add("Základní", THISBACK(ToolEditBasic))
// .Help("Naèíst do editoru jeden ze základních stylù èár");
// bar.Separator();
ToolEditSelectAll(bar);
}
void PathEditorCtrl::ToolEditCopy(Bar& bar)
{
bar.Add(IsSelection(), "Kopírovat", CtrlImg::copy(), THISBACK(OnEditCopy))
.Key(K_CTRL_C) //, K_CTRL_INSERT)
.Help("Zkopírovat vybrané objekty do schránky");
}
void PathEditorCtrl::OnEditCopy()
{
if(IsSelection() && !WriteClipboard())
PromptOK("Chyba pøi zápisu do schránky.");
}
void PathEditorCtrl::ToolEditCut(Bar& bar)
{
bar.Add(IsSelection(), "Vyjmout", CtrlImg::cut(), THISBACK(OnEditCut))
.Key(K_CTRL_X) //, K_CTRL_DELETE)
.Help("Odstranit vybrané objekty ze stylu a pøesunout je do schránky");
}
void PathEditorCtrl::OnEditCut()
{
if(IsSelection())
if(WriteClipboard())
OnEditDelete();
else
PromptOK("Chyba pøi zápisu do schránky.");
}
void PathEditorCtrl::ToolEditPaste(Bar& bar)
{
bar.Add("Vložit", CtrlImg::paste(), THISBACK(OnEditPaste))
.Key(K_CTRL_V) //, K_SHIFT_INSERT)
.Help("Zkopírovat vybrané objekty do schránky");
}
void PathEditorCtrl::OnEditPaste()
{
if(IsSelection() && !ReadClipboard())
PromptOK("Chyba pøi zápisu do schránky.");
}
void PathEditorCtrl::ToolEditSelectAll(Bar& bar)
{
bar.Add("Vybrat vše", THISBACK(OnEditSelectAll))
.Key(K_CTRL_A)
.Help("Oznaèit všechny úseky jako vybrané");
}
void PathEditorCtrl::OnEditSelectAll()
{
ClearSelection();
Vector<int> to_add;
for(int i = 0; i < traces.GetCount(); i++)
to_add.Add(i);
AddSelection(to_add);
}
void PathEditorCtrl::ToolEditColor(Bar& bar)
{
bar.Add(IsSelection(), "Barva...", PathImg::edit_color(), THISBACK(OnEditColor))
.Key(K_CTRL_R)
.Help("Nastavit barvu vybraných objektù");
}
void PathEditorCtrl::OnEditColor()
{
if(!IsSelection())
return;
bool ok;
Color c = RunDlgSelectColor(traces[GetSelectionLeader()].color, false, "Barva objektù...", &ok);
if(ok)
for(int i = 0; i < selection.GetCount(); i++)
SetObject(selection[i], traces[selection[i]].GetTraceColor(c));
}
void PathEditorCtrl::ToolEditDelete(Bar& bar)
{
bar.Add(IsSelection(), "Smazat", CtrlImg::remove(), THISBACK(OnEditDelete))
.Key(K_DELETE)
.Help("Smazat vybrané objekty");
}
void PathEditorCtrl::OnEditDelete()
{
RefreshSelection();
Vector<int> sel = selection.PickKeys();
selection.Clear();
Sort(sel);
while(!sel.IsEmpty())
traces.Remove(sel.Pop());
WhenRescan();
Update();
UpdateSample();
}
void PathEditorCtrl::ToolView(Bar& bar)
{
ToolViewZoomIn(bar);
ToolViewZoomOut(bar);
if(bar.IsMenuBar())
ToolViewZoomFull(bar);
ToolViewPan(bar);
bar.MenuSeparator();
ToolViewZoomHorzIn(bar);
ToolViewZoomHorzOut(bar);
ToolViewZoomVertIn(bar);
ToolViewZoomVertOut(bar);
}
void PathEditorCtrl::ToolViewZoomIn(Bar& bar)
{
bar.Add("Zvìtšit", PathImg::view_zoom_in(), THISBACK(OnViewZoomIn))
.Check(edit_mode == EDIT_ZOOM)
.Help("Zvìtšit vybrané místo nebo oblast");
}
void PathEditorCtrl::OnViewZoomIn()
{
edit_mode = (edit_mode == EDIT_ZOOM ? EDIT_NORMAL : EDIT_ZOOM);
WhenRescan();
}
void PathEditorCtrl::ToolViewZoomOut(Bar& bar)
{
bar.Add("Zmenšit", PathImg::view_zoom_out(), THISBACK(OnViewZoomOut))
.Help("Zmenšit mìøítko zobrazení stylu èáry");
}
void PathEditorCtrl::OnViewZoomOut()
{
OnViewZoomHorzOut();
OnViewZoomVertOut();
}
void PathEditorCtrl::ToolViewZoomFull(Bar& bar)
{
bar.Add("Podle okna", PathImg::view_zoom_full(), THISBACK(OnViewZoomFull))
.Help("Nastavit mìøítko zobrazení podle velikosti okna");
}
void PathEditorCtrl::OnViewZoomFull()
{
OnViewZoomHorzFull();
OnViewZoomVertFull();
}
void PathEditorCtrl::ToolViewZoomHorzIn(Bar& bar)
{
bar.Add("Horiz. zvìtšit", THISBACK(OnViewZoomHorzIn))
.Help("Zvìtšit mìøítko vodorovné osy");
}
void PathEditorCtrl::OnViewZoomHorzIn()
{
int half = GetSize().cx >> 1;
double mpos = hruler.FromClient(half);
hruler.SetZoom(min(hruler.GetScale() * 1.5, 1000.0), 0);
hruler.SetZoom(hruler.GetScale(), half - hruler.ToClient(mpos));
UpdateScroll();
}
void PathEditorCtrl::ToolViewZoomHorzOut(Bar& bar)
{
bar.Add("Horiz. zmenšit", THISBACK(OnViewZoomHorzOut))
.Help("Zmenšit mìøítko vodorovné osy");
}
void PathEditorCtrl::OnViewZoomHorzOut()
{
int half = GetSize().cx >> 1;
double mpos = hruler.FromClient(half);
hruler.SetZoom(max(hruler.GetScale() / 1.5, 1.0), 0);
hruler.SetZoom(hruler.GetScale(), half - hruler.ToClient(mpos));
UpdateScroll();
}
void PathEditorCtrl::ToolViewZoomHorzFull(Bar& bar)
{
bar.Add("Horiz. podle okna", THISBACK(OnViewZoomHorzFull))
.Help("Nastavit vodorovné mìøítko podle velikosti okna");
}
void PathEditorCtrl::OnViewZoomHorzFull()
{
int avail = GetSize().cx - 2 * GAP;
double wd = max(1e-3, style->begin + style->segment + style->end);
hruler.SetZoom(avail / wd, GAP);
UpdateScroll();
}
void PathEditorCtrl::ToolViewZoomVertIn(Bar& bar)
{
bar.Add("Vert. zvìtšit", THISBACK(OnViewZoomVertIn))
.Help("Zvìtšit mìøítko svislé osy");
}
void PathEditorCtrl::OnViewZoomVertIn()
{
int half = GetSize().cy >> 1;
double mpos = vruler.FromClient(half);
vruler.SetZoom(min(vruler.GetScale() * 1.5, 1000.0), 0);
vruler.SetZoom(vruler.GetScale(), half - vruler.ToClient(mpos));
UpdateScroll();
}
void PathEditorCtrl::ToolViewZoomVertOut(Bar& bar)
{
bar.Add("Vert. zmenšit", THISBACK(OnViewZoomVertOut))
.Help("Zmenšit mìøítko svislé osy");
}
void PathEditorCtrl::OnViewZoomVertOut()
{
int half = GetSize().cy >> 1;
double mpos = vruler.FromClient(half);
vruler.SetZoom(max(vruler.GetScale() / 1.5, 1.0), 0);
vruler.SetZoom(vruler.GetScale(), half - vruler.ToClient(mpos));
UpdateScroll();
}
void PathEditorCtrl::ToolViewZoomVertFull(Bar& bar)
{
bar.Add("Vert. podle okna", THISBACK(OnViewZoomVertFull))
.Help("Nastavit mìøítko svislé osy podle velikosti okna");
}
void PathEditorCtrl::OnViewZoomVertFull()
{
int avail = GetSize().cy - 2 * GAP;
double ht = max(1e-3, style->width);
vruler.SetZoom(avail / ht, GetSize().cy >> 1);
UpdateScroll();
}
void PathEditorCtrl::ToolViewPan(Bar& bar)
{
bar.Add("Posunout", PathImg::view_pan(), THISBACK(OnViewPan))
.Check(edit_mode == EDIT_PAN)
.Help("Posunout myší zobrazený výøez");
}
void PathEditorCtrl::OnViewPan()
{
edit_mode = (edit_mode == EDIT_PAN ? EDIT_NORMAL : EDIT_PAN);
WhenRescan();
}
void PathEditorCtrl::ToolSetup(Bar& bar)
{
ToolSetupGrid(bar);
ToolSetupSnap(bar);
ToolSetupRuler(bar);
ToolSetupAxis(bar);
// bar.ThinBar();
bar.Separator();
ToolSetupStyle(bar);
ToolSetupSetup(bar);
if(bar.IsToolBar())
{
bar.Separator();
// bar.ThinBar();
bar.Add(sample_size_tag);
bar.Add(sample_size.SizePos(), 80);
bar.Add(sample_width_tag);
bar.Add(sample_width.SizePos(), 40);
}
}
void PathEditorCtrl::ToolSetupGrid(Bar& bar)
{
bar.Add("Møížka", PathImg::setup_grid(), THISBACK(OnSetupGrid))
.Check(setup.do_grid)
.Help("Zobrazit / skrýt pomocnou møížku");
}
void PathEditorCtrl::OnSetupGrid()
{
setup.do_grid = !setup.do_grid;
WhenRescan();
Refresh();
}
void PathEditorCtrl::ToolSetupRuler(Bar& bar)
{
bar.Add("Pravítko", PathImg::setup_ruler(), THISBACK(OnSetupRuler))
.Check(setup.do_ruler)
.Help("Zobrazit / skrýt pravítko");
}
void PathEditorCtrl::OnSetupRuler()
{
setup.do_ruler = !setup.do_ruler;
WhenRescan();
Layout();
}
void PathEditorCtrl::ToolSetupAxis(Bar& bar)
{
bar.Add("Osa", PathImg::setup_axis(), THISBACK(OnSetupAxis))
.Check(setup.do_axis)
.Help("Zobrazit / skrýt osu ukázkové úseèky");
}
void PathEditorCtrl::OnSetupAxis()
{
setup.do_axis = !setup.do_axis;
WhenRescan();
UpdateSample();
}
void PathEditorCtrl::ToolSetupSnap(Bar& bar)
{
bar.Add("Pøichytit do møížky", PathImg::setup_snap(), THISBACK(OnSetupSnap))
.Check(setup.do_snap)
.Help("Pøichytit souøadnice myši do møížky");
}
void PathEditorCtrl::OnSetupSnap()
{
setup.do_snap = !setup.do_snap;
WhenRescan();
Refresh();
}
void PathEditorCtrl::ToolSetupStyle(Bar& bar)
{
bar.Add("Styl èáry", THISBACK(OnSetupStyle))
.Help("Zobrazit/zmìnit vlastnosti stylu èáry");
}
void PathEditorCtrl::OnSetupStyle()
{
if(RunDlgPathStyleSetup(*style))
{
WhenRescan();
Layout();
UpdateSample();
}
}
void PathEditorCtrl::ToolSetupSetup(Bar& bar)
{
bar.Add("Editor", PathImg::setup_setup(), THISBACK(OnSetupSetup))
.Help("Zobrazit/zmìnit vlastnosti editoru stylù èar");
}
void PathEditorCtrl::OnSetupSetup()
{
if(RunDlgPathEditorSetup(setup))
{
WhenRescan();
Layout();
}
}
//////////////////////////////////////////////////////////////////////
// DlgPathEditor::
class DlgPathEditor
{
public:
typedef DlgPathEditor CLASSNAME;
DlgPathEditor();
bool Run(PathStyle& style, const String& title);
void Serialize(Stream& stream);
static ConfigItem& config();
void Rescan() { tool_bar.Set(THISBACK(ToolRoot)); menu_bar.Set(THISBACK(ToolRoot)); }
public:
TOOL(Root)
TOOL(Edit)
TOOL(EditSave)
private:
TopWindow dialog;
MenuBar menu_bar;
ToolBar tool_bar;
StatusBar status_bar;
EditField coords;
PathEditorCtrl path;
};
CONFIG_ITEM(DlgPathEditor::config, "DlgPathEditor", 1, 1, 1)
bool RunDlgPathEditor(PathStyle& style, const String& title) { return DlgPathEditor().Run(style, title); }
//////////////////////////////////////////////////////////////////////
// DlgPathEditor::
DlgPathEditor::DlgPathEditor()
{
#ifdef DEBUG_DRAW
dialog.NoBackPaint();
#endif
dialog.AddFrame(menu_bar);
dialog.AddFrame(tool_bar);
dialog.AddFrame(status_bar);
status_bar << coords.VSizePos(0, 0).RightPos(0, 200);
coords.SetReadOnly();
menu_bar.Set(THISBACK(ToolRoot));
tool_bar.Set(THISBACK(ToolRoot));
dialog << path.SizePos();
dialog.Sizeable().Zoomable();
dialog.SetMinSize(Size(300, 200));
path.WhenRescan = THISBACK(Rescan);
}
bool DlgPathEditor::Run(PathStyle& style, const String& title)
{
dialog.Title(Nvl(title, "Styl èáry").ToWString());
ReadConfigSelf();
path.SetOwner(style);
path.SetCoords(coords);
path.PumpTraces(false);
dialog.Open();
path.OnViewZoomFull();
bool ok = (dialog.Run() == IDOK);
WriteConfigSelf();
if(ok)
path.PumpTraces(true);
Ctrl::IgnoreMouseUp();
return ok;
}
void DlgPathEditor::Serialize(Stream& stream)
{
stream % path.setup;
if(stream.IsLoading())
path.UpdateSetup();
}
void DlgPathEditor::ToolRoot(Bar& bar)
{
bar.Add("Edit", THISBACK(ToolEdit))
.Help("Základní editaèní pøíkazy");
bar.Add("Pohled", callback(&path, &PathEditorCtrl::ToolView))
.Help("Mìøítko a poloha zobrazeného výøezu");
bar.Add("Vlastnosti", callback(&path, &PathEditorCtrl::ToolSetup))
.Help("Vlastnosti èáry a editoru");
}
void DlgPathEditor::ToolEdit(Bar& bar)
{
if(bar.IsToolBar())
{
ToolEditSave(bar);
// bar.ThinBar();
bar.Separator();
}
path.ToolEdit(bar);
if(bar.IsMenuBar())
{
bar.Separator();
ToolEditSave(bar);
}
}
void DlgPathEditor::ToolEditSave(Bar& bar)
{
bar.Add("Uložit a zavøít", CtrlImg::save(), THISBACK(OnEditSave))
.Key(K_CTRL_S)
.Help("Zavøít editor a použít nadefinovaný styl èáry");
}
void DlgPathEditor::OnEditSave()
{
dialog.AcceptBreak(IDOK);
}
template <class T>
class WithPopupEdit : public T
{
public:
WithPopupEdit();
virtual void Deactivate();
virtual bool Key(dword key, int);
bool PopUp(Ctrl *parent, const Rect& prect);
public:
Callback WhenCancel;
Callback WhenSelect;
protected:
virtual void DoClose(bool ok);
protected:
bool open;
};
template <class T>
WithPopupEdit<T>::WithPopupEdit()
{
this->SetFrame(BlackFrame());
open = false;
}
template <class T>
void WithPopupEdit<T>::Deactivate()
{
if(open) {
if(!this->Accept())
{
this->SetFocus();
return;
}
DoClose(true);
this->IgnoreMouseClick();
WhenSelect();
}
}
template <class T>
void WithPopupEdit<T>::DoClose(bool ok)
{
open = false;
// EndModalLoop(ok ? 1 : 0);
this->EndLoop(ok ? 1 : 0);
}
template <class T>
bool WithPopupEdit<T>::Key(dword key, int repcnt)
{
if(key == K_ENTER)
{
if(this->Accept())
{
DoClose(true);
WhenSelect();
}
return true;
}
else if(key == K_ESCAPE)
{
this->Reject();
DoClose(false);
WhenCancel();
return true;
}
return T::Key(key, repcnt);
}
template <class T>
bool WithPopupEdit<T>::PopUp(Ctrl *parent, const Rect& prect)
{
ASSERT(parent);
if(!this->IsOpen())
{
Ctrl *wnd = parent->GetTopCtrl();
if(!wnd || !wnd->IsOpen() || !wnd->IsVisible())
return false;
open = false;
this->SetRect(prect);
T::PopUp(parent);
}
this->Enable();
this->SetFocus();
open = true;
EventLoop(this);
bool ok = !!GetExitCode();
// bool ok = !!RunModalLoop();
this->Disable();
return ok;
}
PathStyleMapCtrl::PathStyleMapCtrl()
{
cursor = -1;
map = 0;
scroll_pos = 0;
SetFrame(InsetFrame());
scroll.NoAutoHide().NoAutoDisable();
scroll.WhenScroll = THISBACK(OnScroll);
AddFrame(scroll);
WhenBar = THISBACK(ToolLocal);
}
void PathStyleMapCtrl::Set(PathStyleMap *m)
{
map = m;
Layout();
}
bool PathStyleMapCtrl::Key(dword key, int repcnt)
{
int c = cursor;
int pg = max(1, GetSize().cy / pos_add.cx - 1);
switch(key)
{
case K_LEFT: c--; break;
case K_RIGHT: c++; break;
case K_UP: c -= count.cx; break;
case K_DOWN: c += count.cx; break;
case K_PAGEUP: c -= count.cx * pg; break;
case K_PAGEDOWN: c += count.cx * pg; break;
case K_HOME: c = 0; break;
case K_END: c = map->GetCount() - 1; break;
default:
return Bar::Scan(WhenBar, key) || Ctrl::Key(key, repcnt);
}
SetCursor(minmax(c, 0, map->GetCount() - 1));
return true;
}
void PathStyleMapCtrl::Layout()
{
if(!map)
{
count = total = Size(0, 0);
pos_add = Size(1, 1);
cell = Size(0, 0);
gap_offset = offset = Point(0, 0);
scroll_pos = 0;
return;
}
Size size = scroll.GetReducedViewSize();
count.cx = max(1, (size.cx - GAP) / WIDTH);
count.cy = idivceil(map->map.GetCount(), count.cx);
pos_add.cx = (size.cx - GAP) / count.cx;
pos_add.cy = HEIGHT + GAP;
cell = pos_add - GAP;
total = count * pos_add - GAP;
offset.x = (size.cx - total.cx) >> 1;
offset.y = GAP;
gap_offset.x = offset.x - (GAP >> 1);
gap_offset.y = GAP >> 1;
scroll.Set(scroll_pos, size.cy, 2 * GAP + total.cy);
scroll_pos = scroll;
Refresh();
}
Rect PathStyleMapCtrl::GetEditRect(int right, int bottom)
{
Size size = PathImg::rename().GetSize();
return RectC(right - size.cx, bottom - size.cy, size.cx, size.cy);
}
bool PathStyleMapCtrl::InEditRect(int right, int bottom, Point mouse)
{
Rect rc = GetEditRect(right, bottom).Inflated(2);
if(!rc.Contains(mouse))
return false;
Point pt = rc.CenterPoint();
return mouse.x + mouse.y >= pt.x + pt.y;
}
void PathStyleMapCtrl::Paint(Draw& draw)
{
Rect rc = draw.GetPaintRect();
draw.Begin();
Size todo = ClientToRange(rc);
for(int i = todo.cx; i < todo.cy; i++)
{
Rect item = IndexToClient(i);
if(item && rc)
{
DrawFrame(draw, item, Black, White);
draw.DrawRect(item.Deflated(1), i == cursor ? LtCyan : SLtGray);
Rect box = item.Deflated(IGAP);
draw.Clipoff(Rect(box.left, box.top, box.right, box.top + TEXT_HEIGHT));
String qtf;
qtf << "[=A+108";
if(i == cursor)
qtf << '*';
qtf << " \1" << map->GetSortName(i);
Document doc(qtf);
doc.Paint(DOC_SCREEN_ZOOM, draw, 0, 0, box.Width(), SLtGray);
draw.DrawImage(GetEditRect(box.Width(), TEXT_HEIGHT), PathImg::rename());
draw.End();
draw.Clipoff(Rect(box.left, box.top + TEXT_HEIGHT, box.right, box.bottom));
PathDraw path;
path.Set(draw, map->GetSortStyle(i), Black, LINE_HEIGHT / 2);
Rect er = GetEditRect(box.Width(), box.Height() - TEXT_HEIGHT);
path.Line(0, LINE_HEIGHT / 2, er.left, LINE_HEIGHT / 2);
path.Paint();
draw.DrawImage(er, PathImg::edit());
draw.End();
draw.ExcludeClip(item);
}
}
draw.DrawRect(rc, SLtGray);
draw.End();
}
int PathStyleMapCtrl::ClientToIndex(Point pt) const
{
pt = idivfloor(pt - gap_offset + Size(0, scroll_pos), pos_add);
if(pt.x < 0 || pt.x >= count.cx || pt.y < 0)
return -1;
return pt.x + pt.y * count.cx;
}
Size PathStyleMapCtrl::ClientToRange(const Rect& rc) const
{
if(rc.IsEmpty())
return Size(-1, -1);
int top = idivfloor(rc.top - gap_offset.y + scroll_pos, pos_add.cy);
int bottom = idivfloor(rc.bottom - 1 - gap_offset.y + scroll_pos, pos_add.cy);
int left = idivfloor(rc.left - gap_offset.x, pos_add.cx);
int right = idivfloor(rc.right - 1 - gap_offset.x, pos_add.cx);
if(bottom < 0 || right < 0 || left >= count.cx)
return Size(-1, -1);
if(top < bottom) // multiple lines
return Size(max(0, count.cx * top), min(map->GetCount(), count.cx * (bottom + 1)));
return Size(max(0, count.cx * top + max(0, left)), min(map->GetCount(), count.cx * top + min(count.cx, right + 1)));
}
Rect PathStyleMapCtrl::IndexToClient(int i) const
{
return Rect(offset - Size(0, scroll_pos) + pos_add * Size(i % count.cx, i / count.cx), cell);
}
void PathStyleMapCtrl::OnScroll()
{
scroll_pos = scroll;
Refresh();
}
void PathStyleMapCtrl::LeftDown(Point pt, dword keyflags)
{
int i = ClientToIndex(pt);
if(IsValid(i))
SetCursor(i);
SetWantFocus();
Rect rc = IndexToClient(i).Deflated(IGAP);
if(InEditRect(rc.right, rc.top + TEXT_HEIGHT, pt))
OnRename();
if(InEditRect(rc.right, rc.bottom, pt))
OnEdit();
}
void PathStyleMapCtrl::LeftDouble(Point pt, dword keyflags)
{
int i = ClientToIndex(pt);
if(IsCursor() && i == cursor)
WhenLeftDouble();
}
void PathStyleMapCtrl::LeftUp(Point pt, dword keyflags)
{
}
void PathStyleMapCtrl::RightDown(Point pt, dword keyflags)
{
int i = ClientToIndex(pt);
if(IsValid(i))
SetCursor(i);
SetWantFocus();
MenuBar::Execute(this, WhenBar, UPP::GetMousePos());
}
void PathStyleMapCtrl::SetCursor(int c)
{
RefreshItem(cursor);
RefreshItem(cursor = c);
if(IsCursor() && IsOpen() && IsVisible())
{
Rect rc = IndexToClient(c) + Size(0, scroll);
rc.Inflate(GAP);
scroll.ScrollInto(rc.top, rc.Height());
}
}
void PathStyleMapCtrl::RefreshItem(int i)
{
if(i >= 0 && i < map->map.GetCount())
Refresh(IndexToClient(i));
}
void PathStyleMapCtrl::ToolLocal(Bar& bar)
{
ToolNew(bar);
ToolEdit(bar);
ToolRename(bar);
ToolCopy(bar);
bar.Separator();
ToolExport(bar);
ToolImport(bar);
bar.Separator();
ToolRemove(bar);
}
void PathStyleMapCtrl::ToolNew(Bar& bar)
{
bar.Add("Nový", THISBACK(OnNew));
}
void PathStyleMapCtrl::OnNew()
{
PathStyle new_style;
new_style <<= PathStyle::solid();
String px = "Nový styl";
if(RunDlgPathEditor(new_style, px))
{
String n = map->GetUniqueName(px);
map->Set(n, new_style);
map->Touch();
Layout();
SetCursor(n);
OnRename();
}
}
void PathStyleMapCtrl::ToolEdit(Bar& bar)
{
bar.Add(IsCursor(), "Upravit", THISBACK(OnEdit));
}
void PathStyleMapCtrl::OnEdit()
{
if(IsCursor())
{
int c = GetCursor();
String n = map->GetSortName(c);
PathStyle edited_style;
edited_style <<= map->GetSortStyle(c);
if(RunDlgPathEditor(edited_style, "Upravit styl '" + n + "'"))
{
map->Set(n, edited_style);
map->Touch();
RefreshItem(c);
}
}
}
void PathStyleMapCtrl::ToolRename(Bar& bar)
{
bar.Add(IsCursor(), "Pøejmenovat", THISBACK(OnRename));
}
void PathStyleMapCtrl::OnRename()
{
if(!IsCursor())
{
BeepExclamation();
return;
}
int i = GetCursor();
Rect rc = IndexToClient(i) + Size(0, scroll);
scroll.ScrollInto(rc.top, rc.Height());
rc = IndexToClient(i) + GetScreenView().TopLeft();
rc.bottom = rc.top + 2 * IGAP + TEXT_HEIGHT;
rc.Deflate(IGAP);
WithPopupEdit<DocEdit> pedit;
pedit <<= map->GetSortName(i);
while(pedit.PopUp(this, rc))
{
String n = ~pedit;
if(map->FindSortName(n, i) >= 0)
{ // duplicate name
Exclamation("Styl èáry [* \1" + n + "\1] již v systému existuje. Zadejte prosím jiný název.");
continue;
}
int x = map->GetSort(i);
map->Rename(x, n);
map->Touch();
Refresh();
SetCursor(n);
break;
}
}
void PathStyleMapCtrl::ToolCopy(Bar& bar)
{
bar.Add("Duplikovat", THISBACK(OnCopy));
}
void PathStyleMapCtrl::OnCopy()
{
if(IsCursor())
{
int c = GetCursor();
String prefix = map->GetSortName(c);
int l = prefix.GetLength();
if(l > 0 && IsDigit(prefix[l - 1]))
{
while(l > 0 && IsDigit(prefix[l - 1]))
l--;
if(l > 0 && prefix[l - 1] == ' ')
prefix.Trim(l - 1);
}
PathStyle new_style;
new_style <<= map->GetSortStyle(c);
String q = map->GetUniqueName(prefix);
map->Set(q, new_style);
map->Touch();
Layout();
SetCursor(q);
OnRename();
}
}
void PathStyleMapCtrl::ToolExport(Bar& bar)
{
bar.Add("Export", THISBACK(OnExport))
.Help("Exportovat styly èar do souboru");
}
String recent;
void PathStyleMapCtrl::OnExport()
{
if(map->IsEmpty())
{
PromptOK("V systému nebyly nalezeny žádné styly èar.");
return;
}
FileSelector fsel;
fsel.Type("Styly èar (*.wml)", "*.wml")
.DefaultExt("wml");
fsel <<= recent;
if(!fsel.ExecuteSaveAs("Exportovat styly èar..."))
return;
String exp = map->Export();
if(!SaveFile(~fsel, exp))
PromptOK("Chyba pøi zápisu do souboru [* \1" + ~fsel + "\1].");
}
void PathStyleMapCtrl::ToolImport(Bar& bar)
{
bar.Add("Import", THISBACK(OnImport))
.Help("Importovat styly èar ze souboru...");
}
void PathStyleMapCtrl::OnImport()
{
FileSelector fsel;
fsel.Type("Styly èar (*.wml)", "*.wml")
.DefaultExt("wml");
fsel <<= recent;
if(!fsel.ExecuteOpen("Importovat styly èar"))
return;
String imp = LoadFile(recent = ~fsel);
if(imp.IsVoid())
{
PromptOK("Nelze naèíst soubor [* \1" + ~fsel + "\1].");
return;
}
int old_count = map->GetCount();
String old_name = map->GetName();
try
{
map->Import(imp);
}
catch(Exc e)
{
PromptOK("Chyba pøi importu souboru [* \1" + ~fsel + "\1]: \1" + e + "\1.");
}
map->Touch();
if((old_count > 0 && !IsNull(old_name)) || IsNull(map->GetName()))
map->SetName(old_name);
if(map->GetCount() > old_count)
SetCursor(map->map.GetKey(old_count));
WhenRename();
}
void PathStyleMapCtrl::ToolRemove(Bar& bar)
{
bar.Add("Smazat", THISBACK(OnRemove))
.Help("Odstranit styl èáry ze systému");
}
void PathStyleMapCtrl::OnRemove()
{
if(IsCursor())
{
int c = cursor;
map->Remove(map->GetSortName(c));
map->Touch();
c = min(c, map->GetCount() - 1);
Layout();
SetCursor(c);
}
}
class PathStyleMapLayoutEx : public WithPathStyleMapLayout<TopWindow>
{
public:
PathStyleMapCtrl browser;
};
//////////////////////////////////////////////////////////////////////
// DlgPathStyleMap::
class DlgPathStyleMap
{
public:
typedef DlgPathStyleMap CLASSNAME;
DlgPathStyleMap();
bool Run(PathStyleMap& map, String& style, bool editor = false, bool read_only = false);
private:
// void UpdateMaps();
// void OnTab();
private:
PathStyleMapLayoutEx dialog;
PathStyleMap *map;
// const Vector<PathStyleMap *> *maps;
// Vector<int> map_index;
};
bool RunDlgPathStyleMap(PathStyleMap& map, String& style, bool editor, bool read_only)
{ return DlgPathStyleMap().Run(map, style, editor, read_only); }
RegisterHelpTopicObjectTitle(DlgPathStyleMap, "Mapa stylù")
DlgPathStyleMap::DlgPathStyleMap()
{
map = 0;
// HelpLayoutOKCancel(dialog, this);
CtrlLayoutOKCancel(dialog, DlgPathStyleMapHelpTitle());
dialog.HelpTopic("DlgPathStyleMap");
dialog.Sizeable().MaximizeBox();
// dialog.browser.WhenRename = THISBACK(UpdateMaps);
dialog.tab.Hide();
// dialog.tab <<= THISBACK(OnTab);
}
bool DlgPathStyleMap::Run(PathStyleMap& m, String& style, bool editor, bool read_only)
{
if(editor) {
dialog.ok.Hide();
dialog.cancel.SetLabel(t_("Close"));
}
map = &m;
// ASSERT(!maps->IsEmpty());
// UpdateMaps();
dialog.browser.Set(map);
dialog.browser.SetCursor(Nvl(style, ".solid"));
while(dialog.Run() == IDOK) {
int c = dialog.browser.GetCursor();
if(c >= 0) {
style = map->GetSortName(c);
if(style == ".solid") style = Null;
return true;
}
BeepExclamation();
}
return false;
}
/*
void DlgPathStyleMap::UpdateMaps()
{
int tab = dialog.tab.Get();
tab = (tab >= 0 && tab < map_index.GetCount() ? map_index[tab] : -1);
map_index = GetSortOrder(*maps, XRelation<const PathStyleMap *>(StdCsNumSort, XDeref(XField(&PathStyleMap::name))));
dialog.tab.Clear();
int new_tab = -1;
for(int i = 0; i < map_index.GetCount(); i++)
{
int x = map_index[i];
dialog.tab.Add(Nvl((*maps)[x]->GetName(), "(bez názvu)"));
if(x == tab)
new_tab = i;
}
dialog.tab.Set(new_tab);
}
*/
/*
void DlgPathStyleMap::OnTab()
{
int x = dialog.tab.Get();
if(x >= 0 && x < map_index.GetCount())
dialog.browser.Set((*maps)[map_index[x]]);
}
*/
PathStyleCtrl::PathStyleCtrl()
: path_map(0)
{
SetDisplay(display);
}
void PathStyleCtrl::DoAction()
{
String s = GetData();
if(RunDlgPathStyleMap(*path_map, s, false, false))
SetDataAction(s);
}
}