1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import os.path
20
21 import wx.calendar
22
23 from timelinelib.config.paths import ICONS_DIR
24 from timelinelib.time import try_to_create_wx_date_time_from_dmy
25 from timelinelib.time import WxTimeType
26 from timelinelib.wxgui.utils import _display_error_message
27
28
30
31 - def __init__(self, parent, show_time=True, config=None):
38
40 self.time_picker.Show(show)
41 self.GetSizer().Layout()
42
45
48
50 self.date_picker = WxDatePicker(self)
51 self.date_button = self._create_date_button()
52 self.time_picker = WxTimePicker(self)
53 self._layout_controls()
54
60
62 sizer = wx.BoxSizer(wx.HORIZONTAL)
63 flags = wx.ALIGN_CENTER_VERTICAL
64 sizer.Add(self.date_picker, proportion=1, flag=flags)
65 sizer.Add(self.date_button, proportion=0, flag=flags)
66 sizer.Add(self.time_picker, proportion=0, flag=flags)
67 self.SetSizerAndFit(sizer)
68
76
78 wx_date = self.controller.get_value()
79 calendar_popup = CalendarPopup(self, wx_date, self.config)
80 calendar_popup.Bind(wx.calendar.EVT_CALENDAR_SEL_CHANGED,
81 self._calendar_on_date_changed)
82 calendar_popup.Bind(wx.calendar.EVT_CALENDAR,
83 self._calendar_on_date_changed_dclick)
84 return calendar_popup
85
87 btn = evt.GetEventObject()
88 pos = btn.ClientToScreen((0,0))
89 size = btn.GetSize()
90 self.calendar_popup.Position(pos, (0, size[1]))
91
93 wx_date = evt.GetEventObject().GetDate()
94 self.date_picker.set_date(wx_date)
95
97 self.time_picker.SetFocus()
98 self.calendar_popup.Dismiss()
99
100
102
103 - def __init__(self, date_picker, time_picker, now_fn):
104 self.date_picker = date_picker
105 self.time_picker = time_picker
106 self.now_fn = now_fn
107
109 if date_time == None:
110 date_time = self.now_fn()
111 self.date_picker.set_date(date_time)
112 self.time_picker.set_time(date_time)
113
115 wx_date = self.date_picker.get_date()
116 if self.time_picker.IsShown():
117 wx_time = self.time_picker.get_time()
118 wx_date.SetHour(wx_time.GetHour())
119 wx_date.SetMinute(wx_time.GetMinute())
120 return wx_date
121
122
124
126 self.config = config
127 self.controller = CalendarPopupController(self)
128 wx.PopupTransientWindow.__init__(self, parent, style=wx.BORDER_NONE)
129 border = 2
130 style = self._get_cal_style()
131 self.cal = wx.calendar.CalendarCtrl(self, -1, wx_date,
132 pos=(border,border), style=style)
133 self._set_cal_range()
134 self._set_size(border)
135 self._bind_events()
136
138 style = (wx.calendar.CAL_SHOW_HOLIDAYS |
139 wx.calendar.CAL_SEQUENTIAL_MONTH_SELECTION)
140 if self.config.week_start == "monday":
141 style |= wx.calendar.CAL_MONDAY_FIRST
142 else:
143 style |= wx.calendar.CAL_SUNDAY_FIRST
144 return style
145
151
153 size = self.cal.GetBestSize()
154 self.SetSize((size.width + border * 2, size.height + border * 2))
155
157 def on_month(evt):
158 self.controller.on_month()
159 def on_day(evt):
160 self.controller.on_day()
161 self.cal.Bind(wx.calendar.EVT_CALENDAR_MONTH, on_month)
162 self.cal.Bind(wx.calendar.EVT_CALENDAR_DAY, on_day)
163
166
167
169
171 self.calendar_popup = calendar_popup
172 self.repop = False
173 self.repoped = False
174
177
180
182
183
184
185 if self.repop and not self.repoped:
186 self.calendar_popup.Popup()
187 self.repoped = True
188
189
191
193 wx.TextCtrl.__init__(self, parent, style=wx.TE_PROCESS_ENTER)
194 self.controller = WxDatePickerController(self)
195 self._bind_events()
196 self._resize_to_fit_text()
197
200
203
206
209
211 self.Bind(wx.EVT_SET_FOCUS, self._on_set_focus)
212 self.Bind(wx.EVT_KILL_FOCUS, self._on_kill_focus)
213 self.Bind(wx.EVT_CHAR, self._on_char)
214 self.Bind(wx.EVT_TEXT, self._on_text)
215 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
216
221
223
224
225 self.controller.on_kill_focus()
226 self.SetSelection(0, 0)
227
229 if evt.GetKeyCode() == wx.WXK_TAB:
230 if evt.ShiftDown():
231 skip = self.controller.on_shift_tab()
232 else:
233 skip = self.controller.on_tab()
234 else:
235 skip = True
236 evt.Skip(skip)
237
238 - def _on_text(self, evt):
239 self.controller.on_text_changed()
240
242 if evt.GetKeyCode() == wx.WXK_UP:
243 self.controller.on_up()
244 elif evt.GetKeyCode() == wx.WXK_DOWN:
245 self.controller.on_down()
246 else:
247 evt.Skip()
248
250 w, h = self.GetTextExtent("0000-00-00")
251 width = w + 20
252 self.SetMinSize((width, -1))
253
254
256
257 - def __init__(self, py_date_picker, error_bg="pink"):
258 self.wx_date_picker = py_date_picker
259 self.error_bg = error_bg
260 self.original_bg = self.wx_date_picker.GetBackgroundColour()
261 self.separator = WxTimeType().event_date_string(WxTimeType().now())[4]
262 self.region_year = 0
263 self.region_month = 1
264 self.region_day = 2
265 self.region_siblings = ((self.region_year, self.region_month),
266 (self.region_month, self.region_day))
267 self.preferred_day = None
268 self.save_preferred_day = True
269 self.last_selection = None
270
272 try:
273 (year, month, day) = self._parse_year_month_day()
274 if year == 0:
275 raise ValueError("Invalid date.")
276 if year < 0:
277 year +=1
278 wx_date = try_to_create_wx_date_time_from_dmy(day, month - 1, year)
279 self._ensure_date_within_allowed_period(wx_date)
280 return wx_date
281 except ValueError:
282 raise ValueError("Invalid date.")
283
285 bc_year = wx.DateTime.ConvertYearToBC(wx_date.Year)
286 tmp_date = wx.DateTimeFromDMY(wx_date.Day, wx_date.Month, bc_year)
287 date_string = WxTimeType().event_date_string(tmp_date)
288 self.wx_date_picker.set_date_string(date_string)
289
291 if self.last_selection:
292 start, end = self.last_selection
293 self.wx_date_picker.SetSelection(start, end)
294 else:
295 self._select_region_if_possible(self.region_year)
296 self.last_selection = self.wx_date_picker.GetSelection()
297
299 if self.last_selection:
300 self.last_selection = self.wx_date_picker.GetSelection()
301
303 for (left_region, right_region) in self.region_siblings:
304 if self._insertion_point_in_region(left_region):
305 self._select_region_if_possible(right_region)
306 return False
307 return True
308
310 for (left_region, right_region) in self.region_siblings:
311 if self._insertion_point_in_region(right_region):
312 self._select_region_if_possible(left_region)
313 return False
314 return True
315
316 - def on_text_changed(self):
317 self._change_background_depending_on_date_validity()
318 if self._current_date_is_valid():
319 current_date = self.get_date()
320
321
322
323
324
325
326 if self.save_preferred_day:
327 self._save_preferred_day(current_date)
328
330 if not self._current_date_is_valid():
331 return
332 if self._insertion_point_in_region(self.region_year):
333 new_date = self._increment_year()
334 elif self._insertion_point_in_region(self.region_month):
335 new_date = self._increment_month()
336 else:
337 new_date = self._increment_day()
338 self._save_preferred_day(new_date)
339 self._set_new_date_and_restore_selection(new_date)
340
342 if not self._current_date_is_valid():
343 return
344 if self._insertion_point_in_region(self.region_year):
345 new_date = self._decrement_year()
346 elif self._insertion_point_in_region(self.region_month):
347 new_date = self._decrement_month()
348 else:
349 new_date = self._decrement_day()
350 self._save_preferred_day(new_date)
351 self._set_new_date_and_restore_selection(new_date)
352
358
366
372
378
386
396
398 if self._current_date_is_valid():
399 color = self.original_bg
400 else:
401 color = self.error_bg
402 self.wx_date_picker.SetBackgroundColour(color)
403 self.wx_date_picker.SetFocus()
404 self.wx_date_picker.Refresh()
405
407 date_string = self.wx_date_picker.get_date_string()
408 date_bc = False
409 if (date_string[0:1] == self.separator):
410 date_string = date_string[1:]
411 date_bc = True
412 components = date_string.split(self.separator)
413 if len(components) != 3:
414 raise ValueError()
415 year = int(components[self.region_year])
416 if date_bc:
417 year = -year
418 month = int(components[self.region_month])
419 day = int(components[self.region_day])
420 return (year, month, day)
421
426
428 def restore_selection(selection):
429 self.wx_date_picker.SetSelection(selection[0], selection[1])
430 selection = self.wx_date_picker.GetSelection()
431 self.save_preferred_day = False
432 if self.preferred_day != None:
433 new_date = self._set_valid_day(new_date.Year, new_date.Month,
434 self.preferred_day)
435 self.set_date(new_date)
436 restore_selection(selection)
437 self.save_preferred_day = True
438
445
447 day = date.Day
448 if day > 28:
449 self.preferred_day = day
450 else:
451 self.preferred_day = None
452
454 try:
455 self.get_date()
456 except ValueError:
457 return False
458 return True
459
461 region_range = self._get_region_range(region)
462 if region_range:
463 self.wx_date_picker.SetSelection(region_range[0], region_range[-1])
464
466 region_range = self._get_region_range(n)
467 if region_range:
468 return self.wx_date_picker.GetInsertionPoint() in region_range
469
471
472
473 def region_is_not_valid(region):
474 return region not in (self.region_year, self.region_month,
475 self.region_day)
476 def date_has_exactly_two_seperators(datestring):
477 return len(datestring.split(self.separator)) == 3
478 def calculate_pos_range(region, datestring):
479 pos_of_separator1 = datestring.find(self.separator)
480 pos_of_separator2 = datestring.find(self.separator,
481 pos_of_separator1 + 1)
482 if region == self.region_year:
483 return range(0, pos_of_separator1 + 1)
484 elif region == self.region_month:
485 return range(pos_of_separator1 + 1, pos_of_separator2 + 1)
486 else:
487 return range(pos_of_separator2 + 1, len(datestring) + 1)
488 if region_is_not_valid(n):
489 return None
490 date = self.wx_date_picker.get_date_string()
491 if not date_has_exactly_two_seperators(date):
492 return None
493 pos_range = calculate_pos_range(n, date)
494 return pos_range
495
496
498
500 wx.TextCtrl.__init__(self, parent, style=wx.TE_PROCESS_ENTER)
501 self.controller = WxTimePickerController(self)
502 self._bind_events()
503 self._resize_to_fit_text()
504
507
510
513
516
518 self.Bind(wx.EVT_SET_FOCUS, self._on_set_focus)
519 self.Bind(wx.EVT_KILL_FOCUS, self._on_kill_focus)
520 self.Bind(wx.EVT_CHAR, self._on_char)
521 self.Bind(wx.EVT_TEXT, self._on_text)
522 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
523
528
530
531
532 self.controller.on_kill_focus()
533 self.SetSelection(0, 0)
534
536 if evt.GetKeyCode() == wx.WXK_TAB:
537 if evt.ShiftDown():
538 skip = self.controller.on_shift_tab()
539 else:
540 skip = self.controller.on_tab()
541 else:
542 skip = True
543 evt.Skip(skip)
544
545 - def _on_text(self, evt):
546 self.controller.on_text_changed()
547
549 if evt.GetKeyCode() == wx.WXK_UP:
550 self.controller.on_up()
551 elif evt.GetKeyCode() == wx.WXK_DOWN:
552 self.controller.on_down()
553 else:
554 evt.Skip()
555
557 w, h = self.GetTextExtent("00:00")
558 width = w + 20
559 self.SetMinSize((width, -1))
560
561
563
565 self.wx_time_picker = wx_time_picker
566 self.original_bg = self.wx_time_picker.GetBackgroundColour()
567 self.separator = WxTimeType().event_time_string(WxTimeType().now())[2]
568 self.hour_part = 0
569 self.minute_part = 1
570 self.last_selection = None
571
573 try:
574 split = self.wx_time_picker.get_time_string().split(self.separator)
575 if len(split) != 2:
576 raise ValueError()
577 hour_string, minute_string = split
578 hour = int(hour_string)
579 minute = int(minute_string)
580 return wx.DateTimeFromHMS(hour, minute, 0)
581 except ValueError:
582 raise ValueError("Invalid time.")
583
587
589 if self.last_selection:
590 start, end = self.last_selection
591 self.wx_time_picker.SetSelection(start, end)
592 else:
593 self._select_part(self.hour_part)
594
596 self.last_selection = self.wx_time_picker.GetSelection()
597
599 if self._in_minute_part():
600 return True
601 self._select_part(self.minute_part)
602 return False
603
605 if self._in_hour_part():
606 return True
607 self._select_part(self.hour_part)
608 return False
609
610 - def on_text_changed(self):
611 try:
612 self.get_time()
613 self.wx_time_picker.SetBackgroundColour(self.original_bg)
614 except ValueError:
615 self.wx_time_picker.SetBackgroundColour("pink")
616 self.wx_time_picker.Refresh()
617
619 if not self._time_is_valid():
620 return
621 if self._in_hour_part():
622 new_time = self._increment_hour()
623 else:
624 new_time = self._increment_minutes()
625 self._set_new_time_and_restore_selection(new_time)
626
628 if not self._time_is_valid():
629 return
630 if self._in_hour_part():
631 new_time = self._decrement_hour()
632 else:
633 new_time = self._decrement_minutes()
634 self._set_new_time_and_restore_selection(new_time)
635
637 time = self.get_time()
638 new_hour = time.Hour + 1
639 if new_hour > 23:
640 new_hour = 0
641 time.SetHour(new_hour)
642 return time
643
645 time = self.get_time()
646 new_hour = time.Hour
647 new_minute = time.Minute + 1
648 if new_minute > 59:
649 new_minute = 0
650 new_hour = time.Hour + 1
651 if new_hour > 23:
652 new_hour = 0
653 time.SetHour(new_hour)
654 time.SetMinute(new_minute)
655 return time
656
658 time = self.get_time()
659 new_hour = time.Hour - 1
660 if new_hour < 0:
661 new_hour = 23
662 time.SetHour(new_hour)
663 return time
664
666 time = self.get_time()
667 new_hour = time.Hour
668 new_minute = time.Minute - 1
669 if new_minute < 0:
670 new_minute = 59
671 new_hour = time.Hour - 1
672 if new_hour < 0:
673 new_hour = 23
674 time.SetHour(new_hour)
675 time.SetMinute(new_minute)
676 return time
677
679 def restore_selection(selection):
680 self.wx_time_picker.SetSelection(selection[0], selection[1])
681 selection = self.wx_time_picker.GetSelection()
682 self.set_time(new_time)
683 restore_selection(selection)
684
686 try:
687 self.get_time()
688 except ValueError:
689 return False
690 return True
691
693 if self._separator_pos() == -1:
694 return
695 if part == self.hour_part:
696 self.wx_time_picker.SetSelection(0, self._separator_pos())
697 else:
698 time_string_len = len(self.wx_time_picker.get_time_string())
699 self.wx_time_picker.SetSelection(self._separator_pos() + 1, time_string_len)
700 self.preferred_part = part
701
703 if self._separator_pos() == -1:
704 return
705 return self.wx_time_picker.GetInsertionPoint() <= self._separator_pos()
706
708 if self._separator_pos() == -1:
709 return
710 return self.wx_time_picker.GetInsertionPoint() > self._separator_pos()
711
714