- Integrated ViewTask and ViewText completely.
- Obsoleted Table.{h,cpp}, Grid.{h,cpp}, grid.t.cpp
- Fixed ViewTask rendering bug that caused the full width to be used every time.
This commit is contained in:
Paul Beckingham 2011-05-13 01:10:30 -04:00
parent c8c7e02bc8
commit 6fb3bc5b03
14 changed files with 208 additions and 2002 deletions

View file

@ -16,7 +16,6 @@ set (task_SRCS API.cpp API.h
Duration.cpp Duration.h
File.cpp File.h
Filter.cpp Filter.h
Grid.cpp Grid.h
Hooks.cpp Hooks.h
JSON.cpp JSON.h
Location.cpp Location.h
@ -29,7 +28,6 @@ set (task_SRCS API.cpp API.h
Subst.cpp Subst.h
TDB.cpp TDB.h
TDB2.cpp TDB2.h
Table.cpp Table.h
Task.cpp Task.h
Taskmod.cpp Taskmod.h
Timer.cpp Timer.h

View file

@ -1,421 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2006 - 2011, Paul Beckingham, Federico Hernandez.
// 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
//
//
//
// Grid implements a sparse 2D array of Cell objects. Grid makes every effort
// to perform well on cell insertion and retrieval. A Cell is a variant type,
// capable of storing char, bool, int, float, double and std::string types.
//
// Every cell is accessible from both mColumns and mRows. This allows the
// client code to specify which method is used, because there will be a
// performance penalty with one of the methods, depending on the layout of
// cells within the grid.
//
// mColumns, like mRows, is a vector of a vector of Cell*.
//
// mColumns
// [0..n]
// +---+---+-----------+---+
// | 0 | 1 | | n |
// +---+---+-----------+---+
// | |
// v |
// +---+ . +---+ . | .
// mRows | 0 | -------> | x | v
// [0..1] +---+ . +---+ +---+
// | 1 | -------> | y | --------> | z |
// +---+ . +---+ +---+
// | |
// | |
// | |
// +---+ . . . . .
// | n |
// +---+ . . . . .
//
//
//
////////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <stdlib.h>
#include <text.h>
#include <Grid.h>
////////////////////////////////////////////////////////////////////////////////
Grid::Grid ()
{
}
////////////////////////////////////////////////////////////////////////////////
// The cells are deleted via their mRows reference, not by their mColumns
// reference. This is because the cells are doubly-linked, and so the
// convention is that rows own cells, columns merely own pointers.
Grid::~Grid ()
{
std::vector < std::vector <Cell*>* >::iterator row;
std::vector <Cell*>::iterator col;
for (row = mRows.begin (); row != mRows.end (); ++row)
if (*row)
for (col = (*row)->begin (); col != (*row)->end (); ++col)
if (*col)
delete *col;
std::vector < std::vector <Cell*>* >::iterator it;
for (it = mRows.begin (); it != mRows.end (); ++it)
if (*it)
delete *it;
for (it = mColumns.begin (); it != mColumns.end (); ++it)
if (*it)
delete *it;
}
////////////////////////////////////////////////////////////////////////////////
void Grid::add (
const unsigned int row,
const unsigned int col,
const bool value)
{
expandGrid (row, col);
insertCell (row, col, new Cell (value));
}
void Grid::add (
const unsigned int row,
const unsigned int col,
const char value)
{
expandGrid (row, col);
insertCell (row, col, new Cell (value));
}
void Grid::add (
const unsigned int row,
const unsigned int col,
const int value)
{
expandGrid (row, col);
insertCell (row, col, new Cell (value));
}
void Grid::add (
const unsigned int row,
const unsigned int col,
const float value)
{
expandGrid (row, col);
insertCell (row, col, new Cell (value));
}
void Grid::add (
const unsigned int row,
const unsigned int col,
const double value)
{
expandGrid (row, col);
insertCell (row, col, new Cell (value));
}
void Grid::add (
const unsigned int row,
const unsigned int col,
const char* value)
{
expandGrid (row, col);
insertCell (row, col, new Cell (std::string (value)));
}
void Grid::add (
const unsigned int row,
const unsigned int col,
const std::string& value)
{
expandGrid (row, col);
insertCell (row, col, new Cell (value));
}
////////////////////////////////////////////////////////////////////////////////
unsigned int Grid::width () const
{
return mColumns.size ();
}
unsigned int Grid::height () const
{
return mRows.size ();
}
////////////////////////////////////////////////////////////////////////////////
Grid::Cell* Grid::byRow (const unsigned int row, const unsigned int col) const
{
if (row < mRows.size () &&
mRows[row] != NULL &&
col < mRows[row]->size ())
return (*mRows[row])[col];
return NULL;
}
Grid::Cell* Grid::byColumn (const unsigned int row, const unsigned int col) const
{
if (col < mColumns.size () &&
mColumns[col] != NULL &&
row < mColumns[col]->size ())
return (*mColumns[col])[row];
return NULL;
}
////////////////////////////////////////////////////////////////////////////////
void Grid::expandGrid (const unsigned int row, const unsigned int col)
{
// If the new row is outside the bounds of the current grid, add blank rows to
// pad, then a new row vector.
if (row >= mRows.size ())
{
for (unsigned int r = mRows.size (); r <= row; ++r)
if (r < row)
mRows.push_back (NULL);
else
mRows.push_back (new std::vector <Cell*>);
}
// If the new row is within the bounds of the current grid, ensure that the
// row points to a vector of cells.
else if (mRows[row] == NULL)
mRows[row] = new std::vector <Cell*>;
if (col >= mRows[row]->size ())
for (unsigned int c = mRows[row]->size (); c <= col; ++c)
mRows[row]->push_back (NULL);
// If the new col is outside the bounds of the current grid, add blank cols to
// pad, then a new col vector.
if (col >= mColumns.size ())
{
for (unsigned int c = mColumns.size (); c <= col; ++c)
if (c < col)
mColumns.push_back (NULL);
else
mColumns.push_back (new std::vector <Cell*>);
}
// If the new col is within the bounds of the current grid, ensure that the
// col points to a vector of cells.
else if (mColumns[col] == NULL)
mColumns[col] = new std::vector <Cell*>;
if (row >= mColumns[col]->size ())
for (unsigned int r = mColumns[col]->size (); r <= row; ++r)
mColumns[col]->push_back (NULL);
}
////////////////////////////////////////////////////////////////////////////////
void Grid::insertCell (
const unsigned int row,
const unsigned int col,
Cell* cell)
{
// Delete any existing cell, because cells are owned by rows, not columns.
if ((*mRows[row])[col] != NULL)
delete (*mRows[row])[col];
(*mRows[row])[col] = cell;
(*mColumns[col])[row] = cell;
}
////////////////////////////////////////////////////////////////////////////////
Grid::Cell::Cell (const bool value)
: mType (CELL_BOOL)
, mBool (value)
{
}
Grid::Cell::Cell (const char value)
: mType (CELL_CHAR)
, mChar (value)
{
}
Grid::Cell::Cell (const int value)
: mType (CELL_INT)
, mInt (value)
{
}
Grid::Cell::Cell (const float value)
: mType (CELL_FLOAT)
, mFloat (value)
{
}
Grid::Cell::Cell (const double value)
: mType (CELL_DOUBLE)
, mDouble (value)
{
}
Grid::Cell::Cell (const std::string& value)
: mType (CELL_STRING)
, mString (value)
{
}
////////////////////////////////////////////////////////////////////////////////
// These cast operators make a best approximation to an appropriate rendering,
// given the format change.
Grid::Cell::operator bool () const
{
switch (mType)
{
case CELL_BOOL: return mBool;
case CELL_CHAR: return mChar != '\0' &&
mChar != ' ' &&
mChar != '0';
case CELL_INT: return mInt != 0;
case CELL_FLOAT: return mFloat != 0.0;
case CELL_DOUBLE: return mDouble != 0.0;
case CELL_STRING: return mString.length () > 0;
}
return false;
}
Grid::Cell::operator char () const
{
switch (mType)
{
case CELL_BOOL: return mBool ? 'Y' : 'N'; // TODO i18n
case CELL_CHAR: return mChar;
case CELL_INT: return (char) mInt;
case CELL_FLOAT: return (char) (int) mFloat;
case CELL_DOUBLE: return (char) (int) mDouble;
case CELL_STRING: return mString[0];
}
return '\0';
}
Grid::Cell::operator int () const
{
switch (mType)
{
case CELL_BOOL: return mBool ? 1 : 0;
case CELL_CHAR: return (int) mChar;
case CELL_INT: return mInt;
case CELL_FLOAT: return (int) mFloat;
case CELL_DOUBLE: return (int) mDouble;
case CELL_STRING: return atoi (mString.c_str ());
}
return 0;
}
Grid::Cell::operator float () const
{
switch (mType)
{
case CELL_BOOL: return mBool ? 1.0 : 0.0;
case CELL_CHAR: return (float) (int) mChar;
case CELL_INT: return (float) mInt;
case CELL_FLOAT: return mFloat;
case CELL_DOUBLE: return (float) mDouble;
case CELL_STRING: return (float) atof (mString.c_str ());
}
return 0.0;
}
Grid::Cell::operator double () const
{
switch (mType)
{
case CELL_BOOL: return mBool ? 1.0 : 0.0;
case CELL_CHAR: return (double) (int) mChar;
case CELL_INT: return (double) mInt;
case CELL_FLOAT: return (double) mFloat;
case CELL_DOUBLE: return mDouble;
case CELL_STRING: return (double) atof (mString.c_str ());
}
return 0.0;
}
Grid::Cell::operator std::string () const
{
switch (mType)
{
case CELL_BOOL: return mBool ? "true" : "false"; // TODO i18n
case CELL_CHAR: return format (mChar);
case CELL_INT: return format (mInt);
case CELL_FLOAT: return format (mFloat, 1, 6);
case CELL_DOUBLE: return format (mDouble, 1, 8);
case CELL_STRING: return mString;
}
return std::string ("");
}
////////////////////////////////////////////////////////////////////////////////
bool Grid::Cell::operator== (const Grid::Cell& rhs) const
{
switch (mType)
{
case CELL_BOOL: return mBool == rhs.mBool ? true : false;
case CELL_CHAR: return mChar == rhs.mChar ? true : false;
case CELL_INT: return mInt == rhs.mInt ? true : false;
case CELL_FLOAT: return mFloat == rhs.mFloat ? true : false;
case CELL_DOUBLE: return mDouble == rhs.mDouble ? true : false;
case CELL_STRING: return mString == rhs.mString ? true : false;
default: break; // To prevent warnings.
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Grid::Cell::operator!= (const Grid::Cell& rhs) const
{
switch (mType)
{
case CELL_BOOL: return mBool != rhs.mBool ? true : false;
case CELL_CHAR: return mChar != rhs.mChar ? true : false;
case CELL_INT: return mInt != rhs.mInt ? true : false;
case CELL_FLOAT: return mFloat != rhs.mFloat ? true : false;
case CELL_DOUBLE: return mDouble != rhs.mDouble ? true : false;
case CELL_STRING: return mString != rhs.mString ? true : false;
default: break; // To prevent warnings.
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
Grid::Cell::cellType Grid::Cell::type () const
{
return mType;
}
////////////////////////////////////////////////////////////////////////////////

View file

@ -1,106 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2006 - 2011, Paul Beckingham, Federico Hernandez.
// 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
//
////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDED_GRID
#define INCLUDED_GRID
#include <stdio.h>
#include <string>
#include <vector>
////////////////////////////////////////////////////////////////////////////////
class Grid
{
public:
class Cell
{
public:
Cell (const bool);
Cell (const char);
Cell (const int);
Cell (const float);
Cell (const double);
Cell (const std::string&);
Cell (const Cell&);
Cell& operator= (const Cell&);
operator bool () const;
operator char () const;
operator int () const;
operator float () const;
operator double () const;
operator std::string () const;
bool operator== (const Cell&) const;
bool operator!= (const Cell&) const;
enum cellType {CELL_BOOL, CELL_CHAR, CELL_INT, CELL_FLOAT, CELL_DOUBLE, CELL_STRING};
cellType type () const;
private:
cellType mType;
bool mBool;
char mChar;
int mInt;
float mFloat;
double mDouble;
std::string mString;
};
public:
Grid ();
~Grid ();
Grid (const Grid&);
Grid& operator= (const Grid&);
void add (const unsigned int, const unsigned int, const bool);
void add (const unsigned int, const unsigned int, const char);
void add (const unsigned int, const unsigned int, const int);
void add (const unsigned int, const unsigned int, const float);
void add (const unsigned int, const unsigned int, const double);
void add (const unsigned int, const unsigned int, const char*);
void add (const unsigned int, const unsigned int, const std::string&);
unsigned int width () const;
unsigned int height () const;
Cell* byRow (const unsigned int, const unsigned int) const;
Cell* byColumn (const unsigned int, const unsigned int) const;
private:
void expandGrid (const unsigned int, const unsigned int);
void insertCell (const unsigned int, const unsigned int, Cell*);
private:
std::vector < std::vector <Cell*>* > mRows;
std::vector < std::vector <Cell*>* > mColumns;
};
#endif
////////////////////////////////////////////////////////////////////////////////

View file

@ -284,7 +284,7 @@ void TDB2::commit ()
#include "TDB.h"
#include "Directory.h"
#include "File.h"
#include "Table.h"
#include "ViewText.h"
#include "Timer.h"
#include "Color.h"
#include "main.h"
@ -1000,24 +1000,12 @@ void TDB::undo ()
// Attributes are all there is, so figure the different attribute names
// between before and after.
Table table;
table.setTableWidth (context.getWidth ());
table.setTableIntraPadding (2);
table.addColumn (" ");
table.addColumn ("Prior Values");
table.addColumn ("Current Values");
if (context.color () && context.config.getBoolean ("fontunderline"))
{
table.setColumnUnderline (1);
table.setColumnUnderline (2);
}
else
table.setTableDashedUnderline ();
table.setColumnWidth (0, Table::minimum);
table.setColumnWidth (1, Table::flexible);
table.setColumnWidth (2, Table::flexible);
ViewText view;
view.width (context.getWidth ());
view.intraPadding (2);
view.add (Column::factory ("string", ""));
view.add (Column::factory ("string", "Prior Values"));
view.add (Column::factory ("string", "Current Values"));
Task after (current);
@ -1040,10 +1028,9 @@ void TDB::undo ()
int row;
foreach (name, beforeOnly)
{
row = table.addRow ();
table.addCell (row, 0, *name);
table.addCell (row, 1, renderAttribute (*name, before.get (*name)));
table.setCellColor (row, 1, color_red);
row = view.addRow ();
view.set (row, 0, *name, red);
view.set (row, 1, renderAttribute (*name, before.get (*name)), red);
}
foreach (name, before)
@ -1053,25 +1040,18 @@ void TDB::undo ()
if (currentValue != "")
{
row = table.addRow ();
table.addCell (row, 0, name->first);
table.addCell (row, 1, renderAttribute (name->first, priorValue));
table.addCell (row, 2, renderAttribute (name->first, currentValue));
if (priorValue != currentValue)
{
table.setCellColor (row, 1, color_red);
table.setCellColor (row, 2, color_green);
}
row = view.addRow ();
view.set (row, 0, name->first);
view.set (row, 1, renderAttribute (name->first, priorValue), priorValue != currentValue ? color_red : color_green);
view.set (row, 2, renderAttribute (name->first, currentValue), priorValue != currentValue ? color_red : color_green);
}
}
foreach (name, afterOnly)
{
row = table.addRow ();
table.addCell (row, 0, *name);
table.addCell (row, 2, renderAttribute (*name, after.get (*name)));
table.setCellColor (row, 2, color_green);
row = view.addRow ();
view.set (row, 0, *name);
view.set (row, 2, renderAttribute (*name, after.get (*name)), color_green);
}
}
else
@ -1079,15 +1059,14 @@ void TDB::undo ()
int row;
foreach (name, after)
{
row = table.addRow ();
table.addCell (row, 0, name->first);
table.addCell (row, 2, renderAttribute (name->first, after.get (name->first)));
table.setCellColor (row, 2, color_green);
row = view.addRow ();
view.set (row, 0, name->first);
view.set (row, 2, renderAttribute (name->first, after.get (name->first)), color_green);
}
}
std::cout << "\n"
<< table.render ()
<< view.render ()
<< "\n";
}
@ -1114,27 +1093,21 @@ void TDB::undo ()
Task after (current);
// Generate table header.
Table table;
table.setTableWidth (context.getWidth ());
table.setTableIntraPadding (2);
table.addColumn (" ");
table.addColumn (" ");
table.setColumnWidth (0, Table::minimum);
table.setColumnWidth (1, Table::flexible);
table.setColumnJustification (0, Table::right);
table.setColumnJustification (1, Table::left);
ViewText view
view.width (context.getWidth ());
view.intraPadding (2);
view.addColumn (Column::factory ("string.right", ""));
view.addColumn (Column::factory ("string", ""));
int row = table.addRow ();
table.addCell (row, 0, "--- previous state");
table.addCell (row, 1, "Undo will restore this state");
table.setRowColor (row, color_red);
int row = view.addRow ();
view.set (row, 0, "--- previous state", color_red);
view.set (row, 1, "Undo will restore this state", color_red);
row = table.addRow ();
table.addCell (row, 0, "+++ current state "); // Note trailing space.
table.addCell (row, 1, "Change made " + lastChange.toString (context.config.get ("dateformat")));
table.setRowColor (row, color_green);
row = view.addRow ();
view.set (row, 0, "+++ current state ", color_green); // Note trailing space.
view.set (row, 1, "Change made " + lastChange.toString (context.config.get ("dateformat")), color_green);
table.addRow ();
view.addRow ();
// Add rows to table showing diffs.
std::vector <std::string> all;
@ -1172,55 +1145,49 @@ void TDB::undo ()
{
// Show nothing - no point displaying that which did not change.
// row = table.addRow ();
// table.addCell (row, 0, *a + ":");
// table.addCell (row, 1, before_att);
// row = view.addRow ();
// view.set (row, 0, *a + ":");
// view.set (row, 1, before_att);
}
// Attribute deleted.
else if (before_att != "" && after_att == "")
{
row = table.addRow ();
table.addCell (row, 0, "-" + *a + ":");
table.addCell (row, 1, before_att);
table.setRowColor (row, color_red);
row = view.addRow ();
view.set (row, 0, "-" + *a + ":", color_red);
view.set (row, 1, before_att, color_red);
row = table.addRow ();
table.addCell (row, 0, "+" + *a + ":");
table.setRowColor (row, color_green);
row = view.addRow ();
view.set (row, 0, "+" + *a + ":", color_green);
}
// Attribute added.
else if (before_att == "" && after_att != "")
{
row = table.addRow ();
table.addCell (row, 0, "-" + *a + ":");
table.setRowColor (row, color_red);
row = view.addRow ();
view.set (row, 0, "-" + *a + ":", color_red);
row = table.addRow ();
table.addCell (row, 0, "+" + *a + ":");
table.addCell (row, 1, after_att);
table.setRowColor (row, color_green);
row = view.addRow ();
view.set (row, 0, "+" + *a + ":", color_green);
view.set (row, 1, after_att, color_green);
}
// Attribute changed.
else
{
row = table.addRow ();
table.addCell (row, 0, "-" + *a + ":");
table.addCell (row, 1, before_att);
table.setRowColor (row, color_red);
row = view.addRow ();
view.set (row, 0, "-" + *a + ":", color_red);
view.set (row, 1, before_att, color_red);
row = table.addRow ();
table.addCell (row, 0, "+" + *a + ":");
table.addCell (row, 1, after_att);
table.setRowColor (row, color_green);
row = view.addRow ();
view.set (row, 0, "+" + *a + ":", color_green);
view.set (row, 1, after_att, color_green);
}
}
}
std::cout << "\n"
<< table.render ()
<< view.render ()
<< "\n";
}

