bool taskDiff (const Task& before, const Task& after) { // Attributes are all there is, so figure the different attribute names // between before and after. std::vector <std::string> beforeAtts; Task::const_iterator att; for (att = before.begin (); att != before.end (); ++att) beforeAtts.push_back (att->first); std::vector <std::string> afterAtts; for (att = after.begin (); att != after.end (); ++att) afterAtts.push_back (att->first); std::vector <std::string> beforeOnly; std::vector <std::string> afterOnly; listDiff (beforeAtts, afterAtts, beforeOnly, afterOnly); if (beforeOnly.size () != afterOnly.size ()) return true; std::vector <std::string>::iterator name; for (name = beforeAtts.begin (); name != beforeAtts.end (); ++name) if (*name != "uuid" && before.get (*name) != after.get (*name)) return true; return false; }
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))); } } }
bool taskDiff (const Task& before, const Task& after) { // Attributes are all there is, so figure the different attribute names // between before and after. std::vector <std::string> beforeAtts; foreach (att, before) beforeAtts.push_back (att->first); std::vector <std::string> afterAtts; foreach (att, after) afterAtts.push_back (att->first); std::vector <std::string> beforeOnly; std::vector <std::string> afterOnly; listDiff (beforeAtts, afterAtts, beforeOnly, afterOnly); if (beforeOnly.size () || afterOnly.size ()) return true; foreach (name, beforeAtts) if (*name != "uuid" && before.get (*name) != after.get (*name)) return true; return false; }
void TDB2::update ( Task& task, const bool add_to_backlog, const bool addition /* = false */) { // Validate to add metadata. task.validate (false); // If the task already exists, it is a modification, else addition. Task original; if (not addition && get (task.get ("uuid"), original)) { // Update only if the tasks differ if (task == original) return; if (add_to_backlog) { // All locally modified tasks are timestamped, implicitly overwriting any // changes the user or hooks tried to apply to the "modified" attribute. task.setAsNow ("modified"); } // Update the task, wherever it is. if (!pending.modify_task (task)) completed.modify_task (task); // time <time> // old <task> // new <task> // --- undo.add_line ("time " + ISO8601d ().toEpochString () + "\n"); undo.add_line ("old " + original.composeF4 () + "\n"); undo.add_line ("new " + task.composeF4 () + "\n"); undo.add_line ("---\n"); } else { // Add new task to either pending or completed. std::string status = task.get ("status"); if (status == "completed" || status == "deleted") completed.add_task (task); else pending.add_task (task); // Add undo data lines: // time <time> // new <task> // --- undo.add_line ("time " + ISO8601d ().toEpochString () + "\n"); undo.add_line ("new " + task.composeF4 () + "\n"); undo.add_line ("---\n"); } // Add task to backlog. if (add_to_backlog) backlog.add_line (task.composeJSON () + "\n"); }
std::string onProjectChange (Task& task1, Task& task2) { if (task1.get ("project") == task2.get ("project")) return onProjectChange (task1); std::string messages = onProjectChange (task1); messages += onProjectChange (task2); return messages; }
//////////////////////////////////////////////////////////////////////////////// // Implements: // Deleting task 123 'This is a test' // // The 'effect' string should contain: // {1} ID // {2} Description void feedback_affected (const std::string& effect, const Task& task) { if (context.verbose ("affected")) { if (task.id) std::cout << format (effect, task.id, task.get ("description")) << "\n"; else std::cout << format (effect, task.get ("uuid"), task.get ("description")) << "\n"; } }
std::string onProjectChange (Task& task1, Task& task2) { if (task1.get ("project") == task2.get ("project")) return onProjectChange (task1, false); std::string messages1 = onProjectChange (task1); std::string messages2 = onProjectChange (task2); if (messages1.length () && messages2.length ()) return messages1 + '\n' + messages2; return messages1 + messages2; }
//////////////////////////////////////////////////////////////////////////////// // Implements: // Deleting task 123 'This is a test' // // The 'effect' string should contain: // {1} ID // {2} Description void feedback_affected (const std::string& effect, const Task& task) { if (context.verbose ("affected") || context.config.getBoolean ("echo.command")) // Deprecated 2.0 { if (task.id) std::cout << format (effect, task.id, task.get ("description")) << "\n"; else std::cout << format (effect, task.get ("uuid"), task.get ("description")) << "\n"; } }
static void colorizeKeyword (Task& task, const std::string& rule, const Color& base, Color& c, bool merge) { // Observe the case sensitivity setting. bool sensitive = context.config.getBoolean ("search.case.sensitive"); // The easiest thing to check is the description, because it is just one // attribute. if (find (task.get ("description"), rule.substr (14), sensitive) != std::string::npos) applyColor (base, c, merge); // Failing the description check, look at all annotations, returning on the // first match. else { for (auto& it : task) { if (it.first.substr (0, 11) == "annotation_" && find (it.second, rule.substr (14), sensitive) != std::string::npos) { applyColor (base, c, merge); return; } } } }
bool TF2::modify_task (const Task& task) { std::string uuid = task.get ("uuid"); if (context.cli2.getCommand () == "import") { // Update map used for faster lookup auto i = _tasks_map.find (uuid); if (i != _tasks_map.end ()) { i->second = task; } } for (auto& i : _tasks) { if (i.get ("uuid") == uuid) { // Modify in-place. i = task; _modified_tasks.push_back (task); _dirty = true; return true; } } return false; }
const std::vector <Task> TDB2::children (Task& task) { std::vector <Task> results; std::string parent = task.get ("uuid"); // 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.get ("parent") == parent) results.push_back (i); } } } return results; }
static void colorizeKeyword (Task& task, const std::string& rule, const Color& base, Color& c) { // Observe the case sensitivity setting. bool sensitive = context.config.getBoolean ("search.case.sensitive"); // The easiest thing to check is the description, because it is just one // attribute. if (find (task.get ("description"), rule.substr (14), sensitive) != std::string::npos) c.blend (base); // Failing the description check, look at all annotations, returning on the // first match. else { Task::iterator it; for (it = task.begin (); it != task.end (); ++it) { if (it->first.substr (0, 11) == "annotation_" && find (it->second, rule.substr (14), sensitive) != std::string::npos) { c.blend (base); return; } } } }
//////////////////////////////////////////////////////////////////////////////// // Set the minimum and maximum widths for the value. void ColumnTags::measure (Task& task, int& minimum, int& maximum) { if (_style == "indicator") minimum = maximum = context.config.get ("tag.indicator").length (); else if (_style == "count") minimum = maximum = 3; else if (_style == "default" || _style == "list") { std::string tags = task.get (_name); minimum = 0; maximum = tags.length (); if (maximum) { std::vector <std::string> all; split (all, tags, ','); std::vector <std::string>::iterator i; for (i = all.begin (); i != all.end (); ++i) if ((int)i->length () > minimum) minimum = i->length () + 1; } } else throw format (STRING_COLUMN_BAD_FORMAT, _name, _style); }
//////////////////////////////////////////////////////////////////////////////// // Set the minimum and maximum widths for the value. void ColumnBg::measure (Task& task, int& minimum, int& maximum) { std::string bg = task.get (_name); minimum = longestWord (bg); maximum = bg.length (); }
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))); } } }
//////////////////////////////////////////////////////////////////////////////// // Returns true if the supplied task adds a cycle to the dependency chain. bool dependencyIsCircular (const Task& task) { std::stack <Task> s; std::vector <std::string> deps_current; std::string task_uuid = task.get ("uuid"); s.push (task); while (!s.empty ()) { Task& current = s.top (); current.getDependencies (deps_current); // This is a basic depth first search that always terminates given the // assumption that any cycles in the dependency graph must have been // introduced by the task that is being checked. // Since any previous cycles would have been prevented by this very // function, this is a reasonable assumption. for (unsigned int i = 0; i < deps_current.size (); i++) { context.tdb2.get (deps_current[i], current); if (task_uuid == current.get ("uuid")) { // Cycle found, initial task reached for the second time! return true; } s.push (current); } s.pop (); } return false; }
std::string onExpiration (Task& task) { std::stringstream msg; if (context.verbose ("affected")) msg << format (STRING_FEEDBACK_EXPIRED, task.id, task.get ("description")); return msg.str (); }
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"))); }
virtual std::size_t push(Task task) { // Precondition verification: assert(nullptr != task.get()); assert(!m_cancelled); // Tries to push the task in the form of message to the input queue: return m_input_queue->push(task); }
//////////////////////////////////////////////////////////////////////////////// // 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); } }
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); } }
void dependencyGetBlocked (const Task& task, std::vector <Task>& blocked) { std::string uuid = task.get ("uuid"); const std::vector <Task>& all = context.tdb2.pending.get_tasks (); std::vector <Task>::const_iterator it; for (it = all.begin (); it != all.end (); ++it) if ((it->getStatus () == Task::pending || it->getStatus () == Task::waiting) && it->has ("depends") && it->get ("depends").find (uuid) != std::string::npos) blocked.push_back (*it); }
static void colorizeProject (Task& task, const std::string& rule, const Color& base, Color& c) { // Observe the case sensitivity setting. bool sensitive = context.config.getBoolean ("search.case.sensitive"); std::string project = task.get ("project"); std::string rule_trunc = rule.substr (14); // Match project names leftmost. if (rule_trunc.length () <= project.length ()) if (compare (rule_trunc, project.substr (0, rule_trunc.length ()), sensitive)) c.blend (base); }
std::string CmdEdit::formatDuration ( Task& task, const std::string& attribute) { std::string value = task.get (attribute); if (value.length ()) { Duration dur (value); value = dur.formatSeconds (); } return value; }
//////////////////////////////////////////////////////////////////////////////// // The uuid and id attributes must be exempt from comparison. bool Task::operator== (const Task& other) { if (size () != other.size ()) return false; Task::iterator i; for (i = this->begin (); i != this->end (); ++i) if (i->first != "uuid" && i->second != other.get (i->first)) return false; return true; }
//////////////////////////////////////////////////////////////////////////////// // 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); } }
std::string CmdEdit::formatDate ( Task& task, const std::string& attribute, const std::string& dateformat) { std::string value = task.get (attribute); if (value.length ()) { Date dt (value); value = dt.toString (dateformat); } return value; }
void dependencyGetBlocking (const Task& task, std::vector <Task>& blocking) { std::string depends = task.get ("depends"); if (depends != "") { const std::vector <Task>& all = context.tdb2.pending.get_tasks (); std::vector <Task>::const_iterator it; for (it = all.begin (); it != all.end (); ++it) if ((it->getStatus () == Task::pending || it->getStatus () == Task::waiting) && depends.find (it->get ("uuid")) != std::string::npos) blocking.push_back (*it); } }
void TF2::add_task (Task& task) { _tasks.push_back (task); // For subsequent queries _added_tasks.push_back (task); // For commit/synch // For faster lookup if (context.cli2.getCommand () == "import") _tasks_map.insert (std::pair<std::string, Task> (task.get("uuid"), task)); Task::status status = task.getStatus (); if (task.id == 0 && (status == Task::pending || status == Task::recurring || status == Task::waiting)) { task.id = context.tdb2.next_id (); } _I2U[task.id] = task.get ("uuid"); _U2I[task.get ("uuid")] = task.id; _dirty = true; }
//////////////////////////////////////////////////////////////////////////////// // Set the minimum and maximum widths for the value. void ColumnRecur::measure (Task& task, unsigned int& minimum, unsigned 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); }