1 #include "LayDes.h"
2
3 using namespace LayoutKeys;
4
5 #define IMAGECLASS LayImg
6 #define IMAGEFILE <ide/LayDes/LayDes.iml>
7 #include <Draw/iml_source.h>
8
9 #define LTIMING(x) // TIMING(x)
10 #define LLOG(x)
11
12 #define MARGIN 8
13
sLay1(int & pos,int & r,int align,int a,int b,int sz)14 static void sLay1(int& pos, int& r, int align, int a, int b, int sz)
15 {
16 pos = a;
17 int size = b;
18 switch(align) {
19 case Ctrl::CENTER:
20 pos = (sz - b) / 2 + a;
21 break;
22 case Ctrl::RIGHT:
23 pos = sz - (a + b);
24 break;
25 case Ctrl::SIZE:
26 size = sz - (a + b);
27 break;
28 }
29 r = pos + max(size, 0);
30 }
31
CtrlRect(Ctrl::LogPos pos,Size sz)32 Rect LayDes::CtrlRect(Ctrl::LogPos pos, Size sz)
33 {
34 Rect r;
35 sLay1(r.left, r.right, pos.x.GetAlign(), pos.x.GetA(), pos.x.GetB(), sz.cx);
36 sLay1(r.top, r.bottom, pos.y.GetAlign(), pos.y.GetA(), pos.y.GetB(), sz.cy);
37 return r;
38 }
39
CtrlRectZ(Ctrl::LogPos pos,Size sz)40 Rect LayDes::CtrlRectZ(Ctrl::LogPos pos, Size sz)
41 {
42 Rect r = CtrlRect(pos, sz);
43 r.left = HorzLayoutZoom(r.left);
44 r.right = HorzLayoutZoom(r.right);
45 r.top = VertLayoutZoom(r.top);
46 r.bottom = VertLayoutZoom(r.bottom);
47 return r;
48 }
49
AddHandle(Draw & w,int x,int y)50 void LayDes::AddHandle(Draw& w, int x, int y)
51 {
52 w.DrawRect(x, y, DPI(6), DPI(6), SColorMark);
53 handle.Add(Point(x, y));
54 }
55
Normalize(Point p)56 Point LayDes::Normalize(Point p)
57 {
58 p += sb;
59 p.Offset(-MARGIN, -MARGIN);
60 return p;
61 }
62
ZPoint(Point p)63 Point LayDes::ZPoint(Point p)
64 {
65 Size csz, dsz;
66 GetZoomRatio(csz, dsz);
67 if(csz.cx && csz.cy && dsz.cx && dsz.cy) {
68 p.x = p.x * dsz.cx / csz.cx;
69 p.y = p.y * dsz.cy / csz.cy;
70 }
71 return p;
72 }
73
SetSb()74 void LayDes::SetSb()
75 {
76 Size sz = Size(0, 0);
77 if(!IsNull(currentlayout)) {
78 LayoutData& l = CurrentLayout();
79 sz = l.size;
80 for(int i = 0; i < l.item.GetCount(); i++)
81 sz = max(sz, (Size)CtrlRect(l.item[i].pos, l.size).BottomRight());
82 }
83 sz += Size(MARGIN, MARGIN);
84 Size csz, dsz;
85 GetZoomRatio(csz, dsz);
86 if(csz.cx && csz.cy && dsz.cx && dsz.cy) {
87 sz.cx = sz.cx * csz.cx / dsz.cx;
88 sz.cy = sz.cy * csz.cy / dsz.cy;
89 }
90 sb.SetTotal(sz);
91 sb.SetPage(sb.GetReducedViewSize());
92 }
93
Scroll()94 void LayDes::Scroll()
95 {
96 Refresh();
97 }
98
Layout()99 void LayDes::Layout()
100 {
101 SetSb();
102 list.ScrollIntoCursor();
103 }
104
GetSprings(Rect & l,Rect & t,Rect & r,Rect & b)105 void LayDes::GetSprings(Rect& l, Rect& t, Rect& r, Rect& b)
106 {
107 if(IsNull(currentlayout) || !cursor.GetCount()) {
108 l = t = r = b = Null;
109 return;
110 }
111 Rect ir = CtrlRectZ(CurrentItem().pos, CurrentLayout().size);
112 int h4 = ir.Height() / 4;
113 int w4 = ir.Width() / 4;
114 int fv = DPI(5);
115 int tn = DPI(10);
116 l = RectC(-MARGIN, ir.top + h4 - fv, ir.left + MARGIN, tn);
117 t = RectC(ir.left + w4 - fv, -MARGIN, tn, ir.top + MARGIN);
118 r = RectC(ir.right, ir.bottom - h4 - fv, 9999, tn);
119 b = RectC(ir.right - w4 - fv, ir.bottom, tn, 9999);
120 }
121
DrawSpring(Draw & w,const Rect & r,bool horz,bool hard)122 void LayDes::DrawSpring(Draw& w, const Rect& r, bool horz, bool hard)
123 {
124 if(hard)
125 w.DrawRect(horz ? r.DeflatedVert(4) : r.DeflatedHorz(4), Red);
126 else
127 if(horz)
128 for(int x = r.left; x < r.right; x += 4)
129 w.DrawRect(x, r.top, 1, r.Height(), SColorShadow);
130 else
131 for(int y = r.top; y < r.bottom; y += 4)
132 w.DrawRect(r.left, y, r.Width(), 1, SColorShadow);
133 }
134
ParseLayoutRef(String cls,String & base) const135 int LayDes::ParseLayoutRef(String cls, String& base) const
136 {
137 const char *p = cls;
138 if(p[0] != 'W' || p[1] != 'i' || p[2] != 't' || p[3] != 'h')
139 return -1;
140 const char *b = (p += 4);
141 while(IsAlNum(*p) || *p == '_')
142 p++;
143 if(p <= b)
144 return -1;
145 String layoutid(b, p);
146 int fi = FindFieldIndex(layout, &LayoutData::name, layoutid);
147 if(fi < 0)
148 return -1;
149 while(*p && (byte)*p <= ' ')
150 p++;
151 if(*p++ != '<')
152 return -1;
153 while(*p && (byte)*p <= ' ')
154 p++;
155 const char *e = cls.End();
156 while(e > p && (byte)e[-1] <= ' ')
157 e--;
158 if(e > p && e[-1] == '>')
159 e--;
160 while(e > p && (byte)e[-1] <= ' ')
161 e--;
162 base = String(p, e);
163 return fi;
164 }
165
PaintLayoutItems(Draw & w,int layid,Size size,Index<int> & passed,const Vector<bool> & cursor)166 void LayDes::PaintLayoutItems(Draw& w, int layid, Size size, Index<int>& passed, const Vector<bool>& cursor)
167 {
168 if(passed.Find(layid) >= 0)
169 return;
170 passed.Add(layid);
171 LayoutData& l = layout[layid];
172 bool nocursor = true;
173 for(int i = 0; i < l.item.GetCount(); i++) {
174 LayoutItem& m = l.item[i];
175 if(m.hide)
176 continue;
177 Rect r = CtrlRectZ(m.pos, size);
178 String dummy;
179 int lr = ParseLayoutRef(m.type, dummy);
180 DrawFrame(w, r, Blend(SLtGray(), SCyan(), 35));
181 w.Clipoff(r);
182 if(lr < 0)
183 m.Paint(w, r.Size());
184 else
185 if(i >= cursor.GetCount() ? nocursor : cursor[i]) {
186 PaintLayoutItems(w, lr, r.Size(), passed, Vector<bool>());
187 nocursor = false;
188 }
189 w.End();
190 }
191 passed.Drop();
192 }
193
Paint(Draw & w)194 void LayDes::Paint(Draw& w)
195 {
196 LTIMING("Paint");
197 Size sz = GetSize();
198 w.DrawRect(sz, SColorPaper);
199 if(!IsNull(fileerror))
200 w.DrawText(16, 16, "FILE ERROR: " + fileerror, ArialZ(14).Bold(), Red);
201 if(IsNull(currentlayout))
202 return;
203 w.Offset(-sb.Get());
204 LayoutData& l = CurrentLayout();
205 w.Offset(MARGIN, MARGIN);
206 Size lsz = LayoutZoom(l.size);
207 w.DrawRect(0, 0, lsz.cx, lsz.cy, SLtGray);
208 if(setting.paintgrid) {
209 int gx = minmax((int)~setting.gridx, 1, 32);
210 int gy = minmax((int)~setting.gridy, 1, 32);
211 for(int x = 0; x < l.size.cx; x += gx)
212 for(int y = 0; y < l.size.cy; y += gy)
213 w.DrawRect(x, y, 1, 1, SColorPaper);
214 }
215 DrawFrame(w, -1, -1, lsz.cx + 2, lsz.cy + 2, SColorText);
216 handle.Clear();
217 AddHandle(w, lsz.cx, lsz.cy / 2 - DPI(3));
218 AddHandle(w, lsz.cx / 2 - DPI(3), lsz.cy);
219 AddHandle(w, lsz.cx, lsz.cy);
220 int i;
221 Index<int> passed;
222 Vector<bool> cursorflags;
223 if(!cursor.IsEmpty()) {
224 cursorflags.SetCount(l.item.GetCount(), cursor.IsEmpty());
225 for(i = 0; i < cursor.GetCount(); i++)
226 cursorflags[cursor[i]] = true;
227 }
228 PaintLayoutItems(w, currentlayout, l.size, passed, cursorflags);
229 if(!HasCapture() || draghandle == 14) {
230 for(i = 0; i < cursor.GetCount(); i++) {
231 LayoutItem& m = l.item[cursor[i]];
232 Rect r = CtrlRectZ(m.pos, l.size);
233 DrawFatFrame(w, r, i == cursor.GetCount() - 1 ? Cyan : Brown, DPI(-3));
234 if(i == cursor.GetCount() - 1) {
235 int lrm = r.left + r.Width() / 2 - DPI(3);
236 int tbm = r.top + r.Height() / 2 - DPI(3);
237 AddHandle(w, r.left - DPI(6), r.top - DPI(6));
238 AddHandle(w, lrm, r.top - DPI(6));
239 AddHandle(w, r.right, r.top - DPI(6));
240 AddHandle(w, r.left - DPI(6), tbm);
241 AddHandle(w, r.right, tbm);
242 AddHandle(w, r.left - DPI(6), r.bottom);
243 AddHandle(w, lrm, r.bottom);
244 AddHandle(w, r.right, r.bottom);
245 }
246 }
247 if(cursor.GetCount()) {
248 LayoutItem& m = CurrentItem();
249 Rect l, t, r, b;
250 GetSprings(l, t, r, b);
251 DrawSpring(w, l, true, m.pos.x.GetAlign() & Ctrl::LEFT);
252 DrawSpring(w, r, true, m.pos.x.GetAlign() & Ctrl::RIGHT);
253 DrawSpring(w, t, false, m.pos.y.GetAlign() & Ctrl::TOP);
254 DrawSpring(w, b, false, m.pos.y.GetAlign() & Ctrl::BOTTOM);
255 }
256 }
257 if(HasCapture() && draghandle == 14)
258 DrawFrame(w, dragrect.Normalized(), LtRed);
259 w.End();
260 w.End();
261 }
262
SaveState()263 void LayDes::SaveState()
264 {
265 if(IsNull(currentlayout))
266 return;
267 CurrentLayout().SaveState();
268 SetBar();
269 }
270
SetStatus(bool down)271 void LayDes::SetStatus(bool down)
272 {
273 String s;
274 if(!IsNull(currentlayout)) {
275 Size sz = CurrentLayout().size;
276 s << sz;
277 if(cursor.GetCount()) {
278 Rect r = CtrlRect(CurrentItem().pos, sz);
279 s << ": " << r << " - {" << sz.cx - r.right << ", " << sz.cy - r.bottom << '}';
280 }
281 }
282 status.SetLabel(s);
283 Refresh();
284 SetBar();
285 SetSb();
286 }
287
FindHandle(Point p)288 int LayDes::FindHandle(Point p)
289 {
290 for(int i = 0; i < handle.GetCount(); i++) {
291 Point h = handle[i];
292 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))
293 return i;
294 }
295 return -1;
296 }
297
FindItem(Point p)298 int LayDes::FindItem(Point p)
299 {
300 if(IsNull(currentlayout))
301 return -1;
302 LayoutData& l = CurrentLayout();
303 int ii = -1;
304 int min = INT_MAX;
305 for(int i = 0; i < l.item.GetCount(); i++) {
306 LayoutItem& m = l.item[i];
307 if(m.hide)
308 continue;
309 Rect r = CtrlRect(m.pos, l.size);
310 if(r.Contains(p)) {
311 int mm = r.Width() * r.Height();
312 if(mm < min) {
313 ii = i;
314 min = mm;
315 }
316 }
317 }
318 return ii;
319 }
320
CursorImage(Point p,dword keyflags)321 Image LayDes::CursorImage(Point p, dword keyflags)
322 {
323 int hi;
324 if(HasCapture())
325 hi = draghandle;
326 else
327 hi = FindHandle(Normalize(p));
328 Image (*id[11])() = {
329 Image::SizeHorz, Image::SizeVert, Image::SizeBottomRight,
330 Image::SizeTopLeft, Image::SizeVert, Image::SizeTopRight,
331 Image::SizeHorz, Image::SizeHorz,
332 Image::SizeBottomLeft, Image::SizeVert, Image::SizeBottomRight,
333 };
334 if(hi >= 0 && hi < 11)
335 return (*id[hi])();
336 return Image::Arrow();
337 }
338
MakeLogc(int align,int a,int b,int sz)339 Ctrl::Logc MakeLogc(int align, int a, int b, int sz)
340 {
341 int isz = b - a;
342 switch(align) {
343 case Ctrl::LEFT:
344 return Ctrl::PosLeft(a, isz);
345 case Ctrl::RIGHT:
346 return Ctrl::PosRight(sz - b, isz);
347 case Ctrl::CENTER:
348 return Ctrl::PosCenter(isz, a - (sz - isz) / 2);
349 }
350 return Ctrl::PosSize(a, sz - b);
351 }
352
MakeLogPos(int ax,int ay,const Rect & r,Size sz)353 Ctrl::LogPos MakeLogPos(int ax, int ay, const Rect& r, Size sz)
354 {
355 return Ctrl::LogPos(MakeLogc(ax, r.left, r.right, sz.cx),
356 MakeLogc(ay, r.top, r.bottom, sz.cy));
357 }
358
MakeLogPos(Ctrl::LogPos p,const Rect & r,Size sz)359 Ctrl::LogPos MakeLogPos(Ctrl::LogPos p, const Rect& r, Size sz)
360 {
361 return MakeLogPos(p.x.GetAlign(), p.y.GetAlign(), r, sz);
362 }
363
364 struct IDisplay : public Display {
365 Color paper, ink;
366 Font font;
367
GetStdSizeIDisplay368 Size GetStdSize(const Value& q) const {
369 Size sz = GetSmartTextSize(~q, font);
370 sz.cx += 2 * DPI(4);
371 return sz;
372 }
373
PaintIDisplay374 void Paint(Draw& w, const Rect& r, const Value& q,
375 Color, Color, dword s) const {
376 w.DrawRect(r, paper);
377 DrawSmartText(w, r.left + DPI(4), r.top, r.Width(), ~q, font, ink);
378 }
IDisplayIDisplay379 IDisplay(Color paper, Color ink, Font font = StdFont())
380 : paper(paper), ink(ink), font(font) {}
381 };
382
383 struct TDisplay : public Display {
384 Color paper, ink;
385 Font font;
386
GetStdSizeTDisplay387 Size GetStdSize(const Value& q) const {
388 Size sz = GetSmartTextSize(~q, font);
389 sz.cx += 2 * DPI(4) + DPI(6) + sz.cy * 8 / 3;
390 return sz;
391 }
392
PaintTDisplay393 void Paint(Draw& w, const Rect& r, const Value& q,
394 Color, Color, dword s) const {
395 w.DrawRect(r, paper);
396 int dx = r.Height() * 8 / 3;
397 Image icon = GetTypeIcon(String(q), dx, r.Height(), 1, paper);
398 w.DrawImage(r.left + DPI(4), r.top + (r.Height() - icon.GetSize().cy) / 2, icon);
399 DrawSmartText(w, r.left + dx + DPI(6), r.top, r.Width(), ~q, font, ink);
400 }
TDisplayTDisplay401 TDisplay(Color paper, Color ink, Font font = StdFont())
402 : paper(paper), ink(ink), font(font) {}
403 };
404
SyncItems()405 void LayDes::SyncItems()
406 {
407 LTIMING("SyncItems");
408 if(IsNull(currentlayout))
409 return;
410 int i;
411 for(i = 0; i < item.GetCount(); i++)
412 SyncItem(i, 0);
413 for(i = 0; i < cursor.GetCount(); i++)
414 SyncItem(cursor[i], 1);
415 if(cursor.GetCount()) {
416 SyncItem(cursor.Top(), 2);
417 item.SetCursor(cursor.Top());
418 }
419 else
420 item.KillCursor();
421 SetStatus();
422 SyncProperties(true);
423 }
424
SyncItem(int i,int style)425 void LayDes::SyncItem(int i, int style)
426 {
427 if(CurrentLayout().item[i].hide) {
428 static IDisplay dnormal(SColorPaper, SColorText, StdFont().Italic());
429 static IDisplay dselect(SColorLtFace, SColorText, StdFont().Italic());
430 static IDisplay dcursor(SColorFace, SColorText, StdFont().Italic());
431 static TDisplay tnormal(SColorPaper, SColorText, StdFont().Italic());
432 static TDisplay tselect(SColorLtFace, SColorText, StdFont().Italic());
433 static TDisplay tcursor(SColorFace, SColorText, StdFont().Italic());
434 static IDisplay lnormal(SColorPaper, Green, StdFont().Italic());
435 static IDisplay lselect(SColorLtFace, Green, StdFont().Italic());
436 static IDisplay lcursor(SColorFace, Green, StdFont().Italic());
437 const Display *noicon[] = { &dnormal, &dselect, &dcursor };
438 const Display *isicon[] = { &tnormal, &tselect, &tcursor };
439 const Display *label[] = { &lnormal, &lselect, &lcursor };
440 bool icons = setting.showicons;
441 item.SetDisplay(i, 0, *(icons ? isicon : noicon)[style]);
442 item.SetDisplay(i, 1, *(IsNull(CurrentLayout().item[i].variable) ? label : noicon)[style]);
443 }
444 else {
445 static IDisplay dnormal(SColorPaper, SColorText);
446 static IDisplay dselect(SColorLtFace, SColorText);
447 static IDisplay dcursor(SColorFace, SColorText);
448 static TDisplay tnormal(SColorPaper, SColorText);
449 static TDisplay tselect(SColorLtFace, SColorText);
450 static TDisplay tcursor(SColorFace, SColorText);
451 static IDisplay lnormal(SColorPaper, Green);
452 static IDisplay lselect(SColorLtFace, Green);
453 static IDisplay lcursor(SColorFace, Green);
454 const Display *noicon[] = { &dnormal, &dselect, &dcursor };
455 const Display *isicon[] = { &tnormal, &tselect, &tcursor };
456 const Display *label[] = { &lnormal, &lselect, &lcursor };
457 bool icons = setting.showicons;
458 item.SetDisplay(i, 0, *(icons ? isicon : noicon)[style]);
459 item.SetDisplay(i, 1, *(IsNull(CurrentLayout().item[i].variable) ? label : noicon)[style]);
460 }
461 }
462
SyncProperties(bool sync_type_var)463 void LayDes::SyncProperties(bool sync_type_var)
464 {
465 property.Clear();
466 if(sync_type_var) {
467 type.Disable();
468 variable.Disable();
469 type <<= variable <<= Null;
470 }
471 if(cursor.GetCount()) {
472 LayoutItem& m = CurrentItem();
473 int i;
474 for(i = 0; i < m.property.GetCount(); i++) {
475 property.Add(m.property[i]);
476 m.property[i].WhenAction = THISBACK(PropertyChanged);
477 }
478 if(sync_type_var) {
479 String tp = m.type;
480 type <<= m.type;
481 for(i = 0; i < cursor.GetCount() - 1; i++)
482 if(CurrentLayout().item[cursor[i]].type != tp) {
483 type <<= Null;
484 break;
485 }
486 variable <<= m.variable;
487 type.Enable();
488 variable.Enable();
489 }
490 }
491 }
492
SelectOne(int ii,dword flags)493 void LayDes::SelectOne(int ii, dword flags)
494 {
495 if(ii < 0) {
496 if(flags & (K_SHIFT|K_CTRL))
497 return;
498 cursor.Clear();
499 }
500 else
501 if(flags & (K_SHIFT|K_CTRL)) {
502 int q = FindIndex(cursor, ii);
503 if(q >= 0)
504 cursor.Remove(q);
505 else
506 cursor.Add(ii);
507 }
508 else {
509 cursor.Clear();
510 cursor.Add(ii);
511 }
512 SyncItems();
513 }
514
StoreItemRects()515 void LayDes::StoreItemRects()
516 {
517 if(IsNull(currentlayout))
518 return;
519 LayoutData& l = CurrentLayout();
520 itemrect.SetCount(cursor.GetCount());
521 for(int i = 0; i < cursor.GetCount(); i++)
522 itemrect[i] = CtrlRect(l.item[cursor[i]].pos, l.size);
523 }
524
LeftDown(Point p,dword keyflags)525 void LayDes::LeftDown(Point p, dword keyflags)
526 {
527 if(IsNull(currentlayout))
528 return;
529 SaveState();
530 SetFocus();
531 SetCapture();
532 LayoutData& l = CurrentLayout();
533 draglayoutsize = l.size;
534 p = Normalize(p);
535 draghandle = FindHandle(p);
536 dragbase = ZPoint(p);
537 if(draghandle >= 0)
538 StoreItemRects();
539 else {
540 int ii = FindItem(dragbase);
541 if(ii >= 0) {
542 if(GetShift() || GetCtrl() || FindIndex(cursor, ii) < 0)
543 SelectOne(ii, keyflags);
544 if(cursor.GetCount()) {
545 draghandle = 11;
546 StoreItemRects();
547 SetStatus();
548 }
549 }
550 else {
551 if(cursor.GetCount()) {
552 LayoutItem& m = CurrentItem();
553 StoreItemRects();
554 Rect lr, tr, rr, br;
555 GetSprings(lr, tr, rr, br);
556 int xa = m.pos.x.GetAlign();
557 int ya = m.pos.y.GetAlign();
558 if(lr.Contains(p))
559 xa ^= 1;
560 if(rr.Contains(p))
561 xa ^= 2;
562 if(tr.Contains(p))
563 ya ^= 1;
564 if(br.Contains(p))
565 ya ^= 2;
566 if(xa != m.pos.x.GetAlign() || ya != m.pos.y.GetAlign()) {
567 SetSprings(MAKELONG(xa, ya));
568 SetStatus();
569 return;
570 }
571 }
572 dragrect.left = dragrect.right = p.x;
573 dragrect.top = dragrect.bottom = p.y;
574 draghandle = 14;
575 if(GetCtrl() || GetShift())
576 basesel = cursor.GetCount();
577 else {
578 basesel = 0;
579 cursor.Clear();
580 }
581 SyncItems();
582 }
583 }
584 SetStatus(true);
585 }
586
LeftRepeat(Point p,dword keyflags)587 void LayDes::LeftRepeat(Point p, dword keyflags)
588 {
589 MouseMove(p, keyflags);
590 }
591
MouseMove(Point p,dword keyflags)592 void LayDes::MouseMove(Point p, dword keyflags)
593 {
594 if(!HasCapture() || IsNull(currentlayout))
595 return;
596 Point pz = Normalize(p);
597 p = ZPoint(pz);
598 LayoutData& l = CurrentLayout();
599 bool smallmove = max(abs(p.x - dragbase.x), abs(p.y - dragbase.y)) < 4;
600 if(draghandle == 14) {
601 dragrect.right = pz.x;
602 dragrect.bottom = pz.y;
603 cursor.SetCount(basesel);
604 Rect r = dragrect.Normalized();
605 r = Rect(ZPoint(r.TopLeft()), ZPoint(r.BottomRight()));
606 int mind = INT_MAX;
607 int mini = -1;
608 for(int i = 0; i < l.item.GetCount(); i++) {
609 LayoutItem& m = l.item[i];
610 Rect ir = CtrlRect(m.pos, l.size);
611 if(r.Contains(ir) && FindIndex(cursor, i) < 0) {
612 Point ip = ir.CenterPoint();
613 int mm = (ip.x - dragrect.left) * (ip.x - dragrect.left)
614 + (ip.y - dragrect.top) * (ip.y - dragrect.top);
615 if(mm < mind) {
616 mind = mm;
617 mini = cursor.GetCount();
618 }
619 cursor.Add(i);
620 }
621 }
622 if(mini >= 0)
623 Swap(cursor.Top(), cursor[mini]);
624 if(cursor.GetCount())
625 item.SetCursor(cursor.Top());
626 else
627 item.KillCursor();
628 SyncItems();
629 SetStatus(false);
630 LTIMING("MouseMove Sync");
631 Sync();
632 return;
633 }
634 int gx = 1;
635 int gy = 1;
636 if(usegrid == !(keyflags & K_ALT)) {
637 gx = minmax((int)~setting.gridx, 1, 32);
638 gy = minmax((int)~setting.gridy, 1, 32);
639 }
640 p -= dragbase;
641 if(draghandle < 3) {
642 Vector<Rect> r;
643 r.SetCount(l.item.GetCount());
644 for(int i = 0; i < l.item.GetCount(); i++)
645 r[i] = CtrlRect(l.item[i].pos, l.size);
646 if((draghandle + 1) & 1)
647 l.size.cx = max(0, draglayoutsize.cx + p.x) / gx * gx;
648 if((draghandle + 1) & 2)
649 l.size.cy = max(0, draglayoutsize.cy + p.y) / gy * gy;
650 if(!sizespring)
651 for(int i = 0; i < l.item.GetCount(); i++) {
652 LayoutItem& m = l.item[i];
653 m.pos = MakeLogPos(m.pos, r[i], l.size);
654 }
655 SetStatus(true);
656 SetSb();
657 Sync();
658 return;
659 }
660 if(!item.IsCursor())
661 return;
662 if(draghandle == 11) {
663 if(smallmove)
664 return;
665 draghandle = 12;
666 }
667 Point md = Point(0, 0);
668 for(int i = 0; i < cursor.GetCount(); i++) {
669 LayoutItem& m = l.item[cursor[i]];
670 Rect r = itemrect[i];
671 Size minsize = ignoreminsize ? Size(0, 0) : m.GetMinSize();
672 if(keyflags & K_CTRL)
673 minsize = Size(0, 0);
674 if(draghandle == 3 || draghandle == 4 || draghandle == 5)
675 r.top = min(r.bottom - minsize.cy, (r.top + p.y) / gy * gy);
676 if(draghandle == 8 || draghandle == 9 || draghandle == 10)
677 r.bottom = max(r.top + minsize.cy, (r.bottom + p.y) / gy * gy);
678 if(draghandle == 3 || draghandle == 6 || draghandle == 8)
679 r.left = min(r.right - minsize.cx, (r.left + p.x) / gy * gy);
680 if(draghandle == 5 || draghandle == 7 || draghandle == 10)
681 r.right = max(r.left + minsize.cx, (r.right + p.x) / gy * gy);
682 if(draghandle == 12) {
683 Size sz = r.GetSize();
684 if(i == 0) {
685 Rect q = r;
686 r.left = (r.left + p.x) / gx * gx;
687 r.top = (r.top + p.y) / gy * gy;
688 md = r.TopLeft() - q.TopLeft();
689 }
690 else
691 r += md;
692 r.SetSize(sz);
693 }
694 m.pos = MakeLogPos(m.pos, r, draglayoutsize);
695 }
696 SetStatus(true);
697 Sync();
698 }
699
LeftUp(Point p,dword keyflags)700 void LayDes::LeftUp(Point p, dword keyflags)
701 {
702 if(draghandle == 11 && (keyflags & (K_SHIFT|K_CTRL)) == 0)
703 SelectOne(FindItem(ZPoint(Normalize(p))), 0);
704 draghandle = -1;
705 SyncItems();
706 }
707
CreateCtrl(const String & _type)708 void LayDes::CreateCtrl(const String& _type)
709 {
710 if(IsNull(currentlayout))
711 return;
712 LayoutData& l = CurrentLayout();
713 int c = l.item.GetCount();
714 if(cursor.GetCount())
715 c = Max(cursor) + 1;
716 LayoutItem& m = l.item.Insert(c);
717 m.Create(_type);
718 Point p = dragbase;
719 Size sza, szb;
720 GetZoomRatio(sza, szb);
721 if(sza.cx)
722 p.x = szb.cx * p.x / sza.cx;
723 if(sza.cy)
724 p.y = szb.cy * p.y / sza.cy;
725 if(usegrid) {
726 p.x = p.x / (int)~setting.gridx * (int)~setting.gridx;
727 p.y = p.y / (int)~setting.gridy * (int)~setting.gridy;
728 }
729 Rect r(p, m.GetStdSize());
730 m.pos = MakeLogPos(Ctrl::LEFT, Ctrl::TOP, r, l.size);
731 cursor.Clear();
732 cursor.Add(c);
733 ReloadItems();
734 if(!search.HasFocus()) {
735 if(IsNull(_type))
736 type.SetFocus();
737 else {
738 int q = m.FindProperty("SetLabel");
739 if(q >= 0 && findarg(_type, "Label", "LabelBox") >= 0)
740 m.property[q].PlaceFocus(0, 0);
741 else
742 variable.SetFocus();
743 }
744 }
745 }
746
Group(Bar & bar,const String & group)747 void LayDes::Group(Bar& bar, const String& group)
748 {
749 int i;
750 Vector<String> type;
751 for(i = 0; i < LayoutTypes().GetCount(); i++) {
752 LayoutType& m = LayoutTypes()[i];
753 if((IsNull(group) || m.group == group) && m.kind == LAYOUT_CTRL)
754 type.Add(LayoutTypes().GetKey(i));
755 }
756 Sort(type);
757 int h = 3 * StdFont().Info().GetHeight() / 2;
758 int w = 8 * h / 3;
759 if(auto *mb = dynamic_cast<MenuBar *>(&bar))
760 mb->LeftGap(w + 6);
761 int q = 0;
762 for(i = 0; i < type.GetCount(); i++) {
763 bar.Add(type[i], GetTypeIcon(type[i], w, h, 0, SLtGray),
764 THISBACK1(CreateCtrl, type[i]));
765 if((q++ + 2) % 16 == 0)
766 bar.Break();
767 }
768 }
769
TemplateGroup(Bar & bar,TempGroup tg)770 void LayDes::TemplateGroup(Bar& bar, TempGroup tg)
771 {
772 int i;
773 Vector<String> type;
774 for(i = 0; i < LayoutTypes().GetCount(); i++) {
775 LayoutType& m = LayoutTypes()[i];
776 if((IsNull(tg.group) || m.group == tg.group) && m.kind == LAYOUT_CTRL)
777 type.Add(LayoutTypes().GetKey(i));
778 }
779 Sort(type);
780 int h = 3 * StdFont().Info().GetHeight() / 2;
781 int w = 8 * h / 3;
782 if(auto *mb = dynamic_cast<MenuBar *>(&bar))
783 mb->LeftGap(w + 6);
784 int q = 0;
785 for(i = 0; i < type.GetCount(); i++) {
786 bar.Add(type[i], GetTypeIcon(type[i], w, h, 0, SLtGray),
787 THISBACK1(CreateCtrl, tg.temp + '<' + type[i] + '>'));
788 if((q++ + 2) % 16 == 0)
789 bar.Break();
790 }
791 }
792
Template(Bar & bar,const String & temp)793 void LayDes::Template(Bar& bar, const String& temp)
794 {
795 Index<String> group;
796 Vector<String> type;
797 int h = 3 * StdFont().Info().GetHeight() / 2;
798 int w = 8 * h / 3;
799 if(auto *mb = dynamic_cast<MenuBar *>(&bar))
800 mb->LeftGap(w + 6);
801 int i;
802 for(i = 0; i < LayoutTypes().GetCount(); i++) {
803 LayoutType& m = LayoutTypes()[i];
804 if(!IsNull(m.group))
805 group.FindAdd(m.group);
806 else
807 if(m.kind == LAYOUT_CTRL)
808 type.Add(LayoutTypes().GetKey(i));
809 }
810 Vector<String> sg = group.PickKeys();
811 Sort(sg);
812 Sort(type);
813 int q = 0;
814 for(i = 0; i < sg.GetCount(); i++) {
815 bar.Add(sg[i], THISBACK1(TemplateGroup, TempGroup(temp, sg[i])));
816 if((q++ + 2) % 16 == 0)
817 bar.Break();
818 }
819 bar.Add("All", THISBACK1(TemplateGroup, TempGroup(temp, String())));
820 if((q++ + 2) % 16 == 0)
821 bar.Break();
822 for(i = 0; i < type.GetCount(); i++) {
823 bar.Add(type[i], GetTypeIcon(type[i], w, h, 0, SLtGray),
824 THISBACK1(CreateCtrl, temp + '<' + type[i] + '>'));
825 if((q++ + 2) % 16 == 0)
826 bar.Break();
827 }
828 }
829
Templates(Bar & bar)830 void LayDes::Templates(Bar& bar)
831 {
832 Vector<String> temp;
833 int i;
834 for(i = 0; i < LayoutTypes().GetCount(); i++)
835 if(LayoutTypes()[i].kind == LAYOUT_TEMPLATE)
836 temp.Add(LayoutTypes().GetKey(i));
837 Sort(temp);
838 int q = 0;
839 for(i = 0; i < temp.GetCount(); i++) {
840 bar.Add(temp[i], THISBACK1(Template, temp[i]));
841 if((q++ + 2) % 16 == 0)
842 bar.Break();
843 }
844 }
845
RightDown(Point p,dword keyflags)846 void LayDes::RightDown(Point p, dword keyflags)
847 {
848 if(IsNull(currentlayout) || HasCapture()) return;
849 dragbase = Normalize(p);
850 MenuBar menu;
851 menu.MaxIconSize(Size(64, 64));
852 int h = StdFont().Info().GetHeight();
853 int w = 8 * h / 3;
854 menu.LeftGap(w + 2);
855 menu.Add("User class", THISBACK1(CreateCtrl, ""));
856 menu.Separator();
857 Index<String> group;
858 Vector<String> type;
859 int i;
860 for(i = 0; i < LayoutTypes().GetCount(); i++) {
861 LayoutType& m = LayoutTypes()[i];
862 if(!IsNull(m.group))
863 group.FindAdd(m.group);
864 else
865 if(m.kind == LAYOUT_CTRL)
866 type.Add(LayoutTypes().GetKey(i));
867 }
868 Vector<String> sg = group.PickKeys();
869 Sort(sg);
870 Sort(type);
871 int q = 0;
872 for(i = 0; i < sg.GetCount(); i++) {
873 menu.Add(sg[i], THISBACK1(Group, sg[i]));
874 if((q++ + 2) % 16 == 0)
875 menu.Break();
876 }
877 menu.Add("All", THISBACK1(Group, String()));
878 menu.Add("Templates", THISBACK(Templates));
879 if((q++ + 2) % 16 == 0)
880 menu.Break();
881 for(i = 0; i < type.GetCount(); i++) {
882 menu.Add(type[i], GetTypeIcon(type[i], w, h, 0, SLtGray),
883 THISBACK1(CreateCtrl, type[i]));
884 if((q++ + 2) % 16 == 0)
885 menu.Break();
886 }
887 menu.Execute();
888 }
889
LoadItems()890 void LayDes::LoadItems()
891 {
892 int nitems = CurrentLayout().item.GetCount();
893 item.SetCount(nitems);
894 for(int i = 0; i < nitems; i++)
895 LoadItem(i);
896 property.Clear();
897 }
898
GetLabel(const LayoutItem & m)899 String GetLabel(const LayoutItem& m)
900 {
901 EscValue l = m.ExecuteMethod("GetLabelMethod");
902 if(l.IsVoid())
903 for(int p = 0; p < m.property.GetCount(); p++)
904 if(m.property[p].name == "SetLabel") {
905 Value prop = ~m.property[p];
906 return IsString(prop) && !IsNull(prop) ? AsCString(prop) : Null;
907 }
908 return l;
909 }
910
LoadItem(int ii)911 void LayDes::LoadItem(int ii)
912 {
913 const LayoutItem& m = CurrentLayout().item[ii];
914 String varlbl = m.variable;
915 if(IsNull(varlbl))
916 varlbl = GetLabel(m);
917 item.Set(ii, 0, m.type);
918 item.Set(ii, 1, varlbl);
919 item.Set(ii, 2, m.hide);
920 }
921
ReloadItems()922 void LayDes::ReloadItems()
923 {
924 int q = item.GetScroll();
925 LoadItems();
926 item.ScrollTo(q);
927 SyncItems();
928 }
929
Undo()930 void LayDes::Undo()
931 {
932 if(IsNull(currentlayout))
933 return;
934 if(CurrentLayout().IsUndo()) {
935 CurrentLayout().Undo();
936 cursor.Clear();
937 ReloadItems();
938 }
939 }
940
Redo()941 void LayDes::Redo()
942 {
943 if(IsNull(currentlayout))
944 return;
945 if(CurrentLayout().IsRedo()) {
946 CurrentLayout().Redo();
947 cursor.Clear();
948 ReloadItems();
949 }
950 }
951
Cut()952 void LayDes::Cut()
953 {
954 if(IsNull(currentlayout) || cursor.GetCount() == 0)
955 return;
956 Copy();
957 Delete();
958 }
959
Delete()960 void LayDes::Delete()
961 {
962 SaveState();
963 Vector<int> sel(cursor, 1);
964 Sort(sel);
965 cursor.Clear();
966 CurrentLayout().item.Remove(sel);
967 ReloadItems();
968 }
969
SaveSelection(bool scrolled)970 String LayDes::SaveSelection(bool scrolled)
971 {
972 return CurrentLayout().Save(cursor, scrolled * ZPoint(sb).y, "\r\n") + "\r\n";
973 }
974
LoadLayoutData(const String & s)975 LayoutData LayDes::LoadLayoutData(const String& s)
976 {
977 try {
978 LayoutData l;
979 l.SetCharset(charset);
980 CParser p(s);
981 l.Read(p);
982 return l;
983 }
984 catch(CParser::Error) {}
985 return LayoutData();
986 }
987
Copy()988 void LayDes::Copy()
989 {
990 if(IsNull(currentlayout) || cursor.GetCount() == 0)
991 return;
992 WriteClipboardUnicodeText(ToUnicode(SaveSelection(), charset));
993 }
994
SelectAll()995 void LayDes::SelectAll()
996 {
997 if(IsNull(currentlayout))
998 return;
999 LayoutData& l = CurrentLayout();
1000 int q = cursor.GetCount() ? cursor.Top() : -1;
1001 cursor.Clear();
1002 for(int i = 0; i < l.item.GetCount(); i++)
1003 if(i != q)
1004 cursor.Add(i);
1005 if(q >= 0)
1006 cursor.Add(q);
1007 SyncItems();
1008 }
1009
Duplicate()1010 void LayDes::Duplicate()
1011 {
1012 if(IsNull(currentlayout) || cursor.GetCount() == 0)
1013 return;
1014 SaveState();
1015 LayoutData& l = CurrentLayout();
1016 LayoutData d = LoadLayoutData(SaveSelection(false));
1017 int q = Max(cursor) + 1;
1018 cursor.Clear();
1019 for(int i = 0; i < d.item.GetCount(); i++) {
1020 LayoutItem& m = d.item[i];
1021 d.item[i].pos = MakeLogPos(m.pos, CtrlRect(m.pos, l.size).Offseted(0, 24), l.size);
1022 cursor.Add(q + i);
1023 }
1024 CurrentLayout().item.InsertPick(q, pick(d.item));
1025 ReloadItems();
1026 }
1027
Matrix()1028 void LayDes::Matrix()
1029 {
1030 if(IsNull(currentlayout) || cursor.GetCount() == 0)
1031 return;
1032 SaveState();
1033 if(matrix.Execute() != IDOK)
1034 return;
1035 LayoutData& l = CurrentLayout();
1036 Rect r = CtrlRect(l.item[cursor[0]].pos, l.size);
1037 for(int i = 1; i < cursor.GetCount(); i++)
1038 r.Union(CtrlRect(l.item[cursor[i]].pos, l.size));
1039 String ls = SaveSelection();
1040 int q = Max(cursor) + 1;
1041 for(int x = 0; x < Nvl((int)~matrix.nx, 1); x++)
1042 for(int y = 0; y < Nvl((int)~matrix.ny, 1); y++)
1043 if(x || y) {
1044 LayoutData d = LoadLayoutData(ls);
1045 for(int i = 0; i < d.item.GetCount(); i++) {
1046 LayoutItem& m = d.item[i];
1047 Rect r = CtrlRect(m.pos, l.size);
1048 r.Offset((r.Width() + Nvl((int)~matrix.dx)) * x,
1049 (r.Height() + Nvl((int)~matrix.dy)) * y);
1050 d.item[i].pos = MakeLogPos(m.pos, r, l.size);
1051 cursor.Add(q + i);
1052 }
1053 int w = d.item.GetCount();
1054 CurrentLayout().item.InsertPick(q, pick(d.item));
1055 q += w;
1056 }
1057 ReloadItems();
1058 }
1059
Paste()1060 void LayDes::Paste()
1061 {
1062 if(IsNull(currentlayout))
1063 return;
1064 SaveState();
1065 try {
1066 LayoutData l = LoadLayoutData(FromUnicode(ReadClipboardUnicodeText(), charset));
1067 int q = item.GetCount();
1068 if(cursor.GetCount())
1069 {
1070 q = 0;
1071 for(int i = 0; i < cursor.GetCount(); i++)
1072 q = max(q, cursor[i] + 1);
1073 }
1074 cursor.Clear();
1075 for(int i = 0; i < l.item.GetCount(); i++)
1076 cursor.Add(i + q);
1077 CurrentLayout().item.InsertPick(q, pick(l.item));
1078 ReloadItems();
1079 }
1080 catch(CParser::Error) {}
1081 }
1082
Align(int type)1083 void LayDes::Align(int type)
1084 {
1085 if(IsNull(currentlayout) || cursor.GetCount() == 0)
1086 return;
1087 SaveState();
1088 LayoutData& l = CurrentLayout();
1089 Rect cr = CtrlRect(l.item[cursor.Top()].pos, l.size);
1090 for(int i = 0; i < cursor.GetCount(); i++) {
1091 LayoutItem& m = l.item[cursor[i]];
1092 Rect r = CtrlRect(m.pos, l.size);
1093 switch(type) {
1094 case A_LEFT:
1095 r.OffsetHorz(cr.left - r.left);
1096 break;
1097 case A_HCENTER:
1098 r.OffsetHorz(cr.left + (cr.Width() - r.Width()) / 2 - r.left);
1099 break;
1100 case A_RIGHT:
1101 r.OffsetHorz(cr.right - r.right);
1102 break;
1103 case A_TOP:
1104 r.OffsetVert(cr.top - r.top);
1105 break;
1106 case A_VCENTER:
1107 r.OffsetVert(cr.top + (cr.Height() - r.Height()) / 2 - r.top);
1108 break;
1109 case A_BOTTOM:
1110 r.OffsetVert(cr.bottom - r.bottom);
1111 break;
1112 case A_SAMEWIDTH:
1113 r.right = r.left + cr.Width();
1114 break;
1115 case A_SAMEHEIGHT:
1116 r.bottom = r.top + cr.Height();
1117 break;
1118 case A_SAMESIZE:
1119 r.SetSize(cr.Size());
1120 break;
1121 case A_HORZCENTER:
1122 r.OffsetHorz((l.size.cx - r.Width()) / 2 - r.left);
1123 break;
1124 case A_VERTCENTER:
1125 r.OffsetVert((l.size.cy - r.Height()) / 2 - r.top);
1126 break;
1127 case A_MINWIDTH:
1128 r.SetSize(m.GetMinSize().cx, r.Height());
1129 break;
1130 case A_MINHEIGHT:
1131 r.SetSize(r.Width(), m.GetMinSize().cy);
1132 break;
1133 case A_LABEL:
1134 if(m.type == "Label") {
1135 Rect rr = r;
1136 int q = cursor[i] - 1;
1137 while(q >= 0) {
1138 if(l.item[q].type != "Label") {
1139 rr = CtrlRect(l.item[q].pos, l.size);
1140 break;
1141 }
1142 q--;
1143 }
1144 q = cursor[i] + 1;
1145 while(q < l.item.GetCount()) {
1146 if(l.item[q].type != "Label") {
1147 rr = CtrlRect(l.item[q].pos, l.size);
1148 break;
1149 }
1150 q++;
1151 }
1152 r.OffsetVert(rr.top + (rr.Height() - r.Height()) / 2 - r.top);
1153 }
1154 break;
1155 }
1156 m.pos = MakeLogPos(m.pos, r, l.size);
1157 // if(i == cursor.GetCount() - 1)
1158 // sb.ScrollInto(r.Offseted(MARGIN, MARGIN));
1159 }
1160 SetStatus();
1161 }
1162
SetSprings(dword s)1163 void LayDes::SetSprings(dword s)
1164 {
1165 if(IsNull(currentlayout))
1166 return;
1167 LayoutData& l = CurrentLayout();
1168 SaveState();
1169 int xa = (int16)LOWORD(s);
1170 int ya = (int16)HIWORD(s);
1171 for(int i = 0; i < cursor.GetCount(); i++) {
1172 Ctrl::LogPos& pos = l.item[cursor[i]].pos;
1173 Rect r = CtrlRect(pos, l.size);
1174 if(xa >= 0)
1175 pos.x = MakeLogc(xa, r.left, r.right, l.size.cx);
1176 if(ya >= 0)
1177 pos.y = MakeLogc(ya, r.top, r.bottom, l.size.cy);
1178 if(xa == AUTOSPRING) {
1179 pos.x = MakeLogc((r.left < l.size.cx / 2 ? LEFT : 0) |
1180 (r.right > l.size.cx / 2 ? RIGHT : 0),
1181 r.left, r.right, l.size.cx);
1182 pos.y = MakeLogc((r.top < l.size.cy / 2 ? TOP : 0) |
1183 (r.bottom > l.size.cy/ 2 ? BOTTOM : 0),
1184 r.top, r.bottom, l.size.cy);
1185 }
1186 }
1187 SetStatus();
1188 }
1189
ShowSelection(bool s)1190 void LayDes::ShowSelection(bool s)
1191 {
1192 if(IsNull(currentlayout) || cursor.GetCount() == 0)
1193 return;
1194 LayoutData& l = CurrentLayout();
1195 for(int i = 0; i < cursor.GetCount(); i++)
1196 l.item[cursor[i]].hide = !s;
1197 SyncItems();
1198 Refresh();
1199 }
1200
MoveUp()1201 void LayDes::MoveUp()
1202 {
1203 SaveState();
1204 if(IsNull(currentlayout) || cursor.GetCount() == 0)
1205 return;
1206 LayoutData& l = CurrentLayout();
1207 Vector<int> sc(cursor, 1);
1208 Sort(sc);
1209 int q = 0;
1210 while(q < sc.GetCount() && sc[q] == q)
1211 q++;
1212 int im = q;
1213 while(q < sc.GetCount()) {
1214 int i = sc[q++];
1215 l.item.Swap(i, i - 1);
1216 }
1217 for(q = 0; q < cursor.GetCount(); q++)
1218 if(cursor[q] >= im)
1219 cursor[q]--;
1220 ReloadItems();
1221 }
1222
MoveDown()1223 void LayDes::MoveDown()
1224 {
1225 SaveState();
1226 if(IsNull(currentlayout) || cursor.GetCount() == 0)
1227 return;
1228 LayoutData& l = CurrentLayout();
1229 Vector<int> sc(cursor, 1);
1230 Sort(sc);
1231 int q = sc.GetCount() - 1;
1232 int u = l.item.GetCount() - 1;
1233 while(q >= 0 && sc[q] == u--)
1234 q--;
1235 int im = q >= 0 ? sc[q] : -1;
1236 while(q >= 0) {
1237 int i = sc[q--];
1238 l.item.Swap(i, i + 1);
1239 }
1240 for(q = 0; q < cursor.GetCount(); q++)
1241 if(cursor[q] <= im)
1242 cursor[q]++;
1243 ReloadItems();
1244 }
1245
DnDInsert(int line,PasteClip & d)1246 void LayDes::DnDInsert(int line, PasteClip& d)
1247 {
1248 if(GetInternalPtr<ArrayCtrl>(d, "layout-item") == &item && item.IsCursor() &&
1249 !IsNull(currentlayout) && cursor.GetCount() && d.Accept()) {
1250 SaveState();
1251 LayoutData& l = CurrentLayout();
1252 Buffer<bool> sel(l.item.GetCount(), false);
1253 int n = l.item.GetCount();
1254 l.item.InsertN(n, n);
1255 for(int i = 0; i < cursor.GetCount(); i++)
1256 sel[cursor[i]] = true;
1257 cursor.Clear();
1258 int j = n;
1259 for(int i = 0; i < line; i++)
1260 if(!sel[i])
1261 l.item.Swap(j++, i);
1262 for(int i = 0; i < n; i++)
1263 if(sel[i]) {
1264 cursor.Add(j - n);
1265 l.item.Swap(j++, i);
1266 }
1267 for(int i = line; i < n; i++)
1268 if(!sel[i])
1269 l.item.Swap(j++, i);
1270 l.item.Remove(0, n);
1271 ReloadItems();
1272 }
1273 }
1274
Drag()1275 void LayDes::Drag()
1276 {
1277 item.DoDragAndDrop(InternalClip(item, "layout-item"), item.GetDragSample(), DND_MOVE);
1278 }
1279
1280
RectLess(const Rect & a,const Rect & b)1281 bool RectLess(const Rect& a, const Rect& b)
1282 {
1283 int d = min(a.bottom, b.bottom) - max(a.top, b.top);
1284 int w = min(a.GetHeight(), b.GetHeight());
1285 return d > w / 2 ? a.left < b.left : a.top < b.top;
1286 }
1287
SortItems()1288 void LayDes::SortItems()
1289 {
1290 SaveState();
1291 if(IsNull(currentlayout) || cursor.GetCount() < 2)
1292 return;
1293 LayoutData& l = CurrentLayout();
1294
1295 Sort(cursor);
1296 int count = cursor.GetCount();
1297
1298 Array<LayoutItem> item;
1299 Vector<Rect> rect;
1300 for(int i = 0; i < count; ++i) {
1301 rect.Add(CtrlRect(l.item[cursor[i]].pos, l.size));
1302 item.Add() = pick(l.item[cursor[i]]);
1303 }
1304 l.item.Remove(cursor);
1305
1306 bool swap = false;
1307 do {
1308 swap = false;
1309 for(int i = 0; i < count - 1; i++)
1310 if(RectLess(rect[i + 1], rect[i])) {
1311 Swap(rect[i], rect[i + 1]);
1312 Swap(item[i], item[i + 1]);
1313 swap = true;
1314 }
1315 }
1316 while(swap);
1317
1318 int ii = cursor[0];
1319 l.item.InsertPick(ii, pick(item));
1320
1321 cursor.Clear();
1322 for(int i = 0; i < count; i++)
1323 cursor.Add(i + ii);
1324
1325 ReloadItems();
1326 }
1327
Flush()1328 void LayDes::Flush()
1329 {
1330 currentlayout = Null;
1331 }
1332
CurrentLayout()1333 LayoutData& LayDes::CurrentLayout()
1334 {
1335 return layout[currentlayout];
1336 }
1337
LayoutCursor()1338 void LayDes::LayoutCursor()
1339 {
1340 Flush();
1341 draghandle = -1;
1342 currentlayout = list.GetKey();
1343 cursor.Clear();
1344 type.Disable();
1345 variable.Disable();
1346 property.Clear();
1347 if(IsNull(currentlayout))
1348 return;
1349 LoadItems();
1350 SyncItems();
1351 SetSb();
1352 if(!search.HasFocus())
1353 SetFocus();
1354 }
1355
PrevLayout()1356 void LayDes::PrevLayout()
1357 {
1358 list.Key(K_UP, 0);
1359 }
1360
NextLayout()1361 void LayDes::NextLayout()
1362 {
1363 list.Key(K_DOWN, 0);
1364 }
1365
SyncLayoutList()1366 void LayDes::SyncLayoutList()
1367 {
1368 int sc = list.GetScroll();
1369 int c = list.GetKey();
1370 list.Clear();
1371 String s = ToUpper((String)~search);
1372 for(int i = 0; i < layout.GetCount(); i++)
1373 if(ToUpper(layout[i].name).Find(s) >= 0)
1374 list.Add(i, layout[i].name);
1375 list.ScrollTo(sc);
1376 if(!IsNull(c))
1377 list.FindSetCursor(c);
1378 LayoutCursor();
1379 }
1380
Search()1381 void LayDes::Search()
1382 {
1383 SyncLayoutList();
1384 if(!list.IsCursor())
1385 list.GoBegin();
1386 }
1387
GoTo(int key)1388 void LayDes::GoTo(int key)
1389 {
1390 if(list.FindSetCursor(key))
1391 return;
1392 search <<= Null;
1393 SyncLayoutList();
1394 list.FindSetCursor(key);
1395 }
1396
AddLayout(bool insert)1397 void LayDes::AddLayout(bool insert)
1398 {
1399 String name;
1400 for(;;) {
1401 if(!EditText(name, "Add new layout", "Layout", CharFilterCid))
1402 return;
1403 CParser p(name);
1404 if(p.IsId())
1405 break;
1406 Exclamation("Invalid name!");
1407 }
1408 int q = list.GetKey();
1409 if(!insert || IsNull(q) || !(q >= 0 && q < layout.GetCount()))
1410 q = layout.GetCount();
1411 layout.Insert(q).name = name;
1412 SyncLayoutList();
1413 GoTo(q);
1414 LayoutCursor();
1415 }
1416
DuplicateLayout()1417 void LayDes::DuplicateLayout()
1418 {
1419 if(IsNull(currentlayout))
1420 return;
1421 LayoutData& c = CurrentLayout();
1422 String name = c.name;
1423 for(;;) {
1424 if(!EditText(name, "Duplicate layout", "Layout", CharFilterCid))
1425 return;
1426 CParser p(name);
1427 if(p.IsId())
1428 break;
1429 Exclamation("Invalid name!");
1430 }
1431 String data = c.Save(0, "\r\n");
1432 CParser p(data);
1433 int next = currentlayout + 1;
1434 LayoutData& d = layout.Insert(next);
1435 d.Read(p);
1436 d.name = name;
1437 SyncLayoutList();
1438 GoTo(next);
1439 LayoutCursor();
1440 }
1441
RenameLayout()1442 void LayDes::RenameLayout()
1443 {
1444 if(IsNull(currentlayout))
1445 return;
1446 String name = layout[currentlayout].name;
1447 if(!EditText(name, "Rename layout", "Layout", CharFilterCid))
1448 return;
1449 int q = list.GetKey();
1450 layout[currentlayout].name = name;
1451 SyncLayoutList();
1452 GoTo(q);
1453 LayoutCursor();
1454 }
1455
RemoveLayout()1456 void LayDes::RemoveLayout()
1457 {
1458 if(IsNull(currentlayout) || !PromptYesNo("Remove [* " + DeQtf(layout[currentlayout].name) + "] ?"))
1459 return;
1460 int q = list.GetKey();
1461 layout.Remove(currentlayout);
1462 SyncLayoutList();
1463 if(!IsNull(q)) {
1464 GoTo(q + 1);
1465 if(!list.IsCursor())
1466 list.GoEnd();
1467 }
1468 LayoutCursor();
1469 }
1470
MoveLayoutUp()1471 void LayDes::MoveLayoutUp()
1472 {
1473 if(!IsNull(search)) {
1474 search <<= Null;
1475 SyncLayoutList();
1476 }
1477 int q = list.GetKey();
1478 if(q > 0) {
1479 layout.Swap(q, q - 1);
1480 SyncLayoutList();
1481 GoTo(q - 1);
1482 }
1483 }
1484
MoveLayoutDown()1485 void LayDes::MoveLayoutDown()
1486 {
1487 if(!IsNull(search)) {
1488 search <<= Null;
1489 SyncLayoutList();
1490 }
1491 int q = list.GetKey();
1492 if(q >= 0 && q < layout.GetCount() - 1) {
1493 layout.Swap(q, q + 1);
1494 SyncLayoutList();
1495 GoTo(q + 1);
1496 }
1497 }
1498
DnDInsertLayout(int line,PasteClip & d)1499 void LayDes::DnDInsertLayout(int line, PasteClip& d)
1500 {
1501 if(GetInternalPtr<ArrayCtrl>(d, "layout") == &list && list.IsCursor() &&
1502 line >= 0 && line <= layout.GetCount() && d.Accept()) {
1503 if(!IsNull(search)) {
1504 search <<= Null;
1505 SyncLayoutList();
1506 }
1507 int c = list.GetKey();
1508 layout.Move(c, line);
1509 if(c <= line)
1510 line--;
1511 SyncLayoutList();
1512 GoTo(line);
1513 }
1514 }
1515
DragLayout()1516 void LayDes::DragLayout()
1517 {
1518 list.DoDragAndDrop(InternalClip(list, "layout"), list.GetDragSample(), DND_MOVE);
1519 }
1520
LayoutMenu(Bar & bar)1521 void LayDes::LayoutMenu(Bar& bar)
1522 {
1523 bool iscursor = list.IsCursor();
1524 bar.Add("Add new layout..", THISBACK1(AddLayout, false));
1525 bar.Add("Insert new layout..", THISBACK1(AddLayout, true));
1526 bar.Add(iscursor, "Duplicate layout..", THISBACK(DuplicateLayout));
1527 bar.Add(iscursor, "Rename layout..", THISBACK(RenameLayout));
1528 bar.Add(iscursor, "Remove layout..", THISBACK(RemoveLayout));
1529 bar.Separator();
1530 int q = list.GetKey();
1531 bar.Add(iscursor && q > 0,
1532 AK_MOVELAYOUTUP, LayImg::MoveUp(), THISBACK(MoveLayoutUp));
1533 bar.Add(iscursor && q < layout.GetCount() - 1,
1534 AK_MOVELAYOUTDOWN, LayImg::MoveDown(), THISBACK(MoveLayoutDown));
1535 }
1536
CurrentItem()1537 LayoutItem& LayDes::CurrentItem()
1538 {
1539 return CurrentLayout().item[cursor.Top()];
1540 }
1541
PropertyChanged()1542 void LayDes::PropertyChanged()
1543 {
1544 if(item.IsCursor())
1545 {
1546 CurrentItem().Invalidate();
1547 int c = item.GetCursor();
1548 LoadItem(c);
1549 SyncItem(c, 2);
1550 }
1551 Refresh();
1552 SetBar();
1553 }
1554
FrameFocus()1555 void LayDes::FrameFocus()
1556 {
1557 if(property.HasFocusDeep()) {
1558 SaveState();
1559 SetStatus();
1560 }
1561 }
1562
ItemClick()1563 void LayDes::ItemClick()
1564 {
1565 if(IsNull(currentlayout))
1566 return;
1567 SaveState();
1568 if(GetShift()) {
1569 if(cursor.GetCount()) {
1570 int i = minmax(item.GetCursor(), 0, cursor.Top());
1571 int m = max(item.GetCursor(), cursor.Top());
1572 cursor.Clear();
1573 while(i <= m)
1574 cursor.Add(i++);
1575 SyncItems();
1576 }
1577 }
1578 else if(item.IsCursor()) {
1579 if(!GetCtrl())
1580 cursor.Clear();
1581 int c = item.GetCursor();
1582 int q = FindIndex(cursor, c);
1583 if(q >= 0)
1584 cursor.Remove(q);
1585 else
1586 cursor.Add(c);
1587 }
1588 SetFocus();
1589 SyncItems();
1590 }
1591
SyncUsc()1592 void LayDes::SyncUsc()
1593 {
1594 type.ClearList();
1595 for(int i = 0; i < LayoutTypes().GetCount(); i++)
1596 if(LayoutTypes()[i].kind != LAYOUT_SUBCTRL)
1597 type.AddList(LayoutTypes().GetKey(i));
1598 if(!IsNull(currentlayout)) {
1599 LayoutData& d = CurrentLayout();
1600 for(int i = 0; i < d.item.GetCount(); i++)
1601 d.item[i].Invalidate();
1602 }
1603 Refresh();
1604 }
1605
TypeEdit()1606 void LayDes::TypeEdit()
1607 {
1608 if(IsNull(currentlayout) || cursor.GetCount() == 0)
1609 return;
1610 LayoutData& l = CurrentLayout();
1611 for(int i = 0; i < cursor.GetCount(); i++) {
1612 LayoutItem& m = l.item[cursor[i]];
1613 m.SetCharset(charset);
1614 String s = m.SaveProperties();
1615 m.Create(~type);
1616 try {
1617 CParser p(s);
1618 m.ReadProperties(p, false);
1619 }
1620 catch(CParser::Error&) {}
1621 item.Set(cursor[i], 0, m.type);
1622 }
1623 SyncProperties(false);
1624 SetStatus();
1625 }
1626
VariableEdit()1627 void LayDes::VariableEdit()
1628 {
1629 if(IsNull(currentlayout) || cursor.GetCount() == 0)
1630 return;
1631 LayoutData& l = CurrentLayout();
1632 LayoutItem& m = l.item[cursor.Top()];
1633 m.variable = ~variable;
1634 if(IsNull(m.variable))
1635 item.Set(cursor.Top(), 1, GetLabel(m));
1636 else
1637 item.Set(cursor.Top(), 1, m.variable);
1638 SyncItem(cursor.Top(), 2);
1639 }
1640
RoundStep(int org,int d,int g)1641 static int RoundStep(int org, int d, int g)
1642 {
1643 return d ? itimesfloor(org + d * g + (d > 0 ? 0 : g - 1), g) - org : 0;
1644 }
1645
DoHotKey(dword key)1646 bool LayDes::DoHotKey(dword key)
1647 {
1648 if(key == K_CTRL_F) {
1649 search.SetFocus();
1650 return true;
1651 }
1652 return false;
1653 }
1654
DoKey(dword key,int count)1655 bool LayDes::DoKey(dword key, int count)
1656 {
1657 SaveState();
1658 Point move(0, 0);
1659 if(!IsNull(currentlayout) && !cursor.IsEmpty()) {
1660 switch(key & ~K_CTRL) {
1661 case K_SHIFT_LEFT: move.x = -1; break;
1662 case K_SHIFT_RIGHT: move.x = +1; break;
1663 case K_SHIFT_UP: move.y = -1; break;
1664 case K_SHIFT_DOWN: move.y = +1; break;
1665 }
1666 if(move.x | move.y) {
1667 Size grid(1, 1);
1668 if(usegrid) {
1669 grid.cx = minmax<int>(~setting.gridx, 1, 32);
1670 grid.cy = minmax<int>(~setting.gridy, 1, 32);
1671 }
1672 LayoutData& l = CurrentLayout();
1673 Rect master = CtrlRect(l.item[cursor.Top()].pos, l.size);
1674 Size shift;
1675 shift.cx = RoundStep(key & K_CTRL ? master.Width() : master.left, move.x, grid.cx);
1676 shift.cy = RoundStep(key & K_CTRL ? master.Height() : master.top, move.y, grid.cy);
1677 for(int i = 0; i < cursor.GetCount(); i++) {
1678 LayoutItem& item = l.item[cursor[i]];
1679 Rect rc = CtrlRect(item.pos, l.size);
1680 rc.right += shift.cx;
1681 rc.bottom += shift.cy;
1682 if(!(key & K_CTRL)) {
1683 rc.left += shift.cx;
1684 rc.top += shift.cy;
1685 }
1686 item.pos = MakeLogPos(item.pos, rc, l.size);
1687 }
1688 SetStatus(false);
1689 return true;
1690 }
1691 }
1692 switch(key) {
1693 case K_PAGEUP:
1694 PrevLayout();
1695 return true;
1696 case K_PAGEDOWN:
1697 NextLayout();
1698 return true;
1699 case K_UP:
1700 case K_DOWN:
1701 {
1702 dword k = (key == K_PAGEUP ? K_UP : key == K_PAGEDOWN ? K_DOWN : key);
1703 Ptr<Ctrl> focus = GetFocusCtrl();
1704 if(!item.IsCursor() && item.GetCount() > 0)
1705 item.SetCursor(k == K_DOWN ? 0 : item.GetCount() - 1);
1706 else
1707 item.Key(k, count);
1708 ItemClick();
1709 if(!!focus)
1710 focus->SetWantFocus();
1711 }
1712 return true;
1713 default:
1714 if(key >= ' ' && key < 65536) {
1715 if(IsNull(currentlayout) || cursor.GetCount() == 0)
1716 return false;
1717 LayoutItem& m = CurrentItem();
1718 for(int i = 0; i < m.property.GetCount(); i++)
1719 if(m.property[i].name == "SetLabel")
1720 return m.property[i].PlaceFocus(key, count);
1721 }
1722 break;
1723 }
1724 return MenuBar::Scan(THISBACK(LayoutMenu), key);
1725 }
1726