remove margins in the bitmap button
[wxWidgets.git] / src / generic / datectlg.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: generic/datectlg.cpp
3 // Purpose: generic wxDatePickerCtrlGeneric implementation
4 // Author: Andreas Pflug
5 // Modified by:
6 // Created: 2005-01-19
7 // RCS-ID: $Id$
8 // Copyright: (c) 2005 Andreas Pflug <pgadmin@pse-consulting.de>
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #include "wx/wxprec.h"
21
22 #ifdef __BORLANDC__
23 #pragma hdrstop
24 #endif
25
26 #if wxUSE_DATEPICKCTRL
27
28 #include "wx/datectrl.h"
29
30 // use this version if we're explicitly requested to do it or if it's the only
31 // one we have
32 #if wxUSE_DATEPICKCTRL_GENERIC || !defined(wxHAS_NATIVE_DATEPICKCTRL)
33
34 #ifndef WX_PRECOMP
35 #include "wx/bmpbuttn.h"
36 #include "wx/dialog.h"
37 #include "wx/dcmemory.h"
38 #include "wx/panel.h"
39 #include "wx/textctrl.h"
40 #include "wx/valtext.h"
41 #endif
42
43 // otherwise it's defined in the native version implementation
44 #ifndef wxHAS_NATIVE_DATEPICKCTRL
45 #define _WX_DEFINE_DATE_EVENTS_
46 #endif
47
48 #include "wx/dateevt.h"
49 #include "wx/generic/datectrl.h"
50
51 #include "wx/calctrl.h"
52 #include "wx/popupwin.h"
53 #include "wx/renderer.h"
54
55 // ----------------------------------------------------------------------------
56 // constants
57 // ----------------------------------------------------------------------------
58
59 enum
60 {
61 CTRLID_TXT = 101,
62 CTRLID_CAL,
63 CTRLID_BTN,
64 CTRLID_PAN
65 };
66
67 #ifndef DEFAULT_ITEM_WIDTH
68 #define DEFAULT_ITEM_WIDTH 100
69 #endif
70
71 // ============================================================================
72 // wxDatePickerCtrlGeneric implementation
73 // ============================================================================
74
75 BEGIN_EVENT_TABLE(wxDatePickerCtrlGeneric, wxDatePickerCtrlBase)
76 EVT_BUTTON(CTRLID_BTN, wxDatePickerCtrlGeneric::OnClick)
77 EVT_TEXT(CTRLID_TXT, wxDatePickerCtrlGeneric::OnText)
78 EVT_CHILD_FOCUS(wxDatePickerCtrlGeneric::OnChildSetFocus)
79 END_EVENT_TABLE()
80
81 #ifndef wxHAS_NATIVE_DATEPICKCTRL
82 IMPLEMENT_DYNAMIC_CLASS(wxDatePickerCtrl, wxDatePickerCtrlBase)
83 #endif
84
85 // ----------------------------------------------------------------------------
86 // creation
87 // ----------------------------------------------------------------------------
88
89 bool wxDatePickerCtrlGeneric::Create(wxWindow *parent,
90 wxWindowID id,
91 const wxDateTime& date,
92 const wxPoint& pos,
93 const wxSize& size,
94 long style,
95 const wxString& name)
96 {
97 wxASSERT_MSG( !(style & wxDP_SPIN),
98 _T("wxDP_SPIN style not supported, use wxDP_DEFAULT") );
99
100 if ( !wxControl::Create(parent, id, pos, size,
101 style | wxCLIP_CHILDREN | wxWANTS_CHARS,
102 wxDefaultValidator, name) )
103
104 {
105 return false;
106 }
107
108 InheritAttributes();
109
110 m_txt = new wxTextCtrl(this, CTRLID_TXT);
111 m_txt->Connect(wxID_ANY, wxID_ANY, wxEVT_KEY_DOWN,
112 (wxObjectEventFunction)&wxDatePickerCtrlGeneric::OnEditKey,
113 0, this);
114 m_txt->Connect(wxID_ANY, wxID_ANY, wxEVT_KILL_FOCUS,
115 (wxObjectEventFunction)&wxDatePickerCtrlGeneric::OnKillFocus,
116 0, this);
117
118 const int height = m_txt->GetBestSize().y - 4; // FIXME: fudge
119 wxBitmap bmp(height, height);
120 {
121 wxMemoryDC dc;
122 dc.SelectObject(bmp);
123 wxRendererNative::Get().DrawComboBoxDropButton
124 (
125 this,
126 dc,
127 wxRect(0, 0, height, height)
128 );
129 }
130
131 wxBitmapButton *btn = new wxBitmapButton(this, CTRLID_BTN, bmp);
132 btn->SetMargins(0, 0);
133 m_btn = btn;
134
135 m_popup = new wxPopupWindow(this);
136 m_popup->SetFont(GetFont());
137
138 wxPanel *panel=new wxPanel(m_popup, CTRLID_PAN,
139 wxPoint(0, 0), wxDefaultSize,
140 wxSUNKEN_BORDER);
141 m_cal = new wxCalendarCtrl(panel, CTRLID_CAL, wxDefaultDateTime,
142 wxPoint(0,0), wxDefaultSize,
143 wxCAL_SHOW_HOLIDAYS | wxSUNKEN_BORDER);
144 m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_SEL_CHANGED,
145 (wxObjectEventFunction)&wxDatePickerCtrlGeneric::OnSelChange,
146 0, this);
147 m_cal->Connect(wxID_ANY, wxID_ANY, wxEVT_KEY_DOWN,
148 (wxObjectEventFunction)&wxDatePickerCtrlGeneric::OnCalKey,
149 0, this);
150 m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_DOUBLECLICKED,
151 (wxObjectEventFunction)&wxDatePickerCtrlGeneric::OnSelChange,
152 0, this);
153 m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_DAY_CHANGED,
154 (wxObjectEventFunction)&wxDatePickerCtrlGeneric::OnSelChange,
155 0, this);
156 m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_MONTH_CHANGED,
157 (wxObjectEventFunction)&wxDatePickerCtrlGeneric::OnSelChange,
158 0, this);
159 m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_YEAR_CHANGED,
160 (wxObjectEventFunction)&wxDatePickerCtrlGeneric::OnSelChange,
161 0, this);
162
163 wxWindow *yearControl = m_cal->GetYearControl();
164
165 Connect(wxID_ANY, wxID_ANY, wxEVT_SET_FOCUS,
166 (wxObjectEventFunction)&wxDatePickerCtrlGeneric::OnSetFocus);
167
168 wxClientDC dc(yearControl);
169 dc.SetFont(m_font);
170 wxCoord width, dummy;
171 dc.GetTextExtent(wxT("2000"), &width, &dummy);
172 width += ConvertDialogToPixels(wxSize(20,0)).x;
173
174 wxSize calSize = m_cal->GetBestSize();
175 wxSize yearSize = yearControl->GetSize();
176 yearSize.x = width;
177
178 wxPoint yearPosition = yearControl->GetPosition();
179
180 SetFormat(wxT("%x"));
181
182 if (date.IsValid())
183 m_txt->SetValue(date.Format(m_format));
184
185
186 #ifdef __WXMSW__
187 #define CALBORDER 0
188 #else
189 #define CALBORDER 4
190 #endif
191
192 width = yearPosition.x + yearSize.x+2+CALBORDER/2;
193 if (width < calSize.x-4)
194 width = calSize.x-4;
195
196 int calPos = (width-calSize.x)/2;
197 if (calPos == -1)
198 {
199 calPos = 0;
200 width += 2;
201 }
202 m_cal->SetSize(calPos, 0, calSize.x, calSize.y);
203 yearControl->SetSize(width-yearSize.x-CALBORDER/2, yearPosition.y,
204 yearSize.x, yearSize.y);
205 m_cal->GetMonthControl()->Move(0, 0);
206
207
208
209 panel->SetClientSize(width+CALBORDER/2, calSize.y-2+CALBORDER);
210 m_popup->SetClientSize(panel->GetSize());
211 m_popup->Hide();
212
213 return TRUE;
214 }
215
216
217 void wxDatePickerCtrlGeneric::Init()
218 {
219 m_popup = NULL;
220 m_txt = NULL;
221 m_cal = NULL;
222 m_btn = NULL;
223
224 m_dropped = false;
225 m_ignoreDrop = false;
226 }
227
228
229 bool wxDatePickerCtrlGeneric::Destroy()
230 {
231 if (m_cal)
232 m_cal->Destroy();
233 if (m_popup)
234 m_popup->Destroy();
235 if (m_txt)
236 m_txt->Destroy();
237 if (m_btn)
238 m_btn->Destroy();
239
240 m_popup = NULL;
241 m_txt = NULL;
242 m_cal = NULL;
243 m_btn = NULL;
244
245 return wxControl::Destroy();
246 }
247
248 // ----------------------------------------------------------------------------
249 // overridden base class methods
250 // ----------------------------------------------------------------------------
251
252 void wxDatePickerCtrlGeneric::DoMoveWindow(int x, int y, int w, int h)
253 {
254 wxControl::DoMoveWindow(x, y, w, h);
255 wxSize bs=m_btn->GetBestSize();
256 int eh=m_txt->GetBestSize().y;
257
258 m_txt->SetSize(0, 0, w-bs.x-1, h > eh ? eh : h);
259 m_btn->SetSize(w - bs.x, 0, bs.x, h > bs.y ? bs.y : h);
260
261 if (m_dropped)
262 DropDown();
263 }
264
265 wxSize wxDatePickerCtrlGeneric::DoGetBestSize() const
266 {
267 int bh=m_btn->GetBestSize().y;
268 int eh=m_txt->GetBestSize().y;
269 return wxSize(DEFAULT_ITEM_WIDTH, bh > eh ? bh : eh);
270 }
271
272
273 bool wxDatePickerCtrlGeneric::Show(bool show)
274 {
275 if ( !wxControl::Show(show) )
276 {
277 return FALSE;
278 }
279
280 if (!show)
281 {
282 if (m_popup)
283 {
284 m_popup->Hide();
285 m_dropped = false;
286 }
287 }
288
289 return TRUE;
290 }
291
292
293 bool wxDatePickerCtrlGeneric::Enable(bool enable)
294 {
295 if ( !wxControl::Enable(enable) )
296 {
297 return FALSE;
298 }
299
300 if (!enable)
301 {
302 if (m_cal)
303 m_cal->Hide();
304 }
305 if (m_btn)
306 m_btn->Enable(enable);
307 return TRUE;
308 }
309
310 // ----------------------------------------------------------------------------
311 // wxDatePickerCtrlGeneric API
312 // ----------------------------------------------------------------------------
313
314 bool
315 wxDatePickerCtrlGeneric::SetDateRange(const wxDateTime& lowerdate,
316 const wxDateTime& upperdate)
317 {
318 return m_cal->SetDateRange(lowerdate, upperdate);
319 }
320
321 bool wxDatePickerCtrlGeneric::SetFormat(const wxChar *fmt)
322 {
323 wxString currentText;
324 wxDateTime currentDate;
325 if (m_txt)
326 {
327 currentText = m_txt->GetValue();
328 if (!currentText.IsEmpty())
329 currentDate.ParseFormat(currentText, m_format);
330 }
331 wxDateTime dt;
332 dt.ParseFormat(wxT("2003-10-13"), wxT("%Y-%m-%d"));
333 wxString str=dt.Format(fmt);
334 wxChar *p=(wxChar*)str.c_str();
335
336 m_format=wxEmptyString;
337
338 while (*p)
339 {
340 int n=wxAtoi(p);
341 if (n == dt.GetDay())
342 {
343 m_format.Append(wxT("%d"));
344 p += 2;
345 }
346 else if (n == (int)dt.GetMonth()+1)
347 {
348 m_format.Append(wxT("%m"));
349 p += 2;
350 }
351 else if (n == dt.GetYear())
352 {
353 m_format.Append(wxT("%Y"));
354 p += 4;
355 }
356 else if (n == (dt.GetYear() % 100))
357 {
358 m_format.Append(wxT("%y"));
359 p += 2;
360 }
361 else
362 m_format.Append(*p++);
363 }
364
365 if (m_txt)
366 {
367 wxStringList valList;
368 wxChar c;
369 for (c='0'; c <= '9'; c++)
370 valList.Add(wxString(c, 1));
371 wxChar *p=(wxChar*)m_format.c_str();
372 while (*p)
373 {
374 if (*p == '%')
375 p += 2;
376 else
377 valList.Add(wxString(*p++, 1));
378 }
379 wxTextValidator tv(wxFILTER_INCLUDE_CHAR_LIST);
380 tv.SetIncludeList(valList);
381
382 m_txt->SetValidator(tv);
383
384 if (!currentText.IsEmpty())
385 m_txt->SetValue(currentDate.Format(m_format));
386 }
387 return true;
388 }
389
390
391 wxDateTime wxDatePickerCtrlGeneric::GetValue() const
392 {
393 wxDateTime dt;
394 wxString txt=m_txt->GetValue();
395
396 if (!txt.IsEmpty())
397 dt.ParseFormat(txt, m_format);
398
399 return dt;
400 }
401
402
403 void wxDatePickerCtrlGeneric::SetValue(const wxDateTime& date)
404 {
405 if (m_cal)
406 {
407 if (date.IsValid())
408 m_txt->SetValue(date.Format(m_format));
409 else
410 m_txt->SetValue(wxEmptyString);
411 }
412 }
413
414
415 bool wxDatePickerCtrlGeneric::GetRange(wxDateTime *dt1, wxDateTime *dt2) const
416 {
417 if (dt1)
418 *dt1 = m_cal->GetLowerDateLimit();
419 if (dt1)
420 *dt2 = m_cal->GetUpperDateLimit();
421 return true;
422 }
423
424
425 void
426 wxDatePickerCtrlGeneric::SetRange(const wxDateTime &dt1, const wxDateTime &dt2)
427 {
428 m_cal->SetDateRange(dt1, dt2);
429 }
430
431 // ----------------------------------------------------------------------------
432 // event handlers
433 // ----------------------------------------------------------------------------
434
435 void wxDatePickerCtrlGeneric::DropDown(bool down)
436 {
437 if (m_popup)
438 {
439 if (down)
440 {
441 wxDateTime dt;
442 if (!m_txt->GetValue().IsEmpty())
443 dt.ParseFormat(m_txt->GetValue(), m_format);
444
445 if (dt.IsValid())
446 m_cal->SetDate(dt);
447 else
448 m_cal->SetDate(wxDateTime::Today());
449
450 wxPoint pos=GetParent()->ClientToScreen(GetPosition());
451 m_popup->Move(pos.x, pos.y + GetSize().y);
452 m_popup->Show();
453 m_dropped = true;
454 }
455 else
456 {
457 if (m_dropped)
458 m_popup->Hide();
459 m_dropped = false;
460 }
461 }
462 }
463
464
465 void wxDatePickerCtrlGeneric::OnChildSetFocus(wxChildFocusEvent &ev)
466 {
467 ev.Skip();
468 m_ignoreDrop = false;
469
470 wxWindow *w=(wxWindow*)ev.GetEventObject();
471 while (w)
472 {
473 if (w == m_popup)
474 return;
475 w = w->GetParent();
476 }
477
478 if (m_dropped)
479 {
480 DropDown(false);
481 if (ev.GetEventObject() == m_btn)
482 m_ignoreDrop = true;
483 }
484 }
485
486
487 void wxDatePickerCtrlGeneric::OnClick(wxCommandEvent& WXUNUSED(event))
488 {
489 if (m_ignoreDrop)
490 {
491 m_ignoreDrop = false;
492 m_txt->SetFocus();
493 }
494 else
495 {
496 DropDown();
497 m_cal->SetFocus();
498 }
499 }
500
501
502 void wxDatePickerCtrlGeneric::OnSetFocus(wxFocusEvent& WXUNUSED(ev))
503 {
504 if (m_txt)
505 {
506 m_txt->SetFocus();
507 m_txt->SetSelection(0, 100);
508 }
509 }
510
511
512 void wxDatePickerCtrlGeneric::OnKillFocus(wxFocusEvent &ev)
513 {
514 ev.Skip();
515
516 wxDateTime dt;
517 dt.ParseFormat(m_txt->GetValue(), m_format);
518 if (!dt.IsValid())
519 m_txt->SetValue(wxEmptyString);
520 else
521 m_txt->SetValue(dt.Format(m_format));
522 }
523
524
525 void wxDatePickerCtrlGeneric::OnSelChange(wxCalendarEvent &ev)
526 {
527 if (m_cal)
528 {
529 m_txt->SetValue(m_cal->GetDate().Format(m_format));
530 if (ev.GetEventType() == wxEVT_CALENDAR_DOUBLECLICKED)
531 {
532 DropDown(false);
533 m_txt->SetFocus();
534 }
535 }
536 ev.SetEventObject(this);
537 ev.SetId(GetId());
538 GetParent()->ProcessEvent(ev);
539 }
540
541
542 void wxDatePickerCtrlGeneric::OnText(wxCommandEvent &ev)
543 {
544 ev.SetEventObject(this);
545 ev.SetId(GetId());
546 GetParent()->ProcessEvent(ev);
547
548 // We'll create an additional event if the date is valid.
549 // If the date isn't valid, the user's probable in the middle of typing
550 wxString txt=m_txt->GetValue();
551 wxDateTime dt;
552 if (!txt.IsEmpty())
553 {
554 dt.ParseFormat(txt, m_format);
555 if (!dt.IsValid())
556 return;
557 }
558
559 wxCalendarEvent cev(m_cal, wxEVT_CALENDAR_SEL_CHANGED);
560 cev.SetEventObject(this);
561 cev.SetId(GetId());
562 cev.SetDate(dt);
563
564 GetParent()->ProcessEvent(cev);
565 }
566
567
568 void wxDatePickerCtrlGeneric::OnEditKey(wxKeyEvent & ev)
569 {
570 if (ev.GetKeyCode() == WXK_DOWN && !ev.HasModifiers())
571 DropDown();
572 else
573 ev.Skip();
574 }
575
576
577 void wxDatePickerCtrlGeneric::OnCalKey(wxKeyEvent & ev)
578 {
579 if (ev.GetKeyCode() == WXK_ESCAPE && !ev.HasModifiers())
580 DropDown(false);
581 else
582 ev.Skip();
583 }
584
585 #endif // wxUSE_DATEPICKCTRL_GENERIC
586
587 #endif // wxUSE_DATEPICKCTRL
588