/////////////////////////////////////////////////////////////////////
// CursorGridCtrl.cpp : implementation file
//
// Written by Dmitriy Oshchepkov aka DmitrO, mailto: dmitro@russian.ru
// Copyright (c) 2005-2008. All Rights Reserved.
// Based on CVirtualGridCtrl class written by Uri Mellshior, (c) 2002-2003.
// Version: 1.0
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "CursorGridCtrl.h"
#include "MemDC.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define DEFAULT_LEFT_TEXT_MARGIN 2
#define DEFAULT_RIGHT_TEXT_MARGIN 2
#define DEFAULT_TOP_TEXT_MARGIN 1
#define DEFAULT_BOTTOM_TEXT_MARGIN 3

#define TIP_TIMEOUT 5000
#define RESIZE_AREA_WIDTH 3
#define RESIZE_MIN_WIDTH 17
#define QUICKSEARCH_TIMEOUT 900
#define TIMER_ID_UPDATE 0x00000001
#define COLOR_ALTERNATION RGB(0xF0, 0xF0, 0xF0)

#define COLUMN_HELPER_CX (16 + 1 + 1)
#define COLUMN_HELPER_CY (16 + 1 + 1)
#define COLUMN_HELPER_MARGIN 8
#define COLUMN_HELPER_MINROWWIDTH 70

#define TIMER_ID_AUTOHIDE 0x00000002
#define HELPER_DISPLAY_TIMEOUT 900

#define TIMER_ID_SCROLL 0x00000003
#define SCROLL_TIMEOUT 100

#define DRAG_SCROLL_FIELD_WIDTH 30
#define DRAG_SCROLL_DELAY 900

/////////////////////////////////////////////////////////////////////////////
// CGridColumn
/////////////////////////////////////////////////////////////////////////////

CGridColumn::CGridColumn()
{
    m_nMinWidth = 50;
    m_bVisible = TRUE;
    m_HTextAlignment = htaAuto;
    
    m_crText = CLR_DEFAULT;
    m_crBack = CLR_DEFAULT;
    m_pFont = NULL;
    m_pHeaderFont = NULL;
    m_pRowsImages = NULL;
    m_nImageWidth = 0;
    m_bFixedWidth = FALSE;
    m_crHeaderText = CLR_DEFAULT;
    m_crHeaderBack = CLR_DEFAULT;
    m_OrderMarker = omNone;
    m_bChangeVisible = TRUE;
}

CGridColumn::~CGridColumn()
{
}

void CGridColumn::UpdateColumns(BOOL bRefill /*= FALSE*/)
{
    CGridColumns* pGridColumns = static_cast<CGridColumns*>(GetCollection());
    if (pGridColumns)
    {
        pGridColumns->RecalcVisibles();
        if (pGridColumns->m_pGrid)
        {
            CCursorGridCtrl* pGrid = pGridColumns->m_pGrid;
            if (bRefill)
                pGrid->FillRows(pGrid->m_nTopRowIndex, pGrid->m_nVisibleRows);
            pGrid->Invalidate();
        }
    }
}

void CGridColumn::RedrawGrid()
{
    CGridColumns* pGridColumns = static_cast<CGridColumns*>(GetCollection());
    if (pGridColumns && m_bVisible)
        if (pGridColumns->m_pGrid)
            pGridColumns->m_pGrid->InvalidateRect(pGridColumns->m_pGrid->GetGridRect());
}

void CGridColumn::RedrawHeader()
{
    CGridColumns* pGridColumns = static_cast<CGridColumns*>(GetCollection());
    if (pGridColumns && m_bVisible)
        if (pGridColumns->m_pGrid)
            pGridColumns->m_pGrid->InvalidateRect(GetBoundsRect());
}

CRect CGridColumn::GetBoundsRect()
{
    CRect rect = m_rect;
    CGridColumns* pGridColumns = static_cast<CGridColumns*>(GetCollection());
    rect.OffsetRect(-pGridColumns->m_pGrid->GetOrigin(), 0);
    return rect;
}

int CGridColumn::GetWidth() const
{
    return m_nMinWidth;
}

void CGridColumn::SetWidth(int nWidth)
{
    if (m_nMinWidth < 0)
        m_nMinWidth = 0;
    if (m_nMinWidth > 10000)
        m_nMinWidth = 10000;
    if (m_nMinWidth == nWidth)
        return;
    m_nMinWidth = nWidth;
    UpdateColumns();
}

CString CGridColumn::GetCaption() const
{
    return m_strCaption;
}

void CGridColumn::SetCaption(LPCTSTR szCaption)
{
    if (!m_strCaption.Compare(szCaption))
        return;
    m_strCaption = szCaption;
    RedrawHeader();
}

BOOL CGridColumn::GetVisible() const
{
    return m_bVisible;
}

void CGridColumn::SetVisible(BOOL bVisible)
{
    if (m_bVisible == bVisible)
        return;
    m_bVisible = bVisible;

    CGridColumns* pGridColumns = static_cast<CGridColumns*>(GetCollection());
    if (pGridColumns)
    {
        if (m_bVisible)
            pGridColumns->QueryDataField(this);
        else
            pGridColumns->ReleaseDataField(this);
    }
    UpdateColumns(bVisible);
}

HorizTextAlignment CGridColumn::GetHTextAlignment() const
{
    return m_HTextAlignment;
}

void CGridColumn::SetHTextAlignment(HorizTextAlignment textAlignment)
{
    if (m_HTextAlignment == textAlignment)
        return;
    m_HTextAlignment = textAlignment;
    RedrawGrid();
}

COLORREF CGridColumn::GetTextColor() const
{
    return m_crText;
}

void CGridColumn::SetTextColor(COLORREF crTextColor)
{
    if (m_crText == crTextColor)
        return;
    m_crText = crTextColor;
    RedrawGrid();
}

COLORREF CGridColumn::GetBackColor() const
{
    return m_crBack;
}

void CGridColumn::SetBackColor(COLORREF crBackColor)
{
    if (m_crBack == crBackColor)
        return;
    m_crBack = crBackColor;
    RedrawGrid();
}

CFont* CGridColumn::GetFont() const
{
    return m_pFont;
}

void CGridColumn::SetFont(CFont* pFont)
{
    if (m_pFont == pFont)
        return;
    m_pFont = pFont;
    RedrawGrid();
}

CFont* CGridColumn::GetHeaderFont() const
{
    return m_pHeaderFont;
}

void CGridColumn::SetHeaderFont(CFont* pFont)
{
    if (m_pHeaderFont == pFont)
        return;
    m_pHeaderFont = pFont;
    RedrawHeader();
}

CImageList* CGridColumn::GetRowsImages() const
{
    return m_pRowsImages;
}

void CGridColumn::SetRowsImages(CImageList* pRowsImages)
{
    if (m_pRowsImages == pRowsImages)
        return;
    m_pRowsImages = pRowsImages;
    if (m_pRowsImages)
    {
        IMAGEINFO imageInfo;
        if (m_pRowsImages->GetImageInfo(0, &imageInfo))
            m_nImageWidth = imageInfo.rcImage.right - imageInfo.rcImage.left;
        else
            m_nImageWidth = 0;
    }
    RedrawGrid();
}

BOOL CGridColumn::GetFixedWidth() const
{
    return m_bFixedWidth;
}

void CGridColumn::SetFixedWidth(BOOL bFixedWidth /*= TRUE*/)
{
    if (m_bFixedWidth == bFixedWidth)
        return;
    m_bFixedWidth = bFixedWidth;
    UpdateColumns();
}

COLORREF CGridColumn::GetHeaderTextColor() const
{
    return m_crHeaderText;
}

void CGridColumn::SetHeaderTextColor(COLORREF crTextColor)
{
    if (m_crHeaderText == crTextColor)
        return;
    m_crHeaderText = crTextColor;
    RedrawHeader();
}

COLORREF CGridColumn::GetHeaderBackColor() const
{
    return m_crHeaderBack;
}

void CGridColumn::SetHeaderBackColor(COLORREF crBackColor)
{
    if (m_crHeaderBack == crBackColor)
        return;
    m_crHeaderBack = crBackColor;
    RedrawHeader();
}

LPCSTR CGridColumn::GetDataField() const
{
    return m_strDataField;
}

void CGridColumn::SetDataField(LPCSTR szDataField)
{
    if (!stricmp(m_strDataField, szDataField))
        return;

    CGridColumns* pGridColumns = static_cast<CGridColumns*>(GetCollection());
    
    if (m_bVisible && pGridColumns)
        pGridColumns->ReleaseDataField(this);

    m_strDataField = szDataField;

    if (m_bVisible && pGridColumns)
        pGridColumns->QueryDataField(this);
}

OrderMarker CGridColumn::GetOrderMarker() const
{
    return m_OrderMarker;
}

void CGridColumn::SetOrderMarker(OrderMarker orderMarker)
{
    if (m_OrderMarker == orderMarker)
        return;
    m_OrderMarker = orderMarker;
    RedrawHeader();
}

BOOL CGridColumn::GetChangeVisible() const
{
    return m_bChangeVisible;
}

void CGridColumn::SetChangeVisible(BOOL bChangeVisible /*= TRUE*/)
{
    if (m_bChangeVisible == bChangeVisible)
        return;
    m_bChangeVisible = bChangeVisible;
    if (!m_bVisible)
    {
        CGridColumns* pGridColumns = static_cast<CGridColumns*>(GetCollection());
        if (pGridColumns && pGridColumns->m_pGrid)
        {
            if (pGridColumns->m_pGrid->m_ColumnHelper.IsDisplay())
                pGridColumns->m_pGrid->m_ColumnHelper.Show();
        }
    }
}

/////////////////////////////////////////////////////////////////////////////
// CGridColumns
/////////////////////////////////////////////////////////////////////////////

CGridColumns::CGridColumns()
{
    m_pGrid = NULL;
    m_nMinWidth = 0;
    m_nResizableMinWidth = 0;
}

CGridColumns::~CGridColumns()
{
    Clear();
}

void CGridColumns::OnAdd(CCollectionItem* pItem)
{
    CGridColumn* pGridColumn = static_cast<CGridColumn*>(pItem);
    QueryDataField(pGridColumn);
    RecalcVisibles();
    m_pGrid->FillRows(m_pGrid->m_nTopRowIndex, m_pGrid->m_nVisibleRows);
}

void CGridColumns::OnRemove(CCollectionItem* pItem, int nIndexWas)
{
    CGridColumn* pGridColumn = static_cast<CGridColumn*>(pItem);
    ReleaseDataField(pGridColumn);
    pGridColumn->Release();

    if (nIndexWas != -1)
    {
        for (int i = 0; i < m_pGrid->m_rows.GetSize(); i++)
        {
            CGridRow* pGridRow = m_pGrid->m_rows[i];
            delete pGridRow->m_cells[nIndexWas];
            pGridRow->m_cells.RemoveAt(nIndexWas);
        }
        if (nIndexWas > GetUpperBound() && nIndexWas == m_pGrid->m_nCurrentColumn)
            m_pGrid->m_nCurrentColumn = -1;
        RecalcVisibles();
    }
}

void CGridColumns::OnRename(CCollectionItem* pItem)
{
}

int CGridColumns::GetSize() const
{
    return GetCount();
};

CGridColumn* CGridColumns::Add(LPCSTR szName)
{
    CGridColumn* pGridColumn = NewColumn();
    pGridColumn->SetName(szName);
    CCollection::Add(pGridColumn);
    return pGridColumn;
}

CGridColumn* CGridColumns::Insert(int nIndex, LPCSTR szName)
{
    if (nIndex < 0 || nIndex > GetCount())
        return NULL;

    CGridColumn* pGridColumn = NewColumn();
    pGridColumn->SetName(szName);
    CCollection::Insert(nIndex, pGridColumn);
    return pGridColumn;
}

CGridColumn* CGridColumns::GetAt(int nIndex) const
{
    if (nIndex < 0 || nIndex > GetUpperBound())
        return NULL;
    else
        return static_cast<CGridColumn*>(GetItem(nIndex));
}

CGridColumn* CGridColumns::GetAt(LPCSTR szName) const
{
    CCollectionItem* pCollectionItem = GetItem(szName);
    if (pCollectionItem)
        return static_cast<CGridColumn*>(pCollectionItem);
    else
        return NULL;
}

int CGridColumns::IndexOf(LPCSTR szName) const
{
    return GetIndex(szName);
}

int CGridColumns::IndexOf(CGridColumn* pColumn) const
{
    return GetIndex(pColumn);
}

BOOL CGridColumns::Remove(int nIndex)
{
    if (nIndex < 0 || nIndex > GetUpperBound())
        return FALSE;

    CCollection::Remove(nIndex);
    return TRUE;
}

void CGridColumns::RecalcVisibles()
{
    m_nMinWidth = 0;
    m_nResizableMinWidth = 0;
    for (int i = 0; i < GetCount(); i++)
    {
        CGridColumn* pColumn = static_cast<CGridColumn*>(GetItem(i));
        if (pColumn->m_bVisible)
        {
            m_nMinWidth += pColumn->m_nMinWidth;
            if (!pColumn->m_bFixedWidth)
                m_nResizableMinWidth += pColumn->m_nMinWidth;
        }
    };
    if (m_pGrid)
        m_pGrid->ColumnsChanged();
}

void CGridColumns::RemoveAll()
{
    Clear();
    for (int i = 0; i < m_pGrid->m_rows.GetSize(); i++)
        m_pGrid->m_rows[i]->m_cells.RemoveAll();
    
    m_nMinWidth = 0;
    m_nResizableMinWidth = 0;
    m_pGrid->m_nCurrentColumn = -1;
    m_pGrid->Invalidate();
}

void CGridColumns::QueryDataField(CGridColumn* pColumn)
{
    if (m_pGrid && !pColumn->m_strDataField.IsEmpty() && m_pGrid->m_pProvider)
        m_pGrid->m_pProvider->QueryField(pColumn->m_strDataField);
}

void CGridColumns::ReleaseDataField(CGridColumn* pColumn)
{
    if (m_pGrid && !pColumn->m_strDataField.IsEmpty() && m_pGrid->m_pProvider)
        m_pGrid->m_pProvider->ReleaseField(pColumn->m_strDataField);
}

BOOL CGridColumns::Move(int nIndex, int nOffset)
{
    if (!m_pGrid)
        return TRUE;
    
    if (nIndex < 0 || nIndex >= GetCount() || nOffset == 0)
        return FALSE;
    int nNewIndex = nIndex + nOffset;
    if (nNewIndex < 0 || nNewIndex >= GetCount())
        return FALSE;
    CCollection::Move(nIndex, nOffset);
    for (int i = 0; i < m_pGrid->m_rows.GetSize(); i++)
        m_pGrid->m_rows[i]->Move(nIndex, nOffset);
    m_pGrid->RecalcColumsWidth();
    return TRUE;
}

int CGridColumns::GetVisibleWidth()
{
	int nWidth = 0;
	for (int i = 0; i < GetSize(); i++)
	{
		CGridColumn* pGridColumn = GetAt(i);
		if (pGridColumn->m_bVisible)
			nWidth += pGridColumn->m_rect.Width();
	}
	return nWidth;
}

/////////////////////////////////////////////////////////////////////////////
// CGridItem
/////////////////////////////////////////////////////////////////////////////

#define CGI_TEXT                0x0001
#define CGI_IMAGE               0x0002
#define CGI_CHECKBOX            0x0004

#define CGI_TEXT_H_ALIGN        0x0018
#define CGI_TEXT_AUTO           0x0000
#define CGI_TEXT_LEFT           0x0008
#define CGI_TEXT_RIGHT          0x0010
#define CGI_TEXT_CENTER         0x0018

#define CGI_CHECKBOX_STATE      0x0060
#define CGI_CHECKBOX_UNCHECKED  0x0000
#define CGI_CHECKBOX_CHECKED    0x0020
#define CGI_CHECKBOX_UNDEFINED  0x0040

CGridItem::CGridItem(CGridColumn* pGridColumn) : m_pGridColumn(pGridColumn)
{
    m_wFlagsMask = 0;
    m_nImageNumber = -1;

    m_crText = CLR_DEFAULT;
    m_crBack = CLR_DEFAULT;
    m_pFont = NULL;
}

CGridItem::~CGridItem()
{
}

CGridColumn* CGridItem::GetColumn() const
{
    return m_pGridColumn;
}

BOOL CGridItem::GetShowText() const
{
    return m_wFlagsMask & CGI_TEXT != 0;
}

void CGridItem::SetShowText(BOOL bEnable /*= TRUE*/)
{
    if (bEnable)
        m_wFlagsMask |= CGI_TEXT;
    else
        m_wFlagsMask &= ~CGI_TEXT;
}

BOOL CGridItem::GetShowImage() const
{
    return m_wFlagsMask & CGI_IMAGE != 0;
}

void CGridItem::SetShowImage(BOOL bEnable /*= TRUE*/)
{
    if (bEnable)
        m_wFlagsMask |= CGI_IMAGE;
    else
        m_wFlagsMask &= ~CGI_IMAGE;
}

BOOL CGridItem::GetShowCheckBox() const
{
    return m_wFlagsMask & CGI_CHECKBOX != 0;
}

void CGridItem::SetShowCheckBox(BOOL bEnable /*= TRUE*/)
{
    if (bEnable)
        m_wFlagsMask |= CGI_CHECKBOX;
    else
        m_wFlagsMask &= ~CGI_CHECKBOX;
}

LPCSTR CGridItem::GetText() const
{
    return m_strText;
}

void CGridItem::SetText(LPCSTR szText)
{
    m_strText = szText;
}

HorizTextAlignment CGridItem::GetTextHAlign() const
{
    return (HorizTextAlignment)((m_wFlagsMask & CGI_TEXT_H_ALIGN) >> 3);
}

void CGridItem::SetTextHAlign(HorizTextAlignment horizTextAlignment)
{
    m_wFlagsMask &= ~CGI_TEXT_H_ALIGN;
    m_wFlagsMask |= (horizTextAlignment << 3);
}

int CGridItem::GetImageIndex() const
{
    return m_nImageNumber;
}

void CGridItem::SetImageIndex(int nImageIndex)
{
    m_nImageNumber = nImageIndex;
}

int CGridItem::GetCheckValue() const
{
    return (m_wFlagsMask & CGI_CHECKBOX_STATE) >> 5;
}

void CGridItem::SetCheckValue(int nCheckValue)
{
    m_wFlagsMask &= ~CGI_CHECKBOX_STATE;
    m_wFlagsMask |= (nCheckValue << 5);
}

COLORREF CGridItem::GetTextColor() const
{
    return m_crText;
}

void CGridItem::SetTextColor(COLORREF crTextColor)
{
    m_crText = crTextColor;
}

COLORREF CGridItem::GetBackColor() const
{
    return m_crBack;
}

void CGridItem::SetBackColor(COLORREF crBackColor)
{
    m_crBack = crBackColor;
}

CFont* CGridItem::GetFont() const
{
    return m_pFont;
}

void CGridItem::SetFont(CFont* pFont)
{
    m_pFont = pFont;
}

/////////////////////////////////////////////////////////////////////////////
// CGridRow
/////////////////////////////////////////////////////////////////////////////

CGridRow::CGridRow(CGridColumns* pColumns, CDataRow* pDataRow)
    : m_pColumns(pColumns), m_pDataRow(pDataRow)
{
    m_bIsCurrent = false;
    m_bIsSelected = false;

    m_crText = CLR_DEFAULT;
    m_crBack = CLR_DEFAULT;
    m_pFont = NULL;
}

CGridRow::~CGridRow()
{
    for (int i = 0; i < m_cells.GetSize(); i++)
        delete m_cells[i];
    
    if (m_bIsCurrent)
    {
        m_pColumns->m_pGrid->m_nCurrentDataRowIndex = -1;
        m_pColumns->m_pGrid->m_pCurrentDataRowOutOfWindow = m_pDataRow;
    }
    else
        delete m_pDataRow;
}

BOOL CGridRow::CleanUp()
{
    if (!m_cells.GetSize())
        return FALSE;

    for (int i = 0; i < m_cells.GetSize(); i++)
        delete m_cells[i];
    m_cells.RemoveAll();
    m_crText = CLR_DEFAULT;
    m_crBack = CLR_DEFAULT;
    m_pFont = NULL;

    return TRUE;
}

