?? minicalendarctrl.cpp
字號(hào):
// ----------------------------------------------------------------------------
// Written by Tony Ioanides (tonyi@bigpond.com)
// Copyright (c) 2003 Tony Ioanides.
//
// This code may be used in compiled form in any way you desire. This file may
// be redistributed by any means PROVIDING it is not sold for profit without
// the authors written consent, and providing that this notice and the authors
// name is included.
//
// This file is provided "as is" with no expressed or implied warranty.
// The author accepts no liability if it causes any damage to you or your
// computer whatsoever.
// ----------------------------------------------------------------------------
#include "stdafx.h"
#include "MiniCalendarCtrl.h"
#include <algorithm>
#ifndef NUM_ELEMENTS
#define NUM_ELEMENTS(a) (sizeof(a)/sizeof(*a))
#endif // NUM_ELEMENTS
// ----------------------------------------------------------------------------
static CRect
MC_GetArrowRect(CRect rectHeader, BOOL bLeftArrow)
{
const int yMid = (rectHeader.top + rectHeader.bottom) / 2;
const int cx = rectHeader.Height() / 5;
rectHeader.top = yMid - cx;
rectHeader.bottom = yMid + cx;
if (bLeftArrow)
{
rectHeader.left += 6;
rectHeader.right = rectHeader.left + cx;
}
else
{
rectHeader.right -= 6;
rectHeader.left = rectHeader.right - cx;
}
return rectHeader;
}
// ----------------------------------------------------------------------------
static void
MC_DrawArrow(CDCHandle dc, CRect rectHeader, BOOL bLeftArrow)
{
RECT rect = MC_GetArrowRect(rectHeader, bLeftArrow);
if (bLeftArrow)
std::swap(rect.left, rect.right);
POINT ptTriangle[] =
{
{ rect.left, rect.top },
{ rect.left, rect.bottom },
{ rect.right, (rect.top + rect.bottom) / 2 }
};
HBRUSH hbrOrig = dc.SelectStockBrush(BLACK_BRUSH);
dc.Polygon(ptTriangle, NUM_ELEMENTS(ptTriangle));
dc.SelectBrush(hbrOrig);
}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// Internal constants
enum
{
nMonthsPerYear = 12,
nDaysPerWeek = 7,
nRowsPerCell = 6,
nDaysPerCell = nRowsPerCell * nDaysPerWeek,
nBorderSize = 4,
nCellMargin = 10
};
#define ID_TIMER_FIRST 0xDEAD
#define ID_TIMER_REPEAT 0xC0DE
#define HEADER_FORMAT _T("%B %Y")
// ----------------------------------------------------------------------------
CMiniCalendarCtrl::CMiniCalendarCtrl()
{
m_crBack = GetSysColor(COLOR_WINDOW);
m_bShow3dBorder = 1;
m_bShowNonMonthDays = 1;
m_bHighlightToday = 1;
m_bMultiSelEnabled = 1;
m_nCols = 1;
m_nRows = 1;
m_nFirstDayOfWeek = 1;
m_nStartMonth = 1;
m_nStartYear = 1970;
m_nMonthsToScroll = 1;
m_daySpace.cx = 4;
m_daySpace.cy = 4;
m_nActiveElement = 0;
}
// ----------------------------------------------------------------------------
BOOL
CMiniCalendarCtrl::IsDateSelected(const COleDateTime& date) const
{
if (m_dtSelect.GetStatus() != COleDateTime::valid)
return FALSE;
if (date == m_dtSelect)
return TRUE;
if (m_bMultiSelEnabled && m_dtAnchor.GetStatus() == COleDateTime::valid)
return m_dtSelect > m_dtAnchor ?
(date >= m_dtAnchor && date <= m_dtSelect) :
(date >= m_dtSelect && date <= m_dtAnchor);
return FALSE;
}
// ----------------------------------------------------------------------------
SIZE
CMiniCalendarCtrl::GetMaxSize() const
{
return GetMaxSize(m_nCols, m_nRows);
}
// ----------------------------------------------------------------------------
SIZE
CMiniCalendarCtrl::GetMaxSize(UINT nCols, UINT nRows) const
{
const int cxyBorder = m_bShow3dBorder ? (nBorderSize * 2) : 0;
return CSize(
nCols * m_cellSize.cx + cxyBorder,
nRows * m_cellSize.cy + cxyBorder);
}
// ----------------------------------------------------------------------------
BOOL
CMiniCalendarCtrl::SetLayout(UINT nCols, UINT nRows)
{
if (nCols > 0 && nRows > 0 && ((int) nCols != m_nCols || (int) nRows != m_nRows))
{
m_nCols = nCols;
m_nRows = nRows;
AutoSize();
return TRUE;
}
return FALSE;
}
// ----------------------------------------------------------------------------
BOOL
CMiniCalendarCtrl::SetFirstDayOfWeek(UINT nFirstDayOfWeek)
{
if (nFirstDayOfWeek >= 1 && nFirstDayOfWeek <= nDaysPerWeek &&
nFirstDayOfWeek != (UINT) m_nFirstDayOfWeek)
{
m_nFirstDayOfWeek = nFirstDayOfWeek;
Invalidate();
return TRUE;
}
return FALSE;
}
// ----------------------------------------------------------------------------
void
CMiniCalendarCtrl::AutoSize()
{
const SIZE size = GetMaxSize();
SetWindowPos(0, 0, 0, size.cx, size.cy, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
}
// ----------------------------------------------------------------------------
BOOL
CMiniCalendarCtrl::EnsureVisible(const COleDateTime& date)
{
if (date.GetStatus() != COleDateTime::valid)
return FALSE;
if (date >= GetMonthFromCell(0, 0) &&
date <= GetMonthFromCell(m_nCols - 1, m_nRows - 1))
return FALSE;
m_nStartMonth = date.GetMonth();
m_nStartYear = date.GetYear();
RedrawWindow();
return TRUE;
}
// ----------------------------------------------------------------------------
BOOL
CMiniCalendarCtrl::ResetRange()
{
if (m_dtAnchor.GetStatus() == COleDateTime::valid)
{
m_dtAnchor.SetStatus(COleDateTime::invalid);
NotifySelChanged();
Invalidate();
return TRUE;
}
m_dtAnchor.SetStatus(COleDateTime::invalid);
return FALSE;
}
// ----------------------------------------------------------------------------
LRESULT
CMiniCalendarCtrl::OnCreate(LPCREATESTRUCT lpcs)
{
hNotifyWindow = GetParent();
m_font[FontHeader].bBold = TRUE;
m_font[FontHeader].nFontSize = 10;
m_font[FontSpecialDayNumber].bBold = TRUE;
for (int i = 0; i < NUM_ELEMENTS(m_font); ++i)
CreateFont(i);
ApplyStyle(lpcs->style);
RecalcLayout();
AutoSize();
EnsureVisible(COleDateTime::GetCurrentTime());
SetMsgHandled(FALSE);
return 0;
}
// ----------------------------------------------------------------------------
LRESULT
CMiniCalendarCtrl::OnStyleChanged(UINT nType, LPSTYLESTRUCT lpss)
{
if (nType & GWL_STYLE)
ApplyStyle(lpss->styleNew);
SetMsgHandled(FALSE);
return 0;
}
// ----------------------------------------------------------------------------
LRESULT
CMiniCalendarCtrl::OnTimer(UINT nID, TIMERPROC /*fnProc*/)
{
switch (nID)
{
case ID_TIMER_FIRST:
KillTimer(ID_TIMER_FIRST);
if (m_nActiveElement & htHeader)
{
DoScroll(m_nActiveElement, m_nMonthsToScroll);
int nElapse = 250;
DWORD nRepeat = 40;
if (SystemParametersInfo(SPI_GETKEYBOARDSPEED, 0, &nRepeat, 0))
nElapse = 10000 / (5 * nRepeat + 25); // milli-seconds, approximated
SetTimer(ID_TIMER_REPEAT, nElapse);
}
break;
case ID_TIMER_REPEAT:
if (m_nActiveElement & htHeader)
DoScroll(m_nActiveElement, m_nMonthsToScroll);
else if (GetCapture() != m_hWnd)
KillTimer(ID_TIMER_REPEAT);
break;
default:
break;
}
return 0;
}
// ----------------------------------------------------------------------------
LRESULT
CMiniCalendarCtrl::OnMouseMove(UINT /*nFlags*/, CPoint point)
{
if (GetCapture() == m_hWnd)
{
m_nActiveElement &= ~htHeader;
HitTestInfo ht;
ht.ptHit = point;
if (HitTest(ht))
{
if ((m_nActiveElement & htDate) && (ht.nFlags & htDate) && ht.dtHit != m_dtSelect)
{
m_dtSelect = ht.dtHit;
if (ht.nFlags & (htBack | htNext))
DoScroll(ht.nFlags);
else
RedrawWindow();
NotifySelChanged();
}
if ((m_nActiveElement & (htBack | htNext)) &&
ht.nFlags == UINT(htHeader | m_nActiveElement))
{
ATLTRACE("ResumeScroll\n");
m_nActiveElement = ht.nFlags;
}
}
}
return 0;
}
// ----------------------------------------------------------------------------
LRESULT
CMiniCalendarCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
m_nActiveElement = 0;
HitTestInfo ht;
ht.ptHit = point;
if (HitTest(ht))
{
SetCapture();
m_nActiveElement = ht.nFlags;
if (m_nActiveElement & htHeader)
{
if (m_nActiveElement & (htBack | htNext))
{
DoScroll(ht.nFlags, m_nMonthsToScroll);
int nElapse = 250;
int nDelay = 0;
if (SystemParametersInfo(SPI_GETKEYBOARDDELAY, 0, &nDelay, 0))
nElapse += nDelay * 250; // all milli-seconds
SetTimer(ID_TIMER_FIRST, nElapse);
}
else
{
// Show month list here.
}
}
else if (m_nActiveElement & htButton)
{
}
else if (m_nActiveElement & htDate)
{
if (m_dtAnchor.GetStatus() != COleDateTime::valid || ! (nFlags & MK_SHIFT))
m_dtAnchor = ht.dtHit;
m_dtSelect = ht.dtHit;
if (m_nActiveElement & (htBack | htNext))
DoScroll(ht.nFlags);
else
RedrawWindow();
NotifySelChanged();
}
else
ResetRange();
}
return 0;
}
// ----------------------------------------------------------------------------
LRESULT
CMiniCalendarCtrl::OnLButtonUp(UINT /*nFlags*/, CPoint /*point*/)
{
ReleaseCapture();
m_nActiveElement = 0;
return 0;
}
// ----------------------------------------------------------------------------
LRESULT
CMiniCalendarCtrl::OnCaptureChanged(HWND /*hWnd*/)
{
m_nActiveElement = 0;
return 0;
}
// ----------------------------------------------------------------------------
void
CMiniCalendarCtrl::DrawBorder(CDCHandle dc, CRect& rectClient)
{
if (m_bShow3dBorder)
{
static const struct
{
COLORREF crTL;
COLORREF crBR;
} border[] =
{
{ COLOR_3DFACE, COLOR_BTNTEXT },
{ COLOR_3DHILIGHT, COLOR_3DSHADOW },
{ COLOR_3DFACE, COLOR_3DFACE },
{ COLOR_3DSHADOW, COLOR_3DHILIGHT }
};
for (int i = 0; i < NUM_ELEMENTS(border); ++i)
{
dc.Draw3dRect(rectClient,
GetSysColor(border[i].crTL),
GetSysColor(border[i].crBR));
rectClient.DeflateRect(1, 1);
}
}
}
// ----------------------------------------------------------------------------
int
CMiniCalendarCtrl::DrawHeader(CDCHandle dc, CPoint pt, int nCol, int nRow)
{
CRect rectHeader(0, 0, m_cellSize.cx, m_cyHeader);
rectHeader.OffsetRect(pt);
// Draw 'button' part
dc.Draw3dRect(rectHeader,
GetSysColor(COLOR_3DHILIGHT), GetSysColor(COLOR_3DSHADOW));
rectHeader.DeflateRect(1, 1);
dc.FillSolidRect(rectHeader, GetSysColor(COLOR_3DFACE));
rectHeader.DeflateRect(1, 1);
// Draw the text
const COleDateTime dt = GetMonthFromCell(nCol, nRow);
const CString str = dt.Format(HEADER_FORMAT);
SelectFont(dc, FontHeader);
dc.DrawText(str, str.GetLength(), rectHeader,
DT_CENTER | DT_VCENTER | DT_SINGLELINE);
// Draw arrow(s) (if required)
if (nRow == 0 && (nCol == 0 || nCol == m_nCols - 1))
{
rectHeader.SetRect(0, 0, m_cellSize.cx, m_cyHeader);
rectHeader.OffsetRect(pt);
if (nCol == 0)
MC_DrawArrow(dc, rectHeader, TRUE);
if (nCol == m_nCols - 1)
MC_DrawArrow(dc, rectHeader, FALSE);
}
return m_cyHeader;
}
// ----------------------------------------------------------------------------
int
CMiniCalendarCtrl::DrawDaysOfWeek(CDCHandle dc, CPoint pt, int /*nCol*/, int /*nRow*/)
{
const int cxColumn = m_dateSize.cx + m_daySpace.cx;
const int xBeg = pt.x + m_xCol;
SelectFont(dc, FontDayName);
// Draw day names.
CRect rectDate(xBeg, pt.y, xBeg + cxColumn, pt.y + m_cyDayNames);
for (int i = 1; i <= nDaysPerWeek; ++i)
{
dc.DrawText(GetDayOfWeekName(i), 1, rectDate,
DT_SINGLELINE | DT_RIGHT | DT_VCENTER);
rectDate.OffsetRect(cxColumn, 0);
}
rectDate.bottom += 4;
// Draw separator.
dc.SelectBrush(GetSysColorBrush(COLOR_3DSHADOW));
dc.PatBlt(xBeg, rectDate.bottom - 2,
cxColumn * nDaysPerWeek + m_daySpace.cx, 1, PATCOPY);
return rectDate.bottom - pt.y;
}
// ----------------------------------------------------------------------------
int
CMiniCalendarCtrl::DrawDays(CDCHandle dc, CPoint pt, int nCol, int nRow)
{
const int cxColumn = m_dateSize.cx + m_daySpace.cx;
const int xBeg = pt.x + m_xCol;
COleDateTime dt = GetFirstDayInCell(nCol, nRow);
const COleDateTime dtCell = GetMonthFromCell(nCol, nRow);
const COleDateTime dtToday = COleDateTime::GetCurrentTime();
const BOOL bBegRange = nCol == 0 && nRow == 0;
const BOOL bEndRange = nCol == m_nCols - 1 && nRow == m_nRows - 1;
const int nThisMonth = dtCell.GetMonth();
// Determine position of 'today' in this cell (if required).
CPoint ptToday(-1, -1);
if (m_bHighlightToday &&
dtToday.GetMonth() == nThisMonth && dtToday.GetYear() == dtCell.GetYear())
{
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -