$uvs: PENDING CONFLICT #include "TabBar.h" #define IMAGECLASS TabBarImg #define IMAGEFILE #include // AlignedFrame void AlignedFrame::FrameLayout(Rect &r) { switch(layout) { case LEFT: LayoutFrameLeft(r, this, framesize); break; case TOP: LayoutFrameTop(r, this, framesize); break; case RIGHT: LayoutFrameRight(r, this, framesize); break; case BOTTOM: LayoutFrameBottom(r, this, framesize); break; } r.top += border; r.left += border; r.right -= border; r.bottom -= border; } void AlignedFrame::FrameAddSize(Size& sz) { sz += border * 2; $uvs: REPOSITORY INSERT IsVert() ? sz.cx += framesize : sz.cy += framesize; $uvs: END REPOSITORY INSERT $uvs: REPOSITORY DELETE switch(layout) { case LEFT: sz.cx += framesize; break; case TOP: sz.cy += framesize; break; case RIGHT: sz.cx -= framesize; break; case BOTTOM: sz.cy -= framesize; break; } $uvs: END REPOSITORY DELETE } void AlignedFrame::FramePaint(Draw& w, const Rect& r) { if(border > 0) { Rect n = r; switch(layout) { case LEFT: n.left += framesize; break; case TOP: n.top += framesize; break; case RIGHT: n.right -= framesize; break; case BOTTOM: n.bottom -= framesize; break; } FieldFrame().FramePaint(w, n); } else FrameCtrl::FramePaint(w, r); } AlignedFrame& AlignedFrame::SetFrameSize(int sz, bool refresh) { framesize = sz; if (refresh) RefreshParentLayout(); return *this; } void AlignedFrame::Fix(Size& sz) { if(IsVert()) Swap(sz.cx, sz.cy); } void AlignedFrame::Fix(Point& p) { if(IsVert()) Swap(p.x, p.y); } // TabScrollBar TabScrollBar::TabScrollBar() { Clear(); } void TabScrollBar::Clear() { total = 0; pos = 0; ps = 0; start_pos = 0; new_pos = 0; old_pos = 0; ready = false; } void 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; } void TabScrollBar::Paint(Draw &w) { if(!ready) { UpdatePos(); ready = true; } Size rsz = GetSize(); w.DrawRect(rsz, White); Point p; if(total > sz.cx) { p = Point(ffloor(pos), 1); rsz = Size(fceil(size), 2); } else { p = Point(0, 1); rsz = Size(sz.cx, 2); } Fix(p); Fix(rsz); w.DrawRect(p.x, p.y, rsz.cx, rsz.cy, Blue); } void TabScrollBar::Layout() { UpdatePos(false); } void 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 = tabs(p.x - pos); UpdatePos(); UpdateActionRefresh(); } void TabScrollBar::LeftUp(Point p, dword keyflags) { ReleaseCapture(); Fix(p); old_pos = p.x; } void TabScrollBar::MouseMove(Point p, dword keyflags) { if(!HasCapture()) return; Fix(p); new_pos = p.x; UpdatePos(); UpdateActionRefresh(); } void TabScrollBar::MouseWheel(Point p, int zdelta, dword keyflags) { AddPos(-zdelta / 4, true); UpdateActionRefresh(); } int TabScrollBar::GetPos() const { return ffloor(ps); } void TabScrollBar::SetPos(int p, bool dontscale) { pos = total > 0 ? dontscale ? p : iscale(p, sz.cx, total) : 0; UpdatePos(false); Refresh(); } void TabScrollBar::AddPos(int p, bool dontscale) { pos += total > 0 ? dontscale ? p : iscale(p, sz.cx, total) : 0; UpdatePos(false); Refresh(); } int TabScrollBar::GetTotal() const { return total; } void TabScrollBar::SetTotal(int t) { bool upd = total < t; total = t; UpdatePos(upd); Refresh(); } void TabScrollBar::AddTotal(int t) { total += t; UpdatePos(); Refresh(); } void TabScrollBar::GoEnd() { pos = total; UpdatePos(false); Refresh(); } void TabScrollBar::GoBegin() { pos = 0; UpdatePos(false); Refresh(); } void TabScrollBar::Set(const TabScrollBar& t) { total = t.total; pos = t.pos; ps = t.ps; Refresh(); } bool TabScrollBar::IsScrollable() const { return IsHorz() && total > sz.cx && sz.cx > 0 || IsVert() && total > sz.cy && sz.cy > 0; } // TabBar TabBar::Style TabBar::leftstyle; TabBar::Style TabBar::rightstyle; TabBar::Style TabBar::bottomstyle; TabBar::TabBar() { Clear(); display = NULL; crosses = true; grouping = true; isctrl = false; isdrag = false; inactiveshadow = true; autoscrollhide = true; neverempty = 1; style[0] = style[1] = style[2] = style[3] = NULL; SetAlign(TOP); SetFrameSize(GetHeight()); BackPaint(); } void TabBar::CloseAll() { for(int i = tabs.GetCount() - 1; i >= 0; i--) if(i != active) tabs.Remove(i); SyncScrollBar(tabs[0].cx); MakeGroups(); Repos(); SetCursor(0); $uvs: REPOSITORY INSERT WhenCloseAll(); $uvs: END REPOSITORY INSERT } int TabBar::GetNextId() { return ++id; } void TabBar::ContextMenu(Bar& bar) { if (highlight >= 0) { bar.Add(tabs.GetCount() > 1, "Close", THISBACK1(Close, highlight)); bar.Separator(); } int cnt = groups.GetCount(); for(int i = 0; i < cnt; i++) { String name = Format("%s (%d)", groups[i].name, groups[i].count); Bar::Item &it = i > 0 ? bar.Add(name, THISBACK1(GroupMenu, i)) : bar.Add(name, THISBACK1(DoGrouping, i)); if(i == group) it.Image(TabBarImg::CHK); if(i == 0 && cnt > 1) bar.Separator(); } bar.Separator(); bar.Add("Close all tabs except current", THISBACK(CloseAll)); } void TabBar::GroupMenu(Bar &bar, int n) { bar.Add("Set active", THISBACK1(DoGrouping, n)); bar.Add("Close", THISBACK1(DoCloseGroup, n)); } bool TabBar::Tab::HasMouse(const Point& p) const { return visible && p.x >= x && p.x < x + cx; } bool TabBar::Tab::HasMouseCross(const Point& p, int h, int type) const { if(!visible) return false; Size isz = TabBarImg::CR0().GetSize(); int iy = (h - isz.cy) / 2; int ix = x + (type ? cx - isz.cx - QT_MARGIN : QT_MARGIN); return p.x >= ix && p.x < ix + isz.cx && p.y >= iy && p.y < iy + isz.cy; } int TabBar::FindGroup(const String& g) const { for(int i = 0; i < groups.GetCount(); i++) if(groups[i].name == g) return i; return -1; } void TabBar::MakeGroups() { groups[0].count = tabs.GetCount(); groups[0].first = 0; groups[0].last = tabs.GetCount() - 1; for(int i = 1; i < groups.GetCount(); i++) { groups[i].count = 0; groups[i].first = 10000000; groups[i].last = 0; } for(int i = 0; i < tabs.GetCount(); i++) { Tab &tab = tabs[i]; int n = FindGroup(tab.group); ASSERT(n >= 0); if (n != 0) { if(groups[n].active < 0) groups[n].active = tab.id; groups[n].count++; groups[n].last = i; if(i < groups[n].first) groups[n].first = i; if(i > groups[n].last) groups[n].last = i; } } int removed = 0; for(int i = 1; i < groups.GetCount(); i++) if(groups[i].count == 0) { groups.Remove(i - removed); removed++; } if(group > groups.GetCount() - 1 && group > 0) group--; } void TabBar::DoGrouping(int n) { group = n; Repos(); SyncScrollBar(GetWidth()); SetCursor(-1); } void TabBar::DoCloseGroup(int n) { int cnt = groups.GetCount(); if(cnt <= 0) return; if (cnt == n) --group; String group = groups[n].name; for(int i = tabs.GetCount() - 1; i >= 0; i--) { if(group == tabs[i].group && tabs.GetCount() > 1) { Value v = tabs[i].data; tabs.Remove(i); WhenClose(v); } } if(cnt > 1) groups.Remove(n); MakeGroups(); Repos(); SetCursor(-1); WhenCursor(); } void TabBar::NewGroup(const String &name, const Value &data) { Group &g = groups.Add(); g.name = name; g.data = data; g.count = 0; g.first = 10000000; g.last = 0; g.active = -1; } Value TabBar::AlignValue(int align, const Value &v, const Size &isz) { if (align == TOP) return v; if (IsTypeRaw(v)) { switch(align) { case AlignedFrame::LEFT: return RotateAntiClockwise((Image)v); case AlignedFrame::RIGHT: return RotateClockwise((Image)v); case AlignedFrame::BOTTOM: return MirrorVert((Image)v); } } else if (!IsTypeRaw(v)) { ImageDraw w(isz.cx, isz.cy); ChPaint(w, isz, v); ImageBuffer img; Image temp = (Image)w; switch(align) { case AlignedFrame::LEFT: temp = RotateAntiClockwise(temp); img = temp; // GCC img.SetHotSpot(Point(1, 5)); img.Set2ndSpot(Point(0, isz.cx/2)); break; case AlignedFrame::RIGHT: temp = RotateClockwise(temp); img = temp; img.SetHotSpot(Point(isz.cy-10, isz.cx-10)); img.Set2ndSpot(Point(isz.cy/2, isz.cx/2)); break; case AlignedFrame::BOTTOM: temp = MirrorVert(temp); img = temp; img.SetHotSpot(Point(10, 10)); img.Set2ndSpot(Point(isz.cx/2, isz.cy/2)); break; } return (Image)img; } return v; } void TabBar::TabCenter(Point &p, const Size &sz, int h) { if (IsHorz()) p.y += (sz.cy - h) / 2; else p.x += (sz.cx - h) / 2 * ((GetAlign() == LEFT) ? 1 : -1); } void TabBar::DrawTabData(Draw& w, Point p, const Size &sz, const Value& q, const Font &font, Color ink, dword style) { WString txt; Font f = font; Color i = ink; int a = TextAngle(); if(IsType(q)) { const AttrText& t = ValueTo(q); txt = t.text; if(!IsNull(t.font)) f = t.font; i = IsNull(t.ink) ? ink : t.ink; } else txt = IsString(q) ? q : StdConvert().Format(q); TabCenterText(p, sz, font); w.DrawText(p.x, p.y, TextAngle(), txt, f, i); } void TabBar::DrawTab(Draw &w, const Style &s, const Size &sz, int n, bool enable) { TabBar::Tab &t = tabs[n]; int cnt = tabs.GetCount(); Size tsz, isz(0, 0); Point p; int align = GetAlign(); bool ac = n == active; bool hl = n == highlight; int ndx = !enable ? CTRL_DISABLED : ac ? CTRL_PRESSED : hl ? CTRL_HOT : CTRL_NORMAL; const Value& sv = (cnt == 1 ? s.both : n == 0 ? s.first : n == cnt - 1 ? s.last : s.normal)[ndx]; if (ac) { p = Point(t.x - sc.GetPos() + s.margin - s.extendleft - s.sel.left, 0); tsz = Size(t.cx + s.sel.right + s.sel.left, t.cy + s.sel.bottom); if (align == LEFT) { p.x += s.sel.left; tsz.cx += s.sel.left; } } else { p = Point(t.x - sc.GetPos() + s.margin - s.extendleft, (IsBR() ? s.sel.bottom : s.sel.top)); tsz = Size(t.cx + s.sel.right + s.sel.left, t.cy - s.sel.top); } if (IsVert()) ChPaint(w, p.y, p.x, tsz.cy, tsz.cx, sv); else ChPaint(w, p.x, p.y, tsz.cx, tsz.cy, sv); if(crosses && cnt > neverempty) { Point cp; const Image &cimg = TabBarImg::CR0(); // Use style? isz = cimg.GetSize(); Fix(isz); if (align == LEFT) cp.x = p.x + QT_MARGIN; else cp.x = p.x + tsz.cx - isz.cx - QT_MARGIN; cp.y = p.y + (tsz.cy - isz.cy) / 2; Fix(cp); w.DrawImage(cp.x, cp.y, (ac || hl) ? (cross == n ? TabBarImg::CR2 : ac ? TabBarImg::CR1 : TabBarImg::CR0) : TabBarImg::CR0); isz.cx += 2; } p.x += QT_MARGIN; tsz.cx -= QT_MARGIN * 2; switch (align) { case BOTTOM: if (ac) p.y -= s.sel.top; break; case RIGHT: p.y = sz.cx - p.y; break; case LEFT: p.x += tsz.cx; } tsz.cx -= isz.cx; Fix(p); Fix(tsz); if (display) display->Paint(w, Rect(p, tsz), t.data, s.text_color[ndx], SColorDisabled(), ndx); else { DrawTabData(w, p, tsz, t.data, s.font, s.text_color[ndx], ndx); } } void TabBar::Paint(Draw &w) { const Style &st = *style[GetAlign()]; Size sz = GetSize(); w.DrawRect(sz, SColorFace()); IsVert() ? w.DrawRect((GetAlign() == LEFT) ? sz.cx -1 : 1, 0, 1, sz.cy, Color(128, 128, 128)) : w.DrawRect(0, (GetAlign() == TOP) ? sz.cy - 1 : 1, sz.cx, 1, Color(128, 128, 128)); if (!tabs.GetCount()) return; int cxy = IsVert() ? sz.cy : sz.cx; for(int i = 0; i < tabs.GetCount(); i++) if(tabs[i].visible && i != active) { DrawTab(w, st, sz, i, IsEnabled()); if (tabs[i].x > cxy) break; } if (inactiveshadow) for(int i = 0; i < tabs.GetCount() && tabs[i].x < cxy; i++) if(!tabs[i].visible && i != active) DrawTab(w, st, sz, i, false); if(active >= 0) DrawTab(w, st, sz, active, true); if(target >= 0) { int dragtab = isctrl ? highlight : active; if(target != dragtab && target != dragtab + 1) { Tab &tab = tabs[dragtab]; Size tsz(tab.cx, IsVert() ? sz.cx : sz.cy); Fix(tsz); if (dragimg.IsEmpty()) { int alpha = 180; ImageDraw img(tsz); img.Alpha().DrawRect(tsz, Color(alpha, alpha, alpha)); img.DrawRect(tsz, SColorFace()); Point temp = Point(tab.x, tab.y); tab.x = tab.y = 0; DrawTab(img, st, sz, dragtab, true); dragimg = img; tab.x = temp.x; tab.y = temp.y; } int last = GetLast(); int first = GetFirst(); int x = (target == last + 1 ? tabs[last].Right() : tabs[target].x) - sc.GetPos() - (target <= first ? 1 : 2) + st.margin - (target > 0 ? st.extendleft : 0); if (IsVert()) w.DrawImage(0, x - tsz.cx/2, tsz.cy, tsz.cx, dragimg); else w.DrawImage(x - tsz.cx/2, 0, tsz.cx, tsz.cy, dragimg); /* int y = st.sel.top; Color c(255, 0, 0); int cy = sz.cy - y; if (IsHorz()) { w.DrawRect(x + 1, y, 2, cy, c); w.DrawRect(x, y, 4, 1, c); w.DrawRect(x, y + cy - 1, 4, 1, c); } else{ w.DrawRect(y, x + 1, cy, 2, c); w.DrawRect(y, x, 1, 4, c); w.DrawRect(y + cy - 1, x, 1, 4, c); } */ } } } void TabBar::Scroll() { $uvs: REPOSITORY DELETE if (IsVisible()) scroller.Scroll(*this, IsVert() ? Point(0, sc.GetPos()) : Point(sc.GetPos(), 0)); } int TabBar::GetWidth(int n) { Tab &t = tabs[n]; t.tsz = GetStdSize(t.data); return QT_MARGIN * 2 + t.tsz.cx + (QT_SPACE + TabBarImg::CR0().GetSize().cx) * crosses; } Size TabBar::GetStdSize(Value &q) { if (display) return display->GetStdSize(q); else if (q.GetType() == STRING_V) return GetTextSize((String)q, StdFont()); else if (q.GetType() == WSTRING_V) return GetTextSize((WString)q, StdFont()); return GetTextSize("A Tab", StdFont()); } TabBar& TabBar::Add(const Value &data, String group, bool make_active) { return Insert(tabs.GetCount(), data, group, make_active); } TabBar& TabBar::Insert(int ix, const Value &data, String group, bool make_active) { Tab &t = tabs.Insert(ix); int cnt = tabs.GetCount() - 1; t.data = data; t.id = GetNextId(); int g = 0; if (!group.IsEmpty()) { g = FindGroup(group); if (g < 0) { NewGroup(group); g = groups.GetCount() - 1; } } t.group = groups[g].name; MakeGroups(); Repos(); active = -1; SetCursor(minmax(ix, 0, cnt)); return *this; } int TabBar::GetWidth()const { if (!tabs.GetCount()) return 0; int j = GetLast(); return tabs[GetLast()].Right() + style[GetAlign()]->margin * 2; } int TabBar::GetStyleHeight(const Style &s) { return s.tabheight + s.sel.top /*+ style->sel.bottom */ + QT_SBSEPARATOR; } void TabBar::Repos() { if(!tabs.GetCount()) return; String g = GetGroupName(); int j; bool first = true; j = 0; for(int i = 0; i < tabs.GetCount(); i++) j = TabPos(g, first, i, j, false); if (inactiveshadow) for(int i = 0; i < tabs.GetCount(); i++) if (!tabs[i].visible) j = TabPos(g, first, i, j, true); SyncScrollBar(GetWidth()); } int TabBar::TabPos(const String &g, bool &first, int i, int j, bool inactive) { bool v = IsNull(g) ? true : g == tabs[i].group; if(v) { tabs[i].x = first ? 0 : tabs[j].Right(); j = i; first = false; } else { tabs[i].x = 0; if (inactive) { tabs[i].x = tabs[j].Right(); return (j = i); } } tabs[i].visible = v; tabs[i].y = 0; tabs[i].cx = GetWidth(i); tabs[i].cy = style[1]->tabheight + style[1]->sel.top; return j; } void TabBar::SyncScrollBar(int total) { if (total > 0) sc.SetTotal(total); if (autoscrollhide) { int h = GetHeight(); bool v = GetFrameSize() > h; bool nv = sc.IsScrollable(); sc.Show(nv); if (v != nv) SetFrameSize((nv ? sc.GetFrameSize() : 0) + h, false); } else sc.Show(); } int TabBar::FindId(int id) const { for(int i = 0; i < tabs.GetCount(); i++) if(tabs[i].id == id) return i; return -1; } int TabBar::GetNext(int n) const { for(int i = n + 1; i < tabs.GetCount(); i++) if(tabs[i].visible) return i; return -1; } int TabBar::GetPrev(int n) const { for(int i = n - 1; i >= 0; i--) if(tabs[i].visible) return i; return -1; } void TabBar::Clear() { highlight = -1; active = -1; target = -1; id = -1; tabs.Clear(); groups.Clear(); NewGroup("All"); group = 0; $uvs: END REPOSITORY DELETE Refresh(); } $uvs: REPOSITORY INSERT int TabBar::GetWidth(int n) { Tab &t = tabs[n]; t.tsz = GetStdSize(t.data); return QT_MARGIN * 2 + t.tsz.cx + (QT_SPACE + TabBarImg::CR0().GetSize().cx) * crosses; } Size TabBar::GetStdSize(Value &q) { if (display) return display->GetStdSize(q); else if (q.GetType() == STRING_V) return GetTextSize((String)q, StdFont()); else if (q.GetType() == WSTRING_V) return GetTextSize((WString)q, StdFont()); return GetTextSize("A Tab", StdFont()); } TabBar& TabBar::Add(const Value &data, String group, bool make_active) { return Insert(tabs.GetCount(), data, group, make_active); } TabBar& TabBar::Insert(int ix, const Value &data, String group, bool make_active) { Tab &t = tabs.Insert(ix); int cnt = tabs.GetCount() - 1; t.data = data; t.id = GetNextId(); int g = 0; if (!group.IsEmpty()) { g = FindGroup(group); if (g < 0) { NewGroup(group); g = groups.GetCount() - 1; $uvs: END REPOSITORY INSERT $uvs: REPOSITORY DELETE TabBar& TabBar::Crosses(bool b) { crosses = b; Repos(); return *this; } TabBar& TabBar::Grouping(bool b) { grouping = b; return *this; } TabBar& TabBar::AutoScrollHide(bool b) { autoscrollhide = b; SyncScrollBar(GetWidth()); return *this; } TabBar& TabBar::InactiveShadow(bool b) { inactiveshadow = b; if (b) Repos(); return *this; } void TabBar::FrameSet() { int al = GetAlign(); Ctrl::ClearFrames(); sc.Clear(); sc.SetFrameSize(QT_SBHEIGHT).SetAlign((al >= 2) ? al - 2 : al + 2); sc <<= THISBACK(Scroll); if (!sc.IsChild()) AddFrame(sc); style[0] = &StyleLeft(); style[1] = &StyleDefault(); style[2] = &StyleRight(); style[3] = &StyleBottom(); SyncScrollBar(0); } void TabBar::ResetStyles() { StyleLeft(); StyleRight(); StyleBottom(); } void TabBar::FrameLayout(Rect& r) { if (autoscrollhide && tabs.GetCount()) SyncScrollBar(-1); AlignedFrame::FrameLayout(r); } int TabBar::Find(const Value &v) const { for (int i = 0; i < tabs.GetCount(); i++) if (tabs[i].data == v) return i; return -1; } void TabBar::Set(int n, const Value &data, const String &group) { ASSERT(n >= 0 && n < tabs.GetCount()); tabs[n].data = data; if (!IsNull(data)) SetTabGroup(n, group); else Repos(); } void TabBar::LeftDown(Point p, dword keyflags) { if(keyflags & K_SHIFT) { highlight = -1; Refresh(); SetCapture(); Fix(p); oldp = p; return; } isctrl = keyflags & K_CTRL; if(isctrl) return; if(cross != -1) { Value v = tabs[cross].data; Close(cross); WhenClose(v); } else if(highlight >= 0) { SetCursor(highlight); WhenCursor(); } } void TabBar::LeftUp(Point p, dword keyflags) { ReleaseCapture(); } void TabBar::RightDown(Point p, dword keyflags) { MenuBar::Execute(THISBACK(ContextMenu), GetMousePos()); } void TabBar::MiddleDown(Point p, dword keyflags) { Close(highlight); } void TabBar::MiddleUp(Point p, dword keyflags) { } int TabBar::GetTargetTab(Point p) { p.x += sc.GetPos(); int f = GetFirst(); int l = GetLast(); 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); // if(tabs[i].visible && p.x >= tabs[i].x && p.x <= tabs[i].x + tabs[i].cx) // return i; return -1; } void TabBar::MouseWheel(Point p, int zdelta, dword keyflags) { sc.AddPos(-zdelta / 4, true); Scroll(); MouseMove(p, 0); } void TabBar::MouseMove(Point p, dword keyflags) { Fix(p); if(HasCapture()) { sc.AddPos(p.x - oldp.x, true); oldp = p; return; } p.x += sc.GetPos() - style[1]->margin; Size sz = GetSize(); Fix(sz); int h = highlight; bool iscross = false; bool istab = false; for(int i = 0; i < tabs.GetCount(); i++) { Point np(p.x + (i > 0 ? style[1]->extendleft : 0), p.y); if(tabs[i].HasMouse(np)) { istab = true; int h = sz.cy + (active == i ? 0 : style[1]->sel.top); iscross = crosses ? tabs[i].HasMouseCross(np, h, GetAlign()) : false; if(highlight != i || (iscross && cross != i)) { cross = iscross ? i : -1; highlight = i; WhenHighlight(); Refresh(); return; } $uvs: END REPOSITORY DELETE } } $uvs: REPOSITORY INSERT t.group = groups[g].name; $uvs: END REPOSITORY INSERT $uvs: REPOSITORY DELETE if(!istab && h >= 0) { highlight = -1; WhenHighlight(); Refresh(); } if(!iscross && cross >= 0) { cross = -1; Refresh(); } } void TabBar::MouseLeave() { if(isdrag) return; highlight = -1; cross = -1; Refresh(); } void TabBar::DragAndDrop(Point p, PasteClip& d) { Fix(p); int c = GetTargetTab(p); int tab = isctrl ? highlight : active; if (&GetInternal(d) != this) return; bool sametab = c == tab || c == tab + 1; bool internal = AcceptInternal(d, "tabs"); 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 = FindId(id); isdrag = false; MakeGroups(); Repos(); 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 TabBar::CancelMode() { isdrag = false; dragimg.Clear(); target = -1; Refresh(); } void TabBar::LeftDrag(Point p, dword keyflags) { if(keyflags & K_SHIFT) return; if(highlight < 0) return; isdrag = true; DoDragAndDrop(InternalClip(*this, "tabs")); } void TabBar::DragEnter() { } void TabBar::DragLeave() { target = -1; dragimg.Clear(); Refresh(); } void TabBar::DragRepeat(Point p) { if(target >= 0) { int dx = GetDragScroll(this, p, 16).x; if(dx != 0) sc.AddPos(dx); } } void TabBar::SetCursor(int n) { if(tabs.GetCount() == 0) return; if(n < 0) { n = max(0, FindId(GetGroupActive())); active = -1; } bool is_all = IsGroupAll(); bool same_group = tabs[n].group == GetGroupName(); if((same_group || is_all) && active == n) return; active = n; if(!is_all && !same_group) { SetGroup(tabs[n].group); Repos(); } SetGroupActive(tabs[n].id); int cx = tabs[n].x - sc.GetPos(); if(cx < 0) sc.AddPos(cx - 10); else { Size sz = Ctrl::GetSize(); Fix(sz); cx = tabs[n].x + tabs[n].cx - sz.cx - sc.GetPos(); if(cx > 0) sc.AddPos(cx + 10); } if(HasMouse()) MouseMove(GetMouseViewPos(), 0); UpdateActionRefresh(); } void TabBar::SetTabGroup(int n, const String &group) { ASSERT(n >= 0 && n < tabs.GetCount()); int g = FindGroup(group); if (g <= 0) NewGroup(group); else if (groups[g].active == tabs[n].id) SetGroupActive(tabs[n].id); tabs[n].group = group; $uvs: END REPOSITORY DELETE MakeGroups(); Repos(); $uvs: REPOSITORY INSERT active = -1; SetCursor(minmax(ix, 0, cnt)); return *this; } int TabBar::GetWidth()const { if (!tabs.GetCount()) return 0; int j = GetLast(); return tabs[GetLast()].Right() + style[GetAlign()]->margin * 2; } int TabBar::GetStyleHeight(const Style &s) { return s.tabheight + s.sel.top /*+ style->sel.bottom */ + QT_SBSEPARATOR; } void TabBar::Repos() { if(!tabs.GetCount()) return; String g = GetGroupName(); int j; bool first = true; j = 0; for(int i = 0; i < tabs.GetCount(); i++) j = TabPos(g, first, i, j, false); if (inactiveshadow) for(int i = 0; i < tabs.GetCount(); i++) if (!tabs[i].visible) j = TabPos(g, first, i, j, true); SyncScrollBar(GetWidth()); } int TabBar::TabPos(const String &g, bool &first, int i, int j, bool inactive) { bool v = IsNull(g) ? true : g == tabs[i].group; if(v) { tabs[i].x = first ? 0 : tabs[j].Right(); j = i; first = false; $uvs: END REPOSITORY INSERT $uvs: REPOSITORY DELETE } void TabBar::Close(int n) { if(tabs.GetCount() <= neverempty) return; if(n == active) { int c = FindId(tabs[n].id); int nc = GetNext(c); if(nc < 0) nc = max(0, GetPrev(c)); SetGroupActive(tabs[nc].id); } sc.AddTotal(-tabs[n].cx); tabs.Remove(n); MakeGroups(); Repos(); SetCursor(-1); } const TabBar::Style& TabBar::AlignStyle(int align, Style &s) { Size sz(30, GetStyleHeight(s)); for (int i = 0; i < 4; i++) s.first[i] = AlignValue(align, s.first[i], sz); for (int i = 0; i < 4; i++) s.last[i] = AlignValue(align, s.last[i], sz); for (int i = 0; i < 4; i++) s.normal[i] = AlignValue(align, s.normal[i], sz); for (int i = 0; i < 4; i++) s.both[i] = AlignValue(align, s.both[i], sz); return s; } const TabBar::Style& TabBar::StyleLeft() { leftstyle = StyleDefault(); return AlignStyle(AlignedFrame::LEFT, leftstyle); } const TabBar::Style& TabBar::StyleRight() { rightstyle = StyleDefault(); return AlignStyle(AlignedFrame::RIGHT, rightstyle); } const TabBar::Style& TabBar::StyleBottom() { bottomstyle = StyleDefault(); return AlignStyle(AlignedFrame::BOTTOM, bottomstyle); } void TabBar::Serialize(Stream& s) { int version = 0x00; int cnt; if (s.IsLoading()) Clear(); s / version / group / highlight / active; if (s.IsLoading()) { cnt = groups.GetCount(); s / cnt; for (int i = 1; i < groups.GetCount(); i++) { Group &g = groups[i]; s % g.data / g.active; } cnt = tabs.GetCount(); s / cnt; for (int i = 0; i < tabs.GetCount(); i++) { Tab &t = tabs[i]; int g = FindGroup(t.group); s % t.data / t.id / g; } $uvs: END REPOSITORY DELETE } else { $uvs: REPOSITORY INSERT tabs[i].x = 0; if (inactive) { tabs[i].x = tabs[j].Right(); return (j = i); } } tabs[i].visible = v; tabs[i].y = 0; tabs[i].cx = GetWidth(i); tabs[i].cy = style[1]->tabheight + style[1]->sel.top; return j; } void TabBar::SyncScrollBar(int total) { if (total > 0) sc.SetTotal(total); if (autoscrollhide) { int h = GetHeight(); bool v = GetFrameSize() > h; bool nv = sc.IsScrollable(); sc.Show(nv); if (v != nv) SetFrameSize((nv ? sc.GetFrameSize() : 0) + h, false); } else sc.Show(); } int TabBar::FindId(int id) const { for(int i = 0; i < tabs.GetCount(); i++) if(tabs[i].id == id) return i; return -1; } int TabBar::GetNext(int n) const { for(int i = n + 1; i < tabs.GetCount(); i++) if(tabs[i].visible) return i; return -1; } int TabBar::GetPrev(int n) const { for(int i = n - 1; i >= 0; i--) if(tabs[i].visible) return i; return -1; } void TabBar::Clear() { highlight = -1; active = -1; target = -1; id = -1; tabs.Clear(); groups.Clear(); NewGroup("All"); group = 0; Refresh(); } TabBar& TabBar::Crosses(bool b) { crosses = b; Repos(); return *this; } TabBar& TabBar::Grouping(bool b) { grouping = b; return *this; } TabBar& TabBar::AutoScrollHide(bool b) { autoscrollhide = b; SyncScrollBar(GetWidth()); return *this; } TabBar& TabBar::InactiveShadow(bool b) { inactiveshadow = b; if (b) Repos(); return *this; } void TabBar::FrameSet() { int al = GetAlign(); Ctrl::ClearFrames(); sc.Clear(); sc.SetFrameSize(QT_SBHEIGHT).SetAlign((al >= 2) ? al - 2 : al + 2); sc <<= THISBACK(Scroll); if (!sc.IsChild()) AddFrame(sc); style[0] = &StyleLeft(); style[1] = &StyleDefault(); style[2] = &StyleRight(); style[3] = &StyleBottom(); SyncScrollBar(0); } void TabBar::ResetStyles() { StyleLeft(); StyleRight(); StyleBottom(); } void TabBar::FrameLayout(Rect& r) { if (autoscrollhide && tabs.GetCount()) SyncScrollBar(-1); AlignedFrame::FrameLayout(r); } int TabBar::Find(const Value &v) const { for (int i = 0; i < tabs.GetCount(); i++) if (tabs[i].data == v) return i; return -1; } void TabBar::Set(int n, const Value &data, const String &group) { ASSERT(n >= 0 && n < tabs.GetCount()); tabs[n].data = data; if (!IsNull(data)) SetTabGroup(n, group); else Repos(); } void TabBar::LeftDown(Point p, dword keyflags) { if(keyflags & K_SHIFT) { highlight = -1; Refresh(); SetCapture(); Fix(p); oldp = p; return; } isctrl = keyflags & K_CTRL; if(isctrl) return; if(cross != -1) { Value v = tabs[cross].data; Close(cross); WhenClose(v); } else if(highlight >= 0) { SetCursor(highlight); WhenCursor(); $uvs: END REPOSITORY INSERT $uvs: REPOSITORY DELETE s / cnt; groups.SetCount(cnt); for (int i = 1; i < cnt; i++) { Group &g = groups[i]; s % g.data / g.active; } s / cnt; tabs.SetCount(cnt); for (int i = 0; i < tabs.GetCount(); i++) { int g; Tab &t = tabs[i]; s % t.data / t.id / g; t.group = groups[g].name; } MakeGroups(); Repos(); $uvs: END REPOSITORY DELETE } } $uvs: REPOSITORY INSERT void TabBar::LeftUp(Point p, dword keyflags) { ReleaseCapture(); } void TabBar::RightDown(Point p, dword keyflags) { MenuBar::Execute(THISBACK(ContextMenu), GetMousePos()); } void TabBar::MiddleDown(Point p, dword keyflags) { Close(highlight); } void TabBar::MiddleUp(Point p, dword keyflags) { } int TabBar::GetTargetTab(Point p) { p.x += sc.GetPos(); int f = GetFirst(); int l = GetLast(); 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); // if(tabs[i].visible && p.x >= tabs[i].x && p.x <= tabs[i].x + tabs[i].cx) // return i; return -1; } void TabBar::MouseWheel(Point p, int zdelta, dword keyflags) { sc.AddPos(-zdelta / 4, true); Scroll(); MouseMove(p, 0); } void TabBar::MouseMove(Point p, dword keyflags) { Fix(p); if(HasCapture()) { sc.AddPos(p.x - oldp.x, true); oldp = p; return; } p.x += sc.GetPos() - style[1]->margin; Size sz = GetSize(); Fix(sz); int h = highlight; bool iscross = false; bool istab = false; for(int i = 0; i < tabs.GetCount(); i++) { Point np(p.x + (i > 0 ? style[1]->extendleft : 0), p.y); if(tabs[i].HasMouse(np)) { istab = true; int h = sz.cy + (active == i ? 0 : style[1]->sel.top); iscross = crosses ? tabs[i].HasMouseCross(np, h, GetAlign()) : false; if(highlight != i || (iscross && cross != i)) { cross = iscross ? i : -1; highlight = i; WhenHighlight(); Refresh(); return; } } } if(!istab && h >= 0) { highlight = -1; WhenHighlight(); Refresh(); } if(!iscross && cross >= 0) { cross = -1; Refresh(); } } void TabBar::MouseLeave() { if(isdrag) return; highlight = -1; cross = -1; Refresh(); } void TabBar::DragAndDrop(Point p, PasteClip& d) { Fix(p); int c = GetTargetTab(p); int tab = isctrl ? highlight : active; if (&GetInternal(d) != this) return; bool sametab = c == tab || c == tab + 1; bool internal = AcceptInternal(d, "tabs"); 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 = FindId(id); isdrag = false; MakeGroups(); Repos(); 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 TabBar::CancelMode() { isdrag = false; dragimg.Clear(); target = -1; Refresh(); } void TabBar::LeftDrag(Point p, dword keyflags) { if(keyflags & K_SHIFT) return; if(highlight < 0) return; isdrag = true; DoDragAndDrop(InternalClip(*this, "tabs")); } void TabBar::DragEnter() { } void TabBar::DragLeave() { target = -1; dragimg.Clear(); Refresh(); } void TabBar::DragRepeat(Point p) { if(target >= 0) { int dx = GetDragScroll(this, p, 16).x; if(dx != 0) sc.AddPos(dx); } } void TabBar::SetCursor(int n) { if(tabs.GetCount() == 0) return; if(n < 0) { n = max(0, FindId(GetGroupActive())); active = -1; } bool is_all = IsGroupAll(); bool same_group = tabs[n].group == GetGroupName(); if((same_group || is_all) && active == n) return; active = n; if(!is_all && !same_group) { SetGroup(tabs[n].group); Repos(); } SetGroupActive(tabs[n].id); int cx = tabs[n].x - sc.GetPos(); if(cx < 0) sc.AddPos(cx - 10); else { Size sz = Ctrl::GetSize(); Fix(sz); cx = tabs[n].x + tabs[n].cx - sz.cx - sc.GetPos(); if(cx > 0) sc.AddPos(cx + 10); } if(HasMouse()) MouseMove(GetMouseViewPos(), 0); UpdateActionRefresh(); } void TabBar::SetTabGroup(int n, const String &group) { ASSERT(n >= 0 && n < tabs.GetCount()); int g = FindGroup(group); if (g <= 0) NewGroup(group); else if (groups[g].active == tabs[n].id) SetGroupActive(tabs[n].id); tabs[n].group = group; MakeGroups(); Repos(); } void TabBar::Close(int n) { if(tabs.GetCount() <= neverempty) return; if(n == active) { int c = FindId(tabs[n].id); int nc = GetNext(c); if(nc < 0) nc = max(0, GetPrev(c)); SetGroupActive(tabs[nc].id); } sc.AddTotal(-tabs[n].cx); tabs.Remove(n); MakeGroups(); Repos(); SetCursor(-1); } const TabBar::Style& TabBar::AlignStyle(int align, Style &s) { Size sz(30, GetStyleHeight(s)); for (int i = 0; i < 4; i++) s.first[i] = AlignValue(align, s.first[i], sz); for (int i = 0; i < 4; i++) s.last[i] = AlignValue(align, s.last[i], sz); for (int i = 0; i < 4; i++) s.normal[i] = AlignValue(align, s.normal[i], sz); for (int i = 0; i < 4; i++) s.both[i] = AlignValue(align, s.both[i], sz); return s; } const TabBar::Style& TabBar::StyleLeft() { leftstyle = StyleDefault(); return AlignStyle(AlignedFrame::LEFT, leftstyle); } const TabBar::Style& TabBar::StyleRight() { rightstyle = StyleDefault(); return AlignStyle(AlignedFrame::RIGHT, rightstyle); } const TabBar::Style& TabBar::StyleBottom() { bottomstyle = StyleDefault(); return AlignStyle(AlignedFrame::BOTTOM, bottomstyle); } void TabBar::Serialize(Stream& s) { int version = 0x00; int cnt; if (s.IsLoading()) Clear(); s / version / group / highlight / active; if (s.IsLoading()) { cnt = groups.GetCount(); s / cnt; for (int i = 1; i < groups.GetCount(); i++) { Group &g = groups[i]; s % g.data / g.active; } cnt = tabs.GetCount(); s / cnt; for (int i = 0; i < tabs.GetCount(); i++) { Tab &t = tabs[i]; int g = FindGroup(t.group); s % t.data / t.id / g; } } else { s / cnt; groups.SetCount(cnt); for (int i = 1; i < cnt; i++) { Group &g = groups[i]; s % g.data / g.active; } s / cnt; tabs.SetCount(cnt); for (int i = 0; i < tabs.GetCount(); i++) { int g; Tab &t = tabs[i]; s % t.data / t.id / g; t.group = groups[g].name; } MakeGroups(); Repos(); } } $uvs: END REPOSITORY INSERT