From cbecec263a51159a73b51040584c485355004fdc Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Sat, 20 Jun 2009 15:37:01 -0400 Subject: [PATCH] Enhancement - all attribute modifiers working - Implemented before, below, under, after, above, over. --- src/Att.cpp | 87 +++++++++++++++++++++++++++++++++++++----------- src/Att.h | 2 +- src/Context.cpp | 29 +++++++++++----- src/Duration.cpp | 6 ++++ src/Duration.h | 1 + 5 files changed, 97 insertions(+), 28 deletions(-) diff --git a/src/Att.cpp b/src/Att.cpp index f72af973b..cde0cc6c0 100644 --- a/src/Att.cpp +++ b/src/Att.cpp @@ -66,16 +66,16 @@ static const char* modifiableNames[] = // Synonyms on the same line. static const char* modifierNames[] = { - "before", "under", "below", - "after", "over", "above", + "before", "under", "below", + "after", "over", "above", "none", "any", - "is", - "isnt", "not", - "has", "contains", + "is", "equals", + "isnt", "not", + "has", "contains", "hasnt", - "startswith", - "endswith", + "startswith", "left", + "endswith", "right", }; #define NUM_INTERNAL_NAMES (sizeof (internalNames) / sizeof (internalNames[0])) @@ -385,7 +385,7 @@ bool Att::validMod (const std::string& mod) //////////////////////////////////////////////////////////////////////////////// // The type of an attribute is useful for modifier evaluation. -std::string type (const std::string& name) +std::string Att::type (const std::string& name) const { if (name == "due" || name == "until" || @@ -468,14 +468,17 @@ void Att::parse (Nibbler& n) // Record that does not have modifiers, but may have a value. bool Att::match (const Att& other) const { - // If there are no mods, just perform a straight compare on value. - if (mMod == "" && mValue != other.mValue) - return false; + // All matches are assumed to pass, any short-circuit on non-match. - // Assume a match, and short-circuit on mismatch. + // If there are no mods, just perform a straight compare on value. + if (mMod == "") + { + if (mValue != other.mValue) + return false; + } // is = equal. Nop. - else if (mMod == "is") // TODO i18n + else if (mMod == "is" || mMod == "equals") // TODO i18n { if (mValue != other.mValue) return false; @@ -503,7 +506,7 @@ bool Att::match (const Att& other) const } // startswith = first characters must match. - else if (mMod == "startswith") // TODO i18n + else if (mMod == "startswith" || mMod == "left") // TODO i18n { if (other.mValue.length () < mValue.length ()) return false; @@ -513,7 +516,7 @@ bool Att::match (const Att& other) const } // endswith = last characters must match. - else if (mMod == "endswith") // TODO i18n + else if (mMod == "endswith" || mMod == "right") // TODO i18n { if (other.mValue.length () < mValue.length ()) return false; @@ -541,15 +544,61 @@ bool Att::match (const Att& other) const // before = under = below = < else if (mMod == "before" || mMod == "under" || mMod == "below") { - // TODO Typed compare - return false; + std::string which = type (mName); + if (which == "duration") + { + Duration literal (mValue); + Duration variable ((time_t)::atoi (other.mValue.c_str ())); + if (!(variable < literal)) + return false; + } + else if (which == "date") + { + Date literal (mValue); + Date variable ((time_t)::atoi (other.mValue.c_str ())); + if (! (variable < literal)) + return false; + } + else if (which == "number") + { + if (::atoi (mValue.c_str ()) >= ::atoi (other.mValue.c_str ())) + return false; + } + else if (which == "text") + { + if (::strcmp (mValue.c_str (), other.mValue.c_str ()) <= 0) + return false; + } } // after = over = above = > else if (mMod == "after" || mMod == "over" || mMod == "above") { - // TODO Typed compare - return false; + std::string which = type (mName); + if (which == "duration") + { + Duration literal (mValue); + Duration variable ((time_t)::atoi (other.mValue.c_str ())); + if (! (variable > literal)) + return false; + } + else if (which == "date") + { + Date literal (mValue); + Date variable ((time_t)::atoi (other.mValue.c_str ())); + if (! (variable > literal)) + return false; + } + else if (which == "number") + { + if (::atoi (mValue.c_str ()) <= ::atoi (other.mValue.c_str ())) + return false; + } + else if (which == "text") + { + if (::strcmp (mValue.c_str (), other.mValue.c_str ()) >= 0) + return false; + } } return true; diff --git a/src/Att.h b/src/Att.h index deb906bea..324f85876 100644 --- a/src/Att.h +++ b/src/Att.h @@ -49,7 +49,7 @@ public: static bool validNameValue (const std::string&, const std::string&, const std::string&); static bool validNameValue (std::string&, std::string&, std::string&); static bool validMod (const std::string&); - static std::string type (const std::string&); + std::string type (const std::string&) const; void parse (const std::string&); void parse (Nibbler&); bool match (const Att&) const; diff --git a/src/Context.cpp b/src/Context.cpp index d60e1ad55..0168e0f9f 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -540,7 +540,7 @@ void Context::autoFilter (Task& t, Filter& f) foreach (att, t) { // Words are found in the description using the .has modifier. - if (att->first == "description") + if (att->first == "description" && att->second.mod () == "") { std::vector words; split (words, att->second.value (), ' '); @@ -552,7 +552,7 @@ void Context::autoFilter (Task& t, Filter& f) } // Projects are matched left-most. - else if (att->first == "project") + else if (att->first == "project" && att->second.mod () == "") { f.push_back (Att ("project", "startswith", att->second.value ())); debug ("auto filter: " + att->first + ".startswith:" + att->second.value ()); @@ -564,14 +564,27 @@ void Context::autoFilter (Task& t, Filter& f) { } - // 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") + // Every task has a unique uuid by default, and it shouldn't be included, + // because it is guaranteed to not match. + else if (att->first == "uuid") + { + } + + // The mechanism for filtering on tags is +/-. + else if (att->first == "tags") + { + } + + // Generic attribute matching. + else { f.push_back (att->second); - debug ("auto filter: " + att->first + ":" + att->second.value ()); + debug ("auto filter: " + + att->first + + (att->second.mod () != "" ? + ("." + att->second.mod () + ":") : + ":") + + att->second.value ()); } } diff --git a/src/Duration.cpp b/src/Duration.cpp index a6f0d716b..19f616a2f 100644 --- a/src/Duration.cpp +++ b/src/Duration.cpp @@ -39,6 +39,12 @@ Duration::Duration () { } +//////////////////////////////////////////////////////////////////////////////// +Duration::Duration (time_t input) +{ + mDays = input; +} + //////////////////////////////////////////////////////////////////////////////// Duration::Duration (const std::string& input) { diff --git a/src/Duration.h b/src/Duration.h index f8e4b0f68..e02bf30bf 100644 --- a/src/Duration.h +++ b/src/Duration.h @@ -34,6 +34,7 @@ class Duration { public: Duration (); // Default constructor + Duration (time_t); // Default constructor Duration (const std::string&); // Parse bool operator< (const Duration&); bool operator> (const Duration&);