branch: externals/calibre
commit ac4c02aeb9b9454ed206a0f72ad7c2bed53a6571
Author: Kjartan Óli Ágústsson <[email protected]>
Commit: Kjartan Óli Ágústsson <[email protected]>

    Add support for composite filters
    
    * calibre-db.el (calibre-composite-filter-p): Create
    (calibre--get-filter-items): Handle composite filters.
    * calibre-search.el (calibre-search): Add docstring.
    Add binding to compose composite filters
    (calibre-search-composing-filter, calibre-search-compose-apply,
    calibre-search-compose-cleanup,
    calibre-search--composition-function, calibre-search-compose-author,
    calibre-search-compose-publisher, calibre-search-compose-tag,
    calibre-search-compose-series, calibre-search-compose-format,
    calibre-search-compose): Create
    * calibre-virtual-library.el (calibre-virtual-libraries): Change type
    to support composite filters.
---
 calibre-db.el              | 26 ++++++++++++++++-------
 calibre-search.el          | 52 ++++++++++++++++++++++++++++++++++++++++++++--
 calibre-virtual-library.el | 24 ++++++++++++++-------
 3 files changed, 85 insertions(+), 17 deletions(-)

diff --git a/calibre-db.el b/calibre-db.el
index 313d6e18ff..3f6c42bdb1 100644
--- a/calibre-db.el
+++ b/calibre-db.el
@@ -176,15 +176,27 @@ WHERE s.name = ?" `[,series])))
 FROM data
 WHERE format = ?" `[,format])))
 
