123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241 |
- //
- // DateField.cs: text entry for date
- //
- // Author: Barry Nolte
- //
- // Licensed under the MIT license
- //
- using System;
- using System.Globalization;
- using System.Linq;
- using NStack;
- namespace Terminal.Gui {
- /// <summary>
- /// Date edit widget
- /// </summary>
- /// <remarks>
- /// This widget provides date editing functionality, and mouse support.
- /// </remarks>
- public class DateField : TextField {
- bool isShort;
- int longFieldLen = 10;
- int shortFieldLen = 8;
- int FieldLen { get { return isShort ? shortFieldLen : longFieldLen; } }
- string sepChar;
- string longFormat;
- string shortFormat;
- string Format { get { return isShort ? shortFormat : longFormat; } }
- /// <summary>
- /// Public constructor that creates a date edit field at an absolute position and fixed size.
- /// </summary>
- /// <param name="x">The x coordinate.</param>
- /// <param name="y">The y coordinate.</param>
- /// <param name="date">Initial date contents.</param>
- /// <param name="isShort">If true, shows only two digits for the year.</param>
- public DateField(int x, int y, DateTime date, bool isShort = false) : base(x, y, isShort ? 10 : 12, "")
- {
- CultureInfo cultureInfo = CultureInfo.CurrentCulture;
- sepChar = cultureInfo.DateTimeFormat.DateSeparator;
- longFormat = $" {cultureInfo.DateTimeFormat.ShortDatePattern}";
- shortFormat = GetShortFormat(longFormat);
- this.isShort = isShort;
- CursorPosition = 1;
- Date = date;
- Changed += DateField_Changed;
- }
- void DateField_Changed(object sender, ustring e)
- {
- if (!DateTime.TryParseExact(Text.ToString(), Format, CultureInfo.CurrentCulture, DateTimeStyles.None, out DateTime result))
- Text = e;
- }
- string GetShortFormat(string lf)
- {
- return lf.Replace("yyyy", "yy");
- }
- /// <summary>
- /// Gets or sets the date in the widget.
- /// </summary>
- /// <remarks>
- /// </remarks>
- public DateTime Date {
- get {
- if (!DateTime.TryParseExact(Text.ToString(), Format, CultureInfo.CurrentCulture, DateTimeStyles.None, out DateTime result)) return new DateTime();
- return result;
- }
- set {
- this.Text = value.ToString(Format);
- }
- }
- bool SetText(Rune key)
- {
- var text = TextModel.ToRunes(Text);
- var newText = text.GetRange(0, CursorPosition);
- newText.Add(key);
- if (CursorPosition < FieldLen)
- newText = newText.Concat(text.GetRange(CursorPosition + 1, text.Count - (CursorPosition + 1))).ToList();
- return SetText(ustring.Make(newText));
- }
- bool SetText(ustring text)
- {
- ustring[] vals = text.Split(ustring.Make(sepChar));
- ustring[] frm = ustring.Make(Format).Split(ustring.Make(sepChar));
- bool isValidDate = true;
- int idx = GetFormatIndex(frm, "y");
- int year = Int32.Parse(vals[idx].ToString());
- int month;
- int day;
- idx = GetFormatIndex(frm, "M");
- if (Int32.Parse(vals[idx].ToString()) < 1) {
- isValidDate = false;
- month = 1;
- vals[idx] = "1";
- } else if (Int32.Parse(vals[idx].ToString()) > 12) {
- isValidDate = false;
- month = 12;
- vals[idx] = "12";
- } else
- month = Int32.Parse(vals[idx].ToString());
- idx = GetFormatIndex(frm, "d");
- if (Int32.Parse(vals[idx].ToString()) < 1) {
- isValidDate = false;
- day = 1;
- vals[idx] = "1";
- } else if (Int32.Parse(vals[idx].ToString()) > 31) {
- isValidDate = false;
- day = DateTime.DaysInMonth(year, month);
- vals[idx] = day.ToString();
- } else
- day = Int32.Parse(vals[idx].ToString());
- string date = GetData(month, day, year, frm);
- Text = date;
- if (!DateTime.TryParseExact(date, Format, CultureInfo.CurrentCulture, DateTimeStyles.None, out DateTime result) ||
- !isValidDate)
- return false;
- return true;
- }
- string GetData(int month, int day, int year, ustring[] fm)
- {
- string data = " ";
- for (int i = 0; i < fm.Length; i++) {
- if (fm[i].Contains("M"))
- data += $"{month,2:00}";
- else if (fm[i].Contains("d"))
- data += $"{day,2:00}";
- else
- data += isShort ? $"{year,2:00}" : $"{year,4:0000}";
- if (i < 2)
- data += $"{sepChar}";
- }
- return data;
- }
- int GetFormatIndex(ustring[] fm, string t)
- {
- int idx = -1;
- for (int i = 0; i < fm.Length; i++) {
- if (fm[i].Contains(t)) {
- idx = i;
- break;
- }
- }
- return idx;
- }
- void IncCursorPosition()
- {
- if (CursorPosition == FieldLen)
- return;
- if (Text[++CursorPosition] == sepChar.ToCharArray()[0])
- CursorPosition++;
- }
- void DecCursorPosition()
- {
- if (CursorPosition == 1)
- return;
- if (Text[--CursorPosition] == sepChar.ToCharArray()[0])
- CursorPosition--;
- }
- void AdjCursorPosition()
- {
- if (Text[CursorPosition] == sepChar.ToCharArray()[0])
- CursorPosition++;
- }
- public override bool ProcessKey(KeyEvent kb)
- {
- switch (kb.Key) {
- case Key.DeleteChar:
- case Key.ControlD:
- SetText('0');
- break;
- case Key.Delete:
- case Key.Backspace:
- SetText('0');
- DecCursorPosition();
- break;
- // Home, C-A
- case Key.Home:
- case Key.ControlA:
- CursorPosition = 1;
- break;
- case Key.CursorLeft:
- case Key.ControlB:
- DecCursorPosition();
- break;
- case Key.End:
- case Key.ControlE: // End
- CursorPosition = FieldLen;
- break;
- case Key.CursorRight:
- case Key.ControlF:
- IncCursorPosition();
- break;
- default:
- // Ignore non-numeric characters.
- if (kb.Key < (Key)((int)'0') || kb.Key > (Key)((int)'9'))
- return false;
- if (SetText(TextModel.ToRunes(ustring.Make((uint)kb.Key)).First()))
- IncCursorPosition();
- return true;
- }
- return true;
- }
- public override bool MouseEvent(MouseEvent ev)
- {
- if (!ev.Flags.HasFlag(MouseFlags.Button1Clicked))
- return false;
- if (!HasFocus)
- SuperView.SetFocus(this);
- var point = ev.X;
- if (point > FieldLen)
- point = FieldLen;
- if (point < 1)
- point = 1;
- CursorPosition = point;
- AdjCursorPosition();
- return true;
- }
- }
- }
|