//----------------------------------------------------------------------------
// ObjectWindow - OWL NExt
// Copyright 1999. Yura Bidus. All Rights reserved.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the
// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.
//
//  OVERVIEW
//  ~~~~~~~~
//  Source file for implementation of TCoolGrid (TControl).
//  (Generated by OWL 6.x Class Expert for MS VC++, Version 1.5)
//----------------------------------------------------------------------------
#include <coolprj/pch.h>
#pragma hdrstop
 
#include <owl/scroller.h>
#include <owl/uihelper.h>
#include <owl/uimetric.h>
#include <owl/celarray.h>
 
#include <algorithm>
 
using namespace owl;
using namespace std;
 
 
#include <coolprj/coolgrid.h>
 
//#include "resource.h"    // Definition of all resources.
 
//#define USE_AUTOORG
 
#ifdef WIN32
const int MSB=0x80000000;
#else
const int MSB=0x8000;
#endif
 
 
//
const int HitDelta = 2;
 
//
static TCoolGrid::TCell*
CreateCoolCell(TCoolGrid&, const TCellPos&)
{
  return 0;
}
//
static TCoolGrid::TColumn*
CreateCoolColumn(TCoolGrid& grid, int /*loc*/)
{
  return new TCoolGrid::TColumn(grid);
}
//
void TCellRange::Normalize()
{
  if(srow > erow){
    std::swap(srow, erow);
    std::swap(scol, ecol);
  }
  if(srow == erow && scol > ecol)
    std::swap(scol, ecol);
}
//
//-------------------------------------------------------------------
// class TCoolGrid::TCellMap
// ~~~~~ ~~~~~~~~~~~~~~~~~~~
//
void TCoolGrid::TCellMap::Flush(bool del)
{
  if(del){
    for(iterator itr = begin(); itr != end(); itr++)
      delete (*itr).second;
  }
  clear();
}
//
//-------------------------------------------------------------------
// class TCoolGrid::TColumnMap
// ~~~~~ ~~~~~~~~~~~~~~~~~~~~~
//
void TCoolGrid::TColumnMap::Flush(bool del)
{
  if(del){
    for(iterator itr = begin(); itr != end(); itr++)
      delete (*itr).second;
  }
  clear();
}
 
//-------------------------------------------------------------------
// class TCoolGrid::TInPlaceEdit
// ~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~
//
TCoolGrid::TInPlaceEdit::TInPlaceEdit()
:
  ExitOnArrows(false),
  LastKey(0)
{
}
//
void TCoolGrid::TInPlaceEdit::EndEdit()
{
  GetGrid()->SendMessage(COOLGRID_ENDEDIT);
}
////////////////////////////////////////////////////////////////////
// class TCoolGrid::TInPlaceEditCtrl
// ~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
DEFINE_RESPONSE_TABLE1(TCoolGrid::TInPlaceEditCtrl, TEdit)
  EV_WM_CHAR,
  EV_WM_KEYDOWN,
  EV_WM_KILLFOCUS,
  EV_WM_GETDLGCODE,
END_RESPONSE_TABLE;
//
TCoolGrid::TInPlaceEditCtrl::TInPlaceEditCtrl(TCoolGrid& parent, uint textLimit)
:
  TEdit(&parent,0,_T(""), 0,0,0,0, textLimit) // default value
{
  ModifyStyle(WS_VISIBLE,0);
}
//
TCoolGrid::TInPlaceEditCtrl::~TInPlaceEditCtrl()
{
  Destroy(IDCANCEL);
}
//
TCoolGrid* TCoolGrid::TInPlaceEditCtrl::GetGrid()
{
  return TYPESAFE_DOWNCAST(GetParentO(), TCoolGrid);
}
//
void TCoolGrid::TInPlaceEditCtrl::EvChar(uint key, uint repeatCount, uint flags)
{
  if(key == VK_TAB || key == VK_RETURN || key == VK_ESCAPE){
    if(key == VK_ESCAPE)
      Undo();
    LastKey = key;
    GetGrid()->SetFocus();
    return;
  }
 
  // call base functions
  TEdit::EvChar(key, repeatCount, flags);
 
  // Resize edit control if needed
  TTmpBuffer<_TCHAR> buff(MAX_PATH);
  GetText(buff, MAX_PATH);
 
  // add some extra buffer
  _tcscat(buff,_T("  "));
 
  TWindowDC dc(*this);
  dc.SelectObject(TFont(GetWindowFont()));
  const auto size = dc.GetTextExtentPoint32(&buff[0]);
  dc.RestoreFont();
 
  // Get client rect
  TRect parentRect;
  GetParentO()->GetClientRect(parentRect);
  TRect clientRect;
  GetClientRect(clientRect);
  MapWindowPoints(GetParentH(), (TPoint*)&clientRect, 2);
 
  // Check whether control needs to be resized
  // and whether there is space to grow
  if (size.cx > clientRect.Width()){
    if( size.cx + clientRect.left < parentRect.right)
      clientRect.right = clientRect.left + size.cx;
    else
      clientRect.right = parentRect.right;
    MoveWindow(clientRect);
  }
}
//
void TCoolGrid::TInPlaceEditCtrl::EvKeyDown(uint key, uint repeatCount, uint flags)
{
  if((key==VK_PRIOR||key==VK_NEXT||key==VK_DOWN||key==VK_UP||key==VK_RIGHT||
      key==VK_LEFT) && (ExitOnArrows||GetKeyState(VK_CONTROL)<0)){
    LastKey = key;
    GetGrid()->SetFocus();
    return;
  }
  TEdit::EvKeyDown(key, repeatCount, flags);
}
//
uint TCoolGrid::TInPlaceEditCtrl::EvGetDlgCode(const MSG* msg)
{
  return TEdit::EvGetDlgCode(msg)|DLGC_WANTALLKEYS;
}
//
void TCoolGrid::TInPlaceEditCtrl::EvKillFocus(HWND hWndGetFocus)
{
  ShowWindow(SW_HIDE);
  TEdit::EvKillFocus(hWndGetFocus);
  EndEdit();
}
//
void TCoolGrid::TInPlaceEditCtrl::Activate(const TRect& rect, uint firstKey)
{
  TCoolGrid* grid = GetGrid();
  PRECONDITION(grid);
  if(grid->GetFont())
    SetWindowFont(*grid->GetFont(), false);
  MoveWindow(rect, false);
 
  ExitOnArrows = (firstKey != VK_LBUTTON);    // If mouse click brought us here,
 
  // Activate the edit box.
  ShowWindow(SW_SHOW);
  SetFocus();
 
  switch(firstKey){
    case VK_LBUTTON:
    case VK_RETURN: {
        TTmpBuffer<_TCHAR> buff(MAX_PATH);
        GetText(buff, MAX_PATH);
        SetSelection((int)_tcslen(buff), -1);
      }
      return;
    case VK_BACK:{
        TTmpBuffer<_TCHAR> buff(MAX_PATH);
        GetText(buff, MAX_PATH);
        SetSelection((int)_tcslen(buff), -1);
      }
      break;
    case VK_TAB: case VK_DOWN: case VK_UP: case VK_RIGHT: case VK_LEFT:
    case VK_NEXT: case VK_PRIOR: case VK_HOME: case VK_SPACE: case VK_END:
      SetSelection(0, -1);
      return;
    default:
      SetSelection(0, -1);
  }
  SendMessage(WM_CHAR, firstKey);
}
//
uint TCoolGrid::TInPlaceEditCtrl::Transfer(void* buffer, TTransferDirection direction)
{
  return TEdit::Transfer(buffer, direction);
}
////////////////////////////////////////////////////////////////////
// class TCoolGrid::TInPlaceListBox
// ~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~
//
DEFINE_RESPONSE_TABLE1(TCoolGrid::TInPlaceListBox, TComboBox)
  EV_WM_KEYDOWN,
  EV_WM_KILLFOCUS,
  EV_WM_GETDLGCODE,
END_RESPONSE_TABLE;
//
TCoolGrid::TInPlaceListBox::TInPlaceListBox(TCoolGrid& parent, uint textLimit)
:
  TComboBox(&parent, -1, 0,0,0,0, CBS_DROPDOWNLIST, textLimit)
{
  ModifyStyle(WS_VISIBLE|CBS_SORT, 0);
}
//
TCoolGrid::TInPlaceListBox::~TInPlaceListBox()
{
  Destroy(IDCANCEL);
}
//
void TCoolGrid::TInPlaceListBox::SetupWindow()
{
  TComboBox::SetupWindow();
  TComboBox::Transfer(&Data, tdSetData);
}
//
TCoolGrid* TCoolGrid::TInPlaceListBox::GetGrid()
{
  return TYPESAFE_DOWNCAST(GetParentO(), TCoolGrid);
}
//
void TCoolGrid::TInPlaceListBox::Activate(const TRect& rect, uint firstKey)
{
  TCoolGrid* grid = GetGrid();
  PRECONDITION(grid);
  if(grid->GetFont())
    SetWindowFont(*grid->GetFont(), false);
  TRect clientRect(rect);
  clientRect.bottom += clientRect.Height()*5;
  MoveWindow(clientRect, false);
 
//  ExitOnArrows = (firstKey != VK_LBUTTON);    // If mouse click brought us here,
  ExitOnArrows = false;
 
  SetDroppedWidth(4);
  SetHorizontalExtent(0); // no horz scrolling
 
  // Activate the edit box.
  ShowWindow(SW_SHOW);
  SetFocus();
  PostMessage(WM_CHAR, firstKey);
}
//
uint TCoolGrid::TInPlaceListBox::Transfer(void* buffer, TTransferDirection direction)
{
  if (!buffer && direction != tdSizeData) return 0;
  if (direction == tdGetData){
    GetWindowText((LPTSTR)buffer, TextLimit);
  }
  else if (direction == tdSetData){
    SetWindowText((LPTSTR)buffer);
    SetSelString((LPTSTR)buffer, 0);
    Data.SelectString((LPTSTR)buffer);
  }
  return TextLimit;
}
//
inline bool checkExceptKeys(uint key)
{
  return (key==VK_TAB||key==VK_RETURN||key==VK_ESCAPE);
}
//
inline bool checkHotListBoxKeys(uint key)
{
  return (key==VK_PRIOR||key==VK_NEXT||key==VK_DOWN||key==VK_UP||
          key==VK_RIGHT||key==VK_LEFT);
}
//
void TCoolGrid::TInPlaceListBox::EvKeyDown(uint key, uint repeatCount, uint flags)
{
  if(checkExceptKeys(key) || (checkHotListBoxKeys(key) &&
     (ExitOnArrows||GetKeyState(VK_CONTROL)<0))){
    if(key == VK_ESCAPE)
      Undo();
    LastKey = key;
    GetGrid()->SetFocus();
    return;
  }
  TComboBox::EvKeyDown(key, repeatCount, flags);
}
//
uint TCoolGrid::TInPlaceListBox::EvGetDlgCode(const MSG* msg)
{
  return TComboBox::EvGetDlgCode(msg)|DLGC_WANTALLKEYS;
}
//
void TCoolGrid::TInPlaceListBox::Undo()
{
  SetWindowText(Data.GetSelection().c_str());
}
//
void TCoolGrid::TInPlaceListBox::EvKillFocus(HWND hWndGetFocus)
{
  ShowWindow(SW_HIDE);
  TComboBox::EvKillFocus(hWndGetFocus);
  EndEdit();
}
//------------------------------------------------------------------------
// class TCoolGrid::TInPlaceComboBox::TLocalEdit
// ~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Local edit
#define IDC_LOCAL_COMBOEDIT 1001
DEFINE_RESPONSE_TABLE1(TCoolGrid::TInPlaceComboBox::TLocalEdit, TEdit)
  EV_WM_KEYDOWN,
  EV_WM_CHAR,
  EV_WM_KILLFOCUS,
  EV_WM_GETDLGCODE,
  EV_WM_LBUTTONDBLCLK,
