From 7b525c0b1e7f008f7390648db7e6a8983119617b Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Sat, 12 Nov 2022 14:20:31 +0100 Subject: [PATCH 001/104] Remove timer from page prefetching function --- lisp/pdf-cache.el | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lisp/pdf-cache.el b/lisp/pdf-cache.el index 9c25e07d..5872c69c 100644 --- a/lisp/pdf-cache.el +++ b/lisp/pdf-cache.el @@ -451,10 +451,7 @@ WINDOW and IMAGE-WIDTH decide the page and scale of the final image." (image-size (pdf-view-create-page page)) (pdf-util-debug (message "Prefetched page %s." page)) - ;; Avoid max-lisp-eval-depth - (run-with-timer - 0.001 nil - #'pdf-cache--prefetch-pages window image-width))))))) + (pdf-cache--prefetch-pages window image-width))))))) (condition-case err (pdf-info-renderpage page image-width) (error From 84048f6cc8d9a8e35b34bddda4e9d12f8acd692b Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Sun, 22 Jan 2023 22:32:05 +0100 Subject: [PATCH 002/104] Don't assume that bookmark jump will display the buffer As a result don't assume that there is a window displaying the buffer. Instead of `bookmark-after-jump-hook` use `window-buffer-change-functions` so that the buffer is actually displayed when the code assuming a window is run. --- lisp/pdf-view.el | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/lisp/pdf-view.el b/lisp/pdf-view.el index 96b4237c..e1b9c089 100644 --- a/lisp/pdf-view.el +++ b/lisp/pdf-view.el @@ -1696,28 +1696,28 @@ The optional, boolean args exclude certain attributes." "The bookmark handler-function interface for bookmark BMK. See also `pdf-view-bookmark-make-record'." - (let ((page (bookmark-prop-get bmk 'page)) - (slice (bookmark-prop-get bmk 'slice)) - (size (bookmark-prop-get bmk 'size)) - (origin (bookmark-prop-get bmk 'origin)) - (file (bookmark-prop-get bmk 'filename)) - (show-fn-sym (make-symbol "pdf-view-bookmark-after-jump-hook"))) + (let* ((page (bookmark-prop-get bmk 'page)) + (slice (bookmark-prop-get bmk 'slice)) + (size (bookmark-prop-get bmk 'size)) + (origin (bookmark-prop-get bmk 'origin)) + (file (bookmark-prop-get bmk 'filename)) + (buf (or (find-buffer-visiting file) + (find-file-noselect file))) + (show-fn-sym (make-symbol "pdf-show-buffer-function"))) (fset show-fn-sym - (lambda () - (remove-hook 'bookmark-after-jump-hook show-fn-sym) - (unless (derived-mode-p 'pdf-view-mode) - (pdf-view-mode)) - (with-selected-window - (or (get-buffer-window (current-buffer) 0) - (selected-window)) + (lambda (win) + (with-current-buffer buf + (remove-hook 'window-buffer-change-functions show-fn-sym t) + (unless (derived-mode-p 'pdf-view-mode) + (pdf-view-mode)) (when size (setq-local pdf-view-display-size size)) (when slice (apply 'pdf-view-set-slice slice)) (when (numberp page) - (pdf-view-goto-page page)) + (pdf-view-goto-page page win)) (when origin - (let ((size (pdf-view-image-size t))) + (let ((size (pdf-view-image-size t win))) (image-set-window-hscroll (round (/ (* (car origin) (car size)) (frame-char-width)))) @@ -1726,9 +1726,8 @@ See also `pdf-view-bookmark-make-record'." (if pdf-view-have-image-mode-pixel-vscroll 1 (frame-char-height)))))))))) - (add-hook 'bookmark-after-jump-hook show-fn-sym) - (set-buffer (or (find-buffer-visiting file) - (find-file-noselect file))))) + (set-buffer buf) + (add-hook 'window-buffer-change-functions show-fn-sym nil t))) (defun pdf-view-bookmark-jump (bmk) "Switch to bookmark BMK. From e6dfe00841a15403544cb6fd79105431dc1d58a9 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Tue, 24 Jan 2023 18:45:05 +0100 Subject: [PATCH 003/104] Accommodate Emacs 26 If `window-buffer-change-functions` isn't bound fallback to `bookmark-after-jump-hook`. The window argument for the inserted hook is optional to accommodate both cases. --- lisp/pdf-view.el | 52 ++++++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/lisp/pdf-view.el b/lisp/pdf-view.el index e1b9c089..115c00e8 100644 --- a/lisp/pdf-view.el +++ b/lisp/pdf-view.el @@ -1703,31 +1703,39 @@ See also `pdf-view-bookmark-make-record'." (file (bookmark-prop-get bmk 'filename)) (buf (or (find-buffer-visiting file) (find-file-noselect file))) + (buf-chg-fns-p (boundp 'window-buffer-change-functions)) + (hook (if buf-chg-fns-p + 'window-buffer-change-functions + 'bookmark-after-jump-hook)) (show-fn-sym (make-symbol "pdf-show-buffer-function"))) (fset show-fn-sym - (lambda (win) - (with-current-buffer buf - (remove-hook 'window-buffer-change-functions show-fn-sym t) - (unless (derived-mode-p 'pdf-view-mode) - (pdf-view-mode)) - (when size - (setq-local pdf-view-display-size size)) - (when slice - (apply 'pdf-view-set-slice slice)) - (when (numberp page) - (pdf-view-goto-page page win)) - (when origin - (let ((size (pdf-view-image-size t win))) - (image-set-window-hscroll - (round (/ (* (car origin) (car size)) - (frame-char-width)))) - (image-set-window-vscroll - (round (/ (* (cdr origin) (cdr size)) - (if pdf-view-have-image-mode-pixel-vscroll - 1 - (frame-char-height)))))))))) + (lambda (&optional win) + (when (eq buf (current-buffer)) + (with-selected-window + (or win + (get-buffer-window buf 0) + (selected-window)) + (remove-hook hook show-fn-sym buf-chg-fns-p) + (unless (derived-mode-p 'pdf-view-mode) + (pdf-view-mode)) + (when size + (setq-local pdf-view-display-size size)) + (when slice + (apply 'pdf-view-set-slice slice)) + (when (numberp page) + (pdf-view-goto-page page win)) + (when origin + (let ((size (pdf-view-image-size t win))) + (image-set-window-hscroll + (round (/ (* (car origin) (car size)) + (frame-char-width)))) + (image-set-window-vscroll + (round (/ (* (cdr origin) (cdr size)) + (if pdf-view-have-image-mode-pixel-vscroll + 1 + (frame-char-height))))))))))) (set-buffer buf) - (add-hook 'window-buffer-change-functions show-fn-sym nil t))) + (add-hook hook show-fn-sym nil buf-chg-fns-p))) (defun pdf-view-bookmark-jump (bmk) "Switch to bookmark BMK. From 961e41c263191750c3d14cccf20b689672ea55b6 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Mon, 6 Mar 2023 19:12:16 +0100 Subject: [PATCH 004/104] Use window-buffer-change-functions only if buffer has not window --- lisp/pdf-view.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lisp/pdf-view.el b/lisp/pdf-view.el index 115c00e8..56c47d7c 100644 --- a/lisp/pdf-view.el +++ b/lisp/pdf-view.el @@ -1704,7 +1704,7 @@ See also `pdf-view-bookmark-make-record'." (buf (or (find-buffer-visiting file) (find-file-noselect file))) (buf-chg-fns-p (boundp 'window-buffer-change-functions)) - (hook (if buf-chg-fns-p + (hook (if (and buf-chg-fns-p (not (get-buffer-window buf))) 'window-buffer-change-functions 'bookmark-after-jump-hook)) (show-fn-sym (make-symbol "pdf-show-buffer-function"))) From 1767957f47135efdefd402423ddb956970d40660 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Tue, 21 Mar 2023 18:29:38 +0100 Subject: [PATCH 005/104] Save the same bookmark if it wasn't restored --- lisp/pdf-view.el | 79 ++++++++++++++++++++++++++---------------------- 1 file changed, 43 insertions(+), 36 deletions(-) diff --git a/lisp/pdf-view.el b/lisp/pdf-view.el index 56c47d7c..5e75d98d 100644 --- a/lisp/pdf-view.el +++ b/lisp/pdf-view.el @@ -1667,40 +1667,39 @@ the selection styles." ;; * Bookmark + Register Integration ;; * ================================================================== * +(defvar pdf-view--bookmark-to-restore nil + "Used to hold a bookmark that is still to be restored.") (defun pdf-view-bookmark-make-record (&optional no-page no-slice no-size no-origin) ;; TODO: add NO-PAGE, NO-SLICE, NO-SIZE, NO-ORIGIN to the docstring. "Create a bookmark PDF record. The optional, boolean args exclude certain attributes." - (let ((displayed-p (eq (current-buffer) - (window-buffer)))) - (cons (buffer-name) - (append (bookmark-make-record-default nil t 1) - `(,(unless no-page - (cons 'page (pdf-view-current-page))) - ,(unless no-slice - (cons 'slice (and displayed-p - (pdf-view-current-slice)))) - ,(unless no-size - (cons 'size pdf-view-display-size)) - ,(unless no-origin - (cons 'origin - (and displayed-p - (let ((edges (pdf-util-image-displayed-edges nil t))) - (pdf-util-scale-pixel-to-relative - (cons (car edges) (cadr edges)) nil t))))) - (handler . pdf-view-bookmark-jump-handler)))))) + (or pdf-view--bookmark-to-restore + (let ((displayed-p (eq (current-buffer) + (window-buffer)))) + (cons (buffer-name) + (append (bookmark-make-record-default nil t 1) + `(,(unless no-page + (cons 'page (pdf-view-current-page))) + ,(unless no-slice + (cons 'slice (and displayed-p + (pdf-view-current-slice)))) + ,(unless no-size + (cons 'size pdf-view-display-size)) + ,(unless no-origin + (cons 'origin + (and displayed-p + (let ((edges (pdf-util-image-displayed-edges nil t))) + (pdf-util-scale-pixel-to-relative + (cons (car edges) (cadr edges)) nil t))))) + (handler . pdf-view-bookmark-jump-handler))))))) ;;;###autoload (defun pdf-view-bookmark-jump-handler (bmk) "The bookmark handler-function interface for bookmark BMK. See also `pdf-view-bookmark-make-record'." - (let* ((page (bookmark-prop-get bmk 'page)) - (slice (bookmark-prop-get bmk 'slice)) - (size (bookmark-prop-get bmk 'size)) - (origin (bookmark-prop-get bmk 'origin)) - (file (bookmark-prop-get bmk 'filename)) + (let* ((file (bookmark-prop-get bmk 'filename)) (buf (or (find-buffer-visiting file) (find-file-noselect file))) (buf-chg-fns-p (boundp 'window-buffer-change-functions)) @@ -1718,23 +1717,31 @@ See also `pdf-view-bookmark-make-record'." (remove-hook hook show-fn-sym buf-chg-fns-p) (unless (derived-mode-p 'pdf-view-mode) (pdf-view-mode)) - (when size + (when-let ((size (bookmark-prop-get + pdf-view--bookmark-to-restore 'size))) (setq-local pdf-view-display-size size)) - (when slice + (when-let ((slice (bookmark-prop-get + pdf-view--bookmark-to-restore 'slice))) (apply 'pdf-view-set-slice slice)) - (when (numberp page) + (when-let ((page (bookmark-prop-get + pdf-view--bookmark-to-restore 'page)) + ((numberp page))) (pdf-view-goto-page page win)) - (when origin - (let ((size (pdf-view-image-size t win))) - (image-set-window-hscroll - (round (/ (* (car origin) (car size)) - (frame-char-width)))) - (image-set-window-vscroll - (round (/ (* (cdr origin) (cdr size)) - (if pdf-view-have-image-mode-pixel-vscroll - 1 - (frame-char-height))))))))))) + (when-let ((origin (bookmark-prop-get + pdf-view--bookmark-to-restore 'origin)) + (size (pdf-view-image-size t win))) + + (image-set-window-hscroll + (round (/ (* (car origin) (car size)) + (frame-char-width)))) + (image-set-window-vscroll + (round (/ (* (cdr origin) (cdr size)) + (if pdf-view-have-image-mode-pixel-vscroll + 1 + (frame-char-height)))))) + (setq-local pdf-view--bookmark-to-restore nil))))) (set-buffer buf) + (setq-local pdf-view--bookmark-to-restore bmk) (add-hook hook show-fn-sym nil buf-chg-fns-p))) (defun pdf-view-bookmark-jump (bmk) From 99e0201c94c9feac7eaa5e83999e9cd3653c7e6c Mon Sep 17 00:00:00 2001 From: Daniel Nicolai Date: Sun, 1 May 2022 12:44:09 +0200 Subject: [PATCH 006/104] Implement `pdf-view-roll-minor-mode` --- lisp/pdf-annot.el | 3 +- lisp/pdf-isearch.el | 3 +- lisp/pdf-links.el | 21 +++++++----- lisp/pdf-roll.el | 78 +++++++++++++++++++++++++++++++++++++++++++++ lisp/pdf-sync.el | 1 + lisp/pdf-tools.el | 1 + lisp/pdf-util.el | 7 ++-- lisp/pdf-view.el | 59 +++++++++++++++++++++++++--------- 8 files changed, 146 insertions(+), 27 deletions(-) create mode 100644 lisp/pdf-roll.el diff --git a/lisp/pdf-annot.el b/lisp/pdf-annot.el index 7c63dd2c..cd5a1251 100644 --- a/lisp/pdf-annot.el +++ b/lisp/pdf-annot.el @@ -999,7 +999,8 @@ other annotations." `("white" "steel blue" 0.35 ,@edges)) :map (pdf-view-apply-hotspot-functions window page size) - :width (car size)))) + :width (car size)) + (when pdf-view-roll-minor-mode page))) (pdf-util-scroll-to-edges (pdf-util-scale-relative-to-pixel (car edges))))))) diff --git a/lisp/pdf-isearch.el b/lisp/pdf-isearch.el index f5468eb5..20e2932e 100644 --- a/lisp/pdf-isearch.el +++ b/lisp/pdf-isearch.el @@ -743,7 +743,8 @@ MATCH-BG LAZY-FG LAZY-BG\)." occur-hack-p) (eq page (pdf-view-current-page))) (pdf-view-display-image - (pdf-view-create-image data :width width)))))))) + (pdf-view-create-image data :width width) + (when pdf-view-roll-minor-mode page)))))))) (pdf-info-renderpage-text-regions page width t nil nil `(,fg1 ,bg1 ,@(pdf-util-scale-pixel-to-relative diff --git a/lisp/pdf-links.el b/lisp/pdf-links.el index d54981e0..8ca62ab5 100644 --- a/lisp/pdf-links.el +++ b/lisp/pdf-links.el @@ -209,16 +209,20 @@ scroll the current page." (when (derived-mode-p 'pdf-view-mode) (when (> .page 0) (pdf-view-goto-page .page)) - (when .top + ;; TODO fix pdf-util-tooltip-arrow function for image-roll + ;; compatibility + + ;;(when .top ;; Showing the tooltip delays displaying the page for ;; some reason (sit-for/redisplay don't help), do it ;; later. - (run-with-idle-timer 0.001 nil - (lambda () - (when (window-live-p window) - (with-selected-window window - (when (derived-mode-p 'pdf-view-mode) - (pdf-util-tooltip-arrow .top))))))))))) + ;;(run-with-idle-timer 0.001 nil + ;;(lambda () + ;;(when (window-live-p window) + ;;(with-selected-window window + ;;(when (derived-mode-p 'pdf-view-mode) + ;;(pdf-util-tooltip-arrow .top))))))) + )))) (uri (funcall pdf-links-browse-uri-function .uri)) (t @@ -266,7 +270,8 @@ See `pdf-links-action-perform' for the interface." (pdf-view-current-page) (car size) image-data 'pdf-links-read-link-action)) (pdf-view-display-image - (create-image image-data (pdf-view-image-type) t)) + (create-image image-data (pdf-view-image-type) t) + (when pdf-view-roll-minor-mode (pdf-view-current-page))) (pdf-links-read-link-action--read-chars prompt alist)) (pdf-view-redisplay)))) diff --git a/lisp/pdf-roll.el b/lisp/pdf-roll.el new file mode 100644 index 00000000..df336651 --- /dev/null +++ b/lisp/pdf-roll.el @@ -0,0 +1,78 @@ +;;; pdf-roll.el --- Add continuous scroll. -*- lexical-binding: t -*- + +;; Copyright (C) 2013, 2014 Andreas Politz + +;; Author: Daniel Nicolai +;; Keywords: files, multimedia + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: +;; + +(require 'image-roll) + +(eval-when-compile + (require 'pdf-view)) + + +(defun pdf-scroll-page-sizes () + (let (s) + (dotimes (i (pdf-info-number-of-pages) (nreverse s)) + (push (pdf-view-desired-image-size (1+ i)) s)))) + +(defun pdf-scroll-set-redisplay-flag-function () + (setf (pdf-view-window-needs-redisplay) t)) + +(define-minor-mode pdf-view-roll-minor-mode + "If enabled display document on a virtual scroll providing +continuous scrolling." + :lighter " Continuous" + :keymap `((,(kbd "S-") . image-roll-scroll-screen-forward) + (,(kbd "S-") . image-roll-scroll-screen-backward)) + :version 28.1 + + (cond (pdf-view-roll-minor-mode + (setq-local image-roll-last-page (pdf-cache-number-of-pages) + image-roll-display-page-function 'pdf-view-display-page + image-roll-page-sizes-function 'pdf-scroll-page-sizes + image-roll-set-redisplay-flag-function 'pdf-scroll-set-redisplay-flag-function + + image-roll-center t) + + (add-hook 'window-configuration-change-hook 'image-roll--redisplay nil t) + + (remove-hook 'image-mode-new-window-functions + #'pdf-view-new-window-function t) + (add-hook 'image-mode-new-window-functions 'image-roll--new-window-function nil t) + + (let ((inhibit-read-only t)) + (erase-buffer) + (image-roll--new-window-function (list (selected-window)))) + (pdf-view-redisplay)) + (t + (remove-hook 'window-configuration-change-hook 'image-roll--redisplay t) + + (remove-hook 'image-mode-new-window-functions 'image-roll--new-window-function t) + (add-hook 'image-mode-new-window-functions + #'pdf-view-new-window-function nil t) + (let ((inhibit-read-only t)) + (erase-buffer) + (insert-file-contents-literally (buffer-file-name)) + (pdf-view-new-window-function (list (selected-window))) + (set-buffer-modified-p nil))))) + +(provide 'pdf-roll) + +;;; pdf-roll.el ends here diff --git a/lisp/pdf-sync.el b/lisp/pdf-sync.el index cfbc2a3b..af4fe4f9 100644 --- a/lisp/pdf-sync.el +++ b/lisp/pdf-sync.el @@ -645,6 +645,7 @@ Needs to have `pdf-sync-backward-debug-minor-mode' enabled." ;; * Forward search (TeX -> PDF) ;; * ================================================================== * +;; TODO adapt for `pdf-view-roll-minor-mode' (see pdf-scroll.el) (defun pdf-sync-forward-search (&optional line column) "Display the PDF location corresponding to LINE, COLUMN." (interactive) diff --git a/lisp/pdf-tools.el b/lisp/pdf-tools.el index 670792cb..43eb7cba 100644 --- a/lisp/pdf-tools.el +++ b/lisp/pdf-tools.el @@ -94,6 +94,7 @@ (require 'pdf-view) (require 'pdf-util) (require 'pdf-info) +(require 'pdf-roll) (require 'cus-edit) (require 'compile) (require 'cl-lib) diff --git a/lisp/pdf-util.el b/lisp/pdf-util.el index d0a123c0..4666cada 100644 --- a/lisp/pdf-util.el +++ b/lisp/pdf-util.el @@ -344,7 +344,8 @@ needed." (unless context-pixel (setq context-pixel 0)) (let* ((win (window-inside-pixel-edges)) - (image-width (car (pdf-view-image-size t))) + (image-width (car (pdf-view-image-size (unless pdf-view-roll-minor-mode + t)))) (image-left (* (frame-char-width) (window-hscroll))) (edges (pdf-util-translate @@ -384,7 +385,9 @@ Note: For versions of emacs before 27 this will return lines instead of pixels. This is because of a change that occurred to `image-mode' in 27." (pdf-util-assert-pdf-window) (let* ((win (window-inside-pixel-edges)) - (image-height (cdr (pdf-view-image-size t))) + (image-height (cdr (pdf-view-image-size + (unless pdf-view-roll-minor-mode + t)))) (image-top (window-vscroll nil t)) (edges (pdf-util-translate edges diff --git a/lisp/pdf-view.el b/lisp/pdf-view.el index 5e75d98d..2e6d28c6 100644 --- a/lisp/pdf-view.el +++ b/lisp/pdf-view.el @@ -33,6 +33,9 @@ (require 'bookmark) (require 'password-cache) +(eval-when-compile + (require 'image-roll)) + (declare-function cua-copy-region "cua-base") (declare-function pdf-tools-pdf-buffer-p "pdf-tools") @@ -809,7 +812,7 @@ to previous page only on typing DEL (ARG is nil)." (image-set-window-hscroll hscroll))) (image-scroll-down arg))) -(defun pdf-view-next-line-or-next-page (&optional arg) +(defun pdf-view--next-line-or-next-page (&optional arg) "Scroll upward by ARG lines if possible, else go to the next page. When `pdf-view-continuous' is non-nil, scrolling a line upward @@ -827,7 +830,13 @@ at the bottom edge of the page moves to the next page." (image-set-window-hscroll hscroll))) (image-next-line arg))) -(defun pdf-view-previous-line-or-previous-page (&optional arg) +(defun pdf-view-next-line-or-next-page (&optional arg) + (interactive "p") + (if pdf-view-roll-minor-mode + (image-roll-scroll-forward) + (pdf-view--next-line-or-next-page arg))) + +(defun pdf-view--previous-line-or-previous-page (&optional arg) "Scroll downward by ARG lines if possible, else go to the previous page. When `pdf-view-continuous' is non-nil, scrolling a line downward @@ -845,6 +854,12 @@ at the top edge of the page moves to the previous page." (image-set-window-hscroll hscroll))) (image-previous-line arg))) +(defun pdf-view-previous-line-or-previous-page (&optional arg) + (interactive "p") + (if pdf-view-roll-minor-mode + (image-roll-scroll-backward) + (pdf-view--previous-line-or-previous-page arg))) + (defun pdf-view-goto-label (label) "Go to the page corresponding to LABEL. @@ -1033,6 +1048,9 @@ See also `pdf-view-use-imagemagick'." (hotspots (pdf-view-apply-hotspot-functions window page size))) (pdf-view-create-image data + :margin (cons 0 (if pdf-view-roll-minor-mode + image-roll-vertical-margin + 0)) :width (car size) :rotation (or pdf-view--current-rotation 0) :map hotspots @@ -1069,11 +1087,14 @@ It is equal to \(LEFT . TOP\) of the current slice in pixel." (setf (pdf-view-window-needs-redisplay window) nil) (pdf-view-display-image (pdf-view-create-page page window) + (when pdf-view-roll-minor-mode page) window)) -(defun pdf-view-display-image (image &optional window inhibit-slice-p) +(defun pdf-view-display-image (image page &optional window inhibit-slice-p) ;; TODO: write documentation! - (let ((ol (pdf-view-current-overlay window))) + (let ((ol (if pdf-view-roll-minor-mode + (image-roll-page-overlay page) + (pdf-view-current-overlay window)))) (when (window-live-p (overlay-get ol 'window)) (let* ((size (image-size image t)) (slice (if (not inhibit-slice-p) @@ -1084,7 +1105,8 @@ It is equal to \(LEFT . TOP\) of the current slice in pixel." (car (image-size image))) (car (image-size image)))))) (setf (pdf-view-current-image window) image) - (move-overlay ol (point-min) (point-max)) + (unless pdf-view-roll-minor-mode + (move-overlay ol (point-min) (point-max))) ;; In case the window is wider than the image, center the image ;; horizontally. (overlay-put ol 'before-string @@ -1100,15 +1122,16 @@ It is equal to \(LEFT . TOP\) of the current slice in pixel." (pdf-util-scale slice size 'round)) image) image)) - (let* ((win (overlay-get ol 'window)) - (hscroll (image-mode-window-get 'hscroll win)) - (vscroll (image-mode-window-get 'vscroll win))) - ;; Reset scroll settings, in case they were changed. - (if hscroll (set-window-hscroll win hscroll)) - (if vscroll (set-window-vscroll - win vscroll pdf-view-have-image-mode-pixel-vscroll))))))) - -(defun pdf-view-redisplay (&optional window) + (unless pdf-view-roll-minor-mode + (let* ((win (overlay-get ol 'window)) + (hscroll (image-mode-window-get 'hscroll win)) + (vscroll (image-mode-window-get 'vscroll win))) + ;; Reset scroll settings, in case they were changed. + (if hscroll (set-window-hscroll win hscroll)) + (if vscroll (set-window-vscroll + win vscroll pdf-view-have-image-mode-pixel-vscroll)))))))) + +(defun pdf-view--redisplay (&optional window) "Redisplay page in WINDOW. If WINDOW is t, redisplay pages in all windows." @@ -1129,6 +1152,11 @@ If WINDOW is t, redisplay pages in all windows." (setf (pdf-view-window-needs-redisplay window) t))))) (force-mode-line-update))) +(defun pdf-view-redisplay (&optional window) + (if pdf-view-roll-minor-mode + (image-roll--redisplay window) + (pdf-view--redisplay window))) + (defun pdf-view-redisplay-pages (&rest pages) "Redisplay PAGES in all windows." (pdf-util-assert-pdf-buffer) @@ -1550,7 +1578,8 @@ This is more useful for commands like (pdf-info-renderpage-text-regions page width nil selection-style nil `(,(car colors) ,(cdr colors) ,@region))) - :width width)))) + :width width) + (when pdf-view-roll-minor-mode page)))) (defun pdf-view-kill-ring-save () "Copy the region to the `kill-ring'." From 5feef10f19049e9209ee0c9ee8f46eef8d1d1b53 Mon Sep 17 00:00:00 2001 From: Daniel Nicolai Date: Wed, 4 May 2022 00:08:39 +0200 Subject: [PATCH 007/104] Update names to adhere to Emacs coding conventions See https://github.com/dalanicolai/image-roll.el/issues/1 --- lisp/pdf-roll.el | 18 +++++++++--------- lisp/pdf-view.el | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lisp/pdf-roll.el b/lisp/pdf-roll.el index df336651..75afdfd8 100644 --- a/lisp/pdf-roll.el +++ b/lisp/pdf-roll.el @@ -27,12 +27,12 @@ (require 'pdf-view)) -(defun pdf-scroll-page-sizes () +(defun pdf-roll-page-sizes () (let (s) (dotimes (i (pdf-info-number-of-pages) (nreverse s)) (push (pdf-view-desired-image-size (1+ i)) s)))) -(defun pdf-scroll-set-redisplay-flag-function () +(defun pdf-roll-set-redisplay-flag-function () (setf (pdf-view-window-needs-redisplay) t)) (define-minor-mode pdf-view-roll-minor-mode @@ -46,25 +46,25 @@ continuous scrolling." (cond (pdf-view-roll-minor-mode (setq-local image-roll-last-page (pdf-cache-number-of-pages) image-roll-display-page-function 'pdf-view-display-page - image-roll-page-sizes-function 'pdf-scroll-page-sizes - image-roll-set-redisplay-flag-function 'pdf-scroll-set-redisplay-flag-function + image-roll-page-sizes-function 'pdf-roll-page-sizes + image-roll-set-redisplay-flag-function 'pdf-roll-set-redisplay-flag-function image-roll-center t) - (add-hook 'window-configuration-change-hook 'image-roll--redisplay nil t) + (add-hook 'window-configuration-change-hook 'image-roll-redisplay nil t) (remove-hook 'image-mode-new-window-functions #'pdf-view-new-window-function t) - (add-hook 'image-mode-new-window-functions 'image-roll--new-window-function nil t) + (add-hook 'image-mode-new-window-functions 'image-roll-new-window-function nil t) (let ((inhibit-read-only t)) (erase-buffer) - (image-roll--new-window-function (list (selected-window)))) + (image-roll-new-window-function (list (selected-window)))) (pdf-view-redisplay)) (t - (remove-hook 'window-configuration-change-hook 'image-roll--redisplay t) + (remove-hook 'window-configuration-change-hook 'image-roll-redisplay t) - (remove-hook 'image-mode-new-window-functions 'image-roll--new-window-function t) + (remove-hook 'image-mode-new-window-functions 'image-roll-new-window-function t) (add-hook 'image-mode-new-window-functions #'pdf-view-new-window-function nil t) (let ((inhibit-read-only t)) diff --git a/lisp/pdf-view.el b/lisp/pdf-view.el index 2e6d28c6..f90af604 100644 --- a/lisp/pdf-view.el +++ b/lisp/pdf-view.el @@ -1154,7 +1154,7 @@ If WINDOW is t, redisplay pages in all windows." (defun pdf-view-redisplay (&optional window) (if pdf-view-roll-minor-mode - (image-roll--redisplay window) + (image-roll-redisplay window) (pdf-view--redisplay window))) (defun pdf-view-redisplay-pages (&rest pages) From bc1844843f79538d33380146155084bc2da58ab8 Mon Sep 17 00:00:00 2001 From: Daniel Nicolai Date: Wed, 4 May 2022 09:44:51 +0200 Subject: [PATCH 008/104] Fix pdf-view-next-page function https://github.com/dalanicolai/image-roll.el/commit/ec8e13818c6932effd75ff0e02a51446e2ebfac5 --- lisp/pdf-view.el | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lisp/pdf-view.el b/lisp/pdf-view.el index f90af604..1d12e7cd 100644 --- a/lisp/pdf-view.el +++ b/lisp/pdf-view.el @@ -698,8 +698,10 @@ windows." Optional parameter N moves N pages forward." (interactive "p") - (pdf-view-goto-page (+ (pdf-view-current-page) - (or n 1)))) + (if pdf-view-roll-minor-mode + (image-roll-next-page n) + (pdf-view-goto-page (+ (pdf-view-current-page) + (or n 1))))) (defun pdf-view-previous-page (&optional n) "View the previous page in the PDF. From 28de3d6ff18c8413f9b810b8cc6301225731b47c Mon Sep 17 00:00:00 2001 From: Daniel Nicolai Date: Thu, 5 May 2022 15:23:42 +0200 Subject: [PATCH 009/104] Quick fix issue #2 (function does not respect ARG) --- lisp/pdf-view.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lisp/pdf-view.el b/lisp/pdf-view.el index 1d12e7cd..a051f4c3 100644 --- a/lisp/pdf-view.el +++ b/lisp/pdf-view.el @@ -835,7 +835,7 @@ at the bottom edge of the page moves to the next page." (defun pdf-view-next-line-or-next-page (&optional arg) (interactive "p") (if pdf-view-roll-minor-mode - (image-roll-scroll-forward) + (dotimes (_ (or arg 1)) (image-roll-scroll-forward)) (pdf-view--next-line-or-next-page arg))) (defun pdf-view--previous-line-or-previous-page (&optional arg) @@ -859,7 +859,7 @@ at the top edge of the page moves to the previous page." (defun pdf-view-previous-line-or-previous-page (&optional arg) (interactive "p") (if pdf-view-roll-minor-mode - (image-roll-scroll-backward) + (dotimes (_ (or arg 1)) (image-roll-scroll-backward)) (pdf-view--previous-line-or-previous-page arg))) (defun pdf-view-goto-label (label) From 2c9fdce1819b12a1ab9bf943fa14df448d38041f Mon Sep 17 00:00:00 2001 From: Daniel Nicolai Date: Mon, 9 May 2022 18:14:26 +0200 Subject: [PATCH 010/104] Fix isearch functionality --- lisp/pdf-isearch.el | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lisp/pdf-isearch.el b/lisp/pdf-isearch.el index 20e2932e..8ad33100 100644 --- a/lisp/pdf-isearch.el +++ b/lisp/pdf-isearch.el @@ -278,7 +278,8 @@ This is a Isearch interface function." ;; Don't get off track. (when (or (and (bobp) (not isearch-forward)) (and (eobp) isearch-forward)) - (goto-char (1+ (/ (buffer-size) 2)))) + (unless pdf-view-roll-minor-mode + (goto-char (1+ (/ (buffer-size) 2))))) ;; Signal success to isearch. (if isearch-forward (re-search-forward ".") @@ -347,7 +348,8 @@ This is a Isearch interface function." pdf-isearch-current-match nil pdf-isearch-current-matches nil pdf-isearch-current-parameter nil) - (goto-char (1+ (/ (buffer-size) 2)))) + (unless pdf-view-roll-minor-mode + (goto-char (1+ (/ (buffer-size) 2))))) (defun pdf-isearch-same-search-p (&optional ignore-search-string-p) "Return non-nil, if search parameter have not changed. @@ -742,6 +744,8 @@ MATCH-BG LAZY-FG LAZY-BG\)." (or isearch-mode occur-hack-p) (eq page (pdf-view-current-page))) + (when pdf-view-roll-minor-mode + (pdf-view-goto-page page)) (pdf-view-display-image (pdf-view-create-image data :width width) (when pdf-view-roll-minor-mode page)))))))) From 254950a9124c27cf3e98c65b1519bed7691b6771 Mon Sep 17 00:00:00 2001 From: Daniel Nicolai Date: Tue, 10 May 2022 15:12:38 +0200 Subject: [PATCH 011/104] Add mouse support --- lisp/pdf-roll.el | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lisp/pdf-roll.el b/lisp/pdf-roll.el index 75afdfd8..01f46049 100644 --- a/lisp/pdf-roll.el +++ b/lisp/pdf-roll.el @@ -60,7 +60,10 @@ continuous scrolling." (let ((inhibit-read-only t)) (erase-buffer) (image-roll-new-window-function (list (selected-window)))) - (pdf-view-redisplay)) + (pdf-view-redisplay) + + (define-key pdf-view-mode-map (kbd "") 'pdf-view-next-line-or-next-page) + (define-key pdf-view-mode-map (kbd "") 'pdf-view-previous-line-or-previous-page)) (t (remove-hook 'window-configuration-change-hook 'image-roll-redisplay t) @@ -71,7 +74,9 @@ continuous scrolling." (erase-buffer) (insert-file-contents-literally (buffer-file-name)) (pdf-view-new-window-function (list (selected-window))) - (set-buffer-modified-p nil))))) + (set-buffer-modified-p nil)) + (define-key pdf-view-mode-map (kbd "") 'mwheel-scroll) + (define-key pdf-view-mode-map (kbd "") 'mwheel-scroll)))) (provide 'pdf-roll) From b842782a0778d37c2cc5ebfc87e5f43a0e3d11fe Mon Sep 17 00:00:00 2001 From: Daniel Nicolai Date: Wed, 18 May 2022 13:46:00 +0200 Subject: [PATCH 012/104] Add pdf-annots-action-perform function (avy-like edit annots) --- lisp/pdf-annot.el | 61 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/lisp/pdf-annot.el b/lisp/pdf-annot.el index cd5a1251..d40210cd 100644 --- a/lisp/pdf-annot.el +++ b/lisp/pdf-annot.el @@ -1541,6 +1541,66 @@ At any given point of time, only one annotation can be in edit mode." (error "No annotation at this position")) (pdf-annot-edit-contents a))) +(defun pdf-annot-edit (annot) + "Activate ANNOT, for editing. + +Interactively, annot is read via `pdf-annot-read-annot'. +This function displays characters around the annots in the current +page and starts reading characters (ignoring case). After a +sufficient number of characters have been read, the corresponding +annot's annot is invoked. Additionally, SPC may be used to +scroll the current page." + (interactive + (list (or (pdf-annot-read-annot "Activate annot (SPC scrolls): ") + (error "No annot selected")))) + (pdf-annot-activate-annotation annot)) + +;; TODO 'merge' this function with `pdf-links-read-link-action' into a single +;; universal 'read-action' function (in `pdf-util'?) +(defun pdf-annot-read-annot (prompt) + "Using PROMPT, interactively read an annot-action. + +See `pdf-annot-edit' for the interface." + (pdf-util-assert-pdf-window) + (let* ((annots (pdf-annot-getannots (pdf-view-current-page) nil nil)) + (keys (pdf-links-read-link-action--create-keys + (length annots))) + (key-strings (mapcar (apply-partially 'apply 'string) + keys)) + (alist (cl-mapcar 'cons keys annots)) + (size (pdf-view-image-size)) + (colors (pdf-util-face-colors + 'pdf-links-read-link pdf-view-dark-minor-mode)) + (args (list + :foreground (car colors) + :background (cdr colors) + :formats + `((?c . ,(lambda (_edges) (pop key-strings))) + (?P . ,(number-to-string + (max 1 (* (cdr size) + pdf-links-convert-pointsize-scale))))) + :commands pdf-links-read-link-convert-commands + :apply (pdf-util-scale-relative-to-pixel + (mapcar (lambda (l) (cdr (assq 'edges l))) + annots))))) + ;; (print (plist-get args :apply)) + (unless annots + (error "No annots on this page")) + (unwind-protect + (let ((image-data + (pdf-cache-get-image + (pdf-view-current-page) + (car size) (car size) 'pdf-annot-read-annot))) + (unless image-data + (setq image-data (apply 'pdf-util-convert-page args )) + (pdf-cache-put-image + (pdf-view-current-page) + (car size) image-data 'pdf-annot-read-annot)) + (pdf-view-display-image + (create-image image-data (pdf-view-image-type) t) + (when pdf-view-roll-minor-mode (pdf-view-current-page))) + (pdf-links-read-link-action--read-chars prompt alist)) + (pdf-view-redisplay)))) ;; * ================================================================== * @@ -1583,6 +1643,7 @@ Currently supported properties are page, type, label, date and contents." (defvar pdf-annot-list-mode-map (let ((km (make-sparse-keymap))) + (define-key km (kbd "e") 'pdf-annot-edit) (define-key km (kbd "C-c C-f") #'pdf-annot-list-follow-minor-mode) (define-key km (kbd "SPC") #'pdf-annot-list-display-annotation-from-id) km)) From c17ce1dd74433d713dad7430445140d2f8039d45 Mon Sep 17 00:00:00 2001 From: Daniel Nicolai Date: Fri, 20 May 2022 12:52:30 +0200 Subject: [PATCH 013/104] Add history tracking function to image-roll-after-change-page-hook --- lisp/pdf-roll.el | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lisp/pdf-roll.el b/lisp/pdf-roll.el index 01f46049..7c3a15f6 100644 --- a/lisp/pdf-roll.el +++ b/lisp/pdf-roll.el @@ -57,6 +57,8 @@ continuous scrolling." #'pdf-view-new-window-function t) (add-hook 'image-mode-new-window-functions 'image-roll-new-window-function nil t) + (add-hook 'image-roll-after-change-page-hook 'pdf-history-before-change-page-hook nil t) + (let ((inhibit-read-only t)) (erase-buffer) (image-roll-new-window-function (list (selected-window)))) @@ -70,6 +72,10 @@ continuous scrolling." (remove-hook 'image-mode-new-window-functions 'image-roll-new-window-function t) (add-hook 'image-mode-new-window-functions #'pdf-view-new-window-function nil t) + + (remove-hook 'pdf-view-after-change-page-hook + #'pdf-history-before-change-page-hook t) + (let ((inhibit-read-only t)) (erase-buffer) (insert-file-contents-literally (buffer-file-name)) From b2b6c30b67c50c181da3d512762d7cc434c1ed85 Mon Sep 17 00:00:00 2001 From: Daniel Nicolai Date: Thu, 1 Dec 2022 11:03:40 +0100 Subject: [PATCH 014/104] Additionally remove overlays after erase-buffer --- lisp/pdf-roll.el | 2 ++ lisp/pdf-view.el | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lisp/pdf-roll.el b/lisp/pdf-roll.el index 7c3a15f6..64926219 100644 --- a/lisp/pdf-roll.el +++ b/lisp/pdf-roll.el @@ -61,6 +61,7 @@ continuous scrolling." (let ((inhibit-read-only t)) (erase-buffer) + (remove-overlays) (image-roll-new-window-function (list (selected-window)))) (pdf-view-redisplay) @@ -78,6 +79,7 @@ continuous scrolling." (let ((inhibit-read-only t)) (erase-buffer) + (remove-overlays) (insert-file-contents-literally (buffer-file-name)) (pdf-view-new-window-function (list (selected-window))) (set-buffer-modified-p nil)) diff --git a/lisp/pdf-view.el b/lisp/pdf-view.el index a051f4c3..1955764f 100644 --- a/lisp/pdf-view.el +++ b/lisp/pdf-view.el @@ -1709,7 +1709,7 @@ The optional, boolean args exclude certain attributes." (let ((displayed-p (eq (current-buffer) (window-buffer)))) (cons (buffer-name) - (append (bookmark-make-record-default nil t 1) + (append (bookmark-make-record-default nil t (if pdf-view-roll-minor-mode (point) 1)) `(,(unless no-page (cons 'page (pdf-view-current-page))) ,(unless no-slice @@ -1717,7 +1717,7 @@ The optional, boolean args exclude certain attributes." (pdf-view-current-slice)))) ,(unless no-size (cons 'size pdf-view-display-size)) - ,(unless no-origin + ,(unless (or no-origin pdf-view-roll-minor-mode) (cons 'origin (and displayed-p (let ((edges (pdf-util-image-displayed-edges nil t))) From 0e7d2b88033677b9951049ae35af3bc2065956bd Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Mon, 22 May 2023 21:45:57 +0200 Subject: [PATCH 015/104] Image-roll related tweaks --- lisp/pdf-roll.el | 4 ++-- lisp/pdf-view.el | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lisp/pdf-roll.el b/lisp/pdf-roll.el index 64926219..58c70663 100644 --- a/lisp/pdf-roll.el +++ b/lisp/pdf-roll.el @@ -51,7 +51,7 @@ continuous scrolling." image-roll-center t) - (add-hook 'window-configuration-change-hook 'image-roll-redisplay nil t) + (add-hook 'window-size-change-functions 'image-roll-redisplay nil t) (remove-hook 'image-mode-new-window-functions #'pdf-view-new-window-function t) @@ -68,7 +68,7 @@ continuous scrolling." (define-key pdf-view-mode-map (kbd "") 'pdf-view-next-line-or-next-page) (define-key pdf-view-mode-map (kbd "") 'pdf-view-previous-line-or-previous-page)) (t - (remove-hook 'window-configuration-change-hook 'image-roll-redisplay t) + (remove-hook 'window-size-change-functions 'image-roll-redisplay t) (remove-hook 'image-mode-new-window-functions 'image-roll-new-window-function t) (add-hook 'image-mode-new-window-functions diff --git a/lisp/pdf-view.el b/lisp/pdf-view.el index 1955764f..24145bda 100644 --- a/lisp/pdf-view.el +++ b/lisp/pdf-view.el @@ -38,6 +38,14 @@ (declare-function cua-copy-region "cua-base") (declare-function pdf-tools-pdf-buffer-p "pdf-tools") + +(declare-function image-roll-scroll-forward "image-roll") +(declare-function image-roll-scroll-backward "image-roll") +(declare-function image-roll-next-page "image-roll") +(declare-function image-roll-redisplay "image-roll") + +(defvar pdf-view-roll-minor-mode) + ;; * ================================================================== * ;; * Customizations From 18050dda71fceb1c0220220549a37b5e532eab14 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Wed, 24 May 2023 18:00:45 +0200 Subject: [PATCH 016/104] Fix link selection when multiple pages are displayed --- lisp/pdf-links.el | 69 ++++++++++++++++++++++------------------------- lisp/pdf-util.el | 14 +++++++--- 2 files changed, 43 insertions(+), 40 deletions(-) diff --git a/lisp/pdf-links.el b/lisp/pdf-links.el index 8ca62ab5..1d232a9b 100644 --- a/lisp/pdf-links.el +++ b/lisp/pdf-links.el @@ -28,6 +28,7 @@ (require 'pdf-isearch) (require 'let-alist) (require 'org) +(eval-when-compile (require 'image-roll nil t)) ;;; Code: @@ -235,45 +236,39 @@ scroll the current page." See `pdf-links-action-perform' for the interface." (pdf-util-assert-pdf-window) - (let* ((links (pdf-cache-pagelinks - (pdf-view-current-page))) + (let* ((pages (if pdf-view-roll-minor-mode + (nreverse (image-roll-displayed-pages)) + (list (pdf-view-current-page)))) + (links (mapcar #'pdf-cache-pagelinks pages)) (keys (pdf-links-read-link-action--create-keys - (length links))) - (key-strings (mapcar (apply-partially 'apply 'string) - keys)) - (alist (cl-mapcar 'cons keys links)) - (size (pdf-view-image-size)) + (apply #'+ (mapcar #'length links)))) + (alist (cl-mapcar 'cons keys (apply #'append links))) (colors (pdf-util-face-colors - 'pdf-links-read-link pdf-view-dark-minor-mode)) - (args (list - :foreground (car colors) - :background (cdr colors) - :formats - `((?c . ,(lambda (_edges) (pop key-strings))) - (?P . ,(number-to-string - (max 1 (* (cdr size) - pdf-links-convert-pointsize-scale))))) - :commands pdf-links-read-link-convert-commands - :apply (pdf-util-scale-relative-to-pixel - (mapcar (lambda (l) (cdr (assq 'edges l))) - links))))) - (unless links - (error "No links on this page")) - (unwind-protect - (let ((image-data - (pdf-cache-get-image - (pdf-view-current-page) - (car size) (car size) 'pdf-links-read-link-action))) - (unless image-data - (setq image-data (apply 'pdf-util-convert-page args )) - (pdf-cache-put-image - (pdf-view-current-page) - (car size) image-data 'pdf-links-read-link-action)) - (pdf-view-display-image - (create-image image-data (pdf-view-image-type) t) - (when pdf-view-roll-minor-mode (pdf-view-current-page))) - (pdf-links-read-link-action--read-chars prompt alist)) - (pdf-view-redisplay)))) + 'pdf-links-read-link pdf-view-dark-minor-mode))) + (if (not links) + (error "No links on displayed pages") + (unwind-protect + (progn + (dolist (page pages) + (pdf-view-display-image + (create-image (pdf-util-convert-image + (or (overlay-get (image-roll-page-overlay page) 'display) + (pdf-view-current-image)) + :foreground (car colors) + :background (cdr colors) + :formats + `((?c . ,(lambda (_edges) (apply #'string (pop keys)))) + (?P . ,(number-to-string + (max 1 (* (cdr (pdf-view-desired-image-size page)) + pdf-links-convert-pointsize-scale))))) + :commands pdf-links-read-link-convert-commands + :apply (pdf-util-scale-relative-to-pixel + (mapcar (lambda (l) (cdr (assq 'edges l))) + (pop links)))) + (pdf-view-image-type) t) + page)) + (pdf-links-read-link-action--read-chars prompt alist)) + (pdf-view-redisplay))))) (defun pdf-links-read-link-action--read-chars (prompt alist) (catch 'done diff --git a/lisp/pdf-util.el b/lisp/pdf-util.el index 4666cada..cc6c69b0 100644 --- a/lisp/pdf-util.el +++ b/lisp/pdf-util.el @@ -1042,8 +1042,8 @@ Returns the convert process." (set-process-sentinel proc callback)) proc))) -(defun pdf-util-convert-page (&rest specs) - "Convert image of current page according to SPECS. +(defun pdf-util-convert-image (image &rest specs) + "Convert IMAGE page according to SPECS. Return the converted PNG image as a string. See also `pdf-util-convert'." @@ -1053,7 +1053,7 @@ Return the converted PNG image as a string. See also (out-file (make-temp-file "pdf-util-convert" nil ".png"))) (unwind-protect (let ((image-data - (plist-get (cdr (pdf-view-current-image)) :data))) + (plist-get (cdr image) :data))) (with-temp-file in-file (set-buffer-multibyte nil) (set-buffer-file-coding-system 'binary) @@ -1066,6 +1066,14 @@ Return the converted PNG image as a string. See also (when (file-exists-p out-file) (delete-file out-file))))) +(defun pdf-util-convert-page (&rest specs) + "Convert image of current page according to SPECS. + +Return the converted PNG image as a string. See also +`pdf-util-convert'." + + (pdf-util-assert-pdf-window) + (apply #'pdf-util-convert-image (pdf-view-current-image) specs)) (defun pdf-util-convert--create-commands (spec) (let ((fg "red") From 3453628fbf9b11c0ea687deb2d497595f3036cec Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Wed, 14 Jun 2023 10:35:17 +0200 Subject: [PATCH 017/104] Add margin to the image with link hints --- lisp/pdf-links.el | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lisp/pdf-links.el b/lisp/pdf-links.el index 1d232a9b..1c312650 100644 --- a/lisp/pdf-links.el +++ b/lisp/pdf-links.el @@ -265,7 +265,10 @@ See `pdf-links-action-perform' for the interface." :apply (pdf-util-scale-relative-to-pixel (mapcar (lambda (l) (cdr (assq 'edges l))) (pop links)))) - (pdf-view-image-type) t) + (pdf-view-image-type) t + :margin (cons 0 (if pdf-view-roll-minor-mode + image-roll-vertical-margin + 0))) page)) (pdf-links-read-link-action--read-chars prompt alist)) (pdf-view-redisplay))))) From 2e9dfa521481cb83d7324182bd0324ae76cb0519 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Wed, 14 Jun 2023 15:24:26 +0200 Subject: [PATCH 018/104] Fix turning off pdf-roll and minor tweaks --- lisp/pdf-links.el | 6 ++++-- lisp/pdf-view.el | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lisp/pdf-links.el b/lisp/pdf-links.el index 1c312650..2a8c621e 100644 --- a/lisp/pdf-links.el +++ b/lisp/pdf-links.el @@ -30,6 +30,8 @@ (require 'org) (eval-when-compile (require 'image-roll nil t)) + +(defvar pdf-view-roll-minor-mode) ;;; Code: @@ -104,7 +106,7 @@ do something with it." ;;;###autoload (define-minor-mode pdf-links-minor-mode - "Handle links in PDF documents.\\ + "Handle links in PDF documents. If this mode is enabled, most links in the document may be activated by clicking on them or by pressing \\[pdf-links-action-perform] and selecting @@ -152,7 +154,7 @@ links via \\[pdf-links-isearch-link]. (nreverse hotspots))) (defun pdf-links-action-to-string (link) - "Return a string representation of ACTION." + "Return a string representation of action for LINK." (let-alist link (concat (cl-case .type diff --git a/lisp/pdf-view.el b/lisp/pdf-view.el index 24145bda..824efe13 100644 --- a/lisp/pdf-view.el +++ b/lisp/pdf-view.el @@ -1218,7 +1218,7 @@ If WINDOW is t, redisplay pages in all windows." ;; `window' property is only effective if its value is a window). (cl-assert (eq t (car winprops))) (delete-overlay ol)) - (image-mode-window-put 'overlay ol winprops) + (image-mode-window-put 'overlay ol) ;; Clean up some overlays. (dolist (ov (overlays-in (point-min) (point-max))) (when (and (windowp (overlay-get ov 'window)) From 61bd009cf66b84d27a7e2bb069a1f6b8246b37f7 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Wed, 14 Jun 2023 15:42:00 +0200 Subject: [PATCH 019/104] Bind/unbind mwheel-scroll-up/down-function --- lisp/pdf-roll.el | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lisp/pdf-roll.el b/lisp/pdf-roll.el index 58c70663..a384bda3 100644 --- a/lisp/pdf-roll.el +++ b/lisp/pdf-roll.el @@ -22,6 +22,7 @@ ;; (require 'image-roll) +(require 'pdf-view) (eval-when-compile (require 'pdf-view)) @@ -48,8 +49,9 @@ continuous scrolling." image-roll-display-page-function 'pdf-view-display-page image-roll-page-sizes-function 'pdf-roll-page-sizes image-roll-set-redisplay-flag-function 'pdf-roll-set-redisplay-flag-function - - image-roll-center t) + image-roll-center t + mwheel-scroll-up-function #'image-roll-scroll-forward + mwheel-scroll-down-function #'image-roll-scroll-backward) (add-hook 'window-size-change-functions 'image-roll-redisplay nil t) @@ -68,6 +70,8 @@ continuous scrolling." (define-key pdf-view-mode-map (kbd "") 'pdf-view-next-line-or-next-page) (define-key pdf-view-mode-map (kbd "") 'pdf-view-previous-line-or-previous-page)) (t + (setq-local mwheel-scroll-up-function #'pdf-view-scroll-up-or-next-page + mwheel-scroll-down-function #'pdf-view-scroll-down-or-previous-page) (remove-hook 'window-size-change-functions 'image-roll-redisplay t) (remove-hook 'image-mode-new-window-functions 'image-roll-new-window-function t) @@ -82,9 +86,7 @@ continuous scrolling." (remove-overlays) (insert-file-contents-literally (buffer-file-name)) (pdf-view-new-window-function (list (selected-window))) - (set-buffer-modified-p nil)) - (define-key pdf-view-mode-map (kbd "") 'mwheel-scroll) - (define-key pdf-view-mode-map (kbd "") 'mwheel-scroll)))) + (set-buffer-modified-p nil))))) (provide 'pdf-roll) From 485cce506a4856539a9cdd4b87099c1deca101b4 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Thu, 15 Jun 2023 13:25:59 +0200 Subject: [PATCH 020/104] Make pdf-sync work with pdf-roll --- lisp/pdf-sync.el | 17 ++++++++++------- lisp/pdf-view.el | 9 +++++++-- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/lisp/pdf-sync.el b/lisp/pdf-sync.el index af4fe4f9..44effe87 100644 --- a/lisp/pdf-sync.el +++ b/lisp/pdf-sync.el @@ -277,15 +277,18 @@ Has no effect if `pdf-sync-backward-use-heuristic' is nil." (xy (posn-object-x-y posn))) (unless image (error "Outside of image area")) - (pdf-sync-backward-search (car xy) (cdr xy)))) + (pdf-sync-backward-search + (car xy) (cdr xy) + (and (bound-and-true-p pdf-view-roll-minor-mode) + (/ (1+ (posn-point posn)) 2))))) -(defun pdf-sync-backward-search (x y) - "Go to the source corresponding to image coordinates X, Y. +(defun pdf-sync-backward-search (x y &optional page) + "Go to the source corresponding to image coordinates X, Y on PAGE. Try to find the exact position, if `pdf-sync-backward-use-heuristic' is non-nil." (cl-destructuring-bind (source finder) - (pdf-sync-backward-correlate x y) + (pdf-sync-backward-correlate x y page) (pop-to-buffer (or (find-buffer-visiting source) (find-file-noselect source)) pdf-sync-backward-display-action) @@ -293,8 +296,8 @@ Try to find the exact position, if (funcall finder) (run-hooks 'pdf-sync-backward-hook))) -(defun pdf-sync-backward-correlate (x y) - "Find the source corresponding to image coordinates X, Y. +(defun pdf-sync-backward-correlate (x y &optional page) + "Find the source corresponding to image coordinates X, Y on PAGE. Returns a list \(SOURCE FINDER\), where SOURCE is the name of the TeX file and FINDER a function of zero arguments which, when @@ -303,7 +306,7 @@ point to the correct position." (pdf-util-assert-pdf-window) (let ((size (pdf-view-image-size)) - (page (pdf-view-current-page))) + (page (or page (pdf-view-current-page)))) (setq x (/ x (float (car size))) y (/ y (float (cdr size)))) (let-alist (pdf-info-synctex-backward-search page x y) diff --git a/lisp/pdf-view.el b/lisp/pdf-view.el index 824efe13..b045f0f4 100644 --- a/lisp/pdf-view.el +++ b/lisp/pdf-view.el @@ -1074,8 +1074,13 @@ If DISPLAYED-P is non-nil, return the size of the displayed image. These values may be different, if slicing is used." (if displayed-p (with-selected-window (or window (selected-window)) - (image-display-size - (image-get-display-property) t)) + (let ((display-prop (image-get-display-property))) + (if (eq (car display-prop) 'space) + (progn (cl-callf cdr display-prop) + (cons (car (plist-get display-prop :width)) + (car (plist-get display-prop :height)))) + (image-display-size + (image-get-display-property) t)))) (image-size (pdf-view-current-image window) t))) (defun pdf-view-image-offset (&optional window) From 99b005b99ab6ed83d0af03c5957f1ea2fad7c6e7 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Thu, 15 Jun 2023 22:05:41 +0200 Subject: [PATCH 021/104] Further tooltip fixes --- lisp/pdf-links.el | 18 +++++++----------- lisp/pdf-util.el | 9 +++++---- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/lisp/pdf-links.el b/lisp/pdf-links.el index 2a8c621e..215ccbf7 100644 --- a/lisp/pdf-links.el +++ b/lisp/pdf-links.el @@ -212,20 +212,16 @@ scroll the current page." (when (derived-mode-p 'pdf-view-mode) (when (> .page 0) (pdf-view-goto-page .page)) - ;; TODO fix pdf-util-tooltip-arrow function for image-roll - ;; compatibility - - ;;(when .top + (when .top ;; Showing the tooltip delays displaying the page for ;; some reason (sit-for/redisplay don't help), do it ;; later. - ;;(run-with-idle-timer 0.001 nil - ;;(lambda () - ;;(when (window-live-p window) - ;;(with-selected-window window - ;;(when (derived-mode-p 'pdf-view-mode) - ;;(pdf-util-tooltip-arrow .top))))))) - )))) + (run-with-idle-timer 0.001 nil + (lambda () + (when (window-live-p window) + (with-selected-window window + (when (derived-mode-p 'pdf-view-mode) + (pdf-util-tooltip-arrow .top))))))))))) (uri (funcall pdf-links-browse-uri-function .uri)) (t diff --git a/lisp/pdf-util.el b/lisp/pdf-util.el index cc6c69b0..023d4702 100644 --- a/lisp/pdf-util.el +++ b/lisp/pdf-util.el @@ -28,6 +28,7 @@ (require 'pdf-macs) (require 'cl-lib) (require 'format-spec) +(require 'image-mode) (require 'faces) ;; These functions are only used after a PdfView window was asserted, @@ -37,6 +38,7 @@ (declare-function pdf-cache-pagesize "pdf-cache") (declare-function pdf-view-image-type "pdf-view") +(defvar pdf-view-roll-minor-mode) ;; * ================================================================== * @@ -639,10 +641,9 @@ string." (cdr (pdf-view-image-offset)) (window-vscroll nil t) (frame-char-height)))) - (when (overlay-get (pdf-view-current-overlay) 'before-string) - (let* ((e (window-inside-pixel-edges)) - (xw (pdf-util-with-edges (e) e-width))) - (cl-incf dx (/ (- xw (car (pdf-view-image-size t))) 2)))) + (let* ((e (window-inside-pixel-edges)) + (xw (pdf-util-with-edges (e) e-width))) + (cl-incf dx (/ (- xw (car (pdf-view-image-size t))) 2))) (pdf-util-tooltip-in-window (propertize " " 'display (propertize From 10a5b996e8f4f17d1d8340f9def063ab805cb23c Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Fri, 16 Jun 2023 15:31:23 +0200 Subject: [PATCH 022/104] Add some bindings to pdf-view-roll-minor-mode-map --- lisp/pdf-roll.el | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/lisp/pdf-roll.el b/lisp/pdf-roll.el index a384bda3..291d265f 100644 --- a/lisp/pdf-roll.el +++ b/lisp/pdf-roll.el @@ -40,8 +40,14 @@ "If enabled display document on a virtual scroll providing continuous scrolling." :lighter " Continuous" - :keymap `((,(kbd "S-") . image-roll-scroll-screen-forward) - (,(kbd "S-") . image-roll-scroll-screen-backward)) + :keymap (let ((map (make-sparse-keymap))) + (define-key map [remap pdf-view-previous-line-or-previous-page] 'image-roll-scroll-backward) + (define-key map [remap pdf-view-next-line-or-next-page] 'image-roll-scroll-forward) + (define-key map "" 'image-roll-scroll-mouse-wheel) + (define-key map "" 'image-roll-scroll-mouse-wheel) + (define-key map (kbd "S-") 'image-roll-scroll-screen-forward) + (define-key map (kbd "S-") 'image-roll-scroll-screen-backward) + map) :version 28.1 (cond (pdf-view-roll-minor-mode @@ -56,7 +62,7 @@ continuous scrolling." (add-hook 'window-size-change-functions 'image-roll-redisplay nil t) (remove-hook 'image-mode-new-window-functions - #'pdf-view-new-window-function t) + #'pdf-view-new-window-function t) (add-hook 'image-mode-new-window-functions 'image-roll-new-window-function nil t) (add-hook 'image-roll-after-change-page-hook 'pdf-history-before-change-page-hook nil t) @@ -76,7 +82,7 @@ continuous scrolling." (remove-hook 'image-mode-new-window-functions 'image-roll-new-window-function t) (add-hook 'image-mode-new-window-functions - #'pdf-view-new-window-function nil t) + #'pdf-view-new-window-function nil t) (remove-hook 'pdf-view-after-change-page-hook #'pdf-history-before-change-page-hook t) From ae02ece2db42f065c1f62c7e178b425f54c14473 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Fri, 16 Jun 2023 17:30:49 +0200 Subject: [PATCH 023/104] Use line-spacing to make margins --- lisp/pdf-links.el | 5 +---- lisp/pdf-roll.el | 1 + lisp/pdf-view.el | 9 +++------ 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/lisp/pdf-links.el b/lisp/pdf-links.el index 215ccbf7..2378ae3d 100644 --- a/lisp/pdf-links.el +++ b/lisp/pdf-links.el @@ -263,10 +263,7 @@ See `pdf-links-action-perform' for the interface." :apply (pdf-util-scale-relative-to-pixel (mapcar (lambda (l) (cdr (assq 'edges l))) (pop links)))) - (pdf-view-image-type) t - :margin (cons 0 (if pdf-view-roll-minor-mode - image-roll-vertical-margin - 0))) + (pdf-view-image-type) t) page)) (pdf-links-read-link-action--read-chars prompt alist)) (pdf-view-redisplay))))) diff --git a/lisp/pdf-roll.el b/lisp/pdf-roll.el index 291d265f..1894e16e 100644 --- a/lisp/pdf-roll.el +++ b/lisp/pdf-roll.el @@ -56,6 +56,7 @@ continuous scrolling." image-roll-page-sizes-function 'pdf-roll-page-sizes image-roll-set-redisplay-flag-function 'pdf-roll-set-redisplay-flag-function image-roll-center t + line-spacing image-roll-vertical-margin mwheel-scroll-up-function #'image-roll-scroll-forward mwheel-scroll-down-function #'image-roll-scroll-backward) diff --git a/lisp/pdf-view.el b/lisp/pdf-view.el index b045f0f4..398d90e2 100644 --- a/lisp/pdf-view.el +++ b/lisp/pdf-view.el @@ -1058,9 +1058,6 @@ See also `pdf-view-use-imagemagick'." (hotspots (pdf-view-apply-hotspot-functions window page size))) (pdf-view-create-image data - :margin (cons 0 (if pdf-view-roll-minor-mode - image-roll-vertical-margin - 0)) :width (car size) :rotation (or pdf-view--current-rotation 0) :map hotspots @@ -1129,8 +1126,8 @@ It is equal to \(LEFT . TOP\) of the current slice in pixel." displayed-width) (propertize " " 'display `(space :align-to - ,(/ (- (window-width window) - displayed-width) 2))))) + ,(/ (- (window-width window) + displayed-width) 2))))) (overlay-put ol 'display (if slice (list (cons 'slice @@ -1366,7 +1363,7 @@ current theme's colors." (pdf-util-assert-pdf-buffer) (pdf-cache-clear-images) (when get-theme - (pdf-view-set-theme-background)) + (pdf-view-set-theme-background)) (pdf-view-redisplay t)) (define-minor-mode pdf-view-themed-minor-mode From ff9df5f89c3d77f2ce716d71ab09b4364bf3a67e Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Sat, 17 Jun 2023 01:18:18 +0200 Subject: [PATCH 024/104] Renamings and tweaks --- lisp/pdf-roll.el | 19 +++++++++++-------- lisp/pdf-view.el | 4 ++-- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/lisp/pdf-roll.el b/lisp/pdf-roll.el index 1894e16e..7e19b03b 100644 --- a/lisp/pdf-roll.el +++ b/lisp/pdf-roll.el @@ -37,14 +37,14 @@ (setf (pdf-view-window-needs-redisplay) t)) (define-minor-mode pdf-view-roll-minor-mode - "If enabled display document on a virtual scroll providing -continuous scrolling." + "If enabled display document on a virtual scroll providing continuous scrolling." :lighter " Continuous" :keymap (let ((map (make-sparse-keymap))) (define-key map [remap pdf-view-previous-line-or-previous-page] 'image-roll-scroll-backward) (define-key map [remap pdf-view-next-line-or-next-page] 'image-roll-scroll-forward) - (define-key map "" 'image-roll-scroll-mouse-wheel) - (define-key map "" 'image-roll-scroll-mouse-wheel) + (define-key map [remap mouse-set-point] 'ignore) + (define-key map (kbd "") 'image-roll-scroll-mouse-wheel) + (define-key map (kbd "") 'image-roll-scroll-mouse-wheel) (define-key map (kbd "S-") 'image-roll-scroll-screen-forward) (define-key map (kbd "S-") 'image-roll-scroll-screen-backward) map) @@ -60,7 +60,8 @@ continuous scrolling." mwheel-scroll-up-function #'image-roll-scroll-forward mwheel-scroll-down-function #'image-roll-scroll-backward) - (add-hook 'window-size-change-functions 'image-roll-redisplay nil t) + (add-hook 'window-size-change-functions 'image-roll-window-size-change-function nil t) + (add-hook 'window-configuration-change-hook 'image-roll-window-configuration-change-hook nil t) (remove-hook 'image-mode-new-window-functions #'pdf-view-new-window-function t) @@ -79,14 +80,16 @@ continuous scrolling." (t (setq-local mwheel-scroll-up-function #'pdf-view-scroll-up-or-next-page mwheel-scroll-down-function #'pdf-view-scroll-down-or-previous-page) - (remove-hook 'window-size-change-functions 'image-roll-redisplay t) + + (remove-hook 'window-size-change-functions 'image-roll-window-size-change-function t) + (remove-hook 'window-configuration-change-hook 'image-roll-window-configuration-change-hook) (remove-hook 'image-mode-new-window-functions 'image-roll-new-window-function t) (add-hook 'image-mode-new-window-functions #'pdf-view-new-window-function nil t) - (remove-hook 'pdf-view-after-change-page-hook - #'pdf-history-before-change-page-hook t) + (remove-hook 'image-roll-after-change-page-hook + 'pdf-history-before-change-page-hook t) (let ((inhibit-read-only t)) (erase-buffer) diff --git a/lisp/pdf-view.el b/lisp/pdf-view.el index 398d90e2..260cc2e5 100644 --- a/lisp/pdf-view.el +++ b/lisp/pdf-view.el @@ -42,7 +42,7 @@ (declare-function image-roll-scroll-forward "image-roll") (declare-function image-roll-scroll-backward "image-roll") (declare-function image-roll-next-page "image-roll") -(declare-function image-roll-redisplay "image-roll") +(declare-function image-roll-window-size-change-function "image-roll") (defvar pdf-view-roll-minor-mode) @@ -1166,7 +1166,7 @@ If WINDOW is t, redisplay pages in all windows." (defun pdf-view-redisplay (&optional window) (if pdf-view-roll-minor-mode - (image-roll-redisplay window) + (image-roll-window-size-change-function window) (pdf-view--redisplay window))) (defun pdf-view-redisplay-pages (&rest pages) From a89793bd9ebb5d7d05f7fb0c7d22ce7917cbd123 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Sat, 17 Jun 2023 02:38:10 +0200 Subject: [PATCH 025/104] Remove hooks image-mode and pdf-view hooks from window-conf-chg-h I think these interfere with setting vscroll and sometimes overwrite the setting done by image-roll commands. --- lisp/pdf-roll.el | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/lisp/pdf-roll.el b/lisp/pdf-roll.el index 7e19b03b..8a36b4d8 100644 --- a/lisp/pdf-roll.el +++ b/lisp/pdf-roll.el @@ -21,6 +21,7 @@ ;;; Commentary: ;; +;;; Code: (require 'image-roll) (require 'pdf-view) @@ -29,11 +30,14 @@ (defun pdf-roll-page-sizes () + "Return a list of (WIDTH . HEIGHT) cons cells for all pages." (let (s) - (dotimes (i (pdf-info-number-of-pages) (nreverse s)) - (push (pdf-view-desired-image-size (1+ i)) s)))) + (dotimes (i (pdf-info-number-of-pages)) + (push (pdf-view-desired-image-size (1+ i)) s)) + (nreverse s))) (defun pdf-roll-set-redisplay-flag-function () + "Set redisplay flag for pdf." (setf (pdf-view-window-needs-redisplay) t)) (define-minor-mode pdf-view-roll-minor-mode @@ -62,9 +66,10 @@ (add-hook 'window-size-change-functions 'image-roll-window-size-change-function nil t) (add-hook 'window-configuration-change-hook 'image-roll-window-configuration-change-hook nil t) + (remove-hook 'window-configuration-change-hook 'image-mode-reapply-winprops t) + (remove-hook 'window-configuration-change-hook 'pdf-view-redisplay-some-windows t) - (remove-hook 'image-mode-new-window-functions - #'pdf-view-new-window-function t) + (remove-hook 'image-mode-new-window-functions#'pdf-view-new-window-function t) (add-hook 'image-mode-new-window-functions 'image-roll-new-window-function nil t) (add-hook 'image-roll-after-change-page-hook 'pdf-history-before-change-page-hook nil t) @@ -81,6 +86,8 @@ (setq-local mwheel-scroll-up-function #'pdf-view-scroll-up-or-next-page mwheel-scroll-down-function #'pdf-view-scroll-down-or-previous-page) + (add-hook 'window-configuration-change-hook 'image-mode-reapply-winprops nil t) + (add-hook 'window-configuration-change-hook 'pdf-view-redisplay-some-windows nil t) (remove-hook 'window-size-change-functions 'image-roll-window-size-change-function t) (remove-hook 'window-configuration-change-hook 'image-roll-window-configuration-change-hook) From 6cc9934813072e704bf8b0ac6969a7a170bc9cfc Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Sun, 18 Jun 2023 21:14:49 +0200 Subject: [PATCH 026/104] Remove unneeded functions --- lisp/pdf-roll.el | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/lisp/pdf-roll.el b/lisp/pdf-roll.el index 8a36b4d8..14dbb20a 100644 --- a/lisp/pdf-roll.el +++ b/lisp/pdf-roll.el @@ -29,17 +29,6 @@ (require 'pdf-view)) -(defun pdf-roll-page-sizes () - "Return a list of (WIDTH . HEIGHT) cons cells for all pages." - (let (s) - (dotimes (i (pdf-info-number-of-pages)) - (push (pdf-view-desired-image-size (1+ i)) s)) - (nreverse s))) - -(defun pdf-roll-set-redisplay-flag-function () - "Set redisplay flag for pdf." - (setf (pdf-view-window-needs-redisplay) t)) - (define-minor-mode pdf-view-roll-minor-mode "If enabled display document on a virtual scroll providing continuous scrolling." :lighter " Continuous" @@ -57,8 +46,6 @@ (cond (pdf-view-roll-minor-mode (setq-local image-roll-last-page (pdf-cache-number-of-pages) image-roll-display-page-function 'pdf-view-display-page - image-roll-page-sizes-function 'pdf-roll-page-sizes - image-roll-set-redisplay-flag-function 'pdf-roll-set-redisplay-flag-function image-roll-center t line-spacing image-roll-vertical-margin mwheel-scroll-up-function #'image-roll-scroll-forward From 9e557be9f9168d67df7cf0463a2509db23360f9f Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Mon, 19 Jun 2023 10:44:20 +0200 Subject: [PATCH 027/104] Fix pdf-view-page-size to work with image-roll --- lisp/pdf-roll.el | 4 ---- lisp/pdf-view.el | 20 +++++++++++--------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/lisp/pdf-roll.el b/lisp/pdf-roll.el index 14dbb20a..62ecdf84 100644 --- a/lisp/pdf-roll.el +++ b/lisp/pdf-roll.el @@ -25,10 +25,6 @@ (require 'image-roll) (require 'pdf-view) -(eval-when-compile - (require 'pdf-view)) - - (define-minor-mode pdf-view-roll-minor-mode "If enabled display document on a virtual scroll providing continuous scrolling." :lighter " Continuous" diff --git a/lisp/pdf-view.el b/lisp/pdf-view.el index 260cc2e5..89fa8a8d 100644 --- a/lisp/pdf-view.el +++ b/lisp/pdf-view.el @@ -42,7 +42,9 @@ (declare-function image-roll-scroll-forward "image-roll") (declare-function image-roll-scroll-backward "image-roll") (declare-function image-roll-next-page "image-roll") -(declare-function image-roll-window-size-change-function "image-roll") +(declare-function image-roll-redisplay "image-roll") +(declare-function image-roll-page-overlay "image-roll") +(declare-function image-roll-page-at-current-pos "image-roll") (defvar pdf-view-roll-minor-mode) @@ -1071,13 +1073,13 @@ If DISPLAYED-P is non-nil, return the size of the displayed image. These values may be different, if slicing is used." (if displayed-p (with-selected-window (or window (selected-window)) - (let ((display-prop (image-get-display-property))) - (if (eq (car display-prop) 'space) - (progn (cl-callf cdr display-prop) - (cons (car (plist-get display-prop :width)) - (car (plist-get display-prop :height)))) - (image-display-size - (image-get-display-property) t)))) + (image-display-size + (if pdf-view-roll-minor-mode + (overlay-get + (image-roll-page-overlay (image-roll-page-at-current-pos) window) + 'display) + (image-get-display-property)) + t)) (image-size (pdf-view-current-image window) t))) (defun pdf-view-image-offset (&optional window) @@ -1166,7 +1168,7 @@ If WINDOW is t, redisplay pages in all windows." (defun pdf-view-redisplay (&optional window) (if pdf-view-roll-minor-mode - (image-roll-window-size-change-function window) + (image-roll-redisplay) (pdf-view--redisplay window))) (defun pdf-view-redisplay-pages (&rest pages) From 273b6e961f89da9165473493c6d5b71a605b5779 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Mon, 19 Jun 2023 22:20:57 +0200 Subject: [PATCH 028/104] Move to the beginning of link target with image roll --- lisp/pdf-util.el | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lisp/pdf-util.el b/lisp/pdf-util.el index 023d4702..ae42e86e 100644 --- a/lisp/pdf-util.el +++ b/lisp/pdf-util.el @@ -629,7 +629,9 @@ string." (dy image-top) (pos (list dx dy dx (+ dy (* 2 (frame-char-height))))) (vscroll - (pdf-util-required-vscroll pos)) + (if pdf-view-roll-minor-mode + image-top + (pdf-util-required-vscroll pos))) (tooltip-frame-parameters `((border-width . 0) (internal-border-width . 0) From 383fcbcac6ccda0452b607fc75dfd38758235ab6 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Mon, 19 Jun 2023 22:21:57 +0200 Subject: [PATCH 029/104] Change image roll to rely on pre-display-functions --- lisp/pdf-roll.el | 24 ++++++------------------ lisp/pdf-view.el | 3 +-- 2 files changed, 7 insertions(+), 20 deletions(-) diff --git a/lisp/pdf-roll.el b/lisp/pdf-roll.el index 62ecdf84..00b147af 100644 --- a/lisp/pdf-roll.el +++ b/lisp/pdf-roll.el @@ -47,39 +47,27 @@ mwheel-scroll-up-function #'image-roll-scroll-forward mwheel-scroll-down-function #'image-roll-scroll-backward) - (add-hook 'window-size-change-functions 'image-roll-window-size-change-function nil t) - (add-hook 'window-configuration-change-hook 'image-roll-window-configuration-change-hook nil t) (remove-hook 'window-configuration-change-hook 'image-mode-reapply-winprops t) (remove-hook 'window-configuration-change-hook 'pdf-view-redisplay-some-windows t) - (remove-hook 'image-mode-new-window-functions#'pdf-view-new-window-function t) - (add-hook 'image-mode-new-window-functions 'image-roll-new-window-function nil t) + (add-hook 'pre-redisplay-functions 'image-roll-pre-redisplay nil t) (add-hook 'image-roll-after-change-page-hook 'pdf-history-before-change-page-hook nil t) (let ((inhibit-read-only t)) (erase-buffer) - (remove-overlays) - (image-roll-new-window-function (list (selected-window)))) - (pdf-view-redisplay) - - (define-key pdf-view-mode-map (kbd "") 'pdf-view-next-line-or-next-page) - (define-key pdf-view-mode-map (kbd "") 'pdf-view-previous-line-or-previous-page)) + (remove-overlays)) + (image-roll-new-window-function)) (t (setq-local mwheel-scroll-up-function #'pdf-view-scroll-up-or-next-page mwheel-scroll-down-function #'pdf-view-scroll-down-or-previous-page) (add-hook 'window-configuration-change-hook 'image-mode-reapply-winprops nil t) (add-hook 'window-configuration-change-hook 'pdf-view-redisplay-some-windows nil t) - (remove-hook 'window-size-change-functions 'image-roll-window-size-change-function t) - (remove-hook 'window-configuration-change-hook 'image-roll-window-configuration-change-hook) - - (remove-hook 'image-mode-new-window-functions 'image-roll-new-window-function t) - (add-hook 'image-mode-new-window-functions - #'pdf-view-new-window-function nil t) + (add-hook 'image-mode-new-window-functions #'pdf-view-new-window-function nil t) - (remove-hook 'image-roll-after-change-page-hook - 'pdf-history-before-change-page-hook t) + (remove-hook 'pre-redisplay-functions 'image-roll-pre-redisplay t) + (remove-hook 'image-roll-after-change-page-hook 'pdf-history-before-change-page-hook t) (let ((inhibit-read-only t)) (erase-buffer) diff --git a/lisp/pdf-view.el b/lisp/pdf-view.el index 89fa8a8d..23b33d5d 100644 --- a/lisp/pdf-view.el +++ b/lisp/pdf-view.el @@ -1729,7 +1729,7 @@ The optional, boolean args exclude certain attributes." (pdf-view-current-slice)))) ,(unless no-size (cons 'size pdf-view-display-size)) - ,(unless (or no-origin pdf-view-roll-minor-mode) + ,(unless no-origin (cons 'origin (and displayed-p (let ((edges (pdf-util-image-displayed-edges nil t))) @@ -1773,7 +1773,6 @@ See also `pdf-view-bookmark-make-record'." (when-let ((origin (bookmark-prop-get pdf-view--bookmark-to-restore 'origin)) (size (pdf-view-image-size t win))) - (image-set-window-hscroll (round (/ (* (car origin) (car size)) (frame-char-width)))) From 3eb9033041213301030319075439f4860d5cc3ed Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Tue, 20 Jun 2023 01:43:28 +0200 Subject: [PATCH 030/104] More tooltip tweaks + vscroll on page jump --- lisp/pdf-links.el | 11 ++--------- lisp/pdf-view.el | 14 +++++++++----- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/lisp/pdf-links.el b/lisp/pdf-links.el index 2378ae3d..90fdc02c 100644 --- a/lisp/pdf-links.el +++ b/lisp/pdf-links.el @@ -213,15 +213,8 @@ scroll the current page." (when (> .page 0) (pdf-view-goto-page .page)) (when .top - ;; Showing the tooltip delays displaying the page for - ;; some reason (sit-for/redisplay don't help), do it - ;; later. - (run-with-idle-timer 0.001 nil - (lambda () - (when (window-live-p window) - (with-selected-window window - (when (derived-mode-p 'pdf-view-mode) - (pdf-util-tooltip-arrow .top))))))))))) + (when (derived-mode-p 'pdf-view-mode) + (pdf-util-tooltip-arrow .top))))))) (uri (funcall pdf-links-browse-uri-function .uri)) (t diff --git a/lisp/pdf-view.el b/lisp/pdf-view.el index 23b33d5d..c54d6f55 100644 --- a/lisp/pdf-view.el +++ b/lisp/pdf-view.el @@ -696,6 +696,7 @@ windows." (setf (pdf-view-current-page window) page) (run-hooks 'pdf-view-change-page-hook)) (when (window-live-p window) + (image-set-window-vscroll 0) (pdf-view-redisplay window)) (when changing-p (pdf-view-deactivate-region) @@ -1075,9 +1076,12 @@ image. These values may be different, if slicing is used." (with-selected-window (or window (selected-window)) (image-display-size (if pdf-view-roll-minor-mode - (overlay-get - (image-roll-page-overlay (image-roll-page-at-current-pos) window) - 'display) + (progn (unless (memq (pdf-view-current-page) + (image-mode-window-get 'displayed-pages window)) + (pdf-view-display-page (pdf-view-current-page) window)) + (overlay-get + (image-roll-page-overlay (image-roll-page-at-current-pos) window) + 'display)) (image-get-display-property)) t)) (image-size (pdf-view-current-image window) t))) @@ -1107,7 +1111,7 @@ It is equal to \(LEFT . TOP\) of the current slice in pixel." (defun pdf-view-display-image (image page &optional window inhibit-slice-p) ;; TODO: write documentation! (let ((ol (if pdf-view-roll-minor-mode - (image-roll-page-overlay page) + (image-roll-page-overlay page window) (pdf-view-current-overlay window)))) (when (window-live-p (overlay-get ol 'window)) (let* ((size (image-size image t)) @@ -1168,7 +1172,7 @@ If WINDOW is t, redisplay pages in all windows." (defun pdf-view-redisplay (&optional window) (if pdf-view-roll-minor-mode - (image-roll-redisplay) + (image-roll-redisplay window) (pdf-view--redisplay window))) (defun pdf-view-redisplay-pages (&rest pages) From 76ff9462ac6bc9ad2bcb351aa843eecd5c6d63b5 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Wed, 21 Jun 2023 00:24:15 +0200 Subject: [PATCH 031/104] Fix pdf-view-image-size-again --- lisp/pdf-view.el | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/lisp/pdf-view.el b/lisp/pdf-view.el index c54d6f55..373afc37 100644 --- a/lisp/pdf-view.el +++ b/lisp/pdf-view.el @@ -1067,24 +1067,22 @@ See also `pdf-view-use-imagemagick'." :pointer 'arrow))) (defun pdf-view-image-size (&optional displayed-p window) - ;; TODO: add WINDOW to docstring. - "Return the size in pixel of the current image. + "Return the size in pixel of the current image in WINDOW. If DISPLAYED-P is non-nil, return the size of the displayed image. These values may be different, if slicing is used." - (if displayed-p - (with-selected-window (or window (selected-window)) - (image-display-size - (if pdf-view-roll-minor-mode - (progn (unless (memq (pdf-view-current-page) - (image-mode-window-get 'displayed-pages window)) - (pdf-view-display-page (pdf-view-current-page) window)) - (overlay-get - (image-roll-page-overlay (image-roll-page-at-current-pos) window) - 'display)) - (image-get-display-property)) - t)) - (image-size (pdf-view-current-image window) t))) + (let ((display-prop (if pdf-view-roll-minor-mode + (progn (setq window (if (windowp window) window (selected-window))) + (unless (memq (pdf-view-current-page window) + (image-mode-window-get 'displayed-pages window)) + (pdf-view-display-page (pdf-view-current-page window) window)) + (overlay-get (image-roll-page-overlay + (image-roll-page-at-current-pos) window) + 'display)) + (image-get-display-property)))) + (if displayed-p + (image-display-size display-prop t) + (image-size display-prop t)))) (defun pdf-view-image-offset (&optional window) ;; TODO: add WINDOW to docstring. From 7460366aa166f00305fee6bac18478e45fb66566 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Fri, 23 Jun 2023 23:31:44 +0200 Subject: [PATCH 032/104] Make indirect buffers work --- lisp/pdf-sync.el | 1 - lisp/pdf-view.el | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lisp/pdf-sync.el b/lisp/pdf-sync.el index 44effe87..4a1c8fa7 100644 --- a/lisp/pdf-sync.el +++ b/lisp/pdf-sync.el @@ -648,7 +648,6 @@ Needs to have `pdf-sync-backward-debug-minor-mode' enabled." ;; * Forward search (TeX -> PDF) ;; * ================================================================== * -;; TODO adapt for `pdf-view-roll-minor-mode' (see pdf-scroll.el) (defun pdf-sync-forward-search (&optional line column) "Display the PDF location corresponding to LINE, COLUMN." (interactive) diff --git a/lisp/pdf-view.el b/lisp/pdf-view.el index 373afc37..c18d5407 100644 --- a/lisp/pdf-view.el +++ b/lisp/pdf-view.el @@ -513,7 +513,8 @@ PNG images in Emacs buffers." This may be different from variable `buffer-file-name' when operating on a local copy of a remote file." (or pdf-view--buffer-file-name - (buffer-file-name))) + (buffer-file-name) + (buffer-file-name (buffer-base-buffer)))) (defun pdf-view--write-contents-function () "Function for `write-contents-functions' to save the buffer." From e9d0fee24948ab646cc478b3dd3a0f9665322c92 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Sun, 25 Jun 2023 14:37:20 +0200 Subject: [PATCH 033/104] Various changes and refactors for image-roll --- lisp/pdf-links.el | 20 ++++++++------ lisp/pdf-roll.el | 13 +++++++-- lisp/pdf-sync.el | 4 +-- lisp/pdf-view.el | 70 ++++++++++++++++++++++------------------------- 4 files changed, 57 insertions(+), 50 deletions(-) diff --git a/lisp/pdf-links.el b/lisp/pdf-links.el index 90fdc02c..e5e11c7e 100644 --- a/lisp/pdf-links.el +++ b/lisp/pdf-links.el @@ -28,10 +28,12 @@ (require 'pdf-isearch) (require 'let-alist) (require 'org) -(eval-when-compile (require 'image-roll nil t)) + (defvar pdf-view-roll-minor-mode) +(declare-function image-roll-page-overlay "image-roll") +(declare-function image-roll-displayed-pages "image-roll") ;;; Code: @@ -211,7 +213,7 @@ scroll the current page." (with-selected-window window (when (derived-mode-p 'pdf-view-mode) (when (> .page 0) - (pdf-view-goto-page .page)) + (pdf-view-goto-page .page window)) (when .top (when (derived-mode-p 'pdf-view-mode) (pdf-util-tooltip-arrow .top))))))) @@ -227,8 +229,9 @@ scroll the current page." See `pdf-links-action-perform' for the interface." (pdf-util-assert-pdf-window) - (let* ((pages (if pdf-view-roll-minor-mode - (nreverse (image-roll-displayed-pages)) + (let* ((win (selected-window)) + (pages (if pdf-view-roll-minor-mode + (reverse (image-mode-window-get 'displayed-pages win)) (list (pdf-view-current-page)))) (links (mapcar #'pdf-cache-pagelinks pages)) (keys (pdf-links-read-link-action--create-keys @@ -243,21 +246,22 @@ See `pdf-links-action-perform' for the interface." (dolist (page pages) (pdf-view-display-image (create-image (pdf-util-convert-image - (or (overlay-get (image-roll-page-overlay page) 'display) + (or (overlay-get (image-roll-page-overlay page win) 'display) (pdf-view-current-image)) :foreground (car colors) :background (cdr colors) :formats `((?c . ,(lambda (_edges) (apply #'string (pop keys)))) (?P . ,(number-to-string - (max 1 (* (cdr (pdf-view-desired-image-size page)) + (max 1 (* (cdr (pdf-view-desired-image-size page win)) pdf-links-convert-pointsize-scale))))) :commands pdf-links-read-link-convert-commands :apply (pdf-util-scale-relative-to-pixel (mapcar (lambda (l) (cdr (assq 'edges l))) - (pop links)))) + (pop links)) + nil nil win)) (pdf-view-image-type) t) - page)) + page win)) (pdf-links-read-link-action--read-chars prompt alist)) (pdf-view-redisplay))))) diff --git a/lisp/pdf-roll.el b/lisp/pdf-roll.el index 00b147af..396e6dc0 100644 --- a/lisp/pdf-roll.el +++ b/lisp/pdf-roll.el @@ -25,6 +25,15 @@ (require 'image-roll) (require 'pdf-view) +(defun pdf-roll-page-image (page window) + "Function to retrieve image of the PAGE in WINDOW." + (let ((image (pdf-view-create-page page window))) + (if-let ((slice (pdf-view-current-slice window))) + (list (cons 'slice + (pdf-util-scale slice (image-size image t) 'round)) + image) + image))) + (define-minor-mode pdf-view-roll-minor-mode "If enabled display document on a virtual scroll providing continuous scrolling." :lighter " Continuous" @@ -41,9 +50,9 @@ (cond (pdf-view-roll-minor-mode (setq-local image-roll-last-page (pdf-cache-number-of-pages) - image-roll-display-page-function 'pdf-view-display-page + image-roll-page-image-function 'pdf-roll-page-image image-roll-center t - line-spacing image-roll-vertical-margin + face-remapping-alist '((default . image-roll-default)) mwheel-scroll-up-function #'image-roll-scroll-forward mwheel-scroll-down-function #'image-roll-scroll-backward) diff --git a/lisp/pdf-sync.el b/lisp/pdf-sync.el index 4a1c8fa7..b97b3d00 100644 --- a/lisp/pdf-sync.el +++ b/lisp/pdf-sync.el @@ -280,7 +280,7 @@ Has no effect if `pdf-sync-backward-use-heuristic' is nil." (pdf-sync-backward-search (car xy) (cdr xy) (and (bound-and-true-p pdf-view-roll-minor-mode) - (/ (1+ (posn-point posn)) 2))))) + (/ (+ (posn-point posn) 3) 4))))) (defun pdf-sync-backward-search (x y &optional page) "Go to the source corresponding to image coordinates X, Y on PAGE. @@ -659,7 +659,7 @@ Needs to have `pdf-sync-backward-debug-minor-mode' enabled." buffer pdf-sync-forward-display-action) (pdf-util-assert-pdf-window) (when page - (pdf-view-goto-page page) + (pdf-view-goto-page page (selected-window)) (when y1 (let ((top (* y1 (cdr (pdf-view-image-size))))) (pdf-util-tooltip-arrow (round top)))))) diff --git a/lisp/pdf-view.el b/lisp/pdf-view.el index c18d5407..519c799e 100644 --- a/lisp/pdf-view.el +++ b/lisp/pdf-view.el @@ -33,9 +33,6 @@ (require 'bookmark) (require 'password-cache) -(eval-when-compile - (require 'image-roll)) - (declare-function cua-copy-region "cua-base") (declare-function pdf-tools-pdf-buffer-p "pdf-tools") @@ -45,6 +42,7 @@ (declare-function image-roll-redisplay "image-roll") (declare-function image-roll-page-overlay "image-roll") (declare-function image-roll-page-at-current-pos "image-roll") +(declare-function image-roll-display-image "image-roll") (defvar pdf-view-roll-minor-mode) @@ -1078,7 +1076,7 @@ image. These values may be different, if slicing is used." (image-mode-window-get 'displayed-pages window)) (pdf-view-display-page (pdf-view-current-page window) window)) (overlay-get (image-roll-page-overlay - (image-roll-page-at-current-pos) window) + (pdf-view-current-page window) window) 'display)) (image-get-display-property)))) (if displayed-p @@ -1103,43 +1101,39 @@ It is equal to \(LEFT . TOP\) of the current slice in pixel." "Display page PAGE in WINDOW." (setf (pdf-view-window-needs-redisplay window) nil) (pdf-view-display-image - (pdf-view-create-page page window) - (when pdf-view-roll-minor-mode page) - window)) + (pdf-view-create-page page window) page window)) (defun pdf-view-display-image (image page &optional window inhibit-slice-p) ;; TODO: write documentation! - (let ((ol (if pdf-view-roll-minor-mode - (image-roll-page-overlay page window) - (pdf-view-current-overlay window)))) - (when (window-live-p (overlay-get ol 'window)) - (let* ((size (image-size image t)) - (slice (if (not inhibit-slice-p) - (pdf-view-current-slice window))) - (displayed-width (floor - (if slice - (* (nth 2 slice) - (car (image-size image))) - (car (image-size image)))))) - (setf (pdf-view-current-image window) image) - (unless pdf-view-roll-minor-mode - (move-overlay ol (point-min) (point-max))) - ;; In case the window is wider than the image, center the image - ;; horizontally. - (overlay-put ol 'before-string - (when (> (window-width window) - displayed-width) - (propertize " " 'display - `(space :align-to - ,(/ (- (window-width window) - displayed-width) 2))))) - (overlay-put ol 'display - (if slice - (list (cons 'slice - (pdf-util-scale slice size 'round)) - image) - image)) - (unless pdf-view-roll-minor-mode + (if pdf-view-roll-minor-mode + (image-roll-display-image image page (or window (selected-window))) + (let ((ol (pdf-view-current-overlay window))) + (when (window-live-p (overlay-get ol 'window)) + (let* ((size (image-size image t)) + (slice (if (not inhibit-slice-p) + (pdf-view-current-slice window))) + (displayed-width (floor + (if slice + (* (nth 2 slice) + (car (image-size image))) + (car (image-size image)))))) + (setf (pdf-view-current-image window) image) + (move-overlay ol (point-min) (point-max)) + ;; In case the window is wider than the image, center the image + ;; horizontally. + (overlay-put ol 'before-string + (when (> (window-width window) + displayed-width) + (propertize " " 'display + `(space :align-to + ,(/ (- (window-width window) + displayed-width) 2))))) + (overlay-put ol 'display + (if slice + (list (cons 'slice + (pdf-util-scale slice size 'round)) + image) + image)) (let* ((win (overlay-get ol 'window)) (hscroll (image-mode-window-get 'hscroll win)) (vscroll (image-mode-window-get 'vscroll win))) From f9b735d6d904197a79ee024bf5e2059d91f6f011 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Sun, 25 Jun 2023 22:28:03 +0200 Subject: [PATCH 034/104] Try to find a live window that displayed buffer when bookmarking --- lisp/pdf-view.el | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/lisp/pdf-view.el b/lisp/pdf-view.el index 519c799e..941e2852 100644 --- a/lisp/pdf-view.el +++ b/lisp/pdf-view.el @@ -1715,23 +1715,24 @@ the selection styles." The optional, boolean args exclude certain attributes." (or pdf-view--bookmark-to-restore - (let ((displayed-p (eq (current-buffer) - (window-buffer)))) + (let ((win (car (cl-find-if #'window-live-p image-mode-winprops-alist + :key #'car-safe)))) (cons (buffer-name) - (append (bookmark-make-record-default nil t (if pdf-view-roll-minor-mode (point) 1)) + (append (bookmark-make-record-default + nil t (if pdf-view-roll-minor-mode (point) 1)) `(,(unless no-page - (cons 'page (pdf-view-current-page))) + (cons 'page (pdf-view-current-page win))) ,(unless no-slice - (cons 'slice (and displayed-p - (pdf-view-current-slice)))) + (cons 'slice (and win (pdf-view-current-slice win)))) ,(unless no-size (cons 'size pdf-view-display-size)) ,(unless no-origin (cons 'origin - (and displayed-p - (let ((edges (pdf-util-image-displayed-edges nil t))) + (and win + (let* ((edges (pdf-util-image-displayed-edges win t))) (pdf-util-scale-pixel-to-relative - (cons (car edges) (cadr edges)) nil t))))) + (cons (car edges) (cadr edges)) nil + (eq (current-buffer) (window-buffer)) win))))) (handler . pdf-view-bookmark-jump-handler))))))) ;;;###autoload From 87526748daa9e71637264f39e9c618cc91af7df3 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Mon, 26 Jun 2023 15:09:56 +0200 Subject: [PATCH 035/104] Better integration of pdf-roll with pdf-view-display-image --- lisp/pdf-roll.el | 24 ++++++++++++++++++------ lisp/pdf-view.el | 5 +++-- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/lisp/pdf-roll.el b/lisp/pdf-roll.el index 396e6dc0..0fd7ceaf 100644 --- a/lisp/pdf-roll.el +++ b/lisp/pdf-roll.el @@ -25,14 +25,26 @@ (require 'image-roll) (require 'pdf-view) + +(defun pdf-roll-maybe-slice-image (image &optional window inhibit-slice-p) + "Return a sliced IMAGE if `pdf-view-current-slice' in WINDOW is non-nil. +If INHIBIT-SLICE-P is non-nil, disregard `pdf-view-current-slice'." + (if-let ((slice (pdf-view-current-slice window)) + ((not inhibit-slice-p))) + (list (cons 'slice + (pdf-util-scale slice (image-size image t) 'round)) + image) + image)) + +(defun pdf-roll-display-image (image page &optional window inhibit-slice-p) + "Display IMAGE for PAGE in WINDOW. +If INHIBIT-SLICE-P is non-nil, disregard `pdf-view-current-slice'." + (let ((image (pdf-roll-maybe-slice-image image window inhibit-slice-p))) + (image-roll-display-image image page window))) + (defun pdf-roll-page-image (page window) "Function to retrieve image of the PAGE in WINDOW." - (let ((image (pdf-view-create-page page window))) - (if-let ((slice (pdf-view-current-slice window))) - (list (cons 'slice - (pdf-util-scale slice (image-size image t) 'round)) - image) - image))) + (pdf-roll-maybe-slice-image (pdf-view-create-page page window) window)) (define-minor-mode pdf-view-roll-minor-mode "If enabled display document on a virtual scroll providing continuous scrolling." diff --git a/lisp/pdf-view.el b/lisp/pdf-view.el index 941e2852..512b1411 100644 --- a/lisp/pdf-view.el +++ b/lisp/pdf-view.el @@ -42,7 +42,7 @@ (declare-function image-roll-redisplay "image-roll") (declare-function image-roll-page-overlay "image-roll") (declare-function image-roll-page-at-current-pos "image-roll") -(declare-function image-roll-display-image "image-roll") +(declare-function pdf-roll-display-image "pdf-roll") (defvar pdf-view-roll-minor-mode) @@ -1106,7 +1106,8 @@ It is equal to \(LEFT . TOP\) of the current slice in pixel." (defun pdf-view-display-image (image page &optional window inhibit-slice-p) ;; TODO: write documentation! (if pdf-view-roll-minor-mode - (image-roll-display-image image page (or window (selected-window))) + (pdf-roll-display-image + image page (or window (selected-window)) inhibit-slice-p) (let ((ol (pdf-view-current-overlay window))) (when (window-live-p (overlay-get ol 'window)) (let* ((size (image-size image t)) From f2f7918692c4c5c542400ee0b4d4d3ad3fb2dfa6 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Mon, 26 Jun 2023 15:11:08 +0200 Subject: [PATCH 036/104] Fix pdf-links hopefully correctly this time The image convert needs is one without any size related stuff in its image spec. So remove them and rescale the edges according to the size of this image --- lisp/pdf-links.el | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/lisp/pdf-links.el b/lisp/pdf-links.el index e5e11c7e..5b1436e8 100644 --- a/lisp/pdf-links.el +++ b/lisp/pdf-links.el @@ -244,24 +244,30 @@ See `pdf-links-action-perform' for the interface." (unwind-protect (progn (dolist (page pages) - (pdf-view-display-image - (create-image (pdf-util-convert-image - (or (overlay-get (image-roll-page-overlay page win) 'display) - (pdf-view-current-image)) - :foreground (car colors) - :background (cdr colors) - :formats - `((?c . ,(lambda (_edges) (apply #'string (pop keys)))) - (?P . ,(number-to-string - (max 1 (* (cdr (pdf-view-desired-image-size page win)) - pdf-links-convert-pointsize-scale))))) - :commands pdf-links-read-link-convert-commands - :apply (pdf-util-scale-relative-to-pixel - (mapcar (lambda (l) (cdr (assq 'edges l))) - (pop links)) - nil nil win)) - (pdf-view-image-type) t) - page win)) + (let* ((image (or (overlay-get (image-roll-page-overlay page win) 'display) + (pdf-view-current-image))) + (image (or (assoc 'image image) image)) + (height (cdr (image-size image t))) + (orig-image (create-image (plist-get (cdr image) :data) + (pdf-view-image-type) t))) + (pdf-view-display-image + (create-image (pdf-util-convert-image + orig-image + :foreground (car colors) + :background (cdr colors) + :formats + `((?c . ,(lambda (_edges) (apply #'string (pop keys)))) + (?P . ,(number-to-string + (max 1 (* height + pdf-links-convert-pointsize-scale))))) + :commands pdf-links-read-link-convert-commands + :apply (pdf-util-scale + (mapcar (lambda (l) (cdr (assq 'edges l))) + (pop links)) + (image-size orig-image t))) + (pdf-view-image-type) t + :height height) + page win))) (pdf-links-read-link-action--read-chars prompt alist)) (pdf-view-redisplay))))) From 953e3e3cc23f28029e238495721419debfe9b518 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Tue, 27 Jun 2023 00:45:21 +0200 Subject: [PATCH 037/104] Fix text selection with pdf-roll --- lisp/pdf-view.el | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/lisp/pdf-view.el b/lisp/pdf-view.el index 512b1411..f10010a8 100644 --- a/lisp/pdf-view.el +++ b/lisp/pdf-view.el @@ -1494,6 +1494,7 @@ Stores the region in `pdf-view-active-region'." (setq begin-inside-image-p nil) (posn-x-y pos))) (abs-begin (posn-x-y pos)) + (page (/ (+ 3 (posn-point pos)) 4)) (selection-style (or selection-style pdf-view-selection-style)) pdf-view-continuous region) @@ -1536,20 +1537,31 @@ Stores the region in `pdf-view-active-region'." (+ (car begin) (car dxy)))) (max 0 (min (cdr size) (+ (cdr begin) (cdr dxy))))))))) - (let ((iregion (if rectangle-p - (list (min (car begin) (car end)) - (min (cdr begin) (cdr end)) - (max (car begin) (car end)) - (max (cdr begin) (cdr end))) - (list (car begin) (cdr begin) - (car end) (cdr end))))) + (let* ((iregion (if rectangle-p + (list (min (car begin) (car end)) + (min (cdr begin) (cdr end)) + (max (car begin) (car end)) + (max (cdr begin) (cdr end))) + (list (car begin) (cdr begin) + (car end) (cdr end)))) + (y (cdr (posn-x-y pos))) + (dy (- y (cdr abs-begin)))) (setq region (pdf-util-scale-pixel-to-relative iregion)) (pdf-view-display-region (cons region pdf-view-active-region) rectangle-p - selection-style) - (pdf-util-scroll-to-edges iregion))))) + selection-style + page) + (if pdf-view-roll-minor-mode + (cond + ((and (> dy 0) (< (- (window-text-height window t) y) 20)) + (image-roll-scroll-forward + (min 20 (or (nth 3 (pos-visible-in-window-p (posn-point pos) window t)) 0)))) + ((and (< dy 0) (< (- y (window-header-line-height window)) 20)) + (image-roll-scroll-backward + (min 20 (or (nth 2 (pos-visible-in-window-p (posn-point pos) window t)) 0))))) + (pdf-util-scroll-to-edges iregion)))))) (setq pdf-view-active-region (append pdf-view-active-region (list region))) @@ -1571,7 +1583,7 @@ This is more useful for commands like (interactive "@e") (pdf-view-mouse-set-region event nil t)) -(defun pdf-view-display-region (&optional region rectangle-p selection-style) +(defun pdf-view-display-region (&optional region rectangle-p selection-style page) ;; TODO: write documentation! (unless region (pdf-view-assert-active-region) @@ -1579,7 +1591,7 @@ This is more useful for commands like (let ((colors (pdf-util-face-colors (if rectangle-p 'pdf-view-rectangle 'pdf-view-region) (bound-and-true-p pdf-view-dark-minor-mode))) - (page (pdf-view-current-page)) + (page (or page (pdf-view-current-page))) (width (car (pdf-view-image-size)))) (pdf-view-display-image (pdf-view-create-image From 44d6403cca29f158d16d71677ea01ea027f7b6a5 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Tue, 27 Jun 2023 23:20:57 +0200 Subject: [PATCH 038/104] Fix isearch pdf-occur --- lisp/pdf-isearch.el | 87 ++++++++++++++++++++++++++------------------- lisp/pdf-occur.el | 11 +++--- 2 files changed, 57 insertions(+), 41 deletions(-) diff --git a/lisp/pdf-isearch.el b/lisp/pdf-isearch.el index 8ad33100..ef97ebf0 100644 --- a/lisp/pdf-isearch.el +++ b/lisp/pdf-isearch.el @@ -33,7 +33,9 @@ (require 'let-alist) ;;; Code: +(defvar pdf-view-roll-minor-mode) +(defvar pdf-isearch--hl-matches-tick 0) ;; * ================================================================== * @@ -249,31 +251,40 @@ This is a Isearch interface function." (when (> (length string) 0) (let ((same-search-p (pdf-isearch-same-search-p)) (oldpage pdf-isearch-current-page) - (matches (pdf-isearch-search-page string)) + (pages (or (image-mode-window-get 'displayed-pages (selected-window)) + (pdf-view-current-page))) + matches next-match) - ;; matches is a list of list of edges ((x0 y1 x1 y2) ...), - ;; sorted top to bottom ,left to right. Coordinates are in image - ;; space. - (unless isearch-forward - (setq matches (reverse matches))) - (when pdf-isearch-filter-matches-function - (setq matches (funcall pdf-isearch-filter-matches-function matches))) + (dolist (page pages) + (let ((page-matches (pdf-isearch-search-page string page))) + ;; matches is a list of list of edges ((x0 y1 x1 y2) ...), + ;; sorted top to bottom ,left to right. Coordinates are in image + ;; space. + (unless isearch-forward + (setq page-matches (reverse page-matches))) + (when pdf-isearch-filter-matches-function + (setq page-matches (funcall pdf-isearch-filter-matches-function page-matches))) + (push page-matches matches))) ;; Where to go next ? (setq pdf-isearch-current-page (pdf-view-current-page) - pdf-isearch-current-matches matches + pdf-isearch-current-matches (car matches) next-match (pdf-isearch-next-match oldpage pdf-isearch-current-page - pdf-isearch-current-match matches + pdf-isearch-current-match (car matches) same-search-p isearch-forward) pdf-isearch-current-parameter (list string isearch-regexp isearch-case-fold-search isearch-word)) + (cl-callf nreverse matches) (cond (next-match (setq pdf-isearch-current-match next-match) - (pdf-isearch-hl-matches next-match matches) + (cl-incf pdf-isearch--hl-matches-tick) + (dolist (page pages) + (pdf-isearch-hl-matches (when (eq page pdf-isearch-current-page) next-match) + (pop matches) nil page)) (pdf-isearch-focus-match next-match) ;; Don't get off track. (when (or (and (bobp) (not isearch-forward)) @@ -285,11 +296,12 @@ This is a Isearch interface function." (re-search-forward ".") (re-search-backward "."))) ((and (not pdf-isearch-narrow-to-page) - (not (pdf-isearch-empty-match-p matches))) + (not (pdf-isearch-empty-match-p pdf-isearch-current-matches))) (let ((next-page (pdf-isearch-find-next-matching-page string pdf-isearch-current-page t))) (when next-page (pdf-view-goto-page next-page) + (when pdf-view-roll-minor-mode (image-roll-pre-redisplay (selected-window))) (pdf-isearch-search-function string)))))))) (defun pdf-isearch-push-state-function () @@ -309,6 +321,7 @@ This is a Isearch interface function." pdf-isearch-current-page page) (pdf-view-goto-page pdf-isearch-current-page) + (when pdf-view-roll-minor-mode (image-roll-pre-redisplay (selected-window))) (when pdf-isearch-current-match (pdf-isearch-hl-matches pdf-isearch-current-match @@ -326,6 +339,7 @@ This is a Isearch interface function." (unless (or pdf-isearch-narrow-to-page (= page (pdf-view-current-page))) (pdf-view-goto-page page) + (when pdf-view-roll-minor-mode (image-roll-pre-redisplay (selected-window))) (let ((next-screen-context-lines 0)) (if (= page 1) (image-scroll-down) @@ -387,6 +401,7 @@ there was no previous search, this function returns t." (defun pdf-isearch-redisplay () "Redisplay the current highlighting." + (cl-incf pdf-isearch--hl-matches-tick) (pdf-isearch-hl-matches pdf-isearch-current-match pdf-isearch-current-matches)) @@ -566,10 +581,10 @@ is no such page." (= incr 8)) ;;Don't bother right away. (setq reporter (apply - 'make-progress-reporter "Searching" - (if isearch-forward - (list (car pages) (pdf-cache-number-of-pages) nil 0) - (list 1 (cdr pages) nil 0))))) + 'make-progress-reporter "Searching" + (if isearch-forward + (list (car pages) (pdf-cache-number-of-pages) nil 0) + (list 1 (cdr pages) nil 0))))) (when reporter (progress-reporter-update reporter (if isearch-forward @@ -676,18 +691,18 @@ it is assumed to be ordered with respect to FORWARD-P." (let ((matched (apply 'pdf-util-edges-union match))) (pdf-util-with-edges (matched) (cl-loop for next in matches do - (let ((edges (apply 'pdf-util-edges-union next))) - (pdf-util-with-edges (edges) - (when (if forward-p - (or (>= edges-top matched-bot) - (and (or (>= edges-top matched-top) - (>= edges-bot matched-bot)) - (>= edges-right matched-right))) - (or (<= edges-bot matched-top) - (and (or (<= edges-bot matched-bot) - (<= edges-top matched-top)) - (<= edges-left matched-left)))) - (cl-return next)))))))) + (let ((edges (apply 'pdf-util-edges-union next))) + (pdf-util-with-edges (edges) + (when (if forward-p + (or (>= edges-top matched-bot) + (and (or (>= edges-top matched-top) + (>= edges-bot matched-bot)) + (>= edges-right matched-right))) + (or (<= edges-bot matched-top) + (and (or (<= edges-bot matched-bot) + (<= edges-top matched-top)) + (<= edges-left matched-left)))) + (cl-return next)))))))) @@ -718,19 +733,18 @@ MATCH-BG LAZY-FG LAZY-BG\)." (car lazy) (cdr lazy))))))) -(defvar pdf-isearch--hl-matches-tick 0) - -(defun pdf-isearch-hl-matches (current matches &optional occur-hack-p) +(defun pdf-isearch-hl-matches (current matches &optional occur-hack-p page) "Highlighting edges CURRENT and MATCHES." (cl-check-type current pdf-isearch-match) (cl-check-type matches (list-of pdf-isearch-match)) + (setq page (or page (pdf-view-current-page))) (cl-destructuring-bind (fg1 bg1 fg2 bg2) (pdf-isearch-current-colors) (let* ((width (car (pdf-view-image-size))) - (page (pdf-view-current-page)) (window (selected-window)) + (page (or page (pdf-view-current-page))) (buffer (current-buffer)) - (tick (cl-incf pdf-isearch--hl-matches-tick)) + (tick pdf-isearch--hl-matches-tick) (pdf-info-asynchronous (lambda (status data) (when (and (null status) @@ -743,9 +757,8 @@ MATCH-BG LAZY-FG LAZY-BG\)." (when (and (derived-mode-p 'pdf-view-mode) (or isearch-mode occur-hack-p) - (eq page (pdf-view-current-page))) - (when pdf-view-roll-minor-mode - (pdf-view-goto-page page)) + (or (eq page (pdf-view-current-page)) + (memq page (image-mode-window-get 'displayed-pages window)))) (pdf-view-display-image (pdf-view-create-image data :width width) (when pdf-view-roll-minor-mode page)))))))) @@ -755,7 +768,7 @@ MATCH-BG LAZY-FG LAZY-BG\)." current)) `(,fg2 ,bg2 ,@(pdf-util-scale-pixel-to-relative (apply 'append - (remove current matches)))))))) + (remove current matches)))))))) ;; * ================================================================== * diff --git a/lisp/pdf-occur.el b/lisp/pdf-occur.el index 47bf2d29..16b57e02 100644 --- a/lisp/pdf-occur.el +++ b/lisp/pdf-occur.el @@ -282,12 +282,15 @@ FIXME: EVENT not used at the moment." (setq window (selected-window))) (with-selected-window window (when page - (pdf-view-goto-page page)) + (pdf-view-goto-page page) + (when pdf-view-roll-minor-mode + (image-roll-pre-redisplay window))) ;; Abuse isearch. (when match (let ((pixel-match (pdf-util-scale-relative-to-pixel match)) (pdf-isearch-batch-mode t)) + (cl-incf pdf-isearch--hl-matches-tick) (pdf-isearch-hl-matches pixel-match nil t) (pdf-isearch-focus-match-batch pixel-match)))))))) @@ -629,9 +632,9 @@ matches linked with PAGE." (setq pdf-occur-number-of-matches 0) (setq pdf-occur-search-pages-left (apply #'+ (mapcar (lambda (elt) - (1+ (- (cdr (nth 1 elt)) - (car (nth 1 elt))))) - batches))))) + (1+ (- (cdr (nth 1 elt)) + (car (nth 1 elt))))) + batches))))) From 83aa3deeda2aafa3a0a1d84b63a9a6b612ec5e6c Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Wed, 28 Jun 2023 15:14:35 +0200 Subject: [PATCH 039/104] Streamline divergence from upstream --- lisp/pdf-isearch.el | 5 +-- lisp/pdf-occur.el | 4 +- lisp/pdf-util.el | 107 ++++++++++++++++++++++---------------------- lisp/pdf-view.el | 11 ++--- 4 files changed, 62 insertions(+), 65 deletions(-) diff --git a/lisp/pdf-isearch.el b/lisp/pdf-isearch.el index ef97ebf0..a382be35 100644 --- a/lisp/pdf-isearch.el +++ b/lisp/pdf-isearch.el @@ -301,7 +301,6 @@ This is a Isearch interface function." string pdf-isearch-current-page t))) (when next-page (pdf-view-goto-page next-page) - (when pdf-view-roll-minor-mode (image-roll-pre-redisplay (selected-window))) (pdf-isearch-search-function string)))))))) (defun pdf-isearch-push-state-function () @@ -321,7 +320,6 @@ This is a Isearch interface function." pdf-isearch-current-page page) (pdf-view-goto-page pdf-isearch-current-page) - (when pdf-view-roll-minor-mode (image-roll-pre-redisplay (selected-window))) (when pdf-isearch-current-match (pdf-isearch-hl-matches pdf-isearch-current-match @@ -339,7 +337,6 @@ This is a Isearch interface function." (unless (or pdf-isearch-narrow-to-page (= page (pdf-view-current-page))) (pdf-view-goto-page page) - (when pdf-view-roll-minor-mode (image-roll-pre-redisplay (selected-window))) (let ((next-screen-context-lines 0)) (if (= page 1) (image-scroll-down) @@ -761,7 +758,7 @@ MATCH-BG LAZY-FG LAZY-BG\)." (memq page (image-mode-window-get 'displayed-pages window)))) (pdf-view-display-image (pdf-view-create-image data :width width) - (when pdf-view-roll-minor-mode page)))))))) + page))))))) (pdf-info-renderpage-text-regions page width t nil nil `(,fg1 ,bg1 ,@(pdf-util-scale-pixel-to-relative diff --git a/lisp/pdf-occur.el b/lisp/pdf-occur.el index 16b57e02..4ad54dfc 100644 --- a/lisp/pdf-occur.el +++ b/lisp/pdf-occur.el @@ -282,9 +282,7 @@ FIXME: EVENT not used at the moment." (setq window (selected-window))) (with-selected-window window (when page - (pdf-view-goto-page page) - (when pdf-view-roll-minor-mode - (image-roll-pre-redisplay window))) + (pdf-view-goto-page page)) ;; Abuse isearch. (when match (let ((pixel-match diff --git a/lisp/pdf-util.el b/lisp/pdf-util.el index ae42e86e..35b1f2c4 100644 --- a/lisp/pdf-util.el +++ b/lisp/pdf-util.el @@ -386,42 +386,45 @@ needed. Note: For versions of emacs before 27 this will return lines instead of pixels. This is because of a change that occurred to `image-mode' in 27." (pdf-util-assert-pdf-window) - (let* ((win (window-inside-pixel-edges)) - (image-height (cdr (pdf-view-image-size - (unless pdf-view-roll-minor-mode - t)))) - (image-top (window-vscroll nil t)) - (edges (pdf-util-translate - edges - (pdf-view-image-offset) t))) - (pdf-util-with-edges (win edges) - (let* ((context-pixel (or context-pixel - (* next-screen-context-lines - (frame-char-height)))) - ;;Be careful not to modify edges. - (edges-top (- edges-top context-pixel)) - (edges-bot (+ edges-bot context-pixel)) - (vscroll - (cond ((< edges-top image-top) - (max 0 (if eager-p - (- edges-bot win-height) - edges-top))) - ((> (min image-height - edges-bot) - (+ image-top win-height)) - (min (- image-height win-height) - (if eager-p - edges-top - (- edges-bot win-height))))))) - - - (when vscroll - (round - ;; `image-set-window-vscroll' changed in version 27 to using - ;; pixels, not lines. - (if (version< emacs-version "27") - (/ vscroll (float (frame-char-height))) - vscroll))))))) + (if pdf-view-roll-minor-mode + (max 0 (- (nth 1 edges) + (* next-screen-context-lines (frame-char-height)))) + (let* ((win (window-inside-pixel-edges)) + (image-height (cdr (pdf-view-image-size + (unless pdf-view-roll-minor-mode + t)))) + (image-top (window-vscroll nil t)) + (edges (pdf-util-translate + edges + (pdf-view-image-offset) t))) + (pdf-util-with-edges (win edges) + (let* ((context-pixel (or context-pixel + (* next-screen-context-lines + (frame-char-height)))) + ;;Be careful not to modify edges. + (edges-top (- edges-top context-pixel)) + (edges-bot (+ edges-bot context-pixel)) + (vscroll + (cond ((< edges-top image-top) + (max 0 (if eager-p + (- edges-bot win-height) + edges-top))) + ((> (min image-height + edges-bot) + (+ image-top win-height)) + (min (- image-height win-height) + (if eager-p + edges-top + (- edges-bot win-height))))))) + + + (when vscroll + (round + ;; `image-set-window-vscroll' changed in version 27 to using + ;; pixels, not lines. + (if (version< emacs-version "27") + (/ vscroll (float (frame-char-height))) + vscroll)))))))) (defun pdf-util-scroll-to-edges (edges &optional eager-p) "Scroll window such that image EDGES are visible. @@ -629,9 +632,7 @@ string." (dy image-top) (pos (list dx dy dx (+ dy (* 2 (frame-char-height))))) (vscroll - (if pdf-view-roll-minor-mode - image-top - (pdf-util-required-vscroll pos))) + (pdf-util-required-vscroll pos)) (tooltip-frame-parameters `((border-width . 0) (internal-border-width . 0) @@ -790,8 +791,8 @@ respective sequence." (cl-macrolet ((make-matrix (rows columns) `(apply #'vector - (cl-loop for i from 1 to ,rows - collect (make-vector ,columns nil)))) + (cl-loop for i from 1 to ,rows + collect (make-vector ,columns nil)))) (mset (matrix row column newelt) `(aset (aref ,matrix ,row) ,column ,newelt)) (mref (matrix row column) @@ -806,21 +807,21 @@ respective sequence." (if (equal a b) 1 -1))))) (cl-loop for i from 0 to len1 do - (mset d i 0 (- i))) + (mset d i 0 (- i))) (cl-loop for j from 0 to len2 do - (mset d 0 j (if suffix-p 0 (- j)))) + (mset d 0 j (if suffix-p 0 (- j)))) (cl-loop for i from 1 to len1 do - (cl-loop for j from 1 to len2 do - (let ((max (max - (1- (mref d (1- i) j)) - (+ (mref d i (1- j)) - (if (and prefix-p (= i len1)) 0 -1)) - (+ (mref d (1- i) (1- j)) - (funcall similarity-fn - (elt seq1 (1- i)) - (elt seq2 (1- j))))))) - (mset d i j max)))) + (cl-loop for j from 1 to len2 do + (let ((max (max + (1- (mref d (1- i) j)) + (+ (mref d i (1- j)) + (if (and prefix-p (= i len1)) 0 -1)) + (+ (mref d (1- i) (1- j)) + (funcall similarity-fn + (elt seq1 (1- i)) + (elt seq2 (1- j))))))) + (mset d i j max)))) (let ((i len1) (j len2) diff --git a/lisp/pdf-view.el b/lisp/pdf-view.el index f10010a8..f3ac51ce 100644 --- a/lisp/pdf-view.el +++ b/lisp/pdf-view.el @@ -40,6 +40,7 @@ (declare-function image-roll-scroll-backward "image-roll") (declare-function image-roll-next-page "image-roll") (declare-function image-roll-redisplay "image-roll") +(declare-function image-roll-pre-redisplay "image-roll") (declare-function image-roll-page-overlay "image-roll") (declare-function image-roll-page-at-current-pos "image-roll") (declare-function pdf-roll-display-image "pdf-roll") @@ -696,7 +697,9 @@ windows." (run-hooks 'pdf-view-change-page-hook)) (when (window-live-p window) (image-set-window-vscroll 0) - (pdf-view-redisplay window)) + (if pdf-view-roll-minor-mode + (image-roll-pre-redisplay window) + (pdf-view-redisplay window))) (when changing-p (pdf-view-deactivate-region) (force-mode-line-update) @@ -708,10 +711,8 @@ windows." Optional parameter N moves N pages forward." (interactive "p") - (if pdf-view-roll-minor-mode - (image-roll-next-page n) - (pdf-view-goto-page (+ (pdf-view-current-page) - (or n 1))))) + (pdf-view-goto-page (+ (pdf-view-current-page) + (or n 1)))) (defun pdf-view-previous-page (&optional n) "View the previous page in the PDF. From f02a48acda6b4a7c2d80bd518e956d4472d361c2 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Wed, 28 Jun 2023 18:31:17 +0200 Subject: [PATCH 040/104] Further isearch fixes + autoload pdf roll minor mode --- lisp/pdf-isearch.el | 85 ++++++++++++++++++++++++--------------------- lisp/pdf-occur.el | 1 - lisp/pdf-roll.el | 1 + lisp/pdf-tools.el | 1 - lisp/pdf-view.el | 16 ++++----- 5 files changed, 54 insertions(+), 50 deletions(-) diff --git a/lisp/pdf-isearch.el b/lisp/pdf-isearch.el index a382be35..ab8b1aea 100644 --- a/lisp/pdf-isearch.el +++ b/lisp/pdf-isearch.el @@ -267,7 +267,7 @@ This is a Isearch interface function." (push page-matches matches))) ;; Where to go next ? (setq pdf-isearch-current-page (pdf-view-current-page) - pdf-isearch-current-matches (car matches) + pdf-isearch-current-matches matches next-match (pdf-isearch-next-match oldpage pdf-isearch-current-page @@ -282,9 +282,7 @@ This is a Isearch interface function." (next-match (setq pdf-isearch-current-match next-match) (cl-incf pdf-isearch--hl-matches-tick) - (dolist (page pages) - (pdf-isearch-hl-matches (when (eq page pdf-isearch-current-page) next-match) - (pop matches) nil page)) + (pdf-isearch-hl-matches next-match matches nil pages) (pdf-isearch-focus-match next-match) ;; Don't get off track. (when (or (and (bobp) (not isearch-forward)) @@ -323,7 +321,8 @@ This is a Isearch interface function." (when pdf-isearch-current-match (pdf-isearch-hl-matches pdf-isearch-current-match - pdf-isearch-current-matches)) + pdf-isearch-current-matches + nil (image-mode-window-get 'displayed-pages (selected-window)))) (image-set-window-hscroll hscroll) (image-set-window-vscroll vscroll)))) @@ -398,9 +397,12 @@ there was no previous search, this function returns t." (defun pdf-isearch-redisplay () "Redisplay the current highlighting." - (cl-incf pdf-isearch--hl-matches-tick) - (pdf-isearch-hl-matches pdf-isearch-current-match - pdf-isearch-current-matches)) + (pdf-isearch-hl-matches + pdf-isearch-current-match + pdf-isearch-current-matches + nil + (or (image-mode-window-get 'displayed-pages (selected-window)) + (list (pdf-view-current-page))))) (defun pdf-isearch-update () "Update search and redisplay, if necessary." @@ -432,7 +434,7 @@ there was no previous search, this function returns t." (cl-every (lambda (edges) (cl-every 'zerop edges)) match)) - matches))) + (apply #'append matches)))) (defun pdf-isearch-occur () "Run `occur' using the last search string or regexp." @@ -730,42 +732,45 @@ MATCH-BG LAZY-FG LAZY-BG\)." (car lazy) (cdr lazy))))))) -(defun pdf-isearch-hl-matches (current matches &optional occur-hack-p page) +(defun pdf-isearch-hl-matches (current matches &optional occur-hack-p pages) "Highlighting edges CURRENT and MATCHES." (cl-check-type current pdf-isearch-match) - (cl-check-type matches (list-of pdf-isearch-match)) - (setq page (or page (pdf-view-current-page))) + (cl-check-type matches (list-of (list-of pdf-isearch-match))) (cl-destructuring-bind (fg1 bg1 fg2 bg2) (pdf-isearch-current-colors) - (let* ((width (car (pdf-view-image-size))) - (window (selected-window)) - (page (or page (pdf-view-current-page))) + (let* ((window (selected-window)) + (pages (or pages + (image-mode-window-get 'displayed-pages (selected-window)) + (list (pdf-view-current-page)))) (buffer (current-buffer)) - (tick pdf-isearch--hl-matches-tick) - (pdf-info-asynchronous - (lambda (status data) - (when (and (null status) - (eq tick pdf-isearch--hl-matches-tick) - (buffer-live-p buffer) - (window-live-p window) - (eq (window-buffer window) - buffer)) - (with-selected-window window - (when (and (derived-mode-p 'pdf-view-mode) - (or isearch-mode - occur-hack-p) - (or (eq page (pdf-view-current-page)) - (memq page (image-mode-window-get 'displayed-pages window)))) - (pdf-view-display-image - (pdf-view-create-image data :width width) - page))))))) - (pdf-info-renderpage-text-regions - page width t nil nil - `(,fg1 ,bg1 ,@(pdf-util-scale-pixel-to-relative - current)) - `(,fg2 ,bg2 ,@(pdf-util-scale-pixel-to-relative - (apply 'append - (remove current matches)))))))) + (tick (cl-incf pdf-isearch--hl-matches-tick))) + (dolist (page pages) + (let* ((width (car (pdf-view-image-size nil window page))) + (pdf-info-asynchronous + (lambda (status data) + (when (and (null status) + (eq tick pdf-isearch--hl-matches-tick) + (buffer-live-p buffer) + (window-live-p window) + (eq (window-buffer window) + buffer)) + (with-selected-window window + (when (and (derived-mode-p 'pdf-view-mode) + (or isearch-mode occur-hack-p + (memq last-command '(isearch-repeat-forward isearch-repeat-backward))) + (or (eq page (pdf-view-current-page)) + (memq page (image-mode-window-get 'displayed-pages window)))) + (pdf-view-display-image + (pdf-view-create-image data :width width) + page window))))))) + (pdf-info-renderpage-text-regions + page width t nil nil + `(,fg1 ,bg1 ,@(pdf-util-scale-pixel-to-relative + (when (eq page (pdf-view-current-page window)) + current))) + `(,fg2 ,bg2 ,@(pdf-util-scale-pixel-to-relative + (apply 'append + (remove current (pop matches))))))))))) ;; * ================================================================== * diff --git a/lisp/pdf-occur.el b/lisp/pdf-occur.el index 4ad54dfc..ea5fa174 100644 --- a/lisp/pdf-occur.el +++ b/lisp/pdf-occur.el @@ -288,7 +288,6 @@ FIXME: EVENT not used at the moment." (let ((pixel-match (pdf-util-scale-relative-to-pixel match)) (pdf-isearch-batch-mode t)) - (cl-incf pdf-isearch--hl-matches-tick) (pdf-isearch-hl-matches pixel-match nil t) (pdf-isearch-focus-match-batch pixel-match)))))))) diff --git a/lisp/pdf-roll.el b/lisp/pdf-roll.el index 0fd7ceaf..dca88563 100644 --- a/lisp/pdf-roll.el +++ b/lisp/pdf-roll.el @@ -46,6 +46,7 @@ If INHIBIT-SLICE-P is non-nil, disregard `pdf-view-current-slice'." "Function to retrieve image of the PAGE in WINDOW." (pdf-roll-maybe-slice-image (pdf-view-create-page page window) window)) +;;;###autoload (define-minor-mode pdf-view-roll-minor-mode "If enabled display document on a virtual scroll providing continuous scrolling." :lighter " Continuous" diff --git a/lisp/pdf-tools.el b/lisp/pdf-tools.el index 43eb7cba..670792cb 100644 --- a/lisp/pdf-tools.el +++ b/lisp/pdf-tools.el @@ -94,7 +94,6 @@ (require 'pdf-view) (require 'pdf-util) (require 'pdf-info) -(require 'pdf-roll) (require 'cus-edit) (require 'compile) (require 'cl-lib) diff --git a/lisp/pdf-view.el b/lisp/pdf-view.el index f3ac51ce..ad37ed04 100644 --- a/lisp/pdf-view.el +++ b/lisp/pdf-view.el @@ -1066,19 +1066,19 @@ See also `pdf-view-use-imagemagick'." :map hotspots :pointer 'arrow))) -(defun pdf-view-image-size (&optional displayed-p window) +(defun pdf-view-image-size (&optional displayed-p window page) "Return the size in pixel of the current image in WINDOW. If DISPLAYED-P is non-nil, return the size of the displayed -image. These values may be different, if slicing is used." +image. These values may be different, if slicing is used. + +If PAGE is non-nil return its size instead of current page." (let ((display-prop (if pdf-view-roll-minor-mode (progn (setq window (if (windowp window) window (selected-window))) - (unless (memq (pdf-view-current-page window) - (image-mode-window-get 'displayed-pages window)) - (pdf-view-display-page (pdf-view-current-page window) window)) - (overlay-get (image-roll-page-overlay - (pdf-view-current-page window) window) - 'display)) + (setq page (or page (pdf-view-current-page window))) + (unless (memq page (image-mode-window-get 'displayed-pages window)) + (pdf-view-display-page page window)) + (overlay-get (image-roll-page-overlay page window) 'display)) (image-get-display-property)))) (if displayed-p (image-display-size display-prop t) From 5fa27c07d1d5ab2b71f1dec92e3d700d2ab2a55e Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Tue, 1 Aug 2023 09:09:28 +0200 Subject: [PATCH 041/104] Remap scroll-up/down commands --- lisp/pdf-roll.el | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lisp/pdf-roll.el b/lisp/pdf-roll.el index dca88563..67bb523a 100644 --- a/lisp/pdf-roll.el +++ b/lisp/pdf-roll.el @@ -53,6 +53,8 @@ If INHIBIT-SLICE-P is non-nil, disregard `pdf-view-current-slice'." :keymap (let ((map (make-sparse-keymap))) (define-key map [remap pdf-view-previous-line-or-previous-page] 'image-roll-scroll-backward) (define-key map [remap pdf-view-next-line-or-next-page] 'image-roll-scroll-forward) + (define-key map [remap pdf-view-scroll-down-or-previous-page] 'image-roll-scroll-backward) + (define-key map [remap pdf-view-scroll-up-or-next-page] 'image-roll-scroll-forward) (define-key map [remap mouse-set-point] 'ignore) (define-key map (kbd "") 'image-roll-scroll-mouse-wheel) (define-key map (kbd "") 'image-roll-scroll-mouse-wheel) From 94dd31a6f1f27ad4818c9bd26057d7d773c28079 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Wed, 2 Aug 2023 19:22:16 +0200 Subject: [PATCH 042/104] Fix finding next matching page Also unset displayed-pages when toggling the mode --- lisp/pdf-isearch.el | 7 ++++--- lisp/pdf-roll.el | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lisp/pdf-isearch.el b/lisp/pdf-isearch.el index ab8b1aea..2073ca56 100644 --- a/lisp/pdf-isearch.el +++ b/lisp/pdf-isearch.el @@ -252,7 +252,7 @@ This is a Isearch interface function." (let ((same-search-p (pdf-isearch-same-search-p)) (oldpage pdf-isearch-current-page) (pages (or (image-mode-window-get 'displayed-pages (selected-window)) - (pdf-view-current-page))) + (list (pdf-view-current-page)))) matches next-match) (dolist (page pages) @@ -428,13 +428,14 @@ there was no previous search, this function returns t." (message "%s" msg)))) (defun pdf-isearch-empty-match-p (matches) - (and matches + (let ((all-matches (apply #'append matches))) + (and all-matches (cl-every (lambda (match) (cl-every (lambda (edges) (cl-every 'zerop edges)) match)) - (apply #'append matches)))) + all-matches)))) (defun pdf-isearch-occur () "Run `occur' using the last search string or regexp." diff --git a/lisp/pdf-roll.el b/lisp/pdf-roll.el index 67bb523a..1fd772d0 100644 --- a/lisp/pdf-roll.el +++ b/lisp/pdf-roll.el @@ -97,6 +97,7 @@ If INHIBIT-SLICE-P is non-nil, disregard `pdf-view-current-slice'." (erase-buffer) (remove-overlays) (insert-file-contents-literally (buffer-file-name)) + (image-mode-window-put 'displayed-pages nil) (pdf-view-new-window-function (list (selected-window))) (set-buffer-modified-p nil))))) From 865af2dc106903e7f225928eb3bd673918324f56 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Thu, 3 Aug 2023 19:24:43 +0200 Subject: [PATCH 043/104] Accommodate image-mode commands using an advice `pdf-view-mode-map` uses `image-mode-map` as its parent map. These commands are used for various things including horizontal scrolling. They look for the image at `point-min` which is not the case for pdf-view-roll-minor-mode. So we add a before-until advice that checks for this minor mode and returns the correct image if it is active. This approach might need to be revisited. An alternative is to use narrow the buffer but it can have many unintended consequences. I think advice is better because it is very targeted. --- lisp/pdf-roll.el | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lisp/pdf-roll.el b/lisp/pdf-roll.el index 1fd772d0..539ac298 100644 --- a/lisp/pdf-roll.el +++ b/lisp/pdf-roll.el @@ -101,6 +101,19 @@ If INHIBIT-SLICE-P is non-nil, disregard `pdf-view-current-slice'." (pdf-view-new-window-function (list (selected-window))) (set-buffer-modified-p nil))))) +(defun pdf-roll--get-display-property () + "`:before-until' advice for `image-get-display-property'. +`image-get-display-property' looks at the `point-min'. This function instead +returns the display property for the current page if `pdf-view-roll-minor-mode' +is non-nil." + (when pdf-view-roll-minor-mode + (get-char-property (image-roll-page-to-pos (pdf-view-current-page)) + 'display + (if (eq (window-buffer) (current-buffer)) + (selected-window))))) + +(advice-add 'image-get-display-property :before-until #'pdf-roll--get-display-property) + (provide 'pdf-roll) ;;; pdf-roll.el ends here From 4332202060d6fcfb6c351a41a7f1f51cea4bae75 Mon Sep 17 00:00:00 2001 From: Daniel Nicolai Date: Tue, 3 May 2022 15:22:49 +0200 Subject: [PATCH 044/104] Add image-roll (for rebase) This commit 'fixes' issue #1. --- image-roll.el | 581 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 581 insertions(+) create mode 100644 image-roll.el diff --git a/image-roll.el b/image-roll.el new file mode 100644 index 00000000..8b029f95 --- /dev/null +++ b/image-roll.el @@ -0,0 +1,581 @@ +;;; image-roll.el --- Virtual scroll display engine -*- lexical-binding:t -*- + +;; Copyright (C) 202 Free Software Foundation, Inc. + +;; Author: D. L. Nicolai +;; Version: 1.0 +;; Keywords: files, pdf +;; URL: https://github.com/dalanicolai/image-roll.el + + +;;; Commentary: + +;; This package provides a virtual scroll engine for displaying books/documents. +;; The main purpose of the package is to provide a continuous scrolling when +;; viewing documents. + +;; The package is written in a way that it supports images/pages of different +;; sizes on the same roll (see comment above `image-roll-scroll-forward'). Also +;; there is no minumum or maximum on the range of the sizes, and finally, it is +;; written to support being displayed in (any number of) multiple windows. + +;; The core functionality, i.e. the 'scroll' is provided by the +;; `image-roll-new-window-function' and `image-roll-redisplay' functions. The +;; function `image-roll-new-window-function' should be added to the +;; `image-mode-new-window-functions' while the `image-roll-redisplay' should be +;; added to the `window-configuration-change-hook' both as buffer local hook +;; functions (i.e. by passing a non-nil LOCAL argument to `add-hook'). For the +;; `image-mode-new-window-functions' to have effect, the `image-mode-winprops' +;; should be initialized by either using `image-mode-setup-winprops' (like in +;; the body of `pdf-view-mode') or by initializing the +;; `image-mode-winprops-alist' explicitly (by setting its value to nil, like in +;; the `image-roll-mode' example). + +;; The package is meant to be used in combination with some other package that +;; provides features to extract and manage the data from the document. An +;; example of such a file is the file `pdf-scroll.el' at URL: +;; https://github.com/dalanicolai/pdf-tools/blob/image-roll-version/lisp/pdf-scroll.el +;; The file `pdf-scroll.el' provides the configurations for pdf-tools to work +;; with `image-roll.el' + +;; However, for development purposes, the package provides an `image-roll-demo' +;; function. Also, as an example of its usage it includes a function +;; `image-roll-directory' which can be used to view all images in some directory +;; using the roll. + +;; This file provides four buffer local variables that should be set to the +;; values of the functions that correctly 'retrieve' the required data from the +;; document. See their docstrings and the `image-roll-directory' function (or +;; `pdf-scroll.el') for more info. + + +;;; Issues + +;; No real issues are known when using this package in a clean way, i.e. by +;; starting from `emacs -Q', then loading this package and using it. + +;; However, I have experienced some errors in redisplay when using this package +;; in combination with vertico, marginalia and savehist. In that case sometimes +;; Emacs its `redisplay' can get a little 'confused/messed up', so that a page +;; (although never the first page but only later pages) will not show directly +;; when creating a new (second, third, ...) window. In that case `redisplay' can +;; be forced by 'activating' the minibuffer (e.g. by pressing `M-x') and hiding +;; it again. It is a weird bug, because it only happens when installing those +;; packages via `use-package', but not when the packages are installed via +;; `package-install'. Also it seems to occur mostly when these three packages +;; are combined. Additionally, it might be might 'due to' using multiple Emacs +;; versions (you can try if the issue occurs on the other Emacs version also, +;; probably not). See +;; https://lists.gnu.org/archive/html/emacs-devel/2022-04/msg00829.html Anyway, +;; I don't know what causes the bug, but this is what I have noticed from +;; inspecting it. + + +(require 'image-mode) +(require 'svg) + +(defgroup image-roll nil + "Image roll configurations." + :group 'applications + :version "28.1") + +(defcustom image-roll-vertical-margin 2 + "Vertical margin around image (pixels), i.e. page separation height. +Because the margin is added to both sides of each page, the page +separation height is twice this value." + :type 'integer) + +(defcustom image-roll-overlay-face-bg-color "gray" + "Background color of overlay, i.e. page separation color." + :type 'color) + +;; (defcustom image-roll-step-size +;; ;; (lambda () +;; ;; (let* ((o (image-roll-page-overlay)) +;; ;; (s (overlay-get o 'display)) +;; ;; (w (image-property s :width))) +;; ;; (if w +;; ;; (* 50 +;; ;; (/ (float (if (consp w) (car w) w)) +;; ;; (window-pixel-height))) +;; ;; 50))) +;; "Scroll step size. +;; The value can be either a number or a function that takes no +;; arguments and returns a positive number. If the number is equal +;; to or larger than 1, it represents pixel units. Otherwise, if the +;; value is between 0 and 1, it represents a fraction of the current +;; page height." +;; :type '(choice function integer float)) + +(defcustom image-roll-step-size 50 + "Scroll step size in pixels units." + :type '(choice function integer float)) + +(defcustom image-roll-center nil + "When non-nil, center the roll horizontally in the window." + :type 'boolean) + +;; (defvar-local image-roll-number-of-pages-function nil +;; "Function that should return the total number of pages. +;; The function should return an integer with the total number of +;; pages in the document.") +(defvar-local image-roll-last-page 0) + +(defvar-local image-roll-page-sizes-function nil + "Function that should return page-sizes of document. +The function should return a list of conses of the form (WIDTH . +HEIGHT), both numbers.") + +(defvar-local image-roll-set-redisplay-flag-function nil + "Function that sets the `needs-redisplay' window property. +Setting this is required for the +`pdf-view-redisplay-some-windows' to work correctly. The same +mechanism should probably get ported to image-roll.el also, to +make redisplay work correctly on multiple windows. However, this +is an advanced feature that is not required for basic image-roll +usage.") + +(defvar-local image-roll-display-page-function nil + "Function that sets the overlay's display property. +The function receives the page number as a single +argument (PAGE). The function should use `(image-roll-page-overlay +PAGE)' to add the image of the page as the overlay's +display-property.") + + +(defmacro image-roll-debug (object) + `(progn (print (format "%s = %s" ,object (eval ,object)) + #'external-debugging-output) + (eval ,object))) + +;; define as macro's for setf-ability + +;; TODO update docstring +(defmacro image-roll-overlays (&optional window) + "List of overlays that make up a scroll." + `(image-mode-window-get 'overlays ,window)) + +(defmacro image-roll-page-overlay (&optional page) + "Return the overlay that hold page number PAGE. +Implemented as macro to make it setf'able." + `(nth (1- (or ,page (image-roll-current-page))) + (image-roll-overlays))) + +(defmacro image-roll-page-overlay-get (page prop) + "Get overlay-property PROP of overlay holding page number PAGE. +Implemented as macro to make it setf'able." + `(overlay-get (nth (1- ,page) (image-roll-overlays)) + ,prop)) + +(defmacro image-roll-current-page (&optional window) + "Return the page number of the currently displayed page. +The current page is the page that overlaps with the window +start (this choice was made in order to simplify the scrolling +logic)" + `(image-mode-window-get 'page ,window)) + +(defmacro image-roll-displayed-pages (&optional window) + "Return list of currently displayed pages." + `(image-mode-window-get 'displayed-pages ,window)) + +(defsubst image-roll-overlay-height (page) + (+ (cdr (image-roll-page-overlay-get page 'overlay-size)) + (* 2 image-roll-vertical-margin))) + +(defsubst image-roll-page-to-pos (page) + (1- (* 2 page))) + +(defun image-roll-visible-overlays () + "Page numbers of currently visible overlays. +The numbers are returned in a list. Overlays that are only +partially visible are included." + (let* (visible + (page (image-roll-current-page)) + (available-height (window-pixel-height)) + (last-page image-roll-last-page)) + + (push page visible) + (cl-decf available-height (- (image-roll-overlay-height page) + (window-vscroll nil t))) + (cl-incf page) + + (unless (> page image-roll-last-page) + (while (> available-height 0) + (push page visible) + (if (= page last-page) + (setq available-height 0) + (cl-decf available-height (image-roll-overlay-height page)) + (cl-incf page)))) + visible)) + +(defun image-roll-undisplay-page (page) + "Undisplay PAGE. +Replaces the image display property of the overlay holding PAGE +with a space. It size is determined from the image its +`image-size'." + (display-warning '(image-roll) (format "undisplay %s" page) + :debug "*image-roll-debug-log*") + (let* ((o (image-roll-page-overlay page)) + (im (overlay-get o 'display)) + (s (image-size im t)) + (w (car s)) + (h (cdr s))) + (overlay-put o 'display `(space . (:width (,w) :height (,h)))) + (overlay-put o 'face `(:background "gray")))) + +(defun image-roll-new-window-function (winprops) + "Function called first after displaying buffer in a new window. +If the buffer is newly created, then it does not contain any +overlay and this function erases the buffer contents, after which +it inserts empty spaces that each hold a overlay. If the buffer +already has overlays (i.e. a second or subsequent window is +created), the function simply copies the overlays and adds the +new window as window overlay-property to each overlay. + +This function should be added to image-roll (continuous scroll) +minor mode commands, after erasing the buffer to create the +overlays." + ;; (if (= (buffer-size) 0) + (if (not (overlays-at 1)) + (let (overlays + (pages image-roll-last-page) + (win (car winprops)) + (inhibit-read-only t)) + + (erase-buffer) + + ;; here we only add the 'page' and 'window' overlay-properties, we add + ;; more properties/information as soon as it becomes available in the + ;; 'image-roll-redisplay' function + (dotimes (i pages) + (let ((i (1+ i))) + (insert " ") + (let ((o (make-overlay (1- (point)) (point)))) + (overlay-put o 'page i) + (overlay-put o 'window win) + (push o overlays)) + (insert "\n"))) + (delete-char -1) + (image-mode-window-put 'overlays (nreverse overlays)) + (set-buffer-modified-p nil) + + ;; required to make `pdf-view-redisplay-some-windows' call `pdf-view-redisplay' + (when-let (fun image-roll-set-redisplay-flag-function) + (funcall fun))) + (let ((ols (mapcar (lambda (o) + (let ((oc (copy-overlay o))) + (overlay-put oc 'window (car winprops)) + oc)) + (image-roll-overlays)))) + (image-mode-window-put 'overlays ols winprops))) + + ;; initial `image-roll-redisplay' needs to know which page(s) to display + (setf (image-roll-current-page (car winprops)) + (or (image-roll-current-page (car winprops)) 1))) + +(defun image-roll-redisplay (&optional window no-relative-vscroll) + "Redisplay the scroll. +Besides that this function can be called directly, it should also +be added to the `window-configuration-change-hook'. + +The argument WINDOW is not used in the body, but it exists to +make the function compatible with `pdf-tools' (in which case it +is a substitute for the `pdf-view-redisplay' function)." + (display-warning '(image-roll) + (format "redisplay %s" (car (image-mode-winprops))) + :debug "*image-roll-debug-log*") + + ;; NOTE the `(when (listp image-mode-winprops-alist)' from + ;; `image-mode-reapply-winprops' was removed here (but in the end might turn + ;; out to be required) + + ;; Beware: this call to image-mode-winprops can't be optimized away, because + ;; it not only gets the winprops data but sets it up if needed (e.g. it's used + ;; by doc-view to display the image in a new window). + (image-mode-winprops nil t) + + (let* ((pages image-roll-last-page) + (page-sizes (if image-roll-page-sizes-function + (funcall image-roll-page-sizes-function) + (make-list pages (if (functionp image-roll-demo-page-size) + (funcall image-roll-demo-page-size) + image-roll-demo-page-size)))) + (n 0)) + + (dolist (page-size page-sizes) + (let* ((page-width (car page-size)) + (overley-heigth (+ (cdr page-size) (* 2 image-roll-vertical-margin))) + (o (nth n (image-roll-overlays)))) + (when image-roll-center + (overlay-put o 'before-string + (when (> (window-pixel-width) page-width) + (propertize " " 'display + `(space :align-to + (,(floor (/ (- (window-pixel-width) page-width) 2)))))))) + (overlay-put o 'display `(space . (:width (,page-width) :height (,overley-heigth)))) + (overlay-put o 'face `(:background ,image-roll-overlay-face-bg-color)) + (overlay-put o 'overlay-size page-size) + (setq n (1+ n))))) + ;; Determine to display pages and display. Undisplay pages is not necessary as + ;; this is taken care off by `image-roll-update-displayed-pages'. + (let (displayed) + (dolist (p (image-roll-visible-overlays)) + (apply image-roll-display-page-function + p + (when (eq image-roll-display-page-function + 'doc-view-insert-image) + `(:width ,doc-view-image-width))) + (push p displayed)) + ;; store displayed images for determining which images to update when update + ;; is triggered + (image-mode-window-put 'displayed-pages (reverse displayed)) + ;; store the height of the visible pages for determining when to update + ;; images, namely when some part of the roll outside this range becomes + ;; visible + (image-mode-window-put 'visible-pages-vscroll-limit + (- (apply #'+ (mapcar #'image-roll-overlay-height displayed)) + (window-text-height nil t)))) + ;; we only need to jump to the right page, the vscroll is conserved and if + ;; required can be set to 0 before the redisplay + (when-let (p (image-roll-current-page)) + (goto-char (1- (* 2 p))) + ;; (redisplay) + ;; TODO implement in redisplay, `fractional vscroll' (in page units) + (image-set-window-vscroll (or (image-mode-window-get 'vscroll) + image-roll-vertical-margin)))) + +(defun image-roll-update-displayed-pages () + (let ((old (print (image-mode-window-get 'displayed-pages) #'external-debugging-output)) + (new (print (image-roll-visible-overlays) #'external-debugging-output))) + ;; dolist because if images/pages are small enough (or after jumps), there + ;; might be multiple image that need to get updated + (dolist (p (cl-set-difference old new)) + (image-roll-undisplay-page p) + (image-mode-window-put 'displayed-pages + (setq old (delete p old)))) ; important to update/setq old before + ;; setting/appending new below + (dolist (p (cl-set-difference new old)) + (apply image-roll-display-page-function + p + (when (eq image-roll-display-page-function + 'doc-view-insert-image) + `(:width ,doc-view-image-width))) + (image-mode-window-put 'displayed-pages (setq old (append old (list p))))) + ;; update also visible-range + (image-mode-window-put 'visible-pages-vscroll-limit + (- (apply #'+ (mapcar #'image-roll-overlay-height new)) + (window-text-height nil t))))) + +(defun image-roll-goto-page-start () + (interactive) + (image-set-window-vscroll 0)) + +;; NOTE code based on (taken from) `pdf-view-goto-page'. +(defun image-roll-goto-page (page &optional window) + "Go to PAGE in document." + (interactive + (list (if current-prefix-arg + (prefix-numeric-value current-prefix-arg) + (read-number "Page: ")))) + (unless (and (>= page 1) + (<= page (count-lines (point-min) (point-max)))) + (error "No such page: %d" page)) + (unless window + (setq window + ;; (if (pdf-util-pdf-window-p) + (selected-window) + ;; t))) + )) + (save-selected-window + ;; Select the window for the hooks below. + (when (window-live-p window) + (select-window window 'norecord)) + (let ((changing-p + (not (eq page (image-roll-current-page window))))) + (when changing-p + ;; (run-hooks 'pdf-view-before-change-page-hook) + (setf (image-roll-current-page window) page) + ;; (run-hooks 'pdf-view-change-page-hook)) + (when (window-live-p window) + (goto-char (image-roll-page-to-pos page)) + (image-roll-update-displayed-pages)) + ;; (when changing-p + ;; (pdf-view-deactivate-region) + ;; (force-mode-line-update) + ;; (run-hooks 'pdf-view-after-change-page-hook)))) + nil)))) + +(defun image-roll-next-page (&optional n) + (interactive "p") + ;; (set-window-start nil (+ (point) 2)) + (image-roll-goto-page (+ (image-roll-current-page) n))) + +(defun image-roll-previous-page (&optional n) + (interactive "p") + (image-roll-next-page (- n))) + + +(defun image-roll-scroll-forward (&optional backward screen) + (interactive) + (let* ((current-page (image-roll-current-page)) + (new-page current-page) + (current-overlay-height (image-roll-overlay-height current-page)) + (visible-pages-vscroll-limit (image-mode-window-get 'visible-pages-vscroll-limit)) + (step-size (if screen + (window-text-height nil t) + image-roll-step-size)) + + ;; determine number of pages to forward/backward (e.g. when scrolling a + ;; full screen the images/pages are small). We subtract, from the step + ;; size, the remaining visible part of the current-page, and then + ;; continue subtracting page-overlay heights until no available height + ;; is left. To also set the scroll correctly after jumping the number + ;; of pages, we store the remaining height, i.e. the remaining height + ;; before the last step where the available height becomes negative + ;; (for the edge case when available height is 0, there is no new + ;; overlay visible yet and the `=' is not included in the `while' + ;; condition) + (available-height step-size) + (remaining-height available-height) + new-vscroll) + (cond (backward + (cl-decf available-height (window-vscroll nil t)) + (while (and (> new-page 0) (> available-height 0)) + (setq remaining-height available-height) + (setq new-page (1- new-page)) ; allow new-page become < 1 + (cl-decf available-height (image-roll-overlay-height new-page)))) + (t + ;; (cl-decf available-height (- (image-roll-overlay-height current-page) + ;; (window-vscroll nil t))) + (cl-decf available-height (- (image-roll-overlay-height current-page) + (window-vscroll nil t))) + (while (and (< new-page (1+ image-roll-last-page)) (> available-height 0)) + (setq remaining-height available-height) + (setq new-page (1+ new-page)) ; allow new-page > last-page + (cl-decf available-height (image-roll-overlay-height new-page))))) + + (when backward + (setq step-size (- step-size))) + + (when (= new-page current-page) + (setq new-vscroll (+ (window-vscroll nil t) step-size))) + + + (if (cond ((< new-page current-page) + (goto-char (image-roll-page-to-pos new-page)) + (setf (image-roll-current-page) (max new-page 1)) + (image-set-window-vscroll + (if (< new-page 1) + (user-error "Beginning of document") + + (- (image-roll-overlay-height (image-roll-current-page)) + remaining-height)))) + ((> new-page current-page) + ;; (print "hier") + (goto-char (image-roll-page-to-pos new-page)) + (setf (image-roll-current-page) (min new-page image-roll-last-page)) + (if (> new-page image-roll-last-page) + (user-error "End of document") + (image-set-window-vscroll + remaining-height))) + ((> new-vscroll + visible-pages-vscroll-limit) + (image-set-window-vscroll (if (< new-page image-roll-last-page) + new-vscroll + (user-error "End of document"))))) + ;; (- visible-pages-vscroll-limit + ;; image-roll-vertical-margin) + (image-roll-update-displayed-pages) + (image-set-window-vscroll new-vscroll)))) + +(defun image-roll-scroll-backward () + (interactive) + (image-roll-scroll-forward t)) + +(defun image-roll-scroll-screen-forward () + (interactive) + (image-roll-scroll-forward nil t)) + +(defun image-roll-scroll-screen-backward () + (interactive) + (image-roll-scroll-forward t t)) + +(defun image-roll-quick-scroll () + (interactive) + (dotimes (i 200) + (image-roll-scroll-forward) + (sit-for 0.0001))) + +(defun image-roll-demo-display-page (page) + "Return demo image of page. +This function is used for the image-roll-demo." + (let* ((o (image-roll-page-overlay page)) + (s (cdr (overlay-get o 'display))) + (w (car (plist-get s :width))) + (h (car (plist-get s :height))) + (svg (svg-create w h))) + (unless w (print "NO W" #'external-debugging-output)) + (svg-rectangle svg 0 0 w h :fill-color "white") + (svg-text svg + (number-to-string page) + :font-size "40" + :fill "black" + :x 20 + :y 50) + (when image-roll-center + (overlay-put o 'before-string + (when (> (window-pixel-width) w) + (propertize " " 'display + `(space :align-to + (,(floor (/ (- (window-pixel-width) w) 2)))))))) + (overlay-put o 'display (svg-image svg :margin `(0 . ,image-roll-vertical-margin))))) + +(define-derived-mode image-roll-mode special-mode "Image Roll" + ;; we don't use `(image-mode-setup-winprops)' because it would additionally + ;; add `image-mode-reapply-winprops' to the + ;; `window-configuration-change-hook', but `image-roll-redisplay' already + ;; reapplies the vscroll, so we simply initialize the + ;; `image-mode-winprops-alist' here, and add lines from + ;; `image-mode-reapply-winprops' at the start of `image-roll-redisplay'. + (add-hook 'window-configuration-change-hook 'image-roll-redisplay nil t) + (add-hook 'image-mode-new-window-functions 'image-roll-new-window-function nil t) + (unless (listp image-mode-winprops-alist) + (setq image-mode-winprops-alist nil))) +;; (add-hook 'window-configuration-change-hook +;; #'image-mode-reapply-winprops nil t)) +;; (image-mode-setup-winprops)) + +(setq image-roll-mode-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "") 'image-roll-scroll-forward) + (define-key map (kbd "") 'image-roll-scroll-backward) + (define-key map (kbd "") 'image-roll-next-page) + (define-key map (kbd "") 'image-roll-previous-page) + (define-key map (kbd "S-") 'image-roll-scroll-screen-forward) + (define-key map (kbd "S-") 'image-roll-scroll-screen-backward) + (define-key map [remap goto-line] 'image-roll-goto-page) + map)) + +(defun image-roll-demo (&optional page-size pages) + (interactive) + (let ((buf-name "*image-roll-demo*")) + ;; (if (get-buffer buf-name) + ;; (switch-to-buffer (current-buffer)) + (with-current-buffer (get-buffer-create buf-name) + (erase-buffer) + (image-roll-mode) + (setq cursor-type nil) + (setq image-roll-step-size 50) + (setq-local image-roll-last-page (or pages 3) + image-roll-display-page-function 'image-roll-demo-display-page + image-roll-demo-page-size (or page-size + (lambda () + (let ((w (window-pixel-width))) + (cons w (* 1.4 w)))))) + + (setq image-roll-center t) + (switch-to-buffer (current-buffer))))) + +(provide 'image-roll) + +;;; image-roll.el ends here From f27cb0c393c3a51447b3ae4f823934b378041923 Mon Sep 17 00:00:00 2001 From: Daniel Nicolai Date: Wed, 4 May 2022 09:05:50 +0200 Subject: [PATCH 045/104] Fix page jump via 'set-window-start' See https://lists.gnu.org/archive/html/emacs-devel/2022-05/msg00032.html Thanks Yuan and Po --- image-roll.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/image-roll.el b/image-roll.el index 8b029f95..5254689f 100644 --- a/image-roll.el +++ b/image-roll.el @@ -407,7 +407,7 @@ is a substitute for the `pdf-view-redisplay' function)." (defun image-roll-next-page (&optional n) (interactive "p") - ;; (set-window-start nil (+ (point) 2)) + (set-window-start nil (+ (point) 2) t) (image-roll-goto-page (+ (image-roll-current-page) n))) (defun image-roll-previous-page (&optional n) From 4047501fc805a0444242f26e8405707800c3a031 Mon Sep 17 00:00:00 2001 From: Daniel Nicolai Date: Tue, 10 May 2022 15:55:49 +0200 Subject: [PATCH 046/104] Add mouse support to image-roll-mode --- image-roll.el | 2 ++ 1 file changed, 2 insertions(+) diff --git a/image-roll.el b/image-roll.el index 5254689f..7a38d7b5 100644 --- a/image-roll.el +++ b/image-roll.el @@ -549,6 +549,8 @@ This function is used for the image-roll-demo." (let ((map (make-sparse-keymap))) (define-key map (kbd "") 'image-roll-scroll-forward) (define-key map (kbd "") 'image-roll-scroll-backward) + (define-key map (kbd "") 'image-roll-scroll-forward) + (define-key map (kbd "") 'image-roll-scroll-backward) (define-key map (kbd "") 'image-roll-next-page) (define-key map (kbd "") 'image-roll-previous-page) (define-key map (kbd "S-") 'image-roll-scroll-screen-forward) From 9ffdcd809d0b76364d261edfbe4d6913cdd5dba4 Mon Sep 17 00:00:00 2001 From: Ihor Radchenko Date: Sat, 14 May 2022 12:21:12 +0800 Subject: [PATCH 047/104] Add missing docstrings * image-roll.el (image-roll-next-page, image-roll-previous-page, image-roll-scroll-forward): Add docstring. --- image-roll.el | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/image-roll.el b/image-roll.el index 7a38d7b5..15fa6020 100644 --- a/image-roll.el +++ b/image-roll.el @@ -406,16 +406,21 @@ is a substitute for the `pdf-view-redisplay' function)." nil)))) (defun image-roll-next-page (&optional n) + "Go to next page or next Nth page." (interactive "p") (set-window-start nil (+ (point) 2) t) (image-roll-goto-page (+ (image-roll-current-page) n))) (defun image-roll-previous-page (&optional n) + "Go to previous page or previous Nth page." (interactive "p") (image-roll-next-page (- n))) (defun image-roll-scroll-forward (&optional backward screen) + "Scroll image forward by `image-roll-step-size'. +When BACKWARD is non-nil, scroll backward instead. +When SCREEN is non-nil, scroll by window height." (interactive) (let* ((current-page (image-roll-current-page)) (new-page current-page) From 4c7b89e3dd93abe1df025aa6e19c9c5a042b06dc Mon Sep 17 00:00:00 2001 From: Ihor Radchenko Date: Sat, 14 May 2022 12:22:12 +0800 Subject: [PATCH 048/104] Add change page hooks * image-roll.el (image-roll-change-page-hook): (image-roll-before-change-page-hook): (image-roll-after-change-page-hook): New hooks to be ran when changing current page. (image-roll-goto-page): (image-roll-scroll-forward): Call the new hooks. --- image-roll.el | 88 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 62 insertions(+), 26 deletions(-) diff --git a/image-roll.el b/image-roll.el index 15fa6020..154a28a7 100644 --- a/image-roll.el +++ b/image-roll.el @@ -142,6 +142,26 @@ argument (PAGE). The function should use `(image-roll-page-overlay PAGE)' to add the image of the page as the overlay's display-property.") +(defcustom image-roll-change-page-hook nil + "Hook run after changing to another page, but before displaying it. + +See also `image-roll-before-change-page-hook' and +`image-roll-after-change-page-hook'." + :type 'hook) + +(defcustom image-roll-before-change-page-hook nil + "Hook run before changing to another page. + +See also `image-roll-change-page-hook' and +`image-roll-after-change-page-hook'." + :type 'hook) + +(defcustom image-roll-after-change-page-hook nil + "Hook run after changing to and displaying another page. + +See also `image-roll-change-page-hook' and +`image-roll-before-change-page-hook'." + :type 'hook) (defmacro image-roll-debug (object) `(progn (print (format "%s = %s" ,object (eval ,object)) @@ -393,17 +413,19 @@ is a substitute for the `pdf-view-redisplay' function)." (let ((changing-p (not (eq page (image-roll-current-page window))))) (when changing-p - ;; (run-hooks 'pdf-view-before-change-page-hook) + (run-hooks 'image-roll-before-change-page-hook) (setf (image-roll-current-page window) page) - ;; (run-hooks 'pdf-view-change-page-hook)) - (when (window-live-p window) - (goto-char (image-roll-page-to-pos page)) - (image-roll-update-displayed-pages)) + (run-hooks 'image-roll-change-page-hook)) + (when (window-live-p window) + (goto-char (image-roll-page-to-pos page)) + (image-roll-update-displayed-pages)) + (when changing-p + (run-hooks 'image-roll-after-change-page-hook)) ;; (when changing-p ;; (pdf-view-deactivate-region) ;; (force-mode-line-update) ;; (run-hooks 'pdf-view-after-change-page-hook)))) - nil)))) + nil))) (defun image-roll-next-page (&optional n) "Go to next page or next Nth page." @@ -467,30 +489,44 @@ When SCREEN is non-nil, scroll by window height." (if (cond ((< new-page current-page) - (goto-char (image-roll-page-to-pos new-page)) + (when (>= new-page 1) + (run-hooks 'image-roll-before-change-page-hook)) (setf (image-roll-current-page) (max new-page 1)) - (image-set-window-vscroll - (if (< new-page 1) - (user-error "Beginning of document") - - (- (image-roll-overlay-height (image-roll-current-page)) - remaining-height)))) + (when (>= new-page 1) + (run-hooks 'image-roll-change-page-hook)) + (goto-char (image-roll-page-to-pos new-page)) + (prog1 + (image-set-window-vscroll + (if (< new-page 1) + (user-error "Beginning of document") + + (- (image-roll-overlay-height (image-roll-current-page)) + remaining-height))) + (when (>= new-page 1) + (run-hooks 'image-roll-after-change-page-hook)))) ((> new-page current-page) ;; (print "hier") - (goto-char (image-roll-page-to-pos new-page)) + (when (<= new-page image-roll-last-page) + (run-hooks 'image-roll-before-change-page-hook)) (setf (image-roll-current-page) (min new-page image-roll-last-page)) - (if (> new-page image-roll-last-page) - (user-error "End of document") - (image-set-window-vscroll - remaining-height))) - ((> new-vscroll - visible-pages-vscroll-limit) - (image-set-window-vscroll (if (< new-page image-roll-last-page) - new-vscroll - (user-error "End of document"))))) - ;; (- visible-pages-vscroll-limit - ;; image-roll-vertical-margin) - (image-roll-update-displayed-pages) + (when (<= new-page image-roll-last-page) + (run-hooks 'image-roll-change-page-hook)) + (goto-char (image-roll-page-to-pos new-page)) + (prog1 + (if (> new-page image-roll-last-page) + (user-error "End of document") + (image-set-window-vscroll + remaining-height)) + (when (<= new-page image-roll-last-page) + (run-hooks 'image-roll-after-change-page-hook)))) + ((> new-vscroll + visible-pages-vscroll-limit) + (image-set-window-vscroll (if (< new-page image-roll-last-page) + new-vscroll + (user-error "End of document"))))) + ;; (- visible-pages-vscroll-limit + ;; image-roll-vertical-margin) + (image-roll-update-displayed-pages) (image-set-window-vscroll new-vscroll)))) (defun image-roll-scroll-backward () From 2b39f3f160404207f1beb4448e52c4d7ef285e6c Mon Sep 17 00:00:00 2001 From: Daniel Nicolai Date: Mon, 16 May 2022 10:41:44 +0200 Subject: [PATCH 049/104] Fix indentation * image-roll.el (image-roll-change-page-hook): (image-roll-before-change-page-hook): (image-roll-after-change-page-hook): New hooks to be ran when changing current page. (image-roll-goto-page): (image-roll-scroll-forward): Call the new hooks. --- image-roll.el | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/image-roll.el b/image-roll.el index 154a28a7..4d502bfb 100644 --- a/image-roll.el +++ b/image-roll.el @@ -348,13 +348,13 @@ is a substitute for the `pdf-view-redisplay' function)." (push p displayed)) ;; store displayed images for determining which images to update when update ;; is triggered - (image-mode-window-put 'displayed-pages (reverse displayed)) - ;; store the height of the visible pages for determining when to update - ;; images, namely when some part of the roll outside this range becomes - ;; visible - (image-mode-window-put 'visible-pages-vscroll-limit - (- (apply #'+ (mapcar #'image-roll-overlay-height displayed)) - (window-text-height nil t)))) + (image-mode-window-put 'displayed-pages (reverse displayed)) + ;; store the height of the visible pages for determining when to update + ;; images, namely when some part of the roll outside this range becomes + ;; visible + (image-mode-window-put 'visible-pages-vscroll-limit + (- (apply #'+ (mapcar #'image-roll-overlay-height displayed)) + (window-text-height nil t)))) ;; we only need to jump to the right page, the vscroll is conserved and if ;; required can be set to 0 before the redisplay (when-let (p (image-roll-current-page)) @@ -403,9 +403,9 @@ is a substitute for the `pdf-view-redisplay' function)." (unless window (setq window ;; (if (pdf-util-pdf-window-p) - (selected-window) - ;; t))) - )) + (selected-window) + ;; t))) + )) (save-selected-window ;; Select the window for the hooks below. (when (window-live-p window) @@ -475,7 +475,7 @@ When SCREEN is non-nil, scroll by window height." ;; (cl-decf available-height (- (image-roll-overlay-height current-page) ;; (window-vscroll nil t))) (cl-decf available-height (- (image-roll-overlay-height current-page) - (window-vscroll nil t))) + (window-vscroll nil t))) (while (and (< new-page (1+ image-roll-last-page)) (> available-height 0)) (setq remaining-height available-height) (setq new-page (1+ new-page)) ; allow new-page > last-page @@ -490,43 +490,43 @@ When SCREEN is non-nil, scroll by window height." (if (cond ((< new-page current-page) (when (>= new-page 1) - (run-hooks 'image-roll-before-change-page-hook)) + (run-hooks 'image-roll-before-change-page-hook)) (setf (image-roll-current-page) (max new-page 1)) (when (>= new-page 1) - (run-hooks 'image-roll-change-page-hook)) + (run-hooks 'image-roll-change-page-hook)) (goto-char (image-roll-page-to-pos new-page)) (prog1 (image-set-window-vscroll - (if (< new-page 1) + (if (< new-page 1) (user-error "Beginning of document") (- (image-roll-overlay-height (image-roll-current-page)) remaining-height))) (when (>= new-page 1) - (run-hooks 'image-roll-after-change-page-hook)))) + (run-hooks 'image-roll-after-change-page-hook)))) ((> new-page current-page) ;; (print "hier") (when (<= new-page image-roll-last-page) - (run-hooks 'image-roll-before-change-page-hook)) + (run-hooks 'image-roll-before-change-page-hook)) (setf (image-roll-current-page) (min new-page image-roll-last-page)) (when (<= new-page image-roll-last-page) - (run-hooks 'image-roll-change-page-hook)) + (run-hooks 'image-roll-change-page-hook)) (goto-char (image-roll-page-to-pos new-page)) (prog1 (if (> new-page image-roll-last-page) (user-error "End of document") - (image-set-window-vscroll + (image-set-window-vscroll remaining-height)) (when (<= new-page image-roll-last-page) - (run-hooks 'image-roll-after-change-page-hook)))) + (run-hooks 'image-roll-after-change-page-hook)))) ((> new-vscroll visible-pages-vscroll-limit) (image-set-window-vscroll (if (< new-page image-roll-last-page) new-vscroll (user-error "End of document"))))) - ;; (- visible-pages-vscroll-limit - ;; image-roll-vertical-margin) - (image-roll-update-displayed-pages) + ;; (- visible-pages-vscroll-limit + ;; image-roll-vertical-margin) + (image-roll-update-displayed-pages) (image-set-window-vscroll new-vscroll)))) (defun image-roll-scroll-backward () @@ -602,8 +602,8 @@ This function is used for the image-roll-demo." (defun image-roll-demo (&optional page-size pages) (interactive) (let ((buf-name "*image-roll-demo*")) - ;; (if (get-buffer buf-name) - ;; (switch-to-buffer (current-buffer)) + ;; (if (get-buffer buf-name) + ;; (switch-to-buffer (current-buffer)) (with-current-buffer (get-buffer-create buf-name) (erase-buffer) (image-roll-mode) From a543f13a2e4834ce57df54ade807f6ebcb119ca6 Mon Sep 17 00:00:00 2001 From: Daniel Nicolai Date: Wed, 15 Jun 2022 15:15:29 +0200 Subject: [PATCH 050/104] Add basic keybindings --- image-roll.el | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/image-roll.el b/image-roll.el index 4d502bfb..dbb274a1 100644 --- a/image-roll.el +++ b/image-roll.el @@ -175,11 +175,11 @@ See also `image-roll-change-page-hook' and "List of overlays that make up a scroll." `(image-mode-window-get 'overlays ,window)) -(defmacro image-roll-page-overlay (&optional page) +(defmacro image-roll-page-overlay (&optional page window) "Return the overlay that hold page number PAGE. Implemented as macro to make it setf'able." `(nth (1- (or ,page (image-roll-current-page))) - (image-roll-overlays))) + (image-roll-overlays ,window))) (defmacro image-roll-page-overlay-get (page prop) "Get overlay-property PROP of overlay holding page number PAGE. @@ -588,11 +588,15 @@ This function is used for the image-roll-demo." (setq image-roll-mode-map (let ((map (make-sparse-keymap))) + (define-key map (kbd "C-n") 'image-roll-scroll-forward) (define-key map (kbd "") 'image-roll-scroll-forward) + (define-key map (kbd "C-p") 'image-roll-scroll-backward) (define-key map (kbd "") 'image-roll-scroll-backward) (define-key map (kbd "") 'image-roll-scroll-forward) (define-key map (kbd "") 'image-roll-scroll-backward) + (define-key map "n" 'image-roll-next-page) (define-key map (kbd "") 'image-roll-next-page) + (define-key map "p" 'image-roll-previous-page) (define-key map (kbd "") 'image-roll-previous-page) (define-key map (kbd "S-") 'image-roll-scroll-screen-forward) (define-key map (kbd "S-") 'image-roll-scroll-screen-backward) From 53f11e7f31af79c64cc60dbf7e069a6c50940d09 Mon Sep 17 00:00:00 2001 From: Daniel Nicolai Date: Fri, 24 Jun 2022 10:58:07 +0200 Subject: [PATCH 051/104] Add modifications to support search functionality --- image-roll.el | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/image-roll.el b/image-roll.el index dbb274a1..3a388cf0 100644 --- a/image-roll.el +++ b/image-roll.el @@ -126,6 +126,8 @@ separation height is twice this value." The function should return a list of conses of the form (WIDTH . HEIGHT), both numbers.") +(defvar-local image-roll-text-contents nil) + (defvar-local image-roll-set-redisplay-flag-function nil "Function that sets the `needs-redisplay' window property. Setting this is required for the @@ -194,6 +196,11 @@ start (this choice was made in order to simplify the scrolling logic)" `(image-mode-window-get 'page ,window)) +(defun image-roll-page-at-current-pos () + (seq-find #'numberp (mapcar (lambda (o) + (overlay-get o 'page)) + (overlays-at (point))))) + (defmacro image-roll-displayed-pages (&optional window) "Return list of currently displayed pages." `(image-mode-window-get 'displayed-pages ,window)) @@ -203,7 +210,7 @@ logic)" (* 2 image-roll-vertical-margin))) (defsubst image-roll-page-to-pos (page) - (1- (* 2 page))) + (overlay-start (nth (1- page) (image-roll-overlays)))) (defun image-roll-visible-overlays () "Page numbers of currently visible overlays. @@ -268,10 +275,12 @@ overlays." ;; more properties/information as soon as it becomes available in the ;; 'image-roll-redisplay' function (dotimes (i pages) - (let ((i (1+ i))) - (insert " ") - (let ((o (make-overlay (1- (point)) (point)))) - (overlay-put o 'page i) + (let ((text (or (nth i image-roll-text-contents) " "))) + (when (= (length text) 0) + (setq text " ")) + (insert text) + (let ((o (make-overlay (- (point) (length text)) (point)))) + (overlay-put o 'page (1+ i)) (overlay-put o 'window win) (push o overlays)) (insert "\n"))) @@ -358,7 +367,7 @@ is a substitute for the `pdf-view-redisplay' function)." ;; we only need to jump to the right page, the vscroll is conserved and if ;; required can be set to 0 before the redisplay (when-let (p (image-roll-current-page)) - (goto-char (1- (* 2 p))) + (goto-char (overlay-start (nth (1- p) (image-roll-overlays)))) ;; (redisplay) ;; TODO implement in redisplay, `fractional vscroll' (in page units) (image-set-window-vscroll (or (image-mode-window-get 'vscroll) @@ -398,7 +407,7 @@ is a substitute for the `pdf-view-redisplay' function)." (prefix-numeric-value current-prefix-arg) (read-number "Page: ")))) (unless (and (>= page 1) - (<= page (count-lines (point-min) (point-max)))) + (<= page image-roll-last-page)) (error "No such page: %d" page)) (unless window (setq window From c91b18f913aadc0a782ec2894bee24983737b877 Mon Sep 17 00:00:00 2001 From: Daniel Nicolai Date: Fri, 16 Sep 2022 20:56:41 +0200 Subject: [PATCH 052/104] Shabby fix for pdf-view-auto-slice-minor-mode support --- image-roll.el | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/image-roll.el b/image-roll.el index 3a388cf0..1d02ffc3 100644 --- a/image-roll.el +++ b/image-roll.el @@ -243,7 +243,10 @@ with a space. It size is determined from the image its (display-warning '(image-roll) (format "undisplay %s" page) :debug "*image-roll-debug-log*") (let* ((o (image-roll-page-overlay page)) - (im (overlay-get o 'display)) + (d (overlay-get o 'display)) + (im (if pdf-view-auto-slice-minor-mode + (nth 1 d) + d)) (s (image-size im t)) (w (car s)) (h (cdr s))) From 0c98a27b8a576902630b5fa5ea110f9f9050b9e7 Mon Sep 17 00:00:00 2001 From: Daniel Nicolai Date: Sat, 3 Dec 2022 22:13:55 +0100 Subject: [PATCH 053/104] Fix reapply hscroll on redisplay (see issue #13) --- image-roll.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/image-roll.el b/image-roll.el index 1d02ffc3..3fad3174 100644 --- a/image-roll.el +++ b/image-roll.el @@ -374,7 +374,8 @@ is a substitute for the `pdf-view-redisplay' function)." ;; (redisplay) ;; TODO implement in redisplay, `fractional vscroll' (in page units) (image-set-window-vscroll (or (image-mode-window-get 'vscroll) - image-roll-vertical-margin)))) + image-roll-vertical-margin)) + (set-window-vscroll (selected-window) (image-mode-window-get 'vscroll winprops) t))) (defun image-roll-update-displayed-pages () (let ((old (print (image-mode-window-get 'displayed-pages) #'external-debugging-output)) From 0ae41b17c09ced4580e50c4d1337246944c87141 Mon Sep 17 00:00:00 2001 From: NightMachinery Date: Sun, 4 Dec 2022 02:01:06 +0330 Subject: [PATCH 054/104] fixed hscroll --- image-roll.el | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/image-roll.el b/image-roll.el index 3fad3174..48241f30 100644 --- a/image-roll.el +++ b/image-roll.el @@ -375,7 +375,10 @@ is a substitute for the `pdf-view-redisplay' function)." ;; TODO implement in redisplay, `fractional vscroll' (in page units) (image-set-window-vscroll (or (image-mode-window-get 'vscroll) image-roll-vertical-margin)) - (set-window-vscroll (selected-window) (image-mode-window-get 'vscroll winprops) t))) + + (image-set-window-hscroll (or + (image-mode-window-get 'hscroll) + 0)))) (defun image-roll-update-displayed-pages () (let ((old (print (image-mode-window-get 'displayed-pages) #'external-debugging-output)) From 49b4ac764457b225efb7f63187ecf07f6fe77565 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Thu, 4 May 2023 20:04:23 +0200 Subject: [PATCH 055/104] Silence warnings --- image-roll.el | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/image-roll.el b/image-roll.el index 48241f30..ca5608be 100644 --- a/image-roll.el +++ b/image-roll.el @@ -244,7 +244,7 @@ with a space. It size is determined from the image its :debug "*image-roll-debug-log*") (let* ((o (image-roll-page-overlay page)) (d (overlay-get o 'display)) - (im (if pdf-view-auto-slice-minor-mode + (im (if (bound-and-true-p pdf-view-auto-slice-minor-mode) (nth 1 d) d)) (s (image-size im t)) @@ -305,7 +305,7 @@ overlays." (setf (image-roll-current-page (car winprops)) (or (image-roll-current-page (car winprops)) 1))) -(defun image-roll-redisplay (&optional window no-relative-vscroll) +(defun image-roll-redisplay (&optional _window _no-relative-vscroll) "Redisplay the scroll. Besides that this function can be called directly, it should also be added to the `window-configuration-change-hook'. @@ -326,12 +326,9 @@ is a substitute for the `pdf-view-redisplay' function)." ;; by doc-view to display the image in a new window). (image-mode-winprops nil t) - (let* ((pages image-roll-last-page) - (page-sizes (if image-roll-page-sizes-function - (funcall image-roll-page-sizes-function) - (make-list pages (if (functionp image-roll-demo-page-size) - (funcall image-roll-demo-page-size) - image-roll-demo-page-size)))) + (let* ((image-roll-last-page) + (page-sizes (when image-roll-page-sizes-function + (funcall image-roll-page-sizes-function))) (n 0)) (dolist (page-size page-sizes) @@ -356,7 +353,7 @@ is a substitute for the `pdf-view-redisplay' function)." p (when (eq image-roll-display-page-function 'doc-view-insert-image) - `(:width ,doc-view-image-width))) + `(:width ,(bound-and-true-p doc-view-image-width)))) (push p displayed)) ;; store displayed images for determining which images to update when update ;; is triggered @@ -395,7 +392,7 @@ is a substitute for the `pdf-view-redisplay' function)." p (when (eq image-roll-display-page-function 'doc-view-insert-image) - `(:width ,doc-view-image-width))) + `(:width ,(bound-and-true-p doc-view-image-width)))) (image-mode-window-put 'displayed-pages (setq old (append old (list p))))) ;; update also visible-range (image-mode-window-put 'visible-pages-vscroll-limit @@ -462,10 +459,9 @@ When SCREEN is non-nil, scroll by window height." (interactive) (let* ((current-page (image-roll-current-page)) (new-page current-page) - (current-overlay-height (image-roll-overlay-height current-page)) (visible-pages-vscroll-limit (image-mode-window-get 'visible-pages-vscroll-limit)) (step-size (if screen - (window-text-height nil t) + (/ (window-text-height nil t) 2) image-roll-step-size)) ;; determine number of pages to forward/backward (e.g. when scrolling a @@ -559,7 +555,7 @@ When SCREEN is non-nil, scroll by window height." (defun image-roll-quick-scroll () (interactive) - (dotimes (i 200) + (dotimes (_i 200) (image-roll-scroll-forward) (sit-for 0.0001))) From adce6397fb70258b349a0b9e33e265604ae94fbc Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Mon, 22 May 2023 21:46:40 +0200 Subject: [PATCH 056/104] Fix grey boxes and refactor --- image-roll.el | 217 ++++++++++++++++++++++---------------------------- 1 file changed, 94 insertions(+), 123 deletions(-) diff --git a/image-roll.el b/image-roll.el index ca5608be..7690c890 100644 --- a/image-roll.el +++ b/image-roll.el @@ -6,6 +6,7 @@ ;; Version: 1.0 ;; Keywords: files, pdf ;; URL: https://github.com/dalanicolai/image-roll.el +;; Package-Requires: ((emacs "26.1")) ;;; Commentary: @@ -70,6 +71,7 @@ ;; I don't know what causes the bug, but this is what I have noticed from ;; inspecting it. +;;; Code: (require 'image-mode) (require 'svg) @@ -190,7 +192,7 @@ Implemented as macro to make it setf'able." ,prop)) (defmacro image-roll-current-page (&optional window) - "Return the page number of the currently displayed page. + "Return the page number of the currently displayed page in WINDOW. The current page is the page that overlaps with the window start (this choice was made in order to simplify the scrolling logic)" @@ -244,9 +246,9 @@ with a space. It size is determined from the image its :debug "*image-roll-debug-log*") (let* ((o (image-roll-page-overlay page)) (d (overlay-get o 'display)) - (im (if (bound-and-true-p pdf-view-auto-slice-minor-mode) - (nth 1 d) - d)) + (im (if (imagep d) + d + (nth 1 d))) (s (image-size im t)) (w (car s)) (h (cdr s))) @@ -305,7 +307,7 @@ overlays." (setf (image-roll-current-page (car winprops)) (or (image-roll-current-page (car winprops)) 1))) -(defun image-roll-redisplay (&optional _window _no-relative-vscroll) +(defun image-roll-redisplay (&optional window _no-relative-vscroll) "Redisplay the scroll. Besides that this function can be called directly, it should also be added to the `window-configuration-change-hook'. @@ -313,9 +315,6 @@ be added to the `window-configuration-change-hook'. The argument WINDOW is not used in the body, but it exists to make the function compatible with `pdf-tools' (in which case it is a substitute for the `pdf-view-redisplay' function)." - (display-warning '(image-roll) - (format "redisplay %s" (car (image-mode-winprops))) - :debug "*image-roll-debug-log*") ;; NOTE the `(when (listp image-mode-winprops-alist)' from ;; `image-mode-reapply-winprops' was removed here (but in the end might turn @@ -324,76 +323,72 @@ is a substitute for the `pdf-view-redisplay' function)." ;; Beware: this call to image-mode-winprops can't be optimized away, because ;; it not only gets the winprops data but sets it up if needed (e.g. it's used ;; by doc-view to display the image in a new window). - (image-mode-winprops nil t) - - (let* ((image-roll-last-page) - (page-sizes (when image-roll-page-sizes-function - (funcall image-roll-page-sizes-function))) - (n 0)) - - (dolist (page-size page-sizes) - (let* ((page-width (car page-size)) - (overley-heigth (+ (cdr page-size) (* 2 image-roll-vertical-margin))) - (o (nth n (image-roll-overlays)))) - (when image-roll-center - (overlay-put o 'before-string - (when (> (window-pixel-width) page-width) - (propertize " " 'display - `(space :align-to - (,(floor (/ (- (window-pixel-width) page-width) 2)))))))) - (overlay-put o 'display `(space . (:width (,page-width) :height (,overley-heigth)))) - (overlay-put o 'face `(:background ,image-roll-overlay-face-bg-color)) - (overlay-put o 'overlay-size page-size) - (setq n (1+ n))))) - ;; Determine to display pages and display. Undisplay pages is not necessary as - ;; this is taken care off by `image-roll-update-displayed-pages'. - (let (displayed) - (dolist (p (image-roll-visible-overlays)) - (apply image-roll-display-page-function - p - (when (eq image-roll-display-page-function - 'doc-view-insert-image) - `(:width ,(bound-and-true-p doc-view-image-width)))) - (push p displayed)) - ;; store displayed images for determining which images to update when update - ;; is triggered - (image-mode-window-put 'displayed-pages (reverse displayed)) - ;; store the height of the visible pages for determining when to update - ;; images, namely when some part of the roll outside this range becomes - ;; visible - (image-mode-window-put 'visible-pages-vscroll-limit - (- (apply #'+ (mapcar #'image-roll-overlay-height displayed)) - (window-text-height nil t)))) - ;; we only need to jump to the right page, the vscroll is conserved and if - ;; required can be set to 0 before the redisplay - (when-let (p (image-roll-current-page)) - (goto-char (overlay-start (nth (1- p) (image-roll-overlays)))) - ;; (redisplay) - ;; TODO implement in redisplay, `fractional vscroll' (in page units) - (image-set-window-vscroll (or (image-mode-window-get 'vscroll) - image-roll-vertical-margin)) - - (image-set-window-hscroll (or - (image-mode-window-get 'hscroll) - 0)))) + (setq window (if (window-live-p window) window (selected-window))) + (when (ignore-errors (image-mode-winprops window t)) + (with-selected-window window + (let* ((page-sizes (when image-roll-page-sizes-function + (funcall image-roll-page-sizes-function))) + (overlays (image-roll-overlays))) + + (dolist (page-size page-sizes) + (let* ((page-width (car page-size)) + (overley-height (+ (cdr page-size) (* 2 image-roll-vertical-margin))) + (o (pop overlays))) + (when image-roll-center + (overlay-put o 'before-string + (when (> (window-pixel-width) page-width) + (propertize " " 'display + `(space :align-to + (,(floor (/ (- (window-pixel-width) page-width) 2)))))))) + (overlay-put o 'display `(space . (:width (,page-width) :height (,overley-height)))) + (overlay-put o 'face `(:background ,image-roll-overlay-face-bg-color)) + (overlay-put o 'overlay-size page-size)))) + ;; Determine to display pages and display. Undisplay pages is not necessary as + ;; this is taken care off by `image-roll-update-displayed-pages'. + (let (displayed) + (dolist (p (image-roll-visible-overlays)) + (apply image-roll-display-page-function + p + (when (eq image-roll-display-page-function + 'doc-view-insert-image) + `(:width ,(bound-and-true-p doc-view-image-width)))) + (push p displayed)) + ;; store displayed images for determining which images to update when update + ;; is triggered + (image-mode-window-put 'displayed-pages (nreverse displayed)) + ;; store the height of the visible pages for determining when to update + ;; images, namely when some part of the roll outside this range becomes + ;; visible + (image-mode-window-put 'visible-pages-vscroll-limit + (- (apply #'+ (mapcar #'image-roll-overlay-height displayed)) + (window-text-height nil t)))) + ;; we only need to jump to the right page, the vscroll is conserved and if + ;; required can be set to 0 before the redisplay + (when-let (p (image-roll-current-page)) + (set-window-start nil + (goto-char (overlay-start (nth (1- p) (image-roll-overlays)))) + t) + ;; (redisplay) + ;; TODO implement in redisplay, `fractional vscroll' (in page units) + (image-set-window-vscroll (or (image-mode-window-get 'vscroll) + image-roll-vertical-margin)) + + (image-set-window-hscroll (or + (image-mode-window-get 'hscroll) + 0)))))) (defun image-roll-update-displayed-pages () - (let ((old (print (image-mode-window-get 'displayed-pages) #'external-debugging-output)) - (new (print (image-roll-visible-overlays) #'external-debugging-output))) + (let ((old (image-mode-window-get 'displayed-pages)) + (new (image-roll-visible-overlays))) ;; dolist because if images/pages are small enough (or after jumps), there ;; might be multiple image that need to get updated - (dolist (p (cl-set-difference old new)) - (image-roll-undisplay-page p) - (image-mode-window-put 'displayed-pages - (setq old (delete p old)))) ; important to update/setq old before + (dolist (p old) + (image-roll-undisplay-page p)) + (image-mode-window-put 'displayed-pages nil) ;; setting/appending new below - (dolist (p (cl-set-difference new old)) - (apply image-roll-display-page-function - p - (when (eq image-roll-display-page-function - 'doc-view-insert-image) - `(:width ,(bound-and-true-p doc-view-image-width)))) - (image-mode-window-put 'displayed-pages (setq old (append old (list p))))) + (dolist (p new) + (funcall image-roll-display-page-function p)) + (image-mode-window-put 'displayed-pages new) ;; update also visible-range (image-mode-window-put 'visible-pages-vscroll-limit (- (apply #'+ (mapcar #'image-roll-overlay-height new)) @@ -461,7 +456,7 @@ When SCREEN is non-nil, scroll by window height." (new-page current-page) (visible-pages-vscroll-limit (image-mode-window-get 'visible-pages-vscroll-limit)) (step-size (if screen - (/ (window-text-height nil t) 2) + (* (window-text-height nil t) 0.9) image-roll-step-size)) ;; determine number of pages to forward/backward (e.g. when scrolling a @@ -484,8 +479,6 @@ When SCREEN is non-nil, scroll by window height." (setq new-page (1- new-page)) ; allow new-page become < 1 (cl-decf available-height (image-roll-overlay-height new-page)))) (t - ;; (cl-decf available-height (- (image-roll-overlay-height current-page) - ;; (window-vscroll nil t))) (cl-decf available-height (- (image-roll-overlay-height current-page) (window-vscroll nil t))) (while (and (< new-page (1+ image-roll-last-page)) (> available-height 0)) @@ -493,53 +486,31 @@ When SCREEN is non-nil, scroll by window height." (setq new-page (1+ new-page)) ; allow new-page > last-page (cl-decf available-height (image-roll-overlay-height new-page))))) + (when (< new-page 1) + (message "Beginning of document") + (setq new-page 1)) + + (when (> new-page image-roll-last-page) + (message "End of document") + (setq new-page image-roll-last-page)) + (when backward - (setq step-size (- step-size))) - - (when (= new-page current-page) - (setq new-vscroll (+ (window-vscroll nil t) step-size))) - - - (if (cond ((< new-page current-page) - (when (>= new-page 1) - (run-hooks 'image-roll-before-change-page-hook)) - (setf (image-roll-current-page) (max new-page 1)) - (when (>= new-page 1) - (run-hooks 'image-roll-change-page-hook)) - (goto-char (image-roll-page-to-pos new-page)) - (prog1 - (image-set-window-vscroll - (if (< new-page 1) - (user-error "Beginning of document") - - (- (image-roll-overlay-height (image-roll-current-page)) - remaining-height))) - (when (>= new-page 1) - (run-hooks 'image-roll-after-change-page-hook)))) - ((> new-page current-page) - ;; (print "hier") - (when (<= new-page image-roll-last-page) - (run-hooks 'image-roll-before-change-page-hook)) - (setf (image-roll-current-page) (min new-page image-roll-last-page)) - (when (<= new-page image-roll-last-page) - (run-hooks 'image-roll-change-page-hook)) - (goto-char (image-roll-page-to-pos new-page)) - (prog1 - (if (> new-page image-roll-last-page) - (user-error "End of document") - (image-set-window-vscroll - remaining-height)) - (when (<= new-page image-roll-last-page) - (run-hooks 'image-roll-after-change-page-hook)))) - ((> new-vscroll - visible-pages-vscroll-limit) - (image-set-window-vscroll (if (< new-page image-roll-last-page) - new-vscroll - (user-error "End of document"))))) - ;; (- visible-pages-vscroll-limit - ;; image-roll-vertical-margin) - (image-roll-update-displayed-pages) - (image-set-window-vscroll new-vscroll)))) + (cl-callf - step-size) + (cl-callf2 - (image-roll-overlay-height new-page) remaining-height)) + + (cond ((= new-page current-page) + (setq new-vscroll (+ (window-vscroll nil t) step-size)) + (image-set-window-vscroll new-vscroll) + (when (> new-vscroll visible-pages-vscroll-limit) + (image-roll-update-displayed-pages))) + (t + (run-hooks 'image-roll-before-change-page-hook) + (setf (image-roll-current-page) new-page) + (run-hooks 'image-roll-change-page-hook) + (set-window-start nil (goto-char (image-roll-page-to-pos new-page)) t) + (image-set-window-vscroll remaining-height) + (run-hooks 'image-roll-after-change-page-hook) + (image-roll-update-displayed-pages))))) (defun image-roll-scroll-backward () (interactive) @@ -590,7 +561,7 @@ This function is used for the image-roll-demo." ;; reapplies the vscroll, so we simply initialize the ;; `image-mode-winprops-alist' here, and add lines from ;; `image-mode-reapply-winprops' at the start of `image-roll-redisplay'. - (add-hook 'window-configuration-change-hook 'image-roll-redisplay nil t) + (add-hook 'window-size-change-functions 'image-roll-redisplay nil t) (add-hook 'image-mode-new-window-functions 'image-roll-new-window-function nil t) (unless (listp image-mode-winprops-alist) (setq image-mode-winprops-alist nil))) From d69b3d27e7730facb488d569bc1ffc3e3d6c6ac9 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Tue, 23 May 2023 20:42:30 +0200 Subject: [PATCH 057/104] Put back differential displaying, undisplaying --- image-roll.el | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/image-roll.el b/image-roll.el index 7690c890..adb95aec 100644 --- a/image-roll.el +++ b/image-roll.el @@ -382,11 +382,10 @@ is a substitute for the `pdf-view-redisplay' function)." (new (image-roll-visible-overlays))) ;; dolist because if images/pages are small enough (or after jumps), there ;; might be multiple image that need to get updated - (dolist (p old) + (dolist (p (cl-set-difference old new)) (image-roll-undisplay-page p)) - (image-mode-window-put 'displayed-pages nil) ;; setting/appending new below - (dolist (p new) + (dolist (p (cl-set-difference new old)) (funcall image-roll-display-page-function p)) (image-mode-window-put 'displayed-pages new) ;; update also visible-range From 7fa87c4299aa9b3e696ca94dc86eb5b4659a9907 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Thu, 1 Jun 2023 00:33:41 +0200 Subject: [PATCH 058/104] Tweaks --- image-roll.el | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/image-roll.el b/image-roll.el index adb95aec..6e8c4bc1 100644 --- a/image-roll.el +++ b/image-roll.el @@ -324,12 +324,13 @@ is a substitute for the `pdf-view-redisplay' function)." ;; it not only gets the winprops data but sets it up if needed (e.g. it's used ;; by doc-view to display the image in a new window). (setq window (if (window-live-p window) window (selected-window))) - (when (ignore-errors (image-mode-winprops window t)) + (when (and (memq 'image-roll-new-window-function image-mode-new-window-functions) + (eq (current-buffer) (window-buffer window))) + (image-mode-winprops window t) (with-selected-window window (let* ((page-sizes (when image-roll-page-sizes-function (funcall image-roll-page-sizes-function))) (overlays (image-roll-overlays))) - (dolist (page-size page-sizes) (let* ((page-width (car page-size)) (overley-height (+ (cdr page-size) (* 2 image-roll-vertical-margin))) @@ -373,9 +374,8 @@ is a substitute for the `pdf-view-redisplay' function)." (image-set-window-vscroll (or (image-mode-window-get 'vscroll) image-roll-vertical-margin)) - (image-set-window-hscroll (or - (image-mode-window-get 'hscroll) - 0)))))) + (image-set-window-hscroll (or (image-mode-window-get 'hscroll) + 0)))))) (defun image-roll-update-displayed-pages () (let ((old (image-mode-window-get 'displayed-pages)) From 077bfe5bb1b0745a55647722f727a8cd3ac452af Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Wed, 14 Jun 2023 10:56:23 +0200 Subject: [PATCH 059/104] Hack for a mysterious bug Sometimes the images just disappear and the buffer starts showing the literal binary contents of pdf file. This detects it and shows images again. --- image-roll.el | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/image-roll.el b/image-roll.el index 6e8c4bc1..34396176 100644 --- a/image-roll.el +++ b/image-roll.el @@ -326,7 +326,9 @@ is a substitute for the `pdf-view-redisplay' function)." (setq window (if (window-live-p window) window (selected-window))) (when (and (memq 'image-roll-new-window-function image-mode-new-window-functions) (eq (current-buffer) (window-buffer window))) - (image-mode-winprops window t) + (if (equal (buffer-substring (point-min) (1+ (point-min))) "%") + (image-roll-new-window-function `(,(selected-window))) + (image-mode-winprops window t)) (with-selected-window window (let* ((page-sizes (when image-roll-page-sizes-function (funcall image-roll-page-sizes-function))) @@ -369,11 +371,9 @@ is a substitute for the `pdf-view-redisplay' function)." (set-window-start nil (goto-char (overlay-start (nth (1- p) (image-roll-overlays)))) t) - ;; (redisplay) ;; TODO implement in redisplay, `fractional vscroll' (in page units) (image-set-window-vscroll (or (image-mode-window-get 'vscroll) image-roll-vertical-margin)) - (image-set-window-hscroll (or (image-mode-window-get 'hscroll) 0)))))) From e2a612b62cec35eb318a1248634997177d12bc58 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Fri, 16 Jun 2023 15:30:21 +0200 Subject: [PATCH 060/104] Add command to scroll with mouse wheel --- image-roll.el | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/image-roll.el b/image-roll.el index 34396176..9d885d00 100644 --- a/image-roll.el +++ b/image-roll.el @@ -523,6 +523,16 @@ When SCREEN is non-nil, scroll by window height." (interactive) (image-roll-scroll-forward t t)) +(defun image-roll-scroll-mouse-wheel (event) + "Scroll according to mouse wheel EVENT." + (interactive "e") + (with-selected-window (posn-window (event-start event)) + (image-roll-scroll-forward + (pcase (event-basic-type event) + ('wheel-down nil) + ('wheel-up t) + (_ (error "Event must be wheel down or wheel up event")))))) + (defun image-roll-quick-scroll () (interactive) (dotimes (_i 200) @@ -574,6 +584,8 @@ This function is used for the image-roll-demo." (define-key map (kbd "") 'image-roll-scroll-forward) (define-key map (kbd "C-p") 'image-roll-scroll-backward) (define-key map (kbd "") 'image-roll-scroll-backward) + (define-key map (kbd "") 'image-roll-scroll-mouse-wheel) + (define-key map (kbd "") 'image-roll-scroll-mouse-wheel) (define-key map (kbd "") 'image-roll-scroll-forward) (define-key map (kbd "") 'image-roll-scroll-backward) (define-key map "n" 'image-roll-next-page) From 6f5f43b626451dbb94681e5b08a9218f436ce72c Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Sat, 17 Jun 2023 00:35:10 +0200 Subject: [PATCH 061/104] Make the window being used explicit in most places --- image-roll.el | 368 +++++++++++++++++++++++--------------------------- 1 file changed, 172 insertions(+), 196 deletions(-) diff --git a/image-roll.el b/image-roll.el index 9d885d00..62594fec 100644 --- a/image-roll.el +++ b/image-roll.el @@ -91,24 +91,6 @@ separation height is twice this value." "Background color of overlay, i.e. page separation color." :type 'color) -;; (defcustom image-roll-step-size -;; ;; (lambda () -;; ;; (let* ((o (image-roll-page-overlay)) -;; ;; (s (overlay-get o 'display)) -;; ;; (w (image-property s :width))) -;; ;; (if w -;; ;; (* 50 -;; ;; (/ (float (if (consp w) (car w) w)) -;; ;; (window-pixel-height))) -;; ;; 50))) -;; "Scroll step size. -;; The value can be either a number or a function that takes no -;; arguments and returns a positive number. If the number is equal -;; to or larger than 1, it represents pixel units. Otherwise, if the -;; value is between 0 and 1, it represents a fraction of the current -;; page height." -;; :type '(choice function integer float)) - (defcustom image-roll-step-size 50 "Scroll step size in pixels units." :type '(choice function integer float)) @@ -128,8 +110,6 @@ separation height is twice this value." The function should return a list of conses of the form (WIDTH . HEIGHT), both numbers.") -(defvar-local image-roll-text-contents nil) - (defvar-local image-roll-set-redisplay-flag-function nil "Function that sets the `needs-redisplay' window property. Setting this is required for the @@ -172,25 +152,6 @@ See also `image-roll-change-page-hook' and #'external-debugging-output) (eval ,object))) -;; define as macro's for setf-ability - -;; TODO update docstring -(defmacro image-roll-overlays (&optional window) - "List of overlays that make up a scroll." - `(image-mode-window-get 'overlays ,window)) - -(defmacro image-roll-page-overlay (&optional page window) - "Return the overlay that hold page number PAGE. -Implemented as macro to make it setf'able." - `(nth (1- (or ,page (image-roll-current-page))) - (image-roll-overlays ,window))) - -(defmacro image-roll-page-overlay-get (page prop) - "Get overlay-property PROP of overlay holding page number PAGE. -Implemented as macro to make it setf'able." - `(overlay-get (nth (1- ,page) (image-roll-overlays)) - ,prop)) - (defmacro image-roll-current-page (&optional window) "Return the page number of the currently displayed page in WINDOW. The current page is the page that overlaps with the window @@ -198,34 +159,44 @@ start (this choice was made in order to simplify the scrolling logic)" `(image-mode-window-get 'page ,window)) +(defsubst image-roll-page-to-pos (page) + "Get the buffer position displaing PAGE." + (1- (* 2 page))) + +(defun image-roll-page-overlay (&optional page window) + "Return overlay displaying PAGE in WINDOW." + (cl-find (or window (selected-window)) + (overlays-at (image-roll-page-to-pos + (or page (image-roll-current-page window)))) + :key (lambda (ov) (overlay-get ov 'window)))) + (defun image-roll-page-at-current-pos () - (seq-find #'numberp (mapcar (lambda (o) - (overlay-get o 'page)) - (overlays-at (point))))) + "Page at point." + (if (cl-oddp (point)) + (/ (1+ (point)) 2) + (error "No page is displayed at current position"))) (defmacro image-roll-displayed-pages (&optional window) - "Return list of currently displayed pages." + "Return list of pages currently displayed in WINDOW." `(image-mode-window-get 'displayed-pages ,window)) -(defsubst image-roll-overlay-height (page) - (+ (cdr (image-roll-page-overlay-get page 'overlay-size)) - (* 2 image-roll-vertical-margin))) - -(defsubst image-roll-page-to-pos (page) - (overlay-start (nth (1- page) (image-roll-overlays)))) +(defsubst image-roll-overlay-height (page &optional window) + "Get the height of overlay displaying PAGE on WINDOW." + (+ (cdr (overlay-get (image-roll-page-overlay page window) 'overlay-size)) + image-roll-vertical-margin)) -(defun image-roll-visible-overlays () - "Page numbers of currently visible overlays. +(defun image-roll-visible-overlays (&optional window) + "Page numbers of overlays currently visible in WINDOW. The numbers are returned in a list. Overlays that are only partially visible are included." (let* (visible - (page (image-roll-current-page)) - (available-height (window-pixel-height)) + (page (image-roll-current-page window)) + (available-height (window-pixel-height window)) (last-page image-roll-last-page)) (push page visible) - (cl-decf available-height (- (image-roll-overlay-height page) - (window-vscroll nil t))) + (cl-decf available-height (- (image-roll-overlay-height page window) + (window-vscroll window t))) (cl-incf page) (unless (> page image-roll-last-page) @@ -233,18 +204,18 @@ partially visible are included." (push page visible) (if (= page last-page) (setq available-height 0) - (cl-decf available-height (image-roll-overlay-height page)) + (cl-decf available-height (image-roll-overlay-height page window)) (cl-incf page)))) visible)) -(defun image-roll-undisplay-page (page) - "Undisplay PAGE. +(defun image-roll-undisplay-page (page &optional window) + "Undisplay PAGE from WINDOW. Replaces the image display property of the overlay holding PAGE with a space. It size is determined from the image its `image-size'." (display-warning '(image-roll) (format "undisplay %s" page) :debug "*image-roll-debug-log*") - (let* ((o (image-roll-page-overlay page)) + (let* ((o (image-roll-page-overlay page window)) (d (overlay-get o 'display)) (im (if (imagep d) d @@ -255,8 +226,8 @@ with a space. It size is determined from the image its (overlay-put o 'display `(space . (:width (,w) :height (,h)))) (overlay-put o 'face `(:background "gray")))) -(defun image-roll-new-window-function (winprops) - "Function called first after displaying buffer in a new window. +(defun image-roll-new-window-function (&optional winprops) + "Setup image roll in a new window. If the buffer is newly created, then it does not contain any overlay and this function erases the buffer contents, after which it inserts empty spaces that each hold a overlay. If the buffer @@ -264,48 +235,74 @@ already has overlays (i.e. a second or subsequent window is created), the function simply copies the overlays and adds the new window as window overlay-property to each overlay. +WINPROPS are the initial window properties. + This function should be added to image-roll (continuous scroll) minor mode commands, after erasing the buffer to create the overlays." - ;; (if (= (buffer-size) 0) - (if (not (overlays-at 1)) - (let (overlays - (pages image-roll-last-page) - (win (car winprops)) - (inhibit-read-only t)) - - (erase-buffer) - - ;; here we only add the 'page' and 'window' overlay-properties, we add - ;; more properties/information as soon as it becomes available in the - ;; 'image-roll-redisplay' function - (dotimes (i pages) - (let ((text (or (nth i image-roll-text-contents) " "))) - (when (= (length text) 0) - (setq text " ")) - (insert text) - (let ((o (make-overlay (- (point) (length text)) (point)))) - (overlay-put o 'page (1+ i)) - (overlay-put o 'window win) - (push o overlays)) - (insert "\n"))) - (delete-char -1) - (image-mode-window-put 'overlays (nreverse overlays)) - (set-buffer-modified-p nil) - - ;; required to make `pdf-view-redisplay-some-windows' call `pdf-view-redisplay' - (when-let (fun image-roll-set-redisplay-flag-function) - (funcall fun))) - (let ((ols (mapcar (lambda (o) - (let ((oc (copy-overlay o))) - (overlay-put oc 'window (car winprops)) - oc)) - (image-roll-overlays)))) - (image-mode-window-put 'overlays ols winprops))) - - ;; initial `image-roll-redisplay' needs to know which page(s) to display - (setf (image-roll-current-page (car winprops)) - (or (image-roll-current-page (car winprops)) 1))) + (let ((win (or (car winprops) (selected-window)))) + (with-current-buffer (window-buffer win) + (if (not (overlays-at 1)) + (let (overlays + (pages image-roll-last-page) + (inhibit-read-only t)) + + (erase-buffer) + + ;; here we only add the 'page' and 'window' overlay-properties, we add + ;; more properties/information as soon as it becomes available in the + ;; 'image-roll-redisplay' function + (dotimes (i pages) + (insert " ") + (let ((o (make-overlay (1- (point)) (point)))) + (overlay-put o 'page (1+ i)) + (overlay-put o 'window win) + (push o overlays)) + (insert "\n")) + (delete-char -1) + (set-buffer-modified-p nil) + + ;; required to make `pdf-view-redisplay-some-windows' call `pdf-view-redisplay' + (when-let (fun image-roll-set-redisplay-flag-function) + (funcall fun))) + (dotimes (i (/ (point-max) 2)) + (overlay-put (copy-overlay (car (overlays-at (1+ (* 2 i))))) + 'window (car winprops)))) + + ;; initial `image-roll-redisplay' needs to know which page(s) to display + (setf (image-roll-current-page (car winprops)) + (or (image-roll-current-page (car winprops)) 1))))) + +(defun image-roll-set-vscroll (vscroll win) + "Set vscroll to VSCROLL in window WIN." + (image-mode-window-put 'vscroll vscroll win) + (set-window-vscroll win vscroll t t)) + +(defun image-roll-set-hscroll (hscroll win) + "Set hscroll to HSCROLL in window WIN." + (image-mode-window-put 'hscroll hscroll win) + (set-window-hscroll win hscroll)) + +(defun image-roll-window-state-change-function (win) + "Handle state change for window WIN. +It should be added to `window-state-change-functions' buffer locally." + (when-let (((memq 'image-roll-new-window-function image-mode-new-window-functions)) + (p (image-mode-window-get 'page win))) + (set-window-start win (goto-char (1- (* 2 p))) t) + (image-roll-set-vscroll (or (image-mode-window-get 'vscroll win) + image-roll-vertical-margin) + win) + (image-roll-set-hscroll (or (image-mode-window-get 'hscroll) 0) + win))) + +(defun image-roll-update-vscroll-limit (displayed &optional window) + "Update the total available vscroll from DISPLAYED pages on WINDOW." + (image-mode-window-put 'visible-pages-vscroll-limit + (let ((sumheights 0)) + (dolist (page displayed) + (cl-incf sumheights (image-roll-overlay-height page window))) + (- sumheights (window-text-height nil t))) + window)) (defun image-roll-redisplay (&optional window _no-relative-vscroll) "Redisplay the scroll. @@ -327,79 +324,68 @@ is a substitute for the `pdf-view-redisplay' function)." (when (and (memq 'image-roll-new-window-function image-mode-new-window-functions) (eq (current-buffer) (window-buffer window))) (if (equal (buffer-substring (point-min) (1+ (point-min))) "%") - (image-roll-new-window-function `(,(selected-window))) + (image-roll-new-window-function `(,window)) (image-mode-winprops window t)) - (with-selected-window window - (let* ((page-sizes (when image-roll-page-sizes-function - (funcall image-roll-page-sizes-function))) - (overlays (image-roll-overlays))) - (dolist (page-size page-sizes) - (let* ((page-width (car page-size)) - (overley-height (+ (cdr page-size) (* 2 image-roll-vertical-margin))) - (o (pop overlays))) - (when image-roll-center - (overlay-put o 'before-string - (when (> (window-pixel-width) page-width) - (propertize " " 'display - `(space :align-to - (,(floor (/ (- (window-pixel-width) page-width) 2)))))))) - (overlay-put o 'display `(space . (:width (,page-width) :height (,overley-height)))) - (overlay-put o 'face `(:background ,image-roll-overlay-face-bg-color)) - (overlay-put o 'overlay-size page-size)))) - ;; Determine to display pages and display. Undisplay pages is not necessary as - ;; this is taken care off by `image-roll-update-displayed-pages'. - (let (displayed) - (dolist (p (image-roll-visible-overlays)) - (apply image-roll-display-page-function - p - (when (eq image-roll-display-page-function - 'doc-view-insert-image) - `(:width ,(bound-and-true-p doc-view-image-width)))) - (push p displayed)) - ;; store displayed images for determining which images to update when update - ;; is triggered - (image-mode-window-put 'displayed-pages (nreverse displayed)) - ;; store the height of the visible pages for determining when to update - ;; images, namely when some part of the roll outside this range becomes - ;; visible - (image-mode-window-put 'visible-pages-vscroll-limit - (- (apply #'+ (mapcar #'image-roll-overlay-height displayed)) - (window-text-height nil t)))) - ;; we only need to jump to the right page, the vscroll is conserved and if - ;; required can be set to 0 before the redisplay - (when-let (p (image-roll-current-page)) - (set-window-start nil - (goto-char (overlay-start (nth (1- p) (image-roll-overlays)))) - t) - ;; TODO implement in redisplay, `fractional vscroll' (in page units) - (image-set-window-vscroll (or (image-mode-window-get 'vscroll) - image-roll-vertical-margin)) - (image-set-window-hscroll (or (image-mode-window-get 'hscroll) - 0)))))) - -(defun image-roll-update-displayed-pages () - (let ((old (image-mode-window-get 'displayed-pages)) - (new (image-roll-visible-overlays))) + (let* ((page-sizes (when image-roll-page-sizes-function + (funcall image-roll-page-sizes-function))) + (page-num 0)) + (dolist (page-size page-sizes) + (let* ((page-width (car page-size)) + (overley-height (cdr page-size)) + (o (image-roll-page-overlay (cl-incf page-num) window))) + (when image-roll-center + (overlay-put o 'before-string + (when (> (window-pixel-width window) page-width) + (propertize " " 'display + `(space :align-to + (,(floor (/ (- (window-pixel-width) page-width) 2)))))))) + (overlay-put o 'display `(space . (:width (,page-width) :height (,overley-height)))) + (overlay-put o 'face `(:background ,image-roll-overlay-face-bg-color)) + (overlay-put o 'overlay-size page-size)))) + ;; Determine to display pages and display. Undisplay pages is not necessary as + ;; this is taken care off by `image-roll-update-displayed-pages'. + (let (displayed) + (dolist (p (image-roll-visible-overlays window)) + (apply image-roll-display-page-function + p + (when (eq image-roll-display-page-function + 'doc-view-insert-image) + `(:width ,(bound-and-true-p doc-view-image-width)))) + (push p displayed)) + ;; store displayed images for determining which images to update when update + ;; is triggered + (image-mode-window-put 'displayed-pages (nreverse displayed) window) + ;; store the height of the visible pages for determining when to update + ;; images, namely when some part of the roll outside this range becomes + ;; visible + (image-roll-update-vscroll-limit displayed window)) + ;; we only need to jump to the right page, the vscroll is conserved and if + ;; required can be set to 0 before the redisplay + (image-roll-window-state-change-function window))) + +(defun image-roll-update-displayed-pages (&optional window) + "Update the pages displayed in WINDOW." + (let ((old (image-mode-window-get 'displayed-pages window)) + (new (image-roll-visible-overlays window))) ;; dolist because if images/pages are small enough (or after jumps), there ;; might be multiple image that need to get updated (dolist (p (cl-set-difference old new)) - (image-roll-undisplay-page p)) + (image-roll-undisplay-page p window)) ;; setting/appending new below (dolist (p (cl-set-difference new old)) - (funcall image-roll-display-page-function p)) - (image-mode-window-put 'displayed-pages new) + (funcall image-roll-display-page-function p window)) + (image-mode-window-put 'displayed-pages new window) ;; update also visible-range - (image-mode-window-put 'visible-pages-vscroll-limit - (- (apply #'+ (mapcar #'image-roll-overlay-height new)) - (window-text-height nil t))))) + (image-roll-update-vscroll-limit new window))) (defun image-roll-goto-page-start () + "Go to the start of the first displayed page." (interactive) (image-set-window-vscroll 0)) ;; NOTE code based on (taken from) `pdf-view-goto-page'. (defun image-roll-goto-page (page &optional window) - "Go to PAGE in document." + "Go to PAGE in WINDOW." (interactive (list (if current-prefix-arg (prefix-numeric-value current-prefix-arg) @@ -407,16 +393,8 @@ is a substitute for the `pdf-view-redisplay' function)." (unless (and (>= page 1) (<= page image-roll-last-page)) (error "No such page: %d" page)) - (unless window - (setq window - ;; (if (pdf-util-pdf-window-p) - (selected-window) - ;; t))) - )) - (save-selected-window - ;; Select the window for the hooks below. - (when (window-live-p window) - (select-window window 'norecord)) + (setq window (or window (selected-window))) + (with-selected-window window (let ((changing-p (not (eq page (image-roll-current-page window))))) (when changing-p @@ -428,10 +406,6 @@ is a substitute for the `pdf-view-redisplay' function)." (image-roll-update-displayed-pages)) (when changing-p (run-hooks 'image-roll-after-change-page-hook)) - ;; (when changing-p - ;; (pdf-view-deactivate-region) - ;; (force-mode-line-update) - ;; (run-hooks 'pdf-view-after-change-page-hook)))) nil))) (defun image-roll-next-page (&optional n) @@ -446,16 +420,16 @@ is a substitute for the `pdf-view-redisplay' function)." (image-roll-next-page (- n))) -(defun image-roll-scroll-forward (&optional backward screen) - "Scroll image forward by `image-roll-step-size'. +(defun image-roll-scroll-forward (&optional backward screen window) + "Scroll image forward by `image-roll-step-size' in WINDOW. When BACKWARD is non-nil, scroll backward instead. When SCREEN is non-nil, scroll by window height." (interactive) - (let* ((current-page (image-roll-current-page)) + (let* ((current-page (image-roll-current-page window)) (new-page current-page) - (visible-pages-vscroll-limit (image-mode-window-get 'visible-pages-vscroll-limit)) + (visible-pages-vscroll-limit (image-mode-window-get 'visible-pages-vscroll-limit window)) (step-size (if screen - (* (window-text-height nil t) 0.9) + (* (window-text-height window t) 0.9) image-roll-step-size)) ;; determine number of pages to forward/backward (e.g. when scrolling a @@ -472,18 +446,18 @@ When SCREEN is non-nil, scroll by window height." (remaining-height available-height) new-vscroll) (cond (backward - (cl-decf available-height (window-vscroll nil t)) + (cl-decf available-height (window-vscroll window t)) (while (and (> new-page 0) (> available-height 0)) (setq remaining-height available-height) (setq new-page (1- new-page)) ; allow new-page become < 1 - (cl-decf available-height (image-roll-overlay-height new-page)))) + (cl-decf available-height (image-roll-overlay-height new-page window)))) (t - (cl-decf available-height (- (image-roll-overlay-height current-page) + (cl-decf available-height (- (image-roll-overlay-height current-page window) (window-vscroll nil t))) (while (and (< new-page (1+ image-roll-last-page)) (> available-height 0)) (setq remaining-height available-height) (setq new-page (1+ new-page)) ; allow new-page > last-page - (cl-decf available-height (image-roll-overlay-height new-page))))) + (cl-decf available-height (image-roll-overlay-height new-page window))))) (when (< new-page 1) (message "Beginning of document") @@ -495,21 +469,21 @@ When SCREEN is non-nil, scroll by window height." (when backward (cl-callf - step-size) - (cl-callf2 - (image-roll-overlay-height new-page) remaining-height)) + (cl-callf2 - (image-roll-overlay-height new-page window) remaining-height)) (cond ((= new-page current-page) - (setq new-vscroll (+ (window-vscroll nil t) step-size)) - (image-set-window-vscroll new-vscroll) + (setq new-vscroll (+ (window-vscroll window t) step-size)) + (image-roll-set-vscroll new-vscroll window) (when (> new-vscroll visible-pages-vscroll-limit) - (image-roll-update-displayed-pages))) + (image-roll-update-displayed-pages window))) (t (run-hooks 'image-roll-before-change-page-hook) - (setf (image-roll-current-page) new-page) + (setf (image-roll-current-page window) new-page) (run-hooks 'image-roll-change-page-hook) - (set-window-start nil (goto-char (image-roll-page-to-pos new-page)) t) - (image-set-window-vscroll remaining-height) + (set-window-start window (goto-char (image-roll-page-to-pos new-page)) t) + (image-roll-set-vscroll remaining-height window) (run-hooks 'image-roll-after-change-page-hook) - (image-roll-update-displayed-pages))))) + (image-roll-update-displayed-pages window))))) (defun image-roll-scroll-backward () (interactive) @@ -526,12 +500,14 @@ When SCREEN is non-nil, scroll by window height." (defun image-roll-scroll-mouse-wheel (event) "Scroll according to mouse wheel EVENT." (interactive "e") - (with-selected-window (posn-window (event-start event)) - (image-roll-scroll-forward - (pcase (event-basic-type event) - ('wheel-down nil) - ('wheel-up t) - (_ (error "Event must be wheel down or wheel up event")))))) + (let ((win (posn-window (event-start event)))) + (with-current-buffer (window-buffer win) + (image-roll-scroll-forward + (pcase (event-basic-type event) + ('wheel-down nil) + ('wheel-up t) + (_ (error "Event must be wheel down or wheel up event"))) + nil win)))) (defun image-roll-quick-scroll () (interactive) From fb8b1c8fba13cadb11131fcf068e7817eb0c9b13 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Sat, 17 Jun 2023 01:18:52 +0200 Subject: [PATCH 062/104] Renamings and tweaks --- image-roll.el | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/image-roll.el b/image-roll.el index 62594fec..8fbc0eca 100644 --- a/image-roll.el +++ b/image-roll.el @@ -159,6 +159,10 @@ start (this choice was made in order to simplify the scrolling logic)" `(image-mode-window-get 'page ,window)) +(defmacro image-roll-displayed-pages (&optional window) + "Return list of pages currently displayed in WINDOW." + `(image-mode-window-get 'displayed-pages ,window)) + (defsubst image-roll-page-to-pos (page) "Get the buffer position displaing PAGE." (1- (* 2 page))) @@ -176,10 +180,6 @@ logic)" (/ (1+ (point)) 2) (error "No page is displayed at current position"))) -(defmacro image-roll-displayed-pages (&optional window) - "Return list of pages currently displayed in WINDOW." - `(image-mode-window-get 'displayed-pages ,window)) - (defsubst image-roll-overlay-height (page &optional window) "Get the height of overlay displaying PAGE on WINDOW." (+ (cdr (overlay-get (image-roll-page-overlay page window) 'overlay-size)) @@ -283,9 +283,9 @@ overlays." (image-mode-window-put 'hscroll hscroll win) (set-window-hscroll win hscroll)) -(defun image-roll-window-state-change-function (win) +(defun image-roll-window-configuration-change-hook (&optional win) "Handle state change for window WIN. -It should be added to `window-state-change-functions' buffer locally." +It should be added to `window-configuration-change-hook' buffer locally." (when-let (((memq 'image-roll-new-window-function image-mode-new-window-functions)) (p (image-mode-window-get 'page win))) (set-window-start win (goto-char (1- (* 2 p))) t) @@ -304,18 +304,11 @@ It should be added to `window-state-change-functions' buffer locally." (- sumheights (window-text-height nil t))) window)) -(defun image-roll-redisplay (&optional window _no-relative-vscroll) - "Redisplay the scroll. +(defun image-roll-window-size-change-function (&optional window _no-relative-vscroll) + "Redisplay the scroll in WINDOW. Besides that this function can be called directly, it should also -be added to the `window-configuration-change-hook'. - -The argument WINDOW is not used in the body, but it exists to -make the function compatible with `pdf-tools' (in which case it -is a substitute for the `pdf-view-redisplay' function)." - - ;; NOTE the `(when (listp image-mode-winprops-alist)' from - ;; `image-mode-reapply-winprops' was removed here (but in the end might turn - ;; out to be required) +be added to the `window-size-change-function'. It is a substitute for the +`pdf-view-redisplay' function." ;; Beware: this call to image-mode-winprops can't be optimized away, because ;; it not only gets the winprops data but sets it up if needed (e.g. it's used @@ -361,7 +354,7 @@ is a substitute for the `pdf-view-redisplay' function)." (image-roll-update-vscroll-limit displayed window)) ;; we only need to jump to the right page, the vscroll is conserved and if ;; required can be set to 0 before the redisplay - (image-roll-window-state-change-function window))) + (image-roll-window-configuration-change-hook window))) (defun image-roll-update-displayed-pages (&optional window) "Update the pages displayed in WINDOW." @@ -546,7 +539,7 @@ This function is used for the image-roll-demo." ;; reapplies the vscroll, so we simply initialize the ;; `image-mode-winprops-alist' here, and add lines from ;; `image-mode-reapply-winprops' at the start of `image-roll-redisplay'. - (add-hook 'window-size-change-functions 'image-roll-redisplay nil t) + (add-hook 'window-size-change-functions 'image-roll-window-size-change-function nil t) (add-hook 'image-mode-new-window-functions 'image-roll-new-window-function nil t) (unless (listp image-mode-winprops-alist) (setq image-mode-winprops-alist nil))) From bd8f6a11411e40a8c3123d2874adf596a452cdea Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Sat, 17 Jun 2023 01:35:09 +0200 Subject: [PATCH 063/104] Only select windows --- image-roll.el | 65 ++++++++++++++++++++++++--------------------------- 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/image-roll.el b/image-roll.el index 8fbc0eca..9846f647 100644 --- a/image-roll.el +++ b/image-roll.el @@ -240,38 +240,35 @@ WINPROPS are the initial window properties. This function should be added to image-roll (continuous scroll) minor mode commands, after erasing the buffer to create the overlays." - (let ((win (or (car winprops) (selected-window)))) - (with-current-buffer (window-buffer win) - (if (not (overlays-at 1)) - (let (overlays - (pages image-roll-last-page) - (inhibit-read-only t)) - - (erase-buffer) - - ;; here we only add the 'page' and 'window' overlay-properties, we add - ;; more properties/information as soon as it becomes available in the - ;; 'image-roll-redisplay' function - (dotimes (i pages) - (insert " ") - (let ((o (make-overlay (1- (point)) (point)))) - (overlay-put o 'page (1+ i)) - (overlay-put o 'window win) - (push o overlays)) - (insert "\n")) - (delete-char -1) - (set-buffer-modified-p nil) - - ;; required to make `pdf-view-redisplay-some-windows' call `pdf-view-redisplay' - (when-let (fun image-roll-set-redisplay-flag-function) - (funcall fun))) - (dotimes (i (/ (point-max) 2)) - (overlay-put (copy-overlay (car (overlays-at (1+ (* 2 i))))) - 'window (car winprops)))) - - ;; initial `image-roll-redisplay' needs to know which page(s) to display - (setf (image-roll-current-page (car winprops)) - (or (image-roll-current-page (car winprops)) 1))))) + (let ((win (or (and (windowp (car winprops)) (car winprops)) (selected-window)))) + (if (not (overlays-at 1)) + (let ((pages image-roll-last-page) + (inhibit-read-only t)) + + (erase-buffer) + + ;; here we only add the 'page' and 'window' overlay-properties, we add + ;; more properties/information as soon as it becomes available in the + ;; 'image-roll-redisplay' function + (dotimes (i pages) + (insert " ") + (let ((o (make-overlay (1- (point)) (point)))) + (overlay-put o 'page (1+ i)) + (overlay-put o 'window win)) + (insert "\n")) + (delete-char -1) + (set-buffer-modified-p nil) + + ;; required to make `pdf-view-redisplay-some-windows' call `pdf-view-redisplay' + (when-let (fun image-roll-set-redisplay-flag-function) + (funcall fun))) + (dotimes (i (/ (point-max) 2)) + (overlay-put (copy-overlay (car (overlays-at (1+ (* 2 i))))) + 'window (car winprops)))) + + ;; initial `image-roll-redisplay' needs to know which page(s) to display + (setf (image-roll-current-page (car winprops)) + (or (image-roll-current-page (car winprops)) 1)))) (defun image-roll-set-vscroll (vscroll win) "Set vscroll to VSCROLL in window WIN." @@ -307,7 +304,7 @@ It should be added to `window-configuration-change-hook' buffer locally." (defun image-roll-window-size-change-function (&optional window _no-relative-vscroll) "Redisplay the scroll in WINDOW. Besides that this function can be called directly, it should also -be added to the `window-size-change-function'. It is a substitute for the +be added to the `window-size-change-functions'. It is a substitute for the `pdf-view-redisplay' function." ;; Beware: this call to image-mode-winprops can't be optimized away, because @@ -316,7 +313,7 @@ be added to the `window-size-change-function'. It is a substitute for the (setq window (if (window-live-p window) window (selected-window))) (when (and (memq 'image-roll-new-window-function image-mode-new-window-functions) (eq (current-buffer) (window-buffer window))) - (if (equal (buffer-substring (point-min) (1+ (point-min))) "%") + (if (and (> (point-max) 1) (equal (buffer-substring (point-min) (1+ (point-min))) "%")) (image-roll-new-window-function `(,window)) (image-mode-winprops window t)) (let* ((page-sizes (when image-roll-page-sizes-function From 134e8d1b79d4107be434e29b93fe7088073f3ed2 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Sat, 17 Jun 2023 02:34:33 +0200 Subject: [PATCH 064/104] Get back to using with-selected-window for scrolling with wheel --- image-roll.el | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/image-roll.el b/image-roll.el index 9846f647..999c89d3 100644 --- a/image-roll.el +++ b/image-roll.el @@ -490,14 +490,12 @@ When SCREEN is non-nil, scroll by window height." (defun image-roll-scroll-mouse-wheel (event) "Scroll according to mouse wheel EVENT." (interactive "e") - (let ((win (posn-window (event-start event)))) - (with-current-buffer (window-buffer win) - (image-roll-scroll-forward - (pcase (event-basic-type event) - ('wheel-down nil) - ('wheel-up t) - (_ (error "Event must be wheel down or wheel up event"))) - nil win)))) + (with-selected-window (posn-window (event-start event)) + (image-roll-scroll-forward + (pcase (event-basic-type event) + ('wheel-down nil) + ('wheel-up t) + (_ (error "Event must be wheel down or wheel up event")))))) (defun image-roll-quick-scroll () (interactive) From 87b0ffa285c4b203651f1379c7e79d0ddd1bc0a9 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Sat, 17 Jun 2023 02:58:58 +0200 Subject: [PATCH 065/104] Don't set window start in window-configuration-change-hook I now think it was the cause of weirdness --- image-roll.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/image-roll.el b/image-roll.el index 999c89d3..a81469cf 100644 --- a/image-roll.el +++ b/image-roll.el @@ -285,7 +285,7 @@ overlays." It should be added to `window-configuration-change-hook' buffer locally." (when-let (((memq 'image-roll-new-window-function image-mode-new-window-functions)) (p (image-mode-window-get 'page win))) - (set-window-start win (goto-char (1- (* 2 p))) t) + (goto-char (1- (* 2 p))) t (image-roll-set-vscroll (or (image-mode-window-get 'vscroll win) image-roll-vertical-margin) win) From c9789931ca37e2a0e76ba760c854599875702545 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Sat, 17 Jun 2023 10:11:58 +0200 Subject: [PATCH 066/104] Some cleanup --- image-roll.el | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/image-roll.el b/image-roll.el index a81469cf..a176c142 100644 --- a/image-roll.el +++ b/image-roll.el @@ -82,9 +82,7 @@ :version "28.1") (defcustom image-roll-vertical-margin 2 - "Vertical margin around image (pixels), i.e. page separation height. -Because the margin is added to both sides of each page, the page -separation height is twice this value." + "Vertical margin between images in pixels, i.e. page separation height." :type 'integer) (defcustom image-roll-overlay-face-bg-color "gray" @@ -93,7 +91,7 @@ separation height is twice this value." (defcustom image-roll-step-size 50 "Scroll step size in pixels units." - :type '(choice function integer float)) + :type 'integer) (defcustom image-roll-center nil "When non-nil, center the roll horizontally in the window." @@ -285,12 +283,9 @@ overlays." It should be added to `window-configuration-change-hook' buffer locally." (when-let (((memq 'image-roll-new-window-function image-mode-new-window-functions)) (p (image-mode-window-get 'page win))) - (goto-char (1- (* 2 p))) t - (image-roll-set-vscroll (or (image-mode-window-get 'vscroll win) - image-roll-vertical-margin) - win) - (image-roll-set-hscroll (or (image-mode-window-get 'hscroll) 0) - win))) + (goto-char (1- (* 2 p))) + (image-roll-set-vscroll (or (image-mode-window-get 'vscroll win) 0) win) + (image-roll-set-hscroll (or (image-mode-window-get 'hscroll) 0) win))) (defun image-roll-update-vscroll-limit (displayed &optional window) "Update the total available vscroll from DISPLAYED pages on WINDOW." @@ -298,7 +293,7 @@ It should be added to `window-configuration-change-hook' buffer locally." (let ((sumheights 0)) (dolist (page displayed) (cl-incf sumheights (image-roll-overlay-height page window))) - (- sumheights (window-text-height nil t))) + (- sumheights (window-text-height window t))) window)) (defun image-roll-window-size-change-function (&optional window _no-relative-vscroll) From 387ca7c72484746c4fb6bf925cf9fd1b516775fd Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Sat, 17 Jun 2023 10:26:52 +0200 Subject: [PATCH 067/104] Reorganize the scrolling functions --- image-roll.el | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/image-roll.el b/image-roll.el index a176c142..b6b2e87f 100644 --- a/image-roll.el +++ b/image-roll.el @@ -405,17 +405,15 @@ be added to the `window-size-change-functions'. It is a substitute for the (image-roll-next-page (- n))) -(defun image-roll-scroll-forward (&optional backward screen window) - "Scroll image forward by `image-roll-step-size' in WINDOW. -When BACKWARD is non-nil, scroll backward instead. -When SCREEN is non-nil, scroll by window height." +(defun image-roll-scroll-forward (&optional backward step-size window) + "Scroll image forward by STEP-SIZE in WINDOW. +By default STEP SIZE is `image-roll-step-size'. When BACKWARD is non-nil, +scroll backward instead." (interactive) (let* ((current-page (image-roll-current-page window)) (new-page current-page) (visible-pages-vscroll-limit (image-mode-window-get 'visible-pages-vscroll-limit window)) - (step-size (if screen - (* (window-text-height window t) 0.9) - image-roll-step-size)) + (step-size (or step-size image-roll-step-size)) ;; determine number of pages to forward/backward (e.g. when scrolling a ;; full screen the images/pages are small). We subtract, from the step @@ -470,17 +468,22 @@ When SCREEN is non-nil, scroll by window height." (run-hooks 'image-roll-after-change-page-hook) (image-roll-update-displayed-pages window))))) -(defun image-roll-scroll-backward () +(defun image-roll-scroll-backward (&optional step-size) + "Scroll backward by STEP-SIZE." (interactive) - (image-roll-scroll-forward t)) + (image-roll-scroll-forward t step-size)) -(defun image-roll-scroll-screen-forward () +(defun image-roll-scroll-screen-forward (&optional backward) + "Scroll forward by almost a full screen. +Id BACKWARD is non-nil scroll backward instead." (interactive) - (image-roll-scroll-forward nil t)) + (image-roll-scroll-forward + backward (* (window-text-height nil t) 0.9))) (defun image-roll-scroll-screen-backward () + "SCROLL backward by almost a full screen." (interactive) - (image-roll-scroll-forward t t)) + (image-roll-scroll-screen-forward t)) (defun image-roll-scroll-mouse-wheel (event) "Scroll according to mouse wheel EVENT." From 1a176e21bb13a133218540c126f1fd93f314163b Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Sun, 18 Jun 2023 21:15:22 +0200 Subject: [PATCH 068/104] Display and undisplay images dynamically --- image-roll.el | 464 +++++++++++++++----------------------------------- 1 file changed, 139 insertions(+), 325 deletions(-) diff --git a/image-roll.el b/image-roll.el index b6b2e87f..88662318 100644 --- a/image-roll.el +++ b/image-roll.el @@ -72,10 +72,9 @@ ;; inspecting it. ;;; Code: - (require 'image-mode) -(require 'svg) +;;; Custom Variables (defgroup image-roll nil "Image roll configurations." :group 'applications @@ -87,7 +86,8 @@ (defcustom image-roll-overlay-face-bg-color "gray" "Background color of overlay, i.e. page separation color." - :type 'color) + :type 'color + :set (lambda (_ color) (put 'image-roll 'face `(:background ,color)))) (defcustom image-roll-step-size 50 "Scroll step size in pixels units." @@ -97,25 +97,12 @@ "When non-nil, center the roll horizontally in the window." :type 'boolean) -;; (defvar-local image-roll-number-of-pages-function nil -;; "Function that should return the total number of pages. -;; The function should return an integer with the total number of -;; pages in the document.") -(defvar-local image-roll-last-page 0) - -(defvar-local image-roll-page-sizes-function nil - "Function that should return page-sizes of document. -The function should return a list of conses of the form (WIDTH . -HEIGHT), both numbers.") +(defcustom image-roll-after-change-page-hook nil + "Hook run after changing to and displaying another page." + :type 'hook) -(defvar-local image-roll-set-redisplay-flag-function nil - "Function that sets the `needs-redisplay' window property. -Setting this is required for the -`pdf-view-redisplay-some-windows' to work correctly. The same -mechanism should probably get ported to image-roll.el also, to -make redisplay work correctly on multiple windows. However, this -is an advanced feature that is not required for basic image-roll -usage.") +;;; Local variables to be set by implementations +(defvar-local image-roll-last-page 0) (defvar-local image-roll-display-page-function nil "Function that sets the overlay's display property. @@ -124,32 +111,6 @@ argument (PAGE). The function should use `(image-roll-page-overlay PAGE)' to add the image of the page as the overlay's display-property.") -(defcustom image-roll-change-page-hook nil - "Hook run after changing to another page, but before displaying it. - -See also `image-roll-before-change-page-hook' and -`image-roll-after-change-page-hook'." - :type 'hook) - -(defcustom image-roll-before-change-page-hook nil - "Hook run before changing to another page. - -See also `image-roll-change-page-hook' and -`image-roll-after-change-page-hook'." - :type 'hook) - -(defcustom image-roll-after-change-page-hook nil - "Hook run after changing to and displaying another page. - -See also `image-roll-change-page-hook' and -`image-roll-before-change-page-hook'." - :type 'hook) - -(defmacro image-roll-debug (object) - `(progn (print (format "%s = %s" ,object (eval ,object)) - #'external-debugging-output) - (eval ,object))) - (defmacro image-roll-current-page (&optional window) "Return the page number of the currently displayed page in WINDOW. The current page is the page that overlaps with the window @@ -157,6 +118,7 @@ start (this choice was made in order to simplify the scrolling logic)" `(image-mode-window-get 'page ,window)) +;;; Utility Macros and inline functions (defmacro image-roll-displayed-pages (&optional window) "Return list of pages currently displayed in WINDOW." `(image-mode-window-get 'displayed-pages ,window)) @@ -169,60 +131,33 @@ logic)" "Return overlay displaying PAGE in WINDOW." (cl-find (or window (selected-window)) (overlays-at (image-roll-page-to-pos - (or page (image-roll-current-page window)))) + (or page (image-roll-page-at-current-pos)))) :key (lambda (ov) (overlay-get ov 'window)))) (defun image-roll-page-at-current-pos () "Page at point." (if (cl-oddp (point)) (/ (1+ (point)) 2) - (error "No page is displayed at current position"))) + (error "No page is displayed at current position (%s)" (point)))) (defsubst image-roll-overlay-height (page &optional window) "Get the height of overlay displaying PAGE on WINDOW." - (+ (cdr (overlay-get (image-roll-page-overlay page window) 'overlay-size)) - image-roll-vertical-margin)) - -(defun image-roll-visible-overlays (&optional window) - "Page numbers of overlays currently visible in WINDOW. -The numbers are returned in a list. Overlays that are only -partially visible are included." - (let* (visible - (page (image-roll-current-page window)) - (available-height (window-pixel-height window)) - (last-page image-roll-last-page)) - - (push page visible) - (cl-decf available-height (- (image-roll-overlay-height page window) - (window-vscroll window t))) - (cl-incf page) - - (unless (> page image-roll-last-page) - (while (> available-height 0) - (push page visible) - (if (= page last-page) - (setq available-height 0) - (cl-decf available-height (image-roll-overlay-height page window)) - (cl-incf page)))) - visible)) + (when-let (ov (image-roll-page-overlay page window)) + (+ (cdr (overlay-get ov 'overlay-size)) image-roll-vertical-margin))) +;;; Helper functions (defun image-roll-undisplay-page (page &optional window) "Undisplay PAGE from WINDOW. Replaces the image display property of the overlay holding PAGE with a space. It size is determined from the image its `image-size'." - (display-warning '(image-roll) (format "undisplay %s" page) - :debug "*image-roll-debug-log*") (let* ((o (image-roll-page-overlay page window)) (d (overlay-get o 'display)) (im (if (imagep d) - d - (nth 1 d))) - (s (image-size im t)) - (w (car s)) - (h (cdr s))) - (overlay-put o 'display `(space . (:width (,w) :height (,h)))) - (overlay-put o 'face `(:background "gray")))) + d + (nth 1 d)))) + (when (imagep im) + (overlay-put o 'display nil)))) (defun image-roll-new-window-function (&optional winprops) "Setup image roll in a new window. @@ -244,29 +179,25 @@ overlays." (inhibit-read-only t)) (erase-buffer) + (remove-overlays) ;; here we only add the 'page' and 'window' overlay-properties, we add ;; more properties/information as soon as it becomes available in the ;; 'image-roll-redisplay' function - (dotimes (i pages) + (dotimes (_ pages) (insert " ") (let ((o (make-overlay (1- (point)) (point)))) - (overlay-put o 'page (1+ i)) + (overlay-put o 'category 'image-roll) (overlay-put o 'window win)) (insert "\n")) (delete-char -1) - (set-buffer-modified-p nil) - - ;; required to make `pdf-view-redisplay-some-windows' call `pdf-view-redisplay' - (when-let (fun image-roll-set-redisplay-flag-function) - (funcall fun))) + (goto-char 1) + (set-buffer-modified-p nil)) (dotimes (i (/ (point-max) 2)) (overlay-put (copy-overlay (car (overlays-at (1+ (* 2 i))))) 'window (car winprops)))) - ;; initial `image-roll-redisplay' needs to know which page(s) to display - (setf (image-roll-current-page (car winprops)) - (or (image-roll-current-page (car winprops)) 1)))) + (cl-callf or (image-roll-current-page win) 1))) (defun image-roll-set-vscroll (vscroll win) "Set vscroll to VSCROLL in window WIN." @@ -281,20 +212,27 @@ overlays." (defun image-roll-window-configuration-change-hook (&optional win) "Handle state change for window WIN. It should be added to `window-configuration-change-hook' buffer locally." - (when-let (((memq 'image-roll-new-window-function image-mode-new-window-functions)) - (p (image-mode-window-get 'page win))) - (goto-char (1- (* 2 p))) + (when (memq 'image-roll-new-window-function image-mode-new-window-functions) (image-roll-set-vscroll (or (image-mode-window-get 'vscroll win) 0) win) (image-roll-set-hscroll (or (image-mode-window-get 'hscroll) 0) win))) -(defun image-roll-update-vscroll-limit (displayed &optional window) - "Update the total available vscroll from DISPLAYED pages on WINDOW." - (image-mode-window-put 'visible-pages-vscroll-limit - (let ((sumheights 0)) - (dolist (page displayed) - (cl-incf sumheights (image-roll-overlay-height page window))) - (- sumheights (window-text-height window t))) - window)) +(defun image-roll-display-pages (&optional window force) + "Display pages to fill the WINDOW. +If FORCE is non-nill redisplay a page even if it is already displayed." + (let (displayed + (page (image-roll-page-at-current-pos))) + (while (pos-visible-in-window-p (image-roll-page-to-pos page) window t) + (when (or force (not (overlay-get (image-roll-page-overlay page window) 'display))) + (funcall image-roll-display-page-function page window)) + (push page displayed) + (cl-incf page)) + ;; store displayed images for determining which images to update when update + ;; is triggered + (cl-callf cl-union (image-mode-window-get 'displayed-pages window) displayed) + ;; store the height of the visible pages for determining when to update + ;; images, namely when some part of the roll outside this range becomes + ;; visible + displayed)) (defun image-roll-window-size-change-function (&optional window _no-relative-vscroll) "Redisplay the scroll in WINDOW. @@ -311,39 +249,9 @@ be added to the `window-size-change-functions'. It is a substitute for the (if (and (> (point-max) 1) (equal (buffer-substring (point-min) (1+ (point-min))) "%")) (image-roll-new-window-function `(,window)) (image-mode-winprops window t)) - (let* ((page-sizes (when image-roll-page-sizes-function - (funcall image-roll-page-sizes-function))) - (page-num 0)) - (dolist (page-size page-sizes) - (let* ((page-width (car page-size)) - (overley-height (cdr page-size)) - (o (image-roll-page-overlay (cl-incf page-num) window))) - (when image-roll-center - (overlay-put o 'before-string - (when (> (window-pixel-width window) page-width) - (propertize " " 'display - `(space :align-to - (,(floor (/ (- (window-pixel-width) page-width) 2)))))))) - (overlay-put o 'display `(space . (:width (,page-width) :height (,overley-height)))) - (overlay-put o 'face `(:background ,image-roll-overlay-face-bg-color)) - (overlay-put o 'overlay-size page-size)))) ;; Determine to display pages and display. Undisplay pages is not necessary as ;; this is taken care off by `image-roll-update-displayed-pages'. - (let (displayed) - (dolist (p (image-roll-visible-overlays window)) - (apply image-roll-display-page-function - p - (when (eq image-roll-display-page-function - 'doc-view-insert-image) - `(:width ,(bound-and-true-p doc-view-image-width)))) - (push p displayed)) - ;; store displayed images for determining which images to update when update - ;; is triggered - (image-mode-window-put 'displayed-pages (nreverse displayed) window) - ;; store the height of the visible pages for determining when to update - ;; images, namely when some part of the roll outside this range becomes - ;; visible - (image-roll-update-vscroll-limit displayed window)) + (image-roll-display-pages window t) ;; we only need to jump to the right page, the vscroll is conserved and if ;; required can be set to 0 before the redisplay (image-roll-window-configuration-change-hook window))) @@ -351,22 +259,32 @@ be added to the `window-size-change-functions'. It is a substitute for the (defun image-roll-update-displayed-pages (&optional window) "Update the pages displayed in WINDOW." (let ((old (image-mode-window-get 'displayed-pages window)) - (new (image-roll-visible-overlays window))) + (new (image-roll-display-pages window))) ;; dolist because if images/pages are small enough (or after jumps), there ;; might be multiple image that need to get updated (dolist (p (cl-set-difference old new)) (image-roll-undisplay-page p window)) ;; setting/appending new below - (dolist (p (cl-set-difference new old)) - (funcall image-roll-display-page-function p window)) + (setf (image-roll-current-page window) (image-roll-page-at-current-pos)) (image-mode-window-put 'displayed-pages new window) - ;; update also visible-range - (image-roll-update-vscroll-limit new window))) - + new)) + +(defun image-roll--goto-point (vscroll &optional window redisplay) + "Make page at point the current page with VSCROLL in WINDOW. +If REDISPLAY is non-nil perform redisplay for `set-window-start' to take effect." + (set-window-start window (point) t) + (image-roll-set-vscroll vscroll window) + (image-roll-update-displayed-pages window) + (when redisplay + (redisplay) + (message "%S" (window-start))) + (run-hooks 'image-roll-after-change-page-hook)) + +;;; Page navigation commands (defun image-roll-goto-page-start () "Go to the start of the first displayed page." (interactive) - (image-set-window-vscroll 0)) + (image-roll-set-vscroll 0 nil)) ;; NOTE code based on (taken from) `pdf-view-goto-page'. (defun image-roll-goto-page (page &optional window) @@ -378,206 +296,102 @@ be added to the `window-size-change-functions'. It is a substitute for the (unless (and (>= page 1) (<= page image-roll-last-page)) (error "No such page: %d" page)) - (setq window (or window (selected-window))) - (with-selected-window window - (let ((changing-p - (not (eq page (image-roll-current-page window))))) - (when changing-p - (run-hooks 'image-roll-before-change-page-hook) - (setf (image-roll-current-page window) page) - (run-hooks 'image-roll-change-page-hook)) - (when (window-live-p window) - (goto-char (image-roll-page-to-pos page)) - (image-roll-update-displayed-pages)) - (when changing-p - (run-hooks 'image-roll-after-change-page-hook)) - nil))) + (if (eq page (image-roll-page-at-current-pos)) + (image-roll-set-vscroll 0 window) + (goto-char (image-roll-page-to-pos page)) + (image-roll--goto-point 0 window t))) (defun image-roll-next-page (&optional n) "Go to next page or next Nth page." (interactive "p") - (set-window-start nil (+ (point) 2) t) - (image-roll-goto-page (+ (image-roll-current-page) n))) + (set-window-start nil (+ (point) (* 2 n)) t) + (image-roll-goto-page (+ (image-roll-page-at-current-pos) n))) (defun image-roll-previous-page (&optional n) "Go to previous page or previous Nth page." (interactive "p") (image-roll-next-page (- n))) - -(defun image-roll-scroll-forward (&optional backward step-size window) - "Scroll image forward by STEP-SIZE in WINDOW. -By default STEP SIZE is `image-roll-step-size'. When BACKWARD is non-nil, -scroll backward instead." - (interactive) - (let* ((current-page (image-roll-current-page window)) - (new-page current-page) - (visible-pages-vscroll-limit (image-mode-window-get 'visible-pages-vscroll-limit window)) - (step-size (or step-size image-roll-step-size)) - - ;; determine number of pages to forward/backward (e.g. when scrolling a - ;; full screen the images/pages are small). We subtract, from the step - ;; size, the remaining visible part of the current-page, and then - ;; continue subtracting page-overlay heights until no available height - ;; is left. To also set the scroll correctly after jumping the number - ;; of pages, we store the remaining height, i.e. the remaining height - ;; before the last step where the available height becomes negative - ;; (for the edge case when available height is 0, there is no new - ;; overlay visible yet and the `=' is not included in the `while' - ;; condition) - (available-height step-size) - (remaining-height available-height) - new-vscroll) - (cond (backward - (cl-decf available-height (window-vscroll window t)) - (while (and (> new-page 0) (> available-height 0)) - (setq remaining-height available-height) - (setq new-page (1- new-page)) ; allow new-page become < 1 - (cl-decf available-height (image-roll-overlay-height new-page window)))) - (t - (cl-decf available-height (- (image-roll-overlay-height current-page window) - (window-vscroll nil t))) - (while (and (< new-page (1+ image-roll-last-page)) (> available-height 0)) - (setq remaining-height available-height) - (setq new-page (1+ new-page)) ; allow new-page > last-page - (cl-decf available-height (image-roll-overlay-height new-page window))))) - - (when (< new-page 1) - (message "Beginning of document") - (setq new-page 1)) - - (when (> new-page image-roll-last-page) - (message "End of document") - (setq new-page image-roll-last-page)) - - (when backward - (cl-callf - step-size) - (cl-callf2 - (image-roll-overlay-height new-page window) remaining-height)) - - (cond ((= new-page current-page) - (setq new-vscroll (+ (window-vscroll window t) step-size)) - (image-roll-set-vscroll new-vscroll window) - (when (> new-vscroll visible-pages-vscroll-limit) - (image-roll-update-displayed-pages window))) - (t - (run-hooks 'image-roll-before-change-page-hook) - (setf (image-roll-current-page window) new-page) - (run-hooks 'image-roll-change-page-hook) - (set-window-start window (goto-char (image-roll-page-to-pos new-page)) t) - (image-roll-set-vscroll remaining-height window) - (run-hooks 'image-roll-after-change-page-hook) - (image-roll-update-displayed-pages window))))) - -(defun image-roll-scroll-backward (&optional step-size) - "Scroll backward by STEP-SIZE." - (interactive) - (image-roll-scroll-forward t step-size)) - -(defun image-roll-scroll-screen-forward (&optional backward) - "Scroll forward by almost a full screen. -Id BACKWARD is non-nil scroll backward instead." - (interactive) +;;; Scrolling Commands +(defun image-roll-scroll-forward (&optional pixels window) + "Scroll image PIXELS forward in WINDOW. +By default PIXELS is `image-roll-step-size'. When PIXELS is negative scroll +backward instead. + +With a prefix arg PIXELS is the numeric value times `image-roll-step-size'." + (interactive (list (* (prefix-numeric-value current-prefix-arg) image-roll-step-size))) + (setq pixels (or pixels image-roll-step-size)) + (when (> 0 pixels) (image-roll-scroll-backward (- pixels) window)) + (let ((pos (point))) + (while (when-let ((data (pos-visible-in-window-p (point) window t)) + (occupied-pixels (if (nth 2 data) + (nth 4 data) + (line-pixel-height)))) + (and (> pixels occupied-pixels) + (cl-decf pixels occupied-pixels))) + (forward-char 2)) + (unless (pos-visible-in-window-p (point) window t) + (while (and (if (eobp) + (prog1 nil (message "End of buffer.")) + t) + (progn (funcall image-roll-display-page-function + (image-roll-page-at-current-pos) window) + (let ((height (line-pixel-height))) + (when (> pixels height) + (cl-decf pixels height))))) + (forward-char 2))) + (if (eq pos (point)) + (progn (image-roll-set-vscroll (+ (window-vscroll window t) pixels) + window) + (image-roll-update-displayed-pages window)) + (image-roll--goto-point pixels window)))) + +(defun image-roll-scroll-backward (&optional pixels window) + "Scroll image PIXELS backwards in WINDOW. +By default PIXELS is `image-roll-step-size'. When PIXELS is negative scroll +forward instead. + +With a prefix arg PIXELS is the numeric value times `image-roll-step-size'." + (interactive (list (* (prefix-numeric-value current-prefix-arg) image-roll-step-size))) + (setq pixels (or pixels image-roll-step-size)) + (when (> 0 pixels) (image-roll-scroll-backward (- pixels) window)) + (let* ((data (pos-visible-in-window-p (point) window t)) + (pixels-top (if (nth 2 data) (nth 2 data) 0))) + (if (< pixels pixels-top) + (progn (image-roll-set-vscroll (- (window-vscroll window t) pixels) + window) + (image-roll-update-displayed-pages window)) + (cl-decf pixels pixels-top) + (while (and (if (bobp) + (prog1 nil (message "Beginning of buffer.")) + t) + (progn (forward-char -2) + (funcall image-roll-display-page-function + (image-roll-page-at-current-pos) window) + (cl-decf pixels (line-pixel-height))) + (> pixels 0))) + (image-roll--goto-point (- pixels) window)))) + +(defun image-roll-scroll-screen-forward (&optional arg) + "Scroll forward by (almost) ARG many full screens." + (interactive "p") (image-roll-scroll-forward - backward (* (window-text-height nil t) 0.9))) + (* (window-text-height nil t) (- arg 0.1)))) -(defun image-roll-scroll-screen-backward () - "SCROLL backward by almost a full screen." - (interactive) - (image-roll-scroll-screen-forward t)) +(defun image-roll-scroll-screen-backward (&optional arg) + "Scroll backward by (almost) ARG many full screens." + (interactive "p") + (image-roll-scroll-backward + (* (window-text-height nil t) (- arg 0.1)))) (defun image-roll-scroll-mouse-wheel (event) "Scroll according to mouse wheel EVENT." (interactive "e") (with-selected-window (posn-window (event-start event)) - (image-roll-scroll-forward - (pcase (event-basic-type event) - ('wheel-down nil) - ('wheel-up t) - (_ (error "Event must be wheel down or wheel up event")))))) - -(defun image-roll-quick-scroll () - (interactive) - (dotimes (_i 200) - (image-roll-scroll-forward) - (sit-for 0.0001))) - -(defun image-roll-demo-display-page (page) - "Return demo image of page. -This function is used for the image-roll-demo." - (let* ((o (image-roll-page-overlay page)) - (s (cdr (overlay-get o 'display))) - (w (car (plist-get s :width))) - (h (car (plist-get s :height))) - (svg (svg-create w h))) - (unless w (print "NO W" #'external-debugging-output)) - (svg-rectangle svg 0 0 w h :fill-color "white") - (svg-text svg - (number-to-string page) - :font-size "40" - :fill "black" - :x 20 - :y 50) - (when image-roll-center - (overlay-put o 'before-string - (when (> (window-pixel-width) w) - (propertize " " 'display - `(space :align-to - (,(floor (/ (- (window-pixel-width) w) 2)))))))) - (overlay-put o 'display (svg-image svg :margin `(0 . ,image-roll-vertical-margin))))) - -(define-derived-mode image-roll-mode special-mode "Image Roll" - ;; we don't use `(image-mode-setup-winprops)' because it would additionally - ;; add `image-mode-reapply-winprops' to the - ;; `window-configuration-change-hook', but `image-roll-redisplay' already - ;; reapplies the vscroll, so we simply initialize the - ;; `image-mode-winprops-alist' here, and add lines from - ;; `image-mode-reapply-winprops' at the start of `image-roll-redisplay'. - (add-hook 'window-size-change-functions 'image-roll-window-size-change-function nil t) - (add-hook 'image-mode-new-window-functions 'image-roll-new-window-function nil t) - (unless (listp image-mode-winprops-alist) - (setq image-mode-winprops-alist nil))) -;; (add-hook 'window-configuration-change-hook -;; #'image-mode-reapply-winprops nil t)) -;; (image-mode-setup-winprops)) - -(setq image-roll-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "C-n") 'image-roll-scroll-forward) - (define-key map (kbd "") 'image-roll-scroll-forward) - (define-key map (kbd "C-p") 'image-roll-scroll-backward) - (define-key map (kbd "") 'image-roll-scroll-backward) - (define-key map (kbd "") 'image-roll-scroll-mouse-wheel) - (define-key map (kbd "") 'image-roll-scroll-mouse-wheel) - (define-key map (kbd "") 'image-roll-scroll-forward) - (define-key map (kbd "") 'image-roll-scroll-backward) - (define-key map "n" 'image-roll-next-page) - (define-key map (kbd "") 'image-roll-next-page) - (define-key map "p" 'image-roll-previous-page) - (define-key map (kbd "") 'image-roll-previous-page) - (define-key map (kbd "S-") 'image-roll-scroll-screen-forward) - (define-key map (kbd "S-") 'image-roll-scroll-screen-backward) - (define-key map [remap goto-line] 'image-roll-goto-page) - map)) - -(defun image-roll-demo (&optional page-size pages) - (interactive) - (let ((buf-name "*image-roll-demo*")) - ;; (if (get-buffer buf-name) - ;; (switch-to-buffer (current-buffer)) - (with-current-buffer (get-buffer-create buf-name) - (erase-buffer) - (image-roll-mode) - (setq cursor-type nil) - (setq image-roll-step-size 50) - (setq-local image-roll-last-page (or pages 3) - image-roll-display-page-function 'image-roll-demo-display-page - image-roll-demo-page-size (or page-size - (lambda () - (let ((w (window-pixel-width))) - (cons w (* 1.4 w)))))) - - (setq image-roll-center t) - (switch-to-buffer (current-buffer))))) + (pcase (event-basic-type event) + ('wheel-down (image-roll-scroll-forward)) + ('wheel-up (image-roll-scroll-backward)) + (_ (error "Event must be wheel down or wheel up event"))))) (provide 'image-roll) From 7a61ee126559b2b90629f21a69d5fbbff70d0e1d Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Sun, 18 Jun 2023 21:16:06 +0200 Subject: [PATCH 069/104] Move demo to its own file --- image-roll-mode.el | 92 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 image-roll-mode.el diff --git a/image-roll-mode.el b/image-roll-mode.el new file mode 100644 index 00000000..14d09ba0 --- /dev/null +++ b/image-roll-mode.el @@ -0,0 +1,92 @@ +;;; image-roll-mode.el --- Demo for image roll. -*- lexical-binding: t; -*- + +;;; Commentary: +;; Demo for image roll. Contains a major mode for displaying images. + +;;; Code: + +(require 'svg) +(require 'image-roll) + +(defun image-roll-demo-display-page (page) + "Return demo image of PAGE. +This function is used for the `image-roll-demo'." + (let* ((o (image-roll-page-overlay page)) + (s (cdr (overlay-get o 'display))) + (w (car (plist-get s :width))) + (h (car (plist-get s :height))) + (svg (svg-create w h))) + (unless w (print "NO W" #'external-debugging-output)) + (svg-rectangle svg 0 0 w h :fill-color "white") + (svg-text svg + (number-to-string page) + :font-size "40" + :fill "black" + :x 20 + :y 50) + (when image-roll-center + (overlay-put o 'before-string + (when (> (window-pixel-width) w) + (propertize " " 'display + `(space :align-to + (,(floor (/ (- (window-pixel-width) w) 2)))))))) + (overlay-put o 'display (svg-image svg :margin `(0 . ,image-roll-vertical-margin))))) + +(define-derived-mode image-roll-mode special-mode "Image Roll" + ;; we don't use `(image-mode-setup-winprops)' because it would additionally + ;; add `image-mode-reapply-winprops' to the + ;; `window-configuration-change-hook', but `image-roll-redisplay' already + ;; reapplies the vscroll, so we simply initialize the + ;; `image-mode-winprops-alist' here, and add lines from + ;; `image-mode-reapply-winprops' at the start of `image-roll-redisplay'. + (add-hook 'window-size-change-functions 'image-roll-window-size-change-function nil t) + (add-hook 'image-mode-new-window-functions 'image-roll-new-window-function nil t) + (unless (listp image-mode-winprops-alist) + (setq image-mode-winprops-alist nil))) +;; (add-hook 'window-configuration-change-hook +;; #'image-mode-reapply-winprops nil t)) +;; (image-mode-setup-winprops)) + +(setq image-roll-mode-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "C-n") 'image-roll-scroll-forward) + (define-key map (kbd "") 'image-roll-scroll-forward) + (define-key map (kbd "C-p") 'image-roll-scroll-backward) + (define-key map (kbd "") 'image-roll-scroll-backward) + (define-key map (kbd "") 'image-roll-scroll-mouse-wheel) + (define-key map (kbd "") 'image-roll-scroll-mouse-wheel) + (define-key map (kbd "") 'image-roll-scroll-forward) + (define-key map (kbd "") 'image-roll-scroll-backward) + (define-key map "n" 'image-roll-next-page) + (define-key map (kbd "") 'image-roll-next-page) + (define-key map "p" 'image-roll-previous-page) + (define-key map (kbd "") 'image-roll-previous-page) + (define-key map (kbd "S-") 'image-roll-scroll-screen-forward) + (define-key map (kbd "S-") 'image-roll-scroll-screen-backward) + (define-key map [remap goto-line] 'image-roll-goto-page) + map)) + +(defun image-roll-demo (&optional page-size pages) + "Display a demo buffer displaying some images. +PAGE-SIZE is the size of each image and PAGES is the number of images." + (interactive) + (let ((buf-name "*image-roll-demo*")) + ;; (if (get-buffer buf-name) + ;; (switch-to-buffer (current-buffer)) + (with-current-buffer (get-buffer-create buf-name) + (erase-buffer) + (image-roll-mode) + (setq cursor-type nil) + (setq image-roll-step-size 50) + (setq-local image-roll-last-page (or pages 3) + image-roll-display-page-function 'image-roll-demo-display-page + image-roll-demo-page-size (or page-size + (lambda () + (let ((w (window-pixel-width))) + (cons w (* 1.4 w)))))) + + (setq image-roll-center t) + (switch-to-buffer (current-buffer))))) +(provide 'image-roll-mode) + +;;; image-roll-mode.el ends here From 89a41000429f385bc76754f8b9e0cf363c0b671a Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Mon, 19 Jun 2023 15:28:01 +0200 Subject: [PATCH 070/104] Simplify and correct scroll-forward + tweaks --- image-roll.el | 98 ++++++++++++++++++++++++--------------------------- 1 file changed, 46 insertions(+), 52 deletions(-) diff --git a/image-roll.el b/image-roll.el index 88662318..7da577b2 100644 --- a/image-roll.el +++ b/image-roll.el @@ -74,6 +74,9 @@ ;;; Code: (require 'image-mode) +(put 'image-roll 'display '(space :width 25 :height 1000)) +(put 'image-roll 'evaporate t) + ;;; Custom Variables (defgroup image-roll nil "Image roll configurations." @@ -111,6 +114,7 @@ argument (PAGE). The function should use `(image-roll-page-overlay PAGE)' to add the image of the page as the overlay's display-property.") +;;; Utility Macros and inline functions (defmacro image-roll-current-page (&optional window) "Return the page number of the currently displayed page in WINDOW. The current page is the page that overlaps with the window @@ -118,7 +122,6 @@ start (this choice was made in order to simplify the scrolling logic)" `(image-mode-window-get 'page ,window)) -;;; Utility Macros and inline functions (defmacro image-roll-displayed-pages (&optional window) "Return list of pages currently displayed in WINDOW." `(image-mode-window-get 'displayed-pages ,window)) @@ -140,11 +143,6 @@ logic)" (/ (1+ (point)) 2) (error "No page is displayed at current position (%s)" (point)))) -(defsubst image-roll-overlay-height (page &optional window) - "Get the height of overlay displaying PAGE on WINDOW." - (when-let (ov (image-roll-page-overlay page window)) - (+ (cdr (overlay-get ov 'overlay-size)) image-roll-vertical-margin))) - ;;; Helper functions (defun image-roll-undisplay-page (page &optional window) "Undisplay PAGE from WINDOW. @@ -157,7 +155,7 @@ with a space. It size is determined from the image its d (nth 1 d)))) (when (imagep im) - (overlay-put o 'display nil)))) + (overlay-put o 'display (get 'image-roll 'display))))) (defun image-roll-new-window-function (&optional winprops) "Setup image roll in a new window. @@ -179,7 +177,6 @@ overlays." (inhibit-read-only t)) (erase-buffer) - (remove-overlays) ;; here we only add the 'page' and 'window' overlay-properties, we add ;; more properties/information as soon as it becomes available in the @@ -191,11 +188,11 @@ overlays." (overlay-put o 'window win)) (insert "\n")) (delete-char -1) - (goto-char 1) (set-buffer-modified-p nil)) - (dotimes (i (/ (point-max) 2)) - (overlay-put (copy-overlay (car (overlays-at (1+ (* 2 i))))) - 'window (car winprops)))) + (unless (image-roll-page-overlay 1 win) + (dotimes (i (/ (point-max) 2)) + (overlay-put (copy-overlay (car (overlays-at (1+ (* 2 i))))) + 'window win)))) ;; initial `image-roll-redisplay' needs to know which page(s) to display (cl-callf or (image-roll-current-page win) 1))) @@ -222,19 +219,25 @@ If FORCE is non-nill redisplay a page even if it is already displayed." (let (displayed (page (image-roll-page-at-current-pos))) (while (pos-visible-in-window-p (image-roll-page-to-pos page) window t) - (when (or force (not (overlay-get (image-roll-page-overlay page window) 'display))) - (funcall image-roll-display-page-function page window)) + (when (or force (eq (car (overlay-get (image-roll-page-overlay page window) 'display)) + 'space)) + (funcall image-roll-display-page-function page window)) (push page displayed) (cl-incf page)) ;; store displayed images for determining which images to update when update ;; is triggered (cl-callf cl-union (image-mode-window-get 'displayed-pages window) displayed) - ;; store the height of the visible pages for determining when to update - ;; images, namely when some part of the roll outside this range becomes - ;; visible displayed)) -(defun image-roll-window-size-change-function (&optional window _no-relative-vscroll) +(defun image-roll-redisplay (&optional window) + "Analogue of `pdf-view-redisplay' for WINDOW." + (let* ((destination (image-roll-page-to-pos (image-roll-current-page window))) + (no-jumpp (eq destination (point))) + (vscroll (image-mode-window-get 'vscroll window))) + (goto-char destination) + (image-roll--goto-point (if no-jumpp vscroll 0) window t no-jumpp))) + +(defun image-roll-window-size-change-function (&optional window) "Redisplay the scroll in WINDOW. Besides that this function can be called directly, it should also be added to the `window-size-change-functions'. It is a substitute for the @@ -249,17 +252,13 @@ be added to the `window-size-change-functions'. It is a substitute for the (if (and (> (point-max) 1) (equal (buffer-substring (point-min) (1+ (point-min))) "%")) (image-roll-new-window-function `(,window)) (image-mode-winprops window t)) - ;; Determine to display pages and display. Undisplay pages is not necessary as - ;; this is taken care off by `image-roll-update-displayed-pages'. - (image-roll-display-pages window t) - ;; we only need to jump to the right page, the vscroll is conserved and if - ;; required can be set to 0 before the redisplay - (image-roll-window-configuration-change-hook window))) - -(defun image-roll-update-displayed-pages (&optional window) - "Update the pages displayed in WINDOW." + (image-roll-redisplay window))) + +(defun image-roll-update-displayed-pages (&optional window force) + "Update the pages displayed in WINDOW. +When FORCE is non-nil redisplay even the already displayed pages." (let ((old (image-mode-window-get 'displayed-pages window)) - (new (image-roll-display-pages window))) + (new (image-roll-display-pages window force))) ;; dolist because if images/pages are small enough (or after jumps), there ;; might be multiple image that need to get updated (dolist (p (cl-set-difference old new)) @@ -269,16 +268,15 @@ be added to the `window-size-change-functions'. It is a substitute for the (image-mode-window-put 'displayed-pages new window) new)) -(defun image-roll--goto-point (vscroll &optional window redisplay) +(defun image-roll--goto-point (vscroll &optional window force no-run-hooks) "Make page at point the current page with VSCROLL in WINDOW. -If REDISPLAY is non-nil perform redisplay for `set-window-start' to take effect." +When FORCE is non-nil redisplay even the already displayed pages. +If NO-RUN-HOOKS is non-nil don't run `image-roll-after-change-page-hook'." (set-window-start window (point) t) - (image-roll-set-vscroll vscroll window) - (image-roll-update-displayed-pages window) - (when redisplay - (redisplay) - (message "%S" (window-start))) - (run-hooks 'image-roll-after-change-page-hook)) + (when vscroll (image-roll-set-vscroll vscroll window)) + (image-roll-update-displayed-pages window force) + (unless no-run-hooks (run-hooks 'image-roll-after-change-page-hook)) + (set-window-start window (point) t)) ;;; Page navigation commands (defun image-roll-goto-page-start () @@ -299,7 +297,7 @@ If REDISPLAY is non-nil perform redisplay for `set-window-start' to take effect. (if (eq page (image-roll-page-at-current-pos)) (image-roll-set-vscroll 0 window) (goto-char (image-roll-page-to-pos page)) - (image-roll--goto-point 0 window t))) + (image-roll--goto-point 0 window))) (defun image-roll-next-page (&optional n) "Go to next page or next Nth page." @@ -323,23 +321,19 @@ With a prefix arg PIXELS is the numeric value times `image-roll-step-size'." (setq pixels (or pixels image-roll-step-size)) (when (> 0 pixels) (image-roll-scroll-backward (- pixels) window)) (let ((pos (point))) - (while (when-let ((data (pos-visible-in-window-p (point) window t)) - (occupied-pixels (if (nth 2 data) - (nth 4 data) - (line-pixel-height)))) + (while (let* ((data (pos-visible-in-window-p (point) window t)) + (occupied-pixels (cond ((nth 2 data) (nth 4 data)) + (data (line-pixel-height)) + (t (funcall image-roll-display-page-function + (image-roll-page-at-current-pos) window) + (line-pixel-height))))) (and (> pixels occupied-pixels) - (cl-decf pixels occupied-pixels))) + (if (eq (point) (1- (point-max))) + (prog1 nil + (setq pixels (- occupied-pixels 10)) + (message "End of buffer")) + (cl-decf pixels occupied-pixels)))) (forward-char 2)) - (unless (pos-visible-in-window-p (point) window t) - (while (and (if (eobp) - (prog1 nil (message "End of buffer.")) - t) - (progn (funcall image-roll-display-page-function - (image-roll-page-at-current-pos) window) - (let ((height (line-pixel-height))) - (when (> pixels height) - (cl-decf pixels height))))) - (forward-char 2))) (if (eq pos (point)) (progn (image-roll-set-vscroll (+ (window-vscroll window t) pixels) window) From bda191b7043dcbee1152869aef48916a084b5753 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Mon, 19 Jun 2023 22:24:04 +0200 Subject: [PATCH 071/104] Rely on pre-display-functions --- image-roll.el | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/image-roll.el b/image-roll.el index 7da577b2..85c0cdb4 100644 --- a/image-roll.el +++ b/image-roll.el @@ -114,6 +114,10 @@ argument (PAGE). The function should use `(image-roll-page-overlay PAGE)' to add the image of the page as the overlay's display-property.") +;;; Other Variables +(defvar-local image-roll--last-state nil + "Local variable that tracks window, point and vscroll to handle changes.") + ;;; Utility Macros and inline functions (defmacro image-roll-current-page (&optional window) "Return the page number of the currently displayed page in WINDOW. @@ -194,7 +198,10 @@ overlays." (overlay-put (copy-overlay (car (overlays-at (1+ (* 2 i))))) 'window win)))) ;; initial `image-roll-redisplay' needs to know which page(s) to display - (cl-callf or (image-roll-current-page win) 1))) + (unless image-roll--last-state + (setq image-roll--last-state (list t))) + (cl-callf or (image-roll-current-page win) 1) + (image-roll-redisplay))) (defun image-roll-set-vscroll (vscroll win) "Set vscroll to VSCROLL in window WIN." @@ -231,28 +238,21 @@ If FORCE is non-nill redisplay a page even if it is already displayed." (defun image-roll-redisplay (&optional window) "Analogue of `pdf-view-redisplay' for WINDOW." - (let* ((destination (image-roll-page-to-pos (image-roll-current-page window))) - (no-jumpp (eq destination (point))) - (vscroll (image-mode-window-get 'vscroll window))) - (goto-char destination) - (image-roll--goto-point (if no-jumpp vscroll 0) window t no-jumpp))) - -(defun image-roll-window-size-change-function (&optional window) - "Redisplay the scroll in WINDOW. -Besides that this function can be called directly, it should also -be added to the `window-size-change-functions'. It is a substitute for the -`pdf-view-redisplay' function." - - ;; Beware: this call to image-mode-winprops can't be optimized away, because - ;; it not only gets the winprops data but sets it up if needed (e.g. it's used - ;; by doc-view to display the image in a new window). - (setq window (if (window-live-p window) window (selected-window))) - (when (and (memq 'image-roll-new-window-function image-mode-new-window-functions) - (eq (current-buffer) (window-buffer window))) - (if (and (> (point-max) 1) (equal (buffer-substring (point-min) (1+ (point-min))) "%")) - (image-roll-new-window-function `(,window)) - (image-mode-winprops window t)) - (image-roll-redisplay window))) + (goto-char (image-roll-page-to-pos (image-roll-current-page window)))) + +(defun image-roll-pre-redisplay (win) + "Handle modifications to the state in window WIN. +It should be added to `pre-redisplay-functions' buffer locally." + (when image-roll--last-state + (let* ((state (alist-get win image-roll--last-state)) + (size-changed (not (and (eq (window-pixel-height win) (nth 1 state)) + (eq (window-pixel-width win) (nth 2 state))))) + (point-changed (not (eq (point) (nth 0 state)))) + (vscroll (image-mode-window-get 'vscroll win))) + (when (or size-changed point-changed) + (setf (alist-get win image-roll--last-state) + `(,(point) ,(window-pixel-height win) ,(window-pixel-width win))) + (image-roll--goto-point vscroll win size-changed point-changed))))) (defun image-roll-update-displayed-pages (&optional window force) "Update the pages displayed in WINDOW. From 08b21e1d3884c91e34820e5ffc02fce9b453f39b Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Tue, 20 Jun 2023 01:45:02 +0200 Subject: [PATCH 072/104] More work on pre-display method --- image-roll.el | 69 +++++++++++++++++++++++---------------------------- 1 file changed, 31 insertions(+), 38 deletions(-) diff --git a/image-roll.el b/image-roll.el index 85c0cdb4..e46d9b17 100644 --- a/image-roll.el +++ b/image-roll.el @@ -175,13 +175,13 @@ WINPROPS are the initial window properties. This function should be added to image-roll (continuous scroll) minor mode commands, after erasing the buffer to create the overlays." - (let ((win (or (and (windowp (car winprops)) (car winprops)) (selected-window)))) + (let ((win (or (and (windowp winprops) winprops) (selected-window)))) (if (not (overlays-at 1)) (let ((pages image-roll-last-page) (inhibit-read-only t)) (erase-buffer) - + (setq image-roll--last-state (list t)) ;; here we only add the 'page' and 'window' overlay-properties, we add ;; more properties/information as soon as it becomes available in the ;; 'image-roll-redisplay' function @@ -198,10 +198,9 @@ overlays." (overlay-put (copy-overlay (car (overlays-at (1+ (* 2 i))))) 'window win)))) ;; initial `image-roll-redisplay' needs to know which page(s) to display - (unless image-roll--last-state - (setq image-roll--last-state (list t))) (cl-callf or (image-roll-current-page win) 1) - (image-roll-redisplay))) + (cl-callf or (image-mode-window-get 'vscroll win) 0) + (image-roll-redisplay win))) (defun image-roll-set-vscroll (vscroll win) "Set vscroll to VSCROLL in window WIN." @@ -238,21 +237,29 @@ If FORCE is non-nill redisplay a page even if it is already displayed." (defun image-roll-redisplay (&optional window) "Analogue of `pdf-view-redisplay' for WINDOW." - (goto-char (image-roll-page-to-pos (image-roll-current-page window)))) + (setq window (if (windowp window) window (selected-window))) + (goto-char (image-roll-page-to-pos (image-roll-current-page window))) + (image-roll-update-displayed-pages window t)) (defun image-roll-pre-redisplay (win) "Handle modifications to the state in window WIN. It should be added to `pre-redisplay-functions' buffer locally." - (when image-roll--last-state - (let* ((state (alist-get win image-roll--last-state)) - (size-changed (not (and (eq (window-pixel-height win) (nth 1 state)) - (eq (window-pixel-width win) (nth 2 state))))) - (point-changed (not (eq (point) (nth 0 state)))) - (vscroll (image-mode-window-get 'vscroll win))) - (when (or size-changed point-changed) - (setf (alist-get win image-roll--last-state) - `(,(point) ,(window-pixel-height win) ,(window-pixel-width win))) - (image-roll--goto-point vscroll win size-changed point-changed))))) + (with-demoted-errors "Error in image roll pre-display: %S" + (when image-roll--last-state + (image-roll-set-vscroll (image-mode-window-get 'vscroll win) win) + (let* ((state (alist-get win image-roll--last-state)) + (size-changed (not (and (eq (window-pixel-height win) (nth 1 state)) + (eq (window-pixel-width win) (nth 2 state))))) + (point-changed (not (eq (point) (nth 0 state)))) + (vscroll-changed (not (eq (window-vscroll nil t) (nth 3 state))))) + (setq disable-point-adjustment t) + (unless (and state (overlays-at 1)) (image-roll-new-window-function win)) + (when (or size-changed point-changed vscroll-changed) + (setf (alist-get win image-roll--last-state) + `(,(point) ,(window-pixel-height win) ,(window-pixel-width win))) + (set-window-start win (point) t) + (image-roll-update-displayed-pages win size-changed) + (when point-changed (run-hooks 'image-roll-after-change-page-hook))))))) (defun image-roll-update-displayed-pages (&optional window force) "Update the pages displayed in WINDOW. @@ -266,17 +273,7 @@ When FORCE is non-nil redisplay even the already displayed pages." ;; setting/appending new below (setf (image-roll-current-page window) (image-roll-page-at-current-pos)) (image-mode-window-put 'displayed-pages new window) - new)) - -(defun image-roll--goto-point (vscroll &optional window force no-run-hooks) - "Make page at point the current page with VSCROLL in WINDOW. -When FORCE is non-nil redisplay even the already displayed pages. -If NO-RUN-HOOKS is non-nil don't run `image-roll-after-change-page-hook'." - (set-window-start window (point) t) - (when vscroll (image-roll-set-vscroll vscroll window)) - (image-roll-update-displayed-pages window force) - (unless no-run-hooks (run-hooks 'image-roll-after-change-page-hook)) - (set-window-start window (point) t)) + (cl-set-difference new old))) ;;; Page navigation commands (defun image-roll-goto-page-start () @@ -294,10 +291,8 @@ If NO-RUN-HOOKS is non-nil don't run `image-roll-after-change-page-hook'." (unless (and (>= page 1) (<= page image-roll-last-page)) (error "No such page: %d" page)) - (if (eq page (image-roll-page-at-current-pos)) - (image-roll-set-vscroll 0 window) - (goto-char (image-roll-page-to-pos page)) - (image-roll--goto-point 0 window))) + (goto-char (image-roll-page-to-pos page)) + (image-roll-set-vscroll 0 window)) (defun image-roll-next-page (&optional n) "Go to next page or next Nth page." @@ -335,10 +330,9 @@ With a prefix arg PIXELS is the numeric value times `image-roll-step-size'." (cl-decf pixels occupied-pixels)))) (forward-char 2)) (if (eq pos (point)) - (progn (image-roll-set-vscroll (+ (window-vscroll window t) pixels) - window) - (image-roll-update-displayed-pages window)) - (image-roll--goto-point pixels window)))) + (image-roll-set-vscroll (+ (window-vscroll window t) pixels) + window) + (image-roll-set-vscroll pixels window)))) (defun image-roll-scroll-backward (&optional pixels window) "Scroll image PIXELS backwards in WINDOW. @@ -352,9 +346,8 @@ With a prefix arg PIXELS is the numeric value times `image-roll-step-size'." (let* ((data (pos-visible-in-window-p (point) window t)) (pixels-top (if (nth 2 data) (nth 2 data) 0))) (if (< pixels pixels-top) - (progn (image-roll-set-vscroll (- (window-vscroll window t) pixels) + (image-roll-set-vscroll (- (window-vscroll window t) pixels) window) - (image-roll-update-displayed-pages window)) (cl-decf pixels pixels-top) (while (and (if (bobp) (prog1 nil (message "Beginning of buffer.")) @@ -364,7 +357,7 @@ With a prefix arg PIXELS is the numeric value times `image-roll-step-size'." (image-roll-page-at-current-pos) window) (cl-decf pixels (line-pixel-height))) (> pixels 0))) - (image-roll--goto-point (- pixels) window)))) + (image-roll-set-vscroll (- pixels) window)))) (defun image-roll-scroll-screen-forward (&optional arg) "Scroll forward by (almost) ARG many full screens." From 626f607eab99de8247b10b081573436a13499990 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Tue, 20 Jun 2023 13:43:40 +0200 Subject: [PATCH 073/104] Remove unneeded code from undisplay + more tweaks --- image-roll.el | 52 +++++++++++++++++++-------------------------------- 1 file changed, 19 insertions(+), 33 deletions(-) diff --git a/image-roll.el b/image-roll.el index e46d9b17..dea0f5b3 100644 --- a/image-roll.el +++ b/image-roll.el @@ -148,18 +148,12 @@ logic)" (error "No page is displayed at current position (%s)" (point)))) ;;; Helper functions -(defun image-roll-undisplay-page (page &optional window) - "Undisplay PAGE from WINDOW. -Replaces the image display property of the overlay holding PAGE -with a space. It size is determined from the image its -`image-size'." - (let* ((o (image-roll-page-overlay page window)) - (d (overlay-get o 'display)) - (im (if (imagep d) - d - (nth 1 d)))) - (when (imagep im) - (overlay-put o 'display (get 'image-roll 'display))))) +(defun image-roll-undisplay-pages (pages &optional window) + "Undisplay PAGES from WINDOW. +Replaces the display property of the overlay holding a page with a space." + (dolist (page pages) + (overlay-put (image-roll-page-overlay page window) + 'display (get 'image-roll 'display)))) (defun image-roll-new-window-function (&optional winprops) "Setup image roll in a new window. @@ -200,7 +194,7 @@ overlays." ;; initial `image-roll-redisplay' needs to know which page(s) to display (cl-callf or (image-roll-current-page win) 1) (cl-callf or (image-mode-window-get 'vscroll win) 0) - (image-roll-redisplay win))) + (goto-char (image-roll-page-to-pos (image-roll-current-page win))))) (defun image-roll-set-vscroll (vscroll win) "Set vscroll to VSCROLL in window WIN." @@ -212,13 +206,6 @@ overlays." (image-mode-window-put 'hscroll hscroll win) (set-window-hscroll win hscroll)) -(defun image-roll-window-configuration-change-hook (&optional win) - "Handle state change for window WIN. -It should be added to `window-configuration-change-hook' buffer locally." - (when (memq 'image-roll-new-window-function image-mode-new-window-functions) - (image-roll-set-vscroll (or (image-mode-window-get 'vscroll win) 0) win) - (image-roll-set-hscroll (or (image-mode-window-get 'hscroll) 0) win))) - (defun image-roll-display-pages (&optional window force) "Display pages to fill the WINDOW. If FORCE is non-nill redisplay a page even if it is already displayed." @@ -237,9 +224,10 @@ If FORCE is non-nill redisplay a page even if it is already displayed." (defun image-roll-redisplay (&optional window) "Analogue of `pdf-view-redisplay' for WINDOW." - (setq window (if (windowp window) window (selected-window))) - (goto-char (image-roll-page-to-pos (image-roll-current-page window))) - (image-roll-update-displayed-pages window t)) + (when (image-roll-page-overlay 1 window) + (setq window (if (windowp window) window (selected-window))) + (goto-char (image-roll-page-to-pos (image-roll-current-page window))) + (image-roll-update-displayed-pages window t))) (defun image-roll-pre-redisplay (win) "Handle modifications to the state in window WIN. @@ -253,10 +241,12 @@ It should be added to `pre-redisplay-functions' buffer locally." (point-changed (not (eq (point) (nth 0 state)))) (vscroll-changed (not (eq (window-vscroll nil t) (nth 3 state))))) (setq disable-point-adjustment t) - (unless (and state (overlays-at 1)) (image-roll-new-window-function win)) + (unless (image-roll-page-overlay 1 win) + (image-roll-new-window-function win)) (when (or size-changed point-changed vscroll-changed) (setf (alist-get win image-roll--last-state) - `(,(point) ,(window-pixel-height win) ,(window-pixel-width win))) + `(,(point) ,(window-pixel-height win) ,(window-pixel-width win) + ,(window-vscroll nil t))) (set-window-start win (point) t) (image-roll-update-displayed-pages win size-changed) (when point-changed (run-hooks 'image-roll-after-change-page-hook))))))) @@ -266,11 +256,9 @@ It should be added to `pre-redisplay-functions' buffer locally." When FORCE is non-nil redisplay even the already displayed pages." (let ((old (image-mode-window-get 'displayed-pages window)) (new (image-roll-display-pages window force))) - ;; dolist because if images/pages are small enough (or after jumps), there + ;; If images/pages are small enough (or after jumps), there ;; might be multiple image that need to get updated - (dolist (p (cl-set-difference old new)) - (image-roll-undisplay-page p window)) - ;; setting/appending new below + (image-roll-undisplay-pages (cl-set-difference old new)) (setf (image-roll-current-page window) (image-roll-page-at-current-pos)) (image-mode-window-put 'displayed-pages new window) (cl-set-difference new old))) @@ -329,10 +317,8 @@ With a prefix arg PIXELS is the numeric value times `image-roll-step-size'." (message "End of buffer")) (cl-decf pixels occupied-pixels)))) (forward-char 2)) - (if (eq pos (point)) - (image-roll-set-vscroll (+ (window-vscroll window t) pixels) - window) - (image-roll-set-vscroll pixels window)))) + (image-roll-set-vscroll (+ (if (eq pos (point)) (window-vscroll window t) 0) pixels) + window))) (defun image-roll-scroll-backward (&optional pixels window) "Scroll image PIXELS backwards in WINDOW. From 6a95a7445ca33c9d43d076ea2f3fbc8f5422a84b Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Wed, 21 Jun 2023 00:23:16 +0200 Subject: [PATCH 074/104] Get rid of overlays for dead windows --- image-roll.el | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/image-roll.el b/image-roll.el index dea0f5b3..8582a4e9 100644 --- a/image-roll.el +++ b/image-roll.el @@ -188,9 +188,14 @@ overlays." (delete-char -1) (set-buffer-modified-p nil)) (unless (image-roll-page-overlay 1 win) - (dotimes (i (/ (point-max) 2)) - (overlay-put (copy-overlay (car (overlays-at (1+ (* 2 i))))) - 'window win)))) + (dotimes (i (/ (point-max) 2)) + (overlay-put (copy-overlay (car (overlays-at (1+ (* 2 i))))) + 'window win)) + (dolist (win-st image-roll--last-state) + (when-let ((win-old (car-safe win-st)) + ((not (window-live-p win-old)))) + (remove-overlays (point-min) (point-max) 'window win-old))) + (cl-callf2 cl-delete-if-not #'window-live-p image-roll--last-state :key #'car-safe))) ;; initial `image-roll-redisplay' needs to know which page(s) to display (cl-callf or (image-roll-current-page win) 1) (cl-callf or (image-mode-window-get 'vscroll win) 0) @@ -233,7 +238,7 @@ If FORCE is non-nill redisplay a page even if it is already displayed." "Handle modifications to the state in window WIN. It should be added to `pre-redisplay-functions' buffer locally." (with-demoted-errors "Error in image roll pre-display: %S" - (when image-roll--last-state + (when (eq win (get-buffer-window)) (image-roll-set-vscroll (image-mode-window-get 'vscroll win) win) (let* ((state (alist-get win image-roll--last-state)) (size-changed (not (and (eq (window-pixel-height win) (nth 1 state)) @@ -258,7 +263,7 @@ When FORCE is non-nil redisplay even the already displayed pages." (new (image-roll-display-pages window force))) ;; If images/pages are small enough (or after jumps), there ;; might be multiple image that need to get updated - (image-roll-undisplay-pages (cl-set-difference old new)) + (image-roll-undisplay-pages (cl-set-difference old new) window) (setf (image-roll-current-page window) (image-roll-page-at-current-pos)) (image-mode-window-put 'displayed-pages new window) (cl-set-difference new old))) From c814210591937722d820a7d50578aa134e415b27 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Wed, 21 Jun 2023 18:42:01 +0200 Subject: [PATCH 075/104] Pass window argument + fix arithmetic condition There was a bug where the pages won't display because of not not passing the window argument. There was was also the problem that page would fail to render if the remaining portion was exactly equal to step-size. Lastly do work in pre-display whenever possible. So in `image-mode-redisplay` we just mark window for redisplay by calling `force-window-update` --- image-roll.el | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/image-roll.el b/image-roll.el index 8582a4e9..f4e63965 100644 --- a/image-roll.el +++ b/image-roll.el @@ -152,8 +152,8 @@ logic)" "Undisplay PAGES from WINDOW. Replaces the display property of the overlay holding a page with a space." (dolist (page pages) - (overlay-put (image-roll-page-overlay page window) - 'display (get 'image-roll 'display)))) + (overlay-put (image-roll-page-overlay page window) + 'display (get 'image-roll 'display)))) (defun image-roll-new-window-function (&optional winprops) "Setup image roll in a new window. @@ -229,32 +229,32 @@ If FORCE is non-nill redisplay a page even if it is already displayed." (defun image-roll-redisplay (&optional window) "Analogue of `pdf-view-redisplay' for WINDOW." + (setq window (if (windowp window) window (selected-window))) (when (image-roll-page-overlay 1 window) - (setq window (if (windowp window) window (selected-window))) (goto-char (image-roll-page-to-pos (image-roll-current-page window))) - (image-roll-update-displayed-pages window t))) + (setf (nth 1 (alist-get window image-roll--last-state)) nil) + (force-window-update window))) (defun image-roll-pre-redisplay (win) "Handle modifications to the state in window WIN. It should be added to `pre-redisplay-functions' buffer locally." (with-demoted-errors "Error in image roll pre-display: %S" - (when (eq win (get-buffer-window)) - (image-roll-set-vscroll (image-mode-window-get 'vscroll win) win) - (let* ((state (alist-get win image-roll--last-state)) - (size-changed (not (and (eq (window-pixel-height win) (nth 1 state)) - (eq (window-pixel-width win) (nth 2 state))))) - (point-changed (not (eq (point) (nth 0 state)))) - (vscroll-changed (not (eq (window-vscroll nil t) (nth 3 state))))) - (setq disable-point-adjustment t) - (unless (image-roll-page-overlay 1 win) - (image-roll-new-window-function win)) - (when (or size-changed point-changed vscroll-changed) - (setf (alist-get win image-roll--last-state) - `(,(point) ,(window-pixel-height win) ,(window-pixel-width win) - ,(window-vscroll nil t))) - (set-window-start win (point) t) - (image-roll-update-displayed-pages win size-changed) - (when point-changed (run-hooks 'image-roll-after-change-page-hook))))))) + (image-roll-set-vscroll (image-mode-window-get 'vscroll win) win) + (let* ((state (alist-get win image-roll--last-state)) + (size-changed (not (and (eq (window-pixel-height win) (nth 1 state)) + (eq (window-pixel-width win) (nth 2 state))))) + (point-changed (not (eq (point) (nth 0 state)))) + (vscroll-changed (not (eq (window-vscroll win t) (nth 3 state))))) + (setq disable-point-adjustment t) + (unless (image-roll-page-overlay 1 win) + (image-roll-new-window-function win)) + (when (or size-changed point-changed vscroll-changed) + (setf (alist-get win image-roll--last-state) + `(,(point) ,(window-pixel-height win) ,(window-pixel-width win) + ,(window-vscroll win t))) + (set-window-start win (point) t) + (image-roll-update-displayed-pages win size-changed) + (when point-changed (run-hooks 'image-roll-after-change-page-hook)))))) (defun image-roll-update-displayed-pages (&optional window force) "Update the pages displayed in WINDOW. @@ -315,7 +315,7 @@ With a prefix arg PIXELS is the numeric value times `image-roll-step-size'." (t (funcall image-roll-display-page-function (image-roll-page-at-current-pos) window) (line-pixel-height))))) - (and (> pixels occupied-pixels) + (and (>= pixels occupied-pixels) (if (eq (point) (1- (point-max))) (prog1 nil (setq pixels (- occupied-pixels 10)) From 4eaac76356e887d3a4d429dfa5955dceec06abd8 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Sun, 25 Jun 2023 14:48:50 +0200 Subject: [PATCH 076/104] Use overlays for adding margin This allows to scroll by leaving the point on the line for a margin. To ensure margins are thin enough, the default face is remapped to one with a 1 pixel sized font. Also clamp the vscroll whenever the size of window or the first image changes. --- image-roll.el | 267 +++++++++++++++++++++++++++----------------------- 1 file changed, 142 insertions(+), 125 deletions(-) diff --git a/image-roll.el b/image-roll.el index f4e63965..82e7c648 100644 --- a/image-roll.el +++ b/image-roll.el @@ -76,21 +76,24 @@ (put 'image-roll 'display '(space :width 25 :height 1000)) (put 'image-roll 'evaporate t) +(put 'image-roll-margin 'evaporate t) ;;; Custom Variables (defgroup image-roll nil "Image roll configurations." - :group 'applications - :version "28.1") + :group 'applications) + +(defface image-roll-default `((t :font ,(font-spec :family "monospace" :size 1))) + "Default face for image roll documents.") (defcustom image-roll-vertical-margin 2 "Vertical margin between images in pixels, i.e. page separation height." :type 'integer) -(defcustom image-roll-overlay-face-bg-color "gray" +(defcustom image-roll-margin-color "gray" "Background color of overlay, i.e. page separation color." :type 'color - :set (lambda (_ color) (put 'image-roll 'face `(:background ,color)))) + :set (lambda (_ color) (put 'image-roll-margin 'face `(:background ,color)))) (defcustom image-roll-step-size 50 "Scroll step size in pixels units." @@ -107,18 +110,16 @@ ;;; Local variables to be set by implementations (defvar-local image-roll-last-page 0) -(defvar-local image-roll-display-page-function nil - "Function that sets the overlay's display property. -The function receives the page number as a single -argument (PAGE). The function should use `(image-roll-page-overlay -PAGE)' to add the image of the page as the overlay's -display-property.") +(defvar-local image-roll-page-image-function nil + "Function of two arguments that retrieves the image for a page. +The first argument is the page number and the second argument is the window in +which the page will be displayed. There return value is an image spec.") ;;; Other Variables -(defvar-local image-roll--last-state nil +(defvar-local image-roll--state nil "Local variable that tracks window, point and vscroll to handle changes.") -;;; Utility Macros and inline functions +;;; Utility Macros and functions (defmacro image-roll-current-page (&optional window) "Return the page number of the currently displayed page in WINDOW. The current page is the page that overlaps with the window @@ -126,37 +127,82 @@ start (this choice was made in order to simplify the scrolling logic)" `(image-mode-window-get 'page ,window)) -(defmacro image-roll-displayed-pages (&optional window) - "Return list of pages currently displayed in WINDOW." - `(image-mode-window-get 'displayed-pages ,window)) - (defsubst image-roll-page-to-pos (page) "Get the buffer position displaing PAGE." - (1- (* 2 page))) + (- (* 4 page) 3)) + +(defun image-roll--pos-overlay (pos window) + "Return an overlay for WINDOW at POS." + (cl-find window (overlays-at pos) :key (lambda (ov) (overlay-get ov 'window)))) (defun image-roll-page-overlay (&optional page window) "Return overlay displaying PAGE in WINDOW." - (cl-find (or window (selected-window)) - (overlays-at (image-roll-page-to-pos - (or page (image-roll-page-at-current-pos)))) - :key (lambda (ov) (overlay-get ov 'window)))) + (image-roll--pos-overlay + (image-roll-page-to-pos (or page (image-roll-page-at-current-pos))) + (or window (selected-window)))) (defun image-roll-page-at-current-pos () "Page at point." (if (cl-oddp (point)) - (/ (1+ (point)) 2) + (/ (+ (point) 3) 4) (error "No page is displayed at current position (%s)" (point)))) -;;; Helper functions +(defun image-roll-set-vscroll (vscroll win) + "Set vscroll to VSCROLL in window WIN." + (image-mode-window-put 'vscroll vscroll win) + (set-window-vscroll win vscroll t t)) + +;;; Displaying/Undisplaying pages +(defun image-roll-display-image (image page window) + "Display IMAGE for PAGE in WINDOW." + (let* ((size (image-display-size image t)) + (overlay (image-roll-page-overlay page window)) + (margin-pos (+ (image-roll-page-to-pos page) 2)) + (margin-overlay (image-roll--pos-overlay margin-pos window)) + (align-to (when (> (window-width window t) (car size)) + (/ (- (window-width window t) (car size)) 2))) + (before-string (when align-to (propertize " " 'display `(space :align-to (,align-to)))))) + (overlay-put overlay 'display image) + (overlay-put overlay 'before-string before-string) + (overlay-put margin-overlay 'display `(space :width (,(car size)) :height (,image-roll-vertical-margin))) + (overlay-put margin-overlay 'before-string before-string) + (cdr size))) + +(defun image-roll-display-page (page window &optional force) + "Display PAGE in WINDOW. +With FORCE non-nil display fetch page again even if it is already displayed." + (if-let ((display (overlay-get (image-roll-page-overlay page window) 'display)) + ((or force (eq (car display) 'space)))) + (image-roll-display-image (funcall image-roll-page-image-function page window) + page window) + (cdr (image-display-size display t)))) + +(defun image-roll-display-pages (&optional window force) + "Display pages to fill the WINDOW. +If FORCE is non-nill redisplay a page even if it is already displayed." + (let (displayed + (page (image-roll-page-at-current-pos))) + (image-roll-set-vscroll (min (image-mode-window-get 'vscroll window) + (1- (image-roll-display-page page window force))) + window) + (push page displayed) + (while (pos-visible-in-window-p (image-roll-page-to-pos (cl-incf page)) window t) + (image-roll-display-page page window force) + (push page displayed)) + ;; store displayed images for determining which images to update when update + ;; is triggered + (cl-callf cl-union (image-mode-window-get 'displayed-pages window) displayed) + displayed)) + (defun image-roll-undisplay-pages (pages &optional window) "Undisplay PAGES from WINDOW. Replaces the display property of the overlay holding a page with a space." (dolist (page pages) (overlay-put (image-roll-page-overlay page window) 'display (get 'image-roll 'display)))) - -(defun image-roll-new-window-function (&optional winprops) - "Setup image roll in a new window. +;;; State Management +(defun image-roll-new-window-function (&optional win) + "Setup image roll in a new window WIN. If the buffer is newly created, then it does not contain any overlay and this function erases the buffer contents, after which it inserts empty spaces that each hold a overlay. If the buffer @@ -164,109 +210,79 @@ already has overlays (i.e. a second or subsequent window is created), the function simply copies the overlays and adds the new window as window overlay-property to each overlay. -WINPROPS are the initial window properties. - This function should be added to image-roll (continuous scroll) minor mode commands, after erasing the buffer to create the overlays." - (let ((win (or (and (windowp winprops) winprops) (selected-window)))) - (if (not (overlays-at 1)) - (let ((pages image-roll-last-page) - (inhibit-read-only t)) - - (erase-buffer) - (setq image-roll--last-state (list t)) - ;; here we only add the 'page' and 'window' overlay-properties, we add - ;; more properties/information as soon as it becomes available in the - ;; 'image-roll-redisplay' function - (dotimes (_ pages) - (insert " ") - (let ((o (make-overlay (1- (point)) (point)))) - (overlay-put o 'category 'image-roll) - (overlay-put o 'window win)) - (insert "\n")) - (delete-char -1) - (set-buffer-modified-p nil)) - (unless (image-roll-page-overlay 1 win) - (dotimes (i (/ (point-max) 2)) - (overlay-put (copy-overlay (car (overlays-at (1+ (* 2 i))))) - 'window win)) - (dolist (win-st image-roll--last-state) - (when-let ((win-old (car-safe win-st)) - ((not (window-live-p win-old)))) - (remove-overlays (point-min) (point-max) 'window win-old))) - (cl-callf2 cl-delete-if-not #'window-live-p image-roll--last-state :key #'car-safe))) - ;; initial `image-roll-redisplay' needs to know which page(s) to display - (cl-callf or (image-roll-current-page win) 1) - (cl-callf or (image-mode-window-get 'vscroll win) 0) - (goto-char (image-roll-page-to-pos (image-roll-current-page win))))) - -(defun image-roll-set-vscroll (vscroll win) - "Set vscroll to VSCROLL in window WIN." - (image-mode-window-put 'vscroll vscroll win) - (set-window-vscroll win vscroll t t)) - -(defun image-roll-set-hscroll (hscroll win) - "Set hscroll to HSCROLL in window WIN." - (image-mode-window-put 'hscroll hscroll win) - (set-window-hscroll win hscroll)) - -(defun image-roll-display-pages (&optional window force) - "Display pages to fill the WINDOW. -If FORCE is non-nill redisplay a page even if it is already displayed." - (let (displayed - (page (image-roll-page-at-current-pos))) - (while (pos-visible-in-window-p (image-roll-page-to-pos page) window t) - (when (or force (eq (car (overlay-get (image-roll-page-overlay page window) 'display)) - 'space)) - (funcall image-roll-display-page-function page window)) - (push page displayed) - (cl-incf page)) - ;; store displayed images for determining which images to update when update - ;; is triggered - (cl-callf cl-union (image-mode-window-get 'displayed-pages window) displayed) - displayed)) + (setq win (or (and (windowp win) win) (selected-window))) + (if (not (overlays-at 1)) + (let ((pages image-roll-last-page) + (inhibit-read-only t)) + (erase-buffer) + (setq image-roll--state (list t)) + (dotimes (i (* 2 pages)) + (insert " ") + (let ((o (make-overlay (1- (point)) (point)))) + (overlay-put o 'category (if (eq 0 (mod i 2)) 'image-roll 'image-roll-margin)) + (overlay-put o 'window win)) + (insert "\n")) + (delete-char -1) + (set-buffer-modified-p nil)) + (unless (image-roll-page-overlay 1 win) + (dotimes (i (/ (point-max) 2)) + (overlay-put (copy-overlay (car (overlays-at (1+ (* 2 i))))) + 'window win)) + (dolist (win-st image-roll--state) + (when-let ((win-old (car-safe win-st)) + ((not (window-live-p win-old)))) + (remove-overlays (point-min) (point-max) 'window win-old))) + (cl-callf2 cl-delete-if-not #'window-live-p image-roll--state :key #'car-safe))) + ;; initial `image-roll-redisplay' needs to know which page(s) to display + (cl-callf or (image-roll-current-page win) 1) + (cl-callf or (image-mode-window-get 'vscroll win) 0)) (defun image-roll-redisplay (&optional window) "Analogue of `pdf-view-redisplay' for WINDOW." (setq window (if (windowp window) window (selected-window))) (when (image-roll-page-overlay 1 window) - (goto-char (image-roll-page-to-pos (image-roll-current-page window))) - (setf (nth 1 (alist-get window image-roll--last-state)) nil) + (setf (alist-get window image-roll--state) nil) + (set-window-start window (goto-char (image-roll-page-to-pos + (image-roll-current-page window))) t) (force-window-update window))) (defun image-roll-pre-redisplay (win) "Handle modifications to the state in window WIN. It should be added to `pre-redisplay-functions' buffer locally." - (with-demoted-errors "Error in image roll pre-display: %S" - (image-roll-set-vscroll (image-mode-window-get 'vscroll win) win) - (let* ((state (alist-get win image-roll--last-state)) - (size-changed (not (and (eq (window-pixel-height win) (nth 1 state)) + (with-demoted-errors "Error in image roll pre-redisplay: %S" + (let* ((state (alist-get win image-roll--state)) + (height (window-pixel-height win)) + (page (image-roll-current-page win)) + (vscroll (image-mode-window-get 'vscroll win)) + (size-changed (not (and (eq height (nth 1 state)) (eq (window-pixel-width win) (nth 2 state))))) - (point-changed (not (eq (point) (nth 0 state)))) - (vscroll-changed (not (eq (window-vscroll win t) (nth 3 state))))) + (page-changed (not (eq page (nth 0 state)))) + (vscroll-changed (not (eq vscroll (nth 3 state))))) + (when size-changed + (let ((page-pos (pos-visible-in-window-p (image-roll-page-to-pos page) win t))) + (when (and (nth 2 page-pos) (nth 1 state)) + (setq vscroll (min (max 1 (- vscroll (- height (nth 1 state)))) + (1- (+ (nth 2 page-pos) (nth 4 page-pos)))))))) + (image-roll-set-vscroll vscroll win) (setq disable-point-adjustment t) (unless (image-roll-page-overlay 1 win) (image-roll-new-window-function win)) - (when (or size-changed point-changed vscroll-changed) - (setf (alist-get win image-roll--last-state) - `(,(point) ,(window-pixel-height win) ,(window-pixel-width win) - ,(window-vscroll win t))) - (set-window-start win (point) t) - (image-roll-update-displayed-pages win size-changed) - (when point-changed (run-hooks 'image-roll-after-change-page-hook)))))) - -(defun image-roll-update-displayed-pages (&optional window force) - "Update the pages displayed in WINDOW. -When FORCE is non-nil redisplay even the already displayed pages." - (let ((old (image-mode-window-get 'displayed-pages window)) - (new (image-roll-display-pages window force))) - ;; If images/pages are small enough (or after jumps), there - ;; might be multiple image that need to get updated - (image-roll-undisplay-pages (cl-set-difference old new) window) - (setf (image-roll-current-page window) (image-roll-page-at-current-pos)) - (image-mode-window-put 'displayed-pages new window) - (cl-set-difference new old))) + (when (or size-changed page-changed vscroll-changed) + (setf (alist-get win image-roll--state) + `(,page ,height ,(window-pixel-width win) ,vscroll)) + (set-window-start win (goto-char (image-roll-page-to-pos page)) t) + (let ((old (image-mode-window-get 'displayed-pages win)) + (new (image-roll-display-pages win size-changed))) + ;; If images/pages are small enough (or after jumps), there + ;; might be multiple image that need to get updated + (image-roll-undisplay-pages (cl-set-difference old new) win) + (image-mode-window-put 'displayed-pages new win) + (when (> (length new) 1) + (forward-char 2))) + (when page-changed (run-hooks 'image-roll-after-change-page-hook)))))) ;;; Page navigation commands (defun image-roll-goto-page-start () @@ -274,7 +290,6 @@ When FORCE is non-nil redisplay even the already displayed pages." (interactive) (image-roll-set-vscroll 0 nil)) -;; NOTE code based on (taken from) `pdf-view-goto-page'. (defun image-roll-goto-page (page &optional window) "Go to PAGE in WINDOW." (interactive @@ -284,13 +299,12 @@ When FORCE is non-nil redisplay even the already displayed pages." (unless (and (>= page 1) (<= page image-roll-last-page)) (error "No such page: %d" page)) - (goto-char (image-roll-page-to-pos page)) + (setf (image-roll-current-page window) page) (image-roll-set-vscroll 0 window)) (defun image-roll-next-page (&optional n) "Go to next page or next Nth page." (interactive "p") - (set-window-start nil (+ (point) (* 2 n)) t) (image-roll-goto-page (+ (image-roll-page-at-current-pos) n))) (defun image-roll-previous-page (&optional n) @@ -307,21 +321,22 @@ backward instead. With a prefix arg PIXELS is the numeric value times `image-roll-step-size'." (interactive (list (* (prefix-numeric-value current-prefix-arg) image-roll-step-size))) (setq pixels (or pixels image-roll-step-size)) + (setq window (or window (selected-window))) (when (> 0 pixels) (image-roll-scroll-backward (- pixels) window)) - (let ((pos (point))) + (let ((pos (goto-char (window-start window)))) (while (let* ((data (pos-visible-in-window-p (point) window t)) (occupied-pixels (cond ((nth 2 data) (nth 4 data)) (data (line-pixel-height)) - (t (funcall image-roll-display-page-function - (image-roll-page-at-current-pos) window) - (line-pixel-height))))) + (t (image-roll-display-page + (image-roll-page-at-current-pos) window))))) (and (>= pixels occupied-pixels) (if (eq (point) (1- (point-max))) (prog1 nil (setq pixels (- occupied-pixels 10)) (message "End of buffer")) (cl-decf pixels occupied-pixels)))) - (forward-char 2)) + (forward-char 4)) + (setf (image-roll-current-page window) (image-roll-page-at-current-pos)) (image-roll-set-vscroll (+ (if (eq pos (point)) (window-vscroll window t) 0) pixels) window))) @@ -333,7 +348,9 @@ forward instead. With a prefix arg PIXELS is the numeric value times `image-roll-step-size'." (interactive (list (* (prefix-numeric-value current-prefix-arg) image-roll-step-size))) (setq pixels (or pixels image-roll-step-size)) + (setq window (or window (selected-window))) (when (> 0 pixels) (image-roll-scroll-backward (- pixels) window)) + (goto-char (window-start window)) (let* ((data (pos-visible-in-window-p (point) window t)) (pixels-top (if (nth 2 data) (nth 2 data) 0))) (if (< pixels pixels-top) @@ -343,12 +360,13 @@ With a prefix arg PIXELS is the numeric value times `image-roll-step-size'." (while (and (if (bobp) (prog1 nil (message "Beginning of buffer.")) t) - (progn (forward-char -2) - (funcall image-roll-display-page-function - (image-roll-page-at-current-pos) window) + (progn (forward-char -4) + (image-roll-display-page + (image-roll-page-at-current-pos) window) (cl-decf pixels (line-pixel-height))) (> pixels 0))) - (image-roll-set-vscroll (- pixels) window)))) + (image-roll-set-vscroll (- pixels) window))) + (setf (image-roll-current-page window) (image-roll-page-at-current-pos))) (defun image-roll-scroll-screen-forward (&optional arg) "Scroll forward by (almost) ARG many full screens." @@ -372,5 +390,4 @@ With a prefix arg PIXELS is the numeric value times `image-roll-step-size'." (_ (error "Event must be wheel down or wheel up event"))))) (provide 'image-roll) - ;;; image-roll.el ends here From f5c725c33fbf899f41519ee72816dd313fd9989d Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Tue, 27 Jun 2023 00:18:11 +0200 Subject: [PATCH 077/104] Check for overlays first --- image-roll.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/image-roll.el b/image-roll.el index 82e7c648..ba7fa49c 100644 --- a/image-roll.el +++ b/image-roll.el @@ -253,6 +253,8 @@ overlays." "Handle modifications to the state in window WIN. It should be added to `pre-redisplay-functions' buffer locally." (with-demoted-errors "Error in image roll pre-redisplay: %S" + (unless (image-roll-page-overlay 1 win) + (image-roll-new-window-function win)) (let* ((state (alist-get win image-roll--state)) (height (window-pixel-height win)) (page (image-roll-current-page win)) @@ -268,8 +270,6 @@ It should be added to `pre-redisplay-functions' buffer locally." (1- (+ (nth 2 page-pos) (nth 4 page-pos)))))))) (image-roll-set-vscroll vscroll win) (setq disable-point-adjustment t) - (unless (image-roll-page-overlay 1 win) - (image-roll-new-window-function win)) (when (or size-changed page-changed vscroll-changed) (setf (alist-get win image-roll--state) `(,page ,height ,(window-pixel-width win) ,vscroll)) From 2d4d98079fc60d03c89775adcc1f762671255bd5 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Tue, 27 Jun 2023 22:19:46 +0200 Subject: [PATCH 078/104] Tweak pre-redisplay function --- image-roll.el | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/image-roll.el b/image-roll.el index ba7fa49c..21552d3f 100644 --- a/image-roll.el +++ b/image-roll.el @@ -150,7 +150,7 @@ logic)" (defun image-roll-set-vscroll (vscroll win) "Set vscroll to VSCROLL in window WIN." (image-mode-window-put 'vscroll vscroll win) - (set-window-vscroll win vscroll t t)) + (set-window-vscroll win vscroll t)) ;;; Displaying/Undisplaying pages (defun image-roll-display-image (image page window) @@ -177,11 +177,10 @@ With FORCE non-nil display fetch page again even if it is already displayed." page window) (cdr (image-display-size display t)))) -(defun image-roll-display-pages (&optional window force) - "Display pages to fill the WINDOW. +(defun image-roll-display-pages (page &optional window force) + "Display pages to fill the WINDOW starting from PAGE. If FORCE is non-nill redisplay a page even if it is already displayed." - (let (displayed - (page (image-roll-page-at-current-pos))) + (let (displayed) (image-roll-set-vscroll (min (image-mode-window-get 'vscroll window) (1- (image-roll-display-page page window force))) window) @@ -245,8 +244,6 @@ overlays." (setq window (if (windowp window) window (selected-window))) (when (image-roll-page-overlay 1 window) (setf (alist-get window image-roll--state) nil) - (set-window-start window (goto-char (image-roll-page-to-pos - (image-roll-current-page window))) t) (force-window-update window))) (defun image-roll-pre-redisplay (win) @@ -263,25 +260,19 @@ It should be added to `pre-redisplay-functions' buffer locally." (eq (window-pixel-width win) (nth 2 state))))) (page-changed (not (eq page (nth 0 state)))) (vscroll-changed (not (eq vscroll (nth 3 state))))) - (when size-changed - (let ((page-pos (pos-visible-in-window-p (image-roll-page-to-pos page) win t))) - (when (and (nth 2 page-pos) (nth 1 state)) - (setq vscroll (min (max 1 (- vscroll (- height (nth 1 state)))) - (1- (+ (nth 2 page-pos) (nth 4 page-pos)))))))) (image-roll-set-vscroll vscroll win) + (set-window-start win (image-roll-page-to-pos page) t) (setq disable-point-adjustment t) (when (or size-changed page-changed vscroll-changed) - (setf (alist-get win image-roll--state) - `(,page ,height ,(window-pixel-width win) ,vscroll)) - (set-window-start win (goto-char (image-roll-page-to-pos page)) t) (let ((old (image-mode-window-get 'displayed-pages win)) - (new (image-roll-display-pages win size-changed))) + (new (image-roll-display-pages page win size-changed))) ;; If images/pages are small enough (or after jumps), there ;; might be multiple image that need to get updated (image-roll-undisplay-pages (cl-set-difference old new) win) (image-mode-window-put 'displayed-pages new win) - (when (> (length new) 1) - (forward-char 2))) + (set-window-point win (+ (window-start win) (if (> (length new) 1) 2 0)))) + (setf (alist-get win image-roll--state) + `(,page ,height ,(window-pixel-width win) ,vscroll nil)) (when page-changed (run-hooks 'image-roll-after-change-page-hook)))))) ;;; Page navigation commands From 200cc23ca83d158e8837a16271fe641f1004b4ee Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Wed, 28 Jun 2023 15:08:19 +0200 Subject: [PATCH 079/104] Obey next-screen-context-lines --- image-roll.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/image-roll.el b/image-roll.el index 21552d3f..27d1b059 100644 --- a/image-roll.el +++ b/image-roll.el @@ -363,13 +363,13 @@ With a prefix arg PIXELS is the numeric value times `image-roll-step-size'." "Scroll forward by (almost) ARG many full screens." (interactive "p") (image-roll-scroll-forward - (* (window-text-height nil t) (- arg 0.1)))) + (- (* (window-text-height nil t) arg) (* next-screen-context-lines (frame-char-height))))) (defun image-roll-scroll-screen-backward (&optional arg) "Scroll backward by (almost) ARG many full screens." (interactive "p") (image-roll-scroll-backward - (* (window-text-height nil t) (- arg 0.1)))) + (- (* (window-text-height nil t) arg) (* next-screen-context-lines (frame-char-height))))) (defun image-roll-scroll-mouse-wheel (event) "Scroll according to mouse wheel EVENT." From 07099b11ff230e245aa3a9891b2390b718e00491 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Thu, 29 Jun 2023 22:43:44 +0200 Subject: [PATCH 080/104] Actually use image-roll-center and set hscroll too if present --- image-roll.el | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/image-roll.el b/image-roll.el index 27d1b059..36f9f23f 100644 --- a/image-roll.el +++ b/image-roll.el @@ -159,7 +159,7 @@ logic)" (overlay (image-roll-page-overlay page window)) (margin-pos (+ (image-roll-page-to-pos page) 2)) (margin-overlay (image-roll--pos-overlay margin-pos window)) - (align-to (when (> (window-width window t) (car size)) + (align-to (when (and image-roll-center (> (window-width window t) (car size))) (/ (- (window-width window t) (car size)) 2))) (before-string (when align-to (propertize " " 'display `(space :align-to (,align-to)))))) (overlay-put overlay 'display image) @@ -199,6 +199,7 @@ Replaces the display property of the overlay holding a page with a space." (dolist (page pages) (overlay-put (image-roll-page-overlay page window) 'display (get 'image-roll 'display)))) + ;;; State Management (defun image-roll-new-window-function (&optional win) "Setup image roll in a new window WIN. @@ -260,7 +261,8 @@ It should be added to `pre-redisplay-functions' buffer locally." (eq (window-pixel-width win) (nth 2 state))))) (page-changed (not (eq page (nth 0 state)))) (vscroll-changed (not (eq vscroll (nth 3 state))))) - (image-roll-set-vscroll vscroll win) + (set-window-vscroll win vscroll t) + (set-window-hscroll win (or (image-mode-window-get 'hscroll win) 0)) (set-window-start win (image-roll-page-to-pos page) t) (setq disable-point-adjustment t) (when (or size-changed page-changed vscroll-changed) From 305004efea4a3d27ecfb3ebe77c84364a240368a Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Fri, 30 Jun 2023 11:43:37 +0200 Subject: [PATCH 081/104] Handle last page and change in window selection better --- image-roll.el | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/image-roll.el b/image-roll.el index 36f9f23f..594ca838 100644 --- a/image-roll.el +++ b/image-roll.el @@ -149,6 +149,7 @@ logic)" (defun image-roll-set-vscroll (vscroll win) "Set vscroll to VSCROLL in window WIN." + (image-mode-winprops win t) (image-mode-window-put 'vscroll vscroll win) (set-window-vscroll win vscroll t)) @@ -254,16 +255,17 @@ It should be added to `pre-redisplay-functions' buffer locally." (unless (image-roll-page-overlay 1 win) (image-roll-new-window-function win)) (let* ((state (alist-get win image-roll--state)) - (height (window-pixel-height win)) (page (image-roll-current-page win)) + (height (window-pixel-height win)) (vscroll (image-mode-window-get 'vscroll win)) (size-changed (not (and (eq height (nth 1 state)) (eq (window-pixel-width win) (nth 2 state))))) (page-changed (not (eq page (nth 0 state)))) - (vscroll-changed (not (eq vscroll (nth 3 state))))) + (vscroll-changed (not (eq vscroll (nth 3 state)))) + (start (image-roll-page-to-pos page))) (set-window-vscroll win vscroll t) (set-window-hscroll win (or (image-mode-window-get 'hscroll win) 0)) - (set-window-start win (image-roll-page-to-pos page) t) + (set-window-start win start t) (setq disable-point-adjustment t) (when (or size-changed page-changed vscroll-changed) (let ((old (image-mode-window-get 'displayed-pages win)) @@ -272,7 +274,8 @@ It should be added to `pre-redisplay-functions' buffer locally." ;; might be multiple image that need to get updated (image-roll-undisplay-pages (cl-set-difference old new) win) (image-mode-window-put 'displayed-pages new win) - (set-window-point win (+ (window-start win) (if (> (length new) 1) 2 0)))) + (set-window-point win (+ start + (if (pos-visible-in-window-p (+ 2 start) win) 2 0)))) (setf (alist-get win image-roll--state) `(,page ,height ,(window-pixel-width win) ,vscroll nil)) (when page-changed (run-hooks 'image-roll-after-change-page-hook)))))) From 802076fbcdcaf56b483a3aa7c07658b2f09d9125 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Thu, 3 Aug 2023 20:54:01 +0200 Subject: [PATCH 082/104] Combine image-roll.el and pdf-roll.el files pdf-roll now contains both their contents. All image-roll-* symbols are now pdf-roll-* symbols --- image-roll.el | 389 ---------------------------------------------- lisp/pdf-links.el | 6 +- lisp/pdf-roll.el | 337 ++++++++++++++++++++++++++++++++++++--- lisp/pdf-view.el | 28 ++-- 4 files changed, 332 insertions(+), 428 deletions(-) delete mode 100644 image-roll.el diff --git a/image-roll.el b/image-roll.el deleted file mode 100644 index 594ca838..00000000 --- a/image-roll.el +++ /dev/null @@ -1,389 +0,0 @@ -;;; image-roll.el --- Virtual scroll display engine -*- lexical-binding:t -*- - -;; Copyright (C) 202 Free Software Foundation, Inc. - -;; Author: D. L. Nicolai -;; Version: 1.0 -;; Keywords: files, pdf -;; URL: https://github.com/dalanicolai/image-roll.el -;; Package-Requires: ((emacs "26.1")) - - -;;; Commentary: - -;; This package provides a virtual scroll engine for displaying books/documents. -;; The main purpose of the package is to provide a continuous scrolling when -;; viewing documents. - -;; The package is written in a way that it supports images/pages of different -;; sizes on the same roll (see comment above `image-roll-scroll-forward'). Also -;; there is no minumum or maximum on the range of the sizes, and finally, it is -;; written to support being displayed in (any number of) multiple windows. - -;; The core functionality, i.e. the 'scroll' is provided by the -;; `image-roll-new-window-function' and `image-roll-redisplay' functions. The -;; function `image-roll-new-window-function' should be added to the -;; `image-mode-new-window-functions' while the `image-roll-redisplay' should be -;; added to the `window-configuration-change-hook' both as buffer local hook -;; functions (i.e. by passing a non-nil LOCAL argument to `add-hook'). For the -;; `image-mode-new-window-functions' to have effect, the `image-mode-winprops' -;; should be initialized by either using `image-mode-setup-winprops' (like in -;; the body of `pdf-view-mode') or by initializing the -;; `image-mode-winprops-alist' explicitly (by setting its value to nil, like in -;; the `image-roll-mode' example). - -;; The package is meant to be used in combination with some other package that -;; provides features to extract and manage the data from the document. An -;; example of such a file is the file `pdf-scroll.el' at URL: -;; https://github.com/dalanicolai/pdf-tools/blob/image-roll-version/lisp/pdf-scroll.el -;; The file `pdf-scroll.el' provides the configurations for pdf-tools to work -;; with `image-roll.el' - -;; However, for development purposes, the package provides an `image-roll-demo' -;; function. Also, as an example of its usage it includes a function -;; `image-roll-directory' which can be used to view all images in some directory -;; using the roll. - -;; This file provides four buffer local variables that should be set to the -;; values of the functions that correctly 'retrieve' the required data from the -;; document. See their docstrings and the `image-roll-directory' function (or -;; `pdf-scroll.el') for more info. - - -;;; Issues - -;; No real issues are known when using this package in a clean way, i.e. by -;; starting from `emacs -Q', then loading this package and using it. - -;; However, I have experienced some errors in redisplay when using this package -;; in combination with vertico, marginalia and savehist. In that case sometimes -;; Emacs its `redisplay' can get a little 'confused/messed up', so that a page -;; (although never the first page but only later pages) will not show directly -;; when creating a new (second, third, ...) window. In that case `redisplay' can -;; be forced by 'activating' the minibuffer (e.g. by pressing `M-x') and hiding -;; it again. It is a weird bug, because it only happens when installing those -;; packages via `use-package', but not when the packages are installed via -;; `package-install'. Also it seems to occur mostly when these three packages -;; are combined. Additionally, it might be might 'due to' using multiple Emacs -;; versions (you can try if the issue occurs on the other Emacs version also, -;; probably not). See -;; https://lists.gnu.org/archive/html/emacs-devel/2022-04/msg00829.html Anyway, -;; I don't know what causes the bug, but this is what I have noticed from -;; inspecting it. - -;;; Code: -(require 'image-mode) - -(put 'image-roll 'display '(space :width 25 :height 1000)) -(put 'image-roll 'evaporate t) -(put 'image-roll-margin 'evaporate t) - -;;; Custom Variables -(defgroup image-roll nil - "Image roll configurations." - :group 'applications) - -(defface image-roll-default `((t :font ,(font-spec :family "monospace" :size 1))) - "Default face for image roll documents.") - -(defcustom image-roll-vertical-margin 2 - "Vertical margin between images in pixels, i.e. page separation height." - :type 'integer) - -(defcustom image-roll-margin-color "gray" - "Background color of overlay, i.e. page separation color." - :type 'color - :set (lambda (_ color) (put 'image-roll-margin 'face `(:background ,color)))) - -(defcustom image-roll-step-size 50 - "Scroll step size in pixels units." - :type 'integer) - -(defcustom image-roll-center nil - "When non-nil, center the roll horizontally in the window." - :type 'boolean) - -(defcustom image-roll-after-change-page-hook nil - "Hook run after changing to and displaying another page." - :type 'hook) - -;;; Local variables to be set by implementations -(defvar-local image-roll-last-page 0) - -(defvar-local image-roll-page-image-function nil - "Function of two arguments that retrieves the image for a page. -The first argument is the page number and the second argument is the window in -which the page will be displayed. There return value is an image spec.") - -;;; Other Variables -(defvar-local image-roll--state nil - "Local variable that tracks window, point and vscroll to handle changes.") - -;;; Utility Macros and functions -(defmacro image-roll-current-page (&optional window) - "Return the page number of the currently displayed page in WINDOW. -The current page is the page that overlaps with the window -start (this choice was made in order to simplify the scrolling -logic)" - `(image-mode-window-get 'page ,window)) - -(defsubst image-roll-page-to-pos (page) - "Get the buffer position displaing PAGE." - (- (* 4 page) 3)) - -(defun image-roll--pos-overlay (pos window) - "Return an overlay for WINDOW at POS." - (cl-find window (overlays-at pos) :key (lambda (ov) (overlay-get ov 'window)))) - -(defun image-roll-page-overlay (&optional page window) - "Return overlay displaying PAGE in WINDOW." - (image-roll--pos-overlay - (image-roll-page-to-pos (or page (image-roll-page-at-current-pos))) - (or window (selected-window)))) - -(defun image-roll-page-at-current-pos () - "Page at point." - (if (cl-oddp (point)) - (/ (+ (point) 3) 4) - (error "No page is displayed at current position (%s)" (point)))) - -(defun image-roll-set-vscroll (vscroll win) - "Set vscroll to VSCROLL in window WIN." - (image-mode-winprops win t) - (image-mode-window-put 'vscroll vscroll win) - (set-window-vscroll win vscroll t)) - -;;; Displaying/Undisplaying pages -(defun image-roll-display-image (image page window) - "Display IMAGE for PAGE in WINDOW." - (let* ((size (image-display-size image t)) - (overlay (image-roll-page-overlay page window)) - (margin-pos (+ (image-roll-page-to-pos page) 2)) - (margin-overlay (image-roll--pos-overlay margin-pos window)) - (align-to (when (and image-roll-center (> (window-width window t) (car size))) - (/ (- (window-width window t) (car size)) 2))) - (before-string (when align-to (propertize " " 'display `(space :align-to (,align-to)))))) - (overlay-put overlay 'display image) - (overlay-put overlay 'before-string before-string) - (overlay-put margin-overlay 'display `(space :width (,(car size)) :height (,image-roll-vertical-margin))) - (overlay-put margin-overlay 'before-string before-string) - (cdr size))) - -(defun image-roll-display-page (page window &optional force) - "Display PAGE in WINDOW. -With FORCE non-nil display fetch page again even if it is already displayed." - (if-let ((display (overlay-get (image-roll-page-overlay page window) 'display)) - ((or force (eq (car display) 'space)))) - (image-roll-display-image (funcall image-roll-page-image-function page window) - page window) - (cdr (image-display-size display t)))) - -(defun image-roll-display-pages (page &optional window force) - "Display pages to fill the WINDOW starting from PAGE. -If FORCE is non-nill redisplay a page even if it is already displayed." - (let (displayed) - (image-roll-set-vscroll (min (image-mode-window-get 'vscroll window) - (1- (image-roll-display-page page window force))) - window) - (push page displayed) - (while (pos-visible-in-window-p (image-roll-page-to-pos (cl-incf page)) window t) - (image-roll-display-page page window force) - (push page displayed)) - ;; store displayed images for determining which images to update when update - ;; is triggered - (cl-callf cl-union (image-mode-window-get 'displayed-pages window) displayed) - displayed)) - -(defun image-roll-undisplay-pages (pages &optional window) - "Undisplay PAGES from WINDOW. -Replaces the display property of the overlay holding a page with a space." - (dolist (page pages) - (overlay-put (image-roll-page-overlay page window) - 'display (get 'image-roll 'display)))) - -;;; State Management -(defun image-roll-new-window-function (&optional win) - "Setup image roll in a new window WIN. -If the buffer is newly created, then it does not contain any -overlay and this function erases the buffer contents, after which -it inserts empty spaces that each hold a overlay. If the buffer -already has overlays (i.e. a second or subsequent window is -created), the function simply copies the overlays and adds the -new window as window overlay-property to each overlay. - -This function should be added to image-roll (continuous scroll) -minor mode commands, after erasing the buffer to create the -overlays." - (setq win (or (and (windowp win) win) (selected-window))) - (if (not (overlays-at 1)) - (let ((pages image-roll-last-page) - (inhibit-read-only t)) - (erase-buffer) - (setq image-roll--state (list t)) - (dotimes (i (* 2 pages)) - (insert " ") - (let ((o (make-overlay (1- (point)) (point)))) - (overlay-put o 'category (if (eq 0 (mod i 2)) 'image-roll 'image-roll-margin)) - (overlay-put o 'window win)) - (insert "\n")) - (delete-char -1) - (set-buffer-modified-p nil)) - (unless (image-roll-page-overlay 1 win) - (dotimes (i (/ (point-max) 2)) - (overlay-put (copy-overlay (car (overlays-at (1+ (* 2 i))))) - 'window win)) - (dolist (win-st image-roll--state) - (when-let ((win-old (car-safe win-st)) - ((not (window-live-p win-old)))) - (remove-overlays (point-min) (point-max) 'window win-old))) - (cl-callf2 cl-delete-if-not #'window-live-p image-roll--state :key #'car-safe))) - ;; initial `image-roll-redisplay' needs to know which page(s) to display - (cl-callf or (image-roll-current-page win) 1) - (cl-callf or (image-mode-window-get 'vscroll win) 0)) - -(defun image-roll-redisplay (&optional window) - "Analogue of `pdf-view-redisplay' for WINDOW." - (setq window (if (windowp window) window (selected-window))) - (when (image-roll-page-overlay 1 window) - (setf (alist-get window image-roll--state) nil) - (force-window-update window))) - -(defun image-roll-pre-redisplay (win) - "Handle modifications to the state in window WIN. -It should be added to `pre-redisplay-functions' buffer locally." - (with-demoted-errors "Error in image roll pre-redisplay: %S" - (unless (image-roll-page-overlay 1 win) - (image-roll-new-window-function win)) - (let* ((state (alist-get win image-roll--state)) - (page (image-roll-current-page win)) - (height (window-pixel-height win)) - (vscroll (image-mode-window-get 'vscroll win)) - (size-changed (not (and (eq height (nth 1 state)) - (eq (window-pixel-width win) (nth 2 state))))) - (page-changed (not (eq page (nth 0 state)))) - (vscroll-changed (not (eq vscroll (nth 3 state)))) - (start (image-roll-page-to-pos page))) - (set-window-vscroll win vscroll t) - (set-window-hscroll win (or (image-mode-window-get 'hscroll win) 0)) - (set-window-start win start t) - (setq disable-point-adjustment t) - (when (or size-changed page-changed vscroll-changed) - (let ((old (image-mode-window-get 'displayed-pages win)) - (new (image-roll-display-pages page win size-changed))) - ;; If images/pages are small enough (or after jumps), there - ;; might be multiple image that need to get updated - (image-roll-undisplay-pages (cl-set-difference old new) win) - (image-mode-window-put 'displayed-pages new win) - (set-window-point win (+ start - (if (pos-visible-in-window-p (+ 2 start) win) 2 0)))) - (setf (alist-get win image-roll--state) - `(,page ,height ,(window-pixel-width win) ,vscroll nil)) - (when page-changed (run-hooks 'image-roll-after-change-page-hook)))))) - -;;; Page navigation commands -(defun image-roll-goto-page-start () - "Go to the start of the first displayed page." - (interactive) - (image-roll-set-vscroll 0 nil)) - -(defun image-roll-goto-page (page &optional window) - "Go to PAGE in WINDOW." - (interactive - (list (if current-prefix-arg - (prefix-numeric-value current-prefix-arg) - (read-number "Page: ")))) - (unless (and (>= page 1) - (<= page image-roll-last-page)) - (error "No such page: %d" page)) - (setf (image-roll-current-page window) page) - (image-roll-set-vscroll 0 window)) - -(defun image-roll-next-page (&optional n) - "Go to next page or next Nth page." - (interactive "p") - (image-roll-goto-page (+ (image-roll-page-at-current-pos) n))) - -(defun image-roll-previous-page (&optional n) - "Go to previous page or previous Nth page." - (interactive "p") - (image-roll-next-page (- n))) - -;;; Scrolling Commands -(defun image-roll-scroll-forward (&optional pixels window) - "Scroll image PIXELS forward in WINDOW. -By default PIXELS is `image-roll-step-size'. When PIXELS is negative scroll -backward instead. - -With a prefix arg PIXELS is the numeric value times `image-roll-step-size'." - (interactive (list (* (prefix-numeric-value current-prefix-arg) image-roll-step-size))) - (setq pixels (or pixels image-roll-step-size)) - (setq window (or window (selected-window))) - (when (> 0 pixels) (image-roll-scroll-backward (- pixels) window)) - (let ((pos (goto-char (window-start window)))) - (while (let* ((data (pos-visible-in-window-p (point) window t)) - (occupied-pixels (cond ((nth 2 data) (nth 4 data)) - (data (line-pixel-height)) - (t (image-roll-display-page - (image-roll-page-at-current-pos) window))))) - (and (>= pixels occupied-pixels) - (if (eq (point) (1- (point-max))) - (prog1 nil - (setq pixels (- occupied-pixels 10)) - (message "End of buffer")) - (cl-decf pixels occupied-pixels)))) - (forward-char 4)) - (setf (image-roll-current-page window) (image-roll-page-at-current-pos)) - (image-roll-set-vscroll (+ (if (eq pos (point)) (window-vscroll window t) 0) pixels) - window))) - -(defun image-roll-scroll-backward (&optional pixels window) - "Scroll image PIXELS backwards in WINDOW. -By default PIXELS is `image-roll-step-size'. When PIXELS is negative scroll -forward instead. - -With a prefix arg PIXELS is the numeric value times `image-roll-step-size'." - (interactive (list (* (prefix-numeric-value current-prefix-arg) image-roll-step-size))) - (setq pixels (or pixels image-roll-step-size)) - (setq window (or window (selected-window))) - (when (> 0 pixels) (image-roll-scroll-backward (- pixels) window)) - (goto-char (window-start window)) - (let* ((data (pos-visible-in-window-p (point) window t)) - (pixels-top (if (nth 2 data) (nth 2 data) 0))) - (if (< pixels pixels-top) - (image-roll-set-vscroll (- (window-vscroll window t) pixels) - window) - (cl-decf pixels pixels-top) - (while (and (if (bobp) - (prog1 nil (message "Beginning of buffer.")) - t) - (progn (forward-char -4) - (image-roll-display-page - (image-roll-page-at-current-pos) window) - (cl-decf pixels (line-pixel-height))) - (> pixels 0))) - (image-roll-set-vscroll (- pixels) window))) - (setf (image-roll-current-page window) (image-roll-page-at-current-pos))) - -(defun image-roll-scroll-screen-forward (&optional arg) - "Scroll forward by (almost) ARG many full screens." - (interactive "p") - (image-roll-scroll-forward - (- (* (window-text-height nil t) arg) (* next-screen-context-lines (frame-char-height))))) - -(defun image-roll-scroll-screen-backward (&optional arg) - "Scroll backward by (almost) ARG many full screens." - (interactive "p") - (image-roll-scroll-backward - (- (* (window-text-height nil t) arg) (* next-screen-context-lines (frame-char-height))))) - -(defun image-roll-scroll-mouse-wheel (event) - "Scroll according to mouse wheel EVENT." - (interactive "e") - (with-selected-window (posn-window (event-start event)) - (pcase (event-basic-type event) - ('wheel-down (image-roll-scroll-forward)) - ('wheel-up (image-roll-scroll-backward)) - (_ (error "Event must be wheel down or wheel up event"))))) - -(provide 'image-roll) -;;; image-roll.el ends here diff --git a/lisp/pdf-links.el b/lisp/pdf-links.el index 5b1436e8..674a7653 100644 --- a/lisp/pdf-links.el +++ b/lisp/pdf-links.el @@ -32,8 +32,8 @@ (defvar pdf-view-roll-minor-mode) -(declare-function image-roll-page-overlay "image-roll") -(declare-function image-roll-displayed-pages "image-roll") +(declare-function pdf-roll-page-overlay "pdf-roll") +(declare-function pdf-roll-displayed-pages "pdf-roll") ;;; Code: @@ -244,7 +244,7 @@ See `pdf-links-action-perform' for the interface." (unwind-protect (progn (dolist (page pages) - (let* ((image (or (overlay-get (image-roll-page-overlay page win) 'display) + (let* ((image (or (overlay-get (pdf-roll-page-overlay page win) 'display) (pdf-view-current-image))) (image (or (assoc 'image image) image)) (height (cdr (image-size image t))) diff --git a/lisp/pdf-roll.el b/lisp/pdf-roll.el index 539ac298..05f981d4 100644 --- a/lisp/pdf-roll.el +++ b/lisp/pdf-roll.el @@ -22,10 +22,73 @@ ;; ;;; Code: -(require 'image-roll) (require 'pdf-view) +(put 'pdf-roll 'display '(space :width 25 :height 1000)) +(put 'pdf-roll 'evaporate t) +(put 'pdf-roll-margin 'evaporate t) +;;; Custom Variables +(defgroup pdf-roll nil + "Image roll configurations." + :group 'applications) + +(defface pdf-roll-default `((t :font ,(font-spec :family "monospace" :size 1))) + "Default face for image roll documents.") + +(defcustom pdf-roll-vertical-margin 2 + "Vertical margin between images in pixels, i.e. page separation height." + :type 'integer) + +(defcustom pdf-roll-margin-color "gray" + "Background color of overlay, i.e. page separation color." + :type 'color + :set (lambda (_ color) (put 'pdf-roll-margin 'face `(:background ,color)))) + +(defcustom pdf-roll-step-size 50 + "Scroll step size in pixels units." + :type 'integer) + +;;; Variables +(defvar-local pdf-roll-last-page 0) +(defvar-local pdf-roll--state nil + "Local variable that tracks window, point and vscroll to handle changes.") + +;;; Utility Macros and functions +(defmacro pdf-roll-current-page (&optional window) + "Return the page number of the currently displayed page in WINDOW. +The current page is the page that overlaps with the window +start (this choice was made in order to simplify the scrolling +logic)" + `(image-mode-window-get 'page ,window)) + +(defsubst pdf-roll-page-to-pos (page) + "Get the buffer position displaing PAGE." + (- (* 4 page) 3)) + +(defun pdf-roll--pos-overlay (pos window) + "Return an overlay for WINDOW at POS." + (cl-find window (overlays-at pos) :key (lambda (ov) (overlay-get ov 'window)))) + +(defun pdf-roll-page-overlay (&optional page window) + "Return overlay displaying PAGE in WINDOW." + (pdf-roll--pos-overlay + (pdf-roll-page-to-pos (or page (pdf-roll-page-at-current-pos))) + (or window (selected-window)))) + +(defun pdf-roll-page-at-current-pos () + "Page at point." + (if (cl-oddp (point)) + (/ (+ (point) 3) 4) + (error "No page is displayed at current position (%s)" (point)))) + +(defun pdf-roll-set-vscroll (vscroll win) + "Set vscroll to VSCROLL in window WIN." + (image-mode-winprops win t) + (image-mode-window-put 'vscroll vscroll win) + (set-window-vscroll win vscroll t)) + +;;; Displaying/Undisplaying pages (defun pdf-roll-maybe-slice-image (image &optional window inhibit-slice-p) "Return a sliced IMAGE if `pdf-view-current-slice' in WINDOW is non-nil. If INHIBIT-SLICE-P is non-nil, disregard `pdf-view-current-slice'." @@ -36,52 +99,282 @@ If INHIBIT-SLICE-P is non-nil, disregard `pdf-view-current-slice'." image) image)) +(defun pdf-roll--display-image (image page window) + "Display IMAGE for PAGE in WINDOW." + (let* ((size (image-display-size image t)) + (overlay (pdf-roll-page-overlay page window)) + (margin-pos (+ (pdf-roll-page-to-pos page) 2)) + (margin-overlay (pdf-roll--pos-overlay margin-pos window)) + (align-to (when (> (window-width window t) (car size)) + (/ (- (window-width window t) (car size)) 2))) + (before-string (when align-to (propertize " " 'display `(space :align-to (,align-to)))))) + (overlay-put overlay 'display image) + (overlay-put overlay 'before-string before-string) + (overlay-put margin-overlay 'display `(space :width (,(car size)) :height (,pdf-roll-vertical-margin))) + (overlay-put margin-overlay 'before-string before-string) + (cdr size))) + (defun pdf-roll-display-image (image page &optional window inhibit-slice-p) "Display IMAGE for PAGE in WINDOW. If INHIBIT-SLICE-P is non-nil, disregard `pdf-view-current-slice'." (let ((image (pdf-roll-maybe-slice-image image window inhibit-slice-p))) - (image-roll-display-image image page window))) + (pdf-roll--display-image image page window))) (defun pdf-roll-page-image (page window) "Function to retrieve image of the PAGE in WINDOW." (pdf-roll-maybe-slice-image (pdf-view-create-page page window) window)) +(defun pdf-roll-display-page (page window &optional force) + "Display PAGE in WINDOW. +With FORCE non-nil display fetch page again even if it is already displayed." + (if-let ((display (overlay-get (pdf-roll-page-overlay page window) 'display)) + ((or force (eq (car display) 'space)))) + (pdf-roll-display-image (pdf-roll-page-image page window) + page window) + (cdr (image-display-size display t)))) + +(defun pdf-roll-display-pages (page &optional window force) + "Display pages to fill the WINDOW starting from PAGE. +If FORCE is non-nill redisplay a page even if it is already displayed." + (let (displayed) + (pdf-roll-set-vscroll (min (image-mode-window-get 'vscroll window) + (1- (pdf-roll-display-page page window force))) + window) + (push page displayed) + (while (pos-visible-in-window-p (pdf-roll-page-to-pos (cl-incf page)) window t) + (pdf-roll-display-page page window force) + (push page displayed)) + ;; store displayed images for determining which images to update when update + ;; is triggered + (cl-callf cl-union (image-mode-window-get 'displayed-pages window) displayed) + displayed)) + +(defun pdf-roll-undisplay-pages (pages &optional window) + "Undisplay PAGES from WINDOW. +Replaces the display property of the overlay holding a page with a space." + (dolist (page pages) + (overlay-put (pdf-roll-page-overlay page window) + 'display (get 'pdf-roll 'display)))) + +;;; State Management +(defun pdf-roll-new-window-function (&optional win) + "Setup image roll in a new window WIN. +If the buffer is newly created, then it does not contain any +overlay and this function erases the buffer contents, after which +it inserts empty spaces that each hold a overlay. If the buffer +already has overlays (i.e. a second or subsequent window is +created), the function simply copies the overlays and adds the +new window as window overlay-property to each overlay. + +This function should be added to pdf-roll (continuous scroll) +minor mode commands, after erasing the buffer to create the +overlays." + (setq win (or (and (windowp win) win) (selected-window))) + (if (not (overlays-at 1)) + (let ((pages pdf-roll-last-page) + (inhibit-read-only t)) + (erase-buffer) + (setq pdf-roll--state (list t)) + (dotimes (i (* 2 pages)) + (insert " ") + (let ((o (make-overlay (1- (point)) (point)))) + (overlay-put o 'category (if (eq 0 (mod i 2)) 'pdf-roll 'pdf-roll-margin)) + (overlay-put o 'window win)) + (insert "\n")) + (delete-char -1) + (set-buffer-modified-p nil)) + (unless (pdf-roll-page-overlay 1 win) + (dotimes (i (/ (point-max) 2)) + (overlay-put (copy-overlay (car (overlays-at (1+ (* 2 i))))) + 'window win)) + (dolist (win-st pdf-roll--state) + (when-let ((win-old (car-safe win-st)) + ((not (window-live-p win-old)))) + (remove-overlays (point-min) (point-max) 'window win-old))) + (cl-callf2 cl-delete-if-not #'window-live-p pdf-roll--state :key #'car-safe))) + ;; initial `pdf-roll-redisplay' needs to know which page(s) to display + (cl-callf or (pdf-roll-current-page win) 1) + (cl-callf or (image-mode-window-get 'vscroll win) 0)) + +(defun pdf-roll-redisplay (&optional window) + "Analogue of `pdf-view-redisplay' for WINDOW." + (setq window (if (windowp window) window (selected-window))) + (when (pdf-roll-page-overlay 1 window) + (setf (alist-get window pdf-roll--state) nil) + (force-window-update window))) + +(defun pdf-roll-pre-redisplay (win) + "Handle modifications to the state in window WIN. +It should be added to `pre-redisplay-functions' buffer locally." + (with-demoted-errors "Error in image roll pre-redisplay: %S" + (unless (pdf-roll-page-overlay 1 win) + (pdf-roll-new-window-function win)) + (let* ((state (alist-get win pdf-roll--state)) + (page (pdf-roll-current-page win)) + (height (window-pixel-height win)) + (vscroll (image-mode-window-get 'vscroll win)) + (size-changed (not (and (eq height (nth 1 state)) + (eq (window-pixel-width win) (nth 2 state))))) + (page-changed (not (eq page (nth 0 state)))) + (vscroll-changed (not (eq vscroll (nth 3 state)))) + (start (pdf-roll-page-to-pos page))) + (set-window-vscroll win vscroll t) + (set-window-hscroll win (or (image-mode-window-get 'hscroll win) 0)) + (set-window-start win start t) + (setq disable-point-adjustment t) + (when (or size-changed page-changed vscroll-changed) + (let ((old (image-mode-window-get 'displayed-pages win)) + (new (pdf-roll-display-pages page win size-changed))) + ;; If images/pages are small enough (or after jumps), there + ;; might be multiple image that need to get updated + (pdf-roll-undisplay-pages (cl-set-difference old new) win) + (image-mode-window-put 'displayed-pages new win) + (set-window-point win (+ start + (if (pos-visible-in-window-p (+ 2 start) win) 2 0)))) + (setf (alist-get win pdf-roll--state) + `(,page ,height ,(window-pixel-width win) ,vscroll nil)) + (when page-changed (run-hooks 'pdf-view-after-change-page-hook)))))) + +;;; Page navigation commands +(defun pdf-roll-goto-page-start () + "Go to the start of the first displayed page." + (interactive) + (pdf-roll-set-vscroll 0 nil)) + +(defun pdf-roll-goto-page (page &optional window) + "Go to PAGE in WINDOW." + (interactive + (list (if current-prefix-arg + (prefix-numeric-value current-prefix-arg) + (read-number "Page: ")))) + (unless (and (>= page 1) + (<= page pdf-roll-last-page)) + (error "No such page: %d" page)) + (setf (pdf-roll-current-page window) page) + (pdf-roll-set-vscroll 0 window)) + +(defun pdf-roll-next-page (&optional n) + "Go to next page or next Nth page." + (interactive "p") + (pdf-roll-goto-page (+ (pdf-roll-page-at-current-pos) n))) + +(defun pdf-roll-previous-page (&optional n) + "Go to previous page or previous Nth page." + (interactive "p") + (pdf-roll-next-page (- n))) + +;;; Scrolling Commands +(defun pdf-roll-scroll-forward (&optional pixels window) + "Scroll image PIXELS forward in WINDOW. +By default PIXELS is `pdf-roll-step-size'. When PIXELS is negative scroll +backward instead. + +With a prefix arg PIXELS is the numeric value times `pdf-roll-step-size'." + (interactive (list (* (prefix-numeric-value current-prefix-arg) pdf-roll-step-size))) + (setq pixels (or pixels pdf-roll-step-size)) + (setq window (or window (selected-window))) + (when (> 0 pixels) (pdf-roll-scroll-backward (- pixels) window)) + (let ((pos (goto-char (window-start window)))) + (while (let* ((data (pos-visible-in-window-p (point) window t)) + (occupied-pixels (cond ((nth 2 data) (nth 4 data)) + (data (line-pixel-height)) + (t (pdf-roll-display-page + (pdf-roll-page-at-current-pos) window))))) + (and (>= pixels occupied-pixels) + (if (eq (point) (1- (point-max))) + (prog1 nil + (setq pixels (- occupied-pixels 10)) + (message "End of buffer")) + (cl-decf pixels occupied-pixels)))) + (forward-char 4)) + (setf (pdf-roll-current-page window) (pdf-roll-page-at-current-pos)) + (pdf-roll-set-vscroll (+ (if (eq pos (point)) (window-vscroll window t) 0) pixels) + window))) + +(defun pdf-roll-scroll-backward (&optional pixels window) + "Scroll image PIXELS backwards in WINDOW. +By default PIXELS is `pdf-roll-step-size'. When PIXELS is negative scroll +forward instead. + +With a prefix arg PIXELS is the numeric value times `pdf-roll-step-size'." + (interactive (list (* (prefix-numeric-value current-prefix-arg) pdf-roll-step-size))) + (setq pixels (or pixels pdf-roll-step-size)) + (setq window (or window (selected-window))) + (when (> 0 pixels) (pdf-roll-scroll-backward (- pixels) window)) + (goto-char (window-start window)) + (let* ((data (pos-visible-in-window-p (point) window t)) + (pixels-top (if (nth 2 data) (nth 2 data) 0))) + (if (< pixels pixels-top) + (pdf-roll-set-vscroll (- (window-vscroll window t) pixels) + window) + (cl-decf pixels pixels-top) + (while (and (if (bobp) + (prog1 nil (message "Beginning of buffer.")) + t) + (progn (forward-char -4) + (pdf-roll-display-page + (pdf-roll-page-at-current-pos) window) + (cl-decf pixels (line-pixel-height))) + (> pixels 0))) + (pdf-roll-set-vscroll (- pixels) window))) + (setf (pdf-roll-current-page window) (pdf-roll-page-at-current-pos))) + +(defun pdf-roll-scroll-screen-forward (&optional arg) + "Scroll forward by (almost) ARG many full screens." + (interactive "p") + (pdf-roll-scroll-forward + (- (* (window-text-height nil t) arg) (* next-screen-context-lines (frame-char-height))))) + +(defun pdf-roll-scroll-screen-backward (&optional arg) + "Scroll backward by (almost) ARG many full screens." + (interactive "p") + (pdf-roll-scroll-backward + (- (* (window-text-height nil t) arg) (* next-screen-context-lines (frame-char-height))))) + +(defun pdf-roll-scroll-mouse-wheel (event) + "Scroll according to mouse wheel EVENT." + (interactive "e") + (with-selected-window (posn-window (event-start event)) + (pcase (event-basic-type event) + ('wheel-down (pdf-roll-scroll-forward)) + ('wheel-up (pdf-roll-scroll-backward)) + (_ (error "Event must be wheel down or wheel up event"))))) + +;;; Minor mode ;;;###autoload (define-minor-mode pdf-view-roll-minor-mode "If enabled display document on a virtual scroll providing continuous scrolling." :lighter " Continuous" :keymap (let ((map (make-sparse-keymap))) - (define-key map [remap pdf-view-previous-line-or-previous-page] 'image-roll-scroll-backward) - (define-key map [remap pdf-view-next-line-or-next-page] 'image-roll-scroll-forward) - (define-key map [remap pdf-view-scroll-down-or-previous-page] 'image-roll-scroll-backward) - (define-key map [remap pdf-view-scroll-up-or-next-page] 'image-roll-scroll-forward) + (define-key map [remap pdf-view-previous-line-or-previous-page] 'pdf-roll-scroll-backward) + (define-key map [remap pdf-view-next-line-or-next-page] 'pdf-roll-scroll-forward) + (define-key map [remap pdf-view-scroll-down-or-previous-page] 'pdf-roll-scroll-backward) + (define-key map [remap pdf-view-scroll-up-or-next-page] 'pdf-roll-scroll-forward) (define-key map [remap mouse-set-point] 'ignore) - (define-key map (kbd "") 'image-roll-scroll-mouse-wheel) - (define-key map (kbd "") 'image-roll-scroll-mouse-wheel) - (define-key map (kbd "S-") 'image-roll-scroll-screen-forward) - (define-key map (kbd "S-") 'image-roll-scroll-screen-backward) + (define-key map (kbd "") 'pdf-roll-scroll-mouse-wheel) + (define-key map (kbd "") 'pdf-roll-scroll-mouse-wheel) + (define-key map (kbd "S-") 'pdf-roll-scroll-screen-forward) + (define-key map (kbd "S-") 'pdf-roll-scroll-screen-backward) map) :version 28.1 (cond (pdf-view-roll-minor-mode - (setq-local image-roll-last-page (pdf-cache-number-of-pages) - image-roll-page-image-function 'pdf-roll-page-image - image-roll-center t - face-remapping-alist '((default . image-roll-default)) - mwheel-scroll-up-function #'image-roll-scroll-forward - mwheel-scroll-down-function #'image-roll-scroll-backward) + (setq-local pdf-roll-last-page (pdf-cache-number-of-pages) + face-remapping-alist '((default . pdf-roll-default)) + mwheel-scroll-up-function #'pdf-roll-scroll-forward + mwheel-scroll-down-function #'pdf-roll-scroll-backward) (remove-hook 'window-configuration-change-hook 'image-mode-reapply-winprops t) (remove-hook 'window-configuration-change-hook 'pdf-view-redisplay-some-windows t) (remove-hook 'image-mode-new-window-functions#'pdf-view-new-window-function t) - (add-hook 'pre-redisplay-functions 'image-roll-pre-redisplay nil t) - (add-hook 'image-roll-after-change-page-hook 'pdf-history-before-change-page-hook nil t) + (add-hook 'pre-redisplay-functions 'pdf-roll-pre-redisplay nil t) + (add-hook 'pdf-roll-after-change-page-hook 'pdf-history-before-change-page-hook nil t) (let ((inhibit-read-only t)) (erase-buffer) (remove-overlays)) - (image-roll-new-window-function)) + (pdf-roll-new-window-function)) (t (setq-local mwheel-scroll-up-function #'pdf-view-scroll-up-or-next-page mwheel-scroll-down-function #'pdf-view-scroll-down-or-previous-page) @@ -90,8 +383,8 @@ If INHIBIT-SLICE-P is non-nil, disregard `pdf-view-current-slice'." (add-hook 'window-configuration-change-hook 'pdf-view-redisplay-some-windows nil t) (add-hook 'image-mode-new-window-functions #'pdf-view-new-window-function nil t) - (remove-hook 'pre-redisplay-functions 'image-roll-pre-redisplay t) - (remove-hook 'image-roll-after-change-page-hook 'pdf-history-before-change-page-hook t) + (remove-hook 'pre-redisplay-functions 'pdf-roll-pre-redisplay t) + (remove-hook 'pdf-roll-after-change-page-hook 'pdf-history-before-change-page-hook t) (let ((inhibit-read-only t)) (erase-buffer) @@ -107,7 +400,7 @@ If INHIBIT-SLICE-P is non-nil, disregard `pdf-view-current-slice'." returns the display property for the current page if `pdf-view-roll-minor-mode' is non-nil." (when pdf-view-roll-minor-mode - (get-char-property (image-roll-page-to-pos (pdf-view-current-page)) + (get-char-property (pdf-roll-page-to-pos (pdf-view-current-page)) 'display (if (eq (window-buffer) (current-buffer)) (selected-window))))) diff --git a/lisp/pdf-view.el b/lisp/pdf-view.el index ad37ed04..f890bc6f 100644 --- a/lisp/pdf-view.el +++ b/lisp/pdf-view.el @@ -36,13 +36,13 @@ (declare-function cua-copy-region "cua-base") (declare-function pdf-tools-pdf-buffer-p "pdf-tools") -(declare-function image-roll-scroll-forward "image-roll") -(declare-function image-roll-scroll-backward "image-roll") -(declare-function image-roll-next-page "image-roll") -(declare-function image-roll-redisplay "image-roll") -(declare-function image-roll-pre-redisplay "image-roll") -(declare-function image-roll-page-overlay "image-roll") -(declare-function image-roll-page-at-current-pos "image-roll") +(declare-function pdf-roll-scroll-forward "pdf-roll") +(declare-function pdf-roll-scroll-backward "pdf-roll") +(declare-function pdf-roll-next-page "pdf-roll") +(declare-function pdf-roll-redisplay "pdf-roll") +(declare-function pdf-roll-pre-redisplay "pdf-roll") +(declare-function pdf-roll-page-overlay "pdf-roll") +(declare-function pdf-roll-page-at-current-pos "pdf-roll") (declare-function pdf-roll-display-image "pdf-roll") (defvar pdf-view-roll-minor-mode) @@ -698,7 +698,7 @@ windows." (when (window-live-p window) (image-set-window-vscroll 0) (if pdf-view-roll-minor-mode - (image-roll-pre-redisplay window) + (pdf-roll-pre-redisplay window) (pdf-view-redisplay window))) (when changing-p (pdf-view-deactivate-region) @@ -846,7 +846,7 @@ at the bottom edge of the page moves to the next page." (defun pdf-view-next-line-or-next-page (&optional arg) (interactive "p") (if pdf-view-roll-minor-mode - (dotimes (_ (or arg 1)) (image-roll-scroll-forward)) + (dotimes (_ (or arg 1)) (pdf-roll-scroll-forward)) (pdf-view--next-line-or-next-page arg))) (defun pdf-view--previous-line-or-previous-page (&optional arg) @@ -870,7 +870,7 @@ at the top edge of the page moves to the previous page." (defun pdf-view-previous-line-or-previous-page (&optional arg) (interactive "p") (if pdf-view-roll-minor-mode - (dotimes (_ (or arg 1)) (image-roll-scroll-backward)) + (dotimes (_ (or arg 1)) (pdf-roll-scroll-backward)) (pdf-view--previous-line-or-previous-page arg))) (defun pdf-view-goto-label (label) @@ -1078,7 +1078,7 @@ If PAGE is non-nil return its size instead of current page." (setq page (or page (pdf-view-current-page window))) (unless (memq page (image-mode-window-get 'displayed-pages window)) (pdf-view-display-page page window)) - (overlay-get (image-roll-page-overlay page window) 'display)) + (overlay-get (pdf-roll-page-overlay page window) 'display)) (image-get-display-property)))) (if displayed-p (image-display-size display-prop t) @@ -1167,7 +1167,7 @@ If WINDOW is t, redisplay pages in all windows." (defun pdf-view-redisplay (&optional window) (if pdf-view-roll-minor-mode - (image-roll-redisplay window) + (pdf-roll-redisplay window) (pdf-view--redisplay window))) (defun pdf-view-redisplay-pages (&rest pages) @@ -1557,10 +1557,10 @@ Stores the region in `pdf-view-active-region'." (if pdf-view-roll-minor-mode (cond ((and (> dy 0) (< (- (window-text-height window t) y) 20)) - (image-roll-scroll-forward + (pdf-roll-scroll-forward (min 20 (or (nth 3 (pos-visible-in-window-p (posn-point pos) window t)) 0)))) ((and (< dy 0) (< (- y (window-header-line-height window)) 20)) - (image-roll-scroll-backward + (pdf-roll-scroll-backward (min 20 (or (nth 2 (pos-visible-in-window-p (posn-point pos) window t)) 0))))) (pdf-util-scroll-to-edges iregion)))))) (setq pdf-view-active-region From 81b004c5e79c0ff776a3cee96dbd966e125d260e Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Mon, 7 Aug 2023 16:20:12 +0200 Subject: [PATCH 083/104] Cleanup Remove `pdf-roll-scroll-mouse-wheel` and its bindings Follow `pdf-tools` behavior more closely in scrolling Reinitialize mode when buffer is reverted --- lisp/pdf-roll.el | 74 ++++++++++++++++++------------------------------ 1 file changed, 27 insertions(+), 47 deletions(-) diff --git a/lisp/pdf-roll.el b/lisp/pdf-roll.el index 05f981d4..8e34b8e1 100644 --- a/lisp/pdf-roll.el +++ b/lisp/pdf-roll.el @@ -31,7 +31,7 @@ ;;; Custom Variables (defgroup pdf-roll nil "Image roll configurations." - :group 'applications) + :group 'pdf-view) (defface pdf-roll-default `((t :font ,(font-spec :family "monospace" :size 1))) "Default face for image roll documents.") @@ -45,23 +45,11 @@ :type 'color :set (lambda (_ color) (put 'pdf-roll-margin 'face `(:background ,color)))) -(defcustom pdf-roll-step-size 50 - "Scroll step size in pixels units." - :type 'integer) - ;;; Variables -(defvar-local pdf-roll-last-page 0) -(defvar-local pdf-roll--state nil +(defvar pdf-roll--state nil "Local variable that tracks window, point and vscroll to handle changes.") ;;; Utility Macros and functions -(defmacro pdf-roll-current-page (&optional window) - "Return the page number of the currently displayed page in WINDOW. -The current page is the page that overlaps with the window -start (this choice was made in order to simplify the scrolling -logic)" - `(image-mode-window-get 'page ,window)) - (defsubst pdf-roll-page-to-pos (page) "Get the buffer position displaing PAGE." (- (* 4 page) 3)) @@ -99,9 +87,11 @@ If INHIBIT-SLICE-P is non-nil, disregard `pdf-view-current-slice'." image) image)) -(defun pdf-roll--display-image (image page window) - "Display IMAGE for PAGE in WINDOW." - (let* ((size (image-display-size image t)) +(defun pdf-roll-display-image (image page &optional window inhibit-slice-p) + "Display IMAGE for PAGE in WINDOW. +If INHIBIT-SLICE-P is non-nil, disregard `pdf-view-current-slice'." + (let* ((image (pdf-roll-maybe-slice-image image window inhibit-slice-p)) + (size (image-display-size image t)) (overlay (pdf-roll-page-overlay page window)) (margin-pos (+ (pdf-roll-page-to-pos page) 2)) (margin-overlay (pdf-roll--pos-overlay margin-pos window)) @@ -114,12 +104,6 @@ If INHIBIT-SLICE-P is non-nil, disregard `pdf-view-current-slice'." (overlay-put margin-overlay 'before-string before-string) (cdr size))) -(defun pdf-roll-display-image (image page &optional window inhibit-slice-p) - "Display IMAGE for PAGE in WINDOW. -If INHIBIT-SLICE-P is non-nil, disregard `pdf-view-current-slice'." - (let ((image (pdf-roll-maybe-slice-image image window inhibit-slice-p))) - (pdf-roll--display-image image page window))) - (defun pdf-roll-page-image (page window) "Function to retrieve image of the PAGE in WINDOW." (pdf-roll-maybe-slice-image (pdf-view-create-page page window) window)) @@ -171,7 +155,7 @@ minor mode commands, after erasing the buffer to create the overlays." (setq win (or (and (windowp win) win) (selected-window))) (if (not (overlays-at 1)) - (let ((pages pdf-roll-last-page) + (let ((pages (pdf-cache-number-of-pages)) (inhibit-read-only t)) (erase-buffer) (setq pdf-roll--state (list t)) @@ -193,7 +177,7 @@ overlays." (remove-overlays (point-min) (point-max) 'window win-old))) (cl-callf2 cl-delete-if-not #'window-live-p pdf-roll--state :key #'car-safe))) ;; initial `pdf-roll-redisplay' needs to know which page(s) to display - (cl-callf or (pdf-roll-current-page win) 1) + (cl-callf or (pdf-view-current-page win) 1) (cl-callf or (image-mode-window-get 'vscroll win) 0)) (defun pdf-roll-redisplay (&optional window) @@ -210,7 +194,7 @@ It should be added to `pre-redisplay-functions' buffer locally." (unless (pdf-roll-page-overlay 1 win) (pdf-roll-new-window-function win)) (let* ((state (alist-get win pdf-roll--state)) - (page (pdf-roll-current-page win)) + (page (pdf-view-current-page win)) (height (window-pixel-height win)) (vscroll (image-mode-window-get 'vscroll win)) (size-changed (not (and (eq height (nth 1 state)) @@ -248,9 +232,9 @@ It should be added to `pre-redisplay-functions' buffer locally." (prefix-numeric-value current-prefix-arg) (read-number "Page: ")))) (unless (and (>= page 1) - (<= page pdf-roll-last-page)) + (<= page (pdf-cache-number-of-pages))) (error "No such page: %d" page)) - (setf (pdf-roll-current-page window) page) + (setf (pdf-view-current-page window) page) (pdf-roll-set-vscroll 0 window)) (defun pdf-roll-next-page (&optional n) @@ -270,8 +254,8 @@ By default PIXELS is `pdf-roll-step-size'. When PIXELS is negative scroll backward instead. With a prefix arg PIXELS is the numeric value times `pdf-roll-step-size'." - (interactive (list (* (prefix-numeric-value current-prefix-arg) pdf-roll-step-size))) - (setq pixels (or pixels pdf-roll-step-size)) + (interactive (list (* (prefix-numeric-value current-prefix-arg) (frame-char-height)))) + (setq pixels (or pixels (frame-char-height))) (setq window (or window (selected-window))) (when (> 0 pixels) (pdf-roll-scroll-backward (- pixels) window)) (let ((pos (goto-char (window-start window)))) @@ -287,7 +271,7 @@ With a prefix arg PIXELS is the numeric value times `pdf-roll-step-size'." (message "End of buffer")) (cl-decf pixels occupied-pixels)))) (forward-char 4)) - (setf (pdf-roll-current-page window) (pdf-roll-page-at-current-pos)) + (setf (pdf-view-current-page window) (pdf-roll-page-at-current-pos)) (pdf-roll-set-vscroll (+ (if (eq pos (point)) (window-vscroll window t) 0) pixels) window))) @@ -297,8 +281,8 @@ By default PIXELS is `pdf-roll-step-size'. When PIXELS is negative scroll forward instead. With a prefix arg PIXELS is the numeric value times `pdf-roll-step-size'." - (interactive (list (* (prefix-numeric-value current-prefix-arg) pdf-roll-step-size))) - (setq pixels (or pixels pdf-roll-step-size)) + (interactive (list (* (prefix-numeric-value current-prefix-arg) (frame-char-height)))) + (setq pixels (or pixels (frame-char-height))) (setq window (or window (selected-window))) (when (> 0 pixels) (pdf-roll-scroll-backward (- pixels) window)) (goto-char (window-start window)) @@ -317,7 +301,7 @@ With a prefix arg PIXELS is the numeric value times `pdf-roll-step-size'." (cl-decf pixels (line-pixel-height))) (> pixels 0))) (pdf-roll-set-vscroll (- pixels) window))) - (setf (pdf-roll-current-page window) (pdf-roll-page-at-current-pos))) + (setf (pdf-view-current-page window) (pdf-roll-page-at-current-pos))) (defun pdf-roll-scroll-screen-forward (&optional arg) "Scroll forward by (almost) ARG many full screens." @@ -331,15 +315,6 @@ With a prefix arg PIXELS is the numeric value times `pdf-roll-step-size'." (pdf-roll-scroll-backward (- (* (window-text-height nil t) arg) (* next-screen-context-lines (frame-char-height))))) -(defun pdf-roll-scroll-mouse-wheel (event) - "Scroll according to mouse wheel EVENT." - (interactive "e") - (with-selected-window (posn-window (event-start event)) - (pcase (event-basic-type event) - ('wheel-down (pdf-roll-scroll-forward)) - ('wheel-up (pdf-roll-scroll-backward)) - (_ (error "Event must be wheel down or wheel up event"))))) - ;;; Minor mode ;;;###autoload (define-minor-mode pdf-view-roll-minor-mode @@ -351,16 +326,13 @@ With a prefix arg PIXELS is the numeric value times `pdf-roll-step-size'." (define-key map [remap pdf-view-scroll-down-or-previous-page] 'pdf-roll-scroll-backward) (define-key map [remap pdf-view-scroll-up-or-next-page] 'pdf-roll-scroll-forward) (define-key map [remap mouse-set-point] 'ignore) - (define-key map (kbd "") 'pdf-roll-scroll-mouse-wheel) - (define-key map (kbd "") 'pdf-roll-scroll-mouse-wheel) (define-key map (kbd "S-") 'pdf-roll-scroll-screen-forward) (define-key map (kbd "S-") 'pdf-roll-scroll-screen-backward) map) :version 28.1 (cond (pdf-view-roll-minor-mode - (setq-local pdf-roll-last-page (pdf-cache-number-of-pages) - face-remapping-alist '((default . pdf-roll-default)) + (setq-local face-remapping-alist '((default . pdf-roll-default)) mwheel-scroll-up-function #'pdf-roll-scroll-forward mwheel-scroll-down-function #'pdf-roll-scroll-backward) @@ -371,6 +343,10 @@ With a prefix arg PIXELS is the numeric value times `pdf-roll-step-size'." (add-hook 'pre-redisplay-functions 'pdf-roll-pre-redisplay nil t) (add-hook 'pdf-roll-after-change-page-hook 'pdf-history-before-change-page-hook nil t) + (add-function :after (local 'revert-buffer-function) #'pdf-view-roll-minor-mode) + + (make-local-variable 'pdf-roll--state) + (let ((inhibit-read-only t)) (erase-buffer) (remove-overlays)) @@ -383,9 +359,13 @@ With a prefix arg PIXELS is the numeric value times `pdf-roll-step-size'." (add-hook 'window-configuration-change-hook 'pdf-view-redisplay-some-windows nil t) (add-hook 'image-mode-new-window-functions #'pdf-view-new-window-function nil t) + (remove-function (local 'revert-buffer-function) #'pdf-view-roll-minor-mode) + (remove-hook 'pre-redisplay-functions 'pdf-roll-pre-redisplay t) (remove-hook 'pdf-roll-after-change-page-hook 'pdf-history-before-change-page-hook t) + (kill-local-variable 'pdf-roll--state) + (let ((inhibit-read-only t)) (erase-buffer) (remove-overlays) From aadd40640588c15f0fd583787c25b60abcb02e47 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Mon, 7 Aug 2023 17:33:53 +0200 Subject: [PATCH 084/104] Fix annotations and text selection by tracking page number This changes the format of pdf-view-active-region to (cons page list-of-edges). The page number is needed since when multiple pages are displayed the active region is not necessarily on current page. --- lisp/pdf-annot.el | 9 +++++---- lisp/pdf-view.el | 43 ++++++++++++++++++++++--------------------- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/lisp/pdf-annot.el b/lisp/pdf-annot.el index d40210cd..09ddfde3 100644 --- a/lisp/pdf-annot.el +++ b/lisp/pdf-annot.el @@ -1156,11 +1156,12 @@ EV describes the captured mouse event." "Click where a new text annotation should be added ...")) (event-start ev)))) -(defun pdf-annot-add-markup-annotation (list-of-edges type &optional color +(defun pdf-annot-add-markup-annotation (region type &optional color property-alist) "Add a new markup annotation in the selected window. -LIST-OF-EDGES determines the marked up area and should be a list +REGION determines the marked up area and should be a cons cell +\(PAGE . LIST-OF-EDGES\) where LIST-OF-EDGES should be list of \(LEFT TOP RIGHT BOT\), each value a relative coordinate. TYPE should be one of `squiggly', `underline', `strike-out' or @@ -1183,7 +1184,7 @@ Return the new annotation." (pdf-util-assert-pdf-window) (pdf-annot-add-annotation type - list-of-edges + (cdr region) (pdf-annot-merge-alists (and color `((color . ,color))) property-alist @@ -1192,7 +1193,7 @@ Return the new annotation." (when pdf-annot-color-history `((color . ,(car pdf-annot-color-history)))) '((color . "#ffff00"))) - (pdf-view-current-page))) + (car region))) (defun pdf-annot-add-squiggly-markup-annotation (list-of-edges &optional color property-alist) diff --git a/lisp/pdf-view.el b/lisp/pdf-view.el index f890bc6f..b49170d2 100644 --- a/lisp/pdf-view.el +++ b/lisp/pdf-view.el @@ -230,7 +230,7 @@ Must be one of `glyph', `word', or `line'." ;; * ================================================================== * (defvar-local pdf-view-active-region nil - "The active region as a list of edges. + "The active region as a cons cell of page and list of edges. Edge values are relative coordinates.") @@ -1174,8 +1174,9 @@ If WINDOW is t, redisplay pages in all windows." "Redisplay PAGES in all windows." (pdf-util-assert-pdf-buffer) (dolist (window (get-buffer-window-list nil nil t)) - (when (memq (pdf-view-current-page window) - pages) + (when (cl-some (lambda (page) (memq page pages)) + (or (image-mode-window-get 'displayed-pages window) + (list (pdf-view-current-page window)))) (pdf-view-redisplay window)))) (defun pdf-view-maybe-redisplay-resized-windows () @@ -1445,7 +1446,7 @@ supersede hotspots in lower ones." (setq deactivate-mark nil)) (defun pdf-view-active-region (&optional deactivate-p) - "Return the active region, a list of edges. + "Return the active region, as a cons cell of page number and list of edges. Deactivate the region if DEACTIVATE-P is non-nil." (pdf-view-assert-active-region) @@ -1495,10 +1496,13 @@ Stores the region in `pdf-view-active-region'." (setq begin-inside-image-p nil) (posn-x-y pos))) (abs-begin (posn-x-y pos)) - (page (/ (+ 3 (posn-point pos)) 4)) + (page (if pdf-view-roll-minor-mode + (/ (+ 3 (posn-point pos)) 4) + (pdf-view-current-page))) (selection-style (or selection-style pdf-view-selection-style)) pdf-view-continuous region) + (setq pdf-view-active-region (list page)) (when (pdf-util-track-mouse-dragging (event 0.05) (let* ((pos (event-start event)) (end (posn-object-x-y pos)) @@ -1550,10 +1554,9 @@ Stores the region in `pdf-view-active-region'." (setq region (pdf-util-scale-pixel-to-relative iregion)) (pdf-view-display-region - (cons region pdf-view-active-region) + (cons page (cons region (cdr pdf-view-active-region))) rectangle-p - selection-style - page) + selection-style) (if pdf-view-roll-minor-mode (cond ((and (> dy 0) (< (- (window-text-height window t) y) 20)) @@ -1563,9 +1566,7 @@ Stores the region in `pdf-view-active-region'." (pdf-roll-scroll-backward (min 20 (or (nth 2 (pos-visible-in-window-p (posn-point pos) window t)) 0))))) (pdf-util-scroll-to-edges iregion)))))) - (setq pdf-view-active-region - (append pdf-view-active-region - (list region))) + (cl-callf append (cdr pdf-view-active-region) (list region)) (pdf-view--push-mark)))) (defun pdf-view-mouse-extend-region (event) @@ -1584,7 +1585,7 @@ This is more useful for commands like (interactive "@e") (pdf-view-mouse-set-region event nil t)) -(defun pdf-view-display-region (&optional region rectangle-p selection-style page) +(defun pdf-view-display-region (&optional region rectangle-p selection-style) ;; TODO: write documentation! (unless region (pdf-view-assert-active-region) @@ -1592,7 +1593,7 @@ This is more useful for commands like (let ((colors (pdf-util-face-colors (if rectangle-p 'pdf-view-rectangle 'pdf-view-region) (bound-and-true-p pdf-view-dark-minor-mode))) - (page (or page (pdf-view-current-page))) + (page (car region)) (width (car (pdf-view-image-size)))) (pdf-view-display-image (pdf-view-create-image @@ -1602,7 +1603,7 @@ This is more useful for commands like `(,(car colors) ,(cdr colors) 0.35 ,@region)) (pdf-info-renderpage-text-regions page width nil selection-style nil - `(,(car colors) ,(cdr colors) ,@region))) + `(,(car colors) ,(cdr colors) ,@(cdr region)))) :width width) (when pdf-view-roll-minor-mode page)))) @@ -1619,7 +1620,7 @@ This is more useful for commands like (interactive) (pdf-view-deactivate-region) (setq pdf-view-active-region - (list (list 0 0 1 1))) + (cons (pdf-view-current-page) (list (list 0 0 1 1)))) (pdf-view--push-mark) (pdf-view-display-region)) @@ -1629,10 +1630,10 @@ This is more useful for commands like (mapcar (lambda (edges) (pdf-info-gettext - (pdf-view-current-page) + (car pdf-view-active-region) edges pdf-view-selection-style)) - pdf-view-active-region)) + (cdr pdf-view-active-region))) (defun pdf-view-extract-region-image (regions &optional page size output-buffer no-display-p) @@ -1654,11 +1655,11 @@ the `convert' program is used." (interactive (list (if (pdf-view-active-region-p) (pdf-view-active-region t) - '((0 0 1 1))))) + '(,(pdf-view-current-page) (0 0 1 1))))) (unless page - (setq page (pdf-view-current-page))) + (setq page (car regions))) (unless size - (setq size (pdf-view-image-size))) + (setq size (pdf-view-image-size nil nil page))) (unless output-buffer (setq output-buffer (get-buffer-create "*PDF image*"))) (let* ((images (mapcar (lambda (edges) @@ -1670,7 +1671,7 @@ the `convert' program is used." :crop-to edges) nil file nil 'no-message) file)) - regions)) + (cdr regions))) result) (unwind-protect (progn From 1310f85473be725cb6e94403cff7dbb2ef12c25a Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Mon, 7 Aug 2023 17:50:39 +0200 Subject: [PATCH 085/104] Don't hardcode numbers --- lisp/pdf-view.el | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lisp/pdf-view.el b/lisp/pdf-view.el index b49170d2..9befef32 100644 --- a/lisp/pdf-view.el +++ b/lisp/pdf-view.el @@ -1499,6 +1499,7 @@ Stores the region in `pdf-view-active-region'." (page (if pdf-view-roll-minor-mode (/ (+ 3 (posn-point pos)) 4) (pdf-view-current-page))) + (margin (frame-char-height)) (selection-style (or selection-style pdf-view-selection-style)) pdf-view-continuous region) @@ -1559,12 +1560,14 @@ Stores the region in `pdf-view-active-region'." selection-style) (if pdf-view-roll-minor-mode (cond - ((and (> dy 0) (< (- (window-text-height window t) y) 20)) + ((and (> dy 0) (< (- (window-text-height window t) y) margin)) (pdf-roll-scroll-forward - (min 20 (or (nth 3 (pos-visible-in-window-p (posn-point pos) window t)) 0)))) - ((and (< dy 0) (< (- y (window-header-line-height window)) 20)) + (min margin + (or (nth 3 (pos-visible-in-window-p (posn-point pos) window t)) 0)))) + ((and (< dy 0) (< (- y (window-header-line-height window)) margin)) (pdf-roll-scroll-backward - (min 20 (or (nth 2 (pos-visible-in-window-p (posn-point pos) window t)) 0))))) + (min margin + (or (nth 2 (pos-visible-in-window-p (posn-point pos) window t)) 0))))) (pdf-util-scroll-to-edges iregion)))))) (cl-callf append (cdr pdf-view-active-region) (list region)) (pdf-view--push-mark)))) From 7982e8f93d0f146424f1d8c3758cb836a5989507 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Tue, 8 Aug 2023 11:11:14 +0200 Subject: [PATCH 086/104] Bring scrolling behavior in line with image-mode/pdf-tools --- lisp/pdf-roll.el | 58 +++++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/lisp/pdf-roll.el b/lisp/pdf-roll.el index 8e34b8e1..c5f51c92 100644 --- a/lisp/pdf-roll.el +++ b/lisp/pdf-roll.el @@ -248,72 +248,74 @@ It should be added to `pre-redisplay-functions' buffer locally." (pdf-roll-next-page (- n))) ;;; Scrolling Commands -(defun pdf-roll-scroll-forward (&optional pixels window) - "Scroll image PIXELS forward in WINDOW. -By default PIXELS is `pdf-roll-step-size'. When PIXELS is negative scroll -backward instead. - -With a prefix arg PIXELS is the numeric value times `pdf-roll-step-size'." - (interactive (list (* (prefix-numeric-value current-prefix-arg) (frame-char-height)))) - (setq pixels (or pixels (frame-char-height))) +(defun pdf-roll-scroll-forward (&optional n window pixels) + "Scroll image N lines forward in WINDOW. +Line height is determined by `frame-char-height'. When N is negative +scroll backward instead. With a prefix arg N is its numeric value. + +If PIXELS is non-nil N is number of pixels instead of lines." + (interactive (list (prefix-numeric-value current-prefix-arg))) + (setq n (* (or n 1) (if pixels 1 (frame-char-height)))) (setq window (or window (selected-window))) - (when (> 0 pixels) (pdf-roll-scroll-backward (- pixels) window)) + (when (> 0 n) (pdf-roll-scroll-backward (- n) window)) (let ((pos (goto-char (window-start window)))) (while (let* ((data (pos-visible-in-window-p (point) window t)) (occupied-pixels (cond ((nth 2 data) (nth 4 data)) (data (line-pixel-height)) (t (pdf-roll-display-page (pdf-roll-page-at-current-pos) window))))) - (and (>= pixels occupied-pixels) + (and (>= n occupied-pixels) (if (eq (point) (1- (point-max))) (prog1 nil - (setq pixels (- occupied-pixels 10)) + (setq n (- occupied-pixels 10)) (message "End of buffer")) - (cl-decf pixels occupied-pixels)))) + (cl-decf n occupied-pixels)))) (forward-char 4)) (setf (pdf-view-current-page window) (pdf-roll-page-at-current-pos)) - (pdf-roll-set-vscroll (+ (if (eq pos (point)) (window-vscroll window t) 0) pixels) + (pdf-roll-set-vscroll (+ (if (eq pos (point)) (window-vscroll window t) 0) n) window))) -(defun pdf-roll-scroll-backward (&optional pixels window) - "Scroll image PIXELS backwards in WINDOW. -By default PIXELS is `pdf-roll-step-size'. When PIXELS is negative scroll -forward instead. +(defun pdf-roll-scroll-backward (&optional n window pixels) + "Scroll image N lines backwards in WINDOW. +Line height is determined by `frame-char-height'. When N is negative +scroll forward instead. With a prefix arg N is its numeric value. -With a prefix arg PIXELS is the numeric value times `pdf-roll-step-size'." +If PIXELS is non-nil N is number of pixels instead of lines." (interactive (list (* (prefix-numeric-value current-prefix-arg) (frame-char-height)))) - (setq pixels (or pixels (frame-char-height))) + (setq n (* (or n 1) (if pixels 1 (frame-char-height)))) (setq window (or window (selected-window))) - (when (> 0 pixels) (pdf-roll-scroll-backward (- pixels) window)) + (when (> 0 n) (pdf-roll-scroll-backward (- n) window)) (goto-char (window-start window)) (let* ((data (pos-visible-in-window-p (point) window t)) (pixels-top (if (nth 2 data) (nth 2 data) 0))) - (if (< pixels pixels-top) - (pdf-roll-set-vscroll (- (window-vscroll window t) pixels) + (if (< n pixels-top) + (pdf-roll-set-vscroll (- (window-vscroll window t) n) window) - (cl-decf pixels pixels-top) + (cl-decf n pixels-top) (while (and (if (bobp) (prog1 nil (message "Beginning of buffer.")) t) (progn (forward-char -4) (pdf-roll-display-page (pdf-roll-page-at-current-pos) window) - (cl-decf pixels (line-pixel-height))) - (> pixels 0))) - (pdf-roll-set-vscroll (- pixels) window))) + (cl-decf n (line-pixel-height))) + (> n 0))) + (pdf-roll-set-vscroll (- n) window))) (setf (pdf-view-current-page window) (pdf-roll-page-at-current-pos))) (defun pdf-roll-scroll-screen-forward (&optional arg) "Scroll forward by (almost) ARG many full screens." (interactive "p") (pdf-roll-scroll-forward - (- (* (window-text-height nil t) arg) (* next-screen-context-lines (frame-char-height))))) + (- (* (window-text-height nil t) arg) (* next-screen-context-lines (frame-char-height))) + nil t)) (defun pdf-roll-scroll-screen-backward (&optional arg) "Scroll backward by (almost) ARG many full screens." (interactive "p") (pdf-roll-scroll-backward - (- (* (window-text-height nil t) arg) (* next-screen-context-lines (frame-char-height))))) + (- (* (window-text-height nil t) arg) (* next-screen-context-lines (frame-char-height))) + nil t)) ;;; Minor mode ;;;###autoload From 2901056ae8e73488e8ead0295eb3878ce6b198cd Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Tue, 8 Aug 2023 16:19:52 +0200 Subject: [PATCH 087/104] Get h/vscroll with image-mode-window-get if buffer is not displayed This affects bookmarks made when buffer is not displayed. It also affects save-place-pdf-view. --- lisp/pdf-util.el | 12 ++++++++---- lisp/pdf-view.el | 3 ++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/lisp/pdf-util.el b/lisp/pdf-util.el index 35b1f2c4..a1f5e461 100644 --- a/lisp/pdf-util.el +++ b/lisp/pdf-util.el @@ -160,7 +160,7 @@ See also `pdf-util-scale'." The result depends on the currently displayed page in WINDOW. See also `pdf-util-scale'." - (pdf-util-assert-pdf-window window) + (when displayed-p (pdf-util-assert-pdf-window window)) (pdf-util-scale-to list-of-pixel-edges (pdf-view-image-size displayed-p window) @@ -313,15 +313,19 @@ depending on the input." "Return the visible region of the image in WINDOW. Returns a list of pixel edges." - (pdf-util-assert-pdf-window) + (when displayed-p (pdf-util-assert-pdf-window)) (let* ((edges (window-inside-pixel-edges window)) (isize (pdf-view-image-size displayed-p window)) (offset (if displayed-p `(0 . 0) (pdf-view-image-offset window))) - (hscroll (* (window-hscroll window) + (hscroll (* (if displayed-p + (window-hscroll window) + (image-mode-window-get 'hscroll window)) (frame-char-width (window-frame window)))) - (vscroll (window-vscroll window t)) + (vscroll (if displayed-p + (window-vscroll window t) + (image-mode-window-get 'vscroll window))) (x0 (+ hscroll (car offset))) (y0 (+ vscroll (cdr offset))) (x1 (min (car isize) diff --git a/lisp/pdf-view.el b/lisp/pdf-view.el index 9befef32..e749c35c 100644 --- a/lisp/pdf-view.el +++ b/lisp/pdf-view.el @@ -1747,7 +1747,8 @@ The optional, boolean args exclude certain attributes." ,(unless no-origin (cons 'origin (and win - (let* ((edges (pdf-util-image-displayed-edges win t))) + (let* ((edges (pdf-util-image-displayed-edges + win (eq (window-buffer win) (current-buffer))))) (pdf-util-scale-pixel-to-relative (cons (car edges) (cadr edges)) nil (eq (current-buffer) (window-buffer)) win))))) From 8d8df39bba563995c26f7171fc33d01850f33bf5 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Tue, 8 Aug 2023 20:26:06 +0200 Subject: [PATCH 088/104] Support pixel-scroll-precision-mode in Emacs 29 Also be sparing in use of `pos-visible-in-window-p`, it seems to cause a lot of allocations. --- lisp/pdf-roll.el | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/lisp/pdf-roll.el b/lisp/pdf-roll.el index c5f51c92..3b6ab858 100644 --- a/lisp/pdf-roll.el +++ b/lisp/pdf-roll.el @@ -117,16 +117,24 @@ With FORCE non-nil display fetch page again even if it is already displayed." page window) (cdr (image-display-size display t)))) -(defun pdf-roll-display-pages (page &optional window force) +(defun pdf-roll-display-pages (page &optional window force pscrolling) "Display pages to fill the WINDOW starting from PAGE. If FORCE is non-nill redisplay a page even if it is already displayed." - (let (displayed) - (pdf-roll-set-vscroll (min (image-mode-window-get 'vscroll window) - (1- (pdf-roll-display-page page window force))) - window) + (let (displayed + (available-height (window-pixel-height window))) + (when (and pscrolling (> page 1)) + (pdf-roll-display-page (1- page) window force) + (push (1- page) displayed)) + (let ((vscroll (image-mode-window-get 'vscroll window)) + (im-height (pdf-roll-display-page page window force))) + (pdf-roll-set-vscroll (min vscroll (1- im-height)) window) + (cl-callf - available-height (- im-height (window-vscroll window t)))) (push page displayed) - (while (pos-visible-in-window-p (pdf-roll-page-to-pos (cl-incf page)) window t) - (pdf-roll-display-page page window force) + (while (and (> available-height 0) (< page (pdf-cache-number-of-pages))) + (cl-callf - available-height (pdf-roll-display-page (cl-incf page) window force)) + (push page displayed)) + (when (and pscrolling (< page (pdf-cache-number-of-pages))) + (pdf-roll-display-page (cl-incf page) window force) (push page displayed)) ;; store displayed images for determining which images to update when update ;; is triggered @@ -194,7 +202,12 @@ It should be added to `pre-redisplay-functions' buffer locally." (unless (pdf-roll-page-overlay 1 win) (pdf-roll-new-window-function win)) (let* ((state (alist-get win pdf-roll--state)) - (page (pdf-view-current-page win)) + (pscrolling (memq last-command + '(pixel-scroll-precision pixel-scroll-start-momentum + pixel-scroll-interpolate-up pixel-scroll-interpolate-down))) + (page (progn (when pscrolling + (setf (pdf-view-current-page) (/ (+ (window-start win) 5) 4))) + (pdf-view-current-page win))) (height (window-pixel-height win)) (vscroll (image-mode-window-get 'vscroll win)) (size-changed (not (and (eq height (nth 1 state)) @@ -202,13 +215,16 @@ It should be added to `pre-redisplay-functions' buffer locally." (page-changed (not (eq page (nth 0 state)))) (vscroll-changed (not (eq vscroll (nth 3 state)))) (start (pdf-roll-page-to-pos page))) - (set-window-vscroll win vscroll t) - (set-window-hscroll win (or (image-mode-window-get 'hscroll win) 0)) - (set-window-start win start t) + (if pscrolling + (progn (image-mode-window-put 'vscroll (window-vscroll win t)) + (image-mode-window-put 'hscroll (window-hscroll win))) + (set-window-vscroll win vscroll t) + (set-window-hscroll win (or (image-mode-window-get 'hscroll win) 0)) + (set-window-start win start t)) (setq disable-point-adjustment t) (when (or size-changed page-changed vscroll-changed) (let ((old (image-mode-window-get 'displayed-pages win)) - (new (pdf-roll-display-pages page win size-changed))) + (new (pdf-roll-display-pages page win size-changed pscrolling))) ;; If images/pages are small enough (or after jumps), there ;; might be multiple image that need to get updated (pdf-roll-undisplay-pages (cl-set-difference old new) win) From b42fad6d279f0081751c9bc0ca1bc1632206d53d Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Tue, 8 Aug 2023 21:01:41 +0200 Subject: [PATCH 089/104] Deal with nil --- lisp/pdf-util.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lisp/pdf-util.el b/lisp/pdf-util.el index a1f5e461..cca7d0d7 100644 --- a/lisp/pdf-util.el +++ b/lisp/pdf-util.el @@ -321,11 +321,11 @@ Returns a list of pixel edges." (pdf-view-image-offset window))) (hscroll (* (if displayed-p (window-hscroll window) - (image-mode-window-get 'hscroll window)) + (or (image-mode-window-get 'hscroll window) 0)) (frame-char-width (window-frame window)))) (vscroll (if displayed-p (window-vscroll window t) - (image-mode-window-get 'vscroll window))) + (or (image-mode-window-get 'vscroll window) 0))) (x0 (+ hscroll (car offset))) (y0 (+ vscroll (cdr offset))) (x1 (min (car isize) From f3497d8460aa0b4c32bfe72a3d03004c3102a49b Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Wed, 9 Aug 2023 17:55:56 +0200 Subject: [PATCH 090/104] Pass on win parameter to image-mode-window-get-call This is needed to make scrolling work even when the pdf window is not selected. --- lisp/pdf-roll.el | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lisp/pdf-roll.el b/lisp/pdf-roll.el index 3b6ab858..78e890e3 100644 --- a/lisp/pdf-roll.el +++ b/lisp/pdf-roll.el @@ -206,7 +206,7 @@ It should be added to `pre-redisplay-functions' buffer locally." '(pixel-scroll-precision pixel-scroll-start-momentum pixel-scroll-interpolate-up pixel-scroll-interpolate-down))) (page (progn (when pscrolling - (setf (pdf-view-current-page) (/ (+ (window-start win) 5) 4))) + (setf (pdf-view-current-page win) (/ (+ (window-start win) 5) 4))) (pdf-view-current-page win))) (height (window-pixel-height win)) (vscroll (image-mode-window-get 'vscroll win)) @@ -216,8 +216,8 @@ It should be added to `pre-redisplay-functions' buffer locally." (vscroll-changed (not (eq vscroll (nth 3 state)))) (start (pdf-roll-page-to-pos page))) (if pscrolling - (progn (image-mode-window-put 'vscroll (window-vscroll win t)) - (image-mode-window-put 'hscroll (window-hscroll win))) + (progn (image-mode-window-put 'vscroll (window-vscroll win t) win) + (image-mode-window-put 'hscroll (window-hscroll win)) win) (set-window-vscroll win vscroll t) (set-window-hscroll win (or (image-mode-window-get 'hscroll win) 0)) (set-window-start win start t)) From 7b1fe20288bc6b92088de9964a284f0240e05f4a Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Thu, 10 Aug 2023 15:03:16 +0200 Subject: [PATCH 091/104] Enable/disable pixel-scroll-precision-mode along with pdf-roll --- lisp/pdf-roll.el | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lisp/pdf-roll.el b/lisp/pdf-roll.el index 78e890e3..f7412787 100644 --- a/lisp/pdf-roll.el +++ b/lisp/pdf-roll.el @@ -365,6 +365,10 @@ If PIXELS is non-nil N is number of pixels instead of lines." (make-local-variable 'pdf-roll--state) + (when (local-variable-p 'pixel-scroll-precision-mode) + (kill-local-variable 'pixel-scroll-precision-mode) + (kill-local-variable 'mwheel-coalesce-scroll-events)) + (let ((inhibit-read-only t)) (erase-buffer) (remove-overlays)) @@ -384,6 +388,10 @@ If PIXELS is non-nil N is number of pixels instead of lines." (kill-local-variable 'pdf-roll--state) + (when (bound-and-true-p pixel-scroll-precision-mode) + (setq-local pixel-scroll-precision-mode nil) + (setq-local mwheel-coalesce-scroll-events t)) + (let ((inhibit-read-only t)) (erase-buffer) (remove-overlays) From 376702493a39068b08823378c7616d765f38446a Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Thu, 10 Aug 2023 19:58:25 +0200 Subject: [PATCH 092/104] Define pdf-view-roll-minor-mode in pdf-view Due to pervasive nature of changes the variable needs to be consulted at multiple places this streamlines that. Also use context-pixels if used in required-vscroll --- lisp/pdf-isearch.el | 2 -- lisp/pdf-links.el | 1 - lisp/pdf-util.el | 6 +++--- lisp/pdf-view.el | 2 +- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/lisp/pdf-isearch.el b/lisp/pdf-isearch.el index 2073ca56..2c0dddae 100644 --- a/lisp/pdf-isearch.el +++ b/lisp/pdf-isearch.el @@ -33,8 +33,6 @@ (require 'let-alist) ;;; Code: -(defvar pdf-view-roll-minor-mode) - (defvar pdf-isearch--hl-matches-tick 0) diff --git a/lisp/pdf-links.el b/lisp/pdf-links.el index 674a7653..35420854 100644 --- a/lisp/pdf-links.el +++ b/lisp/pdf-links.el @@ -31,7 +31,6 @@ -(defvar pdf-view-roll-minor-mode) (declare-function pdf-roll-page-overlay "pdf-roll") (declare-function pdf-roll-displayed-pages "pdf-roll") ;;; Code: diff --git a/lisp/pdf-util.el b/lisp/pdf-util.el index cca7d0d7..9e9a3d61 100644 --- a/lisp/pdf-util.el +++ b/lisp/pdf-util.el @@ -350,8 +350,7 @@ needed." (unless context-pixel (setq context-pixel 0)) (let* ((win (window-inside-pixel-edges)) - (image-width (car (pdf-view-image-size (unless pdf-view-roll-minor-mode - t)))) + (image-width (car (pdf-view-image-size t))) (image-left (* (frame-char-width) (window-hscroll))) (edges (pdf-util-translate @@ -392,7 +391,8 @@ pixels. This is because of a change that occurred to `image-mode' in 27." (pdf-util-assert-pdf-window) (if pdf-view-roll-minor-mode (max 0 (- (nth 1 edges) - (* next-screen-context-lines (frame-char-height)))) + (or context-pixel + (* next-screen-context-lines (frame-char-height))))) (let* ((win (window-inside-pixel-edges)) (image-height (cdr (pdf-view-image-size (unless pdf-view-roll-minor-mode diff --git a/lisp/pdf-view.el b/lisp/pdf-view.el index e749c35c..0cde59e5 100644 --- a/lisp/pdf-view.el +++ b/lisp/pdf-view.el @@ -45,7 +45,7 @@ (declare-function pdf-roll-page-at-current-pos "pdf-roll") (declare-function pdf-roll-display-image "pdf-roll") -(defvar pdf-view-roll-minor-mode) +(defvar pdf-view-roll-minor-mode nil) ;; * ================================================================== * From 7084e72304ea1f2671c86366e2e048fab8381f24 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Fri, 11 Aug 2023 11:13:18 +0200 Subject: [PATCH 093/104] Don't try to slice twice --- lisp/pdf-roll.el | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lisp/pdf-roll.el b/lisp/pdf-roll.el index f7412787..6b483d38 100644 --- a/lisp/pdf-roll.el +++ b/lisp/pdf-roll.el @@ -104,16 +104,12 @@ If INHIBIT-SLICE-P is non-nil, disregard `pdf-view-current-slice'." (overlay-put margin-overlay 'before-string before-string) (cdr size))) -(defun pdf-roll-page-image (page window) - "Function to retrieve image of the PAGE in WINDOW." - (pdf-roll-maybe-slice-image (pdf-view-create-page page window) window)) - (defun pdf-roll-display-page (page window &optional force) "Display PAGE in WINDOW. With FORCE non-nil display fetch page again even if it is already displayed." (if-let ((display (overlay-get (pdf-roll-page-overlay page window) 'display)) ((or force (eq (car display) 'space)))) - (pdf-roll-display-image (pdf-roll-page-image page window) + (pdf-roll-display-image (pdf-view-create-page page window) page window) (cdr (image-display-size display t)))) From e43c5c3d60c5adcaf9f4618353832839f95a38f3 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Sat, 12 Aug 2023 07:41:55 +0200 Subject: [PATCH 094/104] Fix interactive spec of pdf-view-scroll-backward --- lisp/pdf-roll.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lisp/pdf-roll.el b/lisp/pdf-roll.el index 6b483d38..37bbf0f5 100644 --- a/lisp/pdf-roll.el +++ b/lisp/pdf-roll.el @@ -293,7 +293,7 @@ Line height is determined by `frame-char-height'. When N is negative scroll forward instead. With a prefix arg N is its numeric value. If PIXELS is non-nil N is number of pixels instead of lines." - (interactive (list (* (prefix-numeric-value current-prefix-arg) (frame-char-height)))) + (interactive (list (prefix-numeric-value current-prefix-arg))) (setq n (* (or n 1) (if pixels 1 (frame-char-height)))) (setq window (or window (selected-window))) (when (> 0 n) (pdf-roll-scroll-backward (- n) window)) From 01a6c1641930037079a683012913f6b16af84593 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Sat, 12 Aug 2023 17:37:08 +0200 Subject: [PATCH 095/104] Make end of document behavior more predictable The intended behavior is: the last page should occupy at least half the window and scrolling past that shouldn't work --- lisp/pdf-roll.el | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/lisp/pdf-roll.el b/lisp/pdf-roll.el index 37bbf0f5..675ba199 100644 --- a/lisp/pdf-roll.el +++ b/lisp/pdf-roll.el @@ -202,7 +202,8 @@ It should be added to `pre-redisplay-functions' buffer locally." '(pixel-scroll-precision pixel-scroll-start-momentum pixel-scroll-interpolate-up pixel-scroll-interpolate-down))) (page (progn (when pscrolling - (setf (pdf-view-current-page win) (/ (+ (window-start win) 5) 4))) + (setf (pdf-view-current-page win) + (/ (min (+ (window-start win) 5) (point-max)) 4))) (pdf-view-current-page win))) (height (window-pixel-height win)) (vscroll (image-mode-window-get 'vscroll win)) @@ -211,7 +212,11 @@ It should be added to `pre-redisplay-functions' buffer locally." (page-changed (not (eq page (nth 0 state)))) (vscroll-changed (not (eq vscroll (nth 3 state)))) (start (pdf-roll-page-to-pos page))) - (if pscrolling + (if (and pscrolling + (or (not (eq start (- (point-max) 3))) + (let ((visible-pixels (nth 4 (pos-visible-in-window-p start win t)))) + (and visible-pixels (> visible-pixels (/ (window-text-height win t) 2)))) + (prog1 nil (message "End of buffer")))) (progn (image-mode-window-put 'vscroll (window-vscroll win t) win) (image-mode-window-put 'hscroll (window-hscroll win)) win) (set-window-vscroll win vscroll t) @@ -276,16 +281,16 @@ If PIXELS is non-nil N is number of pixels instead of lines." (data (line-pixel-height)) (t (pdf-roll-display-page (pdf-roll-page-at-current-pos) window))))) - (and (>= n occupied-pixels) - (if (eq (point) (1- (point-max))) - (prog1 nil - (setq n (- occupied-pixels 10)) - (message "End of buffer")) - (cl-decf n occupied-pixels)))) + (if (eq (point) (- (point-max) 3)) + (prog1 nil + (setq n (min n (max 0 (- occupied-pixels (/ (window-text-height window t) 2))))) + (message "End of buffer")) + (when (>= n occupied-pixels) + (cl-decf n occupied-pixels)))) (forward-char 4)) (setf (pdf-view-current-page window) (pdf-roll-page-at-current-pos)) (pdf-roll-set-vscroll (+ (if (eq pos (point)) (window-vscroll window t) 0) n) - window))) + window))) (defun pdf-roll-scroll-backward (&optional n window pixels) "Scroll image N lines backwards in WINDOW. From e8f0e178dc5266deacb94f5aefeaa13e79392d27 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Sat, 12 Aug 2023 17:38:51 +0200 Subject: [PATCH 096/104] Fix the re-initialization after revert `pdf-view-roll-minor-mode` needs to be reinitialized about reverting, so we add a function to revert-buffer-function. Previously it was `pdf-view-roll-minor-mode` itself but that didn't not have the write calling convention and caused errors. --- lisp/pdf-roll.el | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/lisp/pdf-roll.el b/lisp/pdf-roll.el index 675ba199..4084d864 100644 --- a/lisp/pdf-roll.el +++ b/lisp/pdf-roll.el @@ -336,6 +336,14 @@ If PIXELS is non-nil N is number of pixels instead of lines." ;;; Minor mode ;;;###autoload +(defun pdf-roll-initialize (&rest _args) + "Fun to initialize `pdf-view-roll-minor-mode'. +It is also added to `revert-buffer-function'." + (let ((inhibit-read-only t)) + (erase-buffer) + (remove-overlays)) + (pdf-roll-new-window-function)) + (define-minor-mode pdf-view-roll-minor-mode "If enabled display document on a virtual scroll providing continuous scrolling." :lighter " Continuous" @@ -362,7 +370,7 @@ If PIXELS is non-nil N is number of pixels instead of lines." (add-hook 'pre-redisplay-functions 'pdf-roll-pre-redisplay nil t) (add-hook 'pdf-roll-after-change-page-hook 'pdf-history-before-change-page-hook nil t) - (add-function :after (local 'revert-buffer-function) #'pdf-view-roll-minor-mode) + (add-function :after (local 'revert-buffer-function) #'pdf-roll-initialize) (make-local-variable 'pdf-roll--state) @@ -370,10 +378,7 @@ If PIXELS is non-nil N is number of pixels instead of lines." (kill-local-variable 'pixel-scroll-precision-mode) (kill-local-variable 'mwheel-coalesce-scroll-events)) - (let ((inhibit-read-only t)) - (erase-buffer) - (remove-overlays)) - (pdf-roll-new-window-function)) + (pdf-roll-initialize)) (t (setq-local mwheel-scroll-up-function #'pdf-view-scroll-up-or-next-page mwheel-scroll-down-function #'pdf-view-scroll-down-or-previous-page) @@ -382,7 +387,7 @@ If PIXELS is non-nil N is number of pixels instead of lines." (add-hook 'window-configuration-change-hook 'pdf-view-redisplay-some-windows nil t) (add-hook 'image-mode-new-window-functions #'pdf-view-new-window-function nil t) - (remove-function (local 'revert-buffer-function) #'pdf-view-roll-minor-mode) + (remove-function (local 'revert-buffer-function) #'pdf-roll-initialize) (remove-hook 'pre-redisplay-functions 'pdf-roll-pre-redisplay t) (remove-hook 'pdf-roll-after-change-page-hook 'pdf-history-before-change-page-hook t) From 27f46183a9a86615fc753e1a4b98242975040cf7 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Thu, 17 Aug 2023 09:20:19 +0200 Subject: [PATCH 097/104] Move autoload cookie back to the minor mode definition --- lisp/pdf-roll.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lisp/pdf-roll.el b/lisp/pdf-roll.el index 4084d864..d76ebe99 100644 --- a/lisp/pdf-roll.el +++ b/lisp/pdf-roll.el @@ -335,7 +335,6 @@ If PIXELS is non-nil N is number of pixels instead of lines." nil t)) ;;; Minor mode -;;;###autoload (defun pdf-roll-initialize (&rest _args) "Fun to initialize `pdf-view-roll-minor-mode'. It is also added to `revert-buffer-function'." @@ -344,6 +343,7 @@ It is also added to `revert-buffer-function'." (remove-overlays)) (pdf-roll-new-window-function)) +;;;###autoload (define-minor-mode pdf-view-roll-minor-mode "If enabled display document on a virtual scroll providing continuous scrolling." :lighter " Continuous" From d88c02adf8633beb7212257a4588f4d0b77953dd Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Sun, 17 Sep 2023 14:14:12 +0200 Subject: [PATCH 098/104] Don't restore file contents when turning off mode This prevents errors when the buffer is not visiting a file and doesn't seem to cause any problems with normal functionality but might need to be revisited. Also pass window to a call of assert-pdf-window. Lastly, clear displayed pages with initializing mode. --- lisp/pdf-roll.el | 3 +-- lisp/pdf-util.el | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lisp/pdf-roll.el b/lisp/pdf-roll.el index d76ebe99..4a308c14 100644 --- a/lisp/pdf-roll.el +++ b/lisp/pdf-roll.el @@ -341,6 +341,7 @@ It is also added to `revert-buffer-function'." (let ((inhibit-read-only t)) (erase-buffer) (remove-overlays)) + (image-mode-window-put 'displayed-pages nil) (pdf-roll-new-window-function)) ;;;###autoload @@ -399,9 +400,7 @@ It is also added to `revert-buffer-function'." (setq-local mwheel-coalesce-scroll-events t)) (let ((inhibit-read-only t)) - (erase-buffer) (remove-overlays) - (insert-file-contents-literally (buffer-file-name)) (image-mode-window-put 'displayed-pages nil) (pdf-view-new-window-function (list (selected-window))) (set-buffer-modified-p nil))))) diff --git a/lisp/pdf-util.el b/lisp/pdf-util.el index 9e9a3d61..1fb94007 100644 --- a/lisp/pdf-util.el +++ b/lisp/pdf-util.el @@ -313,7 +313,7 @@ depending on the input." "Return the visible region of the image in WINDOW. Returns a list of pixel edges." - (when displayed-p (pdf-util-assert-pdf-window)) + (when displayed-p (pdf-util-assert-pdf-window window)) (let* ((edges (window-inside-pixel-edges window)) (isize (pdf-view-image-size displayed-p window)) (offset (if displayed-p From 12ba622bca3a399e8d298f6ca3cf1c6549525d6c Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Sat, 30 Sep 2023 15:31:11 +0200 Subject: [PATCH 099/104] Use line-prefix to center images This removes jankiness when scrolling --- lisp/pdf-roll.el | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lisp/pdf-roll.el b/lisp/pdf-roll.el index 4a308c14..6122343b 100644 --- a/lisp/pdf-roll.el +++ b/lisp/pdf-roll.el @@ -95,13 +95,12 @@ If INHIBIT-SLICE-P is non-nil, disregard `pdf-view-current-slice'." (overlay (pdf-roll-page-overlay page window)) (margin-pos (+ (pdf-roll-page-to-pos page) 2)) (margin-overlay (pdf-roll--pos-overlay margin-pos window)) - (align-to (when (> (window-width window t) (car size)) - (/ (- (window-width window t) (car size)) 2))) - (before-string (when align-to (propertize " " 'display `(space :align-to (,align-to)))))) + (offset (when (> (window-width window t) (car size)) + `(space :width (,(/ (- (window-width window t) (car size)) 2)))))) (overlay-put overlay 'display image) - (overlay-put overlay 'before-string before-string) + (overlay-put overlay 'line-prefix offset) (overlay-put margin-overlay 'display `(space :width (,(car size)) :height (,pdf-roll-vertical-margin))) - (overlay-put margin-overlay 'before-string before-string) + (overlay-put margin-overlay 'line-prefix offset) (cdr size))) (defun pdf-roll-display-page (page window &optional force) From cca8b1c6826528351e2a5605fcf2074b09c4a6aa Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Thu, 16 Nov 2023 11:16:17 +0100 Subject: [PATCH 100/104] Fix handling of empty search string --- lisp/pdf-isearch.el | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lisp/pdf-isearch.el b/lisp/pdf-isearch.el index 2c0dddae..b576e55a 100644 --- a/lisp/pdf-isearch.el +++ b/lisp/pdf-isearch.el @@ -279,7 +279,6 @@ This is a Isearch interface function." (cond (next-match (setq pdf-isearch-current-match next-match) - (cl-incf pdf-isearch--hl-matches-tick) (pdf-isearch-hl-matches next-match matches nil pages) (pdf-isearch-focus-match next-match) ;; Don't get off track. @@ -314,13 +313,14 @@ This is a Isearch interface function." pdf-isearch-current-matches matches pdf-isearch-current-match match pdf-isearch-current-page page) - (pdf-view-goto-page pdf-isearch-current-page) - (when pdf-isearch-current-match - (pdf-isearch-hl-matches - pdf-isearch-current-match - pdf-isearch-current-matches - nil (image-mode-window-get 'displayed-pages (selected-window)))) + (if pdf-isearch-current-match + (pdf-isearch-hl-matches + pdf-isearch-current-match + pdf-isearch-current-matches + nil (image-mode-window-get 'displayed-pages (selected-window))) + (when pdf-view-roll-minor-mode + (pdf-view-redisplay))) (image-set-window-hscroll hscroll) (image-set-window-vscroll vscroll)))) From 902439c1a066b0c18c36e8af5f7595e834f70bb4 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Fri, 17 Nov 2023 21:26:37 +0100 Subject: [PATCH 101/104] Fix rectangle selection --- lisp/pdf-view.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lisp/pdf-view.el b/lisp/pdf-view.el index 0cde59e5..d6edabdd 100644 --- a/lisp/pdf-view.el +++ b/lisp/pdf-view.el @@ -1603,7 +1603,7 @@ This is more useful for commands like (if rectangle-p (pdf-info-renderpage-highlight page width nil - `(,(car colors) ,(cdr colors) 0.35 ,@region)) + `(,(car colors) ,(cdr colors) 0.35 ,@(cdr region))) (pdf-info-renderpage-text-regions page width nil selection-style nil `(,(car colors) ,(cdr colors) ,@(cdr region)))) From 472c80b2b9ab3153546cec435ae0dae0b15c24da Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Sun, 19 Nov 2023 22:47:38 +0100 Subject: [PATCH 102/104] Avoid moving the point too much during isearch-repeat --- lisp/pdf-isearch.el | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lisp/pdf-isearch.el b/lisp/pdf-isearch.el index b576e55a..9ffc2e41 100644 --- a/lisp/pdf-isearch.el +++ b/lisp/pdf-isearch.el @@ -287,8 +287,14 @@ This is a Isearch interface function." (unless pdf-view-roll-minor-mode (goto-char (1+ (/ (buffer-size) 2))))) ;; Signal success to isearch. + ;; Moving the point is for `pdf-roll'. It ensures that + ;; `re-search-forward' takes us back to the starting point. Otherwise + ;; every call to `isearch-repeat' will increment/decrement the point + ;; and that causes recentering. (if isearch-forward - (re-search-forward ".") + (progn (unless (bobp) (forward-char -1)) + (re-search-forward ".")) + (unless (eobp) (forward-char 1)) (re-search-backward "."))) ((and (not pdf-isearch-narrow-to-page) (not (pdf-isearch-empty-match-p pdf-isearch-current-matches))) From 009e9c9d16bc232c4446e1c0c870a681b647b1c9 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Thu, 29 Feb 2024 12:04:58 +0100 Subject: [PATCH 103/104] Add page information to text annotations --- lisp/pdf-annot.el | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lisp/pdf-annot.el b/lisp/pdf-annot.el index 09ddfde3..b49b18e5 100644 --- a/lisp/pdf-annot.el +++ b/lisp/pdf-annot.el @@ -1087,8 +1087,8 @@ Return the new annotation." (pdf-annot-activate-annotation a)) a)) -(defun pdf-annot-add-text-annotation (pos &optional icon property-alist) - "Add a new text annotation at POS in the selected window. +(defun pdf-annot-add-text-annotation (pos &optional icon property-alist page) + "Add a new text annotation at POS on PAGE in the selected window. POS should be a image position object or a cons \(X . Y\), both being image coordinates. @@ -1116,6 +1116,9 @@ Return the new annotation." (list posn))) (pdf-util-assert-pdf-window) (when (posnp pos) + (setq page (or page + (when pdf-view-roll-minor-mode + (1+ (/ (posn-point pos) 4))))) (setq pos (posn-object-x-y pos))) (let ((isize (pdf-view-image-size)) (x (car pos)) @@ -1140,7 +1143,8 @@ Return the new annotation." property-alist (cdr (assq 'text pdf-annot-default-annotation-properties)) (cdr (assq t pdf-annot-default-annotation-properties)) - `((color . ,(car pdf-annot-color-history)))))))) + `((color . ,(car pdf-annot-color-history)))) + page)))) (defun pdf-annot-mouse-add-text-annotation (ev) "Add a text annotation using the mouse. From d22c98df8ab6e150e5776b8de3fd8d1a88b883c5 Mon Sep 17 00:00:00 2001 From: Rahguzar Date: Mon, 8 Jul 2024 16:07:53 +0200 Subject: [PATCH 104/104] Be more defensive during pre-redisplay --- lisp/pdf-roll.el | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lisp/pdf-roll.el b/lisp/pdf-roll.el index 6122343b..e47063b7 100644 --- a/lisp/pdf-roll.el +++ b/lisp/pdf-roll.el @@ -106,11 +106,10 @@ If INHIBIT-SLICE-P is non-nil, disregard `pdf-view-current-slice'." (defun pdf-roll-display-page (page window &optional force) "Display PAGE in WINDOW. With FORCE non-nil display fetch page again even if it is already displayed." - (if-let ((display (overlay-get (pdf-roll-page-overlay page window) 'display)) - ((or force (eq (car display) 'space)))) - (pdf-roll-display-image (pdf-view-create-page page window) - page window) - (cdr (image-display-size display t)))) + (let ((display (overlay-get (pdf-roll-page-overlay page window) 'display))) + (if (or force (not display) (eq (car display) 'space)) + (pdf-roll-display-image (pdf-view-create-page page window) page window) + (cdr (image-display-size display t))))) (defun pdf-roll-display-pages (page &optional window force pscrolling) "Display pages to fill the WINDOW starting from PAGE.