- Sorting implemented using a non-relocatable task list, a stable
  sort, and type-specific comparisons.
This commit is contained in:
Paul Beckingham 2011-05-05 22:37:54 -04:00
parent 9268e552d8
commit 4584da8895
8 changed files with 176 additions and 174 deletions

View file

@ -18,9 +18,9 @@ set (task_SRCS API.cpp API.h Att.cpp Att.h Cmd.cpp Cmd.h Color.cpp Color.h
TransportCurl.h Tree.cpp Tree.h burndown.cpp command.cpp TransportCurl.h Tree.cpp Tree.h burndown.cpp command.cpp
custom.cpp dependency.cpp diag.cpp edit.cpp export.cpp custom.cpp dependency.cpp diag.cpp edit.cpp export.cpp
history.cpp i18n.h import.cpp interactive.cpp recur.cpp history.cpp i18n.h import.cpp interactive.cpp recur.cpp
report.cpp rules.cpp rx.cpp rx.h text.cpp text.h utf8.cpp utf8.h report.cpp rules.cpp rx.cpp rx.h sort.cpp text.cpp text.h
util.cpp util.h Uri.cpp Uri.h Variant.cpp Variant.h View.cpp utf8.cpp utf8.h util.cpp util.h Uri.cpp Uri.h Variant.cpp
View.h) Variant.h View.cpp View.h)
add_library (task STATIC ${task_SRCS}) add_library (task STATIC ${task_SRCS})
add_executable (task_executable main.cpp) add_executable (task_executable main.cpp)

View file

