escreen / escreen.el

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
;;; escreen.el --- emacs window session manager

;;; Copyright (C) 1992, 94, 95, 97, 2001 Noah S. Friedman

;;; Author: Noah Friedman <friedman@splode.com>
;;; Maintainer: friedman@splode.com
;;; Keywords: extensions
;;; Created: 1992-03-23

;;; $Id$

;; 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 2, 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, you can either send email to this
;; program's maintainer or write to: The Free Software Foundation,
;; Inc.; 59 Temple Place, Suite 330; Boston, MA 02111-1307, USA.

;;; Commentary:

;; To install, put this file in your load-path, byte-compile it, and add
;; the following to your .emacs:
;;
;;   (load "escreen")
;;   (escreen-install)

;; If you are using Emacs 18 or Emacs 19, you may have trouble loading this
;; program because of the customs syntax officially introduced in Emacs 20.
;; In that case, first load cust-stub.el, available from
;;
;;     http://www.splode.com/~friedman/software/emacs-lisp/
;;
;; Updates to escreen.el will also be made available on that page.

;; Inspired by:
;;   * wicos.el, written by Heikki Suopanki <suopanki@phoenix.oulu.fi>
;;   * `screen', written by Oliver Laumann, Juergen Weigert,
;;     and Michael Schroeder.

;; Todo:
;;   * per-frame screen configurations
;;   * symbolic names for screens (a la wicos.el)
;;   * switching active screen from pull-down menu from menubar
;;   * switching active screen from escreen menu
;;   * persistance of screens across instances of emacs
;;     (this may be impossible to do sanely.)

;;; Code:

;; Variable declarations -- can be set by user

(defgroup escreen nil
  "Window configuration management"
  :group 'escreen
  :group 'extensions)

(defcustom escreen-max-screens 10
  "*Maximum number of screens that may be created."
  :type 'integer
  :group 'escreen)

(defcustom escreen-new-screen-default-buffer "*scratch*"
  "*Default buffer to display in newly-created screens."
  :type 'string
  :group 'escreen)

(defcustom escreen-restore-killed-buffers nil
  "*If non-nil, automatically revisit files if they have been killed.
That is, if a buffer was killed while in another screen session,
recreate them, visiting whatever file they were visiting."
  :type 'boolean
  :group 'escreen)

(defcustom escreen-preserve-buffer-list-order t
  "*If non-nil, preserve buffer list for each screen when switching.
When returning to a previously-saved screen, the buffer list order is
restored.  Buffers which have been created since the saved screen was last
visited, are put at the end of the list but the relative order is preserved.

This buffer list order is returned by the function `buffer-list' and
affects the behavior of `other-buffer', etc.

In Emacs 20 and later, each frame has its own ordered buffer list.
Switching screen sessions affects the selected frame's buffer list only."
  :type 'boolean
  :group 'escreen)

(defcustom escreen-number-mode t
  "*If non-nil, display current escreen number in mode line."
  :type 'boolean
  :group 'escreen)

(defcustom escreen-install-number-mode-format t
  "*If non-nil, install `escreen-mode-line-format' on `global-mode-string'.
This is performed by `escreen-install'."
  :type 'boolean
  :group 'escreen)

(defcustom escreen-goto-screen-hook nil
  "*Hook to run after `escreen-goto-screen' completes.
An example function that can make use of this hook is
`escreen-enable-number-mode-if-more-than-one-screen'."
  :type 'hook
  :group 'escreen)

(defcustom escreen-menu-mode-hook nil
  "*Hook to run by `escreen-menu' after everything else."
  :type 'hook
  :group 'escreen)


;; Keybindings

