From 78fae5195b7b70ed030d5a42a8135176be9f7041 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Sat, 20 Sep 2008 20:24:58 -0400 Subject: [PATCH 01/10] - Converted 1.4.2 references to 1.5.0. --- ChangeLog | 7 +++++-- Makefile.am | 2 +- NEWS | 2 +- TUTORIAL | 6 ------ configure.ac | 2 +- html/README | 14 -------------- html/task.html | 33 ++++++--------------------------- html/versions.html | 30 ++++++++++++++++++++++++++++++ script.txt | 2 +- 9 files changed, 45 insertions(+), 53 deletions(-) delete mode 100644 TUTORIAL delete mode 100644 html/README diff --git a/ChangeLog b/ChangeLog index 756fddc64..c8bf8b17c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,11 @@ represents a feature release, and the Z represents a patch. ------ current release --------------------------- +1.5.0 (?) + + Removed deprecated TUTORIAL file. + +------ old releases ------------------------------ + 1.4.2 (9/18/2008) + "task undo" can now retract a "task done" command, provided no reports have been run (and therefore TDB::gc run) @@ -30,8 +35,6 @@ represents a feature release, and the Z represents a patch. + Bug: Source now properly includes in order to build clean using gcc 4.3 (thanks to H. İbrahim Güngör) ------- old releases ------------------------------ - 1.4.1 (7/18/2008) + Bug: Descriptions can not be altered with "task 123 New description" + Tweak: For "task calendar" month names are now centered over the month diff --git a/Makefile.am b/Makefile.am index 685ed0a57..fb05a3a4a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,3 +1,3 @@ SUBDIRS = src -EXTRA_DIST = TUTORIAL DEVELOPERS +EXTRA_DIST = DEVELOPERS diff --git a/NEWS b/NEWS index 18b4251e8..7698ef623 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,4 @@ -Welcome to Task 1.4.0. +Welcome to Task 1.5.0. Task has been built and tested on the following configurations: diff --git a/TUTORIAL b/TUTORIAL deleted file mode 100644 index 7f635f4c2..000000000 --- a/TUTORIAL +++ /dev/null @@ -1,6 +0,0 @@ - -This TUTORIAL file has been deprecated. It is superceded by a richer and more -extensive online version that can be found at: - - http://www.beckingham.net/task.html - diff --git a/configure.ac b/configure.ac index 9d7bc2d33..e89a1aa53 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.61) -AC_INIT(task, 1.4.2, bugs@beckingham.net) +AC_INIT(task, 1.5.0, bugs@beckingham.net) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([src/task.cpp]) AC_CONFIG_HEADER([auto.h]) diff --git a/html/README b/html/README deleted file mode 100644 index ecd393fc9..000000000 --- a/html/README +++ /dev/null @@ -1,14 +0,0 @@ -Documentation Restructuring - high level pages - download - previous verisons - tutorial - recurrence - priorities - subprojects - tags - - - need page banner - need task link off beckinghma.net - diff --git a/html/task.html b/html/task.html index 5218402bf..2d370c8fb 100644 --- a/html/task.html +++ b/html/task.html @@ -1,7 +1,7 @@ - Task 1.4.2 + Task 1.5.0 @@ -75,12 +75,12 @@ - +
Source:task-1.4.2.tar.gztask-1.5.0.tar.gz
-

New in version 1.4.2 (9/18/2008)

+

New in version 1.5.0 (?)

diff --git a/html/versions.html b/html/versions.html index 81bdcdecc..6c52c60cc 100644 --- a/html/versions.html +++ b/html/versions.html @@ -35,6 +35,36 @@

+

+

New in version 1.4.2 (9/18/2008)

+ task-1.4.2.tar.gz +

+ +
    +
  • "task undo" can now retract a "task done" command, provided no + reports have been run. +
  • Task now correctly sorts on entire strings, instead of just the + first character (thanks to Andy Lester). +
  • Task now uses dashes (-----) to underline column headings when + color is disabled (thanks to Vincent Fleuranceau). +
  • Task now allows mixed case attribute names (pri:, PRI:, Pri: ...) + and commands (add, ADD, Add ...) (thanks to Vincent Fleuranceau). +
  • Task now supports a default project and priority for new tasks, via + the new "default.project" and "default.priority" configuration variables + (thanks to Vincent Fleuranceau). +
  • Task supports improved word-wrapping to the terminal width. +
  • Task now supports "default.command" configuration variable (for example + it could contain "list due:tomorrow") which is the command that is run + whenever task is invoked with no arguments. +
  • Task supports modifying the existing description of a task, with the + following syntax: task <id> "new description ...". +
  • Fixed bug so that relative dates in filters (task list due:eom, + task list due:tomorrow, task list due:23rd ...) are now properly + supported. +
  • Fixed bug so that source now properly includes <string.h> in + order to build clean using gcc 4.3 (thanks to H. İbrahim Güngör) +
+

New in version 1.4.1 (7/18/2008)

task-1.4.1.tar.gz diff --git a/script.txt b/script.txt index e94233329..e7d1913e6 100644 --- a/script.txt +++ b/script.txt @@ -98,7 +98,7 @@ task history History shows general activity - how completed etc, by month And that's it. There are more commands than this - covered in the TUTORIAL file, but this should give + covered in the online documentation, but this should give the basic idea. Thank you for watching. From d837a25be735b3c47f8b039feb12834ccb640d5d Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Sat, 20 Sep 2008 20:46:20 -0400 Subject: [PATCH 02/10] - Removed support for the "command.logging" configuration variable and the "task usage" command. --- ChangeLog | 8 ++--- html/advanced.html | 13 -------- html/config.html | 7 ----- html/task.html | 4 ++- html/usage.html | 2 +- src/Config.cpp | 1 - src/TDB.cpp | 54 --------------------------------- src/TDB.h | 2 -- src/parse.cpp | 1 - src/report.cpp | 75 ---------------------------------------------- src/task.cpp | 9 ------ src/task.h | 1 - 12 files changed, 6 insertions(+), 171 deletions(-) diff --git a/ChangeLog b/ChangeLog index c8bf8b17c..c9f0cdaab 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,14 +1,10 @@ -Version numbers are of the form: - - X.Y.Z - -where the X represents a major version number, or architecture. The Y -represents a feature release, and the Z represents a patch. ------ current release --------------------------- 1.5.0 (?) + Removed deprecated TUTORIAL file. + + Removed "usage" command, and support for "command.logging" configuration + variable. ------ old releases ------------------------------ diff --git a/html/advanced.html b/html/advanced.html index 126bbf3a2..07f293754 100644 --- a/html/advanced.html +++ b/html/advanced.html @@ -313,19 +313,6 @@ ID Project Pri Description This command displays all the colors that task supports.

- % task usage -

- If logging has been enabled by the "command.logging=on" directive - in the .taskrc file, then task will record every command that is - run. When this command is run, task will display a count of how - many times each command was used. -

- -

- This command is for the purpose of seeing whether command are - actually used. -

- % task version

This can be used to show the version number of task, and to display diff --git a/html/config.html b/html/config.html index 251766af1..37cd127b8 100644 --- a/html/config.html +++ b/html/config.html @@ -51,13 +51,6 @@ /Users/paul/.task -

command.logging
-
- May be "on" or "off", defaulting to "off". This determines - whether task records commands. This is not generally useful, - except while developing task. -
-
confirmation
May be "yes" or "no", and determines whether task will ask for diff --git a/html/task.html b/html/task.html index 2d370c8fb..7609c422d 100644 --- a/html/task.html +++ b/html/task.html @@ -96,7 +96,9 @@

New in version 1.5.0 (?)

    -
  • +
  • Removed deprecated TUTORIAL file. +
  • Removed "usage" command, and support for "command.logging" configuration + variable.

