// ****************************************************************************
// Copyright (C) 1998 by Dieter Windau
// All rights reserved
//
// colpick.cpp:  implementation file
// Version:      1.5
// Date:         08.11.1998
// Author:       Dieter Windau
//
// Freeware OWL classes that extents the dockable and gadget system
//
// TColorPicker is based on the JPColorPicker class written by Jo Parrello
//
// You are free to use/modify this code but leave this header intact.
// May not be sold for profit.
//
// Tested with Borland C++ 5.02, OWL 5.02, OWL6 patch #3 and with Windows
// NT 4.0 SP3 but I think the class should work with Windows 95 too.
// This file is provided "as is" with no expressed or implied warranty.
// Use at your own risk.
//
// This package contains many classes and source that are based on other OWL
// developers work. Very special thanks to Alan Chambers, Christopher Kohlhoff,
// Jo Parrello, Mark Hatsell, Michael Mogensen and Yura Bidus
//
// Please send me bug reports, bug fixes, enhancements, requests, etc., and
// I'll try to keep this in next versions:
//   EMail: dieter.windau@usa.net
//   Web:   http://members.aol.com/softengage/index.htm
//
// ****************************************************************************
 
#include <owlext\pch.h>
#pragma hdrstop
 
#include <owl/tooltip.h>
#include <owl/gadgetwi.h>
#include <owl/uihelper.h>
#include <owl/chooseco.h>
 
#include <owlext/util.h>
#include <owlext/harborex.h>
#include <owlext/dockingex.h>
#include <owlext/colpick.h>
 
using namespace owl;
using namespace std;
 
