From 40dc0490e11369a9bd725f06f21f7fea03c60f37 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Mon, 26 May 2008 21:39:10 -0400 Subject: [PATCH] - Removed unused variable only detectable on Ubuntu 8. - Removed misplaced files. --- Date.cpp | 304 ------ parse.cpp | 379 ------- src/task.cpp | 11 - task.cpp | 2760 -------------------------------------------------- 4 files changed, 3454 deletions(-) delete mode 100644 Date.cpp delete mode 100644 parse.cpp delete mode 100644 task.cpp diff --git a/Date.cpp b/Date.cpp deleted file mode 100644 index 9bd67a4c8..000000000 --- a/Date.cpp +++ /dev/null @@ -1,304 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Copyright 2005 - 2008, Paul Beckingham. All rights reserved. -// -// -//////////////////////////////////////////////////////////////////////////////// -#include -#include -#include -#include "task.h" -#include "Date.h" - -//////////////////////////////////////////////////////////////////////////////// -// Defaults to "now". -Date::Date () -{ - mT = time (NULL); -} - -//////////////////////////////////////////////////////////////////////////////// -Date::Date (const time_t t) -{ - mT = t; -} - -//////////////////////////////////////////////////////////////////////////////// -Date::Date (const int m, const int d, const int y) -{ - // Error if not valid. - struct tm t = {0}; - t.tm_mday = d; - t.tm_mon = m - 1; - t.tm_year = y - 1900; - - mT = mktime (&t); -} - -//////////////////////////////////////////////////////////////////////////////// -Date::Date (const std::string& mdy) -{ - size_t firstSlash = mdy.find ("/"); - size_t secondSlash = mdy.find ("/", firstSlash + 1); - if (firstSlash != std::string::npos && - secondSlash != std::string::npos) - { - int m = ::atoi (mdy.substr (0, firstSlash ).c_str ()); - int d = ::atoi (mdy.substr (firstSlash + 1, secondSlash - firstSlash).c_str ()); - int y = ::atoi (mdy.substr (secondSlash + 1, std::string::npos ).c_str ()); - if (!valid (m, d, y)) - throw std::string ("\"") + mdy + "\" is not a valid date."; - - // Duplicate Date::Date (const int, const int, const int); - struct tm t = {0}; - t.tm_mday = d; - t.tm_mon = m - 1; - t.tm_year = y - 1900; - - mT = mktime (&t); - } - else - throw std::string ("\"") + mdy + "\" is not a valid date."; -} - -//////////////////////////////////////////////////////////////////////////////// -Date::Date (const Date& rhs) -{ - mT = rhs.mT; -} - -//////////////////////////////////////////////////////////////////////////////// -Date::~Date () -{ -} - -//////////////////////////////////////////////////////////////////////////////// -time_t Date::toEpoch () -{ - return mT; -} - -//////////////////////////////////////////////////////////////////////////////// -void Date::toEpoch (time_t& epoch) -{ - epoch = mT; -} - -//////////////////////////////////////////////////////////////////////////////// -void Date::toMDY (int& m, int& d, int& y) -{ - struct tm* t = localtime (&mT); - - m = t->tm_mon + 1; - d = t->tm_mday; - y = t->tm_year + 1900; -} - -//////////////////////////////////////////////////////////////////////////////// -void Date::toString (std::string& output) -{ - output = toString (); -} - -//////////////////////////////////////////////////////////////////////////////// -std::string Date::toString (void) -{ - int m, d, y; - toMDY (m, d, y); - - char formatted [11]; - sprintf (formatted, "%d/%d/%d", m, d, y); - return std::string (formatted); -} - -//////////////////////////////////////////////////////////////////////////////// -bool Date::valid (const int m, const int d, const int y) -{ - // Check that the year is valid. - if (y < 0) - return false; - - // Check that the month is valid. - if (m < 1 || m > 12) - return false; - - // Finally check that the days fall within the acceptable range for this - // month, and whether or not this is a leap year. - if (d < 1 || d > Date::daysInMonth (m, y)) - return false; - - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -bool Date::leapYear (int year) -{ - bool ly = false; - - if (!(year % 4)) ly = true; - else if (!(year % 400)) ly = true; - else if (!(year % 100)) ly = false; - - return ly; -} - -//////////////////////////////////////////////////////////////////////////////// -int Date::daysInMonth (int month, int year) -{ - static int days[2][12] = - { - {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, - {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} - }; - - return days[Date::leapYear (year) ? 1 : 0][month - 1]; -} - -//////////////////////////////////////////////////////////////////////////////// -std::string Date::monthName (int month) -{ - static const char* months[12] = - { - "January", - "February", - "March", - "April", - "May", - "June", - "July", - "August", - "September", - "October", - "November", - "December", - }; - - assert (month > 0); - assert (month <= 12); - return months[month -1]; -} - -//////////////////////////////////////////////////////////////////////////////// -void Date::dayName (int dow, std::string& name) -{ - static const char* days[7] = - { - "Sunday", - "Monday", - "Tuesday", - "Wednesday", - "Thursday", - "Friday", - "Saturday", - }; - - name = days[dow]; -} - -//////////////////////////////////////////////////////////////////////////////// -std::string Date::dayName (int dow) -{ - static const char* days[7] = - { - "Sunday", - "Monday", - "Tuesday", - "Wednesday", - "Thursday", - "Friday", - "Saturday", - }; - - return days[dow]; -} - -//////////////////////////////////////////////////////////////////////////////// -int Date::dayOfWeek () -{ - struct tm* t = localtime (&mT); - return t->tm_wday; -} - -//////////////////////////////////////////////////////////////////////////////// -int Date::month () -{ - struct tm* t = localtime (&mT); - return t->tm_mon + 1; -} - -//////////////////////////////////////////////////////////////////////////////// -int Date::day () -{ - struct tm* t = localtime (&mT); - return t->tm_mday; -} - -//////////////////////////////////////////////////////////////////////////////// -int Date::year () -{ - struct tm* t = localtime (&mT); - return t->tm_year + 1900; -} - -//////////////////////////////////////////////////////////////////////////////// -bool Date::operator== (const Date& rhs) -{ - return rhs.mT == mT; -} - -//////////////////////////////////////////////////////////////////////////////// -bool Date::operator!= (const Date& rhs) -{ - return rhs.mT != mT; -} - -//////////////////////////////////////////////////////////////////////////////// -bool Date::operator< (const Date& rhs) -{ - return mT < rhs.mT; -} - -//////////////////////////////////////////////////////////////////////////////// -bool Date::operator> (const Date& rhs) -{ - return mT > rhs.mT; -} - -//////////////////////////////////////////////////////////////////////////////// -bool Date::operator<= (const Date& rhs) -{ - return mT <= rhs.mT; -} - -//////////////////////////////////////////////////////////////////////////////// -bool Date::operator>= (const Date& rhs) -{ - return mT >= rhs.mT; -} - -//////////////////////////////////////////////////////////////////////////////// -Date Date::operator+ (const int delta) -{ - return Date::Date (mT + delta); -} - -//////////////////////////////////////////////////////////////////////////////// -Date& Date::operator+= (const int delta) -{ - mT += (time_t) delta; - return *this; -} - -//////////////////////////////////////////////////////////////////////////////// -Date& Date::operator-= (const int delta) -{ - mT -= (time_t) delta; - return *this; -} - -//////////////////////////////////////////////////////////////////////////////// -time_t Date::operator- (const Date& rhs) -{ - return mT - rhs.mT; -} - -//////////////////////////////////////////////////////////////////////////////// diff --git a/parse.cpp b/parse.cpp deleted file mode 100644 index 71b9e8481..000000000 --- a/parse.cpp +++ /dev/null @@ -1,379 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Copyright 2006 - 2008, Paul Beckingham. All rights reserved. -// -// -//////////////////////////////////////////////////////////////////////////////// -#include -#include -#include -#include - -#include "Date.h" -#include "task.h" -#include "T.h" - -//////////////////////////////////////////////////////////////////////////////// -static const char* colors[] = -{ - "bold", - "underline", - "bold_underline", - - "black", - "red", - "green", - "yellow", - "blue", - "magenta", - "cyan", - "white", - - "bold_black", - "bold_red", - "bold_green", - "bold_yellow", - "bold_blue", - "bold_magenta", - "bold_cyan", - "bold_white", - - "underline_black", - "underline_red", - "underline_green", - "underline_yellow", - "underline_blue", - "underline_magenta", - "underline_cyan", - "underline_white", - - "bold_underline_black", - "bold_underline_red", - "bold_underline_green", - "bold_underline_yellow", - "bold_underline_blue", - "bold_underline_magenta", - "bold_underline_cyan", - "bold_underline_white", - - "on_black", - "on_red", - "on_green", - "on_yellow", - "on_blue", - "on_magenta", - "on_cyan", - "on_white", - - "on_bright_black", - "on_bright_red", - "on_bright_green", - "on_bright_yellow", - "on_bright_blue", - "on_bright_magenta", - "on_bright_cyan", - "on_bright_white", - "", -}; - -static const char* attributes[] = -{ - "project", - "priority", - "fg", - "bg", - "due", - "entry", - "start", - "end", - "", -}; - -static const char* commands[] = -{ - "active", - "add", - "calendar", - "colors", - "completed", - "delete", - "done", - "export", - "history", - "info", - "list", - "long", - "ls", - "next", - "overdue", - "projects", - "start", - "stats", - "summary", - "tags", - "usage", - "version", - "", -}; - -void guess (const std::string& type, const char** list, std::string& candidate) -{ - std::vector options; - for (int i = 0; list[i][0]; ++i) - options.push_back (list[i]); - - std::vector matches; - autoComplete (candidate, options, matches); - if (1 == matches.size ()) - candidate = matches[0]; - - else if (0 == matches.size ()) - throw std::string ("Unrecognized ") + type + " '" + candidate + "'"; - - else - { - std::string error = "Ambiguous "; - error += type; - error += " '"; - error += candidate; - error += "' - could be either of "; - for (size_t i = 0; i < matches.size (); ++i) - { - if (i) - error += ", "; - error += matches[i]; - } - - throw error; - } -} - -//////////////////////////////////////////////////////////////////////////////// -static bool isCommand (const std::string& candidate) -{ - std::vector options; - for (int i = 0; commands[i][0]; ++i) - options.push_back (commands[i]); - - std::vector matches; - autoComplete (candidate, options, matches); - if (0 == matches.size ()) - return false; - - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -bool validDate (std::string& date) -{ - size_t firstSlash = date.find ("/"); - size_t secondSlash = date.find ("/", firstSlash + 1); - if (firstSlash != std::string::npos && - secondSlash != std::string::npos) - { - int m = ::atoi (date.substr (0, firstSlash ).c_str ()); - int d = ::atoi (date.substr (firstSlash + 1, secondSlash - firstSlash).c_str ()); - int y = ::atoi (date.substr (secondSlash + 1, std::string::npos ).c_str ()); - if (!Date::valid (m, d, y)) - throw std::string ("\"") + date + "\" is not a valid date."; - - // Convert to epoch form. - Date dt (m, d, y); - time_t t; - dt.toEpoch (t); - char converted[12]; - sprintf (converted, "%u", (unsigned int) t); - date = converted; - } - else - throw std::string ("Badly formed date - use the MM/DD/YYYY format"); - - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -static bool validPriority (std::string& input) -{ - if (input != "H" && - input != "M" && - input != "L" && - input != "") - throw std::string ("\"") + - input + - "\" is not a valid priority. Use H, M, L or leave blank."; - - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -static bool validAttribute (std::string& name, std::string& value) -{ - guess ("attribute", attributes, name); - - if ((name == "fg" || name == "bg") && value != "") - guess ("color", colors, value); - - else if (name == "due" && value != "") - validDate (value); - - else if (name == "priority") - { - for (std::string::iterator i = value.begin (); i != value.end (); ++i) - *i = ::toupper (*i); - - return validPriority (value); - } - - else if (name == "entry" || - name == "start" || - name == "end") - throw std::string ("\"") + - name + - "\" is not an attribute you may modify directly."; - - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -static bool validId (const std::string& input) -{ - for (size_t i = 0; i < input.length (); ++i) - if (!::isdigit (input[i])) - return false; - - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -static bool validTag (std::string& input) -{ - if ((input[0] == '-' || input[0] == '+') && - input.length () > 1) - return true; - - return false; -} - -//////////////////////////////////////////////////////////////////////////////// -static bool validDescription (const std::string& input) -{ - if (input.length () > 0) - return true; - - return false; -} - -//////////////////////////////////////////////////////////////////////////////// -static bool validCommand (std::string& input) -{ - guess ("command", commands, input); - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -static bool validSubstitution ( - std::string& input, - std::string& from, - std::string& to) -{ - size_t first = input.find ('/'); - if (first != std::string::npos) - { - size_t second = input.find ('/', first + 1); - if (second != std::string::npos) - { - size_t third = input.find ('/', second + 1); - if (third != std::string::npos) - { - if (first == 0 && - first < second && - second < third && - third == input.length () - 1) - { - from = input.substr (first + 1, second - first - 1); - to = input.substr (second + 1, third - second - 1); - return true; - } - } - } - } - - return false; -} - -//////////////////////////////////////////////////////////////////////////////// -// Token Distinguishing characteristic -// ------- ----------------------------- -// command first positional -// id \d+ -// description default, accumulate -// substitution /\w+/\w*/ -// tags [-+]\w+ -// attributes \w+:.+ -// -void parse ( - std::vector & args, - std::string& command, - T& task) -{ - command = ""; - - std::string descCandidate = ""; - for (size_t i = 0; i < args.size (); ++i) - { - std::string arg (args[i]); - size_t colon; // Pointer to colon in argument. - std::string from; - std::string to; - - // An id is the first argument found that contains all digits. - if (command != "add" && // "add" doesn't require an ID - task.getId () == 0 && - validId (arg)) - task.setId (::atoi (arg.c_str ())); - - // Tags begin with + or - and contain arbitrary text. - else if (validTag (arg)) - { - if (arg[0] == '+') - task.addTag (arg.substr (1, std::string::npos)); - else if (arg[0] == '-') - task.addRemoveTag (arg.substr (1, std::string::npos)); - } - - // Attributes contain a constant string followed by a colon, followed by a - // value. - else if ((colon = arg.find (":")) != std::string::npos) - { - std::string name = arg.substr (0, colon); - std::string value = arg.substr (colon + 1, std::string::npos); - - if (validAttribute (name, value)) - task.setAttribute (name, value); - } - - // Substitution of description text. - else if (validSubstitution (arg, from, to)) - { - task.setSubstitution (from, to); - } - - // Command. - else if (command == "") - { - if (!isCommand (arg)) - descCandidate += std::string (arg) + " "; - else if (validCommand (arg)) - command = arg; - } - - // Anything else is just considered description. - else - descCandidate += std::string (arg) + " "; - } - - if (validDescription (descCandidate)) - task.setDescription (descCandidate); -} - -//////////////////////////////////////////////////////////////////////////////// - diff --git a/src/task.cpp b/src/task.cpp index 12bdf31dd..6b6b8b4a0 100644 --- a/src/task.cpp +++ b/src/task.cpp @@ -2144,17 +2144,6 @@ void handleReportOverdue (const TDB& tdb, T& task, Config& conf) //////////////////////////////////////////////////////////////////////////////// void handleReportStats (const TDB& tdb, T& task, Config& conf) { - // Determine window size, and set table accordingly. - int width = 80; -#ifdef HAVE_LIBNCURSES - if (conf.get ("curses", true)) - { - WINDOW* w = initscr (); - width = w->_maxx + 1; - endwin (); - } -#endif - // Get all the tasks. std::vector tasks; tdb.allT (tasks); diff --git a/task.cpp b/task.cpp deleted file mode 100644 index 38d60fe57..000000000 --- a/task.cpp +++ /dev/null @@ -1,2760 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Copyright 2006 - 2008, Paul Beckingham. -// All rights reserved. -// -// -//////////////////////////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include -#include -#include - -#include "Config.h" -#include "Date.h" -#include "Table.h" -#include "stlmacros.h" -#include "TDB.h" -#include "T.h" -#include "task.h" - -#ifdef HAVE_LIBNCURSES -#include -#endif - -//////////////////////////////////////////////////////////////////////////////// -void usage (Config& conf) -{ - Table table; - int width = 80; -#ifdef HAVE_LIBNCURSES - if (conf.get ("curses", true)) - { - WINDOW* w = initscr (); - width = w->_maxx + 1; - endwin (); - } -#endif - - table.addColumn (" "); - table.addColumn (" "); - table.addColumn (" "); - - table.setColumnJustification (0, Table::left); - table.setColumnJustification (1, Table::left); - table.setColumnJustification (2, Table::left); - - table.setColumnWidth (0, Table::minimum); - table.setColumnWidth (1, Table::minimum); - table.setColumnWidth (2, Table::flexible); - table.setTableWidth (width); - - int row = table.addRow (); - table.addCell (row, 0, "Usage:"); - table.addCell (row, 1, "task"); - - row = table.addRow (); - table.addCell (row, 1, "task add [tags] [attrs] desc..."); - table.addCell (row, 2, "Adds a new task"); - - row = table.addRow (); - table.addCell (row, 1, "task list [tags] [attrs] desc..."); - table.addCell (row, 2, "Lists all tasks matching the specified criteria"); - - row = table.addRow (); - table.addCell (row, 1, "task long [tags] [attrs] desc..."); - table.addCell (row, 2, "Lists all task, all data, matching the specified criteria"); - - row = table.addRow (); - table.addCell (row, 1, "task ls [tags] [attrs] desc..."); - table.addCell (row, 2, "Minimal listing of all tasks matching the specified criteria"); - - row = table.addRow (); - table.addCell (row, 1, "task completed [tags] [attrs] desc..."); - table.addCell (row, 2, "Chronological listing of all completed tasks matching the specified criteria"); - - row = table.addRow (); - table.addCell (row, 1, "task ID [tags] [attrs] [desc...]"); - table.addCell (row, 2, "Modifies the existing task with provided arguments"); - - row = table.addRow (); - table.addCell (row, 1, "task ID /from/to/"); - table.addCell (row, 2, "Perform the substitution on the desc, for fixing mistakes"); - - row = table.addRow (); - table.addCell (row, 1, "task delete ID"); - table.addCell (row, 2, "Deletes the specified task"); - - row = table.addRow (); - table.addCell (row, 1, "task info ID"); - table.addCell (row, 2, "Shows all data, metadata for specified task"); - - row = table.addRow (); - table.addCell (row, 1, "task start ID"); - table.addCell (row, 2, "Marks specified task as started, starts the clock ticking"); - - row = table.addRow (); - table.addCell (row, 1, "task done ID"); - table.addCell (row, 2, "Marks the specified task as completed"); - - row = table.addRow (); - table.addCell (row, 1, "task projects"); - table.addCell (row, 2, "Shows a list of all project names used, and how many tasks are in each"); - - row = table.addRow (); - table.addCell (row, 1, "task tags"); - table.addCell (row, 2, "Shows a list of all tags used"); - - row = table.addRow (); - table.addCell (row, 1, "task summary"); - table.addCell (row, 2, "Shows a report of task status by project"); - - row = table.addRow (); - table.addCell (row, 1, "task history"); - table.addCell (row, 2, "Shows a report of task history, by month"); - - row = table.addRow (); - table.addCell (row, 1, "task next"); - table.addCell (row, 2, "Shows the most important tasks for each project"); - - row = table.addRow (); - table.addCell (row, 1, "task calendar"); - table.addCell (row, 2, "Shows a monthly calendar, with due tasks marked"); - - row = table.addRow (); - table.addCell (row, 1, "task active"); - table.addCell (row, 2, "Shows all task that are started, but not completed"); - - row = table.addRow (); - table.addCell (row, 1, "task overdue"); - table.addCell (row, 2, "Shows all incomplete tasks that are beyond their due date"); - - row = table.addRow (); - table.addCell (row, 1, "task stats"); - table.addCell (row, 2, "Shows task database statistics"); - - row = table.addRow (); - table.addCell (row, 1, "task usage"); - table.addCell (row, 2, "Shows task command usage frequency"); - - row = table.addRow (); - table.addCell (row, 1, "task export"); - table.addCell (row, 2, "Exports all tasks as a CSV file"); - - row = table.addRow (); - table.addCell (row, 1, "task color"); - table.addCell (row, 2, "Displays all possible colors"); - - row = table.addRow (); - table.addCell (row, 1, "task version"); - table.addCell (row, 2, "Shows the task version number"); - - std::cout << table.render () - << std::endl; - - std::cout - << "ID is the numeric identifier displayed by the 'task list' command" << "\n" - << "\n" - << "Tags are arbitrary words, any quantity:" << "\n" - << " +tag The + means add the tag" << "\n" - << " -tag The - means remove the tag" << "\n" - << "\n" - << "Attributes are:" << "\n" - << " project: Project name" << "\n" - << " priority: Priority" << "\n" - << " due: Due date" << "\n" - << " fg: Foreground color" << "\n" - << " bg: Background color" << "\n" - << "\n" - << "Any command or attribute name may be abbreviated if still unique:" << "\n" - << " task list project:Home" << "\n" - << " task li pro:Home" << "\n" - << "\n" - << "Some task descriptions need to be escaped because of the shell:" << "\n" - << " task add \"quoted ' quote\"" << "\n" - << " task add escaped \\' quote" << "\n" - << "\n" - << "Many characters have special meaning to the shell, including:" << "\n" - << " $ ! ' \" ( ) ; \\ ` * ? { } [ ] < > | & % # ~" << "\n" - << std::endl; -} - -//////////////////////////////////////////////////////////////////////////////// -int main (int argc, char** argv) -{ -// TODO Find out what this is, and either promote it to live code, or remove it. -// std::set_terminate (__gnu_cxx::__verbose_terminate_handler); - - try - { - // Load the config file from the home directory. If the file cannot be - // found, offer to create a sample one. - Config conf; - struct passwd* pw = getpwuid (getuid ()); - if (!pw) - throw std::string ("Could not read home directory from passwd file."); - - std::string home = pw->pw_dir; - home += "/.taskrc"; - if (!conf.load (home)) - conf.createDefault (home); - - TDB tdb; - tdb.dataDirectory (conf.get ("data.location")); - - // Log commands, if desired. - if (conf.get ("command.logging") == "on") - tdb.logCommand (argc, argv); - - // Parse the command line. - std::vector args; - for (int i = 1; i < argc; ++i) - args.push_back (argv[i]); - - std::string command; - T task; - parse (args, command, task); - - if (command == "add") handleAdd (tdb, task, conf); - else if (command == "projects") handleProjects (tdb, task, conf); - else if (command == "tags") handleTags (tdb, task, conf); - else if (command == "list") handleList (tdb, task, conf); - else if (command == "info") handleInfo (tdb, task, conf); - else if (command == "long") handleLongList (tdb, task, conf); - else if (command == "ls") handleSmallList (tdb, task, conf); - else if (command == "colors") handleColor ( conf); - else if (command == "completed") handleCompleted (tdb, task, conf); - else if (command == "delete") handleDelete (tdb, task, conf); - else if (command == "start") handleStart (tdb, task, conf); - else if (command == "done") handleDone (tdb, task, conf); - else if (command == "export") handleExport (tdb, task, conf); - else if (command == "version") handleVersion ( conf); - else if (command == "summary") handleReportSummary (tdb, task, conf); - else if (command == "next") handleReportNext (tdb, task, conf); - else if (command == "history") handleReportHistory (tdb, task, conf); - else if (command == "calendar") handleReportCalendar (tdb, task, conf); - else if (command == "active") handleReportActive (tdb, task, conf); - else if (command == "overdue") handleReportOverdue (tdb, task, conf); - else if (command == "stats") handleReportStats (tdb, task, conf); - else if (command == "usage") handleReportUsage (tdb, task, conf); - else if (command == "" && task.getId ()) handleModify (tdb, task, conf); - else usage (conf); - } - - catch (std::string& error) - { - std::cout << error << std::endl; - return -1; - } - - catch (...) - { - std::cout << "Unknown error." << std::endl; - return -2; - } - -// return 0; - exit (0); -} - -//////////////////////////////////////////////////////////////////////////////// -std::string epochToString (const std::string& epoch) -{ - char formatted[12] = {0}; - - if (epoch.length () && epoch.find ("/") == std::string::npos) - { - Date dt (::atoi (epoch.c_str ())); - int m, d, y; - dt.toMDY (m, d, y); - sprintf (formatted, "%d/%d/%04d", m, d, y); - } - - return formatted; -} - -//////////////////////////////////////////////////////////////////////////////// -void handleAdd (const TDB& tdb, T& task, Config& conf) -{ - char entryTime[16]; - sprintf (entryTime, "%u", (unsigned int) time (NULL)); - task.setAttribute ("entry", entryTime); - - if (task.getDescription () == "") -// std::cout << "Cannot add a blank task" << std::endl; - throw std::string ("Cannot add a blank task."); - - if (!tdb.addT (task)) - throw std::string ("Could not create new task."); -} - -//////////////////////////////////////////////////////////////////////////////// -void handleProjects (const TDB& tdb, T& task, Config& conf) -{ - // Get all the tasks, including deleted ones. - std::vector tasks; - tdb.pendingT (tasks); - - // Scan all the tasks for their project name, building a map using project - // names as keys. - std::map unique; - for (unsigned int i = 0; i < tasks.size (); ++i) - { - T task (tasks[i]); - unique[task.getAttribute ("project")] += 1; - } - - // Render a list of project names from the map. - Table table; - table.addColumn ("Project"); - table.addColumn ("Tasks"); - - table.setColumnUnderline (0); - table.setColumnUnderline (1); - - table.setColumnJustification (1, Table::right); - - foreach (i, unique) - { - int row = table.addRow (); - table.addCell (row, 0, i->first); - table.addCell (row, 1, i->second); - } - - std::cout << std::endl - << table.render () - << std::endl - << unique.size () - << (unique.size () == 1 ? " project" : " projects") - << std::endl; -} - -//////////////////////////////////////////////////////////////////////////////// -void handleTags (const TDB& tdb, T& task, Config& conf) -{ - // Get all the tasks. - std::vector tasks; - tdb.pendingT (tasks); - - // Scan all the tasks for their project name, building a map using project - // names as keys. - std::map unique; - for (unsigned int i = 0; i < tasks.size (); ++i) - { - T task (tasks[i]); - - std::vector tags; - task.getTags (tags); - - for (unsigned int t = 0; t < tags.size (); ++t) - unique[tags[t]] = ""; - } - - // Render a list of tag names from the map. - foreach (i, unique) - std::cout << i->first << std::endl; - - if (unique.size ()) - std::cout << std::endl - << unique.size () - << (unique.size () == 1 ? " tag" : " tags") - << std::endl; - else - std::cout << "No tags." - << std::endl; - -} - -//////////////////////////////////////////////////////////////////////////////// -// Successively apply filters based on the task object built from the command -// line. Tasks that match all the specified criteria are listed. -void handleList (const TDB& tdb, T& task, Config& conf) -{ - // Determine window size, and set table accordingly. - int width = 80; -#ifdef HAVE_LIBNCURSES - if (conf.get ("curses", true)) - { - WINDOW* w = initscr (); - width = w->_maxx + 1; - endwin (); - } -#endif - - /* int count = */ tdb.gc (); - - // Get the pending tasks. - std::vector tasks; - tdb.pendingT (tasks); - - initializeColorRules (conf); - - // Create a table for output. - Table table; - table.setTableWidth (width); - table.addColumn ("ID"); - table.addColumn ("Project"); - table.addColumn ("Pri"); - table.addColumn ("Due"); - table.addColumn ("Active"); - table.addColumn ("Age"); - table.addColumn ("Description"); - - table.setColumnUnderline (0); - table.setColumnUnderline (1); - table.setColumnUnderline (2); - table.setColumnUnderline (3); - table.setColumnUnderline (4); - table.setColumnUnderline (5); - table.setColumnUnderline (6); - - table.setColumnWidth (0, Table::minimum); - table.setColumnWidth (1, Table::minimum); - table.setColumnWidth (2, Table::minimum); - table.setColumnWidth (3, Table::minimum); - table.setColumnWidth (4, Table::minimum); - table.setColumnWidth (5, Table::minimum); - table.setColumnWidth (6, Table::flexible); - - table.setColumnJustification (0, Table::right); - table.setColumnJustification (3, Table::right); - table.setColumnJustification (5, Table::right); - - table.sortOn (3, Table::ascendingDate); - table.sortOn (2, Table::descendingPriority); - table.sortOn (1, Table::ascendingCharacter); - - // Split any description specified into words. - std::vector descWords; - split (descWords, task.getDescription (), ' '); - - // Get all the tags to match against. - std::vector tagList; - task.getTags (tagList); - - // Get all the attributes to match against. - std::map attrList; - task.getAttributes (attrList); - - // Iterate over each task, and apply selection criteria. - for (unsigned int i = 0; i < tasks.size (); ++i) - { - T refTask (tasks[i]); - - // Apply description filter. - unsigned int matches = 0; - for (unsigned int w = 0; w < descWords.size (); ++w) - if (refTask.getDescription ().find (descWords[w]) != std::string::npos) - ++matches; - - if (matches == descWords.size ()) - { - // Apply attribute filter. - matches = 0; - foreach (a, attrList) - if (a->second == refTask.getAttribute (a->first)) - ++matches; - - if (matches == attrList.size ()) - { - // Apply tag filter. - matches = 0; - for (unsigned int t = 0; t < tagList.size (); ++t) - if (refTask.hasTag (tagList[t])) - ++matches; - - if (matches == tagList.size ()) - { - // Now format the matching task. - bool imminent = false; - bool overdue = false; - Date now; - std::string due = refTask.getAttribute ("due"); - if (due.length () && due.find ("/") == std::string::npos) - { - Date dt (::atoi (due.c_str ())); - int m, d, y; - dt.toMDY (m, d, y); - char formatted[12]; - sprintf (formatted, "%d/%d/%04d", m, d, y); - due = formatted; - - overdue = (dt < now) ? true : false; - now += 7 * 86400; - imminent = dt < now ? true : false; - } - - std::string active; - if (refTask.getAttribute ("start") != "") - active = "*"; - - std::string age; - std::string created = refTask.getAttribute ("entry"); - if (created.length () && created.find ("/") == std::string::npos) - { - Date dt (::atoi (created.c_str ())); - formatTimeDeltaDays (age, (time_t) (now - dt)); - } - - // All criteria match, so add refTask to the output table. - int row = table.addRow (); - table.addCell (row, 0, refTask.getId ()); - table.addCell (row, 1, refTask.getAttribute ("project")); - table.addCell (row, 2, refTask.getAttribute ("priority")); - table.addCell (row, 3, due); - table.addCell (row, 4, active); - table.addCell (row, 5, age); - table.addCell (row, 6, refTask.getDescription ()); - - if (conf.get ("color", true)) - { - Text::color fg = Text::colorCode (refTask.getAttribute ("fg")); - Text::color bg = Text::colorCode (refTask.getAttribute ("bg")); - autoColorize (refTask, fg, bg); - table.setRowFg (row, fg); - table.setRowBg (row, bg); - - if (fg == Text::nocolor) - { - if (overdue) - table.setCellFg (row, 3, Text::red); - else if (imminent) - table.setCellFg (row, 3, Text::yellow); - } - } - } - } - } - } - - if (table.rowCount ()) - std::cout << std::endl - << table.render () - << std::endl - << table.rowCount () - << (table.rowCount () == 1 ? " task" : " tasks") - << std::endl; - else - std::cout << "No matches." - << std::endl; - -/* - if (count) - std::cout << std::endl - << "[gc: " - << count - << " transferred]" - << std::endl; -*/ -} - -//////////////////////////////////////////////////////////////////////////////// -// Successively apply filters based on the task object built from the command -// line. Tasks that match all the specified criteria are listed. Show a narrow -// list that works better on mobile devices. -void handleSmallList (const TDB& tdb, T& task, Config& conf) -{ - // Determine window size, and set table accordingly. - int width = 80; -#ifdef HAVE_LIBNCURSES - if (conf.get ("curses", true)) - { - WINDOW* w = initscr (); - width = w->_maxx + 1; - endwin (); - } -#endif - - /* int count = */ tdb.gc (); - - // Get the pending tasks. - std::vector tasks; - tdb.pendingT (tasks); - - initializeColorRules (conf); - - // Create a table for output. - Table table; - table.setTableWidth (width); - table.addColumn ("ID"); - table.addColumn ("Project"); - table.addColumn ("Pri"); - table.addColumn ("Description"); - - table.setColumnUnderline (0); - table.setColumnUnderline (1); - table.setColumnUnderline (2); - table.setColumnUnderline (3); - - table.setColumnWidth (0, Table::minimum); - table.setColumnWidth (1, Table::minimum); - table.setColumnWidth (2, Table::minimum); - table.setColumnWidth (3, Table::flexible); - - table.setColumnJustification (0, Table::right); - table.setColumnJustification (3, Table::left); - - table.sortOn (2, Table::descendingPriority); - table.sortOn (1, Table::ascendingCharacter); - - // Split any description specified into words. - std::vector descWords; - split (descWords, task.getDescription (), ' '); - - // Get all the tags to match against. - std::vector tagList; - task.getTags (tagList); - - // Get all the attributes to match against. - std::map attrList; - task.getAttributes (attrList); - - // Iterate over each task, and apply selection criteria. - for (unsigned int i = 0; i < tasks.size (); ++i) - { - T refTask (tasks[i]); - - // Apply description filter. - unsigned int matches = 0; - for (unsigned int w = 0; w < descWords.size (); ++w) - if (refTask.getDescription ().find (descWords[w]) != std::string::npos) - ++matches; - - if (matches == descWords.size ()) - { - // Apply attribute filter. - matches = 0; - foreach (a, attrList) - if (a->second == refTask.getAttribute (a->first)) - ++matches; - - if (matches == attrList.size ()) - { - // Apply tag filter. - matches = 0; - for (unsigned int t = 0; t < tagList.size (); ++t) - if (refTask.hasTag (tagList[t])) - ++matches; - - if (matches == tagList.size ()) - { - // Now format the matching task. - bool imminent = false; - bool overdue = false; - Date now; - std::string due = refTask.getAttribute ("due"); - if (due.length () && due.find ("/") == std::string::npos) - { - Date dt (::atoi (due.c_str ())); - int m, d, y; - dt.toMDY (m, d, y); - char formatted[12]; - sprintf (formatted, "%d/%d/%04d", m, d, y); - due = formatted; - - overdue = (dt < now) ? true : false; - now += 7 * 86400; - imminent = dt < now ? true : false; - } - - std::string active; - if (refTask.getAttribute ("start") != "") - active = "*"; - - std::string age; - std::string created = refTask.getAttribute ("entry"); - if (created.length () && created.find ("/") == std::string::npos) - { - Date dt (::atoi (created.c_str ())); - formatTimeDeltaDays (age, (time_t) (now - dt)); - } - - // All criteria match, so add refTask to the output table. - int row = table.addRow (); - table.addCell (row, 0, refTask.getId ()); - table.addCell (row, 1, refTask.getAttribute ("project")); - table.addCell (row, 2, refTask.getAttribute ("priority")); - table.addCell (row, 3, refTask.getDescription ()); - - if (conf.get ("color", true)) - { - Text::color fg = Text::colorCode (refTask.getAttribute ("fg")); - Text::color bg = Text::colorCode (refTask.getAttribute ("bg")); - autoColorize (refTask, fg, bg); - table.setRowFg (row, fg); - table.setRowBg (row, bg); - - if (fg == Text::nocolor) - { - if (overdue) - table.setCellFg (row, 3, Text::red); - else if (imminent) - table.setCellFg (row, 3, Text::yellow); - } - } - } - } - } - } - - if (table.rowCount ()) - std::cout << std::endl - << table.render () - << std::endl - << table.rowCount () - << (table.rowCount () == 1 ? " task" : " tasks") - << std::endl; - else - std::cout << "No matches." - << std::endl; - -/* - if (count) - std::cout << std::endl - << "[gc: " - << count - << " transferred]" - << std::endl; -*/ -} - -//////////////////////////////////////////////////////////////////////////////// -// Successively apply filters based on the task object built from the command -// line. Tasks that match all the specified criteria are listed. -void handleCompleted (const TDB& tdb, T& task, Config& conf) -{ - // Determine window size, and set table accordingly. - int width = 80; -#ifdef HAVE_LIBNCURSES - if (conf.get ("curses", true)) - { - WINDOW* w = initscr (); - width = w->_maxx + 1; - endwin (); - } -#endif - - /* int count = */ tdb.gc (); - - // Get the pending tasks. - std::vector tasks; - tdb.completedT (tasks); - - initializeColorRules (conf); - - // Create a table for output. - Table table; - table.setTableWidth (width); - table.addColumn ("Done"); - table.addColumn ("Project"); - table.addColumn ("Description"); - - table.setColumnUnderline (0); - table.setColumnUnderline (1); - table.setColumnUnderline (2); - - table.setColumnWidth (0, Table::minimum); - table.setColumnWidth (1, Table::minimum); - table.setColumnWidth (2, Table::flexible); - - table.setColumnJustification (0, Table::right); - table.setColumnJustification (1, Table::left); - table.setColumnJustification (2, Table::left); - - table.sortOn (0, Table::ascendingDate); - - // Split any description specified into words. - std::vector descWords; - split (descWords, task.getDescription (), ' '); - - // Get all the tags to match against. - std::vector tagList; - task.getTags (tagList); - - // Get all the attributes to match against. - std::map attrList; - task.getAttributes (attrList); - - // Iterate over each task, and apply selection criteria. - for (unsigned int i = 0; i < tasks.size (); ++i) - { - T refTask (tasks[i]); - - // Apply description filter. - unsigned int matches = 0; - for (unsigned int w = 0; w < descWords.size (); ++w) - if (refTask.getDescription ().find (descWords[w]) != std::string::npos) - ++matches; - - if (matches == descWords.size ()) - { - // Apply attribute filter. - matches = 0; - foreach (a, attrList) - if (a->second == refTask.getAttribute (a->first)) - ++matches; - - if (matches == attrList.size ()) - { - // Apply tag filter. - matches = 0; - for (unsigned int t = 0; t < tagList.size (); ++t) - if (refTask.hasTag (tagList[t])) - ++matches; - - if (matches == tagList.size ()) - { - // Now format the matching task. - Date end (::atoi (refTask.getAttribute ("end").c_str ())); - - // All criteria match, so add refTask to the output table. - int row = table.addRow (); - - table.addCell (row, 0, end.toString ()); - table.addCell (row, 1, refTask.getAttribute ("project")); - table.addCell (row, 2, refTask.getDescription ()); - - if (conf.get ("color", true)) - { - Text::color fg = Text::colorCode (refTask.getAttribute ("fg")); - Text::color bg = Text::colorCode (refTask.getAttribute ("bg")); - autoColorize (refTask, fg, bg); - table.setRowFg (row, fg); - table.setRowBg (row, bg); - } - } - } - } - } - - if (table.rowCount ()) - std::cout << std::endl - << table.render () - << std::endl - << table.rowCount () - << (table.rowCount () == 1 ? " task" : " tasks") - << std::endl; - else - std::cout << "No matches." - << std::endl; - -/* - if (count) - std::cout << std::endl - << "[gc: " - << count - << " transferred]" - << std::endl; -*/ -} - -//////////////////////////////////////////////////////////////////////////////// -// Display all information for the given task. -void handleInfo (const TDB& tdb, T& task, Config& conf) -{ - // Determine window size, and set table accordingly. - int width = 80; -#ifdef HAVE_LIBNCURSES - if (conf.get ("curses", true)) - { - WINDOW* w = initscr (); - width = w->_maxx + 1; - endwin (); - } -#endif - - // Get all the tasks. - std::vector tasks; - tdb.allPendingT (tasks); - - Table table; - table.setTableWidth (width); - - table.addColumn ("Name"); - table.addColumn ("Value"); - - table.setColumnUnderline (0); - table.setColumnUnderline (1); - - table.setColumnWidth (0, Table::minimum); - table.setColumnWidth (1, Table::minimum); - - table.setColumnJustification (0, Table::left); - table.setColumnJustification (1, Table::left); - - // Find the task. - for (unsigned int i = 0; i < tasks.size (); ++i) - { - T refTask (tasks[i]); - - if (refTask.getId () == task.getId ()) - { - Date now; - - int row = table.addRow (); - table.addCell (row, 0, "ID"); - table.addCell (row, 1, refTask.getId ()); - - row = table.addRow (); - table.addCell (row, 0, "Status"); - table.addCell (row, 1, ( refTask.getStatus () == T::pending ? "Pending" - : refTask.getStatus () == T::completed ? "Completed" - : refTask.getStatus () == T::deleted ? "Deleted" - : "")); - - row = table.addRow (); - table.addCell (row, 0, "Description"); - table.addCell (row, 1, refTask.getDescription ()); - - if (refTask.getAttribute ("project") != "") - { - row = table.addRow (); - table.addCell (row, 0, "Project"); - table.addCell (row, 1, refTask.getAttribute ("project")); - } - - if (refTask.getAttribute ("priority") != "") - { - row = table.addRow (); - table.addCell (row, 0, "Priority"); - table.addCell (row, 1, refTask.getAttribute ("priority")); - } - - // due (colored) - bool imminent = false; - bool overdue = false; - std::string due = refTask.getAttribute ("due"); - if (due != "") - { - row = table.addRow (); - table.addCell (row, 0, "Due"); - table.addCell (row, 1, epochToString (due)); - - if (due.length () && due.find ("/") == std::string::npos) - { - Date dt (::atoi (due.c_str ())); - - overdue = (dt < now) ? true : false; - now += 7 * 86400; - imminent = dt < now ? true : false; - - if (conf.get ("color", true)) - { - if (overdue) - table.setCellFg (row, 1, Text::red); - else if (imminent) - table.setCellFg (row, 1, Text::yellow); - } - } - } - - // start - if (refTask.getAttribute ("start") != "") - { - row = table.addRow (); - table.addCell (row, 0, "Start"); - table.addCell (row, 1, epochToString (refTask.getAttribute ("start"))); - } - - // end - if (refTask.getAttribute ("end") != "") - { - row = table.addRow (); - table.addCell (row, 0, "End"); - table.addCell (row, 1, epochToString (refTask.getAttribute ("end"))); - } - - // tags ... - std::vector tags; - refTask.getTags (tags); - if (tags.size ()) - { - std::string allTags; - join (allTags, " ", tags); - - row = table.addRow (); - table.addCell (row, 0, "Tags"); - table.addCell (row, 1, allTags); - } - - row = table.addRow (); - table.addCell (row, 0, "UUID"); - table.addCell (row, 1, refTask.getUUID ()); - - row = table.addRow (); - table.addCell (row, 0, "Entered"); - std::string entry = epochToString (refTask.getAttribute ("entry")); - - std::string age; - std::string created = refTask.getAttribute ("entry"); - if (created.length () && created.find ("/") == std::string::npos) - { - Date dt (::atoi (created.c_str ())); - formatTimeDeltaDays (age, (time_t) (now - dt)); - } - - table.addCell (row, 1, entry + " (" + age + ")"); - } - } - - if (table.rowCount ()) - std::cout << std::endl - << table.render () - << std::endl - << table.rowCount () - << (table.rowCount () == 1 ? " task" : " tasks") - << std::endl; - else - std::cout << "No matches." << std::endl; -} - -//////////////////////////////////////////////////////////////////////////////// -// Successively apply filters based on the task object built from the command -// line. Tasks that match all the specified criteria are listed. -void handleLongList (const TDB& tdb, T& task, Config& conf) -{ - // Determine window size, and set table accordingly. - int width = 80; -#ifdef HAVE_LIBNCURSES - if (conf.get ("curses", true)) - { - WINDOW* w = initscr (); - width = w->_maxx + 1; - endwin (); - } -#endif - - /* int count = */ tdb.gc (); - - // Get all the tasks. - std::vector tasks; - tdb.pendingT (tasks); - - initializeColorRules (conf); - - // Create a table for output. - Table table; - table.setTableWidth (width); - table.addColumn ("ID"); - table.addColumn ("Project"); - table.addColumn ("Pri"); - table.addColumn ("Entry"); - table.addColumn ("Start"); - table.addColumn ("Due"); - table.addColumn ("Age"); - table.addColumn ("Description"); - - table.setColumnUnderline (0); - table.setColumnUnderline (1); - table.setColumnUnderline (2); - table.setColumnUnderline (3); - table.setColumnUnderline (4); - table.setColumnUnderline (5); - table.setColumnUnderline (6); - table.setColumnUnderline (7); - - table.setColumnWidth (0, Table::minimum); - table.setColumnWidth (1, Table::minimum); - table.setColumnWidth (2, Table::minimum); - table.setColumnWidth (3, Table::minimum); - table.setColumnWidth (4, Table::minimum); - table.setColumnWidth (5, Table::minimum); - table.setColumnWidth (6, Table::minimum); - table.setColumnWidth (7, Table::flexible); - - table.setColumnJustification (0, Table::right); - table.setColumnJustification (3, Table::right); - table.setColumnJustification (4, Table::right); - table.setColumnJustification (5, Table::right); - table.setColumnJustification (6, Table::right); - - table.sortOn (5, Table::ascendingDate); - table.sortOn (2, Table::descendingPriority); - table.sortOn (1, Table::ascendingCharacter); - - // Split any description specified into words. - std::vector descWords; - split (descWords, task.getDescription (), ' '); - - // Get all the tags to match against. - std::vector tagList; - task.getTags (tagList); - - // Get all the attributes to match against. - std::map attrList; - task.getAttributes (attrList); - - // Iterate over each task, and apply selection criteria. - for (unsigned int i = 0; i < tasks.size (); ++i) - { - T refTask (tasks[i]); - - // Apply description filter. - unsigned int matches = 0; - for (unsigned int w = 0; w < descWords.size (); ++w) - if (refTask.getDescription ().find (descWords[w]) != std::string::npos) - ++matches; - - if (matches == descWords.size ()) - { - // Apply attribute filter. - matches = 0; - foreach (a, attrList) - if (a->second == refTask.getAttribute (a->first)) - ++matches; - - if (matches == attrList.size ()) - { - // Apply tag filter. - matches = 0; - for (unsigned int t = 0; t < tagList.size (); ++t) - if (refTask.hasTag (tagList[t])) - ++matches; - - if (matches == tagList.size ()) - { - Date now; - - std::string started = refTask.getAttribute ("start"); - if (started.length () && started.find ("/") == std::string::npos) - { - Date dt (::atoi (started.c_str ())); - int m, d, y; - dt.toMDY (m, d, y); - char formatted[12]; - sprintf (formatted, "%d/%d/%04d", m, d, y); - started = formatted; - } - - std::string entered = refTask.getAttribute ("entry"); - if (entered.length () && entered.find ("/") == std::string::npos) - { - Date dt (::atoi (entered.c_str ())); - int m, d, y; - dt.toMDY (m, d, y); - char formatted[12]; - sprintf (formatted, "%d/%d/%04d", m, d, y); - entered = formatted; - } - - // Now format the matching task. - bool imminent = false; - bool overdue = false; - std::string due = refTask.getAttribute ("due"); - if (due.length () && due.find ("/") == std::string::npos) - { - Date dt (::atoi (due.c_str ())); - int m, d, y; - dt.toMDY (m, d, y); - char formatted[12]; - sprintf (formatted, "%d/%d/%04d", m, d, y); - due = formatted; - - overdue = (dt < now) ? true : false; - now += 7 * 86400; - imminent = dt < now ? true : false; - } - - std::string age; - std::string created = refTask.getAttribute ("entry"); - if (created.length () && created.find ("/") == std::string::npos) - { - Date dt (::atoi (created.c_str ())); - formatTimeDeltaDays (age, (time_t) (now - dt)); - } - - // All criteria match, so add refTask to the output table. - int row = table.addRow (); - table.addCell (row, 0, refTask.getId ()); - table.addCell (row, 1, refTask.getAttribute ("project")); - table.addCell (row, 2, refTask.getAttribute ("priority")); - table.addCell (row, 3, entered); - table.addCell (row, 4, started); - table.addCell (row, 5, due); - table.addCell (row, 6, age); - table.addCell (row, 7, refTask.getDescription ()); - - if (conf.get ("color", true)) - { - Text::color fg = Text::colorCode (refTask.getAttribute ("fg")); - Text::color bg = Text::colorCode (refTask.getAttribute ("bg")); - autoColorize (refTask, fg, bg); - table.setRowFg (row, fg); - table.setRowBg (row, bg); - - if (fg == Text::nocolor) - { - if (overdue) - table.setCellFg (row, 3, Text::red); - else if (imminent) - table.setCellFg (row, 3, Text::yellow); - } - } - } - } - } - } - - if (table.rowCount ()) - std::cout << std::endl - << table.render () - << std::endl - << table.rowCount () - << (table.rowCount () == 1 ? " task" : " tasks") - << std::endl; - else - std::cout << "No matches." << std::endl; - -/* - if (count) - std::cout << std::endl - << "[gc: " - << count - << " transferred]" - << std::endl; -*/ -} - -//////////////////////////////////////////////////////////////////////////////// -// Project Tasks Avg Age Status -// A 12 13d XXXXXXXX------ -// B 109 3d 12h XX------------ -void handleReportSummary (const TDB& tdb, T& task, Config& conf) -{ - // Generate unique list of project names. - std::map allProjects; - std::vector pending; - tdb.pendingT (pending); - for (unsigned int i = 0; i < pending.size (); ++i) - { - T task (pending[i]); - allProjects[task.getAttribute ("project")] = false; - } - - std::vector completed; - tdb.completedT (completed); - for (unsigned int i = 0; i < completed.size (); ++i) - { - T task (completed[i]); - allProjects[task.getAttribute ("project")] = false; - } - - // Initialize counts, sum. - std::map countPending; - std::map countCompleted; - std::map sumEntry; - std::map counter; - time_t now = time (NULL); - - foreach (i, allProjects) - { - countPending [i->first] = 0; - countCompleted [i->first] = 0; - sumEntry [i->first] = 0.0; - counter [i->first] = 0; - } - - // Count the pending tasks. - for (unsigned int i = 0; i < pending.size (); ++i) - { - T task (pending[i]); - std::string project = task.getAttribute ("project"); - ++countPending[project]; - - time_t entry = ::atoi (task.getAttribute ("entry").c_str ()); - if (entry) - { - sumEntry[project] = sumEntry[project] + (double) (now - entry); - ++counter[project]; - } - } - - // Count the completed tasks. - for (unsigned int i = 0; i < completed.size (); ++i) - { - T task (completed[i]); - std::string project = task.getAttribute ("project"); - countCompleted[project] = countCompleted[project] + 1; - ++counter[project]; - - time_t entry = ::atoi (task.getAttribute ("entry").c_str ()); - time_t end = ::atoi (task.getAttribute ("end").c_str ()); - if (entry && end) - sumEntry[project] = sumEntry[project] + (double) (end - entry); - } - - // Create a table for output. - Table table; - table.addColumn ("Project"); - table.addColumn ("Remaining"); - table.addColumn ("Avg age"); - table.addColumn ("Complete"); - table.addColumn ("0% 100%"); - - table.setColumnUnderline (0); - table.setColumnUnderline (1); - table.setColumnUnderline (2); - table.setColumnUnderline (3); - - table.setColumnJustification (1, Table::right); - table.setColumnJustification (2, Table::right); - table.setColumnJustification (3, Table::right); - - table.sortOn (0, Table::ascendingCharacter); - - int barWidth = 30; - foreach (i, allProjects) - { - if (countPending[i->first] > 0) - { - int row = table.addRow (); - table.addCell (row, 0, (i->first == "" ? "(none)" : i->first)); - table.addCell (row, 1, countPending[i->first]); - if (counter[i->first]) - { - std::string age; - formatTimeDeltaDays (age, (time_t) (sumEntry[i->first] / counter[i->first])); - table.addCell (row, 2, age); - } - - int c = countCompleted[i->first]; - int p = countPending[i->first]; - int completedBar = (c * barWidth) / (c + p); - - std::string bar; - if (conf.get ("color", true)) - { - bar = "\033[42m"; - for (int b = 0; b < completedBar; ++b) - bar += " "; - - bar += "\033[40m"; - for (int b = 0; b < barWidth - completedBar; ++b) - bar += " "; - - bar += "\033[0m"; - } - else - { - for (int b = 0; b < completedBar; ++b) - bar += "="; - - for (int b = 0; b < barWidth - completedBar; ++b) - bar += " "; - } - table.addCell (row, 4, bar); - - char percent[12]; - sprintf (percent, "%d%%", 100 * c / (c + p)); - table.addCell (row, 3, percent); - } - } - - if (table.rowCount ()) - std::cout << std::endl - << table.render () - << std::endl - << table.rowCount () - << (table.rowCount () == 1 ? " project" : " projects") - << std::endl; - else - std::cout << "No projects." << std::endl; -} - -//////////////////////////////////////////////////////////////////////////////// -// A summary of the most important pending tasks. -// -// For every project, pull important tasks to present as an 'immediate' task -// list. This hides the overwhelming quantity of other tasks. -// -// Present at most three tasks for every project. Select the tasks using -// these criteria: -// - due:< 1wk, pri:* -// - due:*, pri:H -// - pri:H -// - due:*, pri:M -// - pri:M -// - due:*, pri:L -// - pri:L -// - due:*, pri:* <-- everything else -// -// Make the "three" tasks a configurable number -// -void handleReportNext (const TDB& tdb, T& task, Config& conf) -{ - // Load all pending. - std::vector pending; - tdb.allPendingT (pending); - - // Restrict to matching subset. - std::vector matching; - gatherNextTasks (tdb, task, conf, pending, matching); - - // Determine window size, and set table accordingly. - int width = 80; -#ifdef HAVE_LIBNCURSES - if (conf.get ("curses", true)) - { - WINDOW* w = initscr (); - width = w->_maxx + 1; - endwin (); - } -#endif - - /* int gcCount = */ tdb.gc (); - - // Get the pending tasks. - std::vector tasks; - tdb.pendingT (tasks); - - initializeColorRules (conf); - - // Create a table for output. - Table table; - table.setTableWidth (width); - table.addColumn ("ID"); - table.addColumn ("Project"); - table.addColumn ("Pri"); - table.addColumn ("Due"); - table.addColumn ("Active"); - table.addColumn ("Age"); - table.addColumn ("Description"); - - table.setColumnUnderline (0); - table.setColumnUnderline (1); - table.setColumnUnderline (2); - table.setColumnUnderline (3); - table.setColumnUnderline (4); - table.setColumnUnderline (5); - table.setColumnUnderline (6); - - table.setColumnWidth (0, Table::minimum); - table.setColumnWidth (1, Table::minimum); - table.setColumnWidth (2, Table::minimum); - table.setColumnWidth (3, Table::minimum); - table.setColumnWidth (4, Table::minimum); - table.setColumnWidth (5, Table::minimum); - table.setColumnWidth (6, Table::flexible); - - table.setColumnJustification (0, Table::right); - table.setColumnJustification (3, Table::right); - table.setColumnJustification (5, Table::right); - - table.sortOn (3, Table::ascendingDate); - table.sortOn (2, Table::descendingPriority); - table.sortOn (1, Table::ascendingCharacter); - - // Split any description specified into words. - std::vector descWords; - split (descWords, task.getDescription (), ' '); - - // Get all the tags to match against. - std::vector tagList; - task.getTags (tagList); - - // Get all the attributes to match against. - std::map attrList; - task.getAttributes (attrList); - - // Iterate over each task, and apply selection criteria. - foreach (i, matching) - { - T refTask (pending[*i]); - - // Apply description filter. - unsigned int matches = 0; - for (unsigned int w = 0; w < descWords.size (); ++w) - if (refTask.getDescription ().find (descWords[w]) != std::string::npos) - ++matches; - - if (matches == descWords.size ()) - { - // Apply attribute filter. - matches = 0; - foreach (a, attrList) - if (a->second == refTask.getAttribute (a->first)) - ++matches; - - if (matches == attrList.size ()) - { - // Apply tag filter. - matches = 0; - for (unsigned int t = 0; t < tagList.size (); ++t) - if (refTask.hasTag (tagList[t])) - ++matches; - - if (matches == tagList.size ()) - { - // Now format the matching task. - bool imminent = false; - bool overdue = false; - Date now; - std::string due = refTask.getAttribute ("due"); - if (due.length () && due.find ("/") == std::string::npos) - { - Date dt (::atoi (due.c_str ())); - int m, d, y; - dt.toMDY (m, d, y); - char formatted[12]; - sprintf (formatted, "%d/%d/%04d", m, d, y); - due = formatted; - - overdue = (dt < now) ? true : false; - now += 7 * 86400; - imminent = dt < now ? true : false; - } - - std::string active; - if (refTask.getAttribute ("start") != "") - active = "*"; - - std::string age; - std::string created = refTask.getAttribute ("entry"); - if (created.length () && created.find ("/") == std::string::npos) - { - Date dt (::atoi (created.c_str ())); - formatTimeDeltaDays (age, (time_t) (now - dt)); - } - - // All criteria match, so add refTask to the output table. - int row = table.addRow (); - table.addCell (row, 0, refTask.getId ()); - table.addCell (row, 1, refTask.getAttribute ("project")); - table.addCell (row, 2, refTask.getAttribute ("priority")); - table.addCell (row, 3, due); - table.addCell (row, 4, active); - table.addCell (row, 5, age); - table.addCell (row, 6, refTask.getDescription ()); - - if (conf.get ("color", true)) - { - Text::color fg = Text::colorCode (refTask.getAttribute ("fg")); - Text::color bg = Text::colorCode (refTask.getAttribute ("bg")); - autoColorize (refTask, fg, bg); - table.setRowFg (row, fg); - table.setRowBg (row, bg); - - if (fg == Text::nocolor) - { - if (overdue) - table.setCellFg (row, 3, Text::red); - else if (imminent) - table.setCellFg (row, 3, Text::yellow); - } - } - } - } - } - } - - if (table.rowCount ()) - std::cout << std::endl - << table.render () - << std::endl - << table.rowCount () - << (table.rowCount () == 1 ? " task" : " tasks") - << std::endl; - else - std::cout << "No matches." - << std::endl; - -/* - if (gcCount) - std::cout << std::endl - << "[gc: " - << gcCount - << " transferred]" - << std::endl; -*/ -} - -//////////////////////////////////////////////////////////////////////////////// -// Year Month Added Completed Deleted -// 2006 November 87 63 14 -// 2006 December 21 6 1 -time_t monthlyEpoch (const std::string& date) -{ - // Convert any date in epoch form to m/d/y, then convert back - // to epoch form for the date m/1/y. - if (date.length ()) - { - Date d1 (::atoi (date.c_str ())); - int m, d, y; - d1.toMDY (m, d, y); - Date d2 (m, 1, y); - time_t epoch; - d2.toEpoch (epoch); - return epoch; - } - - return 0; -} - -void handleReportHistory (const TDB& tdb, T& task, Config& conf) -{ - std::map groups; - std::map addedGroup; - std::map completedGroup; - std::map deletedGroup; - - // Scan the pending tasks. - std::vector pending; - tdb.allPendingT (pending); - for (unsigned int i = 0; i < pending.size (); ++i) - { - T task (pending[i]); - time_t epoch = monthlyEpoch (task.getAttribute ("entry")); - if (epoch) - { - groups[epoch] = 0; - - if (addedGroup.find (epoch) != addedGroup.end ()) - addedGroup[epoch] = addedGroup[epoch] + 1; - else - addedGroup[epoch] = 1; - - if (task.getStatus () == T::deleted) - { - epoch = monthlyEpoch (task.getAttribute ("end")); - - if (deletedGroup.find (epoch) != deletedGroup.end ()) - deletedGroup[epoch] = deletedGroup[epoch] + 1; - else - deletedGroup[epoch] = 1; - } - else if (task.getStatus () == T::completed) - { - epoch = monthlyEpoch (task.getAttribute ("end")); - - if (completedGroup.find (epoch) != completedGroup.end ()) - completedGroup[epoch] = completedGroup[epoch] + 1; - else - completedGroup[epoch] = 1; - } - } - } - - // Scan the completed tasks. - std::vector completed; - tdb.allCompletedT (completed); - for (unsigned int i = 0; i < completed.size (); ++i) - { - T task (completed[i]); - time_t epoch = monthlyEpoch (task.getAttribute ("entry")); - if (epoch) - { - groups[epoch] = 0; - - if (addedGroup.find (epoch) != addedGroup.end ()) - addedGroup[epoch] = addedGroup[epoch] + 1; - else - addedGroup[epoch] = 1; - - epoch = monthlyEpoch (task.getAttribute ("end")); - if (task.getStatus () == T::deleted) - { - epoch = monthlyEpoch (task.getAttribute ("end")); - - if (deletedGroup.find (epoch) != deletedGroup.end ()) - deletedGroup[epoch] = deletedGroup[epoch] + 1; - else - deletedGroup[epoch] = 1; - } - else if (task.getStatus () == T::completed) - { - epoch = monthlyEpoch (task.getAttribute ("end")); - if (completedGroup.find (epoch) != completedGroup.end ()) - completedGroup[epoch] = completedGroup[epoch] + 1; - else - completedGroup[epoch] = 1; - } - } - } - - // Now build the table. - Table table; - table.addColumn ("Year"); - table.addColumn ("Month"); - table.addColumn ("Added"); - table.addColumn ("Completed"); - table.addColumn ("Deleted"); - table.addColumn ("Net"); - - table.setColumnUnderline (0); - table.setColumnUnderline (1); - table.setColumnUnderline (2); - table.setColumnUnderline (3); - table.setColumnUnderline (4); - table.setColumnUnderline (5); - - table.setColumnJustification (2, Table::right); - table.setColumnJustification (3, Table::right); - table.setColumnJustification (4, Table::right); - table.setColumnJustification (5, Table::right); - - const char *months[] = - { - "January", "February", "March", "April", "May", "June", - "July", "August", "September", "October", "November", "December", - }; - - int priorYear = 0; - foreach (i, groups) - { - int row = table.addRow (); - - Date dt (i->first); - int m, d, y; - dt.toMDY (m, d, y); - - if (y != priorYear) - { - table.addCell (row, 0, y); - priorYear = y; - } - table.addCell (row, 1, months[m - 1]); - - int net = 0; - - if (addedGroup.find (i->first) != addedGroup.end ()) - { - table.addCell (row, 2, addedGroup[i->first]); - net +=addedGroup[i->first]; - } - - if (completedGroup.find (i->first) != completedGroup.end ()) - { - table.addCell (row, 3, completedGroup[i->first]); - net -= completedGroup[i->first]; - } - - if (deletedGroup.find (i->first) != deletedGroup.end ()) - { - table.addCell (row, 4, deletedGroup[i->first]); - net -= deletedGroup[i->first]; - } - - table.addCell (row, 5, net); - if (conf.get ("color", true) && net) - table.setCellFg (row, 5, net > 0 ? Text::red: Text::green); - } - - if (table.rowCount ()) - std::cout << std::endl - << table.render () - << std::endl; - else - std::cout << "No tasks." << std::endl; -} - -//////////////////////////////////////////////////////////////////////////////// -// A summary of the command usage. Not useful to users, but used to display -// usage statistics for feedback. -// -// 2006-12-04 19:59:43 "task list" -// -void handleReportUsage (const TDB& tdb, T& task, Config& conf) -{ - if (conf.get ("command.logging") == "on") - { - std::map usage; - std::vector all; - tdb.logRead (all); - for (unsigned int i = 0; i < all.size (); ++i) - { - // 0123456789012345678901 - // v 21 - // 2006-12-04 19:59:43 "task list" - std::string command = all[i].substr (21, all[i].length () - 22); - - // Parse as a command line. - std::vector args; - split (args, command, " "); - - try - { - T task; - std::string commandName; - parse (args, commandName, task); - - usage[commandName]++; - } - - // Deliberately ignore errors from parsing the command log, as there may - // be commands from a prior version of task in there, which were - // abbreviated, and are now ambiguous. - catch (...) {} - } - - // Now render the table. - Table table; - table.addColumn ("Command"); - table.addColumn ("Frequency"); - table.setColumnUnderline (0); - table.setColumnUnderline (1); - table.setColumnJustification (1, Table::right); - table.sortOn (1, Table::descendingNumeric); - - foreach (i, usage) - { - int row = table.addRow (); - table.addCell (row, 0, (i->first == "" ? "(modify)" : i->first)); - table.addCell (row, 1, i->second); - } - - if (table.rowCount ()) - std::cout << std::endl - << table.render () - << std::endl; - else - std::cout << "No usage." << std::endl; - } - else - std::cout << "Command logging is not enabled, so no history has been kept." - << std::endl; -} - -//////////////////////////////////////////////////////////////////////////////// -void handleReportCalendar (const TDB& tdb, T& task, Config& conf) -{ - // Today. - Date date; - int m = date.month (); - int y = date.year (); - int today = date.day (); - - // Read all the tasks, filter by those that have a due date. - std::vector annotations; - std::vector pending; - tdb.pendingT (pending); - for (unsigned int i = 0; i < pending.size (); ++i) - { - T task (pending[i]); - if (task.getAttribute ("due") != "") - { - Date d (::atoi (task.getAttribute ("due").c_str ())); - if (d.year () == y && d.month () == m) - annotations.push_back (d.day ()); - } - } - - pending.clear (); - - Table table; - table.addColumn ("Su"); - table.addColumn ("Mo"); - table.addColumn ("Tu"); - table.addColumn ("We"); - table.addColumn ("Th"); - table.addColumn ("Fr"); - table.addColumn ("Sa"); - - table.setColumnUnderline (0); - table.setColumnUnderline (1); - table.setColumnUnderline (2); - table.setColumnUnderline (3); - table.setColumnUnderline (4); - table.setColumnUnderline (5); - table.setColumnUnderline (6); - - table.setColumnJustification (0, Table::right); - table.setColumnJustification (1, Table::right); - table.setColumnJustification (2, Table::right); - table.setColumnJustification (3, Table::right); - table.setColumnJustification (4, Table::right); - table.setColumnJustification (5, Table::right); - table.setColumnJustification (6, Table::right); - - int days = Date::daysInMonth (m, y); - int row = table.addRow (); - for (int d = 1; d <= days; ++d) - { - Date temp (m, d, y); - int dow = temp.dayOfWeek (); - - table.addCell (row, dow, d); - - if (conf.get ("color", true) && d == today) - table.setCellFg (row, dow, Text::cyan); - - for (unsigned int a = 0; a < annotations.size (); ++a) - { - if (conf.get ("color", true) && annotations[a] == d) - { - table.setCellFg (row, dow, Text::black); - table.setCellBg (row, dow, d < today ? Text::red : Text::yellow); - } - } - - if (dow == 6 && d < days) - row = table.addRow (); - } - - std::cout << std::endl - << Date::monthName (m) - << " " - << y - << std::endl - << std::endl - << table.render () - << std::endl; -} - -//////////////////////////////////////////////////////////////////////////////// -void handleReportActive (const TDB& tdb, T& task, Config& conf) -{ - // Determine window size, and set table accordingly. - int width = 80; -#ifdef HAVE_LIBNCURSES - if (conf.get ("curses", true)) - { - WINDOW* w = initscr (); - width = w->_maxx + 1; - endwin (); - } -#endif - - // Get all the tasks. - std::vector tasks; - tdb.pendingT (tasks); - - initializeColorRules (conf); - - // Create a table for output. - Table table; - table.setTableWidth (width); - table.addColumn ("ID"); - table.addColumn ("Project"); - table.addColumn ("Pri"); - table.addColumn ("Due"); - table.addColumn ("Description"); - - table.setColumnUnderline (0); - table.setColumnUnderline (1); - table.setColumnUnderline (2); - table.setColumnUnderline (3); - table.setColumnUnderline (4); - - table.setColumnWidth (0, Table::minimum); - table.setColumnWidth (1, Table::minimum); - table.setColumnWidth (2, Table::minimum); - table.setColumnWidth (3, Table::minimum); - table.setColumnWidth (4, Table::flexible); - - table.setColumnJustification (0, Table::right); - table.setColumnJustification (3, Table::right); - - table.sortOn (3, Table::ascendingDate); - table.sortOn (2, Table::descendingPriority); - table.sortOn (1, Table::ascendingCharacter); - - // Split any description specified into words. - std::vector descWords; - split (descWords, task.getDescription (), ' '); - - // Get all the tags to match against. - std::vector tagList; - task.getTags (tagList); - - // Get all the attributes to match against. - std::map attrList; - task.getAttributes (attrList); - - // Iterate over each task, and apply selection criteria. - for (unsigned int i = 0; i < tasks.size (); ++i) - { - T refTask (tasks[i]); - if (refTask.getAttribute ("start") != "") - { - bool imminent = false; - bool overdue = false; - std::string due = refTask.getAttribute ("due"); - if (due.length () && due.find ("/") == std::string::npos) - { - Date dt (::atoi (due.c_str ())); - int m, d, y; - dt.toMDY (m, d, y); - char formatted[12]; - sprintf (formatted, "%d/%d/%04d", m, d, y); - due = formatted; - - Date now; - overdue = dt < now ? true : false; - now += 7 * 86400; - imminent = dt < now ? true : false; - } - - // All criteria match, so add refTask to the output table. - int row = table.addRow (); - table.addCell (row, 0, refTask.getId ()); - table.addCell (row, 1, refTask.getAttribute ("project")); - table.addCell (row, 2, refTask.getAttribute ("priority")); - table.addCell (row, 3, due); - table.addCell (row, 4, refTask.getDescription ()); - - if (conf.get ("color", true)) - { - Text::color fg = Text::colorCode (refTask.getAttribute ("fg")); - Text::color bg = Text::colorCode (refTask.getAttribute ("bg")); - autoColorize (refTask, fg, bg); - table.setRowFg (row, fg); - table.setRowBg (row, bg); - - if (fg == Text::nocolor) - { - if (overdue) - table.setCellFg (row, 3, Text::red); - else if (imminent) - table.setCellFg (row, 3, Text::yellow); - } - } - } - } - - if (table.rowCount ()) - std::cout << std::endl - << table.render () - << std::endl - << table.rowCount () - << (table.rowCount () == 1 ? " task" : " tasks") - << std::endl; - else - std::cout << "No active tasks." << std::endl; -} - -//////////////////////////////////////////////////////////////////////////////// -void handleReportOverdue (const TDB& tdb, T& task, Config& conf) -{ - // Determine window size, and set table accordingly. - int width = 80; -#ifdef HAVE_LIBNCURSES - if (conf.get ("curses", true)) - { - WINDOW* w = initscr (); - width = w->_maxx + 1; - endwin (); - } -#endif - - // Get all the tasks. - std::vector tasks; - tdb.pendingT (tasks); - - initializeColorRules (conf); - - // Create a table for output. - Table table; - table.setTableWidth (width); - table.addColumn ("ID"); - table.addColumn ("Project"); - table.addColumn ("Pri"); - table.addColumn ("Due"); - table.addColumn ("Description"); - - table.setColumnUnderline (0); - table.setColumnUnderline (1); - table.setColumnUnderline (2); - table.setColumnUnderline (3); - table.setColumnUnderline (4); - - table.setColumnWidth (0, Table::minimum); - table.setColumnWidth (1, Table::minimum); - table.setColumnWidth (2, Table::minimum); - table.setColumnWidth (3, Table::minimum); - table.setColumnWidth (4, Table::flexible); - - table.setColumnJustification (0, Table::right); - table.setColumnJustification (3, Table::right); - - table.sortOn (3, Table::ascendingDate); - table.sortOn (2, Table::descendingPriority); - table.sortOn (1, Table::ascendingCharacter); - - // Split any description specified into words. - std::vector descWords; - split (descWords, task.getDescription (), ' '); - - // Get all the tags to match against. - std::vector tagList; - task.getTags (tagList); - - // Get all the attributes to match against. - std::map attrList; - task.getAttributes (attrList); - - Date now; - - // Iterate over each task, and apply selection criteria. - for (unsigned int i = 0; i < tasks.size (); ++i) - { - T refTask (tasks[i]); - std::string due; - if ((due = refTask.getAttribute ("due")) != "") - { - if (due.length () && due.find ("/") == std::string::npos) - { - Date dt (::atoi (due.c_str ())); - int m, d, y; - dt.toMDY (m, d, y); - char formatted[12]; - sprintf (formatted, "%d/%d/%04d", m, d, y); - due = formatted; - - // If overdue. - if (dt < now) - { - // All criteria match, so add refTask to the output table. - int row = table.addRow (); - table.addCell (row, 0, refTask.getId ()); - table.addCell (row, 1, refTask.getAttribute ("project")); - table.addCell (row, 2, refTask.getAttribute ("priority")); - table.addCell (row, 3, due); - table.addCell (row, 4, refTask.getDescription ()); - - if (conf.get ("color", true)) - { - Text::color fg = Text::colorCode (refTask.getAttribute ("fg")); - Text::color bg = Text::colorCode (refTask.getAttribute ("bg")); - autoColorize (refTask, fg, bg); - table.setRowFg (row, fg); - table.setRowBg (row, bg); - - if (fg == Text::nocolor) - table.setCellFg (row, 3, Text::red); - } - } - } - } - } - - if (table.rowCount ()) - std::cout << std::endl - << table.render () - << std::endl - << table.rowCount () - << (table.rowCount () == 1 ? " task" : " tasks") - << std::endl; - else - std::cout << "No overdue tasks." << std::endl; -} - -//////////////////////////////////////////////////////////////////////////////// -void handleReportStats (const TDB& tdb, T& task, Config& conf) -{ - // Get all the tasks. - std::vector tasks; - tdb.allT (tasks); - - Date now; - time_t earliest = time (NULL); - time_t latest = 1; - int totalT = 0; - int deletedT = 0; - int pendingT = 0; - int completedT = 0; - int taggedT = 0; - float daysPending = 0.0; - int descLength = 0; - - std::vector ::iterator it; - for (it = tasks.begin (); it != tasks.end (); ++it) - { - ++totalT; - if (it->getStatus () == T::deleted) ++deletedT; - if (it->getStatus () == T::pending) ++pendingT; - if (it->getStatus () == T::completed) ++completedT; - - time_t entry = ::atoi (it->getAttribute ("entry").c_str ()); - if (entry < earliest) earliest = entry; - if (entry > latest) latest = entry; - - if (it->getStatus () == T::completed) - { - time_t end = ::atoi (it->getAttribute ("end").c_str ()); - daysPending += (end - entry) / 86400.0; - } - - if (it->getStatus () == T::pending) - daysPending += (now - entry) / 86400.0; - - descLength += it->getDescription ().length (); - - std::vector tags; - it->getTags (tags); - if (tags.size ()) ++taggedT; - } - - // TODO Unused feature list - - std::cout << "Pending " << pendingT << std::endl - << "Completed " << completedT << std::endl - << "Deleted " << deletedT << std::endl - << "Total " << totalT << std::endl; - - Date e (earliest); - std::cout << "Oldest task " << e.toString () << std::endl; - Date l (latest); - std::cout << "Newest task " << l.toString () << std::endl; - std::cout << "Task used for " << formatSeconds (latest - earliest) << std::endl; - - if (totalT) - std::cout << "Task added every " << formatSeconds ((latest - earliest) / totalT) << std::endl; - - if (completedT) - std::cout << "Task completed every " << formatSeconds ((latest - earliest) / completedT) << std::endl; - - if (deletedT) - std::cout << "Task deleted every " << formatSeconds ((latest - earliest) / deletedT) << std::endl; - - if (pendingT || completedT) - std::cout << "Average time pending " << formatSeconds ((int) ((daysPending / (pendingT + completedT)) * 86400)) << std::endl; - - if (totalT) - { - std::cout << "Average desc length " << (int) (descLength / totalT) << " characters" << std::endl; - std::cout << "Tasks tagged " << std::setprecision (3) << (100.0 * taggedT / totalT) << "%" << std::endl; - } -} - -//////////////////////////////////////////////////////////////////////////////// -void handleVersion (Config& conf) -{ - // Determine window size, and set table accordingly. - int width = 80; -#ifdef HAVE_LIBNCURSES - if (conf.get ("curses", true)) - { - WINDOW* w = initscr (); - width = w->_maxx + 1; - endwin (); - } -#endif - - // Handle case for zero width on mobile device. - if (width == 0) - width = 80; - - // Create a table for output. - Table table; - table.setTableWidth (width); - table.addColumn ("Config variable"); - table.addColumn ("Value"); - table.setColumnUnderline (0); - table.setColumnUnderline (1); - table.setColumnWidth (0, Table::minimum); - table.setColumnWidth (1, Table::minimum); - table.setColumnJustification (0, Table::left); - table.setColumnJustification (1, Table::left); - table.sortOn (0, Table::ascendingCharacter); - - std::vector all; - conf.all (all); - foreach (i, all) - { - std::string value = conf.get (*i); - if (value != "") - { - int row = table.addRow (); - table.addCell (row, 0, *i); - table.addCell (row, 1, value); - } - } - - std::cout << "Copyright (C) 2006 - 2008 Paul Beckingham." - << std::endl - << PACKAGE - << " " - << VERSION - << std::endl - << std::endl - << table.render () - << std::endl; - - // Verify installation. This is mentioned in the documentation as the way to - // ensure everything is properly installed. - - if (all.size () == 0) - std::cout << "Configuration error: .taskrc contains no entries" - << std::endl; - else - { - if (conf.get ("data.location") == "") - std::cout << "Configuration error: data.location not specified in .taskrc " - "file." - << std::endl; - - if (access (conf.get ("data.location").c_str (), X_OK)) - std::cout << "Configuration error: data.location contains a directory name" - " that doesn't exist, or is unreadable." - << std::endl; - } -} - -//////////////////////////////////////////////////////////////////////////////// -void handleDelete (const TDB& tdb, T& task, Config& conf) -{ - if (conf.get ("confirmation") != "yes" || confirm ("Permanently delete task?")) - tdb.deleteT (task); - else - std::cout << "Task not deleted." << std::endl; -} - -//////////////////////////////////////////////////////////////////////////////// -void handleStart (const TDB& tdb, T& task, Config& conf) -{ - std::vector all; - tdb.pendingT (all); - - std::vector ::iterator it; - for (it = all.begin (); it != all.end (); ++it) - { - if (it->getId () == task.getId ()) - { - T original (*it); - - if (original.getAttribute ("start") == "") - { - char startTime[16]; - sprintf (startTime, "%u", (unsigned int) time (NULL)); - original.setAttribute ("start", startTime); - - original.setId (task.getId ()); - tdb.modifyT (original); - - nag (tdb, task, conf); - return; - } - else - std::cout << "Task " << task.getId () << " already started." << std::endl; - } - } - - throw std::string ("Task not found."); -} - -//////////////////////////////////////////////////////////////////////////////// -void handleDone (const TDB& tdb, T& task, Config& conf) -{ - if (!tdb.completeT (task)) - throw std::string ("Could not mark task as completed."); - - nag (tdb, task, conf); -} - -//////////////////////////////////////////////////////////////////////////////// -void handleExport (const TDB& tdb, T& task, Config& conf) -{ - std::string file = trim (task.getDescription ()); - if (file.length () > 0) - { - std::ofstream out (file.c_str ()); - if (out.good ()) - { - out << "'id'," - << "'status'," - << "'tags'," - << "'entry'," - << "'start'," - << "'due'," - << "'end'," - << "'project'," - << "'priority'," - << "'fg'," - << "'bg'," - << "'description'" - << "\n"; - - std::vector all; - tdb.allT (all); - foreach (t, all) - { - out << t->composeCSV ().c_str (); - } - out.close (); - } - else - throw std::string ("Could not write to export file."); - } - else - throw std::string ("You must specify a file to write to."); -} - -//////////////////////////////////////////////////////////////////////////////// -void handleModify (const TDB& tdb, T& task, Config& conf) -{ - std::vector all; - tdb.pendingT (all); - - std::vector ::iterator it; - for (it = all.begin (); it != all.end (); ++it) - { - if (it->getId () == task.getId ()) - { - T original (*it); - - // A non-zero value forces a file write. - int changes = 0; - - // Apply a new description, if any. - if (task.getDescription () != "") - { - original.setDescription (task.getDescription ()); - ++changes; - } - - // Apply or remove tags, if any. - std::vector tags; - task.getTags (tags); - for (unsigned int i = 0; i < tags.size (); ++i) - { - if (tags[i][0] == '+') - original.addTag (tags[i].substr (1, std::string::npos)); - else - original.addTag (tags[i]); - - ++changes; - } - - task.getRemoveTags (tags); - for (unsigned int i = 0; i < tags.size (); ++i) - { - if (tags[i][0] == '-') - original.removeTag (tags[i].substr (1, std::string::npos)); - else - original.removeTag (tags[i]); - - ++changes; - } - - // Apply or remove attributes, if any. - std::map attributes; - task.getAttributes (attributes); - foreach (i, attributes) - { - if (i->second == "") - original.removeAttribute (i->first); - else - original.setAttribute (i->first, i->second); - - ++changes; - } - - std::string from; - std::string to; - task.getSubstitution (from, to); - if (from != "") - { - std::string description = original.getDescription (); - size_t pattern = description.find (from); - if (pattern != std::string::npos) - { - description = description.substr (0, pattern) + - to + - description.substr (pattern + from.length (), std::string::npos); - original.setDescription (description); - ++changes; - } - } - - if (changes) - { - original.setId (task.getId ()); - tdb.modifyT (original); - } - - return; - } - } - - throw std::string ("Task not found."); -} - -//////////////////////////////////////////////////////////////////////////////// -void handleColor (Config& conf) -{ - std::cout << std::endl << "Foreground" << std::endl - << " " - << Text::colorize (Text::bold, Text::nocolor, "bold") << " " - << Text::colorize (Text::underline, Text::nocolor, "underline") << " " - << Text::colorize (Text::bold_underline, Text::nocolor, "bold_underline") << std::endl - - << " " << Text::colorize (Text::black, Text::nocolor, "black") << " " - << Text::colorize (Text::bold_black, Text::nocolor, "bold_black") << " " - << Text::colorize (Text::underline_black, Text::nocolor, "underline_black") << " " - << Text::colorize (Text::bold_underline_black, Text::nocolor, "bold_underline_black") << std::endl - - << " " << Text::colorize (Text::red, Text::nocolor, "red") << " " - << Text::colorize (Text::bold_red, Text::nocolor, "bold_red") << " " - << Text::colorize (Text::underline_red, Text::nocolor, "underline_red") << " " - << Text::colorize (Text::bold_underline_red, Text::nocolor, "bold_underline_red") << std::endl - - << " " << Text::colorize (Text::green, Text::nocolor, "green") << " " - << Text::colorize (Text::bold_green, Text::nocolor, "bold_green") << " " - << Text::colorize (Text::underline_green, Text::nocolor, "underline_green") << " " - << Text::colorize (Text::bold_underline_green, Text::nocolor, "bold_underline_green") << std::endl - - << " " << Text::colorize (Text::yellow, Text::nocolor, "yellow") << " " - << Text::colorize (Text::bold_yellow, Text::nocolor, "bold_yellow") << " " - << Text::colorize (Text::underline_yellow, Text::nocolor, "underline_yellow") << " " - << Text::colorize (Text::bold_underline_yellow, Text::nocolor, "bold_underline_yellow") << std::endl - - << " " << Text::colorize (Text::blue, Text::nocolor, "blue") << " " - << Text::colorize (Text::bold_blue, Text::nocolor, "bold_blue") << " " - << Text::colorize (Text::underline_blue, Text::nocolor, "underline_blue") << " " - << Text::colorize (Text::bold_underline_blue, Text::nocolor, "bold_underline_blue") << std::endl - - << " " << Text::colorize (Text::magenta, Text::nocolor, "magenta") << " " - << Text::colorize (Text::bold_magenta, Text::nocolor, "bold_magenta") << " " - << Text::colorize (Text::underline_magenta, Text::nocolor, "underline_magenta") << " " - << Text::colorize (Text::bold_underline_magenta, Text::nocolor, "bold_underline_magenta") << std::endl - - << " " << Text::colorize (Text::cyan, Text::nocolor, "cyan") << " " - << Text::colorize (Text::bold_cyan, Text::nocolor, "bold_cyan") << " " - << Text::colorize (Text::underline_cyan, Text::nocolor, "underline_cyan") << " " - << Text::colorize (Text::bold_underline_cyan, Text::nocolor, "bold_underline_cyan") << std::endl - - << " " << Text::colorize (Text::white, Text::nocolor, "white") << " " - << Text::colorize (Text::bold_white, Text::nocolor, "bold_white") << " " - << Text::colorize (Text::underline_white, Text::nocolor, "underline_white") << " " - << Text::colorize (Text::bold_underline_white, Text::nocolor, "bold_underline_white") << std::endl - - << std::endl << "Background" << std::endl - << " " << Text::colorize (Text::nocolor, Text::on_black, "on_black") << " " - << Text::colorize (Text::nocolor, Text::on_bright_black, "on_bright_black") << std::endl - - << " " << Text::colorize (Text::nocolor, Text::on_red, "on_red") << " " - << Text::colorize (Text::nocolor, Text::on_bright_red, "on_bright_red") << std::endl - - << " " << Text::colorize (Text::nocolor, Text::on_green, "on_green") << " " - << Text::colorize (Text::nocolor, Text::on_bright_green, "on_bright_green") << std::endl - - << " " << Text::colorize (Text::nocolor, Text::on_yellow, "on_yellow") << " " - << Text::colorize (Text::nocolor, Text::on_bright_yellow, "on_bright_yellow") << std::endl - - << " " << Text::colorize (Text::nocolor, Text::on_blue, "on_blue") << " " - << Text::colorize (Text::nocolor, Text::on_bright_blue, "on_bright_blue") << std::endl - - << " " << Text::colorize (Text::nocolor, Text::on_magenta, "on_magenta") << " " - << Text::colorize (Text::nocolor, Text::on_bright_magenta, "on_bright_magenta") << std::endl - - << " " << Text::colorize (Text::nocolor, Text::on_cyan, "on_cyan") << " " - << Text::colorize (Text::nocolor, Text::on_bright_cyan, "on_bright_cyan") << std::endl - - << " " << Text::colorize (Text::nocolor, Text::on_white, "on_white") << " " - << Text::colorize (Text::nocolor, Text::on_bright_white, "on_bright_white") << std::endl - - << std::endl; -} - -//////////////////////////////////////////////////////////////////////////////// -void gatherNextTasks ( - const TDB& tdb, - T& task, - Config& conf, - std::vector & pending, - std::vector & all) -{ - // For counting tasks by project. - std::map countByProject; - std::map matching; - - Date now; - - // How many items per project? Default 3. - int limit = conf.get ("next", 3); - - // due:< 1wk, pri:* - for (unsigned int i = 0; i < pending.size (); ++i) - { - if (pending[i].getStatus () == T::pending) - { - std::string due = pending[i].getAttribute ("due"); - if (due != "") - { - Date d (::atoi (due.c_str ())); - if (d < now + (7 * 24 * 60 * 60)) // if due:< 1wk - { - std::string project = pending[i].getAttribute ("project"); - if (countByProject[project] < limit && matching.find (i) == matching.end ()) - { - ++countByProject[project]; - matching[i] = true; - } - } - } - } - } - - // due:*, pri:H - for (unsigned int i = 0; i < pending.size (); ++i) - { - if (pending[i].getStatus () == T::pending) - { - std::string due = pending[i].getAttribute ("due"); - if (due != "") - { - std::string priority = pending[i].getAttribute ("priority"); - if (priority == "H") - { - std::string project = pending[i].getAttribute ("project"); - if (countByProject[project] < limit && matching.find (i) == matching.end ()) - { - ++countByProject[project]; - matching[i] = true; - } - } - } - } - } - - // pri:H - for (unsigned int i = 0; i < pending.size (); ++i) - { - if (pending[i].getStatus () == T::pending) - { - std::string priority = pending[i].getAttribute ("priority"); - if (priority == "H") - { - std::string project = pending[i].getAttribute ("project"); - if (countByProject[project] < limit && matching.find (i) == matching.end ()) - { - ++countByProject[project]; - matching[i] = true; - } - } - } - } - - // due:*, pri:M - for (unsigned int i = 0; i < pending.size (); ++i) - { - if (pending[i].getStatus () == T::pending) - { - std::string due = pending[i].getAttribute ("due"); - if (due != "") - { - std::string priority = pending[i].getAttribute ("priority"); - if (priority == "M") - { - std::string project = pending[i].getAttribute ("project"); - if (countByProject[project] < limit && matching.find (i) == matching.end ()) - { - ++countByProject[project]; - matching[i] = true; - } - } - } - } - } - - // pri:M - for (unsigned int i = 0; i < pending.size (); ++i) - { - if (pending[i].getStatus () == T::pending) - { - std::string priority = pending[i].getAttribute ("priority"); - if (priority == "M") - { - std::string project = pending[i].getAttribute ("project"); - if (countByProject[project] < limit && matching.find (i) == matching.end ()) - { - ++countByProject[project]; - matching[i] = true; - } - } - } - } - - // due:*, pri:L - for (unsigned int i = 0; i < pending.size (); ++i) - { - if (pending[i].getStatus () == T::pending) - { - std::string due = pending[i].getAttribute ("due"); - if (due != "") - { - std::string priority = pending[i].getAttribute ("priority"); - if (priority == "L") - { - std::string project = pending[i].getAttribute ("project"); - if (countByProject[project] < limit && matching.find (i) == matching.end ()) - { - ++countByProject[project]; - matching[i] = true; - } - } - } - } - } - - // pri:L - for (unsigned int i = 0; i < pending.size (); ++i) - { - if (pending[i].getStatus () == T::pending) - { - std::string priority = pending[i].getAttribute ("priority"); - if (priority == "L") - { - std::string project = pending[i].getAttribute ("project"); - if (countByProject[project] < limit && matching.find (i) == matching.end ()) - { - ++countByProject[project]; - matching[i] = true; - } - } - } - } - - // due:, pri: - for (unsigned int i = 0; i < pending.size (); ++i) - { - if (pending[i].getStatus () == T::pending) - { - std::string due = pending[i].getAttribute ("due"); - if (due == "") - { - std::string priority = pending[i].getAttribute ("priority"); - if (priority == "") - { - std::string project = pending[i].getAttribute ("project"); - if (countByProject[project] < limit && matching.find (i) == matching.end ()) - { - ++countByProject[project]; - matching[i] = true; - } - } - } - } - } - - // Convert map to vector. - foreach (i, matching) - all.push_back (i->first); -} - -//////////////////////////////////////////////////////////////////////////////// -void nag (const TDB& tdb, T& task, Config& conf) -{ - std::string nagMessage = conf.get ("nag", std::string ("")); - if (nagMessage != "") - { - // Load all pending. - std::vector pending; - tdb.allPendingT (pending); - - // Restrict to matching subset. - std::vector matching; - gatherNextTasks (tdb, task, conf, pending, matching); - - foreach (i, matching) - if (pending[*i].getId () == task.getId ()) - return; - - std::cout << nagMessage << std::endl; - } -} - -//////////////////////////////////////////////////////////////////////////////// -