diff --git a/workgroups-commands.el b/workgroups-commands.el new file mode 100644 index 0000000000000000000000000000000000000000..a6c796fd0c5fb5734c717f5c8dd2008cefa7e77a --- /dev/null +++ b/workgroups-commands.el @@ -0,0 +1,826 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; ;;; +;;; ;;; +;;; Commands ;;; +;;; ;;; +;;; ;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + + +;;; workgroup switching commands + +(defun wg-switch-to-workgroup (workgroup &optional noerror) + "Switch to WORKGROUP." + (interactive (list (wg-read-workgroup-name))) + (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 + (wg-restore-workgroup workgroup) + (wg-set-previous-workgroup current) + (wg-set-current-workgroup workgroup) + (run-hooks 'wg-switch-to-workgroup-hook) + (wg-fontified-message + (:cmd "Switched: ") + (wg-workgroup-name (wg-current-workgroup t)) + ;;(wg-workgroup-list-display) + )) + (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)))))) + +(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)))) + +(defun wg-delete-other-workgroups (&optional workgroup) + "Delete all workgroups but WORKGROUP." + (interactive) + (let ((workgroup (wg-get-workgroup workgroup))) + (unless (or wg-no-confirm-on-destructive-operation + (y-or-n-p "Really delete all other workgroups? ")) + (error "Cancelled")) + (dolist (w (wg-workgroup-list-or-error)) + (unless (eq w workgroup) + (wg-delete-workgroup w))) + (unless (wg-current-workgroup-p workgroup) + (wg-switch-to-workgroup workgroup)) + (wg-fontified-message + (:cmd "Deleted: ") + (:msg "All workgroups but ") + (:cur (wg-workgroup-name workgroup))))) + + + +;;; workgroup updating and reverting commands + +(defun wg-revert-workgroup (&optional workgroup) + "Restore WORKGROUP's window configuration to its state at the last save." + (interactive) + (let* ((workgroup (wg-get-workgroup workgroup)) + (base-wconfig (wg-workgroup-base-wconfig workgroup))) + (if (wg-current-workgroup-p workgroup) + (wg-restore-wconfig-undoably base-wconfig) + (wg-add-wconfig-to-undo-list workgroup base-wconfig)) + (wg-fontified-message + (:cmd "Reverted: ") + (:cur (wg-workgroup-name workgroup))))) + +(defun wg-revert-all-workgroups () + "Revert all workgroups to their base wconfigs. +Only workgroups' working-wconfigs in `selected-frame' are +reverted." + (interactive) + (mapc #'wg-revert-workgroup (wg-workgroup-list-or-error)) + (wg-fontified-message + (:cmd "Reverted: ") + (:msg "All"))) + + + +;;; saved wconfig commands + +(defun wg-save-wconfig () + "Save the current wconfig to the current workgroup's saved wconfigs." + (interactive) + (let* ((workgroup (wg-current-workgroup)) + (name (wg-read-saved-wconfig-name workgroup)) + (wconfig (wg-current-wconfig))) + (setf (wg-wconfig-name wconfig) name) + (wg-workgroup-save-wconfig workgroup wconfig) + (wg-fontified-message + (:cmd "Saved: ") + (:cur name)))) + +(defun wg-restore-saved-wconfig () + "Restore one of the current workgroup's saved wconfigs in `selected-frame'." + (interactive) + (let ((workgroup (wg-current-workgroup))) + (wg-restore-wconfig-undoably + (wg-workgroup-get-saved-wconfig + workgroup + (wg-completing-read + "Saved wconfig: " + (mapcar 'wg-wconfig-name (wg-workgroup-saved-wconfigs workgroup)) + nil t))))) + +(defun wg-kill-saved-wconfig () + "Kill one of the current workgroup's saved wconfigs. +Also add it to the wconfig kill-ring." + (interactive) + (let* ((workgroup (wg-current-workgroup)) + (wconfig (wg-read-saved-wconfig workgroup))) + (wg-workgroup-kill-saved-wconfig workgroup wconfig) + (wg-add-to-wconfig-kill-ring wconfig) + (wg-fontified-message + (:cmd "Deleted: ") + (:cur (wg-wconfig-name wconfig))))) + + + +;;; workgroup-list reorganization commands + +(defun wg-swap-workgroups () + "Swap the previous and current workgroups." + (interactive) + (wg-swap-workgroups-in-workgroup-list + (wg-current-workgroup) (wg-previous-workgroup)) + (wg-fontified-message + (:cmd "Swapped: ") + (wg-workgroup-list-display))) + +(defun wg-offset-workgroup-left (&optional workgroup n) + "Offset WORKGROUP leftward in `wg-workgroup-list' cyclically." + (interactive (list nil current-prefix-arg)) + (wg-cyclic-offset-workgroup (wg-get-workgroup workgroup) (or n -1)) + (wg-fontified-message + (:cmd "Offset left: ") + (wg-workgroup-list-display))) + +(defun wg-offset-workgroup-right (&optional workgroup n) + "Offset WORKGROUP rightward in `wg-workgroup-list' cyclically." + (interactive (list nil current-prefix-arg)) + (wg-cyclic-offset-workgroup (wg-get-workgroup workgroup) (or n 1)) + (wg-fontified-message + (:cmd "Offset right: ") + (wg-workgroup-list-display))) + + + +;;; undo/redo commands + +(defun wg-undo-wconfig-change (&optional workgroup) + "Undo a change to the current workgroup's window-configuration." + (interactive) + (let* ((workgroup (wg-get-workgroup workgroup)) + (undid? (wg-workgroup-offset-position-in-undo-list workgroup 1))) + (wg-fontified-message + (:cmd "Undo: ") + (wg-undo-timeline-display workgroup) + (:cur (if undid? "" " No more undo info"))))) + +(defun wg-redo-wconfig-change (&optional workgroup) + "Redo a change to the current workgroup's window-configuration." + (interactive) + (let* ((workgroup (wg-get-workgroup workgroup)) + (redid? (wg-workgroup-offset-position-in-undo-list workgroup -1))) + (wg-fontified-message + (:cmd "Redo: ") + (wg-undo-timeline-display workgroup) + (:cur (if redid? "" " No more redo info"))))) + +(defun wg-undo-once-all-workgroups () + "Do what the name says. Useful for instance when you +accidentally call `wg-revert-all-workgroups' and want to return +all workgroups to their un-reverted state." + (interactive) + (mapc 'wg-undo-wconfig-change (wg-workgroup-list-or-error)) + (wg-message "Undid once on all workgroups.")) + +(defun wg-redo-once-all-workgroups () + "Do what the name says. Probably useless. Included for +symetry with `wg-undo-once-all-workgroups'." + (interactive) + (mapc 'wg-redo-wconfig-change (wg-workgroup-list-or-error)) + (wg-message "Redid once on all workgroups.")) + + + +;;; buffer-list-filter commands + +(defun wg-switch-to-buffer () + "Workgroups' version of `switch-to-buffer'." + (interactive) + (wg-buffer-internal 'switch-to-buffer "Buffer")) + +(defun wg-switch-to-buffer-other-window () + "Workgroups' version of `switch-to-buffer-other-window'." + (interactive) + (wg-buffer-internal + 'switch-to-buffer-other-window "Switch to buffer in other window")) + +(defun wg-switch-to-buffer-other-frame () + "Workgroups' version of `switch-to-buffer-other-frame'." + (interactive) + (wg-buffer-internal + 'switch-to-buffer-other-frame "Switch to buffer in other frame")) + +(defun wg-kill-buffer () + "Workgroups' version of `kill-buffer'." + (interactive) + (wg-buffer-internal + 'kill-buffer "Kill buffer" (buffer-name (current-buffer)))) + +(defun wg-display-buffer () + "Workgroups' version of `display-buffer'." + (interactive) + (wg-buffer-internal 'display-buffer "Display buffer")) + +(defun wg-insert-buffer () + "Workgroups' version of `insert-buffer'." + (interactive) + (wg-buffer-internal 'insert-buffer "Insert buffer")) + +;; FIXME: If you C-h i for info, then wg-next-buffer, you occasionally don't +;; switch to the buffer you were on previously. +(defun wg-next-buffer-internal (buffer-list &optional prev noerror) + "Switch to the next buffer in Workgroups' filtered buffer list." + (when buffer-list + (let* ((cur (current-buffer)) + (next (or (wg-cyclic-nth-from-elt cur buffer-list (if prev -1 1)) + (car buffer-list)))) + (unless (eq cur next) + (switch-to-buffer next) + (unless prev (bury-buffer cur)) + next)))) + +(defun wg-next-buffer (&optional prev) + "Switch to the next buffer in Workgroups' filtered buffer list. +In the post-command message the current buffer is rotated to the +middle of the list to more easily see where `wg-previous-buffer' +will take you." + (interactive) + (let ((command (if prev 'previous-buffer 'next-buffer))) + (if (not (wg-filter-buffer-list-p)) + (call-interactively (wg-prior-mapping workgroups-mode command)) + (wg-with-buffer-list-filters command + (wg-awhen (wg-filtered-buffer-list) (wg-next-buffer-internal it prev)) + (wg-message (wg-buffer-command-display)))))) + +(defun wg-previous-buffer () + "Switch to the next buffer in Workgroups' filtered buffer list." + (interactive) + (wg-next-buffer t)) + +(defun wg-bury-buffer (&optional buffer-or-name) + "Remove BUFFER-OR-NAME from the current workgroup, bury it, +and switch to the next buffer in the buffer-list-filter." + (interactive (list (current-buffer))) + (if (not (wg-filter-buffer-list-p)) + (call-interactively (wg-prior-mapping workgroups-mode 'bury-buffer)) + (wg-with-buffer-list-filters 'bury-buffer + (wg-next-buffer-internal (wg-filtered-buffer-list)) + (bury-buffer buffer-or-name) + (wg-message (wg-buffer-command-display))))) + +(defun wg-banish-buffer (&optional buffer-or-name) + "Dissociate BUFFER-OR-NAME from the current workgroup, and bury it." + (interactive) + (let ((buffer (or buffer-or-name (current-buffer)))) + (wg-workgroup-dissociate-bufobj (wg-current-workgroup) buffer) + (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 (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) + + ;;(wg-set-session-parameter nil wg-key-load-last-workgroup wg-load-last-workgroup) + ;;(wg-set-session-parameter nil wg-key-current-workgroup "wg-") + ;;(wg-session-parameter nil wg-key-current-workgroup) + ;;(wg-remove-session-parameter nil wg-key-current-workgroup) + ;;(if wg-load-last-workgroup + ;; (let ((curr-wg-name (wg-workgroup-name (wg-current-workgroup)))) + ;; (wg-set-session-parameter nil wg-key-current-workgroup curr-wg-name) + ;; )) + (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." + (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-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)) + ;;(message (wg-session-parameter nil wg-key-current-workgroup)) + ;;(message "session") + ;;(sleep-for 3) + ) + (wg-awhen (and wg-switch-to-first-workgroup-on-find-session-file + (wg-workgroup-list)) + (wg-switch-to-workgroup (car it))) + (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)")))) + + + ;;(wg-remove-session-parameter nil wg-key-current-workgroup) + ;;(if (wg-session-parameter nil wg-key-load-last-workgroup wg-load-last-workgroup) + ;; (let ((cur-wg (wg-session-parameter nil wg-key-current-workgroup))) + ;; (if cur-wg + ;; (wg-switch-to-workgroup (wg-get-workgroup cur-wg)) + ;; ))) + ) + +(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)) + +(defun wg-toggle-morph () + "Toggle `wg-morph-on'." + (interactive) + (wg-toggle-and-message 'wg-morph-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)