Custom function for next / previous month in agend

2019-08-03 00:13发布

问题:

I'm looking for a way to select next or previous month in both agenda view and the calendar. I've written a concept/prototype function (below), but it doesn't calculate the next or previous months and I would also need to rewrite the function every month.

The date format for the org-agenda-month-view is different than the date format for calendar-other-month. Further down below are some functions that are related to what I'm trying to accomplish -- e.g., calendar already has the ability to move forward or backward by month.

I think what may be needed is a function that identifies the month being viewed and then adds plus-or-minus one month (in the proper format) when hitting the next or previous button.

(defun lawlist-org-agenda-view-mode-dispatch ()
  "Select the month in agenda view."
  (interactive)
  (message "View: [7] JUL | [8] AUG | [9] SEP | [o]CT | [n]OV | [d]EC ")
  (let ((a (read-char-exclusive)))
    (case a
      (?7
        (org-agenda nil "a")
        (org-agenda-month-view 201307)
        (calendar)
        (calendar-other-month 7 2013)
        (lawlist-org-agenda-view-mode-dispatch))
      (?8
        (org-agenda nil "a")
        (org-agenda-month-view 201308)
        (calendar)
        (calendar-other-month 8 2013)
        (lawlist-org-agenda-view-mode-dispatch))
      (?9
        (org-agenda nil "a")
        (org-agenda-month-view 201309)
        (calendar)
        (calendar-other-month 9 2013)
        (lawlist-org-agenda-view-mode-dispatch))
      (?o
        (org-agenda nil "a")
        (org-agenda-month-view 201310)
        (calendar)
        (calendar-other-month 10 2013)
        (lawlist-org-agenda-view-mode-dispatch))
      (?n
        (org-agenda nil "a")
        (org-agenda-month-view 201311)
        (calendar)
        (calendar-other-month 11 2013)
        (lawlist-org-agenda-view-mode-dispatch))
      (?d
        (org-agenda nil "a")
        (org-agenda-month-view 201312)
        (calendar)
        (calendar-other-month 12 2013)
        (lawlist-org-agenda-view-mode-dispatch))
      (?q (message "Abort"))
      (otherwise (error "Either press \"q\" to quit, or select another option." )))))

Here are some related functions I've extracted from cal-move.el and calendar.el:

(defun calendar-other-month (month year &optional event)
  "Display a three-month calendar centered around MONTH and YEAR.
EVENT is an event like `last-nonmenu-event'."
  (interactive (let ((event (list last-nonmenu-event)))
                 (append (calendar-read-date 'noday) event)))
  (save-selected-window
    (and event
         (setq event (event-start event))
         (select-window (posn-window event)))
    (unless (and (= month displayed-month)
                 (= year displayed-year))
      (let ((old-date (calendar-cursor-to-date))
            (today (calendar-current-date)))
        (calendar-generate-window month year)
        (calendar-cursor-to-visible-date
         (cond
          ((calendar-date-is-visible-p old-date) old-date)
          ((calendar-date-is-visible-p today) today)
          (t (list month 1 year))))))))

;;;###cal-autoload
(defun calendar-forward-month (arg)
  "Move the cursor forward ARG months.
Movement is backward if ARG is negative."
  (interactive "p")
  (calendar-cursor-to-nearest-date)
  (let* ((cursor-date (calendar-cursor-to-date t))
         (month (calendar-extract-month cursor-date))
         (day (calendar-extract-day cursor-date))
         (year (calendar-extract-year cursor-date))
         (last (progn
                 (calendar-increment-month month year arg)
                 (calendar-last-day-of-month month year)))
         (day (min last day))
         ;; Put the new month on the screen, if needed, and go to the new date.
         (new-cursor-date (list month day year)))
    (if (not (calendar-date-is-visible-p new-cursor-date))
        (calendar-other-month month year))
    (calendar-cursor-to-visible-date new-cursor-date))
  (run-hooks 'calendar-move-hook))

;;;###cal-autoload
(defun calendar-backward-month (arg)
  "Move the cursor backward by ARG months.
Movement is forward if ARG is negative."
  (interactive "p")
  (calendar-forward-month (- arg)))

;;;###cal-autoload
(defun calendar-forward-year (arg)
  "Move the cursor forward by ARG years.
Movement is backward if ARG is negative."
  (interactive "p")
  (calendar-forward-month (* 12 arg)))

;;;###cal-autoload
(defun calendar-backward-year (arg)
  "Move the cursor backward ARG years.
Movement is forward is ARG is negative."
  (interactive "p")
  (calendar-forward-month (* -12 arg)))

;;;###cal-autoload
(defun calendar-scroll-left (&optional arg event)
  "Scroll the displayed calendar left by ARG months.
If ARG is negative the calendar is scrolled right.  Maintains the relative
position of the cursor with respect to the calendar as well as possible.
EVENT is an event like `last-nonmenu-event'."
  (interactive (list (prefix-numeric-value current-prefix-arg)
                     last-nonmenu-event))
  (unless arg (setq arg 1))
  (save-selected-window
    ;; Nil if called from menu-bar.
    (if (setq event (event-start event)) (select-window (posn-window event)))
    (calendar-cursor-to-nearest-date)
    (unless (zerop arg)
      (let ((old-date (calendar-cursor-to-date))
            (today (calendar-current-date))
            (month displayed-month)
            (year displayed-year))
        (calendar-increment-month month year arg)
        (calendar-generate-window month year)
        (calendar-cursor-to-visible-date
         (cond
          ((calendar-date-is-visible-p old-date) old-date)
          ((calendar-date-is-visible-p today) today)
          (t (list month 1 year))))))
    (run-hooks 'calendar-move-hook)))

(define-obsolete-function-alias
  'scroll-calendar-left 'calendar-scroll-left "23.1")

;;;###cal-autoload
(defun calendar-scroll-right (&optional arg event)
  "Scroll the displayed calendar window right by ARG months.
If ARG is negative the calendar is scrolled left.  Maintains the relative
position of the cursor with respect to the calendar as well as possible.
EVENT is an event like `last-nonmenu-event'."
  (interactive (list (prefix-numeric-value current-prefix-arg)
                     last-nonmenu-event))
  (calendar-scroll-left (- (or arg 1)) event))

(define-obsolete-function-alias
  'scroll-calendar-right 'calendar-scroll-right "23.1")

回答1:

Here's my take:

(defvar lawlist-month)

(defun lawlist-org-agenda-view-mode-dispatch ()
  "Select the month in agenda view."
  (interactive)
  (message "View: [1-9] [o]CT [n]OV [d]EC, j(next), k(prev).")
  (let* ((a (read-char-exclusive))
         (month (case a
                  (?o 10)
                  (?n 11)
                  (?d 12)
                  (?j (or (and lawlist-month (mod (1+ lawlist-month) 12)) 1))
                  (?k (or (and lawlist-month (mod (1- lawlist-month) 12)) 1))
                  (t (and (> a ?0) (<= a ?9) (- a ?0))))))
    (if (setq lawlist-month month)
        (let ((year (nth 5 (decode-time (current-time)))))
           (org-agenda nil "a")
           (org-agenda-month-view
            (read (format "%d%02d" year month)))
           (calendar)
           (calendar-other-month month year)
           (lawlist-org-agenda-view-mode-dispatch))
      (message "Aborted"))))

It still misses some functionality like saving the window configuration and recovering on abort.

UPD

The updated code can be found in this gist. I've added other years besides current, with support for j/k, as well as h/l for years.