namespace OwlExt {
 
// ******************** TColorPickerData **************************************
 
COLORREF TColorPickerData::ColorTable8[8] =
{
  RGB(0x00, 0x00, 0x00),
  RGB(0x7F, 0x7F, 0x7F),
  RGB(0x00, 0x00, 0xFF),
  RGB(0xFF, 0x00, 0x00),
  RGB(0xFF, 0xFF, 0x00),
  RGB(0x00, 0xFF, 0x00),
  RGB(0xC0, 0xC0, 0xC0),
  RGB(0xFF, 0xFF, 0xFF)
};
 
COLORREF TColorPickerData::ColorTable16[16] =
{
  RGB(0x00, 0x00, 0x00),
  RGB(0x7F, 0x7F, 0x7F),
  RGB(0x8B, 0x00, 0x00),
  RGB(0x8B, 0x8B, 0x00),
  RGB(0x00, 0x8B, 0x00),
  RGB(0x00, 0x8B, 0x8B),
  RGB(0x00, 0x00, 0x8B),
  RGB(0x80, 0x00, 0x80),
  RGB(0xFF, 0xFF, 0xFF),
  RGB(0xC0, 0xC0, 0xC0),
  RGB(0xFF, 0x00, 0x00),
  RGB(0xFF, 0xFF, 0x00),
  RGB(0x00, 0xFF, 0x00),
  RGB(0x00, 0xFF, 0xFF),
  RGB(0x00, 0x00, 0xFF),
  RGB(0xFF, 0x00, 0xFF)
};
 
COLORREF TColorPickerData::ColorTable40[40] =
{
  RGB(0x00, 0x00, 0x00),
  RGB(0xA5, 0x2A, 0x00),
  RGB(0x00, 0x40, 0x40),
  RGB(0x00, 0x55, 0x00),
  RGB(0x00, 0x00, 0x5E),
  RGB(0x00, 0x00, 0x8B),
  RGB(0x4B, 0x00, 0x82),
  RGB(0x28, 0x28, 0x28),
  RGB(0x8B, 0x00, 0x00),
  RGB(0xFF, 0x68, 0x20),
  RGB(0x8B, 0x8B, 0x00),
  RGB(0x00, 0x93, 0x00),
  RGB(0x38, 0x8E, 0x8E),
  RGB(0x00, 0x00, 0xFF),
  RGB(0x7B, 0x7B, 0xC0),
  RGB(0x66, 0x66, 0x66),
  RGB(0xFF, 0x00, 0x00),
  RGB(0xFF, 0xAD, 0x5B),
  RGB(0x32, 0xCD, 0x32),
  RGB(0x3C, 0xB3, 0x71),
  RGB(0x7F, 0xFF, 0xD4),
  RGB(0x7D, 0x9E, 0xC0),
  RGB(0x80, 0x00, 0x80),
  RGB(0x7F, 0x7F, 0x7F),
  RGB(0xFF, 0xC0, 0xCB),
  RGB(0xFF, 0xD7, 0x00),
  RGB(0xFF, 0xFF, 0x00),
  RGB(0x00, 0xFF, 0x00),
  RGB(0x40, 0xE0, 0xD0),
  RGB(0xC0, 0xFF, 0xFF),
  RGB(0x48, 0x00, 0x48),
  RGB(0xC0, 0xC0, 0xC0),
  RGB(0xFF, 0xE4, 0xE1),
  RGB(0xD2, 0xB4, 0x8C),
  RGB(0xFF, 0xFF, 0xE0),
  RGB(0x98, 0xFB, 0x98),
  RGB(0xAF, 0xEE, 0xEE),
  RGB(0x68, 0x83, 0x8B),
  RGB(0xE6, 0xE6, 0xFA),
  RGB(0xFF, 0xFF, 0xFF)
};
 
TColorPickerData::TColorPickerData(
                   COLORREF colors[],
                   uint     numColors,
                   uint     numColumn,
                   bool     autoParentNotify,
                   bool     usePalette,
                   bool     showDefaultField,
                   uint     defaultFieldResId,
                   COLORREF defaultFieldColor,
                   bool     showCustomField,
                   uint     customFieldResId)
{
  NumColors = numColors;
  Colors = new TColor[NumColors];
  for (uint i=0; i<NumColors; i++)
    Colors[i] = colors[i];
  NumColumn = numColumn;
  NumColumn = std::min(NumColumn, NumColors);
  AutoParentNotify = autoParentNotify;
  UsePalette = usePalette;
  ShowDefaultField = showDefaultField;
  DefaultFieldResId = defaultFieldResId;
  DefaultFieldColor = defaultFieldColor;
  ShowCustomField = showCustomField;
  CustomFieldResId = customFieldResId;
}
 
TColorPickerData::~TColorPickerData()
{
  delete[] Colors;
}
 
TColorPickerData& TColorPickerData::operator =(const TColorPickerData& d)
{
  NumColors = d.NumColors;
  Colors = new TColor[NumColors];
  for (uint i=0; i<NumColors; i++)
    Colors[i] = d.Colors[i];
  NumColumn = d.NumColumn;
  NumColumn = std::min(NumColumn, NumColors);
  AutoParentNotify = d.AutoParentNotify;
  UsePalette = d.UsePalette;
  ShowDefaultField = d.ShowDefaultField;
  DefaultFieldResId = d.DefaultFieldResId;
  DefaultFieldColor = d.DefaultFieldColor;
  ShowCustomField = d.ShowCustomField;
  CustomFieldResId = d.CustomFieldResId;
  return *this;
}
 
bool TColorPickerData::operator ==(const TColorPickerData& d) const
{
  if (NumColors == d.NumColors &&
    NumColumn == d.NumColumn &&
    AutoParentNotify == d.AutoParentNotify &&
    UsePalette == d.UsePalette &&
    ShowDefaultField == d.ShowDefaultField &&
    DefaultFieldResId == d.DefaultFieldResId &&
    DefaultFieldColor == d.DefaultFieldColor &&
    ShowCustomField == d.ShowCustomField &&
    CustomFieldResId == d.CustomFieldResId) {
      for (uint i=0; i<NumColors; i++) {
        if (Colors[i] != d.Colors[i])
          return false;
      }
      return true;
  }
  return false;
}
 
bool TColorPickerData::operator !=(const TColorPickerData& d) const
{
  return (*this == d) ? false : true;
}
 
// ******************** TColorPicker ******************************************
 
#define DEFAULT_BOX_VALUE  -3
#define CUSTOM_BOX_VALUE   -2
#define INVALID_COLOR      -1
 
#define FLATUPSTATE         1 // paint recessed
#define FLATNORMALSTATE     2 // paint without border
#define FLATDOWNSTATE       3 // paint pressed
#define FLATDOWNSEL         4 // paint pressed (not mask rect)
 
ColorTableEntry TColorPicker::MapColorTable[NumMapColors] =
{
  { RGB(0x00, 0x00, 0x00), IDS_COLORPICKER + 0 },
  { RGB(0xA5, 0x2A, 0x00), IDS_COLORPICKER + 1 },
  { RGB(0x00, 0x40, 0x40), IDS_COLORPICKER + 2 },
  { RGB(0x00, 0x55, 0x00), IDS_COLORPICKER + 3 },
  { RGB(0x00, 0x00, 0x5E), IDS_COLORPICKER + 4 },
  { RGB(0x00, 0x00, 0x8B), IDS_COLORPICKER + 5 },
  { RGB(0x4B, 0x00, 0x82), IDS_COLORPICKER + 6 },
  { RGB(0x28, 0x28, 0x28), IDS_COLORPICKER + 7 },
  { RGB(0x8B, 0x00, 0x00), IDS_COLORPICKER + 8 },
  { RGB(0xFF, 0x68, 0x20), IDS_COLORPICKER + 9 },
  { RGB(0x8B, 0x8B, 0x00), IDS_COLORPICKER + 10 },
  { RGB(0x00, 0x93, 0x00), IDS_COLORPICKER + 11 },
  { RGB(0x38, 0x8E, 0x8E), IDS_COLORPICKER + 12 },
  { RGB(0x00, 0x00, 0xFF), IDS_COLORPICKER + 13 },
  { RGB(0x7B, 0x7B, 0xC0), IDS_COLORPICKER + 14 },
  { RGB(0x66, 0x66, 0x66), IDS_COLORPICKER + 15 },
  { RGB(0xFF, 0x00, 0x00), IDS_COLORPICKER + 16 },
  { RGB(0xFF, 0xAD, 0x5B), IDS_COLORPICKER + 17 },
  { RGB(0x32, 0xCD, 0x32), IDS_COLORPICKER + 18 },
  { RGB(0x3C, 0xB3, 0x71), IDS_COLORPICKER + 19 },
  { RGB(0x7F, 0xFF, 0xD4), IDS_COLORPICKER + 20 },
  { RGB(0x7D, 0x9E, 0xC0), IDS_COLORPICKER + 21 },
  { RGB(0x80, 0x00, 0x80), IDS_COLORPICKER + 22 },
  { RGB(0x7F, 0x7F, 0x7F), IDS_COLORPICKER + 23 },
  { RGB(0xFF, 0xC0, 0xCB), IDS_COLORPICKER + 24 },
  { RGB(0xFF, 0xD7, 0x00), IDS_COLORPICKER + 25 },
  { RGB(0xFF, 0xFF, 0x00), IDS_COLORPICKER + 26 },
  { RGB(0x00, 0xFF, 0x00), IDS_COLORPICKER + 27 },
  { RGB(0x40, 0xE0, 0xD0), IDS_COLORPICKER + 28 },
  { RGB(0xC0, 0xFF, 0xFF), IDS_COLORPICKER + 29 },
  { RGB(0x48, 0x00, 0x48), IDS_COLORPICKER + 30 },
  { RGB(0xC0, 0xC0, 0xC0), IDS_COLORPICKER + 31 },
  { RGB(0xFF, 0xE4, 0xE1), IDS_COLORPICKER + 32 },
  { RGB(0xD2, 0xB4, 0x8C), IDS_COLORPICKER + 33 },
  { RGB(0xFF, 0xFF, 0xE0), IDS_COLORPICKER + 34 },
  { RGB(0x98, 0xFB, 0x98), IDS_COLORPICKER + 35 },
  { RGB(0xAF, 0xEE, 0xEE), IDS_COLORPICKER + 36 },
  { RGB(0x68, 0x83, 0x8B), IDS_COLORPICKER + 37 },
  { RGB(0xE6, 0xE6, 0xFA), IDS_COLORPICKER + 38 },
  { RGB(0xFF, 0xFF, 0xFF), IDS_COLORPICKER + 39 },
  { RGB(0x00, 0x8B, 0x00), IDS_COLORPICKER + 40 },
  { RGB(0x00, 0x8B, 0x8B), IDS_COLORPICKER + 41 },
  { RGB(0xFF, 0x00, 0xFF), IDS_COLORPICKER + 42 }
};
 
COLORREF TColorPicker::CustColors[NumCustColors] =
{
  0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000,
  0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000,
  0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000,
  0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000
};
 
uint TColorPicker::ColorPickerMessage = 0;
 
//
// Build a response table for all messages/commands handled by the application.
//
DEFINE_RESPONSE_TABLE1(TColorPicker, TWindow)
EV_WM_ACTIVATEAPP,
EV_WM_KILLFOCUS,
EV_WM_MOUSEMOVE,
EV_WM_LBUTTONDOWN,
EV_WM_PALETTECHANGED,
EV_WM_QUERYNEWPALETTE,
EV_WM_KEYDOWN,
END_RESPONSE_TABLE;
 
TColorPicker::TColorPicker(TWindow* parent,
               const TColorPickerData& data,
               TColor   startColor,
               int      refId,
               LPCTSTR  title,
               TModule* module):
TWindow(parent, title, module),
Data(data)
{
  RefId = refId;
  CurrentChosenColor = startColor;
  CurrentCustomColor = Data.DefaultFieldColor;
  ColorPickerMessage = ::RegisterWindowMessage(TColorPickerMessage);
  PickerTooltip = 0;
  ColorDialogOpened = false;
  PaletteRealized = false;
  BoxMargin = ::GetSystemMetrics(SM_CXBORDER) + 5;
  Attr.Style = WS_POPUP;
  Attr.ExStyle |= WS_EX_WINDOWEDGE;
  Attr.ExStyle |= WS_EX_TOPMOST;
  CurrentSel = INVALID_COLOR;
  ChosenColorSel = GetIndexByColor(CurrentChosenColor);
  NumRows = Data.NumColors / Data.NumColumn;
  if (Data.NumColors % Data.NumColumn)
    NumRows++;
 
  if (Data.UsePalette == false)
    MyLogPalette = 0;
  else {
    MyLogPalette = (LOGPALETTE*)new char[sizeof(LOGPALETTE) +
      sizeof(PALETTEENTRY) * Data.NumColors];
    MyLogPalette->palVersion = 0x300;
    MyLogPalette->palNumEntries = (WORD) Data.NumColors;
    for (uint i=0; i<Data.NumColors; ++i) {
      TColor color (Data.Colors[i]);
      MyLogPalette->palPalEntry[i].peRed   = color.Red();
      MyLogPalette->palPalEntry[i].peGreen = color.Green();
      MyLogPalette->palPalEntry[i].peBlue  = color.Blue();
      MyLogPalette->palPalEntry[i].peFlags = PC_RESERVED;
    }
  }
 
  WindowSize = TSize(Data.NumColumn*BoxSize + 2*BoxMargin,
    NumRows*BoxSize + 2*BoxMargin);
  if (Data.ShowCustomField)
    WindowSize.cy += CustomHeight+DistToCustom;;
  if (Data.ShowDefaultField)
    WindowSize.cy += (DefaultHeight+DistToColor);
  SetCurrentChosenColor(startColor);
}
 
TColorPicker::~TColorPicker()
{
  Destroy(IDCANCEL);
  delete[] MyLogPalette;
  if (PickerTooltip)
    delete PickerTooltip;
}
 
// Another application has the focus, so we hide the picker window.
void TColorPicker::EvActivateApp(bool active, DWORD hTask)
{
  TWindow::EvActivateApp(active, hTask);
  if (!active && !ColorDialogOpened)
    ExitPicker();
}
 
// The picker window has lost the focus, so we hide it.
//
void TColorPicker::EvKillFocus(THandle hWndGetFocus )
{
  TWindow::EvKillFocus(hWndGetFocus);
  ReleaseCapture();
}
 
// Hide the picker window and release the mouse capture.
//
void TColorPicker::ExitPicker()
{
  ReleaseCapture();
  ShowWindow(SW_HIDE);
}
 
// Retrieve the name of this window's class.
//
auto TColorPicker::GetWindowClassName() -> TWindowClassName
{
  return TWindowClassName{_T("TColorPickerWindow")};
}
 
// Retrieve the window class informations.
//
void TColorPicker::GetWindowClass(WNDCLASS& wndClass)
{
  TWindow::GetWindowClass(wndClass);
  wndClass.style = CS_CLASSDC | CS_HREDRAW | CS_VREDRAW | CS_SAVEBITS;
}
 
// We paint all the cells.
//
void TColorPicker::Paint(TDC& dc, bool erase, TRect& rect)
{
  TWindow::Paint(dc, erase, rect);
 
  TUIBorder border(GetClientRect(), TUIBorder::WndRaised);
  border.Paint(dc);
 
  DrawCell(dc, DEFAULT_BOX_VALUE);
  DrawCell(dc, CUSTOM_BOX_VALUE);
  if (MyLogPalette && dc.GetDeviceCaps(RASTERCAPS) & RC_PALETTE) {
    TPalette palette(*MyLogPalette);
    dc.SelectObject(palette);
    PaletteRealized = (dc.RealizePalette() > 0);
  }
  for (uint i=0; i<Data.NumColors; i++)
    DrawCell(dc, i);
  dc.RestoreObjects();
}
 
// We create the tooltip control and we send all data to it.
//
void TColorPicker::SetupWindow()
{
  TRect rect;
  owl::tstring toolstring;
 
  TWindow::SetupWindow();
  SetBkgndColor(TColor::Sys3dFace);
  SetWindowFont((HFONT)GetStockObject(ANSI_VAR_FONT), true);
  PickerTooltip = new TTooltip(this);
  PickerTooltip->Create();
 
  if (Data.ShowDefaultField) {
    DefaultText = GetModule()->LoadString(Data.DefaultFieldResId);
    toolstring =  GetModule()->LoadString(Data.DefaultFieldResId+2);
    if (toolstring.empty())
      toolstring = DefaultText;
    GetCellRect(DEFAULT_BOX_VALUE, rect);
    PickerTooltip->AddTool({*this, 1}, rect, toolstring);
  }
  else
    DefaultText = _T("");
 
  if (Data.ShowCustomField) {
    CustomText = GetModule()->LoadString(Data.CustomFieldResId);
    toolstring =  GetModule()->LoadString(Data.CustomFieldResId+2);
    if (toolstring.empty())
      toolstring = CustomText;
    GetCellRect(CUSTOM_BOX_VALUE, rect);
    PickerTooltip->AddTool({*this, 1}, rect, toolstring);
  }
  else
    CustomText = _T("");
 
  for (uint i=0; i<Data.NumColors; i++) {
    for (uint j=0; j<NumMapColors; j++) {
      if (Data.Colors[i] == MapColorTable[j].Color) {
        toolstring = GetModule()->LoadString(MapColorTable[j].ResId);
        break;
      }
    }
 
    if (!toolstring.empty()){
      GetCellRect(i, rect);
      PickerTooltip->AddTool({*this, 1}, rect, toolstring);
    }
  }
}
 
// We must show the picker window at the given coordinates.
//
void TColorPicker::ShowPickerWindow(TPoint& pt, TRect& rect)
{
  CurrentSel = INVALID_COLOR;
  ColorDialogOpened = false;
  TSize size = WindowSize;
  TSize checksize (size.cx + 1, size.cy + 1);
  TPoint point (pt);
  TSize ScreenSize(::GetSystemMetrics(SM_CXSCREEN),
    ::GetSystemMetrics(SM_CYSCREEN));
 
  // Check the vertical position.
  //
  if (point.y + size.cy > ScreenSize.cy) {
    if ((rect.top >= 0) && (rect.bottom >= 0) && (rect.top - checksize.cy >= 0))
      point.y = rect.top - checksize.cy;
    else point.y = ScreenSize.cy - checksize.cy;
  }
 
  // Check the horizontal position.
  //
  if (point.x + checksize.cx > ScreenSize.cx)
    point.x = ScreenSize.cx - checksize.cx;
  SetWindowPos(HWND_TOPMOST, point.x, point.y, size.cx, size.cy, SWP_SHOWWINDOW);
  SetCapture();
}
 
// We retrieve the rectangle for a single cell.
//
void TColorPicker::GetCellRect(int index, TRect& rect)
{
  TRect clientrect = GetClientRect();
  if (index == DEFAULT_BOX_VALUE) {
    if (Data.ShowDefaultField) {
      rect = TRect(BoxMargin, BoxMargin,
        WindowSize.cx-BoxMargin, BoxMargin+DefaultHeight);
    }
    else {
      rect = TRect(0,0,0,0);
    }
  }
  else if (index == CUSTOM_BOX_VALUE) {
    if (Data.ShowCustomField) {
      rect = TRect(BoxMargin, WindowSize.cy-BoxMargin-CustomHeight,
        WindowSize.cx - BoxMargin, WindowSize.cy - BoxMargin);
    }
    else {
      rect = TRect(0,0,0,0);
    }
  }
  else if ((index >= 0) && (index < (int)Data.NumColors)) {
    TPoint pt;
    pt.x = (index - ((index/Data.NumColumn)*Data.NumColumn))*BoxSize+BoxMargin;
    pt.y = (index/Data.NumColumn) * BoxSize + BoxMargin;
    if (Data.ShowDefaultField)
      pt.y += (DefaultHeight+DistToColor);
    rect = TRect(pt, TSize(BoxSize, BoxSize));
  }
}
 
// We draw a single cell.
//
void TColorPicker::DrawCell(TDC& dc, int index)
{
  TRect rect;
  GetCellRect(index, rect);
  PaintBorder(dc, index);
  rect.Inflate(-2, -2);
  if (index != INVALID_COLOR) {
    if (GetFlatState(index) == FLATDOWNSTATE) {
      FillMaskRect(dc, rect);
    }
    else {
      dc.FillRect(rect, TBrush(TColor::Sys3dFace));
    }
    rect.Inflate(-1, -1);
    TBrush framebrush(TColor::Sys3dShadow);
    if (index != CUSTOM_BOX_VALUE)
      dc.FrameRect(rect, framebrush);
    dc.RestoreBrush();
    rect.Inflate(-1, -1);
  }
  if (index >= 0 && index < (int)Data.NumColors) {
    dc.SelectStockObject(NULL_PEN);
    if (PaletteRealized) {
      TColor palColor(PALETTEINDEX(index));
      dc.FillRect(rect, TBrush(palColor));
    }
    else {
      TColor palColor(Data.Colors[index]);
      dc.FillRect(rect, TBrush(palColor));
    }
    dc.RestorePen();
  }
  else {
    if (index != INVALID_COLOR) {
      dc.SelectStockObject(ANSI_VAR_FONT);
      TColor oldtextcol = dc.SetTextColor(TColor::SysBtnText);
      int oldbkmode = dc.SetBkMode(TRANSPARENT);
      if (index == DEFAULT_BOX_VALUE && !DefaultText.empty()) {
        dc.DrawText(DefaultText.c_str(), -1, rect,
          DT_CENTER | DT_SINGLELINE | DT_VCENTER);
      }
 
      if (index == CUSTOM_BOX_VALUE && !CustomText.empty()) {
        dc.DrawText(CustomText.c_str(), -1, rect,
          DT_CENTER | DT_SINGLELINE | DT_VCENTER);
 
        // paint separator line
        //
        TPen pen(TColor::Sys3dShadow);
        dc.SelectObject(pen);
        dc.MoveTo(BoxMargin, rect.top-4-DistToCustom/2);
        dc.LineTo(WindowSize.cy-2*BoxMargin+2, rect.top-4-DistToCustom/2);
        dc.RestorePen();
        TPen pen2(TColor::Sys3dHilight);
        dc.SelectObject(pen2);
        dc.MoveTo(BoxMargin, rect.top-3-DistToCustom/2);
        dc.LineTo(WindowSize.cy-2*BoxMargin+2, rect.top-3-DistToCustom/2);
        dc.RestorePen();
      }
      dc.SetTextColor(oldtextcol);
      dc.SetBkMode(oldbkmode);
      dc.RestoreObjects();
    }
  }
}
 
// Retrieve the current flat state for a cell.
//
int TColorPicker::GetFlatState(int index)
{
  if (index == ChosenColorSel) {
    if (index == CurrentSel)
      return FLATDOWNSEL;
    else
      return FLATDOWNSTATE;
  }
  if (index == CurrentSel)
    return FLATUPSTATE;
  return FLATNORMALSTATE;
}
 
// We retrieve the index when we know the color.
//
int TColorPicker::GetIndexByColor(TColor color)
{
  if (color == Data.DefaultFieldColor) {
    if (Data.ShowDefaultField)
      return DEFAULT_BOX_VALUE;
  }
  for (uint i=0; i<Data.NumColors; i++) {
    if (Data.Colors[i] == color)
      return i;
  }
  return INVALID_COLOR;
}
 
// We changed the current selection cell.
//
void TColorPicker::ColorSelectionChanged(int index)
{
  if (index != CurrentSel) {
    TClientDC dc (*this);
 
    if (MyLogPalette && dc.GetDeviceCaps(RASTERCAPS) & RC_PALETTE) {
      TPalette palette(*MyLogPalette);
      dc.SelectObject(palette);
      PaletteRealized = (dc.RealizePalette() > 0);
    }
 
    int oldsel = CurrentSel;
    CurrentSel = index;
    DrawCell(dc, oldsel);
    if (CurrentSel != INVALID_COLOR) {
      DrawCell(dc, CurrentSel);
      if (CurrentSel == DEFAULT_BOX_VALUE && Data.ShowDefaultField)
        SetStatusBarMessage(Data.DefaultFieldResId+1);
      else if (CurrentSel == CUSTOM_BOX_VALUE && Data.ShowCustomField)
        SetStatusBarMessage(Data.CustomFieldResId+1);
      else
        SetStatusBarMessage(-1);
    }
    else
      SetStatusBarMessage(-1);
 
    dc.RestoreObjects();
  }
}
 
// We move the cursor to another cell.
//
void TColorPicker::EvMouseMove(uint modKeys, const TPoint& point)
{
  TWindow::EvMouseMove(modKeys, point);
  ColorSelectionChanged(GetIndexFromPoint(point));
}
 
// We changed the current chosen color.
//
void TColorPicker::ChosenColorChanged(int index)
{
  bool canExit = false;
  int oldchosen = ChosenColorSel;
  TColor chosencolor;
  if (index != INVALID_COLOR && index == ChosenColorSel) {
    chosencolor = CurrentChosenColor;
    canExit = true;
  }
  else if (index != ChosenColorSel && index != INVALID_COLOR) {
    ChosenColorSel = index;
    Invalidate(false);
    UpdateWindow();
    if (ChosenColorSel >= 0) {
      chosencolor = Data.Colors[ChosenColorSel];
      canExit = true;
    }
    else {
      if (ChosenColorSel == DEFAULT_BOX_VALUE) {
        chosencolor = Data.DefaultFieldColor;
        canExit = true;
      }
    }
  }
  if (index == CUSTOM_BOX_VALUE) {
    ColorDialogOpened = true;
    TChooseColorDialog::TData choose;
    choose.Flags = CC_RGBINIT | CC_FULLOPEN;
    if (CurrentCustomColor == Data.DefaultFieldColor)
      choose.Color = CurrentChosenColor;
    else
      choose.Color = CurrentCustomColor;
    choose.CustColors = (TColor*)CustColors;
    if (TChooseColorDialog(this, choose).Execute() == IDOK) {
      ChosenColorSel = GetIndexByColor(choose.Color);
      chosencolor = choose.Color;
      CurrentCustomColor = choose.Color;
      canExit = true;
    }
    else {
      ChosenColorSel = oldchosen;
      Invalidate(false);
      UpdateWindow();
      SetCapture();
    }
    ColorDialogOpened = false;
    Invalidate(true);
  }
  if (canExit) {
    CurrentChosenColor = chosencolor;
    if (Data.AutoParentNotify)
      NotifyAtParent();
    ExitPicker();
  }
}
 
// We pressed the left mouse button.
//
void TColorPicker::EvLButtonDown(uint modKeys, const TPoint& point)
{
  TWindow::EvLButtonDown(modKeys, point);
 
  // If we are in a cell, change the current chosen color and exit.
  // If we aren't in the client rectangle, we exit.
  if (GetClientRect().Contains(point))
    ChosenColorChanged(GetIndexFromPoint(point));
  else
    ExitPicker();
}
 
// Retrieve the cell's index when we know the current position of the cursor.
//
int TColorPicker::GetIndexFromPoint(TPoint point)
{
  TRect rect;
  GetCellRect(DEFAULT_BOX_VALUE, rect);
  if (rect.Contains(point))
    return DEFAULT_BOX_VALUE;
  GetCellRect(CUSTOM_BOX_VALUE, rect);
  if (rect.Contains(point))
    return CUSTOM_BOX_VALUE;
  for (uint i=0; i<Data.NumColors; i++) {
    GetCellRect(i, rect);
    if (rect.Contains(point))
      return i;
  }
  return (int) INVALID_COLOR;
}
 
// We paint the border of a cell.
//
void TColorPicker::PaintBorder(TDC& dc, int index)
{
  TRect rect;
  GetCellRect(index, rect);
  if (GetFlatState(index) == FLATUPSTATE) {
    TUIBorder border(rect, TUIBorder::TEdge(TUIBorder::RaisedInner),
      TUIBorder::Rect | TUIBorder::Adjust);
    border.Paint(dc);
  }
  else if (GetFlatState(index) == FLATDOWNSTATE ||
    GetFlatState(index) == FLATDOWNSEL) {
      TUIBorder(rect, TUIBorder::Recessed).Paint(dc);
  }
  else {
    TBrush brush (TColor::Sys3dFace);
    dc.FrameRect(rect, brush);
    dc.RestoreBrush();
  }
}
 
// We dispatch all mouse messages to the tooltip control.
//
bool TColorPicker::PreProcessMsg(MSG& msg)
{
  if (PickerTooltip && PickerTooltip->IsWindow()) {
    if (msg.hwnd == *this || IsChild(msg.hwnd)) {
      if (msg.message >= WM_MOUSEFIRST && msg.message <= WM_MOUSELAST) {
        PickerTooltip->RelayEvent(msg);
      }
    }
  }
  return (TWindow::PreProcessMsg(msg));
}
 
// We display a message on the status bar.
//
void TColorPicker::SetStatusBarMessage(int id)
{
  TWindow* parent= GetParentO();
  TDecoratedFrame* frame = 0;
  if (parent)
    frame = TYPESAFE_DOWNCAST(parent, TDecoratedFrame);
  while (parent && !frame) {
    parent = parent->GetParentO();
    if (parent)
      frame = TYPESAFE_DOWNCAST(parent, TDecoratedFrame);
  }
  if (frame) {
    if (id > 0) {
      frame->HandleMessage(WM_MENUSELECT, id, 0);
    }
    else {
      frame->HandleMessage(WM_MENUSELECT, 0xFFFF0000, 0);
    }
    frame->HandleMessage(WM_ENTERIDLE, MSGF_MENU);
  }
}
 
// Retrieve the current chosen color.
//
TColor TColorPicker::GetCurrentChosenColor()
{
  return CurrentChosenColor;
}
 
// Set the current chosen color.
//
void TColorPicker::SetCurrentChosenColor(TColor color)
{
  CurrentChosenColor = color;
  ChosenColorSel = GetIndexByColor(CurrentChosenColor);
  if (GetHandle() && IsWindowVisible())
    Invalidate();
}
 
// We notify the parent window that the user has selected a color on the picker.
//
void TColorPicker::NotifyAtParent()
{
  ::PostMessage(GetParentH(), ColorPickerMessage, (WPARAM)RefId,
    (LPARAM)CurrentChosenColor.GetValue());
}
 
// Someone has changed the palette. We must realize our palette again.
//
void TColorPicker::EvPaletteChanged(THandle hWndPalChg)
{
  TWindow::EvPaletteChanged(hWndPalChg);
  if (hWndPalChg != GetHandle())
    Invalidate();
}
 
// Give us the chance to realize our palette.
//
bool TColorPicker::EvQueryNewPalette()
{
  bool result;
  Invalidate();
  result = TWindow::EvQueryNewPalette() | PaletteRealized;
  return result;
}
 
// We manage the keys to move the current selection box.
//
void TColorPicker::EvKeyDown(uint key, uint repeatCount, uint flags)
{
  int newselection;
  TWindow::EvKeyDown(key, repeatCount, flags);
 
  switch (key) {
  case VK_ESCAPE:
    ExitPicker();
    break;
 
  case VK_RETURN:
  case VK_SPACE:
    ChosenColorChanged(CurrentSel);
    break;
 
  case VK_LEFT:
    if ((CurrentSel == INVALID_COLOR) || (CurrentSel == DEFAULT_BOX_VALUE)) {
      if (Data.ShowCustomField)
        newselection = CUSTOM_BOX_VALUE;
      else
        newselection = Data.NumColors - 1;
    }
    else if (CurrentSel == CUSTOM_BOX_VALUE) {
      newselection = Data.NumColors - 1;
    }
    else if (CurrentSel == 0) {
      if (Data.ShowDefaultField)
        newselection = DEFAULT_BOX_VALUE;
      else if (Data.ShowCustomField)
        newselection = CUSTOM_BOX_VALUE;
      else
        newselection = Data.NumColors - 1;
    }
    else
      newselection = CurrentSel - 1;
    ColorSelectionChanged(newselection);
    break;
 
  case VK_RIGHT:
    if ((CurrentSel == INVALID_COLOR) || (CurrentSel == CUSTOM_BOX_VALUE)) {
      if (Data.ShowDefaultField)
        newselection = DEFAULT_BOX_VALUE;
      else
        newselection = 0;
    }
    else if (CurrentSel == DEFAULT_BOX_VALUE) {
      newselection = 0;
    }
    else if (CurrentSel == (int)Data.NumColors - 1) {
      if (Data.ShowCustomField)
        newselection = CUSTOM_BOX_VALUE;
      else if (Data.ShowDefaultField)
        newselection = DEFAULT_BOX_VALUE;
      else
        newselection = 0;
    }
    else
      newselection = CurrentSel + 1;
    ColorSelectionChanged(newselection);
    break;
 
  case VK_DOWN:
    if ((CurrentSel == INVALID_COLOR) || (CurrentSel == CUSTOM_BOX_VALUE)) {
      if (Data.ShowDefaultField)
        newselection = DEFAULT_BOX_VALUE;
      else
        newselection = 0;
    }
    else if (CurrentSel == DEFAULT_BOX_VALUE) {
      newselection = 0;
    }
    else if (CurrentSel + Data.NumColumn > Data.NumColors - 1) {
      if (Data.ShowCustomField)
        newselection = CUSTOM_BOX_VALUE;
      else if (Data.ShowDefaultField)
        newselection = DEFAULT_BOX_VALUE;
      else
        newselection = 0;
    }
    else
      newselection = CurrentSel + Data.NumColumn;
    ColorSelectionChanged(newselection);
    break;
 
  case VK_UP:
    if ((CurrentSel == INVALID_COLOR) || (CurrentSel == DEFAULT_BOX_VALUE)) {
      if (Data.ShowCustomField)
        newselection = CUSTOM_BOX_VALUE;
      else
        newselection = Data.NumColors - 1;
    }
    else if (CurrentSel == CUSTOM_BOX_VALUE) {
      newselection = Data.NumColors - 1;
    }
    else if ((CurrentSel - (int)Data.NumColumn) < 0) {
      if (Data.ShowDefaultField)
        newselection = DEFAULT_BOX_VALUE;
      else if (Data.ShowCustomField)
        newselection = CUSTOM_BOX_VALUE;
      else
        newselection = Data.NumColors - 1;
    }
    else
      newselection = CurrentSel - Data.NumColumn;
    ColorSelectionChanged(newselection);
    break;
 
  case VK_HOME:
    if (Data.ShowDefaultField)
      newselection = DEFAULT_BOX_VALUE;
    else
      newselection = 0;
    ColorSelectionChanged(newselection);
    break;
 
  case VK_END:
    if (Data.ShowCustomField)
      newselection = CUSTOM_BOX_VALUE;
    else
      newselection = Data.NumColors - 1;
    ColorSelectionChanged(newselection);
    break;
  }
}
 
// We retrieve a value from the custom colors array.
//
TColor TColorPicker::GetCustomColorsValue(int index)
{
  if (index < 0 && index >= NumCustColors)
    index = 0;
  return TColor(CustColors[index]);
}
 
// We set a value on the custom colors array.
//
void TColorPicker::SetCustomColorsValue(int index, TColor color)
{
  if (index >= 0 && index < NumCustColors)
    CustColors[index] = color;
}
 
} // OwlExt namespace
//======================================================================================

V547 Expression 'index < 0 && index >= NumCustColors' is always false.

V773 The 'Colors' pointer was assigned values twice without releasing the memory. A memory leak is possible.

V794 The assignment operator should be protected from the case of 'this == &d'.

V818 It is more efficient to use an initialization list 'CurrentChosenColor(startColor)' rather than an assignment operator.

V815 Decreased performance. Consider replacing the expression 'DefaultText = ""' with 'DefaultText.clear()'.

V815 Decreased performance. Consider replacing the expression 'CustomText = ""' with 'CustomText.clear()'.

V821 Decreased performance. The 'framebrush' variable can be constructed in a lower level scope.