;;; window-layout.el --- window layout manager ;; Copyright (C) 2010-2017 SAKURAI Masashi ;; Author: SAKURAI Masashi ;; Version: 1.4 ;; Package-Version: 1.4 ;; Package-Commit: cd2e4f967b610c2bbef53182829e47250d027056 ;; Keywords: window, layout ;; 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 3 of the License, 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. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; Split a frame or window into some windows according to a layout ;; recipe. ;;; Example code ;; ;; Layout function ;; ; -> three pane layout. ;; (setq wm ; <-- window management object ;; (wlf:layout ;; '(| (:left-size-ratio 0.3) ;; folder ;; (- (:upper-max-size 15) ;; summary ;; message)) ;; '((:name folder ;; :buffer "folder buffer") ;; (:name summary ;; :buffer "summary buffer") ;; (:name message ;; :buffer "message buffer") ;; ))) ;; ;; Window controlling ;; (wlf:show wm 'summary) ;; (wlf:hide wm 'summary) ;; (wlf:toggle wm 'summary) ;; (wlf:select wm 'summary) ;; (wlf:toggle-maximize wm 'summary) ;; ;; Window updating ;; (wlf:refresh wm) ;; (wlf:reset-window-sizes wm) ;; (wlf:reset-init wm) ;; ;; Accessing a buffer ;; (wlf:get-buffer wm 'summary) -> <#buffer object> ;; (wlf:set-buffer wm 'summary "*scratch*") ;; ;; Accessing a window ;; (wlf:get-window wm 'summary) ;; ;; Layout hook ;; (defun wlf:test-hook (wset) (message "HOOK : %s" wset)) ;; (wlf:layout-hook-add wm 'wlf:test-hook) ;; (wlf:layout-hook-remove wm 'wlf:test-hook) ;;; `wlf:layout' function ;; * Layout recipe: ;; ( (split type) (split option) ;; (left window name or recipe) ;; (right window name or recipe) ) ;; - : split vertically ;; | : split horizontally ;; split option (the prefix 'left' can be replaced by 'right', 'upper' and 'lower'.) ;; :left-size (column or row number) window size ;; :left-max-size (column or row number) if window size is larger than this value, the window is shrunken. ;; :left-size-ratio (0.0 - 1.0) window size ratio. the size of the other side is the rest. ;; ;; Note: ;; The split option can be omitted. ;; The size parameters, :size, :max-size and :size-ratio, are mutually ;; exclusive. The size of a window is related with one of the other ;; side window. So, if both side windows set size parameters, the ;; window size may not be adjusted as you write. ;; * Window options: ;; :name [*] the window name. ;; :buffer a buffer name or a buffer object to show the window. If nil or omitted, the current buffer remains. If symbol, it is evaluated as a global variable. ;; :default-hide (t/nil) if t, the window is hided initially. (default: nil) ;; :fix-size (t/nil) if t, when the windows are laid out again, the window size is remained. (default: nil) ;; * subwindow-p option: ;; If this option is not nil, this function splits the windows within ;; the current window. If this option is nil or omitted, this function ;; uses the entire space of the current frame. Because some user ;; actions and complicated window layouts may cause unexpected split ;; behaviors, it is easy to use the entire space of a frame. ;; * Return value (Window management object): ;; You should not access the management object directly, because it is not ;; intended direct access. ;; You can make some management objects to switch the window layout. ;; * Layout hook ;; After splitting windows, registered hook are called with one ;; argument, the window management object. ;;; Code: (eval-when-compile (require 'cl)) (defmacro wlf:aif (test-form then-form &rest else-forms) (declare (debug (form form &rest form)) (indent 2)) `(let ((it ,test-form)) (if it ,then-form ,@else-forms))) (defmacro wlf:acond (&rest clauses) (declare (debug (&rest form))) (if (null clauses) nil (let ((cl1 (car clauses)) (sym (gensym))) `(let ((,sym ,(car cl1))) (if ,sym (let ((it ,sym)) ,@(cdr cl1)) (wlf:acond ,@(cdr clauses))))))) (defun wlf:current-first-line-point (window) "[internal] return the point at the beginning of the first line of the WINDOW." (when (windowp window) (with-selected-window window (with-current-buffer (window-buffer window) (save-excursion (move-to-window-line 0) (point)))))) (defstruct wlf:wset "Window-set management structure recipe : an input recipe object. winfo-list : a list of window management structures. wholep : if non nil, this function uses whole frame window. layout-hook : if doing layout windows, these hooks are called. The hook function has one argument: wset object." recipe winfo-list wholep layout-hook) (defstruct wlf:window "Window management structure name : a symbol of the window name. options : an option plist given by the recipe. shown : 'show/'hide. if 'hide, the window is not displayed. window : a window object. vertical : if the window is split vertically, the value is t. last-size : if the window is alive, the window size is saved before laying out. edges : a list of window edges returned by `window-edges'. first-line-point : the cursor position of the window. window-point : the scroll position of the window. (If more than 2 windows display the same buffer, the values of first-line-point and window-point may be different between windows.)" name options shown window vertical last-size edges first-line-point window-point) (defun wlf:window-shown-set (winfo i) "[internal] translate the argument: nil -> 'hide / t -> 'show" (setf (wlf:window-shown winfo) (if i 'show 'hide))) (defun wlf:window-shown-p (winfo) "[internal] Return t, if the window should be shown." (eq 'show (wlf:window-shown winfo))) (defun wlf:window-shown-toggle (winfo) "[internal] Toggle window displaying state." (setf (wlf:window-shown winfo) (if (and (wlf:window-shown-p winfo) (wlf:window-live-window winfo)) 'hide 'show))) (defun wlf:window-window-by-edge (winfo) "[internal] Return a window object which corresponding to WINFO. This function retrieves the window object from the edge position in current frame." (and (wlf:window-edges winfo) (destructuring-bind (left top right bottom) (wlf:window-edges winfo) (let ((swin (window-at (+ 2 left) (+ 2 top)))) (and swin (destructuring-bind (sl st sr sb) (window-edges swin) (if (and (equal left sl) (< (abs (- top st)) 3)) t (message "OLD:%S NEW:%S" (wlf:window-edges winfo) (window-edges swin)) nil)) swin))))) (defun wlf:window-live-window (winfo) "[internal] Return a window object if the window is not null and alive, return nil otherwise." (let ((win (wlf:window-window winfo))) (and (wlf:window-shown-p winfo) win (window-live-p win) win))) (defun wlf:window-size (winfo) "[internal] Return current window size." (let ((window (wlf:window-window winfo))) (cond ((wlf:window-vertical winfo) (window-height window)) (t (window-width window))))) (defmacro wlf:window-option-get (winfo option-key) "[internal] Return an option value from an option property list." `(plist-get (wlf:window-options ,winfo) ',option-key)) (defun wlf:clear-windows (winfo-list wholep) "[internal] Destroy windows and return the window object to start dividing." (cond (wholep ; using the whole area (delete-other-windows (get-largest-window)) (get-largest-window)) (t ; nested windows (let ((wins (loop for i in winfo-list for win = (wlf:window-live-window i) if win collect win))) (if (> (length wins) 1) (loop for w in (cdr wins) unless (one-window-p) do (delete-window w))) (or (car wins) (selected-window)))))) (defun wlf:get-winfo (name winfo-list) "[internal] Select a window info object from a winfo list." (wlf:aif (loop for i in winfo-list when (eq (wlf:window-name i) name) return i) it (error "Window name %s is not found." name))) (defun wlf:build-windows-rec (recipe winfo-list) "[internal] Split the selected window with the recipe." (let* ((split-type (car recipe)) (split-action (cond ((eq '- split-type) 'split-window-vertically) ((eq '| split-type) 'split-window-horizontally) (t 'split-window-vertically))) (verticalp (eq 'split-window-vertically split-action)) (split-options (cadr recipe)) (recipe-nodes (cddr recipe)) (former-recipe (car recipe-nodes)) (latter-recipe (cadr recipe-nodes)) (latter-window (funcall split-action)) (former-window (selected-window))) (unless (window-live-p former-window) (error "Can not create a window (former-window is not live)")) (select-window former-window) (when (and split-options (plist-get split-options ':leftp)) (wlf:apply-split-options split-options verticalp)) (if (symbolp former-recipe) (let ((winfo (wlf:get-winfo former-recipe winfo-list))) (unless (wlf:window-shown winfo) (wlf:window-shown-set winfo (null (wlf:window-option-get winfo :default-hide)))) (setf (wlf:window-window winfo) former-window) (setf (wlf:window-vertical winfo) verticalp) (wlf:apply-winfo winfo)) (wlf:build-windows-rec former-recipe winfo-list)) (unless (window-live-p latter-window) (error "Can not create a window (latter-window is not live.)")) (select-window latter-window) (when (and split-options (plist-get split-options ':rightp)) (wlf:apply-split-options split-options verticalp)) (if (symbolp latter-recipe) (let ((winfo (wlf:get-winfo latter-recipe winfo-list))) (unless (wlf:window-shown winfo) (wlf:window-shown-set winfo (null (wlf:window-option-get winfo :default-hide)))) (setf (wlf:window-window winfo) latter-window) (setf (wlf:window-vertical winfo) (eq 'split-window-vertically split-action)) (wlf:apply-winfo winfo)) (wlf:build-windows-rec latter-recipe winfo-list)) )) (defun wlf:apply-split-options (split-options verticalp) "[internal] Apply split options to the current window." (let ((size (if verticalp (window-height) (window-width)))) (condition-case err (wlf:acond ((plist-get split-options ':max-size) (if (< it size) (wlf:window-shrink (selected-window) verticalp (- size it)))) ((plist-get split-options ':size) (wlf:window-resize (selected-window) verticalp it)) ((plist-get split-options ':size-ratio) (wlf:window-resize (selected-window) verticalp (truncate (* 2 size it))))) (error (message "wlf:warning : %s" err))))) (defun wlf:window-shrink (window verticalp shrink-size) "[internal] Shrink window size." (cond (verticalp (shrink-window shrink-size)) (t (shrink-window-horizontally shrink-size)))) (defun wlf:window-resize (window verticalp target-size) "[internal] Resize window." (with-selected-window window (cond (verticalp (let ((current-size (window-height window))) (shrink-window (- current-size target-size)))) (t (let ((current-size (window-width window))) (shrink-window-horizontally (- current-size target-size))))))) (defun wlf:apply-winfo (winfo) "[internal] Apply layout options to the current window." (if (not (wlf:window-shown-p winfo)) (delete-window (selected-window)) (let ((buffer (wlf:aif (wlf:window-option-get winfo :buffer) (get-buffer (if (symbolp it) (symbol-value it) it))))) (when (buffer-live-p buffer) (set-window-buffer (selected-window) buffer))))) (defun wlf:set-window-points (winfo) "[internal] Set the scroll position and cursor one which are recorded in WINFO." (let ((window (wlf:window-window winfo))) (when (window-live-p window) (with-selected-window window (wlf:aif (wlf:window-first-line-point winfo) (progn ;;(message "WLF:set-window-points: [%s] first-line: %i" (wlf:window-name winfo) it) (goto-char it) (recenter 0))) ;; Go to previously saved window point if point is shown or ;; end of buffer is shown (therefore the point is shown). In ;; the latter case, window point (`it') is also at the EOB ;; (therefore `<' check does not work). (wlf:aif (wlf:window-window-point winfo) (progn (let ((win-last-point (window-end nil t))) ;;(message "WLF:set-window-points: [%s] window-point: %i last-point: %i" (wlf:window-name winfo) it win-last-point) (goto-char (if (or (< it win-last-point) (= (point-max) win-last-point)) it (1- win-last-point)))))))))) (defun wlf:collect-window-edges (winfo-list) "[internal] At the end of window laying out, this function is called to collect window edges." (loop for winfo in winfo-list if (wlf:window-live-window winfo) do (setf (wlf:window-edges winfo) (window-edges (wlf:window-window winfo))))) (defun wlf:calculate-last-window-sizes (winfo-list) "[internal] Calculate summations of the last window size: width and height. Return a cons cell, car is width and cdr is height." (loop for winfo in winfo-list with width = 0 with height = 0 for size = (wlf:window-last-size winfo) if size do (cond ((wlf:window-vertical winfo) (incf height size)) (t (incf width size))) finally return (cons width height))) (defun wlf:calculate-init-window-sizes (winfo-list) "[internal] Calculate summations of the initial window size: width and height. Return a cons cell, car is width and cdr is height." (loop for winfo in winfo-list with width = 0 with height = 0 for win = (wlf:window-live-window winfo) if win do (cond ((wlf:window-vertical winfo) (incf height (window-height win))) (t (incf width (window-width win)))) finally return (cons width height))) (defun wlf:restore-window-sizes (winfo-list) "[internal] Restore the window sizes those are modified by the user." ;;checking window layout modification (let* ((init-size (wlf:calculate-init-window-sizes winfo-list)) (last-size (wlf:calculate-last-window-sizes winfo-list)) (total-width (car init-size)) (total-height (cdr init-size)) (width-remainp (eql (car last-size) total-width)) (height-remainp (eql (cdr last-size) total-height))) ;;restore window size (loop for winfo in winfo-list for win = (wlf:window-live-window winfo) for to-size = (wlf:window-last-size winfo) for verticalp = (wlf:window-vertical winfo) do (when (and win to-size (null (wlf:window-option-get winfo :fix-size)) (if verticalp (and height-remainp (not (eql to-size total-height))) (and width-remainp (not (eql to-size total-width))))) (wlf:window-resize win verticalp to-size))))) (defun wlf:make-winfo-list (wparams) "[internal] Return a list of window info objects." (loop for p in wparams collect (make-wlf:window :name (plist-get p ':name) :options p))) (defun wlf:translate-recipe (recipe) "[internal] Translate split options recursively. :left-foo, :upper-foo --> :leftp t :foo :right-foo, :lower-foo --> :rightp t :foo " (if (or (symbolp recipe) (null recipe)) recipe (let* (split-options new-split-options (recipe-nodes (if (= 3 (length recipe)) (cdr recipe) (setq split-options (cadr recipe)) (cddr recipe)))) (when split-options (loop for i in split-options do (if (symbolp i) (let* ((label-name (symbol-name i))) (wlf:acond ((string-match ":\\(left\\|upper\\)-" label-name) (setq label-name (concat ":" (substring label-name (match-end 0)))) (push ':leftp new-split-options) (push t new-split-options)) ((string-match ":\\(right\\|lower\\)-" label-name) (setq label-name (concat ":" (substring label-name (match-end 0)))) (push ':rightp new-split-options) (push t new-split-options))) (push (intern label-name) new-split-options)) (push i new-split-options)))) (list (car recipe) (nreverse new-split-options) (wlf:translate-recipe (car recipe-nodes)) (wlf:translate-recipe (cadr recipe-nodes)))))) (defun wlf:max-window-size-p (winfo) "[internal] If current window size is equal to the frame size (maximum window size), return t. Otherwise return nil." (let ((wsize (wlf:window-size winfo))) (cond ((wlf:window-vertical winfo) (>= wsize (1- (frame-height)))) (t (>= wsize (1- (frame-width))))))) (defun wlf:save-current-window-sizes (recipe winfo-list) "[internal] Save current window sizes and points, before clearing the windows. The saved sizes are used at `wlf:restore-window-sizes'. The saved points are used in `wlf:apply-winfo'." (loop for winfo in winfo-list do (setf (wlf:window-last-size winfo) nil)) (wlf:aif (frame-parameter (selected-frame) 'wlf:recipe) (if (equal recipe it) (loop for winfo in winfo-list do (let ((win (wlf:window-live-window winfo))) (setf (wlf:window-last-size winfo) (and win (wlf:max-window-size-p winfo) (wlf:window-size winfo)) (wlf:window-window-point winfo) (and win (window-point win)) (wlf:window-first-line-point winfo) (wlf:current-first-line-point win)))))) (set-frame-parameter (selected-frame) 'wlf:recipe recipe)) (defun wlf:layout (recipe window-params &optional subwindow-p) "Lay out windows and return a management object. RECIPE is a structure of splitting windows. WINDOW-PARAMS is a list of the window layout parameters. If SUBWINDOW-P is nil, this function uses the entire space of the current frame. If SUBWINDOW-P is non-nil, this function splits the windows within the current window. See the comment text to know the further information about parameters. " (wlf:layout-internal (wlf:no-layout recipe window-params subwindow-p))) (defun wlf:no-layout (recipe window-params &optional subwindow-p) "Just return a management object, does not change window layout. See the comment of `wlf:layout' function for arguments." (make-wlf:wset :recipe (wlf:translate-recipe recipe) :winfo-list (wlf:make-winfo-list window-params) :wholep (not subwindow-p))) (defmacro wlf:with-wset (wset &rest body) "Define local variables: recipe, winfo-list, wholep, layout-hook." (declare (debug (symbolp &rest form)) (indent 1)) `(let* ((recipe (wlf:wset-recipe wset)) (winfo-list (wlf:wset-winfo-list wset)) (wholep (wlf:wset-wholep wset)) (layout-hook (wlf:wset-layout-hook wset))) ,@body)) (defun wlf:layout-internal (wset &optional restore-window-size) "[internal] Lay out windows and return a management object. If RESTORE-WINDOW-SIZE is not nil, this function does not restore the current window size which can be modified by users." (wlf:maximize-info-clear) (wlf:with-wset wset (let ((last-wname (wlf:get-window-name wset (selected-window))) (last-buffer (current-buffer)) val) (wlf:save-current-window-sizes recipe winfo-list) (select-window (wlf:clear-windows winfo-list wholep)) (wlf:build-windows-rec recipe winfo-list) (mapc #'wlf:set-window-points winfo-list) (unless restore-window-size (wlf:restore-window-sizes winfo-list)) (wlf:collect-window-edges winfo-list) (setq val (make-wlf:wset :recipe recipe :winfo-list winfo-list :wholep wholep)) (loop for h in (wlf:wset-layout-hook wset) do (funcall h wset)) (wlf:aif (or ;; Try to find active window by window name first: (ignore-errors (wlf:get-window wset last-wname)) ;; If not found, select the same buffer as before: (get-buffer-window last-buffer)) (select-window it)) val))) (defun wlf:layout-hook-add (wset func) "Add FUNC to layout-hook of the WSET, and return the layout-hook. The function FUNC should have one argument : wset object." (let ((hook (wlf:wset-layout-hook wset))) (unless (member func hook) (setf (wlf:wset-layout-hook wset) (cons func hook))) (wlf:wset-layout-hook wset))) (defun wlf:layout-hook-remove (wset func) "Remove FUNC from layout-hook of the WSET, and return the layout-hook." (let ((hook (wlf:wset-layout-hook wset))) (when (member func hook) (setf (wlf:wset-layout-hook wset) (remove func hook))) (wlf:wset-layout-hook wset))) (defun wlf:refresh (wset) "Refresh the window layout. WSET is a management object which is returned by `wlf:layout'." (wlf:layout-internal wset)) (defun wlf:reset-window-sizes (wset) "Reset the window sizes by window recipe parameters." (wlf:layout-internal wset t)) (defun wlf:reset-init (wset) "Reset the window sizes and display statuses by window recipe parameters." (loop for winfo in (wlf:wset-winfo-list wset) do (setf (wlf:window-shown winfo) nil)) (wlf:layout-internal wset t)) (defun wlf:show (wset &rest winfo-names) "Display the window. WSET is the management object which is returned by `wlf:layout'. WINFO-NAME is the window name which is defined by the argument of `wlf:layout'." (loop for wn in winfo-names do (wlf:window-shown-set (wlf:get-winfo wn (wlf:wset-winfo-list wset)) t)) (wlf:layout-internal wset)) (defun wlf:hide (wset &rest winfo-names) "Hide the window. WSET is the management object which is returned by `wlf:layout'. WINFO-NAME is the window name which is defined by the argument of `wlf:layout'." (loop for wn in winfo-names do (wlf:window-shown-set (wlf:get-winfo wn (wlf:wset-winfo-list wset)) nil)) (wlf:layout-internal wset)) (defun wlf:toggle (wset &rest winfo-names) "Toggle the window. WSET is the management object which is returned by `wlf:layout'. WINFO-NAME is the window name which is defined by the argument of `wlf:layout'." (loop for wn in winfo-names do (wlf:window-shown-toggle (wlf:get-winfo wn (wlf:wset-winfo-list wset)))) (wlf:layout-internal wset)) (defun wlf:select (wset winfo-name) "Select the indicated window. WSET is the management object which is returned by `wlf:layout'. WINFO-NAME is the window name which is defined by the argument of `wlf:layout'. If the window is nil or deleted, no window is selected." (wlf:aif (wlf:window-live-window (wlf:get-winfo winfo-name (wlf:wset-winfo-list wset))) (select-window it))) (defun wlf:get-window (wset winfo-name) "Return the indicated window. WSET is the management object which is returned by `wlf:layout'. WINFO-NAME is the window name which is defined by the argument of `wlf:layout'. If the window is nil or deleted, return nil. Note that after the other window configuration is applied by `set-window-configuration', this function may return nil because the window configured by wlf is not alive. Since many functions calls `set-window-configuration', programs depend on `wlf:get-window' should watch invocations of `set-window-configuration'. See also `wlf:get-window-name'. It is approximately the inverse function of `wlf:get-window'. `wlf:get-window-name' converts window to window name." (wlf:window-live-window (wlf:get-winfo winfo-name (wlf:wset-winfo-list wset)))) (defun wlf:set-buffer (wset winfo-name buf &optional selectp) "Set the buffer on the window. WSET is the management object which is returned by `wlf:layout'. WINFO-NAME is the window name which is defined by the argument of `wlf:layout'. BUF is a buffer name or object to show in the window." (when (stringp buf) (setq buf (get-buffer buf))) (let* ((winfo (wlf:get-winfo winfo-name (wlf:wset-winfo-list wset))) (window (wlf:window-live-window winfo)) (curwin (selected-window))) (unless (buffer-live-p buf) (error "Buffer is dead. at wlf:set-buffer. (%s)" winfo-name)) (plist-put (wlf:window-options winfo) :buffer buf) (when (and window (not (eql (get-buffer buf) (window-buffer window)))) (set-window-buffer window buf)) (cond (selectp (select-window window)) ((active-minibuffer-window) (select-window (minibuffer-window)))) window)) (defun wlf:get-buffer (wset winfo-name) "Return the buffer object on the window. This function uses the structure data, not currently displayed window. WSET is the management object which is returned by `wlf:layout'. WINFO-NAME is the window name which is defined by the argument of `wlf:layout'." (wlf:window-option-get (wlf:get-winfo winfo-name (wlf:wset-winfo-list wset)) :buffer)) (defun wlf:window-name-p (wset winfo-name) "Return t if WINFO-NAME exists in WSET. Otherwise return nil." (if (wlf:get-winfo winfo-name (wlf:wset-winfo-list wset)) t nil)) (defun wlf:window-displayed-p (wset winfo-name) "Return t if the window of WINFO-NAME is displayed. Otherwise return nil." (wlf:aif (wlf:get-winfo winfo-name (wlf:wset-winfo-list wset)) (wlf:window-shown-p it) nil)) (defun wlf:wopts-replace-buffer (wopts buffer-alist) "Helper function for the argument of `wlf:layout'. This function replaces or adds buffer objects in the window options. WOPTS is a window option list. BUFFER-ALIST is an alist of pairs of a window name and a buffer object (or buffer name)." (loop for pair in buffer-alist for name = (car pair) for buf = (cdr pair) for opts = (loop for i in wopts if (eq (plist-get i ':name) name) return i) do (plist-put opts ':buffer buf)) wopts) (defun wlf:copy-windows (wset) "Return a copied wset object for `set-window-configuration' hacking." (make-wlf:wset :recipe (wlf:wset-recipe wset) :winfo-list (loop for i in (wlf:wset-winfo-list wset) collect (wlf:copy-winfo i)) :wholep (wlf:wset-wholep wset) :layout-hook (wlf:wset-layout-hook wset))) (defun wlf:copy-winfo (winfo) "[internal] Return a shallow copied window object." (make-wlf:window :name (wlf:window-name winfo) :options (copy-sequence (wlf:window-options winfo)) :shown (wlf:window-shown winfo) :window (wlf:window-window winfo) :vertical (wlf:window-vertical winfo) :last-size (wlf:window-last-size winfo) :edges (wlf:window-edges winfo))) (defun wlf:maximize-info-get () "[internal] Return toggle-maximize info." (frame-parameter (selected-frame) 'wlf:maximize)) (defun wlf:maximize-info-set (val) "[internal] Set toggle-maximize info." (set-frame-parameter (selected-frame) 'wlf:maximize val)) (defun wlf:maximize-info-clear () "[internal] Clear toggle-maximize info." (wlf:maximize-info-set nil)) (defun wlf:collect-window-states (wset) "[internal] Collect current window states." (loop for winfo in (wlf:wset-winfo-list wset) collect (cons (wlf:window-name winfo) (wlf:window-shown-p winfo)))) (defun wlf:revert-window-states (wset states) "[internal] Revert window states whose are collected by `wlf:collect-window-states'." (loop for s in states with winfo-list = (wlf:wset-winfo-list wset) for wname = (car s) for winfo = (wlf:get-winfo wname winfo-list) do (wlf:window-shown-set winfo (cdr s)))) (defun wlf:maximize-window-states (wset winfo-name) "[internal] Set show state at the WINFO-NAME window, set hide state at the other windows." (loop for winfo in (wlf:wset-winfo-list wset) if (eq (wlf:window-name winfo) winfo-name) do (wlf:window-shown-set winfo t) else do (wlf:window-shown-set winfo nil))) (defun wlf:toggle-maximize (wset winfo-name) "Toggle WINFO-NAME window maximizing." ;; structure : (wlf:maximize maximized-winfo-name states-list) (let ((prev-states (wlf:maximize-info-get)) (current-states (wlf:collect-window-states wset)) next-states) (cond (prev-states ; now maximizing (cond ((eq winfo-name (cadr prev-states)) ; revert windows (wlf:revert-window-states wset (caddr prev-states))) (t ; maximize other window (setf (cadr prev-states) winfo-name) (setq next-states prev-states) (wlf:maximize-window-states wset winfo-name)))) (t ; maximize a window (wlf:maximize-window-states wset winfo-name) (setq next-states (list 'wlf:maximize winfo-name current-states)))) (wlf:layout-internal wset) (wlf:maximize-info-set next-states))) (defun wlf:get-window-name (wset window) "Return the window name that is corresponding to the WINDOW object. If the WINDOW is not found, return nil. See also `wlf:get-window'. It is sort of the inverse function. It returns WINDOW by given name." (loop for winfo in (wlf:wset-winfo-list wset) for win = (wlf:window-window winfo) if (and win (window-live-p win) (eql win window)) return (wlf:window-name winfo))) (defun wlf:wset-live-p (wset) "Return t if WSET is valid, return nil otherwise." (let ((die-count 0) (shown-count 0)) (loop for winfo in (wlf:wset-winfo-list wset) for win = (wlf:window-window winfo) if (wlf:window-shown-p winfo) do (incf shown-count) (unless (and win (window-live-p win)) (incf die-count))) (cond ((= die-count 0) t) (t (loop for winfo in (wlf:wset-winfo-list wset) for (left top right bottom) = (wlf:window-edges winfo) for sw = (window-at left top) with windows = nil do (when sw (cond ((memq sw windows) (return nil)) (t (push sw windows)))) finally return t))))) (defun wlf:wset-clear-window-points (wset) "Clear the last preserved window-point and first-line-point slots for all windows. If users want change the window layout and re-use `wlf:wset' instance, this function should be called to forget wrong window-positions." (loop for winfo in (wlf:wset-winfo-list wset) do (setf (wlf:window-window-point winfo) nil (wlf:window-first-line-point winfo) nil))) (defun wlf:wset-fix-windows (wset) "Update window object instances with the current window configuration." (loop for winfo in (wlf:wset-winfo-list wset) for win = (wlf:window-window winfo) do (cond ((wlf:window-shown-p winfo) ; should be displayed (cond (win ; the previous window object exists (let ((swin (wlf:window-window-by-edge winfo))) (cond (swin ; found a window object (setf (wlf:window-window winfo) swin (wlf:window-edges winfo) (window-edges swin))) (t ; found no window object (wlf:window-shown-set winfo nil) (setf (wlf:window-window winfo) nil (wlf:window-edges winfo) nil))))) (t ; no previous window object (wlf:window-shown-set winfo nil) (setf (wlf:window-window winfo) nil (wlf:window-edges winfo) nil)))) (t ; not displayed (setf (wlf:window-window winfo) nil (wlf:window-edges winfo) nil))))) ;;; test ;; (setq ss ;; (wlf:layout ;; '(| (:left-max-size 20) ;; folder ;; (- (:lower-size-ratio 0.6) ;; summary message)) ;; '((:name folder :buffer "*info*") ;; (:name summary :buffer "*Messages*") ;; (:name message :buffer "window-layout.el" :default-hide nil)))) ;; (setq dd ;; (wlf:no-layout ;; '(| (:left-size-ratio 0.33) ;; folder ;; (| (:left-size-ratio 0.5) ;; summary message)) ;; '((:name folder :buffer "*info*") ;; (:name summary :buffer "*Messages*") ;; (:name message :buffer "window-layout.el")))) ;; (setq ff (wlf:no-layout ;; '(| (:left-max-size 40) ;; (- (:upper-size-ratio 0.25) ;; files ;; (- (:upper-size-ratio 0.3) ;; history sub)) ;; (| (:right-max-size 30) ;; main imenu)) ;; '((:name main :buffer "window-layout.el") ;; (:name files :buffer "*scratch*") ;; (:name history :buffer "*Messages*") ;; (:name sub :buffer "*Info*") ;; (:name imenu :buffer "*info*" :default-hide t)))) ;; (wlf:show ss 'folder) ;; (wlf:hide ss 'folder) ;; (wlf:toggle-maximize ss 'message) ;; (wlf:toggle-maximize ss 'folder) ;; (wlf:toggle ss 'folder 'summary) ;; (wlf:toggle ss 'summary) ;; (wlf:reset-window-sizes ss) ;; (wlf:select ss 'summary) ;; (wlf:get-buffer ss 'message) ;; (wlf:set-buffer ss 'message "*scratch*") ;; (wlf:refresh ss) ;; (wlf:refresh dd) ;; (wlf:refresh ff) ;; (wlf:toggle ff 'imenu) ;; (wlf:reset-window-sizes ff) ;; (wlf:get-window-name ss (selected-window)) ;; (wlf:wset-live-p ss) ;; (wlf:wset-live-p ss 1) ;; (wlf:wopts-replace-buffer ;; '((:name folder :buffer "*info*") ;; (:name summary :buffer "*Messages*") ;; (:name message :buffer "window-layout.el" :default-hide nil)) ;; '((folder . "*Messages*") (summary . "*scratch*"))) ;; (defun wlf:test-hook (wset) (message "HOOK : %s" wset)) ;; (wlf:layout-hook-add ss 'wlf:test-hook) ;; (wlf:layout-hook-remove ss 'wlf:test-hook) (provide 'window-layout) ;;; window-layout.el ends here