diff --git a/ChangeLog b/ChangeLog index 0a834d4a6..1633bb281 100644 --- a/ChangeLog +++ b/ChangeLog @@ -13,6 +13,8 @@ Features + The 'summary' report now uses 'project.indented' format. + Applied patch to allow ID ranges and UUIDs when editing dependencies (thanks to Louis-Claude Canon). + + Supports 'scheduled' date for tasks, which represent the earliest opportunity + to work on a task. + Performance improvements: + Added parse-free convenience functions + Filter optimization: with no 'OR' or 'XOR' operators, no UUIDS but with IDs diff --git a/NEWS b/NEWS index 8f0e21ca3..b93c838ed 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,8 @@ New Features in taskwarrior 2.0.1 - The new 'project.indented' format is available and used in the 'projects' and 'summary' commands. + - Support for the 'scheduled' date for a task, which represent the earliest + opportunity to work on a task. Please refer to the ChangeLog file for full details. There are too many to list here. @@ -13,7 +15,8 @@ New commands in taskwarrior 2.0.1 New configuration options in taskwarrior 2.0.1 - - None + - urgency.scheduled.coefficient + - color.scheduled Newly deprecated features in taskwarrior 2.0.1 diff --git a/doc/man/taskrc.5.in b/doc/man/taskrc.5.in index 3aa49f673..d21755513 100644 --- a/doc/man/taskrc.5.in +++ b/doc/man/taskrc.5.in @@ -733,6 +733,9 @@ Task is due today .B color.active Task is started, therefore active. .br +.B color.scheduled +Task is scheduled, therefore ready for work. +.br .B color.blocked Task is blocked by a dependency. .br @@ -944,47 +947,51 @@ has a configurable coefficient. Those coefficients are: .TP .B urgency.next.coefficient=15.0 .RS -Urgency coefficients for 'next' special tag +Urgency coefficient for 'next' special tag .RE .B urgency.blocking.coefficient=8.0 .RS -Urgency coefficients for blocking tasks +Urgency coefficient for blocking tasks .RE .B urgency.blocked.coefficient=-5.0 .RS -Urgency coefficients for blocked tasks +Urgency coefficient for blocked tasks .RE .B urgency.due.coefficient=12.0 .RS -Urgency coefficients for due dates +Urgency coefficient for due dates .RE .B urgency.priority.coefficient=6.0 .RS -Urgency coefficients for priorities +Urgency coefficient for priorities .RE .B urgency.waiting.coefficient=-3.0 .RS -Urgency coefficients for waiting status +Urgency coefficient for waiting status .RE .B urgency.active.coefficient=4.0 .RS -Urgency coefficients for active tasks +Urgency coefficient for active tasks +.RE +.B urgency.scheduled.coefficient=5.0 +.RS +Urgency coefficient for scheduled tasks .RE .B urgency.project.coefficient=1.0 .RS -Urgency coefficients for projects +Urgency coefficient for projects .RE .B urgency.tags.coefficient=1.0 .RS -Urgency coefficients for tags +Urgency coefficient for tags .RE .B urgency.annotations.coefficient=1.0 .RS -Urgency coefficients for annotations +Urgency coefficient for annotations .RE .B urgency.age.coefficient=2.0 .RS -Urgency coefficients for the age of tasks +Urgency coefficient for the age of tasks .RE .B urgency.age.max=365 .RS diff --git a/src/Config.cpp b/src/Config.cpp index e9a42b62b..38e6d6616 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -140,12 +140,13 @@ std::string Config::_defaults = "urgency.blocking.coefficient=8.0 # Urgency coefficient for blocking tasks\n" "urgency.priority.coefficient=6.0 # Urgency coefficient for priorities\n" "urgency.active.coefficient=4.0 # Urgency coefficient for active tasks\n" + "urgency.scheduled.coefficient=5.0 # Urgency coefficient for scheduled tasks\n" "urgency.age.coefficient=2.0 # Urgency coefficient for age\n" "urgency.annotations.coefficient=1.0 # Urgency coefficient for annotations\n" "urgency.tags.coefficient=1.0 # Urgency coefficient for tags\n" "urgency.project.coefficient=1.0 # Urgency coefficient for projects\n" "urgency.blocked.coefficient=-5.0 # Urgency coefficient for blocked tasks\n" - "urgency.waiting.coefficient=-3.0 # Urgency coefficient for waiting status\n" + "urgency.waiting.coefficient=-3.0 # Urgency coefficient for waiting status\n" "urgency.age.max=365 # Maximum age in days\n" "\n" "#urgency.user.project.foo.coefficient=5.0 # Urgency coefficients for 'foo' project\n" @@ -315,138 +316,101 @@ std::string Config::_defaults = "\n" "# Reports\n" "\n" - "# task long\n" "report.long.description=Lists all pending tasks\n" "report.long.columns=id,project,priority,entry,start,due,recur,due.countdown,entry.age,depends,tags,description\n" "report.long.labels=ID,Project,Pri,Added,Started,Due,Recur,Countdown,Age,Deps,Tags,Description\n" "report.long.sort=due+,priority-,project+\n" "report.long.filter=status:pending\n" - "#report.long.dateformat=m/d/Y\n" - "#report.long.annotations=full\n" "\n" - "# task list\n" "report.list.description=Lists all pending tasks\n" "report.list.columns=id,project,priority,due,start.active,entry.age,description\n" "report.list.labels=ID,Project,Pri,Due,Active,Age,Description\n" "report.list.sort=due+,priority-,start-,project+\n" "report.list.filter=status:pending\n" - "#report.list.dateformat=m/d/Y\n" - "#report.list.annotations=full\n" "\n" - "# task ls\n" "report.ls.description=Minimal listing of all pending tasks\n" "report.ls.columns=id,project,priority,description\n" "report.ls.labels=ID,Project,Pri,Description\n" "report.ls.sort=priority-,project+\n" "report.ls.filter=status:pending\n" - "#report.ls.dateformat=m/d/Y\n" - "#report.ls.annotations=full\n" "\n" - "# task minimal\n" "report.minimal.description=Minimal listing of all pending tasks\n" "report.minimal.columns=id,project,description.truncated\n" "report.minimal.labels=ID,Project,Description\n" "report.minimal.sort=project+,description+\n" "report.minimal.filter=status:pending\n" - "#report.minimal.dateformat=m/d/Y\n" - "#report.minimal.annotations=full\n" "\n" - "# task newest\n" "report.newest.description=Shows the newest tasks\n" "report.newest.columns=id,project,priority,due,start.active,entry.age,description\n" "report.newest.labels=ID,Project,Pri,Due,Active,Age,Description\n" "report.newest.sort=id-\n" "report.newest.filter=status:pending limit:10\n" - "#report.newest.dateformat=m/d/Y\n" - "#report.newest.annotations=full\n" "\n" - "# task oldest\n" "report.oldest.description=Shows the oldest tasks\n" "report.oldest.columns=id,project,priority,due,start.active,entry.age,description\n" "report.oldest.labels=ID,Project,Pri,Due,Active,Age,Description\n" "report.oldest.sort=id+\n" "report.oldest.filter=status:pending limit:10\n" - "#report.oldest.dateformat=m/d/Y\n" - "#report.oldest.annotations=full\n" "\n" - "# task overdue\n" "report.overdue.description=Lists overdue tasks\n" "report.overdue.columns=id,project,priority,due,start.active,entry.age,description\n" "report.overdue.labels=ID,Project,Pri,Due,Active,Age,Description\n" "report.overdue.sort=due+,priority-,start-,project+\n" "report.overdue.filter=status:pending due.before:now\n" - "#report.overdue.dateformat=m/d/Y\n" - "#report.overdue.annotations=full\n" "\n" - "# task active\n" "report.active.description=Lists active tasks\n" "report.active.columns=id,project,priority,due,start.active,entry.age,description\n" "report.active.labels=ID,Project,Pri,Due,Active,Age,Description\n" "report.active.sort=due+,priority-,project+\n" "report.active.filter=status:pending start.any:\n" - "#report.active.dateformat=m/d/Y\n" - "#report.active.annotations=full\n" "\n" - "# task completed\n" "report.completed.description=Lists completed tasks\n" "report.completed.columns=end,project,priority,entry.age,description,uuid\n" "report.completed.labels=Complete,Project,Pri,Age,Description,UUID\n" "report.completed.sort=end+,priority-,project+\n" "report.completed.filter=status:completed\n" - "#report.completed.dateformat=m/d/Y\n" - "#report.completed.annotations=full\n" "\n" - "# task recurring\n" "report.recurring.description=Lists recurring tasks\n" "report.recurring.columns=id,project,priority,due,recur,start.active,entry.age,description\n" "report.recurring.labels=ID,Project,Pri,Due,Recur,Active,Age,Description\n" "report.recurring.sort=due+,priority-,start-,project+\n" "report.recurring.filter=status:pending parent.any:\n" - "#report.recurring.dateformat=m/d/Y\n" - "#report.recurring.annotations=full\n" "\n" - "# task waiting\n" "report.waiting.description=Lists all waiting tasks\n" "report.waiting.columns=id,project,priority,wait,entry.age,description\n" "report.waiting.labels=ID,Project,Pri,Wait,Age,Description\n" "report.waiting.sort=wait+,priority-,project+\n" "report.waiting.filter=status:waiting\n" - "#report.waiting.dateformat=m/d/Y\n" - "#report.waiting.annotations=full\n" "\n" - "# task all\n" "report.all.description=Lists all pending and completed tasks\n" "report.all.columns=id,status,project,priority,due,end,start.active,entry.age,description\n" "report.all.labels=ID,Status,Project,Pri,Due,Completed,Active,Age,Description\n" "report.all.sort=entry+\n" "report.all.filter=status.not:deleted\n" - "#report.all.dateformat=m/d/Y\n" - "#report.all.annotations=full\n" "\n" - "# task next\n" "report.next.description=Lists the most urgent tasks\n" "report.next.columns=id,project,priority,due,start.active,entry.age,urgency,description\n" "report.next.filter=status:pending limit:page\n" "report.next.labels=ID,Project,Pri,Due,A,Age,Urgency,Description\n" "report.next.sort=urgency-,due+,priority-,start-,project+\n" - "#report.next.dateformat=m/d/Y\n" - "#report.next.annotations=full\n" "\n" - "# task blocked\n" + "report.ready.description=Lists the most urgent tasks\n" + "report.ready.columns=id,project,priority,due,start.active,entry.age,urgency,description\n" + "report.ready.filter=status:pending limit:page wait.none: (scheduled.none: or scheduled.before:now)\n" + "report.ready.labels=ID,Project,Pri,Due,A,Age,Urgency,Description\n" + "report.ready.sort=urgency-,due+,priority-,start-,project+\n" + "\n" "report.blocked.description=Lists all blocked tasks\n" "report.blocked.columns=id,depends,project,priority,due,start.active,entry.age,description\n" "report.blocked.labels=ID,Deps,Project,Pri,Due,Active,Age,Description\n" "report.blocked.sort=due+,priority-,start-,project+\n" "report.blocked.filter=status:pending depends.any:\n" - "#report.blocked.dateformat=m/d/Y\n" "\n" - "# task unblocked\n" "report.unblocked.description=Lists all unblocked tasks\n" "report.unblocked.columns=id,depends,project,priority,due,start.active,entry.age,description\n" "report.unblocked.labels=ID,Deps,Project,Pri,Due,Active,Age,Description\n" "report.unblocked.sort=due+,priority-,start-,project+\n" "report.unblocked.filter=status:pending depends.none:\n" - "#report.unblocked.dateformat=m/d/Y\n" "\n"; //////////////////////////////////////////////////////////////////////////////// diff --git a/src/Task.cpp b/src/Task.cpp index 73dd99b22..fb62338dd 100644 --- a/src/Task.cpp +++ b/src/Task.cpp @@ -53,6 +53,7 @@ static std::map coefficients; float urgencyPriorityCoefficient = 0.0; float urgencyProjectCoefficient = 0.0; float urgencyActiveCoefficient = 0.0; +float urgencyScheduledCoefficient = 0.0; float urgencyWaitingCoefficient = 0.0; float urgencyBlockedCoefficient = 0.0; float urgencyAnnotationsCoefficient = 0.0; @@ -74,6 +75,7 @@ void initializeUrgencyCoefficients () urgencyPriorityCoefficient = context.config.getReal ("urgency.priority.coefficient"); urgencyProjectCoefficient = context.config.getReal ("urgency.project.coefficient"); urgencyActiveCoefficient = context.config.getReal ("urgency.active.coefficient"); + urgencyScheduledCoefficient = context.config.getReal ("urgency.scheduled.coefficient"); urgencyWaitingCoefficient = context.config.getReal ("urgency.waiting.coefficient"); urgencyBlockedCoefficient = context.config.getReal ("urgency.blocked.coefficient"); urgencyAnnotationsCoefficient = context.config.getReal ("urgency.annotations.coefficient"); @@ -1131,35 +1133,14 @@ void Task::validate () // 2) To provide suitable warnings about odd states - // When a task has a due date, other dates should conform. - if (has ("due")) - { - Date due (get_date ("due")); - - // Verify wait < due - if (has ("wait")) - { - Date wait (get_date ("wait")); - if (wait > due) - context.footnote (STRING_TASK_VALID_WAIT); - } - - Date entry (get_date ("entry")); - - if (has ("start")) - { - Date start (get_date ("start")); - if (entry > start) - context.footnote (STRING_TASK_VALID_START); - } - - if (has ("end")) - { - Date end (get_date ("end")); - if (entry > end) - context.footnote (STRING_TASK_VALID_END); - } - } + // Date relationships. + validate_before ("wait", "due"); + validate_before ("entry", "start"); + validate_before ("entry", "end"); + validate_before ("wait", "scheduled"); + validate_before ("scheduled", "start"); + validate_before ("scheduled", "due"); + validate_before ("scheduled", "end"); // 3) To generate errors when the inconsistencies are not fixable @@ -1196,6 +1177,19 @@ void Task::validate () } } +void Task::validate_before (const std::string& left, const std::string& right) +{ + if (has (left) && + has (right)) + { + Date date_left (get_date (left)); + Date date_right (get_date (right)); + + if (date_left > date_right) + context.footnote (format (STRING_TASK_VALID_BEFORE, left, right)); + } +} + //////////////////////////////////////////////////////////////////////////////// int Task::determineVersion (const std::string& line) { @@ -1290,6 +1284,7 @@ float Task::urgency_c () const value += fabsf (urgencyPriorityCoefficient) > epsilon ? (urgency_priority () * urgencyPriorityCoefficient) : 0.0; value += fabsf (urgencyProjectCoefficient) > epsilon ? (urgency_project () * urgencyProjectCoefficient) : 0.0; value += fabsf (urgencyActiveCoefficient) > epsilon ? (urgency_active () * urgencyActiveCoefficient) : 0.0; + value += fabsf (urgencyScheduledCoefficient) > epsilon ? (urgency_scheduled () * urgencyScheduledCoefficient) : 0.0; value += fabsf (urgencyWaitingCoefficient) > epsilon ? (urgency_waiting () * urgencyWaitingCoefficient) : 0.0; value += fabsf (urgencyBlockedCoefficient) > epsilon ? (urgency_blocked () * urgencyBlockedCoefficient) : 0.0; value += fabsf (urgencyAnnotationsCoefficient) > epsilon ? (urgency_annotations () * urgencyAnnotationsCoefficient) : 0.0; @@ -1305,6 +1300,7 @@ float Task::urgency_c () const << "# pri " << (urgency_priority () * urgencyPriorityCoefficient) << "# pro " << (urgency_project () * urgencyProjectCoefficient) << "# act " << (urgency_active () * urgencyActiveCoefficient) + << "# sch " << (urgency_scheduled () * urgencyScheduledCoefficient) << "# wai " << (urgency_waiting () * urgencyWaitingCoefficient) << "# blk " << (urgency_blocked () * urgencyBlockedCoefficient) << "# ann " << (urgency_annotations () * urgencyAnnotationsCoefficient) @@ -1394,6 +1390,16 @@ float Task::urgency_active () const return 0.0; } +//////////////////////////////////////////////////////////////////////////////// +float Task::urgency_scheduled () const +{ + if (has ("scheduled") && + get_date ("scheduled") < time (NULL)) + return 1.0; + + return 0.0; +} + //////////////////////////////////////////////////////////////////////////////// float Task::urgency_waiting () const { diff --git a/src/Task.h b/src/Task.h index 8e2a7515e..edd59d86f 100644 --- a/src/Task.h +++ b/src/Task.h @@ -110,10 +110,12 @@ public: private: int determineVersion (const std::string&); void legacyParse (const std::string&); + void validate_before (const std::string&, const std::string&); inline float urgency_priority () const; inline float urgency_project () const; inline float urgency_active () const; + inline float urgency_scheduled () const; inline float urgency_waiting () const; inline float urgency_blocked () const; inline float urgency_annotations () const; diff --git a/src/columns/CMakeLists.txt b/src/columns/CMakeLists.txt index 9d66eb26a..9b0cb464e 100644 --- a/src/columns/CMakeLists.txt +++ b/src/columns/CMakeLists.txt @@ -21,6 +21,7 @@ set (columns_SRCS Column.cpp Column.h ColPriority.cpp ColPriority.h ColProject.cpp ColProject.h ColRecur.cpp ColRecur.h + ColScheduled.cpp ColScheduled.h ColStart.cpp ColStart.h ColStatus.cpp ColStatus.h ColString.cpp ColString.h diff --git a/src/columns/ColScheduled.cpp b/src/columns/ColScheduled.cpp new file mode 100644 index 000000000..3897f5bd0 --- /dev/null +++ b/src/columns/ColScheduled.cpp @@ -0,0 +1,118 @@ +//////////////////////////////////////////////////////////////////////////////// +// taskwarrior - a command line task list manager. +// +// Copyright 2006-2012, Paul Beckingham, Federico Hernandez. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// http://www.opensource.org/licenses/mit-license.php +// +//////////////////////////////////////////////////////////////////////////////// + +#define L10N // Localization complete. + +#include +#include +#include +#include +#include +#include +#include + +extern Context context; + +//////////////////////////////////////////////////////////////////////////////// +ColumnScheduled::ColumnScheduled () +{ + _name = "scheduled"; + _label = STRING_COLUMN_LABEL_SCHED; + + _styles.push_back ("countdown"); + + Date now; + now += 125; + _examples.push_back (Duration (now - Date ()).formatCompact ()); +} + +//////////////////////////////////////////////////////////////////////////////// +ColumnScheduled::~ColumnScheduled () +{ +} + +//////////////////////////////////////////////////////////////////////////////// +bool ColumnScheduled::validate (std::string& value) +{ + return ColumnDate::validate (value); +} + +//////////////////////////////////////////////////////////////////////////////// +// Overriden so that style <----> label are linked. +// Note that you can not determine which gets called first. +void ColumnScheduled::setStyle (const std::string& value) +{ + _style = value; + + if (_style == "countdown" && _label == STRING_COLUMN_LABEL_DUE) + _label = STRING_COLUMN_LABEL_COUNT; +} + +//////////////////////////////////////////////////////////////////////////////// +// Set the minimum and maximum widths for the value. +void ColumnScheduled::measure (Task& task, int& minimum, int& maximum) +{ + minimum = maximum = 0; + + if (task.has (_name)) + { + if (_style == "countdown") + { + Date date ((time_t) strtol (task.get (_name).c_str (), NULL, 10)); + Date now; + minimum = maximum = Duration (now - date).format ().length (); + } + else + ColumnDate::measure (task, minimum, maximum); + } +} + +//////////////////////////////////////////////////////////////////////////////// +void ColumnScheduled::render ( + std::vector & lines, + Task& task, + int width, + Color& color) +{ + if (task.has (_name)) + { + if (_style == "countdown") + { + Date date ((time_t) strtol (task.get (_name).c_str (), NULL, 10)); + Date now; + + lines.push_back ( + color.colorize ( + rightJustify ( + Duration (now - date).format (), width))); + } + else + ColumnDate::render (lines, task, width, color); + } +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/columns/ColScheduled.h b/src/columns/ColScheduled.h new file mode 100644 index 000000000..594969697 --- /dev/null +++ b/src/columns/ColScheduled.h @@ -0,0 +1,47 @@ +//////////////////////////////////////////////////////////////////////////////// +// taskwarrior - a command line task list manager. +// +// Copyright 2006-2012, Paul Beckingham, Federico Hernandez. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// http://www.opensource.org/licenses/mit-license.php +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDED_COLSCHED +#define INCLUDED_COLSCHED +#define L10N // Localization complete. + +#include + +class ColumnScheduled : public ColumnDate +{ +public: + ColumnScheduled (); + ~ColumnScheduled (); + + bool validate (std::string&); + void setStyle (const std::string&); + void measure (Task&, int&, int&); + void render (std::vector &, Task&, int, Color&); +}; + +#endif +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/columns/Column.cpp b/src/columns/Column.cpp index e1f6712d1..ba131f3bf 100644 --- a/src/columns/Column.cpp +++ b/src/columns/Column.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -94,6 +95,7 @@ Column* Column::factory (const std::string& name, const std::string& report) else if (column_name == "priority") c = new ColumnPriority (); else if (column_name == "project") c = new ColumnProject (); else if (column_name == "recur") c = new ColumnRecur (); + else if (column_name == "scheduled") c = new ColumnScheduled (); else if (column_name == "start") c = new ColumnStart (); else if (column_name == "status") c = new ColumnStatus (); else if (column_name == "tags") c = new ColumnTags (); @@ -137,6 +139,7 @@ void Column::factory (std::map & all) c = new ColumnPriority (); all[c->_name] = c; c = new ColumnProject (); all[c->_name] = c; c = new ColumnRecur (); all[c->_name] = c; + c = new ColumnScheduled (); all[c->_name] = c; c = new ColumnStart (); all[c->_name] = c; c = new ColumnStatus (); all[c->_name] = c; c = new ColumnTags (); all[c->_name] = c; diff --git a/src/commands/CmdEdit.cpp b/src/commands/CmdEdit.cpp index 43c8b8326..0d512d4b3 100644 --- a/src/commands/CmdEdit.cpp +++ b/src/commands/CmdEdit.cpp @@ -159,6 +159,7 @@ std::string CmdEdit::formatTask (Task task) << " Created: " << formatDate (task, "entry") << "\n" << " Started: " << formatDate (task, "start") << "\n" << " Ended: " << formatDate (task, "end") << "\n" + << " Scheduled: " << formatDate (task, "scheduled") << "\n" << " Due: " << formatDate (task, "due") << "\n" << " Until: " << formatDate (task, "until") << "\n" << " Recur: " << task.get ("recur") << "\n" @@ -344,6 +345,37 @@ void CmdEdit::parseTask (Task& task, const std::string& after) } } + // scheduled + value = findValue (after, "\n Scheduled:"); + if (value != "") + { + if (task.get ("scheduled") != "") + { + Date original (task.get_date ("scheduled")); + std::string formatted = original.toString (context.config.get ("dateformat")); + + if (formatted != value) + { + context.footnote (STRING_EDIT_SCHED_MOD); + task.set ("scheduled", value); + } + } + else + { + context.footnote (STRING_EDIT_SCHED_MOD); + task.set ("scheduled", value); + } + } + else + { + if (task.get ("scheduled") != "") + { + context.footnote (STRING_EDIT_SCHED_DEL); + task.setStatus (Task::pending); + task.remove ("scheduled"); + } + } + // due value = findValue (after, "\n Due:"); if (value != "") diff --git a/src/commands/CmdInfo.cpp b/src/commands/CmdInfo.cpp index efcda3294..a17b0cafa 100644 --- a/src/commands/CmdInfo.cpp +++ b/src/commands/CmdInfo.cpp @@ -244,6 +244,14 @@ int CmdInfo::execute (std::string& output) view.set (row, 1, Date (task->get_date ("wait")).toString (dateformat)); } + // scheduled + if (task->has ("scheduled")) + { + row = view.addRow (); + view.set (row, 0, STRING_COLUMN_LABEL_SCHED); + view.set (row, 1, Date (task->get_date ("scheduled")).toString (dateformat)); + } + // start if (task->has ("start")) { diff --git a/src/commands/CmdShow.cpp b/src/commands/CmdShow.cpp index af86e898d..095d5de29 100644 --- a/src/commands/CmdShow.cpp +++ b/src/commands/CmdShow.cpp @@ -112,6 +112,7 @@ int CmdShow::execute (std::string& output) " color.pri.M" " color.pri.none" " color.recurring" + " color.scheduled" " color.summary.background" " color.summary.bar" " color.sync.added" @@ -185,6 +186,7 @@ int CmdShow::execute (std::string& output) " taskd.credentials" " undo.style" " urgency.active.coefficient" + " urgency.scheduled.coefficient" " urgency.annotations.coefficient" " urgency.blocked.coefficient" " urgency.blocking.coefficient" diff --git a/src/en-US.h b/src/en-US.h index 23286d76c..1e8c17461 100644 --- a/src/en-US.h +++ b/src/en-US.h @@ -182,6 +182,7 @@ #define STRING_COLUMN_LABEL_COLUMN "Columns" #define STRING_COLUMN_LABEL_STYLES "Supported Formats" #define STRING_COLUMN_LABEL_EXAMPLES "Example" +#define STRING_COLUMN_LABEL_SCHED "Scheduled" // Column Examples #define STRING_COLUMN_EXAMPLES_TAGS "home @chore" @@ -615,6 +616,8 @@ #define STRING_EDIT_END_MOD "End date modified." #define STRING_EDIT_END_DEL "End date removed." #define STRING_EDIT_END_SET_ERR "Cannot set a done date on a pending task." +#define STRING_EDIT_SCHED_MOD "Scheduled date modified." +#define STRING_EDIT_SCHED_DEL "Scheduled date removed." #define STRING_EDIT_DUE_MOD "Due date modified." #define STRING_EDIT_DUE_DEL "Due date removed." #define STRING_EDIT_DUE_DEL_ERR "Cannot remove a due date from a recurring task." @@ -761,9 +764,7 @@ #define STRING_TASK_DEPEND_CIRCULAR "Circular dependency detected and disallowed." #define STRING_TASK_VALID_DESC "A task must have a description." #define STRING_TASK_VALID_BLANK "Cannot add a task that is blank." -#define STRING_TASK_VALID_WAIT "Warning: You have specified a 'wait' date that is after the 'due' date." -#define STRING_TASK_VALID_START "Warning: You have specified a 'start' date that is before the 'entry' date." -#define STRING_TASK_VALID_END "Warning: You have specified an 'end' date that is before the 'entry' date." +#define STRING_TASK_VALID_BEFORE "Warning: You have specified that the '{1}' date is after the '{2}' date." #define STRING_TASK_VALID_REC_DUE "A recurring task must also have a 'due' date." #define STRING_TASK_VALID_UNTIL "Only recurring tasks may have an 'until' date." #define STRING_TASK_VALID_RECUR "The recurrence value '{1}' is not valid." @@ -873,12 +874,14 @@ " priority: Priority\n" \ " due: Due date\n" \ " recur: Recurrence frequency\n" \ - " until: Recurrence end date\n" \ + " until: Expiration date of a task\n" \ " limit: Desired number of rows in report, or 'page'\n" \ " wait: Date until task becomes pending\n" \ " entry: Date task was created\n" \ " end: Date task was completed/deleted\n" \ " start: Date task was started\n" \ + " scheduled: Date task is scheduled to start\n" \ + " depends: Other tasks that this task depends upon\n" \ "\n" \ "Attribute modifiers make filters more precise. Supported modifiers are:\n" \ " before (synonyms under, below)\n" \ diff --git a/src/rules.cpp b/src/rules.cpp index 088da9b97..b8654ca43 100644 --- a/src/rules.cpp +++ b/src/rules.cpp @@ -139,6 +139,14 @@ static void colorizeActive (Task& task, const std::string& rule, Color& c) c.blend (gsColor[rule]); } +//////////////////////////////////////////////////////////////////////////////// +static void colorizeScheduled (Task& task, const std::string& rule, Color& c) +{ + if (gsColor[rule].nontrivial () && + task.has ("scheduled")) + c.blend (gsColor[rule]); +} + //////////////////////////////////////////////////////////////////////////////// static void colorizeTag (Task& task, const std::string& rule, Color& c) { @@ -291,6 +299,7 @@ void autoColorize (Task& task, Color& c) else if (*r == "color.pri.H") colorizePriorityH (task, *r, c); else if (*r == "color.pri.none") colorizePriorityNone (task, *r, c); else if (*r == "color.active") colorizeActive (task, *r, c); + else if (*r == "color.scheduled") colorizeScheduled (task, *r, c); else if (*r == "color.project.none") colorizeProjectNone (task, *r, c); else if (*r == "color.tag.none") colorizeTagNone (task, *r, c); else if (*r == "color.due") colorizeDue (task, *r, c); diff --git a/test/urgency.t b/test/urgency.t index 6ba8e3b90..0a0246767 100755 --- a/test/urgency.t +++ b/test/urgency.t @@ -28,7 +28,7 @@ use strict; use warnings; -use Test::More tests => 46; +use Test::More tests => 48; # Create the rc file. if (open my $fh, '>', 'urgency.rc') @@ -300,6 +300,16 @@ qx{../src/task rc:urgency.rc add 11a +TAG}; # task 43 $output = qx{../src/task rc:urgency.rc 43 _urgency}; like ($output, qr/urgency 18$/ms, '+TAG = 18'); +# scheduled 0 (scheduled future) +qx {../src/task rc:urgency.rc add 12a scheduled:eom}; +$output = qx{../src/task rc:urgency.rc 44 _urgency}; +like ($output, qr/urgency 0$/ms, 'scheduled future = 0'); + +# scheduled 5 (scheduled past) +qx {../src/task rc:urgency.rc add 12b scheduled:yesterday}; +$output = qx{../src/task rc:urgency.rc 45 _urgency}; +like ($output, qr/urgency 5$/ms, 'scheduled past = 5'); + # Cleanup. unlink qw(pending.data completed.data undo.data backlog.data synch.key urgency.rc); ok (! -r 'pending.data' && diff --git a/test/wait.t b/test/wait.t index 10565ca14..3f8f975a6 100755 --- a/test/wait.t +++ b/test/wait.t @@ -75,7 +75,7 @@ $output = qx{../src/task rc:wait.rc all status:waiting wait:tomorrow}; like ($output, qr/tomorrow/ms, 'waiting task visible when specifically queried'); $output = qx{../src/task rc:wait.rc add Complain due:today wait:tomorrow}; -like ($output, qr/Warning: You have specified a 'wait' date that is after the 'due' date\./, 'warning on wait after due'); +like ($output, qr/Warning: You have specified that the 'wait' date is after the 'due' date\./, 'warning on wait after due'); # Cleanup. unlink qw(pending.data completed.data undo.data backlog.data synch.key wait.rc);