leocad/win/Clrpopup.cpp

566 lines
14 KiB
C++

#include "lc_global.h"
#include <math.h>
#include "ClrPick.h"
#include "ClrPopup.h"
#include "propertiesgridctrl.h"
#include "lc_colors.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define INVALID_COLOUR -1
#define MAX_COLOURS 256
/////////////////////////////////////////////////////////////////////////////
// CColorPopup
CColorPopup::CColorPopup()
{
Initialise();
}
CColorPopup::CColorPopup(CPoint p, int nColor, CWnd* pParentWnd, bool IgnoreMouse)
{
Initialise();
m_nColor = m_nInitialColor = nColor;
m_pParent = pParentWnd;
m_IgnoreMouse = IgnoreMouse;
CColorPopup::Create(p, nColor, pParentWnd);
}
void CColorPopup::Initialise()
{
m_nNumColumns = 0;
m_nNumRows = 0;
m_nBoxSize = 18;
m_nMargin = ::GetSystemMetrics(SM_CXEDGE);
m_nCurrentSel = INVALID_COLOUR;
m_nChosenColorSel = INVALID_COLOUR;
m_pParent = NULL;
m_nColor = m_nInitialColor = 0;
m_IgnoreMouse = false;
// Idiot check: Make sure the colour square is at least 5 x 5;
if (m_nBoxSize - 2 * m_nMargin - 2 < 5)
m_nBoxSize = 5 + 2 * m_nMargin + 2;
// Create the font
NONCLIENTMETRICS ncm;
ncm.cbSize = sizeof(NONCLIENTMETRICS);
VERIFY(SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0));
m_Font.CreateFontIndirect(&(ncm.lfMessageFont));
// Create the palette
int NumColors = gColorList.GetSize() < MAX_COLOURS ? gColorList.GetSize() : MAX_COLOURS;
struct {
LOGPALETTE LogPalette;
PALETTEENTRY PalEntry[MAX_COLOURS];
} pal;
LOGPALETTE* pLogPalette = (LOGPALETTE*) &pal;
pLogPalette->palVersion = 0x300;
pLogPalette->palNumEntries = (WORD)gColorList.GetSize();
for (int i = 0; i < NumColors; i++)
{
float* Value = gColorList[i].Value;
pLogPalette->palPalEntry[i].peRed = (BYTE)(Value[0] * 255);
pLogPalette->palPalEntry[i].peGreen = (BYTE)(Value[1] * 255);
pLogPalette->palPalEntry[i].peBlue = (BYTE)(Value[2] * 255);
pLogPalette->palPalEntry[i].peFlags = 0;
}
m_Palette.CreatePalette(pLogPalette);
}
CColorPopup::~CColorPopup()
{
m_Font.DeleteObject();
m_Palette.DeleteObject();
}
BOOL CColorPopup::Create(CPoint p, int nColor, CWnd* pParentWnd)
{
ASSERT(pParentWnd && ::IsWindow(pParentWnd->GetSafeHwnd()));
ASSERT(pParentWnd->IsKindOf(RUNTIME_CLASS(CColorPicker)) || pParentWnd->IsKindOf(RUNTIME_CLASS(CMFCPropertyGridCtrl)));
m_pParent = pParentWnd;
m_nColor = m_nInitialColor = nColor;
// Get the class name and create the window
CString szClassName = AfxRegisterWndClass(CS_CLASSDC|CS_SAVEBITS|CS_HREDRAW|CS_VREDRAW, 0, (HBRUSH)GetStockObject(LTGRAY_BRUSH),0);
if (!CWnd::CreateEx(0, szClassName, _T(""), WS_VISIBLE|WS_POPUP, p.x, p.y, 100, 100, pParentWnd->GetSafeHwnd(), 0, NULL))
return FALSE;
// Set the window size
SetWindowSize();
// Calculate the layout
CalculateLayout();
// Create the tooltips
CreateToolTips();
// Find which cell (if any) corresponds to the initial colour
for (int i = 0; i < mCells.GetSize(); i++)
{
if (mCells[i].ColorIndex == nColor)
{
m_nChosenColorSel = i;
break;
}
}
// Capture all mouse events for the life of this window
SetCapture();
return TRUE;
}
BEGIN_MESSAGE_MAP(CColorPopup, CWnd)
//{{AFX_MSG_MAP(CColorPopup)
ON_WM_NCDESTROY()
ON_WM_LBUTTONUP()
ON_WM_PAINT()
ON_WM_MOUSEMOVE()
ON_WM_KEYDOWN()
ON_WM_QUERYNEWPALETTE()
ON_WM_PALETTECHANGED()
ON_WM_KILLFOCUS()
ON_WM_ACTIVATEAPP()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CColorPopup message handlers
// For tooltips
BOOL CColorPopup::PreTranslateMessage(MSG* pMsg)
{
m_ToolTip.RelayEvent(pMsg);
return CWnd::PreTranslateMessage(pMsg);
}
// If an arrow key is pressed, then move the selection
void CColorPopup::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
if (nChar == VK_LEFT)
{
if (m_nCurrentSel > 0)
ChangeSelection(m_nCurrentSel - 1);
}
else if (nChar == VK_RIGHT)
{
if (m_nCurrentSel < mCells.GetSize() - 1)
ChangeSelection(m_nCurrentSel + 1);
}
else if (nChar == VK_UP || nChar == VK_DOWN)
{
if (m_nCurrentSel < 0 || m_nCurrentSel >= mCells.GetSize())
m_nCurrentSel = 0;
int CurGroup = 0;
int NumCells = 0;
for (CurGroup = 0; CurGroup < LC_NUM_COLORGROUPS; CurGroup++)
{
int NumColors = gColorGroups[CurGroup].Colors.GetSize();
if (m_nCurrentSel < NumCells + NumColors)
break;
NumCells += NumColors;
}
int Row = (m_nCurrentSel - NumCells) / m_nNumColumns;
int Column = (m_nCurrentSel - NumCells) % m_nNumColumns;
if (nChar == VK_UP)
{
if (Row > 0)
ChangeSelection(m_nCurrentSel - m_nNumColumns);
else if (CurGroup > 0)
{
int NumColors = gColorGroups[CurGroup - 1].Colors.GetSize();
int NumColumns = NumColors % m_nNumColumns;
if (NumColumns <= Column + 1)
ChangeSelection(m_nCurrentSel - NumColumns - m_nNumColumns);
else
ChangeSelection(m_nCurrentSel - NumColumns);
}
}
else if (nChar == VK_DOWN)
{
int NumColors = gColorGroups[CurGroup].Colors.GetSize();
if (m_nCurrentSel + m_nNumColumns < NumCells + NumColors)
ChangeSelection(m_nCurrentSel + m_nNumColumns);
else
{
int NumColumns = NumColors % m_nNumColumns;
if (NumColumns > Column)
ChangeSelection(m_nCurrentSel + NumColumns);
else
ChangeSelection(m_nCurrentSel + m_nNumColumns + NumColumns);
}
}
}
else if (nChar == VK_ESCAPE)
{
m_nColor = m_nInitialColor;
EndSelection(CPN_SELENDCANCEL);
return;
}
else if (nChar == VK_RETURN || nChar == VK_SPACE)
{
EndSelection(CPN_SELENDOK);
return;
}
CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
}
// auto-deletion
void CColorPopup::OnNcDestroy()
{
CWnd::OnNcDestroy();
delete this;
}
void CColorPopup::OnPaint()
{
CPaintDC dc(this); // device context for painting
CRect rect;
GetClientRect(rect);
dc.FillSolidRect(rect, ::GetSysColor(COLOR_3DFACE));
CFont *pOldFont = (CFont*)dc.SelectObject(&m_Font);
dc.SetBkMode(TRANSPARENT);
for (int GroupIdx = 0; GroupIdx < LC_NUM_COLORGROUPS; GroupIdx++)
dc.DrawText(gColorGroups[GroupIdx].Name, mGroups[GroupIdx], DT_CENTER | DT_VCENTER | DT_SINGLELINE);
dc.SelectObject(pOldFont);
// Draw colour cells
for (int i = 0; i < mCells.GetSize(); i++)
DrawCell(&dc, i);
// Draw raised window edge (ex-window style WS_EX_WINDOWEDGE is sposed to do this,
// but for some reason isn't
dc.DrawEdge(rect, EDGE_RAISED, BF_RECT);
}
void CColorPopup::OnMouseMove(UINT nFlags, CPoint point)
{
int nNewSelection = INVALID_COLOUR;
for (int i = 0; i < mCells.GetSize(); i++)
{
if (mCells[i].Rect.PtInRect(point))
{
nNewSelection = i;
break;
}
}
if (nNewSelection == INVALID_COLOUR)
{
CWnd::OnMouseMove(nFlags, point);
return;
}
m_IgnoreMouse = false;
// Has the row/col selection changed? If yes, then redraw old and new cells.
if (nNewSelection != m_nCurrentSel)
ChangeSelection(nNewSelection);
CWnd::OnMouseMove(nFlags, point);
}
// End selection on LButtonUp
void CColorPopup::OnLButtonUp(UINT nFlags, CPoint point)
{
CWnd::OnLButtonUp(nFlags, point);
if (m_IgnoreMouse)
{
m_IgnoreMouse = false;
return;
}
DWORD pos = GetMessagePos();
point = CPoint(LOWORD(pos), HIWORD(pos));
if (m_WindowRect.PtInRect(point))
EndSelection(CPN_SELENDOK);
else
EndSelection(CPN_SELENDCANCEL);
}
/////////////////////////////////////////////////////////////////////////////
// CColorPopup implementation
// Works out an appropriate size and position of this window
void CColorPopup::SetWindowSize()
{
CSize TextSize(0, 0);
// Calculate the number of columns and rows.
m_nNumColumns = 14;
m_nNumRows = 0;
for (int GroupIdx = 0; GroupIdx < LC_NUM_COLORGROUPS; GroupIdx++)
m_nNumRows += (gColorGroups[GroupIdx].Colors.GetSize() + m_nNumColumns - 1) / m_nNumColumns;
// Calculate text size.
CClientDC dc(this);
CFont* pOldFont = (CFont*) dc.SelectObject(&m_Font);
for (int GroupIdx = 0; GroupIdx < LC_NUM_COLORGROUPS; GroupIdx++)
{
lcColorGroup* Group = &gColorGroups[GroupIdx];
CSize NameSize = dc.GetTextExtent(Group->Name);
if (NameSize.cx > TextSize.cx)
TextSize.cx = NameSize.cx;
TextSize.cy += NameSize.cy + 2;
}
dc.SelectObject(pOldFont);
// Get the current window position, and set the new size
CRect rect;
GetWindowRect(rect);
m_WindowRect.SetRect(rect.left, rect.top,
rect.left + m_nNumColumns*m_nBoxSize + 2*m_nMargin,
rect.top + m_nNumRows*m_nBoxSize + 2*m_nMargin);
if (TextSize.cx > m_WindowRect.Width())
m_WindowRect.right = m_WindowRect.left + TextSize.cx;
m_WindowRect.bottom += TextSize.cy;
// Need to check it'll fit on screen: Too far right?
CSize ScreenSize(::GetSystemMetrics(SM_CXSCREEN), ::GetSystemMetrics(SM_CYSCREEN));
if (m_WindowRect.right > ScreenSize.cx)
m_WindowRect.OffsetRect(-(m_WindowRect.right - ScreenSize.cx), 0);
// Too far left?
if (m_WindowRect.left < 0)
m_WindowRect.OffsetRect( -m_WindowRect.left, 0);
// Bottom falling out of screen?
if (m_WindowRect.bottom > ScreenSize.cy)
{
CRect ParentRect;
m_pParent->GetWindowRect(ParentRect);
m_WindowRect.OffsetRect(0, -(ParentRect.Height() + m_WindowRect.Height()));
}
// Set the window size and position
MoveWindow(m_WindowRect, TRUE);
}
void CColorPopup::CalculateLayout()
{
CRect ClientRect;
GetClientRect(&ClientRect);
CClientDC dc(this);
CFont* pOldFont = (CFont*) dc.SelectObject(&m_Font);
int CurCell = 0;
int CurY = m_nMargin;
mGroups.RemoveAll();
mGroups.SetSize(LC_NUM_COLORGROUPS);
for (int GroupIdx = 0; GroupIdx < LC_NUM_COLORGROUPS; GroupIdx++)
{
lcColorGroup* Group = &gColorGroups[GroupIdx];
int CurColumn = 0;
CSize TextSize = dc.GetTextExtent(Group->Name);
mGroups[GroupIdx].SetRect(0, CurY + 1, ClientRect.Width(), CurY + TextSize.cy + 2);
CurY += TextSize.cy + 2;
for (int ColorIdx = 0; ColorIdx < Group->Colors.GetSize(); ColorIdx++)
{
int Left = m_nMargin + CurColumn * m_nBoxSize;
int Right = Left + m_nBoxSize;
int Top = CurY;
int Bottom = CurY + m_nBoxSize;
lcColor* Color = &gColorList[Group->Colors[ColorIdx]];
CColorPopupCell Cell;
Cell.Color = RGB(Color->Value[0] * 255, Color->Value[1] * 255, Color->Value[2] * 255);
Cell.ColorIndex = Group->Colors[ColorIdx];
Cell.Rect.SetRect(Left, Top, Right, Bottom);
mCells.Add(Cell);
CurColumn++;
if (CurColumn == m_nNumColumns)
{
CurColumn = 0;
CurY += m_nBoxSize;
}
CurCell++;
}
if (CurColumn != 0)
CurY += m_nBoxSize;
}
dc.SelectObject(pOldFont);
}
void CColorPopup::CreateToolTips()
{
// Create the tool tip
if (!m_ToolTip.Create(this)) return;
// Add a tool for each cell
for (int i = 0; i < mCells.GetSize(); i++)
{
CString Text;
lcColor* Color = &gColorList[mCells[i].ColorIndex];
Text.Format("%s (%d)", Color->Name, Color->Code);
m_ToolTip.AddTool(this, Text, mCells[i].Rect, i + 1);
}
}
void CColorPopup::ChangeSelection(int nIndex)
{
CClientDC dc(this); // device context for drawing
if (m_nCurrentSel >= 0 && m_nCurrentSel < mCells.GetSize())
{
// Set Current selection as invalid and redraw old selection (this way
// the old selection will be drawn unselected)
int OldSel = m_nCurrentSel;
m_nCurrentSel = INVALID_COLOUR;
DrawCell(&dc, OldSel);
}
// Set the current selection as row/col and draw (it will be drawn selected)
m_nCurrentSel = nIndex;
DrawCell(&dc, m_nCurrentSel);
m_nColor = m_nCurrentSel;
}
void CColorPopup::EndSelection(int nMessage)
{
ReleaseCapture();
if (nMessage == CPN_SELENDCANCEL)
m_nColor = m_nInitialColor;
int ColorIndex = m_nColor >= 0 ? mCells[m_nColor].ColorIndex : 0;
if (m_pParent->IsKindOf(RUNTIME_CLASS(CMFCPropertyGridCtrl)))
{
CLeoCADMFCPropertyGridCtrl* Ctrl = (CLeoCADMFCPropertyGridCtrl*)m_pParent;
Ctrl->SetColor(ColorIndex);
}
else
m_pParent->SendMessage(nMessage, 0, (LPARAM)ColorIndex);
DestroyWindow();
}
void CColorPopup::DrawCell(CDC* pDC, int nIndex)
{
CRect rect = mCells[nIndex].Rect;
// Select and realize the palette
CPalette* pOldPalette;
if (pDC->GetDeviceCaps(RASTERCAPS) & RC_PALETTE)
{
pOldPalette = pDC->SelectPalette(&m_Palette, FALSE);
pDC->RealizePalette();
}
// fill background
if (m_nChosenColorSel == nIndex && m_nCurrentSel != nIndex)
pDC->FillSolidRect(rect, ::GetSysColor(COLOR_3DHILIGHT));
else
pDC->FillSolidRect(rect, ::GetSysColor(COLOR_3DFACE));
// Draw button
if (m_nCurrentSel == nIndex)
pDC->DrawEdge(rect, EDGE_RAISED, BF_RECT);
else if (m_nChosenColorSel == nIndex)
pDC->DrawEdge(rect, EDGE_SUNKEN, BF_RECT);
CBrush brush(PALETTERGB(GetRValue(mCells[nIndex].Color), GetGValue(mCells[nIndex].Color), GetBValue(mCells[nIndex].Color) ));
CPen pen;
pen.CreatePen(PS_SOLID, 1, ::GetSysColor(COLOR_3DSHADOW));
CBrush* pOldBrush = (CBrush*) pDC->SelectObject(&brush);
CPen* pOldPen = (CPen*) pDC->SelectObject(&pen);
// Draw the cell colour
rect.DeflateRect(m_nMargin+1, m_nMargin+1);
pDC->Rectangle(rect);
rect.DeflateRect(1, 1);
rect.bottom -= 1;
// restore DC and cleanup
pDC->SelectObject(pOldBrush);
pDC->SelectObject(pOldPen);
brush.DeleteObject();
pen.DeleteObject();
if (pDC->GetDeviceCaps(RASTERCAPS) & RC_PALETTE)
pDC->SelectPalette(pOldPalette, FALSE);
}
BOOL CColorPopup::OnQueryNewPalette()
{
Invalidate();
return CWnd::OnQueryNewPalette();
}
void CColorPopup::OnPaletteChanged(CWnd* pFocusWnd)
{
CWnd::OnPaletteChanged(pFocusWnd);
if (pFocusWnd->GetSafeHwnd() != GetSafeHwnd())
Invalidate();
}
void CColorPopup::OnKillFocus(CWnd* pNewWnd)
{
CWnd::OnKillFocus(pNewWnd);
ReleaseCapture();
//DestroyWindow(); - causes crash when Custom colour dialog appears.
}
// KillFocus problem fix suggested by Paul Wilkerson.
void CColorPopup::OnActivateApp(BOOL bActive, DWORD hTask)
{
CWnd::OnActivateApp(bActive, hTask);
// If Deactivating App, cancel this selection
if (!bActive)
EndSelection(CPN_SELENDCANCEL);
}