than deleted.
@end deffn
+The following function checks whether a frame can be safely deleted. It
+is useful to avoid that a subsequent call of @code{delete-frame} throws
+an error.
+
+@defun frame-deletable-p &optional frame
+This function returns non-@code{nil} if the frame specified by
+@var{frame} can be safely deleted. @var{frame} must be a live frame and
+defaults to the selected frame.
+
+A frame cannot be safely deleted in the following cases:
+
+@itemize @bullet
+@item
+It is the only visible or iconified frame (@pxref{Visibility of
+Frames}).
+
+@item
+It hosts the active minibuffer window and minibuffer windows do not
+follow the selected frame (@pxref{Basic Minibuffer,,, emacs}).
+
+@item
+All other visible or iconified frames are either child frames
+(@pxref{Child Frames}) or have a non-@code{nil} @code{delete-before}
+parameter.
+
+@item
+The frame or one of its descendants hosts the minibuffer window of a
+frame that is not a descendant of the frame (@pxref{Child Frames}).
+@end itemize
+
+These conditions cover most cases where @code{delete-frame} might fail
+when called from top-level. They do not catch some special cases like,
+for example, deleting a frame during a drag-and-drop operation
+(@pxref{Drag and Drop}). In any such case, it will be better to wrap
+the @code{delete-frame} call in a @code{condition-case} form.
+@end defun
+
@node Finding All Frames
@section Finding All Frames
operations more intuitively.
+++
-*** 'quit-restore-window' now handles the values 'killing' and 'burying'
-for its BURY-OR-KILL argument just like 'kill' and 'bury' but assumes
+*** 'quit-restore-window' handles new values for BURY-OR-KILL argument.
+The values 'killing' and 'burying' are like 'kill' and 'bury' but assume
that the actual killing or burying of the buffer is done by the caller.
+++
With this option set, 'quit-restore-window' will delete its window more
aggressively rather than switching to some other buffer in it.
+** Frames
+
++++
+*** New function 'frame-deletable-p'.
+Calling this function before 'delete-frame' is useful to avoid that the
+latter throws an error when the argument frame cannot be deleted.
+
** Tab Bars and Tab Lines
---
(sexp :tag "Value")))
:group 'frames)
+(defun frame-deletable-p (&optional frame)
+ "Return non-nil if specified FRAME can be safely deleted.
+FRAME must be a live frame and defaults to the selected frame.
+
+FRAME cannot be safely deleted in the following cases:
+
+- FRAME is the only visible or iconified frame.
+
+- FRAME hosts the active minibuffer window that does not follow the
+ selected frame.
+
+- All other visible or iconified frames are either child frames or have
+ a non-nil `delete-before' parameter.
+
+- FRAME or one of its descendants hosts the minibuffer window of a frame
+ that is not a descendant of FRAME.
+
+This covers most cases where `delete-frame' might fail when called from
+top-level. It does not catch some special cases like, for example,
+deleting a frame during a drag-and-drop operation. In any such case, it
+will be better to wrap the `delete-frame' call in a `condition-case'
+form."
+ (setq frame (window-normalize-frame frame))
+ (let ((active-minibuffer-window (active-minibuffer-window))
+ deletable)
+ (catch 'deletable
+ (when (and active-minibuffer-window
+ (eq (window-frame active-minibuffer-window) frame)
+ (not (eq (default-toplevel-value
+ 'minibuffer-follows-selected-frame)
+ t)))
+ (setq deletable nil)
+ (throw 'deletable nil))
+
+ (let ((frames (delq frame (frame-list))))
+ (dolist (other frames)
+ ;; A suitable "other" frame must be either visible or
+ ;; iconified. Child frames and frames with a non-nil
+ ;; 'delete-before' parameter do not qualify as other frame -
+ ;; either of these will depend on a "suitable" frame found in
+ ;; this loop.
+ (unless (or (frame-parent other)
+ (frame-parameter other 'delete-before)
+ (not (frame-visible-p other)))
+ (setq deletable t))
+
+ ;; Some frame not descending from FRAME may use the minibuffer
+ ;; window of FRAME or the minibuffer window of a frame
+ ;; descending from FRAME.
+ (when (let* ((minibuffer-window (minibuffer-window other))
+ (minibuffer-frame
+ (and minibuffer-window
+ (window-frame minibuffer-window))))
+ (and minibuffer-frame
+ ;; If the other frame is a descendant of
+ ;; FRAME, it will be deleted together with
+ ;; FRAME ...
+ (not (frame-ancestor-p frame other))
+ ;; ... but otherwise the other frame must
+ ;; neither use FRAME nor any descendant of
+ ;; it as minibuffer frame.
+ (or (eq minibuffer-frame frame)
+ (frame-ancestor-p frame minibuffer-frame))))
+ (setq deletable nil)
+ (throw 'deletable nil))))
+
+ deletable)))
+
(defun handle-delete-frame (event)
"Handle delete-frame events from the X server."
(interactive "e")
The value should be a list of functions that take two arguments. The
first argument is the window about to be deleted. The second argument
if non-nil, means that the window is the only window on its frame and
-should be deleted together with its frame. The window's buffer is
-current when running this hook.
+should be deleted together with its frame. If the window is live, its
+buffer is current when running this hook.
If any of these functions returns nil, the window will not be deleted
and another buffer will be shown in it. This hook is run implicitly by
;; WINDOW's frame can be deleted only if there are other frames
;; on the same terminal, and it does not contain the active
;; minibuffer.
- (unless (or (eq frame (next-frame frame 0))
- ;; We can delete our frame only if no other frame
- ;; currently uses our minibuffer window.
- (catch 'other
- (dolist (other (frame-list))
- (when (and (not (eq other frame))
- (eq (window-frame (minibuffer-window other))
- frame))
- (throw 'other t))))
- (let ((minibuf (active-minibuffer-window)))
- (and minibuf (eq frame (window-frame minibuf))
- (not (eq (default-toplevel-value
- 'minibuffer-follows-selected-frame)
- t))))
+ (unless (or (not (frame-deletable-p (window-frame window)))
(or no-run
- (not (with-current-buffer (window-buffer window)
- (run-hook-with-args-until-failure
+ (if (window-live-p window)
+ (not (with-current-buffer (window-buffer window)
+ (run-hook-with-args-until-failure
+ 'window-deletable-functions window t)))
+ (not (run-hook-with-args-until-failure
'window-deletable-functions window t)))))
'frame))
((window-minibuffer-p window)
((and (or ignore-window-parameters
(not (eq window (window-main-window frame))))
(or no-run
- (with-current-buffer (window-buffer window)
+ (if (window-live-p window)
+ (with-current-buffer (window-buffer window)
+ (run-hook-with-args-until-failure
+ 'window-deletable-functions window nil))
(run-hook-with-args-until-failure
'window-deletable-functions window nil))))
;; Otherwise, WINDOW can be deleted unless it is the main window