I've been working on a general purpose grid control for Aurora, and possibly other languages if I package it in a DLL. Havent decided yet if I'll just give it away or perhaps charge $5 with source included.
I will post new screen shots and examples as I continue with its development.
The image below was created with this code:
sub main()
{
CGridWnd grid;
grid.Create(0,0,640,480,AWS_VISIBLE|AWS_SYSMENU|AWS_BORDER|AWS_CAPTION|AWS_SIZE,0,"Grid Test",0);
grid.SetGridSize(10,10);
for(int x=1;x<11;x++)
{
grid.SetHeaderText(1,x,NumToStr(x));
grid.SetHeaderText(0,x,ToChar('A'-1+x));
}
grid.SetCellText(1,1,"Test");
grid.SetCellText(2,1,"Test2");
grid.SetCellText(10,10,"Last Cell\nA multi line one",DT_WORD_ELLIPSIS);
grid.SetCellColor(1,1,RGB(255,0,0),RGB(255,255,255));
grid.SetCellColor(2,1,RGB(255,255,255),RGB(0,0,192));
grid.SetCellColor(10,10,0,RGB(255,207,0));
grid.SetRowHeight(10,40);
grid.SetColumnWidth(10,100);
while grid.IsValid() { wait(); }
}
This will maybe help:
http://www.ionicwind.com/forums/index.php?topic=878.0
What is this based on?
Quote from: zumwalt on October 16, 2006, 06:54:16 AM
What is this based on?
All Aurora code from scratch. Tested on 98 - XP for consistancy of drawing.
Quote from: kale
This will maybe help:
Yes I know about Sapero's efforts on converting that code. And it is a fine job. The PellesC grid needs a few more features and I didn't want to step on his code.
I also wanted a grid control that had a bit more automation, with the ability to link with the database classes, a definitive serialization format, etc.
Paul.
I am impressed.
Neat. If you decide to give it away, does that mean you would add the control to Aurora or just distribute the code?
I will be needing a grid for Aurora very soon. I was going to use Sapero's but what's the estimated date of completion for this one? No rush, I'd rather see something bug free and feature rich than rushed. ;D
Probably a week to finish the base code.
Still working on the grid. Inplace editing is done, font control is done, and boolean cells are now supported. The grid control can be used two ways. #1) By deriving a new class and overriding handlers, or #2) by using it as a normal control and using OnControl and OnNotify in the parent window.
Paul.
Great news!
Haim
Here is the current source of the grid control. I will be including it with Aurora as part of the installation, still needs some work yet.
//Aurora Grid Control class
//(c) 2006 Paul Turley
//Reproduction without permission prohibited.
#define HDS_HORZ 0x0000
#define HDS_BUTTONS 0x0002
#define HDS_HOTTRACK 0x0004
#define HDS_HIDDEN 0x0008
#define HDS_DRAGDROP 0x0040
#define HDS_FULLDRAG 0x0080
#define HDS_FILTERBAR 0x0100
#define HDS_FLAT 0x0200
#define HDM_FIRST 0x1200
#define HDM_LAYOUT (HDM_FIRST + 5)
//our own custom notification message, sized to word so we can use WM_COMMAND instead of WM_NOTIFY
#define HDN_TRACKDONE (HDN_FIRST - 99) & 0xFFFF
#define WM_COMMAND 0x0111
#define WM_NOTIFY 0x004E
#define WM_GETFONT 0x31
/* flags for DrawFrameControl */
#define DFC_CAPTION 1
#define DFC_MENU 2
#define DFC_SCROLL 3
#define DFC_BUTTON 4
#define DFC_POPUPMENU 5
#define DFCS_CAPTIONCLOSE 0x0000
#define DFCS_CAPTIONMIN 0x0001
#define DFCS_CAPTIONMAX 0x0002
#define DFCS_CAPTIONRESTORE 0x0003
#define DFCS_CAPTIONHELP 0x0004
#define DFCS_MENUARROW 0x0000
#define DFCS_MENUCHECK 0x0001
#define DFCS_MENUBULLET 0x0002
#define DFCS_MENUARROWRIGHT 0x0004
#define DFCS_SCROLLUP 0x0000
#define DFCS_SCROLLDOWN 0x0001
#define DFCS_SCROLLLEFT 0x0002
#define DFCS_SCROLLRIGHT 0x0003
#define DFCS_SCROLLCOMBOBOX 0x0005
#define DFCS_SCROLLSIZEGRIP 0x0008
#define DFCS_SCROLLSIZEGRIPRIGHT 0x0010
#define DFCS_BUTTONCHECK 0x0000
#define DFCS_BUTTONRADIOIMAGE 0x0001
#define DFCS_BUTTONRADIOMASK 0x0002
#define DFCS_BUTTONRADIO 0x0004
#define DFCS_BUTTON3STATE 0x0008
#define DFCS_BUTTONPUSH 0x0010
#define DFCS_INACTIVE 0x0100
#define DFCS_PUSHED 0x0200
#define DFCS_CHECKED 0x0400
#define DFCS_TRANSPARENT 0x0800
#define DFCS_HOT 0x1000
#define DFCS_ADJUSTRECT 0x2000
#define DFCS_FLAT 0x4000
#define DFCS_MONO 0x8000
#define DT_TOP 0x00000000
#define DT_LEFT 0x00000000
#define DT_CENTER 0x00000001
#define DT_RIGHT 0x00000002
#define DT_VCENTER 0x00000004
#define DT_BOTTOM 0x00000008
#define DT_WORDBREAK 0x00000010
#define DT_SINGLELINE 0x00000020
#define DT_EXPANDTABS 0x00000040
#define DT_TABSTOP 0x00000080
#define DT_NOCLIP 0x00000100
#define DT_EXTERNALLEADING 0x00000200
#define DT_CALCRECT 0x00000400
#define DT_NOPREFIX 0x00000800
#define DT_INTERNAL 0x00001000
#define DT_EDITCONTROL 0x00002000
#define DT_PATH_ELLIPSIS 0x00004000
#define DT_END_ELLIPSIS 0x00008000
#define DT_MODIFYSTRING 0x00010000
#define DT_RTLREADING 0x00020000
#define DT_WORD_ELLIPSIS 0x00040000
#define OCR_SIZEWE 32644
#define OCR_SIZENS 32645
#define OCR_IBEAM 32513
struct WINDOWPOS
{
unsigned int hwnd;
unsigned int hwndInsertAfter;
int x;
int y;
int cx;
int cy;
unsigned int flags;
}
struct HDLAYOUT
{
RECT *prc;
WINDOWPOS *pwpos;
}
struct NMHDR
{
unsigned int hwndFrom;
unsigned int idFrom;
unsigned int code;
}
declare import,SendMessage alias "SendMessageA" (unsigned int hWnd, unsigned int uMsg, unsigned int wParam, unsigned int lParam), unsigned int;
declare import,PostMessage alias "PostMessageA" (unsigned int hWnd, unsigned int uMsg, unsigned int wParam, unsigned int lParam), unsigned int;
declare import,DrawFrameControl(
UINT hdc, // handle to device context
RECT *lprc, // bounding rectangle
UINT uType, // frame-control type
UINT uState // frame-control state
),int;
declare import, DrawText alias "DrawTextA"(
UINT hDC, // handle to DC
STRING *lpString, // text to draw
int nCount, // text length
RECT *lpRect, // formatting dimensions
UINT uFormat // text-drawing options
),int;
declare import,SetTextAlign(
UINT hdc, // handle to DC
UINT fMode // text-alignment option
),UINT;
declare import,InvalidateRect(unsigned int hWnd, pointer lprc, int bErase), int;
declare import,PtInRect(
RECT *lprc, // rectangle
POINT pt BYVAL // point
),int;
declare import,InflateRect(
RECT *lprc, // address of rectangle
int dx, // amount to increase or decrease width
int dy // amount to increase or decrease height
),int;
declare import, SetCapture(uint hwnd),uint;
declare import, ReleaseCapture(),int;
declare import, SetWindowLongA(uint hwnd,int nIndex,uint value),INT;
declare import, SelectObject(unsigned int hdc, unsigned int hdiobj), unsigned int;
declare extern InitializeControl(CControl *control,int type);
//hit testing return values
#define HIT_NOWHERE 0
#define HIT_LEFT_RESIZE 1
#define HIT_CELL 2
#define HIT_LEFT_BUTTON 4
//Cell types
#define CELL_TEXT 0x0001
#define CELL_IMAGE 0x0002
#define CELL_CHECKBOX 0x0003
class gridCell
{
declare gridCell();
string m_strText;
uint m_cForeColor;
uint m_cBackColor;
uint m_uStyle;
uint m_uType;
int m_bReadOnly;
}
class CGridWnd : CControl
{
declare CGridWnd();
declare _CGridWnd();
declare virtual Create(int l,int t,int w,int h,int style,int nID,string title,CWindow *parent),int;
declare virtual OnClose(),int;
declare virtual OnHScroll(int nCommand,int nPos,int nID),int;
declare virtual OnLButtonDblClk(int x,int y,int flags),int;
declare virtual OnLButtonDown(int x,int y, int flags),int;
declare virtual OnLButtonUp(int x,int y,int flags),int;
declare virtual OnMouseMove(int x,int y,int flags),int;
declare virtual OnPaint(),int;
declare virtual OnRButtonUp(int x,int y,int flags),int;
declare virtual OnVScroll(int nCommand,int nPos,int nID),int;
declare virtual OnSize(int nType,int cx,int cy),int;
declare virtual OnControl(int nID, int nNotifyCode, unsigned int hControl),int;
declare HitTest(int x,int y,opt int *item = NULL),int;
// operations
public:
declare SetGridSize(uint x,uint y),int;
declare AddRow(),int;
declare SetCellText(uint x,uint y,string text,opt uint format = DT_VCENTER);
declare SetCellColor(uint x,uint y,uint fg,uint bg);
declare SetCellType(uint x,uint y,uint type);
declare SetHeaderText(int header,int iItem,string text);
declare SetRowHeight(int iRow,int Height);
declare SetColumnWidth(int iColumn,int Width);
declare SetDefaultFont(string typeface,int height,int weight,OPT int flags=0);
declare SetHeaderFont(string typeface,int height,int weight,OPT int flags=0);
declare ShowGridLines(int bShow);
CEdit m_cellEdit;
protected:
declare CalculateRects();
CHeaderCtrl *m_topHeader;
CHeaderCtrl *m_LeftHeader;
pointer *m_gridArray;
RECT *m_gridRects;
int m_nTopWidth;
uint m_nCountX;
uint m_nCountY;
uint m_hNSCursor;
uint m_hIBeamCursor;
int m_bDragLeft;
int m_nDragItem;
int m_nDragStartY;
int m_nDragLastY;
int m_bEditShown;
int m_nEditItem;
int m_bShowGridLines;
//fonts
string m_strDefaultFont;
int m_nDefaultFontSize;
int m_nDefaultFontWeight;
string m_strHeaderFont;
int m_nHeaderFontSize;
int m_nHeaderFontWeight;
uint m_hHeaderFont;
}
//gridCell Implementation
gridCell::gridCell()
{
m_strText = "";
m_cForeColor = 0;
m_cBackColor = 0xFFFFFF;
m_uStyle = DT_VCENTER | DT_SINGLELINE;
m_bReadOnly = false;
m_uType = CELL_TEXT;
}
//CGridWnd Implementation
CGridWnd::CGridWnd()
{
m_topHeader = NULL;
m_leftHeader = NULL;
m_nTopWidth = 0;
m_gridArray = NULL;
m_gridRects = NULL;
CCursor c;
c.LoadOEMCursor(OCR_SIZENS);
m_hNSCursor = c.Detach();
c.LoadOEMCursor(OCR_IBEAM);
m_hIBeamCursor = c.Detach();
m_bDragLeft = false;
m_nDragItem = 0;
m_nDragStartY = 0;
m_nDragLastY = 0;
m_bEditshown = false;
m_nEditItem = 0;
m_bShowGridLines = true;
m_strDefaultFont = "Verdana";
m_nDefaultFontSize = 10;
m_nDefaultFontWeight = 400;
m_strHeaderFont = "Verdana";
m_nHeaderFontSize = 10;
m_nHeaderFontWeight = 700;
m_nCountX = 0;
m_nCountY = 0;
}
CGridWnd::_CGridWnd()
{
if(m_topHeader)
delete m_topHeader;
if(m_leftHeader)
delete m_leftHeader;
int count = m_nCountX * m_nCountY;
//sanity checks
if(m_gridArray)
{
for(int z = 0;z<count;z++)
{
gridCell *temp = *m_gridArray[z];
delete temp;
}
delete m_gridArray;
}
if(m_gridRects)
delete m_gridRects;
}
CGridWnd::Create(int l,int t,int w,int h,int style,int nID,string title,CWindow *parent),int
{
int nReturn = CWindow!!Create(l,t,w,h,style,0,title,parent);
RECT rc = GetClientRect();
HDLAYOUT hdl;
WINDOWPOS wp;
hdl.prc = &rc;
hdl.pwpos = ℘
if(1)
{
m_TopHeader = new(CHeaderCtrl,1);
m_TopHeader->Create(0,0,0,0,0x02|ACCS_TOP,9998,"",this);
SendMessage(m_topHeader->GetHandle(),HDM_LAYOUT,0,&hdl);
m_TopHeader->SetSize(wp.x,wp.y,wp.cx,wp.cy);
m_nTopWidth = wp.cy;
m_TopHeader->SetDefaultItemSize(50);
m_TopHeader->InsertItem(0,"*");
m_TopHeader->ShowWindow(SWSHOW);
}
if(1)
{
m_leftHeader = new(CHeaderCtrl,1);
m_LeftHeader->Create(0,0,wp.cx,wp.cy,0,9999,"",this);
m_LeftHeader->SetDefaultItemSize(wp.cy);
m_LeftHeader->InsertItem(0,"*");
}
if(m_hWnd)
{
if(nID)
SetWindowLongA(m_hwnd, -12, nID);
InitializeControl(this,CTCUSTOM);
}
SetFont(m_strDefaultFont,m_nDefaultFontSize,m_nDefaultFontWeight);
m_topHeader->SetFont(m_strHeaderFont,m_nHeaderFontSize,m_nHeaderFontWeight);
m_hHeaderFont = SendMessage(m_topHeader->GetHandle(),WM_GETFONT,0,0);
return nReturn;
}
CGridWnd::OnSize(int nType,int cx,int cy),int
{
RECT rc = GetClientRect(),rchead;
HDLAYOUT hdl;
WINDOWPOS wp;
if(m_topHeader)
{
rchead = m_topHeader->GetWindowRect();
hdl.prc = &rc;
hdl.pwpos = ℘
SendMessage(m_topHeader->GetHandle(),HDM_LAYOUT,0,&hdl);
m_TopHeader->SetSize(wp.x,wp.y,wp.cx,rchead.bottom-rchead.top);
}
}
CGridWnd::OnClose(),int
{
Destroy();
return 0;
}
CGridWnd::OnLButtonDblClk(int x,int y,int flags),int
{
return 0;
}
CGridWnd::OnLButtonDown(int x,int y, int flags),int
{
int item;
int where = HitTest(x,y,&item);
if(m_bEditShown) //is the edit control shown?
{
string newtext = m_cellEdit.GetText();
gridCell *temp = *m_gridArray[m_nEditItem];
temp->m_strText = newtext;
m_cellEdit.Destroy();
InvalidateRect(GetHandle(),*m_gridRects[m_nEditItem],0);
m_bEditShown = false;
}
if(where == HIT_LEFT_RESIZE)
{
m_bDragLeft = true;
m_nDragItem = item;
SetCapture(GetHandle());
m_nDragStartY = y;
m_nDragLastY = y;
RECT rc;
m_topHeader->GetItemRect(0,&rc);
SetRasterOp(RMXORPEN);
line(0,y,rc.right,y,RGB(255,255,255));
SetRasterOp(RMCOPYPEN);
}
else if(where == HIT_CELL)
{
temp = *m_gridArray[item];
if(!temp->m_bReadOnly)
{
if(temp->m_uType == CELL_TEXT)
{
RECT rcEdit = *m_gridRects[item];
rcEdit.top--;
rcEdit.bottom++;
rcEdit.left--;
//make sure the edit control text can be seen for a minimum of 5 chars.
POINT pt = GetTextSize("XXXXX");
if((rcEdit.bottom - rcEdit.Top) < (pt.y + 4))
rcEdit.bottom = rcEdit.Top + pt.y + 4;
if((rcEdit.right - rcEdit.left) < (pt.x + 4))
rcEdit.right = rcEdit.left + pt.x + 4;
m_cellEdit.Create(rcEdit.left,rcEdit.top,rcEdit.right - rcEdit.left,rcEdit.bottom - rcEdit.top,
AWS_VISIBLE | AES_WANTRETURN | AES_MULTILINE | AES_AUTOHSCROLL | AES_AUTOVSCROLL,9997,temp->m_strText,this);
m_cellEdit.SetFont(m_strDefaultFont,m_nDefaultFontSize,m_nDefaultFontWeight);
m_nEditItem = item;
m_bEditShown = true;
SetFocus(9997);
}
else if(temp->m_uType == CELL_CHECKBOX)
{
if(temp->m_strText == "1")
temp->m_strText = "0"
else
temp->m_strText = "1";
InvalidateRect(GetHandle(),*m_gridRects[item],false);
}
}
}
return 0;
}
CGridWnd::OnLButtonUp(int x,int y,int flags),int
{
if(m_bDragLeft)
{
m_bDragLeft = false;
ReleaseCapture();
if(x!=m_nDragStartY)
{
RECT rc;
m_LeftHeader->GetItemRect(m_nDragItem,&rc);
int sz = rc.right-rc.left;
sz += (y - m_nDragStartY);
if(sz < 1)
sz = 1;
if(m_nDragItem == 0)
{
rc = m_topHeader->GetWindowRect();
m_topHeader->SetSize(0,0,rc.right-rc.left,sz);
m_nTopWidth = sz;
m_leftHeader->SetItemSize(m_nDragItem,sz);
CalculateRects();
InvalidateRect(GetHandle(),NULL,1);
}
else
{
m_leftHeader->SetItemSize(m_nDragItem,sz);
CalculateRects();
InvalidateRect(GetHandle(),NULL,1);
}
}
}
return 0;
}
CGridWnd::OnControl(int nID, int nNotifyCode, unsigned int hControl),int
{
select(nID)
{
case 9998:
if(nNotifyCode == HDN_ENDTRACK || nNotifyCode == HDN_ENDTRACKW)
{
if(m_bEditShown) //is the edit control already shown?
{
string newtext = m_cellEdit.GetText();
gridCell *temp = *m_gridArray[m_nEditItem];
temp->m_strText = newtext;
m_cellEdit.Destroy();
InvalidateRect(GetHandle(),*m_gridRects[m_nEditItem],0);
m_bEditShown = false;
}
PostMessage(GetHandle(),WM_COMMAND,9998 | ((HDN_TRACKDONE << 16) & 0xFFFF0000),hControl);
}
if(nNotifyCode == HDN_TRACKDONE)
{
CalculateRects();
InvalidateRect(GetHandle(),NULL,1);
}
}
}
//SetGridSize is destructive. It removes and reallocates the entire grid
CGridWnd::SetGridSize(uint x,uint y),int
{
int count = m_nCountX * m_nCountY;
//sanity checks
if(m_gridArray)
{
for(int z = 0;z<count;z++)
{
gridCell *temp = *m_gridArray[z];
delete temp;
}
delete m_gridArray;
}
if(m_gridRects)
delete m_gridRects;
//Allocate the grid
m_nCountX = x;
m_nCountY = y;
count = x * y;
m_gridRects = new(RECT,count);
m_gridArray = new(INT,count);
for(z = 0;z < count;z++)
{
*m_gridArray[z] = new(gridCell,1);
temp = *m_gridArray[z];
}
//set up the headers
m_topHeader->DeleteAllItems();
m_leftHeader->DeleteAllItems();
//the *
m_topHeader->InsertItem(0,"*");
m_leftHeader->InsertItem(0,"*");
for(z = 1;z < m_nCountX+1;z++)
{
m_topHeader->InsertItem(z,"");
}
for(z = 1;z < m_nCountY+1;z++)
{
m_leftHeader->InsertItem(z,"");
}
CalculateRects();
InvalidateRect(GetHandle(),NULL,1);
return true;
}
CGridWnd::AddRow(),int
{
}
CGridWnd::CalculateRects()
{
RECT rcTop;
RECT rcLeft;
RECT rcCell;
if(m_nCountX && m_nCountY)
{
if(m_topHeader && m_leftHeader)
{
int top = m_nTopWidth;
m_topHeader->GetItemRect(0,&rcTop);
int width = rcTop.right - rcTop.left;
for(int y = 1;y <= m_nCountY;y++)
{
//calculate the left header rect for this position
m_leftHeader->GetItemRect(y,&rcLeft);
height = rcLeft.right - rcLeft.left;
rcLeft.left = 0;
rcLeft.top = top;
top += height;
rcLeft.right = width;
rcLeft.bottom = rcLeft.top + height;
for(int x = 1;x <= m_nCountX;x++)
{
m_topHeader->GetItemRect(x,&rcTop);
//the cell rect is
//top.left,left.top,top.right,left.bottom
*m_gridRects[(x-1)+((y-1)*m_nCountX)].left = rcTop.left+1;
*m_gridRects[(x-1)+((y-1)*m_nCountX)].top = rcLeft.top+1;
*m_gridRects[(x-1)+((y-1)*m_nCountX)].right = rcTop.right-1;
*m_gridRects[(x-1)+((y-1)*m_nCountX)].bottom = rcLeft.bottom-1;
}
}
}
}
}
CGridWnd::SetRowHeight(int iRow,int Height)
{
if(iRow > -1 && iRow <= m_nCountY)
{
m_leftHeader->SetItemSize(iRow,Height);
CalculateRects();
InvalidateRect(GetHandle(),NULL,1);
}
}
CGridWnd::SetColumnWidth(int iColumn,int Width)
{
if(iColumn > -1 && iColumn <= m_nCountX)
{
m_topHeader->SetItemSize(iColumn,Width);
CalculateRects();
InvalidateRect(GetHandle(),NULL,1);
}
}
//cell positions are ones based
CGridWnd::SetCellText(uint x,uint y,string text,opt uint format)
{
if((x > 0) && (x <= m_nCountX))
{
if((y > 0) && (y <= m_nCountY))
{
x--;
y--;
gridCell *temp = *m_gridArray[x+(y * m_nCountX)];
temp->m_strText = text;
temp->m_uStyle = format;
InvalidateRect(GetHandle(),*m_gridRects[x+(y * m_nCountX)],0);
}
}
}
CGridWnd::SetCellColor(uint x,uint y,uint fg,uint bg)
{
if((x > 0) && (x <= m_nCountX))
{
if((y > 0) && (y <= m_nCountY))
{
x--;
y--;
gridCell *temp = *m_gridArray[x+(y * m_nCountX)];
temp->m_cForeColor = fg;
temp->m_cBackColor = bg;
InvalidateRect(GetHandle(),*m_gridRects[x+(y * m_nCountX)],0);
}
}
}
CGridWnd::SetCellType(uint x,uint y,uint type)
{
if((x > 0) && (x <= m_nCountX))
{
if((y > 0) && (y <= m_nCountY))
{
x--;
y--;
gridCell *temp = *m_gridArray[x+(y * m_nCountX)];
temp->m_uType = type;
InvalidateRect(GetHandle(),*m_gridRects[x+(y * m_nCountX)],0);
}
}
}
//header positions are ones based
CGridWnd::SetHeaderText(int header,int iItem,string text)
{
if(header == 0)
{
if((iItem > 0) && (iItem <= m_nCountX))
{
if(m_TopHeader)
m_TopHeader->SetItemText(iItem,text);
}
}
else if(header == 1)
{
if((iItem > 0) && (iItem <= m_nCountY))
{
if(m_leftHeader)
m_leftHeader->SetItemText(iItem,text);
}
}
}
CGridWnd::ShowGridLines(int bShow)
{
m_bShowGridLines = bShow;
InvalidateRect(GetHandle(),NULL,1);
}
CGridWnd::OnPaint(),int
{
//draw the left header
int top = m_nTopWidth;
int width,height;
DrawMode(TRANSPARENT);
UINT hdc = GetHDC();
int taOld = SetTextAlign(hdc,0);
if(m_leftHeader && m_topHeader)
{
RECT rc;
m_topHeader->GetItemRect(0,&rc);
int leftCount = m_leftHeader->GetItemCount();
int topCount = m_topHeader->GetItemCount();
width = rc.right - rc.left;
uint oldFont = SelectObject(hdc,m_hHeaderFont);
for(int x=1;x < leftCount;x++)
{
m_LeftHeader->GetItemRect(x,&rc);
height = rc.right - rc.left;
rc.left = 0;
rc.top = top;
top += height;
rc.right = width;
rc.bottom = rc.top + height;
DrawFrameControl(hdc,&rc,DFC_BUTTON,DFCS_BUTTONPUSH);
rc.left += 4;
rc.top += 2;
DrawText(hdc,m_leftHeader->GetItemText(x),len(m_leftHeader->GetItemText(x)),&rc,DT_VCENTER);
}
if(oldFont)
SelectObject(hdc,oldFont);
//draw the gridlines
if(m_bShowGridlines)
{
height = top;
m_TopHeader->GetItemRect(m_TopHeader->GetItemCount()-1,&rc);
int length = rc.right;
for(x = 1;x< topCount;x++)
{
m_topHeader->GetItemRect(x,&rc);
Line(rc.right-1,rc.bottom,rc.right-1,height,m_fg);
}
top = m_nTopWidth;
for(x = 1;x< leftCount;x++)
{
m_leftHeader->GetItemRect(x,&rc);
height = rc.right - rc.left;
rc.left = 0;
rc.top = top;
top += height;
rc.right = width;
rc.bottom = rc.top + height;
line(rc.right,top-1,length,top-1,m_fg);
}
}
//draw the data
int total = m_nCountX * m_nCountY;
for(x = 0; x < total;x++)
{
gridCell *temp = *m_gridArray[x];
DrawRect(*m_gridRects[x].left-1,*m_gridRects[x].top-1,*m_gridRects[x].right - *m_gridRects[x].left + 1,*m_gridRects[x].bottom - *m_gridRects[x].top + 1,temp->m_cBackColor,temp->m_cBackColor);
FrontPen(temp->m_cForeColor);
if(temp->m_uType == CELL_TEXT)
{
DrawText(hdc,temp->m_strText,len(temp->m_strText),*m_gridRects[x],temp->m_uStyle);
}
else if(temp->m_uType == CELL_CHECKBOX)
{
}
if(m_bEditShown && m_nEditItem == x)
{
//tell the edit control to redraw since a right click menu might invalidate it.
InvalidateRect(m_CellEdit.GetHandle(),NULL,1);
}
}
}
SetTextAlign(hdc,taOld);
ReleaseHDC(hdc);
return 0;
}
CGridWnd::OnRButtonUp(int x,int y,int flags),int
{
return 0;
}
CGridWnd::OnVScroll(int nCommand,int nPos,int nID),int
{
return 0;
}
CGridWnd::OnHScroll(int nCommand,int nPos,int nID),int
{
return 0;
}
CGridWnd::OnMouseMove(int x,int y,int flags),int
{
uint hCursor = NULL;
int item;
int ht = HitTest(x,y,&item);
if(m_bDragLeft)
ht = HIT_LEFT_RESIZE;
select(ht)
{
case HIT_LEFT_RESIZE:
SetCursor(CS_CUSTOM,m_hNSCursor);
case HIT_CELL:
SetCursor(CS_CUSTOM,m_hIBeamCursor);
default:
SetCursor(CS_ARROW);
}
if(m_bDragLeft)
{
RECT rc;
m_topHeader->GetItemRect(0,&rc);
SetRasterOp(RMXORPEN);
line(0,m_nDragLastY,rc.right,m_nDragLastY,RGB(255,255,255));
line(0,y,rc.right,y,RGB(255,255,255));
m_nDragLastY = y;
SetRasterOp(RMCOPYPEN);
}
return 0;
}
CGridWnd::HitTest(int x,int y,opt int *item = NULL),int
{
POINT pt;
pt.x = x;
pt.y = y;
RECT rc;
int top = 0;//m_nTopWidth;
m_topHeader->GetItemRect(0,&rc);
int leftCount = m_leftHeader->GetItemCount();
width = rc.right - rc.left;
for(int xx=0;xx < leftCount;xx++)
{
m_LeftHeader->GetItemRect(xx,&rc);
height = rc.right - rc.left;
rc.left = 0;
rc.top = top;
top += height;
rc.right = width;
rc.bottom = rc.top + height;
//deflate the rect and check for body hit
rc.bottom -= 3;
if(!PtInRect(&rc,pt))
{
//check for edge
rc.bottom += 6;
if(PtInRect(&rc,pt))
{
if(item) {
*item = xx; }
return HIT_LEFT_RESIZE;
}
}
else
{
if(item) {
*item = xx; }
return HIT_LEFT_BUTTON;
}
}
//check for cell rects
for(xx = 0;xx < (m_nCountX * m_nCountY);xx++)
{
if(PtInRect(*m_gridRects[xx],pt))
{
if(item) {
*item = xx; }
return HIT_CELL;
}
}
if(item) {
*item = -1; }
return HIT_NOWHERE;
}
CGridWnd::SetDefaultFont(string typeface,int height,int weight,OPT int flags=0)
{
m_strDefaultFont = typeface;
m_nDefaultFontSize = height;
m_nDefaultFontWeight = weight;
SetFont(typeface,height,weight,flags);
InvalidateRect(GetHandle(),NULL,1);
}
CGridWnd::SetHeaderFont(string typeface,int height,int weight,OPT int flags=0)
{
m_strHeaderFont = typeface;
m_nHeaderFontSize = height;
m_nHeaderFontWeight = weight;
m_topHeader->SetFont(m_strHeaderFont,m_nHeaderFontSize,m_nHeaderFontWeight,flags);
m_hHeaderFont = SendMessage(m_topHeader->GetHandle(),WM_GETFONT,0,0);
}
sub main()
{
CGridWnd grid;
grid.Create(0,0,640,480,AWS_VISIBLE|AWS_SYSMENU|AWS_BORDER|AWS_CAPTION|AWS_SIZE,99,"Grid Test",0);
grid.SetGridSize(10,10);
//grid.ShowGridLines(false);
for(int x=1;x<11;x++)
{
grid.SetHeaderText(1,x,NumToStr(x));
grid.SetHeaderText(0,x,ToChar('A'-1+x));
}
grid.SetHeaderFont("Tahoma",10,700);
//grid.SetColor(rgb(255,0,0),rgb(0,0,255));
grid.SetCellText(1,1,"Test");
grid.SetCellText(2,1,"Test2");
grid.SetCellText(10,10,"Last Cell\nA multi line one",DT_WORD_ELLIPSIS);
grid.SetCellColor(1,1,RGB(255,0,0),RGB(255,255,255));
grid.SetCellColor(2,1,RGB(255,255,255),RGB(0,0,192));
grid.SetCellColor(10,10,0,RGB(255,207,0));
grid.SetRowHeight(10,40);
grid.SetColumnWidth(10,100);
while grid.IsValid() { wait(); }
}
Wow! Great work. 8)
Can't wait to have it as part of Aurora.
Thanks Paul for all your work!!
Haim
Paul,
When I compile and run you sample (compoles fine and runs fine), I get a "Program Error" when I close it.
Regarding cell dimensions:
1. How can I fix the cell heights and/or widths to prevent changing their size?
2. When I drag/resize the cell height, I overlap the cell above, hiding them never to uncover. How can I prevent this?
it looks like there needs to be a limit set somewhere in the CGridWnd::OnMouseMove or CGridWnd::HitTest methods.
As stated it still needs more work.
I don't get a program error when closing. Debug it and let me know where.
Locking resize on the row lables is relatively easy. On the header it will require a bit more work by subclassing the windows header control and intercepting a few messages. Just like the advanced list view demo for Emergence does.
Paul.