Changeset - 3edbfb87fe7d
[Not reviewed]
0 5 0
Sergey Pashinin - 11 years ago 2014-07-01 19:44:58
sergey@pashinin.com
Save/restore frames (#11)

Probably works (for me) with usual file buffers
5 files changed with 75 insertions and 24 deletions:
0 comments (0 inline, 0 general)
src/workgroups-commands.el
Show inline comments
 
;;; workgroups-commands --- main commands
 
;;; Commentary:
 
;;; Code:
 

	
 
(require 'cl-lib)
 
(eval-when-compile
 
  (require 'ido)
 
  (require 'iswitchb))
 

	
 
(require 'ring)
 
(require 'workgroups-variables)
 
(require 'workgroups-utils-basic)
 
(require 'workgroups-pickel)
 
(require 'workgroups-functions)
 

	
 
;;; workgroup switching commands
 

	
 
(defun wg-switch-to-workgroup (workgroup &optional noerror)
 
  "Switch to WORKGROUP."
 
  (interactive (list (wg-read-workgroup-name)))
 
  ;; Set a parameter when using ECB
 
  ;; Mark if ECB is active
 
  (if (wg-current-workgroup t)
 
      (wg-set-workgroup-parameter (wg-current-workgroup t) 'ecb (and (boundp 'ecb-minor-mode)
 
                                                                     ecb-minor-mode)))
 
  ;;(wg-set-workgroup-parameter (wg-current-workgroup t) 'ecb-win-config (ecb-current-window-configuration))
 
  ;; (type-of (ecb-current-window-configuration))
 
  ;; (type-of (car (ecb-current-window-configuration)))
 
  ;; (type-of (car (nthcdr 3 (ecb-current-window-configuration))))
 
  ;; (wg-pickelable-or-error (ecb-current-window-configuration))
 
  ;;(ecb-current-window-configuration)
 
  ;;)
 
  (let ((workgroup (wg-get-workgroup-create workgroup))
 
        (current (wg-current-workgroup t)))
 
    (when (and (eq workgroup current) (not noerror))
 
      (error "Already on: %s" (wg-workgroup-name current)))
 
    (when current (push current wg-deactivation-list))
 
    (unwind-protect
 
        (progn
 
          ;; Before switching - turn off ECB
 
          ;; https://github.com/pashinin/workgroups2/issues/34
 
          (if (and (boundp 'ecb-minor-mode)
 
                   ecb-minor-mode
 
                   (equal ecb-frame (selected-frame)))
 
              (let ((ecb-split-edit-window-after-start 'before-deactivation))
 
                (ecb-deactivate)))
 

	
 
          (wg-restore-workgroup workgroup)
 
          (wg-set-previous-workgroup current)
 
          (wg-set-current-workgroup workgroup)
 

	
 
          ;; Save "last-workgroup" to the session params
 
          (if (and (wg-current-session t)
 
                   (wg-current-workgroup t))
 
              (wg-set-session-parameter (wg-current-session t)
 
                                        'last-workgroup
 
                                        (wg-workgroup-name (wg-current-workgroup))))
 

	
 
          ;; If a workgroup had ECB - turn it on
 
          (if (and (boundp 'ecb-minor-mode)
 
                   (not ecb-minor-mode)
 
                   (wg-workgroup-parameter (wg-current-workgroup t) 'ecb nil))
 
              (let ((ecb-split-edit-window-after-start 'before-deactivation))
 
                (ecb-activate)))
 
          ;;(ecb-last-window-config-before-deactivation
 
          ;; (wg-workgroup-parameter (wg-current-workgroup t) 'ecb-win-config nil)))
 

	
 
          (run-hooks 'wg-switch-to-workgroup-hook)
 
          (wg-fontified-message
 
            (:cmd "Switched: ")
 
            (wg-workgroup-name (wg-current-workgroup t))
 
            ))
 
      (when current (pop wg-deactivation-list)))))
 

	
 
(defun wg-switch-to-workgroup-other-frame (workgroup &optional n)
 
  "Switch to WORKGROUP in the frame N places cyclically from `selected-frame'.
 
Use `current-prefix-arg' for N if non-nil.  Otherwise N defaults to 1."
 
  (interactive (list (wg-read-workgroup-name) current-prefix-arg))
 
  (with-selected-frame (wg-cyclic-nth-from-frame (or n 1))
 
    (wg-switch-to-workgroup workgroup)))
 

	
 
(defun wg-switch-to-workgroup-at-index (index)
 
  "Switch to the workgroup at INDEX in `wg-workgroup-list'."
 
  (interactive (list (or current-prefix-arg (wg-read-workgroup-index))))
 
  (let ((wl (wg-workgroup-list-or-error)))
 
    (wg-switch-to-workgroup
 
     (or (nth index wl) (error "There are only %d workgroups" (length wl))))))
 

	
 
(cl-macrolet
 
    ((define-range-of-switch-to-workgroup-at-index (num)
 
       `(progn
 
          ,@(wg-docar (i (wg-range 0 num))
 
              `(defun ,(intern (format "wg-switch-to-workgroup-at-index-%d" i)) ()
 
                 ,(format "Switch to the workgroup at index %d." i)
 
                 (interactive)
 
                 (wg-switch-to-workgroup-at-index ,i))))))
 
  (define-range-of-switch-to-workgroup-at-index 10))
 

	
 
(defun wg-switch-to-cyclic-nth-from-workgroup (workgroup n)
 
  "Switch N workgroups cyclically from WORKGROUP in `wg-workgroup-list.'"
 
  (let ((workgroup-list (wg-workgroup-list-or-error))
 
        (workgroup (wg-get-workgroup workgroup t)))
 
    (wg-switch-to-workgroup
 
     (cond ((not workgroup) (car workgroup-list))
 
           ((= 1 (length workgroup-list)) (error "There's only one workgroup"))
 
           (t (wg-cyclic-nth-from-workgroup workgroup n))))))
 

	
 
(defun wg-switch-to-workgroup-left (&optional workgroup n)
 
  "Switch to the workgroup (- N) places from WORKGROUP in `wg-workgroup-list'.
 
Use `current-prefix-arg' for N if non-nil.  Otherwise N defaults to 1."
 
  (interactive (list nil current-prefix-arg))
 
  (wg-switch-to-cyclic-nth-from-workgroup workgroup (- (or n 1))))
 

	
 
(defun wg-switch-to-workgroup-right (&optional workgroup n)
 
  "Switch to the workgroup N places from WORKGROUP in `wg-workgroup-list'.
 
Use `current-prefix-arg' for N if non-nil.  Otherwise N defaults to 1."
 
  (interactive (list nil current-prefix-arg))
 
  (wg-switch-to-cyclic-nth-from-workgroup workgroup (or n 1)))
 

	
 
(defun wg-switch-to-workgroup-left-other-frame (&optional n)
 
  "Like `wg-switch-to-workgroup-left', but operates on the next frame."
 
  (interactive "p")
 
  (with-selected-frame (wg-cyclic-nth-from-frame (or n 1))
 
    (call-interactively 'wg-switch-to-workgroup-left)))
 

	
 
(defun wg-switch-to-workgroup-right-other-frame (&optional n)
 
  "Like `wg-switch-to-workgroup-right', but operates on the next frame."
 
  (interactive "p")
 
  (with-selected-frame (wg-cyclic-nth-from-frame (or n -1))
 
    (call-interactively 'wg-switch-to-workgroup-right)))
 

	
 
(defun wg-switch-to-previous-workgroup ()
 
  "Switch to the previous workgroup."
 
  (interactive)
 
  (wg-switch-to-workgroup (wg-previous-workgroup)))
 

	
 

	
 

	
 
;;; workgroup creation commands
 

	
 
(defun wg-create-workgroup (name &optional blank)
 
  "Create and add a workgroup named NAME.
 
Optional argument BLANK non-nil (set interactively with a prefix
 
arg) means use a blank, one window window-config.  Otherwise use
 
the current window-configuration.  Keep in mind that even though
 
the current window-config may be used, other parameters of the
 
current workgroup are not copied to the created workgroup.  For
 
that, use `wg-clone-workgroup'."
 
  (interactive (list (wg-read-new-workgroup-name) current-prefix-arg))
 
  (wg-switch-to-workgroup (wg-make-and-add-workgroup name blank))
 
  (wg-fontified-message
 
    (:cmd "Created: ")
 
    (:cur name) "  "
 
    (wg-workgroup-list-display)))
 

	
 
(defun wg-clone-workgroup (workgroup name)
 
  "Create and add a clone of WORKGROUP named NAME.
 
Keep in mind that only WORKGROUP's top-level alist structure is
 
copied, so destructive operations on the keys or values of
 
WORKGROUP will be reflected in the clone, and vice-versa.  Be
 
safe -- don't mutate them."
 
  (interactive (list nil (wg-read-new-workgroup-name)))
 
  (let* ((workgroup (wg-get-workgroup workgroup))
 
         (clone (wg-copy-workgroup workgroup)))
 
    (setf (wg-workgroup-name clone) name
 
          (wg-workgroup-uid clone) (wg-generate-uid))
 
    (when (wg-check-and-add-workgroup clone)
 
      (wg-flag-workgroup-modified clone))
 
    (wg-set-workgroup-working-wconfig
 
     clone (wg-workgroup-working-wconfig workgroup))
 
    (wg-switch-to-workgroup clone)
 
    (wg-fontified-message
 
      (:cmd "Cloned: ")
 
      (:cur (wg-workgroup-name workgroup))
 
      (:msg " to ")
 
      (:cur name) "  "
 
      (wg-workgroup-list-display))))
 

	
 

	
 

	
 
;;; workgroup killing commands
 

	
 
(defun wg-wconfig-kill-ring ()
 
  "Return `wg-wconfig-kill-ring', creating it first if necessary."
 
  (or wg-wconfig-kill-ring
 
      (setq wg-wconfig-kill-ring (make-ring wg-wconfig-kill-ring-max))))
 

	
 
(defun wg-add-to-wconfig-kill-ring (wconfig)
 
  "Add WCONFIG to `wg-wconfig-kill-ring'."
 
  (ring-insert (wg-wconfig-kill-ring) wconfig))
 

	
 
(defun wg-kill-workgroup (&optional workgroup)
 
  "Kill WORKGROUP, saving its working-wconfig to the kill ring."
 
  (interactive)
 
  (let* ((workgroup (wg-get-workgroup workgroup))
 
         (to (or (wg-previous-workgroup t)
 
                 (wg-cyclic-nth-from-workgroup workgroup))))
 
    (wg-add-to-wconfig-kill-ring (wg-workgroup-working-wconfig workgroup))
 
    (wg-delete-workgroup workgroup)
 
    (if (eq workgroup to) (wg-restore-wconfig (wg-make-blank-wconfig))
 
      (wg-switch-to-workgroup to))
 
    (wg-fontified-message
 
      (:cmd "Killed: ")
 
      (:cur (wg-workgroup-name workgroup)) "  "
 
      (wg-workgroup-list-display))))
 

	
 
(defun wg-kill-ring-save-base-wconfig (&optional workgroup)
 
  "Save WORKGROUP's base wconfig to the kill ring."
 
  (interactive)
 
  (let ((workgroup (wg-get-workgroup workgroup)))
 
    (wg-add-to-wconfig-kill-ring (wg-workgroup-base-wconfig workgroup))
 
    (wg-fontified-message
 
      (:cmd "Saved: ")
 
      (:cur (wg-workgroup-name workgroup))
 
      (:cur "'s ")
 
      (:msg "base wconfig to the kill ring"))))
 

	
 
(defun wg-kill-ring-save-working-wconfig (&optional workgroup)
 
  "Save WORKGROUP's working-wconfig to `wg-wconfig-kill-ring'."
 
  (interactive)
 
  (let ((workgroup (wg-get-workgroup workgroup)))
 
    (wg-add-to-wconfig-kill-ring (wg-workgroup-working-wconfig workgroup))
 
    (wg-fontified-message
 
      (:cmd "Saved: ")
 
      (:cur (wg-workgroup-name workgroup))
 
      (:cur "'s ")
 
      (:msg "working-wconfig to the kill ring"))))
 

	
 
(defun wg-yank-wconfig ()
 
  "Restore a wconfig from `wg-wconfig-kill-ring'.
 
Successive yanks restore wconfigs sequentially from the kill
 
ring, starting at the front."
 
  (interactive)
 
  (when (zerop (ring-length (wg-wconfig-kill-ring)))
 
    (error "The kill-ring is empty"))
 
  (let ((pos (if (not (eq real-last-command 'wg-yank-wconfig)) 0
 
               (1+ (or (get 'wg-yank-wconfig :position) 0)))))
 
    (put 'wg-yank-wconfig :position pos)
 
    (wg-restore-wconfig-undoably (ring-ref (wg-wconfig-kill-ring) pos))
 
    (wg-fontified-message
 
      (:cmd "Yanked: ")
 
      (:msg (format "%S" pos)) "  "
 
      (wg-workgroup-list-display))))
 

	
 
(defun wg-kill-workgroup-and-buffers (&optional workgroup)
 
  "Kill WORKGROUP and the buffers in its working-wconfig."
 
  (interactive)
 
  (let* ((workgroup (wg-get-workgroup workgroup))
 
         (bufs (save-window-excursion
 
                 (wg-restore-workgroup workgroup)
 
                 (mapcar #'window-buffer (window-list)))))
 
    (wg-kill-workgroup workgroup)
 
    (mapc #'kill-buffer bufs)
 
    (wg-fontified-message
 
      (:cmd "Killed: ")
 
      (:cur (wg-workgroup-name workgroup))
 
      (:msg " and its buffers ") "\n"
 
      (wg-workgroup-list-display))))
 
@@ -485,354 +494,373 @@ and switch to the next buffer in the buffer-list-filter."
 
    (wg-bury-buffer buffer)))
 

	
 
(defun wg-associate-buffer-with-workgroup (&optional workgroup buffer weak)
 
  "Associate BUFFER with WORKGROUP.
 
WEAK non-nil means weakly associate BUFFER."
 
  (interactive (list nil nil current-prefix-arg))
 
  (let* ((workgroup (wg-get-workgroup workgroup))
 
         (buffer (or buffer (current-buffer)))
 
         (bname (buffer-name buffer))
 
         (wgname (wg-workgroup-name workgroup)))
 
    (if (wg-workgroup-associate-bufobj workgroup buffer weak)
 
        (wg-message "%s-associated %S with %s"
 
                    (if weak "Weakly" "Strongly") bname wgname)
 
      (wg-message "%S is already associated with %s" bname wgname))))
 

	
 
(defun wg-associate-visible-buffers-with-workgroup (&optional workgroup weak)
 
  "Associate all buffers visible in `selected-frame' with WORKGROUP.
 
WEAK non-nil means weakly associate them.  Otherwise strongly
 
associate them."
 
  (interactive (list nil current-prefix-arg))
 
  (let ((workgroup (wg-get-workgroup workgroup))
 
        (buffers (mapcar 'window-buffer (window-list))))
 
    (dolist (buffer buffers)
 
      (wg-workgroup-associate-bufobj workgroup buffer weak))
 
    (wg-fontified-message
 
      (:cmd (format "%s associated: " (if weak "Weakly" "Strongly")))
 
      (wg-buffer-list-display buffers))))
 

	
 
(defun wg-dissociate-buffer-from-workgroup (&optional workgroup buffer)
 
  "Dissociate BUFFER from WORKGROUP."
 
  (interactive (list nil nil))
 
  (let ((workgroup (wg-get-workgroup workgroup))
 
        (buffer (or buffer (current-buffer))))
 
    (wg-message
 
     (if (wg-workgroup-dissociate-bufobj workgroup buffer)
 
         "Dissociated %S from %s" "%S isn't associated with %s")
 
     (wg-buffer-name buffer)
 
     (wg-workgroup-name workgroup))))
 

	
 
(defun wg-restore-workgroup-associated-buffers (&optional workgroup)
 
  "Restore all the buffers associated with WORKGROUP that can be restored."
 
  (interactive)
 
  (let* ((workgroup (wg-get-workgroup workgroup))
 
         (restored-buffers (wg-restore-workgroup-associated-buffers-internal
 
                            workgroup)))
 
    (wg-fontified-message
 
      (:cmd "Restored: ")
 
      (wg-buffer-list-display restored-buffers))))
 

	
 
(defun wg-cycle-buffer-association-type ()
 
  "Cycle the current buffer's association type in the current workgroup.
 
See `wg-workgroup-cycle-bufobj-association-type' for details."
 
  (interactive)
 
  (let* ((workgroup (wg-current-workgroup))
 
         (buffer (current-buffer))
 
         (type (wg-workgroup-cycle-bufobj-association-type workgroup buffer)))
 
    (force-mode-line-update)
 
    (wg-fontified-message
 
      (:cur (buffer-name buffer))
 
      (:cmd (cl-case type
 
              (strong " strongly associated with ")
 
              (weak " weakly associated with ")
 
              (otherwise " unassociated with ")))
 
      (:cur (wg-workgroup-name workgroup)))))
 

	
 
(defun wg-dissociate-weakly-associated-buffers (&optional workgroup)
 
  "Dissociate from the current workgroup all weakly associated buffers."
 
  (interactive)
 
  (let ((workgroup (wg-get-workgroup workgroup)))
 
    (wg-workgroup-dissociate-weakly-associated-buffers workgroup)
 
    (wg-fontified-message
 
      (:cmd "Remaining buffers: ")
 
      (wg-buffer-list-display (wg-workgroup-associated-buffers workgroup)))))
 

	
 

	
 

	
 
;;; window-tree commands
 
;;
 
;; TODO: These are half-hearted.  Clean them up; allow specification of the
 
;; window-tree depth at which to operate; add complex window creation commands;
 
;; and add window splitting, deletion and locking commands.
 

	
 
(defun wg-transpose-window-internal (workgroup offset)
 
  "Move `selected-window' by OFFSET in its wlist."
 
  (wg-restore-wconfig-undoably
 
   (wg-wconfig-move-window
 
    (wg-workgroup-working-wconfig
 
     (wg-get-workgroup workgroup))
 
    offset)))
 

	
 
(defun wg-backward-transpose-window (&optional workgroup offset)
 
  "Move `selected-window' backward by OFFSET in its wlist."
 
  (interactive (list nil current-prefix-arg))
 
  (wg-transpose-window-internal workgroup (or offset -1)))
 

	
 
(defun wg-transpose-window (&optional workgroup offset)
 
  "Move `selected-window' forward by OFFSET in its wlist."
 
  (interactive (list nil current-prefix-arg))
 
  (wg-transpose-window-internal workgroup (or offset 1)))
 

	
 
(defun wg-reverse-frame-horizontally (&optional workgroup)
 
  "Reverse the order of all horizontally split wtrees."
 
  (interactive)
 
  (wg-restore-wconfig-undoably
 
   (wg-reverse-wconfig
 
    (wg-workgroup-working-wconfig
 
     (wg-get-workgroup workgroup)))))
 

	
 
(defun wg-reverse-frame-vertically (&optional workgroup)
 
  "Reverse the order of all vertically split wtrees."
 
  (interactive)
 
  (wg-restore-wconfig-undoably
 
   (wg-reverse-wconfig
 
    (wg-workgroup-working-wconfig
 
     (wg-get-workgroup workgroup))
 
    t)))
 

	
 
(defun wg-reverse-frame-horizontally-and-vertically (&optional workgroup)
 
  "Reverse the order of all wtrees."
 
  (interactive)
 
  (wg-restore-wconfig-undoably
 
   (wg-reverse-wconfig
 
    (wg-workgroup-working-wconfig
 
     (wg-get-workgroup workgroup))
 
    'both)))
 

	
 
(defun wg-toggle-window-dedicated-p ()
 
  "Toggle `window-dedicated-p' in `selected-window'."
 
  (interactive)
 
  (set-window-dedicated-p nil (not (window-dedicated-p)))
 
  (force-mode-line-update t)
 
  (wg-fontified-message
 
    (:cmd "Window:")
 
    (:cur (concat (unless (window-dedicated-p) " not") " dedicated"))))
 

	
 

	
 

	
 
;;; misc commands
 

	
 
(defun wg-rename-workgroup (workgroup newname)
 
  "Rename WORKGROUP to NEWNAME."
 
  (interactive (list nil (wg-read-new-workgroup-name "New name: ")))
 
  (let* ((workgroup (wg-get-workgroup workgroup))
 
         (oldname (wg-workgroup-name workgroup)))
 
    (setf (wg-workgroup-name workgroup) newname)
 
    (wg-flag-workgroup-modified workgroup)
 
    (wg-fontified-message
 
      (:cmd "Renamed: ")
 
      (:cur oldname)
 
      (:msg " to ")
 
      (:cur (wg-workgroup-name workgroup)))))
 

	
 
(defun wg-reset (&optional force)
 
  "Reset Workgroups.
 
Resets all frame parameters, buffer-local vars, the current
 
Workgroups session object, etc."
 
  (interactive "P")
 
  (unless (or force wg-no-confirm-on-destructive-operation
 
              (y-or-n-p "Really reset Workgroups? "))
 
    (error "Canceled"))
 
  (wg-reset-internal)
 
  (wg-fontified-message
 
    (:cmd "Reset: ")
 
    (:msg "Workgroups")))
 

	
 

	
 

	
 
;;; file commands
 

	
 
(defun wg-read-session-save-file-name ()
 
  "Read and return a new session filename."
 
  (read-file-name "Save session as: "))
 

	
 
(defun wg-write-session-file (filename &optional confirm)
 
  "Write the current session into file FILENAME.
 
This makes the session visit that file, and marks it as not modified.
 

	
 
If optional second arg CONFIRM is non-nil, this function asks for
 
confirmation before overwriting an existing file.  Interactively,
 
confirmation is required unless you supply a prefix argument.
 

	
 
Think of it as `write-file' for Workgroups sessions."
 
  (interactive (list (wg-read-session-save-file-name)
 
                     (not current-prefix-arg)))
 
  (when (and confirm (file-exists-p filename))
 
    (unless (y-or-n-p (format "File `%s' exists; overwrite? " filename))
 
      (error "Cancelled")))
 
  (unless (file-writable-p filename)
 
    (error "File %s can't be written to" filename))
 
  (wg-perform-session-maintenance)
 
  (setf (wg-session-file-name (wg-current-session)) filename)
 
  (setf (wg-session-version (wg-current-session)) wg-version)
 
  (if wg-control-frames
 
      (wg-save-frames))
 
  (wg-write-sexp-to-file
 
   (wg-pickel-all-session-parameters (wg-current-session))
 
   filename)
 
  (wg-mark-everything-unmodified)
 
  (wg-fontified-message (:cmd "Wrote: ") (:file filename)))
 

	
 
(defun wg-determine-session-save-file-name ()
 
  "Return the filename in which to save the session."
 
  (or (wg-session-file-name (wg-current-session))
 
      (and wg-use-default-session-file wg-default-session-file)))
 

	
 
(defun wg-save-session (&optional force)
 
  "Save the current Workgroups session if it's been modified.
 
Think of it as `save-buffer' for Workgroups sessions.  Optional
 
argument FORCE non-nil, or interactively with a prefix arg, save
 
the session regardless of whether it's been modified."
 
  (interactive "P")
 
  (if (and (not (wg-modified-p)) (not force))
 
      (wg-message "(The session is unmodified)")
 
    (wg-write-session-file
 
     (or (wg-determine-session-save-file-name)
 
         (wg-read-session-save-file-name)))))
 

	
 
(defun wg-query-and-save-if-modified ()
 
  "Query for save when `wg-modified-p'."
 
  (or (not (wg-modified-p))
 
      (when (y-or-n-p "Save modified workgroups? ")
 
        (wg-save-session))))
 

	
 
(defun wg-save-session-on-exit (behavior)
 
  "Perform session-saving operations based on BEHAVIOR."
 
  (cl-case behavior
 
    (ask (wg-query-and-save-if-modified))
 
    (save
 
     (if (wg-determine-session-save-file-name)
 
         (wg-save-session)
 
       (wg-query-and-save-if-modified)))))
 

	
 
(defun wg-save-frames ()
 
  "Save opened frames as a session parameter.
 
Exclude `selected-frame' and daemon one (if any).
 
http://stackoverflow.com/questions/21151992/why-emacs-as-daemon-gives-1-more-frame-than-is-opened"
 
  (interactive)
 
  (let ((fl (frame-list)))
 
    (mapc (lambda (frame)
 
            (if (string-equal "initial_terminal" (terminal-name frame))
 
                (delete frame fl))) fl)
 
    (setq fl (delete (selected-frame) fl))
 
    (if (wg-current-session t)
 
          (wg-set-session-parameter (wg-current-session t)
 
                                    'frame-list
 
                                    (mapcar 'wg-frame-to-wconfig fl)))))
 

	
 
(defun wg-find-session-file (filename)
 
  "Load a session visiting FILENAME, creating one if none already exists."
 
  (interactive "FFind session file: ")
 
  (cond ((file-exists-p filename)
 
         (let ((session (wg-read-sexp-from-file filename)))
 
           (unless (wg-session-p session)
 
             (error "%S is not a Workgroups session file." filename))
 
           (setf (wg-session-file-name session) filename)
 
           (wg-reset-internal (wg-unpickel-session-parameters session)))
 
         (wg-awhen (and wg-switch-to-first-workgroup-on-find-session-file
 
                        (wg-workgroup-list))
 
           (if (and wg-open-this-wg
 
                    (member wg-open-this-wg (wg-workgroup-names)))
 
               (wg-switch-to-workgroup wg-open-this-wg)
 
             (if (and wg-load-last-workgroup
 
                      (member (wg-session-parameter (wg-current-session t) 'last-workgroup)
 
                              (wg-workgroup-names)))
 
                 (wg-switch-to-workgroup
 
                  (wg-session-parameter (wg-current-session t) 'last-workgroup))
 
               (wg-switch-to-workgroup (car it)))
 
             ))
 
         (if wg-control-frames
 
             (wg-restore-frames))
 
         (wg-fontified-message (:cmd "Loaded: ") (:file filename)))
 
        (t
 
         (wg-query-and-save-if-modified)
 
         (wg-reset-internal (wg-make-session :file-name filename))
 
         (wg-fontified-message
 
           (:cmd "(New Workgroups session file)")))))
 

	
 
(defun wg-find-file-in-new-workgroup (filename)
 
  "Create a new blank workgroup and find file FILENAME in it."
 
  (interactive "FFind file in new workgroup: ")
 
  (wg-create-workgroup (file-name-nondirectory filename) t)
 
  (find-file filename))
 

	
 
(defun wg-find-file-read-only-in-new-workgroup (filename)
 
  "Create a new workgroup and find file FILENAME read-only in it."
 
  (interactive "FFind file read only in new workgroup: ")
 
  (wg-create-workgroup (file-name-nondirectory filename) t)
 
  (find-file-read-only filename))
 

	
 
(defun wg-dired-in-new-workgroup (dirname &optional switches)
 
  "Create a workgroup and open DIRNAME in dired with SWITCHES."
 
  (interactive (list (read-directory-name "Dired (directory): ")
 
                     current-prefix-arg))
 
  (wg-create-workgroup dirname)
 
  (dired dirname switches))
 

	
 

	
 

	
 
;;; toggle commands
 

	
 
(defun wg-toggle-and-message (symbol)
 
  "Toggle SYMBOL's truthiness and message the new value."
 
  (wg-fontified-message
 
    (:cmd (format "%s: " symbol))
 
    (:msg (format "%s" (wg-toggle symbol)))))
 

	
 
(defun wg-toggle-buffer-list-filtration ()
 
  "Toggle `wg-buffer-list-filtration-on'."
 
  (interactive)
 
  (wg-toggle-and-message 'wg-buffer-list-filtration-on))
 

	
 
(defun wg-toggle-mode-line-display ()
 
  "Toggle `wg-mode-line-display-on'."
 
  (interactive)
 
  (wg-toggle-and-message 'wg-mode-line-display-on))
 

	
 

	
 

	
 
;;; echo commands
 

	
 
(defun wg-echo-current-workgroup ()
 
  "Display the name of the current workgroup in the echo area."
 
  (interactive)
 
  (wg-fontified-message
 
    (:cmd "Current: ")
 
    (:cur (wg-workgroup-name (wg-current-workgroup)))))
 

	
 
(defun wg-echo-all-workgroups ()
 
  "Display the names of all workgroups in the echo area."
 
  (interactive)
 
  (wg-fontified-message
 
    (:cmd "Workgroups: ")
 
    (wg-workgroup-list-display)))
 

	
 
(defun wg-echo-time ()
 
  "Echo the current time.  Optionally includes `battery' info."
 
  (interactive)
 
  (wg-message ;; Pass through format to escape the % in `battery'
 
   "%s" (wg-fontify
 
          (:cmd "Current time: ")
 
          (:msg (format-time-string wg-time-format))
 
          (when (and wg-display-battery (fboundp 'battery))
 
            (wg-fontify "\n" (:cmd "Battery: ") (:msg (battery)))))))
 

	
 
(defun wg-echo-version ()
 
  "Echo Workgroups' current version number."
 
  (interactive)
 
  (wg-fontified-message
 
    (:cmd "Workgroups version: ")
 
    (:msg wg-version)))
 

	
 
(defun wg-echo-last-message ()
 
  "Echo the last message Workgroups sent to the echo area.
 
The string is passed through a format arg to escape %'s."
 
  (interactive)
 
  (message "%s" wg-last-message))
 

	
 

	
 

	
 
;;; help commands
 

	
 
(defun wg-help ()
 
  "Just call `apropos-command' on \"^wg-\".
 
There used to be a bunch of help-buffer construction stuff here,
 
including a `wg-help' variable that basically duplicated every
 
command's docstring;  But why, when there's `apropos-command'?"
 
  (interactive)
 
  (apropos-command "^wg-"))
 

	
 

	
 
(require 'workgroups-commands-minibuffer)
 

	
 
(provide 'workgroups-commands)
src/workgroups-pickel.el
Show inline comments
 
;;; workgroups-pickel.el --- Elisp object serdes used by Workgroups
 
;;
 
;; Copyright (C) 2010, 2011 tlh
 
;;
 
;; Author: tlh <thunkout at gmail dot com>
 
;; Keywords: serialization deserialization serdes
 
;; Homepage: https://github.com/tlh/workgroups.el
 
;; Version   1.0.0
 

	
 
;; 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 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, write to the Free Software
 
;; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 
;; USA
 

	
 
;;; Commentary:
 
;;
 
;;; Code:
 

	
 
(require 'cl-lib)
 
(require 'workgroups-utils-basic)
 

	
 

	
 
;;; vars
 

	
 
(defvar wg-pickel-identifier '~pickel!~
 
  "Symbol identifying a stream as a pickel.")
 

	
 
(defvar wg-pickel-pickelable-types
 
  '(integer
 
    float
 
    symbol
 
    string
 
    cons
 
    vector
 
    hash-table
 
    buffer
 
    ;;window-configuration
 
    ;;frame
 
    ;;window
 
    ;;process
 
    )
 
  "Types pickel can serialize.")
 

	
 
(defvar wg-pickel-object-serializers
 
  '((integer    . identity)
 
    (float      . identity)
 
    (string     . identity)
 
    (symbol     . wg-pickel-symbol-serializer)
 
    (cons       . wg-pickel-cons-serializer)
 
    (vector     . wg-pickel-vector-serializer)
 
    (hash-table . wg-pickel-hash-table-serializer)
 
    ;;(window-configuration   . wg-pickel-window-configuration-serializer)
 
    (buffer     . wg-pickel-buffer-serializer))
 
  "Alist mapping types to object serialization functions.")
 

	
 
(defvar wg-pickel-link-serializers
 
  '((cons       . wg-pickel-cons-link-serializer)
 
    (vector     . wg-pickel-vector-link-serializer)
 
    (hash-table . wg-pickel-hash-table-link-serializer))
 
  "Alist mapping types to link serialization functions.")
 

	
 
(defvar wg-pickel-object-deserializers
 
  '((s . wg-pickel-deserialize-uninterned-symbol)
 
    (c . wg-pickel-deserialize-cons)
 
    (v . wg-pickel-deserialize-vector)
 
    (h . wg-pickel-deserialize-hash-table)
 
    (b . wg-pickel-deserialize-buffer))
 
  ;; (f . wg-pickel-deserialize-frame))
 
  "Alist mapping type keys to object deserialization functions.")
 

	
 
(defvar wg-pickel-link-deserializers
 
  `((c . wg-pickel-cons-link-deserializer)
 
    (v . wg-pickel-vector-link-deserializer)
 
    (h . wg-pickel-hash-table-link-deserializer))
 
  "Alist mapping type keys to link deserialization functions.")
 

	
 

	
 

	
 
;;; errors and predicates
 

	
 
(put 'wg-pickel-unpickelable-type-error
 
     'error-conditions
 
     '(error wg-pickel-errors wg-pickel-unpickelable-type-error))
 

	
 
(put 'wg-pickel-unpickelable-type-error
 
     'error-message
 
     "Attemp to pickel unpickelable type")
 

	
 
(defun wg-pickelable-or-error (obj)
 
  "Error when OBJ isn't pickelable."
 
  (unless (memq (type-of obj) wg-pickel-pickelable-types)
 
    (signal 'wg-pickel-unpickelable-type-error
 
            (format "Can't pickel objects of type: %S" (type-of obj))))
 
  (cl-typecase obj
 
    (cons
 
     (wg-pickelable-or-error (car obj))
 
     (wg-pickelable-or-error (cdr obj)))
 
    (vector
 
     (cl-map nil 'wg-pickelable-or-error obj))
 
    (hash-table
 
     (wg-dohash (key value obj)
 
       (wg-pickelable-or-error key)
 
       (wg-pickelable-or-error value)))))
 

	
 
(defun wg-pickelable-p (obj)
 
  (condition-case err
 
      (progn (wg-pickelable-or-error obj) t)
 
    (wg-pickel-unpickelable-type-error nil)))
 

	
 
(defun wg-pickel-p (obj)
 
  "Return t when OBJ is a pickel, nil otherwise."
 
  (and (consp obj) (eq (car obj) wg-pickel-identifier)))
 

	
 

	
 

	
 
;; accessor functions
 

	
 
(defun wg-pickel-object-serializer (obj)
 
  "Return the object serializer for the `type-of' OBJ."
 
  (or (wg-aget wg-pickel-object-serializers (type-of obj))
 
      (error "Invalid type: %S" (type-of obj))))
 

	
 
(defun wg-pickel-link-serializer (obj)
 
  "Return the link serializer for the `type-of' OBJ."
 
  (wg-aget wg-pickel-link-serializers (type-of obj)))
 

	
 
(defun wg-pickel-object-deserializer (key)
 
  "Return the object deserializer for type key KEY, or error."
 
  (or (wg-aget wg-pickel-object-deserializers key)
 
      (error "Invalid object deserializer key: %S" key)))
 

	
 
(defun wg-pickel-link-deserializer (key)
 
  "Return the link deserializer for type key KEY, or error."
 
  (or (wg-aget wg-pickel-link-deserializers key)
 
      (error "Invalid link deserializer key: %S" key)))
 

	
 

	
 

	
 
;;; bindings
 

	
 
(defun wg-pickel-make-bindings-table (obj)
 
  "Return a table binding unique subobjects of OBJ to ids."
 
  (let ((binds (make-hash-table :test 'eq))
 
        (id -1))
 
    (cl-labels
 
     ((inner (obj)
 
           (unless (gethash obj binds)
 
              (puthash obj (cl-incf id) binds)
 
              (cl-case (type-of obj)
 
                (cons
 
                 (inner (car obj))
 
                 (inner (cdr obj)))
 
                (vector
 
                 (dotimes (idx (length obj))
 
                   (inner (aref obj idx))))
 
                (hash-table
 
                 (wg-dohash (key val obj)
 
                   (inner key)
 
                   (inner val)))))))
 
      (inner obj)
 
      binds)))
 

	
 

	
 

	
 
;;; object serialization
 

	
 
(defun wg-pickel-symbol-serializer (symbol)
 
  "Return SYMBOL's serialization."
 
  (cond ((eq symbol t) t)
 
        ((eq symbol nil) nil)
 
        ((intern-soft symbol) symbol)
 
        (t (list 's (symbol-name symbol)))))
 

	
 
(defun wg-pickel-cons-serializer (cons)
 
  "Return CONS's serialization."
 
  (list 'c))
 

	
 
(defun wg-pickel-vector-serializer (vector)
 
  "Return VECTOR's serialization."
 
  (list 'v (length vector)))
 

	
 
(defun wg-pickel-hash-table-serializer (table)
 
  "Return HASH-TABLE's serialization."
 
  (list 'h
 
        (hash-table-test table)
 
        (hash-table-size table)
 
        (hash-table-rehash-size table)
 
        (hash-table-rehash-threshold table)
 
        (hash-table-weakness table)))
 

	
 
(defun wg-pickel-window-configuration-serializer (wc)
 
  "Return Window configuration WC's serialization."
 
  (list 'wc
 
        1))
 

	
 
(defun wg-pickel-buffer-serializer (buffer)
 
  "Return BUFFER's UID in workgroups buffer list."
 
  (list 'b (wg-add-buffer-to-buf-list buffer)))
 

	
 
(defun wg-pickel-serialize-objects (binds)
 
  "Return a list of serializations of the objects in BINDS."
 
  (let (result)
 
    (wg-dohash (obj id binds result)
 
      (setq result
 
            (nconc (list id (funcall (wg-pickel-object-serializer obj) obj))
 
                   result)))))
 

	
 

	
 

	
 
;;; link serialization
 

	
 
(defun wg-pickel-cons-link-serializer (cons binds)
 
  "Return the serialization of CONS's links in BINDS."
 
  (list 'c
 
        (gethash cons binds)
 
        (gethash (car cons) binds)
 
        (gethash (cdr cons) binds)))
 

	
 
(defun wg-pickel-vector-link-serializer (vector binds)
 
  "Return the serialization of VECTOR's links in BINDS."
 
  (let (result)
 
    (dotimes (i (length vector) result)
 
      (setq result
 
            (nconc (list 'v
 
                         (gethash vector binds)
 
                         i
 
                         (gethash (aref vector i) binds))
 
                   result)))))
 

	
 
(defun wg-pickel-hash-table-link-serializer (table binds)
 
  "Return the serialization of TABLE's links in BINDS."
 
  (let (result)
 
    (wg-dohash (key value table result)
 
      (setq result
 
            (nconc (list 'h
 
                         (gethash key binds)
 
                         (gethash value binds)
 
                         (gethash table binds))
 
                   result)))))
 

	
 
(defun wg-pickel-serialize-links (binds)
 
  "Return a list of serializations of the links between objects in BINDS."
 
  (let (result)
 
    (wg-dohash (obj id binds result)
 
      (wg-awhen (wg-pickel-link-serializer obj)
 
        (setq result (nconc (funcall it obj binds) result))))))
 

	
 

	
 

	
 
;;; object deserialization
 

	
 
(defun wg-pickel-deserialize-uninterned-symbol (name)
 
  "Return a new uninterned symbol from NAME."
 
  (make-symbol name))
 

	
 
(defun wg-pickel-deserialize-cons ()
 
  "Return a new cons cell initialized to nil."
 
  (cons nil nil))
 

	
 
(defun wg-pickel-deserialize-vector (length)
src/workgroups-restore.el
Show inline comments
 
;;; workgroups-restore --- Functions to restore buffers and frames
 
;;; Commentary:
 
;;
 
;; TIPS
 
;;---------
 
;; Each Emacs frame ("window") can contain several workgroups.
 
;; WCONFIG is settings for each workgroup (buffers, parameters,...)
 
;;
 
;; So restoring WCONFIG using `wg-restore-wconfig' is restoring buffers
 
;; for current workgroup. Generally speaking each Workgroup can have
 
;; several WCONFIGs.
 
;;
 
;;; Code:
 

	
 
(require 'workgroups-variables)
 

	
 
(defun wg-restore-default-buffer ()
 
  "Switch to `wg-default-buffer'."
 
  (switch-to-buffer wg-default-buffer t))
 

	
 
(defun wg-restore-existing-buffer (buf)
 
  "Switch to and return BUF's referrent (some live buffer) if it exists."
 
  (wg-awhen (wg-find-buf-in-buffer-list buf (buffer-list))
 
    (switch-to-buffer it t)
 
    (wg-set-buffer-uid-or-error (wg-buf-uid buf))
 
    it))
 

	
 
(defun wg-restore-file-buffer (buf)
 
  "Restore BUF by finding its file.  Return the created buffer.
 
If BUF's file doesn't exist, call `wg-restore-default-buffer'"
 
  (wg-when-let ((file-name (wg-buf-file-name buf)))
 
    (when (or wg-restore-remote-buffers
 
              (not (file-remote-p file-name)))
 
      (cond ((file-exists-p file-name)
 
             (find-file file-name)
 
             (rename-buffer (wg-buf-name buf) t)
 
             (wg-set-buffer-uid-or-error (wg-buf-uid buf))
 
             (when wg-restore-mark
 
               (set-mark (wg-buf-mark buf))
 
               (deactivate-mark))
 
             (wg-deserialize-buffer-local-variables buf)
 
             (current-buffer))
 
            (t
 
             ;; try directory
 
             (if (not (file-remote-p file-name))
 
                 (if (file-directory-p (file-name-directory file-name))
 
                     (progn
 
                       (dired (file-name-directory file-name))
 
                       (current-buffer))
 
                   (progn
 
                     (message "Attempt to restore nonexistent file: %S" file-name)
 
                     nil))
 
               nil)
 
             )))))
 

	
 
(defun wg-restore-special-buffer (buf)
 
  "Restore a buffer BUF with DESERIALIZER-FN."
 
  (wg-when-let
 
      ((special-data (wg-buf-special-data buf))
 
       (buffer (save-window-excursion
 
                 (condition-case err
 
                     (funcall (car special-data) buf)
 
                   (error (message "Error deserializing %S: %S"
 
                                   (wg-buf-name buf) err)
 
                          nil)))))
 
    (switch-to-buffer buffer t)
 
    (wg-set-buffer-uid-or-error (wg-buf-uid buf))
 
    buffer))
 

	
 
(defun wg-restore-buffer (buf)
 
  "Restore BUF and return it."
 
  (let (wg-buffer-auto-association-on)
 
    (or (wg-restore-existing-buffer buf)
 
        (wg-restore-special-buffer buf)
 
        (wg-restore-file-buffer buf)
 
        (progn (wg-restore-default-buffer) nil))))
 

	
 
(defun wg-restore-window-positions (win &optional window)
 
  "Restore various positions in WINDOW from their values in WIN."
 
  (let ((window (or window (selected-window))))
 
    (wg-with-slots win
 
        ((win-point wg-win-point)
 
         (win-start wg-win-start)
 
         (win-hscroll wg-win-hscroll))
 
      (set-window-start window win-start t)
 
      (set-window-hscroll window win-hscroll)
 
      (set-window-point
 
       window
 
       (cond ((not wg-restore-point) win-start)
 
             ((eq win-point :max) (point-max))
 
             (t win-point)))
 
      (when (>= win-start (point-max)) (recenter)))))
 

	
 
(defun wg-restore-window (win)
 
  "Restore WIN in `selected-window'."
 
  (let ((selwin (selected-window))
 
        (buf (wg-find-buf-by-uid (wg-win-buf-uid win))))
 
    (if (not buf) (wg-restore-default-buffer)
 
      (when (wg-restore-buffer buf)
 
        (wg-restore-window-positions win selwin)
 
        (when wg-restore-window-dedicated-p
 
          (set-window-dedicated-p selwin (wg-win-dedicated win)))))))
 

	
 
(defun wg-reset-window-tree ()
 
  "Delete all but one window in `selected-frame', and reset
 
various parameters of that window in preparation for restoring
 
a wtree."
 
  (delete-other-windows)
 
  (set-window-dedicated-p nil nil))
 

	
 
(defun wg-restore-window-tree-helper (w)
 
  "Recursion helper for `wg-restore-window-tree'."
 
  (if (wg-wtree-p w)
 
      (cl-loop with dir = (wg-wtree-dir w)
 
               for (win . rest) on (wg-wtree-wlist w)
 
               do (when rest (split-window nil (wg-w-size win dir) (not dir)))
 
               do (wg-restore-window-tree-helper win))
 
    (wg-restore-window w)
 
    (when (wg-win-selected w)
 
      (setq wg-window-tree-selected-window (selected-window)))
 
    (when (wg-win-minibuffer-scroll w)
 
      (setq minibuffer-scroll-window (selected-window)))
 
    (other-window 1)))
 

	
 
(defun wg-restore-window-tree (wtree)
 
  "Restore WTREE in `selected-frame'."
 
  (let ((window-min-width wg-window-min-width)
 
        (window-min-height wg-window-min-height)
 
        (wg-window-tree-selected-window nil))
 
    (wg-reset-window-tree)
 
    (wg-restore-window-tree-helper wtree)
 
    (wg-awhen wg-window-tree-selected-window (select-window it))))
 

	
 
(defun wg-wconfig-restore-frame-position (wconfig)
 
  "Restore `selected-frame's position from WCONFIG."
 
(defun wg-wconfig-restore-frame-position (wconfig &optional frame)
 
  "Use WCONFIG to restore FRAME's position.
 
If frame is nil then `selected-frame'."
 
  (wg-when-let ((left (wg-wconfig-left wconfig))
 
                (top (wg-wconfig-top wconfig)))
 
    ;; Check that arguments are integers
 
    ;; Problem: https://github.com/pashinin/workgroups2/issues/15
 
    (if (and (integerp left)
 
             (integerp top))
 
        (set-frame-position (selected-frame) left top))))
 
        (set-frame-position frame left top))))
 

	
 
(defun wg-wconfig-restore-scroll-bars (wconfig)
 
  "Restore `selected-frame's scroll-bar settings from WCONFIG."
 
  (set-frame-parameter
 
   nil 'vertical-scroll-bars (wg-wconfig-vertical-scroll-bars wconfig))
 
  (set-frame-parameter
 
   nil 'scroll-bar-width (wg-wconfig-scroll-bar-width wconfig)))
 

	
 
;;(defun wg-wconfig-restore-fullscreen (wconfig)
 
;;  "Restore `selected-frame's fullscreen settings from WCONFIG."
 
;;  (set-frame-parameter
 
;;   nil 'fullscreen (wg-wconfig-parameters wconfig))
 
;;  )
 

	
 
(defun wg-scale-wconfig-to-frame (wconfig)
 
  "Scale WCONFIG buffers to fit current frame size.
 
Return a scaled copy of WCONFIG."
 
  (interactive)
 
  (wg-scale-wconfigs-wtree wconfig
 
                           (frame-parameter nil 'width)
 
                           (frame-parameter nil 'height)))
 

	
 
(defun wg-frame-resize-and-position (wconfig)
 
  "Resize and position a frame based on WCONFIG of current workgroup."
 
(defun wg-frame-resize-and-position (wconfig &optional frame)
 
  "Apply WCONFIG's size and position to a FRAME."
 
  (interactive)
 
  (unless frame
 
    (setq frame (selected-frame)))
 
  (let* ((params (wg-wconfig-parameters wconfig))
 
         fullscreen)
 
    (set-frame-parameter nil 'fullscreen (if (assoc 'fullscreen params)
 
                                             (cdr (assoc 'fullscreen params))
 
                                           nil))
 
    (set-frame-parameter frame 'fullscreen (if (assoc 'fullscreen params)
 
                                               (cdr (assoc 'fullscreen params))
 
                                             nil))
 
    (when (and wg-restore-frame-position
 
               (not (frame-parameter nil 'fullscreen)))
 
      (wg-wconfig-restore-frame-position wconfig))
 
               (not (frame-parameter frame 'fullscreen)))
 
      (wg-wconfig-restore-frame-position wconfig frame))
 
    ))
 

	
 
(defun wg-restore-frame-size-position (wconfig)
 
(defun wg-restore-frame-size-position (wconfig &optional fs)
 
  "Smart-restore of frame size and position.
 

	
 
Depending on `wg-remember-frame-for-each-wg' frame parameters may
 
be restored for each workgroup.
 

	
 
If `wg-remember-frame-for-each-wg' is nil (by default) then
 
current frame parameters are saved/restored to/from first
 
workgroup. And frame parameters for all other workgroups are just
 
ignored.
 
"
 
  (interactive)
 
  (let* ((params (wg-wconfig-parameters wconfig))
 
         fullscreen)
 
    ;; Frame maximized / fullscreen / none
 
    (unless wg-remember-frame-for-each-wg
 
      (setq params (wg-wconfig-parameters (wg-workgroup-working-wconfig (wg-first-workgroup)))))
 
    (setq fullscreen (if (assoc 'fullscreen params)
 
                         (cdr (assoc 'fullscreen params))
 
                       nil))
 
    (when (and fullscreen
 
             (or wg-remember-frame-for-each-wg
 
                 (null (wg-current-workgroup t))))
 
    (when (and fs
 
               fullscreen
 
               (or wg-remember-frame-for-each-wg
 
                   (null (wg-current-workgroup t))))
 
      (set-frame-parameter nil 'fullscreen fullscreen)
 
      ;; I had bugs restoring maximized frame:
 
      ;; Frame could be maximized but buffers are not scaled to fit it.
 
      ;;
 
      ;; Maybe because of `set-frame-parameter' takes some time to finish and is async.
 
      ;; So I tried this and it helped
 
      (sleep-for 0 100))
 

	
 
    ;; Position
 
    (when (and wg-restore-frame-position
 
               wg-remember-frame-for-each-wg
 
               (not (frame-parameter nil 'fullscreen)))
 
      (wg-wconfig-restore-frame-position wconfig))
 
    ))
 

	
 
;; FIXME: throw a specific error if the restoration was unsuccessful
 
(defun wg-restore-wconfig (wconfig)
 
  "Restore a workgroup configuration WCONFIG in `selected-frame'.
 
(defun wg-restore-frames ()
 
  "Try to recreate opened frames, take info from session's 'frame-list parameter."
 
  (interactive)
 
  (delete-other-frames)
 
  (when (wg-current-session t)
 
    (let ((fl (wg-session-parameter (wg-current-session t) 'frame-list nil))
 
          (frame (selected-frame)))
 
      (mapc (lambda (wconfig)
 
              (with-selected-frame (make-frame)
 
                ;;(wg-frame-resize-and-position wconfig)
 
                ;;(wg-restore-frame-size-position wconfig)
 
                ;;(wg-wconfig-restore-frame-position wconfig)
 
                (wg-restore-wconfig wconfig)
 
                )) fl)
 
      (select-frame-set-input-focus frame))))
 

	
 
;; FIXME: throw a specific error if the restoration was unsuccessful
 
(defun wg-restore-wconfig (wconfig &optional frame)
 
  "Restore a workgroup configuration WCONFIG in a FRAME.
 
Runs each time you're switching workgroups."
 
  (unless frame
 
    (setq frame (selected-frame)))
 
  (let ((wg-record-incorrectly-restored-bufs t)
 
        (wg-incorrectly-restored-bufs nil)
 
        (params (wg-wconfig-parameters wconfig))
 
        fullscreen wtree)
 
    (wg-barf-on-active-minibuffer)
 
    (wg-restore-frame-size-position wconfig)
 
    (when wg-restore-scroll-bars
 
      (wg-wconfig-restore-scroll-bars wconfig))
 
    (setq wtree (wg-scale-wconfig-to-frame wconfig))  ; scale wtree to frame size
 

	
 
    ;; Restore buffers
 
    (wg-restore-window-tree wtree)
 

	
 
    ;; Restore frame position
 
    (when (and wg-restore-frame-position
 
               (not (frame-parameter nil 'fullscreen))
 
               (null (wg-current-workgroup t)))
 
      (wg-wconfig-restore-frame-position wconfig))
 
      (wg-wconfig-restore-frame-position wconfig frame))
 

	
 
    (when wg-incorrectly-restored-bufs
 
      (message "Unable to restore these buffers: %S\
 
If you want, restore them manually and try again."
 
               (mapcar 'wg-buf-name wg-incorrectly-restored-bufs)))))
 

	
 

	
 

	
 
(provide 'workgroups-restore)
 
;;; workgroups-restore.el ends here
src/workgroups-structs.el
Show inline comments
 
;;; workgroups-structs.el --- Data structures for WG
 
;;; Commentary:
 
;;
 
;; Copyright (C) Sergey Pashinin
 
;; Author: Sergey Pashinin <sergey@pashinin.com>
 
;;
 
;; `wg-defstruct'
 
;;
 
;; It creates some functions named like "wg-buf-...", "wg-session-..."
 
;;
 
;; To get a value you can use:
 
;; (wg-session-... (wg-current-session))
 
;; Like:
 
;; (wg-session-file-name (wg-current-session))
 
;; (wg-workgroup-parameters (wg-current-workgroup))
 
;;
 
;; To set a value (in `wg-write-session-file'):
 
;;
 
;; (setf (wg-session-file-name (wg-current-session)) filename)
 
;;
 
;;; Code:
 

	
 
(require 'workgroups-utils-basic)
 

	
 
(wg-defstruct wg buf
 
  (uid (wg-generate-uid))
 
  (name)
 
  (file-name)
 
  (point)
 
  (mark)
 
  (local-vars)
 
  (special-data)
 
  ;; This may be used later:
 
  (gc))
 

	
 
(wg-defstruct wg win
 
  (uid)
 
  (parameters)
 
  (edges)
 
  (point)
 
  (start)
 
  (hscroll)
 
  (dedicated)
 
  (selected)
 
  (minibuffer-scroll)
 
  (buf-uid))
 

	
 
(wg-defstruct wg wtree
 
  (uid)
 
  (dir)
 
  (edges)
 
  (wlist))
 

	
 
(wg-defstruct wg wconfig
 
  (uid (wg-generate-uid))
 
  (name)
 
  (parameters)
 
  (left)
 
  (top)
 
  (width)
 
  (height)
 
  (vertical-scroll-bars)
 
  (scroll-bar-width)
 
  (wtree)
 
  ;;(fullscreen)
 
  )
 
;; wg-wconfig
 
  (wtree))
 

	
 
(wg-defstruct wg workgroup
 
  (uid (wg-generate-uid))
 
  (name)
 
  (modified)
 
  (parameters)
 
  (base-wconfig)
 
  (selected-frame-wconfig)
 
  (saved-wconfigs)
 
  (strong-buf-uids)
 
  (weak-buf-uids))
 

	
 
(wg-defstruct wg session
 
  (uid (wg-generate-uid))
 
  (name)
 
  (modified)
 
  (parameters)
 
  (file-name)
 
  (version wg-version)
 
  (workgroup-list)
 
  (buf-list))
 

	
 
(wg-defstruct wg workgroup-state
 
  (undo-pointer)
 
  (undo-list))
 

	
 
(provide 'workgroups-structs)
 
;;; workgroups-structs.el ends here
src/workgroups-variables.el
Show inline comments
 
;;; workgroups-variables --- Workgroups vars and consts
 
;;; Commentary:
 
;;; Code:
 

	
 
(defconst wg-version "1.0.3"
 
  "Current version of workgroups.")
 

	
 
;;; customization
 

	
 
(defgroup workgroups nil
 
  "Workgroups for Emacs -- Emacs session manager"
 
  :group 'convenience
 
  :version wg-version)
 

	
 
(defcustom workgroups-mode nil
 
  "Non-nil if Workgroups mode is enabled."
 
  :set 'custom-set-minor-mode
 
  :initialize 'custom-initialize-default
 
  :group 'workgroups
 
  :type 'boolean)
 

	
 
(defcustom wg-first-wg-name "First workgroup"
 
  "Title of the first workgroup created."
 
  :type 'string
 
  :group 'workgroups)
 

	
 
(defcustom wg-load-last-workgroup t
 
  "Load last active (not first) workgroup from all your workgroups if it exists."
 
  :group 'workgroups
 
  :type 'boolean)
 

	
 
(defcustom wg-control-frames t
 
  "Save/restore frames."
 
  :group 'workgroups
 
  :type 'boolean)
 

	
 

	
 
;; keybinding customization
 

	
 
(defcustom wg-prefix-key (kbd "C-c z")
 
  "Workgroups' prefix key.
 
Setting this variable requires that `workgroups-mode' be turned
 
off and then on again to take effect."
 
  :type 'string
 
  :group 'workgroups)
 

	
 

	
 
;; hooks
 

	
 
(defcustom workgroups-mode-hook nil
 
  "Hook run when `workgroups-mode' is turned on."
 
  :type 'hook
 
  :group 'workgroups)
 

	
 
(defcustom workgroups-mode-exit-hook nil
 
  "Hook run when `workgroups-mode' is turned off."
 
  :type 'hook
 
  :group 'workgroups)
 

	
 
(defcustom wg-switch-to-workgroup-hook nil
 
  "Hook run by `wg-switch-to-workgroup'."
 
  :type 'hook
 
  :group 'workgroups)
 

	
 
(defcustom wg-buffer-list-finalization-hook nil
 
  "Functions in this hook can modify `wg-temp-buffer-list'
 
arbitrarily, provided its final value is still a list of the
 
names of live buffer.  Any final adjustments the user wishes to
 
make to the filtered buffer list before ido/iswitchb get ahold of
 
it should be made here."
 
  :type 'hook
 
  :group 'workgroups)
 

	
 
(defcustom wg-pre-window-configuration-change-hook nil
 
  "Hook run before any function that triggers
 
`window-configuration-change-hook'."
 
  :type 'hook
 
  :group 'workgroups)
 

	
 

	
 
;; save and load customization
 
(defcustom wg-use-default-session-file (not (daemonp))
 
  "Generally, non-nil means take care of saving and loading automatically,
 
and nil means leave it up to the user.
 

	
 
FIXME: docstring this"
 
  :type 'boolean
 
  :group 'workgroups)
 

	
 
(defcustom wg-default-session-file
 
  "~/.emacs_workgroups"
 
  "Default filename to be used to save workgroups."
 
  :type 'file
 
  :group 'workgroups)
 

	
 
(defcustom wg-open-this-wg nil
 
  "Try to open this workgroup on start. If nil - nothing happens."
 
  :type 'string
 
  :group 'workgroups)
 

	
 
(defcustom wg-switch-to-first-workgroup-on-find-session-file t
 
  "Non-nil means switch to the first workgroup in a session file
 
when it's found with `wg-find-session-file'."
 
  :type 'boolean
 
  :group 'workgroups)
 

	
 
(defcustom wg-emacs-exit-save-behavior 'save
 
  "Determines save behavior on Emacs exit.
 
Possible values:
 

	
 
`ask'           Ask the user whether to save if there are unsaved changes
 

	
 
`save'          Call `wg-save-session' when there are unsaved changes
 

	
 
Anything else   Exit Emacs without saving changes"
 
  :type 'symbol
 
  :group 'workgroups)
 

	
 
(defcustom wg-workgroups-mode-exit-save-behavior 'save
 
  "Determines save behavior on `workgroups-mode' exit.
 
Possible values:
 

	
 
`ask'           Ask the user whether to saveif there are unsaved changes
 

	
 
`save'          Call `wg-save-session' when there are unsaved changes
 

	
 
Anything else   Exit `workgroups-mode' without saving changes"
 
  :type 'symbol
 
  :group 'workgroups)
 

	
 

	
 
;; minibuffer customization
 

	
 
(defcustom wg-confirm-on-get-workgroup-create nil
 
  "Non-nil means request confirmation before creating a new
 
workgroup when `wg-get-workgroup-create' is called with a string
 
that doesn't name an existing workgroup."
 
  :type 'boolean
 
  :group 'workgroups)
 

	
 
(defcustom wg-no-confirm-on-destructive-operation nil
 
  "Non-nil means don't request confirmation before various
 
destructive operations, like `wg-reset'."
 
  :type 'boolean
 
  :group 'workgroups)
 

	
 
(defcustom wg-minibuffer-message-timeout 0.75
 
  "Bound to `minibuffer-message-timeout' when messaging while the
 
minibuffer is active."
 
  :type 'float
 
  :group 'workgroups)
 

	
 

	
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
;;
 
;; FIXME:
 
;;
 
;; Only set `wg-workgroup-base-wconfig' on `wg-write-session-file' or
 
;; `delete-frame' and only with the most recently changed working-wconfig.
 
;; Then, since it's not overwritten on every call to
 
;; `wg-workgroup-working-wconfig', its restoration can be retried after manually
 
;; recreating buffers that couldn't be restored.  So it takes over the
 
;; 'incorrect restoration' portion of the base wconfig's duty.  All that leaves
 
;; to base wconfigs is that they're a saved wconfig the user felt was important.
 
;; So why not allow more of of them?  A workgroup could stash an unlimited
 
;; number of wconfigs.
 
;;
 
;; TODO:
 
;;
 
;;   * Write new commands for restoring stashed wconfigs
 
;;
 
;;   * Add this message on improper restoration of `base-wconfig':
 
;;
 
;;       "Unable to restore 'buf1', 'buf2'... Hit C-whatever to retry after
 
;;        manually recreating these buffers."
 
;;
 
;;
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 

	
 

	
 
;; workgroup restoration customization
 

	
 
;; TODO: possibly add `buffer-file-coding-system', `text-scale-mode-amount'
 
(defcustom wg-buffer-local-variables-alist
 
  `((major-mode nil wg-deserialize-buffer-major-mode)
 
    (mark-ring wg-serialize-buffer-mark-ring wg-deserialize-buffer-mark-ring)
 
    (left-fringe-width nil nil)
 
    (right-fringe-width nil nil)
 
    (fringes-outside-margins nil nil)
 
    (left-margin-width nil nil)
 
    (right-margin-width nil nil)
 
    (vertical-scroll-bar nil nil))
 
  "Alist mapping buffer-local variable symbols to serdes functions.
 

	
 
The `car' of each entry should be a buffer-local variable symbol.
 

	
 
The `cadr' of the entry should be either nil or a function of no
 
arguments.  If nil, the variable's value is used as-is, and
 
should have a readable printed representation.  If a function,
 
`funcall'ing it should yield a serialization of the value of the
 
variable.
 

	
 
The `caddr' of the entry should be either nil or a function of
 
one argument.  If nil, the serialized value from above is
 
assigned to the variable as-is.  It a function, `funcall'ing it
 
on the serialized value from above should do whatever is
 
necessary to properly restore the original value of the variable.
 
For example, in the case of `major-mode' it should funcall the
 
value (a major-mode function symbol) rather than just assigning
 
it to `major-mode'."
 
  :type 'alist
 
  :group 'workgroups)
 

	
 
(defcustom wg-special-buffer-serdes-functions
 
  '(wg-serialize-comint-buffer
 
    wg-serialize-speedbar-buffer)
 
  "List of functions providing special buffer serialization/deserialization.
 

	
 
Use `wg-support' macro and this variable will be filled
 
automatically.
 

	
 
An entry should be either a function symbol or a lambda, and should
 
accept a single Emacs buffer object as an argument.
 

	
 
When a buffer is to be serialized, it is passed to each of these
 
functions in turn until one returns non-nil, or the list ends.  A
 
return value of nil indicates that the function can't handle
 
buffers of that type.  A non-nil return value indicates that it
0 comments (0 inline, 0 general)