www.pudn.com > xtable2.zip > XTable.cpp
// XTable.cpp : implementation file
//
#include "stdafx.h"
#include "XMLTable.h"
#include "XTable.h"
#include "MemDC.h"
#define XTABLE_CLASSNAME _T("XTableCtrl") // Window class name
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// XTable
IMPLEMENT_DYNAMIC(XTable, CWnd)
XTable::XTable(int rows, int cols)
{
RegisterWindowClass ();
defaultCell.table = this;
defaultHeight = 20;
defaultWidth = 80;
cells = NULL;
rowHeight = NULL;
colWidth = NULL;
this->rows = 0;
this->cols = 0;
SetRows (rows);
SetCols (cols);
focusRow = 0;;
focusCol = 0;
}
XTable::~XTable()
{
delete [] cells;
delete [] rowHeight;
delete [] colWidth;
}
// creates the control - use like any other window create control
BOOL XTable::Create(const RECT& rect, CWnd* pParentWnd, UINT nID, DWORD dwStyle)
{
ASSERT(pParentWnd->GetSafeHwnd());
if (!CWnd::Create(XTABLE_CLASSNAME, NULL, dwStyle, rect, pParentWnd, nID))
return FALSE;
return TRUE;
}
int XTable::ToXML (CMarkup& markup)
{
markup.AddChildElem ("table");
markup.AddChildAttr("border", "1");
markup.IntoElem ();
for (int i = 0; i < GetRows (); i ++)
{
markup.AddChildElem ("tr");
markup.IntoElem();
for (int j = 0; j < GetCols (); j ++)
{
XCell* cell = GetCells (i, j);
if (cell->colSpan > 0 && cell->rowSpan > 0)
{
markup.AddChildElem ("td");
if (cell->colSpan == 1 && i == 0)
markup.AddChildAttr ("width", GetColWidth (j));
if (cell->rowSpan == 1 && j == 0)
markup.AddChildAttr ("height", GetRowHeight (i));
cell->ToXML (markup);
}
}
markup.OutOfElem();
}
markup.OutOfElem();
return 0;
}
int XTable::FromXML (CMarkup& markup)
{
markup.IntoElem ();
int maxCol = 0;
int maxRow = 0;
int row = 0;
int col = 0;
SetCols (MAX_COLS);
SetRows (MAX_ROWS);
while (markup.FindChildElem("tr"))
{
markup.IntoElem();
col = 0;
while (markup.FindChildElem("td"))
{
XCell* cell;
while (true)
{
cell = GetCells (row, col);
if (!cell)
break;
if (cell->colSpan >= 1 && cell->rowSpan >= 1)
break;
col ++;
}
cell->FromXML (markup);
if (cell->colSpan > 1 || cell->rowSpan > 1)
JoinCells (row, col, row+cell->rowSpan-1, col+cell->colSpan-1);
if (cell->colSpan == 1 && markup.GetChildAttr("width") != "")
{
int width = atoi (markup.GetChildAttr("width"));
if (row == 0 || width > GetColWidth(col))
SetColWidth (col, width);
}
if (cell->rowSpan == 1 && markup.GetChildAttr("height") != "")
{
int height = atoi (markup.GetChildAttr("height"));
if (col == 0 || height > GetRowHeight(row))
SetRowHeight (row, height);
}
col += cell->colSpan;
if (col > maxCol) maxCol = col;
if (row + cell->rowSpan > maxRow) maxRow = row + cell->rowSpan;
}
markup.OutOfElem();
row ++;
}
markup.OutOfElem();
SetRows (maxRow);
SetCols (maxCol);
return 0;
}
int XTable::SetRows(int newRows)
{
if (rows < 0 || rows > MAX_ROWS) return -1;
if (rows == newRows) return 0;
int cellsSize = cols * rows;
XCell* newCells = cells;
int* newRowHeight = new int [newRows];
if ( rows < newRows)
{
if (cellsSize < newRows * cols)
{
cellsSize *= 2;
if (cellsSize < newRows * cols)
cellsSize = newRows * cols;
newCells = new XCell [cellsSize];
//复制旧数据
for (int i = 0; i < rows; i++)
{
for(int j = 0; j < cols ; j++)
{
newCells [i * cols +j] = cells [i * cols +j];
}
newRowHeight [i] = rowHeight [i];
}
}
//初始化新数据
for (int i = rows; i < newRows; i++)
{
for (int j = 0; j < cols; j++)
{
newCells [i * cols + j] = defaultCell;
newCells [i * cols + j].SetSpan (1, 1);
}
newRowHeight [i] = defaultHeight;
}
delete [] rowHeight;
rowHeight = newRowHeight;
}
else
{
if (cellsSize > newRows * cols * 2)
{
cellsSize = newRows * cols;
newCells = new XCell [cellsSize];
}
//复制数据
for (int i = 0; i < newRows; i ++)
{
for (int j = 0; j < cols; j++)
newCells [i * cols + j] = cells[i * cols +j];
}
}
if (newCells != cells)
{
delete [] cells;
cells = newCells;
}
rows = newRows;
return 0;
}
int XTable::GetRows()
{
return rows;
}
int XTable::SetCols(int newCols)
{
if (newCols < 0 || newCols > MAX_COLS) return -1;
if (this->cols == newCols) return 0;
int cellsSize = cols * rows;
XCell* newCells = cells;
int* newColWidth = new int [newCols];
if ( cols < newCols)
{
if (cellsSize < newCols * rows)
{
cellsSize *= 2;
if (cellsSize < newCols * rows)
cellsSize = newCols * rows;
newCells = new XCell [cellsSize];
//复制旧数据
for (int i = 0; i < rows; i++)
{
for(int j = 0; j < cols ; j++)
{
newCells [i * newCols +j] = cells [i * cols +j];
newColWidth[j] = colWidth [j];
}
}
}
//初始化新数据
for (int i = 0; i < rows; i++)
{
for (int j = cols; j < newCols; j++)
{
newCells [i * newCols + j] = defaultCell;
newCells [i * newCols + j].SetSpan (1, 1);
newColWidth[j] = defaultWidth;
}
}
delete [] colWidth;
colWidth = newColWidth;
}
else
{
if (cellsSize > newCols * rows * 2)
{
cellsSize = newCols * rows;
newCells = new XCell [cellsSize];
}
//复制数据
for (int i = 0; i < rows; i ++)
{
for (int j = 0; j < newCols; j++)
newCells [i * newCols + j] = cells[i * cols +j];
}
}
if (newCells != cells)
{
delete [] cells;
cells = newCells;
}
cols = newCols;
return 0;
}
int XTable::GetCols()
{
return cols;
}
int XTable::JoinCells (int startRow, int startCol, int endRow, int endCol)
{
if (startRow < 0 || startRow >= rows) return -1;
if (endRow < 0 || startRow >= rows) return -1;
if (startCol < 0 || startCol >= cols) return -1;
if (endCol < 0 || endCol >= cols) return -1;
if (startRow > endRow || startCol > endCol) return -1;
for (int i = startRow; i <= endRow; i++)
{
for (int j = startCol; j <=endCol; j++)
{
cells [i * cols + j].SetSpan(0,0);
}
}
cells [startRow * cols + startCol].SetSpan(endRow - startRow+1, endCol - startCol+1);
return 0;
}
int XTable::UnjoinCells (int row, int col)
{
if (row < 0 || row >= this->rows) return -1;
if (col < 0 || col >= this->cols) return -1;
if (cells [row * cols + col].rowSpan <= 1 && cells [row * cols + col].colSpan <= 1 ) return -1;
for (int i = row; i <= row + cells [row * cols + col].rowSpan; i++)
{
for (int j = col; j <= cells [row * cols + col].colSpan; j++)
{
cells[i*cols+j] = defaultCell;
cells [i * cols + j].SetSpan(1,1);
}
}
return 0;
}
int XTable::SetRowHeight(int row, int height)
{
rowHeight [row] = height;
return 0;
}
int XTable::GetRowHeight(int row)
{
return rowHeight [row];
}
int XTable::SetColWidth(int col, int width)
{
colWidth [col] = width;
return 0;
}
int XTable::GetColWidth(int col)
{
return colWidth [col];
}
int XTable::GetRowsHeight(int startRow, int endRow)
{
if (startRow < 0 || startRow >= rows) return -1;
if (endRow < 0 || endRow >= rows) return -1;
if (startRow > endRow) return -1;
int height = 0;
for (int i = startRow; i <= endRow; i++)
{
height += rowHeight[i];
}
return height;
}
int XTable::GetColsWidth(int startCol, int endCol)
{
if (startCol < 0 || startCol >= cols) return -1;
if (endCol < 0 || endCol >= cols) return -1;
if (startCol > endCol) return -1;
int width = 0;
for (int i = startCol; i <= endCol; i++)
{
width += colWidth[i];
}
return width;
}
int XTable::SetCells(int row, int col, XCell& cell)
{
cells[row * cols + col] = cell;
return 0;
}
XCell* XTable::GetCells(int row, int col)
{
return &cells [row * cols + col];
}
int XTable::SetText(int row, int col, CString str)
{
XCell* cell = GetCells (row, col);
if (!cell) return -1;
return cell->SetText (str);
}
CString XTable::GetText(int row, int col)
{
return cells[row * cols + col].GetText();
}
int XTable::SetTextColor(int row, int col, COLORREF color)
{
cells[row * cols + col].SetTextColor(color);
return 0;
}
COLORREF XTable::GetTextColor(int row, int col)
{
return cells[row * cols + col].GetTextColor();
}
int XTable::SetTextFont(int row, int col, CFont& font)
{
cells[row * cols + col].SetTextFont(&font);
return 0;
}
CFont* XTable::GetTextFont(int row, int col)
{
return cells[row * cols + col].GetTextFont();
}
int XTable::SetTextFontSize(int row, int col, int size)
{
cells[row * cols + col].SetTextFontSize(size);
return 0;
}
int XTable::GetTextFontSize(int row, int col)
{
return cells[row * cols + col].GetTextFontSize();
}
int XTable::SetOverlap (int row, int col, bool enable)
{
XCell* cell = GetCells (row, col);
if (!cell) return -1;
return cell->SetOverlap (enable);
}
bool XTable::GetOverlap (int row, int col)
{
XCell* cell = GetCells (row, col);
if (!cell) return false;
return cell->GetOverlap ();
}
int XTable::SetAlignment (int row, int col, int align)
{
XCell* cell = GetCells (row, col);
if (!cell) return -1;
return cell->SetAlignment (align);
}
int XTable::GetAlignment (int row, int col)
{
XCell* cell = GetCells (row, col);
if (!cell) return -1;
return cell->GetAlignment ();
}
int XTable::SetWordbreak (int row, int col, bool wordbreak)
{
XCell* cell = GetCells (row, col);
if (!cell) return -1;
return cell->SetWordbreak (wordbreak);
}
bool XTable::GetWordbreak (int row, int col)
{
XCell* cell = GetCells (row, col);
if (!cell) return false;
return cell->GetWordbreak ();
}
int XTable::SetBackColor(int row, int col, COLORREF color)
{
cells[row * cols + col].SetBackColor(color);
return 0;
}
COLORREF XTable::GetBackColor(int row, int col)
{
return cells[row * cols + col].GetBackColor();
}
int XTable::SetBackMode(int row, int col, int mode)
{
cells[row * cols + col].SetBackMode(mode);
return 0;
}
int XTable::GetBackMode(int row, int col)
{
return cells[row * cols + col].GetBackMode();
}
RECT XTable::GetRect(int row, int col)
{
RECT rect;
int rowSpan = GetCells(row, col)->rowSpan;
int colSpan = GetCells(row, col)->colSpan;
rect.top = GetRowsHeight(0, row-1);
rect.left = GetColsWidth(0, col-1);
rect.bottom = rect.top + GetRowsHeight (row, row + rowSpan-1);
rect.right = rect.left + GetColsWidth (col, col + colSpan-1);
return rect;
}
bool XTable::HitTest (CPoint point, int& row, int& col)
{
for (int i= 0; i < rows; i++)
{
for(int j=0; j < rows; j++)
{
RECT rect = GetRect (i,j);
if (rect.top <= point.y && rect.bottom > point.y && rect.left <= point.x && rect.right > point.x)
{
row = i;
col = j;
return true;
}
}
}
return false;
}
void XTable::OnDraw(CDC* pDC)
{
static bool lock = false;
if (lock) return;
if (!::IsWindow(m_hWnd)) return;
lock = true;
Draw (pDC);
lock = false;
}
int XTable::Draw(CDC* pDC)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
XCell& cell = cells[i*cols+j];
if (cell.colSpan !=0 && cell.rowSpan != 0)
{
RECT rect = GetRect(i,j);
if (cell.GetOverlap())
{
RECT textRect = GetRect(i,j);
cell.CalcTextRect(pDC, &textRect);
if (textRect.right > rect.right)
{
for (j = j+1; j < cols; j ++)
{
if (cells[i*cols+j].text != "")
break;
rect.right += colWidth [j];
if (rect.right > textRect.right)
break;
}
j --;
}
}
cell.Draw(pDC,rect);
}
}
}
if (focusRow < rows && focusCol < cols) //**
{
RECT rect = GetRect (focusRow, focusCol);
GetCells (focusRow, focusCol)->DrawHitBorder(pDC, rect, RGB(255, 0, 20));
}
return 0;
}
BEGIN_MESSAGE_MAP(XTable, CWnd)
ON_WM_PAINT()
ON_WM_KEYDOWN()
ON_WM_LBUTTONDOWN()
ON_WM_ERASEBKGND()
END_MESSAGE_MAP()
// XTable message handlers
void XTable::OnPaint()
{
CPaintDC dc(this); // device context for painting
CMemDC MemDC(&dc);
OnDraw(&MemDC);
}
BOOL XTable::OnEraseBkgnd(CDC* pDC)
{
return TRUE;
}
void XTable::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
CPoint point;
RECT rect = GetRect(focusRow,focusCol);
point.x = rect.left;
point.y = rect.top;
switch (nChar)
{
case VK_DOWN:
point.y =rect.bottom + 1;
break;
case VK_UP:
point.y =point.y -2;
break;
case VK_LEFT:
point.x = point.x - 2;
break;
case VK_RIGHT:
point.x =rect.right + 1;
break;
default:
return ;
}
HitTest (point,focusRow,focusCol);
Invalidate();
CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
}
void XTable::OnLButtonDown(UINT nFlags, CPoint point)
{
HitTest (point,focusRow,focusCol);
SetFocus ();
Invalidate();
CWnd::OnLButtonDown(nFlags, point);
}
BOOL XTable::RegisterWindowClass()
{
WNDCLASS wndcls;
HINSTANCE hInst = AfxGetInstanceHandle();
if (!(::GetClassInfo(hInst, XTABLE_CLASSNAME, &wndcls)))
{
// otherwise we need to register a new class
wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
wndcls.lpfnWndProc = ::DefWindowProc;
wndcls.cbClsExtra = wndcls.cbWndExtra = 0;
wndcls.hInstance = hInst;
wndcls.hIcon = NULL;
wndcls.hCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
wndcls.hbrBackground = (HBRUSH) (COLOR_3DFACE + 1);
wndcls.lpszMenuName = NULL;
wndcls.lpszClassName = XTABLE_CLASSNAME;
if (!AfxRegisterClass(&wndcls))
{
AfxThrowResourceException();
return FALSE;
}
}
return TRUE;
}
int XTable::Test()
{
defaultHeight = 20;
defaultWidth = 80;
SetRows (46);
SetCols (20);
for (int i = 0; i < 46; i ++)
SetBackColor (i, 0, RGB(0xC8, 0x96, 0x96));
SetRowHeight (0, 10);
SetRowHeight (2, 10);
SetRowHeight (7, 10);
SetRowHeight (9, 10);
SetRowHeight (13, 10);
SetRowHeight (15, 10);
SetRowHeight (19, 10);
SetRowHeight (21, 10);
SetRowHeight (24, 10);
SetRowHeight (28, 10);
SetRowHeight (31, 10);
SetRowHeight (34, 10);
for (int i = 0; i < 9; i ++)
SetBackColor (0, i, RGB(0xC8, 0x96, 0x96));
SetColWidth (0, 10);
JoinCells (1,1,1,8);
SetBackColor (1,1, RGB(0x64,0x96,0xC8));
SetText (1,1, "Default edit control respects cell's celltype, font size and type");
JoinCells (3,1,3,2);
SetText (3,1, "Using default font");
SetText (3,3, "Enter text");
JoinCells (3,5,5,6);
SetText (3,5, "This cell contains multiline text, which provides multiline edit.");
SetWordbreak (3,5,true);
JoinCells (3,7,5,8);
SetText (3,7, "This cell contains multiline text, which provides multiline edit.");
SetWordbreak (3,7,true);
SetText (4,1, "You can even edit a long (cell overlapping) string.");
SetOverlap(4,1,true);
SetText (4,3, "Test");
JoinCells (5,1,6,2);
SetText (5,1, "Enter your name here.");
JoinCells (5,3,5,4);
SetText (5,3, "Enter your text here ...");
JoinCells (8,1,8,8);
SetBackColor (8,1, RGB(0x64,0x96,0xC8));
SetText (8,1, "Default edit controls also allow us to customize user's editing experience by:");
JoinCells (10,1,10,2);
SetBackColor (10,1, RGB(0xDC,0xC8,0xB4));
SetText (10,1, "Converting input to upper case:");
SetText (10,3, "TYPE HERE ...");
JoinCells (10,5,10,6);
SetBackColor (10,5, RGB(0xDC,0xC8,0xB4));
SetText (10,5, "Converting input to lower case:");
SetText (10,7, "type here ...");
JoinCells (11,1,11,2);
SetBackColor (11,1, RGB(0xDC,0xC8,0xB4));
SetText (11,1, "Allowing password input:");
SetText (11,3, "*********");
SetText (11,5, "Entered password was:*********");
SetOverlap(11,5,true);
JoinCells (12,1,12,2);
SetBackColor (12,1, RGB(0xDC,0xC8,0xB4));
SetText (12,1, "Allowing aligned input:");
SetText (12,3, "Left aligned");
SetText (12,5, "Centered");
SetAlignment (12,5,DT_CENTER);
SetText (12,7, "Right aligned");
SetAlignment (12,7,DT_RIGHT);
JoinCells (14,1,14,8);
SetBackColor (14,1, RGB(0x64,0x96,0xC8));
SetText (14,1, "Ultimate Grid allow us to process user's input with various notifications.");
JoinCells (16,1,16,5);
SetBackColor (16,1, RGB(0xDC,0xC8,0xB4));
SetText (16,1, "We can verify data it is entered, for example to limit number of char to 4:");
SetText (16,6, "");
JoinCells (17,1,17,5);
SetBackColor (17,1, RGB(0xDC,0xC8,0xB4));
SetText (17,1, "or that the numeric value entered is under 500 (number < 500):");
SetText (17,6, "");
JoinCells (18,1,18,5);
SetBackColor (18,1, RGB(0xDC,0xC8,0xB4));
SetText (18,1, "We can also test user's input after it is completed, or force user's input:");
SetText (18,6, "");
JoinCells (20,1,20,8);
SetBackColor (20,1, RGB(0x64,0x96,0xC8));
SetText (20,1, "The Ultimate Grid also allows us to use other edit controls when needed:");
JoinCells (22,1,23,5);
SetBackColor (22,1, RGB(0xDC,0xC8,0xB4));
SetText (22,1, "Just by setting a mask on a cell, the grid will change edit controls to use a mask edit. By default Ultimate Grid creates an instance of CUGMEdit to use as mask edit control.");
SetWordbreak (22,1,true);
JoinCells (22,6,22,7);
SetText (22,6, "