[office/tellico] /: Add entry filtering based on image size

Robby Stephenson null at kde.org
Wed May 20 20:01:40 BST 2020


Git commit fe9fc73158c2201fd634b867d3e79a970b4e9556 by Robby Stephenson.
Committed on 20/05/2020 at 19:00.
Pushed by rstephenson into branch 'master'.

Add entry filtering based on image size

The larger of the image dimension, whether width or height, is used for
comparison (under the idea the image would fit inside a square). Unit
tests are included.

GUI: New strings related to image size comparison

M  +2    -2    doc/fundamentals.docbook
M  +41   -18   src/filter.cpp
M  +3    -0    src/filter.h
M  +8    -0    src/gui/filterrulewidget.cpp
M  +2    -1    src/gui/filterrulewidget.h
M  +34   -0    src/tests/filtertest.cpp

https://invent.kde.org/office/tellico/commit/fe9fc73158c2201fd634b867d3e79a970b4e9556

diff --git a/doc/fundamentals.docbook b/doc/fundamentals.docbook
index 11176ef2..66c077f7 100644
--- a/doc/fundamentals.docbook
+++ b/doc/fundamentals.docbook
@@ -84,7 +84,7 @@ If an entry does not contain a value for the field being used to form the groups
 If any filters are saved for the collection, the <interface>Filter View</interface> becomes active. As you add or
 modify entries, they will automatically be checked against the filters and add or removed as
 appropriate. The filter may be modified by double-clicking on the item to open
-the <interface>Advanced Filter Dialog</interface>. Right-clicking gives you the option to delete it altogether.
+the <link linkend="filter-dialog"><interface>Advanced Filter Dialog</interface></link>. Right-clicking gives you the option to delete it altogether.
 </para>
 
 <screenshot>
@@ -396,7 +396,7 @@ find those that meet any of them. The <interface>Advanced Filter Dialog</interfa
 </screenshot>
 
 <para>
-Each row contains a single filter rule. Select the field to match in the first box in the row, then select a matching rule in the center. Rules may match values that contain a certain word, or are exactly equal. A regular expression may also be used to match entries, in which case the <guibutton>Edit...</guibutton> button becomes active for editing the expression, if &kregexpeditor; is installed on your computer. Finally, the word or string to match should be entered in the text box.
+Each row contains a single filter rule. Select the field to match in the first box in the row, then select a matching rule in the center. Rules may match values that contain a certain word, or are exactly equal. A regular expression may also be used to match entries, in which case the <guibutton>Edit...</guibutton> button becomes active for editing the expression, if &kregexpeditor; is installed on your computer. Finally, the word or string to match should be entered in the text box. For image fields, the image size can be used for filtering where the filter value is compared against the larger image dimension, whether width or height.
 </para>
 
 <para>
diff --git a/src/filter.cpp b/src/filter.cpp
index 1c28dd2e..0bb701cb 100644
--- a/src/filter.cpp
+++ b/src/filter.cpp
@@ -25,10 +25,14 @@
 #include "filter.h"
 #include "entry.h"
 #include "utils/string_utils.h"
+#include "images/imageinfo.h"
+#include "images/imagefactory.h"
 #include "tellico_debug.h"
 
 #include <QRegExp>
 
+#include <functional>
+
 using Tellico::Filter;
 using Tellico::FilterRule;
 
@@ -87,6 +91,11 @@ bool FilterRule::equals(Tellico::Data::EntryPtr entry_) const {
         return true;
       }
     }
