From 036e63a3dc1cbc843720b752f5c02208990bcb97 Mon Sep 17 00:00:00 2001 From: Keigh Rim Date: Tue, 8 May 2018 19:13:57 -0400 Subject: [PATCH 1/7] added bunch rules to ignore --- .gitignore | 69 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 58 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index 7ca2255d..407b5ffa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,28 +1,17 @@ -# ReSharper meta directory -_ReSharper* - # Build Folders (you can keep bin if you'd like, to store dlls and pdbs) bin obj *.pdb -*.exe *.dll # user settings *.user *.suo -# if you have pictures, windows will likely create those annoying index files -Thumbs.db - # mstest test results TestResults -# generated installer packages -*.msi -*.exe - # finalbuilder log/tmp files *.fbl7 *.fb7lck @@ -36,6 +25,64 @@ testtasks.txt # ignore installer output Installer/Output +Installer/ProgramFiles # NuGet Packages Directory packages/ + +# ides +.idea +*.iml +.vs +_ReSharper* + +# linux +*~ +.directory # KDE directory preferences +.Trash-* # Linux trash folder which might appear on any partition or disk + +# macos +.DS_Store +.AppleDouble +.LSOverride +Icon # Icon must end with two \r +._* # Thumbnails +.DocumentRevisions-V100 # Files that might appear in the root of a volume +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.AppleDB # Directories potentially created on remote AFP share +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# windows +Thumbs.db +ehthumbs.db +Desktop.ini +$RECYCLE.BIN/ +*.cab # Windows Installer files +*.msi +*.msm +*.msp +*.lnk # Windows shortcuts + +# idea +.idea +*.iml +out +gen + +# visual studio +.vs + +# sqlite +*.db +*.sqlite3 + +# ctag generated file +tags +.tags From 838f9cc2018d462504a1c253acc7d70fd04cffd6 Mon Sep 17 00:00:00 2001 From: Keigh Rim Date: Tue, 8 May 2018 20:44:25 -0400 Subject: [PATCH 2/7] fixed bug in DONE filter #349 --- Client/MainWindowViewModel.cs | 65 +++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/Client/MainWindowViewModel.cs b/Client/MainWindowViewModel.cs index 389fa12e..272de064 100644 --- a/Client/MainWindowViewModel.cs +++ b/Client/MainWindowViewModel.cs @@ -590,65 +590,70 @@ public static IEnumerable FilterList(IEnumerable tasks) { foreach ( var filter in - filters.Split(new string[] {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries)) + filters.Split(new string[] {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries)) { if (filter.Equals("due:today", StringComparison.OrdinalIgnoreCase) && task.DueDate == DateTime.Now.ToString("yyyy-MM-dd")) continue; - else if (filter.Equals("due:future", StringComparison.OrdinalIgnoreCase) - && task.DueDate.IsDateGreaterThan(DateTime.Now)) + if (filter.Equals("due:future", StringComparison.OrdinalIgnoreCase) + && task.DueDate.IsDateGreaterThan(DateTime.Now)) continue; - else if (filter.Equals("due:past", StringComparison.OrdinalIgnoreCase) - && task.DueDate.IsDateLessThan(DateTime.Now)) + if (filter.Equals("due:past", StringComparison.OrdinalIgnoreCase) + && task.DueDate.IsDateLessThan(DateTime.Now)) continue; - else if (filter.Equals("due:active", StringComparison.OrdinalIgnoreCase) - && !task.DueDate.IsNullOrEmpty() - && !task.DueDate.IsDateGreaterThan(DateTime.Now)) + if (filter.Equals("due:active", StringComparison.OrdinalIgnoreCase) + && !task.DueDate.IsNullOrEmpty() + && !task.DueDate.IsDateGreaterThan(DateTime.Now)) continue; - else if (filter.Equals("-due:today", StringComparison.OrdinalIgnoreCase) - && task.DueDate == DateTime.Now.ToString("yyyy-MM-dd")) + if (filter.Equals("-due:today", StringComparison.OrdinalIgnoreCase) + && task.DueDate == DateTime.Now.ToString("yyyy-MM-dd")) { include = false; continue; } - else if (filter.Equals("-due:future", StringComparison.OrdinalIgnoreCase) - && task.DueDate.IsDateGreaterThan(DateTime.Now)) + if (filter.Equals("-due:future", StringComparison.OrdinalIgnoreCase) + && task.DueDate.IsDateGreaterThan(DateTime.Now)) { include = false; continue; } - else if (filter.Equals("-due:past", StringComparison.OrdinalIgnoreCase) - && task.DueDate.IsDateLessThan(DateTime.Now)) + if (filter.Equals("-due:past", StringComparison.OrdinalIgnoreCase) + && task.DueDate.IsDateLessThan(DateTime.Now)) { include = false; continue; } - else if (filter.Equals("-due:active", StringComparison.OrdinalIgnoreCase) - && !task.DueDate.IsNullOrEmpty() - && !task.DueDate.IsDateGreaterThan(DateTime.Now)) + if (filter.Equals("-due:active", StringComparison.OrdinalIgnoreCase) + && !task.DueDate.IsNullOrEmpty() + && !task.DueDate.IsDateGreaterThan(DateTime.Now)) { include = false; continue; } - else if (filter.Equals("-DONE", StringComparison.Ordinal) && task.Completed) + + // "DONE" filter is different from "due" filter in that + // "due" is part of todotxt syntax and would very unlikely occur in task text + // while the word "done" can occur in the text + if (filter.Equals("-DONE", StringComparison.Ordinal)) { - include = false; - continue; + if (task.Completed) include = false; } - else if (filter.Equals("DONE", StringComparison.Ordinal) && !task.Completed) + else if (filter.Equals("DONE", StringComparison.Ordinal)) { - include = false; - continue; + if (!task.Completed) include = false; } - - if (filter.Substring(0, 1) == "-") + // so if the filter is "DONE" or "-DONE", pass the substring test + else { - if (task.Raw.Contains(filter.Substring(1), comparer)) + if (filter.Substring(0, 1) == "-") + { + if (task.Raw.Contains(filter.Substring(1), comparer)) + include = false; + } + else if (!task.Raw.Contains(filter, comparer)) + { include = false; - } - else if (!task.Raw.Contains(filter, comparer)) - { - include = false; + } } } } From 54ab5e1bd1850fa3b48ef0156d6528006554d1c9 Mon Sep 17 00:00:00 2001 From: Keigh Rim Date: Tue, 8 May 2018 21:39:26 -0400 Subject: [PATCH 3/7] dotsettings file is now ignored --- .gitignore | 1 + ToDo.Net.sln.DotSettings | 7 ------- 2 files changed, 1 insertion(+), 7 deletions(-) delete mode 100644 ToDo.Net.sln.DotSettings diff --git a/.gitignore b/.gitignore index 407b5ffa..937270c6 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,7 @@ packages/ *.iml .vs _ReSharper* +*.sln.Dotsettings # linux *~ diff --git a/ToDo.Net.sln.DotSettings b/ToDo.Net.sln.DotSettings deleted file mode 100644 index 15c8d557..00000000 --- a/ToDo.Net.sln.DotSettings +++ /dev/null @@ -1,7 +0,0 @@ - - ONLY_FOR_MULTILINE - ONLY_FOR_MULTILINE - ONLY_FOR_MULTILINE - ONLY_FOR_MULTILINE - ONLY_FOR_MULTILINE - True \ No newline at end of file From 9d0628c977100f3706a913be726b845172d59f8e Mon Sep 17 00:00:00 2001 From: Keigh Rim Date: Tue, 8 May 2018 23:21:36 -0400 Subject: [PATCH 4/7] portable settings file now is global * keeps only one set of configurations (previously keps diff sets for diff machines) --- Client/PortableSettingsProvider.cs | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/Client/PortableSettingsProvider.cs b/Client/PortableSettingsProvider.cs index f840cb4c..59cb90d6 100644 --- a/Client/PortableSettingsProvider.cs +++ b/Client/PortableSettingsProvider.cs @@ -22,7 +22,7 @@ private string _filePath get { return Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), - string.Format("{0}.settings", ApplicationName)); + string.Format("{0}.settings", ApplicationName)); } } @@ -43,15 +43,9 @@ private XmlNode _localSettingsNode } } - private XmlNode _globalSettingsNode - { - get { return GetSettingsNode(_globalSettingsNodeName); } - } + private XmlNode _globalSettingsNode => GetSettingsNode(_globalSettingsNodeName); - private XmlNode _rootNode - { - get { return _rootDocument.SelectSingleNode(_rootNodeName); } - } + private XmlNode _rootNode => _rootDocument.SelectSingleNode(_rootNodeName); private XmlDocument _rootDocument { @@ -132,9 +126,7 @@ public override SettingsPropertyValueCollection GetPropertyValues(SettingsContex private void SetValue(SettingsPropertyValue propertyValue) { - XmlNode targetNode = IsGlobal(propertyValue.Property) - ? _globalSettingsNode - : _localSettingsNode; + XmlNode targetNode = _globalSettingsNode; XmlNode settingNode = targetNode.SelectSingleNode(string.Format("setting[@name='{0}']", propertyValue.Name)); @@ -156,7 +148,7 @@ private void SetValue(SettingsPropertyValue propertyValue) private string GetValue(SettingsProperty property) { - XmlNode targetNode = IsGlobal(property) ? _globalSettingsNode : _localSettingsNode; + XmlNode targetNode = _globalSettingsNode; XmlNode settingNode = targetNode.SelectSingleNode(string.Format("setting[@name='{0}']", property.Name)); if (settingNode == null) @@ -200,7 +192,6 @@ public XmlDocument GetBlankXmlDocument() public void Reset(SettingsContext context) { - _localSettingsNode.RemoveAll(); _globalSettingsNode.RemoveAll(); _xmlDocument.Save(_filePath); From 7a5eed271eb2cf49d85c4493922a4ad61d8a3768 Mon Sep 17 00:00:00 2001 From: Geliy Sokolov Date: Thu, 6 Sep 2018 10:31:39 +1100 Subject: [PATCH 5/7] Implements "minimise on close" feature. --- Client/Controls/MainWindow.xaml | 1 + Client/Controls/MainWindow.xaml.cs | 15 +++++++++++++++ Client/Controls/Options.xaml | 1 + Client/Controls/Options.xaml.cs | 1 + Client/MainWindowViewModel.cs | 1 + Client/User.Designer.cs | 13 +++++++++++++ Client/User.settings | 3 +++ Client/app.config | 3 +++ 8 files changed, 38 insertions(+) diff --git a/Client/Controls/MainWindow.xaml b/Client/Controls/MainWindow.xaml index d1f403ff..f86eac95 100644 --- a/Client/Controls/MainWindow.xaml +++ b/Client/Controls/MainWindow.xaml @@ -5,6 +5,7 @@ xmlns:local="clr-namespace:Client" xmlns:convertors="clr-namespace:Client.Converters" Loaded="Window_Loaded" + Closing="Window_Closing" Title="todotxt.net" mc:Ignorable="d" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" diff --git a/Client/Controls/MainWindow.xaml.cs b/Client/Controls/MainWindow.xaml.cs index 6b8b2639..8898a0c0 100644 --- a/Client/Controls/MainWindow.xaml.cs +++ b/Client/Controls/MainWindow.xaml.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics; using System.Reflection; using System.Windows; @@ -136,6 +137,20 @@ private void Window_Loaded(object sender, RoutedEventArgs e) DataContext = ViewModel; } + private void Window_Closing(object sender, CancelEventArgs e) + { + if (Application.Current.ShutdownMode == ShutdownMode.OnExplicitShutdown) + { + return; + } + + if (User.Default.MinimiseToSystemTray && User.Default.MinimiseOnClose) + { + e.Cancel = true; + WindowState = WindowState.Minimized; + } + } + #region window location handlers private void Window_LocationChanged(object sender, EventArgs e) { diff --git a/Client/Controls/Options.xaml b/Client/Controls/Options.xaml index 3a979688..389fd473 100644 --- a/Client/Controls/Options.xaml +++ b/Client/Controls/Options.xaml @@ -46,6 +46,7 @@ + diff --git a/Client/Controls/Options.xaml.cs b/Client/Controls/Options.xaml.cs index 2fa0d10c..0fdaa588 100644 --- a/Client/Controls/Options.xaml.cs +++ b/Client/Controls/Options.xaml.cs @@ -33,6 +33,7 @@ public Options(FontInfo taskFont) cbAddCreationDate.IsChecked = User.Default.AddCreationDate; cbDebugOn.IsChecked = User.Default.DebugLoggingOn; cbMinToSysTray.IsChecked = User.Default.MinimiseToSystemTray; + cbMinOnClose.IsChecked = User.Default.MinimiseOnClose; cbRequireCtrlEnter.IsChecked = User.Default.RequireCtrlEnter; cbAllowGrouping.IsChecked = User.Default.AllowGrouping; cbMoveFocusToTaskListAfterAddingNewTask.IsChecked = User.Default.MoveFocusToTaskListAfterAddingNewTask; diff --git a/Client/MainWindowViewModel.cs b/Client/MainWindowViewModel.cs index 272de064..4de48c18 100644 --- a/Client/MainWindowViewModel.cs +++ b/Client/MainWindowViewModel.cs @@ -1493,6 +1493,7 @@ public void ShowOptionsDialog() User.Default.AddCreationDate = o.cbAddCreationDate.IsChecked.Value; User.Default.DebugLoggingOn = o.cbDebugOn.IsChecked.Value; User.Default.MinimiseToSystemTray = o.cbMinToSysTray.IsChecked.Value; + User.Default.MinimiseOnClose = o.cbMinOnClose.IsChecked.Value; User.Default.RequireCtrlEnter = o.cbRequireCtrlEnter.IsChecked.Value; User.Default.AllowGrouping = o.cbAllowGrouping.IsChecked.Value; User.Default.PreserveWhiteSpace = o.cbPreserveWhiteSpace.IsChecked.Value; diff --git a/Client/User.Designer.cs b/Client/User.Designer.cs index 2a93b4da..33b2471c 100644 --- a/Client/User.Designer.cs +++ b/Client/User.Designer.cs @@ -203,6 +203,18 @@ public bool MinimiseToSystemTray { } } + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool MinimiseOnClose { + get { + return ((bool)(this["MinimiseOnClose"])); + } + set { + this["MinimiseOnClose"] = value; + } + } + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("False")] @@ -490,6 +502,7 @@ public bool ShowHidenTasks { this["ShowHidenTasks"] = value; } } + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("True")] diff --git a/Client/User.settings b/Client/User.settings index c6f16d4a..51440d80 100644 --- a/Client/User.settings +++ b/Client/User.settings @@ -47,6 +47,9 @@ False + + False + False diff --git a/Client/app.config b/Client/app.config index da9a2d4d..a5789485 100644 --- a/Client/app.config +++ b/Client/app.config @@ -52,6 +52,9 @@ False + + False + False From 938c9b7fdd708714dcc66703791ee2c2d0fa766c Mon Sep 17 00:00:00 2001 From: Geliy Sokolov Date: Thu, 1 Nov 2018 15:28:53 +1100 Subject: [PATCH 6/7] Implement "New task with priority" #370 - Added shortcut for shift + 9 for new task with priotity - Added intelli completion for priorities --- Client/Controls/IntellisenseTextBox.cs | 21 +++++++++++++++++++-- Client/Controls/MainWindow.xaml | 3 +++ Client/Controls/MainWindow.xaml.cs | 5 +++++ Client/MainWindowViewModel.cs | 9 +++++++++ Client/Resource.Designer.cs | 4 +++- Client/Resource.resx | 1 + 6 files changed, 40 insertions(+), 3 deletions(-) diff --git a/Client/Controls/IntellisenseTextBox.cs b/Client/Controls/IntellisenseTextBox.cs index 2687128c..fce84d97 100644 --- a/Client/Controls/IntellisenseTextBox.cs +++ b/Client/Controls/IntellisenseTextBox.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Text.RegularExpressions; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; @@ -22,6 +23,9 @@ public class IntellisenseTextBox : TextBox private ListBox IntellisenseList { get; set; } private int IntelliPos { get; set; } // used to position the Intellisense popup + private readonly Regex StartDateWithPriorityRegex = new Regex(@"[0-9]{4}\-[0-9]{2}\-[0-9]{2}\s\("); + private readonly List Priorities = Enumerable.Range('A', 26).Select(i => $"({Convert.ToChar(i)})").ToList(); + public TaskList TaskList { get @@ -249,7 +253,7 @@ private string FindIntelliWord() } /// - /// Triggers the Intellisense popup to appear when "+" or "@" is pressed in the text box. + /// Triggers the Intellisense popup to appear when "+", "@" or "(" is pressed in the text box. /// /// Not used /// Event arguments @@ -259,12 +263,17 @@ private void IntellisenseTextBox_TextChanged(object sender, TextChangedEventArgs { return; } - + if (this.TaskList == null) { return; } + CheckKeyAndShowPopup(); + } + + public void CheckKeyAndShowPopup() + { var lastAddedCharacter = this.Text.Substring(this.CaretIndex - 1, 1); switch (lastAddedCharacter) { @@ -277,6 +286,14 @@ private void IntellisenseTextBox_TextChanged(object sender, TextChangedEventArgs this.IntelliPos = this.CaretIndex - 1; ShowIntellisensePopup(this.TaskList.Contexts, this.GetRectFromCharacterIndex(this.IntelliPos)); break; + case "(": + if (this.CaretIndex == 1 || + (this.CaretIndex == 12 && StartDateWithPriorityRegex.IsMatch(this.Text.Substring(0, 12)))) + { + this.IntelliPos = this.CaretIndex - 1; + ShowIntellisensePopup(Priorities, this.GetRectFromCharacterIndex(this.IntelliPos)); + } + break; } } diff --git a/Client/Controls/MainWindow.xaml b/Client/Controls/MainWindow.xaml index f86eac95..e1d04280 100644 --- a/Client/Controls/MainWindow.xaml +++ b/Client/Controls/MainWindow.xaml @@ -32,6 +32,7 @@ + @@ -120,6 +121,7 @@ + @@ -303,6 +305,7 @@ + diff --git a/Client/Controls/MainWindow.xaml.cs b/Client/Controls/MainWindow.xaml.cs index 8898a0c0..216d1158 100644 --- a/Client/Controls/MainWindow.xaml.cs +++ b/Client/Controls/MainWindow.xaml.cs @@ -306,6 +306,11 @@ private void NewTaskExecuted(object sender, RoutedEventArgs e) ViewModel.AddNewTask(); } + private void NewTaskWithPriorityExecuted(object sender, RoutedEventArgs e) + { + ViewModel.AddNewTaskWithPriority(); + } + private void UpdateTaskExecuted(object sender, RoutedEventArgs e) { ViewModel.UpdateTask(); diff --git a/Client/MainWindowViewModel.cs b/Client/MainWindowViewModel.cs index 4de48c18..48b26c20 100644 --- a/Client/MainWindowViewModel.cs +++ b/Client/MainWindowViewModel.cs @@ -17,6 +17,7 @@ using System.ComponentModel; using System.Text.RegularExpressions; using System.Globalization; +using System.Windows.Threading; namespace Client { @@ -970,6 +971,14 @@ public void AddNewTask() _window.taskText.Focus(); } + public void AddNewTaskWithPriority() + { + AddNewTask(); + _window.taskText.Text = _window.taskText.Text.Length > 0 ? $"( {_window.taskText.Text}" : "("; + _window.taskText.CaretIndex = 1; + Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>_window.taskText.CheckKeyAndShowPopup())); + } + public void UpdateTask() { // Abort if no task, or more than one task, is selected. diff --git a/Client/Resource.Designer.cs b/Client/Resource.Designer.cs index b0ddef76..d1543990 100644 --- a/Client/Resource.Designer.cs +++ b/Client/Resource.Designer.cs @@ -100,6 +100,7 @@ internal static string CSS { /// - O or Ctrl+O: open todo.txt file /// - C or Ctrl+N: new todo.txt file /// - N: new task + /// - ( or Shift+9: new task with priority /// - J: next task /// - K: prev task /// - X: toggle task completion @@ -109,7 +110,8 @@ internal static string CSS { /// - T: append text to selected tasks /// - P: Postpone task (enter days to postpone, or day of week to postpone to) /// - F: filter tasks (free-text, one filter condition per line) - /// - I: set priority /// [rest of string was truncated]";. + /// - I: set priority + /// [rest of string was truncated]";. /// internal static string HelpText { get { diff --git a/Client/Resource.resx b/Client/Resource.resx index 03d355cd..78e9e243 100644 --- a/Client/Resource.resx +++ b/Client/Resource.resx @@ -196,6 +196,7 @@ - O or Ctrl+O: open todo.txt file - C or Ctrl+N: new todo.txt file - N: new task + - ( or Shift+9: new task with priority - J: next task - K: prev task - X: toggle task completion From a893fd6023817fbb297237ec58519447a1f44485 Mon Sep 17 00:00:00 2001 From: tenbob Date: Mon, 19 Nov 2018 10:19:25 +0000 Subject: [PATCH 7/7] Update README.md This change is to clarify the description which was rather misleading. It implied that the keyboard driven interface was the the **only** one available. Of course the app has a menu interface as well. I missed out on this excellent project for some time because I thought it would have a steep learning curve and be difficult to use. --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 59d7214c..2afa161a 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,8 @@ Please send your pull requests to the 'dev' branch. If you forget it's not a maj #### Goals - - minimalist, keyboard-driven UI + - menu driven interface for novices + - minimalist, keyboard-driven UI for expert users - vim/gmail/twitter-like keyboard nav (single key, easily accessible) - re-usable library that other projects can use as a todo.txt API - API (but not UI) runs under Mono