From 97d732e5f7ece0992cc2ba79a8b4ab075587af29 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Tue, 16 Jun 2009 10:42:53 -0400 Subject: [PATCH] Enhancements - filters - The project attribute is now automatically filtered with project.startswith: to provide leftmost matching (ie subprojects). - Unmodifiable attributes (uuid, start ...) are now prevented from being updated if the command is designated as a "write" command. --- src/Att.cpp | 50 ++++++++++++++++++++++++++----------------------- src/Context.cpp | 22 +++++++++++++++------- 2 files changed, 42 insertions(+), 30 deletions(-) diff --git a/src/Att.cpp b/src/Att.cpp index 8b736b994..7255c95c3 100644 --- a/src/Att.cpp +++ b/src/Att.cpp @@ -44,7 +44,7 @@ static const char* internalNames[] = "end", "mask", "imask", -// "limit", + "limit", "status", "description", }; @@ -200,7 +200,6 @@ bool Att::validName (const std::string& name) } //////////////////////////////////////////////////////////////////////////////// -// TODO Obsolete bool Att::validModifiableName (const std::string& name) { for (unsigned int i = 0; i < NUM_MODIFIABLE_NAMES; ++i) @@ -294,6 +293,14 @@ bool Att::validNameValue ( mod = matches[0]; } + // Some attributes are intended to be private, unless the command is read- + // only, in which cased these are perfectly valid elements of a filter. + if (context.cmd.isWriteCommand () && + !validModifiableName (name)) + throw std::string ("\"") + + name + + "\" is not an attribute you may modify directly."; + // Thirdly, make sure the value has the expected form or values. if (name == "project") { @@ -333,13 +340,8 @@ bool Att::validNameValue ( Text::guessColor (value); } - else if (name == "due") - { - if (value != "") - Date (value); - } - - else if (name == "until") + else if (name == "due" || + name == "until") { if (value != "") Date (value); @@ -351,28 +353,30 @@ bool Att::validNameValue ( Duration (value); } - // TODO Not ready for prime time. else if (name == "limit") { if (value == "" || !digitsOnly (value)) throw std::string ("The '") + name + "' attribute must be an integer."; } - // Some attributes are intended to be private, unless the command is read- - // only, in which cased these are perfectly valid elements of a filter. - else if (name == "entry" || - name == "start" || - name == "end" || - name == "mask" || - name == "imask" || - name == "uuid" || - name == "status" || - name == "description") + else if (name == "status") { - if (context.cmd.isWriteCommand ()) + value = lowerCase (value); + + std::vector matches; + std::vector candidates; + candidates.push_back ("pending"); + candidates.push_back ("completed"); + candidates.push_back ("deleted"); + candidates.push_back ("recurring"); + autoComplete (value, candidates, matches); + + if (matches.size () == 1) + value = matches[0]; + else throw std::string ("\"") + - name + - "\" is not an attribute you may modify directly."; + value + + "\" is not a valid status. Use 'pending', 'completed', 'deleted' or 'recurring'."; } else diff --git a/src/Context.cpp b/src/Context.cpp index 0fc640450..c3578f9f6 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -494,7 +494,7 @@ void Context::constructFilter () { foreach (att, task) { - // TODO this doesn't work. + // Words are found in the description using the .has modifier. if (att->first == "description") { std::vector words; @@ -502,19 +502,27 @@ void Context::constructFilter () foreach (word, words) { filter.push_back (Att ("description", "has", *word)); - std::cout << "Context::constructFilter " << att->first << "=" << *word << std::endl; + std::cout << "# auto filter: " << att->first << ".has:" << *word << "" << std::endl; } } + // Projects are matched left-most. + else if (att->first == "project") + { + filter.push_back (Att ("project", "startswith", att->second.value ())); + std::cout << "# auto filter: " << att->first << ".startswith:" << att->second.value () << "" << std::endl; + } + // TODO Don't create a uuid for every task? // Every task has a unique uuid by default, and it shouldn't be included. // The mechanism for filtering on tags is +/-, not tags:foo which // means that there can only be one tag, "foo". else if (att->first != "uuid" && - att->first != "tags") + att->first != "tags" && + att->first != "project") { filter.push_back (att->second); - std::cout << "Context::constructFilter " << att->first << "=" << att->second.value () << std::endl; + std::cout << "# auto filter: " << att->first << ":" << att->second.value () << "" << std::endl; } } @@ -524,14 +532,14 @@ void Context::constructFilter () foreach (tag, tagAdditions) { filter.push_back (Att ("tags", "has", *tag)); - std::cout << "Context::constructFilter tags=+" << *tag << std::endl; + std::cout << "# auto filter: +" << *tag << "" << std::endl; } - // TODO Include tagRemovals. + // Include tagRemovals. foreach (tag, tagRemovals) { filter.push_back (Att ("tags", "hasnt", *tag)); - std::cout << "Context::constructFilter tags=-" << *tag << std::endl; + std::cout << "# auto filter: -" << *tag << "" << std::endl; } }