- Merged 1.4.3 to master

This commit is contained in:
Paul Beckingham 2008-11-01 16:31:30 -04:00
parent a815492111
commit 2cae1df42f
27 changed files with 950 additions and 431 deletions

View file

@ -17,4 +17,5 @@ With thanks to:
Stas Antons Stas Antons
Vincent Fleuranceau Vincent Fleuranceau
T. Charles Yun T. Charles Yun
ArchiMark

View file

@ -8,6 +8,15 @@
------ old releases ------------------------------ ------ 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) 1.4.2 (9/18/2008)
+ "task undo" can now retract a "task done" command, provided no reports + "task undo" can now retract a "task done" command, provided no reports
have been run (and therefore TDB::gc run) have been run (and therefore TDB::gc run)

2
NEWS
View file

@ -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: Task has been built and tested on the following configurations:

6
TUTORIAL Normal file
View file

@ -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

View file

@ -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

View file

@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script. # Process this file with autoconf to produce a configure script.
AC_PREREQ(2.61) 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 AM_INIT_AUTOMAKE
AC_CONFIG_SRCDIR([src/task.cpp]) AC_CONFIG_SRCDIR([src/task.cpp])
AC_CONFIG_HEADER([auto.h]) AC_CONFIG_HEADER([auto.h])
@ -19,7 +19,7 @@ AC_CHECK_LIB(ncurses,endwin)
# Checks for header files. # Checks for header files.
AC_HEADER_STDC AC_HEADER_STDC
AC_CHECK_HEADERS([stdlib.h sys/file.h sys/stat.h sys/time.h unistd.h]) 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. # Checks for typedefs, structures, and compiler characteristics.
AC_HEADER_STDBOOL AC_HEADER_STDBOOL

View file

@ -1,7 +1,7 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"> <html xmlns="http://www.w3.org/1999/xhtml">
<head> <head>
<title>Task 1.4.1</title> <title>30-Second Tutorial</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" /> <meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link rel="stylesheet" href="task.css" type="text/css" /> <link rel="stylesheet" href="task.css" type="text/css" />
</head> </head>

View file

@ -1,7 +1,7 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"> <html xmlns="http://www.w3.org/1999/xhtml">
<head> <head>
<title>Task 1.4.1</title> <title>Advanced Usage</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" /> <meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link rel="stylesheet" href="task.css" type="text/css" /> <link rel="stylesheet" href="task.css" type="text/css" />
</head> </head>
@ -313,6 +313,24 @@ ID Project Pri Description
This command displays all the colors that task supports. This command displays all the colors that task supports.
</p> </p>
<strong>% task usage</strong>
<p>
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.
</p>
<p>
This command is for the purpose of seeing whether command are
actually used.
</p>
<p>
This command is deprecated, and will be removed in task 1.5.0
</p>
<strong>% task version</strong> <strong>% task version</strong>
<p> <p>
This can be used to show the version number of task, and to display This can be used to show the version number of task, and to display

View file

@ -1,7 +1,7 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"> <html xmlns="http://www.w3.org/1999/xhtml">
<head> <head>
<title>Task 1.4.1</title> <title>Color Usage</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" /> <meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link rel="stylesheet" href="task.css" type="text/css" /> <link rel="stylesheet" href="task.css" type="text/css" />
</head> </head>

View file

@ -1,7 +1,7 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"> <html xmlns="http://www.w3.org/1999/xhtml">
<head> <head>
<title>Task 1.4.1</title> <title>Task Configuration</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" /> <meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link rel="stylesheet" href="task.css" type="text/css" /> <link rel="stylesheet" href="task.css" type="text/css" />
</head> </head>
@ -279,6 +279,42 @@ ID Project Pri Description
preceding "task" program name. preceding "task" program name.
</p> </p>
</dd> </dd>
<dt>shadow.file</dt>
<dd>
<p>
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.
</p>
<p>
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.
</p>
<p>
This feature can be useful in maintaining a current file for
use by the "Samurize" program.
</p>
</dd>
<dt>shadow.command</dt>
<dd>
<p>
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.
</p>
<p>
If this command is not specified, task will use the default.command
value instead. If that is not specified, the command "list" is used.
</p>
</dd>
</div> </div>
<br /> <br />

View file

@ -1,7 +1,7 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"> <html xmlns="http://www.w3.org/1999/xhtml">
<head> <head>
<title>Task 1.4.1</title> <title>Date Handling</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" /> <meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link rel="stylesheet" href="task.css" type="text/css" /> <link rel="stylesheet" href="task.css" type="text/css" />
</head> </head>

92
html/filter.html Normal file
View file

@ -0,0 +1,92 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Task Filters</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link rel="stylesheet" href="task.css" type="text/css" />
</head>
<body>
<div id="container">
<table>
<tr>
<td>
<div id="toolbar">
<a href="task.html">Home</a>
<a href="setup.html">Setup</a>
<a href="30second.html">30-second Tutorial</a>
<a href="simple.html">Simple</a>
<a href="advanced.html">Advanced</a>
<a href="shell.html">Shell</a>
<a href="config.html">Configuration</a>
<a href="color.html">Colors</a>
<a href="usage.html">Usage</a>
<a href="recur.html">Recurrence</a>
<a href="date.html">Date Handling</a>
<a href="troubleshooting.html">Troubleshooting</a>
<a href="versions.html">Old Versions</a>
</div>
<div id="content">
<br />
<br />
<br />
<h2 class="title">Task Filters</h2>
<div class="content">
<p>
</p>
</div>
<br />
<br />
<div class="content">
<p>
Copyright 2006-2008, P. Beckingham. All rights reserved.
</p>
</div>
</div>
</td>
<td align="right" valign="top" width="200px">
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<script type="text/javascript"><!--
google_ad_client = "pub-9709799404235424";
/* Task Main */
google_ad_slot = "8660617875";
google_ad_width = 120;
google_ad_height = 600;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</td>
</tr>
</table>
</div>
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-4737637-1");
pageTracker._initData();
pageTracker._trackPageview();
</script>
</body>
</html>

View file

@ -1,7 +1,7 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"> <html xmlns="http://www.w3.org/1999/xhtml">
<head> <head>
<title>Task 1.4.1</title> <title>Recurring Tasks</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" /> <meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link rel="stylesheet" href="task.css" type="text/css" /> <link rel="stylesheet" href="task.css" type="text/css" />
</head> </head>

View file

@ -1,7 +1,7 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"> <html xmlns="http://www.w3.org/1999/xhtml">
<head> <head>
<title>Task 1.4.1</title> <title>Task Setup</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" /> <meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link rel="stylesheet" href="task.css" type="text/css" /> <link rel="stylesheet" href="task.css" type="text/css" />
</head> </head>

98
html/shadow.html Normal file
View file

