Commit c17587fe authored by Stefan Monnier's avatar Stefan Monnier
Browse files

Use expand-file-name rather than concat.

(doc-view-cache-directory): Add the UID so multiple users won't clash.
(doc-view-current-overlay, doc-view-pending-cache-flush): New vars.
(doc-view-goto-page, doc-view-insert-image, doc-view-buffer-message)
(doc-view-toggle-display): Use an overlay over the whole buffer so as
not to have to touch the buffer's content.
(doc-view-initiate-display): New function, extracted from doc-view-mode.
(doc-view-mode): Use it.  Don't mark as a special mode.
Put the page numbers in the modeline.
Set up the overlay.  Hide the cursor.  Run the mode hook.
Use after-revert-hook rather than revert-buffer-function.
(doc-view-search-internal): Fix typo.
(doc-view-convert-current-doc, doc-view-insert-image): Delay the
image-cache flush.
(doc-view-reconvert-doc): Don't reset the whole mode.
(doc-view-make-safe-dir): New function.
(doc-view-current-cache-dir): Use it.
parent 36d4b145
2007-10-30 Stefan Monnier <monnier@iro.umontreal.ca>
* doc-view.el: Use expand-file-name rather than concat.
(doc-view-cache-directory): Add the UID so multiple users won't clash.
(doc-view-current-overlay, doc-view-pending-cache-flush): New vars.
(doc-view-goto-page, doc-view-insert-image, doc-view-buffer-message)
(doc-view-toggle-display): Use an overlay over the whole buffer so as
not to have to touch the buffer's content.
(doc-view-initiate-display): New function, extracted from doc-view-mode.
(doc-view-mode): Use it. Don't mark as a special mode.
Put the page numbers in the modeline.
Set up the overlay. Hide the cursor. Run the mode hook.
Use after-revert-hook rather than revert-buffer-function.
(doc-view-search-internal): Fix typo.
(doc-view-convert-current-doc, doc-view-insert-image): Delay the
image-cache flush.
(doc-view-reconvert-doc): Don't reset the whole mode.
(doc-view-make-safe-dir): New function.
(doc-view-current-cache-dir): Use it.
2007-10-30 Jason Rumney <jasonr@gnu.org> 2007-10-30 Jason Rumney <jasonr@gnu.org>
* time.el (display-time-world-list): Test for zoneinfo support. * time.el (display-time-world-list): Test for zoneinfo support.
......
...@@ -103,9 +103,10 @@ ...@@ -103,9 +103,10 @@
;; Todo: ;; Todo:
;; - better menu. ;; - better menu.
;; - don't use `find-file'. ;; - don't use `find-file'.
;; - `reload' without changing the slicing.
;; - Bind slicing to a drag event. ;; - Bind slicing to a drag event.
;; - zoom ;; - zoom (the whole document and/or just the region around the cursor).
;; - get rid of the silly arrow in the fringe.
;; - improve anti-aliasing (pdf-utils gets it better).
(require 'dired) (require 'dired)
(require 'image-mode) (require 'image-mode)
...@@ -156,8 +157,8 @@ Needed for searching." ...@@ -156,8 +157,8 @@ Needed for searching."
:type 'file :type 'file
:group 'doc-view) :group 'doc-view)
(defcustom doc-view-cache-directory (concat temporary-file-directory (defcustom doc-view-cache-directory
"doc-view") (expand-file-name (concat "docview" (user-uid)) temporary-file-directory)
"The base directory, where the PNG images will be saved." "The base directory, where the PNG images will be saved."
:type 'directory :type 'directory
:group 'doc-view) :group 'doc-view)
...@@ -201,6 +202,8 @@ has finished." ...@@ -201,6 +202,8 @@ has finished."
(defvar doc-view-current-image nil (defvar doc-view-current-image nil
"Only used internally.") "Only used internally.")
(defvar doc-view-current-overlay)
(defvar doc-view-pending-cache-flush nil)
(defvar doc-view-current-info nil (defvar doc-view-current-info nil
"Only used internally.") "Only used internally.")
...@@ -303,16 +306,14 @@ has finished." ...@@ -303,16 +306,14 @@ has finished."
(setq contexts (concat contexts " - \"" m "\"\n"))) (setq contexts (concat contexts " - \"" m "\"\n")))
contexts))))) contexts)))))
;; Update the buffer ;; Update the buffer
(let ((inhibit-read-only t)) (doc-view-insert-image (nth (1- page) doc-view-current-files)
(erase-buffer) :pointer 'arrow)
(let ((beg (point))) (overlay-put doc-view-current-overlay 'help-echo doc-view-current-info)
(doc-view-insert-image (nth (1- page) doc-view-current-files) (goto-char (point-min))
:pointer 'arrow) ;; This seems to be needed for set-window-hscroll (in
(put-text-property beg (point) 'help-echo doc-view-current-info)) ;; image-forward-hscroll) to do something useful, I don't have time to
(insert "\n" doc-view-current-info) ;; debug this now. :-( --Stef
(goto-char (point-min)) (forward-char)))
(forward-char))
(set-buffer-modified-p nil)))
(defun doc-view-next-page (&optional arg) (defun doc-view-next-page (&optional arg)
"Browse ARG pages forward." "Browse ARG pages forward."
...@@ -369,20 +370,49 @@ has finished." ...@@ -369,20 +370,49 @@ has finished."
(when (eq major-mode 'doc-view-mode) (when (eq major-mode 'doc-view-mode)
(kill-buffer (current-buffer)))) (kill-buffer (current-buffer))))
(defun doc-view-make-safe-dir (dir)
(condition-case nil
(let ((umask (default-file-modes)))
(unwind-protect
(progn
;; Create temp files with strict access rights. It's easy to
;; loosen them later, whereas it's impossible to close the
;; time-window of loose permissions otherwise.
(set-default-file-modes #o0700)
(make-directory dir))
;; Reset the umask.
(set-default-file-modes umask)))
(file-already-exists
(if (file-symlink-p dir)
(error "Danger: %s points to a symbolic link" dir))
;; In case it was created earlier with looser rights.
;; We could check the mode info returned by file-attributes, but it's
;; a pain to parse and it may not tell you what we want under
;; non-standard file-systems. So let's just say what we want and let
;; the underlying C code and file-system figure it out.
;; This also ends up checking a bunch of useful conditions: it makes
;; sure we have write-access to the directory and that we own it, thus
;; closing a bunch of security holes.
(set-file-modes dir #o0700))))
(defun doc-view-current-cache-dir () (defun doc-view-current-cache-dir ()
"Return the directory where the png files of the current doc should be saved. "Return the directory where the png files of the current doc should be saved.
It's a subdirectory of `doc-view-cache-directory'." It's a subdirectory of `doc-view-cache-directory'."
(if doc-view-current-cache-dir (if doc-view-current-cache-dir
doc-view-current-cache-dir doc-view-current-cache-dir
;; Try and make sure doc-view-cache-directory exists and is safe.
(doc-view-make-safe-dir doc-view-cache-directory)
;; Now compute the subdirectory to use.
(setq doc-view-current-cache-dir (setq doc-view-current-cache-dir
(file-name-as-directory (file-name-as-directory
(concat (file-name-as-directory doc-view-cache-directory) (expand-file-name
(let ((doc buffer-file-name)) (let ((doc buffer-file-name))
(concat (file-name-nondirectory doc) (concat (file-name-nondirectory doc)
"-" "-"
(with-temp-buffer (with-temp-buffer
(insert-file-contents-literally doc) (insert-file-contents-literally doc)
(md5 (current-buffer)))))))))) (md5 (current-buffer)))))
doc-view-cache-directory)))))
(defun doc-view-remove-if (predicate list) (defun doc-view-remove-if (predicate list)
"Return LIST with all items removed that satisfy PREDICATE." "Return LIST with all items removed that satisfy PREDICATE."
...@@ -393,7 +423,7 @@ It's a subdirectory of `doc-view-cache-directory'." ...@@ -393,7 +423,7 @@ It's a subdirectory of `doc-view-cache-directory'."
;;;; Conversion Functions ;;;; Conversion Functions
(defun doc-view-reconvert-doc (&rest args) (defun doc-view-reconvert-doc ()
"Reconvert the current document. "Reconvert the current document.
Should be invoked when the cached images aren't up-to-date." Should be invoked when the cached images aren't up-to-date."
(interactive) (interactive)
...@@ -401,7 +431,7 @@ Should be invoked when the cached images aren't up-to-date." ...@@ -401,7 +431,7 @@ Should be invoked when the cached images aren't up-to-date."
;; Clear the old cached files ;; Clear the old cached files
(when (file-exists-p (doc-view-current-cache-dir)) (when (file-exists-p (doc-view-current-cache-dir))
(dired-delete-file (doc-view-current-cache-dir) 'always)) (dired-delete-file (doc-view-current-cache-dir) 'always))
(doc-view-mode)) (doc-view-initiate-display))
(defun doc-view-dvi->pdf-sentinel (proc event) (defun doc-view-dvi->pdf-sentinel (proc event)
"If DVI->PDF conversion was successful, convert the PDF to PNG now." "If DVI->PDF conversion was successful, convert the PDF to PNG now."
...@@ -412,8 +442,8 @@ Should be invoked when the cached images aren't up-to-date." ...@@ -412,8 +442,8 @@ Should be invoked when the cached images aren't up-to-date."
mode-line-process nil) mode-line-process nil)
;; Now go on converting this PDF to a set of PNG files. ;; Now go on converting this PDF to a set of PNG files.
(let* ((pdf (process-get proc 'pdf-file)) (let* ((pdf (process-get proc 'pdf-file))
(png (concat (doc-view-current-cache-dir) (png (expand-file-name "page-%d.png"
"page-%d.png"))) (doc-view-current-cache-dir))))
(doc-view-pdf/ps->png pdf png)))) (doc-view-pdf/ps->png pdf png))))
(defun doc-view-dvi->pdf (dvi pdf) (defun doc-view-dvi->pdf (dvi pdf)
...@@ -493,8 +523,8 @@ Should be invoked when the cached images aren't up-to-date." ...@@ -493,8 +523,8 @@ Should be invoked when the cached images aren't up-to-date."
mode-line-process nil) mode-line-process nil)
;; Now we can transform to plain text. ;; Now we can transform to plain text.
(doc-view-pdf->txt (process-get proc 'pdf-file) (doc-view-pdf->txt (process-get proc 'pdf-file)
(concat (doc-view-current-cache-dir) (expand-file-name "doc.txt"
"doc.txt")))) (doc-view-current-cache-dir)))))
(defun doc-view-ps->pdf (ps pdf) (defun doc-view-ps->pdf (ps pdf)
"Convert PS to PDF asynchronously." "Convert PS to PDF asynchronously."
...@@ -516,18 +546,23 @@ Should be invoked when the cached images aren't up-to-date." ...@@ -516,18 +546,23 @@ Should be invoked when the cached images aren't up-to-date."
"Convert `buffer-file-name' to a set of png files, one file per page. "Convert `buffer-file-name' to a set of png files, one file per page.
Those files are saved in the directory given by the function Those files are saved in the directory given by the function
`doc-view-current-cache-dir'." `doc-view-current-cache-dir'."
(clear-image-cache) ;; Let stale files still display while we recompute the new ones, so only
(let ((png-file (concat (doc-view-current-cache-dir) ;; flush the cache when the conversion is over. One of the reasons why it
"page-%d.png"))) ;; is important to keep displaying the stale page is so that revert-buffer
(make-directory (doc-view-current-cache-dir) t) ;; preserves the horizontal/vertical scroll settings (which are otherwise
;; resets during the redisplay).
(setq doc-view-pending-cache-flush t)
(let ((png-file (expand-file-name "page-%d.png"
(doc-view-current-cache-dir))))
(make-directory (doc-view-current-cache-dir))
(if (not (string= (file-name-extension buffer-file-name) "dvi")) (if (not (string= (file-name-extension buffer-file-name) "dvi"))
;; Convert to PNG images. ;; Convert to PNG images.
(doc-view-pdf/ps->png buffer-file-name png-file) (doc-view-pdf/ps->png buffer-file-name png-file)
;; DVI files have to be converted to PDF before Ghostscript can process ;; DVI files have to be converted to PDF before Ghostscript can process
;; it. ;; it.
(doc-view-dvi->pdf buffer-file-name (doc-view-dvi->pdf buffer-file-name
(concat (file-name-as-directory doc-view-current-cache-dir) (expand-file-name "doc.pdf"
"doc.pdf"))))) doc-view-current-cache-dir)))))
;;;; Slicing ;;;; Slicing
...@@ -583,9 +618,16 @@ again." ...@@ -583,9 +618,16 @@ again."
(defun doc-view-insert-image (file &rest args) (defun doc-view-insert-image (file &rest args)
"Insert the given png FILE. "Insert the given png FILE.
ARGS is a list of image descriptors." ARGS is a list of image descriptors."
(when doc-view-pending-cache-flush
(clear-image-cache)
(setq doc-view-pending-cache-flush nil))
(let ((image (apply 'create-image file 'png nil args))) (let ((image (apply 'create-image file 'png nil args)))
(setq doc-view-current-image image) (setq doc-view-current-image image)
(insert-image image (concat "[" file "]") nil doc-view-current-slice))) (move-overlay doc-view-current-overlay (point-min) (point-max))
(overlay-put doc-view-current-overlay 'display
(if doc-view-current-slice
(list (cons 'slice doc-view-current-slice) image)
image))))
(defun doc-view-sort (a b) (defun doc-view-sort (a b)
"Return non-nil if A should be sorted before B. "Return non-nil if A should be sorted before B.
...@@ -605,9 +647,14 @@ Predicate for sorting `doc-view-current-files'." ...@@ -605,9 +647,14 @@ Predicate for sorting `doc-view-current-files'."
(doc-view-goto-page doc-view-current-page))) (doc-view-goto-page doc-view-current-page)))
(defun doc-view-buffer-message () (defun doc-view-buffer-message ()
(insert (propertize "Welcome to DocView!" 'face 'bold) ;; Only show this message initially, not when refreshing the buffer (in which
"\n" ;; case it's better to keep displaying the "stale" page while computing
" ;; the fresh new ones).
(unless (overlay-get doc-view-current-overlay 'display)
(overlay-put doc-view-current-overlay 'display
(concat (propertize "Welcome to DocView!" 'face 'bold)
"\n"
"
If you see this buffer it means that the document you want to view is being If you see this buffer it means that the document you want to view is being
converted to PNG and the conversion of the first page hasn't finished yet or converted to PNG and the conversion of the first page hasn't finished yet or
`doc-view-conversion-refresh-interval' is set to nil. `doc-view-conversion-refresh-interval' is set to nil.
...@@ -616,7 +663,7 @@ For now these keys are useful: ...@@ -616,7 +663,7 @@ For now these keys are useful:
`q' : Bury this buffer. Conversion will go on in background. `q' : Bury this buffer. Conversion will go on in background.
`k' : Kill the conversion process and this buffer. `k' : Kill the conversion process and this buffer.
`K' : Kill the conversion process.\n")) `K' : Kill the conversion process.\n"))))
(defun doc-view-show-tooltip () (defun doc-view-show-tooltip ()
(interactive) (interactive)
...@@ -632,20 +679,17 @@ For now these keys are useful: ...@@ -632,20 +679,17 @@ For now these keys are useful:
(progn (progn
(doc-view-kill-proc) (doc-view-kill-proc)
(setq buffer-read-only nil) (setq buffer-read-only nil)
(erase-buffer) (delete-overlay doc-view-current-overlay)
(insert-file-contents buffer-file-name)
;; Switch to the previously used major mode or fall back to fundamental ;; Switch to the previously used major mode or fall back to fundamental
;; mode. ;; mode.
(if doc-view-previous-major-mode (if doc-view-previous-major-mode
(funcall doc-view-previous-major-mode) (funcall doc-view-previous-major-mode)
(fundamental-mode)) (fundamental-mode))
(doc-view-minor-mode 1) (doc-view-minor-mode 1))
(set-buffer-modified-p nil))
;; Switch to doc-view-mode ;; Switch to doc-view-mode
(when (and (buffer-modified-p) (when (and (buffer-modified-p)
(y-or-n-p "The buffer has been modified. Save the changes? ")) (y-or-n-p "The buffer has been modified. Save the changes? "))
(save-buffer)) (save-buffer))
(erase-buffer)
(doc-view-mode))) (doc-view-mode)))
;;;; Searching ;;;; Searching
...@@ -664,11 +708,11 @@ the pagenumber and CONTEXTS are all lines of text containing a match." ...@@ -664,11 +708,11 @@ the pagenumber and CONTEXTS are all lines of text containing a match."
(when (match-string 1) (incf page)) (when (match-string 1) (incf page))
(when (match-string 2) (when (match-string 2)
(if (/= page lastpage) (if (/= page lastpage)
(setq matches (push (cons page (push (cons page
(list (buffer-substring (list (buffer-substring
(line-beginning-position) (line-beginning-position)
(line-end-position)))) (line-end-position))))
matches)) matches)
(setq matches (cons (setq matches (cons
(append (append
(or (or
...@@ -698,8 +742,8 @@ conversion finished." ...@@ -698,8 +742,8 @@ conversion finished."
(interactive) (interactive)
;; New search, so forget the old results. ;; New search, so forget the old results.
(setq doc-view-current-search-matches nil) (setq doc-view-current-search-matches nil)
(let ((txt (concat (doc-view-current-cache-dir) (let ((txt (expand-file-name "doc.txt"
"doc.txt"))) (doc-view-current-cache-dir))))
(if (file-readable-p txt) (if (file-readable-p txt)
(progn (progn
(setq doc-view-current-search-matches (setq doc-view-current-search-matches
...@@ -721,13 +765,13 @@ conversion finished." ...@@ -721,13 +765,13 @@ conversion finished."
;; Doc is a PS, so convert it to PDF (which will be converted to ;; Doc is a PS, so convert it to PDF (which will be converted to
;; TXT thereafter). ;; TXT thereafter).
(doc-view-ps->pdf buffer-file-name (doc-view-ps->pdf buffer-file-name
(concat (doc-view-current-cache-dir) (expand-file-name "doc.pdf"
"doc.pdf"))) (doc-view-current-cache-dir))))
((string= ext "dvi") ((string= ext "dvi")
;; Doc is a DVI. This means that a doc.pdf already exists in its ;; Doc is a DVI. This means that a doc.pdf already exists in its
;; cache subdirectory. ;; cache subdirectory.
(doc-view-pdf->txt (concat (doc-view-current-cache-dir) (doc-view-pdf->txt (expand-file-name "doc.pdf"
"doc.pdf") (doc-view-current-cache-dir))
txt)) txt))
(t (error "DocView doesn't know what to do")))))))) (t (error "DocView doesn't know what to do"))))))))
...@@ -761,7 +805,30 @@ conversion finished." ...@@ -761,7 +805,30 @@ conversion finished."
;;;; User interface commands and the mode ;;;; User interface commands and the mode
(put 'doc-view-mode 'mode-class 'special) ;; (put 'doc-view-mode 'mode-class 'special)
(defun doc-view-initiate-display ()
;; Switch to image display if possible
(if (and (display-images-p)
(image-type-available-p 'png))
(progn
(doc-view-buffer-message)
(setq doc-view-current-page (or doc-view-current-page 1))
(if (file-exists-p (doc-view-current-cache-dir))
(progn
(message "DocView: using cached files!")
(doc-view-display buffer-file-name))
(doc-view-convert-current-doc))
(message
"%s"
(substitute-command-keys
(concat "Type \\[doc-view-toggle-display] to toggle between "
"editing or viewing the document."))))
(message
"%s"
(substitute-command-keys
(concat "No image (png) support available. Type \\[doc-view-toggle-display] "
"to switch to an editing mode.")))))
;;;###autoload ;;;###autoload
(defun doc-view-mode () (defun doc-view-mode ()
...@@ -783,37 +850,22 @@ toggle between displaying the document or editing it as text." ...@@ -783,37 +850,22 @@ toggle between displaying the document or editing it as text."
(make-local-variable 'doc-view-current-cache-dir) (make-local-variable 'doc-view-current-cache-dir)
(make-local-variable 'doc-view-current-info) (make-local-variable 'doc-view-current-info)
(make-local-variable 'doc-view-current-search-matches) (make-local-variable 'doc-view-current-search-matches)
;; The file should already be in the current buffer. --Stef (set (make-local-variable 'doc-view-current-overlay)
;; (insert-file-contents buffer-file-name) (make-overlay (point-min) (point-max) nil t))
(add-hook 'change-major-mode-hook
(lambda () (delete-overlay doc-view-current-overlay))
nil t)
(set (make-local-variable 'mode-line-position)
'(" P" (:eval (number-to-string doc-view-current-page))
"/" (:eval (number-to-string (length doc-view-current-files)))))
(set (make-local-variable 'cursor-type) nil)
(use-local-map doc-view-mode-map) (use-local-map doc-view-mode-map)
(set (make-local-variable 'revert-buffer-function) 'doc-view-reconvert-doc) (set (make-local-variable 'after-revert-hook) 'doc-view-reconvert-doc)
(setq mode-name "DocView" (setq mode-name "DocView"
buffer-read-only t buffer-read-only t
major-mode 'doc-view-mode) major-mode 'doc-view-mode)
;; Switch to image display if possible (doc-view-initiate-display)
(if (and (display-images-p) (run-mode-hooks 'doc-view-mode-hook))
(image-type-available-p 'png))
(let ((inhibit-read-only t))
(erase-buffer)
(doc-view-buffer-message)
(set-buffer-modified-p nil)
(setq doc-view-current-page (or doc-view-current-page 1))
(if (file-exists-p (doc-view-current-cache-dir))
(progn
(message "DocView: using cached files!")
(doc-view-display buffer-file-name))
(doc-view-convert-current-doc))
(use-local-map doc-view-mode-map)
(message
"%s"
(substitute-command-keys
(concat "Type \\[doc-view-toggle-display] to toggle between "
"editing or viewing the document."))))
(message
"%s"
(substitute-command-keys
(concat "No image (png) support available. Type \\[doc-view-toggle-display] "
"to switch to an editing mode.")))))
;;;###autoload ;;;###autoload
(define-minor-mode doc-view-minor-mode (define-minor-mode doc-view-minor-mode
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment