diff --git a/ChangeLog b/ChangeLog
index 7b2c3a6ff..b25b789ba 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -29,6 +29,8 @@
to help ensure higher quality releases.
+ Fixed bug that caused performance hit during table rendering.
+ Fixed bug that concatenated a modified description without spaces.
+ + Added new column 'recur' that displays the recurrence period of any
+ recurring tasks. This column can be added to any custom report.
------ old releases ------------------------------
diff --git a/html/custom.html b/html/custom.html
index 7a970e58e..8b202f5c1 100644
--- a/html/custom.html
+++ b/html/custom.html
@@ -88,6 +88,7 @@ report.mine.sort=priority-,project+
Added new column 'recur' that displays the recurrence period of any
+ recurring tasks. This column can be added to any custom report.
diff --git a/src/Table.cpp b/src/Table.cpp
index fc0ff1296..071837cdf 100644
--- a/src/Table.cpp
+++ b/src/Table.cpp
@@ -941,6 +941,24 @@ void Table::sort (std::vector & order)
((std::string)*left == "M" && (std::string)*right == "H"))
SWAP
break;
+
+ case ascendingPeriod:
+ if ((std::string)*left == "" && (std::string)*right != "")
+ break;
+ else if ((std::string)*left != "" && (std::string)*right == "")
+ SWAP
+ else if (convertDuration ((std::string)*left) > convertDuration ((std::string)*right))
+ SWAP
+ break;
+
+ case descendingPeriod:
+ if ((std::string)*left != "" && (std::string)*right == "")
+ break;
+ else if ((std::string)*left == "" && (std::string)*right != "")
+ SWAP
+ else if (convertDuration ((std::string)*left) < convertDuration ((std::string)*right))
+ SWAP
+ break;
}
}
}
diff --git a/src/Table.h b/src/Table.h
index 4048dae70..fe098e896 100644
--- a/src/Table.h
+++ b/src/Table.h
@@ -37,9 +37,16 @@ class Table
{
public:
enum just {left, center, right};
- enum order {ascendingNumeric, ascendingCharacter, ascendingPriority,
- ascendingDate, descendingNumeric, descendingCharacter,
- descendingPriority, descendingDate};
+ enum order {ascendingNumeric,
+ ascendingCharacter,
+ ascendingPriority,
+ ascendingDate,
+ ascendingPeriod,
+ descendingNumeric,
+ descendingCharacter,
+ descendingPriority,
+ descendingDate,
+ descendingPeriod};
enum sizing {minimum = -1, flexible = 0};
Table ();
diff --git a/src/report.cpp b/src/report.cpp
index afa460821..6f25c90a4 100644
--- a/src/report.cpp
+++ b/src/report.cpp
@@ -2447,6 +2447,16 @@ std::string handleCustomReport (
table.addCell (row, columnCount, tasks[row].getDescription ());
}
+ else if (*col == "recur")
+ {
+ table.addColumn ("Recur");
+ table.setColumnWidth (columnCount, Table::minimum);
+ table.setColumnJustification (columnCount, Table::right);
+
+ for (unsigned int row = 0; row < tasks.size (); ++row)
+ table.addCell (row, columnCount, tasks[row].getAttribute ("recur"));
+ }
+
// Common to all columns.
// Add underline.
if (conf.get (std::string ("color"), true) || conf.get (std::string ("_forcecolor"), false))
@@ -2487,6 +2497,12 @@ std::string handleCustomReport (
Table::ascendingDate :
Table::descendingDate));
+ else if (column == "recur")
+ table.sortOn (columnIndex[column],
+ (direction == '+' ?
+ Table::ascendingPeriod :
+ Table::descendingPeriod));
+
else
table.sortOn (columnIndex[column],
(direction == '+' ?
@@ -2567,6 +2583,7 @@ void validReportColumns (const std::vector & columns)
*it != "age" &&
*it != "active" &&
*it != "tags" &&
+ *it != "recur" &&
*it != "description")
bad.push_back (*it);
diff --git a/src/task.h b/src/task.h
index 03a5d5f7d..925b75811 100644
--- a/src/task.h
+++ b/src/task.h
@@ -126,7 +126,7 @@ void formatTimeDeltaDays (std::string&, time_t);
std::string formatSeconds (time_t);
const std::string uuid ();
const char* optionalBlankLine (Config&);
-int convertDuration (std::string&);
+int convertDuration (const std::string&);
std::string expandPath (const std::string&);
#ifdef SOLARIS
diff --git a/src/tests/recur.t b/src/tests/recur.t
new file mode 100755
index 000000000..96a251bd2
--- /dev/null
+++ b/src/tests/recur.t
@@ -0,0 +1,67 @@
+#! /usr/bin/perl
+################################################################################
+## task - a command line task list manager.
+##
+## Copyright 2006 - 2009, Paul Beckingham.
+## All rights reserved.
+##
+## This program is free software; you can redistribute it and/or modify it under
+## the terms of the GNU General Public License as published by the Free Software
+## Foundation; either version 2 of the License, or (at your option) any later
+## version.
+##
+## This program is distributed in the hope that it will be useful, but WITHOUT
+## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+## FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+## details.
+##
+## You should have received a copy of the GNU General Public License along with
+## this program; if not, write to the
+##
+## Free Software Foundation, Inc.,
+## 51 Franklin Street, Fifth Floor,
+## Boston, MA
+## 02110-1301
+## USA
+##
+################################################################################
+
+use strict;
+use warnings;
+use Test::More tests => 6;
+
+# Create the rc file.
+if (open my $fh, '>', 'recur.rc')
+{
+ print $fh "data.location=.\n",
+ "report.asc.columns=id,recur,description\n",
+ "report.asc.sort=recur+\n",
+ "report.desc.columns=id,recur,description\n",
+ "report.desc.sort=recur-\n";
+ close $fh;
+ ok (-r 'recur.rc', 'Created recur.rc');
+}
+
+# Create a few recurring tasks, and test the sort order of the recur column.
+qx{../task rc:recur.rc add due:tomorrow recur:daily first};
+qx{../task rc:recur.rc add due:tomorrow recur:weekly second};
+qx{../task rc:recur.rc add due:tomorrow recur:3d third};
+
+my $output = qx{../task rc:recur.rc asc};
+like ($output, qr/first .* third .* second/msx, 'daily 3d weekly');
+
+$output = qx{../task rc:recur.rc desc};
+like ($output, qr/second .* third .* first/msx, 'weekly 3d daily');
+
+# Cleanup.
+unlink 'shadow.txt';
+ok (!-r 'shadow.txt', 'Removed shadow.txt');
+
+unlink 'pending.data';
+ok (!-r 'pending.data', 'Removed pending.data');
+
+unlink 'recur.rc';
+ok (!-r 'recur.rc', 'Removed recur.rc');
+
+exit 0;
+
diff --git a/src/util.cpp b/src/util.cpp
index bc1bea9f2..7f1e6b911 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -241,9 +241,9 @@ const std::string uuid ()
////////////////////////////////////////////////////////////////////////////////
// Recognize the following constructs, and return the number of days represented
-int convertDuration (std::string& input)
+int convertDuration (const std::string& input)
{
- input = lowerCase (input);
+ std::string lower_input = lowerCase (input);
Date today;
std::vector supported;
@@ -263,7 +263,7 @@ int convertDuration (std::string& input)
supported.push_back ("yearly");
std::vector matches;
- if (autoComplete (input, supported, matches) == 1)
+ if (autoComplete (lower_input, supported, matches) == 1)
{
std::string found = matches[0];
@@ -279,19 +279,18 @@ int convertDuration (std::string& input)
}
// Support \d+ d|w|m|q|y
-
else
{
// Verify all digits followed by d, w, m, q, or y.
- unsigned int length = input.length ();
+ unsigned int length = lower_input.length ();
for (unsigned int i = 0; i < length; ++i)
{
- if (! isdigit (input[i]) &&
+ if (! isdigit (lower_input[i]) &&
i == length - 1)
{
- int number = ::atoi (input.substr (0, i).c_str ());
+ int number = ::atoi (lower_input.substr (0, i).c_str ());
- switch (input[length - 1])
+ switch (lower_input[length - 1])
{
case 'd': return number * 1; break;
case 'w': return number * 7; break;