update
[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 (+ 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 get-top-windows ()
65   (let (topwindows)
66     (walk-windows (function (lambda (w)
67                               (let ((e (window-edges w)))
68                                 (if (< (nth 1 e) window-min-height)
69                                   (setq topwindows (cons (cons (nth 0 e) w) topwindows)))))))
70     (loop for w in (sort topwindows (function (lambda (a b) (< (car a) (car b)))))
71           collect (cdr w) )))
72
73 (defun setup-my-windows (&optional n)
74   (interactive "P")
75   (if n
76       (if (integerp n)
77           (setq my-windows-count n)
78         (setq my-windows-count nil)))
79   (let* ((width (if my-windows-count
80                     (- (/ (frame-width) my-windows-count) 4)
81                   (or whitespace-line-column 100)))
82          (min width) (distribute t)
83          (currentbuffer (current-buffer))
84          (currentwindow (selected-window))
85          (topwindows (loop for w in (get-top-windows)
86                            for b = (window-buffer w)
87                            if (not (member (buffer-name b)
88                                            setup-my-windows-junk-buffers))
89                            collect (list (nth 0 (window-edges w))
90                                          w
91                                          b
92                                          (window-point w)
93                                          (window-start w)
94                                          (equal w currentwindow))))
95          firstwindow newwindow newtopwindows newbottomwindow)
96     (delete-other-windows (nth 1 (car topwindows)))
97     (save-selected-window
98       (setq newbottomwindow (split-window-vertically
99                              (- (window-height) (max 5 (/ (* (frame-height) 15) 100)) -1)))
100       (select-window newbottomwindow)
101       (switch-to-buffer (get-buffer-create "*compilation*"))
102       (if (eq currentbuffer (current-buffer))
103           (setq newwindow (selected-window))))
104     (setq firstwindow (selected-window))
105     (setq newtopwindows (list (selected-window)))
106     (while (> (window-width) (+ width 3 min 3))
107       (select-window (split-window-horizontally (+ width 3)))
108       (setq newtopwindows (cons (selected-window) newtopwindows))
109       (switch-to-buffer (get-buffer-create "*scratch*")))
110     (setq newtopwindows (reverse newtopwindows))
111     (loop for w in newtopwindows
112           for (pos win buf point start iscurrent) in
113               (loop for w in topwindows
114                     for (pos win buf point start iscurrent) = w
115                     if (not (member (buffer-name buf) setup-my-windows-precious-buffers))
116                     collect w)
117           do (progn
118                (select-window w)
119                (set-window-buffer w buf)
120                (set-window-start w start)
121                (goto-char point)
122                (if iscurrent
123                    (setq newwindow w))))
124     (setq newtopwindows (reverse newtopwindows))
125     (setq topwindows (reverse topwindows))
126     (loop for w in newtopwindows
127           for (pos win buf point start iscurrent) in
128               (loop for w in topwindows
129                     for (pos win buf point start iscurrent) = w
130                     if (member (buffer-name buf) setup-my-windows-precious-buffers)
131                     collect w)
132           do (progn
133                (select-window w)
134                (set-window-buffer w buf)
135                (set-window-start w start)
136                (goto-char point)
137                (if iscurrent
138                    (setq newwindow w))))
139     (setq newwindow
140           (or newwindow
141               (loop for w in newtopwindows
142                     if (eq (window-buffer w) currentbuffer) return w)
143               (loop for w in newtopwindows
144                     for name = (buffer-name (window-buffer w))
145                     if (string= name "*scratch*") return w)
146               (loop for w in newtopwindows
147                     for name = (buffer-name (window-buffer w))
148                     if (and (= (aref name 0) ?*)
149                             (not (member name setup-my-windows-precious-buffers))) return w)
150               firstwindow))
151     (when (and distribute (> (length newtopwindows) 1))
152       (pjb-balance-windows t))
153     (select-window newwindow)
154     (if (not (member (buffer-name currentbuffer) setup-my-windows-junk-buffers))
155         (switch-to-buffer currentbuffer))
156     newbottomwindow))
157
158 (defun my-split-window-sensibly (window)
159   (if (and (> (window-height window) (- (frame-height (window-frame window)) window-min-height))
160            (> (window-height window) (max 5 (/ (* (frame-height) 15) 100))))
161       (split-window-sensibly window)))
162
163 (defun my-pop-to-buffer (buffer)
164   ;; display buffer in rightmost window if not displayed currently
165   (let ((w (get-buffer-window buffer)))
166     (unless w
167       (setq w (car (last (get-top-windows)))))
168     (select-window w)
169     (switch-to-buffer buffer)))
170
171 (defun my-display-at-bottom (&optional buffer)
172   ;; call my-setup-window and display current-buffer or BUFFER in bottom frame
173   (interactive)
174   (if (not buffer) (setq buffer (current-buffer)))
175   (bury-buffer)
176   ;; why does save-selected-window not work here ???
177   (save-selected-window
178     (select-window (setup-my-windows))
179     (switch-to-buffer buffer)))
180
181 (setq split-window-preferred-function 'my-split-window-sensibly)
182
183 (global-set-key "\C-x7" 'split-window-3-horizontally)
184 (global-set-key "\C-x8" (lambda () (interactive) (split-window-n-horizontally 100 50)))
185 (global-set-key "\C-x9" 'setup-my-windows)
186 (global-set-key "\C-\M-_" (lambda () (interactive) (safe-shrink-window 5)))
187 (global-set-key "\C-x_" 'my-display-at-bottom)
188
189 (defun my-swap-window-to-right (&optional below)
190   "If swap buffer in this window with buffer on the right. If BELOW is set,
191 instead move current buffer to right and replace it with the next buffer from
192 the buffer stack in the current window."
193   (interactive "P")
194   (let ((cb (current-buffer))
195         (cw (selected-window)))
196     (if below
197         (switch-to-buffer nil))
198     (windmove-right)
199     (if (not below)
200         (set-window-buffer cw (current-buffer)))
201     (switch-to-buffer cb)))
202
203 (defun my-swap-window-to-left (&optional below)
204   (interactive "P")
205   (let ((cb (current-buffer))
206         (cw (selected-window)))
207     (if below
208         (switch-to-buffer nil))
209     (windmove-left)
210     (if (not below)
211         (set-window-buffer cw (current-buffer)))
212     (switch-to-buffer cb)))
213
214 (global-set-key "\C-x>" 'my-swap-window-to-right)
215 (global-set-key "\C-x<" 'my-swap-window-to-left)
216
217 (defun maximize-window-15 ()
218   (interactive)
219   (maximize-window (max 5 (/ (* (frame-height) 15) 100))))
220
221 (global-set-key [(ctrl meta ?+)]  'maximize-window-15)
222
223 (defun safe-max-window-horizontally ()
224   (interactive)
225   (let ((found nil)
226         (width 0)
227         (count 0)
228         (current (selected-window)))
229     (walk-windows (function (lambda (w)
230                               (let ((e (window-edges w)))
231                                 (if (< (nth 1 e) window-min-height)
232                                     (progn
233                                       (setq width (+ width (window-width w))
234                                             count (1+ count))
235                                       (if (equal w current)
236                                           (setq found t)))))))
237                   'nomini)
238     (if (not found)
239         (error "Current window is not a top window"))
240     (shrink-window-horizontally (- (- width (window-width) (* window-min-width (1- count)))))))
241
242 (defun safe-max-window ()
243   (interactive)
244   (maximize-window 5)
245   (condition-case nil
246       (safe-max-window-horizontally)
247     (error nil)))
248
249 (global-set-key "\C-x=" 'safe-max-window)
250 (global-set-key "\C-x-" 'maximize-window-15)