void CGridRow::FillItems()
{
    int i;
    CleanUp();
    for (i = 0; i < m_pColumns->GetSize(); i++)
        m_cells.Add(NewItem(m_pColumns->GetAt(i)));

    OnFillItems();
    
    for (i = 0; i < m_cells.GetSize(); i++)
    {
        CGridColumn* pColumn = m_pColumns->GetAt(i);
        if (!pColumn->m_bVisible)
            continue;
        if (pColumn->m_strDataField.IsEmpty())
            continue;
        int nIndex = m_pDataRow->GetFieldIndexOf(pColumn->m_strDataField);
        if (nIndex == -1)
            continue;

        CGridItem* pGridItem = m_cells[i];
        HorizTextAlignment HAlign = htaAuto;
        switch (m_pDataRow->GetFieldType(nIndex))
        {
        case dtUndefined:
            if (!pGridItem->GetShowText())
            {
                pGridItem->SetShowText();
                m_pDataRow->FormatField(nIndex, pGridItem->m_strText);
            }
            HAlign = htaLeft;
            break;
        case dtText:
            if (!pGridItem->GetShowText())
            {
                pGridItem->SetShowText();
                m_pDataRow->FormatField(nIndex, pGridItem->m_strText);
                pGridItem->m_strText.TrimRight();
            }
            HAlign = htaLeft;
            break;
        case dtNumeric:
            if (!pGridItem->GetShowText())
            {
                pGridItem->SetShowText();
                m_pDataRow->FormatField(nIndex, pGridItem->m_strText);
            }
            HAlign = htaRight;
            break;
        case dtDate:
            if (!pGridItem->GetShowText())
            {
                pGridItem->SetShowText();
                m_pDataRow->FormatField(nIndex, pGridItem->m_strText);
            }
            HAlign = htaLeft;
            break;
        case dtTrue:
            pGridItem->SetShowCheckBox();
            pGridItem->SetCheckValue(1);
            break;
        case dtFalse:
            pGridItem->SetShowCheckBox();
            pGridItem->SetCheckValue(0);
            break;
        }
        if (pGridItem->GetTextHAlign() == htaAuto)
        {
            if (pColumn->m_HTextAlignment != htaAuto)
                pGridItem->SetTextHAlign(pColumn->m_HTextAlignment);
            else
                pGridItem->SetTextHAlign(HAlign);
        }
    }
}

CCursorGridCtrl* CGridRow::GetGrid() const
{
    return m_pColumns->m_pGrid;
}

CDataRow* CGridRow::GetDataRow() const
{
    return m_pDataRow;
}

CGridItem* CGridRow::GetGridItem(int nIndex) const
{
    return m_cells[nIndex];
}

COLORREF CGridRow::GetTextColor() const
{
    return m_crText;
}

void CGridRow::SetTextColor(COLORREF crTextColor)
{
    m_crText = crTextColor;
}

COLORREF CGridRow::GetBackColor() const
{
    return m_crBack;
}

void CGridRow::SetBackColor(COLORREF crBackColor)
{
    m_crBack = crBackColor;
}

CFont* CGridRow::GetFont() const
{
    return m_pFont;
}

void CGridRow::SetFont(CFont* pFont)
{
    m_pFont = pFont;
}

void CGridRow::Move(int nIndex, int nOffset)
{
    if (!m_cells.GetSize())
        return;
    
    CGridItem* pItem = m_cells[nIndex];
    m_cells.RemoveAt(nIndex);
    m_cells.InsertAt(nIndex + nOffset, pItem);
}

/////////////////////////////////////////////////////////////////////////////
// CGridTip window
/////////////////////////////////////////////////////////////////////////////

CGridTip::CGridTip(CCursorGridCtrl* pGrid): m_pGrid(pGrid)
{
    WNDCLASS wndcls;
    HINSTANCE hInst = AfxGetInstanceHandle();
    if(!(::GetClassInfo(hInst, GRIDTIP_CLASSNAME, &wndcls)))
    {
        wndcls.style			= CS_SAVEBITS;
        wndcls.lpfnWndProc		= ::DefWindowProc;
        wndcls.cbClsExtra		= wndcls.cbWndExtra = 0;
        wndcls.hInstance		= hInst;
        wndcls.hIcon			= NULL;
        wndcls.hCursor			= LoadCursor( hInst, IDC_ARROW );
        wndcls.hbrBackground	= (HBRUSH)(COLOR_INFOBK +1);
        wndcls.lpszMenuName		= NULL;
        wndcls.lpszClassName	= GRIDTIP_CLASSNAME;

        if (!AfxRegisterClass(&wndcls))
            AfxThrowResourceException();
    }
    m_bCreated = FALSE;
    m_crTextColor = CLR_DEFAULT;
    m_crBackColor = CLR_DEFAULT;
}

CGridTip::~CGridTip()
{
    if (m_bCreated)
        DestroyWindow();
}

BEGIN_MESSAGE_MAP(CGridTip, CWnd)
	//{{AFX_MSG_MAP(CGridTip)
	ON_WM_MOUSEMOVE()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

BOOL CGridTip::DestroyWindow() 
{
    m_bCreated = FALSE;
    return CWnd::DestroyWindow();
}

void CGridTip::Show(const CString& strText, LPRECT pTextRect, DWORD dwTipID,
                    CFont* pFont, BOOL bByCursor)
{
    if (!m_bCreated)
    {
        ASSERT_VALID(m_pParentWnd);
        m_bCreated = CreateEx(WS_EX_TOOLWINDOW | WS_EX_TOPMOST, GRIDTIP_CLASSNAME, NULL, WS_POPUP,
            CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
            NULL, NULL, NULL);
        if (!m_bCreated)
            return;
    }

    if (m_dwTipedID == dwTipID)
        return;
    else
        m_dwTipedID = dwTipID;

    if (strText.IsEmpty())
        return;

    CRect TextRect(pTextRect);

    if (TextRect.IsRectEmpty())
        return;

    if (IsWindowVisible())
        return;

    if (GetFocus() == NULL)
        return;

    m_rectHover = TextRect;
    m_pGrid->ClientToScreen(m_rectHover);

    CClientDC dc(this);
    CFont *pOldFont;
    pOldFont = dc.SelectObject(pFont);
    CSize size = dc.GetTextExtent(strText);

    if (size.cx > TextRect.Width() - m_pGrid->GetLeftTextMargin() - m_pGrid->GetRightTextMargin() - 1)
    {
//        dc.SelectStockObject(ANSI_VAR_FONT);

        CRect rectDisplay;
        
        CSize TextSize = dc.GetTextExtent(strText);
        int nWidth = TextSize.cx + m_pGrid->GetLeftTextMargin() + m_pGrid->GetRightTextMargin() - 1;
        int nHeight = TextSize.cy + m_pGrid->GetTopTextMargin() + m_pGrid->GetBottomTextMargin() - 1;

        if (bByCursor)
        {
            CPoint point;
            ::GetCursorPos(&point);
            point.Offset(0, ::GetSystemMetrics(SM_CYCURSOR) / 2);
            rectDisplay.left = point.x;
            rectDisplay.top = point.y;
        }
        else
        {
            rectDisplay = TextRect;
            m_pGrid->ClientToScreen(rectDisplay);
        }
        rectDisplay.right = rectDisplay.left + nWidth;
        rectDisplay.bottom = rectDisplay.top + nHeight;

        rectDisplay.InflateRect(1, 1);

        SetWindowPos(&wndTop, rectDisplay.left, rectDisplay.top, rectDisplay.Width(), rectDisplay.Height(), SWP_SHOWWINDOW | SWP_NOACTIVATE);

        if (m_crBackColor != CLR_DEFAULT)
        {
            CBrush backBrush(m_crBackColor);
            CBrush* pOldBrush = dc.SelectObject(&backBrush);
            CRect rect;
            dc.GetClipBox(&rect);
            dc.PatBlt(rect.left, rect.top, rect.Width(), rect.Height(),  PATCOPY);
            dc.SelectObject(pOldBrush);
        }
        
        CBrush BorderBrush;
        BorderBrush.CreateSolidBrush(m_pGrid->GetLinesColor());
        
        CRect rect;
        dc.GetClipBox(&rect);
        dc.FrameRect(rect, &BorderBrush);

        if (m_crTextColor != CLR_DEFAULT)
            dc.SetTextColor(m_crTextColor);

        dc.SetBkMode(TRANSPARENT);
        dc.TextOut(m_pGrid->GetLeftTextMargin() + 1, m_pGrid->GetTopTextMargin() + 1, strText);

        SetCapture();

        SetTimer(TIMER_ID_AUTOHIDE, TIP_TIMEOUT, NULL);
    }

    dc.SelectObject(pOldFont);
}

void CGridTip::Hide()
{
    if (!::IsWindow(GetSafeHwnd()))
        return;

    if (GetCapture()->GetSafeHwnd() == GetSafeHwnd())
        ReleaseCapture();

    if (IsWindowVisible())
    {
        BOOL bSuccess = KillTimer(TIMER_ID_AUTOHIDE);
        ASSERT(bSuccess);
    }

    ShowWindow(SW_HIDE);
}

void CGridTip::OnMouseMove(UINT nFlags, CPoint point) 
{
    ClientToScreen(&point);
    if (!m_rectHover.PtInRect(point)) 
    {
        Hide();
        
        CWnd *pWnd = WindowFromPoint(point);
        if (pWnd == this) 
            pWnd = m_pGrid;
        
        int hittest = (int)pWnd->SendMessage(WM_NCHITTEST, 0, MAKELONG(point.x,point.y));
        
        if (hittest == HTCLIENT)
        {
            pWnd->ScreenToClient(&point);
            pWnd->PostMessage(WM_MOUSEMOVE, nFlags, MAKELONG(point.x,point.y));
        }
        else
            pWnd->PostMessage( WM_NCMOUSEMOVE, hittest, MAKELONG(point.x,point.y));
    }
    ::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
}

BOOL CGridTip::PreTranslateMessage(MSG* pMsg) 
{
    CWnd *pWnd;
	int hittest;
	switch (pMsg->message)
	{
	case WM_LBUTTONDOWN:
	case WM_RBUTTONDOWN:
	case WM_MBUTTONDOWN:
		{
		POINTS pts = MAKEPOINTS( pMsg->lParam );
		POINT  point;
		point.x = pts.x;
		point.y = pts.y;

		ClientToScreen( &point );
        Hide();

		pWnd = WindowFromPoint( point );
		if (!pWnd)
			return CWnd::PreTranslateMessage(pMsg);

		if( pWnd->GetSafeHwnd() == GetSafeHwnd()) 
			pWnd = m_pGrid;

		hittest = (int)pWnd->SendMessage(WM_NCHITTEST,0,MAKELONG(point.x,point.y));

		if (hittest == HTCLIENT) 
		{
			pWnd->ScreenToClient( &point );
		}
		else 
		{
			switch (pMsg->message)
            {
			case WM_LBUTTONDOWN: 
				pMsg->message = WM_NCLBUTTONDOWN;
				break;
			case WM_RBUTTONDOWN: 
				pMsg->message = WM_NCRBUTTONDOWN;
				break;
			case WM_MBUTTONDOWN: 
				pMsg->message = WM_NCMBUTTONDOWN;
				break;
			}
			pMsg->wParam = hittest;
		}
		pWnd->GetParent()->PostMessage(WM_MOUSEACTIVATE, (WPARAM)pWnd->GetParentOwner()->GetSafeHwnd(), MAKELPARAM(hittest, MA_ACTIVATE));
        pMsg->lParam = MAKELONG(point.x,point.y);
        pWnd->PostMessage(pMsg->message, pMsg->wParam, pMsg->lParam);
		return TRUE;
		}
	case WM_TIMER:
        Hide();
		return TRUE;
    }
    
    if(GetFocus() == NULL)
    {
        Hide();
        return TRUE;
    }
    return CWnd::PreTranslateMessage(pMsg);
}

/////////////////////////////////////////////////////////////////////////////
// CQSEdit window
/////////////////////////////////////////////////////////////////////////////

CQSEdit::CQSEdit()
{
}

CQSEdit::~CQSEdit()
{
}

BEGIN_MESSAGE_MAP(CQSEdit, CEdit)
	//{{AFX_MSG_MAP(CQSEdit)
	ON_WM_KILLFOCUS()
	ON_WM_KEYDOWN()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

BOOL CQSEdit::PreTranslateMessage(MSG* pMsg)
{
    return CEdit::PreTranslateMessage(pMsg);
}

void CQSEdit::OnKillFocus(CWnd* pNewWnd)
{
}

void CQSEdit::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    CEdit::OnKeyDown(nChar, nRepCnt, nFlags);
}

/////////////////////////////////////////////////////////////////////////////
// CQuickSearch window
/////////////////////////////////////////////////////////////////////////////

CQuickSearch::CQuickSearch(CCursorGridCtrl* pGrid) : m_pGrid(pGrid)
{
    HINSTANCE hInstance = AfxGetInstanceHandle();
    WNDCLASS wc;
    
    if (!(::GetClassInfo(hInstance, QUICKSEARCH_CLASSNAME, &wc)))
    {
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
        wc.hCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
        wc.hIcon = NULL;
        wc.hInstance = hInstance;
        wc.lpfnWndProc = ::DefWindowProc;
        wc.lpszClassName = QUICKSEARCH_CLASSNAME;
        wc.lpszMenuName = NULL;
        wc.style = CS_SAVEBITS;
        
        if (!AfxRegisterClass(&wc))
        {
            AfxThrowResourceException();
            return;
        }
    }
    m_bIsWorking = FALSE;
    m_nTimeout = QUICKSEARCH_TIMEOUT;
}

CQuickSearch::~CQuickSearch()
{
    if (::IsWindow(GetSafeHwnd()))
        DestroyWindow();
}

BEGIN_MESSAGE_MAP(CQuickSearch, CWnd)
	//{{AFX_MSG_MAP(CQuickSearch)
	ON_WM_PAINT()
	ON_WM_TIMER()
    ON_WM_NCACTIVATE()
	//}}AFX_MSG_MAP
    ON_EN_KILLFOCUS(100, OnEditKillFocus)
//	ON_EN_CHANGE(100, OnEditChange)
END_MESSAGE_MAP()

BOOL CQuickSearch::IsWorking() const
{
    return m_bIsWorking;
}

void CQuickSearch::Hide()
{
    if (!::IsWindow(GetSafeHwnd()))
        return;
    m_bIsWorking = FALSE;
    m_pGrid->SetFocus();
    DestroyWindow();
}

void CQuickSearch::Show(LPRECT pRect, LPCSTR szFieldName, LPCSTR szInitStr)
{
    Hide();
    
    BOOL bSuccess;
    ASSERT_VALID(m_pParentWnd);

    bSuccess = CreateEx(WS_EX_TOOLWINDOW | WS_EX_TOPMOST, QUICKSEARCH_CLASSNAME, NULL, WS_POPUP,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        NULL, NULL, NULL);

    ASSERT(bSuccess);

    if (!bSuccess)
        return;

    m_bIsWorking = TRUE;
    m_strFieldName = szFieldName;

    CRect rectDisplay(pRect);
    m_pGrid->ClientToScreen(rectDisplay);
    rectDisplay.left--;

    CClientDC dc(this);
    dc.SelectStockObject(ANSI_VAR_FONT);
    CSize TextSize = dc.GetTextExtent("j^");
    rectDisplay.bottom = 1 + rectDisplay.top + m_pGrid->GetTopTextMargin() + TextSize.cy + m_pGrid->GetBottomTextMargin();

    CRect rectEdit;
    rectEdit.left = 1 + m_pGrid->GetLeftTextMargin();
    rectEdit.top = 1 + m_pGrid->GetTopTextMargin();
    rectEdit.right = rectEdit.left + rectDisplay.Width() - m_pGrid->GetLeftTextMargin() - m_pGrid->GetRightTextMargin();
    rectEdit.bottom = rectEdit.top + rectDisplay.Height() - m_pGrid->GetTopTextMargin() - m_pGrid->GetBottomTextMargin();
    bSuccess = m_edit.Create(WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL | ES_LEFT, rectEdit, this, 100);
    CFont font;
    font.CreateStockObject(ANSI_VAR_FONT);
    m_edit.SetFont(&font);
    m_edit.SetWindowText(szInitStr);
    m_edit.SetSel(1, 1);
    m_edit.SetFocus();
    
    SetWindowPos(&wndTop, rectDisplay.left, rectDisplay.top, rectDisplay.Width(), rectDisplay.Height(), SWP_SHOWWINDOW | SWP_NOACTIVATE);
//    m_bIsDirty = TRUE;
	m_strPattern ="";
    if (m_nTimeout)
        SetTimer(1, m_nTimeout, NULL);
}

void CQuickSearch::OnPaint() 
{
	CPaintDC dc(this);

    CRect rectClient;
    GetClientRect(rectClient);

    CBrush BorderBrush;
    BorderBrush.CreateSolidBrush(m_pGrid->GetLinesColor());
    dc.FrameRect(rectClient, &BorderBrush);
}

BOOL CQuickSearch::IsDirty()
{
	CString str;
	m_edit.GetWindowText(str);

	return (m_strPattern != str);
}

BOOL CQuickSearch::PreTranslateMessage(MSG* pMsg) 
{
    switch (pMsg->message)
    {
    case WM_KEYDOWN:
		switch (pMsg->wParam)
		{
		case VK_ESCAPE:
			Hide();
			return TRUE;
		case VK_UP:
		case VK_DOWN:
        case VK_PRIOR:
        case VK_NEXT:
        case VK_HOME:
        case VK_END:
        case VK_F5:
		case VK_TAB:
			m_pGrid->PostMessage(WM_KEYDOWN, pMsg->wParam, pMsg->lParam);
			Hide();
			return TRUE;
		case VK_RETURN:
			if (IsDirty())
			{
				CString strBeforeSearch, strAfterSearch;
				m_edit.GetWindowText(strBeforeSearch);
				Search();
				m_edit.GetWindowText(strAfterSearch);
				if (strBeforeSearch == strAfterSearch)
				{
//					m_pGrid->PostMessage(WM_KEYDOWN, pMsg->wParam, pMsg->lParam);
					Hide();
				}
			}
			else
			{
				m_pGrid->PostMessage(WM_KEYDOWN, pMsg->wParam, pMsg->lParam);
				Hide();
			}
			return TRUE;
        default:
            if (m_nTimeout)
            {
                KillTimer(1);
                SetTimer(1, m_nTimeout, NULL);
            }
		}
        break;
    }
    return CWnd::PreTranslateMessage(pMsg);
}

void CQuickSearch::OnTimer(UINT nIDEvent)
{
	if (IsDirty())
		Search();
}

void CQuickSearch::Search()
{
    if (m_nTimeout)
        KillTimer(1);
//    m_bIsDirty = FALSE;
    m_edit.GetWindowText(m_strPattern);
    if (m_strPattern.IsEmpty())
        return;

	if (m_pGrid->OnQuickSearch(m_strPattern))
	{
		stQuickSearchPattern QSPattern;
		QSPattern.szFieldName = m_strFieldName;
		QSPattern.dataType = dtText;
		QSPattern.pTextData = &m_strPattern;
		m_pGrid->m_pProvider->QuickSearch(QSPattern);
/*
		int nStart, nEnd;
		m_edit.GetSel(nStart, nEnd);
		m_edit.SetWindowText(strPattern);
		m_edit.SetSel(nStart, nEnd);
*/
	}
	int nLast = m_strPattern.GetLength();
	m_edit.SetWindowText(m_strPattern);
	m_edit.SetSel(nLast, nLast);
}

BOOL CQuickSearch::OnNcActivate(BOOL bActive)
{
    AfxGetMainWnd()->SendMessage(WM_NCACTIVATE, TRUE, 0);
    return TRUE;
}

void CQuickSearch::OnEditKillFocus()
{
	Hide();
}
/*
void CQuickSearch::OnEditChange()
{
    m_bIsDirty = TRUE;
}
*/
/////////////////////////////////////////////////////////////////////////////
// CReposColumnWnd window
/////////////////////////////////////////////////////////////////////////////

CReposColumnWnd::CReposColumnWnd(CCursorGridCtrl* pGrid): m_pGrid(pGrid)
{
    HINSTANCE hInstance = AfxGetInstanceHandle();
    WNDCLASS wc;
    
    if (!(::GetClassInfo(hInstance, REPOSCOLUMN_CLASSNAME, &wc)))
    {
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        wc.hbrBackground = NULL;
        wc.hCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
        wc.hIcon = NULL;
        wc.hInstance = hInstance;
        wc.lpfnWndProc = ::DefWindowProc;
        wc.lpszClassName = REPOSCOLUMN_CLASSNAME;
        wc.lpszMenuName = NULL;
        wc.style = CS_SAVEBITS;
        
        if (!AfxRegisterClass(&wc))
        {
            AfxThrowResourceException();
            return;
        }
    }
    m_bArrowsAreDrawn = FALSE;
}

CReposColumnWnd::~CReposColumnWnd()
{
}

BEGIN_MESSAGE_MAP(CReposColumnWnd, CWnd)
	//{{AFX_MSG_MAP(CReposColumnWnd)
    ON_WM_LBUTTONUP()
    ON_WM_MOUSEMOVE()
    ON_WM_PAINT()
    ON_WM_NCACTIVATE()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

