Enhancement - Hooks

- Implemented lots of command hooks.
This commit is contained in:
Paul Beckingham 2010-01-30 23:39:51 -05:00
parent 78c7408380
commit dd423d315b
6 changed files with 2818 additions and 2651 deletions

View file

@ -248,7 +248,9 @@ int Context::dispatch (std::string &out)
else if (cmd.validCustom (cmd.command)) { if (!inShadow) tdb.gc (); rc = handleCustomReport (cmd.command, out); } else if (cmd.validCustom (cmd.command)) { if (!inShadow) tdb.gc (); rc = handleCustomReport (cmd.command, out); }
// If the command is not recognized, display usage. // If the command is not recognized, display usage.
else { rc = shortUsage (out); } else { hooks.trigger ("pre-usage-command");
rc = shortUsage (out);
hooks.trigger ("post-usage-command"); }
// Only update the shadow file if such an update was not suppressed (shadow), // Only update the shadow file if such an update was not suppressed (shadow),
if (cmd.isWriteCommand () && !inShadow) if (cmd.isWriteCommand () && !inShadow)

File diff suppressed because it is too large Load diff

View file

@ -111,522 +111,530 @@ int runCustomReport (
std::string &outs) std::string &outs)
{ {
int rc = 0; int rc = 0;
// Load report configuration.
std::vector <std::string> columns;
split (columns, columnList, ',');
validReportColumns (columns);
std::vector <std::string> labels; if (context.hooks.trigger ("pre-custom-report-command") &&
split (labels, labelList, ','); context.hooks.trigger (std::string ("pre-") + report + "-command"))
if (columns.size () != labels.size () && labels.size () != 0)
throw std::string ("There are a different number of columns than labels ") +
"for report '" + report + "'.";
std::map <std::string, std::string> columnLabels;
if (labels.size ())
for (unsigned int i = 0; i < columns.size (); ++i)
columnLabels[columns[i]] = labels[i];
std::vector <std::string> sortOrder;
split (sortOrder, sortList, ',');
validSortColumns (columns, sortOrder);
std::vector <std::string> filterArgs;
split (filterArgs, filterList, ' ');
{ {
Cmd cmd (report); // Load report configuration.
Task task; std::vector <std::string> columns;
Sequence sequence; split (columns, columnList, ',');
Subst subst; validReportColumns (columns);
Filter filter;
context.parse (filterArgs, cmd, task, sequence, subst, filter);
context.sequence.combine (sequence); std::vector <std::string> labels;
split (labels, labelList, ',');
// Allow limit to be overridden by the command line. if (columns.size () != labels.size () && labels.size () != 0)
if (!context.task.has ("limit") && task.has ("limit")) throw std::string ("There are a different number of columns than labels ") +
context.task.set ("limit", task.get ("limit")); "for report '" + report + "'.";
foreach (att, filter) std::map <std::string, std::string> columnLabels;
context.filter.push_back (*att); if (labels.size ())
} for (unsigned int i = 0; i < columns.size (); ++i)
columnLabels[columns[i]] = labels[i];
// Filter sequence. std::vector <std::string> sortOrder;
if (context.sequence.size ()) split (sortOrder, sortList, ',');
context.filter.applySequence (tasks, context.sequence); validSortColumns (columns, sortOrder);
// Initialize colorization for subsequent auto colorization. std::vector <std::string> filterArgs;
initializeColorRules (); split (filterArgs, filterList, ' ');
Table table;
table.setTableWidth (context.getWidth ());
table.setDateFormat (context.config.get ("dateformat"));
table.setReportName (report);
foreach (task, tasks)
table.addRow ();
int columnCount = 0;
int dueColumn = -1;
foreach (col, columns)
{
// Add each column individually.
if (*col == "id")
{ {
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "ID"); Cmd cmd (report);
table.setColumnWidth (columnCount, Table::minimum); Task task;
table.setColumnJustification (columnCount, Table::right); Sequence sequence;
Subst subst;
Filter filter;
context.parse (filterArgs, cmd, task, sequence, subst, filter);
int row = 0; context.sequence.combine (sequence);
foreach (task, tasks)
if (task->id != 0) // Allow limit to be overridden by the command line.
table.addCell (row++, columnCount, task->id); if (!context.task.has ("limit") && task.has ("limit"))
else context.task.set ("limit", task.get ("limit"));
table.addCell (row++, columnCount, "-");
foreach (att, filter)
context.filter.push_back (*att);
} }
else if (*col == "uuid") // Filter sequence.
if (context.sequence.size ())
context.filter.applySequence (tasks, context.sequence);
// Initialize colorization for subsequent auto colorization.
initializeColorRules ();
Table table;
table.setTableWidth (context.getWidth ());
table.setDateFormat (context.config.get ("dateformat"));
table.setReportName (report);
foreach (task, tasks)
table.addRow ();
int columnCount = 0;
int dueColumn = -1;
foreach (col, columns)
{ {
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "UUID"); // Add each column individually.
table.setColumnWidth (columnCount, Table::minimum); if (*col == "id")
table.setColumnJustification (columnCount, Table::left);
int row = 0;
foreach (task, tasks)
table.addCell (row++, columnCount, task->get ("uuid"));
}
else if (*col == "project")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Project");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::left);
int row = 0;
foreach (task, tasks)
table.addCell (row++, columnCount, task->get ("project"));
}
else if (*col == "priority")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Pri");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::left);
int row = 0;
foreach (task, tasks)
table.addCell (row++, columnCount, task->get ("priority"));
}
else if (*col == "priority_long")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Pri");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::left);
int row = 0;
std::string pri;
foreach (task, tasks)
{ {
pri = task->get ("priority"); table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "ID");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
if (pri == "H") pri = "High"; // TODO i18n int row = 0;
else if (pri == "M") pri = "Medium"; // TODO i18n foreach (task, tasks)
else if (pri == "L") pri = "Low"; // TODO i18n if (task->id != 0)
table.addCell (row++, columnCount, task->id);
table.addCell (row++, columnCount, pri); else
table.addCell (row++, columnCount, "-");
} }
}
else if (*col == "entry") else if (*col == "uuid")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Added");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
std::string entered;
for (unsigned int row = 0; row < tasks.size(); ++row)
{ {
entered = tasks[row].get ("entry"); table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "UUID");
if (entered.length ()) table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::left);
int row = 0;
foreach (task, tasks)
table.addCell (row++, columnCount, task->get ("uuid"));
}
else if (*col == "project")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Project");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::left);
int row = 0;
foreach (task, tasks)
table.addCell (row++, columnCount, task->get ("project"));
}
else if (*col == "priority")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Pri");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::left);
int row = 0;
foreach (task, tasks)
table.addCell (row++, columnCount, task->get ("priority"));
}
else if (*col == "priority_long")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Pri");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::left);
int row = 0;
std::string pri;
foreach (task, tasks)
{ {
Date dt (::atoi (entered.c_str ())); pri = task->get ("priority");
entered = dt.toString (context.config.get ("dateformat"));
table.addCell (row, columnCount, entered); if (pri == "H") pri = "High"; // TODO i18n
else if (pri == "M") pri = "Medium"; // TODO i18n
else if (pri == "L") pri = "Low"; // TODO i18n
table.addCell (row++, columnCount, pri);
} }
} }
}
else if (*col == "entry_time") else if (*col == "entry")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Added");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
std::string entered;
for (unsigned int row = 0; row < tasks.size(); ++row)
{ {
entered = tasks[row].get ("entry"); table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Added");
if (entered.length ()) table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
std::string entered;
for (unsigned int row = 0; row < tasks.size(); ++row)
{ {
Date dt (::atoi (entered.c_str ())); entered = tasks[row].get ("entry");
entered = dt.toStringWithTime (context.config.get ("dateformat")); if (entered.length ())
table.addCell (row, columnCount, entered); {
Date dt (::atoi (entered.c_str ()));
entered = dt.toString (context.config.get ("dateformat"));
table.addCell (row, columnCount, entered);
}
} }
} }
}
else if (*col == "start") else if (*col == "entry_time")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Started");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
std::string started;
for (unsigned int row = 0; row < tasks.size(); ++row)
{ {
started = tasks[row].get ("start"); table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Added");
if (started.length ()) table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
std::string entered;
for (unsigned int row = 0; row < tasks.size(); ++row)
{ {
Date dt (::atoi (started.c_str ())); entered = tasks[row].get ("entry");
started = dt.toString (context.config.get ("dateformat")); if (entered.length ())
table.addCell (row, columnCount, started); {
Date dt (::atoi (entered.c_str ()));
entered = dt.toStringWithTime (context.config.get ("dateformat"));
table.addCell (row, columnCount, entered);
}
} }
} }
}
else if (*col == "start_time") else if (*col == "start")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Started");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
std::string started;
for (unsigned int row = 0; row < tasks.size(); ++row)
{ {
started = tasks[row].get ("start"); table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Started");
if (started.length ()) table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
std::string started;
for (unsigned int row = 0; row < tasks.size(); ++row)
{ {
Date dt (::atoi (started.c_str ())); started = tasks[row].get ("start");
started = dt.toStringWithTime (context.config.get ("dateformat")); if (started.length ())
table.addCell (row, columnCount, started); {
Date dt (::atoi (started.c_str ()));
started = dt.toString (context.config.get ("dateformat"));
table.addCell (row, columnCount, started);
}
} }
} }
}
else if (*col == "end") else if (*col == "start_time")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Completed");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
std::string started;
for (unsigned int row = 0; row < tasks.size(); ++row)
{ {
started = tasks[row].get ("end"); table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Started");
if (started.length ()) table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
std::string started;
for (unsigned int row = 0; row < tasks.size(); ++row)
{ {
Date dt (::atoi (started.c_str ())); started = tasks[row].get ("start");
started = dt.toString (context.config.get ("dateformat")); if (started.length ())
table.addCell (row, columnCount, started); {
Date dt (::atoi (started.c_str ()));
started = dt.toStringWithTime (context.config.get ("dateformat"));
table.addCell (row, columnCount, started);
}
} }
} }
}
else if (*col == "end_time") else if (*col == "end")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Completed");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
std::string format = context.config.get ("dateformat");
std::string started;
for (unsigned int row = 0; row < tasks.size(); ++row)
{ {
started = tasks[row].get ("end"); table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Completed");
if (started.length ()) table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
std::string started;
for (unsigned int row = 0; row < tasks.size(); ++row)
{ {
Date dt (::atoi (started.c_str ())); started = tasks[row].get ("end");
started = dt.toStringWithTime (format); if (started.length ())
table.addCell (row, columnCount, started); {
Date dt (::atoi (started.c_str ()));
started = dt.toString (context.config.get ("dateformat"));
table.addCell (row, columnCount, started);
}
} }
} }
}
else if (*col == "due") else if (*col == "end_time")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Due");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::left);
std::string format = context.config.get ("report." + report + ".dateformat");
if (format == "")
format = context.config.get ("dateformat.report");
if (format == "")
format = context.config.get ("dateformat");
int row = 0;
std::string due;
foreach (task, tasks)
table.addCell (row++, columnCount, getDueDate (*task, format));
dueColumn = columnCount;
}
else if (*col == "age")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Age");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
std::string created;
std::string age;
Date now;
for (unsigned int row = 0; row < tasks.size(); ++row)
{ {
created = tasks[row].get ("entry"); table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Completed");
if (created.length ()) table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
std::string format = context.config.get ("dateformat");
std::string started;
for (unsigned int row = 0; row < tasks.size(); ++row)
{ {
Date dt (::atoi (created.c_str ())); started = tasks[row].get ("end");
age = formatSeconds ((time_t) (now - dt)); if (started.length ())
table.addCell (row, columnCount, age); {
Date dt (::atoi (started.c_str ()));
started = dt.toStringWithTime (format);
table.addCell (row, columnCount, started);
}
} }
} }
}
else if (*col == "age_compact") else if (*col == "due")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Age");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
std::string created;
std::string age;
Date now;
for (unsigned int row = 0; row < tasks.size(); ++row)
{ {
created = tasks[row].get ("entry"); table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Due");
if (created.length ()) table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::left);
std::string format = context.config.get ("report." + report + ".dateformat");
if (format == "")
format = context.config.get ("dateformat.report");
if (format == "")
format = context.config.get ("dateformat");
int row = 0;
std::string due;
foreach (task, tasks)
table.addCell (row++, columnCount, getDueDate (*task, format));
dueColumn = columnCount;
}
else if (*col == "age")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Age");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
std::string created;
std::string age;
Date now;
for (unsigned int row = 0; row < tasks.size(); ++row)
{ {
Date dt (::atoi (created.c_str ())); created = tasks[row].get ("entry");
age = formatSecondsCompact ((time_t) (now - dt)); if (created.length ())
table.addCell (row, columnCount, age); {
Date dt (::atoi (created.c_str ()));
age = formatSeconds ((time_t) (now - dt));
table.addCell (row, columnCount, age);
}
} }
} }
}
else if (*col == "active") else if (*col == "age_compact")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Active");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::left);
for (unsigned int row = 0; row < tasks.size(); ++row)
if (tasks[row].has ("start"))
table.addCell (row, columnCount, "*");
}
else if (*col == "tags")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Tags");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::left);
int row = 0;
std::vector <std::string> all;
std::string tags;
foreach (task, tasks)
{ {
task->getTags (all); table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Age");
join (tags, " ", all); table.setColumnWidth (columnCount, Table::minimum);
table.addCell (row++, columnCount, tags); table.setColumnJustification (columnCount, Table::right);
}
}
else if (*col == "description_only") std::string created;
{ std::string age;
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Description"); Date now;
table.setColumnWidth (columnCount, Table::flexible); for (unsigned int row = 0; row < tasks.size(); ++row)
table.setColumnJustification (columnCount, Table::left);
int row = 0;
foreach (task, tasks)
table.addCell (row++, columnCount, task->get ("description"));
}
else if (*col == "description")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Description");
table.setColumnWidth (columnCount, Table::flexible);
table.setColumnJustification (columnCount, Table::left);
int row = 0;
foreach (task, tasks)
table.addCell (row++, columnCount, getFullDescription (*task, report));
}
else if (*col == "recur")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Recur");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
for (unsigned int row = 0; row < tasks.size(); ++row)
{
std::string recur = tasks[row].get ("recur");
if (recur != "")
table.addCell (row, columnCount, recur);
}
}
else if (*col == "recurrence_indicator")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "R");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
for (unsigned int row = 0; row < tasks.size(); ++row)
if (tasks[row].has ("recur"))
table.addCell (row, columnCount, "R");
}
else if (*col == "tag_indicator")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "T");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
for (unsigned int row = 0; row < tasks.size(); ++row)
if (tasks[row].getTagCount ())
table.addCell (row, columnCount, "+");
}
else if (*col == "wait")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Wait");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
int row = 0;
std::string wait;
foreach (task, tasks)
{
wait = task->get ("wait");
if (wait != "")
{ {
Date dt (::atoi (wait.c_str ())); created = tasks[row].get ("entry");
wait = dt.toString (context.config.get ("dateformat")); if (created.length ())
table.addCell (row++, columnCount, wait); {
Date dt (::atoi (created.c_str ()));
age = formatSecondsCompact ((time_t) (now - dt));
table.addCell (row, columnCount, age);
}
} }
} }
else if (*col == "active")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Active");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::left);
for (unsigned int row = 0; row < tasks.size(); ++row)
if (tasks[row].has ("start"))
table.addCell (row, columnCount, "*");
}
else if (*col == "tags")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Tags");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::left);
int row = 0;
std::vector <std::string> all;
std::string tags;
foreach (task, tasks)
{
task->getTags (all);
join (tags, " ", all);
table.addCell (row++, columnCount, tags);
}
}
else if (*col == "description_only")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Description");
table.setColumnWidth (columnCount, Table::flexible);
table.setColumnJustification (columnCount, Table::left);
int row = 0;
foreach (task, tasks)
table.addCell (row++, columnCount, task->get ("description"));
}
else if (*col == "description")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Description");
table.setColumnWidth (columnCount, Table::flexible);
table.setColumnJustification (columnCount, Table::left);
int row = 0;
foreach (task, tasks)
table.addCell (row++, columnCount, getFullDescription (*task, report));
}
else if (*col == "recur")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Recur");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
for (unsigned int row = 0; row < tasks.size(); ++row)
{
std::string recur = tasks[row].get ("recur");
if (recur != "")
table.addCell (row, columnCount, recur);
}
}
else if (*col == "recurrence_indicator")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "R");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
for (unsigned int row = 0; row < tasks.size(); ++row)
if (tasks[row].has ("recur"))
table.addCell (row, columnCount, "R");
}
else if (*col == "tag_indicator")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "T");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
for (unsigned int row = 0; row < tasks.size(); ++row)
if (tasks[row].getTagCount ())
table.addCell (row, columnCount, "+");
}
else if (*col == "wait")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Wait");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
int row = 0;
std::string wait;
foreach (task, tasks)
{
wait = task->get ("wait");
if (wait != "")
{
Date dt (::atoi (wait.c_str ()));
wait = dt.toString (context.config.get ("dateformat"));
table.addCell (row++, columnCount, wait);
}
}
}
// Common to all columns.
// Add underline.
if ((context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) &&
context.config.getBoolean ("fontunderline"))
table.setColumnUnderline (columnCount);
else
table.setTableDashedUnderline ();
++columnCount;
} }
// Common to all columns. // Dynamically add sort criteria.
// Add underline. // Build a map of column names -> index.
if ((context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) && std::map <std::string, unsigned int> columnIndex;
context.config.getBoolean ("fontunderline")) for (unsigned int c = 0; c < columns.size (); ++c)
table.setColumnUnderline (columnCount); columnIndex[columns[c]] = c;
else
table.setTableDashedUnderline ();
++columnCount; foreach (sortColumn, sortOrder)
}
// Dynamically add sort criteria.
// Build a map of column names -> index.
std::map <std::string, unsigned int> columnIndex;
for (unsigned int c = 0; c < columns.size (); ++c)
columnIndex[columns[c]] = c;
foreach (sortColumn, sortOrder)
{
// Separate column and direction.
std::string column = sortColumn->substr (0, sortColumn->length () - 1);
char direction = (*sortColumn)[sortColumn->length () - 1];
// TODO This code should really be using Att::type.
if (column == "id")
table.sortOn (columnIndex[column],
(direction == '+' ?
Table::ascendingNumeric :
Table::descendingNumeric));
else if (column == "priority")
table.sortOn (columnIndex[column],
(direction == '+' ?
Table::ascendingPriority :
Table::descendingPriority));
else if (column == "entry" || column == "start" || column == "wait" ||
column == "until" || column == "end")
table.sortOn (columnIndex[column],
(direction == '+' ?
Table::ascendingDate :
Table::descendingDate));
else if (column == "due")
table.sortOn (columnIndex[column],
(direction == '+' ?
Table::ascendingDueDate :
Table::descendingDueDate));
else if (column == "recur")
table.sortOn (columnIndex[column],
(direction == '+' ?
Table::ascendingPeriod :
Table::descendingPeriod));
else
table.sortOn (columnIndex[column],
(direction == '+' ?
Table::ascendingCharacter :
Table::descendingCharacter));
}
// Now auto colorize all rows.
if (context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor"))
{
for (unsigned int row = 0; row < tasks.size (); ++row)
{ {
Color c (tasks[row].get ("fg") + " " + tasks[row].get ("bg")); // Separate column and direction.
autoColorize (tasks[row], c); std::string column = sortColumn->substr (0, sortColumn->length () - 1);
table.setRowColor (row, c); char direction = (*sortColumn)[sortColumn->length () - 1];
// TODO This code should really be using Att::type.
if (column == "id")
table.sortOn (columnIndex[column],
(direction == '+' ?
Table::ascendingNumeric :
Table::descendingNumeric));
else if (column == "priority")
table.sortOn (columnIndex[column],
(direction == '+' ?
Table::ascendingPriority :
Table::descendingPriority));
else if (column == "entry" || column == "start" || column == "wait" ||
column == "until" || column == "end")
table.sortOn (columnIndex[column],
(direction == '+' ?
Table::ascendingDate :
Table::descendingDate));
else if (column == "due")
table.sortOn (columnIndex[column],
(direction == '+' ?
Table::ascendingDueDate :
Table::descendingDueDate));
else if (column == "recur")
table.sortOn (columnIndex[column],
(direction == '+' ?
Table::ascendingPeriod :
Table::descendingPeriod));
else
table.sortOn (columnIndex[column],
(direction == '+' ?
Table::ascendingCharacter :
Table::descendingCharacter));
} }
}
// If an alternating row color is specified, notify the table. // Now auto colorize all rows.
if (context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) if (context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor"))
{ {
Color alternate (context.config.get ("color.alternate")); for (unsigned int row = 0; row < tasks.size (); ++row)
if (alternate.nontrivial ()) {
table.setTableAlternateColor (alternate); Color c (tasks[row].get ("fg") + " " + tasks[row].get ("bg"));
} autoColorize (tasks[row], c);
table.setRowColor (row, c);
}
}
// Limit the number of rows according to the report definition. // If an alternating row color is specified, notify the table.
int maximum = context.config.getInteger (std::string ("report.") + report + ".limit"); if (context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor"))
{
Color alternate (context.config.get ("color.alternate"));
if (alternate.nontrivial ())
table.setTableAlternateColor (alternate);
}
// If the custom report has a defined limit, then allow a numeric override. // Limit the number of rows according to the report definition.
// This is an integer specified as a filter (limit:10). int maximum = context.config.getInteger (std::string ("report.") + report + ".limit");
if (context.task.has ("limit"))
maximum = atoi (context.task.get ("limit").c_str ());
std::stringstream out; // If the custom report has a defined limit, then allow a numeric override.
if (table.rowCount ()) // This is an integer specified as a filter (limit:10).
out << optionalBlankLine () if (context.task.has ("limit"))
<< table.render (maximum) maximum = atoi (context.task.get ("limit").c_str ());
<< optionalBlankLine ()
<< table.rowCount () std::stringstream out;
<< (table.rowCount () == 1 ? " task" : " tasks") if (table.rowCount ())
out << optionalBlankLine ()
<< table.render (maximum)
<< optionalBlankLine ()
<< table.rowCount ()
<< (table.rowCount () == 1 ? " task" : " tasks")
<< std::endl;
else {
out << "No matches."
<< std::endl; << std::endl;
else { rc = 1;
out << "No matches." }
<< std::endl;
rc = 1; outs = out.str ();
context.hooks.trigger (std::string ("post-") + report + "-command");
context.hooks.trigger ("post-custom-report-command");
} }
outs = out.str ();
return rc; return rc;
} }

