4fde816acaf86e2544452f50f42315a238b9311e
[emacs-init.git] / setup / mywin.el
1 (defun split-window-3-horizontally ()
2   "Split window horizontally into three equal sized windows"
3   (interactive)
4   (let ((w (window-width)))
5     (split-window-horizontally (- w (/ w 3) -2))
6     (split-window-horizontally)))
7
8 (defun split-window-n-horizontally (width &optional min)
9   "Split window horizontally into WIDTH wide windows making the last
10 window no smaller than MIN. MIN defaults to WIDTH and WIDTH defaults
11 to 80."
12   (interactive "P")
13   (if (not width) (setq width 80))
14   (if (not min) (setq min width))
15   (save-selected-window
16     (while (> (window-width) (+ width 3 min 3))
17       (select-window (split-window-horizontally (+ width 3)))
18       (switch-to-buffer (get-buffer-create "*scratch*")))))
19
20 (defun maximize-window(&optional min-height)
21   "Enlarge the current window by as many lines as is possible without making any other
22 window smaller than MIN-HEIGHT lines."
23   (interactive)
24   ;; this algorithm is copied from window.el / balance-windows()
25   (let ((min-height (or min-height i(+ window-min-height 0)))
26         (count -1)
27         size)
28         ;; Don't count the lines that are above the uppermost windows.
29         ;; (These are the menu bar lines, if any.)
30     ;; Find all the different vpos's at which windows start,
31     ;; then count them.  But ignore levels that differ by only 1.
32     (save-window-excursion
33       (let (tops (prev-top -2))
34         (walk-windows (function (lambda (w)
35                                   (setq tops (cons (nth 1 (window-edges w))
36                                                    tops))))
37                       'nomini)
38         (setq tops (sort tops '<))
39         (while tops
40           (if (> (car tops) (1+ prev-top))
41               (setq prev-top (car tops)
42                     count (1+ count)))
43           (setq tops (cdr tops)))
44         (setq count (1+ count))))
45     (setq size (- (frame-height) (* (1- count) min-height)))
46     (enlarge-window (- size (window-height)))))
47
48 (defun safe-shrink-window(&optional n min-height)
49   "Like shrink-window but will not remove a window"
50   (interactive)
51   (let* ((min-height (or min-height window-min-height))
52          (n (or n 1)))
53     (if (< (- (window-height) n) min-height)
54         (shrink-window (- (window-height) min-height))
55       (shrink-window n))))
56
57 (defconst setup-my-windows-precious-buffers
58   '("*eshell*"))
59 (defconst setup-my-windows-junk-buffers
60   '("*scratch*" "*Messages*" "*Calculator" "*Calc Trail*" "*compilation*" "*fetchmail*"))
61
62 (defvar my-windows-count nil)
63
64 (defun setup-my-windows (&optional n)
65   (interactive "P")
66   (if n
67       (if (integerp n)
68           (setq my-windows-count n)
69         (setq my-windows-count nil)))
70   (let* ((width (if my-windows-count
71                     (- (/ (frame-width) my-windows-count) 4)
72                   (or whitespace-line-column 100)))
73          (min width) (distribute t)
74          (currentbuffer (current-buffer))
75          (currentwindow (selected-window))
76          topwindows firstwindow newwindow newtopwindows)
77     (walk-windows (function (lambda (w)
78                               (let ((e (window-edges w)))
79                                 (if (< (nth 1 e) window-min-height)
80                                     (setq topwindows (cons (list (nth 0 e)
81                                                                  w
82                                                                  (window-buffer w)
83                                                                  (window-point w)
84                                                                  (window-start w)
85                                                                  (equal w currentwindow))
86                                                            topwindows))))))
87                             'nomini)
88     (setq topwindows (sort topwindows (function (lambda (a b) (< (car a) (car b))))))
89     (setq topwindows (loop for w in topwindows
90                            for (pos win buf point start iscurrent) = w
91                            if (not (member (buffer-name buf) setup-my-windows-junk-buffers))
92                            collect w))
93     (delete-other-windows (nth 1 (car topwindows)))
94     (save-selected-window
95       (select-window (split-window-vertically
96                       (- (window-height) (max 5 (/ (* (frame-height) 15) 100)) -1)))
97       (switch-to-buffer (get-buffer-create "*compilation*"))
98       (if (eq currentbuffer (current-buffer))
99           (setq newwindow (selected-window))))
100     (setq firstwindow (selected-window))
101     (setq newtopwindows (list (selected-window)))
102     (while (> (window-width) (+ width 3 min 3))
103       (select-window (split-window-horizontally (+ width 3)))
104       (setq newtopwindows (cons (selected-window) newtopwindows))
105       (switch-to-buffer (get-buffer-create "*scratch*")))
106     (setq newtopwindows (reverse newtopwindows))
107     (loop for w in newtopwindows
108           for (pos win buf point start iscurrent) in
109               (loop for w in topwindows
110                     for (pos win buf point start iscurrent) = w
111                     if (not (member (buffer-name buf) setup-my-windows-precious-buffers))
112                     collect w)
113           do (progn
114                (select-window w)
115                (set-window-buffer w buf)
116                (set-window-start w start)
117                (goto-char point)
118                (if iscurrent
119                    (setq newwindow w))))
120     (setq newtopwindows (reverse newtopwindows))
121     (setq topwindows (reverse topwindows))
122     (loop for w in newtopwindows
123           for (pos win buf point start iscurrent) in
124               (loop for w in topwindows
125                     for (pos win buf point start iscurrent) = w
126                     if (member (buffer-name buf) setup-my-windows-precious-buffers)
127                     collect w)
128           do (progn
129                (select-window w)
130                (set-window-buffer w buf)
131                (set-window-start w start)
132                (goto-char point)
133                (if iscurrent
134                    (setq newwindow w))))
135     (setq newwindow
136           (or newwindow
137               (loop for w in newtopwindows
138                     if (eq (window-buffer w) currentbuffer) return w)
139               (loop for w in newtopwindows
140                     for name = (buffer-name (window-buffer w))
141                     if (string= name "*scratch*") return w)
142               (loop for w in newtopwindows
143                     for name = (buffer-name (window-buffer w))
144                     if (and (= (aref name 0) ?*)
145                             (not (member name setup-my-windows-precious-buffers))) return w)
146               firstwindow))
147     (when (and distribute (> (length newtopwindows) 1))
148       (pjb-balance-windows t))
149     (select-window newwindow)
150     (if (not (member (buffer-name currentbuffer) setup-my-windows-junk-buffers))
151         (switch-to-buffer currentbuffer))))
152
153 (defun my-split-window-sensibly (window)
154   (if (and (> (window-height window) (- (frame-height (window-frame window)) window-min-height))
155            (> (window-height window) (max 5 (/ (* (frame-height) 15) 100))))
156       (split-window-sensibly window)))
157
158 (setq split-window-preferred-function 'my-split-window-sensibly)
159
160 (global-set-key "\C-x7" 'split-window-3-horizontally)
161 (global-set-key "\C-x8" (lambda () (interactive) (split-window-n-horizontally 100 50)))
162 (global-set-key "\C-x9" 'setup-my-windows)
163 (global-set-key "\C-\M-_" (lambda () (interactive) (safe-shrink-window 5)))
164
165 (defun my-swap-window-to-right (&optional below)
166   "If swap buffer in this window with buffer on the right. If BELOW is set,
167 instead move current buffer to right and replace it with the next buffer from
168 the buffer stack in the current window."
169   (interactive "P")
170   (let ((cb (current-buffer))
171         (cw (selected-window)))
172     (if below
173         (switch-to-buffer nil))
174     (windmove-right)
175     (if (not below)
176         (set-window-buffer cw (current-buffer)))
177     (switch-to-buffer cb)))
178
179 (defun my-swap-window-to-left (&optional below)
180   (interactive "P")
181   (let ((cb (current-buffer))
182         (cw (selected-window)))
183     (if below
184         (switch-to-buffer nil))
185     (windmove-left)
186     (if (not below)
187         (set-window-buffer cw (current-buffer)))
188     (switch-to-buffer cb)))
189
190 (global-set-key "\C-x>" 'my-swap-window-to-right)
191 (global-set-key "\C-x<" 'my-swap-window-to-left)
192
193 (defun maximize-window-15 ()
194   (interactive)
195   (maximize-window (max 5 (/ (* (frame-height) 15) 100))))
196
197 (global-set-key [(ctrl meta ?+)]  'maximize-window-15)
198
199 (defun safe-max-window-horizontally ()
200   (interactive)
201   (let ((found nil)
202         (width 0)
203         (count 0)
204         (current (selected-window)))
205     (walk-windows (function (lambda (w)
206                               (let ((e (window-edges w)))
207                                 (if (< (nth 1 e) window-min-height)
208                                     (progn
209                                       (setq width (+ width (window-width w))
210                                             count (1+ count))
211                                       (if (equal w current)
212                                           (setq found t)))))))
213                   'nomini)
214     (if (not found)
215         (error "Current window is not a top window"))
216     (shrink-window-horizontally (- (- width (window-width) (* window-min-width (1- count)))))))
217
218 (defun safe-max-window ()
219   (interactive)
220   (safe-max-window-horizontally)
221   (maximize-window 5))
222
223 (global-set-key "\C-x=" 'safe-max-window)
224 (global-set-key "\C-x-" 'maximize-window-15)