#include "VectorDes.h" #pragma hdrstop #define LLOG(x) // RLOG(x) #define IMAGEFILE #define IMAGECLASS VecImg #include #define LAYOUTFILE #include static const char setup_key[] = "vectordes-setup"; INITBLOCK { RegisterGlobalConfig(setup_key); }; class VectorFontCtrl : public WithVectorDesFontLayout { public: typedef VectorFontCtrl CLASSNAME; VectorFontCtrl(); virtual Value GetData() const; virtual void SetData(const Value& v); }; VectorFontCtrl::VectorFontCtrl() { CtrlLayout(*this); VectorMap face_map; for(int i = 0; i < Font::GetFaceCount(); i++) face_map.FindAdd(Font::GetFaceName(i), i); Vector fname = face_map.PickKeys(); Vector find = face_map.PickValues(); IndexSort(fname, find, GetLanguageInfo()); for(int i = 0; i < fname.GetCount(); i++) font.Add(find[i], fname[i]); font <<= height <<= bold <<= italic <<= underline <<= strikeout <<= THISBACK(Action); } Value VectorFontCtrl::GetData() const { Font out; out.Face(~font).Height(Nvl((int)~height, 0)) .Bold(bold).Italic(italic).Underline(underline).Strikeout(strikeout); return out; } void VectorFontCtrl::SetData(const Value& v) { Font f = v; font <<= f.GetFace(); height <<= f.GetHeight(); bold = f.IsBold(); italic = f.IsItalic(); underline = f.IsUnderline(); strikeout = f.IsStrikeout(); } One CreateVectorFontCtrl() { return new VectorFontCtrl; } class DlgVectorDesSetup : public WithVectorDesSetupLayout { public: typedef DlgVectorDesSetup CLASSNAME; DlgVectorDesSetup(); bool Run(VectorCtrl::Setup& setup); private: void OnSnap(); void OnGrid(); }; DlgVectorDesSetup::DlgVectorDesSetup() { CtrlLayoutOKCancel(*this, "Preferences"); snap <<= THISBACK(OnSnap); grid <<= THISBACK(OnGrid); } bool DlgVectorDesSetup::Run(VectorCtrl::Setup& setup) { CtrlRetriever rtvr; rtvr (grid, setup.grid) (grid_cx, setup.grid_size.cx) (grid_cy, setup.grid_size.cy) (grid_style, setup.grid_style) (grid_above_objects, setup.grid_above_objects) (snap, setup.snap) (snap_cx, setup.snap_size.cx) (snap_cy, setup.snap_size.cy) (snap_to_objects, setup.snap_to_objects) ; snap_to_objects.Hide(); // temporary OnGrid(); OnSnap(); if(TopWindow::Run() != IDOK) return false; rtvr.Retrieve(); return true; } void DlgVectorDesSetup::OnSnap() { snap_cx.Enable(snap); snap_cy.Enable(snap); } void DlgVectorDesSetup::OnGrid() { grid_cx.Enable(grid); grid_cy.Enable(grid); grid_style.Enable(grid); } inline Rect SortRect(Point p1, Point p2) { return Rect(min(p1.x, p2.x), min(p1.y, p2.y), max(p1.x, p2.x) + 1, max(p1.y, p2.y) + 1); } bool IsDragDistance(Point pt1, Point pt2) { #ifdef PLATFORM_WIN32 return tabs(pt1.x - pt2.x) >= GetSystemMetrics(SM_CXDRAG) || tabs(pt1.y - pt2.y) >= GetSystemMetrics(SM_CYDRAG); #endif #ifdef PLATFORM_POSIX enum { CXDRAG = 4, CYDRAG = 4 }; // todo? are there any CXDRAG / CYDRAG system metrics in LINUX? return tabs(pt1.x - pt2.x) >= CXDRAG || tabs(pt1.y - pt2.y) >= CYDRAG; #endif } int GetRectDragMask(Rect rc, Point pt, int tolerance) { Point center = rc.CenterPoint(); int m = (tabs(rc.left - pt.x) <= tolerance ? 1 : 0) | (tabs(rc.top - pt.y) <= tolerance ? 2 : 0) | (tabs(rc.right - pt.x) <= tolerance ? 4 : 0) | (tabs(rc.bottom - pt.y) <= tolerance ? 8 : 0); if(m & 5) if((m & 10) || tabs(center.y - pt.y) <= tolerance) return m; if(m & 10) if((m & 5) || tabs(center.x - pt.x) <= tolerance) return m; return 0; } DragDropCtrl::DragDropCtrl() : state(0) { } bool DragDropCtrl::Push(Point pt, dword keyflags) { return true; } void DragDropCtrl::Drag(Point pt, Point last, Point next, dword keyflags) { Rect rc_last = Null, rc_next = Null; if(!IsNull(last)) rc_last = RectSort(pt, last); if(!IsNull(next)) rc_next = RectSort(pt, next); if(rc_last != rc_next) DragRect(rc_last, rc_next, keyflags); } void DragDropCtrl::Drop(Point pt, Point end, dword keyflags) { DropRect(RectSort(pt, end), keyflags); } void DragDropCtrl::Click(Point pt, dword keyflags) { // no-op, should be implemented in derived class } void DragDropCtrl::DragRect(const Rect& last, const Rect& next, dword keyflags) { ViewDraw draw(this); DrawDragRect(draw, last, next, draw.GetClip(), 1, Yellow(), NULL); } void DragDropCtrl::DropRect(const Rect& rc, dword keyflags) { // no-op, should be implemented in derived class } void DragDropCtrl::DragShow(bool _show) { if(_show && state == HIDDEN) { Drag(start, Null, last, last_key); state = ON; } if(!_show && state == ON) { Drag(start, last, Null, last_key); state = HIDDEN; } } void DragDropCtrl::LeftDown(Point pt, dword keyflags) { SetWantFocus(); LLOG("DragDropCtrl::LeftDown -> " << pt << ", keyflags " << FormatIntHex(keyflags)); if(Push(pt, last_key = keyflags)) { // begin drag & drop state = POSSIBLE; start = last = pt; SetCapture(); } } void DragDropCtrl::LeftDouble(Point pt, dword keyflags) { SetWantFocus(); Click(pt, keyflags | DBLCLK); } void DragDropCtrl::DragStop(bool accept, dword keyflags) { ReleaseCapture(); DragHide(); if(state == HIDDEN && accept) Drop(start, last, last_key = keyflags); else if(state == POSSIBLE && accept) Click(start, last_key = keyflags); state = OFF; } void DragDropCtrl::LeftUp(Point pt, dword keyflags) { LLOG("DragDropCtrl::LeftUp -> " << pt); DragStop(true, keyflags); } void DragDropCtrl::MouseMove(Point pt, dword keyflags) { LLOG("DragDropCtrl::MouseMove -> " << pt); if(keyflags != last_key) DragHide(); if(state == POSSIBLE && IsDragDistance(pt, start)) { state = ON; Drag(start, Null, last = pt, last_key = keyflags); } else if(state == ON || state == HIDDEN) { Point plast = (state == ON ? last : Point(Null)); last = pt; last_key = keyflags; state = ON; Drag(start, plast, pt, last_key); } } bool DragDropCtrl::Key(dword key, int repcnt) { if(key == K_ESCAPE) { DragStop(false); return true; } return Ctrl::Key(key, repcnt); } VectorCtrl::Setup::Setup() { snap = grid = snap_to_objects = grid_above_objects = false; grid_style = GRID_POINTS; grid_size = Size(8, 8); snap_size = Size(4, 4); } void VectorCtrl::Setup::Serialize(Stream& stream) { int version = 2; stream / version; stream.Pack(grid, snap, snap_to_objects, grid_above_objects); stream % grid_size % snap_size; if(version >= 2) stream / grid_style; } void VectorCtrl::Setup::LoadGlobal() { LoadFromGlobal(*this, setup_key); } void VectorCtrl::Setup::SaveGlobal() { StoreToGlobal(*this, setup_key); } VectorCtrl::VectorCtrl() { BackPaint(); editmode = EDIT_TRACK; tool_coords.SetReadOnly().NoWantFocus(); SetFrame(InsetFrame()); AddFrame(scrollbars); scrollbars.NoAutoHide().NoAutoDisable(); scrollbars.WhenScroll = THISBACK(OnScroll); SetZoom(Zoom(1, 1)); trackmode = Point(-1, -1); setup.LoadGlobal(); } void VectorCtrl::SetData(const Value& value) { SetImage(value); } Value VectorCtrl::GetData() const { return image; } void VectorCtrl::SetZoom(::Zoom z) { zoom = z; Layout(); } void VectorCtrl::Layout() { pixel_size = zoom * image.GetSize(); scrollbars.Set(scrollbars, scrollbars.GetReducedViewSize(), pixel_size + 2 * GAP); Refresh(); } Scaling VectorCtrl::GetImageScaling() const { return Scaling(image.GetSize(), pixel_size); } void VectorCtrl::Paint(Draw& draw) { DragHide(); draw.DrawRect(draw.GetClip(), IsEnabled() ? SColorPaper() : SColorShadow()); Rect rc(pixel_size); rc.Offset(Point(GAP, GAP) - scrollbars); draw.DrawRect(rc, SColorFace()); draw.Offset(rc.TopLeft()); ScalingDraw scaled(&draw, GetImageScaling()); if(!setup.grid_above_objects) PaintGrid(scaled); image.DesignPaint(scaled); Size sz = image.GetSize() * scaled; if(IsEditable() && IsEnabled()) { DrawHotRect(draw, sz.cx, (sz.cy >> 1) - 2, 5, 5, true, LtBlue()); DrawHotRect(draw, (sz.cx >> 1) - 2, sz.cy, 5, 5, true, LtBlue()); DrawHotRect(draw, sz.cx, sz.cy, 5, 5, true, LtBlue()); } for(int i = 0; i < selection.GetCount(); i++) image[selection.GetKey(i)].SelPaint(scaled, selection[i], i == selection.GetCount() - 1); if(setup.grid_above_objects) PaintGrid(scaled); draw.End(); } void VectorCtrl::PaintGrid(ScalingDraw& scaled) { if(!setup.grid) return; if(scaled.X(setup.grid_size.cx) <= 2 || scaled.Y(setup.grid_size.cy) <= 2) return; Rect clip = scaled.draw->GetClip() & Rect(scaled.pixel_size); switch(setup.grid_style) { case Setup::GRID_LINES: { Size sz = image.GetSize(); for(int i = 0; i < sz.cx; i += setup.grid_size.cx) scaled.draw->DrawRect(scaled.X(i), clip.top, 1, clip.Height(), SColorShadow()); for(int i = 0; i < sz.cy; i += setup.grid_size.cy) scaled.draw->DrawRect(clip.left, scaled.Y(i), clip.Width(), 1, SColorShadow()); break; } case Setup::GRID_POINTS: { Size count = image.GetSize() / setup.grid_size + 1; Buffer xpos(count.cx), ypos(count.cy); for(int i = 0; i < count.cx; i++) xpos[i] = scaled.X(i * setup.grid_size.cx); for(int i = 0; i < count.cy; i++) ypos[i] = scaled.Y(i * setup.grid_size.cy); for(int y = 0; y < count.cy; y++) for(int x = 0; x < count.cx; x++) scaled.draw->DrawRect(xpos[x], ypos[y], 1, 1, SColorShadow()); break; } } } void VectorCtrl::MouseMove(Point pt, dword keyflags) { SyncCoords(ClientToSnap(pt)); DragDropCtrl::MouseMove(pt, keyflags); } Image VectorCtrl::CursorImage(Point pt, dword keyflags) { Point mode = trackmode, nearest; if(!IsDragging()) mode = GetTrackMode(pt, nearest); return Nvl(image.Cursor(mode, keyflags), Image::Arrow()); /* switch(mode) { case DM_NEW: return !IsNull(new_icon) ? new_icon : Image::Arrow(); case DM_SELECT: return CtrlImg::HandCursor(); case DM_POINT: return CtrlImg::SizeVeHo0(); case DM_RECT: { switch(rm) { case 1: case 4: return CtrlImg::SizeHorz0(); case 2: case 8: return CtrlImg::SizeVert0(); case 1 | 2: case 4 | 8: return CtrlImg::SizeHoVe0(); case 1 | 8: case 4 | 2: return CtrlImg::SizeVeHo0(); } break; } case DM_MOVE: { return Image::SizeAll(); } case DM_IMG_SIZE: { switch(rm) { case 1: return CtrlImg::SizeHorz0(); case 2: return CtrlImg::SizeVert0(); case 3: return CtrlImg::SizeHoVe0(); } return Image::Arrow(); } } return DragDropCtrl::CursorImage(pt, keyflags); */ } void VectorCtrl::RightDown(Point pt, dword keyflags) { if(IsEditable()) MenuBar::Execute(this, THISBACK(LocalMenu), GetScreenView().TopLeft() + pt); } void VectorCtrl::LocalMenu(Bar& bar) { InsertMenu(bar); bar.MenuSeparator(); EditMenu(bar); } void VectorCtrl::EditMenu(Bar& bar) { bar.Add("Edit", VecImg::edit(), THISBACK(DoEditEdit)) .Check(!new_object && editmode == EDIT_TRACK); bar.MenuSeparator(); bar.Add(IsSelection(), "Copy", CtrlImg::copy(), THISBACK(DoEditCopy)) .Key(K_CTRL_C) .Help("Copy selected objects to the clipboard"); bar.Add(IsSelection(), "Cut", CtrlImg::cut(), THISBACK(DoEditCut)) .Key(K_CTRL_X) .Help("Remove selected object from the image and place them on the clipboard"); bar.Add("Paste", CtrlImg::paste(), THISBACK(DoEditPaste)) .Key(K_CTRL_V) .Help("Insert objects from the clipboard"); bar.Add("Duplicate", THISBACK(DoEditDuplicate)) .Key(K_CTRL_D) .Help("Create a copy of the selected objects"); bar.Add(!selection.IsEmpty(), "Delete", THISBACK(DoEditDelete)) .Key(K_DELETE) .Help("Delete selected object(s)"); bar.MenuSeparator(); bar.Add("Align horz", THISBACK(AlignHorzMenu)); bar.Add("Align vert", THISBACK(AlignVertMenu)); bar.Add("Equal size", THISBACK(SizeMenu)); bar.Separator(); bar.Add("Preferences", VecImg::edit_setup(), THISBACK(DoEditSetup)) .Help("View / edit vector designer preferences"); } void VectorCtrl::DoEditEdit() { new_object = NULL; editmode = EDIT_TRACK; WhenRescan(); } bool VectorCtrl::Copy() { if(!IsSelection()) { BeepExclamation(); return false; } VectorImage clipimg; clipimg.SetName("clip"); for(int i = 0; i < selection.GetCount(); i++) clipimg.Add(image[selection.GetKey(i)].Copy()); WriteClipboardText(clipimg.SaveVec(0)); /* { Exclamation("Error writing system clipboard."); return false; }*/ BeepInformation(); return true; } void VectorCtrl::DoEditCopy() { Copy(); } void VectorCtrl::DoEditCut() { if(Copy()) DoEditDelete(); } void VectorCtrl::PickPaste(VectorImage img) { int insertpos = image.GetCount(); if(!selection.IsEmpty()) { insertpos = selection.GetKey(0); for(int i = 1; i < selection.GetCount(); i++) insertpos = max(insertpos, selection.GetKey(i)); insertpos++; } ClearSelection(); for(int i = 0; !img.IsEmpty(); i++) { image.Insert(insertpos + i, img.Detach(0)); AddSelection(insertpos + i); } UpdateActionRefresh(); } void VectorCtrl::DoEditPaste() { try { String s = ReadClipboardText(); if(IsNull(s)) { BeepExclamation(); return; } CParser parser(s); VectorImage clipimg; clipimg.LoadVec(parser); PickPaste(clipimg); } catch(Exc e) { ShowExc(e); } } void VectorCtrl::DoEditDuplicate() { if(!IsSelection()) { BeepExclamation(); return; } Point move(10, 10); if(setup.grid) move = setup.grid_size; VectorImage help; for(int i = 0; i < selection.GetCount(); i++) { One obj = image[selection.GetKey(i)].Copy(); obj->Track(VectorObject::TRACK_MOVE, Point(0, 0), move, 0); help.Add(obj); } PickPaste(help); } void VectorCtrl::DoEditDelete() { if(selection.IsEmpty()) { BeepExclamation(); return; } Vector objix; objix <<= selection.GetKeys(); ClearSelection(); Sort(objix); for(int i = objix.GetCount(); --i >= 0;) image.Remove(objix[i]); UpdateActionRefresh(); WhenUserSelect(); } void VectorCtrl::AlignHorzMenu(Bar& bar) { bool msel = (selection.GetCount() >= 2); bar.Add(msel, "Left", VecImg::AlignLeft(), THISBACK2(DoAlign, ALIGN_LEFT, ALIGN_NULL)) .Key(K_SHIFT|K_ALT|K_LEFT); bar.Add(msel, "Center", VecImg::AlignHCenter(), THISBACK2(DoAlign, ALIGN_CENTER, ALIGN_NULL)) .Key(K_SHIFT_F9); bar.Add(msel, "Right", VecImg::AlignRight(), THISBACK2(DoAlign, ALIGN_RIGHT, ALIGN_NULL)) .Key(K_ALT|K_SHIFT|K_RIGHT); } void VectorCtrl::AlignVertMenu(Bar& bar) { bool msel = (selection.GetCount() >= 2); bar.Add(msel, "Top", VecImg::AlignTop(), THISBACK2(DoAlign, ALIGN_NULL, ALIGN_TOP)) .Key(K_ALT|K_SHIFT|K_UP); bar.Add(msel, "Center", VecImg::AlignVCenter(), THISBACK2(DoAlign, ALIGN_NULL, ALIGN_CENTER)) .Key(K_F9); bar.Add(msel, "Bottom", VecImg::AlignBottom(), THISBACK2(DoAlign, ALIGN_NULL, ALIGN_BOTTOM)) .Key(K_ALT|K_SHIFT|K_DOWN); } void VectorCtrl::DoAlign(Alignment halign, Alignment valign) { if(selection.GetCount() < 2) { BeepExclamation(); return; } int lkey = selection.TopKey(); const Index& leader = selection.Top(); Point align = Null; for(int i = 0; i < leader.GetCount(); i++) if(leader[i] & VectorObject::ARG_MASK) { const VectorObject& obj = image[lkey]; if(leader[i] & VectorObject::RECT_MASK) { Rect rc = obj.Info().RectArgs()->Get(&obj, leader[i] >> VectorObject::INDEX_SHIFT); switch(halign) { case ALIGN_LEFT: align.x = rc.left; break; case ALIGN_CENTER: align.x = (rc.left + rc.right) >> 1; break; case ALIGN_RIGHT: align.x = rc.right; break; } switch(valign) { case ALIGN_TOP: align.y = rc.top; break; case ALIGN_CENTER: align.y = (rc.top + rc.bottom) >> 1; break; case ALIGN_BOTTOM: align.y = rc.bottom; break; } } else align = obj.Info().PointArgs()->Get(&obj, leader[i] >> VectorObject::INDEX_SHIFT); break; } if(IsNull(align.x) && halign != ALIGN_NULL || IsNull(align.y) && valign != ALIGN_NULL) { BeepExclamation(); return; } for(int i = selection.GetCount() - 2; i >= 0; i--) image[selection.GetKey(i)].Align(halign, valign, align); UpdateActionRefresh(); } void VectorCtrl::SizeMenu(Bar& bar) { bool msel = (selection.GetCount() >= 2); bar.Add(msel, "Equal width", VecImg::SameWidth(), THISBACK2(DoSameSize, true, false)) .Key(K_SHIFT_F8); bar.Add(msel, "Equal height", VecImg::SameHeight(), THISBACK2(DoSameSize, false, true)) .Key(K_CTRL_F8); bar.Add(msel, "Equal size", VecImg::SameSize(), THISBACK2(DoSameSize, true, true)) .Key(K_F8); } void VectorCtrl::DoSameSize(bool cx, bool cy) { if(selection.GetCount() < 2) { BeepExclamation(); return; } Rect box = image[selection.TopKey()].GetBox(); if(IsNull(box)) { BeepExclamation(); return; } Size sz(cx ? box.Width() : (int)Null, cy ? box.Height() : (int)Null); for(int i = selection.GetCount() - 2; i >= 0; i--) image[selection.GetKey(i)].EqualSize(sz); UpdateActionRefresh(); } void VectorCtrl::InsertMenu(Bar& bar) { int lng = GetCurrentLanguage(); String current_type; if(new_object) current_type = new_object->Info().type; for(int i = 0; i < VectorObject::RegisteredObjects().GetCount(); i++) { const VectorInfo *e = VectorObject::RegisteredObjects()[i]; bar.Add(e->GetName(lng), e->icon, THISBACK1(OnNewObject, e)) .Check(e->type == current_type); } } /* Rect VectorCtrl::ApplyRect(Point pt, Point end, Rect rc) const { if(rectmask & 1) rc.left = end.x; if(rectmask & 2) rc.top = end.y; if(rectmask & 4) rc.right = end.x; if(rectmask & 8) rc.bottom = end.y; rc.Normalize(); return rc; } */ void VectorCtrl::ViewMenu(Bar& bar) { bar.Add("Zoom in", VecImg::view_zoom_in(), THISBACK(DoViewZoomIn)) .Key(K_CTRL_ADD); bar.Add("Zoom out", VecImg::view_zoom_out(), THISBACK(DoViewZoomOut)) .Key(K_CTRL_SUBTRACT); bar.Add("Zoom full", VecImg::view_zoom_full(), THISBACK(DoViewZoomFull)) .Key(K_CTRL_MULTIPLY); bar.Add("Zoom area", VecImg::view_zoom_area(), THISBACK(DoViewZoomArea)) .Check(editmode == EDIT_ZOOM); bar.MenuSeparator(); bar.Add("Pan", VecImg::view_pan(), THISBACK(DoViewPan)) .Check(editmode == EDIT_PAN); if(bar.IsToolBar()) bar.Add(tool_coords.SizePos(), 200); } void VectorCtrl::DoViewZoomIn() { Point center = GetImageSize() >> 1; Point pos = scrollbars.Get(), page = scrollbars.GetPage(), total = scrollbars.GetTotal(); if(page.x < total.x) center.x = (pos.x + (page.x >> 1)) / zoom; if(page.y < total.y) center.y = (pos.y + (page.y >> 1)) / zoom; SetZoom(Zoom(zoom.m * 2, zoom.d)); scrollbars.Set(ImageToClient(center) - Point(GetSize() >> 1)); } void VectorCtrl::DoViewZoomOut() { Point center = GetImageSize() >> 1; Point pos = scrollbars.Get(), page = scrollbars.GetPage(), total = scrollbars.GetTotal(); if(page.x < total.x) center.x = (pos.x + (page.x >> 1)) / zoom; if(page.y < total.y) center.y = (pos.y + (page.y >> 1)) / zoom; if(zoom.m >= 10) SetZoom(Zoom(zoom.m >> 1, zoom.d)); scrollbars.Set(ImageToClient(center) - Point(GetSize() >> 1)); } void VectorCtrl::DoViewZoomFull() { Size image_size = GetImageSize(); Size pixel_size = GetFitSize(image_size, scrollbars.GetReducedViewSize() - 2 * GAP); SetZoom(Zoom(pixel_size.cx, image_size.cx)); } void VectorCtrl::DoViewZoomArea() { DragStop(); editmode = EDIT_ZOOM; WhenRescan(); } void VectorCtrl::DoViewPan() { DragStop(); editmode = EDIT_PAN; WhenRescan(); } Point VectorCtrl::GetTrackMode(Point pt, Point& nearest) const { nearest = Point(-1, -1); static const int TOLERANCE = 5; Size imgsize = ImageToClient(Point(image.GetSize())); int szcx = tabs(pt.x - (imgsize.cx >> 1)) <= TOLERANCE ? 0 : tabs(pt.x - imgsize.cx) <= TOLERANCE ? 1 : -1; int szcy = tabs(pt.y - (imgsize.cy >> 1)) <= TOLERANCE ? 0 : tabs(pt.y - imgsize.cy) <= TOLERANCE ? 1 : -1; Point mode; if((szcx | szcy) > 0) mode = Point(-1, ((szcx | (szcy << 1)) << VectorObject::INDEX_SHIFT) | VectorObject::TRACK_IMAGE_SIZE); else { mode = image.Nearest(GetImageScaling(), pt + ClientOffset(), MAXDIST2, selection.GetKeys()); if(mode.x < 0) mode = image.Nearest(GetImageScaling(), pt + ClientOffset(), MAXDIST2); } bool sel = (mode.x >= 0 && selection.Find(mode.x) >= 0); if(new_object) mode = Point(-1, VectorObject::TRACK_NEW); else if(sel && (mode.y & VectorObject::TRACK_MASK) == VectorObject::TRACK_RECT && mode.y & VectorObject::ARG_MASK) { const VectorObject& obj = image[mode.x]; const VectorInfo& ii = obj.Info(); if(const VectorGeomArg *ra = ii.RectArgs()) { Rect rc = ra->Get(&obj, mode.y >> VectorObject::INDEX_SHIFT); int rm = GetRectDragMask(rc.Inflated(0) * GetImageScaling(), pt + ClientOffset(), TOLERANCE); if(rm) mode.y = rm | (mode.y & ~VectorObject::TRACK_MASK); else mode.y = VectorObject::TRACK_MOVE; } } nearest = mode; if(mode.x >= 0 && !sel) mode.y = (mode.y & ~VectorObject::TRACK_MASK) | VectorObject::TRACK_SELECT; return mode; } bool VectorCtrl::Push(Point pt, dword keyflags) { switch(editmode) { case EDIT_ZOOM: { return true; } case EDIT_PAN: { pan_start = scrollbars.Get(); return true; } case EDIT_TRACK: { break; } } if(!IsEditable()) return false; Point nearest; trackmode = GetTrackMode(pt, nearest); track_start = image.TrackPoint(trackmode); bool is_sel = IsSelected(trackmode.x); int tm = trackmode.y & VectorObject::TRACK_MASK; if(tm == VectorObject::TRACK_SELECT || !is_sel || keyflags & (K_SHIFT | K_CTRL)) { if(nearest.x >= 0) { if(tm == VectorObject::TRACK_SELECT) trackmode = nearest; VectorMap< int, Index > sel; sel.Add(trackmode.x).Add(trackmode.y); if(keyflags & K_CTRL) XorSelection(sel); else if((keyflags & K_SHIFT) || is_sel) AddSelection(sel); else if(!is_sel) PickSelection(sel); WhenUserSelect(); } } return true; } const Index& VectorCtrl::FindSelTrack(int i) const { static Index dummy; return selection.Get(i, dummy); } void VectorCtrl::Click(Point pt, dword keyflags) { if(IsEditable() && (trackmode.y & VectorObject::TRACK_MASK) == VectorObject::TRACK_NEW && !!new_object) { ClearSelection(); new_object->Create(pt, pt, keyflags); int nobj = AddObject(-new_object); new_object = empty_object->Copy(); UpdateActionRefresh(); AddSelection(nobj); WhenUserSelect(); } else if(!(keyflags & (K_SHIFT | K_CTRL)) && trackmode.y < 0) { ClearSelection(); WhenUserSelect(); } } void VectorCtrl::Drag(Point pt, Point last, Point next, dword keyflags) { switch(editmode) { case EDIT_ZOOM: { DragDropCtrl::Drag(pt, last, next, keyflags); return; } case EDIT_PAN: { if(!IsNull(next)) { scrollbars.Set(pan_start - (next - pt)); } return; } case EDIT_TRACK: { if(trackmode.y < 0) { DragDropCtrl::Drag(pt, last, next, keyflags); return; } break; } } if(!IsEditable()) return; ViewDraw draw(this); #ifdef PLATFORM_WIN32 SetROP2(draw, R2_NOTXORPEN); #endif draw.Offset(Point(GAP, GAP) - scrollbars); ScalingDraw scaled(&draw, GetImageScaling()); Point pi = (!IsNull(track_start) ? track_start : ClientToSnap(pt)); bool l = !IsNull(last), n = !IsNull(next); Point li = l ? ClientToSnap(last) : pi; Point ni = n ? ClientToSnap(next) : pi; if(!((trackmode.y & VectorObject::TRACK_MASK) & ~VectorObject::TRACK_RECT_SIDES)) { for(int i = 0; i < selection.GetCount(); i++) { const VectorObject& o = image[selection.GetKey(i)]; One c1 = o.Copy(), c2 = o.Copy(); if(l) c1->Track(trackmode.y, pi, li, keyflags); if(n) c2->Track(trackmode.y, pi, ni, keyflags); if(l) c1->DragPaint(scaled); if(n) { c2->DragPaint(scaled); tool_coords <<= c2->FormatCoords(); } } return; } switch(trackmode.y & VectorObject::TRACK_MASK) { case VectorObject::TRACK_NEW: { if(!!new_object) { One c1 = new_object->Copy(), c2 = new_object->Copy(); if(l) c1->Create(pi, li, keyflags); if(n) c2->Create(pi, ni, keyflags); if(l) c1->DragPaint(scaled); if(n) { c2->DragPaint(scaled); tool_coords <<= c2->FormatCoords(); } } break; } case VectorObject::TRACK_IMAGE_SIZE: { if(n) { Size sz = image.GetSize(); if(trackmode.y & (1 << VectorObject::INDEX_SHIFT)) sz.cx = minmax(ni.x, 10, 1000000); if(trackmode.y & (2 << VectorObject::INDEX_SHIFT)) sz.cy = minmax(ni.y, 10, 1000000); if(sz != image.GetSize()) { RLOG("SetImageSize: " << image.GetSize() << " -> " << sz << ", next = " << next); SetImageSize(sz); WhenImageSize(); tool_coords <<= NFormat("%d x %d", sz.cx, sz.cy); } } break; } case VectorObject::TRACK_MOVE: { for(int i = 0; i < selection.GetCount(); i++) { const VectorObject& obj = image[selection.GetKey(i)]; One c1 = obj.Copy(), c2 = obj.Copy(); if(l) c1->Track(trackmode.y, pi, li, keyflags); if(n) c2->Track(trackmode.y, pi, ni, keyflags); if(l) c1->DragPaint(scaled); if(n) { c2->DragPaint(scaled); tool_coords <<= c2->FormatCoords(); } } break; } case VectorObject::TRACK_SELECT: { DragDropCtrl::Drag(pt, last, next, keyflags); tool_coords <<= AsString(RectSort(last, next)); break; } default: { if(trackmode.x >= 0) { const VectorObject& obj = image[trackmode.x]; One c1 = obj.Copy(), c2 = obj.Copy(); if(l) c1->Track(trackmode.y, pi, li, keyflags); if(n) c2->Track(trackmode.y, pi, ni, keyflags); if(l) c1->DragPaint(scaled); if(n) { c2->DragPaint(scaled); tool_coords <<= c2->FormatCoords(); } break; } } } } void VectorCtrl::Drop(Point pt, Point end, dword keyflags) { switch(editmode) { case EDIT_ZOOM: { Rect rc = ClientToImage(RectSort(pt, end)); Size out = scrollbars.GetReducedViewSize(), in = rc.Size(); int m = out.cx, d = in.cx; if(out.cx * in.cy > out.cy * in.cx) m = out.cy, d = in.cy; SetZoom(Zoom(m, d)); scrollbars.Set(ImageToClient(rc.CenterPoint()) - (GetSize() >> 1) + scrollbars.Get()); return; } case EDIT_PAN: { return; } default: { break; } } if(!IsEditable()) return; Point pi = (!IsNull(track_start) ? track_start : ClientToSnap(pt)); Point ei = ClientToSnap(end); if(trackmode.y < 0) { Rect rc = RectSort(pt, end); VectorMap< int, Index > cont = image.Contains(GetImageScaling(), rc + ClientOffset()); if(keyflags & K_CTRL) XorSelection(cont); else if(keyflags & K_SHIFT) AddSelection(cont); else PickSelection(cont); WhenUserSelect(); return; } if(!((trackmode.y & VectorObject::TRACK_MASK) & ~VectorObject::TRACK_RECT_SIDES)) { for(int i = 0; i < selection.GetCount(); i++) image[selection.GetKey(i)].Track(trackmode.y, pi, ei, keyflags); UpdateActionRefresh(); return; } switch(trackmode.y & VectorObject::TRACK_MASK) { case VectorObject::TRACK_NEW: { if(!!new_object) { ClearSelection(); new_object->Create(pi, ei, keyflags); int nobj = AddObject(-new_object); new_object = empty_object->Copy(); UpdateActionRefresh(); AddSelection(nobj); WhenUserSelect(); } break; } case VectorObject::TRACK_IMAGE_SIZE: break; case VectorObject::TRACK_MOVE: { for(int i = 0; i < selection.GetCount(); i++) image[selection.GetKey(i)].Track(trackmode.y, pi, ei, keyflags); UpdateActionRefresh(); break; } case VectorObject::TRACK_SELECT: { PickSelection(image.Contains(GetImageScaling(), SortRect(pt + ClientOffset(), end + ClientOffset()))); WhenUserSelect(); break; } default: { if(trackmode.x >= 0) { image[trackmode.x].Track(trackmode.y, pi, ei, keyflags); UpdateActionRefresh(); break; } } } } bool VectorCtrl::Key(dword key, int repcnt) { if(IsEditable() && IsSelection() && (key & K_SHIFT)) { bool size = (key & K_CTRL); switch(key & ~K_SHIFT & ~K_CTRL) { case K_UP: KeyMove(0, -1, size); return true; case K_DOWN: KeyMove(0, +1, size); return true; case K_LEFT: KeyMove(-1, 0, size); return true; case K_RIGHT: KeyMove(+1, 0, size); return true; } } return DragDropCtrl::Key(key, repcnt); } void VectorCtrl::KeyMove(int dx, int dy, bool resize) { if(IsSelection()) { if(setup.snap) { int topi = selection.TopKey(); const Index& topsel = selection.Top(); const VectorObject& obj = image[topi]; const VectorInfo& info = obj.Info(); dx *= setup.snap_size.cx; dy *= setup.snap_size.cy; for(int i = 0; i < topsel.GetCount(); i++) if((topsel[i] & VectorObject::ARG_MASK)) { Point pt; int t = topsel[i] >> VectorObject::INDEX_SHIFT; if(topsel[i] & VectorObject::RECT_MASK) { Rect rc = info.RectArgs()->Get(&obj, t); pt = (resize ? rc.BottomRight() : rc.TopLeft()); } else pt = info.PointArgs()->Get(&obj, t); if(dx && setup.snap_size.cx > 1) dx = idivfloor(dx + pt.x + (dx > 0 ? 0 : setup.snap_size.cx - 1), setup.snap_size.cx) * setup.snap_size.cx - pt.x; if(dy && setup.snap_size.cy > 1) dy = idivfloor(dy + pt.y + (dy > 0 ? 0 : setup.snap_size.cy - 1), setup.snap_size.cy) * setup.snap_size.cy - pt.y; break; } } for(int i = 0; i < selection.GetCount(); i++) { VectorObject& obj = image[selection.GetKey(i)]; obj.Track(resize ? (int)VectorObject::TRACK_SIZE : (int)VectorObject::TRACK_MOVE, Point(0, 0), Point(dx, dy), 0); } UpdateActionRefresh(); SyncCoords(Null); } } void VectorCtrl::SetImage(VectorImage img) { image = img; Layout(); } void VectorCtrl::SetImageSize(Size sz) { image.SetSize(sz); Layout(); } void VectorCtrl::ClearSelection() { selection.Clear(); PostSyncArgEditors(); } void VectorCtrl::AddSelection(int sel, int track) { Index mask; int f = selection.Find(sel); if(f >= 0) { mask = selection[f]; selection.Remove(f); } if(track >= 0) mask.FindAdd(track); selection.Add(sel) = mask; PostSyncArgEditors(); } void VectorCtrl::AddSelection(const VectorMap< int, Index >& sel) { for(int i = 0; i < sel.GetCount(); i++) { int key = sel.GetKey(i); Index mask; mask <<= sel[i]; int f = selection.Find(key); if(f >= 0) { FindAppend(mask, selection[f]); selection.Remove(f); } selection.Add(key) = mask; } PostSyncArgEditors(); } void VectorCtrl::XorSelection(const VectorMap< int, Index >& sel) { for(int i = 0; i < sel.GetCount(); i++) { int key = sel.GetKey(i); Index selmask; selmask <<= sel[i]; int f = selection.Find(key); if(f >= 0) { Index oldmask = selection[f]; selection.Remove(f); for(int o = 0; o < oldmask.GetCount(); o++) { int s = selmask.Find(oldmask[o]); if(s >= 0) selmask.Remove(s); else selmask.Add(oldmask[o]); } } if(!selmask.IsEmpty()) selection.Add(key) = selmask; } PostSyncArgEditors(); } void VectorCtrl::PickSelection(pick_ VectorMap< int, Index >& sel) { selection = sel; PostSyncArgEditors(); } void VectorCtrl::RemoveSelection(const Vector& rm_index) { Index found; for(int i = 0; i < rm_index.GetCount(); i++) { int f = selection.Find(rm_index[i]); if(f >= 0) found.FindAdd(f); } Vector flist = found.PickKeys(); Sort(flist); while(!flist.IsEmpty()) selection.Remove(flist.Pop()); PostSyncArgEditors(); } void VectorCtrl::PostSyncArgEditors() { sel_args.Clear(); tc_sync_editors.KillSet(200, THISBACK(SyncArgEditors)); } void VectorCtrl::SyncArgEditors() { tc_sync_editors.Kill(); sel_args.Clear(); sel_editors.Clear(); int name_len = 0; for(int i = 0; i < selection.GetCount(); i++) { int isel = selection.GetKey(i); const VectorObject& obj = image[isel]; const VectorInfo& info = obj.Info(); for(int a = 0; a < info.GetCount(); a++) { const VectorArg& varg = info[a]; Value v = obj.GetArg(a); int f; for(f = sel_args.Find(varg.name); f >= 0; f = sel_args.FindNext(f)) if(sel_args[f].vtype == varg.vtype && sel_args[f].editor == varg.editor) { sel_args[f].obj_args.Add(Point(a, isel)); if(sel_args[f].value != v) sel_args[f].multi = true; break; } if(f < 0) { ArgEditor& ed = sel_args.Add(varg.name); ed.name = varg.name; ed.vtype = varg.vtype; ed.editor = varg.editor; ed.value = v; ed.multi = false; ed.obj_args.Add(Point(a, isel)); name_len = max(name_len, GetSmartTextSize(ScreenInfo(), ed.name + ": ", StdFont()).cx); } } } Sort(sel_args, FieldRelation(&ArgEditor::name, GetLanguageInfo())); int ypos = 4, yend = ypos; for(int i = 0; i < sel_args.GetCount(); i++) { const ArgEditor& sa = sel_args[i]; One editctrl = VectorArg::CreateEditor(sa.vtype, sa.editor); if(!!editctrl) { One