@ -0,0 +1,98 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Task Shadow Files</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link rel="stylesheet" href="task.css" type="text/css" />
</head>
<body>
<div id="container">
<table>
<tr>
<td>
<div id="toolbar">
<a href="task.html">Home</a>
<a href="setup.html">Setup</a>
<a href="30second.html">30-second Tutorial</a>
<a href="simple.html">Simple</a>
<a href="advanced.html">Advanced</a>
<a href="shell.html">Shell</a>
<a href="config.html">Configuration</a>
<a href="color.html">Colors</a>
<a href="usage.html">Usage</a>
<a href="recur.html">Recurrence</a>
<a href="date.html">Date Handling</a>
<a href="troubleshooting.html">Troubleshooting</a>
<a href="versions.html">Old Versions</a>
</div>
<div id="content">
<br />
<br />
<br />
<h2 class="title">Task Shadow Files</h2>
<div class="content">
<p>
</p>
</div>
<br />
<br />
<div class="content">
<p>
Copyright 2006-2008, P. Beckingham. All rights reserved.
</p>
</div>
</div>
</td>
<td align="right" valign="top" width="200px">
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<script type="text/javascript"><!--
google_ad_client = "pub-9709799404235424";
/* Task Main */
google_ad_slot = "8660617875";
google_ad_width = 120;
google_ad_height = 600;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</td>
</tr>
</table>
</div>
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-4737637-1");
pageTracker._initData();
pageTracker._trackPageview();
</script>
</body>
</html>

View file

@ -1,7 +1,7 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"> <html xmlns="http://www.w3.org/1999/xhtml">
<head> <head>
<title>Task 1.4.1</title> <title>Interacting with the Shell</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" /> <meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link rel="stylesheet" href="task.css" type="text/css" /> <link rel="stylesheet" href="task.css" type="text/css" />
</head> </head>

View file

@ -1,7 +1,7 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"> <html xmlns="http://www.w3.org/1999/xhtml">
<head> <head>
<title>Task 1.4.1</title> <title>Simple Usage</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" /> <meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link rel="stylesheet" href="task.css" type="text/css" /> <link rel="stylesheet" href="task.css" type="text/css" />
</head> </head>

View file

@ -1,7 +1,7 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"> <html xmlns="http://www.w3.org/1999/xhtml">
<head> <head>
<title>Task 1.5.0</title> <title>Latest Release</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" /> <meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link rel="stylesheet" href="task.css" type="text/css" /> <link rel="stylesheet" href="task.css" type="text/css" />
</head> </head>
@ -54,8 +54,8 @@
<li><a href="date.html">Date Handling</a> <li><a href="date.html">Date Handling</a>
<li><a href="troubleshooting.html">Troubleshooting</a> <li><a href="troubleshooting.html">Troubleshooting</a>
<li><a href="versions.html">Old Versions</a> <li><a href="versions.html">Old Versions</a>
<li><a href="filter.html">Filters (coming soon)</a>
<li>Filters (coming soon) <li><a href="shadow.html">Shadow Files (coming soon)</a>
</ul> </ul>
<p> <p>
@ -75,26 +75,28 @@
<table> <table>
<tr> <tr>
<td>Source:</td> <td>Source:</td>
<td><a href="http://www.beckingham.net/task-1.5.0.tar.gz">task-1.5.0.tar.gz</a></td> <td><a href="http://www.beckingham.net/task-1.4.3.tar.gz">task-1.4.3.tar.gz</a></td>
</tr> </tr>
<tr> <tr>
<td>Mac OS X 10.5 (Leopard) Intel-only:</td> <td>Mac OS X 10.5 (Leopard) Intel-only:</td>
<td><a href="http://www.beckingham.net/task-1.5.0.pkg">task-1.5.0.pkg</a></td> <td><a href="http://www.beckingham.net/task-1.4.3.pkg">task-1.4.3.pkg</a></td>
</tr> </tr>
<tr> <tr>
<td> <td>
Debian package: Debian package:
(Thanks to <a href="http://blog.rfquerin.org">Richard Querin</a>): (Thanks to <a href="http://blog.rfquerin.org">Richard Querin</a>):
</td> </td>
<td><a href="http://www.beckingham.net/task_1.5.0-1_i386.deb">task_1.5.0-1_i386.deb</a></td> <td><a href="http://www.beckingham.net/task_1.4.3-1_i386.deb">task_1.4.3-1_i386.deb</a></td>
</tr> </tr>
</table> </table>
<h4>New in version 1.5.0 (?)</h4> <h4>New in version 1.4.3 (9/18/2008)</h4>
<ul> <ul>
<li>Removed deprecated TUTORIAL file. <li>Fixed misleading task count at bottom of "info" report.
<li>Removed "usage" command, and support for "command.logging" configuration <li>Added support for a shadow file that contains a plain text task report,
variable. 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".
</ul> </ul>
<p> <p>

View file

@ -1,7 +1,7 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"> <html xmlns="http://www.w3.org/1999/xhtml">
<head> <head>
<title>Task 1.4.1</title> <title>Task Usage</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" /> <meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link rel="stylesheet" href="task.css" type="text/css" /> <link rel="stylesheet" href="task.css" type="text/css" />
</head> </head>

View file

@ -38,6 +38,12 @@
<p> <p>
<h4>New in version 1.4.2 (9/18/2008)</h4> <h4>New in version 1.4.2 (9/18/2008)</h4>
<a href="http://www.beckingham.net/task-1.4.2.tar.gz">task-1.4.2.tar.gz</a> <a href="http://www.beckingham.net/task-1.4.2.tar.gz">task-1.4.2.tar.gz</a>
<br />
Mac OS X 10.5 (Leopard) Intel-only:
<a href="http://www.beckingham.net/task-1.4.2.pkg">task-1.4.2.pkg</a>
<br />
Debian package: <a href="http://www.beckingham.net/task_1.4.2-1_i386.deb">task_1.4.2-1_i386.deb</a>
(Thanks to <a href="http://blog.rfquerin.org">Richard Querin</a>)
</p> </p>
<ul> <ul>

1
src/.gitignore vendored
View file

@ -1,2 +1 @@
./Makefile
*.o *.o

View file

@ -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); T task (t);
std::vector <std::string> tags; std::vector <std::string> tags;
@ -254,7 +254,9 @@ bool TDB::addT (const T& t) const
if (task.getStatus () == T::pending || if (task.getStatus () == T::pending ||
task.getStatus () == T::recurring) task.getStatus () == T::recurring)
{
return writePending (task); return writePending (task);
}
return writeCompleted (task); return writeCompleted (task);
} }
@ -312,6 +314,7 @@ bool TDB::overwritePending (std::vector <T>& all)
fputs (it->compose ().c_str (), out); fputs (it->compose ().c_str (), out);
fclose (out); fclose (out);
dbChanged ();
return true; return true;
} }
@ -319,7 +322,7 @@ bool TDB::overwritePending (std::vector <T>& all)
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
bool TDB::writePending (const T& t) const bool TDB::writePending (const T& t)
{ {
// Write a single task to the pending file // Write a single task to the pending file
FILE* out; FILE* out;
@ -334,6 +337,7 @@ bool TDB::writePending (const T& t) const
fputs (t.compose ().c_str (), out); fputs (t.compose ().c_str (), out);
fclose (out); fclose (out);
dbChanged ();
return true; 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 // Write a single task to the pending file
FILE* out; FILE* out;
@ -356,6 +360,7 @@ bool TDB::writeCompleted (const T& t) const
fputs (t.compose ().c_str (), out); fputs (t.compose ().c_str (), out);
fclose (out); fclose (out);
dbChanged ();
return true; 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) ();
}
////////////////////////////////////////////////////////////////////////////////

View file