END_RESPONSE_TABLE;
//
TCoolGrid::TInPlaceComboBox::TLocalEdit::TLocalEdit(TWindow* parent, HWND hWnd)
:
  TEdit(parent,0,0,0,0,0,0,0)
{
  AttachHandle(hWnd);
}
//
TCoolGrid::TInPlaceComboBox*
TCoolGrid::TInPlaceComboBox::TLocalEdit::GetComboBox()
{
  return TYPESAFE_DOWNCAST(GetParentO(), TCoolGrid::TInPlaceComboBox);
}
//
inline bool checkHotLocalEditKeys(uint key)
{
  return (key==VK_PRIOR||key==VK_NEXT||key==VK_TAB||key==VK_RETURN||
          key==VK_ESCAPE||key==VK_DOWN||key==VK_UP||key==VK_RIGHT||
          key==VK_LEFT);
}
//
void TCoolGrid::TInPlaceComboBox::TLocalEdit::EvKeyDown(uint key, uint repeatCount, uint flags)
{
  TCoolGrid::TInPlaceComboBox* cBox = GetComboBox();
  if(checkHotLocalEditKeys(key)){
    if(checkExceptKeys(key) || cBox->ExitOnArrows || GetKeyState(VK_CONTROL) < 0){
      cBox->HandleMessage(WM_KEYDOWN, key, MkUint32(repeatCount,flags));
      return;
    }
    Inherited::EvKeyDown(key, repeatCount, flags);
  }
  else if(!cBox->IsReadOnly())
    Inherited::EvKeyDown(key, repeatCount, flags);
}
//
void TCoolGrid::TInPlaceComboBox::TLocalEdit::EvChar(uint key, uint repeatCount, uint flags)
{
  if(!GetComboBox()->IsReadOnly())
    Inherited::EvKeyDown(key, repeatCount, flags);
}
//
uint TCoolGrid::TInPlaceComboBox::TLocalEdit::EvGetDlgCode(const MSG* msg)
{
  return Inherited::EvGetDlgCode(msg)|DLGC_WANTALLKEYS;
}
//
void TCoolGrid::TInPlaceComboBox::TLocalEdit::EvKillFocus(HWND hWndGetFocus)
{
  TCoolGrid::TInPlaceComboBox* cBox = GetComboBox();
  cBox->ShowWindow(SW_HIDE);
  Inherited::EvKillFocus(hWndGetFocus);
  cBox->EndEdit();
}
//
void TCoolGrid::TInPlaceComboBox::TLocalEdit::EvLButtonDblClk(uint modKeys, const TPoint& point)
{
  Inherited::EvLButtonDblClk(modKeys, point);
  TComboBox* cbox = GetComboBox();
  int index = cbox->GetSelIndex();
  if(index==CB_ERR || (index+1) >= cbox->GetCount())
    index = -1;
  index++;
  cbox->SetSelIndex(index);
}
//------------------------------------------------------------------------
// class TCoolGrid::TInPlaceComboBox
// ~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
// DropDown combobox
//
DEFINE_RESPONSE_TABLE1(TCoolGrid::TInPlaceComboBox, TInPlaceListBox)
  EV_WM_KEYDOWN,
  EV_WM_KILLFOCUS,
END_RESPONSE_TABLE;
//
TCoolGrid::TInPlaceComboBox::TInPlaceComboBox(TCoolGrid& parent, uint textLimit)
:
  TInPlaceListBox(parent, textLimit)
  ,LocalEdit(0)
  ,ReadOnly(false)
{
  ModifyStyle(CBS_DROPDOWNLIST, CBS_DROPDOWN);
}
//
void TCoolGrid::TInPlaceComboBox::SetupWindow()
{
  TListBox::SetupWindow();
 
  SetTextLimit(TextLimit);
 
  THandle hWnd = ::GetWindow(GetHandle(), GW_CHILD);
  while (hWnd) {
    if(!GetWindowPtr(hWnd)){
      TWindow* wnd = 0; InUse(wnd);
      if(::GetDlgCtrlID(hWnd)==IDC_LOCAL_COMBOEDIT)
        wnd = LocalEdit = new TLocalEdit(this, hWnd);
      else
        wnd = new TWindow(hWnd);
      wnd->SetParent(this);
    }
    hWnd = ::GetWindow(hWnd, GW_HWNDNEXT);
  }
  TComboBox::Transfer(&Data, tdSetData);
}
//
void TCoolGrid::TInPlaceComboBox::SetReadOnly(bool readonly)
{
  ReadOnly = readonly;
  if(GetHandle())
    LocalEdit->SetReadOnly(ReadOnly);
}
//
void TCoolGrid::TInPlaceComboBox::Activate(const TRect& rect, uint firstKey)
{
  //  TInPlaceListBox::Activate(rect, firstKey);
  TCoolGrid* grid = GetGrid();
  PRECONDITION(grid);
  if(grid->GetFont())
    SetWindowFont(*grid->GetFont(), false);
  TRect clientRect(rect);
  clientRect.bottom += clientRect.Height()*5;
  MoveWindow(clientRect, false);
 
//  ExitOnArrows = (firstKey != VK_LBUTTON);    // If mouse click brought us here,
 
  SetDroppedWidth(4);
  SetHorizontalExtent(0); // no horz scrolling
 
  // Activate the edit box.
  ShowWindow(SW_SHOW);
  SetFocus();
 
  switch(firstKey){
    case VK_LBUTTON:
    case VK_RETURN:{
        TTmpBuffer<_TCHAR> buff(MAX_PATH);
        LocalEdit->GetText(buff, MAX_PATH);
        LocalEdit->SetSelection((int)_tcslen(buff), -1);
      }
      return;
    case VK_BACK:{
        TTmpBuffer<_TCHAR> buff(MAX_PATH);
        LocalEdit->GetText(buff, MAX_PATH);
        LocalEdit->SetSelection((int)_tcslen(buff), -1);
      }
      break;
    case VK_DOWN: case VK_UP: case VK_RIGHT: case VK_LEFT:
    case VK_NEXT: case VK_PRIOR: case VK_HOME: case VK_END:
      LocalEdit->SetSelection(0, -1);
      return;
    default:
      LocalEdit->SetSelection(0, -1);
  }
  PostMessage(WM_CHAR, firstKey);
}
//
void TCoolGrid::TInPlaceComboBox::EvKillFocus(HWND hWndGetFocus)
{
  TComboBox::EvKillFocus(hWndGetFocus);
}
//
inline bool checkHotComboBoxKeys(uint key)
{
  return (key==VK_PRIOR||key==VK_NEXT||key==VK_DOWN||key==VK_UP||
          key==VK_RIGHT||key==VK_LEFT);
}
//
void TCoolGrid::TInPlaceComboBox::EvKeyDown(uint key, uint repeatCount, uint flags)
{
  if(checkExceptKeys(key) || (checkHotComboBoxKeys(key) &&
     (ExitOnArrows||GetKeyState(VK_CONTROL)<0))){
    if(key == VK_ESCAPE)
      Undo();
    LastKey = key;
    GetGrid()->SetFocus();
    return;
  }
  TComboBox::EvKeyDown(key, repeatCount, flags);
}
//
////////////////////////////////////////////////////////////////////
// class TCoolGrid::TCell
// ~~~~~ ~~~~~~~~~~~~~~~~
//
TCoolGrid::TCell::TCell()
:
  Parent(0)
{
}
//
TCoolGrid::TCell::~TCell()
{
}
//
void TCoolGrid::TCell::Paint(TDC& dc, const TRect& rect)
{
  TColor oldClr = dc.SetBkColor(TColor::SysWindow);
  dc.TextRect(rect);
  dc.SetBkColor(oldClr);
}
////////////////////////////////////////////////////////////////////
//
// class TTextCell
// ~~~~~ ~~~~~~~~~
//
TCoolGrid::TTextCell::TTextCell()
:
  Text(0),
  TextLimit(255),
  Format(DT_CENTER|DT_SINGLELINE|DT_VCENTER|DT_END_ELLIPSIS|DT_NOCLIP),
  BgColor(TColor::SysWindow),
  TxColor(TColor::SysWindowText)
{
}
//
TCoolGrid::TTextCell::TTextCell(LPCTSTR text, uint textLimit)
:
  Text(0),
  TextLimit(textLimit),
  Format(DT_CENTER|DT_SINGLELINE|DT_VCENTER|DT_END_ELLIPSIS|DT_NOCLIP),
  BgColor(TColor::SysWindow),
  TxColor(TColor::SysWindowText)
{
  SetText(text);
}
//-----------------------------------------------------------------------------
TCoolGrid::TTextCell::TTextCell(const owl::tstring& text, uint textLimit)
:
  Text(0),
  TextLimit(textLimit),
  Format(DT_CENTER|DT_SINGLELINE|DT_VCENTER|DT_END_ELLIPSIS|DT_NOCLIP),
  BgColor(TColor::SysWindow),
  TxColor(TColor::SysWindowText)
{
  SetText(text);
}
//-----------------------------------------------------------------------------
TCoolGrid::TTextCell::~TTextCell()
{
  delete[] Text;
}
//
void TCoolGrid::TTextCell::SetText(const owl::tstring& text)
{
  SetText(text.c_str());
}
//
void TCoolGrid::TTextCell::SetText(LPCTSTR text)
{
  delete[] Text;
  Text = text ? strnewdup(text) : 0;
}
//
void TCoolGrid::TTextCell::Paint(TDC& dc, const TRect& rect)
{
  PRECONDITION(Parent);
  if(!Text){
    TCell::Paint(dc, rect);
    return;
  }
 
  TFont* font = Parent->GetFont();
  if(font){
    CHECK(font->IsGDIObject());
    dc.SelectObject(*font);
  }
 
  TColor oldBgClr = dc.SetBkColor(BgColor);
  TColor oldTxClr = dc.SetTextColor(TxColor);
  dc.TextRect(rect);
  TRect r = rect;
  dc.DrawTextEx(Text, -1, &r, Format);
  dc.SetBkColor(oldBgClr);
  dc.SetTextColor(oldTxClr);
 
  if(font)
    dc.RestoreFont();
}
//
// Transfers state information for TTextCell
//
// The direction passed specifies whether data is to be read from or
// written to the passed buffer, or whether the data element size is simply to
// be returned
//
// The return value is the size (in bytes) of the transfer data
uint TCoolGrid::TTextCell::Transfer(void* buffer, TTransferDirection direction)
{
  if (!buffer && direction != tdSizeData) return 0;
  if (direction == tdGetData)
    _tcscpy((_TCHAR*)buffer, Text);
 
  else if (direction == tdSetData)
    SetText((_TCHAR *)buffer);
 
  return TextLimit;
}
////////////////////////////////////////////////////////////////////
// class TCoolGrid::TBitmapCell
// ~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
//
TCoolGrid::TBitmapCell::TBitmapCell(const TResId& resIdorIndex, int numImages,
                                    int startImage, bool sharedCels)
:
  ImageArray(0),            // Created on first call to GetDesiredSize
  CurrentImage(startImage),
  NumImages(numImages),
  BitmapOrigin(0,0)
{
  if(sharedCels) {
    ResId = 0;
    CHECK(resIdorIndex.IsInt());
    ImageIndex = resIdorIndex.GetInt();
  }
  else {
    ResId = resIdorIndex;
    ImageIndex = 0;
  }
}
//
TCoolGrid::TBitmapCell::~TBitmapCell()
{
  delete ImageArray;
}
//
int
TCoolGrid::TBitmapCell::SelectImage(int imageNum, bool immediate)
{
  PRECONDITION(Parent);
  PRECONDITION(imageNum >=0 && imageNum < NumImages);
 
  uint oldImageNum = CurrentImage;
 
  if (imageNum != CurrentImage) {
    CurrentImage = imageNum;
    Parent->Invalidate(false);
  }
 
  if(immediate)
    Parent->UpdateWindow();
  return oldImageNum;
}
//
// Handle a system color change by cleaning up & reloading & processing the
// bitmap. Is also called to create the initial bitmap.
//
void
TCoolGrid::TBitmapCell::SysColorChange()
{
  PRECONDITION(Parent);
  delete ImageArray;
 
  if(ResId){
    TDib dib(*Parent->GetModule(), ResId);
    dib.MapUIColors(TDib::MapFace | TDib::MapText | TDib::MapShadow |
      TDib::MapHighlight);
    ImageArray = new TCelArray(dib, NumImages);
  }
}
//
void TCoolGrid::TBitmapCell::Paint(TDC& dc, const TRect& rect)
{
  PRECONDITION(Parent);
  TCelArray& imageArray = ImageArray ? *ImageArray : Parent->GetCelArray();
 
  imageArray.BitBlt(ImageIndex+CurrentImage, dc, rect.left, rect.top);
}
////////////////////////////////////////////////////////////////////
// class TCoolGrid::TBitmapTextCell
// ~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~
//
TCoolGrid::TBitmapTextCell::TBitmapTextCell(const TResId& resIdorIndex,
  int numImages, LPCTSTR text, uint textLimit, int startImage, bool sharedCels)
