- Added support for shadow file, shadow file command

- Added support for TDB::onChange callback
This commit is contained in:
Paul Beckingham 2008-10-09 17:19:57 -04:00
parent e7304e86ce
commit a5ec1e4b27
7 changed files with 168 additions and 81 deletions

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.4.2, 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])

View file

@ -206,7 +206,9 @@ bool TDB::deleteT (const T& t)
sprintf (endTime, "%u", (unsigned int) time (NULL)); sprintf (endTime, "%u", (unsigned int) time (NULL));
it->setAttribute ("end", endTime); it->setAttribute ("end", endTime);
return overwritePending (all); bool status = overwritePending (all);
dbChanged ();
return status;
} }
return false; return false;
@ -230,14 +232,16 @@ bool TDB::completeT (const T& t)
sprintf (endTime, "%u", (unsigned int) time (NULL)); sprintf (endTime, "%u", (unsigned int) time (NULL));
it->setAttribute ("end", endTime); it->setAttribute ("end", endTime);
return overwritePending (all); bool status = overwritePending (all);
dbChanged ();
return status;
} }
return false; return false;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
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;
@ -256,9 +260,15 @@ 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); {
bool status = writePending (task);
dbChanged ();
return status;
}
return writeCompleted (task); bool status = writeCompleted (task);
dbChanged ();
return status;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -283,7 +293,9 @@ bool TDB::modifyT (const T& t)
pending.push_back (*it); pending.push_back (*it);
} }
return overwritePending (pending); bool status = overwritePending (pending);
dbChanged ();
return status;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -493,4 +505,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,25 +45,29 @@ 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;
bool logCommand (int, char**) const; bool logCommand (int, char**) 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&) const;
bool writeCompleted (const T&) const; bool writeCompleted (const T&) const;
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;
std::string mLogFile; std::string mLogFile;
int mId; int mId;
std::vector <void (*)()> mOnChange;
}; };
#endif #endif

View file

@ -47,7 +47,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));

View file

@ -659,9 +659,6 @@ void handleInfo (TDB& tdb, T& task, Config& conf)
if (table.rowCount ()) if (table.rowCount ())
std::cout << optionalBlankLine (conf) std::cout << optionalBlankLine (conf)
<< table.render () << table.render ()
<< optionalBlankLine (conf)
<< table.rowCount ()
<< (table.rowCount () == 1 ? " task" : " tasks")
<< std::endl; << std::endl;
else else
std::cout << "No matches." << std::endl; std::cout << "No matches." << std::endl;

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)
{ {
@ -284,6 +289,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.
@ -294,63 +300,17 @@ int main (int argc, char** argv)
} }
TDB tdb; TDB tdb;
gTdb = &tdb;
tdb.dataDirectory (expandPath (conf.get ("data.location"))); tdb.dataDirectory (expandPath (conf.get ("data.location")));
// Log commands, if desired. // Log commands, if desired.
if (conf.get ("command.logging") == "on") if (conf.get ("command.logging") == "on")
tdb.logCommand (argc, argv); tdb.logCommand (argc, argv);
// If argc == 1 and the default.command configuration variable is set, // Set up TDB callback.
// then use that, otherwise stick with argc/argv. tdb.onChange (&onChangeCallback);
std::vector <std::string> args;
std::string defaultCommand = conf.get ("default.command");
if (argc == 1 && defaultCommand != "")
{
// 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]);
}
std::string command; 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 == "usage") handleReportUsage (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)
@ -400,12 +360,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;
} }
@ -553,7 +519,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);
} }
@ -572,7 +537,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);
} }
@ -588,7 +552,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);
} }
@ -607,7 +570,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);
} }
@ -623,7 +585,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 +600,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);
} }
@ -648,7 +608,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);
} }
@ -666,7 +625,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;
@ -674,11 +632,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) ? '-'
@ -686,15 +641,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 += "?";
@ -712,3 +663,107 @@ void updateRecurrenceMask (
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Using gTdb and gConf, generate a report.
void onChangeCallback ()
{
if (gConf && gTdb)
{
gConf->set ("curses", "off");
gConf->set ("color", "off");
// Determine if shadow file is enabled.
std::string shadowFile = expandPath (gConf->get ("shadow.file"));
if (shadowFile != "")
{
std::string command = gConf->get ("shadow.command", "list");
int width = gConf->get ("shadow.width", 80);
// Run report.
try
{
std::vector <std::string> args;
split (args, command, ' ');
runTaskCommand (args, *gTdb, *gConf);
}
catch (std::string& error)
{
std::cout << error << std::endl;
}
catch (...)
{
std::cout << "Unknown error." << std::endl;
}
}
}
}
////////////////////////////////////////////////////////////////////////////////
void 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]);
runTaskCommand (args, tdb, conf);
}
////////////////////////////////////////////////////////////////////////////////
void 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);
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 == "usage") handleReportUsage (tdb, task, conf);
else if (command == "" && task.getId ()) handleModify (tdb, task, conf);
else if (command == "help") longUsage (conf);
else shortUsage (conf);
}
////////////////////////////////////////////////////////////////////////////////

View file

@ -66,9 +66,12 @@ 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 ();
void runTaskCommand (int, char**, TDB&, Config&);
void 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 handleProjects (TDB&, T&, Config&);
void handleTags (TDB&, T&, Config&); void handleTags (TDB&, T&, Config&);
void handleUndelete (TDB&, T&, Config&); void handleUndelete (TDB&, T&, Config&);