@ -45,23 +45,27 @@ public:
bool allCompletedT (std::vector <T>&) const; bool allCompletedT (std::vector <T>&) const;
bool deleteT (const T&); bool deleteT (const T&);
bool completeT (const T&); bool completeT (const T&);
bool addT (const T&) const; bool addT (const T&);
bool modifyT (const T&); bool modifyT (const T&);
bool logRead (std::vector <std::string>&) const; bool logRead (std::vector <std::string>&) const;
int gc (); int gc ();
int nextId (); int nextId ();
void onChange (void (*)());
private: private:
bool lock (FILE*) const; bool lock (FILE*) const;
bool overwritePending (std::vector <T>&); bool overwritePending (std::vector <T>&);
bool writePending (const T&) const; bool writePending (const T&);
bool writeCompleted (const T&) const; bool writeCompleted (const T&);
bool readLockedFile (const std::string&, std::vector <std::string>&) const; bool readLockedFile (const std::string&, std::vector <std::string>&) const;
void dbChanged ();
private: private:
std::string mPendingFile; std::string mPendingFile;
std::string mCompletedFile; std::string mCompletedFile;
int mId; int mId;
std::vector <void (*)()> mOnChange;
}; };
#endif #endif

View file

@ -26,6 +26,7 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <iostream> #include <iostream>
#include <iomanip> #include <iomanip>
#include <sstream>
#include <fstream> #include <fstream>
#include <sys/types.h> #include <sys/types.h>
#include <stdio.h> #include <stdio.h>
@ -47,7 +48,7 @@
#endif #endif
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void handleAdd (const TDB& tdb, T& task, Config& conf) void handleAdd (TDB& tdb, T& task, Config& conf)
{ {
char entryTime[16]; char entryTime[16];
sprintf (entryTime, "%u", (unsigned int) time (NULL)); sprintf (entryTime, "%u", (unsigned int) time (NULL));
@ -67,7 +68,6 @@ void handleAdd (const TDB& tdb, T& task, Config& conf)
task.setAttribute ("mask", ""); task.setAttribute ("mask", "");
} }
/**/
// Override with default.project, if not specified. // Override with default.project, if not specified.
if (task.getAttribute ("project") == "") if (task.getAttribute ("project") == "")
task.setAttribute ("project", conf.get ("default.project", "")); task.setAttribute ("project", conf.get ("default.project", ""));
@ -79,8 +79,8 @@ void handleAdd (const TDB& tdb, T& task, Config& conf)
if (validPriority (defaultPriority)) if (validPriority (defaultPriority))
task.setAttribute ("priority", defaultPriority); task.setAttribute ("priority", defaultPriority);
} }
/**/
// Disallow blank descriptions.
if (task.getDescription () == "") if (task.getDescription () == "")
throw std::string ("Cannot add a blank task."); 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. // Get all the tasks, including deleted ones.
std::vector <T> tasks; std::vector <T> tasks;
tdb.pendingT (tasks); tdb.pendingT (tasks);
@ -127,21 +129,25 @@ void handleProjects (TDB& tdb, T& task, Config& conf)
table.addCell (row, 1, i->second); table.addCell (row, 1, i->second);
} }
std::cout << optionalBlankLine (conf) out << optionalBlankLine (conf)
<< table.render () << table.render ()
<< optionalBlankLine (conf) << optionalBlankLine (conf)
<< unique.size () << unique.size ()
<< (unique.size () == 1 ? " project" : " projects") << (unique.size () == 1 ? " project" : " projects")
<< std::endl; << std::endl;
} }
else else
std::cout << "No projects." out << "No projects."
<< std::endl; << 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. // Get all the tasks.
std::vector <T> tasks; std::vector <T> tasks;
tdb.pendingT (tasks); tdb.pendingT (tasks);
@ -166,20 +172,23 @@ void handleTags (TDB& tdb, T& task, Config& conf)
std::cout << i->first << std::endl; std::cout << i->first << std::endl;
if (unique.size ()) if (unique.size ())
std::cout << optionalBlankLine (conf) out << optionalBlankLine (conf)
<< unique.size () << unique.size ()
<< (unique.size () == 1 ? " tag" : " tags") << (unique.size () == 1 ? " tag" : " tags")
<< std::endl; << std::endl;
else else
std::cout << "No tags." out << "No tags."
<< std::endl; << std::endl;
return out.str ();
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// If a task is deleted, but is still in the pending file, then it may be // If a task is deleted, but is still in the pending file, then it may be
// undeleted simply by changing it's status. // 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 <T> all; std::vector <T> all;
tdb.allPendingT (all); tdb.allPendingT (all);
@ -193,8 +202,8 @@ void handleUndelete (TDB& tdb, T& task, Config& conf)
{ {
if (it->getAttribute ("recur") != "") if (it->getAttribute ("recur") != "")
{ {
std::cout << "Task does not support 'undelete' for recurring tasks." << std::endl; out << "Task does not support 'undelete' for recurring tasks." << std::endl;
return; return out.str ();
} }
T restored (*it); T restored (*it);
@ -202,27 +211,31 @@ void handleUndelete (TDB& tdb, T& task, Config& conf)
restored.removeAttribute ("end"); restored.removeAttribute ("end");
tdb.modifyT (restored); tdb.modifyT (restored);
std::cout << "Task " << id << " successfully undeleted." << std::endl; out << "Task " << id << " successfully undeleted." << std::endl;
return; return out.str ();
} }
else else
{ {
std::cout << "Task " << id << " is not deleted - therefore cannot undelete." << std::endl; out << "Task " << id << " is not deleted - therefore cannot undelete." << std::endl;
return; return out.str ();
} }
} }
} }
std::cout << "Task " << id out << "Task " << id
<< " not found - tasks can only be reliably undeleted if the undelete" << std::endl << " not found - tasks can only be reliably undeleted if the undelete" << std::endl
<< "command is run immediately after the errant delete command." << 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 // If a task is done, but is still in the pending file, then it may be undone
// simply by changing it's status. // 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 <T> all; std::vector <T> all;
tdb.allPendingT (all); tdb.allPendingT (all);
@ -235,35 +248,36 @@ void handleUndo (TDB& tdb, T& task, Config& conf)
if (it->getStatus () == T::completed) if (it->getStatus () == T::completed)
{ {
if (it->getAttribute ("recur") != "") if (it->getAttribute ("recur") != "")
{ return std::string ("Task does not support 'undo' for recurring tasks.\n");
std::cout << "Task does not support 'undo' for recurring tasks." << std::endl;
return;
}
T restored (*it); T restored (*it);
restored.setStatus (T::pending); restored.setStatus (T::pending);
restored.removeAttribute ("end"); restored.removeAttribute ("end");
tdb.modifyT (restored); tdb.modifyT (restored);
std::cout << "Task " << id << " successfully undone." << std::endl; out << "Task " << id << " successfully undone." << std::endl;
return; return out.str ();
} }
else else
{ {
std::cout << "Task " << id << " is not done - therefore cannot be undone." << std::endl; out << "Task " << id << " is not done - therefore cannot be undone." << std::endl;
return; return out.str ();
} }
} }
} }
std::cout << "Task " << id out << "Task " << id
<< " not found - tasks can only be reliably undone if the undo" << std::endl << " not found - tasks can only be reliably undone if the undo" << std::endl
<< "command is run immediately after the errant done command." << 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. // Determine window size, and set table accordingly.
int width = conf.get ("defaultwidth", 80); int width = conf.get ("defaultwidth", 80);
#ifdef HAVE_LIBNCURSES #ifdef HAVE_LIBNCURSES
@ -329,40 +343,42 @@ void handleVersion (Config& conf)
} }
} }
std::cout << "Copyright (C) 2006 - 2008, P. Beckingham." out << "Copyright (C) 2006 - 2008, P. Beckingham."
<< std::endl << std::endl
<< (conf.get ("color", true) ? Text::colorize (Text::bold, Text::nocolor, PACKAGE) : PACKAGE) << (conf.get ("color", true) ? Text::colorize (Text::bold, Text::nocolor, PACKAGE) : PACKAGE)
<< " " << " "
<< (conf.get ("color", true) ? Text::colorize (Text::bold, Text::nocolor, VERSION) : VERSION) << (conf.get ("color", true) ? Text::colorize (Text::bold, Text::nocolor, VERSION) : VERSION)
<< std::endl << std::endl
<< disclaimer.render () << disclaimer.render ()
<< std::endl << std::endl
<< table.render () << table.render ()
<< link.render () << link.render ()
<< std::endl; << std::endl;
// Verify installation. This is mentioned in the documentation as the way to // Verify installation. This is mentioned in the documentation as the way to
// ensure everything is properly installed. // ensure everything is properly installed.
if (all.size () == 0) if (all.size () == 0)
std::cout << "Configuration error: .taskrc contains no entries" out << "Configuration error: .taskrc contains no entries"
<< std::endl; << std::endl;
else else
{ {
if (conf.get ("data.location") == "") if (conf.get ("data.location") == "")
std::cout << "Configuration error: data.location not specified in .taskrc " out << "Configuration error: data.location not specified in .taskrc "
"file." "file."
<< std::endl; << std::endl;
if (access (expandPath (conf.get ("data.location")).c_str (), X_OK)) if (access (expandPath (conf.get ("data.location")).c_str (), X_OK))
std::cout << "Configuration error: data.location contains a directory name" out << "Configuration error: data.location contains a directory name"
" that doesn't exist, or is unreadable." " that doesn't exist, or is unreadable."
<< std::endl; << 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?")) if (conf.get ("confirmation") != "yes" || confirm ("Permanently delete task?"))
{ {
@ -386,7 +402,7 @@ void handleDelete (TDB& tdb, T& task, Config& conf)
sibling->getUUID () == parent) sibling->getUUID () == parent)
tdb.deleteT (*sibling); tdb.deleteT (*sibling);
return; return std::string ("");
} }
else else
{ {
@ -394,7 +410,7 @@ void handleDelete (TDB& tdb, T& task, Config& conf)
t->setStatus (T::deleted); t->setStatus (T::deleted);
updateRecurrenceMask (tdb, all, *t); updateRecurrenceMask (tdb, all, *t);
tdb.deleteT (*t); tdb.deleteT (*t);
return; return std::string ("");
} }
} }
else else
@ -405,11 +421,13 @@ void handleDelete (TDB& tdb, T& task, Config& conf)
} }
} }
else 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 <T> all; std::vector <T> all;
tdb.pendingT (all); tdb.pendingT (all);
@ -431,14 +449,19 @@ void handleStart (TDB& tdb, T& task, Config& conf)
tdb.modifyT (original); tdb.modifyT (original);
nag (tdb, task, conf); nag (tdb, task, conf);
return; return std::string ("");
} }
else 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."); 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)) if (conf.get ("color", true))
{ {
std::cout << optionalBlankLine (conf) << "Foreground" << std::endl out << optionalBlankLine (conf) << "Foreground" << std::endl
<< " " << " "
<< Text::colorize (Text::bold, Text::nocolor, "bold") << " " << Text::colorize (Text::bold, Text::nocolor, "bold") << " "
<< Text::colorize (Text::underline, Text::nocolor, "underline") << " " << Text::colorize (Text::underline, Text::nocolor, "underline") << " "
<< Text::colorize (Text::bold_underline, Text::nocolor, "bold_underline") << std::endl << Text::colorize (Text::bold_underline, Text::nocolor, "bold_underline") << std::endl
<< " " << Text::colorize (Text::black, Text::nocolor, "black") << " " << " " << Text::colorize (Text::black, Text::nocolor, "black") << " "
<< Text::colorize (Text::bold_black, Text::nocolor, "bold_black") << " " << Text::colorize (Text::bold_black, Text::nocolor, "bold_black") << " "
<< Text::colorize (Text::underline_black, Text::nocolor, "underline_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::bold_underline_black, Text::nocolor, "bold_underline_black") << std::endl
<< " " << Text::colorize (Text::red, Text::nocolor, "red") << " " << " " << Text::colorize (Text::red, Text::nocolor, "red") << " "
<< Text::colorize (Text::bold_red, Text::nocolor, "bold_red") << " " << Text::colorize (Text::bold_red, Text::nocolor, "bold_red") << " "
<< Text::colorize (Text::underline_red, Text::nocolor, "underline_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::bold_underline_red, Text::nocolor, "bold_underline_red") << std::endl
<< " " << Text::colorize (Text::green, Text::nocolor, "green") << " " << " " << Text::colorize (Text::green, Text::nocolor, "green") << " "
<< Text::colorize (Text::bold_green, Text::nocolor, "bold_green") << " " << Text::colorize (Text::bold_green, Text::nocolor, "bold_green") << " "
<< Text::colorize (Text::underline_green, Text::nocolor, "underline_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::bold_underline_green, Text::nocolor, "bold_underline_green") << std::endl
<< " " << Text::colorize (Text::yellow, Text::nocolor, "yellow") << " " << " " << Text::colorize (Text::yellow, Text::nocolor, "yellow") << " "
<< Text::colorize (Text::bold_yellow, Text::nocolor, "bold_yellow") << " " << Text::colorize (Text::bold_yellow, Text::nocolor, "bold_yellow") << " "
<< Text::colorize (Text::underline_yellow, Text::nocolor, "underline_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::bold_underline_yellow, Text::nocolor, "bold_underline_yellow") << std::endl
<< " " << Text::colorize (Text::blue, Text::nocolor, "blue") << " " << " " << Text::colorize (Text::blue, Text::nocolor, "blue") << " "
<< Text::colorize (Text::bold_blue, Text::nocolor, "bold_blue") << " " << Text::colorize (Text::bold_blue, Text::nocolor, "bold_blue") << " "
<< Text::colorize (Text::underline_blue, Text::nocolor, "underline_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::bold_underline_blue, Text::nocolor, "bold_underline_blue") << std::endl
<< " " << Text::colorize (Text::magenta, Text::nocolor, "magenta") << " " << " " << Text::colorize (Text::magenta, Text::nocolor, "magenta") << " "
<< Text::colorize (Text::bold_magenta, Text::nocolor, "bold_magenta") << " " << Text::colorize (Text::bold_magenta, Text::nocolor, "bold_magenta") << " "
<< Text::colorize (Text::underline_magenta, Text::nocolor, "underline_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::bold_underline_magenta, Text::nocolor, "bold_underline_magenta") << std::endl
<< " " << Text::colorize (Text::cyan, Text::nocolor, "cyan") << " " << " " << Text::colorize (Text::cyan, Text::nocolor, "cyan") << " "
<< Text::colorize (Text::bold_cyan, Text::nocolor, "bold_cyan") << " " << Text::colorize (Text::bold_cyan, Text::nocolor, "bold_cyan") << " "
<< Text::colorize (Text::underline_cyan, Text::nocolor, "underline_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::bold_underline_cyan, Text::nocolor, "bold_underline_cyan") << std::endl
<< " " << Text::colorize (Text::white, Text::nocolor, "white") << " " << " " << Text::colorize (Text::white, Text::nocolor, "white") << " "
<< Text::colorize (Text::bold_white, Text::nocolor, "bold_white") << " " << Text::colorize (Text::bold_white, Text::nocolor, "bold_white") << " "
<< Text::colorize (Text::underline_white, Text::nocolor, "underline_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::bold_underline_white, Text::nocolor, "bold_underline_white") << std::endl
<< std::endl << "Background" << std::endl << std::endl << "Background" << std::endl
<< " " << Text::colorize (Text::nocolor, Text::on_black, "on_black") << " " << " " << 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_bright_black, "on_bright_black") << std::endl
<< " " << Text::colorize (Text::nocolor, Text::on_red, "on_red") << " " << " " << 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_bright_red, "on_bright_red") << std::endl
<< " " << Text::colorize (Text::nocolor, Text::on_green, "on_green") << " " << " " << 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_bright_green, "on_bright_green") << std::endl
<< " " << Text::colorize (Text::nocolor, Text::on_yellow, "on_yellow") << " " << " " << 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_bright_yellow, "on_bright_yellow") << std::endl
<< " " << Text::colorize (Text::nocolor, Text::on_blue, "on_blue") << " " << " " << 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_bright_blue, "on_bright_blue") << std::endl
<< " " << Text::colorize (Text::nocolor, Text::on_magenta, "on_magenta") << " " << " " << 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_bright_magenta, "on_bright_magenta") << std::endl
<< " " << Text::colorize (Text::nocolor, Text::on_cyan, "on_cyan") << " " << " " << 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_bright_cyan, "on_bright_cyan") << std::endl
<< " " << Text::colorize (Text::nocolor, Text::on_white, "on_white") << " " << " " << 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_bright_white, "on_bright_white") << std::endl
<< optionalBlankLine (conf); << optionalBlankLine (conf);
} }
else 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 ();
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View file

