]> code.delx.au - gnu-emacs/blobdiff - lisp/ses.el
(ses-call-printer-return): Doc fix (Nil -> nil).
[gnu-emacs] / lisp / ses.el
index 314ca6038617db15898bb515a1c6e8d38371582b..1cc1e354ff86e2580a8e139ef912aaa124a1986d 100644 (file)
@@ -1,6 +1,6 @@
-;;; ses.el -- Simple Emacs Spreadsheet
+;;; ses.el -- Simple Emacs Spreadsheet  -*- coding: utf-8 -*-
 
-;; Copyright (C) 2002,03,04  Free Software Foundation, Inc.
+;; Copyright (C) 2002, 2003, 2004, 2005, 2006  Free Software Foundation, Inc.
 
 ;; Author: Jonathan Yavner <jyavner@member.fsf.org>
 ;; Maintainer: Jonathan Yavner <jyavner@member.fsf.org>
 
 ;; You should have received a copy of the GNU General Public License
 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
+
+;;; Commentary:
 
 ;;; To-do list:
+
 ;; * Use $ or … for truncated fields
 ;; * Add command to make a range of columns be temporarily invisible.
 ;; * Allow paste of one cell to a range of cells -- copy formula to each.
 ;; * Left-margin column for row number.
 ;; * Move a row by dragging its number in the left-margin.
 
+
+;;; Code:
+
 (require 'unsafep)
 
 
-;;;----------------------------------------------------------------------------
-;;;; User-customizable variables
-;;;----------------------------------------------------------------------------
+;;----------------------------------------------------------------------------
+;; User-customizable variables
+;;----------------------------------------------------------------------------
 
 (defgroup ses nil
-  "Simple Emacs Spreadsheet"
+  "Simple Emacs Spreadsheet."
   :group  'applications
   :prefix "ses-"
   :version "21.1")
@@ -66,8 +72,9 @@
                  function))
 
 (defcustom ses-after-entry-functions '(forward-char)
-  "Things to do after entering a value into a cell.  An abnormal hook that
-usually runs a cursor-movement function.  Each function is called with ARG=1."
+  "Things to do after entering a value into a cell.
+An abnormal hook that usually runs a cursor-movement function.
+Each function is called with ARG=1."
   :group 'ses
   :type 'hook
   :options '(forward-char backward-char next-line previous-line))
@@ -78,9 +85,9 @@ usually runs a cursor-movement function.  Each function is called with ARG=1."
   :type 'hook)
 
 
-;;;----------------------------------------------------------------------------
-;;;; Global variables and constants
-;;;----------------------------------------------------------------------------
+;;----------------------------------------------------------------------------
+;; Global variables and constants
+;;----------------------------------------------------------------------------
 
 (defvar ses-read-cell-history nil
   "List of formulas that have been typed in.")
@@ -92,7 +99,7 @@ usually runs a cursor-movement function.  Each function is called with ARG=1."
   "Context menu when mouse-3 is used on the header-line in an SES buffer."
   '("SES header row"
     ["Set current row" ses-set-header-row t]
-    ["Unset row" ses-unset-header-row (> header-row 0)]))
+    ["Unset row" ses-unset-header-row (> ses--header-row 0)]))
 
 (defconst ses-mode-map
   (let ((keys `("\C-c\M-\C-l" ses-reconstruct-all
@@ -141,8 +148,7 @@ usually runs a cursor-movement function.  Each function is called with ARG=1."
        (newmap (make-sparse-keymap)))
     (set-keymap-parent newmap minibuffer-local-map)
     (while keys
-      (define-key newmap (car keys) (cadr keys))
-      (setq keys (cddr keys)))
+      (define-key newmap (pop keys) (pop keys)))
     newmap)
   "Local keymap for SES minibuffer cell-editing.")
 
@@ -208,14 +214,14 @@ usually runs a cursor-movement function.  Each function is called with ARG=1."
     map))
 
 (defconst ses-print-data-boundary "\n\014\n"
-  "Marker string denoting the boundary between print area and data area")
+  "Marker string denoting the boundary between print area and data area.")
 
 (defconst ses-initial-global-parameters
   "\n( ;Global parameters (these are read first)\n 2 ;SES file-format\n 1 ;numrows\n 1 ;numcols\n)\n\n"
-  "Initial contents for the three-element list at the bottom of the data area")
+  "Initial contents for the three-element list at the bottom of the data area.")
 
 (defconst ses-initial-file-trailer
-  ";;; Local Variables:\n;;; mode: ses\n;;; End:\n"
+  ";; Local Variables:\n;; mode: ses\n;; End:\n"
   "Initial contents for the file-trailer area at the bottom of the file.")
 
 (defconst ses-initial-file-contents
@@ -231,17 +237,6 @@ usually runs a cursor-movement function.  Each function is called with ARG=1."
          ses-initial-file-trailer)
   "The initial contents of an empty spreadsheet.")
 
-(defconst ses-cell-size 4
-  "A cell consists of a SYMBOL, a FORMULA, a PRINTER-function, and a list of
-REFERENCES.")
-
-(defconst ses-paramlines-plist
-  '(ses--col-widths  2 ses--col-printers  3 ses--default-printer  4
-    ses--header-row  5 ses--file-format   8 ses--numrows          9
-    ses--numcols    10)
-  "Offsets from last cell line to various parameter lines in the data area
-of a spreadsheet.")
-
 (defconst ses-box-prop '(:box (:line-width 2 :style released-button))
   "Display properties to create a raised box for cells in the header line.")
 
@@ -253,13 +248,19 @@ functions.  None of these standard-printer functions is suitable for use as a
 column printer or a global-default printer because they invoke the column or
 default printer and then modify its output.")
 
+
+;;----------------------------------------------------------------------------
+;; Local variables and constants
+;;----------------------------------------------------------------------------
+
 (eval-and-compile
   (defconst ses-localvars
     '(ses--blank-line ses--cells ses--col-printers ses--col-widths ses--curcell
       ses--curcell-overlay ses--default-printer ses--deferred-narrow
       ses--deferred-recalc ses--deferred-write ses--file-format
       ses--header-hscroll ses--header-row ses--header-string ses--linewidth
-      ses--numcols ses--numrows ses--symbolic-formulas
+      ses--numcols ses--numrows ses--symbolic-formulas ses--data-marker
+      ses--params-marker
       ;;Global variables that we override
       mode-line-process next-line-add-newlines transient-mark-mode)
     "Buffer-local variables used by SES."))
@@ -270,14 +271,21 @@ default printer and then modify its output.")
     (make-local-variable x)
     (set x nil)))
 
+(defconst ses-paramlines-plist
+  '(ses--col-widths  -5 ses--col-printers -4 ses--default-printer -3
+    ses--header-row  -2 ses--file-format   1 ses--numrows          2
+    ses--numcols      3)
+  "Offsets from 'Global parameters' line to various parameter lines in the
+data area of a spreadsheet.")
+
 
-;;;
-;;;  "Side-effect variables".  They are set in one function, altered in
-;;;  another as a side effect, then read back by the first, as a way of
-;;;  passing back more than one value.  These declarations are just to make
-;;;  the compiler happy, and to conform to standard Emacs-Lisp practice (I
-;;;  think the make-local-variable trick above is cleaner).
-;;;
+;;
+;;  "Side-effect variables".  They are set in one function, altered in
+;;  another as a side effect, then read back by the first, as a way of
+;;  passing back more than one value.  These declarations are just to make
+;;  the compiler happy, and to conform to standard Emacs-Lisp practice (I
+;;  think the make-local-variable trick above is cleaner).
+;;
 
 (defvar ses-relocate-return nil
   "Set by `ses-relocate-formula' and `ses-relocate-range', read by
@@ -289,21 +297,26 @@ need to be recalculated.")
 (defvar ses-call-printer-return nil
   "Set to t if last cell printer invoked by `ses-call-printer' requested
 left-justification of the result.  Set to error-signal if ses-call-printer
-encountered an error during printing.  Nil otherwise.")
+encountered an error during printing.  nil otherwise.")
 
 (defvar ses-start-time nil
   "Time when current operation started.  Used by `ses-time-check' to decide
 when to emit a progress message.")
 
 
-;;;----------------------------------------------------------------------------
-;;;; Macros
-;;;----------------------------------------------------------------------------
+;;----------------------------------------------------------------------------
+;; Macros
+;;----------------------------------------------------------------------------
 
 (defmacro ses-get-cell (row col)
   "Return the cell structure that stores information about cell (ROW,COL)."
   `(aref (aref ses--cells ,row) ,col))
 
+;; We might want to use defstruct here, but cells are explicitly used as
+;; arrays in ses-set-cell, so we'd need to fix this first.  --Stef
+(defsubst ses-make-cell (&optional symbol formula printer references)
+  (vector symbol formula printer references))
+
 (defmacro ses-cell-symbol (row &optional col)
   "From a CELL or a pair (ROW,COL), get the symbol that names the local-variable holding its value.  (0,0) => A1."
   `(aref ,(if col `(ses-get-cell ,row ,col) row) 0))
@@ -355,7 +368,7 @@ PRINTER are deferred until first use."
        (setq printer `(ses-safe-printer ,printer)))
     (aset (aref ses--cells (car rowcol))
          (cdr rowcol)
-         (vector sym formula printer references)))
+         (ses-make-cell sym formula printer references)))
   (set sym value)
   sym)
 
@@ -368,7 +381,7 @@ macro to prevent propagate-on-load viruses."
   ;;print area (excluding the terminating newline)
   (setq ses--col-widths widths
        ses--linewidth  (apply '+ -1 (mapcar '1+ widths))
-       ses--blank-line (concat (make-string ses--linewidth ? ) "\n"))
+       ses--blank-line (concat (make-string ses--linewidth ?\s) "\n"))
   t)
 
 (defmacro ses-column-printers (printers)
@@ -392,35 +405,16 @@ for safety.  This is a macro to prevent propagate-on-load viruses."
 (defmacro ses-header-row (row)
   "Load the header row from the spreadsheet file and checks it
 for safety.  This is a macro to prevent propagate-on-load viruses."
-  (or (and (wholenump row) (< row ses--numrows))
+  (or (and (wholenump row) (or (zerop ses--numrows) (< row ses--numrows)))
       (error "Bad header-row"))
   (setq ses--header-row row)
   t)
 
-(defmacro ses-dotimes-msg (spec msg &rest body)
-  "(ses-dotimes-msg (VAR LIMIT) MSG BODY...): Like `dotimes', but
-a message is emitted using MSG every second or so during the loop."
-  (let ((msgvar   (make-symbol "msg"))
-       (limitvar (make-symbol "limit"))
-       (var      (car spec))
-       (limit    (cadr spec)))
-    `(let ((,limitvar ,limit)
-          (,msgvar   ,msg))
-       (setq ses-start-time (float-time))
-       (message ,msgvar)
-       (setq ,msgvar (concat ,msgvar " (%d%%)"))
-       (dotimes (,var ,limitvar)
-        (ses-time-check ,msgvar '(/ (* ,var 100) ,limitvar))
-        ,@body)
-       (message nil))))
-
-(put 'ses-dotimes-msg 'lisp-indent-function 2)
-(def-edebug-spec ses-dotimes-msg ((symbolp form) form body))
-
 (defmacro ses-dorange (curcell &rest body)
   "Execute BODY repeatedly, with the variables `row' and `col' set to each
 cell in the range specified by CURCELL.  The range is available in the
 variables `minrow', `maxrow', `mincol', and `maxcol'."
+  (declare (indent defun) (debug (form body)))
   (let ((cur (make-symbol "cur"))
        (min (make-symbol "min"))
        (max (make-symbol "max"))
@@ -442,9 +436,6 @@ variables `minrow', `maxrow', `mincol', and `maxcol'."
             (setq col (+ ,c mincol))
             ,@body))))))
 