:
  TTextCell(text, textLimit),
  ImageArray(0),            // Created on first call to GetDesiredSize
  CurrentImage(startImage),
  NumImages(numImages),
  BitmapOrigin(0,0)
{
  if(sharedCels) {
    ResId = 0;
    CHECK(resIdorIndex.IsInt());
    ImageIndex = resIdorIndex.GetInt();
  }
  else {
    ResId = resIdorIndex;
    ImageIndex = 0;
  }
}
//
TCoolGrid::TBitmapTextCell::~TBitmapTextCell()
{
  delete ImageArray;
}
//
int
TCoolGrid::TBitmapTextCell::SelectImage(int imageNum, bool immediate)
{
  PRECONDITION(Parent);
  PRECONDITION(imageNum >=0 && imageNum < NumImages);
 
  uint oldImageNum = CurrentImage;
 
  if (imageNum != CurrentImage) {
    CurrentImage = imageNum;
    Parent->Invalidate(false);
  }
 
  if(immediate)
    Parent->UpdateWindow();
  return oldImageNum;
}
//
// Handle a system color change by cleaning up & reloading & processing the
// bitmap. Is also called to create the initial bitmap.
//
void
TCoolGrid::TBitmapTextCell::SysColorChange()
{
  PRECONDITION(Parent);
  delete ImageArray;
 
  if(ResId){
    CHECK(NumImages);
    TDib dib(*Parent->GetModule(), ResId);
    dib.MapUIColors(TDib::MapFace | TDib::MapText | TDib::MapShadow |
      TDib::MapHighlight);
    ImageArray = new TCelArray(dib, NumImages);
  }
}
//
void TCoolGrid::TBitmapTextCell::Paint(TDC& dc, const TRect& rect)
{
  PRECONDITION(Parent);
 
  TRect textRect,bmpRect;
  Layout(rect,textRect,bmpRect);
 
  TColor oldBgClr = dc.SetBkColor(BgColor);
  TColor oldTxClr = dc.SetTextColor(TxColor);
  dc.TextRect(rect);
  dc.SetBkColor(oldBgClr);
  dc.SetTextColor(oldTxClr);
 
  TTextCell::Paint(dc, textRect);
  PaintBitmap(dc, bmpRect);
}
//
void TCoolGrid::TBitmapTextCell::Layout(const TRect& src,TRect& txr,TRect& bmpr)
{
  PRECONDITION(Parent);
 
  if(NumImages){
    TCelArray& imageArray = ImageArray ? *ImageArray : Parent->GetCelArray();
 
    TSize bsize = imageArray.CelSize();
    bmpr.top = src.top + (src.Height()-bsize.cy)/2;
    bmpr.left = src.left;
    bmpr.right = bmpr.left + bsize.cx;
    bmpr.bottom = src.bottom;
  }
  txr.top    = src.top;
  txr.left  = bmpr.right;
  txr.right = src.right;
  txr.bottom= src.bottom;
}
//
void TCoolGrid::TBitmapTextCell::PaintBitmap(TDC& dc, const TRect& rect)
{
  PRECONDITION(Parent);
  if(NumImages){
    TCelArray& imageArray = ImageArray ? *ImageArray : Parent->GetCelArray();
    imageArray.BitBlt(ImageIndex+CurrentImage, dc, rect.left, rect.top);
  }
}
////////////////////////////////////////////////////////////////////
// class TCoolGrid::TButtonCell
// ~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
//
TCoolGrid::TButtonCell::TButtonCell(const TResId& resIdorIndex,
  int numImages, LPCTSTR text, uint textLimit, int startImage, bool sharedCels)
:
  TBitmapTextCell(resIdorIndex, numImages,text,textLimit,startImage,sharedCels)
{
  SetTxColor(TColor::SysBtnText);
  SetBgColor(TColor::Sys3dFace);
}
//
void TCoolGrid::TButtonCell::Paint(TDC& dc, const TRect& rect)
{
  Inherited::Paint(dc, rect);
  if(TCell::IsSet(cfButtonDown))
    TUIBorder(rect, TUIBorder::ButtonDn).Paint(dc);
  else
    TUIBorder(rect, TUIBorder::ButtonUp).Paint(dc);
}
//
void TCoolGrid::TButtonCell::MouseDown(uint /*modKeys*/, const TPoint&)
{
  PRECONDITION(Parent);
  if(Parent->CellSetCapture(*this)){
    TCell::Set(cfButtonDown);
    Parent->Invalidate();
  }
}
//
void TCoolGrid::TButtonCell::MouseUp(uint modKeys, const TPoint& point)
{
  if(Parent->CellGetCaptured()==this){
    TCell::Clear(cfButtonDown);
    Parent->CellReleaseCapture(*this);
    TCellPos pos = Parent->Point2Pos(point);
    Parent->Invalidate();
    if(Parent->GetCell(pos) == this)
      Action(modKeys, point);
  }
}
////////////////////////////////////////////////////////////////////
// class TCoolGrid::TCommandCell
// ~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
//
TCoolGrid::TCommandCell::TCommandCell(int id, const TResId& resIdorIndex,
  int numImages, LPCTSTR text, uint textLimit, int startImage, bool sharedCels)
:
  TButtonCell(resIdorIndex, numImages,text,textLimit,startImage,sharedCels),
  Id(id)
{
}
//
void TCoolGrid::TCommandCell::Action(uint /*modKeys*/, const TPoint&)
{
  Parent->SendNotification(Parent->GetHandle(), Id, BN_CLICKED,0,WM_COMMAND);
}
////////////////////////////////////////////////////////////////////
// class TCoolGrid::TEditCell
// ~~~~~ ~~~~~~~~~~~~~~~~~~~~
//
TCoolGrid::TEditCell::TEditCell()
:
  InPlaceEdit(0)
{
  Set(cfTransfer); // enable transfer
}
//-----------------------------------------------------------------------------
TCoolGrid::TEditCell::TEditCell(LPCTSTR text, uint textLimit)
:
  TCoolGrid::TTextCell(text, textLimit),
  InPlaceEdit(0)
{
  Set(cfTransfer); // enable transfer
}
//-----------------------------------------------------------------------------
TCoolGrid::TEditCell::TEditCell(const owl::tstring& text, uint textLimit)
:
  TCoolGrid::TTextCell(text, textLimit),
  InPlaceEdit(0)
{
  Set(cfTransfer); // enable transfer
}
//-----------------------------------------------------------------------------
TCoolGrid::TEditCell::~TEditCell()
{
  delete InPlaceEdit;
}
//
void TCoolGrid::TEditCell::SetInPlaceEdit(TInPlaceEdit* edit)
{
  delete InPlaceEdit;
  InPlaceEdit = edit;
}
////////////////////////////////////////////////////////////////////
// class TCoolGrid::THeaderCell
// ~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
//
//
TCoolGrid::THeaderCell::THeaderCell(LPCTSTR text, uint textLimit)
:
  TCoolGrid::TTextCell(text, textLimit)
{
  SetBgColor(TColor::Sys3dFace);
}
//
TCoolGrid::THeaderCell::THeaderCell(const owl::tstring& text, uint textLimit)
:
  TCoolGrid::TTextCell(text, textLimit)
{
  SetBgColor(TColor::Sys3dFace);
}
//
void TCoolGrid::THeaderCell::Paint(TDC& dc, const TRect& rect)
{
  PRECONDITION(Parent);
  if(!Text){
    TTextCell::Paint(dc, rect);
    return;
  }
//  TColor oldBgClr = dc.SetBkColor(BgColor);
//  TColor oldTxClr = dc.SetTextColor(TxColor);
  TRect r = rect;
  dc.DrawTextEx(Text, -1, &r, Format);
//  dc.SetBkColor(oldBgClr);
//  dc.SetTextColor(oldTxClr);
}
////////////////////////////////////////////////////////////////////
// class TCoolGrid::TComboBoxCell
// ~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~
//
TCoolGrid::TComboBoxCell::TComboBoxCell()
:
  InPlaceEdit(0)
{
  Set(cfTransfer); // enable transfer
}
//
TCoolGrid::TComboBoxCell::TComboBoxCell(LPCTSTR text, uint textLimit)
:
  TTextCell(text, textLimit),
  InPlaceEdit(0)
{
  Set(cfTransfer); // enable transfer
}
//
TCoolGrid::TComboBoxCell::TComboBoxCell(const owl::tstring& text, uint textLimit)
:
  TTextCell(text, textLimit),
  InPlaceEdit(0)
{
  Set(cfTransfer); // enable transfer
}
//
TCoolGrid::TComboBoxCell::~TComboBoxCell()
{
  delete InPlaceEdit;
}
//
void TCoolGrid::TComboBoxCell::SetInPlaceEdit(TInPlaceEdit* edit)
{
  delete InPlaceEdit;
  InPlaceEdit = edit;
}
//
void TCoolGrid::TComboBoxCell::Paint(TDC& dc, const TRect& rect)
{
  TRect btnRect(rect);
  if(!IsSet(cfNoShowButton))
    btnRect.right -= TUIMetric::CxVScroll;
 
  Inherited::Paint(dc, btnRect);
 
  if(!IsSet(cfNoShowButton)){
    btnRect.left  = rect.right - btnRect.Height();
    btnRect.right = rect.right;
    TUIPart().Paint(dc, btnRect, TUIPart::uiScroll,TUIPart::ScrollDown);
  }
}
//
uint TCoolGrid::TComboBoxCell::Transfer(void* buffer, TTransferDirection direction)
{
  return Inherited::Transfer(buffer, direction);
}
////////////////////////////////////////////////////////////////////
// class TCoolGrid::TColumn
// ~~~~~ ~~~~~~~~~~~~~~~~~~
//
TCoolGrid::TColumn::TColumn(TCoolGrid& parent)
:
  Parent(parent),
  InPlaceEdit(0),
  DefColor(TColor::SysWindow),
  Font(0),
  BoldFont(0),
  CellBuilder(TCell_FUNCTOR(CreateCoolCell)),
  Header(0)
{
  Set(cfTransfer);
}
//
TCoolGrid::TColumn::~TColumn()
{
  delete InPlaceEdit;
  delete Font;
  delete Header;
}
//
TCoolGrid::TCell*
TCoolGrid::TColumn::operator [](int loc)
{
  TCellMap::iterator itr = CellMap.find(loc);
  if(itr != CellMap.end())
    return (*itr).second;
  return 0;
}
//
void TCoolGrid::TColumn::SetInPlaceEdit(TInPlaceEdit* edit)
{
  delete InPlaceEdit;
  InPlaceEdit = edit;
}
//
void TCoolGrid::TColumn::SetCell(int loc, TCell& cell)
{
  cell.SetParent(&Parent);
  TCellMap::iterator itr = CellMap.find(loc);
  if(itr != CellMap.end()){
    delete (*itr).second;
    (*itr).second = &cell;
  }
  else
    CellMap.insert(TCellMap::value_type(loc, &cell));
  cell.SysColorChange();
}
//
void TCoolGrid::TColumn::RemoveCell(int loc)
{
  TCellMap::iterator itr = CellMap.find(loc);
  if(itr != CellMap.end()){
    delete (*itr).second;
    CellMap.erase(itr);
  }
}
//
void TCoolGrid::TColumn::SetHeader(const owl::tstring& text)
{
  if(Header)
    Header->SetText(text.c_str());
  else
    SetHeader(new THeaderCell(text.c_str()));
}
//
void TCoolGrid::TColumn::SetHeader(THeaderCell* cell)
{
  delete Header;
  Header = cell;
  if(Header){
    Header->SetParent(&Parent);
    Header->SysColorChange();
  }
}
//
void TCoolGrid::TColumn::PaintDefaultHeader(TDC& dc, const TRect& rect)
{
  Parent.PaintHorizHeaderCell(dc, rect, Id);
}
//
void TCoolGrid::TColumn::PaintHeader(TDC& dc, const TRect& rect, THeaderCell* cell)
{
  PRECONDITION(cell);
  TColor oldBgClr = dc.SetBkColor(cell->GetBgColor());
  TColor oldTxClr = dc.SetTextColor(cell->GetTxColor());
  VERIFY(dc.TextRect(rect));
 
  TUIBorder::TStyle style = TUIBorder::Raised;
  TFont* font = GetFont();
  const TCellPos& fpos = Parent.GetFocusPos();
  if(fpos.Valid() && fpos.col == Id && !Parent.IsRowSelection()){
    style = TUIBorder::ButtonUp;
    font = GetBoldFont();
  }
 
  TUIBorder(rect,style,TUIBorder::Rect|TUIBorder::Soft).Paint(dc);
 
  if(font){
    CHECK(font->IsGDIObject());
    dc.SelectObject(*font);
  }
  TRect textRect(rect);
  textRect.Inflate(-2,-2);
  Header->Paint(dc, textRect);
  if(font)
    dc.RestoreFont();
  dc.SetBkColor(oldBgClr);
  dc.SetTextColor(oldTxClr);
}
//
void TCoolGrid::TColumn::Paint(TDC& dc, const TRect& rect)
{
  int lastCellId = Parent.GetRowCount()-1;
  bool hasHeader = Parent.IsHorizHeader();
 
  int headerDelta = 0;
  if(hasHeader)
    headerDelta = Parent.GetHeaderHeight() + Parent.GetLineHeight();
 
  TRect colmnRect(rect);
  colmnRect.top -= Parent.GetTopPos();
  colmnRect.right += Parent.GetLineWidth();
 
  colmnRect.top += headerDelta;
  colmnRect.bottom = Parent.Index2Y(lastCellId) +
                     Parent.GetRowHeight(lastCellId) + headerDelta;
 
 
  TColor oldClr = dc.SetBkColor(DefColor);
  dc.TextRect(colmnRect);
  dc.SetBkColor(oldClr);
 
  for(TCellMap::iterator itr = CellMap.begin();
      itr != CellMap.end(); itr++){
    TRect cellRect(colmnRect);
    cellRect.top    = Parent.Index2Y((*itr).first) + headerDelta;
    cellRect.bottom = cellRect.top + Parent.GetRowHeight((*itr).first);
    if(cellRect.top > rect.bottom)
      break;
    if(cellRect.bottom > headerDelta)
      (*itr).second->Paint(dc, cellRect);
  }
 
  if(hasHeader){
    TRect hdrRect(rect);
    hdrRect.bottom = hdrRect.top + headerDelta;
    hdrRect.right  += Parent.GetLineWidth();
    if(Header)
      PaintHeader(dc, hdrRect, Header);
    else
      PaintDefaultHeader(dc, hdrRect);
  }
 
  // fill rest area
  colmnRect.top     = colmnRect.bottom;
  colmnRect.bottom  = rect.bottom;
  oldClr = dc.SetBkColor(TColor::SysAppWorkspace);
  dc.TextRect(colmnRect);
  dc.SetBkColor(oldClr);
}
//
void
TCoolGrid::TColumn::SetFont(const TFont& font)
{
  delete Font;
  Font  = new TFont(font);
}
//
void
TCoolGrid::TColumn::SetBoldFont(const TFont& font)
{
  delete BoldFont;
  BoldFont = new TFont(font);
}
//
TCoolGrid::TCell*
TCoolGrid::TColumn::FirstThat(TCellCondOperator& test)
{
  for(TCellMap::iterator itr = CellMap.begin(); itr != CellMap.end(); itr++){
    if(test((*itr).second))
      return (*itr).second;
  }
  return 0;
}
//
TCoolGrid::TCell*
TCoolGrid::TColumn::LastThat(TCellCondOperator& test)
{
  TCell* lastCell = 0;
  for(TCellMap::iterator itr = CellMap.begin(); itr != CellMap.end(); itr++){
    if(test((*itr).second))
      lastCell = (*itr).second;
  }
  return lastCell;
}
//
void
TCoolGrid::TColumn::ForEach(TCellActionOperator& action)
{
  for(TCellMap::iterator itr = CellMap.begin(); itr != CellMap.end(); itr++)
    action((*itr).second);
}
//
struct TTransferCellOp: public TCellActionOperator {
  TTransferCellOp(void* buff, TTransferDirection dir)
    :Data(buff),Direction(dir){}
  void operator ()(TCoolGrid::TCell* cell)
    {
      if(cell->IsSet(TCoolGrid::TCell::cfTransfer))
        Data = (char*)Data + cell->Transfer(Data, Direction);
    }
  void*               Data;
  TTransferDirection  Direction;
};
//
uint TCoolGrid::TColumn::Transfer(void* buffer, TTransferDirection direction)
{
  if (!buffer && direction != tdSizeData) return 0;
  TTransferCellOp op(buffer, direction);
  ForEach(op);
  return static_cast<uint>((char*)op.Data - (char*)buffer);
}
//
// struct TColSelection
//
void TCoolGrid::TColSelection::Paint(TDC& dc)
{
  TRect rowRect;
  rowRect.top   = 0;
  rowRect.bottom= Parent->Index2Y(Parent->RowCount-1) +
                  Parent->GetRowHeight(Parent->RowCount-1);
  if(Parent->IsFlagSet(TCoolGrid::gfHorizHeader))
    rowRect.bottom  += Parent->GetHeaderHeight()+Parent->GetLineHeight();
  rowRect.left  = Parent->Index2X(Column);
  if(Parent->IsFlagSet(TCoolGrid::gfVertHeader))
    rowRect.left  += Parent->GetHeaderWidth()+Parent->GetLineWidth();
  rowRect.right = rowRect.left + Parent->GetColumnWidth(Column);
  VERIFY(dc.InvertRect(rowRect));
}
//
void TCoolGrid::TColSelection::PaintHHeader(TDC& dc)
{
  if(Parent->IsFlagSet(TCoolGrid::gfHorizHeader)){
    TRect rowRect;
    rowRect.top   = 0;
    rowRect.bottom= Parent->GetHeaderHeight()+Parent->GetLineHeight();
    rowRect.left  = Parent->Index2X(Column);
    if(Parent->IsFlagSet(TCoolGrid::gfVertHeader))
      rowRect.left  += Parent->GetHeaderWidth()+Parent->GetLineWidth();
    rowRect.right = rowRect.left + Parent->GetColumnWidth(Column);
    VERIFY(dc.InvertRect(rowRect));
  }
}
 
