#include "stdafx.h" #include "DataGrid.h" /* DataGrid global variables */ DG_LIST *g_DGList = NULL; int g_DGGridNumber = 0; CDataGrid::CDataGrid() { // Init DataGrid variables m_hWnd = NULL; m_hParentWnd = NULL; } CDataGrid::~CDataGrid() { } void CDataGrid::Resize() { // Recalculate DataGrid relative size RECT rectParent; GetWindowRect( m_hParentWnd, &rectParent ); SetWindowPos( m_hWnd, HWND_NOTOPMOST, int((double(m_DGRect.left)/100.0)*(rectParent.right-rectParent.left)), int((double(m_DGRect.top)/100.0)*(rectParent.bottom-rectParent.top)), int((double(m_DGRect.right)/100.0)*(rectParent.right-rectParent.left)), int((double(m_DGRect.bottom)/100.0)*(rectParent.bottom-rectParent.top)), SWP_NOZORDER | SWP_SHOWWINDOW ); RECT clientRect; GetClientRect( m_hWnd, &clientRect ); RecalcWindow(m_hWnd); InvalidateRect( m_hWnd, NULL, TRUE ); UpdateWindow(m_hWnd); } BOOL CDataGrid::Create(RECT wndRect, HWND hParent, int numCols) { BOOL result = FALSE; // Set DatGrid parent window handle m_hParentWnd = hParent; // Calculate DataGrid relative offset and size RECT rectParent; GetWindowRect( hParent, &rectParent ); m_DGRect.left = int(100*double(wndRect.left)/double(rectParent.right-rectParent.left)); m_DGRect.top = int(100*double(wndRect.top)/double(rectParent.right-rectParent.left)); m_DGRect.right = int(100*double(wndRect.right-wndRect.left)/double(rectParent.right-rectParent.left)); m_DGRect.bottom = int(100*double(wndRect.bottom-wndRect.top)/double(rectParent.bottom-rectParent.top)); WNDCLASSEX wincl; wincl.hInstance = GetModuleHandle(NULL); wincl.lpszClassName = "DATAGRID"; wincl.lpfnWndProc = DataGridProc; wincl.style = CS_DBLCLKS; wincl.cbSize = sizeof (WNDCLASSEX); wincl.hIcon = LoadIcon( NULL, IDI_APPLICATION ); wincl.hIconSm = LoadIcon( NULL, IDI_APPLICATION ); wincl.hCursor = LoadCursor( NULL, IDC_ARROW ); wincl.lpszMenuName = NULL; wincl.cbClsExtra = 0; wincl.cbWndExtra = 0; wincl.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); ATOM reg = RegisterClassEx(&wincl); DWORD error = GetLastError(); // Register DataGrid window class if ( ( reg ) || ( error == ERROR_CLASS_ALREADY_EXISTS ) ) { // Create DataGrid window m_hWnd = CreateWindowEx( WS_EX_CLIENTEDGE, "DATAGRID", "", WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_VSCROLL | WS_HSCROLL, wndRect.left, wndRect.top, wndRect.right-wndRect.left, wndRect.bottom-wndRect.top, hParent, NULL, GetModuleHandle(NULL), NULL ); // Hide DataGrid window scroll bars ShowScrollBar( m_hWnd, SB_BOTH, FALSE ); if ( m_hWnd ) { AddDGGrid( m_hWnd, m_hParentWnd ); result = TRUE; } // Init DataGrid GDI objects InitDGGlobals( hParent, m_hWnd ); DG_LIST* dgList = GetDGGrid(m_hWnd); if ( dgList != NULL ) { // Init DataGrid columns info if ( numCols < DG_MAXCOLUMN ) dgList->dg_ColumnNumber = numCols; else dgList->dg_ColumnNumber = DG_MAXCOLUMN; dgList->dg_Columns = new DG_COLUMN[dgList->dg_ColumnNumber]; for ( int i=0; idg_ColumnNumber; i++ ) { dgList->dg_Columns[i].columnWidth = 50; dgList->dg_Columns[i].textAlign = DGTA_LEFT; strcpy( dgList->dg_Columns[i].columnText, " " ); } // Create EDIT control dgList->dg_hEditWnd = CreateWindow( "EDIT", "", WS_CHILD | WS_CLIPSIBLINGS | ES_AUTOHSCROLL, 0, 0, 100, 20, m_hWnd, NULL, GetModuleHandle(NULL), NULL ); SendMessage( dgList->dg_hEditWnd, WM_SETFONT, (WPARAM)dgList->dg_hRowFont, MAKELPARAM(TRUE,0) ); } } return result; } HWND CDataGrid::GetWindowHandle() { // Return DataGrid window handle return m_hWnd; } void InitDGGlobals(HWND hParent, HWND hWnd) { DG_LIST* dgList = GetDGGrid(hWnd); if ( dgList != NULL ) { // Create fonts HDC hParentDC = GetDC(hParent); LOGFONT lf; lf.lfHeight = -MulDiv( 10, GetDeviceCaps(hParentDC,LOGPIXELSY), 72 ); lf.lfWidth = 0; lf.lfEscapement = 0; lf.lfOrientation = 0; lf.lfWeight = FW_NORMAL; lf.lfItalic = FALSE; lf.lfUnderline = FALSE; lf.lfStrikeOut = FALSE; lf.lfCharSet = ANSI_CHARSET; lf.lfOutPrecision = OUT_DEFAULT_PRECIS; lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; lf.lfQuality = DEFAULT_QUALITY; lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; strcpy( lf.lfFaceName, "Arial" ); dgList->dg_hColumnFont = CreateFontIndirect(&lf); memcpy( &dgList->dg_LFColumnFont, &lf, sizeof(LOGFONT) ); lf.lfHeight = -MulDiv( 9, GetDeviceCaps(hParentDC,LOGPIXELSY), 72 ); dgList->dg_hRowFont = CreateFontIndirect(&lf); memcpy( &dgList->dg_LFRowFont, &lf, sizeof(LOGFONT) ); ReleaseDC( hParent, hParentDC ); // Create background brush LOGBRUSH lb; lb.lbColor = DGBGR_COLOR; lb.lbStyle = BS_SOLID; dgList->dg_hBgBrush = CreateBrushIndirect(&lb); // Create cell pen LOGPEN lp; lp.lopnColor = RGB(210,210,210); lp.lopnStyle = PS_SOLID; lp.lopnWidth.x = 1; dgList->dg_hCellPen = CreatePenIndirect(&lp); } } BOOL CDataGrid::SetColumnInfo(int columnIndex, char* columnText, int columnWidth, int textAlign) { BOOL result = FALSE; DG_LIST* dgList = GetDGGrid(m_hWnd); if ( dgList != NULL ) { // Check column index if ( columnIndex < dgList->dg_ColumnNumber ) { // Set new DataGrid column info strncpy( dgList->dg_Columns[columnIndex].columnText, columnText, 1024 ); dgList->dg_Columns[columnIndex].columnWidth = columnWidth; dgList->dg_Columns[columnIndex].textAlign = textAlign; dgList->dg_Columns[columnIndex].pressed = false; result = TRUE; } } return result; } BOOL CDataGrid::SetItemInfo(int rowIndex, int columnIndex, char* itemText, int textAlign, bool readOnly) { BOOL result = FALSE; DG_LIST* dgList = GetDGGrid(m_hWnd); if ( dgList != NULL ) { if ( SetDGItemText( m_hWnd, rowIndex, columnIndex, itemText ) ) { dgList->dg_Rows[rowIndex].textAlign[columnIndex] = textAlign; if ( dgList->dg_EnableEdit ) dgList->dg_Rows[rowIndex].readOnly[columnIndex] = readOnly; result = TRUE; } } return result; } BOOL SetDGItemText(HWND hWnd, int rowIndex, int columnIndex, char* buffer) { BOOL result = FALSE; DG_LIST* dgList = GetDGGrid(hWnd); if ( dgList != NULL ) { // Check column and row index if ( ( columnIndex < dgList->dg_ColumnNumber ) && ( rowIndex < dgList->dg_RowNumber ) && ( buffer != NULL ) ) { // Set DataGrid row info if ( strlen(dgList->dg_Rows[rowIndex].rowText[columnIndex]) < strlen(buffer) ) { delete dgList->dg_Rows[rowIndex].rowText[columnIndex]; dgList->dg_Rows[rowIndex].rowText[columnIndex] = new char[strlen(buffer)+1]; } if ( strlen(buffer) == 0 ) strcpy( dgList->dg_Rows[rowIndex].rowText[columnIndex], " " ); else strcpy( dgList->dg_Rows[rowIndex].rowText[columnIndex], buffer ); result = TRUE; } } return result; } BOOL CDataGrid::SetItemInfo(DG_ITEMINFO* itemInfo) { BOOL result = FALSE; DG_LIST* dgList = GetDGGrid(m_hWnd); if ( dgList != NULL ) { if ( ( itemInfo != NULL ) && ( itemInfo->dgItem < dgList->dg_RowNumber ) && ( itemInfo->dgSubitem < dgList->dg_ColumnNumber ) ) { switch ( itemInfo->dgMask ) { case DG_TEXTEDIT: { // Set DataGrid item text SetDGItemText( m_hWnd, itemInfo->dgItem, itemInfo->dgSubitem, itemInfo->dgText ); } break; case DG_TEXTALIGN: { // Set DataGrid item text align dgList->dg_Rows[itemInfo->dgItem].textAlign[itemInfo->dgSubitem] = itemInfo->dgTextAlign; } break; case DG_TEXTHIGHLIGHT: { // Select DataGrid item SelectItem( itemInfo->dgItem, itemInfo->dgSubitem ); } break; case DG_TEXTRONLY: { // Set DataGrid item edit mode dgList->dg_Rows[itemInfo->dgItem].readOnly[itemInfo->dgSubitem] = itemInfo->dgReadOnly; } break; case DG_TEXTBGCOLOR: { // Set DataGrid row background color dgList->dg_Rows[itemInfo->dgItem].bgColor = itemInfo->dgBgColor; } break; } } } return result; } BOOL CDataGrid::GetItemText(int rowIndex, int columnIndex, char* buffer, int buffer_size) { BOOL result = GetDGItemText( m_hWnd, rowIndex, columnIndex, buffer, buffer_size ); return result; } BOOL GetDGItemText(HWND hWnd, int rowIndex, int columnIndex, char* buffer, int buffer_size) { BOOL result = FALSE; // Clear return buffer strcpy( buffer, "" ); DG_LIST* dgList = GetDGGrid(hWnd); // Check column and row index if ( ( columnIndex < dgList->dg_ColumnNumber ) && ( rowIndex < dgList->dg_RowNumber ) ) { // Get DataGrid item text strncpy( buffer, dgList->dg_Rows[rowIndex].rowText[columnIndex], buffer_size ); result = TRUE; } return result; } LRESULT CALLBACK DataGridProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_CREATE: { DG_LIST* dgList = GetDGGrid(hwnd); if ( dgList != NULL ) { HDC hDC = GetDC(dgList->dg_hWnd); RECT rectClient; GetClientRect( dgList->dg_hWnd, &rectClient ); dgList->dg_hMemBitmap = CreateCompatibleBitmap( hDC, (rectClient.right-rectClient.left), (rectClient.bottom-rectClient.top) ); dgList->dg_hMemDC = CreateCompatibleDC(hDC); dgList->dg_hOldMemBitmap = (HBITMAP)SelectObject( dgList->dg_hMemDC, dgList->dg_hMemBitmap ); SetFocus(dgList->dg_hWnd); ReleaseDC( dgList->dg_hWnd, hDC ); } } break; case WM_DESTROY: { // Delete DataGrid grid DestroyDGGrid(hwnd); } break; case WM_SIZE: { DG_LIST* dgList = GetDGGrid(hwnd); if ( dgList != NULL ) { // Delete memory device context if ( dgList->dg_hMemDC ) { SelectObject( dgList->dg_hMemDC, dgList->dg_hOldMemBitmap ); DeleteDC(dgList->dg_hMemDC); dgList->dg_hMemDC = NULL; } // Delete memory bitmap if ( dgList->dg_hMemBitmap ) { DeleteObject(dgList->dg_hMemBitmap); dgList->dg_hMemBitmap = NULL; } HDC hDC = GetDC(dgList->dg_hWnd); RECT rectClient; GetClientRect( dgList->dg_hWnd, &rectClient ); dgList->dg_hMemBitmap = CreateCompatibleBitmap( hDC, (rectClient.right-rectClient.left), (rectClient.bottom-rectClient.top) ); dgList->dg_hMemDC = CreateCompatibleDC(hDC); dgList->dg_hOldMemBitmap = (HBITMAP)SelectObject( dgList->dg_hMemDC, dgList->dg_hMemBitmap ); ReleaseDC( dgList->dg_hWnd, hDC ); } } case WM_PAINT: { PAINTSTRUCT ps; BeginPaint( hwnd, &ps ); DG_LIST* dgList = GetDGGrid(hwnd); if ( dgList != NULL ) { // Draw DataGrid rows DrawRows(dgList->dg_hWnd); // Draw DataGrid columns DrawColumns(dgList->dg_hWnd); RECT clientRect; GetClientRect( dgList->dg_hWnd, &clientRect ); BitBlt( ps.hdc, 0, 0, (clientRect.right-clientRect.left), (clientRect.bottom-clientRect.top), dgList->dg_hMemDC, 0, 0, SRCCOPY ); } EndPaint( hwnd, &ps ); } break; case WM_ERASEBKGND: { DG_LIST* dgList = GetDGGrid(hwnd); if ( dgList != NULL ) { RECT clientRect; GetClientRect( dgList->dg_hWnd, &clientRect ); FillRect( dgList->dg_hMemDC, &clientRect, dgList->dg_hBgBrush ); } } break; case WM_HSCROLL: { switch ( LOWORD(wParam) ) { case SB_LINERIGHT: case SB_PAGERIGHT: { int OldPos = GetScrollPos( hwnd, SB_HORZ ); SetScrollPos( hwnd, SB_HORZ, OldPos+10, TRUE ); int NewPos = GetScrollPos( hwnd, SB_HORZ ); InvalidateRect( hwnd, NULL, TRUE ); UpdateWindow(hwnd); } break; case SB_LINELEFT: case SB_PAGELEFT: { int OldPos = GetScrollPos( hwnd, SB_HORZ ); SetScrollPos( hwnd, SB_HORZ, OldPos-10, TRUE ); int NewPos = GetScrollPos( hwnd, SB_HORZ ); InvalidateRect( hwnd, NULL, TRUE ); UpdateWindow(hwnd); } break; case SB_THUMBTRACK: { SCROLLINFO si; si.cbSize = sizeof(SCROLLINFO); si.fMask = SIF_ALL; GetScrollInfo( hwnd, SB_HORZ, &si ); int OldPos = si.nPos; si.nPos = si.nTrackPos; int NewPos = si.nPos; SetScrollPos( hwnd, SB_HORZ, si.nPos, TRUE ); InvalidateRect( hwnd, NULL, TRUE ); UpdateWindow(hwnd); } break; } DG_LIST* dgList = GetDGGrid(hwnd); if ( dgList->dg_Edit ) { int scrollX = GetScrollPos( hwnd, SB_HORZ ); int scrollY = GetScrollPos( hwnd, SB_VERT ); RECT columnRect, clientRect; GetClientRect( hwnd, &clientRect ); GetColumnRect( hwnd, 0, &columnRect ); int offsetLeft = scrollX + clientRect.left; int offsetRight = scrollX + (clientRect.right-clientRect.left); int offsetTop = scrollY + clientRect.top + (columnRect.bottom-columnRect.top); int offsetBottom = scrollY + (clientRect.bottom - clientRect.top); if ( ( dgList->dg_EditRect.top >= offsetTop ) && ( dgList->dg_EditRect.bottom <= offsetBottom ) && ( dgList->dg_EditRect.right >= offsetLeft ) && ( dgList->dg_EditRect.left <= offsetRight ) ) { SetWindowPos( dgList->dg_hEditWnd, HWND_NOTOPMOST, dgList->dg_EditRect.left-scrollX, dgList->dg_EditRect.top-scrollY, dgList->dg_EditRect.right-dgList->dg_EditRect.left, dgList->dg_EditRect.bottom-dgList->dg_EditRect.top, SWP_NOZORDER | SWP_SHOWWINDOW ); SetFocus(dgList->dg_hEditWnd); } else ShowWindow( dgList->dg_hEditWnd, SW_HIDE ); } } break; case WM_VSCROLL: { switch ( LOWORD(wParam) ) { case SB_LINEUP: { RECT rowRect; GetRowRect( hwnd, 0, &rowRect ); int OldPos = GetScrollPos( hwnd, SB_VERT ); int diff = (rowRect.bottom-rowRect.top) - 1; SetScrollPos( hwnd, SB_VERT, OldPos-diff, TRUE ); InvalidateRect( hwnd, NULL, TRUE ); UpdateWindow(hwnd); } break; case SB_PAGEUP: { RECT rowRect, clientRect; GetRowRect( hwnd, 0, &rowRect ); GetClientRect( hwnd, &clientRect ); int OldPos = GetScrollPos( hwnd, SB_VERT ); int diff = (clientRect.bottom-clientRect.top) / (rowRect.bottom-rowRect.top-1) - 2; diff *= (rowRect.bottom-rowRect.top-1); SetScrollPos( hwnd, SB_VERT, OldPos-diff, TRUE ); InvalidateRect( hwnd, NULL, TRUE ); UpdateWindow(hwnd); } break; case SB_LINEDOWN: { RECT rowRect; GetRowRect( hwnd, 0, &rowRect ); int OldPos = GetScrollPos( hwnd, SB_VERT ); int diff = (rowRect.bottom-rowRect.top) - 1; SetScrollPos( hwnd, SB_VERT, OldPos+diff, TRUE ); InvalidateRect( hwnd, NULL, TRUE ); UpdateWindow(hwnd); } break; case SB_PAGEDOWN: { RECT rowRect, clientRect; GetRowRect( hwnd, 0, &rowRect ); GetClientRect( hwnd, &clientRect ); int OldPos = GetScrollPos( hwnd, SB_VERT ); int diff = (clientRect.bottom-clientRect.top) / (rowRect.bottom-rowRect.top-1) - 2; diff *= (rowRect.bottom-rowRect.top-1); SetScrollPos( hwnd, SB_VERT, OldPos+diff, TRUE ); InvalidateRect( hwnd, NULL, TRUE ); UpdateWindow(hwnd); } break; case SB_THUMBTRACK: { RECT rowRect, clientRect, columnRect; GetRowRect( hwnd, 0, &rowRect ); GetColumnRect( hwnd, 0, &columnRect ); GetClientRect( hwnd, &clientRect ); clientRect.top = columnRect.bottom; SCROLLINFO si; si.cbSize = sizeof(SCROLLINFO); si.fMask = SIF_ALL; GetScrollInfo( hwnd, SB_VERT, &si ); int OldPos = si.nPos; int NewPos = si.nTrackPos; int diff = NewPos % (rowRect.bottom-rowRect.top-1); NewPos -= diff; si.nPos = NewPos; si.nTrackPos = NewPos; SetScrollPos( hwnd, SB_VERT, NewPos, TRUE ); InvalidateRect( hwnd, NULL, TRUE ); UpdateWindow(hwnd); } break; } DG_LIST* dgList = GetDGGrid(hwnd); if ( dgList->dg_Edit ) { int scrollX = GetScrollPos( hwnd, SB_HORZ ); int scrollY = GetScrollPos( hwnd, SB_VERT ); RECT columnRect, clientRect; GetClientRect( hwnd, &clientRect ); GetColumnRect( hwnd, 0, &columnRect ); int offsetLeft = scrollX + clientRect.left; int offsetRight = scrollX + (clientRect.right-clientRect.left); int offsetTop = scrollY + clientRect.top + (columnRect.bottom-columnRect.top); int offsetBottom = scrollY + (clientRect.bottom - clientRect.top); if ( ( dgList->dg_EditRect.top >= offsetTop ) && ( dgList->dg_EditRect.bottom <= offsetBottom ) && ( dgList->dg_EditRect.right >= offsetLeft ) && ( dgList->dg_EditRect.left <= offsetRight ) ) { SetWindowPos( dgList->dg_hEditWnd, HWND_NOTOPMOST, dgList->dg_EditRect.left-scrollX, dgList->dg_EditRect.top-scrollY, dgList->dg_EditRect.right-dgList->dg_EditRect.left, dgList->dg_EditRect.bottom-dgList->dg_EditRect.top, SWP_NOZORDER | SWP_SHOWWINDOW ); SetFocus(dgList->dg_hEditWnd); } else ShowWindow( dgList->dg_hEditWnd, SW_HIDE ); } } break; case WM_MOUSEWHEEL: { short int zDelta = (short)HIWORD(wParam); if ( zDelta > 0 ) { // Scroll page up int scrollCode = SB_PAGEUP; short int pos = GetScrollPos( hwnd, SB_VERT ); SendMessage( hwnd, WM_VSCROLL, MAKEWPARAM(scrollCode,pos), (LPARAM)NULL ); } else { // Scroll page down int scrollCode = SB_PAGEDOWN; short int pos = GetScrollPos( hwnd, SB_VERT ); SendMessage( hwnd, WM_VSCROLL, MAKEWPARAM(scrollCode,pos), (LPARAM)NULL ); } } break; case WM_LBUTTONDOWN: { int scrollX = GetScrollPos( hwnd, SB_HORZ ); int scrollY = GetScrollPos( hwnd, SB_VERT ); int xPos = LOWORD(lParam) + scrollX; int yPos = HIWORD(lParam) + scrollY; DG_LIST* dgList = GetDGGrid(hwnd); if ( dgList != NULL ) { if ( dgList->dg_Edit ) { char text[1024]; SendMessage( dgList->dg_hEditWnd, WM_GETTEXT, (WPARAM)1024, (LPARAM)text ); ShowWindow( dgList->dg_hEditWnd, SW_HIDE ); dgList->dg_Edit = FALSE; SetDGItemText( hwnd, dgList->dg_SelectedRow, dgList->dg_SelectedColumn, text ); // Send notification to the parent window SendMessage( dgList->dg_hParent, WM_COMMAND, MAKEWPARAM(0,DGM_ITEMTEXTCHANGED), (LPARAM)hwnd ); } // Check for resized columns RECT rect; if ( dgList->dg_Resize == FALSE ) dgList->dg_Resize = CheckColumnResize( hwnd, xPos, yPos, &dgList->dg_Column, &rect ); // Check for clicked columns if ( !dgList->dg_Resize ) dgList->dg_Click = CheckColumnClick( hwnd, xPos, yPos, &dgList->dg_Column ); if ( dgList->dg_Click ) dgList->dg_Columns[dgList->dg_Column].pressed = true; // Check for selected rows if ( !dgList->dg_Click ) { if ( CheckRows( hwnd, xPos, yPos, &dgList->dg_Row ) ) { // Send notification to the parent window SendMessage( dgList->dg_hParent, WM_COMMAND, MAKEWPARAM(0,DGM_ITEMCHANGED), (LPARAM)hwnd ); } } InvalidateRect( hwnd, NULL, FALSE ); UpdateWindow(hwnd); SetFocus(hwnd); // Capture mouse SetCapture(hwnd); } } break; case WM_LBUTTONUP: { int scrollX = GetScrollPos( hwnd, SB_HORZ ); int scrollY = GetScrollPos( hwnd, SB_VERT ); int xPos = LOWORD(lParam) + scrollX; int yPos = HIWORD(lParam) + scrollY; DG_LIST* dgList = GetDGGrid(hwnd); if ( dgList != NULL ) { for ( int i=0; idg_ColumnNumber; i++ ) dgList->dg_Columns[i].pressed = false; if ( dgList->dg_Resize ) { // Send notification to the parent window SendMessage( dgList->dg_hParent, WM_COMMAND, MAKEWPARAM(0,DGM_COLUMNRESIZED), (LPARAM)hwnd ); } RECT rect; dgList->dg_Resize = FALSE; if ( ( dgList->dg_Click ) && ( dgList->dg_EnableSort ) ) { SortDGItems( hwnd, dgList->dg_Column ); dgList->dg_Click = FALSE; // Send notification to the parent window SendMessage( dgList->dg_hParent, WM_COMMAND, MAKEWPARAM(0,DGM_COLUMNCLICKED), (LPARAM)hwnd ); } dgList->dg_Cursor = CheckColumnResize( hwnd, xPos, yPos, &dgList->dg_Column, &rect ); InvalidateRect( hwnd, NULL, FALSE ); UpdateWindow(hwnd); // Release mouse ReleaseCapture(); } } break; case WM_NCLBUTTONUP: { DG_LIST* dgList = GetDGGrid(hwnd); if ( dgList != NULL ) { for ( int i=0; idg_ColumnNumber; i++ ) dgList->dg_Columns[i].pressed = false; if ( dgList->dg_Resize ) { // Send notification to the parent window SendMessage( dgList->dg_hParent, WM_COMMAND, MAKEWPARAM(0,DGM_COLUMNRESIZED), (LPARAM)hwnd ); } dgList->dg_Resize = FALSE; if ( ( dgList->dg_Click ) && ( dgList->dg_EnableSort ) ) { SortDGItems( hwnd, dgList->dg_Column ); dgList->dg_Click = FALSE; // Send notification to the parent window SendMessage( dgList->dg_hParent, WM_COMMAND, MAKEWPARAM(0,DGM_COLUMNCLICKED), (LPARAM)hwnd ); } InvalidateRect( hwnd, NULL, FALSE ); UpdateWindow(hwnd); } } break; case WM_LBUTTONDBLCLK: { DG_LIST* dgList = GetDGGrid(hwnd); if ( ( dgList != NULL ) && ( dgList->dg_EnableEdit ) ) { int scrollX = GetScrollPos( hwnd, SB_HORZ ); int scrollY = GetScrollPos( hwnd, SB_VERT ); RECT columnRect, clientRect; GetClientRect( hwnd, &clientRect ); GetColumnRect( hwnd, 0, &columnRect ); int offsetLeft = scrollX + clientRect.left; int offsetRight = scrollX + (clientRect.right-clientRect.left); int offsetTop = scrollY + clientRect.top + (columnRect.bottom-columnRect.top); int offsetBottom = scrollY + (clientRect.bottom - clientRect.top); GetCellRect( hwnd, &dgList->dg_EditRect ); if ( ( dgList->dg_EditRect.top >= offsetTop ) && ( dgList->dg_EditRect.bottom <= offsetBottom ) && ( dgList->dg_EditRect.right >= offsetLeft ) && ( dgList->dg_EditRect.left <= offsetRight ) ) { if ( !dgList->dg_Rows[dgList->dg_SelectedRow].readOnly[dgList->dg_SelectedColumn] ) { if ( CheckRows( hwnd, LOWORD(lParam) + scrollX, HIWORD(lParam) + scrollY, &dgList->dg_Row ) ) { char text[1024]; GetDGItemText( hwnd, dgList->dg_SelectedRow, dgList->dg_SelectedColumn, text, 1024 ); SetWindowPos( dgList->dg_hEditWnd, HWND_NOTOPMOST, dgList->dg_EditRect.left-scrollX, dgList->dg_EditRect.top-scrollY, dgList->dg_EditRect.right-dgList->dg_EditRect.left, dgList->dg_EditRect.bottom-dgList->dg_EditRect.top, SWP_NOZORDER | SWP_SHOWWINDOW ); SendMessage( dgList->dg_hEditWnd, WM_SETTEXT, (WPARAM)0, (LPARAM)(LPCTSTR)text ); SetFocus(dgList->dg_hEditWnd); dgList->dg_Edit = TRUE; } } } } } break; case WM_MOUSEMOVE: { int scrollX = GetScrollPos( hwnd, SB_HORZ ); int scrollY = GetScrollPos( hwnd, SB_VERT ); int xPos = LOWORD(lParam) + scrollX; int yPos = HIWORD(lParam) + scrollY; DG_LIST* dgList = GetDGGrid(hwnd); if ( dgList != NULL ) { int oldSize, newSize; RECT rect; if ( wParam == MK_LBUTTON ) { if ( ( dgList->dg_Resize ) && ( dgList->dg_EnableResize ) ) { // Get column rectangle GetColumnRect( hwnd, dgList->dg_Column, &rect ); RECT wndRect; GetWindowRect( hwnd, &wndRect ); POINT pt = {LOWORD(lParam), HIWORD(lParam)}; if ( ( pt.x >= 0 ) && ( pt.x <= 2000 ) ) { // Calculate new column size oldSize = rect.right - rect.left; newSize = xPos - rect.left; if ( newSize < 0 ) newSize = 2; // Resize DataGrid column dgList->dg_Columns[dgList->dg_Column].columnWidth = newSize; RecalcWindow(hwnd); RECT clientRect, columnRect; GetColumnRect( hwnd, 0, &columnRect ); GetClientRect( hwnd, &clientRect ); clientRect.top += (columnRect.bottom-columnRect.top); InvalidateRect( hwnd, NULL, TRUE ); UpdateWindow(hwnd); dgList->dg_Cursor = TRUE; } } else dgList->dg_Cursor = FALSE; } else if ( dgList->dg_EnableResize ) dgList->dg_Cursor = CheckColumnResize( hwnd, xPos, yPos, &dgList->dg_Column, &rect ); } } break; case WM_SETCURSOR: { DG_LIST* dgList = GetDGGrid(hwnd); if ( dgList != NULL ) { // Check cursor flag if ( ( dgList->dg_Cursor ) && ( LOWORD(lParam) == HTCLIENT ) ) SetCursor(LoadCursor(NULL, IDC_SIZEWE)); else SetCursor(LoadCursor(NULL, IDC_ARROW)); } } break; case WM_KEYDOWN: { DG_LIST* dgList = GetDGGrid(hwnd); if ( dgList != NULL ) { if ( dgList->dg_Selected != NULL ) { /* KEY_DOWN pressed */ if ( int(wParam) == 40 ) { // Select next item SelectNextItem( hwnd, dgList->dg_Selected ); EnsureVisible( hwnd, dgList->dg_SelectedRow, dgList->dg_SelectedColumn ); } /* KEY_UP pressed */ else if ( int(wParam) == 38 ) { // Select previous item SelectPreviousItem( hwnd, dgList->dg_Selected ); EnsureVisible( hwnd, dgList->dg_SelectedRow, dgList->dg_SelectedColumn ); } /* KEY_LEFT pressed */ else if ( int(wParam) == 37 ) { // Select previous subitem SelectPreviousSubitem(hwnd); EnsureVisible( hwnd, dgList->dg_SelectedRow, dgList->dg_SelectedColumn ); } /* KEY_RIGHT pressed */ else if ( int(wParam) == 39 ) { // Select next subitem SelectNextSubitem(hwnd); EnsureVisible( hwnd, dgList->dg_SelectedRow, dgList->dg_SelectedColumn ); } /* KEY_PAGEUP pressed */ else if ( int(wParam) == 33 ) { // Scroll page up int scrollCode = SB_PAGEUP; short int pos = GetScrollPos( hwnd, SB_VERT ); SendMessage( hwnd, WM_VSCROLL, MAKEWPARAM(scrollCode,pos), (LPARAM)NULL ); } /* KEY_PAGEDOWN pressed */ else if ( int(wParam) == 34 ) { // Scroll page up int scrollCode = SB_PAGEDOWN; short int pos = GetScrollPos( hwnd, SB_VERT ); SendMessage( hwnd, WM_VSCROLL, MAKEWPARAM(scrollCode,pos), (LPARAM)NULL ); } // Send notification to the parent window SendMessage( dgList->dg_hParent, WM_COMMAND, MAKEWPARAM(0,DGM_ITEMCHANGED), (LPARAM)hwnd ); InvalidateRect( hwnd, NULL, TRUE ); UpdateWindow(hwnd); } } } break; default: return DefWindowProc( hwnd, message, wParam, lParam ); } return 0; } void DrawColumns(HWND hWnd) { int offsetX=0, offsetY=0; RECT rect; SIZE size; TEXTMETRIC tm; DRAWTEXTPARAMS dtp; dtp.cbSize = sizeof(DRAWTEXTPARAMS); dtp.iLeftMargin = 5; dtp.iRightMargin = 5; dtp.iTabLength = 0; // Find DataGrid DG_LIST* dgList = GetDGGrid(hWnd); HDC hDC = dgList->dg_hMemDC; int scrollX = GetScrollPos( hWnd, SB_HORZ ); offsetX -= scrollX; HFONT hOldFont = (HFONT)SelectObject( hDC, dgList->dg_hColumnFont ); COLORREF oldTxtColor = SetTextColor( hDC, dgList->dg_ColumnTextColor ); SetBkMode( hDC, TRANSPARENT ); for ( int i=0; idg_ColumnNumber; i++ ) { // Get column text dimensions GetTextExtentPoint( hDC, dgList->dg_Columns[i].columnText, strlen(dgList->dg_Columns[i].columnText), &size ); GetTextMetrics( hDC, &tm ); // Set column rectangle rect.left = offsetX; rect.top = offsetY; rect.right = rect.left + dgList->dg_Columns[i].columnWidth; rect.bottom = rect.top + size.cy + tm.tmInternalLeading; offsetX = rect.right; if ( dgList->dg_EnableSort ) { // Draw sorting column if ( !dgList->dg_Columns[i].pressed ) DrawFrameControl( hDC, &rect, DFC_BUTTON, DFCS_BUTTONPUSH ); else DrawFrameControl( hDC, &rect, DFC_BUTTON, DFCS_BUTTONPUSH | DFCS_FLAT ); } else { // Draw non-sorting column rect.bottom += 2; DrawEdge( hDC, &rect, EDGE_ETCHED, BF_RECT | BF_MIDDLE | BF_ADJUST ); } // Draw column text DrawTextEx( hDC, dgList->dg_Columns[i].columnText, strlen(dgList->dg_Columns[i].columnText), &rect, dgList->dg_Columns[i].textAlign | DT_VCENTER | DT_SINGLELINE | DT_WORD_ELLIPSIS, &dtp ); } SetTextColor( hDC, oldTxtColor ); SetBkMode( hDC, OPAQUE ); SelectObject( hDC, hOldFont ); } void DrawRows(HWND hWnd) { int offsetX=0, offsetY=0, offY=0; RECT rect, colRect, clientRect; SIZE size; TEXTMETRIC tm; DRAWTEXTPARAMS dtp; dtp.cbSize = sizeof(DRAWTEXTPARAMS); dtp.iLeftMargin = 5; dtp.iRightMargin = 5; dtp.iTabLength = 0; // Find DataGrid DG_LIST* dgList = GetDGGrid(hWnd); HDC hDC = dgList->dg_hMemDC; GetClientRect( hWnd, &clientRect ); int scrollX = GetScrollPos( hWnd, SB_HORZ ); offsetX -= scrollX+1; int scrollY = GetScrollPos( hWnd, SB_VERT ); offY -= scrollY; // Get column rectangle GetColumnRect( hWnd, 0, &colRect ); offsetY += (colRect.bottom - colRect.top) - 1; HFONT hOldFont = (HFONT)SelectObject( hDC, dgList->dg_hRowFont ); HPEN hOldPen; HPEN hBgPen = CreatePen( PS_SOLID, 1, DGBGR_COLOR ); if ( dgList->dg_EnableGrid ) hOldPen = (HPEN)SelectObject( hDC, dgList->dg_hCellPen ); else hOldPen = (HPEN)SelectObject( hDC, hBgPen ); COLORREF oldTxtColor = SetTextColor( hDC, dgList->dg_RowTextColor ); SetBkMode( hDC, TRANSPARENT ); // Get row clipping boundaries RECT rRect, cRect; GetRowRect( hWnd, 0, &rRect ); GetClientRect( hWnd, &cRect ); int rowOffsetT = scrollY / (rRect.bottom-rRect.top-1); if ( ( scrollY % (rRect.bottom-rRect.top-1) != 0 ) && ( rowOffsetT > 0 ) ) rowOffsetT--; offY += rowOffsetT*(rRect.bottom-rRect.top-1); int rowOffsetB = (scrollY + cRect.bottom-cRect.top) / (rRect.bottom-rRect.top-1); if ( rowOffsetB > dgList->dg_RowNumber ) rowOffsetB = dgList->dg_RowNumber; for ( int i=rowOffsetT; idg_ColumnNumber; j++ ) { // Get column rectangle GetColumnRect( hWnd, j, &colRect ); // Get row text dimensions GetTextExtentPoint( hDC, dgList->dg_Rows[i].rowText[j], strlen(dgList->dg_Rows[i].rowText[j]), &size ); GetTextMetrics( hDC, &tm ); // Set row rectangle rect.left = offsetX; rect.top = offsetY + offY; rect.right = rect.left + (colRect.right-colRect.left+1); rect.bottom = rect.top + size.cy + tm.tmInternalLeading; offsetX = rect.right - 1; // If item is full or partially visible if ( ( ( rect.top >= offsetY ) && ( rect.bottom <= clientRect.bottom ) ) || ( ( rect.top < clientRect.bottom ) && ( rect.bottom > clientRect.bottom ) ) || ( ( rect.top < offsetY ) && ( rect.bottom > offsetY ) ) ) { // Check if row is selected if ( dgList->dg_Rows[i].selected == false ) { // Draw row HBRUSH hBgBrush; if ( !dgList->dg_Rows[i].readOnly[j] ) hBgBrush = CreateSolidBrush(dgList->dg_Rows[i].bgColor); else hBgBrush = CreateSolidBrush(DGRONLY_COLOR); HBRUSH hOldBrush = (HBRUSH)SelectObject( hDC, hBgBrush ); Rectangle( hDC, rect.left, rect.top, rect.right, rect.bottom ); SelectObject( hDC, hOldBrush ); DeleteObject(hBgBrush); // Draw row text DrawTextEx( hDC, dgList->dg_Rows[i].rowText[j], strlen(dgList->dg_Rows[i].rowText[j]), &rect, dgList->dg_Rows[i].textAlign[j] | DT_VCENTER | DT_SINGLELINE | DT_WORD_ELLIPSIS, &dtp ); } else { // Draw row HBRUSH hSelectionBrush = CreateSolidBrush(RGB(220,230,250)); HBRUSH hOldBrush = (HBRUSH)SelectObject( hDC, hSelectionBrush ); Rectangle( hDC, rect.left, rect.top, rect.right, rect.bottom ); SelectObject( hDC, hOldBrush ); DeleteObject(hSelectionBrush); // Draw row text COLORREF oldTextColor = SetTextColor( hDC, RGB(130,130,130) ); DrawTextEx( hDC, dgList->dg_Rows[i].rowText[j], strlen(dgList->dg_Rows[i].rowText[j]), &rect, dgList->dg_Rows[i].textAlign[j] | DT_VCENTER | DT_SINGLELINE | DT_WORD_ELLIPSIS, &dtp ); SetTextColor( hDC, oldTextColor ); } if ( ( j == dgList->dg_SelectedColumn ) && ( i == dgList->dg_SelectedRow ) ) { // Draw focus RECT focusRect = rect; focusRect.left += 1; focusRect.right -= 1; focusRect.top += 1; focusRect.bottom -= 1; DrawFocusRect( hDC, &focusRect ); } } } offsetX = -scrollX-1; offY += (rect.bottom - rect.top) - 1; } DeleteObject(hBgPen); SetTextColor( hDC, oldTxtColor ); SetBkMode( hDC, OPAQUE ); SelectObject( hDC, hOldPen ); SelectObject( hDC, hOldFont ); } BOOL CheckColumnResize(HWND hWnd, int x, int y, int* col, RECT* colRect) { BOOL result = FALSE; RECT rect; SIZE size; int offsetX=0, offsetY=0; TEXTMETRIC tm; DG_LIST* dgList = GetDGGrid(hWnd); HDC hDC = GetDC(hWnd); int scrollY = GetScrollPos( hWnd, SB_VERT ); offsetY += scrollY; HFONT hOldFont = (HFONT)SelectObject( hDC, dgList->dg_hColumnFont ); dgList->dg_Cursor = FALSE; for ( int i=0; idg_ColumnNumber; i++ ) { GetTextExtentPoint( hDC, dgList->dg_Columns[i].columnText, strlen(dgList->dg_Columns[i].columnText), &size ); GetTextMetrics( hDC, &tm ); rect.left = offsetX; rect.top = offsetY; rect.right = rect.left + dgList->dg_Columns[i].columnWidth; rect.bottom = rect.top + size.cy + tm.tmInternalLeading; offsetX = rect.right; // Check mouse position if ( ( abs(rect.right-x) < 3 ) && ( rect.top <= y ) && ( rect.bottom >= y ) ) { *col = i; *colRect = rect; result = TRUE; break; } } SelectObject( hDC, hOldFont ); ReleaseDC( hWnd, hDC ); return result; } BOOL CheckColumnClick(HWND hWnd, int x, int y, int* col) { BOOL result = FALSE; POINT pt = {x, y}; RECT rect; SIZE size; int offsetX=0, offsetY=0; TEXTMETRIC tm; DG_LIST* dgList = GetDGGrid(hWnd); HDC hDC = GetDC(hWnd); *col = -1; int scrollY = GetScrollPos( hWnd, SB_VERT ); offsetY += scrollY; HFONT hOldFont = (HFONT)SelectObject( hDC, dgList->dg_hColumnFont ); dgList->dg_Cursor = FALSE; for ( int i=0; idg_ColumnNumber; i++ ) { GetTextExtentPoint( hDC, dgList->dg_Columns[i].columnText, strlen(dgList->dg_Columns[i].columnText), &size ); GetTextMetrics( hDC, &tm ); rect.left = offsetX; rect.top = offsetY; rect.right = rect.left + dgList->dg_Columns[i].columnWidth; rect.bottom = rect.top + size.cy + tm.tmInternalLeading; offsetX = rect.right; // Check mouse position if ( PtInRect( &rect, pt ) ) { *col = i; result = TRUE; break; } } SelectObject( hDC, hOldFont ); ReleaseDC( hWnd, hDC ); return result; } void GetColumnRect(HWND hWnd, int col, RECT* colRect) { SIZE size; int offsetX=0, offsetY=0; TEXTMETRIC tm; // Find DataGrid DG_LIST* dgList = GetDGGrid(hWnd); HDC hDC = GetDC(hWnd); HFONT hOldFont = (HFONT)SelectObject( hDC, dgList->dg_hColumnFont ); for ( int i=0; idg_ColumnNumber; i++ ) { GetTextExtentPoint( hDC, dgList->dg_Columns[i].columnText, strlen(dgList->dg_Columns[i].columnText), &size ); GetTextMetrics( hDC, &tm ); if ( i == col ) { colRect->left = offsetX; colRect->top = offsetY; colRect->right = colRect->left + dgList->dg_Columns[i].columnWidth; colRect->bottom = colRect->top + size.cy + tm.tmInternalLeading; if ( !dgList->dg_EnableSort ) colRect->bottom += 2; break; } offsetX += dgList->dg_Columns[i].columnWidth; } SelectObject( hDC, hOldFont ); ReleaseDC( hWnd, hDC ); } int CDataGrid::GetResizedColumn() { int result; DG_LIST* dgList = GetDGGrid(m_hWnd); if ( dgList != NULL ) result = dgList->dg_Column; // Return resized column return result; } void GetRowRect(HWND hWnd, int row, RECT* rowRect) { int offsetX=0, offsetY=0; RECT colRect; SIZE size; TEXTMETRIC tm; DRAWTEXTPARAMS dtp; dtp.cbSize = sizeof(DRAWTEXTPARAMS); dtp.iLeftMargin = 5; dtp.iRightMargin = 5; dtp.iTabLength = 0; DG_LIST* dgList = GetDGGrid(hWnd); HDC hDC = GetDC(hWnd); int scrollX = GetScrollPos( hWnd, SB_HORZ ); offsetX -= scrollX; // Get column rectangle GetColumnRect( hWnd, 0, &colRect ); offsetY += (colRect.bottom - colRect.top) - 1; HFONT hOldFont = (HFONT)SelectObject( hDC, dgList->dg_hRowFont ); HPEN hOldPen = (HPEN)SelectObject( hDC, dgList->dg_hCellPen ); SetBkMode( hDC, TRANSPARENT ); for ( int i=0; idg_RowNumber; i++ ) { // Get row text dimensions GetTextExtentPoint( hDC, dgList->dg_Rows[i].rowText[0], strlen(dgList->dg_Rows[i].rowText[0]), &size ); GetTextMetrics( hDC, &tm ); if ( i == row ) { // Get row rectangle rowRect->left = offsetX; rowRect->top = offsetY; rowRect->right = rowRect->left + dgList->dg_Columns[0].columnWidth; rowRect->bottom = rowRect->top + size.cy + tm.tmInternalLeading; break; } offsetY += (rowRect->bottom - rowRect->top) - 1; } SetBkMode( hDC, OPAQUE ); SelectObject( hDC, hOldPen ); SelectObject( hDC, hOldFont ); ReleaseDC( hWnd, hDC ); } BOOL CheckRows(HWND hWnd, int x, int y, int* row) { BOOL result = FALSE; DG_LIST* dgList = GetDGGrid(hWnd); HDC hDC = GetDC(hWnd); *row = -1; if ( dgList->dg_Resize ) return result; // Clear selection dgList->dg_Selected = NULL; dgList->dg_SelectedRow = -1; dgList->dg_SelectedColumn = -1; int offsetX=0, offsetY=0, offY=0; RECT rect, colRect; SIZE size; TEXTMETRIC tm; DRAWTEXTPARAMS dtp; dtp.cbSize = sizeof(DRAWTEXTPARAMS); dtp.iLeftMargin = 5; dtp.iRightMargin = 5; dtp.iTabLength = 0; int scrollX = GetScrollPos( hWnd, SB_HORZ ); offsetX -= scrollX; int scrollY = GetScrollPos( hWnd, SB_VERT ); offY -= scrollY; POINT pt = {x-scrollX, y}; // Get column rectangle GetColumnRect( hWnd, 0, &colRect ); offsetY += (colRect.bottom - colRect.top) - 1; HFONT hOldFont = (HFONT)SelectObject( hDC, dgList->dg_hRowFont ); HPEN hOldPen = (HPEN)SelectObject( hDC, dgList->dg_hCellPen ); SetBkMode( hDC, TRANSPARENT ); bool found = false; DG_ROW *curr = dgList->dg_Rows; int rowInd = 0; for ( int j=0; jdg_RowNumber; j++ ) { // Reset row selection flag dgList->dg_Rows[j].selected = false; for ( int i=0; idg_ColumnNumber; i++ ) { // Get row text dimensions GetTextExtentPoint( hDC, dgList->dg_Rows[j].rowText[i], strlen(dgList->dg_Rows[j].rowText[i]), &size ); GetTextMetrics( hDC, &tm ); // Set row rectangle rect.left = offsetX; rect.top = offsetY; rect.right = rect.left + dgList->dg_Columns[i].columnWidth; rect.bottom = rect.top + size.cy + tm.tmInternalLeading; offsetX = rect.right - 1; // Check if row is selected if ( ( PtInRect( &rect, pt ) ) && ( found == false ) ) { *row = rowInd; dgList->dg_Rows[j].selected = true; found = true; // Mark selection dgList->dg_Selected = &dgList->dg_Rows[j]; dgList->dg_SelectedRow = j; dgList->dg_SelectedColumn = i; result = TRUE; } } offsetX = -scrollX; offsetY += (rect.bottom - rect.top) - 1; rowInd++; } SetBkMode( hDC, OPAQUE ); SelectObject( hDC, hOldPen ); SelectObject( hDC, hOldFont ); ReleaseDC( hWnd, hDC ); return result; } void RecalcWindow(HWND hWnd) { int sizeX = 0; int sizeY = 0; DG_LIST* dgList = GetDGGrid(hWnd); if ( dgList != NULL ) { // Get DataGrid window rectangle RECT rect; GetClientRect( hWnd, &rect ); for ( int i=0; idg_ColumnNumber; i++ ) { // Calculate total columns width sizeX += dgList->dg_Columns[i].columnWidth; } // Check horizontal scroll bar visibility int scrollDiff = (rect.right - rect.left) - sizeX; if ( scrollDiff < 0 ) { // Show horizontal scroll bar SCROLLINFO si; si.cbSize = sizeof(SCROLLINFO); si.fMask = SIF_ALL; si.nPos = GetScrollPos( hWnd, SB_HORZ ); si.nMin = 0; si.nMax = sizeX; si.nPage = si.nMax - si.nMin + 1 - abs(scrollDiff); si.nTrackPos = 0; SetScrollInfo( hWnd, SB_HORZ, &si, TRUE ); ShowScrollBar( hWnd, SB_HORZ, TRUE ); } else ShowScrollBar( hWnd, SB_HORZ, FALSE ); if ( dgList->dg_RowNumber > 0 ) { RECT colRect, rowRect; DRAWTEXTPARAMS dtp; dtp.cbSize = sizeof(DRAWTEXTPARAMS); dtp.iLeftMargin = 5; dtp.iRightMargin = 5; dtp.iTabLength = 0; // Get column rectangle GetColumnRect( hWnd, 0, &colRect ); sizeY += (colRect.bottom - colRect.top) - 1; // Get row rectangle GetRowRect( hWnd, 0, &rowRect ); sizeY += dgList->dg_RowNumber * (rowRect.bottom-rowRect.top-1) + 1; // Check vertical scroll bar visibility scrollDiff = (rect.bottom - rect.top) - sizeY; if ( scrollDiff < 0 ) { // Show vertical scroll bar SCROLLINFO si; si.cbSize = sizeof(SCROLLINFO); si.fMask = SIF_ALL; si.nPos = GetScrollPos( hWnd, SB_VERT ); si.nMin = 0; si.nMax = sizeY; si.nPage = si.nMax - si.nMin + 1 - abs(scrollDiff); si.nTrackPos = 0; SetScrollInfo( hWnd, SB_VERT, &si, TRUE ); ShowScrollBar( hWnd, SB_VERT, TRUE ); } else ShowScrollBar( hWnd, SB_VERT, FALSE ); } else ShowScrollBar( hWnd, SB_VERT, FALSE ); } } BOOL CDataGrid::InsertItem(char* itemText, int textAlign) { BOOL result = FALSE; DG_LIST* dgList = GetDGGrid(m_hWnd); if ( dgList != NULL ) { if ( dgList->dg_RowNumber < DG_MAXROW ) { dgList->dg_RowNumber++; if ( dgList->dg_Rows == NULL ) dgList->dg_Rows = (DG_ROW*)malloc(dgList->dg_RowNumber*sizeof(DG_ROW)); else dgList->dg_Rows = (DG_ROW*)realloc(dgList->dg_Rows, dgList->dg_RowNumber*sizeof(DG_ROW)); dgList->dg_Rows[dgList->dg_RowNumber-1].rowText = new char*[dgList->dg_ColumnNumber]; dgList->dg_Rows[dgList->dg_RowNumber-1].textAlign = new int[dgList->dg_ColumnNumber]; dgList->dg_Rows[dgList->dg_RowNumber-1].readOnly = new bool[dgList->dg_ColumnNumber]; for ( int j=0; jdg_ColumnNumber; j++ ) { dgList->dg_Rows[dgList->dg_RowNumber-1].rowText[j] = new char[strlen(itemText)+1]; strcpy( dgList->dg_Rows[dgList->dg_RowNumber-1].rowText[j], " " ); dgList->dg_Rows[dgList->dg_RowNumber-1].textAlign[j] = textAlign; dgList->dg_Rows[dgList->dg_RowNumber-1].readOnly[j] = false; } strcpy( dgList->dg_Rows[dgList->dg_RowNumber-1].rowText[0], itemText ); dgList->dg_Rows[dgList->dg_RowNumber-1].selected = false; dgList->dg_Rows[dgList->dg_RowNumber-1].bgColor = DGBGR_COLOR; result = TRUE; // Send notification to the parent window SendMessage( dgList->dg_hParent, WM_COMMAND, MAKEWPARAM(0,DGM_ITEMADDED), (LPARAM)m_hWnd ); } } return result; } BOOL CDataGrid::RemoveItem(int row) { BOOL result = FALSE; DG_LIST* dgList = GetDGGrid(m_hWnd); if ( ( row >= 0 ) && ( row < dgList->dg_RowNumber ) ) { dgList->dg_RowNumber--; if ( row <= dgList->dg_RowNumber-1 ) { for ( int i=row; idg_RowNumber; i++ ) memcpy( &dgList->dg_Rows[i], &dgList->dg_Rows[i+1], sizeof(DG_ROW) ); dgList->dg_Rows = (DG_ROW*)realloc(dgList->dg_Rows, dgList->dg_RowNumber*sizeof(DG_ROW)); } else dgList->dg_Rows = (DG_ROW*)realloc(dgList->dg_Rows, dgList->dg_RowNumber*sizeof(DG_ROW)); // Mark selection dgList->dg_Selected = NULL; dgList->dg_SelectedRow = -1; dgList->dg_SelectedColumn = -1; result = TRUE; } // Send notification to the parent window SendMessage( dgList->dg_hParent, WM_COMMAND, MAKEWPARAM(0,DGM_ITEMREMOVED), (LPARAM)m_hWnd ); return result; } void CDataGrid::RemoveAllItems() { DG_LIST* dgList = GetDGGrid(m_hWnd); // Delete all items if ( dgList->dg_Rows != NULL ) { dgList->dg_Edit = FALSE; ShowWindow( dgList->dg_hEditWnd, SW_HIDE ); free(dgList->dg_Rows); dgList->dg_Rows = NULL; dgList->dg_RowNumber = 0; dgList->dg_Row = -1; SetScrollPos( m_hWnd, SB_HORZ, 0, TRUE ); SetScrollPos( m_hWnd, SB_VERT, 0, TRUE ); RecalcWindow(m_hWnd); } InvalidateRect( m_hWnd, NULL, TRUE ); UpdateWindow(m_hWnd); } int CDataGrid::GetSelectedRow() { int result = -1; DG_LIST* dgList = GetDGGrid(m_hWnd); if ( dgList != NULL ) { // Return selected row result = dgList->dg_SelectedRow; } return result; } int CDataGrid::GetSelectedColumn() { int result = -1; DG_LIST* dgList = GetDGGrid(m_hWnd); if ( dgList != NULL ) { // Return selected column result = dgList->dg_SelectedColumn; } return result; } void CDataGrid::Update() { // Update DataGrid window RecalcWindow(m_hWnd); InvalidateRect( m_hWnd, NULL, TRUE ); UpdateWindow(m_hWnd); } void CDataGrid::SetCompareFunction(DGCOMPARE CompareFunction) { DG_LIST* dgList = GetDGGrid(m_hWnd); if ( dgList != NULL ) { // Set custom comparison function dgList->dg_CompareFunc = (DGCOMPARE)CompareFunction; } } void SortDGItems(HWND hWnd, int column) { DG_LIST* dgList = GetDGGrid(hWnd); if ( ( dgList != NULL ) && ( dgList->dg_CompareFunc != NULL ) ) { // Send notification to the parent window SendMessage( dgList->dg_hParent, WM_COMMAND, MAKEWPARAM(0,DGM_STARTSORTING), (LPARAM)hWnd ); // Sort DataGrid items Sort( hWnd, column, dgList->dg_RowNumber ); // Send notification to the parent window SendMessage( dgList->dg_hParent, WM_COMMAND, MAKEWPARAM(0,DGM_ENDSORTING), (LPARAM)hWnd ); // Set selection focus SetDGSelection( hWnd, dgList->dg_SelectedRow, dgList->dg_SelectedColumn ); InvalidateRect( hWnd, NULL, TRUE ); UpdateWindow(hWnd); } } void Sort(HWND hWnd, int col, int size) { int i; DG_ROW temp; DG_LIST* dgList = GetDGGrid(hWnd); for ( i=(size+1/2); i>=0; i-- ) SiftDown( hWnd, i, size-1, col ); for ( i=size-1; i>=1; i-- ) { memcpy( &temp, &dgList->dg_Rows[0], sizeof(DG_ROW) ); memcpy( &dgList->dg_Rows[0], &dgList->dg_Rows[i], sizeof(DG_ROW) ); memcpy( &dgList->dg_Rows[i], &temp, sizeof(DG_ROW) ); SiftDown( hWnd, 0, i-1, col ); } } void SiftDown( HWND hWnd, int root, int bottom, int col ) { int done, maxChild; DG_ROW temp; DG_LIST* dgList = GetDGGrid(hWnd); done = 0; while ( (root*2 < bottom) && (!done) ) { if ( root*2 == bottom ) maxChild = root * 2; else if ( root*2 < bottom ) { if ( dgList->dg_CompareFunc(dgList->dg_Rows[root*2].rowText[col], dgList->dg_Rows[root*2+1].rowText[col], col) > 0 ) maxChild = root * 2; else maxChild = root * 2 + 1; if ( dgList->dg_CompareFunc(dgList->dg_Rows[root].rowText[col], dgList->dg_Rows[maxChild].rowText[col], col) < 0 ) { memcpy( &temp, &dgList->dg_Rows[root], sizeof(DG_ROW) ); memcpy( &dgList->dg_Rows[root], &dgList->dg_Rows[maxChild], sizeof(DG_ROW) ); memcpy( &dgList->dg_Rows[maxChild], &temp, sizeof(DG_ROW) ); root = maxChild; } else done = 1; } else done = 1; } } void SetDGSelection(HWND hWnd, int rowIndex, int columnIndex) { DG_LIST* dgList = GetDGGrid(hWnd); bool found = false; for ( int j=0; jdg_RowNumber; j++ ) { if ( ( dgList->dg_Rows[j].selected ) && ( !found ) ) { // Mark selection dgList->dg_Selected = &dgList->dg_Rows[j]; dgList->dg_SelectedRow = j; dgList->dg_SelectedColumn = columnIndex; found = true; break; } if ( found ) break; } } void SelectNextItem(HWND hWnd, DG_ROW* item) { DG_LIST* dgList = GetDGGrid(hWnd); for ( int i=0; idg_RowNumber; i++ ) { if ( ( (&dgList->dg_Rows[i]) == dgList->dg_Selected ) && ( i < dgList->dg_RowNumber-1 ) ) { // Set selected item dgList->dg_Rows[i].selected = false; dgList->dg_Rows[i+1].selected = true; dgList->dg_Selected = &dgList->dg_Rows[i+1]; dgList->dg_SelectedRow = i+1; break; } } } void SelectPreviousItem(HWND hWnd, DG_ROW* item) { DG_LIST* dgList = GetDGGrid(hWnd); for ( int i=0; idg_RowNumber; i++ ) { if ( ( (&dgList->dg_Rows[i]) == dgList->dg_Selected ) && ( i > 0 ) ) { // Set selected item dgList->dg_Rows[i].selected = false; dgList->dg_Rows[i-1].selected = true; dgList->dg_Selected = &dgList->dg_Rows[i-1]; dgList->dg_SelectedRow = i-1; break; } } } void SelectNextSubitem(HWND hWnd) { DG_LIST* dgList = GetDGGrid(hWnd); // Set selected subitem if ( dgList->dg_SelectedColumn < dgList->dg_ColumnNumber-1 ) dgList->dg_SelectedColumn++; else dgList->dg_SelectedColumn = dgList->dg_ColumnNumber-1; } void SelectPreviousSubitem(HWND hWnd) { DG_LIST* dgList = GetDGGrid(hWnd); // Set selected subitem if ( dgList->dg_SelectedColumn > 0 ) dgList->dg_SelectedColumn--; else dgList->dg_SelectedColumn = 0; } void EnsureVisible(HWND hWnd, int rowIndex, int colIndex) { // Ensure DataGrid item is visible EnsureRowVisible( hWnd, rowIndex ); EnsureColumnVisible( hWnd, colIndex ); } void EnsureRowVisible(HWND hWnd, int rowIndex) { // Check DataGrid row visibility DG_LIST* dgList = GetDGGrid(hWnd); RECT rowRect, clientRect, columnRect; GetClientRect( hWnd, &clientRect ); GetColumnRect( hWnd, 0, &columnRect ); int clientHeight = clientRect.bottom - clientRect.top; GetRowRect( hWnd, 0, &rowRect ); int rowOffsetT = dgList->dg_SelectedRow*(rowRect.bottom-rowRect.top-1); int rowOffsetB = dgList->dg_SelectedRow*(rowRect.bottom-rowRect.top-1) + (columnRect.bottom-columnRect.top-1); int scrollY = GetScrollPos( hWnd, SB_VERT ); if ( scrollY > rowOffsetT ) SetScrollPos( hWnd, SB_VERT, rowOffsetT, TRUE ); else if ( scrollY+clientHeight < rowOffsetB+(rowRect.bottom - rowRect.top) ) { int pos = rowOffsetB + (rowRect.bottom - rowRect.top) - (scrollY+clientHeight); SetScrollPos( hWnd, SB_VERT, scrollY+pos, TRUE ); } } void EnsureColumnVisible(HWND hWnd, int columnIndex) { // Check DataGrid column visibility DG_LIST* dgList = GetDGGrid(hWnd); RECT columnRect, clientRect; GetClientRect( hWnd, &clientRect ); int scrollX = GetScrollPos( hWnd, SB_HORZ ); int columnOffsetL = 0; for ( int i=0; idg_SelectedColumn; i++ ) { GetColumnRect( hWnd, i, &columnRect ); columnOffsetL += (columnRect.right-columnRect.left); } GetColumnRect( hWnd, dgList->dg_SelectedColumn, &columnRect ); int columnOffsetR = columnOffsetL + (columnRect.right-columnRect.left); if ( scrollX + clientRect.left > columnOffsetL ) { int pos = scrollX + clientRect.left - columnOffsetL; SetScrollPos( hWnd, SB_HORZ, columnOffsetL, TRUE ); } else if ( scrollX+clientRect.right < columnOffsetR ) { int pos = columnOffsetR - (scrollX+clientRect.right); SetScrollPos( hWnd, SB_HORZ, scrollX+pos, TRUE ); } } void CDataGrid::SetItemBgColor(int rowIndex, COLORREF bgColor) { DG_LIST* dgList = GetDGGrid(m_hWnd); if ( ( rowIndex >= 0 ) && ( rowIndex < dgList->dg_RowNumber ) ) { dgList->dg_Rows[rowIndex].bgColor = bgColor; InvalidateRect( m_hWnd, NULL, TRUE ); UpdateWindow(m_hWnd); } } BOOL AddDGGrid(HWND hWnd, HWND hParent) { BOOL result = FALSE; // Check number of created DataGrids if ( g_DGGridNumber < DG_MAXGRID ) { // Create new DataGrid DG_LIST* newDGGrid = new DG_LIST; newDGGrid->dg_hWnd = hWnd; newDGGrid->dg_hParent = hParent; newDGGrid->dg_Columns = NULL; newDGGrid->dg_Rows = NULL; newDGGrid->dg_ColumnNumber = 0; newDGGrid->dg_RowNumber = 0; newDGGrid->dg_Column = -1; newDGGrid->dg_Row = -1; newDGGrid->dg_SelectedColumn = -1; newDGGrid->dg_SelectedRow = -1; newDGGrid->dg_Selected = NULL; newDGGrid->dg_Cursor = FALSE; newDGGrid->dg_Resize = FALSE; newDGGrid->dg_Click = FALSE; newDGGrid->dg_hColumnFont = NULL; newDGGrid->dg_hRowFont = NULL; newDGGrid->dg_hBgBrush = NULL; newDGGrid->dg_hCellPen = NULL; newDGGrid->dg_hMemBitmap = NULL; newDGGrid->dg_hMemDC = NULL; newDGGrid->dg_hOldMemBitmap = NULL; newDGGrid->dg_Edit = FALSE; newDGGrid->dg_hEditWnd = NULL; newDGGrid->dg_EnableEdit = TRUE; newDGGrid->dg_EnableSort = TRUE; newDGGrid->dg_EnableResize = TRUE; newDGGrid->dg_EnableGrid = TRUE; newDGGrid->dg_CompareFunc = NULL; newDGGrid->dg_ColumnTextColor = DGTXT_COLOR; newDGGrid->dg_RowTextColor = DGTXT_COLOR; newDGGrid->next = NULL; DG_LIST* curr = g_DGList; if ( curr == NULL ) g_DGList = newDGGrid; else { while ( curr->next != NULL ) curr = curr->next; curr->next = newDGGrid; } g_DGGridNumber++; } return result; } void DestroyDGGrid(HWND hWnd) { DG_LIST* dgList = DetachDGGrid(hWnd); if ( dgList != NULL ) { // Delete columns if ( dgList->dg_Columns ) { delete dgList->dg_Columns; dgList->dg_Columns = NULL; } // Delete rows free(dgList->dg_Rows); dgList->dg_Rows = NULL; // Delete column font if ( dgList->dg_hColumnFont ) { DeleteObject(dgList->dg_hColumnFont); dgList->dg_hColumnFont = NULL; } // Delete row font if ( dgList->dg_hRowFont ) { DeleteObject(dgList->dg_hRowFont); dgList->dg_hRowFont = NULL; } // Delete background brush if ( dgList->dg_hBgBrush ) { DeleteObject(dgList->dg_hBgBrush); dgList->dg_hBgBrush = NULL; } // Delete cell pen if ( dgList->dg_hCellPen ) { DeleteObject(dgList->dg_hCellPen); dgList->dg_hCellPen = NULL; } // Delete memory device context if ( dgList->dg_hMemDC ) { SelectObject( dgList->dg_hMemDC, dgList->dg_hOldMemBitmap ); DeleteDC(dgList->dg_hMemDC); dgList->dg_hMemDC = NULL; } // Delete memory bitmap if ( dgList->dg_hMemBitmap ) { DeleteObject(dgList->dg_hMemBitmap); dgList->dg_hMemBitmap = NULL; } g_DGGridNumber--; } } DG_LIST* GetDGGrid(HWND hWnd) { DG_LIST* result = NULL; if ( g_DGGridNumber > 0 ) { // Find DataGrid DG_LIST *dgList = g_DGList; while ( ( dgList != NULL ) && ( dgList->dg_hWnd != hWnd ) ) dgList = dgList->next; result = dgList; } return result; } DG_LIST* DetachDGGrid(HWND hWnd) { DG_LIST* result = NULL; if ( g_DGGridNumber > 0 ) { // Find DataGrid DG_LIST *curr, *prev; curr = g_DGList; while ( ( curr != NULL ) && ( curr->dg_hWnd != hWnd ) ) { prev = curr; curr = curr->next; } if ( curr == g_DGList ) g_DGList = g_DGList->next; else prev->next = curr->next; result = curr; } return result; } void GetCellRect(HWND hWnd, RECT* cellRect) { DG_LIST* dgList = GetDGGrid(hWnd); if ( dgList != NULL ) { RECT rowRect, columnRect; GetColumnRect( hWnd, dgList->dg_SelectedColumn, &columnRect ); GetRowRect( hWnd, 0, &rowRect ); int rowOffset = dgList->dg_SelectedRow * (rowRect.bottom-rowRect.top-1) + (columnRect.bottom-columnRect.top-1); cellRect->left = columnRect.left; cellRect->right = cellRect->left + (columnRect.right-columnRect.left) - 1; cellRect->top = rowOffset + 1; cellRect->bottom = cellRect->top + (rowRect.bottom-rowRect.top-1) - 1; } } void CDataGrid::EnableSort(BOOL enable) { DG_LIST* dgList = GetDGGrid(m_hWnd); if ( dgList != NULL ) { // Set sort flag dgList->dg_EnableSort = enable; } } void CDataGrid::EnableEdit(BOOL enable) { DG_LIST* dgList = GetDGGrid(m_hWnd); if ( dgList != NULL ) { // Set edit flag dgList->dg_EnableEdit = enable; } } void CDataGrid::EnableResize(BOOL enable) { DG_LIST* dgList = GetDGGrid(m_hWnd); if ( dgList != NULL ) { // Set resize flag dgList->dg_EnableResize = enable; } } void CDataGrid::EnableGrid(BOOL enable) { DG_LIST* dgList = GetDGGrid(m_hWnd); if ( dgList != NULL ) { // Set grid flag dgList->dg_EnableGrid = enable; } } void CDataGrid::SetRowTxtColor(COLORREF txtColor) { DG_LIST* dgList = GetDGGrid(m_hWnd); if ( dgList != NULL ) { // Set DataGrid Text color dgList->dg_RowTextColor = txtColor; InvalidateRect( m_hWnd, NULL, TRUE ); UpdateWindow(m_hWnd); } } void CDataGrid::SetColumnTxtColor(COLORREF txtColor) { DG_LIST* dgList = GetDGGrid(m_hWnd); if ( dgList != NULL ) { // Set DataGrid Text color dgList->dg_ColumnTextColor = txtColor; InvalidateRect( m_hWnd, NULL, TRUE ); UpdateWindow(m_hWnd); } } int CDataGrid::GetRowNumber() { int result; DG_LIST* dgList = GetDGGrid(m_hWnd); if ( dgList != NULL ) { // Get DataGrid row number result = dgList->dg_RowNumber; } return result; } void CDataGrid::GetColumnFont(LOGFONT* lf) { DG_LIST* dgList = GetDGGrid(m_hWnd); if ( dgList != NULL ) { // Get DataGrid column font memcpy( lf, &dgList->dg_LFColumnFont, sizeof(LOGFONT) ); } } void CDataGrid::SetColumnFont(LOGFONT* lf) { DG_LIST* dgList = GetDGGrid(m_hWnd); if ( dgList != NULL ) { // Set DataGrid column font memcpy( &dgList->dg_LFColumnFont, lf, sizeof(LOGFONT) ); if ( dgList->dg_hColumnFont ) DeleteObject(dgList->dg_hColumnFont); dgList->dg_hColumnFont = CreateFontIndirect(lf); } } void CDataGrid::GetRowFont(LOGFONT* lf) { DG_LIST* dgList = GetDGGrid(m_hWnd); if ( dgList != NULL ) { // Get DataGrid row font memcpy( lf, &dgList->dg_LFRowFont, sizeof(LOGFONT) ); } } void CDataGrid::SetRowFont(LOGFONT* lf) { DG_LIST* dgList = GetDGGrid(m_hWnd); if ( dgList != NULL ) { // Set DataGrid row font memcpy( &dgList->dg_LFRowFont, lf, sizeof(LOGFONT) ); if ( dgList->dg_hRowFont ) DeleteObject(dgList->dg_hRowFont); dgList->dg_hRowFont = CreateFontIndirect(lf); } } COLORREF CDataGrid::GetColumnTxtColor() { DG_LIST* dgList = GetDGGrid(m_hWnd); if ( dgList != NULL ) { // Get DataGrid column text color return dgList->dg_ColumnTextColor; } else return RGB(0,0,0); } void CDataGrid::GetItemInfo(DG_ITEMINFO* itemInfo) { DG_LIST* dgList = GetDGGrid(m_hWnd); if ( dgList != NULL ) { // Get DataGrid item info if ( ( itemInfo != NULL ) && ( itemInfo->dgItem < dgList->dg_RowNumber ) && ( itemInfo->dgSubitem < dgList->dg_ColumnNumber ) ) { switch ( itemInfo->dgMask ) { case DG_TEXTEDIT: { // Get DataGrid item text GetDGItemText( m_hWnd, itemInfo->dgItem, itemInfo->dgSubitem, itemInfo->dgText, itemInfo->dgTextLen ); } break; case DG_TEXTALIGN: { // Get DataGrid item text alignment itemInfo->dgTextAlign = dgList->dg_Rows[itemInfo->dgItem].textAlign[itemInfo->dgSubitem]; } break; case DG_TEXTHIGHLIGHT: { // Get DataGrid row selection state itemInfo->dgSelected = dgList->dg_Rows[itemInfo->dgItem].selected; } break; case DG_TEXTRONLY: { // Get DataGrid item edit mode itemInfo->dgReadOnly = dgList->dg_Rows[itemInfo->dgItem].readOnly[itemInfo->dgSubitem]; } break; case DG_TEXTBGCOLOR: { // Get DataGrid row background color itemInfo->dgBgColor = dgList->dg_Rows[itemInfo->dgItem].bgColor; } break; } } } } COLORREF CDataGrid::GetRowTxtColor() { DG_LIST* dgList = GetDGGrid(m_hWnd); if ( dgList != NULL ) { // Get DataGrid row text color return dgList->dg_RowTextColor; } else return RGB(0,0,0); } void CDataGrid::EnsureVisible(int row, int column) { DG_LIST* dgList = GetDGGrid(m_hWnd); if ( dgList != NULL ) { // Ensure DataGrid item is full visible ::EnsureVisible( m_hWnd, row, column ); } } void CDataGrid::SelectItem(int row, int column) { DG_LIST* dgList = GetDGGrid(m_hWnd); if ( dgList != NULL ) { // Set DataGrid selection for ( int j=0; jdg_RowNumber; j++ ) { if ( j == row ) { // Mark selection dgList->dg_Rows[j].selected = true; dgList->dg_Selected = &dgList->dg_Rows[j]; dgList->dg_SelectedRow = row; dgList->dg_SelectedColumn = column; } else dgList->dg_Rows[j].selected = false; } } }