Merge from trunk
[bpt/emacs.git] / lisp / ses.el
1 ;;; ses.el -- Simple Emacs Spreadsheet -*- coding: utf-8 -*-
2
3 ;; Copyright (C) 2002-2012 Free Software Foundation, Inc.
4
5 ;; Author: Jonathan Yavner <jyavner@member.fsf.org>
6 ;; Maintainer: Vincent Belaïche <vincentb1@users.sourceforge.net>
7 ;; Keywords: spreadsheet Dijkstra
8
9 ;; This file is part of GNU Emacs.
10
11 ;; GNU Emacs is free software: you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation, either version 3 of the License, or
14 ;; (at your option) any later version.
15
16 ;; GNU Emacs is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details.
20
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
23
24 ;;; Commentary:
25
26 ;;; To-do list:
27
28 ;; * split (catch 'cycle ...) call back into one or more functions
29 ;; * Use $ or … for truncated fields
30 ;; * Add command to make a range of columns be temporarily invisible.
31 ;; * Allow paste of one cell to a range of cells -- copy formula to each.
32 ;; * Do something about control characters & octal codes in cell print
33 ;; areas. Use string-width?
34 ;; * Input validation functions. How specified?
35 ;; * Faces (colors & styles) in print cells.
36 ;; * Move a column by dragging its letter in the header line.
37 ;; * Left-margin column for row number.
38 ;; * Move a row by dragging its number in the left-margin.
39
40 ;;; Cycle detection
41
42 ;; Cycles used to be detected by stationarity of ses--deferred-recalc. This was
43 ;; working fine in most cases, however failed in some cases of several path
44 ;; racing together.
45 ;;
46 ;; The current algorithm is based on Dijkstra's algorithm. The cycle length is
47 ;; stored in some cell property. In order not to reset in all cells such
48 ;; property at each update, the cycle length is stored in this property along
49 ;; with some update attempt id that is incremented at each update. The current
50 ;; update id is ses--Dijkstra-attempt-nb. In case there is a cycle the cycle
51 ;; length diverge to infinite so it will exceed ses--Dijkstra-weight-bound at
52 ;; some point of time that allows detection. Otherwise it converges to the
53 ;; longest path length in the update tree.
54
55
56 ;;; Code:
57
58 (require 'unsafep)
59 (eval-when-compile (require 'cl))
60
61
62 ;;----------------------------------------------------------------------------
63 ;; User-customizable variables
64 ;;----------------------------------------------------------------------------
65
66 (defgroup ses nil
67 "Simple Emacs Spreadsheet."
68 :group 'applications
69 :prefix "ses-"
70 :version "21.1")
71
72 (defcustom ses-initial-size '(1 . 1)
73 "Initial size of a new spreadsheet, as a cons (NUMROWS . NUMCOLS)."
74 :group 'ses
75 :type '(cons (integer :tag "numrows") (integer :tag "numcols")))
76
77 (defcustom ses-initial-column-width 7
78 "Initial width of columns in a new spreadsheet."
79 :group 'ses
80 :type '(integer :match (lambda (widget value) (> value 0))))
81
82 (defcustom ses-initial-default-printer "%.7g"
83 "Initial default printer for a new spreadsheet."
84 :group 'ses
85 :type '(choice string
86 (list :tag "Parenthesized string" string)
87 function))
88
89 (defcustom ses-after-entry-functions '(forward-char)
90 "Things to do after entering a value into a cell.
91 An abnormal hook that usually runs a cursor-movement function.
92 Each function is called with ARG=1."
93 :group 'ses
94 :type 'hook
95 :options '(forward-char backward-char next-line previous-line))
96
97 (defcustom ses-mode-hook nil
98 "Hook functions to be run upon entering SES mode."
99 :group 'ses
100 :type 'hook)
101
102
103 ;;----------------------------------------------------------------------------
104 ;; Global variables and constants
105 ;;----------------------------------------------------------------------------
106
107 (defvar ses-read-cell-history nil
108 "List of formulas that have been typed in.")
109
110 (defvar ses-read-printer-history nil
111 "List of printer functions that have been typed in.")
112
113 (easy-menu-define ses-header-line-menu nil
114 "Context menu when mouse-3 is used on the header-line in an SES buffer."
115 '("SES header row"
116 ["Set current row" ses-set-header-row t]
117 ["Unset row" ses-unset-header-row (> ses--header-row 0)]))
118
119 (defconst ses-mode-map
120 (let ((keys `("\C-c\M-\C-l" ses-reconstruct-all
121 "\C-c\C-l" ses-recalculate-all
122 "\C-c\C-n" ses-renarrow-buffer
123 "\C-c\C-c" ses-recalculate-cell
124 "\C-c\M-\C-s" ses-sort-column
125 "\C-c\M-\C-h" ses-set-header-row
126 "\C-c\C-t" ses-truncate-cell
127 "\C-c\C-j" ses-jump
128 "\C-c\C-p" ses-read-default-printer
129 "\M-\C-l" ses-reprint-all
130 [?\S-\C-l] ses-reprint-all
131 [header-line down-mouse-3] ,ses-header-line-menu
132 [header-line mouse-2] ses-sort-column-click))
133 (newmap (make-sparse-keymap)))
134 (while keys
135 (define-key (1value newmap) (car keys) (cadr keys))
136 (setq keys (cddr keys)))
137 newmap)
138 "Local keymap for Simple Emacs Spreadsheet.")
139
140 (easy-menu-define ses-menu ses-mode-map
141 "Menu bar menu for SES."
142 '("SES"
143 ["Insert row" ses-insert-row (ses-in-print-area)]
144 ["Delete row" ses-delete-row (ses-in-print-area)]
145 ["Insert column" ses-insert-column (ses-in-print-area)]
146 ["Delete column" ses-delete-column (ses-in-print-area)]
147 ["Set column printer" ses-read-column-printer t]
148 ["Set column width" ses-set-column-width t]
149 ["Set default printer" ses-read-default-printer t]
150 ["Jump to cell" ses-jump t]
151 ["Set cell printer" ses-read-cell-printer t]
152 ["Recalculate cell" ses-recalculate-cell t]
153 ["Truncate cell display" ses-truncate-cell t]
154 ["Export values" ses-export-tsv t]
155 ["Export formulas" ses-export-tsf t]))
156
157 (defconst ses-mode-edit-map
158 (let ((keys '("\C-c\C-r" ses-insert-range
159 "\C-c\C-s" ses-insert-ses-range
160 [S-mouse-3] ses-insert-range-click
161 [C-S-mouse-3] ses-insert-ses-range-click
162 "\M-\C-i" lisp-complete-symbol))
163 (newmap (make-sparse-keymap)))
164 (set-keymap-parent newmap minibuffer-local-map)
165 (while keys
166 (define-key newmap (pop keys) (pop keys)))
167 newmap)
168 "Local keymap for SES minibuffer cell-editing.")
169
170 ;Local keymap for SES print area
171 (defalias 'ses-mode-print-map
172 (let ((keys '([backtab] backward-char
173 [tab] ses-forward-or-insert
174 "\C-i" ses-forward-or-insert ; Needed for ses-coverage.el?
175 "\M-o" ses-insert-column
176 "\C-o" ses-insert-row
177 "\C-m" ses-edit-cell
178 "\M-k" ses-delete-column
179 "\M-y" ses-yank-pop
180 "\C-k" ses-delete-row
181 "\C-j" ses-append-row-jump-first-column
182 "\M-h" ses-mark-row
183 "\M-H" ses-mark-column
184 "\C-d" ses-clear-cell-forward
185 "\C-?" ses-clear-cell-backward
186 "(" ses-read-cell
187 "\"" ses-read-cell
188 "'" ses-read-symbol
189 "=" ses-edit-cell
190 "c" ses-recalculate-cell
191 "j" ses-jump
192 "p" ses-read-cell-printer
193 "t" ses-truncate-cell
194 "w" ses-set-column-width
195 "x" ses-export-keymap
196 "\M-p" ses-read-column-printer))
197 (repl '(;;We'll replace these wherever they appear in the keymap
198 clipboard-kill-region ses-kill-override
199 end-of-line ses-end-of-line
200 kill-line ses-delete-row
201 kill-region ses-kill-override
202 open-line ses-insert-row))
203 (numeric "0123456789.-")
204 (newmap (make-keymap)))
205 ;;Get rid of printables
206 (suppress-keymap newmap t)
207 ;;These keys insert themselves as the beginning of a numeric value
208 (dotimes (x (length numeric))
209 (define-key newmap (substring numeric x (1+ x)) 'ses-read-cell))
210 ;;Override these global functions wherever they're bound
211 (while repl
212 (substitute-key-definition (car repl) (cadr repl) newmap
213 (current-global-map))
214 (setq repl (cddr repl)))
215 ;;Apparently substitute-key-definition doesn't catch this?
216 (define-key newmap [(menu-bar) edit cut] 'ses-kill-override)
217 ;;Define our other local keys
218 (while keys
219 (define-key newmap (car keys) (cadr keys))
220 (setq keys (cddr keys)))
221 newmap))
222
223 ;;Helptext for ses-mode wants keymap as variable, not function
224 (defconst ses-mode-print-map (symbol-function 'ses-mode-print-map))
225
226 ;;Key map used for 'x' key.
227 (defalias 'ses-export-keymap
228 (let ((map (make-sparse-keymap "SES export")))
229 (define-key map "T" (cons " tab-formulas" 'ses-export-tsf))
230 (define-key map "t" (cons " tab-values" 'ses-export-tsv))
231 map))
232
233 (defconst ses-print-data-boundary "\n\014\n"
234 "Marker string denoting the boundary between print area and data area.")
235
236 (defconst ses-initial-global-parameters
237 "\n( ;Global parameters (these are read first)\n 2 ;SES file-format\n 1 ;numrows\n 1 ;numcols\n)\n\n"
238 "Initial contents for the three-element list at the bottom of the data area.")
239
240 (defconst ses-initial-file-trailer
241 ";; Local Variables:\n;; mode: ses\n;; End:\n"
242 "Initial contents for the file-trailer area at the bottom of the file.")
243
244 (defconst ses-initial-file-contents
245 (concat " \n" ; One blank cell in print area.
246 ses-print-data-boundary
247 "(ses-cell A1 nil nil nil nil)\n" ; One blank cell in data area.
248 "\n" ; End-of-row terminator for the one row in data area.
249 "(ses-column-widths [7])\n"
250 "(ses-column-printers [nil])\n"
251 "(ses-default-printer \"%.7g\")\n"
252 "(ses-header-row 0)\n"
253 ses-initial-global-parameters
254 ses-initial-file-trailer)
255 "The initial contents of an empty spreadsheet.")
256
257 (defconst ses-box-prop '(:box (:line-width 2 :style released-button))
258 "Display properties to create a raised box for cells in the header line.")
259
260 (defconst ses-standard-printer-functions
261 '(ses-center ses-center-span ses-dashfill ses-dashfill-span
262 ses-tildefill-span)
263 "List of print functions to be included in initial history of printer
264 functions. None of these standard-printer functions is suitable for use as a
265 column printer or a global-default printer because they invoke the column or
266 default printer and then modify its output.")
267
268
269 ;;----------------------------------------------------------------------------
270 ;; Local variables and constants
271 ;;----------------------------------------------------------------------------
272
273 (eval-and-compile
274 (defconst ses-localvars
275 '(ses--blank-line ses--cells ses--col-printers
276 ses--col-widths ses--curcell ses--curcell-overlay
277 ses--default-printer
278 ses--deferred-narrow ses--deferred-recalc
279 ses--deferred-write ses--file-format
280 (ses--header-hscroll . -1) ; Flag for "initial recalc needed"
281 ses--header-row ses--header-string ses--linewidth
282 ses--numcols ses--numrows ses--symbolic-formulas
283 ses--data-marker ses--params-marker (ses--Dijkstra-attempt-nb . 0)
284 ses--Dijkstra-weight-bound
285 ;; This list is useful to speed-up clean-up of symbols when
286 ;; an area containing renamed cell is deleted.
287 ses--renamed-cell-symb-list
288 ;; Global variables that we override
289 mode-line-process next-line-add-newlines transient-mark-mode)
290 "Buffer-local variables used by SES.")
291
292 (defun ses-set-localvars ()
293 "Set buffer-local and initialize some SES variables."
294 (dolist (x ses-localvars)
295 (cond
296 ((symbolp x)
297 (set (make-local-variable x) nil))
298 ((consp x)
299 (set (make-local-variable (car x)) (cdr x)))
300 (t (error "Unexpected elements `%S' in list `ses-localvars'" x))))))
301
302 (eval-when-compile ; silence compiler
303 (ses-set-localvars))
304
305 ;;; This variable is documented as being permitted in file-locals:
306 (put 'ses--symbolic-formulas 'safe-local-variable 'consp)
307
308 (defconst ses-paramlines-plist
309 '(ses--col-widths -5 ses--col-printers -4 ses--default-printer -3
310 ses--header-row -2 ses--file-format 1 ses--numrows 2
311 ses--numcols 3)
312 "Offsets from 'Global parameters' line to various parameter lines in the
313 data area of a spreadsheet.")
314
315
316 ;;
317 ;; "Side-effect variables". They are set in one function, altered in
318 ;; another as a side effect, then read back by the first, as a way of
319 ;; passing back more than one value. These declarations are just to make
320 ;; the compiler happy, and to conform to standard Emacs-Lisp practice (I
321 ;; think the make-local-variable trick above is cleaner).
322 ;;
323
324 (defvar ses-relocate-return nil
325 "Set by `ses-relocate-formula' and `ses-relocate-range', read by
326 `ses-relocate-all'. Set to 'delete if a cell-reference was deleted from a
327 formula--so the formula needs recalculation. Set to 'range if the size of a
328 `ses-range' was changed--so both the formula's value and list of dependents
329 need to be recalculated.")
330
331 (defvar ses-call-printer-return nil
332 "Set to t if last cell printer invoked by `ses-call-printer' requested
333 left-justification of the result. Set to error-signal if `ses-call-printer'
334 encountered an error during printing. Otherwise nil.")
335
336 (defvar ses-start-time nil
337 "Time when current operation started. Used by `ses-time-check' to decide
338 when to emit a progress message.")
339
340
341 ;;----------------------------------------------------------------------------
342 ;; Macros
343 ;;----------------------------------------------------------------------------
344
345 (defmacro ses-get-cell (row col)
346 "Return the cell structure that stores information about cell (ROW,COL)."
347 `(aref (aref ses--cells ,row) ,col))
348
349 ;; We might want to use defstruct here, but cells are explicitly used as
350 ;; arrays in ses-set-cell, so we'd need to fix this first. --Stef
351 (defsubst ses-make-cell (&optional symbol formula printer references
352 property-list)
353 (vector symbol formula printer references property-list))
354
355 (defmacro ses-cell-symbol (row &optional col)
356 "From a CELL or a pair (ROW,COL), get the symbol that names the local-variable holding its value. (0,0) => A1."
357 `(aref ,(if col `(ses-get-cell ,row ,col) row) 0))
358 (put 'ses-cell-symbol 'safe-function t)
359
360 (defmacro ses-cell-formula (row &optional col)
361 "From a CELL or a pair (ROW,COL), get the function that computes its value."
362 `(aref ,(if col `(ses-get-cell ,row ,col) row) 1))
363
364 (defmacro ses-cell-printer (row &optional col)
365 "From a CELL or a pair (ROW,COL), get the function that prints its value."
366 `(aref ,(if col `(ses-get-cell ,row ,col) row) 2))
367
368 (defmacro ses-cell-references (row &optional col)
369 "From a CELL or a pair (ROW,COL), get the list of symbols for cells whose
370 functions refer to its value."
371 `(aref ,(if col `(ses-get-cell ,row ,col) row) 3))
372
373 (defun ses-cell-property-get-fun (property-name cell)
374 ;; To speed up property fetching, each time a property is found it is placed
375 ;; in the first position. This way, after the first get, the full property
376 ;; list needs to be scanned only when the property does not exist for that
377 ;; cell.
378 (let* ((plist (aref cell 4))
379 (ret (plist-member plist property-name)))
380 (if ret
381 ;; Property was found.
382 (let ((val (cadr ret)))
383 (if (eq ret plist)
384 ;; Property found is already in the first position, so just return
385 ;; its value.
386 val
387 ;; Property is not in the first position, the following will move it
388 ;; there before returning its value.
389 (let ((next (cddr ret)))
390 (if next
391 (progn
392 (setcdr ret (cdr next))
393 (setcar ret (car next)))
394 (setcdr (last plist 1) nil)))
395 (aset cell 4
396 `(,property-name ,val ,@plist))
397 val)))))
398
399 (defmacro ses-cell-property-get (property-name row &optional col)
400 "Get property named PROPERTY-NAME from a CELL or a pair (ROW,COL).
401
402 When COL is omitted, CELL=ROW is a cell object. When COL is
403 present ROW and COL are the integer coordinates of the cell of
404 interest."
405 (declare (debug t))
406 `(ses-cell-property-get-fun
407 ,property-name
408 ,(if col `(ses-get-cell ,row ,col) row)))
409
410 (defun ses-cell-property-delq-fun (property-name cell)
411 (let ((ret (plist-get (aref cell 4) property-name)))
412 (if ret
413 (setcdr ret (cddr ret)))))
414
415 (defun ses-cell-property-set-fun (property-name property-val cell)
416 (let* ((plist (aref cell 4))
417 (ret (plist-member plist property-name)))
418 (if ret
419 (setcar (cdr ret) property-val)
420 (aset cell 4 `(,property-name ,property-val ,@plist)))))
421
422 (defmacro ses-cell-property-set (property-name property-value row &optional col)
423 "From a CELL or a pair (ROW,COL), set the property value of
424 the corresponding cell with name PROPERTY-NAME to PROPERTY-VALUE."
425 (if property-value
426 `(ses-cell-property-set-fun ,property-name ,property-value
427 ,(if col `(ses-get-cell ,row ,col) row))
428 `(ses-cell-property-delq-fun ,property-name
429 ,(if col `(ses-get-cell ,row ,col) row))))
430
431 (defun ses-cell-property-pop-fun (property-name cell)
432 (let* ((plist (aref cell 4))
433 (ret (plist-member plist property-name)))
434 (if ret
435 (prog1 (cadr ret)
436 (let ((next (cddr ret)))
437 (if next
438 (progn
439 (setcdr ret (cdr next))
440 (setcar ret (car next)))
441 (if (eq plist ret)
442 (aset cell 4 nil)
443 (setcdr (last plist 2) nil))))))))
444
445
446 (defmacro ses-cell-property-pop (property-name row &optional col)
447 "From a CELL or a pair (ROW,COL), get and remove the property value of
448 the corresponding cell with name PROPERTY-NAME."
449 `(ses-cell-property-pop-fun ,property-name
450 ,(if col `(ses-get-cell ,row ,col) row)))
451
452 (defun ses-cell-property-get-handle-fun (property-name cell)
453 (let* ((plist (aref cell 4))
454 (ret (plist-member plist property-name)))
455 (if ret
456 (if (eq ret plist)
457 (cdr ret)
458 (let ((val (cadr ret))
459 (next (cddr ret)))
460 (if next
461 (progn
462 (setcdr ret (cdr next))
463 (setcar ret (car next)))
464 (setcdr (last plist 2) nil))
465 (setq ret (cons val plist))
466 (aset cell 4 (cons property-name ret))
467 ret))
468 (setq ret (cons nil plist))
469 (aset cell 4 (cons property-name ret))
470 ret)))
471
472 (defmacro ses-cell-property-get-handle (property-name row &optional col)
473 "From a CELL or a pair (ROW,COL), get a cons cell whose car is
474 the property value of the corresponding cell property with name
475 PROPERTY-NAME."
476 `(ses-cell-property-get-handle-fun ,property-name
477 ,(if col `(ses-get-cell ,row ,col) row)))
478
479
480 (defalias 'ses-cell-property-handle-car 'car)
481 (defalias 'ses-cell-property-handle-setcar 'setcar)
482
483 (defmacro ses-cell-value (row &optional col)
484 "From a CELL or a pair (ROW,COL), get the current value for that cell."
485 `(symbol-value (ses-cell-symbol ,row ,col)))
486
487 (defmacro ses-col-width (col)
488 "Return the width for column COL."
489 `(aref ses--col-widths ,col))
490
491 (defmacro ses-col-printer (col)
492 "Return the default printer for column COL."
493 `(aref ses--col-printers ,col))
494
495 (defmacro ses-sym-rowcol (sym)
496 "From a cell-symbol SYM, gets the cons (row . col). A1 => (0 . 0).
497 Result is nil if SYM is not a symbol that names a cell."
498 `(and (symbolp ,sym) (get ,sym 'ses-cell)))
499
500 (defmacro ses-cell (sym value formula printer references)
501 "Load a cell SYM from the spreadsheet file. Does not recompute VALUE from
502 FORMULA, does not reprint using PRINTER, does not check REFERENCES. This is a
503 macro to prevent propagate-on-load viruses. Safety-checking for FORMULA and
504 PRINTER are deferred until first use."
505 (let ((rowcol (ses-sym-rowcol sym)))
506 (ses-formula-record formula)
507 (ses-printer-record printer)
508 (or (atom formula)
509 (eq safe-functions t)
510 (setq formula `(ses-safe-formula ,formula)))
511 (or (not printer)
512 (stringp printer)
513 (eq safe-functions t)
514 (setq printer `(ses-safe-printer ,printer)))
515 (aset (aref ses--cells (car rowcol))
516 (cdr rowcol)
517 (ses-make-cell sym formula printer references)))
518 (set sym value)
519 sym)
520
521 (defmacro ses-column-widths (widths)
522 "Load the vector of column widths from the spreadsheet file. This is a
523 macro to prevent propagate-on-load viruses."
524 (or (and (vectorp widths) (= (length widths) ses--numcols))
525 (error "Bad column-width vector"))
526 ;;To save time later, we also calculate the total width of each line in the
527 ;;print area (excluding the terminating newline)
528 (setq ses--col-widths widths
529 ses--linewidth (apply '+ -1 (mapcar '1+ widths))
530 ses--blank-line (concat (make-string ses--linewidth ?\s) "\n"))
531 t)
532
533 (defmacro ses-column-printers (printers)
534 "Load the vector of column printers from the spreadsheet file and checks
535 them for safety. This is a macro to prevent propagate-on-load viruses."
536 (or (and (vectorp printers) (= (length printers) ses--numcols))
537 (error "Bad column-printers vector"))
538 (dotimes (x ses--numcols)
539 (aset printers x (ses-safe-printer (aref printers x))))
540 (setq ses--col-printers printers)
541 (mapc 'ses-printer-record printers)
542 t)
543
544 (defmacro ses-default-printer (def)
545 "Load the global default printer from the spreadsheet file and checks it
546 for safety. This is a macro to prevent propagate-on-load viruses."
547 (setq ses--default-printer (ses-safe-printer def))
548 (ses-printer-record def)
549 t)
550
551 (defmacro ses-header-row (row)
552 "Load the header row from the spreadsheet file and checks it
553 for safety. This is a macro to prevent propagate-on-load viruses."
554 (or (and (wholenump row) (or (zerop ses--numrows) (< row ses--numrows)))
555 (error "Bad header-row"))
556 (setq ses--header-row row)
557 t)
558
559 (defmacro ses-dorange (curcell &rest body)
560 "Execute BODY repeatedly, with the variables `row' and `col' set to each
561 cell in the range specified by CURCELL. The range is available in the
562 variables `minrow', `maxrow', `mincol', and `maxcol'."
563 (declare (indent defun) (debug (form body)))
564 (let ((cur (make-symbol "cur"))
565 (min (make-symbol "min"))
566 (max (make-symbol "max"))
567 (r (make-symbol "r"))
568 (c (make-symbol "c")))
569 `(let* ((,cur ,curcell)
570 (,min (ses-sym-rowcol (if (consp ,cur) (car ,cur) ,cur)))
571 (,max (ses-sym-rowcol (if (consp ,cur) (cdr ,cur) ,cur))))
572 (let ((minrow (car ,min))
573 (maxrow (car ,max))
574 (mincol (cdr ,min))
575 (maxcol (cdr ,max))
576 row col)
577 (if (or (> minrow maxrow) (> mincol maxcol))
578 (error "Empty range"))
579 (dotimes (,r (- maxrow minrow -1))
580 (setq row (+ ,r minrow))
581 (dotimes (,c (- maxcol mincol -1))
582 (setq col (+ ,c mincol))
583 ,@body))))))
584
585 ;;Support for coverage testing.
586 (defmacro 1value (form)
587 "For code-coverage testing, indicate that FORM is expected to always have
588 the same value."
589 form)
590 (defmacro noreturn (form)
591 "For code-coverage testing, indicate that FORM will always signal an error."
592 form)
593
594
595 ;;----------------------------------------------------------------------------
596 ;; Utility functions
597 ;;----------------------------------------------------------------------------
598
599 (defun ses-vector-insert (array idx new)
600 "Create a new vector which is one larger than ARRAY and has NEW inserted
601 before element IDX."
602 (let* ((len (length array))
603 (result (make-vector (1+ len) new)))
604 (dotimes (x len)
605 (aset result
606 (if (< x idx) x (1+ x))
607 (aref array x)))
608 result))
609
610 ;;Allow ARRAY to be a symbol for use in buffer-undo-list
611 (defun ses-vector-delete (array idx count)
612 "Create a new vector which is a copy of ARRAY with COUNT objects removed
613 starting at element IDX. ARRAY is either a vector or a symbol whose value
614 is a vector--if a symbol, the new vector is assigned as the symbol's value."
615 (let* ((a (if (arrayp array) array (symbol-value array)))
616 (len (- (length a) count))
617 (result (make-vector len nil)))
618 (dotimes (x len)
619 (aset result x (aref a (if (< x idx) x (+ x count)))))
620 (if (symbolp array)
621 (set array result))
622 result))
623
624 (defun ses-delete-line (count)
625 "Like `kill-line', but no kill ring."
626 (let ((pos (point)))
627 (forward-line count)
628 (delete-region pos (point))))
629
630 (defun ses-printer-validate (printer)
631 "Signal an error if PRINTER is not a valid SES cell printer."
632 (or (not printer)
633 (stringp printer)
634 (functionp printer)
635 (and (stringp (car-safe printer)) (not (cdr printer)))
636 (error "Invalid printer function"))
637 printer)
638
639 (defun ses-printer-record (printer)
640 "Add PRINTER to `ses-read-printer-history' if not already there, after first
641 checking that it is a valid printer function."
642 (ses-printer-validate printer)
643 ;;To speed things up, we avoid calling prin1 for the very common "nil" case.
644 (if printer
645 (add-to-list 'ses-read-printer-history (prin1-to-string printer))))
646
647 (defun ses-formula-record (formula)
648 "If FORMULA is of the form 'symbol, add it to the list of symbolic formulas
649 for this spreadsheet."
650 (when (and (eq (car-safe formula) 'quote)
651 (symbolp (cadr formula)))
652 (add-to-list 'ses--symbolic-formulas
653 (list (symbol-name (cadr formula))))))
654
655 (defun ses-column-letter (col)
656 "Return the alphabetic name of column number COL.
657 0-25 become A-Z; 26-701 become AA-ZZ, and so on."
658 (let ((units (char-to-string (+ ?A (% col 26)))))
659 (if (< col 26)
660 units
661 (concat (ses-column-letter (1- (/ col 26))) units))))
662
663 (defun ses-create-cell-symbol (row col)
664 "Produce a symbol that names the cell (ROW,COL). (0,0) => 'A1."
665 (intern (concat (ses-column-letter col) (number-to-string (1+ row)))))
666
667 (defun ses-create-cell-variable-range (minrow maxrow mincol maxcol)
668 "Create buffer-local variables for cells. This is undoable."
669 (push `(apply ses-destroy-cell-variable-range ,minrow ,maxrow ,mincol ,maxcol)
670 buffer-undo-list)
671 (let (sym xrow xcol)
672 (dotimes (row (1+ (- maxrow minrow)))
673 (dotimes (col (1+ (- maxcol mincol)))
674 (setq xrow (+ row minrow)
675 xcol (+ col mincol)
676 sym (ses-create-cell-symbol xrow xcol))
677 (put sym 'ses-cell (cons xrow xcol))
678 (make-local-variable sym)))))
679
680 (defun ses-create-cell-variable (sym row col)
681 "Create a buffer-local variable `SYM' for cell at position (ROW, COL).
682
683 SYM is the symbol for that variable, ROW and COL are integers for
684 row and column of the cell, with numbering starting from 0.
685
686 Return nil in case of failure."
687 (unless (local-variable-p sym)
688 (make-local-variable sym)
689 (put sym 'ses-cell (cons row col))))
690
691 ;; We do not delete the ses-cell properties for the cell-variables, in
692 ;; case a formula that refers to this cell is in the kill-ring and is
693 ;; later pasted back in.
694 (defun ses-destroy-cell-variable-range (minrow maxrow mincol maxcol)
695 "Destroy buffer-local variables for cells. This is undoable."
696 (let (sym)
697 (dotimes (row (1+ (- maxrow minrow)))
698 (dotimes (col (1+ (- maxcol mincol)))
699 (let ((xrow (+ row minrow)) (xcol (+ col mincol)))
700 (setq sym (if (and (< xrow ses--numrows) (< xcol ses--numcols))
701 (ses-cell-symbol xrow xcol)
702 (ses-create-cell-symbol xrow xcol))))
703 (if (boundp sym)
704 (push `(apply ses-set-with-undo ,sym ,(symbol-value sym))
705 buffer-undo-list))
706 (kill-local-variable sym))))
707 (push `(apply ses-create-cell-variable-range ,minrow ,maxrow ,mincol ,maxcol)
708 buffer-undo-list))
709
710 (defun ses-reset-header-string ()
711 "Flag the header string for update. Upon undo, the header string will be
712 updated again."
713 (push '(apply ses-reset-header-string) buffer-undo-list)
714 (setq ses--header-hscroll -1))
715
716 ;;Split this code off into a function to avoid coverage-testing difficulties
717 (defun ses-time-check (format arg)
718 "If `ses-start-time' is more than a second ago, call `message' with FORMAT
719 and (eval ARG) and reset `ses-start-time' to the current time."
720 (when (> (- (float-time) ses-start-time) 1.0)
721 (message format (eval arg))
722 (setq ses-start-time (float-time)))
723 nil)
724
725
726 ;;----------------------------------------------------------------------------
727 ;; The cells
728 ;;----------------------------------------------------------------------------
729
730 (defun ses-set-cell (row col field val)
731 "Install VAL as the contents for field FIELD (named by a quoted symbol) of
732 cell (ROW,COL). This is undoable. The cell's data will be updated through
733 `post-command-hook'."
734 (let ((cell (ses-get-cell row col))
735 (elt (plist-get '(value t symbol 0 formula 1 printer 2 references 3)
736 field))
737 change)
738 (or elt (signal 'args-out-of-range nil))
739 (setq change (if (eq elt t)
740 (ses-set-with-undo (ses-cell-symbol cell) val)
741 (ses-aset-with-undo cell elt val)))
742 (if change
743 (add-to-list 'ses--deferred-write (cons row col))))
744 nil) ; Make coverage-tester happy.
745
746 (defun ses-cell-set-formula (row col formula)
747 "Store a new formula for (ROW . COL) and enqueue the cell for
748 recalculation via `post-command-hook'. Updates the reference lists for the
749 cells that this cell refers to. Does not update cell value or reprint the
750 cell. To avoid inconsistencies, this function is not interruptible, which
751 means Emacs will crash if FORMULA contains a circular list."
752 (let* ((cell (ses-get-cell row col))
753 (old (ses-cell-formula cell)))
754 (let ((sym (ses-cell-symbol cell))
755 (oldref (ses-formula-references old))
756 (newref (ses-formula-references formula))
757 (inhibit-quit t)
758 x xrow xcol)
759 (add-to-list 'ses--deferred-recalc sym)
760 ;;Delete old references from this cell. Skip the ones that are also
761 ;;in the new list.
762 (dolist (ref oldref)
763 (unless (memq ref newref)
764 (setq x (ses-sym-rowcol ref)
765 xrow (car x)
766 xcol (cdr x))
767 (ses-set-cell xrow xcol 'references
768 (delq sym (ses-cell-references xrow xcol)))))
769 ;;Add new ones. Skip ones left over from old list
770 (dolist (ref newref)
771 (setq x (ses-sym-rowcol ref)
772 xrow (car x)
773 xcol (cdr x)
774 x (ses-cell-references xrow xcol))
775 (or (memq sym x)
776 (ses-set-cell xrow xcol 'references (cons sym x))))
777 (ses-formula-record formula)
778 (ses-set-cell row col 'formula formula))))
779
780
781 (defun ses-repair-cell-reference-all ()
782 "Repair cell reference and warn if there was some reference corruption."
783 (interactive "*")
784 (let (errors)
785 ;; Step 1, reset :ses-repair-reference cell property in the whole sheet.
786 (dotimes (row ses--numrows)
787 (dotimes (col ses--numcols)
788 (let ((references (ses-cell-property-pop :ses-repair-reference
789 row col)))
790 (when references
791 (push (list
792 (ses-cell-symbol row col)
793 :corrupt-property
794 references) errors)))))
795
796 ;; Step 2, build new.
797 (dotimes (row ses--numrows)
798 (dotimes (col ses--numcols)
799 (let* ((cell (ses-get-cell row col))
800 (sym (ses-cell-symbol cell))
801 (formula (ses-cell-formula cell))
802 (new-ref (ses-formula-references formula)))
803 (dolist (ref new-ref)
804 (let* ((rowcol (ses-sym-rowcol ref))
805 (h (ses-cell-property-get-handle :ses-repair-reference
806 (car rowcol) (cdr rowcol))))
807 (unless (memq ref (ses-cell-property-handle-car h))
808 (ses-cell-property-handle-setcar
809 h
810 (cons sym
811 (ses-cell-property-handle-car h)))))))))
812
813 ;; Step 3, overwrite with check.
814 (dotimes (row ses--numrows)
815 (dotimes (col ses--numcols)
816 (let* ((cell (ses-get-cell row col))
817 (irrelevant (ses-cell-references cell))
818 (new-ref (ses-cell-property-pop :ses-repair-reference cell))
819 missing)
820 (dolist (ref new-ref)
821 (if (memq ref irrelevant)
822 (setq irrelevant (delq ref irrelevant))
823 (push ref missing)))
824 (ses-set-cell row col 'references new-ref)
825 (when (or missing irrelevant)
826 (push `( ,(ses-cell-symbol cell)
827 ,@(and missing (list :missing missing))
828 ,@(and irrelevant (list :irrelevant irrelevant)))
829 errors)))))
830 (if errors
831 (warn "----------------------------------------------------------------
832 Some references were corrupted.
833
834 The following is a list where each element ELT is such
835 that (car ELT) is the reference of cell CELL with corruption,
836 and (cdr ELT) is a property list where
837
838 * property `:corrupt-property' means that
839 property `:ses-repair-reference' of cell CELL was initially non
840 nil,
841
842 * property `:missing' is a list of missing references
843
844 * property `:irrelevant' is a list of non needed references
845
846 %S" errors)
847 (message "No reference corruption found"))))
848
849 (defun ses-calculate-cell (row col force)
850 "Calculate and print the value for cell (ROW,COL) using the cell's formula
851 function and print functions, if any. Result is nil for normal operation, or
852 the error signal if the formula or print function failed. The old value is
853 left unchanged if it was *skip* and the new value is nil.
854 Any cells that depend on this cell are queued for update after the end of
855 processing for the current keystroke, unless the new value is the same as
856 the old and FORCE is nil."
857 (let ((cell (ses-get-cell row col))
858 cycle-error formula-error printer-error)
859 (let ((oldval (ses-cell-value cell))
860 (formula (ses-cell-formula cell))
861 newval
862 this-cell-Dijkstra-attempt-h
863 this-cell-Dijkstra-attempt
864 this-cell-Dijkstra-attempt+1
865 ref-cell-Dijkstra-attempt-h
866 ref-cell-Dijkstra-attempt
867 ref-rowcol)
868 (when (eq (car-safe formula) 'ses-safe-formula)
869 (setq formula (ses-safe-formula (cadr formula)))
870 (ses-set-cell row col 'formula formula))
871 (condition-case sig
872 (setq newval (eval formula))
873 (error
874 ;; Variable `sig' can't be nil.
875 (nconc sig (list (ses-cell-symbol cell)))
876 (setq formula-error sig
877 newval '*error*)))
878 (if (and (not newval) (eq oldval '*skip*))
879 ;; Don't lose the *skip* --- previous field spans this one.
880 (setq newval '*skip*))
881 (catch 'cycle
882 (when (or force (not (eq newval oldval)))
883 (add-to-list 'ses--deferred-write (cons row col)) ; In case force=t.
884 (setq this-cell-Dijkstra-attempt-h
885 (ses-cell-property-get-handle :ses-Dijkstra-attempt cell);
886 this-cell-Dijkstra-attempt
887 (ses-cell-property-handle-car this-cell-Dijkstra-attempt-h))
888 (if (null this-cell-Dijkstra-attempt)
889 (ses-cell-property-handle-setcar
890 this-cell-Dijkstra-attempt-h
891 (setq this-cell-Dijkstra-attempt
892 (cons ses--Dijkstra-attempt-nb 0)))
893 (unless (= ses--Dijkstra-attempt-nb
894 (car this-cell-Dijkstra-attempt))
895 (setcar this-cell-Dijkstra-attempt ses--Dijkstra-attempt-nb)
896 (setcdr this-cell-Dijkstra-attempt 0)))
897 (setq this-cell-Dijkstra-attempt+1
898 (1+ (cdr this-cell-Dijkstra-attempt)))
899 (ses-set-cell row col 'value newval)
900 (dolist (ref (ses-cell-references cell))
901 (add-to-list 'ses--deferred-recalc ref)
902 (setq ref-rowcol (ses-sym-rowcol ref)
903 ref-cell-Dijkstra-attempt-h
904 (ses-cell-property-get-handle
905 :ses-Dijkstra-attempt
906 (car ref-rowcol) (cdr ref-rowcol))
907 ref-cell-Dijkstra-attempt
908 (ses-cell-property-handle-car ref-cell-Dijkstra-attempt-h))
909
910 (if (null ref-cell-Dijkstra-attempt)
911 (ses-cell-property-handle-setcar
912 ref-cell-Dijkstra-attempt-h
913 (setq ref-cell-Dijkstra-attempt
914 (cons ses--Dijkstra-attempt-nb
915 this-cell-Dijkstra-attempt+1)))
916 (if (= (car ref-cell-Dijkstra-attempt) ses--Dijkstra-attempt-nb)
917 (setcdr ref-cell-Dijkstra-attempt
918 (max (cdr ref-cell-Dijkstra-attempt)
919 this-cell-Dijkstra-attempt+1))
920 (setcar ref-cell-Dijkstra-attempt ses--Dijkstra-attempt-nb)
921 (setcdr ref-cell-Dijkstra-attempt
922 this-cell-Dijkstra-attempt+1)))
923
924 (when (> this-cell-Dijkstra-attempt+1 ses--Dijkstra-weight-bound)
925 ;; Update print of this cell.
926 (throw 'cycle (setq formula-error
927 `(error ,(format "Found cycle on cells %S"
928 (ses-cell-symbol cell)))
929 cycle-error formula-error)))))))
930 (setq printer-error (ses-print-cell row col))
931 (or
932 (and cycle-error
933 (error (error-message-string cycle-error)))
934 formula-error printer-error)))
935
936 (defun ses-clear-cell (row col)
937 "Delete formula and printer for cell (ROW,COL)."
938 (ses-set-cell row col 'printer nil)
939 (ses-cell-set-formula row col nil))
940
941 (defcustom ses-self-reference-early-detection nil
942 "True if cycle detection is early for cells that refer to themselves."
943 :version "24.1"
944 :type 'boolean
945 :group 'ses)
946
947 (defun ses-update-cells (list &optional force)
948 "Recalculate cells in LIST, checking for dependency loops. Prints
949 progress messages every second. Dependent cells are not recalculated
950 if the cell's value is unchanged and FORCE is nil."
951 (let ((ses--deferred-recalc list)
952 (nextlist list)
953 (pos (point))
954 curlist prevlist this-sym this-rowcol formula)
955 (with-temp-message " "
956 (while ses--deferred-recalc
957 ;; In each loop, recalculate cells that refer only to other cells that
958 ;; have already been recalculated or aren't in the recalculation region.
959 ;; Repeat until all cells have been processed or until the set of cells
960 ;; being worked on stops changing.
961 (if prevlist
962 (message "Recalculating... (%d cells left)"
963 (length ses--deferred-recalc)))
964 (setq curlist ses--deferred-recalc
965 ses--deferred-recalc nil
966 prevlist nextlist)
967 (while curlist
968 ;; this-sym has to be popped from curlist *BEFORE* the check, and not
969 ;; after because of the case of cells referring to themselves.
970 (setq this-sym (pop curlist)
971 this-rowcol (ses-sym-rowcol this-sym)
972 formula (ses-cell-formula (car this-rowcol)
973 (cdr this-rowcol)))
974 (or (catch 'ref
975 (dolist (ref (ses-formula-references formula))
976 (if (and ses-self-reference-early-detection (eq ref this-sym))
977 (error "Cycle found: cell %S is self-referring" this-sym)
978 (when (or (memq ref curlist)
979 (memq ref ses--deferred-recalc))
980 ;; This cell refers to another that isn't done yet
981 (add-to-list 'ses--deferred-recalc this-sym)
982 (throw 'ref t)))))
983 ;; ses-update-cells is called from post-command-hook, so
984 ;; inhibit-quit is implicitly bound to t.
985 (when quit-flag
986 ;; Abort the recalculation. User will probably undo now.
987 (error "Quit"))
988 (ses-calculate-cell (car this-rowcol) (cdr this-rowcol) force)))
989 (dolist (ref ses--deferred-recalc)
990 (add-to-list 'nextlist ref)))
991 (when ses--deferred-recalc
992 ;; Just couldn't finish these.
993 (dolist (x ses--deferred-recalc)
994 (let ((this-rowcol (ses-sym-rowcol x)))
995 (ses-set-cell (car this-rowcol) (cdr this-rowcol) 'value '*error*)
996 (1value (ses-print-cell (car this-rowcol) (cdr this-rowcol)))))
997 (error "Circular references: %s" ses--deferred-recalc))
998 (message " "))
999 ;; Can't use save-excursion here: if the cell under point is updated,
1000 ;; save-excursion's marker will move past the cell.
1001 (goto-char pos)))
1002
1003
1004 ;;----------------------------------------------------------------------------
1005 ;; The print area
1006 ;;----------------------------------------------------------------------------
1007
1008 (defun ses-in-print-area ()
1009 "Return t if point is in print area of spreadsheet."
1010 (<= (point) ses--data-marker))
1011
1012 ;; We turn off point-motion-hooks and explicitly position the cursor, in case
1013 ;; the intangible properties have gotten screwed up (e.g., when ses-goto-print
1014 ;; is called during a recursive ses-print-cell).
1015 (defun ses-goto-print (row col)
1016 "Move point to print area for cell (ROW,COL)."
1017 (let ((inhibit-point-motion-hooks t)
1018 (n 0))
1019 (goto-char (point-min))
1020 (forward-line row)
1021 ;; Calculate column position.
1022 (dotimes (c col)
1023 (setq n (+ n (ses-col-width c) 1)))
1024 ;; Move to the position.
1025 (and (> n (move-to-column n))
1026 (eolp)
1027 ;; Move point to the bol of next line (for TAB at the last cell).
1028 (forward-char))))
1029
1030 (defun ses-set-curcell ()
1031 "Set `ses--curcell' to the current cell symbol, or a cons (BEG,END) for a
1032 region, or nil if cursor is not at a cell."
1033 (if (or (not mark-active)
1034 deactivate-mark
1035 (= (region-beginning) (region-end)))
1036 ;; Single cell.
1037 (setq ses--curcell (get-text-property (point) 'intangible))
1038 ;; Range.
1039 (let ((bcell (get-text-property (region-beginning) 'intangible))
1040 (ecell (get-text-property (1- (region-end)) 'intangible)))
1041 (when (= (region-end) ses--data-marker)
1042 ;; Correct for overflow.
1043 (setq ecell (get-text-property (- (region-end) 2) 'intangible)))
1044 (setq ses--curcell (if (and bcell ecell)
1045 (cons bcell ecell)
1046 nil))))
1047 nil)
1048
1049 (defun ses-check-curcell (&rest args)
1050 "Signal an error if `ses--curcell' is inappropriate.
1051 The end marker is appropriate if some argument is 'end.
1052 A range is appropriate if some argument is 'range.
1053 A single cell is appropriate unless some argument is 'needrange."
1054 (if (eq ses--curcell t)
1055 ;; curcell recalculation was postponed, but user typed ahead.
1056 (ses-set-curcell))
1057 (cond
1058 ((not ses--curcell)
1059 (or (memq 'end args)
1060 (error "Not at cell")))
1061 ((consp ses--curcell)
1062 (or (memq 'range args)
1063 (memq 'needrange args)
1064 (error "Can't use a range")))
1065 ((memq 'needrange args)
1066 (error "Need a range"))))
1067
1068 (defun ses-print-cell (row col)
1069 "Format and print the value of cell (ROW,COL) to the print area.
1070 Use the cell's printer function. If the cell's new print form is too wide,
1071 it will spill over into the following cell, but will not run off the end of the
1072 row or overwrite the next non-nil field. Result is nil for normal operation,
1073 or the error signal if the printer function failed and the cell was formatted
1074 with \"%s\". If the cell's value is *skip*, nothing is printed because the
1075 preceding cell has spilled over."
1076 (catch 'ses-print-cell
1077 (let* ((cell (ses-get-cell row col))
1078 (value (ses-cell-value cell))
1079 (printer (ses-cell-printer cell))
1080 (maxcol (1+ col))
1081 text sig startpos x)
1082 ;; Create the string to print.
1083 (cond
1084 ((eq value '*skip*)
1085 ;; Don't print anything.
1086 (throw 'ses-print-cell nil))
1087 ((eq value '*error*)
1088 (setq text (make-string (ses-col-width col) ?#)))
1089 (t
1090 ;; Deferred safety-check on printer.
1091 (if (eq (car-safe printer) 'ses-safe-printer)
1092 (ses-set-cell row col 'printer
1093 (setq printer (ses-safe-printer (cadr printer)))))
1094 ;; Print the value.
1095 (setq text (ses-call-printer (or printer
1096 (ses-col-printer col)
1097 ses--default-printer)
1098 value))
1099 (if (consp ses-call-printer-return)
1100 ;; Printer returned an error.
1101 (setq sig ses-call-printer-return))))
1102 ;; Adjust print width to match column width.
1103 (let ((width (ses-col-width col))
1104 (len (string-width text)))
1105 (cond
1106 ((< len width)
1107 ;; Fill field to length with spaces.
1108 (setq len (make-string (- width len) ?\s)
1109 text (if (eq ses-call-printer-return t)
1110 (concat text len)
1111 (concat len text))))
1112 ((> len width)
1113 ;; Spill over into following cells, if possible.
1114 (let ((maxwidth width))
1115 (while (and (> len maxwidth)
1116 (< maxcol ses--numcols)
1117 (or (not (setq x (ses-cell-value row maxcol)))
1118 (eq x '*skip*)))
1119 (unless x
1120 ;; Set this cell to '*skip* so it won't overwrite our spillover.
1121 (ses-set-cell row maxcol 'value '*skip*))
1122 (setq maxwidth (+ maxwidth (ses-col-width maxcol) 1)
1123 maxcol (1+ maxcol)))
1124 (if (<= len maxwidth)
1125 ;; Fill to complete width of all the fields spanned.
1126 (setq text (concat text (make-string (- maxwidth len) ?\s)))
1127 ;; Not enough room to end of line or next non-nil field. Truncate
1128 ;; if string or decimal; otherwise fill with error indicator.
1129 (setq sig `(error "Too wide" ,text))
1130 (cond
1131 ((stringp value)
1132 (setq text (truncate-string-to-width text maxwidth 0 ?\s)))
1133 ((and (numberp value)
1134 (string-match "\\.[0-9]+" text)
1135 (>= 0 (setq width
1136 (- len maxwidth
1137 (- (match-end 0) (match-beginning 0))))))
1138 ;; Turn 6.6666666666e+49 into 6.66e+49. Rounding is too hard!
1139 (setq text (concat (substring text
1140 0
1141 (- (match-beginning 0) width))
1142 (substring text (match-end 0)))))
1143 (t
1144 (setq text (make-string maxwidth ?#)))))))))
1145 ;; Substitute question marks for tabs and newlines. Newlines are used as
1146 ;; row-separators; tabs could confuse the reimport logic.
1147 (setq text (replace-regexp-in-string "[\t\n]" "?" text))
1148 (ses-goto-print row col)
1149 (setq startpos (point))
1150 ;; Install the printed result. This is not interruptible.
1151 (let ((inhibit-read-only t)
1152 (inhibit-quit t))
1153 (let ((inhibit-point-motion-hooks t))
1154 (delete-region (point) (progn
1155 (move-to-column (+ (current-column)
1156 (string-width text)))
1157 (1+ (point)))))
1158 ;; We use concat instead of inserting separate strings in order to
1159 ;; reduce the number of cells in the undo list.
1160 (setq x (concat text (if (< maxcol ses--numcols) " " "\n")))
1161 ;; We use set-text-properties to prevent a wacky print function from
1162 ;; inserting rogue properties, and to ensure that the keymap property is
1163 ;; inherited (is it a bug that only unpropertized strings actually
1164 ;; inherit from surrounding text?)
1165 (set-text-properties 0 (length x) nil x)
1166 (insert-and-inherit x)
1167 (put-text-property startpos (point) 'intangible
1168 (ses-cell-symbol cell))
1169 (when (and (zerop row) (zerop col))
1170 ;; Reconstruct special beginning-of-buffer attributes.
1171 (put-text-property (point-min) (point) 'keymap 'ses-mode-print-map)
1172 (put-text-property (point-min) (point) 'read-only 'ses)
1173 (put-text-property (point-min) (1+ (point-min)) 'front-sticky t)))
1174 (if (= row (1- ses--header-row))
1175 ;; This line is part of the header --- force recalc.
1176 (ses-reset-header-string))
1177 ;; If this cell (or a preceding one on the line) previously spilled over
1178 ;; and has gotten shorter, redraw following cells on line recursively.
1179 (when (and (< maxcol ses--numcols)
1180 (eq (ses-cell-value row maxcol) '*skip*))
1181 (ses-set-cell row maxcol 'value nil)
1182 (ses-print-cell row maxcol))
1183 ;; Return to start of cell.
1184 (goto-char startpos)
1185 sig)))
1186
1187 (defun ses-call-printer (printer &optional value)
1188 "Invoke PRINTER (a string or parenthesized string or function-symbol or
1189 lambda of one argument) on VALUE. Result is the printed cell as a string.
1190 The variable `ses-call-printer-return' is set to t if the printer used
1191 parenthesis to request left-justification, or the error-signal if the
1192 printer signaled one (and \"%s\" is used as the default printer), else nil."
1193 (setq ses-call-printer-return nil)
1194 (condition-case signal
1195 (cond
1196 ((stringp printer)
1197 (if value
1198 (format printer value)
1199 ""))
1200 ((stringp (car-safe printer))
1201 (setq ses-call-printer-return t)
1202 (if value
1203 (format (car printer) value)
1204 ""))
1205 (t
1206 (setq value (funcall printer (or value "")))
1207 (if (stringp value)
1208 value
1209 (or (stringp (car-safe value))
1210 (error "Printer should return \"string\" or (\"string\")"))
1211 (setq ses-call-printer-return t)
1212 (car value))))
1213 (error
1214 (setq ses-call-printer-return signal)
1215 (prin1-to-string value t))))
1216
1217 (defun ses-adjust-print-width (col change)
1218 "Insert CHANGE spaces in front of column COL, or at end of line if
1219 COL=NUMCOLS. Deletes characters if CHANGE < 0. Caller should bind
1220 `inhibit-quit' to t."
1221 (let ((inhibit-read-only t)
1222 (blank (if (> change 0) (make-string change ?\s)))
1223 (at-end (= col ses--numcols)))
1224 (ses-set-with-undo 'ses--linewidth (+ ses--linewidth change))
1225 ;; ses-set-with-undo always returns t for strings.
1226 (1value (ses-set-with-undo 'ses--blank-line
1227 (concat (make-string ses--linewidth ?\s) "\n")))
1228 (dotimes (row ses--numrows)
1229 (ses-goto-print row col)
1230 (when at-end
1231 ;; Insert new columns before newline.
1232 (let ((inhibit-point-motion-hooks t))
1233 (backward-char 1)))
1234 (if blank
1235 (insert blank)
1236 (delete-char (- change))))))
1237
1238 (defun ses-print-cell-new-width (row col)
1239 "Same as `ses-print-cell', except if the cell's value is *skip*,
1240 the preceding nonskipped cell is reprinted. This function is used
1241 when the width of cell (ROW,COL) has changed."
1242 (if (not (eq (ses-cell-value row col) '*skip*))
1243 (ses-print-cell row col)
1244 ;;Cell was skipped over - reprint previous
1245 (ses-goto-print row col)
1246 (backward-char 1)
1247 (let ((rowcol (ses-sym-rowcol (get-text-property (point) 'intangible))))
1248 (ses-print-cell (car rowcol) (cdr rowcol)))))
1249
1250
1251 ;;----------------------------------------------------------------------------
1252 ;; The data area
1253 ;;----------------------------------------------------------------------------
1254
1255 (defun ses-narrowed-p () (/= (- (point-max) (point-min)) (buffer-size)))
1256
1257 (defun ses-widen ()
1258 "Turn off narrowing, to be reenabled at end of command loop."
1259 (if (ses-narrowed-p)
1260 (setq ses--deferred-narrow t))
1261 (widen))
1262
1263 (defun ses-goto-data (def &optional col)
1264 "Move point to data area for (DEF,COL). If DEF is a row
1265 number, COL is the column number for a data cell -- otherwise DEF
1266 is one of the symbols ses--col-widths, ses--col-printers,
1267 ses--default-printer, ses--numrows, or ses--numcols."
1268 (ses-widen)
1269 (let ((inhibit-point-motion-hooks t)) ; In case intangible attrs are wrong.
1270 (if col
1271 ;; It's a cell.
1272 (progn
1273 (goto-char ses--data-marker)
1274 (forward-line (+ 1 (* def (1+ ses--numcols)) col)))
1275 ;; Convert def-symbol to offset.
1276 (setq def (plist-get ses-paramlines-plist def))
1277 (or def (signal 'args-out-of-range nil))
1278 (goto-char ses--params-marker)
1279 (forward-line def))))
1280
1281 (defun ses-set-parameter (def value &optional elem)
1282 "Set parameter DEF to VALUE (with undo) and write the value to the data area.
1283 See `ses-goto-data' for meaning of DEF. Newlines in the data are escaped.
1284 If ELEM is specified, it is the array subscript within DEF to be set to VALUE."
1285 (save-excursion
1286 ;; We call ses-goto-data early, using the old values of numrows and numcols
1287 ;; in case one of them is being changed.
1288 (ses-goto-data def)
1289 (let ((inhibit-read-only t)
1290 (fmt (plist-get '(ses--col-widths "(ses-column-widths %S)"
1291 ses--col-printers "(ses-column-printers %S)"
1292 ses--default-printer "(ses-default-printer %S)"
1293 ses--header-row "(ses-header-row %S)"
1294 ses--file-format " %S ;SES file-format"
1295 ses--numrows " %S ;numrows"
1296 ses--numcols " %S ;numcols")
1297 def))
1298 oldval)
1299 (if elem
1300 (progn
1301 (setq oldval (aref (symbol-value def) elem))
1302 (aset (symbol-value def) elem value))
1303 (setq oldval (symbol-value def))
1304 (set def value))
1305 ;; Special undo since it's outside the narrowed buffer.
1306 (let (buffer-undo-list)
1307 (delete-region (point) (line-end-position))
1308 (insert (format fmt (symbol-value def))))
1309 (push `(apply ses-set-parameter ,def ,oldval ,elem) buffer-undo-list))))
1310
1311
1312 (defun ses-write-cells ()
1313 "Write cells in `ses--deferred-write' from local variables to data area.
1314 Newlines in the data are escaped."
1315 (let* ((inhibit-read-only t)
1316 (print-escape-newlines t)
1317 rowcol row col cell sym formula printer text)
1318 (setq ses-start-time (float-time))
1319 (with-temp-message " "
1320 (save-excursion
1321 (while ses--deferred-write
1322 (ses-time-check "Writing... (%d cells left)"
1323 '(length ses--deferred-write))
1324 (setq rowcol (pop ses--deferred-write)
1325 row (car rowcol)
1326 col (cdr rowcol)
1327 cell (ses-get-cell row col)
1328 sym (ses-cell-symbol cell)
1329 formula (ses-cell-formula cell)
1330 printer (ses-cell-printer cell))
1331 (if (eq (car-safe formula) 'ses-safe-formula)
1332 (setq formula (cadr formula)))
1333 (if (eq (car-safe printer) 'ses-safe-printer)
1334 (setq printer (cadr printer)))
1335 ;; This is noticeably faster than (format "%S %S %S %S %S")
1336 (setq text (concat "(ses-cell "
1337 (symbol-name sym)
1338 " "
1339 (prin1-to-string (symbol-value sym))
1340 " "
1341 (prin1-to-string formula)
1342 " "
1343 (prin1-to-string printer)
1344 " "
1345 (if (atom (ses-cell-references cell))
1346 "nil"
1347 (concat "("
1348 (mapconcat 'symbol-name
1349 (ses-cell-references cell)
1350 " ")
1351 ")"))
1352 ")"))
1353 (ses-goto-data row col)
1354 (delete-region (point) (line-end-position))
1355 (insert text)))
1356 (message " "))))
1357
1358
1359 ;;----------------------------------------------------------------------------
1360 ;; Formula relocation
1361 ;;----------------------------------------------------------------------------
1362
1363 (defun ses-formula-references (formula &optional result-so-far)
1364 "Produce a list of symbols for cells that this FORMULA's value
1365 refers to. For recursive calls, RESULT-SO-FAR is the list being
1366 constructed, or t to get a wrong-type-argument error when the
1367 first reference is found."
1368 (if (ses-sym-rowcol formula)
1369 ;;Entire formula is one symbol
1370 (add-to-list 'result-so-far formula)
1371 (if (consp formula)
1372 (cond
1373 ((eq (car formula) 'ses-range)
1374 (dolist (cur
1375 (cdr (funcall 'macroexpand
1376 (list 'ses-range (nth 1 formula)
1377 (nth 2 formula)))))
1378 (add-to-list 'result-so-far cur)))
1379 ((null (eq (car formula) 'quote))
1380 ;;Recursive call for subformulas
1381 (dolist (cur formula)
1382 (setq result-so-far (ses-formula-references cur result-so-far))))
1383 (t
1384 ;;Ignore other stuff
1385 ))
1386 ;; other type of atom are ignored
1387 ))
1388 result-so-far)
1389
1390 (defsubst ses-relocate-symbol (sym rowcol startrow startcol rowincr colincr)
1391 "Relocate one symbol SYM, which corresponds to ROWCOL (a cons of ROW and
1392 COL). Cells starting at (STARTROW,STARTCOL) are being shifted
1393 by (ROWINCR,COLINCR)."
1394 (let ((row (car rowcol))
1395 (col (cdr rowcol)))
1396 (if (or (< row startrow) (< col startcol))
1397 sym
1398 (setq row (+ row rowincr)
1399 col (+ col colincr))
1400 (if (and (>= row startrow) (>= col startcol)
1401 (< row ses--numrows) (< col ses--numcols))
1402 ;;Relocate this variable
1403 (ses-create-cell-symbol row col)
1404 ;;Delete reference to a deleted cell
1405 nil))))
1406
1407 (defun ses-relocate-formula (formula startrow startcol rowincr colincr)
1408 "Produce a copy of FORMULA where all symbols that refer to cells in row
1409 STARTROW or above, and col STARTCOL or above, are altered by adding ROWINCR
1410 and COLINCR. STARTROW and STARTCOL are 0-based. Example:
1411 (ses-relocate-formula '(+ A1 B2 D3) 1 2 1 -1)
1412 => (+ A1 B2 C4)
1413 If ROWINCR or COLINCR is negative, references to cells being deleted are
1414 removed. Example:
1415 (ses-relocate-formula '(+ A1 B2 D3) 0 1 0 -1)
1416 => (+ A1 C3)
1417 Sets `ses-relocate-return' to 'delete if cell-references were removed."
1418 (let (rowcol result)
1419 (if (or (atom formula) (eq (car formula) 'quote))
1420 (if (and (setq rowcol (ses-sym-rowcol formula))
1421 (string-match "\\`[A-Z]+[0-9]+\\'" (symbol-name formula)))
1422 (ses-relocate-symbol formula rowcol
1423 startrow startcol rowincr colincr)
1424 formula) ; Pass through as-is.
1425 (dolist (cur formula)
1426 (setq rowcol (ses-sym-rowcol cur))
1427 (cond
1428 (rowcol
1429 (setq cur (ses-relocate-symbol cur rowcol
1430 startrow startcol rowincr colincr))
1431 (if cur
1432 (push cur result)
1433 ;; Reference to a deleted cell. Set a flag in ses-relocate-return.
1434 ;; don't change the flag if it's already 'range, since range implies
1435 ;; 'delete.
1436 (unless ses-relocate-return
1437 (setq ses-relocate-return 'delete))))
1438 ((eq (car-safe cur) 'ses-range)
1439 (setq cur (ses-relocate-range cur startrow startcol rowincr colincr))
1440 (if cur
1441 (push cur result)))
1442 ((or (atom cur) (eq (car cur) 'quote))
1443 ;; Constants pass through unchanged.
1444 (push cur result))
1445 (t
1446 ;; Recursively copy and alter subformulas.
1447 (push (ses-relocate-formula cur startrow startcol
1448 rowincr colincr)
1449 result))))
1450 (nreverse result))))
1451
1452 (defun ses-relocate-range (range startrow startcol rowincr colincr)
1453 "Relocate one RANGE, of the form '(ses-range min max). Cells starting
1454 at (STARTROW,STARTCOL) are being shifted by (ROWINCR,COLINCR). Result is the
1455 new range, or nil if the entire range is deleted. If new rows are being added
1456 just beyond the end of a row range, or new columns just beyond a column range,
1457 the new rows/columns will be added to the range. Sets `ses-relocate-return'
1458 if the range was altered."
1459 (let* ((minorig (cadr range))
1460 (minrowcol (ses-sym-rowcol minorig))
1461 (min (ses-relocate-symbol minorig minrowcol
1462 startrow startcol
1463 rowincr colincr))
1464 (maxorig (nth 2 range))
1465 (maxrowcol (ses-sym-rowcol maxorig))
1466 (max (ses-relocate-symbol maxorig maxrowcol
1467 startrow startcol
1468 rowincr colincr))
1469 field)
1470 (cond
1471 ((and (not min) (not max))
1472 (setq range nil)) ; The entire range is deleted.
1473 ((zerop colincr)
1474 ;; Inserting or deleting rows.
1475 (setq field 'car)
1476 (if (not min)
1477 ;; Chopped off beginning of range.
1478 (setq min (ses-create-cell-symbol startrow (cdr minrowcol))
1479 ses-relocate-return 'range))
1480 (if (not max)
1481 (if (> rowincr 0)
1482 ;; Trying to insert a nonexistent row.
1483 (setq max (ses-create-cell-symbol (1- ses--numrows)
1484 (cdr minrowcol)))
1485 ;; End of range is being deleted.
1486 (setq max (ses-create-cell-symbol (1- startrow) (cdr minrowcol))
1487 ses-relocate-return 'range))
1488 (and (> rowincr 0)
1489 (= (car maxrowcol) (1- startrow))
1490 (= (cdr minrowcol) (cdr maxrowcol))
1491 ;; Insert after ending row of vertical range --- include it.
1492 (setq max (ses-create-cell-symbol (+ startrow rowincr -1)
1493 (cdr maxrowcol))))))
1494 (t
1495 ;; Inserting or deleting columns.
1496 (setq field 'cdr)
1497 (if (not min)
1498 ;; Chopped off beginning of range.
1499 (setq min (ses-create-cell-symbol (car minrowcol) startcol)
1500 ses-relocate-return 'range))
1501 (if (not max)
1502 (if (> colincr 0)
1503 ;; Trying to insert a nonexistent column.
1504 (setq max (ses-create-cell-symbol (car maxrowcol)
1505 (1- ses--numcols)))
1506 ;; End of range is being deleted.
1507 (setq max (ses-create-cell-symbol (car maxrowcol) (1- startcol))
1508 ses-relocate-return 'range))
1509 (and (> colincr 0)
1510 (= (cdr maxrowcol) (1- startcol))
1511 (= (car minrowcol) (car maxrowcol))
1512 ;; Insert after ending column of horizontal range --- include it.
1513 (setq max (ses-create-cell-symbol (car maxrowcol)
1514 (+ startcol colincr -1)))))))
1515 (when range
1516 (if (/= (- (funcall field maxrowcol)
1517 (funcall field minrowcol))
1518 (- (funcall field (ses-sym-rowcol max))
1519 (funcall field (ses-sym-rowcol min))))
1520 ;; This range has changed size.
1521 (setq ses-relocate-return 'range))
1522 `(ses-range ,min ,max ,@(cdddr range)))))
1523
1524 (defun ses-relocate-all (minrow mincol rowincr colincr)
1525 "Alter all cell values, symbols, formulas, and reference-lists to relocate
1526 the rectangle (MINROW,MINCOL)..(NUMROWS,NUMCOLS) by adding ROWINCR and COLINCR
1527 to each symbol."
1528 (let (reform)
1529 (let (mycell newval xrow)
1530 (dotimes-with-progress-reporter
1531 (row ses--numrows) "Relocating formulas..."
1532 (dotimes (col ses--numcols)
1533 (setq ses-relocate-return nil
1534 mycell (ses-get-cell row col)
1535 newval (ses-relocate-formula (ses-cell-formula mycell)
1536 minrow mincol rowincr colincr)
1537 xrow (- row rowincr))
1538 (ses-set-cell row col 'formula newval)
1539 (if (eq ses-relocate-return 'range)
1540 ;; This cell contains a (ses-range X Y) where a cell has been
1541 ;; inserted or deleted in the middle of the range.
1542 (push (cons row col) reform))
1543 (if ses-relocate-return
1544 ;; This cell referred to a cell that's been deleted or is no
1545 ;; longer part of the range. We can't fix that now because
1546 ;; reference lists cells have been partially updated.
1547 (add-to-list 'ses--deferred-recalc
1548 (ses-create-cell-symbol row col)))
1549 (setq newval (ses-relocate-formula (ses-cell-references mycell)
1550 minrow mincol rowincr colincr))
1551 (ses-set-cell row col 'references newval)
1552 (and (>= row minrow) (>= col mincol)
1553 (let ((sym (ses-cell-symbol row col))
1554 (xcol (- col colincr)))
1555 (if (and
1556 sym
1557 (>= xrow 0)
1558 (>= xcol 0)
1559 (null (eq sym
1560 (ses-create-cell-symbol xrow xcol))))
1561 ;; This is a renamed cell, do not update the cell
1562 ;; name, but just update the coordinate property.
1563 (put sym 'ses-cell (cons row col))
1564 (ses-set-cell row col 'symbol
1565 (setq sym (ses-create-cell-symbol row col)))
1566 (unless (and (boundp sym) (local-variable-p sym))
1567 (set (make-local-variable sym) nil)
1568 (put sym 'ses-cell (cons row col)))))) )))
1569 ;; Relocate the cell values.
1570 (let (oldval myrow mycol xrow xcol)
1571 (cond
1572 ((and (<= rowincr 0) (<= colincr 0))
1573 ;; Deletion of rows and/or columns.
1574 (dotimes-with-progress-reporter
1575 (row (- ses--numrows minrow)) "Relocating variables..."
1576 (setq myrow (+ row minrow))
1577 (dotimes (col (- ses--numcols mincol))
1578 (setq mycol (+ col mincol)
1579 xrow (- myrow rowincr)
1580 xcol (- mycol colincr))
1581 (let ((sym (ses-cell-symbol myrow mycol))
1582 (xsym (ses-create-cell-symbol xrow xcol)))
1583 ;; Make the value relocation only when if the cell is not
1584 ;; a renamed cell. Otherwise this is not needed.
1585 (and (eq sym xsym)
1586 (ses-set-cell myrow mycol 'value
1587 (if (and (< xrow ses--numrows) (< xcol ses--numcols))
1588 (ses-cell-value xrow xcol)
1589 ;;Cell is off the end of the array
1590 (symbol-value xsym))))))))
1591
1592 ((and (wholenump rowincr) (wholenump colincr))
1593 ;; Insertion of rows and/or columns. Run the loop backwards.
1594 (let ((disty (1- ses--numrows))
1595 (distx (1- ses--numcols))
1596 myrow mycol)
1597 (dotimes-with-progress-reporter
1598 (row (- ses--numrows minrow)) "Relocating variables..."
1599 (setq myrow (- disty row))
1600 (dotimes (col (- ses--numcols mincol))
1601 (setq mycol (- distx col)
1602 xrow (- myrow rowincr)
1603 xcol (- mycol colincr))
1604 (if (or (< xrow minrow) (< xcol mincol))
1605 ;; Newly-inserted value.
1606 (setq oldval nil)
1607 ;; Transfer old value.
1608 (setq oldval (ses-cell-value xrow xcol)))
1609 (ses-set-cell myrow mycol 'value oldval)))
1610 t)) ; Make testcover happy by returning non-nil here.
1611 (t
1612 (error "ROWINCR and COLINCR must have the same sign"))))
1613 ;; Reconstruct reference lists for cells that contain ses-ranges that have
1614 ;; changed size.
1615 (when reform
1616 (message "Fixing ses-ranges...")
1617 (let (row col)
1618 (setq ses-start-time (float-time))
1619 (while reform
1620 (ses-time-check "Fixing ses-ranges... (%d left)" '(length reform))
1621 (setq row (caar reform)
1622 col (cdar reform)
1623 reform (cdr reform))
1624 (ses-cell-set-formula row col (ses-cell-formula row col))))
1625 (message nil))))
1626
1627
1628 ;;----------------------------------------------------------------------------
1629 ;; Undo control
1630 ;;----------------------------------------------------------------------------
1631
1632 (defun ses-begin-change ()
1633 "For undo, remember point before we start changing hidden stuff."
1634 (let ((inhibit-read-only t))
1635 (insert-and-inherit "X")
1636 (delete-region (1- (point)) (point))))
1637
1638 (defun ses-set-with-undo (sym newval)
1639 "Like set, but undoable. Result is t if value has changed."
1640 ;; We try to avoid adding redundant entries to the undo list, but this is
1641 ;; unavoidable for strings because equal ignores text properties and there's
1642 ;; no easy way to get the whole property list to see if it's different!
1643 (unless (and (boundp sym)
1644 (equal (symbol-value sym) newval)
1645 (not (stringp newval)))
1646 (push (if (boundp sym)
1647 `(apply ses-set-with-undo ,sym ,(symbol-value sym))
1648 `(apply ses-unset-with-undo ,sym))
1649 buffer-undo-list)
1650 (set sym newval)
1651 t))
1652
1653 (defun ses-unset-with-undo (sym)
1654 "Set SYM to be unbound. This is undoable."
1655 (when (1value (boundp sym)) ; Always bound, except after a programming error.
1656 (push `(apply ses-set-with-undo ,sym ,(symbol-value sym)) buffer-undo-list)
1657 (makunbound sym)))
1658
1659 (defun ses-aset-with-undo (array idx newval)
1660 "Like `aset', but undoable.
1661 Result is t if element has changed."
1662 (unless (equal (aref array idx) newval)
1663 (push `(apply ses-aset-with-undo ,array ,idx
1664 ,(aref array idx)) buffer-undo-list)
1665 (aset array idx newval)
1666 t))
1667
1668
1669 ;;----------------------------------------------------------------------------
1670 ;; Startup for major mode
1671 ;;----------------------------------------------------------------------------
1672
1673 (defun ses-load ()
1674 "Parse the current buffer and set up buffer-local variables.
1675 Does not execute cell formulas or print functions."
1676 (widen)
1677 ;; Read our global parameters, which should be a 3-element list.
1678 (goto-char (point-max))
1679 (search-backward ";; Local Variables:\n" nil t)
1680 (backward-list 1)
1681 (setq ses--params-marker (point-marker))
1682 (let ((params (condition-case nil (read (current-buffer)) (error nil))))
1683 (or (and (= (safe-length params) 3)
1684 (numberp (car params))
1685 (numberp (cadr params))
1686 (>= (cadr params) 0)
1687 (numberp (nth 2 params))
1688 (> (nth 2 params) 0))
1689 (error "Invalid SES file"))
1690 (setq ses--file-format (car params)
1691 ses--numrows (cadr params)
1692 ses--numcols (nth 2 params))
1693 (when (= ses--file-format 1)
1694 (let (buffer-undo-list) ; This is not undoable.
1695 (ses-goto-data 'ses--header-row)
1696 (insert "(ses-header-row 0)\n")
1697 (ses-set-parameter 'ses--file-format 2)
1698 (message "Upgrading from SES-1 file format")))
1699 (or (= ses--file-format 2)
1700 (error "This file needs a newer version of the SES library code"))
1701 ;; Initialize cell array.
1702 (setq ses--cells (make-vector ses--numrows nil))
1703 (dotimes (row ses--numrows)
1704 (aset ses--cells row (make-vector ses--numcols nil))))
1705 ;; Skip over print area, which we assume is correct.
1706 (goto-char (point-min))
1707 (forward-line ses--numrows)
1708 (or (looking-at ses-print-data-boundary)
1709 (error "Missing marker between print and data areas"))
1710 (forward-char 1)
1711 (setq ses--data-marker (point-marker))
1712 (forward-char (1- (length ses-print-data-boundary)))
1713 ;; Initialize printer and symbol lists.
1714 (mapc 'ses-printer-record ses-standard-printer-functions)
1715 (setq ses--symbolic-formulas nil)
1716 ;; Load cell definitions.
1717 (dotimes (row ses--numrows)
1718 (dotimes (col ses--numcols)
1719 (let* ((x (read (current-buffer)))
1720 (sym (car-safe (cdr-safe x))))
1721 (or (and (looking-at "\n")
1722 (eq (car-safe x) 'ses-cell)
1723 (ses-create-cell-variable sym row col))
1724 (error "Cell-def error"))
1725 (eval x)))
1726 (or (looking-at "\n\n")
1727 (error "Missing blank line between rows")))
1728 ;; Load global parameters.
1729 (let ((widths (read (current-buffer)))
1730 (n1 (char-after (point)))
1731 (printers (read (current-buffer)))
1732 (n2 (char-after (point)))
1733 (def-printer (read (current-buffer)))
1734 (n3 (char-after (point)))
1735 (head-row (read (current-buffer)))
1736 (n4 (char-after (point))))
1737 (or (and (eq (car-safe widths) 'ses-column-widths)
1738 (= n1 ?\n)
1739 (eq (car-safe printers) 'ses-column-printers)
1740 (= n2 ?\n)
1741 (eq (car-safe def-printer) 'ses-default-printer)
1742 (= n3 ?\n)
1743 (eq (car-safe head-row) 'ses-header-row)
1744 (= n4 ?\n))
1745 (error "Invalid SES global parameters"))
1746 (1value (eval widths))
1747 (1value (eval def-printer))
1748 (1value (eval printers))
1749 (1value (eval head-row)))
1750 ;; Should be back at global-params.
1751 (forward-char 1)
1752 (or (looking-at (replace-regexp-in-string "1" "[0-9]+"
1753 ses-initial-global-parameters))
1754 (error "Problem with column-defs or global-params"))
1755 ;; Check for overall newline count in definitions area.
1756 (forward-line 3)
1757 (let ((start (point)))
1758 (ses-goto-data 'ses--numrows)
1759 (or (= (point) start)
1760 (error "Extraneous newlines someplace?"))))
1761
1762 (defun ses-setup ()
1763 "Set up for display of only the printed cell values.
1764
1765 Narrows the buffer to show only the print area. Gives it `read-only' and
1766 `intangible' properties. Sets up highlighting for current cell."
1767 (interactive)
1768 (let ((end (point-min))
1769 (inhibit-read-only t)
1770 (inhibit-point-motion-hooks t)
1771 (was-modified (buffer-modified-p))
1772 pos sym)
1773 (ses-goto-data 0 0) ; Include marker between print-area and data-area.
1774 (set-text-properties (point) (point-max) nil) ; Delete garbage props.
1775 (mapc 'delete-overlay (overlays-in (point-min) (point-max)))
1776 ;; The print area is read-only (except for our special commands) and uses a
1777 ;; special keymap.
1778 (put-text-property (point-min) (1- (point)) 'read-only 'ses)
1779 (put-text-property (point-min) (1- (point)) 'keymap 'ses-mode-print-map)
1780 ;; For the beginning of the buffer, we want the read-only and keymap
1781 ;; attributes to be inherited from the first character.
1782 (put-text-property (point-min) (1+ (point-min)) 'front-sticky t)
1783 ;; Create intangible properties, which also indicate which cell the text
1784 ;; came from.
1785 (dotimes-with-progress-reporter (row ses--numrows) "Finding cells..."
1786 (dotimes (col ses--numcols)
1787 (setq pos end
1788 sym (ses-cell-symbol row col))
1789 ;; Include skipped cells following this one.
1790 (while (and (< col (1- ses--numcols))
1791 (eq (ses-cell-value row (1+ col)) '*skip*))
1792 (setq end (+ end (ses-col-width col) 1)
1793 col (1+ col)))
1794 (setq end (save-excursion
1795 (goto-char pos)
1796 (move-to-column (+ (current-column) (- end pos)
1797 (ses-col-width col)))
1798 (if (eolp)
1799 (+ end (ses-col-width col) 1)
1800 (forward-char)
1801 (point))))
1802 (put-text-property pos end 'intangible sym)))
1803 ;; Adding these properties did not actually alter the text.
1804 (unless was-modified
1805 (restore-buffer-modified-p nil)
1806 (buffer-disable-undo)
1807 (buffer-enable-undo)))
1808 ;; Create the underlining overlay. It's impossible for (point) to be 2,
1809 ;; because column A must be at least 1 column wide.
1810 (setq ses--curcell-overlay (make-overlay (1+ (point-min)) (1+ (point-min))))
1811 (overlay-put ses--curcell-overlay 'face 'underline))
1812
1813 (defun ses-cleanup ()
1814 "Cleanup when changing a buffer from SES mode to something else.
1815 Delete overlays, remove special text properties."
1816 (widen)
1817 (let ((inhibit-read-only t)
1818 ;; When reverting, hide the buffer name, otherwise Emacs will ask the
1819 ;; user "the file is modified, do you really want to make modifications
1820 ;; to this buffer", where the "modifications" refer to the irrelevant
1821 ;; set-text-properties below.
1822 (buffer-file-name nil)
1823 (was-modified (buffer-modified-p)))
1824 ;; Delete read-only, keymap, and intangible properties.
1825 (set-text-properties (point-min) (point-max) nil)
1826 ;; Delete overlay.
1827 (mapc 'delete-overlay (overlays-in (point-min) (point-max)))
1828 (unless was-modified
1829 (restore-buffer-modified-p nil))))
1830
1831 ;;;###autoload
1832 (defun ses-mode ()
1833 "Major mode for Simple Emacs Spreadsheet.
1834 See \"ses-example.ses\" (in `data-directory') for more info.
1835
1836 Key definitions:
1837 \\{ses-mode-map}
1838 These key definitions are active only in the print area (the visible part):
1839 \\{ses-mode-print-map}
1840 These are active only in the minibuffer, when entering or editing a formula:
1841 \\{ses-mode-edit-map}"
1842 (interactive)
1843 (unless (and (boundp 'ses--deferred-narrow)
1844 (eq ses--deferred-narrow 'ses-mode))
1845 (kill-all-local-variables)
1846 (ses-set-localvars)
1847 (setq major-mode 'ses-mode
1848 mode-name "SES"
1849 next-line-add-newlines nil
1850 truncate-lines t
1851 ;; SES deliberately puts lots of trailing whitespace in its buffer.
1852 show-trailing-whitespace nil
1853 ;; Cell ranges do not work reasonably without this.
1854 transient-mark-mode t
1855 ;; Not to use tab characters for safe (tabs may do bad for column
1856 ;; calculation).
1857 indent-tabs-mode nil)
1858 (1value (add-hook 'change-major-mode-hook 'ses-cleanup nil t))
1859 (1value (add-hook 'before-revert-hook 'ses-cleanup nil t))
1860 (setq header-line-format '(:eval (progn
1861 (when (/= (window-hscroll)
1862 ses--header-hscroll)
1863 ;; Reset ses--header-hscroll first,
1864 ;; to avoid recursion problems when
1865 ;; debugging ses-create-header-string
1866 (setq ses--header-hscroll
1867 (window-hscroll))
1868 (ses-create-header-string))
1869 ses--header-string)))
1870 (let ((was-empty (zerop (buffer-size)))
1871 (was-modified (buffer-modified-p)))
1872 (save-excursion
1873 (if was-empty
1874 ;; Initialize buffer to contain one cell, for now.
1875 (insert ses-initial-file-contents))
1876 (ses-load)
1877 (ses-setup))
1878 (when was-empty
1879 (unless (equal ses-initial-default-printer
1880 (1value ses--default-printer))
1881 (1value (ses-read-default-printer ses-initial-default-printer)))
1882 (unless (= ses-initial-column-width (1value (ses-col-width 0)))
1883 (1value (ses-set-column-width 0 ses-initial-column-width)))
1884 (ses-set-curcell)
1885 (if (> (car ses-initial-size) (1value ses--numrows))
1886 (1value (ses-insert-row (1- (car ses-initial-size)))))
1887 (if (> (cdr ses-initial-size) (1value ses--numcols))
1888 (1value (ses-insert-column (1- (cdr ses-initial-size)))))
1889 (ses-write-cells)
1890 (restore-buffer-modified-p was-modified)
1891 (buffer-disable-undo)
1892 (buffer-enable-undo)
1893 (goto-char (point-min))))
1894 (use-local-map ses-mode-map)
1895 ;; Set the deferred narrowing flag (we can't narrow until after
1896 ;; after-find-file completes). If .ses is on the auto-load alist and the
1897 ;; file has "mode: ses", our ses-mode function will be called twice! Use a
1898 ;; special flag to detect this (will be reset by ses-command-hook). For
1899 ;; find-alternate-file, post-command-hook doesn't get run for some reason,
1900 ;; so use an idle timer to make sure.
1901 (setq ses--deferred-narrow 'ses-mode)
1902 (1value (add-hook 'post-command-hook 'ses-command-hook nil t))
1903 (run-with-idle-timer 0.01 nil 'ses-command-hook)
1904 (run-mode-hooks 'ses-mode-hook)))
1905
1906 (put 'ses-mode 'mode-class 'special)
1907
1908 (defun ses-command-hook ()
1909 "Invoked from `post-command-hook'. If point has moved to a different cell,
1910 moves the underlining overlay. Performs any recalculations or cell-data
1911 writes that have been deferred. If buffer-narrowing has been deferred,
1912 narrows the buffer now."
1913 (condition-case err
1914 (when (eq major-mode 'ses-mode) ; Otherwise, not our buffer anymore.
1915 (when ses--deferred-recalc
1916 ;; We reset the deferred list before starting on the recalc --- in
1917 ;; case of error, we don't want to retry the recalc after every
1918 ;; keystroke!
1919 (ses-initialize-Dijkstra-attempt)
1920 (let ((old ses--deferred-recalc))
1921 (setq ses--deferred-recalc nil)
1922 (ses-update-cells old)))
1923 (when ses--deferred-write
1924 ;; We don't reset the deferred list before starting --- the most
1925 ;; likely error is keyboard-quit, and we do want to keep trying these
1926 ;; writes after a quit.
1927 (ses-write-cells)
1928 (push '(apply ses-widen) buffer-undo-list))
1929 (when ses--deferred-narrow
1930 ;; We're not allowed to narrow the buffer until after-find-file has
1931 ;; read the local variables at the end of the file. Now it's safe to
1932 ;; do the narrowing.
1933 (narrow-to-region (point-min) ses--data-marker)
1934 (setq ses--deferred-narrow nil))
1935 ;; Update the modeline.
1936 (let ((oldcell ses--curcell))
1937 (ses-set-curcell)
1938 (unless (eq ses--curcell oldcell)
1939 (cond
1940 ((not ses--curcell)
1941 (setq mode-line-process nil))
1942 ((atom ses--curcell)
1943 (setq mode-line-process (list " cell "
1944 (symbol-name ses--curcell))))
1945 (t
1946 (setq mode-line-process (list " range "
1947 (symbol-name (car ses--curcell))
1948 "-"
1949 (symbol-name (cdr ses--curcell))))))
1950 (force-mode-line-update)))
1951 ;; Use underline overlay for single-cells only, turn off otherwise.
1952 (if (listp ses--curcell)
1953 (move-overlay ses--curcell-overlay 2 2)
1954 (let ((next (next-single-property-change (point) 'intangible)))
1955 (move-overlay ses--curcell-overlay (point) (1- next))))
1956 (when (not (pos-visible-in-window-p))
1957 ;; Scrolling will happen later.
1958 (run-with-idle-timer 0.01 nil 'ses-command-hook)
1959 (setq ses--curcell t)))
1960 ;; Prevent errors in this post-command-hook from silently erasing the hook!
1961 (error
1962 (unless executing-kbd-macro
1963 (ding))
1964 (message "%s" (error-message-string err))))
1965 nil) ; Make coverage-tester happy.
1966
1967 (defun ses-create-header-string ()
1968 "Set up `ses--header-string' as the buffer's header line.
1969 Based on the current set of columns and `window-hscroll' position."
1970 (let ((totwidth (- (window-hscroll)))
1971 result width x)
1972 ;; Leave room for the left-side fringe and scrollbar.
1973 (push (propertize " " 'display '((space :align-to 0))) result)
1974 (dotimes (col ses--numcols)
1975 (setq width (ses-col-width col)
1976 totwidth (+ totwidth width 1))
1977 (if (= totwidth 1)
1978 ;; Scrolled so intercolumn space is leftmost.
1979 (push " " result))
1980 (when (> totwidth 1)
1981 (if (> ses--header-row 0)
1982 (save-excursion
1983 (ses-goto-print (1- ses--header-row) col)
1984 (setq x (buffer-substring-no-properties (point)
1985 (+ (point) width)))
1986 ;; Strip trailing space.
1987 (if (string-match "[ \t]+\\'" x)
1988 (setq x (substring x 0 (match-beginning 0))))
1989 ;; Cut off excess text.
1990 (if (>= (length x) totwidth)
1991 (setq x (substring x 0 (- totwidth -1)))))
1992 (setq x (ses-column-letter col)))
1993 (push (propertize x 'face ses-box-prop) result)
1994 (push (propertize "."
1995 'display `((space :align-to ,(1- totwidth)))
1996 'face ses-box-prop)
1997 result)
1998 ;; Allow the following space to be squished to make room for the 3-D box
1999 ;; Coverage test ignores properties, thinks this is always a space!
2000 (push (1value (propertize " " 'display `((space :align-to ,totwidth))))
2001 result)))
2002 (if (> ses--header-row 0)
2003 (push (propertize (format " [row %d]" ses--header-row)
2004 'display '((height (- 1))))
2005 result))
2006 (setq ses--header-string (apply 'concat (nreverse result)))))
2007
2008
2009 ;;----------------------------------------------------------------------------
2010 ;; Redisplay and recalculation
2011 ;;----------------------------------------------------------------------------
2012
2013 (defun ses-jump (sym)
2014 "Move point to cell SYM."
2015 (interactive "SJump to cell: ")
2016 (let ((rowcol (ses-sym-rowcol sym)))
2017 (or rowcol (error "Invalid cell name"))
2018 (if (eq (symbol-value sym) '*skip*)
2019 (error "Cell is covered by preceding cell"))
2020 (ses-goto-print (car rowcol) (cdr rowcol))))
2021
2022 (defun ses-jump-safe (cell)
2023 "Like `ses-jump', but no error if invalid cell."
2024 (condition-case nil
2025 (ses-jump cell)
2026 (error)))
2027
2028 (defun ses-reprint-all (&optional nonarrow)
2029 "Recreate the display area. Calls all printer functions. Narrows to
2030 print area if NONARROW is nil."
2031 (interactive "*P")
2032 (widen)
2033 (unless nonarrow
2034 (setq ses--deferred-narrow t))
2035 (let ((startcell (get-text-property (point) 'intangible))
2036 (inhibit-read-only t))
2037 (ses-begin-change)
2038 (goto-char (point-min))
2039 (search-forward ses-print-data-boundary)
2040 (backward-char (length ses-print-data-boundary))
2041 (delete-region (point-min) (point))
2042 ;; Insert all blank lines before printing anything, so ses-print-cell can
2043 ;; find the data area when inserting or deleting *skip* values for cells.
2044 (dotimes (row ses--numrows)
2045 (insert-and-inherit ses--blank-line))
2046 (dotimes-with-progress-reporter (row ses--numrows) "Reprinting..."
2047 (if (eq (ses-cell-value row 0) '*skip*)
2048 ;; Column deletion left a dangling skip.
2049 (ses-set-cell row 0 'value nil))
2050 (dotimes (col ses--numcols)
2051 (ses-print-cell row col))
2052 (beginning-of-line 2))
2053 (ses-jump-safe startcell)))
2054
2055 (defun ses-initialize-Dijkstra-attempt ()
2056 (setq ses--Dijkstra-attempt-nb (1+ ses--Dijkstra-attempt-nb)
2057 ses--Dijkstra-weight-bound (* ses--numrows ses--numcols)))
2058
2059 (defun ses-recalculate-cell ()
2060 "Recalculate and reprint the current cell or range.
2061
2062 For an individual cell, shows the error if the formula or printer
2063 signals one, or otherwise shows the cell's complete value. For a range, the
2064 cells are recalculated in \"natural\" order, so cells that other cells refer
2065 to are recalculated first."
2066 (interactive "*")
2067 (ses-check-curcell 'range)
2068 (ses-begin-change)
2069 (ses-initialize-Dijkstra-attempt)
2070 (let (sig cur-rowcol)
2071 (setq ses-start-time (float-time))
2072 (if (atom ses--curcell)
2073 (when
2074 (setq cur-rowcol (ses-sym-rowcol ses--curcell)
2075 sig (progn
2076 (ses-cell-property-set :ses-Dijkstra-attempt
2077 (cons ses--Dijkstra-attempt-nb 0)
2078 (car cur-rowcol) (cdr cur-rowcol) )
2079 (ses-calculate-cell (car cur-rowcol) (cdr cur-rowcol) t)))
2080 (nconc sig (list (ses-cell-symbol (car cur-rowcol)
2081 (cdr cur-rowcol)))))
2082 ;; First, recalculate all cells that don't refer to other cells and
2083 ;; produce a list of cells with references.
2084 (ses-dorange ses--curcell
2085 (ses-time-check "Recalculating... %s" '(ses-cell-symbol row col))
2086 (condition-case nil
2087 (progn
2088 ;; The t causes an error if the cell has references. If no
2089 ;; references, the t will be the result value.
2090 (1value (ses-formula-references (ses-cell-formula row col) t))
2091 (ses-cell-property-set :ses-Dijkstra-attempt
2092 (cons ses--Dijkstra-attempt-nb 0)
2093 row col)
2094 (when (setq sig (ses-calculate-cell row col t))
2095 (nconc sig (list (ses-cell-symbol row col)))))
2096 (wrong-type-argument
2097 ;; The formula contains a reference.
2098 (add-to-list 'ses--deferred-recalc (ses-cell-symbol row col))))))
2099 ;; Do the update now, so we can force recalculation.
2100 (let ((x ses--deferred-recalc))
2101 (setq ses--deferred-recalc nil)
2102 (condition-case hold
2103 (ses-update-cells x t)
2104 (error (setq sig hold))))
2105 (cond
2106 (sig
2107 (message "%s" (error-message-string sig)))
2108 ((consp ses--curcell)
2109 (message " "))
2110 (t
2111 (princ (symbol-value ses--curcell))))))
2112
2113 (defun ses-recalculate-all ()
2114 "Recalculate and reprint all cells."
2115 (interactive "*")
2116 (let ((startcell (get-text-property (point) 'intangible))
2117 (ses--curcell (cons 'A1 (ses-cell-symbol (1- ses--numrows)
2118 (1- ses--numcols)))))
2119 (ses-recalculate-cell)
2120 (ses-jump-safe startcell)))
2121
2122 (defun ses-truncate-cell ()
2123 "Reprint current cell, but without spillover into any following blank cells."
2124 (interactive "*")
2125 (ses-check-curcell)
2126 (let* ((rowcol (ses-sym-rowcol ses--curcell))
2127 (row (car rowcol))
2128 (col (cdr rowcol)))
2129 (when (and (< col (1- ses--numcols)) ;;Last column can't spill over, anyway
2130 (eq (ses-cell-value row (1+ col)) '*skip*))
2131 ;; This cell has spill-over. We'll momentarily pretend the following cell
2132 ;; has a `t' in it.
2133 (eval `(let ((,(ses-cell-symbol row (1+ col)) t))
2134 (ses-print-cell row col)))
2135 ;; Now remove the *skip*. ses-print-cell is always nil here.
2136 (ses-set-cell row (1+ col) 'value nil)
2137 (1value (ses-print-cell row (1+ col))))))
2138
2139 (defun ses-reconstruct-all ()
2140 "Reconstruct buffer based on cell data stored in Emacs variables."
2141 (interactive "*")
2142 (ses-begin-change)
2143 ;;Reconstruct reference lists.
2144 (let (x yrow ycol)
2145 ;;Delete old reference lists
2146 (dotimes-with-progress-reporter
2147 (row ses--numrows) "Deleting references..."
2148 (dotimes (col ses--numcols)
2149 (ses-set-cell row col 'references nil)))
2150 ;;Create new reference lists
2151 (dotimes-with-progress-reporter
2152 (row ses--numrows) "Computing references..."
2153 (dotimes (col ses--numcols)
2154 (dolist (ref (ses-formula-references (ses-cell-formula row col)))
2155 (setq x (ses-sym-rowcol ref)
2156 yrow (car x)
2157 ycol (cdr x))
2158 (ses-set-cell yrow ycol 'references
2159 (cons (ses-cell-symbol row col)
2160 (ses-cell-references yrow ycol)))))))
2161 ;; Delete everything and reconstruct basic data area.
2162 (ses-widen)
2163 (let ((inhibit-read-only t))
2164 (goto-char (point-max))
2165 (if (search-backward ";; Local Variables:\n" nil t)
2166 (delete-region (point-min) (point))
2167 ;; Buffer is quite screwed up --- can't even save the user-specified
2168 ;; locals.
2169 (delete-region (point-min) (point-max))
2170 (insert ses-initial-file-trailer)
2171 (goto-char (point-min)))
2172 ;; Create a blank display area.
2173 (dotimes (row ses--numrows)
2174 (insert ses--blank-line))
2175 (insert ses-print-data-boundary)
2176 (backward-char (1- (length ses-print-data-boundary)))
2177 (setq ses--data-marker (point-marker))
2178 (forward-char (1- (length ses-print-data-boundary)))
2179 ;; Placeholders for cell data.
2180 (insert (make-string (* ses--numrows (1+ ses--numcols)) ?\n))
2181 ;; Placeholders for col-widths, col-printers, default-printer, header-row.
2182 (insert "\n\n\n\n")
2183 (insert ses-initial-global-parameters)
2184 (backward-char (1- (length ses-initial-global-parameters)))
2185 (setq ses--params-marker (point-marker))
2186 (forward-char (1- (length ses-initial-global-parameters))))
2187 (ses-set-parameter 'ses--col-widths ses--col-widths)
2188 (ses-set-parameter 'ses--col-printers ses--col-printers)
2189 (ses-set-parameter 'ses--default-printer ses--default-printer)
2190 (ses-set-parameter 'ses--header-row ses--header-row)
2191 (ses-set-parameter 'ses--numrows ses--numrows)
2192 (ses-set-parameter 'ses--numcols ses--numcols)
2193 ;;Keep our old narrowing
2194 (ses-setup)
2195 (ses-recalculate-all)
2196 (goto-char (point-min)))
2197
2198
2199 ;;----------------------------------------------------------------------------
2200 ;; Input of cell formulas
2201 ;;----------------------------------------------------------------------------
2202
2203 (defun ses-edit-cell (row col newval)
2204 "Display current cell contents in minibuffer, for editing. Returns nil if
2205 cell formula was unsafe and user declined confirmation."
2206 (interactive
2207 (progn
2208 (barf-if-buffer-read-only)
2209 (ses-check-curcell)
2210 (let* ((rowcol (ses-sym-rowcol ses--curcell))
2211 (row (car rowcol))
2212 (col (cdr rowcol))
2213 (formula (ses-cell-formula row col))
2214 initial)
2215 (if (eq (car-safe formula) 'ses-safe-formula)
2216 (setq formula (cadr formula)))
2217 (if (eq (car-safe formula) 'quote)
2218 (setq initial (format "'%S" (cadr formula)))
2219 (setq initial (prin1-to-string formula)))
2220 (if (stringp formula)
2221 ;; Position cursor inside close-quote.
2222 (setq initial (cons initial (length initial))))
2223 (list row col
2224 (read-from-minibuffer (format "Cell %s: " ses--curcell)
2225 initial
2226 ses-mode-edit-map
2227 t ; Convert to Lisp object.
2228 'ses-read-cell-history)))))
2229 (when (ses-warn-unsafe newval 'unsafep)
2230 (ses-begin-change)
2231 (ses-cell-set-formula row col newval)
2232 t))
2233
2234 (defun ses-read-cell (row col newval)
2235 "Self-insert for initial character of cell function."
2236 (interactive
2237 (let* ((initial (this-command-keys))
2238 (rowcol (progn (ses-check-curcell) (ses-sym-rowcol ses--curcell)))
2239 (curval (ses-cell-formula (car rowcol) (cdr rowcol))))
2240 (barf-if-buffer-read-only)
2241 (list (car rowcol)
2242 (cdr rowcol)
2243 (read-from-minibuffer
2244 (format "Cell %s: " ses--curcell)
2245 (cons (if (equal initial "\"") "\"\""
2246 (if (equal initial "(") "()" initial)) 2)
2247 ses-mode-edit-map
2248 t ; Convert to Lisp object.
2249 'ses-read-cell-history
2250 (prin1-to-string (if (eq (car-safe curval) 'ses-safe-formula)
2251 (cadr curval)
2252 curval))))))
2253 (when (ses-edit-cell row col newval)
2254 (ses-command-hook) ; Update cell widths before movement.
2255 (dolist (x ses-after-entry-functions)
2256 (funcall x 1))))
2257
2258 (defun ses-read-symbol (row col symb)
2259 "Self-insert for a symbol as a cell formula. The set of all symbols that
2260 have been used as formulas in this spreadsheet is available for completions."
2261 (interactive
2262 (let ((rowcol (progn (ses-check-curcell) (ses-sym-rowcol ses--curcell)))
2263 newval)
2264 (barf-if-buffer-read-only)
2265 (setq newval (completing-read (format "Cell %s ': " ses--curcell)
2266 ses--symbolic-formulas))
2267 (list (car rowcol)
2268 (cdr rowcol)
2269 (if (string= newval "")
2270 nil ; Don't create zero-length symbols!
2271 (list 'quote (intern newval))))))
2272 (when (ses-edit-cell row col symb)
2273 (ses-command-hook) ; Update cell widths before movement.
2274 (dolist (x ses-after-entry-functions)
2275 (funcall x 1))))
2276
2277 (defun ses-clear-cell-forward (count)
2278 "Delete formula and printer for current cell and then move to next cell.
2279 With prefix, deletes several cells."
2280 (interactive "*p")
2281 (if (< count 0)
2282 (1value (ses-clear-cell-backward (- count)))
2283 (ses-check-curcell)
2284 (ses-begin-change)
2285 (dotimes (x count)
2286 (ses-set-curcell)
2287 (let ((rowcol (ses-sym-rowcol ses--curcell)))
2288 (or rowcol (signal 'end-of-buffer nil))
2289 (ses-clear-cell (car rowcol) (cdr rowcol)))
2290 (forward-char 1))))
2291
2292 (defun ses-clear-cell-backward (count)
2293 "Move to previous cell and then delete it. With prefix, deletes several
2294 cells."
2295 (interactive "*p")
2296 (if (< count 0)
2297 (1value (ses-clear-cell-forward (- count)))
2298 (ses-check-curcell 'end)
2299 (ses-begin-change)
2300 (dotimes (x count)
2301 (backward-char 1) ; Will signal 'beginning-of-buffer if appropriate.
2302 (ses-set-curcell)
2303 (let ((rowcol (ses-sym-rowcol ses--curcell)))
2304 (ses-clear-cell (car rowcol) (cdr rowcol))))))
2305
2306
2307 ;;----------------------------------------------------------------------------
2308 ;; Input of cell-printer functions
2309 ;;----------------------------------------------------------------------------
2310
2311 (defun ses-read-printer (prompt default)
2312 "Common code for `ses-read-cell-printer', `ses-read-column-printer', and `ses-read-default-printer'.
2313 PROMPT should end with \": \". Result is t if operation was canceled."
2314 (barf-if-buffer-read-only)
2315 (if (eq default t)
2316 (setq default "")
2317 (setq prompt (format "%s [currently %S]: "
2318 (substring prompt 0 -2)
2319 default)))
2320 (let ((new (read-from-minibuffer prompt
2321 nil ; Initial contents.
2322 ses-mode-edit-map
2323 t ; Evaluate the result.
2324 'ses-read-printer-history
2325 (prin1-to-string default))))
2326 (if (equal new default)
2327 ;; User changed mind, decided not to change printer.
2328 (setq new t)
2329 (ses-printer-validate new)
2330 (or (not new)
2331 (stringp new)
2332 (stringp (car-safe new))
2333 (ses-warn-unsafe new 'unsafep-function)
2334 (setq new t)))
2335 new))
2336
2337 (defun ses-read-cell-printer (newval)
2338 "Set the printer function for the current cell or range.
2339
2340 A printer function is either a string (a format control-string with one
2341 %-sequence -- result from format will be right-justified), or a list of one
2342 string (result from format will be left-justified), or a lambda-expression of
2343 one argument, or a symbol that names a function of one argument. In the
2344 latter two cases, the function's result should be either a string (will be
2345 right-justified) or a list of one string (will be left-justified)."
2346 (interactive
2347 (let ((default t)
2348 x)
2349 (ses-check-curcell 'range)
2350 ;;Default is none if not all cells in range have same printer
2351 (catch 'ses-read-cell-printer
2352 (ses-dorange ses--curcell
2353 (setq x (ses-cell-printer row col))
2354 (if (eq (car-safe x) 'ses-safe-printer)
2355 (setq x (cadr x)))
2356 (if (eq default t)
2357 (setq default x)
2358 (unless (equal default x)
2359 ;;Range contains differing printer functions
2360 (setq default t)
2361 (throw 'ses-read-cell-printer t)))))
2362 (list (ses-read-printer (format "Cell %S printer: " ses--curcell)
2363 default))))
2364 (unless (eq newval t)
2365 (ses-begin-change)
2366 (ses-dorange ses--curcell
2367 (ses-set-cell row col 'printer newval)
2368 (ses-print-cell row col))))
2369
2370 (defun ses-read-column-printer (col newval)
2371 "Set the printer function for the current column.
2372 See `ses-read-cell-printer' for input forms."
2373 (interactive
2374 (let ((col (cdr (ses-sym-rowcol ses--curcell))))
2375 (ses-check-curcell)
2376 (list col (ses-read-printer (format "Column %s printer: "
2377 (ses-column-letter col))
2378 (ses-col-printer col)))))
2379
2380 (unless (eq newval t)
2381 (ses-begin-change)
2382 (ses-set-parameter 'ses--col-printers newval col)
2383 (save-excursion
2384 (dotimes (row ses--numrows)
2385 (ses-print-cell row col)))))
2386
2387 (defun ses-read-default-printer (newval)
2388 "Set the default printer function for cells that have no other.
2389 See `ses-read-cell-printer' for input forms."
2390 (interactive
2391 (list (ses-read-printer "Default printer: " ses--default-printer)))
2392 (unless (eq newval t)
2393 (ses-begin-change)
2394 (ses-set-parameter 'ses--default-printer newval)
2395 (ses-reprint-all t)))
2396
2397
2398 ;;----------------------------------------------------------------------------
2399 ;; Spreadsheet size adjustments
2400 ;;----------------------------------------------------------------------------
2401
2402 (defun ses-insert-row (count)
2403 "Insert a new row before the current one.
2404 With prefix, insert COUNT rows before current one."
2405 (interactive "*p")
2406 (ses-check-curcell 'end)
2407 (or (> count 0) (signal 'args-out-of-range nil))
2408 (ses-begin-change)
2409 (let ((inhibit-quit t)
2410 (inhibit-read-only t)
2411 (row (or (car (ses-sym-rowcol ses--curcell)) ses--numrows))
2412 newrow)
2413 ;;Create a new set of cell-variables
2414 (ses-create-cell-variable-range ses--numrows (+ ses--numrows count -1)
2415 0 (1- ses--numcols))
2416 (ses-set-parameter 'ses--numrows (+ ses--numrows count))
2417 ;;Insert each row
2418 (ses-goto-print row 0)
2419 (dotimes-with-progress-reporter (x count) "Inserting row..."
2420 ;;Create a row of empty cells. The `symbol' fields will be set by
2421 ;;the call to ses-relocate-all.
2422 (setq newrow (make-vector ses--numcols nil))
2423 (dotimes (col ses--numcols)
2424 (aset newrow col (ses-make-cell)))
2425 (setq ses--cells (ses-vector-insert ses--cells row newrow))
2426 (push `(apply ses-vector-delete ses--cells ,row 1) buffer-undo-list)
2427 (insert ses--blank-line))
2428 ;;Insert empty lines in cell data area (will be replaced by
2429 ;;ses-relocate-all)
2430 (ses-goto-data row 0)
2431 (insert (make-string (* (1+ ses--numcols) count) ?\n))
2432 (ses-relocate-all row 0 count 0)
2433 ;;If any cell printers insert constant text, insert that text
2434 ;;into the line.
2435 (let ((cols (mapconcat #'ses-call-printer ses--col-printers nil))
2436 (global (ses-call-printer ses--default-printer)))
2437 (if (or (> (length cols) 0) (> (length global) 0))
2438 (dotimes (x count)
2439 (dotimes (col ses--numcols)
2440 ;;These cells are always nil, only constant formatting printed
2441 (1value (ses-print-cell (+ x row) col))))))
2442 (when (> ses--header-row row)
2443 ;;Inserting before header
2444 (ses-set-parameter 'ses--header-row (+ ses--header-row count))
2445 (ses-reset-header-string)))
2446 ;;Reconstruct text attributes
2447 (ses-setup)
2448 ;;Prepare for undo
2449 (push '(apply ses-widen) buffer-undo-list)
2450 ;;Return to current cell
2451 (if ses--curcell
2452 (ses-jump-safe ses--curcell)
2453 (ses-goto-print (1- ses--numrows) 0)))
2454
2455 (defun ses-delete-row (count)
2456 "Delete the current row.
2457 With prefix, deletes COUNT rows starting from the current one."
2458 (interactive "*p")
2459 (ses-check-curcell)
2460 (or (> count 0) (signal 'args-out-of-range nil))
2461 (let ((inhibit-quit t)
2462 (inhibit-read-only t)
2463 (row (car (ses-sym-rowcol ses--curcell))))
2464 (setq count (min count (- ses--numrows row)))
2465 (ses-begin-change)
2466 (ses-set-parameter 'ses--numrows (- ses--numrows count))
2467 ;;Delete lines from print area
2468 (ses-goto-print row 0)
2469 (ses-delete-line count)
2470 ;;Delete lines from cell data area
2471 (ses-goto-data row 0)
2472 (ses-delete-line (* count (1+ ses--numcols)))
2473 ;;Relocate variables and formulas
2474 (ses-set-with-undo 'ses--cells (ses-vector-delete ses--cells row count))
2475 (ses-relocate-all row 0 (- count) 0)
2476 (ses-destroy-cell-variable-range ses--numrows (+ ses--numrows count -1)
2477 0 (1- ses--numcols))
2478 (when (> ses--header-row row)
2479 (if (<= ses--header-row (+ row count))
2480 ;;Deleting the header row
2481 (ses-set-parameter 'ses--header-row 0)
2482 (ses-set-parameter 'ses--header-row (- ses--header-row count)))
2483 (ses-reset-header-string)))
2484 ;;Reconstruct attributes
2485 (ses-setup)
2486 ;;Prepare for undo
2487 (push '(apply ses-widen) buffer-undo-list)
2488 (ses-jump-safe ses--curcell))
2489
2490 (defun ses-insert-column (count &optional col width printer)
2491 "Insert a new column before COL (default is the current one).
2492 With prefix, insert COUNT columns before current one.
2493 If COL is specified, the new column(s) get the specified WIDTH and PRINTER
2494 \(otherwise they're taken from the current column)."
2495 (interactive "*p")
2496 (ses-check-curcell)
2497 (or (> count 0) (signal 'args-out-of-range nil))
2498 (or col
2499 (setq col (cdr (ses-sym-rowcol ses--curcell))
2500 width (ses-col-width col)
2501 printer (ses-col-printer col)))
2502 (ses-begin-change)
2503 (let ((inhibit-quit t)
2504 (inhibit-read-only t)
2505 (widths ses--col-widths)
2506 (printers ses--col-printers)
2507 has-skip)
2508 ;;Create a new set of cell-variables
2509 (ses-create-cell-variable-range 0 (1- ses--numrows)
2510 ses--numcols (+ ses--numcols count -1))
2511 ;;Insert each column.
2512 (dotimes-with-progress-reporter (x count) "Inserting column..."
2513 ;;Create a column of empty cells. The `symbol' fields will be set by
2514 ;;the call to ses-relocate-all.
2515 (ses-adjust-print-width col (1+ width))
2516 (ses-set-parameter 'ses--numcols (1+ ses--numcols))
2517 (dotimes (row ses--numrows)
2518 (and (< (1+ col) ses--numcols) (eq (ses-cell-value row col) '*skip*)
2519 ;;Inserting in the middle of a spill-over
2520 (setq has-skip t))
2521 (ses-aset-with-undo ses--cells row
2522 (ses-vector-insert (aref ses--cells row)
2523 col (ses-make-cell)))
2524 ;;Insert empty lines in cell data area (will be replaced by
2525 ;;ses-relocate-all)
2526 (ses-goto-data row col)
2527 (insert ?\n))
2528 ;; Insert column width and printer.
2529 (setq widths (ses-vector-insert widths col width)
2530 printers (ses-vector-insert printers col printer)))
2531 (ses-set-parameter 'ses--col-widths widths)
2532 (ses-set-parameter 'ses--col-printers printers)
2533 (ses-reset-header-string)
2534 (ses-relocate-all 0 col 0 count)
2535 (if has-skip
2536 (ses-reprint-all t)
2537 (when (or (> (length (ses-call-printer printer)) 0)
2538 (> (length (ses-call-printer ses--default-printer)) 0))
2539 ;; Either column printer or global printer inserts some constant text.
2540 ;; Reprint the new columns to insert that text.
2541 (dotimes (x ses--numrows)
2542 (dotimes (y count)
2543 ;; Always nil here --- this is a blank column.
2544 (1value (ses-print-cell-new-width x (+ y col))))))
2545 (ses-setup)))
2546 (ses-jump-safe ses--curcell))
2547
2548 (defun ses-delete-column (count)
2549 "Delete the current column.
2550 With prefix, deletes COUNT columns starting from the current one."
2551 (interactive "*p")
2552 (ses-check-curcell)
2553 (or (> count 0) (signal 'args-out-of-range nil))
2554 (let ((inhibit-quit t)
2555 (inhibit-read-only t)
2556 (rowcol (ses-sym-rowcol ses--curcell))
2557 (width 0)
2558 col origrow has-skip)
2559 (setq origrow (car rowcol)
2560 col (cdr rowcol)
2561 count (min count (- ses--numcols col)))
2562 (if (= count ses--numcols)
2563 (error "Can't delete all columns!"))
2564 ;;Determine width of column(s) being deleted
2565 (dotimes (x count)
2566 (setq width (+ width (ses-col-width (+ col x)) 1)))
2567 (ses-begin-change)
2568 (ses-set-parameter 'ses--numcols (- ses--numcols count))
2569 (ses-adjust-print-width col (- width))
2570 (dotimes-with-progress-reporter (row ses--numrows) "Deleting column..."
2571 ;;Delete lines from cell data area
2572 (ses-goto-data row col)
2573 (ses-delete-line count)
2574 ;;Delete cells. Check if deletion area begins or ends with a skip.
2575 (if (or (eq (ses-cell-value row col) '*skip*)
2576 (and (< col ses--numcols)
2577 (eq (ses-cell-value row (+ col count)) '*skip*)))
2578 (setq has-skip t))
2579 (ses-aset-with-undo ses--cells row
2580 (ses-vector-delete (aref ses--cells row) col count)))
2581 ;;Update globals
2582 (ses-set-parameter 'ses--col-widths
2583 (ses-vector-delete ses--col-widths col count))
2584 (ses-set-parameter 'ses--col-printers
2585 (ses-vector-delete ses--col-printers col count))
2586 (ses-reset-header-string)
2587 ;;Relocate variables and formulas
2588 (ses-relocate-all 0 col 0 (- count))
2589 (ses-destroy-cell-variable-range 0 (1- ses--numrows)
2590 ses--numcols (+ ses--numcols count -1))
2591 (if has-skip
2592 (ses-reprint-all t)
2593 (ses-setup))
2594 (if (>= col ses--numcols)
2595 (setq col (1- col)))
2596 (ses-goto-print origrow col)))
2597
2598 (defun ses-forward-or-insert (&optional count)
2599 "Move to next cell in row, or inserts a new cell if already in last one, or
2600 inserts a new row if at bottom of print area. Repeat COUNT times."
2601 (interactive "p")
2602 (ses-check-curcell 'end)
2603 (setq deactivate-mark t) ; Doesn't combine well with ranges.
2604 (dotimes (x count)
2605 (ses-set-curcell)
2606 (if (not ses--curcell)
2607 (progn ; At bottom of print area.
2608 (barf-if-buffer-read-only)
2609 (ses-insert-row 1))
2610 (let ((col (cdr (ses-sym-rowcol ses--curcell))))
2611 (when (/= 32
2612 (char-before (next-single-property-change (point)
2613 'intangible)))
2614 ;; We're already in last nonskipped cell on line. Need to create a
2615 ;; new column.
2616 (barf-if-buffer-read-only)
2617 (ses-insert-column (- count x)
2618 ses--numcols
2619 (ses-col-width col)
2620 (ses-col-printer col)))))
2621 (forward-char)))
2622
2623 (defun ses-append-row-jump-first-column ()
2624 "Insert a new row after current one and jump to its first column."
2625 (interactive "*")
2626 (ses-check-curcell)
2627 (ses-begin-change)
2628 (beginning-of-line 2)
2629 (ses-set-curcell)
2630 (ses-insert-row 1))
2631
2632 (defun ses-set-column-width (col newwidth)
2633 "Set the width of the current column."
2634 (interactive
2635 (let ((col (cdr (progn (ses-check-curcell) (ses-sym-rowcol ses--curcell)))))
2636 (barf-if-buffer-read-only)
2637 (list col
2638 (if current-prefix-arg
2639 (prefix-numeric-value current-prefix-arg)
2640 (read-from-minibuffer (format "Column %s width [currently %d]: "
2641 (ses-column-letter col)
2642 (ses-col-width col))
2643 nil ; No initial contents.
2644 nil ; No override keymap.
2645 t ; Convert to Lisp object.
2646 nil ; No history.
2647 (number-to-string
2648 (ses-col-width col))))))) ; Default value.
2649 (if (< newwidth 1)
2650 (error "Invalid column width"))
2651 (ses-begin-change)
2652 (ses-reset-header-string)
2653 (save-excursion
2654 (let ((inhibit-quit t))
2655 (ses-adjust-print-width col (- newwidth (ses-col-width col)))
2656 (ses-set-parameter 'ses--col-widths newwidth col))
2657 (dotimes (row ses--numrows)
2658 (ses-print-cell-new-width row col))))
2659
2660
2661 ;;----------------------------------------------------------------------------
2662 ;; Cut and paste, import and export
2663 ;;----------------------------------------------------------------------------
2664
2665 (defadvice copy-region-as-kill (around ses-copy-region-as-kill
2666 activate preactivate)
2667 "It doesn't make sense to copy read-only or intangible attributes into the
2668 kill ring. It probably doesn't make sense to copy keymap properties.
2669 We'll assume copying front-sticky properties doesn't make sense, either.
2670
2671 This advice also includes some SES-specific code because otherwise it's too
2672 hard to override how mouse-1 works."
2673 (when (> beg end)
2674 (let ((temp beg))
2675 (setq beg end
2676 end temp)))
2677 (if (not (and (eq major-mode 'ses-mode)
2678 (eq (get-text-property beg 'read-only) 'ses)
2679 (eq (get-text-property (1- end) 'read-only) 'ses)))
2680 ad-do-it ; Normal copy-region-as-kill.
2681 (kill-new (ses-copy-region beg end))
2682 (if transient-mark-mode
2683 (setq deactivate-mark t))
2684 nil))
2685
2686 (defun ses-copy-region (beg end)
2687 "Treat the region as rectangular. Convert the intangible attributes to
2688 SES attributes recording the contents of the cell as of the time of copying."
2689 (when (= end ses--data-marker)
2690 ;;Avoid overflow situation
2691 (setq end (1- ses--data-marker)))
2692 (let* ((inhibit-point-motion-hooks t)
2693 (x (mapconcat 'ses-copy-region-helper
2694 (extract-rectangle beg (1- end)) "\n")))
2695 (remove-text-properties 0 (length x)
2696 '(read-only t
2697 intangible t
2698 keymap t
2699 front-sticky t)
2700 x)
2701 x))
2702
2703 (defun ses-copy-region-helper (line)
2704 "Converts one line (of a rectangle being extracted from a spreadsheet) to
2705 external form by attaching to each print cell a 'ses attribute that records
2706 the corresponding data cell."
2707 (or (> (length line) 1)
2708 (error "Empty range"))
2709 (let ((inhibit-read-only t)
2710 (pos 0)
2711 mycell next sym rowcol)
2712 (while pos
2713 (setq sym (get-text-property pos 'intangible line)
2714 next (next-single-property-change pos 'intangible line)
2715 rowcol (ses-sym-rowcol sym)
2716 mycell (ses-get-cell (car rowcol) (cdr rowcol)))
2717 (put-text-property pos (or next (length line))
2718 'ses
2719 (list (ses-cell-symbol mycell)
2720 (ses-cell-formula mycell)
2721 (ses-cell-printer mycell))
2722 line)
2723 (setq pos next)))
2724 line)
2725
2726 (defun ses-kill-override (beg end)
2727 "Generic override for any commands that kill text.
2728 We clear the killed cells instead of deleting them."
2729 (interactive "r")
2730 (ses-check-curcell 'needrange)
2731 ;; For some reason, the text-read-only error is not caught by `delete-region',
2732 ;; so we have to use subterfuge.
2733 (let ((buffer-read-only t))
2734 (1value (condition-case x
2735 (noreturn (funcall (lookup-key (current-global-map)
2736 (this-command-keys))
2737 beg end))
2738 (buffer-read-only nil)))) ; The expected error.
2739 ;; Because the buffer was marked read-only, the kill command turned itself
2740 ;; into a copy. Now we clear the cells or signal the error. First we check
2741 ;; whether the buffer really is read-only.
2742 (barf-if-buffer-read-only)
2743 (ses-begin-change)
2744 (ses-dorange ses--curcell
2745 (ses-clear-cell row col))
2746 (ses-jump (car ses--curcell)))
2747
2748 (defadvice yank (around ses-yank activate preactivate)
2749 "In SES mode, the yanked text is inserted as cells.
2750
2751 If the text contains 'ses attributes (meaning it went to the kill-ring from a
2752 SES buffer), the formulas and print functions are restored for the cells. If
2753 the text contains tabs, this is an insertion of tab-separated formulas.
2754 Otherwise the text is inserted as the formula for the current cell.
2755
2756 When inserting cells, the formulas are usually relocated to keep the same
2757 relative references to neighboring cells. This is best if the formulas
2758 generally refer to other cells within the yanked text. You can use the C-u
2759 prefix to specify insertion without relocation, which is best when the
2760 formulas refer to cells outside the yanked text.
2761
2762 When inserting formulas, the text is treated as a string constant if it doesn't
2763 make sense as a sexp or would otherwise be considered a symbol. Use 'sym to
2764 explicitly insert a symbol, or use the C-u prefix to treat all unmarked words
2765 as symbols."
2766 (if (not (and (eq major-mode 'ses-mode)
2767 (eq (get-text-property (point) 'keymap) 'ses-mode-print-map)))
2768 ad-do-it ; Normal non-SES yank.
2769 (ses-check-curcell 'end)
2770 (push-mark (point))
2771 (let ((text (current-kill (cond
2772 ((listp arg) 0)
2773 ((eq arg '-) -1)
2774 (t (1- arg))))))
2775 (or (ses-yank-cells text arg)
2776 (ses-yank-tsf text arg)
2777 (ses-yank-one (ses-yank-resize 1 1)
2778 text
2779 0
2780 (if (memq (aref text (1- (length text))) '(?\t ?\n))
2781 ;; Just one cell --- delete final tab or newline.
2782 (1- (length text)))
2783 arg)))
2784 (if (consp arg)
2785 (exchange-point-and-mark))))
2786
2787 (defun ses-yank-pop (arg)
2788 "Replace just-yanked stretch of killed text with a different stretch.
2789 This command is allowed only immediately after a `yank' or a `yank-pop',
2790 when the region contains a stretch of reinserted previously-killed text.
2791 We replace it with a different stretch of killed text.
2792 Unlike standard `yank-pop', this function uses `undo' to delete the
2793 previous insertion."
2794 (interactive "*p")
2795 (or (eq last-command 'yank)
2796 ;;Use noreturn here just to avoid a "poor-coverage" warning in its
2797 ;;macro definition.
2798 (noreturn (error "Previous command was not a yank")))
2799 (undo)
2800 (ses-set-curcell)
2801 (yank (1+ (or arg 1)))
2802 (setq this-command 'yank))
2803
2804 (defun ses-yank-cells (text arg)
2805 "If the TEXT has a proper set of 'ses attributes, insert the text as
2806 cells, else return nil. The cells are reprinted--the supplied text is
2807 ignored because the column widths, default printer, etc. at yank time might
2808 be different from those at kill-time. ARG is a list to indicate that
2809 formulas are to be inserted without relocation."
2810 (let ((first (get-text-property 0 'ses text))
2811 (last (get-text-property (1- (length text)) 'ses text)))
2812 (when (and first last) ;;Otherwise not proper set of attributes
2813 (setq first (ses-sym-rowcol (car first))
2814 last (ses-sym-rowcol (car last)))
2815 (let* ((needrows (- (car last) (car first) -1))
2816 (needcols (- (cdr last) (cdr first) -1))
2817 (rowcol (ses-yank-resize needrows needcols))
2818 (rowincr (- (car rowcol) (car first)))
2819 (colincr (- (cdr rowcol) (cdr first)))
2820 (pos 0)
2821 myrow mycol x)
2822 (dotimes-with-progress-reporter (row needrows) "Yanking..."
2823 (setq myrow (+ row (car rowcol)))
2824 (dotimes (col needcols)
2825 (setq mycol (+ col (cdr rowcol))
2826 last (get-text-property pos 'ses text)
2827 pos (next-single-property-change pos 'ses text)
2828 x (ses-sym-rowcol (car last)))
2829 (if (not last)
2830 ;; Newline --- all remaining cells on row are skipped.
2831 (setq x (cons (- myrow rowincr) (+ needcols colincr -1))
2832 last (list nil nil nil)
2833 pos (1- pos)))
2834 (if (/= (car x) (- myrow rowincr))
2835 (error "Cell row error"))
2836 (if (< (- mycol colincr) (cdr x))
2837 ;; Some columns were skipped.
2838 (let ((oldcol mycol))
2839 (while (< (- mycol colincr) (cdr x))
2840 (ses-clear-cell myrow mycol)
2841 (setq col (1+ col)
2842 mycol (1+ mycol)))
2843 (ses-print-cell myrow (1- oldcol)))) ;; This inserts *skip*.
2844 (when (car last) ; Skip this for *skip* cells.
2845 (setq x (nth 2 last))
2846 (unless (equal x (ses-cell-printer myrow mycol))
2847 (or (not x)
2848 (stringp x)
2849 (eq (car-safe x) 'ses-safe-printer)
2850 (setq x `(ses-safe-printer ,x)))
2851 (ses-set-cell myrow mycol 'printer x))
2852 (setq x (cadr last))
2853 (if (atom arg)
2854 (setq x (ses-relocate-formula x 0 0 rowincr colincr)))
2855 (or (atom x)
2856 (eq (car-safe x) 'ses-safe-formula)
2857 (setq x `(ses-safe-formula ,x)))
2858 (ses-cell-set-formula myrow mycol x)))
2859 (when pos
2860 (if (get-text-property pos 'ses text)
2861 (error "Missing newline between rows"))
2862 (setq pos (next-single-property-change pos 'ses text))))
2863 t))))
2864
2865 (defun ses-yank-one (rowcol text from to arg)
2866 "Insert the substring [FROM,TO] of TEXT as the formula for cell ROWCOL (a
2867 cons of ROW and COL). Treat plain symbols as strings unless ARG is a list."
2868 (let ((val (condition-case nil
2869 (read-from-string text from to)
2870 (error (cons nil from)))))
2871 (cond
2872 ((< (cdr val) (or to (length text)))
2873 ;; Invalid sexp --- leave it as a string.
2874 (setq val (substring text from to)))
2875 ((and (car val) (symbolp (car val)))
2876 (if (consp arg)
2877 (setq val (list 'quote (car val))) ; Keep symbol.
2878 (setq val (substring text from to)))) ; Treat symbol as text.
2879 (t
2880 (setq val (car val))))
2881 (let ((row (car rowcol))
2882 (col (cdr rowcol)))
2883 (or (atom val)
2884 (setq val `(ses-safe-formula ,val)))
2885 (ses-cell-set-formula row col val))))
2886
2887 (defun ses-yank-tsf (text arg)
2888 "If TEXT contains tabs and/or newlines, treat the tabs as
2889 column-separators and the newlines as row-separators and insert the text as
2890 cell formulas--else return nil. Treat plain symbols as strings unless ARG
2891 is a list. Ignore a final newline."
2892 (if (or (not (string-match "[\t\n]" text))
2893 (= (match-end 0) (length text)))
2894 ;;Not TSF format
2895 nil
2896 (if (/= (aref text (1- (length text))) ?\n)
2897 (setq text (concat text "\n")))
2898 (let ((pos -1)
2899 (spots (list -1))
2900 (cols 0)
2901 (needrows 0)
2902 needcols rowcol)
2903 ;;Find all the tabs and newlines
2904 (while (setq pos (string-match "[\t\n]" text (1+ pos)))
2905 (push pos spots)
2906 (setq cols (1+ cols))
2907 (when (eq (aref text pos) ?\n)
2908 (if (not needcols)
2909 (setq needcols cols)
2910 (or (= needcols cols)
2911 (error "Inconsistent row lengths")))
2912 (setq cols 0
2913 needrows (1+ needrows))))
2914 ;;Insert the formulas
2915 (setq rowcol (ses-yank-resize needrows needcols))
2916 (dotimes (row needrows)
2917 (dotimes (col needcols)
2918 (ses-yank-one (cons (+ (car rowcol) needrows (- row) -1)
2919 (+ (cdr rowcol) needcols (- col) -1))
2920 text (1+ (cadr spots)) (car spots) arg)
2921 (setq spots (cdr spots))))
2922 (ses-goto-print (+ (car rowcol) needrows -1)
2923 (+ (cdr rowcol) needcols -1))
2924 t)))
2925
2926 (defun ses-yank-resize (needrows needcols)
2927 "If this yank will require inserting rows and/or columns, ask for
2928 confirmation and then insert them. Result is (row,col) for top left of yank
2929 spot, or error signal if user requests cancel."
2930 (ses-begin-change)
2931 (let ((rowcol (if ses--curcell
2932 (ses-sym-rowcol ses--curcell)
2933 (cons ses--numrows 0)))
2934 rowbool colbool)
2935 (setq needrows (- (+ (car rowcol) needrows) ses--numrows)
2936 needcols (- (+ (cdr rowcol) needcols) ses--numcols)
2937 rowbool (> needrows 0)
2938 colbool (> needcols 0))
2939 (when (or rowbool colbool)
2940 ;;Need to insert. Get confirm
2941 (or (y-or-n-p (format "Yank will insert %s%s%s. Continue? "
2942 (if rowbool (format "%d rows" needrows) "")
2943 (if (and rowbool colbool) " and " "")
2944 (if colbool (format "%d columns" needcols) "")))
2945 (error "Cancelled"))
2946 (when rowbool
2947 (let (ses--curcell)
2948 (save-excursion
2949 (ses-goto-print ses--numrows 0)
2950 (ses-insert-row needrows))))
2951 (when colbool
2952 (ses-insert-column needcols
2953 ses--numcols
2954 (ses-col-width (1- ses--numcols))
2955 (ses-col-printer (1- ses--numcols)))))
2956 rowcol))
2957
2958 (defun ses-export-tsv (beg end)
2959 "Export values from the current range, with tabs between columns and
2960 newlines between rows. Result is placed in kill ring."
2961 (interactive "r")
2962 (ses-export-tab nil))
2963
2964 (defun ses-export-tsf (beg end)
2965 "Export formulas from the current range, with tabs between columns and
2966 newlines between rows. Result is placed in kill ring."
2967 (interactive "r")
2968 (ses-export-tab t))
2969
2970 (defun ses-export-tab (want-formulas)
2971 "Export the current range with tabs between columns and newlines between rows.
2972 Result is placed in kill ring. The export is values unless WANT-FORMULAS
2973 is non-nil. Newlines and tabs in the export text are escaped."
2974 (ses-check-curcell 'needrange)
2975 (let ((print-escape-newlines t)
2976 result item)
2977 (ses-dorange ses--curcell
2978 (setq item (if want-formulas
2979 (ses-cell-formula row col)
2980 (ses-cell-value row col)))
2981 (if (eq (car-safe item) 'ses-safe-formula)
2982 ;;Hide our deferred safety-check marker
2983 (setq item (cadr item)))
2984 (if (or (not item) (eq item '*skip*))
2985 (setq item ""))
2986 (when (eq (car-safe item) 'quote)
2987 (push "'" result)
2988 (setq item (cadr item)))
2989 (setq item (prin1-to-string item t))
2990 (setq item (replace-regexp-in-string "\t" "\\\\t" item))
2991 (push item result)
2992 (cond
2993 ((< col maxcol)
2994 (push "\t" result))
2995 ((< row maxrow)
2996 (push "\n" result))))
2997 (setq result (apply 'concat (nreverse result)))
2998 (kill-new result)))
2999
3000
3001 ;;----------------------------------------------------------------------------
3002 ;; Other user commands
3003 ;;----------------------------------------------------------------------------
3004
3005 (defun ses-unset-header-row ()
3006 "Select the default header row."
3007 (interactive)
3008 (ses-set-header-row 0))
3009
3010 (defun ses-set-header-row (row)
3011 "Set the ROW to display in the header-line.
3012 With a numerical prefix arg, use that row.
3013 With no prefix arg, use the current row.
3014 With a \\[universal-argument] prefix arg, prompt the user.
3015 The top row is row 1. Selecting row 0 displays the default header row."
3016 (interactive
3017 (list (if (numberp current-prefix-arg) current-prefix-arg
3018 (let ((currow (1+ (car (ses-sym-rowcol ses--curcell)))))
3019 (if current-prefix-arg
3020 (read-number "Header row: " currow)
3021 currow)))))
3022 (if (or (< row 0) (> row ses--numrows))
3023 (error "Invalid header-row"))
3024 (ses-begin-change)
3025 (let ((oldval ses--header-row))
3026 (let (buffer-undo-list)
3027 (ses-set-parameter 'ses--header-row row))
3028 (push `(apply ses-set-header-row ,oldval) buffer-undo-list))
3029 (ses-reset-header-string))
3030
3031 (defun ses-mark-row ()
3032 "Mark the entirety of current row as a range."
3033 (interactive)
3034 (ses-check-curcell 'range)
3035 (let ((row (car (ses-sym-rowcol (or (car-safe ses--curcell) ses--curcell)))))
3036 (push-mark (point))
3037 (ses-goto-print (1+ row) 0)
3038 (push-mark (point) nil t)
3039 (ses-goto-print row 0)))
3040
3041 (defun ses-mark-column ()
3042 "Mark the entirety of current column as a range."
3043 (interactive)
3044 (ses-check-curcell 'range)
3045 (let ((col (cdr (ses-sym-rowcol (or (car-safe ses--curcell) ses--curcell))))
3046 (row 0))
3047 (push-mark (point))
3048 (ses-goto-print (1- ses--numrows) col)
3049 (forward-char 1)
3050 (push-mark (point) nil t)
3051 (while (eq '*skip* (ses-cell-value row col))
3052 ;;Skip over initial cells in column that can't be selected
3053 (setq row (1+ row)))
3054 (ses-goto-print row col)))
3055
3056 (defun ses-end-of-line ()
3057 "Move point to last cell on line."
3058 (interactive)
3059 (ses-check-curcell 'end 'range)
3060 (when ses--curcell ; Otherwise we're at the bottom row, which is empty
3061 ; anyway.
3062 (let ((col (1- ses--numcols))
3063 row rowcol)
3064 (if (symbolp ses--curcell)
3065 ;; Single cell.
3066 (setq row (car (ses-sym-rowcol ses--curcell)))
3067 ;; Range --- use whichever end of the range the point is at.
3068 (setq rowcol (ses-sym-rowcol (if (< (point) (mark))
3069 (car ses--curcell)
3070 (cdr ses--curcell))))
3071 ;; If range already includes the last cell in a row, point is actually
3072 ;; in the following row.
3073 (if (<= (cdr rowcol) (1- col))
3074 (setq row (car rowcol))
3075 (setq row (1+ (car rowcol)))
3076 (if (= row ses--numrows)
3077 ;;Already at end - can't go anywhere
3078 (setq col 0))))
3079 (when (< row ses--numrows) ; Otherwise it's a range that includes last cell.
3080 (while (eq (ses-cell-value row col) '*skip*)
3081 ;; Back to beginning of multi-column cell.
3082 (setq col (1- col)))
3083 (ses-goto-print row col)))))
3084
3085 (defun ses-renarrow-buffer ()
3086 "Narrow the buffer so only the print area is visible.
3087 Use after \\[widen]."
3088 (interactive)
3089 (setq ses--deferred-narrow t))
3090
3091 (defun ses-sort-column (sorter &optional reverse)
3092 "Sort the range by a specified column.
3093 With prefix, sorts in REVERSE order."
3094 (interactive "*sSort column: \nP")
3095 (ses-check-curcell 'needrange)
3096 (let ((min (ses-sym-rowcol (car ses--curcell)))
3097 (max (ses-sym-rowcol (cdr ses--curcell))))
3098 (let ((minrow (car min))
3099 (mincol (cdr min))
3100 (maxrow (car max))
3101 (maxcol (cdr max))
3102 keys extracts end)
3103 (setq sorter (cdr (ses-sym-rowcol (intern (concat sorter "1")))))
3104 (or (and sorter (>= sorter mincol) (<= sorter maxcol))
3105 (error "Invalid sort column"))
3106 ;;Get key columns and sort them
3107 (dotimes (x (- maxrow minrow -1))
3108 (ses-goto-print (+ minrow x) sorter)
3109 (setq end (next-single-property-change (point) 'intangible))
3110 (push (cons (buffer-substring-no-properties (point) end)
3111 (+ minrow x))
3112 keys))
3113 (setq keys (sort keys #'(lambda (x y) (string< (car x) (car y)))))
3114 ;;Extract the lines in reverse sorted order
3115 (or reverse
3116 (setq keys (nreverse keys)))
3117 (dolist (x keys)
3118 (ses-goto-print (cdr x) (1+ maxcol))
3119 (setq end (point))
3120 (ses-goto-print (cdr x) mincol)
3121 (push (ses-copy-region (point) end) extracts))
3122 (deactivate-mark)
3123 ;;Paste the lines sequentially
3124 (dotimes (x (- maxrow minrow -1))
3125 (ses-goto-print (+ minrow x) mincol)
3126 (ses-set-curcell)
3127 (ses-yank-cells (pop extracts) nil)))))
3128
3129 (defun ses-sort-column-click (event reverse)
3130 "Mouse version of `ses-sort-column'."
3131 (interactive "*e\nP")
3132 (setq event (event-end event))
3133 (select-window (posn-window event))
3134 (setq event (car (posn-col-row event))) ; Click column.
3135 (let ((col 0))
3136 (while (and (< col ses--numcols) (> event (ses-col-width col)))
3137 (setq event (- event (ses-col-width col) 1)
3138 col (1+ col)))
3139 (if (>= col ses--numcols)
3140 (ding)
3141 (ses-sort-column (ses-column-letter col) reverse))))
3142
3143 (defun ses-insert-range ()
3144 "Insert into minibuffer the list of cells currently highlighted in the
3145 spreadsheet."
3146 (interactive "*")
3147 (let (x)
3148 (with-current-buffer (window-buffer minibuffer-scroll-window)
3149 (ses-command-hook) ; For ses-coverage.
3150 (ses-check-curcell 'needrange)
3151 (setq x (cdr (macroexpand `(ses-range ,(car ses--curcell)
3152 ,(cdr ses--curcell))))))
3153 (insert (substring (prin1-to-string (nreverse x)) 1 -1))))
3154
3155 (defun ses-insert-ses-range ()
3156 "Insert \"(ses-range x y)\" in the minibuffer to represent the currently
3157 highlighted range in the spreadsheet."
3158 (interactive "*")
3159 (let (x)
3160 (with-current-buffer (window-buffer minibuffer-scroll-window)
3161 (ses-command-hook) ; For ses-coverage.
3162 (ses-check-curcell 'needrange)
3163 (setq x (format "(ses-range %S %S)"
3164 (car ses--curcell)
3165 (cdr ses--curcell))))
3166 (insert x)))
3167
3168 (defun ses-insert-range-click (event)
3169 "Mouse version of `ses-insert-range'."
3170 (interactive "*e")
3171 (mouse-set-point event)
3172 (ses-insert-range))
3173
3174 (defun ses-insert-ses-range-click (event)
3175 "Mouse version of `ses-insert-ses-range'."
3176 (interactive "*e")
3177 (mouse-set-point event)
3178 (ses-insert-ses-range))
3179
3180 (defun ses-replace-name-in-formula (formula old-name new-name)
3181 (let ((new-formula formula))
3182 (unless (and (consp formula)
3183 (eq (car-safe formula) 'quote))
3184 (while formula
3185 (let ((elt (car-safe formula)))
3186 (cond
3187 ((consp elt)
3188 (setcar formula (ses-replace-name-in-formula elt old-name new-name)))
3189 ((and (symbolp elt)
3190 (eq (car-safe formula) old-name))
3191 (setcar formula new-name))))
3192 (setq formula (cdr formula))))
3193 new-formula))
3194
3195 (defun ses-rename-cell (new-name)
3196 "Rename current cell."
3197 (interactive "*SEnter new name: ")
3198 (ses-check-curcell)
3199 (or
3200 (and (local-variable-p new-name)
3201 (ses-sym-rowcol new-name)
3202 ;; this test is needed because ses-cell property of deleted cells
3203 ;; is not deleted in case of subsequent undo
3204 (memq new-name ses--renamed-cell-symb-list)
3205 (error "Already a cell name"))
3206 (and (boundp new-name)
3207 (null (yes-or-no-p (format "`%S' is already bound outside this buffer, continue? "
3208 new-name)))
3209 (error "Already a bound cell name")))
3210 (let* ((rowcol (ses-sym-rowcol ses--curcell))
3211 (cell (ses-get-cell (car rowcol) (cdr rowcol))))
3212 (put new-name 'ses-cell rowcol)
3213 (dolist (reference (ses-cell-references (car rowcol) (cdr rowcol)))
3214 (let* ((rowcol (ses-sym-rowcol reference))
3215 (cell (ses-get-cell (car rowcol) (cdr rowcol))))
3216 (ses-cell-set-formula (car rowcol)
3217 (cdr rowcol)
3218 (ses-replace-name-in-formula
3219 (ses-cell-formula cell)
3220 ses--curcell
3221 new-name))))
3222 (push new-name ses--renamed-cell-symb-list)
3223 (set new-name (symbol-value ses--curcell))
3224 (aset cell 0 new-name)
3225 (put ses--curcell 'ses-cell nil)
3226 (makunbound ses--curcell)
3227 (setq ses--curcell new-name)
3228 (let* ((pos (point))
3229 (inhibit-read-only t)
3230 (col (current-column))
3231 (end (save-excursion
3232 (move-to-column (1+ col))
3233 (if (eolp)
3234 (+ pos (ses-col-width col) 1)
3235 (point)))))
3236 (put-text-property pos end 'intangible new-name))) )
3237
3238 ;;----------------------------------------------------------------------------
3239 ;; Checking formulas for safety
3240 ;;----------------------------------------------------------------------------
3241
3242 (defun ses-safe-printer (printer)
3243 "Return PRINTER if safe, or the substitute printer `ses-unsafe' otherwise."
3244 (if (or (stringp printer)
3245 (stringp (car-safe printer))
3246 (not printer)
3247 (ses-warn-unsafe printer 'unsafep-function))
3248 printer
3249 'ses-unsafe))
3250
3251 (defun ses-safe-formula (formula)
3252 "Return FORMULA if safe, or the substitute formula *unsafe* otherwise."
3253 (if (ses-warn-unsafe formula 'unsafep)
3254 formula
3255 `(ses-unsafe ',formula)))
3256
3257 (defun ses-warn-unsafe (formula checker)
3258 "Apply CHECKER to FORMULA.
3259 If result is non-nil, asks user for confirmation about FORMULA,
3260 which might be unsafe. Returns t if formula is safe or user allows
3261 execution anyway. Always returns t if `safe-functions' is t."
3262 (if (eq safe-functions t)
3263 t
3264 (setq checker (funcall checker formula))
3265 (if (not checker)
3266 t
3267 (y-or-n-p (format "Formula %S\nmight be unsafe %S. Process it? "
3268 formula checker)))))
3269
3270
3271 ;;----------------------------------------------------------------------------
3272 ;; Standard formulas
3273 ;;----------------------------------------------------------------------------
3274
3275 (defun ses--clean-! (&rest x)
3276 "Clean by `delq' list X from any occurrence of `nil' or `*skip*'."
3277 (delq nil (delq '*skip* x)))
3278
3279 (defun ses--clean-_ (x y)
3280 "Clean list X by replacing by Y any occurrence of `nil' or `*skip*'.
3281
3282 This will change X by making `setcar' on its cons cells."
3283 (let ((ret x) ret-elt)
3284 (while ret
3285 (setq ret-elt (car ret))
3286 (when (memq ret-elt '(nil *skip*))
3287 (setcar ret y))
3288 (setq ret (cdr ret))))
3289 x)
3290
3291 (defmacro ses-range (from to &rest rest)
3292 "Expand to a list of cell-symbols for the range going from
3293 FROM up to TO. The range automatically expands to include any
3294 new row or column inserted into its middle. The SES library code
3295 specifically looks for the symbol `ses-range', so don't create an
3296 alias for this macro!
3297
3298 By passing in REST some flags one can configure the way the range
3299 is read and how it is formatted.
3300
3301 In the sequel we assume that cells A1, B1, A2 B2 have respective values
3302 1 2 3 and 4.
3303
3304 Readout direction is specified by a `>v', '`>^', `<v', `<^',
3305 `v>', `v<', `^>', `^<' flag. For historical reasons, in absence
3306 of such a flag, a default direction of `^<' is assumed. This
3307 way `(ses-range A1 B2 ^>)' will evaluate to `(1 3 2 4)',
3308 while `(ses-range A1 B2 >^)' will evaluate to (3 4 1 2).
3309
3310 If the range is one row, then `>' can be used as a shorthand to
3311 `>v' or `>^', and `<' to `<v' or `<^'.
3312
3313 If the range is one column, then `v' can be used as a shorthand to
3314 `v>' or `v<', and `^' to `^>' or `v<'.
3315
3316 A `!' flag will remove all cells whose value is nil or `*skip*'.
3317
3318 A `_' flag will replace nil or `*skip*' by the value following
3319 the `_' flag. If the `_' flag is the last argument, then they are
3320 replaced by integer 0.
3321
3322 A `*', `*1' or `*2' flag will vectorize the range in the sense of
3323 Calc. See info node `(Calc) Top'. Flag `*' will output either a
3324 vector or a matrix depending on the number of rows, `*1' will
3325 flatten the result to a one row vector, and `*2' will make a
3326 matrix whatever the number of rows.
3327
3328 Warning: interaction with Calc is experimental and may produce
3329 confusing results if you are not aware of Calc data format.
3330 Use `math-format-value' as a printer for Calc objects."
3331 (let (result-row
3332 result
3333 (prev-row -1)
3334 (reorient-x nil)
3335 (reorient-y nil)
3336 transpose vectorize
3337 (clean 'list))
3338 (ses-dorange (cons from to)
3339 (when (/= prev-row row)
3340 (push result-row result)
3341 (setq result-row nil))
3342 (push (ses-cell-symbol row col) result-row)
3343 (setq prev-row row))
3344 (push result-row result)
3345 (while rest
3346 (let ((x (pop rest)))
3347 (case x
3348 ((>v) (setq transpose nil reorient-x nil reorient-y nil))
3349 ((>^)(setq transpose nil reorient-x nil reorient-y t))
3350 ((<^)(setq transpose nil reorient-x t reorient-y t))
3351 ((<v)(setq transpose nil reorient-x t reorient-y nil))
3352 ((v>)(setq transpose t reorient-x nil reorient-y t))
3353 ((^>)(setq transpose t reorient-x nil reorient-y nil))
3354 ((^<)(setq transpose t reorient-x t reorient-y nil))
3355 ((v<)(setq transpose t reorient-x t reorient-y t))
3356 ((* *2 *1) (setq vectorize x))
3357 ((!) (setq clean 'ses--clean-!))
3358 ((_) (setq clean `(lambda (&rest x) (ses--clean-_ x ,(if rest (pop rest) 0)))))
3359 (t
3360 (cond
3361 ; shorthands one row
3362 ((and (null (cddr result)) (memq x '(> <)))
3363 (push (intern (concat (symbol-name x) "v")) rest))
3364 ; shorthands one col
3365 ((and (null (cdar result)) (memq x '(v ^)))
3366 (push (intern (concat (symbol-name x) ">")) rest))
3367 (t (error "Unexpected flag `%S' in ses-range" x)))))))
3368 (if reorient-y
3369 (setcdr (last result 2) nil)
3370 (setq result (cdr (nreverse result))))
3371 (unless reorient-x
3372 (setq result (mapcar 'nreverse result)))
3373 (when transpose
3374 (let ((ret (mapcar (lambda (x) (list x)) (pop result))) iter)
3375 (while result
3376 (setq iter ret)
3377 (dolist (elt (pop result))
3378 (setcar iter (cons elt (car iter)))
3379 (setq iter (cdr iter))))
3380 (setq result ret)))
3381
3382 (flet ((vectorize-*1
3383 (clean result)
3384 (cons clean (cons (quote 'vec) (apply 'append result))))
3385 (vectorize-*2
3386 (clean result)
3387 (cons clean (cons (quote 'vec) (mapcar (lambda (x)
3388 (cons clean (cons (quote 'vec) x)))
3389 result)))))
3390 (case vectorize
3391 ((nil) (cons clean (apply 'append result)))
3392 ((*1) (vectorize-*1 clean result))
3393 ((*2) (vectorize-*2 clean result))
3394 ((*) (if (cdr result)
3395 (vectorize-*2 clean result)
3396 (vectorize-*1 clean result)))))))
3397
3398 (defun ses-delete-blanks (&rest args)
3399 "Return ARGS reversed, with the blank elements (nil and *skip*) removed."
3400 (let (result)
3401 (dolist (cur args)
3402 (unless (memq cur '(nil *skip*))
3403 (push cur result)))
3404 result))
3405
3406 (defun ses+ (&rest args)
3407 "Compute the sum of the arguments, ignoring blanks."
3408 (apply '+ (apply 'ses-delete-blanks args)))
3409
3410 (defun ses-average (list)
3411 "Computes the sum of the numbers in LIST, divided by their length. Blanks
3412 are ignored. Result is always floating-point, even if all args are integers."
3413 (setq list (apply 'ses-delete-blanks list))
3414 (/ (float (apply '+ list)) (length list)))
3415
3416 (defmacro ses-select (fromrange test torange)
3417 "Select cells in FROMRANGE that are `equal' to TEST.
3418 For each match, return the corresponding cell from TORANGE.
3419 The ranges are macroexpanded but not evaluated so they should be
3420 either (ses-range BEG END) or (list ...). The TEST is evaluated."
3421 (setq fromrange (cdr (macroexpand fromrange))
3422 torange (cdr (macroexpand torange))
3423 test (eval test))
3424 (or (= (length fromrange) (length torange))
3425 (error "ses-select: Ranges not same length"))
3426 (let (result)
3427 (dolist (x fromrange)
3428 (if (equal test (symbol-value x))
3429 (push (car torange) result))
3430 (setq torange (cdr torange)))
3431 (cons 'list result)))
3432
3433 ;;All standard formulas are safe
3434 (dolist (x '(ses-cell-value ses-range ses-delete-blanks ses+ ses-average
3435 ses-select))
3436 (put x 'side-effect-free t))
3437
3438
3439 ;;----------------------------------------------------------------------------
3440 ;; Standard print functions
3441 ;;----------------------------------------------------------------------------
3442
3443 ;; These functions use the variables 'row' and 'col' that are dynamically bound
3444 ;; by ses-print-cell. We define these variables at compile-time to make the
3445 ;; compiler happy.
3446 (defvar row)
3447 (defvar col)
3448
3449 (defun ses-center (value &optional span fill)
3450 "Print VALUE, centered within column.
3451 FILL is the fill character for centering (default = space).
3452 SPAN indicates how many additional rightward columns to include
3453 in width (default = 0)."
3454 (let ((printer (or (ses-col-printer col) ses--default-printer))
3455 (width (ses-col-width col))
3456 half)
3457 (or fill (setq fill ?\s))
3458 (or span (setq span 0))
3459 (setq value (ses-call-printer printer value))
3460 (dotimes (x span)
3461 (setq width (+ width 1 (ses-col-width (+ col span (- x))))))
3462 ;; Set column width.
3463 (setq width (- width (string-width value)))
3464 (if (<= width 0)
3465 value ; Too large for field, anyway.
3466 (setq half (make-string (/ width 2) fill))
3467 (concat half value half
3468 (if (> (% width 2) 0) (char-to-string fill))))))
3469
3470 (defun ses-center-span (value &optional fill)
3471 "Print VALUE, centered within the span that starts in the current column
3472 and continues until the next nonblank column.
3473 FILL specifies the fill character (default = space)."
3474 (let ((end (1+ col)))
3475 (while (and (< end ses--numcols)
3476 (memq (ses-cell-value row end) '(nil *skip*)))
3477 (setq end (1+ end)))
3478 (ses-center value (- end col 1) fill)))
3479
3480 (defun ses-dashfill (value &optional span)
3481 "Print VALUE centered using dashes.
3482 SPAN indicates how many rightward columns to include in width (default = 0)."
3483 (ses-center value span ?-))
3484
3485 (defun ses-dashfill-span (value)
3486 "Print VALUE, centered using dashes within the span that starts in the
3487 current column and continues until the next nonblank column."
3488 (ses-center-span value ?-))
3489
3490 (defun ses-tildefill-span (value)
3491 "Print VALUE, centered using tildes within the span that starts in the
3492 current column and continues until the next nonblank column."
3493 (ses-center-span value ?~))
3494
3495 (defun ses-unsafe (value)
3496 "Substitute for an unsafe formula or printer."
3497 (error "Unsafe formula or printer"))
3498
3499 ;;All standard printers are safe, including ses-unsafe!
3500 (dolist (x (cons 'ses-unsafe ses-standard-printer-functions))
3501 (put x 'side-effect-free t))
3502
3503 (defun ses-unload-function ()
3504 "Unload the Simple Emacs Spreadsheet."
3505 (dolist (fun '(copy-region-as-kill yank))
3506 (ad-remove-advice fun 'around (intern (concat "ses-" (symbol-name fun))))
3507 (ad-update fun))
3508 ;; continue standard unloading
3509 nil)
3510
3511 (provide 'ses)
3512
3513 ;;; ses.el ends here