//
// struct TRowSelection
//
void TCoolGrid::TRowSelection::Paint(TDC& dc)
{
  TRect rowRect;
  rowRect.top   = Parent->Index2Y(Row);
  if(Parent->IsFlagSet(TCoolGrid::gfHorizHeader))
    rowRect.top  += Parent->GetHeaderHeight()+Parent->GetLineHeight();
  rowRect.bottom= rowRect.top + Parent->GetRowHeight(Row);
  rowRect.left  = 0;
  rowRect.right = Parent->Index2X(Parent->GetColCount()-1) +
                  Parent->GetColumnWidth(Parent->GetColCount()-1);
  if(Parent->IsFlagSet(TCoolGrid::gfVertHeader))
    rowRect.right  += Parent->GetHeaderWidth()+Parent->GetLineWidth();
  VERIFY(dc.InvertRect(rowRect));
}
//
void TCoolGrid::TRowSelection::PaintVHeader(TDC& dc)
{
  if(Parent->IsFlagSet(TCoolGrid::gfVertHeader)){
    TRect rowRect;
    rowRect.top   = Parent->Index2Y(Row);
    if(Parent->IsFlagSet(TCoolGrid::gfHorizHeader))
      rowRect.top  += Parent->GetHeaderHeight() + Parent->GetLineHeight();
    rowRect.bottom = rowRect.top + Parent->GetRowHeight(Row);
    rowRect.left   = 0;
    rowRect.right  = Parent->GetHeaderWidth() + Parent->GetLineWidth();
    VERIFY(dc.InvertRect(rowRect));
  }
}
//
void TCoolGrid::TRandSelection::Paint(TDC&)
{
}
//
void TCoolGrid::TAllSelection::Paint(TDC& dc)
{
  TRect rect;
  Parent->GetClientRect(rect);
  VERIFY(dc.InvertRect(rect)); InUse(dc);
}
//
void TCoolGrid::TAllSelection::PaintHHeader(TDC& dc)
{
  if(Parent->IsFlagSet(TCoolGrid::gfHorizHeader)){
    TRect rect;
    Parent->GetClientRect(rect);
    rect.bottom = Parent->GetHeaderHeight()+Parent->GetLineHeight();
    VERIFY(dc.InvertRect(rect));
  }
}
//
void TCoolGrid::TAllSelection::PaintVHeader(TDC& dc)
{
  if(Parent->IsFlagSet(TCoolGrid::gfVertHeader)){
    TRect rect;
    Parent->GetClientRect(rect);
    rect.right  = Parent->GetHeaderWidth() + Parent->GetLineWidth();
    VERIFY(dc.InvertRect(rect));
  }
}
//
TCoolGridScroller::TCoolGridScroller(TWindow* window, int xUnit, int yUnit,
                                     long xRange,long yRange)
:
  TScroller(window, xUnit, yUnit, xRange,yRange)
{
}
//-----------------------------------------------------------------------------
// sets the range of the Window's scrollbars & repaints as necessary
//
void
TCoolGridScroller::SetSBarRange()
{
  PRECONDITION(Window);
  PRECONDITION(Window->GetHandle());
 
  if (Window && Window->GetHandle()) {
    if (HasHScrollBar) {
      int curMin, curMax;
      GetScrollRange(SB_HORZ, curMin, curMax);
      int newMax = std::max(0, std::min(int(XRange-1), SHRT_MAX));
      if (newMax != curMax || curMin != 0)
        SetScrollRange(SB_HORZ, 0, newMax, true);
    }
 
    if (HasVScrollBar) {
      int curMin, curMax;
      GetScrollRange(SB_VERT, curMin, curMax);
      int newMax = std::max(0, std::min(int(YRange-1), SHRT_MAX));
      if (newMax != curMax || curMin != 0)
        SetScrollRange(SB_VERT, 0, newMax, true);
    }
  }
}
//-----------------------------------------------------------------------------
void TCoolGridScroller::ScrollTo(long x, long y)
{
  PRECONDITION(Window);
  PRECONDITION(Window->GetHandle());
 
  if (Window && Window->GetHandle()) {
    long  newXPos = std::max(0L, std::min(x, XRange - XPage));
    long  newYPos = std::max(0L, std::min(y, YRange - YPage));
 
    if (newXPos != XPos || newYPos != YPos) {
      //
      // scaling isn't needed here. if condition is met, ScrollWindow()
      // will succeed since XPage and YPage are ints
      //
      // if condition isn't met, ScrollWindow() is called in EndScroll()
      // as a result of calling UpdateWindow()
      //
      // EndScroll() performs the necessary scaling
      //
      TCoolGrid* wnd = TYPESAFE_DOWNCAST(Window, TCoolGrid);
      CHECK(wnd);
      if (AutoOrg || (abs(YPos-newYPos) < YPage && abs(XPos-newXPos) < XPage)){
        TRect clientRect;
        Window->GetClientRect(clientRect);
        if((YPos - newYPos) == 0)
          clientRect.left += wnd->GetHeaderWidth() + wnd->GetLineWidth();
        if((XPos - newXPos) == 0)
          clientRect.top += wnd->GetHeaderHeight() + wnd->GetLineHeight();
        Window->ScrollWindow((int)(XPos - newXPos) * XUnit,
                (int)(YPos - newYPos) * YUnit, &clientRect, &clientRect);
      }
      else
        wnd->Invalidate();
 
      XPos = newXPos;
      YPos = newYPos;
      Window->UpdateWindow();
    }
  }
}
//
//{{TCoolGrid Implementation}}
 
//
// Build a response table for all messages/commands handled
// by the application.
//
DEFINE_RESPONSE_TABLE1(TCoolGrid, TControl)
  EV_WM_LBUTTONDOWN,
  EV_WM_SIZE,
  EV_WM_LBUTTONDBLCLK,
  EV_WM_KEYDOWN,
  EV_MESSAGE(COOLGRID_ENDEDIT, EvEndEdit),
  EV_WM_CHAR,
  EV_WM_SETCURSOR,
  EV_WM_MOUSEMOVE,
  EV_WM_LBUTTONUP,
  EV_WM_SYSCOLORCHANGE,
END_RESPONSE_TABLE;
 
int TCoolGrid::DefaultWidth   = 50;
int TCoolGrid::DefaultHeight  = -1;
 
//--------------------------------------------------------
// TCoolGrid Constructor
//
TCoolGrid::TCoolGrid(TWindow* parent,int id,LPCTSTR title,int x, int y, int w, int h,TModule* module)
:
  TControl(parent,id,title,x,y,w,h,module),
 
  Flags(gfBothHeaders|gfResHHeader),
  MemBitmap(0),
  Font(0),
  BoldFont(0),
  DefColor(TColor::SysWindow),
 
  Width(DefaultWidth),
  Height(DefaultHeight),
  ColCount(0),
  RowCount(0),
  CharSize(-1,-1),
  HeaderWidth(DefaultWidth),
  HeaderHeight(DefaultHeight),
  DefVertFormat(_T("%d")),
  DefHorFormat(_T("%d")),
  LineWidth(1),
  LineHeight(1),
 
  FocusedCell(-1,-1),
  Capture(0),
  InPlaceEdit(0),
  CurrentEdit(0),
 
  ColBuilder(TColumn_FUNCTOR(CreateCoolColumn)),
  CellBuilder(TCell_FUNCTOR(CreateCoolCell)),
  Selection(0),
  SharedCels(0)
{
  SetBkgndColor(TColor::Transparent);
 
  NONCLIENTMETRICS ncm;
  ncm.cbSize = sizeof(NONCLIENTMETRICS);
  SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0);
 
  LOGFONT lfFont   = ncm.lfSmCaptionFont;
  lfFont.lfWeight = FW_NORMAL;
  Font  = new TFont(lfFont);
 
  lfFont.lfWeight = FW_BOLD;
  BoldFont = new TFont(lfFont);
}
 
