Files
wxWidgets/src/generic/calctrl.cpp
Stefan Csomor 3ff066a4ec macro naming changes
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@23376 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2003-09-04 01:19:40 +00:00

1842 lines
53 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// Name: generic/calctrl.cpp
// Purpose: implementation fo the generic wxCalendarCtrl
// Author: Vadim Zeitlin
// Modified by:
// Created: 29.12.99
// RCS-ID: $Id$
// Copyright: (c) 1999 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
// ============================================================================
// declarations
// ============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
#pragma implementation "calctrl.h"
#endif
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#ifndef WX_PRECOMP
#include "wx/dcclient.h"
#include "wx/settings.h"
#include "wx/brush.h"
#include "wx/combobox.h"
#include "wx/stattext.h"
#include "wx/textctrl.h"
#endif //WX_PRECOMP
#if wxUSE_CALENDARCTRL
#include "wx/spinctrl.h"
#include "wx/calctrl.h"
#define DEBUG_PAINT 0
// ----------------------------------------------------------------------------
// private classes
// ----------------------------------------------------------------------------
class wxMonthComboBox : public wxComboBox
{
public:
wxMonthComboBox(wxCalendarCtrl *cal);
void OnMonthChange(wxCommandEvent& event) { m_cal->OnMonthChange(event); }
private:
wxCalendarCtrl *m_cal;
DECLARE_EVENT_TABLE()
DECLARE_NO_COPY_CLASS(wxMonthComboBox)
};
class wxYearSpinCtrl : public wxSpinCtrl
{
public:
wxYearSpinCtrl(wxCalendarCtrl *cal);
void OnYearTextChange(wxCommandEvent& event)
{
m_cal->SetUserChangedYear();
m_cal->OnYearChange(event);
}
void OnYearChange(wxSpinEvent& event) { m_cal->OnYearChange(event); }
private:
wxCalendarCtrl *m_cal;
DECLARE_EVENT_TABLE()
DECLARE_NO_COPY_CLASS(wxYearSpinCtrl)
};
// ----------------------------------------------------------------------------
// wxWin macros
// ----------------------------------------------------------------------------
BEGIN_EVENT_TABLE(wxCalendarCtrl, wxControl)
EVT_PAINT(wxCalendarCtrl::OnPaint)
EVT_CHAR(wxCalendarCtrl::OnChar)
EVT_LEFT_DOWN(wxCalendarCtrl::OnClick)
EVT_LEFT_DCLICK(wxCalendarCtrl::OnDClick)
END_EVENT_TABLE()
BEGIN_EVENT_TABLE(wxMonthComboBox, wxComboBox)
EVT_COMBOBOX(-1, wxMonthComboBox::OnMonthChange)
END_EVENT_TABLE()
BEGIN_EVENT_TABLE(wxYearSpinCtrl, wxSpinCtrl)
EVT_TEXT(-1, wxYearSpinCtrl::OnYearTextChange)
EVT_SPINCTRL(-1, wxYearSpinCtrl::OnYearChange)
END_EVENT_TABLE()
#if wxUSE_EXTENDED_RTTI
WX_DEFINE_FLAGS( wxCalendarCtrlStyle )
wxBEGIN_FLAGS( wxCalendarCtrlStyle )
// new style border flags, we put them first to
// use them for streaming out
wxFLAGS_MEMBER(wxBORDER_SIMPLE)
wxFLAGS_MEMBER(wxBORDER_SUNKEN)
wxFLAGS_MEMBER(wxBORDER_DOUBLE)
wxFLAGS_MEMBER(wxBORDER_RAISED)
wxFLAGS_MEMBER(wxBORDER_STATIC)
wxFLAGS_MEMBER(wxBORDER_NONE)
// old style border flags
wxFLAGS_MEMBER(wxSIMPLE_BORDER)
wxFLAGS_MEMBER(wxSUNKEN_BORDER)
wxFLAGS_MEMBER(wxDOUBLE_BORDER)
wxFLAGS_MEMBER(wxRAISED_BORDER)
wxFLAGS_MEMBER(wxSTATIC_BORDER)
wxFLAGS_MEMBER(wxNO_BORDER)
// standard window styles
wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
wxFLAGS_MEMBER(wxCLIP_CHILDREN)
wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
wxFLAGS_MEMBER(wxWANTS_CHARS)
wxFLAGS_MEMBER(wxNO_FULL_REPAINT_ON_RESIZE)
wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
wxFLAGS_MEMBER(wxVSCROLL)
wxFLAGS_MEMBER(wxHSCROLL)
wxFLAGS_MEMBER(wxCAL_SUNDAY_FIRST)
wxFLAGS_MEMBER(wxCAL_MONDAY_FIRST)
wxFLAGS_MEMBER(wxCAL_SHOW_HOLIDAYS)
wxFLAGS_MEMBER(wxCAL_NO_YEAR_CHANGE)
wxFLAGS_MEMBER(wxCAL_NO_MONTH_CHANGE)
wxFLAGS_MEMBER(wxCAL_SEQUENTIAL_MONTH_SELECTION)
wxFLAGS_MEMBER(wxCAL_SHOW_SURROUNDING_WEEKS)
wxEND_FLAGS( wxCalendarCtrlStyle )
IMPLEMENT_DYNAMIC_CLASS_XTI(wxCalendarCtrl, wxControl,"wx/calctrl.h")
wxBEGIN_PROPERTIES_TABLE(wxCalendarCtrl)
wxEVENT_RANGE_PROPERTY( Updated , wxEVT_CALENDAR_SEL_CHANGED , wxEVT_CALENDAR_WEEKDAY_CLICKED , wxCalendarEvent )
wxHIDE_PROPERTY( Children )
wxPROPERTY( Date,wxDateTime, SetDate , GetDate, , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
wxPROPERTY_FLAGS( WindowStyle , wxCalendarCtrlStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
wxEND_PROPERTIES_TABLE()
wxBEGIN_HANDLERS_TABLE(wxCalendarCtrl)
wxEND_HANDLERS_TABLE()
wxCONSTRUCTOR_6( wxCalendarCtrl , wxWindow* , Parent , wxWindowID , Id , wxDateTime , Date , wxPoint , Position , wxSize , Size , long , WindowStyle )
#else
IMPLEMENT_DYNAMIC_CLASS(wxCalendarCtrl, wxControl)
#endif
IMPLEMENT_DYNAMIC_CLASS(wxCalendarEvent, wxCommandEvent)
// ----------------------------------------------------------------------------
// events
// ----------------------------------------------------------------------------
DEFINE_EVENT_TYPE(wxEVT_CALENDAR_SEL_CHANGED)
DEFINE_EVENT_TYPE(wxEVT_CALENDAR_DAY_CHANGED)
DEFINE_EVENT_TYPE(wxEVT_CALENDAR_MONTH_CHANGED)
DEFINE_EVENT_TYPE(wxEVT_CALENDAR_YEAR_CHANGED)
DEFINE_EVENT_TYPE(wxEVT_CALENDAR_DOUBLECLICKED)
DEFINE_EVENT_TYPE(wxEVT_CALENDAR_WEEKDAY_CLICKED)
// ============================================================================
// implementation
// ============================================================================
// ----------------------------------------------------------------------------
// wxMonthComboBox and wxYearSpinCtrl
// ----------------------------------------------------------------------------
wxMonthComboBox::wxMonthComboBox(wxCalendarCtrl *cal)
: wxComboBox(cal->GetParent(), -1,
wxEmptyString,
wxDefaultPosition,
wxDefaultSize,
0, NULL,
wxCB_READONLY | wxCLIP_SIBLINGS)
{
m_cal = cal;
wxDateTime::Month m;
for ( m = wxDateTime::Jan; m < wxDateTime::Inv_Month; wxNextMonth(m) )
{
Append(wxDateTime::GetMonthName(m));
}
SetSelection(m_cal->GetDate().GetMonth());
SetSize(-1, -1, -1, -1, wxSIZE_AUTO_WIDTH|wxSIZE_AUTO_HEIGHT);
}
wxYearSpinCtrl::wxYearSpinCtrl(wxCalendarCtrl *cal)
: wxSpinCtrl(cal->GetParent(), -1,
cal->GetDate().Format(_T("%Y")),
wxDefaultPosition,
wxDefaultSize,
wxSP_ARROW_KEYS | wxCLIP_SIBLINGS,
-4300, 10000, cal->GetDate().GetYear())
{
m_cal = cal;
}
// ----------------------------------------------------------------------------
// wxCalendarCtrl
// ----------------------------------------------------------------------------
wxCalendarCtrl::wxCalendarCtrl(wxWindow *parent,
wxWindowID id,
const wxDateTime& date,
const wxPoint& pos,
const wxSize& size,
long style,
const wxString& name)
{
Init();
(void)Create(parent, id, date, pos, size, style, name);
}
void wxCalendarCtrl::Init()
{
m_comboMonth = NULL;
m_spinYear = NULL;
m_staticYear = NULL;
m_staticMonth = NULL;
m_userChangedYear = FALSE;
m_widthCol =
m_heightRow = 0;
wxDateTime::WeekDay wd;
for ( wd = wxDateTime::Sun; wd < wxDateTime::Inv_WeekDay; wxNextWDay(wd) )
{
m_weekdays[wd] = wxDateTime::GetWeekDayName(wd, wxDateTime::Name_Abbr);
}
for ( size_t n = 0; n < WXSIZEOF(m_attrs); n++ )
{
m_attrs[n] = NULL;
}
m_colHighlightFg = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
m_colHighlightBg = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
m_colHolidayFg = *wxRED;
// don't set m_colHolidayBg - by default, same as our bg colour
m_colHeaderFg = *wxBLUE;
m_colHeaderBg = *wxLIGHT_GREY;
}
bool wxCalendarCtrl::Create(wxWindow *parent,
wxWindowID id,
const wxDateTime& date,
const wxPoint& pos,
const wxSize& size,
long style,
const wxString& name)
{
if ( !wxControl::Create(parent, id, pos, size,
style | wxCLIP_CHILDREN | wxWANTS_CHARS,
wxDefaultValidator, name) )
{
return FALSE;
}
// needed to get the arrow keys normally used for the dialog navigation
SetWindowStyle(style | wxWANTS_CHARS);
m_date = date.IsValid() ? date : wxDateTime::Today();
m_lowdate = wxDefaultDateTime;
m_highdate = wxDefaultDateTime;
if ( !HasFlag(wxCAL_SEQUENTIAL_MONTH_SELECTION) )
{
m_spinYear = new wxYearSpinCtrl(this);
m_staticYear = new wxStaticText(GetParent(), -1, m_date.Format(_T("%Y")),
wxDefaultPosition, wxDefaultSize,
wxALIGN_CENTRE);
m_comboMonth = new wxMonthComboBox(this);
m_staticMonth = new wxStaticText(GetParent(), -1, m_date.Format(_T("%B")),
wxDefaultPosition, wxDefaultSize,
wxALIGN_CENTRE);
}
ShowCurrentControls();
wxSize sizeReal;
if ( size.x == -1 || size.y == -1 )
{
sizeReal = DoGetBestSize();
if ( size.x != -1 )
sizeReal.x = size.x;
if ( size.y != -1 )
sizeReal.y = size.y;
}
else
{
sizeReal = size;
}
// we need to set the position as well because the main control position
// is not the same as the one specified in pos if we have the controls
// above it
SetSize(pos.x, pos.y, sizeReal.x, sizeReal.y);
SetBackgroundColour(*wxWHITE);
SetFont(*wxSWISS_FONT);
SetHolidayAttrs();
return TRUE;
}
wxCalendarCtrl::~wxCalendarCtrl()
{
for ( size_t n = 0; n < WXSIZEOF(m_attrs); n++ )
{
delete m_attrs[n];
}
}
// ----------------------------------------------------------------------------
// forward wxWin functions to subcontrols
// ----------------------------------------------------------------------------
bool wxCalendarCtrl::Destroy()
{
if ( m_staticYear )
m_staticYear->Destroy();
if ( m_spinYear )
m_spinYear->Destroy();
if ( m_comboMonth )
m_comboMonth->Destroy();
if ( m_staticMonth )
m_staticMonth->Destroy();
m_staticYear = NULL;
m_spinYear = NULL;
m_comboMonth = NULL;
m_staticMonth = NULL;
return wxControl::Destroy();
}
bool wxCalendarCtrl::Show(bool show)
{
if ( !wxControl::Show(show) )
{
return FALSE;
}
if ( !(GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION) )
{
if ( GetMonthControl() )
{
GetMonthControl()->Show(show);
GetYearControl()->Show(show);
}
}
return TRUE;
}
bool wxCalendarCtrl::Enable(bool enable)
{
if ( !wxControl::Enable(enable) )
{
return FALSE;
}
if ( !(GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION) )
{
GetMonthControl()->Enable(enable);
GetYearControl()->Enable(enable);
}
return TRUE;
}
// ----------------------------------------------------------------------------
// enable/disable month/year controls
// ----------------------------------------------------------------------------
void wxCalendarCtrl::ShowCurrentControls()
{
if ( !HasFlag(wxCAL_SEQUENTIAL_MONTH_SELECTION) )
{
if ( AllowMonthChange() )
{
m_comboMonth->Show();
m_staticMonth->Hide();
if ( AllowYearChange() )
{
m_spinYear->Show();
m_staticYear->Hide();
// skip the rest
return;
}
}
else
{
m_comboMonth->Hide();
m_staticMonth->Show();
}
// year change not allowed here
m_spinYear->Hide();
m_staticYear->Show();
}
}
wxControl *wxCalendarCtrl::GetMonthControl() const
{
return AllowMonthChange() ? (wxControl *)m_comboMonth : (wxControl *)m_staticMonth;
}
wxControl *wxCalendarCtrl::GetYearControl() const
{
return AllowYearChange() ? (wxControl *)m_spinYear : (wxControl *)m_staticYear;
}
void wxCalendarCtrl::EnableYearChange(bool enable)
{
if ( enable != AllowYearChange() )
{
long style = GetWindowStyle();
if ( enable )
style &= ~wxCAL_NO_YEAR_CHANGE;
else
style |= wxCAL_NO_YEAR_CHANGE;
SetWindowStyle(style);
ShowCurrentControls();
if ( GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION )
{
Refresh();
}
}
}
void wxCalendarCtrl::EnableMonthChange(bool enable)
{
if ( enable != AllowMonthChange() )
{
long style = GetWindowStyle();
if ( enable )
style &= ~wxCAL_NO_MONTH_CHANGE;
else
style |= wxCAL_NO_MONTH_CHANGE;
SetWindowStyle(style);
ShowCurrentControls();
if ( GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION )
{
Refresh();
}
}
}
// ----------------------------------------------------------------------------
// changing date
// ----------------------------------------------------------------------------
bool wxCalendarCtrl::SetDate(const wxDateTime& date)
{
bool retval = TRUE;
bool sameMonth = m_date.GetMonth() == date.GetMonth(),
sameYear = m_date.GetYear() == date.GetYear();
if ( IsDateInRange(date) )
{
if ( sameMonth && sameYear )
{
// just change the day
ChangeDay(date);
}
else
{
if ( AllowMonthChange() && (AllowYearChange() || sameYear) )
{
// change everything
m_date = date;
if ( !(GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION) )
{
// update the controls
m_comboMonth->SetSelection(m_date.GetMonth());
if ( AllowYearChange() )
{
if ( !m_userChangedYear )
m_spinYear->SetValue(m_date.Format(_T("%Y")));
}
}
// as the month changed, holidays did too
SetHolidayAttrs();
// update the calendar
Refresh();
}
else
{
// forbidden
retval = FALSE;
}
}
}
m_userChangedYear = FALSE;
return retval;
}
void wxCalendarCtrl::ChangeDay(const wxDateTime& date)
{
if ( m_date != date )
{
// we need to refresh the row containing the old date and the one
// containing the new one
wxDateTime dateOld = m_date;
m_date = date;
RefreshDate(dateOld);
// if the date is in the same row, it was already drawn correctly
if ( GetWeek(m_date) != GetWeek(dateOld) )
{
RefreshDate(m_date);
}
}
}
void wxCalendarCtrl::SetDateAndNotify(const wxDateTime& date)
{
wxDateTime::Tm tm1 = m_date.GetTm(),
tm2 = date.GetTm();
wxEventType type;
if ( tm1.year != tm2.year )
type = wxEVT_CALENDAR_YEAR_CHANGED;
else if ( tm1.mon != tm2.mon )
type = wxEVT_CALENDAR_MONTH_CHANGED;
else if ( tm1.mday != tm2.mday )
type = wxEVT_CALENDAR_DAY_CHANGED;
else
return;
if ( SetDate(date) )
{
GenerateEvents(type, wxEVT_CALENDAR_SEL_CHANGED);
}
}
// ----------------------------------------------------------------------------
// date range
// ----------------------------------------------------------------------------
bool wxCalendarCtrl::SetLowerDateLimit(const wxDateTime& date /* = wxDefaultDateTime */)
{
bool retval = TRUE;
if ( !(date.IsValid()) || ( ( m_highdate.IsValid() ) ? ( date <= m_highdate ) : TRUE ) )
{
m_lowdate = date;
}
else
{
retval = FALSE;
}
return retval;
}
bool wxCalendarCtrl::SetUpperDateLimit(const wxDateTime& date /* = wxDefaultDateTime */)
{
bool retval = TRUE;
if ( !(date.IsValid()) || ( ( m_lowdate.IsValid() ) ? ( date >= m_lowdate ) : TRUE ) )
{
m_highdate = date;
}
else
{
retval = FALSE;
}
return retval;
}
bool wxCalendarCtrl::SetDateRange(const wxDateTime& lowerdate /* = wxDefaultDateTime */, const wxDateTime& upperdate /* = wxDefaultDateTime */)
{
bool retval = TRUE;
if (
( !( lowerdate.IsValid() ) || ( ( upperdate.IsValid() ) ? ( lowerdate <= upperdate ) : TRUE ) ) &&
( !( upperdate.IsValid() ) || ( ( lowerdate.IsValid() ) ? ( upperdate >= lowerdate ) : TRUE ) ) )
{
m_lowdate = lowerdate;
m_highdate = upperdate;
}
else
{
retval = FALSE;
}
return retval;
}
// ----------------------------------------------------------------------------
// date helpers
// ----------------------------------------------------------------------------
wxDateTime wxCalendarCtrl::GetStartDate() const
{
wxDateTime::Tm tm = m_date.GetTm();
wxDateTime date = wxDateTime(1, tm.mon, tm.year);
// rewind back
date.SetToPrevWeekDay(GetWindowStyle() & wxCAL_MONDAY_FIRST
? wxDateTime::Mon : wxDateTime::Sun);
if ( GetWindowStyle() & wxCAL_SHOW_SURROUNDING_WEEKS )
{
// We want to offset the calendar if we start on the first..
if ( date.GetDay() == 1 )
{
date -= wxDateSpan::Week();
}
}
return date;
}
bool wxCalendarCtrl::IsDateShown(const wxDateTime& date) const
{
if ( !(GetWindowStyle() & wxCAL_SHOW_SURROUNDING_WEEKS) )
{
return date.GetMonth() == m_date.GetMonth();
}
else
{
return TRUE;
}
}
bool wxCalendarCtrl::IsDateInRange(const wxDateTime& date) const
{
bool retval = TRUE;
// Check if the given date is in the range specified
retval = ( ( ( m_lowdate.IsValid() ) ? ( date >= m_lowdate ) : TRUE )
&& ( ( m_highdate.IsValid() ) ? ( date <= m_highdate ) : TRUE ) );
return retval;
}
bool wxCalendarCtrl::ChangeYear(wxDateTime* target) const
{
bool retval = FALSE;
if ( !(IsDateInRange(*target)) )
{
if ( target->GetYear() < m_date.GetYear() )
{
if ( target->GetYear() >= GetLowerDateLimit().GetYear() )
{
*target = GetLowerDateLimit();
retval = TRUE;
}
else
{
*target = m_date;
}
}
else
{
if ( target->GetYear() <= GetUpperDateLimit().GetYear() )
{
*target = GetUpperDateLimit();
retval = TRUE;
}
else
{
*target = m_date;
}
}
}
else
{
retval = TRUE;
}
return retval;
}
bool wxCalendarCtrl::ChangeMonth(wxDateTime* target) const
{
bool retval = TRUE;
if ( !(IsDateInRange(*target)) )
{
retval = FALSE;
if ( target->GetMonth() < m_date.GetMonth() )
{
*target = GetLowerDateLimit();
}
else
{
*target = GetUpperDateLimit();
}
}
return retval;
}
size_t wxCalendarCtrl::GetWeek(const wxDateTime& date) const
{
size_t retval = date.GetWeekOfMonth(GetWindowStyle() & wxCAL_MONDAY_FIRST
? wxDateTime::Monday_First
: wxDateTime::Sunday_First);
if ( (GetWindowStyle() & wxCAL_SHOW_SURROUNDING_WEEKS) )
{
// we need to offset an extra week if we "start" on the 1st of the month
wxDateTime::Tm tm = date.GetTm();
wxDateTime datetest = wxDateTime(1, tm.mon, tm.year);
// rewind back
datetest.SetToPrevWeekDay(GetWindowStyle() & wxCAL_MONDAY_FIRST
? wxDateTime::Mon : wxDateTime::Sun);
if ( datetest.GetDay() == 1 )
{
retval += 1;
}
}
return retval;
}
// ----------------------------------------------------------------------------
// size management
// ----------------------------------------------------------------------------
// this is a composite control and it must arrange its parts each time its
// size or position changes: the combobox and spinctrl are along the top of
// the available area and the calendar takes up therest of the space
// the static controls are supposed to be always smaller than combo/spin so we
// always use the latter for size calculations and position the static to take
// the same space
// the constants used for the layout
#define VERT_MARGIN 5 // distance between combo and calendar
#ifdef __WXMAC__
#define HORZ_MARGIN 5 // spin
#else
#define HORZ_MARGIN 15 // spin
#endif
wxSize wxCalendarCtrl::DoGetBestSize() const
{
// calc the size of the calendar
((wxCalendarCtrl *)this)->RecalcGeometry(); // const_cast
wxCoord width = 7*m_widthCol,
height = 7*m_heightRow + m_rowOffset + VERT_MARGIN;
if ( !HasFlag(wxCAL_SEQUENTIAL_MONTH_SELECTION) )
{
// the combobox doesn't report its height correctly (it returns the
// height including the drop down list) so don't use it
height += m_spinYear->GetBestSize().y;
}
if ( !HasFlag(wxBORDER_NONE) )
{
// the border would clip the last line otherwise
height += 6;
width += 4;
}
return wxSize(width, height);
}
void wxCalendarCtrl::DoSetSize(int x, int y,
int width, int height,
int sizeFlags)
{
wxControl::DoSetSize(x, y, width, height, sizeFlags);
}
void wxCalendarCtrl::DoMoveWindow(int x, int y, int width, int height)
{
int yDiff;
if ( !HasFlag(wxCAL_SEQUENTIAL_MONTH_SELECTION) )
{
wxSize sizeCombo = m_comboMonth->GetSize();
wxSize sizeStatic = m_staticMonth->GetSize();
wxSize sizeSpin = m_spinYear->GetSize();
int dy = (sizeCombo.y - sizeStatic.y) / 2;
/*
In the calender the size of the combobox for the year
is just defined by a margin from the month combobox to
the left border. While in wxUniv the year control can't
show all 4 digits, in wxMsw it show almost twice as
much. Instead the year should use it's best size and be
left aligned to the calendar. Just in case the month in
any language is longer than it has space in the
calendar it is shortend.This way the year always can
show the 4 digits.
This patch relies on the fact that a combobox has a
good best size implementation. This is not the case
with wxMSW but I don't know why.
Otto Wyss
*/
#ifdef __WXUNIVERSAL__
if (sizeCombo.x + HORZ_MARGIN - sizeSpin.x > width)
{
m_comboMonth->SetSize(x, y, width - HORZ_MARGIN - sizeSpin.x, sizeCombo.y);
}
else
{
m_comboMonth->Move(x, y);
}
m_staticMonth->Move(x, y + dy);
m_spinYear->Move(x + width - sizeSpin.x, y);
m_staticYear->Move(x + width - sizeSpin.x, y + dy);
#else
m_comboMonth->Move(x, y);
m_staticMonth->SetSize(x, y + dy, sizeCombo.x, sizeStatic.y);
int xDiff = sizeCombo.x + HORZ_MARGIN;
m_spinYear->SetSize(x + xDiff, y, width - xDiff, sizeCombo.y);
m_staticYear->SetSize(x + xDiff, y + dy, width - xDiff, sizeStatic.y);
#endif
yDiff = wxMax(sizeSpin.y, sizeCombo.y) + VERT_MARGIN;
}
else // no controls on the top
{
yDiff = 0;
}
wxControl::DoMoveWindow(x, y + yDiff, width, height - yDiff);
}
void wxCalendarCtrl::DoGetPosition(int *x, int *y) const
{
wxControl::DoGetPosition(x, y);
if ( !(GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION) )
{
// our real top corner is not in this position
if ( y )
{
*y -= GetMonthControl()->GetSize().y + VERT_MARGIN;
}
}
}
void wxCalendarCtrl::DoGetSize(int *width, int *height) const
{
wxControl::DoGetSize(width, height);
if ( !(GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION) )
{
// our real height is bigger
if ( height && GetMonthControl())
{
*height += GetMonthControl()->GetSize().y + VERT_MARGIN;
}
}
}
void wxCalendarCtrl::RecalcGeometry()
{
if ( m_widthCol != 0 )
return;
wxClientDC dc(this);
dc.SetFont(m_font);
// determine the column width (we assume that the weekday names are always
// wider (in any language) than the numbers)
m_widthCol = 0;
wxDateTime::WeekDay wd;
for ( wd = wxDateTime::Sun; wd < wxDateTime::Inv_WeekDay; wxNextWDay(wd) )
{
wxCoord width;
dc.GetTextExtent(m_weekdays[wd], &width, &m_heightRow);
if ( width > m_widthCol )
{
m_widthCol = width;
}
}
// leave some margins
m_widthCol += 2;
m_heightRow += 2;
m_rowOffset = (GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION) ? m_heightRow : 0; // conditional in relation to style
}
// ----------------------------------------------------------------------------
// drawing
// ----------------------------------------------------------------------------
void wxCalendarCtrl::OnPaint(wxPaintEvent& WXUNUSED(event))
{
wxPaintDC dc(this);
dc.SetFont(m_font);
RecalcGeometry();
#if DEBUG_PAINT
wxLogDebug("--- starting to paint, selection: %s, week %u\n",
m_date.Format("%a %d-%m-%Y %H:%M:%S").c_str(),
GetWeek(m_date));
#endif
wxCoord y = 0;
if ( HasFlag(wxCAL_SEQUENTIAL_MONTH_SELECTION) )
{
// draw the sequential month-selector
dc.SetBackgroundMode(wxTRANSPARENT);
dc.SetTextForeground(*wxBLACK);
dc.SetBrush(wxBrush(m_colHeaderBg, wxSOLID));
dc.SetPen(wxPen(m_colHeaderBg, 1, wxSOLID));
dc.DrawRectangle(0, y, GetClientSize().x, m_heightRow);
// Get extent of month-name + year
wxCoord monthw, monthh;
wxString headertext = m_date.Format(wxT("%B %Y"));
dc.GetTextExtent(headertext, &monthw, &monthh);
// draw month-name centered above weekdays
wxCoord monthx = ((m_widthCol * 7) - monthw) / 2;
wxCoord monthy = ((m_heightRow - monthh) / 2) + y;
dc.DrawText(headertext, monthx, monthy);
// calculate the "month-arrows"
wxPoint leftarrow[3];
wxPoint rightarrow[3];
int arrowheight = monthh / 2;
leftarrow[0] = wxPoint(0, arrowheight / 2);
leftarrow[1] = wxPoint(arrowheight / 2, 0);
leftarrow[2] = wxPoint(arrowheight / 2, arrowheight - 1);
rightarrow[0] = wxPoint(0, 0);
rightarrow[1] = wxPoint(arrowheight / 2, arrowheight / 2);
rightarrow[2] = wxPoint(0, arrowheight - 1);
// draw the "month-arrows"
wxCoord arrowy = (m_heightRow - arrowheight) / 2;
wxCoord larrowx = (m_widthCol - (arrowheight / 2)) / 2;
wxCoord rarrowx = ((m_widthCol - (arrowheight / 2)) / 2) + m_widthCol*6;
m_leftArrowRect = wxRect(0, 0, 0, 0);
m_rightArrowRect = wxRect(0, 0, 0, 0);
if ( AllowMonthChange() )
{
wxDateTime ldpm = wxDateTime(1,m_date.GetMonth(), m_date.GetYear()) - wxDateSpan::Day(); // last day prev month
// Check if range permits change
if ( IsDateInRange(ldpm) && ( ( ldpm.GetYear() == m_date.GetYear() ) ? TRUE : AllowYearChange() ) )
{
m_leftArrowRect = wxRect(larrowx - 3, arrowy - 3, (arrowheight / 2) + 8, (arrowheight + 6));
dc.SetBrush(wxBrush(*wxBLACK, wxSOLID));
dc.SetPen(wxPen(*wxBLACK, 1, wxSOLID));
dc.DrawPolygon(3, leftarrow, larrowx , arrowy, wxWINDING_RULE);
dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.DrawRectangle(m_leftArrowRect);
}
wxDateTime fdnm = wxDateTime(1,m_date.GetMonth(), m_date.GetYear()) + wxDateSpan::Month(); // first day next month
if ( IsDateInRange(fdnm) && ( ( fdnm.GetYear() == m_date.GetYear() ) ? TRUE : AllowYearChange() ) )
{
m_rightArrowRect = wxRect(rarrowx - 4, arrowy - 3, (arrowheight / 2) + 8, (arrowheight + 6));
dc.SetBrush(wxBrush(*wxBLACK, wxSOLID));
dc.SetPen(wxPen(*wxBLACK, 1, wxSOLID));
dc.DrawPolygon(3, rightarrow, rarrowx , arrowy, wxWINDING_RULE);
dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.DrawRectangle(m_rightArrowRect);
}
}
y += m_heightRow;
}
// first draw the week days
if ( IsExposed(0, y, 7*m_widthCol, m_heightRow) )
{
#if DEBUG_PAINT
wxLogDebug("painting the header");
#endif
dc.SetBackgroundMode(wxTRANSPARENT);
dc.SetTextForeground(m_colHeaderFg);
dc.SetBrush(wxBrush(m_colHeaderBg, wxSOLID));
dc.SetPen(wxPen(m_colHeaderBg, 1, wxSOLID));
dc.DrawRectangle(0, y, GetClientSize().x, m_heightRow);
bool startOnMonday = (GetWindowStyle() & wxCAL_MONDAY_FIRST) != 0;
for ( size_t wd = 0; wd < 7; wd++ )
{
size_t n;
if ( startOnMonday )
n = wd == 6 ? 0 : wd + 1;
else
n = wd;
wxCoord dayw, dayh;
dc.GetTextExtent(m_weekdays[n], &dayw, &dayh);
dc.DrawText(m_weekdays[n], (wd*m_widthCol) + ((m_widthCol- dayw) / 2), y); // center the day-name
}
}
// then the calendar itself
dc.SetTextForeground(*wxBLACK);
//dc.SetFont(*wxNORMAL_FONT);
y += m_heightRow;
wxDateTime date = GetStartDate();
#if DEBUG_PAINT
wxLogDebug("starting calendar from %s\n",
date.Format("%a %d-%m-%Y %H:%M:%S").c_str());
#endif
dc.SetBackgroundMode(wxSOLID);
for ( size_t nWeek = 1; nWeek <= 6; nWeek++, y += m_heightRow )
{
// if the update region doesn't intersect this row, don't paint it
if ( !IsExposed(0, y, 7*m_widthCol, m_heightRow - 1) )
{
date += wxDateSpan::Week();
continue;
}
#if DEBUG_PAINT
wxLogDebug("painting week %d at y = %d\n", nWeek, y);
#endif
for ( size_t wd = 0; wd < 7; wd++ )
{
if ( IsDateShown(date) )
{
// don't use wxDate::Format() which prepends 0s
unsigned int day = date.GetDay();
wxString dayStr = wxString::Format(_T("%u"), day);
wxCoord width;
dc.GetTextExtent(dayStr, &width, (wxCoord *)NULL);
bool changedColours = FALSE,
changedFont = FALSE;
bool isSel = FALSE;
wxCalendarDateAttr *attr = NULL;
if ( date.GetMonth() != m_date.GetMonth() || !IsDateInRange(date) )
{
// surrounding week or out-of-range
// draw "disabled"
dc.SetTextForeground(*wxLIGHT_GREY);
changedColours = TRUE;
}
else
{
isSel = date.IsSameDate(m_date);
attr = m_attrs[day - 1];
if ( isSel )
{
dc.SetTextForeground(m_colHighlightFg);
dc.SetTextBackground(m_colHighlightBg);
changedColours = TRUE;
}
else if ( attr )
{
wxColour colFg, colBg;
if ( attr->IsHoliday() )
{
colFg = m_colHolidayFg;
colBg = m_colHolidayBg;
}
else
{
colFg = attr->GetTextColour();
colBg = attr->GetBackgroundColour();
}
if ( colFg.Ok() )
{
dc.SetTextForeground(colFg);
changedColours = TRUE;
}
if ( colBg.Ok() )
{
dc.SetTextBackground(colBg);
changedColours = TRUE;
}
if ( attr->HasFont() )
{
dc.SetFont(attr->GetFont());
changedFont = TRUE;
}
}
}
wxCoord x = wd*m_widthCol + (m_widthCol - width) / 2;
dc.DrawText(dayStr, x, y + 1);
if ( !isSel && attr && attr->HasBorder() )
{
wxColour colBorder;
if ( attr->HasBorderColour() )
{
colBorder = attr->GetBorderColour();
}
else
{
colBorder = m_foregroundColour;
}
wxPen pen(colBorder, 1, wxSOLID);
dc.SetPen(pen);
dc.SetBrush(*wxTRANSPARENT_BRUSH);
switch ( attr->GetBorder() )
{
case wxCAL_BORDER_SQUARE:
dc.DrawRectangle(x - 2, y,
width + 4, m_heightRow);
break;
case wxCAL_BORDER_ROUND:
dc.DrawEllipse(x - 2, y,
width + 4, m_heightRow);
break;
default:
wxFAIL_MSG(_T("unknown border type"));
}
}
if ( changedColours )
{
dc.SetTextForeground(m_foregroundColour);
dc.SetTextBackground(m_backgroundColour);
}
if ( changedFont )
{
dc.SetFont(m_font);
}
}
//else: just don't draw it
date += wxDateSpan::Day();
}
}
// Greying out out-of-range background
bool showSurrounding = (GetWindowStyle() & wxCAL_SHOW_SURROUNDING_WEEKS) != 0;
date = ( showSurrounding ) ? GetStartDate() : wxDateTime(1, m_date.GetMonth(), m_date.GetYear());
if ( !IsDateInRange(date) )
{
wxDateTime firstOOR = GetLowerDateLimit() - wxDateSpan::Day(); // first out-of-range
wxBrush oorbrush = *wxLIGHT_GREY_BRUSH;
oorbrush.SetStyle(wxFDIAGONAL_HATCH);
HighlightRange(&dc, date, firstOOR, wxTRANSPARENT_PEN, &oorbrush);
}
date = ( showSurrounding ) ? GetStartDate() + wxDateSpan::Weeks(6) - wxDateSpan::Day() : wxDateTime().SetToLastMonthDay(m_date.GetMonth(), m_date.GetYear());
if ( !IsDateInRange(date) )
{
wxDateTime firstOOR = GetUpperDateLimit() + wxDateSpan::Day(); // first out-of-range
wxBrush oorbrush = *wxLIGHT_GREY_BRUSH;
oorbrush.SetStyle(wxFDIAGONAL_HATCH);
HighlightRange(&dc, firstOOR, date, wxTRANSPARENT_PEN, &oorbrush);
}
#if DEBUG_PAINT
wxLogDebug("+++ finished painting");
#endif
}
void wxCalendarCtrl::RefreshDate(const wxDateTime& date)
{
RecalcGeometry();
wxRect rect;
// always refresh the whole row at once because our OnPaint() will draw
// the whole row anyhow - and this allows the small optimisation in
// OnClick() below to work
rect.x = 0;
rect.y = (m_heightRow * GetWeek(date)) + m_rowOffset;
rect.width = 7*m_widthCol;
rect.height = m_heightRow;
#ifdef __WXMSW__
// VZ: for some reason, the selected date seems to occupy more space under
// MSW - this is probably some bug in the font size calculations, but I
// don't know where exactly. This fix is ugly and leads to more
// refreshes than really needed, but without it the selected days
// leaves even more ugly underscores on screen.
rect.Inflate(0, 1);
#endif // MSW
#if DEBUG_PAINT
wxLogDebug("*** refreshing week %d at (%d, %d)-(%d, %d)\n",
GetWeek(date),
rect.x, rect.y,
rect.x + rect.width, rect.y + rect.height);
#endif
Refresh(TRUE, &rect);
}
void wxCalendarCtrl::HighlightRange(wxPaintDC* pDC, const wxDateTime& fromdate, const wxDateTime& todate, wxPen* pPen, wxBrush* pBrush)
{
// Highlights the given range using pen and brush
// Does nothing if todate < fromdate
#if DEBUG_PAINT
wxLogDebug("+++ HighlightRange: (%s) - (%s) +++", fromdate.Format("%d %m %Y"), todate.Format("%d %m %Y"));
#endif
if ( todate >= fromdate )
{
// do stuff
// date-coordinates
int fd, fw;
int td, tw;
// implicit: both dates must be currently shown - checked by GetDateCoord
if ( GetDateCoord(fromdate, &fd, &fw) && GetDateCoord(todate, &td, &tw) )
{
#if DEBUG_PAINT
wxLogDebug("Highlight range: (%i, %i) - (%i, %i)", fd, fw, td, tw);
#endif
if ( ( (tw - fw) == 1 ) && ( td < fd ) )
{
// special case: interval 7 days or less not in same week
// split in two seperate intervals
wxDateTime tfd = fromdate + wxDateSpan::Days(7-fd);
wxDateTime ftd = tfd + wxDateSpan::Day();
#if DEBUG_PAINT
wxLogDebug("Highlight: Seperate segments");
#endif
// draw seperately
HighlightRange(pDC, fromdate, tfd, pPen, pBrush);
HighlightRange(pDC, ftd, todate, pPen, pBrush);
}
else
{
int numpoints;
wxPoint corners[8]; // potentially 8 corners in polygon
if ( fw == tw )
{
// simple case: same week
numpoints = 4;
corners[0] = wxPoint((fd - 1) * m_widthCol, (fw * m_heightRow) + m_rowOffset);
corners[1] = wxPoint((fd - 1) * m_widthCol, ((fw + 1 ) * m_heightRow) + m_rowOffset);
corners[2] = wxPoint(td * m_widthCol, ((tw + 1) * m_heightRow) + m_rowOffset);
corners[3] = wxPoint(td * m_widthCol, (tw * m_heightRow) + m_rowOffset);
}
else
{
int cidx = 0;
// "complex" polygon
corners[cidx] = wxPoint((fd - 1) * m_widthCol, (fw * m_heightRow) + m_rowOffset); cidx++;
if ( fd > 1 )
{
corners[cidx] = wxPoint((fd - 1) * m_widthCol, ((fw + 1) * m_heightRow) + m_rowOffset); cidx++;
corners[cidx] = wxPoint(0, ((fw + 1) * m_heightRow) + m_rowOffset); cidx++;
}
corners[cidx] = wxPoint(0, ((tw + 1) * m_heightRow) + m_rowOffset); cidx++;
corners[cidx] = wxPoint(td * m_widthCol, ((tw + 1) * m_heightRow) + m_rowOffset); cidx++;
if ( td < 7 )
{
corners[cidx] = wxPoint(td * m_widthCol, (tw * m_heightRow) + m_rowOffset); cidx++;
corners[cidx] = wxPoint(7 * m_widthCol, (tw * m_heightRow) + m_rowOffset); cidx++;
}
corners[cidx] = wxPoint(7 * m_widthCol, (fw * m_heightRow) + m_rowOffset); cidx++;
numpoints = cidx;
}
// draw the polygon
pDC->SetBrush(*pBrush);
pDC->SetPen(*pPen);
pDC->DrawPolygon(numpoints, corners);
}
}
}
// else do nothing
#if DEBUG_PAINT
wxLogDebug("--- HighlightRange ---");
#endif
}
bool wxCalendarCtrl::GetDateCoord(const wxDateTime& date, int *day, int *week) const
{
bool retval = TRUE;
#if DEBUG_PAINT
wxLogDebug("+++ GetDateCoord: (%s) +++", date.Format("%d %m %Y"));
#endif
if ( IsDateShown(date) )
{
bool startOnMonday = ( GetWindowStyle() & wxCAL_MONDAY_FIRST ) != 0;
// Find day
*day = date.GetWeekDay();
if ( *day == 0 ) // sunday
{
*day = ( startOnMonday ) ? 7 : 1;
}
else
{
day += ( startOnMonday ) ? 0 : 1;
}
int targetmonth = date.GetMonth() + (12 * date.GetYear());
int thismonth = m_date.GetMonth() + (12 * m_date.GetYear());
// Find week
if ( targetmonth == thismonth )
{
*week = GetWeek(date);
}
else
{
if ( targetmonth < thismonth )
{
*week = 1; // trivial
}
else // targetmonth > thismonth
{
wxDateTime ldcm;
int lastweek;
int lastday;
// get the datecoord of the last day in the month currently shown
#if DEBUG_PAINT
wxLogDebug(" +++ LDOM +++");
#endif
GetDateCoord(ldcm.SetToLastMonthDay(m_date.GetMonth(), m_date.GetYear()), &lastday, &lastweek);
#if DEBUG_PAINT
wxLogDebug(" --- LDOM ---");
#endif
wxTimeSpan span = date - ldcm;
int daysfromlast = span.GetDays();
#if DEBUG_PAINT
wxLogDebug("daysfromlast: %i", daysfromlast);
#endif
if ( daysfromlast + lastday > 7 ) // past week boundary
{
int wholeweeks = (daysfromlast / 7);
*week = wholeweeks + lastweek;
if ( (daysfromlast - (7 * wholeweeks) + lastday) > 7 )
{
*week += 1;
}
}
else
{
*week = lastweek;
}
}
}
}
else
{
*day = -1;
*week = -1;
retval = FALSE;
}
#if DEBUG_PAINT
wxLogDebug("--- GetDateCoord: (%s) = (%i, %i) ---", date.Format("%d %m %Y"), *day, *week);
#endif
return retval;
}
// ----------------------------------------------------------------------------
// mouse handling
// ----------------------------------------------------------------------------
void wxCalendarCtrl::OnDClick(wxMouseEvent& event)
{
if ( HitTest(event.GetPosition()) != wxCAL_HITTEST_DAY )
{
event.Skip();
}
else
{
GenerateEvent(wxEVT_CALENDAR_DOUBLECLICKED);
}
}
void wxCalendarCtrl::OnClick(wxMouseEvent& event)
{
wxDateTime date;
wxDateTime::WeekDay wday;
switch ( HitTest(event.GetPosition(), &date, &wday) )
{
case wxCAL_HITTEST_DAY:
if ( IsDateInRange(date) )
{
ChangeDay(date);
GenerateEvents(wxEVT_CALENDAR_DAY_CHANGED,
wxEVT_CALENDAR_SEL_CHANGED);
}
break;
case wxCAL_HITTEST_HEADER:
{
wxCalendarEvent event(this, wxEVT_CALENDAR_WEEKDAY_CLICKED);
event.m_wday = wday;
(void)GetEventHandler()->ProcessEvent(event);
}
break;
case wxCAL_HITTEST_DECMONTH:
case wxCAL_HITTEST_INCMONTH:
case wxCAL_HITTEST_SURROUNDING_WEEK:
SetDateAndNotify(date); // we probably only want to refresh the control. No notification.. (maybe as an option?)
break;
default:
wxFAIL_MSG(_T("unknown hittest code"));
// fall through
case wxCAL_HITTEST_NOWHERE:
event.Skip();
break;
}
}
wxCalendarHitTestResult wxCalendarCtrl::HitTest(const wxPoint& pos,
wxDateTime *date,
wxDateTime::WeekDay *wd)
{
RecalcGeometry();
wxCoord y = pos.y;
///////////////////////////////////////////////////////////////////////////////////////////////////////
if ( (GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION) )
{
// Header: month
// we need to find out if the hit is on left arrow, on month or on right arrow
// left arrow?
if ( wxRegion(m_leftArrowRect).Contains(pos) == wxInRegion )
{
if ( date )
{
if ( IsDateInRange(m_date - wxDateSpan::Month()) )
{
*date = m_date - wxDateSpan::Month();
}
else
{
*date = GetLowerDateLimit();
}
}
return wxCAL_HITTEST_DECMONTH;
}
if ( wxRegion(m_rightArrowRect).Contains(pos) == wxInRegion )
{
if ( date )
{
if ( IsDateInRange(m_date + wxDateSpan::Month()) )
{
*date = m_date + wxDateSpan::Month();
}
else
{
*date = GetUpperDateLimit();
}
}
return wxCAL_HITTEST_INCMONTH;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
// Header: Days
int wday = pos.x / m_widthCol;
// if ( y < m_heightRow )
if ( y < (m_heightRow + m_rowOffset) )
{
if ( y > m_rowOffset )
{
if ( wd )
{
if ( GetWindowStyle() & wxCAL_MONDAY_FIRST )
{
wday = wday == 6 ? 0 : wday + 1;
}
*wd = (wxDateTime::WeekDay)wday;
}
return wxCAL_HITTEST_HEADER;
}
else
{
return wxCAL_HITTEST_NOWHERE;
}
}
// int week = (y - m_heightRow) / m_heightRow;
int week = (y - (m_heightRow + m_rowOffset)) / m_heightRow;
if ( week >= 6 || wday >= 7 )
{
return wxCAL_HITTEST_NOWHERE;
}
wxDateTime dt = GetStartDate() + wxDateSpan::Days(7*week + wday);
if ( IsDateShown(dt) )
{
if ( date )
*date = dt;
if ( dt.GetMonth() == m_date.GetMonth() )
{
return wxCAL_HITTEST_DAY;
}
else
{
return wxCAL_HITTEST_SURROUNDING_WEEK;
}
}
else
{
return wxCAL_HITTEST_NOWHERE;
}
}
// ----------------------------------------------------------------------------
// subcontrols events handling
// ----------------------------------------------------------------------------
void wxCalendarCtrl::OnMonthChange(wxCommandEvent& event)
{
wxDateTime::Tm tm = m_date.GetTm();
wxDateTime::Month mon = (wxDateTime::Month)event.GetInt();
if ( tm.mday > wxDateTime::GetNumberOfDays(mon, tm.year) )
{
tm.mday = wxDateTime::GetNumberOfDays(mon, tm.year);
}
wxDateTime target = wxDateTime(tm.mday, mon, tm.year);
ChangeMonth(&target);
SetDateAndNotify(target);
}
void wxCalendarCtrl::OnYearChange(wxCommandEvent& event)
{
int year = (int)event.GetInt();
if ( year == INT_MIN )
{
// invalid year in the spin control, ignore it
return;
}
wxDateTime::Tm tm = m_date.GetTm();
if ( tm.mday > wxDateTime::GetNumberOfDays(tm.mon, year) )
{
tm.mday = wxDateTime::GetNumberOfDays(tm.mon, year);
}
wxDateTime target = wxDateTime(tm.mday, tm.mon, year);
if ( ChangeYear(&target) )
{
SetDateAndNotify(target);
}
else
{
// In this case we don't want to change the date. That would put us
// inside the same year but a strange number of months forward/back..
m_spinYear->SetValue(target.GetYear());
}
}
// ----------------------------------------------------------------------------
// keyboard interface
// ----------------------------------------------------------------------------
void wxCalendarCtrl::OnChar(wxKeyEvent& event)
{
wxDateTime target;
switch ( event.GetKeyCode() )
{
case _T('+'):
case WXK_ADD:
target = m_date + wxDateSpan::Year();
if ( ChangeYear(&target) )
{
SetDateAndNotify(target);
}
break;
case _T('-'):
case WXK_SUBTRACT:
target = m_date - wxDateSpan::Year();
if ( ChangeYear(&target) )
{
SetDateAndNotify(target);
}
break;
case WXK_PRIOR:
target = m_date - wxDateSpan::Month();
ChangeMonth(&target);
SetDateAndNotify(target); // always
break;
case WXK_NEXT:
target = m_date + wxDateSpan::Month();
ChangeMonth(&target);
SetDateAndNotify(target); // always
break;
case WXK_RIGHT:
if ( event.ControlDown() )
{
target = wxDateTime(m_date).SetToNextWeekDay(
GetWindowStyle() & wxCAL_MONDAY_FIRST
? wxDateTime::Sun : wxDateTime::Sat);
if ( !IsDateInRange(target) )
{
target = GetUpperDateLimit();
}
SetDateAndNotify(target);
}
else
SetDateAndNotify(m_date + wxDateSpan::Day());
break;
case WXK_LEFT:
if ( event.ControlDown() )
{
target = wxDateTime(m_date).SetToPrevWeekDay(
GetWindowStyle() & wxCAL_MONDAY_FIRST
? wxDateTime::Mon : wxDateTime::Sun);
if ( !IsDateInRange(target) )
{
target = GetLowerDateLimit();
}
SetDateAndNotify(target);
}
else
SetDateAndNotify(m_date - wxDateSpan::Day());
break;
case WXK_UP:
SetDateAndNotify(m_date - wxDateSpan::Week());
break;
case WXK_DOWN:
SetDateAndNotify(m_date + wxDateSpan::Week());
break;
case WXK_HOME:
if ( event.ControlDown() )
SetDateAndNotify(wxDateTime::Today());
else
SetDateAndNotify(wxDateTime(1, m_date.GetMonth(), m_date.GetYear()));
break;
case WXK_END:
SetDateAndNotify(wxDateTime(m_date).SetToLastMonthDay());
break;
case WXK_RETURN:
GenerateEvent(wxEVT_CALENDAR_DOUBLECLICKED);
break;
default:
event.Skip();
}
}
// ----------------------------------------------------------------------------
// holidays handling
// ----------------------------------------------------------------------------
void wxCalendarCtrl::EnableHolidayDisplay(bool display)
{
long style = GetWindowStyle();
if ( display )
style |= wxCAL_SHOW_HOLIDAYS;
else
style &= ~wxCAL_SHOW_HOLIDAYS;
SetWindowStyle(style);
if ( display )
SetHolidayAttrs();
else
ResetHolidayAttrs();
Refresh();
}
void wxCalendarCtrl::SetHolidayAttrs()
{
if ( GetWindowStyle() & wxCAL_SHOW_HOLIDAYS )
{
ResetHolidayAttrs();
wxDateTime::Tm tm = m_date.GetTm();
wxDateTime dtStart(1, tm.mon, tm.year),
dtEnd = dtStart.GetLastMonthDay();
wxDateTimeArray hol;
wxDateTimeHolidayAuthority::GetHolidaysInRange(dtStart, dtEnd, hol);
size_t count = hol.GetCount();
for ( size_t n = 0; n < count; n++ )
{
SetHoliday(hol[n].GetDay());
}
}
}
void wxCalendarCtrl::SetHoliday(size_t day)
{
wxCHECK_RET( day > 0 && day < 32, _T("invalid day in SetHoliday") );
wxCalendarDateAttr *attr = GetAttr(day);
if ( !attr )
{
attr = new wxCalendarDateAttr;
}
attr->SetHoliday(TRUE);
// can't use SetAttr() because it would delete this pointer
m_attrs[day - 1] = attr;
}
void wxCalendarCtrl::ResetHolidayAttrs()
{
for ( size_t day = 0; day < 31; day++ )
{
if ( m_attrs[day] )
{
m_attrs[day]->SetHoliday(FALSE);
}
}
}
// ----------------------------------------------------------------------------
// wxCalendarEvent
// ----------------------------------------------------------------------------
void wxCalendarEvent::Init()
{
m_wday = wxDateTime::Inv_WeekDay;
}
wxCalendarEvent::wxCalendarEvent(wxCalendarCtrl *cal, wxEventType type)
: wxCommandEvent(type, cal->GetId())
{
m_date = cal->GetDate();
SetEventObject(cal);
}
#endif // wxUSE_CALENDARCTRL