void CReposColumnWnd::OnLButtonUp(UINT nFlags, CPoint point)
{
    HideArrows();
    ReleaseCapture();
    DestroyWindow();
    if (m_nShift)
        m_pGrid->m_pColumns->Move(m_nCapturedIndex, m_nShift);

    if (m_bChangeVisible)
        m_pCapturedColumn->SetVisible(!m_pCapturedColumn->m_bVisible);
    
    m_pGrid->m_CaptureType = CCursorGridCtrl::ctNone;
    m_pGrid->m_ColumnHelper.Show();
    m_pGrid->SetFocus();
}

void CReposColumnWnd::OnMouseMove(UINT nFlags, CPoint point)
{
    ClientToScreen(&point);
    SetWindowPos(NULL, point.x - m_Offset.cx, point.y - m_Offset.cy, 0, 0, SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER);

    int nShift = 0;
    BOOL bChangeVisible = FALSE;

    m_pGrid->ScreenToClient(&point);

    int nHitColIndex = m_pGrid->HitTestHeader(point);
    LPCSTR szCursor = IDC_NO;
    if (nHitColIndex == -1)
    {
        if (m_pCapturedColumn->m_bVisible)
        {
            if (m_pCapturedColumn->m_bChangeVisible)
            {
                CRect rectHelper;
                m_pGrid->m_ColumnHelper.GetWindowRect(rectHelper);
                m_pGrid->ScreenToClient(rectHelper);
                if (rectHelper.PtInRect(point))
                {
                    szCursor = IDC_ARROW;
                    bChangeVisible = TRUE;
                }
            }
        }
        else
        {
            CRect rectHeader = m_pGrid->GetHeaderRect();
            if (rectHeader.PtInRect(point))
            {
                szCursor = IDC_ARROW;
                bChangeVisible = TRUE;
            }
        }
    }
    else
    {
        if (m_pGrid->m_bChangePositionOfColumns)
        {
            szCursor = IDC_ARROW;
            if (m_pGrid->DisplayColumn(m_pGrid->m_pColumns->GetAt(nHitColIndex)))
                nHitColIndex = m_pGrid->HitTestHeader(point);
        }
        else
        {
            if (!m_pCapturedColumn->m_bVisible)
            {
                CRect rectHeader = m_pGrid->GetHeaderRect();
                if (rectHeader.PtInRect(point))
                {
                    szCursor = IDC_ARROW;
                    bChangeVisible = TRUE;
                }
            }
            nHitColIndex = -1;
        }
    }
    SetCursor(AfxGetApp()->LoadStandardCursor(szCursor));

    if (nHitColIndex != -1 && nHitColIndex != m_nCapturedIndex)
    {
        CRect rectHitCol = m_pGrid->m_pColumns->GetAt(nHitColIndex)->GetBoundsRect();
        if (nHitColIndex < m_nCapturedIndex)
        {
            if (point.x > rectHitCol.left + rectHitCol.Width() / 2)
            {
                nHitColIndex = m_pGrid->GetNextColumn(nHitColIndex, 1);
                if (nHitColIndex > m_nCapturedIndex || nHitColIndex == -1)
                    nHitColIndex = m_nCapturedIndex;
            }
        }
        else
        {
            if (point.x < rectHitCol.left + rectHitCol.Width() / 2)
            {
                nHitColIndex = m_pGrid->GetNextColumn(nHitColIndex, -1);
                if (nHitColIndex < m_nCapturedIndex || nHitColIndex == -1)
                    nHitColIndex = m_nCapturedIndex;
            }
        }
        nShift = nHitColIndex - m_nCapturedIndex;
        if (!m_pCapturedColumn->m_bVisible)
            bChangeVisible = TRUE;
    }

    DrawArrows(nShift, bChangeVisible);
    CWnd::OnMouseMove(nFlags, point);
}

void CReposColumnWnd::DrawArrows(int nShift, BOOL bChangeVisible)
{
    CPoint point;
    CGridColumn* pColumn = m_pGrid->m_pColumns->GetAt(m_nCapturedIndex + nShift);
    if (!pColumn->m_bVisible)
    {
        int nIndex = m_pGrid->GetNextColumn(m_nCapturedIndex, -1);
        if (nIndex == -1)
        {
            nIndex = m_pGrid->GetNextColumn(m_nCapturedIndex, 1);
            if (nIndex == -1)
            {
                CRect rect;
                m_pGrid->GetClientRect(rect);
                point.x = rect.left - 8;
                point.y = rect.top - 16;
            }
            else
            {
                CRect rect = m_pGrid->m_pColumns->GetAt(nIndex)->GetBoundsRect();
                point.x = rect.left - 8;
                point.y = rect.top - 16;
            }
        }
        else
        {
            CRect rect = m_pGrid->m_pColumns->GetAt(nIndex)->GetBoundsRect();
            point.x = rect.right - 8;
            point.y = rect.top - 16;
        }
    }
    else
    {
        CRect rect = pColumn->GetBoundsRect();
        if (nShift > 0)
            point.x = rect.right - 8;
        else
            point.x = rect.left - 8;
        point.y = rect.top - 16;
    }

    if (m_pCapturedColumn->m_bVisible && nShift || !m_pCapturedColumn->m_bVisible && bChangeVisible)
    {
        m_pGrid->ClientToScreen(&point);
        m_pGrid->GetParent()->ScreenToClient(&point);
        
        if (nShift != m_nShift)
            HideArrows();

        if (!m_bArrowsAreDrawn)
        {
            m_bArrowsAreDrawn = TRUE;
            m_ptLastArrows = point;
        }

        CDC* pDC = m_pGrid->GetParent()->GetDC();
        m_pGrid->m_ilInternalImages.Draw(pDC, iiTopReposArrow, point, ILD_NORMAL);
        point.Offset(0, 16 + m_pGrid->m_nRowHeight);
        m_pGrid->m_ilInternalImages.Draw(pDC, iiBottomReposArrow, point, ILD_NORMAL);
        m_pGrid->GetParent()->ReleaseDC(pDC);
    }
    else
        HideArrows();

    m_nShift = nShift;
    m_bChangeVisible = bChangeVisible;
}

void CReposColumnWnd::HideArrows()
{
    if (!m_bArrowsAreDrawn)
        return;

    CRect rect(m_ptLastArrows, CSize(16, 16 + 16 + m_pGrid->m_nRowHeight));
    m_pGrid->GetParent()->InvalidateRect(rect);

    m_bArrowsAreDrawn = FALSE;
}

void CReposColumnWnd::OnPaint()
{
    CPaintDC dc(this);
    
    CRect rect;
    GetClientRect(rect);
    CFont* pOldFont = dc.SelectObject(&m_pGrid->m_font);
    if (m_pGrid->m_HeadersStyle == hsFlat)
    {
        rect.left++;
        rect.top++;
        m_pGrid->DrawHeader(&dc, m_pCapturedColumn, rect);
        rect.left--;
        rect.top--;
        CBrush BorderBrush;
        BorderBrush.CreateSolidBrush(m_pGrid->GetLinesColor());
        dc.FrameRect(rect, &BorderBrush);
    }
    else
        m_pGrid->DrawHeader(&dc, m_pCapturedColumn, rect);
    dc.SelectObject(pOldFont);
}

BOOL CReposColumnWnd::OnNcActivate(BOOL bActive)
{
    AfxGetMainWnd()->SendMessage(WM_NCACTIVATE, TRUE, 0);
    return TRUE;
}

void CReposColumnWnd::Show(CGridColumn* pColumn, CSize& Offset)
{
    BOOL bSuccess;

    m_pCapturedColumn = pColumn;
    m_Offset = Offset;
    m_nCapturedIndex = m_pGrid->m_pColumns->IndexOf(m_pCapturedColumn);
    
    int cx, cy;
    if (m_pCapturedColumn->m_bVisible)
    {
        cx = m_pCapturedColumn->m_rect.Width();
        cy = m_pCapturedColumn->m_rect.Height();

        m_pGrid->m_ColumnHelper.Show();
    }
    else
    {
        cx = m_pCapturedColumn->m_nMinWidth;
        if (cx < COLUMN_HELPER_MINROWWIDTH - 1)
            cx = COLUMN_HELPER_MINROWWIDTH - 1;
        cy = m_pGrid->m_nRowHeight;
    }

    if (m_pGrid->m_HeadersStyle == hsFlat)
    {
        cx++;
        cy++;
    }
    CPoint point;
    GetCursorPos(&point);

    bSuccess = CreateEx(WS_EX_TOOLWINDOW | WS_EX_TOPMOST, REPOSCOLUMN_CLASSNAME, NULL, WS_POPUP|WS_VISIBLE,
        point.x - cx/2, point.y - cy/2, cx, cy,
        NULL, NULL, NULL);

    ASSERT(bSuccess);
    if (!bSuccess)
        return;
    SetCapture();
}

/////////////////////////////////////////////////////////////////////////////
// CColumnHelper window
/////////////////////////////////////////////////////////////////////////////

CColumnHelper::CColumnHelper(CCursorGridCtrl* pGrid): m_pGrid(pGrid)
{
    HINSTANCE hInstance = AfxGetInstanceHandle();
    WNDCLASS wc;
    
    if (!(::GetClassInfo(hInstance, COLUMNHELPER_CLASSNAME, &wc)))
    {
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        wc.hbrBackground = NULL;
        wc.hCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
        wc.hIcon = NULL;
        wc.hInstance = hInstance;
        wc.lpfnWndProc = ::DefWindowProc;
        wc.lpszClassName = COLUMNHELPER_CLASSNAME;
        wc.lpszMenuName = NULL;
        wc.style = CS_SAVEBITS;
        
        if (!AfxRegisterClass(&wc))
        {
            AfxThrowResourceException();
            return;
        }
    }
    m_bDisplay = FALSE;
}

CColumnHelper::~CColumnHelper()
{
}

BEGIN_MESSAGE_MAP(CColumnHelper, CWnd)
	//{{AFX_MSG_MAP(CColumnHelper)
    ON_WM_NCCALCSIZE()
    ON_WM_NCPAINT()
    ON_WM_PAINT()
    ON_WM_LBUTTONDOWN()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

void CColumnHelper::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS* lpncsp)
{
    lpncsp->rgrc[0].top += COLUMN_HELPER_CX;
}

void CColumnHelper::OnNcPaint() 
{
    CWindowDC dc(this);
    CRect rectWindow;
    GetWindowRect(rectWindow);
    CRect rect;
    rect.left = rectWindow.Width() - COLUMN_HELPER_CX;
    rect.right = rectWindow.Width();
    rect.top = 0;
    rect.bottom = COLUMN_HELPER_CY;
    
    dc.FillSolidRect(rect, ::GetSysColor(COLOR_BTNFACE));

    m_pGrid->m_ilInternalImages.Draw(&dc, iiHelperIcon, CPoint(rect.left + 1, rect.top + 1), ILD_NORMAL);

    CBrush brush;
    brush.CreateSolidBrush(m_pGrid->GetLinesColor());
    dc.FrameRect(rect, &brush);

    if (m_columns.GetSize())
    {
        dc.MoveTo(rectWindow.Width(), COLUMN_HELPER_CY - 1);
        CPen pen;
        pen.CreatePen(PS_SOLID, 1, m_pGrid->GetLinesColor());
        CPen* pOldPen = dc.SelectObject(&pen);
        dc.LineTo(rectWindow.Width() - GetColumnWidth(m_columns[0]) - 1, COLUMN_HELPER_CY - 1);
        dc.SelectObject(pOldPen);
    }
}

void CColumnHelper::OnPaint()
{
    CPaintDC dc(this);
    
    CRect rectHeader;
    GetClientRect(rectHeader);
    rectHeader.bottom = m_pGrid->m_nRowHeight;

    CBrush brush;
    brush.CreateSolidBrush(m_pGrid->GetLinesColor());

    CFont* OldFont = dc.SelectObject(&m_pGrid->m_font);
    for (int i = 0; i < m_columns.GetSize(); i++)
    {
        CGridColumn* pColumn = m_columns[i];
        rectHeader.left = rectHeader.right - GetColumnWidth(pColumn) + 1;
        m_pGrid->DrawHeader(&dc, pColumn, rectHeader);

        CRect rectFrame(rectHeader);
        rectFrame.left--;
        rectFrame.top--;
        dc.FrameRect(rectFrame, &brush);

        rectHeader.OffsetRect(0, m_pGrid->m_nRowHeight);
    }
    dc.SelectObject(OldFont);
}

void CColumnHelper::OnLButtonDown(UINT nFlags, CPoint point)
{
    int nIndex = point.y / m_pGrid->m_nRowHeight;
    CGridColumn* pColumn = m_columns[nIndex];
    CRect rect;
    GetClientRect(rect);
    m_pGrid->m_ReposColumnWnd.Show(pColumn, CSize(point.x - (rect.Width() - GetColumnWidth(pColumn)), point.y % m_pGrid->m_nRowHeight));
    m_pGrid->m_CaptureType = CCursorGridCtrl::ctMoveColumn;
    CWnd::OnLButtonDown(nFlags, point);
}

void CColumnHelper::Show()
{
    BOOL bSuccess;

    if (!m_hWnd)
    {
        bSuccess = CreateEx(0, COLUMNHELPER_CLASSNAME, NULL, WS_CHILD,
            CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
            m_pGrid->m_hWnd, NULL, NULL);
    }

    int cx = COLUMN_HELPER_CX;
    int cy = COLUMN_HELPER_CY;
    m_columns.RemoveAll();
    int i;
    for (i = 0; i < m_pGrid->m_pColumns->GetSize(); i++)
    {
        CGridColumn* pColumn = m_pGrid->m_pColumns->GetAt(i);
        if (!pColumn->m_bVisible && pColumn->m_bChangeVisible)
        {
            m_columns.Add(pColumn);
            int nWidth = GetColumnWidth(pColumn);

            if (nWidth > cx)
                cx = nWidth;
            cy += m_pGrid->m_nRowHeight;
        }
    }

    CRect rect = m_pGrid->GetGridRect();
    int x = rect.right - cx - COLUMN_HELPER_MARGIN;
    int y = rect.top + COLUMN_HELPER_MARGIN;
    SetWindowPos(NULL, x, y, cx, cy, SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_NOOWNERZORDER|SWP_NOZORDER);

    CRgn rgnWindow;
    bSuccess = rgnWindow.CreateRectRgn(cx - COLUMN_HELPER_CX, 0, cx, COLUMN_HELPER_CY);

    int CurY = COLUMN_HELPER_CY;
    for (i = 0; i < m_columns.GetSize(); i++)
    {
        CRgn rgn;
        bSuccess = rgn.CreateRectRgn(cx - GetColumnWidth(m_columns[i]), CurY - 1, cx, CurY + m_pGrid->m_nRowHeight);
        int nRetVal = rgnWindow.CombineRgn(&rgnWindow, &rgn, RGN_OR);

        CurY += m_pGrid->m_nRowHeight;
    }

    bSuccess = SetWindowRgn(rgnWindow, FALSE);
    ShowWindow(SW_NORMAL);
    m_bDisplay = TRUE;
}

void CColumnHelper::Hide()
{
    if (!m_hWnd)
        return;
    ShowWindow(SW_HIDE);
    m_bDisplay = FALSE;
}

int CColumnHelper::GetColumnWidth(CGridColumn* pColumn)
{
    int nWidth = pColumn->m_nMinWidth + 1;
    if (nWidth < COLUMN_HELPER_MINROWWIDTH)
        nWidth = COLUMN_HELPER_MINROWWIDTH;
    return nWidth;
}

BOOL CColumnHelper::IsDisplay()
{
    return m_bDisplay;
}

/////////////////////////////////////////////////////////////////////////////
// CCursorGridCtrl window
/////////////////////////////////////////////////////////////////////////////

IMPLEMENT_DYNCREATE(CCursorGridCtrl, CWnd);

CCursorGridCtrl::CCursorGridCtrl(): m_Tip(this), m_QuickSearch(this), m_ReposColumnWnd(this), m_ColumnHelper(this),
    m_DropTarget(this)
{
    HINSTANCE hInstance = AfxGetInstanceHandle();
    WNDCLASS wc;
    
    if (!(::GetClassInfo(hInstance, CURSOR_GRID_CLASS_NAME, &wc)))
    {
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
        wc.hCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
        wc.hIcon = NULL;
        wc.hInstance = hInstance;
        wc.lpfnWndProc = ::DefWindowProc;
        wc.lpszClassName = CURSOR_GRID_CLASS_NAME;
        wc.lpszMenuName = NULL;
        wc.style = /*CS_HREDRAW | CS_VREDRAW |*/ CS_DBLCLKS;
        
        if (!AfxRegisterClass(&wc))
        {
            AfxThrowResourceException();
            return;
        }
    }

    m_nTopRowIndex = 0;
    m_pProvider = NULL;
    m_bShowHeader = TRUE;
    m_pCapturedColumn = NULL;
    m_CaptureType = ctNone;
    m_VScrollerPresent = spAuto;
    m_nCurrentColumn = -1;
    m_nCurrentDataRowIndex = -1;
    m_pCurrentDataRowOutOfWindow = NULL;
    m_RowSelectMode = rsmCell;
    m_bHorizontalLines = TRUE;
    m_bVerticalLines = TRUE;
    m_StartView = svFromBegin;
    m_bAutoDelete = FALSE;
    m_HeadersStyle = hs3D;
    BOOL bSuccess;
    bSuccess = m_ilInternalImages.Attach(ImageList_LoadBitmap(_Module.m_hInst, (LPCTSTR)IDB_CG_IMAGES, 16, 0, RGB(0x00, 0x80, 0x80)));
    ASSERT(bSuccess);
    m_nLastLParam = ULONG_MAX;
    m_bLockDrawing = FALSE;
    m_nRefreshTimeout = 0;
    m_crGridLines = CLR_DEFAULT;
    m_bChangePositionOfColumns = TRUE;
    m_crText = CLR_DEFAULT;
    m_crBack = CLR_DEFAULT;
    m_crSelectionText = CLR_DEFAULT;
    m_crSelectionBack = CLR_DEFAULT;
    m_crAlternationRowBack = CLR_DEFAULT;
    m_bUseAlternationRowColor = FALSE;
    m_bEvenRow = TRUE;
	m_bEnableDrag = FALSE;
	m_bEnableDrop = FALSE;

	m_nRowHeight = 0;
	m_nVisibleRows = 1;

	m_nLeftTextMargin = DEFAULT_LEFT_TEXT_MARGIN;
	m_nRightTextMargin = DEFAULT_RIGHT_TEXT_MARGIN;
	m_nTopTextMargin = DEFAULT_TOP_TEXT_MARGIN;
	m_nBottomTextMargin = DEFAULT_BOTTOM_TEXT_MARGIN;
}

BOOL CCursorGridCtrl::Create(const RECT& rect, CWnd *pParentWnd, UINT nID, BOOL bAutoDelete /*= FALSE*/, BOOL bVisible /*= TRUE*/, BOOL bDisabled /*= FALSE*/)
{
    BOOL bSuccess;
    m_bAutoDelete = bAutoDelete;
	DWORD dwStyle = WS_CHILD | WS_TABSTOP | WS_CLIPCHILDREN;
	if (bVisible)
		dwStyle |= WS_VISIBLE;
	if (bDisabled)
		dwStyle |= WS_DISABLED;
    bSuccess = CWnd::Create(CURSOR_GRID_CLASS_NAME, NULL, dwStyle, rect, pParentWnd, nID);
    if (bSuccess)
        SetTimer(TIMER_ID_AUTOHIDE, HELPER_DISPLAY_TIMEOUT, NULL);
    return bSuccess;
}

CCursorGridCtrl::~CCursorGridCtrl()
{
    ClearRows();
    if (m_nCurrentDataRowIndex == -1 && m_pCurrentDataRowOutOfWindow)
    {
        delete m_pCurrentDataRowOutOfWindow;
        m_pCurrentDataRowOutOfWindow = NULL;
    }
    if (m_pProvider)
        m_pProvider->SetControl(NULL);

    m_pColumns->Release();
}

BEGIN_MESSAGE_MAP(CCursorGridCtrl, CWnd)
	//{{AFX_MSG_MAP(CCursorGridCtrl)
	ON_WM_CREATE()
	ON_WM_PAINT()
	ON_WM_ERASEBKGND()
	ON_WM_SIZE()
	ON_WM_HSCROLL()
	ON_WM_NCHITTEST()
	ON_WM_SETCURSOR()
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_MOUSEMOVE()
	ON_WM_VSCROLL()
	ON_WM_MOUSEWHEEL()
	ON_WM_KEYDOWN()
	ON_WM_GETDLGCODE()
	ON_WM_SYSKEYDOWN()
	ON_WM_RBUTTONDOWN()
	ON_WM_RBUTTONUP()
	ON_WM_KILLFOCUS()
	ON_WM_SETFOCUS()
	ON_WM_NCDESTROY()
	ON_WM_LBUTTONDBLCLK()
	ON_WM_CONTEXTMENU()
	ON_MESSAGE(WM_GETFONT, OnGetFont)
	ON_MESSAGE(WM_SETFONT, OnSetFont)
	ON_MESSAGE(DP_DATA_CHANGE, OnDataChange)
	ON_WM_TIMER()
	ON_WM_CHAR()
	ON_WM_KEYUP()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

BOOL CCursorGridCtrl::PreTranslateMessage(MSG* pMsg) 
{
    switch (pMsg->message)
    {
	case WM_COMMAND:
		GetParent()->SendMessage(WM_COMMAND, pMsg->wParam, pMsg->lParam);
		return TRUE;

    case WM_KEYDOWN:
        switch (pMsg->wParam)
        {
        case VK_TAB:
            {
                if (!IS_CTRL_PRESSED())
                {
                    int nIndex = GetNextColumn(m_nCurrentColumn, IS_SHIFT_PRESSED() ? -1 : 1);
                    if (nIndex != -1)
                    {
                        SetCurrentCell(m_nCurrentDataRowIndex, nIndex);
                        return TRUE;
                    }
                }
            }
            break;
        case VK_RETURN:
            OnSelection();
            return TRUE;
        }
        break;
    case WM_LBUTTONDOWN:
        if (pMsg->hwnd == m_hWnd && pMsg->lParam == m_nLastLParam && ::GetTickCount() - m_dwLastTickCount <= ::GetDoubleClickTime())
        {
            PostMessage(WM_LBUTTONDBLCLK, pMsg->wParam, pMsg->lParam);
            return TRUE;
        }
        m_dwLastTickCount = ::GetTickCount();
        m_nLastLParam = pMsg->lParam;
        break;
    }
    return CWnd::PreTranslateMessage(pMsg);
}

/////////////////////////////////////////////////////////////////////////////
// CCursorGridCtrl message handlers

int CCursorGridCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (CWnd::OnCreate(lpCreateStruct) == -1)
		return -1;
	
	InitFont(GetParent()->GetFont());
	return 0;
}

void CCursorGridCtrl::InitFont(CFont* pFont)
{
    BOOL bSuccess;
    int nRetVal;

    LOGFONT lf;
    nRetVal = pFont->GetLogFont(&lf);
    ASSERT(nRetVal);
    bSuccess = m_font.CreateFontIndirect(&lf);
    ASSERT(bSuccess);

    CDC *pDC = GetDC();
    CFont* pOldFont = pDC->SelectObject(pFont);

    m_nFontHeight = pDC->GetTextExtent("^j").cy;

    pDC->SelectObject(pOldFont);
    nRetVal = ReleaseDC(pDC);

    ASSERT(nRetVal);

    m_nRowHeight = m_nFontHeight + GetTopTextMargin() + GetBottomTextMargin();
    RecalcColumsWidth();
    CalcRowsParams();
}

void CCursorGridCtrl::PreSubclassWindow() 
{
    m_pColumns = NewColumns();
    m_pColumns->m_pGrid = this;

    BOOL bSuccess;
    
    if (::IsWindow(m_hWnd))
    {
        bSuccess = ModifyStyleEx(0, WS_EX_CLIENTEDGE);
        ASSERT(bSuccess);
        
        CRect rect;
        GetWindowRect(rect);
        CWnd* pParentWnd = GetParent();

        if (pParentWnd)
		{
			pParentWnd->ScreenToClient(rect);
			rect.InflateRect(1, 1);
			MoveWindow(rect);
			rect.DeflateRect(1, 1);
			MoveWindow(rect);

            if (!rect.IsRectNull())
                InitFont(pParentWnd->GetFont());
		}
    }
    
    CalcRowsParams();
}

void CCursorGridCtrl::OnNcDestroy() 
{
	CWnd::OnNcDestroy();
    if (m_bAutoDelete)
        delete this;
}

void CCursorGridCtrl::OnPaint() 
{
	CPaintDC dc(this);

    if (m_bLockDrawing)
        return;

    //Paint(&dc);
    CMemDC memDC(&dc);
    Paint(memDC);
}

void CCursorGridCtrl::Paint(CDC *pDC)
{
    if (m_bShowHeader && pDC->RectVisible(GetHeaderRect()))
        PaintHeaders(pDC);

	CRect rect = GetGridRect();
    if (pDC->RectVisible(rect))
    {
		COLORREF crBack = m_crBack != CLR_DEFAULT ? m_crBack : ::GetSysColor(COLOR_WINDOW);
		CBrush br(crBack);
		CBrush* pOldBrush = pDC->SelectObject(&br);
		pDC->FillRect(&rect, &br);
		pDC->SelectObject(pOldBrush);

        PaintCells(pDC);

        if (m_bHorizontalLines || m_bVerticalLines)
            PaintGridLines(pDC);
        
        if (m_pProvider)
            PaintError(pDC);

        if (m_DropTarget.m_bDragOn)
            PaintDropTarget(pDC);
    }
}

void CCursorGridCtrl::PaintDropTarget(CDC* pDC)
{
    CRect rect(m_DropTarget.m_rectTarget);
    CPoint point = pDC->GetWindowOrg();
    rect.OffsetRect(-point.x, -point.y);
    CSize size(2,2);
    pDC->DrawDragRect(rect, size, NULL, size);
}

void CCursorGridCtrl::PaintError(CDC* pDC)
{
    LPCSTR szError = m_pProvider->GetLastError();
    if (!szError)
        return;

    CString strError(szError);
    if (!strError.GetLength())
        return;

    CRect GridRect = GetGridRect();
    CRect rect = GridRect;
    int nWidth = rect.Width() - rect.Width() / 4;
    rect.left = (rect.Width() - nWidth) / 2;
    rect.right = rect.left + nWidth;

    CFont* pOldFont = pDC->SelectObject(&m_font);

    UINT nFlags = DT_LEFT | DT_NOPREFIX | DT_WORDBREAK;
    CRect TestTextRect(rect);
    TestTextRect.left += 1;
    TestTextRect.right -= 1;
    pDC->DrawText(strError, TestTextRect, nFlags | DT_CALCRECT);
    int nTextHeight = TestTextRect.Height();
    rect.top = GridRect.top + (GridRect.Height() - nTextHeight + 2) / 2;
    rect.bottom = rect.top + nTextHeight + 2;

    CBrush br(::GetSysColor(COLOR_INFOBK));
    CBrush* pOldBrush = pDC->SelectObject(&br);
    pDC->Rectangle(rect.left, rect.top, rect.right, rect.bottom);
    pDC->SelectObject(pOldBrush);
    
    pDC->SetBkMode(TRANSPARENT);
    rect.left += 1;
    rect.right -= 1;
    pDC->DrawText(strError, rect, nFlags);

    pDC->SelectObject(pOldFont);
}

CRect CCursorGridCtrl::GetHeaderRect() const
{
    CRect rect;
    
    GetClientRect(rect);
    rect.bottom = rect.top;
    
    if (m_bShowHeader)
        rect.bottom += GetHeaderHeight();
    
    return rect;
}

int CCursorGridCtrl::GetHeaderHeight() const
{
    return m_nFontHeight + GetTopTextMargin() + GetBottomTextMargin();
}

BOOL CCursorGridCtrl::OnEraseBkgnd(CDC* pDC) 
{
	return TRUE;
}

CGridColumns* CCursorGridCtrl::GetColumns() const
{
    return m_pColumns;
}

int CCursorGridCtrl::GetOrigin()
{
    BOOL bSuccess;
    SCROLLINFO si;
    int Origin = 0;
    
    bSuccess = GetScrollInfo(SB_HORZ, &si);
    if (bSuccess)
        Origin = si.nPos;
    return Origin;
}

void CCursorGridCtrl::PaintHeaders(CDC *pDC)
{
    CRect Rect;
    CFont* pOldFont;
    int i;
    CRect rectHeader = GetHeaderRect();

	if (m_pColumns->GetSize() == 0)
	{
		pDC->FillSolidRect(&rectHeader, ::GetSysColor(COLOR_BTNFACE));
		if (m_HeadersStyle == hs3D)
			Paint3DFrame(pDC, &rectHeader, BF_RECT);
		else
			pDC->FillSolidRect(rectHeader.left, rectHeader.bottom - 1, rectHeader.Width(), 1, GetLinesColor());
		return;
	}

    pOldFont = pDC->SelectObject(&m_font);
    
    GetClientRect(Rect);
    int VisLeft = GetOrigin();
    int VisRight = VisLeft + Rect.right;

    int nStrokeCount = 1;

    for (i = 0; i < m_pColumns->GetSize(); i++)
    {
        CGridColumn* pColumn = m_pColumns->GetAt(i);
        Rect = pColumn->m_rect;
        if (pColumn->m_bVisible && Rect.right > VisLeft && Rect.left < VisRight)
        {
            Rect.OffsetRect(-VisLeft, 0);
            DrawHeader(pDC, pColumn, Rect);
            nStrokeCount++;
        }
    }
    
    pDC->SelectObject(pOldFont);
    
    if (m_HeadersStyle == hsFlat)
    {
        int nPointCount = nStrokeCount * 2;
        LPPOINT lpPoints = new POINT[nPointCount];
        LPDWORD lpPolyPoints = new DWORD[nStrokeCount];

        for (i = 0; i < nStrokeCount; i ++)
            lpPolyPoints[i] = 2;

        lpPoints[0].x = rectHeader.left;
        lpPoints[0].y = rectHeader.bottom - 1;
        lpPoints[1].x = rectHeader.right;
        lpPoints[1].y = rectHeader.bottom - 1;

        LPPOINT lpCurPoins = lpPoints + 2;

        for (i = 0; i < m_pColumns->GetSize(); i++)
        {
            CGridColumn* pColumn = m_pColumns->GetAt(i);
            Rect = pColumn->m_rect;
            if (pColumn->m_bVisible && Rect.right > VisLeft && Rect.left < VisRight)
            {
                Rect.OffsetRect(-VisLeft, 0);
            
                lpCurPoins[0].x = Rect.right - 1;
                lpCurPoins[0].y = Rect.top;
                lpCurPoins[1].x = Rect.right - 1;
                lpCurPoins[1].y = Rect.bottom;

                lpCurPoins += 2;
            }
        }
    
        BOOL bSuccess;
        CPen *pOldPen;
        CPen penGridLines;
        penGridLines.CreatePen(PS_SOLID, 1, GetLinesColor());
        pOldPen = pDC->SelectObject(&penGridLines);
    
        bSuccess = pDC->PolyPolyline(lpPoints, lpPolyPoints, nStrokeCount);
        ASSERT(bSuccess);
    
        pOldPen = pDC->SelectObject(pOldPen);

        delete[] lpPoints;
        delete[] lpPolyPoints;
    }
}

void CCursorGridCtrl::PaintCells(CDC *pDC)
{
    if (!m_pColumns->GetSize() || m_nTopRowIndex == -1 || !m_rows.GetSize())
    {
        if (IsFocused())
        {
            CRect rect;
            GetClientRect(rect);
            rect.bottom = m_nRowHeight - 1;
            if (m_bShowHeader)
                rect.OffsetRect(0, GetHeaderHeight());
            pDC->DrawFocusRect(rect);
        }
        return;
    }
    
    CRect ClientRect;
    CFont* pOldFont;
    pOldFont = pDC->SelectObject(&m_font);
    
    GetClientRect(ClientRect);
    int VisLeft = GetOrigin();
    int VisRight = VisLeft + ClientRect.right;
    int nOffsetY = GetHeaderRect().Height();

    int i;
    struct stColInfo
    {
        int nIndex;
        RECT rect;
    } *pColInfo = new stColInfo[m_pColumns->GetSize()];
    
    stColInfo* pCurInfo = pColInfo;
    for (i = 0; i < m_pColumns->GetSize(); i++)
    {
        CGridColumn* pColumn = m_pColumns->GetAt(i);
        CRect& rectCol = pColumn->m_rect;
        if (pColumn->m_bVisible && rectCol.right > VisLeft && rectCol.left < VisRight)
        {
            pCurInfo->nIndex = i;
            RECT& rectInfo = pCurInfo->rect;
            rectInfo.left = rectCol.left - VisLeft;
            rectInfo.right = rectCol.right - VisLeft;
            rectInfo.top = rectCol.top + nOffsetY;
            rectInfo.bottom = rectInfo.top + m_nRowHeight;

            pCurInfo++;
        }
    }
    
    CRect rectDraw(ClientRect.left, nOffsetY, ClientRect.right, nOffsetY + m_nRowHeight);

    stColInfo* pEndInfo = pCurInfo;
    int nVisibleRows = m_nVisibleRows;
    BOOL bIsFocused = IsFocused();
    for (i = m_nTopRowIndex; i < m_rows.GetSize() && nVisibleRows; i++, nVisibleRows--)
    {
        if (pDC->RectVisible(rectDraw))
        {
            CGridRow* pGridRow = m_rows[i];
            for (pCurInfo = pColInfo; pCurInfo != pEndInfo; pCurInfo++)
            {
                BOOL bSelected;
                if (pGridRow->m_bIsCurrent)
                {
                    if (m_RowSelectMode == rsmRow)
                        bSelected = TRUE;
                    else
                        bSelected = m_nCurrentColumn == pCurInfo->nIndex;
                }
                else
                    bSelected = FALSE;

                BOOL bAlterBack;
                if (m_bUseAlternationRowColor)
                {
                    if (m_bEvenRow)
                        bAlterBack = !(i & 1);
                    else
                        bAlterBack = (i & 1);
                }
                else
                    bAlterBack = FALSE;

                DrawCell(pDC, &pCurInfo->rect, pGridRow->m_cells[pCurInfo->nIndex], pGridRow, bSelected, bAlterBack);

                if (bIsFocused && m_RowSelectMode == rsmCell && pGridRow->m_bIsCurrent && m_nCurrentColumn == pCurInfo->nIndex)
                {
                    CRect Rect(pCurInfo->rect);
                    Rect.right--;
                    Rect.bottom--;
                    pDC->DrawFocusRect(Rect);
                }
                ::OffsetRect(&pCurInfo->rect, 0, m_nRowHeight);
            }
            if (bIsFocused && m_RowSelectMode == rsmRow && pGridRow->m_bIsCurrent)
            {
                CRect rect(rectDraw);
                rect.bottom--;
                pDC->DrawFocusRect(rect);
            }
        }
        else
        {
            for (pCurInfo = pColInfo; pCurInfo != pEndInfo; pCurInfo++)
                ::OffsetRect(&pCurInfo->rect, 0, m_nRowHeight);
        }
        rectDraw.OffsetRect(0, m_nRowHeight);
    }

    delete[] pColInfo;

    pDC->SelectObject(pOldFont);
}

void CCursorGridCtrl::DrawCell(CDC* pDC, LPRECT pRect, CGridItem* pGridItem, CGridRow* pGridRow, BOOL bSelected, BOOL bAlterBack)
{
    CRect rect(pRect);
    if (!rect.Width())
        return;
    
    int nRetVal;
    rect.right--;
    rect.bottom--;

    COLORREF crBack, crText;
    if (bSelected)
    {
        if (m_crSelectionBack != CLR_DEFAULT)
            crBack = m_crSelectionBack;
        else
            crBack = ::GetSysColor(COLOR_HIGHLIGHT);

        if (m_crSelectionText != CLR_DEFAULT)
            crText = m_crSelectionText;
        else
            crText = ::GetSysColor(COLOR_HIGHLIGHTTEXT);
    }
    else
    {
        if (pGridItem->m_crBack != CLR_DEFAULT)
            crBack = pGridItem->m_crBack;
        else if (pGridRow->m_crBack != CLR_DEFAULT)
            crBack = pGridRow->m_crBack;
        else if (pGridItem->m_pGridColumn->m_crBack != CLR_DEFAULT)
            crBack = pGridItem->m_pGridColumn->m_crBack;
        else if (bAlterBack)
            crBack = m_crAlternationRowBack == CLR_DEFAULT ? COLOR_ALTERNATION : m_crAlternationRowBack;
		else if (m_crBack != CLR_DEFAULT)
			crBack = m_crBack;
        else
            crBack = ::GetSysColor(COLOR_WINDOW);

        if (pGridItem->m_crText != CLR_DEFAULT)
            crText = pGridItem->m_crText;
        else if (pGridRow->m_crText != CLR_DEFAULT)
            crText = pGridRow->m_crText;
        else if (pGridItem->m_pGridColumn->m_crText != CLR_DEFAULT)
            crText = pGridItem->m_pGridColumn->m_crText;
		else if (m_crText != CLR_DEFAULT)
			crText = m_crText;
        else
            crText = ::GetSysColor(COLOR_WINDOWTEXT);

    }

    pDC->FillSolidRect(rect, crBack);
    pDC->SetTextColor(crText);
    
    if (pGridItem->m_wFlagsMask & CGI_CHECKBOX)
    {
        int nImageIndex;
        switch (pGridItem->m_wFlagsMask & CGI_CHECKBOX_STATE)
        {
        case CGI_CHECKBOX_UNCHECKED: nImageIndex = iiCheckBoxUnchecked; break;
        case CGI_CHECKBOX_CHECKED: nImageIndex = iiCheckBoxChecked; break;
        default: nImageIndex = iiCheckBoxUndefined; break;
        }
        m_ilInternalImages.Draw(pDC, nImageIndex, rect.TopLeft(), ILD_NORMAL);
        rect.left += 16;
    }

    if (pGridItem->m_wFlagsMask & CGI_IMAGE && rect.Width())
    {
        int nImageWidth;
        int nImageNumber = pGridItem->m_nImageNumber;
        if (nImageNumber == -1)
        {
            CRect ImageRect(rect);
            CPoint point = pDC->GetWindowOrg();
            CRect rectClip(ImageRect);
            rectClip.OffsetRect(-point.x, -point.y);
            CRgn rgn;
            rgn.CreateRectRgnIndirect(rectClip);
            pDC->SelectClipRgn(&rgn);
            if (pGridItem->DrawImage(pDC, ImageRect))
                nImageWidth = ImageRect.Width();
            else
                nImageWidth = 0;
            pDC->SelectClipRgn(NULL);
            rgn.DeleteObject();
            pGridItem->m_pGridColumn->m_nImageWidth = nImageWidth;
        }
        else
        {
            CImageList* pRowsImages = pGridItem->m_pGridColumn->m_pRowsImages;
            if (pRowsImages)
                if (nImageNumber < pRowsImages->GetImageCount())
                {
                    pRowsImages->Draw(pDC, nImageNumber, CPoint(rect.left, rect.top), ILD_NORMAL);
                    nImageWidth = pGridItem->m_pGridColumn->m_nImageWidth;
                }
        }
        rect.left += nImageWidth;
    }

    if (pGridItem->m_wFlagsMask & CGI_TEXT && pGridItem->m_strText.GetLength() && rect.Width())
    {
        CFont* pOldFont = NULL;
        if (pGridItem->m_pFont)
            pOldFont = pDC->SelectObject(pGridItem->m_pFont);
        else if (pGridRow->m_pFont)
            pOldFont = pDC->SelectObject(pGridRow->m_pFont);
        else if (pGridItem->m_pGridColumn->m_pFont)
            pOldFont = pDC->SelectObject(pGridItem->m_pGridColumn->m_pFont);

        DRAWTEXTPARAMS dtp;
        dtp.cbSize = sizeof(DRAWTEXTPARAMS);
        dtp.iLeftMargin = GetLeftTextMargin();
        dtp.iRightMargin = GetRightTextMargin();

        CPoint point = pDC->GetWindowOrg();
        CRect rectClip(rect);
        rectClip.OffsetRect(-point.x, -point.y);
        CRgn rgn;
        rgn.CreateRectRgnIndirect(rectClip);
        pDC->SelectClipRgn(&rgn);

        UINT nFlags = DT_NOPREFIX | DT_NOCLIP;

        WORD wHAlign = pGridItem->m_wFlagsMask & CGI_TEXT_H_ALIGN;
        if (wHAlign == CGI_TEXT_LEFT)
            nFlags |= DT_LEFT | DT_END_ELLIPSIS;
        else if (wHAlign == CGI_TEXT_RIGHT)
        {
            nFlags |= DT_RIGHT | DT_END_ELLIPSIS;
            dtp.iRightMargin++; //one pixel for vertical line
        }
        else if (wHAlign == CGI_TEXT_CENTER)
            nFlags |= DT_CENTER;
        else //deault is feft
            nFlags |= DT_LEFT | DT_END_ELLIPSIS;

        char arTextBuffer[1024];
        int nLen = pGridItem->m_strText.GetLength();
        if (nLen < 1024)
            strcpy(arTextBuffer, pGridItem->m_strText);
        else
        {
            nLen = 1023;
            strncpy(arTextBuffer, pGridItem->m_strText, nLen);
            arTextBuffer[nLen] = 0;
        }

        rect.OffsetRect(0, GetTopTextMargin());
        pDC->SetBkMode(TRANSPARENT);
        nRetVal = ::DrawTextEx(
            pDC->m_hDC,
            arTextBuffer,
            lstrlen(arTextBuffer),
            rect,
            nFlags,
            &dtp
            );

        pDC->SelectClipRgn(NULL);
        rgn.DeleteObject();
        
        if (pOldFont)
            pDC->SelectObject(pOldFont);
    }
}