//--------------------------------------------------------
// TCoolGrid Destructor
//
TCoolGrid::~TCoolGrid()
{
  delete Selection;
  delete MemBitmap;
  delete InPlaceEdit;
  delete Font;
  delete BoldFont;
  delete SharedCels;
  Destroy(IDCANCEL);
}
//
auto TCoolGrid::GetWindowClassName() -> TWindowClassName
{
  return TWindowClassName{_T("CoolGrid")};
}
//
void TCoolGrid::GetWindowClass(WNDCLASS& wndClass)
{
  // Call base class function.
  TControl::GetWindowClass(wndClass);
  wndClass.style |= CS_DBLCLKS|CS_HREDRAW|CS_VREDRAW;
}
//
int TCoolGrid::FindMaxColWidth() const
{
  int width = Width;
  for(TDimMap::const_iterator itr = WidthMap.begin(); itr != WidthMap.end(); itr++)
    width = std::max(width, (*itr).second);
  return width;
}
//
void TCoolGrid::SetCelArray(TCelArray* sharedCels)
{
  delete SharedCels;
  SharedCels = sharedCels;
}
//
TCelArray& TCoolGrid::GetCelArray(int minX, int minY)
{
  if (!SharedCels) {
    //!CQ default??? BOGUS values here for now on purpose
    if(!minX)
      minX = 10;
    if(!minY)
      minY = 10;
    SharedCels = new TCelArray(TSize(minX,minY), 0, 10, 5);
  }
  return *SharedCels;
}
//
void TCoolGrid::Paint(TDC& dc, bool /*erase*/, TRect& /*rect*/)
{
  TRect clientRect;
  GetClientRect(clientRect);
 
  TRect columnRect(clientRect.TopLeft(), TSize(FindMaxColWidth(), clientRect.Height()));
 
  // must be member variables
  TMemoryDC  memDC(dc);
  if(!MemBitmap)
    MemBitmap = new TBitmap(dc, columnRect.Width()+GetLineWidth(),
                            columnRect.Height()+GetLineHeight());
  memDC.SelectObject(*MemBitmap);
 
  int deltaWidth = HeaderWidth + LineWidth;
  TRect workRect(columnRect);
  workRect.right  += LineWidth;
  workRect.bottom += LineHeight;
 
  if(IsFlagSet(gfVertHeader)){
    workRect.right = workRect.left + deltaWidth;
    PaintHeaderColumn(memDC, workRect);
    // paint corner
    if(IsFlagSet(gfHorizHeader)){
      TRect cellRect(columnRect);
      cellRect.bottom = cellRect.top + HeaderHeight + LineHeight;
      cellRect.right  = cellRect.left + deltaWidth;
      PaintCornerCell(memDC, cellRect);
    }
    VERIFY(dc.BitBlt(workRect, memDC, TPoint(0,0)));
    workRect.Offset(deltaWidth, 0);
  }
 
  //
  // Paint Vertical headre selections and
  // exclude vertical header area from paint
  //
  if(IsVertHeader()){
    // Paint Vertical Borders for Focused Cell
    if(Selection)
      Selection->PaintVHeader(dc);
    else if(FocusedCell.Valid()){
      // paint on DC, not on MemDC, why, maby better would be on MemDC ???
      if(IsFlagSet(gfRowSelect)){
        // paint full row
        TRect rowRect;
        rowRect.top    = Index2Y(FocusedCell.row);
        if(IsHorizHeader())
          rowRect.top += HeaderHeight+LineHeight;
        rowRect.bottom = rowRect.top + GetRowHeight(FocusedCell.row);
        rowRect.left   = 0;
        rowRect.right  = HeaderWidth+LineWidth;
        if(IsFlagSet(gfNoRowInvert)){
          rowRect.Inflate(1,1);
          rowRect.right +=2;// will be erased
          TUIBorder(rowRect, TUIBorder::ButtonDn, TUIBorder::Rect|TUIBorder::Soft).Paint(dc);
        }
        else{
          VERIFY(dc.InvertRect(rowRect));
        }
      }
    }
    // exclude horisontal headrs from painting
#ifndef _MSC_VER // VC fail to compile this code
    TRect clipRect(columnRect);
    clipRect.right = clipRect.left + deltaWidth;
    dc.ExcludeClipRect(clipRect);
#else
    int oldright = columnRect.right;
    columnRect.right = columnRect.left + deltaWidth;
    dc.ExcludeClipRect(columnRect);
    columnRect.right = oldright;
#endif
  }
 
  //
  // Draw all columns
  //
  workRect.Offset(-GetOffsetPos(), 0);
  int i = FirstVisibleColumn();
  for(; i < ColCount && workRect.left < clientRect.right; i++){
 
    // calculate rectangle
    TDimMap::iterator itr1 = WidthMap.find(i);
    int col_with = itr1 != WidthMap.end() ? (*itr1).second : Width;
    columnRect.right = columnRect.left+col_with;
 
    TColumnMap::iterator itr = ColumnMap.find(i);
    if(itr != ColumnMap.end())
      (*itr).second->Paint(memDC, columnRect);
    else
      PaintDefaultColumn(memDC, columnRect, i);
 
    // Paint this Column Grid
    PaintGridLines(memDC, columnRect);
    workRect.right = workRect.left + col_with + LineWidth;
    VERIFY(dc.BitBlt(workRect, memDC, TPoint(0,0)));
 
    workRect.Offset(col_with+GetLineWidth(), 0);
  }
  workRect.Offset(GetOffsetPos(), 0);
 
  //
  // Restore original bitmap before leaving
  //
  memDC.RestoreBitmap();
 
  // fill rest area
  if(workRect.left < clientRect.right){
    workRect.right = clientRect.right + LineWidth - GetOffsetPos();
    TColor oldClr = dc.SetBkColor(TColor::SysAppWorkspace);
    VERIFY(dc.TextRect(workRect));
    dc.SetBkColor(oldClr);
  }
 
  //
  // Paint Vertical headre selections and
  // exclude Horisontal header area from paint
  //
  if(IsFlagSet(gfHorizHeader)){
    // Paint Vertical Borders for Focused Cell
    if(Selection)
      Selection->PaintHHeader(dc);
    TRect clipRect(clientRect);
    clipRect.bottom = clipRect.top + HeaderHeight + LineHeight;
    dc.ExcludeClipRect(clipRect);
  }
 
  // Paint Rest of Focused Cell
  if(Selection)
    Selection->Paint(dc);
  else if(FocusedCell.Valid()){
    TRect cellRect;
    GetCellRect(FocusedCell, cellRect);
    PaintFocusedCell(dc, cellRect);
  }
}
//
void TCoolGrid::Cell2EditTransfer(TCell& cell, TInPlaceEdit& edit)
{
   uint size = cell.Transfer(0, tdSizeData);
   if(size){
     TAPointer<uint8> buffer(new uint8[size]);
     cell.Transfer((void*)(uint8*)buffer, tdGetData);
     edit.Transfer((void*)(uint8*)buffer, tdSetData);
   }
}
//
void TCoolGrid::Edit2CellTransfer(TInPlaceEdit& edit, TCell& cell)
{
  // transfer data to/from edit
  uint size = edit.Transfer(0, tdSizeData);
  if(size){
    TAPointer<uint8> buffer(new uint8[size]);
    edit.Transfer((void*)(uint8*)buffer, tdGetData);
    cell.Transfer((void*)(uint8*)buffer, tdSetData);
  }
}
//
TResult TCoolGrid::EvEndEdit(TParam1, TParam2)
{
  SetFocus();
 
  if(CurrentEdit){
    TCell* cell = GetFocusedCell();
    if(cell)
      Cell2EditTransfer(*cell, *CurrentEdit);
    TRect rect;
    GetCellRect(FocusedCell, rect);
    InvalidateRect(rect);
    uint lastKey = CurrentEdit->GetLastKey();
    CurrentEdit = 0;
    switch (lastKey){
      case VK_TAB: case VK_DOWN: case VK_UP: case VK_RIGHT: case VK_LEFT:
      case VK_NEXT: case VK_PRIOR: case VK_HOME: case VK_END:
        EvKeyDown(lastKey, 1, 0);
        //OnEditCell(m_idCurrentCell.row, m_idCurrentCell.col, pgvItem->lParam);
    }
  }
  return 0;
}
//
void TCoolGrid::PaintVDivider(const TPoint& point, bool redraw)
{
  TRect rect;
  GetClientRect(rect);
  TClientDC dc(GetHandle());
  if(redraw){
    rect.top    = LastDrawPoint.y;
    rect.bottom = LastDrawPoint.y+2;
    dc.InvertRect(rect);
  }
  int pos_y = point.y;
  if(MinDividerIndex==-1){
    if(pos_y < HitDelta*2)
      pos_y = HitDelta*2;
  }
  else {
    int minPos = Index2Y(MinDividerIndex)+HitDelta*2;
    minPos += IsHorizHeader() ? (HeaderHeight+LineHeight) : 0;
    if(pos_y < minPos)
      pos_y = minPos;
  }
 
  rect.top    = pos_y;
  rect.bottom = pos_y+2;
  dc.InvertRect(rect);
  LastDrawPoint = TPoint(LastDrawPoint.x, pos_y);
}
//
void TCoolGrid::PaintHDivider(const TPoint& point, bool redraw)
{
  TRect rect;
  GetClientRect(rect);
  TClientDC dc(GetHandle());
  if(redraw){
    rect.left    = LastDrawPoint.x;
    rect.right  = LastDrawPoint.x  + 2;
    dc.InvertRect(rect);
  }
  int pos_x = point.x;
  if(MinDividerIndex==-1){
    if(pos_x < HitDelta*4)
      pos_x = HitDelta*4;
  }
  else {
    int minPos = Index2X(MinDividerIndex)+HitDelta*4;
    minPos += IsVertHeader() ? (HeaderWidth+LineWidth) : 0;
    if(pos_x < minPos)
      pos_x = minPos;
  }
  rect.left    = pos_x;
  rect.right  = pos_x+2;
  dc.InvertRect(rect);
  LastDrawPoint = TPoint(pos_x, LastDrawPoint.y);
}
//
void TCoolGrid::ResizeColumn(int column, int delta)
{
  if(column==-1)
    SetHeaderWidth(GetHeaderWidth()+delta);
  else
    SetColumnWidth(column, GetColumnWidth(column)+delta);
  delete MemBitmap;
  MemBitmap = 0;
  Invalidate();
}
//
void TCoolGrid::ResizeRow(int row, int delta)
{
  if(row==-1)
    SetHeaderHeight(GetHeaderHeight()+delta);
  else
    SetRowHeight(row, GetRowHeight(row)+delta);
  Invalidate();
}
//
void TCoolGrid::CleanupSelection()
{
  if(Selection){
    delete Selection;
    Selection = 0;
    Invalidate();
    UpdateWindow();
  }
}
//
void TCoolGrid::EvLButtonDown(uint modKeys, const TPoint& point)
{
  SetFocus(); // destroy any InplaysEdits
  CleanupSelection();
  MouseDownPoint = point;
 
  THitResult hitRes = hrGridBody;
  if(IsFlagSet(gfBothHeaders))
    hitRes = HitTest(point);
  if(hitRes!=hrGridBody){
    if(hitRes==hrVHeader){
      if(IsFlagSet(gfRowSelect)){
        // if Set new row
        SetFocusCell(TCellPos(FocusedCell.col,Y2Index(point.y)));
      }
      else{
        SetFocusCell(TCellPos(-1,Y2Index(point.y)));
        Selection = new TRowSelection(this, Y2Index(point.y));
        Invalidate();
      }
    }
    else if(hitRes==hrBothHds && !IsFlagSet(gfRowSelect)){
      SetFocusCell(TCellPos());
      Selection = new TAllSelection(this);
      Invalidate();
    }
    else if(hitRes==hrHHeader && !IsFlagSet(gfRowSelect)){
      SetFocusCell(TCellPos(X2Index(point.x),-1));
      Selection = new TColSelection(this, X2Index(point.x));
      Invalidate();
    }
    else if(hitRes==hrVHDivider && IsFlagSet(gfResVHeader)){
      MinDividerIndex = Y2Index(point.y-HitDelta);
      PaintVDivider(point);
      SetFlag(gfInVResize);
    }
    else if(hitRes==hrHHDivider && IsFlagSet(gfResHHeader)){
      MinDividerIndex = X2Index(point.x-HitDelta);
      PaintHDivider(point);
      SetFlag(gfInHResize);
    }
  }
  else{
    TCellPos pos = Point2Pos(point);
    if(pos.Valid()){
      SelectionStart = (modKeys & MK_SHIFT) ? FocusedCell : pos;
      if(pos==FocusedCell) // maby edit if in Up will be the same
        SetFlag(gfMabyEdit);
      SetFocusCell(pos);
      TCell* cell = GetFocusedCell();
      if(cell)
        cell->MouseDown(modKeys, point);
    }
  }
  Inherited::EvLButtonDown(modKeys, point);
}
//
void TCoolGrid::EvLButtonDblClk(uint modKeys, const TPoint& point)
{
  Inherited::EvLButtonDblClk(modKeys, point);
  TCellPos pos = Point2Pos(point);
  if(pos.Valid()){
    SetFocusCell(pos);
    ShowInplaceEdit(VK_LBUTTON);
  }
  ClearFlag(gfMabyEdit);
}
//
void TCoolGrid::EvMouseMove(uint modKeys, const TPoint& point)
{
  if(Capture)
    Capture->MouseMove(modKeys, point);
  else{
    if(IsFlagSet(gfInVResize)||IsFlagSet(gfInHResize)){
      if(IsFlagSet(gfInVResize))
        PaintVDivider(point, true);
      else
        PaintHDivider(point, true);
    }
  }
  Inherited::EvMouseMove(modKeys, point);
}
//
void TCoolGrid::EvLButtonUp(uint modKeys, const TPoint& point)
{
  Inherited::EvLButtonUp(modKeys, point);
  if(IsFlagSet(gfMabyEdit) && Point2Pos(point)==FocusedCell)
    ShowInplaceEdit(VK_LBUTTON);
  else if(IsFlagSet(gfInVResize)||IsFlagSet(gfInHResize)){
    if(IsFlagSet(gfInVResize)){
      PaintVDivider(point, true);
      PaintVDivider(LastDrawPoint);
      ClearFlag(gfInVResize);
      if(LastDrawPoint.y != MouseDownPoint.y)
        ResizeRow(Y2Index(MouseDownPoint.y-HitDelta),LastDrawPoint.y-MouseDownPoint.y);
    }
    else{
      PaintHDivider(point, true);
      PaintHDivider(LastDrawPoint);
      ClearFlag(gfInHResize);
      if(LastDrawPoint.x != MouseDownPoint.x)
        ResizeColumn(X2Index(MouseDownPoint.x-HitDelta), LastDrawPoint.x-MouseDownPoint.x);
    }
  }
  ClearFlag(gfMabyEdit);
  if(Capture)
    Capture->MouseUp(modKeys, point);
}
//
void TCoolGrid::ShowInplaceEdit(uint key)
{
  TCell* cell = GetFocusedCell();
 
  // create new cell if possible
  if(!cell){
    TColumn* col = GetColumn(FocusedCell.col);
    if(!col){
      col = (ColBuilder)(*this, FocusedCell.col);
      CHECK(col);
      cell = col->GetCellBuilder()(*this, FocusedCell);
      if(cell){
        SetColumn(FocusedCell.col, *col);
        col->SetCell(FocusedCell.row, *cell);
      }
      else
        delete col;
    }
    if(!cell){
      cell = CellBuilder(*this, FocusedCell);
      if(cell)
        SetCell(FocusedCell, *cell);
    }
  }
 
  if(cell){
    // find InPlaceEdit
    CurrentEdit = cell->GetInPlaceEdit();
    if(!CurrentEdit)
      CurrentEdit = GetColumnEdit(FocusedCell.col);
    if(!CurrentEdit)
      CurrentEdit = GetInPlaceEdit();
 
    // If found show it
    if(CurrentEdit){
      TRect rect;
      GetCellRect(FocusedCell, rect);
 
      // transfer data to/from edit
      Cell2EditTransfer(*cell, *CurrentEdit);
      CurrentEdit->Activate(rect, key);
    }
  }
}
//
void TCoolGrid::SetupWindow()
{
  // Call base class function.
  Inherited::SetupWindow();
 
  if(!Scroller)
    //Scroller = new TScroller(this, 7, 16, 80, 60);
    Scroller = new TCoolGridScroller(this, 7, 16, 80, 60);
#if !defined(USE_AUTOORG)
  Scroller->AutoOrg = false;
#endif
 
  // Set height
  TClientDC clientDC(*this);
  if(GetFont())
    CharSize = GetFont()->GetTextExtent(clientDC, _T("X"));
  else
    CharSize = clientDC.GetTextExtentPoint32(_T("X"));
  CharSize.cy += 4;
  if(Height < 0)
    Height = CharSize.cy;
  if(HeaderHeight < 0)
    HeaderHeight = CharSize.cy;
 
  AdjustScroller();
}
//
void TCoolGrid::AdjustScroller()
{
  static bool in_work = false;
  if(!Scroller || in_work)
    return;
 
  in_work = true;
  TRect clientRect;
  GetClientRect(clientRect);
 
  // Only show scrollbars when image is larger than
  // the client area and we are not stretching to fit.
  int lastRowId = GetRowCount()-1;
  int lastColId  = GetColCount()-1;
  int headerHDelta = 0;
  int headerVDelta = 0;
  if(IsHorizHeader())
    headerHDelta = GetHeaderHeight() + GetLineHeight();
  if(IsVertHeader())
    headerVDelta = GetHeaderWidth() + GetLineWidth();
 
  int bottom=Index2Y(lastRowId)+GetTopPos()+GetRowHeight(lastRowId)+GetLineHeight()+headerHDelta;
  int right=Index2X(lastColId)+GetOffsetPos()+GetColumnWidth(lastColId)+GetLineWidth()+headerVDelta;
 
  Scroller->SetUnits(1, 1);
  //TPoint range(std::max(right - clientRect.Width(), 0), std::max(bottom - clientRect.Height(), 0));
  TPoint range(std::max(right, 0), std::max(bottom, 0));
  Scroller->SetRange(range.x, range.y);
//  Scroller->XLine = 10;              // # of horz/vert scroll units per line
//  Scroller->YLine = 10;              //
//  Scroller->XPage = clientRect.Width()*2/3;   // # of horz/vert scroll units per page
//  Scroller->YPage = clientRect.Height()*2/3;  //
 
  //Scroller->ScrollTo(0, 0);
  if (!GetUpdateRect(clientRect, false))
    Invalidate(false);
  in_work = false;
}
//
void TCoolGrid::EvSize(uint sizeType, const TSize& size)
{
//  static bool inScroller = false;
//  if(!inScroller){
//    inScroller = true;
 
    SetFocus(); // Auto-destroy any InPlaceEdit's
 
    Inherited::EvSize(sizeType, size);
 
    if (sizeType != SIZEICONIC){
      delete MemBitmap;
      MemBitmap = 0;
//      AdjustScroller();
//      Invalidate(true);
    }
//    inScroller = false;
//  }
  //else
  //  Inherited::EvSize(sizeType, size);
}
//
void TCoolGrid::EvKeyDown(uint key, uint /*repeatCount*/, uint /*flags*/)
{
  bool is_shift = ::GetAsyncKeyState(VK_SHIFT) & MSB;
  bool is_ctrl  = ::GetAsyncKeyState(VK_CONTROL) & MSB;
 
  switch (key){
    //case VK_ESCAPE:
    //  KeyEscape(repeatCount);
    //  break;
    case VK_TAB:
      if(!FocusedCell.Valid())
        SetFocusCell(TCellPos(0,0));
      else{
        TCellPos pos(FocusedCell.col+1, FocusedCell.row);
        if(is_shift)
          pos.col = FocusedCell.col-1;
        if(FocusedCell != pos && pos.col >=0 && pos.col < ColCount)
          SetFocusCell(pos);
      }
      break;
    case VK_BACK:
      //if(FocusedCell.Valid()&& FocusedCell.col-1 >= 0){
      //  TCellPos pos(FocusedCell.col);
      //  pos.col--;
      //  SetFocusCell(pos);
      //}
      break;
    case VK_RIGHT:
      if(!IsFlagSet(gfRowSelect)){
        if(!FocusedCell.Valid())
          SetFocusCell(TCellPos(0,0));
        else if(FocusedCell.col+1 < ColCount){
          TCellPos pos(FocusedCell);
          pos.col++;
          if(is_ctrl)
            pos.col = ColCount - 1;
          SetFocusCell(pos);
        }
        break;
      } // if Row select -> right the same as down
    case VK_DOWN:
      if(!FocusedCell.Valid())
        SetFocusCell(TCellPos(0,0));
      else if(FocusedCell.row+1 < RowCount){
        TCellPos pos(FocusedCell);
        pos.row++;
        if(is_ctrl)
          pos.row = RowCount - 1;
        SetFocusCell(pos);
      }
      break;
    case VK_LEFT:
      if(!IsFlagSet(gfRowSelect)){
        if(!FocusedCell.Valid())
          SetFocusCell(TCellPos(0,0));
        else if(FocusedCell.col-1 >= 0){
          TCellPos pos(FocusedCell);
          pos.col--;
          if(is_ctrl)
            pos.col = 0;
          SetFocusCell(pos);
        }
        break;
      }
    case VK_UP:
      if(!FocusedCell.Valid())
        SetFocusCell(TCellPos(0,0));
      if(FocusedCell.row-1 >= 0){
        TCellPos pos(FocusedCell);
        pos.row--;
        if(is_ctrl)
          pos.row = 0;
        SetFocusCell(pos);
      }
      break;
    case VK_NEXT: //Page Down
      break;
    case VK_PRIOR://Page up
      break;
    case VK_HOME:
      if(!FocusedCell.Valid())
        SetFocusCell(TCellPos(0,0));
      else{
        TCellPos pos(0,FocusedCell.row);
        if(is_ctrl)
          pos.row = 0;
        if(FocusedCell != pos)
          SetFocusCell(pos);
      }
      break;
    case VK_END:
      if(FocusedCell.Valid()){
        TCellPos pos(ColCount-1, FocusedCell.row);
        if(is_ctrl)
          pos.row = RowCount-1;
        if(FocusedCell != pos)
          SetFocusCell(pos);
      }
      break;
    case VK_INSERT:
      break;
    case VK_DELETE:
      break;
      //Anything else is a character key - let windows have it
      //back and we will catch it in EvChar.
    default:
      DefaultProcessing();
      return; //NOTE - return not break!
  }
  //
  //Inherited::EvKeyDown(key, repeatCount, flags);//?????????????????????????????????
  // INSERT>> Your code here.
}
//
void TCoolGrid::EvChar(uint key, uint repeatCount, uint flags)
{
  if( (key != VK_RETURN && key <= VK_ESCAPE) || key == VK_TAB){
    Inherited::EvChar(key, repeatCount, flags);
    return;
  }
  bool is_ctrl  = ::GetAsyncKeyState(VK_CONTROL) & MSB;
  if(!is_ctrl)
    ShowInplaceEdit(key);
  else
    Inherited::EvChar(key, repeatCount, flags);
}
//
bool TCoolGrid::EvSetCursor(HWND hWndCursor, uint hitTest, uint mouseMsg)
{
  if (hitTest == HTCLIENT && (IsFlagSet(gfResVHeader)||IsFlagSet(gfResHHeader))){
    TPoint point;
    GetCursorPos(point);
    ScreenToClient(point);
    THitResult hitRes = HitTest(point);
    if(hitRes == hrVHDivider && IsFlagSet(gfResVHeader)){
      ::SetCursor(::LoadCursor(0, TResId(IDC_SIZENS)));
      return true;
    }
    else if(hitRes == hrHHDivider && IsFlagSet(gfResHHeader)){
      ::SetCursor(::LoadCursor(0, TResId(IDC_SIZEWE)));
      return true;
    }
  }
  return Inherited::EvSetCursor(hWndCursor, hitTest, mouseMsg);
}
//
TCoolGrid::THitResult
TCoolGrid::HitTest(const TPoint& point)
{
  TRect clientRect;
  GetClientRect(clientRect);
  if(clientRect.Contains(point)){
    if(IsVertHeader()||IsHorizHeader()){
      if(IsVertHeader()){
        if(point.x < (HeaderWidth-HitDelta)){
          if(IsHorizHeader()){
            if(point.y < (HeaderHeight-HitDelta))
              return hrBothHds;
            else if(point.y >= (HeaderHeight-HitDelta) && point.y < (HeaderHeight+HitDelta))
              return hrVHDivider;
          }
          int index = Y2Index(point.y-HitDelta);
          int pos_y = Index2Y(index) + GetRowHeight(index);
          pos_y += IsHorizHeader() ? (HeaderHeight+LineHeight) : 0;
          if(point.y >= (pos_y-HitDelta) && point.y < (pos_y+HitDelta))
            return hrVHDivider;
          return hrVHeader;
        }
      }
      if(IsHorizHeader() && point.y < (HeaderHeight-HitDelta)){
        if(IsVertHeader()){
          if(point.x >= (HeaderWidth-HitDelta) && point.x < (HeaderWidth+HitDelta))
            return hrHHDivider;
        }
        int index = X2Index(point.x-HitDelta);
        int pos_x = Index2X(index) + GetColumnWidth(index);
        pos_x += IsVertHeader() ? (HeaderWidth+LineWidth) : 0;
        if(point.x > (pos_x-HitDelta) && point.x < (pos_x+HitDelta))
          return hrHHDivider;
        return hrHHeader;
      }
    }
    return hrGridBody;
  }
  return hrNone;
}
//
void TCoolGrid::SetResetFlag(uint flag, bool status, bool redraw)
{
  if(IsFlagSet(flag) != status){
    status ? SetFlag(flag) : ClearFlag(flag);
    if(redraw && GetHandle())
      Invalidate();
  }
}
//
void TCoolGrid::EnableSelection(bool enable)
{
  if(IsFlagSet(gfSelectable) != enable){
    enable ? SetFlag(gfSelectable) : ClearFlag(gfSelectable);
    CleanupSelection();
    if(GetHandle())
      Invalidate();
  }
}
//
void TCoolGrid::EnableRowInvert(bool enable)
{
  if(IsFlagSet(gfNoRowInvert) == enable){
    enable ? ClearFlag(gfNoRowInvert) : SetFlag(gfNoRowInvert);
    if(GetHandle())
      Invalidate();
  }
}
//
void TCoolGrid::SetColumn(uint loc, TColumn& col)
{
  col.Id = loc;
  TColumnMap::iterator itr = ColumnMap.find(loc);
  if(itr != ColumnMap.end()){
    delete (*itr).second;
    (*itr).second = &col;
  }
  else
    ColumnMap[loc] = &col;
  if(GetHandle()){
    AdjustScroller();
    Invalidate();
  }
}
//
void TCoolGrid::SetGridDim(uint cols, uint rows, int col_width, int row_width)
{
  ColCount  = cols;
  RowCount  = rows;
  Width     = col_width < 0 ? Width : col_width;
  Height    = row_width < 0 ? Height : row_width;
  if(GetHandle()){
    AdjustScroller();
    Invalidate();
  }
}
//
void TCoolGrid::SetRowHeight(uint loc, uint height)
{
  TDimMap::iterator itr = HeightMap.find(loc);
  if(itr != HeightMap.end())
    (*itr).second = height;
  else
    HeightMap[loc] = height;
  if(GetHandle()){
    AdjustScroller();
    Invalidate();
  }
}
//
uint TCoolGrid::GetRowHeight(uint loc) const
{
  TDimMap::const_iterator itr = HeightMap.find(loc);
  if(itr != HeightMap.end())
    return (*itr).second;
  else
    return Height;
}
//
void TCoolGrid::SetHeaderHeight(int height)
{
  HeaderHeight = height;
  if(GetHandle()){
    AdjustScroller();
    Invalidate();
  }
}
//
void TCoolGrid::SetHeaderWidth(int width)
{
  HeaderWidth = width;
  if(GetHandle()){
    AdjustScroller();
    Invalidate();
  }
}
//
void TCoolGrid::SetColumnWidth(uint loc, uint width)
{
  TDimMap::iterator itr = WidthMap.find(loc);
  if(itr != WidthMap.end())
    (*itr).second = width;
  else
    WidthMap[loc] = width;
  if(GetHandle()){
    AdjustScroller();
    Invalidate();
  }
}
//
uint TCoolGrid::GetColumnWidth(uint loc) const
{
  TDimMap::const_iterator itr = WidthMap.find(loc);
  if(itr != WidthMap.end())
    return (*itr).second;
  else
    return Width;
}
//
void TCoolGrid::InvalidateHeaders(const TRect& rect)
{
  if(IsFlagSet(gfVertHeader)){
    TRect vrect(rect);
    vrect.left  = 0;
    vrect.right = HeaderWidth+LineWidth;
    InvalidateRect(vrect);
  }
  if(IsFlagSet(gfHorizHeader)){
    TRect hrect(rect);
    hrect.top = 0;
    hrect.bottom = HeaderHeight+LineHeight;
    InvalidateRect(hrect);
  }
}
//
void TCoolGrid::InvalidateCell(const TCellPos& pos)
{
  if(pos.Valid()){
    TRect rect;
    if(IsFlagSet(gfRowSelect)){
      rect.top  = Index2Y(pos.row);
      if(IsHorizHeader())
        rect.top += HeaderHeight+LineHeight;
      rect.bottom = rect.top + GetRowHeight(pos.row);
      rect.left   = 0;
      rect.right  = Index2X(ColCount-1)+GetColumnWidth(ColCount-1);
      if(IsFlagSet(gfVertHeader))
        rect.right  += HeaderWidth+LineWidth;
    }
    else{
      GetCellRect(pos, rect);
      InvalidateHeaders(rect);
    }
    InvalidateRect(rect.Inflate(4,4));
  }
}
//
bool TCoolGrid::SetFocusCell(const TCellPos& pos)
{
  if(pos==FocusedCell)
    return false;
 
  bool dirty = false;
  TCell* cell = GetFocusedCell();
  if(cell){
    dirty = true;
    cell->Clear(TCell::cfFocused);
  }
//  if(IsFlagSet(gfRowSelect)){
//    if(FocusedCell.row!=pos.row)
//      InvalidateCell(FocusedCell);
//  }else
    InvalidateCell(FocusedCell);
 
  FocusedCell = pos;
  cell = GetFocusedCell();
  if(cell){
    dirty = true;
    cell->Set(TCell::cfFocused);
  }
 
//  if(IsFlagSet(gfRowSelect)){
//    if(FocusedCell.row!=pos.row)
//      InvalidateCell(FocusedCell);
//  }else
    InvalidateCell(FocusedCell);
 
  return dirty;
}
//
TCoolGrid::TCell*
TCoolGrid::GetCell(const TCellPos& pos)
{
  if(pos.col >= 0 && pos.col < ColCount){
    if(pos.row >= 0 && pos.row < RowCount){
      TColumnMap::iterator itr = ColumnMap.find(pos.col);
      if(itr != ColumnMap.end()){
        TCellMap::iterator citr = (*itr).second->CellMap.find(pos.row);
        if(citr != (*itr).second->CellMap.end())
          return (*citr).second;
      }
    }
  }
  return 0;
}
//
int TCoolGrid::X2Index(int point_x) const
{
  int width = 0;
  if(IsVertHeader())
    width += HeaderWidth+LineWidth;
  if(point_x < width)
    return -1;
 
  point_x += GetOffsetPos();
  int last_index = 0;
  for(TDimMap::const_iterator itr = WidthMap.begin(); itr != WidthMap.end(); itr++){
    if((*itr).first > last_index){
      int width_d = Width + LineWidth;
      if((width + ((*itr).first-last_index)*width_d) > point_x)
        return last_index + (point_x - width)/width_d;
      width += ((*itr).first-last_index)*width_d;
      last_index = (*itr).first;
    }
    width += (*itr).second+LineWidth;
    if(width > point_x)
      return last_index;
    last_index++;
  }
  last_index += (point_x - width)/(Width+LineWidth);
  return last_index >= ColCount ? -1 : last_index;
}
//
int TCoolGrid::Y2Index(int point_y) const
{
  int height = 0;
  if(IsHorizHeader())
    height += HeaderHeight + LineHeight;
  if(point_y < height)
    return -1;
 
  point_y += GetTopPos();
  int last_index = 0;
  for(TDimMap::const_iterator itr = HeightMap.begin(); itr != HeightMap.end(); itr++){
    if((*itr).first > last_index){
      int height_d = Height + LineHeight;
      if((height + ((*itr).first-last_index)*height_d) > point_y)
        return last_index + (point_y - height)/height_d;
      height += ((*itr).first-last_index)*height_d;
      last_index = (*itr).first;
    }
    height += (*itr).second + LineHeight;
    if(height > point_y)
      return last_index;
    last_index++;
  }
  last_index += (point_y - height)/(Height + LineHeight);
  return last_index >= RowCount ? -1 : last_index;
}
//
int TCoolGrid::Index2X(int pos_x) const
{
  if(pos_x >= ColCount)
    return 0;
 
  int width = 0;
  int x_count = pos_x;
  for(TDimMap::const_iterator itr = WidthMap.begin(); itr != WidthMap.end(); itr++){
    if((*itr).first >= pos_x)
      break;
    width += (*itr).second+LineWidth;
    x_count--;
  }
  if(x_count > 0)
    width += x_count*(Width+LineWidth);
  return width-GetOffsetPos();
}
//
int TCoolGrid::Index2Y(int pos_y) const
{
  if(pos_y >= RowCount)
    return 0;
 
  int height = 0;
  int y_count = pos_y;
  for(TDimMap::const_iterator itr = HeightMap.begin(); itr != HeightMap.end(); itr++){
    if((*itr).first >= pos_y)
      break;
    height += (*itr).second+LineHeight;
    y_count--;
  }
  if(y_count > 0)
    height += y_count*(Height+LineHeight);
  return height-GetTopPos();
}
//
TCellPos
TCoolGrid::Point2Pos(const TPoint& point) const
{
  return TCellPos(X2Index(point.x), Y2Index(point.y));
}
//
struct TTransferColOp: public TColActionOperator{
  TTransferColOp(void* buff, TTransferDirection dir)
    :Data(buff),Direction(dir){}
  void operator ()(TCoolGrid::TColumn* col)
    {
      if(col->IsSet(TCoolGrid::TColumn::cfTransfer))
        Data = (char*)Data + col->Transfer(Data, Direction);
    }
  void*               Data;
  TTransferDirection  Direction;
};
//
uint TCoolGrid::Transfer(void* buffer, TTransferDirection direction)
{
  if (!buffer && direction != tdSizeData) return 0;
  TTransferColOp op(buffer, direction);
  ForEach(op);
  return static_cast<int>((char*)op.Data - (char*)buffer);
}
//
TCoolGrid::TColumn*
TCoolGrid::FirstThat(TColCondOperator& test)
{
  for(TColumnMap::iterator itr = ColumnMap.begin(); itr != ColumnMap.end(); itr++)
    if(test((*itr).second))
      return (*itr).second;
  return 0;
}
//
TCoolGrid::TColumn*
TCoolGrid::LastThat(TColCondOperator& test)
{
  TColumn* lastColumn = 0;
  for(TColumnMap::iterator itr = ColumnMap.begin(); itr != ColumnMap.end(); itr++){
    if(test((*itr).second))
      lastColumn = (*itr).second;
  }
  return lastColumn;
}
//
void TCoolGrid::ForEach(TColActionOperator& action)
{
  for(TColumnMap::iterator itr = ColumnMap.begin(); itr != ColumnMap.end(); itr++)
    action((*itr).second);
}
//
struct TConditColForOp: public TColCondOperator{
  TConditColForOp(TCellCondOperator& test):Test(test),Cell(0){}
  bool operator ()(TCoolGrid::TColumn* col)
    {
      Cell = col->FirstThat(Test);
      return Cell != 0;
    }
  TCellCondOperator&  Test;
  TCoolGrid::TCell*   Cell;
};
//
TCoolGrid::TCell*
TCoolGrid::FirstThat(TCellCondOperator& test)
{
  TConditColForOp op(test);
  return op.Cell;
}
//
struct TConditColLastOp: public TColCondOperator{
  TConditColLastOp(TCellCondOperator& test):Test(test),Cell(0){}
  bool operator ()(TCoolGrid::TColumn* col)
    {
      Cell = col->LastThat(Test);
      return Cell != 0;
    }
  TCellCondOperator&  Test;
  TCoolGrid::TCell*   Cell;
};
//
TCoolGrid::TCell*
TCoolGrid::LastThat(TCellCondOperator& test)
{
  TConditColLastOp op(test);
  LastThat(op);
  return op.Cell;
}
//
struct TActionColOp: public TColActionOperator{
  TActionColOp(TCellActionOperator& action):Action(action){}
  void operator ()(TCoolGrid::TColumn* col)
    {
      col->ForEach(Action);
    }
  TCellActionOperator& Action;
};
//
void TCoolGrid::ForEach(TCellActionOperator& action)
{
  TActionColOp op(action);
  ForEach(op);
}
//
// Respond to WM_SYSCOLORCHANGE to let the cells update their UI colors, and
// to let this grig window update its background color.
//
//
struct TSysColorChangeOp: public TCellActionOperator{
  void operator ()(TCoolGrid::TCell* cell)
    {  cell->SysColorChange(); }
};
void TCoolGrid::EvSysColorChange()
{
  TSysColorChangeOp op;
  ForEach(op);
}
//
void TCoolGrid::SetFont(const TFont& font)
{
  delete Font;
  Font  = new TFont(font);
}
//
void TCoolGrid::SetBoldFont(const TFont& font)
{
  delete BoldFont;
  BoldFont  = new TFont(font);
}
//
void TCoolGrid::SetCell(const TCellPos& pos, TCell& cell)
{
  TColumn* column = CreateColumn(pos.col);
  column->SetCell(pos.row, cell);
 
  if(pos.row >= RowCount)
    RowCount = pos.row+1;
  if(pos.col >= ColCount)
    ColCount = pos.col+1;
}
//
void TCoolGrid::RemoveCell(const TCellPos& pos)
{
  TColumnMap::iterator itr = ColumnMap.find(pos.col);
  CHECK(itr != ColumnMap.end());
  if(itr != ColumnMap.end())
    (*itr).second->RemoveCell(pos.row);
}
//
TCoolGrid::TColumn*
TCoolGrid::CreateColumn(int loc)
{
  TColumnMap::iterator itr = ColumnMap.find(loc);
  if(itr == ColumnMap.end()){
    TColumn* col = (ColBuilder)(*this, loc);
    col->Id = loc;
    itr = ColumnMap.insert(TColumnMap::value_type(loc, col)).first;
  }
  return (*itr).second;
}
//
void TCoolGrid::SetInPlaceEdit(TInPlaceEdit* edit)
{
  delete InPlaceEdit;
  InPlaceEdit = edit;
}
//
void TCoolGrid::GetCellRect(const TCellPos& pos, TRect& rect) const
{
  rect.top    = Index2Y(pos.row);
  if(IsHorizHeader())
    rect.top += HeaderHeight+LineHeight;
  rect.bottom = rect.top + GetRowHeight(pos.row);
  rect.left   = Index2X(pos.col);
  if(IsVertHeader())
    rect.left += HeaderWidth+LineWidth;
  rect.right  = rect.left + GetColumnWidth(pos.col);
}
//
void  TCoolGrid::RemoveColumn(uint loc)
{
  TColumnMap::iterator itr = ColumnMap.find(loc);
  if(itr != ColumnMap.end()){
    delete (*itr).second;
    ColumnMap.erase(itr);
  }
}
//
TCoolGrid::TColumn*
TCoolGrid::GetColumn(int loc)
{
  TColumnMap::iterator itr = ColumnMap.find(loc);
  if(itr != ColumnMap.end())
    return (*itr).second;
  return 0;
}
//
void  TCoolGrid::SetColumnEdit(int column, TInPlaceEdit* edit)
{
  CreateColumn(column)->SetInPlaceEdit(edit);
}
//
TCoolGrid::TInPlaceEdit*
TCoolGrid::GetColumnEdit(int column)
{
  TColumn* col = GetColumn(column);
  if(col)
    return col->GetInPlaceEdit();
  return 0;
}
//
int TCoolGrid::FirstVisibleRow()
{
  //int index = X2Index(Scroller->YPos);
  int index = Y2Index(1);
  if(index==-1)
    index = 0;
  return index;
}
//
int TCoolGrid::FirstVisibleColumn()
{
  //int index = X2Index(Scroller->XPos);
  int index = X2Index(1);
  if(index==-1)
    index = 0;
  return index;
}
//
void TCoolGrid::PaintGridLines(TDC& dc, const TRect& rect)
{
  dc.SelectObject(TUIFace::GetDitherBrush());
  PaintRowLines(dc, rect);
  PaintColLines(dc, rect);
  dc.RestoreBrush();
}
//
static uint raster_op = PATCOPY; // PATINVERT
//
void TCoolGrid::PaintRowLines(TDC& dc, const TRect& rect)
{
  uint width = rect.Width();
  int headerDelta = 0;
  int y = -GetTopPos();
  if(IsHorizHeader())
    headerDelta = HeaderHeight+LineHeight;
  y += headerDelta;
  int last_index = 0;
  for(TDimMap::iterator itr = HeightMap.begin(); itr != HeightMap.end(); itr++){
    int index = (*itr).first;
    while(last_index < index){
      y += Height+LineHeight;
      if(y > headerDelta)
        VERIFY(dc.PatBlt(rect.left, y, width, LineHeight, raster_op));
      last_index++;
    }
    y += (*itr).second+LineHeight;
    if(y > headerDelta)
      VERIFY(dc.PatBlt(rect.left, y-1, width, LineHeight, raster_op));
    last_index++;
  }
  for(;last_index < RowCount; last_index++){
    y += Height+LineHeight;
    if(y > headerDelta)
      VERIFY(dc.PatBlt(rect.left, y-1, width, LineHeight, raster_op));
  }
}
//
void TCoolGrid::PaintColLines(TDC& dc, const TRect& rect)
{
  int height = Index2Y(RowCount-1) + GetRowHeight(RowCount-1);
  int delta = 0;
  if(IsHorizHeader())
    delta += HeaderHeight+LineHeight;
  VERIFY(dc.PatBlt(rect.right, rect.top+delta, LineWidth, height-rect.top, raster_op));
}
//
void TCoolGrid::PaintFocusedCell(TDC& dc, const TRect& rect)
{
  // paint on DC, not on MemDC, why, maby better would be on MemDC ???
  if(IsFlagSet(gfRowSelect)){
    // paint full row
    TRect rowRect(rect);
    rowRect.left  = 0;//GetOffsetPos();
    rowRect.right = Index2X(ColCount-1)+GetColumnWidth(ColCount-1);
    if(IsFlagSet(gfVertHeader))
      rowRect.right += HeaderWidth + LineWidth;
    if(IsFlagSet(gfNoRowInvert)){
      rowRect.Inflate(1,1);
      TUIBorder(rowRect, TUIBorder::ButtonDn, TUIBorder::Rect|TUIBorder::Soft).Paint(dc);
    }
    else
      VERIFY(dc.InvertRect(rowRect));
  }
  else{
    TBrush shbr(TColor::Sys3dDkShadow);
    VERIFY(dc.FrameRect(rect.InflatedBy(-1, -1), shbr));
    //VERIFY(dc.FrameRect(rect.InflatedBy(-1, -1), TUIFace::GetDitherBrush()));
  }
}
//
void TCoolGrid::PaintCornerCell(TDC& dc, const TRect& rect)
{
  TColor oldClr = dc.SetBkColor(TColor::Sys3dFace);
  VERIFY(dc.TextRect(rect));
  TUIBorder(rect,TUIBorder::Embossed,TUIBorder::Soft).Paint(dc);
  dc.SetBkColor(oldClr);
}
//
void TCoolGrid::PaintDefaultColumn(TDC& dc, const TRect& rect, int index)
{
  TRect colmnRect(rect);
  int headerDelta = 0;
  if(IsFlagSet(gfHorizHeader)){
    TRect headerRect(rect);
    headerDelta = HeaderHeight + LineHeight;
    headerRect.bottom = headerRect.top + headerDelta;
    headerRect.right += LineWidth;
    PaintHorizHeaderCell(dc, headerRect, index);
    colmnRect.top += headerDelta;
  }
  colmnRect.bottom = Index2Y(RowCount-1) + GetRowHeight(RowCount-1) + headerDelta;
 
  TColor oldClr = dc.SetBkColor(DefColor);
  dc.TextRect(colmnRect);
 
  // fill rest area
  colmnRect.top     = colmnRect.bottom;
  colmnRect.bottom  = rect.bottom+1;
  colmnRect.right++;
  dc.SetBkColor(TColor::SysAppWorkspace);
  dc.TextRect(colmnRect);
 
  dc.SetBkColor(oldClr);
}
//
void TCoolGrid::PaintHorizHeaderCell(TDC& dc, const TRect& rect, int index)
{
  TColor oldClr = dc.SetBkColor(TColor::Sys3dFace);
  VERIFY(dc.TextRect(rect));
 
  TUIBorder::TStyle style = TUIBorder::Raised;
  TFont* font = GetFont();
  if(FocusedCell.Valid() && FocusedCell.col == index && !IsFlagSet(gfRowSelect)){
    style = TUIBorder::ButtonUp;
    font = GetBoldFont();
  }
  TUIBorder(rect,style,TUIBorder::Rect|TUIBorder::Soft).Paint(dc);
 
  if(font){
    CHECK(font->IsGDIObject());
    dc.SelectObject(*font);
  }
  TRect textRect(rect);
  textRect.Inflate(-2,-2);
  _TCHAR buffer[80];
  wsprintf(buffer, DefHorFormat.c_str(), index);
  VERIFY(dc.DrawTextEx(buffer, -1, &textRect, DT_CENTER|DT_SINGLELINE|DT_VCENTER|DT_END_ELLIPSIS));
 
  if(font)
    dc.RestoreFont();
  dc.SetBkColor(oldClr);
}
//
void TCoolGrid::PaintVertHeaderCell(TDC& dc, const TRect& rect, int index)
{
  THeaderCell* cell = 0;
 
  THeaderMap::iterator itr = VertTitles.find(index);
  if(itr != VertTitles.end())
    cell = (THeaderCell*)(*itr).second;
 
  TColor oldBkClr = dc.SetBkColor(cell ? cell->GetBgColor() : TColor::Sys3dFace);
  TColor oldTxClr = dc.SetTextColor(cell ? cell->GetTxColor() : TColor::SysWindowText);
 
  VERIFY(dc.TextRect(rect));
 
  TUIBorder::TStyle style = TUIBorder::Raised;
 
  TFont* font = GetFont();
  if(FocusedCell.Valid() && FocusedCell.row == index){
    style = TUIBorder::ButtonUp;
    font = GetBoldFont();
  }
  if(font){
    CHECK(font->IsGDIObject());
    dc.SelectObject(*font);
  }
 
  TUIBorder(rect, style, TUIBorder::Rect|TUIBorder::Soft).Paint(dc);
 
  TRect textRect(rect);
  textRect.Inflate(-2,-2);
  textRect.top++;
 
  if(cell)
    cell->Paint(dc, textRect);
  else{
    _TCHAR buffer[80];
    wsprintf(buffer, DefVertFormat.c_str(), index);
    VERIFY(dc.DrawTextEx(buffer, -1, &textRect, DT_CENTER|DT_SINGLELINE|DT_VCENTER|DT_END_ELLIPSIS));
  }
  if(font)
    dc.RestoreFont();
 
  dc.SetBkColor(oldBkClr);
  dc.SetTextColor(oldTxClr);
}
//
void TCoolGrid::SetDefVertFormat(const owl::tstring& format)
{
  DefVertFormat = format;
  if(GetHandle())
    Invalidate();
}
//
void TCoolGrid::SetDefHorFormat(const owl::tstring& format)
{
  DefHorFormat = format;
  if(GetHandle())
    Invalidate();
}
//
void TCoolGrid::SetHorTitle(uint column, const owl::tstring& text)
{
  CreateColumn(column)->SetHeader(text);
  if(GetHandle())
    Invalidate();
}
//
void TCoolGrid::SetVertTitle(uint row, const owl::tstring& text)
{
  THeaderMap::iterator itr = VertTitles.find(row);
  if(itr != VertTitles.end())
    ((THeaderCell*)(*itr).second)->SetText(text.c_str());
  else{
    THeaderCell* cell = new THeaderCell(text.c_str());
    cell->SetParent(this);
    VertTitles[row] = cell;
  }
  if(GetHandle())
    Invalidate();
}
//
void TCoolGrid::SetVertTitle(uint row, THeaderCell& cell)
{
  THeaderMap::iterator itr = VertTitles.find(row);
  if(itr != VertTitles.end()){
    delete (*itr).second;
    (*itr).second = &cell;
  }
  else
    VertTitles[row] = &cell;
  cell.SetParent(this);
  if(GetHandle())
    Invalidate();
}
//
void TCoolGrid::PaintHeaderColumn(TDC& dc, const TRect& rect)
{
  int last_index = 0;
  TRect workRect(rect);
  int topPos = GetTopPos();
 
  workRect.top -= topPos;
  if(IsHorizHeader())
    workRect.top += HeaderHeight + LineHeight;
 
  for(TCellMap::iterator itr = VertTitles.begin(); itr != VertTitles.end(); itr++){
    if((*itr).first >= last_index){
      while(last_index < (*itr).first-1){
        int height = GetRowHeight(last_index);
        if(0 < Index2Y(last_index) + height){
          workRect.bottom = workRect.top + height + LineHeight;
          PaintVertHeaderCell(dc, workRect, last_index);
        }
        workRect.top += height + LineHeight;
        last_index++;
      }
      int height = GetRowHeight(last_index);
      if(0 < Index2Y(last_index)+height){
        workRect.bottom = workRect.top + height + LineHeight;
        PaintVertHeaderCell(dc, workRect, last_index);
      }
      workRect.top += height + LineHeight;
      last_index++;
    }
  }
  for(;last_index < RowCount; last_index++){
    int height = GetRowHeight(last_index);
    if(0 < Index2Y(last_index)+height){
      workRect.bottom = workRect.top + height + LineHeight;
      PaintVertHeaderCell(dc, workRect,last_index);
    }
    workRect.top += height + LineHeight;
  }
 
  TColor oldClr = dc.SetBkColor(TColor::SysAppWorkspace);
  workRect.bottom = rect.bottom + 1;
  workRect.right++;
  dc.TextRect(workRect);
  dc.SetBkColor(oldClr);
}
//
// Cells always get notified when a left button down occurs within their
// bounding rectangle. If you want mouse drags and a mouse up then you
// need to capture the mouse
//
// Fails if already captured
bool TCoolGrid::CellSetCapture(TCell& cell)
{
  if (Capture)
    return false;
  else {
    Capture = &cell;
    SetCapture();
    return true;
  }
}
//
void TCoolGrid::CellReleaseCapture(TCell& cell)
{
  if (&cell == Capture) {
    Capture = 0;
    ReleaseCapture();
  }
}
//

