virtual bool SetDate(const wxDateTime& date);
     virtual wxDateTime GetDate() const;
 
+    virtual bool SetDateRange(const wxDateTime& lowerdate = wxDefaultDateTime,
+                              const wxDateTime& upperdate = wxDefaultDateTime);
+    virtual bool GetDateRange(wxDateTime *lowerdate, wxDateTime *upperdate) const;
+
     virtual bool EnableMonthChange(bool enable = true);
 
     virtual void Mark(size_t day, bool mark);
 
     // implementation
     // --------------
-    wxDateTime m_selectedDate;
+
+    void GTKGenerateEvent(wxEventType type);
 
 private:
+    bool IsInValidRange(const wxDateTime& dt) const;
+
+    // Range of the dates that can be selected by user, either or both may be
+    // invalid to indicate that no corresponding restriction is set.
+    wxDateTime m_validStart,
+               m_validEnd;
+
+    // Last known selected date, may be different from the real selection in
+    // the control while a handler for day-selected is running.
+    wxDateTime m_selectedDate;
+
     DECLARE_DYNAMIC_CLASS(wxGtkCalendarCtrl)
     wxDECLARE_NO_COPY_CLASS(wxGtkCalendarCtrl);
 };
 
     /**
         Sets the current date.
 
-        The @a date parameter must be valid.
+        The @a date parameter must be valid and in the currently valid range as
+        set by SetDateRange(), otherwise the current date is not changed and
+        the function returns @false.
     */
     virtual bool SetDate(const wxDateTime& date);
 
 
     /**
         @name Date Range Functions
-
-        The functions in this section are currently implemented in the generic
-        and MSW versions and do nothing in the native GTK implementation.
      */
     //@{
 
     /**
-        Restrict the dates shown by the control to the specified range.
+        Restrict the dates that can be selected in the control to the specified
+        range.
 
         If either date is set, the corresponding limit will be enforced and
         @true returned. If none are set, the existing restrictions are removed
 
 static void gtk_day_selected_callback(GtkWidget *WXUNUSED(widget),
                                       wxGtkCalendarCtrl *cal)
 {
-    wxDateTime date = cal->GetDate();
-    if (cal->m_selectedDate == date)
-        return;
-
-    cal->m_selectedDate = date;
-
-    cal->GenerateEvent(wxEVT_CALENDAR_SEL_CHANGED);
-    // send deprecated event
-    cal->GenerateEvent(wxEVT_CALENDAR_DAY_CHANGED);
+    cal->GTKGenerateEvent(wxEVT_CALENDAR_SEL_CHANGED);
 }
 
 static void gtk_day_selected_double_click_callback(GtkWidget *WXUNUSED(widget),
                                                    wxGtkCalendarCtrl *cal)
 {
-    cal->GenerateEvent(wxEVT_CALENDAR_DOUBLECLICKED);
+    cal->GTKGenerateEvent(wxEVT_CALENDAR_DOUBLECLICKED);
 }
 
 static void gtk_month_changed_callback(GtkWidget *WXUNUSED(widget),
                                        wxGtkCalendarCtrl *cal)
 {
-    cal->GenerateEvent(wxEVT_CALENDAR_PAGE_CHANGED);
+    cal->GTKGenerateEvent(wxEVT_CALENDAR_PAGE_CHANGED);
 }
 
 // callbacks that send deprecated events
 static void gtk_prev_month_callback(GtkWidget *WXUNUSED(widget),
                                     wxGtkCalendarCtrl *cal)
 {
-    cal->GenerateEvent(wxEVT_CALENDAR_MONTH_CHANGED);
+    cal->GTKGenerateEvent(wxEVT_CALENDAR_MONTH_CHANGED);
 }
 
 static void gtk_prev_year_callback(GtkWidget *WXUNUSED(widget),
                                     wxGtkCalendarCtrl *cal)
 {
-    cal->GenerateEvent(wxEVT_CALENDAR_YEAR_CHANGED);
+    cal->GTKGenerateEvent(wxEVT_CALENDAR_YEAR_CHANGED);
 }
 
 }
     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) )
 
 bool wxGtkCalendarCtrl::SetDate(const wxDateTime& date)
 {
+    if ( date.IsValid() && !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,