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