#include "LayDes.h" using namespace LayoutKeys; #define IMAGECLASS LayImg #define IMAGEFILE #include #define LTIMING(x) // TIMING(x) #define LLOG(x) #define MARGIN 8 static void sLay1(int& pos, int& r, int align, int a, int b, int sz) { pos = a; int size = b; switch(align) { case Ctrl::CENTER: pos = (sz - b) / 2 + a; break; case Ctrl::RIGHT: pos = sz - (a + b); break; case Ctrl::SIZE: size = sz - (a + b); break; } r = pos + max(size, 0); } Rect LayDes::CtrlRect(Ctrl::LogPos pos, Size sz) { Rect r; sLay1(r.left, r.right, pos.x.GetAlign(), pos.x.GetA(), pos.x.GetB(), sz.cx); sLay1(r.top, r.bottom, pos.y.GetAlign(), pos.y.GetA(), pos.y.GetB(), sz.cy); return r; } Rect LayDes::CtrlRectZ(Ctrl::LogPos pos, Size sz) { Rect r = CtrlRect(pos, sz); r.left = HorzLayoutZoom(r.left); r.right = HorzLayoutZoom(r.right); r.top = VertLayoutZoom(r.top); r.bottom = VertLayoutZoom(r.bottom); return r; } void LayDes::AddHandle(Draw& w, int x, int y) { w.DrawRect(x, y, DPI(6), DPI(6), SColorMark); handle.Add(Point(x, y)); } Point LayDes::Normalize(Point p) { p += sb; p.Offset(-MARGIN, -MARGIN); Pointf h = p; h /= GetScale(); return h; } Point LayDes::ZPoint(Point p) { Size csz, dsz; GetZoomRatio(csz, dsz); if(csz.cx && csz.cy && dsz.cx && dsz.cy) { p.x = p.x * dsz.cx / csz.cx; p.y = p.y * dsz.cy / csz.cy; } return p; } void LayDes::SetSb() { Size sz = Size(0, 0); if(!IsNull(currentlayout)) { LayoutData& l = CurrentLayout(); sz = l.size; for(int i = 0; i < l.item.GetCount(); i++) sz = max(sz, (Size)CtrlRect(l.item[i].pos, l.size).BottomRight()); } sz += 2 * Size(MARGIN, MARGIN); Size csz, dsz; GetZoomRatio(csz, dsz); if(csz.cx && csz.cy && dsz.cx && dsz.cy) { sz.cx = sz.cx * csz.cx / dsz.cx; sz.cy = sz.cy * csz.cy / dsz.cy; } double scale = GetScale(); sb.SetTotal(scale * sz); sb.SetPage(sb.GetReducedViewSize()); sb.SetLine(DPI(8)); } void LayDes::Scroll() { Refresh(); } void LayDes::Layout() { SetSb(); list.ScrollIntoCursor(); } void LayDes::GetSprings(Rect& l, Rect& t, Rect& r, Rect& b) { if(IsNull(currentlayout) || !cursor.GetCount()) { l = t = r = b = Null; return; } Rect ir = CtrlRectZ(CurrentItem().pos, CurrentLayout().size); int h4 = ir.Height() / 4; int w4 = ir.Width() / 4; int fv = DPI(5); int tn = DPI(10); l = RectC(-MARGIN, ir.top + h4 - fv, ir.left + MARGIN, tn); t = RectC(ir.left + w4 - fv, -MARGIN, tn, ir.top + MARGIN); r = RectC(ir.right, ir.bottom - h4 - fv, 9999, tn); b = RectC(ir.right - w4 - fv, ir.bottom, tn, 9999); } void LayDes::DrawSpring(Draw& w, const Rect& r, bool horz, bool hard) { if(hard) w.DrawRect(horz ? r.DeflatedVert(4) : r.DeflatedHorz(4), Red); else if(horz) for(int x = r.left; x < r.right; x += 4) w.DrawRect(x, r.top, 1, r.Height(), SColorShadow); else for(int y = r.top; y < r.bottom; y += 4) w.DrawRect(r.left, y, r.Width(), 1, SColorShadow); } int LayDes::ParseLayoutRef(String cls, String& base) const { const char *p = cls; if(p[0] != 'W' || p[1] != 'i' || p[2] != 't' || p[3] != 'h') return -1; const char *b = (p += 4); while(IsAlNum(*p) || *p == '_') p++; if(p <= b) return -1; String layoutid(b, p); int fi = FindFieldIndex(layout, &LayoutData::name, layoutid); if(fi < 0) return -1; while(*p && (byte)*p <= ' ') p++; if(*p++ != '<') return -1; while(*p && (byte)*p <= ' ') p++; const char *e = cls.End(); while(e > p && (byte)e[-1] <= ' ') e--; if(e > p && e[-1] == '>') e--; while(e > p && (byte)e[-1] <= ' ') e--; base = String(p, e); return fi; } void LayDes::PaintLayoutItems(Draw& w, int layid, Size size, Index& passed, const Vector& cursor) { if(passed.Find(layid) >= 0) return; passed.Add(layid); LayoutData& l = layout[layid]; Buffer lrs(l.item.GetCount()); String dummy; for(int i = 0; i < l.item.GetCount(); i++) lrs[i] = ParseLayoutRef(l.item[i].type, dummy); for(int i = 0; i < l.item.GetCount(); i++) { LayoutItem& m = l.item[i]; if(m.hide) continue; Rect r = CtrlRectZ(m.pos, size); DrawFrame(w, r, Blend(SLtGray(), SCyan(), 35)); bool show = true; if(i < cursor.GetCount() && !cursor[i] && m.type != "LabelBox") // does this widget hide part of some widget that is current selected? for(int j = 0; j < l.item.GetCount(); j++) if(j != i && j < cursor.GetCount() && cursor[j]) { LayoutItem& mm = l.item[j]; if(!mm.hide && r.Intersects(CtrlRectZ(mm.pos, size)) && (LayoutTypes().Find(mm.type) >= 0 || lrs[j] >= 0) && mm.type != "LabelBox" && HasCapture() && findarg(draghandle, 11, 12) >= 0) { show = false; break; } } if(show) { w.Clipoff(r); if(lrs[i] < 0) m.Paint(w, r.Size()); else { Size sz = r.GetSize(); PaintLayoutItems(w, lrs[i], CtrlRect(m.pos, size).GetSize(), passed, Vector()); Font fnt = LayFont(); String s = m.type; Size tsz = GetTextSize(s, fnt); int tcy2 = tsz.cy / 2; if(tsz.cx + tcy2 > sz.cx && s.TrimStart("With")) tsz = GetTextSize(s, fnt); int q = s.Find('<'); if(tsz.cx + tcy2 > sz.cx && q >= 0) { s.Trim(q); tsz = GetTextSize(s, fnt); } if(tsz.cx + tcy2 > sz.cx && s.TrimEnd("Layout")) tsz = GetTextSize(s, fnt); w.DrawRect(sz.cx - tsz.cx - tsz.cy, sz.cy - tsz.cy, tsz.cx + tsz.cy, tsz.cy, SGray()); w.DrawText(sz.cx - tsz.cx - tsz.cy / 2, sz.cy - tsz.cy, s, fnt, SYellow()); } w.End(); } } passed.Drop(); } void LayDes::Paint2(Draw& w) { LayoutData& l = CurrentLayout(); Size lsz = LayoutZoom(l.size); w.DrawRect(0, 0, lsz.cx, lsz.cy, SLtGray); if(setting.paintgrid) { int gx = minmax((int)~setting.gridx, 1, 32); int gy = minmax((int)~setting.gridy, 1, 32); for(int x = 0; x < lsz.cx; x += gx) for(int y = 0; y < lsz.cy; y += gy) w.DrawRect(x, y, 1, 1, SColorPaper); } DrawFrame(w, -1, -1, lsz.cx + 2, lsz.cy + 2, SColorText); handle.Clear(); AddHandle(w, lsz.cx, lsz.cy / 2 - DPI(3)); AddHandle(w, lsz.cx / 2 - DPI(3), lsz.cy); AddHandle(w, lsz.cx, lsz.cy); int i; Index passed; Vector cursorflags; if(!cursor.IsEmpty()) { cursorflags.SetCount(l.item.GetCount(), cursor.IsEmpty()); for(i = 0; i < cursor.GetCount(); i++) cursorflags[cursor[i]] = true; } PaintLayoutItems(w, currentlayout, l.size, passed, cursorflags); if(!HasCapture() || draghandle == 14) { for(i = 0; i < cursor.GetCount(); i++) { LayoutItem& m = l.item[cursor[i]]; Rect r = CtrlRectZ(m.pos, l.size); DrawFatFrame(w, r, i == cursor.GetCount() - 1 ? Cyan : Brown, DPI(-3)); if(i == cursor.GetCount() - 1) { int lrm = r.left + r.Width() / 2 - DPI(3); int tbm = r.top + r.Height() / 2 - DPI(3); AddHandle(w, r.left - DPI(6), r.top - DPI(6)); AddHandle(w, lrm, r.top - DPI(6)); AddHandle(w, r.right, r.top - DPI(6)); AddHandle(w, r.left - DPI(6), tbm); AddHandle(w, r.right, tbm); AddHandle(w, r.left - DPI(6), r.bottom); AddHandle(w, lrm, r.bottom); AddHandle(w, r.right, r.bottom); } } if(cursor.GetCount()) { LayoutItem& m = CurrentItem(); Rect l, t, r, b; GetSprings(l, t, r, b); DrawSpring(w, l, true, m.pos.x.GetAlign() & Ctrl::LEFT); DrawSpring(w, r, true, m.pos.x.GetAlign() & Ctrl::RIGHT); DrawSpring(w, t, false, m.pos.y.GetAlign() & Ctrl::TOP); DrawSpring(w, b, false, m.pos.y.GetAlign() & Ctrl::BOTTOM); } } if(HasCapture() && draghandle == 14) DrawFrame(w, dragrect.Normalized(), LtRed); } void LayDes::MouseWheel(Point p, int zdelta, dword keyflags) { if(keyflags & K_CTRL) { layout_zoom = clamp(layout_zoom - sgn(zdelta), 0, 15); Refresh(); SetBar(); SetSb(); } else if(keyflags & K_SHIFT) sb.WheelX(zdelta); else sb.WheelY(zdelta); } void LayDes::HorzMouseWheel(Point, int zdelta, dword) { sb.WheelX(zdelta); } double LayDes::GetScale() { return (20 - layout_zoom) / 20.0; } void LayDes::Paint(Draw& w) { LTIMING("Paint"); Size sz = GetSize(); if(!IsNull(fileerror)) { w.DrawRect(sz, SColorPaper()); w.DrawText(16, 16, "FILE ERROR: " + fileerror, ArialZ(14).Bold(), Red); } if(IsNull(currentlayout)) return; if(layout_zoom) { DrawPainter sw(w, sz); sw.Co(); sw.Clear(SColorPaper()); sw.Offset(-sb.Get()); sw.Offset(MARGIN, MARGIN); sw.Scale(GetScale()); Paint2(sw); sw.End(); sw.End(); } else { w.DrawRect(sz, SColorPaper()); w.Offset(-sb.Get()); w.Offset(MARGIN, MARGIN); Paint2(w); w.End(); w.End(); } } void LayDes::SaveState() { if(IsNull(currentlayout)) return; CurrentLayout().SaveState(); SetBar(); } void LayDes::SetStatus(bool down) { String s; if(!IsNull(currentlayout)) { Size sz = CurrentLayout().size; s << sz; if(cursor.GetCount()) { Rect r = CtrlRect(CurrentItem().pos, sz); s << ": " << r << " - {" << sz.cx - r.right << ", " << sz.cy - r.bottom << '}'; } } status.SetLabel(s); Refresh(); SetBar(); SetSb(); } int LayDes::FindHandle(Point p) { for(int i = 0; i < handle.GetCount(); i++) { Point h = handle[i]; if(p.x >= h.x - DPI(1) && p.x < h.x + DPI(7) && p.y >= h.y - DPI(1) && p.y < h.y + DPI(7)) return i; } return -1; } int LayDes::FindItem(Point p, bool cursor_first) { if(IsNull(currentlayout)) return -1; LayoutData& l = CurrentLayout(); int ii = -1; int min = INT_MAX; for(int pass = !cursor_first; pass < 2 && ii < 0; pass++) for(int i = 0; i < (pass ? l.item.GetCount() : cursor.GetCount()); i++) { int i2 = pass ? i : cursor[i]; LayoutItem& m = l.item[i2]; if(m.hide) continue; Rect r = CtrlRect(m.pos, l.size); if(r.Contains(p)) { int mm = r.Width() * r.Height(); if(mm < min) { ii = i2; min = mm; } } } return ii; } Image LayDes::CursorImage(Point p, dword keyflags) { int hi; if(HasCapture()) hi = draghandle; else hi = FindHandle(Normalize(p)); Image (*id[11])() = { Image::SizeHorz, Image::SizeVert, Image::SizeBottomRight, Image::SizeTopLeft, Image::SizeVert, Image::SizeTopRight, Image::SizeHorz, Image::SizeHorz, Image::SizeBottomLeft, Image::SizeVert, Image::SizeBottomRight, }; if(hi >= 0 && hi < 11) return (*id[hi])(); return Image::Arrow(); } Ctrl::Logc MakeLogc(int align, int a, int b, int sz) { int isz = b - a; switch(align) { case Ctrl::LEFT: return Ctrl::PosLeft(a, isz); case Ctrl::RIGHT: return Ctrl::PosRight(sz - b, isz); case Ctrl::CENTER: return Ctrl::PosCenter(isz, a - (sz - isz) / 2); } return Ctrl::PosSize(a, sz - b); } Ctrl::LogPos MakeLogPos(int ax, int ay, const Rect& r, Size sz) { return Ctrl::LogPos(MakeLogc(ax, r.left, r.right, sz.cx), MakeLogc(ay, r.top, r.bottom, sz.cy)); } Ctrl::LogPos MakeLogPos(Ctrl::LogPos p, const Rect& r, Size sz) { return MakeLogPos(p.x.GetAlign(), p.y.GetAlign(), r, sz); } struct IDisplay : public Display { Color paper, ink; Font font; Size GetStdSize(const Value& q) const { Size sz = GetSmartTextSize(~q, font); sz.cx += 2 * DPI(4); return sz; } void Paint(Draw& w, const Rect& r, const Value& q, Color, Color, dword s) const { w.DrawRect(r, paper); DrawSmartText(w, r.left + DPI(4), r.top, r.Width(), ~q, font, ink); } IDisplay(Color paper, Color ink, Font font = StdFont()) : paper(paper), ink(ink), font(font) {} }; struct TDisplay : public Display { Color paper, ink; Font font; Size GetStdSize(const Value& q) const { Size sz = GetSmartTextSize(~q, font); sz.cx += 2 * DPI(4) + DPI(6) + sz.cy * 8 / 3; return sz; } void Paint(Draw& w, const Rect& r, const Value& q, Color, Color, dword s) const { w.DrawRect(r, paper); int dx = r.Height() * 8 / 3; Image icon = GetTypeIcon(String(q), dx, r.Height(), 1, paper); w.DrawImage(r.left + DPI(4), r.top + (r.Height() - icon.GetSize().cy) / 2, icon); DrawSmartText(w, r.left + dx + DPI(6), r.top, r.Width(), ~q, font, ink); } TDisplay(Color paper, Color ink, Font font = StdFont()) : paper(paper), ink(ink), font(font) {} }; void LayDes::SyncItems() { LTIMING("SyncItems"); if(IsNull(currentlayout)) return; int i; for(i = 0; i < item.GetCount(); i++) SyncItem(i, 0); for(i = 0; i < cursor.GetCount(); i++) SyncItem(cursor[i], 1); if(cursor.GetCount()) { SyncItem(cursor.Top(), 2); item.SetCursor(cursor.Top()); } else item.KillCursor(); SetStatus(); SyncProperties(true); } void LayDes::SyncItem(int i, int style) { if(CurrentLayout().item[i].hide) { static IDisplay dnormal(SColorPaper, SColorText, StdFont().Italic()); static IDisplay dselect(SColorLtFace, SColorText, StdFont().Italic()); static IDisplay dcursor(SColorFace, SColorText, StdFont().Italic()); static TDisplay tnormal(SColorPaper, SColorText, StdFont().Italic()); static TDisplay tselect(SColorLtFace, SColorText, StdFont().Italic()); static TDisplay tcursor(SColorFace, SColorText, StdFont().Italic()); static IDisplay lnormal(SColorPaper, Green, StdFont().Italic()); static IDisplay lselect(SColorLtFace, Green, StdFont().Italic()); static IDisplay lcursor(SColorFace, Green, StdFont().Italic()); const Display *noicon[] = { &dnormal, &dselect, &dcursor }; const Display *isicon[] = { &tnormal, &tselect, &tcursor }; const Display *label[] = { &lnormal, &lselect, &lcursor }; bool icons = setting.showicons; item.SetDisplay(i, 0, *(icons ? isicon : noicon)[style]); item.SetDisplay(i, 1, *(IsNull(CurrentLayout().item[i].variable) ? label : noicon)[style]); } else { static IDisplay dnormal(SColorPaper, SColorText); static IDisplay dselect(SColorLtFace, SColorText); static IDisplay dcursor(SColorFace, SColorText); static TDisplay tnormal(SColorPaper, SColorText); static TDisplay tselect(SColorLtFace, SColorText); static TDisplay tcursor(SColorFace, SColorText); static IDisplay lnormal(SColorPaper, Green); static IDisplay lselect(SColorLtFace, Green); static IDisplay lcursor(SColorFace, Green); const Display *noicon[] = { &dnormal, &dselect, &dcursor }; const Display *isicon[] = { &tnormal, &tselect, &tcursor }; const Display *label[] = { &lnormal, &lselect, &lcursor }; bool icons = setting.showicons; item.SetDisplay(i, 0, *(icons ? isicon : noicon)[style]); item.SetDisplay(i, 1, *(IsNull(CurrentLayout().item[i].variable) ? label : noicon)[style]); } } void LayDes::SyncProperties(bool sync_type_var) { property.Clear(); if(sync_type_var) { type.Disable(); variable.Disable(); type <<= variable <<= Null; } if(cursor.GetCount()) { LayoutItem& m = CurrentItem(); int i; for(i = 0; i < m.property.GetCount(); i++) { property.Add(m.property[i]); m.property[i].WhenAction = THISBACK(PropertyChanged); } if(sync_type_var) { String tp = m.type; type <<= m.type; for(i = 0; i < cursor.GetCount() - 1; i++) if(CurrentLayout().item[cursor[i]].type != tp) { type <<= Null; break; } variable <<= m.variable; type.Enable(); variable.Enable(); } } } void LayDes::SelectOne(int ii, dword flags) { if(ii < 0) { if(flags & (K_SHIFT|K_CTRL)) return; cursor.Clear(); } else if(flags & (K_SHIFT|K_CTRL)) { int q = FindIndex(cursor, ii); if(q >= 0) cursor.Remove(q); else cursor.Add(ii); } else { cursor.Clear(); cursor.Add(ii); } SyncItems(); } void LayDes::StoreItemRects() { if(IsNull(currentlayout)) return; LayoutData& l = CurrentLayout(); itemrect.SetCount(cursor.GetCount()); for(int i = 0; i < cursor.GetCount(); i++) itemrect[i] = CtrlRect(l.item[cursor[i]].pos, l.size); } void LayDes::LeftDown(Point p, dword keyflags) { if(IsNull(currentlayout)) return; SaveState(); SetFocus(); SetCapture(); LayoutData& l = CurrentLayout(); draglayoutsize = l.size; p = Normalize(p); draghandle = FindHandle(p); dragbase = ZPoint(p); if(draghandle >= 0) StoreItemRects(); else { int ii = FindItem(dragbase, true); if(ii >= 0) { if(GetShift() || GetCtrl() || FindIndex(cursor, ii) < 0) SelectOne(ii, keyflags); if(cursor.GetCount()) { draghandle = 11; StoreItemRects(); SetStatus(); } } else { if(cursor.GetCount()) { LayoutItem& m = CurrentItem(); StoreItemRects(); Rect lr, tr, rr, br; GetSprings(lr, tr, rr, br); int xa = m.pos.x.GetAlign(); int ya = m.pos.y.GetAlign(); if(lr.Contains(p)) xa ^= 1; if(rr.Contains(p)) xa ^= 2; if(tr.Contains(p)) ya ^= 1; if(br.Contains(p)) ya ^= 2; if(xa != m.pos.x.GetAlign() || ya != m.pos.y.GetAlign()) { SetSprings(MAKELONG(xa, ya)); SetStatus(); return; } } dragrect.left = dragrect.right = p.x; dragrect.top = dragrect.bottom = p.y; draghandle = 14; if(GetCtrl() || GetShift()) basesel = cursor.GetCount(); else { basesel = 0; cursor.Clear(); } SyncItems(); } } SetStatus(true); } void LayDes::LeftRepeat(Point p, dword keyflags) { MouseMove(p, keyflags); } void LayDes::MouseMove(Point p, dword keyflags) { if(!HasCapture() || IsNull(currentlayout)) return; Point pz = Normalize(p); p = ZPoint(pz); LayoutData& l = CurrentLayout(); bool smallmove = max(abs(p.x - dragbase.x), abs(p.y - dragbase.y)) < 4; if(draghandle == 14) { dragrect.right = pz.x; dragrect.bottom = pz.y; cursor.SetCount(basesel); Rect r = dragrect.Normalized(); r = Rect(ZPoint(r.TopLeft()), ZPoint(r.BottomRight())); int mind = INT_MAX; int mini = -1; for(int i = 0; i < l.item.GetCount(); i++) { LayoutItem& m = l.item[i]; Rect ir = CtrlRect(m.pos, l.size); if(r.Contains(ir) && FindIndex(cursor, i) < 0) { Point ip = ir.CenterPoint(); int mm = (ip.x - dragrect.left) * (ip.x - dragrect.left) + (ip.y - dragrect.top) * (ip.y - dragrect.top); if(mm < mind) { mind = mm; mini = cursor.GetCount(); } cursor.Add(i); } } if(mini >= 0) Swap(cursor.Top(), cursor[mini]); if(cursor.GetCount()) item.SetCursor(cursor.Top()); else item.KillCursor(); SyncItems(); SetStatus(false); LTIMING("MouseMove Sync"); Sync(); return; } int gx = 1; int gy = 1; if(usegrid == !(keyflags & K_ALT)) { gx = minmax((int)~setting.gridx, 1, 32); gy = minmax((int)~setting.gridy, 1, 32); } p -= dragbase; if(draghandle < 3) { Vector r; r.SetCount(l.item.GetCount()); for(int i = 0; i < l.item.GetCount(); i++) r[i] = CtrlRect(l.item[i].pos, l.size); if((draghandle + 1) & 1) l.size.cx = max(0, draglayoutsize.cx + p.x) / gx * gx; if((draghandle + 1) & 2) l.size.cy = max(0, draglayoutsize.cy + p.y) / gy * gy; if(!sizespring) for(int i = 0; i < l.item.GetCount(); i++) { LayoutItem& m = l.item[i]; m.pos = MakeLogPos(m.pos, r[i], l.size); } SetStatus(true); SetSb(); Sync(); return; } if(!item.IsCursor()) return; if(draghandle == 11) { if(smallmove) return; draghandle = 12; } Point md = Point(0, 0); for(int i = 0; i < cursor.GetCount(); i++) { LayoutItem& m = l.item[cursor[i]]; Rect r = itemrect[i]; Size minsize = ignoreminsize ? Size(0, 0) : m.GetMinSize(); if(keyflags & K_CTRL) minsize = Size(0, 0); if(draghandle == 3 || draghandle == 4 || draghandle == 5) r.top = min(r.bottom - minsize.cy, (r.top + p.y) / gy * gy); if(draghandle == 8 || draghandle == 9 || draghandle == 10) r.bottom = max(r.top + minsize.cy, (r.bottom + p.y) / gy * gy); if(draghandle == 3 || draghandle == 6 || draghandle == 8) r.left = min(r.right - minsize.cx, (r.left + p.x) / gy * gy); if(draghandle == 5 || draghandle == 7 || draghandle == 10) r.right = max(r.left + minsize.cx, (r.right + p.x) / gy * gy); if(draghandle == 12) { Size sz = r.GetSize(); if(i == 0) { Rect q = r; r.left = (r.left + p.x) / gx * gx; r.top = (r.top + p.y) / gy * gy; md = r.TopLeft() - q.TopLeft(); } else r += md; r.SetSize(sz); } m.pos = MakeLogPos(m.pos, r, draglayoutsize); } SetStatus(true); Sync(); } void LayDes::LeftUp(Point p, dword keyflags) { if(draghandle == 11 && (keyflags & (K_SHIFT|K_CTRL)) == 0) SelectOne(FindItem(ZPoint(Normalize(p))), 0); draghandle = -1; SyncItems(); } void LayDes::CreateCtrl(const String& _type) { if(IsNull(currentlayout)) return; LayoutData& l = CurrentLayout(); int c = l.item.GetCount(); if(cursor.GetCount()) c = Max(cursor) + 1; LayoutItem& m = l.item.Insert(c); m.Create(_type); Point p = dragbase; Size sza, szb; GetZoomRatio(sza, szb); if(sza.cx) p.x = szb.cx * p.x / sza.cx; if(sza.cy) p.y = szb.cy * p.y / sza.cy; if(usegrid) { p.x = p.x / (int)~setting.gridx * (int)~setting.gridx; p.y = p.y / (int)~setting.gridy * (int)~setting.gridy; } Rect r(p, m.GetStdSize()); m.pos = MakeLogPos(Ctrl::LEFT, Ctrl::TOP, r, l.size); cursor.Clear(); cursor.Add(c); ReloadItems(); if(!search.HasFocus()) { if(IsNull(_type)) type.SetFocus(); else { int q = m.FindProperty("SetLabel"); if(q >= 0 && findarg(_type, "Label", "LabelBox") >= 0) m.property[q].PlaceFocus(0, 0); else variable.SetFocus(); } } } void LayDes::Group(Bar& bar, const String& group) { int i; Vector type; for(i = 0; i < LayoutTypes().GetCount(); i++) { LayoutType& m = LayoutTypes()[i]; if((IsNull(group) || m.group == group) && m.kind == LAYOUT_CTRL) type.Add(LayoutTypes().GetKey(i)); } Sort(type); int h = 3 * StdFont().Info().GetHeight() / 2; int w = 8 * h / 3; if(auto *mb = dynamic_cast(&bar)) mb->LeftGap(w + 6); int q = 0; for(i = 0; i < type.GetCount(); i++) { bar.Add(type[i], GetTypeIcon(type[i], w, h, 0, SLtGray), THISBACK1(CreateCtrl, type[i])); if((q++ + 2) % 16 == 0) bar.Break(); } } void LayDes::TemplateGroup(Bar& bar, TempGroup tg) { int i; Vector type; for(i = 0; i < LayoutTypes().GetCount(); i++) { LayoutType& m = LayoutTypes()[i]; if((IsNull(tg.group) || m.group == tg.group) && m.kind == LAYOUT_CTRL) type.Add(LayoutTypes().GetKey(i)); } Sort(type); int h = 3 * StdFont().Info().GetHeight() / 2; int w = 8 * h / 3; if(auto *mb = dynamic_cast(&bar)) mb->LeftGap(w + 6); int q = 0; for(i = 0; i < type.GetCount(); i++) { bar.Add(type[i], GetTypeIcon(type[i], w, h, 0, SLtGray), THISBACK1(CreateCtrl, tg.temp + '<' + type[i] + '>')); if((q++ + 2) % 16 == 0) bar.Break(); } } void LayDes::Template(Bar& bar, const String& temp) { Index group; Vector type; int h = 3 * StdFont().Info().GetHeight() / 2; int w = 8 * h / 3; if(auto *mb = dynamic_cast(&bar)) mb->LeftGap(w + 6); int i; for(i = 0; i < LayoutTypes().GetCount(); i++) { LayoutType& m = LayoutTypes()[i]; if(!IsNull(m.group)) group.FindAdd(m.group); else if(m.kind == LAYOUT_CTRL) type.Add(LayoutTypes().GetKey(i)); } Vector sg = group.PickKeys(); Sort(sg); Sort(type); int q = 0; for(i = 0; i < sg.GetCount(); i++) { bar.Add(sg[i], THISBACK1(TemplateGroup, TempGroup(temp, sg[i]))); if((q++ + 2) % 16 == 0) bar.Break(); } bar.Add("All", THISBACK1(TemplateGroup, TempGroup(temp, String()))); if((q++ + 2) % 16 == 0) bar.Break(); for(i = 0; i < type.GetCount(); i++) { bar.Add(type[i], GetTypeIcon(type[i], w, h, 0, SLtGray), THISBACK1(CreateCtrl, temp + '<' + type[i] + '>')); if((q++ + 2) % 16 == 0) bar.Break(); } } void LayDes::Templates(Bar& bar) { Vector temp; int i; for(i = 0; i < LayoutTypes().GetCount(); i++) if(LayoutTypes()[i].kind == LAYOUT_TEMPLATE) temp.Add(LayoutTypes().GetKey(i)); Sort(temp); int q = 0; for(i = 0; i < temp.GetCount(); i++) { bar.Add(temp[i], THISBACK1(Template, temp[i])); if((q++ + 2) % 16 == 0) bar.Break(); } } void LayDes::RightDown(Point p, dword keyflags) { if(IsNull(currentlayout) || HasCapture()) return; dragbase = Normalize(p); MenuBar menu; int h = StdFont().Info().GetHeight(); int w = 8 * h / 3; menu.LeftGap(w + 2); menu.Add("User class", THISBACK1(CreateCtrl, "")); menu.Separator(); Index group; Vector type; int i; for(i = 0; i < LayoutTypes().GetCount(); i++) { LayoutType& m = LayoutTypes()[i]; if(!IsNull(m.group)) group.FindAdd(m.group); else if(m.kind == LAYOUT_CTRL) type.Add(LayoutTypes().GetKey(i)); } Vector sg = group.PickKeys(); Sort(sg); Sort(type); int q = 0; for(i = 0; i < sg.GetCount(); i++) { menu.Add(sg[i], THISBACK1(Group, sg[i])); if((q++ + 2) % 16 == 0) menu.Break(); } menu.Add("All", THISBACK1(Group, String())); menu.Add("Templates", THISBACK(Templates)); if((q++ + 2) % 16 == 0) menu.Break(); for(i = 0; i < type.GetCount(); i++) { menu.Add(type[i], GetTypeIcon(type[i], w, h, 0, SLtGray), THISBACK1(CreateCtrl, type[i])); if((q++ + 2) % 16 == 0) menu.Break(); } menu.Execute(); } void LayDes::LoadItems() { int nitems = CurrentLayout().item.GetCount(); item.SetCount(nitems); for(int i = 0; i < nitems; i++) LoadItem(i); property.Clear(); } String GetLabel(const LayoutItem& m) { EscValue l = m.ExecuteMethod("GetLabelMethod"); if(l.IsVoid()) for(int p = 0; p < m.property.GetCount(); p++) if(m.property[p].name == "SetLabel") { Value prop = ~m.property[p]; return IsString(prop) && !IsNull(prop) ? AsCString(prop) : Null; } return l; } void LayDes::LoadItem(int ii) { const LayoutItem& m = CurrentLayout().item[ii]; String varlbl = m.variable; if(IsNull(varlbl)) varlbl = GetLabel(m); item.Set(ii, 0, m.type); item.Set(ii, 1, varlbl); item.Set(ii, 2, m.hide); } void LayDes::ReloadItems() { int q = item.GetScroll(); LoadItems(); item.ScrollTo(q); SyncItems(); } void LayDes::Undo() { if(IsNull(currentlayout)) return; if(CurrentLayout().IsUndo()) { CurrentLayout().Undo(); cursor.Clear(); ReloadItems(); } } void LayDes::Redo() { if(IsNull(currentlayout)) return; if(CurrentLayout().IsRedo()) { CurrentLayout().Redo(); cursor.Clear(); ReloadItems(); } } void LayDes::Cut() { if(IsNull(currentlayout) || cursor.GetCount() == 0) return; Copy(); Delete(); } void LayDes::Delete() { SaveState(); Vector sel(cursor, 1); Sort(sel); cursor.Clear(); CurrentLayout().item.Remove(sel); ReloadItems(); } String LayDes::SaveSelection(bool scrolled) { return CurrentLayout().Save(cursor, scrolled * ZPoint(sb).y, "\r\n") + "\r\n"; } LayoutData LayDes::LoadLayoutData(const String& s) { try { LayoutData l; l.SetCharset(charset); CParser p(s); l.Read(p); return l; } catch(CParser::Error) {} return LayoutData(); } void LayDes::Copy() { if(IsNull(currentlayout) || cursor.GetCount() == 0) return; WriteClipboardUnicodeText(ToUnicode(SaveSelection(), charset)); } void LayDes::SelectAll() { if(IsNull(currentlayout)) return; LayoutData& l = CurrentLayout(); int q = cursor.GetCount() ? cursor.Top() : -1; cursor.Clear(); for(int i = 0; i < l.item.GetCount(); i++) if(i != q) cursor.Add(i); if(q >= 0) cursor.Add(q); SyncItems(); } void LayDes::Duplicate() { if(IsNull(currentlayout) || cursor.GetCount() == 0) return; SaveState(); LayoutData& l = CurrentLayout(); LayoutData d = LoadLayoutData(SaveSelection(false)); int q = Max(cursor) + 1; cursor.Clear(); for(int i = 0; i < d.item.GetCount(); i++) { LayoutItem& m = d.item[i]; d.item[i].pos = MakeLogPos(m.pos, CtrlRect(m.pos, l.size).Offseted(0, 24), l.size); cursor.Add(q + i); } CurrentLayout().item.InsertPick(q, pick(d.item)); ReloadItems(); } void LayDes::Matrix() { if(IsNull(currentlayout) || cursor.GetCount() == 0) return; SaveState(); if(matrix.Execute() != IDOK) return; LayoutData& l = CurrentLayout(); Rect r = CtrlRect(l.item[cursor[0]].pos, l.size); for(int i = 1; i < cursor.GetCount(); i++) r.Union(CtrlRect(l.item[cursor[i]].pos, l.size)); String ls = SaveSelection(); int q = Max(cursor) + 1; for(int x = 0; x < Nvl((int)~matrix.nx, 1); x++) for(int y = 0; y < Nvl((int)~matrix.ny, 1); y++) if(x || y) { LayoutData d = LoadLayoutData(ls); for(int i = 0; i < d.item.GetCount(); i++) { LayoutItem& m = d.item[i]; Rect r = CtrlRect(m.pos, l.size); r.Offset((r.Width() + Nvl((int)~matrix.dx)) * x, (r.Height() + Nvl((int)~matrix.dy)) * y); d.item[i].pos = MakeLogPos(m.pos, r, l.size); cursor.Add(q + i); } int w = d.item.GetCount(); CurrentLayout().item.InsertPick(q, pick(d.item)); q += w; } ReloadItems(); } void LayDes::Paste() { if(IsNull(currentlayout)) return; SaveState(); try { LayoutData l = LoadLayoutData(FromUnicode(ReadClipboardUnicodeText(), charset)); int q = item.GetCount(); if(cursor.GetCount()) { q = 0; for(int i = 0; i < cursor.GetCount(); i++) q = max(q, cursor[i] + 1); } cursor.Clear(); for(int i = 0; i < l.item.GetCount(); i++) cursor.Add(i + q); CurrentLayout().item.InsertPick(q, pick(l.item)); ReloadItems(); } catch(CParser::Error) {} } void LayDes::Align(int type) { if(IsNull(currentlayout) || cursor.GetCount() == 0) return; SaveState(); LayoutData& l = CurrentLayout(); Rect cr = CtrlRect(l.item[cursor.Top()].pos, l.size); for(int i = 0; i < cursor.GetCount(); i++) { LayoutItem& m = l.item[cursor[i]]; Rect r = CtrlRect(m.pos, l.size); switch(type) { case A_LEFT: r.OffsetHorz(cr.left - r.left); break; case A_HCENTER: r.OffsetHorz(cr.left + (cr.Width() - r.Width()) / 2 - r.left); break; case A_RIGHT: r.OffsetHorz(cr.right - r.right); break; case A_TOP: r.OffsetVert(cr.top - r.top); break; case A_VCENTER: r.OffsetVert(cr.top + (cr.Height() - r.Height()) / 2 - r.top); break; case A_BOTTOM: r.OffsetVert(cr.bottom - r.bottom); break; case A_SAMEWIDTH: r.right = r.left + cr.Width(); break; case A_SAMEHEIGHT: r.bottom = r.top + cr.Height(); break; case A_SAMESIZE: r.SetSize(cr.Size()); break; case A_HORZCENTER: r.OffsetHorz((l.size.cx - r.Width()) / 2 - r.left); break; case A_VERTCENTER: r.OffsetVert((l.size.cy - r.Height()) / 2 - r.top); break; case A_MINWIDTH: r.SetSize(m.GetMinSize().cx, r.Height()); break; case A_MINHEIGHT: r.SetSize(r.Width(), m.GetMinSize().cy); break; case A_LABEL: if(m.type == "Label") { Rect rr = r; int q = cursor[i] - 1; while(q >= 0) { if(l.item[q].type != "Label") { rr = CtrlRect(l.item[q].pos, l.size); break; } q--; } q = cursor[i] + 1; while(q < l.item.GetCount()) { if(l.item[q].type != "Label") { rr = CtrlRect(l.item[q].pos, l.size); break; } q++; } r.OffsetVert(rr.top + (rr.Height() - r.Height()) / 2 - r.top); } break; } m.pos = MakeLogPos(m.pos, r, l.size); // if(i == cursor.GetCount() - 1) // sb.ScrollInto(r.Offseted(MARGIN, MARGIN)); } SetStatus(); } void LayDes::SetSprings(dword s) { if(IsNull(currentlayout)) return; LayoutData& l = CurrentLayout(); SaveState(); int xa = (int16)LOWORD(s); int ya = (int16)HIWORD(s); for(int i = 0; i < cursor.GetCount(); i++) { Ctrl::LogPos& pos = l.item[cursor[i]].pos; Rect r = CtrlRect(pos, l.size); if(xa >= 0) pos.x = MakeLogc(xa, r.left, r.right, l.size.cx); if(ya >= 0) pos.y = MakeLogc(ya, r.top, r.bottom, l.size.cy); if(xa == AUTOSPRING) { pos.x = MakeLogc((r.left < l.size.cx / 2 ? LEFT : 0) | (r.right > l.size.cx / 2 ? RIGHT : 0), r.left, r.right, l.size.cx); pos.y = MakeLogc((r.top < l.size.cy / 2 ? TOP : 0) | (r.bottom > l.size.cy/ 2 ? BOTTOM : 0), r.top, r.bottom, l.size.cy); } } SetStatus(); } void LayDes::ShowSelection(bool s) { if(IsNull(currentlayout) || cursor.GetCount() == 0) return; LayoutData& l = CurrentLayout(); for(int i = 0; i < cursor.GetCount(); i++) l.item[cursor[i]].hide = !s; SyncItems(); Refresh(); } void LayDes::MoveUp() { SaveState(); if(IsNull(currentlayout) || cursor.GetCount() == 0) return; LayoutData& l = CurrentLayout(); Vector sc(cursor, 1); Sort(sc); int q = 0; while(q < sc.GetCount() && sc[q] == q) q++; int im = q; while(q < sc.GetCount()) { int i = sc[q++]; l.item.Swap(i, i - 1); } for(q = 0; q < cursor.GetCount(); q++) if(cursor[q] >= im) cursor[q]--; ReloadItems(); } void LayDes::MoveDown() { SaveState(); if(IsNull(currentlayout) || cursor.GetCount() == 0) return; LayoutData& l = CurrentLayout(); Vector sc(cursor, 1); Sort(sc); int q = sc.GetCount() - 1; int u = l.item.GetCount() - 1; while(q >= 0 && sc[q] == u--) q--; int im = q >= 0 ? sc[q] : -1; while(q >= 0) { int i = sc[q--]; l.item.Swap(i, i + 1); } for(q = 0; q < cursor.GetCount(); q++) if(cursor[q] <= im) cursor[q]++; ReloadItems(); } void LayDes::DnDInsert(int line, PasteClip& d) { if(GetInternalPtr(d, "layout-item") == &item && item.IsCursor() && !IsNull(currentlayout) && cursor.GetCount() && d.Accept()) { SaveState(); LayoutData& l = CurrentLayout(); Buffer sel(l.item.GetCount(), false); int n = l.item.GetCount(); l.item.InsertN(n, n); for(int i = 0; i < cursor.GetCount(); i++) sel[cursor[i]] = true; cursor.Clear(); int j = n; for(int i = 0; i < line; i++) if(!sel[i]) l.item.Swap(j++, i); for(int i = 0; i < n; i++) if(sel[i]) { cursor.Add(j - n); l.item.Swap(j++, i); } for(int i = line; i < n; i++) if(!sel[i]) l.item.Swap(j++, i); l.item.Remove(0, n); ReloadItems(); } } void LayDes::Drag() { item.DoDragAndDrop(InternalClip(item, "layout-item"), item.GetDragSample(), DND_MOVE); } bool RectLess(const Rect& a, const Rect& b) { int d = min(a.bottom, b.bottom) - max(a.top, b.top); int w = min(a.GetHeight(), b.GetHeight()); return d > w / 2 ? a.left < b.left : a.top < b.top; } void LayDes::SortItems() { SaveState(); if(IsNull(currentlayout) || cursor.GetCount() < 2) return; LayoutData& l = CurrentLayout(); Sort(cursor); int count = cursor.GetCount(); Array item; Vector rect; for(int i = 0; i < count; ++i) { rect.Add(CtrlRect(l.item[cursor[i]].pos, l.size)); item.Add() = pick(l.item[cursor[i]]); } l.item.Remove(cursor); bool swap = false; do { swap = false; for(int i = 0; i < count - 1; i++) if(RectLess(rect[i + 1], rect[i])) { Swap(rect[i], rect[i + 1]); Swap(item[i], item[i + 1]); swap = true; } } while(swap); int ii = cursor[0]; l.item.InsertPick(ii, pick(item)); cursor.Clear(); for(int i = 0; i < count; i++) cursor.Add(i + ii); ReloadItems(); } void LayDes::Flush() { currentlayout = Null; } LayoutData& LayDes::CurrentLayout() { return layout[currentlayout]; } void LayDes::LayoutCursor() { Flush(); draghandle = -1; currentlayout = list.GetKey(); cursor.Clear(); type.Disable(); variable.Disable(); property.Clear(); if(IsNull(currentlayout)) return; LoadItems(); SyncItems(); SetSb(); if(!search.HasFocus()) SetFocus(); } void LayDes::PrevLayout() { list.Key(K_UP, 0); } void LayDes::NextLayout() { list.Key(K_DOWN, 0); } void LayDes::SyncLayoutList() { int sc = list.GetScroll(); int c = list.GetKey(); list.Clear(); String s = ToUpper((String)~search); for(int i = 0; i < layout.GetCount(); i++) if(ToUpper(layout[i].name).Find(s) >= 0) list.Add(i, layout[i].name); list.ScrollTo(sc); if(!IsNull(c)) list.FindSetCursor(c); LayoutCursor(); } void LayDes::Search() { SyncLayoutList(); if(!list.IsCursor()) list.GoBegin(); } void LayDes::GoTo(int key) { if(list.FindSetCursor(key)) return; search <<= Null; SyncLayoutList(); list.FindSetCursor(key); } void LayDes::AddLayout(bool insert) { String name; for(;;) { if(!EditText(name, "Add new layout", "Layout", CharFilterCid)) return; CParser p(name); if(p.IsId()) break; Exclamation("Invalid name!"); } int q = list.GetKey(); if(!insert || IsNull(q) || !(q >= 0 && q < layout.GetCount())) q = layout.GetCount(); layout.Insert(q).name = name; SyncLayoutList(); GoTo(q); LayoutCursor(); } void LayDes::DuplicateLayout() { if(IsNull(currentlayout)) return; LayoutData& c = CurrentLayout(); String name = c.name; for(;;) { if(!EditText(name, "Duplicate layout", "Layout", CharFilterCid)) return; CParser p(name); if(p.IsId()) break; Exclamation("Invalid name!"); } String data = c.Save(0, "\r\n"); CParser p(data); int next = currentlayout + 1; LayoutData& d = layout.Insert(next); d.Read(p); d.name = name; SyncLayoutList(); GoTo(next); LayoutCursor(); } void LayDes::RenameLayout() { if(IsNull(currentlayout)) return; String name = layout[currentlayout].name; if(!EditText(name, "Rename layout", "Layout", CharFilterCid)) return; int q = list.GetKey(); layout[currentlayout].name = name; SyncLayoutList(); GoTo(q); LayoutCursor(); } void LayDes::RemoveLayout() { if(IsNull(currentlayout) || !PromptYesNo("Remove [* " + DeQtf(layout[currentlayout].name) + "] ?")) return; int q = list.GetKey(); layout.Remove(currentlayout); SyncLayoutList(); if(!IsNull(q)) { GoTo(q + 1); if(!list.IsCursor()) list.GoEnd(); } LayoutCursor(); } void LayDes::MoveLayoutUp() { if(!IsNull(search)) { search <<= Null; SyncLayoutList(); } int q = list.GetKey(); if(q > 0) { layout.Swap(q, q - 1); SyncLayoutList(); GoTo(q - 1); } } void LayDes::MoveLayoutDown() { if(!IsNull(search)) { search <<= Null; SyncLayoutList(); } int q = list.GetKey(); if(q >= 0 && q < layout.GetCount() - 1) { layout.Swap(q, q + 1); SyncLayoutList(); GoTo(q + 1); } } void LayDes::DnDInsertLayout(int line, PasteClip& d) { if(GetInternalPtr(d, "layout") == &list && list.IsCursor() && line >= 0 && line <= layout.GetCount() && d.Accept()) { if(!IsNull(search)) { search <<= Null; SyncLayoutList(); } int c = list.GetKey(); layout.Move(c, line); if(c <= line) line--; SyncLayoutList(); GoTo(line); } } void LayDes::DragLayout() { list.DoDragAndDrop(InternalClip(list, "layout"), list.GetDragSample(), DND_MOVE); } void LayDes::LayoutMenu(Bar& bar) { bool iscursor = list.IsCursor(); bar.Add("Add new layout..", THISBACK1(AddLayout, false)); bar.Add("Insert new layout..", THISBACK1(AddLayout, true)); bar.Add(iscursor, "Duplicate layout..", LayImg::Duplicate(), THISBACK(DuplicateLayout)); bar.Add(iscursor, "Rename layout..", THISBACK(RenameLayout)); bar.Add(iscursor, "Remove layout..", LayImg::Remove(), THISBACK(RemoveLayout)); bar.Separator(); int q = list.GetKey(); bar.Add(iscursor && q > 0, AK_MOVELAYOUTUP, LayImg::MoveUp(), THISBACK(MoveLayoutUp)); bar.Add(iscursor && q < layout.GetCount() - 1, AK_MOVELAYOUTDOWN, LayImg::MoveDown(), THISBACK(MoveLayoutDown)); } LayoutItem& LayDes::CurrentItem() { return CurrentLayout().item[cursor.Top()]; } void LayDes::PropertyChanged() { if(item.IsCursor()) { CurrentItem().Invalidate(); int c = item.GetCursor(); LoadItem(c); SyncItem(c, 2); } Refresh(); SetBar(); } void LayDes::FrameFocus() { if(property.HasFocusDeep()) { SaveState(); SetStatus(); } } void LayDes::ItemClick() { if(IsNull(currentlayout)) return; SaveState(); if(GetShift()) { if(cursor.GetCount()) { int i = minmax(item.GetCursor(), 0, cursor.Top()); int m = max(item.GetCursor(), cursor.Top()); cursor.Clear(); while(i <= m) cursor.Add(i++); SyncItems(); } } else if(item.IsCursor()) { if(!GetCtrl()) cursor.Clear(); int c = item.GetCursor(); int q = FindIndex(cursor, c); if(q >= 0) cursor.Remove(q); else cursor.Add(c); } SetFocus(); SyncItems(); } void LayDes::SyncUsc() { type.ClearList(); for(int i = 0; i < LayoutTypes().GetCount(); i++) if(LayoutTypes()[i].kind != LAYOUT_SUBCTRL) type.AddList(LayoutTypes().GetKey(i)); if(!IsNull(currentlayout)) { LayoutData& d = CurrentLayout(); for(int i = 0; i < d.item.GetCount(); i++) d.item[i].Invalidate(); } Refresh(); } void LayDes::TypeEdit() { if(IsNull(currentlayout) || cursor.GetCount() == 0) return; LayoutData& l = CurrentLayout(); for(int i = 0; i < cursor.GetCount(); i++) { LayoutItem& m = l.item[cursor[i]]; m.SetCharset(charset); String s = m.SaveProperties(); m.Create(~type); try { CParser p(s); m.ReadProperties(p, false); } catch(CParser::Error&) {} item.Set(cursor[i], 0, m.type); } SyncProperties(false); SetStatus(); } void LayDes::VariableEdit() { if(IsNull(currentlayout) || cursor.GetCount() == 0) return; LayoutData& l = CurrentLayout(); LayoutItem& m = l.item[cursor.Top()]; m.variable = ~variable; if(IsNull(m.variable)) item.Set(cursor.Top(), 1, GetLabel(m)); else item.Set(cursor.Top(), 1, m.variable); SyncItem(cursor.Top(), 2); } static int RoundStep(int org, int d, int g) { return d ? itimesfloor(org + d * g + (d > 0 ? 0 : g - 1), g) - org : 0; } bool LayDes::DoHotKey(dword key) { if(key == K_CTRL_F) { search.SetFocus(); return true; } return false; } bool LayDes::DoKey(dword key, int count) { SaveState(); Point move(0, 0); if(!IsNull(currentlayout) && !cursor.IsEmpty()) { switch(key & ~K_CTRL) { case K_SHIFT_LEFT: move.x = -1; break; case K_SHIFT_RIGHT: move.x = +1; break; case K_SHIFT_UP: move.y = -1; break; case K_SHIFT_DOWN: move.y = +1; break; } if(move.x | move.y) { Size grid(1, 1); if(usegrid) { grid.cx = minmax(~setting.gridx, 1, 32); grid.cy = minmax(~setting.gridy, 1, 32); } LayoutData& l = CurrentLayout(); Rect master = CtrlRect(l.item[cursor.Top()].pos, l.size); Size shift; shift.cx = RoundStep(key & K_CTRL ? master.Width() : master.left, move.x, grid.cx); shift.cy = RoundStep(key & K_CTRL ? master.Height() : master.top, move.y, grid.cy); for(int i = 0; i < cursor.GetCount(); i++) { LayoutItem& item = l.item[cursor[i]]; Rect rc = CtrlRect(item.pos, l.size); rc.right += shift.cx; rc.bottom += shift.cy; if(!(key & K_CTRL)) { rc.left += shift.cx; rc.top += shift.cy; } item.pos = MakeLogPos(item.pos, rc, l.size); } SetStatus(false); return true; } } switch(key) { case K_PAGEUP: PrevLayout(); return true; case K_PAGEDOWN: NextLayout(); return true; case K_UP: case K_DOWN: { dword k = (key == K_PAGEUP ? K_UP : key == K_PAGEDOWN ? K_DOWN : key); Ptr focus = GetFocusCtrl(); if(!item.IsCursor() && item.GetCount() > 0) item.SetCursor(k == K_DOWN ? 0 : item.GetCount() - 1); else item.Key(k, count); ItemClick(); if(!!focus) focus->SetWantFocus(); } return true; default: if(key >= ' ' && key < 65536) { if(IsNull(currentlayout) || cursor.GetCount() == 0) return false; LayoutItem& m = CurrentItem(); for(int i = 0; i < m.property.GetCount(); i++) if(m.property[i].name == "SetLabel") return m.property[i].PlaceFocus(key, count); } break; } return MenuBar::Scan(THISBACK(LayoutMenu), key); }