void CCursorGridCtrl::DrawHeader(CDC *pDC, CGridColumn* pColumn, LPRECT pRect)
{
    int nRetVal;
    COLORREF cr;
    if (pColumn->m_crHeaderBack != CLR_DEFAULT)
        cr = pColumn->m_crHeaderBack;
    else
        cr = ::GetSysColor(COLOR_BTNFACE);

    pDC->FillSolidRect(pRect, cr);

    if (pColumn->m_crHeaderText != CLR_DEFAULT)
        cr = pColumn->m_crHeaderText;
    else
        cr = ::GetSysColor(COLOR_WINDOWTEXT);

    pDC->SetTextColor(cr);

    CRect rect(pRect);
    if (pColumn->m_OrderMarker != omNone)
        rect.right -= 16;
    
    if (rect.Width() && pColumn->m_strCaption.GetLength())
    {
        CFont* pOldFont = NULL;
        if (pColumn->m_pHeaderFont)
            pOldFont = pDC->SelectObject(pColumn->m_pHeaderFont);

        DRAWTEXTPARAMS dtp;
        dtp.cbSize = sizeof(DRAWTEXTPARAMS);
        dtp.iLeftMargin = GetLeftTextMargin();
        dtp.iRightMargin = GetRightTextMargin();

        CPoint point = pDC->GetWindowOrg();
        CRect rectClip(rect);
        rectClip.OffsetRect(-point.x, -point.y);
        CRgn rgn;
        rgn.CreateRectRgnIndirect(rectClip);
        pDC->SelectClipRgn(&rgn);

        UINT nFlags = DT_END_ELLIPSIS | DT_NOPREFIX | DT_LEFT | DT_NOCLIP;

        char arTextBuffer[1024];
        int nLen = pColumn->m_strCaption.GetLength();
        if (nLen < 1024)
            strcpy(arTextBuffer, pColumn->m_strCaption);
        else
        {
            nLen = 1023;
            strncpy(arTextBuffer, pColumn->m_strCaption, nLen);
            arTextBuffer[nLen] = 0;
        }
        
        rect.OffsetRect(0, GetTopTextMargin());
        pDC->SetBkMode(TRANSPARENT);
        nRetVal = ::DrawTextEx(
            pDC->m_hDC,
            arTextBuffer,
            lstrlen(arTextBuffer),
            rect,
            nFlags,
            &dtp
            );

        pDC->SelectClipRgn(NULL);
        rgn.DeleteObject();
        
        if (pOldFont)
            pDC->SelectObject(pOldFont);
    }

    if (pColumn->m_OrderMarker != omNone)
    {
        int nImageIndex;
        switch (pColumn->m_OrderMarker)
        {
        case omAsc: nImageIndex = iiOrderAsc; break;
        case omDesc: nImageIndex = iiOrderDesc; break;
        case omAsc2: nImageIndex = iiOrderAsc2; break;
        case omDesc2: nImageIndex = iiOrderDesc2; break;
        case omAsc3: nImageIndex = iiOrderAsc3; break;
        case omDesc3: nImageIndex = iiOrderDesc3; break;
        }
        m_ilInternalImages.Draw(pDC, nImageIndex, CPoint(pRect->right - 16, pRect->top), ILD_NORMAL);
    }

    if (m_HeadersStyle == hs3D)
        Paint3DFrame(pDC, pRect, BF_RECT);
}

void CCursorGridCtrl::Paint3DFrame(CDC *pDC, LPRECT lpRect, UINT nSideFlags)
{
    CRect rect(*lpRect);
    BOOL bSuccess;
    
    bSuccess = pDC->DrawEdge(rect, BDR_RAISEDOUTER, nSideFlags & (~BF_TOPLEFT));
    ASSERT(bSuccess);
    
    if (nSideFlags & BF_BOTTOM)
        rect.bottom --;
    if (nSideFlags & BF_RIGHT)
        rect.right --;
    
    bSuccess = pDC->DrawEdge(rect, BDR_RAISEDINNER, nSideFlags & (~BF_BOTTOMRIGHT));
    ASSERT(bSuccess);
    
    if (nSideFlags & BF_TOP)
        rect.top ++;
    if (nSideFlags & BF_LEFT)
        rect.left ++;
    
    bSuccess = pDC->DrawEdge(rect, BDR_RAISEDINNER, nSideFlags & (~BF_TOPLEFT));
    ASSERT(bSuccess);
    
    if (nSideFlags & BF_BOTTOM)
        rect.bottom --;
    if (nSideFlags & BF_RIGHT)
        rect.right --;
    
    bSuccess = pDC->DrawEdge(rect, BDR_RAISEDOUTER, nSideFlags & (~BF_BOTTOMRIGHT));
    ASSERT(bSuccess);
}

void CCursorGridCtrl::RecalcColumsWidth()
{
    CRect ClientRect;
    GetClientRect(ClientRect);
    int nGridWidth;
    if (ClientRect.Width() < m_pColumns->m_nMinWidth)
        nGridWidth = m_pColumns->m_nResizableMinWidth;
    else
        nGridWidth = ClientRect.Width() - (m_pColumns->m_nMinWidth - m_pColumns->m_nResizableMinWidth);

    
    CRect rect(0, 0, 0, GetHeaderHeight());
    CGridColumn* pLastColumn = NULL;
    for (int i = 0; i < m_pColumns->GetSize(); i++)
    {
        CGridColumn* pColumn = m_pColumns->GetAt(i);
        if (pColumn->m_bVisible)
        {
            rect.left += rect.Width();
            if (pColumn->m_bFixedWidth)
                rect.right = rect.left + pColumn->m_nMinWidth;
            else
                rect.right = rect.left + MulDiv(pColumn->m_nMinWidth, nGridWidth, m_pColumns->m_nResizableMinWidth);
            pColumn->m_rect = rect;
            pLastColumn = pColumn;
        }
    }
    if (pLastColumn)
    {
        if (ClientRect.Width() < m_pColumns->m_nMinWidth)
            pLastColumn->m_rect.right = m_pColumns->m_nMinWidth;
        else
            pLastColumn->m_rect.right = ClientRect.right;
    }

    SetHScrollInfo();
    Invalidate();
}

void CCursorGridCtrl::SetHScrollInfo()
{
    SCROLLINFO si;
    si.cbSize = sizeof(SCROLLINFO);
    si.nMin = 0;
    si.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
    int nGridWidth = GetGridRect().Width();
	int nColumnsWidth = m_pColumns->GetVisibleWidth();
    if (nGridWidth < nColumnsWidth)
    {
        si.nMax = nColumnsWidth;
        si.nPos = GetOrigin();
        si.nPage = nGridWidth;
        SetScrollInfo(SB_HORZ, &si);
        ShowScrollBar(SB_HORZ);
    }
    else
    {
        si.nMax = 0;
        si.nPos = 0;
        si.nPage = 0;
        SetScrollInfo(SB_HORZ, &si);
        ShowScrollBar(SB_HORZ, FALSE);
    }
}

void CCursorGridCtrl::OnSize(UINT nType, int cx, int cy) 
{
	CWnd::OnSize(nType, cx, cy);
    RecalcColumsWidth();
    CalcRowsParams();
}

BOOL CCursorGridCtrl::HScrollLine(SCROLLINFO& si, int nDirection)
{
	int nPrevPos = si.nPos;

	if (nDirection < 0)
	{
		int nLeft = si.nPos;
		for (int i = 0; i < m_pColumns->GetSize(); i++)
		{
			LPRECT pRect = m_pColumns->GetAt(i)->m_rect;
			if (pRect->right >= nLeft)
			{
				si.nPos += pRect->left - nLeft;
				break;
			}
		}
	}
	else if (nDirection > 0)
	{
		CRect ClientRect;
		GetClientRect(ClientRect);
        int nRight = si.nPos + ClientRect.right;
        for (int i = m_pColumns->GetUpperBound(); i >= 0; i--)
        {
            LPRECT pRect = m_pColumns->GetAt(i)->m_rect;
            if (pRect->left <= nRight)
            {
                si.nPos += pRect->right - nRight;
                break;
            }
        }
	}
	if (nPrevPos != si.nPos)
	{
		SetScrollInfo(SB_HORZ, &si);
		Invalidate();
		return TRUE;
	}
	else
		return FALSE;

}

void CCursorGridCtrl::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
	if(GetFocus() != this) 
		SetFocus();
    SCROLLINFO si;
    CRect rect;
    BOOL bSuccess;

    m_Tip.Hide();
    
    GetClientRect(rect);
    
    bSuccess = GetScrollInfo(SB_HORZ, &si);
    ASSERT(bSuccess);

    si.cbSize = sizeof(SCROLLINFO);
    si.fMask = SIF_POS;

    switch (nSBCode)
	{
    case SB_LINELEFT:
		HScrollLine(si, -1);
        break;
    case SB_LINERIGHT:
		HScrollLine(si, 1);
        break;
    case SB_PAGELEFT:
        si.nPos -= rect.right;
        if (si.nPos < 0)
            si.nPos = 0;
        bSuccess = SetScrollInfo(SB_HORZ, &si);
        if (!bSuccess)
            break;
        Invalidate();
        break;
    case SB_PAGERIGHT:
        si.nPos += rect.right;
        bSuccess = SetScrollInfo(SB_HORZ, &si);
        if (!bSuccess)
            break;
        Invalidate();
        break;
    case SB_THUMBPOSITION:
    case SB_THUMBTRACK:
        bSuccess = GetScrollInfo(SB_HORZ, &si, SIF_TRACKPOS);
        ASSERT(bSuccess);
        si.fMask = SIF_POS;
        si.nPos = si.nTrackPos;
        bSuccess = SetScrollInfo(SB_HORZ, &si);
        if (!bSuccess)
            break;
        Invalidate();
        break;
    default:
        break;
    }
    
	CWnd::OnHScroll(nSBCode, nPos, pScrollBar);
}

UINT CCursorGridCtrl::OnNcHitTest(CPoint point) 
{
    m_hitTest = point;
    ScreenToClient(&m_hitTest);
	
	return CWnd::OnNcHitTest(point);
}

BOOL CCursorGridCtrl::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) 
{
    if (nHitTest == HTCLIENT && m_bShowHeader)
    {
        CRect rect(GetHeaderRect());
        
        if (rect.PtInRect(m_hitTest) && GetResizeHeaderAt(m_hitTest))
        {
            HINSTANCE hInst = AfxFindResourceHandle(MAKEINTRESOURCE(AFX_IDC_HSPLITBAR), RT_GROUP_CURSOR);
            HCURSOR hCursor = ::LoadCursor(hInst, MAKEINTRESOURCE(AFX_IDC_HSPLITBAR));
            ::SetCursor(hCursor);
            return TRUE;
        }
    }
	
	return CWnd::OnSetCursor(pWnd, nHitTest, message);
}

CGridColumn* CCursorGridCtrl::GetResizeHeaderAt(CPoint point)
{
    CRect rect;
    for (int i = 0; i < m_pColumns->GetSize(); i++)
    {
        CGridColumn* pGridColumn = m_pColumns->GetAt(i);
        if (pGridColumn->m_bVisible && !pGridColumn->m_bFixedWidth)
        {
            rect = pGridColumn->GetBoundsRect();
            rect.left = rect.right - RESIZE_AREA_WIDTH;
            rect.right += RESIZE_AREA_WIDTH;
            if (rect.PtInRect(point))
                return pGridColumn;
        }
    }

    return NULL;
}

int CCursorGridCtrl::HitTestHeader(CPoint point)
{
    CRect rect;
    for (int i = 0; i < m_pColumns->GetSize(); i++)
    {
        CGridColumn* pGridColumn = m_pColumns->GetAt(i);
        if (pGridColumn->m_bVisible)
            if (pGridColumn->GetBoundsRect().PtInRect(point))
                return i;
    }
    return -1;
}

void CCursorGridCtrl::OnLButtonDown(UINT nFlags, CPoint point) 
{
	if(GetFocus() != this) 
		SetFocus();

    m_Tip.Hide();
    m_hitMouseButton = point;

    int nColumn, nRow;

    switch(HitTest(point, nColumn, nRow))
    {
    case ghtHeader:
        m_pCapturedColumn = GetResizeHeaderAt(point);
        if (m_pCapturedColumn)
            m_CaptureType = ctResizeColumn;
        else
        {
            m_pCapturedColumn = m_pColumns->GetAt(nColumn);
            m_CaptureType = ctClickHeader;
        }

        if (m_pCapturedColumn)
            SetCapture();

        break;
    case ghtCell:
        SetCurrent(nRow, nColumn);
        if (m_bEnableDrag)
            m_CaptureType = ctDragDelay;
		else
		{
			m_CaptureType = ctScroll;
	        SetTimer(TIMER_ID_SCROLL, SCROLL_TIMEOUT, NULL);
		}
		SetCapture();
        break;
    case ghtCheckBox:
        SetCurrent(nRow, nColumn);
        OnClickCheckBox();
        break;
    }
    CWnd::OnLButtonDown(nFlags, point);
}

GridHitTest CCursorGridCtrl::HitTest(CPoint point, int &nColumn, int &nRow)
{
    CRect HeaderRect = GetHeaderRect();
    
    if (m_bShowHeader && HeaderRect.PtInRect(point))
    {
        nColumn = HitTestHeader(point);
        if (nColumn != -1)
            return ghtHeader;
    }
    else
    {
        nColumn = HitTestHeader(CPoint(point.x, point.y % m_nRowHeight));
        nRow = m_nTopRowIndex + (point.y - HeaderRect.Height()) / m_nRowHeight;
        if (nRow <= m_rows.GetUpperBound())
        {
            if (nColumn != -1)
            {
                if (m_rows[nRow]->m_cells[nColumn]->m_wFlagsMask & CGI_CHECKBOX)
                {
                    CPoint ptTL = m_pColumns->GetAt(nColumn)->GetBoundsRect().TopLeft();
                    ptTL.y += HeaderRect.bottom + (nRow - m_nTopRowIndex) * m_nRowHeight;
                    CRect rect(ptTL, CSize(10, 10));
                    rect.OffsetRect(3, 3);
                    if (rect.PtInRect(point))
                        return ghtCheckBox;
                }
                return ghtCell;
            }
        }
        else
            nRow = -1;
    }
    return ghtSpace;
}

void CCursorGridCtrl::SetCurrent(int nRow, int nColumn)
{
    SetCurrentCell(nRow, nColumn);
/*
    if (m_nCurrentDataRowIndex == m_nTopRowIndex + m_nVisibleRows - 1 && m_bLastRowNotFull)
        ScrollDown(1);
*/
}

void CCursorGridCtrl::ScrollCurrentCell(int xDirection, int yDirection)
{
	if (xDirection)
	{
		int nIndex = GetNextColumn(m_nCurrentColumn, xDirection < 0 ? -1 : 1);
		if (nIndex != -1)
			SetCurrentCell(m_nCurrentDataRowIndex, nIndex);
	}
	
	if (yDirection < 0)
	{
		if (m_nCurrentDataRowIndex == -1 && m_pCurrentDataRowOutOfWindow)
			Reset(m_pCurrentDataRowOutOfWindow);
		
		if (m_nCurrentDataRowIndex != -1)
		{
			if (m_nCurrentDataRowIndex > m_nTopRowIndex && m_nCurrentDataRowIndex < m_nTopRowIndex + m_nVisibleRows)
				SetCurrentCell(m_nCurrentDataRowIndex - 1, m_nCurrentColumn);
			else
			{
				int nScroll = m_nTopRowIndex - m_nCurrentDataRowIndex + 1;
				if (nScroll > 0)
				{
					ScrollUp(nScroll);
					SetCurrentCell(m_nTopRowIndex, m_nCurrentColumn);
					RedrawWindow(GetGridRect());
				}
				else
				{
					nScroll += GetPageSize() - 1;
					if (nScroll < 0)
						ScrollDown(-nScroll);
					SetCurrentCell(m_nTopRowIndex + GetPageSize() - 1, m_nCurrentColumn);
					RedrawWindow(GetGridRect());
				}
			}
		}
	}
	else if (yDirection > 0)
	{
		if (m_nCurrentDataRowIndex == -1 && m_pCurrentDataRowOutOfWindow)
			Reset(m_pCurrentDataRowOutOfWindow);
		
		if (m_nCurrentDataRowIndex != -1)
		{
			if (m_nCurrentDataRowIndex >= m_nTopRowIndex && m_nCurrentDataRowIndex < m_nTopRowIndex + GetPageSize() - 1)
			{
				if (m_nCurrentDataRowIndex < m_rows.GetUpperBound())
					SetCurrentCell(m_nCurrentDataRowIndex + 1, m_nCurrentColumn);
			}
			else
			{
				int nScroll = m_nCurrentDataRowIndex - (m_nTopRowIndex + GetPageSize()) + 2;
				if (nScroll > 0)
				{
					ScrollDown(nScroll);
					SetCurrentCell(m_nTopRowIndex + GetPageSize() - 1, m_nCurrentColumn);
					RedrawWindow(GetGridRect());
				}
				else
				{
					nScroll = m_nTopRowIndex - m_nCurrentDataRowIndex - 1;
					if (nScroll > 0)
						ScrollUp(nScroll);
					SetCurrentCell(m_nTopRowIndex, m_nCurrentColumn);
					RedrawWindow(GetGridRect());
				}
			}
		}
	}
}

void CCursorGridCtrl::OnLButtonUp(UINT nFlags, CPoint point) 
{
    switch (m_CaptureType)
    {
    case ctResizeColumn:
        {
            int i, nTotalWidth = 0, nTotalMinWidth = m_pColumns->m_nMinWidth;
            for (i = 0; i < m_pColumns->GetSize(); i++)
            {
                CGridColumn* pGridColumn = m_pColumns->GetAt(i);
                if (pGridColumn->m_bVisible)
                {
                    if (pGridColumn->m_bFixedWidth)
                        nTotalMinWidth -= pGridColumn->m_nMinWidth;
                    else
                        nTotalWidth += pGridColumn->m_rect.Width();
                }
            }
            m_pColumns->m_nResizableMinWidth = 0;
            for (i = 0; i < m_pColumns->GetSize(); i++)
            {
                CGridColumn* pGridColumn = m_pColumns->GetAt(i);
                if (pGridColumn->m_bVisible)
                {
                    if (!pGridColumn->m_bFixedWidth)
                    {
                        pGridColumn->m_nMinWidth = MulDiv(pGridColumn->m_rect.Width(), nTotalMinWidth, nTotalWidth);
                        m_pColumns->m_nResizableMinWidth += pGridColumn->m_nMinWidth;
                    }
                }
            }
        }
        break;
    case ctClickHeader:
        {
            int nColumnIndex = HitTestHeader(point);
            if (nColumnIndex != -1 && m_pColumns->GetAt(nColumnIndex) == m_pCapturedColumn)
                OnHeaderSelection(m_pCapturedColumn);
        }
        break;
	case ctScroll:
        KillTimer(TIMER_ID_SCROLL);
		break;
    }

    if (m_CaptureType != ctNone)
    {
        ReleaseCapture();
        m_CaptureType = ctNone;
    }
    
	CWnd::OnLButtonUp(nFlags, point);
}

void CCursorGridCtrl::OnRButtonUp(UINT nFlags, CPoint point)
{
    if (m_CaptureType != ctNone)
    {
        ReleaseCapture();
        m_CaptureType = ctNone;
    }
	
	CWnd::OnRButtonUp(nFlags, point);
}

void CCursorGridCtrl::OnMouseMove(UINT nFlags, CPoint point) 
{
    switch (m_CaptureType)
    {
    case ctResizeColumn:
        StepColResize(point.x);
        break;
    case ctClickHeader:
        {
            int dx = point.x - m_hitMouseButton.x;
            if (dx < 0)
                dx = -dx;
            int dy = point.y - m_hitMouseButton.y;
            if (dy < 0)
                dy = -dy;
            if (dx > RESIZE_AREA_WIDTH || dy > RESIZE_AREA_WIDTH)
            {
                if (m_bChangePositionOfColumns || m_pCapturedColumn->m_bChangeVisible)
                {
                    m_Tip.Hide();
                    CRect rect = m_pCapturedColumn->GetBoundsRect();
                    m_ReposColumnWnd.Show(m_pCapturedColumn, CSize(m_hitMouseButton.x - rect.left, m_hitMouseButton.y - rect.top));
                    m_CaptureType = ctMoveColumn;
                }
                else
                    m_CaptureType = ctNone;
            }
        }
        break;
    case ctMoveColumn:
        break;
    case ctDragDelay:
        {
            CRect rect(m_hitMouseButton, m_hitMouseButton);
            rect.InflateRect(RESIZE_AREA_WIDTH, RESIZE_AREA_WIDTH);
            if (!rect.PtInRect(point))
            {
                m_CaptureType = ctNone;
                ReleaseCapture();
				RedrawWindow(GetGridRect());
                OnBeginDrag();
            }
        }
        break;
	case ctScroll:
		{
			CRect rect = GetGridRect();
			if (rect.PtInRect(point))
			{
				int nColumn, nRow;
				if (HitTest(point, nColumn, nRow) == ghtCell)
					SetCurrent(nRow, nColumn);
			}
			else
			{
				int xDirection = point.x < rect.TopLeft().x ? -1 : point.x > rect.BottomRight().x ? 1 : 0;
				int yDirection = point.y < rect.TopLeft().y ? -1 : point.y > rect.BottomRight().y ? 1 : 0;
				ScrollCurrentCell(xDirection, yDirection);
			}
		}
		break;
    default:
        if (!m_ColumnHelper.IsDisplay())
            ShowHint(point);
        break;
    }
	CWnd::OnMouseMove(nFlags, point);
}

