rat_widget/calendar/
month.rs

1use crate::_private::NonExhaustive;
2use crate::calendar::event::CalOutcome;
3use crate::calendar::selection::{NoSelection, RangeSelection, SingleSelection};
4use crate::calendar::style::CalendarStyle;
5use crate::calendar::{CalendarSelection, first_day_of_month, last_day_of_month};
6use crate::text::HasScreenCursor;
7use crate::util::{block_size, revert_style};
8use chrono::{Datelike, Days, NaiveDate, Weekday};
9use rat_event::util::MouseFlagsN;
10use rat_focus::{FocusBuilder, FocusFlag, HasFocus};
11use rat_reloc::RelocatableState;
12use ratatui::buffer::Buffer;
13use ratatui::layout::{Alignment, Rect};
14use ratatui::style::Style;
15use ratatui::style::Stylize;
16use ratatui::text::Span;
17use ratatui::widgets::block::Title;
18use ratatui::widgets::{Block, StatefulWidget, Widget};
19use std::cell::RefCell;
20use std::cmp::max;
21use std::collections::HashMap;
22use std::marker::PhantomData;
23use std::rc::Rc;
24
25/// Month widget.
26///
27/// Renders one month of a calendar.
28///
29/// There is movement and selection support within the month,
30/// but no scrolling. Use a Calendar for full calendar features.
31///
32/// ```rust ignore
33/// Month::new()
34///     .locale(Locale::de_AT_euro)
35///     .styles(THEME.month_style())
36///     .title_align(Alignment::Left)
37///     .day_styles(&date_styles)
38///     .show_weekdays()
39///     .block(Block::bordered().borders(Borders::TOP))
40///     .render(l2[2], frame.buffer_mut(), &mut state.month1);
41/// ```
42///
43/// ```rust ignore
44/// match state.month1.handle(event, Regular) {
45///     CalOutcome::Selected => {
46///         // doit
47///         Outcome::Changed
48///     }
49///     r => r.into()
50/// }
51/// ```
52///
53#[derive(Debug, Clone)]
54pub struct Month<'a, Selection> {
55    /// Start date of the month.
56    start_date: Option<NaiveDate>,
57
58    /// Base style.
59    style: Style,
60    /// Title style.
61    title_style: Option<Style>,
62    /// Title align.
63    title_align: Alignment,
64    /// Week number style.
65    weeknum_style: Option<Style>,
66    /// Week day style.
67    weekday_style: Option<Style>,
68    /// Default day style.
69    day_style: Option<Style>,
70    /// Styling for a single date.
71    day_styles: Option<&'a HashMap<NaiveDate, Style>>,
72    /// Selection
73    select_style: Option<Style>,
74    /// Focus
75    focus_style: Option<Style>,
76
77    /// Show year in title.
78    hide_year: bool,
79    /// Show month name
80    hide_month: bool,
81    /// Show Weekdays above
82    hide_weekdays: bool,
83
84    /// Block
85    block: Option<Block<'a>>,
86
87    /// Locale
88    loc: chrono::Locale,
89
90    phantom: PhantomData<Selection>,
91}
92
93/// State & event-handling.
94#[derive(Debug)]
95pub struct MonthState<Selection = SingleSelection> {
96    /// Total area.
97    /// __readonly__. renewed for each render.
98    pub area: Rect,
99    /// Area inside the border.
100    /// __readonly__. renewed for each render.
101    pub inner: Rect,
102    /// Area of the main calendar.
103    /// __readonly__. renewed for each render.
104    pub area_cal: Rect,
105    /// Area for the days of the month.
106    /// __readonly__. renewed for each render.
107    pub area_days: [Rect; 31],
108    /// Area for all the week numbers.
109    /// __readonly__. renewed for each render.
110    pub area_weeknum: Rect,
111    /// Area for the week numbers.
112    /// __readonly__. renewed for each render.
113    pub area_weeks: [Rect; 6],
114
115    /// Startdate
116    start_date: NaiveDate,
117
118    /// Selection model.
119    /// The selection model can be shared with other Month widgets.
120    pub selection: Rc<RefCell<Selection>>,
121
122    /// Set to the container-focus if part of a container.
123    /// __read+write__
124    pub container: Option<FocusFlag>,
125
126    /// Focus
127    /// __read+write__
128    pub focus: FocusFlag,
129    /// Mouse flags
130    /// __read+write__
131    pub mouse: MouseFlagsN,
132
133    pub non_exhaustive: NonExhaustive,
134}
135
136impl<Selection> Default for Month<'_, Selection> {
137    fn default() -> Self {
138        Self {
139            start_date: None,
140            style: Default::default(),
141            title_style: Default::default(),
142            title_align: Default::default(),
143            weeknum_style: Default::default(),
144            weekday_style: Default::default(),
145            day_style: Default::default(),
146            day_styles: Default::default(),
147            select_style: Default::default(),
148            focus_style: Default::default(),
149            hide_year: Default::default(),
150            hide_month: Default::default(),
151            hide_weekdays: Default::default(),
152            block: Default::default(),
153            loc: Default::default(),
154            phantom: PhantomData,
155        }
156    }
157}
158
159impl<'a, Selection> Month<'a, Selection> {
160    /// New calendar.
161    pub fn new() -> Self {
162        Self::default()
163    }
164
165    /// Sets the starting date. This can be any date of the month.
166    /// If no date is set, the start_date of the state is used.
167    #[inline]
168    pub fn date(mut self, s: NaiveDate) -> Self {
169        self.start_date = Some(first_day_of_month(s));
170        self
171    }
172
173    /// Locale for month-names, day-names.
174    #[inline]
175    pub fn locale(mut self, loc: chrono::Locale) -> Self {
176        self.loc = loc;
177        self
178    }
179
180    /// Show year title
181    #[inline]
182    pub fn hide_year(mut self) -> Self {
183        self.hide_year = true;
184        self
185    }
186
187    /// Show month title
188    #[inline]
189    pub fn hide_month(mut self) -> Self {
190        self.hide_month = true;
191        self
192    }
193
194    /// Show month title
195    #[inline]
196    pub fn show_year(mut self) -> Self {
197        self.hide_year = false;
198        self
199    }
200
201    /// Show month title
202    #[inline]
203    #[deprecated(since = "2.3.0", note = "typo, use show_month()")]
204    pub fn show_show_month(mut self) -> Self {
205        self.hide_month = false;
206        self
207    }
208
209    /// Show month title
210    #[inline]
211    pub fn show_month(mut self) -> Self {
212        self.hide_month = false;
213        self
214    }
215
216    /// Show weekday titles
217    #[inline]
218    pub fn hide_weekdays(mut self) -> Self {
219        self.hide_weekdays = true;
220        self
221    }
222
223    /// Show weekday titles
224    #[inline]
225    pub fn show_weekdays(mut self) -> Self {
226        self.hide_weekdays = false;
227        self
228    }
229
230    /// Set the composite style.
231    #[inline]
232    pub fn styles(mut self, s: CalendarStyle) -> Self {
233        self.style = s.style;
234        if s.title.is_some() {
235            self.title_style = s.title;
236        }
237        if s.weeknum.is_some() {
238            self.weeknum_style = s.weeknum;
239        }
240        if s.weekday.is_some() {
241            self.weekday_style = s.weekday;
242        }
243        if s.day.is_some() {
244            self.day_style = s.day;
245        }
246        if s.select.is_some() {
247            self.select_style = s.select;
248        }
249        if s.focus.is_some() {
250            self.focus_style = s.focus;
251        }
252        if s.block.is_some() {
253            self.block = s.block;
254        }
255        self.block = self.block.map(|v| v.style(self.style));
256        self
257    }
258
259    /// Base style.
260    pub fn style(mut self, style: Style) -> Self {
261        self.style = style;
262        self
263    }
264
265    /// Style for the selection
266    pub fn select_style(mut self, style: Style) -> Self {
267        self.select_style = Some(style);
268        self
269    }
270
271    /// Style for the focus.
272    pub fn focus_style(mut self, style: Style) -> Self {
273        self.focus_style = Some(style);
274        self
275    }
276
277    /// Sets the default day-style.
278    #[inline]
279    pub fn day_style(mut self, s: impl Into<Style>) -> Self {
280        self.day_style = Some(s.into());
281        self
282    }
283
284    /// Set a map date->Style for highlighting some dates.
285    #[inline]
286    pub fn day_styles(mut self, styles: &'a HashMap<NaiveDate, Style>) -> Self {
287        self.day_styles = Some(styles);
288        self
289    }
290
291    /// Set the week number style.
292    #[inline]
293    pub fn week_style(mut self, s: impl Into<Style>) -> Self {
294        self.weeknum_style = Some(s.into());
295        self
296    }
297
298    /// Set the week day style.
299    #[inline]
300    pub fn weekday_style(mut self, s: impl Into<Style>) -> Self {
301        self.weekday_style = Some(s.into());
302        self
303    }
304
305    /// Set the month-name style.
306    #[inline]
307    pub fn title_style(mut self, s: impl Into<Style>) -> Self {
308        self.title_style = Some(s.into());
309        self
310    }
311
312    /// Set the month-name align.
313    #[inline]
314    pub fn title_align(mut self, a: Alignment) -> Self {
315        self.title_align = a;
316        self
317    }
318
319    /// Block
320    #[inline]
321    pub fn block(mut self, b: Block<'a>) -> Self {
322        self.block = Some(b);
323        self.block = self.block.map(|v| v.style(self.style));
324        self
325    }
326
327    /// Inherent width of the widget.
328    #[inline]
329    pub fn width(&self) -> u16 {
330        8 * 3 + block_size(&self.block).width
331    }
332
333    /// Inherent height for the widget.
334    /// This is not a fixed value, depends on the month.
335    #[inline]
336    pub fn height(&self, state: &MonthState<Selection>) -> u16 {
337        let start_date = if let Some(start_date) = self.start_date {
338            start_date
339        } else {
340            state.start_date
341        };
342
343        let r = MonthState::<Selection>::count_weeks(start_date) as u16;
344        let w = if !self.hide_weekdays { 1 } else { 0 };
345        let b = max(1, block_size(&self.block).height);
346        r + w + b
347    }
348}
349
350impl<'a, Selection> StatefulWidget for &Month<'a, Selection>
351where
352    Selection: CalendarSelection,
353{
354    type State = MonthState<Selection>;
355
356    fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
357        render_ref(self, area, buf, state);
358    }
359}
360
361impl<Selection> StatefulWidget for Month<'_, Selection>
362where
363    Selection: CalendarSelection,
364{
365    type State = MonthState<Selection>;
366
367    fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
368        render_ref(&self, area, buf, state);
369    }
370}
371
372fn render_ref<Selection: CalendarSelection>(
373    widget: &Month<'_, Selection>,
374    area: Rect,
375    buf: &mut Buffer,
376    state: &mut MonthState<Selection>,
377) {
378    state.area = area;
379    if let Some(start_date) = widget.start_date {
380        state.start_date = start_date;
381    }
382
383    let mut day = state.start_date;
384
385    let focus_style = widget.focus_style.unwrap_or(revert_style(widget.style));
386    let select_style = if let Some(select_style) = widget.select_style {
387        if state.is_container_focused() || state.is_focused() {
388            focus_style
389        } else {
390            select_style
391        }
392    } else {
393        if state.is_container_focused() || state.is_focused() {
394            focus_style
395        } else {
396            revert_style(widget.style)
397        }
398    };
399    let day_style = widget.day_style.unwrap_or(widget.style);
400    let week_style = widget.weeknum_style.unwrap_or(widget.style);
401    let weekday_style = widget.weekday_style.unwrap_or(widget.style);
402
403    let title_style = if let Some(title_style) = widget.title_style {
404        title_style
405    } else {
406        widget.style
407    };
408    let title_style = if state.is_focused() {
409        title_style.patch(focus_style)
410    } else {
411        title_style
412    };
413
414    let mut block = if let Some(block) = widget.block.clone() {
415        block
416    } else {
417        Block::new().style(widget.style)
418    };
419    let title_format = if !widget.hide_month {
420        if !widget.hide_year { "%B %Y" } else { "%B" }
421    } else {
422        ""
423    };
424    if !title_format.is_empty() {
425        block = block
426            .title(Title::from(
427                day.format_localized(title_format, widget.loc).to_string(),
428            ))
429            .title_style(title_style)
430            .title_alignment(widget.title_align)
431    }
432    state.inner = block.inner(area);
433    block.render(area, buf);
434
435    let month = day.month();
436    let mut w = 0;
437    let mut x = state.inner.x;
438    let mut y = state.inner.y;
439
440    // week days
441    if !widget.hide_weekdays {
442        let mut week_0 = day.week(Weekday::Mon).first_day();
443
444        x += 3;
445        buf.set_style(Rect::new(x, y, 3 * 7, 1), weekday_style);
446        for _ in 0..7 {
447            let area = Rect::new(x, y, 2, 1).intersection(state.inner);
448
449            let day_name = week_0.format_localized("%a", widget.loc).to_string();
450            Span::from(format!("{:2} ", day_name)).render(area, buf);
451
452            x += 3;
453            week_0 = week_0 + Days::new(1);
454        }
455        x = state.inner.x;
456        y += 1;
457    }
458
459    // reset areas
460    for i in 0..31 {
461        state.area_days[i] = Rect::default();
462    }
463    for i in 0..6 {
464        state.area_weeks[i] = Rect::default();
465    }
466    state.area_cal = Rect::new(x + 3, y, 7 * 3, state.week_len() as u16);
467    state.area_weeknum = Rect::new(x, y, 3, state.week_len() as u16);
468
469    // first line may omit a few days
470    state.area_weeks[w] = Rect::new(x, y, 2, 1).intersection(state.inner);
471    Span::from(day.format_localized("%V", widget.loc).to_string())
472        .style(week_style)
473        .render(state.area_weeks[w], buf);
474
475    x += 3;
476
477    for wd in [
478        Weekday::Mon,
479        Weekday::Tue,
480        Weekday::Wed,
481        Weekday::Thu,
482        Weekday::Fri,
483        Weekday::Sat,
484        Weekday::Sun,
485    ] {
486        if day.weekday() != wd {
487            x += 3;
488        } else {
489            let day_style = calc_day_style(widget, state, day, day_style, select_style);
490            state.area_days[day.day0() as usize] = Rect::new(x, y, 2, 1).intersection(state.inner);
491
492            Span::from(day.format_localized("%e", widget.loc).to_string())
493                .style(day_style)
494                .render(state.area_days[day.day0() as usize], buf);
495
496            if wd != Weekday::Sun && state.selection.is_selected(day + Days::new(1)) {
497                let mut gap_area = state.area_days[day.day0() as usize];
498                gap_area.x += 2;
499                gap_area.width = 1;
500                Span::from(" ").style(day_style).render(gap_area, buf);
501            }
502
503            x += 3;
504            day = day + Days::new(1);
505        }
506    }
507
508    w += 1;
509    x = state.inner.x;
510    y += 1;
511
512    while month == day.month() {
513        state.area_weeks[w] = Rect::new(x, y, 2, 1).intersection(state.inner);
514        Span::from(day.format_localized("%V", widget.loc).to_string())
515            .style(week_style)
516            .render(state.area_weeks[w], buf);
517
518        x += 3;
519
520        for wd in [
521            Weekday::Mon,
522            Weekday::Tue,
523            Weekday::Wed,
524            Weekday::Thu,
525            Weekday::Fri,
526            Weekday::Sat,
527            Weekday::Sun,
528        ] {
529            if day.month() == month {
530                let day_style = calc_day_style(widget, state, day, day_style, select_style);
531
532                state.area_days[day.day0() as usize] =
533                    Rect::new(x, y, 2, 1).intersection(state.inner);
534
535                Span::from(day.format_localized("%e", widget.loc).to_string())
536                    .style(day_style)
537                    .render(state.area_days[day.day0() as usize], buf);
538
539                if wd != Weekday::Sun && state.selection.is_selected(day + Days::new(1)) {
540                    let mut gap_area = state.area_days[day.day0() as usize];
541                    gap_area.x += 2;
542                    gap_area.width = 1;
543                    Span::from(" ").style(day_style).render(gap_area, buf);
544                }
545
546                x += 3;
547                day = day + Days::new(1);
548            } else {
549                x += 3;
550            }
551        }
552
553        w += 1;
554        x = state.inner.x;
555        y += 1;
556    }
557}
558
559fn calc_day_style<Selection: CalendarSelection>(
560    widget: &Month<'_, Selection>,
561    state: &mut MonthState<Selection>,
562    day: NaiveDate,
563    day_style: Style,
564    select_style: Style,
565) -> Style {
566    let day_style = if let Some(day_styles) = widget.day_styles {
567        if let Some(day_style) = day_styles.get(&day) {
568            *day_style
569        } else {
570            day_style
571        }
572    } else {
573        day_style
574    };
575
576    if (state.is_container_focused() || state.is_focused())
577        && state.selection.lead_selection() == Some(day)
578    {
579        day_style.patch(select_style.underlined())
580    } else if state.selection.is_selected(day) {
581        day_style.patch(select_style)
582    } else {
583        day_style
584    }
585}
586
587impl<Selection> HasFocus for MonthState<Selection> {
588    fn build(&self, builder: &mut FocusBuilder) {
589        builder.leaf_widget(self);
590    }
591
592    #[inline]
593    fn focus(&self) -> FocusFlag {
594        self.focus.clone()
595    }
596
597    #[inline]
598    fn area(&self) -> Rect {
599        self.area
600    }
601}
602
603impl<Selection> HasScreenCursor for MonthState<Selection> {
604    fn screen_cursor(&self) -> Option<(u16, u16)> {
605        None
606    }
607}
608
609impl<Selection> RelocatableState for MonthState<Selection> {
610    fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
611        self.area.relocate(shift, clip);
612        self.inner.relocate(shift, clip);
613        self.area_cal.relocate(shift, clip);
614        self.area_weeknum.relocate(shift, clip);
615        self.area_days.relocate(shift, clip);
616        self.area_weeks.relocate(shift, clip);
617    }
618}
619
620impl<Selection> Clone for MonthState<Selection>
621where
622    Selection: Clone,
623{
624    fn clone(&self) -> Self {
625        Self {
626            area: self.area,
627            inner: self.inner,
628            area_cal: self.area_cal.clone(),
629            area_days: self.area_days.clone(),
630            area_weeknum: self.area_weeknum.clone(),
631            area_weeks: self.area_weeks.clone(),
632            start_date: self.start_date,
633            selection: self.selection.clone(),
634            container: self.container.clone(),
635            focus: self.focus.new_instance(),
636            mouse: Default::default(),
637            non_exhaustive: NonExhaustive,
638        }
639    }
640}
641
642impl<Selection> Default for MonthState<Selection>
643where
644    Selection: Default,
645{
646    #[allow(deprecated)]
647    fn default() -> Self {
648        Self {
649            area: Default::default(),
650            inner: Default::default(),
651            area_cal: Default::default(),
652            area_days: Default::default(),
653            area_weeknum: Default::default(),
654            area_weeks: Default::default(),
655            start_date: Default::default(),
656            selection: Default::default(),
657            container: Default::default(),
658            focus: Default::default(),
659            mouse: Default::default(),
660            non_exhaustive: NonExhaustive,
661        }
662    }
663}
664
665impl<Selection> MonthState<Selection> {
666    pub fn new() -> Self
667    where
668        Selection: Default,
669    {
670        Self::default()
671    }
672
673    pub fn named(name: &str) -> Self
674    where
675        Selection: Default,
676    {
677        let mut z = Self::default();
678        z.focus = z.focus.with_name(name);
679        z
680    }
681
682    /// Sets the start-date of the calendar. You can set every date, it
683    /// will always be changed to the first of the month.
684    ///
685    /// Setting this will be useless if the date is set with the Month widget.
686    pub fn set_start_date(&mut self, date: NaiveDate) -> bool {
687        let old_value = self.start_date;
688        self.start_date = first_day_of_month(date);
689        old_value != self.start_date
690    }
691
692    /// Start date of this month. Will always be the first.
693    pub fn start_date(&self) -> NaiveDate {
694        self.start_date
695    }
696
697    /// End date of this month.
698    pub fn end_date(&self) -> NaiveDate {
699        last_day_of_month(self.start_date)
700    }
701
702    fn in_range(&self, date: NaiveDate) -> bool {
703        date >= self.start_date() && date <= self.end_date()
704    }
705
706    /// Nr of weeks in this month.
707    pub fn week_len(&self) -> usize {
708        Self::count_weeks(self.start_date)
709    }
710
711    /// Nr of weeks for the given month
712    pub fn count_weeks(day: NaiveDate) -> usize {
713        let mut day = day.with_day0(0).expect("date");
714        let month = day.month();
715
716        let mut weeks = 1;
717        for weekday in [
718            Weekday::Mon,
719            Weekday::Tue,
720            Weekday::Wed,
721            Weekday::Thu,
722            Weekday::Fri,
723            Weekday::Sat,
724            Weekday::Sun,
725        ] {
726            // run through first week
727            if day.weekday() == weekday {
728                day = day + Days::new(1);
729            }
730        }
731        // count mondays
732        while month == day.month() {
733            weeks += 1;
734            day = day + Days::new(7);
735        }
736
737        weeks
738    }
739
740    // is there a container for this month?
741    fn is_container_focused(&self) -> bool {
742        self.container
743            .as_ref()
744            .map(|v| v.is_focused())
745            .unwrap_or(false)
746    }
747
748    /// Returns None.
749    pub fn screen_cursor(&self) -> Option<(u16, u16)> {
750        None
751    }
752}
753
754impl<Selection> MonthState<Selection>
755where
756    Selection: CalendarSelection,
757{
758    /// Lead selection
759    pub fn lead_selection(&self) -> Option<NaiveDate> {
760        self.selection.lead_selection()
761    }
762}
763
764impl MonthState<NoSelection> {}
765
766impl MonthState<SingleSelection> {
767    /// Removes all selection.
768    pub fn clear_selection(&mut self) {
769        self.selection.borrow_mut().clear();
770    }
771
772    /// Select a day by index.
773    pub fn select_day(&mut self, n: usize) -> CalOutcome {
774        if let Some(date) = self.start_date.with_day0(n as u32) {
775            if self.selection.borrow_mut().select(date) {
776                CalOutcome::Selected
777            } else {
778                CalOutcome::Continue
779            }
780        } else {
781            CalOutcome::Continue
782        }
783    }
784
785    /// Select the last day of the month.
786    pub fn select_last(&mut self) -> CalOutcome {
787        let date = self.end_date();
788        if self.selection.borrow_mut().select(date) {
789            CalOutcome::Selected
790        } else {
791            CalOutcome::Continue
792        }
793    }
794
795    /// Select by date.
796    /// Returns true if the date is valid for this month.
797    /// If false it doesn't change the selection.
798    pub fn select_date(&mut self, d: NaiveDate) -> bool {
799        let start = self.start_date;
800        if d.year() == start.year() && d.month() == start.month() {
801            self.selection.borrow_mut().select(d)
802        } else {
803            false
804        }
805    }
806
807    /// Lead selected day
808    pub fn selected_date(&self) -> Option<NaiveDate> {
809        self.selection.lead_selection()
810    }
811
812    /// Select previous day.
813    pub fn prev_day(&mut self, n: usize) -> CalOutcome {
814        let base_start = self.start_date();
815        let base_end = self.end_date();
816
817        let date = if let Some(date) = self.selection.lead_selection() {
818            if date >= base_start && date <= base_end {
819                date - Days::new(n as u64)
820            } else if date < base_start {
821                self.start_date()
822            } else {
823                self.end_date()
824            }
825        } else {
826            self.end_date()
827        };
828
829        if self.in_range(date) {
830            if self.selection.borrow_mut().select(date) {
831                CalOutcome::Selected
832            } else {
833                CalOutcome::Continue
834            }
835        } else {
836            CalOutcome::Continue
837        }
838    }
839
840    /// Select next day.
841    pub fn next_day(&mut self, n: usize) -> CalOutcome {
842        let base_start = self.start_date();
843        let base_end = self.end_date();
844
845        let date = if let Some(date) = self.selection.lead_selection() {
846            if date >= base_start && date <= base_end {
847                date + Days::new(n as u64)
848            } else if date < base_start {
849                self.start_date()
850            } else {
851                self.end_date()
852            }
853        } else {
854            self.start_date()
855        };
856
857        if self.in_range(date) {
858            if self.selection.borrow_mut().select(date) {
859                CalOutcome::Selected
860            } else {
861                CalOutcome::Continue
862            }
863        } else {
864            CalOutcome::Continue
865        }
866    }
867}
868
869impl MonthState<RangeSelection> {
870    /// Removes all selection.
871    pub fn clear_selection(&mut self) {
872        self.selection.borrow_mut().clear();
873    }
874
875    /// Select a week by index.
876    pub fn select_week(&mut self, n: usize, extend: bool) -> CalOutcome {
877        if n < self.week_len() {
878            let date = self.start_date() + Days::new(7 * n as u64);
879            if self.selection.borrow_mut().select_week(date, extend) {
880                CalOutcome::Selected
881            } else {
882                CalOutcome::Continue
883            }
884        } else {
885            CalOutcome::Continue
886        }
887    }
888
889    /// Select a day by index.
890    pub fn select_day(&mut self, n: usize, extend: bool) -> CalOutcome {
891        if let Some(date) = self.start_date.with_day0(n as u32) {
892            if self.selection.borrow_mut().select_day(date, extend) {
893                CalOutcome::Selected
894            } else {
895                CalOutcome::Continue
896            }
897        } else {
898            CalOutcome::Continue
899        }
900    }
901
902    /// Select the last day
903    pub fn select_last(&mut self, extend: bool) -> CalOutcome {
904        let date = self.end_date();
905        if self.selection.borrow_mut().select_day(date, extend) {
906            CalOutcome::Selected
907        } else {
908            CalOutcome::Continue
909        }
910    }
911
912    /// Select a week by date
913    /// Returns true if the date is valid for this month.
914    /// If false it doesn't change the selection.
915    pub fn select_week_by_date(&mut self, date: NaiveDate, extend: bool) -> bool {
916        let base = self.start_date;
917
918        let start = date.week(Weekday::Mon).first_day();
919        let end = date.week(Weekday::Mon).last_day();
920
921        if (start.year() == base.year() && start.month() == base.month())
922            || (end.year() == base.year() && end.month() == base.month())
923        {
924            self.selection.borrow_mut().select_week(start, extend)
925        } else {
926            false
927        }
928    }
929
930    /// Select by date.
931    /// Returns true if the date is valid for this month.
932    /// If false it doesn't change the selection.
933    pub fn select_date(&mut self, d: NaiveDate, extend: bool) -> bool {
934        let base = self.start_date;
935        if d.year() == base.year() && d.month() == base.month() {
936            self.selection.borrow_mut().select_day(d, extend)
937        } else {
938            false
939        }
940    }
941
942    /// Lead selected day
943    pub fn selected_date(&self) -> Option<NaiveDate> {
944        self.selection.lead_selection()
945    }
946
947    /// Select previous day.
948    pub fn prev_day(&mut self, n: usize, extend: bool) -> CalOutcome {
949        let base_start = self.start_date();
950        let base_end = self.end_date();
951
952        let date = if let Some(date) = self.selection.lead_selection() {
953            if date >= base_start && date <= base_end {
954                date - Days::new(n as u64)
955            } else if date < base_start {
956                self.start_date()
957            } else {
958                self.end_date()
959            }
960        } else {
961            self.end_date()
962        };
963
964        if self.in_range(date) {
965            if self.selection.borrow_mut().select_day(date, extend) {
966                CalOutcome::Selected
967            } else {
968                CalOutcome::Continue
969            }
970        } else {
971            CalOutcome::Continue
972        }
973    }
974
975    /// Select previous day.
976    pub fn next_day(&mut self, n: usize, extend: bool) -> CalOutcome {
977        let base_start = self.start_date();
978        let base_end = self.end_date();
979
980        let date = if let Some(date) = self.selection.lead_selection() {
981            if date >= base_start && date <= base_end {
982                date + Days::new(n as u64)
983            } else if date < base_start {
984                self.start_date()
985            } else {
986                self.end_date()
987            }
988        } else {
989            self.start_date()
990        };
991
992        if self.in_range(date) {
993            if self.selection.borrow_mut().select_day(date, extend) {
994                CalOutcome::Selected
995            } else {
996                CalOutcome::Continue
997            }
998        } else {
999            CalOutcome::Continue
1000        }
1001    }
1002
1003    /// Select previous week.
1004    pub fn prev_week(&mut self, n: usize, extend: bool) -> CalOutcome {
1005        let base_start = self.start_date();
1006        let base_end = self.end_date();
1007
1008        if let Some(date) = self.selection.lead_selection() {
1009            let new_date = if date >= base_start && date <= base_end {
1010                date - Days::new(7 * n as u64)
1011            } else if date < base_start {
1012                self.start_date()
1013            } else {
1014                self.end_date()
1015            };
1016            let new_date_end = new_date.week(Weekday::Mon).last_day();
1017            if new_date_end >= base_start && new_date_end <= base_end {
1018                if self.selection.borrow_mut().select_week(new_date, extend) {
1019                    CalOutcome::Selected
1020                } else {
1021                    CalOutcome::Continue
1022                }
1023            } else {
1024                CalOutcome::Continue
1025            }
1026        } else {
1027            let new_date = self.end_date();
1028            if self.selection.borrow_mut().select_week(new_date, extend) {
1029                CalOutcome::Selected
1030            } else {
1031                CalOutcome::Continue
1032            }
1033        }
1034    }
1035
1036    /// Select previous day.
1037    pub fn next_week(&mut self, n: usize, extend: bool) -> CalOutcome {
1038        let start = self.start_date();
1039        let end = self.end_date();
1040
1041        let new_date = if let Some(date) = self.selection.lead_selection() {
1042            let date_end = date.week(Weekday::Mon).last_day();
1043            if date_end >= start && date_end <= end {
1044                date + Days::new(7 * n as u64)
1045            } else if date_end < start {
1046                self.start_date()
1047            } else {
1048                self.end_date()
1049            }
1050        } else {
1051            self.start_date()
1052        };
1053
1054        if new_date >= start && new_date <= end {
1055            if self.selection.borrow_mut().select_week(new_date, extend) {
1056                CalOutcome::Selected
1057            } else {
1058                CalOutcome::Continue
1059            }
1060        } else {
1061            CalOutcome::Continue
1062        }
1063    }
1064}