(w32-system-shells): Add TCC (new name for 4NT.)
[bpt/emacs.git] / lisp / ses.el
index 09f7809..ced4994 100644 (file)
@@ -1,6 +1,6 @@
 ;;; ses.el -- Simple Emacs Spreadsheet  -*- coding: utf-8 -*-
 
-;; Copyright (C) 2002, 2003, 2004, 2005  Free Software Foundation, Inc.
+;; Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008  Free Software Foundation, Inc.
 
 ;; Author: Jonathan Yavner <jyavner@member.fsf.org>
 ;; Maintainer: Jonathan Yavner <jyavner@member.fsf.org>
@@ -10,7 +10,7 @@
 
 ;; GNU Emacs is free software; you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2, or (at your option)
+;; the Free Software Foundation; either version 3, or (at your option)
 ;; any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
@@ -20,8 +20,8 @@
 
 ;; 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:
 
@@ -49,7 +49,7 @@
 ;;----------------------------------------------------------------------------
 
 (defgroup ses nil
-  "Simple Emacs Spreadsheet"
+  "Simple Emacs Spreadsheet."
   :group  'applications
   :prefix "ses-"
   :version "21.1")
@@ -148,8 +148,7 @@ 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.")
 
@@ -173,8 +172,10 @@ Each function is called with ARG=1."
                "\""      ses-read-cell
                "'"       ses-read-symbol
                "="       ses-edit-cell
+               "c"       ses-recalculate-cell
                "j"       ses-jump
                "p"       ses-read-cell-printer
+               "t"       ses-truncate-cell
                "w"       ses-set-column-width
                "x"       ses-export-keymap
                "\M-p"    ses-read-column-printer))
@@ -238,13 +239,6 @@ Each function is called with ARG=1."
          ses-initial-file-trailer)
   "The initial contents of an empty spreadsheet.")
 
-(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.")
 
@@ -256,13 +250,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."))
@@ -273,6 +273,16 @@ default printer and then modify its output.")
     (make-local-variable x)
     (set x nil)))
 
+;;;This variable is documented as being permitted in file-locals:
+(put 'ses--symbolic-formulas 'safe-local-variable 'consp)
+
+(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
@@ -292,7 +302,7 @@ 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.  Otherwise nil.")
 
 (defvar ses-start-time nil
   "Time when current operation started.  Used by `ses-time-check' to decide
@@ -376,7 +386,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)
@@ -400,7 +410,7 @@ 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)
@@ -409,6 +419,7 @@ for safety.  This is a macro to prevent propagate-on-load viruses."
   "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"))