(defcustom escreen-prefix-char "\C-\\"
  "*Character prefixing escreen commands.
If you wish to change this, you must also do

   (global-set-key escreen-prefix-char 'escreen-prefix)

to update the prefix in the global keymap."
  :type 'string
  :group 'escreen)

(defvar escreen-map nil
  "*Keymap for escreen commands.")
(cond
 ((null escreen-map)
  (setq escreen-map (make-sparse-keymap))
  (define-key escreen-map escreen-prefix-char 'escreen-goto-last-screen)
  (define-key escreen-map "0"    'escreen-goto-screen-0)
  (define-key escreen-map "1"    'escreen-goto-screen-1)
  (define-key escreen-map "2"    'escreen-goto-screen-2)
  (define-key escreen-map "3"    'escreen-goto-screen-3)
  (define-key escreen-map "4"    'escreen-goto-screen-4)
  (define-key escreen-map "5"    'escreen-goto-screen-5)
  (define-key escreen-map "6"    'escreen-goto-screen-6)
  (define-key escreen-map "7"    'escreen-goto-screen-7)
  (define-key escreen-map "8"    'escreen-goto-screen-8)
  (define-key escreen-map "9"    'escreen-goto-screen-9)
  (define-key escreen-map "?"    'escreen-help)
  (define-key escreen-map "\C-b" 'escreen-menu)
  (define-key escreen-map "a"    'escreen-get-active-screen-numbers)
  (define-key escreen-map "b"    'escreen-get-current-screen-number)
  (define-key escreen-map "c"    'escreen-create-screen)
  (define-key escreen-map "g"    'escreen-goto-screen)
  (define-key escreen-map "k"    'escreen-kill-screen)
  (define-key escreen-map "n"    'escreen-goto-next-screen)
  (define-key escreen-map "p"    'escreen-goto-prev-screen)))

(defalias 'escreen-prefix escreen-map)


;;; Internal variables.  Do not set these yourself.

;; This should not be modified by the user.  The information it provides is
;; critical and the calling conventions are different than for
;; escreen-map-data-format.  The order here is important too.
;; Do not change this data structure without also changing the
;; escreen-configuration-data-map-critical-* accessors.
(defvar escreen-map-critical-data-format
  (list 'current-buffer
        (function (lambda () (buffer-name)))
        'buffer-file-name))

;; If you want to add or change this list, it's best to set
;; escreen-configuration-alist to nil and run escreen-install afterward.
;; Otherwise, the new table will be used with old data and may cause errors.
;;
;; Note that resetting escreen in this way loses all but the current
;; window configuration.
(defvar escreen-map-data-format
  '((escreen-map-save-window-start    . escreen-map-restore-window-start)
    (mark-marker                      . escreen-map-restore-mark-marker)
    (escreen-map-save-point           . escreen-map-restore-point)
    (escreen-map-save-narrowed-region . escreen-map-restore-narrowed-region)
    (escreen-map-save-truncate-lines  . escreen-map-restore-truncate-lines)
    (escreen-map-save-mode-line-face  . escreen-map-restore-mode-line-face)
    (escreen-map-save-menu-bar-mode   . escreen-map-restore-menu-bar-mode)
    (buffer-list                      . escreen-map-restore-buffer-list)))

;; Keeps track of escreen state (window config, buffers, etc.)
;; The structure of each elt is
;;
;;  (screen-number #<window-configuration>
;;                 (((critical-data-buffer-1) user-data-buffer-1 ...)
;;                  ((critical-data-buffer-2) user-data-buffer-2 ...)
;;                  ...)
;;                 selected-window-number)
;;
(defvar escreen-configuration-alist nil)

;; Current screen number.  Smallest possible screen number is 0.
(defvar escreen-current-screen-number 0)

;; Current screen number as a string.
;; Smallest possible screen number is 0.
(defvar escreen-current-screen-string
  (int-to-string escreen-current-screen-number))

;; Last-visited screen number.  Smallest possible screen number is 0.
(defvar escreen-last-screen-number 0)

;; Highest screen number currently in use.
(defvar escreen-highest-screen-number-used 0)

;; It's ok to change this, but it makes use of internal variables
(defvar escreen-mode-line-format
  '(escreen-number-mode ("S" escreen-current-screen-string " ")))


;;;###autoload
(defun escreen-install ()
  (interactive)
  (global-set-key escreen-prefix-char 'escreen-prefix)

  ;; Install screen number on global-mode-string
  (and escreen-install-number-mode-format
       (let ((elt '("" escreen-mode-line-format)))
         (or (escreen-member elt global-mode-string)
             (setq global-mode-string
                   (cons elt global-mode-string)))))

  (if escreen-number-mode
      (escreen-number-mode 1))

  ;; Initialize escreen-configuration-alist by placing current window
  ;; config in it.
  (escreen-save-current-screen-configuration))

(defun escreen-number-mode (&optional prefix)
  "*Toggle escreen-number-mode (see variable docstring).
If called with a positive prefix argument, always enable.
If called with a negative prefix argument, always disable.
If called with no prefix argument, toggle current state."
  (interactive "P")
  (setq escreen-number-mode
        (cond ((null prefix)
               (not escreen-number-mode))
              (t
               (>= (prefix-numeric-value prefix) 0)))))


(defun escreen-create-screen ()
  "Create a new screen and switch to it.
New screen will display one window with the buffer specified by
`escreen-new-screen-default-buffer'."
  (interactive)
  (let ((new-screen-number (escreen-first-unused-screen-number)))
    (or new-screen-number
        (error "escreen: No more screens (see \"escreen-max-screens\")"))
    ;; Save window configuration before switching to a new one.
    (escreen-save-current-screen-configuration)
    (and (> new-screen-number escreen-highest-screen-number-used)
         (setq escreen-highest-screen-number-used new-screen-number))
    (setq escreen-last-screen-number escreen-current-screen-number)
    (setq escreen-current-screen-number new-screen-number)
    (setq escreen-current-screen-string (int-to-string new-screen-number))
    (delete-other-windows)
    (switch-to-buffer escreen-new-screen-default-buffer)
    ;; Save new window configuration so that it's in the alist.
    (escreen-save-current-screen-configuration))
  ;; We run this hook because, in a sense, we have gone to a new
  ;; screen. but we don't actually call escreen-goto-screen because of the
  ;; extra setup work here.
  (run-hooks 'escreen-goto-screen-hook))

(defun escreen-kill-screen (&optional number)
  "Kill current screen, or screen given by optional argument NUMBER.
No error occurs if the specified screen number doesn't exist.
You cannot kill the last existing screen.
Switch to previous screen if killing active one."
  (interactive)
  (let* ((screen-number (or number escreen-current-screen-number))
         (killing-current-screen-p (eq escreen-current-screen-number
                                       screen-number))
         (screen-data (escreen-configuration-escreen screen-number))
         previous-screen)
    (cond (screen-data
           (and killing-current-screen-p
                (escreen-configuration-one-screen-p)
                (error "escreen: only one screen, can't kill."))
           ;; Don't bother looking for previous screen number unless killing
           ;; current screen, because only then do we need to switch screens.
           (and killing-current-screen-p
                (setq previous-screen (escreen-get-prev-screen-number)))
           (escreen-configuration-escreen-delete screen-data)
           (and (eq screen-number escreen-highest-screen-number-used)
                ;; We're killing the screen with the highest number.
                ;; Look for the next highest number.
                (setq escreen-highest-screen-number-used
                      (car (sort (escreen-configuration-screen-numbers) '>))))
           (and killing-current-screen-p
                (escreen-goto-screen previous-screen 'dont-update-current))))))


(defun escreen-goto-screen (number &optional dont-update-current)
  "Switch to screen number N.
Optional arg DONT-UPDATE-CURRENT means don't save the current screen
configuration, though this isn't intended to be used interactively."
  (interactive "NGo to escreen number: ")
  (let ((screen-data (escreen-configuration-escreen number)))
    (or screen-data
        (error "escreen: %d: invalid screen number." number))
    (or dont-update-current
        (escreen-save-current-screen-configuration))
    (escreen-restore-screen-map screen-data)
    (setq escreen-current-screen-string (int-to-string number))
    (or dont-update-current
        (setq escreen-last-screen-number escreen-current-screen-number))
    (setq escreen-current-screen-number number))
  (run-hooks 'escreen-goto-screen-hook))

(defun escreen-goto-last-screen ()
  "Switch to the last visited screen."
  (interactive)
  (let ((n (if (= escreen-last-screen-number escreen-current-screen-number)
               (escreen-get-next-screen-number escreen-last-screen-number)
             escreen-last-screen-number)))
    (setq escreen-last-screen-number escreen-current-screen-number)
    (escreen-goto-screen n)))

(defun escreen-goto-prev-screen (&optional n)
  "Switch to the previous screen.
This is the nearest lower-numbered existing screen from the current one,
wrapping around list of screens if necessary.
If prefix arg N given, jump to the Nth previous screen."
  (interactive "p")
  (if (< n 0)
      (escreen-goto-prev-or-next-screen-internal (- n) 'next)
    (escreen-goto-prev-or-next-screen-internal n 'prev)))

(defun escreen-goto-next-screen (&optional n)
  "Switch to the next screen.
This is the nearest greater-numbered existing screen from the current one,
wrapping around list of screens if necessary.
If prefix arg N given, jump to the Nth next screen."
  (interactive "p")
  (if (< n 0)
      (escreen-goto-prev-or-next-screen-internal (- n) 'prev)
    (escreen-goto-prev-or-next-screen-internal n 'next)))

(defun escreen-goto-prev-or-next-screen-internal (n prev-or-next)
  (let ((total (length (escreen-get-active-screen-numbers)))
        (func (if (eq prev-or-next 'next)
                  'escreen-get-next-screen-number
                'escreen-get-prev-screen-number))
        (i 0)
        (screen-number escreen-current-screen-number))
    (and (> n total)
         ;; Trim off excess amount so we do fewer iterations, since
         ;; wrapping over the total number of screens even once is
         ;; wasteful and slow.
         (setq n (- n (* (/ n total) total))))
    (while (< i n)
      (setq screen-number (funcall func screen-number)
            i (1+ i)))
    (escreen-goto-screen screen-number)))

(defun escreen-goto-screen-0 () (interactive) (escreen-goto-screen 0))
(defun escreen-goto-screen-1 () (interactive) (escreen-goto-screen 1))
(defun escreen-goto-screen-2 () (interactive) (escreen-goto-screen 2))
(defun escreen-goto-screen-3 () (interactive) (escreen-goto-screen 3))
(defun escreen-goto-screen-4 () (interactive) (escreen-goto-screen 4))
(defun escreen-goto-screen-5 () (interactive) (escreen-goto-screen 5))
(defun escreen-goto-screen-6 () (interactive) (escreen-goto-screen 6))
(defun escreen-goto-screen-7 () (interactive) (escreen-goto-screen 7))
(defun escreen-goto-screen-8 () (interactive) (escreen-goto-screen 8))
(defun escreen-goto-screen-9 () (interactive) (escreen-goto-screen 9))


(defun escreen-get-current-screen-number ()
  "Returns the currently selected screen number.
If called interactively, also print this result in the minibuffer."
  (interactive)
  (if (interactive-p)
      (message "escreen: current screen is number %d"
               escreen-current-screen-number)
    escreen-current-screen-number))

(defun escreen-get-active-screen-numbers ()
  "Print a list of the active screen numbers in the echo area.
Returns a list of numbers which represent screen numbers presently in use."
  (interactive)
  (let ((screen-list (sort (escreen-configuration-screen-numbers) '<))
        (str "escreen: active screens:"))
    (and (interactive-p)
         (progn
           (mapcar (function (lambda (num)
                               (setq str (format "%s %d" str num))))
                   screen-list)
           (message str)))
    screen-list))

(defun escreen-help ()
  "Display a short summary of escreen commands."
  (interactive)
  (if (string-lessp emacs-version "19")
      ;; emacs 18 couldn't list only bindings with a common prefix.
      (describe-bindings)
    ;; Emacs 19 can handle escreen-prefix-char (as a string) directly, but
    ;; for XEmacs, it must be converted to a vector.
    (describe-bindings (escreen-string-to-vector escreen-prefix-char))))

(defun escreen-string-to-vector (s)
  (let* ((l (length s))
         (v (make-vector l nil))
         (i 0))
    (while (< i l)
      (aset v i (aref s i))
      (setq i (1+ i)))
    v))


;; Return the first unused number available for designation as a screen
;; number, or nil if  escreen-max-screens  screens are already in use.
(defun escreen-first-unused-screen-number ()
  (let ((number 0))
    (while (and (< number escreen-max-screens)
                (escreen-configuration-escreen number))
      (setq number (1+ number)))
    (and (< number escreen-max-screens) number)))

;; Save window configuration, buffer configuration, and current marks and
;; point for each displayed buffer for the current screen.
(defun escreen-save-current-screen-configuration ()
  (let ((screen-data (escreen-screen-defined))
        (new-alist-member nil))
    (if screen-data
        (setcdr screen-data (escreen-save-screen-map))
      (setq new-alist-member (cons escreen-current-screen-number
                                   (escreen-save-screen-map)))
      (setq escreen-configuration-alist
            (cons new-alist-member escreen-configuration-alist)))))

;; Return attributes for screen N, or nil if it doesn't exist.
(defun escreen-screen-defined (&optional n)
  (escreen-configuration-escreen (or n escreen-current-screen-number)))

;; Return nearest number less than current screen number that is
;; an active screen, wrapping around end of screen list if necessary.
(defun escreen-get-prev-screen-number (&optional current-screen-number)
  (or current-screen-number
      (setq current-screen-number escreen-current-screen-number))
  (if (eq 0 escreen-highest-screen-number-used)
      0
    ;; Decrement/wrap current screen number
    (setq current-screen-number (1- current-screen-number))
    (and (< current-screen-number 0)
         (setq current-screen-number escreen-highest-screen-number-used))
    (while (not (assq current-screen-number escreen-configuration-alist))
      ;; Decrement/wrap current screen number
      (setq current-screen-number (1- current-screen-number))
      (and (< current-screen-number 0)
           (setq current-screen-number escreen-highest-screen-number-used)))
    current-screen-number))

;; Return nearest number greater than current screen number that is
;; an active screen, wrapping around end of screen list if necessary.
(defun escreen-get-next-screen-number (&optional current-screen-number)
  (or current-screen-number
      (setq current-screen-number escreen-current-screen-number))
  (if (eq 0 escreen-highest-screen-number-used)
      0
    ;; Increment/wrap current screen number
    (setq current-screen-number (1+ current-screen-number))
    (and (> current-screen-number escreen-highest-screen-number-used)
         (setq current-screen-number 0))
    (while (not (assq current-screen-number escreen-configuration-alist))
      ;; Increment/wrap current screen number
      (setq current-screen-number (1+ current-screen-number))
      (and (> current-screen-number escreen-highest-screen-number-used)
           (setq current-screen-number 0)))
    current-screen-number))


;;; Primitive accessors for escreen-configuration-alist

(defun escreen-configuration-escreen (number)
  (assq number escreen-configuration-alist))

(defun escreen-configuration-escreen-delete (data)
  (setq escreen-configuration-alist
        (delq (if (numberp data)
                  (escreen-configuration-escreen data)
                data)
              escreen-configuration-alist)))

(defun escreen-configuration-screen-numbers ()
  (mapcar 'car escreen-configuration-alist))

(defun escreen-configuration-one-screen-p ()
  (>= 1 (length escreen-configuration-alist)))

;; Sort the alist so that they are in order numerically.
(defun escreen-configuration-alist-sort-by-number ()
  (setq escreen-configuration-alist
        (sort escreen-configuration-alist
              (function (lambda (a b)
                          (< (car a) (car b)))))))

;;; map-data sub-accessors

(defun escreen-configuration-screen-number (l)
  (nth 0 l))

(defun escreen-configuration-window-data-configuration (l)
  (nth 1 l))

(defun escreen-configuration-data-map (l)
  (nth 2 l))

(defun escreen-configuration-selected-window-count (l)
  (nth 3 l))

;;; screen map data accessors

(defun escreen-configuration-data-map-critical (data)
  (car data))

(defun escreen-configuration-data-map-user (data)
  (cdr data))

;;; critical map data accessors

(defun escreen-configuration-data-map-critical-buffer (crit-map)
  (nth 0 crit-map))

(defun escreen-configuration-data-map-critical-buffer-name (crit-map)
  (nth 1 crit-map))

(defun escreen-configuration-data-map-critical-buffer-file-name (crit-map)
  (nth 2 crit-map))


(defun escreen-save-screen-map ()
  (let ((config (current-window-configuration))
        (win-data nil)
        (sel-win-count 0)
        (sel-window (selected-window))
        (first-window (escreen-first-window))
        (window nil))
    (save-excursion
      (save-window-excursion
        (select-window first-window)
        (while (not (eq window first-window))
          (cond ((null sel-window))
                ((eq (selected-window) sel-window)
                 (setq sel-window nil))
                (t
                 (setq sel-win-count (1+ sel-win-count))))
          (setq win-data
                (cons (cons (escreen-save-critical-data)
                            (escreen-save-user-data))
                      win-data))
          (setq window (select-window (next-window)))
          (set-buffer (window-buffer (selected-window))))))
    (list config (nreverse win-data) sel-win-count)))

(defun escreen-restore-screen-map (map)
  (let ((config (escreen-configuration-window-data-configuration map))
        (map (escreen-configuration-data-map map))
        (sel-win-number (escreen-configuration-selected-window-count map))
        (win-count 0)
        (sel-win nil))
    (set-window-configuration config)
    (select-window (escreen-first-window))
    (while map
      (and (= win-count sel-win-number)
           (setq sel-win (selected-window)))
      (setq win-count (1+ win-count))

      (escreen-restore-critical-data
        (escreen-configuration-data-map-critical (car map)))
      (widen)
      (escreen-restore-user-data
        (escreen-configuration-data-map-user (car map)))
      (select-window (next-window))
      (setq map (cdr map)))
    (select-window (or sel-win (escreen-first-window)))))

(defun escreen-save-critical-data ()
  (mapcar 'funcall escreen-map-critical-data-format))

(defun escreen-restore-critical-data (data)
  (let ((buffer (escreen-configuration-data-map-critical-buffer data))
        (buffer-name
         (escreen-configuration-data-map-critical-buffer-name data))
        (buf-file-name
         (escreen-configuration-data-map-critical-buffer-file-name data)))
    (cond ((escreen-killed-buffer-p buffer)
           (cond ((null escreen-restore-killed-buffers)
                  (set-window-buffer (selected-window)
                                     (get-buffer-create
                                      escreen-new-screen-default-buffer)))
                 ((stringp buf-file-name)
                  (setq buffer (find-file-noselect buf-file-name))
                  (set-window-buffer (selected-window) buffer)
                  (or (get-buffer buffer-name)
                      (rename-buffer buffer-name)))
                 (t
                  (set-window-buffer (selected-window)
                                     (get-buffer-create
                                      escreen-new-screen-default-buffer)))))
          (t
           (set-window-buffer (selected-window) buffer)))))

(defun escreen-save-user-data ()
  (mapcar (function (lambda (pair) (funcall (car pair))))
          escreen-map-data-format))

(defun escreen-restore-user-data (data)
  (let ((funlist escreen-map-data-format))
    (while (and data funlist)
      (funcall (cdr (car funlist)) (car data))
      (setq funlist (cdr funlist))
      (setq data (cdr data)))))


;; Functions used to save and restore screen configuration state.
;; These are mapped over via presence in escreen-map-data-format.

(defun escreen-map-save-window-start ()
  (escreen-make-marker (window-start)))

(defun escreen-map-restore-window-start (p)
  (and (escreen-position-valid-p p)
       (set-window-start (selected-window) p t)))

(defun escreen-map-restore-mark-marker (mark)
  (cond ((escreen-position-valid-p mark)
         (set-marker (or (mark-marker)
                         ;; when XEmacs zmacs-regions are set, mark-marker
                         ;; can return nil unless optional arg forcep is
                         ;; non-nil.
                         ;; In Emacs transient-mark-mode, mark-marker will
                         ;; still return a marker, so no magic needed.
                         (mark-marker t))
                     (marker-position mark)
                     (marker-buffer mark)))))

(defun escreen-map-save-point ()
  ;; If there is a process mark in the current buffer and point is at it,
  ;; then return the process mark also.  That way, when we return to this
  ;; screen, point will be at the end of the process output even if that
  ;; has advanced since then.  Otherwise, just use a before-insertion
  ;; marker (if supported).
  (let* ((point-mark (escreen-make-marker (point-marker) nil t))
         (proc (get-buffer-process (current-buffer)))
         (proc-mark (and proc (process-mark proc))))
    (if (and (escreen-position-valid-p proc-mark)
             (= proc-mark (point)))
        (cons proc-mark point-mark)
      point-mark)))

(defun escreen-map-restore-point (pos)
  (cond ((consp pos)
         (cond ((escreen-position-valid-p (car pos))
                (goto-char (car pos)))
               ((escreen-position-valid-p (cdr pos))
                (goto-char (cdr pos)))))
        (t
         (and (escreen-position-valid-p pos)
              (goto-char pos)))))

(defun escreen-map-save-narrowed-region ()
  (cons (and (> (point-min) 1)
             (escreen-make-marker (point-min)))
        (and (<= (point-max) (buffer-size))
             (escreen-make-marker (point-max) nil t))))

(defun escreen-map-restore-narrowed-region (reg)
  (let ((size (1+ (buffer-size)))
        (beg (or (car reg) (point-min)))
        (end (or (cdr reg) (point-max))))
    (and (escreen-position-valid-p beg)
         (escreen-position-valid-p end)
         (<= beg size)
         (<= end size)
         (narrow-to-region beg end))))

(defun escreen-map-save-truncate-lines ()
  truncate-lines)

(defun escreen-map-restore-truncate-lines (v)
  (setq truncate-lines v))

(defun escreen-map-save-mode-line-face ()
  (cond ((fboundp 'face-reverse-p)
         ;; XEmacs mode line face properties
         (list (face-reverse-p 'modeline)
               (face-background 'modeline)
               (face-foreground 'modeline)))
        ((boundp 'mode-line-inverse-video)
         mode-line-inverse-video)))

(defun escreen-map-restore-mode-line-face (v)
  (cond ((fboundp 'face-reverse-p)
         (set-face-reverse-p 'modeline (nth 0 v))
         (set-face-background 'modeline (nth 1 v))
         (set-face-foreground 'modeline (nth 2 v)))
        ((boundp 'mode-line-inverse-video)
         (setq mode-line-inverse-video v))))

;; Emacs 19.30 and beyond supports menu bars on ascii terminals, but beware
;; of turning them off or on once escreen is loaded; if a stored window
;; configuration was for a frame with a menu bar, but there is no menu bar
;; presently, that will crash emacs.  This fatal bug is present in all
;; versions of Emacs prior to 21.0.
(defun escreen-map-save-menu-bar-mode ()
  (and (boundp 'menu-bar-mode)
       menu-bar-mode))

(defun escreen-map-restore-menu-bar-mode (v)
  (cond ((fboundp 'menu-bar-mode)
         (if v
             (menu-bar-mode 1)
           (menu-bar-mode -1)))))

(defun escreen-map-restore-buffer-list (olist)
  (and escreen-preserve-buffer-list-order
       (escreen-set-buffer-list-order olist)))


(defun escreen-killed-buffer-p (buffer)
  (not (if (fboundp 'buffer-live-p)
           (buffer-live-p buffer)
         ;; Emacs 18 doesn't have buffer-live-p.
         ;; Killed buffers have no names.
         (buffer-name buffer))))

(defun escreen-position-valid-p (pos)
  (cond ((numberp pos)
         (<= pos (1+ (buffer-size))))
        ((markerp pos)
         (and (eq (marker-buffer pos) (current-buffer))
              (numberp (marker-position pos))
              (<= pos (1+ (buffer-size)))))
        (t nil)))

(defun escreen-set-buffer-list-order (olist)
  (let (firstbuf buf)
    (while olist
      (setq buf (car olist))
      (and (stringp buf)
           (setq buf (get-buffer buf)))
      (cond ((escreen-killed-buffer-p buf))
            (t
             (bury-buffer buf)
             (or firstbuf
                 (setq firstbuf buf))))
      (setq olist (cdr olist)))
    (setq olist (buffer-list))
    (while (not (eq (car olist) firstbuf))
      (bury-buffer (car olist))
      (setq olist (cdr olist)))))

;; Copy existing marker, or make a new one from point.
;; Emacs 19.30 and later can create markers which are advanced if text is
;; inserted before them, without needing to call insert-before-markers
;; explicitly.  This is useful for storing point, mark, etc. since the
;; buffer may be edited while we are in other escreens.
(defun escreen-make-marker (pos &optional buffer insertion-type)
  (let ((new-marker nil))
    (cond ((markerp pos)
           (setq new-marker (copy-marker pos))
           (and buffer
                (set-marker new-marker (marker-position pos) buffer)))
          (t
           (setq new-marker (make-marker))
           (set-marker new-marker pos buffer)))
    (and (fboundp 'set-marker-insertion-type)
         (set-marker-insertion-type new-marker insertion-type))
    new-marker))

(defun escreen-first-window ()
  (cond ((fboundp 'frame-highest-window)
         (funcall 'frame-highest-window))
        ((fboundp 'frame-first-window)
         (funcall 'frame-first-window))
        ((one-window-p)
         (selected-window))
        (t
         (let ((win (selected-window)))
           (while (not (escreen-first-window-p win))
             (setq win (next-window win)))
           win))))

(defun escreen-first-window-p (win)
  (let ((edges (escreen-window-edges win)))
    (and (= (nth 0 edges) 0)
         (= (nth 1 edges) 0))))


(defun escreen-menu ()
  (interactive)
  (escreen-configuration-alist-sort-by-number)
  (let ((escreen-menu-buffer (get-buffer-create "*Escreen List*"))
        alist data-map screen-number)
    ;; Display buffer now so update of screen cofiguration will be correct.
    (display-buffer escreen-menu-buffer)
    ;; Update escreen-configuration-alist to contain up-to-date information
    ;; on current screen, since we'll be displaying data about it.
    (escreen-save-current-screen-configuration)
    (setq alist escreen-configuration-alist)
    (save-excursion
      (set-buffer escreen-menu-buffer)
      (setq buffer-read-only nil)
      (erase-buffer)
      (insert " Screen Buffers\n ------ -------\n")
      (while alist
        (setq screen-data (car alist))
        (setq alist (cdr alist))

        (setq screen-number (escreen-configuration-screen-number screen-data))
        (setq data-map (escreen-configuration-data-map screen-data))

        (if (= screen-number escreen-current-screen-number)
            (insert (format "*%-6d " screen-number))
          (insert (format " %-6d " screen-number)))
        (while data-map
          (insert (if (> (current-column) 0) "" "        ")
                  (escreen-configuration-data-map-critical-buffer-name
                   (escreen-configuration-data-map-critical (car data-map)))
                  "\n")
          (setq data-map (cdr data-map)))
        (insert "\n"))
      (escreen-menu-mode))))

(defun escreen-menu-mode ()
  (fundamental-mode)
  (kill-all-local-variables)
  (setq buffer-undo-list t)
  (setq truncate-lines t)
  (setq buffer-read-only t)
  (setq major-mode 'escreen-menu-mode)
  (setq mode-name "Escreen Menu")
  (run-hooks 'escreen-menu-mode-hook))


;; Install this by doing
;;
;;    (add-hook 'escreen-goto-screen-hook
;;              'escreen-enable-number-mode-if-more-than-one-screen)
;;
;; By doing so, escreen-number-mode is disabled whenever only a single
;; escreen is in use.  The only reason for doing this, however, is to save
;; valuable mode line real estate.
(defun escreen-enable-number-mode-if-more-than-one-screen ()
  (if (> (length (escreen-get-active-screen-numbers)) 1)
      (escreen-number-mode 1)
    (escreen-number-mode -1))
  (escreen-force-mode-line-update t))


;;; Compatiblity tweaks for Emacs 19, 18, and XEmacs.

(defun escreen-window-edges (&rest args)
  (apply (if (fboundp 'window-edges)
             'window-edges
           ;; For XEmacs 19.13 and later.
           'window-pixel-edges)
         args))

(defun escreen-force-mode-line-update (&optional allp)
  (if (fboundp 'force-mode-line-update)
      (force-mode-line-update allp)
    ;; Emacs 18 doesn't have force-mode-line-update
    (and all (save-excursion (set-buffer (other-buffer))))
    (set-buffer-modified-p (buffer-modified-p))))

;; Some versions of emacs 18 did not have `member'.
(defun escreen-member (x y)
  (if (fboundp 'member)
      (member x y)
    (while (and y (not (equal x (car y))))
      (setq y (cdr y)))
    y))

(provide 'escreen)

;;; escreen.el ends here
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.