1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import sys
20 import re
21
22 import wx
23
24 from timelinelib.calendar.monthnames import abbreviated_name_of_month
25 from timelinelib.db.objects import TimePeriod
26 from timelinelib.db.objects import time_period_center
27 from timelinelib.drawing.interface import Strip
28 from timelinelib.drawing.utils import get_default_font
29 from timelinelib.time.typeinterface import TimeType
30
31
32
33 US_PER_SEC = 1000000
34 US_PER_HOUR = 60 * 60 * 1000 * 1000
35 US_PER_MINUTE = 60 * 1000 * 1000
36 US_PER_DAY = 24 * 60 * 60 * US_PER_SEC
37 MIN_YEAR = -4700
38 MAX_YEAR = 120000
39
41
44
46 return not (self == other)
47
50
63
65 return [
66 (_("Go to &Today\tCtrl+T"), go_to_today_fn),
67 (_("Go to D&ate...\tCtrl+G"), go_to_date_fn),
68 ("SEP", None),
69 (_("Backward\tPgUp"), backward_fn),
70 (_("Forward\tPgDn"), forward_fn),
71 (_("Forward One Wee&k\tCtrl+K"), forward_one_week_fn),
72 (_("Back One &Week\tCtrl+W"), backward_one_week_fn),
73 (_("Forward One Mont&h\tCtrl+h"), forward_one_month_fn),
74 (_("Back One &Month\tCtrl+M"), backward_one_month_fn),
75 (_("Forward One Yea&r\tCtrl+R"), forward_one_year_fn),
76 (_("Back One &Year\tCtrl+Y"), backward_one_year_fn),
77 ("SEP", None),
78 (_("Fit Millennium"), fit_millennium_fn),
79 (_("Fit Century"), fit_century_fn),
80 (_("Fit Decade"), fit_decade_fn),
81 (_("Fit Year"), fit_year_fn),
82 (_("Fit Month"), fit_month_fn),
83 (_("Fit Week"), fit_week_fn),
84 (_("Fit Day"), fit_day_fn),
85 ]
86
89
94 def label_without_time(time):
95 return "%s %s %s" % (
96 time.Format("%d"),
97 abbreviated_name_of_month(int(time.Format("%m"))),
98 time.Format("%Y"))
99 def time_label(time):
100 return time.Format("%H:%M")
101 if time_period.is_period():
102 if time_period.has_nonzero_time():
103 label = u"%s to %s" % (label_with_time(time_period.start_time),
104 label_with_time(time_period.end_time))
105 else:
106 label = u"%s to %s" % (label_without_time(time_period.start_time),
107 label_without_time(time_period.end_time))
108 else:
109 if time_period.has_nonzero_time():
110 label = u"%s" % label_with_time(time_period.start_time)
111 else:
112 label = u"%s" % label_without_time(time_period.start_time)
113 return label
114
136
138 min_time = wx.DateTimeFromDMY(1, 0, MIN_YEAR)
139 return (min_time, _("can't be before year %d") % (MIN_YEAR))
140
142 max_time = wx.DateTimeFromDMY(1, 0, MAX_YEAR)
143 return (max_time, _("can't be after year %d") % (MAX_YEAR))
144
146 """
147 Return a tuple (major_strip, minor_strip) for current time period and
148 window size.
149 """
150 today = metrics.time_period.start_time
151 tomorrow = today + wx.DateSpan.Day()
152 day_period = TimePeriod(self, today, tomorrow)
153 one_day_width = metrics.calc_exact_width(day_period)
154 if one_day_width > 600:
155 return (StripDay(), StripHour())
156 elif one_day_width > 45:
157 return (StripWeek(config), StripWeekday())
158 elif one_day_width > 25:
159 return (StripMonth(), StripDay())
160 elif one_day_width > 1.5:
161 return (StripYear(), StripMonth())
162 elif one_day_width > 0.12:
163 return (StripDecade(), StripYear())
164 elif one_day_width > 0.012:
165 return (StripCentury(), StripDecade())
166 else:
167 return (StripCentury(), StripCentury())
168
174
177
179 return wx.DateTime.Now()
180
186
196
198 max_zoom_delta = wx.TimeSpan.Days(1200 * 365)
199 return (max_zoom_delta, _("Can't zoom wider than 1200 years"))
200
202 return (wx.TimeSpan.Hour(), _("Can't zoom deeper than 1 hour"))
203
206
208 nonzero_time = (not time_period.start_time.IsSameTime(wx.DateTimeFromHMS(0, 0, 0)) or
209 not time_period.end_time.IsSameTime(wx.DateTimeFromHMS(0, 0, 0)) )
210 return nonzero_time
211
214
222
224 return (delta.GetMilliseconds() > 3600000) or (delta.GetDays() > 0)
225
229
233
236
239
246
248 if time.Year == 0:
249 return time + wx.DateSpan.Year()
250 else:
251 return time
252
253
255 navigation_fn(lambda tp: tp.center(wx.DateTime.Now()))
256
257
259 def navigate_to(time):
260 navigation_fn(lambda tp: tp.center(time))
261 main_frame.display_time_editor_dialog(
262 WxTimeType(), current_period.mean_time(), navigate_to, _("Go to Date"))
263
264
265 -def backward_fn(main_frame, current_period, navigation_fn):
267
268
269 -def forward_fn(main_frame, current_period, navigation_fn):
271
272
273 -def move_page_smart(current_period, navigation_fn, direction):
274 start, end = current_period.start_time, current_period.end_time
275 year_diff = end.Year - start.Year
276 start_months = start.Year * 12 + start.Month
277 end_months = end.Year * 12 + end.Month
278 month_diff = end_months - start_months
279 whole_years = start.Month == end.Month and start.Day == end.Day
280 whole_months = start.Day == 1 and end.Day == 1
281 direction_backward = direction < 0
282
283 if whole_years and year_diff > 0:
284 _move_smart_year(navigation_fn, direction, start, end)
285
286 elif whole_months and month_diff > 0:
287 _move_smart_month(navigation_fn, direction_backward, start, end)
288
289 else:
290 navigation_fn(lambda tp: tp.move_delta(direction*current_period.delta()))
291
292
294 year_diff = direction * (end.Year - start.Year)
295 start.SetYear(start.Year + year_diff)
296 end.SetYear(end.Year + year_diff)
297 navigation_fn(lambda tp: tp.update(start, end))
298
299
301 years = int(months / 12)
302 month = months - years * 12
303 if month == 12:
304 month = 0
305 years -=1
306 return years, month
307
308
310 if direction_backward:
311 _move_smart_month_backward(navigation_fn, start, end)
312 else:
313 _move_smart_month_forward(navigation_fn, start, end)
314
315
317 start_months = start.Year * 12 + start.Month
318 end_months = end.Year * 12 + end.Month
319 month_diff = end_months - start_months
320 new_end = start
321 new_start_year, new_start_month = _months_to_year_and_month(
322 start_months -
323 month_diff)
324 new_start = wx.DateTimeFromDMY(start.Day, new_start_month, new_start_year,
325 start.Hour, start.Minute, start.Second)
326 navigation_fn(lambda tp: tp.update(new_start, new_end))
327
328
330 start_months = start.Year * 12 + start.Month
331 end_months = end.Year * 12 + end.Month
332 month_diff = end_months - start_months
333 new_start = end
334 new_end_year, new_end_month = _months_to_year_and_month(
335 end_months +
336 month_diff)
337 new_end = wx.DateTimeFromDMY(end.Day, new_end_month, new_end_year,
338 end.Hour, end.Minute, end.Second)
339 navigation_fn(lambda tp: tp.update(new_start, new_end))
340
341
343 week = wx.DateSpan.Week()
344 navigation_fn(lambda tp: tp.move_delta(week))
345
346
348 week = wx.DateSpan.Week()
349 navigation_fn(lambda tp: tp.move_delta(-1 * week))
350
351
353 """
354 Currently does not notice leap years.
355 """
356 tm = current_period.mean_time()
357 if direction > 0:
358 if tm.Month == 1:
359 d = 28
360 elif tm.Month in (3,5,8,10):
361 d = 30
362 else:
363 d = 31
364 else:
365 if tm.Month == 2:
366 d = 28
367 elif tm.Month in (4,6,9,11):
368 d = 30
369 else:
370 d = 31
371 mv = wx.DateSpan.Days(d)
372 navigation_fn(lambda tp: tp.move_delta(direction*mv))
373
374
377
378
381
382
384 yr = wx.DateSpan.Year()
385 navigation_fn(lambda tp: tp.move_delta(yr))
386
387
389 yr = wx.DateSpan.Year()
390 navigation_fn(lambda tp: tp.move_delta(-1*yr))
391
392
394 mean = current_period.mean_time()
395 start = wx.DateTimeFromDMY(1, 0, int(mean.Year/1000)*1000)
396 end = wx.DateTimeFromDMY(1, 0, int(mean.Year/1000)*1000 + 1000)
397 navigation_fn(lambda tp: tp.update(start, end))
398
399
401 mean = current_period.mean_time()
402 start = wx.DateTimeFromDMY(1, 0, int(mean.Year/100)*100)
403 end = wx.DateTimeFromDMY(1, 0, int(mean.Year/100)*100 + 100)
404 navigation_fn(lambda tp: tp.update(start, end))
405
406
408 mean = current_period.mean_time()
409 start = wx.DateTimeFromDMY(1, 0, int(mean.Year/10)*10)
410 end = wx.DateTimeFromDMY(1, 0, int(mean.Year/10)*10+10)
411 navigation_fn(lambda tp: tp.update(start, end))
412
413
414 -def fit_year_fn(main_frame, current_period, navigation_fn):
415 mean = current_period.mean_time()
416 start = wx.DateTimeFromDMY(1, 0, mean.Year)
417 end = wx.DateTimeFromDMY(1, 0, mean.Year + 1)
418 navigation_fn(lambda tp: tp.update(start, end))
419
420
421 -def fit_month_fn(main_frame, current_period, navigation_fn):
422 mean = current_period.mean_time()
423 start = wx.DateTimeFromDMY(1, mean.Month, mean.Year)
424 if mean.Month == 11:
425 end = wx.DateTimeFromDMY(1, 0, mean.Year + 1)
426 else:
427 end = wx.DateTimeFromDMY(1, mean.Month + 1, mean.Year)
428 navigation_fn(lambda tp: tp.update(start, end))
429
430
431 -def fit_day_fn(main_frame, current_period, navigation_fn):
432 mean = current_period.mean_time()
433 start = wx.DateTimeFromDMY(mean.Day, mean.Month, mean.Year)
434 end = start + wx.DateSpan.Day()
435 navigation_fn(lambda tp: tp.update(start, end))
436
437
438 -def fit_week_fn(main_frame, current_period, navigation_fn):
446
447
449
450 - def label(self, time, major=False):
451 if major:
452
453 start_year = self._century_start_year(time.Year)
454 next_start_year = start_year + 100
455 return str(next_start_year / 100) + " century"
456 return ""
457
459 return wx.DateTimeFromDMY(1, 0, max(self._century_start_year(time.Year), MIN_YEAR))
460
462 return time + wx.DateSpan.Years(100)
463
466
468 return (int(year) / 100) * 100
469
470
472
473 - def label(self, time, major=False):
474
475 return str(self._decade_start_year(time.Year)) + "s"
476
478 return wx.DateTimeFromDMY(1, 0, self._decade_start_year(time.Year))
479
481 return time + wx.DateSpan.Year() * 10
482
484 return (int(year) / 10) * 10
485
488
489
491
492 - def label(self, time, major=False):
493 return str(wx.DateTime.ConvertYearToBC(time.Year))
494
496 return wx.DateTimeFromDMY(1, 0, time.Year)
497
499 return time + wx.DateSpan.Year()
500
503
504
506
507 - def label(self, time, major=False):
508 if major:
509 return "%s %s" % (time.GetMonthName(time.Month, time.Name_Abbr), time.Year)
510 return time.GetMonthName(time.Month, wx.DateTime.Name_Abbr)
511
513 return wx.DateTimeFromDMY(1, time.Month, time.Year)
514
516 if time.Month < 11:
517 return wx.DateTimeFromDMY(1, time.Month + 1, time.Year, 0, 0)
518 else:
519 return wx.DateTimeFromDMY(1, 0, time.Year + 1, 0, 0)
520
523
524
526
527 - def label(self, time, major=False):
528 if major:
529 month_name = time.GetMonthName(time.Month, time.Name_Abbr)
530 return "%s %s %s" % (time.Day, month_name, time.Year)
531 return str(time.Day)
532
534 return wx.DateTimeFromDMY(time.Day, time.Month, time.Year)
535
537 return time + wx.DateSpan.Day()
538
540 saturday_or_sunday = (0,6)
541 bold = False
542 if (time_period.start_time.GetWeekDay() in saturday_or_sunday):
543 bold = True
544 return get_default_font(8, bold)
545
546
548
552
553 - def label(self, time, major=False):
554 if major:
555
556 first_weekday = self.start(time)
557 next_first_weekday = self.increment(first_weekday)
558 last_weekday = next_first_weekday - wx.DateSpan.Day()
559 range_string = self._time_range_string(first_weekday, last_weekday)
560 if self.config.week_start == "monday":
561 return (_("Week") + " %s (%s)") % (time.GetWeekOfYear(), range_string)
562 else:
563
564 return range_string
565
566 return ""
567
569 stripped_date = wx.DateTimeFromDMY(time.Day, time.Month, time.Year)
570 if self.config.week_start == "monday":
571 days_to_subtract = stripped_date.GetWeekDay() - 1
572 else:
573 days_to_subtract = stripped_date.GetWeekDay() % 7
574 return stripped_date - wx.DateSpan.Days(days_to_subtract)
575
577 return time + wx.DateSpan.Week()
578
581
583 """
584 Examples:
585
586 * 1-7 Jun 2009
587 * 28 Jun-3 Jul 2009
588 * 28 Jun 08-3 Jul 2009
589 """
590 if time1.Year == time2.Year:
591 if time1.Month == time2.Month:
592 return "%s-%s %s %s" % (time1.Day, time2.Day,
593 time1.GetMonthName(time1.Month, time1.Name_Abbr),
594 time1.Year)
595 return "%s %s-%s %s %s" % (time1.Day,
596 time1.GetMonthName(time1.Month, time1.Name_Abbr),
597 time2.Day,
598 time2.GetMonthName(time2.Month, time2.Name_Abbr),
599 time1.Year)
600 return "%s %s %s-%s %s %s" % (time1.Day,
601 time1.GetMonthName(time1.Month, time1.Name_Abbr),
602 time1.Year,
603 time2.Day,
604 time2.GetMonthName(time2.Month, time2.Name_Abbr),
605 time2.Year)
606
607
609
610 - def label(self, time, major=False):
611 if major:
612
613 return ""
614 return time.GetWeekDayName(time.GetWeekDay(), wx.DateTime.Name_Abbr)
615
617 return wx.DateTimeFromDMY(time.Day, time.Month, time.Year)
618
620 return time + wx.DateSpan.Day()
621
624
625
627
628 - def label(self, time, major=False):
629 if major:
630
631 return ""
632 return str(time.Hour)
633
635 start_time = wx.DateTimeFromDMY(time.Day, time.Month, time.Year, time.Hour)
636 return start_time
637
639 new_time = time + wx.TimeSpan.Hour()
640 return new_time
641
644
645
647
648
649
650
651 counter = 0
652 milliseconds = microsecs / 1000
653 while abs(milliseconds) > sys.maxint:
654 milliseconds = milliseconds / 2
655 counter += 1
656 delta = wx.TimeSpan.Milliseconds(milliseconds)
657 while counter > 0:
658 delta = delta * 2;
659 counter -= 1
660 return delta
661
662
664 """Return the number of microseconds that the delta represents."""
665 days = delta.GetDays()
666 hours = delta.GetHours()
667 minutes = delta.GetMinutes()
668 seconds = delta.GetSeconds()
669 milliseconds = delta.GetMilliseconds()
670 neg = False
671 if days < 0:
672 neg = True
673 days = -days
674 hours = -hours
675 minutes = -minutes
676 seconds = -seconds
677 milliseconds = - milliseconds
678 microsecs = days * US_PER_DAY
679 if hours >= 0:
680 microsecs = hours * US_PER_HOUR
681 if minutes >= 0:
682 microsecs = minutes * US_PER_MINUTE
683 if seconds >= 0:
684 microsecs = seconds * US_PER_SEC
685 if milliseconds >= 0:
686 microsecs = milliseconds * 1000
687 if neg:
688 microsecs = -microsecs
689 return microsecs
690
691
693 return _move_period_time_span(period, wx.TimeSpan.Days(num))
694
695
697 return _move_period_time_span(period, wx.TimeSpan.Days(7*num))
698
699
701 new_start_time = period.start_time + wx.DateSpan.Months(num)
702 if new_start_time.Day != period.start_time.Day:
703 return None
704 return _move_period_time_span(period, new_start_time-period.start_time)
705
706
708 new_start_time = period.start_time + wx.DateSpan.Years(num)
709 if (new_start_time.Month != period.start_time.Month or
710 new_start_time.Day != period.start_time.Day):
711 return None
712 return _move_period_time_span(period, new_start_time-period.start_time)
713
714
716 try:
717 new_start = period.start_time + time_span
718 new_end = period.end_time + time_span
719 return TimePeriod(period.time_type, new_start, new_end)
720 except ValueError:
721 return None
722
723
725 def fail_with_invalid_date():
726 raise ValueError("Invalid date")
727 try:
728 datetime = wx.DateTimeFromDMY(day, month, year, hour, minute, second)
729 except AssertionError:
730 fail_with_invalid_date()
731 else:
732 if not datetime.IsValid():
733 fail_with_invalid_date()
734 return datetime
735