bongo-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[bongo-devel] Marks


From: Daniel Jensen
Subject: [bongo-devel] Marks
Date: Wed, 10 Jan 2007 15:20:32 +0100
User-agent: Gnus/5.110006 (No Gnus v0.6) Emacs/22.0.92 (gnu/linux)

Here's a patch, introducing marks. I'd appreciate your comments.

I emulated dired where applicable. Currently only track lines can be
marked. For some reason I did not think header lines should be marked.

Marking commands:

key             binding
---             -------
m               bongo-mark-line
u               bongo-unmark-line
U               bongo-unmark-all-marks
% m             bongo-mark-lines-regexp
% a             bongo-mark-lines-artist-regexp
% b             bongo-mark-lines-album-regexp
% t             bongo-mark-lines-track-regexp
% y             bongo-mark-lines-year-regexp

Commands modified for using marks:

key             binding
---             -------
e               bongo-append-enqueue
E               bongo-insert-enqueue
RET             bongo-dwim
k               bongo-kill-line
c               bongo-copy-line-as-kill

Yanking killed lines is broken!

Customize the display with `bongo-mark-character' and face
`bongo-marked-line'.

diff -Naur bongo-old/bongo.el bongo-new/bongo.el
--- bongo-old/bongo.el  2007-01-08 19:56:57.000000000 +0100
+++ bongo-new/bongo.el  2007-01-10 15:10:56.000000000 +0100
@@ -3055,6 +3055,181 @@
         (bongo-maybe-insert-intermediate-header)))))
 
 