@ -26,6 +26,7 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <iostream> #include <iostream>
#include <iomanip> #include <iomanip>
#include <sstream>
#include <fstream> #include <fstream>
#include <sys/types.h> #include <sys/types.h>
#include <stdio.h> #include <stdio.h>
@ -110,8 +111,10 @@ void filter (std::vector<T>& all, T& task)
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Successively apply filters based on the task object built from the command // Successively apply filters based on the task object built from the command
// line. Tasks that match all the specified criteria are listed. // 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. // Determine window size, and set table accordingly.
int width = conf.get ("defaultwidth", 80); int width = conf.get ("defaultwidth", 80);
#ifdef HAVE_LIBNCURSES #ifdef HAVE_LIBNCURSES
@ -242,23 +245,27 @@ void handleList (TDB& tdb, T& task, Config& conf)
} }
if (table.rowCount ()) if (table.rowCount ())
std::cout << optionalBlankLine (conf) out << optionalBlankLine (conf)
<< table.render () << table.render ()
<< optionalBlankLine (conf) << optionalBlankLine (conf)
<< table.rowCount () << table.rowCount ()
<< (table.rowCount () == 1 ? " task" : " tasks") << (table.rowCount () == 1 ? " task" : " tasks")
<< std::endl; << std::endl;
else else
std::cout << "No matches." out << "No matches."
<< std::endl; << std::endl;
return out.str ();
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Successively apply filters based on the task object built from the command // 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 // line. Tasks that match all the specified criteria are listed. Show a narrow
// list that works better on mobile devices. // 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. // Determine window size, and set table accordingly.
int width = conf.get ("defaultwidth", 80); int width = conf.get ("defaultwidth", 80);
#ifdef HAVE_LIBNCURSES #ifdef HAVE_LIBNCURSES
@ -371,22 +378,26 @@ void handleSmallList (TDB& tdb, T& task, Config& conf)
} }
if (table.rowCount ()) if (table.rowCount ())
std::cout << optionalBlankLine (conf) out << optionalBlankLine (conf)
<< table.render () << table.render ()
<< optionalBlankLine (conf) << optionalBlankLine (conf)
<< table.rowCount () << table.rowCount ()
<< (table.rowCount () == 1 ? " task" : " tasks") << (table.rowCount () == 1 ? " task" : " tasks")
<< std::endl; << std::endl;
else else
std::cout << "No matches." out << "No matches."
<< std::endl; << std::endl;
return out.str ();
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Successively apply filters based on the task object built from the command // Successively apply filters based on the task object built from the command
// line. Tasks that match all the specified criteria are listed. // 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. // Determine window size, and set table accordingly.
int width = conf.get ("defaultwidth", 80); int width = conf.get ("defaultwidth", 80);
#ifdef HAVE_LIBNCURSES #ifdef HAVE_LIBNCURSES
@ -459,21 +470,25 @@ void handleCompleted (TDB& tdb, T& task, Config& conf)
} }
if (table.rowCount ()) if (table.rowCount ())
std::cout << optionalBlankLine (conf) out << optionalBlankLine (conf)
<< table.render () << table.render ()
<< optionalBlankLine (conf) << optionalBlankLine (conf)
<< table.rowCount () << table.rowCount ()
<< (table.rowCount () == 1 ? " task" : " tasks") << (table.rowCount () == 1 ? " task" : " tasks")
<< std::endl; << std::endl;
else else
std::cout << "No matches." out << "No matches."
<< std::endl; << std::endl;
return out.str ();
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Display all information for the given task. // 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. // Determine window size, and set table accordingly.
int width = conf.get ("defaultwidth", 80); int width = conf.get ("defaultwidth", 80);
#ifdef HAVE_LIBNCURSES #ifdef HAVE_LIBNCURSES
@ -657,21 +672,22 @@ void handleInfo (TDB& tdb, T& task, Config& conf)
} }
if (table.rowCount ()) if (table.rowCount ())
std::cout << optionalBlankLine (conf) out << optionalBlankLine (conf)
<< table.render () << table.render ()
<< optionalBlankLine (conf) << std::endl;
<< table.rowCount ()
<< (table.rowCount () == 1 ? " task" : " tasks")
<< std::endl;
else 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 // Successively apply filters based on the task object built from the command
// line. Tasks that match all the specified criteria are listed. // 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. // Determine window size, and set table accordingly.
int width = conf.get ("defaultwidth", 80); int width = conf.get ("defaultwidth", 80);
#ifdef HAVE_LIBNCURSES #ifdef HAVE_LIBNCURSES
@ -827,22 +843,26 @@ void handleLongList (TDB& tdb, T& task, Config& conf)
} }
if (table.rowCount ()) if (table.rowCount ())
std::cout << optionalBlankLine (conf) out << optionalBlankLine (conf)
<< table.render () << table.render ()
<< optionalBlankLine (conf) << optionalBlankLine (conf)
<< table.rowCount () << table.rowCount ()
<< (table.rowCount () == 1 ? " task" : " tasks") << (table.rowCount () == 1 ? " task" : " tasks")
<< std::endl; << std::endl;
else else
std::cout << "No matches." << std::endl; out << "No matches." << std::endl;
return out.str ();
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Project Tasks Avg Age Status // Project Tasks Avg Age Status
// A 12 13d XXXXXXXX------ // A 12 13d XXXXXXXX------
// B 109 3d 12h XX------------ // 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. // Generate unique list of project names.
tdb.gc (); tdb.gc ();
std::map <std::string, bool> allProjects; std::map <std::string, bool> allProjects;
@ -984,14 +1004,16 @@ void handleReportSummary (TDB& tdb, T& task, Config& conf)
} }
if (table.rowCount ()) if (table.rowCount ())
std::cout << optionalBlankLine (conf) out << optionalBlankLine (conf)
<< table.render () << table.render ()
<< optionalBlankLine (conf) << optionalBlankLine (conf)
<< table.rowCount () << table.rowCount ()
<< (table.rowCount () == 1 ? " project" : " projects") << (table.rowCount () == 1 ? " project" : " projects")
<< std::endl; << std::endl;
else 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 // 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. // Load all pending.
tdb.gc (); tdb.gc ();
std::vector <T> pending; std::vector <T> pending;
@ -1154,15 +1178,17 @@ void handleReportNext (TDB& tdb, T& task, Config& conf)
} }
if (table.rowCount ()) if (table.rowCount ())
std::cout << optionalBlankLine (conf) out << optionalBlankLine (conf)
<< table.render () << table.render ()
<< optionalBlankLine (conf) << optionalBlankLine (conf)
<< table.rowCount () << table.rowCount ()
<< (table.rowCount () == 1 ? " task" : " tasks") << (table.rowCount () == 1 ? " task" : " tasks")
<< std::endl; << std::endl;
else else
std::cout << "No matches." out << "No matches."
<< std::endl; << std::endl;
return out.str ();
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -1187,8 +1213,10 @@ time_t monthlyEpoch (const std::string& date)
return 0; return 0;
} }
void handleReportHistory (TDB& tdb, T& task, Config& conf) std::string handleReportHistory (TDB& tdb, T& task, Config& conf)
{ {
std::stringstream out;
std::map <time_t, int> groups; std::map <time_t, int> groups;
std::map <time_t, int> addedGroup; std::map <time_t, int> addedGroup;
std::map <time_t, int> completedGroup; std::map <time_t, int> completedGroup;
@ -1363,16 +1391,20 @@ void handleReportHistory (TDB& tdb, T& task, Config& conf)
} }
if (table.rowCount ()) if (table.rowCount ())
std::cout << optionalBlankLine (conf) out << optionalBlankLine (conf)
<< table.render () << table.render ()
<< std::endl; << std::endl;
else 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. // Determine window size, and set table accordingly.
int width = conf.get ("defaultwidth", 80); int width = conf.get ("defaultwidth", 80);
#ifdef HAVE_LIBNCURSES #ifdef HAVE_LIBNCURSES
@ -1574,24 +1606,105 @@ void handleReportGHistory (TDB& tdb, T& task, Config& conf)
if (table.rowCount ()) if (table.rowCount ())
{ {
std::cout << optionalBlankLine (conf) out << optionalBlankLine (conf)
<< table.render () << table.render ()
<< std::endl; << std::endl;
if (conf.get ("color", true)) if (conf.get ("color", true))
std::cout << "Legend: " out << "Legend: "
<< Text::colorize (Text::black, Text::on_red, "added") << Text::colorize (Text::black, Text::on_red, "added")
<< ", " << ", "
<< Text::colorize (Text::black, Text::on_green, "completed") << Text::colorize (Text::black, Text::on_green, "completed")
<< ", " << ", "
<< Text::colorize (Text::black, Text::on_yellow, "deleted") << Text::colorize (Text::black, Text::on_yellow, "deleted")
<< optionalBlankLine (conf) << optionalBlankLine (conf)
<< std::endl; << std::endl;
else else
std::cout << "Legend: + added, X completed, - deleted" << std::endl; out << "Legend: + added, X completed, - deleted" << std::endl;
} }
else 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 <std::string, int> usage;
std::vector <std::string> 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 <std::string> 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. // Load all the pending tasks.
tdb.gc (); tdb.gc ();
std::vector <T> pending; std::vector <T> pending;
@ -1751,7 +1866,7 @@ void handleReportCalendar (TDB& tdb, T& task, Config& conf)
int mTo = newest.month (); int mTo = newest.month ();
int yTo = newest.year (); int yTo = newest.year ();
std::cout << std::endl; out << std::endl;
std::string output; std::string output;
int monthsPerLine = (conf.get ("monthsperline", 1)); 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 left = (18 - month.length ()) / 2 + 1;
int right = 18 - left - month.length (); int right = 18 - left - month.length ();
std::cout << std::setw (left) << ' ' out << std::setw (left) << ' '
<< month << month
<< ' ' << ' '
<< nextY << nextY
<< std::setw (right) << ' '; << std::setw (right) << ' ';
if (++nextM > 12) if (++nextM > 12)
{ {
@ -1781,10 +1896,10 @@ void handleReportCalendar (TDB& tdb, T& task, Config& conf)
} }
} }
std::cout << std::endl out << std::endl
<< optionalBlankLine (conf) << optionalBlankLine (conf)
<< renderMonths (mFrom, yFrom, today, pending, conf) << renderMonths (mFrom, yFrom, today, pending, conf)
<< std::endl; << std::endl;
mFrom += monthsPerLine; mFrom += monthsPerLine;
if (mFrom > 12) if (mFrom > 12)
@ -1794,20 +1909,24 @@ void handleReportCalendar (TDB& tdb, T& task, Config& conf)
} }
} }
std::cout << "Legend: " out << "Legend: "
<< Text::colorize (Text::cyan, Text::nocolor, "today") << Text::colorize (Text::cyan, Text::nocolor, "today")
<< ", " << ", "
<< Text::colorize (Text::black, Text::on_yellow, "due") << Text::colorize (Text::black, Text::on_yellow, "due")
<< ", " << ", "
<< Text::colorize (Text::black, Text::on_red, "overdue") << Text::colorize (Text::black, Text::on_red, "overdue")
<< "." << "."
<< optionalBlankLine (conf) << optionalBlankLine (conf)
<< std::endl; << 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. // Determine window size, and set table accordingly.
int width = conf.get ("defaultwidth", 80); int width = conf.get ("defaultwidth", 80);
#ifdef HAVE_LIBNCURSES #ifdef HAVE_LIBNCURSES
@ -1913,19 +2032,23 @@ void handleReportActive (TDB& tdb, T& task, Config& conf)
} }
if (table.rowCount ()) if (table.rowCount ())
std::cout << optionalBlankLine (conf) out << optionalBlankLine (conf)
<< table.render () << table.render ()
<< optionalBlankLine (conf) << optionalBlankLine (conf)
<< table.rowCount () << table.rowCount ()
<< (table.rowCount () == 1 ? " task" : " tasks") << (table.rowCount () == 1 ? " task" : " tasks")
<< std::endl; << std::endl;
else 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. // Determine window size, and set table accordingly.
int width = conf.get ("defaultwidth", 80); int width = conf.get ("defaultwidth", 80);
#ifdef HAVE_LIBNCURSES #ifdef HAVE_LIBNCURSES
@ -2020,21 +2143,25 @@ void handleReportOverdue (TDB& tdb, T& task, Config& conf)
} }
if (table.rowCount ()) if (table.rowCount ())
std::cout << optionalBlankLine (conf) out << optionalBlankLine (conf)
<< table.render () << table.render ()
<< optionalBlankLine (conf) << optionalBlankLine (conf)
<< table.rowCount () << table.rowCount ()
<< (table.rowCount () == 1 ? " task" : " tasks") << (table.rowCount () == 1 ? " task" : " tasks")
<< std::endl; << std::endl;
else 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 // Successively apply filters based on the task object built from the command
// line. Tasks that match all the specified criteria are listed. // 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. // Determine window size, and set table accordingly.
int width = conf.get ("defaultwidth", 80); int width = conf.get ("defaultwidth", 80);
#ifdef HAVE_LIBNCURSES #ifdef HAVE_LIBNCURSES
@ -2164,22 +2291,26 @@ void handleReportOldest (TDB& tdb, T& task, Config& conf)
} }
if (table.rowCount ()) if (table.rowCount ())
std::cout << optionalBlankLine (conf) out << optionalBlankLine (conf)
<< table.render () << table.render ()
<< optionalBlankLine (conf) << optionalBlankLine (conf)
<< table.rowCount () << table.rowCount ()
<< (table.rowCount () == 1 ? " task" : " tasks") << (table.rowCount () == 1 ? " task" : " tasks")
<< std::endl; << std::endl;
else else
std::cout << "No matches." out << "No matches."
<< std::endl; << std::endl;
return out.str ();
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Successively apply filters based on the task object built from the command // Successively apply filters based on the task object built from the command
// line. Tasks that match all the specified criteria are listed. // 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. // Determine window size, and set table accordingly.
int width = conf.get ("defaultwidth", 80); int width = conf.get ("defaultwidth", 80);
#ifdef HAVE_LIBNCURSES #ifdef HAVE_LIBNCURSES
@ -2310,21 +2441,25 @@ void handleReportNewest (TDB& tdb, T& task, Config& conf)
} }
if (table.rowCount ()) if (table.rowCount ())
std::cout << optionalBlankLine (conf) out << optionalBlankLine (conf)
<< table.render () << table.render ()
<< optionalBlankLine (conf) << optionalBlankLine (conf)
<< table.rowCount () << table.rowCount ()
<< (table.rowCount () == 1 ? " task" : " tasks") << (table.rowCount () == 1 ? " task" : " tasks")
<< std::endl; << std::endl;
else else
std::cout << "No matches." out << "No matches."
<< std::endl; << 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. // Get all the tasks.
std::vector <T> tasks; std::vector <T> tasks;
tdb.allT (tasks); tdb.allT (tasks);
@ -2371,40 +2506,42 @@ void handleReportStats (TDB& tdb, T& task, Config& conf)
if (tags.size ()) ++taggedT; if (tags.size ()) ++taggedT;
} }
std::cout << "Pending " << pendingT << std::endl out << "Pending " << pendingT << std::endl
<< "Recurring " << recurringT << std::endl << "Recurring " << recurringT << std::endl
<< "Completed " << completedT << std::endl << "Completed " << completedT << std::endl
<< "Deleted " << deletedT << std::endl << "Deleted " << deletedT << std::endl
<< "Total " << totalT << std::endl; << "Total " << totalT << std::endl;
if (tasks.size ()) if (tasks.size ())
{ {
Date e (earliest); 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); Date l (latest);
std::cout << "Newest task " << l.toString (conf.get ("dateformat", "m/d/Y")) << std::endl; out << "Newest task " << l.toString (conf.get ("dateformat", "m/d/Y")) << std::endl;
std::cout << "Task used for " << formatSeconds (latest - earliest) << std::endl; out << "Task used for " << formatSeconds (latest - earliest) << std::endl;
} }
if (totalT) 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) 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) 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) if (pendingT || completedT)
std::cout << "Average time pending " out << "Average time pending "
<< formatSeconds ((int) ((daysPending / (pendingT + completedT)) * 86400)) << formatSeconds ((int) ((daysPending / (pendingT + completedT)) * 86400))
<< std::endl; << std::endl;
if (totalT) if (totalT)
{ {
std::cout << "Average desc length " << (int) (descLength / totalT) << " characters" << std::endl; out << "Average desc length " << (int) (descLength / totalT) << " characters" << std::endl;
std::cout << "Tasks tagged " << std::setprecision (3) << (100.0 * taggedT / totalT) << "%" << std::endl; out << "Tasks tagged " << std::setprecision (3) << (100.0 * taggedT / totalT) << "%" << std::endl;
} }
return out.str ();
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View file

