From 916b8641b3961a7c4b46af9e88614d9b49630dce Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Sun, 20 Jun 2010 17:32:11 -0400 Subject: [PATCH] Feature #415 - Added feature #415, which supports displaying just a single page of tasks, by specifying either 'limit:page' to a command, or 'report.xxx.limit:page' in a report specification (thanks to T. Charles Yun). - Modified the 'next' report to only display a page, by default. --- ChangeLog | 4 ++ doc/man/task.1 | 5 ++- src/Att.cpp | 4 +- src/Config.cpp | 2 +- src/Context.h | 2 +- src/Table.cpp | 30 +++++++++++--- src/Table.h | 2 +- src/command.cpp | 4 +- src/custom.cpp | 75 ++++++++++++++++++++++++++++++----- src/interactive.cpp | 33 ++++++++++++++++ src/main.h | 1 + src/report.cpp | 9 +++-- src/tests/limit.t | 96 +++++++++++++++++++++++++++++++++++++++++++++ 13 files changed, 239 insertions(+), 28 deletions(-) create mode 100755 src/tests/limit.t diff --git a/ChangeLog b/ChangeLog index d95aec23c..a741fc5e2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -20,7 +20,11 @@ list all used projects/tags, not just the ones used in current pending tasks. Controlled by the 'list.all.projects' and 'list.all.tags' configuration variables (thanks to Dirk Deimeke). + + Added feature #415, which supports displaying just a single page of tasks, + by specifying either 'limit:page' to a command, or 'report.xxx.limit:page' + in a report specification (thanks to T. Charles Yun). + Improvements to the man pages (thanks to T. Charles Yun). + + Modified the 'next' report to only display a page, by default. + Added feature #408, making it possible to delete annotations with the new denotate command and the provided description (thanks to Dirk Deimeke). + Fixed bug #406 so that task now includes command aliases in the _commands diff --git a/doc/man/task.1 b/doc/man/task.1 index 4f7d74db0..f2d4003ea 100644 --- a/doc/man/task.1 +++ b/doc/man/task.1 @@ -302,7 +302,10 @@ Specifies background color. .TP .B limit: -Specifies the desired number of rows a report should have. +Specifies the desired number of tasks a report should show, if a positive +integer is given. The value 'page' may also be used, and will limit the +report output to as many lines of text as will fit on screen. This defaults +to 25 lines, if ncurses is not installed or enabled. .TP .B wait: diff --git a/src/Att.cpp b/src/Att.cpp index b68c55d93..4857ceffa 100644 --- a/src/Att.cpp +++ b/src/Att.cpp @@ -344,8 +344,8 @@ bool Att::validNameValue ( else if (name == "limit") { - if (value == "" || !digitsOnly (value)) - throw std::string ("The '") + name + "' attribute must be an integer."; + if (value == "" || (value != "page" && !digitsOnly (value))) + throw std::string ("The '") + name + "' attribute must be an integer, or the value 'page'."; } else if (name == "status") diff --git a/src/Config.cpp b/src/Config.cpp index 08ced4556..4367e6c09 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -293,7 +293,7 @@ std::string Config::defaults = "report.next.columns=id,project,priority,due,active,age,description\n" "report.next.labels=ID,Project,Pri,Due,Active,Age,Description\n" "report.next.sort=due+,priority-,project+\n" - "report.next.filter=status:pending\n" + "report.next.filter=status:pending limit:page\n" "#report.next.dateformat=m/d/Y\n" "#report.next.annotations=full\n" "\n"; diff --git a/src/Context.h b/src/Context.h index 1382619cf..f4a29e3f2 100644 --- a/src/Context.h +++ b/src/Context.h @@ -55,6 +55,7 @@ public: void shadow (); // shadow file update int getWidth (); // determine terminal width + int getHeight (); // determine terminal height void header (const std::string&); // Header message sink void footnote (const std::string&); // Footnote message sink @@ -93,7 +94,6 @@ public: std::vector tagRemovals; Hooks hooks; -private: std::vector headers; std::vector footnotes; std::vector debugMessages; diff --git a/src/Table.cpp b/src/Table.cpp index c45194e62..a9771f196 100644 --- a/src/Table.cpp +++ b/src/Table.cpp @@ -1024,10 +1024,13 @@ void Table::clean (std::string& value) } //////////////////////////////////////////////////////////////////////////////// -const std::string Table::render (int maximum /* = 0 */) +const std::string Table::render (int maxrows /* = 0 */, int maxlines /* = 0 */) { Timer t ("Table::render"); + // May not exceed maxlines, if non-zero. + int renderedlines = 0; + calculateColumnWidths (); // Print column headers in column order. @@ -1048,8 +1051,12 @@ const std::string Table::render (int maximum /* = 0 */) } output += "\n"; + ++renderedlines; if (underline.length ()) + { output += underline + "\n"; + ++renderedlines; + } // Determine row order, according to sort options. std::vector order; @@ -1060,14 +1067,17 @@ const std::string Table::render (int maximum /* = 0 */) if (mSortColumns.size ()) sort (order); - // If a non-zero maximum is specified, then it limits the number of rows of + // If a non-zero maxrows is specified, then it limits the number of rows of // the table that are rendered. - int limit = mRows; - if (maximum != 0) - limit = min (maximum, mRows); + int limitrows = mRows; + if (maxrows != 0) + limitrows = min (maxrows, mRows); + + // If a non-zero maxlines is specified, then it limits the number of lines + // of output from the table that are rendered. // Print all rows. - for (int row = 0; row < limit; ++row) + for (int row = 0; row < limitrows; ++row) { std::vector > columns; std::vector blanks; @@ -1105,6 +1115,11 @@ const std::string Table::render (int maximum /* = 0 */) // Trim right. output.erase (output.find_last_not_of (" ") + 1); output += "\n"; + + ++renderedlines; + + if (maxlines != 0 && renderedlines >= maxlines) + break; } } else @@ -1113,6 +1128,9 @@ const std::string Table::render (int maximum /* = 0 */) output.erase (output.find_last_not_of (" ") + 1); output += "\n"; } + + if (maxlines != 0 && renderedlines >= maxlines) + break; } optimize (output); diff --git a/src/Table.h b/src/Table.h index 6fbd651b4..e04417496 100644 --- a/src/Table.h +++ b/src/Table.h @@ -92,7 +92,7 @@ public: int rowCount (); int columnCount (); - const std::string render (int maximum = 0); + const std::string render (int maxrows = 0, int maxlines = 0); private: std::string getCell (const int, const int); diff --git a/src/command.cpp b/src/command.cpp index 02336535a..5a2dbea61 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -647,7 +647,7 @@ int handleShow (std::string &outs) "next project shadow.command shadow.file shadow.notify weekstart editor " "import.synonym.id import.synonym.uuid complete.all.projects " "complete.all.tags search.case.sensitive hooks active.indicator tag.indicator " - "recurrence.indicator recurrence.limit " + "recurrence.indicator recurrence.limit list.all.projects list.all.tags " #ifdef FEATURE_SHELL "shell.prompt " #endif @@ -735,7 +735,7 @@ int handleShow (std::string &outs) out << std::endl << table.render () - << (table.rowCount () == 0 ? "No matching configuration variables\n" : "") + << (table.rowCount () == 0 ? "No matching configuration variables\n" : "") << std::endl; // Display the unrecognized variables. diff --git a/src/custom.cpp b/src/custom.cpp index 792217f74..eb264db60 100644 --- a/src/custom.cpp +++ b/src/custom.cpp @@ -715,23 +715,37 @@ int runCustomReport ( table.setTableAlternateColor (alternate); } - // Limit the number of rows according to the report definition. - int maximum = context.config.getInteger (std::string ("report.") + report + ".limit"); + // Report output can be limited by rows or lines. + int maxrows = 0; + int maxlines = 0; + getLimits (report, maxrows, maxlines); - // If the custom report has a defined limit, then allow a numeric override. - // This is an integer specified as a filter (limit:10). - if (context.task.has ("limit")) - maximum = atoi (context.task.get ("limit").c_str ()); + // Adjust for fluff in the output. + if (maxlines) + maxlines -= (context.config.getBoolean ("blanklines") ? 2 : 0) + + 1 + + context.headers.size () + + context.footnotes.size (); std::stringstream out; if (table.rowCount ()) + { out << optionalBlankLine () - << table.render (maximum) + << table.render (maxrows, maxlines) << optionalBlankLine () << table.rowCount () - << (table.rowCount () == 1 ? " task" : " tasks") - << std::endl; - else { + << (table.rowCount () == 1 ? " task" : " tasks"); + + if (maxrows) + out << ", " << maxrows << " shown"; + + if (maxlines) + out << ", truncated to " << maxlines-1 << " lines"; + + out << std::endl; + } + else + { out << "No matches." << std::endl; rc = 1; @@ -813,3 +827,44 @@ void validSortColumns ( } //////////////////////////////////////////////////////////////////////////////// +// A value of zero mean unlimited. +// A value of 'page' means however many screen lines there are. +// A value of a positive integer is a row limit. +void getLimits (const std::string& report, int& rows, int& lines) +{ + rows = 0; + lines = 0; + + int screenheight = 0; + + // If a report has a stated limit, use it. + if (report != "") + { + std::string name = "report." + report + ".limit"; + if (context.config.get (name) == "page") + lines = screenheight = context.getHeight (); + else + rows = context.config.getInteger (name); + } + + // If the custom report has a defined limit, then allow a numeric override. + // This is an integer specified as a filter (limit:10). + if (context.task.has ("limit")) + { + if (context.task.get ("limit") == "page") + { + if (screenheight == 0) + screenheight = context.getHeight (); + + rows = 0; + lines = screenheight; + } + else + { + rows = atoi (context.task.get ("limit").c_str ()); + lines = 0; + } + } +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/interactive.cpp b/src/interactive.cpp index 241f9cecd..7614ae5fd 100644 --- a/src/interactive.cpp +++ b/src/interactive.cpp @@ -167,3 +167,36 @@ int Context::getWidth () } //////////////////////////////////////////////////////////////////////////////// +int Context::getHeight () +{ + // Determine window size, and set table accordingly. + int height = 25; // TODO Is there a better number? + +#ifdef HAVE_LIBNCURSES + if (config.getBoolean ("curses")) + { +#ifdef FEATURE_NCURSES_COLS + initscr (); + height = LINES; +#else + WINDOW* w = initscr (); + height = w->_maxy + 1; +#endif + endwin (); + + std::stringstream out; + out << "Context::getHeight: ncurses determined height of " << height << " characters"; + debug (out.str ()); + } + else + debug ("Context::getHeight: ncurses available but disabled."); +#else + std::stringstream out; + out << "Context::getHeight: no ncurses, using height of " << height << " characters"; + debug (out.str ()); +#endif + + return height; +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/main.h b/src/main.h index ade00a904..79423e5e6 100644 --- a/src/main.h +++ b/src/main.h @@ -117,6 +117,7 @@ int runCustomReport (const std::string&, const std::string&, std::string&); void validReportColumns (const std::vector &); void validSortColumns (const std::vector &, const std::vector &); +void getLimits (const std::string&, int&, int&); // rules.cpp void initializeColorRules (); diff --git a/src/report.cpp b/src/report.cpp index a0a28d030..c57b8d10a 100644 --- a/src/report.cpp +++ b/src/report.cpp @@ -283,7 +283,7 @@ int longUsage (std::string &outs) << " until: Recurrence end date" << "\n" << " fg: Foreground color" << "\n" << " bg: Background color" << "\n" - << " limit: Desired number of rows in report" << "\n" + << " limit: Desired number of rows in report, or 'page'" << "\n" << " wait: Date until task becomes pending" << "\n" << "\n" << "Attribute modifiers improve filters. Supported modifiers are:" << "\n" @@ -1994,15 +1994,15 @@ std::string renderMonths ( case 1: // imminent cellColor.blend (color_due); break; - + case 2: // today cellColor.blend (color_duetoday); break; - + case 3: // overdue cellColor.blend (color_overdue); break; - + case 0: // not due at all default: break; @@ -2318,6 +2318,7 @@ int handleReportCalendar (std::string &outs) holTable.addCell (row, 1, holName); } } + out << optionalBlankLine () << holTable.render () << std::endl; diff --git a/src/tests/limit.t b/src/tests/limit.t new file mode 100755 index 000000000..3c9e0ce7b --- /dev/null +++ b/src/tests/limit.t @@ -0,0 +1,96 @@ +#! /usr/bin/perl +################################################################################ +## task - a command line task list manager. +## +## Copyright 2006 - 2010, 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 => 8; + +# Create the rc file. +if (open my $fh, '>', 'limit.rc') +{ + print $fh "data.location=.\n"; + close $fh; + ok (-r 'limit.rc', 'Created limit.rc'); +} + +# Add a large number of tasks (> 25). +qx{../task rc:limit.rc add one}; +qx{../task rc:limit.rc add two}; +qx{../task rc:limit.rc add three}; +qx{../task rc:limit.rc add four}; +qx{../task rc:limit.rc add five}; +qx{../task rc:limit.rc add six}; +qx{../task rc:limit.rc add seven}; +qx{../task rc:limit.rc add eight}; +qx{../task rc:limit.rc add nine}; +qx{../task rc:limit.rc add ten}; +qx{../task rc:limit.rc add eleven}; +qx{../task rc:limit.rc add twelve}; +qx{../task rc:limit.rc add thirteen}; +qx{../task rc:limit.rc add fourteen}; +qx{../task rc:limit.rc add fifteen}; +qx{../task rc:limit.rc add sixteen}; +qx{../task rc:limit.rc add seventeen}; +qx{../task rc:limit.rc add eighteen}; +qx{../task rc:limit.rc add nineteen}; +qx{../task rc:limit.rc add twenty}; +qx{../task rc:limit.rc add twenty one}; +qx{../task rc:limit.rc add twenty two}; +qx{../task rc:limit.rc add twenty three}; +qx{../task rc:limit.rc add twenty four}; +qx{../task rc:limit.rc add twenty five}; +qx{../task rc:limit.rc add twenty six}; +qx{../task rc:limit.rc add twenty seven}; +qx{../task rc:limit.rc add twenty eight}; +qx{../task rc:limit.rc add twenty nine}; +qx{../task rc:limit.rc add thirty}; + +my $output = qx{../task rc:limit.rc ls}; +like ($output, qr/^30 tasks$/ms, 'unlimited'); + +$output = qx{../task rc:limit.rc ls limit:0}; +like ($output, qr/^30 tasks$/ms, 'limited to 0 - unlimited'); + +$output = qx{../task rc:limit.rc ls limit:3}; +like ($output, qr/^30 tasks, 3 shown$/ms, 'limited to 3'); + +$output = qx{../task rc:limit.rc ls limit:page}; +like ($output, qr/^30 tasks, truncated to 20 lines$/ms, 'limited to page'); + +# Cleanup. +unlink 'pending.data'; +ok (!-r 'pending.data', 'Removed pending.data'); + +unlink 'undo.data'; +ok (!-r 'undo.data', 'Removed undo.data'); + +unlink 'limit.rc'; +ok (!-r 'limit.rc', 'Removed limit.rc'); + +exit 0; +