void CCursorGridCtrl::ShowHint(CPoint point)
{
    CRect HeaderRect = GetHeaderRect();
    int nColIndex = HitTestHeader(CPoint(point.x, 0));
    if (nColIndex != -1)
    {
        CString strTipText;
        CRect TipRect;
        DWORD dwTipedObject = 0;

        CGridColumn* pGridColumn = m_pColumns->GetAt(nColIndex);
        TipRect = pGridColumn->GetBoundsRect();
        BOOL bByCursor;
        CFont* pFont = &m_font;
        if (HeaderRect.PtInRect(point))
        {
            TipRect.left += RESIZE_AREA_WIDTH;
            if (pGridColumn->m_OrderMarker != omNone)
                TipRect.right -= 16;
            TipRect.right -= RESIZE_AREA_WIDTH;
            strTipText = pGridColumn->GetCaption();
            dwTipedObject = (DWORD)pGridColumn;
            bByCursor = TRUE;
            if (pGridColumn->m_pHeaderFont)
                pFont = pGridColumn->m_pHeaderFont;
        }
        else
        {
            if (m_nTopRowIndex == -1)
                return;
            
            int nOffsetInRows = (point.y - HeaderRect.Height()) / m_nRowHeight;
            int nRowIndex = m_nTopRowIndex + nOffsetInRows;
            if (nRowIndex < m_rows.GetSize())
            {
                CGridRow* pGridRow = m_rows[nRowIndex];
                CGridItem* pGridItem = pGridRow->m_cells[nColIndex];
                strTipText = pGridItem->m_strText;
                TipRect.OffsetRect(0, HeaderRect.Height() + nOffsetInRows * m_nRowHeight);
                if (pGridItem->m_wFlagsMask & CGI_CHECKBOX)
                    TipRect.left += 16;
                if (pGridItem->m_wFlagsMask & CGI_IMAGE)
                    TipRect.left += pGridColumn->m_nImageWidth;
                dwTipedObject = (DWORD)pGridItem;
                bByCursor = FALSE;
                if (pGridItem->m_pFont)
                    pFont = pGridItem->m_pFont;
                else if (pGridRow->m_pFont)
                    pFont = pGridRow->m_pFont;
                else if (pGridItem->m_pGridColumn->m_pFont)
                    pFont = pGridItem->m_pGridColumn->m_pFont;
            }
        }

        if (!TipRect.PtInRect(point))
            return;

        if (dwTipedObject)
    	    m_Tip.Show(strTipText, TipRect, dwTipedObject, pFont, bByCursor);
    }
}

void CCursorGridCtrl::StepColResize(int x)
{
    int nNewWidth = x + GetOrigin() - m_pCapturedColumn->m_rect.left;
    if (nNewWidth < RESIZE_MIN_WIDTH)
        nNewWidth = RESIZE_MIN_WIDTH;
    if (nNewWidth > 10000)
        nNewWidth = 10000;

    if (nNewWidth == m_pCapturedColumn->m_rect.Width())
        return;

    if (IS_SHIFT_PRESSED())
    {
        int dx = nNewWidth - m_pCapturedColumn->m_rect.Width();
		if (dx < 0)
		{
			int xav = GetGridRect().Width() - m_pColumns->GetVisibleWidth();
			dx = max(dx, xav);
		}

        for (int i = m_pColumns->GetUpperBound(); i >= 0; i--)
        {
            CGridColumn* pGridColumn = m_pColumns->GetAt(i);
            if (pGridColumn->m_bVisible)
            {
                if (pGridColumn == m_pCapturedColumn)
                {
                    pGridColumn->m_rect.right += dx;
                    break;
                }
                else
                    pGridColumn->m_rect.OffsetRect(dx, 0);
            }
        }

        m_pColumns->m_nMinWidth = 0;
        for (i = 0; i < m_pColumns->GetSize(); i++)
        {
            CGridColumn* pGridColumn = m_pColumns->GetAt(i);
            if (pGridColumn->m_bVisible)
            {
                pGridColumn->m_nMinWidth = pGridColumn->m_rect.Width();
                m_pColumns->m_nMinWidth += pGridColumn->m_nMinWidth;
            }
        }
        SetHScrollInfo();
    }
    else if (IS_CTRL_PRESSED())
    {
        int dx = nNewWidth - m_pCapturedColumn->m_rect.Width();
        for (int i = m_pColumns->IndexOf(m_pCapturedColumn) + 1; i < m_pColumns->GetSize(); i++)
        {
            CGridColumn* pGridColumn = m_pColumns->GetAt(i);
            if (pGridColumn->m_bVisible)
            {
                if (pGridColumn->m_bFixedWidth)
                    break;

                int nWidth = pGridColumn->m_rect.Width();
                nWidth -= dx;
                if (nWidth < RESIZE_MIN_WIDTH)
                    nWidth = RESIZE_MIN_WIDTH;
                pGridColumn->m_rect.left = pGridColumn->m_rect.right - nWidth;
                m_pCapturedColumn->m_rect.right = pGridColumn->m_rect.left;
                break;
            }            
        }        
    }
    else
    {
        int i;
        int nResizeWidth = 0;
        int CurX;
        for (i = m_pColumns->GetUpperBound(); i >= 0; i--)
        {
            CGridColumn* pGridColumn = m_pColumns->GetAt(i);
            if (pGridColumn == m_pCapturedColumn)
                break;
            if (pGridColumn->m_bVisible)
            {
                if (!nResizeWidth)
                    nResizeWidth = CurX = pGridColumn->m_rect.right;
                if (pGridColumn->m_bFixedWidth)
                    nResizeWidth -= pGridColumn->m_rect.Width();
            }
        }
        
        if (!nResizeWidth)
            return;
        
        int nNewResizeWidth = nResizeWidth - (m_pCapturedColumn->m_rect.left + nNewWidth);
        nResizeWidth -= m_pCapturedColumn->m_rect.right;
        
        for (i = m_pColumns->GetUpperBound(); i >= 0; i--)
        {
            CGridColumn* pGridColumn = m_pColumns->GetAt(i);
            if (pGridColumn == m_pCapturedColumn)
            {
                pGridColumn->m_rect.right = CurX;
                break;
            }

            if (pGridColumn->m_bVisible)
            {
                int nWidth;
                if (pGridColumn->m_bFixedWidth)
                    nWidth = pGridColumn->m_nMinWidth;
                else
                    nWidth = MulDiv(pGridColumn->m_rect.Width(), nNewResizeWidth, nResizeWidth);
                    if (nWidth < RESIZE_MIN_WIDTH)
                        nWidth = RESIZE_MIN_WIDTH;
                pGridColumn->m_rect.right = CurX;
                CurX -= nWidth;
                pGridColumn->m_rect.left = CurX;
            }
        }
        
    }
    Invalidate();
}

void CCursorGridCtrl::CalcRowsParams()
{
    if (m_nRowHeight == 0)
        return;

    int nVisChange = m_nVisibleRows;
    int nRowsHeight = GetGridRect().Height();
    m_nVisibleRows = nRowsHeight / m_nRowHeight;
    m_bLastRowNotFull = nRowsHeight % m_nRowHeight > 0;
    if (m_bLastRowNotFull)
        m_nVisibleRows++;
	if (m_nVisibleRows == 0)
		m_nVisibleRows = 1;

    if (!m_pProvider)
        return;

    nVisChange -= m_nVisibleRows;
    if (nVisChange < 0)
    {
        int nGetRows = m_nTopRowIndex + m_nVisibleRows - m_rows.GetSize();
        if (nGetRows > 0 && !m_bIsEOF)
            GetRowsDown(nGetRows);

        FillRows(m_nTopRowIndex + m_nVisibleRows + nVisChange, -nVisChange);
        SetVScrollInfo();

        if (m_bIsEOF && nGetRows > 1)
            ScrollUp(nGetRows - 1);
    }
    else if (nVisChange > 0)
    {
        int nScroll = m_nCurrentDataRowIndex - m_nTopRowIndex - m_nVisibleRows + 2;
        nScroll = min(nScroll, m_nCurrentDataRowIndex - m_nTopRowIndex);
        if (nScroll > 0)
        {
            if (m_bIsEOF && m_nCurrentDataRowIndex == m_rows.GetUpperBound())
            {
                m_nTopRowIndex++;
                InvalidateRect(GetGridRect());
            }
            else
                ScrollDown(nScroll);
        }
    }
    else
        SetVScrollInfo();
}

CRect CCursorGridCtrl::GetGridRect() const
{
    CRect rect;
    GetClientRect(rect);
    rect.top = GetHeaderRect().bottom;
    if (rect.top > rect.bottom)
        rect.top = rect.bottom;
    return rect;
}

void CCursorGridCtrl::PaintGridLines(CDC *pDC)
{
    int nStrokeCount;
    int nPointCount;
    int i;

    CRect rect = GetGridRect();

    int nFullVisibleRows = 0;
    if (m_bHorizontalLines)
        nFullVisibleRows = min(m_nVisibleRows, m_rows.GetSize() - m_nTopRowIndex);

    nStrokeCount = nFullVisibleRows;

    int VisLeft = GetOrigin();
    int VisRight = VisLeft + rect.right;

    if (m_bVerticalLines)
        for (i = 0; i < m_pColumns->GetSize(); i++)
        {
            CGridColumn* pColumn = m_pColumns->GetAt(i);
            CRect Rect = pColumn->m_rect;
            if (pColumn->m_bVisible && Rect.right > VisLeft && Rect.left < VisRight)
                nStrokeCount++;
        }

    if (nStrokeCount)
    {
        nPointCount = nStrokeCount * 2;
        
        LPPOINT lpPoints = new POINT[nPointCount];
        LPDWORD lpPolyPoints = new DWORD[nStrokeCount];

        for (i = 0; i < nStrokeCount; i ++)
            lpPolyPoints[i] = 2;
        
        //Horizontal
        int nLeft = rect.left;
        int nRight = rect.right;
        int y = rect.top;
        
        for (i = 0; i < nFullVisibleRows; i++)
        {
            y += m_nRowHeight;
            int nIndex = i * 2;

            lpPoints[nIndex].x = nLeft;
            lpPoints[nIndex].y = y - 1;
            lpPoints[nIndex + 1].x = nRight;
            lpPoints[nIndex + 1].y = y - 1;
        }

        //Vertical
        if (m_bVerticalLines)
        {
            int nTop = rect.top;
            int nBottom;
            if (m_bHorizontalLines)
                nBottom = y;
            else
                nBottom = rect.bottom;

            int c = 0;
            for (i = 0; i < m_pColumns->GetSize(); i++)
            {
                CGridColumn* pColumn = m_pColumns->GetAt(i);
                CRect Rect = pColumn->m_rect;
                if (pColumn->m_bVisible && Rect.right > VisLeft && Rect.left < VisRight)
                {
                    Rect.OffsetRect(-VisLeft, 0);

                    int x = Rect.left;
                    int nIndex = nFullVisibleRows * 2 + c * 2;
                    c++;
                
                    lpPoints[nIndex].x = x - 1;
                    lpPoints[nIndex].y = nTop;
                    lpPoints[nIndex + 1].x = x - 1;
                    lpPoints[nIndex + 1].y = nBottom;
                }
            }
        }
        
        BOOL bSuccess;
        CPen *pOldPen;
        CPen penGridLines;
        penGridLines.CreatePen(PS_SOLID, 1, GetLinesColor());
        pOldPen = pDC->SelectObject(&penGridLines);
        
        bSuccess = pDC->PolyPolyline(lpPoints, lpPolyPoints, nStrokeCount);
        ASSERT(bSuccess);
        
        pOldPen = pDC->SelectObject(pOldPen);

        delete[] lpPoints;
        delete[] lpPolyPoints;
    }
}

void CCursorGridCtrl::ClearRows()
{
    OnChangeCurrentData(NULL);
    for (int i = 0; i < m_rows.GetSize(); i++)
        delete m_rows[i];
    m_rows.RemoveAll();

    m_bIsBOF = TRUE;
    m_bIsEOF = TRUE;
}

CDataProvider* CCursorGridCtrl::GetDataProvider() const
{
    return m_pProvider;
}

void CCursorGridCtrl::SetDataProvider(CDataProvider* pProvider)
{
    ClearRows();
    if (m_nCurrentDataRowIndex == -1 && m_pCurrentDataRowOutOfWindow)
    {
        delete m_pCurrentDataRowOutOfWindow;
        m_pCurrentDataRowOutOfWindow = NULL;
    }

    if (m_pProvider)
        m_pProvider->SetControl(NULL);

    m_pProvider = pProvider;

    if (!m_pProvider)
    {
		if (m_hWnd)
			Invalidate();
        return;
    }

    for (int i = 0; i < m_pColumns->GetCount(); i++)
    {
        CGridColumn* pGridColumn = m_pColumns->GetAt(i);
        if (!pGridColumn->m_strDataField.IsEmpty())
            m_pProvider->QueryField(pGridColumn->m_strDataField);
    }

    m_pProvider->SetControl(*this);
}

void CCursorGridCtrl::Update()
{
    if (!m_pProvider)
        return;

    BOOL bReset = FALSE;

    if (m_DropTarget.m_bDragOn)
    {
        m_DropTarget.m_pDropOverRow = NULL;
        CRect rect;
        rect.SetRectEmpty();
        m_DropTarget.DrawDragRect(rect);
    }
    
    if (m_rows.GetSize())
    {
        if (m_nTopRowIndex & 1)
            m_bEvenRow = !m_bEvenRow;

        int i;
        for (i = 0; i < m_nTopRowIndex; i++)
            delete m_rows[i];
        m_rows.RemoveAt(0, m_nTopRowIndex);

        if (m_nCurrentDataRowIndex != -1)
            m_nCurrentDataRowIndex -= m_nTopRowIndex;
        m_nTopRowIndex = 0;
    
        for (i = 1; i < m_rows.GetSize(); i++)
            delete m_rows[i];
        m_rows.RemoveAt(1, m_rows.GetUpperBound());
    
        if (m_pProvider->RefreshRow(m_rows[0]->m_pDataRow))
        {
            GetRowsDown(m_nVisibleRows - 1);
            if (m_nCurrentDataRowIndex == -1)
            {
                if (!m_pProvider->RefreshRow(m_pCurrentDataRowOutOfWindow))
                    SetCurrentCell(0, m_nCurrentColumn);
            }
            else
                OnChangeCurrentData(m_rows[m_nCurrentDataRowIndex]->m_pDataRow);
        }
        else
        {
            BOOL bTopIsCurrent = m_nCurrentDataRowIndex == 0;
            GetRowsDown(m_nVisibleRows);
            delete m_rows[0];
            m_rows.RemoveAt(0);
            if (m_nCurrentDataRowIndex != -1)
                m_nCurrentDataRowIndex--;

            if (m_rows.GetSize())
            {
                if (bTopIsCurrent)
                    SetCurrentCell(0, m_nCurrentColumn);
                else
                {
                    if (m_nCurrentDataRowIndex == -1)
                    {
                        if (!m_pProvider->RefreshRow(m_pCurrentDataRowOutOfWindow))
                            SetCurrentCell(0, m_nCurrentColumn);
						else
							OnChangeCurrentData(m_pCurrentDataRowOutOfWindow);
                    }
                    else
                        OnChangeCurrentData(m_rows[m_nCurrentDataRowIndex]->m_pDataRow);
                }
            }
            else
                bReset = TRUE;
        }

        if (m_bIsBOF && !bReset)
			GetRowsUp(1);

    }
    else
        bReset = TRUE;

    if (bReset)
        Reset();
    else
    {
        FillRows(m_nTopRowIndex, m_nVisibleRows);
        SetVScrollInfo();
        Invalidate();
    }
}

void CCursorGridCtrl::ActivateRow(CDataRow* pActRow)
{
    for (int i = m_nTopRowIndex; i < m_nTopRowIndex + m_nVisibleRows && i < m_rows.GetSize(); i++)
    {
        if (m_pProvider->CompareRows(m_rows[i]->m_pDataRow, pActRow))
        {
            SetCurrentCell(i, m_nCurrentColumn);
/*
            if (m_bLastRowNotFull && i == m_nTopRowIndex + m_nVisibleRows - 1)
                ScrollDown(1);
*/
            return;
        }
    }
    Reset(pActRow);
}

void CCursorGridCtrl::Reset(CDataRow* pActRow /*= NULL*/)
{
    ClearRows();
    CDataRow* pCurrentRowToDel = m_pCurrentDataRowOutOfWindow;
    m_nCurrentDataRowIndex = -1;
    m_pCurrentDataRowOutOfWindow = NULL;

    if (pActRow)
    {
        int nGetRows = m_nVisibleRows;
        m_pProvider->QueryRows(pActRow, nGetRows, m_nVisibleRows);
        CDataRow* pDataRow;
        while (pDataRow = m_pProvider->Fetch())
        {
            m_rows.Add(NewGridRow(m_pColumns, pDataRow));
            nGetRows--;
        }
        if (nGetRows > 0)
            m_bIsEOF = TRUE;
        else
            m_bIsEOF = FALSE;
    
        int nGoten = min(m_nVisibleRows / 2, m_rows.GetSize());
        m_nTopRowIndex = -1;
        GetRowsUp(m_nVisibleRows - nGoten);

        int nCurrentRow;
        if (m_nTopRowIndex != -1)
        {
            nCurrentRow = m_nTopRowIndex;
            m_nTopRowIndex = nCurrentRow - m_nVisibleRows / 2;
            if (m_nTopRowIndex + m_nVisibleRows > m_rows.GetSize())
                m_nTopRowIndex = m_rows.GetSize() - GetPageSize();
            if (m_nTopRowIndex < 0)
                m_nTopRowIndex = 0;
        }
        else
        {
            nCurrentRow = 0;
            if (m_rows.GetSize())
                m_nTopRowIndex = 0;
        }
        FillRows(m_nTopRowIndex, m_nVisibleRows);
        SetVScrollInfo();
        SetCurrentCell(nCurrentRow, m_nCurrentColumn);
    }
    else
    {
        m_nTopRowIndex = 0;
        int nCurrentRowIndex;
        if (m_StartView == svFromBegin)
        {
            m_bIsBOF = TRUE;
            m_bIsEOF = FALSE;
            GetRowsDown(m_nVisibleRows);
            nCurrentRowIndex = 0;
        }
        else //svFromEnd
        {
            m_bIsBOF = FALSE;
            m_bIsEOF = TRUE;
            GetRowsUp(m_nVisibleRows);
            m_nTopRowIndex = m_nTopRowIndex - GetPageSize();
            if (m_nTopRowIndex < 0)
                m_nTopRowIndex = 0;
            nCurrentRowIndex = m_rows.GetUpperBound();
        }

        FillRows(m_nTopRowIndex, m_nVisibleRows);

        m_nCurrentColumn = -1;
        ColumnsChanged();
        SetCurrentCell(nCurrentRowIndex, m_nCurrentColumn);
        SetVScrollInfo();
        Invalidate();
    }

    delete pCurrentRowToDel;
}

LRESULT CCursorGridCtrl::OnDataChange(WPARAM wparam, LPARAM lparam)
{
    switch (wparam)
    {
    case DP_RESET:
        Reset((CDataRow*)lparam);
        break;
    case DP_FIELDS:
        Update();
        break;
    case DP_CURROW:
        ActivateRow((CDataRow*)lparam);
        break;
    }
    return 0;
}

