1 ;;; ffsanim.el --- Form Feed Slides animate -*- lexical-binding: t; -*-
3 ;; Copyright (C) 2022 Amin Bandali <bandali@gnu.org>
5 ;; Author: Amin Bandali <bandali@gnu.org>
7 ;; Keywords: outlines, tools
9 ;; This program is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation, either version 3 of the License, or
12 ;; (at your option) any later version.
14 ;; This program is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;; GNU General Public License for more details.
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with this program. If not, see <https://www.gnu.org/licenses/>.
24 ;; A simple mode for doing simple plain text presentations where the
25 ;; slides are separated using the form feed character (\f). Uses
26 ;; animate.el to animate each slide.
28 ;; Configuration: TODO
32 ;; (add-to-list 'load-path (b/lisp "ffs"))
33 ;; (run-with-idle-timer 0.5 nil #'require 'ffsanim)
34 ;; (with-eval-after-load 'ffsanim
35 ;; (defvar b/original-default-height)
36 ;; (defvar b/ffsanim-default-height 300)
42 ;; b/original-default-height (face-attribute 'default :height))
43 ;; (set-face-attribute
44 ;; 'default nil :height b/ffsanim-default-height)
48 ;; ffsanim-mode-map (kbd "q")
52 ;; (set-face-attribute
53 ;; 'default nil :height b/original-default-height)
61 "Major mode for form feed-separated plain text presentations."
65 (defcustom ffsanim-buffer-name
"*ffsanim*"
66 "The name of the ffsanim presentation buffer."
70 (defcustom ffsanim-edit-buffer-name
"*ffsanim-edit*"
71 "The name of the ffsanim-edit buffer used when editing a slide."
75 (defvar ffsanim--source-buffer-name
""
76 "The name of the form feed-separated \"source\" buffer for a
79 (defun ffsanim--buffer ()
80 "Get the ffsanim presentation buffer."
81 (get-buffer-create ffsanim-buffer-name
))
83 (defmacro ffsanim-define-move-to-slide
(name &optional doc
&rest body
)
84 "Define a function for moving to a slide.
85 Symbol NAME is the name describing the movement.
86 DOC is the documentation string to use for the function."
87 (declare (debug (&define name
[&optional stringp
] def-body
))
88 (doc-string 2) (indent defun
))
89 (when (and doc
(not (stringp doc
)))
90 ;; `doc' is the first element of `body', not an actual docstring
93 (let* ((sn (symbol-name name
))
94 (fname (intern (format "ffsanim-%s-slide" (downcase sn
)))))
99 (pop-to-buffer-same-window
100 (get-buffer ffsanim--source-buffer-name
))
103 (prog1 (buffer-string)
105 (pop-to-buffer-same-window (ffsanim--buffer)))))
106 (animation-buffer-name (buffer-name (ffsanim--buffer)))
107 (inhibit-read-only t
))
108 (animate-sequence (split-string s
"\n") 0)))))
110 (defun ffsanim-edit-slide (&optional add-before-or-after
)
111 "Pop to a new buffer to edit a slide.
112 If ADD-BEFORE-OR-AFTER is nil or not given, we are editing an
113 existing slide. Otherwise, if it is `add-before' then the new
114 slide will be added before the current slide, and if it is
115 `add-after' then the new slide will be added after the current
116 slide. The logic for handling this is in `ffsanim-edit-done'."
119 (s (with-current-buffer (get-buffer ffsanim--source-buffer-name
)
121 (if add-before-or-after
; if we are adding a new slide
122 "\n" ; start with just a newline
124 (prog1 (buffer-string)
126 (pop-to-buffer-same-window
127 (get-buffer-create ffsanim-edit-buffer-name
))
129 (ffsanim-edit-mode 1)
131 (goto-char (point-min))
132 (setq-local ffsanim--new-location add-before-or-after
)
134 (substitute-command-keys "Edit, then use `\\[ffsanim-edit-done]' \
135 to apply your changes or `\\[ffsanim-edit-discard]' to discard them."))))
137 (defun ffsanim-edit-discard ()
138 "Discard current ffsanim-edit buffer and return to the presentation."
140 (let ((buf (current-buffer)))
141 (quit-windows-on buf
)
143 (pop-to-buffer-same-window (ffsanim--buffer)))
145 (defun ffsanim-edit-done ()
146 "Apply the ffsanim-edit changes and return to the presentation."
149 (str (buffer-string))
150 (s (if (string-suffix-p "\n" str
)
153 (l ffsanim--new-location
))
154 (with-current-buffer (get-buffer ffsanim--source-buffer-name
)
159 (insert (format "\n%s\f" s
))
160 (setq f
#'ffsanim-previous-slide
))
163 (insert (format "\n%s\f" s
))
164 (setq f
#'ffsanim-next-slide
))
167 (delete-region (point-min) (point-max))
170 (setq f
#'ffsanim-current-slide
)))))
171 (ffsanim-edit-discard)
174 (defun ffsanim-new-slide-before ()
175 "Add a new slide before the current slide."
177 (ffsanim-edit-slide 'add-before
))
179 (defun ffsanim-new-slide-after ()
180 "Add a new slide after the current slide."
182 (ffsanim-edit-slide 'add-after
))
184 (defvar ffsanim--old-mode-line-format nil
185 "The value of `mode-line-format' in the ffsanim presentation buffer
186 before the last call to `ffsanim--toggle-mode-line'.")
188 (defun ffsanim--toggle-mode-line ()
189 "Toggle the display of the mode-line in the current buffer."
192 (setq-local ffsanim--old-mode-line-format mode-line-format
193 mode-line-format nil
)
194 (setq-local mode-line-format ffsanim--old-mode-line-format
195 ffsanim--old-mode-line-format nil
))
198 (ffsanim-define-move-to-slide previous
199 "Go to the previous slide."
203 (ffsanim-define-move-to-slide next
204 "Go to the next slide."
207 (ffsanim-define-move-to-slide current
208 "Reload and renimate the current slide."
211 (ffsanim-define-move-to-slide first
212 "Go to the first slide."
213 (goto-char (point-min)))
215 (ffsanim-define-move-to-slide last
216 "Go to the last slide."
217 (goto-char (point-max)))
219 (define-derived-mode ffsanim-mode special-mode
"ffsanim"
220 "Major mode for form feed-separated plain text presentations."
223 (setq-local animate-total-added-delay
0.3)
224 (show-paren-local-mode -
1)
225 (display-battery-mode -
1)
226 (ffsanim--toggle-mode-line)
227 (ffsanim-current-slide))
229 (defvar ffsanim-edit-mode-map
230 (let ((map (make-sparse-keymap)))
231 (define-key map
(kbd "C-c C-k") #'ffsanim-edit-discard
)
232 (define-key map
(kbd "C-c C-c") #'ffsanim-edit-done
)
234 "Keymap for `ffsanim-edit-mode'.")
236 (define-minor-mode ffsanim-edit-mode
237 "Minor mode for editing a single ffsanim slide.
238 When done editing the slide, run \\[ffsanim-edit-done] to apply your
239 changes, or \\[ffsanim-edit-discard] to discard them."
241 :lighter
" ffsanim-edit"
242 :keymap ffsanim-edit-mode-map
243 (defvar-local ffsanim--new-location nil
244 "The location where the new slide should be inserted.
245 See the docstring for `ffsanim-edit-slide' for more details."))
247 (define-key ffsanim-mode-map
(kbd "p") #'ffsanim-previous-slide
)
248 (define-key ffsanim-mode-map
(kbd "n") #'ffsanim-next-slide
)
249 (define-key ffsanim-mode-map
(kbd "DEL") #'ffsanim-previous-slide
)
250 (define-key ffsanim-mode-map
(kbd "SPC") #'ffsanim-next-slide
)
251 (define-key ffsanim-mode-map
(kbd "g") #'ffsanim-current-slide
)
252 (define-key ffsanim-mode-map
(kbd "<") #'ffsanim-first-slide
)
253 (define-key ffsanim-mode-map
(kbd ">") #'ffsanim-last-slide
)
254 (define-key ffsanim-mode-map
(kbd "e") #'ffsanim-edit-slide
)
255 (define-key ffsanim-mode-map
(kbd "O") #'ffsanim-new-slide-before
)
256 (define-key ffsanim-mode-map
(kbd "o") #'ffsanim-new-slide-after
)
257 (define-key ffsanim-mode-map
(kbd "m") #'ffsanim--toggle-mode-line
)
260 "Start an ffsanim presentation with current buffer as source."
262 (setq ffsanim--source-buffer-name
(buffer-name))
263 (pop-to-buffer-same-window (ffsanim--buffer))
267 ;;; ffsanim.el ends here