From fd68d27dde26788820ebdc3ad816cefb6a5c659b Mon Sep 17 00:00:00 2001 From: Gregory Heytings Date: Tue, 16 Nov 2021 17:01:37 +0000 Subject: [PATCH] Undelete deleted frames. * lisp/frame.el (undelete-frame): New command. (undeleted-frame--save-deleted-frame): New auxiliary function. (undelete-frame--deleted-frames): New auxiliary variable. (make-frame-command): Add a prefix argument, and call the new command. * src/frame.c (Fdelete_frame): Update docstring. * lisp/menu-bar.el (menu-bar-file-menu): Add an entry for the new command. * doc/emacs/frames.tex (Creating Frames, Frame Commands): Document the new command. * etc/NEWS: Document the new command. See bug#51883. --- doc/emacs/frames.texi | 11 +++++--- etc/NEWS | 10 +++++++ lisp/frame.el | 61 +++++++++++++++++++++++++++++++++++++++---- lisp/menu-bar.el | 4 +++ src/frame.c | 3 +++ 5 files changed, 81 insertions(+), 8 deletions(-) diff --git a/doc/emacs/frames.texi b/doc/emacs/frames.texi index c14ada2957..f5ede6af5e 100644 --- a/doc/emacs/frames.texi +++ b/doc/emacs/frames.texi @@ -452,8 +452,11 @@ Creating Frames @item C-x 5 2 @kindex C-x 5 2 @findex make-frame-command -Create a new frame using the default frame parameters -(@code{make-frame-command}). +Create a new frame using the default frame parameters, or, with a prefix +argument, undelete one of the 16 most recently deleted frames +(@code{make-frame-command}). A prefix argument undeletes the last deleted +frame, a numerical prefix argument between 0 and 15 undeletes the +corresponding deleted frame, where 0 is the most recently deleted frame. @item C-x 5 c @kindex C-x 5 c @@ -510,7 +513,9 @@ Frame Commands @kindex C-x 5 0 @findex delete-frame Delete the selected frame (@code{delete-frame}). This signals an -error if there is only one frame. +error if there is only one frame. The 16 most recently deleted frames +can be undeleted with the @kbd{C-x 5 2} command, when it is used with +a prefix argument. @item C-z @kindex C-z @r{(X windows)} diff --git a/etc/NEWS b/etc/NEWS index 312fc18f4f..ab47f8cf5b 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -130,6 +130,16 @@ For example, a 'display-buffer-alist' entry of will make the body of the chosen window 40 columns wide. For the height use 'window-height' in combination with 'body-lines'. +--- +** Frames + ++++ +*** With a prefix argument, the key 'C-x 5 2' undeletes deleted frames. +The 16 most recently deleted frames can be undeleted. A prefix argument +undeletes the last deleted frame, a numerical prefix argument between 0 +and 15 undeletes the corresponding deleted frame, where 0 is the most +recently deleted frame. + ** Better detection of text suspiciously reordered on display. The function 'bidi-find-overridden-directionality' has been extended to detect reordering effects produced by embeddings and isolates diff --git a/lisp/frame.el b/lisp/frame.el index 2c73737a54..199e9e8005 100644 --- a/lisp/frame.el +++ b/lisp/frame.el @@ -775,16 +775,23 @@ close-display-connection (mapc #'delete-frame frames) (x-close-connection display)))) -(defun make-frame-command () +(defun make-frame-command (&optional arg) "Make a new frame, on the same terminal as the selected frame. If the terminal is a text-only terminal, this also selects the new frame. +With a prefix argument ARG, undelete the most recently deleted +frame. +With a numerical prefix argument ARG between 0 and 15, undelete +the ARGth deleted frame, where 0 is most recently deleted frame. + When called from Lisp, returns the new frame." - (interactive) - (if (display-graphic-p) - (make-frame) - (select-frame (make-frame)))) + (interactive "P") + (if arg + (undelete-frame arg) + (if (display-graphic-p) + (make-frame) + (select-frame (make-frame))))) (defun clone-frame (&optional frame no-windows) "Make a new frame with the same parameters and windows as FRAME. @@ -2484,6 +2491,50 @@ delete-other-frames (if iconify (iconify-frame this) (delete-frame this))) (setq this next)))) +(eval-when-compile (require 'frameset)) + +(defvar undelete-frame--deleted-frames nil + "Internal variable used by `undelete-frame--save-deleted-frame'.") + +(defun undeleted-frame--save-deleted-frame (frame) + "Save the configuration of frames deleted with `delete-frame'. +Only the 16 most recently deleted frames are saved." + (when (frame-live-p frame) + (setq undelete-frame--deleted-frames + (cons (cons + (display-graphic-p) + (frameset-save (list frame))) + undelete-frame--deleted-frames)) + (if (> (length undelete-frame--deleted-frames) 16) + (setq undelete-frame--deleted-frames + (butlast undelete-frame--deleted-frames))))) + +(add-hook 'delete-frame-functions #'undeleted-frame--save-deleted-frame) + +(defun undelete-frame (&optional arg) + "Undelete a frame deleted with `delete-frame'. +When ARG is nil or a list, the last deleted frame is undeleted. +When ARG is a number between 0 and 15, the ARGth deleted frame is +undeleted. +When called from Lisp, returns the new frame." + (interactive "P") + (let* ((frames (frame-list)) + (n (if (listp arg) 0 arg)) + (frameset (nth n undelete-frame--deleted-frames))) + (if (not frameset) + (message "No deleted frame saved at position %d" n) + (if (not (eq (display-graphic-p) (car frameset))) + (message + "Cannot undelete %sgraphic display frame on a %sgraphic display" + (if (display-graphic-p) "non-" "") + (if (display-graphic-p) "" "non-")) + (setq undelete-frame--deleted-frames + (delq frameset undelete-frame--deleted-frames)) + (frameset-restore (cdr frameset)) + (let ((frame (car (seq-difference (frame-list) frames)))) + (when frame + (select-frame-set-input-focus frame) + frame)))))) ;;; Window dividers. (defgroup window-divider nil diff --git a/lisp/menu-bar.el b/lisp/menu-bar.el index 1a81f1a3d0..a1e0195bb5 100644 --- a/lisp/menu-bar.el +++ b/lisp/menu-bar.el @@ -109,6 +109,10 @@ menu-bar-file-menu (bindings--define-key menu [separator-tab] menu-bar-separator)) + (bindings--define-key menu [undelete-last-deleted-frame] + '(menu-item "Undelete Frame" undelete-frame + :help "Undelete last deleted frame")) + ;; Don't use delete-frame as event name because that is a special ;; event. (bindings--define-key menu [delete-this-frame] diff --git a/src/frame.c b/src/frame.c index 79a7c89e0d..9e11bc93ed 100644 --- a/src/frame.c +++ b/src/frame.c @@ -2376,6 +2376,9 @@ DEFUN ("delete-frame", Fdelete_frame, Sdelete_frame, 0, 2, "", doc: /* Delete FRAME, permanently eliminating it from use. FRAME must be a live frame and defaults to the selected one. +The 16 most recently deleted frames can however be undeleted with +`undelete-frame', which see. + A frame may not be deleted if its minibuffer serves as surrogate minibuffer for another frame. Normally, you may not delete a frame if all other frames are invisible, but if the second optional argument -- 2.33.0