// MyCell - version 1.0
// Written by Yanxueming <yanxm2003@hotmail.com>
// Copyright (C) 2006-2006
// All rights reserved.
//
// The code and information is provided "as-is" without
// warranty of any kind, either expressed or implied.
#include "stdafx.h"
#include <atltypes.h>
#include <atlgdi.h>
#include "../include/GridBase.h"
#include "../include/RgnLight.h"
#include "../include/msg.h"
namespace mycell{
void GetWndUpdateRgn(HWND hWnd, CRgnLight& rgnValue)
{
HRGN hRgn = CreateRectRgn(0, 0, 0, 0);
if (hRgn)
{
switch (GetUpdateRgn(hWnd, hRgn, FALSE))
{
case SIMPLEREGION:
case COMPLEXREGION:
rgnValue.FromGdi(hRgn);
}
DeleteObject(hRgn);
}
}
BOOL GridBase::IsRowFullVisible(int row)
{
RowHeader& rh=get_RowHeader();
int const topRow=rh.get_TopRow();
int const bottomRow=rh.get_BottomRow();
if(row<topRow || row>bottomRow)
return FALSE;
if(row==bottomRow){
RECT const rc=GetCellRect(row,HEADER_COL);
RECT rCli;
GetClientRect(&rCli);
return rc.bottom<=rCli.bottom;
}else
return TRUE;
}
BOOL GridBase::IsColFullVisible(int col)
{
ColHeader& ch=get_ColHeader();
int const leftCol=ch.get_LeftCol();
int const rightCol=ch.get_RightCol();
if(col<leftCol || col>rightCol)
return FALSE;
if(col==rightCol){
RECT const rc=GetCellRect(HEADER_COL,col);
RECT rCli;
GetClientRect(&rCli);
return rc.right<=rCli.right;
}else
return TRUE;
}
LRESULT GridBase::OnPaint(UINT,WPARAM,LPARAM,BOOL&)
{
//GetUpdateRect(&rcClip_);
AtlTrace(_T("\nGridBase::OnPaint BeginPaint"));
RecalcVisibleMergeCells();
CRgnLight rgnClip;
GetWndUpdateRgn(m_hWnd,rgnClip);
rgnClip.Optimize();
CPaintDC dc(m_hWnd);
RECT rcli;
GetClientRect(&rcli);
DrawEnvironment de;
render_.draw(dc,&rcli,&rgnClip,de);
/*
#ifdef _DEBUG
int i=0;
#endif
for (LPCRECT pRect = rgnClip.GetFirst();pRect; pRect = CRgnLight::GetNext(pRect))
{
// paint the rectangle piece of your shape
#ifdef _DEBUG
AtlTrace(_T("\nGridBase::OnPaint UpdateRect[%d](%d,%d,%d,%d)"),++i,pRect->left,pRect->top,pRect->right,pRect->bottom);
#endif
//de.lprcClip=pRect;
render_.draw(dc,&rcli,pRect,de);
}
AtlTrace(_T("\nGridBase::OnPaint EndPaint tickcount=%d"),GetTickCount());
*/
return 0;
}
int GridBase::GetCellText(int row,int col,LPTSTR buf)
{
buf[0]=0;
SendMessageToListener(row,col,OCN_GETCELLTEXT,(LPARAM)buf);
/*
RowHeader& rh=get_RowHeader();
ColHeader& ch=get_ColHeader();
TCHAR sz[100],sz1[100];
rh.get_text(row,sz);
ch.get_text(col,sz1);
wsprintf(buf,_T("%s%s"),sz1,sz);
*/
for(int i=0;i!=MAX_CELLTEXT && buf[i];++i)
;
return i;
}
// Sends a message to the listener in the form of a WM_NOTIFY message with
// a NM_GRIDVIEW structure attached
LRESULT GridBase::SendMessageToListener(int nRow, int nCol, int nNotifyCode,LPARAM lParam) const
{
HWND const hWndListener=get_listener();
if (!(hWndListener && ::IsWindow(hWndListener) && IsWindow() ))
return 0;
//_ASSERT(hWndListener!=m_hWnd);
//if(hWndListener==m_hWnd) return 1;
NM_GRIDVIEW nmgv;
nmgv.row = nRow;
nmgv.col = nCol;
nmgv.lParam = lParam;
nmgv.hdr.hwndFrom = m_hWnd;
nmgv.hdr.idFrom = GetDlgCtrlID();
nmgv.hdr.code = nNotifyCode;
return ::SendMessage(hWndListener,WM_NOTIFY,nmgv.hdr.idFrom, (LPARAM)&nmgv);
}
RECT GridBase::get_ScrollRect()
{
RECT rc;
GetClientRect(&rc);
if(get_ShowRowHeader()){
RowHeader& rh=get_RowHeader();
rc.left=rh.get_width();
}
if(get_ShowColHeader()){
ColHeader& ch=get_ColHeader();
rc.top=ch.get_height();
}
return rc;
}
int GridBase::GetScrollPos32(int nBar, BOOL bGetTrackPos)
{
SCROLLINFO si;
si.cbSize = sizeof(SCROLLINFO);
if (bGetTrackPos){
si.fMask = SIF_TRACKPOS;
if (GetScrollInfo(nBar, &si))
return si.nTrackPos;
}else {
si.fMask = SIF_POS;
if (GetScrollInfo(nBar, &si))
return si.nPos;
}
return 0;
}
void GridBase::ResetScrollBars(BOOL bResetHCrollBar,BOOL bResetVScrollBar)
{
HWND const hWnd=m_hWnd;
CRect rect;
::GetClientRect(hWnd,rect);
if(get_ShowRowHeader())
rect.left=get_RowHeader().get_width();
if(get_ShowColHeader())
rect.top=get_ColHeader().get_height();
if (rect.left >= rect.right || rect.top >= rect.bottom)
return;
SCROLLINFO si;
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
if(bResetVScrollBar)
{
si.nMin = 0;
int const heights=get_RowHeader().get_heights();
if(heights>rect.Height()){
si.nPage = rect.Height();
si.nMax = heights;
int const oldTopRow=get_RowHeader().get_TopRow();
si.nPos = get_RowHeader().get_DiffHeights(0,get_RowHeader().get_TopRow());
}else{
si.nPage = 0;
si.nPos = 0;
si.nMax = 0;
}
::SetScrollInfo(hWnd,SB_VERT, &si, TRUE);
}
if(bResetHCrollBar)
{
si.nMin = 0;
int const widths=get_ColHeader().get_widths();
if(widths>rect.Width()){
si.nPage = rect.Width();
si.nMax = widths;
si.nPos = get_ColHeader().get_DiffWidths(0,get_ColHeader().get_LeftCol());
}else{
si.nPage = 0;
si.nPos = 0;
si.nMax = 0;
}
::SetScrollInfo(hWnd,SB_HORZ, &si, TRUE);
}
}
void GridBase::EnsureVisible(int row,int col,BOOL bPartial)
{
RowHeader& rh=rowHeader_;
ColHeader& ch=colHeader_;
int const topRow=rh.get_TopRow();
int const bottomRow=rh.get_BottomRow();
int const leftCol=ch.get_LeftCol();
int const rightCol=ch.get_RightCol();
if(row<topRow){
int const diff=rh.get_DiffHeights(row,topRow);
RECT rc;
GetClientRect(&rc);
get_ContentRect(&rc);
rc.left=0;
ScrollWindow(0,diff,&rc,&rc);
int nScrollPos=GetScrollPos(SB_VERT);
SetScrollPos(SB_VERT,nScrollPos-diff);
rh.put_TopRow(row,&rc);
}else if(row>bottomRow){
RECT rc;
GetClientRect(&rc);
get_ContentRect(&rc);
rc.left=0;
int const height=rc.bottom-rc.top;
int newTopRow=row,hi=0;
for(;newTopRow!=topRow;--newTopRow){
hi+=rh.get_RowHeight(newTopRow);
if(hi>height){
break;
}
}
++newTopRow;
int const diff=rh.get_DiffHeights(topRow,newTopRow);
ScrollWindow(0,-diff,&rc,&rc);
int nScrollPos=GetScrollPos(SB_VERT);
SetScrollPos(SB_VERT,nScrollPos+diff);
rh.put_TopRow(newTopRow,&rc);
}else if(row==bottomRow && !IsRowFullVisible(row)){
SendMessage(WM_VSCROLL,SB_LINEDOWN,TRUE);
}
if(col<leftCol){
int const diff=ch.get_DiffWidths(col,leftCol);
RECT rc;
GetClientRect(&rc);
get_ContentRect(&rc);
rc.top=0;
ScrollWindow(diff,0,&rc,&rc);
int nScrollPos=GetScrollPos(SB_HORZ);
SetScrollPos(SB_HORZ,nScrollPos-diff);
ch.put_LeftCol(col,&rc);
}else if(col>rightCol){
RECT rc;
GetClientRect(&rc);
get_ContentRect(&rc);
rc.top=0;
int const width=rc.right-rc.left;
int newLeftCol=col,wi=0;
for(;newLeftCol!=leftCol;--newLeftCol){
wi+=ch.get_ColWidth(newLeftCol);
if(wi>width){
break;
}
}
++newLeftCol;
int const diff=ch.get_DiffWidths(leftCol,newLeftCol);
ScrollWindow(-diff,0,&rc,&rc);
int nScrollPos=GetScrollPos(SB_HORZ);
SetScrollPos(SB_HORZ,nScrollPos+diff);
ch.put_LeftCol(newLeftCol,&rc);
}else if(rightCol==col){
if(!IsColFullVisible(col))
SendMessage(WM_HSCROLL,SB_LINERIGHT,TRUE);
}
if(topRow!=rh.get_TopRow() || leftCol!=ch.get_LeftCol()){
RecalcVisibleMergeCells();
UpdateWindow();
}
}
void GridBase::put_RowHeight(int row,int newHeight)
{
RowHeader& rh=get_RowHeader();
int const oldTopRow=rh.get_TopRow();
int const oldBottomRow=rh.get_BottomRow();
if(row>=oldTopRow && row<=oldBottomRow){
int const oldHeight=rh.get_RowHeight(row);
int const delta=newHeight-oldHeight;
RECT rCli;GetClientRect(&rCli);
CRect const rcCell=GetCellRect(row,IGNOR_COL);
rh.put_RowHeight(row,newHeight);
RECT const rScroll=get_ScrollRect();
AtlTrace(_T("\nGrid::put_RowHeight cause bottomRow from %d"),rh.get_BottomRow());
rh.validate_BottomRow(&rScroll);
AtlTrace(_T(" to %d"),rh.get_BottomRow());
rCli.top=min(rcCell.bottom,rcCell.top+newHeight);
ScrollWindow(0,delta,&rCli,&rCli);
//UpdateWindow();
rCli.top=rcCell.top;
rCli.bottom=rcCell.top+newHeight;
InvalidateRect(&rCli,FALSE);
VisibleMergeCellMgr& vmc=GetVisibleMergeCellMgr();
vmc.InvalidRect(&rCli);
UpdateWindow();
}else
rh.put_RowHeight(row,newHeight);
ResetScrollBars(FALSE,TRUE);
}
//void Grid::ColumnAutoFit(int col)
//{
// CClientDC dc(m_hWnd);
// ColumnAutoFitPolicy afp(this,col);
// int const nWidth=GetAutoFit<ColumnAutoFitPolicy>(&afp,dc);
// if(nWidth>0){
// put_ColWidth(col,nWidth);
// }
//}
void GridBase::put_ColWidth(int col,int newWidth)
{
ColHeader& ch=get_ColHeader();
int const oldLeftCol=ch.get_LeftCol();
int const oldRightCol=ch.get_RightCol();
if(col>=oldLeftCol && col<=oldRightCol){
int const oldWidth=ch.get_ColWidth(col);
int const delta=newWidth-oldWidth;
RECT rCli;GetClientRect(&rCli);
CRect const rcCell=GetCellRect(IGNOR_ROW,col);
ch.put_ColWidth(col,newWidth);
RECT const rScroll=get_ScrollRect();
AtlTrace(_T("\nGrid::put_ColWidth cause rightCol from %d"),ch.get_RightCol());
ch.validate_RightCol(&rScroll);
AtlTrace(_T(" to %d"),ch.get_RightCol());
rCli.left=min(rcCell.right,rcCell.left+newWidth);
ScrollWindow(delta,0,&rCli,&rCli);
//UpdateWindow();
rCli.left=rcCell.left;
rCli.right=rcCell.left+newWidth;
InvalidateRect(&rCli,FALSE);
VisibleMergeCellMgr& vmc=GetVisibleMergeCellMgr();
vmc.InvalidRect(&rCli);
UpdateWindow();
}else
ch.put_ColWidth(col,newWidth);
ResetScrollBars(TRUE,FALSE);
}
void GridBase::CopyToClipboard(int y1,int x1,int y2,int x2)
{
CWaitCursor wc;
//#ifdef UNICODE
// std::wstring vecstr;
//#else
// std::string vecstr;
//#endif
//vector<wchar_t> vecstr;
//vecstr.resize((y2-y1+1)*(x2-x1+1));
std::wstring vecstr;
#ifndef UNICODE
WCHAR wtext[MAX_CELLTEXT];
#endif
TCHAR text[MAX_CELLTEXT];
_ASSERT(y1<=y2);
_ASSERT(x1<=x2);
if(y1<0)
y1=0;
if(x1<0)
x1=0;
for(;y1<=y2;++y1)
{
for(int x=x1;x<=x2;++x)
{
int const nCount=GetCellText(y1,x,text);
if(nCount>0){
#ifdef UNICODE
vecstr+=text;
#else
wtext[0]='\0';
int lRet=MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,text,nCount,wtext,MAX_CELLTEXT);
if(lRet>0){
if(lRet>=MAX_CELLTEXT)
lRet=MAX_CELLTEXT-1;
wtext[lRet]='\0';
for(int i=0;i!=lRet;++i)
{
if(wtext[i]=='\t')
wtext[i]=' ';
}
vecstr+=wtext;
}
#endif
}
vecstr+=0x09;
}
vecstr[vecstr.size()-1]=0x0D;
vecstr+=0x0A;
}
if(!vecstr.empty()){
if(::OpenClipboard(0)){
EmptyClipboard();
//#ifdef UNICODE
HGLOBAL hglb=GlobalAlloc(GMEM_MOVEABLE,(vecstr.size()+1)*sizeof(wchar_t));
wchar_t* lpstr=(wchar_t*)GlobalLock(hglb);
wcscpy(lpstr,vecstr.c_str());
GlobalUnlock(hglb);
SetClipboardData(CF_UNICODETEXT,hglb);
//#else
// HGLOBAL hglb=GlobalAlloc(GMEM_MOVEABLE,(vecstr.size()+1)*sizeof(char));
// char* lpstr=(char*)GlobalLock(hglb);
// strcpy(lpstr,vecstr.c_str());
// GlobalUnlock(hglb);
// SetClipboardData(CF_TEXT,hglb);
//#endif
CloseClipboard();
GlobalFree(hglb);
CloseClipboard();
}else{
FormatMessage(m_hWnd);
}
}
}
void GridBase::RowHeaderAutoFit()
{
CClientDC dc(m_hWnd);
CRect const rcContent=get_ContentRect();
RowHeaderAutoFitPolicy afp(this);
int nWidth=GetAutoFit<RowHeaderAutoFitPolicy>(&afp,dc,rcContent.Width()-1);
if(nWidth>0){
RowHeader& rh=get_RowHeader();
int const oldWidth=rh.get_width();
nWidth=max(nWidth,rh.get_MinWidth());
int const xDelta=nWidth-oldWidth;
if(xDelta){
rh.put_width(nWidth);
if(IsWindow()){
RECT rcContent=get_ContentRect();
rcContent.top=0;
ScrollWindow(xDelta,0,&rcContent,&rcContent);
rcContent.right=rcContent.left;
rcContent.left=0;
InvalidateRect(&rcContent,FALSE);
UpdateWindow();
}
}
}
}
BOOL GridBase::delete_row(int row)
{
BOOL bAllowDelete=TRUE;
SendMessageToListener(row,-1,OCN_DELETEROW,(LPARAM)&bAllowDelete);
if(!bAllowDelete)
return FALSE;
RECT const rcContent=get_ContentRect();
RowHeader& rh=get_RowHeader();
int const topRow=rh.get_TopRow();
int const bottomRow=rh.get_BottomRow();
int const rowHeight=rh.get_RowHeight(row);
rh.delete_row(row,&rcContent);
if(row>=topRow && row<=bottomRow){
int const newTopRow=rh.get_TopRow();
RECT rcScroll={0,rcContent.top+rh.get_DiffHeights(topRow,row),rcContent.right,rcContent.bottom};
//if(row!=bottomRow){
ScrollWindow(0,-rowHeight,&rcScroll,&rcScroll);
//}
if(topRow!=newTopRow){
rcScroll.top=rcContent.top;
int const yDelta=rh.get_DiffHeights(newTopRow,topRow);
ScrollWindow(0,yDelta,&rcScroll,&rcScroll);
ResetScrollBars(FALSE,TRUE);
}
UpdateWindow();
}else{
SCROLLINFO si;
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_RANGE;
GetScrollInfo(SB_VERT,&si);
si.nMax -= rowHeight;
SetScrollInfo(SB_VERT, &si);
}
return TRUE;
}
void GridBase::put_rows(int rows)
{
_ASSERT(rows>=0);
RowHeader& rh=get_RowHeader();
int const topRow=rh.get_TopRow();
int const bottomRow=rh.get_BottomRow();
rh.put_rows(rows);
if(IsWindow()){
RECT rcContent=get_ContentRect();
if(bottomRow>=rows)
rh.put_BottomRow(rows>0?rows-1:0,&rcContent,TRUE);
rh.validate_BottomRow(&rcContent);
rcContent.left=0;
int const newTopRow=rh.get_TopRow();
if(newTopRow==topRow){
int const newBottomRow=rh.get_BottomRow();
int const minBottomRow=min(bottomRow,newBottomRow);
rcContent.top=rcContent.top+rh.get_DiffHeights(rh.get_TopRow(),minBottomRow+1);
InvalidateRect(&rcContent,FALSE);
}else{
RECT rcScroll={0,rcContent.top,rcContent.right,rcContent.bottom};
int diff=rh.get_DiffHeights(newTopRow,topRow);
ScrollWindow(0,diff,&rcScroll,&rcScroll);
int const minBottomRow=min(bottomRow,rh.get_BottomRow());
rcContent.top=rcContent.top+rh.get_DiffHeights(rh.get_TopRow(),minBottomRow+1);
InvalidateRect(&rcContent,FALSE);
}
//Selection& s=get_Selection();
CellRange const cr=get_Selection();
//s.get_Range(cr.first.col,cr.first.row,cr.second.col,cr.second.row);
CellRange const oldSeletion=cr;
//cr.Normalize();
if(cr.first.row>rows-1){
put_Selection(cr.first.col,rows-1,cr.second.col,rows-1);
}else if(cr.second.col>rows-1)
selection_.put_Range(cr.first.col,cr.first.row,cr.second.col,rows-1);
CellID activeCell;
selection_.GetActiveCell(activeCell.row,activeCell.col);
if(activeCell.row>rows-1){
selection_.SetActiveCell(rows-1,activeCell.col);
}
selection_.InvalidateWindow(activeCell,oldSeletion);
UpdateWindow();
ResetScrollBars(FALSE,TRUE);
_ASSERT(newTopRow<rows);
}
}
void GridBase::put_cols(int cols)
{
_ASSERT(cols>=0);
ColHeader& ch=get_ColHeader();
int const leftCol=ch.get_LeftCol();
int const rightCol=ch.get_RightCol();
ch.put_cols(cols);
if(IsWindow()){
RECT rcContent=get_ContentRect();
if(rightCol>=cols)
ch.put_RightCol(cols-1,&rcContent,TRUE);
if(cols>0){
ch.validate_RightCol(&rcContent);
rcContent.top=0;
int const newLeftCol=ch.get_LeftCol();
if(newLeftCol==leftCol){
int const minRightCol=min(rightCol,ch.get_RightCol());
rcContent.left=rcContent.left+ch.get_DiffWidths(ch.get_LeftCol(),minRightCol+1);
InvalidateRect(&rcContent,FALSE);
}else{
RECT rcScroll={rcContent.left,0,rcContent.right,rcContent.bottom};
int diff=ch.get_DiffWidths(newLeftCol,leftCol);
ScrollWindow(diff,0,&rcScroll,&rcScroll);
int const minRightCol=min(rightCol,ch.get_RightCol());
rcContent.left=rcContent.left+ch.get_DiffWidths(ch.get_LeftCol(),minRightCol+1);
InvalidateRect(&rcContent,FALSE);
}
Selection& s=selection_;//get_Selection();
CellRange cr;
s.get_Range(cr.first.col,cr.first.row,cr.second.col,cr.second.row);
CellRange const oldSeletion=cr;
cr.Normalize();
if(cr.first.col>cols-1){
s.put_Range(cols-1,cr.first.row,cols-1,cr.second.row);
}else if(cr.second.col>cols-1)
s.put_Range(cr.first.col,cr.first.row,cols-1,cr.second.row);
CellID activeCell;
s.GetActiveCell(activeCell.row,activeCell.col);
if(activeCell.col>cols-1){
s.SetActiveCell(activeCell.row,cols-1);
}
s.InvalidateWindow(activeCell,oldSeletion);
_ASSERT(newLeftCol<cols);
}else{
rcContent.top=0;
InvalidateRect(&rcContent,FALSE);
}
UpdateWindow();
ResetScrollBars(TRUE,FALSE);
}
}
}//namespace mycell