ultimatepp/uppsrc/RichText/TableData.cpp
cxl 2a51384295 Core: VoidColor, CtrlLib: VoidColor in ColorPusher, RichEdit: Support for Null color in cell background
git-svn-id: svn://ultimatepp.org/upp/trunk@6587 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2013-11-19 20:07:19 +00:00

580 lines
13 KiB
C++

#include "RichText.h"
NAMESPACE_UPP
void RichTable::Invalidate()
{
cpy.page = -1;
length = tabcount = -1;
}
void RichTable::InvalidateRefresh(int i, int j)
{
if(i < format.header)
r_row = -1;
else
if(r_row != i || r_column != j)
if(r_row == -2 && clayout.sz == GetSize()) {
r_row = i;
r_column = j;
r_py = cpy;
r_pyy = clayout[min(GetRows() - 1, i + cell[i][j].vspan)].pyy;
r_page = cpage;
}
else
r_row = -1;
Invalidate();
}
void RichTable::Normalize0()
{
int nx = format.column.GetCount();
int ny = cell.GetCount();
for(int i = 0; i < ny; i++)
cell[i].SetCount(nx);
Invalidate();
r_py.page = -1;
r_row = -1;
ci.Alloc(ny);
for(int i = 0; i < ny; i++)
ci[i].Alloc(nx);
for(int i = 0; i < ny; i++)
for(int j = 0; j < nx; j++)
if(ci[i][j].valid) {
RichCell& c = cell[i][j];
int vs = i < format.header ? min(format.header - 1, i + c.vspan) : min(ny - 1, i + c.vspan);
int hs = min(nx - 1, j + c.hspan);
c.vspan = vs - i;
c.hspan = hs - j;
for(int ii = i; ii <= vs; ii++)
for(int jj = j; jj <= hs; jj++) {
if(i != ii || j != jj) {
CellInfo& f = ci[ii][jj];
f.valid = false;
cell[ii][jj].Clear();
f.master = Point(j, i);
cell[ii][jj].hspan = hs - jj;
}
}
}
}
bool RichTable::IsRowEmpty(int row)
{
for(int i = 0; i < GetColumns(); i++)
if(ci[row][i].valid && cell[row][i].vspan == 0)
return false;
return true;
}
bool RichTable::IsColumnEmpty(int column)
{
for(int i = 0; i < GetRows(); i++)
if(ci[i][column].valid)
return false;
return true;
}
void RichTable::RemoveRow0(int rowi)
{
if(rowi < format.header)
format.header--;
for(int i = 0; i < GetColumns(); i++) {
CellInfo& cf = ci[rowi][i];
if(!cf.valid && cf.master.x == i)
cell[cf.master.y][cf.master.x].vspan--;
}
cell.Remove(rowi);
}
void RichTable::RemoveColumn0(int column)
{
for(int i = 0; i < cell.GetCount(); i++) {
CellInfo& cf = ci[i][column];
if(!cf.valid && cf.master.y == i)
cell[cf.master.y][cf.master.x].hspan--;
cell[i].Remove(column);
}
format.column.Remove(column);
}
void RichTable::Normalize()
{
Normalize0();
int i = 1;
while(i < GetRows())
if(IsRowEmpty(i)) {
Array<RichCell> r(cell[i], 1);
RemoveRow0(i);
for(int j = 0; j < GetColumns(); j++)
if(ci[i][j].valid) {
cell[i][j] <<= r[j];
cell[i][j].vspan--;
}
Normalize0();
}
else
i++;
int j = 1;
while(j < GetColumns())
if(IsColumnEmpty(j)) {
Array<RichCell> r;
for(int i = 0; i < GetRows(); i++)
r.Add() <<= cell[i][j];
int c = format.column[j];
RemoveColumn0(j);
format.column[min(GetColumns() - 1, j - 1)] += c;
for(int i = 0; i < GetRows(); i++)
if(ci[i][j].valid)
cell[i][j] <<= r[i];
Normalize0();
}
else
j++;
int sum = Sum(format.column);
if(sum != 10000) {
r_row = -1;
if(format.column.GetCount()) {
if(format.column.GetCount() > 1) {
int q = 0;
for(int i = 0; i < format.column.GetCount() - 1; i++)
q += sum <= 0 ? (format.column[i] = 10000 / format.column.GetCount())
: (format.column[i] = format.column[i] * 10000 / sum);
format.column[format.column.GetCount() - 1] = 10000 - q;
}
else
format.column[0] = 10000;
}
}
}
int RichTable::GetLength() const
{
if(length < 0) {
length = 0;
for(int i = 0; i < cell.GetCount(); i++)
for(int j = 0; j < format.column.GetCount(); j++)
if(ci[i][j].valid)
length += cell[i][j].text.GetLength() + 1;
}
return length ? length - 1 : 0;
}
Point RichTable::FindCell(int& pos) const
{
for(int i = 0; i < cell.GetCount(); i++)
for(int j = 0; j < format.column.GetCount(); j++)
if(ci[i][j].valid) {
int l = cell[i][j].text.GetLength() + 1;
if(pos < l)
return Point(j, i);
pos -= l;
}
NEVER();
return Point();
}
int RichTable::GetCellPos(int ii, int jj) const
{
int pos = 0;
for(int i = 0; i < ii; i++)
for(int j = 0; j < format.column.GetCount(); j++)
if(ci[i][j].valid)
pos += cell[i][j].text.GetLength() + 1;
for(int j = 0; j < jj; j++)
if(ci[ii][j].valid)
pos += cell[ii][j].text.GetLength() + 1;
return pos;
}
Point RichTable::GetMasterCell(int i, int j) const
{
i = min(GetRows() - 1, i);
j = min(GetColumns() - 1, j);
const CellInfo& cf = ci[i][j];
return cf.valid ? Point(j, i) : cf.master;
}
const RichCell& RichTable::GetMaster(int i, int j) const
{
Point p = GetMasterCell(i, j);
return cell[p.y][p.x];
}
int RichTable::GetTableCount(int ii, int jj) const
{
int ti = 0;
for(int i = 0; i < ii; i++)
for(int j = 0; j < format.column.GetCount(); j++)
if(ci[i][j].valid)
ti += cell[i][j].text.GetTableCount();
for(int j = 0; j < jj; j++)
if(ci[ii][j].valid)
ti += cell[ii][j].text.GetTableCount();
return ti;
}
void RichTable::ClearSpelling()
{
for(int i = 0; i < cell.GetCount(); i++)
for(int j = 0; j < format.column.GetCount(); j++)
if(ci[i][j].valid)
cell[i][j].text.ClearSpelling();
}
int RichTable::GetTableCount() const
{
if(tabcount < 0) {
tabcount = 0;
for(int i = 0; i < cell.GetCount(); i++) {
for(int j = 0; j < format.column.GetCount(); j++)
if(ci[i][j].valid)
tabcount += cell[i][j].text.GetTableCount();
}
}
return tabcount;
}
void RichTable::Validate()
{
r_row = -2;
}
int RichTable::GetInvalid(PageY& top, PageY& bottom, RichContext rc) const
{
if(r_row == -2)
return -1;
const TabLayout& tab = Realize(rc);
if(r_row >= 0 && r_page == rc.page
&& r_py == rc.py && tab[min(GetRows() - 1, r_row + cell[r_row][r_column].vspan)].pyy == r_pyy) {
const PaintRow& pr = tab[r_row];
const RichCell& cl = cell[r_row][r_column];
top = pr.py;
bottom = tab[min(cell.GetCount() - 1, r_row + cl.vspan)].pyy;
return 0;
}
return 1;
}
void RichTable::AddColumn(int cx)
{
format.column.Add(cx);
}
void RichTable::SetPick(int i, int j, pick_ RichTxt& text)
{
cell.At(i).At(j).text = text;
}
RichTxt RichTable::GetPick(int i, int j)
{
return cell[i][j].text;
}
void RichTable::SetQTF(int i, int j, const char *qtf)
{
SetPick(i, j, ParseQTF(qtf));
}
void RichTable::SetFormat(int i, int j, const RichCell::Format& fmt)
{
cell.At(i).At(j).format = fmt;
}
void RichTable::SetSpan(int i, int j, int vspan, int hspan)
{
RichCell& c = cell.At(i).At(j);
c.vspan = vspan;
c.hspan = hspan;
}
Size RichTable::GetSpan(int i, int j) const
{
const RichCell& c = cell[i][j];
return Size(c.hspan, c.vspan);
}
void RichTable::SetFormat(const Format& fmt)
{
format = fmt;
}
RichTable RichTable::Copy(const Rect& sel) const
{
RichTable r;
r.format = format;
r.format.header = max(0, format.header - sel.top);
r.format.column.Remove(0, sel.left);
r.format.column.SetCount(sel.right - sel.left + 1);
for(int i = sel.top; i <= sel.bottom; i++)
for(int j = sel.left; j <= sel.right; j++)
r.cell.At(i - sel.top).At(j - sel.left) <<= cell[i][j];
r.Normalize();
return r;
}
void RichTable::RemoveRow(int rowi)
{
RemoveRow0(rowi);
Normalize();
}
void RichTable::InsertRow(int rowi, const RichStyles& style)
{
if(rowi < format.header)
format.header++;
int si;
if(rowi < GetRows()) {
for(int i = 0; i < GetColumns(); i++) {
CellInfo& cf = ci[rowi][i];
if(!cf.valid && cf.master.x == i)
cell[cf.master.y][cf.master.x].vspan++;
}
si = rowi + 1;
}
else
si = rowi - 1;
cell.Insert(rowi).SetCount(GetColumns());
if(si >= 0)
for(int i = 0; i < GetColumns(); i++) {
RichCell& c = cell[rowi][i];
const RichCell& sc = cell[si][i];
c.format = sc.format;
DUMP(sc.hspan);
c.hspan = sc.hspan;
c.ClearText(sc.text.GetFirstFormat(style), style);
}
Normalize();
}
void RichTable::RemoveColumn(int column)
{
RemoveColumn0(column);
Normalize();
}
void RichTable::InsertColumn(int column, const RichStyles& style)
{
int sci = column < GetColumns() ? column + 1 : column - 1;
for(int i = 0; i < cell.GetCount(); i++) {
CellInfo& cf = ci[i][column];
if(!cf.valid && cf.master.y == i)
cell[cf.master.y][cf.master.x].hspan++;
RichCell& c = cell[i].Insert(column);
if(sci >= 0) {
const RichCell& sc = cell[i][sci];
c.format = sc.format;
c.vspan = sc.vspan;
c.ClearText(sc.text.GetFirstFormat(style), style);
}
}
int c = format.column[min(column, GetColumns() - 1)];
format.column.Insert(column, c);
Normalize();
}
void RichTable::SplitCell(Point cl, Size sz, const RichStyles& style)
{
const RichCell& sc = cell[cl.y][cl.x];
int ext = sz.cy - cell[cl.y][cl.x].vspan - 1;
if(ext > 0) {
cell.InsertN(cl.y + 1, ext);
if(cl.y < format.header)
format.header += ext;
for(int i = 0; i < ext; i++) {
cell[cl.y + 1 + i].SetCount(GetColumns());
for(int j = 0; j < GetColumns(); j++) {
RichCell& c = cell[cl.y + 1 + i][j];
const RichCell& sc = cell[cl.y][j];
c.format = sc.format;
c.hspan = sc.hspan;
c.ClearText(sc.text.GetFirstFormat(style), style);
}
}
for(int i = 0; i < GetColumns(); i++) {
CellInfo& cf = ci[cl.y][i];
if(cf.valid)
cell[cl.y][i].vspan += ext;
else
if(cf.master.x == i)
cell[cf.master.y][cf.master.x].vspan += ext;
}
}
cell[cl.y][cl.x].vspan = 0;
if(ext < 0)
cell[cl.y + sz.cy - 1][cl.x].vspan = -ext;
for(int i = 1; i < sz.cy; i++) {
RichCell& c = cell[cl.y + i][cl.x];
c.format = sc.format;
c.hspan = sc.hspan;
c.ClearText(sc.text.GetFirstFormat(style), style);
}
Normalize0();
ext = sz.cx - cell[cl.y][cl.x].hspan - 1;
if(ext > 0) {
int clx = 0;
for(int i = 0; i <= cell[cl.y][cl.x].hspan; i++)
clx += format.column[cl.x + i];
format.column.InsertN(cl.x, ext);
int q = clx / sz.cx;
for(int i = 1; i < sz.cx; i++)
format.column[cl.x + i] = q;
format.column[cl.x] = clx - (sz.cx - 1) * q;
for(int i = 0; i < cell.GetCount(); i++) {
cell[i].InsertN(cl.x + 1, ext);
for(int q = 0; q < ext; q++) {
RichCell& c = cell[i][cl.x + 1 + q];
const RichCell& sc = cell[i][cl.x];
c.format = sc.format;
c.vspan = sc.vspan;
c.ClearText(sc.text.GetFirstFormat(style), style);
}
CellInfo& cf = ci[i][cl.x];
if(cf.valid)
cell[i][cl.x].hspan += ext;
else
if(cf.master.y == i)
cell[cf.master.y][cf.master.x].hspan += ext;
}
}
for(int i = 0; i < sz.cy; i++)
for(int j = 0; j < sz.cx; j++) {
RichCell& c = cell[cl.y + i][cl.x + j];
if(j < sz.cx - 1)
c.hspan = 0;
if(j) {
const RichCell& sc = cell[cl.y + i][cl.x];
c.format = sc.format;
c.vspan = sc.vspan;
c.ClearText(sc.text.GetFirstFormat(style), style);
}
}
Normalize();
}
void sMatchRect(Rect& t, const Rect& s)
{
if(t.left != s.left)
t.left = Null;
if(t.top != s.top)
t.top = Null;
if(t.right != s.right)
t.right = Null;
if(t.bottom != s.bottom)
t.bottom = Null;
}
RichCell::Format RichTable::GetCellFormat(const Rect& sel) const
{
RichCell::Format fmt;
for(int i = sel.top; i <= sel.bottom; i++)
for(int j = sel.left; j <= sel.right; j++)
if(i == sel.top && j == sel.left)
fmt = cell[i][j].format;
else
if(ci[i][j].valid) {
const RichCell::Format& f = cell[i][j].format;
sMatchRect(fmt.border, f.border);
sMatchRect(fmt.margin, f.margin);
if(fmt.align != f.align)
fmt.align = Null;
if(fmt.color != f.color)
fmt.color = VoidColor;
if(fmt.bordercolor != f.bordercolor)
fmt.bordercolor = VoidColor;
}
return fmt;
}
void sSetRect(Rect& t, const Rect& s)
{
if(!IsNull(s.left))
t.left = s.left;
if(!IsNull(s.top))
t.top = s.top;
if(!IsNull(s.right))
t.right = s.right;
if(!IsNull(s.bottom))
t.bottom = s.bottom;
}
void RichTable::SetCellFormat(const Rect& sel, const RichCell::Format& fmt, bool setkeep)
{
for(int i = sel.top; i <= sel.bottom; i++)
for(int j = sel.left; j <= sel.right; j++)
if(ci[i][j].valid) {
RichCell::Format& f = cell[i][j].format;
sSetRect(f.border, fmt.border);
sSetRect(f.margin, fmt.margin);
if(!IsNull(fmt.align))
f.align = fmt.align;
if(fmt.color != VoidColor)
f.color = fmt.color;
if(fmt.bordercolor != VoidColor)
f.bordercolor = fmt.bordercolor;
if(!IsNull(fmt.minheight))
f.minheight = fmt.minheight;
if(setkeep)
f.keep = fmt.keep;
}
Normalize();
}
void RichTable::Paste(Point pos, const RichTable& tab)
{
for(int i = 0; i < tab.GetRows(); i++)
for(int j = 0; j < min(GetColumns() - pos.x, tab.GetColumns()); j++)
cell.At(i + pos.y, cell[cell.GetCount() - 1]).At(j + pos.x) <<= tab[i][j];
Normalize();
}
void RichTable::AdjustSel(Rect& sel) const
{
again:
for(int i = sel.top; i <= sel.bottom; i++)
for(int j = sel.left; j <= sel.right; j++) {
Point p = GetMasterCell(i, j);
const RichCell& c = cell[p.y][p.x];
Point pp = p + Point(c.hspan, c.vspan);
if(p.x < sel.left) {
sel.left = p.x;
goto again;
}
if(pp.x > sel.right) {
sel.right = pp.x;
goto again;
}
if(p.y < sel.top) {
sel.top = p.y;
goto again;
}
if(pp.y > sel.bottom) {
sel.bottom = pp.y;
goto again;
}
}
}
RichTable::RichTable(const RichTable& src, int)
{
format = src.format;
cell <<= src.cell;
r_row = -1;
Normalize();
}
RichTable::RichTable()
{
cpy.page = -1;
r_row = -1;
Invalidate();
}
END_UPP_NAMESPACE