Mercurial > hg > xemacs-beta
diff lisp/vm/tapestry.el @ 0:376386a54a3c r19-14
Import from CVS: tag r19-14
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:45:50 +0200 |
parents | |
children | 859a2309aef8 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/vm/tapestry.el Mon Aug 13 08:45:50 2007 +0200 @@ -0,0 +1,565 @@ +;;; Tools to configure your GNU Emacs windows +;;; Copyright (C) 1991, 1993, 1994, 1995 Kyle E. Jones +;;; +;;; This program 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 1, or (at your option) +;;; any later version. +;;; +;;; This program 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. +;;; +;;; A copy of the GNU General Public License can be obtained from this +;;; program's author (send electronic mail to kyle@uunet.uu.net) or from +;;; the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA +;;; 02139, USA. +;;; +;;; Send bug reports to kyle@uunet.uu.net. + +(provide 'tapestry) + +(defvar tapestry-version "1.07") + +(defun tapestry (&optional frame-list) + "Returns a list containing complete information about the current +configuration of Emacs frames, windows, buffers and cursor +positions. Call the function set-tapestry with the list that this function +returns to restore the configuration. + +Optional first arg FRAME-LIST should be a list of frames; only +configuration information about these frames will be returned. + +The configuration information is returned in a form that can be saved and +restored across multiple Emacs sessions." + (let ((frames (or frame-list (tapestry-frame-list))) + (frame-map (tapestry-frame-map)) + (sf (tapestry-selected-frame)) + (other-maps nil)) + (unwind-protect + (while frames + (tapestry-select-frame (car frames)) + (setq other-maps (cons (list (tapestry-window-map) + (tapestry-buffer-map) + (tapestry-position-map)) + other-maps) + frames (cdr frames))) + (tapestry-select-frame sf)) + (list frame-map other-maps))) + +(defun set-tapestry (map &optional n root-window-edges) + "Restore the frame/window/buffer configuration described by MAP, +which should be a list previously returned by a call to +tapestry. + +Optional second arg N causes frame reconfiguration to be skipped +and the windows of the current frame will configured according to +the window map of the Nth frame in MAP. + +Optional third arg ROOT-WINDOW-EDGES non-nil should be a list +containing the edges of a window in the current frame. This list +should be in the same form as returned by the `window-edges' +function. The window configuration from MAP will be restored in +this window. If no window with these exact edges exists, a +window that lies entirely within the edge coordinates will be +expanded until the edge coordinates match or the window bounded by +ROOT-WINDOW-EDGES is entirely contained within the expanded +window. If no window entirely within the ROOT-WINDOW-EDGES edge +coordinates can be found, the window with the greatest overlap of +ROOT-WINDOW-EDGES will be used." + (let ((sf (tapestry-selected-frame)) + frame-list frame-map other-maps other-map) + (setq frame-map (nth 0 map) + other-maps (nth 1 map)) + (if (and root-window-edges (null n)) + (setq n 1)) + (if n + (let (first-window) + (setq other-map (nth (1- n) other-maps)) + (if (null other-map) + (error "No such map, %d" n)) + (setq first-window + (tapestry-set-window-map (nth 0 other-map) root-window-edges)) + (tapestry-set-buffer-map (nth 1 other-map) first-window) + (tapestry-set-position-map (nth 2 other-map) first-window)) + (tapestry-set-frame-map frame-map) + ;; frame list is reversed relative to the map order because + ;; created frames are added to the head of the list instead + ;; of the tail. + (setq frame-list (nreverse (tapestry-frame-list))) + (unwind-protect + (while other-maps + (tapestry-select-frame (car frame-list)) + (tapestry-set-window-map (nth 0 (car other-maps))) + (tapestry-set-buffer-map (nth 1 (car other-maps))) + (tapestry-set-position-map (nth 2 (car other-maps))) + (setq other-maps (cdr other-maps) + frame-list (cdr frame-list))) + (and (tapestry-frame-live-p sf) (tapestry-select-frame sf)))))) + +(defun tapestry-frame-map () + (let ((map (mapcar 'tapestry-frame-parameters (tapestry-frame-list))) + list cell frame-list) + (setq list map + frame-list (tapestry-frame-list)) + (while list + (setq cell (assq 'minibuffer (car list))) + (if (and cell (windowp (cdr cell))) + (if (eq (tapestry-window-frame (cdr cell)) (car frame-list)) + (setcdr cell t) + (setcdr cell 'none))) + (setq list (cdr list) + frame-list (cdr frame-list))) + map )) + +(defun tapestry-set-frame-map (map) + ;; some parameters can only be set only at frame creation time. + ;; so all existing frames must die. + (let ((doomed-frames (tapestry-frame-list))) + (while map + (tapestry-make-frame (car map)) + (setq map (cdr map))) + (while doomed-frames + (tapestry-delete-frame (car doomed-frames)) + (setq doomed-frames (cdr doomed-frames))))) + +(defun tapestry-window-map () + (let (maps map0 map1 map0-edges map1-edges x-unchanged y-unchanged) + (setq maps (mapcar 'tapestry-window-edges (tapestry-window-list))) + (while (cdr maps) + (setq map0 maps) + (while (cdr map0) + (setq map1 (cdr map0) + map0-edges (tapestry-find-window-map-edges (car map0)) + map1-edges (tapestry-find-window-map-edges (car map1)) + x-unchanged (and (= (car map0-edges) (car map1-edges)) + (= (nth 2 map0-edges) (nth 2 map1-edges))) + y-unchanged (and (= (nth 1 map0-edges) (nth 1 map1-edges)) + (= (nth 3 map0-edges) (nth 3 map1-edges)))) + (cond ((and (not x-unchanged) (not y-unchanged)) + (setq map0 (cdr map0))) + ((or (and x-unchanged (eq (car (car map0)) '-)) + (and y-unchanged (eq (car (car map0)) '|))) + (nconc (car map0) (list (car map1))) + (setcdr map0 (cdr map1))) + (t + (setcar map0 (list (if x-unchanged '- '|) + (car map0) + (car map1))) + (setcdr map0 (cdr map1)))))) + (car maps))) + +(defun tapestry-set-window-map (map &optional root-window-edges) + (let ((map-width (tapestry-compute-map-width map)) + (map-height (tapestry-compute-map-height map)) + (root-window nil)) + (if root-window-edges + (let (w-list w-edges w-area + exact-w inside-w overlap-w max-overlap overlap) + (while (null root-window) + (setq exact-w nil + inside-w nil + overlap-w nil + max-overlap -1 + w-list (tapestry-window-list)) + (while w-list + (setq w-edges (tapestry-window-edges (car w-list)) + w-area (tapestry-window-area w-edges)) + (if (equal w-edges root-window-edges) + (setq exact-w (car w-list) + w-list nil) + (setq overlap (tapestry-window-overlap w-edges + root-window-edges) + overlap (if overlap (tapestry-window-area overlap) 0) + w-area (tapestry-window-area w-edges)) + (if (< max-overlap overlap) + (setq max-overlap overlap + overlap-w (car w-list))) + ;; set inside-w each time we find a window inside + ;; the root window edges. FSF Emacs gives space + ;; to the window above or to the left if there is + ;; such a window. therefore we want to find the + ;; inside window that is bottom-most or right-most so that + ;; when we delete it, its space will be given to + ;; what will be the root window. + (if (= w-area overlap) + (setq inside-w (car w-list))) + (setq w-list (cdr w-list)))) + (cond (exact-w (setq root-window exact-w)) + (inside-w + ;; how could a window be inside the root window + ;; edges and there only be one window? a + ;; multi-line minibuffer, that's how! + (if (not (one-window-p t)) + (delete-window inside-w))) + (t (setq root-window overlap-w)))) + (tapestry-apply-window-map map map-width map-height root-window) + root-window ) + (if (tapestry-windows-match-map map map-width map-height) + (tapestry-first-window) + (delete-other-windows) + (setq root-window (selected-window)) + (tapestry-apply-window-map map map-width map-height root-window) + root-window )))) + +(defun tapestry-buffer-map () + (let ((w-list (tapestry-window-list)) + b list) + (while w-list + (setq b (window-buffer (car w-list)) + list (cons (list (buffer-file-name b) + (buffer-name b)) + list) + w-list (cdr w-list))) + (nreverse list))) + +(defun tapestry-set-buffer-map (buffer-map &optional first-window) + (let ((w-list (tapestry-window-list first-window)) wb) + (while (and w-list buffer-map) + (setq wb (car buffer-map)) + (set-window-buffer + (car w-list) + (if (car wb) + (or (get-file-buffer (car wb)) + (find-file-noselect (car wb))) + (get-buffer-create (nth 1 wb)))) + (setq w-list (cdr w-list) + buffer-map (cdr buffer-map))))) + +(defun tapestry-position-map () + (let ((sw (selected-window)) + (w-list (tapestry-window-list)) + list) + (while w-list + (setq list (cons (list (window-start (car w-list)) + (window-point (car w-list)) + (window-hscroll (car w-list)) + (eq (car w-list) sw)) + list) + w-list (cdr w-list))) + (nreverse list))) + +(defun tapestry-set-position-map (position-map &optional first-window) + (let ((w-list (tapestry-window-list first-window)) + (osw (selected-window)) + sw p) + (while (and w-list position-map) + (setq p (car position-map)) + (and (car p) (set-window-start (car w-list) (car p))) + (and (nth 1 p) (set-window-point (car w-list) (nth 1 p))) + (and (nth 2 p) (set-window-hscroll (car w-list) (nth 2 p))) + (and (nth 3 p) (setq sw (car w-list))) + ;; move this buffer up in the buffer-list + (select-window (car w-list)) + (setq w-list (cdr w-list) + position-map (cdr position-map))) + (select-window (or sw osw)))) + +(defun tapestry-apply-window-map (map map-width map-height current-window + &optional + root-window-width + root-window-height) + (let ((window-min-height 1) + (window-min-width 1) + horizontal) + (if (null root-window-width) + (setq root-window-height (window-height current-window) + root-window-width (window-width current-window))) + (while map + (cond + ((numberp (car map)) (setq map nil)) + ((eq (car map) '-) (setq horizontal nil)) + ((eq (car map) '|) (setq horizontal t)) + (t + (if (cdr map) + (split-window + current-window + (if horizontal + (/ (* (tapestry-compute-map-width (car map)) + root-window-width) + map-width) + (/ (* (tapestry-compute-map-height (car map)) + root-window-height) + map-height)) + horizontal)) + (if (not (numberp (car (car map)))) + (setq current-window + (tapestry-apply-window-map (car map) + map-width map-height + current-window + root-window-width + root-window-height))) + (and (cdr map) (setq current-window (next-window current-window 0))))) + (setq map (cdr map))) + current-window )) + +(defun tapestry-windows-match-map (map + &optional + map-width map-height + window-map + window-map-width + window-map-height) + (or map-width + (setq map-width (tapestry-compute-map-width map) + map-height (tapestry-compute-map-height map))) + (or window-map + (setq window-map (tapestry-window-map) + window-map-height (tapestry-compute-map-height window-map) + window-map-width (tapestry-compute-map-width window-map))) + (let ((result t)) + (cond ((numberp (car map)) + (and (numberp (car window-map)) + (= (/ (* (nth 0 map) window-map-width) + map-width) + (nth 0 window-map)) + (= (/ (* (nth 1 map) window-map-height) + map-height) + (nth 1 window-map)) + (= (/ (* (nth 2 map) window-map-width) + map-width) + (nth 2 window-map)) + (= (/ (* (nth 3 map) window-map-height) + map-height) + (nth 3 window-map)))) + ((eq (car map) '-) + (if (not (eq (car window-map) '-)) + nil + (setq map (cdr map) + window-map (cdr window-map)) + (while (and result map window-map) + (setq result (tapestry-windows-match-map (car map) + map-width + map-height + (car window-map) + window-map-width + window-map-height) + map (cdr map) + window-map (cdr window-map))) + (and result (null map) (null window-map)))) + ((eq (car map) '|) + (if (not (eq (car window-map) '|)) + nil + (setq map (cdr map) + window-map (cdr window-map)) + (while (and result map window-map) + (setq result (tapestry-windows-match-map (car map) + map-width + map-height + (car window-map) + window-map-width + window-map-height) + map (cdr map) + window-map (cdr window-map))) + (and result (null map) (null window-map))))))) + +(defun tapestry-find-window-map-edges (map) + (let (nw-edges se-edges) + (setq nw-edges map) + (while (and (consp nw-edges) (not (numberp (car nw-edges)))) + (setq nw-edges (car (cdr nw-edges)))) + (setq se-edges map) + (while (and (consp se-edges) (not (numberp (car se-edges)))) + (while (cdr se-edges) + (setq se-edges (cdr se-edges))) + (setq se-edges (car se-edges))) + (if (eq nw-edges se-edges) + nw-edges + (setq nw-edges (copy-sequence nw-edges)) + (setcdr (nthcdr 1 nw-edges) (nthcdr 2 se-edges)) + nw-edges ))) + +(defun tapestry-compute-map-width (map) + (let ((edges (tapestry-find-window-map-edges map))) + (- (nth 2 edges) (car edges)))) + +(defun tapestry-compute-map-height (map) + (let ((edges (tapestry-find-window-map-edges map))) + (- (nth 3 edges) (nth 1 edges)))) + +;; delq is to memq as delassq is to assq +(defun tapestry-delassq (elt list) + (let ((prev nil) + (curr list)) + (while curr + (if (eq elt (car (car curr))) + (if (null prev) + (setq list (cdr list) curr list) + (setcdr prev (cdr curr)) + (setq curr (cdr curr))) + (setq prev curr curr (cdr curr)))) + list )) + +(defun tapestry-remove-frame-parameters (map params) + (let (frame-map) + (while params + (setq frame-map (nth 0 map)) + (while frame-map + (setcar frame-map (tapestry-delassq (car params) (car frame-map))) + (setq frame-map (cdr frame-map))) + (setq params (cdr params))))) + +(defun tapestry-nullify-tapestry-elements (map &optional buf-file-name buf-name + window-start window-point + window-hscroll selected-window) + (let (p) + (setq map (nth 1 map)) + (while map + (setq p (nth 1 (car map))) + (while p + (and buf-file-name (setcar (car p) nil)) + (and buf-name (setcar (cdr (car p)) nil)) + (setq p (cdr p))) + (setq p (nth 2 (car map))) + (while p + (and window-start (setcar (car p) nil)) + (and window-point (setcar (cdr (car p)) nil)) + (and window-hscroll (setcar (nthcdr 2 (car p)) nil)) + (and selected-window (setcar (nthcdr 3 (car p)) nil)) + (setq p (cdr p))) + (setq map (cdr map))))) + +(defun tapestry-replace-tapestry-element (map what function) + (let (mapi mapj p old new) + (cond ((eq what 'buffer-file-name) + (setq mapi 1 mapj 0)) + ((eq what 'buffer-name) + (setq mapi 1 mapj 1)) + ((eq what 'window-start) + (setq mapi 2 mapj 0)) + ((eq what 'window-point) + (setq mapi 2 mapj 1)) + ((eq what 'window-hscroll) + (setq mapi 2 mapj 2)) + ((eq what 'selected-window) + (setq mapi 2 mapj 3))) + (setq map (nth 1 map)) + (while map + (setq p (nth mapi (car map))) + (while p + (setq old (nth mapj (car p)) + new (funcall function old)) + (if (not (equal old new)) + (setcar (nthcdr mapj (car p)) new)) + (setq p (cdr p))) + (setq map (cdr map))))) + +(defun tapestry-window-list (&optional first-window) + (let* ((first-window (or first-window (tapestry-first-window))) + (windows (cons first-window nil)) + (current-cons windows) + (w (next-window first-window 'nomini))) + (while (not (eq w first-window)) + (setq current-cons (setcdr current-cons (cons w nil))) + (setq w (next-window w 'nomini))) + windows )) + +(defun tapestry-first-window () + (if (eq (tapestry-selected-frame) + (tapestry-window-frame (minibuffer-window))) + (next-window (minibuffer-window)) + (let ((w (selected-window)) + (top (or (cdr (assq 'menu-bar-lines (tapestry-frame-parameters))) 0)) + edges) + (while (or (not (= 0 (car (setq edges (tapestry-window-edges w))))) + ;; >= instead of = because in FSF Emacs 19.2x + ;; (whenever the Lucid menubar code was added) the + ;; menu-bar-lines frame parameter == 1 when the + ;; Lucid menubar is enabled even though the + ;; menubar doesn't steal the first line from the + ;; window. + (not (>= top (nth 1 edges)))) + (setq w (next-window w 'nomini))) + w ))) + +(defun tapestry-window-area (edges) + (* (- (nth 3 edges) (nth 1 edges)) + (- (nth 2 edges) (nth 0 edges)))) + +(defun tapestry-window-overlap (e0 e1) + (let (top left bottom right) + (cond ((and (<= (nth 0 e0) (nth 0 e1)) (< (nth 0 e1) (nth 2 e0))) + (setq left (nth 0 e1))) + ((and (<= (nth 0 e1) (nth 0 e0)) (< (nth 0 e0) (nth 2 e1))) + (setq left (nth 0 e0)))) + (cond ((and (< (nth 0 e0) (nth 2 e1)) (<= (nth 2 e1) (nth 2 e0))) + (setq right (nth 2 e1))) + ((and (< (nth 0 e1) (nth 2 e0)) (<= (nth 2 e0) (nth 2 e1))) + (setq right (nth 2 e0)))) + (cond ((and (<= (nth 1 e0) (nth 1 e1)) (< (nth 1 e1) (nth 3 e0))) + (setq top (nth 1 e1))) + ((and (<= (nth 1 e1) (nth 1 e0)) (< (nth 1 e0) (nth 3 e1))) + (setq top (nth 1 e0)))) + (cond ((and (< (nth 1 e0) (nth 3 e1)) (<= (nth 3 e1) (nth 3 e0))) + (setq bottom (nth 3 e1))) + ((and (< (nth 1 e1) (nth 3 e0)) (<= (nth 3 e0) (nth 3 e1))) + (setq bottom (nth 3 e0)))) + (and left top right bottom (list left top right bottom)))) + +(defun tapestry-window-edges (&optional window) + (if (fboundp 'window-pixel-edges) + (let ((edges (window-pixel-edges window)) + tmp) + (setq tmp edges) + (setcar tmp (/ (car tmp) (face-width 'default))) + (setq tmp (cdr tmp)) + (setcar tmp (/ (car tmp) (face-height 'default))) + (setq tmp (cdr tmp)) + (setcar tmp (/ (car tmp) (face-width 'default))) + (setq tmp (cdr tmp)) + (setcar tmp (/ (car tmp) (face-height 'default))) + edges ) + (window-edges window))) + +;; We call these functions instead of calling the Emacs 19 frame +;; functions directly to let this package work with v18 Emacs. + +(defun tapestry-frame-list () + (if (fboundp 'frame-list) + (frame-list) + (list nil))) + +(defun tapestry-frame-parameters (&optional f) + (if (fboundp 'frame-parameters) + (frame-parameters f) + nil )) + +(defun tapestry-window-frame (w) + (if (fboundp 'window-frame) + (window-frame w) + nil )) + +(defun tapestry-modify-frame-parameters (f alist) + (if (fboundp 'modify-frame-parameters) + (modify-frame-parameters f alist) + nil )) + +(defun tapestry-select-frame (f) + (if (fboundp 'select-frame) + (select-frame f) + nil )) + +(defun tapestry-selected-frame () + (if (fboundp 'selected-frame) + (selected-frame) + nil )) + +(defun tapestry-next-frame (&optional f all) + (if (fboundp 'next-frame) + (next-frame f all) + nil )) + +(defun tapestry-make-frame (&optional alist) + (if (fboundp 'make-frame) + (make-frame alist) + nil )) + +(defun tapestry-delete-frame (&optional f) + (if (fboundp 'delete-frame) + (delete-frame f) + nil )) + +(defun tapestry-frame-live-p (f) + (if (fboundp 'frame-live-p) + (frame-live-p f) + t ))