+public:
+
+ wxCalendarComboPopup() : wxCalendarCtrl(),
+ wxComboPopup()
+ {
+ }
+
+ virtual void Init()
+ {
+ }
+
+ // NB: Don't create lazily since it didn't work that way before
+ // wxComboCtrl was used, and changing behaviour would almost
+ // certainly introduce new bugs.
+ virtual bool Create(wxWindow* parent)
+ {
+ if ( !wxCalendarCtrl::Create(parent, wxID_ANY, wxDefaultDateTime,
+ wxPoint(0, 0), wxDefaultSize,
+ wxCAL_SHOW_HOLIDAYS | wxBORDER_SUNKEN) )
+ return false;
+
+ wxWindow *yearControl = wxCalendarCtrl::GetYearControl();
+
+ wxClientDC dc(yearControl);
+ dc.SetFont(yearControl->GetFont());
+ wxCoord width, dummy;
+ dc.GetTextExtent(wxT("2000"), &width, &dummy);
+ width += ConvertDialogToPixels(wxSize(20, 0)).x;
+
+ wxSize calSize = wxCalendarCtrl::GetBestSize();
+ wxSize yearSize = yearControl->GetSize();
+ yearSize.x = width;
+
+ wxPoint yearPosition = yearControl->GetPosition();
+
+ SetFormat("%x");
+
+ width = yearPosition.x + yearSize.x+2+CALBORDER/2;
+ if (width < calSize.x-4)
+ width = calSize.x-4;
+
+ int calPos = (width-calSize.x)/2;
+ if (calPos == -1)
+ {
+ calPos = 0;
+ width += 2;
+ }
+ wxCalendarCtrl::SetSize(calPos, 0, calSize.x, calSize.y);
+ yearControl->SetSize(width-yearSize.x-CALBORDER/2, yearPosition.y,
+ yearSize.x, yearSize.y);
+ wxCalendarCtrl::GetMonthControl()->Move(0, 0);
+
+ m_useSize.x = width+CALBORDER/2;
+ m_useSize.y = calSize.y-2+CALBORDER;
+
+ wxWindow* tx = m_combo->GetTextCtrl();
+ if ( !tx )
+ tx = m_combo;
+
+ tx->Connect(wxEVT_KILL_FOCUS,
+ wxFocusEventHandler(wxCalendarComboPopup::OnKillTextFocus),
+ NULL, this);
+
+ return true;
+ }
+
+ virtual wxSize GetAdjustedSize(int WXUNUSED(minWidth),
+ int WXUNUSED(prefHeight),
+ int WXUNUSED(maxHeight))
+ {
+ return m_useSize;
+ }
+
+ virtual wxWindow *GetControl() { return this; }
+
+ void SetDateValue(const wxDateTime& date)
+ {
+ if ( date.IsValid() )
+ {
+ m_combo->SetText(date.Format(m_format));
+ }
+ else // invalid date
+ {
+ wxASSERT_MSG( HasDPFlag(wxDP_ALLOWNONE),
+ _T("this control must have a valid date") );
+
+ m_combo->SetText(wxEmptyString);
+ }
+
+ m_currentDate = date;
+ }
+
+ const wxDateTime& GetDateValue() const
+ {
+ return m_currentDate;
+ }
+
+ bool ParseDateTime(const wxString& s, wxDateTime* pDt)
+ {
+ wxASSERT(pDt);
+
+ if ( !s.empty() )
+ {
+ pDt->ParseFormat(s.c_str(), m_format);
+ if ( !pDt->IsValid() )
+ return false;
+ }
+
+ return true;
+ }
+
+ void SendDateEvent(const wxDateTime& dt)
+ {
+ //
+ // Sends both wxCalendarEvent and wxDateEvent
+ wxWindow* datePicker = m_combo->GetParent();
+
+ wxCalendarEvent cev((wxCalendarCtrl*) this, wxEVT_CALENDAR_SEL_CHANGED);
+ cev.SetEventObject(datePicker);
+ cev.SetId(datePicker->GetId());
+ cev.SetDate(dt);
+ datePicker->GetEventHandler()->ProcessEvent(cev);
+
+ wxDateEvent event(datePicker, dt, wxEVT_DATE_CHANGED);
+ datePicker->GetEventHandler()->ProcessEvent(event);
+ }
+
+private:
+
+ void OnCalKey(wxKeyEvent & ev)
+ {
+ if (ev.GetKeyCode() == WXK_ESCAPE && !ev.HasModifiers())
+ Dismiss();
+ else
+ ev.Skip();
+ }
+
+ void OnSelChange(wxCalendarEvent &ev)
+ {
+ m_currentDate = wxCalendarCtrl::GetDate();
+ m_combo->SetText(m_currentDate.Format(m_format));
+
+ if ( ev.GetEventType() == wxEVT_CALENDAR_DOUBLECLICKED )
+ {
+ Dismiss();
+ }
+
+ SendDateEvent(m_currentDate);
+ }
+
+ void OnKillTextFocus(wxFocusEvent &ev)
+ {
+ ev.Skip();
+
+ wxDateTime dt;
+ wxString value = m_combo->GetValue();
+ if ( !ParseDateTime(value, &dt) )
+ {
+ if ( !HasDPFlag(wxDP_ALLOWNONE) )
+ dt = m_currentDate;
+ }
+
+ if ( dt.IsValid() )
+ m_combo->SetText(dt.Format(m_format));
+ else
+ m_combo->SetText(wxEmptyString);
+
+ // notify that we had to change the date after validation
+ if ( (dt.IsValid() && (!m_currentDate.IsValid() || m_currentDate != dt)) ||
+ (!dt.IsValid() && m_currentDate.IsValid()) )
+ {
+ m_currentDate = dt;
+ SendDateEvent(dt);
+ }
+ }
+
+ bool HasDPFlag(int flag)
+ {
+ return m_combo->GetParent()->HasFlag(flag);
+ }
+
+ bool SetFormat(const wxString& fmt)
+ {
+ m_format.clear();
+
+ wxDateTime dt;
+ dt.ParseFormat(wxT("2003-10-13"), wxT("%Y-%m-%d"));
+ wxString str(dt.Format(fmt));
+
+ const wxChar *p = str.c_str();
+ while ( *p )
+ {
+ int n=wxAtoi(p);
+ if (n == dt.GetDay())
+ {
+ m_format.Append(wxT("%d"));
+ p += 2;
+ }
+ else if (n == (int)dt.GetMonth()+1)
+ {
+ m_format.Append(wxT("%m"));
+ p += 2;
+ }
+ else if (n == dt.GetYear())
+ {
+ m_format.Append(wxT("%Y"));
+ p += 4;
+ }
+ else if (n == (dt.GetYear() % 100))
+ {
+ if ( HasDPFlag(wxDP_SHOWCENTURY) )
+ m_format.Append(wxT("%Y"));
+ else
+ m_format.Append(wxT("%y"));
+ p += 2;
+ }
+ else
+ m_format.Append(*p++);
+ }
+
+ if ( m_combo )
+ {
+ wxArrayString allowedChars;
+ for ( wxChar c = _T('0'); c <= _T('9'); c++ )
+ allowedChars.Add(wxString(c, 1));
+
+ const wxChar *p2 = m_format.c_str();
+ while ( *p2 )
+ {
+ if ( *p2 == '%')
+ p2 += 2;
+ else
+ allowedChars.Add(wxString(*p2++, 1));
+ }
+
+ #if wxUSE_VALIDATORS
+ wxTextValidator tv(wxFILTER_INCLUDE_CHAR_LIST);
+ tv.SetIncludes(allowedChars);
+ m_combo->SetValidator(tv);
+ #endif
+
+ if (m_currentDate.IsValid())
+ m_combo->SetText(m_currentDate.Format(m_format));
+ }
+
+ return true;
+ }
+
+ virtual void SetStringValue(const wxString& s)
+ {
+ wxDateTime dt;
+ if ( ParseDateTime(s, &dt) )
+ m_currentDate = dt;
+ else if ( HasDPFlag(wxDP_ALLOWNONE) )
+ m_currentDate = dt;
+ }
+
+ virtual wxString GetStringValue() const
+ {
+ if ( !m_currentDate.IsValid() )
+ return wxEmptyString;
+
+ return m_currentDate.Format(m_format);
+ }
+
+private:
+
+ wxSize m_useSize;
+ wxString m_format;
+ wxDateTime m_currentDate;
+
+ DECLARE_EVENT_TABLE()