+;;;; Marks
+
+(defvar bongo-marks nil
+  "A list of marked lines in the buffer.")
+(make-variable-buffer-local 'bongo-marks)
+
+(defun bongo-line-marker ()
+  "Return a new marker for the current line."
+  (set-marker (make-marker) (bongo-point-at-bol)))
+
+(defun bongo-marked-track-line-p ()
+  "Return non-nil if the line at point is marked."
+  (member (bongo-line-marker) bongo-marks))
+
+(defun bongo-force-visible-line ()
+  "Expand section headers above if the current line is invisible."
+  ;; redisplaying an invisible is broken
+  (while (bongo-before-invisible-text-p)
+    (save-excursion
+      (goto-char (bongo-point-before-previous-line-satisfying
+                  (lambda ()
+                    (and (bongo-header-line-p)
+                         (not (bongo-before-invisible-text-p))))))
+      (bongo-expand))))
+
+(defun bongo-set-mark (&optional unset)
+  "Set a mark at line, or clear it if UNSET is non-nil."
+  (if unset
+      (setq bongo-marks (delete (bongo-line-marker) bongo-marks))
+    (add-to-list 'bongo-marks (bongo-line-marker)))
+  (bongo-force-visible-line)
+  (bongo-redisplay-line))
+
+(defun bongo-repeat-over-tracks (n function)
+  "Repeat FUNCTION over the next N track lines."
+  (while (> n 0)
+    (funcall function)
+    (let ((next (bongo-point-at-next-track-line)))
+      (when next
+        (goto-char next))
+      (setq n (if next (1- n) 0))))
+  (while (< n 0)
+    (let ((prev (bongo-point-at-previous-track-line)))
+      (when prev
+        (goto-char prev))
+      (setq n (if prev (1+ n) 0)))
+    (funcall function)))
+
+(defun bongo-mark-line (n)
+  "Mark the N following tracks for use in later Bongo commands."
+  (interactive "p")
+  (unless (bongo-track-line-p)
+    (goto-char (bongo-point-at-next-track-line)))
+  (bongo-repeat-over-tracks n 'bongo-set-mark))
+
+(defun bongo-unmark-line (n)
+  "Remove marks from the N following lines."
+  (interactive "p")
+  (unless (bongo-track-line-p)
+    (goto-char (bongo-point-at-next-track-line)))
+  (bongo-repeat-over-tracks n (lambda () (bongo-set-mark 'unset))))
+
+(defun bongo-line-matches-p (regexp readers)
+  "Return t if a field in the line at point matches REGEXP.
+Argument READERS is a list of infoset reader functions to limit
+search on their values."
+  (catch 'match
+    (let ((infoset (bongo-line-infoset)))
+      (dolist (reader readers)
+        (let ((value (funcall reader infoset)))
+          (when (and value (string-match regexp value))
+            (throw 'match t)))))))
+
+(defun bongo-mark-lines-matching (regexp infoset-readers)
+  "Mark lines matching REGEXP.
+Return the number of lines marked.  See `bongo-line-matches-p'."
+  (save-excursion
+    (let ((line-move-ignore-invisible nil)
+          (mark-count 0))
+      (goto-char (point-min))
+      (while (not (eobp))
+        (when (and (bongo-track-line-p)
+                   (bongo-line-matches-p regexp infoset-readers))
+          (bongo-set-mark)
+          (setq mark-count (1+ mark-count)))
+        (forward-line))
+      mark-count)))
+
+(defun bongo-repeat-over-marked-lines (function &optional mark-list)
+  "Call FUNCTION at every marked line.
+Use `bongo-marks' or optional MARK-LIST."
+  (save-excursion
+    (let ((line-move-ignore-invisible nil))
+      (dolist (mark (or mark-list bongo-marks))
+        (goto-char mark)
+        (funcall function)))))
+
+(defun bongo-unmark-all-marks ()
+  "Remove all marks from the buffer."
+  (interactive)
+  (let ((old-marks bongo-marks))
+    (setq bongo-marks nil)
+    (bongo-repeat-over-marked-lines     
+     (lambda ()
+       (bongo-force-visible-line)
+       (bongo-redisplay-line))
+     old-marks)))
+
+(defun bongo-process-marks (function &optional mark-list)
+  "Call FUNCTION on marked lines in the buffer.
+Use in reverse order marks in `bongo-marks' or optional MARK-LIST."
+  (bongo-repeat-over-marked-lines function
+                                  (or mark-list (reverse bongo-marks)))
+  (bongo-unmark-all-marks))
+
+(defvar bongo-regexp-history nil
+  "History list of regular expressions used in Bongo commands.")
+
+(defun bongo-read-regexp (prompt)
+  (read-from-minibuffer prompt nil nil nil 'bongo-regexp-history))
+
+(defun bongo-mark-lines-regexp (regexp &optional separate-fields)
+  "Mark lines with fields matching REGEXP.
+With prefix arg, match fields artist, album and track title
+separately.  Otherwise, match against a formatted infoset."
+  (interactive
+   (list (bongo-read-regexp "Mark (regexp): ")
+         current-prefix-arg))
+  (message "Marked %d matching tracks."
+           (bongo-mark-lines-matching regexp
+                                      (if separate-fields
+                                          '(bongo-infoset-artist-name
+                                            bongo-infoset-album-title
+                                            bongo-infoset-track-title)
+                                        '(bongo-format-infoset)))))
+
+(defun bongo-mark-lines-artist-regexp (regexp)
+  "Mark lines with artist fields matching REGEXP."
+  (interactive
+   (list (bongo-read-regexp "Mark artists (regexp): ")))
+  (message "Marked %d matching tracks."
+           (bongo-mark-lines-matching regexp '(bongo-infoset-artist-name))))
+
+(defun bongo-mark-lines-album-regexp (regexp)
+  "Mark lines with album fields matching REGEXP."
+  (interactive
+   (list (bongo-read-regexp "Mark albums (regexp): ")))
+  (message "Marked %d matching tracks."
+           (bongo-mark-lines-matching regexp '(bongo-infoset-album-title))))
+
+(defun bongo-mark-lines-track-regexp (regexp)
+  "Mark lines with track fields matching REGEXP."
+  (interactive
+   (list (bongo-read-regexp "Mark track titles (regexp): ")))
+  (message "Marked %d matching tracks."
+           (bongo-mark-lines-matching regexp '(bongo-infoset-track-title))))
+
+(defun bongo-mark-lines-year-regexp (regexp)
+  "Mark lines with year fields matching REGEXP."
+  (interactive
+   (list (bongo-read-regexp "Mark year (regexp): ")))
+  (message "Marked %d matching tracks."
+           (bongo-mark-lines-matching regexp '(bongo-infoset-album-year))))
+
+(defface bongo-marked-line
+  '((t (:inherit font-lock-warning-face)))
+  "Face used for marking Bongo track lines."
+  :group 'bongo-faces)
+
+(defcustom bongo-mark-character ?*
+  "Character prefixed to marked lines."
+  :type 'character
+  :group 'bongo-display)
+
+
 ;;;; Backends
 
 (defun bongo-backend (backend-name)
@@ -4945,14 +5120,22 @@
 If point is neither on a track nor on a header, do nothing.
 With numerical prefix argument N, play or enqueue the next N tracks.
 With \\[universal-argument] as prefix argument in a playlist buffer,
-  intra-playlist-enqueue the track instead of playing it."
+  intra-playlist-enqueue the track instead of playing it.
+If there are marked tracks and the buffer is a library buffer,
+enqueue the tracks and start playing the first one."
   (interactive "P")
   (cond ((and (bongo-track-line-p) (bongo-library-buffer-p))
-         (let ((position (if (bongo-playing-p)
-                             (bongo-insert-enqueue-line
-                              (prefix-numeric-value n))
-                           (bongo-append-enqueue-line
-                            (prefix-numeric-value n)))))
+         (let ((position nil)
+               (mode (if (bongo-playing-p) 'insert 'append)))
+           (if bongo-marks
+               (bongo-process-marks
+                (lambda ()
+                  (let ((nextpos (bongo-enqueue-line mode)))
+                    (setq position (or position nextpos))))
+                ;; prevent reverse insertion
+                (when (eq mode 'insert) bongo-marks))
+             (setq position
+                   (bongo-enqueue-line mode (prefix-numeric-value n))))
            (with-bongo-playlist-buffer
              (bongo-play-line position))))
         ((and (bongo-track-line-p) (bongo-playlist-buffer-p))
@@ -6505,12 +6688,18 @@
         (invisible (bongo-line-get-property 'invisible))
         (currently-playing (bongo-currently-playing-track-line-p))
         (played (bongo-played-track-line-p))
+        (marked (bongo-marked-track-line-p))
         (properties (bongo-line-get-semantic-properties)))
     (save-excursion
       (bongo-clear-line)
       (bongo-line-set-properties properties)
-      (dotimes (dummy indentation)
-        (insert bongo-indentation-string))
+      (let ((str ""))
+        (dotimes (dummy indentation)
+          (setq str (concat str bongo-indentation-string)))
+        (if marked
+            (insert bongo-mark-character
+                    (substring str (if (string= str "") 0 1)))
+          (insert str)))
       (let* ((bongo-infoset-formatting-target
               (current-buffer))
              (bongo-infoset-formatting-target-line
@@ -6520,7 +6709,9 @@
                              (when invisible
                                (list 'invisible invisible)))))
         (insert
-         (cond (header
+         (cond (marked
+                (bongo-facify content 'bongo-marked-line))
+               (header
                 (bongo-format-header content collapsed))
                (currently-playing
                 (bongo-facify content 'bongo-currently-playing-track))
@@ -6652,11 +6843,19 @@
   "In Bongo, kill the current section, track, or line.
 If the current line is a header line, kill the whole section.
 If the current line is a track line, kill the track.
+If there are marked lines, kill them instead.
 Otherwise, just kill the line as `kill-line' would.
 See also `bongo-copy-line-as-kill'."
   (interactive)
   (let ((inhibit-read-only t))
     (cond
+     (bongo-marks
+      (let ((mark-list (nreverse bongo-marks)))
+        (setq bongo-marks nil)
+        (bongo-process-marks (lambda ()
+                               (bongo-kill-line)
+                               (append-next-kill))
+                             mark-list)))
      ((bongo-track-line-p)
       (when (bongo-current-track-line-p)
         (bongo-unset-current-track-position))
@@ -6685,6 +6884,7 @@
   "In Bongo, save the current object as if killed, but don't kill it.
 If the current line is a header line, copy the whole section.
 If the current line is a track line, copy the track.
+If there are marked lines, copy them instead.
 Otherwise, just copy the current line.
 
 If SKIP is non-nil, move point past the copied text after copying.
@@ -6692,14 +6892,23 @@
 
 See also `bongo-kill-line'."
   (interactive "p")
-  (when (eq last-command 'bongo-copy-line-as-kill)
-    (append-next-kill))
-  (let ((end (if (bongo-object-line-p)
-                 (bongo-point-after-object)
-               (bongo-point-after-line))))
-    (copy-region-as-kill (bongo-point-before-line) end)
-    (when skip
-      (goto-char end))))
+  (if bongo-marks
+      (let ((mark-list (nreverse bongo-marks)))
+        (setq bongo-marks nil)
+        (bongo-process-marks (lambda ()
+                               (bongo-copy-line-as-kill)
+                               (append-next-kill))
+                             mark-list)
+        (setq bongo-marks mark-list)
+        (bongo-unmark-all-marks))
+    (when (eq last-command 'bongo-copy-line-as-kill)
+      (append-next-kill))
+    (let ((end (if (bongo-object-line-p)
+                   (bongo-point-after-object)
+                 (bongo-point-after-line))))
+      (copy-region-as-kill (bongo-point-before-line) end)
+      (when skip
+        (goto-char end)))))
 
 (defun bongo-kill-region (&optional beg end)
   "In Bongo, kill the lines in the region between BEG and END.
@@ -6941,11 +7150,19 @@
 (defun bongo-enqueue (mode &optional n)
   "Insert the next N tracks or sections into the Bongo playlist.
 If the region is active, ignore N and enqueue the region instead.
+If there are marked tracks, ignore N and enqueue them instead.
 If MODE is `insert', insert just below the current track.
 If MODE is `append', append to the end of the playlist."
-  (if (bongo-region-active-p)
-      (bongo-enqueue-region mode (region-beginning) (region-end))
-    (bongo-enqueue-line mode n 'skip)))
+  (cond (bongo-marks
+         (bongo-process-marks
+          (lambda ()
+            (bongo-enqueue-line mode
+                                (when (eq mode 'insert)
+                                  ;; prevent reverse
+                                  bongo-marks)))))
+        ((bongo-region-active-p)
+         (bongo-enqueue-region mode (region-beginning) (region-end)))
+        (t (bongo-enqueue-line mode n 'skip))))
 
 (defun bongo-append-enqueue (&optional n)
   "Append the next N tracks or sections to the Bongo playlist buffer.
@@ -7344,6 +7561,15 @@
     (define-key map "F" 'bongo-reset-playlist)
     (define-key map "r" 'bongo-rename-line)
     (define-key map "d" 'bongo-dired-line)
+    (define-key map "m" 'bongo-mark-line)
+    (define-key map "u" 'bongo-unmark-line)
+    (define-key map "U" 'bongo-unmark-all-marks)
+    (define-key map "%" nil)            ; For Emacs 21.
+    (define-key map "%m" 'bongo-mark-lines-regexp)
+    (define-key map "%a" 'bongo-mark-lines-artist-regexp)
+    (define-key map "%b" 'bongo-mark-lines-album-regexp)
+    (define-key map "%t" 'bongo-mark-lines-track-regexp)
+    (define-key map "%y" 'bongo-mark-lines-year-regexp)
     (when (require 'volume nil t)
       (if bongo-xmms-refugee-mode
           (define-key map "V" 'volume)

reply via email to

[Prev in Thread] Current Thread [Next in Thread]