;;; emacspeak-dismal.el --- Speech enable Dismal -- An Emacs Spreadsheet program  -*- lexical-binding: t; -*-
;;; Description: spread sheet extension
;;; Keywords:emacspeak, audio interface to emacs spread sheets
;;{{{  LCD Archive entry: 

;;; LCD Archive Entry:
;;; emacspeak| T. V. Raman |tv.raman.tv@gmail.com
;;; A speech interface to Emacs |
;;; $Date: 2008-06-21 10:50:41 -0700 (Sat, 21 Jun 2008) $ |
;;;  $Revision: 4532 $ | 
;;; Location undetermined
;;;

;;}}}
;;{{{  Copyright:
;;;Copyright (C) 1995 -- 2018, T. V. Raman 
;;; Copyright (c) 1995 by T. V. Raman  
;;; All Rights Reserved. 
;;;
;;; This file is not part of GNU Emacs, but the same permissions apply.
;;;
;;; 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)
;;; any later version.
;;;
;;; GNU Emacs is distributed in the hope that it will be useful,
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;; GNU General Public License for more details.
;;;
;;; 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, 675 Mass Ave, Cambridge, MA 02139, USA.

;;}}}
;;{{{  Introduction

;;; emacspeak extensions to the dismal spreadsheet. 
;;; Dismal can be found at ftp://cs.nyu.edu/pub/local/fox/dismal
;;; Code:

;;}}}
;;{{{  requires 
(require 'cl-lib)
(cl-declaim  (optimize  (safety 0) (speed 3)))
(require 'emacspeak-preamble)
;;}}}
;;{{{ Forward Decls:

(declare-function dismal-get-val "ext:dismal" (r c))
(declare-function  dismal-convert-cellexpr-to-string "dismal" (sexp))
(declare-function dismal-get-exp "dismal" (r c))
(declare-function  dismal-display-current-cell-expr "dismal" (r c))
(declare-function  dis-forward-row "dismal" (rows))
(declare-function dis-backward-row "dismal" (rows))
(declare-function  dis-forward-column "dismal" (cols))
(declare-function  dis-backward-column "dismal" (cols))
(declare-function dis-recalculate-matrix "dismal" nil)

;;}}}
;;{{{  helper functions:

;;; return cell value as a string

(defun emacspeak-dismal-cell-value (row col)
  (let ((value (dismal-get-val row col)))
    (if (floatp value)
        (format "%.2f" value)
      (dismal-convert-cellexpr-to-string value)value)))

(defun emacspeak-dismal-current-cell-value ()
  (cl-declare (special dismal-current-row dismal-current-col))
  (emacspeak-dismal-cell-value dismal-current-row dismal-current-col))

;;; return entry in col 0 of current row as a string:

(defun emacspeak-dismal-current-row-header ()
  (cl-declare (special dismal-current-row))
  (dismal-convert-cellexpr-to-string
   (dismal-get-exp dismal-current-row  0)))

(defun emacspeak-dismal-current-col-header ()
  (cl-declare (special dismal-current-col))
  (dismal-convert-cellexpr-to-string
   (dismal-get-exp 0  dismal-current-col)))

;;}}}
;;{{{  Additional interactive commands

(defun emacspeak-dismal-display-cell-expression ()
  "Display the expression in the message area"
  (interactive)
  (cl-declare (special dismal-current-row dismal-current-col))
  (dismal-display-current-cell-expr dismal-current-row dismal-current-col))

(defun emacspeak-dismal-display-cell-value ()
  "Display the cell value in the message area"
  (interactive)
  (cl-declare (special dismal-current-cell))
  (message "%s = %s"
           dismal-current-cell
           (emacspeak-dismal-current-cell-value)))

(defun emacspeak-dismal-display-cell-with-row-header ()
  "Displays current cell along with its row header.
The `row header' is the entry in column 0."
  (interactive)
  (cl-declare (special))
  (let ((row-head  (emacspeak-dismal-current-row-header))
        (value (emacspeak-dismal-current-cell-value)))
    (message "%s is %s"
             row-head value)))

(defun emacspeak-dismal-display-cell-with-col-header ()
  "Display current cell along with its column header.
The `column header' is the entry in row 0."
  (interactive)
  (let ((col-head  (emacspeak-dismal-current-col-header))
        (value (emacspeak-dismal-current-cell-value)))
    (message "%s is %s"
             col-head value)))

(defun emacspeak-dismal-forward-row-and-summarize (rows)
  "Move forward by arg rows
 (the next row by default)and summarize it."
  (interactive "p")
  (dis-forward-row rows)
  (emacspeak-dismal-row-summarize))

(defun emacspeak-dismal-backward-row-and-summarize (rows)
  "Move backward by arg rows
 (the previous row by default)and summarize it."
  (interactive "p")
  (dis-backward-row rows)
  (emacspeak-dismal-row-summarize))

(defun emacspeak-dismal-forward-col-and-summarize (cols)
  "Move forward by arg columns
 (the next column by default)and summarize it."
  (interactive "p")
  (dis-forward-column cols)
  (emacspeak-dismal-col-summarize))

(defun emacspeak-dismal-backward-col-and-summarize (cols)
  "Move backward by arg columns
 (the previous column by default)and summarize it."
  (interactive "p")
  (dis-backward-column cols)
  (emacspeak-dismal-col-summarize))

;;}}}
;;{{{  Intelligent summaries

(defvar emacspeak-dismal-sheet-summarizer-list nil
  "Specifies how the entire sheet  should be summarized. ")

(make-variable-buffer-local 'emacspeak-dismal-sheet-summarizer-list)

(defvar emacspeak-dismal-row-summarizer-list nil
  "Specifies how rows should be summarized. ")

(make-variable-buffer-local 'emacspeak-dismal-row-summarizer-list)

(defvar emacspeak-dismal-col-summarizer-list nil
  "Specifies how cols should be summarized. ")

(make-variable-buffer-local 'emacspeak-dismal-col-summarizer-list)

(setq-default emacspeak-dismal-row-summarizer-list nil)
(setq-default emacspeak-dismal-col-summarizer-list nil)
(setq-default emacspeak-dismal-sheet-summarizer-list nil)
(defvar emacspeak-dismal-value-personality voice-animate
  "Personality used for speaking cell values in summaries.")

(defun emacspeak-dismal-row-summarize  ()
  "Summarizes a row using the specification in list
emacspeak-dismal-row-summarizer-list"
  (interactive)
  (cl-declare (special emacspeak-dismal-row-summarizer-list
                       emacspeak-dismal-value-personality
                       voice-lock-mode
                       dismal-current-row))
  (unless  (and  emacspeak-dismal-row-summarizer-list
                 (vectorp emacspeak-dismal-row-summarizer-list))
    (setq emacspeak-dismal-row-summarizer-list
          (read-minibuffer "Specify summarizer as a vector:
" "[")))
  (let ((summary nil))
    (setq summary 
          (mapconcat
            #'(lambda (token)
              (let ((value nil))
                (cond
                 ((stringp token) token)
                 ((numberp token)
                  (setq value
                        (format "%s"
                                (emacspeak-dismal-cell-value
                                 dismal-current-row token)))
                  (put-text-property  0   (length value)
                                      'personality  emacspeak-dismal-value-personality 
                                      value)
                  value)
                 ((and (listp token)
                       (numberp (cl-first token))
                       (numberp (cl-second token)))
                  (setq value
                        (format "%s"
                                (emacspeak-dismal-cell-value
                                 (cl-first token)
                                 (cl-second token))))
                  (put-text-property 0   (length value)
                                     'personality emacspeak-dismal-value-personality 
                                     value)
                  value)
                 (t  (format "%s" token)))))
           emacspeak-dismal-row-summarizer-list 
           " "))
    (dtk-speak summary)))

(defun emacspeak-dismal-col-summarize  ()
  "Summarizes a col using the specification in list
emacspeak-dismal-col-summarizer-list"
  (interactive)
  (cl-declare (special emacspeak-dismal-col-summarizer-list
                       emacspeak-dismal-value-personality voice-lock-mode
                       dismal-current-col))
  (unless  (and  emacspeak-dismal-col-summarizer-list
                 (vectorp emacspeak-dismal-col-summarizer-list))
    (setq emacspeak-dismal-col-summarizer-list
          (read-minibuffer "Specify summarizer as a vector:
" "[")))
  (let ((summary nil))
    (setq summary 
          (mapconcat
            #'(lambda (token)
              (let ((value nil))
                (cond
                 ((stringp token) token)
                 ((numberp token)
                  (setq value
                        (format "%s"
                                (emacspeak-dismal-cell-value token
                                                             dismal-current-col)))
                  (put-text-property 0 (length value)
                                     'personality
                                     emacspeak-dismal-value-personality value)
                  value)
                 ((and (listp token)
                       (numberp (cl-first token))
                       (numberp (cl-second token)))
                  (setq value
                        (format "%s"
                                (emacspeak-dismal-cell-value
                                 (cl-first token)
                                 (cl-second token))))
                  (put-text-property 0 (length value)
                                     'personality
                                     emacspeak-dismal-value-personality value)
                  value)
                 (t  (format "%s" token)))))
           emacspeak-dismal-col-summarizer-list 
           " "))
    (dtk-speak summary)))

(defun emacspeak-dismal-sheet-summarize  ()
  "Summarizes a sheet using the specification in list
emacspeak-dismal-sheet-summarizer-list"
  (interactive)
  (cl-declare (special emacspeak-dismal-row-summarizer-list))
  (when emacspeak-dismal-sheet-summarizer-list
    (let ((emacspeak-speak-messages nil))
      (dis-recalculate-matrix))
    (message 
     (mapconcat
       #'(lambda (token)
         (cond
          ((stringp token) token)
          ((and (listp token)
                (numberp (cl-first token))
                (numberp (cl-second token)))
           (emacspeak-dismal-cell-value
            (cl-first token)
            (cl-second token)))
          (t  (format "%s" token))))
      emacspeak-dismal-sheet-summarizer-list 
      " "))))

(defun emacspeak-dismal-set-row-summarizer-list ()
  "Specify or reset row summarizer list."
  (interactive)
  (cl-declare (special emacspeak-dismal-col-summarizer-list))
  (setq emacspeak-dismal-row-summarizer-list
        (read-minibuffer
         "Specify summarizer as a list: "
         (format "%S"
                 (or emacspeak-dismal-row-summarizer-list  "[")))))

(defun emacspeak-dismal-set-col-summarizer-list ()
  "Specify or reset col summarizer list."
  (interactive)
  (cl-declare (special emacspeak-dismal-col-summarizer-list))
  (setq emacspeak-dismal-col-summarizer-list
        (read-minibuffer
         "Specify summarizer as a vector: "
         (format "%S"
                 (or emacspeak-dismal-col-summarizer-list  "[")))))

(defun emacspeak-dismal-set-sheet-summarizer-list ()
  "Specify or reset sheet summarizer list."
  (interactive)
  (cl-declare (special emacspeak-dismal-sheet-summarizer-list))
  (setq emacspeak-dismal-sheet-summarizer-list
        (read-minibuffer
         "Specify summarizer as a list: "
         (format "%S"
                 (or emacspeak-dismal-sheet-summarizer-list  "[")))))

;;}}}
;;{{{  key bindings

;;; record emacspeak stat that we want dismal to save
(defvar emacspeak-dismal-already-customized-dismal nil
  "Records if we have customized dismal.
Checked by emacspeak specific dis-mode-hooks entry.")

(add-hook 'dis-mode-hooks
          #'(lambda nil
              (cl-declare (special dismal-saved-variables dismal-map))
              (define-key dismal-map (concat emacspeak-prefix "e")
                'dis-last-column)
              (define-key dismal-map emacspeak-prefix 'emacspeak-prefix-command)
              (unless emacspeak-dismal-already-customized-dismal
                (setq emacspeak-dismal-already-customized-dismal t)
                (push 'emacspeak-dismal-sheet-summarizer-list
                      dismal-saved-variables)
                (push 'emacspeak-dismal-row-summarizer-list
                      dismal-saved-variables)
                (push 'emacspeak-dismal-col-summarizer-list
                      dismal-saved-variables))))

(add-hook
 'dis-mode-hooks
 #'(lambda nil
     (cl-declare (special dismal-map emacspeak-prefix))
     (local-unset-key "\M-[")
     (local-unset-key emacspeak-prefix)
     (define-key dismal-map emacspeak-prefix 'emacspeak-prefix-command)
     (define-key dismal-map (concat emacspeak-prefix "e")
       'dis-last-column)
     (define-key dismal-map  "," 'emacspeak-dismal-display-cell-expression)
     (define-key dismal-map  "." 'emacspeak-dismal-display-cell-value)
     (define-key dismal-map "R" 'emacspeak-dismal-display-cell-with-row-header)
     (define-key dismal-map "S" 'emacspeak-dismal-sheet-summarize)
     (define-key dismal-map "C" 'emacspeak-dismal-display-cell-with-col-header)
     (define-key dismal-map "\M-m" 'emacspeak-dismal-row-summarize)
     (define-key dismal-map '[up] 'emacspeak-dismal-backward-row-and-summarize)
     (define-key dismal-map '[down] 'emacspeak-dismal-forward-row-and-summarize)
     (define-key dismal-map '[left] 'emacspeak-dismal-backward-col-and-summarize)
     (define-key dismal-map '[right] 'emacspeak-dismal-forward-col-and-summarize)))

;;}}}
;;{{{  Advice some commands. 

;;}}}
;;{{{ customize for use with html helper mode
(defadvice dis-html-dump-file (around fix pre act comp)
  "Sets html-helper-build-new-buffer to nil first so we dont
end up building a template page first."

  (let ((html-helper-build-new-buffer nil)) ad-do-it))

(defadvice dis-html-dump-range (around fix pre act comp)
  "Sets html-helper-build-new-buffer to nil first so we dont
end up building a template page first."

  (let ((html-helper-build-new-buffer nil)) ad-do-it))

;;}}}
(provide  'emacspeak-dismal)
;;{{{  emacs local variables 

;;; local variables:
;;; folded-file: t
;;; end: 

;;}}}
