void ColumnParent::render ( std::vector <std::string>& lines, Task& task, int width, Color& color) { if (task.has (_name)) { // f30cb9c3-3fc0-483f-bfb2-3bf134f00694 default // 34f00694 short if (_style == "default" || _style == "long") { lines.push_back (color.colorize (leftJustify (task.get (_name), width))); } else if (_style == "short") { if (task.has (_name)) lines.push_back (color.colorize (leftJustify (task.get (_name).substr (28), width))); else lines.push_back (color.colorize (leftJustify ("", width))); } } }
void ColumnUDA::render ( std::vector <std::string>& lines, Task& task, int width, Color& color) { if (task.has (_name)) { if (_style == "default") { std::string value = task.get (_name); if (_type == "date") { // Determine the output date format, which uses a hierarchy of definitions. // rc.report.<report>.dateformat // rc.dateformat.report // rc.dateformat. std::string format = context.config.get ("report." + _report + ".dateformat"); if (format == "") format = context.config.get ("dateformat.report"); if (format == "") format = context.config.get ("dateformat"); lines.push_back ( color.colorize ( leftJustify ( Date ((time_t) strtol (value.c_str (), NULL, 10)) .toString (format), width))); } else if (_type == "duration") { lines.push_back ( color.colorize ( rightJustify ( Duration (value).formatISO (), width))); } else if (_type == "string") { std::vector <std::string> raw; wrapText (raw, value, width, _hyphenate); std::vector <std::string>::iterator i; for (i = raw.begin (); i != raw.end (); ++i) lines.push_back (color.colorize (leftJustify (*i, width))); } else if (_type == "numeric") { lines.push_back (color.colorize (rightJustify (value, width))); } } else if (_style == "indicator") { if (task.has (_name)) lines.push_back ( color.colorize ( rightJustify (context.config.get ("uda." + _name + ".indicator"), width))); } } }
int CmdLog::execute (std::string& output) { int rc = 0; // Apply the command line modifications to the new task. A3 modifications = context.a3.extract_modifications (); Task task; modify_task_description_replace (task, modifications); task.setStatus (Task::completed); // Recurring tasks get a special status. if (task.has ("recur")) throw std::string (STRING_CMD_LOG_NO_RECUR); if (task.has ("wait")) throw std::string (STRING_CMD_LOG_NO_WAITING); context.tdb2.add (task); if (context.verbose ("project")) context.footnote (onProjectChange (task)); context.tdb2.commit (); if (context.verbose ("affected") || context.config.getBoolean ("echo.command")) // Deprecated 2.0 output = std::string (STRING_CMD_LOG_LOGGED) + "\n"; return rc; }
//////////////////////////////////////////////////////////////////////////////// // Set the minimum and maximum widths for the value. // void ColumnUDA::measure (Task& task, unsigned int& minimum, unsigned int& maximum) { minimum = maximum = 0; if (task.has (_name)) { if (_style == "default") { std::string value = task.get (_name); if (value != "") { if (_type == "date") { // Determine the output date format, which uses a hierarchy of definitions. // rc.report.<report>.dateformat // rc.dateformat.report // rc.dateformat Date date ((time_t) strtol (value.c_str (), NULL, 10)); std::string format = context.config.get ("report." + _report + ".dateformat"); if (format == "") format = context.config.get ("dateformat.report"); if (format == "") format = context.config.get ("dateformat"); minimum = maximum = Date::length (format); } else if (_type == "duration") { minimum = maximum = utf8_width (Duration (value).formatISO ()); } else if (_type == "string") { std::string stripped = Color::strip (value); maximum = longestLine (stripped); minimum = longestWord (stripped); } else if (_type == "numeric") { minimum = maximum = utf8_width (value); } } } else if (_style == "indicator") { if (task.has (_name)) minimum = maximum = utf8_width (context.config.get ("uda." + _name + ".indicator")); } else throw format (STRING_COLUMN_BAD_FORMAT, _name, _style); } }
//////////////////////////////////////////////////////////////////////////////// // Set the minimum and maximum widths for the value. void ColumnStart::measure (Task& task, unsigned int& minimum, unsigned int& maximum) { minimum = maximum = 0; if (task.has (_name)) { if (_style == "active") { if (! task.has ("end")) minimum = maximum = utf8_width (context.config.get ("active.indicator")); } else ColumnDate::measure (task, minimum, maximum); } }
const std::vector <Task> TDB2::siblings (Task& task) { std::vector <Task> results; if (task.has ("parent")) { std::string parent = task.get ("parent"); // First load and scan pending. if (! pending._loaded_tasks) pending.load_tasks (); for (auto& i : pending._tasks) { // Do not include self in results. if (i.id != task.id) { // Do not include completed or deleted tasks. if (i.getStatus () != Task::completed && i.getStatus () != Task::deleted) { // If task has the same parent, it is a sibling. if (i.has ("parent") && i.get ("parent") == parent) { results.push_back (i); } } } } } return results; }
void ColumnMask::render ( std::vector <std::string>& lines, Task& task, int, Color& color) { if (task.has (_name)) lines.push_back (color.colorize (task.get ("mask"))); }
static void colorizeUDA (Task& task, const std::string& rule, const Color& base, Color& c, bool merge) { // Is the rule color.uda.name.value or color.uda.name? size_t pos = rule.find (".", 10); if (pos == std::string::npos) { if (task.has (rule.substr (10))) applyColor (base, c, merge); } else { const std::string uda = rule.substr (10, pos - 10); const std::string val = rule.substr (pos + 1); if ((val == "none" && ! task.has (uda)) || task.get (uda) == val) applyColor (base, c, merge); } }
//////////////////////////////////////////////////////////////////////////////// // Set the minimum and maximum widths for the value. void ColumnDate::measure (Task& task, unsigned int& minimum, unsigned int& maximum) { minimum = maximum = 0; if (task.has (_name)) { Date date (task.get_date (_name)); if (_style == "default" || _style == "formatted") { // Determine the output date format, which uses a hierarchy of definitions. // rc.report.<report>.dateformat // rc.dateformat.report // rc.dateformat. std::string format = context.config.get ("report." + _report + ".dateformat"); if (format == "") format = context.config.get ("dateformat.report"); if (format == "") format = context.config.get ("dateformat"); minimum = maximum = Date::length (format); } else if (_style == "countdown") { Date now; minimum = maximum = Duration (now - date).format ().length (); } else if (_style == "julian") { minimum = maximum = format (date.toJulian (), 13, 12).length (); } else if (_style == "epoch") { minimum = maximum = date.toEpochString ().length (); } else if (_style == "iso") { minimum = maximum = date.toISO ().length (); } else if (_style == "age") { Date now; minimum = maximum = Duration (now - date).formatCompact ().length (); } else if (_style == "remaining") { Date now; if (date > now) minimum = maximum = Duration (date - now).format ().length (); } else throw format (STRING_COLUMN_BAD_FORMAT, _name, _style); } }
static void colorizeOverdue (Task& task, const Color& base, Color& c) { if (task.has ("due")) { Task::status status = task.getStatus (); if (status != Task::completed && status != Task::deleted && getDueState (task.get ("due")) == 3) c.blend (base); } }
//////////////////////////////////////////////////////////////////////////////// // Set the minimum and maximum widths for the value. void ColumnMask::measure (Task& task, unsigned int& minimum, unsigned int& maximum) { minimum = maximum = 0; if (task.has (_name)) { minimum = maximum = task.get ("mask").length (); if (_style != "default") throw format (STRING_COLUMN_BAD_FORMAT, _name, _style); } }
void ColumnStart::render ( std::vector <std::string>& lines, Task& task, int width, Color& color) { if (task.has (_name)) { if (_style == "active") { if (! task.has ("end")) lines.push_back ( color.colorize ( rightJustify ( context.config.get ("active.indicator"), width))); } else ColumnDate::render (lines, task, width, color); } }
static void colorizeOverdue (Task& task, const Color& base, Color& c, bool merge) { if (task.has ("due")) { Task::status status = task.getStatus (); if (status != Task::completed && status != Task::deleted && task.getDateState ("due") == Task::dateBeforeToday) applyColor (base, c, merge); } }
//////////////////////////////////////////////////////////////////////////////// // Set the minimum and maximum widths for the value. void ColumnParent::measure (Task& task, unsigned int& minimum, unsigned int& maximum) { minimum = maximum = 0; if (task.has (_name)) { if (_style == "default" || _style == "long") minimum = maximum = 36; else if (_style == "short") minimum = maximum = 8; else throw format (STRING_COLUMN_BAD_FORMAT, _name, _style); } }
static void colorizeDueToday (Task& task, const Color& base, Color& c, bool merge) { if (task.has ("due")) { Task::status status = task.getStatus (); Task::dateState dateState = task.getDateState ("due"); if (status != Task::completed && status != Task::deleted && (dateState == Task::dateLaterToday || dateState == Task::dateEarlierToday)) applyColor (base, c, merge); } }
//////////////////////////////////////////////////////////////////////////////// // Set the minimum and maximum widths for the value. void ColumnTags::measure (Task& task, unsigned int& minimum, unsigned int& maximum) { minimum = maximum = 0; if (task.has (_name)) { if (_style == "indicator") { if (task.has ("tags")) minimum = maximum = utf8_width (context.config.get ("tag.indicator")); else minimum = maximum = 0; } else if (_style == "count") { minimum = maximum = 3; } else if (_style == "default" || _style == "list") { std::string tags = task.get (_name); maximum = utf8_width (tags); if (maximum) { std::vector <std::string> all; split (all, tags, ','); for (auto& i : all) { unsigned int length = utf8_width (i); if (length > minimum) minimum = length; } } } else throw format (STRING_COLUMN_BAD_FORMAT, _name, _style); } }
//////////////////////////////////////////////////////////////////////////////// // Set the minimum and maximum widths for the value. void ColumnRecur::measure (Task& task, unsigned int& minimum, unsigned int& maximum) { minimum = maximum = 0; if (task.has (_name)) { if (_style == "default" || _style == "duration") { minimum = maximum = ISO8601p (task.get ("recur")).format ().length (); } else if (_style == "indicator") { if (task.has ("recur")) minimum = maximum = utf8_width (context.config.get ("recurrence.indicator")); else minimum = maximum = 0; } else throw format (STRING_COLUMN_BAD_FORMAT, _name, _style); } }
//////////////////////////////////////////////////////////////////////////////// // Set the minimum and maximum widths for the value. void ColumnRecur::measure (Task& task, int& minimum, int& maximum) { if (_style == "default" || _style == "duration") { minimum = maximum = Duration (task.get ("recur")).formatCompact ().length (); } else if (_style == "indicator") { if (task.has (_name)) minimum = maximum = context.config.get ("recurrence.indicator").length (); } else throw format (STRING_COLUMN_BAD_FORMAT, _name, _style); }
//////////////////////////////////////////////////////////////////////////////// // Set the minimum and maximum widths for the value. void ColumnDue::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 ColumnTags::render ( std::vector <std::string>& lines, Task& task, int width, Color& color) { if (task.has (_name)) { std::string tags = task.get (_name); if (_style == "default" || _style == "list") { std::vector <std::string> allTags; split (allTags, tags, ','); if (allTags.size () > 1) { std::sort (allTags.begin (), allTags.end ()); join (tags, " ", allTags); } std::vector <std::string> all; wrapText (all, tags, width, _hyphenate); for (auto& i : all) lines.push_back (color.colorize (leftJustify (i, width))); } else if (_style == "indicator") { lines.push_back ( color.colorize ( rightJustify (context.config.get ("tag.indicator"), width))); } else if (_style == "count") { std::vector <std::string> all; split (all, tags, ','); lines.push_back ( color.colorize ( rightJustify ("[" + format ((int)all.size ()) + "]", width))); } } }
void ColumnDue::render ( std::vector <std::string>& 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); } }
void ColumnRecur::render ( std::vector <std::string>& lines, Task& task, int width, Color& color) { if (_style == "default" || _style == "duration") { lines.push_back ( color.colorize ( rightJustify ( Duration (task.get ("recur")).formatCompact (), width))); } else if (_style == "indicator") { if (task.has (_name)) lines.push_back ( color.colorize ( rightJustify (context.config.get ("recurrence.indicator"), width))); } }
static void colorizeUDA (Task& task, const std::string& rule, const Color& base, Color& c) { if (task.has (rule.substr (10))) c.blend (base); }
void ColumnDate::render ( std::vector <std::string>& lines, Task& task, int width, Color& color) { if (task.has (_name)) { Date date (task.get_date (_name)); if (_style == "default" || _style == "formatted") { // Determine the output date format, which uses a hierarchy of definitions. // rc.report.<report>.dateformat // rc.dateformat.report // rc.dateformat std::string format = context.config.get ("report." + _report + ".dateformat"); if (format == "") format = context.config.get ("dateformat.report"); if (format == "") format = context.config.get ("dateformat"); lines.push_back ( color.colorize ( leftJustify ( date.toString (format), width))); } else if (_style == "countdown") { Date now; lines.push_back ( color.colorize ( rightJustify ( Duration (now - date).format (), width))); } else if (_style == "julian") { lines.push_back ( color.colorize ( rightJustify ( format (date.toJulian (), 13, 12), width))); } else if (_style == "epoch") { lines.push_back ( color.colorize ( rightJustify ( date.toEpochString (), width))); } else if (_style == "iso") { lines.push_back ( color.colorize ( leftJustify ( date.toISO (), width))); } else if (_style == "age") { Date now; lines.push_back ( color.colorize ( leftJustify ( Duration (now - date).formatCompact (), width))); } else if (_style == "remaining") { Date now; if (date > now) lines.push_back ( color.colorize ( rightJustify ( Duration (date - now).format (), width))); else lines.push_back (""); } } }
int CmdDuplicate::execute (std::string&) { int rc = 0; int count = 0; // Apply filter. Filter filter; std::vector <Task> filtered; filter.subset (filtered); if (filtered.size () == 0) { context.footnote (STRING_FEEDBACK_NO_TASKS_SP); return 1; } // Accumulated project change notifications. std::map <std::string, std::string> projectChanges; for (auto& task : filtered) { // Duplicate the specified task. Task dup (task); dup.id = 0; // Reset, and TDB2::add will set. dup.set ("uuid", uuid ()); // Needs a new UUID. dup.remove ("start"); // Does not inherit start date. dup.remove ("end"); // Does not inherit end date. dup.remove ("entry"); // Does not inherit entry date. // When duplicating a child task, downgrade it to a plain task. if (dup.has ("parent")) { dup.remove ("parent"); dup.remove ("recur"); dup.remove ("until"); dup.remove ("imask"); std::cout << format (STRING_CMD_DUPLICATE_NON_REC, task.id) << "\n"; } // When duplicating a parent task, create a new parent task. else if (dup.getStatus () == Task::recurring) { dup.remove ("mask"); std::cout << format (STRING_CMD_DUPLICATE_REC, task.id) << "\n"; } dup.setStatus (Task::pending); // Does not inherit status. // Must occur after Task::recurring check. dup.modify (Task::modAnnotate); if (permission (format (STRING_CMD_DUPLICATE_CONFIRM, task.id, task.get ("description")), filtered.size ())) { context.tdb2.add (dup); ++count; feedback_affected (STRING_CMD_DUPLICATE_TASK, task); if (context.verbose ("new-id")) std::cout << format (STRING_CMD_ADD_FEEDBACK, dup.id) + "\n"; else if (context.verbose ("new-uuid")) std::cout << format (STRING_CMD_ADD_FEEDBACK, dup.get ("uuid")) + "\n"; if (context.verbose ("project")) projectChanges[task.get ("project")] = onProjectChange (task); } else { std::cout << STRING_CMD_DUPLICATE_NO << "\n"; rc = 1; if (_permission_quit) break; } } // Now list the project changes. for (auto& change : projectChanges) if (change.first != "") context.footnote (change.second); feedback_affected (count == 1 ? STRING_CMD_DUPLICATE_1 : STRING_CMD_DUPLICATE_N, count); return rc; }
int CmdModify::execute (std::string& output) { int rc = 0; int count = 0; // Apply filter. Filter filter; std::vector <Task> filtered; filter.subset (filtered); if (filtered.size () == 0) { context.footnote (STRING_FEEDBACK_NO_TASKS_SP); return 1; } // TODO Complain when no modifications are specified. // Accumulated project change notifications. std::map <std::string, std::string> projectChanges; std::vector <Task>::iterator task; for (task = filtered.begin (); task != filtered.end (); ++task) { Task before (*task); task->modify (Task::modReplace); if (taskDiff (before, *task)) { // Perform some logical consistency checks. if (task->has ("recur") && !task->has ("due") && !before.has ("due")) throw std::string (STRING_CMD_MODIFY_NO_DUE); if (before.has ("recur") && before.has ("due") && (!task->has ("due") || task->get ("due") == "")) throw std::string (STRING_CMD_MODIFY_REM_DUE); if (before.has ("recur") && (!task->has ("recur") || task->get ("recur") == "")) throw std::string (STRING_CMD_MODIFY_REC_ALWAYS); // Delete the specified task. std::string question; if (task->id != 0) question = format (STRING_CMD_MODIFY_CONFIRM, task->id, task->get ("description")); else question = format (STRING_CMD_MODIFY_CONFIRM, task->get ("uuid"), task->get ("description")); if (permission (*task, taskDifferences (before, *task) + question, filtered.size ())) { updateRecurrenceMask (*task); dependencyChainOnModify (before, *task); ++count; feedback_affected (STRING_CMD_MODIFY_TASK, *task); feedback_unblocked (*task); context.tdb2.modify (*task); if (context.verbose ("project")) projectChanges[task->get ("project")] = onProjectChange (before, *task); // Task potentially has siblings - modify them. if (task->has ("parent")) { if ((context.config.get ("recurrence.confirmation") == "prompt" && confirm (STRING_CMD_MODIFY_RECUR)) || context.config.getBoolean ("recurrence.confirmation")) { std::vector <Task> siblings = context.tdb2.siblings (*task); std::vector <Task>::iterator sibling; for (sibling = siblings.begin (); sibling != siblings.end (); ++sibling) { Task alternate (*sibling); sibling->modify (Task::modReplace); updateRecurrenceMask (*sibling); dependencyChainOnModify (alternate, *sibling); ++count; feedback_affected (STRING_CMD_MODIFY_TASK_R, *sibling); feedback_unblocked (*sibling); context.tdb2.modify (*sibling); if (context.verbose ("project")) projectChanges[sibling->get ("project")] = onProjectChange (alternate, *sibling); } // Modify the parent Task parent; context.tdb2.get (task->get ("parent"), parent); parent.modify (Task::modReplace); context.tdb2.modify (parent); } } // Task potentially has child tasks - modify them. else if (task->get ("status") == "recurring") { std::vector <Task> children = context.tdb2.children (*task); if (children.size () && (! context.config.getBoolean ("recurrence.confirmation") || confirm (STRING_CMD_MODIFY_RECUR))) { std::vector <Task>::iterator child; for (child = children.begin (); child != children.end (); ++child) { Task alternate (*child); child->modify (Task::modReplace); updateRecurrenceMask (*child); context.tdb2.modify (*child); dependencyChainOnModify (alternate, *child); if (context.verbose ("project")) projectChanges[child->get ("project")] = onProjectChange (alternate, *child); ++count; feedback_affected (STRING_CMD_MODIFY_TASK_R, *child); } } } } else { std::cout << STRING_CMD_MODIFY_NO << "\n"; rc = 1; if (_permission_quit) break; } } } // Now list the project changes. std::map <std::string, std::string>::iterator i; for (i = projectChanges.begin (); i != projectChanges.end (); ++i) if (i->first != "") context.footnote (i->second); feedback_affected (count == 1 ? STRING_CMD_MODIFY_1 : STRING_CMD_MODIFY_N, count); return rc; }
static void colorizeRecurring (Task& task, const Color& base, Color& c, bool merge) { if (task.has ("recur")) applyColor (base, c, merge); }
static void colorizeActive (Task& task, const Color& base, Color& c) { if (task.has ("start") && !task.has ("end")) c.blend (base); }
static void colorizeScheduled (Task& task, const Color& base, Color& c) { if (task.has ("scheduled") && Date (task.get_date ("scheduled")) <= now) c.blend (base); }
static void colorizeRecurring (Task& task, const Color& base, Color& c) { if (task.has ("recur")) c.blend (base); }