-(put 'ses-dorange 'lisp-indent-function 'defun)
-(def-edebug-spec ses-dorange (form body))
-
 ;;Support for coverage testing.
 (defmacro 1value (form)
   "For code-coverage testing, indicate that FORM is expected to always have
@@ -455,9 +446,9 @@ the same value."
   form)
 
 
-;;;----------------------------------------------------------------------------
-;;;; Utility functions
-;;;----------------------------------------------------------------------------
+;;----------------------------------------------------------------------------
+;; Utility functions
+;;----------------------------------------------------------------------------
 
 (defun ses-vector-insert (array idx new)
   "Create a new vector which is one larger than ARRAY and has NEW inserted
@@ -527,7 +518,7 @@ for this spreadsheet."
 
 (defun ses-create-cell-variable-range (minrow maxrow mincol maxcol)
   "Create buffer-local variables for cells.  This is undoable."
-  (push `(ses-destroy-cell-variable-range ,minrow ,maxrow ,mincol ,maxcol)
+  (push `(apply ses-destroy-cell-variable-range ,minrow ,maxrow ,mincol ,maxcol)
        buffer-undo-list)
   (let (sym xrow xcol)
     (dotimes (row (1+ (- maxrow minrow)))
@@ -538,9 +529,9 @@ for this spreadsheet."
        (put sym 'ses-cell (cons xrow xcol))
        (make-local-variable sym)))))
 
-;;;We do not delete the ses-cell properties for the cell-variables, in case a
-;;;formula that refers to this cell is in the kill-ring and is later pasted
-;;;back in.
+;;We do not delete the ses-cell properties for the cell-variables, in case a
+;;formula that refers to this cell is in the kill-ring and is later pasted
+;;back in.
 (defun ses-destroy-cell-variable-range (minrow maxrow mincol maxcol)
   "Destroy buffer-local variables for cells.  This is undoable."
   (let (sym)
@@ -548,16 +539,16 @@ for this spreadsheet."
       (dotimes (col (1+ (- maxcol mincol)))
        (setq sym (ses-create-cell-symbol (+ row minrow) (+ col mincol)))
        (if (boundp sym)
-           (push `(ses-set-with-undo ,sym ,(symbol-value sym))
+           (push `(apply ses-set-with-undo ,sym ,(symbol-value sym))
                  buffer-undo-list))
        (kill-local-variable sym))))
-  (push `(ses-create-cell-variable-range ,minrow ,maxrow ,mincol ,maxcol)
+  (push `(apply ses-create-cell-variable-range ,minrow ,maxrow ,mincol ,maxcol)
        buffer-undo-list))
 
 (defun ses-reset-header-string ()
   "Flags the header string for update.  Upon undo, the header string will be
 updated again."
-  (push '(ses-reset-header-string) buffer-undo-list)
+  (push '(apply ses-reset-header-string) buffer-undo-list)
   (setq ses--header-hscroll -1))
 
 ;;Split this code off into a function to avoid coverage-testing difficulties
@@ -570,9 +561,9 @@ and (eval ARG) and reset `ses-start-time' to the current time."
   nil)
 
 
-;;;----------------------------------------------------------------------------
-;;;; The cells
-;;;----------------------------------------------------------------------------
+;;----------------------------------------------------------------------------
+;; The cells
+;;----------------------------------------------------------------------------
 
 (defun ses-set-cell (row col field val)
   "Install VAL as the contents for field FIELD (named by a quoted symbol) of
@@ -634,8 +625,7 @@ processing for the current keystroke, unless the new value is the same as
 the old and FORCE is nil."
   (let ((cell (ses-get-cell row col))
        formula-error printer-error)
-    (let ((symbol  (ses-cell-symbol  cell))
-         (oldval  (ses-cell-value   cell))
+    (let ((oldval  (ses-cell-value   cell))
          (formula (ses-cell-formula cell))
          newval)
       (if (eq (car-safe formula) 'ses-safe-formula)
@@ -664,7 +654,7 @@ the old and FORCE is nil."
 (defun ses-update-cells (list &optional force)
   "Recalculate cells in LIST, checking for dependency loops.  Prints
 progress messages every second.  Dependent cells are not recalculated
-if the cell's value is unchanged if FORCE is nil."
+if the cell's value is unchanged and FORCE is nil."
   (let ((ses--deferred-recalc list)
        (nextlist             list)
        (pos                  (point))
@@ -717,17 +707,17 @@ if the cell's value is unchanged if FORCE is nil."
     (goto-char pos)))
 
 
-;;;----------------------------------------------------------------------------
-;;;; The print area
-;;;----------------------------------------------------------------------------
+;;----------------------------------------------------------------------------
+;; The print area
+;;----------------------------------------------------------------------------
 
 (defun ses-in-print-area ()
   "Returns t if point is in print area of spreadsheet."
-  (eq (get-text-property (point) 'keymap) 'ses-mode-print-map))
+  (<= (point) ses--data-marker))
 
-;;;We turn off point-motion-hooks and explicitly position the cursor, in case
-;;;the intangible properties have gotten screwed up (e.g., when
-;;;ses-goto-print is called during a recursive ses-print-cell).
+;;We turn off point-motion-hooks and explicitly position the cursor, in case
+;;the intangible properties have gotten screwed up (e.g., when
+;;ses-goto-print is called during a recursive ses-print-cell).
 (defun ses-goto-print (row col)
   "Move point to print area for cell (ROW,COL)."
   (let ((inhibit-point-motion-hooks t))
@@ -772,11 +762,11 @@ argument is 'range.  A single cell is appropriate unless some argument is
     (error "Need a range"))))
 
 (defun ses-print-cell (row col)
-  "Format and print the value of cell (ROW,COL) to the print area, using the
-cell's printer function.  If the cell's new print form is too wide, it will
-spill over into the following cell, but will not run off the end of the row
-or overwrite the next non-nil field.  Result is nil for normal operation, or
-the error signal if the printer function failed and the cell was formatted
+  "Format and print the value of cell (ROW,COL) to the print area.
+Use the cell's printer function.  If the cell's new print form is too wide,
+it will spill over into the following cell, but will not run off the end of the
+row or overwrite the next non-nil field.  Result is nil for normal operation,
+or the error signal if the printer function failed and the cell was formatted
 with \"%s\".  If the cell's value is *skip*, nothing is printed because the
 preceding cell has spilled over."
   (catch 'ses-print-cell
@@ -811,7 +801,7 @@ preceding cell has spilled over."
        (cond
         ((< len width)
          ;;Fill field to length with spaces
-         (setq len  (make-string (- width len) ? )
+         (setq len  (make-string (- width len) ?\s)
                text (if (eq ses-call-printer-return t)
                         (concat text len)
                       (concat len text))))
@@ -829,7 +819,7 @@ preceding cell has spilled over."
                    maxcol   (1+ maxcol)))
            (if (<= len maxwidth)
                ;;Fill to complete width of all the fields spanned
-               (setq text (concat text (make-string (- maxwidth len) ? )))
+               (setq text (concat text (make-string (- maxwidth len) ?\s)))
              ;;Not enough room to end of line or next non-nil field.  Truncate
              ;;if string or decimal; otherwise fill with error indicator
              (setq sig `(error "Too wide" ,text))
@@ -891,7 +881,7 @@ preceding cell has spilled over."
 lambda of one argument) on VALUE.  Result is the the printed cell as a
 string.  The variable `ses-call-printer-return' is set to t if the printer
 used parenthesis to request left-justification, or the error-signal if the
-printer signalled one (and \"%s\" is used as the default printer), else nil."
+printer signaled one (and \"%s\" is used as the default printer), else nil."
   (setq ses-call-printer-return nil)
   (unless value
     (setq value ""))
@@ -919,12 +909,12 @@ printer signalled one (and \"%s\" is used as the default printer), else nil."
 COL=NUMCOLS.  Deletes characters if CHANGE < 0.  Caller should bind
 inhibit-quit to t."
   (let ((inhibit-read-only t)
-       (blank  (if (> change 0) (make-string change ? )))
+       (blank  (if (> change 0) (make-string change ?\s)))
        (at-end (= col ses--numcols)))
     (ses-set-with-undo 'ses--linewidth (+ ses--linewidth change))
     ;;ses-set-with-undo always returns t for strings.
     (1value (ses-set-with-undo 'ses--blank-line
-                              (concat (make-string ses--linewidth ? ) "\n")))
+                              (concat (make-string ses--linewidth ?\s) "\n")))
     (dotimes (row ses--numrows)
       (ses-goto-print row col)
       (when at-end
@@ -948,55 +938,70 @@ cell (ROW,COL) has changed."
       (ses-print-cell (car rowcol) (cdr rowcol)))))
 
 
-;;;----------------------------------------------------------------------------
-;;;; The data area
-;;;----------------------------------------------------------------------------
+;;----------------------------------------------------------------------------
+;; The data area
+;;----------------------------------------------------------------------------
+
+(defun ses-narrowed-p () (/= (- (point-max) (point-min)) (buffer-size)))
+
+(defun ses-widen ()
+  "Turn off narrowing, to be reenabled at end of command loop."
+  (if (ses-narrowed-p)
+      (setq ses--deferred-narrow t))
+  (widen))
 
 (defun ses-goto-data (def &optional col)
   "Move point to data area for (DEF,COL).  If DEF is a row
 number, COL is the column number for a data cell -- otherwise DEF
 is one of the symbols ses--col-widths, ses--col-printers,
 ses--default-printer, ses--numrows, or ses--numcols."
-  (if (< (point-max) (buffer-size))
-      (setq ses--deferred-narrow t))
-  (widen)
+  (ses-widen)
   (let ((inhibit-point-motion-hooks t)) ;In case intangible attrs are wrong
-    (goto-char (point-min))
     (if col
-      ;;It's a cell
-      (forward-line (+ ses--numrows 2 (* def (1+ ses--numcols)) col))
-    ;;Convert def-symbol to offset
-    (setq def (plist-get ses-paramlines-plist def))
-    (or def (signal 'args-out-of-range nil))
-    (forward-line (+ (* ses--numrows (+ ses--numcols 2)) def)))))
+       ;;It's a cell
+       (progn
+         (goto-char ses--data-marker)
+         (forward-line (+ 1 (* def (1+ ses--numcols)) col)))
+      ;;Convert def-symbol to offset
+      (setq def (plist-get ses-paramlines-plist def))
+      (or def (signal 'args-out-of-range nil))
+      (goto-char ses--params-marker)
+      (forward-line def))))
 
 (defun ses-set-parameter (def value &optional elem)
-  "Sets parameter DEF to VALUE (with undo) and writes the value to the data
-area.  See `ses-goto-data' for meaning of DEF.  Newlines in the data
-are escaped.  If ELEM is specified, it is the array subscript within DEF to
-be set to VALUE."
+  "Set parameter DEF to VALUE (with undo) and write the value to the data area.
+See `ses-goto-data' for meaning of DEF.  Newlines in the data are escaped.
+If ELEM is specified, it is the array subscript within DEF to be set to VALUE."
   (save-excursion
     ;;We call ses-goto-data early, using the old values of numrows and
     ;;numcols in case one of them is being changed.
     (ses-goto-data def)
-    (if elem
-       (ses-aset-with-undo (symbol-value def) elem value)
-      (ses-set-with-undo def value))
     (let ((inhibit-read-only t)
-         (fmt (plist-get '(ses--column-widths   "(ses-column-widths %S)"
+         (fmt (plist-get '(ses--col-widths      "(ses-column-widths %S)"
                            ses--col-printers    "(ses-column-printers %S)"
                            ses--default-printer "(ses-default-printer %S)"
                            ses--header-row      "(ses-header-row %S)"
                            ses--file-format     " %S ;SES file-format"
                            ses--numrows         " %S ;numrows"
                            ses--numcols         " %S ;numcols")
-                         def)))
-      (delete-region (point) (line-end-position))
-      (insert (format fmt (symbol-value def))))))
+                         def))
+         oldval)
+      (if elem
+         (progn
+           (setq oldval (aref (symbol-value def) elem))
+           (aset (symbol-value def) elem value))
+       (setq oldval (symbol-value def))
+       (set def value))
+      ;;Special undo since it's outside the narrowed buffer
+      (let (buffer-undo-list)
+       (delete-region (point) (line-end-position))
+       (insert (format fmt (symbol-value def))))
+      (push `(apply ses-set-parameter ,def ,oldval ,elem) buffer-undo-list))))
+
 
 (defun ses-write-cells ()
-  "`ses--deferred-write' is a list of (ROW,COL) for cells to be written from
-buffer-local variables to data area.  Newlines in the data are escaped."
+  "Write cells in `ses--deferred-write' from local variables to data area.
+Newlines in the data are escaped."
   (let* ((inhibit-read-only t)
         (print-escape-newlines t)
         rowcol row col cell sym formula printer text)
@@ -1041,9 +1046,9 @@ buffer-local variables to data area.  Newlines in the data are escaped."
       (message " "))))
 
 
