#include "DockCtrl/DockCtrl.h" #define IMAGECLASS DockCtrlImgs #define IMAGEFILE #include void DockCtrlCustomFrame::FrameLayout(Rect& r) { switch(layout) { case LAYOUT_LEFT: LayoutFrameLeft(r, this, size); break; case LAYOUT_TOP: LayoutFrameTop(r, this, size); break; case LAYOUT_RIGHT: LayoutFrameRight(r, this, size); break; case LAYOUT_BOTTOM: LayoutFrameBottom(r, this, size); break; } } void DockCtrlCustomFrame::FrameAddSize(Size& sz) { switch(layout) { case LAYOUT_LEFT: sz.cx += size; break; case LAYOUT_TOP: sz.cy += size; break; case LAYOUT_RIGHT: sz.cx += size; break; case LAYOUT_BOTTOM: sz.cy += size; break; } } DockCtrlCustomFrame& DockCtrlCustomFrame::SetStyle(const DockCtrlChStyle::Style& s) { style = &s; return *this; } const DockCtrlChStyle::Style* DockCtrlCustomFrame::GetStyle() { return style ? style : &DockCtrlChStyle::StyleDefault(); } //---------------------------------------------------------------------------------------------- void TabInterface::Add(String name, bool activate) { Tab &t = tabs.Add(); if(IsNull(name) || name == t_("")) name = t_("Untitled"); else t.name = name; t.id = GetNextId(); t.active = activate; t.visible = true; t.highlighted = false; t.icon = DockCtrlImgs::DefaultImage(); ReposTabs(); SetActiveTab(tabs.GetCount() - 1); } void TabInterface::Add(DockableCtrl& dock, bool activate) { Tab &t = tabs.Add(); if(dock.GetLabel().IsVoid() || dock.GetLabel() == t_("")) t.name = t_("Untitled"); else t.name = dock.GetLabel(); t.id = GetNextId(); t.active = activate; t.visible = true; t.highlighted = false; t.icon = dock.GetIcon(); t.dock = &dock; ReposTabs(); SetActiveTab(tabs.GetCount() - 1); } void TabInterface::Remove(DockableCtrl& dock) { } void TabInterface::Remove(int n) { } void TabInterface::Close(int n) { if(tabs.GetCount() < 1 || n >= tabs.GetCount()) // !!! return; int c = Find(tabs[n].id); int nc = GetNext(c); if(nc < 0) nc = max(0, GetPrev(c)); if(hasscrollbar) scrollbar.AddTotal(-tabs[n].cx); tabs.Remove(n); ReposTabs(); SetActiveTab(nc); } void TabInterface::CloseAll() { for(int i = tabs.GetCount() - 1; i >= 0; i--) { tabs.At(i).dock = NULL; tabs.At(i).id = -1; tabs.Remove(i); } if(hasscrollbar) scrollbar.SetTotal(0);//scrollbar.SetTotal(tabs[0].cx); ReposTabs(); SetActiveTab(-1); id = -1; } void TabInterface::SetActiveTab(int n) { int tabcount = tabs.GetCount(); if(tabcount == 0) return; if(n < 0 || n >= tabcount) n = max(0, tabcount - 1); Tab& t = tabs[active = n]; int cx = t.x - (int(hasscrollbar) * scrollbar.GetPos()); if(hasscrollbar) { if(cx < 0) scrollbar.AddPos(cx - 10); else { cx = t.x + t.cx - GetSize().cx - scrollbar.GetPos(); if(cx > 0) scrollbar.AddPos(cx + 10); } } if(HasMouse())MouseMove(GetMouseViewPos(), 0); UpdateActionRefresh(); WhenSelect(); } void TabInterface::SetActiveTab(DockableCtrl& dock) { SetActiveTab(Find(dock)); } void TabInterface::SetActiveTabTitle(String name, Image icon) { if(active < 0) return; Tab& t = tabs.At(active); t.name = name; t.icon = icon; ReposTabs(); } TabInterface& TabInterface::HasScrollBar(bool b) { if(b) { AddFrame(scrollbar); scrollbar.WhenScroll = THISBACK(OnScrollBar); } else RemoveFrame(scrollbar); Refresh(); hasscrollbar = b; return *this; } int TabInterface::GetActiveTab() { return active; } int TabInterface::Find(int itemid) { for(int i = 0; i < tabs.GetCount(); i++) if(tabs[i].id == itemid) return i; return -1; } int TabInterface::Find(DockableCtrl& dock) { for(int i = 0; i < tabs.GetCount(); i++) if(tabs[i].dock == &dock) return i; return -1; } int TabInterface::GetNext(int n) { for(int i = n + 1; i < tabs.GetCount(); i++) if(tabs[i].visible) return i; return -1; } int TabInterface::GetPrev(int n) { for(int i = n - 1; i >= 0; i--) if(tabs[i].visible) return i; return -1; } int TabInterface::GetWidth(int n) { Tab &t = tabs[n]; t.textsize = GetTextSize(t.name, style->font); return TAB_MARGIN * 2 + t.textsize.cx + ((hasfileicon ? TAB_SPACE : TAB_SPACE / 2) + DockCtrlImgs::DClose().GetSize().cx) * hastabbutton + (TAB_FILEICON + TAB_SPACEICON) * hasfileicon; } int TabInterface::GetWidth() { return tabs[tabs.GetCount() - 1].Right() + style->margin * 2; } int TabInterface::GetHeight() { return style->tabheight + style->sel.bottom + (int(hasscrollbar) * (TAB_SBHEIGHT + TAB_SBSEPARATOR)); } void TabInterface::ReposTabs() { if(!tabs.GetCount()) return; int j = 0; bool first = true; for(int i = 0; i < tabs.GetCount(); i++) { tabs[i].x = first ? 0 : tabs[j].Right(); j = i; first = false; tabs[i].visible = true; tabs[i].y = 0; tabs[i].cx = GetWidth(i); tabs[i].cy = style->tabheight + style->sel.top; } if(hasscrollbar) scrollbar.SetTotal(GetWidth()); } void TabInterface::DrawLabel(Draw& w, int x, int y, Tab& t, bool isactive) { String fn = GetFileName(t.name); if(horizontal) DrawFileName(w, x + TAB_MARGIN + (TAB_FILEICON + TAB_SPACEICON) * int(hasfileicon), (y - t.textsize.cy) / 2, t.textsize.cx, t.textsize.cy, fn.ToWString(), false, StdFont(), Black, LtBlue, Null, Null, false); else w.DrawText((y + t.textsize.cy) / 2 + 1, x + TAB_MARGIN + (TAB_FILEICON + TAB_SPACEICON) * int(hasfileicon), 2700, fn.ToWString(), style->font); } void TabInterface::DrawTab(Draw& w, Size &sz, int i) { Size isz = DockCtrlImgs::DClose().GetSize(); int cnt = tabs.GetCount(); Tab &t = tabs[i]; bool ac = i == active; bool hl = i == highlight; int lx = i > 0 ? style->extendleft : 0; int x = t.x - (hasscrollbar ? scrollbar.GetPos() : 0) + style->margin - lx; int ndx = !IsEnabled() ? CTRL_DISABLED : ac ? CTRL_PRESSED : hl ? CTRL_HOT : CTRL_NORMAL; Image nimg; int cx = t.cx + lx + (ac ? style->sel.right + style->sel.left : 0); int cy = ac ? t.cy - style->sel.top + style->sel.bottom : t.cy - style->sel.top; int h = style->sel.top + style->sel.bottom; int hs = ac ? 0 : h; if(layout == LAYOUT_RIGHT || layout == LAYOUT_BOTTOM) hs = -hs; ImageDraw img(cx, cy); img.DrawRect(0, 0, cx, cy, SColorFace); const Value& sv = (cnt == 1 ? style->both : i == 0 ? style->first : i == cnt - 1 ? style->last : style->normal)[ndx]; ChPaint(img, 0, 0, cx, cy, sv); if(hastabbutton && tabs.GetCount()) img.DrawImage(t.cx - isz.cx - TAB_MARGIN, (cy + h - isz.cy) / 2, (ac || hl) ? (tabbutton == i ? DockCtrlImgs::DCloseh : ac ? DockCtrlImgs::DCloses : DockCtrlImgs::DClose) : DockCtrlImgs::DClose); if(hasfileicon) img.DrawImage(TAB_MARGIN, (cy + h - t.icon.GetSize().cx) / 2, t.icon); int shift = 0; switch(layout) { case LAYOUT_LEFT: nimg = MirrorVert(RotateAntiClockwise(img)); break; case LAYOUT_TOP: nimg = img; break; case LAYOUT_RIGHT: nimg = RotateClockwise(img); shift = TAB_SBSEPARATOR; break; case LAYOUT_BOTTOM: nimg = MirrorVert(img); shift = TAB_SBSEPARATOR; break; } if(ac) w.DrawImage(horizontal ? x - style->sel.left : shift, horizontal ? shift : x - style->sel.left, nimg); else w.DrawImage(horizontal ? x : style->sel.left - shift, horizontal ? style->sel.top - shift : x, nimg); DrawLabel(w, x, cy + hs, t, ac); } void TabInterface::Paint(Draw& d) { Size sz = GetSize(); d.DrawRect(sz, SColorFace()); if(!horizontal) d.DrawRect(layout == LAYOUT_LEFT ? sz.cx - 1: 0, 0, 1, sz.cy, LtGray()); else d.DrawRect(0, layout == LAYOUT_TOP ? sz.cy - 1 : 0, sz.cx, 1, LtGray()); Fix(sz); int tabcount = tabs.GetCount(); for(int i = 0; i < tabcount; i++) if(tabs[i].IsVisible() && active != i && i < tabcount) DrawTab(d, sz, i); if(active >= 0 && active < tabcount) DrawTab(d, sz, active); if(target >= 0) { int last = tabs.GetCount() - 1; int first = 0; if(target != active && target != active + 1) { int x = (target == last + 1 ? tabs[last].Right() : tabs[target].x) - (hasscrollbar ? scrollbar.GetPos() : 0) - (target <= first ? 1 : 2) + style->margin - (target > 0 ? style->extendleft : 0); int y = style->sel.top; int cy = sz.cy - y; Color c(255, 0, 0); if(horizontal) { d.DrawRect(x + 1, y, 2, cy, c); d.DrawRect(x, y, 4, 1, c); d.DrawRect(x, y + cy - 1, 4, 1, c); } else { Swap(x, y); d.DrawRect(x, y + 1, cy, 2, c); d.DrawRect(x, y, 1, 4, c); d.DrawRect(x + cy - 1, y, 1, 4, c); } } } } void TabInterface::LeftDown(Point p, dword keyflags) { if(keyflags & K_SHIFT) { highlight = -1; Refresh(); SetCapture(); oldp = p; return; } isctrl = keyflags & K_CTRL; if(isctrl) return; if(tabbutton >= 0) { int i = Find(tabbutton); if(i == -1 && active >= 0) i = active; if(i >= 0) { DockableCtrl* dock = tabs[i].dock; if(dock) WhenClose(tabbutton, *dock); } } if(highlight >= 0) { SetActiveTab(highlight); if(active >= 0) WhenActive(active, *tabs[active].dock); } } void TabInterface::LeftUp(Point p, dword keyflags) { ReleaseCapture(); } void TabInterface::MiddleDrag(Point p, dword keyflags) { if(!isdraggable) return; isdragged = true; DoDragAndDrop(InternalClip(*this, Format(internalname, this))); } void TabInterface::LeftDrag(Point p, dword keyflags) { if(keyflags & K_SHIFT) return; if(highlight < 0) return; if(isctrl) { isdragged = true; DoDragAndDrop(InternalClip(*this, Format(internalname, this))); return; } if(highlight >= 0) { Fix(p); SetActiveTab(highlight); p.x += (int(hasscrollbar) * scrollbar.GetPos()); if(tabs[active].HasMouse(p)) WhenDrag(active, *tabs[active].dock); } } void TabInterface::RightDown(Point p, dword keyflags) { if(highlight >= 0) SetActiveTab(highlight); WhenContext(active, *tabs[active].dock); } void TabInterface::RightUp(Point p, dword keyflags) { } void TabInterface::MouseMove(Point p, dword keyflags) { Fix(p); if(HasCapture() && hasscrollbar) { scrollbar.AddPos(p.x - oldp.x, true); oldp = p; return; } p.x += (int(hasscrollbar) * scrollbar.GetPos()) - style->margin + style->extendleft; Size sz = GetSize(); Fix(sz); int h = highlight; bool istabbutton = false; bool istab = false; for(int i = 0; i < tabs.GetCount(); i++) { if(tabs[i].HasMouse(p)) { istab = true; //int h = sz.cy + (active == i ? 0 : style->sel.top); int h = style->sel.top + style->sel.bottom; int hs = active == i ? style->sel.top : h; if(layout == LAYOUT_RIGHT || layout == LAYOUT_BOTTOM) hs = -hs; hs += sz.cy; int ws = active == i ? style->sel.left : 0; istabbutton = hastabbutton ? tabs[i].HasMouseButton(p, ws, hs) : false; if(highlight != i || (istabbutton && tabbutton != i)) { tabbutton = istabbutton ? i : -1; highlight = i; WhenHighlight(); Refresh(); return; } } } if(!istab && h >= 0) { highlight = -1; WhenHighlight(); Refresh(); } if(!istabbutton && tabbutton >= 0) { tabbutton = -1; Refresh(); } } void TabInterface::MouseWheel(Point p, int zdelta, dword keyflags) { if(!hasscrollbar) return; scrollbar.AddPos(-zdelta / 4, true); MouseMove(p, 0); } void TabInterface::MouseLeave() { if(isdragged) return; highlight = -1; tabbutton = -1; Refresh(); } int TabInterface::GetTargetTab(Point p) { Fix(p); p.x += (hasscrollbar ? scrollbar.GetPos() : 0); int f = 0; int l = tabs.GetCount() - 1; if(tabs[f].visible && p.x < tabs[f].x + tabs[f].cx / 2) return f; for(int i = l; i >= f; i--) if(tabs[i].visible && p.x >= tabs[i].x + tabs[i].cx / 2) return i == l ? i + 1 : GetNext(i); return -1; } int TabInterface::GetSourceTab(Point p) { int cnt = tabs.GetCount(); if(((active >= 0 && active < cnt) || (highlight >= 0 && highlight < cnt))) { for(int i = tabs.GetCount() - 1; i >= 0; i--) if(tabs[i].visible && tabs[i].HasMouse(p)) return (source = i); } return (source = highlight); } void TabInterface::DragAndDrop(Point p, PasteClip& d) { int c = GetTargetTab(p); int tab = isctrl ? highlight : active; if(tab == -1) return; bool sametab = c == tab || c == tab + 1; bool internal = AcceptInternal(d, internalname); if(!sametab && d.IsAccepted()) { if(internal) { int id = tabs[active].id; Tab t = tabs[tab]; tabs.Insert(c, t); tabs.Remove(tab + int(c < tab)); active = Find(id); isdragged = false; ReposTabs(); return; } else if(d.IsPaste()) { CancelMode(); return; } } else { //d.Reject(); //unfortunately after Reject DragLeave stops working until d is accepted } if(c != target) { target = c; Refresh(); } } void TabInterface::DragEnter() { } void TabInterface::DragLeave() { target = -1; Refresh(); } void TabInterface::DragRepeat(Point p) { { int dx = GetDragScroll(this, p, 16).x; if(dx != 0 && hasscrollbar) scrollbar.AddPos(dx); } } void TabInterface::CancelMode() { isdragged = false; target = -1; Refresh(); } TabInterface& TabInterface::SetLayout(int l) { DockCtrlCustomFrame::SetLayout(l); switch(l) { case LAYOUT_LEFT: horizontal = scrollbar.horizontal = false; scrollbar.SetLayout(LAYOUT_RIGHT); break; case LAYOUT_TOP: horizontal = scrollbar.horizontal = true; scrollbar.SetLayout(LAYOUT_BOTTOM); break; case LAYOUT_RIGHT: horizontal = scrollbar.horizontal = false; scrollbar.SetLayout(LAYOUT_LEFT); break; case LAYOUT_BOTTOM: horizontal = scrollbar.horizontal = true; scrollbar.SetLayout(LAYOUT_TOP); break; } RefreshLayout(); return *this; } TabInterface::TabInterface() { internalname = "tabs"; id = -1; active = -1; highlight = -1; visible = -1; fileicon = -1; tabbutton = -1; target = -1; // drag target. source = -1; // drag source. hasfileicon = false; hastabbutton = true; isdraggable = true; isctrl = false; isdragged = false; SetStyle(TabCtrl::StyleDefault()); DockCtrlCustomFrame::layout = LAYOUT_BOTTOM; HasScrollBar(true); SetSize(GetHeight() + 2); BackPaint(); } TabInterface::~TabInterface() { for(int i = 0; i < tabs.GetCount(); i++) tabs.Remove(i); } //---------------------------------------------------------------------------------------------- TabInterface::TabScrollBar::TabScrollBar() { DockCtrlCustomFrame::layout = LAYOUT_TOP; DockCtrlCustomFrame::SetSize(TAB_SBHEIGHT); total = 0; pos = 0; ps = 0; start_pos = 0; new_pos = 0; old_pos = 0; ready = false; } void TabInterface::TabScrollBar::Paint(Draw &w) { if(!ready) { UpdatePos(); ready = true; } Size sz = GetSize(); w.DrawRect(sz, SColorFace()); if(horizontal) { if(total > sz.cx) w.DrawRect(ffloor(pos), 1, fceil(size), 2, Blue); else w.DrawRect(0, sz.cy - 1, sz.cx, 1, White); } else { if(total > sz.cy) w.DrawRect(1, ffloor(pos), 2, fceil(size), Blue); else w.DrawRect(sz.cx - 1, 0, 1, sz.cy, White); } } void TabInterface::TabScrollBar::LeftDown(Point p, dword keyflags) { SetCapture(); Fix(p); old_pos = new_pos = p.x; if(p.x < pos || p.x > pos + size) start_pos = size / 2; else start_pos = UPP::tabs(p.x - pos); UpdatePos(); Refresh(); } void TabInterface::TabScrollBar::LeftUp(Point p, dword keyflags) { ReleaseCapture(); Fix(p); old_pos = p.x; } void TabInterface::TabScrollBar::MouseMove(Point p, dword keyflags) { if(!HasCapture()) return; Fix(p); new_pos = p.x; UpdatePos(); Refresh(); } void TabInterface::TabScrollBar::MouseWheel(Point p, int zdelta, dword keyflags) { AddPos(-zdelta / 4, true); } void TabInterface::TabScrollBar::Layout() { UpdatePos(false); } int TabInterface::TabScrollBar::GetPos() const { return ffloor(ps); } void TabInterface::TabScrollBar::SetPos(int p, bool dontscale) { Fix(sz); pos = total > 0 ? dontscale ? p : iscale(p, sz.cx, total) : 0; UpdatePos(false); Refresh(); } void TabInterface::TabScrollBar::AddPos(int p, bool dontscale) { Fix(sz); pos += total > 0 ? dontscale ? p : iscale(p, sz.cx, total) : 0; UpdatePos(false); Refresh(); } int TabInterface::TabScrollBar::GetTotal() const { return total; } void TabInterface::TabScrollBar::AddTotal(int t) { total += t; UpdatePos(); Refresh(); } void TabInterface::TabScrollBar::SetTotal(int t) { bool upd = total < t; total = t; UpdatePos(upd); Refresh(); } void TabInterface::TabScrollBar::GoEnd() { pos = total; UpdatePos(false); Refresh(); } void TabInterface::TabScrollBar::GoBegin() { pos = 0; UpdatePos(false); Refresh(); } void TabInterface::TabScrollBar::Set(const TabScrollBar& t) { total = t.total; pos = t.pos; ps = t.ps; Refresh(); } void TabInterface::TabScrollBar::UpdatePos(bool update) { sz = GetSize(); Fix(sz); if(total <= 0 || sz.cx <= 0) cs = ics = 0; else { cs = sz.cx / ((double) total + 0.5); ics = total / ((double) sz.cx); } size = sz.cx * cs; if(update) pos = new_pos - start_pos; if(pos < 0) pos = 0; else if(pos + size > sz.cx) pos = sz.cx - size; ps = total > sz.cx ? pos * ics : 0; WhenScroll(); } TabInterface::TabScrollBar& TabInterface::TabScrollBar::SetLayout(int l) { layout = l; RefreshLayout(); return *this; }