diff --git a/html/usage.html b/html/usage.html index 1a5b58d0a..97b98f5df 100644 --- a/html/usage.html +++ b/html/usage.html @@ -45,6 +45,7 @@ task info ID task start ID task done ID + task undo ID task projects task tags task summary @@ -57,7 +58,6 @@ task oldest task newest task stats - task usage task export task color task version diff --git a/src/Config.cpp b/src/Config.cpp index 6fe00db3a..81bfcb6d2 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -112,7 +112,6 @@ void Config::createDefault (const std::string& home) if ((out = fopen (rcFile.c_str (), "w"))) { fprintf (out, "data.location=%s\n", dataDir.c_str ()); - fprintf (out, "command.logging=off\n"); fprintf (out, "confirmation=yes\n"); fprintf (out, "next=2\n"); fprintf (out, "dateformat=m/d/Y\n"); diff --git a/src/TDB.cpp b/src/TDB.cpp index abb3812ae..dcced05af 100644 --- a/src/TDB.cpp +++ b/src/TDB.cpp @@ -37,7 +37,6 @@ TDB::TDB () : mPendingFile ("") , mCompletedFile ("") -, mLogFile ("") , mId (1) { } @@ -54,7 +53,6 @@ void TDB::dataDirectory (const std::string& directory) { mPendingFile = directory + "/pending.data"; mCompletedFile = directory + "/completed.data"; - mLogFile = directory + "/command.log"; } else { @@ -286,58 +284,6 @@ bool TDB::modifyT (const T& t) return overwritePending (pending); } -//////////////////////////////////////////////////////////////////////////////// -bool TDB::logRead (std::vector & entries) const -{ - entries.clear (); - return readLockedFile (mLogFile, entries); -} - -//////////////////////////////////////////////////////////////////////////////// -bool TDB::logCommand (int argc, char** argv) const -{ - // Get time info. - time_t now; - time (&now); - struct tm* t = localtime (&now); - - // Generate timestamp. - char timestamp[20]; - sprintf (timestamp, "%04d-%02d-%02d %02d:%02d:%02d", - t->tm_year + 1900, - t->tm_mon + 1, - t->tm_mday, - t->tm_hour, - t->tm_min, - t->tm_sec); - - std::string command = timestamp; - command += " \""; - for (int i = 0; i < argc; ++i) - command += std::string (i ? " " : "") + argv[i]; - command += "\"\n"; - - if (! access (mLogFile.c_str (), F_OK | W_OK)) - { - FILE* out; - if ((out = fopen (mLogFile.c_str (), "a"))) - { -#ifdef HAVE_FLOCK - int retry = 0; - while (flock (fileno (out), LOCK_EX) && ++retry <= 3) - delay (0.25); -#endif - - fputs (command.c_str (), out); - - fclose (out); - return true; - } - } - - return false; -} - //////////////////////////////////////////////////////////////////////////////// bool TDB::lock (FILE* file) const { diff --git a/src/TDB.h b/src/TDB.h index ccdeea788..9ceb9b3d8 100644 --- a/src/TDB.h +++ b/src/TDB.h @@ -48,7 +48,6 @@ public: bool addT (const T&) const; bool modifyT (const T&); bool logRead (std::vector &) const; - bool logCommand (int, char**) const; int gc (); int nextId (); @@ -62,7 +61,6 @@ private: private: std::string mPendingFile; std::string mCompletedFile; - std::string mLogFile; int mId; }; diff --git a/src/parse.cpp b/src/parse.cpp index c468a822c..bf835ac55 100644 --- a/src/parse.cpp +++ b/src/parse.cpp @@ -142,7 +142,6 @@ static const char* commands[] = "tags", "undelete", "undo", - "usage", "version", "", }; diff --git a/src/report.cpp b/src/report.cpp index c82abeaac..5c1d8ac8f 100644 --- a/src/report.cpp +++ b/src/report.cpp @@ -1594,81 +1594,6 @@ void handleReportGHistory (TDB& tdb, T& task, Config& conf) 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, conf); - - 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"); - - if (conf.get ("color", true)) - { - table.setColumnUnderline (0); - table.setColumnUnderline (1); - } - else - table.setTableDashedUnderline (); - - table.setColumnJustification (1, Table::right); - table.sortOn (1, Table::descendingNumeric); - table.setDateFormat (conf.get ("dateformat", "m/d/Y")); - - 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 << optionalBlankLine (conf) - << 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; -} - //////////////////////////////////////////////////////////////////////////////// std::string renderMonths ( int firstMonth, diff --git a/src/task.cpp b/src/task.cpp index 29951dee3..e53f9675f 100644 --- a/src/task.cpp +++ b/src/task.cpp @@ -178,10 +178,6 @@ static void shortUsage (Config& conf) 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"); @@ -296,10 +292,6 @@ int main (int argc, char** argv) TDB tdb; tdb.dataDirectory (expandPath (conf.get ("data.location"))); - // Log commands, if desired. - if (conf.get ("command.logging") == "on") - tdb.logCommand (argc, argv); - // If argc == 1 and the default.command configuration variable is set, // then use that, otherwise stick with argc/argv. std::vector args; @@ -347,7 +339,6 @@ int main (int argc, char** argv) else if (command == "oldest") handleReportOldest (tdb, task, conf); else if (command == "newest") handleReportNewest (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 if (command == "help") longUsage (conf); else shortUsage (conf); diff --git a/src/task.h b/src/task.h index 7b732f423..9c417435b 100644 --- a/src/task.h +++ b/src/task.h @@ -92,7 +92,6 @@ void handleReportSummary (TDB&, T&, Config&); void handleReportNext (TDB&, T&, Config&); void handleReportHistory (TDB&, T&, Config&); void handleReportGHistory (TDB&, T&, Config&); -void handleReportUsage (const TDB&, T&, Config&); void handleReportCalendar (TDB&, T&, Config&); void handleReportActive (TDB&, T&, Config&); void handleReportOverdue (TDB&, T&, Config&); From f26a9c67be807b46da80e951e4d5b3573d26bd2c Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Fri, 26 Sep 2008 22:25:38 -0400 Subject: [PATCH 03/10] - Built 1.4.2 and packaged it as a Mac OS X Leopard Intel binary - Modified incorrect Debian packages in versions.html - Added Debian and Mac packages to task.html --- .gitignore | 2 +- binary/COPYING.txt | 281 ++++++++++++++++++++++++++++++++++++++++++ binary/README.txt | 51 ++++++++ html/task.html | 4 - html/versions.html | 5 +- task.pmdoc/01task.xml | 2 +- task.pmdoc/index.xml | 2 +- 7 files changed, 339 insertions(+), 8 deletions(-) create mode 100644 binary/COPYING.txt create mode 100644 binary/README.txt diff --git a/.gitignore b/.gitignore index 36f7d8f5a..c61760acc 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,7 @@ config.h.in config.status src/.deps src/Makefile -src/task +*/task stamp-h1 Makefile configure diff --git a/binary/COPYING.txt b/binary/COPYING.txt new file mode 100644 index 000000000..627d3eb39 --- /dev/null +++ b/binary/COPYING.txt @@ -0,0 +1,281 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + diff --git a/binary/README.txt b/binary/README.txt new file mode 100644 index 000000000..01b01accd --- /dev/null +++ b/binary/README.txt @@ -0,0 +1,51 @@ +Thank you for taking a look at task. Task is a GTD utility featuring: + + - Robust C++ implementation + - Tags + - Colorful, tabular output + - Reports, graphs + - Lots of commands + - Low-level API + - Abbreviations for all commands, options + - Multi-user file locking + - Clean architecture allowing quick addition of new features + - Recurring tasks + +It is intended that features, mainly in the form of reports will be added +frequently, with best practices and useful reports evolving from usage patterns. + +Task is scope-limited to GTD functionality only. + +You may want to watch the old task movie on YouTube: + + http://www.youtube.com/watch?v=l68LCl6BYvs + +or the new improved one: + + http://www.youtube.com/watch?v=D2Kn4DMOVSw + +Either will give you a fairly good idea of what task is capable of, and +whether it fits in to your way of working. As a command line application, +task is not for everyone and some of you may prefer to not proceed. The +movie or online tutorial file are the quickest way for you to make that +decision. The online tutorial can be found at: + + http://www.beckingham.net/task.html + +Task is based on ideas presented in the todo.sh script, found on: + + http://todotxt.org + +Task has many more features than todo.sh, but fundamentally, they are +both working toward the same goals, which is to help you follow basic +Getting Things Done (GTD) principles. + +All feedback is welcome, in addition to any bug reports or patches to: + + task@beckingham.net + +Got an idea for an enhancement? Send a message! + +I have found that task makes me more productive and organized. +I hope task can do the same for you. + diff --git a/html/task.html b/html/task.html index 5218402bf..33a50887f 100644 --- a/html/task.html +++ b/html/task.html @@ -77,13 +77,10 @@ Source: task-1.4.2.tar.gz - -

New in version 1.4.2 (9/18/2008)

diff --git a/html/versions.html b/html/versions.html index 81bdcdecc..45266a634 100644 --- a/html/versions.html +++ b/html/versions.html @@ -41,6 +41,9 @@
Mac OS X 10.5 (Leopard) Intel-only: task-1.4.1.pkg +
+ Debian package: task_1.4.1-1_i386.deb + (Thanks to Richard Querin)

    @@ -54,7 +57,7 @@

    New in version 1.4.0 (7/10/2008)

    Source: task-1.4.0.tar.gz
    - Debian package: task_1.4.1-1_i386.deb + Debian package: task_1.4.0-1_i386.deb (Thanks to Richard Querin)

    diff --git a/task.pmdoc/01task.xml b/task.pmdoc/01task.xml index 585107ecd..8e25f6b7a 100644 --- a/task.pmdoc/01task.xml +++ b/task.pmdoc/01task.xml @@ -1 +1 @@ -com.beckingham.task141.task.pkg1/Users/paul/task.git/binary/task/usr/local/binparentlocationTyperelocatableinstallTo.pathinstallTo01task-contents.xml/CVS$/\.svn$/\.cvsignore$/\.cvspass$/\.DS_Store$ \ No newline at end of file +com.beckingham.task142.task.pkg/Users/paul/task.git/binary/task/usr/local/binparentlocationTyperelocatableversioninstallTo.pathinstallTo01task-contents.xml/CVS$/\.svn$/\.cvsignore$/\.cvspass$/\.DS_Store$ \ No newline at end of file diff --git a/task.pmdoc/index.xml b/task.pmdoc/index.xml index 05fd05d2a..4b1ce006c 100644 --- a/task.pmdoc/index.xml +++ b/task.pmdoc/index.xml @@ -1 +1 @@ -Task 1.4.1/Users/paul/task.git/binary/task-1.4.1.pkgcom.beckingham/Users/paul/task.git/binary/COPYING.txt/Users/paul/task.git/binary/README.txt01task.xmlproperties.titleproperties.systemDomainproperties.anywhereDomain \ No newline at end of file +Task 1.4.2/Users/paul/Documents/task-1.4.2.pkgcom.beckingham/Users/paul/task.git/binary/COPYING.txt/Users/paul/task.git/binary/README.txt01task.xmlproperties.titleproperties.anywhereDomainproperties.systemDomain \ No newline at end of file From a815492111b819a61e93cace8f9bd76ef4f33b97 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Fri, 26 Sep 2008 22:30:42 -0400 Subject: [PATCH 04/10] - New links page referring to task links on the web. --- html/links.html | 101 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 html/links.html diff --git a/html/links.html b/html/links.html new file mode 100644 index 000000000..660206c77 --- /dev/null +++ b/html/links.html @@ -0,0 +1,101 @@ + + + + Task on the Web + + + + + +
    + + + + + + +
    + + +
    +
    +
    +
    +

    Task

    +

    + Task is an open source, command-line, TODO list manager. +

    + +

    + Here you will find information on how to acquire, build, configure, + use and become proficient with the task program. +

    + + + + +
    +
    +
    +

    + Copyright 2006-2008, P. Beckingham. All rights reserved. +

    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + +
    + +
    + + + + + + + From 82c0fea708ee27380d4ca6ed81e0019467179b20 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Sat, 1 Nov 2008 16:14:15 -0400 Subject: [PATCH 05/10] - Remove debugging code. --- src/task.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/task.cpp b/src/task.cpp index 9dbdb2b79..daabfc9db 100644 --- a/src/task.cpp +++ b/src/task.cpp @@ -679,13 +679,10 @@ void updateRecurrenceMask ( // Using gTdb and gConf, generate a report. void onChangeCallback () { - std::cout << "--- callback" << std::endl; try { if (gConf && gTdb) { - std::cout << "--- valid globals" << std::endl; - // Determine if shadow file is enabled. std::string shadowFile = expandPath (gConf->get ("shadow.file")); if (shadowFile != "") @@ -695,8 +692,6 @@ void onChangeCallback () gConf->set ("curses", "off"); gConf->set ("color", "off"); - std::cout << "--- shadowFile " << shadowFile<< std::endl; - // Run report. Use shadow.command, using default.command as a fallback // with "list" as a default. std::string command = gConf->get ("shadow.command", @@ -714,8 +709,6 @@ void onChangeCallback () else throw std::string ("Could not write file '") + shadowFile + "'"; - std::cout << "--- Complete " << std::endl; - gConf->set ("curses", oldCurses); gConf->set ("color", oldColor); } From 2cae1df42f7bc72fa1150baabd1152faf2ee4d42 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Sat, 1 Nov 2008 16:31:30 -0400 Subject: [PATCH 06/10] - Merged 1.4.3 to master --- AUTHORS | 1 + ChangeLog | 9 + NEWS | 2 +- TUTORIAL | 6 + announcement.txt | 36 ---- configure.ac | 4 +- html/30second.html | 2 +- html/advanced.html | 20 ++- html/color.html | 2 +- html/config.html | 38 +++- html/date.html | 2 +- html/filter.html | 92 ++++++++++ html/recur.html | 2 +- html/setup.html | 2 +- html/shadow.html | 98 ++++++++++ html/shell.html | 2 +- html/simple.html | 2 +- html/task.html | 22 +-- html/usage.html | 2 +- html/versions.html | 6 + src/.gitignore | 1 - src/TDB.cpp | 27 ++- src/TDB.h | 10 +- src/command.cpp | 277 ++++++++++++++++------------- src/report.cpp | 433 +++++++++++++++++++++++++++++---------------- src/task.cpp | 231 +++++++++++++++++------- src/task.h | 52 +++--- 27 files changed, 950 insertions(+), 431 deletions(-) create mode 100644 TUTORIAL delete mode 100644 announcement.txt create mode 100644 html/filter.html create mode 100644 html/shadow.html diff --git a/AUTHORS b/AUTHORS index d045cad9b..027de8b31 100644 --- a/AUTHORS +++ b/AUTHORS @@ -17,4 +17,5 @@ With thanks to: Stas Antons Vincent Fleuranceau T. Charles Yun + ArchiMark diff --git a/ChangeLog b/ChangeLog index c9f0cdaab..c2b641218 100644 --- a/ChangeLog +++ b/ChangeLog @@ -8,6 +8,15 @@ ------ old releases ------------------------------ +1.4.3 (10/9/2008) + + Fixed misleading task count at bottom on "info" report. + + Added support for a shadow file that contains a plain text task report, + with the "shadow.file" and "shadow.command" configuration variables. + The shadow file is automatically updated whenever the task database + changes. Useful for integrating with "Samurize". + +------ old releases ------------------------------ + 1.4.2 (9/18/2008) + "task undo" can now retract a "task done" command, provided no reports have been run (and therefore TDB::gc run) diff --git a/NEWS b/NEWS index 7698ef623..e936a4cb7 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,4 @@ -Welcome to Task 1.5.0. +Welcome to Task 1.4.3. Task has been built and tested on the following configurations: diff --git a/TUTORIAL b/TUTORIAL new file mode 100644 index 000000000..e389b623a --- /dev/null +++ b/TUTORIAL @@ -0,0 +1,6 @@ + +This TUTORIAL file has been deprecated. It is superseded by a richer and more +extensive online version that can be found at: + + http://www.beckingham.net/task.html + diff --git a/announcement.txt b/announcement.txt deleted file mode 100644 index 037106408..000000000 --- a/announcement.txt +++ /dev/null @@ -1,36 +0,0 @@ -Some considerable time ago - longer than I had hoped - I demonstrated an -alternate implementation of the todo script, called task, in the form of a -YouTube movie: - - http://www.youtube.com/watch?v=l68LCl6BYvs - -A lot has happened since then, and the task program has been slowly improving -thanks to feedback from some early testers, and continuous use by me. Today, -I have uploaded a new movie: - - http://www.youtube.com/watch?v=D2Kn4DMOVSw - -This movie includes most of the changes and improvements to task, but behind -the scenes are the biggest changes. There was a rewrite of the underlying -storage mechanism yielding a clean API for the front end, and the code was -reviewed for portability and converted to use GNU autoconf/automake. - -Task has been released under GPL, and so far has been tested on: - - Max OS X 10.4 (Tiger) - Max OS X 10.5 (Leopard) - Fedora 8 - Fedora 9 - Ubuntu 8 (Hardy Heron) - Solaris 10 - -Task has been making me more organized and productive for some time now. -Perhaps some of you might find it useful, and I welcome feedback of all kinds. - -You can find the task source code at: - - http://www.beckingham.net/task-1.0.0.tar.gz - -Thank you. -Paul Beckingham - diff --git a/configure.ac b/configure.ac index e89a1aa53..0c1c977b3 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.61) -AC_INIT(task, 1.5.0, bugs@beckingham.net) +AC_INIT(task, 1.4.3, bugs@beckingham.net) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([src/task.cpp]) AC_CONFIG_HEADER([auto.h]) @@ -19,7 +19,7 @@ AC_CHECK_LIB(ncurses,endwin) # Checks for header files. AC_HEADER_STDC AC_CHECK_HEADERS([stdlib.h sys/file.h sys/stat.h sys/time.h unistd.h]) -AC_CHECK_HEADERS([string vector map]) +AC_CHECK_HEADERS([sstream string vector map]) # Checks for typedefs, structures, and compiler characteristics. AC_HEADER_STDBOOL diff --git a/html/30second.html b/html/30second.html index f1450c650..227b0bbf2 100644 --- a/html/30second.html +++ b/html/30second.html @@ -1,7 +1,7 @@ - Task 1.4.1 + 30-Second Tutorial diff --git a/html/advanced.html b/html/advanced.html index 07f293754..97a344cdb 100644 --- a/html/advanced.html +++ b/html/advanced.html @@ -1,7 +1,7 @@ - Task 1.4.1 + Advanced Usage @@ -313,6 +313,24 @@ ID Project Pri Description This command displays all the colors that task supports.

    + % task usage +

    + If logging has been enabled by the "command.logging=on" directive + in the .taskrc file, then task will record every command that is + run. When this command is run, task will display a count of how + many times each command was used. + +

    + +

    + This command is for the purpose of seeing whether command are + actually used. +

    + +

    + This command is deprecated, and will be removed in task 1.5.0 +

    + % task version

    This can be used to show the version number of task, and to display diff --git a/html/color.html b/html/color.html index 72c70772b..f88357e0c 100644 --- a/html/color.html +++ b/html/color.html @@ -1,7 +1,7 @@ - Task 1.4.1 + Color Usage diff --git a/html/config.html b/html/config.html index 37cd127b8..144df2498 100644 --- a/html/config.html +++ b/html/config.html @@ -1,7 +1,7 @@ - Task 1.4.1 + Task Configuration @@ -279,6 +279,42 @@ ID Project Pri Description preceding "task" program name.

+ +
shadow.file
+
+

+ If specified, designates a file path that will be autoamtically + written to by task, whenever the task database changes. In other + words, it is automatically kept up to date. +

+ +

+ The shadow.command configuration variable is used to determine + which report is written to the shadow file. There is no color + used in the shadow file. +

+ +

+ This feature can be useful in maintaining a current file for + use by the "Samurize" program. +

+
+ +
shadow.command
+
+

+ This is the command that is run to maintain the shadow file, + determined by the shadow.file configuration variable. The + format is identical to that of default.command - please see + the documentation for default.command. +

+ +

+ If this command is not specified, task will use the default.command + value instead. If that is not specified, the command "list" is used. +

+
+

diff --git a/html/date.html b/html/date.html index 0d4c8b711..f5dcee84a 100644 --- a/html/date.html +++ b/html/date.html @@ -1,7 +1,7 @@ - Task 1.4.1 + Date Handling diff --git a/html/filter.html b/html/filter.html new file mode 100644 index 000000000..af801375b --- /dev/null +++ b/html/filter.html @@ -0,0 +1,92 @@ + + + + Task Filters + + + + + +
+ + + + + + +
+ + +
+
+
+
+

Task Filters

+
+

+

+
+ +
+
+
+

+ Copyright 2006-2008, P. Beckingham. All rights reserved. +

+
+ +
+
+
+
+
+
+
+
+
+
+
+
+ + + +
+ +
+ + + + + + + diff --git a/html/recur.html b/html/recur.html index ca7e9d626..2a72f76b6 100644 --- a/html/recur.html +++ b/html/recur.html @@ -1,7 +1,7 @@ - Task 1.4.1 + Recurring Tasks diff --git a/html/setup.html b/html/setup.html index 209260e28..68ecbf8b6 100644 --- a/html/setup.html +++ b/html/setup.html @@ -1,7 +1,7 @@ - Task 1.4.1 + Task Setup diff --git a/html/shadow.html b/html/shadow.html new file mode 100644 index 000000000..c1d0f5926 --- /dev/null +++ b/html/shadow.html @@ -0,0 +1,98 @@ + + + + Task Shadow Files + + + + + +
+ + + + + + +
+ + +
+
+
+
+

Task Shadow Files

+
+

+ + + + + + +

+
+ +
+
+
+

+ Copyright 2006-2008, P. Beckingham. All rights reserved. +

+
+ +
+
+
+
+
+
+
+
+
+
+
+
+ + + +
+ +
+ + + + + + + diff --git a/html/shell.html b/html/shell.html index 147ea2aa4..6bade3cc6 100644 --- a/html/shell.html +++ b/html/shell.html @@ -1,7 +1,7 @@ - Task 1.4.1 + Interacting with the Shell diff --git a/html/simple.html b/html/simple.html index cfa72564f..85ca5782b 100644 --- a/html/simple.html +++ b/html/simple.html @@ -1,7 +1,7 @@ - Task 1.4.1 + Simple Usage diff --git a/html/task.html b/html/task.html index aeafd3ecf..0c0236789 100644 --- a/html/task.html +++ b/html/task.html @@ -1,7 +1,7 @@ - Task 1.5.0 + Latest Release @@ -54,8 +54,8 @@
  • Date Handling
  • Troubleshooting
  • Old Versions - -
  • Filters (coming soon) +
  • Filters (coming soon) +
  • Shadow Files (coming soon)

    @@ -75,26 +75,28 @@ - + - + - +
    Source:task-1.5.0.tar.gztask-1.4.3.tar.gz
    Mac OS X 10.5 (Leopard) Intel-only:task-1.5.0.pkgtask-1.4.3.pkg
    Debian package: (Thanks to Richard Querin): task_1.5.0-1_i386.debtask_1.4.3-1_i386.deb
    -

    New in version 1.5.0 (?)

    +

    New in version 1.4.3 (9/18/2008)

      -
    • Removed deprecated TUTORIAL file. -
    • Removed "usage" command, and support for "command.logging" configuration - variable. +
    • Fixed misleading task count at bottom of "info" report. +
    • Added support for a shadow file that contains a plain text task report, + with the "shadow.file" and "shadow.command" configuration variables. + The shadow file is automatically updated whenever the task database + changes. Useful for integrating with "Samurize".

    diff --git a/html/usage.html b/html/usage.html index 97b98f5df..92b7b1d88 100644 --- a/html/usage.html +++ b/html/usage.html @@ -1,7 +1,7 @@ - Task 1.4.1 + Task Usage diff --git a/html/versions.html b/html/versions.html index 190a7251a..409c74fa4 100644 --- a/html/versions.html +++ b/html/versions.html @@ -38,6 +38,12 @@

    New in version 1.4.2 (9/18/2008)

    task-1.4.2.tar.gz +
    + Mac OS X 10.5 (Leopard) Intel-only: + task-1.4.2.pkg +
    + Debian package: task_1.4.2-1_i386.deb + (Thanks to Richard Querin)

      diff --git a/src/.gitignore b/src/.gitignore index 2327b8b98..5761abcfd 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -1,2 +1 @@ -./Makefile *.o diff --git a/src/TDB.cpp b/src/TDB.cpp index dcced05af..deae68184 100644 --- a/src/TDB.cpp +++ b/src/TDB.cpp @@ -235,7 +235,7 @@ bool TDB::completeT (const T& t) } //////////////////////////////////////////////////////////////////////////////// -bool TDB::addT (const T& t) const +bool TDB::addT (const T& t) { T task (t); std::vector tags; @@ -254,7 +254,9 @@ bool TDB::addT (const T& t) const if (task.getStatus () == T::pending || task.getStatus () == T::recurring) + { return writePending (task); + } return writeCompleted (task); } @@ -312,6 +314,7 @@ bool TDB::overwritePending (std::vector & all) fputs (it->compose ().c_str (), out); fclose (out); + dbChanged (); return true; } @@ -319,7 +322,7 @@ bool TDB::overwritePending (std::vector & all) } //////////////////////////////////////////////////////////////////////////////// -bool TDB::writePending (const T& t) const +bool TDB::writePending (const T& t) { // Write a single task to the pending file FILE* out; @@ -334,6 +337,7 @@ bool TDB::writePending (const T& t) const fputs (t.compose ().c_str (), out); fclose (out); + dbChanged (); return true; } @@ -341,7 +345,7 @@ bool TDB::writePending (const T& t) const } //////////////////////////////////////////////////////////////////////////////// -bool TDB::writeCompleted (const T& t) const +bool TDB::writeCompleted (const T& t) { // Write a single task to the pending file FILE* out; @@ -356,6 +360,7 @@ bool TDB::writeCompleted (const T& t) const fputs (t.compose ().c_str (), out); fclose (out); + dbChanged (); return true; } @@ -439,4 +444,20 @@ int TDB::nextId () } //////////////////////////////////////////////////////////////////////////////// +void TDB::onChange (void (*callback)()) +{ + if (callback) + mOnChange.push_back (callback); +} + +//////////////////////////////////////////////////////////////////////////////// +// Iterate over callbacks. +void TDB::dbChanged () +{ + foreach (i, mOnChange) + if (*i) + (**i) (); +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/TDB.h b/src/TDB.h index 9ceb9b3d8..d335e909b 100644 --- a/src/TDB.h +++ b/src/TDB.h @@ -45,23 +45,27 @@ public: bool allCompletedT (std::vector &) const; bool deleteT (const T&); bool completeT (const T&); - bool addT (const T&) const; + bool addT (const T&); bool modifyT (const T&); bool logRead (std::vector &) const; int gc (); int nextId (); + void onChange (void (*)()); + private: bool lock (FILE*) const; bool overwritePending (std::vector &); - bool writePending (const T&) const; - bool writeCompleted (const T&) const; + bool writePending (const T&); + bool writeCompleted (const T&); bool readLockedFile (const std::string&, std::vector &) const; + void dbChanged (); private: std::string mPendingFile; std::string mCompletedFile; int mId; + std::vector mOnChange; }; #endif diff --git a/src/command.cpp b/src/command.cpp index c0d288598..b696a8f93 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -26,6 +26,7 @@ //////////////////////////////////////////////////////////////////////////////// #include #include +#include #include #include #include @@ -47,7 +48,7 @@ #endif //////////////////////////////////////////////////////////////////////////////// -void handleAdd (const TDB& tdb, T& task, Config& conf) +void handleAdd (TDB& tdb, T& task, Config& conf) { char entryTime[16]; sprintf (entryTime, "%u", (unsigned int) time (NULL)); @@ -67,7 +68,6 @@ void handleAdd (const TDB& tdb, T& task, Config& conf) task.setAttribute ("mask", ""); } -/**/ // Override with default.project, if not specified. if (task.getAttribute ("project") == "") task.setAttribute ("project", conf.get ("default.project", "")); @@ -79,8 +79,8 @@ void handleAdd (const TDB& tdb, T& task, Config& conf) if (validPriority (defaultPriority)) task.setAttribute ("priority", defaultPriority); } -/**/ + // Disallow blank descriptions. if (task.getDescription () == "") throw std::string ("Cannot add a blank task."); @@ -89,8 +89,10 @@ void handleAdd (const TDB& tdb, T& task, Config& conf) } //////////////////////////////////////////////////////////////////////////////// -void handleProjects (TDB& tdb, T& task, Config& conf) +std::string handleProjects (TDB& tdb, T& task, Config& conf) { + std::stringstream out; + // Get all the tasks, including deleted ones. std::vector tasks; tdb.pendingT (tasks); @@ -127,21 +129,25 @@ void handleProjects (TDB& tdb, T& task, Config& conf) table.addCell (row, 1, i->second); } - std::cout << optionalBlankLine (conf) - << table.render () - << optionalBlankLine (conf) - << unique.size () - << (unique.size () == 1 ? " project" : " projects") - << std::endl; + out << optionalBlankLine (conf) + << table.render () + << optionalBlankLine (conf) + << unique.size () + << (unique.size () == 1 ? " project" : " projects") + << std::endl; } else - std::cout << "No projects." - << std::endl; + out << "No projects." + << std::endl; + + return out.str (); } //////////////////////////////////////////////////////////////////////////////// -void handleTags (TDB& tdb, T& task, Config& conf) +std::string handleTags (TDB& tdb, T& task, Config& conf) { + std::stringstream out; + // Get all the tasks. std::vector tasks; tdb.pendingT (tasks); @@ -166,20 +172,23 @@ void handleTags (TDB& tdb, T& task, Config& conf) std::cout << i->first << std::endl; if (unique.size ()) - std::cout << optionalBlankLine (conf) - << unique.size () - << (unique.size () == 1 ? " tag" : " tags") - << std::endl; + out << optionalBlankLine (conf) + << unique.size () + << (unique.size () == 1 ? " tag" : " tags") + << std::endl; else - std::cout << "No tags." - << std::endl; + out << "No tags." + << std::endl; + + return out.str (); } //////////////////////////////////////////////////////////////////////////////// // If a task is deleted, but is still in the pending file, then it may be // undeleted simply by changing it's status. -void handleUndelete (TDB& tdb, T& task, Config& conf) +std::string handleUndelete (TDB& tdb, T& task, Config& conf) { + std::stringstream out; std::vector all; tdb.allPendingT (all); @@ -193,8 +202,8 @@ void handleUndelete (TDB& tdb, T& task, Config& conf) { if (it->getAttribute ("recur") != "") { - std::cout << "Task does not support 'undelete' for recurring tasks." << std::endl; - return; + out << "Task does not support 'undelete' for recurring tasks." << std::endl; + return out.str (); } T restored (*it); @@ -202,27 +211,31 @@ void handleUndelete (TDB& tdb, T& task, Config& conf) restored.removeAttribute ("end"); tdb.modifyT (restored); - std::cout << "Task " << id << " successfully undeleted." << std::endl; - return; + out << "Task " << id << " successfully undeleted." << std::endl; + return out.str (); } else { - std::cout << "Task " << id << " is not deleted - therefore cannot undelete." << std::endl; - return; + out << "Task " << id << " is not deleted - therefore cannot undelete." << std::endl; + return out.str (); } } } - std::cout << "Task " << id - << " not found - tasks can only be reliably undeleted if the undelete" << std::endl - << "command is run immediately after the errant delete command." << std::endl; + out << "Task " << id + << " not found - tasks can only be reliably undeleted if the undelete" << std::endl + << "command is run immediately after the errant delete command." << std::endl; + + return out.str (); } //////////////////////////////////////////////////////////////////////////////// // If a task is done, but is still in the pending file, then it may be undone // simply by changing it's status. -void handleUndo (TDB& tdb, T& task, Config& conf) +std::string handleUndo (TDB& tdb, T& task, Config& conf) { + std::stringstream out; + std::vector all; tdb.allPendingT (all); @@ -235,35 +248,36 @@ void handleUndo (TDB& tdb, T& task, Config& conf) if (it->getStatus () == T::completed) { if (it->getAttribute ("recur") != "") - { - std::cout << "Task does not support 'undo' for recurring tasks." << std::endl; - return; - } + return std::string ("Task does not support 'undo' for recurring tasks.\n"); T restored (*it); restored.setStatus (T::pending); restored.removeAttribute ("end"); tdb.modifyT (restored); - std::cout << "Task " << id << " successfully undone." << std::endl; - return; + out << "Task " << id << " successfully undone." << std::endl; + return out.str (); } else { - std::cout << "Task " << id << " is not done - therefore cannot be undone." << std::endl; - return; + out << "Task " << id << " is not done - therefore cannot be undone." << std::endl; + return out.str (); } } } - std::cout << "Task " << id - << " not found - tasks can only be reliably undone if the undo" << std::endl - << "command is run immediately after the errant done command." << std::endl; + out << "Task " << id + << " not found - tasks can only be reliably undone if the undo" << std::endl + << "command is run immediately after the errant done command." << std::endl; + + return out.str (); } //////////////////////////////////////////////////////////////////////////////// -void handleVersion (Config& conf) +std::string handleVersion (Config& conf) { + std::stringstream out; + // Determine window size, and set table accordingly. int width = conf.get ("defaultwidth", 80); #ifdef HAVE_LIBNCURSES @@ -329,40 +343,42 @@ void handleVersion (Config& conf) } } - std::cout << "Copyright (C) 2006 - 2008, P. Beckingham." - << std::endl - << (conf.get ("color", true) ? Text::colorize (Text::bold, Text::nocolor, PACKAGE) : PACKAGE) - << " " - << (conf.get ("color", true) ? Text::colorize (Text::bold, Text::nocolor, VERSION) : VERSION) - << std::endl - << disclaimer.render () - << std::endl - << table.render () - << link.render () - << std::endl; + out << "Copyright (C) 2006 - 2008, P. Beckingham." + << std::endl + << (conf.get ("color", true) ? Text::colorize (Text::bold, Text::nocolor, PACKAGE) : PACKAGE) + << " " + << (conf.get ("color", true) ? Text::colorize (Text::bold, Text::nocolor, VERSION) : VERSION) + << std::endl + << disclaimer.render () + << std::endl + << table.render () + << link.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; + out << "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; + out << "Configuration error: data.location not specified in .taskrc " + "file." + << std::endl; if (access (expandPath (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; + out << "Configuration error: data.location contains a directory name" + " that doesn't exist, or is unreadable." + << std::endl; } + + return out.str (); } //////////////////////////////////////////////////////////////////////////////// -void handleDelete (TDB& tdb, T& task, Config& conf) +std::string handleDelete (TDB& tdb, T& task, Config& conf) { if (conf.get ("confirmation") != "yes" || confirm ("Permanently delete task?")) { @@ -386,7 +402,7 @@ void handleDelete (TDB& tdb, T& task, Config& conf) sibling->getUUID () == parent) tdb.deleteT (*sibling); - return; + return std::string (""); } else { @@ -394,7 +410,7 @@ void handleDelete (TDB& tdb, T& task, Config& conf) t->setStatus (T::deleted); updateRecurrenceMask (tdb, all, *t); tdb.deleteT (*t); - return; + return std::string (""); } } else @@ -405,11 +421,13 @@ void handleDelete (TDB& tdb, T& task, Config& conf) } } else - std::cout << "Task not deleted." << std::endl; + return std::string ("Task not deleted.\n"); + + return std::string (""); } //////////////////////////////////////////////////////////////////////////////// -void handleStart (TDB& tdb, T& task, Config& conf) +std::string handleStart (TDB& tdb, T& task, Config& conf) { std::vector all; tdb.pendingT (all); @@ -431,14 +449,19 @@ void handleStart (TDB& tdb, T& task, Config& conf) tdb.modifyT (original); nag (tdb, task, conf); - return; + return std::string (""); } else - std::cout << "Task " << task.getId () << " already started." << std::endl; + { + std::stringstream out; + out << "Task " << task.getId () << " already started." << std::endl; + return out.str (); + } } } throw std::string ("Task not found."); + return std::string (""); // To satisfy gcc. } //////////////////////////////////////////////////////////////////////////////// @@ -597,87 +620,91 @@ void handleModify (TDB& tdb, T& task, Config& conf) } //////////////////////////////////////////////////////////////////////////////// -void handleColor (Config& conf) +std::string handleColor (Config& conf) { + std::stringstream out; + if (conf.get ("color", true)) { - std::cout << optionalBlankLine (conf) << "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 + out << optionalBlankLine (conf) << "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::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::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::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::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::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::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::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 + << " " << 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 + << 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_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_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_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_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_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_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 + << " " << Text::colorize (Text::nocolor, Text::on_white, "on_white") << " " + << Text::colorize (Text::nocolor, Text::on_bright_white, "on_bright_white") << std::endl - << optionalBlankLine (conf); + << optionalBlankLine (conf); } else { - std::cout << "Color is currently turned off in your .taskrc file." << std::endl; + out << "Color is currently turned off in your .taskrc file." << std::endl; } + + return out.str (); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/report.cpp b/src/report.cpp index 5c1d8ac8f..8d3339c58 100644 --- a/src/report.cpp +++ b/src/report.cpp @@ -26,6 +26,7 @@ //////////////////////////////////////////////////////////////////////////////// #include #include +#include #include #include #include @@ -110,8 +111,10 @@ void filter (std::vector& all, T& task) //////////////////////////////////////////////////////////////////////////////// // Successively apply filters based on the task object built from the command // line. Tasks that match all the specified criteria are listed. -void handleList (TDB& tdb, T& task, Config& conf) +std::string handleList (TDB& tdb, T& task, Config& conf) { + std::stringstream out; + // Determine window size, and set table accordingly. int width = conf.get ("defaultwidth", 80); #ifdef HAVE_LIBNCURSES @@ -242,23 +245,27 @@ void handleList (TDB& tdb, T& task, Config& conf) } if (table.rowCount ()) - std::cout << optionalBlankLine (conf) - << table.render () - << optionalBlankLine (conf) - << table.rowCount () - << (table.rowCount () == 1 ? " task" : " tasks") - << std::endl; + out << optionalBlankLine (conf) + << table.render () + << optionalBlankLine (conf) + << table.rowCount () + << (table.rowCount () == 1 ? " task" : " tasks") + << std::endl; else - std::cout << "No matches." - << std::endl; + out << "No matches." + << std::endl; + + return out.str (); } //////////////////////////////////////////////////////////////////////////////// // 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 (TDB& tdb, T& task, Config& conf) +std::string handleSmallList (TDB& tdb, T& task, Config& conf) { + std::stringstream out; + // Determine window size, and set table accordingly. int width = conf.get ("defaultwidth", 80); #ifdef HAVE_LIBNCURSES @@ -371,22 +378,26 @@ void handleSmallList (TDB& tdb, T& task, Config& conf) } if (table.rowCount ()) - std::cout << optionalBlankLine (conf) - << table.render () - << optionalBlankLine (conf) - << table.rowCount () - << (table.rowCount () == 1 ? " task" : " tasks") - << std::endl; + out << optionalBlankLine (conf) + << table.render () + << optionalBlankLine (conf) + << table.rowCount () + << (table.rowCount () == 1 ? " task" : " tasks") + << std::endl; else - std::cout << "No matches." - << std::endl; + out << "No matches." + << std::endl; + + return out.str (); } //////////////////////////////////////////////////////////////////////////////// // Successively apply filters based on the task object built from the command // line. Tasks that match all the specified criteria are listed. -void handleCompleted (TDB& tdb, T& task, Config& conf) +std::string handleCompleted (TDB& tdb, T& task, Config& conf) { + std::stringstream out; + // Determine window size, and set table accordingly. int width = conf.get ("defaultwidth", 80); #ifdef HAVE_LIBNCURSES @@ -459,21 +470,25 @@ void handleCompleted (TDB& tdb, T& task, Config& conf) } if (table.rowCount ()) - std::cout << optionalBlankLine (conf) - << table.render () - << optionalBlankLine (conf) - << table.rowCount () - << (table.rowCount () == 1 ? " task" : " tasks") - << std::endl; + out << optionalBlankLine (conf) + << table.render () + << optionalBlankLine (conf) + << table.rowCount () + << (table.rowCount () == 1 ? " task" : " tasks") + << std::endl; else - std::cout << "No matches." - << std::endl; + out << "No matches." + << std::endl; + + return out.str (); } //////////////////////////////////////////////////////////////////////////////// // Display all information for the given task. -void handleInfo (TDB& tdb, T& task, Config& conf) +std::string handleInfo (TDB& tdb, T& task, Config& conf) { + std::stringstream out; + // Determine window size, and set table accordingly. int width = conf.get ("defaultwidth", 80); #ifdef HAVE_LIBNCURSES @@ -657,21 +672,22 @@ void handleInfo (TDB& tdb, T& task, Config& conf) } if (table.rowCount ()) - std::cout << optionalBlankLine (conf) - << table.render () - << optionalBlankLine (conf) - << table.rowCount () - << (table.rowCount () == 1 ? " task" : " tasks") - << std::endl; + out << optionalBlankLine (conf) + << table.render () + << std::endl; else - std::cout << "No matches." << std::endl; + out << "No matches." << std::endl; + + return out.str (); } //////////////////////////////////////////////////////////////////////////////// // Successively apply filters based on the task object built from the command // line. Tasks that match all the specified criteria are listed. -void handleLongList (TDB& tdb, T& task, Config& conf) +std::string handleLongList (TDB& tdb, T& task, Config& conf) { + std::stringstream out; + // Determine window size, and set table accordingly. int width = conf.get ("defaultwidth", 80); #ifdef HAVE_LIBNCURSES @@ -827,22 +843,26 @@ void handleLongList (TDB& tdb, T& task, Config& conf) } if (table.rowCount ()) - std::cout << optionalBlankLine (conf) - << table.render () - << optionalBlankLine (conf) - << table.rowCount () - << (table.rowCount () == 1 ? " task" : " tasks") - << std::endl; + out << optionalBlankLine (conf) + << table.render () + << optionalBlankLine (conf) + << table.rowCount () + << (table.rowCount () == 1 ? " task" : " tasks") + << std::endl; else - std::cout << "No matches." << std::endl; + out << "No matches." << std::endl; + + return out.str (); } //////////////////////////////////////////////////////////////////////////////// // Project Tasks Avg Age Status // A 12 13d XXXXXXXX------ // B 109 3d 12h XX------------ -void handleReportSummary (TDB& tdb, T& task, Config& conf) +std::string handleReportSummary (TDB& tdb, T& task, Config& conf) { + std::stringstream out; + // Generate unique list of project names. tdb.gc (); std::map allProjects; @@ -984,14 +1004,16 @@ void handleReportSummary (TDB& tdb, T& task, Config& conf) } if (table.rowCount ()) - std::cout << optionalBlankLine (conf) - << table.render () - << optionalBlankLine (conf) - << table.rowCount () - << (table.rowCount () == 1 ? " project" : " projects") - << std::endl; + out << optionalBlankLine (conf) + << table.render () + << optionalBlankLine (conf) + << table.rowCount () + << (table.rowCount () == 1 ? " project" : " projects") + << std::endl; else - std::cout << "No projects." << std::endl; + out << "No projects." << std::endl; + + return out.str (); } //////////////////////////////////////////////////////////////////////////////// @@ -1013,8 +1035,10 @@ void handleReportSummary (TDB& tdb, T& task, Config& conf) // // Make the "three" tasks a configurable number // -void handleReportNext (TDB& tdb, T& task, Config& conf) +std::string handleReportNext (TDB& tdb, T& task, Config& conf) { + std::stringstream out; + // Load all pending. tdb.gc (); std::vector pending; @@ -1154,15 +1178,17 @@ void handleReportNext (TDB& tdb, T& task, Config& conf) } if (table.rowCount ()) - std::cout << optionalBlankLine (conf) - << table.render () - << optionalBlankLine (conf) - << table.rowCount () - << (table.rowCount () == 1 ? " task" : " tasks") - << std::endl; + out << optionalBlankLine (conf) + << table.render () + << optionalBlankLine (conf) + << table.rowCount () + << (table.rowCount () == 1 ? " task" : " tasks") + << std::endl; else - std::cout << "No matches." - << std::endl; + out << "No matches." + << std::endl; + + return out.str (); } //////////////////////////////////////////////////////////////////////////////// @@ -1187,8 +1213,10 @@ time_t monthlyEpoch (const std::string& date) return 0; } -void handleReportHistory (TDB& tdb, T& task, Config& conf) +std::string handleReportHistory (TDB& tdb, T& task, Config& conf) { + std::stringstream out; + std::map groups; std::map addedGroup; std::map completedGroup; @@ -1363,16 +1391,20 @@ void handleReportHistory (TDB& tdb, T& task, Config& conf) } if (table.rowCount ()) - std::cout << optionalBlankLine (conf) - << table.render () - << std::endl; + out << optionalBlankLine (conf) + << table.render () + << std::endl; else - std::cout << "No tasks." << std::endl; + out << "No tasks." << std::endl; + + return out.str (); } //////////////////////////////////////////////////////////////////////////////// -void handleReportGHistory (TDB& tdb, T& task, Config& conf) +std::string handleReportGHistory (TDB& tdb, T& task, Config& conf) { + std::stringstream out; + // Determine window size, and set table accordingly. int width = conf.get ("defaultwidth", 80); #ifdef HAVE_LIBNCURSES @@ -1574,24 +1606,105 @@ void handleReportGHistory (TDB& tdb, T& task, Config& conf) if (table.rowCount ()) { - std::cout << optionalBlankLine (conf) - << table.render () - << std::endl; + out << optionalBlankLine (conf) + << table.render () + << std::endl; if (conf.get ("color", true)) - std::cout << "Legend: " - << Text::colorize (Text::black, Text::on_red, "added") - << ", " - << Text::colorize (Text::black, Text::on_green, "completed") - << ", " - << Text::colorize (Text::black, Text::on_yellow, "deleted") - << optionalBlankLine (conf) - << std::endl; + out << "Legend: " + << Text::colorize (Text::black, Text::on_red, "added") + << ", " + << Text::colorize (Text::black, Text::on_green, "completed") + << ", " + << Text::colorize (Text::black, Text::on_yellow, "deleted") + << optionalBlankLine (conf) + << std::endl; else - std::cout << "Legend: + added, X completed, - deleted" << std::endl; + out << "Legend: + added, X completed, - deleted" << std::endl; } else - std::cout << "No tasks." << std::endl; + out << "No tasks." << std::endl; + + return out.str (); +} + +//////////////////////////////////////////////////////////////////////////////// +// 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" +// +std::string handleReportUsage (const TDB& tdb, T& task, Config& conf) +{ + std::stringstream out; + + 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, conf); + + 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"); + + if (conf.get ("color", true)) + { + table.setColumnUnderline (0); + table.setColumnUnderline (1); + } + else + table.setTableDashedUnderline (); + + table.setColumnJustification (1, Table::right); + table.sortOn (1, Table::descendingNumeric); + table.setDateFormat (conf.get ("dateformat", "m/d/Y")); + + 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 ()) + out << optionalBlankLine (conf) + << table.render () + << std::endl; + else + out << "No usage." << std::endl; + } + else + out << "Command logging is not enabled, so no history has been kept." + << std::endl; + + return out.str (); } //////////////////////////////////////////////////////////////////////////////// @@ -1719,8 +1832,10 @@ std::string renderMonths ( } //////////////////////////////////////////////////////////////////////////////// -void handleReportCalendar (TDB& tdb, T& task, Config& conf) +std::string handleReportCalendar (TDB& tdb, T& task, Config& conf) { + std::stringstream out; + // Load all the pending tasks. tdb.gc (); std::vector pending; @@ -1751,7 +1866,7 @@ void handleReportCalendar (TDB& tdb, T& task, Config& conf) int mTo = newest.month (); int yTo = newest.year (); - std::cout << std::endl; + out << std::endl; std::string output; int monthsPerLine = (conf.get ("monthsperline", 1)); @@ -1768,11 +1883,11 @@ void handleReportCalendar (TDB& tdb, T& task, Config& conf) int left = (18 - month.length ()) / 2 + 1; int right = 18 - left - month.length (); - std::cout << std::setw (left) << ' ' - << month - << ' ' - << nextY - << std::setw (right) << ' '; + out << std::setw (left) << ' ' + << month + << ' ' + << nextY + << std::setw (right) << ' '; if (++nextM > 12) { @@ -1781,10 +1896,10 @@ void handleReportCalendar (TDB& tdb, T& task, Config& conf) } } - std::cout << std::endl - << optionalBlankLine (conf) - << renderMonths (mFrom, yFrom, today, pending, conf) - << std::endl; + out << std::endl + << optionalBlankLine (conf) + << renderMonths (mFrom, yFrom, today, pending, conf) + << std::endl; mFrom += monthsPerLine; if (mFrom > 12) @@ -1794,20 +1909,24 @@ void handleReportCalendar (TDB& tdb, T& task, Config& conf) } } - std::cout << "Legend: " - << Text::colorize (Text::cyan, Text::nocolor, "today") - << ", " - << Text::colorize (Text::black, Text::on_yellow, "due") - << ", " - << Text::colorize (Text::black, Text::on_red, "overdue") - << "." - << optionalBlankLine (conf) - << std::endl; + out << "Legend: " + << Text::colorize (Text::cyan, Text::nocolor, "today") + << ", " + << Text::colorize (Text::black, Text::on_yellow, "due") + << ", " + << Text::colorize (Text::black, Text::on_red, "overdue") + << "." + << optionalBlankLine (conf) + << std::endl; + + return out.str (); } //////////////////////////////////////////////////////////////////////////////// -void handleReportActive (TDB& tdb, T& task, Config& conf) +std::string handleReportActive (TDB& tdb, T& task, Config& conf) { + std::stringstream out; + // Determine window size, and set table accordingly. int width = conf.get ("defaultwidth", 80); #ifdef HAVE_LIBNCURSES @@ -1913,19 +2032,23 @@ void handleReportActive (TDB& tdb, T& task, Config& conf) } if (table.rowCount ()) - std::cout << optionalBlankLine (conf) - << table.render () - << optionalBlankLine (conf) - << table.rowCount () - << (table.rowCount () == 1 ? " task" : " tasks") - << std::endl; + out << optionalBlankLine (conf) + << table.render () + << optionalBlankLine (conf) + << table.rowCount () + << (table.rowCount () == 1 ? " task" : " tasks") + << std::endl; else - std::cout << "No active tasks." << std::endl; + out << "No active tasks." << std::endl; + + return out.str (); } //////////////////////////////////////////////////////////////////////////////// -void handleReportOverdue (TDB& tdb, T& task, Config& conf) +std::string handleReportOverdue (TDB& tdb, T& task, Config& conf) { + std::stringstream out; + // Determine window size, and set table accordingly. int width = conf.get ("defaultwidth", 80); #ifdef HAVE_LIBNCURSES @@ -2020,21 +2143,25 @@ void handleReportOverdue (TDB& tdb, T& task, Config& conf) } if (table.rowCount ()) - std::cout << optionalBlankLine (conf) - << table.render () - << optionalBlankLine (conf) - << table.rowCount () - << (table.rowCount () == 1 ? " task" : " tasks") - << std::endl; + out << optionalBlankLine (conf) + << table.render () + << optionalBlankLine (conf) + << table.rowCount () + << (table.rowCount () == 1 ? " task" : " tasks") + << std::endl; else - std::cout << "No overdue tasks." << std::endl; + out << "No overdue tasks." << std::endl; + + return out.str (); } //////////////////////////////////////////////////////////////////////////////// // Successively apply filters based on the task object built from the command // line. Tasks that match all the specified criteria are listed. -void handleReportOldest (TDB& tdb, T& task, Config& conf) +std::string handleReportOldest (TDB& tdb, T& task, Config& conf) { + std::stringstream out; + // Determine window size, and set table accordingly. int width = conf.get ("defaultwidth", 80); #ifdef HAVE_LIBNCURSES @@ -2164,22 +2291,26 @@ void handleReportOldest (TDB& tdb, T& task, Config& conf) } if (table.rowCount ()) - std::cout << optionalBlankLine (conf) - << table.render () - << optionalBlankLine (conf) - << table.rowCount () - << (table.rowCount () == 1 ? " task" : " tasks") - << std::endl; + out << optionalBlankLine (conf) + << table.render () + << optionalBlankLine (conf) + << table.rowCount () + << (table.rowCount () == 1 ? " task" : " tasks") + << std::endl; else - std::cout << "No matches." - << std::endl; + out << "No matches." + << std::endl; + + return out.str (); } //////////////////////////////////////////////////////////////////////////////// // Successively apply filters based on the task object built from the command // line. Tasks that match all the specified criteria are listed. -void handleReportNewest (TDB& tdb, T& task, Config& conf) +std::string handleReportNewest (TDB& tdb, T& task, Config& conf) { + std::stringstream out; + // Determine window size, and set table accordingly. int width = conf.get ("defaultwidth", 80); #ifdef HAVE_LIBNCURSES @@ -2310,21 +2441,25 @@ void handleReportNewest (TDB& tdb, T& task, Config& conf) } if (table.rowCount ()) - std::cout << optionalBlankLine (conf) - << table.render () - << optionalBlankLine (conf) - << table.rowCount () - << (table.rowCount () == 1 ? " task" : " tasks") - << std::endl; + out << optionalBlankLine (conf) + << table.render () + << optionalBlankLine (conf) + << table.rowCount () + << (table.rowCount () == 1 ? " task" : " tasks") + << std::endl; else - std::cout << "No matches." - << std::endl; + out << "No matches." + << std::endl; + + return out.str (); } //////////////////////////////////////////////////////////////////////////////// -void handleReportStats (TDB& tdb, T& task, Config& conf) +std::string handleReportStats (TDB& tdb, T& task, Config& conf) { + std::stringstream out; + // Get all the tasks. std::vector tasks; tdb.allT (tasks); @@ -2371,40 +2506,42 @@ void handleReportStats (TDB& tdb, T& task, Config& conf) if (tags.size ()) ++taggedT; } - std::cout << "Pending " << pendingT << std::endl - << "Recurring " << recurringT << std::endl - << "Completed " << completedT << std::endl - << "Deleted " << deletedT << std::endl - << "Total " << totalT << std::endl; + out << "Pending " << pendingT << std::endl + << "Recurring " << recurringT << std::endl + << "Completed " << completedT << std::endl + << "Deleted " << deletedT << std::endl + << "Total " << totalT << std::endl; if (tasks.size ()) { Date e (earliest); - std::cout << "Oldest task " << e.toString (conf.get ("dateformat", "m/d/Y")) << std::endl; + out << "Oldest task " << e.toString (conf.get ("dateformat", "m/d/Y")) << std::endl; Date l (latest); - std::cout << "Newest task " << l.toString (conf.get ("dateformat", "m/d/Y")) << std::endl; - std::cout << "Task used for " << formatSeconds (latest - earliest) << std::endl; + out << "Newest task " << l.toString (conf.get ("dateformat", "m/d/Y")) << std::endl; + out << "Task used for " << formatSeconds (latest - earliest) << std::endl; } if (totalT) - std::cout << "Task added every " << formatSeconds ((latest - earliest) / totalT) << std::endl; + out << "Task added every " << formatSeconds ((latest - earliest) / totalT) << std::endl; if (completedT) - std::cout << "Task completed every " << formatSeconds ((latest - earliest) / completedT) << std::endl; + out << "Task completed every " << formatSeconds ((latest - earliest) / completedT) << std::endl; if (deletedT) - std::cout << "Task deleted every " << formatSeconds ((latest - earliest) / deletedT) << std::endl; + out << "Task deleted every " << formatSeconds ((latest - earliest) / deletedT) << std::endl; if (pendingT || completedT) - std::cout << "Average time pending " + out << "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; + out << "Average desc length " << (int) (descLength / totalT) << " characters" << std::endl; + out << "Tasks tagged " << std::setprecision (3) << (100.0 * taggedT / totalT) << "%" << std::endl; } + + return out.str (); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/task.cpp b/src/task.cpp index e53f9675f..6bf8b06bf 100644 --- a/src/task.cpp +++ b/src/task.cpp @@ -46,6 +46,11 @@ #include #endif +//////////////////////////////////////////////////////////////////////////////// +// Globals for exclusive use by callback function. +static TDB* gTdb = NULL; +static Config* gConf = NULL; + //////////////////////////////////////////////////////////////////////////////// static void shortUsage (Config& conf) { @@ -280,6 +285,7 @@ int main (int argc, char** argv) // Load the config file from the home directory. If the file cannot be // found, offer to create a sample one. Config conf; + gConf = &conf; loadConfFile (argc, argv, conf); // When redirecting output to a file, do not use color, curses. @@ -290,69 +296,41 @@ int main (int argc, char** argv) } TDB tdb; - tdb.dataDirectory (expandPath (conf.get ("data.location"))); + gTdb = &tdb; + std::string dataLocation = expandPath (conf.get ("data.location")); + tdb.dataDirectory (dataLocation); - // If argc == 1 and the default.command configuration variable is set, - // then use that, otherwise stick with argc/argv. - std::vector args; - std::string defaultCommand = conf.get ("default.command"); - if (argc == 1 && defaultCommand != "") + // Log commands, if desired. + if (conf.get ("command.logging") == "on") + tdb.logCommand (argc, argv); + + // Set up TDB callback. + std::string shadowFile = expandPath (conf.get ("shadow.file")); + if (shadowFile != "") { - // Stuff the command line. - split (args, defaultCommand, ' '); - std::cout << "[task " << defaultCommand << "]" << std::endl; - } - else - { - // Parse the command line. - for (int i = 1; i < argc; ++i) - args.push_back (argv[i]); + if (shadowFile == dataLocation + "/pending.data") + throw std::string ("Configuration variable 'shadow.file' is set to " + "overwrite your pending tasks. Please change it."); + + if (shadowFile == dataLocation + "/completed.data") + throw std::string ("Configuration variable 'shadow.file' is set to " + "overwrite your completed tasks. Please change it."); + + tdb.onChange (&onChangeCallback); } - std::string command; - T task; - parse (args, command, task, conf); - - 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 == "undelete") handleUndelete (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 == "undo") handleUndo (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 == "ghistory") handleReportGHistory (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 == "oldest") handleReportOldest (tdb, task, conf); - else if (command == "newest") handleReportNewest (tdb, task, conf); - else if (command == "stats") handleReportStats (tdb, task, conf); - else if (command == "" && task.getId ()) handleModify (tdb, task, conf); - else if (command == "help") longUsage (conf); - else shortUsage (conf); + std::cout << runTaskCommand (argc, argv, tdb, conf); } catch (std::string& error) { - std::cout << error << std::endl; + std::cerr << error << std::endl; return -1; } catch (...) { - std::cout << "Unknown error." << std::endl; + std::cerr << "Unknown error." << std::endl; return -2; } @@ -391,12 +369,18 @@ int getDueState (const std::string& due) if (due.length ()) { Date dt (::atoi (due.c_str ())); - Date now; - if (dt < now) + // rightNow is the current date + time. + Date rightNow; + + // By performing this conversion, today is set up as the same date, but + // midnight. + Date today (rightNow.month (), rightNow.day (), rightNow.year ()); + + if (dt < today) return 2; - Date nextweek = now + 7 * 86400; + Date nextweek = today + 7 * 86400; if (dt < nextweek) return 1; } @@ -544,7 +528,6 @@ Date getNextRecurrence (Date& current, std::string& period) while (! Date::valid (m, d, y)) --d; -// std::cout << "# next " << current.toString () << " + " << period << " = " << m << "/" << d << "/" << y << std::endl; return Date (m, d, y); } @@ -563,7 +546,6 @@ Date getNextRecurrence (Date& current, std::string& period) while (! Date::valid (m, d, y)) --d; -// std::cout << "# next " << current.toString () << " + " << period << " = " << m << "/" << d << "/" << y << std::endl; return Date (m, d, y); } @@ -579,7 +561,6 @@ Date getNextRecurrence (Date& current, std::string& period) while (! Date::valid (m, d, y)) --d; -// std::cout << "# next " << current.toString () << " + " << period << " = " << m << "/" << d << "/" << y << std::endl; return Date (m, d, y); } @@ -598,7 +579,6 @@ Date getNextRecurrence (Date& current, std::string& period) while (! Date::valid (m, d, y)) --d; -// std::cout << "# next " << current.toString () << " + " << period << " = " << m << "/" << d << "/" << y << std::endl; return Date (m, d, y); } @@ -614,7 +594,6 @@ Date getNextRecurrence (Date& current, std::string& period) while (! Date::valid (m, d, y)) --d; -// std::cout << "# next " << current.toString () << " + " << period << " = " << m << "/" << d << "/" << y << std::endl; return Date (m, d, y); } @@ -630,7 +609,6 @@ Date getNextRecurrence (Date& current, std::string& period) while (! Date::valid (m, d, y)) --d; -// std::cout << "# next " << current.toString () << " + " << period << " = " << m << "/" << d << "/" << y << std::endl; return Date (m, d, y); } @@ -639,7 +617,6 @@ Date getNextRecurrence (Date& current, std::string& period) { y += 2; -// std::cout << "# next " << current.toString () << " + " << period << " = " << m << "/" << d << "/" << y << std::endl; return Date (m, d, y); } @@ -657,7 +634,6 @@ void updateRecurrenceMask ( T& task) { std::string parent = task.getAttribute ("parent"); -// std::cout << "# updateRecurrenceMask of " << parent << std::endl; if (parent != "") { std::vector ::iterator it; @@ -665,11 +641,8 @@ void updateRecurrenceMask ( { if (it->getUUID () == parent) { -// std::cout << "# located parent task" << std::endl; unsigned int index = atoi (task.getAttribute ("imask").c_str ()); -// std::cout << "# child imask=" << index << std::endl; std::string mask = it->getAttribute ("mask"); -// std::cout << "# parent mask=" << mask << std::endl; if (mask.length () > index) { mask[index] = (task.getStatus () == T::pending) ? '-' @@ -677,15 +650,11 @@ void updateRecurrenceMask ( : (task.getStatus () == T::deleted) ? 'X' : '?'; -// std::cout << "# setting parent mask to=" << mask << std::endl; it->setAttribute ("mask", mask); -// std::cout << "# tdb.modifyT (parent)" << std::endl; tdb.modifyT (*it); } else { -// std::cout << "# mask of insufficient length" << std::endl; -// std::cout << "# should never occur" << std::endl; std::string mask; for (unsigned int i = 0; i < index; ++i) mask += "?"; @@ -703,3 +672,129 @@ void updateRecurrenceMask ( } //////////////////////////////////////////////////////////////////////////////// +// Using gTdb and gConf, generate a report. +void onChangeCallback () +{ + try + { + if (gConf && gTdb) + { + // Determine if shadow file is enabled. + std::string shadowFile = expandPath (gConf->get ("shadow.file")); + if (shadowFile != "") + { + std::string oldCurses = gConf->get ("curses"); + std::string oldColor = gConf->get ("color"); + gConf->set ("curses", "off"); + gConf->set ("color", "off"); + + // Run report. Use shadow.command, using default.command as a fallback + // with "list" as a default. + std::string command = gConf->get ("shadow.command", + gConf->get ("default.command", "list")); + std::vector args; + split (args, command, ' '); + std::string result = runTaskCommand (args, *gTdb, *gConf); + + std::ofstream out (shadowFile.c_str ()); + if (out.good ()) + { + out << result; + out.close (); + } + else + throw std::string ("Could not write file '") + shadowFile + "'"; + + gConf->set ("curses", oldCurses); + gConf->set ("color", oldColor); + } + else + throw std::string ("No specified shadow file '") + shadowFile + "'."; + } + else + throw std::string ("Internal error (TDB/Config)."); + } + + catch (std::string& error) + { + std::cout << error << std::endl; + } + + catch (...) + { + std::cout << "Unknown error." << std::endl; + } +} + +//////////////////////////////////////////////////////////////////////////////// +std::string runTaskCommand ( + int argc, + char** argv, + TDB& tdb, + Config& conf) +{ + std::vector args; + for (int i = 1; i < argc; ++i) + args.push_back (argv[i]); + + return runTaskCommand (args, tdb, conf); +} + +//////////////////////////////////////////////////////////////////////////////// +std::string runTaskCommand ( + std::vector & args, + TDB& tdb, + Config& conf) +{ + // If argc == 1 and the default.command configuration variable is set, + // then use that, otherwise stick with argc/argv. + std::string defaultCommand = conf.get ("default.command"); + if (args.size () == 0 && defaultCommand != "") + { + // Stuff the command line. + args.clear (); + split (args, defaultCommand, ' '); + std::cout << "[task " << defaultCommand << "]" << std::endl; + } + + std::string command; + T task; + parse (args, command, task, conf); + + std::string out = ""; + + if (command == "add") handleAdd (tdb, task, conf); + else if (command == "projects") out = handleProjects (tdb, task, conf); + else if (command == "tags") out = handleTags (tdb, task, conf); + else if (command == "list") out = handleList (tdb, task, conf); + else if (command == "info") out = handleInfo (tdb, task, conf); + else if (command == "undelete") out = handleUndelete (tdb, task, conf); + else if (command == "long") out = handleLongList (tdb, task, conf); + else if (command == "ls") out = handleSmallList (tdb, task, conf); + else if (command == "colors") out = handleColor ( conf); + else if (command == "completed") out = handleCompleted (tdb, task, conf); + else if (command == "delete") out = handleDelete (tdb, task, conf); + else if (command == "start") out = handleStart (tdb, task, conf); + else if (command == "done") handleDone (tdb, task, conf); + else if (command == "undo") out = handleUndo (tdb, task, conf); + else if (command == "export") handleExport (tdb, task, conf); + else if (command == "version") out = handleVersion ( conf); + else if (command == "summary") out = handleReportSummary (tdb, task, conf); + else if (command == "next") out = handleReportNext (tdb, task, conf); + else if (command == "history") out = handleReportHistory (tdb, task, conf); + else if (command == "ghistory") out = handleReportGHistory (tdb, task, conf); + else if (command == "calendar") out = handleReportCalendar (tdb, task, conf); + else if (command == "active") out = handleReportActive (tdb, task, conf); + else if (command == "overdue") out = handleReportOverdue (tdb, task, conf); + else if (command == "oldest") out = handleReportOldest (tdb, task, conf); + else if (command == "newest") out = handleReportNewest (tdb, task, conf); + else if (command == "stats") out = handleReportStats (tdb, task, conf); + else if (command == "usage") out = handleReportUsage (tdb, task, conf); + else if (command == "" && task.getId ()) handleModify (tdb, task, conf); + else if (command == "help") longUsage (conf); + else shortUsage (conf); + + return out; +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/task.h b/src/task.h index 9c417435b..8bfb92f0c 100644 --- a/src/task.h +++ b/src/task.h @@ -66,38 +66,42 @@ void handleRecurrence (TDB&, std::vector &); bool generateDueDates (T&, std::vector &); Date getNextRecurrence (Date&, std::string&); void updateRecurrenceMask (TDB&, std::vector &, T&); +void onChangeCallback (); +std::string runTaskCommand (int, char**, TDB&, Config&); +std::string runTaskCommand (std::vector &, TDB&, Config&); // command.cpp -void handleAdd (const TDB&, T&, Config&); -void handleProjects (TDB&, T&, Config&); -void handleTags (TDB&, T&, Config&); -void handleUndelete (TDB&, T&, Config&); -void handleVersion (Config&); +void handleAdd (TDB&, T&, Config&); void handleExport (TDB&, T&, Config&); -void handleDelete (TDB&, T&, Config&); -void handleStart (TDB&, T&, Config&); void handleDone (TDB&, T&, Config&); -void handleUndo (TDB&, T&, Config&); void handleModify (TDB&, T&, Config&); -void handleColor (Config&); +std::string handleProjects (TDB&, T&, Config&); +std::string handleTags (TDB&, T&, Config&); +std::string handleUndelete (TDB&, T&, Config&); +std::string handleVersion (Config&); +std::string handleDelete (TDB&, T&, Config&); +std::string handleStart (TDB&, T&, Config&); +std::string handleUndo (TDB&, T&, Config&); +std::string handleColor (Config&); // report.cpp void filter (std::vector&, T&); -void handleList (TDB&, T&, Config&); -void handleInfo (TDB&, T&, Config&); -void handleLongList (TDB&, T&, Config&); -void handleSmallList (TDB&, T&, Config&); -void handleCompleted (TDB&, T&, Config&); -void handleReportSummary (TDB&, T&, Config&); -void handleReportNext (TDB&, T&, Config&); -void handleReportHistory (TDB&, T&, Config&); -void handleReportGHistory (TDB&, T&, Config&); -void handleReportCalendar (TDB&, T&, Config&); -void handleReportActive (TDB&, T&, Config&); -void handleReportOverdue (TDB&, T&, Config&); -void handleReportStats (TDB&, T&, Config&); -void handleReportOldest (TDB&, T&, Config&); -void handleReportNewest (TDB&, T&, Config&); +std::string handleList (TDB&, T&, Config&); +std::string handleInfo (TDB&, T&, Config&); +std::string handleLongList (TDB&, T&, Config&); +std::string handleSmallList (TDB&, T&, Config&); +std::string handleCompleted (TDB&, T&, Config&); +std::string handleReportSummary (TDB&, T&, Config&); +std::string handleReportNext (TDB&, T&, Config&); +std::string handleReportHistory (TDB&, T&, Config&); +std::string handleReportGHistory (TDB&, T&, Config&); +std::string handleReportUsage (const TDB&, T&, Config&); +std::string handleReportCalendar (TDB&, T&, Config&); +std::string handleReportActive (TDB&, T&, Config&); +std::string handleReportOverdue (TDB&, T&, Config&); +std::string handleReportStats (TDB&, T&, Config&); +std::string handleReportOldest (TDB&, T&, Config&); +std::string handleReportNewest (TDB&, T&, Config&); // util.cpp bool confirm (const std::string&); From 038f4327522b3749cc264de9dbf3af25fcb1e308 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Sat, 1 Nov 2008 16:38:55 -0400 Subject: [PATCH 07/10] - ChangeLog update --- ChangeLog | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9cf6ac639..564c4ed94 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,22 +1,6 @@ ------ current release --------------------------- -1.5.0 (?) - + Removed deprecated TUTORIAL file. - + Removed "usage" command, and support for "command.logging" configuration - variable. - ------- old releases ------------------------------ - -1.4.3 (10/9/2008) - + Fixed misleading task count at bottom on "info" report. - + Added support for a shadow file that contains a plain text task report, - with the "shadow.file" and "shadow.command" configuration variables. - The shadow file is automatically updated whenever the task database - changes. Useful for integrating with "Samurize". - ------- old releases ------------------------------ - 1.4.3 (10/9/2008) + Fixed misleading task count at bottom on "info" report. + Added support for a shadow file that contains a plain text task report, From 396d85cd53b21814b07eb603dc531d6b8d5de713 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Sat, 1 Nov 2008 16:48:28 -0400 Subject: [PATCH 08/10] - Removed "usage" command. --- ChangeLog | 1 + html/advanced.html | 18 ----------- src/report.cpp | 79 ---------------------------------------------- src/task.cpp | 5 --- src/task.h | 1 - 5 files changed, 1 insertion(+), 103 deletions(-) diff --git a/ChangeLog b/ChangeLog index 564c4ed94..ac59f79f0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,7 @@ with the "shadow.file" and "shadow.command" configuration variables. The shadow file is automatically updated whenever the task database changes. Useful for integrating with "Samurize". + + Removed "task usage" command. ------ old releases ------------------------------ diff --git a/html/advanced.html b/html/advanced.html index 97a344cdb..30fc4a436 100644 --- a/html/advanced.html +++ b/html/advanced.html @@ -313,24 +313,6 @@ ID Project Pri Description This command displays all the colors that task supports.

      - % task usage -

      - If logging has been enabled by the "command.logging=on" directive - in the .taskrc file, then task will record every command that is - run. When this command is run, task will display a count of how - many times each command was used. - -

      - -

      - This command is for the purpose of seeing whether command are - actually used. -

      - -

      - This command is deprecated, and will be removed in task 1.5.0 -

      - % task version

      This can be used to show the version number of task, and to display diff --git a/src/report.cpp b/src/report.cpp index 8d3339c58..37d488dca 100644 --- a/src/report.cpp +++ b/src/report.cpp @@ -1628,85 +1628,6 @@ std::string handleReportGHistory (TDB& tdb, T& task, Config& conf) return out.str (); } -//////////////////////////////////////////////////////////////////////////////// -// 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" -// -std::string handleReportUsage (const TDB& tdb, T& task, Config& conf) -{ - std::stringstream out; - - 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, conf); - - 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"); - - if (conf.get ("color", true)) - { - table.setColumnUnderline (0); - table.setColumnUnderline (1); - } - else - table.setTableDashedUnderline (); - - table.setColumnJustification (1, Table::right); - table.sortOn (1, Table::descendingNumeric); - table.setDateFormat (conf.get ("dateformat", "m/d/Y")); - - 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 ()) - out << optionalBlankLine (conf) - << table.render () - << std::endl; - else - out << "No usage." << std::endl; - } - else - out << "Command logging is not enabled, so no history has been kept." - << std::endl; - - return out.str (); -} - //////////////////////////////////////////////////////////////////////////////// std::string renderMonths ( int firstMonth, diff --git a/src/task.cpp b/src/task.cpp index 6bf8b06bf..bb7aff74f 100644 --- a/src/task.cpp +++ b/src/task.cpp @@ -300,10 +300,6 @@ int main (int argc, char** argv) std::string dataLocation = expandPath (conf.get ("data.location")); tdb.dataDirectory (dataLocation); - // Log commands, if desired. - if (conf.get ("command.logging") == "on") - tdb.logCommand (argc, argv); - // Set up TDB callback. std::string shadowFile = expandPath (conf.get ("shadow.file")); if (shadowFile != "") @@ -789,7 +785,6 @@ std::string runTaskCommand ( else if (command == "oldest") out = handleReportOldest (tdb, task, conf); else if (command == "newest") out = handleReportNewest (tdb, task, conf); else if (command == "stats") out = handleReportStats (tdb, task, conf); - else if (command == "usage") out = handleReportUsage (tdb, task, conf); else if (command == "" && task.getId ()) handleModify (tdb, task, conf); else if (command == "help") longUsage (conf); else shortUsage (conf); diff --git a/src/task.h b/src/task.h index 8bfb92f0c..d8bab55ca 100644 --- a/src/task.h +++ b/src/task.h @@ -95,7 +95,6 @@ std::string handleReportSummary (TDB&, T&, Config&); std::string handleReportNext (TDB&, T&, Config&); std::string handleReportHistory (TDB&, T&, Config&); std::string handleReportGHistory (TDB&, T&, Config&); -std::string handleReportUsage (const TDB&, T&, Config&); std::string handleReportCalendar (TDB&, T&, Config&); std::string handleReportActive (TDB&, T&, Config&); std::string handleReportOverdue (TDB&, T&, Config&); From 612a183776a8591f997d7a5885f8512b159a02fd Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Sun, 2 Nov 2008 21:23:34 -0500 Subject: [PATCH 09/10] - Major documentation update - New filter page - New shadow file page --- ChangeLog | 4 +- html/30second.html | 1 + html/advanced.html | 1 + html/color.html | 1 + html/config.html | 1 + html/date.html | 1 + html/filter.html | 41 +++++++++++++++ html/links.html | 105 ++++++++++++++++++++++++++++++++++---- html/recur.html | 1 + html/setup.html | 1 + html/shadow.html | 30 ++++++++++- html/shell.html | 1 + html/simple.html | 1 + html/task.html | 12 +++-- html/troubleshooting.html | 1 + html/usage.html | 1 + html/versions.html | 1 + 17 files changed, 190 insertions(+), 14 deletions(-) diff --git a/ChangeLog b/ChangeLog index ac59f79f0..93dc5afe8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,13 +1,15 @@ ------ current release --------------------------- -1.4.3 (10/9/2008) +1.4.3 (11/1/2008) + Fixed misleading task count at bottom on "info" report. + Added support for a shadow file that contains a plain text task report, with the "shadow.file" and "shadow.command" configuration variables. The shadow file is automatically updated whenever the task database changes. Useful for integrating with "Samurize". + Removed "task usage" command. + + Added documentation for Shadow files. + + Added documentation for task filters. ------ old releases ------------------------------ diff --git a/html/30second.html b/html/30second.html index 227b0bbf2..6cde1c6c2 100644 --- a/html/30second.html +++ b/html/30second.html @@ -25,6 +25,7 @@ Date Handling Troubleshooting Old Versions + Task on the Web

      diff --git a/html/advanced.html b/html/advanced.html index 30fc4a436..03a87d057 100644 --- a/html/advanced.html +++ b/html/advanced.html @@ -25,6 +25,7 @@ Date Handling Troubleshooting Old Versions + Task on the Web
      diff --git a/html/color.html b/html/color.html index f88357e0c..2d939a2e6 100644 --- a/html/color.html +++ b/html/color.html @@ -25,6 +25,7 @@ Date Handling Troubleshooting Old Versions + Task on the Web
      diff --git a/html/config.html b/html/config.html index 144df2498..e20935a90 100644 --- a/html/config.html +++ b/html/config.html @@ -25,6 +25,7 @@ Date Handling Troubleshooting Old Versions + Task on the Web
      diff --git a/html/date.html b/html/date.html index f5dcee84a..9c02cd399 100644 --- a/html/date.html +++ b/html/date.html @@ -25,6 +25,7 @@ Date Handling Troubleshooting Old Versions + Task on the Web
      diff --git a/html/filter.html b/html/filter.html index af801375b..4f353bc44 100644 --- a/html/filter.html +++ b/html/filter.html @@ -25,6 +25,7 @@ Date Handling Troubleshooting Old Versions + Task on the Web
      @@ -34,6 +35,46 @@

      Task Filters

      + A task filter is a means of reducing a task report to a + subset that may consist of all tasks that have a specific + project, priority, tag, or part of the description. +

      + +

      + A task filter consists of additional command line options, + that are specified in the same way as when a task is added. +

      + +

      + All task reports can make use of filters. +

      + +

      + For example, the report: +

      + +
      % task list
      + +

      + Lists all tasks. +

      + +
      % task list the
      + +

      + Lists only tasks with "the" in the task description. +

      + +
      % task list project:Home priority:H
      + +

      + Lists only tasks with both the "Home" project and "H" priority. +

      + +
      % task list +shopping
      + +

      + Lists only tasks with the "shopping" tag.

      diff --git a/html/links.html b/html/links.html index 660206c77..c2739c586 100644 --- a/html/links.html +++ b/html/links.html @@ -25,26 +25,113 @@ Date Handling Troubleshooting Old Versions + Task on the Web



      -

      Task

      +

      Task on the Web

      - Task is an open source, command-line, TODO list manager. + Task links from around the web...

      -

      - Here you will find information on how to acquire, build, configure, - use and become proficient with the task program. -

      +
      + November 2008, Task repository on GitHub +
      +
      + For developers: the task git repository on github.com is now public. +
      +
      - +
      + October 2008, Using Task and Dropbox to manage your To-Do list +
      +
      + by Richard Querin. Richard discusses the ease of setting up task to + use DropBox to share todo lists between work and home. +
      +
      +
      + September 2008, Task visualization +
      +
      + by Stas Antons. Stas - a colleague of mine - presents a visualization + of the simplicity of task. +
      +
      + +
      + June 2008, Building Debian Packages For Task +
      +
      + by Richard Querin. Richard has been providing Debian packages for the + various task releases, and discusses how he got up to speed. +
      +
      + +
      + June 2008, Task 1.0.1 - an attempt at a Cygwin Build How-To +
      +
      + by Richard Querin. Richard shows us how to build task using Cygwin, after + a cry for help on the todo.txt mailing list. +
      +
      + +
      + June 2008, The second task movie +
      +
      + This YouTube movie was made to illustrate some of the features of the task + program, back when task 1.0.0 was released. While task has grown + significantly since then, the commands shown are still valid. It will + soon be time for a new movie! +

      + + This movie has a voice-over that explains what is going on. +

      + + For a higher-quality version, download the whole + movie file (10MB). +

      +
      + +
      + December 2006, The first task movie +
      +
      + This original YouTube task movie was made to illustrate the features of the + then-unreleased task program. The idea was to get some feedback and see + whether anyone was interested in a new implementation of todo.sh, that added + features that are not easily possible with a shell implementation. +

      + + This movie has no voice-over, and you may notice that it exactly duplicates + the commands used in the original todo.sh movie (below). That is, until it + deviates because of new task commands. +

      +
      + +
      + June 2006, The original todo.sh movie +
      +
      + by Gina Trapani. This is the original YouTube todo.sh movie, made to + illustrate the power and simplicity of the original todo.sh program. +
      +
      + +
      + June 2006, Todo.sh, the inspiration for task +
      +
      + by Gina Trapani. The website that introduced me to the power and + simplicity of the original todo.sh program. Contains useful links + and resources - take a look! +


      diff --git a/html/recur.html b/html/recur.html index 2a72f76b6..978c94cef 100644 --- a/html/recur.html +++ b/html/recur.html @@ -25,6 +25,7 @@ Date Handling Troubleshooting Old Versions + Task on the Web
      diff --git a/html/setup.html b/html/setup.html index 68ecbf8b6..7030b3cbc 100644 --- a/html/setup.html +++ b/html/setup.html @@ -25,6 +25,7 @@ Date Handling Troubleshooting Old Versions + Task on the Web
      diff --git a/html/shadow.html b/html/shadow.html index c1d0f5926..0fe592d36 100644 --- a/html/shadow.html +++ b/html/shadow.html @@ -25,6 +25,7 @@ Date Handling Troubleshooting Old Versions + Task on the Web
      @@ -34,12 +35,39 @@

      Task Shadow Files

      + A shadow file is a text file containing a copy of a task + report. It is automatically maintained by task whenever + something changes in the task database. +

      +

      + This means there is always a current version of the task + report kept in a text file. Products such as + Samurize, + MkConsole, + or + GeekTool + can display this file on the computer desktop, so that it + is readily visible. +

      +

      + To use a shadow file, edit your .taskrc configuration file, + and add two entries as shown: +

      +
      shadow.file=/path/to/file
      +shadow.command=list pri:H
      +

      + In this example the shadow file contains a report equivalent + to running "task list pri:H". +

      - +

      + You can use any task command that generates a report, and of + course, you can specify any file name, provided the directory + it resides in already exists.

      diff --git a/html/shell.html b/html/shell.html index 6bade3cc6..ad33441b4 100644 --- a/html/shell.html +++ b/html/shell.html @@ -25,6 +25,7 @@ Date Handling Troubleshooting Old Versions + Task on the Web
      diff --git a/html/simple.html b/html/simple.html index 85ca5782b..9e7f0f88c 100644 --- a/html/simple.html +++ b/html/simple.html @@ -25,6 +25,7 @@ Date Handling Troubleshooting Old Versions + Task on the Web
      diff --git a/html/task.html b/html/task.html index 0c0236789..074d77562 100644 --- a/html/task.html +++ b/html/task.html @@ -25,6 +25,7 @@ Date Handling Troubleshooting Old Versions + Task on the Web

    @@ -77,6 +78,7 @@ Source: task-1.4.3.tar.gz + -

    New in version 1.4.3 (9/18/2008)

    +

    New in version 1.4.3 (11/1/2008)

    • Fixed misleading task count at bottom of "info" report.
    • Added support for a shadow file that contains a plain text task report, with the "shadow.file" and "shadow.command" configuration variables. The shadow file is automatically updated whenever the task database changes. Useful for integrating with "Samurize". +
    • Removed "task usage" command. +
    • Added documentation for Shadow files. +
    • Added documentation for task filters.

    diff --git a/html/troubleshooting.html b/html/troubleshooting.html index d21b59164..5e3147631 100644 --- a/html/troubleshooting.html +++ b/html/troubleshooting.html @@ -25,6 +25,7 @@ Date Handling Troubleshooting Old Versions + Task on the Web

    diff --git a/html/usage.html b/html/usage.html index 92b7b1d88..451c49653 100644 --- a/html/usage.html +++ b/html/usage.html @@ -25,6 +25,7 @@ Date Handling Troubleshooting Old Versions + Task on the Web
    diff --git a/html/versions.html b/html/versions.html index 409c74fa4..347ed039a 100644 --- a/html/versions.html +++ b/html/versions.html @@ -25,6 +25,7 @@ Date Handling Troubleshooting Old Versions + Task on the Web
    From 47ffc0babc8a98f8625cd07b2c696338517ce756 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Sun, 2 Nov 2008 21:43:10 -0500 Subject: [PATCH 10/10] - Deleted old test file. --- src/tests/out | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100755 src/tests/out diff --git a/src/tests/out b/src/tests/out deleted file mode 100755 index c0b81f3b4..000000000 --- a/src/tests/out +++ /dev/null @@ -1,15 +0,0 @@ -./task li due:monday -./task li due:tuesday -./task li due:wednesday -./task li due:thursday -./task li due:friday -./task li due:saturday -./task li due:sunday -./task li due:yesterday -./task li due:today -./task li due:tomorrow -./task li due:eow -./task li due:eom -./task li due:eoy -./task li due:21st -