ultimatepp/bazaar/TabBar/TabBar.cpp
mrjt b8931c0a16 Fixed various -Wall warnings. Missing returns, unused variables etc.
git-svn-id: svn://ultimatepp.org/upp/trunk@826 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2009-01-30 09:54:19 +00:00

1316 lines
26 KiB
C++

#include "TabBar.h"
#define IMAGECLASS TabBarImg
#define IMAGEFILE <TabBar/TabBar.iml>
#include <Draw/iml_source.h>
// 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;
IsVert() ? sz.cx += framesize : sz.cy += framesize;
}
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<Ctrl>::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;
sz = Size(0, 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
{
// Note: sz already 'fixed'
return total > sz.cx && sz.cx > 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;
inactivedisabled = false;
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();
MakeGroups();
Repos();
SetCursor(0);
WhenCloseAll();
}
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 others", 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 >= real_pos.x && p.x < real_pos.x + real_size.cx &&
p.y >= real_pos.y && p.y < real_pos.y + real_size.cy;
}
bool TabBar::Tab::HasMouseCross(const Point& p) const
{
if(!visible)
return false;
return p.x >= cross_pos.x && p.x < cross_pos.x + cross_size.cx &&
p.y >= cross_pos.y && p.y < cross_pos.y + cross_size.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();
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<Image>(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<Color>(v)) {
ImageDraw w(isz.cx, isz.cy);
w.DrawRect(isz, SColorFace());
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(isz.cy / 2, 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::PaintTabData(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;
if(IsType<AttrText>(q)) {
const AttrText& t = ValueTo<AttrText>(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, GetTextAngle(), txt, f, i);
}
void TabBar::PaintTab(Draw &w, const Style &s, const Size &sz, int n, bool enable, bool dragsample)
{
TabBar::Tab &t = tabs[n];
int cnt = dragsample ? 1 : 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;
int c = align == LEFT ? cnt - n : n;
const Value& sv = (cnt == 1 ? s.both : c == 0 ? s.first : c == cnt - 1 ? s.last : s.normal)[ndx];
int lx = n > 0 ? s.extendleft : 0;
int x = t.pos.x - sc.GetPos() + s.margin - lx;
if (ac) {
p = Point(x - s.sel.left, 0);
tsz = Size(t.size.cx + lx + s.sel.right + s.sel.left, t.size.cy + s.sel.bottom);
}
else {
p = Point(x, s.sel.top);
tsz = Size(t.size.cx + lx, t.size.cy - s.sel.top);
}
if (align == BOTTOM) {
p.y -= s.sel.top - TB_SBSEPARATOR;
}
if (align == RIGHT) {
p.y -= s.sel.top - TB_SBSEPARATOR;
}
t.real_pos = p;
t.real_size = tsz;
Fix(t.real_pos);
Fix(t.real_size);
ChPaint(w, t.real_pos.x, t.real_pos.y, t.real_size.cx, t.real_size.cy, sv);
if(crosses && cnt > neverempty) {
Point cp;
const Image &cimg = TabBarImg::CR0();
// Use style? - yes it should be styled, but win32 dosn't support crosses on tabs,
// maybe we should use some generic cross with some magic heuristic styling..
isz = cimg.GetSize();
Fix(isz);
if (align == LEFT)
cp.x = p.x + TB_MARGIN;
else
cp.x = p.x + tsz.cx - isz.cx - TB_MARGIN;
cp.y = p.y + (tsz.cy - isz.cy) / 2;
Fix(cp);
t.cross_pos = cp;
t.cross_size = isz;
w.DrawImage(cp.x, cp.y, (ac || hl) ? (cross == n ? TabBarImg::CR2 : ac ? TabBarImg::CR1 : TabBarImg::CR0) : TabBarImg::CR0);
isz.cx += 2;
}
p.x += TB_MARGIN;
tsz.cx -= TB_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;
break;
}
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 {
PaintTabData(w, p, tsz,
t.data, s.font,
s.text_color[ndx], ndx);
}
}
void TabBar::Paint(Draw &w)
{
int align = GetAlign();
const Style &st = *style[align];
Size sz = GetSize();
w.DrawRect(sz, SColorFace());
IsVert() ?
w.DrawRect(align == LEFT ? sz.cx - 1 : 0, 0, 1, sz.cy, Color(128, 128, 128)):
w.DrawRect(0, align == TOP ? sz.cy - 1 : 0, sz.cx, 1, Color(128, 128, 128));
if (!tabs.GetCount()) return;
int limt = sc.GetPos() + (IsVert() ? sz.cy : sz.cx);
int first = 0;
int last = tabs.GetCount()-1;
// Find first visible tab
for(int i = 0; i < tabs.GetCount(); i++) {
Tab &tab = tabs[i];
if (tab.pos.x + tab.size.cx > sc.GetPos()) {
first = i;
break;
}
}
// Find last visible tab
for(int i = first + 1; i < tabs.GetCount(); i++) {
if (tabs[i].pos.x > limt) {
last = i;
break;
}
}
// Draw active group
for (int i = first; i <= last; i++) {
if(tabs[i].visible && i != active)
PaintTab(w, st, sz, i, IsEnabled());
}
// Draw inactive groups
if (inactivedisabled)
for (int i = first; i <= last; i++) {
if(!tabs[i].visible && i != active)
PaintTab(w, st, sz, i, !IsEnabled());
}
// Draw selected tab
if(active >= first && active <= last)
PaintTab(w, st, sz, active, true);
// Draw drag highlights
if(target >= 0)
{
// Draw target marker
int drag = isctrl ? highlight : active;
if(target != drag && target != drag + 1)
{
last = GetLast();
first = GetFirst();
int x = (target == last + 1 ? tabs[last].Right() : tabs[target].pos.x)
- sc.GetPos() - (target <= first ? 1 : 2)
+ st.margin - (target > 0 ? st.extendleft : 0);
int y = st.sel.top;
int cy = sz.cy - y;
if (IsHorz()) {
w.DrawRect(x + 1, y, 2, cy, SRed());
w.DrawRect(x, y, 4, 1, SRed());
w.DrawRect(x, y + cy - 1, 4, 1, SRed());
}
else{
w.DrawRect(y, x + 1, cy, 2, SRed());
w.DrawRect(y, x, 1, 4, SRed());
w.DrawRect(y + cy - 1, x, 1, 4, SRed());
}
}
// Draw transparent drag image
Point mouse = GetMousePos() - GetScreenRect().TopLeft();
Size isz = dragtab.GetSize();
if (IsHorz())
w.DrawImage(mouse.x - isz.cx/2, 0, isz.cx, isz.cy, dragtab);
else
w.DrawImage(0, mouse.y - isz.cy/2, isz.cx, isz.cy, dragtab);
}
}
Image TabBar::GetDragSample()
{
return GetDragSample(highlight);
}
Image TabBar::GetDragSample(int n)
{
if (n < 0) return Image();
Tab &tab = tabs[n];
const Style& st = *style[GetAlign()];
Size tsz(tab.real_size);
ImageDraw iw(tsz);
iw.DrawRect(tsz, SColorFace()); //this need to be fixed - if inactive tab is dragged gray edges are visible
Point temp = tab.pos;
tab.pos.x = sc.GetPos();
tab.pos.y = 0;
PaintTab(iw, st, GetSize(), n, true, true);
tab.pos.x = temp.x;
tab.pos.y = temp.y;
Image img = iw;
ImageBuffer ib(img);
Unmultiply(ib);
RGBA *s = ~ib;
RGBA *e = s + ib.GetLength();
while(s < e) {
s->a = 180;
s++;
}
Premultiply(ib);
return ib;
}
void TabBar::Scroll()
{
Refresh();
}
int TabBar::GetWidth(int n)
{
Tab &t = tabs[n];
Size tsz = GetStdSize(t.data);
return TB_MARGIN * 2 + tsz.cx + (TB_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;
return tabs[GetLast()].Right() + style[GetAlign()]->margin * 2;
}
int TabBar::GetHeight() const
{
return TabBar::GetStyleHeight(*style[GetAlign()]) + TB_SBSEPARATOR;
}
int TabBar::GetStyleHeight(const Style &s)
{
return s.tabheight + s.sel.top;
}
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 (inactivedisabled)
for(int i = 0; i < tabs.GetCount(); i++)
if (!tabs[i].visible)
j = TabPos(g, first, i, j, true);
SyncScrollBar();
}
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].pos.x = first ? 0 : tabs[j].Right();
j = i;
first = false;
}
else {
tabs[i].pos.x = 0;
if (inactive) {
tabs[i].pos.x = tabs[j].Right();
return (j = i);
}
}
tabs[i].visible = v;
tabs[i].pos.y = 0;
tabs[i].size.cx = GetWidth(i);
tabs[i].size.cy = GetStyleHeight(*style[1]);
return j;
}
void TabBar::SyncScrollBar(bool synctotal)
{
if (synctotal)
sc.SetTotal(GetWidth());
if (autoscrollhide) {
bool v = sc.IsScrollable();
if (sc.IsShown() != v) {
SetFrameSize((v ? sc.GetFrameSize() : 0) + GetHeight(), false);
sc.Show(v);
PostCallback(THISBACK(RefreshParentLayout));
}
}
else {
SetFrameSize(sc.GetFrameSize() + GetHeight(), false);
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;
sc.Hide();
SetFrameSize(GetHeight(), false);
SyncScrollBar(GetWidth());
return *this;
}
TabBar& TabBar::InactiveDisabled(bool b)
{
inactivedisabled = b;
if (b) Repos();
return *this;
}
void TabBar::FrameSet()
{
int al = GetAlign();
Ctrl::ClearFrames();
sc.Clear();
sc.SetFrameSize(TB_SBHEIGHT).SetAlign((al >= 2) ? al - 2 : al + 2);
sc <<= THISBACK(Scroll);
sc.Hide();
if (!sc.IsChild())
AddFrame(sc);
style[0] = &StyleLeft();
style[1] = &StyleDefault();
style[2] = &StyleRight();
style[3] = &StyleBottom();
SyncScrollBar(GetWidth());
}
void TabBar::ResetStyles()
{
StyleLeft();
StyleRight();
StyleBottom();
}
void TabBar::FrameLayout(Rect& r)
{
AlignedFrame::FrameLayout(r);
}
void TabBar::Layout()
{
if (autoscrollhide && tabs.GetCount())
SyncScrollBar(false);
}
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].pos.x + tabs[f].size.cx / 2)
return f;
for(int i = l; i >= f; i--)
if(tabs[i].visible && p.x >= tabs[i].pos.x + tabs[i].size.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)
{
if(HasCapture())
{
Fix(p);
sc.AddPos(p.x - oldp.x, true);
oldp = p;
Refresh();
return;
}
int h = highlight;
bool iscross = false;
bool istab = false;
for(int i = 0; i < tabs.GetCount(); i++)
{
if(tabs[i].HasMouse(p))
{
istab = true;
iscross = crosses ? tabs[i].HasMouseCross(p) : 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<TabBar>(d) != this) return;
bool sametab = c == tab || c == tab + 1;
bool internal = AcceptInternal<TabBar>(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
}
target = c;
Refresh();
}
void TabBar::CancelMode()
{
isdrag = false;
target = -1;
Refresh();
}
void TabBar::LeftDrag(Point p, dword keyflags)
{
if(keyflags & K_SHIFT)
return;
if(highlight < 0)
return;
isdrag = true;
dragtab = GetDragSample();
DoDragAndDrop(InternalClip(*this, "tabs"));
}
void TabBar::DragEnter()
{
}
void TabBar::DragLeave()
{
target = -1;
Refresh();
}
void TabBar::DragRepeat(Point p)
{
if(target >= 0)
{
Point dx = GetDragScroll(this, p, 16);
Fix(dx);
if(dx.x != 0)
sc.AddPos(dx.x);
}
}
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].pos.x - sc.GetPos();
if(cx < 0)
sc.AddPos(cx - 10);
else
{
Size sz = Ctrl::GetSize();
Fix(sz);
cx = tabs[n].pos.x + tabs[n].size.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].size.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;
}
CH_STYLE(TabBar, Style, StyleDefault)
{
Assign(TabCtrl::StyleDefault());
TabBar::ResetStyles();
}
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();
}
}