View file

@ -1,895 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2006 - 2011, Paul Beckingham, Federico Hernandez.
// 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
//
//
//
// Attributes Table Row Column Cell
// ----------------------------------------------------
// foreground color Y Y Y Y
// background color Y Y Y Y
// padding Y Y Y Y
// wrap Y Y Y Y
// width Y - Y Y
// height - Y - Y
// justification Y Y Y Y
//
// Precedence
// If attributes conflict, the precedence order is:
// cell
// row
// column
// table
//
////////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <string.h>
#include <assert.h>
#include <algorithm>
#include "Table.h"
#include "Date.h"
#include "Duration.h"
#include "Timer.h"
#include "text.h"
#include "utf8.h"
#include "util.h"
#include "Context.h"
extern Context context;
Table* table = NULL;
////////////////////////////////////////////////////////////////////////////////
Table::Table ()
: mRows (0)
, mIntraPadding (1)
, mDashedUnderline (false)
, mTablePadding (0)
, mTableWidth (0)
{
}
////////////////////////////////////////////////////////////////////////////////
Table::~Table ()
{
}
////////////////////////////////////////////////////////////////////////////////
void Table::setTableAlternateColor (const Color& c)
{
alternate = c;
}
////////////////////////////////////////////////////////////////////////////////
void Table::setTablePadding (int padding)
{
mTablePadding = padding;
}
////////////////////////////////////////////////////////////////////////////////
void Table::setTableIntraPadding (int padding)
{
mIntraPadding = padding;
}
////////////////////////////////////////////////////////////////////////////////
void Table::setTableWidth (int width)
{
assert (width > 0);
mTableWidth = width;
}
////////////////////////////////////////////////////////////////////////////////
void Table::setTableDashedUnderline ()
{
mDashedUnderline = true;
}
////////////////////////////////////////////////////////////////////////////////
int Table::addColumn (const std::string& col)
{
mSpecifiedWidth.push_back (minimum);
mMaxDataWidth.push_back (col == "" ? 1 : utf8_length (col));
mCalculatedWidth.push_back (0);
mColumnPadding.push_back (0);
mColumns.push_back (col == "" ? " " : col);
return mColumns.size () - 1;
}
////////////////////////////////////////////////////////////////////////////////
void Table::setColumnUnderline (int column)
{
char id[12];
sprintf (id, "col:%d", column);
mUnderline[id] = Color (Color::nocolor, Color::nocolor, true, false, false);
}
////////////////////////////////////////////////////////////////////////////////
void Table::setColumnPadding (int column, int padding)
{
mColumnPadding[column] = padding;
}
////////////////////////////////////////////////////////////////////////////////
void Table::setColumnWidth (int column, int width)
{
assert (width > 0);
mSpecifiedWidth[column] = width;
}
////////////////////////////////////////////////////////////////////////////////
void Table::setColumnWidth (int column, sizing s)
{
mSpecifiedWidth[column] = (int) s;
}
////////////////////////////////////////////////////////////////////////////////
void Table::setColumnCommify (int column)
{
mCommify[column] = true;
}
////////////////////////////////////////////////////////////////////////////////
void Table::setColumnJustification (int column, just j)
{
mJustification[column] = j;
}
////////////////////////////////////////////////////////////////////////////////
void Table::sortOn (int column, order o)
{
mSortColumns.push_back (column);
mSortOrder[column] = o;
}
////////////////////////////////////////////////////////////////////////////////
int Table::addRow ()
{
return mRows++;
}
////////////////////////////////////////////////////////////////////////////////
void Table::setRowColor (const int row, const Color& c)
{
char id[12];
sprintf (id, "row:%d", row);
mColor[id].blend (c);
}
////////////////////////////////////////////////////////////////////////////////
void Table::addCell (const int row, const int col, const std::string& data)
{
int length = 0;
if (mCommify.find (col) != mCommify.end ())
mData.add (row, col, commify (data));
else
mData.add (row, col, data);
// For multi-line cells, find the longest line.
if (data.find ("\n") != std::string::npos)
{
length = 0;
std::vector <std::string> lines;
split (lines, data, "\n");
for (unsigned int i = 0; i < lines.size (); ++i)
if (utf8_length (lines[i]) > length)
length = utf8_length (lines[i]);
}
else
length = utf8_length (data);
// Automatically maintain max width.
mMaxDataWidth[col] = max (mMaxDataWidth[col], length);
}
////////////////////////////////////////////////////////////////////////////////
void Table::addCell (const int row, const int col, const char data)
{
mData.add (row, col, data);
// Automatically maintain max width.
mMaxDataWidth[col] = max (mMaxDataWidth[col], 1);
}
////////////////////////////////////////////////////////////////////////////////
void Table::addCell (const int row, const int col, const int data)
{
char value[12];
sprintf (value, "%d", data);
if (mCommify.find (col) != mCommify.end ())
mData.add (row, col, commify (value));
else
mData.add (row, col, value);
// Automatically maintain max width.
mMaxDataWidth[col] = max (mMaxDataWidth[col], (signed) strlen (value));
}
////////////////////////////////////////////////////////////////////////////////
void Table::addCell (const int row, const int col, const float data)
{
char value[24];
sprintf (value, "%.2f", data);
if (mCommify.find (col) != mCommify.end ())
mData.add (row, col, commify (value));
else
mData.add (row, col, value);
// Automatically maintain max width.
mMaxDataWidth[col] = max (mMaxDataWidth[col], (signed) strlen (value));
}
////////////////////////////////////////////////////////////////////////////////
void Table::addCell (const int row, const int col, const double data)
{
char value[24];
sprintf (value, "%.6f", data);
if (mCommify.find (col) != mCommify.end ())
mData.add (row, col, commify (value));
else
mData.add (row, col, value);
// Automatically maintain max width.
mMaxDataWidth[col] = max (mMaxDataWidth[col], (signed) strlen (value));
}
////////////////////////////////////////////////////////////////////////////////
void Table::setCellColor (const int row, const int col, const Color& c)
{
char id[24];
sprintf (id, "cell:%d,%d", row, col);
mColor[id] = c;
}
////////////////////////////////////////////////////////////////////////////////
std::string Table::getCell (const int row, const int col)
{
Grid::Cell* c = mData.byRow (row, col);
if (c)
return (std::string) *c;
return "";
}
////////////////////////////////////////////////////////////////////////////////
Color Table::getColor (const int index, const int row, const int col)
{
// Color defaults to trivial.
Color c;
// For alternating rows, use Table::alternate.
std::map <std::string, Color>::iterator i;
char id[24];
if (index % 2)
c = alternate;
// Blend with a row color, if specified.
sprintf (id, "row:%d", row);
if ((i = mColor.find (id)) != mColor.end ())
c.blend (i->second);
// Blend with a cell color, if specified.
sprintf (id, "cell:%d,%d", row, col);
if ((i = mColor.find (id)) != mColor.end ())
c.blend (i->second);
return c;
}
////////////////////////////////////////////////////////////////////////////////
Color Table::getHeaderUnderline (int col)
{
char idCol[12];
sprintf (idCol, "col:%d", col);
return mUnderline.find (idCol) != mUnderline.end () ? mUnderline[idCol]
: Color ();
}
////////////////////////////////////////////////////////////////////////////////
int Table::getIntraPadding ()
{
return mIntraPadding;
}
////////////////////////////////////////////////////////////////////////////////
int Table::getPadding (int col)
{
return max (mColumnPadding[col], mTablePadding);
}
////////////////////////////////////////////////////////////////////////////////
// Using mSpecifiedWidth, mMaxDataWidth, generate mCalculatedWidth.
void Table::calculateColumnWidths ()
{
// Ideal case: either no table width is specified, or everything fits without
// wrapping into mTableWidth.
std::vector <int> ideal = mMaxDataWidth;
int width = 0;
int countFlexible = 0;
for (size_t c = 0; c < mColumns.size (); ++c)
{
if (mSpecifiedWidth[c] == flexible)
++countFlexible;
else if (mSpecifiedWidth[c] > 0)
ideal[c] = mSpecifiedWidth[c];
width += mColumnPadding[c] +
ideal[c] +
mColumnPadding[c] +
(c > 0 ? mIntraPadding : 0);
}
if (!mTableWidth || width < mTableWidth)
{
mCalculatedWidth = ideal;
return;
}
// Try again, with available space divided among the flexible columns.
if (countFlexible)
{
ideal = mMaxDataWidth;
width = 0;
for (size_t c = 0; c < mColumns.size (); ++c)
{
if (mSpecifiedWidth[c] > 0)
ideal[c] = mSpecifiedWidth[c];
else if (mSpecifiedWidth[c] == flexible)
{
ideal[c] = 0;
}
width += mColumnPadding[c] +
ideal[c] +
mColumnPadding[c] +
(c > 0 ? mIntraPadding : 0);
}
int available = mTableWidth - width;
if (width < mTableWidth) // if there is room to wiggle in
{
int shared = available / countFlexible;
int remainder = available % countFlexible;
int lastFlexible = mColumns.size () - 1;
for (size_t c = 0; c < mColumns.size (); ++c)
{
if (mSpecifiedWidth[c] == flexible)
{
lastFlexible = c;
ideal[c] += shared;
}
}
// Remainder goes to last column.
ideal[lastFlexible] += remainder;
mCalculatedWidth = ideal;
return;
}
else
{
// The fallback position is to assume no width was specified, and just
// calculate widths accordingly.
mTableWidth = 0;
calculateColumnWidths ();
return;
}
}
// Try again, treating minimum columns as flexible.
// std::cout << "# no flexible columns. Now what?\n";
}
////////////////////////////////////////////////////////////////////////////////
Table::just Table::getJustification (const int row, const int col)
{
if (mJustification.find (col) != mJustification.end ())
return mJustification[col];
return left;
}
////////////////////////////////////////////////////////////////////////////////
Table::just Table::getHeaderJustification (int col)
{
return mJustification.find (col) != mJustification.end () ? mJustification[col]
: left;
}
////////////////////////////////////////////////////////////////////////////////
// data One Data to be rendered
// width 8 Max data width for column/specified width
// padding 1 Extra padding around data
// intraPadding 0 Extra padding between columns only
// justification right Alignment withing padding
//
// Returns:
// " One "
// One data
// ^ ^ padding
// ^ intraPadding
// ^^^^^^^^ width
// ^ ^ fg/bg
//
const std::string Table::formatHeader (
int col,
int width,
int padding)
{
assert (width > 0);
std::string data = mColumns[col];
Color c = getHeaderUnderline (col);
int gap = width - utf8_length (data);
std::string pad = std::string (padding, ' ');
// TODO When the following is replaced by:
// std::string postJust = std::string (gap, ' ');
// two unit tests fail.
std::string postJust = "";
for (int i = 0; i < gap; ++i)
postJust += " ";
std::string intraPad = "";
if (col < (signed) mColumns.size () - 1)
intraPad = std::string (getIntraPadding (), ' ');
return c.colorize (pad + data + postJust + pad) + intraPad;
}
////////////////////////////////////////////////////////////////////////////////
// data One Data to be rendered
// width 8 Max data width for column/specified width
// padding 1 Extra padding around data
// intraPadding 0 Extra padding between columns only
// justification right Alignment withing padding
//
// Returns:
// "------- "
// ------- data
// ^ ^ padding
// ^ intraPadding
// ^^^^^^^^ width
// ^ ^ fg/bg
//
const std::string Table::formatHeaderDashedUnderline (
int col,
int width,
int padding)
{
assert (width > 0);
Color c = getHeaderUnderline (col);
std::string data = std::string (width, '-');
std::string pad = std::string (padding, ' ');
std::string intraPad = "";
// Place the value within the available space - justify.
if (col < (signed) mColumns.size () - 1)
intraPad = std::string (getIntraPadding (), ' ');
return c.colorize (pad + data + pad) + intraPad;
}
////////////////////////////////////////////////////////////////////////////////
void Table::formatCell (
const int index,
const int row,
const int col,
const int width,
const int padding,
std::vector <std::string>& lines,
std::string& blank)
{
assert (width > 0);
Color c = getColor (index, row, col);
just justification = getJustification (row, col);
std::string data = getCell (row, col);
std::string pad = std::string (padding, ' ');
std::string intraPad = "";
if (col < (signed) mColumns.size () - 1)
intraPad = std::string (getIntraPadding (), ' ');
// Break the text into chunks of width characters.
std::string preJust;
std::string postJust;
std::vector <std::string> chunks;
wrapText (chunks, data, width);
for (size_t chunk = 0; chunk < chunks.size (); ++chunk)
{
// Place the data within the available space - justify.
int gap = width - utf8_length (chunks[chunk]);
preJust = "";
postJust = "";
if (justification == left)
postJust = std::string (gap, ' ');
else if (justification == right)
preJust = std::string (gap, ' ');
else if (justification == center)
{
preJust = std::string (gap / 2, ' ');
postJust = std::string (gap - preJust.length (), ' ');
}
lines.push_back (c.colorize (pad + preJust + chunks[chunk] + postJust + pad + intraPad));
}
// The blank is used to vertically pad cells that have blank lines.
pad = std::string (width, ' ');
blank = c.colorize (pad + intraPad);
}
////////////////////////////////////////////////////////////////////////////////
void Table::setDateFormat (const std::string& dateFormat)
{
mDateFormat = dateFormat;
}
////////////////////////////////////////////////////////////////////////////////
void Table::setReportName (const std::string& reportName)
{
mReportName = reportName;
}
////////////////////////////////////////////////////////////////////////////////
int Table::rowCount ()
{
return mRows;
}
////////////////////////////////////////////////////////////////////////////////
int Table::columnCount ()
{
return mColumns.size ();
}
////////////////////////////////////////////////////////////////////////////////
// Essentially a static implementation of a dynamic operator<.
bool sort_compare (int left, int right)
{
for (size_t c = 0; c < table->mSortColumns.size (); ++c)
{
int column = table->mSortColumns[c];
Table::order sort_type = table->mSortOrder[column];
Grid::Cell* cell_left = table->mData.byRow (left, column);
Grid::Cell* cell_right = table->mData.byRow (right, column);
// Equally NULL - next column.
if (cell_left == NULL && cell_right == NULL)
continue;
// Equal - next column
if (cell_left && cell_right && *cell_left == *cell_right)
continue;
// Note: Table::ascendingDueDate is not represented here because it is not
// possible to have a NULL due date, only a blank "".
// nothing < something.
if (cell_left == NULL && cell_right != NULL)
return (sort_type == Table::ascendingNumeric ||
sort_type == Table::ascendingCharacter ||
sort_type == Table::ascendingPriority ||
sort_type == Table::ascendingDate ||
sort_type == Table::ascendingPeriod) ? true : false;
// something > nothing.
if (cell_left != NULL && cell_right == NULL)
return (sort_type == Table::ascendingNumeric ||
sort_type == Table::ascendingCharacter ||
sort_type == Table::ascendingPriority ||
sort_type == Table::ascendingDate ||
sort_type == Table::ascendingPeriod) ? false : true;
// Differing data - do a proper comparison.
if (cell_left && cell_right)
{
switch (sort_type)
{
case Table::ascendingNumeric:
return (float)*cell_left < (float)*cell_right ? true : false;
break;
case Table::descendingNumeric:
return (float)*cell_left < (float)*cell_right ? false : true;
break;
case Table::ascendingCharacter:
return (std::string)*cell_left < (std::string)*cell_right ? true : false;
break;
case Table::descendingCharacter:
return (std::string)*cell_left < (std::string)*cell_right ? false : true;
break;
case Table::ascendingDate:
{
// something > nothing.
if ((std::string)*cell_left != "" && (std::string)*cell_right == "")
return false;
// nothing < something.
else if ((std::string)*cell_left == "" && (std::string)*cell_right != "")
return true;
else
{
Date dl ((std::string)*cell_left, table->mDateFormat);
Date dr ((std::string)*cell_right, table->mDateFormat);
return dl < dr ? true : false;
}
}
break;
case Table::descendingDate:
{
// something > nothing.
if ((std::string)*cell_left != "" && (std::string)*cell_right == "")
return true;
// nothing < something.
else if ((std::string)*cell_left == "" && (std::string)*cell_right != "")
return false;
else
{
Date dl ((std::string)*cell_left, table->mDateFormat);
Date dr ((std::string)*cell_right, table->mDateFormat);
return dl < dr ? false : true;
}
}
break;
case Table::ascendingDueDate:
{
// something > nothing.
if ((std::string)*cell_left != "" && (std::string)*cell_right == "")
return true;
// nothing < something.
else if ((std::string)*cell_left == "" && (std::string)*cell_right != "")
return false;
else
{
std::string format = context.config.get ("report." + table->mReportName + ".dateformat");
if (format == "")
format = context.config.get ("dateformat.report");
if (format == "")
format = context.config.get ("dateformat");
Date dl ((std::string)*cell_left, format);
Date dr ((std::string)*cell_right, format);
return dl < dr ? true : false;
}
}
break;
case Table::descendingDueDate:
{
// something > nothing.
if ((std::string)*cell_left != "" && (std::string)*cell_right == "")
return true;
// nothing < something.
else if ((std::string)*cell_left == "" && (std::string)*cell_right != "")
return false;
else
{
std::string format = context.config.get ("report." + table->mReportName + ".dateformat");
if (format == "")
format = context.config.get ("dateformat.report");
if (format == "")
format = context.config.get ("dateformat");
Date dl ((std::string)*cell_left, format);
Date dr ((std::string)*cell_right, format);
return dl < dr ? false : true;
}
}
break;
case Table::ascendingPriority:
if (((std::string)*cell_left == "" && (std::string)*cell_right != "") ||
((std::string)*cell_left == "L" && ((std::string)*cell_right == "M" || (std::string)*cell_right == "H")) ||
((std::string)*cell_left == "M" && (std::string)*cell_right == "H"))
return true;
else
return false;
break;
case Table::descendingPriority:
if (((std::string)*cell_left != "" && (std::string)*cell_right == "") ||
((std::string)*cell_left == "M" && (std::string)*cell_right == "L") ||
((std::string)*cell_left == "H" && ((std::string)*cell_right == "L" || (std::string)*cell_right == "M")))
return true;
else
return false;
break;
case Table::ascendingPeriod:
if ((std::string)*cell_left == "" && (std::string)*cell_right != "")
return true;
else if ((std::string)*cell_left != "" && (std::string)*cell_right == "")
return false;
else
return Duration ((std::string)*cell_left) < Duration ((std::string)*cell_right) ? true : false;
break;
case Table::descendingPeriod:
if ((std::string)*cell_left != "" && (std::string)*cell_right == "")
return false;
else if ((std::string)*cell_left == "" && (std::string)*cell_right != "")
return true;
else
return Duration ((std::string)*cell_left) < Duration ((std::string)*cell_right) ? false : true;
break;
}
}
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
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.
std::string output;
std::string underline;
for (size_t col = 0; col < mColumns.size (); ++col)
{
output += formatHeader (
col,
mCalculatedWidth[col],
mColumnPadding[col]);
if (mDashedUnderline)
underline += formatHeaderDashedUnderline (
col,
mCalculatedWidth[col],
mColumnPadding[col]);
}
output.erase (output.find_last_not_of (" ") + 1);
output += "\n";
++renderedlines;
if (underline.length ())
{
output += underline + "\n";
++renderedlines;
}
// Determine row order, according to sort options.
std::vector <int> order;
{
Timer t ("Table::render/sort");
for (int row = 0; row < mRows; ++row)
order.push_back (row);
// Only sort if necessary.
if (mSortColumns.size ())
{
table = this; // Substitute for 'this' in the static 'sort_compare'.
std::stable_sort (order.begin (), order.end (), sort_compare);
}
}
// If a non-zero maxrows is specified, then it limits the number of rows of
// the table that are rendered.
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.
Timer t2 ("Table::render/compose");
for (int row = 0; row < limitrows; ++row)
{
std::vector <std::vector <std::string> > columns;
std::vector <std::string> blanks;
size_t maxHeight = 0;
for (size_t col = 0; col < mColumns.size (); ++col)
{
std::vector <std::string> lines;
std::string blank;
formatCell (
row,
order[row],
col,
mCalculatedWidth[col],
mColumnPadding[col],
lines,
blank);
columns.push_back (lines);
blanks.push_back (blank);
maxHeight = max (maxHeight, columns[col].size ());
}
if (maxHeight)
{
for (size_t lines = 0; lines < maxHeight; ++lines)
{
for (size_t col = 0; col < mColumns.size (); ++col)
if (lines < columns[col].size ())
output += columns[col][lines];
else
output += blanks[col];
// Trim right.
output.erase (output.find_last_not_of (" ") + 1);
output += "\n";
++renderedlines;
if (maxlines != 0 && renderedlines >= maxlines)
break;
}
}
else
{
// Trim right.
output.erase (output.find_last_not_of (" ") + 1);
output += "\n";
}
if (maxlines != 0 && renderedlines >= maxlines)
break;
}
return output;
}
////////////////////////////////////////////////////////////////////////////////

View file

@ -1,141 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2006 - 2011, Paul Beckingham, Federico Hernandez.
// 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
//
////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDED_TABLE
#define INCLUDED_TABLE
#include <map>
#include <vector>
#include <string>
#include "Color.h"
#include "Grid.h"
class Table
{
friend bool sort_compare (int, int);
public:
enum just {left, center, right};
enum order {ascendingNumeric,
ascendingCharacter,
ascendingPriority,
ascendingDate,
ascendingDueDate,
ascendingPeriod,
descendingNumeric,
descendingCharacter,
descendingPriority,
descendingDate,
descendingDueDate,
descendingPeriod};
enum sizing {minimum = -1, flexible = 0};
Table ();
virtual ~Table ();
Table (const Table&);
Table& operator= (const Table&);
void setTableAlternateColor (const Color&);
void setTablePadding (int);
void setTableIntraPadding (int);
void setTableWidth (int);
void setTableDashedUnderline ();
int addColumn (const std::string&);
void setColumnUnderline (int);
void setColumnPadding (int, int);
void setColumnWidth (int, int);
void setColumnWidth (int, sizing);
void setColumnJustification (int, just);
void setColumnCommify (int);
void sortOn (int, order);
int addRow ();
void setRowColor (int, const Color&);
void addCell (int, int, const std::string&);
void addCell (int, int, char);
void addCell (int, int, int);
void addCell (int, int, float);
void addCell (int, int, double);
void setCellColor (int, int, const Color&);
void setDateFormat (const std::string&);
void setReportName (const std::string&);
int rowCount ();
int columnCount ();
const std::string render (int maxrows = 0, int maxlines = 0);
private:
std::string getCell (const int, const int);
Color getColor (const int, const int, const int);
Color getHeaderUnderline (const int);
int getPadding (const int);
int getIntraPadding ();
void calculateColumnWidths ();
just getJustification (const int, const int);
just getHeaderJustification (const int);
const std::string formatHeader (const int, const int, const int);
const std::string formatHeaderDashedUnderline (const int, const int, const int);
void formatCell (const int, const int, const int, const int, const int, std::vector <std::string>&, std::string&);
private:
std::vector <std::string> mColumns;
int mRows;
int mIntraPadding;
std::map <std::string, Color> mColor;
std::map <std::string, Color> mUnderline;
bool mDashedUnderline;
Color alternate;
// Padding...
int mTablePadding;
std::vector <int> mColumnPadding;
// Width...
int mTableWidth;
std::vector <int> mSpecifiedWidth;
std::vector <int> mMaxDataWidth;
std::vector <int> mCalculatedWidth;
std::map <int, just> mJustification;
std::map <int, bool> mCommify;
Grid mData;
// Sorting...
std::vector <int> mSortColumns;
std::map <int, order> mSortOrder;
// Misc...
std::string mDateFormat;
std::string mReportName;
};
#endif
////////////////////////////////////////////////////////////////////////////////

View file

@ -111,13 +111,18 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
int global_min = utf8_length ((*i)->getLabel ());
int global_ideal = global_min;
std::vector <Task>::iterator d;
for (d = data.begin (); d != data.end (); ++d)
for (int s = 0; s < sequence.size (); ++s)
{
if (s >= _truncate_lines && _truncate_lines != 0)
break;
if (s >= _truncate_rows && _truncate_rows != 0)
break;
// Determine minimum and ideal width for this column.
int min;
int ideal;
(*i)->measure (*d, min, ideal);
(*i)->measure (data[sequence[s]], min, ideal);
if (min > global_min) global_min = min;
if (ideal > global_ideal) global_ideal = ideal;
@ -162,14 +167,17 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
overage -= sum_minimal;
// Spread 'overage' among columns where width[i] < ideal[i]
while (overage)
bool needed = true;
while (overage && needed)
{
needed = false;
for (int i = 0; i < _columns.size () && overage; ++i)
{
if (widths[i] < ideal[i])
{
++widths[i];
--overage;
needed = true;
}
}
}

View file

@ -222,6 +222,10 @@ std::string ViewText::render ()
bool odd = (row % 2) ? true : false;
Color row_color = odd ? _odd : _even;
// TODO row_color.blend (provided color);
// TODO Problem: colors for columns are specified, not rows,
// therefore there are only cell colors, not intra colors.
Color cell_color;
for (int col = 0; col < _columns.size (); ++col)
{

View file

@ -43,6 +43,7 @@
#include "util.h"
#include "main.h"
#include "Transport.h"
#include "ViewText.h"
#include "../cmake.h"
#ifdef HAVE_COMMIT
#include "../commit.h"
@ -259,41 +260,24 @@ int handleProjects (std::string& outs)
if (unique.size ())
{
// Render a list of project names from the map.
Table table;
table.addColumn ("Project");
table.addColumn ("Tasks");
table.addColumn ("Pri:None");
table.addColumn ("Pri:L");
table.addColumn ("Pri:M");
table.addColumn ("Pri:H");
if (context.color () && context.config.getBoolean ("fontunderline"))
{
table.setColumnUnderline (0);
table.setColumnUnderline (1);
table.setColumnUnderline (2);
table.setColumnUnderline (3);
table.setColumnUnderline (4);
table.setColumnUnderline (5);
}
else
table.setTableDashedUnderline ();
table.setColumnJustification (1, Table::right);
table.setColumnJustification (2, Table::right);
table.setColumnJustification (3, Table::right);
table.setColumnJustification (4, Table::right);
table.setColumnJustification (5, Table::right);
ViewText view;
view.width (context.getWidth ());
view.add (Column::factory ("string", "Project"));
view.add (Column::factory ("string.right", "Tasks"));
view.add (Column::factory ("string.right", "Pri:None"));
view.add (Column::factory ("string.right", "Pri:L"));
view.add (Column::factory ("string.right", "Pri:M"));
view.add (Column::factory ("string.right", "Pri:H"));
foreach (i, unique)
{
int row = table.addRow ();
table.addCell (row, 0, (i->first == "" ? "(none)" : i->first));
table.addCell (row, 1, i->second);
table.addCell (row, 2, none[i->first]);
table.addCell (row, 3, low[i->first]);
table.addCell (row, 4, medium[i->first]);
table.addCell (row, 5, high[i->first]);
int row = view.addRow ();
view.set (row, 0, (i->first == "" ? "(none)" : i->first));
view.set (row, 1, i->second);
view.set (row, 2, none[i->first]);
view.set (row, 3, low[i->first]);
view.set (row, 4, medium[i->first]);
view.set (row, 5, high[i->first]);
}
int number_projects = unique.size ();
@ -301,7 +285,7 @@ int handleProjects (std::string& outs)
--number_projects;
out << optionalBlankLine ()
<< table.render ()
<< view.render ()
<< optionalBlankLine ()
<< number_projects
<< (number_projects == 1 ? " project" : " projects")
@ -382,35 +366,28 @@ int handleTags (std::string& outs)
if (unique.size ())
{
// Render a list of tags names from the map.
Table table;
table.addColumn ("Tag");
table.addColumn ("Count");
if (context.color ())
{
table.setColumnUnderline (0);
table.setColumnUnderline (1);
}
table.setColumnJustification (1, Table::right);
ViewText view;
view.width (context.getWidth ());
view.add (Column::factory ("string", "Tag"));
view.add (Column::factory ("string.right", "Count"));
Color bold ("bold");
bool special = false;
foreach (i, unique)
{
int row = table.addRow ();
table.addCell (row, 0, i->first);
table.addCell (row, 1, i->second);
// Highlight the special tags.
if (context.color () && (i->first == "nocolor" ||
i->first == "nonag"))
{
table.setRowColor (row, bold);
}
special = (context.color () &&
(i->first == "nocolor" ||
i->first == "nonag" ||
i->first == "next")) ? true : false;
int row = view.addRow ();
view.set (row, 0, i->first, special ? bold : Color ());
view.set (row, 1, i->second, special ? bold : Color ());
}
out << optionalBlankLine ()
<< table.render ()
<< view.render ()
<< optionalBlankLine ()
<< unique.size ()
<< (unique.size () == 1 ? " tag" : " tags")
@ -1002,22 +979,18 @@ int handleVersion (std::string& outs)
// Create a table for the disclaimer.
int width = context.getWidth ();
Table disclaimer;
disclaimer.setTableWidth (width);
disclaimer.addColumn (" ");
disclaimer.setColumnWidth (0, Table::flexible);
disclaimer.setColumnJustification (0, Table::left);
disclaimer.addCell (disclaimer.addRow (), 0,
ViewText disclaimer;
disclaimer.width (width);
disclaimer.add (Column::factory ("string", ""));
disclaimer.set (disclaimer.addRow (), 0,
"Taskwarrior may be copied only under the terms of the GNU General Public "
"License, which may be found in the taskwarrior source kit.");
// Create a table for the URL.
Table link;
link.setTableWidth (width);
link.addColumn (" ");
link.setColumnWidth (0, Table::flexible);
link.setColumnJustification (0, Table::left);
link.addCell (link.addRow (), 0,
ViewText link;
link.width (width);
link.add (Column::factory ("string", ""));
link.set (link.addRow (), 0,
"Documentation for taskwarrior can be found using 'man task', 'man taskrc', "
"'man task-tutorial', 'man task-color', 'man task-sync', 'man task-faq' or at "
"http://taskwarrior.org");
@ -1061,7 +1034,9 @@ int handleVersion (std::string& outs)
#ifdef HAVE_LIBLUA
<< "Portions of this software Copyright (C) 1994 2008 Lua.org, PUC-Rio.\n"
#endif
<< "\n"
<< disclaimer.render ()
<< "\n"
<< link.render ()
<< "\n";
@ -1168,26 +1143,11 @@ int handleShow (std::string& outs)
if (context.config.get (*i) != default_config.get (*i))
default_values.push_back (*i);
// Create a table for output.
Table table;
table.setTableWidth (width);
table.setDateFormat (context.config.get ("dateformat"));
table.addColumn ("Config variable");
table.addColumn ("Value");
if (context.color ())
{
table.setColumnUnderline (0);
table.setColumnUnderline (1);
}
else
table.setTableDashedUnderline ();
table.setColumnWidth (0, Table::minimum);
table.setColumnWidth (1, Table::flexible);
table.setColumnJustification (0, Table::left);
table.setColumnJustification (1, Table::left);
table.sortOn (0, Table::ascendingCharacter);
// Create output view.
ViewText view;
view.width (width);
view.add (Column::factory ("string", "Config variable"));
view.add (Column::factory ("string", "Value"));
Color error ("bold white on red");
Color warning ("black on yellow");
@ -1206,25 +1166,22 @@ int handleShow (std::string& outs)
if (loc != std::string::npos)
{
int row = table.addRow ();
table.addCell (row, 0, *i);
table.addCell (row, 1, context.config.get (*i));
// Look for unrecognized.
if (context.color ())
{
if (std::find (unrecognized.begin (), unrecognized.end (), *i) != unrecognized.end ())
table.setRowColor (row, error);
Color color;
if (std::find (unrecognized.begin (), unrecognized.end (), *i) != unrecognized.end ())
color = error;
else if (std::find (default_values.begin (), default_values.end (), *i) != default_values.end ())
color = warning;
else if (std::find (default_values.begin (), default_values.end (), *i) != default_values.end ())
table.setRowColor (row, warning);
}
int row = view.addRow ();
view.set (row, 0, *i, color);
view.set (row, 1, context.config.get (*i), color);
}
}
out << "\n"
<< table.render ()
<< (table.rowCount () == 0 ? "No matching configuration variables.\n\n" : "\n");
<< view.render ()
<< (view.rows () == 0 ? "No matching configuration variables.\n\n" : "\n");
// Display the unrecognized variables.
if (unrecognized.size ())
@ -2374,17 +2331,10 @@ int handleColor (std::string& outs)
std::vector <std::string> all;
context.config.all (all);
Table table;
table.addColumn ("Color");
table.addColumn ("Definition");
if (context.color () && context.config.getBoolean ("fontunderline"))
{
table.setColumnUnderline (0);
table.setColumnUnderline (1);
}
else
table.setTableDashedUnderline ();
ViewText view;
view.width (context.getWidth ());
view.add (Column::factory ("string", "Color"));
view.add (Column::factory ("string", "Definition"));
foreach (item, all)
{
@ -2394,16 +2344,14 @@ int handleColor (std::string& outs)
*item != "color" &&
item->find ("color") == 0)
{
int row = table.addRow ();
table.addCell (row, 0, *item);
table.addCell (row, 1, context.config.get (*item));
table.setRowColor (row, context.config.get (*item));
Color color (context.config.get (*item));
int row = view.addRow ();
view.set (row, 0, *item, color);
view.set (row, 1, context.config.get (*item), color);
}
}
out << optionalBlankLine ()
<< table.render ()
<< optionalBlankLine ()
out << view.render ()
<< "\n";
}

View file

@ -36,7 +36,6 @@
#include <map>
#include <sys/types.h>
#include "Context.h"
#include "Table.h"
#include "Date.h"
#include "Color.h"
#include "../cmake.h"

View file

@ -42,7 +42,6 @@
#include <File.h>
#include <Date.h>
#include <Duration.h>
#include <Table.h>
#include <ViewText.h>
#include <text.h>
#include <util.h>
@ -787,6 +786,7 @@ int handleReportSummary (std::string& outs)
// Create a table for output.
ViewText view;
view.width (context.getWidth ());
view.add (Column::factory ("string", "Project"));
view.add (Column::factory ("string", "Remaining"));
view.add (Column::factory ("string", "Avg age"));
@ -900,31 +900,12 @@ int handleReportTimesheet (std::string& outs)
<< "\n";
// Render the completed table.
Table completed;
completed.setTableWidth (width);
completed.addColumn (" ");
completed.addColumn ("Project");
completed.addColumn ("Due");
completed.addColumn ("Description");
if (context.color () && context.config.getBoolean ("fontunderline"))
{
completed.setColumnUnderline (1);
completed.setColumnUnderline (2);
completed.setColumnUnderline (3);
}
else
completed.setTableDashedUnderline ();
completed.setColumnWidth (0, Table::minimum);
completed.setColumnWidth (1, Table::minimum);
completed.setColumnWidth (2, Table::minimum);
completed.setColumnWidth (3, Table::flexible);
completed.setColumnJustification (0, Table::left);
completed.setColumnJustification (1, Table::left);
completed.setColumnJustification (2, Table::right);
completed.setColumnJustification (3, Table::left);
ViewText completed;
completed.width (width);
completed.add (Column::factory ("string", " "));
completed.add (Column::factory ("string", "Project"));
completed.add (Column::factory ("string.right", "Due"));
completed.add (Column::factory ("string", "Description"));
std::vector <Task>::iterator task;
for (task = tasks.begin (); task != tasks.end (); ++task)
@ -935,56 +916,35 @@ int handleReportTimesheet (std::string& outs)
Date compDate (atoi (task->get ("end").c_str ()));
if (compDate >= start && compDate < end)
{
Color c (task->get ("fg") + " " + task->get ("bg"));
if (context.color ())
autoColorize (*task, c);
int row = completed.addRow ();
std::string format = context.config.get ("dateformat.report");
if (format == "")
format = context.config.get ("dateformat");
completed.addCell (row, 1, task->get ("project"));
completed.addCell (row, 2, getDueDate (*task, format));
completed.addCell (row, 3, getFullDescription (*task, "timesheet"));
if (context.color ())
{
Color c (task->get ("fg") + " " + task->get ("bg"));
autoColorize (*task, c);
completed.setRowColor (row, c);
}
completed.set (row, 1, task->get ("project"), c);
completed.set (row, 2, getDueDate (*task, format), c);
completed.set (row, 3, getFullDescription (*task, "timesheet"), c);
}
}
}
out << " Completed (" << completed.rowCount () << " tasks)\n";
out << " Completed (" << completed.rows () << " tasks)\n";
if (completed.rowCount ())
if (completed.rows ())
out << completed.render ()
<< "\n";
// Now render the started table.
Table started;
started.setTableWidth (width);
started.addColumn (" ");
started.addColumn ("Project");
started.addColumn ("Due");
started.addColumn ("Description");
ViewText started;
started.width (width);
started.add (Column::factory ("string", " "));
started.add (Column::factory ("string", "Project"));
started.add (Column::factory ("string.right", "Due"));
started.add (Column::factory ("string", "Description"));
if (context.color () && context.config.getBoolean ("fontunderline"))
{
started.setColumnUnderline (1);
started.setColumnUnderline (2);
started.setColumnUnderline (3);
}
else
started.setTableDashedUnderline ();
started.setColumnWidth (0, Table::minimum);
started.setColumnWidth (1, Table::minimum);
started.setColumnWidth (2, Table::minimum);
started.setColumnWidth (3, Table::flexible);
started.setColumnJustification (0, Table::left);
started.setColumnJustification (1, Table::left);
started.setColumnJustification (2, Table::right);
started.setColumnJustification (3, Table::left);
for (task = tasks.begin (); task != tasks.end (); ++task)
{
// If task started within range, but not completed withing range.
@ -994,27 +954,25 @@ int handleReportTimesheet (std::string& outs)
Date startDate (atoi (task->get ("start").c_str ()));
if (startDate >= start && startDate < end)
{
Color c (task->get ("fg") + " " + task->get ("bg"));
if (context.color ())
autoColorize (*task, c);
int row = started.addRow ();
std::string format = context.config.get ("dateformat.report");
if (format == "")
format = context.config.get ("dateformat");
started.addCell (row, 1, task->get ("project"));
started.addCell (row, 2, getDueDate (*task, format));
started.addCell (row, 3, getFullDescription (*task, "timesheet"));
started.set (row, 1, task->get ("project"), c);
started.set (row, 2, getDueDate (*task, format), c);
started.set (row, 3, getFullDescription (*task, "timesheet"), c);
if (context.color ())
{
Color c (task->get ("fg") + " " + task->get ("bg"));
autoColorize (*task, c);
started.setRowColor (row, c);
}
}
}
}
out << " Started (" << started.rowCount () << " tasks)\n";
out << " Started (" << started.rows () << " tasks)\n";
if (started.rowCount ())
if (started.rows ())
out << started.render ()
<< "\n\n";
@ -1035,9 +993,6 @@ std::string renderMonths (
std::vector <Task>& all,
int monthsPerLine)
{
Table table;
table.setDateFormat (context.config.get ("dateformat"));
// What day of the week does the user consider the first?
int weekStart = Date::dayOfWeek (context.config.get ("weekstart"));
if (weekStart != 0 && weekStart != 1)
@ -1045,64 +1000,41 @@ std::string renderMonths (
"only contain 'Sunday' or 'Monday'.");
// Build table for the number of months to be displayed.
ViewText view;
view.width (context.getWidth ());
for (int i = 0 ; i < (monthsPerLine * 8); i += 8)
{
if (weekStart == 1)
{
table.addColumn (" ");
table.addColumn ("Mo");
table.addColumn ("Tu");
table.addColumn ("We");
table.addColumn ("Th");
table.addColumn ("Fr");
table.addColumn ("Sa");
table.addColumn ("Su");
view.add (Column::factory ("string", " "));
view.add (Column::factory ("string", "Mo"));
view.add (Column::factory ("string", "Tu"));
view.add (Column::factory ("string", "We"));
view.add (Column::factory ("string", "Th"));
view.add (Column::factory ("string", "Fr"));
view.add (Column::factory ("string", "Sa"));
view.add (Column::factory ("string", "Su"));
}
else
{
table.addColumn (" ");
table.addColumn ("Su");
table.addColumn ("Mo");
table.addColumn ("Tu");
table.addColumn ("We");
table.addColumn ("Th");
table.addColumn ("Fr");
table.addColumn ("Sa");
view.add (Column::factory ("string", " "));
view.add (Column::factory ("string", "Su"));
view.add (Column::factory ("string", "Mo"));
view.add (Column::factory ("string", "Tu"));
view.add (Column::factory ("string", "We"));
view.add (Column::factory ("string", "Th"));
view.add (Column::factory ("string", "Fr"));
view.add (Column::factory ("string", "Sa"));
}
if (context.color () && context.config.getBoolean ("fontunderline"))
{
table.setColumnUnderline (i + 1);
table.setColumnUnderline (i + 2);
table.setColumnUnderline (i + 3);
table.setColumnUnderline (i + 4);
table.setColumnUnderline (i + 5);
table.setColumnUnderline (i + 6);
table.setColumnUnderline (i + 7);
}
else
table.setTableDashedUnderline ();
table.setColumnJustification (i + 0, Table::right);
table.setColumnJustification (i + 1, Table::right);
table.setColumnJustification (i + 2, Table::right);
table.setColumnJustification (i + 3, Table::right);
table.setColumnJustification (i + 4, Table::right);
table.setColumnJustification (i + 5, Table::right);
table.setColumnJustification (i + 6, Table::right);
table.setColumnJustification (i + 7, Table::right);
// This creates a nice gap between the months.
table.setColumnWidth (i + 0, 4);
}
// At most, we need 6 rows.
table.addRow ();
table.addRow ();
table.addRow ();
table.addRow ();
table.addRow ();
table.addRow ();
view.addRow ();
view.addRow ();
view.addRow ();
view.addRow ();
view.addRow ();
view.addRow ();
// Set number of days per month, months to render, and years to render.
std::vector<int> years;
@ -1150,11 +1082,7 @@ std::string renderMonths (
int woy = temp.weekOfYear (weekStart);
if (context.config.getBoolean ("displayweeknumber"))
{
table.addCell (row, (8 * mpl), woy);
if (context.color ())
table.setCellColor (row, (8 * mpl), color_weeknumber);
}
view.set (row, (8 * mpl), woy, color_weeknumber);
// Calculate column id.
int thisCol = dow + // 0 = Sunday
@ -1164,7 +1092,7 @@ std::string renderMonths (
if (thisCol == (8 * mpl))
thisCol += 7;
table.addCell (row, thisCol, d);
view.set (row, thisCol, d);
if (context.color ())
{
@ -1237,7 +1165,8 @@ std::string renderMonths (
}
}
}
table.setCellColor (row, thisCol, cellColor);
// TODO Solve this.
// table.setCellColor (row, thisCol, cellColor);
}
// Check for end of week, and...
@ -1249,7 +1178,7 @@ std::string renderMonths (
}
}
return table.render ();
return view.render ();
}
////////////////////////////////////////////////////////////////////////////////
@ -1567,32 +1496,18 @@ int handleReportCalendar (std::string& outs)
std::vector <std::string> holidays;
context.config.all (holidays);
Table holTable;
holTable.setTableWidth (context.getWidth ());
holTable.addColumn ("Date");
holTable.addColumn ("Holiday");
holTable.sortOn (0, Table::ascendingDueDate);
ViewText holTable;
holTable.width (context.getWidth ());
holTable.add (Column::factory ("string", "Date"));
holTable.add (Column::factory ("string", "Holiday"));
if (context.color () && context.config.getBoolean ("fontunderline"))
{
holTable.setColumnUnderline (0);
holTable.setColumnUnderline (1);
}
else
holTable.setTableDashedUnderline ();
holTable.setColumnWidth (0, Table::minimum);
holTable.setColumnWidth (1, Table::flexible);
holTable.setColumnJustification (0, Table::left);
holTable.setColumnJustification (1, Table::left);
foreach (hol, holidays)
if (hol->substr (0, 8) == "holiday.")
if (hol->substr (hol->size () - 4) == "name")
std::vector <std::string>::iterator it;
for (it = holidays.begin (); it != holidays.end (); ++it)
if (it->substr (0, 8) == "holiday.")
if (it->substr (it->size () - 4) == "name")
{
std::string holName = context.config.get ("holiday." + hol->substr (8, hol->size () - 13) + ".name");
std::string holDate = context.config.get ("holiday." + hol->substr (8, hol->size () - 13) + ".date");
std::string holName = context.config.get ("holiday." + it->substr (8, it->size () - 13) + ".name");
std::string holDate = context.config.get ("holiday." + it->substr (8, it->size () - 13) + ".date");
Date hDate (holDate.c_str (), context.config.get ("dateformat.holiday"));
if (date_after < hDate && hDate < date_before)
@ -1606,8 +1521,8 @@ int handleReportCalendar (std::string& outs)
format = context.config.get ("dateformat");
int row = holTable.addRow ();
holTable.addCell (row, 0, hDate.toString (format));
holTable.addCell (row, 1, holName);
holTable.set (row, 0, hDate.toString (format));
holTable.set (row, 1, holName);
}
}