@@ -430,9 +441,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
@@ -504,10 +512,12 @@ for this spreadsheet."
                 (list (symbol-name (cadr formula))))))
 
 (defun ses-column-letter (col)
-  "Converts a column number to A..Z or AA..ZZ"
-  (if (< col 26)
-      (char-to-string (+ ?A col))
-    (string (+ ?@ (/ col 26)) (+ ?A (% col 26)))))
+  "Return the alphabetic name of column number COL.
+0-25 become A-Z; 26-701 become AA-ZZ, and so on."
+  (let ((units (char-to-string (+ ?A (% col 26)))))
+    (if (< col 26)
+        units
+      (concat (ses-column-letter (1- (/ col 26))) units))))
 
 (defun ses-create-cell-symbol (row col)
   "Produce a symbol that names the cell (ROW,COL).  (0,0) => 'A1."
@@ -625,8 +635,9 @@ the old and FORCE is nil."
     (let ((oldval  (ses-cell-value   cell))
          (formula (ses-cell-formula cell))
          newval)
-      (if (eq (car-safe formula) 'ses-safe-formula)
-         (ses-set-cell row col 'formula (ses-safe-formula (cadr formula))))
+      (when (eq (car-safe formula) 'ses-safe-formula)
+       (setq formula (ses-safe-formula (cadr formula)))
+       (ses-set-cell row col 'formula formula))
       (condition-case sig
          (setq newval (eval formula))
        (error
@@ -651,7 +662,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))
@@ -710,7 +721,7 @@ if the cell's value is unchanged if FORCE is nil."
 
 (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
@@ -734,6 +745,9 @@ region, or nil if cursor is not at a cell."
     ;;Range
     (let ((bcell (get-text-property (region-beginning) 'intangible))
          (ecell (get-text-property (1- (region-end))  'intangible)))
+      (when (= (region-end) ses--data-marker)
+       ;;Correct for overflow
+       (setq ecell (get-text-property (- (region-end) 2)  'intangible)))
       (setq ses--curcell (if (and bcell ecell)
                             (cons bcell ecell)
                           nil))))
@@ -798,7 +812,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))))
@@ -816,7 +830,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))
@@ -875,9 +889,9 @@ preceding cell has spilled over."
 
 (defun ses-call-printer (printer &optional value)
   "Invokes PRINTER (a string or parenthesized string or function-symbol or
-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
+lambda of one argument) on VALUE.  Result is 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 signaled one (and \"%s\" is used as the default printer), else nil."
   (setq ses-call-printer-return nil)
   (unless value
@@ -906,12 +920,12 @@ printer signaled 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
@@ -941,23 +955,29 @@ cell (ROW,COL) has changed."
 
 (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 (ses-narrowed-p)
-      (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)
   "Set parameter DEF to VALUE (with undo) and write the value to the data area.
@@ -967,9 +987,6 @@ If ELEM is specified, it is the array subscript within DEF to be set to VALUE."
     ;;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--col-widths      "(ses-column-widths %S)"
                            ses--col-printers    "(ses-column-printers %S)"
@@ -978,9 +995,20 @@ If ELEM is specified, it is the array subscript within DEF to be set to VALUE."
                            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 ()
   "Write cells in `ses--deferred-write' from local variables to data area.
@@ -1059,6 +1087,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
@@ -1103,23 +1148,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
@@ -1279,23 +1307,6 @@ to each symbol."
 ;; Undo control
 ;;----------------------------------------------------------------------------
 
-;; This should be unnecessary, because the feature is now built in.
-
-(defadvice undo-more (around ses-undo-more activate preactivate)
-  "For SES mode, allow undo outside of narrowed buffer range."
-  (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 (ses-narrowed-p)))
-    (widen)
-    (condition-case x
-       ad-do-it
-      (error
-       ;;Restore narrow if appropriate
-       (ses-command-hook)
-       (signal (car x) (cdr x))))))
-
 (defun ses-begin-change ()
   "For undo, remember point before we start changing hidden stuff."
   (let ((inhibit-read-only t))
@@ -1304,7 +1315,7 @@ to each symbol."
 
 (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)
@@ -1343,11 +1354,12 @@ execute cell formulas or print functions."
   (goto-char (point-max))
   (search-backward ";; Local Variables:\n" nil t)
   (backward-list 1)
+  (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"))
@@ -1372,7 +1384,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)
@@ -1467,22 +1481,27 @@ Narrows the buffer to show only the print area.  Gives it `read-only' and
   (overlay-put ses--curcell-overlay 'face 'underline))
 
 (defun ses-cleanup ()
-  "Cleanup when changing a buffer from SES mode to something else.  Delete
-overlay, remove special text properties."
+  "Cleanup when changing a buffer from SES mode to something else.
+Delete overlays, remove special text properties."
   (widen)
   (let ((inhibit-read-only t)
+        ;; When reverting, hide the buffer name, otherwise Emacs will ask
+        ;; the user "the file is modified, do you really want to make
+        ;; modifications to this buffer", where the "modifications" refer to
+        ;; the irrelevant set-text-properties below.
+        (buffer-file-name nil)
        (was-modified      (buffer-modified-p)))
     ;;Delete read-only, keymap, and intangible properties
     (set-text-properties (point-min) (point-max) nil)
     ;;Delete overlay
     (mapc 'delete-overlay (overlays-in (point-min) (point-max)))
     (unless was-modified
-      (set-buffer-modified-p nil))))
+      (restore-buffer-modified-p nil))))
 
 ;;;###autoload
 (defun ses-mode ()
   "Major mode for Simple Emacs Spreadsheet.
-See \"ses-example.ses\" (in the etc data directory) for more info.
+See \"ses-example.ses\" (in `data-directory') for more info.
 
 Key definitions:
 \\{ses-mode-map}
@@ -1569,19 +1588,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))
@@ -1612,7 +1629,7 @@ 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-create-header-string ()
@@ -1739,7 +1756,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
@@ -1795,9 +1812,7 @@ cells."
                        (cons (ses-cell-symbol row col)
                              (ses-cell-references yrow ycol)))))))
   ;;Delete everything and reconstruct basic data area
-  (if (ses-narrowed-p)
-      (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)
@@ -1810,11 +1825,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)
@@ -1865,20 +1886,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)
@@ -2074,6 +2097,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)
@@ -2110,6 +2135,8 @@ 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)
@@ -2311,6 +2338,9 @@ hard to override how mouse-1 works."
 (defun ses-copy-region (beg end)
   "Treat the region as rectangular.  Convert the intangible attributes to
 SES attributes recording the contents of the cell as of the time of copying."
+  (when (= end ses--data-marker)
+    ;;Avoid overflow situation
+    (setq end (1- ses--data-marker)))
   (let* ((inhibit-point-motion-hooks t)
         (x (mapconcat 'ses-copy-region-helper
                       (extract-rectangle beg (1- end)) "\n")))
@@ -2560,7 +2590,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) "")))
@@ -2644,7 +2674,10 @@ The top row is row 1.  Selecting row 0 displays the default header row."
   (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 ()
@@ -2878,7 +2911,8 @@ 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))
 
 
@@ -2887,7 +2921,7 @@ TEST is evaluated."
 ;;----------------------------------------------------------------------------
 
 ;;These functions use the variables 'row' and 'col' that are
-;;dynamically bound by ses-print-cell.  We define these varables at
+;;dynamically bound by ses-print-cell.  We define these variables at
 ;;compile-time to make the compiler happy.
 (eval-when-compile
   (dolist (x '(row col))
@@ -2901,7 +2935,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)
@@ -2946,6 +2980,19 @@ current column and continues until the next nonblank column."
 (dolist (x (cons 'ses-unsafe ses-standard-printer-functions))
   (put x 'side-effect-free t))
 
+(defun ses-unload-function ()
+  "Unload the Simple Emacs Spreadsheet."
+  (dolist (fun '(copy-region-as-kill yank))
+    (ad-remove-advice fun 'around (intern (concat "ses-" (symbol-name fun))))
+    (ad-update fun))
+  (save-current-buffer
+    (dolist (buf (buffer-list))
+      (set-buffer buf)
+      (when (eq major-mode 'ses-mode)
+       (funcall (or default-major-mode 'fundamental-mode)))))
+  ;; continue standard unloading
+  nil)
+
 (provide 'ses)
 
 ;; arch-tag: 88c1ccf0-4293-4824-8c5d-0757b52217f3