@ -46,6 +46,11 @@
#include <ncurses.h> #include <ncurses.h>
#endif #endif
////////////////////////////////////////////////////////////////////////////////
// Globals for exclusive use by callback function.
static TDB* gTdb = NULL;
static Config* gConf = NULL;
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
static void shortUsage (Config& conf) 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 // Load the config file from the home directory. If the file cannot be
// found, offer to create a sample one. // found, offer to create a sample one.
Config conf; Config conf;
gConf = &conf;
loadConfFile (argc, argv, conf); loadConfFile (argc, argv, conf);
// When redirecting output to a file, do not use color, curses. // When redirecting output to a file, do not use color, curses.
@ -290,69 +296,41 @@ int main (int argc, char** argv)
} }
TDB tdb; 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, // Log commands, if desired.
// then use that, otherwise stick with argc/argv. if (conf.get ("command.logging") == "on")
std::vector <std::string> args; tdb.logCommand (argc, argv);
std::string defaultCommand = conf.get ("default.command");
if (argc == 1 && defaultCommand != "") // Set up TDB callback.
std::string shadowFile = expandPath (conf.get ("shadow.file"));
if (shadowFile != "")
{ {
// Stuff the command line. if (shadowFile == dataLocation + "/pending.data")
split (args, defaultCommand, ' '); throw std::string ("Configuration variable 'shadow.file' is set to "
std::cout << "[task " << defaultCommand << "]" << std::endl; "overwrite your pending tasks. Please change it.");
}
else if (shadowFile == dataLocation + "/completed.data")
{ throw std::string ("Configuration variable 'shadow.file' is set to "
// Parse the command line. "overwrite your completed tasks. Please change it.");
for (int i = 1; i < argc; ++i)
args.push_back (argv[i]); tdb.onChange (&onChangeCallback);
} }
std::string command; std::cout << runTaskCommand (argc, argv, tdb, conf);
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);
} }
catch (std::string& error) catch (std::string& error)
{ {
std::cout << error << std::endl; std::cerr << error << std::endl;
return -1; return -1;
} }
catch (...) catch (...)
{ {
std::cout << "Unknown error." << std::endl; std::cerr << "Unknown error." << std::endl;
return -2; return -2;
} }
@ -391,12 +369,18 @@ int getDueState (const std::string& due)
if (due.length ()) if (due.length ())
{ {
Date dt (::atoi (due.c_str ())); 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; return 2;
Date nextweek = now + 7 * 86400; Date nextweek = today + 7 * 86400;
if (dt < nextweek) if (dt < nextweek)
return 1; return 1;
} }
@ -544,7 +528,6 @@ Date getNextRecurrence (Date& current, std::string& period)
while (! Date::valid (m, d, y)) while (! Date::valid (m, d, y))
--d; --d;
// std::cout << "# next " << current.toString () << " + " << period << " = " << m << "/" << d << "/" << y << std::endl;
return Date (m, d, y); return Date (m, d, y);
} }
@ -563,7 +546,6 @@ Date getNextRecurrence (Date& current, std::string& period)
while (! Date::valid (m, d, y)) while (! Date::valid (m, d, y))
--d; --d;
// std::cout << "# next " << current.toString () << " + " << period << " = " << m << "/" << d << "/" << y << std::endl;
return Date (m, d, y); return Date (m, d, y);
} }
@ -579,7 +561,6 @@ Date getNextRecurrence (Date& current, std::string& period)
while (! Date::valid (m, d, y)) while (! Date::valid (m, d, y))
--d; --d;
// std::cout << "# next " << current.toString () << " + " << period << " = " << m << "/" << d << "/" << y << std::endl;
return Date (m, d, y); return Date (m, d, y);
} }
@ -598,7 +579,6 @@ Date getNextRecurrence (Date& current, std::string& period)
while (! Date::valid (m, d, y)) while (! Date::valid (m, d, y))
--d; --d;
// std::cout << "# next " << current.toString () << " + " << period << " = " << m << "/" << d << "/" << y << std::endl;
return Date (m, d, y); return Date (m, d, y);
} }
@ -614,7 +594,6 @@ Date getNextRecurrence (Date& current, std::string& period)
while (! Date::valid (m, d, y)) while (! Date::valid (m, d, y))
--d; --d;
// std::cout << "# next " << current.toString () << " + " << period << " = " << m << "/" << d << "/" << y << std::endl;
return Date (m, d, y); return Date (m, d, y);
} }
@ -630,7 +609,6 @@ Date getNextRecurrence (Date& current, std::string& period)
while (! Date::valid (m, d, y)) while (! Date::valid (m, d, y))
--d; --d;
// std::cout << "# next " << current.toString () << " + " << period << " = " << m << "/" << d << "/" << y << std::endl;
return Date (m, d, y); return Date (m, d, y);
} }
@ -639,7 +617,6 @@ Date getNextRecurrence (Date& current, std::string& period)
{ {
y += 2; y += 2;
// std::cout << "# next " << current.toString () << " + " << period << " = " << m << "/" << d << "/" << y << std::endl;
return Date (m, d, y); return Date (m, d, y);
} }
@ -657,7 +634,6 @@ void updateRecurrenceMask (
T& task) T& task)
{ {
std::string parent = task.getAttribute ("parent"); std::string parent = task.getAttribute ("parent");
// std::cout << "# updateRecurrenceMask of " << parent << std::endl;
if (parent != "") if (parent != "")
{ {
std::vector <T>::iterator it; std::vector <T>::iterator it;
@ -665,11 +641,8 @@ void updateRecurrenceMask (
{ {
if (it->getUUID () == parent) if (it->getUUID () == parent)
{ {
// std::cout << "# located parent task" << std::endl;
unsigned int index = atoi (task.getAttribute ("imask").c_str ()); unsigned int index = atoi (task.getAttribute ("imask").c_str ());
// std::cout << "# child imask=" << index << std::endl;
std::string mask = it->getAttribute ("mask"); std::string mask = it->getAttribute ("mask");
// std::cout << "# parent mask=" << mask << std::endl;
if (mask.length () > index) if (mask.length () > index)
{ {
mask[index] = (task.getStatus () == T::pending) ? '-' mask[index] = (task.getStatus () == T::pending) ? '-'
@ -677,15 +650,11 @@ void updateRecurrenceMask (
: (task.getStatus () == T::deleted) ? 'X' : (task.getStatus () == T::deleted) ? 'X'
: '?'; : '?';
// std::cout << "# setting parent mask to=" << mask << std::endl;
it->setAttribute ("mask", mask); it->setAttribute ("mask", mask);
// std::cout << "# tdb.modifyT (parent)" << std::endl;
tdb.modifyT (*it); tdb.modifyT (*it);
} }
else else
{ {
// std::cout << "# mask of insufficient length" << std::endl;
// std::cout << "# should never occur" << std::endl;
std::string mask; std::string mask;
for (unsigned int i = 0; i < index; ++i) for (unsigned int i = 0; i < index; ++i)
mask += "?"; 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 <std::string> 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 <std::string> args;
for (int i = 1; i < argc; ++i)
args.push_back (argv[i]);
return runTaskCommand (args, tdb, conf);
}
////////////////////////////////////////////////////////////////////////////////
std::string runTaskCommand (
std::vector <std::string>& 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;
}
////////////////////////////////////////////////////////////////////////////////

View file

@ -66,38 +66,42 @@ void handleRecurrence (TDB&, std::vector <T>&);
bool generateDueDates (T&, std::vector <Date>&); bool generateDueDates (T&, std::vector <Date>&);
Date getNextRecurrence (Date&, std::string&); Date getNextRecurrence (Date&, std::string&);
void updateRecurrenceMask (TDB&, std::vector <T>&, T&); void updateRecurrenceMask (TDB&, std::vector <T>&, T&);
void onChangeCallback ();
std::string runTaskCommand (int, char**, TDB&, Config&);
std::string runTaskCommand (std::vector <std::string>&, TDB&, Config&);
// command.cpp // command.cpp
void handleAdd (const TDB&, T&, Config&); void handleAdd (TDB&, T&, Config&);
void handleProjects (TDB&, T&, Config&);
void handleTags (TDB&, T&, Config&);
void handleUndelete (TDB&, T&, Config&);
void handleVersion (Config&);
void handleExport (TDB&, T&, Config&); void handleExport (TDB&, T&, Config&);
void handleDelete (TDB&, T&, Config&);
void handleStart (TDB&, T&, Config&);
void handleDone (TDB&, T&, Config&); void handleDone (TDB&, T&, Config&);
void handleUndo (TDB&, T&, Config&);
void handleModify (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 // report.cpp
void filter (std::vector<T>&, T&); void filter (std::vector<T>&, T&);
void handleList (TDB&, T&, Config&); std::string handleList (TDB&, T&, Config&);
void handleInfo (TDB&, T&, Config&); std::string handleInfo (TDB&, T&, Config&);
void handleLongList (TDB&, T&, Config&); std::string handleLongList (TDB&, T&, Config&);
void handleSmallList (TDB&, T&, Config&); std::string handleSmallList (TDB&, T&, Config&);
void handleCompleted (TDB&, T&, Config&); std::string handleCompleted (TDB&, T&, Config&);
void handleReportSummary (TDB&, T&, Config&); std::string handleReportSummary (TDB&, T&, Config&);
void handleReportNext (TDB&, T&, Config&); std::string handleReportNext (TDB&, T&, Config&);
void handleReportHistory (TDB&, T&, Config&); std::string handleReportHistory (TDB&, T&, Config&);
void handleReportGHistory (TDB&, T&, Config&); std::string handleReportGHistory (TDB&, T&, Config&);
void handleReportCalendar (TDB&, T&, Config&); std::string handleReportUsage (const TDB&, T&, Config&);
void handleReportActive (TDB&, T&, Config&); std::string handleReportCalendar (TDB&, T&, Config&);
void handleReportOverdue (TDB&, T&, Config&); std::string handleReportActive (TDB&, T&, Config&);
void handleReportStats (TDB&, T&, Config&); std::string handleReportOverdue (TDB&, T&, Config&);
void handleReportOldest (TDB&, T&, Config&); std::string handleReportStats (TDB&, T&, Config&);
void handleReportNewest (TDB&, T&, Config&); std::string handleReportOldest (TDB&, T&, Config&);
std::string handleReportNewest (TDB&, T&, Config&);
// util.cpp // util.cpp
bool confirm (const std::string&); bool confirm (const std::string&);