@ -819,6 +819,31 @@ void Context::parse (
} }
} }
////////////////////////////////////////////////////////////////////////////////
void Context::decomposeSortField (
const std::string& field,
std::string& key,
bool& ascending)
{
int length = field.length ();
if (field[length - 1] == '+')
{
ascending = true;
key = field.substr (0, length - 1);
}
else if (field[length - 1] == '-')
{
ascending = false;
key = field.substr (0, length - 1);
}
else
{
ascending = true;
key = field;
}
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Note: The reason some of these are commented out is because the ::clear // Note: The reason some of these are commented out is because the ::clear
// method is not really "clear" but "clear_some". Some members do not need to // method is not really "clear" but "clear_some". Some members do not need to

View file

@ -73,6 +73,7 @@ public:
std::string canonicalize (const std::string&) const; std::string canonicalize (const std::string&) const;
void disallowModification () const; void disallowModification () const;
void applyOverrides (const std::vector <std::string>&, std::vector <std::string>&); void applyOverrides (const std::vector <std::string>&, std::vector <std::string>&);
void decomposeSortField (const std::string&, std::string&, bool&);
private: private:
void loadCorrectConfigFile (); void loadCorrectConfigFile ();

View file

@ -211,7 +211,7 @@ std::string View::render (std::vector <Task>& data, std::vector <int>& sequence)
// Apply color rules to task. // Apply color rules to task.
Color rule_color; Color rule_color;
autoColorize (data[s], rule_color); autoColorize (data[sequence[s]], rule_color);
// Alternate rows based on |s % 2| // Alternate rows based on |s % 2|
bool odd = (s % 2) ? true : false; bool odd = (s % 2) ? true : false;
@ -221,7 +221,7 @@ std::string View::render (std::vector <Task>& data, std::vector <int>& sequence)
for (int c = 0; c < _columns.size (); ++c) for (int c = 0; c < _columns.size (); ++c)
{ {
cells.push_back (std::vector <std::string> ()); cells.push_back (std::vector <std::string> ());
_columns[c]->render (cells[c], data[s], widths[c], row_color); _columns[c]->render (cells[c], data[sequence[s]], widths[c], row_color);
if (cells[c].size () > max_lines) if (cells[c].size () > max_lines)
max_lines = cells[c].size (); max_lines = cells[c].size ();

View file

@ -161,6 +161,9 @@ std::string taskInfoDifferences (const Task&, const Task&);
std::string renderAttribute (const std::string&, const std::string&); std::string renderAttribute (const std::string&, const std::string&);
std::string feedback (const Task&, const Task&); std::string feedback (const Task&, const Task&);
// sort.cpp
void sort_tasks (std::vector <Task>&, std::vector <int>&, const std::string&);
// list template // list template
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
template <class T> bool listDiff (const T& left, const T& right) template <class T> bool listDiff (const T& left, const T& right)

View file

@ -25,211 +25,172 @@
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <algorithm>
#include <vector> #include <vector>
#include <string> #include <string>
#include <Context.h>
#include <Duration.h>
#include <Task.h> #include <Task.h>
#include <text.h>
static std::vector <Task>* data = NULL; extern Context context;
static std::vector <Task>* global_data = NULL;
static std::vector <std::string> global_keys;
static bool sort_compare (int, int);
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void view_sort ( void sort_tasks (
std::vector <Task>& data) std::vector <Task>& data,
std::vector <int>& order,
const std::string& keys)
{ {
global_data = &data;
// Split the key defs.
global_keys.clear ();
split (global_keys, keys, ',');
// Only sort if necessary.
if (order.size ())
std::stable_sort (order.begin (), order.end (), sort_compare);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Re-implementation, using direct Task access instead of data copies that // Re-implementation, using direct Task access instead of data copies that
// require re-parsing. // require re-parsing.
bool sort_compare (int left, int right) //
// Essentially a static implementation of a dynamic operator<.
static bool sort_compare (int left, int right)
{ {
int result;
std::string field;
bool ascending;
int left_number;
int right_number;
std::string left_string;
std::string right_string;
time_t left_date;
time_t right_date;
float left_real;
float right_real;
std::vector <std::string>::iterator k;
for (k = global_keys.begin (); k != global_keys.end (); ++k)
{
context.decomposeSortField (*k, field, ascending);
// Number.
if (field == "id")
{
left_number = (*global_data)[left].id;
right_number = (*global_data)[right].id;
if (left_number == right_number)
continue;
if (ascending)
return left_number < right_number;
return left_number > right_number;
}
// String.
else if (field == "description" ||
field == "depends" ||
field == "project" ||
field == "status" ||
field == "tags" ||
field == "uuid")
{
left_string = (*global_data)[left].get (field);
right_string = (*global_data)[right].get (field);
if (left_string == right_string)
continue;
if (ascending)
return left_string < right_string;
return left_string > right_string;
}
// Priority.
else if (field == "priority")
{
left_string = (*global_data)[left].get (field);
right_string = (*global_data)[right].get (field);
if (left_string == right_string)
continue;
if (ascending)
return (left_string == "" && right_string != "") ||
(left_string == "L" && (right_string == "M" || right_string == "H")) ||
(left_string == "M" && right_string == "H");
return (left_string != "" && right_string == "") ||
(left_string == "M" && right_string == "L") ||
(left_string == "H" && (right_string == "M" || right_string == "L"));
}
// Date.
else if (field == "due" ||
field == "end" ||
field == "entry" ||
field == "start" ||
field == "until" ||
field == "wait")
{
left_string = (*global_data)[left].get (field);
right_string = (*global_data)[right].get (field);
if (left_string == right_string)
continue;
left_date = atoi (left_string.c_str ());
right_date = atoi (right_string.c_str ());
if (ascending)
return left_date < right_date;
return left_date > right_date;
}
// Duration.
else if (field == "recur")
{
left_string = (*global_data)[left].get (field);
right_string = (*global_data)[right].get (field);
if (left_string == right_string)
continue;
Duration left_duration (left_string);
Duration right_duration (right_string);
if (ascending)
return left_duration < right_duration;
return left_duration > right_duration;
}
// Urgency.
else if (field == "urgency")
{
left_real = (*global_data)[left].urgency ();
right_real = (*global_data)[right].urgency ();
if (left_real == right_real)
continue;
if (ascending)
return left_real < right_real;
return left_real > right_real;
}
}
return false;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// 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;
}

View file

@ -220,7 +220,7 @@ int longestWord (const std::string& input)
{ {
int longest = 0; int longest = 0;
int length = 0; int length = 0;
std::string::size_type i; std::string::size_type i = 0;
int character; int character;
while (character = utf8_next_char (input, i)) while (character = utf8_next_char (input, i))

View file

@ -30,6 +30,7 @@
#include <Task.h> #include <Task.h>
#include <View.h> #include <View.h>
#include <test.h> #include <test.h>
#include <main.h>
Context context; Context context;
@ -71,15 +72,26 @@ int main (int argc, char** argv)
"depends:\"2a64f6e0-bf8e-430d-bf71-9ec3f0d9b661\"" "depends:\"2a64f6e0-bf8e-430d-bf71-9ec3f0d9b661\""
"]"); "]");
t2.id = 11; t2.id = 11;
Task t3 ("["
"status:\"pending\" "
"uuid:\"c44cb9c3-3fc0-483f-bfb2-3bf134f05554\" "
"description:\"Another description\" "
"project:\"Garden\" "
"]");
t3.id = 8;
std::vector <Task> data; std::vector <Task> data;
data.push_back (t1); data.push_back (t1);
data.push_back (t2); data.push_back (t2);
data.push_back (t3);
// Sequence of tasks. // Sequence of tasks.
std::vector <int> sequence; std::vector <int> sequence;
sequence.push_back (0); sequence.push_back (0);
sequence.push_back (1); sequence.push_back (1);
sequence.push_back (2);
sort_tasks (data, sequence, "description+,id-");
// Create colors. // Create colors.
Color header_color (Color (Color::yellow, Color::nocolor, false, false, false)); Color header_color (Color (Color::yellow, Color::nocolor, false, false, false));
@ -95,10 +107,10 @@ int main (int argc, char** argv)
view.add (Column::factory ("tags")); view.add (Column::factory ("tags"));
// view.add (Column::factory ("tags.indicator")); // view.add (Column::factory ("tags.indicator"));
view.add (Column::factory ("tags.count")); view.add (Column::factory ("tags.count"));
// view.add (Column::factory ("description")); view.add (Column::factory ("description"));
// view.add (Column::factory ("description.desc")); // view.add (Column::factory ("description.desc"));
// view.add (Column::factory ("description.truncated")); // view.add (Column::factory ("description.truncated"));
view.add (Column::factory ("description.oneline")); // view.add (Column::factory ("description.oneline"));
// view.add (Column::factory ("description.count")); // view.add (Column::factory ("description.count"));
// view.add (Column::factory ("depends")); // view.add (Column::factory ("depends"));
// view.add (Column::factory ("depends.count")); // view.add (Column::factory ("depends.count"));
@ -134,7 +146,7 @@ int main (int argc, char** argv)
// Render the view. // Render the view.
std::cout << view.render (data, sequence); std::cout << view.render (data, sequence);
t.is (view.lines (), 3, "View::lines == 3"); t.is (view.lines (), 5, "View::lines == 5");
} }
catch (std::string& e) catch (std::string& e)