void CCursorGridCtrl::SetVScrollInfo()
{
    if (m_VScrollerPresent == spNever)
    {
        ShowScrollBar(SB_VERT, FALSE);
        return;
    }

    if (!m_pProvider)
        return;

    BOOL bIsBegin = m_bIsBOF && m_nTopRowIndex == 0;
    BOOL bIsEnd = m_bIsEOF && m_nTopRowIndex + GetPageSize() >= m_rows.GetSize();

    if (m_VScrollerPresent == spAuto && bIsBegin && bIsEnd)
        ShowScrollBar(SB_VERT, FALSE);
    else
    {
        SCROLLINFO si;
        si.cbSize = sizeof(SCROLLINFO);
        si.nMin = 0;

        UINT nRowCount;
        if (m_pProvider->GetRowCount(&nRowCount))
        {
            if (!m_rows.GetSize())
                return;
            si.fMask = SIF_POS | SIF_RANGE | SIF_PAGE;
            si.nMax = nRowCount - 1;
            si.nPos = m_rows[m_nTopRowIndex]->m_pDataRow->GetRowIndex();
            si.nPage = GetPageSize();
        }
        else
        {
            si.fMask = SIF_POS | SIF_RANGE;
            si.nMax = 100;
            if (bIsBegin)
                si.nPos = 0;
            else if(bIsEnd)
                si.nPos = 100;
            else
                si.nPos = 50;
        }

        BOOL bSuccess = SetScrollInfo(SB_VERT, &si);
        ASSERT(bSuccess);
        ShowScrollBar(SB_VERT, TRUE);
    }
}

ScrollerPresent CCursorGridCtrl::GetVScroller() const
{
    return m_VScrollerPresent;
}

void CCursorGridCtrl::SetVScroller(ScrollerPresent scrollerPresent)
{
    if (m_VScrollerPresent == scrollerPresent)
        return;
    m_VScrollerPresent = scrollerPresent;
    SetVScrollInfo();
}

void CCursorGridCtrl::GetRowsDown(int nCount)
{
    while (m_nTopRowIndex > m_nVisibleRows - 1)
    {
        delete m_rows[0];
        m_rows.RemoveAt(0);
        if (m_nCurrentDataRowIndex != -1)
            m_nCurrentDataRowIndex--;
        m_nTopRowIndex--;
        m_bIsBOF = FALSE;
        m_bEvenRow = !m_bEvenRow;
    }

    for (int i = m_nTopRowIndex - 1; i >= 0; i--)
    {
        if (!m_rows[i]->CleanUp())
            break;
    }

    CDataRow* pRowFrom;
    if (m_rows.GetSize())
        pRowFrom = m_rows[m_rows.GetUpperBound()]->m_pDataRow;
    else
        pRowFrom = NULL;

    m_pProvider->QueryRows(pRowFrom, nCount, m_nVisibleRows);

    CDataRow* pDataRow;
    while (pDataRow = m_pProvider->Fetch())
    {
        m_rows.Add(NewGridRow(pDataRow, m_rows.GetSize()));
        nCount--;
    }
    if (nCount > 0)
        m_bIsEOF = TRUE;
    else
        m_bIsEOF = FALSE;
}

void CCursorGridCtrl::GetRowsUp(int nCount)
{
    while (m_rows.GetSize() - (m_nTopRowIndex + m_nVisibleRows) > m_nVisibleRows - 1)
    {
        delete m_rows[m_rows.GetUpperBound()];
        m_rows.RemoveAt(m_rows.GetUpperBound());
        m_bIsEOF = FALSE;
    }

    for (int i = m_nTopRowIndex + m_nVisibleRows; i < m_rows.GetSize(); i++)
    {
        if (!m_rows[i]->CleanUp())
            break;
    }

    CDataRow* pRowFrom;
    if (m_rows.GetSize())
        pRowFrom = m_rows[0]->m_pDataRow;
    else
        pRowFrom = NULL;

    m_pProvider->QueryRows(pRowFrom, -nCount, m_nVisibleRows);

    CDataRow* pDataRow;
    while (pDataRow = m_pProvider->Fetch())
    {
        if (m_nCurrentDataRowIndex != -1)
            m_nCurrentDataRowIndex++;
        m_rows.InsertAt(0, NewGridRow(pDataRow, 0));
        nCount--;
        m_nTopRowIndex++;
        m_bEvenRow = !m_bEvenRow;
    }
    if (nCount > 0)
        m_bIsBOF = TRUE;
    else
        m_bIsBOF = FALSE;
}

CGridRow* CCursorGridCtrl::NewGridRow(CDataRow* pDataRow, int nNewRowIndex)
{
    CGridRow* pGridRow = NewGridRow(m_pColumns, pDataRow);
    if (m_nCurrentDataRowIndex == -1 && m_pCurrentDataRowOutOfWindow)
    {
        if (m_pProvider->CompareRows(pDataRow, m_pCurrentDataRowOutOfWindow))
        {
            delete m_pCurrentDataRowOutOfWindow;
            m_nCurrentDataRowIndex = nNewRowIndex;
            pGridRow->m_bIsCurrent = true;
            OnChangeCurrentData(pDataRow);
        }
    }
    return pGridRow;
}

void CCursorGridCtrl::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
	if(GetFocus() != this) 
		SetFocus();
    m_Tip.Hide();

    UINT nRowCount;

	switch (nSBCode)
    {
    case SB_LINEDOWN:
        ScrollDown(1);
        break;
    case SB_LINEUP:
        ScrollUp(1);
        break;
    case SB_PAGEDOWN:
        ScrollDown(GetPageSize());
        break;
    case SB_PAGEUP:
        ScrollUp(GetPageSize());
        break;
    case SB_THUMBPOSITION:
        if (m_pProvider && !m_pProvider->GetRowCount(&nRowCount))
        {
            if (nPos == 0)
                ToHome();
            else if (nPos == 100)
                ToEnd();
        }
        break;
    case SB_THUMBTRACK:
        if (m_pProvider && m_pProvider->GetRowCount(&nRowCount))
        {
            if (!m_rows.GetSize())
                break;

            SCROLLINFO si;
            si.cbSize = sizeof(SCROLLINFO);
            GetScrollInfo(SB_VERT, &si, SIF_TRACKPOS);
            UINT nIndex = m_rows[m_nTopRowIndex]->m_pDataRow->GetRowIndex();
            int dy = si.nTrackPos - nIndex;
            if (dy > 0)
                ScrollDown(dy);
            else
                ScrollUp(-dy);
        }
        break;
    }
	
	CWnd::OnVScroll(nSBCode, nPos, pScrollBar);
}

BOOL CCursorGridCtrl::ScrollDown(int nPageSize)
{
    int nGetRows = m_nTopRowIndex + m_nVisibleRows + nPageSize - m_rows.GetUpperBound();
    if (nGetRows > 0 && !m_bIsEOF)
        GetRowsDown(nGetRows);
    if (m_nTopRowIndex + m_nVisibleRows <= m_rows.GetUpperBound())
    {
        FillRows(m_nTopRowIndex + m_nVisibleRows, nPageSize);

        int nScroll = m_rows.GetSize() - (m_nTopRowIndex + GetPageSize());
        if (nScroll > nPageSize)
            nScroll = nPageSize;
        m_nTopRowIndex += nScroll;

        InvalidateRect(GetGridRect());
        SetVScrollInfo();
		return TRUE;
    }
    else if (m_bIsEOF && m_nTopRowIndex + GetPageSize() <= m_rows.GetUpperBound() && m_nTopRowIndex < m_rows.GetUpperBound())
    {
        m_nTopRowIndex++;
        InvalidateRect(GetGridRect());
        SetVScrollInfo();
		return TRUE;
    }
	return FALSE;
}

BOOL CCursorGridCtrl::ScrollUp(int nPageSize)
{
    int nGetRows = nPageSize - m_nTopRowIndex;
    if (nGetRows > 0 && !m_bIsBOF)
    {
        GetRowsUp(nGetRows);
        if (m_nTopRowIndex == 0)
        {
            SetVScrollInfo();
            return FALSE;
        }
    }
    if (m_nTopRowIndex > 0)
    {
        int nFirst = m_nTopRowIndex - nPageSize;
        if (nFirst < 0)
            nFirst = 0;

        FillRows(nFirst, m_nTopRowIndex - nFirst);
        m_nTopRowIndex = nFirst;

        InvalidateRect(GetGridRect());
        SetVScrollInfo();
		return TRUE;
    }
	return FALSE;
}

BOOL CCursorGridCtrl::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) 
{
    m_Tip.Hide();
    
    UINT nScroll;
	SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &nScroll, 0);

    if (zDelta < 0)
        ScrollDown(nScroll);
    else
        ScrollUp(nScroll);

	return CWnd::OnMouseWheel(nFlags, zDelta, pt);
}

void CCursorGridCtrl::SetCurrentCell(int nRowIndex, int nColumnIndex)
{
    if (m_nCurrentDataRowIndex == nRowIndex && m_nCurrentColumn == nColumnIndex)
        return;

	BOOL bRowActivated = FALSE;
	BOOL bColumnActivated = FALSE;

    if (m_nCurrentDataRowIndex != nRowIndex)
    {
        if (m_nCurrentDataRowIndex == -1)
        {
            delete m_pCurrentDataRowOutOfWindow;
            m_pCurrentDataRowOutOfWindow = NULL;
        }
        else
            m_rows[m_nCurrentDataRowIndex]->m_bIsCurrent = false;

        if (nRowIndex >= 0 && nRowIndex < m_rows.GetSize())
        {
            m_nCurrentDataRowIndex = nRowIndex;
            m_rows[nRowIndex]->m_bIsCurrent = true;
            OnChangeCurrentData(m_rows[nRowIndex]->m_pDataRow);
			bRowActivated = TRUE;
        }
        else
        {
            m_nCurrentDataRowIndex = -1;
            OnChangeCurrentData(NULL);
        }
    }

    if (nColumnIndex != m_nCurrentColumn)
    {
        m_nCurrentColumn = nColumnIndex;
        DisplayColumn(m_pColumns->GetAt(nColumnIndex));
		bColumnActivated = TRUE;
    }

    if (m_nCurrentDataRowIndex == m_nTopRowIndex + m_nVisibleRows - 1 && m_bLastRowNotFull)
        ScrollDown(1);

	if (bRowActivated)
		OnActivateRow();

	if (bColumnActivated)
		OnActivateColumn();

    OnActivateCell();

    Invalidate();
}

BOOL CCursorGridCtrl::DisplayColumn(CGridColumn* pColumn)
{
    BOOL bShifted = FALSE;
    CRect rect = pColumn->GetBoundsRect();
    SCROLLINFO si;
    si.cbSize = sizeof(SCROLLINFO);
    GetScrollInfo(SB_HORZ, &si);
    if (rect.left < 0)
    {
        si.nPos += rect.left;
        SetScrollInfo(SB_HORZ, &si);
        Invalidate();
        bShifted = TRUE;
    }
    else
    {
        int pos = rect.right - GetGridRect().right;
        if (pos > 0)
        {
            si.nPos += pos;
            SetScrollInfo(SB_HORZ, &si);
            Invalidate();
            bShifted = TRUE;
        }
    }
    return bShifted;
}

int CCursorGridCtrl::GetNextColumn(int nIndex, int nDirection)
{
    for (int i = nIndex + nDirection; i >= 0 && i < m_pColumns->GetSize(); i += nDirection)
    {
        if (m_pColumns->GetAt(i)->m_bVisible)
            return i;
    }
    return -1;
 }

void CCursorGridCtrl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
    if (!(nFlags & (1 << 14)))
        m_Tip.Hide();
    
	switch (nChar)
    {
    case VK_LEFT:
		ScrollCurrentCell(-1, 0);
        break;
    case VK_RIGHT:
		ScrollCurrentCell(1, 0);
        break;
    case VK_UP:
		ScrollCurrentCell(0, -1);
        break;
    case VK_DOWN:
		ScrollCurrentCell(0, 1);
        break;
    case VK_PRIOR:
        {
            if (m_nCurrentDataRowIndex == -1 && m_pCurrentDataRowOutOfWindow)
                Reset(m_pCurrentDataRowOutOfWindow);

            if (m_nCurrentDataRowIndex != -1)
            {
                int nScroll = m_nTopRowIndex + GetPageSize() - 1 - m_nCurrentDataRowIndex;
                if (nScroll > 0)
                    ScrollUp(nScroll);
                SetCurrentCell(m_nTopRowIndex, m_nCurrentColumn);
                RedrawWindow(GetGridRect());
            }
        }
        break;
    case VK_NEXT:
        {
            if (m_nCurrentDataRowIndex == -1 && m_pCurrentDataRowOutOfWindow)
                Reset(m_pCurrentDataRowOutOfWindow);

            if (m_nCurrentDataRowIndex != -1)
            {
                int nScroll = m_nCurrentDataRowIndex - m_nTopRowIndex;
                if (nScroll > 0)
                    ScrollDown(nScroll);
                int nIndex = m_nTopRowIndex + GetPageSize() - 1;
                if (nIndex > m_rows.GetUpperBound())
                    nIndex = m_rows.GetUpperBound();
                SetCurrentCell(nIndex, m_nCurrentColumn);
                RedrawWindow(GetGridRect());
            }
        }
        break;
    case VK_HOME:
        {
            if (m_bIsBOF)
            {
                int nScroll = m_nTopRowIndex;
                if (nScroll > 0)
                    ScrollUp(nScroll);
                if (m_rows.GetSize())
                    SetCurrentCell(m_nTopRowIndex, m_nCurrentColumn);
            }
            else
                ToHome();
        }
        break;
    case VK_END:
        {
            if (m_bIsEOF)
            {
                int nScroll = m_rows.GetUpperBound() - (m_nTopRowIndex + GetPageSize() - 2);
                if (nScroll > 0)
                    ScrollDown(nScroll);
                if (m_rows.GetSize())
                    SetCurrentCell(m_rows.GetUpperBound(), m_nCurrentColumn);
            }
            else
                ToEnd();
        }
        break;
    case VK_F5:
        Update();
        break;
    case VK_CONTROL:
        //m_ColumnHelper.Show();
        break;
    }
	
	CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
}

void CCursorGridCtrl::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    if (nChar == VK_CONTROL)
        //m_ColumnHelper.Hide()
    {};
}

UINT CCursorGridCtrl::OnGetDlgCode()
{
	return DLGC_WANTARROWS | DLGC_WANTTAB | DLGC_WANTCHARS;
}

int CCursorGridCtrl::GetPageSize()
{
    int nPageSize = m_nVisibleRows;
    if (m_bLastRowNotFull)
        nPageSize--;
    return nPageSize;
}

void CCursorGridCtrl::OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
    m_Tip.Hide();
	CWnd::OnSysKeyDown(nChar, nRepCnt, nFlags);
}

void CCursorGridCtrl::OnRButtonDown(UINT nFlags, CPoint point) 
{
	if(GetFocus() != this) 
		SetFocus();
    m_Tip.Hide();
	m_hitMouseButton = point;
	
    int nColumn, nRow;

    switch(HitTest(point, nColumn, nRow))
    {
    case ghtCell:
        SetCurrent(nRow, nColumn);
        if (m_bEnableDrag)
        {
            m_CaptureType = ctDragDelay;
            SetCapture();
        }
        break;
    case ghtCheckBox:
        SetCurrent(nRow, nColumn);
        break;
    }
    CWnd::OnRButtonDown(nFlags, point);
}

void CCursorGridCtrl::ToHome()
{
    ClearRows();

    m_nTopRowIndex = 0;
    m_bIsBOF = TRUE;
    GetRowsDown(m_nVisibleRows);
    FillRows(0, m_nVisibleRows);
    SetCurrentCell(0, m_nCurrentColumn);
    SetVScrollInfo();
}

void CCursorGridCtrl::ToEnd()
{
    ClearRows();
    m_bIsEOF = TRUE;
    GetRowsUp(GetPageSize());
    m_nTopRowIndex = m_rows.GetSize() - GetPageSize();
    if (m_nTopRowIndex < 0)
        m_nTopRowIndex = 0;
    FillRows(m_nTopRowIndex, m_nVisibleRows);
    SetCurrentCell(m_rows.GetUpperBound(), m_nCurrentColumn);
    SetVScrollInfo();
}

BOOL CCursorGridCtrl::IsFocused()
{
    return ::GetFocus() == GetSafeHwnd();
}

void CCursorGridCtrl::OnSetFocus(CWnd* pOldWnd) 
{
	CWnd::OnSetFocus(pOldWnd);
	InvalidateRect(GetGridRect());
}

void CCursorGridCtrl::OnKillFocus(CWnd* pNewWnd) 
{
	CWnd::OnKillFocus(pNewWnd);
    m_Tip.Hide();
	InvalidateRect(GetGridRect());
}

void CCursorGridCtrl::ColumnsChanged()
{
    RecalcColumsWidth();
    
    if (m_nCurrentColumn == -1)
        m_nCurrentColumn = GetNextColumn(m_nCurrentColumn, 1);
    else
    {
        if (!m_pColumns->GetAt(m_nCurrentColumn)->m_bVisible)
            m_nCurrentColumn = GetNextColumn(m_nCurrentColumn, 1);
    }
}

StartView CCursorGridCtrl::GetStartView() const
{
    return m_StartView;
}

void CCursorGridCtrl::SetStartView(StartView startView)
{
    m_StartView = startView;
}

RowSelectMode CCursorGridCtrl::GetRowSelectMode() const
{
    return m_RowSelectMode;
}

void CCursorGridCtrl::SetRowSelectMode(RowSelectMode rowSelectMode)
{
    if (m_RowSelectMode == rowSelectMode)
        return;
    m_RowSelectMode = rowSelectMode;
    InvalidateRect(GetGridRect());
}

BOOL CCursorGridCtrl::GetVerticalLines() const
{
    return m_bVerticalLines;
}

void CCursorGridCtrl::SetVerticalLines(BOOL bVerticalLines /*= TRUE*/)
{
    if (bVerticalLines == m_bVerticalLines)
        return;
    m_bVerticalLines = bVerticalLines;
    InvalidateRect(GetGridRect());
}

BOOL CCursorGridCtrl::GetHorizontalLines() const
{
    return m_bHorizontalLines;
}

void CCursorGridCtrl::SetHorizontalLines(BOOL bHorizontalLines /*= TRUE*/)
{
    if (bHorizontalLines == m_bHorizontalLines)
        return;
    m_bHorizontalLines = bHorizontalLines;
    InvalidateRect(GetGridRect());
}

HeadersStyle CCursorGridCtrl::GetHeadersStyle() const
{
    return m_HeadersStyle;
}

void CCursorGridCtrl::SetHeadersStyle(HeadersStyle headersStyle)
{
    if (headersStyle == m_HeadersStyle)
        return;

    m_HeadersStyle = headersStyle;
    InvalidateRect(GetHeaderRect());
}

CGridColumn* CCursorGridCtrl::GetCurrentColumn() const
{
    if (m_nCurrentColumn == -1)
        return NULL;
    else
        return m_pColumns->GetAt(m_nCurrentColumn);
}

void CCursorGridCtrl::SetCurrentColumn(CGridColumn* pColumn)
{
    int nIndex = m_pColumns->IndexOf(pColumn);
    if (nIndex < 0 || nIndex == m_nCurrentColumn)
        return;
    SetCurrentCell(m_nCurrentDataRowIndex, nIndex);
}

void CCursorGridCtrl::OnLButtonDblClk(UINT nFlags, CPoint point) 
{
    int nColumn, nRow;

    switch(HitTest(point, nColumn, nRow))
    {
    case ghtCell:
        OnSelection();
        break;
    case ghtCheckBox:
        OnClickCheckBox();
        break;
    }
    CWnd::OnLButtonDblClk(nFlags, point);
}

void CCursorGridCtrl::OnContextMenu(CWnd* pWnd, CPoint point) 
{
    if (point.x == -1 && point.y == -1)
    {
        if (m_nCurrentDataRowIndex == -1 || m_nCurrentColumn == -1)
        {
            point.x = 0;
            point.y = 0;
        }
        else
        {
            CRect rect = m_pColumns->GetAt(m_nCurrentColumn)->GetBoundsRect();
            rect.OffsetRect(0, GetHeaderRect().Height() + (m_nCurrentDataRowIndex - m_nTopRowIndex) * m_nRowHeight);
            point.x = rect.left;
            point.y = rect.bottom;
            if (!GetGridRect().PtInRect(point))
            {
                point.x = 0;
                point.y = 0;
            }
        }
        ClientToScreen(&point);
    }
    
    m_ptContextMenuPosition = point;

	ScreenToClient(&point);
    OnShowContextMenu(GetHeaderRect().PtInRect(point));
}

LRESULT CCursorGridCtrl::OnGetFont(WPARAM wparam, LPARAM lparam)
{
    return (LRESULT)m_font.operator HFONT();
}

LRESULT CCursorGridCtrl::OnSetFont(WPARAM wparam, LPARAM lparam)
{
    HFONT hFont = (HFONT)wparam;
    BOOL fRedraw = lparam;

    if (hFont)
        InitFont(CFont::FromHandle(hFont));
    else
        InitFont(GetParent()->GetFont());

    if (fRedraw)
        Invalidate();
    
    return 0;
}