V595 The 'Window' pointer was utilized before it was verified against nullptr. Check lines: 1370, 1372.

V595 The 'Window' pointer was utilized before it was verified against nullptr. Check lines: 1394, 1396.

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: Id.

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: MinDividerIndex.

V1004 The 'grid' pointer was used unsafely after it was verified against nullptr. Check lines: 216, 217.

V1004 The 'grid' pointer was used unsafely after it was verified against nullptr. Check lines: 291, 292.

V1004 The 'grid' pointer was used unsafely after it was verified against nullptr. Check lines: 491, 492.

V1004 The 'cell' pointer was used unsafely after it was verified against nullptr. Check lines: 1122, 1123.

V1004 The 'col' pointer was used unsafely after it was verified against nullptr. Check lines: 1941, 1942.

V522 There might be dereferencing of a potential null pointer 'wnd'.

V560 A part of conditional expression is always false: key == 0x09.

V560 A part of conditional expression is always true: point.y >= (HeaderHeight - HitDelta).

V773 The 'BoldFont' pointer was not released in destructor. A memory leak is possible.

V1027 Pointer to an object of the 'TRect' class is cast to unrelated 'TPoint' class.

V575 The potential null pointer is passed into 'strcpy' function. Inspect the first argument.

V796 It is possible that 'break' statement is missing in switch statement.

