1 #include "Docking.h"
2 
3 namespace Upp {
4 
5 #define ALIGN_ASSERT(al)	ASSERT(al >= 0 && al < 4)
6 #define FRAME_MOVE_DIV 		5 // Outside fraction of the highlight that the mouse must be in to trigger dockpane reordering
7 #define VERSION				5 // Serialisation version
8 
9 /*
10  * Public interface
11 */
State(int reason)12 void DockWindow::State(int reason)
13 {
14 	if (reason == Ctrl::OPEN) {
15 		if (!hideframe[0].GetParent())
16 			DockLayout();
17 		if (!init) {
18 			DockInit();
19 			init = true;
20 			StopHighlight(false);
21 		}
22 	}
23 	TopWindow::State(reason);
24 }
25 
Key(dword key,int count)26 bool DockWindow::Key(dword key, int count)
27 {
28 	DoHotKeys(key);
29 	return TopWindow::Key(key, count);
30 }
31 
DoHotKeys(dword key)32 void DockWindow::DoHotKeys(dword key)
33 {
34 	if (!HasCloseButtons() || IsLocked()) return;
35 	for (int i = 0; i < dockers.GetCount(); i++) {
36 		if (dockers[i]->IsHotKey(key))
37 			HideRestoreDocker(*dockers[i]);
38 	}
39 }
40 
Dock(int align,DockableCtrl & dc,int pos)41 void DockWindow::Dock(int align, DockableCtrl& dc, int pos)
42 {
43 	ALIGN_ASSERT(align);
44 	Register(dc);
45 	DockContainer(align, *GetReleasedContainer(dc), pos);
46 }
47 
Tabify(DockableCtrl & target,DockableCtrl & dc)48 void DockWindow::Tabify(DockableCtrl& target, DockableCtrl& dc)
49 {
50 	ASSERT(GetContainer(target));
51 	Register(dc);
52 	DockAsTab(*GetContainer(target), dc);
53 }
54 
Float(DockableCtrl & dc,Point p)55 void DockWindow::Float(DockableCtrl& dc, Point p)
56 {
57 	if (dc.GetParent() && p.IsNullInstance())
58 		p = GetScreenRect().TopLeft();
59 	else
60 		Register(dc);
61 	FloatContainer(*GetReleasedContainer(dc), p);
62 }
63 
Float(DockableCtrl & dc,const char * title,Point p)64 void DockWindow::Float(DockableCtrl& dc, const char *title, Point p)
65 {
66 	dc.Title(title);
67 	Float(dc, p);
68 }
69 
AutoHide(DockableCtrl & dc)70 void DockWindow::AutoHide(DockableCtrl& dc)
71 {
72 	if (dc.IsAutoHide()) return;
73 	int align = GetDockAlign(dc);
74 	AutoHide(align == DOCK_NONE ? DOCK_TOP : align,  dc);
75 }
76 
AutoHide(int align,DockableCtrl & dc)77 void DockWindow::AutoHide(int align, DockableCtrl& dc)
78 {
79 	ALIGN_ASSERT(align);
80 	Register(dc);
81 	DockCont *c = GetReleasedContainer(dc);
82 	c->StateAutoHide(*this);
83 	hideframe[align].AddCtrl(*c, dc.GetGroup());
84 }
85 
FindDocker(const Ctrl * dc)86 int DockWindow::FindDocker(const Ctrl *dc)
87 {
88 	for (int i = 0; i < dockers.GetCount(); i++)
89 		if (dc == (Ctrl *) dockers[i])
90 			return i;
91 	return -1;
92 }
93 
Register(DockableCtrl & dc)94 DockableCtrl& DockWindow::Register(DockableCtrl& dc)
95 {
96 	int ix = FindDocker(&dc);
97 	if (ix < 0) {
98 		ix = dockers.GetCount();
99 		dockers.Add(&dc);
100 		dockerpos.Add();
101 	}
102 	return *dockers[ix];
103 }
104 
Deregister(const DockableCtrl & dc)105 void DockWindow::Deregister(const DockableCtrl& dc)
106 {
107 	int ix = FindDocker(&dc);
108 	if (ix >= 0) {
109 		DockableCtrl &dc = *dockers[ix];
110 		dockers.Remove(ix);
111 		Close(dc);
112 		dockerpos.Remove(ix);
113 	}
114 	for (int i = 0; i < ctrls.GetCount(); i++) {
115 		if (&dc == &ctrls[i]) {
116 			ctrls.Remove(i);
117 			break;
118 		}
119 	}
120 }
121 
Close(DockableCtrl & dc)122 void DockWindow::Close(DockableCtrl& dc)
123 {
124 	DockCont *c = GetContainer(dc);
125 	if (c && c->GetCount() > 1) {
126 		SaveDockerPos(dc);
127 		dc.Remove();
128 		c->Layout();
129 		return;
130 	}
131 	if (c) CloseContainer(*c);
132 }
133 
ActivateDockable(Ctrl & c)134 void DockWindow::ActivateDockable(Ctrl& c)
135 {
136 	int ix = FindDocker(&c);
137 	if (ix >= 0)
138 		Activate(*dockers[ix]);
139 }
140 
ActivateDockableChild(Ctrl & c)141 void DockWindow::ActivateDockableChild(Ctrl& c)
142 {
143 	Ctrl *p = c.GetParent();
144 	int ix = -1;
145 	while (p && (ix = FindDocker(p)) < 0)
146 		p = p->GetParent();
147 	if (ix >= 0)
148 		Activate(*dockers[ix]);
149 }
150 
Activate(DockableCtrl & dc)151 void DockWindow::Activate(DockableCtrl& dc)
152 {
153 	if (dc.IsVisible() && dc.IsOpen())
154 		return dc.TimedHighlight(200);
155 	DockCont *c = GetContainer(dc);
156 	if (!c)
157 		c = CreateContainer(dc);
158 	if (c->IsHidden())
159 		RestoreDockerPos(dc);
160 	else if (c->IsAutoHide()) {
161 		for (int i = 0; i <= DOCK_BOTTOM; i++)
162 			if (hideframe[i].HasCtrl(*c))
163 				hideframe[i].ShowAnimate(c);
164 	}
165 	else
166 		c->SetCursor(dc);
167 }
168 
SaveDockerPos(DockableCtrl & dc,PosInfo & pi)169 void DockWindow::SaveDockerPos(DockableCtrl& dc, PosInfo& pi)
170 {
171 	// Get the container
172 	DockCont *cont = GetContainer(dc);
173 	if (!cont) {
174 		// Ctrl must be hidden
175 		pi = PosInfo();
176 		return;
177 	}
178 	// Are we tabbed?
179 	pi.tabcont = (cont->GetCount() > 1) ? cont : NULL;
180 	// Find top DockCont in case of nesting
181 	DockCont *parent = GetContainer(*cont);
182 	while (parent) {
183 		cont = parent;
184 		parent = GetContainer(*cont);
185 	}
186 	// Save state
187 	pi.state = cont->GetDockState();
188 	// determine context info
189 	StringStream s;
190 	switch (pi.state) {
191 		case DockCont::STATE_DOCKED: {
192 			int align = GetDockAlign(*cont);
193 			ALIGN_ASSERT(align);
194 			int ps = dockpane[align].GetChildIndex(cont);
195 			ASSERT(ps >= 0);
196 			Size sz = cont->GetSize();
197 			s % align % ps % sz;
198 			break;
199 		}
200 		case DockCont::STATE_FLOATING:
201 			cont->SerializePlacement(s);
202 			break;
203 		case DockCont::STATE_AUTOHIDE:
204 			for (int i = 0; i < 4; i++) {
205 				int ix = hideframe[i].FindCtrl(*cont);
206 				if (ix >= 0) {
207 					s.Put(ix);
208 					break;
209 				}
210 				ASSERT(i != 3); // No alignment found!
211 			}
212 			break;
213 		default:
214 			return;
215 	}
216 	pi.data = s;
217 }
218 
SetDockerPosInfo(DockableCtrl & dc,const PosInfo & pi)219 void DockWindow::SetDockerPosInfo(DockableCtrl& dc, const PosInfo& pi)
220 {
221 	// Find PosInfo record for the ctrl
222 	int ix = FindDocker(&dc);
223 	if (ix < 0) return;
224 	dockerpos[ix] = pi;
225 }
226 
SaveDockerPos(DockableCtrl & dc)227 void DockWindow::SaveDockerPos(DockableCtrl& dc)
228 {
229 	// Find PosInfo record for the ctrl
230 	int ix = FindDocker(&dc);
231 	if (ix < 0) return;
232 	SaveDockerPos(dc, dockerpos[ix]);
233 }
234 
RestoreDockerPos(DockableCtrl & dc,bool savefirst)235 void DockWindow::RestoreDockerPos(DockableCtrl& dc, bool savefirst)
236 {
237 	// Find PosInfo record for the ctrl
238 	int ix = FindDocker(&dc);
239 	if (ix < 0) return;
240 	PosInfo pi = dockerpos[ix];
241 	if (savefirst)
242 		SaveDockerPos(dc);
243 	if (pi.state == DockCont::STATE_NONE) {
244 		Float(dc);
245 		return;
246 	}
247 
248 	// Read position based on state
249 	StringStream s(pi.data);
250 	switch (pi.state) {
251 		case DockCont::STATE_DOCKED: {
252 			int align, ps;
253 			Size sz;
254 			s % align % ps % sz;
255 			ALIGN_ASSERT(align);
256 			if (pi.tabcont && pi.tabcont->GetDockAlign() == align)
257 				DockAsTab(*~pi.tabcont, dc);
258 			else {
259 				DockCont *cont = GetReleasedContainer(dc);
260 				cont->StateDocked(*this);
261 				Dock0(align, *cont, ps, sz);
262 			}
263 			break;
264 		}
265 		case DockCont::STATE_FLOATING: {
266 			DockCont *cont = GetReleasedContainer(dc);
267 			cont->SerializePlacement(s);
268 			if (pi.tabcont && pi.tabcont->IsFloating())
269 				DockAsTab(*~pi.tabcont, dc);
270 			else
271 				FloatContainer(*cont, Null, false);
272 			break;
273 		}
274 		case DockCont::STATE_AUTOHIDE: {
275 			AutoHide(s.Get(), dc);
276 			break;
277 		}
278 		default:
279 			NEVER();
280 	}
281 }
282 
HideRestoreDocker(DockableCtrl & dc)283 void DockWindow::HideRestoreDocker(DockableCtrl& dc)
284 {
285 	if (dc.IsHidden())
286 		DockWindow::RestoreDockerPos(dc);
287 	else
288 		DockWindow::Close(dc);
289 }
290 
DockGroup(int align,String group,int pos)291 void DockWindow::DockGroup(int align, String group, int pos)
292 {
293 	ALIGN_ASSERT(align);
294 	bool all = group == "All";
295 	for (int i = 0; i < dockers.GetCount(); i++)
296 		if (all || dockers[i]->GetGroup() == group && IsDockAllowed(align, *dockers[i]))
297 			Dock(align, *dockers[i], pos);
298 }
299 
ForceDockGroup(int align,String group,int pos)300 void DockWindow::ForceDockGroup(int align, String group, int pos)
301 {
302 	ALIGN_ASSERT(align);
303 	bool all = group == "All";
304 	for (int i = 0; i < dockers.GetCount(); i++)
305 		if (all || dockers[i]->GetGroup() == group)
306 			Dock(align, *dockers[i], pos);
307 }
308 
FloatGroup(String group)309 void DockWindow::FloatGroup(String group)
310 {
311 	bool all = group == "All";
312 	Point p = GetScreenRect().TopLeft();
313 	Point inc(20, 20);
314 	for (int i = 0; i < dockers.GetCount(); i++)
315 		if (all || dockers[i]->GetGroup() == group) {
316 			p += inc;
317 			Float(*dockers[i], p);
318 		}
319 }
320 
AutoHideGroup(int align,String group)321 void DockWindow::AutoHideGroup(int align, String group)
322 {
323 	ALIGN_ASSERT(align);
324 	bool all = group == "All";
325 	for (int i = 0; i < dockers.GetCount(); i++)
326 		if (all || dockers[i]->GetGroup() == group)
327 			AutoHide(align, *dockers[i]);
328 }
329 
AutoHideGroup(String group)330 void DockWindow::AutoHideGroup(String group)
331 {
332 	bool all = group == "All";
333 	for (int i = 0; i < dockers.GetCount(); i++)
334 		if (all || dockers[i]->GetGroup() == group)
335 			AutoHide(*dockers[i]);
336 }
337 
TabDockGroup(int align,String group,int pos)338 void DockWindow::TabDockGroup(int align, String group, int pos)
339 {
340 	if (DockCont *c = TabifyGroup(group)) {
341 		if (c->IsDockAllowed(align))
342 			DockContainer(align, *c, pos);
343 		else
344 			FloatContainer(*c);
345 	}
346 }
347 
ForceTabDockGroup(int align,String group,int pos)348 void DockWindow::ForceTabDockGroup(int align, String group, int pos)
349 {
350 	if (DockCont *c = TabifyGroup(group))
351 		DockContainer(align, *c, pos);
352 }
353 
TabFloatGroup(String group)354 void DockWindow::TabFloatGroup(String group)
355 {
356 	if (DockCont *c = TabifyGroup(group))
357 		FloatContainer(*c);
358 }
359 
TabifyGroup(String group)360 DockCont * DockWindow::TabifyGroup(String group)
361 {
362 	DockCont *c = NULL;
363 	bool all = group == "All";
364 	for (int i = 0; i < dockers.GetCount(); i++)
365 		if (all || dockers[i]->GetGroup() == group) {
366 			if (c)
367 				DockAsTab(*c, *dockers[i]);
368 			else
369 				c = GetReleasedContainer(*dockers[i]);
370 		}
371 	return c;
372 }
373 
CloseGroup(String group)374 void DockWindow::CloseGroup(String group)
375 {
376 	bool all = group == "All";
377 	for (int i = 0; i < dockers.GetCount(); i++)
378 		if (all || dockers[i]->GetGroup() == group)
379 			Close(*dockers[i]);
380 	for (int i = 0; i < conts.GetCount(); i++)
381 	conts[i].Layout();
382 }
383 
384 /*
385  * Container management
386 */
CreateContainer()387 DockCont * DockWindow::CreateContainer()
388 {
389 	DockCont *dc = &conts.Add();
390 	dc->StateNotDocked(this);
391 	SyncContainer(*dc);
392 	return dc;
393 }
394 
CreateContainer(DockableCtrl & dc)395 DockCont * DockWindow::CreateContainer(DockableCtrl& dc)
396 {
397 	DockCont *c = CreateContainer();
398 	c->Add(dc);
399 	return c;
400 }
401 
DestroyContainer(DockCont & c)402 void DockWindow::DestroyContainer(DockCont& c)
403 {
404 	for (int i = 0; i < conts.GetCount(); i++)
405 		if (&conts[i] == &c)
406 			return conts.Remove(i);
407 }
408 
GetReleasedContainer(DockableCtrl & dc)409 DockCont *DockWindow::GetReleasedContainer(DockableCtrl& dc)
410 {
411 	DockCont *c = GetContainer(dc);
412 	if (c && c->GetCount() > 1) {
413 		SaveDockerPos(dc);
414 		dc.Remove();
415 		c->RefreshLayout();
416 		c = NULL;
417 	}
418 	if (c)
419 		Detach(*c);
420 	else
421 		c = CreateContainer(dc);
422 	return c;
423 }
424 
SyncContainer(DockCont & c)425 void DockWindow::SyncContainer(DockCont& c)
426 {
427 //	c.ToolWindow(childtoolwindows);
428 	c.Grouping(grouping);
429 	c.WindowButtons(menubtn, hidebtn, closebtn);
430 	c.SyncButtons();
431 	c.Lock(IsLocked());
432 	if (!tabalign)
433 		c.SyncTabs(TabBar::BOTTOM, tabtext);
434 	else {
435 		int align;
436 		if ((align = GetDockAlign(c)) == DOCK_NONE && c.IsAutoHide())
437 			for (int i = 0; i < DOCK_BOTTOM; i++)
438 				if (hideframe[i].HasCtrl(c)) {
439 					align = i;
440 					break;
441 				}
442 		align = IsTB(align) ? TabBar::RIGHT : TabBar::BOTTOM;
443 		c.SyncTabs(align, tabtext);
444 	}
445 
446 }
447 
SyncAll()448 void DockWindow::SyncAll()
449 {
450 	for (int i = 0; i < conts.GetCount(); i++)
451 		SyncContainer(conts[i]);
452 }
453 
454 /*
455  * Docking/Undocking helpers
456 */
Undock0(Ctrl & c,bool do_animatehl,int fsz,bool ishighlight)457 void DockWindow::Undock0(Ctrl& c, bool do_animatehl, int fsz, bool ishighlight)
458 {
459 	int al = GetDockAlign(c);
460 	Ctrl *p = c.GetParent();
461 	if (p != &dockpane[al]) {
462 		c.Remove();
463 		if (p) {
464 			p->Layout();
465 			p->RefreshLayout();
466 		}
467 	}
468 	else {
469 		if (dockpane[al].GetFirstChild() == dockpane[al].GetLastChild())
470 			fsz = 0;
471 		dockpane[al].Undock(c, do_animatehl, ishighlight); // TODO: fix nasty hack
472 		if (fsz >= 0)
473 			DoFrameSize(do_animatehl, al, fsz);
474 	}
475 }
476 
DoFrameSize(bool animate,int align,int targetsize)477 void DockWindow::DoFrameSize(bool animate, int align, int targetsize)
478 {
479 	if (!animate || !IsAnimatedFrames()) {
480 		dockframe[align].SetSize(targetsize);
481 		if (targetsize <= 0)
482 			dockframe[align].Hide();
483 	}
484 	else {
485 		FrameAnim& a = frameanim[align];
486 		if (a.inc)
487 			dockframe[align].SetSize(a.target);
488 		a.target = max(targetsize, 0);
489 		a.inc = (targetsize - dockframe[align].GetSize()) / dockpane[0].GetAnimMaxTicks();
490 		if (!a.inc)
491 			a.inc = (targetsize > dockframe[align].GetSize()) ? 1 : -1;
492 		KillSetTimeCallback(-dockpane[0].GetAnimInterval(), THISBACK(FrameAnimateTick), TIMEID_ANIMATE);
493 	}
494 }
495 
FrameAnimateTick()496 void DockWindow::FrameAnimateTick()
497 {
498 	bool done = true;
499 	for (int i = 0; i < 4; i++) {
500 		FrameAnim& a = frameanim[i];
501 		if (a.inc) {
502 			int sz = dockframe[i].GetSize() + a.inc;
503 			if (a.inc > 0 && sz >= a.target || a.inc < 0 && sz <= a.target) {
504 				sz = a.target;
505 				a.inc = 0;
506 				if (sz == 0)
507 					dockframe[i].Hide();
508 			}
509 			else
510 				done = false;
511 			dockframe[i].SetSize(sz);
512 		}
513 	}
514 	if (done)
515 		KillTimeCallback(TIMEID_ANIMATE);
516 }
Undock(DockCont & c)517 void DockWindow::Undock(DockCont& c)
518 {
519 	if (!c.IsFloating() && !c.IsHidden()) {
520 		if (c.IsAutoHide()) {
521 			for (int i = 0; i < 4; i++) {
522 				int ix = hideframe[i].FindCtrl(c);
523 				if (ix >= 0) {
524 					hideframe[i].RemoveCtrl(ix);
525 					hideframe[i].RefreshFrame();
526 					break;
527 				}
528 			}
529 		}
530 		else {
531 			int dock = GetDockAlign(c);
532 			if (dock >= 0 && dock < 4) {
533 				Ctrl *p = &c;
534 				while (p && p->GetParent() != &dockpane[dock]) p = p->GetParent();
535 				ASSERT(p);
536 				bool v = !IsTB(dock);
537 				c.SyncUserSize(v, !v);
538 			}
539 			Undock0(c);
540 		}
541 		c.StateNotDocked();
542 	}
543 }
544 
Unfloat(DockCont & c)545 void DockWindow::Unfloat(DockCont& c)
546 {
547 	if (c.IsFloating()) {
548 		c.SyncUserSize(true, true);
549 		if (c.IsOpen() || c.IsPopUp())
550 			c.Close();
551 		c.StateNotDocked();
552 	}
553 }
554 
DockAsTab(DockCont & target,DockableCtrl & dc)555 void DockWindow::DockAsTab(DockCont& target, DockableCtrl& dc)
556 {
557 	DockCont *c = GetContainer(dc);
558 	if (c) {
559 		if (c->GetCount() == 1)
560 			CloseContainer(*c);
561 		else {
562 			Ctrl *c = dc.GetParent();
563 			dc.Remove();
564 			c->Layout();
565 		}
566 	}
567 	target.Add(dc);
568 }
569 
Dock0(int align,Ctrl & c,int pos,bool do_animatehl,bool ishighlight)570 void DockWindow::Dock0(int align, Ctrl& c, int pos, bool do_animatehl, bool ishighlight)
571 {
572 	Dock0(align, c, pos, CtrlBestSize(c, align), do_animatehl, ishighlight);
573 }
574 
Dock0(int align,Ctrl & c,int pos,Size sz,bool do_animatehl,bool ishighlight)575 void DockWindow::Dock0(int align, Ctrl& c, int pos, Size sz, bool do_animatehl, bool ishighlight)
576 {
577 	int fsz = IsTB(align) ? sz.cy : sz.cx;
578 	if (!dockframe[align].IsShown())
579 		dockframe[align].Show();
580 	if (fsz > dockframe[align].GetSize())
581 		DoFrameSize(do_animatehl, align, fsz);
582 	dockpane[align].Dock(c, sz, pos, do_animatehl, ishighlight);
583 }
584 
585 /*
586  * Container docking
587 */
DockContainer(int align,DockCont & c,int pos)588 void DockWindow::DockContainer(int align, DockCont& c, int pos)
589 {
590 	Detach(c);
591 	c.StateDocked(*this);
592 	Dock0(align, c, pos);
593 }
594 
DockContainerAsTab(DockCont & target,DockCont & c,bool do_nested)595 void DockWindow::DockContainerAsTab(DockCont& target, DockCont& c, bool do_nested)
596 {
597 	Detach(c);
598 	if (do_nested) {
599 		c.StateTabbed(*this);
600 		target << c;
601 	}
602 	else {
603 		target.AddFrom(c);
604 		DestroyContainer(c);
605 	}
606 }
607 
FloatContainer(DockCont & c,Point p,bool move)608 void DockWindow::FloatContainer(DockCont& c, Point p, bool move)
609 {
610 	ASSERT(IsOpen());
611 	if (c.IsFloating()) return;
612 	Detach(c);
613 	c.StateFloating(*this);
614 	if (move) {
615 		Size best = CtrlBestSize(c, false);
616 		if (p.IsNullInstance())
617 			p = GetScreenRect().CenterPoint() - best/2;
618 		c.SetClientRect(Rect(p, best));
619 	}
620 	c.Open(this);
621 }
622 
FloatFromTab(DockCont & c,DockCont & tab)623 void DockWindow::FloatFromTab(DockCont& c, DockCont& tab)
624 {
625 	Rect r = c.GetScreenRect();
626 	tab.SetClientRect(r);
627 	tab.StateNotDocked(this);
628 	c.RefreshLayout();
629 	tab.MoveBegin();
630 }
631 
AutoHideContainer(int align,DockCont & c)632 void DockWindow::AutoHideContainer(int align, DockCont& c)
633 {
634 	while (c.GetCount() && !c.IsAutoHide())
635 		AutoHide(align, c.Get(0));
636 }
637 
CloseContainer(DockCont & c)638 void DockWindow::CloseContainer(DockCont& c)
639 {
640 	c.SetAllDockerPos();
641 	c.Clear();
642 	Detach(c);
643 	DestroyContainer(c);
644 }
645 
646 /*
647  * Docking Interface helpers
648 */
GetMouseDockTarget()649 DockCont *DockWindow::GetMouseDockTarget()
650 {
651 	Point mp = GetMousePos();
652 	for (int i = 0; i < 4; i++) {
653 		if (dockpane[i].IsShown()) {
654 			Rect r = dockpane[i].GetScreenRect();
655 			if (r.Contains(mp))
656 				return dynamic_cast<DockCont *>(dockpane[i].ChildFromPoint(mp -= r.TopLeft()));
657 		}
658 	}
659 	return NULL;
660 }
661 
GetDockAlign(const Ctrl & c) const662 int DockWindow::GetDockAlign(const Ctrl& c) const
663 {
664 	Ctrl *p = c.GetParent();
665 	while (p && p->GetParent() != this) p = p->GetParent();
666 	for (int i = 0; i < 4; i++)
667 		if (dockpane[i].GetParent() == p) return i;
668 	return DOCK_NONE;
669 }
670 
GetDockAlign(const Point & p) const671 int DockWindow::GetDockAlign(const Point& p) const
672 {
673 	for (int i = 0; i < 4; i++)
674 		if (dockpane[i].IsVisible() && dockpane[i].GetScreenRect().Contains(p))
675 			return i;
676 	return DOCK_NONE;
677 }
678 
GetAutoHideAlign(const DockCont & c) const679 int DockWindow::GetAutoHideAlign(const DockCont& c) const
680 {
681 	for (int i = 0; i < 4; i++) {
682 		if (hideframe[i].HasCtrl(c))
683 			return i;
684 	}
685 	return DOCK_NONE;
686 }
687 
CtrlBestSize(const Ctrl & c,int align,bool restrict) const688 Size DockWindow::CtrlBestSize(const Ctrl& c, int align, bool restrict) const
689 {
690 	Size mn = c.GetMinSize();
691 	Size mx = c.GetMaxSize();
692 	Size std = c.GetStdSize();
693 	if (restrict) {
694 		if (IsTB(align))
695 			mx.cy = minmax(GetSize().cy/2, mn.cy, mx.cy);
696 		else
697 			mx.cx = minmax(GetSize().cx/2, mn.cx, mx.cx);
698 	}
699 	return minmax(std, mn, mx);
700 }
701 
FindDockTarget(DockCont & dc,int & dock)702 DockCont *DockWindow::FindDockTarget(DockCont& dc, int& dock)
703 {
704 	Point p = GetMousePos();
705 	Rect r = GetScreenView(); // IODO: This is not technically correct if the user has added additional frames.
706 							  // Need to manually FrameAddSize for all frames up tolast dockpane
707 	DockCont *target = NULL;
708 	int align = DOCK_NONE;
709 
710 	dock = DOCK_NONE;
711 
712 	if (r.Contains(p)) {
713 		// Check for mouse near hidden dockpanes
714 		dock = GetPointAlign(p, r, true, true, true);
715 		if (dock != DOCK_NONE && dockpane[dock].IsVisible())
716 			dock = DOCK_NONE;
717 	}
718 	else {
719 		// Check for mouse over a docked container
720 		target = GetMouseDockTarget();
721 		if (target) {
722 			r = target->GetScreenRect();
723 			dock = GetDockAlign(*target);
724 			align = GetPointAlign(p, r, IsTabbing(), IsTB(dock), !IsTB(dock));
725 		}
726 		else
727 			return NULL;
728 	}
729 
730 	if (dock != DOCK_NONE && (!dc.IsDockAllowed(dock)
731 		 || IsPaneAnimating(dock) || IsFrameAnimating(dock))
732 		 || (dock == DOCK_NONE && !target)) {
733 		dock = DOCK_NONE;
734 		return NULL;
735 	}
736 
737 	// Prepare for highlight
738 	if (target) {
739 		GetHighlightCtrl().bounds = GetAlignBounds(align, r, IsTabbing(), IsTB(dock), !IsTB(dock));
740 		if (align == DOCK_NONE)
741 			dock = DOCK_NONE; // Tabbing
742 		// The following code handles the case of an insertion between two docked controls. In this case we must set
743 		//  the highlight bounds to be a union of the bounds from each control. Very ugly.
744 		if (dock != DOCK_NONE) {
745 			Ctrl *c = IsTL(align) ? target->GetPrev() : target->GetNext();
746 			if (c) {
747 				int opal = align > 1 ? align-2 : align+2;
748 				GetHighlightCtrl().bounds.Union(GetAlignBounds(opal, c->GetScreenRect(), IsTabbing()));
749 			}
750 			target = IsTL(align) ? target : dynamic_cast<DockCont*>(target->GetNext());
751 		}
752 
753 	}
754 	else if (dock != DOCK_NONE)
755 		GetHighlightCtrl().bounds = GetAlignBounds(dock, r, true);
756 
757 	return target;
758 }
759 
GetAlignBounds(int al,Rect r,bool center,bool allow_lr,bool allow_tb)760 Rect DockWindow::GetAlignBounds(int al, Rect r, bool center, bool allow_lr, bool allow_tb)
761 {
762 	Size border = r.GetSize()/4;
763 	switch (al) {
764 	case DOCK_LEFT:
765 		r.right = r.left + (center ? border.cx : (GHalf_(r.left + r.right)));
766 		return r;
767 	case DOCK_TOP:
768 		r.bottom = r.top + (center ? border.cy : (GHalf_(r.top + r.bottom)));
769 		return r;
770 	case DOCK_RIGHT:
771 		r.left = r.right - (center ? border.cx : (GHalf_(r.left + r.right)));
772 		return r;
773 	case DOCK_BOTTOM:
774 		r.top = r.bottom - (center ? border.cy : (GHalf_(r.top + r.bottom)));
775 		return r;
776 	}
777 	if (allow_lr)
778 		r.DeflateHorz(border.cx);
779 	if (allow_tb)
780 		r.DeflateVert(border.cy);
781 	return r;
782 }
783 
GetPointAlign(const Point p,Rect r,bool center,bool allow_lr,bool allow_tb)784 int DockWindow::GetPointAlign(const Point p, Rect r, bool center, bool allow_lr, bool allow_tb)
785 {
786 	Size border = r.GetSize();
787 	border.cx = allow_lr ? border.cx/4 : 0;
788 	border.cy = allow_tb ? border.cy/4 : 0;
789 	if (center && r.Deflated(border).Contains(p))
790 		return DOCK_NONE;
791 	int q = GetQuad(p, r);
792 	int al = DOCK_NONE;
793 	if (q == 0 || q == 1)
794 		al = DOCK_TOP;
795 	else if (q == 2 || q == 3)
796 		al = DOCK_RIGHT;
797 	else if (q == 4 || q == 5)
798 		al = DOCK_BOTTOM;
799 	else if (q == 6 || q == 7)
800 		al = DOCK_LEFT;
801 	if (!allow_lr && (al == DOCK_LEFT || al == DOCK_RIGHT))
802 		al = (q == 7 || q == 2) ? DOCK_TOP : DOCK_BOTTOM;
803 	else if (!allow_tb && (al == DOCK_TOP || al == DOCK_BOTTOM))
804 		al = (q == 0 || q == 5) ? DOCK_LEFT : DOCK_RIGHT;
805 	return al;
806 }
807 
GetQuad(Point p,Rect r)808 int DockWindow::GetQuad(Point p, Rect r)
809 /* Finds the quadrant in the rectangle for point p:
810 	-------
811 	|\0|1/|
812 	|7\|/2|
813 	|-----|
814 	|6/|\3|
815 	|/5|4\|
816 	-------
817 */
818 {
819 	Point cp = r.CenterPoint();
820 	p -= cp;
821 	cp -= r.TopLeft();
822 	if (p.x <= 0) { // Left
823 		if (p.y <= 0) // Top-Left
824 			return ((p.y * cp.x)/cp.y > p.x) ? 7 : 0;
825 		else {// Bottom-Left
826 			p.x = -p.x;
827 			return ((p.y * cp.x)/cp.y > p.x) ? 5 : 6;
828 		}
829 	}
830 	else { // Right
831 		if (p.y <= 0) { // Top-Right
832 			p.x = -p.x;
833 			return ((p.y * cp.x)/cp.y > p.x) ? 2 : 1;
834 		}
835 		else // Bottom-Right
836 			return ((p.y * cp.x)/cp.y > p.x) ? 4 : 3;
837 	}
838 }
839 
GetFinalAnimRect(int align,Ctrl & c)840 Rect DockWindow::GetFinalAnimRect(int align, Ctrl& c)
841 {
842 	if (c.GetParent() != &dockpane[align])
843 		// c is docked as a tab
844 		return c.GetScreenRect();
845 
846 	Rect r = dockpane[align].GetFinalAnimRect(c);
847 	r.Offset(dockpane[align].GetScreenRect().TopLeft());
848 	if (IsFrameAnimating(align)) {
849 		switch (align) {
850 		case DOCK_LEFT:
851 			r.right = r.left + frameanim[align].target - 4;
852 			break;
853 		case DOCK_TOP:
854 			r.bottom = r.top + frameanim[align].target - 4;
855 			break;
856 		case DOCK_RIGHT:
857 			r.left = r.right - frameanim[align].target - 4;
858 			break;
859 		case DOCK_BOTTOM:
860 			r.top = r.bottom - frameanim[align].target - 4;
861 			break;
862 		}
863 	}
864 	return r;
865 }
866 
867 // HighlightCtrl
Paint(Draw & w)868 void DockWindow::HighlightCtrl::Paint(Draw& w)
869 {
870 	if (buffer.IsEmpty())
871 		ChPaint(w, GetSize(), *highlight);
872 	else
873 		w.DrawImage(0, 0, buffer);
874 }
875 
SetHighlight(const Value & hl,bool _isnested,bool _cannest,Image bg)876 void DockWindow::HighlightCtrl::SetHighlight(const Value& hl, bool _isnested, bool _cannest, Image bg)
877 {
878 	highlight = &hl;
879 	img = bg;
880 	buffer.Clear();
881 	cannest = _cannest;
882 	isnested = cannest && _isnested;
883 	CreateBuffer();
884 }
885 
SetNested(bool _isnested)886 void DockWindow::HighlightCtrl::SetNested(bool _isnested)
887 {
888 	bool nest = cannest && _isnested;
889 	if (nest != isnested) {
890 		isnested = nest;
891 		CreateBuffer();
892 	}
893 }
894 
CreateBuffer()895 void DockWindow::HighlightCtrl::CreateBuffer()
896 {
897 	if (!img.IsEmpty()) {
898 		Size sz = img.GetSize();
899 		ImageDraw w(sz);
900 		w.DrawImage(0, 0, img);
901 		if (isnested) {
902 			Rect r = sz;
903 			Rect t = r;
904 			const TabCtrl::Style& s = TabBar::StyleDefault();
905 			t.bottom -= s.tabheight + s.sel.top + 1;	// Nasty bodge! See TabBar::GetStyleHeight
906 			r.top = t.bottom-1;
907 			r.right = max(min(150, r.GetWidth()/3), 20);
908 			r.left += 10;
909 			ChPaint(w, r, *highlight);
910 			ChPaint(w, t, *highlight);
911 		}
912 		else
913 			ChPaint(w, sz, *highlight);
914 		buffer = w;
915 		Refresh();
916 	}
917 }
918 
919 // Drag and Drop interface
StartHighlight(DockCont * dc)920 void DockWindow::StartHighlight(DockCont *dc)
921 {
922 	int align = DOCK_NONE;
923 	DockCont *target = FindDockTarget(*dc, align);
924 	if (align != DOCK_NONE || target) {
925 		dc->SyncUserSize(true, true);
926 		Highlight(align, *dc, target);
927 	}
928 	else
929 		StopHighlight(IsAnimatedHighlight());
930 }
931 
Highlight(int align,DockCont & cont,DockCont * target)932 void DockWindow::Highlight(int align, DockCont& cont, DockCont *target)
933 {
934 	HighlightCtrl& hl = GetHighlightCtrl();
935 	DockableCtrl& dc = cont.GetCurrent();
936 	ASSERT(&hl != (Ctrl *)&cont);
937 
938 	hl.SizeHint(cont.GetMinSize(), cont.GetMaxSize(), cont.GetStdSize()).Show();
939 
940 	prehighlightframepos = -1;
941 	if (align != DOCK_NONE) {
942 		if (NeedFrameReorder(align))
943 			DoFrameReorder(align);
944 		// Do highlight
945 		hl.SetHighlight(dc.GetStyle().highlight[0], false, 0);
946 		hl.oldframesize = dockframe[align].GetSize();
947 		int pos = target ? dockpane[align].GetChildIndex(target) : -1;
948 		Dock0(align, hl, pos, IsAnimatedHighlight(), true);
949 	}
950 	else if (target && IsTabbing()) {
951 		hl.Title(cont.GetTitle(true)).Icon(dc.GetIcon());
952 		hl.SetHighlight(dc.GetStyle().highlight[1], CheckNesting(), cont.GetCount() > 1,
953 		             target->GetHighlightImage());
954 		if (false && frameorder) {
955 			align = GetDockAlign(*target);
956 			ALIGN_ASSERT(align);
957 			if (NeedFrameReorder(align))
958 				DoFrameReorder(align);
959 		}
960 		DockAsTab(*target, hl);
961 	}
962 }
963 
StopHighlight(bool do_animatehl)964 void DockWindow::StopHighlight(bool do_animatehl)
965 {
966 	HighlightCtrl& hl = GetHighlightCtrl();
967 	if (hl.GetParent()) {
968 		UndoFrameReorder();
969 		Undock0(hl, do_animatehl, hl.oldframesize, true);
970 		hl.ClearHighlight();
971 	}
972 }
973 
DoFrameReorder(int align)974 void DockWindow::DoFrameReorder(int align)
975 {
976 	// Reorder dockpanes
977 	int ix = FindFrame(dockframe[align]);
978 	if (ix != dockframepos) {
979 		prehighlightframepos = ix;
980 		RemoveFrame(ix);
981 		InsertFrame(dockframepos, dockframe[align]);
982 	}
983 }
984 
UndoFrameReorder()985 void DockWindow::UndoFrameReorder()
986 {
987 	if (IsReorderingFrames()) {
988 		CtrlFrame& c = GetFrame(dockframepos);
989 		RemoveFrame(dockframepos);
990 		InsertFrame(prehighlightframepos, c);
991 		prehighlightframepos = -1;
992 	}
993 }
994 
NeedFrameReorder(int align)995 bool DockWindow::NeedFrameReorder(int align)
996 {
997 	// Determine if we need to re-order the dockpanes to set layout precedence
998 	if (!frameorder) return false;
999 	switch (align) {
1000 	case DOCK_LEFT:
1001 		return GetMousePos().x < (GetHighlightCtrl().bounds.left + GetHighlightCtrl().bounds.Width() / FRAME_MOVE_DIV);
1002 	case DOCK_RIGHT:
1003 		return GetMousePos().x > (GetHighlightCtrl().bounds.right - GetHighlightCtrl().bounds.Width() / FRAME_MOVE_DIV);
1004 	case DOCK_TOP:
1005 		return GetMousePos().y < (GetHighlightCtrl().bounds.top + GetHighlightCtrl().bounds.Height() / FRAME_MOVE_DIV);
1006 	case DOCK_BOTTOM:
1007 		return GetMousePos().y > (GetHighlightCtrl().bounds.bottom - GetHighlightCtrl().bounds.Height() / FRAME_MOVE_DIV);
1008 	}
1009 	return false;
1010 }
1011 
ContainerDragStart(DockCont & dc)1012 void DockWindow::ContainerDragStart(DockCont& dc)
1013 {
1014 	if (!dc.IsFloating()) {
1015 		// Reposition if not under the mouse
1016 		Detach(dc);
1017 		dc.StateFloating(*this);
1018 		Rect r = dc.GetScreenView();
1019 		r.SetSize(CtrlBestSize(dc, false));
1020 		dc.SyncUserSize(true, true);
1021 		if (IsAnimatedHighlight() && dc.IsDocked() && dc.GetParent()) {
1022 			Undock0(dc, true);
1023 			dc.StateNotDocked();
1024 		}
1025 		dc.PlaceClientRect(r);
1026 		if(!dc.IsOpen())
1027 			dc.Open(this);
1028 		dc.StartMouseDrag();
1029 	}
1030 }
1031 
ContainerDragMove(DockCont & dc)1032 void DockWindow::ContainerDragMove(DockCont& dc)
1033 {
1034 	HighlightCtrl& hl = GetHighlightCtrl();
1035 	Point p = GetMousePos();
1036 	if (hl.GetParent()) {
1037 		if (!hl.bounds.Contains(p))
1038 			StopHighlight(IsAnimatedHighlight());
1039 		else {
1040 			if (frameorder) {
1041 				int align = GetDockAlign(hl);
1042 				bool needed = (hl.GetParent() == &dockpane[align]) &&  NeedFrameReorder(align);
1043 				if (!needed && IsReorderingFrames())
1044 					UndoFrameReorder();
1045 				else if (needed && !IsReorderingFrames())
1046 					DoFrameReorder(align);
1047 			}
1048 			hl.SetNested(CheckNesting());
1049 		}
1050 		return KillTimeCallback(TIMEID_ANIMATE_DELAY);
1051 	}
1052 	animdelay ?
1053 		KillSetTimeCallback(animdelay, THISBACK1(StartHighlight, &dc), TIMEID_ANIMATE_DELAY) :
1054 		StartHighlight(&dc);
1055 }
1056 
ContainerDragEnd(DockCont & dc)1057 void DockWindow::ContainerDragEnd(DockCont& dc)
1058 {
1059 	KillTimeCallback(TIMEID_ANIMATE_DELAY);
1060 	if (!dc.IsFloating()) return;
1061 	HighlightCtrl& hl = GetHighlightCtrl();
1062 
1063 	Ctrl *p = hl.GetParent();
1064 	int align = DOCK_NONE;
1065 
1066 	if (p)
1067 		for (int i = 0; i < 4; i++)
1068 			if (p == &dockpane[i]) align = i;
1069 
1070 	if (animatewnd && (p || align != DOCK_NONE))
1071 		dc.Animate(GetFinalAnimRect(align, hl), dockpane[0].GetAnimMaxTicks(), 5);
1072 
1073 	if (align != DOCK_NONE) {
1074 		Unfloat(dc);
1075 		dc.StateDocked(*this);
1076 		dockpane[align].Swap(hl, dc);
1077 		dc.SetFocus();
1078 	}
1079 	else if (DockCont *target = dynamic_cast<DockCont *>(p)) {
1080 		StopHighlight(false);
1081 		DockContainerAsTab(*target, dc, CheckNesting() && dc.GetCount() > 1);
1082 	}
1083 	else
1084 		StopHighlight(false);
1085 }
1086 
1087 /*
1088  * Misc
1089 */
LockLayout(bool lock)1090 void DockWindow::LockLayout(bool lock)
1091 {
1092 	locked = lock;
1093 	SyncAll();
1094 }
1095 
TabAutoAlign(bool al)1096 DockWindow& DockWindow::TabAutoAlign(bool al)
1097 {
1098 	tabalign = al;
1099 	SyncAll();
1100 	return *this;
1101 }
1102 
TabShowText(bool text)1103 DockWindow& DockWindow::TabShowText(bool text)
1104 {
1105 	tabtext = text;
1106 	SyncAll();
1107 	return *this;
1108 }
1109 
SetFrameSize(int align,int size)1110 void DockWindow::SetFrameSize(int align, int size)
1111 {
1112 	ALIGN_ASSERT(align);
1113 	dockframe[align].SetSize(size);
1114 }
1115 
Animate(bool highlight,bool frame,bool window,int ticks,int interval)1116 DockWindow&  DockWindow::Animate(bool highlight, bool frame, bool window, int ticks, int interval)
1117 {
1118 	animatehl = highlight;
1119 	animatewnd = window;
1120 	animatefrm = frame;
1121 	ticks = max(ticks, 1);
1122 	interval = max(interval, 1);
1123 	for (int i = 0; i < 4; i++)
1124 		dockpane[i].SetAnimateRate(ticks, interval);
1125 	return *this;
1126 }
1127 
Grouping(bool _grouping)1128 DockWindow&  DockWindow::Grouping(bool _grouping)
1129 {
1130 	if (_grouping != grouping) {
1131 		grouping = _grouping;
1132 		SyncAll();
1133 	}
1134 	return *this;
1135 }
1136 
SetFrameOrder(int first,int second,int third,int fourth)1137 DockWindow&  DockWindow::SetFrameOrder(int first, int second, int third, int fourth)
1138 {
1139 	if (fourth != DOCK_NONE)
1140 		DoFrameReorder(fourth);
1141 	if (third != DOCK_NONE)
1142 		DoFrameReorder(third);
1143 	if (second != DOCK_NONE)
1144 		DoFrameReorder(second);
1145 	if (first != DOCK_NONE)
1146 		DoFrameReorder(first);
1147 	RefreshLayout();
1148 	return *this;
1149 }
1150 
AllowDockAll()1151 DockWindow&  DockWindow::AllowDockAll()
1152 {
1153 	return AllowDockLeft().AllowDockRight().AllowDockTop().AllowDockBottom();
1154 }
1155 
AllowDockNone()1156 DockWindow&  DockWindow::AllowDockNone()
1157 {
1158 	return AllowDockLeft(false).AllowDockRight(false).AllowDockTop(false).AllowDockBottom(false);
1159 }
1160 
IsDockAllowed(int align,DockableCtrl & dc) const1161 bool DockWindow::IsDockAllowed(int align, DockableCtrl& dc) const
1162 {
1163 	ALIGN_ASSERT(align);
1164 	return dockable[align] && dc.IsDockAllowed(align);
1165 }
1166 
AutoHide(bool v)1167 DockWindow&  DockWindow::AutoHide(bool v)
1168 {
1169 	if (v != autohide) {
1170 		autohide = v;
1171 		SyncAll();
1172 	}
1173 	return *this;
1174 }
1175 
WindowButtons(bool menu,bool hide,bool close)1176 DockWindow&  DockWindow::WindowButtons(bool menu, bool hide, bool close)
1177 {
1178 	if (menu == menubtn && close == closebtn && hide == hidebtn) return *this;;
1179 	menubtn = menu;
1180 	closebtn = close;
1181 	hidebtn = hide;
1182 	SyncAll();
1183  	return *this;
1184 }
1185 
DockLayout(bool tb_precedence)1186 void DockWindow::DockLayout(bool tb_precedence)
1187 {
1188 	if (hideframe[0].GetParent())
1189 		for (int i = 0; i < 4; i++) {
1190 			RemoveFrame(hideframe[i]);
1191 			RemoveFrame(dockframe[i]);
1192 		}
1193 	int i = tb_precedence ? 0 : 1;
1194 	// Top, Bottom, Left, Right
1195 	AddFrame(hideframe[1-i]);
1196 	AddFrame(hideframe[3-i]);
1197 	AddFrame(hideframe[0+i]);
1198 	AddFrame(hideframe[2+i]);
1199 
1200 	dockframepos = GetFrameCount();
1201 	AddFrame(dockframe[1-i]);
1202 	AddFrame(dockframe[3-i]);
1203 	AddFrame(dockframe[0+i]);
1204 	AddFrame(dockframe[2+i]);
1205 }
1206 
Dockable(Ctrl & ctrl,WString title)1207 DockableCtrl&  DockWindow::Dockable(Ctrl& ctrl, WString title)
1208 {
1209 	DockableCtrl& dc = Register(ctrls.Add().SizeHint(ctrl.GetMinSize()));
1210 	if (!IsNull(title)) dc.Title(title);
1211 	dc << ctrl.SizePos();
1212 	return dc;
1213 }
1214 
DockManager()1215 void DockWindow::DockManager()
1216 {
1217 	DockConfigDlg(*this).Run();
1218 }
1219 
DockWindowMenu(Bar & bar)1220 void DockWindow::DockWindowMenu(Bar& bar)
1221 {
1222 	if (IsGrouping())
1223 		menu.GroupListMenu(bar);
1224 	else
1225 		menu.WindowListMenu(bar, t_("All"));
1226 	if (dockers.GetCount())
1227 		bar.Separator();
1228 	if (layouts.GetCount()) {
1229 		bar.Add(t_("Layouts"), callback(&menu, &DockMenu::LayoutListMenu));
1230 		bar.Separator();
1231 	}
1232 	bar.Add(t_("Manage Windows..."), THISBACK(DockManager));
1233 }
1234 
SerializeWindow(Stream & s)1235 void DockWindow::SerializeWindow(Stream& s)
1236 {
1237 	int version = VERSION;
1238 	int v = version;
1239 	s / v;
1240 
1241 	SerializeLayout(s, true);
1242 
1243 	s % tabbing % autohide % animatehl % nestedtabs
1244 	  % grouping % menubtn % closebtn % hidebtn % nesttoggle;
1245 	if (version >= 4)
1246 		s % locked;
1247 	if (version >= 5)
1248 		s % frameorder;
1249 	if (s.IsLoading()) {
1250 		SyncAll();
1251 		init = true;
1252 	}
1253 }
1254 
ClearLayout()1255 void DockWindow::ClearLayout()
1256 {
1257 	// Close everything
1258 	for (int i = 0; i < conts.GetCount(); i++)
1259 		CloseContainer(conts[i]);
1260 	for (int i = 0; i < 4; i++) {
1261 		dockpane[i].Clear();
1262 		hideframe[i].Clear();
1263 		frameanim[i].inc = 0;
1264 	}
1265 	conts.Clear();
1266 }
1267 
SerializeLayout(Stream & s,bool withsavedlayouts)1268 void DockWindow::SerializeLayout(Stream& s, bool withsavedlayouts)
1269 {
1270 	StopHighlight(false);
1271 	int cnt = 0;
1272 	int version = VERSION;
1273 
1274 	s.Magic();
1275 	s % version;
1276 
1277 	// Groups
1278 	ArrayMap<String, Vector<int> > groups;
1279 	if (s.IsStoring())
1280 		for (int i = 0; i < dockers.GetCount(); i++) {
1281 			String g = dockers[i]->GetGroup();
1282 			if (!g.IsEmpty()) {
1283 				int ix = groups.Find(g);
1284 				if (ix < 0) {
1285 					groups.Add(dockers[i]->GetGroup(), Vector<int>());
1286 					ix = groups.GetCount() - 1;
1287 				}
1288 				groups[ix].Add(i);
1289 			}
1290 		}
1291 	s % groups;
1292 	if (s.IsLoading()) {
1293 		ClearLayout();
1294 
1295 		for (int i = 0; i < dockers.GetCount(); i++)
1296 			dockers[i]->SetGroup(Null);
1297 		for (int i = 0; i < groups.GetCount(); i++) {
1298 			Vector<int>& v = groups[i];
1299 			const String& g = groups.GetKey(i);
1300 			for (int j = 0; j < v.GetCount(); j++) {
1301 				int ix = v[j];
1302 				if (ix >= 0 && ix < dockers.GetCount())
1303 					dockers[ix]->SetGroup(g);
1304 			}
1305 		}
1306 	}
1307 
1308 	if (s.IsStoring()) {
1309 		// Write frame order
1310 		for (int i = 0; i < 4; i++) {
1311 			int ps = max(FindFrame(dockframe[i]) - dockframepos, 0);
1312 			s % ps;
1313 		}
1314 		// Write docked
1315 		for (int i = 0; i < 4; i++) {
1316 			DockPane& pane = dockpane[i];
1317 			int fsz = dockframe[i].IsShown() ? dockframe[i].GetSize() : 0;
1318 
1319 			s / fsz % pane;
1320 			DockCont *dc = dynamic_cast<DockCont *>(pane.GetFirstChild());
1321 			for (int j = 0; dc && j < pane.GetCount(); j++) {
1322 				s % *dc;
1323 				dc = dynamic_cast<DockCont *>(dc->GetNext());
1324 			}
1325 		}
1326 		cnt = 0;
1327 		// Count Floating
1328 		for (int i = 0; i < conts.GetCount(); i++)
1329 			if (conts[i].IsFloating()) cnt++;
1330 		// Write Floating
1331 		s / cnt;
1332 		for (int i = 0; i < conts.GetCount(); i++) {
1333 			if (conts[i].IsFloating()) {
1334 				conts[i].Serialize(s);
1335 				conts[i].SerializePlacement(s, false);
1336 			}
1337 		}
1338 		// Write Autohidden
1339 		for (int i = 0; i < 4; i++) {
1340 			cnt = hideframe[i].GetCount();
1341 			s / cnt;
1342 			for (int j = 0; j < hideframe[i].GetCount(); j++) {
1343 				int ix = FindDocker(&hideframe[i].GetCtrl(j)->Get(0));
1344 				if (ix >= 0)
1345 					s / ix;
1346 			}
1347 		}
1348 	}
1349 	else {
1350 		// Read frame order
1351 		if (version >= 5) {
1352 			int ps[4];
1353 			for (int i = 0; i < 4; i++) {
1354 				s % ps[i];
1355 				SetFrame(dockframepos + i, InsetFrame());
1356 			}
1357 			for (int i= 0; i < 4; i++)
1358 				SetFrame(ps[i] + dockframepos, dockframe[i]);
1359 		}
1360 		// Read docked
1361 		for (int i = 0; i < 4; i++) {
1362 			DockPane& pane = dockpane[i];
1363 			dockframe[i].Hide();
1364 			int fsz;
1365 			s / fsz % pane;
1366 
1367 			for (int j = 0; j < pane.GetCount(); j++) {
1368 				DockCont *dc = CreateContainer();
1369 				s % *dc;
1370 				dc->StateDocked(*this);
1371 				pane << *dc;
1372 			}
1373 			if (fsz && pane.GetCount()) {
1374 				dockframe[i].SetSize(fsz);
1375 				dockframe[i].Show();
1376 			}
1377 			else
1378 				dockframe[i].SetSize(0);
1379 		}
1380 		// Read floating
1381 		s / cnt;
1382 		for (int i = 0; i < cnt; i++) {
1383 			DockCont *dc = CreateContainer();
1384 			dc->Serialize(s);
1385 			FloatContainer(*dc);
1386 			dc->SerializePlacement(s, false);
1387 		}
1388 		// Read Autohidden
1389 		for (int i = 0; i < 4; i++) {
1390 			s / cnt;
1391 			for (int j = 0; j < cnt; j++) {
1392 				int ix;
1393 				s / ix;
1394 				if (ix >= 0 && ix < dockers.GetCount())
1395 					AutoHide(i, *dockers[ix]);
1396 			}
1397 		}
1398 		// Clear empty containers
1399 		for (int i = conts.GetCount()-1; i >= 0; i--) {
1400 			if (!conts.GetCount()) CloseContainer(conts[i]);
1401 		}
1402 		RefreshLayout();
1403 	}
1404 	bool haslay = withsavedlayouts;
1405 	s % haslay;
1406 	if (withsavedlayouts && (s.IsStoring() || haslay))
1407 		s % layouts;
1408 	s.Magic();
1409 }
1410 
BackupLayout()1411 void DockWindow::BackupLayout()
1412 {
1413 	StringStream s;
1414 	SerializeLayout(s);
1415 	layoutbackup = s;
1416 }
1417 
RestoreLayout()1418 void DockWindow::RestoreLayout()
1419 {
1420 	if (!layoutbackup.GetCount()) return;
1421 	StringStream s(layoutbackup);
1422 	SerializeLayout(s);
1423 }
1424 
SaveLayout(String name)1425 int DockWindow::SaveLayout(String name)
1426 {
1427 	ASSERT(!name.IsEmpty());
1428 	StringStream s;
1429 	SerializeLayout(s, false);
1430 	int ix = layouts.FindAdd(name, String());
1431 	layouts[ix] = (String)s;
1432 	return ix;
1433 }
1434 
LoadLayout(String name)1435 void DockWindow::LoadLayout(String name)
1436 {
1437 	int ix = layouts.Find(name);
1438 	if (ix >= 0)
1439 		LoadLayout(ix);
1440 }
1441 
LoadLayout(int ix)1442 void DockWindow::LoadLayout(int ix)
1443 {
1444 	ASSERT(ix >= 0 && ix < layouts.GetCount());
1445 	StringStream s((String)layouts[ix]);
1446 	SerializeLayout(s, false);
1447 	RefreshLayout();
1448 }
1449 
EnableFloating(bool enable)1450 void DockWindow::EnableFloating(bool enable)
1451 {
1452 	for (int i = 0; i < conts.GetCount(); i++)
1453 		if (conts[i].IsFloating())
1454 			conts[i].Enable(enable);
1455 }
1456 
DockWindow()1457 DockWindow::DockWindow()
1458 {
1459 	menu.Set(*this);
1460 
1461 #ifdef PLATFORM_WIN32
1462 	childtoolwindows = true;
1463 #endif
1464 
1465 	init= false;
1466 	tabbing = true;
1467 	nestedtabs = false;
1468 	grouping = true;
1469 	menubtn = true;
1470 	nesttoggle = (K_CTRL | K_SHIFT);
1471 	closebtn = true;
1472 	hidebtn = true;
1473 	locked = false;
1474 	tabtext = true;
1475 	tabalign = false;
1476 	autohide = true;
1477 	frameorder = true;
1478 	childtoolwindows = false;
1479 	showlockedhandles = false;
1480 
1481 	for (int i = 0; i < 4; i++) {
1482 		dockframe[i].Set(dockpane[i], 0, i);
1483 		IsTB(i) ? dockpane[i].Horz() : dockpane[i].Vert();
1484 		hideframe[i].SetAlign(i);
1485 		dockframe[i].Hide();
1486 	}
1487 	AllowDockAll().Animate().AnimateDelay(30);
1488 }
1489 
1490 // PopUpDockWindow
1491 #define POPUP_SPACING 0
1492 
ContainerDragStart(DockCont & dc)1493 void PopUpDockWindow::ContainerDragStart(DockCont& dc)
1494 {
1495 	DockWindow::ContainerDragStart(dc);
1496 	last_target = NULL;
1497 	last_popup = NULL;
1498 }
1499 
ContainerDragMove(DockCont & dc)1500 void PopUpDockWindow::ContainerDragMove(DockCont& dc)
1501 {
1502 	int align = DOCK_NONE;
1503 
1504 	// Is the highlight the same as last time? (Quick escape)
1505 	if (last_popup && last_popup->IsPopUp() && last_popup->GetRect().Contains(GetMousePos()))
1506 		return;
1507 
1508 	DockCont *target = GetMouseDockTarget();
1509 	int dock = DOCK_NONE;
1510 	if (target) {
1511 		dock = GetDockAlign(*target);
1512 		if (!dc.IsDockAllowed(dock))
1513 			target = NULL;
1514 	}
1515 	bool target_changed =  (target != last_target)
1516 						&& !GetHighlightCtrl().GetParent()
1517 						&& (!target || !IsPaneAnimating(dock));
1518 
1519 	// Hide show inner popups as necessary
1520 	if (!target && last_target != NULL)
1521 		HidePopUps(true, false);
1522 	else if (target_changed)
1523 		ShowInnerPopUps(dc, target);
1524 	ShowOuterPopUps(dc);
1525 	last_target = target;
1526 
1527 	// Get potential alignment
1528 	align = PopUpHighlight(inner, 5);
1529 	if (align == DOCK_NONE) {
1530 		target = NULL;
1531 		last_target = NULL;
1532 		align = PopUpHighlight(outer, 4);
1533 	}
1534 	else if (align == 4)
1535 		align = DOCK_NONE;
1536 	else if (target) {
1537 		target = IsTL(align) ? target : dynamic_cast<DockCont*>(target->GetNext());
1538 		align = dock;
1539 	}
1540 
1541 	// Do highlight
1542 	if (align != DOCK_NONE || target) {
1543 		if (align == DOCK_NONE) StopHighlight(false);
1544 		dc.SyncUserSize(true, true);
1545 		Highlight(align, dc, target);
1546 	}
1547 	else  {
1548 		PopUpHighlight(hide, 4);
1549 		StopHighlight(IsAnimatedHighlight());
1550 		last_popup = NULL;
1551 	}
1552 }
1553 
ContainerDragEnd(DockCont & dc)1554 void PopUpDockWindow::ContainerDragEnd(DockCont& dc)
1555 {
1556 	int align = DOCK_NONE;
1557 	if (IsAutoHide() && showhide)
1558 		align = PopUpHighlight(hide, 4);
1559 	HidePopUps(true, true);
1560 	if (align != DOCK_NONE)
1561 		AutoHideContainer(align, dc);
1562 	else
1563 		DockWindow::ContainerDragEnd(dc);
1564 	last_target = NULL;
1565 	last_popup = NULL;
1566 }
1567 
PopUpHighlight(PopUpButton * pb,int cnt)1568 int PopUpDockWindow::PopUpHighlight(PopUpButton *pb, int cnt)
1569 {
1570 	int ix = -1;
1571 	Point p = GetMousePos();
1572 	for (int i = 0; i < cnt; i++) {
1573 		if (pb[i].IsPopUp() && pb[i].GetRect().Contains(p)) {
1574 			if (!pb[i].hlight) {
1575 				pb[i].hlight = &(style->highlight);
1576 				pb[i].Refresh();
1577 			}
1578 			ix = i;
1579 			last_popup = &pb[i];
1580 		}
1581 		else if (pb[i].hlight) {
1582 			pb[i].hlight = NULL;
1583 			pb[i].Refresh();
1584 		}
1585 	}
1586 	return ix;
1587 }
1588 
ShowOuterPopUps(DockCont & dc)1589 void PopUpDockWindow::ShowOuterPopUps(DockCont& dc)
1590 {
1591 	Rect wrect = GetScreenRect();
1592 	Point cp = wrect.CenterPoint();
1593 	Size psz(style->outersize, style->outersize);
1594 	Rect prect = Rect(psz);
1595 	psz /= 2;
1596 
1597 	wrect.Deflate(12, 12);
1598 
1599 	if (dc.IsDockAllowed(DOCK_LEFT)) 	ShowPopUp(outer[DOCK_LEFT], prect.Offseted(wrect.left + POPUP_SPACING, cp.y - psz.cy));
1600 	if (dc.IsDockAllowed(DOCK_TOP)) 	ShowPopUp(outer[DOCK_TOP], prect.Offseted(cp.x - psz.cx, wrect.top + POPUP_SPACING));
1601 	if (dc.IsDockAllowed(DOCK_RIGHT)) 	ShowPopUp(outer[DOCK_RIGHT], prect.Offseted(wrect.right - POPUP_SPACING - psz.cx*2, cp.y - psz.cy));
1602 	if (dc.IsDockAllowed(DOCK_BOTTOM)) 	ShowPopUp(outer[DOCK_BOTTOM], prect.Offseted(cp.x - psz.cx, wrect.bottom - POPUP_SPACING - psz.cy*2));
1603 
1604 	if (IsAutoHide() && showhide) {
1605 		ShowPopUp(hide[DOCK_LEFT], prect.Offseted(wrect.left + POPUP_SPACING + style->outersize, cp.y - psz.cy));
1606 		ShowPopUp(hide[DOCK_TOP], prect.Offseted(cp.x - psz.cx, wrect.top + POPUP_SPACING + style->outersize));
1607 		ShowPopUp(hide[DOCK_RIGHT], prect.Offseted(wrect.right - POPUP_SPACING - psz.cx*2 - style->outersize, cp.y - psz.cy));
1608 		ShowPopUp(hide[DOCK_BOTTOM], prect.Offseted(cp.x - psz.cx, wrect.bottom - POPUP_SPACING - psz.cy*2 - style->outersize));
1609 	}
1610 }
1611 
ShowInnerPopUps(DockCont & dc,DockCont * target)1612 void PopUpDockWindow::ShowInnerPopUps(DockCont& dc, DockCont *target)
1613 {
1614 	ASSERT(target);
1615 	Rect wrect = GetScreenRect();
1616 	Size psz(style->innersize, style->innersize);
1617 	Rect prect = Rect(psz);
1618 	Point cp;
1619 	psz /= 2;
1620 
1621 	cp = target->GetScreenRect().CenterPoint();
1622 	int d = psz.cy * 5;
1623 	cp.x = minmax(cp.x, wrect.left + d, wrect.right - d);
1624 	cp.y = minmax(cp.y, wrect.top + d, wrect.bottom - d);
1625 
1626 	int align = GetDockAlign(*target);
1627 	if (IsTB(align)) { // Left/right docking allowed
1628 		ShowPopUp(inner[DOCK_LEFT], prect.Offseted(cp.x - psz.cx*3, cp.y - psz.cy));
1629 		ShowPopUp(inner[DOCK_RIGHT], prect.Offseted(cp.x + psz.cx, cp.y - psz.cy));
1630 	}
1631 	else {
1632 		inner[DOCK_LEFT].Close();
1633 		inner[DOCK_RIGHT].Close();
1634 	}
1635 	if (!IsTB(align)) { // Top/bottom docking allowed
1636 		ShowPopUp(inner[DOCK_TOP], prect.Offseted(cp.x - psz.cx, cp.y - psz.cy*3));
1637 		ShowPopUp(inner[DOCK_BOTTOM], prect.Offseted(cp.x - psz.cx, cp.y + psz.cy));
1638 	}
1639 	else {
1640 		inner[DOCK_TOP].Close();
1641 		inner[DOCK_BOTTOM].Close();
1642 	}
1643 	if (IsTabbing())
1644 		ShowPopUp(inner[4], prect.Offseted(cp.x-psz.cx, cp.y-psz.cy));
1645 }
1646 
ShowPopUp(PopUpButton & pb,const Rect & r)1647 void PopUpDockWindow::ShowPopUp(PopUpButton& pb, const Rect& r)
1648 {
1649 	pb.SetRect(r);
1650 	if (!pb.IsPopUp())
1651 		pb.PopUp(this, false, true, true);
1652 }
1653 
HidePopUps(bool _inner,bool _outer)1654 void PopUpDockWindow::HidePopUps(bool _inner, bool _outer)
1655 {
1656 	if (_inner)
1657 		for (int i = 0; i < 5; i++)
1658 			inner[i].Close();
1659 	if (_outer) {
1660 		for (int i = 0; i < 4; i++)
1661 			outer[i].Close();
1662 		for (int i = 0; i < 4; i++)
1663 			hide[i].Close();
1664 	}
1665 }
1666 
SetStyle(const Style & s)1667 PopUpDockWindow&  PopUpDockWindow::SetStyle(const Style& s)
1668 {
1669 	style = &s;
1670 	for (int i = 0; i < 5; i++)
1671 		inner[i].icon = &s.inner[i];
1672 	for (int i = 0; i < 4; i++)
1673 		outer[i].icon = &s.outer[i];
1674 	for (int i = 0; i < 4; i++)
1675 		hide[i].icon = &s.hide[i];
1676 	return *this;
1677 }
1678 
PopUpDockWindow()1679 PopUpDockWindow::PopUpDockWindow()
1680 : showhide(true)
1681 {
1682 	SetStyle(StyleDefault());
1683 	AnimateDelay(0);
1684 }
1685 
CH_STYLE(PopUpDockWindow,Style,StyleDefault)1686 CH_STYLE(PopUpDockWindow, Style, StyleDefault)
1687 {
1688 	inner[0] = DockingImg::DockLeft();
1689 	inner[1] = DockingImg::DockTop();
1690 	inner[2] = DockingImg::DockRight();
1691 	inner[3] = DockingImg::DockBottom();
1692 	inner[4] = DockingImg::DockTab();
1693 
1694 	outer[0] = DockingImg::DockLeft();
1695 	outer[1] = DockingImg::DockTop();
1696 	outer[2] = DockingImg::DockRight();
1697 	outer[3] = DockingImg::DockBottom();
1698 
1699 	hide[0] = DockingImg::HideLeft();
1700 	hide[1] = DockingImg::HideTop();
1701 	hide[2] = DockingImg::HideRight();
1702 	hide[3] = DockingImg::HideBottom();
1703 
1704 	highlight = DockingImg::DockHL();
1705 
1706 	innersize = 25;
1707 	outersize = 25;
1708 }
1709 
1710 }
1711