1 ;; Calculator for GNU Emacs, part II [calc-sel.el]
2 ;; Copyright (C) 1990, 1991, 1992, 1993, 2001 Free Software Foundation, Inc.
3 ;; Written by Dave Gillespie, daveg@synaptics.com.
5 ;; This file is part of GNU Emacs.
7 ;; GNU Emacs is distributed in the hope that it will be useful,
8 ;; but WITHOUT ANY WARRANTY. No author or distributor
9 ;; accepts responsibility to anyone for the consequences of using it
10 ;; or for whether it serves any particular purpose or works at all,
11 ;; unless he says so in writing. Refer to the GNU Emacs General Public
12 ;; License for full details.
14 ;; Everyone is granted permission to copy, modify and redistribute
15 ;; GNU Emacs, but only under the conditions described in the
16 ;; GNU Emacs General Public License. A copy of this license is
17 ;; supposed to have been given to you along with GNU Emacs so you
18 ;; can know your rights and responsibilities. It should be in a
19 ;; file named COPYING. Among other things, the copyright notice
20 ;; and this notice must be preserved on all copies.
24 ;; This file is autoloaded from calc-ext.el.
29 (defun calc-Need-calc-sel () nil)
32 ;;; Selection commands.
34 (defun calc-select-here (num &optional once keep)
37 (calc-prepare-selection)
38 (let ((found (calc-find-selected-part))
39 (entry calc-selection-cache-entry))
40 (or (and keep (nth 2 entry))
43 (setq calc-keep-selection nil)
44 (message "(Selection will apply to next command only)")))
45 (calc-change-current-selection
47 (if (and num (> (setq num (prefix-numeric-value num)) 0))
49 (while (and (>= (setq num (1- num)) 0)
50 (not (eq found (car entry))))
51 (setq found (calc-find-assoc-parent-formula
54 (calc-grow-assoc-formula (car entry) found))
58 (defun calc-select-once (num)
60 (calc-select-here num t)
63 (defun calc-select-here-maybe (num)
65 (calc-select-here num nil t)
68 (defun calc-select-once-maybe (num)
70 (calc-select-here num t t)
73 (defun calc-select-additional ()
76 (let (calc-keep-selection)
77 (calc-prepare-selection))
78 (let ((found (calc-find-selected-part))
79 (entry calc-selection-cache-entry))
80 (calc-change-current-selection
82 (let ((sel (nth 2 entry)))
85 (while (not (or (eq sel (car entry))
86 (calc-find-sub-formula sel found)))
87 (setq sel (calc-find-assoc-parent-formula
90 (calc-grow-assoc-formula (car entry) found)))
94 (defun calc-select-more (num)
97 (calc-prepare-selection)
98 (let ((entry calc-selection-cache-entry))
100 (let ((sel (nth 2 entry)))
101 (while (and (not (eq sel (car entry)))
102 (>= (setq num (1- (prefix-numeric-value num))) 0))
103 (setq sel (calc-find-assoc-parent-formula (car entry) sel)))
104 (calc-change-current-selection sel))
105 (calc-select-here num))))
108 (defun calc-select-less (num)
111 (calc-prepare-selection)
112 (let ((found (calc-find-selected-part))
113 (entry calc-selection-cache-entry))
114 (calc-change-current-selection
116 (let ((sel (nth 2 entry))
120 (>= (setq num (1- num)) 0))
122 index (calc-find-sub-formula sel found))
123 (and (setq sel (and index (nth index old)))
124 calc-assoc-selections
125 (setq op (assq (car-safe sel) calc-assoc-ops))
126 (memq (car old) (nth index op))
127 (setq num (1+ num))))
131 (defun calc-select-part (num)
133 (or num (setq num (- last-command-char ?0)))
135 (calc-prepare-selection)
136 (let ((sel (calc-find-nth-part (or (nth 2 calc-selection-cache-entry)
137 (car calc-selection-cache-entry))
140 (calc-change-current-selection sel)
141 (error "%d is not a valid sub-formula index" num))))
144 (defun calc-find-nth-part (expr num)
145 (if (and calc-assoc-selections
146 (assq (car-safe expr) calc-assoc-ops))
148 (calc-find-nth-part-rec expr))
149 (if (eq (car-safe expr) 'intv)
150 (and (>= num 1) (<= num 2) (nth (1+ num) expr))
151 (and (not (Math-primp expr)) (>= num 1) (< num (length expr))
155 (defun calc-find-nth-part-rec (expr) ; uses num, op
156 (or (if (and (setq op (assq (car-safe (nth 1 expr)) calc-assoc-ops))
157 (memq (car expr) (nth 1 op)))
158 (calc-find-nth-part-rec (nth 1 expr))
159 (and (= (setq num (1- num)) 0)
161 (if (and (setq op (assq (car-safe (nth 2 expr)) calc-assoc-ops))
162 (memq (car expr) (nth 2 op)))
163 (calc-find-nth-part-rec (nth 2 expr))
164 (and (= (setq num (1- num)) 0)
168 (defun calc-select-next (num)
171 (calc-select-previous (- num))
173 (calc-prepare-selection)
174 (let* ((entry calc-selection-cache-entry)
178 (while (>= (setq num (1- num)) 0)
179 (let* ((parent (calc-find-parent-formula (car entry) sel))
182 (and (eq p t) (setq p nil))
183 (while (and (setq p (cdr p))
184 (not (eq (car p) sel))))
186 (setq sel (or (and calc-assoc-selections
187 (setq op (assq (car-safe (nth 1 p))
189 (memq (car parent) (nth 2 op))
192 (if (and calc-assoc-selections
193 (setq op (assq (car-safe parent) calc-assoc-ops))
194 (consp (setq p (calc-find-parent-formula
195 (car entry) parent)))
196 (eq (nth 1 p) parent)
197 (memq (car p) (nth 1 op)))
199 (error "No \"next\" sub-formula")))))
200 (calc-change-current-selection sel))
201 (if (Math-primp (car entry))
202 (calc-change-current-selection (car entry))
203 (calc-select-part num))))))
206 (defun calc-select-previous (num)
209 (calc-select-next (- num))
211 (calc-prepare-selection)
212 (let* ((entry calc-selection-cache-entry)
216 (while (>= (setq num (1- num)) 0)
217 (let* ((parent (calc-find-parent-formula (car entry) sel))
218 (p (cdr-safe parent))
221 (if (eq (car-safe parent) 'intv) (setq p (cdr p)))
222 (while (and (not (eq (car p) sel))
226 (setq sel (or (and calc-assoc-selections
227 (setq op (assq (car-safe prev)
229 (memq (car parent) (nth 1 op))
232 (if (and calc-assoc-selections
233 (setq op (assq (car-safe parent) calc-assoc-ops))
234 (consp (setq p (calc-find-parent-formula
235 (car entry) parent)))
236 (eq (nth 2 p) parent)
237 (memq (car p) (nth 2 op)))
239 (error "No \"previous\" sub-formula")))))
240 (calc-change-current-selection sel))
241 (if (Math-primp (car entry))
242 (calc-change-current-selection (car entry))
243 (let ((len (if (and calc-assoc-selections
244 (assq (car (car entry)) calc-assoc-ops))
246 (calc-find-nth-part-rec (car entry))
248 (length (car entry)))))
249 (calc-select-part (- len num))))))))
252 (defun calc-find-parent-formula (expr part)
253 (cond ((eq expr part) t)
254 ((Math-primp expr) nil)
257 (while (and (setq p (cdr p))
258 (not (setq res (calc-find-parent-formula
261 (if (eq res t) expr res)))))
265 (defun calc-find-assoc-parent-formula (expr part)
266 (calc-grow-assoc-formula expr (calc-find-parent-formula expr part))
269 (defun calc-grow-assoc-formula (expr part)
270 (if calc-assoc-selections
271 (let ((op (assq (car-safe part) calc-assoc-ops)))
274 (while (and (consp (setq new (calc-find-parent-formula
277 (nth (calc-find-sub-formula new part) op)))
283 (defun calc-find-sub-formula (expr part)
284 (cond ((eq expr part) t)
285 ((Math-primp expr) nil)
288 (while (and (setq expr (cdr expr))
289 (not (calc-find-sub-formula (car expr) part)))
294 (defun calc-unselect (num)
297 (calc-prepare-selection num)
298 (calc-change-current-selection nil))
301 (defun calc-clear-selections ()
304 (let ((limit (calc-stack-size))
307 (if (calc-top n 'sel)
309 (calc-prepare-selection n)
310 (calc-change-current-selection nil)))
312 (calc-clear-command-flag 'position-point))
315 (defun calc-show-selections (arg)
318 (calc-preserve-point)
319 (setq calc-show-selections (if arg
320 (> (prefix-numeric-value arg) 0)
321 (not calc-show-selections)))
322 (let ((p calc-stack))
324 (or (null (nth 2 (car p)))
325 (equal (car p) calc-selection-cache-entry)))
328 (let ((calc-selection-cache-default-entry
329 calc-selection-cache-entry))
331 (and calc-selection-cache-entry
332 (let ((sel (nth 2 calc-selection-cache-entry)))
333 (setcar (nthcdr 2 calc-selection-cache-entry) nil)
334 (calc-change-current-selection sel)))))
335 (message (if calc-show-selections
336 "Displaying only selected part of formulas"
337 "Displaying all but selected part of formulas")))
340 (defun calc-preserve-point ()
341 (or (looking-at "\\.\n+\\'")
343 (setq calc-final-point-line (+ (count-lines (point-min) (point))
345 calc-final-point-column (current-column))
346 (calc-set-command-flag 'position-point)))
349 (defun calc-enable-selections (arg)
352 (calc-preserve-point)
353 (setq calc-use-selections (if arg
354 (> (prefix-numeric-value arg) 0)
355 (not calc-use-selections)))
356 (calc-set-command-flag 'renum-stack)
357 (message (if calc-use-selections
358 "Commands operate only on selected sub-formulas"
359 "Selections of sub-formulas have no effect")))
362 (defun calc-break-selections (arg)
365 (calc-preserve-point)
366 (setq calc-assoc-selections (if arg
367 (<= (prefix-numeric-value arg) 0)
368 (not calc-assoc-selections)))
369 (message (if calc-assoc-selections
370 "Selection treats a+b+c as a sum of three terms"
371 "Selection treats a+b+c as (a+b)+c")))
374 (defun calc-prepare-selection (&optional num)
375 (or num (setq num (calc-locate-cursor-element (point))))
376 (setq calc-selection-true-num num
377 calc-keep-selection t)
378 (or (> num 0) (setq num 1))
379 ;; (if (or (< num 1) (> num (calc-stack-size)))
380 ;; (error "Cursor must be positioned on a stack element"))
381 (let* ((entry (calc-top num 'entry))
383 (or (equal entry calc-selection-cache-entry)
385 (setcar entry (calc-encase-atoms (car entry)))
386 (setq calc-selection-cache-entry entry
387 calc-selection-cache-num num
388 calc-selection-cache-comp
389 (let ((math-comp-tagged t))
390 (math-compose-expr (car entry) 0))
391 calc-selection-cache-offset
392 (+ (car (math-stack-value-offset calc-selection-cache-comp))
393 (length calc-left-label)
394 (if calc-line-numbering 4 0))))))
395 (calc-preserve-point)
397 (setq calc-selection-cache-entry nil)
399 ;;; The following ensures that no two subformulas will be "eq" to each other!
400 (defun calc-encase-atoms (x)
401 (if (or (not (consp x))
402 (equal x '(float 0 0)))
404 (calc-encase-atoms-rec x)
408 (defun calc-encase-atoms-rec (x)
411 (if (eq (car x) 'intv)
413 (while (setq x (cdr x))
414 (if (or (not (consp (car x)))
415 (equal (car x) '(float 0 0)))
416 (setcar x (list 'cplx (car x) 0))
417 (calc-encase-atoms-rec (car x))))))
420 (defun calc-find-selected-part ()
421 (let* ((math-comp-sel-hpos (- (current-column) calc-selection-cache-offset))
425 (math-comp-sel-vpos (save-excursion
427 (let ((line (point)))
428 (calc-cursor-stack-index
429 calc-selection-cache-num)
431 (while (< (point) line)
433 (setq spaces (+ spaces
434 (current-indentation))
436 (- lcount (math-comp-ascent
437 calc-selection-cache-comp) -1))))
438 (math-comp-sel-cpos (- (point) toppt calc-selection-cache-offset
440 (math-comp-sel-tag nil))
441 (and (>= math-comp-sel-hpos 0)
442 (> calc-selection-true-num 0)
443 (math-composition-to-string calc-selection-cache-comp 1000000))
444 (nth 1 math-comp-sel-tag))
447 (defun calc-change-current-selection (sub-expr)
448 (or (eq sub-expr (nth 2 calc-selection-cache-entry))
449 (let ((calc-prepared-composition calc-selection-cache-comp)
450 (buffer-read-only nil)
452 (calc-set-command-flag 'renum-stack)
453 (setcar (nthcdr 2 calc-selection-cache-entry) sub-expr)
454 (calc-cursor-stack-index calc-selection-cache-num)
456 (calc-cursor-stack-index (1- calc-selection-cache-num))
457 (delete-region top (point))
458 (let ((calc-selection-cache-default-entry calc-selection-cache-entry))
459 (insert (math-format-stack-value calc-selection-cache-entry)
463 (defun calc-top-selected (&optional n m)
464 (and calc-any-selections
469 (calc-check-stack (+ n m -1))
470 (let ((top (nthcdr (+ m calc-stack-top -1) calc-stack))
472 (while (>= (setq n (1- n)) 0)
473 (if (nth 2 (car top))
474 (setq sel (if sel t (nth 2 (car top)))))
475 (setq top (cdr top)))
479 (defun calc-replace-sub-formula (expr old new)
480 (setq new (calc-encase-atoms new))
481 (calc-replace-sub-formula-rec expr)
484 (defun calc-replace-sub-formula-rec (expr)
485 (cond ((eq expr old) new)
486 ((Math-primp expr) expr)
489 (mapcar 'calc-replace-sub-formula-rec (cdr expr)))))
492 (defun calc-sel-error ()
493 (error "Illegal operation on sub-formulas")
496 (defun calc-replace-selections (n vals m)
497 (if (calc-top-selected n m)
498 (let ((num (length vals)))
499 (calc-preserve-point)
502 (let* ((old (calc-top-list n m 'entry))
507 (if (nth 2 (car old))
508 (setq val (calc-encase-atoms (car vals))
509 new (cons (calc-replace-sub-formula (car (car old))
514 (setq new (cons (car vals) new)
516 (setq vals (cdr vals)
518 (calc-pop-stack n m t)
519 (calc-push-list (nreverse new)
520 m (and calc-keep-selection (nreverse sel)))))
522 (let* ((old (calc-top-list n m 'entry))
524 (while (and old (not (nth 2 (car old))))
525 (setq old (cdr old)))
527 (while (and (setq more (cdr more)) (not (nth 2 (car more)))))
530 (calc-pop-stack n m t)
532 (let ((val (calc-encase-atoms (car vals))))
533 (calc-push-list (list (calc-replace-sub-formula
537 m (and calc-keep-selection (list val))))
538 (calc-push-list vals))))
539 (t (calc-sel-error))))
540 (calc-pop-stack n m t)
541 (calc-push-list vals m))
543 (setq calc-keep-selection t)
545 (defun calc-delete-selection (n)
546 (let ((entry (calc-top n 'entry)))
548 (if (eq (nth 2 entry) (car entry))
550 (calc-pop-stack 1 n t)
551 (calc-push-list '(0) n))
552 (let ((parent (calc-find-parent-formula (car entry) (nth 2 entry)))
554 (calc-preserve-point)
555 (calc-pop-stack 1 n t)
556 (cond ((or (memq (car parent) '(* / %))
557 (and (eq (car parent) '^)
558 (eq (nth 2 parent) (nth 2 entry))))
560 ((memq (car parent) '(vec calcFunc-min calcFunc-max)))
561 ((and (assq (car parent) calc-tweak-eqn-table)
562 (= (length parent) 3))
568 (calc-push-list (list
570 (calc-replace-sub-formula
573 (if (eq (nth 2 entry) (nth 1 parent))
578 (calc-push-list (list
580 (calc-replace-sub-formula (car entry)
585 (calc-push-list (list
587 (calc-replace-sub-formula (car entry)
593 (calc-pop-stack 1 n t)))
596 (defun calc-roll-down-with-selections (n m)
597 (let ((vals (append (calc-top-list m 1)
598 (calc-top-list (- n m) (1+ m))))
599 (sels (append (calc-top-list m 1 'sel)
600 (calc-top-list (- n m) (1+ m) 'sel))))
601 (calc-pop-push-list n vals 1 sels))
604 (defun calc-roll-up-with-selections (n m)
605 (let ((vals (append (calc-top-list (- n m) 1)
606 (calc-top-list m (- n m -1))))
607 (sels (append (calc-top-list (- n m) 1 'sel)
608 (calc-top-list m (- n m -1) 'sel))))
609 (calc-pop-push-list n vals 1 sels))
612 (defun calc-auto-selection (entry)
615 (and (boundp 'reselect) (setq reselect nil))
616 (calc-prepare-selection)
617 (calc-grow-assoc-formula (car entry) (calc-find-selected-part))))
620 (defun calc-copy-selection ()
623 (calc-preserve-point)
624 (let* ((num (max 1 (calc-locate-cursor-element (point))))
625 (entry (calc-top num 'entry)))
626 (calc-push (or (calc-auto-selection entry) (car entry)))))
629 (defun calc-del-selection ()
632 (calc-preserve-point)
633 (let* ((num (max 1 (calc-locate-cursor-element (point))))
634 (entry (calc-top num 'entry))
635 (sel (calc-auto-selection entry)))
636 (setcar (nthcdr 2 entry) (and (not (eq sel (car entry))) sel))
637 (calc-delete-selection num)))
640 (defun calc-enter-selection ()
643 (calc-preserve-point)
644 (let* ((num (max 1 (calc-locate-cursor-element (point))))
645 (reselect calc-keep-selection)
646 (entry (calc-top num 'entry))
648 (sel (or (calc-auto-selection entry) expr))
650 (let ((calc-dollar-values (list sel))
651 (calc-dollar-used 0))
652 (setq alg (calc-do-alg-entry "" "Replace selection with: "))
655 (setq alg (calc-encase-atoms (car alg)))
656 (calc-pop-push-record-list 1 "repl"
657 (list (calc-replace-sub-formula
660 (list (and reselect alg))))))
664 (defun calc-edit-selection ()
667 (calc-preserve-point)
668 (let* ((num (max 1 (calc-locate-cursor-element (point))))
669 (reselect calc-keep-selection)
670 (entry (calc-top num 'entry))
672 (sel (or (calc-auto-selection entry) expr))
674 (let ((str (math-showing-full-precision
675 (math-format-nice-expr sel (frame-width)))))
676 (calc-edit-mode (list 'calc-finish-selection-edit
677 num (list 'quote sel) reselect))
679 (calc-show-edit-buffer)
682 (defun calc-finish-selection-edit (num sel reselect)
683 (let ((buf (current-buffer))
684 (str (buffer-substring (point) (point-max)))
686 (switch-to-buffer calc-original-buffer)
687 (let ((val (math-read-expr str)))
688 (if (eq (car-safe val) 'error)
690 (switch-to-buffer buf)
691 (goto-char (+ start (nth 1 val)))
692 (error (nth 2 val))))
694 (calc-preserve-point)
696 (calc-trail-display 1 t))
697 (setq val (calc-encase-atoms (calc-normalize val)))
698 (let ((expr (calc-top num 'full)))
699 (if (calc-find-sub-formula expr sel)
700 (calc-pop-push-record-list 1 "edit"
701 (list (calc-replace-sub-formula
704 (list (and reselect val)))
706 (error "Original selection has been lost"))))))
709 (defun calc-sel-evaluate (arg)
712 (calc-preserve-point)
713 (let* ((num (max 1 (calc-locate-cursor-element (point))))
714 (reselect calc-keep-selection)
715 (entry (calc-top num 'entry))
716 (sel (or (calc-auto-selection entry) (car entry))))
717 (calc-with-default-simplification
718 (let ((math-simplify-only nil))
719 (calc-modify-simplify-mode arg)
720 (let ((val (calc-encase-atoms (calc-normalize sel))))
721 (calc-pop-push-record-list 1 "jsmp"
722 (list (calc-replace-sub-formula
723 (car entry) sel val))
725 (list (and reselect val))))))
729 (defun calc-sel-expand-formula (arg)
732 (calc-preserve-point)
733 (let* ((num (max 1 (calc-locate-cursor-element (point))))
734 (reselect calc-keep-selection)
735 (entry (calc-top num 'entry))
736 (sel (or (calc-auto-selection entry) (car entry))))
737 (calc-with-default-simplification
738 (let ((math-simplify-only nil))
739 (calc-modify-simplify-mode arg)
740 (let* ((math-expand-formulas (> arg 0))
741 (val (calc-normalize sel))
744 (setq top (math-expand-formula val))
745 (setq val (calc-normalize top)))
746 (setq val (calc-encase-atoms val))
747 (calc-pop-push-record-list 1 "jexf"
748 (list (calc-replace-sub-formula
749 (car entry) sel val))
751 (list (and reselect val))))))
755 (defun calc-sel-mult-both-sides (no-simp &optional divide)
758 (calc-preserve-point)
759 (let* ((num (max 1 (calc-locate-cursor-element (point))))
760 (reselect calc-keep-selection)
761 (entry (calc-top num 'entry))
763 (sel (or (calc-auto-selection entry) expr))
764 (func (car-safe sel))
766 (setq alg (calc-with-default-simplification
767 (car (calc-do-alg-entry ""
769 "Divide both sides by: "
770 "Multiply both sides by: ")))))
773 (if (and (or (eq func '/)
774 (assq func calc-tweak-eqn-table))
777 (or (memq func '(/ calcFunc-eq calcFunc-neq))
778 (if (math-known-nonposp alg)
780 (setq func (nth 1 (assq func
781 calc-tweak-eqn-table)))
782 (or (math-known-negp alg)
783 (message "Assuming this factor is nonzero")))
784 (or (math-known-posp alg)
785 (if (math-known-nonnegp alg)
786 (message "Assuming this factor is nonzero")
787 (message "Assuming this factor is positive")))))
788 (setq lhs (list (if divide '/ '*) (nth 1 sel) alg)
789 rhs (list (if divide '/ '*) (nth 2 sel) alg))
792 (setq lhs (math-simplify lhs)
793 rhs (math-simplify rhs))
795 (or (Math-equal (nth 1 sel) 1)
796 (Math-equal (nth 1 sel) -1)
797 (and (memq (car-safe (nth 2 sel)) '(+ -))
798 (memq (car-safe alg) '(+ -))))
799 (setq rhs (math-expand-term rhs)))))
800 (setq alg (calc-encase-atoms
801 (calc-normalize (list func lhs rhs)))))
802 (setq rhs (list (if divide '* '/) sel alg))
804 (setq rhs (math-simplify rhs)))
805 (setq alg (calc-encase-atoms
806 (calc-normalize (if divide
808 (list '* alg rhs))))))
809 (calc-pop-push-record-list 1 (if divide "div" "mult")
810 (list (calc-replace-sub-formula
813 (list (and reselect alg)))))
817 (defun calc-sel-div-both-sides (no-simp)
819 (calc-sel-mult-both-sides no-simp t)
822 (defun calc-sel-add-both-sides (no-simp &optional subtract)
825 (calc-preserve-point)
826 (let* ((num (max 1 (calc-locate-cursor-element (point))))
827 (reselect calc-keep-selection)
828 (entry (calc-top num 'entry))
830 (sel (or (calc-auto-selection entry) expr))
831 (func (car-safe sel))
833 (setq alg (calc-with-default-simplification
834 (car (calc-do-alg-entry ""
836 "Subtract from both sides: "
837 "Add to both sides: ")))))
840 (if (and (assq func calc-tweak-eqn-table)
843 (setq lhs (list (if subtract '- '+) (nth 1 sel) alg)
844 rhs (list (if subtract '- '+) (nth 2 sel) alg))
846 (setq lhs (math-simplify lhs)
847 rhs (math-simplify rhs)))
848 (setq alg (calc-encase-atoms
849 (calc-normalize (list func lhs rhs)))))
850 (setq rhs (list (if subtract '+ '-) sel alg))
852 (setq rhs (math-simplify rhs)))
853 (setq alg (calc-encase-atoms
854 (calc-normalize (list (if subtract '- '+) alg rhs)))))
855 (calc-pop-push-record-list 1 (if subtract "sub" "add")
856 (list (calc-replace-sub-formula
859 (list (and reselect alg)))))
863 (defun calc-sel-sub-both-sides (no-simp)
865 (calc-sel-add-both-sides no-simp t)