Files
wxWidgets/src/gtk/calctrl.cpp
Vadim Zeitlin e3a728d637 Assert on invalid date in wxCalendarCtrl::SetDate() in all ports
This was done in wxMSW and wxQt but not in the native GTK+ nor the
generic version, even though they still asserted when actually trying to
use the invalid parameter later.

Make things more clear and consistent by asserting immediately and also
document the behaviour more clearly.
2018-07-30 21:46:50 +02:00

255 lines
7.6 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// Name: src/gtk/calctrl.cpp
// Purpose: implementation of the wxGtkCalendarCtrl
// Author: Marcin Wojdyr
// Copyright: (c) 2008 Marcin Wojdyr
// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#if wxUSE_CALENDARCTRL
#ifndef WX_PRECOMP
#endif //WX_PRECOMP
#include "wx/calctrl.h"
#include "wx/gtk/private/wrapgtk.h"
extern "C" {
static void gtk_day_selected_callback(GtkWidget *WXUNUSED(widget),
wxGtkCalendarCtrl *cal)
{
cal->GTKGenerateEvent(wxEVT_CALENDAR_SEL_CHANGED);
}
static void gtk_day_selected_double_click_callback(GtkWidget *WXUNUSED(widget),
wxGtkCalendarCtrl *cal)
{
cal->GTKGenerateEvent(wxEVT_CALENDAR_DOUBLECLICKED);
}
static void gtk_month_changed_callback(GtkWidget *WXUNUSED(widget),
wxGtkCalendarCtrl *cal)
{
cal->GTKGenerateEvent(wxEVT_CALENDAR_PAGE_CHANGED);
}
// callbacks that send deprecated events
static void gtk_prev_month_callback(GtkWidget *WXUNUSED(widget),
wxGtkCalendarCtrl *cal)
{
cal->GTKGenerateEvent(wxEVT_CALENDAR_MONTH_CHANGED);
}
static void gtk_prev_year_callback(GtkWidget *WXUNUSED(widget),
wxGtkCalendarCtrl *cal)
{
cal->GTKGenerateEvent(wxEVT_CALENDAR_YEAR_CHANGED);
}
}
// ----------------------------------------------------------------------------
// wxGtkCalendarCtrl
// ----------------------------------------------------------------------------
bool wxGtkCalendarCtrl::Create(wxWindow *parent,
wxWindowID id,
const wxDateTime& date,
const wxPoint& pos,
const wxSize& size,
long style,
const wxString& name)
{
if (!PreCreation(parent, pos, size) ||
!CreateBase(parent, id, pos, size, style, wxDefaultValidator, name))
{
wxFAIL_MSG(wxT("wxGtkCalendarCtrl creation failed"));
return false;
}
m_widget = gtk_calendar_new();
g_object_ref(m_widget);
SetDate(date.IsValid() ? date : wxDateTime::Today());
if (style & wxCAL_NO_MONTH_CHANGE)
g_object_set (G_OBJECT (m_widget), "no-month-change", true, NULL);
if (style & wxCAL_SHOW_WEEK_NUMBERS)
g_object_set (G_OBJECT (m_widget), "show-week-numbers", true, NULL);
g_signal_connect_after(m_widget, "day-selected",
G_CALLBACK (gtk_day_selected_callback),
this);
g_signal_connect_after(m_widget, "day-selected-double-click",
G_CALLBACK (gtk_day_selected_double_click_callback),
this);
g_signal_connect_after(m_widget, "month-changed",
G_CALLBACK (gtk_month_changed_callback),
this);
// connect callbacks that send deprecated events
g_signal_connect_after(m_widget, "prev-month",
G_CALLBACK (gtk_prev_month_callback),
this);
g_signal_connect_after(m_widget, "next-month",
G_CALLBACK (gtk_prev_month_callback),
this);
g_signal_connect_after(m_widget, "prev-year",
G_CALLBACK (gtk_prev_year_callback),
this);
g_signal_connect_after(m_widget, "next-year",
G_CALLBACK (gtk_prev_year_callback),
this);
m_parent->DoAddChild(this);
PostCreation(size);
return true;
}
void wxGtkCalendarCtrl::GTKGenerateEvent(wxEventType type)
{
// First check if the new date is in the specified range.
wxDateTime dt = GetDate();
if ( !IsInValidRange(dt) )
{
if ( m_validStart.IsValid() && dt < m_validStart )
dt = m_validStart;
else
dt = m_validEnd;
SetDate(dt);
return;
}
if ( type == wxEVT_CALENDAR_SEL_CHANGED )
{
// Don't generate this event if the new date is the same as the old
// one.
if ( m_selectedDate == dt )
return;
m_selectedDate = dt;
GenerateEvent(type);
// Also send the deprecated event together with the new one.
GenerateEvent(wxEVT_CALENDAR_DAY_CHANGED);
}
else
{
GenerateEvent(type);
}
}
bool wxGtkCalendarCtrl::IsInValidRange(const wxDateTime& dt) const
{
return (!m_validStart.IsValid() || m_validStart <= dt) &&
(!m_validEnd.IsValid() || dt <= m_validEnd);
}
bool
wxGtkCalendarCtrl::SetDateRange(const wxDateTime& lowerdate,
const wxDateTime& upperdate)
{
if ( lowerdate.IsValid() && upperdate.IsValid() && lowerdate >= upperdate )
return false;
m_validStart = lowerdate;
m_validEnd = upperdate;
return true;
}
bool
wxGtkCalendarCtrl::GetDateRange(wxDateTime *lowerdate,
wxDateTime *upperdate) const
{
if ( lowerdate )
*lowerdate = m_validStart;
if ( upperdate )
*upperdate = m_validEnd;
return m_validStart.IsValid() || m_validEnd.IsValid();
}
bool wxGtkCalendarCtrl::EnableMonthChange(bool enable)
{
if ( !wxCalendarCtrlBase::EnableMonthChange(enable) )
return false;
g_object_set (G_OBJECT (m_widget), "no-month-change", !enable, NULL);
return true;
}
bool wxGtkCalendarCtrl::SetDate(const wxDateTime& date)
{
wxCHECK_MSG( date.IsValid(), false, "invalid date" );
if ( !IsInValidRange(date) )
return false;
g_signal_handlers_block_by_func(m_widget,
(gpointer) gtk_day_selected_callback, this);
g_signal_handlers_block_by_func(m_widget,
(gpointer) gtk_month_changed_callback, this);
m_selectedDate = date;
int year = date.GetYear();
int month = date.GetMonth();
int day = date.GetDay();
gtk_calendar_select_month(GTK_CALENDAR(m_widget), month, year);
gtk_calendar_select_day(GTK_CALENDAR(m_widget), day);
g_signal_handlers_unblock_by_func( m_widget,
(gpointer) gtk_month_changed_callback, this);
g_signal_handlers_unblock_by_func( m_widget,
(gpointer) gtk_day_selected_callback, this);
return true;
}
wxDateTime wxGtkCalendarCtrl::GetDate() const
{
guint year, monthGTK, day;
gtk_calendar_get_date(GTK_CALENDAR(m_widget), &year, &monthGTK, &day);
// GTK may return an invalid date, this happens at least when switching the
// month (or the year in case of February in a leap year) and the new month
// has fewer days than the currently selected one making the currently
// selected day invalid, e.g. just choosing May 31 and going back a month
// results in the date being (non existent) April 31 when we're called from
// gtk_prev_month_callback(). We need to manually work around this to avoid
// asserts from wxDateTime ctor.
const wxDateTime::Month month = static_cast<wxDateTime::Month>(monthGTK);
const guint dayMax = wxDateTime::GetNumberOfDays(month, year);
if ( day > dayMax )
day = dayMax;
return wxDateTime(day, month, year);
}
void wxGtkCalendarCtrl::Mark(size_t day, bool mark)
{
if (mark)
gtk_calendar_mark_day(GTK_CALENDAR(m_widget), day);
else
gtk_calendar_unmark_day(GTK_CALENDAR(m_widget), day);
}
#endif // wxUSE_CALENDARCTRL