+  } else if(entry_->collection()->hasField(m_fieldName) &&
+            entry_->collection()->fieldByName(m_fieldName)->type() == Data::Field::Image) {
+    // this is just for image size comparison, all other number comparisons are ok
+    // falling back to the string comparison after this
+    return numberCompare(entry_, std::equal_to<double>());
   } else {
     return m_pattern.compare(entry_->field(m_fieldName), Qt::CaseInsensitive) == 0 ||
            (entry_->collection()->hasField(m_fieldName) &&
@@ -200,27 +209,11 @@ bool FilterRule::after(Tellico::Data::EntryPtr entry_) const {
 }
 
 bool FilterRule::lessThan(Tellico::Data::EntryPtr entry_) const {
-  // empty field name means search all
-  // but the rule widget should limit this function to number fields only
-  if(m_fieldName.isEmpty()) {
-    return false;
-  }
-  const double pattern = m_patternVariant.toDouble();
-  bool ok = false;
-  const double value = entry_->field(m_fieldName).toDouble(&ok);
-  return ok && value < pattern;
+  return numberCompare(entry_, std::less<double>());
 }
 
 bool FilterRule::greaterThan(Tellico::Data::EntryPtr entry_) const {
-  // empty field name means search all
-  // but the rule widget should limit this function to number fields only
-  if(m_fieldName.isEmpty()) {
-    return false;
-  }
-  const double pattern = m_patternVariant.toDouble();
-  bool ok = false;
-  const double value = entry_->field(m_fieldName).toDouble(&ok);
-  return ok && value > pattern;
+  return numberCompare(entry_, std::greater<double>());
 }
 
 void FilterRule::updatePattern() {
@@ -231,6 +224,9 @@ void FilterRule::updatePattern() {
   } else if(m_function == FuncLess || m_function == FuncGreater)  {
     m_patternVariant = m_pattern.toDouble();
   } else {
+    if(m_pattern.isEmpty()) {
+      m_pattern = m_patternVariant.toString();
+    }
     // we don't even use it
     m_patternVariant = QVariant();
   }
@@ -245,6 +241,33 @@ QString FilterRule::pattern() const {
   return m_pattern;
 }
 
+template <typename Func>
+bool FilterRule::numberCompare(Tellico::Data::EntryPtr entry_, Func f) const {
+  // empty field name means search all
+  // but the rule widget should limit this function to number fields only
+  if(m_fieldName.isEmpty()) {
+    return false;
+  }
+
+  bool ok = false;
+  const QString valueString = entry_->field(m_fieldName);
+  double value;
+  if(entry_->collection()->hasField(m_fieldName) &&
+     entry_->collection()->fieldByName(m_fieldName)->type() == Data::Field::Image) {
+    const Data::ImageInfo info = ImageFactory::imageInfo(valueString);
+    ok = !info.isNull();
+    // image size comparison presumes "fitting inside a box" so
+    // consider the pattern value to be the size of the square and compare against biggest dimension
+    value = qMax(info.width(), info.height());
+  } else {
+    value = valueString.toDouble(&ok);
+  }
+  // the equal compare is assumed to use the pattern string and the variant will be empty
+  // TODO: switch to using the variant for everything
+  return ok && f(value, m_patternVariant.isNull() ? m_pattern.toDouble()
+                                                  : m_patternVariant.toDouble());
+}
+
 /*******************************************************/
 
 Filter::Filter(const Filter& other_) : QList<FilterRule*>(), QSharedData()
diff --git a/src/filter.h b/src/filter.h
index 7ddd886c..e880c265 100644
--- a/src/filter.h
+++ b/src/filter.h
@@ -96,6 +96,9 @@ public:
 //  void setPattern(const QString& pattern) { m_pattern = pattern; }
 
 private:
+  template <typename Func>
+  bool numberCompare(Tellico::Data::EntryPtr entry, Func f) const;
+
   bool equals(Data::EntryPtr entry) const;
   bool contains(Data::EntryPtr entry) const;
   bool matchesRegExp(Data::EntryPtr entry) const;
diff --git a/src/gui/filterrulewidget.cpp b/src/gui/filterrulewidget.cpp
index c538574a..fe78c12f 100644
--- a/src/gui/filterrulewidget.cpp
+++ b/src/gui/filterrulewidget.cpp
@@ -152,6 +152,8 @@ void FilterRuleWidget::slotRuleFieldChanged(int which_) {
       m_ruleType = Date;
     } else if(field->type() == Data::Field::Number || field->type() == Data::Field::Rating) {
       m_ruleType = Number;
+    } else if(field->type() == Data::Field::Image) {
+      m_ruleType = Image;
     }
   }
   updateFunctionList();
@@ -271,6 +273,12 @@ void FilterRuleWidget::updateFunctionList() {
       m_ruleFunc->addItem(i18nc("is less than a number", "is less than"), FilterRule::FuncLess);
       m_ruleFunc->addItem(i18nc("is greater than a number", "is greater than"), FilterRule::FuncGreater);
       break;
+    case Image:
+      m_ruleFunc->addItem(i18n("image size equals"), FilterRule::FuncEquals);
+      m_ruleFunc->addItem(i18n("image size does not equal"), FilterRule::FuncNotEquals);
+      m_ruleFunc->addItem(i18nc("image size is less than a number", "image size is less than"), FilterRule::FuncLess);
+      m_ruleFunc->addItem(i18nc("image size is greater than a number", "image size is greater than"), FilterRule::FuncGreater);
+      break;
     case General:
       m_ruleFunc->addItem(i18n("contains"), FilterRule::FuncContains);
       m_ruleFunc->addItem(i18n("does not contain"), FilterRule::FuncNotContains);
diff --git a/src/gui/filterrulewidget.h b/src/gui/filterrulewidget.h
index 9c86804b..ec99dfd4 100644
--- a/src/gui/filterrulewidget.h
+++ b/src/gui/filterrulewidget.h
@@ -109,7 +109,8 @@ private:
   enum RuleType {
     General,
     Date,
-    Number
+    Number,
+    Image
   };
   RuleType m_ruleType;
 };
diff --git a/src/tests/filtertest.cpp b/src/tests/filtertest.cpp
index da5faf80..8238252e 100644
--- a/src/tests/filtertest.cpp
+++ b/src/tests/filtertest.cpp
@@ -29,6 +29,8 @@
 #include "../filter.h"
 #include "../entry.h"
 #include "../collections/bookcollection.h"
+#include "../images/imageinfo.h"
+#include "../images/imagefactory.h"
 
 #include <QTest>
 
@@ -207,6 +209,38 @@ void FilterTest::testFilter() {
 
   entry->setField(QStringLiteral("rating"), QStringLiteral("1"));
   QVERIFY(filter.matches(entry));
+
+  // check image size comparisons
+  Tellico::Data::FieldPtr imageField(new Tellico::Data::Field(QStringLiteral("image"),
+                                                              QStringLiteral("image"),
+                                                              Tellico::Data::Field::Image));
+  coll->addField(imageField);
+  const QString imageName(QStringLiteral("image.png"));
+  entry->setField(QStringLiteral("image"), imageName);
+  // insert image size into cache (128x128)
+  Tellico::Data::ImageInfo imageInfo(imageName, "PNG", 128, 96, false);
+  Tellico::ImageFactory::cacheImageInfo(imageInfo);
+
+  Tellico::FilterRule* rule9 = new Tellico::FilterRule(QStringLiteral("image"),
+                                                       QStringLiteral("96"),
+                                                       Tellico::FilterRule::FuncGreater);
+  QCOMPARE(rule9->pattern(), QStringLiteral("96"));
+  filter.clear();
+  filter.append(rule9);
+  // compares against larger image dimension, so 128 > 96 matches
+  QVERIFY(filter.matches(entry));
+
+  // compares against larger image dimension, so 128 < 96 fails
+  rule9->setFunction(Tellico::FilterRule::FuncLess);
+  QVERIFY(!filter.matches(entry));
+
+  Tellico::Data::ImageInfo imageInfo2(imageName, "PNG", 96, 96, false);
+  Tellico::ImageFactory::cacheImageInfo(imageInfo2);
+
+  rule9->setFunction(Tellico::FilterRule::FuncLess);
+  QVERIFY(!filter.matches(entry));
+  rule9->setFunction(Tellico::FilterRule::FuncEquals);
+  QVERIFY(filter.matches(entry));
 }
 
 void FilterTest::testGroupViewFilter() {


More information about the kde-doc-english mailing list