V796 It is possible that 'break' statement is missing in switch statement.

V803 Decreased performance. In case 'itr' is iterator it's more effective to use prefix form of increment. Replace iterator++ with ++iterator.

V803 Decreased performance. In case 'itr' is iterator it's more effective to use prefix form of increment. Replace iterator++ with ++iterator.

V803 Decreased performance. In case 'itr' is iterator it's more effective to use prefix form of increment. Replace iterator++ with ++iterator.

V803 Decreased performance. In case 'itr' is iterator it's more effective to use prefix form of increment. Replace iterator++ with ++iterator.

V803 Decreased performance. In case 'itr' is iterator it's more effective to use prefix form of increment. Replace iterator++ with ++iterator.

V803 Decreased performance. In case 'itr' is iterator it's more effective to use prefix form of increment. Replace iterator++ with ++iterator.

V803 Decreased performance. In case 'itr' is iterator it's more effective to use prefix form of increment. Replace iterator++ with ++iterator.

V803 Decreased performance. In case 'itr' is iterator it's more effective to use prefix form of increment. Replace iterator++ with ++iterator.

V803 Decreased performance. In case 'itr' is iterator it's more effective to use prefix form of increment. Replace iterator++ with ++iterator.

V803 Decreased performance. In case 'itr' is iterator it's more effective to use prefix form of increment. Replace iterator++ with ++iterator.

V803 Decreased performance. In case 'itr' is iterator it's more effective to use prefix form of increment. Replace iterator++ with ++iterator.

V803 Decreased performance. In case 'itr' is iterator it's more effective to use prefix form of increment. Replace iterator++ with ++iterator.

V803 Decreased performance. In case 'itr' is iterator it's more effective to use prefix form of increment. Replace iterator++ with ++iterator.

V803 Decreased performance. In case 'itr' is iterator it's more effective to use prefix form of increment. Replace iterator++ with ++iterator.

V803 Decreased performance. In case 'itr' is iterator it's more effective to use prefix form of increment. Replace iterator++ with ++iterator.

V803 Decreased performance. In case 'itr' is iterator it's more effective to use prefix form of increment. Replace iterator++ with ++iterator.