View file

@ -603,50 +603,57 @@ ARE_THESE_REALLY_HARMFUL:
// wrench. To be used sparingly. // wrench. To be used sparingly.
int handleEdit (std::string &outs) int handleEdit (std::string &outs)
{ {
std::stringstream out; int rc = 0;
std::vector <Task> tasks; if (context.hooks.trigger ("pre-edit-command"))
context.tdb.lock (context.config.getBoolean ("locking"));
handleRecurrence ();
Filter filter;
context.tdb.loadPending (tasks, filter);
// Filter sequence.
std::vector <Task> all = tasks;
context.filter.applySequence (tasks, context.sequence);
foreach (task, tasks)
{ {
editFile (*task); std::stringstream out;
context.tdb.update (*task);
/* std::vector <Task> tasks;
foreach (other, all) context.tdb.lock (context.config.getBoolean ("locking"));
handleRecurrence ();
Filter filter;
context.tdb.loadPending (tasks, filter);
// Filter sequence.
std::vector <Task> all = tasks;
context.filter.applySequence (tasks, context.sequence);
foreach (task, tasks)
{ {
if (other->id != task.id) // Don't edit the same task again. editFile (*task);
context.tdb.update (*task);
/*
foreach (other, all)
{ {
if (task.has ("parent") && if (other->id != task.id) // Don't edit the same task again.
if (other is parent of task)
{ {
// Transfer everything but mask, imask, uuid, parent. if (task.has ("parent") &&
} if (other is parent of task)
else if (task is parent of other) {
{ // Transfer everything but mask, imask, uuid, parent.
// Transfer everything but mask, imask, uuid, parent. }
} else if (task is parent of other)
else if (task and other are siblings) {
{ // Transfer everything but mask, imask, uuid, parent.
// Transfer everything but mask, imask, uuid, parent. }
else if (task and other are siblings)
{
// Transfer everything but mask, imask, uuid, parent.
}
} }
} }
}
*/ */
}
context.tdb.commit ();
context.tdb.unlock ();
outs = out.str ();
context.hooks.trigger ("post-edit-command");
} }
context.tdb.commit (); return rc;
context.tdb.unlock ();
outs = out.str ();
return 0;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View file

@ -1148,74 +1148,81 @@ static std::string importCSV (const std::vector <std::string>& lines)
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
int handleImport (std::string &outs) int handleImport (std::string &outs)
{ {
std::stringstream out; int rc = 0;
// Use the description as a file name. if (context.hooks.trigger ("pre-import-command"))
std::string file = trim (context.task.get ("description"));
if (file.length () > 0)
{ {
// Load the file. std::stringstream out;
std::vector <std::string> all;
File::read (file, all);
std::vector <std::string> lines; // Use the description as a file name.
std::vector <std::string>::iterator it; std::string file = trim (context.task.get ("description"));
for (it = all.begin (); it != all.end (); ++it) if (file.length () > 0)
{ {
std::string line = *it; // Load the file.
std::vector <std::string> all;
File::read (file, all);
// Strip comments std::vector <std::string> lines;
std::string::size_type pound = line.find ("#"); std::vector <std::string>::iterator it;
if (pound != std::string::npos) for (it = all.begin (); it != all.end (); ++it)
line = line.substr (0, pound); {
std::string line = *it;
trim (line); // Strip comments
std::string::size_type pound = line.find ("#");
if (pound != std::string::npos)
line = line.substr (0, pound);
// Skip blank lines trim (line);
if (line.length () > 0)
lines.push_back (line); // Skip blank lines
if (line.length () > 0)
lines.push_back (line);
}
// Take a guess at the file type.
fileType type = determineFileType (lines);
std::string identifier;
switch (type)
{
case task_1_4_3: identifier = "This looks like an older task export file."; break;
case task_1_5_0: identifier = "This looks like a recent task export file."; break;
case task_1_6_0: identifier = "This looks like a current task export file."; break;
case task_cmd_line: identifier = "This looks like task command line arguments."; break;
case todo_sh_2_0: identifier = "This looks like a todo.sh 2.x file."; break;
case csv: identifier = "This looks like a CSV file, but not a task export file."; break;
case text: identifier = "This looks like a text file with one task per line."; break;
case not_a_clue:
throw std::string ("Task cannot determine which type of file this is, "
"and cannot proceed.");
}
// For tty users, confirm the import, as it is destructive.
if (isatty (fileno (stdout)))
if (! confirm (identifier + " Okay to proceed?"))
throw std::string ("Task will not import any data.");
// Determine which type it might be, then attempt an import.
switch (type)
{
case task_1_4_3: out << importTask_1_4_3 (lines); break;
case task_1_5_0: out << importTask_1_5_0 (lines); break;
case task_1_6_0: out << importTask_1_6_0 (lines); break;
case task_cmd_line: out << importTaskCmdLine (lines); break;
case todo_sh_2_0: out << importTodoSh_2_0 (lines); break;
case csv: out << importCSV (lines); break;
case text: out << importText (lines); break;
case not_a_clue: /* to stop the compiler from complaining. */ break;
}
} }
else
throw std::string ("You must specify a file to import.");
// Take a guess at the file type. outs = out.str ();
fileType type = determineFileType (lines); context.hooks.trigger ("post-import-command");
std::string identifier;
switch (type)
{
case task_1_4_3: identifier = "This looks like an older task export file."; break;
case task_1_5_0: identifier = "This looks like a recent task export file."; break;
case task_1_6_0: identifier = "This looks like a current task export file."; break;
case task_cmd_line: identifier = "This looks like task command line arguments."; break;
case todo_sh_2_0: identifier = "This looks like a todo.sh 2.x file."; break;
case csv: identifier = "This looks like a CSV file, but not a task export file."; break;
case text: identifier = "This looks like a text file with one task per line."; break;
case not_a_clue:
throw std::string ("Task cannot determine which type of file this is, "
"and cannot proceed.");
}
// For tty users, confirm the import, as it is destructive.
if (isatty (fileno (stdout)))
if (! confirm (identifier + " Okay to proceed?"))
throw std::string ("Task will not import any data.");
// Determine which type it might be, then attempt an import.
switch (type)
{
case task_1_4_3: out << importTask_1_4_3 (lines); break;
case task_1_5_0: out << importTask_1_5_0 (lines); break;
case task_1_6_0: out << importTask_1_6_0 (lines); break;
case task_cmd_line: out << importTaskCmdLine (lines); break;
case todo_sh_2_0: out << importTodoSh_2_0 (lines); break;
case csv: out << importCSV (lines); break;
case text: out << importText (lines); break;
case not_a_clue: /* to stop the compiler from complaining. */ break;
}
} }
else
throw std::string ("You must specify a file to import.");
outs = out.str (); return rc;
return 0;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

File diff suppressed because it is too large Load diff