-;;;----------------------------------------------------------------------------
-;;;; Formula relocation
-;;;----------------------------------------------------------------------------
+;;----------------------------------------------------------------------------
+;; Formula relocation
+;;----------------------------------------------------------------------------
 
 (defun ses-formula-references (formula &optional result-so-far)
   "Produce a list of symbols for cells that this formula's value
@@ -1071,6 +1076,23 @@ or t to get a wrong-type-argument error when the first reference is found."
        ))))
   result-so-far)
 
+(defsubst ses-relocate-symbol (sym rowcol startrow startcol rowincr colincr)
+  "Relocate one symbol SYM, whichs corresponds to ROWCOL (a cons of ROW and
+COL).  Cells starting at (STARTROW,STARTCOL) are being shifted
+by (ROWINCR,COLINCR)."
+  (let ((row (car rowcol))
+       (col (cdr rowcol)))
+    (if (or (< row startrow) (< col startcol))
+       sym
+      (setq row (+ row rowincr)
+           col (+ col colincr))
+      (if (and (>= row startrow) (>= col startcol)
+              (< row ses--numrows) (< col ses--numcols))
+         ;;Relocate this variable
+         (ses-create-cell-symbol row col)
+       ;;Delete reference to a deleted cell
+       nil))))
+
 (defun ses-relocate-formula (formula startrow startcol rowincr colincr)
   "Produce a copy of FORMULA where all symbols that refer to cells in row
 STARTROW or above and col STARTCOL or above are altered by adding ROWINCR
@@ -1115,23 +1137,6 @@ Sets `ses-relocate-return' to 'delete if cell-references were removed."
                result))))
       (nreverse result))))
 
-(defun ses-relocate-symbol (sym rowcol startrow startcol rowincr colincr)
-  "Relocate one symbol SYM, whichs corresponds to ROWCOL (a cons of ROW and
-COL).  Cells starting at (STARTROW,STARTCOL) are being shifted
-by (ROWINCR,COLINCR)."
-  (let ((row (car rowcol))
-       (col (cdr rowcol)))
-    (if (or (< row startrow) (< col startcol))
-       sym
-      (setq row (+ row rowincr)
-           col (+ col colincr))
-      (if (and (>= row startrow) (>= col startcol)
-              (< row ses--numrows) (< col ses--numcols))
-         ;;Relocate this variable
-         (ses-create-cell-symbol row col)
-       ;;Delete reference to a deleted cell
-       nil))))
-
 (defun ses-relocate-range (range startrow startcol rowincr colincr)
   "Relocate one RANGE, of the form '(ses-range min max).  Cells starting
 at (STARTROW,STARTCOL) are being shifted by (ROWINCR,COLINCR).  Result is the
@@ -1210,7 +1215,8 @@ the rectangle (MINROW,MINCOL)..(NUMROWS,NUMCOLS) by adding ROWINCR and COLINCR
 to each symbol."
   (let (reform)
     (let (mycell newval)
-      (ses-dotimes-msg (row ses--numrows) "Relocating formulas..."
+      (dotimes-with-progress-reporter
+          (row ses--numrows) "Relocating formulas..."
        (dotimes (col ses--numcols)
          (setq ses-relocate-return nil
                mycell (ses-get-cell row col)
@@ -1238,7 +1244,8 @@ to each symbol."
       (cond
        ((and (<= rowincr 0) (<= colincr 0))
        ;;Deletion of rows and/or columns
-       (ses-dotimes-msg (row (- ses--numrows minrow)) "Relocating variables..."
+       (dotimes-with-progress-reporter
+           (row (- ses--numrows minrow)) "Relocating variables..."
          (setq myrow  (+ row minrow))
          (dotimes (col (- ses--numcols mincol))
            (setq mycol  (+ col mincol)
@@ -1254,7 +1261,8 @@ to each symbol."
        (let ((disty (1- ses--numrows))
              (distx (1- ses--numcols))
              myrow mycol)
-         (ses-dotimes-msg (row (- ses--numrows minrow)) "Relocating variables..."
+         (dotimes-with-progress-reporter
+             (row (- ses--numrows minrow)) "Relocating variables..."
            (setq myrow (- disty row))
            (dotimes (col (- ses--numcols mincol))
              (setq mycol (- distx col)
@@ -1284,57 +1292,27 @@ to each symbol."
       (message nil))))
 
 
-;;;----------------------------------------------------------------------------
-;;;; Undo control
-;;;----------------------------------------------------------------------------
-
-(defadvice undo-more (around ses-undo-more activate preactivate)
-  "Define a meaning for conses in buffer-undo-list whose car is a symbol
-other than t or nil.  To undo these, apply the car--a function--to the
-cdr--its arglist."
-  (let ((ses-count (ad-get-arg 0)))
-    (catch 'undo
-      (dolist (ses-x pending-undo-list)
-       (unless ses-x
-         ;;End of undo boundary
-         (setq ses-count (1- ses-count))
-         (if (<= ses-count 0)
-             ;;We've seen enough boundaries - stop undoing
-             (throw 'undo nil)))
-       (and (consp ses-x) (symbolp (car ses-x)) (fboundp (car ses-x))
-            ;;Undo using apply
-            (apply (car ses-x) (cdr ses-x)))))
-    (if (not (eq major-mode 'ses-mode))
-       ad-do-it
-      ;;Here is some extra code for SES mode.
-      (setq ses--deferred-narrow
-           (or ses--deferred-narrow (< (point-max) (buffer-size))))
-      (widen)
-      (condition-case x
-         ad-do-it
-       (error
-        ;;Restore narrow if appropriate
-        (ses-command-hook)
-        (signal (car x) (cdr x)))))))
+;;----------------------------------------------------------------------------
+;; Undo control
+;;----------------------------------------------------------------------------
 
 (defun ses-begin-change ()
-  "For undo, remember current buffer-position before we start changing hidden
-stuff."
+  "For undo, remember point before we start changing hidden stuff."
   (let ((inhibit-read-only t))
     (insert-and-inherit "X")
     (delete-region (1- (point)) (point))))
 
 (defun ses-set-with-undo (sym newval)
   "Like set, but undoable.  Result is t if value has changed."
-  ;;We avoid adding redundant entries to the undo list, but this is
+  ;;We try to avoid adding redundant entries to the undo list, but this is
   ;;unavoidable for strings because equal ignores text properties and there's
   ;;no easy way to get the whole property list to see if it's different!
   (unless (and (boundp sym)
               (equal (symbol-value sym) newval)
               (not (stringp newval)))
     (push (if (boundp sym)
-             `(ses-set-with-undo ,sym ,(symbol-value sym))
-           `(ses-unset-with-undo ,sym))
+             `(apply ses-set-with-undo ,sym ,(symbol-value sym))
+           `(apply ses-unset-with-undo ,sym))
          buffer-undo-list)
     (set sym newval)
     t))
@@ -1342,20 +1320,20 @@ stuff."
 (defun ses-unset-with-undo (sym)
   "Set SYM to be unbound.  This is undoable."
   (when (1value (boundp sym)) ;;Always bound, except after a programming error
-    (push `(ses-set-with-undo ,sym ,(symbol-value sym)) buffer-undo-list)
+    (push `(apply ses-set-with-undo ,sym ,(symbol-value sym)) buffer-undo-list)
     (makunbound sym)))
 
 (defun ses-aset-with-undo (array idx newval)
   "Like aset, but undoable.  Result is t if element has changed"
   (unless (equal (aref array idx) newval)
-    (push `(ses-aset-with-undo ,array ,idx ,(aref array idx)) buffer-undo-list)
+    (push `(apply ses-aset-with-undo ,array ,idx ,(aref array idx)) buffer-undo-list)
     (aset array idx newval)
     t))
 
 
-;;;----------------------------------------------------------------------------
-;;;; Startup for major mode
-;;;----------------------------------------------------------------------------
+;;----------------------------------------------------------------------------
+;; Startup for major mode
+;;----------------------------------------------------------------------------
 
 (defun ses-load ()
   "Parse the current buffer and sets up buffer-local variables.  Does not
@@ -1363,14 +1341,14 @@ execute cell formulas or print functions."
   (widen)
   ;;Read our global parameters, which should be a 3-element list
   (goto-char (point-max))
-  (search-backward ";;; Local Variables:\n" nil t)
+  (search-backward ";; Local Variables:\n" nil t)
   (backward-list 1)
-  (let ((params (condition-case nil (read (current-buffer)) (error nil)))
-       sym)
+  (setq ses--params-marker (point-marker))
+  (let ((params (condition-case nil (read (current-buffer)) (error nil))))
     (or (and (= (safe-length params) 3)
             (numberp (car params))
             (numberp (cadr params))
-            (> (cadr params) 0)
+            (>= (cadr params) 0)
             (numberp (nth 2 params))
             (> (nth 2 params) 0))
        (error "Invalid SES file"))
@@ -1384,7 +1362,7 @@ execute cell formulas or print functions."
        (ses-set-parameter 'ses--file-format 2)
        (message "Upgrading from SES-1 file format")))
     (or (= ses--file-format 2)
-       (error "This file needs a newer version of the SES library code."))
+       (error "This file needs a newer version of the SES library code"))
     (ses-create-cell-variable-range 0 (1- ses--numrows) 0 (1- ses--numcols))
     ;;Initialize cell array
     (setq ses--cells (make-vector ses--numrows nil))
@@ -1395,7 +1373,9 @@ execute cell formulas or print functions."
   (forward-line ses--numrows)
   (or (looking-at ses-print-data-boundary)
       (error "Missing marker between print and data areas"))
-  (forward-char (length ses-print-data-boundary))
+  (forward-char 1)
+  (setq ses--data-marker (point-marker))
+  (forward-char (1- (length ses-print-data-boundary)))
   ;;Initialize printer and symbol lists
   (mapc 'ses-printer-record ses-standard-printer-functions)
   (setq ses--symbolic-formulas nil)
@@ -1468,7 +1448,7 @@ Narrows the buffer to show only the print area.  Gives it `read-only' and
     (put-text-property (point-min) (1+ (point-min)) 'front-sticky t)
     ;;Create intangible properties, which also indicate which cell the text
     ;;came from.
-    (ses-dotimes-msg (row ses--numrows) "Finding cells..."
+    (dotimes-with-progress-reporter (row ses--numrows) "Finding cells..."
       (dotimes (col ses--numcols)
        (setq pos  end
              sym  (ses-cell-symbol row col))
@@ -1481,7 +1461,7 @@ Narrows the buffer to show only the print area.  Gives it `read-only' and
        (put-text-property pos end 'intangible sym)))
     ;;Adding these properties did not actually alter the text
     (unless was-modified
-      (set-buffer-modified-p nil)
+      (restore-buffer-modified-p nil)
       (buffer-disable-undo)
       (buffer-enable-undo)))
   ;;Create the underlining overlay.  It's impossible for (point) to be 2,
@@ -1494,8 +1474,7 @@ Narrows the buffer to show only the print area.  Gives it `read-only' and
 overlay, remove special text properties."
   (widen)
   (let ((inhibit-read-only t)
-       (was-modified      (buffer-modified-p))
-       end)
+       (was-modified      (buffer-modified-p)))
     ;;Delete read-only, keymap, and intangible properties
     (set-text-properties (point-min) (point-max) nil)
     ;;Delete overlay
@@ -1576,7 +1555,7 @@ These are active only in the minibuffer, when entering or editing a formula:
     (setq ses--deferred-narrow 'ses-mode)
     (1value (add-hook 'post-command-hook 'ses-command-hook nil t))
     (run-with-idle-timer 0.01 nil 'ses-command-hook)
-    (run-hooks 'ses-mode-hook)))
+    (run-mode-hooks 'ses-mode-hook)))
 
 (put 'ses-mode 'mode-class 'special)
 
@@ -1593,19 +1572,17 @@ narrows the buffer now."
          (let ((old ses--deferred-recalc))
            (setq ses--deferred-recalc nil)
            (ses-update-cells old)))
-       (if ses--deferred-write
-           ;;We don't reset the deferred list before starting -- the most
-           ;;likely error is keyboard-quit, and we do want to keep trying
-           ;;these writes after a quit.
-           (ses-write-cells))
+       (when ses--deferred-write
+         ;;We don't reset the deferred list before starting -- the most
+         ;;likely error is keyboard-quit, and we do want to keep trying
+         ;;these writes after a quit.
+         (ses-write-cells)
+         (push '(apply ses-widen) buffer-undo-list))
        (when ses--deferred-narrow
          ;;We're not allowed to narrow the buffer until after-find-file has
          ;;read the local variables at the end of the file.  Now it's safe to
          ;;do the narrowing.
-         (save-excursion
-           (goto-char (point-min))
-           (forward-line ses--numrows)
-           (narrow-to-region (point-min) (point)))
+         (narrow-to-region (point-min) ses--data-marker)
          (setq ses--deferred-narrow nil))
        ;;Update the modeline
        (let ((oldcell ses--curcell))
@@ -1636,53 +1613,40 @@ narrows the buffer now."
     (error
      (unless executing-kbd-macro
        (ding))
-     (message (error-message-string err))))
+     (message "%s" (error-message-string err))))
   nil) ;Make coverage-tester happy
 
-(defun ses-header-string-left-offset ()
-  "Number of characters in left fringe and left scrollbar (if any)."
-  (let ((left-fringe    (round (or (frame-parameter nil 'left-fringe) 0)
-                              (frame-char-width)))
-       (left-scrollbar (if (not (eq (frame-parameter nil
-                                                     'vertical-scroll-bars)
-                                    'left))
-                           0
-                         (let ((x (frame-parameter nil 'scroll-bar-width)))
-                           ;;Non-toolkil bar is always 14 pixels?
-                           (unless x (setq x 14))
-                           ;;Always round up
-                           (ceiling x (frame-char-width))))))
-    (+ left-fringe left-scrollbar)))
-
 (defun ses-create-header-string ()
-  "Sets up `ses--header-string' as the buffer's header line, based on the
-current set of columns and window-scroll position."
-  (let* ((left-offset (ses-header-string-left-offset))
-        (totwidth (- left-offset (window-hscroll)))
-        result width result x)
+  "Set up `ses--header-string' as the buffer's header line.
+Based on the current set of columns and `window-hscroll' position."
+  (let ((totwidth (- (window-hscroll)))
+       result width x)
     ;;Leave room for the left-side fringe and scrollbar
-    (push (make-string left-offset ? ) result)
+    (push (propertize " " 'display '((space :align-to 0))) result)
     (dotimes (col ses--numcols)
       (setq width    (ses-col-width col)
            totwidth (+ totwidth width 1))
-      (if (= totwidth (+ left-offset 1))
+      (if (= totwidth 1)
          ;;Scrolled so intercolumn space is leftmost
          (push " " result))
-      (when (> totwidth (+ left-offset 1))
+      (when (> totwidth 1)
        (if (> ses--header-row 0)
            (save-excursion
              (ses-goto-print (1- ses--header-row) col)
              (setq x (buffer-substring-no-properties (point)
                                                      (+ (point) width)))
-             (if (>= width (- totwidth left-offset))
-                 (setq x (substring x (- width totwidth left-offset -1))))
-             (push (propertize x 'face ses-box-prop) result))
-         (setq x (ses-column-letter col))
+             ;; Strip trailing space.
+             (if (string-match "[ \t]+\\'" x)
+                 (setq x (substring x 0 (match-beginning 0))))
+             ;; Cut off excess text.
+             (if (>= (length x) totwidth)
+                 (setq x (substring x 0 (- totwidth -1)))))
+         (setq x (ses-column-letter col)))
          (push (propertize x 'face ses-box-prop) result)
-         (push (propertize (make-string (- width (length x)) ?.)
+       (push (propertize "."
                            'display    `((space :align-to ,(1- totwidth)))
                            'face       ses-box-prop)
-               result))
+             result)
        ;;Allow the following space to be squished to make room for the 3-D box
        ;;Coverage test ignores properties, thinks this is always a space!
        (push (1value (propertize " " 'display `((space :align-to ,totwidth))))
@@ -1694,9 +1658,9 @@ current set of columns and window-scroll position."
     (setq ses--header-string (apply 'concat (nreverse result)))))
 
 
-;;;----------------------------------------------------------------------------
-;;;; Redisplay and recalculation
-;;;----------------------------------------------------------------------------
+;;----------------------------------------------------------------------------
+;; Redisplay and recalculation
+;;----------------------------------------------------------------------------
 
 (defun ses-jump (sym)
   "Move point to cell SYM."
@@ -1731,7 +1695,7 @@ print area if NONARROW is nil."
     ;;find the data area when inserting or deleting *skip* values for cells
     (dotimes (row ses--numrows)
       (insert-and-inherit ses--blank-line))
-    (ses-dotimes-msg (row ses--numrows) "Reprinting..."
+    (dotimes-with-progress-reporter (row ses--numrows) "Reprinting..."
       (if (eq (ses-cell-value row 0) '*skip*)
          ;;Column deletion left a dangling skip
          (ses-set-cell row 0 'value nil))
@@ -1776,7 +1740,7 @@ to are recalculated first."
        (error (setq sig hold))))
     (cond
      (sig
-      (message (error-message-string sig)))
+      (message "%s" (error-message-string sig)))
      ((consp ses--curcell)
       (message " "))
      (t
@@ -1814,13 +1778,15 @@ cells."
   (interactive "*")
   (ses-begin-change)
   ;;Reconstruct reference lists.
-  (let (refs x yrow ycol)
+  (let (x yrow ycol)
     ;;Delete old reference lists
-    (ses-dotimes-msg (row ses--numrows) "Deleting references..."
+    (dotimes-with-progress-reporter
+        (row ses--numrows) "Deleting references..."
       (dotimes (col ses--numcols)
        (ses-set-cell row col 'references nil)))
     ;;Create new reference lists
-    (ses-dotimes-msg (row ses--numrows) "Computing references..."
+    (dotimes-with-progress-reporter
+        (row ses--numrows) "Computing references..."
       (dotimes (col ses--numcols)
        (dolist (ref (ses-formula-references (ses-cell-formula row col)))
          (setq x    (ses-sym-rowcol ref)
@@ -1830,12 +1796,10 @@ cells."
                        (cons (ses-cell-symbol row col)
                              (ses-cell-references yrow ycol)))))))
   ;;Delete everything and reconstruct basic data area
-  (if (< (point-max) (buffer-size))
-      (setq ses--deferred-narrow t))
-  (widen)
+  (ses-widen)
   (let ((inhibit-read-only t))
     (goto-char (point-max))
-    (if (search-backward ";;; Local Variables:\n" nil t)
+    (if (search-backward ";; Local Variables:\n" nil t)
        (delete-region (point-min) (point))
       ;;Buffer is quite screwed up - can't even save the user-specified locals
       (delete-region (point-min) (point-max))
@@ -1845,11 +1809,17 @@ cells."
     (dotimes (row ses--numrows)
       (insert ses--blank-line))
     (insert ses-print-data-boundary)
+    (backward-char (1- (length ses-print-data-boundary)))
+    (setq ses--data-marker (point-marker))
+    (forward-char (1- (length ses-print-data-boundary)))
     ;;Placeholders for cell data
     (insert (make-string (* ses--numrows (1+ ses--numcols)) ?\n))
     ;;Placeholders for col-widths, col-printers, default-printer, header-row
     (insert "\n\n\n\n")
-    (insert ses-initial-global-parameters))
+    (insert ses-initial-global-parameters)
+    (backward-char (1- (length ses-initial-global-parameters)))
+    (setq ses--params-marker (point-marker))
+    (forward-char (1- (length ses-initial-global-parameters))))
   (ses-set-parameter 'ses--col-widths ses--col-widths)
   (ses-set-parameter 'ses--col-printers ses--col-printers)
   (ses-set-parameter 'ses--default-printer ses--default-printer)
@@ -1862,9 +1832,9 @@ cells."
   (goto-char (point-min)))
 
 
-;;;----------------------------------------------------------------------------
-;;;; Input of cell formulas
-;;;----------------------------------------------------------------------------
+;;----------------------------------------------------------------------------
+;; Input of cell formulas
+;;----------------------------------------------------------------------------
 
 (defun ses-edit-cell (row col newval)
   "Display current cell contents in minibuffer, for editing.  Returns nil if
@@ -1900,20 +1870,22 @@ cell formula was unsafe and user declined confirmation."
 (defun ses-read-cell (row col newval)
   "Self-insert for initial character of cell function."
   (interactive
-   (let ((initial (this-command-keys))
-        (rowcol  (progn (ses-check-curcell) (ses-sym-rowcol ses--curcell))))
+   (let* ((initial (this-command-keys))
+          (rowcol  (progn (ses-check-curcell) (ses-sym-rowcol ses--curcell)))
+          (curval (ses-cell-formula (car rowcol) (cdr rowcol))))
      (barf-if-buffer-read-only)
-     (if (string= initial "\"")
-        (setq initial "\"\"") ;Enter a string
-       (if (string= initial "(")
-          (setq initial "()"))) ;Enter a formula list
      (list (car rowcol)
           (cdr rowcol)
-          (read-from-minibuffer (format "Cell %s: " ses--curcell)
-                                (cons initial 2)
-                                ses-mode-edit-map
-                                t ;Convert to Lisp object
-                                'ses-read-cell-history))))
+           (read-from-minibuffer
+            (format "Cell %s: " ses--curcell)
+            (cons (if (equal initial "\"") "\"\""
+                    (if (equal initial "(") "()" initial)) 2)
+            ses-mode-edit-map
+            t                         ;Convert to Lisp object
+            'ses-read-cell-history
+            (prin1-to-string (if (eq (car-safe curval) 'ses-safe-formula)
+                                (cadr curval)
+                              curval))))))
   (when (ses-edit-cell row col newval)
     (ses-command-hook) ;Update cell widths before movement
     (dolist (x ses-after-entry-functions)
@@ -1968,9 +1940,9 @@ cells."
        (ses-clear-cell (car rowcol) (cdr rowcol))))))
 
 
-;;;----------------------------------------------------------------------------
-;;;; Input of cell-printer functions
-;;;----------------------------------------------------------------------------
+;;----------------------------------------------------------------------------
+;; Input of cell-printer functions
+;;----------------------------------------------------------------------------
 
 (defun ses-read-printer (prompt default)
   "Common code for `ses-read-cell-printer', `ses-read-column-printer', and `ses-read-default-printer'.
@@ -2009,7 +1981,7 @@ latter two cases, the function's result should be either a string (will be
 right-justified) or a list of one string (will be left-justified)."
   (interactive
    (let ((default t)
-        prompt x)
+        x)
      (ses-check-curcell 'range)
      ;;Default is none if not all cells in range have same printer
      (catch 'ses-read-cell-printer
@@ -2059,9 +2031,9 @@ right-justified) or a list of one string (will be left-justified)."
     (ses-reprint-all t)))
 
 
-;;;----------------------------------------------------------------------------
-;;;; Spreadsheet size adjustments
-;;;----------------------------------------------------------------------------
+;;----------------------------------------------------------------------------
+;; Spreadsheet size adjustments
+;;----------------------------------------------------------------------------
 
 (defun ses-insert-row (count)
   "Insert a new row before the current one.  With prefix, insert COUNT rows
@@ -2080,14 +2052,14 @@ before current one."
     (ses-set-parameter 'ses--numrows (+ ses--numrows count))
     ;;Insert each row
     (ses-goto-print row 0)
-    (ses-dotimes-msg (x count) "Inserting row..."
+    (dotimes-with-progress-reporter (x count) "Inserting row..."
       ;;Create a row of empty cells.  The `symbol' fields will be set by
       ;;the call to ses-relocate-all.
       (setq newrow (make-vector ses--numcols nil))
       (dotimes (col ses--numcols)
-       (aset newrow col (make-vector ses-cell-size nil)))
+       (aset newrow col (ses-make-cell)))
       (setq ses--cells (ses-vector-insert ses--cells row newrow))
-      (push `(ses-vector-delete ses--cells ,row 1) buffer-undo-list)
+      (push `(apply ses-vector-delete ses--cells ,row 1) buffer-undo-list)
       (insert ses--blank-line))
     ;;Insert empty lines in cell data area (will be replaced by
     ;;ses-relocate-all)
@@ -2109,6 +2081,8 @@ before current one."
       (ses-reset-header-string)))
   ;;Reconstruct text attributes
   (ses-setup)
+  ;;Prepare for undo
+  (push '(apply ses-widen) buffer-undo-list)
   ;;Return to current cell
   (if ses--curcell
       (ses-jump-safe ses--curcell)
@@ -2122,8 +2096,7 @@ current one."
   (or (> count 0) (signal 'args-out-of-range nil))
   (let ((inhibit-quit t)
        (inhibit-read-only t)
-       (row (car (ses-sym-rowcol ses--curcell)))
-       pos)
+       (row (car (ses-sym-rowcol ses--curcell))))
     (setq count (min count (- ses--numrows row)))
     (ses-begin-change)
     (ses-set-parameter 'ses--numrows (- ses--numrows count))
@@ -2146,13 +2119,15 @@ current one."
       (ses-reset-header-string)))
   ;;Reconstruct attributes
   (ses-setup)
+  ;;Prepare for undo
+  (push '(apply ses-widen) buffer-undo-list)
   (ses-jump-safe ses--curcell))
 
 (defun ses-insert-column (count &optional col width printer)
-  "Insert a new column before COL (default is the current one).  With prefix,
-insert COUNT columns before current one.  If COL is specified, the new
-column(s) get the specified WIDTH and PRINTER (otherwise they're taken from
-the current column)."
+  "Insert a new column before COL (default is the current one).
+With prefix, insert COUNT columns before current one.
+If COL is specified, the new column(s) get the specified WIDTH and PRINTER
+\(otherwise they're taken from the current column)."
   (interactive "*p")
   (ses-check-curcell)
   (or (> count 0) (signal 'args-out-of-range nil))
@@ -2170,7 +2145,7 @@ the current column)."
     (ses-create-cell-variable-range 0            (1- ses--numrows)
                                    ses--numcols (+ ses--numcols count -1))
     ;;Insert each column.
-    (ses-dotimes-msg (x count) "Inserting column..."
+    (dotimes-with-progress-reporter (x count) "Inserting column..."
       ;;Create a column of empty cells.  The `symbol' fields will be set by
       ;;the call to ses-relocate-all.
       (ses-adjust-print-width col (1+ width))
@@ -2181,8 +2156,7 @@ the current column)."
             (setq has-skip t))
        (ses-aset-with-undo ses--cells row
                            (ses-vector-insert (aref ses--cells row)
-                                              col
-                                             (make-vector ses-cell-size nil)))
+                                              col (ses-make-cell)))
        ;;Insert empty lines in cell data area (will be replaced by
        ;;ses-relocate-all)
        (ses-goto-data row col)
@@ -2217,7 +2191,7 @@ from the current one."
        (inhibit-read-only t)
        (rowcol  (ses-sym-rowcol ses--curcell))
        (width 0)
-       new col origrow has-skip)
+       col origrow has-skip)
     (setq origrow (car rowcol)
          col     (cdr rowcol)
          count   (min count (- ses--numcols col)))
@@ -2229,7 +2203,7 @@ from the current one."
     (ses-begin-change)
     (ses-set-parameter 'ses--numcols (- ses--numcols count))
     (ses-adjust-print-width col (- width))
-    (ses-dotimes-msg (row ses--numrows) "Deleting column..."
+    (dotimes-with-progress-reporter (row ses--numrows) "Deleting column..."
       ;;Delete lines from cell data area
       (ses-goto-data row col)
       (ses-delete-line count)
@@ -2320,9 +2294,9 @@ inserts a new row if at bottom of print area.  Repeat COUNT times."
       (ses-print-cell-new-width row col))))
 
 
-;;;----------------------------------------------------------------------------
-;;;; Cut and paste, import and export
-;;;----------------------------------------------------------------------------
+;;----------------------------------------------------------------------------
+;; Cut and paste, import and export
+;;----------------------------------------------------------------------------
 
 (defadvice copy-region-as-kill (around ses-copy-region-as-kill
                                activate preactivate)
@@ -2340,7 +2314,10 @@ hard to override how mouse-1 works."
                (eq (get-text-property beg 'read-only) 'ses)
                (eq (get-text-property (1- end) 'read-only) 'ses)))
       ad-do-it ;Normal copy-region-as-kill
-    (kill-new (ses-copy-region beg end))))
+    (kill-new (ses-copy-region beg end))
+    (if transient-mark-mode
+       (setq deactivate-mark t))
+    nil))
 
 (defun ses-copy-region (beg end)
   "Treat the region as rectangular.  Convert the intangible attributes to
@@ -2475,7 +2452,7 @@ formulas are to be inserted without relocation."
             (colincr  (- (cdr rowcol) (cdr first)))
             (pos      0)
             myrow mycol x)
-       (ses-dotimes-msg (row needrows) "Yanking..."
+       (dotimes-with-progress-reporter (row needrows) "Yanking..."
          (setq myrow (+ row (car rowcol)))
          (dotimes (col needcols)
            (setq mycol (+ col (cdr rowcol))
@@ -2594,7 +2571,7 @@ spot, or error signal if user requests cancel."
          colbool  (> needcols 0))
     (when (or rowbool colbool)
       ;;Need to insert.  Get confirm
-      (or (y-or-n-p (format "Yank will insert %s%s%s.  Continue "
+      (or (y-or-n-p (format "Yank will insert %s%s%s.  Continue? "
                            (if rowbool (format "%d rows" needrows) "")
                            (if (and rowbool colbool) " and " "")
                            (if colbool (format "%d columns" needcols) "")))
@@ -2654,30 +2631,9 @@ WANT-FORMULAS is non-nil.  Newlines and tabs in the export text are escaped."
     (kill-new result)))
 
 
-;;;----------------------------------------------------------------------------
-;;;; Other user commands
-;;;----------------------------------------------------------------------------
-
-;; This should be used by `call-interactively'.
-(defun ses-read-number (prompt &optional default)
-  (let ((n nil))
-    (when default
-      (setq prompt
-           (if (string-match "\\(\\):[^:]*" prompt)
-               (replace-match (format " [%s]" default) t t prompt 1)
-             (concat prompt (format " [%s] " default)))))
-    (while
-       (progn
-         (let ((str (read-from-minibuffer prompt nil nil nil nil
-                                          (number-to-string default))))
-           (setq n (cond
-                    ((zerop (length str)) default)
-                    ((stringp str) (read str)))))
-         (unless (numberp n)
-           (message "Please enter a number.")
-           (sit-for 1)
-           t)))
-    n))
+;;----------------------------------------------------------------------------
+;; Other user commands
+;;----------------------------------------------------------------------------
 
 (defun ses-unset-header-row ()
   "Select the default header row."
@@ -2694,12 +2650,15 @@ The top row is row 1.  Selecting row 0 displays the default header row."
    (list (if (numberp current-prefix-arg) current-prefix-arg
           (let ((currow (1+ (car (ses-sym-rowcol ses--curcell)))))
             (if current-prefix-arg
-                (ses-read-number "Header row: " currow)
+                (read-number "Header row: " currow)
               currow)))))
   (if (or (< row 0) (> row ses--numrows))
       (error "Invalid header-row"))
   (ses-begin-change)
-  (ses-set-parameter 'ses--header-row row)
+  (let ((oldval ses--header-row))
+    (let (buffer-undo-list)
+      (ses-set-parameter 'ses--header-row row))
+    (push `(apply ses-set-header-row ,oldval) buffer-undo-list))
   (ses-reset-header-string))
 
 (defun ses-mark-row ()
@@ -2850,9 +2809,9 @@ highlighted range in the spreadsheet."
   (ses-insert-ses-range))
 
 
-;;;----------------------------------------------------------------------------
-;;;; Checking formulas for safety
-;;;----------------------------------------------------------------------------
+;;----------------------------------------------------------------------------
+;; Checking formulas for safety
+;;----------------------------------------------------------------------------
 
 (defun ses-safe-printer (printer)
   "Returns PRINTER if safe, or the substitute printer `ses-unsafe' otherwise."
@@ -2883,9 +2842,9 @@ is safe or user allows execution anyway.  Always returns t if
                        formula checker)))))
 
 
-;;;----------------------------------------------------------------------------
-;;;; Standard formulas
-;;;----------------------------------------------------------------------------
+;;----------------------------------------------------------------------------
+;; Standard formulas
+;;----------------------------------------------------------------------------
 
 (defmacro ses-range (from to)
   "Expands to a list of cell-symbols for the range.  The range automatically
@@ -2901,8 +2860,8 @@ alias for this macro!"
   "Return ARGS reversed, with the blank elements (nil and *skip*) removed."
   (let (result)
     (dolist (cur args)
-      (and cur (not (eq cur '*skip*))
-          (push cur result)))
+      (unless (memq cur '(nil *skip*))
+       (push cur result)))
     result))
 
 (defun ses+ (&rest args)
@@ -2933,13 +2892,14 @@ TEST is evaluated."
     (cons 'list result)))
 
 ;;All standard formulas are safe
-(dolist (x '(ses-range ses-delete-blanks ses+ ses-average ses-select))
+(dolist (x '(ses-cell-value ses-range ses-delete-blanks ses+ ses-average
+            ses-select))
   (put x 'side-effect-free t))
 
 
-;;;----------------------------------------------------------------------------
-;;;; Standard print functions
-;;;----------------------------------------------------------------------------
+;;----------------------------------------------------------------------------
+;; Standard print functions
+;;----------------------------------------------------------------------------
 
 ;;These functions use the variables 'row' and 'col' that are
 ;;dynamically bound by ses-print-cell.  We define these varables at
@@ -2956,7 +2916,7 @@ columns to include in width (default = 0)."
   (let ((printer (or (ses-col-printer col) ses--default-printer))
        (width   (ses-col-width col))
        half)
-    (or fill (setq fill ? ))
+    (or fill (setq fill ?\s))
     (or span (setq span 0))
     (setq value (ses-call-printer printer value))
     (dotimes (x span)
@@ -3003,5 +2963,5 @@ current column and continues until the next nonblank column."
 
 (provide 'ses)
 
-;;; arch-tag: 88c1ccf0-4293-4824-8c5d-0757b52217f3
-;; ses.el ends here.
+;; arch-tag: 88c1ccf0-4293-4824-8c5d-0757b52217f3
+;;; ses.el ends here