mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-16 14:16:09 -06:00
1710 lines
41 KiB
C++
1710 lines
41 KiB
C++
#include "Docking.h"
|
|
|
|
namespace Upp {
|
|
|
|
#define ALIGN_ASSERT(al) ASSERT(al >= 0 && al < 4)
|
|
#define FRAME_MOVE_DIV 5 // Outside fraction of the highlight that the mouse must be in to trigger dockpane reordering
|
|
#define VERSION 5 // Serialisation version
|
|
|
|
/*
|
|
* Public interface
|
|
*/
|
|
void DockWindow::State(int reason)
|
|
{
|
|
if (reason == Ctrl::OPEN) {
|
|
if (!hideframe[0].GetParent())
|
|
DockLayout();
|
|
if (!init) {
|
|
DockInit();
|
|
init = true;
|
|
StopHighlight(false);
|
|
}
|
|
}
|
|
TopWindow::State(reason);
|
|
}
|
|
|
|
bool DockWindow::Key(dword key, int count)
|
|
{
|
|
DoHotKeys(key);
|
|
return TopWindow::Key(key, count);
|
|
}
|
|
|
|
void DockWindow::DoHotKeys(dword key)
|
|
{
|
|
if (!HasCloseButtons() || IsLocked()) return;
|
|
for (int i = 0; i < dockers.GetCount(); i++) {
|
|
if (dockers[i]->IsHotKey(key))
|
|
HideRestoreDocker(*dockers[i]);
|
|
}
|
|
}
|
|
|
|
void DockWindow::Dock(int align, DockableCtrl& dc, int pos)
|
|
{
|
|
ALIGN_ASSERT(align);
|
|
Register(dc);
|
|
DockContainer(align, *GetReleasedContainer(dc), pos);
|
|
}
|
|
|
|
void DockWindow::Tabify(DockableCtrl& target, DockableCtrl& dc)
|
|
{
|
|
ASSERT(GetContainer(target));
|
|
Register(dc);
|
|
DockAsTab(*GetContainer(target), dc);
|
|
}
|
|
|
|
void DockWindow::Float(DockableCtrl& dc, Point p)
|
|
{
|
|
if (dc.GetParent() && p.IsNullInstance())
|
|
p = GetScreenRect().TopLeft();
|
|
else
|
|
Register(dc);
|
|
FloatContainer(*GetReleasedContainer(dc), p);
|
|
}
|
|
|
|
void DockWindow::Float(DockableCtrl& dc, const char *title, Point p)
|
|
{
|
|
dc.Title(title);
|
|
Float(dc, p);
|
|
}
|
|
|
|
void DockWindow::AutoHide(DockableCtrl& dc)
|
|
{
|
|
if (dc.IsAutoHide()) return;
|
|
int align = GetDockAlign(dc);
|
|
AutoHide(align == DOCK_NONE ? DOCK_TOP : align, dc);
|
|
}
|
|
|
|
void DockWindow::AutoHide(int align, DockableCtrl& dc)
|
|
{
|
|
ALIGN_ASSERT(align);
|
|
Register(dc);
|
|
DockCont *c = GetReleasedContainer(dc);
|
|
c->StateAutoHide(*this);
|
|
hideframe[align].AddCtrl(*c, dc.GetGroup());
|
|
}
|
|
|
|
int DockWindow::FindDocker(const Ctrl *dc)
|
|
{
|
|
for (int i = 0; i < dockers.GetCount(); i++)
|
|
if (dc == (Ctrl *) dockers[i])
|
|
return i;
|
|
return -1;
|
|
}
|
|
|
|
DockableCtrl& DockWindow::Register(DockableCtrl& dc)
|
|
{
|
|
int ix = FindDocker(&dc);
|
|
if (ix < 0) {
|
|
ix = dockers.GetCount();
|
|
dockers.Add(&dc);
|
|
dockerpos.Add();
|
|
}
|
|
return *dockers[ix];
|
|
}
|
|
|
|
void DockWindow::Deregister(const DockableCtrl& dc)
|
|
{
|
|
int ix = FindDocker(&dc);
|
|
if (ix >= 0) {
|
|
DockableCtrl &dc = *dockers[ix];
|
|
dockers.Remove(ix);
|
|
Close(dc);
|
|
dockerpos.Remove(ix);
|
|
}
|
|
for (int i = 0; i < ctrls.GetCount(); i++) {
|
|
if (&dc == &ctrls[i]) {
|
|
ctrls.Remove(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void DockWindow::Close(DockableCtrl& dc)
|
|
{
|
|
DockCont *c = GetContainer(dc);
|
|
if (c && c->GetCount() > 1) {
|
|
SaveDockerPos(dc);
|
|
dc.Remove();
|
|
c->Layout();
|
|
return;
|
|
}
|
|
if (c) CloseContainer(*c);
|
|
}
|
|
|
|
void DockWindow::ActivateDockable(Ctrl& c)
|
|
{
|
|
int ix = FindDocker(&c);
|
|
if (ix >= 0)
|
|
Activate(*dockers[ix]);
|
|
}
|
|
|
|
void DockWindow::ActivateDockableChild(Ctrl& c)
|
|
{
|
|
Ctrl *p = c.GetParent();
|
|
int ix = -1;
|
|
while (p && (ix = FindDocker(p)) < 0)
|
|
p = p->GetParent();
|
|
if (ix >= 0)
|
|
Activate(*dockers[ix]);
|
|
}
|
|
|
|
void DockWindow::Activate(DockableCtrl& dc)
|
|
{
|
|
if (dc.IsVisible() && dc.IsOpen())
|
|
return dc.TimedHighlight(200);
|
|
DockCont *c = GetContainer(dc);
|
|
if (!c)
|
|
c = CreateContainer(dc);
|
|
if (c->IsHidden())
|
|
RestoreDockerPos(dc);
|
|
else if (c->IsAutoHide()) {
|
|
for (int i = 0; i <= DOCK_BOTTOM; i++)
|
|
if (hideframe[i].HasCtrl(*c))
|
|
hideframe[i].ShowAnimate(c);
|
|
}
|
|
else
|
|
c->SetCursor(dc);
|
|
}
|
|
|
|
void DockWindow::SaveDockerPos(DockableCtrl& dc, PosInfo& pi)
|
|
{
|
|
// Get the container
|
|
DockCont *cont = GetContainer(dc);
|
|
if (!cont) {
|
|
// Ctrl must be hidden
|
|
pi = PosInfo();
|
|
return;
|
|
}
|
|
// Are we tabbed?
|
|
pi.tabcont = (cont->GetCount() > 1) ? cont : NULL;
|
|
// Find top DockCont in case of nesting
|
|
DockCont *parent = GetContainer(*cont);
|
|
while (parent) {
|
|
cont = parent;
|
|
parent = GetContainer(*cont);
|
|
}
|
|
// Save state
|
|
pi.state = cont->GetDockState();
|
|
// determine context info
|
|
StringStream s;
|
|
switch (pi.state) {
|
|
case DockCont::STATE_DOCKED: {
|
|
int align = GetDockAlign(*cont);
|
|
ALIGN_ASSERT(align);
|
|
int ps = dockpane[align].GetChildIndex(cont);
|
|
ASSERT(ps >= 0);
|
|
Size sz = cont->GetSize();
|
|
s % align % ps % sz;
|
|
break;
|
|
}
|
|
case DockCont::STATE_FLOATING:
|
|
cont->SerializePlacement(s);
|
|
break;
|
|
case DockCont::STATE_AUTOHIDE:
|
|
for (int i = 0; i < 4; i++) {
|
|
int ix = hideframe[i].FindCtrl(*cont);
|
|
if (ix >= 0) {
|
|
s.Put(ix);
|
|
break;
|
|
}
|
|
ASSERT(i != 3); // No alignment found!
|
|
}
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
pi.data = s;
|
|
}
|
|
|
|
void DockWindow::SetDockerPosInfo(DockableCtrl& dc, const PosInfo& pi)
|
|
{
|
|
// Find PosInfo record for the ctrl
|
|
int ix = FindDocker(&dc);
|
|
if (ix < 0) return;
|
|
dockerpos[ix] = pi;
|
|
}
|
|
|
|
void DockWindow::SaveDockerPos(DockableCtrl& dc)
|
|
{
|
|
// Find PosInfo record for the ctrl
|
|
int ix = FindDocker(&dc);
|
|
if (ix < 0) return;
|
|
SaveDockerPos(dc, dockerpos[ix]);
|
|
}
|
|
|
|
void DockWindow::RestoreDockerPos(DockableCtrl& dc, bool savefirst)
|
|
{
|
|
// Find PosInfo record for the ctrl
|
|
int ix = FindDocker(&dc);
|
|
if (ix < 0) return;
|
|
PosInfo pi = dockerpos[ix];
|
|
if (savefirst)
|
|
SaveDockerPos(dc);
|
|
if (pi.state == DockCont::STATE_NONE) {
|
|
Float(dc);
|
|
return;
|
|
}
|
|
|
|
// Read position based on state
|
|
StringStream s(pi.data);
|
|
switch (pi.state) {
|
|
case DockCont::STATE_DOCKED: {
|
|
int align, ps;
|
|
Size sz;
|
|
s % align % ps % sz;
|
|
ALIGN_ASSERT(align);
|
|
if (pi.tabcont && pi.tabcont->GetDockAlign() == align)
|
|
DockAsTab(*~pi.tabcont, dc);
|
|
else {
|
|
DockCont *cont = GetReleasedContainer(dc);
|
|
cont->StateDocked(*this);
|
|
Dock0(align, *cont, ps, sz);
|
|
}
|
|
break;
|
|
}
|
|
case DockCont::STATE_FLOATING: {
|
|
DockCont *cont = GetReleasedContainer(dc);
|
|
cont->SerializePlacement(s);
|
|
if (pi.tabcont && pi.tabcont->IsFloating())
|
|
DockAsTab(*~pi.tabcont, dc);
|
|
else
|
|
FloatContainer(*cont, Null, false);
|
|
break;
|
|
}
|
|
case DockCont::STATE_AUTOHIDE: {
|
|
AutoHide(s.Get(), dc);
|
|
break;
|
|
}
|
|
default:
|
|
NEVER();
|
|
}
|
|
}
|
|
|
|
void DockWindow::HideRestoreDocker(DockableCtrl& dc)
|
|
{
|
|
if (dc.IsHidden())
|
|
DockWindow::RestoreDockerPos(dc);
|
|
else
|
|
DockWindow::Close(dc);
|
|
}
|
|
|
|
void DockWindow::DockGroup(int align, String group, int pos)
|
|
{
|
|
ALIGN_ASSERT(align);
|
|
bool all = group == "All";
|
|
for (int i = 0; i < dockers.GetCount(); i++)
|
|
if (all || dockers[i]->GetGroup() == group && IsDockAllowed(align, *dockers[i]))
|
|
Dock(align, *dockers[i], pos);
|
|
}
|
|
|
|
void DockWindow::ForceDockGroup(int align, String group, int pos)
|
|
{
|
|
ALIGN_ASSERT(align);
|
|
bool all = group == "All";
|
|
for (int i = 0; i < dockers.GetCount(); i++)
|
|
if (all || dockers[i]->GetGroup() == group)
|
|
Dock(align, *dockers[i], pos);
|
|
}
|
|
|
|
void DockWindow::FloatGroup(String group)
|
|
{
|
|
bool all = group == "All";
|
|
Point p = GetScreenRect().TopLeft();
|
|
Point inc(20, 20);
|
|
for (int i = 0; i < dockers.GetCount(); i++)
|
|
if (all || dockers[i]->GetGroup() == group) {
|
|
p += inc;
|
|
Float(*dockers[i], p);
|
|
}
|
|
}
|
|
|
|
void DockWindow::AutoHideGroup(int align, String group)
|
|
{
|
|
ALIGN_ASSERT(align);
|
|
bool all = group == "All";
|
|
for (int i = 0; i < dockers.GetCount(); i++)
|
|
if (all || dockers[i]->GetGroup() == group)
|
|
AutoHide(align, *dockers[i]);
|
|
}
|
|
|
|
void DockWindow::AutoHideGroup(String group)
|
|
{
|
|
bool all = group == "All";
|
|
for (int i = 0; i < dockers.GetCount(); i++)
|
|
if (all || dockers[i]->GetGroup() == group)
|
|
AutoHide(*dockers[i]);
|
|
}
|
|
|
|
void DockWindow::TabDockGroup(int align, String group, int pos)
|
|
{
|
|
if (DockCont *c = TabifyGroup(group)) {
|
|
if (c->IsDockAllowed(align))
|
|
DockContainer(align, *c, pos);
|
|
else
|
|
FloatContainer(*c);
|
|
}
|
|
}
|
|
|
|
void DockWindow::ForceTabDockGroup(int align, String group, int pos)
|
|
{
|
|
if (DockCont *c = TabifyGroup(group))
|
|
DockContainer(align, *c, pos);
|
|
}
|
|
|
|
void DockWindow::TabFloatGroup(String group)
|
|
{
|
|
if (DockCont *c = TabifyGroup(group))
|
|
FloatContainer(*c);
|
|
}
|
|
|
|
DockCont * DockWindow::TabifyGroup(String group)
|
|
{
|
|
DockCont *c = NULL;
|
|
bool all = group == "All";
|
|
for (int i = 0; i < dockers.GetCount(); i++)
|
|
if (all || dockers[i]->GetGroup() == group) {
|
|
if (c)
|
|
DockAsTab(*c, *dockers[i]);
|
|
else
|
|
c = GetReleasedContainer(*dockers[i]);
|
|
}
|
|
return c;
|
|
}
|
|
|
|
void DockWindow::CloseGroup(String group)
|
|
{
|
|
bool all = group == "All";
|
|
for (int i = 0; i < dockers.GetCount(); i++)
|
|
if (all || dockers[i]->GetGroup() == group)
|
|
Close(*dockers[i]);
|
|
for (int i = 0; i < conts.GetCount(); i++)
|
|
conts[i].Layout();
|
|
}
|
|
|
|
/*
|
|
* Container management
|
|
*/
|
|
DockCont * DockWindow::CreateContainer()
|
|
{
|
|
DockCont *dc = &conts.Add();
|
|
dc->StateNotDocked(this);
|
|
SyncContainer(*dc);
|
|
return dc;
|
|
}
|
|
|
|
DockCont * DockWindow::CreateContainer(DockableCtrl& dc)
|
|
{
|
|
DockCont *c = CreateContainer();
|
|
c->Add(dc);
|
|
return c;
|
|
}
|
|
|
|
void DockWindow::DestroyContainer(DockCont& c)
|
|
{
|
|
for (int i = 0; i < conts.GetCount(); i++)
|
|
if (&conts[i] == &c)
|
|
return conts.Remove(i);
|
|
}
|
|
|
|
DockCont *DockWindow::GetReleasedContainer(DockableCtrl& dc)
|
|
{
|
|
DockCont *c = GetContainer(dc);
|
|
if (c && c->GetCount() > 1) {
|
|
SaveDockerPos(dc);
|
|
dc.Remove();
|
|
c->RefreshLayout();
|
|
c = NULL;
|
|
}
|
|
if (c)
|
|
Detach(*c);
|
|
else
|
|
c = CreateContainer(dc);
|
|
return c;
|
|
}
|
|
|
|
void DockWindow::SyncContainer(DockCont& c)
|
|
{
|
|
// c.ToolWindow(childtoolwindows);
|
|
c.Grouping(grouping);
|
|
c.WindowButtons(menubtn, hidebtn, closebtn);
|
|
c.SyncButtons();
|
|
c.Lock(IsLocked());
|
|
if (!tabalign)
|
|
c.SyncTabs(TabBar::BOTTOM, tabtext);
|
|
else {
|
|
int align;
|
|
if ((align = GetDockAlign(c)) == DOCK_NONE && c.IsAutoHide())
|
|
for (int i = 0; i < DOCK_BOTTOM; i++)
|
|
if (hideframe[i].HasCtrl(c)) {
|
|
align = i;
|
|
break;
|
|
}
|
|
align = IsTB(align) ? TabBar::RIGHT : TabBar::BOTTOM;
|
|
c.SyncTabs(align, tabtext);
|
|
}
|
|
|
|
}
|
|
|
|
void DockWindow::SyncAll()
|
|
{
|
|
for (int i = 0; i < conts.GetCount(); i++)
|
|
SyncContainer(conts[i]);
|
|
}
|
|
|
|
/*
|
|
* Docking/Undocking helpers
|
|
*/
|
|
void DockWindow::Undock0(Ctrl& c, bool do_animatehl, int fsz, bool ishighlight)
|
|
{
|
|
int al = GetDockAlign(c);
|
|
Ctrl *p = c.GetParent();
|
|
if (p != &dockpane[al]) {
|
|
c.Remove();
|
|
if (p) {
|
|
p->Layout();
|
|
p->RefreshLayout();
|
|
}
|
|
}
|
|
else {
|
|
if (dockpane[al].GetFirstChild() == dockpane[al].GetLastChild())
|
|
fsz = 0;
|
|
dockpane[al].Undock(c, do_animatehl, ishighlight); // TODO: fix nasty hack
|
|
if (fsz >= 0)
|
|
DoFrameSize(do_animatehl, al, fsz);
|
|
}
|
|
}
|
|
|
|
void DockWindow::DoFrameSize(bool animate, int align, int targetsize)
|
|
{
|
|
if (!animate || !IsAnimatedFrames()) {
|
|
dockframe[align].SetSize(targetsize);
|
|
if (targetsize <= 0)
|
|
dockframe[align].Hide();
|
|
}
|
|
else {
|
|
FrameAnim& a = frameanim[align];
|
|
if (a.inc)
|
|
dockframe[align].SetSize(a.target);
|
|
a.target = max(targetsize, 0);
|
|
a.inc = (targetsize - dockframe[align].GetSize()) / dockpane[0].GetAnimMaxTicks();
|
|
if (!a.inc)
|
|
a.inc = (targetsize > dockframe[align].GetSize()) ? 1 : -1;
|
|
KillSetTimeCallback(-dockpane[0].GetAnimInterval(), THISBACK(FrameAnimateTick), TIMEID_ANIMATE);
|
|
}
|
|
}
|
|
|
|
void DockWindow::FrameAnimateTick()
|
|
{
|
|
bool done = true;
|
|
for (int i = 0; i < 4; i++) {
|
|
FrameAnim& a = frameanim[i];
|
|
if (a.inc) {
|
|
int sz = dockframe[i].GetSize() + a.inc;
|
|
if (a.inc > 0 && sz >= a.target || a.inc < 0 && sz <= a.target) {
|
|
sz = a.target;
|
|
a.inc = 0;
|
|
if (sz == 0)
|
|
dockframe[i].Hide();
|
|
}
|
|
else
|
|
done = false;
|
|
dockframe[i].SetSize(sz);
|
|
}
|
|
}
|
|
if (done)
|
|
KillTimeCallback(TIMEID_ANIMATE);
|
|
}
|
|
void DockWindow::Undock(DockCont& c)
|
|
{
|
|
if (!c.IsFloating() && !c.IsHidden()) {
|
|
if (c.IsAutoHide()) {
|
|
for (int i = 0; i < 4; i++) {
|
|
int ix = hideframe[i].FindCtrl(c);
|
|
if (ix >= 0) {
|
|
hideframe[i].RemoveCtrl(ix);
|
|
hideframe[i].RefreshFrame();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
int dock = GetDockAlign(c);
|
|
if (dock >= 0 && dock < 4) {
|
|
Ctrl *p = &c;
|
|
while (p && p->GetParent() != &dockpane[dock]) p = p->GetParent();
|
|
ASSERT(p);
|
|
bool v = !IsTB(dock);
|
|
c.SyncUserSize(v, !v);
|
|
}
|
|
Undock0(c);
|
|
}
|
|
c.StateNotDocked();
|
|
}
|
|
}
|
|
|
|
void DockWindow::Unfloat(DockCont& c)
|
|
{
|
|
if (c.IsFloating()) {
|
|
c.SyncUserSize(true, true);
|
|
if (c.IsOpen() || c.IsPopUp())
|
|
c.Close();
|
|
c.StateNotDocked();
|
|
}
|
|
}
|
|
|
|
void DockWindow::DockAsTab(DockCont& target, DockableCtrl& dc)
|
|
{
|
|
DockCont *c = GetContainer(dc);
|
|
if (c) {
|
|
if (c->GetCount() == 1)
|
|
CloseContainer(*c);
|
|
else {
|
|
Ctrl *c = dc.GetParent();
|
|
dc.Remove();
|
|
c->Layout();
|
|
}
|
|
}
|
|
target.Add(dc);
|
|
}
|
|
|
|
void DockWindow::Dock0(int align, Ctrl& c, int pos, bool do_animatehl, bool ishighlight)
|
|
{
|
|
Dock0(align, c, pos, CtrlBestSize(c, align), do_animatehl, ishighlight);
|
|
}
|
|
|
|
void DockWindow::Dock0(int align, Ctrl& c, int pos, Size sz, bool do_animatehl, bool ishighlight)
|
|
{
|
|
int fsz = IsTB(align) ? sz.cy : sz.cx;
|
|
if (!dockframe[align].IsShown())
|
|
dockframe[align].Show();
|
|
if (fsz > dockframe[align].GetSize())
|
|
DoFrameSize(do_animatehl, align, fsz);
|
|
dockpane[align].Dock(c, sz, pos, do_animatehl, ishighlight);
|
|
}
|
|
|
|
/*
|
|
* Container docking
|
|
*/
|
|
void DockWindow::DockContainer(int align, DockCont& c, int pos)
|
|
{
|
|
Detach(c);
|
|
c.StateDocked(*this);
|
|
Dock0(align, c, pos);
|
|
}
|
|
|
|
void DockWindow::DockContainerAsTab(DockCont& target, DockCont& c, bool do_nested)
|
|
{
|
|
Detach(c);
|
|
if (do_nested) {
|
|
c.StateTabbed(*this);
|
|
target << c;
|
|
}
|
|
else {
|
|
target.AddFrom(c);
|
|
DestroyContainer(c);
|
|
}
|
|
}
|
|
|
|
void DockWindow::FloatContainer(DockCont& c, Point p, bool move)
|
|
{
|
|
ASSERT(IsOpen());
|
|
if (c.IsFloating()) return;
|
|
Detach(c);
|
|
c.StateFloating(*this);
|
|
if (move) {
|
|
Size best = CtrlBestSize(c, false);
|
|
if (p.IsNullInstance())
|
|
p = GetScreenRect().CenterPoint() - best/2;
|
|
c.SetClientRect(Rect(p, best));
|
|
}
|
|
c.Open(this);
|
|
}
|
|
|
|
void DockWindow::FloatFromTab(DockCont& c, DockCont& tab)
|
|
{
|
|
Rect r = c.GetScreenRect();
|
|
tab.SetClientRect(r);
|
|
tab.StateNotDocked(this);
|
|
c.RefreshLayout();
|
|
tab.MoveBegin();
|
|
}
|
|
|
|
void DockWindow::AutoHideContainer(int align, DockCont& c)
|
|
{
|
|
while (c.GetCount() && !c.IsAutoHide())
|
|
AutoHide(align, c.Get(0));
|
|
}
|
|
|
|
void DockWindow::CloseContainer(DockCont& c)
|
|
{
|
|
c.SetAllDockerPos();
|
|
c.Clear();
|
|
Detach(c);
|
|
DestroyContainer(c);
|
|
}
|
|
|
|
/*
|
|
* Docking Interface helpers
|
|
*/
|
|
DockCont *DockWindow::GetMouseDockTarget()
|
|
{
|
|
Point mp = GetMousePos();
|
|
for (int i = 0; i < 4; i++) {
|
|
if (dockpane[i].IsShown()) {
|
|
Rect r = dockpane[i].GetScreenRect();
|
|
if (r.Contains(mp))
|
|
return dynamic_cast<DockCont *>(dockpane[i].ChildFromPoint(mp -= r.TopLeft()));
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int DockWindow::GetDockAlign(const Ctrl& c) const
|
|
{
|
|
Ctrl *p = c.GetParent();
|
|
while (p && p->GetParent() != this) p = p->GetParent();
|
|
for (int i = 0; i < 4; i++)
|
|
if (dockpane[i].GetParent() == p) return i;
|
|
return DOCK_NONE;
|
|
}
|
|
|
|
int DockWindow::GetDockAlign(const Point& p) const
|
|
{
|
|
for (int i = 0; i < 4; i++)
|
|
if (dockpane[i].IsVisible() && dockpane[i].GetScreenRect().Contains(p))
|
|
return i;
|
|
return DOCK_NONE;
|
|
}
|
|
|
|
int DockWindow::GetAutoHideAlign(const DockCont& c) const
|
|
{
|
|
for (int i = 0; i < 4; i++) {
|
|
if (hideframe[i].HasCtrl(c))
|
|
return i;
|
|
}
|
|
return DOCK_NONE;
|
|
}
|
|
|
|
Size DockWindow::CtrlBestSize(const Ctrl& c, int align, bool restrict) const
|
|
{
|
|
Size mn = c.GetMinSize();
|
|
Size mx = c.GetMaxSize();
|
|
Size std = c.GetStdSize();
|
|
if (restrict) {
|
|
if (IsTB(align))
|
|
mx.cy = minmax(GetSize().cy/2, mn.cy, mx.cy);
|
|
else
|
|
mx.cx = minmax(GetSize().cx/2, mn.cx, mx.cx);
|
|
}
|
|
return minmax(std, mn, mx);
|
|
}
|
|
|
|
DockCont *DockWindow::FindDockTarget(DockCont& dc, int& dock)
|
|
{
|
|
Point p = GetMousePos();
|
|
Rect r = GetScreenView(); // IODO: This is not technically correct if the user has added additional frames.
|
|
// Need to manually FrameAddSize for all frames up tolast dockpane
|
|
DockCont *target = NULL;
|
|
int align = DOCK_NONE;
|
|
|
|
dock = DOCK_NONE;
|
|
|
|
if (r.Contains(p)) {
|
|
// Check for mouse near hidden dockpanes
|
|
dock = GetPointAlign(p, r, true, true, true);
|
|
if (dock != DOCK_NONE && dockpane[dock].IsVisible())
|
|
dock = DOCK_NONE;
|
|
}
|
|
else {
|
|
// Check for mouse over a docked container
|
|
target = GetMouseDockTarget();
|
|
if (target) {
|
|
r = target->GetScreenRect();
|
|
dock = GetDockAlign(*target);
|
|
align = GetPointAlign(p, r, IsTabbing(), IsTB(dock), !IsTB(dock));
|
|
}
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
if (dock != DOCK_NONE && (!dc.IsDockAllowed(dock)
|
|
|| IsPaneAnimating(dock) || IsFrameAnimating(dock))
|
|
|| (dock == DOCK_NONE && !target)) {
|
|
dock = DOCK_NONE;
|
|
return NULL;
|
|
}
|
|
|
|
// Prepare for highlight
|
|
if (target) {
|
|
GetHighlightCtrl().bounds = GetAlignBounds(align, r, IsTabbing(), IsTB(dock), !IsTB(dock));
|
|
if (align == DOCK_NONE)
|
|
dock = DOCK_NONE; // Tabbing
|
|
// The following code handles the case of an insertion between two docked controls. In this case we must set
|
|
// the highlight bounds to be a union of the bounds from each control. Very ugly.
|
|
if (dock != DOCK_NONE) {
|
|
Ctrl *c = IsTL(align) ? target->GetPrev() : target->GetNext();
|
|
if (c) {
|
|
int opal = align > 1 ? align-2 : align+2;
|
|
GetHighlightCtrl().bounds.Union(GetAlignBounds(opal, c->GetScreenRect(), IsTabbing()));
|
|
}
|
|
target = IsTL(align) ? target : dynamic_cast<DockCont*>(target->GetNext());
|
|
}
|
|
|
|
}
|
|
else if (dock != DOCK_NONE)
|
|
GetHighlightCtrl().bounds = GetAlignBounds(dock, r, true);
|
|
|
|
return target;
|
|
}
|
|
|
|
Rect DockWindow::GetAlignBounds(int al, Rect r, bool center, bool allow_lr, bool allow_tb)
|
|
{
|
|
Size border = r.GetSize()/4;
|
|
switch (al) {
|
|
case DOCK_LEFT:
|
|
r.right = r.left + (center ? border.cx : (GHalf_(r.left + r.right)));
|
|
return r;
|
|
case DOCK_TOP:
|
|
r.bottom = r.top + (center ? border.cy : (GHalf_(r.top + r.bottom)));
|
|
return r;
|
|
case DOCK_RIGHT:
|
|
r.left = r.right - (center ? border.cx : (GHalf_(r.left + r.right)));
|
|
return r;
|
|
case DOCK_BOTTOM:
|
|
r.top = r.bottom - (center ? border.cy : (GHalf_(r.top + r.bottom)));
|
|
return r;
|
|
}
|
|
if (allow_lr)
|
|
r.DeflateHorz(border.cx);
|
|
if (allow_tb)
|
|
r.DeflateVert(border.cy);
|
|
return r;
|
|
}
|
|
|
|
int DockWindow::GetPointAlign(const Point p, Rect r, bool center, bool allow_lr, bool allow_tb)
|
|
{
|
|
Size border = r.GetSize();
|
|
border.cx = allow_lr ? border.cx/4 : 0;
|
|
border.cy = allow_tb ? border.cy/4 : 0;
|
|
if (center && r.Deflated(border).Contains(p))
|
|
return DOCK_NONE;
|
|
int q = GetQuad(p, r);
|
|
int al = DOCK_NONE;
|
|
if (q == 0 || q == 1)
|
|
al = DOCK_TOP;
|
|
else if (q == 2 || q == 3)
|
|
al = DOCK_RIGHT;
|
|
else if (q == 4 || q == 5)
|
|
al = DOCK_BOTTOM;
|
|
else if (q == 6 || q == 7)
|
|
al = DOCK_LEFT;
|
|
if (!allow_lr && (al == DOCK_LEFT || al == DOCK_RIGHT))
|
|
al = (q == 7 || q == 2) ? DOCK_TOP : DOCK_BOTTOM;
|
|
else if (!allow_tb && (al == DOCK_TOP || al == DOCK_BOTTOM))
|
|
al = (q == 0 || q == 5) ? DOCK_LEFT : DOCK_RIGHT;
|
|
return al;
|
|
}
|
|
|
|
int DockWindow::GetQuad(Point p, Rect r)
|
|
/* Finds the quadrant in the rectangle for point p:
|
|
-------
|
|
|\0|1/|
|
|
|7\|/2|
|
|
|-----|
|
|
|6/|\3|
|
|
|/5|4\|
|
|
-------
|
|
*/
|
|
{
|
|
Point cp = r.CenterPoint();
|
|
p -= cp;
|
|
cp -= r.TopLeft();
|
|
if (p.x <= 0) { // Left
|
|
if (p.y <= 0) // Top-Left
|
|
return ((p.y * cp.x)/cp.y > p.x) ? 7 : 0;
|
|
else {// Bottom-Left
|
|
p.x = -p.x;
|
|
return ((p.y * cp.x)/cp.y > p.x) ? 5 : 6;
|
|
}
|
|
}
|
|
else { // Right
|
|
if (p.y <= 0) { // Top-Right
|
|
p.x = -p.x;
|
|
return ((p.y * cp.x)/cp.y > p.x) ? 2 : 1;
|
|
}
|
|
else // Bottom-Right
|
|
return ((p.y * cp.x)/cp.y > p.x) ? 4 : 3;
|
|
}
|
|
}
|
|
|
|
Rect DockWindow::GetFinalAnimRect(int align, Ctrl& c)
|
|
{
|
|
if (c.GetParent() != &dockpane[align])
|
|
// c is docked as a tab
|
|
return c.GetScreenRect();
|
|
|
|
Rect r = dockpane[align].GetFinalAnimRect(c);
|
|
r.Offset(dockpane[align].GetScreenRect().TopLeft());
|
|
if (IsFrameAnimating(align)) {
|
|
switch (align) {
|
|
case DOCK_LEFT:
|
|
r.right = r.left + frameanim[align].target - 4;
|
|
break;
|
|
case DOCK_TOP:
|
|
r.bottom = r.top + frameanim[align].target - 4;
|
|
break;
|
|
case DOCK_RIGHT:
|
|
r.left = r.right - frameanim[align].target - 4;
|
|
break;
|
|
case DOCK_BOTTOM:
|
|
r.top = r.bottom - frameanim[align].target - 4;
|
|
break;
|
|
}
|
|
}
|
|
return r;
|
|
}
|
|
|
|
// HighlightCtrl
|
|
void DockWindow::HighlightCtrl::Paint(Draw& w)
|
|
{
|
|
if (buffer.IsEmpty())
|
|
ChPaint(w, GetSize(), *highlight);
|
|
else
|
|
w.DrawImage(0, 0, buffer);
|
|
}
|
|
|
|
void DockWindow::HighlightCtrl::SetHighlight(const Value& hl, bool _isnested, bool _cannest, Image bg)
|
|
{
|
|
highlight = &hl;
|
|
img = bg;
|
|
buffer.Clear();
|
|
cannest = _cannest;
|
|
isnested = cannest && _isnested;
|
|
CreateBuffer();
|
|
}
|
|
|
|
void DockWindow::HighlightCtrl::SetNested(bool _isnested)
|
|
{
|
|
bool nest = cannest && _isnested;
|
|
if (nest != isnested) {
|
|
isnested = nest;
|
|
CreateBuffer();
|
|
}
|
|
}
|
|
|
|
void DockWindow::HighlightCtrl::CreateBuffer()
|
|
{
|
|
if (!img.IsEmpty()) {
|
|
Size sz = img.GetSize();
|
|
ImageDraw w(sz);
|
|
w.DrawImage(0, 0, img);
|
|
if (isnested) {
|
|
Rect r = sz;
|
|
Rect t = r;
|
|
const TabCtrl::Style& s = TabBar::StyleDefault();
|
|
t.bottom -= s.tabheight + s.sel.top + 1; // Nasty bodge! See TabBar::GetStyleHeight
|
|
r.top = t.bottom-1;
|
|
r.right = max(min(150, r.GetWidth()/3), 20);
|
|
r.left += 10;
|
|
ChPaint(w, r, *highlight);
|
|
ChPaint(w, t, *highlight);
|
|
}
|
|
else
|
|
ChPaint(w, sz, *highlight);
|
|
buffer = w;
|
|
Refresh();
|
|
}
|
|
}
|
|
|
|
// Drag and Drop interface
|
|
void DockWindow::StartHighlight(DockCont *dc)
|
|
{
|
|
int align = DOCK_NONE;
|
|
DockCont *target = FindDockTarget(*dc, align);
|
|
if (align != DOCK_NONE || target) {
|
|
dc->SyncUserSize(true, true);
|
|
Highlight(align, *dc, target);
|
|
}
|
|
else
|
|
StopHighlight(IsAnimatedHighlight());
|
|
}
|
|
|
|
void DockWindow::Highlight(int align, DockCont& cont, DockCont *target)
|
|
{
|
|
HighlightCtrl& hl = GetHighlightCtrl();
|
|
DockableCtrl& dc = cont.GetCurrent();
|
|
ASSERT(&hl != (Ctrl *)&cont);
|
|
|
|
hl.SizeHint(cont.GetMinSize(), cont.GetMaxSize(), cont.GetStdSize()).Show();
|
|
|
|
prehighlightframepos = -1;
|
|
if (align != DOCK_NONE) {
|
|
if (NeedFrameReorder(align))
|
|
DoFrameReorder(align);
|
|
// Do highlight
|
|
hl.SetHighlight(dc.GetStyle().highlight[0], false, 0);
|
|
hl.oldframesize = dockframe[align].GetSize();
|
|
int pos = target ? dockpane[align].GetChildIndex(target) : -1;
|
|
Dock0(align, hl, pos, IsAnimatedHighlight(), true);
|
|
}
|
|
else if (target && IsTabbing()) {
|
|
hl.Title(cont.GetTitle(true)).Icon(dc.GetIcon());
|
|
hl.SetHighlight(dc.GetStyle().highlight[1], CheckNesting(), cont.GetCount() > 1,
|
|
target->GetHighlightImage());
|
|
if (false && frameorder) {
|
|
align = GetDockAlign(*target);
|
|
ALIGN_ASSERT(align);
|
|
if (NeedFrameReorder(align))
|
|
DoFrameReorder(align);
|
|
}
|
|
DockAsTab(*target, hl);
|
|
}
|
|
}
|
|
|
|
void DockWindow::StopHighlight(bool do_animatehl)
|
|
{
|
|
HighlightCtrl& hl = GetHighlightCtrl();
|
|
if (hl.GetParent()) {
|
|
UndoFrameReorder();
|
|
Undock0(hl, do_animatehl, hl.oldframesize, true);
|
|
hl.ClearHighlight();
|
|
}
|
|
}
|
|
|
|
void DockWindow::DoFrameReorder(int align)
|
|
{
|
|
// Reorder dockpanes
|
|
int ix = FindFrame(dockframe[align]);
|
|
if (ix != dockframepos) {
|
|
prehighlightframepos = ix;
|
|
RemoveFrame(ix);
|
|
InsertFrame(dockframepos, dockframe[align]);
|
|
}
|
|
}
|
|
|
|
void DockWindow::UndoFrameReorder()
|
|
{
|
|
if (IsReorderingFrames()) {
|
|
CtrlFrame& c = GetFrame(dockframepos);
|
|
RemoveFrame(dockframepos);
|
|
InsertFrame(prehighlightframepos, c);
|
|
prehighlightframepos = -1;
|
|
}
|
|
}
|
|
|
|
bool DockWindow::NeedFrameReorder(int align)
|
|
{
|
|
// Determine if we need to re-order the dockpanes to set layout precedence
|
|
if (!frameorder) return false;
|
|
switch (align) {
|
|
case DOCK_LEFT:
|
|
return GetMousePos().x < (GetHighlightCtrl().bounds.left + GetHighlightCtrl().bounds.Width() / FRAME_MOVE_DIV);
|
|
case DOCK_RIGHT:
|
|
return GetMousePos().x > (GetHighlightCtrl().bounds.right - GetHighlightCtrl().bounds.Width() / FRAME_MOVE_DIV);
|
|
case DOCK_TOP:
|
|
return GetMousePos().y < (GetHighlightCtrl().bounds.top + GetHighlightCtrl().bounds.Height() / FRAME_MOVE_DIV);
|
|
case DOCK_BOTTOM:
|
|
return GetMousePos().y > (GetHighlightCtrl().bounds.bottom - GetHighlightCtrl().bounds.Height() / FRAME_MOVE_DIV);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void DockWindow::ContainerDragStart(DockCont& dc)
|
|
{
|
|
if (!dc.IsFloating()) {
|
|
// Reposition if not under the mouse
|
|
Detach(dc);
|
|
dc.StateFloating(*this);
|
|
Rect r = dc.GetScreenView();
|
|
r.SetSize(CtrlBestSize(dc, false));
|
|
dc.SyncUserSize(true, true);
|
|
if (IsAnimatedHighlight() && dc.IsDocked() && dc.GetParent()) {
|
|
Undock0(dc, true);
|
|
dc.StateNotDocked();
|
|
}
|
|
dc.PlaceClientRect(r);
|
|
if(!dc.IsOpen())
|
|
dc.Open(this);
|
|
dc.StartMouseDrag();
|
|
}
|
|
}
|
|
|
|
void DockWindow::ContainerDragMove(DockCont& dc)
|
|
{
|
|
HighlightCtrl& hl = GetHighlightCtrl();
|
|
Point p = GetMousePos();
|
|
if (hl.GetParent()) {
|
|
if (!hl.bounds.Contains(p))
|
|
StopHighlight(IsAnimatedHighlight());
|
|
else {
|
|
if (frameorder) {
|
|
int align = GetDockAlign(hl);
|
|
bool needed = (hl.GetParent() == &dockpane[align]) && NeedFrameReorder(align);
|
|
if (!needed && IsReorderingFrames())
|
|
UndoFrameReorder();
|
|
else if (needed && !IsReorderingFrames())
|
|
DoFrameReorder(align);
|
|
}
|
|
hl.SetNested(CheckNesting());
|
|
}
|
|
return KillTimeCallback(TIMEID_ANIMATE_DELAY);
|
|
}
|
|
animdelay ?
|
|
KillSetTimeCallback(animdelay, THISBACK1(StartHighlight, &dc), TIMEID_ANIMATE_DELAY) :
|
|
StartHighlight(&dc);
|
|
}
|
|
|
|
void DockWindow::ContainerDragEnd(DockCont& dc)
|
|
{
|
|
KillTimeCallback(TIMEID_ANIMATE_DELAY);
|
|
if (!dc.IsFloating()) return;
|
|
HighlightCtrl& hl = GetHighlightCtrl();
|
|
|
|
Ctrl *p = hl.GetParent();
|
|
int align = DOCK_NONE;
|
|
|
|
if (p)
|
|
for (int i = 0; i < 4; i++)
|
|
if (p == &dockpane[i]) align = i;
|
|
|
|
if (animatewnd && (p || align != DOCK_NONE))
|
|
dc.Animate(GetFinalAnimRect(align, hl), dockpane[0].GetAnimMaxTicks(), 5);
|
|
|
|
if (align != DOCK_NONE) {
|
|
Unfloat(dc);
|
|
dc.StateDocked(*this);
|
|
dockpane[align].Swap(hl, dc);
|
|
dc.SetFocus();
|
|
}
|
|
else if (DockCont *target = dynamic_cast<DockCont *>(p)) {
|
|
StopHighlight(false);
|
|
DockContainerAsTab(*target, dc, CheckNesting() && dc.GetCount() > 1);
|
|
}
|
|
else
|
|
StopHighlight(false);
|
|
}
|
|
|
|
/*
|
|
* Misc
|
|
*/
|
|
void DockWindow::LockLayout(bool lock)
|
|
{
|
|
locked = lock;
|
|
SyncAll();
|
|
}
|
|
|
|
DockWindow& DockWindow::TabAutoAlign(bool al)
|
|
{
|
|
tabalign = al;
|
|
SyncAll();
|
|
return *this;
|
|
}
|
|
|
|
DockWindow& DockWindow::TabShowText(bool text)
|
|
{
|
|
tabtext = text;
|
|
SyncAll();
|
|
return *this;
|
|
}
|
|
|
|
void DockWindow::SetFrameSize(int align, int size)
|
|
{
|
|
ALIGN_ASSERT(align);
|
|
dockframe[align].SetSize(size);
|
|
}
|
|
|
|
DockWindow& DockWindow::Animate(bool highlight, bool frame, bool window, int ticks, int interval)
|
|
{
|
|
animatehl = highlight;
|
|
animatewnd = window;
|
|
animatefrm = frame;
|
|
ticks = max(ticks, 1);
|
|
interval = max(interval, 1);
|
|
for (int i = 0; i < 4; i++)
|
|
dockpane[i].SetAnimateRate(ticks, interval);
|
|
return *this;
|
|
}
|
|
|
|
DockWindow& DockWindow::Grouping(bool _grouping)
|
|
{
|
|
if (_grouping != grouping) {
|
|
grouping = _grouping;
|
|
SyncAll();
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
DockWindow& DockWindow::SetFrameOrder(int first, int second, int third, int fourth)
|
|
{
|
|
if (fourth != DOCK_NONE)
|
|
DoFrameReorder(fourth);
|
|
if (third != DOCK_NONE)
|
|
DoFrameReorder(third);
|
|
if (second != DOCK_NONE)
|
|
DoFrameReorder(second);
|
|
if (first != DOCK_NONE)
|
|
DoFrameReorder(first);
|
|
RefreshLayout();
|
|
return *this;
|
|
}
|
|
|
|
DockWindow& DockWindow::AllowDockAll()
|
|
{
|
|
return AllowDockLeft().AllowDockRight().AllowDockTop().AllowDockBottom();
|
|
}
|
|
|
|
DockWindow& DockWindow::AllowDockNone()
|
|
{
|
|
return AllowDockLeft(false).AllowDockRight(false).AllowDockTop(false).AllowDockBottom(false);
|
|
}
|
|
|
|
bool DockWindow::IsDockAllowed(int align, DockableCtrl& dc) const
|
|
{
|
|
ALIGN_ASSERT(align);
|
|
return dockable[align] && dc.IsDockAllowed(align);
|
|
}
|
|
|
|
DockWindow& DockWindow::AutoHide(bool v)
|
|
{
|
|
if (v != autohide) {
|
|
autohide = v;
|
|
SyncAll();
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
DockWindow& DockWindow::WindowButtons(bool menu, bool hide, bool close)
|
|
{
|
|
if (menu == menubtn && close == closebtn && hide == hidebtn) return *this;;
|
|
menubtn = menu;
|
|
closebtn = close;
|
|
hidebtn = hide;
|
|
SyncAll();
|
|
return *this;
|
|
}
|
|
|
|
void DockWindow::DockLayout(bool tb_precedence)
|
|
{
|
|
if (hideframe[0].GetParent())
|
|
for (int i = 0; i < 4; i++) {
|
|
RemoveFrame(hideframe[i]);
|
|
RemoveFrame(dockframe[i]);
|
|
}
|
|
int i = tb_precedence ? 0 : 1;
|
|
// Top, Bottom, Left, Right
|
|
AddFrame(hideframe[1-i]);
|
|
AddFrame(hideframe[3-i]);
|
|
AddFrame(hideframe[0+i]);
|
|
AddFrame(hideframe[2+i]);
|
|
|
|
dockframepos = GetFrameCount();
|
|
AddFrame(dockframe[1-i]);
|
|
AddFrame(dockframe[3-i]);
|
|
AddFrame(dockframe[0+i]);
|
|
AddFrame(dockframe[2+i]);
|
|
}
|
|
|
|
DockableCtrl& DockWindow::Dockable(Ctrl& ctrl, WString title)
|
|
{
|
|
DockableCtrl& dc = Register(ctrls.Add().SizeHint(ctrl.GetMinSize()));
|
|
if (!IsNull(title)) dc.Title(title);
|
|
dc << ctrl.SizePos();
|
|
return dc;
|
|
}
|
|
|
|
void DockWindow::DockManager()
|
|
{
|
|
DockConfigDlg(*this).Run();
|
|
}
|
|
|
|
void DockWindow::DockWindowMenu(Bar& bar)
|
|
{
|
|
if (IsGrouping())
|
|
menu.GroupListMenu(bar);
|
|
else
|
|
menu.WindowListMenu(bar, t_("All"));
|
|
if (dockers.GetCount())
|
|
bar.Separator();
|
|
if (layouts.GetCount()) {
|
|
bar.Add(t_("Layouts"), callback(&menu, &DockMenu::LayoutListMenu));
|
|
bar.Separator();
|
|
}
|
|
bar.Add(t_("Manage Windows..."), THISBACK(DockManager));
|
|
}
|
|
|
|
void DockWindow::SerializeWindow(Stream& s)
|
|
{
|
|
int version = VERSION;
|
|
int v = version;
|
|
s / v;
|
|
|
|
SerializeLayout(s, true);
|
|
|
|
s % tabbing % autohide % animatehl % nestedtabs
|
|
% grouping % menubtn % closebtn % hidebtn % nesttoggle;
|
|
if (version >= 4)
|
|
s % locked;
|
|
if (version >= 5)
|
|
s % frameorder;
|
|
if (s.IsLoading()) {
|
|
SyncAll();
|
|
init = true;
|
|
}
|
|
}
|
|
|
|
void DockWindow::ClearLayout()
|
|
{
|
|
// Close everything
|
|
for (int i = 0; i < conts.GetCount(); i++)
|
|
CloseContainer(conts[i]);
|
|
for (int i = 0; i < 4; i++) {
|
|
dockpane[i].Clear();
|
|
hideframe[i].Clear();
|
|
frameanim[i].inc = 0;
|
|
}
|
|
conts.Clear();
|
|
}
|
|
|
|
void DockWindow::SerializeLayout(Stream& s, bool withsavedlayouts)
|
|
{
|
|
StopHighlight(false);
|
|
int cnt = 0;
|
|
int version = VERSION;
|
|
|
|
s.Magic();
|
|
s % version;
|
|
|
|
// Groups
|
|
ArrayMap<String, Vector<int> > groups;
|
|
if (s.IsStoring())
|
|
for (int i = 0; i < dockers.GetCount(); i++) {
|
|
String g = dockers[i]->GetGroup();
|
|
if (!g.IsEmpty()) {
|
|
int ix = groups.Find(g);
|
|
if (ix < 0) {
|
|
groups.Add(dockers[i]->GetGroup(), Vector<int>());
|
|
ix = groups.GetCount() - 1;
|
|
}
|
|
groups[ix].Add(i);
|
|
}
|
|
}
|
|
s % groups;
|
|
if (s.IsLoading()) {
|
|
ClearLayout();
|
|
|
|
for (int i = 0; i < dockers.GetCount(); i++)
|
|
dockers[i]->SetGroup(Null);
|
|
for (int i = 0; i < groups.GetCount(); i++) {
|
|
Vector<int>& v = groups[i];
|
|
const String& g = groups.GetKey(i);
|
|
for (int j = 0; j < v.GetCount(); j++) {
|
|
int ix = v[j];
|
|
if (ix >= 0 && ix < dockers.GetCount())
|
|
dockers[ix]->SetGroup(g);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (s.IsStoring()) {
|
|
// Write frame order
|
|
for (int i = 0; i < 4; i++) {
|
|
int ps = max(FindFrame(dockframe[i]) - dockframepos, 0);
|
|
s % ps;
|
|
}
|
|
// Write docked
|
|
for (int i = 0; i < 4; i++) {
|
|
DockPane& pane = dockpane[i];
|
|
int fsz = dockframe[i].IsShown() ? dockframe[i].GetSize() : 0;
|
|
|
|
s / fsz % pane;
|
|
DockCont *dc = dynamic_cast<DockCont *>(pane.GetFirstChild());
|
|
for (int j = 0; dc && j < pane.GetCount(); j++) {
|
|
s % *dc;
|
|
dc = dynamic_cast<DockCont *>(dc->GetNext());
|
|
}
|
|
}
|
|
cnt = 0;
|
|
// Count Floating
|
|
for (int i = 0; i < conts.GetCount(); i++)
|
|
if (conts[i].IsFloating()) cnt++;
|
|
// Write Floating
|
|
s / cnt;
|
|
for (int i = 0; i < conts.GetCount(); i++) {
|
|
if (conts[i].IsFloating()) {
|
|
conts[i].Serialize(s);
|
|
conts[i].SerializePlacement(s, false);
|
|
}
|
|
}
|
|
// Write Autohidden
|
|
for (int i = 0; i < 4; i++) {
|
|
cnt = hideframe[i].GetCount();
|
|
s / cnt;
|
|
for (int j = 0; j < hideframe[i].GetCount(); j++) {
|
|
int ix = FindDocker(&hideframe[i].GetCtrl(j)->Get(0));
|
|
if (ix >= 0)
|
|
s / ix;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// Read frame order
|
|
if (version >= 5) {
|
|
int ps[4];
|
|
for (int i = 0; i < 4; i++) {
|
|
s % ps[i];
|
|
SetFrame(dockframepos + i, InsetFrame());
|
|
}
|
|
for (int i= 0; i < 4; i++)
|
|
SetFrame(ps[i] + dockframepos, dockframe[i]);
|
|
}
|
|
// Read docked
|
|
for (int i = 0; i < 4; i++) {
|
|
DockPane& pane = dockpane[i];
|
|
dockframe[i].Hide();
|
|
int fsz;
|
|
s / fsz % pane;
|
|
|
|
for (int j = 0; j < pane.GetCount(); j++) {
|
|
DockCont *dc = CreateContainer();
|
|
s % *dc;
|
|
dc->StateDocked(*this);
|
|
pane << *dc;
|
|
}
|
|
if (fsz && pane.GetCount()) {
|
|
dockframe[i].SetSize(fsz);
|
|
dockframe[i].Show();
|
|
}
|
|
else
|
|
dockframe[i].SetSize(0);
|
|
}
|
|
// Read floating
|
|
s / cnt;
|
|
for (int i = 0; i < cnt; i++) {
|
|
DockCont *dc = CreateContainer();
|
|
dc->Serialize(s);
|
|
FloatContainer(*dc);
|
|
dc->SerializePlacement(s, false);
|
|
}
|
|
// Read Autohidden
|
|
for (int i = 0; i < 4; i++) {
|
|
s / cnt;
|
|
for (int j = 0; j < cnt; j++) {
|
|
int ix;
|
|
s / ix;
|
|
if (ix >= 0 && ix < dockers.GetCount())
|
|
AutoHide(i, *dockers[ix]);
|
|
}
|
|
}
|
|
// Clear empty containers
|
|
for (int i = conts.GetCount()-1; i >= 0; i--) {
|
|
if (!conts.GetCount()) CloseContainer(conts[i]);
|
|
}
|
|
RefreshLayout();
|
|
}
|
|
bool haslay = withsavedlayouts;
|
|
s % haslay;
|
|
if (withsavedlayouts && (s.IsStoring() || haslay))
|
|
s % layouts;
|
|
s.Magic();
|
|
}
|
|
|
|
void DockWindow::BackupLayout()
|
|
{
|
|
StringStream s;
|
|
SerializeLayout(s);
|
|
layoutbackup = s;
|
|
}
|
|
|
|
void DockWindow::RestoreLayout()
|
|
{
|
|
if (!layoutbackup.GetCount()) return;
|
|
StringStream s(layoutbackup);
|
|
SerializeLayout(s);
|
|
}
|
|
|
|
int DockWindow::SaveLayout(String name)
|
|
{
|
|
ASSERT(!name.IsEmpty());
|
|
StringStream s;
|
|
SerializeLayout(s, false);
|
|
int ix = layouts.FindAdd(name, String());
|
|
layouts[ix] = (String)s;
|
|
return ix;
|
|
}
|
|
|
|
void DockWindow::LoadLayout(String name)
|
|
{
|
|
int ix = layouts.Find(name);
|
|
if (ix >= 0)
|
|
LoadLayout(ix);
|
|
}
|
|
|
|
void DockWindow::LoadLayout(int ix)
|
|
{
|
|
ASSERT(ix >= 0 && ix < layouts.GetCount());
|
|
StringStream s((String)layouts[ix]);
|
|
SerializeLayout(s, false);
|
|
RefreshLayout();
|
|
}
|
|
|
|
void DockWindow::EnableFloating(bool enable)
|
|
{
|
|
for (int i = 0; i < conts.GetCount(); i++)
|
|
if (conts[i].IsFloating())
|
|
conts[i].Enable(enable);
|
|
}
|
|
|
|
DockWindow::DockWindow()
|
|
{
|
|
menu.Set(*this);
|
|
|
|
#ifdef PLATFORM_WIN32
|
|
childtoolwindows = true;
|
|
#endif
|
|
|
|
init= false;
|
|
tabbing = true;
|
|
nestedtabs = false;
|
|
grouping = true;
|
|
menubtn = true;
|
|
nesttoggle = (K_CTRL | K_SHIFT);
|
|
closebtn = true;
|
|
hidebtn = true;
|
|
locked = false;
|
|
tabtext = true;
|
|
tabalign = false;
|
|
autohide = true;
|
|
frameorder = true;
|
|
childtoolwindows = false;
|
|
showlockedhandles = false;
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
dockframe[i].Set(dockpane[i], 0, i);
|
|
IsTB(i) ? dockpane[i].Horz() : dockpane[i].Vert();
|
|
hideframe[i].SetAlign(i);
|
|
dockframe[i].Hide();
|
|
}
|
|
AllowDockAll().Animate().AnimateDelay(30);
|
|
}
|
|
|
|
// PopUpDockWindow
|
|
#define POPUP_SPACING 0
|
|
|
|
void PopUpDockWindow::ContainerDragStart(DockCont& dc)
|
|
{
|
|
DockWindow::ContainerDragStart(dc);
|
|
last_target = NULL;
|
|
last_popup = NULL;
|
|
}
|
|
|
|
void PopUpDockWindow::ContainerDragMove(DockCont& dc)
|
|
{
|
|
int align = DOCK_NONE;
|
|
|
|
// Is the highlight the same as last time? (Quick escape)
|
|
if (last_popup && last_popup->IsPopUp() && last_popup->GetRect().Contains(GetMousePos()))
|
|
return;
|
|
|
|
DockCont *target = GetMouseDockTarget();
|
|
int dock = DOCK_NONE;
|
|
if (target) {
|
|
dock = GetDockAlign(*target);
|
|
if (!dc.IsDockAllowed(dock))
|
|
target = NULL;
|
|
}
|
|
bool target_changed = (target != last_target)
|
|
&& !GetHighlightCtrl().GetParent()
|
|
&& (!target || !IsPaneAnimating(dock));
|
|
|
|
// Hide show inner popups as necessary
|
|
if (!target && last_target != NULL)
|
|
HidePopUps(true, false);
|
|
else if (target_changed)
|
|
ShowInnerPopUps(dc, target);
|
|
ShowOuterPopUps(dc);
|
|
last_target = target;
|
|
|
|
// Get potential alignment
|
|
align = PopUpHighlight(inner, 5);
|
|
if (align == DOCK_NONE) {
|
|
target = NULL;
|
|
last_target = NULL;
|
|
align = PopUpHighlight(outer, 4);
|
|
}
|
|
else if (align == 4)
|
|
align = DOCK_NONE;
|
|
else if (target) {
|
|
target = IsTL(align) ? target : dynamic_cast<DockCont*>(target->GetNext());
|
|
align = dock;
|
|
}
|
|
|
|
// Do highlight
|
|
if (align != DOCK_NONE || target) {
|
|
if (align == DOCK_NONE) StopHighlight(false);
|
|
dc.SyncUserSize(true, true);
|
|
Highlight(align, dc, target);
|
|
}
|
|
else {
|
|
PopUpHighlight(hide, 4);
|
|
StopHighlight(IsAnimatedHighlight());
|
|
last_popup = NULL;
|
|
}
|
|
}
|
|
|
|
void PopUpDockWindow::ContainerDragEnd(DockCont& dc)
|
|
{
|
|
int align = DOCK_NONE;
|
|
if (IsAutoHide() && showhide)
|
|
align = PopUpHighlight(hide, 4);
|
|
HidePopUps(true, true);
|
|
if (align != DOCK_NONE)
|
|
AutoHideContainer(align, dc);
|
|
else
|
|
DockWindow::ContainerDragEnd(dc);
|
|
last_target = NULL;
|
|
last_popup = NULL;
|
|
}
|
|
|
|
int PopUpDockWindow::PopUpHighlight(PopUpButton *pb, int cnt)
|
|
{
|
|
int ix = -1;
|
|
Point p = GetMousePos();
|
|
for (int i = 0; i < cnt; i++) {
|
|
if (pb[i].IsPopUp() && pb[i].GetRect().Contains(p)) {
|
|
if (!pb[i].hlight) {
|
|
pb[i].hlight = &(style->highlight);
|
|
pb[i].Refresh();
|
|
}
|
|
ix = i;
|
|
last_popup = &pb[i];
|
|
}
|
|
else if (pb[i].hlight) {
|
|
pb[i].hlight = NULL;
|
|
pb[i].Refresh();
|
|
}
|
|
}
|
|
return ix;
|
|
}
|
|
|
|
void PopUpDockWindow::ShowOuterPopUps(DockCont& dc)
|
|
{
|
|
Rect wrect = GetScreenRect();
|
|
Point cp = wrect.CenterPoint();
|
|
Size psz(style->outersize, style->outersize);
|
|
Rect prect = Rect(psz);
|
|
psz /= 2;
|
|
|
|
wrect.Deflate(12, 12);
|
|
|
|
if (dc.IsDockAllowed(DOCK_LEFT)) ShowPopUp(outer[DOCK_LEFT], prect.Offseted(wrect.left + POPUP_SPACING, cp.y - psz.cy));
|
|
if (dc.IsDockAllowed(DOCK_TOP)) ShowPopUp(outer[DOCK_TOP], prect.Offseted(cp.x - psz.cx, wrect.top + POPUP_SPACING));
|
|
if (dc.IsDockAllowed(DOCK_RIGHT)) ShowPopUp(outer[DOCK_RIGHT], prect.Offseted(wrect.right - POPUP_SPACING - psz.cx*2, cp.y - psz.cy));
|
|
if (dc.IsDockAllowed(DOCK_BOTTOM)) ShowPopUp(outer[DOCK_BOTTOM], prect.Offseted(cp.x - psz.cx, wrect.bottom - POPUP_SPACING - psz.cy*2));
|
|
|
|
if (IsAutoHide() && showhide) {
|
|
ShowPopUp(hide[DOCK_LEFT], prect.Offseted(wrect.left + POPUP_SPACING + style->outersize, cp.y - psz.cy));
|
|
ShowPopUp(hide[DOCK_TOP], prect.Offseted(cp.x - psz.cx, wrect.top + POPUP_SPACING + style->outersize));
|
|
ShowPopUp(hide[DOCK_RIGHT], prect.Offseted(wrect.right - POPUP_SPACING - psz.cx*2 - style->outersize, cp.y - psz.cy));
|
|
ShowPopUp(hide[DOCK_BOTTOM], prect.Offseted(cp.x - psz.cx, wrect.bottom - POPUP_SPACING - psz.cy*2 - style->outersize));
|
|
}
|
|
}
|
|
|
|
void PopUpDockWindow::ShowInnerPopUps(DockCont& dc, DockCont *target)
|
|
{
|
|
ASSERT(target);
|
|
Rect wrect = GetScreenRect();
|
|
Size psz(style->innersize, style->innersize);
|
|
Rect prect = Rect(psz);
|
|
Point cp;
|
|
psz /= 2;
|
|
|
|
cp = target->GetScreenRect().CenterPoint();
|
|
int d = psz.cy * 5;
|
|
cp.x = minmax(cp.x, wrect.left + d, wrect.right - d);
|
|
cp.y = minmax(cp.y, wrect.top + d, wrect.bottom - d);
|
|
|
|
int align = GetDockAlign(*target);
|
|
if (IsTB(align)) { // Left/right docking allowed
|
|
ShowPopUp(inner[DOCK_LEFT], prect.Offseted(cp.x - psz.cx*3, cp.y - psz.cy));
|
|
ShowPopUp(inner[DOCK_RIGHT], prect.Offseted(cp.x + psz.cx, cp.y - psz.cy));
|
|
}
|
|
else {
|
|
inner[DOCK_LEFT].Close();
|
|
inner[DOCK_RIGHT].Close();
|
|
}
|
|
if (!IsTB(align)) { // Top/bottom docking allowed
|
|
ShowPopUp(inner[DOCK_TOP], prect.Offseted(cp.x - psz.cx, cp.y - psz.cy*3));
|
|
ShowPopUp(inner[DOCK_BOTTOM], prect.Offseted(cp.x - psz.cx, cp.y + psz.cy));
|
|
}
|
|
else {
|
|
inner[DOCK_TOP].Close();
|
|
inner[DOCK_BOTTOM].Close();
|
|
}
|
|
if (IsTabbing())
|
|
ShowPopUp(inner[4], prect.Offseted(cp.x-psz.cx, cp.y-psz.cy));
|
|
}
|
|
|
|
void PopUpDockWindow::ShowPopUp(PopUpButton& pb, const Rect& r)
|
|
{
|
|
pb.SetRect(r);
|
|
if (!pb.IsPopUp())
|
|
pb.PopUp(this, false, true, true);
|
|
}
|
|
|
|
void PopUpDockWindow::HidePopUps(bool _inner, bool _outer)
|
|
{
|
|
if (_inner)
|
|
for (int i = 0; i < 5; i++)
|
|
inner[i].Close();
|
|
if (_outer) {
|
|
for (int i = 0; i < 4; i++)
|
|
outer[i].Close();
|
|
for (int i = 0; i < 4; i++)
|
|
hide[i].Close();
|
|
}
|
|
}
|
|
|
|
PopUpDockWindow& PopUpDockWindow::SetStyle(const Style& s)
|
|
{
|
|
style = &s;
|
|
for (int i = 0; i < 5; i++)
|
|
inner[i].icon = &s.inner[i];
|
|
for (int i = 0; i < 4; i++)
|
|
outer[i].icon = &s.outer[i];
|
|
for (int i = 0; i < 4; i++)
|
|
hide[i].icon = &s.hide[i];
|
|
return *this;
|
|
}
|
|
|
|
PopUpDockWindow::PopUpDockWindow()
|
|
: showhide(true)
|
|
{
|
|
SetStyle(StyleDefault());
|
|
AnimateDelay(0);
|
|
}
|
|
|
|
CH_STYLE(PopUpDockWindow, Style, StyleDefault)
|
|
{
|
|
inner[0] = DockingImg::DockLeft();
|
|
inner[1] = DockingImg::DockTop();
|
|
inner[2] = DockingImg::DockRight();
|
|
inner[3] = DockingImg::DockBottom();
|
|
inner[4] = DockingImg::DockTab();
|
|
|
|
outer[0] = DockingImg::DockLeft();
|
|
outer[1] = DockingImg::DockTop();
|
|
outer[2] = DockingImg::DockRight();
|
|
outer[3] = DockingImg::DockBottom();
|
|
|
|
hide[0] = DockingImg::HideLeft();
|
|
hide[1] = DockingImg::HideTop();
|
|
hide[2] = DockingImg::HideRight();
|
|
hide[3] = DockingImg::HideBottom();
|
|
|
|
highlight = DockingImg::DockHL();
|
|
|
|
innersize = 25;
|
|
outersize = 25;
|
|
}
|
|
|
|
}
|