]> code.delx.au - gnu-emacs/blobdiff - lisp/net/newst-treeview.el
Convert consecutive FSF copyright years to ranges.
[gnu-emacs] / lisp / net / newst-treeview.el
index eed607c5931f1d7e39684a329b5b9b30b22be3f1..58d86b23946658957cb4e56463913de09d7a7c7d 100644 (file)
@@ -1,13 +1,14 @@
 ;;; newst-treeview.el --- Treeview frontend for newsticker.
 
-;; Copyright (C) 2008 Free Software Foundation, Inc.
+;; Copyright (C) 2008-2011 Free Software Foundation, Inc.
 
 ;; Author:      Ulf Jasper <ulf.jasper@web.de>
 ;; Filename:    newst-treeview.el
 ;; URL:         http://www.nongnu.org/newsticker
 ;; Created:     2007
 ;; Keywords:    News, RSS, Atom
-;; Time-stamp:  "25. August 2008, 19:39:28 (ulf)"
+;; Time-stamp:  "6. Dezember 2009, 19:17:28 (ulf)"
+;; Package:     newsticker
 
 ;; ======================================================================
 
@@ -38,7 +39,7 @@
 
 ;; ======================================================================
 ;;; Code:
-(require 'newsticker-reader "newst-reader")
+(require 'newst-reader)
 (require 'widget)
 (require 'tree-widget)
 (require 'wid-edit)
   30
   "Width of tree window in treeview layout.
 See also `newsticker-treeview-listwindow-height'."
-  :type 'int
+  :type 'integer
   :group 'newsticker-treeview)
 
 (defcustom newsticker-treeview-listwindow-height
   10
   "Height of list window in treeview layout.
 See also `newsticker-treeview-treewindow-width'."
-  :type 'int
+  :type 'integer
   :group 'newsticker-treeview)
 
 (defcustom newsticker-treeview-automatically-mark-displayed-items-as-old
@@ -143,13 +144,15 @@ Example: (\"Topmost group\" \"feed1\" (\"subgroup1\" \"feed 2\")
   "Name of the newsticker groups settings file."
   :type 'string
   :group 'newsticker-treeview)
+(make-obsolete 'newsticker-groups-filename 'newsticker-dir "23.1")
 
 ;; ======================================================================
 ;;; internal variables
 ;; ======================================================================
 (defvar newsticker--treeview-windows nil)
 (defvar newsticker--treeview-buffers nil)
-(defvar newsticker--treeview-current-feed nil)
+(defvar newsticker--treeview-current-feed nil
+  "Feed name of currently shown item.")
 (defvar newsticker--treeview-current-vfeed nil)
 (defvar newsticker--treeview-list-show-feed nil)
 (defvar newsticker--saved-window-config nil)
@@ -263,6 +266,7 @@ their id stays constant."
 
 ;; ======================================================================
 
+(unless (fboundp 'declare-function) (defmacro declare-function (&rest r)))
 (declare-function w3m-toggle-inline-images "ext:w3m" (&optional force no-cache))
 
 (defun newsticker--treeview-render-text (start end)
@@ -304,8 +308,7 @@ their id stays constant."
   "Add news ITEM for FEED to newsticker treeview list window.
 If string SHOW-FEED is non-nil it is shown in the item string."
   (setq newsticker--treeview-list-show-feed show-feed)
-  (save-excursion
-    (set-buffer (newsticker--treeview-list-buffer))
+  (with-current-buffer (newsticker--treeview-list-buffer)
     (let* ((inhibit-read-only t)
            pos1 pos2)
       (goto-char (point-max))
@@ -347,8 +350,7 @@ If string SHOW-FEED is non-nil it is shown in the item string."
 
 (defun newsticker--treeview-list-clear ()
   "Clear the newsticker treeview list window."
-  (save-excursion
-    (set-buffer (newsticker--treeview-list-buffer))
+  (with-current-buffer (newsticker--treeview-list-buffer)
     (let ((inhibit-read-only t))
       (erase-buffer)
       (kill-all-local-variables)
@@ -546,40 +548,40 @@ The sort function is chosen according to the value of
 (defun newsticker--treeview-list-update-faces ()
   "Update faces in the treeview list buffer."
   (let (pos-sel)
-    (save-excursion
-      (set-buffer (newsticker--treeview-list-buffer))
-      (let ((inhibit-read-only t))
-        (goto-char (point-min))
-        (while (not (eobp))
-          (let* ((pos (save-excursion (end-of-line) (point)))
-                 (item (get-text-property (point) :nt-item))
-                 (age (newsticker--age item))
-                 (selected (get-text-property (point) :nt-selected))
-                 (face (cond ((eq age 'new)
-                              'newsticker-treeview-new-face)
-                             ((eq age 'old)
-                              'newsticker-treeview-old-face)
-                             ((eq age 'immortal)
-                              'newsticker-treeview-immortal-face)
-                             ((eq age 'obsolete)
-                              'newsticker-treeview-obsolete-face)
-                             (t
-                              'bold))))
-            (put-text-property (point) pos 'face face)
-            (if selected
-                (move-overlay newsticker--selection-overlay (point)
-                              (1+ pos) ;include newline
-                              (current-buffer)))
-            (if selected (setq pos-sel (point)))
-            (forward-line 1)
-            (beginning-of-line))))) ;; FIXME!?
+    (with-current-buffer (newsticker--treeview-list-buffer)
+      (save-excursion
+        (let ((inhibit-read-only t))
+          (goto-char (point-min))
+          (while (not (eobp))
+            (let* ((pos (point-at-eol))
+                   (item (get-text-property (point) :nt-item))
+                   (age (newsticker--age item))
+                   (selected (get-text-property (point) :nt-selected))
+                   (face (cond ((eq age 'new)
+                                'newsticker-treeview-new-face)
+                               ((eq age 'old)
+                                'newsticker-treeview-old-face)
+                               ((eq age 'immortal)
+                                'newsticker-treeview-immortal-face)
+                               ((eq age 'obsolete)
+                                'newsticker-treeview-obsolete-face)
+                               (t
+                                'bold))))
+              (put-text-property (point) pos 'face face)
+              (if selected
+                  (move-overlay newsticker--selection-overlay (point)
+                                (1+ pos) ;include newline
+                                (current-buffer)))
+              (if selected (setq pos-sel (point)))
+              (forward-line 1)
+              (beginning-of-line)))))) ;; FIXME!?
     (when pos-sel
-      (set-window-point (newsticker--treeview-list-window) pos-sel))))
+      (if (window-live-p (newsticker--treeview-list-window))
+          (set-window-point (newsticker--treeview-list-window) pos-sel)))))
 
 (defun newsticker--treeview-list-clear-highlight ()
   "Clear the highlight in the treeview list buffer."
-  (save-excursion
-    (set-buffer (newsticker--treeview-list-buffer))
+  (with-current-buffer (newsticker--treeview-list-buffer)
     (let ((inhibit-read-only t))
       (put-text-property (point-min) (point-max) :nt-selected nil))
     (newsticker--treeview-list-update-faces)))
@@ -588,27 +590,25 @@ The sort function is chosen according to the value of
   "Update the highlight in the treeview list buffer."
   (newsticker--treeview-list-clear-highlight)
     (let (pos num-lines)
-      (save-excursion
-        (set-buffer (newsticker--treeview-list-buffer))
+      (with-current-buffer (newsticker--treeview-list-buffer)
         (let ((inhibit-read-only t))
-          (put-text-property (save-excursion (beginning-of-line) (point))
-                             (save-excursion (end-of-line) (point))
-                             :nt-selected t))
+          (put-text-property (point-at-bol) (point-at-eol) :nt-selected t))
         (newsticker--treeview-list-update-faces))))
 
 (defun newsticker--treeview-list-highlight-start ()
   "Return position of selection in treeview list buffer."
-  (save-excursion
-    (set-buffer (newsticker--treeview-list-buffer))
-    (goto-char (point-min))
-    (next-single-property-change (point) :nt-selected)))
+  (with-current-buffer (newsticker--treeview-list-buffer)
+    (save-excursion
+      (goto-char (point-min))
+      (next-single-property-change (point) :nt-selected))))
 
 (defun newsticker--treeview-list-update (clear-buffer)
   "Update the faces and highlight in the treeview list buffer.
 If CLEAR-BUFFER is non-nil the list buffer is completely erased."
   (save-excursion
-    (set-window-buffer (newsticker--treeview-list-window)
-                       (newsticker--treeview-list-buffer))
+    (if (window-live-p (newsticker--treeview-list-window))
+        (set-window-buffer (newsticker--treeview-list-window)
+                           (newsticker--treeview-list-buffer)))
     (set-buffer (newsticker--treeview-list-buffer))
     (if clear-buffer
         (let ((inhibit-read-only t))
@@ -630,8 +630,8 @@ If CLEAR-BUFFER is non-nil the list buffer is completely erased."
   "Sort the newsticker list window buffer by the column clicked on.
 Optional argument EVENT is the mouse event that triggered this action."
   (interactive (list last-input-event))
-  (if e (mouse-select-window e))
-  (let* ((pos (event-start e))
+  (if event (mouse-select-window event))
+  (let* ((pos (event-start event))
         (obj (posn-object pos))
         (sort-order (if obj
                       (get-text-property (cdr obj) 'sort-order (car obj))
@@ -667,13 +667,33 @@ for the button."
                 'face face
                 'keymap newsticker-treeview-list-sort-button-map)))
 
+(defun newsticker--treeview-list-select (item)
+  "Select ITEM in treeview's list buffer."
+  (newsticker--treeview-list-clear-highlight)
+    (let (pos num-lines)
+      (save-current-buffer
+        (set-buffer (newsticker--treeview-list-buffer))
+        (goto-char (point-min))
+        (catch 'found
+          (while t
+            (let ((it (get-text-property (point) :nt-item)))
+              (when (eq it item)
+                (newsticker--treeview-list-update-highlight)
+                (newsticker--treeview-list-update-faces)
+                (newsticker--treeview-item-show
+                 item (get-text-property (point) :nt-feed))
+                (throw 'found t)))
+            (forward-line 1)
+            (when (eobp)
+              (goto-char (point-min))
+              (throw 'found nil)))))))
+
 ;; ======================================================================
 ;;; item window
 ;; ======================================================================
 (defun newsticker--treeview-item-show-text (title description)
   "Show text in treeview item buffer consisting of TITLE and DESCRIPTION."
-  (save-excursion
-    (set-buffer (newsticker--treeview-item-buffer))
+  (with-current-buffer (newsticker--treeview-item-buffer)
     (when (fboundp 'w3m-process-stop)
       (w3m-process-stop (current-buffer)))
     (let ((inhibit-read-only t))
@@ -685,13 +705,13 @@ for the button."
       (insert "\n\n" description)
       (when newsticker-justification
         (fill-region (point-min) (point-max) newsticker-justification))
-      (newsticker-treeview-mode)
+      (newsticker-treeview-item-mode)
       (goto-char (point-min)))))
 
-(defun newsticker--treeview-item-show (item feed)
-  "Show news ITEM coming from FEED in treeview item buffer."
-  (save-excursion
-    (set-buffer (newsticker--treeview-item-buffer))
+(defun newsticker--treeview-item-show (item feed-name-symbol)
+  "Show news ITEM coming from FEED-NAME-SYMBOL in treeview item buffer."
+  (setq newsticker--treeview-current-feed (symbol-name feed-name-symbol))
+  (with-current-buffer (newsticker--treeview-item-buffer)
     (when (fboundp 'w3m-process-stop)
       (w3m-process-stop (current-buffer)))
     (let ((inhibit-read-only t)
@@ -703,7 +723,7 @@ for the button."
       (kill-all-local-variables)
       (remove-overlays)
 
-      (when (and item feed)
+      (when (and item feed-name-symbol)
         (let ((wwidth (1- (window-width (newsticker--treeview-item-window)))))
           (if newsticker-use-full-width
               (set (make-local-variable 'fill-column) wwidth))
@@ -719,14 +739,14 @@ for the button."
                    (not is-rendered-HTML))
           (fill-region marker1 marker2 newsticker-justification))
 
-        (newsticker-treeview-mode)
+        (newsticker-treeview-item-mode)
         (goto-char (point-min))
         ;; insert logo at top
         (let* ((newsticker-enable-logo-manipulations nil)
-               (img (newsticker--image-read feed nil)))
+               (img (newsticker--image-read feed-name-symbol nil)))
           (if (and (display-images-p) img)
               (newsticker--insert-image img (car item))
-            (insert (newsticker--real-feed-name feed))))
+            (insert (newsticker--real-feed-name feed-name-symbol))))
         (add-text-properties (point-min) (point)
                              (list 'face 'newsticker-feed-face
                                    'mouse-face 'highlight
@@ -778,17 +798,19 @@ for the button."
       (let ((newsticker-treeview-automatically-mark-displayed-items-as-old nil))
         (newsticker-treeview-mark-item-old t)
         (newsticker--treeview-list-update-faces)))
-  (set-window-point (newsticker--treeview-item-window) 1))
+  (if (window-live-p (newsticker--treeview-item-window))
+      (set-window-point (newsticker--treeview-item-window) 1)))
 
 (defun newsticker--treeview-item-update ()
   "Update the treeview item buffer and window."
   (save-excursion
-    (set-window-buffer (newsticker--treeview-item-window)
-                       (newsticker--treeview-item-buffer))
+    (if (window-live-p (newsticker--treeview-item-window))
+        (set-window-buffer (newsticker--treeview-item-window)
+                           (newsticker--treeview-item-buffer)))
     (set-buffer (newsticker--treeview-item-buffer))
     (let ((inhibit-read-only t))
       (erase-buffer))
-    (newsticker-treeview-mode)))
+    (newsticker-treeview-item-mode)))
 
 ;; ======================================================================
 ;;; Tree window
@@ -863,8 +885,9 @@ Optional arguments CHANGED-WIDGET and EVENT are ignored."
 (defun newsticker--treeview-tree-update ()
   "Update treeview tree buffer and window."
   (save-excursion
-    (set-window-buffer (newsticker--treeview-tree-window)
-                       (newsticker--treeview-tree-buffer))
+    (if (window-live-p (newsticker--treeview-tree-window))
+        (set-window-buffer (newsticker--treeview-tree-window)
+                           (newsticker--treeview-tree-buffer)))
     (set-buffer (newsticker--treeview-tree-buffer))
     (kill-all-local-variables)
     (let ((inhibit-read-only t))
@@ -983,8 +1006,6 @@ If RECURSIVE is non-nil recursively update parent widgets as
 well.  Argument IGNORE is ignored.  Note that this function, if
 called recursively, makes w invalid.  You should keep w's nt-id in
 that case."
-  ;;(message "newsticker--treeview-tree-update-tag %s, %s" (widget-get w :tag)
-    ;;       (widget-type w))
   (let* ((parent (widget-get w :parent))
          (feed (or (widget-get w :nt-feed) (widget-get parent :nt-feed)))
          (vfeed (or (widget-get w :nt-vfeed) (widget-get parent :nt-vfeed)))
@@ -1005,8 +1026,7 @@ that case."
         (let ((p (point))
               (notify (widget-get w :notify)))
           ;; FIXME: This moves point!!!!
-          (save-excursion
-            (set-buffer (newsticker--treeview-tree-buffer))
+          (with-current-buffer (newsticker--treeview-tree-buffer)
             (widget-value-set w (widget-value w)))
           (goto-char p))))))
 
@@ -1038,14 +1058,13 @@ Arguments IGNORE are ignored."
                              (newsticker--treeview-get-current-node)
                                         :parent) :from)))
     (when (or (integerp pos) (and (markerp pos) (marker-position pos)))
-      (save-excursion
-        (set-buffer (newsticker--treeview-tree-buffer))
+      (with-current-buffer (newsticker--treeview-tree-buffer)
         (goto-char pos)
         (move-overlay newsticker--tree-selection-overlay
-                      (save-excursion (beginning-of-line) (point))
-                      (save-excursion (end-of-line) (1+ (point)))
+                      (point-at-bol) (1+ (point-at-eol))
                       (current-buffer)))
-      (set-window-point (newsticker--treeview-tree-window) pos))))
+      (if (window-live-p (newsticker--treeview-tree-window))
+          (set-window-point (newsticker--treeview-tree-window) pos)))))
 
 ;; ======================================================================
 ;;; Toolbar
@@ -1159,8 +1178,7 @@ Arguments IGNORE are ignored."
 (defun newsticker-treeview-browse-url ()
   "Call `browse-url' for the link of the item at point."
   (interactive)
-  (save-excursion
-    (set-buffer (newsticker--treeview-list-buffer))
+  (with-current-buffer (newsticker--treeview-list-buffer)
     (let ((url (get-text-property (point) :nt-link)))
       (when url
         (browse-url url)
@@ -1178,15 +1196,13 @@ Arguments IGNORE are ignored."
                (get-buffer-create "*Newsticker Item*") t)
 
   (unless newsticker--selection-overlay
-    (save-excursion
-      (set-buffer (newsticker--treeview-list-buffer))
+    (with-current-buffer (newsticker--treeview-list-buffer)
       (setq newsticker--selection-overlay (make-overlay (point-min)
                                                         (point-max)))
       (overlay-put newsticker--selection-overlay 'face
                    'newsticker-treeview-selection-face)))
   (unless newsticker--tree-selection-overlay
-    (save-excursion
-      (set-buffer (newsticker--treeview-tree-buffer))
+    (with-current-buffer (newsticker--treeview-tree-buffer)
       (setq newsticker--tree-selection-overlay (make-overlay (point-min)
                                                              (point-max)))
       (overlay-put newsticker--tree-selection-overlay 'face
@@ -1197,25 +1213,29 @@ Arguments IGNORE are ignored."
   (newsticker--treeview-item-update))
 
 (defun newsticker-treeview-update ()
-  "Update all treeview buffers and windows."
+  "Update all treeview buffers and windows.
+Note: does not update the layout."
   (interactive)
-  (newsticker--cache-update)
-  (newsticker--group-manage-orphan-feeds)
-  (newsticker--treeview-list-update t)
-  (newsticker--treeview-item-update)
-  (newsticker--treeview-tree-update-tags)
-  (cond (newsticker--treeview-current-feed
-         (newsticker--treeview-list-items newsticker--treeview-current-feed))
-        (newsticker--treeview-current-vfeed
-         (newsticker--treeview-list-items-with-age
-          (intern newsticker--treeview-current-vfeed))))
-  (newsticker--treeview-tree-update-highlight)
-  (newsticker--treeview-list-update-highlight))
+  (let ((cur-item (newsticker--treeview-get-selected-item)))
+    (newsticker--group-manage-orphan-feeds)
+    (newsticker--treeview-list-update t)
+    (newsticker--treeview-item-update)
+    (newsticker--treeview-tree-update-tags)
+    (cond (newsticker--treeview-current-feed
+           (newsticker--treeview-list-items newsticker--treeview-current-feed))
+          (newsticker--treeview-current-vfeed
+           (newsticker--treeview-list-items-with-age
+            (intern newsticker--treeview-current-vfeed))))
+    (newsticker--treeview-tree-update-highlight)
+    (newsticker--treeview-list-update-highlight)
+    (let ((cur-feed (or newsticker--treeview-current-feed
+                        newsticker--treeview-current-vfeed)))
+      (if (and cur-feed cur-item)
+          (newsticker--treeview-list-select cur-item)))))
 
 (defun newsticker-treeview-quit ()
   "Quit newsticker treeview."
   (interactive)
-  (newsticker-treeview-save)
   (setq newsticker--sentinel-callback nil)
   (bury-buffer "*Newsticker Tree*")
   (bury-buffer "*Newsticker List*")
@@ -1224,28 +1244,38 @@ Arguments IGNORE are ignored."
   (when newsticker--frame
     (if (frame-live-p newsticker--frame)
       (delete-frame newsticker--frame))
-    (setq newsticker--frame nil)))
+    (setq newsticker--frame nil))
+  (newsticker-treeview-save))
 
 (defun newsticker-treeview-save ()
   "Save newsticker data including treeview settings."
   (interactive)
-  (newsticker--cache-save)
-  (save-excursion
-    (let ((coding-system-for-write 'utf-8)
-          (buf (find-file-noselect newsticker-groups-filename)))
-      (when buf
-        (set-buffer buf)
+  (let ((coding-system-for-write 'utf-8)
+        (buf (find-file-noselect (concat newsticker-dir "/groups"))))
+    (when buf
+      (with-current-buffer buf
         (setq buffer-undo-list t)
         (erase-buffer)
         (insert ";; -*- coding: utf-8 -*-\n")
         (insert (prin1-to-string newsticker-groups))
-        (save-buffer)))))
+        (save-buffer)
+        (kill-buffer)))))
 
 (defun newsticker--treeview-load ()
   "Load treeview settings."
   (let* ((coding-system-for-read 'utf-8)
-         (buf (and (file-exists-p newsticker-groups-filename)
-                   (find-file-noselect newsticker-groups-filename))))
+         (filename
+          (or (and (file-exists-p newsticker-groups-filename)
+                   (y-or-n-p
+                    (format "Old newsticker groups (%s) file exists.  Read it? "
+                            newsticker-groups-filename))
+                   newsticker-groups-filename)
+              (concat newsticker-dir "/groups")))
+         (buf (and (file-exists-p filename)
+                   (find-file-noselect filename))))
+    (and (file-exists-p newsticker-groups-filename)
+        (y-or-n-p (format "Delete old newsticker groups file? "))
+        (delete-file newsticker-groups-filename))
     (when buf
       (set-buffer buf)
       (goto-char (point-min))
@@ -1253,7 +1283,8 @@ Arguments IGNORE are ignored."
           (setq newsticker-groups (read buf))
         (error
          (message "Error while reading newsticker groups file!")
-         (setq newsticker-groups nil))))))
+         (setq newsticker-groups nil)))
+      (kill-buffer buf))))
 
 
 (defun newsticker-treeview-scroll-item ()
@@ -1266,9 +1297,9 @@ Arguments IGNORE are ignored."
 (defun newsticker-treeview-show-item ()
   "Show current item."
   (interactive)
+  (newsticker--treeview-restore-layout)
   (newsticker--treeview-list-update-highlight)
-  (save-excursion
-    (set-buffer (newsticker--treeview-list-buffer))
+  (with-current-buffer (newsticker--treeview-list-buffer)
     (beginning-of-line)
     (let ((item (get-text-property (point) :nt-item))
           (feed (get-text-property (point) :nt-feed)))
@@ -1280,7 +1311,7 @@ Arguments IGNORE are ignored."
 (defun newsticker-treeview-next-item ()
   "Move to next item."
   (interactive)
-  (newsticker--treeview-restore-buffers)
+  (newsticker--treeview-restore-layout)
   (save-current-buffer
     (set-buffer (newsticker--treeview-list-buffer))
     (if (newsticker--treeview-list-highlight-start)
@@ -1292,53 +1323,71 @@ Arguments IGNORE are ignored."
 (defun newsticker-treeview-prev-item ()
   "Move to previous item."
   (interactive)
-  (newsticker--treeview-restore-buffers)
+  (newsticker--treeview-restore-layout)
   (save-current-buffer
     (set-buffer (newsticker--treeview-list-buffer))
     (forward-line -1))
   (newsticker-treeview-show-item))
 
-(defun newsticker-treeview-next-new-or-immortal-item ()
-  "Move to next new or immortal item."
+(defun newsticker-treeview-next-new-or-immortal-item (&optional
+                                                      current-item-counts
+                                                      dont-wrap-trees)
+  "Move to next new or immortal item.
+Will move to next feed until an item is found.  Will not move if
+optional argument CURRENT-ITEM-COUNTS is t and current item is
+new or immortal.  Will not move from virtual to ordinary feed
+tree or vice versa if optional argument DONT-WRAP-TREES is non-nil."
   (interactive)
-  (newsticker--treeview-restore-buffers)
+  (newsticker--treeview-restore-layout)
   (newsticker--treeview-list-clear-highlight)
-  (catch 'found
-    (let ((index (newsticker-treeview-next-item)))
-      (while t
-        (save-current-buffer
-          (set-buffer (newsticker--treeview-list-buffer))
-          (forward-line 1)
-          (when (eobp)
-            (forward-line -1)
-            (throw 'found nil)))
-        (when (memq (newsticker--age
-                     (newsticker--treeview-get-selected-item)) '(new immortal))
-            (newsticker-treeview-show-item)
-            (throw 'found t))))))
+  (unless (catch 'found
+            (let ((move (not current-item-counts)))
+              (while t
+                (save-current-buffer
+                  (set-buffer (newsticker--treeview-list-buffer))
+                  (when move (forward-line 1)
+                        (when (eobp)
+                          (forward-line -1)
+                          (throw 'found nil))))
+                (when (memq (newsticker--age
+                             (newsticker--treeview-get-selected-item))
+                            '(new immortal))
+                  (newsticker-treeview-show-item)
+                  (throw 'found t))
+                (setq move t))))
+    (let ((wrap-trees (not dont-wrap-trees)))
+      (when (or (newsticker-treeview-next-feed t)
+                (and wrap-trees (newsticker--treeview-first-feed)))
+        (newsticker-treeview-next-new-or-immortal-item t t)))))
 
 (defun newsticker-treeview-prev-new-or-immortal-item ()
-  "Move to previous new or immortal item."
+  "Move to previous new or immortal item.
+Will move to previous feed until an item is found."
   (interactive)
-  (newsticker--treeview-restore-buffers)
+  (newsticker--treeview-restore-layout)
   (newsticker--treeview-list-clear-highlight)
-  (catch 'found
-    (let ((index (newsticker-treeview-next-item)))
-      (while t
-        (save-current-buffer
-          (set-buffer (newsticker--treeview-list-buffer))
-          (forward-line -1)
-          (when (bobp)
-            (throw 'found nil)))
-        (when (memq (newsticker--age
-                     (newsticker--treeview-get-selected-item)) '(new immortal))
-            (newsticker-treeview-show-item)
-            (throw 'found t))))))
+  (unless (catch 'found
+            (while t
+              (save-current-buffer
+                (set-buffer (newsticker--treeview-list-buffer))
+                (when (bobp)
+                  (throw 'found nil))
+                (forward-line -1))
+              (when (memq (newsticker--age
+                           (newsticker--treeview-get-selected-item))
+                          '(new immortal))
+                (newsticker-treeview-show-item)
+                (throw 'found t))
+                (when (bobp)
+                  (throw 'found nil))))
+    (when (newsticker-treeview-prev-feed t)
+      (set-buffer (newsticker--treeview-list-buffer))
+      (goto-char (point-max))
+      (newsticker-treeview-prev-new-or-immortal-item))))
 
 (defun newsticker--treeview-get-selected-item ()
   "Return item that is currently selected in list buffer."
-  (save-excursion
-    (set-buffer (newsticker--treeview-list-buffer))
+  (with-current-buffer (newsticker--treeview-list-buffer)
     (beginning-of-line)
     (get-text-property (point) :nt-item)))
 
@@ -1367,15 +1416,17 @@ Move to next item unless DONT-PROCEED is non-nil."
   (when item
     (setcar (nthcdr 4 item) new-age)
     ;; clean up ticker FIXME
-    ))
+    )
+  (newsticker--cache-save-feed
+   (newsticker--cache-get-feed (intern newsticker--treeview-current-feed)))
+  (newsticker--treeview-tree-do-update-tags newsticker--treeview-vfeed-tree))
 
 (defun newsticker-treeview-mark-list-items-old ()
   "Mark all listed items as old."
   (interactive)
   (let ((current-feed (or newsticker--treeview-current-feed
                           newsticker--treeview-current-vfeed)))
-    (save-excursion
-      (set-buffer (newsticker--treeview-list-buffer))
+    (with-current-buffer (newsticker--treeview-list-buffer)
       (goto-char (point-min))
       (while (not (eobp))
         (let ((item (get-text-property (point) :nt-item)))
@@ -1402,15 +1453,11 @@ Move to next item unless DONT-PROCEED is non-nil."
 
 (defun newsticker--treeview-set-current-node (node)
   "Make NODE the current node."
-  (save-excursion
-    (set-buffer (newsticker--treeview-tree-buffer))
+  (with-current-buffer (newsticker--treeview-tree-buffer)
     (setq newsticker--treeview-current-node-id
           (widget-get node :nt-id))
     (setq newsticker--treeview-current-feed (widget-get node :nt-feed))
     (setq newsticker--treeview-current-vfeed (widget-get node :nt-vfeed))
-    ;;(message "newsticker--treeview-set-current-node %s/%s" (widget-get node :tag)
-    ;;       (widget-get node :nt-id))
-    ;;       node)
     (newsticker--treeview-tree-update-highlight)))
 
 (defun newsticker--treeview-get-first-child (node)
@@ -1512,37 +1559,59 @@ is activated."
           (node
            (widget-apply-action node)))))
 
-(defun newsticker-treeview-next-feed ()
-  "Move to next feed."
+(defun newsticker--treeview-first-feed ()
+  "Jump to the depth-first feed in the `newsticker-groups' tree."
+  (newsticker-treeview-jump
+   (car (reverse (newsticker--group-get-feeds newsticker-groups t)))))
+
+(defun newsticker-treeview-next-feed (&optional stay-in-tree)
+  "Move to next feed.
+Optional argument STAY-IN-TREE prevents moving from real feed
+tree to virtual feed tree or vice versa.
+Return t if a new feed was activated, nil otherwise."
   (interactive)
-  (newsticker--treeview-restore-buffers)
-  (let ((cur (newsticker--treeview-get-current-node)))
-    ;;(message "newsticker-treeview-next-feed from %s"
-    ;;       (widget-get cur :tag))
-    (if cur
-        (let ((new (or (newsticker--treeview-get-next-sibling cur)
-                       (newsticker--treeview-get-next-uncle cur)
-                       (newsticker--treeview-get-other-tree))))
-          (newsticker--treeview-activate-node new))
-      (newsticker--treeview-activate-node
-       (car (widget-get newsticker--treeview-feed-tree :children)))))
-  (newsticker--treeview-tree-update-highlight))
+  (newsticker--treeview-restore-layout)
+  (let ((cur (newsticker--treeview-get-current-node))
+        (new nil))
+    (setq new
+          (if cur
+              (or (newsticker--treeview-get-next-sibling cur)
+                  (newsticker--treeview-get-next-uncle cur)
+                  (and (not stay-in-tree)
+                       (newsticker--treeview-get-other-tree)))
+            (car (widget-get newsticker--treeview-feed-tree :children))))
+    (if new
+        (progn
+          (newsticker--treeview-activate-node new)
+          (newsticker--treeview-tree-update-highlight)
+          (not (eq new cur)))
+      nil)))
 
-(defun newsticker-treeview-prev-feed ()
-  "Move to previous feed."
+(defun newsticker-treeview-prev-feed (&optional stay-in-tree)
+  "Move to previous feed.
+Optional argument STAY-IN-TREE prevents moving from real feed
+tree to virtual feed tree or vice versa.
+Return t if a new feed was activated, nil otherwise."
   (interactive)
-  (newsticker--treeview-restore-buffers)
-  (let ((cur (newsticker--treeview-get-current-node)))
-    (message "newsticker-treeview-prev-feed from %s"
-             (widget-get cur :tag))
+  (newsticker--treeview-restore-layout)
+  (let ((cur (newsticker--treeview-get-current-node))
+        (new nil))
     (if cur
-        (let ((new (or (newsticker--treeview-get-prev-sibling cur)
-                       (newsticker--treeview-get-prev-uncle cur)
-                       (newsticker--treeview-get-other-tree))))
-          (newsticker--treeview-activate-node new t))
-      (newsticker--treeview-activate-node
-       (car (widget-get newsticker--treeview-feed-tree :children)) t)))
-  (newsticker--treeview-tree-update-highlight))
+      (progn
+        (setq new
+              (if cur
+                  (or (newsticker--treeview-get-prev-sibling cur)
+                      (newsticker--treeview-get-prev-uncle cur)
+                      (and (not stay-in-tree)
+                           (newsticker--treeview-get-other-tree)))
+                (car (widget-get newsticker--treeview-feed-tree :children))))
+        (if new
+            (progn
+              (newsticker--treeview-activate-node new t)
+              (newsticker--treeview-tree-update-highlight)
+              (not (eq new cur)))
+          nil))
+      nil)))
 
 (defun newsticker-treeview-next-page ()
   "Scroll item buffer."
@@ -1566,8 +1635,7 @@ is activated."
         (newsticker--treeview-unfold-node group-name))
       (setq node (newsticker--treeview-get-node-of-feed feed-name)))
     (when node
-      (save-excursion
-        (set-buffer (newsticker--treeview-tree-buffer))
+      (with-current-buffer (newsticker--treeview-tree-buffer)
         (widget-put node :nt-selected t)
         (widget-apply-action node)
         (newsticker--treeview-set-current-node node)))))
@@ -1578,9 +1646,10 @@ is activated."
    (list (let ((completion-ignore-case t))
            (completing-read
             "Jump to feed: "
-            (mapcar 'car (append newsticker-url-list
-                                 newsticker-url-list-defaults))
-                            nil t))))
+            (append '("new" "obsolete" "immortal" "all")
+                    (mapcar 'car (append newsticker-url-list
+                                         newsticker-url-list-defaults)))
+            nil t))))
   (newsticker--treeview-unfold-node feed-name))
 
 ;; ======================================================================
@@ -1669,7 +1738,7 @@ return a nested list."
                   (let ((subfeeds (newsticker--group-get-feeds n t)))
                     (when subfeeds
                       (setq result (append subfeeds result)))))))
-          group)
+          (cdr group))
     result))
 
 (defun newsticker-group-add-group (name parent)
@@ -1823,8 +1892,31 @@ Remove obsolete feeds as well."
     (define-key menu [newsticker-treeview-mark-list-items-old]
       (list 'menu-item "Mark all items old"
             'newsticker-treeview-mark-list-items-old))
+    (define-key menu [newsticker-treeview-mark-item-old]
+      (list 'menu-item "Mark current item old"
+            'newsticker-treeview-mark-item-old))
+    (define-key menu [newsticker-treeview-toggle-item-immortal]
+      (list 'menu-item "Mark current item immortal (toggle)"
+            'newsticker-treeview-toggle-item-immortal))
+    (define-key menu [newsticker-treeview-get-news]
+      (list 'menu-item "Get news for current feed"
+            'newsticker-treeview-get-news))
     menu)
-  "Map for newsticker tree menu.")
+  "Map for newsticker list menu.")
+
+(defvar newsticker-treeview-item-menu
+  (let ((menu (make-sparse-keymap "Newsticker Item")))
+    (define-key menu [newsticker-treeview-mark-item-old]
+      (list 'menu-item "Mark current item old"
+            'newsticker-treeview-mark-item-old))
+    (define-key menu [newsticker-treeview-toggle-item-immortal]
+      (list 'menu-item "Mark current item immortal (toggle)"
+            'newsticker-treeview-toggle-item-immortal))
+    (define-key menu [newsticker-treeview-get-news]
+      (list 'menu-item "Get news for current feed"
+            'newsticker-treeview-get-news))
+    menu)
+  "Map for newsticker item menu.")
 
 (defvar newsticker-treeview-mode-map
   (let ((map (make-sparse-keymap 'newsticker-treeview-mode-map)))
@@ -1889,9 +1981,15 @@ Remove obsolete feeds as well."
   (define-key newsticker-treeview-list-mode-map [down-mouse-3]
     newsticker-treeview-list-menu))
 
+(define-derived-mode newsticker-treeview-item-mode newsticker-treeview-mode
+  "Item"
+  (define-key newsticker-treeview-item-mode-map [down-mouse-3]
+    newsticker-treeview-item-menu))
+
 (defun newsticker-treeview-tree-click (event)
   "Handle click EVENT on a tag in the newsticker tree."
   (interactive "e")
+  (newsticker--treeview-restore-layout)
   (save-excursion
     (switch-to-buffer (window-buffer (posn-window (event-end event))))
     (newsticker-treeview-tree-do-click (posn-point (event-end event)))))
@@ -1900,10 +1998,9 @@ Remove obsolete feeds as well."
   "Actually handle click event.
 POS gives the position where EVENT occurred."
   (interactive)
-  (unless pos (setq pos (point)))
-  (let ((pos (or pos (point)))
-        (nt-id (get-text-property pos :nt-id))
-        (item (get-text-property pos :nt-item)))
+  (let* ((pos (or pos (point)))
+         (nt-id (get-text-property pos :nt-id))
+         (item (get-text-property pos :nt-item)))
     (cond (item
            ;; click in list buffer
            (newsticker-treeview-show-item))
@@ -1918,7 +2015,7 @@ POS gives the position where EVENT occurred."
                (newsticker--treeview-set-current-node w))))))
   (newsticker--treeview-tree-update-highlight))
 
-(defun newsticker--treeview-restore-buffers ()
+(defun newsticker--treeview-restore-layout ()
   "Restore treeview buffers."
   (catch 'error
     (dotimes (i 3)
@@ -1977,7 +2074,6 @@ POS gives the position where EVENT occurred."
   (when newsticker--treeview-current-feed
     (newsticker-get-news newsticker--treeview-current-feed)))
 
-(provide 'newsticker-treeview)
+(provide 'newst-treeview)
 
-;; arch-tag: 5dbaff48-1f3e-4fc6-8ebd-e966fc90d2d4
 ;;; newst-treeview.el ends here