1
test/.gitignore vendored
View file

@ -12,7 +12,6 @@ dom.t
duration.t
file.t
filt.t
grid.t
json.t
lisp.t
list.t

View file

@ -6,8 +6,8 @@ include_directories (${CMAKE_SOURCE_DIR}/src
${TASK_INCLUDE_DIRS})
set (test_SRCS att.t autocomplete.t cmd.t color.t config.t date.t directory.t
dom.t duration.t file.t filt.t grid.t json.t list.t nibbler.t
path.t record.t rectangle.t rx.t seq.t subst.t t.benchmark.t t.t
dom.t duration.t file.t filt.t json.t list.t nibbler.t path.t
record.t rectangle.t rx.t seq.t subst.t t.benchmark.t t.t
taskmod.t tdb.t tdb2.t text.t tree.t tree2.t uri.t util.t
variant.t view.t)

View file

@ -1,69 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2006 - 2011, Paul Beckingham, Federico Hernandez.
// 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
//
////////////////////////////////////////////////////////////////////////////////
#include <Context.h>
#include <Grid.h>
#include <test.h>
Context context;
int main (int argc, char** argv)
{
UnitTest ut (30);
Grid g;
ut.is ((int) g.width (), 0, "Zero width for uninitialized grid");
ut.is ((int) g.height (), 0, "Zero height for uninitialized grid");
g.add (2, 2, false);
ut.is ((int) g.width (), 3, "Width of 3 columns");
ut.is ((int) g.height (), 3, "Height of 3 rows");
Grid g2;
g2.add (0, 1, "value");
g2.add (1, 0, "value");
ut.is ((int) g2.width (), 2, "Width of 2 columns");
ut.is ((int) g2.height (), 2, "Height of 2 rows");
ut.is (g2.byRow (0, 0), NULL, "Gap at 0,0");
ut.ok (g2.byRow (0, 1), "Cell at 0,0");
ut.ok (g2.byRow (1, 0), "Cell at 0,0");
ut.is (g2.byRow (1, 1), NULL, "Gap at 1,1");
Grid g3;
for (int i = 0; i < 14; ++i)
g3.add (i / 4, i % 4, "value");
for (int i = 0; i < 20; ++i)
if (i < 14)
ut.ok (g3.byRow (i / 4, i % 4), "g3 good cell");
else
ut.is (g3.byRow (i / 4, i % 4), NULL, "g3 missing cell");
return 0;
}
////////////////////////////////////////////////////////////////////////////////