////////////////////////////////////////////////////////////////////////// // // pgAdmin III - PostgreSQL Tools // // Copyright (C) 2002 - 2016, The pgAdmin Development Team // This software is released under the PostgreSQL Licence // // calbox.cpp - Date-picker control box // ////////////////////////////////////////////////////////////////////////// #include "pgAdmin3.h" #include #include "ctl/calbox.h" #if !defined(wxUSE_DATEPICKCTRL) || !wxUSE_DATEPICKCTRL #if defined(__WXMSW__) #define TXTCTRL_FLAGS wxNO_BORDER #define CALBORDER 0 #define TXTPOSX 0 #define TXTPOSY 1 #elif defined(__WXGTK__) #define TXTCTRL_FLAGS 0 #define CALBORDER 4 #define TXTPOSX 0 #define TXTPOSY 0 #else #define TXTCTRL_FLAGS 0 #define CALBORDER 4 #define TXTPOSX 0 #define TXTPOSY 0 #endif #define CTRLID_TXT 101 #define CTRLID_CAL 102 #define CTRLID_BTN 103 #define CTRLID_PAN 104 BEGIN_EVENT_TABLE(wxCalendarBox, wxControl) EVT_BUTTON(CTRLID_BTN, wxCalendarBox::OnClick) EVT_TEXT(CTRLID_TXT, wxCalendarBox::OnText) EVT_CHILD_FOCUS(wxCalendarBox::OnChildSetFocus) EVT_SIZE(wxCalendarBox::OnSize) END_EVENT_TABLE() IMPLEMENT_DYNAMIC_CLASS(wxCalendarBox, wxControl) wxCalendarBox::wxCalendarBox(wxWindow *parent, wxWindowID id, const wxDateTime &date, const wxPoint &pos, const wxSize &size, long style, const wxString &name) { Init(); Create(parent, id, date, pos, size, style, name); } bool wxCalendarBox::Create(wxWindow *parent, wxWindowID id, const wxDateTime &date, const wxPoint &pos, const wxSize &size, long style, const wxString &name) { wxString txt; if (date.IsValid()) txt = date.FormatISODate(); if ( !wxControl::Create(parent, id, pos, size, style | wxCLIP_CHILDREN | wxWANTS_CHARS, wxDefaultValidator, name) ) { return false; } InheritAttributes(); wxSize cs = GetClientSize(); wxSize bs = ConvertDialogToPixels(wxSize(10, 0)); wxBitmap bmp(8, 4); { wxMemoryDC dc; dc.SelectObject(bmp); dc.SetBrush(wxBrush(GetBackgroundColour())); dc.SetPen(wxPen(GetBackgroundColour())); dc.DrawRectangle(0, 0, 8, 4); dc.SetBrush(wxBrush(GetForegroundColour())); dc.SetPen(wxPen(GetForegroundColour())); wxPoint pt[3] = { wxPoint(0, 0), wxPoint(6, 0), wxPoint(3, 3) }; dc.DrawPolygon(3, pt); dc.SelectObject(wxNullBitmap); } m_txt = new wxTextCtrl(this, CTRLID_TXT, txt, wxDefaultPosition, size, TXTCTRL_FLAGS); m_txt->Connect(wxID_ANY, wxID_ANY, wxEVT_KEY_DOWN, wxKeyEventHandler(wxCalendarBox::OnEditKey), 0, this); m_txt->Connect(wxID_ANY, wxID_ANY, wxEVT_KILL_FOCUS, wxFocusEventHandler(wxCalendarBox::OnKillFocus), 0, this); SetFormat(wxT("%Y-%m-%d")); m_btn = new wxBitmapButton(this, CTRLID_BTN, bmp, wxDefaultPosition, bs); m_dlg = new wxDialog(this, CTRLID_CAL, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSIMPLE_BORDER); m_dlg->SetFont(GetFont()); wxPanel *panel = new wxPanel(m_dlg, CTRLID_PAN, wxPoint(0, 0), wxDefaultSize, wxSUNKEN_BORDER | wxCLIP_CHILDREN); m_cal = new pgCompatCalendarCtrl(panel, CTRLID_CAL, wxDefaultDateTime, wxPoint(0, 0), wxDefaultSize, wxSUNKEN_BORDER); m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_SEL_CHANGED, wxCalendarEventHandler(wxCalendarBox::OnSelChange), 0, this); m_cal->Connect(wxID_ANY, wxID_ANY, wxEVT_KEY_DOWN, wxKeyEventHandler(wxCalendarBox::OnCalKey), 0, this); m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_DOUBLECLICKED, wxCalendarEventHandler(wxCalendarBox::OnSelChange), 0, this); m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_DAY_CHANGED, wxCalendarEventHandler(wxCalendarBox::OnSelChange), 0, this); m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_MONTH_CHANGED, wxCalendarEventHandler(wxCalendarBox::OnSelChange), 0, this); m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_YEAR_CHANGED, wxCalendarEventHandler(wxCalendarBox::OnSelChange), 0, this); wxWindow *yearControl = m_cal->GetYearControl(); Connect(wxID_ANY, wxID_ANY, wxEVT_SET_FOCUS, wxFocusEventHandler(wxCalendarBox::OnSetFocus)); wxClientDC dc(yearControl); dc.SetFont(m_font); wxCoord width, dummy; dc.GetTextExtent(wxT("2000"), &width, &dummy); width += ConvertDialogToPixels(wxSize(20, 0)).x; wxSize calSize = m_cal->GetBestSize(); wxSize yearSize = yearControl->GetSize(); yearSize.x = width; wxPoint yearPosition = yearControl->GetPosition(); 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; } m_cal->SetSize(calPos, 0, calSize.x, calSize.y); yearControl->SetSize(width - yearSize.x - CALBORDER / 2, yearPosition.y, yearSize.x, yearSize.y); m_cal->GetMonthControl()->Move(0, 0); SetInitialSize(size); panel->SetClientSize(width + CALBORDER / 2, calSize.y - 2 + CALBORDER); m_dlg->SetClientSize(panel->GetSize()); return true; } void wxCalendarBox::Init() { m_dlg = NULL; m_txt = NULL; m_cal = NULL; m_btn = NULL; m_dropped = false; m_ignoreDrop = false; } bool wxCalendarBox::Destroy() { if (m_cal) m_cal->Destroy(); if (m_dlg) m_dlg->Destroy(); if (m_txt) m_txt->Destroy(); if (m_btn) m_btn->Destroy(); m_dlg = NULL; m_txt = NULL; m_cal = NULL; m_btn = NULL; return wxControl::Destroy(); } void wxCalendarBox::DoMoveWindow(int x, int y, int w, int h) { wxControl::DoMoveWindow(x, y, w, h); if (m_dropped) { DropDown(); } } wxSize wxCalendarBox::DoGetBestSize() const { int bh = m_btn->GetBestSize().y; int eh = m_txt->GetBestSize().y; return wxSize(100, bh > eh ? bh : eh); } void wxCalendarBox::OnSize(wxSizeEvent &event) { if ( m_btn ) { wxSize sz = GetClientSize(); wxSize bs = m_btn->GetSize(); int eh = m_txt->GetBestSize().y; m_txt->SetSize(TXTPOSX, TXTPOSY, sz.x - bs.x - TXTPOSX, sz.y > eh ? eh - TXTPOSY : sz.y - TXTPOSY); m_btn->SetSize(sz.x - bs.x, 0, bs.x, sz.y); } event.Skip(); } bool wxCalendarBox::Show(bool show) { if ( !wxControl::Show(show) ) { return false; } if (!show) { if (m_dlg) { m_dlg->Hide(); m_dropped = false; } } return true; } bool wxCalendarBox::Enable(bool enable) { if ( !wxControl::Enable(enable) ) { return false; } if (m_cal) { if (enable) m_cal->Show(); else m_cal->Hide(); } if (m_btn) m_btn->Enable(enable); return true; } bool wxCalendarBox::SetFormat(const wxChar *fmt) { wxDateTime dt; dt.ParseFormat(wxT("2003-10-13"), wxT("%Y-%m-%d")); wxString str = dt.Format(fmt); const wxChar *p = (const wxChar *) str; m_format = wxEmptyString; 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 m_format.Append(*p++); } if (m_txt) { wxArrayString valArray; wxChar c; for (c = '0'; c <= '9'; c++) valArray.Add(wxString(c, 1)); const wxChar *p = (const wxChar *) m_format; while (*p) { if (*p == '%') p += 2; else valArray.Add(wxString(*p++, 1)); } wxTextValidator tv(wxFILTER_INCLUDE_CHAR_LIST); tv.SetIncludes(valArray); m_txt->SetValidator(tv); } return true; } wxDateTime wxCalendarBox::GetValue() { wxDateTime dt; wxString txt = m_txt->GetValue(); if (!txt.IsEmpty()) dt.ParseFormat(txt, m_format); return dt; } bool wxCalendarBox::SetValue(const wxDateTime &date) { bool retval = false; if (m_cal) { if (date.IsValid()) m_txt->SetValue(date.FormatISODate()); else m_txt->SetValue(wxEmptyString); } return retval; } void wxCalendarBox::DropDown(bool down) { if (m_dlg) { if (down) { if (m_txt->GetValue().IsEmpty()) m_cal->SetDate(wxDateTime::Today()); else { wxDateTime dt; dt.ParseFormat(m_txt->GetValue(), m_format); m_cal->SetDate(dt); } wxPoint pos = GetParent()->ClientToScreen(GetPosition()); m_dlg->Move(pos.x, pos.y + GetSize().y); m_dlg->Show(); m_dropped = true; } else { if (m_dropped) m_dlg->Hide(); m_dropped = false; } } } void wxCalendarBox::OnChildSetFocus(wxChildFocusEvent &ev) { ev.Skip(); m_ignoreDrop = false; wxWindow *w = (wxWindow *)ev.GetEventObject(); while (w) { if (w == m_dlg) return; w = w->GetParent(); } if (m_dropped) { DropDown(false); if (ev.GetEventObject() == m_btn) m_ignoreDrop = true; } } void wxCalendarBox::OnClick(wxCommandEvent &event) { if (m_ignoreDrop) { m_ignoreDrop = false; m_txt->SetFocus(); } else { DropDown(); m_cal->SetFocus(); } } void wxCalendarBox::OnSetFocus(wxFocusEvent &ev) { if (m_txt) { m_txt->SetFocus(); m_txt->SetSelection(0, 100); } } void wxCalendarBox::OnKillFocus(wxFocusEvent &ev) { wxDateTime dt; dt.ParseFormat(m_txt->GetValue(), m_format); if (!dt.IsValid()) m_txt->SetValue(wxEmptyString); else m_txt->SetValue(dt.Format(m_format)); } void wxCalendarBox::OnSelChange(wxCalendarEvent &ev) { if (m_cal) { m_txt->SetValue(m_cal->GetDate().FormatISODate()); if (ev.GetEventType() == wxEVT_CALENDAR_DOUBLECLICKED) { DropDown(false); m_txt->SetFocus(); } } ev.SetEventObject(this); ev.SetId(GetId()); #if wxCHECK_VERSION(2, 9, 0) GetParent()->GetEventHandler()->ProcessEvent(ev); #else GetParent()->ProcessEvent(ev); #endif } void wxCalendarBox::OnText(wxCommandEvent &ev) { ev.SetEventObject(this); ev.SetId(GetId()); #if wxCHECK_VERSION(2, 9, 0) GetParent()->GetEventHandler()->ProcessEvent(ev); #else GetParent()->ProcessEvent(ev); #endif // We'll create an additional event if the date is valid. // If the date isn't valid, the user's probably in the middle of typing wxString txt = m_txt->GetValue(); wxDateTime dt; if (!txt.IsEmpty()) dt.ParseFormat(txt, m_format); #if wxCHECK_VERSION(2, 9, 0) wxCalendarEvent cev(m_cal, dt, wxEVT_CALENDAR_SEL_CHANGED); #else wxCalendarEvent cev(m_cal, wxEVT_CALENDAR_SEL_CHANGED); cev.SetDate(dt); #endif cev.SetEventObject(this); cev.SetId(GetId()); #if wxCHECK_VERSION(2, 9, 0) GetParent()->GetEventHandler()->ProcessEvent(cev); #else GetParent()->ProcessEvent(cev); #endif } void wxCalendarBox::OnEditKey(wxKeyEvent &ev) { if (ev.GetKeyCode() == WXK_DOWN && !ev.HasModifiers()) DropDown(); else ev.Skip(); } void wxCalendarBox::OnCalKey(wxKeyEvent &ev) { if (ev.GetKeyCode() == WXK_ESCAPE && !ev.HasModifiers()) DropDown(false); else ev.Skip(); } #endif // !defined(wxUSE_DATEPICKCTRL) || !wxUSE_DATEPICKCTRL