void CCursorGridCtrl::FillRows(int nFirst, int nCount)
{
    if (nFirst < 0)
        return;
    int nEnd = nFirst + nCount;
    if (nEnd > m_rows.GetSize())
        nEnd = m_rows.GetSize();
    if (nEnd <= 0)
        return;

    m_bLockDrawing = TRUE;
    for (int i = nFirst; i < nEnd; i++)
        m_rows[i]->FillItems();
    m_bLockDrawing = FALSE;
    
}

void CCursorGridCtrl::RefreshRows()
{
    Update();
}

void CCursorGridCtrl::RedrawRows()
{
    FillRows(m_nTopRowIndex, m_nVisibleRows);
    SetVScrollInfo();
    Invalidate();
}

void CCursorGridCtrl::SetTextMargins(int nLeft, int nTop, int nRight, int nBottom)
{
	if (nLeft >= 0)
		m_nLeftTextMargin = nLeft;

	if (nTop >= 0)
		m_nTopTextMargin = nTop;

	if (nRight >= 0)
		m_nRightTextMargin = nRight;

	if (nBottom >= 0)
		m_nBottomTextMargin = nBottom;

    m_nRowHeight = m_nFontHeight + GetTopTextMargin() + GetBottomTextMargin();
    RecalcColumsWidth();
    CalcRowsParams();

    SetVScrollInfo();
    Invalidate();
}

USHORT CCursorGridCtrl::GetRefreshTimeout() const
{
    return m_nRefreshTimeout;
}

void CCursorGridCtrl::SetRefreshTimeout(USHORT nTimeout)
{
    if (m_nRefreshTimeout == nTimeout)
        return;
    m_nRefreshTimeout = nTimeout;
    UINT nRetVal;
    if (m_nRefreshTimeout)
        nRetVal = SetTimer(TIMER_ID_UPDATE, m_nRefreshTimeout * 1000, NULL);
    else
        nRetVal = KillTimer(TIMER_ID_UPDATE);
}

void CCursorGridCtrl::OnTimer(UINT nIDEvent)
{
    switch (nIDEvent)
    {
    case TIMER_ID_UPDATE:
        Update();
        break;
    case TIMER_ID_AUTOHIDE:
        {
            CPoint point;
            ::GetCursorPos(&point);
            ScreenToClient(&point);
            CRect rect;
            GetClientRect(rect);
            if (m_ColumnHelper.IsDisplay())
            {
                CRect rectHelper;
                m_ColumnHelper.GetWindowRect(rectHelper);
                ScreenToClient(rectHelper);
                rect.left = rectHelper.left;
                rect.bottom = rectHelper.bottom;
                if (!rect.PtInRect(point) && m_CaptureType != ctMoveColumn)
                    m_ColumnHelper.Hide();
            }
            else
            {
                rect.left = rect.right - 16;
                rect.bottom = rect.top + m_nRowHeight;
                if (rect.PtInRect(point))
                {
                    m_Tip.Hide();
                    m_ColumnHelper.Show();
                }
            }
        }
        break;
	case TIMER_ID_SCROLL:
		{
            CPoint point;
            ::GetCursorPos(&point);
            ScreenToClient(&point);

			CRect rect = GetGridRect();
			if (!rect.PtInRect(point))
			{
				int xDirection = point.x < rect.TopLeft().x ? -1 : point.x > rect.BottomRight().x ? 1 : 0;
				int yDirection = point.y < rect.TopLeft().y ? -1 : point.y > rect.BottomRight().y ? 1 : 0;
				ScrollCurrentCell(xDirection, yDirection);
			}
		}
		break;
    }
    
}

void CCursorGridCtrl::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    if (!m_pProvider)
        return;
    if (m_nCurrentColumn == -1)
        return;

    CGridColumn* pColumn = m_pColumns->GetAt(m_nCurrentColumn);

    DataType dataType = m_pProvider->GetQuickSearchType(pColumn->m_strDataField);
    switch (dataType)
    {
    case dtText:
        break;
    default:
        return;
    }

	if (OnQuickSearchStart(dataType))
	{
		CRect rect(pColumn->GetBoundsRect());
		CRect rectClient;
		GetClientRect(rectClient);
		int nHeight = rect.Height();
		rect.top = rectClient.Height();
		rect.bottom = rect.top + nHeight;
		char arText[] = {nChar, 0};
		m_QuickSearch.Show(rect, pColumn->m_strDataField, arText);
	}
}

USHORT CCursorGridCtrl::GetQuickSearchTimeout() const
{
    return m_QuickSearch.m_nTimeout;
}

void CCursorGridCtrl::SetQuickSearchTimeout(USHORT nTimeout)
{
    m_QuickSearch.m_nTimeout = nTimeout;
}

BOOL CCursorGridCtrl::GetChangePositionOfColumns() const
{
    return m_bChangePositionOfColumns;
}

void CCursorGridCtrl::SetChangePositionOfColumns(BOOL bChangePositionOfColumns /*= TRUE*/)
{
    m_bChangePositionOfColumns = bChangePositionOfColumns;
}

COLORREF CCursorGridCtrl::GetLinesColor()
{
    if (m_crGridLines == CLR_DEFAULT)
        return ::GetSysColor(COLOR_BTNSHADOW);
    else
        return m_crGridLines;
}

COLORREF CCursorGridCtrl::GetGridLinesColor() const
{
    return m_crGridLines;
}

void CCursorGridCtrl::SetGridLinesColor(COLORREF crGridLines)
{
    if (m_crGridLines == crGridLines)
        return;
    m_crGridLines = crGridLines;
    Invalidate();
}

COLORREF CCursorGridCtrl::GetTextColor() const
{
    return m_crText;
}

void CCursorGridCtrl::SetTextColor(COLORREF crTextColor)
{
    if (m_crText == crTextColor)
        return;
    m_crText = crTextColor;
    Invalidate();
}

COLORREF CCursorGridCtrl::GetBackColor() const
{
    return m_crBack;
}

void CCursorGridCtrl::SetBackColor(COLORREF crBackColor)
{
    if (m_crBack == crBackColor)
        return;
    m_crBack = crBackColor;
    Invalidate();
}

COLORREF CCursorGridCtrl::GetSelectionTextColor() const
{
    return m_crSelectionText;
}

void CCursorGridCtrl::SetSelectionTextColor(COLORREF crTextColor)
{
    if (m_crSelectionText == crTextColor)
        return;
    m_crSelectionText = crTextColor;
    Invalidate();
}

COLORREF CCursorGridCtrl::GetSelectionBackColor() const
{
    return m_crSelectionBack;
}

void CCursorGridCtrl::SetSelectionBackColor(COLORREF crBackColor)
{
    if (m_crSelectionBack == crBackColor)
        return;
    m_crSelectionBack = crBackColor;
    Invalidate();
}

COLORREF CCursorGridCtrl::GetAlternationRowBackColor() const
{
    return m_crAlternationRowBack;
}

void CCursorGridCtrl::SetAlternationRowBackColor(COLORREF crBackColor)
{
    if (m_crAlternationRowBack == crBackColor)
        return;
    m_crAlternationRowBack = crBackColor;
	InvalidateRect(GetGridRect());
}

BOOL CCursorGridCtrl::GetUseAlternationRowColor() const
{
    return m_bUseAlternationRowColor;
}

void CCursorGridCtrl::SetUseAlternationRowColor(BOOL bUse /*= TRUE*/)
{
    if (m_bUseAlternationRowColor == bUse)
        return;
    m_bUseAlternationRowColor = bUse;
    Invalidate();
}

void CCursorGridCtrl::OnBeginDrag()
{
    if (m_nCurrentColumn != -1 && m_nCurrentDataRowIndex != -1)
    {
        LPCSTR szText = m_rows[m_nCurrentDataRowIndex]->GetGridItem(m_nCurrentColumn)->GetText();
        HGLOBAL hGlobal = ::GlobalAlloc(GMEM_MOVEABLE, strlen(szText) + 1);
        memcpy(::GlobalLock(hGlobal), szText, strlen(szText) + 1);
        ::GlobalUnlock(hGlobal);

        COleDataSource DS;
        DS.CacheGlobalData(CF_TEXT, hGlobal);
        DS.DoDragDrop(DROPEFFECT_COPY);
        
        ::GlobalFree(hGlobal);
    }
};

CPoint CCursorGridCtrl::GetContextMenuPosition() const
{
	CPoint point;
    if (m_nCurrentDataRowIndex == -1 || m_nCurrentColumn == -1)
    {
        point.x = 0;
        point.y = 0;
    }
    else
    {
        CRect rect = m_pColumns->GetAt(m_nCurrentColumn)->GetBoundsRect();
        rect.OffsetRect(0, GetHeaderRect().Height() + (m_nCurrentDataRowIndex - m_nTopRowIndex) * m_nRowHeight);
        point.x = rect.left;
        point.y = rect.bottom;
        if (!GetGridRect().PtInRect(point))
        {
            point.x = 0;
            point.y = 0;
        }
    }
    ClientToScreen(&point);
	return point;
//    return m_ptContextMenuPosition;
}

BOOL CCursorGridCtrl::GetEnableDrag() const
{
	return m_bEnableDrag;
}

void CCursorGridCtrl::SetEnableDrag(BOOL bEnableDrag /*= TRUE*/)
{
	m_bEnableDrag = bEnableDrag;
}

BOOL CCursorGridCtrl::GetEnableDrop() const
{
	return m_bEnableDrop;
}

void CCursorGridCtrl::SetEnableDrop(BOOL bEnableDrop /*= TRUE*/)
{
    if (m_bEnableDrop == bEnableDrop)
        return;

	m_bEnableDrop = bEnableDrop;

    if (m_bEnableDrop)
        m_DropTarget.Enable();
    else
        m_DropTarget.Disable();
}

DropTargetView CCursorGridCtrl::GetSelfDropTargetView() const
{
    return m_DropTarget.m_SelfDropTargetView;
}

void CCursorGridCtrl::SetSelfDropTargetView(DropTargetView dropTargetView)
{
    m_DropTarget.m_SelfDropTargetView = dropTargetView;
}

BOOL CCursorGridCtrl::GetShowHeader() const
{
	return m_bShowHeader;
}

void CCursorGridCtrl::SetShowHeader(BOOL bShowHeader)
{
	m_bShowHeader = bShowHeader;
	CalcRowsParams();
	Invalidate();
}

BOOL CCursorGridCtrl::GetCurrentCellBounds(CRect &rect) const
{
	BOOL bSuccess = FALSE;
    if (m_nCurrentColumn != -1) 
    { 
           CGridColumn* pColumn = GetCurrentColumn(); 
           rect = pColumn->GetBoundsRect(); 
           if (m_nCurrentDataRowIndex != -1 && m_nCurrentDataRowIndex >= m_nTopRowIndex && m_nCurrentDataRowIndex < m_nTopRowIndex + m_nVisibleRows) 
           { 
                 rect.OffsetRect(0, GetHeaderRect().Height()); 
                 rect.OffsetRect(0, (m_nCurrentDataRowIndex - m_nTopRowIndex) * m_nRowHeight); 
                 bSuccess = rect.IntersectRect(rect, GetGridRect()); 
           } 
     } 
     return bSuccess; 
}

/////////////////////////////////////////////////////////////////////////////
// CCGDropTarget
/////////////////////////////////////////////////////////////////////////////

CCGDropTarget::CCGDropTarget(CCursorGridCtrl* pGrid) : m_pGrid(pGrid)
{
    m_pDropOverColumn = NULL;
    m_pDropOverRow = NULL;
    m_bDragOn = FALSE;
    m_dwDropEffect = DROPEFFECT_NONE;
    m_SelfDropTargetView = dtvNone;
	m_bScrollDelay = FALSE;
}

void CCGDropTarget::Enable()
{
    Register(m_pGrid);
}

void CCGDropTarget::Disable()
{
    Revoke();
}

DROPEFFECT CCGDropTarget::OnDragEnter(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point)
{
	m_bDragOn = TRUE;
	m_dwKeyState = dwKeyState;
	return OnDragOver(pWnd, pDataObject, dwKeyState, point);
}

void CCGDropTarget::OnDragLeave(CWnd* pWnd)
{
    m_bDragOn = FALSE;
    m_pDropOverColumn = NULL;
    m_pDropOverRow = NULL;
	m_bScrollDelay = FALSE;

    CRect rect;
    rect.SetRectEmpty();
    DrawDragRect(rect);
}

DROPEFFECT CCGDropTarget::OnDragOver(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point)
{
    int nColumn, nRow;
    CGridColumn* pColumn = NULL;
    CDataRow* pDataRow = NULL;
    BOOL bDropEnable = FALSE;

	CRect ClientRect;
	m_pGrid->GetClientRect(ClientRect);
	if (!ClientRect.PtInRect(point))
		m_dwDropEffect = DROPEFFECT_NONE;
	else
	{
		switch (m_pGrid->HitTest(point, nColumn, nRow))
		{
		case ghtHeader:
			m_dwDropEffect = DROPEFFECT_NONE;
			break;
		case ghtCell:
		case ghtCheckBox:
		case ghtSpace:
			if (nColumn != -1)
				pColumn = m_pGrid->m_pColumns->GetAt(nColumn);
			if (nRow != -1)
			{
				pDataRow = m_pGrid->m_rows[nRow]->m_pDataRow;
				nRow -= m_pGrid->m_nTopRowIndex;
			}
            bDropEnable = TRUE;
		}
	}
	if (m_pDropOverColumn != pColumn || !CompareRows(m_pDropOverRow, pDataRow))
	{
		m_pDropOverColumn = pColumn;
		m_pDropOverRow = pDataRow;

        DropTargetView dropTargetView;
		if (m_SelfDropTargetView != dtvNone)
			dropTargetView = m_SelfDropTargetView;
		else
			dropTargetView = dtvCell;

		if (bDropEnable)
        {

            switch (m_SelfDropTargetView)
            {
            case dtvCell:
                if (nColumn == m_pGrid->m_nCurrentColumn)
                    if (m_pGrid->m_nCurrentDataRowIndex != -1)
                        if (CompareRows(pDataRow, m_pGrid->m_rows[m_pGrid->m_nCurrentDataRowIndex]->m_pDataRow))
                        {
                            dropTargetView = dtvNone;
                            bDropEnable = FALSE;
                            m_dwDropEffect = DROPEFFECT_NONE;
                        }
                break;
            case dtvRow:
                if (m_pGrid->m_nCurrentDataRowIndex != -1)
                    if (CompareRows(pDataRow, m_pGrid->m_rows[m_pGrid->m_nCurrentDataRowIndex]->m_pDataRow))
                    {
                        dropTargetView = dtvNone;
                        bDropEnable = FALSE;
                        m_dwDropEffect = DROPEFFECT_NONE;
                    }
                break;
            case dtvColumn:
                if (nColumn == m_pGrid->m_nCurrentColumn)
                {
                    dropTargetView = dtvNone;
                    bDropEnable = FALSE;
                    m_dwDropEffect = DROPEFFECT_NONE;
                }
                break;
            }

            if (bDropEnable)
    	        m_dwDropEffect = m_pGrid->OnDragOver(pDataObject, dwKeyState, m_pDropOverRow, m_pDropOverColumn, dropTargetView);
        }

		CRect rect;
		if (m_pDropOverColumn && m_pDropOverRow)
		{
            switch (dropTargetView)
            {
            case dtvCell:
			    rect = m_pDropOverColumn->GetBoundsRect();
			    rect.right--;
			    rect.bottom--;
			    rect.OffsetRect(0, m_pGrid->GetHeaderHeight() + nRow * m_pGrid->m_nRowHeight);
                break;
            case dtvRow:
                rect = ClientRect;
                rect.top = m_pGrid->GetHeaderHeight();
                rect.bottom = rect.top + m_pGrid->m_nRowHeight - 1;
			    rect.OffsetRect(0, nRow * m_pGrid->m_nRowHeight);
                break;
            case dtvColumn:
                rect = m_pDropOverColumn->GetBoundsRect();
                rect.top = m_pGrid->GetHeaderHeight();
                rect.bottom = rect.top + (m_pGrid->m_rows.GetSize() - m_pGrid->m_nTopRowIndex) * m_pGrid->m_nRowHeight;
			    rect.right--;
			    rect.bottom--;
                break;
            default:
                rect.SetRectEmpty();
            }
		}
		else
			rect.SetRectEmpty();
		DrawDragRect(rect);
	}
    return m_dwDropEffect;
}

DROPEFFECT CCGDropTarget::OnDropEx(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropDefault, DROPEFFECT dropList, CPoint point)
{
    CRect rect;
    rect.SetRectEmpty();
    DrawDragRect(rect);

	if (m_dwKeyState & MK_RBUTTON)
	{
		CMenu menu;
		menu.CreatePopupMenu();
		if (dropList & DROPEFFECT_MOVE)
			menu.AppendMenu(MF_STRING, DROPEFFECT_MOVE, "Ïåðåíåñòè");
		if (dropList & DROPEFFECT_COPY)
			menu.AppendMenu(MF_STRING, DROPEFFECT_COPY, "Ñêîïèðîâàòü");
		if (dropList & DROPEFFECT_LINK)
			menu.AppendMenu(MF_STRING, DROPEFFECT_LINK, "Ñâÿçàòü");

		if (menu.GetMenuItemCount() == 1)
			dropDefault = menu.GetMenuItemID(0);
		else if (menu.GetMenuItemCount() == 0)
			dropDefault = DROPEFFECT_NONE;
		else
		{
			menu.AppendMenu(MF_SEPARATOR, -1, "");
			menu.AppendMenu(MF_STRING, DROPEFFECT_NONE, "Îòìåíà");

			menu.SetDefaultItem(dropDefault);
			m_pGrid->ClientToScreen(&point);

			dropDefault = menu.TrackPopupMenu(TPM_RIGHTALIGN | TPM_LEFTBUTTON | TPM_RETURNCMD | TPM_NONOTIFY, point.x, point.y, m_pGrid);
		}
	}

    m_pGrid->OnDrop(pDataObject, dropDefault, m_pDropOverRow, m_pDropOverColumn);
    
    m_pDropOverColumn = NULL;
    m_pDropOverRow = NULL;

    return dropDefault;
}

void CCGDropTarget::DrawDragRect(CRect& rect)
{
    m_rectTarget = rect;
    m_pGrid->InvalidateRect(m_pGrid->GetGridRect());
    /*
    CSize size(2,2);
    CDC* pDC = m_pGrid->GetDC();
    pDC->DrawDragRect(rect, size, m_rectLastTarget, size);
    m_pGrid->ReleaseDC(pDC);
    m_rectLastTarget = rect;
    */
}

BOOL CCGDropTarget::CompareRows(CDataRow* pRow1, CDataRow* pRow2)
{
    if (pRow1 && pRow2)
        return m_pGrid->m_pProvider->CompareRows(pRow1, pRow2);
    else
        return (!pRow1 && !pRow2);
}

DROPEFFECT CCGDropTarget::OnDragScroll(CWnd* pWnd, DWORD dwKeyState, CPoint point)
{
	CRect rectWindow;
	m_pGrid->GetWindowRect(rectWindow);
	m_pGrid->ClientToScreen(&point);
	
	int nVScroll = 0;
	int nHScroll = 0;

	if (point.y <= rectWindow.top + DRAG_SCROLL_FIELD_WIDTH)
		nVScroll = 1;
	else if (point.y >= rectWindow.bottom - DRAG_SCROLL_FIELD_WIDTH)
		nVScroll = -1;

	if (point.x <= rectWindow.left + DRAG_SCROLL_FIELD_WIDTH)
		nHScroll = -1;
	else if (point.x >= rectWindow.right - DRAG_SCROLL_FIELD_WIDTH)
		nHScroll = 1;

	if (nVScroll || nHScroll)
	{
		if (m_bScrollDelay)
		{
			if (::GetTickCount() >= m_dwScrollStart)
			{
				BOOL bScrolled = FALSE;

				if (nVScroll > 0)
					bScrolled = m_pGrid->ScrollUp(nVScroll);
				else if (nVScroll < 0)
					bScrolled = m_pGrid->ScrollDown(-nVScroll);

				if (nHScroll)
				{
					SCROLLINFO si;
					m_pGrid->GetScrollInfo(SB_HORZ, &si);
					si.cbSize = sizeof(SCROLLINFO);
					si.fMask = SIF_POS;
					bScrolled |= m_pGrid->HScrollLine(si, nHScroll);
				}

				if (bScrolled)
				{
					m_pDropOverColumn = NULL;
					m_pDropOverRow = NULL;
					CRect rect;
					rect.SetRectEmpty();
					DrawDragRect(rect);
					return DROPEFFECT_SCROLL;
				}
			}
		}
		else
		{
			m_dwScrollStart = ::GetTickCount() + DRAG_SCROLL_DELAY;
			m_bScrollDelay = TRUE;
		}
	}
	else
		m_bScrollDelay = FALSE;

	return DROPEFFECT_NONE;
}