+(defun calibre-composite-filter-p (object)
+  "Return t if OBJECT is a composite filter."
+  (and (vectorp object) (length= object 2) (listp (elt object 1))))
+
 (defun calibre--get-filter-items (filter)
   "Return the id's of books matching FILTER."
-  (seq-let (_ field value) filter
-    (cl-case field
-      (author (calibre-db--get-author-books value))
-      (tag (calibre-db--get-tag-books value))
-      (publisher (calibre-db--get-publisher-books value))
-      (series (calibre-db--get-series-books value))
-      (format (calibre-db--get-format-books value)))))
+  (if (calibre-composite-filter-p filter)
+      (seq-let (op filters) filter
+        (cl-reduce (if (eq op '+)
+                       #'cl-union
+                     #'cl-intersection)
+                    (mapcar (lambda (f)
+                             (calibre--get-filter-items (vconcat `[,op] f)))
+                           filters)))
+    (seq-let (_ field value) filter
+      (cl-case field
+        (author (calibre-db--get-author-books value))
+        (tag (calibre-db--get-tag-books value))
+        (publisher (calibre-db--get-publisher-books value))
+        (series (calibre-db--get-series-books value))
+        (format (calibre-db--get-format-books value))))))
 
 (defun calibre-library--filter (filters books)
   "Return those books in BOOKS that match FILTERS.
diff --git a/calibre-search.el b/calibre-search.el
index 8e2fb187a1..4f8e402737 100644
--- a/calibre-search.el
+++ b/calibre-search.el
@@ -82,7 +82,7 @@ ARGS is the argument list of a transient command."
   (calibre-library--refresh))
 
 (transient-define-prefix calibre-search ()
-  ""
+  "Filter the library view."
   :transient-suffix 'transient--do-call
   ["Arguments"
    ("-e" "Exclude" "--exclude")]
@@ -92,9 +92,57 @@ ARGS is the argument list of a transient command."
    ("t" "Tag" calibre-library-search-tag)
    ("s" "Series" calibre-library-search-series)
    ("f" "Format" calibre-library-search-format)]
-  ["Misc"
+  ["Actions"
+   ("C" "Compose" calibre-search-compose)
    ("u" "Undo" calibre-library-clear-last-search)
    ("c" "Clear" calibre-library-clear-filters)
    ("q" "Exit" transient-quit-one)])
 
+
+(defvar calibre-search-composing-filter nil)
+(defun calibre-search-compose-apply (&optional args)
+  "Apply the composite filter under construction."
+  (interactive (list (transient-args 'calibre-search-compose)))
+  (if (not calibre-search-composing-filter)
+      (error "Can not apply an empty composite filter")
+    (let ((filter (vector (calibre-search--operation args)
+                          calibre-search-composing-filter)))
+      (setf calibre-library--filters (cons filter calibre-library--filters)
+            calibre-search-composing-filter nil)
+      (calibre-library--refresh))))
+
+(defun calibre-search-compose-cleanup ()
+  "Clear any filter currently being composed."
+  (interactive)
+  (setf calibre-search-composing-filter nil))
+
+(defmacro calibre-search--composition-function (field)
+  "Create a function adding a filter for FIELD to a composite filter."
+  `(defun ,(intern (format "calibre-search-compose-%s" field)) ()
+     ,(format "Add a filter for %s to the composite filter under 
construction." field)
+     (interactive)
+     (setf calibre-search-composing-filter
+           (cons (vector ',(intern field) (,(intern (format 
"calibre-search-chose-%s" field)))) calibre-search-composing-filter))))
+
+(calibre-search--composition-function "author")
+(calibre-search--composition-function "publisher")
+(calibre-search--composition-function "tag")
+(calibre-search--composition-function "series")
+(calibre-search--composition-function "format")
+
+(transient-define-prefix calibre-search-compose ()
+  "Create a composite filter."
+  :transient-suffix 'transient--do-call
+  ["Arguments"
+   ("-e" "Exclude" "--exclude")]
+  ["Compose"
+   ("a" "Author" calibre-search-compose-author)
+   ("p" "Publisher" calibre-search-compose-publisher)
+   ("t" "Tag" calibre-search-compose-tag)
+   ("s" "Series" calibre-search-compose-series)
+   ("f" "Format" calibre-search-compose-format)]
+  ["Actions"
+   ("A" "Apply" calibre-search-compose-apply :transient transient--do-return)
+   ("q" "Quit" calibre-search-compose-cleanup :transient 
transient--do-return)])
+
 (provide 'calibre-search)
diff --git a/calibre-virtual-library.el b/calibre-virtual-library.el
index 70f55357f4..ad60178eb9 100644
--- a/calibre-virtual-library.el
+++ b/calibre-virtual-library.el
@@ -28,7 +28,13 @@
   "A list of predefined filters, i.e. Virtual Libraries.
 Each element is a cons cell (name . filters) where name is a
 string, and filters is a list of vectors [op field value]"
-  :type '(repeat :tag "Virtual Libraries"
+  :type (let ((fields '(choice :tag "Field"
+                               (const :tag "Author" author)
+                               (const :tag "Publisher" publisher)
+                               (const :tag "Series" series)
+                               (const :tag "Tag" tag)
+                               (const :tag "Format" format))))
+          `(repeat :tag "Virtual Libraries"
                  (cons :tag "Virtual Library"
                        (string :tag "Name")
                        (repeat :tag "Filters"
@@ -36,13 +42,15 @@ string, and filters is a list of vectors [op field value]"
                                        (choice :tag "Operation"
                                                (const :tag "Include" +)
                                                (const  :tag "Exclude" -))
-                                       (choice :tag "Field"
-                                               (const :tag "Author" author)
-                                               (const :tag "Publisher" 
publisher)
-                                               (const :tag "Series" series)
-                                               (const :tag "Tag" tag)
-                                               (const :tag "Format" format))
-                                       (string :tag "Value")))))
+                                       (choice :tag "Type"
+                                               (list :tag "Basic"
+                                                       :inline t
+                                                       ,fields
+                                                       (string :tag "Value"))
+                                               (repeat :tag "Composite"
+                                                       (vector :tag "Filter"
+                                                               ,fields
+                                                               (string :tag 
"Value")))))))))
   :group 'calibre
   :package-version '("calibre" . "1.1.0"))
 

Reply via email to